/* * Event char devices, giving access to raw input device events. * * Copyright (c) 1999-2002 Vojtech Pavlik * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define EVDEV_MINOR_BASE 64 #define EVDEV_MINORS 32 #define EVDEV_MIN_BUFFER_SIZE 64U #define EVDEV_BUF_PACKETS 8 #include #include #include #include #include #include #include #include #include #include "input-compat.h" struct evdev { int open; struct input_handle handle; wait_queue_head_t wait; struct evdev_client __rcu *grab; struct list_head client_list; spinlock_t client_lock; /* protects client_list */ struct mutex mutex; struct device dev; struct cdev cdev; bool exist; }; struct evdev_client { unsigned int head; unsigned int tail; unsigned int packet_head; /* [future] position of the first element of next packet */ spinlock_t buffer_lock; /* protects access to buffer, head and tail */ struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; int clkid; unsigned int bufsize; struct input_event buffer[]; }; static void __pass_event(struct evdev_client *client, const struct input_event *event) { client->buffer[client->head++] = *event; client->head &= client->bufsize - 1; if (unlikely(client->head == client->tail)) { /* * This effectively "drops" all unconsumed events, leaving * EV_SYN/SYN_DROPPED plus the newest event in the queue. */ client->tail = (client->head - 2) & (client->bufsize - 1); client->buffer[client->tail].time = event->time; client->buffer[client->tail].type = EV_SYN; client->buffer[client->tail].code = SYN_DROPPED; client->buffer[client->tail].value = 0; client->packet_head = client->tail; } if (event->type == EV_SYN && event->code == SYN_REPORT) { client->packet_head = client->head; kill_fasync(&client->fasync, SIGIO, POLL_IN); } } static void evdev_pass_values(struct evdev_client *client, const struct input_value *vals, unsigned int count, ktime_t mono, ktime_t real) { struct evdev *evdev = client->evdev; const struct input_value *v; struct input_event event; bool wakeup = false; event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ? mono : real); /* Interrupts are disabled, just acquire the lock. */ spin_lock(&client->buffer_lock); for (v = vals; v != vals + count; v++) { event.type = v->type; event.code = v->code; event.value = v->value; __pass_event(client, &event); if (v->type == EV_SYN && v->code == SYN_REPORT) wakeup = true; } spin_unlock(&client->buffer_lock); if (wakeup) wake_up_interruptible(&evdev->wait); } /* * Pass incoming events to all connected clients. */ static void evdev_events(struct input_handle *handle, const struct input_value *vals, unsigned int count) { struct evdev *evdev = handle->private; struct evdev_client *client; ktime_t time_mono, time_real; time_mono = ktime_get(); time_real = ktime_sub(time_mono, ktime_get_monotonic_offset()); rcu_read_lock(); client = rcu_dereference(evdev->grab); if (client) evdev_pass_values(client, vals, count, time_mono, time_real); else list_for_each_entry_rcu(client, &evdev->client_list, node) evdev_pass_values(client, vals, count, time_mono, time_real); rcu_read_unlock(); } /* * Pass incoming event to all connected clients. */ static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { struct input_value vals[] = { { type, code, value } }; evdev_events(handle, vals, 1); } static int evdev_fasync(int fd, struct file *file, int on) { struct evdev_client *client = file->private_data; return fasync_helper(fd, file, on, &client->fasync); } static int evdev_flush(struct file *file, fl_owner_t id) { struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; int retval; retval = mutex_lock_interruptible(&evdev->mutex); if (retval) return retval; if (!evdev->exist) retval = -ENODEV; else retval = input_flush_device(&evdev->handle, file); mutex_unlock(&evdev->mutex); return retval; } static void evdev_free(struct device *dev) { struct evdev *evdev = container_of(dev, struct evdev, dev); input_put_device(evdev->handle.dev); kfree(evdev); } /* * Grabs an event device (along with underlying input device). * This function is called with evdev->mutex taken. */ static int evdev_grab(struct evdev *evdev, struct evdev_client *client) { int error; if (evdev->grab) return -EBUSY; error = input_grab_device(&evdev->handle); if (error) return error; rcu_assign_pointer(evdev->grab, client); return 0; } static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client) { struct evdev_client *grab = rcu_dereference_protected(evdev->grab, lockdep_is_held(&evdev->mutex)); if (grab != client) return -EINVAL; rcu_assign_pointer(evdev->grab, NULL); synchronize_rcu(); input_release_device(&evdev->handle); return 0; } static void evdev_attach_client(struct evdev *evdev, struct evdev_client *client) { spin_lock(&evdev->client_lock); list_add_tail_rcu(&client->node, &evdev->client_list); spin_unlock(&evdev->client_lock); } static void evdev_detach_client(struct evdev *evdev, struct evdev_client *client) { spin_lock(&evdev->client_lock); list_del_rcu(&client->node); spin_unlock(&evdev->client_lock); synchronize_rcu(); } static int evdev_open_device(struct evdev *evdev) { int retval; retval = mutex_lock_interruptible(&evdev->mutex); if (retval) return retval; if (!evdev->exist) retval = -ENODEV; else if (!evdev->open++) { retval = input_open_device(&evdev->handle); if (retval) evdev->open--; } mutex_unlock(&evdev->mutex); return retval; } static void evdev_close_device(struct evdev *evdev) { mutex_lock(&evdev->mutex); if (evdev->exist && !--evdev->open) input_close_device(&evdev->handle); mutex_unlock(&evdev->mutex); } /* * Wake up users waiting for IO so they can disconnect from * dead device. */ static void evdev_hangup(struct evdev *evdev) { struct evdev_client *client; spin_lock(&evdev->client_lock); list_for_each_entry(client, &evdev->client_list, node) kill_fasync(&client->fasync, SIGIO, POLL_HUP); spin_unlock(&evdev->client_lock); wake_up_interruptible(&evdev->wait); } static int evdev_release(struct inode *inode, struct file *file) { struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; mutex_lock(&evdev->mutex); evdev_ungrab(evdev, client); mutex_unlock(&evdev->mutex); evdev_detach_client(evdev, client); kfree(client); evdev_close_device(evdev); return 0; } static unsigned int evdev_compute_buffer_size(struct input_dev *dev) { unsigned int n_events = max(dev->hint_events_per_packet * EVDEV_BUF_PACKETS, EVDEV_MIN_BUFFER_SIZE); return roundup_pow_of_two(n_events); } static int evdev_open(struct inode *inode, struct file *file) { struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev); unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev); struct evdev_client *client; int error; client = kzalloc(sizeof(struct evdev_client) + bufsize * sizeof(struct input_event), GFP_KERNEL); if (!client) return -ENOMEM; client->bufsize = bufsize; spin_lock_init(&client->buffer_lock); client->evdev = evdev; evdev_attach_client(evdev, client); error = evdev_open_device(evdev); if (error) goto err_free_client; file->private_data = client; nonseekable_open(inode, file); return 0; err_free_client: evdev_detach_client(evdev, client); kfree(client); return error; } static ssize_t evdev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; struct input_event event; int retval = 0; if (count != 0 && count < input_event_size()) return -EINVAL; retval = mutex_lock_interruptible(&evdev->mutex); if (retval) return retval; if (!evdev->exist) { retval = -ENODEV; goto out; } while (retval + input_event_size() <= count) { if (input_event_from_user(buffer + retval, &event)) { retval = -EFAULT; goto out; } retval += input_event_size(); input_inject_event(&evdev->handle, event.type, event.code, event.value); } out: mutex_unlock(&evdev->mutex); return retval; } static int evdev_fetch_next_event(struct evdev_client *client, struct input_event *event) { int have_event; spin_lock_irq(&client->buffer_lock); have_event = client->packet_head != client->tail; if (have_event) { *event = client->buffer[client->tail++]; client->tail &= client->bufsize - 1; } spin_unlock_irq(&client->buffer_lock); return have_event; } static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; struct input_event event; size_t read = 0; int error; if (count != 0 && count < input_event_size()) return -EINVAL; for (;;) { if (!evdev->exist) return -ENODEV; if (client->packet_head == client->tail && (file->f_flags & O_NONBLOCK)) return -EAGAIN; /* * count == 0 is special - no IO is done but we check * for error conditions (see above). */ if (count == 0) break; while (read + input_event_size() <= count && evdev_fetch_next_event(client, &event)) { if (input_event_to_user(buffer + read, &event)) return -EFAULT; read += input_event_size(); } if (read) break; if (!(file->f_flags & O_NONBLOCK)) { error = wait_event_interruptible(evdev->wait, client->packet_head != client->tail || !evdev->exist); if (error) return error; } } return read; } /* No kernel lock - fine */ static unsigned int evdev_poll(struct file *file, poll_table *wait) { struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; unsigned int mask; poll_wait(file, &evdev->wait, wait); mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR; if (client->packet_head != client->tail) mask |= POLLIN | POLLRDNORM; return mask; } #ifdef CONFIG_COMPAT #define BITS_PER_LONG_COMPAT (sizeof(compat_long_t) * 8) #define BITS_TO_LONGS_COMPAT(x) ((((x) - 1) / BITS_PER_LONG_COMPAT) + 1) #ifdef __BIG_ENDIAN static int bits_to_user(unsigned long *bits, unsigned int maxbit, unsigned int maxlen, void __user *p, int compat) { int len, i; if (compat) { len = BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t); if (len > maxlen) len = maxlen; for (i = 0; i < len / sizeof(compat_long_t); i++) if (copy_to_user((compat_long_t __user *) p + i, (compat_long_t *) bits + i + 1 - ((i % 2) << 1), sizeof(compat_long_t))) return -EFAULT; } else { len = BITS_TO_LONGS(maxbit) * sizeof(long); if (len > maxlen) len = maxlen; if (copy_to_user(p, bits, len)) return -EFAULT; } return len; } #else static int bits_to_user(unsigned long *bits, unsigned int maxbit, unsigned int maxlen, void __user *p, int compat) { int len = compat ? BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t) : BITS_TO_LONGS(maxbit) * sizeof(long); if (len > maxlen) len = maxlen; return copy_to_user(p, bits, len) ? -EFAULT : len; } #endif /* __BIG_ENDIAN */ #else static int bits_to_user(unsigned long *bits, unsigned int maxbit, unsigned int maxlen, void __user *p, int compat) { int len = BITS_TO_LONGS(maxbit) * sizeof(long); if (len > maxlen) len = maxlen; return copy_to_user(p, bits, len) ? -EFAULT : len; } #endif /* CONFIG_COMPAT */ static int str_to_user(const char *str, unsigned int maxlen, void __user *p) { int len; if (!str) return -ENOENT; len = strlen(str) + 1; if (len > maxlen) len = maxlen; return copy_to_user(p, str, len) ? -EFAULT : len; } #define OLD_KEY_MAX 0x1ff static int handle_eviocgbit(struct input_dev *dev, unsigned int type, unsigned int size, void __user *p, int compat_mode) { static unsigned long keymax_warn_time; unsigned long *bits; int len; switch (type) { case 0: bits = dev->evbit; len = EV_MAX; break; case EV_KEY: bits = dev->keybit; len = KEY_MAX; break; case EV_REL: bits = dev->relbit; len = REL_MAX; break; case EV_ABS: bits = dev->absbit; len = ABS_MAX; break; case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break; case EV_LED: bits = dev->ledbit; len = LED_MAX; break; case EV_SND: bits = dev->sndbit; len = SND_MAX; break; case EV_FF: bits = dev->ffbit; len = FF_MAX; break; case EV_SW: bits = dev->swbit; len = SW_MAX; break; default: return -EINVAL; } /* * Work around bugs in userspace programs that like to do * EVIOCGBIT(EV_KEY, KEY_MAX) and not realize that 'len' * should be in bytes, not in bits. */ if (type == EV_KEY && size == OLD_KEY_MAX) { len = OLD_KEY_MAX; if (printk_timed_ratelimit(&keymax_warn_time, 10 * 1000)) pr_warning("(EVIOCGBIT): Suspicious buffer size %u, " "limiting output to %zu bytes. See " "http://userweb.kernel.org/~dtor/eviocgbit-bug.html\n", OLD_KEY_MAX, BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long)); } return bits_to_user(bits, len, size, p, compat_mode); } #undef OLD_KEY_MAX static int evdev_handle_get_keycode(struct input_dev *dev, void __user *p) { struct input_keymap_entry ke = { .len = sizeof(unsigned int), .flags = 0, }; int __user *ip = (int __user *)p; int error; /* legacy case */ if (copy_from_user(ke.scancode, p, sizeof(unsigned int))) return -EFAULT; error = input_get_keycode(dev, &ke); if (error) return error; if (put_user(ke.keycode, ip + 1)) return -EFAULT; return 0; } static int evdev_handle_get_keycode_v2(struct input_dev *dev, void __user *p) { struct input_keymap_entry ke; int error; if (copy_from_user(&ke, p, sizeof(ke))) return -EFAULT; error = input_get_keycode(dev, &ke); if (error) return error; if (copy_to_user(p, &ke, sizeof(ke))) return -EFAULT; return 0; } static int evdev_handle_set_keycode(struct input_dev *dev, void __user *p) { struct input_keymap_entry ke = { .len = sizeof(unsigned int), .flags = 0, }; int __user *ip = (int __user *)p; if (copy_from_user(ke.scancode, p, sizeof(unsigned int))) return -EFAULT; if (get_user(ke.keycode, ip + 1)) return -EFAULT; return input_set_keycode(dev, &ke); } static int evdev_handle_set_keycode_v2(struct input_dev *dev, void __user *p) { struct input_keymap_entry ke; if (copy_from_user(&ke, p, sizeof(ke))) return -EFAULT; if (ke.len > sizeof(ke.scancode)) return -EINVAL; return input_set_keycode(dev, &ke); } static int evdev_handle_mt_request(struct input_dev *dev, unsigned int size, int __user *ip) { const struct input_mt *mt = dev->mt; unsigned int code; int max_slots; int i; if (get_user(code, &ip[0])) return -EFAULT; if (!mt || !input_is_mt_value(code)) return -EINVAL; max_slots = (size - sizeof(__u32)) / sizeof(__s32); for (i = 0; i < mt->num_slots && i < max_slots; i++) { int value = input_mt_get_value(&mt->slots[i], code); if (put_user(value, &ip[1 + i])) return -EFAULT; } return 0; } static long evdev_do_ioctl(struct file *file, unsigned int cmd, void __user *p, int compat_mode) { struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; struct input_dev *dev = evdev->handle.dev; struct input_absinfo abs; struct ff_effect effect; int __user *ip = (int __user *)p; unsigned int i, t, u, v; unsigned int size; int error; /* First we check for fixed-length commands */ switch (cmd) { case EVIOCGVERSION: return put_user(EV_VERSION, ip); case EVIOCGID: if (copy_to_user(p, &dev->id, sizeof(struct input_id))) return -EFAULT; return 0; case EVIOCGREP: if (!test_bit(EV_REP, dev->evbit)) return -ENOSYS; if (put_user(dev->rep[REP_DELAY], ip)) return -EFAULT; if (put_user(dev->rep[REP_PERIOD], ip + 1)) return -EFAULT; return 0; case EVIOCSREP: if (!test_bit(EV_REP, dev->evbit)) return -ENOSYS; if (get_user(u, ip)) return -EFAULT; if (get_user(v, ip + 1)) return -EFAULT; input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u); input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v); return 0; case EVIOCRMFF: return input_ff_erase(dev, (int)(unsigned long) p, file); case EVIOCGEFFECTS: i = test_bit(EV_FF, dev->evbit) ? dev->ff->max_effects : 0; if (put_user(i, ip)) return -EFAULT; return 0; case EVIOCGRAB: if (p) return evdev_grab(evdev, client); else return evdev_ungrab(evdev, client); case EVIOCSCLOCKID: if (copy_from_user(&i, p, sizeof(unsigned int))) return -EFAULT; if (i != CLOCK_MONOTONIC && i != CLOCK_REALTIME) return -EINVAL; client->clkid = i; return 0; case EVIOCGKEYCODE: return evdev_handle_get_keycode(dev, p); case EVIOCSKEYCODE: return evdev_handle_set_keycode(dev, p); case EVIOCGKEYCODE_V2: return evdev_handle_get_keycode_v2(dev, p); case EVIOCSKEYCODE_V2: return evdev_handle_set_keycode_v2(dev, p); } size = _IOC_SIZE(cmd); /* Now check variable-length commands */ #define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) switch (EVIOC_MASK_SIZE(cmd)) { case EVIOCGPROP(0): return bits_to_user(dev->propbit, INPUT_PROP_MAX, size, p, compat_mode); case EVIOCGMTSLOTS(0): return evdev_handle_mt_request(dev, size, ip); case EVIOCGKEY(0): return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode); case EVIOCGLED(0): return bits_to_user(dev->led, LED_MAX, size, p, compat_mode); case EVIOCGSND(0): return bits_to_user(dev->snd, SND_MAX, size, p, compat_mode); case EVIOCGSW(0): return bits_to_user(dev->sw, SW_MAX, size, p, compat_mode); case EVIOCGNAME(0): return str_to_user(dev->name, size, p); case EVIOCGPHYS(0): return str_to_user(dev->phys, size, p); case EVIOCGUNIQ(0): return str_to_user(dev->uniq, size, p); case EVIOC_MASK_SIZE(EVIOCSFF): if (input_ff_effect_from_user(p, size, &effect)) return -EFAULT; error = input_ff_upload(dev, &effect, file); if (put_user(effect.id, &(((struct ff_effect __user *)p)->id))) return -EFAULT; return error; } /* Multi-number variable-length handlers */ if (_IOC_TYPE(cmd) != 'E') return -EINVAL; if (_IOC_DIR(cmd) == _IOC_READ) { if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0))) return handle_eviocgbit(dev, _IOC_NR(cmd) & EV_MAX, size, p, compat_mode); if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { if (!dev->absinfo) return -EINVAL; t = _IOC_NR(cmd) & ABS_MAX; abs = dev->absinfo[t]; if (copy_to_user(p, &abs, min_t(size_t, size, sizeof(struct input_absinfo)))) return -EFAULT; return 0; } } if (_IOC_DIR(cmd) == _IOC_WRITE) { if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { if (!dev->absinfo) return -EINVAL; t = _IOC_NR(cmd) & ABS_MAX; if (copy_from_user(&abs, p, min_t(size_t, size, sizeof(struct input_absinfo)))) return -EFAULT; if (size < sizeof(struct input_absinfo)) abs.resolution = 0; /* We can't change number of reserved MT slots */ if (t == ABS_MT_SLOT) return -EINVAL; /* * Take event lock to ensure that we are not * changing device parameters in the middle * of event. */ spin_lock_irq(&dev->event_lock); dev->absinfo[t] = abs; spin_unlock_irq(&dev->event_lock); return 0; } } return -EINVAL; } static long evdev_ioctl_handler(struct file *file, unsigned int cmd, void __user *p, int compat_mode) { struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; int retval; retval = mutex_lock_interruptible(&evdev->mutex); if (retval) return retval; if (!evdev->exist) { retval = -ENODEV; goto out; } retval = evdev_do_ioctl(file, cmd, p, compat_mode); out: mutex_unlock(&evdev->mutex); return retval; } static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0); } #ifdef CONFIG_COMPAT static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) { return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1); } #endif static const struct file_operations evdev_fops = { .owner = THIS_MODULE, .read = evdev_read, .write = evdev_write, .poll = evdev_poll, .open = evdev_open, .release = evdev_release, .unlocked_ioctl = evdev_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = evdev_ioctl_compat, #endif .fasync = evdev_fasync, .flush = evdev_flush, .llseek = no_llseek, }; /* * Mark device non-existent. This disables writes, ioctls and * prevents new users from opening the device. Already posted * blocking reads will stay, however new ones will fail. */ static void evdev_mark_dead(struct evdev *evdev) { mutex_lock(&evdev->mutex); evdev->exist = false; mutex_unlock(&evdev->mutex); } static void evdev_cleanup(struct evdev *evdev) { struct input_handle *handle = &evdev->handle; evdev_mark_dead(evdev); evdev_hangup(evdev); cdev_del(&evdev->cdev); /* evdev is marked dead so no one else accesses evdev->open */ if (evdev->open) { input_flush_device(handle, NULL); input_close_device(handle); } } /* * Create new evdev device. Note that input core serializes calls * to connect and disconnect. */ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { struct evdev *evdev; int minor; int dev_no; int error; minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true); if (minor < 0) { error = minor; pr_err("failed to reserve new minor: %d\n", error); return error; } evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); if (!evdev) { error = -ENOMEM; goto err_free_minor; } INIT_LIST_HEAD(&evdev->client_list); spin_lock_init(&evdev->client_lock); mutex_init(&evdev->mutex); init_waitqueue_head(&evdev->wait); evdev->exist = true; dev_no = minor; /* Normalize device number if it falls into legacy range */ if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS) dev_no -= EVDEV_MINOR_BASE; dev_set_name(&evdev->dev, "event%d", dev_no); evdev->handle.dev = input_get_device(dev); evdev->handle.name = dev_name(&evdev->dev); evdev->handle.handler = handler; evdev->handle.private = evdev; evdev->dev.devt = MKDEV(INPUT_MAJOR, minor); evdev->dev.class = &input_class; evdev->dev.parent = &dev->dev; evdev->dev.release = evdev_free; device_initialize(&evdev->dev); error = input_register_handle(&evdev->handle); if (error) goto err_free_evdev; cdev_init(&evdev->cdev, &evdev_fops); evdev->cdev.kobj.parent = &evdev->dev.kobj; error = cdev_add(&evdev->cdev, evdev->dev.devt, 1); if (error) goto err_unregister_handle; error = device_add(&evdev->dev); if (error) goto err_cleanup_evdev; return 0; err_cleanup_evdev: evdev_cleanup(evdev); err_unregister_handle: input_unregister_handle(&evdev->handle); err_free_evdev: put_device(&evdev->dev); err_free_minor: input_free_minor(minor); return error; } static void evdev_disconnect(struct input_handle *handle) { struct evdev *evdev = handle->private; device_del(&evdev->dev); evdev_cleanup(evdev); input_free_minor(MINOR(evdev->dev.devt)); input_unregister_handle(handle); put_device(&evdev->dev); } static const struct input_device_id evdev_ids[] = { { .driver_info = 1 }, /* Matches all devices */ { }, /* Terminating zero entry */ }; MODULE_DEVICE_TABLE(input, evdev_ids); static struct input_handler evdev_handler = { .event = evdev_event, .events = evdev_events, .connect = evdev_connect, .disconnect = evdev_disconnect, .legacy_minors = true, .minor = EVDEV_MINOR_BASE, .name = "evdev", .id_table = evdev_ids, }; static int __init evdev_init(void) { return input_register_handler(&evdev_handler); } static void __exit evdev_exit(void) { input_unregister_handler(&evdev_handler); } module_init(evdev_init); module_exit(evdev_exit); MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("Input driver event char devices"); MODULE_LICENSE("GPL"); d3f65b4b0e82eddef30a6'>sound/aoa/soundbus/sysfs.c58
-rw-r--r--sound/arm/Kconfig19
-rw-r--r--sound/arm/Makefile8
-rw-r--r--sound/arm/aaci.c621
-rw-r--r--sound/arm/aaci.h14
-rw-r--r--sound/arm/pxa2xx-ac97-lib.c260
-rw-r--r--sound/arm/pxa2xx-ac97-regs.h100
-rw-r--r--sound/arm/pxa2xx-ac97.c190
-rw-r--r--sound/arm/pxa2xx-pcm-lib.c301
-rw-r--r--sound/arm/pxa2xx-pcm.c131
-rw-r--r--sound/arm/pxa2xx-pcm.h30
-rw-r--r--sound/atmel/Kconfig14
-rw-r--r--sound/atmel/Makefile5
-rw-r--r--sound/atmel/abdac.c602
-rw-r--r--sound/atmel/ac97c.c660
-rw-r--r--sound/atmel/ac97c.h5
-rw-r--r--sound/core/.kunitconfig5
-rw-r--r--sound/core/Kconfig244
-rw-r--r--sound/core/Makefile51
-rw-r--r--sound/core/compress_offload.c1550
-rw-r--r--sound/core/control.c2239
-rw-r--r--sound/core/control_compat.c285
-rw-r--r--sound/core/control_led.c789
-rw-r--r--sound/core/ctljack.c84
-rw-r--r--sound/core/device.c234
-rw-r--r--sound/core/hrtimer.c87
-rw-r--r--sound/core/hwdep.c246
-rw-r--r--sound/core/hwdep_compat.c39
-rw-r--r--sound/core/info.c1055
-rw-r--r--sound/core/info_oss.c68
-rw-r--r--sound/core/init.c1081
-rw-r--r--sound/core/isadma.c66
-rw-r--r--sound/core/jack.c565
-rw-r--r--sound/core/memalloc.c1192
-rw-r--r--sound/core/memory.c103
-rw-r--r--sound/core/misc.c177
-rw-r--r--sound/core/oss/Makefile3
-rw-r--r--sound/core/oss/io.c4
-rw-r--r--sound/core/oss/linear.c12
-rw-r--r--sound/core/oss/mixer_oss.c424
-rw-r--r--sound/core/oss/mulaw.c8
-rw-r--r--sound/core/oss/pcm_oss.c895
-rw-r--r--sound/core/oss/pcm_plugin.c223
-rw-r--r--sound/core/oss/pcm_plugin.h57
-rw-r--r--sound/core/oss/rate.c12
-rw-r--r--sound/core/oss/route.c8
-rw-r--r--sound/core/pcm.c778
-rw-r--r--sound/core/pcm_compat.c391
-rw-r--r--sound/core/pcm_dmaengine.c477
-rw-r--r--sound/core/pcm_drm_eld.c551
-rw-r--r--sound/core/pcm_iec958.c213
-rw-r--r--sound/core/pcm_lib.c1790
-rw-r--r--sound/core/pcm_local.h83
-rw-r--r--sound/core/pcm_memory.c457
-rw-r--r--sound/core/pcm_misc.c214
-rw-r--r--sound/core/pcm_native.c3413
-rw-r--r--sound/core/pcm_param_trace.h143
-rw-r--r--sound/core/pcm_timer.c40
-rw-r--r--sound/core/pcm_trace.h149
-rw-r--r--sound/core/rawmidi.c1263
-rw-r--r--sound/core/rawmidi_compat.c75
-rw-r--r--sound/core/rtctimer.c188
-rw-r--r--sound/core/seq/Kconfig82
-rw-r--r--sound/core/seq/Makefile40
-rw-r--r--sound/core/seq/oss/Makefile5
-rw-r--r--sound/core/seq/oss/seq_oss.c139
-rw-r--r--sound/core/seq/oss/seq_oss_device.h47
-rw-r--r--sound/core/seq/oss/seq_oss_event.c44
-rw-r--r--sound/core/seq/oss/seq_oss_event.h15
-rw-r--r--sound/core/seq/oss/seq_oss_init.c139
-rw-r--r--sound/core/seq/oss/seq_oss_ioctl.c35
-rw-r--r--sound/core/seq/oss/seq_oss_midi.c193
-rw-r--r--sound/core/seq/oss/seq_oss_midi.h15
-rw-r--r--sound/core/seq/oss/seq_oss_readq.c73
-rw-r--r--sound/core/seq/oss/seq_oss_readq.h17
-rw-r--r--sound/core/seq/oss/seq_oss_rw.c39
-rw-r--r--sound/core/seq/oss/seq_oss_synth.c346
-rw-r--r--sound/core/seq/oss/seq_oss_synth.h22
-rw-r--r--sound/core/seq/oss/seq_oss_timer.c24
-rw-r--r--sound/core/seq/oss/seq_oss_timer.h25
-rw-r--r--sound/core/seq/oss/seq_oss_writeq.c30
-rw-r--r--sound/core/seq/oss/seq_oss_writeq.h15
-rw-r--r--sound/core/seq/seq.c62
-rw-r--r--sound/core/seq/seq_clientmgr.c2153
-rw-r--r--sound/core/seq/seq_clientmgr.h68
-rw-r--r--sound/core/seq/seq_compat.c47
-rw-r--r--sound/core/seq/seq_device.c572
-rw-r--r--sound/core/seq/seq_dummy.c103
-rw-r--r--sound/core/seq/seq_fifo.c112
-rw-r--r--sound/core/seq/seq_fifo.h20
-rw-r--r--sound/core/seq/seq_info.c39
-rw-r--r--sound/core/seq/seq_info.h25
-rw-r--r--sound/core/seq/seq_lock.c34
-rw-r--r--sound/core/seq/seq_lock.h13
-rw-r--r--sound/core/seq/seq_memory.c246
-rw-r--r--sound/core/seq/seq_memory.h50
-rw-r--r--sound/core/seq/seq_midi.c166
-rw-r--r--sound/core/seq/seq_midi_emul.c92
-rw-r--r--sound/core/seq/seq_midi_event.c142
-rw-r--r--sound/core/seq/seq_ports.c447
-rw-r--r--sound/core/seq/seq_ports.h40
-rw-r--r--sound/core/seq/seq_prioq.c251
-rw-r--r--sound/core/seq/seq_prioq.h23
-rw-r--r--sound/core/seq/seq_queue.c329
-rw-r--r--sound/core/seq/seq_queue.h60
-rw-r--r--sound/core/seq/seq_system.c79
-rw-r--r--sound/core/seq/seq_system.h52
-rw-r--r--sound/core/seq/seq_timer.c266
-rw-r--r--sound/core/seq/seq_timer.h28
-rw-r--r--sound/core/seq/seq_ump_client.c539
-rw-r--r--sound/core/seq/seq_ump_convert.c1305
-rw-r--r--sound/core/seq/seq_ump_convert.h23
-rw-r--r--sound/core/seq/seq_virmidi.c237
-rw-r--r--sound/core/seq_device.c310
-rw-r--r--sound/core/sgbuf.c138
-rw-r--r--sound/core/sound.c281
-rw-r--r--sound/core/sound_kunit.c324
-rw-r--r--sound/core/sound_oss.c107
-rw-r--r--sound/core/timer.c1881
-rw-r--r--sound/core/timer_compat.c118
-rw-r--r--sound/core/ump.c1394
-rw-r--r--sound/core/ump_convert.c528
-rw-r--r--sound/core/vmaster.c448
-rw-r--r--sound/drivers/Kconfig98
-rw-r--r--sound/drivers/Makefile21
-rw-r--r--sound/drivers/aloop.c1218
-rw-r--r--sound/drivers/dummy.c419
-rw-r--r--sound/drivers/ml403-ac97cr.c1355
-rw-r--r--sound/drivers/mpu401/Makefile5
-rw-r--r--sound/drivers/mpu401/mpu401.c114
-rw-r--r--sound/drivers/mpu401/mpu401_uart.c198
-rw-r--r--sound/drivers/mtpav.c194
-rw-r--r--sound/drivers/mts64.c292
-rw-r--r--sound/drivers/opl3/Makefile7
-rw-r--r--sound/drivers/opl3/opl3_drums.c46
-rw-r--r--sound/drivers/opl3/opl3_lib.c152
-rw-r--r--sound/drivers/opl3/opl3_midi.c208
-rw-r--r--sound/drivers/opl3/opl3_oss.c54
-rw-r--r--sound/drivers/opl3/opl3_seq.c99
-rw-r--r--sound/drivers/opl3/opl3_synth.c35
-rw-r--r--sound/drivers/opl3/opl3_voice.h28
-rw-r--r--sound/drivers/opl4/Makefile6
-rw-r--r--sound/drivers/opl4/opl4_lib.c62
-rw-r--r--sound/drivers/opl4/opl4_local.h9
-rw-r--r--sound/drivers/opl4/opl4_mixer.c25
-rw-r--r--sound/drivers/opl4/opl4_proc.c38
-rw-r--r--sound/drivers/opl4/opl4_seq.c64
-rw-r--r--sound/drivers/opl4/opl4_synth.c92
-rw-r--r--sound/drivers/opl4/yrw801.c2
-rw-r--r--sound/drivers/pcm-indirect2.c573
-rw-r--r--sound/drivers/pcm-indirect2.h140
-rw-r--r--sound/drivers/pcmtest.c780
-rw-r--r--sound/drivers/pcsp/Makefile3
-rw-r--r--sound/drivers/pcsp/pcsp.c128
-rw-r--r--sound/drivers/pcsp/pcsp.h9
-rw-r--r--sound/drivers/pcsp/pcsp_input.c27
-rw-r--r--sound/drivers/pcsp/pcsp_input.h4
-rw-r--r--sound/drivers/pcsp/pcsp_lib.c85
-rw-r--r--sound/drivers/pcsp/pcsp_mixer.c15
-rw-r--r--sound/drivers/portman2x4.c209
-rw-r--r--sound/drivers/serial-generic.c376
-rw-r--r--sound/drivers/serial-u16550.c253
-rw-r--r--sound/drivers/virmidi.c79
-rw-r--r--sound/drivers/vx/Makefile3
-rw-r--r--sound/drivers/vx/vx_cmd.c17
-rw-r--r--sound/drivers/vx/vx_cmd.h15
-rw-r--r--sound/drivers/vx/vx_core.c281
-rw-r--r--sound/drivers/vx/vx_hwdep.c173
-rw-r--r--sound/drivers/vx/vx_mixer.c178
-rw-r--r--sound/drivers/vx/vx_pcm.c196
-rw-r--r--sound/drivers/vx/vx_uer.c42
-rw-r--r--sound/firewire/Kconfig202
-rw-r--r--sound/firewire/Makefile18
-rw-r--r--sound/firewire/amdtp-am824.c420
-rw-r--r--sound/firewire/amdtp-am824.h49
-rw-r--r--sound/firewire/amdtp-stream-trace.h81
-rw-r--r--sound/firewire/amdtp-stream.c2160
-rw-r--r--sound/firewire/amdtp-stream.h372
-rw-r--r--sound/firewire/bebob/Makefile6
-rw-r--r--sound/firewire/bebob/bebob.c510
-rw-r--r--sound/firewire/bebob/bebob.h256
-rw-r--r--sound/firewire/bebob/bebob_command.c331
-rw-r--r--sound/firewire/bebob/bebob_focusrite.c322
-rw-r--r--sound/firewire/bebob/bebob_hwdep.c177
-rw-r--r--sound/firewire/bebob/bebob_maudio.c785
-rw-r--r--sound/firewire/bebob/bebob_midi.c140
-rw-r--r--sound/firewire/bebob/bebob_pcm.c368
-rw-r--r--sound/firewire/bebob/bebob_proc.c189
-rw-r--r--sound/firewire/bebob/bebob_stream.c987
-rw-r--r--sound/firewire/bebob/bebob_terratec.c53
-rw-r--r--sound/firewire/bebob/bebob_yamaha_terratec.c64
-rw-r--r--sound/firewire/cmp.c351
-rw-r--r--sound/firewire/cmp.h52
-rw-r--r--sound/firewire/dice/Makefile6
-rw-r--r--sound/firewire/dice/dice-alesis.c76
-rw-r--r--sound/firewire/dice/dice-extension.c175
-rw-r--r--sound/firewire/dice/dice-focusrite.c23
-rw-r--r--sound/firewire/dice/dice-harman.c24
-rw-r--r--sound/firewire/dice/dice-hwdep.c172
-rw-r--r--sound/firewire/dice/dice-interface.h378
-rw-r--r--sound/firewire/dice/dice-midi.c151
-rw-r--r--sound/firewire/dice/dice-mytek.c46
-rw-r--r--sound/firewire/dice/dice-pcm.c449
-rw-r--r--sound/firewire/dice/dice-presonus.c60
-rw-r--r--sound/firewire/dice/dice-proc.c307
-rw-r--r--sound/firewire/dice/dice-stream.c699
-rw-r--r--sound/firewire/dice/dice-tcelectronic.c104
-rw-r--r--sound/firewire/dice/dice-teac.c43
-rw-r--r--sound/firewire/dice/dice-transaction.c368
-rw-r--r--sound/firewire/dice/dice-weiss.c104
-rw-r--r--sound/firewire/dice/dice.c501
-rw-r--r--sound/firewire/dice/dice.h238
-rw-r--r--sound/firewire/digi00x/Makefile5
-rw-r--r--sound/firewire/digi00x/amdtp-dot.c412
-rw-r--r--sound/firewire/digi00x/digi00x-hwdep.c181
-rw-r--r--sound/firewire/digi00x/digi00x-midi.c167
-rw-r--r--sound/firewire/digi00x/digi00x-pcm.c346
-rw-r--r--sound/firewire/digi00x/digi00x-proc.c86
-rw-r--r--sound/firewire/digi00x/digi00x-stream.c450
-rw-r--r--sound/firewire/digi00x/digi00x-transaction.c81
-rw-r--r--sound/firewire/digi00x/digi00x.c176
-rw-r--r--sound/firewire/digi00x/digi00x.h160
-rw-r--r--sound/firewire/fcp.c398
-rw-r--r--sound/firewire/fcp.h34
-rw-r--r--sound/firewire/fireface/Makefile5
-rw-r--r--sound/firewire/fireface/amdtp-ff.c167
-rw-r--r--sound/firewire/fireface/ff-hwdep.c190
-rw-r--r--sound/firewire/fireface/ff-midi.c122
-rw-r--r--sound/firewire/fireface/ff-pcm.c387
-rw-r--r--sound/firewire/fireface/ff-proc.c62
-rw-r--r--sound/firewire/fireface/ff-protocol-former.c733
-rw-r--r--sound/firewire/fireface/ff-protocol-latter.c540
-rw-r--r--sound/firewire/fireface/ff-stream.c276
-rw-r--r--sound/firewire/fireface/ff-transaction.c233
-rw-r--r--sound/firewire/fireface/ff.c266
-rw-r--r--sound/firewire/fireface/ff.h171
-rw-r--r--sound/firewire/fireworks/Makefile5
-rw-r--r--sound/firewire/fireworks/fireworks.c366
-rw-r--r--sound/firewire/fireworks/fireworks.h227
-rw-r--r--sound/firewire/fireworks/fireworks_command.c371
-rw-r--r--sound/firewire/fireworks/fireworks_hwdep.c313
-rw-r--r--sound/firewire/fireworks/fireworks_midi.c138
-rw-r--r--sound/firewire/fireworks/fireworks_pcm.c397
-rw-r--r--sound/firewire/fireworks/fireworks_proc.c223
-rw-r--r--sound/firewire/fireworks/fireworks_stream.c368
-rw-r--r--sound/firewire/fireworks/fireworks_transaction.c313
-rw-r--r--sound/firewire/isight.c733
-rw-r--r--sound/firewire/iso-resources.c226
-rw-r--r--sound/firewire/iso-resources.h39
-rw-r--r--sound/firewire/lib.c72
-rw-r--r--sound/firewire/lib.h26
-rw-r--r--sound/firewire/motu/Makefile9
-rw-r--r--sound/firewire/motu/amdtp-motu-trace.h85
-rw-r--r--sound/firewire/motu/amdtp-motu.c485
-rw-r--r--sound/firewire/motu/motu-command-dsp-message-parser.c179
-rw-r--r--sound/firewire/motu/motu-hwdep.c285
-rw-r--r--sound/firewire/motu/motu-midi.c126
-rw-r--r--sound/firewire/motu/motu-pcm.c365
-rw-r--r--sound/firewire/motu/motu-proc.c109
-rw-r--r--sound/firewire/motu/motu-protocol-v1.c467
-rw-r--r--sound/firewire/motu/motu-protocol-v2.c321
-rw-r--r--sound/firewire/motu/motu-protocol-v3.c346
-rw-r--r--sound/firewire/motu/motu-register-dsp-message-parser.c413
-rw-r--r--sound/firewire/motu/motu-stream.c429
-rw-r--r--sound/firewire/motu/motu-transaction.c135
-rw-r--r--sound/firewire/motu/motu.c207
-rw-r--r--sound/firewire/motu/motu.h299
-rw-r--r--sound/firewire/oxfw/Makefile4
-rw-r--r--sound/firewire/oxfw/oxfw-command.c158
-rw-r--r--sound/firewire/oxfw/oxfw-hwdep.c170
-rw-r--r--sound/firewire/oxfw/oxfw-midi.c180
-rw-r--r--sound/firewire/oxfw/oxfw-pcm.c435
-rw-r--r--sound/firewire/oxfw/oxfw-proc.c104
-rw-r--r--sound/firewire/oxfw/oxfw-scs1x.c420
-rw-r--r--sound/firewire/oxfw/oxfw-spkr.c320
-rw-r--r--sound/firewire/oxfw/oxfw-stream.c889
-rw-r--r--sound/firewire/oxfw/oxfw.c404
-rw-r--r--sound/firewire/oxfw/oxfw.h166
-rw-r--r--sound/firewire/packets-buffer.c77
-rw-r--r--sound/firewire/packets-buffer.h27
-rw-r--r--sound/firewire/tascam/Makefile5
-rw-r--r--sound/firewire/tascam/amdtp-tascam.c252
-rw-r--r--sound/firewire/tascam/tascam-hwdep.c260
-rw-r--r--sound/firewire/tascam/tascam-midi.c129
-rw-r--r--sound/firewire/tascam/tascam-pcm.c275
-rw-r--r--sound/firewire/tascam/tascam-proc.c79
-rw-r--r--sound/firewire/tascam/tascam-stream.c551
-rw-r--r--sound/firewire/tascam/tascam-transaction.c399
-rw-r--r--sound/firewire/tascam/tascam.c235
-rw-r--r--sound/firewire/tascam/tascam.h212
-rw-r--r--sound/hda/Kconfig9
-rw-r--r--sound/hda/Makefile8
-rw-r--r--sound/hda/codecs/Kconfig137
-rw-r--r--sound/hda/codecs/Makefile34
-rw-r--r--sound/hda/codecs/analog.c1176
-rw-r--r--sound/hda/codecs/ca0110.c87
-rw-r--r--sound/hda/codecs/ca0132.c10078
-rw-r--r--sound/hda/codecs/ca0132_regs.h396
-rw-r--r--sound/hda/codecs/cirrus/Kconfig44
-rw-r--r--sound/hda/codecs/cirrus/Makefile10
-rw-r--r--sound/hda/codecs/cirrus/cs420x.c786
-rw-r--r--sound/hda/codecs/cirrus/cs421x.c590
-rw-r--r--sound/hda/codecs/cirrus/cs8409-tables.c623
-rw-r--r--sound/hda/codecs/cirrus/cs8409.c1475
-rw-r--r--sound/hda/codecs/cirrus/cs8409.h377
-rw-r--r--sound/hda/codecs/cm9825.c312
-rw-r--r--sound/hda/codecs/cmedia.c106
-rw-r--r--sound/hda/codecs/conexant.c1333
-rw-r--r--sound/hda/codecs/generic.c6145
-rw-r--r--sound/hda/codecs/generic.h357
-rw-r--r--sound/hda/codecs/hdmi/Kconfig88
-rw-r--r--sound/hda/codecs/hdmi/Makefile18
-rw-r--r--sound/hda/codecs/hdmi/atihdmi.c615
-rw-r--r--sound/hda/codecs/hdmi/eld.c230
-rw-r--r--sound/hda/codecs/hdmi/hdmi.c2363
-rw-r--r--sound/hda/codecs/hdmi/hdmi_local.h302
-rw-r--r--sound/hda/codecs/hdmi/intelhdmi.c812
-rw-r--r--sound/hda/codecs/hdmi/nvhdmi-mcp.c383
-rw-r--r--sound/hda/codecs/hdmi/nvhdmi.c240
-rw-r--r--sound/hda/codecs/hdmi/simplehdmi.c251
-rw-r--r--sound/hda/codecs/hdmi/tegrahdmi.c318
-rw-r--r--sound/hda/codecs/helpers/hp_x360.c95
-rw-r--r--sound/hda/codecs/helpers/ideapad_hotkey_led.c36
-rw-r--r--sound/hda/codecs/helpers/ideapad_s740.c492
-rw-r--r--sound/hda/codecs/helpers/thinkpad.c36
-rw-r--r--sound/hda/codecs/realtek/Kconfig104
-rw-r--r--sound/hda/codecs/realtek/Makefile26
-rw-r--r--sound/hda/codecs/realtek/alc260.c290
-rw-r--r--sound/hda/codecs/realtek/alc262.c213
-rw-r--r--sound/hda/codecs/realtek/alc268.c189
-rw-r--r--sound/hda/codecs/realtek/alc269.c8311
-rw-r--r--sound/hda/codecs/realtek/alc662.c1116
-rw-r--r--sound/hda/codecs/realtek/alc680.c67
-rw-r--r--sound/hda/codecs/realtek/alc861.c163
-rw-r--r--sound/hda/codecs/realtek/alc861vd.c137
-rw-r--r--sound/hda/codecs/realtek/alc880.c509
-rw-r--r--sound/hda/codecs/realtek/alc882.c861
-rw-r--r--sound/hda/codecs/realtek/realtek.c2271
-rw-r--r--sound/hda/codecs/realtek/realtek.h319
-rw-r--r--sound/hda/codecs/senarytech.c249
-rw-r--r--sound/hda/codecs/si3054.c (renamed from sound/pci/hda/patch_si3054.c)138
-rw-r--r--sound/hda/codecs/side-codecs/Kconfig143
-rw-r--r--sound/hda/codecs/side-codecs/Makefile28
-rw-r--r--sound/hda/codecs/side-codecs/cirrus_scodec.c73
-rw-r--r--sound/hda/codecs/side-codecs/cirrus_scodec.h13
-rw-r--r--sound/hda/codecs/side-codecs/cirrus_scodec_test.c332
-rw-r--r--sound/hda/codecs/side-codecs/cs35l41_hda.c2098
-rw-r--r--sound/hda/codecs/side-codecs/cs35l41_hda.h110
-rw-r--r--sound/hda/codecs/side-codecs/cs35l41_hda_i2c.c69
-rw-r--r--sound/hda/codecs/side-codecs/cs35l41_hda_property.c582
-rw-r--r--sound/hda/codecs/side-codecs/cs35l41_hda_property.h18
-rw-r--r--sound/hda/codecs/side-codecs/cs35l41_hda_spi.c64
-rw-r--r--sound/hda/codecs/side-codecs/cs35l56_hda.c1286
-rw-r--r--sound/hda/codecs/side-codecs/cs35l56_hda.h56
-rw-r--r--sound/hda/codecs/side-codecs/cs35l56_hda_i2c.c85
-rw-r--r--sound/hda/codecs/side-codecs/cs35l56_hda_spi.c88
-rw-r--r--sound/hda/codecs/side-codecs/hda_component.c209
-rw-r--r--sound/hda/codecs/side-codecs/hda_component.h102
-rw-r--r--sound/hda/codecs/side-codecs/tas2781_hda.c416
-rw-r--r--sound/hda/codecs/side-codecs/tas2781_hda.h90
-rw-r--r--sound/hda/codecs/side-codecs/tas2781_hda_i2c.c834
-rw-r--r--sound/hda/codecs/side-codecs/tas2781_hda_spi.c956
-rw-r--r--sound/hda/codecs/sigmatel.c5169
-rw-r--r--sound/hda/codecs/via.c1174
-rw-r--r--sound/hda/common/Kconfig97
-rw-r--r--sound/hda/common/Makefile13
-rw-r--r--sound/hda/common/auto_parser.c1104
-rw-r--r--sound/hda/common/beep.c345
-rw-r--r--sound/hda/common/bind.c346
-rw-r--r--sound/hda/common/codec.c4048
-rw-r--r--sound/hda/common/controller.c1322
-rw-r--r--sound/hda/common/controller_trace.h99
-rw-r--r--sound/hda/common/hda_auto_parser.h118
-rw-r--r--sound/hda/common/hda_beep.h46
-rw-r--r--sound/hda/common/hda_controller.h215
-rw-r--r--sound/hda/common/hda_jack.h195
-rw-r--r--sound/hda/common/hda_local.h (renamed from sound/pci/hda/hda_local.h)638
-rw-r--r--sound/hda/common/hwdep.c119
-rw-r--r--sound/hda/common/jack.c770
-rw-r--r--sound/hda/common/proc.c (renamed from sound/pci/hda/hda_proc.c)438
-rw-r--r--sound/hda/common/sysfs.c764
-rw-r--r--sound/hda/controllers/Kconfig42
-rw-r--r--sound/hda/controllers/Makefile13
-rw-r--r--sound/hda/controllers/acpi.c325
-rw-r--r--sound/hda/controllers/intel.c2834
-rw-r--r--sound/hda/controllers/intel.h38
-rw-r--r--sound/hda/controllers/intel_trace.h52
-rw-r--r--sound/hda/controllers/tegra.c652
-rw-r--r--sound/hda/core/Kconfig51
-rw-r--r--sound/hda/core/Makefile22
-rw-r--r--sound/hda/core/array.c52
-rw-r--r--sound/hda/core/bus.c285
-rw-r--r--sound/hda/core/component.c351
-rw-r--r--sound/hda/core/controller.c766
-rw-r--r--sound/hda/core/device.c1167
-rw-r--r--sound/hda/core/ext/Makefile4
-rw-r--r--sound/hda/core/ext/bus.c142
-rw-r--r--sound/hda/core/ext/controller.c410
-rw-r--r--sound/hda/core/ext/stream.c449
-rw-r--r--sound/hda/core/hda_bus_type.c97
-rw-r--r--sound/hda/core/hdmi_chmap.c868
-rw-r--r--sound/hda/core/i915.c208
-rw-r--r--sound/hda/core/intel-dsp-config.c851
-rw-r--r--sound/hda/core/intel-nhlt.c388
-rw-r--r--sound/hda/core/intel-sdw-acpi.c204
-rw-r--r--sound/hda/core/local.h25
-rw-r--r--sound/hda/core/regmap.c588
-rw-r--r--sound/hda/core/stream.c1004
-rw-r--r--sound/hda/core/sysfs.c469
-rw-r--r--sound/hda/core/trace.c6
-rw-r--r--sound/hda/core/trace.h101
-rw-r--r--sound/i2c/Makefile7
-rw-r--r--sound/i2c/cs8427.c161
-rw-r--r--sound/i2c/i2c.c38
-rw-r--r--sound/i2c/other/Makefile13
-rw-r--r--sound/i2c/other/ak4113.c141
-rw-r--r--sound/i2c/other/ak4114.c151
-rw-r--r--sound/i2c/other/ak4117.c117
-rw-r--r--sound/i2c/other/ak4xxx-adda.c78
-rw-r--r--sound/i2c/other/pt2258.c31
-rw-r--r--sound/i2c/other/tea575x-tuner.c365
-rw-r--r--sound/i2c/tea6330t.c67
-rw-r--r--sound/isa/Kconfig55
-rw-r--r--sound/isa/Makefile19
-rw-r--r--sound/isa/ad1816a/Makefile3
-rw-r--r--sound/isa/ad1816a/ad1816a.c172
-rw-r--r--sound/isa/ad1816a/ad1816a_lib.c310
-rw-r--r--sound/isa/ad1848/Makefile3
-rw-r--r--sound/isa/ad1848/ad1848.c91
-rw-r--r--sound/isa/adlib.c58
-rw-r--r--sound/isa/als100.c137
-rw-r--r--sound/isa/azt2320.c134
-rw-r--r--sound/isa/cmi8328.c460
-rw-r--r--sound/isa/cmi8330.c221
-rw-r--r--sound/isa/cs423x/Makefile5
-rw-r--r--sound/isa/cs423x/cs4231.c97
-rw-r--r--sound/isa/cs423x/cs4236.c267
-rw-r--r--sound/isa/cs423x/cs4236_lib.c252
-rw-r--r--sound/isa/es1688/Makefile5
-rw-r--r--sound/isa/es1688/es1688.c126
-rw-r--r--sound/isa/es1688/es1688_lib.c478
-rw-r--r--sound/isa/es18xx.c609
-rw-r--r--sound/isa/galaxy/Makefile5
-rw-r--r--sound/isa/galaxy/azt1605.c15
-rw-r--r--sound/isa/galaxy/azt2316.c15
-rw-r--r--sound/isa/galaxy/galaxy.c167
-rw-r--r--sound/isa/gus/Makefile13
-rw-r--r--sound/isa/gus/gus_dma.c153
-rw-r--r--sound/isa/gus/gus_dram.c25
-rw-r--r--sound/isa/gus/gus_instr.c172
-rw-r--r--sound/isa/gus/gus_io.c238
-rw-r--r--sound/isa/gus/gus_irq.c29
-rw-r--r--sound/isa/gus/gus_main.c189
-rw-r--r--sound/isa/gus/gus_mem.c83
-rw-r--r--sound/isa/gus/gus_mem_proc.c19
-rw-r--r--sound/isa/gus/gus_mixer.c50
-rw-r--r--sound/isa/gus/gus_pcm.c397
-rw-r--r--sound/isa/gus/gus_reset.c114
-rw-r--r--sound/isa/gus/gus_tables.h17
-rw-r--r--sound/isa/gus/gus_timer.c41
-rw-r--r--sound/isa/gus/gus_uart.c80
-rw-r--r--sound/isa/gus/gus_volume.c34
-rw-r--r--sound/isa/gus/gusclassic.c106
-rw-r--r--sound/isa/gus/gusextreme.c170
-rw-r--r--sound/isa/gus/gusmax.c207
-rw-r--r--sound/isa/gus/interwave.c425
-rw-r--r--sound/isa/msnd/Makefile7
-rw-r--r--sound/isa/msnd/msnd.c136
-rw-r--r--sound/isa/msnd/msnd.h27
-rw-r--r--sound/isa/msnd/msnd_classic.h15
-rw-r--r--sound/isa/msnd/msnd_midi.c181
-rw-r--r--sound/isa/msnd/msnd_pinnacle.c451
-rw-r--r--sound/isa/msnd/msnd_pinnacle.h15
-rw-r--r--sound/isa/msnd/msnd_pinnacle_mixer.c38
-rw-r--r--sound/isa/opl3sa2.c313
-rw-r--r--sound/isa/opti9xx/Makefile9
-rw-r--r--sound/isa/opti9xx/miro.c463
-rw-r--r--sound/isa/opti9xx/opti92x-ad1848.c445
-rw-r--r--sound/isa/sb/Makefile19
-rw-r--r--sound/isa/sb/emu8000.c275
-rw-r--r--sound/isa/sb/emu8000_callback.c26
-rw-r--r--sound/isa/sb/emu8000_local.h15
-rw-r--r--sound/isa/sb/emu8000_patch.c43
-rw-r--r--sound/isa/sb/emu8000_pcm.c270
-rw-r--r--sound/isa/sb/emu8000_synth.c59
-rw-r--r--sound/isa/sb/jazz16.c151
-rw-r--r--sound/isa/sb/sb16.c241
-rw-r--r--sound/isa/sb/sb16_csp.c362
-rw-r--r--sound/isa/sb/sb16_main.c245
-rw-r--r--sound/isa/sb/sb8.c161
-rw-r--r--sound/isa/sb/sb8_main.c265
-rw-r--r--sound/isa/sb/sb8_midi.c169
-rw-r--r--sound/isa/sb/sb_common.c148
-rw-r--r--sound/isa/sb/sb_mixer.c231
-rw-r--r--sound/isa/sc6000.c358
-rw-r--r--sound/isa/sscape.c459
-rw-r--r--sound/isa/wavefront/Makefile3
-rw-r--r--sound/isa/wavefront/wavefront.c233
-rw-r--r--sound/isa/wavefront/wavefront_fx.c68
-rw-r--r--sound/isa/wavefront/wavefront_midi.c195
-rw-r--r--sound/isa/wavefront/wavefront_synth.c350
-rw-r--r--sound/isa/wss/Makefile3
-rw-r--r--sound/isa/wss/wss_lib.c656
-rw-r--r--sound/last.c30
-rw-r--r--sound/mips/Kconfig25
-rw-r--r--sound/mips/Makefile8
-rw-r--r--sound/mips/ad1843.c18
-rw-r--r--sound/mips/au1x00.c695
-rw-r--r--sound/mips/hal2.c185
-rw-r--r--sound/mips/hal2.h15
-rw-r--r--sound/mips/sgio2audio.c158
-rw-r--r--sound/mips/snd-n64.c369
-rw-r--r--sound/oss/.gitignore3
-rw-r--r--sound/oss/CHANGELOG369
-rw-r--r--sound/oss/Kconfig547
-rw-r--r--sound/oss/Makefile109
-rw-r--r--sound/oss/README.FIRST6
-rw-r--r--sound/oss/ac97_codec.c1203
-rw-r--r--sound/oss/ad1848.c3069
-rw-r--r--sound/oss/ad1848.h24
-rw-r--r--sound/oss/ad1848_mixer.h253
-rw-r--r--sound/oss/aedsp16.c1373
-rw-r--r--sound/oss/au1550_ac97.c2147
-rw-r--r--sound/oss/audio.c985
-rw-r--r--sound/oss/bin2hex.c39
-rw-r--r--sound/oss/coproc.h12
-rw-r--r--sound/oss/dev_table.c258
-rw-r--r--sound/oss/dev_table.h390
-rw-r--r--sound/oss/dmabuf.c1268
-rw-r--r--sound/oss/dmasound/Kconfig7
-rw-r--r--sound/oss/dmasound/Makefile1
-rw-r--r--sound/oss/dmasound/dmasound.h11
-rw-r--r--sound/oss/dmasound/dmasound_atari.c23
-rw-r--r--sound/oss/dmasound/dmasound_core.c101
-rw-r--r--sound/oss/dmasound/dmasound_paula.c32
-rw-r--r--sound/oss/dmasound/dmasound_q40.c3
-rw-r--r--sound/oss/hex2hex.c101
-rw-r--r--sound/oss/kahlua.c231
-rw-r--r--sound/oss/midi_ctrl.h22
-rw-r--r--sound/oss/midi_synth.c716
-rw-r--r--sound/oss/midi_synth.h47
-rw-r--r--sound/oss/midibuf.c425
-rw-r--r--sound/oss/mpu401.c1806
-rw-r--r--sound/oss/mpu401.h11
-rw-r--r--sound/oss/msnd.c413
-rw-r--r--sound/oss/msnd.h278
-rw-r--r--sound/oss/msnd_classic.c3
-rw-r--r--sound/oss/msnd_classic.h185
-rw-r--r--sound/oss/msnd_pinnacle.c1931
-rw-r--r--sound/oss/msnd_pinnacle.h246
-rw-r--r--sound/oss/opl3.c1251
-rw-r--r--sound/oss/opl3_hw.h246
-rw-r--r--sound/oss/os.h46
-rw-r--r--sound/oss/pas2.h17
-rw-r--r--sound/oss/pas2_card.c455
-rw-r--r--sound/oss/pas2_midi.c262
-rw-r--r--sound/oss/pas2_mixer.c336
-rw-r--r--sound/oss/pas2_pcm.c437
-rw-r--r--sound/oss/pss.c1266
-rw-r--r--sound/oss/sb.h185
-rw-r--r--sound/oss/sb_audio.c1098
-rw-r--r--sound/oss/sb_card.c354
-rw-r--r--sound/oss/sb_card.h149
-rw-r--r--sound/oss/sb_common.c1292
-rw-r--r--sound/oss/sb_ess.c1831
-rw-r--r--sound/oss/sb_ess.h34
-rw-r--r--sound/oss/sb_midi.c206
-rw-r--r--sound/oss/sb_mixer.c770
-rw-r--r--sound/oss/sb_mixer.h105
-rw-r--r--sound/oss/sequencer.c1671
-rw-r--r--sound/oss/sound_calls.h87
-rw-r--r--sound/oss/sound_config.h147
-rw-r--r--sound/oss/sound_firmware.h2
-rw-r--r--sound/oss/sound_timer.c327
-rw-r--r--sound/oss/soundcard.c755
-rw-r--r--sound/oss/soundvers.h2
-rw-r--r--sound/oss/swarm_cs4297a.c2768
-rw-r--r--sound/oss/sys_timer.c285
-rw-r--r--sound/oss/trix.c525
-rw-r--r--sound/oss/tuning.h23
-rw-r--r--sound/oss/uart401.c482
-rw-r--r--sound/oss/uart6850.c361
-rw-r--r--sound/oss/ulaw.h69
-rw-r--r--sound/oss/v_midi.c290
-rw-r--r--sound/oss/v_midi.h14
-rw-r--r--sound/oss/vidc.c558
-rw-r--r--sound/oss/vidc.h63
-rw-r--r--sound/oss/vidc_fill.S218
-rw-r--r--sound/oss/vwsnd.c3498
-rw-r--r--sound/oss/waveartist.c2025
-rw-r--r--sound/oss/waveartist.h92
-rw-r--r--sound/parisc/Kconfig1
-rw-r--r--sound/parisc/Makefile3
-rw-r--r--sound/parisc/harmony.c231
-rw-r--r--sound/parisc/harmony.h1
-rw-r--r--sound/pci/Kconfig163
-rw-r--r--sound/pci/Makefile51
-rw-r--r--sound/pci/ac97/Makefile3
-rw-r--r--sound/pci/ac97/ac97_codec.c553
-rw-r--r--sound/pci/ac97/ac97_id.h17
-rw-r--r--sound/pci/ac97/ac97_local.h21
-rw-r--r--sound/pci/ac97/ac97_patch.c637
-rw-r--r--sound/pci/ac97/ac97_patch.h19
-rw-r--r--sound/pci/ac97/ac97_pcm.c93
-rw-r--r--sound/pci/ac97/ac97_proc.c67
-rw-r--r--sound/pci/ad1889.c333
-rw-r--r--sound/pci/ad1889.h1
-rw-r--r--sound/pci/ak4531_codec.c72
-rw-r--r--sound/pci/ali5451/Makefile3
-rw-r--r--sound/pci/ali5451/ali5451.c553
-rw-r--r--sound/pci/als300.c304
-rw-r--r--sound/pci/als4000.c317
-rw-r--r--sound/pci/asihpi/Makefile3
-rw-r--r--sound/pci/asihpi/asihpi.c1773
-rw-r--r--sound/pci/asihpi/hpi.h1309
-rw-r--r--sound/pci/asihpi/hpi6000.c410
-rw-r--r--sound/pci/asihpi/hpi6000.h15
-rw-r--r--sound/pci/asihpi/hpi6205.c814
-rw-r--r--sound/pci/asihpi/hpi6205.h41
-rw-r--r--sound/pci/asihpi/hpi_internal.h1319
-rw-r--r--sound/pci/asihpi/hpi_version.h33
-rw-r--r--sound/pci/asihpi/hpicmn.c636
-rw-r--r--sound/pci/asihpi/hpicmn.h67
-rw-r--r--sound/pci/asihpi/hpidebug.c178
-rw-r--r--sound/pci/asihpi/hpidebug.h343
-rw-r--r--sound/pci/asihpi/hpidspcd.c177
-rw-r--r--sound/pci/asihpi/hpidspcd.h85
-rw-r--r--sound/pci/asihpi/hpifunc.c2612
-rw-r--r--sound/pci/asihpi/hpimsginit.c74
-rw-r--r--sound/pci/asihpi/hpimsginit.h27
-rw-r--r--sound/pci/asihpi/hpimsgx.c260
-rw-r--r--sound/pci/asihpi/hpimsgx.h15
-rw-r--r--sound/pci/asihpi/hpioctl.c375
-rw-r--r--sound/pci/asihpi/hpioctl.h21
-rw-r--r--sound/pci/asihpi/hpios.c31
-rw-r--r--sound/pci/asihpi/hpios.h51
-rw-r--r--sound/pci/asihpi/hpipcida.h15
-rw-r--r--sound/pci/atiixp.c411
-rw-r--r--sound/pci/atiixp_modem.c339
-rw-r--r--sound/pci/au88x0/Makefile7
-rw-r--r--sound/pci/au88x0/au8810.c3
-rw-r--r--sound/pci/au88x0/au8810.h3
-rw-r--r--sound/pci/au88x0/au8820.c3
-rw-r--r--sound/pci/au88x0/au8820.h3
-rw-r--r--sound/pci/au88x0/au8830.c3
-rw-r--r--sound/pci/au88x0/au8830.h3
-rw-r--r--sound/pci/au88x0/au88x0.c268
-rw-r--r--sound/pci/au88x0/au88x0.h48
-rw-r--r--sound/pci/au88x0/au88x0_a3d.c100
-rw-r--r--sound/pci/au88x0/au88x0_a3d.h14
-rw-r--r--sound/pci/au88x0/au88x0_a3ddata.c22
-rw-r--r--sound/pci/au88x0/au88x0_core.c276
-rw-r--r--sound/pci/au88x0/au88x0_eq.c72
-rw-r--r--sound/pci/au88x0/au88x0_eq.h1
-rw-r--r--sound/pci/au88x0/au88x0_eqdata.c19
-rw-r--r--sound/pci/au88x0/au88x0_game.c25
-rw-r--r--sound/pci/au88x0/au88x0_mixer.c19
-rw-r--r--sound/pci/au88x0/au88x0_mpu401.c34
-rw-r--r--sound/pci/au88x0/au88x0_pcm.c281
-rw-r--r--sound/pci/au88x0/au88x0_synth.c76
-rw-r--r--sound/pci/au88x0/au88x0_wt.h1
-rw-r--r--sound/pci/au88x0/au88x0_xtalk.c199
-rw-r--r--sound/pci/au88x0/au88x0_xtalk.h14
-rw-r--r--sound/pci/aw2/Makefile3
-rw-r--r--sound/pci/aw2/aw2-alsa.c319
-rw-r--r--sound/pci/aw2/aw2-saa7146.c26
-rw-r--r--sound/pci/aw2/aw2-saa7146.h20
-rw-r--r--sound/pci/aw2/aw2-tsl.c19
-rw-r--r--sound/pci/aw2/saa7146.h15
-rw-r--r--sound/pci/azt3328.c1591
-rw-r--r--sound/pci/azt3328.h1
-rw-r--r--sound/pci/bt87x.c289
-rw-r--r--sound/pci/ca0106/Makefile4
-rw-r--r--sound/pci/ca0106/ca0106.h54
-rw-r--r--sound/pci/ca0106/ca0106_main.c555
-rw-r--r--sound/pci/ca0106/ca0106_mixer.c148
-rw-r--r--sound/pci/ca0106/ca0106_proc.c96
-rw-r--r--sound/pci/ca0106/ca_midi.c203
-rw-r--r--sound/pci/ca0106/ca_midi.h16
-rw-r--r--sound/pci/cmipci.c760
-rw-r--r--sound/pci/cs4281.c430
-rw-r--r--sound/pci/cs46xx/Makefile1
-rw-r--r--sound/pci/cs46xx/cs46xx.c152
-rw-r--r--sound/pci/cs46xx/cs46xx.h1732
-rw-r--r--sound/pci/cs46xx/cs46xx_dsp_scb_types.h1198
-rw-r--r--sound/pci/cs46xx/cs46xx_dsp_spos.h213
-rw-r--r--sound/pci/cs46xx/cs46xx_dsp_task_types.h237
-rw-r--r--sound/pci/cs46xx/cs46xx_image.h3468
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c1039
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.h23
-rw-r--r--sound/pci/cs46xx/dsp_spos.c423
-rw-r--r--sound/pci/cs46xx/dsp_spos.h17
-rw-r--r--sound/pci/cs46xx/dsp_spos_scb_lib.c185
-rw-r--r--sound/pci/cs46xx/imgs/cwc4630.h320
-rw-r--r--sound/pci/cs46xx/imgs/cwcasync.h176
-rw-r--r--sound/pci/cs46xx/imgs/cwcbinhack.h48
-rw-r--r--sound/pci/cs46xx/imgs/cwcdma.asp170
-rw-r--r--sound/pci/cs46xx/imgs/cwcdma.h68
-rw-r--r--sound/pci/cs46xx/imgs/cwcsnoop.h46
-rw-r--r--sound/pci/cs5530.c168
-rw-r--r--sound/pci/cs5535audio/Makefile3
-rw-r--r--sound/pci/cs5535audio/cs5535audio.c231
-rw-r--r--sound/pci/cs5535audio/cs5535audio.h22
-rw-r--r--sound/pci/cs5535audio/cs5535audio_olpc.c34
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pcm.c91
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pm.c50
-rw-r--r--sound/pci/ctxfi/Makefile3
-rw-r--r--sound/pci/ctxfi/ct20k1reg.h11
-rw-r--r--sound/pci/ctxfi/ct20k2reg.h8
-rw-r--r--sound/pci/ctxfi/ctamixer.c113
-rw-r--r--sound/pci/ctxfi/ctamixer.h21
-rw-r--r--sound/pci/ctxfi/ctatc.c367
-rw-r--r--sound/pci/ctxfi/ctatc.h38
-rw-r--r--sound/pci/ctxfi/ctdaio.c253
-rw-r--r--sound/pci/ctxfi/ctdaio.h26
-rw-r--r--sound/pci/ctxfi/cthardware.c18
-rw-r--r--sound/pci/ctxfi/cthardware.h37
-rw-r--r--sound/pci/ctxfi/cthw20k1.c148
-rw-r--r--sound/pci/ctxfi/cthw20k1.h8
-rw-r--r--sound/pci/ctxfi/cthw20k2.c522
-rw-r--r--sound/pci/ctxfi/cthw20k2.h8
-rw-r--r--sound/pci/ctxfi/ctimap.c8
-rw-r--r--sound/pci/ctxfi/ctimap.h8
-rw-r--r--sound/pci/ctxfi/ctmixer.c279
-rw-r--r--sound/pci/ctxfi/ctmixer.h10
-rw-r--r--sound/pci/ctxfi/ctpcm.c155
-rw-r--r--sound/pci/ctxfi/ctpcm.h8
-rw-r--r--sound/pci/ctxfi/ctresource.c82
-rw-r--r--sound/pci/ctxfi/ctresource.h23
-rw-r--r--sound/pci/ctxfi/ctsrc.c158
-rw-r--r--sound/pci/ctxfi/ctsrc.h23
-rw-r--r--sound/pci/ctxfi/cttimer.c95
-rw-r--r--sound/pci/ctxfi/cttimer.h1
-rw-r--r--sound/pci/ctxfi/ctvmem.c48
-rw-r--r--sound/pci/ctxfi/ctvmem.h9
-rw-r--r--sound/pci/ctxfi/xfi.c87
-rw-r--r--sound/pci/echoaudio/Makefile29
-rw-r--r--sound/pci/echoaudio/darla20.c24
-rw-r--r--sound/pci/echoaudio/darla20_dsp.c17
-rw-r--r--sound/pci/echoaudio/darla24.c24
-rw-r--r--sound/pci/echoaudio/darla24_dsp.c25
-rw-r--r--sound/pci/echoaudio/echo3g.c24
-rw-r--r--sound/pci/echoaudio/echo3g_dsp.c24
-rw-r--r--sound/pci/echoaudio/echoaudio.c1066
-rw-r--r--sound/pci/echoaudio/echoaudio.h70
-rw-r--r--sound/pci/echoaudio/echoaudio_3g.c60
-rw-r--r--sound/pci/echoaudio/echoaudio_dsp.c185
-rw-r--r--sound/pci/echoaudio/echoaudio_dsp.h50
-rw-r--r--sound/pci/echoaudio/echoaudio_gml.c24
-rw-r--r--sound/pci/echoaudio/gina20.c26
-rw-r--r--sound/pci/echoaudio/gina20_dsp.c23
-rw-r--r--sound/pci/echoaudio/gina24.c24
-rw-r--r--sound/pci/echoaudio/gina24_dsp.c61
-rw-r--r--sound/pci/echoaudio/indigo.c24
-rw-r--r--sound/pci/echoaudio/indigo_dsp.c23
-rw-r--r--sound/pci/echoaudio/indigo_express_dsp.c6
-rw-r--r--sound/pci/echoaudio/indigodj.c24
-rw-r--r--sound/pci/echoaudio/indigodj_dsp.c23
-rw-r--r--sound/pci/echoaudio/indigodjx.c22
-rw-r--r--sound/pci/echoaudio/indigodjx_dsp.c11
-rw-r--r--sound/pci/echoaudio/indigoio.c24
-rw-r--r--sound/pci/echoaudio/indigoio_dsp.c20
-rw-r--r--sound/pci/echoaudio/indigoiox.c22
-rw-r--r--sound/pci/echoaudio/indigoiox_dsp.c11
-rw-r--r--sound/pci/echoaudio/layla20.c26
-rw-r--r--sound/pci/echoaudio/layla20_dsp.c46
-rw-r--r--sound/pci/echoaudio/layla24.c24
-rw-r--r--sound/pci/echoaudio/layla24_dsp.c67
-rw-r--r--sound/pci/echoaudio/mia.c26
-rw-r--r--sound/pci/echoaudio/mia_dsp.c29
-rw-r--r--sound/pci/echoaudio/midi.c91
-rw-r--r--sound/pci/echoaudio/mona.c24
-rw-r--r--sound/pci/echoaudio/mona_dsp.c63
-rw-r--r--sound/pci/emu10k1/Makefile10
-rw-r--r--sound/pci/emu10k1/emu10k1.c223
-rw-r--r--sound/pci/emu10k1/emu10k1_callback.c295
-rw-r--r--sound/pci/emu10k1/emu10k1_main.c1370
-rw-r--r--sound/pci/emu10k1/emu10k1_patch.c229
-rw-r--r--sound/pci/emu10k1/emu10k1_synth.c65
-rw-r--r--sound/pci/emu10k1/emu10k1_synth_local.h15
-rw-r--r--sound/pci/emu10k1/emu10k1x.c594
-rw-r--r--sound/pci/emu10k1/emufx.c1485
-rw-r--r--sound/pci/emu10k1/emumixer.c2039
-rw-r--r--sound/pci/emu10k1/emumpu401.c229
-rw-r--r--sound/pci/emu10k1/emupcm.c1375
-rw-r--r--sound/pci/emu10k1/emuproc.c679
-rw-r--r--sound/pci/emu10k1/io.c582
-rw-r--r--sound/pci/emu10k1/irq.c102
-rw-r--r--sound/pci/emu10k1/memory.c269
-rw-r--r--sound/pci/emu10k1/p16v.c360
-rw-r--r--sound/pci/emu10k1/p16v.h81
-rw-r--r--sound/pci/emu10k1/p17v.h21
-rw-r--r--sound/pci/emu10k1/timer.c59
-rw-r--r--sound/pci/emu10k1/tina2.h17
-rw-r--r--sound/pci/emu10k1/voice.c175
-rw-r--r--sound/pci/ens1370.c816
-rw-r--r--sound/pci/es1938.c441
-rw-r--r--sound/pci/es1968.c779
-rw-r--r--sound/pci/fm801.c1170
-rw-r--r--sound/pci/hda/Kconfig220
-rw-r--r--sound/pci/hda/Makefile58
-rw-r--r--sound/pci/hda/hda_beep.c267
-rw-r--r--sound/pci/hda/hda_beep.h56
-rw-r--r--sound/pci/hda/hda_codec.c4949
-rw-r--r--sound/pci/hda/hda_codec.h1048
-rw-r--r--sound/pci/hda/hda_eld.c641
-rw-r--r--sound/pci/hda/hda_generic.c1083
-rw-r--r--sound/pci/hda/hda_hwdep.c818
-rw-r--r--sound/pci/hda/hda_intel.c2843
-rw-r--r--sound/pci/hda/patch_analog.c4820
-rw-r--r--sound/pci/hda/patch_ca0110.c572
-rw-r--r--sound/pci/hda/patch_cirrus.c1311
-rw-r--r--sound/pci/hda/patch_cmedia.c776
-rw-r--r--sound/pci/hda/patch_conexant.c3937
-rw-r--r--sound/pci/hda/patch_hdmi.c1639
-rw-r--r--sound/pci/hda/patch_realtek.c19942
-rw-r--r--sound/pci/hda/patch_sigmatel.c6431
-rw-r--r--sound/pci/hda/patch_via.c6058
-rw-r--r--sound/pci/ice1712/Makefile7
-rw-r--r--sound/pci/ice1712/ak4xxx.c31
-rw-r--r--sound/pci/ice1712/amp.c30
-rw-r--r--sound/pci/ice1712/amp.h16
-rw-r--r--sound/pci/ice1712/aureon.c139
-rw-r--r--sound/pci/ice1712/aureon.h16
-rw-r--r--sound/pci/ice1712/delta.c264
-rw-r--r--sound/pci/ice1712/delta.h27
-rw-r--r--sound/pci/ice1712/envy24ht.h17
-rw-r--r--sound/pci/ice1712/ews.c161
-rw-r--r--sound/pci/ice1712/ews.h16
-rw-r--r--sound/pci/ice1712/hoontech.c110
-rw-r--r--sound/pci/ice1712/hoontech.h17
-rw-r--r--sound/pci/ice1712/ice1712.c762
-rw-r--r--sound/pci/ice1712/ice1712.h50
-rw-r--r--sound/pci/ice1712/ice1724.c871
-rw-r--r--sound/pci/ice1712/juli.c92
-rw-r--r--sound/pci/ice1712/juli.h1
-rw-r--r--sound/pci/ice1712/maya44.c87
-rw-r--r--sound/pci/ice1712/maya44.h1
-rw-r--r--sound/pci/ice1712/phase.c59
-rw-r--r--sound/pci/ice1712/phase.h16
-rw-r--r--sound/pci/ice1712/pontis.c119
-rw-r--r--sound/pci/ice1712/pontis.h16
-rw-r--r--sound/pci/ice1712/prodigy192.c93
-rw-r--r--sound/pci/ice1712/prodigy192.h1
-rw-r--r--sound/pci/ice1712/prodigy_hifi.c275
-rw-r--r--sound/pci/ice1712/prodigy_hifi.h16
-rw-r--r--sound/pci/ice1712/psc724.c447
-rw-r--r--sound/pci/ice1712/psc724.h14
-rw-r--r--sound/pci/ice1712/quartet.c125
-rw-r--r--sound/pci/ice1712/quartet.h1
-rw-r--r--sound/pci/ice1712/revo.c86
-rw-r--r--sound/pci/ice1712/revo.h16
-rw-r--r--sound/pci/ice1712/se.c58
-rw-r--r--sound/pci/ice1712/se.h1
-rw-r--r--sound/pci/ice1712/stac946x.h1
-rw-r--r--sound/pci/ice1712/vt1720_mobo.c27
-rw-r--r--sound/pci/ice1712/vt1720_mobo.h16
-rw-r--r--sound/pci/ice1712/wm8766.c332
-rw-r--r--sound/pci/ice1712/wm8766.h147
-rw-r--r--sound/pci/ice1712/wm8776.c601
-rw-r--r--sound/pci/ice1712/wm8776.h209
-rw-r--r--sound/pci/ice1712/wtm.c199
-rw-r--r--sound/pci/ice1712/wtm.h1
-rw-r--r--sound/pci/intel8x0.c904
-rw-r--r--sound/pci/intel8x0m.c474
-rw-r--r--sound/pci/korg1212/Makefile3
-rw-r--r--sound/pci/korg1212/korg1212.c719
-rw-r--r--sound/pci/lola/Makefile5
-rw-r--r--sound/pci/lola/lola.c714
-rw-r--r--sound/pci/lola/lola.h511
-rw-r--r--sound/pci/lola/lola_clock.c310
-rw-r--r--sound/pci/lola/lola_mixer.c842
-rw-r--r--sound/pci/lola/lola_pcm.c681
-rw-r--r--sound/pci/lola/lola_proc.c203
-rw-r--r--sound/pci/lx6464es/Makefile3
-rw-r--r--sound/pci/lx6464es/lx6464es.c501
-rw-r--r--sound/pci/lx6464es/lx6464es.h33
-rw-r--r--sound/pci/lx6464es/lx_core.c564
-rw-r--r--sound/pci/lx6464es/lx_core.h31
-rw-r--r--sound/pci/lx6464es/lx_defs.h19
-rw-r--r--sound/pci/maestro3.c532
-rw-r--r--sound/pci/mixart/Makefile3
-rw-r--r--sound/pci/mixart/mixart.c349
-rw-r--r--sound/pci/mixart/mixart.h31
-rw-r--r--sound/pci/mixart/mixart_core.c198
-rw-r--r--sound/pci/mixart/mixart_core.h104
-rw-r--r--sound/pci/mixart/mixart_hwdep.c234
-rw-r--r--sound/pci/mixart/mixart_hwdep.h33
-rw-r--r--sound/pci/mixart/mixart_mixer.c132
-rw-r--r--sound/pci/mixart/mixart_mixer.h15
-rw-r--r--sound/pci/nm256/Makefile3
-rw-r--r--sound/pci/nm256/nm256.c429
-rw-r--r--sound/pci/nm256/nm256_coef.c5
-rw-r--r--sound/pci/oxygen/Makefile11
-rw-r--r--sound/pci/oxygen/ak4396.h1
-rw-r--r--sound/pci/oxygen/cm9780.h1
-rw-r--r--sound/pci/oxygen/cs2000.h1
-rw-r--r--sound/pci/oxygen/cs4245.h111
-rw-r--r--sound/pci/oxygen/cs4362a.h1
-rw-r--r--sound/pci/oxygen/cs4398.h1
-rw-r--r--sound/pci/oxygen/hifier.c239
-rw-r--r--sound/pci/oxygen/oxygen.c415
-rw-r--r--sound/pci/oxygen/oxygen.h32
-rw-r--r--sound/pci/oxygen/oxygen_io.c50
-rw-r--r--sound/pci/oxygen/oxygen_lib.c383
-rw-r--r--sound/pci/oxygen/oxygen_mixer.c264
-rw-r--r--sound/pci/oxygen/oxygen_pcm.c343
-rw-r--r--sound/pci/oxygen/oxygen_regs.h18
-rw-r--r--sound/pci/oxygen/pcm1796.h2
-rw-r--r--sound/pci/oxygen/se6x.c146
-rw-r--r--sound/pci/oxygen/virtuoso.c54
-rw-r--r--sound/pci/oxygen/wm8766.h1
-rw-r--r--sound/pci/oxygen/wm8776.h5
-rw-r--r--sound/pci/oxygen/wm8785.h1
-rw-r--r--sound/pci/oxygen/xonar.h3
-rw-r--r--sound/pci/oxygen/xonar_cs43xx.c108
-rw-r--r--sound/pci/oxygen/xonar_dg.c285
-rw-r--r--sound/pci/oxygen/xonar_dg.h57
-rw-r--r--sound/pci/oxygen/xonar_dg_mixer.c456
-rw-r--r--sound/pci/oxygen/xonar_hdmi.c17
-rw-r--r--sound/pci/oxygen/xonar_lib.c26
-rw-r--r--sound/pci/oxygen/xonar_pcm179x.c712
-rw-r--r--sound/pci/oxygen/xonar_wm87x6.c380
-rw-r--r--sound/pci/pcxhr/Makefile3
-rw-r--r--sound/pci/pcxhr/pcxhr.c435
-rw-r--r--sound/pci/pcxhr/pcxhr.h27
-rw-r--r--sound/pci/pcxhr/pcxhr_core.c255
-rw-r--r--sound/pci/pcxhr/pcxhr_core.h21
-rw-r--r--sound/pci/pcxhr/pcxhr_hwdep.c152
-rw-r--r--sound/pci/pcxhr/pcxhr_hwdep.h15
-rw-r--r--sound/pci/pcxhr/pcxhr_mix22.c80
-rw-r--r--sound/pci/pcxhr/pcxhr_mix22.h16
-rw-r--r--sound/pci/pcxhr/pcxhr_mixer.c130
-rw-r--r--sound/pci/pcxhr/pcxhr_mixer.h15
-rw-r--r--sound/pci/riptide/Makefile3
-rw-r--r--sound/pci/riptide/riptide.c629
-rw-r--r--sound/pci/rme32.c524
-rw-r--r--sound/pci/rme96.c906
-rw-r--r--sound/pci/rme9652/Makefile7
-rw-r--r--sound/pci/rme9652/hdsp.c2132
-rw-r--r--sound/pci/rme9652/hdspm.c6314
-rw-r--r--sound/pci/rme9652/rme9652.c669
-rw-r--r--sound/pci/sis7019.c381
-rw-r--r--sound/pci/sis7019.h14
-rw-r--r--sound/pci/sonicvibes.c663
-rw-r--r--sound/pci/trident/Makefile3
-rw-r--r--sound/pci/trident/trident.c132
-rw-r--r--sound/pci/trident/trident.h422
-rw-r--r--sound/pci/trident/trident_main.c836
-rw-r--r--sound/pci/trident/trident_memory.c104
-rw-r--r--sound/pci/via82xx.c623
-rw-r--r--sound/pci/via82xx_modem.c322
-rw-r--r--sound/pci/vx222/Makefile3
-rw-r--r--sound/pci/vx222/vx222.c178
-rw-r--r--sound/pci/vx222/vx222.h21
-rw-r--r--sound/pci/vx222/vx222_ops.c133
-rw-r--r--sound/pci/ymfpci/Makefile3
-rw-r--r--sound/pci/ymfpci/ymfpci.c267
-rw-r--r--sound/pci/ymfpci/ymfpci.h410
-rw-r--r--sound/pci/ymfpci/ymfpci_main.c808
-rw-r--r--sound/pcmcia/Kconfig2
-rw-r--r--sound/pcmcia/Makefile1
-rw-r--r--sound/pcmcia/pdaudiocf/Makefile3
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf.c87
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf.h24
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf_core.c68
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf_irq.c41
-rw-r--r--sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c76
-rw-r--r--sound/pcmcia/vx/Makefile3
-rw-r--r--sound/pcmcia/vx/vxp_mixer.c44
-rw-r--r--sound/pcmcia/vx/vxp_ops.c95
-rw-r--r--sound/pcmcia/vx/vxpocket.c121
-rw-r--r--sound/pcmcia/vx/vxpocket.h19
-rw-r--r--sound/ppc/Kconfig1
-rw-r--r--sound/ppc/Makefile3
-rw-r--r--sound/ppc/awacs.c117
-rw-r--r--sound/ppc/awacs.h15
-rw-r--r--sound/ppc/beep.c47
-rw-r--r--sound/ppc/burgundy.c52
-rw-r--r--sound/ppc/burgundy.h15
-rw-r--r--sound/ppc/daca.c47
-rw-r--r--sound/ppc/keywest.c103
-rw-r--r--sound/ppc/pmac.c320
-rw-r--r--sound/ppc/pmac.h18
-rw-r--r--sound/ppc/powermac.c91
-rw-r--r--sound/ppc/snd_ps3.c145
-rw-r--r--sound/ppc/snd_ps3.h14
-rw-r--r--sound/ppc/snd_ps3_reg.h30
-rw-r--r--sound/ppc/tumbler.c184
-rw-r--r--sound/ppc/tumbler_volume.h13
-rw-r--r--sound/sh/Kconfig3
-rw-r--r--sound/sh/Makefile5
-rw-r--r--sound/sh/aica.c174
-rw-r--r--sound/sh/aica.h15
-rw-r--r--sound/sh/sh_dac_audio.c136
-rw-r--r--sound/soc/Kconfig134
-rw-r--r--sound/soc/Makefile88
-rw-r--r--sound/soc/adi/Kconfig18
-rw-r--r--sound/soc/adi/Makefile6
-rw-r--r--sound/soc/adi/axi-i2s.c302
-rw-r--r--sound/soc/adi/axi-spdif.c267
-rw-r--r--sound/soc/amd/Kconfig190
-rw-r--r--sound/soc/amd/Makefile21
-rw-r--r--sound/soc/amd/acp-config.c337
-rw-r--r--sound/soc/amd/acp-da7219-max98357a.c811
-rw-r--r--sound/soc/amd/acp-es8336.c320
-rw-r--r--sound/soc/amd/acp-pcm-dma.c1442
-rw-r--r--sound/soc/amd/acp-rt5645.c218
-rw-r--r--sound/soc/amd/acp.h223
-rw-r--r--sound/soc/amd/acp/Kconfig189
-rw-r--r--sound/soc/amd/acp/Makefile50
-rw-r--r--sound/soc/amd/acp/acp-i2s.c699
-rw-r--r--sound/soc/amd/acp/acp-legacy-common.c647
-rw-r--r--sound/soc/amd/acp/acp-legacy-mach.c245
-rw-r--r--sound/soc/amd/acp/acp-mach-common.c1803
-rw-r--r--sound/soc/amd/acp/acp-mach.h139
-rw-r--r--sound/soc/amd/acp/acp-pci.c301
-rw-r--r--sound/soc/amd/acp/acp-pdm.c187
-rw-r--r--sound/soc/amd/acp/acp-platform.c360
-rw-r--r--sound/soc/amd/acp/acp-rembrandt.c247
-rw-r--r--sound/soc/amd/acp/acp-renoir.c191
-rw-r--r--sound/soc/amd/acp/acp-sdw-legacy-mach.c565
-rw-r--r--sound/soc/amd/acp/acp-sdw-mach-common.c98
-rw-r--r--sound/soc/amd/acp/acp-sdw-sof-mach.c461
-rw-r--r--sound/soc/amd/acp/acp-sof-mach.c174
-rw-r--r--sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c456
-rw-r--r--sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h12
-rw-r--r--sound/soc/amd/acp/acp63.c298
-rw-r--r--sound/soc/amd/acp/acp70.c228
-rw-r--r--sound/soc/amd/acp/acp_common.h20
-rw-r--r--sound/soc/amd/acp/amd-acp63-acpi-match.c144
-rw-r--r--sound/soc/amd/acp/amd-acp70-acpi-match.c329
-rw-r--r--sound/soc/amd/acp/amd-acpi-mach.c93
-rw-r--r--sound/soc/amd/acp/amd-sdw-acpi.c62
-rw-r--r--sound/soc/amd/acp/amd.h365
-rw-r--r--sound/soc/amd/acp/chip_offset_byte.h146
-rw-r--r--sound/soc/amd/acp/soc_amd_sdw_common.h62
-rw-r--r--sound/soc/amd/acp3x-rt5682-max9836.c556
-rw-r--r--sound/soc/amd/include/acp_2_2_d.h609
-rw-r--r--sound/soc/amd/include/acp_2_2_enum.h1068
-rw-r--r--sound/soc/amd/include/acp_2_2_sh_mask.h2292
-rw-r--r--sound/soc/amd/mach-config.h38
-rw-r--r--sound/soc/amd/ps/Makefile11
-rw-r--r--sound/soc/amd/ps/acp63.h448
-rw-r--r--sound/soc/amd/ps/pci-ps.c755
-rw-r--r--sound/soc/amd/ps/ps-common.c493
-rw-r--r--sound/soc/amd/ps/ps-mach.c80
-rw-r--r--sound/soc/amd/ps/ps-pdm-dma.c463
-rw-r--r--sound/soc/amd/ps/ps-sdw-dma.c804
-rw-r--r--sound/soc/amd/raven/Makefile8
-rw-r--r--sound/soc/amd/raven/acp3x-i2s.c331
-rw-r--r--sound/soc/amd/raven/acp3x-pcm-dma.c526
-rw-r--r--sound/soc/amd/raven/acp3x.h162
-rw-r--r--sound/soc/amd/raven/chip_offset_byte.h639
-rw-r--r--sound/soc/amd/raven/pci-acp3x.c347
-rw-r--r--sound/soc/amd/renoir/Makefile8
-rw-r--r--sound/soc/amd/renoir/acp3x-pdm-dma.c504
-rw-r--r--sound/soc/amd/renoir/acp3x-rn.c77
-rw-r--r--sound/soc/amd/renoir/rn-pci-acp3x.c434
-rw-r--r--sound/soc/amd/renoir/rn_acp3x.h93
-rw-r--r--sound/soc/amd/renoir/rn_chip_offset_byte.h349
-rw-r--r--sound/soc/amd/rpl/Makefile5
-rw-r--r--sound/soc/amd/rpl/rpl-pci-acp6x.c227
-rw-r--r--sound/soc/amd/rpl/rpl_acp6x.h36
-rw-r--r--sound/soc/amd/rpl/rpl_acp6x_chip_offset_byte.h30
-rw-r--r--sound/soc/amd/vangogh/Makefile11
-rw-r--r--sound/soc/amd/vangogh/acp5x-i2s.c417
-rw-r--r--sound/soc/amd/vangogh/acp5x-mach.c489
-rw-r--r--sound/soc/amd/vangogh/acp5x-pcm-dma.c513
-rw-r--r--sound/soc/amd/vangogh/acp5x.h224
-rw-r--r--sound/soc/amd/vangogh/pci-acp5x.c342
-rw-r--r--sound/soc/amd/vangogh/vg_chip_offset_byte.h337
-rw-r--r--sound/soc/amd/yc/Makefile9
-rw-r--r--sound/soc/amd/yc/acp6x-mach.c740
-rw-r--r--sound/soc/amd/yc/acp6x-pdm-dma.c455
-rw-r--r--sound/soc/amd/yc/acp6x.h110
-rw-r--r--sound/soc/amd/yc/acp6x_chip_offset_byte.h444
-rw-r--r--sound/soc/amd/yc/pci-acp6x.c350
-rw-r--r--sound/soc/apple/Kconfig11
-rw-r--r--sound/soc/apple/Makefile3
-rw-r--r--sound/soc/apple/mca.c1212
-rw-r--r--sound/soc/atmel/Kconfig200
-rw-r--r--sound/soc/atmel/Makefile44
-rw-r--r--sound/soc/atmel/atmel-classd.c630
-rw-r--r--sound/soc/atmel/atmel-classd.h121
-rw-r--r--sound/soc/atmel/atmel-i2s.c742
-rw-r--r--sound/soc/atmel/atmel-pcm-dma.c120
-rw-r--r--sound/soc/atmel/atmel-pcm-pdc.c (renamed from sound/soc/atmel/atmel-pcm.c)269
-rw-r--r--sound/soc/atmel/atmel-pcm.h38
-rw-r--r--sound/soc/atmel/atmel-pdmic.c702
-rw-r--r--sound/soc/atmel/atmel-pdmic.h81
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c791
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.h21
-rw-r--r--sound/soc/atmel/atmel_wm8904.c198
-rw-r--r--sound/soc/atmel/mchp-i2s-mcc.c1138
-rw-r--r--sound/soc/atmel/mchp-pdmc.c1156
-rw-r--r--sound/soc/atmel/mchp-spdifrx.c1203
-rw-r--r--sound/soc/atmel/mchp-spdiftx.c903
-rw-r--r--sound/soc/atmel/mikroe-proto.c176
-rw-r--r--sound/soc/atmel/playpaq_wm8510.c471
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c258
-rw-r--r--sound/soc/atmel/sam9x5_wm8731.c208
-rw-r--r--sound/soc/atmel/snd-soc-afeb9260.c186
-rw-r--r--sound/soc/atmel/tse850-pcm5142.c435
-rw-r--r--sound/soc/au1x/Kconfig51
-rw-r--r--sound/soc/au1x/Makefile19
-rw-r--r--sound/soc/au1x/ac97c.c346
-rw-r--r--sound/soc/au1x/db1000.c59
-rw-r--r--sound/soc/au1x/db1200.c199
-rw-r--r--sound/soc/au1x/dbdma2.c230
-rw-r--r--sound/soc/au1x/dma.c328
-rw-r--r--sound/soc/au1x/i2sc.c312
-rw-r--r--sound/soc/au1x/psc-ac97.c282
-rw-r--r--sound/soc/au1x/psc-i2s.c235
-rw-r--r--sound/soc/au1x/psc.h44
-rw-r--r--sound/soc/bcm/Kconfig32
-rw-r--r--sound/soc/bcm/Makefile15
-rw-r--r--sound/soc/bcm/bcm2835-i2s.c931
-rw-r--r--sound/soc/bcm/bcm63xx-i2s-whistler.c303
-rw-r--r--sound/soc/bcm/bcm63xx-i2s.h89
-rw-r--r--sound/soc/bcm/bcm63xx-pcm-whistler.c418
-rw-r--r--sound/soc/bcm/cygnus-pcm.c750
-rw-r--r--sound/soc/bcm/cygnus-ssp.c1405
-rw-r--r--sound/soc/bcm/cygnus-ssp.h127
-rw-r--r--sound/soc/blackfin/Kconfig144
-rw-r--r--sound/soc/blackfin/Makefile29
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.c483
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.h26
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.c453
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.h59
-rw-r--r--sound/soc/blackfin/bf5xx-ad1836.c130
-rw-r--r--sound/soc/blackfin/bf5xx-ad193x.c144
-rw-r--r--sound/soc/blackfin/bf5xx-ad1980.c116
-rw-r--r--sound/soc/blackfin/bf5xx-ad73311.c234
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.c317
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.h26
-rw-r--r--sound/soc/blackfin/bf5xx-i2s.c334
-rw-r--r--sound/soc/blackfin/bf5xx-sport.c1014
-rw-r--r--sound/soc/blackfin/bf5xx-sport.h168
-rw-r--r--sound/soc/blackfin/bf5xx-ssm2602.c166
-rw-r--r--sound/soc/blackfin/bf5xx-tdm-pcm.c351
-rw-r--r--sound/soc/blackfin/bf5xx-tdm-pcm.h18
-rw-r--r--sound/soc/blackfin/bf5xx-tdm.c367
-rw-r--r--sound/soc/blackfin/bf5xx-tdm.h23
-rw-r--r--sound/soc/cirrus/Kconfig36
-rw-r--r--sound/soc/cirrus/Makefile8
-rw-r--r--sound/soc/cirrus/ep93xx-i2s.c (renamed from sound/soc/ep93xx/ep93xx-i2s.c)324
-rw-r--r--sound/soc/cirrus/ep93xx-pcm.c50
-rw-r--r--sound/soc/cirrus/ep93xx-pcm.h11
-rw-r--r--sound/soc/codecs/88pm860x-codec.c543
-rw-r--r--sound/soc/codecs/88pm860x-codec.h126
-rw-r--r--sound/soc/codecs/Kconfig2742
-rw-r--r--sound/soc/codecs/Makefile887
-rw-r--r--sound/soc/codecs/ab8500-codec.c2575
-rw-r--r--sound/soc/codecs/ab8500-codec.h587
-rw-r--r--sound/soc/codecs/ac97.c126
-rw-r--r--sound/soc/codecs/ad1836.c448
-rw-r--r--sound/soc/codecs/ad1836.h53
-rw-r--r--sound/soc/codecs/ad193x-i2c.c47
-rw-r--r--sound/soc/codecs/ad193x-spi.c53
-rw-r--r--sound/soc/codecs/ad193x.c615
-rw-r--r--sound/soc/codecs/ad193x.h87
-rw-r--r--sound/soc/codecs/ad1980.c339
-rw-r--r--sound/soc/codecs/ad1980.h26
-rw-r--r--sound/soc/codecs/ad73311.c59
-rw-r--r--sound/soc/codecs/ad73311.h17
-rw-r--r--sound/soc/codecs/adau-utils.c60
-rw-r--r--sound/soc/codecs/adau-utils.h8
-rw-r--r--sound/soc/codecs/adau1372-i2c.c41
-rw-r--r--sound/soc/codecs/adau1372-spi.c59
-rw-r--r--sound/soc/codecs/adau1372.c1073
-rw-r--r--sound/soc/codecs/adau1372.h22
-rw-r--r--sound/soc/codecs/adau1373.c1626
-rw-r--r--sound/soc/codecs/adau1373.h30
-rw-r--r--sound/soc/codecs/adau1701.c885
-rw-r--r--sound/soc/codecs/adau1701.h16
-rw-r--r--sound/soc/codecs/adau1761-i2c.c68
-rw-r--r--sound/soc/codecs/adau1761-spi.c86
-rw-r--r--sound/soc/codecs/adau1761.c1024
-rw-r--r--sound/soc/codecs/adau1761.h22
-rw-r--r--sound/soc/codecs/adau1781-i2c.c64
-rw-r--r--sound/soc/codecs/adau1781-spi.c82
-rw-r--r--sound/soc/codecs/adau1781.c507
-rw-r--r--sound/soc/codecs/adau1781.h22
-rw-r--r--sound/soc/codecs/adau17x1.c1123
-rw-r--r--sound/soc/codecs/adau17x1.h134
-rw-r--r--sound/soc/codecs/adau1977-i2c.c49
-rw-r--r--sound/soc/codecs/adau1977-spi.c77
-rw-r--r--sound/soc/codecs/adau1977.c1003
-rw-r--r--sound/soc/codecs/adau1977.h36
-rw-r--r--sound/soc/codecs/adau7002.c131
-rw-r--r--sound/soc/codecs/adau7118-hw.c43
-rw-r--r--sound/soc/codecs/adau7118-i2c.c88
-rw-r--r--sound/soc/codecs/adau7118.c575
-rw-r--r--sound/soc/codecs/adau7118.h24
-rw-r--r--sound/soc/codecs/adav801.c44
-rw-r--r--sound/soc/codecs/adav803.c40
-rw-r--r--sound/soc/codecs/adav80x.c882
-rw-r--r--sound/soc/codecs/adav80x.h41
-rw-r--r--sound/soc/codecs/ads117x.c75
-rw-r--r--sound/soc/codecs/ads117x.h13
-rw-r--r--sound/soc/codecs/ak4104.c329
-rw-r--r--sound/soc/codecs/ak4118.c416
-rw-r--r--sound/soc/codecs/ak4375.c606
-rw-r--r--sound/soc/codecs/ak4458.c813
-rw-r--r--sound/soc/codecs/ak4458.h90
-rw-r--r--sound/soc/codecs/ak4535.c287
-rw-r--r--sound/soc/codecs/ak4535.h7
-rw-r--r--sound/soc/codecs/ak4554.c96
-rw-r--r--sound/soc/codecs/ak4613.c927
-rw-r--r--sound/soc/codecs/ak4619.c912
-rw-r--r--sound/soc/codecs/ak4641.c641
-rw-r--r--sound/soc/codecs/ak4642.c633
-rw-r--r--sound/soc/codecs/ak4671.c424
-rw-r--r--sound/soc/codecs/ak4671.h9
-rw-r--r--sound/soc/codecs/ak5386.c203
-rw-r--r--sound/soc/codecs/ak5558.c508
-rw-r--r--sound/soc/codecs/ak5558.h52
-rw-r--r--sound/soc/codecs/alc5623.c1094
-rw-r--r--sound/soc/codecs/alc5623.h157
-rw-r--r--sound/soc/codecs/alc5632.c1193
-rw-r--r--sound/soc/codecs/alc5632.h249
-rw-r--r--sound/soc/codecs/arizona-jack.c1668
-rw-r--r--sound/soc/codecs/arizona.c2857
-rw-r--r--sound/soc/codecs/arizona.h398
-rw-r--r--sound/soc/codecs/audio-iio-aux.c314
-rw-r--r--sound/soc/codecs/aw8738.c104
-rw-r--r--sound/soc/codecs/aw87390.c461
-rw-r--r--sound/soc/codecs/aw87390.h85
-rw-r--r--sound/soc/codecs/aw88081.c1317
-rw-r--r--sound/soc/codecs/aw88081.h329
-rw-r--r--sound/soc/codecs/aw88166.c1815
-rw-r--r--sound/soc/codecs/aw88166.h529
-rw-r--r--sound/soc/codecs/aw88261.c1282
-rw-r--r--sound/soc/codecs/aw88261.h459
-rw-r--r--sound/soc/codecs/aw88395/aw88395.c576
-rw-r--r--sound/soc/codecs/aw88395/aw88395.h58
-rw-r--r--sound/soc/codecs/aw88395/aw88395_data_type.h142
-rw-r--r--sound/soc/codecs/aw88395/aw88395_device.c1721
-rw-r--r--sound/soc/codecs/aw88395/aw88395_device.h218
-rw-r--r--sound/soc/codecs/aw88395/aw88395_lib.c1174
-rw-r--r--sound/soc/codecs/aw88395/aw88395_lib.h92
-rw-r--r--sound/soc/codecs/aw88395/aw88395_reg.h383
-rw-r--r--sound/soc/codecs/aw88399.c2173
-rw-r--r--sound/soc/codecs/aw88399.h629
-rw-r--r--sound/soc/codecs/bd28623.c239
-rw-r--r--sound/soc/codecs/bt-sco.c114
-rw-r--r--sound/soc/codecs/chv3-codec.c42
-rw-r--r--sound/soc/codecs/cirrus_legacy.h21
-rw-r--r--sound/soc/codecs/cpcap.c1884
-rw-r--r--sound/soc/codecs/cq93vc.c136
-rw-r--r--sound/soc/codecs/cros_ec_codec.c1064
-rw-r--r--sound/soc/codecs/cs-amp-lib-test.c2451
-rw-r--r--sound/soc/codecs/cs-amp-lib.c737
-rw-r--r--sound/soc/codecs/cs35l32.c579
-rw-r--r--sound/soc/codecs/cs35l32.h89
-rw-r--r--sound/soc/codecs/cs35l33.c1287
-rw-r--r--sound/soc/codecs/cs35l33.h217
-rw-r--r--sound/soc/codecs/cs35l34.c1217
-rw-r--r--sound/soc/codecs/cs35l34.h265
-rw-r--r--sound/soc/codecs/cs35l35.c1662
-rw-r--r--sound/soc/codecs/cs35l35.h297
-rw-r--r--sound/soc/codecs/cs35l36.c1940
-rw-r--r--sound/soc/codecs/cs35l36.h446
-rw-r--r--sound/soc/codecs/cs35l41-i2c.c96
-rw-r--r--sound/soc/codecs/cs35l41-lib.c1592
-rw-r--r--sound/soc/codecs/cs35l41-spi.c99
-rw-r--r--sound/soc/codecs/cs35l41.c1492
-rw-r--r--sound/soc/codecs/cs35l41.h41
-rw-r--r--sound/soc/codecs/cs35l45-i2c.c76
-rw-r--r--sound/soc/codecs/cs35l45-spi.c78
-rw-r--r--sound/soc/codecs/cs35l45-tables.c332
-rw-r--r--sound/soc/codecs/cs35l45.c1513
-rw-r--r--sound/soc/codecs/cs35l45.h514
-rw-r--r--sound/soc/codecs/cs35l56-i2c.c108
-rw-r--r--sound/soc/codecs/cs35l56-sdw.c606
-rw-r--r--sound/soc/codecs/cs35l56-shared.c1740
-rw-r--r--sound/soc/codecs/cs35l56-spi.c95
-rw-r--r--sound/soc/codecs/cs35l56.c1814
-rw-r--r--sound/soc/codecs/cs35l56.h77
-rw-r--r--sound/soc/codecs/cs40l50-codec.c307
-rw-r--r--sound/soc/codecs/cs4234.c917
-rw-r--r--sound/soc/codecs/cs4234.h287
-rw-r--r--sound/soc/codecs/cs4265.c660
-rw-r--r--sound/soc/codecs/cs4265.h60
-rw-r--r--sound/soc/codecs/cs4270.c490
-rw-r--r--sound/soc/codecs/cs4271-i2c.c43
-rw-r--r--sound/soc/codecs/cs4271-spi.c37
-rw-r--r--sound/soc/codecs/cs4271.c725
-rw-r--r--sound/soc/codecs/cs4271.h12
-rw-r--r--sound/soc/codecs/cs42l42-i2c.c104
-rw-r--r--sound/soc/codecs/cs42l42-sdw.c625
-rw-r--r--sound/soc/codecs/cs42l42.c2495
-rw-r--r--sound/soc/codecs/cs42l42.h82
-rw-r--r--sound/soc/codecs/cs42l43-jack.c962
-rw-r--r--sound/soc/codecs/cs42l43-sdw.c71
-rw-r--r--sound/soc/codecs/cs42l43.c2476
-rw-r--r--sound/soc/codecs/cs42l43.h146
-rw-r--r--sound/soc/codecs/cs42l51-i2c.c61
-rw-r--r--sound/soc/codecs/cs42l51.c565
-rw-r--r--sound/soc/codecs/cs42l51.h20
-rw-r--r--sound/soc/codecs/cs42l52.c1248
-rw-r--r--sound/soc/codecs/cs42l52.h270
-rw-r--r--sound/soc/codecs/cs42l56.c1375
-rw-r--r--sound/soc/codecs/cs42l56.h173
-rw-r--r--sound/soc/codecs/cs42l73.c1393
-rw-r--r--sound/soc/codecs/cs42l73.h212
-rw-r--r--sound/soc/codecs/cs42l83-i2c.c240
-rw-r--r--sound/soc/codecs/cs42l84.c1111
-rw-r--r--sound/soc/codecs/cs42l84.h210
-rw-r--r--sound/soc/codecs/cs42xx8-i2c.c76
-rw-r--r--sound/soc/codecs/cs42xx8.c675
-rw-r--r--sound/soc/codecs/cs42xx8.h238
-rw-r--r--sound/soc/codecs/cs43130.c2781
-rw-r--r--sound/soc/codecs/cs43130.h541
-rw-r--r--sound/soc/codecs/cs4341.c352
-rw-r--r--sound/soc/codecs/cs4349.c382
-rw-r--r--sound/soc/codecs/cs4349.h127
-rw-r--r--sound/soc/codecs/cs47l15.c1505
-rw-r--r--sound/soc/codecs/cs47l24.c1355
-rw-r--r--sound/soc/codecs/cs47l24.h20
-rw-r--r--sound/soc/codecs/cs47l35.c1782
-rw-r--r--sound/soc/codecs/cs47l85.c2733
-rw-r--r--sound/soc/codecs/cs47l90.c2657
-rw-r--r--sound/soc/codecs/cs47l92.c2103
-rw-r--r--sound/soc/codecs/cs48l32-tables.c538
-rw-r--r--sound/soc/codecs/cs48l32.c4076
-rw-r--r--sound/soc/codecs/cs48l32.h403
-rw-r--r--sound/soc/codecs/cs530x-i2c.c88
-rw-r--r--sound/soc/codecs/cs530x-spi.c92
-rw-r--r--sound/soc/codecs/cs530x.c1343
-rw-r--r--sound/soc/codecs/cs530x.h253
-rw-r--r--sound/soc/codecs/cs53l30.c1107
-rw-r--r--sound/soc/codecs/cs53l30.h455
-rw-r--r--sound/soc/codecs/cx20442.c222
-rw-r--r--sound/soc/codecs/cx20442.h7
-rw-r--r--sound/soc/codecs/cx2072x.c1718
-rw-r--r--sound/soc/codecs/cx2072x.h314
-rw-r--r--sound/soc/codecs/da7210.c1234
-rw-r--r--sound/soc/codecs/da7213.c2297
-rw-r--r--sound/soc/codecs/da7213.h614
-rw-r--r--sound/soc/codecs/da7218.c3304
-rw-r--r--sound/soc/codecs/da7218.h1411
-rw-r--r--sound/soc/codecs/da7219-aad.c1080
-rw-r--r--sound/soc/codecs/da7219-aad.h222
-rw-r--r--sound/soc/codecs/da7219.c2735
-rw-r--r--sound/soc/codecs/da7219.h839
-rw-r--r--sound/soc/codecs/da732x.c1568
-rw-r--r--sound/soc/codecs/da732x.h123
-rw-r--r--sound/soc/codecs/da732x_reg.h651
-rw-r--r--sound/soc/codecs/da9055.c1543
-rw-r--r--sound/soc/codecs/dmic.c216
-rw-r--r--sound/soc/codecs/es7134.c316
-rw-r--r--sound/soc/codecs/es7241.c311
-rw-r--r--sound/soc/codecs/es8311.c974
-rw-r--r--sound/soc/codecs/es8311.h162
-rw-r--r--sound/soc/codecs/es8316.c933
-rw-r--r--sound/soc/codecs/es8316.h135
-rw-r--r--sound/soc/codecs/es8323.c791
-rw-r--r--sound/soc/codecs/es8323.h78
-rw-r--r--sound/soc/codecs/es8326.c1374
-rw-r--r--sound/soc/codecs/es8326.h200
-rw-r--r--sound/soc/codecs/es8328-i2c.c51
-rw-r--r--sound/soc/codecs/es8328-spi.c39
-rw-r--r--sound/soc/codecs/es8328.c884
-rw-r--r--sound/soc/codecs/es8328.h290
-rw-r--r--sound/soc/codecs/es8375.c794
-rw-r--r--sound/soc/codecs/es8375.h123
-rw-r--r--sound/soc/codecs/es8389.c962
-rw-r--r--sound/soc/codecs/es8389.h140
-rw-r--r--sound/soc/codecs/es83xx-dsm-common.c89
-rw-r--r--sound/soc/codecs/es83xx-dsm-common.h393
-rw-r--r--sound/soc/codecs/framer-codec.c413
-rw-r--r--sound/soc/codecs/fs-amp-lib.c265
-rw-r--r--sound/soc/codecs/fs-amp-lib.h150
-rw-r--r--sound/soc/codecs/fs210x.c1586
-rw-r--r--sound/soc/codecs/fs210x.h75
-rw-r--r--sound/soc/codecs/gtm601.c109
-rw-r--r--sound/soc/codecs/hda-dai.c105
-rw-r--r--sound/soc/codecs/hda.c396
-rw-r--r--sound/soc/codecs/hda.h19
-rw-r--r--sound/soc/codecs/hdac_hda.c677
-rw-r--r--sound/soc/codecs/hdac_hda.h34
-rw-r--r--sound/soc/codecs/hdac_hdmi.c2048
-rw-r--r--sound/soc/codecs/hdmi-codec.c1212
-rw-r--r--sound/soc/codecs/ics43432.c74
-rw-r--r--sound/soc/codecs/idt821034.c1183
-rw-r--r--sound/soc/codecs/inno_rk3036.c486
-rw-r--r--sound/soc/codecs/inno_rk3036.h124
-rw-r--r--sound/soc/codecs/isabelle.c1154
-rw-r--r--sound/soc/codecs/isabelle.h139
-rw-r--r--sound/soc/codecs/jz4725b.c667
-rw-r--r--sound/soc/codecs/jz4740.c276
-rw-r--r--sound/soc/codecs/jz4760.c872
-rw-r--r--sound/soc/codecs/jz4770.c919
-rw-r--r--sound/soc/codecs/l3.c91
-rw-r--r--sound/soc/codecs/lm4857.c148
-rw-r--r--sound/soc/codecs/lm49453.c1463
-rw-r--r--sound/soc/codecs/lm49453.h376
-rw-r--r--sound/soc/codecs/lochnagar-sc.c266
-rw-r--r--sound/soc/codecs/lpass-macro-common.c93
-rw-r--r--sound/soc/codecs/lpass-macro-common.h77
-rw-r--r--sound/soc/codecs/lpass-rx-macro.c4035
-rw-r--r--sound/soc/codecs/lpass-tx-macro.c2543
-rw-r--r--sound/soc/codecs/lpass-va-macro.c1781
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.c2939
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.h17
-rw-r--r--sound/soc/codecs/madera.c4796
-rw-r--r--sound/soc/codecs/madera.h458
-rw-r--r--sound/soc/codecs/max9759.c198
-rw-r--r--sound/soc/codecs/max9768.c232
-rw-r--r--sound/soc/codecs/max98088.c1653
-rw-r--r--sound/soc/codecs/max98088.h20
-rw-r--r--sound/soc/codecs/max98090.c2723
-rw-r--r--sound/soc/codecs/max98090.h1549
-rw-r--r--sound/soc/codecs/max98095.c2167
-rw-r--r--sound/soc/codecs/max98095.h318
-rw-r--r--sound/soc/codecs/max98357a.c182
-rw-r--r--sound/soc/codecs/max98363.c465
-rw-r--r--sound/soc/codecs/max98363.h36
-rw-r--r--sound/soc/codecs/max98371.c430
-rw-r--r--sound/soc/codecs/max98371.h63
-rw-r--r--sound/soc/codecs/max98373-i2c.c616
-rw-r--r--sound/soc/codecs/max98373-sdw.c888
-rw-r--r--sound/soc/codecs/max98373-sdw.h72
-rw-r--r--sound/soc/codecs/max98373.c510
-rw-r--r--sound/soc/codecs/max98373.h240
-rw-r--r--sound/soc/codecs/max98388.c1013
-rw-r--r--sound/soc/codecs/max98388.h234
-rw-r--r--sound/soc/codecs/max98390.c1135
-rw-r--r--sound/soc/codecs/max98390.h667
-rw-r--r--sound/soc/codecs/max98396.c1914
-rw-r--r--sound/soc/codecs/max98396.h327
-rw-r--r--sound/soc/codecs/max9850.c341
-rw-r--r--sound/soc/codecs/max9850.h33
-rw-r--r--sound/soc/codecs/max98504.c382
-rw-r--r--sound/soc/codecs/max98504.h56
-rw-r--r--sound/soc/codecs/max98520.c766
-rw-r--r--sound/soc/codecs/max98520.h159
-rw-r--r--sound/soc/codecs/max9860.c738
-rw-r--r--sound/soc/codecs/max9860.h154
-rw-r--r--sound/soc/codecs/max9867.c718
-rw-r--r--sound/soc/codecs/max9867.h67
-rw-r--r--sound/soc/codecs/max9877.c337
-rw-r--r--sound/soc/codecs/max9877.h9
-rw-r--r--sound/soc/codecs/max98925.c646
-rw-r--r--sound/soc/codecs/max98925.h829
-rw-r--r--sound/soc/codecs/max98926.c593
-rw-r--r--sound/soc/codecs/max98926.h846
-rw-r--r--sound/soc/codecs/max98927.c914
-rw-r--r--sound/soc/codecs/max98927.h271
-rw-r--r--sound/soc/codecs/mc13783.c789
-rw-r--r--sound/soc/codecs/mc13783.h16
-rw-r--r--sound/soc/codecs/ml26124.c593
-rw-r--r--sound/soc/codecs/ml26124.h172
-rw-r--r--sound/soc/codecs/msm8916-wcd-analog.c1280
-rw-r--r--sound/soc/codecs/msm8916-wcd-digital.c1249
-rw-r--r--sound/soc/codecs/mt6351.c1496
-rw-r--r--sound/soc/codecs/mt6351.h105
-rw-r--r--sound/soc/codecs/mt6357.c1855
-rw-r--r--sound/soc/codecs/mt6357.h660
-rw-r--r--sound/soc/codecs/mt6358.c2430
-rw-r--r--sound/soc/codecs/mt6358.h2310
-rw-r--r--sound/soc/codecs/mt6359-accdet.c1062
-rw-r--r--sound/soc/codecs/mt6359-accdet.h137
-rw-r--r--sound/soc/codecs/mt6359.c2960
-rw-r--r--sound/soc/codecs/mt6359.h4289
-rw-r--r--sound/soc/codecs/mt6660.c580
-rw-r--r--sound/soc/codecs/mt6660.h77
-rw-r--r--sound/soc/codecs/nau8315.c167
-rw-r--r--sound/soc/codecs/nau8325.c899
-rw-r--r--sound/soc/codecs/nau8325.h391
-rw-r--r--sound/soc/codecs/nau8540.c993
-rw-r--r--sound/soc/codecs/nau8540.h265
-rw-r--r--sound/soc/codecs/nau8810.c929
-rw-r--r--sound/soc/codecs/nau8810.h286
-rw-r--r--sound/soc/codecs/nau8821.c1984
-rw-r--r--sound/soc/codecs/nau8821.h586
-rw-r--r--sound/soc/codecs/nau8822.c1221
-rw-r--r--sound/soc/codecs/nau8822.h224
-rw-r--r--sound/soc/codecs/nau8824.c2058
-rw-r--r--sound/soc/codecs/nau8824.h478
-rw-r--r--sound/soc/codecs/nau8825.c2972
-rw-r--r--sound/soc/codecs/nau8825.h538
-rw-r--r--sound/soc/codecs/ntp8835.c480
-rw-r--r--sound/soc/codecs/ntp8918.c396
-rw-r--r--sound/soc/codecs/ntpfw.c137
-rw-r--r--sound/soc/codecs/ntpfw.h23
-rw-r--r--sound/soc/codecs/pcm1681.c334
-rw-r--r--sound/soc/codecs/pcm1754.c185
-rw-r--r--sound/soc/codecs/pcm1789-i2c.c63
-rw-r--r--sound/soc/codecs/pcm1789.c271
-rw-r--r--sound/soc/codecs/pcm1789.h17
-rw-r--r--sound/soc/codecs/pcm179x-i2c.c59
-rw-r--r--sound/soc/codecs/pcm179x-spi.c58
-rw-r--r--sound/soc/codecs/pcm179x.c231
-rw-r--r--sound/soc/codecs/pcm179x.h18
-rw-r--r--sound/soc/codecs/pcm186x-i2c.c60
-rw-r--r--sound/soc/codecs/pcm186x-spi.c61
-rw-r--r--sound/soc/codecs/pcm186x.c706
-rw-r--r--sound/soc/codecs/pcm186x.h219
-rw-r--r--sound/soc/codecs/pcm3008.c198
-rw-r--r--sound/soc/codecs/pcm3008.h22
-rw-r--r--sound/soc/codecs/pcm3060-i2c.c59
-rw-r--r--sound/soc/codecs/pcm3060-spi.c59
-rw-r--r--sound/soc/codecs/pcm3060.c347
-rw-r--r--sound/soc/codecs/pcm3060.h96
-rw-r--r--sound/soc/codecs/pcm3168a-i2c.c69
-rw-r--r--sound/soc/codecs/pcm3168a-spi.c60
-rw-r--r--sound/soc/codecs/pcm3168a.c914
-rw-r--r--sound/soc/codecs/pcm3168a.h97
-rw-r--r--sound/soc/codecs/pcm5102a.c57
-rw-r--r--sound/soc/codecs/pcm512x-i2c.c90
-rw-r--r--sound/soc/codecs/pcm512x-spi.c69
-rw-r--r--sound/soc/codecs/pcm512x.c1799
-rw-r--r--sound/soc/codecs/pcm512x.h264
-rw-r--r--sound/soc/codecs/pcm6240.c2167
-rw-r--r--sound/soc/codecs/pcm6240.h247
-rw-r--r--sound/soc/codecs/peb2466.c2064
-rw-r--r--sound/soc/codecs/pm4125-sdw.c495
-rw-r--r--sound/soc/codecs/pm4125.c1761
-rw-r--r--sound/soc/codecs/pm4125.h293
-rw-r--r--sound/soc/codecs/rk3308_codec.c975
-rw-r--r--sound/soc/codecs/rk3308_codec.h579
-rw-r--r--sound/soc/codecs/rk3328_codec.c534
-rw-r--r--sound/soc/codecs/rk3328_codec.h210
-rw-r--r--sound/soc/codecs/rk817_codec.c540
-rw-r--r--sound/soc/codecs/rl6231.c253
-rw-r--r--sound/soc/codecs/rl6231.h33
-rw-r--r--sound/soc/codecs/rl6347a.c108
-rw-r--r--sound/soc/codecs/rl6347a.h31
-rw-r--r--sound/soc/codecs/rt-sdw-common.c238
-rw-r--r--sound/soc/codecs/rt-sdw-common.h66
-rw-r--r--sound/soc/codecs/rt1011.c2486
-rw-r--r--sound/soc/codecs/rt1011.h704
-rw-r--r--sound/soc/codecs/rt1015.c1190
-rw-r--r--sound/soc/codecs/rt1015.h449
-rw-r--r--sound/soc/codecs/rt1015p.c154
-rw-r--r--sound/soc/codecs/rt1016.c693
-rw-r--r--sound/soc/codecs/rt1016.h232
-rw-r--r--sound/soc/codecs/rt1017-sdca-sdw.c822
-rw-r--r--sound/soc/codecs/rt1017-sdca-sdw.h183
-rw-r--r--sound/soc/codecs/rt1019.c609
-rw-r--r--sound/soc/codecs/rt1019.h164
-rw-r--r--sound/soc/codecs/rt1305.c1179
-rw-r--r--sound/soc/codecs/rt1305.h273
-rw-r--r--sound/soc/codecs/rt1308-sdw.c817
-rw-r--r--sound/soc/codecs/rt1308-sdw.h172
-rw-r--r--sound/soc/codecs/rt1308.c871
-rw-r--r--sound/soc/codecs/rt1308.h294
-rw-r--r--sound/soc/codecs/rt1316-sdw.c794
-rw-r--r--sound/soc/codecs/rt1316-sdw.h52
-rw-r--r--sound/soc/codecs/rt1318-sdw.c868
-rw-r--r--sound/soc/codecs/rt1318-sdw.h96
-rw-r--r--sound/soc/codecs/rt1318.c1353
-rw-r--r--sound/soc/codecs/rt1318.h342
-rw-r--r--sound/soc/codecs/rt1320-sdw.c1823
-rw-r--r--sound/soc/codecs/rt1320-sdw.h113
-rw-r--r--sound/soc/codecs/rt274.c1234
-rw-r--r--sound/soc/codecs/rt274.h214
-rw-r--r--sound/soc/codecs/rt286.c1278
-rw-r--r--sound/soc/codecs/rt286.h200
-rw-r--r--sound/soc/codecs/rt298.c1324
-rw-r--r--sound/soc/codecs/rt298.h211
-rw-r--r--sound/soc/codecs/rt5514-spi.c514
-rw-r--r--sound/soc/codecs/rt5514-spi.h37
-rw-r--r--sound/soc/codecs/rt5514.c1335
-rw-r--r--sound/soc/codecs/rt5514.h286
-rw-r--r--sound/soc/codecs/rt5616.c1414
-rw-r--r--sound/soc/codecs/rt5616.h1816
-rw-r--r--sound/soc/codecs/rt5631.c1742
-rw-r--r--sound/soc/codecs/rt5631.h702
-rw-r--r--sound/soc/codecs/rt5640.c3066
-rw-r--r--sound/soc/codecs/rt5640.h2193
-rw-r--r--sound/soc/codecs/rt5645.c4367
-rw-r--r--sound/soc/codecs/rt5645.h2213
-rw-r--r--sound/soc/codecs/rt5651.c2292
-rw-r--r--sound/soc/codecs/rt5651.h2102
-rw-r--r--sound/soc/codecs/rt5659.c4345
-rw-r--r--sound/soc/codecs/rt5659.h1821
-rw-r--r--sound/soc/codecs/rt5660.c1350
-rw-r--r--sound/soc/codecs/rt5660.h847
-rw-r--r--sound/soc/codecs/rt5663.c3756
-rw-r--r--sound/soc/codecs/rt5663.h1128
-rw-r--r--sound/soc/codecs/rt5665.c4868
-rw-r--r--sound/soc/codecs/rt5665.h2002
-rw-r--r--sound/soc/codecs/rt5668.c2584
-rw-r--r--sound/soc/codecs/rt5668.h1312
-rw-r--r--sound/soc/codecs/rt5670-dsp.h51
-rw-r--r--sound/soc/codecs/rt5670.c3345
-rw-r--r--sound/soc/codecs/rt5670.h2029
-rw-r--r--sound/soc/codecs/rt5677-spi.c638
-rw-r--r--sound/soc/codecs/rt5677-spi.h33
-rw-r--r--sound/soc/codecs/rt5677.c5677
-rw-r--r--sound/soc/codecs/rt5677.h1808
-rw-r--r--sound/soc/codecs/rt5682-i2c.c348
-rw-r--r--sound/soc/codecs/rt5682-sdw.c811
-rw-r--r--sound/soc/codecs/rt5682.c3191
-rw-r--r--sound/soc/codecs/rt5682.h1499
-rw-r--r--sound/soc/codecs/rt5682s.c3349
-rw-r--r--sound/soc/codecs/rt5682s.h1492
-rw-r--r--sound/soc/codecs/rt700-sdw.c572
-rw-r--r--sound/soc/codecs/rt700-sdw.h335
-rw-r--r--sound/soc/codecs/rt700.c1235
-rw-r--r--sound/soc/codecs/rt700.h171
-rw-r--r--sound/soc/codecs/rt711-sdca-sdw.c496
-rw-r--r--sound/soc/codecs/rt711-sdca-sdw.h99
-rw-r--r--sound/soc/codecs/rt711-sdca.c1669
-rw-r--r--sound/soc/codecs/rt711-sdca.h244
-rw-r--r--sound/soc/codecs/rt711-sdw.c585
-rw-r--r--sound/soc/codecs/rt711-sdw.h283
-rw-r--r--sound/soc/codecs/rt711.c1335
-rw-r--r--sound/soc/codecs/rt711.h251
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.c984
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.h107
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.c510
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.h75
-rw-r--r--sound/soc/codecs/rt712-sdca.c1926
-rw-r--r--sound/soc/codecs/rt712-sdca.h261
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.c284
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.h171
-rw-r--r--sound/soc/codecs/rt715-sdca.c1072
-rw-r--r--sound/soc/codecs/rt715-sdca.h132
-rw-r--r--sound/soc/codecs/rt715-sdw.c551
-rw-r--r--sound/soc/codecs/rt715-sdw.h337
-rw-r--r--sound/soc/codecs/rt715.c1133
-rw-r--r--sound/soc/codecs/rt715.h224
-rw-r--r--sound/soc/codecs/rt721-sdca-sdw.c548
-rw-r--r--sound/soc/codecs/rt721-sdca-sdw.h150
-rw-r--r--sound/soc/codecs/rt721-sdca.c1561
-rw-r--r--sound/soc/codecs/rt721-sdca.h274
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.c557
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.h122
-rw-r--r--sound/soc/codecs/rt722-sdca.c1583
-rw-r--r--sound/soc/codecs/rt722-sdca.h250
-rw-r--r--sound/soc/codecs/rt9120.c643
-rw-r--r--sound/soc/codecs/rt9123.c500
-rw-r--r--sound/soc/codecs/rt9123p.c171
-rw-r--r--sound/soc/codecs/rtq9124.c543
-rw-r--r--sound/soc/codecs/rtq9128.c789
-rw-r--r--sound/soc/codecs/sdw-mockup.c275
-rw-r--r--sound/soc/codecs/sgtl5000.c1839
-rw-r--r--sound/soc/codecs/sgtl5000.h408
-rw-r--r--sound/soc/codecs/si476x.c263
-rw-r--r--sound/soc/codecs/sigmadsp-i2c.c96
-rw-r--r--sound/soc/codecs/sigmadsp-regmap.c59
-rw-r--r--sound/soc/codecs/sigmadsp.c809
-rw-r--r--sound/soc/codecs/sigmadsp.h64
-rw-r--r--sound/soc/codecs/simple-amplifier.c110
-rw-r--r--sound/soc/codecs/simple-mux.c192
-rw-r--r--sound/soc/codecs/sma1303.c1811
-rw-r--r--sound/soc/codecs/sma1303.h609
-rw-r--r--sound/soc/codecs/sma1307.c2044
-rw-r--r--sound/soc/codecs/sma1307.h444
-rw-r--r--sound/soc/codecs/spdif_receiver.c87
-rw-r--r--sound/soc/codecs/spdif_transciever.c76
-rw-r--r--sound/soc/codecs/spdif_transmitter.c88
-rw-r--r--sound/soc/codecs/src4xxx-i2c.c46
-rw-r--r--sound/soc/codecs/src4xxx.c518
-rw-r--r--sound/soc/codecs/src4xxx.h113
-rw-r--r--sound/soc/codecs/ssm2305.c99
-rw-r--r--sound/soc/codecs/ssm2518.c814
-rw-r--r--sound/soc/codecs/ssm2518.h19
-rw-r--r--sound/soc/codecs/ssm2602-i2c.c56
-rw-r--r--sound/soc/codecs/ssm2602-spi.c39
-rw-r--r--sound/soc/codecs/ssm2602.c834
-rw-r--r--sound/soc/codecs/ssm2602.h40
-rw-r--r--sound/soc/codecs/ssm3515.c448
-rw-r--r--sound/soc/codecs/ssm4567.c511
-rw-r--r--sound/soc/codecs/sta32x.c1177
-rw-r--r--sound/soc/codecs/sta32x.h207
-rw-r--r--sound/soc/codecs/sta350.c1262
-rw-r--r--sound/soc/codecs/sta350.h234
-rw-r--r--sound/soc/codecs/sta529.c391
-rw-r--r--sound/soc/codecs/stac9766.c354
-rw-r--r--sound/soc/codecs/stac9766.h17
-rw-r--r--sound/soc/codecs/sti-sas.c465
-rw-r--r--sound/soc/codecs/tas2552.c776
-rw-r--r--sound/soc/codecs/tas2552.h138
-rw-r--r--sound/soc/codecs/tas2562.c781
-rw-r--r--sound/soc/codecs/tas2562.h90
-rw-r--r--sound/soc/codecs/tas2764-quirks.h180
-rw-r--r--sound/soc/codecs/tas2764.c950
-rw-r--r--sound/soc/codecs/tas2764.h129
-rw-r--r--sound/soc/codecs/tas2770.c890
-rw-r--r--sound/soc/codecs/tas2770.h151
-rw-r--r--sound/soc/codecs/tas2780.c652
-rw-r--r--sound/soc/codecs/tas2780.h101
-rw-r--r--sound/soc/codecs/tas2781-comlib-i2c.c371
-rw-r--r--sound/soc/codecs/tas2781-comlib.c221
-rw-r--r--sound/soc/codecs/tas2781-fmwlib.c2618
-rw-r--r--sound/soc/codecs/tas2781-i2c.c2132
-rw-r--r--sound/soc/codecs/tas2783-sdw.c1347
-rw-r--r--sound/soc/codecs/tas2783.h110
-rw-r--r--sound/soc/codecs/tas5086.c995
-rw-r--r--sound/soc/codecs/tas571x.c1091
-rw-r--r--sound/soc/codecs/tas571x.h141
-rw-r--r--sound/soc/codecs/tas5720.c827
-rw-r--r--sound/soc/codecs/tas5720.h127
-rw-r--r--sound/soc/codecs/tas5805m.c611
-rw-r--r--sound/soc/codecs/tas6424.c816
-rw-r--r--sound/soc/codecs/tas6424.h158
-rw-r--r--sound/soc/codecs/tda7419.c641
-rw-r--r--sound/soc/codecs/tfa9879.c323
-rw-r--r--sound/soc/codecs/tfa9879.h197
-rw-r--r--sound/soc/codecs/tfa989x.c425
-rw-r--r--sound/soc/codecs/tlv320adc3xxx.c1522
-rw-r--r--sound/soc/codecs/tlv320adcx140.c1215
-rw-r--r--sound/soc/codecs/tlv320adcx140.h159
-rw-r--r--sound/soc/codecs/tlv320aic23-i2c.c58
-rw-r--r--sound/soc/codecs/tlv320aic23-spi.c45
-rw-r--r--sound/soc/codecs/tlv320aic23.c418
-rw-r--r--sound/soc/codecs/tlv320aic23.h11
-rw-r--r--sound/soc/codecs/tlv320aic26.c303
-rw-r--r--sound/soc/codecs/tlv320aic26.h16
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c1853
-rw-r--r--sound/soc/codecs/tlv320aic31xx.h244
-rw-r--r--sound/soc/codecs/tlv320aic32x4-clk.c499
-rw-r--r--sound/soc/codecs/tlv320aic32x4-i2c.c70
-rw-r--r--sound/soc/codecs/tlv320aic32x4-spi.c70
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c1439
-rw-r--r--sound/soc/codecs/tlv320aic32x4.h237
-rw-r--r--sound/soc/codecs/tlv320aic3x-i2c.c72
-rw-r--r--sound/soc/codecs/tlv320aic3x-spi.c77
-rw-r--r--sound/soc/codecs/tlv320aic3x.c1655
-rw-r--r--sound/soc/codecs/tlv320aic3x.h107
-rw-r--r--sound/soc/codecs/tlv320dac33.c986
-rw-r--r--sound/soc/codecs/tlv320dac33.h18
-rw-r--r--sound/soc/codecs/tpa6130a2.c580
-rw-r--r--sound/soc/codecs/tpa6130a2.h31
-rw-r--r--sound/soc/codecs/ts3a227e.c462
-rw-r--r--sound/soc/codecs/ts3a227e.h14
-rw-r--r--sound/soc/codecs/tscs42xx.c1512
-rw-r--r--sound/soc/codecs/tscs42xx.h2701
-rw-r--r--sound/soc/codecs/tscs454.c3481
-rw-r--r--sound/soc/codecs/tscs454.h2323
-rw-r--r--sound/soc/codecs/twl4030.c1279
-rw-r--r--sound/soc/codecs/twl6040.c1567
-rw-r--r--sound/soc/codecs/twl6040.h146
-rw-r--r--sound/soc/codecs/uda1334.c294
-rw-r--r--sound/soc/codecs/uda1342.c347
-rw-r--r--sound/soc/codecs/uda1342.h78
-rw-r--r--sound/soc/codecs/uda134x.c643
-rw-r--r--sound/soc/codecs/uda134x.h34
-rw-r--r--sound/soc/codecs/uda1380.c405
-rw-r--r--sound/soc/codecs/uda1380.h9
-rw-r--r--sound/soc/codecs/wcd-clsh-v2.c905
-rw-r--r--sound/soc/codecs/wcd-clsh-v2.h66
-rw-r--r--sound/soc/codecs/wcd-common.c144
-rw-r--r--sound/soc/codecs/wcd-common.h46
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c1647
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.h343
-rw-r--r--sound/soc/codecs/wcd9335.c5168
-rw-r--r--sound/soc/codecs/wcd9335.h641
-rw-r--r--sound/soc/codecs/wcd934x.c5919
-rw-r--r--sound/soc/codecs/wcd937x-sdw.c1115
-rw-r--r--sound/soc/codecs/wcd937x.c2987
-rw-r--r--sound/soc/codecs/wcd937x.h618
-rw-r--r--sound/soc/codecs/wcd938x-sdw.c1279
-rw-r--r--sound/soc/codecs/wcd938x.c3559
-rw-r--r--sound/soc/codecs/wcd938x.h686
-rw-r--r--sound/soc/codecs/wcd939x-sdw.c1477
-rw-r--r--sound/soc/codecs/wcd939x.c3623
-rw-r--r--sound/soc/codecs/wcd939x.h947
-rw-r--r--sound/soc/codecs/wl1273.c528
-rw-r--r--sound/soc/codecs/wl1273.h101
-rw-r--r--sound/soc/codecs/wm0010.c977
-rw-r--r--sound/soc/codecs/wm1250-ev1.c224
-rw-r--r--sound/soc/codecs/wm2000.c533
-rw-r--r--sound/soc/codecs/wm2000.h17
-rw-r--r--sound/soc/codecs/wm2200.c2493
-rw-r--r--sound/soc/codecs/wm2200.h3670
-rw-r--r--sound/soc/codecs/wm5100-tables.c1481
-rw-r--r--sound/soc/codecs/wm5100.c2691
-rw-r--r--sound/soc/codecs/wm5100.h5311
-rw-r--r--sound/soc/codecs/wm5102.c2185
-rw-r--r--sound/soc/codecs/wm5102.h20
-rw-r--r--sound/soc/codecs/wm5110.c2545
-rw-r--r--sound/soc/codecs/wm5110.h20
-rw-r--r--sound/soc/codecs/wm8350.c577
-rw-r--r--sound/soc/codecs/wm8350.h10
-rw-r--r--sound/soc/codecs/wm8400.c486
-rw-r--r--sound/soc/codecs/wm8400.h7
-rw-r--r--sound/soc/codecs/wm8510.c378
-rw-r--r--sound/soc/codecs/wm8510.h5
-rw-r--r--sound/soc/codecs/wm8523.c378
-rw-r--r--sound/soc/codecs/wm8523.h7
-rw-r--r--sound/soc/codecs/wm8524.c282
-rw-r--r--sound/soc/codecs/wm8580.c530
-rw-r--r--sound/soc/codecs/wm8580.h7
-rw-r--r--sound/soc/codecs/wm8711.c285
-rw-r--r--sound/soc/codecs/wm8711.h5
-rw-r--r--sound/soc/codecs/wm8727.c56
-rw-r--r--sound/soc/codecs/wm8728.c232
-rw-r--r--sound/soc/codecs/wm8728.h5
-rw-r--r--sound/soc/codecs/wm8731-i2c.c68
-rw-r--r--sound/soc/codecs/wm8731-spi.c59
-rw-r--r--sound/soc/codecs/wm8731.c571
-rw-r--r--sound/soc/codecs/wm8731.h34
-rw-r--r--sound/soc/codecs/wm8737.c735
-rw-r--r--sound/soc/codecs/wm8737.h319
-rw-r--r--sound/soc/codecs/wm8741.c609
-rw-r--r--sound/soc/codecs/wm8741.h15
-rw-r--r--sound/soc/codecs/wm8750.c342
-rw-r--r--sound/soc/codecs/wm8750.h6
-rw-r--r--sound/soc/codecs/wm8753.c917
-rw-r--r--sound/soc/codecs/wm8753.h10
-rw-r--r--sound/soc/codecs/wm8770.c701
-rw-r--r--sound/soc/codecs/wm8770.h48
-rw-r--r--sound/soc/codecs/wm8776.c331
-rw-r--r--sound/soc/codecs/wm8776.h5
-rw-r--r--sound/soc/codecs/wm8782.c185
-rw-r--r--sound/soc/codecs/wm8804-i2c.c72
-rw-r--r--sound/soc/codecs/wm8804-spi.c52
-rw-r--r--sound/soc/codecs/wm8804.c705
-rw-r--r--sound/soc/codecs/wm8804.h17
-rw-r--r--sound/soc/codecs/wm8900.c634
-rw-r--r--sound/soc/codecs/wm8900.h5
-rw-r--r--sound/soc/codecs/wm8903.c1912
-rw-r--r--sound/soc/codecs/wm8903.h45
-rw-r--r--sound/soc/codecs/wm8904.c2049
-rw-r--r--sound/soc/codecs/wm8904.h17
-rw-r--r--sound/soc/codecs/wm8940.c635
-rw-r--r--sound/soc/codecs/wm8940.h8
-rw-r--r--sound/soc/codecs/wm8955.c521
-rw-r--r--sound/soc/codecs/wm8955.h5
-rw-r--r--sound/soc/codecs/wm8958-dsp2.c1032
-rw-r--r--sound/soc/codecs/wm8960.c1101
-rw-r--r--sound/soc/codecs/wm8960.h8
-rw-r--r--sound/soc/codecs/wm8961.c858
-rw-r--r--sound/soc/codecs/wm8961.h5
-rw-r--r--sound/soc/codecs/wm8962.c4907
-rw-r--r--sound/soc/codecs/wm8962.h17
-rw-r--r--sound/soc/codecs/wm8971.c370
-rw-r--r--sound/soc/codecs/wm8971.h7
-rw-r--r--sound/soc/codecs/wm8974.c439
-rw-r--r--sound/soc/codecs/wm8974.h5
-rw-r--r--sound/soc/codecs/wm8978.c486
-rw-r--r--sound/soc/codecs/wm8978.h9
-rw-r--r--sound/soc/codecs/wm8983.c1112
-rw-r--r--sound/soc/codecs/wm8983.h1026
-rw-r--r--sound/soc/codecs/wm8985.c760
-rw-r--r--sound/soc/codecs/wm8985.h43
-rw-r--r--sound/soc/codecs/wm8988.c439
-rw-r--r--sound/soc/codecs/wm8988.h6
-rw-r--r--sound/soc/codecs/wm8990.c558
-rw-r--r--sound/soc/codecs/wm8990.h16
-rw-r--r--sound/soc/codecs/wm8991.c1335
-rw-r--r--sound/soc/codecs/wm8991.h815
-rw-r--r--sound/soc/codecs/wm8993.c1132
-rw-r--r--sound/soc/codecs/wm8993.h10
-rw-r--r--sound/soc/codecs/wm8994.c5264
-rw-r--r--sound/soc/codecs/wm8994.h159
-rw-r--r--sound/soc/codecs/wm8995.c2314
-rw-r--r--sound/soc/codecs/wm8995.h4263
-rw-r--r--sound/soc/codecs/wm8996.c3093
-rw-r--r--sound/soc/codecs/wm8996.h3717
-rw-r--r--sound/soc/codecs/wm8997.c1221
-rw-r--r--sound/soc/codecs/wm8997.h20
-rw-r--r--sound/soc/codecs/wm8998.c1437
-rw-r--r--sound/soc/codecs/wm8998.h20
-rw-r--r--sound/soc/codecs/wm9081.c786
-rw-r--r--sound/soc/codecs/wm9081.h5
-rw-r--r--sound/soc/codecs/wm9090.c520
-rw-r--r--sound/soc/codecs/wm9090.h15
-rw-r--r--sound/soc/codecs/wm9705.c296
-rw-r--r--sound/soc/codecs/wm9705.h11
-rw-r--r--sound/soc/codecs/wm9712.c548
-rw-r--r--sound/soc/codecs/wm9712.h11
-rw-r--r--sound/soc/codecs/wm9713.c692
-rw-r--r--sound/soc/codecs/wm9713.h7
-rw-r--r--sound/soc/codecs/wm_adsp.c2101
-rw-r--r--sound/soc/codecs/wm_adsp.h145
-rw-r--r--sound/soc/codecs/wm_hubs.c825
-rw-r--r--sound/soc/codecs/wm_hubs.h55
-rw-r--r--sound/soc/codecs/wsa881x.c1229
-rw-r--r--sound/soc/codecs/wsa883x.c1721
-rw-r--r--sound/soc/codecs/wsa884x.c2181
-rw-r--r--sound/soc/codecs/zl38060.c639
-rw-r--r--sound/soc/davinci/Kconfig86
-rw-r--r--sound/soc/davinci/Makefile20
-rw-r--r--sound/soc/davinci/davinci-evm.c306
-rw-r--r--sound/soc/davinci/davinci-mcasp.c978
-rw-r--r--sound/soc/davinci/davinci-mcasp.h59
-rw-r--r--sound/soc/davinci/davinci-pcm.c884
-rw-r--r--sound/soc/davinci/davinci-pcm.h31
-rw-r--r--sound/soc/davinci/davinci-sffsdr.c181
-rw-r--r--sound/soc/davinci/davinci-vcif.c271
-rw-r--r--sound/soc/dwc/Kconfig23
-rw-r--r--sound/soc/dwc/Makefile6
-rw-r--r--sound/soc/dwc/dwc-i2s.c1104
-rw-r--r--sound/soc/dwc/dwc-pcm.c266
-rw-r--r--sound/soc/dwc/local.h167
-rw-r--r--sound/soc/ep93xx/Kconfig32
-rw-r--r--sound/soc/ep93xx/Makefile15
-rw-r--r--sound/soc/ep93xx/ep93xx-ac97.c468
-rw-r--r--sound/soc/ep93xx/ep93xx-pcm.c338
-rw-r--r--sound/soc/ep93xx/ep93xx-pcm.h20
-rw-r--r--sound/soc/ep93xx/simone.c89
-rw-r--r--sound/soc/ep93xx/snappercl15.c146
-rw-r--r--sound/soc/fsl/Kconfig342
-rw-r--r--sound/soc/fsl/Makefile77
-rw-r--r--sound/soc/fsl/efika-audio-fabric.c41
-rw-r--r--sound/soc/fsl/eukrea-tlv320.c227
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c1112
-rw-r--r--sound/soc/fsl/fsl_asrc.c1579
-rw-r--r--sound/soc/fsl/fsl_asrc.h466
-rw-r--r--sound/soc/fsl/fsl_asrc_common.h178
-rw-r--r--sound/soc/fsl/fsl_asrc_dma.c477
-rw-r--r--sound/soc/fsl/fsl_asrc_m2m.c729
-rw-r--r--sound/soc/fsl/fsl_aud2htx.c309
-rw-r--r--sound/soc/fsl/fsl_aud2htx.h68
-rw-r--r--sound/soc/fsl/fsl_audmix.c569
-rw-r--r--sound/soc/fsl/fsl_audmix.h103
-rw-r--r--sound/soc/fsl/fsl_dma.c236
-rw-r--r--sound/soc/fsl/fsl_dma.h5
-rw-r--r--sound/soc/fsl/fsl_easrc.c2363
-rw-r--r--sound/soc/fsl/fsl_easrc.h655
-rw-r--r--sound/soc/fsl/fsl_esai.c1211
-rw-r--r--sound/soc/fsl/fsl_esai.h351
-rw-r--r--sound/soc/fsl/fsl_micfil.c1618
-rw-r--r--sound/soc/fsl/fsl_micfil.h214
-rw-r--r--sound/soc/fsl/fsl_mqs.c471
-rw-r--r--sound/soc/fsl/fsl_qmc_audio.c974
-rw-r--r--sound/soc/fsl/fsl_rpmsg.c353
-rw-r--r--sound/soc/fsl/fsl_rpmsg.h47
-rw-r--r--sound/soc/fsl/fsl_sai.c1872
-rw-r--r--sound/soc/fsl/fsl_sai.h320
-rw-r--r--sound/soc/fsl/fsl_spdif.c1774
-rw-r--r--sound/soc/fsl/fsl_spdif.h220
-rw-r--r--sound/soc/fsl/fsl_ssi.c2081
-rw-r--r--sound/soc/fsl/fsl_ssi.h494
-rw-r--r--sound/soc/fsl/fsl_ssi_dbg.c140
-rw-r--r--sound/soc/fsl/fsl_utils.c202
-rw-r--r--sound/soc/fsl/fsl_utils.h34
-rw-r--r--sound/soc/fsl/fsl_xcvr.c1843
-rw-r--r--sound/soc/fsl/fsl_xcvr.h403
-rw-r--r--sound/soc/fsl/imx-audio-rpmsg.c145
-rw-r--r--sound/soc/fsl/imx-audmix.c339
-rw-r--r--sound/soc/fsl/imx-audmux.c380
-rw-r--r--sound/soc/fsl/imx-audmux.h12
-rw-r--r--sound/soc/fsl/imx-card.c943
-rw-r--r--sound/soc/fsl/imx-es8328.c255
-rw-r--r--sound/soc/fsl/imx-hdmi.c224
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c54
-rw-r--r--sound/soc/fsl/imx-pcm-fiq.c (renamed from sound/soc/imx/imx-pcm-fiq.c)217
-rw-r--r--sound/soc/fsl/imx-pcm-rpmsg.c852
-rw-r--r--sound/soc/fsl/imx-pcm-rpmsg.h512
-rw-r--r--sound/soc/fsl/imx-pcm.h55
-rw-r--r--sound/soc/fsl/imx-rpmsg.c254
-rw-r--r--sound/soc/fsl/imx-sgtl5000.c224
-rw-r--r--sound/soc/fsl/imx-ssi.h (renamed from sound/soc/imx/imx-ssi.h)48
-rw-r--r--sound/soc/fsl/lpc3xxx-i2s.c372
-rw-r--r--sound/soc/fsl/lpc3xxx-i2s.h80
-rw-r--r--sound/soc/fsl/lpc3xxx-pcm.c72
-rw-r--r--sound/soc/fsl/mpc5200_dma.c212
-rw-r--r--sound/soc/fsl/mpc5200_dma.h4
-rw-r--r--sound/soc/fsl/mpc5200_psc_ac97.c92
-rw-r--r--sound/soc/fsl/mpc5200_psc_ac97.h13
-rw-r--r--sound/soc/fsl/mpc5200_psc_i2s.c75
-rw-r--r--sound/soc/fsl/mpc8610_hpcd.c589
-rw-r--r--sound/soc/fsl/p1022_ds.c318
-rw-r--r--sound/soc/fsl/p1022_rdk.c423
-rw-r--r--sound/soc/fsl/pcm030-audio-fabric.c150
-rw-r--r--sound/soc/generic/Kconfig43
-rw-r--r--sound/soc/generic/Makefile14
-rw-r--r--sound/soc/generic/audio-graph-card.c661
-rw-r--r--sound/soc/generic/audio-graph-card2-custom-sample.c187
-rw-r--r--sound/soc/generic/audio-graph-card2-custom-sample1.dtsi396
-rw-r--r--sound/soc/generic/audio-graph-card2-custom-sample2.dtsi382
-rw-r--r--sound/soc/generic/audio-graph-card2.c1412
-rw-r--r--sound/soc/generic/simple-card-utils.c1262
-rw-r--r--sound/soc/generic/simple-card.c837
-rw-r--r--sound/soc/generic/test-component.c657
-rw-r--r--sound/soc/google/Kconfig9
-rw-r--r--sound/soc/google/Makefile2
-rw-r--r--sound/soc/google/chv3-i2s.c339
-rw-r--r--sound/soc/hisilicon/Kconfig10
-rw-r--r--sound/soc/hisilicon/Makefile2
-rw-r--r--sound/soc/hisilicon/hi6210-i2s.c611
-rw-r--r--sound/soc/hisilicon/hi6210-i2s.h265
-rw-r--r--sound/soc/img/Kconfig47
-rw-r--r--sound/soc/img/Makefile8
-rw-r--r--sound/soc/img/img-i2s-in.c613
-rw-r--r--sound/soc/img/img-i2s-out.c613
-rw-r--r--sound/soc/img/img-parallel-out.c319
-rw-r--r--sound/soc/img/img-spdif-in.c884
-rw-r--r--sound/soc/img/img-spdif-out.c475
-rw-r--r--sound/soc/img/pistachio-internal-dac.c278
-rw-r--r--sound/soc/imx/Kconfig55
-rw-r--r--sound/soc/imx/Makefile17
-rw-r--r--sound/soc/imx/eukrea-tlv320.c131
-rw-r--r--sound/soc/imx/imx-pcm-dma-mx2.c334
-rw-r--r--sound/soc/imx/imx-ssi.c773
-rw-r--r--sound/soc/imx/phycore-ac97.c86
-rw-r--r--sound/soc/imx/wm1133-ev1.c303
-rw-r--r--sound/soc/intel/Kconfig121
-rw-r--r--sound/soc/intel/Makefile12
-rw-r--r--sound/soc/intel/atom/Makefile9
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.c1577
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.h875
-rw-r--r--sound/soc/intel/atom/sst-mfld-dsp.h525
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-compress.c273
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-pcm.c825
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform.h178
-rw-r--r--sound/soc/intel/atom/sst/Makefile8
-rw-r--r--sound/soc/intel/atom/sst/sst.c579
-rw-r--r--sound/soc/intel/atom/sst/sst.h524
-rw-r--r--sound/soc/intel/atom/sst/sst_acpi.c417
-rw-r--r--sound/soc/intel/atom/sst/sst_drv_interface.c689
-rw-r--r--sound/soc/intel/atom/sst/sst_ipc.c375
-rw-r--r--sound/soc/intel/atom/sst/sst_loader.c453
-rw-r--r--sound/soc/intel/atom/sst/sst_pci.c189
-rw-r--r--sound/soc/intel/atom/sst/sst_pvt.c372
-rw-r--r--sound/soc/intel/atom/sst/sst_stream.c471
-rw-r--r--sound/soc/intel/avs/Makefile20
-rw-r--r--sound/soc/intel/avs/apl.c270
-rw-r--r--sound/soc/intel/avs/avs.h367
-rw-r--r--sound/soc/intel/avs/board_selection.c672
-rw-r--r--sound/soc/intel/avs/boards/Kconfig199
-rw-r--r--sound/soc/intel/avs/boards/Makefile41
-rw-r--r--sound/soc/intel/avs/boards/da7219.c282
-rw-r--r--sound/soc/intel/avs/boards/dmic.c126
-rw-r--r--sound/soc/intel/avs/boards/es8336.c331
-rw-r--r--sound/soc/intel/avs/boards/hdaudio.c254
-rw-r--r--sound/soc/intel/avs/boards/i2s_test.c129
-rw-r--r--sound/soc/intel/avs/boards/max98357a.c158
-rw-r--r--sound/soc/intel/avs/boards/max98373.c213
-rw-r--r--sound/soc/intel/avs/boards/max98927.c210
-rw-r--r--sound/soc/intel/avs/boards/nau8825.c315
-rw-r--r--sound/soc/intel/avs/boards/pcm3168a.c155
-rw-r--r--sound/soc/intel/avs/boards/probe.c83
-rw-r--r--sound/soc/intel/avs/boards/rt274.c280
-rw-r--r--sound/soc/intel/avs/boards/rt286.c250
-rw-r--r--sound/soc/intel/avs/boards/rt298.c269
-rw-r--r--sound/soc/intel/avs/boards/rt5514.c197
-rw-r--r--sound/soc/intel/avs/boards/rt5640.c271
-rw-r--r--sound/soc/intel/avs/boards/rt5663.c268
-rw-r--r--sound/soc/intel/avs/boards/rt5682.c345
-rw-r--r--sound/soc/intel/avs/boards/ssm4567.c199
-rw-r--r--sound/soc/intel/avs/cldma.c290
-rw-r--r--sound/soc/intel/avs/cldma.h32
-rw-r--r--sound/soc/intel/avs/cnl.c92
-rw-r--r--sound/soc/intel/avs/control.c214
-rw-r--r--sound/soc/intel/avs/control.h27
-rw-r--r--sound/soc/intel/avs/core.c959
-rw-r--r--sound/soc/intel/avs/debug.h91
-rw-r--r--sound/soc/intel/avs/debugfs.c438
-rw-r--r--sound/soc/intel/avs/dsp.c329
-rw-r--r--sound/soc/intel/avs/icl.c203
-rw-r--r--sound/soc/intel/avs/ipc.c583
-rw-r--r--sound/soc/intel/avs/lnl.c28
-rw-r--r--sound/soc/intel/avs/loader.c750
-rw-r--r--sound/soc/intel/avs/messages.c904
-rw-r--r--sound/soc/intel/avs/messages.h1035
-rw-r--r--sound/soc/intel/avs/mtl.c201
-rw-r--r--sound/soc/intel/avs/path.c1608
-rw-r--r--sound/soc/intel/avs/path.h95
-rw-r--r--sound/soc/intel/avs/pcm.c1773
-rw-r--r--sound/soc/intel/avs/pcm.h16
-rw-r--r--sound/soc/intel/avs/probes.c314
-rw-r--r--sound/soc/intel/avs/ptl.c99
-rw-r--r--sound/soc/intel/avs/registers.h184
-rw-r--r--sound/soc/intel/avs/skl.c180
-rw-r--r--sound/soc/intel/avs/sysfs.c35
-rw-r--r--sound/soc/intel/avs/tgl.c87
-rw-r--r--sound/soc/intel/avs/topology.c2247
-rw-r--r--sound/soc/intel/avs/topology.h238
-rw-r--r--sound/soc/intel/avs/trace.c33
-rw-r--r--sound/soc/intel/avs/trace.h156
-rw-r--r--sound/soc/intel/avs/utils.c302
-rw-r--r--sound/soc/intel/avs/utils.h77
-rw-r--r--sound/soc/intel/boards/Kconfig542
-rw-r--r--sound/soc/intel/boards/Makefile73
-rw-r--r--sound/soc/intel/boards/bdw-rt5650.c335
-rw-r--r--sound/soc/intel/boards/bdw-rt5677.c460
-rw-r--r--sound/soc/intel/boards/bdw_rt286.c259
-rw-r--r--sound/soc/intel/boards/bytcht_cx2072x.c296
-rw-r--r--sound/soc/intel/boards/bytcht_da7213.c307
-rw-r--r--sound/soc/intel/boards/bytcht_es8316.c730
-rw-r--r--sound/soc/intel/boards/bytcht_nocodec.c193
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c1999
-rw-r--r--sound/soc/intel/boards/bytcr_rt5651.c1165
-rw-r--r--sound/soc/intel/boards/bytcr_wm5102.c670
-rw-r--r--sound/soc/intel/boards/cht_bsw_max98090_ti.c643
-rw-r--r--sound/soc/intel/boards/cht_bsw_nau8824.c308
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c726
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5672.c550
-rw-r--r--sound/soc/intel/boards/ehl_rt5660.c316
-rw-r--r--sound/soc/intel/boards/hda_dsp_common.c100
-rw-r--r--sound/soc/intel/boards/hda_dsp_common.h29
-rw-r--r--sound/soc/intel/boards/hsw_rt5640.c173
-rw-r--r--sound/soc/intel/boards/skl_hda_dsp_generic.c178
-rw-r--r--sound/soc/intel/boards/sof_board_helpers.c785
-rw-r--r--sound/soc/intel/boards/sof_board_helpers.h173
-rw-r--r--sound/soc/intel/boards/sof_cirrus_common.c207
-rw-r--r--sound/soc/intel/boards/sof_cirrus_common.h26
-rw-r--r--sound/soc/intel/boards/sof_cs42l42.c307
-rw-r--r--sound/soc/intel/boards/sof_da7219.c490
-rw-r--r--sound/soc/intel/boards/sof_es8336.c858
-rw-r--r--sound/soc/intel/boards/sof_hdmi_common.h24
-rw-r--r--sound/soc/intel/boards/sof_maxim_common.c612
-rw-r--r--sound/soc/intel/boards/sof_maxim_common.h48
-rw-r--r--sound/soc/intel/boards/sof_nau8825.c345
-rw-r--r--sound/soc/intel/boards/sof_nuvoton_common.c74
-rw-r--r--sound/soc/intel/boards/sof_nuvoton_common.h22
-rw-r--r--sound/soc/intel/boards/sof_pcm512x.c444
-rw-r--r--sound/soc/intel/boards/sof_realtek_common.c689
-rw-r--r--sound/soc/intel/boards/sof_realtek_common.h63
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c936
-rw-r--r--sound/soc/intel/boards/sof_sdw.c1500
-rw-r--r--sound/soc/intel/boards/sof_sdw_common.h66
-rw-r--r--sound/soc/intel/boards/sof_sdw_hdmi.c43
-rw-r--r--sound/soc/intel/boards/sof_ssp_amp.c245
-rw-r--r--sound/soc/intel/boards/sof_wm8804.c302
-rw-r--r--sound/soc/intel/catpt/Makefile6
-rw-r--r--sound/soc/intel/catpt/core.h175
-rw-r--r--sound/soc/intel/catpt/device.c399
-rw-r--r--sound/soc/intel/catpt/dsp.c545
-rw-r--r--sound/soc/intel/catpt/ipc.c298
-rw-r--r--sound/soc/intel/catpt/loader.c671
-rw-r--r--sound/soc/intel/catpt/messages.c313
-rw-r--r--sound/soc/intel/catpt/messages.h399
-rw-r--r--sound/soc/intel/catpt/pcm.c1201
-rw-r--r--sound/soc/intel/catpt/registers.h178
-rw-r--r--sound/soc/intel/catpt/sysfs.c56
-rw-r--r--sound/soc/intel/catpt/trace.h83
-rw-r--r--sound/soc/intel/common/Makefile23
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-adl-match.c783
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-arl-match.c534
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-bxt-match.c95
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-byt-match.c220
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cfl-match.c20
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cht-match.c235
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cml-match.c314
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-cnl-match.c84
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-ehl-match.c20
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-glk-match.c68
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-hda-match.c24
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c35
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-icl-match.c189
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-jsl-match.c117
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-kbl-match.c119
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-lnl-match.c801
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-mtl-match.c1246
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-nvl-match.c90
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-ptl-match.c773
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-rpl-match.c568
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c42
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h14
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.c208
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-sdw-mockup-match.h18
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-skl-match.c39
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-ssp-common.c168
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-tgl-match.c834
-rw-r--r--sound/soc/intel/common/soc-intel-quirks.h98
-rw-r--r--sound/soc/intel/common/sof-function-topology-lib.c140
-rw-r--r--sound/soc/intel/common/sof-function-topology-lib.h15
-rw-r--r--sound/soc/intel/keembay/Makefile4
-rw-r--r--sound/soc/intel/keembay/kmb_platform.c928
-rw-r--r--sound/soc/intel/keembay/kmb_platform.h156
-rw-r--r--sound/soc/jz4740/Kconfig23
-rw-r--r--sound/soc/jz4740/Makefile10
-rw-r--r--sound/soc/jz4740/jz4740-i2s.c695
-rw-r--r--sound/soc/jz4740/jz4740-i2s.h16
-rw-r--r--sound/soc/jz4740/jz4740-pcm.c371
-rw-r--r--sound/soc/jz4740/jz4740-pcm.h20
-rw-r--r--sound/soc/jz4740/qi_lb60.c157
-rw-r--r--sound/soc/kirkwood/Kconfig21
-rw-r--r--sound/soc/kirkwood/Makefile9
-rw-r--r--sound/soc/kirkwood/armada-370-db.c156
-rw-r--r--sound/soc/kirkwood/kirkwood-dma.c307
-rw-r--r--sound/soc/kirkwood/kirkwood-i2s.c605
-rw-r--r--sound/soc/kirkwood/kirkwood-openrd.c120
-rw-r--r--sound/soc/kirkwood/kirkwood.h41
-rw-r--r--sound/soc/loongson/Kconfig49
-rw-r--r--sound/soc/loongson/Makefile15
-rw-r--r--sound/soc/loongson/loongson1_ac97.c398
-rw-r--r--sound/soc/loongson/loongson_card.c220
-rw-r--r--sound/soc/loongson/loongson_dma.c345
-rw-r--r--sound/soc/loongson/loongson_dma.h16
-rw-r--r--sound/soc/loongson/loongson_i2s.c276
-rw-r--r--sound/soc/loongson/loongson_i2s.h71
-rw-r--r--sound/soc/loongson/loongson_i2s_pci.c162
-rw-r--r--sound/soc/loongson/loongson_i2s_plat.c185
-rw-r--r--sound/soc/mediatek/Kconfig356
-rw-r--r--sound/soc/mediatek/Makefile13
-rw-r--r--sound/soc/mediatek/common/Makefile8
-rw-r--r--sound/soc/mediatek/common/mtk-afe-fe-dai.c592
-rw-r--r--sound/soc/mediatek/common/mtk-afe-fe-dai.h51
-rw-r--r--sound/soc/mediatek/common/mtk-afe-platform-driver.c160
-rw-r--r--sound/soc/mediatek/common/mtk-afe-platform-driver.h28
-rw-r--r--sound/soc/mediatek/common/mtk-base-afe.h173
-rw-r--r--sound/soc/mediatek/common/mtk-btcvsd.c1410
-rw-r--r--sound/soc/mediatek/common/mtk-dai-adda-common.c70
-rw-r--r--sound/soc/mediatek/common/mtk-dai-adda-common.h45
-rw-r--r--sound/soc/mediatek/common/mtk-dsp-sof-common.c275
-rw-r--r--sound/soc/mediatek/common/mtk-dsp-sof-common.h43
-rw-r--r--sound/soc/mediatek/common/mtk-soc-card.h23
-rw-r--r--sound/soc/mediatek/common/mtk-soundcard-driver.c349
-rw-r--r--sound/soc/mediatek/common/mtk-soundcard-driver.h56
-rw-r--r--sound/soc/mediatek/mt2701/Makefile8
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c298
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h34
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-common.h98
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-pcm.c1483
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-cs42448.c420
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-reg.h141
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-wm8960.c182
-rw-r--r--sound/soc/mediatek/mt6797/Makefile14
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-afe-clk.c123
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-afe-clk.h17
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-afe-common.h59
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-afe-pcm.c900
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-dai-adda.c323
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-dai-hostless.c118
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-dai-pcm.c319
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-interconnection.h33
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-mt6351.c255
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-reg.h1015
-rw-r--r--sound/soc/mediatek/mt7986/Makefile9
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-afe-common.h49
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-afe-pcm.c609
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-dai-etdm.c426
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-reg.h196
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-wm8960.c175
-rw-r--r--sound/soc/mediatek/mt8173/Makefile8
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-afe-common.h65
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-afe-pcm.c1241
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-max98090.c208
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c257
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c325
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650.c362
-rw-r--r--sound/soc/mediatek/mt8183/Makefile15
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-clk.c614
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-clk.h38
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-common.h111
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-pcm.c979
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c887
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-adda.c426
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-hostless.c118
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-i2s.c1058
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-pcm.c319
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-tdm.c748
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-interconnection.h33
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c899
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-reg.h1668
-rw-r--r--sound/soc/mediatek/mt8186/Makefile21
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-clk.c589
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-clk.h103
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-common.h198
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-control.c254
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-gpio.c242
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-gpio.h19
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-pcm.c3005
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-audsys-clk.c152
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-audsys-clk.h14
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-audsys-clkid.h45
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-adda.c776
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-hostless.c298
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-hw-gain.c236
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-i2s.c1231
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-pcm.c419
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-src.c695
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-tdm.c643
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-interconnection.h69
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-misc-control.c252
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-mt6366-common.c57
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-mt6366-common.h17
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-mt6366.c1377
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-reg.h2913
-rw-r--r--sound/soc/mediatek/mt8188/Makefile16
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-clk.c755
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-clk.h137
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-common.h153
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-pcm.c3399
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-audsys-clk.c210
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-audsys-clk.h14
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-audsys-clkid.h87
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-adda.c512
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-dmic.c683
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-etdm.c2712
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-pcm.c368
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-mt6359.c1478
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-reg.h3195
-rw-r--r--sound/soc/mediatek/mt8189/Makefile18
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-afe-clk.c750
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-afe-clk.h76
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-afe-common.h240
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-afe-pcm.c2615
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-dai-adda.c1228
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-dai-i2s.c1463
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-dai-pcm.c332
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-dai-tdm.c672
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-interconnection.h97
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-nau8825.c1178
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-reg.h10773
-rw-r--r--sound/soc/mediatek/mt8192/Makefile16
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-clk.c665
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-clk.h244
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-common.h173
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-control.c161
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-gpio.c307
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-gpio.h19
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-pcm.c2341
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-adda.c1365
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-i2s.c2101
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-pcm.c411
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-tdm.c778
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-interconnection.h65
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c1239
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-reg.h3133
-rw-r--r--sound/soc/mediatek/mt8195/Makefile15
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-clk.c716
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-clk.h119
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-common.h158
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-pcm.c3206
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-audsys-clk.c215
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-audsys-clk.h14
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-audsys-clkid.h93
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-adda.c753
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-etdm.c2778
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-pcm.c369
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-mt6359.c1581
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-reg.h2797
-rw-r--r--sound/soc/mediatek/mt8365/Makefile15
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-afe-clk.c420
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-afe-clk.h32
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-afe-common.h448
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-afe-pcm.c2271
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-dai-adda.c311
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-dai-dmic.c310
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-dai-i2s.c845
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-dai-pcm.c293
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-mt6357.c346
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-reg.h993
-rw-r--r--sound/soc/meson/Kconfig138
-rw-r--r--sound/soc/meson/Makefile46
-rw-r--r--sound/soc/meson/aiu-acodec-ctrl.c203
-rw-r--r--sound/soc/meson/aiu-codec-ctrl.c151
-rw-r--r--sound/soc/meson/aiu-encoder-i2s.c334
-rw-r--r--sound/soc/meson/aiu-encoder-spdif.c209
-rw-r--r--sound/soc/meson/aiu-fifo-i2s.c173
-rw-r--r--sound/soc/meson/aiu-fifo-spdif.c188
-rw-r--r--sound/soc/meson/aiu-fifo.c215
-rw-r--r--sound/soc/meson/aiu-fifo.h48
-rw-r--r--sound/soc/meson/aiu.c358
-rw-r--r--sound/soc/meson/aiu.h88
-rw-r--r--sound/soc/meson/axg-card.c373
-rw-r--r--sound/soc/meson/axg-fifo.c398
-rw-r--r--sound/soc/meson/axg-fifo.h95
-rw-r--r--sound/soc/meson/axg-frddr.c400
-rw-r--r--sound/soc/meson/axg-pdm.c641
-rw-r--r--sound/soc/meson/axg-spdifin.c496
-rw-r--r--sound/soc/meson/axg-spdifout.c447
-rw-r--r--sound/soc/meson/axg-tdm-formatter.c437
-rw-r--r--sound/soc/meson/axg-tdm-formatter.h45
-rw-r--r--sound/soc/meson/axg-tdm-interface.c584
-rw-r--r--sound/soc/meson/axg-tdm.h81
-rw-r--r--sound/soc/meson/axg-tdmin.c260
-rw-r--r--sound/soc/meson/axg-tdmout.c339
-rw-r--r--sound/soc/meson/axg-toddr.c353
-rw-r--r--sound/soc/meson/g12a-toacodec.c356
-rw-r--r--sound/soc/meson/g12a-tohdmitx.c283
-rw-r--r--sound/soc/meson/gx-card.c142
-rw-r--r--sound/soc/meson/meson-card-utils.c334
-rw-r--r--sound/soc/meson/meson-card.h54
-rw-r--r--sound/soc/meson/meson-codec-glue.c147
-rw-r--r--sound/soc/meson/meson-codec-glue.h32
-rw-r--r--sound/soc/meson/t9015.c314
-rw-r--r--sound/soc/mxs/Kconfig22
-rw-r--r--sound/soc/mxs/Makefile11
-rw-r--r--sound/soc/mxs/mxs-pcm.c47
-rw-r--r--sound/soc/mxs/mxs-pcm.h11
-rw-r--r--sound/soc/mxs/mxs-saif.c897
-rw-r--r--sound/soc/mxs/mxs-saif.h123
-rw-r--r--sound/soc/mxs/mxs-sgtl5000.c196
-rw-r--r--sound/soc/nuc900/Kconfig27
-rw-r--r--sound/soc/nuc900/Makefile11
-rw-r--r--sound/soc/nuc900/nuc900-ac97.c425
-rw-r--r--sound/soc/nuc900/nuc900-audio.c75
-rw-r--r--sound/soc/nuc900/nuc900-audio.h113
-rw-r--r--sound/soc/nuc900/nuc900-pcm.c372
-rw-r--r--sound/soc/omap/Kconfig142
-rw-r--r--sound/soc/omap/Makefile39
-rw-r--r--sound/soc/omap/am3517evm.c195
-rw-r--r--sound/soc/omap/igep0020.c138
-rw-r--r--sound/soc/omap/mcpdm.c470
-rw-r--r--sound/soc/omap/mcpdm.h153
-rw-r--r--sound/soc/omap/omap-mcbsp.c866
-rw-r--r--sound/soc/omap/omap-mcbsp.h60
-rw-r--r--sound/soc/omap/omap-mcpdm.c272
-rw-r--r--sound/soc/omap/omap-pcm.c438
-rw-r--r--sound/soc/omap/omap-pcm.h38
-rw-r--r--sound/soc/omap/omap2evm.c140
-rw-r--r--sound/soc/omap/omap3beagle.c150
-rw-r--r--sound/soc/omap/omap3evm.c136
-rw-r--r--sound/soc/omap/overo.c140
-rw-r--r--sound/soc/omap/rx51.c352
-rw-r--r--sound/soc/omap/sdp3430.c345
-rw-r--r--sound/soc/omap/sdp4430.c226
-rw-r--r--sound/soc/omap/zoom2.c291
-rw-r--r--sound/soc/pxa/Kconfig181
-rw-r--r--sound/soc/pxa/Makefile46
-rw-r--r--sound/soc/pxa/corgi.c357
-rw-r--r--sound/soc/pxa/e740_wm9705.c212
-rw-r--r--sound/soc/pxa/e750_wm9705.c185
-rw-r--r--sound/soc/pxa/e800_wm9712.c172
-rw-r--r--sound/soc/pxa/em-x270.c96
-rw-r--r--sound/soc/pxa/imote2.c108
-rw-r--r--sound/soc/pxa/magician.c562
-rw-r--r--sound/soc/pxa/mioa701_wm9713.c247
-rw-r--r--sound/soc/pxa/mmp-sspa.c585
-rw-r--r--sound/soc/pxa/mmp-sspa.h70
-rw-r--r--sound/soc/pxa/palm27x.c224
-rw-r--r--sound/soc/pxa/poodle.c331
-rw-r--r--sound/soc/pxa/pxa-ssp.c594
-rw-r--r--sound/soc/pxa/pxa-ssp.h11
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c268
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.h20
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c205
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.h8
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c146
-rw-r--r--sound/soc/pxa/raumfeld.c312
-rw-r--r--sound/soc/pxa/saarb.c200
-rw-r--r--sound/soc/pxa/spitz.c256
-rw-r--r--sound/soc/pxa/tavorevb3.c200
-rw-r--r--sound/soc/pxa/tosa.c304
-rw-r--r--sound/soc/pxa/z2.c240
-rw-r--r--sound/soc/pxa/zylonite.c292
-rw-r--r--sound/soc/qcom/Kconfig255
-rw-r--r--sound/soc/qcom/Makefile49
-rw-r--r--sound/soc/qcom/apq8016_sbc.c347
-rw-r--r--sound/soc/qcom/apq8096.c145
-rw-r--r--sound/soc/qcom/common.c278
-rw-r--r--sound/soc/qcom/common.h16
-rw-r--r--sound/soc/qcom/lpass-apq8016.c309
-rw-r--r--sound/soc/qcom/lpass-cdc-dma.c303
-rw-r--r--sound/soc/qcom/lpass-cpu.c1303
-rw-r--r--sound/soc/qcom/lpass-hdmi.c254
-rw-r--r--sound/soc/qcom/lpass-hdmi.h102
-rw-r--r--sound/soc/qcom/lpass-ipq806x.c180
-rw-r--r--sound/soc/qcom/lpass-lpaif-reg.h319
-rw-r--r--sound/soc/qcom/lpass-platform.c1396
-rw-r--r--sound/soc/qcom/lpass-sc7180.c325
-rw-r--r--sound/soc/qcom/lpass-sc7280.c455
-rw-r--r--sound/soc/qcom/lpass.h410
-rw-r--r--sound/soc/qcom/qdsp6/Makefile20
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.c1339
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.h821
-rw-r--r--sound/soc/qcom/qdsp6/q6adm.c607
-rw-r--r--sound/soc/qcom/qdsp6/q6adm.h27
-rw-r--r--sound/soc/qcom/qdsp6/q6afe-clocks.c120
-rw-r--r--sound/soc/qcom/qdsp6/q6afe-dai.c1147
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.c1941
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.h274
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-dai.c884
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-lpass-dais.c324
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.c828
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.h156
-rw-r--r--sound/soc/qcom/qdsp6/q6asm-dai.c1343
-rw-r--r--sound/soc/qcom/qdsp6/q6asm.c1676
-rw-r--r--sound/soc/qcom/qdsp6/q6asm.h152
-rw-r--r--sound/soc/qcom/qdsp6/q6core.c375
-rw-r--r--sound/soc/qcom/qdsp6/q6core.h15
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-common.c103
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-common.h25
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-errno.h51
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c185
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.h30
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c658
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h23
-rw-r--r--sound/soc/qcom/qdsp6/q6prm-clocks.c94
-rw-r--r--sound/soc/qcom/qdsp6/q6prm.c240
-rw-r--r--sound/soc/qcom/qdsp6/q6prm.h97
-rw-r--r--sound/soc/qcom/qdsp6/q6routing.c1174
-rw-r--r--sound/soc/qcom/qdsp6/q6routing.h9
-rw-r--r--sound/soc/qcom/qdsp6/q6usb.c422
-rw-r--r--sound/soc/qcom/qdsp6/topology.c1340
-rw-r--r--sound/soc/qcom/sc7180.c582
-rw-r--r--sound/soc/qcom/sc7280.c400
-rw-r--r--sound/soc/qcom/sc8280xp.c194
-rw-r--r--sound/soc/qcom/sdm845.c591
-rw-r--r--sound/soc/qcom/sdw.c216
-rw-r--r--sound/soc/qcom/sdw.h16
-rw-r--r--sound/soc/qcom/sm8250.c205
-rw-r--r--sound/soc/qcom/storm.c143
-rw-r--r--sound/soc/qcom/usb_offload_utils.c56
-rw-r--r--sound/soc/qcom/usb_offload_utils.h30
-rw-r--r--sound/soc/qcom/x1e80100.c210
-rw-r--r--sound/soc/renesas/Kconfig (renamed from sound/soc/sh/Kconfig)59
-rw-r--r--sound/soc/renesas/Makefile28
-rw-r--r--sound/soc/renesas/dma-sh7760.c (renamed from sound/soc/sh/dma-sh7760.c)170
-rw-r--r--sound/soc/renesas/fsi.c2105
-rw-r--r--sound/soc/renesas/hac.c (renamed from sound/soc/sh/hac.c)63
-rw-r--r--sound/soc/renesas/migor.c (renamed from sound/soc/sh/migor.c)75
-rw-r--r--sound/soc/renesas/rcar/Makefile6
-rw-r--r--sound/soc/renesas/rcar/adg.c819
-rw-r--r--sound/soc/renesas/rcar/cmd.c191
-rw-r--r--sound/soc/renesas/rcar/core.c2080
-rw-r--r--sound/soc/renesas/rcar/ctu.c385
-rw-r--r--sound/soc/renesas/rcar/debugfs.c96
-rw-r--r--sound/soc/renesas/rcar/dma.c883
-rw-r--r--sound/soc/renesas/rcar/dvc.c388
-rw-r--r--sound/soc/renesas/rcar/gen.c495
-rw-r--r--sound/soc/renesas/rcar/mix.c352
-rw-r--r--sound/soc/renesas/rcar/msiof.c629
-rw-r--r--sound/soc/renesas/rcar/rsnd.h895
-rw-r--r--sound/soc/renesas/rcar/src.c793
-rw-r--r--sound/soc/renesas/rcar/ssi.c1252
-rw-r--r--sound/soc/renesas/rcar/ssiu.c605
-rw-r--r--sound/soc/renesas/rz-ssi.c1287
-rw-r--r--sound/soc/renesas/sh7760-ac97.c (renamed from sound/soc/sh/sh7760-ac97.c)36
-rw-r--r--sound/soc/renesas/siu.h (renamed from sound/soc/sh/siu.h)32
-rw-r--r--sound/soc/renesas/siu_dai.c (renamed from sound/soc/sh/siu_dai.c)153
-rw-r--r--sound/soc/renesas/siu_pcm.c (renamed from sound/soc/sh/siu_pcm.c)191
-rw-r--r--sound/soc/renesas/ssi.c (renamed from sound/soc/sh/ssi.c)67
-rw-r--r--sound/soc/rockchip/Kconfig95
-rw-r--r--sound/soc/rockchip/Makefile23
-rw-r--r--sound/soc/rockchip/rk3288_hdmi_analog.c263
-rw-r--r--sound/soc/rockchip/rk3399_gru_sound.c627
-rw-r--r--sound/soc/rockchip/rockchip_i2s.c881
-rw-r--r--sound/soc/rockchip/rockchip_i2s.h248
-rw-r--r--sound/soc/rockchip/rockchip_i2s_tdm.c1435
-rw-r--r--sound/soc/rockchip/rockchip_i2s_tdm.h400
-rw-r--r--sound/soc/rockchip/rockchip_max98090.c469
-rw-r--r--sound/soc/rockchip/rockchip_pdm.c716
-rw-r--r--sound/soc/rockchip/rockchip_pdm.h92
-rw-r--r--sound/soc/rockchip/rockchip_rt5645.c249
-rw-r--r--sound/soc/rockchip/rockchip_sai.c1529
-rw-r--r--sound/soc/rockchip/rockchip_sai.h251
-rw-r--r--sound/soc/rockchip/rockchip_spdif.c394
-rw-r--r--sound/soc/rockchip/rockchip_spdif.h60
-rw-r--r--sound/soc/s3c24xx/Kconfig171
-rw-r--r--sound/soc/s3c24xx/Makefile55
-rw-r--r--sound/soc/s3c24xx/aquila_wm8994.c295
-rw-r--r--sound/soc/s3c24xx/goni_wm8994.c298
-rw-r--r--sound/soc/s3c24xx/jive_wm8750.c191
-rw-r--r--sound/soc/s3c24xx/lm4857.h32
-rw-r--r--sound/soc/s3c24xx/ln2440sbc_alc650.c78
-rw-r--r--sound/soc/s3c24xx/neo1973_gta02_wm8753.c504
-rw-r--r--sound/soc/s3c24xx/neo1973_wm8753.c704
-rw-r--r--sound/soc/s3c24xx/regs-i2s-v2.h115
-rw-r--r--sound/soc/s3c24xx/rx1950_uda1380.c333
-rw-r--r--sound/soc/s3c24xx/s3c-ac97.c520
-rw-r--r--sound/soc/s3c24xx/s3c-ac97.h21
-rw-r--r--sound/soc/s3c24xx/s3c-dma.c502
-rw-r--r--sound/soc/s3c24xx/s3c-dma.h30
-rw-r--r--sound/soc/s3c24xx/s3c-i2s-v2.c757
-rw-r--r--sound/soc/s3c24xx/s3c-i2s-v2.h106
-rw-r--r--sound/soc/s3c24xx/s3c-pcm.h124
-rw-r--r--sound/soc/s3c24xx/s3c2412-i2s.c212
-rw-r--r--sound/soc/s3c24xx/s3c2412-i2s.h27
-rw-r--r--sound/soc/s3c24xx/s3c24xx-i2s.c519
-rw-r--r--sound/soc/s3c24xx/s3c24xx-i2s.h35
-rw-r--r--sound/soc/s3c24xx/s3c24xx_simtec.c395
-rw-r--r--sound/soc/s3c24xx/s3c24xx_simtec.h22
-rw-r--r--sound/soc/s3c24xx/s3c24xx_simtec_hermes.c146
-rw-r--r--sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c134
-rw-r--r--sound/soc/s3c24xx/s3c24xx_uda134x.c368
-rw-r--r--sound/soc/s3c24xx/s3c64xx-i2s-v4.c230
-rw-r--r--sound/soc/s3c24xx/s3c64xx-i2s.c242
-rw-r--r--sound/soc/s3c24xx/s3c64xx-i2s.h41
-rw-r--r--sound/soc/s3c24xx/smartq_wm8987.c290
-rw-r--r--sound/soc/s3c24xx/smdk2443_wm9710.c74
-rw-r--r--sound/soc/s3c24xx/smdk64xx_wm8580.c272
-rw-r--r--sound/soc/s3c24xx/smdk_wm9713.c107
-rw-r--r--sound/soc/s6000/Kconfig19
-rw-r--r--sound/soc/s6000/Makefile11
-rw-r--r--sound/soc/s6000/s6000-i2s.c621
-rw-r--r--sound/soc/s6000/s6000-i2s.h23
-rw-r--r--sound/soc/s6000/s6000-pcm.c537
-rw-r--r--sound/soc/s6000/s6000-pcm.h33
-rw-r--r--sound/soc/s6000/s6105-ipcam.c243
-rw-r--r--sound/soc/samsung/Kconfig151
-rw-r--r--sound/soc/samsung/Makefile44
-rw-r--r--sound/soc/samsung/aries_wm8994.c702
-rw-r--r--sound/soc/samsung/arndale.c217
-rw-r--r--sound/soc/samsung/bells.c497
-rw-r--r--sound/soc/samsung/dma.h18
-rw-r--r--sound/soc/samsung/dmaengine.c41
-rw-r--r--sound/soc/samsung/i2s-regs.h158
-rw-r--r--sound/soc/samsung/i2s.c1754
-rw-r--r--sound/soc/samsung/i2s.h27
-rw-r--r--sound/soc/samsung/idma.c428
-rw-r--r--sound/soc/samsung/idma.h19
-rw-r--r--sound/soc/samsung/littlemill.c360
-rw-r--r--sound/soc/samsung/lowland.c213
-rw-r--r--sound/soc/samsung/midas_wm1811.c775
-rw-r--r--sound/soc/samsung/odroid.c349
-rw-r--r--sound/soc/samsung/pcm.c (renamed from sound/soc/s3c24xx/s3c-pcm.c)371
-rw-r--r--sound/soc/samsung/pcm.h11
-rw-r--r--sound/soc/samsung/smdk_spdif.c (renamed from sound/soc/s3c24xx/smdk_spdif.c)64
-rw-r--r--sound/soc/samsung/smdk_wm8994.c179
-rw-r--r--sound/soc/samsung/smdk_wm8994pcm.c138
-rw-r--r--sound/soc/samsung/snow.c254
-rw-r--r--sound/soc/samsung/spdif.c (renamed from sound/soc/s3c24xx/spdif.c)173
-rw-r--r--sound/soc/samsung/spdif.h (renamed from sound/soc/s3c24xx/spdif.h)8
-rw-r--r--sound/soc/samsung/speyside.c388
-rw-r--r--sound/soc/samsung/tm2_wm5110.c677
-rw-r--r--sound/soc/samsung/tobermory.c248
-rw-r--r--sound/soc/sdca/Kconfig59
-rw-r--r--sound/soc/sdca/Makefile15
-rw-r--r--sound/soc/sdca/sdca_asoc.c1559
-rw-r--r--sound/soc/sdca/sdca_class.c304
-rw-r--r--sound/soc/sdca/sdca_class.h37
-rw-r--r--sound/soc/sdca/sdca_class_function.c460
-rw-r--r--sound/soc/sdca/sdca_device.c108
-rw-r--r--sound/soc/sdca/sdca_fdl.c504
-rw-r--r--sound/soc/sdca/sdca_function_device.c117
-rw-r--r--sound/soc/sdca/sdca_function_device.h15
-rw-r--r--sound/soc/sdca/sdca_functions.c2282
-rw-r--r--sound/soc/sdca/sdca_hid.c168
-rw-r--r--sound/soc/sdca/sdca_interrupts.c612
-rw-r--r--sound/soc/sdca/sdca_regmap.c373
-rw-r--r--sound/soc/sdca/sdca_ump.c262
-rw-r--r--sound/soc/sdw_utils/Kconfig6
-rw-r--r--sound/soc/sdw_utils/Makefile12
-rw-r--r--sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c156
-rw-r--r--sound/soc/sdw_utils/soc_sdw_cs42l42.c92
-rw-r--r--sound/soc/sdw_utils/soc_sdw_cs42l43.c174
-rw-r--r--sound/soc/sdw_utils/soc_sdw_cs42l45.c80
-rw-r--r--sound/soc/sdw_utils/soc_sdw_cs_amp.c125
-rw-r--r--sound/soc/sdw_utils/soc_sdw_dmic.c46
-rw-r--r--sound/soc/sdw_utils/soc_sdw_maxim.c139
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt5682.c92
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt700.c89
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt711.c161
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_amp.c307
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_amp_coeff_tables.h300
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_dmic.c45
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c84
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_sdca_jack_common.c223
-rw-r--r--sound/soc/sdw_utils/soc_sdw_ti_amp.c93
-rw-r--r--sound/soc/sdw_utils/soc_sdw_utils.c1612
-rw-r--r--sound/soc/sh/Makefile26
-rw-r--r--sound/soc/sh/fsi-ak4642.c81
-rw-r--r--sound/soc/sh/fsi-da7210.c70
-rw-r--r--sound/soc/sh/fsi-hdmi.c60
-rw-r--r--sound/soc/sh/fsi.c1294
-rw-r--r--sound/soc/soc-ac97.c404
-rw-r--r--sound/soc/soc-acpi.c204
-rw-r--r--sound/soc/soc-cache.c726
-rw-r--r--sound/soc/soc-card-test.c129
-rw-r--r--sound/soc/soc-card.c248
-rw-r--r--sound/soc/soc-component.c1300
-rw-r--r--sound/soc/soc-compress.c675
-rw-r--r--sound/soc/soc-core.c5643
-rw-r--r--sound/soc/soc-dai.c816
-rw-r--r--sound/soc/soc-dapm.c5228
-rw-r--r--sound/soc/soc-devres.c131
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c506
-rw-r--r--sound/soc/soc-jack.c332
-rw-r--r--sound/soc/soc-link.c213
-rw-r--r--sound/soc/soc-ops-test.c548
-rw-r--r--sound/soc/soc-ops.c843
-rw-r--r--sound/soc/soc-pcm.c3004
-rw-r--r--sound/soc/soc-topology-test.c821
-rw-r--r--sound/soc/soc-topology.c2209
-rw-r--r--sound/soc/soc-usb.c322
-rw-r--r--sound/soc/soc-utils-test.c232
-rw-r--r--sound/soc/soc-utils.c310
-rw-r--r--sound/soc/sof/Kconfig306
-rw-r--r--sound/soc/sof/Makefile61
-rw-r--r--sound/soc/sof/amd/Kconfig107
-rw-r--r--sound/soc/sof/amd/Makefile20
-rw-r--r--sound/soc/sof/amd/acp-common.c267
-rw-r--r--sound/soc/sof/amd/acp-dsp-offset.h143
-rw-r--r--sound/soc/sof/amd/acp-ipc.c310
-rw-r--r--sound/soc/sof/amd/acp-loader.c320
-rw-r--r--sound/soc/sof/amd/acp-pcm.c120
-rw-r--r--sound/soc/sof/amd/acp-probes.c147
-rw-r--r--sound/soc/sof/amd/acp-stream.c187
-rw-r--r--sound/soc/sof/amd/acp-trace.c64
-rw-r--r--sound/soc/sof/amd/acp.c968
-rw-r--r--sound/soc/sof/amd/acp.h373
-rw-r--r--sound/soc/sof/amd/acp63.c142
-rw-r--r--sound/soc/sof/amd/acp70.c142
-rw-r--r--sound/soc/sof/amd/pci-acp63.c115
-rw-r--r--sound/soc/sof/amd/pci-acp70.c121
-rw-r--r--sound/soc/sof/amd/pci-rmb.c104
-rw-r--r--sound/soc/sof/amd/pci-rn.c108
-rw-r--r--sound/soc/sof/amd/pci-vangogh.c101
-rw-r--r--sound/soc/sof/amd/rembrandt.c142
-rw-r--r--sound/soc/sof/amd/renoir.c117
-rw-r--r--sound/soc/sof/amd/vangogh.c177
-rw-r--r--sound/soc/sof/compress.c391
-rw-r--r--sound/soc/sof/control.c220
-rw-r--r--sound/soc/sof/core.c843
-rw-r--r--sound/soc/sof/debug.c469
-rw-r--r--sound/soc/sof/fw-file-profile.c345
-rw-r--r--sound/soc/sof/imx/Kconfig45
-rw-r--r--sound/soc/sof/imx/Makefile9
-rw-r--r--sound/soc/sof/imx/imx-common.c484
-rw-r--r--sound/soc/sof/imx/imx-common.h169
-rw-r--r--sound/soc/sof/imx/imx8.c473
-rw-r--r--sound/soc/sof/imx/imx9.c117
-rw-r--r--sound/soc/sof/intel/Kconfig415
-rw-r--r--sound/soc/sof/intel/Makefile53
-rw-r--r--sound/soc/sof/intel/apl.c122
-rw-r--r--sound/soc/sof/intel/atom.c421
-rw-r--r--sound/soc/sof/intel/atom.h74
-rw-r--r--sound/soc/sof/intel/bdw.c698
-rw-r--r--sound/soc/sof/intel/byt.c480
-rw-r--r--sound/soc/sof/intel/cnl.c521
-rw-r--r--sound/soc/sof/intel/ext_manifest.h35
-rw-r--r--sound/soc/sof/intel/hda-bus.c112
-rw-r--r--sound/soc/sof/intel/hda-codec.c458
-rw-r--r--sound/soc/sof/intel/hda-common-ops.c108
-rw-r--r--sound/soc/sof/intel/hda-ctrl.c338
-rw-r--r--sound/soc/sof/intel/hda-dai-ops.c660
-rw-r--r--sound/soc/sof/intel/hda-dai.c942
-rw-r--r--sound/soc/sof/intel/hda-dsp.c1635
-rw-r--r--sound/soc/sof/intel/hda-ipc.c556
-rw-r--r--sound/soc/sof/intel/hda-ipc.h56
-rw-r--r--sound/soc/sof/intel/hda-loader-skl.c578
-rw-r--r--sound/soc/sof/intel/hda-loader.c728
-rw-r--r--sound/soc/sof/intel/hda-mlink.c1120
-rw-r--r--sound/soc/sof/intel/hda-pcm.c353
-rw-r--r--sound/soc/sof/intel/hda-probes.c150
-rw-r--r--sound/soc/sof/intel/hda-sdw-bpt.c445
-rw-r--r--sound/soc/sof/intel/hda-stream.c1213
-rw-r--r--sound/soc/sof/intel/hda-trace.c98
-rw-r--r--sound/soc/sof/intel/hda.c1757
-rw-r--r--sound/soc/sof/intel/hda.h1056
-rw-r--r--sound/soc/sof/intel/icl.c197
-rw-r--r--sound/soc/sof/intel/lnl.c190
-rw-r--r--sound/soc/sof/intel/lnl.h21
-rw-r--r--sound/soc/sof/intel/mtl.c819
-rw-r--r--sound/soc/sof/intel/mtl.h134
-rw-r--r--sound/soc/sof/intel/nvl.c55
-rw-r--r--sound/soc/sof/intel/nvl.h14
-rw-r--r--sound/soc/sof/intel/pci-apl.c111
-rw-r--r--sound/soc/sof/intel/pci-cnl.c149
-rw-r--r--sound/soc/sof/intel/pci-icl.c115
-rw-r--r--sound/soc/sof/intel/pci-lnl.c84
-rw-r--r--sound/soc/sof/intel/pci-mtl.c147
-rw-r--r--sound/soc/sof/intel/pci-nvl.c82
-rw-r--r--sound/soc/sof/intel/pci-ptl.c113
-rw-r--r--sound/soc/sof/intel/pci-skl.c95
-rw-r--r--sound/soc/sof/intel/pci-tgl.c324
-rw-r--r--sound/soc/sof/intel/pci-tng.c250
-rw-r--r--sound/soc/sof/intel/ptl.c158
-rw-r--r--sound/soc/sof/intel/ptl.h19
-rw-r--r--sound/soc/sof/intel/shim.h220
-rw-r--r--sound/soc/sof/intel/skl.c118
-rw-r--r--sound/soc/sof/intel/telemetry.c96
-rw-r--r--sound/soc/sof/intel/telemetry.h35
-rw-r--r--sound/soc/sof/intel/tgl.c256
-rw-r--r--sound/soc/sof/intel/tracepoints.c5
-rw-r--r--sound/soc/sof/iomem-utils.c127
-rw-r--r--sound/soc/sof/ipc.c235
-rw-r--r--sound/soc/sof/ipc3-control.c731
-rw-r--r--sound/soc/sof/ipc3-dtrace.c664
-rw-r--r--sound/soc/sof/ipc3-loader.c419
-rw-r--r--sound/soc/sof/ipc3-pcm.c439
-rw-r--r--sound/soc/sof/ipc3-priv.h67
-rw-r--r--sound/soc/sof/ipc3-topology.c2713
-rw-r--r--sound/soc/sof/ipc3.c1157
-rw-r--r--sound/soc/sof/ipc4-control.c867
-rw-r--r--sound/soc/sof/ipc4-fw-reg.h155
-rw-r--r--sound/soc/sof/ipc4-loader.c633
-rw-r--r--sound/soc/sof/ipc4-mtrace.c672
-rw-r--r--sound/soc/sof/ipc4-pcm.c1324
-rw-r--r--sound/soc/sof/ipc4-priv.h132
-rw-r--r--sound/soc/sof/ipc4-telemetry.c95
-rw-r--r--sound/soc/sof/ipc4-telemetry.h73
-rw-r--r--sound/soc/sof/ipc4-topology.c3809
-rw-r--r--sound/soc/sof/ipc4-topology.h528
-rw-r--r--sound/soc/sof/ipc4.c906
-rw-r--r--sound/soc/sof/loader.c190
-rw-r--r--sound/soc/sof/mediatek/Kconfig45
-rw-r--r--sound/soc/sof/mediatek/Makefile4
-rw-r--r--sound/soc/sof/mediatek/adsp_helper.h50
-rw-r--r--sound/soc/sof/mediatek/mt8186/Makefile4
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186-clk.c100
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186-clk.h24
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186-loader.c58
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.c570
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.h93
-rw-r--r--sound/soc/sof/mediatek/mt8195/Makefile3
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195-clk.c165
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195-clk.h28
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195-loader.c61
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195.c526
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195.h161
-rw-r--r--sound/soc/sof/mediatek/mtk-adsp-common.c215
-rw-r--r--sound/soc/sof/mediatek/mtk-adsp-common.h20
-rw-r--r--sound/soc/sof/nocodec.c115
-rw-r--r--sound/soc/sof/ops.c187
-rw-r--r--sound/soc/sof/ops.h663
-rw-r--r--sound/soc/sof/pcm.c811
-rw-r--r--sound/soc/sof/pm.c389
-rw-r--r--sound/soc/sof/sof-acpi-dev.c102
-rw-r--r--sound/soc/sof/sof-acpi-dev.h16
-rw-r--r--sound/soc/sof/sof-audio.c979
-rw-r--r--sound/soc/sof/sof-audio.h685
-rw-r--r--sound/soc/sof/sof-client-ipc-flood-test.c383
-rw-r--r--sound/soc/sof/sof-client-ipc-kernel-injector.c161
-rw-r--r--sound/soc/sof/sof-client-ipc-msg-injector.c339
-rw-r--r--sound/soc/sof/sof-client-probes-ipc3.c243
-rw-r--r--sound/soc/sof/sof-client-probes-ipc4.c409
-rw-r--r--sound/soc/sof/sof-client-probes.c587
-rw-r--r--sound/soc/sof/sof-client-probes.h72
-rw-r--r--sound/soc/sof/sof-client.c669
-rw-r--r--sound/soc/sof/sof-client.h76
-rw-r--r--sound/soc/sof/sof-of-dev.c104
-rw-r--r--sound/soc/sof/sof-of-dev.h25
-rw-r--r--sound/soc/sof/sof-pci-dev.c296
-rw-r--r--sound/soc/sof/sof-pci-dev.h17
-rw-r--r--sound/soc/sof/sof-priv.h904
-rw-r--r--sound/soc/sof/sof-utils.c76
-rw-r--r--sound/soc/sof/sof-utils.h19
-rw-r--r--sound/soc/sof/stream-ipc.c131
-rw-r--r--sound/soc/sof/topology.c2581
-rw-r--r--sound/soc/sof/trace.c53
-rw-r--r--sound/soc/sof/xtensa/Kconfig3
-rw-r--r--sound/soc/sof/xtensa/Makefile5
-rw-r--r--sound/soc/sof/xtensa/core.c155
-rw-r--r--sound/soc/spacemit/Kconfig15
-rw-r--r--sound/soc/spacemit/Makefile5
-rw-r--r--sound/soc/spacemit/k1_i2s.c461
-rw-r--r--sound/soc/spear/Kconfig14
-rw-r--r--sound/soc/spear/Makefile9
-rw-r--r--sound/soc/spear/spdif_in.c275
-rw-r--r--sound/soc/spear/spdif_in_regs.h47
-rw-r--r--sound/soc/spear/spdif_out.c367
-rw-r--r--sound/soc/spear/spdif_out_regs.h66
-rw-r--r--sound/soc/spear/spear_pcm.c55
-rw-r--r--sound/soc/spear/spear_pcm.h13
-rw-r--r--sound/soc/sprd/Kconfig20
-rw-r--r--sound/soc/sprd/Makefile8
-rw-r--r--sound/soc/sprd/sprd-mcdt.c1006
-rw-r--r--sound/soc/sprd/sprd-mcdt.h107
-rw-r--r--sound/soc/sprd/sprd-pcm-compress.c671
-rw-r--r--sound/soc/sprd/sprd-pcm-dma.c497
-rw-r--r--sound/soc/sprd/sprd-pcm-dma.h58
-rw-r--r--sound/soc/starfive/Kconfig20
-rw-r--r--sound/soc/starfive/Makefile3
-rw-r--r--sound/soc/starfive/jh7110_pwmdac.c528
-rw-r--r--sound/soc/starfive/jh7110_tdm.c662
-rw-r--r--sound/soc/sti/Kconfig12
-rw-r--r--sound/soc/sti/Makefile5
-rw-r--r--sound/soc/sti/sti_uniperif.c506
-rw-r--r--sound/soc/sti/uniperif.h1417
-rw-r--r--sound/soc/sti/uniperif_player.c1149
-rw-r--r--sound/soc/sti/uniperif_reader.c437
-rw-r--r--sound/soc/stm/Kconfig47
-rw-r--r--sound/soc/stm/Makefile18
-rw-r--r--sound/soc/stm/stm32_adfsdm.c409
-rw-r--r--sound/soc/stm/stm32_i2s.c1394
-rw-r--r--sound/soc/stm/stm32_sai.c316
-rw-r--r--sound/soc/stm/stm32_sai.h308
-rw-r--r--sound/soc/stm/stm32_sai_sub.c1799
-rw-r--r--sound/soc/stm/stm32_spdifrx.c1081
-rw-r--r--sound/soc/sunxi/Kconfig70
-rw-r--r--sound/soc/sunxi/Makefile9
-rw-r--r--sound/soc/sunxi/sun4i-codec.c2457
-rw-r--r--sound/soc/sunxi/sun4i-i2s.c1699
-rw-r--r--sound/soc/sunxi/sun4i-spdif.c772
-rw-r--r--sound/soc/sunxi/sun50i-codec-analog.c596
-rw-r--r--sound/soc/sunxi/sun50i-dmic.c437
-rw-r--r--sound/soc/sunxi/sun8i-adda-pr-regmap.c102
-rw-r--r--sound/soc/sunxi/sun8i-adda-pr-regmap.h7
-rw-r--r--sound/soc/sunxi/sun8i-codec-analog.c853
-rw-r--r--sound/soc/sunxi/sun8i-codec.c1720
-rw-r--r--sound/soc/tegra/Kconfig299
-rw-r--r--sound/soc/tegra/Makefile51
-rw-r--r--sound/soc/tegra/tegra186_asrc.c1052
-rw-r--r--sound/soc/tegra/tegra186_asrc.h116
-rw-r--r--sound/soc/tegra/tegra186_dspk.c554
-rw-r--r--sound/soc/tegra/tegra186_dspk.h70
-rw-r--r--sound/soc/tegra/tegra20_ac97.c459
-rw-r--r--sound/soc/tegra/tegra20_ac97.h86
-rw-r--r--sound/soc/tegra/tegra20_das.c205
-rw-r--r--sound/soc/tegra/tegra20_i2s.c510
-rw-r--r--sound/soc/tegra/tegra20_i2s.h150
-rw-r--r--sound/soc/tegra/tegra20_spdif.c429
-rw-r--r--sound/soc/tegra/tegra20_spdif.h457
-rw-r--r--sound/soc/tegra/tegra210_admaif.c1045
-rw-r--r--sound/soc/tegra/tegra210_admaif.h241
-rw-r--r--sound/soc/tegra/tegra210_adx.c752
-rw-r--r--sound/soc/tegra/tegra210_adx.h96
-rw-r--r--sound/soc/tegra/tegra210_ahub.c2272
-rw-r--r--sound/soc/tegra/tegra210_ahub.h171
-rw-r--r--sound/soc/tegra/tegra210_amx.c801
-rw-r--r--sound/soc/tegra/tegra210_amx.h113
-rw-r--r--sound/soc/tegra/tegra210_dmic.c570
-rw-r--r--sound/soc/tegra/tegra210_dmic.h82
-rw-r--r--sound/soc/tegra/tegra210_i2s.c1172
-rw-r--r--sound/soc/tegra/tegra210_i2s.h178
-rw-r--r--sound/soc/tegra/tegra210_mbdrc.c1014
-rw-r--r--sound/soc/tegra/tegra210_mbdrc.h215
-rw-r--r--sound/soc/tegra/tegra210_mixer.c687
-rw-r--r--sound/soc/tegra/tegra210_mixer.h100
-rw-r--r--sound/soc/tegra/tegra210_mvc.c779
-rw-r--r--sound/soc/tegra/tegra210_mvc.h122
-rw-r--r--sound/soc/tegra/tegra210_ope.c420
-rw-r--r--sound/soc/tegra/tegra210_ope.h90
-rw-r--r--sound/soc/tegra/tegra210_peq.c433
-rw-r--r--sound/soc/tegra/tegra210_peq.h56
-rw-r--r--sound/soc/tegra/tegra210_sfc.c3644
-rw-r--r--sound/soc/tegra/tegra210_sfc.h78
-rw-r--r--sound/soc/tegra/tegra30_ahub.c687
-rw-r--r--sound/soc/tegra/tegra30_ahub.h525
-rw-r--r--sound/soc/tegra/tegra30_i2s.c570
-rw-r--r--sound/soc/tegra/tegra30_i2s.h240
-rw-r--r--sound/soc/tegra/tegra_asoc_machine.c1026
-rw-r--r--sound/soc/tegra/tegra_asoc_machine.h51
-rw-r--r--sound/soc/tegra/tegra_audio_graph_card.c267
-rw-r--r--sound/soc/tegra/tegra_cif.h87
-rw-r--r--sound/soc/tegra/tegra_isomgr_bw.c130
-rw-r--r--sound/soc/tegra/tegra_isomgr_bw.h31
-rw-r--r--sound/soc/tegra/tegra_pcm.c225
-rw-r--r--sound/soc/tegra/tegra_pcm.h41
-rw-r--r--sound/soc/tegra/tegra_wm8903.c189
-rw-r--r--sound/soc/ti/Kconfig195
-rw-r--r--sound/soc/ti/Makefile46
-rw-r--r--sound/soc/ti/ams-delta.c (renamed from sound/soc/omap/ams-delta.c)412
-rw-r--r--sound/soc/ti/davinci-evm.c262
-rw-r--r--sound/soc/ti/davinci-i2s.c (renamed from sound/soc/davinci/davinci-i2s.c)581
-rw-r--r--sound/soc/ti/davinci-i2s.h (renamed from sound/soc/davinci/davinci-i2s.h)5
-rw-r--r--sound/soc/ti/davinci-mcasp.c2552
-rw-r--r--sound/soc/ti/davinci-mcasp.h307
-rw-r--r--sound/soc/ti/edma-pcm.c63
-rw-r--r--sound/soc/ti/edma-pcm.h24
-rw-r--r--sound/soc/ti/j721e-evm.c938
-rw-r--r--sound/soc/ti/n810.c (renamed from sound/soc/omap/n810.c)224
-rw-r--r--sound/soc/ti/omap-abe-twl6040.c354
-rw-r--r--sound/soc/ti/omap-dmic.c527
-rw-r--r--sound/soc/ti/omap-dmic.h66
-rw-r--r--sound/soc/ti/omap-hdmi.c407
-rw-r--r--sound/soc/ti/omap-mcbsp-priv.h322
-rw-r--r--sound/soc/ti/omap-mcbsp-st.c502
-rw-r--r--sound/soc/ti/omap-mcbsp.c1441
-rw-r--r--sound/soc/ti/omap-mcbsp.h32
-rw-r--r--sound/soc/ti/omap-mcpdm.c605
-rw-r--r--sound/soc/ti/omap-mcpdm.h93
-rw-r--r--sound/soc/ti/omap-twl4030.c339
-rw-r--r--sound/soc/ti/omap3pandora.c (renamed from sound/soc/omap/omap3pandora.c)219
-rw-r--r--sound/soc/ti/osk5912.c (renamed from sound/soc/omap/osk5912.c)105
-rw-r--r--sound/soc/ti/rx51.c473
-rw-r--r--sound/soc/ti/sdma-pcm.c71
-rw-r--r--sound/soc/ti/sdma-pcm.h21
-rw-r--r--sound/soc/ti/udma-pcm.c43
-rw-r--r--sound/soc/ti/udma-pcm.h18
-rw-r--r--sound/soc/txx9/Kconfig29
-rw-r--r--sound/soc/txx9/Makefile11
-rw-r--r--sound/soc/txx9/txx9aclc-ac97.c242
-rw-r--r--sound/soc/txx9/txx9aclc-generic.c89
-rw-r--r--sound/soc/txx9/txx9aclc.c453
-rw-r--r--sound/soc/txx9/txx9aclc.h74
-rw-r--r--sound/soc/uniphier/Kconfig42
-rw-r--r--sound/soc/uniphier/Makefile11
-rw-r--r--sound/soc/uniphier/aio-compress.c433
-rw-r--r--sound/soc/uniphier/aio-core.c1266
-rw-r--r--sound/soc/uniphier/aio-cpu.c832
-rw-r--r--sound/soc/uniphier/aio-dma.c279
-rw-r--r--sound/soc/uniphier/aio-ld11.c356
-rw-r--r--sound/soc/uniphier/aio-pxs2.c265
-rw-r--r--sound/soc/uniphier/aio-reg.h476
-rw-r--r--sound/soc/uniphier/aio.h354
-rw-r--r--sound/soc/uniphier/evea.c569
-rw-r--r--sound/soc/ux500/Kconfig33
-rw-r--r--sound/soc/ux500/Makefile11
-rw-r--r--sound/soc/ux500/mop500.c167
-rw-r--r--sound/soc/ux500/mop500_ab8500.c439
-rw-r--r--sound/soc/ux500/mop500_ab8500.h17
-rw-r--r--sound/soc/ux500/ux500_msp_dai.c824
-rw-r--r--sound/soc/ux500/ux500_msp_dai.h66
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.c667
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.h487
-rw-r--r--sound/soc/ux500/ux500_pcm.c92
-rw-r--r--sound/soc/ux500/ux500_pcm.h19
-rw-r--r--sound/soc/xilinx/Kconfig27
-rw-r--r--sound/soc/xilinx/Makefile7
-rw-r--r--sound/soc/xilinx/xlnx_formatter_pcm.c727
-rw-r--r--sound/soc/xilinx/xlnx_i2s.c259
-rw-r--r--sound/soc/xilinx/xlnx_spdif.c318
-rw-r--r--sound/soc/xtensa/Kconfig12
-rw-r--r--sound/soc/xtensa/Makefile4
-rw-r--r--sound/soc/xtensa/xtfpga-i2s.c650
-rw-r--r--sound/sound_core.c151
-rw-r--r--sound/sound_firmware.c78
-rw-r--r--sound/sparc/Kconfig2
-rw-r--r--sound/sparc/Makefile7
-rw-r--r--sound/sparc/amd7930.c219
-rw-r--r--sound/sparc/cs4231.c464
-rw-r--r--sound/sparc/dbri.c394
-rw-r--r--sound/spi/Kconfig3
-rw-r--r--sound/spi/Makefile3
-rw-r--r--sound/spi/at73c213.c226
-rw-r--r--sound/spi/at73c213.h19
-rw-r--r--sound/synth/Kconfig3
-rw-r--r--sound/synth/Makefile3
-rw-r--r--sound/synth/emux/Makefile14
-rw-r--r--sound/synth/emux/emux.c75
-rw-r--r--sound/synth/emux/emux_effect.c60
-rw-r--r--sound/synth/emux/emux_hwdep.c41
-rw-r--r--sound/synth/emux/emux_nrpn.c34
-rw-r--r--sound/synth/emux/emux_oss.c58
-rw-r--r--sound/synth/emux/emux_proc.c30
-rw-r--r--sound/synth/emux/emux_seq.c100
-rw-r--r--sound/synth/emux/emux_synth.c106
-rw-r--r--sound/synth/emux/emux_voice.h23
-rw-r--r--sound/synth/emux/soundfont.c324
-rw-r--r--sound/synth/util_mem.c49
-rw-r--r--sound/usb/6fire/Makefile4
-rw-r--r--sound/usb/6fire/chip.c205
-rw-r--r--sound/usb/6fire/chip.h27
-rw-r--r--sound/usb/6fire/comm.c200
-rw-r--r--sound/usb/6fire/comm.h39
-rw-r--r--sound/usb/6fire/common.h25
-rw-r--r--sound/usb/6fire/control.c617
-rw-r--r--sound/usb/6fire/control.h53
-rw-r--r--sound/usb/6fire/firmware.c405
-rw-r--r--sound/usb/6fire/firmware.h23
-rw-r--r--sound/usb/6fire/midi.c203
-rw-r--r--sound/usb/6fire/midi.h37
-rw-r--r--sound/usb/6fire/pcm.c670
-rw-r--r--sound/usb/6fire/pcm.h71
-rw-r--r--sound/usb/Kconfig110
-rw-r--r--sound/usb/Makefile31
-rw-r--r--sound/usb/bcd2000/Makefile4
-rw-r--r--sound/usb/bcd2000/bcd2000.c452
-rw-r--r--sound/usb/caiaq/Makefile1
-rw-r--r--sound/usb/caiaq/audio.c554
-rw-r--r--sound/usb/caiaq/audio.h6
-rw-r--r--sound/usb/caiaq/control.c202
-rw-r--r--sound/usb/caiaq/control.h3
-rw-r--r--sound/usb/caiaq/device.c375
-rw-r--r--sound/usb/caiaq/device.h29
-rw-r--r--sound/usb/caiaq/input.c496
-rw-r--r--sound/usb/caiaq/input.h8
-rw-r--r--sound/usb/caiaq/midi.c84
-rw-r--r--sound/usb/caiaq/midi.h6
-rw-r--r--sound/usb/card.c1190
-rw-r--r--sound/usb/card.h199
-rw-r--r--sound/usb/clock.c671
-rw-r--r--sound/usb/clock.h13
-rw-r--r--sound/usb/debug.h15
-rw-r--r--sound/usb/endpoint.c2101
-rw-r--r--sound/usb/endpoint.h56
-rw-r--r--sound/usb/fcp.c1127
-rw-r--r--sound/usb/fcp.h7
-rw-r--r--sound/usb/format.c611
-rw-r--r--sound/usb/format.h9
-rw-r--r--sound/usb/helper.c79
-rw-r--r--sound/usb/helper.h24
-rw-r--r--sound/usb/hiface/Makefile3
-rw-r--r--sound/usb/hiface/chip.c271
-rw-r--r--sound/usb/hiface/chip.h26
-rw-r--r--sound/usb/hiface/pcm.c594
-rw-r--r--sound/usb/hiface/pcm.h20
-rw-r--r--sound/usb/implicit.c494
-rw-r--r--sound/usb/implicit.h14
-rw-r--r--sound/usb/line6/Kconfig44
-rw-r--r--sound/usb/line6/Makefile19
-rw-r--r--sound/usb/line6/capture.c290
-rw-r--r--sound/usb/line6/capture.h25
-rw-r--r--sound/usb/line6/driver.c900
-rw-r--r--sound/usb/line6/driver.h214
-rw-r--r--sound/usb/line6/midi.c289
-rw-r--r--sound/usb/line6/midi.h47
-rw-r--r--sound/usb/line6/midibuf.c257
-rw-r--r--sound/usb/line6/midibuf.h34
-rw-r--r--sound/usb/line6/pcm.c604
-rw-r--r--sound/usb/line6/pcm.h195
-rw-r--r--sound/usb/line6/playback.c439
-rw-r--r--sound/usb/line6/playback.h31
-rw-r--r--sound/usb/line6/pod.c542
-rw-r--r--sound/usb/line6/podhd.c573
-rw-r--r--sound/usb/line6/toneport.c582
-rw-r--r--sound/usb/line6/variax.c242
-rw-r--r--sound/usb/media.c325
-rw-r--r--sound/usb/media.h74
-rw-r--r--sound/usb/midi.c1124
-rw-r--r--sound/usb/midi.h24
-rw-r--r--sound/usb/midi2.c1233
-rw-r--r--sound/usb/midi2.h33
-rw-r--r--sound/usb/misc/Makefile3
-rw-r--r--sound/usb/misc/ua101.c379
-rw-r--r--sound/usb/mixer.c2918
-rw-r--r--sound/usb/mixer.h99
-rw-r--r--sound/usb/mixer_maps.c490
-rw-r--r--sound/usb/mixer_quirks.c4467
-rw-r--r--sound/usb/mixer_quirks.h7
-rw-r--r--sound/usb/mixer_s1810c.c653
-rw-r--r--sound/usb/mixer_s1810c.h7
-rw-r--r--sound/usb/mixer_scarlett.c1016
-rw-r--r--sound/usb/mixer_scarlett.h7
-rw-r--r--sound/usb/mixer_scarlett2.c9434
-rw-r--r--sound/usb/mixer_scarlett2.h7
-rw-r--r--sound/usb/mixer_us16x08.c1413
-rw-r--r--sound/usb/mixer_us16x08.h122
-rw-r--r--sound/usb/pcm.c1823
-rw-r--r--sound/usb/pcm.h24
-rw-r--r--sound/usb/power.c107
-rw-r--r--sound/usb/power.h28
-rw-r--r--sound/usb/proc.c175
-rw-r--r--sound/usb/proc.h1
-rw-r--r--sound/usb/qcom/Makefile4
-rw-r--r--sound/usb/qcom/mixer_usb_offload.c155
-rw-r--r--sound/usb/qcom/mixer_usb_offload.h11
-rw-r--r--sound/usb/qcom/qc_audio_offload.c1993
-rw-r--r--sound/usb/qcom/usb_audio_qmi_v01.c863
-rw-r--r--sound/usb/qcom/usb_audio_qmi_v01.h164
-rw-r--r--sound/usb/quirks-table.h3925
-rw-r--r--sound/usb/quirks.c2303
-rw-r--r--sound/usb/quirks.h45
-rw-r--r--sound/usb/stream.c1292
-rw-r--r--sound/usb/stream.h13
-rw-r--r--sound/usb/urb.c941
-rw-r--r--sound/usb/urb.h21
-rw-r--r--sound/usb/usbaudio.h232
-rw-r--r--sound/usb/usx2y/Makefile7
-rw-r--r--sound/usb/usx2y/us122l.c377
-rw-r--r--sound/usb/usx2y/us122l.h5
-rw-r--r--sound/usb/usx2y/us144mkii.c620
-rw-r--r--sound/usb/usx2y/us144mkii.h367
-rw-r--r--sound/usb/usx2y/us144mkii_capture.c319
-rw-r--r--sound/usb/usx2y/us144mkii_controls.c444
-rw-r--r--sound/usb/usx2y/us144mkii_midi.c403
-rw-r--r--sound/usb/usx2y/us144mkii_pcm.c370
-rw-r--r--sound/usb/usx2y/us144mkii_pcm.h165
-rw-r--r--sound/usb/usx2y/us144mkii_playback.c456
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.c209
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.h3
-rw-r--r--sound/usb/usx2y/usb_stream.c245
-rw-r--r--sound/usb/usx2y/usb_stream.h105
-rw-r--r--sound/usb/usx2y/usbus428ctldefs.h119
-rw-r--r--sound/usb/usx2y/usbusx2y.c433
-rw-r--r--sound/usb/usx2y/usbusx2y.h100
-rw-r--r--sound/usb/usx2y/usbusx2yaudio.c857
-rw-r--r--sound/usb/usx2y/usx2y.h15
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c770
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.h5
-rw-r--r--sound/usb/validate.c366
-rw-r--r--sound/virtio/Kconfig10
-rw-r--r--sound/virtio/Makefile14
-rw-r--r--sound/virtio/virtio_card.c450
-rw-r--r--sound/virtio/virtio_card.h133
-rw-r--r--sound/virtio/virtio_chmap.c219
-rw-r--r--sound/virtio/virtio_ctl_msg.c303
-rw-r--r--sound/virtio/virtio_ctl_msg.h78
-rw-r--r--sound/virtio/virtio_jack.c233
-rw-r--r--sound/virtio/virtio_kctl.c477
-rw-r--r--sound/virtio/virtio_pcm.c524
-rw-r--r--sound/virtio/virtio_pcm.h127
-rw-r--r--sound/virtio/virtio_pcm_msg.c415
-rw-r--r--sound/virtio/virtio_pcm_ops.c525
-rw-r--r--sound/x86/Kconfig18
-rw-r--r--sound/x86/Makefile5
-rw-r--r--sound/x86/intel_hdmi_audio.c1832
-rw-r--r--sound/x86/intel_hdmi_audio.h145
-rw-r--r--sound/x86/intel_hdmi_lpe_audio.h320
-rw-r--r--sound/xen/Kconfig12
-rw-r--r--sound/xen/Makefile8
-rw-r--r--sound/xen/xen_snd_front.c387
-rw-r--r--sound/xen/xen_snd_front.h54
-rw-r--r--sound/xen/xen_snd_front_alsa.c833
-rw-r--r--sound/xen/xen_snd_front_alsa.h23
-rw-r--r--sound/xen/xen_snd_front_cfg.c519
-rw-r--r--sound/xen/xen_snd_front_cfg.h46
-rw-r--r--sound/xen/xen_snd_front_evtchnl.c463
-rw-r--r--sound/xen/xen_snd_front_evtchnl.h86
3207 files changed, 1251919 insertions, 255961 deletions
diff --git a/sound/Kconfig b/sound/Kconfig
index fcad760f5691..8b40205394fe 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -1,27 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
menuconfig SOUND
tristate "Sound card support"
- depends on HAS_IOMEM
+ depends on HAS_IOMEM || INDIRECT_IOMEM
help
If you have a sound card in your computer, i.e. if it can say more
- than an occasional beep, say Y. Be sure to have all the information
- about your sound card and its configuration down (I/O port,
- interrupt and DMA channel), because you will be asked for it.
-
- You want to read the Sound-HOWTO, available from
- <http://www.tldp.org/docs.html#howto>. General information about
- the modular sound system is contained in the files
- <file:Documentation/sound/oss/Introduction>. The file
- <file:Documentation/sound/oss/README.OSS> contains some slightly
- outdated but still useful information as well. Newer sound
- driver documentation is found in <file:Documentation/sound/alsa/*>.
-
- If you have a PnP sound card and you want to configure it at boot
- time using the ISA PnP tools (read
- <http://www.roestock.demon.co.uk/isapnptools/>), then you need to
- compile the sound card support as a module and load that module
- after the PnP configuration is finished. To do this, choose M here
- and read <file:Documentation/sound/oss/README.modules>; the module
- will be called soundcore.
+ than an occasional beep, say Y.
if SOUND
@@ -52,15 +35,10 @@ config SOUND_OSS_CORE_PRECLAIM
Disabling this allows alternative OSS implementations.
- Please read Documentation/feature-removal-schedule.txt for
- details.
-
If unsure, say Y.
source "sound/oss/dmasound/Kconfig"
-if !M68K
-
menuconfig SND
tristate "Advanced Linux Sound Architecture"
help
@@ -79,8 +57,12 @@ source "sound/isa/Kconfig"
source "sound/pci/Kconfig"
+source "sound/hda/Kconfig"
+
source "sound/ppc/Kconfig"
+source "sound/ac97/Kconfig"
+
source "sound/aoa/Kconfig"
source "sound/arm/Kconfig"
@@ -97,6 +79,8 @@ source "sound/sh/Kconfig"
# here assuming USB is defined before ALSA
source "sound/usb/Kconfig"
+source "sound/firewire/Kconfig"
+
# the following will depend on the order of config.
# here assuming PCMCIA is defined before ALSA
source "sound/pcmcia/Kconfig"
@@ -107,25 +91,18 @@ source "sound/parisc/Kconfig"
source "sound/soc/Kconfig"
-endif # SND
-
-menuconfig SOUND_PRIME
- tristate "Open Sound System (DEPRECATED)"
- select SOUND_OSS_CORE
- help
- Say 'Y' or 'M' to enable Open Sound System drivers.
+source "sound/x86/Kconfig"
-if SOUND_PRIME
+source "sound/synth/Kconfig"
-source "sound/oss/Kconfig"
+source "sound/xen/Kconfig"
-endif # SOUND_PRIME
+source "sound/virtio/Kconfig"
-endif # !M68K
+endif # SND
endif # SOUND
-# AC97_BUS is used from both sound and ucb1400
config AC97_BUS
tristate
help
diff --git a/sound/Makefile b/sound/Makefile
index ec467decfa79..5942311a4232 100644
--- a/sound/Makefile
+++ b/sound/Makefile
@@ -1,19 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0
# Makefile for the Linux sound card driver
#
obj-$(CONFIG_SOUND) += soundcore.o
-obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o
-obj-$(CONFIG_SOUND_PRIME) += oss/
-obj-$(CONFIG_DMASOUND) += oss/
+obj-$(CONFIG_DMASOUND) += oss/dmasound/
obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \
- sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/
+ firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/ \
+ virtio/
obj-$(CONFIG_SND_AOA) += aoa/
# This one must be compilable even if sound is configured out
obj-$(CONFIG_AC97_BUS) += ac97_bus.o
+obj-$(CONFIG_AC97_BUS_NEW) += ac97/
ifeq ($(CONFIG_SND),y)
obj-y += last.o
endif
-soundcore-objs := sound_core.o
+soundcore-y := sound_core.o
diff --git a/sound/ac97/Kconfig b/sound/ac97/Kconfig
new file mode 100644
index 000000000000..f0e31f2bb04d
--- /dev/null
+++ b/sound/ac97/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# AC97 configuration
+#
+
+
+config AC97_BUS_NEW
+ tristate
+ help
+ This is the new AC97 bus type, successor of AC97_BUS. The ported
+ drivers which benefit from the AC97 automatic probing should "select"
+ this instead of the AC97_BUS.
+ Say Y here if you want to have AC97 devices, which are sound oriented
+ devices around an AC-Link.
+
+config AC97_BUS_COMPAT
+ bool
+ depends on AC97_BUS_NEW
+ depends on !AC97_BUS
diff --git a/sound/ac97/Makefile b/sound/ac97/Makefile
new file mode 100644
index 000000000000..f5efa1ad7e7f
--- /dev/null
+++ b/sound/ac97/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# make for AC97 bus drivers
+#
+
+obj-$(CONFIG_AC97_BUS_NEW) += ac97.o
+
+ac97-y += bus.o codec.o
+ac97-$(CONFIG_AC97_BUS_COMPAT) += snd_ac97_compat.o
diff --git a/sound/ac97/ac97_core.h b/sound/ac97/ac97_core.h
new file mode 100644
index 000000000000..5a9677c3d4c3
--- /dev/null
+++ b/sound/ac97/ac97_core.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
+ */
+
+unsigned int snd_ac97_bus_scan_one(struct ac97_controller *adrv,
+ unsigned int codec_num);
+
+static inline bool ac97_ids_match(unsigned int id1, unsigned int id2,
+ unsigned int mask)
+{
+ return (id1 & mask) == (id2 & mask);
+}
diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c
new file mode 100644
index 000000000000..f4254703d29f
--- /dev/null
+++ b/sound/ac97/bus.c
@@ -0,0 +1,548 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
+ */
+
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/idr.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <sound/ac97_codec.h>
+#include <sound/ac97/codec.h>
+#include <sound/ac97/controller.h>
+#include <sound/ac97/regs.h>
+
+#include "ac97_core.h"
+
+/*
+ * Protects ac97_controllers and each ac97_controller structure.
+ */
+static DEFINE_MUTEX(ac97_controllers_mutex);
+static DEFINE_IDR(ac97_adapter_idr);
+static LIST_HEAD(ac97_controllers);
+
+static inline struct ac97_controller*
+to_ac97_controller(struct device *ac97_adapter)
+{
+ return container_of(ac97_adapter, struct ac97_controller, adap);
+}
+
+static int ac97_unbound_ctrl_write(struct ac97_controller *adrv, int slot,
+ unsigned short reg, unsigned short val)
+{
+ return -ENODEV;
+}
+
+static int ac97_unbound_ctrl_read(struct ac97_controller *adrv, int slot,
+ unsigned short reg)
+{
+ return -ENODEV;
+}
+
+static const struct ac97_controller_ops ac97_unbound_ctrl_ops = {
+ .write = ac97_unbound_ctrl_write,
+ .read = ac97_unbound_ctrl_read,
+};
+
+static struct ac97_controller ac97_unbound_ctrl = {
+ .ops = &ac97_unbound_ctrl_ops,
+};
+
+static struct ac97_codec_device *
+ac97_codec_find(struct ac97_controller *ac97_ctrl, unsigned int codec_num)
+{
+ if (codec_num >= AC97_BUS_MAX_CODECS)
+ return ERR_PTR(-EINVAL);
+
+ return ac97_ctrl->codecs[codec_num];
+}
+
+static struct device_node *
+ac97_of_get_child_device(struct ac97_controller *ac97_ctrl, int idx,
+ unsigned int vendor_id)
+{
+ struct device_node *node;
+ u32 reg;
+ char compat[] = "ac97,0000,0000";
+
+ snprintf(compat, sizeof(compat), "ac97,%04x,%04x",
+ vendor_id >> 16, vendor_id & 0xffff);
+
+ for_each_child_of_node(ac97_ctrl->parent->of_node, node) {
+ if ((idx != of_property_read_u32(node, "reg", &reg)) ||
+ !of_device_is_compatible(node, compat))
+ continue;
+ return node;
+ }
+
+ return NULL;
+}
+
+static void ac97_codec_release(struct device *dev)
+{
+ struct ac97_codec_device *adev;
+ struct ac97_controller *ac97_ctrl;
+
+ adev = to_ac97_device(dev);
+ ac97_ctrl = adev->ac97_ctrl;
+ ac97_ctrl->codecs[adev->num] = NULL;
+ of_node_put(dev->of_node);
+ kfree(adev);
+}
+
+static int ac97_codec_add(struct ac97_controller *ac97_ctrl, int idx,
+ unsigned int vendor_id)
+{
+ struct ac97_codec_device *codec;
+ int ret;
+
+ codec = kzalloc(sizeof(*codec), GFP_KERNEL);
+ if (!codec)
+ return -ENOMEM;
+ ac97_ctrl->codecs[idx] = codec;
+ codec->vendor_id = vendor_id;
+ codec->dev.release = ac97_codec_release;
+ codec->dev.bus = &ac97_bus_type;
+ codec->dev.parent = &ac97_ctrl->adap;
+ codec->num = idx;
+ codec->ac97_ctrl = ac97_ctrl;
+
+ device_initialize(&codec->dev);
+ dev_set_name(&codec->dev, "%s:%u", dev_name(ac97_ctrl->parent), idx);
+ codec->dev.of_node = ac97_of_get_child_device(ac97_ctrl, idx,
+ vendor_id);
+
+ ret = device_add(&codec->dev);
+ if (ret) {
+ put_device(&codec->dev);
+ return ret;
+ }
+
+ return 0;
+}
+
+unsigned int snd_ac97_bus_scan_one(struct ac97_controller *adrv,
+ unsigned int codec_num)
+{
+ unsigned short vid1, vid2;
+ int ret;
+
+ ret = adrv->ops->read(adrv, codec_num, AC97_VENDOR_ID1);
+ vid1 = (ret & 0xffff);
+ if (ret < 0)
+ return 0;
+
+ ret = adrv->ops->read(adrv, codec_num, AC97_VENDOR_ID2);
+ vid2 = (ret & 0xffff);
+ if (ret < 0)
+ return 0;
+
+ dev_dbg(&adrv->adap, "%s(codec_num=%u): vendor_id=0x%08x\n",
+ __func__, codec_num, AC97_ID(vid1, vid2));
+ return AC97_ID(vid1, vid2);
+}
+
+static int ac97_bus_scan(struct ac97_controller *ac97_ctrl)
+{
+ int ret, i;
+ unsigned int vendor_id;
+
+ for (i = 0; i < AC97_BUS_MAX_CODECS; i++) {
+ if (ac97_codec_find(ac97_ctrl, i))
+ continue;
+ if (!(ac97_ctrl->slots_available & BIT(i)))
+ continue;
+ vendor_id = snd_ac97_bus_scan_one(ac97_ctrl, i);
+ if (!vendor_id)
+ continue;
+
+ ret = ac97_codec_add(ac97_ctrl, i, vendor_id);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int ac97_bus_reset(struct ac97_controller *ac97_ctrl)
+{
+ ac97_ctrl->ops->reset(ac97_ctrl);
+
+ return 0;
+}
+
+/**
+ * snd_ac97_codec_driver_register - register an AC97 codec driver
+ * @drv: AC97 driver codec to register
+ *
+ * Register an AC97 codec driver to the ac97 bus driver, aka. the AC97 digital
+ * controller.
+ *
+ * Returns 0 on success or error code
+ */
+int snd_ac97_codec_driver_register(struct ac97_codec_driver *drv)
+{
+ drv->driver.bus = &ac97_bus_type;
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(snd_ac97_codec_driver_register);
+
+/**
+ * snd_ac97_codec_driver_unregister - unregister an AC97 codec driver
+ * @drv: AC97 codec driver to unregister
+ *
+ * Unregister a previously registered ac97 codec driver.
+ */
+void snd_ac97_codec_driver_unregister(struct ac97_codec_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(snd_ac97_codec_driver_unregister);
+
+/**
+ * snd_ac97_codec_get_platdata - get platform_data
+ * @adev: the ac97 codec device
+ *
+ * For legacy platforms, in order to have platform_data in codec drivers
+ * available, while ac97 device are auto-created upon probe, this retrieves the
+ * platdata which was setup on ac97 controller registration.
+ *
+ * Returns the platform data pointer
+ */
+void *snd_ac97_codec_get_platdata(const struct ac97_codec_device *adev)
+{
+ struct ac97_controller *ac97_ctrl = adev->ac97_ctrl;
+
+ return ac97_ctrl->codecs_pdata[adev->num];
+}
+EXPORT_SYMBOL_GPL(snd_ac97_codec_get_platdata);
+
+static void ac97_ctrl_codecs_unregister(struct ac97_controller *ac97_ctrl)
+{
+ int i;
+
+ for (i = 0; i < AC97_BUS_MAX_CODECS; i++)
+ if (ac97_ctrl->codecs[i]) {
+ ac97_ctrl->codecs[i]->ac97_ctrl = &ac97_unbound_ctrl;
+ device_unregister(&ac97_ctrl->codecs[i]->dev);
+ }
+}
+
+static ssize_t cold_reset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t len)
+{
+ struct ac97_controller *ac97_ctrl;
+
+ guard(mutex)(&ac97_controllers_mutex);
+ ac97_ctrl = to_ac97_controller(dev);
+ ac97_ctrl->ops->reset(ac97_ctrl);
+ return len;
+}
+static DEVICE_ATTR_WO(cold_reset);
+
+static ssize_t warm_reset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t len)
+{
+ struct ac97_controller *ac97_ctrl;
+
+ if (!dev)
+ return -ENODEV;
+
+ guard(mutex)(&ac97_controllers_mutex);
+ ac97_ctrl = to_ac97_controller(dev);
+ ac97_ctrl->ops->warm_reset(ac97_ctrl);
+ return len;
+}
+static DEVICE_ATTR_WO(warm_reset);
+
+static struct attribute *ac97_controller_device_attrs[] = {
+ &dev_attr_cold_reset.attr,
+ &dev_attr_warm_reset.attr,
+ NULL
+};
+
+static const struct attribute_group ac97_adapter_attr_group = {
+ .name = "ac97_operations",
+ .attrs = ac97_controller_device_attrs,
+};
+
+static const struct attribute_group *ac97_adapter_groups[] = {
+ &ac97_adapter_attr_group,
+ NULL,
+};
+
+static void ac97_del_adapter(struct ac97_controller *ac97_ctrl)
+{
+ scoped_guard(mutex, &ac97_controllers_mutex) {
+ ac97_ctrl_codecs_unregister(ac97_ctrl);
+ list_del(&ac97_ctrl->controllers);
+ }
+
+ device_unregister(&ac97_ctrl->adap);
+}
+
+static void ac97_adapter_release(struct device *dev)
+{
+ struct ac97_controller *ac97_ctrl;
+
+ ac97_ctrl = to_ac97_controller(dev);
+ idr_remove(&ac97_adapter_idr, ac97_ctrl->nr);
+ dev_dbg(&ac97_ctrl->adap, "adapter unregistered by %s\n",
+ dev_name(ac97_ctrl->parent));
+}
+
+static const struct device_type ac97_adapter_type = {
+ .groups = ac97_adapter_groups,
+ .release = ac97_adapter_release,
+};
+
+static int ac97_add_adapter(struct ac97_controller *ac97_ctrl)
+{
+ int ret;
+
+ guard(mutex)(&ac97_controllers_mutex);
+ ret = idr_alloc(&ac97_adapter_idr, ac97_ctrl, 0, 0, GFP_KERNEL);
+ ac97_ctrl->nr = ret;
+ if (ret >= 0) {
+ dev_set_name(&ac97_ctrl->adap, "ac97-%d", ret);
+ ac97_ctrl->adap.type = &ac97_adapter_type;
+ ac97_ctrl->adap.parent = ac97_ctrl->parent;
+ ret = device_register(&ac97_ctrl->adap);
+ if (ret)
+ put_device(&ac97_ctrl->adap);
+ }
+ if (!ret) {
+ list_add(&ac97_ctrl->controllers, &ac97_controllers);
+ dev_dbg(&ac97_ctrl->adap, "adapter registered by %s\n",
+ dev_name(ac97_ctrl->parent));
+ }
+ return ret;
+}
+
+/**
+ * snd_ac97_controller_register - register an ac97 controller
+ * @ops: the ac97 bus operations
+ * @dev: the device providing the ac97 DC function
+ * @slots_available: mask of the ac97 codecs that can be scanned and probed
+ * bit0 => codec 0, bit1 => codec 1 ... bit 3 => codec 3
+ * @codecs_pdata: codec platform data
+ *
+ * Register a digital controller which can control up to 4 ac97 codecs. This is
+ * the controller side of the AC97 AC-link, while the slave side are the codecs.
+ *
+ * Returns a valid controller upon success, negative pointer value upon error
+ */
+struct ac97_controller *snd_ac97_controller_register(
+ const struct ac97_controller_ops *ops, struct device *dev,
+ unsigned short slots_available, void **codecs_pdata)
+{
+ struct ac97_controller *ac97_ctrl;
+ int ret, i;
+
+ ac97_ctrl = kzalloc(sizeof(*ac97_ctrl), GFP_KERNEL);
+ if (!ac97_ctrl)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < AC97_BUS_MAX_CODECS && codecs_pdata; i++)
+ ac97_ctrl->codecs_pdata[i] = codecs_pdata[i];
+
+ ac97_ctrl->ops = ops;
+ ac97_ctrl->slots_available = slots_available;
+ ac97_ctrl->parent = dev;
+ ret = ac97_add_adapter(ac97_ctrl);
+
+ if (ret)
+ goto err;
+ ac97_bus_reset(ac97_ctrl);
+ ac97_bus_scan(ac97_ctrl);
+
+ return ac97_ctrl;
+err:
+ kfree(ac97_ctrl);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(snd_ac97_controller_register);
+
+/**
+ * snd_ac97_controller_unregister - unregister an ac97 controller
+ * @ac97_ctrl: the device previously provided to ac97_controller_register()
+ *
+ */
+void snd_ac97_controller_unregister(struct ac97_controller *ac97_ctrl)
+{
+ ac97_del_adapter(ac97_ctrl);
+}
+EXPORT_SYMBOL_GPL(snd_ac97_controller_unregister);
+
+static int ac97_pm_runtime_suspend(struct device *dev)
+{
+ struct ac97_codec_device *codec = to_ac97_device(dev);
+ int ret = pm_generic_runtime_suspend(dev);
+
+ if (ret == 0 && dev->driver) {
+ if (pm_runtime_is_irq_safe(dev))
+ clk_disable(codec->clk);
+ else
+ clk_disable_unprepare(codec->clk);
+ }
+
+ return ret;
+}
+
+static int ac97_pm_runtime_resume(struct device *dev)
+{
+ struct ac97_codec_device *codec = to_ac97_device(dev);
+ int ret;
+
+ if (dev->driver) {
+ if (pm_runtime_is_irq_safe(dev))
+ ret = clk_enable(codec->clk);
+ else
+ ret = clk_prepare_enable(codec->clk);
+ if (ret)
+ return ret;
+ }
+
+ return pm_generic_runtime_resume(dev);
+}
+
+static const struct dev_pm_ops ac97_pm = {
+ .suspend = pm_generic_suspend,
+ .resume = pm_generic_resume,
+ .freeze = pm_generic_freeze,
+ .thaw = pm_generic_thaw,
+ .poweroff = pm_generic_poweroff,
+ .restore = pm_generic_restore,
+ RUNTIME_PM_OPS(ac97_pm_runtime_suspend, ac97_pm_runtime_resume, NULL)
+};
+
+static int ac97_get_enable_clk(struct ac97_codec_device *adev)
+{
+ int ret;
+
+ adev->clk = clk_get(&adev->dev, "ac97_clk");
+ if (IS_ERR(adev->clk))
+ return PTR_ERR(adev->clk);
+
+ ret = clk_prepare_enable(adev->clk);
+ if (ret)
+ clk_put(adev->clk);
+
+ return ret;
+}
+
+static void ac97_put_disable_clk(struct ac97_codec_device *adev)
+{
+ clk_disable_unprepare(adev->clk);
+ clk_put(adev->clk);
+}
+
+static ssize_t vendor_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ac97_codec_device *codec = to_ac97_device(dev);
+
+ return sysfs_emit(buf, "%08x", codec->vendor_id);
+}
+static DEVICE_ATTR_RO(vendor_id);
+
+static struct attribute *ac97_dev_attrs[] = {
+ &dev_attr_vendor_id.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(ac97_dev);
+
+static int ac97_bus_match(struct device *dev, const struct device_driver *drv)
+{
+ struct ac97_codec_device *adev = to_ac97_device(dev);
+ const struct ac97_codec_driver *adrv = to_ac97_driver(drv);
+ const struct ac97_id *id = adrv->id_table;
+ int i = 0;
+
+ if (adev->vendor_id == 0x0 || adev->vendor_id == 0xffffffff)
+ return false;
+
+ do {
+ if (ac97_ids_match(id[i].id, adev->vendor_id, id[i].mask))
+ return true;
+ } while (id[i++].id);
+
+ return false;
+}
+
+static int ac97_bus_probe(struct device *dev)
+{
+ struct ac97_codec_device *adev = to_ac97_device(dev);
+ struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver);
+ int ret;
+
+ ret = ac97_get_enable_clk(adev);
+ if (ret)
+ return ret;
+
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = adrv->probe(adev);
+ if (ret == 0)
+ return 0;
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
+ ac97_put_disable_clk(adev);
+
+ return ret;
+}
+
+static void ac97_bus_remove(struct device *dev)
+{
+ struct ac97_codec_device *adev = to_ac97_device(dev);
+ struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return;
+
+ adrv->remove(adev);
+ pm_runtime_put_noidle(dev);
+ ac97_put_disable_clk(adev);
+
+ pm_runtime_disable(dev);
+}
+
+const struct bus_type ac97_bus_type = {
+ .name = "ac97bus",
+ .dev_groups = ac97_dev_groups,
+ .match = ac97_bus_match,
+ .pm = pm_ptr(&ac97_pm),
+ .probe = ac97_bus_probe,
+ .remove = ac97_bus_remove,
+};
+
+static int __init ac97_bus_init(void)
+{
+ return bus_register(&ac97_bus_type);
+}
+subsys_initcall(ac97_bus_init);
+
+static void __exit ac97_bus_exit(void)
+{
+ bus_unregister(&ac97_bus_type);
+}
+module_exit(ac97_bus_exit);
+
+MODULE_DESCRIPTION("AC97 bus interface");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
diff --git a/sound/ac97/codec.c b/sound/ac97/codec.c
new file mode 100644
index 000000000000..1c8357ad6cb4
--- /dev/null
+++ b/sound/ac97/codec.c
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
+ */
+
+#include <sound/ac97_codec.h>
+#include <sound/ac97/codec.h>
+#include <sound/ac97/controller.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <sound/soc.h> /* For compat_ac97_* */
+
diff --git a/sound/ac97/snd_ac97_compat.c b/sound/ac97/snd_ac97_compat.c
new file mode 100644
index 000000000000..d2479bba75bf
--- /dev/null
+++ b/sound/ac97/snd_ac97_compat.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr>
+ */
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <sound/ac97/codec.h>
+#include <sound/ac97/compat.h>
+#include <sound/ac97/controller.h>
+#include <sound/soc.h>
+
+#include "ac97_core.h"
+
+static void compat_ac97_release(struct device *dev)
+{
+ kfree(to_ac97_t(dev));
+}
+
+static void compat_ac97_reset(struct snd_ac97 *ac97)
+{
+ struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+ struct ac97_controller *actrl = adev->ac97_ctrl;
+
+ if (actrl->ops->reset)
+ actrl->ops->reset(actrl);
+}
+
+static void compat_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+ struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+ struct ac97_controller *actrl = adev->ac97_ctrl;
+
+ if (actrl->ops->warm_reset)
+ actrl->ops->warm_reset(actrl);
+}
+
+static void compat_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+ unsigned short val)
+{
+ struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+ struct ac97_controller *actrl = adev->ac97_ctrl;
+
+ actrl->ops->write(actrl, ac97->num, reg, val);
+}
+
+static unsigned short compat_ac97_read(struct snd_ac97 *ac97,
+ unsigned short reg)
+{
+ struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+ struct ac97_controller *actrl = adev->ac97_ctrl;
+
+ return actrl->ops->read(actrl, ac97->num, reg);
+}
+
+static const struct snd_ac97_bus_ops compat_snd_ac97_bus_ops = {
+ .reset = compat_ac97_reset,
+ .warm_reset = compat_ac97_warm_reset,
+ .write = compat_ac97_write,
+ .read = compat_ac97_read,
+};
+
+static struct snd_ac97_bus compat_soc_ac97_bus = {
+ .ops = &compat_snd_ac97_bus_ops,
+};
+
+struct snd_ac97 *snd_ac97_compat_alloc(struct ac97_codec_device *adev)
+{
+ struct snd_ac97 *ac97;
+ int ret;
+
+ ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
+ if (ac97 == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ ac97->private_data = adev;
+ ac97->bus = &compat_soc_ac97_bus;
+
+ ac97->dev.parent = &adev->dev;
+ ac97->dev.release = compat_ac97_release;
+ dev_set_name(&ac97->dev, "%s-compat", dev_name(&adev->dev));
+ ret = device_register(&ac97->dev);
+ if (ret) {
+ put_device(&ac97->dev);
+ return ERR_PTR(ret);
+ }
+
+ return ac97;
+}
+EXPORT_SYMBOL_GPL(snd_ac97_compat_alloc);
+
+void snd_ac97_compat_release(struct snd_ac97 *ac97)
+{
+ device_unregister(&ac97->dev);
+}
+EXPORT_SYMBOL_GPL(snd_ac97_compat_release);
+
+int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id,
+ unsigned int id_mask)
+{
+ struct ac97_codec_device *adev = to_ac97_device(ac97->private_data);
+ struct ac97_controller *actrl = adev->ac97_ctrl;
+ unsigned int scanned;
+
+ if (try_warm) {
+ compat_ac97_warm_reset(ac97);
+ scanned = snd_ac97_bus_scan_one(actrl, adev->num);
+ if (ac97_ids_match(scanned, adev->vendor_id, id_mask))
+ return 1;
+ }
+
+ compat_ac97_reset(ac97);
+ compat_ac97_warm_reset(ac97);
+ scanned = snd_ac97_bus_scan_one(actrl, adev->num);
+ if (ac97_ids_match(scanned, adev->vendor_id, id_mask))
+ return 0;
+
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(snd_ac97_reset);
diff --git a/sound/ac97_bus.c b/sound/ac97_bus.c
index a351dd0a09c7..8a44297964f5 100644
--- a/sound/ac97_bus.c
+++ b/sound/ac97_bus.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Linux driver model AC97 bus interface
*
* Author: Nicolas Pitre
* Created: Jan 14, 2005
* Copyright: (C) MontaVista Software Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
*/
#include <linux/module.h>
@@ -18,44 +14,72 @@
#include <sound/ac97_codec.h>
/*
- * Let drivers decide whether they want to support given codec from their
- * probe method. Drivers have direct access to the struct snd_ac97 structure and may
- * decide based on the id field amongst other things.
+ * snd_ac97_check_id() - Reads and checks the vendor ID of the device
+ * @ac97: The AC97 device to check
+ * @id: The ID to compare to
+ * @id_mask: Mask that is applied to the device ID before comparing to @id
+ *
+ * If @id is 0 this function returns true if the read device vendor ID is
+ * a valid ID. If @id is non 0 this functions returns true if @id
+ * matches the read vendor ID. Otherwise the function returns false.
*/
-static int ac97_bus_match(struct device *dev, struct device_driver *drv)
+static bool snd_ac97_check_id(struct snd_ac97 *ac97, unsigned int id,
+ unsigned int id_mask)
{
- return 1;
-}
+ ac97->id = ac97->bus->ops->read(ac97, AC97_VENDOR_ID1) << 16;
+ ac97->id |= ac97->bus->ops->read(ac97, AC97_VENDOR_ID2);
-#ifdef CONFIG_PM
-static int ac97_bus_suspend(struct device *dev, pm_message_t state)
-{
- int ret = 0;
+ if (ac97->id == 0x0 || ac97->id == 0xffffffff)
+ return false;
- if (dev->driver && dev->driver->suspend)
- ret = dev->driver->suspend(dev, state);
+ if (id != 0 && id != (ac97->id & id_mask))
+ return false;
- return ret;
+ return true;
}
-static int ac97_bus_resume(struct device *dev)
+/**
+ * snd_ac97_reset() - Reset AC'97 device
+ * @ac97: The AC'97 device to reset
+ * @try_warm: Try a warm reset first
+ * @id: Expected device vendor ID
+ * @id_mask: Mask that is applied to the device ID before comparing to @id
+ *
+ * This function resets the AC'97 device. If @try_warm is true the function
+ * first performs a warm reset. If @try_warm is false the function issues
+ * cold reset followed by a warm reset. If @id is 0 any valid device ID
+ * will be accepted, otherwise only the ID that matches @id and @id_mask
+ * is accepted.
+ * Returns:
+ * * %1 - if warm reset is successful
+ * * %0 - if cold reset and warm reset is successful
+ * * %-ENODEV - if @id and @id_mask not matching
+ */
+int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id,
+ unsigned int id_mask)
{
- int ret = 0;
+ const struct snd_ac97_bus_ops *ops = ac97->bus->ops;
+
+ if (try_warm && ops->warm_reset) {
+ ops->warm_reset(ac97);
+ if (snd_ac97_check_id(ac97, id, id_mask))
+ return 1;
+ }
+
+ if (ops->reset)
+ ops->reset(ac97);
+ if (ops->warm_reset)
+ ops->warm_reset(ac97);
- if (dev->driver && dev->driver->resume)
- ret = dev->driver->resume(dev);
+ if (snd_ac97_check_id(ac97, id, id_mask))
+ return 0;
- return ret;
+ return -ENODEV;
}
-#endif /* CONFIG_PM */
+EXPORT_SYMBOL_GPL(snd_ac97_reset);
-struct bus_type ac97_bus_type = {
+const struct bus_type ac97_bus_type = {
.name = "ac97",
- .match = ac97_bus_match,
-#ifdef CONFIG_PM
- .suspend = ac97_bus_suspend,
- .resume = ac97_bus_resume,
-#endif /* CONFIG_PM */
};
static int __init ac97_bus_init(void)
@@ -74,4 +98,5 @@ module_exit(ac97_bus_exit);
EXPORT_SYMBOL(ac97_bus_type);
+MODULE_DESCRIPTION("Legacy AC97 bus interface");
MODULE_LICENSE("GPL");
diff --git a/sound/aoa/Kconfig b/sound/aoa/Kconfig
index c081e18b9540..c58308ae4dc1 100644
--- a/sound/aoa/Kconfig
+++ b/sound/aoa/Kconfig
@@ -1,8 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
menuconfig SND_AOA
tristate "Apple Onboard Audio driver"
depends on PPC_PMAC
select SND_PCM
- ---help---
+ help
This option enables the new driver for the various
Apple Onboard Audio components.
diff --git a/sound/aoa/Makefile b/sound/aoa/Makefile
index a8c037f908f8..8dbfb01ba227 100644
--- a/sound/aoa/Makefile
+++ b/sound/aoa/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SND_AOA) += core/
obj-$(CONFIG_SND_AOA_SOUNDBUS) += soundbus/
obj-$(CONFIG_SND_AOA) += fabrics/
diff --git a/sound/aoa/aoa-gpio.h b/sound/aoa/aoa-gpio.h
index 6065b0344e23..77ae75d7594c 100644
--- a/sound/aoa/aoa-gpio.h
+++ b/sound/aoa/aoa-gpio.h
@@ -1,16 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Apple Onboard Audio GPIO definitions
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#ifndef __AOA_GPIO_H
#define __AOA_GPIO_H
#include <linux/workqueue.h>
#include <linux/mutex.h>
-#include <asm/prom.h>
typedef void (*notify_func_t)(void *data);
diff --git a/sound/aoa/aoa.h b/sound/aoa/aoa.h
index e08789484e30..badff9f7cd54 100644
--- a/sound/aoa/aoa.h
+++ b/sound/aoa/aoa.h
@@ -1,14 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Apple Onboard Audio definitions
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#ifndef __AOA_H
#define __AOA_H
-#include <asm/prom.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/asound.h>
@@ -116,8 +114,8 @@ struct aoa_card {
struct snd_card *alsa_card;
};
-extern int aoa_snd_device_new(snd_device_type_t type,
- void * device_data, struct snd_device_ops * ops);
+extern int aoa_snd_device_new(enum snd_device_type type,
+ void *device_data, const struct snd_device_ops *ops);
extern struct snd_card *aoa_get_card(void);
extern int aoa_snd_ctl_add(struct snd_kcontrol* control);
diff --git a/sound/aoa/codecs/Kconfig b/sound/aoa/codecs/Kconfig
index 808eb11ebacd..03f89dbcca75 100644
--- a/sound/aoa/codecs/Kconfig
+++ b/sound/aoa/codecs/Kconfig
@@ -1,32 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SND_AOA_ONYX
tristate "support Onyx chip"
select I2C
select I2C_POWERMAC
- ---help---
+ help
This option enables support for the Onyx (pcm3052)
codec chip found in the latest Apple machines
(most of those with digital audio output).
-#config SND_AOA_TOPAZ
-# tristate "support Topaz chips"
-# ---help---
-# This option enables support for the Topaz (CS84xx)
-# codec chips found in the latest Apple machines,
-# these chips do the digital input and output on
-# some PowerMacs.
-
config SND_AOA_TAS
tristate "support TAS chips"
select I2C
select I2C_POWERMAC
- ---help---
+ help
This option enables support for the tas chips
found in a lot of Apple Machines, especially
iBooks and PowerBooks without digital.
config SND_AOA_TOONIE
tristate "support Toonie chip"
- ---help---
+ help
This option enables support for the toonie codec
found in the Mac Mini. If you have a Mac Mini and
want to hear sound, select this option.
diff --git a/sound/aoa/codecs/Makefile b/sound/aoa/codecs/Makefile
index c3ee77fc4b2d..8feedc771bd9 100644
--- a/sound/aoa/codecs/Makefile
+++ b/sound/aoa/codecs/Makefile
@@ -1,6 +1,7 @@
-snd-aoa-codec-onyx-objs := onyx.o
-snd-aoa-codec-tas-objs := tas.o
-snd-aoa-codec-toonie-objs := toonie.o
+# SPDX-License-Identifier: GPL-2.0
+snd-aoa-codec-onyx-y := onyx.o
+snd-aoa-codec-tas-y := tas.o
+snd-aoa-codec-toonie-y := toonie.o
obj-$(CONFIG_SND_AOA_ONYX) += snd-aoa-codec-onyx.o
obj-$(CONFIG_SND_AOA_TAS) += snd-aoa-codec-tas.o
diff --git a/sound/aoa/codecs/onyx.c b/sound/aoa/codecs/onyx.c
index 91852e49910e..4cf959017c9d 100644
--- a/sound/aoa/codecs/onyx.c
+++ b/sound/aoa/codecs/onyx.c
@@ -1,11 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Apple Onboard Audio driver for Onyx codec
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
*
- * GPL v2, can be found in COPYING.
- *
- *
* This is a driver for the pcm3052 codec chip (codenamed Onyx)
* that is present in newer Apple hardware (with digital output).
*
@@ -29,10 +27,10 @@
* having just a single card on a system, and making the
* 'card' pointer accessible to anyone who needs it instead
* of hiding it in the aoa_snd_* functions...
- *
*/
#include <linux/delay.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/slab.h>
MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
MODULE_LICENSE("GPL");
@@ -74,8 +72,10 @@ static int onyx_read_register(struct onyx *onyx, u8 reg, u8 *value)
return 0;
}
v = i2c_smbus_read_byte_data(onyx->i2c, reg);
- if (v < 0)
+ if (v < 0) {
+ *value = 0;
return -1;
+ }
*value = (u8)v;
onyx->cache[ONYX_REG_CONTROL-FIRSTREGISTER] = *value;
return 0;
@@ -98,7 +98,7 @@ static int onyx_dev_register(struct snd_device *dev)
return 0;
}
-static struct snd_device_ops ops = {
+static const struct snd_device_ops ops = {
.dev_register = onyx_dev_register,
};
@@ -122,10 +122,9 @@ static int onyx_snd_vol_get(struct snd_kcontrol *kcontrol,
struct onyx *onyx = snd_kcontrol_chip(kcontrol);
s8 l, r;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l);
onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r);
- mutex_unlock(&onyx->mutex);
ucontrol->value.integer.value[0] = l + VOLUME_RANGE_SHIFT;
ucontrol->value.integer.value[1] = r + VOLUME_RANGE_SHIFT;
@@ -146,15 +145,13 @@ static int onyx_snd_vol_put(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[1] > -1 + VOLUME_RANGE_SHIFT)
return -EINVAL;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l);
onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r);
if (l + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[0] &&
- r + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[1]) {
- mutex_unlock(&onyx->mutex);
+ r + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[1])
return 0;
- }
onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_LEFT,
ucontrol->value.integer.value[0]
@@ -162,12 +159,11 @@ static int onyx_snd_vol_put(struct snd_kcontrol *kcontrol,
onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT,
ucontrol->value.integer.value[1]
- VOLUME_RANGE_SHIFT);
- mutex_unlock(&onyx->mutex);
return 1;
}
-static struct snd_kcontrol_new volume_control = {
+static const struct snd_kcontrol_new volume_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -198,9 +194,8 @@ static int onyx_snd_inputgain_get(struct snd_kcontrol *kcontrol,
struct onyx *onyx = snd_kcontrol_chip(kcontrol);
u8 ig;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &ig);
- mutex_unlock(&onyx->mutex);
ucontrol->value.integer.value[0] =
(ig & ONYX_ADC_PGA_GAIN_MASK) + INPUTGAIN_RANGE_SHIFT;
@@ -217,19 +212,18 @@ static int onyx_snd_inputgain_put(struct snd_kcontrol *kcontrol,
if (ucontrol->value.integer.value[0] < 3 + INPUTGAIN_RANGE_SHIFT ||
ucontrol->value.integer.value[0] > 28 + INPUTGAIN_RANGE_SHIFT)
return -EINVAL;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
n = v;
n &= ~ONYX_ADC_PGA_GAIN_MASK;
n |= (ucontrol->value.integer.value[0] - INPUTGAIN_RANGE_SHIFT)
& ONYX_ADC_PGA_GAIN_MASK;
onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, n);
- mutex_unlock(&onyx->mutex);
return n != v;
}
-static struct snd_kcontrol_new inputgain_control = {
+static const struct snd_kcontrol_new inputgain_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Capture Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -241,15 +235,9 @@ static struct snd_kcontrol_new inputgain_control = {
static int onyx_snd_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "Line-In", "Microphone" };
+ static const char * const texts[] = { "Line-In", "Microphone" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int onyx_snd_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -258,9 +246,8 @@ static int onyx_snd_capture_source_get(struct snd_kcontrol *kcontrol,
struct onyx *onyx = snd_kcontrol_chip(kcontrol);
s8 v;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
- mutex_unlock(&onyx->mutex);
ucontrol->value.enumerated.item[0] = !!(v&ONYX_ADC_INPUT_MIC);
@@ -271,13 +258,12 @@ static void onyx_set_capture_source(struct onyx *onyx, int mic)
{
s8 v;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v);
v &= ~ONYX_ADC_INPUT_MIC;
if (mic)
v |= ONYX_ADC_INPUT_MIC;
onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, v);
- mutex_unlock(&onyx->mutex);
}
static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol,
@@ -290,7 +276,7 @@ static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static struct snd_kcontrol_new capture_source_control = {
+static const struct snd_kcontrol_new capture_source_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* If we name this 'Input Source', it properly shows up in
* alsamixer as a selection, * but it's shown under the
@@ -318,9 +304,8 @@ static int onyx_snd_mute_get(struct snd_kcontrol *kcontrol,
struct onyx *onyx = snd_kcontrol_chip(kcontrol);
u8 c;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &c);
- mutex_unlock(&onyx->mutex);
ucontrol->value.integer.value[0] = !(c & ONYX_MUTE_LEFT);
ucontrol->value.integer.value[1] = !(c & ONYX_MUTE_RIGHT);
@@ -335,9 +320,9 @@ static int onyx_snd_mute_put(struct snd_kcontrol *kcontrol,
u8 v = 0, c = 0;
int err = -EBUSY;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
if (onyx->analog_locked)
- goto out_unlock;
+ return -EBUSY;
onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
c = v;
@@ -348,13 +333,10 @@ static int onyx_snd_mute_put(struct snd_kcontrol *kcontrol,
c |= ONYX_MUTE_RIGHT;
err = onyx_write_register(onyx, ONYX_REG_DAC_CONTROL, c);
- out_unlock:
- mutex_unlock(&onyx->mutex);
-
return !err ? (v != c) : err;
}
-static struct snd_kcontrol_new mute_control = {
+static const struct snd_kcontrol_new mute_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -379,9 +361,8 @@ static int onyx_snd_single_bit_get(struct snd_kcontrol *kcontrol,
u8 address = (pv >> 8) & 0xff;
u8 mask = pv & 0xff;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, address, &c);
- mutex_unlock(&onyx->mutex);
ucontrol->value.integer.value[0] = !!(c & mask) ^ polarity;
@@ -400,11 +381,10 @@ static int onyx_snd_single_bit_put(struct snd_kcontrol *kcontrol,
u8 address = (pv >> 8) & 0xff;
u8 mask = pv & 0xff;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
if (spdiflock && onyx->spdif_locked) {
/* even if alsamixer doesn't care.. */
- err = -EBUSY;
- goto out_unlock;
+ return -EBUSY;
}
onyx_read_register(onyx, address, &v);
c = v;
@@ -413,14 +393,11 @@ static int onyx_snd_single_bit_put(struct snd_kcontrol *kcontrol,
c |= mask;
err = onyx_write_register(onyx, address, c);
- out_unlock:
- mutex_unlock(&onyx->mutex);
-
return !err ? (v != c) : err;
}
#define SINGLE_BIT(n, type, description, address, mask, flags) \
-static struct snd_kcontrol_new n##_control = { \
+static const struct snd_kcontrol_new n##_control = { \
.iface = SNDRV_CTL_ELEM_IFACE_##type, \
.name = description, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
@@ -482,7 +459,7 @@ static int onyx_spdif_mask_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new onyx_spdif_mask = {
+static const struct snd_kcontrol_new onyx_spdif_mask = {
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
@@ -496,7 +473,7 @@ static int onyx_spdif_get(struct snd_kcontrol *kcontrol,
struct onyx *onyx = snd_kcontrol_chip(kcontrol);
u8 v;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v);
ucontrol->value.iec958.status[0] = v & 0x3e;
@@ -508,7 +485,6 @@ static int onyx_spdif_get(struct snd_kcontrol *kcontrol,
onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
ucontrol->value.iec958.status[4] = v & 0x0f;
- mutex_unlock(&onyx->mutex);
return 0;
}
@@ -519,7 +495,7 @@ static int onyx_spdif_put(struct snd_kcontrol *kcontrol,
struct onyx *onyx = snd_kcontrol_chip(kcontrol);
u8 v;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v);
v = (v & ~0x3e) | (ucontrol->value.iec958.status[0] & 0x3e);
onyx_write_register(onyx, ONYX_REG_DIG_INFO1, v);
@@ -534,12 +510,11 @@ static int onyx_spdif_put(struct snd_kcontrol *kcontrol,
onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
v = (v & ~0x0f) | (ucontrol->value.iec958.status[4] & 0x0f);
onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v);
- mutex_unlock(&onyx->mutex);
return 1;
}
-static struct snd_kcontrol_new onyx_spdif_ctrl = {
+static const struct snd_kcontrol_new onyx_spdif_ctrl = {
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -550,7 +525,7 @@ static struct snd_kcontrol_new onyx_spdif_ctrl = {
/* our registers */
-static u8 register_map[] = {
+static const u8 register_map[] = {
ONYX_REG_DAC_ATTEN_LEFT,
ONYX_REG_DAC_ATTEN_RIGHT,
ONYX_REG_CONTROL,
@@ -566,7 +541,7 @@ static u8 register_map[] = {
ONYX_REG_DIG_INFO4
};
-static u8 initial_values[ARRAY_SIZE(register_map)] = {
+static const u8 initial_values[ARRAY_SIZE(register_map)] = {
0x80, 0x80, /* muted */
ONYX_MRST | ONYX_SRST, /* but handled specially! */
ONYX_MUTE_LEFT | ONYX_MUTE_RIGHT,
@@ -679,14 +654,13 @@ static int onyx_usable(struct codec_info_item *cii,
struct onyx *onyx = cii->codec_data;
int spdif_enabled, analog_enabled;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v);
spdif_enabled = !!(v & ONYX_SPDIF_ENABLE);
onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
analog_enabled =
(v & (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT))
!= (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT);
- mutex_unlock(&onyx->mutex);
switch (ti->tag) {
case 0: return 1;
@@ -702,9 +676,8 @@ static int onyx_prepare(struct codec_info_item *cii,
{
u8 v;
struct onyx *onyx = cii->codec_data;
- int err = -EBUSY;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) {
@@ -713,10 +686,9 @@ static int onyx_prepare(struct codec_info_item *cii,
if (onyx_write_register(onyx,
ONYX_REG_DAC_CONTROL,
v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT))
- goto out_unlock;
+ return -EBUSY;
onyx->analog_locked = 1;
- err = 0;
- goto out_unlock;
+ return 0;
}
#endif
switch (substream->runtime->rate) {
@@ -726,8 +698,7 @@ static int onyx_prepare(struct codec_info_item *cii,
/* these rates are ok for all outputs */
/* FIXME: program spdif channel control bits here so that
* userspace doesn't have to if it only plays pcm! */
- err = 0;
- goto out_unlock;
+ return 0;
default:
/* got some rate that the digital output can't do,
* so disable and lock it */
@@ -735,16 +706,12 @@ static int onyx_prepare(struct codec_info_item *cii,
if (onyx_write_register(onyx,
ONYX_REG_DIG_INFO4,
v & ~ONYX_SPDIF_ENABLE))
- goto out_unlock;
+ return -EBUSY;
onyx->spdif_locked = 1;
- err = 0;
- goto out_unlock;
+ return 0;
}
- out_unlock:
- mutex_unlock(&onyx->mutex);
-
- return err;
+ return -EBUSY;
}
static int onyx_open(struct codec_info_item *cii,
@@ -752,9 +719,8 @@ static int onyx_open(struct codec_info_item *cii,
{
struct onyx *onyx = cii->codec_data;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx->open_count++;
- mutex_unlock(&onyx->mutex);
return 0;
}
@@ -764,11 +730,10 @@ static int onyx_close(struct codec_info_item *cii,
{
struct onyx *onyx = cii->codec_data;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
onyx->open_count--;
if (!onyx->open_count)
onyx->spdif_locked = onyx->analog_locked = 0;
- mutex_unlock(&onyx->mutex);
return 0;
}
@@ -778,7 +743,7 @@ static int onyx_switch_clock(struct codec_info_item *cii,
{
struct onyx *onyx = cii->codec_data;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
/* this *MUST* be more elaborate later... */
switch (what) {
case CLOCK_SWITCH_PREPARE_SLAVE:
@@ -790,7 +755,6 @@ static int onyx_switch_clock(struct codec_info_item *cii,
default: /* silence warning */
break;
}
- mutex_unlock(&onyx->mutex);
return 0;
}
@@ -801,27 +765,21 @@ static int onyx_suspend(struct codec_info_item *cii, pm_message_t state)
{
struct onyx *onyx = cii->codec_data;
u8 v;
- int err = -ENXIO;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v))
- goto out_unlock;
+ return -ENXIO;
onyx_write_register(onyx, ONYX_REG_CONTROL, v | ONYX_ADPSV | ONYX_DAPSV);
/* Apple does a sleep here but the datasheet says to do it on resume */
- err = 0;
- out_unlock:
- mutex_unlock(&onyx->mutex);
-
- return err;
+ return 0;
}
static int onyx_resume(struct codec_info_item *cii)
{
struct onyx *onyx = cii->codec_data;
u8 v;
- int err = -ENXIO;
- mutex_lock(&onyx->mutex);
+ guard(mutex)(&onyx->mutex);
/* reset codec */
onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0);
@@ -833,17 +791,13 @@ static int onyx_resume(struct codec_info_item *cii)
/* take codec out of suspend (if it still is after reset) */
if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v))
- goto out_unlock;
+ return -ENXIO;
onyx_write_register(onyx, ONYX_REG_CONTROL, v & ~(ONYX_ADPSV | ONYX_DAPSV));
/* FIXME: should divide by sample rate, but 8k is the lowest we go */
msleep(2205000/8000);
/* reset all values */
onyx_register_init(onyx);
- err = 0;
- out_unlock:
- mutex_unlock(&onyx->mutex);
-
- return err;
+ return 0;
}
#endif /* CONFIG_PM */
@@ -889,7 +843,7 @@ static int onyx_init_codec(struct aoa_codec *codec)
return -ENODEV;
}
- if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, onyx, &ops)) {
+ if (aoa_snd_device_new(SNDRV_DEV_CODEC, onyx, &ops)) {
printk(KERN_ERR PFX "failed to create onyx snd device!\n");
return -ENODEV;
}
@@ -997,45 +951,9 @@ static void onyx_exit_codec(struct aoa_codec *codec)
onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx);
}
-static int onyx_create(struct i2c_adapter *adapter,
- struct device_node *node,
- int addr)
-{
- struct i2c_board_info info;
- struct i2c_client *client;
-
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "aoa_codec_onyx", I2C_NAME_SIZE);
- info.addr = addr;
- info.platform_data = node;
- client = i2c_new_device(adapter, &info);
- if (!client)
- return -ENODEV;
-
- /*
- * We know the driver is already loaded, so the device should be
- * already bound. If not it means binding failed, which suggests
- * the device doesn't really exist and should be deleted.
- * Ideally this would be replaced by better checks _before_
- * instantiating the device.
- */
- if (!client->driver) {
- i2c_unregister_device(client);
- return -ENODEV;
- }
-
- /*
- * Let i2c-core delete that device on driver removal.
- * This is safe because i2c-core holds the core_lock mutex for us.
- */
- list_add_tail(&client->detected, &client->driver->clients);
- return 0;
-}
-
-static int onyx_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int onyx_i2c_probe(struct i2c_client *client)
{
- struct device_node *node = client->dev.platform_data;
+ struct device_node *node = client->dev.of_node;
struct onyx *onyx;
u8 dummy;
@@ -1055,7 +973,7 @@ static int onyx_i2c_probe(struct i2c_client *client,
goto fail;
}
- strlcpy(onyx->codec.name, "onyx", MAX_CODEC_NAME_LEN);
+ strscpy(onyx->codec.name, "onyx");
onyx->codec.owner = THIS_MODULE;
onyx->codec.init = onyx_init_codec;
onyx->codec.exit = onyx_exit_codec;
@@ -1067,83 +985,33 @@ static int onyx_i2c_probe(struct i2c_client *client,
printk(KERN_DEBUG PFX "created and attached onyx instance\n");
return 0;
fail:
- i2c_set_clientdata(client, NULL);
kfree(onyx);
return -ENODEV;
}
-static int onyx_i2c_attach(struct i2c_adapter *adapter)
-{
- struct device_node *busnode, *dev = NULL;
- struct pmac_i2c_bus *bus;
-
- bus = pmac_i2c_adapter_to_bus(adapter);
- if (bus == NULL)
- return -ENODEV;
- busnode = pmac_i2c_get_bus_node(bus);
-
- while ((dev = of_get_next_child(busnode, dev)) != NULL) {
- if (of_device_is_compatible(dev, "pcm3052")) {
- const u32 *addr;
- printk(KERN_DEBUG PFX "found pcm3052\n");
- addr = of_get_property(dev, "reg", NULL);
- if (!addr)
- return -ENODEV;
- return onyx_create(adapter, dev, (*addr)>>1);
- }
- }
-
- /* if that didn't work, try desperate mode for older
- * machines that have stuff missing from the device tree */
-
- if (!of_device_is_compatible(busnode, "k2-i2c"))
- return -ENODEV;
-
- printk(KERN_DEBUG PFX "found k2-i2c, checking if onyx chip is on it\n");
- /* probe both possible addresses for the onyx chip */
- if (onyx_create(adapter, NULL, 0x46) == 0)
- return 0;
- return onyx_create(adapter, NULL, 0x47);
-}
-
-static int onyx_i2c_remove(struct i2c_client *client)
+static void onyx_i2c_remove(struct i2c_client *client)
{
struct onyx *onyx = i2c_get_clientdata(client);
aoa_codec_unregister(&onyx->codec);
of_node_put(onyx->codec.node);
- if (onyx->codec_info)
- kfree(onyx->codec_info);
- i2c_set_clientdata(client, onyx);
+ kfree(onyx->codec_info);
kfree(onyx);
- return 0;
}
static const struct i2c_device_id onyx_i2c_id[] = {
- { "aoa_codec_onyx", 0 },
+ { "MAC,pcm3052" },
{ }
};
+MODULE_DEVICE_TABLE(i2c,onyx_i2c_id);
static struct i2c_driver onyx_driver = {
.driver = {
.name = "aoa_codec_onyx",
- .owner = THIS_MODULE,
},
- .attach_adapter = onyx_i2c_attach,
.probe = onyx_i2c_probe,
.remove = onyx_i2c_remove,
.id_table = onyx_i2c_id,
};
-static int __init onyx_init(void)
-{
- return i2c_add_driver(&onyx_driver);
-}
-
-static void __exit onyx_exit(void)
-{
- i2c_del_driver(&onyx_driver);
-}
-
-module_init(onyx_init);
-module_exit(onyx_exit);
+module_i2c_driver(onyx_driver);
diff --git a/sound/aoa/codecs/onyx.h b/sound/aoa/codecs/onyx.h
index ffd20254ff76..bbdca841fe90 100644
--- a/sound/aoa/codecs/onyx.h
+++ b/sound/aoa/codecs/onyx.h
@@ -1,16 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Apple Onboard Audio driver for Onyx codec (header)
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#ifndef __SND_AOA_CODEC_ONYX_H
#define __SND_AOA_CODEC_ONYX_H
-#include <stddef.h>
#include <linux/i2c.h>
#include <asm/pmac_low_i2c.h>
-#include <asm/prom.h>
/* PCM3052 register definitions */
diff --git a/sound/aoa/codecs/tas-basstreble.h b/sound/aoa/codecs/tas-basstreble.h
index 69b61136fd54..14dc4e9eea73 100644
--- a/sound/aoa/codecs/tas-basstreble.h
+++ b/sound/aoa/codecs/tas-basstreble.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* This file is only included exactly once!
*
@@ -12,7 +13,7 @@
#define TAS3004_TREBLE_ZERO 36
#define TAS3004_BASS_ZERO 36
-static u8 tas3004_treble_table[] = {
+static const u8 tas3004_treble_table[] = {
150, /* -18 dB */
149,
148,
@@ -98,7 +99,7 @@ static inline u8 tas3004_treble(int idx)
* I have also ignored completely differences of
* +/- 1
*/
-static s8 tas3004_bass_diff_to_treble[] = {
+static const s8 tas3004_bass_diff_to_treble[] = {
2, /* 7 dB, offset 50 */
2,
2,
diff --git a/sound/aoa/codecs/tas-gain-table.h b/sound/aoa/codecs/tas-gain-table.h
index 4cfa6757715e..c9ea01488181 100644
--- a/sound/aoa/codecs/tas-gain-table.h
+++ b/sound/aoa/codecs/tas-gain-table.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
This is the program used to generate below table.
@@ -26,7 +27,7 @@ int main() {
* as easy as calculating
* hwvalue = 1048576.0*exp(0.057564628*dB*2)
* :) */
-static int tas_gaintable[] = {
+static const int tas_gaintable[] = {
0x000000, /* -infinity dB */
0x00014b, /* -70.0 dB */
0x00015f, /* -69.5 dB */
diff --git a/sound/aoa/codecs/tas.c b/sound/aoa/codecs/tas.c
index fd2188c3df2b..7085e0b93e29 100644
--- a/sound/aoa/codecs/tas.c
+++ b/sound/aoa/codecs/tas.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Apple Onboard Audio driver for tas codec
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
*
- * GPL v2, can be found in COPYING.
- *
* Open questions:
* - How to distinguish between 3004 and versions?
*
@@ -59,13 +58,12 @@
* and up to the hardware designer to not wire
* them up in some weird unusable way.
*/
-#include <stddef.h>
#include <linux/i2c.h>
#include <asm/pmac_low_i2c.h>
-#include <asm/prom.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/of.h>
#include <linux/slab.h>
MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
@@ -218,7 +216,7 @@ static int tas_dev_register(struct snd_device *dev)
return 0;
}
-static struct snd_device_ops ops = {
+static const struct snd_device_ops ops = {
.dev_register = tas_dev_register,
};
@@ -237,10 +235,9 @@ static int tas_snd_vol_get(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
ucontrol->value.integer.value[0] = tas->cached_volume_l;
ucontrol->value.integer.value[1] = tas->cached_volume_r;
- mutex_unlock(&tas->mtx);
return 0;
}
@@ -256,22 +253,19 @@ static int tas_snd_vol_put(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[1] > 177)
return -EINVAL;
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
if (tas->cached_volume_l == ucontrol->value.integer.value[0]
- && tas->cached_volume_r == ucontrol->value.integer.value[1]) {
- mutex_unlock(&tas->mtx);
+ && tas->cached_volume_r == ucontrol->value.integer.value[1])
return 0;
- }
tas->cached_volume_l = ucontrol->value.integer.value[0];
tas->cached_volume_r = ucontrol->value.integer.value[1];
if (tas->hw_enabled)
tas_set_volume(tas);
- mutex_unlock(&tas->mtx);
return 1;
}
-static struct snd_kcontrol_new volume_control = {
+static const struct snd_kcontrol_new volume_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -287,10 +281,9 @@ static int tas_snd_mute_get(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
ucontrol->value.integer.value[0] = !tas->mute_l;
ucontrol->value.integer.value[1] = !tas->mute_r;
- mutex_unlock(&tas->mtx);
return 0;
}
@@ -299,22 +292,19 @@ static int tas_snd_mute_put(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
if (tas->mute_l == !ucontrol->value.integer.value[0]
- && tas->mute_r == !ucontrol->value.integer.value[1]) {
- mutex_unlock(&tas->mtx);
+ && tas->mute_r == !ucontrol->value.integer.value[1])
return 0;
- }
tas->mute_l = !ucontrol->value.integer.value[0];
tas->mute_r = !ucontrol->value.integer.value[1];
if (tas->hw_enabled)
tas_set_volume(tas);
- mutex_unlock(&tas->mtx);
return 1;
}
-static struct snd_kcontrol_new mute_control = {
+static const struct snd_kcontrol_new mute_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -339,10 +329,9 @@ static int tas_snd_mixer_get(struct snd_kcontrol *kcontrol,
struct tas *tas = snd_kcontrol_chip(kcontrol);
int idx = kcontrol->private_value;
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
ucontrol->value.integer.value[0] = tas->mixer_l[idx];
ucontrol->value.integer.value[1] = tas->mixer_r[idx];
- mutex_unlock(&tas->mtx);
return 0;
}
@@ -353,24 +342,21 @@ static int tas_snd_mixer_put(struct snd_kcontrol *kcontrol,
struct tas *tas = snd_kcontrol_chip(kcontrol);
int idx = kcontrol->private_value;
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
if (tas->mixer_l[idx] == ucontrol->value.integer.value[0]
- && tas->mixer_r[idx] == ucontrol->value.integer.value[1]) {
- mutex_unlock(&tas->mtx);
+ && tas->mixer_r[idx] == ucontrol->value.integer.value[1])
return 0;
- }
tas->mixer_l[idx] = ucontrol->value.integer.value[0];
tas->mixer_r[idx] = ucontrol->value.integer.value[1];
if (tas->hw_enabled)
tas_set_mixer(tas);
- mutex_unlock(&tas->mtx);
return 1;
}
#define MIXER_CONTROL(n,descr,idx) \
-static struct snd_kcontrol_new n##_control = { \
+static const struct snd_kcontrol_new n##_control = { \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = descr " Playback Volume", \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
@@ -398,9 +384,8 @@ static int tas_snd_drc_range_get(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
ucontrol->value.integer.value[0] = tas->drc_range;
- mutex_unlock(&tas->mtx);
return 0;
}
@@ -413,20 +398,17 @@ static int tas_snd_drc_range_put(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[0] > TAS3004_DRC_MAX)
return -EINVAL;
- mutex_lock(&tas->mtx);
- if (tas->drc_range == ucontrol->value.integer.value[0]) {
- mutex_unlock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
+ if (tas->drc_range == ucontrol->value.integer.value[0])
return 0;
- }
tas->drc_range = ucontrol->value.integer.value[0];
if (tas->hw_enabled)
tas3004_set_drc(tas);
- mutex_unlock(&tas->mtx);
return 1;
}
-static struct snd_kcontrol_new drc_range_control = {
+static const struct snd_kcontrol_new drc_range_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DRC Range",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -442,9 +424,8 @@ static int tas_snd_drc_switch_get(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
ucontrol->value.integer.value[0] = tas->drc_enabled;
- mutex_unlock(&tas->mtx);
return 0;
}
@@ -453,20 +434,17 @@ static int tas_snd_drc_switch_put(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- mutex_lock(&tas->mtx);
- if (tas->drc_enabled == ucontrol->value.integer.value[0]) {
- mutex_unlock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
+ if (tas->drc_enabled == ucontrol->value.integer.value[0])
return 0;
- }
tas->drc_enabled = !!ucontrol->value.integer.value[0];
if (tas->hw_enabled)
tas3004_set_drc(tas);
- mutex_unlock(&tas->mtx);
return 1;
}
-static struct snd_kcontrol_new drc_switch_control = {
+static const struct snd_kcontrol_new drc_switch_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DRC Range Switch",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -478,15 +456,9 @@ static struct snd_kcontrol_new drc_switch_control = {
static int tas_snd_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "Line-In", "Microphone" };
+ static const char * const texts[] = { "Line-In", "Microphone" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int tas_snd_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -494,9 +466,8 @@ static int tas_snd_capture_source_get(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
ucontrol->value.enumerated.item[0] = !!(tas->acr & TAS_ACR_INPUT_B);
- mutex_unlock(&tas->mtx);
return 0;
}
@@ -508,7 +479,7 @@ static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol,
if (ucontrol->value.enumerated.item[0] > 1)
return -EINVAL;
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
oldacr = tas->acr;
/*
@@ -520,17 +491,14 @@ static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol,
if (ucontrol->value.enumerated.item[0])
tas->acr |= TAS_ACR_INPUT_B | TAS_ACR_B_MONAUREAL |
TAS_ACR_B_MON_SEL_RIGHT;
- if (oldacr == tas->acr) {
- mutex_unlock(&tas->mtx);
+ if (oldacr == tas->acr)
return 0;
- }
if (tas->hw_enabled)
tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
- mutex_unlock(&tas->mtx);
return 1;
}
-static struct snd_kcontrol_new capture_source_control = {
+static const struct snd_kcontrol_new capture_source_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* If we name this 'Input Source', it properly shows up in
* alsamixer as a selection, * but it's shown under the
@@ -565,9 +533,8 @@ static int tas_snd_treble_get(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
ucontrol->value.integer.value[0] = tas->treble;
- mutex_unlock(&tas->mtx);
return 0;
}
@@ -579,20 +546,17 @@ static int tas_snd_treble_put(struct snd_kcontrol *kcontrol,
if (ucontrol->value.integer.value[0] < TAS3004_TREBLE_MIN ||
ucontrol->value.integer.value[0] > TAS3004_TREBLE_MAX)
return -EINVAL;
- mutex_lock(&tas->mtx);
- if (tas->treble == ucontrol->value.integer.value[0]) {
- mutex_unlock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
+ if (tas->treble == ucontrol->value.integer.value[0])
return 0;
- }
tas->treble = ucontrol->value.integer.value[0];
if (tas->hw_enabled)
tas_set_treble(tas);
- mutex_unlock(&tas->mtx);
return 1;
}
-static struct snd_kcontrol_new treble_control = {
+static const struct snd_kcontrol_new treble_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Treble",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -616,9 +580,8 @@ static int tas_snd_bass_get(struct snd_kcontrol *kcontrol,
{
struct tas *tas = snd_kcontrol_chip(kcontrol);
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
ucontrol->value.integer.value[0] = tas->bass;
- mutex_unlock(&tas->mtx);
return 0;
}
@@ -630,20 +593,17 @@ static int tas_snd_bass_put(struct snd_kcontrol *kcontrol,
if (ucontrol->value.integer.value[0] < TAS3004_BASS_MIN ||
ucontrol->value.integer.value[0] > TAS3004_BASS_MAX)
return -EINVAL;
- mutex_lock(&tas->mtx);
- if (tas->bass == ucontrol->value.integer.value[0]) {
- mutex_unlock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
+ if (tas->bass == ucontrol->value.integer.value[0])
return 0;
- }
tas->bass = ucontrol->value.integer.value[0];
if (tas->hw_enabled)
tas_set_bass(tas);
- mutex_unlock(&tas->mtx);
return 1;
}
-static struct snd_kcontrol_new bass_control = {
+static const struct snd_kcontrol_new bass_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Bass",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -730,13 +690,13 @@ static int tas_switch_clock(struct codec_info_item *cii, enum clock_switch clock
break;
case CLOCK_SWITCH_SLAVE:
/* Clocks are back, re-init the codec */
- mutex_lock(&tas->mtx);
- tas_reset_init(tas);
- tas_set_volume(tas);
- tas_set_mixer(tas);
- tas->hw_enabled = 1;
- tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio);
- mutex_unlock(&tas->mtx);
+ scoped_guard(mutex, &tas->mtx) {
+ tas_reset_init(tas);
+ tas_set_volume(tas);
+ tas_set_mixer(tas);
+ tas->hw_enabled = 1;
+ tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio);
+ }
break;
default:
/* doesn't happen as of now */
@@ -751,23 +711,21 @@ static int tas_switch_clock(struct codec_info_item *cii, enum clock_switch clock
* our i2c device is suspended, and then take note of that! */
static int tas_suspend(struct tas *tas)
{
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
tas->hw_enabled = 0;
tas->acr |= TAS_ACR_ANALOG_PDOWN;
tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
- mutex_unlock(&tas->mtx);
return 0;
}
static int tas_resume(struct tas *tas)
{
/* reset codec */
- mutex_lock(&tas->mtx);
+ guard(mutex)(&tas->mtx);
tas_reset_init(tas);
tas_set_volume(tas);
tas_set_mixer(tas);
tas->hw_enabled = 1;
- mutex_unlock(&tas->mtx);
return 0;
}
@@ -810,14 +768,13 @@ static int tas_init_codec(struct aoa_codec *codec)
return -EINVAL;
}
- mutex_lock(&tas->mtx);
- if (tas_reset_init(tas)) {
- printk(KERN_ERR PFX "tas failed to initialise\n");
- mutex_unlock(&tas->mtx);
- return -ENXIO;
+ scoped_guard(mutex, &tas->mtx) {
+ if (tas_reset_init(tas)) {
+ printk(KERN_ERR PFX "tas failed to initialise\n");
+ return -ENXIO;
+ }
+ tas->hw_enabled = 1;
}
- tas->hw_enabled = 1;
- mutex_unlock(&tas->mtx);
if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev,
aoa_get_card(),
@@ -826,7 +783,7 @@ static int tas_init_codec(struct aoa_codec *codec)
return -ENODEV;
}
- if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, tas, &ops)) {
+ if (aoa_snd_device_new(SNDRV_DEV_CODEC, tas, &ops)) {
printk(KERN_ERR PFX "failed to create tas snd device!\n");
return -ENODEV;
}
@@ -883,43 +840,9 @@ static void tas_exit_codec(struct aoa_codec *codec)
}
-static int tas_create(struct i2c_adapter *adapter,
- struct device_node *node,
- int addr)
-{
- struct i2c_board_info info;
- struct i2c_client *client;
-
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "aoa_codec_tas", I2C_NAME_SIZE);
- info.addr = addr;
- info.platform_data = node;
-
- client = i2c_new_device(adapter, &info);
- if (!client)
- return -ENODEV;
- /*
- * We know the driver is already loaded, so the device should be
- * already bound. If not it means binding failed, and then there
- * is no point in keeping the device instantiated.
- */
- if (!client->driver) {
- i2c_unregister_device(client);
- return -ENODEV;
- }
-
- /*
- * Let i2c-core delete that device on driver removal.
- * This is safe because i2c-core holds the core_lock mutex for us.
- */
- list_add_tail(&client->detected, &client->driver->clients);
- return 0;
-}
-
-static int tas_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int tas_i2c_probe(struct i2c_client *client)
{
- struct device_node *node = client->dev.platform_data;
+ struct device_node *node = client->dev.of_node;
struct tas *tas;
tas = kzalloc(sizeof(struct tas), GFP_KERNEL);
@@ -934,7 +857,7 @@ static int tas_i2c_probe(struct i2c_client *client,
/* seems that half is a saner default */
tas->drc_range = TAS3004_DRC_MAX / 2;
- strlcpy(tas->codec.name, "tas", MAX_CODEC_NAME_LEN);
+ strscpy(tas->codec.name, "tas");
tas->codec.owner = THIS_MODULE;
tas->codec.init = tas_init_codec;
tas->codec.exit = tas_exit_codec;
@@ -944,8 +867,8 @@ static int tas_i2c_probe(struct i2c_client *client,
goto fail;
}
printk(KERN_DEBUG
- "snd-aoa-codec-tas: tas found, addr 0x%02x on %s\n",
- (unsigned int)client->addr, node->full_name);
+ "snd-aoa-codec-tas: tas found, addr 0x%02x on %pOF\n",
+ (unsigned int)client->addr, node);
return 0;
fail:
mutex_destroy(&tas->mtx);
@@ -953,48 +876,7 @@ static int tas_i2c_probe(struct i2c_client *client,
return -EINVAL;
}
-static int tas_i2c_attach(struct i2c_adapter *adapter)
-{
- struct device_node *busnode, *dev = NULL;
- struct pmac_i2c_bus *bus;
-
- bus = pmac_i2c_adapter_to_bus(adapter);
- if (bus == NULL)
- return -ENODEV;
- busnode = pmac_i2c_get_bus_node(bus);
-
- while ((dev = of_get_next_child(busnode, dev)) != NULL) {
- if (of_device_is_compatible(dev, "tas3004")) {
- const u32 *addr;
- printk(KERN_DEBUG PFX "found tas3004\n");
- addr = of_get_property(dev, "reg", NULL);
- if (!addr)
- continue;
- return tas_create(adapter, dev, ((*addr) >> 1) & 0x7f);
- }
- /* older machines have no 'codec' node with a 'compatible'
- * property that says 'tas3004', they just have a 'deq'
- * node without any such property... */
- if (strcmp(dev->name, "deq") == 0) {
- const u32 *_addr;
- u32 addr;
- printk(KERN_DEBUG PFX "found 'deq' node\n");
- _addr = of_get_property(dev, "i2c-address", NULL);
- if (!_addr)
- continue;
- addr = ((*_addr) >> 1) & 0x7f;
- /* now, if the address doesn't match any of the two
- * that a tas3004 can have, we cannot handle this.
- * I doubt it ever happens but hey. */
- if (addr != 0x34 && addr != 0x35)
- continue;
- return tas_create(adapter, dev, addr);
- }
- }
- return -ENODEV;
-}
-
-static int tas_i2c_remove(struct i2c_client *client)
+static void tas_i2c_remove(struct i2c_client *client)
{
struct tas *tas = i2c_get_clientdata(client);
u8 tmp = TAS_ACR_ANALOG_PDOWN;
@@ -1007,34 +889,21 @@ static int tas_i2c_remove(struct i2c_client *client)
mutex_destroy(&tas->mtx);
kfree(tas);
- return 0;
}
static const struct i2c_device_id tas_i2c_id[] = {
- { "aoa_codec_tas", 0 },
+ { "MAC,tas3004" },
{ }
};
+MODULE_DEVICE_TABLE(i2c,tas_i2c_id);
static struct i2c_driver tas_driver = {
.driver = {
.name = "aoa_codec_tas",
- .owner = THIS_MODULE,
},
- .attach_adapter = tas_i2c_attach,
.probe = tas_i2c_probe,
.remove = tas_i2c_remove,
.id_table = tas_i2c_id,
};
-static int __init tas_init(void)
-{
- return i2c_add_driver(&tas_driver);
-}
-
-static void __exit tas_exit(void)
-{
- i2c_del_driver(&tas_driver);
-}
-
-module_init(tas_init);
-module_exit(tas_exit);
+module_i2c_driver(tas_driver);
diff --git a/sound/aoa/codecs/tas.h b/sound/aoa/codecs/tas.h
index ae177e3466e6..b3891244afa7 100644
--- a/sound/aoa/codecs/tas.h
+++ b/sound/aoa/codecs/tas.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Apple Onboard Audio driver for tas codec (header)
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#ifndef __SND_AOA_CODECTASH
#define __SND_AOA_CODECTASH
diff --git a/sound/aoa/codecs/toonie.c b/sound/aoa/codecs/toonie.c
index 69d2cb601f2a..b59967c49e0a 100644
--- a/sound/aoa/codecs/toonie.c
+++ b/sound/aoa/codecs/toonie.c
@@ -1,11 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Apple Onboard Audio driver for Toonie codec
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
*
- * GPL v2, can be found in COPYING.
- *
- *
* This is a driver for the toonie codec chip. This chip is present
* on the Mac Mini and is nothing but a DAC.
*/
@@ -32,7 +30,7 @@ static int toonie_dev_register(struct snd_device *dev)
return 0;
}
-static struct snd_device_ops ops = {
+static const struct snd_device_ops ops = {
.dev_register = toonie_dev_register,
};
@@ -92,7 +90,7 @@ static int toonie_init_codec(struct aoa_codec *codec)
if (toonie->codec.connected != 1)
return -ENOTCONN;
- if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, toonie, &ops)) {
+ if (aoa_snd_device_new(SNDRV_DEV_CODEC, toonie, &ops)) {
printk(KERN_ERR PFX "failed to create toonie snd device!\n");
return -ENODEV;
}
@@ -128,7 +126,7 @@ static int __init toonie_init(void)
if (!toonie)
return -ENOMEM;
- strlcpy(toonie->codec.name, "toonie", sizeof(toonie->codec.name));
+ strscpy(toonie->codec.name, "toonie");
toonie->codec.owner = THIS_MODULE;
toonie->codec.init = toonie_init_codec;
toonie->codec.exit = toonie_exit_codec;
diff --git a/sound/aoa/core/Makefile b/sound/aoa/core/Makefile
index a1596e88c718..f586c340fe12 100644
--- a/sound/aoa/core/Makefile
+++ b/sound/aoa/core/Makefile
@@ -1,5 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_SND_AOA) += snd-aoa.o
-snd-aoa-objs := core.o \
+snd-aoa-y := core.o \
alsa.o \
gpio-pmf.o \
gpio-feature.o
diff --git a/sound/aoa/core/alsa.c b/sound/aoa/core/alsa.c
index 0fa3855b4790..aad7dfe089c7 100644
--- a/sound/aoa/core/alsa.c
+++ b/sound/aoa/core/alsa.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Apple Onboard Audio Alsa helpers
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#include <linux/module.h>
#include "alsa.h"
@@ -23,17 +22,16 @@ int aoa_alsa_init(char *name, struct module *mod, struct device *dev)
/* cannot be EEXIST due to usage in aoa_fabric_register */
return -EBUSY;
- err = snd_card_create(index, name, mod, sizeof(struct aoa_card),
- &alsa_card);
+ err = snd_card_new(dev, index, name, mod, sizeof(struct aoa_card),
+ &alsa_card);
if (err < 0)
return err;
aoa_card = alsa_card->private_data;
aoa_card->alsa_card = alsa_card;
- alsa_card->dev = dev;
- strlcpy(alsa_card->driver, "AppleOnbdAudio", sizeof(alsa_card->driver));
- strlcpy(alsa_card->shortname, name, sizeof(alsa_card->shortname));
- strlcpy(alsa_card->longname, name, sizeof(alsa_card->longname));
- strlcpy(alsa_card->mixername, name, sizeof(alsa_card->mixername));
+ strscpy(alsa_card->driver, "AppleOnbdAudio");
+ strscpy(alsa_card->shortname, name);
+ strscpy(alsa_card->longname, name);
+ strscpy(alsa_card->mixername, name);
err = snd_card_register(aoa_card->alsa_card);
if (err < 0) {
printk(KERN_ERR "snd-aoa: couldn't register alsa card\n");
@@ -60,8 +58,8 @@ void aoa_alsa_cleanup(void)
}
}
-int aoa_snd_device_new(snd_device_type_t type,
- void * device_data, struct snd_device_ops * ops)
+int aoa_snd_device_new(enum snd_device_type type,
+ void *device_data, const struct snd_device_ops *ops)
{
struct snd_card *card = aoa_get_card();
int err;
diff --git a/sound/aoa/core/alsa.h b/sound/aoa/core/alsa.h
index 9669e4489cab..8966a08c4cf2 100644
--- a/sound/aoa/core/alsa.h
+++ b/sound/aoa/core/alsa.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Apple Onboard Audio Alsa private helpers
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#ifndef __SND_AOA_ALSA_H
diff --git a/sound/aoa/core/core.c b/sound/aoa/core/core.c
index 10bec6c61382..99b032a4081f 100644
--- a/sound/aoa/core/core.c
+++ b/sound/aoa/core/core.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Apple Onboard Audio driver core
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#include <linux/init.h>
diff --git a/sound/aoa/core/gpio-feature.c b/sound/aoa/core/gpio-feature.c
index de8e03afa97b..19ed0e6907da 100644
--- a/sound/aoa/core/gpio-feature.c
+++ b/sound/aoa/core/gpio-feature.c
@@ -1,17 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Apple Onboard Audio feature call GPIO control
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
*
- * GPL v2, can be found in COPYING.
- *
* This file contains the GPIO control routines for
* direct (through feature calls) access to the GPIO
* registers.
*/
-#include <asm/pmac_feature.h>
+#include <linux/of_irq.h>
#include <linux/interrupt.h>
+#include <asm/pmac_feature.h>
#include "../aoa.h"
/* TODO: these are lots of global variables
@@ -81,14 +81,17 @@ static struct device_node *get_gpio(char *name,
if (altname && (strcmp(audio_gpio, altname) == 0))
break;
}
+ of_node_put(gpio);
/* still not found, assume not there */
if (!np)
return NULL;
}
reg = of_get_property(np, "reg", NULL);
- if (!reg)
+ if (!reg) {
+ of_node_put(np);
return NULL;
+ }
*gpioptr = *reg;
@@ -117,7 +120,7 @@ static void get_irq(struct device_node * np, int *irqptr)
if (np)
*irqptr = irq_of_parse_and_map(np, 0);
else
- *irqptr = NO_IRQ;
+ *irqptr = 0;
}
/* 0x4 is outenable, 0x1 is out, thus 4 or 5 */
@@ -209,10 +212,9 @@ static void ftr_handle_notify(struct work_struct *work)
struct gpio_notification *notif =
container_of(work, struct gpio_notification, work.work);
- mutex_lock(&notif->mutex);
+ guard(mutex)(&notif->mutex);
if (notif->notify)
notif->notify(notif->data);
- mutex_unlock(&notif->mutex);
}
static void gpio_enable_dual_edge(int gpio)
@@ -287,10 +289,9 @@ static void ftr_gpio_exit(struct gpio_runtime *rt)
free_irq(linein_detect_irq, &rt->line_in_notify);
if (rt->line_out_notify.gpio_private)
free_irq(lineout_detect_irq, &rt->line_out_notify);
- cancel_delayed_work(&rt->headphone_notify.work);
- cancel_delayed_work(&rt->line_in_notify.work);
- cancel_delayed_work(&rt->line_out_notify.work);
- flush_scheduled_work();
+ cancel_delayed_work_sync(&rt->headphone_notify.work);
+ cancel_delayed_work_sync(&rt->line_in_notify.work);
+ cancel_delayed_work_sync(&rt->line_out_notify.work);
mutex_destroy(&rt->headphone_notify.mutex);
mutex_destroy(&rt->line_in_notify.mutex);
mutex_destroy(&rt->line_out_notify.mutex);
@@ -336,22 +337,20 @@ static int ftr_set_notify(struct gpio_runtime *rt,
return -EINVAL;
}
- if (irq == NO_IRQ)
+ if (!irq)
return -ENODEV;
- mutex_lock(&notif->mutex);
+ guard(mutex)(&notif->mutex);
old = notif->notify;
- if (!old && !notify) {
- err = 0;
- goto out_unlock;
- }
+ if (!old && !notify)
+ return 0;
if (old && notify) {
if (old == notify && notif->data == data)
err = 0;
- goto out_unlock;
+ return err;
}
if (old && !notify)
@@ -360,16 +359,13 @@ static int ftr_set_notify(struct gpio_runtime *rt,
if (!old && notify) {
err = request_irq(irq, ftr_handle_notify_irq, 0, name, notif);
if (err)
- goto out_unlock;
+ return err;
}
notif->notify = notify;
notif->data = data;
- err = 0;
- out_unlock:
- mutex_unlock(&notif->mutex);
- return err;
+ return 0;
}
static int ftr_get_detect(struct gpio_runtime *rt,
diff --git a/sound/aoa/core/gpio-pmf.c b/sound/aoa/core/gpio-pmf.c
index 7e267c9379bc..e76bde25e41a 100644
--- a/sound/aoa/core/gpio-pmf.c
+++ b/sound/aoa/core/gpio-pmf.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Apple Onboard Audio pmf GPIOs
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#include <linux/slab.h>
@@ -75,10 +74,9 @@ static void pmf_handle_notify(struct work_struct *work)
struct gpio_notification *notif =
container_of(work, struct gpio_notification, work.work);
- mutex_lock(&notif->mutex);
+ guard(mutex)(&notif->mutex);
if (notif->notify)
notif->notify(notif->data);
- mutex_unlock(&notif->mutex);
}
static void pmf_gpio_init(struct gpio_runtime *rt)
@@ -107,10 +105,9 @@ static void pmf_gpio_exit(struct gpio_runtime *rt)
/* make sure no work is pending before freeing
* all things */
- cancel_delayed_work(&rt->headphone_notify.work);
- cancel_delayed_work(&rt->line_in_notify.work);
- cancel_delayed_work(&rt->line_out_notify.work);
- flush_scheduled_work();
+ cancel_delayed_work_sync(&rt->headphone_notify.work);
+ cancel_delayed_work_sync(&rt->line_in_notify.work);
+ cancel_delayed_work_sync(&rt->line_out_notify.work);
mutex_destroy(&rt->headphone_notify.mutex);
mutex_destroy(&rt->line_in_notify.mutex);
@@ -156,19 +153,17 @@ static int pmf_set_notify(struct gpio_runtime *rt,
return -EINVAL;
}
- mutex_lock(&notif->mutex);
+ guard(mutex)(&notif->mutex);
old = notif->notify;
- if (!old && !notify) {
- err = 0;
- goto out_unlock;
- }
+ if (!old && !notify)
+ return 0;
if (old && notify) {
if (old == notify && notif->data == data)
err = 0;
- goto out_unlock;
+ return err;
}
if (old && !notify) {
@@ -180,10 +175,8 @@ static int pmf_set_notify(struct gpio_runtime *rt,
if (!old && notify) {
irq_client = kzalloc(sizeof(struct pmf_irq_client),
GFP_KERNEL);
- if (!irq_client) {
- err = -ENOMEM;
- goto out_unlock;
- }
+ if (!irq_client)
+ return -ENOMEM;
irq_client->data = notif;
irq_client->handler = pmf_handle_notify_irq;
irq_client->owner = THIS_MODULE;
@@ -194,17 +187,14 @@ static int pmf_set_notify(struct gpio_runtime *rt,
printk(KERN_ERR "snd-aoa: gpio layer failed to"
" register %s irq (%d)\n", name, err);
kfree(irq_client);
- goto out_unlock;
+ return err;
}
notif->gpio_private = irq_client;
}
notif->notify = notify;
notif->data = data;
- err = 0;
- out_unlock:
- mutex_unlock(&notif->mutex);
- return err;
+ return 0;
}
static int pmf_get_detect(struct gpio_runtime *rt,
diff --git a/sound/aoa/fabrics/Kconfig b/sound/aoa/fabrics/Kconfig
index 3ca475a886b1..0fa2da247ad2 100644
--- a/sound/aoa/fabrics/Kconfig
+++ b/sound/aoa/fabrics/Kconfig
@@ -1,8 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SND_AOA_FABRIC_LAYOUT
tristate "layout-id fabric"
select SND_AOA_SOUNDBUS
select SND_AOA_SOUNDBUS_I2S
- ---help---
+ help
This enables the layout-id fabric for the Apple Onboard
Audio driver, the module holding it all together
based on the device-tree's layout-id property.
diff --git a/sound/aoa/fabrics/Makefile b/sound/aoa/fabrics/Makefile
index da37c10eca51..2c3bee6cfa2c 100644
--- a/sound/aoa/fabrics/Makefile
+++ b/sound/aoa/fabrics/Makefile
@@ -1,3 +1,4 @@
-snd-aoa-fabric-layout-objs += layout.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-aoa-fabric-layout-y += layout.o
obj-$(CONFIG_SND_AOA_FABRIC_LAYOUT) += snd-aoa-fabric-layout.o
diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c
index 3fd1a7e24928..bb2a0ef3004b 100644
--- a/sound/aoa/fabrics/layout.c
+++ b/sound/aoa/fabrics/layout.c
@@ -1,17 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Apple Onboard Audio driver -- layout/machine id fabric
*
* Copyright 2006-2008 Johannes Berg <johannes@sipsolutions.net>
*
- * GPL v2, can be found in COPYING.
- *
- *
* This fabric module looks for sound codecs based on the
* layout-id or device-id property in the device tree.
*/
-#include <asm/prom.h>
#include <linux/list.h>
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
#include "../aoa.h"
#include "../soundbus/soundbus.h"
@@ -112,7 +111,9 @@ MODULE_ALIAS("sound-layout-100");
MODULE_ALIAS("aoa-device-id-14");
MODULE_ALIAS("aoa-device-id-22");
+MODULE_ALIAS("aoa-device-id-31");
MODULE_ALIAS("aoa-device-id-35");
+MODULE_ALIAS("aoa-device-id-44");
/* onyx with all but microphone connected */
static struct codec_connection onyx_connections_nomic[] = {
@@ -361,6 +362,20 @@ static struct layout layouts[] = {
.connections = tas_connections_nolineout,
},
},
+ /* PowerBook6,1 */
+ { .device_id = 31,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_nolineout,
+ },
+ },
+ /* PowerBook6,5 */
+ { .device_id = 44,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_all,
+ },
+ },
/* PowerBook6,7 */
{ .layout_id = 80,
.codecs[0] = {
@@ -636,12 +651,12 @@ static int n##_control_put(struct snd_kcontrol *kcontrol, \
struct snd_ctl_elem_value *ucontrol) \
{ \
struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \
- if (gpio->methods && gpio->methods->get_##n) \
+ if (gpio->methods && gpio->methods->set_##n) \
gpio->methods->set_##n(gpio, \
!!ucontrol->value.integer.value[0]); \
return 1; \
} \
-static struct snd_kcontrol_new n##_ctl = { \
+static const struct snd_kcontrol_new n##_ctl = { \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = description, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
@@ -691,7 +706,7 @@ static int detect_choice_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static struct snd_kcontrol_new headphone_detect_choice = {
+static const struct snd_kcontrol_new headphone_detect_choice = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Headphone Detect Autoswitch",
.info = control_info,
@@ -701,7 +716,7 @@ static struct snd_kcontrol_new headphone_detect_choice = {
.private_value = 0,
};
-static struct snd_kcontrol_new lineout_detect_choice = {
+static const struct snd_kcontrol_new lineout_detect_choice = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line-Out Detect Autoswitch",
.info = control_info,
@@ -733,7 +748,7 @@ static int detected_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new headphone_detected = {
+static const struct snd_kcontrol_new headphone_detected = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Headphone Detected",
.info = control_info,
@@ -742,7 +757,7 @@ static struct snd_kcontrol_new headphone_detected = {
.private_value = 0,
};
-static struct snd_kcontrol_new lineout_detected = {
+static const struct snd_kcontrol_new lineout_detected = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line-Out Detected",
.info = control_info,
@@ -760,7 +775,7 @@ static int check_codec(struct aoa_codec *codec,
struct codec_connection *cc;
/* if the codec has a 'codec' node, we require a reference */
- if (codec->node && (strcmp(codec->node->name, "codec") == 0)) {
+ if (of_node_name_eq(codec->node, "codec")) {
snprintf(propname, sizeof(propname),
"platform-%s-codec-ref", codec->name);
ref = of_get_property(ldev->sound, propname, NULL);
@@ -934,8 +949,7 @@ static void layout_attached_codec(struct aoa_codec *codec)
ldev->gpio.methods->set_lineout(codec->gpio, 1);
ctl = snd_ctl_new1(&lineout_ctl, codec->gpio);
if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
- strlcpy(ctl->id.name,
- "Headphone Switch", sizeof(ctl->id.name));
+ strscpy(ctl->id.name, "Headphone Switch");
ldev->lineout_ctrl = ctl;
aoa_snd_ctl_add(ctl);
ldev->have_lineout_detect =
@@ -948,16 +962,14 @@ static void layout_attached_codec(struct aoa_codec *codec)
ctl = snd_ctl_new1(&lineout_detect_choice,
ldev);
if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
- strlcpy(ctl->id.name,
- "Headphone Detect Autoswitch",
- sizeof(ctl->id.name));
+ strscpy(ctl->id.name,
+ "Headphone Detect Autoswitch");
aoa_snd_ctl_add(ctl);
ctl = snd_ctl_new1(&lineout_detected,
ldev);
if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
- strlcpy(ctl->id.name,
- "Headphone Detected",
- sizeof(ctl->id.name));
+ strscpy(ctl->id.name,
+ "Headphone Detected");
ldev->lineout_detected_ctrl = ctl;
aoa_snd_ctl_add(ctl);
}
@@ -992,8 +1004,8 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
return -ENODEV;
/* by breaking out we keep a reference */
- while ((sound = of_get_next_child(sdev->ofdev.dev.of_node, sound))) {
- if (sound->type && strcasecmp(sound->type, "soundchip") == 0)
+ for_each_child_of_node(sdev->ofdev.dev.of_node, sound) {
+ if (of_node_is_type(sound, "soundchip"))
break;
}
if (!sound)
@@ -1073,14 +1085,14 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
sdev->pcmid = -1;
list_del(&ldev->list);
layouts_list_items--;
+ kfree(ldev);
outnodev:
of_node_put(sound);
layout_device = NULL;
- kfree(ldev);
return -ENODEV;
}
-static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
+static void aoa_fabric_layout_remove(struct soundbus_dev *sdev)
{
struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
int i;
@@ -1109,13 +1121,11 @@ static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
kfree(ldev);
sdev->pcmid = -1;
sdev->pcmname = NULL;
- return 0;
}
-#ifdef CONFIG_PM
-static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t state)
+static int aoa_fabric_layout_suspend(struct device *dev)
{
- struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
+ struct layout_dev *ldev = dev_get_drvdata(dev);
if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
ldev->gpio.methods->all_amps_off(&ldev->gpio);
@@ -1123,39 +1133,33 @@ static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t sta
return 0;
}
-static int aoa_fabric_layout_resume(struct soundbus_dev *sdev)
+static int aoa_fabric_layout_resume(struct device *dev)
{
- struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
+ struct layout_dev *ldev = dev_get_drvdata(dev);
- if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
+ if (ldev->gpio.methods && ldev->gpio.methods->all_amps_restore)
ldev->gpio.methods->all_amps_restore(&ldev->gpio);
return 0;
}
-#endif
+
+static DEFINE_SIMPLE_DEV_PM_OPS(aoa_fabric_layout_pm_ops,
+ aoa_fabric_layout_suspend, aoa_fabric_layout_resume);
static struct soundbus_driver aoa_soundbus_driver = {
.name = "snd_aoa_soundbus_drv",
.owner = THIS_MODULE,
.probe = aoa_fabric_layout_probe,
.remove = aoa_fabric_layout_remove,
-#ifdef CONFIG_PM
- .suspend = aoa_fabric_layout_suspend,
- .resume = aoa_fabric_layout_resume,
-#endif
.driver = {
.owner = THIS_MODULE,
+ .pm = &aoa_fabric_layout_pm_ops,
}
};
static int __init aoa_fabric_layout_init(void)
{
- int err;
-
- err = soundbus_register_driver(&aoa_soundbus_driver);
- if (err)
- return err;
- return 0;
+ return soundbus_register_driver(&aoa_soundbus_driver);
}
static void __exit aoa_fabric_layout_exit(void)
diff --git a/sound/aoa/soundbus/Kconfig b/sound/aoa/soundbus/Kconfig
index 839d1137b9b2..00189aac8014 100644
--- a/sound/aoa/soundbus/Kconfig
+++ b/sound/aoa/soundbus/Kconfig
@@ -1,7 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SND_AOA_SOUNDBUS
tristate "Apple Soundbus support"
select SND_PCM
- ---help---
+ help
This option enables the generic driver for the soundbus
support on Apple machines.
@@ -10,5 +11,5 @@ config SND_AOA_SOUNDBUS
config SND_AOA_SOUNDBUS_I2S
tristate "I2S bus support"
depends on SND_AOA_SOUNDBUS && PCI
- ---help---
+ help
This option enables support for Apple I2S busses.
diff --git a/sound/aoa/soundbus/Makefile b/sound/aoa/soundbus/Makefile
index 0e61f5aa06b5..a10b102daf81 100644
--- a/sound/aoa/soundbus/Makefile
+++ b/sound/aoa/soundbus/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SND_AOA_SOUNDBUS) += snd-aoa-soundbus.o
-snd-aoa-soundbus-objs := core.o sysfs.o
+snd-aoa-soundbus-y := core.o sysfs.o
obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += i2sbus/
diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c
index 7487eb76e034..2a295f610594 100644
--- a/sound/aoa/soundbus/core.c
+++ b/sound/aoa/soundbus/core.c
@@ -1,12 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* soundbus
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
#include "soundbus.h"
MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
@@ -56,10 +57,10 @@ static int soundbus_probe(struct device *dev)
}
-static int soundbus_uevent(struct device *dev, struct kobj_uevent_env *env)
+static int soundbus_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
- struct soundbus_dev * soundbus_dev;
- struct platform_device * of;
+ const struct soundbus_dev * soundbus_dev;
+ const struct platform_device * of;
const char *compat;
int retval = 0;
int cplen, seen = 0;
@@ -74,11 +75,11 @@ static int soundbus_uevent(struct device *dev, struct kobj_uevent_env *env)
of = &soundbus_dev->ofdev;
/* stuff we want to pass to /sbin/hotplug */
- retval = add_uevent_var(env, "OF_NAME=%s", of->dev.of_node->name);
+ retval = add_uevent_var(env, "OF_NAME=%pOFn", of->dev.of_node);
if (retval)
return retval;
- retval = add_uevent_var(env, "OF_TYPE=%s", of->dev.of_node->type);
+ retval = add_uevent_var(env, "OF_TYPE=%s", of_node_get_device_type(of->dev.of_node));
if (retval)
return retval;
@@ -105,7 +106,7 @@ static int soundbus_uevent(struct device *dev, struct kobj_uevent_env *env)
return retval;
}
-static int soundbus_device_remove(struct device *dev)
+static void soundbus_device_remove(struct device *dev)
{
struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
@@ -113,8 +114,6 @@ static int soundbus_device_remove(struct device *dev)
if (dev->driver && drv->remove)
drv->remove(soundbus_dev);
soundbus_dev_put(soundbus_dev);
-
- return 0;
}
static void soundbus_device_shutdown(struct device *dev)
@@ -126,41 +125,15 @@ static void soundbus_device_shutdown(struct device *dev)
drv->shutdown(soundbus_dev);
}
-#ifdef CONFIG_PM
-
-static int soundbus_device_suspend(struct device *dev, pm_message_t state)
-{
- struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
- struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
-
- if (dev->driver && drv->suspend)
- return drv->suspend(soundbus_dev, state);
- return 0;
-}
-
-static int soundbus_device_resume(struct device * dev)
-{
- struct soundbus_dev * soundbus_dev = to_soundbus_device(dev);
- struct soundbus_driver * drv = to_soundbus_driver(dev->driver);
-
- if (dev->driver && drv->resume)
- return drv->resume(soundbus_dev);
- return 0;
-}
-
-#endif /* CONFIG_PM */
-
-static struct bus_type soundbus_bus_type = {
+/* soundbus_dev_attrs is declared in sysfs.c */
+ATTRIBUTE_GROUPS(soundbus_dev);
+static const struct bus_type soundbus_bus_type = {
.name = "aoa-soundbus",
.probe = soundbus_probe,
.uevent = soundbus_uevent,
.remove = soundbus_device_remove,
.shutdown = soundbus_device_shutdown,
-#ifdef CONFIG_PM
- .suspend = soundbus_device_suspend,
- .resume = soundbus_device_resume,
-#endif
- .dev_attrs = soundbus_dev_attrs,
+ .dev_groups = soundbus_dev_groups,
};
int soundbus_add_one(struct soundbus_dev *dev)
diff --git a/sound/aoa/soundbus/i2sbus/Makefile b/sound/aoa/soundbus/i2sbus/Makefile
index 1b949b2a4028..1ddaa0e17d67 100644
--- a/sound/aoa/soundbus/i2sbus/Makefile
+++ b/sound/aoa/soundbus/i2sbus/Makefile
@@ -1,2 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += snd-aoa-i2sbus.o
-snd-aoa-i2sbus-objs := core.o pcm.o control.o
+snd-aoa-i2sbus-y := core.o pcm.o control.o
diff --git a/sound/aoa/soundbus/i2sbus/control.c b/sound/aoa/soundbus/i2sbus/control.c
index 4dc9b49c02cf..a003ef06de63 100644
--- a/sound/aoa/soundbus/i2sbus/control.c
+++ b/sound/aoa/soundbus/i2sbus/control.c
@@ -1,17 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* i2sbus driver -- bus control routines
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/io.h>
-#include <asm/io.h>
-#include <asm/prom.h>
#include <asm/macio.h>
#include <asm/pmac_feature.h>
#include <asm/pmac_pfunc.h>
diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c
index 3ff8cc5f487a..f4d43c854bbd 100644
--- a/sound/aoa/soundbus/i2sbus/core.c
+++ b/sound/aoa/soundbus/i2sbus/core.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* i2sbus driver
*
* Copyright 2006-2008 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#include <linux/module.h>
@@ -11,6 +10,9 @@
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <sound/core.h>
@@ -29,7 +31,7 @@ module_param(force, int, 0444);
MODULE_PARM_DESC(force, "Force loading i2sbus even when"
" no layout-id property is present");
-static struct of_device_id i2sbus_match[] = {
+static const struct of_device_id i2sbus_match[] = {
{ .name = "i2s" },
{ }
};
@@ -45,15 +47,11 @@ static int alloc_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev,
/* We use the PCI APIs for now until the generic one gets fixed
* enough or until we get some macio-specific versions
*/
- r->space = dma_alloc_coherent(
- &macio_get_pci_dev(i2sdev->macio)->dev,
- r->size,
- &r->bus_addr,
- GFP_KERNEL);
-
- if (!r->space) return -ENOMEM;
+ r->space = dma_alloc_coherent(&macio_get_pci_dev(i2sdev->macio)->dev,
+ r->size, &r->bus_addr, GFP_KERNEL);
+ if (!r->space)
+ return -ENOMEM;
- memset(r->space, 0, r->size);
r->cmds = (void*)DBDMA_ALIGN(r->space);
r->bus_cmd_start = r->bus_addr +
(dma_addr_t)((char*)r->cmds - (char*)r->space);
@@ -76,13 +74,11 @@ static void i2sbus_release_dev(struct device *dev)
int i;
i2sdev = container_of(dev, struct i2sbus_dev, sound.ofdev.dev);
-
- if (i2sdev->intfregs) iounmap(i2sdev->intfregs);
- if (i2sdev->out.dbdma) iounmap(i2sdev->out.dbdma);
- if (i2sdev->in.dbdma) iounmap(i2sdev->in.dbdma);
+ iounmap(i2sdev->intfregs);
+ iounmap(i2sdev->out.dbdma);
+ iounmap(i2sdev->in.dbdma);
for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++)
- if (i2sdev->allocated_resource[i])
- release_and_free_resource(i2sdev->allocated_resource[i]);
+ release_and_free_resource(i2sdev->allocated_resource[i]);
free_dbdma_descriptor_ring(i2sdev, &i2sdev->out.dbdma_ring);
free_dbdma_descriptor_ring(i2sdev, &i2sdev->in.dbdma_ring);
for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++)
@@ -97,14 +93,12 @@ static irqreturn_t i2sbus_bus_intr(int irq, void *devid)
struct i2sbus_dev *dev = devid;
u32 intreg;
- spin_lock(&dev->low_lock);
+ guard(spinlock)(&dev->low_lock);
intreg = in_le32(&dev->intfregs->intr_ctl);
/* acknowledge interrupt reasons */
out_le32(&dev->intfregs->intr_ctl, intreg);
- spin_unlock(&dev->low_lock);
-
return IRQ_HANDLED;
}
@@ -152,27 +146,29 @@ static int i2sbus_get_and_fixup_rsrc(struct device_node *np, int index,
return rc;
}
+/* Returns 1 if added, 0 for otherwise; don't return a negative value! */
/* FIXME: look at device node refcounting */
static int i2sbus_add_dev(struct macio_dev *macio,
struct i2sbus_control *control,
struct device_node *np)
{
struct i2sbus_dev *dev;
- struct device_node *child = NULL, *sound = NULL;
+ struct device_node *child, *sound = NULL;
struct resource *r;
int i, layout = 0, rlen, ok = force;
- static const char *rnames[] = { "i2sbus: %s (control)",
- "i2sbus: %s (tx)",
- "i2sbus: %s (rx)" };
- static irq_handler_t ints[] = {
+ char node_name[8];
+ static const char *rnames[] = { "i2sbus: %pOFn (control)",
+ "i2sbus: %pOFn (tx)",
+ "i2sbus: %pOFn (rx)" };
+ static const irq_handler_t ints[] = {
i2sbus_bus_intr,
i2sbus_tx_intr,
i2sbus_rx_intr
};
- if (strlen(np->name) != 5)
+ if (snprintf(node_name, sizeof(node_name), "%pOFn", np) != 5)
return 0;
- if (strncmp(np->name, "i2s-", 4))
+ if (strncmp(node_name, "i2s-", 4))
return 0;
dev = kzalloc(sizeof(struct i2sbus_dev), GFP_KERNEL);
@@ -180,8 +176,8 @@ static int i2sbus_add_dev(struct macio_dev *macio,
return 0;
i = 0;
- while ((child = of_get_next_child(np, child))) {
- if (strcmp(child->name, "sound") == 0) {
+ for_each_child_of_node(np, child) {
+ if (of_node_name_eq(child, "sound")) {
i++;
sound = child;
}
@@ -200,7 +196,8 @@ static int i2sbus_add_dev(struct macio_dev *macio,
* We probably cannot handle all device-id machines,
* so restrict to those we do handle for now.
*/
- if (id && (*id == 22 || *id == 14 || *id == 35)) {
+ if (id && (*id == 22 || *id == 14 || *id == 35 ||
+ *id == 31 || *id == 44)) {
snprintf(dev->sound.modalias, 32,
"aoa-device-id-%d", *id);
ok = 1;
@@ -216,7 +213,7 @@ static int i2sbus_add_dev(struct macio_dev *macio,
* either as the second one in that case is just a modem. */
if (!ok) {
kfree(dev);
- return -ENODEV;
+ return 0;
}
mutex_init(&dev->lock);
@@ -231,13 +228,13 @@ static int i2sbus_add_dev(struct macio_dev *macio,
dev->sound.pcmid = -1;
dev->macio = macio;
dev->control = control;
- dev->bus_number = np->name[4] - 'a';
+ dev->bus_number = node_name[4] - 'a';
INIT_LIST_HEAD(&dev->sound.codec_list);
for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
dev->interrupts[i] = -1;
snprintf(dev->rnames[i], sizeof(dev->rnames[i]),
- rnames[i], np->name);
+ rnames[i], np);
}
for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) {
int irq = irq_of_parse_and_map(np, i);
@@ -262,8 +259,7 @@ static int i2sbus_add_dev(struct macio_dev *macio,
*/
dev->allocated_resource[i] =
request_mem_region(dev->resources[i].start,
- dev->resources[i].end -
- dev->resources[i].start + 1,
+ resource_size(&dev->resources[i]),
dev->rnames[i]);
if (!dev->allocated_resource[i]) {
printk(KERN_ERR "i2sbus: failed to claim resource %d!\n", i);
@@ -272,19 +268,19 @@ static int i2sbus_add_dev(struct macio_dev *macio,
}
r = &dev->resources[aoa_resource_i2smmio];
- rlen = r->end - r->start + 1;
+ rlen = resource_size(r);
if (rlen < sizeof(struct i2s_interface_regs))
goto err;
dev->intfregs = ioremap(r->start, rlen);
r = &dev->resources[aoa_resource_txdbdma];
- rlen = r->end - r->start + 1;
+ rlen = resource_size(r);
if (rlen < sizeof(struct dbdma_regs))
goto err;
dev->out.dbdma = ioremap(r->start, rlen);
r = &dev->resources[aoa_resource_rxdbdma];
- rlen = r->end - r->start + 1;
+ rlen = resource_size(r);
if (rlen < sizeof(struct dbdma_regs))
goto err;
dev->in.dbdma = ioremap(r->start, rlen);
@@ -306,6 +302,10 @@ static int i2sbus_add_dev(struct macio_dev *macio,
if (soundbus_add_one(&dev->sound)) {
printk(KERN_DEBUG "i2sbus: device registration error!\n");
+ if (dev->sound.ofdev.dev.kobj.state_initialized) {
+ soundbus_dev_put(&dev->sound);
+ return 0;
+ }
goto err;
}
@@ -321,12 +321,11 @@ static int i2sbus_add_dev(struct macio_dev *macio,
free_irq(dev->interrupts[i], dev);
free_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring);
free_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring);
- if (dev->intfregs) iounmap(dev->intfregs);
- if (dev->out.dbdma) iounmap(dev->out.dbdma);
- if (dev->in.dbdma) iounmap(dev->in.dbdma);
+ iounmap(dev->intfregs);
+ iounmap(dev->out.dbdma);
+ iounmap(dev->in.dbdma);
for (i=0;i<3;i++)
- if (dev->allocated_resource[i])
- release_and_free_resource(dev->allocated_resource[i]);
+ release_and_free_resource(dev->allocated_resource[i]);
mutex_destroy(&dev->lock);
kfree(dev);
return 0;
@@ -334,7 +333,7 @@ static int i2sbus_add_dev(struct macio_dev *macio,
static int i2sbus_probe(struct macio_dev* dev, const struct of_device_id *match)
{
- struct device_node *np = NULL;
+ struct device_node *np;
int got = 0, err;
struct i2sbus_control *control = NULL;
@@ -346,7 +345,7 @@ static int i2sbus_probe(struct macio_dev* dev, const struct of_device_id *match)
return -ENODEV;
}
- while ((np = of_get_next_child(dev->ofdev.dev.of_node, np))) {
+ for_each_child_of_node(dev->ofdev.dev.of_node, np) {
if (of_device_is_compatible(np, "i2sbus") ||
of_device_is_compatible(np, "i2s-modem")) {
got += i2sbus_add_dev(dev, control, np);
@@ -364,15 +363,13 @@ static int i2sbus_probe(struct macio_dev* dev, const struct of_device_id *match)
return 0;
}
-static int i2sbus_remove(struct macio_dev* dev)
+static void i2sbus_remove(struct macio_dev *dev)
{
struct i2sbus_control *control = dev_get_drvdata(&dev->ofdev.dev);
struct i2sbus_dev *i2sdev, *tmp;
list_for_each_entry_safe(i2sdev, tmp, &control->list, item)
soundbus_remove_one(&i2sdev->sound);
-
- return 0;
}
#ifdef CONFIG_PM
@@ -384,12 +381,6 @@ static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state)
int err, ret = 0;
list_for_each_entry(i2sdev, &control->list, item) {
- /* Notify Alsa */
- if (i2sdev->sound.pcm) {
- /* Suspend PCM streams */
- snd_pcm_suspend_all(i2sdev->sound.pcm);
- }
-
/* Notify codecs */
list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
err = 0;
diff --git a/sound/aoa/soundbus/i2sbus/i2sbus.h b/sound/aoa/soundbus/i2sbus/i2sbus.h
index befefd99e271..7a3cae0d6c26 100644
--- a/sound/aoa/soundbus/i2sbus/i2sbus.h
+++ b/sound/aoa/soundbus/i2sbus/i2sbus.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* i2sbus driver -- private definitions
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#ifndef __I2SBUS_H
#define __I2SBUS_H
@@ -14,7 +13,6 @@
#include <sound/pcm.h>
-#include <asm/prom.h>
#include <asm/pmac_feature.h>
#include <asm/dbdma.h>
diff --git a/sound/aoa/soundbus/i2sbus/interface.h b/sound/aoa/soundbus/i2sbus/interface.h
index c6b5f5452d20..a136274266ea 100644
--- a/sound/aoa/soundbus/i2sbus/interface.h
+++ b/sound/aoa/soundbus/i2sbus/interface.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* i2sbus driver -- interface register definitions
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#ifndef __I2SBUS_INTERFACE_H
#define __I2SBUS_INTERFACE_H
@@ -35,7 +34,7 @@ struct i2s_interface_regs {
__le32 peak_level_in1; /* 0x90 */
PAD(12);
/* total size: 0x100 bytes */
-} __attribute__((__packed__));
+} __packed;
/* interrupt register is just a bitfield with
* interrupt enable and pending bits */
diff --git a/sound/aoa/soundbus/i2sbus/pcm.c b/sound/aoa/soundbus/i2sbus/pcm.c
index be838993926d..4c480ad2c05d 100644
--- a/sound/aoa/soundbus/i2sbus/pcm.c
+++ b/sound/aoa/soundbus/i2sbus/pcm.c
@@ -1,17 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* i2sbus driver -- pcm routines
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <asm/macio.h>
#include <linux/pci.h>
+#include <linux/module.h>
#include "../soundbus.h"
#include "i2sbus.h"
@@ -79,11 +79,10 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
u64 formats = 0;
unsigned int rates = 0;
struct transfer_info v;
- int result = 0;
int bus_factor = 0, sysclock_factor = 0;
int found_this;
- mutex_lock(&i2sdev->lock);
+ guard(mutex)(&i2sdev->lock);
get_pcm_info(i2sdev, in, &pi, &other);
@@ -92,8 +91,7 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
if (pi->active) {
/* alsa messed up */
- result = -EBUSY;
- goto out_unlock;
+ return -EBUSY;
}
/* we now need to assign the hw */
@@ -117,10 +115,8 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
ti++;
}
}
- if (!masks_inited || !bus_factor || !sysclock_factor) {
- result = -ENODEV;
- goto out_unlock;
- }
+ if (!masks_inited || !bus_factor || !sysclock_factor)
+ return -ENODEV;
/* bus dependent stuff */
hw->info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME |
@@ -178,7 +174,7 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
*/
if (other->active) {
/* FIXME: is this guaranteed by the alsa api? */
- hw->formats &= (1ULL << i2sdev->format);
+ hw->formats &= pcm_format_to_bits(i2sdev->format);
/* see above, restrict rates to the one we already have */
hw->rate_min = i2sdev->rate;
hw->rate_max = i2sdev->rate;
@@ -194,15 +190,12 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
hw->periods_max = MAX_DBDMA_COMMANDS;
err = snd_pcm_hw_constraint_integer(pi->substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
- if (err < 0) {
- result = err;
- goto out_unlock;
- }
+ if (err < 0)
+ return err;
list_for_each_entry(cii, &sdev->codec_list, list) {
if (cii->codec->open) {
err = cii->codec->open(cii, pi->substream);
if (err) {
- result = err;
/* unwind */
found_this = 0;
list_for_each_entry_reverse(rev,
@@ -214,14 +207,12 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in)
if (rev == cii)
found_this = 1;
}
- goto out_unlock;
+ return err;
}
}
}
- out_unlock:
- mutex_unlock(&i2sdev->lock);
- return result;
+ return 0;
}
#undef CHECK_RATE
@@ -232,7 +223,7 @@ static int i2sbus_pcm_close(struct i2sbus_dev *i2sdev, int in)
struct pcm_info *pi;
int err = 0, tmp;
- mutex_lock(&i2sdev->lock);
+ guard(mutex)(&i2sdev->lock);
get_pcm_info(i2sdev, in, &pi, NULL);
@@ -246,7 +237,6 @@ static int i2sbus_pcm_close(struct i2sbus_dev *i2sdev, int in)
pi->substream = NULL;
pi->active = 0;
- mutex_unlock(&i2sdev->lock);
return err;
}
@@ -254,26 +244,25 @@ static void i2sbus_wait_for_stop(struct i2sbus_dev *i2sdev,
struct pcm_info *pi)
{
unsigned long flags;
- struct completion done;
- long timeout;
+ DECLARE_COMPLETION_ONSTACK(done);
+ unsigned long time_left;
spin_lock_irqsave(&i2sdev->low_lock, flags);
if (pi->dbdma_ring.stopping) {
- init_completion(&done);
pi->stop_completion = &done;
spin_unlock_irqrestore(&i2sdev->low_lock, flags);
- timeout = wait_for_completion_timeout(&done, HZ);
+ time_left = wait_for_completion_timeout(&done, HZ);
spin_lock_irqsave(&i2sdev->low_lock, flags);
pi->stop_completion = NULL;
- if (timeout == 0) {
+ if (time_left == 0) {
/* timeout expired, stop dbdma forcefully */
printk(KERN_ERR "i2sbus_wait_for_stop: timed out\n");
/* make sure RUN, PAUSE and S0 bits are cleared */
out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16);
pi->dbdma_ring.stopping = 0;
- timeout = 10;
+ time_left = 10;
while (in_le32(&pi->dbdma->status) & ACTIVE) {
- if (--timeout <= 0)
+ if (--time_left <= 0)
break;
udelay(1);
}
@@ -294,12 +283,6 @@ void i2sbus_wait_for_stop_both(struct i2sbus_dev *i2sdev)
}
#endif
-static int i2sbus_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
-}
-
static inline int i2sbus_hw_free(struct snd_pcm_substream *substream, int in)
{
struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream);
@@ -308,7 +291,6 @@ static inline int i2sbus_hw_free(struct snd_pcm_substream *substream, int in)
get_pcm_info(i2sdev, in, &pi, NULL);
if (pi->dbdma_ring.stopping)
i2sbus_wait_for_stop(i2sdev, pi);
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -338,33 +320,26 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
int input_16bit;
struct pcm_info *pi, *other;
int cnt;
- int result = 0;
unsigned int cmd, stopaddr;
- mutex_lock(&i2sdev->lock);
+ guard(mutex)(&i2sdev->lock);
get_pcm_info(i2sdev, in, &pi, &other);
- if (pi->dbdma_ring.running) {
- result = -EBUSY;
- goto out_unlock;
- }
+ if (pi->dbdma_ring.running)
+ return -EBUSY;
if (pi->dbdma_ring.stopping)
i2sbus_wait_for_stop(i2sdev, pi);
- if (!pi->substream || !pi->substream->runtime) {
- result = -EINVAL;
- goto out_unlock;
- }
+ if (!pi->substream || !pi->substream->runtime)
+ return -EINVAL;
runtime = pi->substream->runtime;
pi->active = 1;
if (other->active &&
((i2sdev->format != runtime->format)
- || (i2sdev->rate != runtime->rate))) {
- result = -EINVAL;
- goto out_unlock;
- }
+ || (i2sdev->rate != runtime->rate)))
+ return -EINVAL;
i2sdev->format = runtime->format;
i2sdev->rate = runtime->rate;
@@ -420,10 +395,8 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
bi.bus_factor = cii->codec->bus_factor;
break;
}
- if (!bi.bus_factor) {
- result = -ENODEV;
- goto out_unlock;
- }
+ if (!bi.bus_factor)
+ return -ENODEV;
input_16bit = 1;
break;
case SNDRV_PCM_FORMAT_S32_BE:
@@ -434,8 +407,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
input_16bit = 0;
break;
default:
- result = -EINVAL;
- goto out_unlock;
+ return -EINVAL;
}
/* we assume all sysclocks are the same! */
list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
@@ -446,10 +418,8 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
if (clock_and_divisors(bi.sysclock_factor,
bi.bus_factor,
runtime->rate,
- &sfr) < 0) {
- result = -EINVAL;
- goto out_unlock;
- }
+ &sfr) < 0)
+ return -EINVAL;
switch (bi.bus_factor) {
case 32:
sfr |= I2S_SF_SERIAL_FORMAT_I2S_32X;
@@ -465,10 +435,8 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
int err = 0;
if (cii->codec->prepare)
err = cii->codec->prepare(cii, &bi, pi->substream);
- if (err) {
- result = err;
- goto out_unlock;
- }
+ if (err)
+ return err;
}
/* codecs are fine with it, so set our clocks */
if (input_16bit)
@@ -484,7 +452,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
/* not locking these is fine since we touch them only in this function */
if (in_le32(&i2sdev->intfregs->serial_format) == sfr
&& in_le32(&i2sdev->intfregs->data_word_sizes) == dws)
- goto out_unlock;
+ return 0;
/* let's notify the codecs about clocks going away.
* For now we only do mastering on the i2s cell... */
@@ -522,9 +490,7 @@ static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in)
if (cii->codec->switch_clock)
cii->codec->switch_clock(cii, CLOCK_SWITCH_SLAVE);
- out_unlock:
- mutex_unlock(&i2sdev->lock);
- return result;
+ return 0;
}
#ifdef CONFIG_PM
@@ -539,20 +505,16 @@ static int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd)
{
struct codec_info_item *cii;
struct pcm_info *pi;
- int result = 0;
- unsigned long flags;
- spin_lock_irqsave(&i2sdev->low_lock, flags);
+ guard(spinlock_irqsave)(&i2sdev->low_lock);
get_pcm_info(i2sdev, in, &pi, NULL);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
- if (pi->dbdma_ring.running) {
- result = -EALREADY;
- goto out_unlock;
- }
+ if (pi->dbdma_ring.running)
+ return -EALREADY;
list_for_each_entry(cii, &i2sdev->sound.codec_list, list)
if (cii->codec->start)
cii->codec->start(cii, pi->substream);
@@ -566,7 +528,7 @@ static int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd)
udelay(10);
if (in_le32(&pi->dbdma->status) & ACTIVE) {
pi->dbdma_ring.stopping = 0;
- goto out_unlock; /* keep running */
+ return 0; /* keep running */
}
}
}
@@ -592,10 +554,8 @@ static int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd)
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
- if (!pi->dbdma_ring.running) {
- result = -EALREADY;
- goto out_unlock;
- }
+ if (!pi->dbdma_ring.running)
+ return -EALREADY;
pi->dbdma_ring.running = 0;
/* Set the S0 bit to make the DMA branch to the stop cmd */
@@ -607,13 +567,10 @@ static int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd)
cii->codec->stop(cii, pi->substream);
break;
default:
- result = -EINVAL;
- goto out_unlock;
+ return -EINVAL;
}
- out_unlock:
- spin_unlock_irqrestore(&i2sdev->low_lock, flags);
- return result;
+ return 0;
}
static snd_pcm_uframes_t i2sbus_pcm_pointer(struct i2sbus_dev *i2sdev, int in)
@@ -640,70 +597,67 @@ static inline void handle_interrupt(struct i2sbus_dev *i2sdev, int in)
int dma_stopped = 0;
struct snd_pcm_runtime *runtime;
- spin_lock(&i2sdev->low_lock);
- get_pcm_info(i2sdev, in, &pi, NULL);
- if (!pi->dbdma_ring.running && !pi->dbdma_ring.stopping)
- goto out_unlock;
+ scoped_guard(spinlock, &i2sdev->low_lock) {
+ get_pcm_info(i2sdev, in, &pi, NULL);
+ if (!pi->dbdma_ring.running && !pi->dbdma_ring.stopping)
+ return;
+
+ i = pi->current_period;
+ runtime = pi->substream->runtime;
+ while (pi->dbdma_ring.cmds[i].xfer_status) {
+ if (le16_to_cpu(pi->dbdma_ring.cmds[i].xfer_status) & BT)
+ /*
+ * BT is the branch taken bit. If it took a branch
+ * it is because we set the S0 bit to make it
+ * branch to the stop command.
+ */
+ dma_stopped = 1;
+ pi->dbdma_ring.cmds[i].xfer_status = 0;
+
+ if (++i >= runtime->periods) {
+ i = 0;
+ pi->frame_count += runtime->buffer_size;
+ }
+ pi->current_period = i;
- i = pi->current_period;
- runtime = pi->substream->runtime;
- while (pi->dbdma_ring.cmds[i].xfer_status) {
- if (le16_to_cpu(pi->dbdma_ring.cmds[i].xfer_status) & BT)
/*
- * BT is the branch taken bit. If it took a branch
- * it is because we set the S0 bit to make it
- * branch to the stop command.
+ * Check the frame count. The DMA tends to get a bit
+ * ahead of the frame counter, which confuses the core.
*/
- dma_stopped = 1;
- pi->dbdma_ring.cmds[i].xfer_status = 0;
-
- if (++i >= runtime->periods) {
- i = 0;
- pi->frame_count += runtime->buffer_size;
+ fc = in_le32(&i2sdev->intfregs->frame_count);
+ nframes = i * runtime->period_size;
+ if (fc < pi->frame_count + nframes)
+ pi->frame_count = fc - nframes;
}
- pi->current_period = i;
-
- /*
- * Check the frame count. The DMA tends to get a bit
- * ahead of the frame counter, which confuses the core.
- */
- fc = in_le32(&i2sdev->intfregs->frame_count);
- nframes = i * runtime->period_size;
- if (fc < pi->frame_count + nframes)
- pi->frame_count = fc - nframes;
- }
- if (dma_stopped) {
- timeout = 1000;
- for (;;) {
- status = in_le32(&pi->dbdma->status);
- if (!(status & ACTIVE) && (!in || (status & 0x80)))
- break;
- if (--timeout <= 0) {
- printk(KERN_ERR "i2sbus: timed out "
- "waiting for DMA to stop!\n");
- break;
+ if (dma_stopped) {
+ timeout = 1000;
+ for (;;) {
+ status = in_le32(&pi->dbdma->status);
+ if (!(status & ACTIVE) && (!in || (status & 0x80)))
+ break;
+ if (--timeout <= 0) {
+ printk(KERN_ERR
+ "i2sbus: timed out waiting for DMA to stop!\n");
+ break;
+ }
+ udelay(1);
}
- udelay(1);
- }
- /* Turn off DMA controller, clear S0 bit */
- out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16);
+ /* Turn off DMA controller, clear S0 bit */
+ out_le32(&pi->dbdma->control, (RUN | PAUSE | 1) << 16);
- pi->dbdma_ring.stopping = 0;
- if (pi->stop_completion)
- complete(pi->stop_completion);
+ pi->dbdma_ring.stopping = 0;
+ if (pi->stop_completion)
+ complete(pi->stop_completion);
+ }
+
+ if (!pi->dbdma_ring.running)
+ return;
}
- if (!pi->dbdma_ring.running)
- goto out_unlock;
- spin_unlock(&i2sdev->low_lock);
/* may call _trigger again, hence needs to be unlocked */
snd_pcm_period_elapsed(pi->substream);
- return;
-
- out_unlock:
- spin_unlock(&i2sdev->low_lock);
}
irqreturn_t i2sbus_tx_intr(int irq, void *devid)
@@ -777,11 +731,9 @@ static snd_pcm_uframes_t i2sbus_playback_pointer(struct snd_pcm_substream
return i2sbus_pcm_pointer(i2sdev, 0);
}
-static struct snd_pcm_ops i2sbus_playback_ops = {
+static const struct snd_pcm_ops i2sbus_playback_ops = {
.open = i2sbus_playback_open,
.close = i2sbus_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = i2sbus_hw_params,
.hw_free = i2sbus_playback_hw_free,
.prepare = i2sbus_playback_prepare,
.trigger = i2sbus_playback_trigger,
@@ -847,11 +799,9 @@ static snd_pcm_uframes_t i2sbus_record_pointer(struct snd_pcm_substream
return i2sbus_pcm_pointer(i2sdev, 1);
}
-static struct snd_pcm_ops i2sbus_record_ops = {
+static const struct snd_pcm_ops i2sbus_record_ops = {
.open = i2sbus_record_open,
.close = i2sbus_record_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = i2sbus_hw_params,
.hw_free = i2sbus_record_hw_free,
.prepare = i2sbus_record_prepare,
.trigger = i2sbus_record_trigger,
@@ -930,10 +880,8 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
}
cii = kzalloc(sizeof(struct codec_info_item), GFP_KERNEL);
- if (!cii) {
- printk(KERN_DEBUG "i2sbus: failed to allocate cii\n");
+ if (!cii)
return -ENOMEM;
- }
/* use the private data to point to the codec info */
cii->sdev = soundbus_dev_get(dev);
@@ -967,7 +915,6 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
printk(KERN_DEBUG "i2sbus: failed to create pcm\n");
goto out_put_ci_module;
}
- dev->pcm->dev = &dev->ofdev.dev;
}
/* ALSA yet again sucks.
@@ -987,6 +934,8 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
goto out_put_ci_module;
snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK,
&i2sbus_playback_ops);
+ dev->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].dev->parent =
+ &dev->ofdev.dev;
i2sdev->out.created = 1;
}
@@ -1002,6 +951,8 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
goto out_put_ci_module;
snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE,
&i2sbus_record_ops);
+ dev->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].dev->parent =
+ &dev->ofdev.dev;
i2sdev->in.created = 1;
}
@@ -1023,9 +974,9 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
dev->pcm->private_free = i2sbus_private_free;
/* well, we really should support scatter/gather DMA */
- snd_pcm_lib_preallocate_pages_for_all(
+ snd_pcm_set_managed_buffer_all(
dev->pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(macio_get_pci_dev(i2sdev->macio)),
+ &macio_get_pci_dev(i2sdev->macio)->dev,
64 * 1024, 64 * 1024);
return 0;
diff --git a/sound/aoa/soundbus/soundbus.h b/sound/aoa/soundbus/soundbus.h
index adecbf36f4f6..877cbad93f12 100644
--- a/sound/aoa/soundbus/soundbus.h
+++ b/sound/aoa/soundbus/soundbus.h
@@ -1,14 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* soundbus generic definitions
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- *
- * GPL v2, can be found in COPYING.
*/
#ifndef __SOUNDBUS_H
#define __SOUNDBUS_H
-#include <linux/of_device.h>
+#include <linux/platform_device.h>
#include <sound/pcm.h>
#include <linux/list.h>
@@ -186,10 +185,8 @@ struct soundbus_driver {
/* we don't implement any matching at all */
int (*probe)(struct soundbus_dev* dev);
- int (*remove)(struct soundbus_dev* dev);
+ void (*remove)(struct soundbus_dev *dev);
- int (*suspend)(struct soundbus_dev* dev, pm_message_t state);
- int (*resume)(struct soundbus_dev* dev);
int (*shutdown)(struct soundbus_dev* dev);
struct device_driver driver;
@@ -199,6 +196,6 @@ struct soundbus_driver {
extern int soundbus_register_driver(struct soundbus_driver *drv);
extern void soundbus_unregister_driver(struct soundbus_driver *drv);
-extern struct device_attribute soundbus_dev_attrs[];
+extern struct attribute *soundbus_dev_attrs[];
#endif /* __SOUNDBUS_H */
diff --git a/sound/aoa/soundbus/sysfs.c b/sound/aoa/soundbus/sysfs.c
index e0980b5c2cd8..e87b28428b99 100644
--- a/sound/aoa/soundbus/sysfs.c
+++ b/sound/aoa/soundbus/sysfs.c
@@ -1,42 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
+#include <linux/of.h>
#include <linux/stat.h>
/* FIX UP */
#include "soundbus.h"
-#define soundbus_config_of_attr(field, format_string) \
-static ssize_t \
-field##_show (struct device *dev, struct device_attribute *attr, \
- char *buf) \
-{ \
- struct soundbus_dev *mdev = to_soundbus_device (dev); \
- return sprintf (buf, format_string, mdev->ofdev.dev.of_node->field); \
-}
-
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct soundbus_dev *sdev = to_soundbus_device(dev);
struct platform_device *of = &sdev->ofdev;
- int length;
- if (*sdev->modalias) {
- strlcpy(buf, sdev->modalias, sizeof(sdev->modalias) + 1);
- strcat(buf, "\n");
- length = strlen(buf);
- } else {
- length = sprintf(buf, "of:N%sT%s\n",
- of->dev.of_node->name, of->dev.of_node->type);
- }
+ if (*sdev->modalias)
+ return sysfs_emit(buf, "%s\n", sdev->modalias);
+ else
+ return sysfs_emit(buf, "of:N%pOFn%c%s\n",
+ of->dev.of_node, 'T',
+ of_node_get_device_type(of->dev.of_node));
+}
+static DEVICE_ATTR_RO(modalias);
+
+static ssize_t name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct soundbus_dev *sdev = to_soundbus_device(dev);
+ struct platform_device *of = &sdev->ofdev;
- return length;
+ return sysfs_emit(buf, "%pOFn\n", of->dev.of_node);
}
+static DEVICE_ATTR_RO(name);
+
+static ssize_t type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct soundbus_dev *sdev = to_soundbus_device(dev);
+ struct platform_device *of = &sdev->ofdev;
-soundbus_config_of_attr (name, "%s\n");
-soundbus_config_of_attr (type, "%s\n");
+ return sysfs_emit(buf, "%s\n", of_node_get_device_type(of->dev.of_node));
+}
+static DEVICE_ATTR_RO(type);
-struct device_attribute soundbus_dev_attrs[] = {
- __ATTR_RO(name),
- __ATTR_RO(type),
- __ATTR_RO(modalias),
- __ATTR_NULL
+struct attribute *soundbus_dev_attrs[] = {
+ &dev_attr_name.attr,
+ &dev_attr_type.attr,
+ &dev_attr_modalias.attr,
+ NULL,
};
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
index 885683a3b0bd..dea2c661b353 100644
--- a/sound/arm/Kconfig
+++ b/sound/arm/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# ALSA ARM drivers
menuconfig SND_ARM
@@ -17,21 +18,9 @@ config SND_ARMAACI
select SND_PCM
select SND_AC97_CODEC
-config SND_PXA2XX_PCM
- tristate
- select SND_PCM
-
-config SND_PXA2XX_LIB
- tristate
- select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97
-
-config SND_PXA2XX_LIB_AC97
- bool
-
config SND_PXA2XX_AC97
tristate "AC97 driver for the Intel PXA2xx chip"
depends on ARCH_PXA
- select SND_PXA2XX_PCM
select SND_AC97_CODEC
select SND_PXA2XX_LIB
select SND_PXA2XX_LIB_AC97
@@ -41,3 +30,9 @@ config SND_PXA2XX_AC97
endif # SND_ARM
+config SND_PXA2XX_LIB
+ tristate
+ select SND_DMAENGINE_PCM
+
+config SND_PXA2XX_LIB_AC97
+ bool
diff --git a/sound/arm/Makefile b/sound/arm/Makefile
index 8c0c851d4641..899edb4bb278 100644
--- a/sound/arm/Makefile
+++ b/sound/arm/Makefile
@@ -1,16 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
#
obj-$(CONFIG_SND_ARMAACI) += snd-aaci.o
-snd-aaci-objs := aaci.o
-
-obj-$(CONFIG_SND_PXA2XX_PCM) += snd-pxa2xx-pcm.o
-snd-pxa2xx-pcm-objs := pxa2xx-pcm.o
+snd-aaci-y := aaci.o
obj-$(CONFIG_SND_PXA2XX_LIB) += snd-pxa2xx-lib.o
snd-pxa2xx-lib-y := pxa2xx-pcm-lib.o
snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_AC97) += pxa2xx-ac97-lib.o
obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o
-snd-pxa2xx-ac97-objs := pxa2xx-ac97.o
+snd-pxa2xx-ac97-y := pxa2xx-ac97.o
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c
index 91acc9a243ec..5548ed8e6b1c 100644
--- a/sound/arm/aaci.c
+++ b/sound/arm/aaci.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/sound/arm/aaci.c - ARM PrimeCell AACI PL041 driver
*
* Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* Documentation: ARM DDI 0173B
*/
#include <linux/module.h>
@@ -30,6 +27,8 @@
#define DRIVER_NAME "aaci-pl041"
+#define FRAME_PERIOD_US 21
+
/*
* PM support is not complete. Turn it off.
*/
@@ -48,7 +47,11 @@ static void aaci_ac97_select_codec(struct aaci *aaci, struct snd_ac97 *ac97)
if (v & SLFR_1RXV)
readl(aaci->base + AACI_SL1RX);
- writel(maincr, aaci->base + AACI_MAINCR);
+ if (maincr != readl(aaci->base + AACI_MAINCR)) {
+ writel(maincr, aaci->base + AACI_MAINCR);
+ readl(aaci->base + AACI_MAINCR);
+ udelay(1);
+ }
}
/*
@@ -64,13 +67,13 @@ static void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
unsigned short val)
{
struct aaci *aaci = ac97->private_data;
+ int timeout;
u32 v;
- int timeout = 5000;
if (ac97->num >= 4)
return;
- mutex_lock(&aaci->ac97_sem);
+ guard(mutex)(&aaci->ac97_sem);
aaci_ac97_select_codec(aaci, ac97);
@@ -81,18 +84,19 @@ static void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
writel(val << 4, aaci->base + AACI_SL2TX);
writel(reg << 12, aaci->base + AACI_SL1TX);
- /*
- * Wait for the transmission of both slots to complete.
- */
+ /* Initially, wait one frame period */
+ udelay(FRAME_PERIOD_US);
+
+ /* And then wait an additional eight frame periods for it to be sent */
+ timeout = FRAME_PERIOD_US * 8;
do {
+ udelay(1);
v = readl(aaci->base + AACI_SLFR);
} while ((v & (SLFR_1TXB|SLFR_2TXB)) && --timeout);
- if (!timeout)
+ if (v & (SLFR_1TXB|SLFR_2TXB))
dev_err(&aaci->dev->dev,
"timeout waiting for write to complete\n");
-
- mutex_unlock(&aaci->ac97_sem);
}
/*
@@ -101,14 +105,13 @@ static void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
struct aaci *aaci = ac97->private_data;
+ int timeout, retries = 10;
u32 v;
- int timeout = 5000;
- int retries = 10;
if (ac97->num >= 4)
return ~0;
- mutex_lock(&aaci->ac97_sem);
+ guard(mutex)(&aaci->ac97_sem);
aaci_ac97_select_codec(aaci, ac97);
@@ -117,38 +120,35 @@ static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
*/
writel((reg << 12) | (1 << 19), aaci->base + AACI_SL1TX);
- /*
- * Wait for the transmission to complete.
- */
+ /* Initially, wait one frame period */
+ udelay(FRAME_PERIOD_US);
+
+ /* And then wait an additional eight frame periods for it to be sent */
+ timeout = FRAME_PERIOD_US * 8;
do {
+ udelay(1);
v = readl(aaci->base + AACI_SLFR);
} while ((v & SLFR_1TXB) && --timeout);
- if (!timeout) {
+ if (v & SLFR_1TXB) {
dev_err(&aaci->dev->dev, "timeout on slot 1 TX busy\n");
- v = ~0;
- goto out;
+ return ~0;
}
- /*
- * Give the AC'97 codec more than enough time
- * to respond. (42us = ~2 frames at 48kHz.)
- */
- udelay(42);
+ /* Now wait for the response frame */
+ udelay(FRAME_PERIOD_US);
- /*
- * Wait for slot 2 to indicate data.
- */
- timeout = 5000;
+ /* And then wait an additional eight frame periods for data */
+ timeout = FRAME_PERIOD_US * 8;
do {
+ udelay(1);
cond_resched();
v = readl(aaci->base + AACI_SLFR) & (SLFR_1RXV|SLFR_2RXV);
} while ((v != (SLFR_1RXV|SLFR_2RXV)) && --timeout);
- if (!timeout) {
+ if (v != (SLFR_1RXV|SLFR_2RXV)) {
dev_err(&aaci->dev->dev, "timeout on RX valid\n");
- v = ~0;
- goto out;
+ return ~0;
}
do {
@@ -167,8 +167,6 @@ static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
v = ~0;
}
} while (retries);
- out:
- mutex_unlock(&aaci->ac97_sem);
return v;
}
@@ -179,6 +177,7 @@ aaci_chan_wait_ready(struct aaci_runtime *aacirun, unsigned long mask)
int timeout = 5000;
do {
+ udelay(1);
val = readl(aacirun->base + AACI_SR);
} while (val & mask && timeout--);
}
@@ -202,6 +201,7 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
if (mask & ISR_RXINTR) {
struct aaci_runtime *aacirun = &aaci->capture;
+ bool period_elapsed = false;
void *ptr;
if (!aacirun->substream || !aacirun->start) {
@@ -210,48 +210,46 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
return;
}
- spin_lock(&aacirun->lock);
-
- ptr = aacirun->ptr;
- do {
- unsigned int len = aacirun->fifosz;
- u32 val;
-
- if (aacirun->bytes <= 0) {
- aacirun->bytes += aacirun->period;
- aacirun->ptr = ptr;
- spin_unlock(&aacirun->lock);
- snd_pcm_period_elapsed(aacirun->substream);
- spin_lock(&aacirun->lock);
- }
- if (!(aacirun->cr & CR_EN))
- break;
-
- val = readl(aacirun->base + AACI_SR);
- if (!(val & SR_RXHF))
- break;
- if (!(val & SR_RXFF))
- len >>= 1;
-
- aacirun->bytes -= len;
-
- /* reading 16 bytes at a time */
- for( ; len > 0; len -= 16) {
- asm(
- "ldmia %1, {r0, r1, r2, r3}\n\t"
- "stmia %0!, {r0, r1, r2, r3}"
- : "+r" (ptr)
- : "r" (aacirun->fifo)
- : "r0", "r1", "r2", "r3", "cc");
-
- if (ptr >= aacirun->end)
- ptr = aacirun->start;
- }
- } while(1);
-
- aacirun->ptr = ptr;
+ scoped_guard(spinlock, &aacirun->lock) {
+ ptr = aacirun->ptr;
+ do {
+ unsigned int len = aacirun->fifo_bytes;
+ u32 val;
+
+ if (aacirun->bytes <= 0) {
+ aacirun->bytes += aacirun->period;
+ period_elapsed = true;
+ }
+ if (!(aacirun->cr & CR_EN))
+ break;
+
+ val = readl(aacirun->base + AACI_SR);
+ if (!(val & SR_RXHF))
+ break;
+ if (!(val & SR_RXFF))
+ len >>= 1;
+
+ aacirun->bytes -= len;
+
+ /* reading 16 bytes at a time */
+ for( ; len > 0; len -= 16) {
+ asm(
+ "ldmia %1, {r0, r1, r2, r3}\n\t"
+ "stmia %0!, {r0, r1, r2, r3}"
+ : "+r" (ptr)
+ : "r" (aacirun->fifo)
+ : "r0", "r1", "r2", "r3", "cc");
+
+ if (ptr >= aacirun->end)
+ ptr = aacirun->start;
+ }
+ } while(1);
+
+ aacirun->ptr = ptr;
+ }
- spin_unlock(&aacirun->lock);
+ if (period_elapsed)
+ snd_pcm_period_elapsed(aacirun->substream);
}
if (mask & ISR_URINTR) {
@@ -261,6 +259,7 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
if (mask & ISR_TXINTR) {
struct aaci_runtime *aacirun = &aaci->playback;
+ bool period_elapsed = false;
void *ptr;
if (!aacirun->substream || !aacirun->start) {
@@ -269,48 +268,46 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
return;
}
- spin_lock(&aacirun->lock);
-
- ptr = aacirun->ptr;
- do {
- unsigned int len = aacirun->fifosz;
- u32 val;
-
- if (aacirun->bytes <= 0) {
- aacirun->bytes += aacirun->period;
- aacirun->ptr = ptr;
- spin_unlock(&aacirun->lock);
- snd_pcm_period_elapsed(aacirun->substream);
- spin_lock(&aacirun->lock);
- }
- if (!(aacirun->cr & CR_EN))
- break;
-
- val = readl(aacirun->base + AACI_SR);
- if (!(val & SR_TXHE))
- break;
- if (!(val & SR_TXFE))
- len >>= 1;
-
- aacirun->bytes -= len;
-
- /* writing 16 bytes at a time */
- for ( ; len > 0; len -= 16) {
- asm(
- "ldmia %0!, {r0, r1, r2, r3}\n\t"
- "stmia %1, {r0, r1, r2, r3}"
- : "+r" (ptr)
- : "r" (aacirun->fifo)
- : "r0", "r1", "r2", "r3", "cc");
-
- if (ptr >= aacirun->end)
- ptr = aacirun->start;
- }
- } while (1);
-
- aacirun->ptr = ptr;
+ scoped_guard(spinlock, &aacirun->lock) {
+ ptr = aacirun->ptr;
+ do {
+ unsigned int len = aacirun->fifo_bytes;
+ u32 val;
+
+ if (aacirun->bytes <= 0) {
+ aacirun->bytes += aacirun->period;
+ period_elapsed = true;
+ }
+ if (!(aacirun->cr & CR_EN))
+ break;
+
+ val = readl(aacirun->base + AACI_SR);
+ if (!(val & SR_TXHE))
+ break;
+ if (!(val & SR_TXFE))
+ len >>= 1;
+
+ aacirun->bytes -= len;
+
+ /* writing 16 bytes at a time */
+ for ( ; len > 0; len -= 16) {
+ asm(
+ "ldmia %0!, {r0, r1, r2, r3}\n\t"
+ "stmia %1, {r0, r1, r2, r3}"
+ : "+r" (ptr)
+ : "r" (aacirun->fifo)
+ : "r0", "r1", "r2", "r3", "cc");
+
+ if (ptr >= aacirun->end)
+ ptr = aacirun->start;
+ }
+ } while (1);
+
+ aacirun->ptr = ptr;
+ }
- spin_unlock(&aacirun->lock);
+ if (period_elapsed)
+ snd_pcm_period_elapsed(aacirun->substream);
}
}
@@ -338,7 +335,7 @@ static irqreturn_t aaci_irq(int irq, void *devid)
/*
* ALSA support.
*/
-static struct snd_pcm_hardware aaci_hw_info = {
+static const struct snd_pcm_hardware aaci_hw_info = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -353,7 +350,7 @@ static struct snd_pcm_hardware aaci_hw_info = {
/* rates are setup from the AC'97 codec */
.channels_min = 2,
- .channels_max = 6,
+ .channels_max = 2,
.buffer_bytes_max = 64 * 1024,
.period_bytes_min = 256,
.period_bytes_max = PAGE_SIZE,
@@ -361,12 +358,46 @@ static struct snd_pcm_hardware aaci_hw_info = {
.periods_max = PAGE_SIZE / 16,
};
-static int __aaci_pcm_open(struct aaci *aaci,
- struct snd_pcm_substream *substream,
- struct aaci_runtime *aacirun)
+/*
+ * We can support two and four channel audio. Unfortunately
+ * six channel audio requires a non-standard channel ordering:
+ * 2 -> FL(3), FR(4)
+ * 4 -> FL(3), FR(4), SL(7), SR(8)
+ * 6 -> FL(3), FR(4), SL(7), SR(8), C(6), LFE(9) (required)
+ * FL(3), FR(4), C(6), SL(7), SR(8), LFE(9) (actual)
+ * This requires an ALSA configuration file to correct.
+ */
+static int aaci_rule_channels(struct snd_pcm_hw_params *p,
+ struct snd_pcm_hw_rule *rule)
+{
+ static const unsigned int channel_list[] = { 2, 4, 6 };
+ struct aaci *aaci = rule->private;
+ unsigned int mask = 1 << 0, slots;
+
+ /* pcms[0] is the our 5.1 PCM instance. */
+ slots = aaci->ac97_bus->pcms[0].r[0].slots;
+ if (slots & (1 << AC97_SLOT_PCM_SLEFT)) {
+ mask |= 1 << 1;
+ if (slots & (1 << AC97_SLOT_LFE))
+ mask |= 1 << 2;
+ }
+
+ return snd_interval_list(hw_param_interval(p, rule->var),
+ ARRAY_SIZE(channel_list), channel_list, mask);
+}
+
+static int aaci_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- int ret;
+ struct aaci *aaci = substream->private_data;
+ struct aaci_runtime *aacirun;
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ aacirun = &aaci->playback;
+ } else {
+ aacirun = &aaci->capture;
+ }
aacirun->substream = substream;
runtime->private_data = aacirun;
@@ -374,27 +405,36 @@ static int __aaci_pcm_open(struct aaci *aaci,
runtime->hw.rates = aacirun->pcm->rates;
snd_pcm_limit_hw_rates(runtime);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
- aacirun->pcm->r[1].slots)
- snd_ac97_pcm_double_rate_rules(runtime);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ runtime->hw.channels_max = 6;
+
+ /* Add rule describing channel dependency. */
+ ret = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ aaci_rule_channels, aaci,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (ret)
+ return ret;
+
+ if (aacirun->pcm->r[1].slots)
+ snd_ac97_pcm_double_rate_rules(runtime);
+ }
/*
- * FIXME: ALSA specifies fifo_size in bytes. If we're in normal
- * mode, each 32-bit word contains one sample. If we're in
- * compact mode, each 32-bit word contains two samples, effectively
- * halving the FIFO size. However, we don't know for sure which
- * we'll be using at this point. We set this to the lower limit.
+ * ALSA wants the byte-size of the FIFOs. As we only support
+ * 16-bit samples, this is twice the FIFO depth irrespective
+ * of whether it's in compact mode or not.
*/
- runtime->hw.fifo_size = aaci->fifosize * 2;
-
- ret = request_irq(aaci->dev->irq[0], aaci_irq, IRQF_SHARED|IRQF_DISABLED,
- DRIVER_NAME, aaci);
- if (ret)
- goto out;
-
- return 0;
+ runtime->hw.fifo_size = aaci->fifo_depth * 2;
+
+ guard(mutex)(&aaci->irq_lock);
+ if (!aaci->users++) {
+ ret = request_irq(aaci->dev->irq[0], aaci_irq,
+ IRQF_SHARED, DRIVER_NAME, aaci);
+ if (ret != 0)
+ aaci->users--;
+ }
- out:
return ret;
}
@@ -410,7 +450,10 @@ static int aaci_pcm_close(struct snd_pcm_substream *substream)
WARN_ON(aacirun->cr & CR_EN);
aacirun->substream = NULL;
- free_irq(aaci->dev->irq[0], aaci);
+
+ guard(mutex)(&aaci->irq_lock);
+ if (!--aaci->users)
+ free_irq(aaci->dev->irq[0], aaci);
return 0;
}
@@ -428,20 +471,25 @@ static int aaci_pcm_hw_free(struct snd_pcm_substream *substream)
snd_ac97_pcm_close(aacirun->pcm);
aacirun->pcm_open = 0;
- /*
- * Clear out the DMA and any allocated buffers.
- */
- snd_pcm_lib_free_pages(substream);
-
return 0;
}
+/* Channel to slot mask */
+static const u32 channels_to_slotmask[] = {
+ [2] = CR_SL3 | CR_SL4,
+ [4] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8,
+ [6] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8 | CR_SL6 | CR_SL9,
+};
+
static int aaci_pcm_hw_params(struct snd_pcm_substream *substream,
- struct aaci_runtime *aacirun,
struct snd_pcm_hw_params *params)
{
- int err;
+ struct aaci_runtime *aacirun = substream->runtime->private_data;
struct aaci *aaci = substream->private_data;
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+ int dbl = rate > 48000;
+ int err;
aaci_pcm_hw_free(substream);
if (aacirun->pcm_open) {
@@ -449,23 +497,23 @@ static int aaci_pcm_hw_params(struct snd_pcm_substream *substream,
aacirun->pcm_open = 0;
}
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(params));
- if (err >= 0) {
- unsigned int rate = params_rate(params);
- int dbl = rate > 48000;
+ /* channels is already limited to 2, 4, or 6 by aaci_rule_channels */
+ if (dbl && channels != 2)
+ return -EINVAL;
- err = snd_ac97_pcm_open(aacirun->pcm, rate,
- params_channels(params),
- aacirun->pcm->r[dbl].slots);
+ err = snd_ac97_pcm_open(aacirun->pcm, rate, channels,
+ aacirun->pcm->r[dbl].slots);
- aacirun->pcm_open = err == 0;
- aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;
- aacirun->fifosz = aaci->fifosize * 4;
+ aacirun->pcm_open = err == 0;
+ aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;
+ aacirun->cr |= channels_to_slotmask[channels + dbl * 2];
- if (aacirun->cr & CR_COMPACT)
- aacirun->fifosz >>= 1;
- }
+ /*
+ * fifo_bytes is the number of bytes we transfer to/from
+ * the FIFO, including padding. So that's x4. As we're
+ * in compact mode, the FIFO is half the size.
+ */
+ aacirun->fifo_bytes = aaci->fifo_depth * 4 / 2;
return err;
}
@@ -475,11 +523,11 @@ static int aaci_pcm_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct aaci_runtime *aacirun = runtime->private_data;
+ aacirun->period = snd_pcm_lib_period_bytes(substream);
aacirun->start = runtime->dma_area;
aacirun->end = aacirun->start + snd_pcm_lib_buffer_bytes(substream);
aacirun->ptr = aacirun->start;
- aacirun->period =
- aacirun->bytes = frames_to_bytes(runtime, runtime->period_size);
+ aacirun->bytes = aacirun->period;
return 0;
}
@@ -497,89 +545,6 @@ static snd_pcm_uframes_t aaci_pcm_pointer(struct snd_pcm_substream *substream)
/*
* Playback specific ALSA stuff
*/
-static const u32 channels_to_txmask[] = {
- [2] = CR_SL3 | CR_SL4,
- [4] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8,
- [6] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8 | CR_SL6 | CR_SL9,
-};
-
-/*
- * We can support two and four channel audio. Unfortunately
- * six channel audio requires a non-standard channel ordering:
- * 2 -> FL(3), FR(4)
- * 4 -> FL(3), FR(4), SL(7), SR(8)
- * 6 -> FL(3), FR(4), SL(7), SR(8), C(6), LFE(9) (required)
- * FL(3), FR(4), C(6), SL(7), SR(8), LFE(9) (actual)
- * This requires an ALSA configuration file to correct.
- */
-static unsigned int channel_list[] = { 2, 4, 6 };
-
-static int
-aaci_rule_channels(struct snd_pcm_hw_params *p, struct snd_pcm_hw_rule *rule)
-{
- struct aaci *aaci = rule->private;
- unsigned int chan_mask = 1 << 0, slots;
-
- /*
- * pcms[0] is the our 5.1 PCM instance.
- */
- slots = aaci->ac97_bus->pcms[0].r[0].slots;
- if (slots & (1 << AC97_SLOT_PCM_SLEFT)) {
- chan_mask |= 1 << 1;
- if (slots & (1 << AC97_SLOT_LFE))
- chan_mask |= 1 << 2;
- }
-
- return snd_interval_list(hw_param_interval(p, rule->var),
- ARRAY_SIZE(channel_list), channel_list,
- chan_mask);
-}
-
-static int aaci_pcm_open(struct snd_pcm_substream *substream)
-{
- struct aaci *aaci = substream->private_data;
- int ret;
-
- /*
- * Add rule describing channel dependency.
- */
- ret = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- aaci_rule_channels, aaci,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1);
- if (ret)
- return ret;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- ret = __aaci_pcm_open(aaci, substream, &aaci->playback);
- } else {
- ret = __aaci_pcm_open(aaci, substream, &aaci->capture);
- }
- return ret;
-}
-
-static int aaci_pcm_playback_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct aaci_runtime *aacirun = substream->runtime->private_data;
- unsigned int channels = params_channels(params);
- int ret;
-
- WARN_ON(channels >= ARRAY_SIZE(channels_to_txmask) ||
- !channels_to_txmask[channels]);
-
- ret = aaci_pcm_hw_params(substream, aacirun, params);
-
- /*
- * Enable FIFO, compact mode, 16 bits per sample.
- * FIXME: double rate slots?
- */
- if (ret >= 0)
- aacirun->cr |= channels_to_txmask[channels];
-
- return ret;
-}
-
static void aaci_pcm_playback_stop(struct aaci_runtime *aacirun)
{
u32 ie;
@@ -608,10 +573,8 @@ static void aaci_pcm_playback_start(struct aaci_runtime *aacirun)
static int aaci_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct aaci_runtime *aacirun = substream->runtime->private_data;
- unsigned long flags;
- int ret = 0;
- spin_lock_irqsave(&aacirun->lock, flags);
+ guard(spinlock_irqsave)(&aacirun->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -637,39 +600,22 @@ static int aaci_pcm_playback_trigger(struct snd_pcm_substream *substream, int cm
break;
default:
- ret = -EINVAL;
+ return -EINVAL;
}
- spin_unlock_irqrestore(&aacirun->lock, flags);
-
- return ret;
+ return 0;
}
-static struct snd_pcm_ops aaci_playback_ops = {
+static const struct snd_pcm_ops aaci_playback_ops = {
.open = aaci_pcm_open,
.close = aaci_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = aaci_pcm_playback_hw_params,
+ .hw_params = aaci_pcm_hw_params,
.hw_free = aaci_pcm_hw_free,
.prepare = aaci_pcm_prepare,
.trigger = aaci_pcm_playback_trigger,
.pointer = aaci_pcm_pointer,
};
-static int aaci_pcm_capture_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct aaci_runtime *aacirun = substream->runtime->private_data;
- int ret;
-
- ret = aaci_pcm_hw_params(substream, aacirun, params);
- if (ret >= 0)
- /* Line in record: slot 3 and 4 */
- aacirun->cr |= CR_SL3 | CR_SL4;
-
- return ret;
-}
-
static void aaci_pcm_capture_stop(struct aaci_runtime *aacirun)
{
u32 ie;
@@ -707,10 +653,8 @@ static void aaci_pcm_capture_start(struct aaci_runtime *aacirun)
static int aaci_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct aaci_runtime *aacirun = substream->runtime->private_data;
- unsigned long flags;
- int ret = 0;
- spin_lock_irqsave(&aacirun->lock, flags);
+ guard(spinlock_irqsave)(&aacirun->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -736,12 +680,10 @@ static int aaci_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd
break;
default:
- ret = -EINVAL;
+ return -EINVAL;
}
- spin_unlock_irqrestore(&aacirun->lock, flags);
-
- return ret;
+ return 0;
}
static int aaci_pcm_capture_prepare(struct snd_pcm_substream *substream)
@@ -762,11 +704,10 @@ static int aaci_pcm_capture_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops aaci_capture_ops = {
+static const struct snd_pcm_ops aaci_capture_ops = {
.open = aaci_pcm_open,
.close = aaci_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = aaci_pcm_capture_hw_params,
+ .hw_params = aaci_pcm_hw_params,
.hw_free = aaci_pcm_hw_free,
.prepare = aaci_pcm_capture_prepare,
.trigger = aaci_pcm_capture_trigger,
@@ -776,41 +717,33 @@ static struct snd_pcm_ops aaci_capture_ops = {
/*
* Power Management.
*/
-#ifdef CONFIG_PM
-static int aaci_do_suspend(struct snd_card *card, unsigned int state)
+static int aaci_do_suspend(struct snd_card *card)
{
- struct aaci *aaci = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
- snd_pcm_suspend_all(aaci->pcm);
return 0;
}
-static int aaci_do_resume(struct snd_card *card, unsigned int state)
+static int aaci_do_resume(struct snd_card *card)
{
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-static int aaci_suspend(struct amba_device *dev, pm_message_t state)
+static int aaci_suspend(struct device *dev)
{
- struct snd_card *card = amba_get_drvdata(dev);
+ struct snd_card *card = dev_get_drvdata(dev);
return card ? aaci_do_suspend(card) : 0;
}
-static int aaci_resume(struct amba_device *dev)
+static int aaci_resume(struct device *dev)
{
- struct snd_card *card = amba_get_drvdata(dev);
+ struct snd_card *card = dev_get_drvdata(dev);
return card ? aaci_do_resume(card) : 0;
}
-#else
-#define aaci_do_suspend NULL
-#define aaci_do_resume NULL
-#define aaci_suspend NULL
-#define aaci_resume NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(aaci_dev_pm_ops, aaci_suspend, aaci_resume);
-static struct ac97_pcm ac97_defs[] __devinitdata = {
+static const struct ac97_pcm ac97_defs[] = {
[0] = { /* Front PCM */
.exclusive = 1,
.r = {
@@ -851,12 +784,12 @@ static struct ac97_pcm ac97_defs[] __devinitdata = {
}
};
-static struct snd_ac97_bus_ops aaci_bus_ops = {
+static const struct snd_ac97_bus_ops aaci_bus_ops = {
.write = aaci_ac97_write,
.read = aaci_ac97_read,
};
-static int __devinit aaci_probe_ac97(struct aaci *aaci)
+static int aaci_probe_ac97(struct aaci *aaci)
{
struct snd_ac97_template ac97_template;
struct snd_ac97_bus *ac97_bus;
@@ -874,7 +807,7 @@ static int __devinit aaci_probe_ac97(struct aaci *aaci)
* Give the AC'97 codec more than enough time
* to wake up. (42us = ~2 frames at 48kHz.)
*/
- udelay(42);
+ udelay(FRAME_PERIOD_US * 2);
ret = snd_ac97_bus(aaci->card, 0, &aaci_bus_ops, aaci, &ac97_bus);
if (ret)
@@ -913,32 +846,33 @@ static int __devinit aaci_probe_ac97(struct aaci *aaci)
static void aaci_free_card(struct snd_card *card)
{
struct aaci *aaci = card->private_data;
- if (aaci->base)
- iounmap(aaci->base);
+
+ iounmap(aaci->base);
}
-static struct aaci * __devinit aaci_init_card(struct amba_device *dev)
+static struct aaci *aaci_init_card(struct amba_device *dev)
{
struct aaci *aaci;
struct snd_card *card;
int err;
- err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
- THIS_MODULE, sizeof(struct aaci), &card);
+ err = snd_card_new(&dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, sizeof(struct aaci), &card);
if (err < 0)
return NULL;
card->private_free = aaci_free_card;
- strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
- strlcpy(card->shortname, "ARM AC'97 Interface", sizeof(card->shortname));
+ strscpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+ strscpy(card->shortname, "ARM AC'97 Interface", sizeof(card->shortname));
snprintf(card->longname, sizeof(card->longname),
- "%s at 0x%016llx, irq %d",
- card->shortname, (unsigned long long)dev->res.start,
- dev->irq[0]);
+ "%s PL%03x rev%u at 0x%08llx, irq %d",
+ card->shortname, amba_part(dev), amba_rev(dev),
+ (unsigned long long)dev->res.start, dev->irq[0]);
aaci = card->private_data;
mutex_init(&aaci->ac97_sem);
+ mutex_init(&aaci->irq_lock);
aaci->card = card;
aaci->dev = dev;
@@ -949,7 +883,7 @@ static struct aaci * __devinit aaci_init_card(struct amba_device *dev)
return aaci;
}
-static int __devinit aaci_init_pcm(struct aaci *aaci)
+static int aaci_init_pcm(struct aaci *aaci)
{
struct snd_pcm *pcm;
int ret;
@@ -960,22 +894,27 @@ static int __devinit aaci_init_pcm(struct aaci *aaci)
pcm->private_data = aaci;
pcm->info_flags = 0;
- strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
+ strscpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &aaci_capture_ops);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- NULL, 0, 64 * 1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ aaci->card->dev,
+ 0, 64 * 1024);
}
return ret;
}
-static unsigned int __devinit aaci_size_fifo(struct aaci *aaci)
+static unsigned int aaci_size_fifo(struct aaci *aaci)
{
struct aaci_runtime *aacirun = &aaci->playback;
int i;
+ /*
+ * Enable the channel, but don't assign it to any slots, so
+ * it won't empty onto the AC'97 link.
+ */
writel(CR_FEN | CR_SZ16 | CR_EN, aacirun->base + AACI_TXCR);
for (i = 0; !(readl(aacirun->base + AACI_SR) & SR_TXFF) && i < 4096; i++)
@@ -989,10 +928,12 @@ static unsigned int __devinit aaci_size_fifo(struct aaci *aaci)
* disabling the channel doesn't clear the FIFO.
*/
writel(aaci->maincr & ~MAINCR_IE, aaci->base + AACI_MAINCR);
+ readl(aaci->base + AACI_MAINCR);
+ udelay(1);
writel(aaci->maincr, aaci->base + AACI_MAINCR);
/*
- * If we hit 4096, we failed. Go back to the specified
+ * If we hit 4096 entries, we failed. Go back to the specified
* fifo depth.
*/
if (i == 4096)
@@ -1001,7 +942,8 @@ static unsigned int __devinit aaci_size_fifo(struct aaci *aaci)
return i;
}
-static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id)
+static int aaci_probe(struct amba_device *dev,
+ const struct amba_id *id)
{
struct aaci *aaci;
int ret, i;
@@ -1057,11 +999,12 @@ static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id)
/*
* Size the FIFOs (must be multiple of 16).
+ * This is the number of entries in the FIFO.
*/
- aaci->fifosize = aaci_size_fifo(aaci);
- if (aaci->fifosize & 15) {
- printk(KERN_WARNING "AACI: fifosize = %d not supported\n",
- aaci->fifosize);
+ aaci->fifo_depth = aaci_size_fifo(aaci);
+ if (aaci->fifo_depth & 15) {
+ printk(KERN_WARNING "AACI: FIFO depth %d not supported\n",
+ aaci->fifo_depth);
ret = -ENODEV;
goto out;
}
@@ -1070,12 +1013,10 @@ static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id)
if (ret)
goto out;
- snd_card_set_dev(aaci->card, &dev->dev);
-
ret = snd_card_register(aaci->card);
if (ret == 0) {
- dev_info(&dev->dev, "%s, fifo %d\n", aaci->card->longname,
- aaci->fifosize);
+ dev_info(&dev->dev, "%s\n", aaci->card->longname);
+ dev_info(&dev->dev, "FIFO %u entries\n", aaci->fifo_depth);
amba_set_drvdata(dev, aaci->card);
return ret;
}
@@ -1087,12 +1028,10 @@ static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id)
return ret;
}
-static int __devexit aaci_remove(struct amba_device *dev)
+static void aaci_remove(struct amba_device *dev)
{
struct snd_card *card = amba_get_drvdata(dev);
- amba_set_drvdata(dev, NULL);
-
if (card) {
struct aaci *aaci = card->private_data;
writel(0, aaci->base + AACI_MAINCR);
@@ -1100,11 +1039,9 @@ static int __devexit aaci_remove(struct amba_device *dev)
snd_card_free(card);
amba_release_regions(dev);
}
-
- return 0;
}
-static struct amba_id aaci_ids[] = {
+static const struct amba_id aaci_ids[] = {
{
.id = 0x00041041,
.mask = 0x000fffff,
@@ -1112,29 +1049,19 @@ static struct amba_id aaci_ids[] = {
{ 0, 0 },
};
+MODULE_DEVICE_TABLE(amba, aaci_ids);
+
static struct amba_driver aaci_driver = {
.drv = {
.name = DRIVER_NAME,
+ .pm = &aaci_dev_pm_ops,
},
.probe = aaci_probe,
- .remove = __devexit_p(aaci_remove),
- .suspend = aaci_suspend,
- .resume = aaci_resume,
+ .remove = aaci_remove,
.id_table = aaci_ids,
};
-static int __init aaci_init(void)
-{
- return amba_driver_register(&aaci_driver);
-}
-
-static void __exit aaci_exit(void)
-{
- amba_driver_unregister(&aaci_driver);
-}
-
-module_init(aaci_init);
-module_exit(aaci_exit);
+module_amba_driver(aaci_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ARM PrimeCell PL041 Advanced Audio CODEC Interface driver");
diff --git a/sound/arm/aaci.h b/sound/arm/aaci.h
index 6a4a2eebdda1..18680e7f8d3a 100644
--- a/sound/arm/aaci.h
+++ b/sound/arm/aaci.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* linux/sound/arm/aaci.c - ARM PrimeCell AACI PL041 driver
*
* Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#ifndef AACI_H
#define AACI_H
@@ -210,6 +207,8 @@ struct aaci_runtime {
u32 cr;
struct snd_pcm_substream *substream;
+ unsigned int period; /* byte size of a "period" */
+
/*
* PIO support
*/
@@ -217,15 +216,16 @@ struct aaci_runtime {
void *end;
void *ptr;
int bytes;
- unsigned int period;
- unsigned int fifosz;
+ unsigned int fifo_bytes;
};
struct aaci {
struct amba_device *dev;
struct snd_card *card;
void __iomem *base;
- unsigned int fifosize;
+ unsigned int fifo_depth;
+ unsigned int users;
+ struct mutex irq_lock;
/* AC'97 */
struct mutex ac97_sem;
diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c
index 88eec3847df2..64510318091f 100644
--- a/sound/arm/pxa2xx-ac97-lib.c
+++ b/sound/arm/pxa2xx-ac97-lib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Based on sound/arm/pxa2xx-ac97.c and sound/soc/pxa/pxa2xx-ac97.c
* which contain:
@@ -5,10 +6,6 @@
* Author: Nicolas Pitre
* Created: Dec 02, 2004
* Copyright: MontaVista Software Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/kernel.h>
@@ -16,13 +13,17 @@
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/soc/pxa/cpu.h>
-#include <sound/ac97_codec.h>
#include <sound/pxa2xx-lib.h>
-#include <asm/irq.h>
-#include <mach/regs-ac97.h>
-#include <mach/audio.h>
+#include <linux/platform_data/asoc-pxa.h>
+
+#include "pxa2xx-ac97-regs.h"
static DEFINE_MUTEX(car_mutex);
static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
@@ -30,8 +31,7 @@ static volatile long gsr_bits;
static struct clk *ac97_clk;
static struct clk *ac97conf_clk;
static int reset_gpio;
-
-extern void pxa27x_assert_ac97reset(int reset_gpio, int on);
+static void __iomem *ac97_reg_base;
/*
* Beware PXA27x bugs:
@@ -43,69 +43,75 @@ extern void pxa27x_assert_ac97reset(int reset_gpio, int on);
* 1 jiffy timeout if interrupt never comes).
*/
-unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+int pxa2xx_ac97_read(int slot, unsigned short reg)
{
- unsigned short val = -1;
- volatile u32 *reg_addr;
+ int val = -ENODEV;
+ u32 __iomem *reg_addr;
+
+ if (slot > 0)
+ return -ENODEV;
- mutex_lock(&car_mutex);
+ guard(mutex)(&car_mutex);
/* set up primary or secondary codec space */
if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS)
- reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE;
+ reg_addr = ac97_reg_base +
+ (slot ? SMC_REG_BASE : PMC_REG_BASE);
else
- reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
+ reg_addr = ac97_reg_base +
+ (slot ? SAC_REG_BASE : PAC_REG_BASE);
reg_addr += (reg >> 1);
/* start read access across the ac97 link */
- GSR = GSR_CDONE | GSR_SDONE;
+ writel(GSR_CDONE | GSR_SDONE, ac97_reg_base + GSR);
gsr_bits = 0;
- val = *reg_addr;
+ val = (readl(reg_addr) & 0xffff);
if (reg == AC97_GPIO_STATUS)
- goto out;
- if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 &&
- !((GSR | gsr_bits) & GSR_SDONE)) {
+ return val;
+ if (wait_event_timeout(gsr_wq, (readl(ac97_reg_base + GSR) | gsr_bits) & GSR_SDONE, 1) <= 0 &&
+ !((readl(ac97_reg_base + GSR) | gsr_bits) & GSR_SDONE)) {
printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n",
- __func__, reg, GSR | gsr_bits);
- val = -1;
- goto out;
+ __func__, reg, readl(ac97_reg_base + GSR) | gsr_bits);
+ return -ETIMEDOUT;
}
/* valid data now */
- GSR = GSR_CDONE | GSR_SDONE;
+ writel(GSR_CDONE | GSR_SDONE, ac97_reg_base + GSR);
gsr_bits = 0;
- val = *reg_addr;
+ val = (readl(reg_addr) & 0xffff);
/* but we've just started another cycle... */
- wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
-
-out: mutex_unlock(&car_mutex);
+ wait_event_timeout(gsr_wq, (readl(ac97_reg_base + GSR) | gsr_bits) & GSR_SDONE, 1);
return val;
}
EXPORT_SYMBOL_GPL(pxa2xx_ac97_read);
-void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
- unsigned short val)
+int pxa2xx_ac97_write(int slot, unsigned short reg, unsigned short val)
{
- volatile u32 *reg_addr;
+ u32 __iomem *reg_addr;
+ int ret = 0;
- mutex_lock(&car_mutex);
+ guard(mutex)(&car_mutex);
/* set up primary or secondary codec space */
if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS)
- reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE;
+ reg_addr = ac97_reg_base +
+ (slot ? SMC_REG_BASE : PMC_REG_BASE);
else
- reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE;
+ reg_addr = ac97_reg_base +
+ (slot ? SAC_REG_BASE : PAC_REG_BASE);
reg_addr += (reg >> 1);
- GSR = GSR_CDONE | GSR_SDONE;
+ writel(GSR_CDONE | GSR_SDONE, ac97_reg_base + GSR);
gsr_bits = 0;
- *reg_addr = val;
- if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 &&
- !((GSR | gsr_bits) & GSR_CDONE))
+ writel(val, reg_addr);
+ if (wait_event_timeout(gsr_wq, (readl(ac97_reg_base + GSR) | gsr_bits) & GSR_CDONE, 1) <= 0 &&
+ !((readl(ac97_reg_base + GSR) | gsr_bits) & GSR_CDONE)) {
printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n",
- __func__, reg, GSR | gsr_bits);
+ __func__, reg, readl(ac97_reg_base + GSR) | gsr_bits);
+ ret = -EIO;
+ }
- mutex_unlock(&car_mutex);
+ return ret;
}
EXPORT_SYMBOL_GPL(pxa2xx_ac97_write);
@@ -114,20 +120,17 @@ static inline void pxa_ac97_warm_pxa25x(void)
{
gsr_bits = 0;
- GCR |= GCR_WARM_RST | GCR_PRIRDY_IEN | GCR_SECRDY_IEN;
- wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
+ writel(readl(ac97_reg_base + GCR) | (GCR_WARM_RST), ac97_reg_base + GCR);
}
static inline void pxa_ac97_cold_pxa25x(void)
{
- GCR &= GCR_COLD_RST; /* clear everything but nCRST */
- GCR &= ~GCR_COLD_RST; /* then assert nCRST */
+ writel(readl(ac97_reg_base + GCR) & ( GCR_COLD_RST), ac97_reg_base + GCR); /* clear everything but nCRST */
+ writel(readl(ac97_reg_base + GCR) & (~GCR_COLD_RST), ac97_reg_base + GCR); /* then assert nCRST */
gsr_bits = 0;
- GCR = GCR_COLD_RST;
- GCR |= GCR_CDONE_IE|GCR_SDONE_IE;
- wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
+ writel(GCR_COLD_RST, ac97_reg_base + GCR);
}
#endif
@@ -137,69 +140,61 @@ static inline void pxa_ac97_warm_pxa27x(void)
gsr_bits = 0;
/* warm reset broken on Bulverde, so manually keep AC97 reset high */
- pxa27x_assert_ac97reset(reset_gpio, 1);
+ pxa27x_configure_ac97reset(reset_gpio, true);
udelay(10);
- GCR |= GCR_WARM_RST;
- pxa27x_assert_ac97reset(reset_gpio, 0);
+ writel(readl(ac97_reg_base + GCR) | (GCR_WARM_RST), ac97_reg_base + GCR);
+ pxa27x_configure_ac97reset(reset_gpio, false);
udelay(500);
}
static inline void pxa_ac97_cold_pxa27x(void)
{
- GCR &= GCR_COLD_RST; /* clear everything but nCRST */
- GCR &= ~GCR_COLD_RST; /* then assert nCRST */
+ writel(readl(ac97_reg_base + GCR) & ( GCR_COLD_RST), ac97_reg_base + GCR); /* clear everything but nCRST */
+ writel(readl(ac97_reg_base + GCR) & (~GCR_COLD_RST), ac97_reg_base + GCR); /* then assert nCRST */
gsr_bits = 0;
/* PXA27x Developers Manual section 13.5.2.2.1 */
- clk_enable(ac97conf_clk);
+ clk_prepare_enable(ac97conf_clk);
udelay(5);
- clk_disable(ac97conf_clk);
- GCR = GCR_COLD_RST;
- udelay(50);
+ clk_disable_unprepare(ac97conf_clk);
+ writel(GCR_COLD_RST | GCR_WARM_RST, ac97_reg_base + GCR);
}
#endif
#ifdef CONFIG_PXA3xx
static inline void pxa_ac97_warm_pxa3xx(void)
{
- int timeout = 100;
-
gsr_bits = 0;
/* Can't use interrupts */
- GCR |= GCR_WARM_RST;
- while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
- mdelay(1);
+ writel(readl(ac97_reg_base + GCR) | (GCR_WARM_RST), ac97_reg_base + GCR);
}
static inline void pxa_ac97_cold_pxa3xx(void)
{
- int timeout = 1000;
-
/* Hold CLKBPB for 100us */
- GCR = 0;
- GCR = GCR_CLKBPB;
+ writel(0, ac97_reg_base + GCR);
+ writel(GCR_CLKBPB, ac97_reg_base + GCR);
udelay(100);
- GCR = 0;
+ writel(0, ac97_reg_base + GCR);
- GCR &= GCR_COLD_RST; /* clear everything but nCRST */
- GCR &= ~GCR_COLD_RST; /* then assert nCRST */
+ writel(readl(ac97_reg_base + GCR) & ( GCR_COLD_RST), ac97_reg_base + GCR); /* clear everything but nCRST */
+ writel(readl(ac97_reg_base + GCR) & (~GCR_COLD_RST), ac97_reg_base + GCR); /* then assert nCRST */
gsr_bits = 0;
/* Can't use interrupts on PXA3xx */
- GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
+ writel(readl(ac97_reg_base + GCR) & (~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN)), ac97_reg_base + GCR);
- GCR = GCR_WARM_RST | GCR_COLD_RST;
- while (!(GSR & (GSR_PCR | GSR_SCR)) && timeout--)
- mdelay(10);
+ writel(GCR_WARM_RST | GCR_COLD_RST, ac97_reg_base + GCR);
}
#endif
-bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97)
+bool pxa2xx_ac97_try_warm_reset(void)
{
unsigned long gsr;
+ unsigned int timeout = 100;
#ifdef CONFIG_PXA25x
if (cpu_is_pxa25x())
@@ -216,8 +211,12 @@ bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97)
pxa_ac97_warm_pxa3xx();
else
#endif
- BUG();
- gsr = GSR | gsr_bits;
+ snd_BUG();
+
+ while (!((readl(ac97_reg_base + GSR) | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
+ mdelay(1);
+
+ gsr = readl(ac97_reg_base + GSR) | gsr_bits;
if (!(gsr & (GSR_PCR | GSR_SCR))) {
printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n",
__func__, gsr);
@@ -229,9 +228,10 @@ bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97)
}
EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_warm_reset);
-bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97)
+bool pxa2xx_ac97_try_cold_reset(void)
{
unsigned long gsr;
+ unsigned int timeout = 1000;
#ifdef CONFIG_PXA25x
if (cpu_is_pxa25x())
@@ -248,9 +248,12 @@ bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97)
pxa_ac97_cold_pxa3xx();
else
#endif
- BUG();
+ snd_BUG();
+
+ while (!((readl(ac97_reg_base + GSR) | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--)
+ mdelay(1);
- gsr = GSR | gsr_bits;
+ gsr = readl(ac97_reg_base + GSR) | gsr_bits;
if (!(gsr & (GSR_PCR | GSR_SCR))) {
printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n",
__func__, gsr);
@@ -263,10 +266,12 @@ bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97)
EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_cold_reset);
-void pxa2xx_ac97_finish_reset(struct snd_ac97 *ac97)
+void pxa2xx_ac97_finish_reset(void)
{
- GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
- GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
+ u32 gcr = readl(ac97_reg_base + GCR);
+ gcr &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
+ gcr |= GCR_SDONE_IE|GCR_CDONE_IE;
+ writel(gcr, ac97_reg_base + GCR);
}
EXPORT_SYMBOL_GPL(pxa2xx_ac97_finish_reset);
@@ -274,9 +279,9 @@ static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id)
{
long status;
- status = GSR;
+ status = readl(ac97_reg_base + GSR);
if (status) {
- GSR = status;
+ writel(status, ac97_reg_base + GSR);
gsr_bits |= status;
wake_up(&gsr_wq);
@@ -284,9 +289,9 @@ static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id)
since they tend to spuriously trigger when MMC is used
(hardware bug? go figure)... */
if (cpu_is_pxa27x()) {
- MISR = MISR_EOC;
- PISR = PISR_EOC;
- MCSR = MCSR_EOC;
+ writel(MISR_EOC, ac97_reg_base + MISR);
+ writel(PISR_EOC, ac97_reg_base + PISR);
+ writel(MCSR_EOC, ac97_reg_base + MCSR);
}
return IRQ_HANDLED;
@@ -298,25 +303,32 @@ static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id)
#ifdef CONFIG_PM
int pxa2xx_ac97_hw_suspend(void)
{
- GCR |= GCR_ACLINK_OFF;
- clk_disable(ac97_clk);
+ writel(readl(ac97_reg_base + GCR) | (GCR_ACLINK_OFF), ac97_reg_base + GCR);
+ clk_disable_unprepare(ac97_clk);
return 0;
}
EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_suspend);
int pxa2xx_ac97_hw_resume(void)
{
- clk_enable(ac97_clk);
+ clk_prepare_enable(ac97_clk);
return 0;
}
EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_resume);
#endif
-int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev)
+int pxa2xx_ac97_hw_probe(struct platform_device *dev)
{
int ret;
+ int irq;
pxa2xx_audio_ops_t *pdata = dev->dev.platform_data;
+ ac97_reg_base = devm_platform_ioremap_resource(dev, 0);
+ if (IS_ERR(ac97_reg_base)) {
+ dev_err(&dev->dev, "Missing MMIO resource\n");
+ return PTR_ERR(ac97_reg_base);
+ }
+
if (pdata) {
switch (pdata->reset_gpio) {
case 95:
@@ -332,14 +344,38 @@ int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev)
dev_err(&dev->dev, "Invalid reset GPIO %d\n",
pdata->reset_gpio);
}
+ } else if (!pdata && dev->dev.of_node) {
+ pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ pdata->reset_gpio = of_get_named_gpio(dev->dev.of_node,
+ "reset-gpios", 0);
+ if (pdata->reset_gpio == -ENOENT)
+ pdata->reset_gpio = -1;
+ else if (pdata->reset_gpio < 0)
+ return pdata->reset_gpio;
+ reset_gpio = pdata->reset_gpio;
} else {
if (cpu_is_pxa27x())
reset_gpio = 113;
}
if (cpu_is_pxa27x()) {
- /* Use GPIO 113 as AC97 Reset on Bulverde */
- pxa27x_assert_ac97reset(reset_gpio, 0);
+ /*
+ * This gpio is needed for a work-around to a bug in the ac97
+ * controller during warm reset. The direction and level is set
+ * here so that it is an output driven high when switching from
+ * AC97_nRESET alt function to generic gpio.
+ */
+ ret = gpio_request_one(reset_gpio, GPIOF_OUT_INIT_HIGH,
+ "pxa27x ac97 reset");
+ if (ret < 0) {
+ pr_err("%s: gpio_request_one() failed: %d\n",
+ __func__, ret);
+ goto err_conf;
+ }
+ pxa27x_configure_ac97reset(reset_gpio, false);
+
ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK");
if (IS_ERR(ac97conf_clk)) {
ret = PTR_ERR(ac97conf_clk);
@@ -355,18 +391,24 @@ int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev)
goto err_clk;
}
- ret = clk_enable(ac97_clk);
+ ret = clk_prepare_enable(ac97_clk);
if (ret)
goto err_clk2;
- ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, IRQF_DISABLED, "AC97", NULL);
+ irq = platform_get_irq(dev, 0);
+ if (irq < 0) {
+ ret = irq;
+ goto err_irq;
+ }
+
+ ret = request_irq(irq, pxa2xx_ac97_irq, 0, "AC97", NULL);
if (ret < 0)
goto err_irq;
return 0;
err_irq:
- GCR |= GCR_ACLINK_OFF;
+ writel(readl(ac97_reg_base + GCR) | (GCR_ACLINK_OFF), ac97_reg_base + GCR);
err_clk2:
clk_put(ac97_clk);
ac97_clk = NULL;
@@ -382,18 +424,38 @@ EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_probe);
void pxa2xx_ac97_hw_remove(struct platform_device *dev)
{
- GCR |= GCR_ACLINK_OFF;
- free_irq(IRQ_AC97, NULL);
+ if (cpu_is_pxa27x())
+ gpio_free(reset_gpio);
+ writel(readl(ac97_reg_base + GCR) | (GCR_ACLINK_OFF), ac97_reg_base + GCR);
+ free_irq(platform_get_irq(dev, 0), NULL);
if (ac97conf_clk) {
clk_put(ac97conf_clk);
ac97conf_clk = NULL;
}
- clk_disable(ac97_clk);
+ clk_disable_unprepare(ac97_clk);
clk_put(ac97_clk);
ac97_clk = NULL;
}
EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_remove);
+u32 pxa2xx_ac97_read_modr(void)
+{
+ if (!ac97_reg_base)
+ return 0;
+
+ return readl(ac97_reg_base + MODR);
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_read_modr);
+
+u32 pxa2xx_ac97_read_misr(void)
+{
+ if (!ac97_reg_base)
+ return 0;
+
+ return readl(ac97_reg_base + MISR);
+}
+EXPORT_SYMBOL_GPL(pxa2xx_ac97_read_misr);
+
MODULE_AUTHOR("Nicolas Pitre");
MODULE_DESCRIPTION("Intel/Marvell PXA sound library");
MODULE_LICENSE("GPL");
diff --git a/sound/arm/pxa2xx-ac97-regs.h b/sound/arm/pxa2xx-ac97-regs.h
new file mode 100644
index 000000000000..ae638a1b919b
--- /dev/null
+++ b/sound/arm/pxa2xx-ac97-regs.h
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_ARCH_REGS_AC97_H
+#define __ASM_ARCH_REGS_AC97_H
+
+/*
+ * AC97 Controller registers
+ */
+
+#define POCR (0x0000) /* PCM Out Control Register */
+#define POCR_FEIE (1 << 3) /* FIFO Error Interrupt Enable */
+#define POCR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
+
+#define PICR (0x0004) /* PCM In Control Register */
+#define PICR_FEIE (1 << 3) /* FIFO Error Interrupt Enable */
+#define PICR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
+
+#define MCCR (0x0008) /* Mic In Control Register */
+#define MCCR_FEIE (1 << 3) /* FIFO Error Interrupt Enable */
+#define MCCR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
+
+#define GCR (0x000C) /* Global Control Register */
+#ifdef CONFIG_PXA3xx
+#define GCR_CLKBPB (1 << 31) /* Internal clock enable */
+#endif
+#define GCR_nDMAEN (1 << 24) /* non DMA Enable */
+#define GCR_CDONE_IE (1 << 19) /* Command Done Interrupt Enable */
+#define GCR_SDONE_IE (1 << 18) /* Status Done Interrupt Enable */
+#define GCR_SECRDY_IEN (1 << 9) /* Secondary Ready Interrupt Enable */
+#define GCR_PRIRDY_IEN (1 << 8) /* Primary Ready Interrupt Enable */
+#define GCR_SECRES_IEN (1 << 5) /* Secondary Resume Interrupt Enable */
+#define GCR_PRIRES_IEN (1 << 4) /* Primary Resume Interrupt Enable */
+#define GCR_ACLINK_OFF (1 << 3) /* AC-link Shut Off */
+#define GCR_WARM_RST (1 << 2) /* AC97 Warm Reset */
+#define GCR_COLD_RST (1 << 1) /* AC'97 Cold Reset (0 = active) */
+#define GCR_GIE (1 << 0) /* Codec GPI Interrupt Enable */
+
+#define POSR (0x0010) /* PCM Out Status Register */
+#define POSR_FIFOE (1 << 4) /* FIFO error */
+#define POSR_FSR (1 << 2) /* FIFO Service Request */
+
+#define PISR (0x0014) /* PCM In Status Register */
+#define PISR_FIFOE (1 << 4) /* FIFO error */
+#define PISR_EOC (1 << 3) /* DMA End-of-Chain (exclusive clear) */
+#define PISR_FSR (1 << 2) /* FIFO Service Request */
+
+#define MCSR (0x0018) /* Mic In Status Register */
+#define MCSR_FIFOE (1 << 4) /* FIFO error */
+#define MCSR_EOC (1 << 3) /* DMA End-of-Chain (exclusive clear) */
+#define MCSR_FSR (1 << 2) /* FIFO Service Request */
+
+#define GSR (0x001C) /* Global Status Register */
+#define GSR_CDONE (1 << 19) /* Command Done */
+#define GSR_SDONE (1 << 18) /* Status Done */
+#define GSR_RDCS (1 << 15) /* Read Completion Status */
+#define GSR_BIT3SLT12 (1 << 14) /* Bit 3 of slot 12 */
+#define GSR_BIT2SLT12 (1 << 13) /* Bit 2 of slot 12 */
+#define GSR_BIT1SLT12 (1 << 12) /* Bit 1 of slot 12 */
+#define GSR_SECRES (1 << 11) /* Secondary Resume Interrupt */
+#define GSR_PRIRES (1 << 10) /* Primary Resume Interrupt */
+#define GSR_SCR (1 << 9) /* Secondary Codec Ready */
+#define GSR_PCR (1 << 8) /* Primary Codec Ready */
+#define GSR_MCINT (1 << 7) /* Mic In Interrupt */
+#define GSR_POINT (1 << 6) /* PCM Out Interrupt */
+#define GSR_PIINT (1 << 5) /* PCM In Interrupt */
+#define GSR_ACOFFD (1 << 3) /* AC-link Shut Off Done */
+#define GSR_MOINT (1 << 2) /* Modem Out Interrupt */
+#define GSR_MIINT (1 << 1) /* Modem In Interrupt */
+#define GSR_GSCI (1 << 0) /* Codec GPI Status Change Interrupt */
+
+#define CAR (0x0020) /* CODEC Access Register */
+#define CAR_CAIP (1 << 0) /* Codec Access In Progress */
+
+#define PCDR (0x0040) /* PCM FIFO Data Register */
+#define MCDR (0x0060) /* Mic-in FIFO Data Register */
+
+#define MOCR (0x0100) /* Modem Out Control Register */
+#define MOCR_FEIE (1 << 3) /* FIFO Error */
+#define MOCR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
+
+#define MICR (0x0108) /* Modem In Control Register */
+#define MICR_FEIE (1 << 3) /* FIFO Error */
+#define MICR_FSRIE (1 << 1) /* FIFO Service Request Interrupt Enable */
+
+#define MOSR (0x0110) /* Modem Out Status Register */
+#define MOSR_FIFOE (1 << 4) /* FIFO error */
+#define MOSR_FSR (1 << 2) /* FIFO Service Request */
+
+#define MISR (0x0118) /* Modem In Status Register */
+#define MISR_FIFOE (1 << 4) /* FIFO error */
+#define MISR_EOC (1 << 3) /* DMA End-of-Chain (exclusive clear) */
+#define MISR_FSR (1 << 2) /* FIFO Service Request */
+
+#define MODR (0x0140) /* Modem FIFO Data Register */
+
+#define PAC_REG_BASE (0x0200) /* Primary Audio Codec */
+#define SAC_REG_BASE (0x0300) /* Secondary Audio Codec */
+#define PMC_REG_BASE (0x0400) /* Primary Modem Codec */
+#define SMC_REG_BASE (0x0500) /* Secondary Modem Codec */
+
+#endif /* __ASM_ARCH_REGS_AC97_H */
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index 5d9411839cd7..77b11616a7ee 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -1,92 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip.
*
* Author: Nicolas Pitre
* Created: Dec 02, 2004
* Copyright: MontaVista Software Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/init.h>
+#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
-#include <mach/regs-ac97.h>
-#include <mach/audio.h>
-
-#include "pxa2xx-pcm.h"
+#include <linux/platform_data/asoc-pxa.h>
-static void pxa2xx_ac97_reset(struct snd_ac97 *ac97)
+static void pxa2xx_ac97_legacy_reset(struct snd_ac97 *ac97)
{
- if (!pxa2xx_ac97_try_cold_reset(ac97)) {
- pxa2xx_ac97_try_warm_reset(ac97);
- }
+ if (!pxa2xx_ac97_try_cold_reset())
+ pxa2xx_ac97_try_warm_reset();
- pxa2xx_ac97_finish_reset(ac97);
+ pxa2xx_ac97_finish_reset();
}
-static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
- .read = pxa2xx_ac97_read,
- .write = pxa2xx_ac97_write,
- .reset = pxa2xx_ac97_reset,
-};
+static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97,
+ unsigned short reg)
+{
+ int ret;
-static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_out = {
- .name = "AC97 PCM out",
- .dev_addr = __PREG(PCDR),
- .drcmr = &DRCMR(12),
- .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
- DCMD_BURST32 | DCMD_WIDTH4,
-};
+ ret = pxa2xx_ac97_read(ac97->num, reg);
+ if (ret < 0)
+ return 0;
+ else
+ return (unsigned short)(ret & 0xffff);
+}
+
+static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97,
+ unsigned short reg, unsigned short val)
+{
+ pxa2xx_ac97_write(ac97->num, reg, val);
+}
-static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_in = {
- .name = "AC97 PCM in",
- .dev_addr = __PREG(PCDR),
- .drcmr = &DRCMR(11),
- .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
- DCMD_BURST32 | DCMD_WIDTH4,
+static const struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
+ .read = pxa2xx_ac97_legacy_read,
+ .write = pxa2xx_ac97_legacy_write,
+ .reset = pxa2xx_ac97_legacy_reset,
};
static struct snd_pcm *pxa2xx_ac97_pcm;
static struct snd_ac97 *pxa2xx_ac97_ac97;
-static int pxa2xx_ac97_pcm_startup(struct snd_pcm_substream *substream)
+static int pxa2xx_ac97_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
pxa2xx_audio_ops_t *platform_ops;
- int r;
+ int ret, i;
+
+ ret = pxa2xx_pcm_open(substream);
+ if (ret)
+ return ret;
runtime->hw.channels_min = 2;
runtime->hw.channels_max = 2;
- r = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- AC97_RATES_FRONT_DAC : AC97_RATES_ADC;
- runtime->hw.rates = pxa2xx_ac97_ac97->rates[r];
+ i = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ AC97_RATES_FRONT_DAC : AC97_RATES_ADC;
+ runtime->hw.rates = pxa2xx_ac97_ac97->rates[i];
snd_pcm_limit_hw_rates(runtime);
- platform_ops = substream->pcm->card->dev->platform_data;
- if (platform_ops && platform_ops->startup)
- return platform_ops->startup(substream, platform_ops->priv);
- else
- return 0;
+ platform_ops = substream->pcm->card->dev->platform_data;
+ if (platform_ops && platform_ops->startup) {
+ ret = platform_ops->startup(substream, platform_ops->priv);
+ if (ret < 0)
+ pxa2xx_pcm_close(substream);
+ }
+
+ return ret;
}
-static void pxa2xx_ac97_pcm_shutdown(struct snd_pcm_substream *substream)
+static int pxa2xx_ac97_pcm_close(struct snd_pcm_substream *substream)
{
pxa2xx_audio_ops_t *platform_ops;
- platform_ops = substream->pcm->card->dev->platform_data;
+ platform_ops = substream->pcm->card->dev->platform_data;
if (platform_ops && platform_ops->shutdown)
platform_ops->shutdown(substream, platform_ops->priv);
+
+ return 0;
}
static int pxa2xx_ac97_pcm_prepare(struct snd_pcm_substream *substream)
@@ -94,25 +102,20 @@ static int pxa2xx_ac97_pcm_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
- return snd_ac97_set_rate(pxa2xx_ac97_ac97, reg, runtime->rate);
-}
+ int ret;
-static struct pxa2xx_pcm_client pxa2xx_ac97_pcm_client = {
- .playback_params = &pxa2xx_ac97_pcm_out,
- .capture_params = &pxa2xx_ac97_pcm_in,
- .startup = pxa2xx_ac97_pcm_startup,
- .shutdown = pxa2xx_ac97_pcm_shutdown,
- .prepare = pxa2xx_ac97_pcm_prepare,
-};
+ ret = pxa2xx_pcm_prepare(substream);
+ if (ret < 0)
+ return ret;
-#ifdef CONFIG_PM
+ return snd_ac97_set_rate(pxa2xx_ac97_ac97, reg, runtime->rate);
+}
-static int pxa2xx_ac97_do_suspend(struct snd_card *card, pm_message_t state)
+static int pxa2xx_ac97_do_suspend(struct snd_card *card)
{
pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
- snd_pcm_suspend_all(pxa2xx_ac97_pcm);
snd_ac97_suspend(pxa2xx_ac97_ac97);
if (platform_ops && platform_ops->suspend)
platform_ops->suspend(platform_ops->priv);
@@ -143,7 +146,7 @@ static int pxa2xx_ac97_suspend(struct device *dev)
int ret = 0;
if (card)
- ret = pxa2xx_ac97_do_suspend(card, PMSG_SUSPEND);
+ ret = pxa2xx_ac97_do_suspend(card);
return ret;
}
@@ -159,13 +162,45 @@ static int pxa2xx_ac97_resume(struct device *dev)
return ret;
}
-static const struct dev_pm_ops pxa2xx_ac97_pm_ops = {
- .suspend = pxa2xx_ac97_suspend,
- .resume = pxa2xx_ac97_resume,
+static DEFINE_SIMPLE_DEV_PM_OPS(pxa2xx_ac97_pm_ops, pxa2xx_ac97_suspend, pxa2xx_ac97_resume);
+
+static const struct snd_pcm_ops pxa2xx_ac97_pcm_ops = {
+ .open = pxa2xx_ac97_pcm_open,
+ .close = pxa2xx_ac97_pcm_close,
+ .hw_params = pxa2xx_pcm_hw_params,
+ .prepare = pxa2xx_ac97_pcm_prepare,
+ .trigger = pxa2xx_pcm_trigger,
+ .pointer = pxa2xx_pcm_pointer,
};
-#endif
-static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
+
+static int pxa2xx_ac97_pcm_new(struct snd_card *card)
+{
+ struct snd_pcm *pcm;
+ int ret;
+
+ ret = snd_pcm_new(card, "PXA2xx-PCM", 0, 1, 1, &pcm);
+ if (ret)
+ goto out;
+
+ ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
+ if (ret)
+ goto out;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pxa2xx_ac97_pcm_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pxa2xx_ac97_pcm_ops);
+ ret = pxa2xx_pcm_preallocate_dma_buffer(pcm);
+ if (ret)
+ goto out;
+
+ pxa2xx_ac97_pcm = pcm;
+ ret = 0;
+
+ out:
+ return ret;
+}
+
+static int pxa2xx_ac97_probe(struct platform_device *dev)
{
struct snd_card *card;
struct snd_ac97_bus *ac97_bus;
@@ -179,15 +214,14 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
goto err_dev;
}
- ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
- THIS_MODULE, 0, &card);
+ ret = snd_card_new(&dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, 0, &card);
if (ret < 0)
goto err;
- card->dev = &dev->dev;
- strncpy(card->driver, dev->dev.driver->name, sizeof(card->driver));
+ strscpy(card->driver, dev->dev.driver->name, sizeof(card->driver));
- ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm);
+ ret = pxa2xx_ac97_pcm_new(card);
if (ret)
goto err;
@@ -210,7 +244,6 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
if (pdata && pdata->codec_pdata[0])
snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata[0]);
- snd_card_set_dev(card, &dev->dev);
ret = snd_card_register(card);
if (ret == 0) {
platform_set_drvdata(dev, card);
@@ -226,43 +259,26 @@ err_dev:
return ret;
}
-static int __devexit pxa2xx_ac97_remove(struct platform_device *dev)
+static void pxa2xx_ac97_remove(struct platform_device *dev)
{
struct snd_card *card = platform_get_drvdata(dev);
if (card) {
snd_card_free(card);
- platform_set_drvdata(dev, NULL);
pxa2xx_ac97_hw_remove(dev);
}
-
- return 0;
}
static struct platform_driver pxa2xx_ac97_driver = {
.probe = pxa2xx_ac97_probe,
- .remove = __devexit_p(pxa2xx_ac97_remove),
+ .remove = pxa2xx_ac97_remove,
.driver = {
.name = "pxa2xx-ac97",
- .owner = THIS_MODULE,
-#ifdef CONFIG_PM
.pm = &pxa2xx_ac97_pm_ops,
-#endif
},
};
-static int __init pxa2xx_ac97_init(void)
-{
- return platform_driver_register(&pxa2xx_ac97_driver);
-}
-
-static void __exit pxa2xx_ac97_exit(void)
-{
- platform_driver_unregister(&pxa2xx_ac97_driver);
-}
-
-module_init(pxa2xx_ac97_init);
-module_exit(pxa2xx_ac97_exit);
+module_platform_driver(pxa2xx_ac97_driver);
MODULE_AUTHOR("Nicolas Pitre");
MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c
index 8808b82311b1..571e9d909cdf 100644
--- a/sound/arm/pxa2xx-pcm-lib.c
+++ b/sound/arm/pxa2xx-pcm-lib.c
@@ -1,21 +1,16 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
+// SPDX-License-Identifier: GPL-2.0-only
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/dma/pxa-dma.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/pxa2xx-lib.h>
-
-#include <mach/dma.h>
-
-#include "pxa2xx-pcm.h"
+#include <sound/dmaengine_pcm.h>
static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
@@ -24,158 +19,77 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE,
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
.period_bytes_min = 32,
.period_bytes_max = 8192 - 32,
.periods_min = 1,
- .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc),
+ .periods_max = 256,
.buffer_bytes_max = 128 * 1024,
.fifo_size = 32,
};
-int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd = runtime->private_data;
- size_t totsize = params_buffer_bytes(params);
- size_t period = params_period_bytes(params);
- pxa_dma_desc *dma_desc;
- dma_addr_t dma_buff_phys, next_desc_phys;
-
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = totsize;
-
- dma_desc = rtd->dma_desc_array;
- next_desc_phys = rtd->dma_desc_array_phys;
- dma_buff_phys = runtime->dma_addr;
- do {
- next_desc_phys += sizeof(pxa_dma_desc);
- dma_desc->ddadr = next_desc_phys;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dma_desc->dsadr = dma_buff_phys;
- dma_desc->dtadr = rtd->params->dev_addr;
- } else {
- dma_desc->dsadr = rtd->params->dev_addr;
- dma_desc->dtadr = dma_buff_phys;
- }
- if (period > totsize)
- period = totsize;
- dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN;
- dma_desc++;
- dma_buff_phys += period;
- } while (totsize -= period);
- dma_desc[-1].ddadr = rtd->dma_desc_array_phys;
+ struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_dmaengine_dai_dma_data *dma_params;
+ struct dma_slave_config config;
+ int ret;
- return 0;
-}
-EXPORT_SYMBOL(__pxa2xx_pcm_hw_params);
+ dma_params = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
+ if (!dma_params)
+ return 0;
-int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
+ ret = snd_hwparams_to_dma_slave_config(substream, params, &config);
+ if (ret)
+ return ret;
+
+ snd_dmaengine_pcm_set_config_from_dai_data(substream,
+ snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream),
+ &config);
- if (rtd && rtd->params && rtd->params->drcmr)
- *rtd->params->drcmr = 0;
+ ret = dmaengine_slave_config(chan, &config);
+ if (ret)
+ return ret;
- snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
-EXPORT_SYMBOL(__pxa2xx_pcm_hw_free);
+EXPORT_SYMBOL(pxa2xx_pcm_hw_params);
int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
- struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
- int ret = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
- DCSR(prtd->dma_ch) = DCSR_RUN;
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- DCSR(prtd->dma_ch) &= ~DCSR_RUN;
- break;
-
- case SNDRV_PCM_TRIGGER_RESUME:
- DCSR(prtd->dma_ch) |= DCSR_RUN;
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
- DCSR(prtd->dma_ch) |= DCSR_RUN;
- break;
-
- default:
- ret = -EINVAL;
- }
-
- return ret;
+ return snd_dmaengine_pcm_trigger(substream, cmd);
}
EXPORT_SYMBOL(pxa2xx_pcm_trigger);
snd_pcm_uframes_t
pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *prtd = runtime->private_data;
-
- dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch);
- snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
-
- if (x == runtime->buffer_size)
- x = 0;
- return x;
+ return snd_dmaengine_pcm_pointer(substream);
}
EXPORT_SYMBOL(pxa2xx_pcm_pointer);
-int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
+int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
{
- struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
-
- if (!prtd || !prtd->params)
- return 0;
-
- DCSR(prtd->dma_ch) &= ~DCSR_RUN;
- DCSR(prtd->dma_ch) = 0;
- DCMD(prtd->dma_ch) = 0;
- *prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD;
-
return 0;
}
-EXPORT_SYMBOL(__pxa2xx_pcm_prepare);
+EXPORT_SYMBOL(pxa2xx_pcm_prepare);
-void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
-{
- struct snd_pcm_substream *substream = dev_id;
- struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
- int dcsr;
-
- dcsr = DCSR(dma_ch);
- DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN;
-
- if (dcsr & DCSR_ENDINTR) {
- snd_pcm_period_elapsed(substream);
- } else {
- printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
- rtd->params->name, dma_ch, dcsr);
- snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
- }
-}
-EXPORT_SYMBOL(pxa2xx_pcm_dma_irq);
-
-int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
+int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd;
+ struct snd_dmaengine_dai_dma_data *dma_params;
int ret;
runtime->hw = pxa2xx_pcm_hardware;
+ dma_params = snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd, 0), substream);
+ if (!dma_params)
+ return 0;
+
/*
* For mysterious reasons (and despite what the manual says)
* playback samples are lost if the DMA count is not a multiple
@@ -184,98 +98,97 @@ int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
ret = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
if (ret)
- goto out;
+ return ret;
ret = snd_pcm_hw_constraint_step(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
if (ret)
- goto out;
+ return ret;
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0)
- goto out;
-
- ret = -ENOMEM;
- rtd = kzalloc(sizeof(*rtd), GFP_KERNEL);
- if (!rtd)
- goto out;
- rtd->dma_desc_array =
- dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE,
- &rtd->dma_desc_array_phys, GFP_KERNEL);
- if (!rtd->dma_desc_array)
- goto err1;
-
- rtd->dma_ch = -1;
- runtime->private_data = rtd;
- return 0;
+ return ret;
- err1:
- kfree(rtd);
- out:
- return ret;
+ return snd_dmaengine_pcm_open(
+ substream, dma_request_slave_channel(snd_soc_rtd_to_cpu(rtd, 0)->dev,
+ dma_params->chan_name));
}
-EXPORT_SYMBOL(__pxa2xx_pcm_open);
+EXPORT_SYMBOL(pxa2xx_pcm_open);
-int __pxa2xx_pcm_close(struct snd_pcm_substream *substream)
+int pxa2xx_pcm_close(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd = runtime->private_data;
+ return snd_dmaengine_pcm_close_release_chan(substream);
+}
+EXPORT_SYMBOL(pxa2xx_pcm_close);
- dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
- rtd->dma_desc_array, rtd->dma_desc_array_phys);
- kfree(rtd);
- return 0;
+int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm)
+{
+ size_t size = pxa2xx_pcm_hardware.buffer_bytes_max;
+
+ return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC,
+ pcm->card->dev, size);
}
-EXPORT_SYMBOL(__pxa2xx_pcm_close);
+EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer);
-int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
+int pxa2xx_soc_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- return dma_mmap_writecombine(substream->pcm->card->dev, vma,
- runtime->dma_area,
- runtime->dma_addr,
- runtime->dma_bytes);
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+ int ret;
+
+ ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ return pxa2xx_pcm_preallocate_dma_buffer(pcm);
}
-EXPORT_SYMBOL(pxa2xx_pcm_mmap);
+EXPORT_SYMBOL(pxa2xx_soc_pcm_new);
-int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+int pxa2xx_soc_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = pxa2xx_pcm_hardware.buffer_bytes_max;
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_writecombine(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
- if (!buf->area)
- return -ENOMEM;
- buf->bytes = size;
- return 0;
+ return pxa2xx_pcm_open(substream);
}
-EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer);
+EXPORT_SYMBOL(pxa2xx_soc_pcm_open);
-void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+int pxa2xx_soc_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return pxa2xx_pcm_close(substream);
+}
+EXPORT_SYMBOL(pxa2xx_soc_pcm_close);
+
+int pxa2xx_soc_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ return pxa2xx_pcm_hw_params(substream, params);
+}
+EXPORT_SYMBOL(pxa2xx_soc_pcm_hw_params);
+
+int pxa2xx_soc_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return pxa2xx_pcm_prepare(substream);
+}
+EXPORT_SYMBOL(pxa2xx_soc_pcm_prepare);
+
+int pxa2xx_soc_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ return pxa2xx_pcm_trigger(substream, cmd);
+}
+EXPORT_SYMBOL(pxa2xx_soc_pcm_trigger);
+
+snd_pcm_uframes_t
+pxa2xx_soc_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < 2; stream++) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
- dma_free_writecombine(pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- }
+ return pxa2xx_pcm_pointer(substream);
}
-EXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers);
+EXPORT_SYMBOL(pxa2xx_soc_pcm_pointer);
MODULE_AUTHOR("Nicolas Pitre");
MODULE_DESCRIPTION("Intel PXA2xx sound library");
diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c
deleted file mode 100644
index 535704f77496..000000000000
--- a/sound/arm/pxa2xx-pcm.c
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * linux/sound/arm/pxa2xx-pcm.c -- ALSA PCM interface for the Intel PXA2xx chip
- *
- * Author: Nicolas Pitre
- * Created: Nov 30, 2004
- * Copyright: (C) 2004 MontaVista Software, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <sound/core.h>
-#include <sound/pxa2xx-lib.h>
-
-#include "pxa2xx-pcm.h"
-
-static int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct pxa2xx_pcm_client *client = substream->private_data;
-
- __pxa2xx_pcm_prepare(substream);
-
- return client->prepare(substream);
-}
-
-static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
-{
- struct pxa2xx_pcm_client *client = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct pxa2xx_runtime_data *rtd;
- int ret;
-
- ret = __pxa2xx_pcm_open(substream);
- if (ret)
- goto out;
-
- rtd = runtime->private_data;
-
- rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- client->playback_params : client->capture_params;
- ret = pxa_request_dma(rtd->params->name, DMA_PRIO_LOW,
- pxa2xx_pcm_dma_irq, substream);
- if (ret < 0)
- goto err2;
- rtd->dma_ch = ret;
-
- ret = client->startup(substream);
- if (!ret)
- goto out;
-
- pxa_free_dma(rtd->dma_ch);
- err2:
- __pxa2xx_pcm_close(substream);
- out:
- return ret;
-}
-
-static int pxa2xx_pcm_close(struct snd_pcm_substream *substream)
-{
- struct pxa2xx_pcm_client *client = substream->private_data;
- struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
-
- pxa_free_dma(rtd->dma_ch);
- client->shutdown(substream);
-
- return __pxa2xx_pcm_close(substream);
-}
-
-static struct snd_pcm_ops pxa2xx_pcm_ops = {
- .open = pxa2xx_pcm_open,
- .close = pxa2xx_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = __pxa2xx_pcm_hw_params,
- .hw_free = __pxa2xx_pcm_hw_free,
- .prepare = pxa2xx_pcm_prepare,
- .trigger = pxa2xx_pcm_trigger,
- .pointer = pxa2xx_pcm_pointer,
- .mmap = pxa2xx_pcm_mmap,
-};
-
-static u64 pxa2xx_pcm_dmamask = 0xffffffff;
-
-int pxa2xx_pcm_new(struct snd_card *card, struct pxa2xx_pcm_client *client,
- struct snd_pcm **rpcm)
-{
- struct snd_pcm *pcm;
- int play = client->playback_params ? 1 : 0;
- int capt = client->capture_params ? 1 : 0;
- int ret;
-
- ret = snd_pcm_new(card, "PXA2xx-PCM", 0, play, capt, &pcm);
- if (ret)
- goto out;
-
- pcm->private_data = client;
- pcm->private_free = pxa2xx_pcm_free_dma_buffers;
-
- if (!card->dev->dma_mask)
- card->dev->dma_mask = &pxa2xx_pcm_dmamask;
- if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = 0xffffffff;
-
- if (play) {
- int stream = SNDRV_PCM_STREAM_PLAYBACK;
- snd_pcm_set_ops(pcm, stream, &pxa2xx_pcm_ops);
- ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, stream);
- if (ret)
- goto out;
- }
- if (capt) {
- int stream = SNDRV_PCM_STREAM_CAPTURE;
- snd_pcm_set_ops(pcm, stream, &pxa2xx_pcm_ops);
- ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, stream);
- if (ret)
- goto out;
- }
-
- if (rpcm)
- *rpcm = pcm;
- ret = 0;
-
- out:
- return ret;
-}
-
-EXPORT_SYMBOL(pxa2xx_pcm_new);
-
-MODULE_AUTHOR("Nicolas Pitre");
-MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module");
-MODULE_LICENSE("GPL");
diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h
deleted file mode 100644
index 65f86b56ba42..000000000000
--- a/sound/arm/pxa2xx-pcm.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * linux/sound/arm/pxa2xx-pcm.h -- ALSA PCM interface for the Intel PXA2xx chip
- *
- * Author: Nicolas Pitre
- * Created: Nov 30, 2004
- * Copyright: MontaVista Software, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#include <mach/dma.h>
-
-struct pxa2xx_runtime_data {
- int dma_ch;
- struct pxa2xx_pcm_dma_params *params;
- pxa_dma_desc *dma_desc_array;
- dma_addr_t dma_desc_array_phys;
-};
-
-struct pxa2xx_pcm_client {
- struct pxa2xx_pcm_dma_params *playback_params;
- struct pxa2xx_pcm_dma_params *capture_params;
- int (*startup)(struct snd_pcm_substream *);
- void (*shutdown)(struct snd_pcm_substream *);
- int (*prepare)(struct snd_pcm_substream *);
-};
-
-extern int pxa2xx_pcm_new(struct snd_card *, struct pxa2xx_pcm_client *, struct snd_pcm **);
-
diff --git a/sound/atmel/Kconfig b/sound/atmel/Kconfig
index 94de43a096f1..6ed2d4a73374 100644
--- a/sound/atmel/Kconfig
+++ b/sound/atmel/Kconfig
@@ -1,18 +1,12 @@
-menu "Atmel devices (AVR32 and AT91)"
- depends on AVR32 || ARCH_AT91
-
-config SND_ATMEL_ABDAC
- tristate "Atmel Audio Bitstream DAC (ABDAC) driver"
- select SND_PCM
- depends on DW_DMAC && AVR32
- help
- ALSA sound driver for the Atmel Audio Bitstream DAC (ABDAC).
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Atmel devices (AT91)"
+ depends on ARCH_AT91
config SND_ATMEL_AC97C
tristate "Atmel AC97 Controller (AC97C) driver"
select SND_PCM
select SND_AC97_CODEC
- depends on (DW_DMAC && AVR32) || ARCH_AT91
+ depends on ARCH_AT91
help
ALSA sound driver for the Atmel AC97 controller.
diff --git a/sound/atmel/Makefile b/sound/atmel/Makefile
index 219dcfac6086..a8917d1854c7 100644
--- a/sound/atmel/Makefile
+++ b/sound/atmel/Makefile
@@ -1,5 +1,4 @@
-snd-atmel-abdac-objs := abdac.o
-snd-atmel-ac97c-objs := ac97c.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-atmel-ac97c-y := ac97c.o
-obj-$(CONFIG_SND_ATMEL_ABDAC) += snd-atmel-abdac.o
obj-$(CONFIG_SND_ATMEL_AC97C) += snd-atmel-ac97c.o
diff --git a/sound/atmel/abdac.c b/sound/atmel/abdac.c
deleted file mode 100644
index f2f41c854221..000000000000
--- a/sound/atmel/abdac.c
+++ /dev/null
@@ -1,602 +0,0 @@
-/*
- * Driver for the Atmel on-chip Audio Bitstream DAC (ABDAC)
- *
- * Copyright (C) 2006-2009 Atmel Corporation
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- */
-#include <linux/clk.h>
-#include <linux/bitmap.h>
-#include <linux/dw_dmac.h>
-#include <linux/dmaengine.h>
-#include <linux/dma-mapping.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-
-#include <sound/core.h>
-#include <sound/initval.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/atmel-abdac.h>
-
-/* DAC register offsets */
-#define DAC_DATA 0x0000
-#define DAC_CTRL 0x0008
-#define DAC_INT_MASK 0x000c
-#define DAC_INT_EN 0x0010
-#define DAC_INT_DIS 0x0014
-#define DAC_INT_CLR 0x0018
-#define DAC_INT_STATUS 0x001c
-
-/* Bitfields in CTRL */
-#define DAC_SWAP_OFFSET 30
-#define DAC_SWAP_SIZE 1
-#define DAC_EN_OFFSET 31
-#define DAC_EN_SIZE 1
-
-/* Bitfields in INT_MASK/INT_EN/INT_DIS/INT_STATUS/INT_CLR */
-#define DAC_UNDERRUN_OFFSET 28
-#define DAC_UNDERRUN_SIZE 1
-#define DAC_TX_READY_OFFSET 29
-#define DAC_TX_READY_SIZE 1
-
-/* Bit manipulation macros */
-#define DAC_BIT(name) \
- (1 << DAC_##name##_OFFSET)
-#define DAC_BF(name, value) \
- (((value) & ((1 << DAC_##name##_SIZE) - 1)) \
- << DAC_##name##_OFFSET)
-#define DAC_BFEXT(name, value) \
- (((value) >> DAC_##name##_OFFSET) \
- & ((1 << DAC_##name##_SIZE) - 1))
-#define DAC_BFINS(name, value, old) \
- (((old) & ~(((1 << DAC_##name##_SIZE) - 1) \
- << DAC_##name##_OFFSET)) \
- | DAC_BF(name, value))
-
-/* Register access macros */
-#define dac_readl(port, reg) \
- __raw_readl((port)->regs + DAC_##reg)
-#define dac_writel(port, reg, value) \
- __raw_writel((value), (port)->regs + DAC_##reg)
-
-/*
- * ABDAC supports a maximum of 6 different rates from a generic clock. The
- * generic clock has a power of two divider, which gives 6 steps from 192 kHz
- * to 5112 Hz.
- */
-#define MAX_NUM_RATES 6
-/* ALSA seems to use rates between 192000 Hz and 5112 Hz. */
-#define RATE_MAX 192000
-#define RATE_MIN 5112
-
-enum {
- DMA_READY = 0,
-};
-
-struct atmel_abdac_dma {
- struct dma_chan *chan;
- struct dw_cyclic_desc *cdesc;
-};
-
-struct atmel_abdac {
- struct clk *pclk;
- struct clk *sample_clk;
- struct platform_device *pdev;
- struct atmel_abdac_dma dma;
-
- struct snd_pcm_hw_constraint_list constraints_rates;
- struct snd_pcm_substream *substream;
- struct snd_card *card;
- struct snd_pcm *pcm;
-
- void __iomem *regs;
- unsigned long flags;
- unsigned int rates[MAX_NUM_RATES];
- unsigned int rates_num;
- int irq;
-};
-
-#define get_dac(card) ((struct atmel_abdac *)(card)->private_data)
-
-/* This function is called by the DMA driver. */
-static void atmel_abdac_dma_period_done(void *arg)
-{
- struct atmel_abdac *dac = arg;
- snd_pcm_period_elapsed(dac->substream);
-}
-
-static int atmel_abdac_prepare_dma(struct atmel_abdac *dac,
- struct snd_pcm_substream *substream,
- enum dma_data_direction direction)
-{
- struct dma_chan *chan = dac->dma.chan;
- struct dw_cyclic_desc *cdesc;
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long buffer_len, period_len;
-
- /*
- * We don't do DMA on "complex" transfers, i.e. with
- * non-halfword-aligned buffers or lengths.
- */
- if (runtime->dma_addr & 1 || runtime->buffer_size & 1) {
- dev_dbg(&dac->pdev->dev, "too complex transfer\n");
- return -EINVAL;
- }
-
- buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
- period_len = frames_to_bytes(runtime, runtime->period_size);
-
- cdesc = dw_dma_cyclic_prep(chan, runtime->dma_addr, buffer_len,
- period_len, DMA_TO_DEVICE);
- if (IS_ERR(cdesc)) {
- dev_dbg(&dac->pdev->dev, "could not prepare cyclic DMA\n");
- return PTR_ERR(cdesc);
- }
-
- cdesc->period_callback = atmel_abdac_dma_period_done;
- cdesc->period_callback_param = dac;
-
- dac->dma.cdesc = cdesc;
-
- set_bit(DMA_READY, &dac->flags);
-
- return 0;
-}
-
-static struct snd_pcm_hardware atmel_abdac_hw = {
- .info = (SNDRV_PCM_INFO_MMAP
- | SNDRV_PCM_INFO_MMAP_VALID
- | SNDRV_PCM_INFO_INTERLEAVED
- | SNDRV_PCM_INFO_BLOCK_TRANSFER
- | SNDRV_PCM_INFO_RESUME
- | SNDRV_PCM_INFO_PAUSE),
- .formats = (SNDRV_PCM_FMTBIT_S16_BE),
- .rates = (SNDRV_PCM_RATE_KNOT),
- .rate_min = RATE_MIN,
- .rate_max = RATE_MAX,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = 64 * 4096,
- .period_bytes_min = 4096,
- .period_bytes_max = 4096,
- .periods_min = 6,
- .periods_max = 64,
-};
-
-static int atmel_abdac_open(struct snd_pcm_substream *substream)
-{
- struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
-
- dac->substream = substream;
- atmel_abdac_hw.rate_max = dac->rates[dac->rates_num - 1];
- atmel_abdac_hw.rate_min = dac->rates[0];
- substream->runtime->hw = atmel_abdac_hw;
-
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &dac->constraints_rates);
-}
-
-static int atmel_abdac_close(struct snd_pcm_substream *substream)
-{
- struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
- dac->substream = NULL;
- return 0;
-}
-
-static int atmel_abdac_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
- int retval;
-
- retval = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (retval < 0)
- return retval;
- /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
- if (retval == 1)
- if (test_and_clear_bit(DMA_READY, &dac->flags))
- dw_dma_cyclic_free(dac->dma.chan);
-
- return retval;
-}
-
-static int atmel_abdac_hw_free(struct snd_pcm_substream *substream)
-{
- struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
- if (test_and_clear_bit(DMA_READY, &dac->flags))
- dw_dma_cyclic_free(dac->dma.chan);
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int atmel_abdac_prepare(struct snd_pcm_substream *substream)
-{
- struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
- int retval;
-
- retval = clk_set_rate(dac->sample_clk, 256 * substream->runtime->rate);
- if (retval)
- return retval;
-
- if (!test_bit(DMA_READY, &dac->flags))
- retval = atmel_abdac_prepare_dma(dac, substream, DMA_TO_DEVICE);
-
- return retval;
-}
-
-static int atmel_abdac_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
- int retval = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
- case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
- case SNDRV_PCM_TRIGGER_START:
- clk_enable(dac->sample_clk);
- retval = dw_dma_cyclic_start(dac->dma.chan);
- if (retval)
- goto out;
- dac_writel(dac, CTRL, DAC_BIT(EN));
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
- case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
- case SNDRV_PCM_TRIGGER_STOP:
- dw_dma_cyclic_stop(dac->dma.chan);
- dac_writel(dac, DATA, 0);
- dac_writel(dac, CTRL, 0);
- clk_disable(dac->sample_clk);
- break;
- default:
- retval = -EINVAL;
- break;
- }
-out:
- return retval;
-}
-
-static snd_pcm_uframes_t
-atmel_abdac_pointer(struct snd_pcm_substream *substream)
-{
- struct atmel_abdac *dac = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_uframes_t frames;
- unsigned long bytes;
-
- bytes = dw_dma_get_src_addr(dac->dma.chan);
- bytes -= runtime->dma_addr;
-
- frames = bytes_to_frames(runtime, bytes);
- if (frames >= runtime->buffer_size)
- frames -= runtime->buffer_size;
-
- return frames;
-}
-
-static irqreturn_t abdac_interrupt(int irq, void *dev_id)
-{
- struct atmel_abdac *dac = dev_id;
- u32 status;
-
- status = dac_readl(dac, INT_STATUS);
- if (status & DAC_BIT(UNDERRUN)) {
- dev_err(&dac->pdev->dev, "underrun detected\n");
- dac_writel(dac, INT_CLR, DAC_BIT(UNDERRUN));
- } else {
- dev_err(&dac->pdev->dev, "spurious interrupt (status=0x%x)\n",
- status);
- dac_writel(dac, INT_CLR, status);
- }
-
- return IRQ_HANDLED;
-}
-
-static struct snd_pcm_ops atmel_abdac_ops = {
- .open = atmel_abdac_open,
- .close = atmel_abdac_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = atmel_abdac_hw_params,
- .hw_free = atmel_abdac_hw_free,
- .prepare = atmel_abdac_prepare,
- .trigger = atmel_abdac_trigger,
- .pointer = atmel_abdac_pointer,
-};
-
-static int __devinit atmel_abdac_pcm_new(struct atmel_abdac *dac)
-{
- struct snd_pcm_hardware hw = atmel_abdac_hw;
- struct snd_pcm *pcm;
- int retval;
-
- retval = snd_pcm_new(dac->card, dac->card->shortname,
- dac->pdev->id, 1, 0, &pcm);
- if (retval)
- return retval;
-
- strcpy(pcm->name, dac->card->shortname);
- pcm->private_data = dac;
- pcm->info_flags = 0;
- dac->pcm = pcm;
-
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &atmel_abdac_ops);
-
- retval = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- &dac->pdev->dev, hw.periods_min * hw.period_bytes_min,
- hw.buffer_bytes_max);
-
- return retval;
-}
-
-static bool filter(struct dma_chan *chan, void *slave)
-{
- struct dw_dma_slave *dws = slave;
-
- if (dws->dma_dev == chan->device->dev) {
- chan->private = dws;
- return true;
- } else
- return false;
-}
-
-static int set_sample_rates(struct atmel_abdac *dac)
-{
- long new_rate = RATE_MAX;
- int retval = -EINVAL;
- int index = 0;
-
- /* we start at 192 kHz and work our way down to 5112 Hz */
- while (new_rate >= RATE_MIN && index < (MAX_NUM_RATES + 1)) {
- new_rate = clk_round_rate(dac->sample_clk, 256 * new_rate);
- if (new_rate < 0)
- break;
- /* make sure we are below the ABDAC clock */
- if (new_rate <= clk_get_rate(dac->pclk)) {
- dac->rates[index] = new_rate / 256;
- index++;
- }
- /* divide by 256 and then by two to get next rate */
- new_rate /= 256 * 2;
- }
-
- if (index) {
- int i;
-
- /* reverse array, smallest go first */
- for (i = 0; i < (index / 2); i++) {
- unsigned int tmp = dac->rates[index - 1 - i];
- dac->rates[index - 1 - i] = dac->rates[i];
- dac->rates[i] = tmp;
- }
-
- dac->constraints_rates.count = index;
- dac->constraints_rates.list = dac->rates;
- dac->constraints_rates.mask = 0;
- dac->rates_num = index;
-
- retval = 0;
- }
-
- return retval;
-}
-
-static int __devinit atmel_abdac_probe(struct platform_device *pdev)
-{
- struct snd_card *card;
- struct atmel_abdac *dac;
- struct resource *regs;
- struct atmel_abdac_pdata *pdata;
- struct clk *pclk;
- struct clk *sample_clk;
- int retval;
- int irq;
-
- regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!regs) {
- dev_dbg(&pdev->dev, "no memory resource\n");
- return -ENXIO;
- }
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_dbg(&pdev->dev, "could not get IRQ number\n");
- return irq;
- }
-
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_dbg(&pdev->dev, "no platform data\n");
- return -ENXIO;
- }
-
- pclk = clk_get(&pdev->dev, "pclk");
- if (IS_ERR(pclk)) {
- dev_dbg(&pdev->dev, "no peripheral clock\n");
- return PTR_ERR(pclk);
- }
- sample_clk = clk_get(&pdev->dev, "sample_clk");
- if (IS_ERR(pclk)) {
- dev_dbg(&pdev->dev, "no sample clock\n");
- retval = PTR_ERR(pclk);
- goto out_put_pclk;
- }
- clk_enable(pclk);
-
- retval = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
- THIS_MODULE, sizeof(struct atmel_abdac), &card);
- if (retval) {
- dev_dbg(&pdev->dev, "could not create sound card device\n");
- goto out_put_sample_clk;
- }
-
- dac = get_dac(card);
-
- dac->irq = irq;
- dac->card = card;
- dac->pclk = pclk;
- dac->sample_clk = sample_clk;
- dac->pdev = pdev;
-
- retval = set_sample_rates(dac);
- if (retval < 0) {
- dev_dbg(&pdev->dev, "could not set supported rates\n");
- goto out_free_card;
- }
-
- dac->regs = ioremap(regs->start, regs->end - regs->start + 1);
- if (!dac->regs) {
- dev_dbg(&pdev->dev, "could not remap register memory\n");
- goto out_free_card;
- }
-
- /* make sure the DAC is silent and disabled */
- dac_writel(dac, DATA, 0);
- dac_writel(dac, CTRL, 0);
-
- retval = request_irq(irq, abdac_interrupt, 0, "abdac", dac);
- if (retval) {
- dev_dbg(&pdev->dev, "could not request irq\n");
- goto out_unmap_regs;
- }
-
- snd_card_set_dev(card, &pdev->dev);
-
- if (pdata->dws.dma_dev) {
- struct dw_dma_slave *dws = &pdata->dws;
- dma_cap_mask_t mask;
-
- dws->tx_reg = regs->start + DAC_DATA;
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
-
- dac->dma.chan = dma_request_channel(mask, filter, dws);
- }
- if (!pdata->dws.dma_dev || !dac->dma.chan) {
- dev_dbg(&pdev->dev, "DMA not available\n");
- retval = -ENODEV;
- goto out_unset_card_dev;
- }
-
- strcpy(card->driver, "Atmel ABDAC");
- strcpy(card->shortname, "Atmel ABDAC");
- sprintf(card->longname, "Atmel Audio Bitstream DAC");
-
- retval = atmel_abdac_pcm_new(dac);
- if (retval) {
- dev_dbg(&pdev->dev, "could not register ABDAC pcm device\n");
- goto out_release_dma;
- }
-
- retval = snd_card_register(card);
- if (retval) {
- dev_dbg(&pdev->dev, "could not register sound card\n");
- goto out_release_dma;
- }
-
- platform_set_drvdata(pdev, card);
-
- dev_info(&pdev->dev, "Atmel ABDAC at 0x%p using %s\n",
- dac->regs, dev_name(&dac->dma.chan->dev->device));
-
- return retval;
-
-out_release_dma:
- dma_release_channel(dac->dma.chan);
- dac->dma.chan = NULL;
-out_unset_card_dev:
- snd_card_set_dev(card, NULL);
- free_irq(irq, dac);
-out_unmap_regs:
- iounmap(dac->regs);
-out_free_card:
- snd_card_free(card);
-out_put_sample_clk:
- clk_put(sample_clk);
- clk_disable(pclk);
-out_put_pclk:
- clk_put(pclk);
- return retval;
-}
-
-#ifdef CONFIG_PM
-static int atmel_abdac_suspend(struct platform_device *pdev, pm_message_t msg)
-{
- struct snd_card *card = platform_get_drvdata(pdev);
- struct atmel_abdac *dac = card->private_data;
-
- dw_dma_cyclic_stop(dac->dma.chan);
- clk_disable(dac->sample_clk);
- clk_disable(dac->pclk);
-
- return 0;
-}
-
-static int atmel_abdac_resume(struct platform_device *pdev)
-{
- struct snd_card *card = platform_get_drvdata(pdev);
- struct atmel_abdac *dac = card->private_data;
-
- clk_enable(dac->pclk);
- clk_enable(dac->sample_clk);
- if (test_bit(DMA_READY, &dac->flags))
- dw_dma_cyclic_start(dac->dma.chan);
-
- return 0;
-}
-#else
-#define atmel_abdac_suspend NULL
-#define atmel_abdac_resume NULL
-#endif
-
-static int __devexit atmel_abdac_remove(struct platform_device *pdev)
-{
- struct snd_card *card = platform_get_drvdata(pdev);
- struct atmel_abdac *dac = get_dac(card);
-
- clk_put(dac->sample_clk);
- clk_disable(dac->pclk);
- clk_put(dac->pclk);
-
- dma_release_channel(dac->dma.chan);
- dac->dma.chan = NULL;
- snd_card_set_dev(card, NULL);
- iounmap(dac->regs);
- free_irq(dac->irq, dac);
- snd_card_free(card);
-
- platform_set_drvdata(pdev, NULL);
-
- return 0;
-}
-
-static struct platform_driver atmel_abdac_driver = {
- .remove = __devexit_p(atmel_abdac_remove),
- .driver = {
- .name = "atmel_abdac",
- },
- .suspend = atmel_abdac_suspend,
- .resume = atmel_abdac_resume,
-};
-
-static int __init atmel_abdac_init(void)
-{
- return platform_driver_probe(&atmel_abdac_driver,
- atmel_abdac_probe);
-}
-module_init(atmel_abdac_init);
-
-static void __exit atmel_abdac_exit(void)
-{
- platform_driver_unregister(&atmel_abdac_driver);
-}
-module_exit(atmel_abdac_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Driver for Atmel Audio Bitstream DAC (ABDAC)");
-MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>");
diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c
index 10c3a871a12d..df0a049192de 100644
--- a/sound/atmel/ac97c.c
+++ b/sound/atmel/ac97c.c
@@ -1,25 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for Atmel AC97C
*
* Copyright (C) 2005-2009 Atmel Corporation
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/bitmap.h>
#include <linux/device.h>
-#include <linux/dmaengine.h>
-#include <linux/dma-mapping.h>
#include <linux/atmel_pdc.h>
+#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
-#include <linux/gpio.h>
+#include <linux/string.h>
+#include <linux/types.h>
#include <linux/io.h>
#include <sound/core.h>
@@ -27,36 +25,16 @@
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/ac97_codec.h>
-#include <sound/atmel-ac97c.h>
#include <sound/memalloc.h>
-#include <linux/dw_dmac.h>
-
-#include <mach/cpu.h>
-#include <mach/hardware.h>
-#include <mach/gpio.h>
-
#include "ac97c.h"
-enum {
- DMA_TX_READY = 0,
- DMA_RX_READY,
- DMA_TX_CHAN_PRESENT,
- DMA_RX_CHAN_PRESENT,
-};
-
/* Serialize access to opened variable */
static DEFINE_MUTEX(opened_mutex);
-struct atmel_ac97c_dma {
- struct dma_chan *rx_chan;
- struct dma_chan *tx_chan;
-};
-
struct atmel_ac97c {
struct clk *pclk;
struct platform_device *pdev;
- struct atmel_ac97c_dma dma;
struct snd_pcm_substream *playback_substream;
struct snd_pcm_substream *capture_substream;
@@ -67,14 +45,13 @@ struct atmel_ac97c {
u64 cur_format;
unsigned int cur_rate;
- unsigned long flags;
int playback_period, capture_period;
/* Serialize access to opened variable */
spinlock_t lock;
void __iomem *regs;
int irq;
int opened;
- int reset_pin;
+ struct gpio_desc *reset_pin;
};
#define get_chip(card) ((struct atmel_ac97c *)(card)->private_data)
@@ -84,66 +61,7 @@ struct atmel_ac97c {
#define ac97c_readl(chip, reg) \
__raw_readl((chip)->regs + AC97C_##reg)
-/* This function is called by the DMA driver. */
-static void atmel_ac97c_dma_playback_period_done(void *arg)
-{
- struct atmel_ac97c *chip = arg;
- snd_pcm_period_elapsed(chip->playback_substream);
-}
-
-static void atmel_ac97c_dma_capture_period_done(void *arg)
-{
- struct atmel_ac97c *chip = arg;
- snd_pcm_period_elapsed(chip->capture_substream);
-}
-
-static int atmel_ac97c_prepare_dma(struct atmel_ac97c *chip,
- struct snd_pcm_substream *substream,
- enum dma_data_direction direction)
-{
- struct dma_chan *chan;
- struct dw_cyclic_desc *cdesc;
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long buffer_len, period_len;
-
- /*
- * We don't do DMA on "complex" transfers, i.e. with
- * non-halfword-aligned buffers or lengths.
- */
- if (runtime->dma_addr & 1 || runtime->buffer_size & 1) {
- dev_dbg(&chip->pdev->dev, "too complex transfer\n");
- return -EINVAL;
- }
-
- if (direction == DMA_TO_DEVICE)
- chan = chip->dma.tx_chan;
- else
- chan = chip->dma.rx_chan;
-
- buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
- period_len = frames_to_bytes(runtime, runtime->period_size);
-
- cdesc = dw_dma_cyclic_prep(chan, runtime->dma_addr, buffer_len,
- period_len, direction);
- if (IS_ERR(cdesc)) {
- dev_dbg(&chip->pdev->dev, "could not prepare cyclic DMA\n");
- return PTR_ERR(cdesc);
- }
-
- if (direction == DMA_TO_DEVICE) {
- cdesc->period_callback = atmel_ac97c_dma_playback_period_done;
- set_bit(DMA_TX_READY, &chip->flags);
- } else {
- cdesc->period_callback = atmel_ac97c_dma_capture_period_done;
- set_bit(DMA_RX_READY, &chip->flags);
- }
-
- cdesc->period_callback_param = chip;
-
- return 0;
-}
-
-static struct snd_pcm_hardware atmel_ac97c_hw = {
+static const struct snd_pcm_hardware atmel_ac97c_hw = {
.info = (SNDRV_PCM_INFO_MMAP
| SNDRV_PCM_INFO_MMAP_VALID
| SNDRV_PCM_INFO_INTERLEAVED
@@ -170,7 +88,7 @@ static int atmel_ac97c_playback_open(struct snd_pcm_substream *substream)
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- mutex_lock(&opened_mutex);
+ guard(mutex)(&opened_mutex);
chip->opened++;
runtime->hw = atmel_ac97c_hw;
if (chip->cur_rate) {
@@ -178,8 +96,7 @@ static int atmel_ac97c_playback_open(struct snd_pcm_substream *substream)
runtime->hw.rate_max = chip->cur_rate;
}
if (chip->cur_format)
- runtime->hw.formats = (1ULL << chip->cur_format);
- mutex_unlock(&opened_mutex);
+ runtime->hw.formats = pcm_format_to_bits(chip->cur_format);
chip->playback_substream = substream;
return 0;
}
@@ -189,7 +106,7 @@ static int atmel_ac97c_capture_open(struct snd_pcm_substream *substream)
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- mutex_lock(&opened_mutex);
+ guard(mutex)(&opened_mutex);
chip->opened++;
runtime->hw = atmel_ac97c_hw;
if (chip->cur_rate) {
@@ -197,8 +114,7 @@ static int atmel_ac97c_capture_open(struct snd_pcm_substream *substream)
runtime->hw.rate_max = chip->cur_rate;
}
if (chip->cur_format)
- runtime->hw.formats = (1ULL << chip->cur_format);
- mutex_unlock(&opened_mutex);
+ runtime->hw.formats = pcm_format_to_bits(chip->cur_format);
chip->capture_substream = substream;
return 0;
}
@@ -207,13 +123,12 @@ static int atmel_ac97c_playback_close(struct snd_pcm_substream *substream)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
- mutex_lock(&opened_mutex);
+ guard(mutex)(&opened_mutex);
chip->opened--;
if (!chip->opened) {
chip->cur_rate = 0;
chip->cur_format = 0;
}
- mutex_unlock(&opened_mutex);
chip->playback_substream = NULL;
@@ -224,13 +139,12 @@ static int atmel_ac97c_capture_close(struct snd_pcm_substream *substream)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
- mutex_lock(&opened_mutex);
+ guard(mutex)(&opened_mutex);
chip->opened--;
if (!chip->opened) {
chip->cur_rate = 0;
chip->cur_format = 0;
}
- mutex_unlock(&opened_mutex);
chip->capture_substream = NULL;
@@ -241,75 +155,26 @@ static int atmel_ac97c_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
- int retval;
- retval = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (retval < 0)
- return retval;
- /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
- if (cpu_is_at32ap7000()) {
- /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
- if (retval == 1)
- if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
- dw_dma_cyclic_free(chip->dma.tx_chan);
- }
/* Set restrictions to params. */
- mutex_lock(&opened_mutex);
+ guard(mutex)(&opened_mutex);
chip->cur_rate = params_rate(hw_params);
chip->cur_format = params_format(hw_params);
- mutex_unlock(&opened_mutex);
- return retval;
+ return 0;
}
static int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
- int retval;
-
- retval = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (retval < 0)
- return retval;
- /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
- if (cpu_is_at32ap7000()) {
- if (retval < 0)
- return retval;
- /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
- if (retval == 1)
- if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
- dw_dma_cyclic_free(chip->dma.rx_chan);
- }
/* Set restrictions to params. */
- mutex_lock(&opened_mutex);
+ guard(mutex)(&opened_mutex);
chip->cur_rate = params_rate(hw_params);
chip->cur_format = params_format(hw_params);
- mutex_unlock(&opened_mutex);
-
- return retval;
-}
-static int atmel_ac97c_playback_hw_free(struct snd_pcm_substream *substream)
-{
- struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
- if (cpu_is_at32ap7000()) {
- if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
- dw_dma_cyclic_free(chip->dma.tx_chan);
- }
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int atmel_ac97c_capture_hw_free(struct snd_pcm_substream *substream)
-{
- struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
- if (cpu_is_at32ap7000()) {
- if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
- dw_dma_cyclic_free(chip->dma.rx_chan);
- }
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
@@ -347,10 +212,8 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
switch (runtime->format) {
case SNDRV_PCM_FORMAT_S16_LE:
- if (cpu_is_at32ap7000())
- word |= AC97C_CMR_CEM_LITTLE;
break;
- case SNDRV_PCM_FORMAT_S16_BE: /* fall through */
+ case SNDRV_PCM_FORMAT_S16_BE:
word &= ~(AC97C_CMR_CEM_LITTLE);
break;
default:
@@ -387,18 +250,11 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n",
runtime->rate);
- if (cpu_is_at32ap7000()) {
- if (!test_bit(DMA_TX_READY, &chip->flags))
- retval = atmel_ac97c_prepare_dma(chip, substream,
- DMA_TO_DEVICE);
- } else {
- /* Initialize and start the PDC */
- writel(runtime->dma_addr, chip->regs + ATMEL_PDC_TPR);
- writel(block_size / 2, chip->regs + ATMEL_PDC_TCR);
- writel(runtime->dma_addr + block_size,
- chip->regs + ATMEL_PDC_TNPR);
- writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR);
- }
+ /* Initialize and start the PDC */
+ writel(runtime->dma_addr, chip->regs + ATMEL_PDC_TPR);
+ writel(block_size / 2, chip->regs + ATMEL_PDC_TCR);
+ writel(runtime->dma_addr + block_size, chip->regs + ATMEL_PDC_TNPR);
+ writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR);
return retval;
}
@@ -438,10 +294,8 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
switch (runtime->format) {
case SNDRV_PCM_FORMAT_S16_LE:
- if (cpu_is_at32ap7000())
- word |= AC97C_CMR_CEM_LITTLE;
break;
- case SNDRV_PCM_FORMAT_S16_BE: /* fall through */
+ case SNDRV_PCM_FORMAT_S16_BE:
word &= ~(AC97C_CMR_CEM_LITTLE);
break;
default:
@@ -478,18 +332,11 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n",
runtime->rate);
- if (cpu_is_at32ap7000()) {
- if (!test_bit(DMA_RX_READY, &chip->flags))
- retval = atmel_ac97c_prepare_dma(chip, substream,
- DMA_FROM_DEVICE);
- } else {
- /* Initialize and start the PDC */
- writel(runtime->dma_addr, chip->regs + ATMEL_PDC_RPR);
- writel(block_size / 2, chip->regs + ATMEL_PDC_RCR);
- writel(runtime->dma_addr + block_size,
- chip->regs + ATMEL_PDC_RNPR);
- writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR);
- }
+ /* Initialize and start the PDC */
+ writel(runtime->dma_addr, chip->regs + ATMEL_PDC_RPR);
+ writel(block_size / 2, chip->regs + ATMEL_PDC_RCR);
+ writel(runtime->dma_addr + block_size, chip->regs + ATMEL_PDC_RNPR);
+ writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR);
return retval;
}
@@ -499,43 +346,30 @@ atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
unsigned long camr, ptcr = 0;
- int retval = 0;
camr = ac97c_readl(chip, CAMR);
switch (cmd) {
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
- case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_START:
- if (cpu_is_at32ap7000()) {
- retval = dw_dma_cyclic_start(chip->dma.tx_chan);
- if (retval)
- goto out;
- } else {
- ptcr = ATMEL_PDC_TXTEN;
- }
+ ptcr = ATMEL_PDC_TXTEN;
camr |= AC97C_CMR_CENA | AC97C_CSR_ENDTX;
break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
- case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
- if (cpu_is_at32ap7000())
- dw_dma_cyclic_stop(chip->dma.tx_chan);
- else
- ptcr |= ATMEL_PDC_TXTDIS;
+ ptcr |= ATMEL_PDC_TXTDIS;
if (chip->opened <= 1)
camr &= ~AC97C_CMR_CENA;
break;
default:
- retval = -EINVAL;
- goto out;
+ return -EINVAL;
}
ac97c_writel(chip, CAMR, camr);
- if (!cpu_is_at32ap7000())
- writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
-out:
- return retval;
+ writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
+ return 0;
}
static int
@@ -543,44 +377,31 @@ atmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
unsigned long camr, ptcr = 0;
- int retval = 0;
camr = ac97c_readl(chip, CAMR);
ptcr = readl(chip->regs + ATMEL_PDC_PTSR);
switch (cmd) {
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
- case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_START:
- if (cpu_is_at32ap7000()) {
- retval = dw_dma_cyclic_start(chip->dma.rx_chan);
- if (retval)
- goto out;
- } else {
- ptcr = ATMEL_PDC_RXTEN;
- }
+ ptcr = ATMEL_PDC_RXTEN;
camr |= AC97C_CMR_CENA | AC97C_CSR_ENDRX;
break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
- case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
- if (cpu_is_at32ap7000())
- dw_dma_cyclic_stop(chip->dma.rx_chan);
- else
- ptcr |= (ATMEL_PDC_RXTDIS);
+ ptcr |= ATMEL_PDC_RXTDIS;
if (chip->opened <= 1)
camr &= ~AC97C_CMR_CENA;
break;
default:
- retval = -EINVAL;
- break;
+ return -EINVAL;
}
ac97c_writel(chip, CAMR, camr);
- if (!cpu_is_at32ap7000())
- writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
-out:
- return retval;
+ writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
+ return 0;
}
static snd_pcm_uframes_t
@@ -591,10 +412,7 @@ atmel_ac97c_playback_pointer(struct snd_pcm_substream *substream)
snd_pcm_uframes_t frames;
unsigned long bytes;
- if (cpu_is_at32ap7000())
- bytes = dw_dma_get_src_addr(chip->dma.tx_chan);
- else
- bytes = readl(chip->regs + ATMEL_PDC_TPR);
+ bytes = readl(chip->regs + ATMEL_PDC_TPR);
bytes -= runtime->dma_addr;
frames = bytes_to_frames(runtime, bytes);
@@ -611,10 +429,7 @@ atmel_ac97c_capture_pointer(struct snd_pcm_substream *substream)
snd_pcm_uframes_t frames;
unsigned long bytes;
- if (cpu_is_at32ap7000())
- bytes = dw_dma_get_dst_addr(chip->dma.rx_chan);
- else
- bytes = readl(chip->regs + ATMEL_PDC_RPR);
+ bytes = readl(chip->regs + ATMEL_PDC_RPR);
bytes -= runtime->dma_addr;
frames = bytes_to_frames(runtime, bytes);
@@ -623,23 +438,19 @@ atmel_ac97c_capture_pointer(struct snd_pcm_substream *substream)
return frames;
}
-static struct snd_pcm_ops atmel_ac97_playback_ops = {
+static const struct snd_pcm_ops atmel_ac97_playback_ops = {
.open = atmel_ac97c_playback_open,
.close = atmel_ac97c_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = atmel_ac97c_playback_hw_params,
- .hw_free = atmel_ac97c_playback_hw_free,
.prepare = atmel_ac97c_playback_prepare,
.trigger = atmel_ac97c_playback_trigger,
.pointer = atmel_ac97c_playback_pointer,
};
-static struct snd_pcm_ops atmel_ac97_capture_ops = {
+static const struct snd_pcm_ops atmel_ac97_capture_ops = {
.open = atmel_ac97c_capture_open,
.close = atmel_ac97c_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = atmel_ac97c_capture_hw_params,
- .hw_free = atmel_ac97c_capture_hw_free,
.prepare = atmel_ac97c_capture_prepare,
.trigger = atmel_ac97c_capture_trigger,
.pointer = atmel_ac97c_capture_pointer,
@@ -658,66 +469,57 @@ static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev)
struct snd_pcm_runtime *runtime;
int offset, next_period, block_size;
dev_dbg(&chip->pdev->dev, "channel A event%s%s%s%s%s%s\n",
- casr & AC97C_CSR_OVRUN ? " OVRUN" : "",
- casr & AC97C_CSR_RXRDY ? " RXRDY" : "",
- casr & AC97C_CSR_UNRUN ? " UNRUN" : "",
- casr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "",
- casr & AC97C_CSR_TXRDY ? " TXRDY" : "",
- !casr ? " NONE" : "");
- if (!cpu_is_at32ap7000()) {
- if ((casr & camr) & AC97C_CSR_ENDTX) {
- runtime = chip->playback_substream->runtime;
- block_size = frames_to_bytes(runtime,
- runtime->period_size);
- chip->playback_period++;
-
- if (chip->playback_period == runtime->periods)
- chip->playback_period = 0;
- next_period = chip->playback_period + 1;
- if (next_period == runtime->periods)
- next_period = 0;
-
- offset = block_size * next_period;
-
- writel(runtime->dma_addr + offset,
- chip->regs + ATMEL_PDC_TNPR);
- writel(block_size / 2,
- chip->regs + ATMEL_PDC_TNCR);
-
- snd_pcm_period_elapsed(
- chip->playback_substream);
- }
- if ((casr & camr) & AC97C_CSR_ENDRX) {
- runtime = chip->capture_substream->runtime;
- block_size = frames_to_bytes(runtime,
- runtime->period_size);
- chip->capture_period++;
-
- if (chip->capture_period == runtime->periods)
- chip->capture_period = 0;
- next_period = chip->capture_period + 1;
- if (next_period == runtime->periods)
- next_period = 0;
-
- offset = block_size * next_period;
-
- writel(runtime->dma_addr + offset,
- chip->regs + ATMEL_PDC_RNPR);
- writel(block_size / 2,
- chip->regs + ATMEL_PDC_RNCR);
- snd_pcm_period_elapsed(chip->capture_substream);
- }
+ (casr & AC97C_CSR_OVRUN) ? " OVRUN" : "",
+ (casr & AC97C_CSR_RXRDY) ? " RXRDY" : "",
+ (casr & AC97C_CSR_UNRUN) ? " UNRUN" : "",
+ (casr & AC97C_CSR_TXEMPTY) ? " TXEMPTY" : "",
+ (casr & AC97C_CSR_TXRDY) ? " TXRDY" : "",
+ !casr ? " NONE" : "");
+ if ((casr & camr) & AC97C_CSR_ENDTX) {
+ runtime = chip->playback_substream->runtime;
+ block_size = frames_to_bytes(runtime, runtime->period_size);
+ chip->playback_period++;
+
+ if (chip->playback_period == runtime->periods)
+ chip->playback_period = 0;
+ next_period = chip->playback_period + 1;
+ if (next_period == runtime->periods)
+ next_period = 0;
+
+ offset = block_size * next_period;
+
+ writel(runtime->dma_addr + offset, chip->regs + ATMEL_PDC_TNPR);
+ writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR);
+
+ snd_pcm_period_elapsed(chip->playback_substream);
+ }
+ if ((casr & camr) & AC97C_CSR_ENDRX) {
+ runtime = chip->capture_substream->runtime;
+ block_size = frames_to_bytes(runtime, runtime->period_size);
+ chip->capture_period++;
+
+ if (chip->capture_period == runtime->periods)
+ chip->capture_period = 0;
+ next_period = chip->capture_period + 1;
+ if (next_period == runtime->periods)
+ next_period = 0;
+
+ offset = block_size * next_period;
+
+ writel(runtime->dma_addr + offset, chip->regs + ATMEL_PDC_RNPR);
+ writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR);
+ snd_pcm_period_elapsed(chip->capture_substream);
}
retval = IRQ_HANDLED;
}
if (sr & AC97C_SR_COEVT) {
dev_info(&chip->pdev->dev, "codec channel event%s%s%s%s%s\n",
- cosr & AC97C_CSR_OVRUN ? " OVRUN" : "",
- cosr & AC97C_CSR_RXRDY ? " RXRDY" : "",
- cosr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "",
- cosr & AC97C_CSR_TXRDY ? " TXRDY" : "",
- !cosr ? " NONE" : "");
+ (cosr & AC97C_CSR_OVRUN) ? " OVRUN" : "",
+ (cosr & AC97C_CSR_RXRDY) ? " RXRDY" : "",
+ (cosr & AC97C_CSR_TXEMPTY) ? " TXEMPTY" : "",
+ (cosr & AC97C_CSR_TXRDY) ? " TXRDY" : "",
+ !cosr ? " NONE" : "");
retval = IRQ_HANDLED;
}
@@ -729,7 +531,7 @@ static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev)
return retval;
}
-static struct ac97_pcm at91_ac97_pcm_defs[] __devinitdata = {
+static const struct ac97_pcm at91_ac97_pcm_defs[] = {
/* Playback */
{
.exclusive = 1,
@@ -757,43 +559,32 @@ static struct ac97_pcm at91_ac97_pcm_defs[] __devinitdata = {
},
};
-static int __devinit atmel_ac97c_pcm_new(struct atmel_ac97c *chip)
+static int atmel_ac97c_pcm_new(struct atmel_ac97c *chip)
{
struct snd_pcm *pcm;
struct snd_pcm_hardware hw = atmel_ac97c_hw;
- int capture, playback, retval, err;
+ int retval;
- capture = test_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
- playback = test_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
+ retval = snd_ac97_pcm_assign(chip->ac97_bus,
+ ARRAY_SIZE(at91_ac97_pcm_defs),
+ at91_ac97_pcm_defs);
+ if (retval)
+ return retval;
- if (!cpu_is_at32ap7000()) {
- err = snd_ac97_pcm_assign(chip->ac97_bus,
- ARRAY_SIZE(at91_ac97_pcm_defs),
- at91_ac97_pcm_defs);
- if (err)
- return err;
- }
- retval = snd_pcm_new(chip->card, chip->card->shortname,
- chip->pdev->id, playback, capture, &pcm);
+ retval = snd_pcm_new(chip->card, chip->card->shortname, 0, 1, 1, &pcm);
if (retval)
return retval;
- if (capture)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &atmel_ac97_capture_ops);
- if (playback)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- &atmel_ac97_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &atmel_ac97_capture_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &atmel_ac97_playback_ops);
- retval = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
&chip->pdev->dev, hw.periods_min * hw.period_bytes_min,
hw.buffer_bytes_max);
- if (retval)
- return retval;
pcm->private_data = chip;
pcm->info_flags = 0;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
chip->pcm = pcm;
return 0;
@@ -873,17 +664,6 @@ timed_out:
return 0xffff;
}
-static bool filter(struct dma_chan *chan, void *slave)
-{
- struct dw_dma_slave *dws = slave;
-
- if (dws->dma_dev == chan->device->dev) {
- chan->private = dws;
- return true;
- } else
- return false;
-}
-
static void atmel_ac97c_reset(struct atmel_ac97c *chip)
{
ac97c_writel(chip, MR, 0);
@@ -891,22 +671,32 @@ static void atmel_ac97c_reset(struct atmel_ac97c *chip)
ac97c_writel(chip, CAMR, 0);
ac97c_writel(chip, COMR, 0);
- if (gpio_is_valid(chip->reset_pin)) {
- gpio_set_value(chip->reset_pin, 0);
+ if (!IS_ERR(chip->reset_pin)) {
+ gpiod_set_value(chip->reset_pin, 0);
/* AC97 v2.2 specifications says minimum 1 us. */
udelay(2);
- gpio_set_value(chip->reset_pin, 1);
+ gpiod_set_value(chip->reset_pin, 1);
+ } else {
+ ac97c_writel(chip, MR, AC97C_MR_WRST | AC97C_MR_ENA);
+ udelay(2);
+ ac97c_writel(chip, MR, AC97C_MR_ENA);
}
}
-static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
+static const struct of_device_id atmel_ac97c_dt_ids[] = {
+ { .compatible = "atmel,at91sam9263-ac97c", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, atmel_ac97c_dt_ids);
+
+static int atmel_ac97c_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct snd_card *card;
struct atmel_ac97c *chip;
struct resource *regs;
- struct ac97c_platform_data *pdata;
struct clk *pclk;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = atmel_ac97c_write,
.read = atmel_ac97c_read,
};
@@ -919,32 +709,24 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
return -ENXIO;
}
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_dbg(&pdev->dev, "no platform data\n");
- return -ENXIO;
- }
-
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
- dev_dbg(&pdev->dev, "could not get irq\n");
- return -ENXIO;
- }
-
- if (cpu_is_at32ap7000()) {
- pclk = clk_get(&pdev->dev, "pclk");
- } else {
- pclk = clk_get(&pdev->dev, "ac97_clk");
+ dev_dbg(&pdev->dev, "could not get irq: %d\n", irq);
+ return irq;
}
+ pclk = clk_get(&pdev->dev, "ac97_clk");
if (IS_ERR(pclk)) {
dev_dbg(&pdev->dev, "no peripheral clock\n");
return PTR_ERR(pclk);
}
- clk_enable(pclk);
+ retval = clk_prepare_enable(pclk);
+ if (retval)
+ goto err_prepare_enable;
- retval = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
- THIS_MODULE, sizeof(struct atmel_ac97c), &card);
+ retval = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1,
+ SNDRV_DEFAULT_STR1, THIS_MODULE,
+ sizeof(struct atmel_ac97c), &card);
if (retval) {
dev_dbg(&pdev->dev, "could not create sound card device\n");
goto err_snd_card_new;
@@ -961,31 +743,24 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
spin_lock_init(&chip->lock);
- strcpy(card->driver, "Atmel AC97C");
- strcpy(card->shortname, "Atmel AC97C");
- sprintf(card->longname, "Atmel AC97 controller");
+ strscpy(card->driver, "Atmel AC97C");
+ strscpy(card->shortname, "Atmel AC97C");
+ strscpy(card->longname, "Atmel AC97 controller");
chip->card = card;
chip->pclk = pclk;
chip->pdev = pdev;
- chip->regs = ioremap(regs->start, regs->end - regs->start + 1);
+ chip->regs = ioremap(regs->start, resource_size(regs));
if (!chip->regs) {
dev_dbg(&pdev->dev, "could not remap register memory\n");
+ retval = -ENOMEM;
goto err_ioremap;
}
- if (gpio_is_valid(pdata->reset_pin)) {
- if (gpio_request(pdata->reset_pin, "reset_pin")) {
- dev_dbg(&pdev->dev, "reset pin not available\n");
- chip->reset_pin = -ENODEV;
- } else {
- gpio_direction_output(pdata->reset_pin, 1);
- chip->reset_pin = pdata->reset_pin;
- }
- }
-
- snd_card_set_dev(card, &pdev->dev);
+ chip->reset_pin = devm_gpiod_get_index(dev, "ac97", 2, GPIOD_OUT_HIGH);
+ if (IS_ERR(chip->reset_pin))
+ dev_dbg(dev, "reset pin not available\n");
atmel_ac97c_reset(chip);
@@ -1005,64 +780,16 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
goto err_ac97_bus;
}
- if (cpu_is_at32ap7000()) {
- if (pdata->rx_dws.dma_dev) {
- struct dw_dma_slave *dws = &pdata->rx_dws;
- dma_cap_mask_t mask;
-
- dws->rx_reg = regs->start + AC97C_CARHR + 2;
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
-
- chip->dma.rx_chan = dma_request_channel(mask, filter,
- dws);
-
- dev_info(&chip->pdev->dev, "using %s for DMA RX\n",
- dev_name(&chip->dma.rx_chan->dev->device));
- set_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
- }
-
- if (pdata->tx_dws.dma_dev) {
- struct dw_dma_slave *dws = &pdata->tx_dws;
- dma_cap_mask_t mask;
-
- dws->tx_reg = regs->start + AC97C_CATHR + 2;
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
-
- chip->dma.tx_chan = dma_request_channel(mask, filter,
- dws);
-
- dev_info(&chip->pdev->dev, "using %s for DMA TX\n",
- dev_name(&chip->dma.tx_chan->dev->device));
- set_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
- }
-
- if (!test_bit(DMA_RX_CHAN_PRESENT, &chip->flags) &&
- !test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) {
- dev_dbg(&pdev->dev, "DMA not available\n");
- retval = -ENODEV;
- goto err_dma;
- }
- } else {
- /* Just pretend that we have DMA channel(for at91 i is actually
- * the PDC) */
- set_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
- set_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
- }
-
retval = atmel_ac97c_pcm_new(chip);
if (retval) {
dev_dbg(&pdev->dev, "could not register ac97 pcm device\n");
- goto err_dma;
+ goto err_ac97_bus;
}
retval = snd_card_register(card);
if (retval) {
dev_dbg(&pdev->dev, "could not register sound card\n");
- goto err_dma;
+ goto err_ac97_bus;
}
platform_set_drvdata(pdev, card);
@@ -1072,128 +799,67 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
return 0;
-err_dma:
- if (cpu_is_at32ap7000()) {
- if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
- dma_release_channel(chip->dma.rx_chan);
- if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
- dma_release_channel(chip->dma.tx_chan);
- clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
- clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
- chip->dma.rx_chan = NULL;
- chip->dma.tx_chan = NULL;
- }
err_ac97_bus:
- snd_card_set_dev(card, NULL);
-
- if (gpio_is_valid(chip->reset_pin))
- gpio_free(chip->reset_pin);
-
iounmap(chip->regs);
err_ioremap:
free_irq(irq, chip);
err_request_irq:
snd_card_free(card);
err_snd_card_new:
- clk_disable(pclk);
+ clk_disable_unprepare(pclk);
+err_prepare_enable:
clk_put(pclk);
return retval;
}
-#ifdef CONFIG_PM
-static int atmel_ac97c_suspend(struct platform_device *pdev, pm_message_t msg)
+static int atmel_ac97c_suspend(struct device *pdev)
{
- struct snd_card *card = platform_get_drvdata(pdev);
+ struct snd_card *card = dev_get_drvdata(pdev);
struct atmel_ac97c *chip = card->private_data;
- if (cpu_is_at32ap7000()) {
- if (test_bit(DMA_RX_READY, &chip->flags))
- dw_dma_cyclic_stop(chip->dma.rx_chan);
- if (test_bit(DMA_TX_READY, &chip->flags))
- dw_dma_cyclic_stop(chip->dma.tx_chan);
- }
- clk_disable(chip->pclk);
-
+ clk_disable_unprepare(chip->pclk);
return 0;
}
-static int atmel_ac97c_resume(struct platform_device *pdev)
+static int atmel_ac97c_resume(struct device *pdev)
{
- struct snd_card *card = platform_get_drvdata(pdev);
+ struct snd_card *card = dev_get_drvdata(pdev);
struct atmel_ac97c *chip = card->private_data;
+ int ret = clk_prepare_enable(chip->pclk);
- clk_enable(chip->pclk);
- if (cpu_is_at32ap7000()) {
- if (test_bit(DMA_RX_READY, &chip->flags))
- dw_dma_cyclic_start(chip->dma.rx_chan);
- if (test_bit(DMA_TX_READY, &chip->flags))
- dw_dma_cyclic_start(chip->dma.tx_chan);
- }
- return 0;
+ return ret;
}
-#else
-#define atmel_ac97c_suspend NULL
-#define atmel_ac97c_resume NULL
-#endif
-static int __devexit atmel_ac97c_remove(struct platform_device *pdev)
+static DEFINE_SIMPLE_DEV_PM_OPS(atmel_ac97c_pm, atmel_ac97c_suspend, atmel_ac97c_resume);
+
+static void atmel_ac97c_remove(struct platform_device *pdev)
{
struct snd_card *card = platform_get_drvdata(pdev);
struct atmel_ac97c *chip = get_chip(card);
- if (gpio_is_valid(chip->reset_pin))
- gpio_free(chip->reset_pin);
-
ac97c_writel(chip, CAMR, 0);
ac97c_writel(chip, COMR, 0);
ac97c_writel(chip, MR, 0);
- clk_disable(chip->pclk);
+ clk_disable_unprepare(chip->pclk);
clk_put(chip->pclk);
iounmap(chip->regs);
free_irq(chip->irq, chip);
- if (cpu_is_at32ap7000()) {
- if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
- dma_release_channel(chip->dma.rx_chan);
- if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
- dma_release_channel(chip->dma.tx_chan);
- clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
- clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
- chip->dma.rx_chan = NULL;
- chip->dma.tx_chan = NULL;
- }
-
- snd_card_set_dev(card, NULL);
snd_card_free(card);
-
- platform_set_drvdata(pdev, NULL);
-
- return 0;
}
static struct platform_driver atmel_ac97c_driver = {
- .remove = __devexit_p(atmel_ac97c_remove),
+ .probe = atmel_ac97c_probe,
+ .remove = atmel_ac97c_remove,
.driver = {
.name = "atmel_ac97c",
+ .pm = pm_ptr(&atmel_ac97c_pm),
+ .of_match_table = atmel_ac97c_dt_ids,
},
- .suspend = atmel_ac97c_suspend,
- .resume = atmel_ac97c_resume,
};
-
-static int __init atmel_ac97c_init(void)
-{
- return platform_driver_probe(&atmel_ac97c_driver,
- atmel_ac97c_probe);
-}
-module_init(atmel_ac97c_init);
-
-static void __exit atmel_ac97c_exit(void)
-{
- platform_driver_unregister(&atmel_ac97c_driver);
-}
-module_exit(atmel_ac97c_exit);
+module_platform_driver(atmel_ac97c_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Driver for Atmel AC97 controller");
-MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>");
+MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
diff --git a/sound/atmel/ac97c.h b/sound/atmel/ac97c.h
index ecbba5021c80..6fe9245c44fb 100644
--- a/sound/atmel/ac97c.h
+++ b/sound/atmel/ac97c.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Register definitions for Atmel AC97C
*
* Copyright (C) 2005-2009 Atmel Corporation
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
*/
#ifndef __SOUND_ATMEL_AC97C_H
#define __SOUND_ATMEL_AC97C_H
diff --git a/sound/core/.kunitconfig b/sound/core/.kunitconfig
new file mode 100644
index 000000000000..440f974ba0b7
--- /dev/null
+++ b/sound/core/.kunitconfig
@@ -0,0 +1,5 @@
+CONFIG_KUNIT=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_PCM=y
+CONFIG_SND_CORE_TEST=y
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 475455c76610..48db44fa56fe 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -1,59 +1,88 @@
+# SPDX-License-Identifier: GPL-2.0-only
# ALSA soundcard-configuration
config SND_TIMER
tristate
config SND_PCM
tristate
- select SND_TIMER
- select GCD
+ select SND_TIMER if SND_PCM_TIMER
+
+config SND_PCM_ELD
+ bool
+
+config SND_PCM_IEC958
+ bool
+
+config SND_DMAENGINE_PCM
+ tristate
config SND_HWDEP
tristate
+config SND_SEQ_DEVICE
+ tristate
+
config SND_RAWMIDI
tristate
+ select SND_SEQ_DEVICE if SND_SEQUENCER != n
-# To be effective this also requires INPUT - users should say:
-# select SND_JACK if INPUT=y || INPUT=SND
-# to avoid having to force INPUT on.
-config SND_JACK
- bool
+config SND_UMP
+ tristate
+ select SND_RAWMIDI
-config SND_SEQUENCER
- tristate "Sequencer support"
- select SND_TIMER
+config SND_UMP_LEGACY_RAWMIDI
+ bool "Legacy raw MIDI support for UMP streams"
+ depends on SND_UMP
+ help
+ This option enables the legacy raw MIDI support for UMP streams.
+ When this option is set, an additional rawmidi device for the
+ legacy MIDI 1.0 byte streams is created for each UMP Endpoint.
+ The device contains 16 substreams corresponding to UMP groups.
+
+config SND_CORE_TEST
+ tristate "Sound core KUnit test"
+ depends on KUNIT
+ select SND_PCM
+ default KUNIT_ALL_TESTS
help
- Say Y or M to enable MIDI sequencer and router support. This
- feature allows routing and enqueueing of MIDI events. Events
- can be processed at a given time.
+ This options enables the sound core functions KUnit test.
- Many programs require this feature, so you should enable it
- unless you know what you're doing.
+ KUnit tests run during boot and output the results to the debug
+ log in TAP format (https://testanything.org/). Only useful for
+ kernel devs running KUnit test harness and are not for inclusion
+ into a production build.
-config SND_SEQ_DUMMY
- tristate "Sequencer dummy client"
- depends on SND_SEQUENCER
- help
- Say Y here to enable the dummy sequencer client. This client
- is a simple MIDI-through client: all normal input events are
- redirected to the output port immediately.
+ For more information on KUnit and unit tests in general, refer
+ to the KUnit documentation in Documentation/dev-tools/kunit/.
- You don't need this unless you want to connect many MIDI
- devices or applications together.
- To compile this driver as a module, choose M here: the module
- will be called snd-seq-dummy.
+config SND_COMPRESS_OFFLOAD
+ tristate
+
+config SND_COMPRESS_ACCEL
+ bool
+
+config SND_JACK
+ bool
+
+# enable input device support in jack layer
+config SND_JACK_INPUT_DEV
+ bool
+ depends on SND_JACK
+ default y if INPUT=y || INPUT=SND
config SND_OSSEMUL
+ bool "Enable OSS Emulation"
select SOUND_OSS_CORE
- bool
+ help
+ This option enables the build of OSS emulation layer.
config SND_MIXER_OSS
tristate "OSS Mixer API"
- select SND_OSSEMUL
+ depends on SND_OSSEMUL
help
To enable OSS mixer API emulation (/dev/mixer*), say Y here
- and read <file:Documentation/sound/alsa/OSS-Emulation.txt>.
+ and read <file:Documentation/sound/designs/oss-emulation.rst>.
Many programs still use the OSS API, so say Y.
@@ -62,11 +91,11 @@ config SND_MIXER_OSS
config SND_PCM_OSS
tristate "OSS PCM (digital audio) API"
- select SND_OSSEMUL
+ depends on SND_OSSEMUL
select SND_PCM
help
To enable OSS digital audio (PCM) emulation (/dev/dsp*), say Y
- here and read <file:Documentation/sound/alsa/OSS-Emulation.txt>.
+ here and read <file:Documentation/sound/designs/oss-emulation.rst>.
Many programs still use the OSS API, so say Y.
@@ -76,25 +105,22 @@ config SND_PCM_OSS
config SND_PCM_OSS_PLUGINS
bool "OSS PCM (digital audio) API - Include plugin system"
depends on SND_PCM_OSS
- default y
+ default y
help
- If you disable this option, the ALSA's OSS PCM API will not
- support conversion of channels, formats and rates. It will
- behave like most of new OSS/Free drivers in 2.4/2.6 kernels.
+ If you disable this option, the ALSA's OSS PCM API will not
+ support conversion of channels, formats and rates. It will
+ behave like most of new OSS/Free drivers in 2.4/2.6 kernels.
-config SND_SEQUENCER_OSS
- bool "OSS Sequencer API"
- depends on SND_SEQUENCER
- select SND_OSSEMUL
+config SND_PCM_TIMER
+ bool "PCM timer interface" if EXPERT
+ default y
help
- Say Y here to enable OSS sequencer emulation (both
- /dev/sequencer and /dev/music interfaces).
-
- Many programs still use the OSS API, so say Y.
+ If you disable this option, pcm timer will be unavailable, so
+ those stubs that use pcm timer (e.g. dmix, dsnoop & co) may work
+ incorrectly.
- If you choose M in "Sequencer support" (SND_SEQUENCER),
- this will be compiled as a module. The module will be called
- snd-seq-oss.
+ For some embedded devices, we may disable it to reduce memory
+ footprint, about 20KB on x86_64 platform.
config SND_HRTIMER
tristate "HR-timer backend support"
@@ -108,43 +134,6 @@ config SND_HRTIMER
To compile this driver as a module, choose M here: the module
will be called snd-hrtimer.
-config SND_SEQ_HRTIMER_DEFAULT
- bool "Use HR-timer as default sequencer timer"
- depends on SND_HRTIMER && SND_SEQUENCER
- default y
- help
- Say Y here to use the HR-timer backend as the default sequencer
- timer.
-
-config SND_RTCTIMER
- tristate "RTC Timer support"
- depends on RTC
- select SND_TIMER
- help
- Say Y here to enable RTC timer support for ALSA. ALSA uses
- the RTC timer as a precise timing source and maps the RTC
- timer to ALSA's timer interface. The ALSA sequencer code also
- can use this timing source.
-
- To compile this driver as a module, choose M here: the module
- will be called snd-rtctimer.
-
- Note that this option is exclusive with the new RTC drivers
- (CONFIG_RTC_CLASS) since this requires the old API.
-
-config SND_SEQ_RTCTIMER_DEFAULT
- bool "Use RTC as default sequencer timer"
- depends on SND_RTCTIMER && SND_SEQUENCER
- depends on !SND_SEQ_HRTIMER_DEFAULT
- default y
- help
- Say Y here to use the RTC timer as the default sequencer
- timer. This is strongly recommended because it ensures
- precise MIDI timing even when the system timer runs at less
- than 1000 Hz.
-
- If in doubt, say Y.
-
config SND_DYNAMIC_MINORS
bool "Dynamic device file minor numbers"
help
@@ -155,6 +144,15 @@ config SND_DYNAMIC_MINORS
If you are unsure about this, say N here.
+config SND_MAX_CARDS
+ int "Max number of sound cards"
+ range 4 256
+ default 32
+ depends on SND_DYNAMIC_MINORS
+ help
+ Specify the max number of sound cards that can be assigned
+ on a single machine.
+
config SND_SUPPORT_OLD_API
bool "Support old ALSA API"
default y
@@ -162,23 +160,33 @@ config SND_SUPPORT_OLD_API
Say Y here to support the obsolete ALSA PCM API (ver.0.9.0 rc3
or older).
+config SND_PROC_FS
+ bool "Sound Proc FS Support" if EXPERT
+ depends on PROC_FS
+ default y
+ help
+ Say 'N' to disable Sound proc FS, which may reduce code size about
+ 9KB on x86_64 platform.
+ If unsure say Y.
+
config SND_VERBOSE_PROCFS
bool "Verbose procfs contents"
- depends on PROC_FS
+ depends on SND_PROC_FS
default y
help
Say Y here to include code for verbose procfs contents (provides
- useful information to developers when a problem occurs). On the
- other side, it makes the ALSA subsystem larger.
+ useful information to developers when a problem occurs). On the
+ other side, it makes the ALSA subsystem larger.
-config SND_VERBOSE_PRINTK
- bool "Verbose printk"
+config SND_CTL_FAST_LOOKUP
+ bool "Fast lookup of control elements" if EXPERT
+ default y
+ select XARRAY_MULTI
help
- Say Y here to enable verbose log messages. These messages
- will help to identify source file and position containing
- printed messages.
-
- You don't need this unless you're debugging ALSA.
+ This option enables the faster lookup of control elements.
+ It will consume more memory because of the additional Xarray.
+ If you want to choose the memory footprint over the performance
+ inevitably, turn this off.
config SND_DEBUG
bool "Debug"
@@ -190,7 +198,7 @@ config SND_DEBUG_VERBOSE
depends on SND_DEBUG
help
Say Y here to enable extra-verbose debugging messages.
-
+
Let me repeat: it enables EXTRA-VERBOSE DEBUGGING messages.
So, say Y only if you are ready to be annoyed.
@@ -204,6 +212,49 @@ config SND_PCM_XRUN_DEBUG
sound clicking when system is loaded, it may help to determine
the process or driver which causes the scheduling gaps.
+config SND_CTL_INPUT_VALIDATION
+ bool "Validate input data to control API"
+ help
+ Say Y to enable the additional validation for the input data to
+ each control element, including the value range checks.
+ An error is returned from ALSA core for invalid inputs without
+ passing to the driver. This is a kind of hardening for drivers
+ that have no proper error checks, at the cost of a slight
+ performance overhead.
+
+config SND_CTL_DEBUG
+ bool "Enable debugging feature for control API"
+ depends on SND_DEBUG
+ help
+ Say Y to enable the debugging feature for ALSA control API.
+ It performs the additional sanity-checks for each control element
+ read access, such as whether the values returned from the driver
+ are in the proper ranges or the check of the invalid access at
+ out-of-array areas. The error is printed when the driver gives
+ such unexpected values.
+ When you develop a driver that deals with control elements, it's
+ strongly recommended to try this one once and verify whether you see
+ any relevant errors or not.
+
+config SND_JACK_INJECTION_DEBUG
+ bool "Sound jack injection interface via debugfs"
+ depends on SND_JACK && SND_DEBUG && DEBUG_FS
+ help
+ This option can be used to enable or disable sound jack
+ software injection.
+ Say Y if you are debugging via jack injection interface.
+ If unsure select "N".
+
+config SND_UTIMER
+ bool "Enable support for userspace-controlled virtual timers"
+ depends on SND_TIMER
+ help
+ Say Y to enable the support of userspace-controlled timers. These
+ timers are purely virtual, and they are supposed to be triggered
+ from userspace. They could be quite useful when synchronizing the
+ sound timing with userspace applications (for instance, when sending
+ data through snd-aloop).
+
config SND_VMASTER
bool
@@ -211,4 +262,9 @@ config SND_DMA_SGBUF
def_bool y
depends on X86
+config SND_CTL_LED
+ tristate
+ select NEW_LEDS if SND_CTL_LED
+ select LEDS_TRIGGERS if SND_CTL_LED
+
source "sound/core/seq/Kconfig"
diff --git a/sound/core/Makefile b/sound/core/Makefile
index 350a08d277f4..31a0623cc89d 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -1,33 +1,56 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 1999,2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-y := sound.o init.o memory.o info.o control.o misc.o device.o
+snd-y := sound.o init.o memory.o control.o misc.o device.o
+ifneq ($(CONFIG_SND_PROC_FS),)
+snd-y += info.o
+snd-$(CONFIG_SND_OSSEMUL) += info_oss.o
+endif
snd-$(CONFIG_ISA_DMA_API) += isadma.o
-snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o
+snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o
snd-$(CONFIG_SND_VMASTER) += vmaster.o
-snd-$(CONFIG_SND_JACK) += jack.o
+snd-$(CONFIG_SND_JACK) += ctljack.o jack.o
-snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
- pcm_memory.o
+snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_misc.o \
+ pcm_memory.o memalloc.o
+snd-pcm-$(CONFIG_SND_PCM_TIMER) += pcm_timer.o
+snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o
+snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o
-snd-page-alloc-y := memalloc.o
-snd-page-alloc-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
+# for trace-points
+CFLAGS_pcm_lib.o := -I$(src)
+CFLAGS_pcm_native.o := -I$(src)
-snd-rawmidi-objs := rawmidi.o
-snd-timer-objs := timer.o
-snd-hrtimer-objs := hrtimer.o
-snd-rtctimer-objs := rtctimer.o
-snd-hwdep-objs := hwdep.o
+snd-pcm-dmaengine-y := pcm_dmaengine.o
+
+snd-ctl-led-y := control_led.o
+snd-rawmidi-y := rawmidi.o
+snd-ump-y := ump.o
+snd-ump-$(CONFIG_SND_UMP_LEGACY_RAWMIDI) += ump_convert.o
+snd-timer-y := timer.o
+snd-hrtimer-y := hrtimer.o
+snd-hwdep-y := hwdep.o
+snd-seq-device-y := seq_device.o
+
+snd-compress-y := compress_offload.o
obj-$(CONFIG_SND) += snd.o
+obj-$(CONFIG_SND_CTL_LED) += snd-ctl-led.o
obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o
obj-$(CONFIG_SND_TIMER) += snd-timer.o
obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o
-obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o
-obj-$(CONFIG_SND_PCM) += snd-pcm.o snd-page-alloc.o
+obj-$(CONFIG_SND_PCM) += snd-pcm.o
+obj-$(CONFIG_SND_DMAENGINE_PCM) += snd-pcm-dmaengine.o
+obj-$(CONFIG_SND_SEQ_DEVICE) += snd-seq-device.o
obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o
+obj-$(CONFIG_SND_UMP) += snd-ump.o
+
+obj-$(CONFIG_SND_CORE_TEST) += sound_kunit.o
obj-$(CONFIG_SND_OSSEMUL) += oss/
obj-$(CONFIG_SND_SEQUENCER) += seq/
+
+obj-$(CONFIG_SND_COMPRESS_OFFLOAD) += snd-compress.o
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
new file mode 100644
index 000000000000..da514fef45bc
--- /dev/null
+++ b/sound/core/compress_offload.c
@@ -0,0 +1,1550 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * compress_core.c - compress offload core
+ *
+ * Copyright (C) 2011 Intel Corporation
+ * Authors: Vinod Koul <vinod.koul@linux.intel.com>
+ * Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__
+#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt)
+
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/math64.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/uio.h>
+#include <linux/uaccess.h>
+#include <linux/dma-buf.h>
+#include <linux/module.h>
+#include <linux/compat.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/compress_params.h>
+#include <sound/compress_offload.h>
+#include <sound/compress_driver.h>
+
+/* struct snd_compr_codec_caps overflows the ioctl bit size for some
+ * architectures, so we need to disable the relevant ioctls.
+ */
+#if _IOC_SIZEBITS < 14
+#define COMPR_CODEC_CAPS_OVERFLOW
+#endif
+
+/* TODO:
+ * - add substream support for multiple devices in case of
+ * SND_DYNAMIC_MINORS is not used
+ * - Multiple node representation
+ * driver should be able to register multiple nodes
+ */
+
+struct snd_compr_file {
+ unsigned long caps;
+ struct snd_compr_stream stream;
+};
+
+static void error_delayed_work(struct work_struct *work);
+
+#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
+static void snd_compr_task_free_all(struct snd_compr_stream *stream);
+#else
+static inline void snd_compr_task_free_all(struct snd_compr_stream *stream) { }
+#endif
+
+/*
+ * a note on stream states used:
+ * we use following states in the compressed core
+ * SNDRV_PCM_STATE_OPEN: When stream has been opened.
+ * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by
+ * calling SNDRV_COMPRESS_SET_PARAMS. Running streams will come to this
+ * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain.
+ * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for
+ * playback only). User after setting up stream writes the data buffer
+ * before starting the stream.
+ * SNDRV_PCM_STATE_RUNNING: When stream has been started and is
+ * decoding/encoding and rendering/capturing data.
+ * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done
+ * by calling SNDRV_COMPRESS_DRAIN.
+ * SNDRV_PCM_STATE_PAUSED: When stream is paused. This is done by calling
+ * SNDRV_COMPRESS_PAUSE. It can be stopped or resumed by calling
+ * SNDRV_COMPRESS_STOP or SNDRV_COMPRESS_RESUME respectively.
+ */
+static int snd_compr_open(struct inode *inode, struct file *f)
+{
+ struct snd_compr *compr;
+ struct snd_compr_file *data;
+ struct snd_compr_runtime *runtime;
+ enum snd_compr_direction dirn;
+ int maj = imajor(inode);
+ int ret;
+
+ if ((f->f_flags & O_ACCMODE) == O_WRONLY)
+ dirn = SND_COMPRESS_PLAYBACK;
+ else if ((f->f_flags & O_ACCMODE) == O_RDONLY)
+ dirn = SND_COMPRESS_CAPTURE;
+ else if ((f->f_flags & O_ACCMODE) == O_RDWR)
+ dirn = SND_COMPRESS_ACCEL;
+ else
+ return -EINVAL;
+
+ if (maj == snd_major)
+ compr = snd_lookup_minor_data(iminor(inode),
+ SNDRV_DEVICE_TYPE_COMPRESS);
+ else
+ return -EBADFD;
+
+ if (compr == NULL) {
+ pr_err("no device data!!!\n");
+ return -ENODEV;
+ }
+
+ if (dirn != compr->direction) {
+ pr_err("this device doesn't support this direction\n");
+ snd_card_unref(compr->card);
+ return -EINVAL;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ snd_card_unref(compr->card);
+ return -ENOMEM;
+ }
+
+ INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work);
+
+ data->stream.ops = compr->ops;
+ data->stream.direction = dirn;
+ data->stream.private_data = compr->private_data;
+ data->stream.device = compr;
+ runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
+ if (!runtime) {
+ kfree(data);
+ snd_card_unref(compr->card);
+ return -ENOMEM;
+ }
+ runtime->state = SNDRV_PCM_STATE_OPEN;
+ init_waitqueue_head(&runtime->sleep);
+#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
+ INIT_LIST_HEAD(&runtime->tasks);
+#endif
+ data->stream.runtime = runtime;
+ f->private_data = (void *)data;
+ scoped_guard(mutex, &compr->lock)
+ ret = compr->ops->open(&data->stream);
+ if (ret) {
+ kfree(runtime);
+ kfree(data);
+ }
+ snd_card_unref(compr->card);
+ return ret;
+}
+
+static int snd_compr_free(struct inode *inode, struct file *f)
+{
+ struct snd_compr_file *data = f->private_data;
+ struct snd_compr_runtime *runtime = data->stream.runtime;
+
+ cancel_delayed_work_sync(&data->stream.error_work);
+
+ switch (runtime->state) {
+ case SNDRV_PCM_STATE_RUNNING:
+ case SNDRV_PCM_STATE_DRAINING:
+ case SNDRV_PCM_STATE_PAUSED:
+ data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP);
+ break;
+ default:
+ break;
+ }
+
+ snd_compr_task_free_all(&data->stream);
+
+ data->stream.ops->free(&data->stream);
+ if (!data->stream.runtime->dma_buffer_p)
+ kfree(data->stream.runtime->buffer);
+ kfree(data->stream.runtime);
+ kfree(data);
+ return 0;
+}
+
+static void
+snd_compr_tstamp32_from_64(struct snd_compr_tstamp *tstamp32,
+ const struct snd_compr_tstamp64 *tstamp64)
+{
+ tstamp32->byte_offset = tstamp64->byte_offset;
+ tstamp32->copied_total = (u32)tstamp64->copied_total;
+ tstamp32->pcm_frames = (u32)tstamp64->pcm_frames;
+ tstamp32->pcm_io_frames = (u32)tstamp64->pcm_io_frames;
+ tstamp32->sampling_rate = tstamp64->sampling_rate;
+}
+
+static int snd_compr_update_tstamp(struct snd_compr_stream *stream,
+ struct snd_compr_tstamp64 *tstamp)
+{
+ if (!stream->ops->pointer)
+ return -ENOTSUPP;
+ stream->ops->pointer(stream, tstamp);
+ pr_debug("dsp consumed till %u total %llu bytes\n", tstamp->byte_offset,
+ tstamp->copied_total);
+ if (stream->direction == SND_COMPRESS_PLAYBACK)
+ stream->runtime->total_bytes_transferred = tstamp->copied_total;
+ else
+ stream->runtime->total_bytes_available = tstamp->copied_total;
+ return 0;
+}
+
+static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
+ struct snd_compr_avail64 *avail)
+{
+ memset(avail, 0, sizeof(*avail));
+ snd_compr_update_tstamp(stream, &avail->tstamp);
+ /* Still need to return avail even if tstamp can't be filled in */
+
+ if (stream->runtime->total_bytes_available == 0 &&
+ stream->runtime->state == SNDRV_PCM_STATE_SETUP &&
+ stream->direction == SND_COMPRESS_PLAYBACK) {
+ pr_debug("detected init and someone forgot to do a write\n");
+ return stream->runtime->buffer_size;
+ }
+ pr_debug("app wrote %llu, DSP consumed %llu\n",
+ stream->runtime->total_bytes_available,
+ stream->runtime->total_bytes_transferred);
+ if (stream->runtime->total_bytes_available ==
+ stream->runtime->total_bytes_transferred) {
+ if (stream->direction == SND_COMPRESS_PLAYBACK) {
+ pr_debug("both pointers are same, returning full avail\n");
+ return stream->runtime->buffer_size;
+ } else {
+ pr_debug("both pointers are same, returning no avail\n");
+ return 0;
+ }
+ }
+
+ avail->avail = stream->runtime->total_bytes_available -
+ stream->runtime->total_bytes_transferred;
+ if (stream->direction == SND_COMPRESS_PLAYBACK)
+ avail->avail = stream->runtime->buffer_size - avail->avail;
+
+ pr_debug("ret avail as %zu\n", (size_t)avail->avail);
+ return avail->avail;
+}
+
+static inline size_t snd_compr_get_avail(struct snd_compr_stream *stream)
+{
+ struct snd_compr_avail64 avail;
+
+ return snd_compr_calc_avail(stream, &avail);
+}
+
+static void snd_compr_avail32_from_64(struct snd_compr_avail *avail32,
+ const struct snd_compr_avail64 *avail64)
+{
+ avail32->avail = avail64->avail;
+ snd_compr_tstamp32_from_64(&avail32->tstamp, &avail64->tstamp);
+}
+
+static int snd_compr_ioctl_avail(struct snd_compr_stream *stream,
+ unsigned long arg, bool is_32bit)
+{
+ struct snd_compr_avail64 ioctl_avail64;
+ struct snd_compr_avail ioctl_avail32;
+ size_t avail;
+ const void *copy_from = &ioctl_avail64;
+ size_t copy_size = sizeof(ioctl_avail64);
+
+ if (stream->direction == SND_COMPRESS_ACCEL)
+ return -EBADFD;
+
+ avail = snd_compr_calc_avail(stream, &ioctl_avail64);
+ ioctl_avail64.avail = avail;
+ if (is_32bit) {
+ snd_compr_avail32_from_64(&ioctl_avail32, &ioctl_avail64);
+ copy_from = &ioctl_avail32;
+ copy_size = sizeof(ioctl_avail32);
+ }
+
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ return -EBADFD;
+ case SNDRV_PCM_STATE_XRUN:
+ return -EPIPE;
+ default:
+ break;
+ }
+
+ if (copy_to_user((__u64 __user *)arg, copy_from, copy_size))
+ return -EFAULT;
+ return 0;
+}
+
+static int snd_compr_write_data(struct snd_compr_stream *stream,
+ const char __user *buf, size_t count)
+{
+ void *dstn;
+ size_t copy;
+ struct snd_compr_runtime *runtime = stream->runtime;
+ /* 64-bit Modulus */
+ u64 app_pointer = div64_u64(runtime->total_bytes_available,
+ runtime->buffer_size);
+ app_pointer = runtime->total_bytes_available -
+ (app_pointer * runtime->buffer_size);
+
+ dstn = runtime->buffer + app_pointer;
+ pr_debug("copying %lu at %llu\n", (unsigned long)count, app_pointer);
+ if (count < runtime->buffer_size - app_pointer) {
+ if (copy_from_user(dstn, buf, count))
+ return -EFAULT;
+ } else {
+ copy = runtime->buffer_size - app_pointer;
+ if (copy_from_user(dstn, buf, copy))
+ return -EFAULT;
+ if (copy_from_user(runtime->buffer, buf + copy, count - copy))
+ return -EFAULT;
+ }
+ /* if DSP cares, let it know data has been written */
+ if (stream->ops->ack)
+ stream->ops->ack(stream, count);
+ return count;
+}
+
+static ssize_t snd_compr_write(struct file *f, const char __user *buf,
+ size_t count, loff_t *offset)
+{
+ struct snd_compr_file *data = f->private_data;
+ struct snd_compr_stream *stream;
+ size_t avail;
+ int retval;
+
+ if (snd_BUG_ON(!data))
+ return -EFAULT;
+
+ stream = &data->stream;
+ if (stream->direction == SND_COMPRESS_ACCEL)
+ return -EBADFD;
+ guard(mutex)(&stream->device->lock);
+ /* write is allowed when stream is running or has been setup */
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_SETUP:
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_RUNNING:
+ break;
+ default:
+ return -EBADFD;
+ }
+
+ avail = snd_compr_get_avail(stream);
+ pr_debug("avail returned %lu\n", (unsigned long)avail);
+ /* calculate how much we can write to buffer */
+ if (avail > count)
+ avail = count;
+
+ if (stream->ops->copy) {
+ char __user* cbuf = (char __user*)buf;
+ retval = stream->ops->copy(stream, cbuf, avail);
+ } else {
+ retval = snd_compr_write_data(stream, buf, avail);
+ }
+ if (retval > 0)
+ stream->runtime->total_bytes_available += retval;
+
+ /* while initiating the stream, write should be called before START
+ * call, so in setup move state */
+ if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) {
+ stream->runtime->state = SNDRV_PCM_STATE_PREPARED;
+ pr_debug("stream prepared, Houston we are good to go\n");
+ }
+
+ return retval;
+}
+
+
+static ssize_t snd_compr_read(struct file *f, char __user *buf,
+ size_t count, loff_t *offset)
+{
+ struct snd_compr_file *data = f->private_data;
+ struct snd_compr_stream *stream;
+ size_t avail;
+ int retval;
+
+ if (snd_BUG_ON(!data))
+ return -EFAULT;
+
+ stream = &data->stream;
+ if (stream->direction == SND_COMPRESS_ACCEL)
+ return -EBADFD;
+ guard(mutex)(&stream->device->lock);
+
+ /* read is allowed when stream is running, paused, draining and setup
+ * (yes setup is state which we transition to after stop, so if user
+ * wants to read data after stop we allow that)
+ */
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_SUSPENDED:
+ case SNDRV_PCM_STATE_DISCONNECTED:
+ return -EBADFD;
+ case SNDRV_PCM_STATE_XRUN:
+ return -EPIPE;
+ }
+
+ avail = snd_compr_get_avail(stream);
+ pr_debug("avail returned %lu\n", (unsigned long)avail);
+ /* calculate how much we can read from buffer */
+ if (avail > count)
+ avail = count;
+
+ if (stream->ops->copy)
+ retval = stream->ops->copy(stream, buf, avail);
+ else
+ return -ENXIO;
+ if (retval > 0)
+ stream->runtime->total_bytes_transferred += retval;
+
+ return retval;
+}
+
+static int snd_compr_mmap(struct file *f, struct vm_area_struct *vma)
+{
+ return -ENXIO;
+}
+
+static __poll_t snd_compr_get_poll(struct snd_compr_stream *stream)
+{
+ if (stream->direction == SND_COMPRESS_PLAYBACK)
+ return EPOLLOUT | EPOLLWRNORM;
+ else
+ return EPOLLIN | EPOLLRDNORM;
+}
+
+static __poll_t snd_compr_poll(struct file *f, poll_table *wait)
+{
+ struct snd_compr_file *data = f->private_data;
+ struct snd_compr_stream *stream;
+ struct snd_compr_runtime *runtime;
+ size_t avail;
+ __poll_t retval = 0;
+
+ if (snd_BUG_ON(!data))
+ return EPOLLERR;
+
+ stream = &data->stream;
+ runtime = stream->runtime;
+
+ guard(mutex)(&stream->device->lock);
+
+ switch (runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_XRUN:
+ return snd_compr_get_poll(stream) | EPOLLERR;
+ default:
+ break;
+ }
+
+ poll_wait(f, &runtime->sleep, wait);
+
+#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
+ if (stream->direction == SND_COMPRESS_ACCEL) {
+ struct snd_compr_task_runtime *task;
+ if (runtime->fragments > runtime->active_tasks)
+ retval |= EPOLLOUT | EPOLLWRNORM;
+ task = list_first_entry_or_null(&runtime->tasks,
+ struct snd_compr_task_runtime,
+ list);
+ if (task && task->state == SND_COMPRESS_TASK_STATE_FINISHED)
+ retval |= EPOLLIN | EPOLLRDNORM;
+ return retval;
+ }
+#endif
+
+ avail = snd_compr_get_avail(stream);
+ pr_debug("avail is %lu\n", (unsigned long)avail);
+ /* check if we have at least one fragment to fill */
+ switch (runtime->state) {
+ case SNDRV_PCM_STATE_DRAINING:
+ /* stream has been woken up after drain is complete
+ * draining done so set stream state to stopped
+ */
+ retval = snd_compr_get_poll(stream);
+ runtime->state = SNDRV_PCM_STATE_SETUP;
+ break;
+ case SNDRV_PCM_STATE_RUNNING:
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_PAUSED:
+ if (avail >= runtime->fragment_size)
+ retval = snd_compr_get_poll(stream);
+ break;
+ default:
+ return snd_compr_get_poll(stream) | EPOLLERR;
+ }
+
+ return retval;
+}
+
+static int
+snd_compr_get_caps(struct snd_compr_stream *stream, unsigned long arg)
+{
+ int retval;
+ struct snd_compr_caps caps;
+
+ if (!stream->ops->get_caps)
+ return -ENXIO;
+
+ memset(&caps, 0, sizeof(caps));
+ retval = stream->ops->get_caps(stream, &caps);
+ if (retval)
+ goto out;
+ if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
+ retval = -EFAULT;
+out:
+ return retval;
+}
+
+#ifndef COMPR_CODEC_CAPS_OVERFLOW
+static int
+snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
+{
+ int retval;
+ struct snd_compr_codec_caps *caps __free(kfree) = NULL;
+
+ if (!stream->ops->get_codec_caps)
+ return -ENXIO;
+
+ caps = kzalloc(sizeof(*caps), GFP_KERNEL);
+ if (!caps)
+ return -ENOMEM;
+
+ retval = stream->ops->get_codec_caps(stream, caps);
+ if (retval)
+ return retval;
+ if (copy_to_user((void __user *)arg, caps, sizeof(*caps)))
+ return -EFAULT;
+ return retval;
+}
+#endif /* !COMPR_CODEC_CAPS_OVERFLOW */
+
+int snd_compr_malloc_pages(struct snd_compr_stream *stream, size_t size)
+{
+ struct snd_dma_buffer *dmab;
+ int ret;
+
+ if (snd_BUG_ON(!(stream) || !(stream)->runtime))
+ return -EINVAL;
+ dmab = kzalloc(sizeof(*dmab), GFP_KERNEL);
+ if (!dmab)
+ return -ENOMEM;
+ dmab->dev = stream->dma_buffer.dev;
+ ret = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev, size, dmab);
+ if (ret < 0) {
+ kfree(dmab);
+ return ret;
+ }
+
+ snd_compr_set_runtime_buffer(stream, dmab);
+ stream->runtime->dma_bytes = size;
+ return 1;
+}
+EXPORT_SYMBOL(snd_compr_malloc_pages);
+
+int snd_compr_free_pages(struct snd_compr_stream *stream)
+{
+ struct snd_compr_runtime *runtime;
+
+ if (snd_BUG_ON(!(stream) || !(stream)->runtime))
+ return -EINVAL;
+ runtime = stream->runtime;
+ if (runtime->dma_area == NULL)
+ return 0;
+ if (runtime->dma_buffer_p != &stream->dma_buffer) {
+ /* It's a newly allocated buffer. Release it now. */
+ snd_dma_free_pages(runtime->dma_buffer_p);
+ kfree(runtime->dma_buffer_p);
+ }
+
+ snd_compr_set_runtime_buffer(stream, NULL);
+ return 0;
+}
+EXPORT_SYMBOL(snd_compr_free_pages);
+
+/* revisit this with snd_pcm_preallocate_xxx */
+static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
+ struct snd_compr_params *params)
+{
+ unsigned int buffer_size;
+ void *buffer = NULL;
+
+ if (stream->direction == SND_COMPRESS_ACCEL)
+ goto params;
+
+ buffer_size = params->buffer.fragment_size * params->buffer.fragments;
+ if (stream->ops->copy) {
+ buffer = NULL;
+ /* if copy is defined the driver will be required to copy
+ * the data from core
+ */
+ } else {
+ if (stream->runtime->dma_buffer_p) {
+
+ if (buffer_size > stream->runtime->dma_buffer_p->bytes)
+ dev_err(stream->device->dev,
+ "Not enough DMA buffer");
+ else
+ buffer = stream->runtime->dma_buffer_p->area;
+
+ } else {
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
+ }
+
+ if (!buffer)
+ return -ENOMEM;
+ }
+
+ stream->runtime->buffer = buffer;
+ stream->runtime->buffer_size = buffer_size;
+params:
+ stream->runtime->fragment_size = params->buffer.fragment_size;
+ stream->runtime->fragments = params->buffer.fragments;
+ return 0;
+}
+
+static int
+snd_compress_check_input(struct snd_compr_stream *stream, struct snd_compr_params *params)
+{
+ u32 max_fragments;
+
+ /* first let's check the buffer parameter's */
+ if (params->buffer.fragment_size == 0)
+ return -EINVAL;
+
+ if (stream->direction == SND_COMPRESS_ACCEL)
+ max_fragments = 64; /* safe value */
+ else
+ max_fragments = U32_MAX / params->buffer.fragment_size;
+
+ if (params->buffer.fragments > max_fragments ||
+ params->buffer.fragments == 0)
+ return -EINVAL;
+
+ /* now codec parameters */
+ if (params->codec.id == 0 || params->codec.id > SND_AUDIOCODEC_MAX)
+ return -EINVAL;
+
+ if (params->codec.ch_in == 0 || params->codec.ch_out == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int
+snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_params *params __free(kfree) = NULL;
+ int retval;
+
+ if (stream->runtime->state == SNDRV_PCM_STATE_OPEN || stream->next_track) {
+ /*
+ * we should allow parameter change only when stream has been
+ * opened not in other cases
+ */
+ params = memdup_user((void __user *)arg, sizeof(*params));
+ if (IS_ERR(params))
+ return PTR_ERR(params);
+
+ retval = snd_compress_check_input(stream, params);
+ if (retval)
+ return retval;
+
+ retval = snd_compr_allocate_buffer(stream, params);
+ if (retval)
+ return -ENOMEM;
+
+ retval = stream->ops->set_params(stream, params);
+ if (retval)
+ return retval;
+
+ if (stream->next_track)
+ return retval;
+
+ stream->metadata_set = false;
+ stream->next_track = false;
+
+ stream->runtime->state = SNDRV_PCM_STATE_SETUP;
+ } else {
+ return -EPERM;
+ }
+ return retval;
+}
+
+static int
+snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_codec *params __free(kfree) = NULL;
+ int retval;
+
+ if (!stream->ops->get_params)
+ return -EBADFD;
+
+ params = kzalloc(sizeof(*params), GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+ retval = stream->ops->get_params(stream, params);
+ if (retval)
+ return retval;
+ if (copy_to_user((char __user *)arg, params, sizeof(*params)))
+ return -EFAULT;
+ return retval;
+}
+
+static int
+snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_metadata metadata;
+ int retval;
+
+ if (!stream->ops->get_metadata)
+ return -ENXIO;
+
+ if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
+ return -EFAULT;
+
+ retval = stream->ops->get_metadata(stream, &metadata);
+ if (retval != 0)
+ return retval;
+
+ if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int
+snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_metadata metadata;
+ int retval;
+
+ if (!stream->ops->set_metadata)
+ return -ENXIO;
+ /*
+ * we should allow parameter change only when stream has been
+ * opened not in other cases
+ */
+ if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
+ return -EFAULT;
+
+ retval = stream->ops->set_metadata(stream, &metadata);
+ stream->metadata_set = true;
+
+ return retval;
+}
+
+static inline int snd_compr_tstamp(struct snd_compr_stream *stream,
+ unsigned long arg, bool is_32bit)
+{
+ struct snd_compr_tstamp64 tstamp64 = { 0 };
+ struct snd_compr_tstamp tstamp32 = { 0 };
+ const void *copy_from = &tstamp64;
+ size_t copy_size = sizeof(tstamp64);
+ int ret;
+
+ ret = snd_compr_update_tstamp(stream, &tstamp64);
+ if (ret == 0) {
+ if (is_32bit) {
+ snd_compr_tstamp32_from_64(&tstamp32, &tstamp64);
+ copy_from = &tstamp32;
+ copy_size = sizeof(tstamp32);
+ }
+ ret = copy_to_user((void __user *)arg, copy_from, copy_size) ?
+ -EFAULT :
+ 0;
+ }
+ return ret;
+}
+
+static int snd_compr_pause(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_RUNNING:
+ retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
+ if (!retval)
+ stream->runtime->state = SNDRV_PCM_STATE_PAUSED;
+ break;
+ case SNDRV_PCM_STATE_DRAINING:
+ if (!stream->device->use_pause_in_draining)
+ return -EPERM;
+ retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH);
+ if (!retval)
+ stream->pause_in_draining = true;
+ break;
+ default:
+ return -EPERM;
+ }
+ return retval;
+}
+
+static int snd_compr_resume(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_PAUSED:
+ retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
+ if (!retval)
+ stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
+ break;
+ case SNDRV_PCM_STATE_DRAINING:
+ if (!stream->pause_in_draining)
+ return -EPERM;
+ retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
+ if (!retval)
+ stream->pause_in_draining = false;
+ break;
+ default:
+ return -EPERM;
+ }
+ return retval;
+}
+
+static int snd_compr_start(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_SETUP:
+ if (stream->direction != SND_COMPRESS_CAPTURE)
+ return -EPERM;
+ break;
+ case SNDRV_PCM_STATE_PREPARED:
+ break;
+ default:
+ return -EPERM;
+ }
+
+ retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START);
+ if (!retval)
+ stream->runtime->state = SNDRV_PCM_STATE_RUNNING;
+ return retval;
+}
+
+static int snd_compr_stop(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_SETUP:
+ case SNDRV_PCM_STATE_PREPARED:
+ return -EPERM;
+ default:
+ break;
+ }
+
+ retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
+ if (!retval) {
+ /* clear flags and stop any drain wait */
+ stream->partial_drain = false;
+ stream->metadata_set = false;
+ stream->pause_in_draining = false;
+ snd_compr_drain_notify(stream);
+ stream->runtime->total_bytes_available = 0;
+ stream->runtime->total_bytes_transferred = 0;
+ }
+ return retval;
+}
+
+static void error_delayed_work(struct work_struct *work)
+{
+ struct snd_compr_stream *stream;
+
+ stream = container_of(work, struct snd_compr_stream, error_work.work);
+
+ guard(mutex)(&stream->device->lock);
+
+ stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
+ wake_up(&stream->runtime->sleep);
+}
+
+/**
+ * snd_compr_stop_error: Report a fatal error on a stream
+ * @stream: pointer to stream
+ * @state: state to transition the stream to
+ *
+ * Stop the stream and set its state.
+ *
+ * Should be called with compressed device lock held.
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_compr_stop_error(struct snd_compr_stream *stream,
+ snd_pcm_state_t state)
+{
+ if (stream->runtime->state == state)
+ return 0;
+
+ stream->runtime->state = state;
+
+ pr_debug("Changing state to: %d\n", state);
+
+ queue_delayed_work(system_power_efficient_wq, &stream->error_work, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_compr_stop_error);
+
+static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
+{
+ int ret;
+
+ /*
+ * We are called with lock held. So drop the lock while we wait for
+ * drain complete notification from the driver
+ *
+ * It is expected that driver will notify the drain completion and then
+ * stream will be moved to SETUP state, even if draining resulted in an
+ * error. We can trigger next track after this.
+ */
+ stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+ mutex_unlock(&stream->device->lock);
+
+ /* we wait for drain to complete here, drain can return when
+ * interruption occurred, wait returned error or success.
+ * For the first two cases we don't do anything different here and
+ * return after waking up
+ */
+
+ ret = wait_event_interruptible(stream->runtime->sleep,
+ (stream->runtime->state != SNDRV_PCM_STATE_DRAINING));
+ if (ret == -ERESTARTSYS)
+ pr_debug("wait aborted by a signal\n");
+ else if (ret)
+ pr_debug("wait for drain failed with %d\n", ret);
+
+
+ wake_up(&stream->runtime->sleep);
+ mutex_lock(&stream->device->lock);
+
+ return ret;
+}
+
+static int snd_compr_drain(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_SETUP:
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_PAUSED:
+ return -EPERM;
+ case SNDRV_PCM_STATE_XRUN:
+ return -EPIPE;
+ default:
+ break;
+ }
+
+ retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
+ if (retval) {
+ pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
+ wake_up(&stream->runtime->sleep);
+ return retval;
+ }
+
+ return snd_compress_wait_for_drain(stream);
+}
+
+static int snd_compr_next_track(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ /* only a running stream can transition to next track */
+ if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
+ return -EPERM;
+
+ /* next track doesn't have any meaning for capture streams */
+ if (stream->direction == SND_COMPRESS_CAPTURE)
+ return -EPERM;
+
+ /* you can signal next track if this is intended to be a gapless stream
+ * and current track metadata is set
+ */
+ if (stream->metadata_set == false)
+ return -EPERM;
+
+ retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK);
+ if (retval != 0)
+ return retval;
+ stream->metadata_set = false;
+ stream->next_track = true;
+ return 0;
+}
+
+static int snd_compr_partial_drain(struct snd_compr_stream *stream)
+{
+ int retval;
+
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_SETUP:
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_PAUSED:
+ return -EPERM;
+ case SNDRV_PCM_STATE_XRUN:
+ return -EPIPE;
+ default:
+ break;
+ }
+
+ /* partial drain doesn't have any meaning for capture streams */
+ if (stream->direction == SND_COMPRESS_CAPTURE)
+ return -EPERM;
+
+ /* stream can be drained only when next track has been signalled */
+ if (stream->next_track == false)
+ return -EPERM;
+
+ stream->partial_drain = true;
+ retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
+ if (retval) {
+ pr_debug("Partial drain returned failure\n");
+ wake_up(&stream->runtime->sleep);
+ return retval;
+ }
+
+ stream->next_track = false;
+ return snd_compress_wait_for_drain(stream);
+}
+
+#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
+
+static struct snd_compr_task_runtime *
+snd_compr_find_task(struct snd_compr_stream *stream, __u64 seqno)
+{
+ struct snd_compr_task_runtime *task;
+
+ list_for_each_entry(task, &stream->runtime->tasks, list) {
+ if (task->seqno == seqno)
+ return task;
+ }
+ return NULL;
+}
+
+static void snd_compr_task_free(struct snd_compr_task_runtime *task)
+{
+ if (task->output)
+ dma_buf_put(task->output);
+ if (task->input)
+ dma_buf_put(task->input);
+ kfree(task);
+}
+
+static u64 snd_compr_seqno_next(struct snd_compr_stream *stream)
+{
+ u64 seqno = ++stream->runtime->task_seqno;
+ if (seqno == 0)
+ seqno = ++stream->runtime->task_seqno;
+ return seqno;
+}
+
+static int snd_compr_task_new(struct snd_compr_stream *stream, struct snd_compr_task *utask)
+{
+ struct snd_compr_task_runtime *task;
+ int retval, fd_i, fd_o;
+
+ if (stream->runtime->total_tasks >= stream->runtime->fragments)
+ return -EBUSY;
+ if (utask->origin_seqno != 0 || utask->input_size != 0)
+ return -EINVAL;
+ task = kzalloc(sizeof(*task), GFP_KERNEL);
+ if (task == NULL)
+ return -ENOMEM;
+ task->seqno = utask->seqno = snd_compr_seqno_next(stream);
+ task->input_size = utask->input_size;
+ retval = stream->ops->task_create(stream, task);
+ if (retval < 0)
+ goto cleanup;
+ /* similar functionality as in dma_buf_fd(), but ensure that both
+ file descriptors are allocated before fd_install() */
+ if (!task->input || !task->input->file || !task->output || !task->output->file) {
+ retval = -EINVAL;
+ goto cleanup;
+ }
+ fd_i = get_unused_fd_flags(O_WRONLY|O_CLOEXEC);
+ if (fd_i < 0)
+ goto cleanup;
+ fd_o = get_unused_fd_flags(O_RDONLY|O_CLOEXEC);
+ if (fd_o < 0) {
+ put_unused_fd(fd_i);
+ goto cleanup;
+ }
+ /* keep dmabuf reference until freed with task free ioctl */
+ get_dma_buf(task->input);
+ get_dma_buf(task->output);
+ fd_install(fd_i, task->input->file);
+ fd_install(fd_o, task->output->file);
+ utask->input_fd = fd_i;
+ utask->output_fd = fd_o;
+ list_add_tail(&task->list, &stream->runtime->tasks);
+ stream->runtime->total_tasks++;
+ return 0;
+cleanup:
+ snd_compr_task_free(task);
+ return retval;
+}
+
+static int snd_compr_task_create(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_task *task __free(kfree) = NULL;
+ int retval;
+
+ if (stream->runtime->state != SNDRV_PCM_STATE_SETUP)
+ return -EPERM;
+ task = memdup_user((void __user *)arg, sizeof(*task));
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+ retval = snd_compr_task_new(stream, task);
+ if (retval >= 0)
+ if (copy_to_user((void __user *)arg, task, sizeof(*task)))
+ retval = -EFAULT;
+ return retval;
+}
+
+static int snd_compr_task_start_prepare(struct snd_compr_task_runtime *task,
+ struct snd_compr_task *utask)
+{
+ if (task == NULL)
+ return -EINVAL;
+ if (task->state >= SND_COMPRESS_TASK_STATE_FINISHED)
+ return -EBUSY;
+ if (utask->input_size > task->input->size)
+ return -EINVAL;
+ task->flags = utask->flags;
+ task->input_size = utask->input_size;
+ task->state = SND_COMPRESS_TASK_STATE_IDLE;
+ return 0;
+}
+
+static int snd_compr_task_start(struct snd_compr_stream *stream, struct snd_compr_task *utask)
+{
+ struct snd_compr_task_runtime *task;
+ int retval;
+
+ if (utask->origin_seqno > 0) {
+ task = snd_compr_find_task(stream, utask->origin_seqno);
+ retval = snd_compr_task_start_prepare(task, utask);
+ if (retval < 0)
+ return retval;
+ task->seqno = utask->seqno = snd_compr_seqno_next(stream);
+ utask->origin_seqno = 0;
+ list_move_tail(&task->list, &stream->runtime->tasks);
+ } else {
+ task = snd_compr_find_task(stream, utask->seqno);
+ if (task && task->state != SND_COMPRESS_TASK_STATE_IDLE)
+ return -EBUSY;
+ retval = snd_compr_task_start_prepare(task, utask);
+ if (retval < 0)
+ return retval;
+ }
+ retval = stream->ops->task_start(stream, task);
+ if (retval >= 0) {
+ task->state = SND_COMPRESS_TASK_STATE_ACTIVE;
+ stream->runtime->active_tasks++;
+ }
+ return retval;
+}
+
+static int snd_compr_task_start_ioctl(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_task *task __free(kfree) = NULL;
+ int retval;
+
+ if (stream->runtime->state != SNDRV_PCM_STATE_SETUP)
+ return -EPERM;
+ task = memdup_user((void __user *)arg, sizeof(*task));
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+ retval = snd_compr_task_start(stream, task);
+ if (retval >= 0)
+ if (copy_to_user((void __user *)arg, task, sizeof(*task)))
+ retval = -EFAULT;
+ return retval;
+}
+
+static void snd_compr_task_stop_one(struct snd_compr_stream *stream,
+ struct snd_compr_task_runtime *task)
+{
+ if (task->state != SND_COMPRESS_TASK_STATE_ACTIVE)
+ return;
+ stream->ops->task_stop(stream, task);
+ if (!snd_BUG_ON(stream->runtime->active_tasks == 0))
+ stream->runtime->active_tasks--;
+ list_move_tail(&task->list, &stream->runtime->tasks);
+ task->state = SND_COMPRESS_TASK_STATE_IDLE;
+}
+
+static void snd_compr_task_free_one(struct snd_compr_stream *stream,
+ struct snd_compr_task_runtime *task)
+{
+ snd_compr_task_stop_one(stream, task);
+ stream->ops->task_free(stream, task);
+ list_del(&task->list);
+ snd_compr_task_free(task);
+ stream->runtime->total_tasks--;
+}
+
+static void snd_compr_task_free_all(struct snd_compr_stream *stream)
+{
+ struct snd_compr_task_runtime *task, *temp;
+
+ list_for_each_entry_safe_reverse(task, temp, &stream->runtime->tasks, list)
+ snd_compr_task_free_one(stream, task);
+}
+
+typedef void (*snd_compr_seq_func_t)(struct snd_compr_stream *stream,
+ struct snd_compr_task_runtime *task);
+
+static int snd_compr_task_seq(struct snd_compr_stream *stream, unsigned long arg,
+ snd_compr_seq_func_t fcn)
+{
+ struct snd_compr_task_runtime *task, *temp;
+ __u64 seqno;
+ int retval;
+
+ if (stream->runtime->state != SNDRV_PCM_STATE_SETUP)
+ return -EPERM;
+ retval = copy_from_user(&seqno, (__u64 __user *)arg, sizeof(seqno));
+ if (retval)
+ return -EFAULT;
+ retval = 0;
+ if (seqno == 0) {
+ list_for_each_entry_safe_reverse(task, temp, &stream->runtime->tasks, list)
+ fcn(stream, task);
+ } else {
+ task = snd_compr_find_task(stream, seqno);
+ if (task == NULL) {
+ retval = -EINVAL;
+ } else {
+ fcn(stream, task);
+ }
+ }
+ return retval;
+}
+
+static int snd_compr_task_status(struct snd_compr_stream *stream,
+ struct snd_compr_task_status *status)
+{
+ struct snd_compr_task_runtime *task;
+
+ task = snd_compr_find_task(stream, status->seqno);
+ if (task == NULL)
+ return -EINVAL;
+ status->input_size = task->input_size;
+ status->output_size = task->output_size;
+ status->state = task->state;
+ return 0;
+}
+
+static int snd_compr_task_status_ioctl(struct snd_compr_stream *stream, unsigned long arg)
+{
+ struct snd_compr_task_status *status __free(kfree) = NULL;
+ int retval;
+
+ if (stream->runtime->state != SNDRV_PCM_STATE_SETUP)
+ return -EPERM;
+ status = memdup_user((void __user *)arg, sizeof(*status));
+ if (IS_ERR(status))
+ return PTR_ERR(status);
+ retval = snd_compr_task_status(stream, status);
+ if (retval >= 0)
+ if (copy_to_user((void __user *)arg, status, sizeof(*status)))
+ retval = -EFAULT;
+ return retval;
+}
+
+/**
+ * snd_compr_task_finished: Notify that the task was finished
+ * @stream: pointer to stream
+ * @task: runtime task structure
+ *
+ * Set the finished task state and notify waiters.
+ */
+void snd_compr_task_finished(struct snd_compr_stream *stream,
+ struct snd_compr_task_runtime *task)
+{
+ guard(mutex)(&stream->device->lock);
+ if (!snd_BUG_ON(stream->runtime->active_tasks == 0))
+ stream->runtime->active_tasks--;
+ task->state = SND_COMPRESS_TASK_STATE_FINISHED;
+ wake_up(&stream->runtime->sleep);
+}
+EXPORT_SYMBOL_GPL(snd_compr_task_finished);
+
+MODULE_IMPORT_NS("DMA_BUF");
+#endif /* CONFIG_SND_COMPRESS_ACCEL */
+
+static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+ struct snd_compr_file *data = f->private_data;
+ struct snd_compr_stream *stream;
+
+ if (snd_BUG_ON(!data))
+ return -EFAULT;
+
+ stream = &data->stream;
+
+ guard(mutex)(&stream->device->lock);
+ switch (cmd) {
+ case SNDRV_COMPRESS_IOCTL_VERSION:
+ return put_user(SNDRV_COMPRESS_VERSION,
+ (int __user *)arg) ? -EFAULT : 0;
+ case SNDRV_COMPRESS_GET_CAPS:
+ return snd_compr_get_caps(stream, arg);
+#ifndef COMPR_CODEC_CAPS_OVERFLOW
+ case SNDRV_COMPRESS_GET_CODEC_CAPS:
+ return snd_compr_get_codec_caps(stream, arg);
+#endif
+ case SNDRV_COMPRESS_SET_PARAMS:
+ return snd_compr_set_params(stream, arg);
+ case SNDRV_COMPRESS_GET_PARAMS:
+ return snd_compr_get_params(stream, arg);
+ case SNDRV_COMPRESS_SET_METADATA:
+ return snd_compr_set_metadata(stream, arg);
+ case SNDRV_COMPRESS_GET_METADATA:
+ return snd_compr_get_metadata(stream, arg);
+ }
+
+ if (stream->direction == SND_COMPRESS_ACCEL) {
+#if IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
+ switch (cmd) {
+ case SNDRV_COMPRESS_TASK_CREATE:
+ return snd_compr_task_create(stream, arg);
+ case SNDRV_COMPRESS_TASK_FREE:
+ return snd_compr_task_seq(stream, arg, snd_compr_task_free_one);
+ case SNDRV_COMPRESS_TASK_START:
+ return snd_compr_task_start_ioctl(stream, arg);
+ case SNDRV_COMPRESS_TASK_STOP:
+ return snd_compr_task_seq(stream, arg, snd_compr_task_stop_one);
+ case SNDRV_COMPRESS_TASK_STATUS:
+ return snd_compr_task_status_ioctl(stream, arg);
+ }
+#endif
+ return -ENOTTY;
+ }
+
+ switch (cmd) {
+ case SNDRV_COMPRESS_TSTAMP:
+ return snd_compr_tstamp(stream, arg, true);
+ case SNDRV_COMPRESS_TSTAMP64:
+ return snd_compr_tstamp(stream, arg, false);
+ case SNDRV_COMPRESS_AVAIL:
+ return snd_compr_ioctl_avail(stream, arg, true);
+ case SNDRV_COMPRESS_AVAIL64:
+ return snd_compr_ioctl_avail(stream, arg, false);
+ case SNDRV_COMPRESS_PAUSE:
+ return snd_compr_pause(stream);
+ case SNDRV_COMPRESS_RESUME:
+ return snd_compr_resume(stream);
+ case SNDRV_COMPRESS_START:
+ return snd_compr_start(stream);
+ case SNDRV_COMPRESS_STOP:
+ return snd_compr_stop(stream);
+ case SNDRV_COMPRESS_DRAIN:
+ return snd_compr_drain(stream);
+ case SNDRV_COMPRESS_PARTIAL_DRAIN:
+ return snd_compr_partial_drain(stream);
+ case SNDRV_COMPRESS_NEXT_TRACK:
+ return snd_compr_next_track(stream);
+ }
+
+ return -ENOTTY;
+}
+
+/* support of 32bit userspace on 64bit platforms */
+#ifdef CONFIG_COMPAT
+static long snd_compr_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return snd_compr_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static const struct file_operations snd_compr_file_ops = {
+ .owner = THIS_MODULE,
+ .open = snd_compr_open,
+ .release = snd_compr_free,
+ .write = snd_compr_write,
+ .read = snd_compr_read,
+ .unlocked_ioctl = snd_compr_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = snd_compr_ioctl_compat,
+#endif
+ .mmap = snd_compr_mmap,
+ .poll = snd_compr_poll,
+};
+
+static int snd_compress_dev_register(struct snd_device *device)
+{
+ int ret;
+ struct snd_compr *compr;
+
+ if (snd_BUG_ON(!device || !device->device_data))
+ return -EBADFD;
+ compr = device->device_data;
+
+ pr_debug("reg device %s, direction %d\n", compr->name,
+ compr->direction);
+ /* register compressed device */
+ ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS,
+ compr->card, compr->device,
+ &snd_compr_file_ops, compr, compr->dev);
+ if (ret < 0) {
+ pr_err("snd_register_device failed %d\n", ret);
+ return ret;
+ }
+ return ret;
+
+}
+
+static int snd_compress_dev_disconnect(struct snd_device *device)
+{
+ struct snd_compr *compr;
+
+ compr = device->device_data;
+ snd_unregister_device(compr->dev);
+ return 0;
+}
+
+#ifdef CONFIG_SND_VERBOSE_PROCFS
+static void snd_compress_proc_info_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_compr *compr = (struct snd_compr *)entry->private_data;
+
+ snd_iprintf(buffer, "card: %d\n", compr->card->number);
+ snd_iprintf(buffer, "device: %d\n", compr->device);
+ snd_iprintf(buffer, "stream: %s\n",
+ compr->direction == SND_COMPRESS_PLAYBACK
+ ? "PLAYBACK" : "CAPTURE");
+ snd_iprintf(buffer, "id: %s\n", compr->id);
+}
+
+static int snd_compress_proc_init(struct snd_compr *compr)
+{
+ struct snd_info_entry *entry;
+ char name[16];
+
+ sprintf(name, "compr%i", compr->device);
+ entry = snd_info_create_card_entry(compr->card, name,
+ compr->card->proc_root);
+ if (!entry)
+ return -ENOMEM;
+ entry->mode = S_IFDIR | 0555;
+ compr->proc_root = entry;
+
+ entry = snd_info_create_card_entry(compr->card, "info",
+ compr->proc_root);
+ if (entry)
+ snd_info_set_text_ops(entry, compr,
+ snd_compress_proc_info_read);
+ compr->proc_info_entry = entry;
+
+ return 0;
+}
+
+static void snd_compress_proc_done(struct snd_compr *compr)
+{
+ snd_info_free_entry(compr->proc_info_entry);
+ compr->proc_info_entry = NULL;
+ snd_info_free_entry(compr->proc_root);
+ compr->proc_root = NULL;
+}
+
+static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)
+{
+ strscpy(compr->id, id, sizeof(compr->id));
+}
+#else
+static inline int snd_compress_proc_init(struct snd_compr *compr)
+{
+ return 0;
+}
+
+static inline void snd_compress_proc_done(struct snd_compr *compr)
+{
+}
+
+static inline void snd_compress_set_id(struct snd_compr *compr, const char *id)
+{
+}
+#endif
+
+static int snd_compress_dev_free(struct snd_device *device)
+{
+ struct snd_compr *compr;
+
+ compr = device->device_data;
+ snd_compress_proc_done(compr);
+ put_device(compr->dev);
+ return 0;
+}
+
+/**
+ * snd_compress_new: create new compress device
+ * @card: sound card pointer
+ * @device: device number
+ * @dirn: device direction, should be of type enum snd_compr_direction
+ * @id: ID string
+ * @compr: compress device pointer
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_compress_new(struct snd_card *card, int device,
+ int dirn, const char *id, struct snd_compr *compr)
+{
+ static const struct snd_device_ops ops = {
+ .dev_free = snd_compress_dev_free,
+ .dev_register = snd_compress_dev_register,
+ .dev_disconnect = snd_compress_dev_disconnect,
+ };
+ int ret;
+
+#if !IS_ENABLED(CONFIG_SND_COMPRESS_ACCEL)
+ if (snd_BUG_ON(dirn == SND_COMPRESS_ACCEL))
+ return -EINVAL;
+#endif
+
+ compr->card = card;
+ compr->device = device;
+ compr->direction = dirn;
+ mutex_init(&compr->lock);
+
+ snd_compress_set_id(compr, id);
+
+ ret = snd_device_alloc(&compr->dev, card);
+ if (ret)
+ return ret;
+ dev_set_name(compr->dev, "comprC%iD%i", card->number, device);
+
+ ret = snd_device_new(card, SNDRV_DEV_COMPRESS, compr, &ops);
+ if (ret == 0)
+ snd_compress_proc_init(compr);
+ else
+ put_device(compr->dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_compress_new);
+
+MODULE_DESCRIPTION("ALSA Compressed offload framework");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/core/control.c b/sound/core/control.c
index 45a818002d99..9c3fd5113a61 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -1,36 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for driver control interface
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/threads.h>
#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/math64.h>
+#include <linux/sched/signal.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
#include <sound/control.h>
-/* max number of user-defined controls */
-#define MAX_USER_CONTROLS 32
+// Max allocation size for user controls.
+static int max_user_ctl_alloc_size = 8 * 1024 * 1024;
+module_param_named(max_user_ctl_alloc_size, max_user_ctl_alloc_size, int, 0444);
+MODULE_PARM_DESC(max_user_ctl_alloc_size, "Max allocation size for user controls");
+
#define MAX_CONTROL_COUNT 1028
struct snd_kctl_ioctl {
@@ -39,19 +32,23 @@ struct snd_kctl_ioctl {
};
static DECLARE_RWSEM(snd_ioctl_rwsem);
+static DECLARE_RWSEM(snd_ctl_layer_rwsem);
static LIST_HEAD(snd_control_ioctls);
#ifdef CONFIG_COMPAT
static LIST_HEAD(snd_control_compat_ioctls);
#endif
+static struct snd_ctl_layer_ops *snd_ctl_layer;
+
+static int snd_ctl_remove_locked(struct snd_card *card,
+ struct snd_kcontrol *kcontrol);
static int snd_ctl_open(struct inode *inode, struct file *file)
{
- unsigned long flags;
struct snd_card *card;
struct snd_ctl_file *ctl;
- int err;
+ int i, err;
- err = nonseekable_open(inode, file);
+ err = stream_open(inode, file);
if (err < 0)
return err;
@@ -78,13 +75,13 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
init_waitqueue_head(&ctl->change_sleep);
spin_lock_init(&ctl->read_lock);
ctl->card = card;
- ctl->prefer_pcm_subdevice = -1;
- ctl->prefer_rawmidi_subdevice = -1;
+ for (i = 0; i < SND_CTL_SUBDEV_ITEMS; i++)
+ ctl->preferred_subdevice[i] = -1;
ctl->pid = get_pid(task_pid(current));
file->private_data = ctl;
- write_lock_irqsave(&card->ctl_files_rwlock, flags);
- list_add_tail(&ctl->list, &card->ctl_files);
- write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
+ scoped_guard(write_lock_irqsave, &card->controls_rwlock)
+ list_add_tail(&ctl->list, &card->ctl_files);
+ snd_card_unref(card);
return 0;
__error:
@@ -92,26 +89,25 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
__error2:
snd_card_file_remove(card, file);
__error1:
+ if (card)
+ snd_card_unref(card);
return err;
}
static void snd_ctl_empty_read_queue(struct snd_ctl_file * ctl)
{
- unsigned long flags;
struct snd_kctl_event *cread;
-
- spin_lock_irqsave(&ctl->read_lock, flags);
+
+ guard(spinlock_irqsave)(&ctl->read_lock);
while (!list_empty(&ctl->events)) {
cread = snd_kctl_event(ctl->events.next);
list_del(&cread->list);
kfree(cread);
}
- spin_unlock_irqrestore(&ctl->read_lock, flags);
}
static int snd_ctl_release(struct inode *inode, struct file *file)
{
- unsigned long flags;
struct snd_card *card;
struct snd_ctl_file *ctl;
struct snd_kcontrol *control;
@@ -120,15 +116,18 @@ static int snd_ctl_release(struct inode *inode, struct file *file)
ctl = file->private_data;
file->private_data = NULL;
card = ctl->card;
- write_lock_irqsave(&card->ctl_files_rwlock, flags);
- list_del(&ctl->list);
- write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
- down_write(&card->controls_rwsem);
- list_for_each_entry(control, &card->controls, list)
- for (idx = 0; idx < control->count; idx++)
- if (control->vd[idx].owner == ctl)
- control->vd[idx].owner = NULL;
- up_write(&card->controls_rwsem);
+
+ scoped_guard(write_lock_irqsave, &card->controls_rwlock)
+ list_del(&ctl->list);
+
+ scoped_guard(rwsem_write, &card->controls_rwsem) {
+ list_for_each_entry(control, &card->controls, list)
+ for (idx = 0; idx < control->count; idx++)
+ if (control->vd[idx].owner == ctl)
+ control->vd[idx].owner = NULL;
+ }
+
+ snd_fasync_free(ctl->fasync);
snd_ctl_empty_read_queue(ctl);
put_pid(ctl->pid);
kfree(ctl);
@@ -137,78 +136,114 @@ static int snd_ctl_release(struct inode *inode, struct file *file)
return 0;
}
+/**
+ * snd_ctl_notify - Send notification to user-space for a control change
+ * @card: the card to send notification
+ * @mask: the event mask, SNDRV_CTL_EVENT_*
+ * @id: the ctl element id to send notification
+ *
+ * This function adds an event record with the given id and mask, appends
+ * to the list and wakes up the user-space for notification. This can be
+ * called in the atomic context.
+ */
void snd_ctl_notify(struct snd_card *card, unsigned int mask,
struct snd_ctl_elem_id *id)
{
- unsigned long flags;
struct snd_ctl_file *ctl;
struct snd_kctl_event *ev;
-
+
if (snd_BUG_ON(!card || !id))
return;
- read_lock(&card->ctl_files_rwlock);
-#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+ if (card->shutdown)
+ return;
+
+ guard(read_lock_irqsave)(&card->controls_rwlock);
+#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
card->mixer_oss_change_count++;
#endif
list_for_each_entry(ctl, &card->ctl_files, list) {
if (!ctl->subscribed)
continue;
- spin_lock_irqsave(&ctl->read_lock, flags);
- list_for_each_entry(ev, &ctl->events, list) {
- if (ev->id.numid == id->numid) {
- ev->mask |= mask;
- goto _found;
+ scoped_guard(spinlock, &ctl->read_lock) {
+ list_for_each_entry(ev, &ctl->events, list) {
+ if (ev->id.numid == id->numid) {
+ ev->mask |= mask;
+ goto _found;
+ }
}
+ ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
+ if (ev) {
+ ev->id = *id;
+ ev->mask = mask;
+ list_add_tail(&ev->list, &ctl->events);
+ } else {
+ dev_err(card->dev, "No memory available to allocate event\n");
+ }
+_found:
+ wake_up(&ctl->change_sleep);
}
- ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
- if (ev) {
- ev->id = *id;
- ev->mask = mask;
- list_add_tail(&ev->list, &ctl->events);
- } else {
- snd_printk(KERN_ERR "No memory available to allocate event\n");
- }
- _found:
- wake_up(&ctl->change_sleep);
- spin_unlock_irqrestore(&ctl->read_lock, flags);
- kill_fasync(&ctl->fasync, SIGIO, POLL_IN);
+ snd_kill_fasync(ctl->fasync, SIGIO, POLL_IN);
}
- read_unlock(&card->ctl_files_rwlock);
}
-
EXPORT_SYMBOL(snd_ctl_notify);
/**
- * snd_ctl_new - create a control instance from the template
- * @control: the control template
- * @access: the default control access
+ * snd_ctl_notify_one - Send notification to user-space for a control change
+ * @card: the card to send notification
+ * @mask: the event mask, SNDRV_CTL_EVENT_*
+ * @kctl: the pointer with the control instance
+ * @ioff: the additional offset to the control index
+ *
+ * This function calls snd_ctl_notify() and does additional jobs
+ * like LED state changes.
+ */
+void snd_ctl_notify_one(struct snd_card *card, unsigned int mask,
+ struct snd_kcontrol *kctl, unsigned int ioff)
+{
+ struct snd_ctl_elem_id id = kctl->id;
+ struct snd_ctl_layer_ops *lops;
+
+ id.index += ioff;
+ id.numid += ioff;
+ snd_ctl_notify(card, mask, &id);
+ guard(rwsem_read)(&snd_ctl_layer_rwsem);
+ for (lops = snd_ctl_layer; lops; lops = lops->next)
+ lops->lnotify(card, mask, kctl, ioff);
+}
+EXPORT_SYMBOL(snd_ctl_notify_one);
+
+/**
+ * snd_ctl_new - create a new control instance with some elements
+ * @kctl: the pointer to store new control instance
+ * @count: the number of elements in this control
+ * @access: the default access flags for elements in this control
+ * @file: given when locking these elements
*
- * Allocates a new struct snd_kcontrol instance and copies the given template
- * to the new instance. It does not copy volatile data (access).
+ * Allocates a memory object for a new control instance. The instance has
+ * elements as many as the given number (@count). Each element has given
+ * access permissions (@access). Each element is locked when @file is given.
*
- * Returns the pointer of the new instance, or NULL on failure.
+ * Return: 0 on success, error code on failure
*/
-static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,
- unsigned int access)
+static int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count,
+ unsigned int access, struct snd_ctl_file *file)
{
- struct snd_kcontrol *kctl;
unsigned int idx;
-
- if (snd_BUG_ON(!control || !control->count))
- return NULL;
- if (control->count > MAX_CONTROL_COUNT)
- return NULL;
+ if (count == 0 || count > MAX_CONTROL_COUNT)
+ return -EINVAL;
- kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL);
- if (kctl == NULL) {
- snd_printk(KERN_ERR "Cannot allocate control instance\n");
- return NULL;
+ *kctl = kzalloc(struct_size(*kctl, vd, count), GFP_KERNEL);
+ if (!*kctl)
+ return -ENOMEM;
+
+ (*kctl)->count = count;
+ for (idx = 0; idx < count; idx++) {
+ (*kctl)->vd[idx].access = access;
+ (*kctl)->vd[idx].owner = file;
}
- *kctl = *control;
- for (idx = 0; idx < kctl->count; idx++)
- kctl->vd[idx].access = access;
- return kctl;
+
+ return 0;
}
/**
@@ -216,48 +251,65 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,
* @ncontrol: the initialization record
* @private_data: the private data to set
*
- * Allocates a new struct snd_kcontrol instance and initialize from the given
+ * Allocates a new struct snd_kcontrol instance and initialize from the given
* template. When the access field of ncontrol is 0, it's assumed as
* READWRITE access. When the count field is 0, it's assumes as one.
*
- * Returns the pointer of the newly generated instance, or NULL on failure.
+ * Return: The pointer of the newly generated instance, or %NULL on failure.
*/
struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
void *private_data)
{
- struct snd_kcontrol kctl;
+ struct snd_kcontrol *kctl;
+ unsigned int count;
unsigned int access;
-
+ int err;
+
if (snd_BUG_ON(!ncontrol || !ncontrol->info))
return NULL;
- memset(&kctl, 0, sizeof(kctl));
- kctl.id.iface = ncontrol->iface;
- kctl.id.device = ncontrol->device;
- kctl.id.subdevice = ncontrol->subdevice;
+
+ count = ncontrol->count;
+ if (count == 0)
+ count = 1;
+
+ access = ncontrol->access;
+ if (access == 0)
+ access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE |
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK |
+ SNDRV_CTL_ELEM_ACCESS_LED_MASK |
+ SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK);
+
+ err = snd_ctl_new(&kctl, count, access, NULL);
+ if (err < 0)
+ return NULL;
+
+ /* The 'numid' member is decided when calling snd_ctl_add(). */
+ kctl->id.iface = ncontrol->iface;
+ kctl->id.device = ncontrol->device;
+ kctl->id.subdevice = ncontrol->subdevice;
if (ncontrol->name) {
- strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name));
- if (strcmp(ncontrol->name, kctl.id.name) != 0)
- snd_printk(KERN_WARNING
- "Control name '%s' truncated to '%s'\n",
- ncontrol->name, kctl.id.name);
+ strscpy(kctl->id.name, ncontrol->name, sizeof(kctl->id.name));
+ if (strcmp(ncontrol->name, kctl->id.name) != 0)
+ pr_warn("ALSA: Control name '%s' truncated to '%s'\n",
+ ncontrol->name, kctl->id.name);
}
- kctl.id.index = ncontrol->index;
- kctl.count = ncontrol->count ? ncontrol->count : 1;
- access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
- (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
- SNDRV_CTL_ELEM_ACCESS_INACTIVE|
- SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
- SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND|
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
- kctl.info = ncontrol->info;
- kctl.get = ncontrol->get;
- kctl.put = ncontrol->put;
- kctl.tlv.p = ncontrol->tlv.p;
- kctl.private_value = ncontrol->private_value;
- kctl.private_data = private_data;
- return snd_ctl_new(&kctl, access);
-}
+ kctl->id.index = ncontrol->index;
+
+ kctl->info = ncontrol->info;
+ kctl->get = ncontrol->get;
+ kctl->put = ncontrol->put;
+ kctl->tlv.p = ncontrol->tlv.p;
+
+ kctl->private_value = ncontrol->private_value;
+ kctl->private_data = private_data;
+ return kctl;
+}
EXPORT_SYMBOL(snd_ctl_new1);
/**
@@ -276,88 +328,200 @@ void snd_ctl_free_one(struct snd_kcontrol *kcontrol)
kfree(kcontrol);
}
}
-
EXPORT_SYMBOL(snd_ctl_free_one);
-static unsigned int snd_ctl_hole_check(struct snd_card *card,
- unsigned int count)
+static bool snd_ctl_remove_numid_conflict(struct snd_card *card,
+ unsigned int count)
{
struct snd_kcontrol *kctl;
+ /* Make sure that the ids assigned to the control do not wrap around */
+ if (card->last_numid >= UINT_MAX - count)
+ card->last_numid = 0;
+
list_for_each_entry(kctl, &card->controls, list) {
- if ((kctl->id.numid <= card->last_numid &&
- kctl->id.numid + kctl->count > card->last_numid) ||
- (kctl->id.numid <= card->last_numid + count - 1 &&
- kctl->id.numid + kctl->count > card->last_numid + count - 1))
- return card->last_numid = kctl->id.numid + kctl->count - 1;
+ if (kctl->id.numid < card->last_numid + 1 + count &&
+ kctl->id.numid + kctl->count > card->last_numid + 1) {
+ card->last_numid = kctl->id.numid + kctl->count - 1;
+ return true;
+ }
}
- return card->last_numid;
+ return false;
}
static int snd_ctl_find_hole(struct snd_card *card, unsigned int count)
{
- unsigned int last_numid, iter = 100000;
+ unsigned int iter = 100000;
- last_numid = card->last_numid;
- while (last_numid != snd_ctl_hole_check(card, count)) {
+ while (snd_ctl_remove_numid_conflict(card, count)) {
if (--iter == 0) {
/* this situation is very unlikely */
- snd_printk(KERN_ERR "unable to allocate new control numid\n");
+ dev_err(card->dev, "unable to allocate new control numid\n");
return -ENOMEM;
}
- last_numid = card->last_numid;
}
return 0;
}
-/**
- * snd_ctl_add - add the control instance to the card
- * @card: the card instance
- * @kcontrol: the control instance to add
- *
- * Adds the control instance created via snd_ctl_new() or
- * snd_ctl_new1() to the given card. Assigns also an unique
- * numid used for fast search.
- *
- * Returns zero if successful, or a negative error code on failure.
- *
- * It frees automatically the control which cannot be added.
+/* check whether the given id is contained in the given kctl */
+static bool elem_id_matches(const struct snd_kcontrol *kctl,
+ const struct snd_ctl_elem_id *id)
+{
+ return kctl->id.iface == id->iface &&
+ kctl->id.device == id->device &&
+ kctl->id.subdevice == id->subdevice &&
+ !strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)) &&
+ kctl->id.index <= id->index &&
+ kctl->id.index + kctl->count > id->index;
+}
+
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+/* Compute a hash key for the corresponding ctl id
+ * It's for the name lookup, hence the numid is excluded.
+ * The hash key is bound in LONG_MAX to be used for Xarray key.
*/
-int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
+#define MULTIPLIER 37
+static unsigned long get_ctl_id_hash(const struct snd_ctl_elem_id *id)
+{
+ int i;
+ unsigned long h;
+
+ h = id->iface;
+ h = MULTIPLIER * h + id->device;
+ h = MULTIPLIER * h + id->subdevice;
+ for (i = 0; i < SNDRV_CTL_ELEM_ID_NAME_MAXLEN && id->name[i]; i++)
+ h = MULTIPLIER * h + id->name[i];
+ h = MULTIPLIER * h + id->index;
+ h &= LONG_MAX;
+ return h;
+}
+
+/* add hash entries to numid and ctl xarray tables */
+static void add_hash_entries(struct snd_card *card,
+ struct snd_kcontrol *kcontrol)
+{
+ struct snd_ctl_elem_id id = kcontrol->id;
+ int i;
+
+ xa_store_range(&card->ctl_numids, kcontrol->id.numid,
+ kcontrol->id.numid + kcontrol->count - 1,
+ kcontrol, GFP_KERNEL);
+
+ for (i = 0; i < kcontrol->count; i++) {
+ id.index = kcontrol->id.index + i;
+ if (xa_insert(&card->ctl_hash, get_ctl_id_hash(&id),
+ kcontrol, GFP_KERNEL)) {
+ /* skip hash for this entry, noting we had collision */
+ card->ctl_hash_collision = true;
+ dev_dbg(card->dev, "ctl_hash collision %d:%s:%d\n",
+ id.iface, id.name, id.index);
+ }
+ }
+}
+
+/* remove hash entries that have been added */
+static void remove_hash_entries(struct snd_card *card,
+ struct snd_kcontrol *kcontrol)
+{
+ struct snd_ctl_elem_id id = kcontrol->id;
+ struct snd_kcontrol *matched;
+ unsigned long h;
+ int i;
+
+ for (i = 0; i < kcontrol->count; i++) {
+ xa_erase(&card->ctl_numids, id.numid);
+ h = get_ctl_id_hash(&id);
+ matched = xa_load(&card->ctl_hash, h);
+ if (matched && (matched == kcontrol ||
+ elem_id_matches(matched, &id)))
+ xa_erase(&card->ctl_hash, h);
+ id.index++;
+ id.numid++;
+ }
+}
+#else /* CONFIG_SND_CTL_FAST_LOOKUP */
+static inline void add_hash_entries(struct snd_card *card,
+ struct snd_kcontrol *kcontrol)
+{
+}
+static inline void remove_hash_entries(struct snd_card *card,
+ struct snd_kcontrol *kcontrol)
+{
+}
+#endif /* CONFIG_SND_CTL_FAST_LOOKUP */
+
+enum snd_ctl_add_mode {
+ CTL_ADD_EXCLUSIVE, CTL_REPLACE, CTL_ADD_ON_REPLACE,
+};
+
+/* add/replace a new kcontrol object; call with card->controls_rwsem locked */
+static int __snd_ctl_add_replace(struct snd_card *card,
+ struct snd_kcontrol *kcontrol,
+ enum snd_ctl_add_mode mode)
{
struct snd_ctl_elem_id id;
unsigned int idx;
+ struct snd_kcontrol *old;
+ int err;
+
+ lockdep_assert_held_write(&card->controls_rwsem);
+
+ id = kcontrol->id;
+ if (id.index > UINT_MAX - kcontrol->count)
+ return -EINVAL;
+
+ old = snd_ctl_find_id(card, &id);
+ if (!old) {
+ if (mode == CTL_REPLACE)
+ return -EINVAL;
+ } else {
+ if (mode == CTL_ADD_EXCLUSIVE) {
+ dev_err(card->dev,
+ "control %i:%i:%i:%s:%i is already present\n",
+ id.iface, id.device, id.subdevice, id.name,
+ id.index);
+ return -EBUSY;
+ }
+
+ err = snd_ctl_remove_locked(card, old);
+ if (err < 0)
+ return err;
+ }
+
+ if (snd_ctl_find_hole(card, kcontrol->count) < 0)
+ return -ENOMEM;
+
+ scoped_guard(write_lock_irq, &card->controls_rwlock) {
+ list_add_tail(&kcontrol->list, &card->controls);
+ card->controls_count += kcontrol->count;
+ kcontrol->id.numid = card->last_numid + 1;
+ card->last_numid += kcontrol->count;
+ }
+
+ add_hash_entries(card, kcontrol);
+
+ for (idx = 0; idx < kcontrol->count; idx++)
+ snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_ADD, kcontrol, idx);
+
+ return 0;
+}
+
+static int snd_ctl_add_replace(struct snd_card *card,
+ struct snd_kcontrol *kcontrol,
+ enum snd_ctl_add_mode mode)
+{
int err = -EINVAL;
if (! kcontrol)
return err;
if (snd_BUG_ON(!card || !kcontrol->info))
goto error;
- id = kcontrol->id;
- down_write(&card->controls_rwsem);
- if (snd_ctl_find_id(card, &id)) {
- up_write(&card->controls_rwsem);
- snd_printd(KERN_ERR "control %i:%i:%i:%s:%i is already present\n",
- id.iface,
- id.device,
- id.subdevice,
- id.name,
- id.index);
- err = -EBUSY;
- goto error;
- }
- if (snd_ctl_find_hole(card, kcontrol->count) < 0) {
- up_write(&card->controls_rwsem);
- err = -ENOMEM;
+
+ scoped_guard(rwsem_write, &card->controls_rwsem)
+ err = __snd_ctl_add_replace(card, kcontrol, mode);
+
+ if (err < 0)
goto error;
- }
- list_add_tail(&kcontrol->list, &card->controls);
- card->controls_count += kcontrol->count;
- kcontrol->id.numid = card->last_numid + 1;
- card->last_numid += kcontrol->count;
- up_write(&card->controls_rwsem);
- for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
return 0;
error:
@@ -365,35 +529,99 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
return err;
}
+/**
+ * snd_ctl_add - add the control instance to the card
+ * @card: the card instance
+ * @kcontrol: the control instance to add
+ *
+ * Adds the control instance created via snd_ctl_new() or
+ * snd_ctl_new1() to the given card. Assigns also an unique
+ * numid used for fast search.
+ *
+ * It frees automatically the control which cannot be added.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ *
+ */
+int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
+{
+ return snd_ctl_add_replace(card, kcontrol, CTL_ADD_EXCLUSIVE);
+}
EXPORT_SYMBOL(snd_ctl_add);
/**
- * snd_ctl_remove - remove the control from the card and release it
+ * snd_ctl_replace - replace the control instance of the card
* @card: the card instance
- * @kcontrol: the control instance to remove
+ * @kcontrol: the control instance to replace
+ * @add_on_replace: add the control if not already added
*
- * Removes the control from the card and then releases the instance.
- * You don't need to call snd_ctl_free_one(). You must be in
- * the write lock - down_write(&card->controls_rwsem).
- *
- * Returns 0 if successful, or a negative error code on failure.
+ * Replaces the given control. If the given control does not exist
+ * and the add_on_replace flag is set, the control is added. If the
+ * control exists, it is destroyed first.
+ *
+ * It frees automatically the control which cannot be added or replaced.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
-int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
+int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol,
+ bool add_on_replace)
+{
+ return snd_ctl_add_replace(card, kcontrol,
+ add_on_replace ? CTL_ADD_ON_REPLACE : CTL_REPLACE);
+}
+EXPORT_SYMBOL(snd_ctl_replace);
+
+static int __snd_ctl_remove(struct snd_card *card,
+ struct snd_kcontrol *kcontrol,
+ bool remove_hash)
{
- struct snd_ctl_elem_id id;
unsigned int idx;
+ lockdep_assert_held_write(&card->controls_rwsem);
+
if (snd_BUG_ON(!card || !kcontrol))
return -EINVAL;
- list_del(&kcontrol->list);
- card->controls_count -= kcontrol->count;
- id = kcontrol->id;
- for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_REMOVE, &id);
+
+ if (remove_hash)
+ remove_hash_entries(card, kcontrol);
+
+ scoped_guard(write_lock_irq, &card->controls_rwlock) {
+ list_del(&kcontrol->list);
+ card->controls_count -= kcontrol->count;
+ }
+
+ for (idx = 0; idx < kcontrol->count; idx++)
+ snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_REMOVE, kcontrol, idx);
snd_ctl_free_one(kcontrol);
return 0;
}
+static inline int snd_ctl_remove_locked(struct snd_card *card,
+ struct snd_kcontrol *kcontrol)
+{
+ return __snd_ctl_remove(card, kcontrol, true);
+}
+
+/**
+ * snd_ctl_remove - remove the control from the card and release it
+ * @card: the card instance
+ * @kcontrol: the control instance to remove
+ *
+ * Removes the control from the card and then releases the instance.
+ * You don't need to call snd_ctl_free_one().
+ * Passing NULL to @kcontrol argument is allowed as noop.
+ *
+ * Return: 0 if successful, or a negative error code on failure.
+ *
+ * Note that this function takes card->controls_rwsem lock internally.
+ */
+int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
+{
+ if (!kcontrol)
+ return 0;
+ guard(rwsem_write)(&card->controls_rwsem);
+ return snd_ctl_remove_locked(card, kcontrol);
+}
EXPORT_SYMBOL(snd_ctl_remove);
/**
@@ -403,25 +631,19 @@ EXPORT_SYMBOL(snd_ctl_remove);
*
* Finds the control instance with the given id, removes it from the
* card list and releases it.
- *
- * Returns 0 if successful, or a negative error code on failure.
+ *
+ * Return: 0 if successful, or a negative error code on failure.
*/
int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
{
struct snd_kcontrol *kctl;
- int ret;
- down_write(&card->controls_rwsem);
+ guard(rwsem_write)(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, id);
- if (kctl == NULL) {
- up_write(&card->controls_rwsem);
+ if (kctl == NULL)
return -ENOENT;
- }
- ret = snd_ctl_remove(card, kctl);
- up_write(&card->controls_rwsem);
- return ret;
+ return snd_ctl_remove_locked(card, kctl);
}
-
EXPORT_SYMBOL(snd_ctl_remove_id);
/**
@@ -431,39 +653,77 @@ EXPORT_SYMBOL(snd_ctl_remove_id);
*
* Finds the control instance with the given id, removes it from the
* card list and releases it.
- *
- * Returns 0 if successful, or a negative error code on failure.
+ *
+ * Return: 0 if successful, or a negative error code on failure.
*/
static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
struct snd_ctl_elem_id *id)
{
struct snd_card *card = file->card;
struct snd_kcontrol *kctl;
- int idx, ret;
+ int idx;
+
+ guard(rwsem_write)(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, id);
+ if (kctl == NULL)
+ return -ENOENT;
+ if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER))
+ return -EINVAL;
+ for (idx = 0; idx < kctl->count; idx++)
+ if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file)
+ return -EBUSY;
+ return snd_ctl_remove_locked(card, kctl);
+}
+
+/**
+ * snd_ctl_activate_id - activate/inactivate the control of the given id
+ * @card: the card instance
+ * @id: the control id to activate/inactivate
+ * @active: non-zero to activate
+ *
+ * Finds the control instance with the given id, and activate or
+ * inactivate the control together with notification, if changed.
+ * The given ID data is filled with full information.
+ *
+ * Return: 0 if unchanged, 1 if changed, or a negative error code on failure.
+ */
+int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
+ int active)
+{
+ struct snd_kcontrol *kctl;
+ struct snd_kcontrol_volatile *vd;
+ unsigned int index_offset;
+ int ret;
down_write(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, id);
if (kctl == NULL) {
ret = -ENOENT;
- goto error;
+ goto unlock;
}
- if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) {
- ret = -EINVAL;
- goto error;
+ index_offset = snd_ctl_get_ioff(kctl, id);
+ vd = &kctl->vd[index_offset];
+ ret = 0;
+ if (active) {
+ if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE))
+ goto unlock;
+ vd->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ } else {
+ if (vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)
+ goto unlock;
+ vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
}
- for (idx = 0; idx < kctl->count; idx++)
- if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) {
- ret = -EBUSY;
- goto error;
- }
- ret = snd_ctl_remove(card, kctl);
- if (ret < 0)
- goto error;
- card->user_ctl_count--;
-error:
+ snd_ctl_build_ioff(id, kctl, index_offset);
+ downgrade_write(&card->controls_rwsem);
+ snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_INFO, kctl, index_offset);
+ up_read(&card->controls_rwsem);
+ return 1;
+
+ unlock:
up_write(&card->controls_rwsem);
return ret;
}
+EXPORT_SYMBOL_GPL(snd_ctl_activate_id);
/**
* snd_ctl_rename_id - replace the id of a control on the card
@@ -474,53 +734,96 @@ error:
* Finds the control with the old id from the card, and replaces the
* id with the new one.
*
- * Returns zero if successful, or a negative error code on failure.
+ * The function tries to keep the already assigned numid while replacing
+ * the rest.
+ *
+ * Note that this function should be used only in the card initialization
+ * phase. Calling after the card instantiation may cause issues with
+ * user-space expecting persistent numids.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
struct snd_ctl_elem_id *dst_id)
{
struct snd_kcontrol *kctl;
+ int saved_numid;
- down_write(&card->controls_rwsem);
+ guard(rwsem_write)(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, src_id);
- if (kctl == NULL) {
- up_write(&card->controls_rwsem);
+ if (kctl == NULL)
return -ENOENT;
- }
+ saved_numid = kctl->id.numid;
+ remove_hash_entries(card, kctl);
kctl->id = *dst_id;
- kctl->id.numid = card->last_numid + 1;
- card->last_numid += kctl->count;
- up_write(&card->controls_rwsem);
+ kctl->id.numid = saved_numid;
+ add_hash_entries(card, kctl);
return 0;
}
-
EXPORT_SYMBOL(snd_ctl_rename_id);
/**
- * snd_ctl_find_numid - find the control instance with the given number-id
+ * snd_ctl_rename - rename the control on the card
* @card: the card instance
- * @numid: the number-id to search
- *
- * Finds the control instance with the given number-id from the card.
+ * @kctl: the control to rename
+ * @name: the new name
*
- * Returns the pointer of the instance if found, or NULL if not.
+ * Renames the specified control on the card to the new name.
*
- * The caller must down card->controls_rwsem before calling this function
- * (if the race condition can happen).
+ * Note that this function takes card->controls_rwsem lock internally.
*/
-struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid)
+void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl,
+ const char *name)
+{
+ guard(rwsem_write)(&card->controls_rwsem);
+ remove_hash_entries(card, kctl);
+
+ if (strscpy(kctl->id.name, name, sizeof(kctl->id.name)) < 0)
+ pr_warn("ALSA: Renamed control new name '%s' truncated to '%s'\n",
+ name, kctl->id.name);
+
+ add_hash_entries(card, kctl);
+}
+EXPORT_SYMBOL(snd_ctl_rename);
+
+#ifndef CONFIG_SND_CTL_FAST_LOOKUP
+static struct snd_kcontrol *
+snd_ctl_find_numid_slow(struct snd_card *card, unsigned int numid)
{
struct snd_kcontrol *kctl;
- if (snd_BUG_ON(!card || !numid))
- return NULL;
+ guard(read_lock_irqsave)(&card->controls_rwlock);
list_for_each_entry(kctl, &card->controls, list) {
if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)
return kctl;
}
return NULL;
}
+#endif /* !CONFIG_SND_CTL_FAST_LOOKUP */
+/**
+ * snd_ctl_find_numid - find the control instance with the given number-id
+ * @card: the card instance
+ * @numid: the number-id to search
+ *
+ * Finds the control instance with the given number-id from the card.
+ *
+ * Return: The pointer of the instance if found, or %NULL if not.
+ *
+ * Note that this function takes card->controls_rwlock lock internally.
+ */
+struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card,
+ unsigned int numid)
+{
+ if (snd_BUG_ON(!card || !numid))
+ return NULL;
+
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+ return xa_load(&card->ctl_numids, numid);
+#else
+ return snd_ctl_find_numid_slow(card, numid);
+#endif
+}
EXPORT_SYMBOL(snd_ctl_find_numid);
/**
@@ -530,145 +833,304 @@ EXPORT_SYMBOL(snd_ctl_find_numid);
*
* Finds the control instance with the given id from the card.
*
- * Returns the pointer of the instance if found, or NULL if not.
+ * Return: The pointer of the instance if found, or %NULL if not.
*
- * The caller must down card->controls_rwsem before calling this function
- * (if the race condition can happen).
+ * Note that this function takes card->controls_rwlock lock internally.
*/
struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
- struct snd_ctl_elem_id *id)
+ const struct snd_ctl_elem_id *id)
{
struct snd_kcontrol *kctl;
if (snd_BUG_ON(!card || !id))
return NULL;
+
if (id->numid != 0)
return snd_ctl_find_numid(card, id->numid);
- list_for_each_entry(kctl, &card->controls, list) {
- if (kctl->id.iface != id->iface)
- continue;
- if (kctl->id.device != id->device)
- continue;
- if (kctl->id.subdevice != id->subdevice)
- continue;
- if (strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)))
- continue;
- if (kctl->id.index > id->index)
- continue;
- if (kctl->id.index + kctl->count <= id->index)
- continue;
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+ kctl = xa_load(&card->ctl_hash, get_ctl_id_hash(id));
+ if (kctl && elem_id_matches(kctl, id))
return kctl;
- }
+ if (!card->ctl_hash_collision)
+ return NULL; /* we can rely on only hash table */
+#endif
+ /* no matching in hash table - try all as the last resort */
+ guard(read_lock_irqsave)(&card->controls_rwlock);
+ list_for_each_entry(kctl, &card->controls, list)
+ if (elem_id_matches(kctl, id))
+ return kctl;
+
return NULL;
}
-
EXPORT_SYMBOL(snd_ctl_find_id);
static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
unsigned int cmd, void __user *arg)
{
- struct snd_ctl_card_info *info;
+ struct snd_ctl_card_info *info __free(kfree) = NULL;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (! info)
return -ENOMEM;
- down_read(&snd_ioctl_rwsem);
- info->card = card->number;
- strlcpy(info->id, card->id, sizeof(info->id));
- strlcpy(info->driver, card->driver, sizeof(info->driver));
- strlcpy(info->name, card->shortname, sizeof(info->name));
- strlcpy(info->longname, card->longname, sizeof(info->longname));
- strlcpy(info->mixername, card->mixername, sizeof(info->mixername));
- strlcpy(info->components, card->components, sizeof(info->components));
- up_read(&snd_ioctl_rwsem);
- if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) {
- kfree(info);
- return -EFAULT;
+ scoped_guard(rwsem_read, &snd_ioctl_rwsem) {
+ info->card = card->number;
+ strscpy(info->id, card->id, sizeof(info->id));
+ strscpy(info->driver, card->driver, sizeof(info->driver));
+ strscpy(info->name, card->shortname, sizeof(info->name));
+ strscpy(info->longname, card->longname, sizeof(info->longname));
+ strscpy(info->mixername, card->mixername, sizeof(info->mixername));
+ strscpy(info->components, card->components, sizeof(info->components));
}
- kfree(info);
+ if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info)))
+ return -EFAULT;
return 0;
}
static int snd_ctl_elem_list(struct snd_card *card,
- struct snd_ctl_elem_list __user *_list)
+ struct snd_ctl_elem_list *list)
{
- struct list_head *plist;
- struct snd_ctl_elem_list list;
struct snd_kcontrol *kctl;
- struct snd_ctl_elem_id *dst, *id;
- unsigned int offset, space, first, jidx;
-
- if (copy_from_user(&list, _list, sizeof(list)))
- return -EFAULT;
- offset = list.offset;
- space = list.space;
- first = 0;
- /* try limit maximum space */
- if (space > 16384)
- return -ENOMEM;
- if (space > 0) {
- /* allocate temporary buffer for atomic operation */
- dst = vmalloc(space * sizeof(struct snd_ctl_elem_id));
- if (dst == NULL)
- return -ENOMEM;
- down_read(&card->controls_rwsem);
- list.count = card->controls_count;
- plist = card->controls.next;
- while (plist != &card->controls) {
- if (offset == 0)
- break;
- kctl = snd_kcontrol(plist);
- if (offset < kctl->count)
- break;
+ struct snd_ctl_elem_id id;
+ unsigned int offset, space, jidx;
+
+ offset = list->offset;
+ space = list->space;
+
+ guard(rwsem_read)(&card->controls_rwsem);
+ list->count = card->controls_count;
+ list->used = 0;
+ if (!space)
+ return 0;
+ list_for_each_entry(kctl, &card->controls, list) {
+ if (offset >= kctl->count) {
offset -= kctl->count;
- plist = plist->next;
- }
- list.used = 0;
- id = dst;
- while (space > 0 && plist != &card->controls) {
- kctl = snd_kcontrol(plist);
- for (jidx = offset; space > 0 && jidx < kctl->count; jidx++) {
- snd_ctl_build_ioff(id, kctl, jidx);
- id++;
- space--;
- list.used++;
- }
- plist = plist->next;
- offset = 0;
+ continue;
}
- up_read(&card->controls_rwsem);
- if (list.used > 0 &&
- copy_to_user(list.pids, dst,
- list.used * sizeof(struct snd_ctl_elem_id))) {
- vfree(dst);
- return -EFAULT;
+ for (jidx = offset; jidx < kctl->count; jidx++) {
+ snd_ctl_build_ioff(&id, kctl, jidx);
+ if (copy_to_user(list->pids + list->used, &id, sizeof(id)))
+ return -EFAULT;
+ list->used++;
+ if (!--space)
+ return 0;
}
- vfree(dst);
- } else {
- down_read(&card->controls_rwsem);
- list.count = card->controls_count;
- up_read(&card->controls_rwsem);
+ offset = 0;
}
+ return 0;
+}
+
+static int snd_ctl_elem_list_user(struct snd_card *card,
+ struct snd_ctl_elem_list __user *_list)
+{
+ struct snd_ctl_elem_list list;
+ int err;
+
+ if (copy_from_user(&list, _list, sizeof(list)))
+ return -EFAULT;
+ err = snd_ctl_elem_list(card, &list);
+ if (err)
+ return err;
if (copy_to_user(_list, &list, sizeof(list)))
return -EFAULT;
+
return 0;
}
-static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
- struct snd_ctl_elem_info *info)
+/* Check whether the given kctl info is valid */
+static int snd_ctl_check_elem_info(struct snd_card *card,
+ const struct snd_ctl_elem_info *info)
+{
+ static const unsigned int max_value_counts[] = {
+ [SNDRV_CTL_ELEM_TYPE_BOOLEAN] = 128,
+ [SNDRV_CTL_ELEM_TYPE_INTEGER] = 128,
+ [SNDRV_CTL_ELEM_TYPE_ENUMERATED] = 128,
+ [SNDRV_CTL_ELEM_TYPE_BYTES] = 512,
+ [SNDRV_CTL_ELEM_TYPE_IEC958] = 1,
+ [SNDRV_CTL_ELEM_TYPE_INTEGER64] = 64,
+ };
+
+ if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
+ info->type > SNDRV_CTL_ELEM_TYPE_INTEGER64) {
+ if (card)
+ dev_err(card->dev,
+ "control %i:%i:%i:%s:%i: invalid type %d\n",
+ info->id.iface, info->id.device,
+ info->id.subdevice, info->id.name,
+ info->id.index, info->type);
+ return -EINVAL;
+ }
+ if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED &&
+ info->value.enumerated.items == 0) {
+ if (card)
+ dev_err(card->dev,
+ "control %i:%i:%i:%s:%i: zero enum items\n",
+ info->id.iface, info->id.device,
+ info->id.subdevice, info->id.name,
+ info->id.index);
+ return -EINVAL;
+ }
+ if (info->count > max_value_counts[info->type]) {
+ if (card)
+ dev_err(card->dev,
+ "control %i:%i:%i:%s:%i: invalid count %d\n",
+ info->id.iface, info->id.device,
+ info->id.subdevice, info->id.name,
+ info->id.index, info->count);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* The capacity of struct snd_ctl_elem_value.value.*/
+static const unsigned int value_sizes[] = {
+ [SNDRV_CTL_ELEM_TYPE_BOOLEAN] = sizeof(long),
+ [SNDRV_CTL_ELEM_TYPE_INTEGER] = sizeof(long),
+ [SNDRV_CTL_ELEM_TYPE_ENUMERATED] = sizeof(unsigned int),
+ [SNDRV_CTL_ELEM_TYPE_BYTES] = sizeof(unsigned char),
+ [SNDRV_CTL_ELEM_TYPE_IEC958] = sizeof(struct snd_aes_iec958),
+ [SNDRV_CTL_ELEM_TYPE_INTEGER64] = sizeof(long long),
+};
+
+/* fill the remaining snd_ctl_elem_value data with the given pattern */
+static void fill_remaining_elem_value(struct snd_ctl_elem_value *control,
+ struct snd_ctl_elem_info *info,
+ u32 pattern)
+{
+ size_t offset = value_sizes[info->type] * info->count;
+
+ offset = DIV_ROUND_UP(offset, sizeof(u32));
+ memset32((u32 *)control->value.bytes.data + offset, pattern,
+ sizeof(control->value) / sizeof(u32) - offset);
+}
+
+/* check whether the given integer ctl value is valid */
+static int sanity_check_int_value(struct snd_card *card,
+ const struct snd_ctl_elem_value *control,
+ const struct snd_ctl_elem_info *info,
+ int i, bool print_error)
+{
+ long long lval, lmin, lmax, lstep;
+ u64 rem;
+
+ switch (info->type) {
+ default:
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ lval = control->value.integer.value[i];
+ lmin = 0;
+ lmax = 1;
+ lstep = 0;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ lval = control->value.integer.value[i];
+ lmin = info->value.integer.min;
+ lmax = info->value.integer.max;
+ lstep = info->value.integer.step;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_INTEGER64:
+ lval = control->value.integer64.value[i];
+ lmin = info->value.integer64.min;
+ lmax = info->value.integer64.max;
+ lstep = info->value.integer64.step;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+ lval = control->value.enumerated.item[i];
+ lmin = 0;
+ lmax = info->value.enumerated.items - 1;
+ lstep = 0;
+ break;
+ }
+
+ if (lval < lmin || lval > lmax) {
+ if (print_error)
+ dev_err(card->dev,
+ "control %i:%i:%i:%s:%i: value out of range %lld (%lld/%lld) at count %i\n",
+ control->id.iface, control->id.device,
+ control->id.subdevice, control->id.name,
+ control->id.index, lval, lmin, lmax, i);
+ return -EINVAL;
+ }
+ if (lstep) {
+ div64_u64_rem(lval, lstep, &rem);
+ if (rem) {
+ if (print_error)
+ dev_err(card->dev,
+ "control %i:%i:%i:%s:%i: unaligned value %lld (step %lld) at count %i\n",
+ control->id.iface, control->id.device,
+ control->id.subdevice, control->id.name,
+ control->id.index, lval, lstep, i);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/* check whether the all input values are valid for the given elem value */
+static int sanity_check_input_values(struct snd_card *card,
+ const struct snd_ctl_elem_value *control,
+ const struct snd_ctl_elem_info *info,
+ bool print_error)
+{
+ int i, ret;
+
+ switch (info->type) {
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ case SNDRV_CTL_ELEM_TYPE_INTEGER64:
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+ for (i = 0; i < info->count; i++) {
+ ret = sanity_check_int_value(card, control, info, i,
+ print_error);
+ if (ret < 0)
+ return ret;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* perform sanity checks to the given snd_ctl_elem_value object */
+static int sanity_check_elem_value(struct snd_card *card,
+ const struct snd_ctl_elem_value *control,
+ const struct snd_ctl_elem_info *info,
+ u32 pattern)
+{
+ size_t offset;
+ int ret;
+ u32 *p;
+
+ ret = sanity_check_input_values(card, control, info, true);
+ if (ret < 0)
+ return ret;
+
+ /* check whether the remaining area kept untouched */
+ offset = value_sizes[info->type] * info->count;
+ offset = DIV_ROUND_UP(offset, sizeof(u32));
+ p = (u32 *)control->value.bytes.data + offset;
+ for (; offset < sizeof(control->value) / sizeof(u32); offset++, p++) {
+ if (*p != pattern) {
+ ret = -EINVAL;
+ break;
+ }
+ *p = 0; /* clear the checked area */
+ }
+
+ return ret;
+}
+
+static int __snd_ctl_elem_info(struct snd_card *card,
+ struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *info,
+ struct snd_ctl_file *ctl)
{
- struct snd_card *card = ctl->card;
- struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
unsigned int index_offset;
int result;
-
- down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_id(card, &info->id);
- if (kctl == NULL) {
- up_read(&card->controls_rwsem);
- return -ENOENT;
- }
+
#ifdef CONFIG_SND_DEBUG
info->access = 0;
#endif
@@ -687,27 +1149,47 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
} else {
info->owner = -1;
}
+ if (!snd_ctl_skip_validation(info) &&
+ snd_ctl_check_elem_info(card, info) < 0)
+ result = -EINVAL;
}
- up_read(&card->controls_rwsem);
return result;
}
+static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ struct snd_card *card = ctl->card;
+ struct snd_kcontrol *kctl;
+
+ guard(rwsem_read)(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, &info->id);
+ if (!kctl)
+ return -ENOENT;
+ return __snd_ctl_elem_info(card, kctl, info, ctl);
+}
+
static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,
struct snd_ctl_elem_info __user *_info)
{
+ struct snd_card *card = ctl->card;
struct snd_ctl_elem_info info;
int result;
if (copy_from_user(&info, _info, sizeof(info)))
return -EFAULT;
- snd_power_lock(ctl->card);
- result = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
- if (result >= 0)
- result = snd_ctl_elem_info(ctl, &info);
- snd_power_unlock(ctl->card);
- if (result >= 0)
- if (copy_to_user(_info, &info, sizeof(info)))
- return -EFAULT;
+ result = snd_power_ref_and_wait(card);
+ if (result)
+ return result;
+ result = snd_ctl_elem_info(ctl, &info);
+ snd_power_unref(card);
+ if (result < 0)
+ return result;
+ /* drop internal access flags */
+ info.access &= ~(SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK|
+ SNDRV_CTL_ELEM_ACCESS_LED_MASK);
+ if (copy_to_user(_info, &info, sizeof(info)))
+ return -EFAULT;
return result;
}
@@ -717,45 +1199,68 @@ static int snd_ctl_elem_read(struct snd_card *card,
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
unsigned int index_offset;
- int result;
+ struct snd_ctl_elem_info info;
+ const u32 pattern = 0xdeadbeef;
+ int ret;
- down_read(&card->controls_rwsem);
+ guard(rwsem_read)(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, &control->id);
- if (kctl == NULL) {
- result = -ENOENT;
- } else {
- index_offset = snd_ctl_get_ioff(kctl, &control->id);
- vd = &kctl->vd[index_offset];
- if ((vd->access & SNDRV_CTL_ELEM_ACCESS_READ) &&
- kctl->get != NULL) {
- snd_ctl_build_ioff(&control->id, kctl, index_offset);
- result = kctl->get(kctl, control);
- } else
- result = -EPERM;
+ if (!kctl)
+ return -ENOENT;
+
+ index_offset = snd_ctl_get_ioff(kctl, &control->id);
+ vd = &kctl->vd[index_offset];
+ if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_READ) || !kctl->get)
+ return -EPERM;
+
+ snd_ctl_build_ioff(&control->id, kctl, index_offset);
+
+#ifdef CONFIG_SND_CTL_DEBUG
+ /* info is needed only for validation */
+ memset(&info, 0, sizeof(info));
+ info.id = control->id;
+ ret = __snd_ctl_elem_info(card, kctl, &info, NULL);
+ if (ret < 0)
+ return ret;
+#endif
+
+ if (!snd_ctl_skip_validation(&info))
+ fill_remaining_elem_value(control, &info, pattern);
+ ret = kctl->get(kctl, control);
+ if (ret < 0)
+ return ret;
+ if (!snd_ctl_skip_validation(&info) &&
+ sanity_check_elem_value(card, control, &info, pattern) < 0) {
+ dev_err(card->dev,
+ "control %i:%i:%i:%s:%i: access overflow\n",
+ control->id.iface, control->id.device,
+ control->id.subdevice, control->id.name,
+ control->id.index);
+ return -EINVAL;
}
- up_read(&card->controls_rwsem);
- return result;
+ return 0;
}
static int snd_ctl_elem_read_user(struct snd_card *card,
struct snd_ctl_elem_value __user *_control)
{
- struct snd_ctl_elem_value *control;
+ struct snd_ctl_elem_value *control __free(kfree) = NULL;
int result;
control = memdup_user(_control, sizeof(*control));
if (IS_ERR(control))
return PTR_ERR(control);
- snd_power_lock(card);
- result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
- if (result >= 0)
- result = snd_ctl_elem_read(card, control);
- snd_power_unlock(card);
- if (result >= 0)
- if (copy_to_user(_control, control, sizeof(*control)))
- result = -EFAULT;
- kfree(control);
+ result = snd_power_ref_and_wait(card);
+ if (result)
+ return result;
+ result = snd_ctl_elem_read(card, control);
+ snd_power_unref(card);
+ if (result < 0)
+ return result;
+
+ if (copy_to_user(_control, control, sizeof(*control)))
+ return -EFAULT;
return result;
}
@@ -765,38 +1270,57 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
unsigned int index_offset;
- int result;
+ int result = 0;
- down_read(&card->controls_rwsem);
+ down_write(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, &control->id);
if (kctl == NULL) {
- result = -ENOENT;
+ up_write(&card->controls_rwsem);
+ return -ENOENT;
+ }
+
+ index_offset = snd_ctl_get_ioff(kctl, &control->id);
+ vd = &kctl->vd[index_offset];
+ if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || kctl->put == NULL ||
+ (file && vd->owner && vd->owner != file)) {
+ up_write(&card->controls_rwsem);
+ return -EPERM;
+ }
+
+ snd_ctl_build_ioff(&control->id, kctl, index_offset);
+ /* validate input values */
+ if (IS_ENABLED(CONFIG_SND_CTL_INPUT_VALIDATION)) {
+ struct snd_ctl_elem_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.id = control->id;
+ result = __snd_ctl_elem_info(card, kctl, &info, NULL);
+ if (!result)
+ result = sanity_check_input_values(card, control, &info,
+ false);
+ }
+ if (!result)
+ result = kctl->put(kctl, control);
+ if (result < 0) {
+ up_write(&card->controls_rwsem);
+ return result;
+ }
+
+ if (result > 0) {
+ downgrade_write(&card->controls_rwsem);
+ snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_VALUE, kctl, index_offset);
+ up_read(&card->controls_rwsem);
} else {
- index_offset = snd_ctl_get_ioff(kctl, &control->id);
- vd = &kctl->vd[index_offset];
- if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ||
- kctl->put == NULL ||
- (file && vd->owner && vd->owner != file)) {
- result = -EPERM;
- } else {
- snd_ctl_build_ioff(&control->id, kctl, index_offset);
- result = kctl->put(kctl, control);
- }
- if (result > 0) {
- up_read(&card->controls_rwsem);
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
- &control->id);
- return 0;
- }
+ up_write(&card->controls_rwsem);
}
- up_read(&card->controls_rwsem);
- return result;
+
+ return 0;
}
static int snd_ctl_elem_write_user(struct snd_ctl_file *file,
struct snd_ctl_elem_value __user *_control)
{
- struct snd_ctl_elem_value *control;
+ struct snd_ctl_elem_value *control __free(kfree) = NULL;
struct snd_card *card;
int result;
@@ -805,15 +1329,16 @@ static int snd_ctl_elem_write_user(struct snd_ctl_file *file,
return PTR_ERR(control);
card = file->card;
- snd_power_lock(card);
- result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
- if (result >= 0)
- result = snd_ctl_elem_write(card, file, control);
- snd_power_unlock(card);
- if (result >= 0)
- if (copy_to_user(_control, control, sizeof(*control)))
- result = -EFAULT;
- kfree(control);
+ result = snd_power_ref_and_wait(card);
+ if (result < 0)
+ return result;
+ result = snd_ctl_elem_write(card, file, control);
+ snd_power_unref(card);
+ if (result < 0)
+ return result;
+
+ if (copy_to_user(_control, control, sizeof(*control)))
+ return -EFAULT;
return result;
}
@@ -824,25 +1349,18 @@ static int snd_ctl_elem_lock(struct snd_ctl_file *file,
struct snd_ctl_elem_id id;
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
- int result;
-
+
if (copy_from_user(&id, _id, sizeof(id)))
return -EFAULT;
- down_write(&card->controls_rwsem);
+ guard(rwsem_write)(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, &id);
- if (kctl == NULL) {
- result = -ENOENT;
- } else {
- vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
- if (vd->owner != NULL)
- result = -EBUSY;
- else {
- vd->owner = file;
- result = 0;
- }
- }
- up_write(&card->controls_rwsem);
- return result;
+ if (!kctl)
+ return -ENOENT;
+ vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
+ if (vd->owner)
+ return -EBUSY;
+ vd->owner = file;
+ return 0;
}
static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
@@ -852,107 +1370,244 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
struct snd_ctl_elem_id id;
struct snd_kcontrol *kctl;
struct snd_kcontrol_volatile *vd;
- int result;
-
+
if (copy_from_user(&id, _id, sizeof(id)))
return -EFAULT;
- down_write(&card->controls_rwsem);
+ guard(rwsem_write)(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, &id);
- if (kctl == NULL) {
- result = -ENOENT;
- } else {
- vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
- if (vd->owner == NULL)
- result = -EINVAL;
- else if (vd->owner != file)
- result = -EPERM;
- else {
- vd->owner = NULL;
- result = 0;
- }
- }
- up_write(&card->controls_rwsem);
- return result;
+ if (!kctl)
+ return -ENOENT;
+ vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
+ if (!vd->owner)
+ return -EINVAL;
+ if (vd->owner != file)
+ return -EPERM;
+ vd->owner = NULL;
+ return 0;
}
struct user_element {
struct snd_ctl_elem_info info;
- void *elem_data; /* element data */
+ struct snd_card *card;
+ char *elem_data; /* element data */
unsigned long elem_data_size; /* size of element data in bytes */
void *tlv_data; /* TLV data */
unsigned long tlv_data_size; /* TLV data size */
void *priv_data; /* private data (like strings for enumerated type) */
- unsigned long priv_data_size; /* size of private data in bytes */
};
+// check whether the addition (in bytes) of user ctl element may overflow the limit.
+static bool check_user_elem_overflow(struct snd_card *card, ssize_t add)
+{
+ return (ssize_t)card->user_ctl_alloc_size + add > max_user_ctl_alloc_size;
+}
+
static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- struct user_element *ue = kcontrol->private_data;
+ struct user_element *ue = snd_kcontrol_chip(kcontrol);
+ unsigned int offset;
+ offset = snd_ctl_get_ioff(kcontrol, &uinfo->id);
*uinfo = ue->info;
+ snd_ctl_build_ioff(&uinfo->id, kcontrol, offset);
+
+ return 0;
+}
+
+static int snd_ctl_elem_user_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct user_element *ue = snd_kcontrol_chip(kcontrol);
+ const char *names;
+ unsigned int item;
+ unsigned int offset;
+
+ item = uinfo->value.enumerated.item;
+
+ offset = snd_ctl_get_ioff(kcontrol, &uinfo->id);
+ *uinfo = ue->info;
+ snd_ctl_build_ioff(&uinfo->id, kcontrol, offset);
+
+ item = min(item, uinfo->value.enumerated.items - 1);
+ uinfo->value.enumerated.item = item;
+
+ names = ue->priv_data;
+ for (; item > 0; --item)
+ names += strlen(names) + 1;
+ strscpy(uinfo->value.enumerated.name, names);
+
return 0;
}
static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct user_element *ue = kcontrol->private_data;
+ struct user_element *ue = snd_kcontrol_chip(kcontrol);
+ unsigned int size = ue->elem_data_size;
+ char *src = ue->elem_data +
+ snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size;
- memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size);
+ memcpy(&ucontrol->value, src, size);
return 0;
}
static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- int change;
- struct user_element *ue = kcontrol->private_data;
-
- change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0;
+ int err, change;
+ struct user_element *ue = snd_kcontrol_chip(kcontrol);
+ unsigned int size = ue->elem_data_size;
+ char *dst = ue->elem_data +
+ snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size;
+
+ err = sanity_check_input_values(ue->card, ucontrol, &ue->info, false);
+ if (err < 0)
+ return err;
+
+ change = memcmp(&ucontrol->value, dst, size) != 0;
if (change)
- memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size);
+ memcpy(dst, &ucontrol->value, size);
return change;
}
-static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol,
- int op_flag,
- unsigned int size,
- unsigned int __user *tlv)
+/* called in controls_rwsem write lock */
+static int replace_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
+ unsigned int size)
{
- struct user_element *ue = kcontrol->private_data;
- int change = 0;
- void *new_data;
+ struct user_element *ue = snd_kcontrol_chip(kctl);
+ unsigned int *container;
+ unsigned int mask = 0;
+ int i;
+ int change;
- if (op_flag > 0) {
- if (size > 1024 * 128) /* sane value */
- return -EINVAL;
+ lockdep_assert_held_write(&ue->card->controls_rwsem);
+
+ if (size > 1024 * 128) /* sane value */
+ return -EINVAL;
+
+ // does the TLV size change cause overflow?
+ if (check_user_elem_overflow(ue->card, (ssize_t)(size - ue->tlv_data_size)))
+ return -ENOMEM;
+
+ container = vmemdup_user(buf, size);
+ if (IS_ERR(container))
+ return PTR_ERR(container);
- new_data = memdup_user(tlv, size);
- if (IS_ERR(new_data))
- return PTR_ERR(new_data);
- change = ue->tlv_data_size != size;
- if (!change)
- change = memcmp(ue->tlv_data, new_data, size);
- kfree(ue->tlv_data);
- ue->tlv_data = new_data;
- ue->tlv_data_size = size;
+ change = ue->tlv_data_size != size;
+ if (!change)
+ change = memcmp(ue->tlv_data, container, size) != 0;
+ if (!change) {
+ kvfree(container);
+ return 0;
+ }
+
+ if (ue->tlv_data == NULL) {
+ /* Now TLV data is available. */
+ for (i = 0; i < kctl->count; ++i)
+ kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ mask = SNDRV_CTL_EVENT_MASK_INFO;
} else {
- if (! ue->tlv_data_size || ! ue->tlv_data)
- return -ENXIO;
- if (size < ue->tlv_data_size)
- return -ENOSPC;
- if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size))
- return -EFAULT;
+ ue->card->user_ctl_alloc_size -= ue->tlv_data_size;
+ ue->tlv_data_size = 0;
+ kvfree(ue->tlv_data);
}
+
+ ue->tlv_data = container;
+ ue->tlv_data_size = size;
+ // decremented at private_free.
+ ue->card->user_ctl_alloc_size += size;
+
+ mask |= SNDRV_CTL_EVENT_MASK_TLV;
+ for (i = 0; i < kctl->count; ++i)
+ snd_ctl_notify_one(ue->card, mask, kctl, i);
+
return change;
}
+static int read_user_tlv(struct snd_kcontrol *kctl, unsigned int __user *buf,
+ unsigned int size)
+{
+ struct user_element *ue = snd_kcontrol_chip(kctl);
+
+ if (ue->tlv_data_size == 0 || ue->tlv_data == NULL)
+ return -ENXIO;
+
+ if (size < ue->tlv_data_size)
+ return -ENOSPC;
+
+ if (copy_to_user(buf, ue->tlv_data, ue->tlv_data_size))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kctl, int op_flag,
+ unsigned int size, unsigned int __user *buf)
+{
+ if (op_flag == SNDRV_CTL_TLV_OP_WRITE)
+ return replace_user_tlv(kctl, buf, size);
+ else
+ return read_user_tlv(kctl, buf, size);
+}
+
+/* called in controls_rwsem write lock */
+static int snd_ctl_elem_init_enum_names(struct user_element *ue)
+{
+ char *names, *p;
+ size_t buf_len, name_len;
+ unsigned int i;
+ const uintptr_t user_ptrval = ue->info.value.enumerated.names_ptr;
+
+ lockdep_assert_held_write(&ue->card->controls_rwsem);
+
+ buf_len = ue->info.value.enumerated.names_length;
+ if (buf_len > 64 * 1024)
+ return -EINVAL;
+
+ if (check_user_elem_overflow(ue->card, buf_len))
+ return -ENOMEM;
+ names = vmemdup_user((const void __user *)user_ptrval, buf_len);
+ if (IS_ERR(names))
+ return PTR_ERR(names);
+
+ /* check that there are enough valid names */
+ p = names;
+ for (i = 0; i < ue->info.value.enumerated.items; ++i) {
+ name_len = strnlen(p, buf_len);
+ if (name_len == 0 || name_len >= 64 || name_len == buf_len) {
+ kvfree(names);
+ return -EINVAL;
+ }
+ p += name_len + 1;
+ buf_len -= name_len + 1;
+ }
+
+ ue->priv_data = names;
+ ue->info.value.enumerated.names_ptr = 0;
+ // increment the allocation size; decremented again at private_free.
+ ue->card->user_ctl_alloc_size += ue->info.value.enumerated.names_length;
+
+ return 0;
+}
+
+static size_t compute_user_elem_size(size_t size, unsigned int count)
+{
+ return sizeof(struct user_element) + size * count;
+}
+
static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
{
- struct user_element *ue = kcontrol->private_data;
- if (ue->tlv_data)
- kfree(ue->tlv_data);
+ struct user_element *ue = snd_kcontrol_chip(kcontrol);
+
+ // decrement the allocation size.
+ ue->card->user_ctl_alloc_size -= compute_user_elem_size(ue->elem_data_size, kcontrol->count);
+ ue->card->user_ctl_alloc_size -= ue->tlv_data_size;
+ if (ue->priv_data)
+ ue->card->user_ctl_alloc_size -= ue->info.value.enumerated.names_length;
+
+ kvfree(ue->tlv_data);
+ kvfree(ue->priv_data);
kfree(ue);
}
@@ -960,99 +1615,128 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
struct snd_ctl_elem_info *info, int replace)
{
struct snd_card *card = file->card;
- struct snd_kcontrol kctl, *_kctl;
+ struct snd_kcontrol *kctl;
+ unsigned int count;
unsigned int access;
long private_size;
+ size_t alloc_size;
struct user_element *ue;
- int idx, err;
-
- if (card->user_ctl_count >= MAX_USER_CONTROLS)
- return -ENOMEM;
- if (info->count < 1)
+ unsigned int offset;
+ int err;
+
+ if (!*info->id.name)
return -EINVAL;
- access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
- (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
- SNDRV_CTL_ELEM_ACCESS_INACTIVE|
- SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE));
- info->id.numid = 0;
- memset(&kctl, 0, sizeof(kctl));
- down_write(&card->controls_rwsem);
- _kctl = snd_ctl_find_id(card, &info->id);
- err = 0;
- if (_kctl) {
- if (replace)
- err = snd_ctl_remove(card, _kctl);
- else
- err = -EBUSY;
- } else {
- if (replace)
- err = -ENOENT;
+ if (strnlen(info->id.name, sizeof(info->id.name)) >= sizeof(info->id.name))
+ return -EINVAL;
+
+ /* Delete a control to replace them if needed. */
+ if (replace) {
+ info->id.numid = 0;
+ err = snd_ctl_remove_user_ctl(file, &info->id);
+ if (err)
+ return err;
}
- up_write(&card->controls_rwsem);
+
+ /* Check the number of elements for this userspace control. */
+ count = info->owner;
+ if (count == 0)
+ count = 1;
+ if (count > MAX_CONTROL_COUNT)
+ return -EINVAL;
+
+ /* Arrange access permissions if needed. */
+ access = info->access;
+ if (access == 0)
+ access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_WRITE);
+
+ /* In initial state, nothing is available as TLV container. */
+ if (access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
+ access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+ access |= SNDRV_CTL_ELEM_ACCESS_USER;
+
+ /*
+ * Check information and calculate the size of data specific to
+ * this userspace control.
+ */
+ /* pass NULL to card for suppressing error messages */
+ err = snd_ctl_check_elem_info(NULL, info);
if (err < 0)
return err;
- memcpy(&kctl.id, &info->id, sizeof(info->id));
- kctl.count = info->owner ? info->owner : 1;
- access |= SNDRV_CTL_ELEM_ACCESS_USER;
- kctl.info = snd_ctl_elem_user_info;
- if (access & SNDRV_CTL_ELEM_ACCESS_READ)
- kctl.get = snd_ctl_elem_user_get;
- if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
- kctl.put = snd_ctl_elem_user_put;
- if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
- kctl.tlv.c = snd_ctl_elem_user_tlv;
- access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
- }
- switch (info->type) {
- case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
- case SNDRV_CTL_ELEM_TYPE_INTEGER:
- private_size = sizeof(long);
- if (info->count > 128)
- return -EINVAL;
- break;
- case SNDRV_CTL_ELEM_TYPE_INTEGER64:
- private_size = sizeof(long long);
- if (info->count > 64)
- return -EINVAL;
- break;
- case SNDRV_CTL_ELEM_TYPE_BYTES:
- private_size = sizeof(unsigned char);
- if (info->count > 512)
- return -EINVAL;
- break;
- case SNDRV_CTL_ELEM_TYPE_IEC958:
- private_size = sizeof(struct snd_aes_iec958);
- if (info->count != 1)
- return -EINVAL;
- break;
- default:
+ /* user-space control doesn't allow zero-size data */
+ if (info->count < 1)
return -EINVAL;
- }
- private_size *= info->count;
- ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL);
- if (ue == NULL)
+ private_size = value_sizes[info->type] * info->count;
+ alloc_size = compute_user_elem_size(private_size, count);
+
+ guard(rwsem_write)(&card->controls_rwsem);
+ if (check_user_elem_overflow(card, alloc_size))
+ return -ENOMEM;
+
+ /*
+ * Keep memory object for this userspace control. After passing this
+ * code block, the instance should be freed by snd_ctl_free_one().
+ *
+ * Note that these elements in this control are locked.
+ */
+ err = snd_ctl_new(&kctl, count, access, file);
+ if (err < 0)
+ return err;
+ memcpy(&kctl->id, &info->id, sizeof(kctl->id));
+ ue = kzalloc(alloc_size, GFP_KERNEL);
+ if (!ue) {
+ kfree(kctl);
return -ENOMEM;
+ }
+ kctl->private_data = ue;
+ kctl->private_free = snd_ctl_elem_user_free;
+
+ // increment the allocated size; decremented again at private_free.
+ card->user_ctl_alloc_size += alloc_size;
+
+ /* Set private data for this userspace control. */
+ ue->card = card;
ue->info = *info;
ue->info.access = 0;
ue->elem_data = (char *)ue + sizeof(*ue);
ue->elem_data_size = private_size;
- kctl.private_free = snd_ctl_elem_user_free;
- _kctl = snd_ctl_new(&kctl, access);
- if (_kctl == NULL) {
- kfree(ue);
- return -ENOMEM;
+ if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
+ err = snd_ctl_elem_init_enum_names(ue);
+ if (err < 0) {
+ snd_ctl_free_one(kctl);
+ return err;
+ }
}
- _kctl->private_data = ue;
- for (idx = 0; idx < _kctl->count; idx++)
- _kctl->vd[idx].owner = file;
- err = snd_ctl_add(card, _kctl);
- if (err < 0)
- return err;
- down_write(&card->controls_rwsem);
- card->user_ctl_count++;
- up_write(&card->controls_rwsem);
+ /* Set callback functions. */
+ if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED)
+ kctl->info = snd_ctl_elem_user_enum_info;
+ else
+ kctl->info = snd_ctl_elem_user_info;
+ if (access & SNDRV_CTL_ELEM_ACCESS_READ)
+ kctl->get = snd_ctl_elem_user_get;
+ if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
+ kctl->put = snd_ctl_elem_user_put;
+ if (access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
+ kctl->tlv.c = snd_ctl_elem_user_tlv;
+ /* This function manage to free the instance on failure. */
+ err = __snd_ctl_add_replace(card, kctl, CTL_ADD_EXCLUSIVE);
+ if (err < 0) {
+ snd_ctl_free_one(kctl);
+ return err;
+ }
+ offset = snd_ctl_get_ioff(kctl, &info->id);
+ snd_ctl_build_ioff(&info->id, kctl, offset);
+ /*
+ * Here we cannot fill any field for the number of elements added by
+ * this operation because there're no specific fields. The usage of
+ * 'owner' field for this purpose may cause any bugs to userspace
+ * applications because the field originally means PID of a process
+ * which locks the element.
+ */
return 0;
}
@@ -1060,9 +1744,19 @@ static int snd_ctl_elem_add_user(struct snd_ctl_file *file,
struct snd_ctl_elem_info __user *_info, int replace)
{
struct snd_ctl_elem_info info;
+ int err;
+
if (copy_from_user(&info, _info, sizeof(info)))
return -EFAULT;
- return snd_ctl_elem_add(file, &info, replace);
+ err = snd_ctl_elem_add(file, &info, replace);
+ if (err < 0)
+ return err;
+ if (copy_to_user(_info, &info, sizeof(info))) {
+ snd_ctl_remove_user_ctl(file, &info.id);
+ return -EFAULT;
+ }
+
+ return 0;
}
static int snd_ctl_elem_remove(struct snd_ctl_file *file,
@@ -1096,65 +1790,110 @@ static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr)
return 0;
}
+static int call_tlv_handler(struct snd_ctl_file *file, int op_flag,
+ struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_id *id,
+ unsigned int __user *buf, unsigned int size)
+{
+ static const struct {
+ int op;
+ int perm;
+ } pairs[] = {
+ {SNDRV_CTL_TLV_OP_READ, SNDRV_CTL_ELEM_ACCESS_TLV_READ},
+ {SNDRV_CTL_TLV_OP_WRITE, SNDRV_CTL_ELEM_ACCESS_TLV_WRITE},
+ {SNDRV_CTL_TLV_OP_CMD, SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND},
+ };
+ struct snd_kcontrol_volatile *vd = &kctl->vd[snd_ctl_get_ioff(kctl, id)];
+ int i;
+
+ /* Check support of the request for this element. */
+ for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
+ if (op_flag == pairs[i].op && (vd->access & pairs[i].perm))
+ break;
+ }
+ if (i == ARRAY_SIZE(pairs))
+ return -ENXIO;
+
+ if (kctl->tlv.c == NULL)
+ return -ENXIO;
+
+ /* Write and command operations are not allowed for locked element. */
+ if (op_flag != SNDRV_CTL_TLV_OP_READ &&
+ vd->owner != NULL && vd->owner != file)
+ return -EPERM;
+
+ return kctl->tlv.c(kctl, op_flag, size, buf);
+}
+
+static int read_tlv_buf(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id,
+ unsigned int __user *buf, unsigned int size)
+{
+ struct snd_kcontrol_volatile *vd = &kctl->vd[snd_ctl_get_ioff(kctl, id)];
+ unsigned int len;
+
+ if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ))
+ return -ENXIO;
+
+ if (kctl->tlv.p == NULL)
+ return -ENXIO;
+
+ len = sizeof(unsigned int) * 2 + kctl->tlv.p[1];
+ if (size < len)
+ return -ENOMEM;
+
+ if (copy_to_user(buf, kctl->tlv.p, len))
+ return -EFAULT;
+
+ return 0;
+}
+
static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
- struct snd_ctl_tlv __user *_tlv,
+ struct snd_ctl_tlv __user *buf,
int op_flag)
{
- struct snd_card *card = file->card;
- struct snd_ctl_tlv tlv;
+ struct snd_ctl_tlv header;
+ unsigned int __user *container;
+ unsigned int container_size;
struct snd_kcontrol *kctl;
+ struct snd_ctl_elem_id id;
struct snd_kcontrol_volatile *vd;
- unsigned int len;
- int err = 0;
- if (copy_from_user(&tlv, _tlv, sizeof(tlv)))
+ lockdep_assert_held(&file->card->controls_rwsem);
+
+ if (copy_from_user(&header, buf, sizeof(header)))
return -EFAULT;
- if (tlv.length < sizeof(unsigned int) * 2)
+
+ /* In design of control core, numerical ID starts at 1. */
+ if (header.numid == 0)
return -EINVAL;
- down_read(&card->controls_rwsem);
- kctl = snd_ctl_find_numid(card, tlv.numid);
- if (kctl == NULL) {
- err = -ENOENT;
- goto __kctl_end;
- }
- if (kctl->tlv.p == NULL) {
- err = -ENXIO;
- goto __kctl_end;
- }
- vd = &kctl->vd[tlv.numid - kctl->id.numid];
- if ((op_flag == 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) ||
- (op_flag > 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) ||
- (op_flag < 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) {
- err = -ENXIO;
- goto __kctl_end;
- }
+
+ /* At least, container should include type and length fields. */
+ if (header.length < sizeof(unsigned int) * 2)
+ return -EINVAL;
+ container_size = header.length;
+ container = buf->tlv;
+
+ kctl = snd_ctl_find_numid(file->card, header.numid);
+ if (kctl == NULL)
+ return -ENOENT;
+
+ /* Calculate index of the element in this set. */
+ id = kctl->id;
+ snd_ctl_build_ioff(&id, kctl, header.numid - id.numid);
+ vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
+
if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
- if (vd->owner != NULL && vd->owner != file) {
- err = -EPERM;
- goto __kctl_end;
- }
- err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv);
- if (err > 0) {
- up_read(&card->controls_rwsem);
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id);
- return 0;
- }
+ return call_tlv_handler(file, op_flag, kctl, &id, container,
+ container_size);
} else {
- if (op_flag) {
- err = -ENXIO;
- goto __kctl_end;
+ if (op_flag == SNDRV_CTL_TLV_OP_READ) {
+ return read_tlv_buf(kctl, &id, container,
+ container_size);
}
- len = kctl->tlv.p[1] + 2 * sizeof(unsigned int);
- if (tlv.length < len) {
- err = -ENOMEM;
- goto __kctl_end;
- }
- if (copy_to_user(_tlv->tlv, kctl->tlv.p, len))
- err = -EFAULT;
}
- __kctl_end:
- up_read(&card->controls_rwsem);
- return err;
+
+ /* Not supported. */
+ return -ENXIO;
}
static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -1176,7 +1915,7 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
case SNDRV_CTL_IOCTL_CARD_INFO:
return snd_ctl_card_info(card, ctl, cmd, argp);
case SNDRV_CTL_IOCTL_ELEM_LIST:
- return snd_ctl_elem_list(card, argp);
+ return snd_ctl_elem_list_user(card, argp);
case SNDRV_CTL_IOCTL_ELEM_INFO:
return snd_ctl_elem_info_user(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_READ:
@@ -1196,30 +1935,42 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
return snd_ctl_subscribe_events(ctl, ip);
case SNDRV_CTL_IOCTL_TLV_READ:
- return snd_ctl_tlv_ioctl(ctl, argp, 0);
+ err = snd_power_ref_and_wait(card);
+ if (err < 0)
+ return err;
+ scoped_guard(rwsem_read, &card->controls_rwsem)
+ err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ);
+ snd_power_unref(card);
+ return err;
case SNDRV_CTL_IOCTL_TLV_WRITE:
- return snd_ctl_tlv_ioctl(ctl, argp, 1);
+ err = snd_power_ref_and_wait(card);
+ if (err < 0)
+ return err;
+ scoped_guard(rwsem_write, &card->controls_rwsem)
+ err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE);
+ snd_power_unref(card);
+ return err;
case SNDRV_CTL_IOCTL_TLV_COMMAND:
- return snd_ctl_tlv_ioctl(ctl, argp, -1);
+ err = snd_power_ref_and_wait(card);
+ if (err < 0)
+ return err;
+ scoped_guard(rwsem_write, &card->controls_rwsem)
+ err = snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD);
+ snd_power_unref(card);
+ return err;
case SNDRV_CTL_IOCTL_POWER:
return -ENOPROTOOPT;
case SNDRV_CTL_IOCTL_POWER_STATE:
-#ifdef CONFIG_PM
- return put_user(card->power_state, ip) ? -EFAULT : 0;
-#else
return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0;
-#endif
}
- down_read(&snd_ioctl_rwsem);
+
+ guard(rwsem_read)(&snd_ioctl_rwsem);
list_for_each_entry(p, &snd_control_ioctls, list) {
err = p->fioctl(card, ctl, cmd, arg);
- if (err != -ENOIOCTLCMD) {
- up_read(&snd_ioctl_rwsem);
+ if (err != -ENOIOCTLCMD)
return err;
- }
}
- up_read(&snd_ioctl_rwsem);
- snd_printdd("unknown ioctl = 0x%x\n", cmd);
+ dev_dbg(card->dev, "unknown ioctl = 0x%x\n", cmd);
return -ENOTTY;
}
@@ -1242,7 +1993,7 @@ static ssize_t snd_ctl_read(struct file *file, char __user *buffer,
struct snd_ctl_event ev;
struct snd_kctl_event *kev;
while (list_empty(&ctl->events)) {
- wait_queue_t wait;
+ wait_queue_entry_t wait;
if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
err = -EAGAIN;
goto __end_lock;
@@ -1253,6 +2004,8 @@ static ssize_t snd_ctl_read(struct file *file, char __user *buffer,
spin_unlock_irq(&ctl->read_lock);
schedule();
remove_wait_queue(&ctl->change_sleep, &wait);
+ if (ctl->card->shutdown)
+ return -ENODEV;
if (signal_pending(current))
return -ERESTARTSYS;
spin_lock_irq(&ctl->read_lock);
@@ -1279,9 +2032,9 @@ static ssize_t snd_ctl_read(struct file *file, char __user *buffer,
return result > 0 ? result : err;
}
-static unsigned int snd_ctl_poll(struct file *file, poll_table * wait)
+static __poll_t snd_ctl_poll(struct file *file, poll_table * wait)
{
- unsigned int mask;
+ __poll_t mask;
struct snd_ctl_file *ctl;
ctl = file->private_data;
@@ -1291,7 +2044,7 @@ static unsigned int snd_ctl_poll(struct file *file, poll_table * wait)
mask = 0;
if (!list_empty(&ctl->events))
- mask |= POLLIN | POLLRDNORM;
+ mask |= EPOLLIN | EPOLLRDNORM;
return mask;
}
@@ -1308,25 +2061,37 @@ static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *
if (pn == NULL)
return -ENOMEM;
pn->fioctl = fcn;
- down_write(&snd_ioctl_rwsem);
+ guard(rwsem_write)(&snd_ioctl_rwsem);
list_add_tail(&pn->list, lists);
- up_write(&snd_ioctl_rwsem);
return 0;
}
+/**
+ * snd_ctl_register_ioctl - register the device-specific control-ioctls
+ * @fcn: ioctl callback function
+ *
+ * called from each device manager like pcm.c, hwdep.c, etc.
+ *
+ * Return: zero if successful, or a negative error code
+ */
int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_register_ioctl(fcn, &snd_control_ioctls);
}
-
EXPORT_SYMBOL(snd_ctl_register_ioctl);
#ifdef CONFIG_COMPAT
+/**
+ * snd_ctl_register_ioctl_compat - register the device-specific 32bit compat
+ * control-ioctls
+ * @fcn: ioctl callback function
+ *
+ * Return: zero if successful, or a negative error code
+ */
int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_register_ioctl(fcn, &snd_control_compat_ioctls);
}
-
EXPORT_SYMBOL(snd_ctl_register_ioctl_compat);
#endif
@@ -1340,33 +2105,42 @@ static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,
if (snd_BUG_ON(!fcn))
return -EINVAL;
- down_write(&snd_ioctl_rwsem);
+ guard(rwsem_write)(&snd_ioctl_rwsem);
list_for_each_entry(p, lists, list) {
if (p->fioctl == fcn) {
list_del(&p->list);
- up_write(&snd_ioctl_rwsem);
kfree(p);
return 0;
}
}
- up_write(&snd_ioctl_rwsem);
snd_BUG();
return -EINVAL;
}
+/**
+ * snd_ctl_unregister_ioctl - de-register the device-specific control-ioctls
+ * @fcn: ioctl callback function to unregister
+ *
+ * Return: zero if successful, or a negative error code
+ */
int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_unregister_ioctl(fcn, &snd_control_ioctls);
}
-
EXPORT_SYMBOL(snd_ctl_unregister_ioctl);
#ifdef CONFIG_COMPAT
+/**
+ * snd_ctl_unregister_ioctl_compat - de-register the device-specific compat
+ * 32bit control-ioctls
+ * @fcn: ioctl callback function to unregister
+ *
+ * Return: zero if successful, or a negative error code
+ */
int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn)
{
return _snd_ctl_unregister_ioctl(fcn, &snd_control_compat_ioctls);
}
-
EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat);
#endif
@@ -1375,8 +2149,28 @@ static int snd_ctl_fasync(int fd, struct file * file, int on)
struct snd_ctl_file *ctl;
ctl = file->private_data;
- return fasync_helper(fd, file, on, &ctl->fasync);
+ return snd_fasync_helper(fd, file, on, &ctl->fasync);
+}
+
+/* return the preferred subdevice number if already assigned;
+ * otherwise return -1
+ */
+int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type)
+{
+ struct snd_ctl_file *kctl;
+ int subdevice = -1;
+
+ guard(read_lock_irqsave)(&card->controls_rwlock);
+ list_for_each_entry(kctl, &card->ctl_files, list) {
+ if (kctl->pid == task_pid(current)) {
+ subdevice = kctl->preferred_subdevice[type];
+ if (subdevice != -1)
+ break;
+ }
+ }
+ return subdevice;
}
+EXPORT_SYMBOL_GPL(snd_ctl_get_preferred_subdevice);
/*
* ioctl32 compat
@@ -1388,6 +2182,84 @@ static int snd_ctl_fasync(int fd, struct file * file, int on)
#endif
/*
+ * control layers (audio LED etc.)
+ */
+
+/**
+ * snd_ctl_request_layer - request to use the layer
+ * @module_name: Name of the kernel module (NULL == build-in)
+ *
+ * Return: zero if successful, or an error code when the module cannot be loaded
+ */
+int snd_ctl_request_layer(const char *module_name)
+{
+ struct snd_ctl_layer_ops *lops;
+
+ if (module_name == NULL)
+ return 0;
+ scoped_guard(rwsem_read, &snd_ctl_layer_rwsem) {
+ for (lops = snd_ctl_layer; lops; lops = lops->next)
+ if (strcmp(lops->module_name, module_name) == 0)
+ return 0;
+ }
+ return request_module(module_name);
+}
+EXPORT_SYMBOL_GPL(snd_ctl_request_layer);
+
+/**
+ * snd_ctl_register_layer - register new control layer
+ * @lops: operation structure
+ *
+ * The new layer can track all control elements and do additional
+ * operations on top (like audio LED handling).
+ */
+void snd_ctl_register_layer(struct snd_ctl_layer_ops *lops)
+{
+ struct snd_card *card;
+ int card_number;
+
+ scoped_guard(rwsem_write, &snd_ctl_layer_rwsem) {
+ lops->next = snd_ctl_layer;
+ snd_ctl_layer = lops;
+ }
+ for (card_number = 0; card_number < SNDRV_CARDS; card_number++) {
+ card = snd_card_ref(card_number);
+ if (card) {
+ scoped_guard(rwsem_read, &card->controls_rwsem)
+ lops->lregister(card);
+ snd_card_unref(card);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(snd_ctl_register_layer);
+
+/**
+ * snd_ctl_disconnect_layer - disconnect control layer
+ * @lops: operation structure
+ *
+ * It is expected that the information about tracked cards
+ * is freed before this call (the disconnect callback is
+ * not called here).
+ */
+void snd_ctl_disconnect_layer(struct snd_ctl_layer_ops *lops)
+{
+ struct snd_ctl_layer_ops *lops2, *prev_lops2;
+
+ guard(rwsem_write)(&snd_ctl_layer_rwsem);
+ for (lops2 = snd_ctl_layer, prev_lops2 = NULL; lops2; lops2 = lops2->next) {
+ if (lops2 == lops) {
+ if (!prev_lops2)
+ snd_ctl_layer = lops->next;
+ else
+ prev_lops2->next = lops->next;
+ break;
+ }
+ prev_lops2 = lops2;
+ }
+}
+EXPORT_SYMBOL_GPL(snd_ctl_disconnect_layer);
+
+/*
* INIT PART
*/
@@ -1397,31 +2269,35 @@ static const struct file_operations snd_ctl_f_ops =
.read = snd_ctl_read,
.open = snd_ctl_open,
.release = snd_ctl_release,
- .llseek = no_llseek,
.poll = snd_ctl_poll,
.unlocked_ioctl = snd_ctl_ioctl,
.compat_ioctl = snd_ctl_ioctl_compat,
.fasync = snd_ctl_fasync,
};
+/* call lops under rwsems; called from snd_ctl_dev_*() below() */
+#define call_snd_ctl_lops(_card, _op) \
+ do { \
+ struct snd_ctl_layer_ops *lops; \
+ guard(rwsem_read)(&(_card)->controls_rwsem); \
+ guard(rwsem_read)(&snd_ctl_layer_rwsem); \
+ for (lops = snd_ctl_layer; lops; lops = lops->next) \
+ lops->_op(_card); \
+ } while (0)
+
/*
* registration of the control device
*/
static int snd_ctl_dev_register(struct snd_device *device)
{
struct snd_card *card = device->device_data;
- int err, cardnum;
- char name[16];
+ int err;
- if (snd_BUG_ON(!card))
- return -ENXIO;
- cardnum = card->number;
- if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
- return -ENXIO;
- sprintf(name, "controlC%i", cardnum);
- if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
- &snd_ctl_f_ops, card, name)) < 0)
+ err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
+ &snd_ctl_f_ops, card, card->ctl_dev);
+ if (err < 0)
return err;
+ call_snd_ctl_lops(card, lregister);
return 0;
}
@@ -1432,25 +2308,16 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
{
struct snd_card *card = device->device_data;
struct snd_ctl_file *ctl;
- int err, cardnum;
-
- if (snd_BUG_ON(!card))
- return -ENXIO;
- cardnum = card->number;
- if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
- return -ENXIO;
- read_lock(&card->ctl_files_rwlock);
- list_for_each_entry(ctl, &card->ctl_files, list) {
- wake_up(&ctl->change_sleep);
- kill_fasync(&ctl->fasync, SIGIO, POLL_ERR);
+ scoped_guard(read_lock_irqsave, &card->controls_rwlock) {
+ list_for_each_entry(ctl, &card->ctl_files, list) {
+ wake_up(&ctl->change_sleep);
+ snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR);
+ }
}
- read_unlock(&card->ctl_files_rwlock);
- if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL,
- card, -1)) < 0)
- return err;
- return 0;
+ call_snd_ctl_lops(card, ldisconnect);
+ return snd_unregister_device(card->ctl_dev);
}
/*
@@ -1461,12 +2328,18 @@ static int snd_ctl_dev_free(struct snd_device *device)
struct snd_card *card = device->device_data;
struct snd_kcontrol *control;
- down_write(&card->controls_rwsem);
- while (!list_empty(&card->controls)) {
- control = snd_kcontrol(card->controls.next);
- snd_ctl_remove(card, control);
+ scoped_guard(rwsem_write, &card->controls_rwsem) {
+ while (!list_empty(&card->controls)) {
+ control = snd_kcontrol(card->controls.next);
+ __snd_ctl_remove(card, control, false);
+ }
+
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+ xa_destroy(&card->ctl_numids);
+ xa_destroy(&card->ctl_hash);
+#endif
}
- up_write(&card->controls_rwsem);
+ put_device(card->ctl_dev);
return 0;
}
@@ -1476,19 +2349,43 @@ static int snd_ctl_dev_free(struct snd_device *device)
*/
int snd_ctl_create(struct snd_card *card)
{
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_ctl_dev_free,
.dev_register = snd_ctl_dev_register,
.dev_disconnect = snd_ctl_dev_disconnect,
};
+ int err;
if (snd_BUG_ON(!card))
return -ENXIO;
- return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
+ if (snd_BUG_ON(card->number < 0 || card->number >= SNDRV_CARDS))
+ return -ENXIO;
+
+ err = snd_device_alloc(&card->ctl_dev, card);
+ if (err < 0)
+ return err;
+ dev_set_name(card->ctl_dev, "controlC%d", card->number);
+
+ err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
+ if (err < 0)
+ put_device(card->ctl_dev);
+ return err;
}
/*
- * Frequently used control callbacks
+ * Frequently used control callbacks/helpers
+ */
+
+/**
+ * snd_ctl_boolean_mono_info - Helper function for a standard boolean info
+ * callback with a mono channel
+ * @kcontrol: the kcontrol instance
+ * @uinfo: info to store
+ *
+ * This is a function that can be used as info callback for a standard
+ * boolean control with a single mono channel.
+ *
+ * Return: Zero (always successful)
*/
int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -1499,9 +2396,19 @@ int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
uinfo->value.integer.max = 1;
return 0;
}
-
EXPORT_SYMBOL(snd_ctl_boolean_mono_info);
+/**
+ * snd_ctl_boolean_stereo_info - Helper function for a standard boolean info
+ * callback with stereo two channels
+ * @kcontrol: the kcontrol instance
+ * @uinfo: info to store
+ *
+ * This is a function that can be used as info callback for a standard
+ * boolean control with stereo two channels.
+ *
+ * Return: Zero (always successful)
+ */
int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -1511,5 +2418,37 @@ int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
uinfo->value.integer.max = 1;
return 0;
}
-
EXPORT_SYMBOL(snd_ctl_boolean_stereo_info);
+
+/**
+ * snd_ctl_enum_info - fills the info structure for an enumerated control
+ * @info: the structure to be filled
+ * @channels: the number of the control's channels; often one
+ * @items: the number of control values; also the size of @names
+ * @names: an array containing the names of all control values
+ *
+ * Sets all required fields in @info to their appropriate values.
+ * If the control's accessibility is not the default (readable and writable),
+ * the caller has to fill @info->access.
+ *
+ * Return: Zero (always successful)
+ */
+int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,
+ unsigned int items, const char *const names[])
+{
+ info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ info->count = channels;
+ info->value.enumerated.items = items;
+ if (!items)
+ return 0;
+ if (info->value.enumerated.item >= items)
+ info->value.enumerated.item = items - 1;
+ WARN(strlen(names[info->value.enumerated.item]) >= sizeof(info->value.enumerated.name),
+ "ALSA: too long item name '%s'\n",
+ names[info->value.enumerated.item]);
+ strscpy(info->value.enumerated.name,
+ names[info->value.enumerated.item],
+ sizeof(info->value.enumerated.name));
+ return 0;
+}
+EXPORT_SYMBOL(snd_ctl_enum_info);
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index 426874429a5e..6459809ed364 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* compat ioctls for control API
*
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* this file included from control.c */
@@ -35,24 +22,22 @@ struct snd_ctl_elem_list32 {
static int snd_ctl_elem_list_compat(struct snd_card *card,
struct snd_ctl_elem_list32 __user *data32)
{
- struct snd_ctl_elem_list __user *data;
+ struct snd_ctl_elem_list data = {};
compat_caddr_t ptr;
int err;
- data = compat_alloc_user_space(sizeof(*data));
-
/* offset, space, used, count */
- if (copy_in_user(data, data32, 4 * sizeof(u32)))
+ if (copy_from_user(&data, data32, 4 * sizeof(u32)))
return -EFAULT;
/* pids */
- if (get_user(ptr, &data32->pids) ||
- put_user(compat_ptr(ptr), &data->pids))
+ if (get_user(ptr, &data32->pids))
return -EFAULT;
- err = snd_ctl_elem_list(card, data);
+ data.pids = compat_ptr(ptr);
+ err = snd_ctl_elem_list(card, &data);
if (err < 0)
return err;
/* copy the result */
- if (copy_in_user(data32, data, 4 * sizeof(u32)))
+ if (copy_to_user(data32, &data, 4 * sizeof(u32)))
return -EFAULT;
return 0;
}
@@ -83,75 +68,72 @@ struct snd_ctl_elem_info32 {
u32 items;
u32 item;
char name[64];
+ u64 names_ptr;
+ u32 names_length;
} enumerated;
unsigned char reserved[128];
} value;
unsigned char reserved[64];
-} __attribute__((packed));
+} __packed;
static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl,
struct snd_ctl_elem_info32 __user *data32)
{
- struct snd_ctl_elem_info *data;
+ struct snd_card *card = ctl->card;
+ struct snd_ctl_elem_info *data __free(kfree) = NULL;
int err;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (! data)
return -ENOMEM;
- err = -EFAULT;
/* copy id */
if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
- goto error;
+ return -EFAULT;
/* we need to copy the item index.
* hope this doesn't break anything..
*/
if (get_user(data->value.enumerated.item, &data32->value.enumerated.item))
- goto error;
-
- snd_power_lock(ctl->card);
- err = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
- if (err >= 0)
- err = snd_ctl_elem_info(ctl, data);
- snd_power_unlock(ctl->card);
+ return -EFAULT;
+ err = snd_power_ref_and_wait(card);
if (err < 0)
- goto error;
+ return err;
+ err = snd_ctl_elem_info(ctl, data);
+ snd_power_unref(card);
+ if (err < 0)
+ return err;
/* restore info to 32bit */
- err = -EFAULT;
/* id, type, access, count */
if (copy_to_user(&data32->id, &data->id, sizeof(data->id)) ||
copy_to_user(&data32->type, &data->type, 3 * sizeof(u32)))
- goto error;
+ return -EFAULT;
if (put_user(data->owner, &data32->owner))
- goto error;
+ return -EFAULT;
switch (data->type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
case SNDRV_CTL_ELEM_TYPE_INTEGER:
if (put_user(data->value.integer.min, &data32->value.integer.min) ||
put_user(data->value.integer.max, &data32->value.integer.max) ||
put_user(data->value.integer.step, &data32->value.integer.step))
- goto error;
+ return -EFAULT;
break;
case SNDRV_CTL_ELEM_TYPE_INTEGER64:
if (copy_to_user(&data32->value.integer64,
&data->value.integer64,
sizeof(data->value.integer64)))
- goto error;
+ return -EFAULT;
break;
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
if (copy_to_user(&data32->value.enumerated,
&data->value.enumerated,
sizeof(data->value.enumerated)))
- goto error;
+ return -EFAULT;
break;
default:
break;
}
- err = 0;
- error:
- kfree(data);
- return err;
+ return 0;
}
/* read / write */
@@ -168,38 +150,45 @@ struct snd_ctl_elem_value32 {
unsigned char reserved[128];
};
+#ifdef CONFIG_X86_X32_ABI
+/* x32 has a different alignment for 64bit values from ia32 */
+struct snd_ctl_elem_value_x32 {
+ struct snd_ctl_elem_id id;
+ unsigned int indirect; /* bit-field causes misalignment */
+ union {
+ s32 integer[128];
+ unsigned char data[512];
+ s64 integer64[64];
+ } value;
+ unsigned char reserved[128];
+};
+#endif /* CONFIG_X86_X32_ABI */
/* get the value type and count of the control */
static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
int *countp)
{
struct snd_kcontrol *kctl;
- struct snd_ctl_elem_info *info;
+ struct snd_ctl_elem_info *info __free(kfree) = NULL;
int err;
- down_read(&card->controls_rwsem);
+ guard(rwsem_read)(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, id);
- if (! kctl) {
- up_read(&card->controls_rwsem);
- return -ENXIO;
- }
+ if (!kctl)
+ return -ENOENT;
info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (info == NULL) {
- up_read(&card->controls_rwsem);
+ if (info == NULL)
return -ENOMEM;
- }
info->id = *id;
err = kctl->info(kctl, info);
- up_read(&card->controls_rwsem);
if (err >= 0) {
err = info->type;
*countp = info->count;
}
- kfree(info);
return err;
}
-static int get_elem_size(int type, int count)
+static int get_elem_size(snd_ctl_elem_type_t type, int count)
{
switch (type) {
case SNDRV_CTL_ELEM_TYPE_INTEGER64:
@@ -217,11 +206,13 @@ static int get_elem_size(int type, int count)
static int copy_ctl_value_from_user(struct snd_card *card,
struct snd_ctl_elem_value *data,
- struct snd_ctl_elem_value32 __user *data32,
+ void __user *userdata,
+ void __user *valuep,
int *typep, int *countp)
{
+ struct snd_ctl_elem_value32 __user *data32 = userdata;
int i, type, size;
- int uninitialized_var(count);
+ int count;
unsigned int indirect;
if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
@@ -234,22 +225,22 @@ static int copy_ctl_value_from_user(struct snd_card *card,
if (type < 0)
return type;
- if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
- type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
+ if (type == (__force int)SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
+ type == (__force int)SNDRV_CTL_ELEM_TYPE_INTEGER) {
for (i = 0; i < count; i++) {
+ s32 __user *intp = valuep;
int val;
- if (get_user(val, &data32->value.integer[i]))
+ if (get_user(val, &intp[i]))
return -EFAULT;
data->value.integer.value[i] = val;
}
} else {
- size = get_elem_size(type, count);
+ size = get_elem_size((__force snd_ctl_elem_type_t)type, count);
if (size < 0) {
- printk(KERN_ERR "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
+ dev_err(card->dev, "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
return -EINVAL;
}
- if (copy_from_user(data->value.bytes.data,
- data32->value.data, size))
+ if (copy_from_user(data->value.bytes.data, valuep, size))
return -EFAULT;
}
@@ -259,58 +250,71 @@ static int copy_ctl_value_from_user(struct snd_card *card,
}
/* restore the value to 32bit */
-static int copy_ctl_value_to_user(struct snd_ctl_elem_value32 __user *data32,
+static int copy_ctl_value_to_user(void __user *userdata,
+ void __user *valuep,
struct snd_ctl_elem_value *data,
int type, int count)
{
+ struct snd_ctl_elem_value32 __user *data32 = userdata;
int i, size;
- if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
- type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
+ if (type == (__force int)SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
+ type == (__force int)SNDRV_CTL_ELEM_TYPE_INTEGER) {
for (i = 0; i < count; i++) {
+ s32 __user *intp = valuep;
int val;
val = data->value.integer.value[i];
- if (put_user(val, &data32->value.integer[i]))
+ if (put_user(val, &intp[i]))
return -EFAULT;
}
} else {
- size = get_elem_size(type, count);
- if (copy_to_user(data32->value.data,
- data->value.bytes.data, size))
+ size = get_elem_size((__force snd_ctl_elem_type_t)type, count);
+ if (copy_to_user(valuep, data->value.bytes.data, size))
return -EFAULT;
}
+ if (copy_to_user(&data32->id, &data->id, sizeof(data32->id)))
+ return -EFAULT;
return 0;
}
-static int snd_ctl_elem_read_user_compat(struct snd_card *card,
- struct snd_ctl_elem_value32 __user *data32)
+static int __ctl_elem_read_user(struct snd_card *card,
+ void __user *userdata, void __user *valuep)
{
- struct snd_ctl_elem_value *data;
+ struct snd_ctl_elem_value *data __free(kfree) = NULL;
int err, type, count;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
- if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
- goto error;
-
- snd_power_lock(card);
- err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
- if (err >= 0)
- err = snd_ctl_elem_read(card, data);
- snd_power_unlock(card);
- if (err >= 0)
- err = copy_ctl_value_to_user(data32, data, type, count);
- error:
- kfree(data);
+ err = copy_ctl_value_from_user(card, data, userdata, valuep,
+ &type, &count);
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_elem_read(card, data);
+ if (err < 0)
+ return err;
+ return copy_ctl_value_to_user(userdata, valuep, data, type, count);
+}
+
+static int ctl_elem_read_user(struct snd_card *card,
+ void __user *userdata, void __user *valuep)
+{
+ int err;
+
+ err = snd_power_ref_and_wait(card);
+ if (err < 0)
+ return err;
+ err = __ctl_elem_read_user(card, userdata, valuep);
+ snd_power_unref(card);
return err;
}
-static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
- struct snd_ctl_elem_value32 __user *data32)
+static int __ctl_elem_write_user(struct snd_ctl_file *file,
+ void __user *userdata, void __user *valuep)
{
- struct snd_ctl_elem_value *data;
+ struct snd_ctl_elem_value *data __free(kfree) = NULL;
struct snd_card *card = file->card;
int err, type, count;
@@ -318,68 +322,100 @@ static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
if (data == NULL)
return -ENOMEM;
- if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
- goto error;
-
- snd_power_lock(card);
- err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
- if (err >= 0)
- err = snd_ctl_elem_write(card, file, data);
- snd_power_unlock(card);
- if (err >= 0)
- err = copy_ctl_value_to_user(data32, data, type, count);
- error:
- kfree(data);
+ err = copy_ctl_value_from_user(card, data, userdata, valuep,
+ &type, &count);
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_elem_write(card, file, data);
+ if (err < 0)
+ return err;
+ return copy_ctl_value_to_user(userdata, valuep, data, type, count);
+}
+
+static int ctl_elem_write_user(struct snd_ctl_file *file,
+ void __user *userdata, void __user *valuep)
+{
+ struct snd_card *card = file->card;
+ int err;
+
+ err = snd_power_ref_and_wait(card);
+ if (err < 0)
+ return err;
+ err = __ctl_elem_write_user(file, userdata, valuep);
+ snd_power_unref(card);
return err;
}
+static int snd_ctl_elem_read_user_compat(struct snd_card *card,
+ struct snd_ctl_elem_value32 __user *data32)
+{
+ return ctl_elem_read_user(card, data32, &data32->value);
+}
+
+static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
+ struct snd_ctl_elem_value32 __user *data32)
+{
+ return ctl_elem_write_user(file, data32, &data32->value);
+}
+
+#ifdef CONFIG_X86_X32_ABI
+static int snd_ctl_elem_read_user_x32(struct snd_card *card,
+ struct snd_ctl_elem_value_x32 __user *data32)
+{
+ return ctl_elem_read_user(card, data32, &data32->value);
+}
+
+static int snd_ctl_elem_write_user_x32(struct snd_ctl_file *file,
+ struct snd_ctl_elem_value_x32 __user *data32)
+{
+ return ctl_elem_write_user(file, data32, &data32->value);
+}
+#endif /* CONFIG_X86_X32_ABI */
+
/* add or replace a user control */
static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
struct snd_ctl_elem_info32 __user *data32,
int replace)
{
- struct snd_ctl_elem_info *data;
- int err;
+ struct snd_ctl_elem_info *data __free(kfree) = NULL;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (! data)
return -ENOMEM;
- err = -EFAULT;
/* id, type, access, count */ \
if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) ||
copy_from_user(&data->type, &data32->type, 3 * sizeof(u32)))
- goto error;
- if (get_user(data->owner, &data32->owner) ||
- get_user(data->type, &data32->type))
- goto error;
+ return -EFAULT;
+ if (get_user(data->owner, &data32->owner))
+ return -EFAULT;
switch (data->type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
case SNDRV_CTL_ELEM_TYPE_INTEGER:
if (get_user(data->value.integer.min, &data32->value.integer.min) ||
get_user(data->value.integer.max, &data32->value.integer.max) ||
get_user(data->value.integer.step, &data32->value.integer.step))
- goto error;
+ return -EFAULT;
break;
case SNDRV_CTL_ELEM_TYPE_INTEGER64:
if (copy_from_user(&data->value.integer64,
&data32->value.integer64,
sizeof(data->value.integer64)))
- goto error;
+ return -EFAULT;
break;
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
if (copy_from_user(&data->value.enumerated,
&data32->value.enumerated,
sizeof(data->value.enumerated)))
- goto error;
+ return -EFAULT;
+ data->value.enumerated.names_ptr =
+ (uintptr_t)compat_ptr(data->value.enumerated.names_ptr);
break;
default:
break;
}
- err = snd_ctl_elem_add(file, data, replace);
- error:
- kfree(data);
- return err;
+ return snd_ctl_elem_add(file, data, replace);
}
enum {
@@ -389,6 +425,10 @@ enum {
SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct snd_ctl_elem_value32),
SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct snd_ctl_elem_info32),
SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct snd_ctl_elem_info32),
+#ifdef CONFIG_X86_X32_ABI
+ SNDRV_CTL_IOCTL_ELEM_READ_X32 = _IOWR('U', 0x12, struct snd_ctl_elem_value_x32),
+ SNDRV_CTL_IOCTL_ELEM_WRITE_X32 = _IOWR('U', 0x13, struct snd_ctl_elem_value_x32),
+#endif /* CONFIG_X86_X32_ABI */
};
static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
@@ -427,18 +467,21 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns
return snd_ctl_elem_add_compat(ctl, argp, 0);
case SNDRV_CTL_IOCTL_ELEM_REPLACE32:
return snd_ctl_elem_add_compat(ctl, argp, 1);
+#ifdef CONFIG_X86_X32_ABI
+ case SNDRV_CTL_IOCTL_ELEM_READ_X32:
+ return snd_ctl_elem_read_user_x32(ctl->card, argp);
+ case SNDRV_CTL_IOCTL_ELEM_WRITE_X32:
+ return snd_ctl_elem_write_user_x32(ctl, argp);
+#endif /* CONFIG_X86_X32_ABI */
}
- down_read(&snd_ioctl_rwsem);
+ guard(rwsem_read)(&snd_ioctl_rwsem);
list_for_each_entry(p, &snd_control_compat_ioctls, list) {
if (p->fioctl) {
err = p->fioctl(ctl->card, ctl, cmd, arg);
- if (err != -ENOIOCTLCMD) {
- up_read(&snd_ioctl_rwsem);
+ if (err != -ENOIOCTLCMD)
return err;
- }
}
}
- up_read(&snd_ioctl_rwsem);
return -ENOIOCTLCMD;
}
diff --git a/sound/core/control_led.c b/sound/core/control_led.c
new file mode 100644
index 000000000000..e33dfcf863cf
--- /dev/null
+++ b/sound/core/control_led.c
@@ -0,0 +1,789 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * LED state routines for driver control interface
+ * Copyright (c) 2021 by Jaroslav Kysela <perex@perex.cz>
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/leds.h>
+#include <sound/core.h>
+#include <sound/control.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
+MODULE_DESCRIPTION("ALSA control interface to LED trigger code.");
+MODULE_LICENSE("GPL");
+
+#define MAX_LED (((SNDRV_CTL_ELEM_ACCESS_MIC_LED - SNDRV_CTL_ELEM_ACCESS_SPK_LED) \
+ >> SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) + 1)
+
+#define to_led_card_dev(_dev) \
+ container_of(_dev, struct snd_ctl_led_card, dev)
+
+enum snd_ctl_led_mode {
+ MODE_FOLLOW_MUTE = 0,
+ MODE_FOLLOW_ROUTE,
+ MODE_OFF,
+ MODE_ON,
+};
+
+struct snd_ctl_led_card {
+ struct device dev;
+ int number;
+ struct snd_ctl_led *led;
+};
+
+struct snd_ctl_led {
+ struct device dev;
+ struct list_head controls;
+ const char *name;
+ unsigned int group;
+ enum led_audio trigger_type;
+ enum snd_ctl_led_mode mode;
+ struct snd_ctl_led_card *cards[SNDRV_CARDS];
+};
+
+struct snd_ctl_led_ctl {
+ struct list_head list;
+ struct snd_card *card;
+ unsigned int access;
+ struct snd_kcontrol *kctl;
+ unsigned int index_offset;
+};
+
+static DEFINE_MUTEX(snd_ctl_led_mutex);
+static bool snd_ctl_led_card_valid[SNDRV_CARDS];
+static struct led_trigger *snd_ctl_ledtrig_audio[NUM_AUDIO_LEDS];
+static struct snd_ctl_led snd_ctl_leds[MAX_LED] = {
+ {
+ .name = "speaker",
+ .group = (SNDRV_CTL_ELEM_ACCESS_SPK_LED >> SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) - 1,
+ .trigger_type = LED_AUDIO_MUTE,
+ .mode = MODE_FOLLOW_MUTE,
+ },
+ {
+ .name = "mic",
+ .group = (SNDRV_CTL_ELEM_ACCESS_MIC_LED >> SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) - 1,
+ .trigger_type = LED_AUDIO_MICMUTE,
+ .mode = MODE_FOLLOW_MUTE,
+ },
+};
+
+static void snd_ctl_led_sysfs_add(struct snd_card *card);
+static void snd_ctl_led_sysfs_remove(struct snd_card *card);
+
+#define UPDATE_ROUTE(route, cb) \
+ do { \
+ int route2 = (cb); \
+ if (route2 >= 0) \
+ route = route < 0 ? route2 : (route | route2); \
+ } while (0)
+
+static inline unsigned int access_to_group(unsigned int access)
+{
+ return ((access & SNDRV_CTL_ELEM_ACCESS_LED_MASK) >>
+ SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) - 1;
+}
+
+static inline unsigned int group_to_access(unsigned int group)
+{
+ return (group + 1) << SNDRV_CTL_ELEM_ACCESS_LED_SHIFT;
+}
+
+static struct snd_ctl_led *snd_ctl_led_get_by_access(unsigned int access)
+{
+ unsigned int group = access_to_group(access);
+ if (group >= MAX_LED)
+ return NULL;
+ return &snd_ctl_leds[group];
+}
+
+/*
+ * A note for callers:
+ * The two static variables info and value are protected using snd_ctl_led_mutex.
+ */
+static int snd_ctl_led_get(struct snd_ctl_led_ctl *lctl)
+{
+ static struct snd_ctl_elem_info info;
+ static struct snd_ctl_elem_value value;
+ struct snd_kcontrol *kctl = lctl->kctl;
+ unsigned int i;
+ int result;
+
+ memset(&info, 0, sizeof(info));
+ info.id = kctl->id;
+ info.id.index += lctl->index_offset;
+ info.id.numid += lctl->index_offset;
+ result = kctl->info(kctl, &info);
+ if (result < 0)
+ return -1;
+ memset(&value, 0, sizeof(value));
+ value.id = info.id;
+ result = kctl->get(kctl, &value);
+ if (result < 0)
+ return -1;
+ if (info.type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
+ info.type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
+ for (i = 0; i < info.count; i++)
+ if (value.value.integer.value[i] != info.value.integer.min)
+ return 1;
+ } else if (info.type == SNDRV_CTL_ELEM_TYPE_INTEGER64) {
+ for (i = 0; i < info.count; i++)
+ if (value.value.integer64.value[i] != info.value.integer64.min)
+ return 1;
+ }
+ return 0;
+}
+
+static void snd_ctl_led_set_state(struct snd_card *card, unsigned int access,
+ struct snd_kcontrol *kctl, unsigned int ioff)
+{
+ struct snd_ctl_led *led;
+ struct snd_ctl_led_ctl *lctl;
+ int route;
+ bool found;
+
+ led = snd_ctl_led_get_by_access(access);
+ if (!led)
+ return;
+ route = -1;
+ found = false;
+ scoped_guard(mutex, &snd_ctl_led_mutex) {
+ /* the card may not be registered (active) at this point */
+ if (card && !snd_ctl_led_card_valid[card->number])
+ return;
+ list_for_each_entry(lctl, &led->controls, list) {
+ if (lctl->kctl == kctl && lctl->index_offset == ioff)
+ found = true;
+ UPDATE_ROUTE(route, snd_ctl_led_get(lctl));
+ }
+ if (!found && kctl && card) {
+ lctl = kzalloc(sizeof(*lctl), GFP_KERNEL);
+ if (lctl) {
+ lctl->card = card;
+ lctl->access = access;
+ lctl->kctl = kctl;
+ lctl->index_offset = ioff;
+ list_add(&lctl->list, &led->controls);
+ UPDATE_ROUTE(route, snd_ctl_led_get(lctl));
+ }
+ }
+ }
+ switch (led->mode) {
+ case MODE_OFF: route = 1; break;
+ case MODE_ON: route = 0; break;
+ case MODE_FOLLOW_ROUTE: if (route >= 0) route ^= 1; break;
+ case MODE_FOLLOW_MUTE: /* noop */ break;
+ }
+ if (route >= 0) {
+ struct led_trigger *trig = snd_ctl_ledtrig_audio[led->trigger_type];
+
+ led_trigger_event(trig, route ? LED_OFF : LED_ON);
+ }
+}
+
+static struct snd_ctl_led_ctl *snd_ctl_led_find(struct snd_kcontrol *kctl, unsigned int ioff)
+{
+ struct list_head *controls;
+ struct snd_ctl_led_ctl *lctl;
+ unsigned int group;
+
+ for (group = 0; group < MAX_LED; group++) {
+ controls = &snd_ctl_leds[group].controls;
+ list_for_each_entry(lctl, controls, list)
+ if (lctl->kctl == kctl && lctl->index_offset == ioff)
+ return lctl;
+ }
+ return NULL;
+}
+
+static unsigned int snd_ctl_led_remove(struct snd_kcontrol *kctl, unsigned int ioff,
+ unsigned int access)
+{
+ struct snd_ctl_led_ctl *lctl;
+ unsigned int ret = 0;
+
+ guard(mutex)(&snd_ctl_led_mutex);
+ lctl = snd_ctl_led_find(kctl, ioff);
+ if (lctl && (access == 0 || access != lctl->access)) {
+ ret = lctl->access;
+ list_del(&lctl->list);
+ kfree(lctl);
+ }
+ return ret;
+}
+
+static void snd_ctl_led_notify(struct snd_card *card, unsigned int mask,
+ struct snd_kcontrol *kctl, unsigned int ioff)
+{
+ struct snd_kcontrol_volatile *vd;
+ unsigned int access, access2;
+
+ if (mask == SNDRV_CTL_EVENT_MASK_REMOVE) {
+ access = snd_ctl_led_remove(kctl, ioff, 0);
+ if (access)
+ snd_ctl_led_set_state(card, access, NULL, 0);
+ } else if (mask & SNDRV_CTL_EVENT_MASK_INFO) {
+ vd = &kctl->vd[ioff];
+ access = vd->access & SNDRV_CTL_ELEM_ACCESS_LED_MASK;
+ access2 = snd_ctl_led_remove(kctl, ioff, access);
+ if (access2)
+ snd_ctl_led_set_state(card, access2, NULL, 0);
+ if (access)
+ snd_ctl_led_set_state(card, access, kctl, ioff);
+ } else if ((mask & (SNDRV_CTL_EVENT_MASK_ADD |
+ SNDRV_CTL_EVENT_MASK_VALUE)) != 0) {
+ vd = &kctl->vd[ioff];
+ access = vd->access & SNDRV_CTL_ELEM_ACCESS_LED_MASK;
+ if (access)
+ snd_ctl_led_set_state(card, access, kctl, ioff);
+ }
+}
+
+DEFINE_FREE(snd_card_unref, struct snd_card *, if (_T) snd_card_unref(_T))
+
+static int snd_ctl_led_set_id(int card_number, struct snd_ctl_elem_id *id,
+ unsigned int group, bool set)
+{
+ struct snd_card *card __free(snd_card_unref) = NULL;
+ struct snd_kcontrol *kctl;
+ struct snd_kcontrol_volatile *vd;
+ unsigned int ioff, access, new_access;
+
+ card = snd_card_ref(card_number);
+ if (!card)
+ return -ENXIO;
+ guard(rwsem_write)(&card->controls_rwsem);
+ kctl = snd_ctl_find_id(card, id);
+ if (!kctl)
+ return -ENOENT;
+ ioff = snd_ctl_get_ioff(kctl, id);
+ vd = &kctl->vd[ioff];
+ access = vd->access & SNDRV_CTL_ELEM_ACCESS_LED_MASK;
+ if (access != 0 && access != group_to_access(group))
+ return -EXDEV;
+ new_access = vd->access & ~SNDRV_CTL_ELEM_ACCESS_LED_MASK;
+ if (set)
+ new_access |= group_to_access(group);
+ if (new_access != vd->access) {
+ vd->access = new_access;
+ snd_ctl_led_notify(card, SNDRV_CTL_EVENT_MASK_INFO, kctl, ioff);
+ }
+ return 0;
+}
+
+static void snd_ctl_led_refresh(void)
+{
+ unsigned int group;
+
+ for (group = 0; group < MAX_LED; group++)
+ snd_ctl_led_set_state(NULL, group_to_access(group), NULL, 0);
+}
+
+static void snd_ctl_led_ctl_destroy(struct snd_ctl_led_ctl *lctl)
+{
+ list_del(&lctl->list);
+ kfree(lctl);
+}
+
+static void snd_ctl_led_clean(struct snd_card *card)
+{
+ unsigned int group;
+ struct snd_ctl_led_ctl *lctl, *_lctl;
+ struct snd_ctl_led *led;
+
+ for (group = 0; group < MAX_LED; group++) {
+ led = &snd_ctl_leds[group];
+ list_for_each_entry_safe(lctl, _lctl, &led->controls, list)
+ if (!card || lctl->card == card)
+ snd_ctl_led_ctl_destroy(lctl);
+ }
+}
+
+static int snd_ctl_led_reset(int card_number, unsigned int group)
+{
+ struct snd_card *card __free(snd_card_unref) = NULL;
+ struct snd_ctl_led_ctl *lctl, *_lctl;
+ struct snd_ctl_led *led;
+ struct snd_kcontrol_volatile *vd;
+ bool change = false;
+
+ card = snd_card_ref(card_number);
+ if (!card)
+ return -ENXIO;
+
+ scoped_guard(mutex, &snd_ctl_led_mutex) {
+ if (!snd_ctl_led_card_valid[card_number])
+ return -ENXIO;
+ led = &snd_ctl_leds[group];
+ list_for_each_entry_safe(lctl, _lctl, &led->controls, list)
+ if (lctl->card == card) {
+ vd = &lctl->kctl->vd[lctl->index_offset];
+ vd->access &= ~group_to_access(group);
+ snd_ctl_led_ctl_destroy(lctl);
+ change = true;
+ }
+ }
+ if (change)
+ snd_ctl_led_set_state(NULL, group_to_access(group), NULL, 0);
+ return 0;
+}
+
+static void snd_ctl_led_register(struct snd_card *card)
+{
+ struct snd_kcontrol *kctl;
+ unsigned int ioff;
+
+ if (snd_BUG_ON(card->number < 0 ||
+ card->number >= ARRAY_SIZE(snd_ctl_led_card_valid)))
+ return;
+ scoped_guard(mutex, &snd_ctl_led_mutex)
+ snd_ctl_led_card_valid[card->number] = true;
+ /* the register callback is already called with held card->controls_rwsem */
+ list_for_each_entry(kctl, &card->controls, list)
+ for (ioff = 0; ioff < kctl->count; ioff++)
+ snd_ctl_led_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, kctl, ioff);
+ snd_ctl_led_refresh();
+ snd_ctl_led_sysfs_add(card);
+}
+
+static void snd_ctl_led_disconnect(struct snd_card *card)
+{
+ snd_ctl_led_sysfs_remove(card);
+ scoped_guard(mutex, &snd_ctl_led_mutex) {
+ snd_ctl_led_card_valid[card->number] = false;
+ snd_ctl_led_clean(card);
+ }
+ snd_ctl_led_refresh();
+}
+
+static void snd_ctl_led_card_release(struct device *dev)
+{
+ struct snd_ctl_led_card *led_card = to_led_card_dev(dev);
+
+ kfree(led_card);
+}
+
+static void snd_ctl_led_release(struct device *dev)
+{
+}
+
+static void snd_ctl_led_dev_release(struct device *dev)
+{
+}
+
+/*
+ * sysfs
+ */
+
+static ssize_t mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev);
+ const char *str = NULL;
+
+ switch (led->mode) {
+ case MODE_FOLLOW_MUTE: str = "follow-mute"; break;
+ case MODE_FOLLOW_ROUTE: str = "follow-route"; break;
+ case MODE_ON: str = "on"; break;
+ case MODE_OFF: str = "off"; break;
+ }
+ return sysfs_emit(buf, "%s\n", str);
+}
+
+static ssize_t mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev);
+ char _buf[16];
+ size_t l = min(count, sizeof(_buf) - 1);
+ enum snd_ctl_led_mode mode;
+
+ memcpy(_buf, buf, l);
+ _buf[l] = '\0';
+ if (strstr(_buf, "mute"))
+ mode = MODE_FOLLOW_MUTE;
+ else if (strstr(_buf, "route"))
+ mode = MODE_FOLLOW_ROUTE;
+ else if (strncmp(_buf, "off", 3) == 0 || strncmp(_buf, "0", 1) == 0)
+ mode = MODE_OFF;
+ else if (strncmp(_buf, "on", 2) == 0 || strncmp(_buf, "1", 1) == 0)
+ mode = MODE_ON;
+ else
+ return count;
+
+ scoped_guard(mutex, &snd_ctl_led_mutex)
+ led->mode = mode;
+
+ snd_ctl_led_set_state(NULL, group_to_access(led->group), NULL, 0);
+ return count;
+}
+
+static ssize_t brightness_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev);
+ struct led_trigger *trig = snd_ctl_ledtrig_audio[led->trigger_type];
+
+ return sysfs_emit(buf, "%u\n", led_trigger_get_brightness(trig));
+}
+
+static DEVICE_ATTR_RW(mode);
+static DEVICE_ATTR_RO(brightness);
+
+static struct attribute *snd_ctl_led_dev_attrs[] = {
+ &dev_attr_mode.attr,
+ &dev_attr_brightness.attr,
+ NULL,
+};
+
+static const struct attribute_group snd_ctl_led_dev_attr_group = {
+ .attrs = snd_ctl_led_dev_attrs,
+};
+
+static const struct attribute_group *snd_ctl_led_dev_attr_groups[] = {
+ &snd_ctl_led_dev_attr_group,
+ NULL,
+};
+
+static char *find_eos(char *s)
+{
+ while (*s && *s != ',')
+ s++;
+ if (*s)
+ s++;
+ return s;
+}
+
+static char *parse_uint(char *s, unsigned int *val)
+{
+ unsigned long long res;
+ if (kstrtoull(s, 10, &res))
+ res = 0;
+ *val = res;
+ return find_eos(s);
+}
+
+static char *parse_string(char *s, char *val, size_t val_size)
+{
+ if (*s == '"' || *s == '\'') {
+ char c = *s;
+ s++;
+ while (*s && *s != c) {
+ if (val_size > 1) {
+ *val++ = *s;
+ val_size--;
+ }
+ s++;
+ }
+ } else {
+ while (*s && *s != ',') {
+ if (val_size > 1) {
+ *val++ = *s;
+ val_size--;
+ }
+ s++;
+ }
+ }
+ *val = '\0';
+ if (*s)
+ s++;
+ return s;
+}
+
+static char *parse_iface(char *s, snd_ctl_elem_iface_t *val)
+{
+ if (!strncasecmp(s, "card", 4))
+ *val = SNDRV_CTL_ELEM_IFACE_CARD;
+ else if (!strncasecmp(s, "mixer", 5))
+ *val = SNDRV_CTL_ELEM_IFACE_MIXER;
+ return find_eos(s);
+}
+
+/*
+ * These types of input strings are accepted:
+ *
+ * unsigned integer - numid (equivaled to numid=UINT)
+ * string - basic mixer name (equivalent to iface=MIXER,name=STR)
+ * numid=UINT
+ * [iface=MIXER,][device=UINT,][subdevice=UINT,]name=STR[,index=UINT]
+ */
+static ssize_t set_led_id(struct snd_ctl_led_card *led_card, const char *buf, size_t count,
+ bool attach)
+{
+ char buf2[256], *s, *os;
+ struct snd_ctl_elem_id id;
+ int err;
+
+ if (strscpy(buf2, buf, sizeof(buf2)) < 0)
+ return -E2BIG;
+ memset(&id, 0, sizeof(id));
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ s = buf2;
+ while (*s) {
+ os = s;
+ if (!strncasecmp(s, "numid=", 6)) {
+ s = parse_uint(s + 6, &id.numid);
+ } else if (!strncasecmp(s, "iface=", 6)) {
+ s = parse_iface(s + 6, &id.iface);
+ } else if (!strncasecmp(s, "device=", 7)) {
+ s = parse_uint(s + 7, &id.device);
+ } else if (!strncasecmp(s, "subdevice=", 10)) {
+ s = parse_uint(s + 10, &id.subdevice);
+ } else if (!strncasecmp(s, "name=", 5)) {
+ s = parse_string(s + 5, id.name, sizeof(id.name));
+ } else if (!strncasecmp(s, "index=", 6)) {
+ s = parse_uint(s + 6, &id.index);
+ } else if (s == buf2) {
+ while (*s) {
+ if (*s < '0' || *s > '9')
+ break;
+ s++;
+ }
+ if (*s == '\0')
+ parse_uint(buf2, &id.numid);
+ else {
+ for (; *s >= ' '; s++);
+ *s = '\0';
+ strscpy(id.name, buf2, sizeof(id.name));
+ }
+ break;
+ }
+ if (*s == ',')
+ s++;
+ if (s == os)
+ break;
+ }
+
+ err = snd_ctl_led_set_id(led_card->number, &id, led_card->led->group, attach);
+ if (err < 0)
+ return err;
+
+ return count;
+}
+
+static ssize_t attach_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
+ return set_led_id(led_card, buf, count, true);
+}
+
+static ssize_t detach_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
+ return set_led_id(led_card, buf, count, false);
+}
+
+static ssize_t reset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
+ int err;
+
+ if (count > 0 && buf[0] == '1') {
+ err = snd_ctl_led_reset(led_card->number, led_card->led->group);
+ if (err < 0)
+ return err;
+ }
+ return count;
+}
+
+static ssize_t list_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
+ struct snd_card *card __free(snd_card_unref) = NULL;
+ struct snd_ctl_led_ctl *lctl;
+ size_t l = 0;
+
+ card = snd_card_ref(led_card->number);
+ if (!card)
+ return -ENXIO;
+ guard(rwsem_read)(&card->controls_rwsem);
+ guard(mutex)(&snd_ctl_led_mutex);
+ if (snd_ctl_led_card_valid[led_card->number]) {
+ list_for_each_entry(lctl, &led_card->led->controls, list) {
+ if (lctl->card != card)
+ continue;
+ if (l)
+ l += sysfs_emit_at(buf, l, " ");
+ l += sysfs_emit_at(buf, l, "%u",
+ lctl->kctl->id.numid + lctl->index_offset);
+ }
+ }
+ return l;
+}
+
+static DEVICE_ATTR_WO(attach);
+static DEVICE_ATTR_WO(detach);
+static DEVICE_ATTR_WO(reset);
+static DEVICE_ATTR_RO(list);
+
+static struct attribute *snd_ctl_led_card_attrs[] = {
+ &dev_attr_attach.attr,
+ &dev_attr_detach.attr,
+ &dev_attr_reset.attr,
+ &dev_attr_list.attr,
+ NULL,
+};
+
+static const struct attribute_group snd_ctl_led_card_attr_group = {
+ .attrs = snd_ctl_led_card_attrs,
+};
+
+static const struct attribute_group *snd_ctl_led_card_attr_groups[] = {
+ &snd_ctl_led_card_attr_group,
+ NULL,
+};
+
+static struct device snd_ctl_led_dev;
+
+static void snd_ctl_led_sysfs_add(struct snd_card *card)
+{
+ unsigned int group;
+ struct snd_ctl_led_card *led_card;
+ struct snd_ctl_led *led;
+ char link_name[32];
+
+ for (group = 0; group < MAX_LED; group++) {
+ led = &snd_ctl_leds[group];
+ led_card = kzalloc(sizeof(*led_card), GFP_KERNEL);
+ if (!led_card)
+ goto cerr2;
+ led_card->number = card->number;
+ led_card->led = led;
+ device_initialize(&led_card->dev);
+ led_card->dev.release = snd_ctl_led_card_release;
+ if (dev_set_name(&led_card->dev, "card%d", card->number) < 0)
+ goto cerr;
+ led_card->dev.parent = &led->dev;
+ led_card->dev.groups = snd_ctl_led_card_attr_groups;
+ if (device_add(&led_card->dev))
+ goto cerr;
+ led->cards[card->number] = led_card;
+ snprintf(link_name, sizeof(link_name), "led-%s", led->name);
+ if (sysfs_create_link(&card->ctl_dev->kobj, &led_card->dev.kobj,
+ link_name))
+ dev_err(card->dev,
+ "%s: can't create symlink to controlC%i device\n",
+ __func__, card->number);
+ if (sysfs_create_link(&led_card->dev.kobj, &card->card_dev.kobj,
+ "card"))
+ dev_err(card->dev,
+ "%s: can't create symlink to card%i\n",
+ __func__, card->number);
+
+ continue;
+cerr:
+ put_device(&led_card->dev);
+cerr2:
+ dev_err(card->dev, "snd_ctl_led: unable to add card%d", card->number);
+ }
+}
+
+static void snd_ctl_led_sysfs_remove(struct snd_card *card)
+{
+ unsigned int group;
+ struct snd_ctl_led_card *led_card;
+ struct snd_ctl_led *led;
+ char link_name[32];
+
+ for (group = 0; group < MAX_LED; group++) {
+ led = &snd_ctl_leds[group];
+ led_card = led->cards[card->number];
+ if (!led_card)
+ continue;
+ snprintf(link_name, sizeof(link_name), "led-%s", led->name);
+ sysfs_remove_link(&card->ctl_dev->kobj, link_name);
+ sysfs_remove_link(&led_card->dev.kobj, "card");
+ device_unregister(&led_card->dev);
+ led->cards[card->number] = NULL;
+ }
+}
+
+/*
+ * Control layer registration
+ */
+static struct snd_ctl_layer_ops snd_ctl_led_lops = {
+ .module_name = SND_CTL_LAYER_MODULE_LED,
+ .lregister = snd_ctl_led_register,
+ .ldisconnect = snd_ctl_led_disconnect,
+ .lnotify = snd_ctl_led_notify,
+};
+
+static int __init snd_ctl_led_init(void)
+{
+ struct snd_ctl_led *led;
+ unsigned int group;
+
+ led_trigger_register_simple("audio-mute", &snd_ctl_ledtrig_audio[LED_AUDIO_MUTE]);
+ led_trigger_register_simple("audio-micmute", &snd_ctl_ledtrig_audio[LED_AUDIO_MICMUTE]);
+
+ device_initialize(&snd_ctl_led_dev);
+ snd_ctl_led_dev.class = &sound_class;
+ snd_ctl_led_dev.release = snd_ctl_led_dev_release;
+ dev_set_name(&snd_ctl_led_dev, "ctl-led");
+ if (device_add(&snd_ctl_led_dev)) {
+ put_device(&snd_ctl_led_dev);
+ return -ENOMEM;
+ }
+ for (group = 0; group < MAX_LED; group++) {
+ led = &snd_ctl_leds[group];
+ INIT_LIST_HEAD(&led->controls);
+ device_initialize(&led->dev);
+ led->dev.parent = &snd_ctl_led_dev;
+ led->dev.release = snd_ctl_led_release;
+ led->dev.groups = snd_ctl_led_dev_attr_groups;
+ dev_set_name(&led->dev, led->name);
+ if (device_add(&led->dev)) {
+ put_device(&led->dev);
+ for (; group > 0; group--) {
+ led = &snd_ctl_leds[group - 1];
+ device_unregister(&led->dev);
+ }
+ device_unregister(&snd_ctl_led_dev);
+ return -ENOMEM;
+ }
+ }
+ snd_ctl_register_layer(&snd_ctl_led_lops);
+ return 0;
+}
+
+static void __exit snd_ctl_led_exit(void)
+{
+ struct snd_ctl_led *led;
+ struct snd_card *card;
+ unsigned int group, card_number;
+
+ snd_ctl_disconnect_layer(&snd_ctl_led_lops);
+ for (card_number = 0; card_number < SNDRV_CARDS; card_number++) {
+ if (!snd_ctl_led_card_valid[card_number])
+ continue;
+ card = snd_card_ref(card_number);
+ if (card) {
+ snd_ctl_led_sysfs_remove(card);
+ snd_card_unref(card);
+ }
+ }
+ for (group = 0; group < MAX_LED; group++) {
+ led = &snd_ctl_leds[group];
+ device_unregister(&led->dev);
+ }
+ device_unregister(&snd_ctl_led_dev);
+ snd_ctl_led_clean(NULL);
+
+ led_trigger_unregister_simple(snd_ctl_ledtrig_audio[LED_AUDIO_MUTE]);
+ led_trigger_unregister_simple(snd_ctl_ledtrig_audio[LED_AUDIO_MICMUTE]);
+}
+
+module_init(snd_ctl_led_init)
+module_exit(snd_ctl_led_exit)
+
+MODULE_ALIAS("ledtrig:audio-mute");
+MODULE_ALIAS("ledtrig:audio-micmute");
diff --git a/sound/core/ctljack.c b/sound/core/ctljack.c
new file mode 100644
index 000000000000..709b1a9c2caa
--- /dev/null
+++ b/sound/core/ctljack.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Helper functions for jack-detection kcontrols
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include <sound/control.h>
+
+#define jack_detect_kctl_info snd_ctl_boolean_mono_info
+
+static int jack_detect_kctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = kcontrol->private_value;
+ return 0;
+}
+
+static const struct snd_kcontrol_new jack_detect_kctl = {
+ /* name is filled later */
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = jack_detect_kctl_info,
+ .get = jack_detect_kctl_get,
+};
+
+static int get_available_index(struct snd_card *card, const char *name)
+{
+ struct snd_ctl_elem_id sid;
+
+ memset(&sid, 0, sizeof(sid));
+
+ sid.index = 0;
+ sid.iface = SNDRV_CTL_ELEM_IFACE_CARD;
+ strscpy(sid.name, name, sizeof(sid.name));
+
+ while (snd_ctl_find_id(card, &sid)) {
+ sid.index++;
+ /* reset numid; otherwise snd_ctl_find_id() hits this again */
+ sid.numid = 0;
+ }
+
+ return sid.index;
+}
+
+static void jack_kctl_name_gen(char *name, const char *src_name, int size)
+{
+ size_t count = strlen(src_name);
+ bool need_cat = true;
+
+ /* remove redundant " Jack" from src_name */
+ if (count >= 5)
+ need_cat = strncmp(&src_name[count - 5], " Jack", 5) ? true : false;
+
+ snprintf(name, size, need_cat ? "%s Jack" : "%s", src_name);
+
+}
+
+struct snd_kcontrol *
+snd_kctl_jack_new(const char *name, struct snd_card *card)
+{
+ struct snd_kcontrol *kctl;
+
+ kctl = snd_ctl_new1(&jack_detect_kctl, NULL);
+ if (!kctl)
+ return NULL;
+
+ jack_kctl_name_gen(kctl->id.name, name, sizeof(kctl->id.name));
+ kctl->id.index = get_available_index(card, kctl->id.name);
+ kctl->private_value = 0;
+ return kctl;
+}
+
+void snd_kctl_jack_report(struct snd_card *card,
+ struct snd_kcontrol *kctl, bool status)
+{
+ if (kctl->private_value == status)
+ return;
+ kctl->private_value = status;
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+}
diff --git a/sound/core/device.c b/sound/core/device.c
index a67dfac08c03..cdc5af526739 100644
--- a/sound/core/device.c
+++ b/sound/core/device.c
@@ -1,26 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Device management routines
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
#include <linux/time.h>
+#include <linux/export.h>
#include <linux/errno.h>
#include <sound/core.h>
@@ -38,72 +24,71 @@
* The data pointer plays a role as the identifier, too, so the
* pointer address must be unique and unchanged.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
-int snd_device_new(struct snd_card *card, snd_device_type_t type,
- void *device_data, struct snd_device_ops *ops)
+int snd_device_new(struct snd_card *card, enum snd_device_type type,
+ void *device_data, const struct snd_device_ops *ops)
{
struct snd_device *dev;
+ struct list_head *p;
if (snd_BUG_ON(!card || !device_data || !ops))
return -ENXIO;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL) {
- snd_printk(KERN_ERR "Cannot allocate device\n");
+ if (!dev)
return -ENOMEM;
- }
+ INIT_LIST_HEAD(&dev->list);
dev->card = card;
dev->type = type;
dev->state = SNDRV_DEV_BUILD;
dev->device_data = device_data;
dev->ops = ops;
- list_add(&dev->list, &card->devices); /* add to the head of list */
+
+ /* insert the entry in an incrementally sorted list */
+ list_for_each_prev(p, &card->devices) {
+ struct snd_device *pdev = list_entry(p, struct snd_device, list);
+ if ((unsigned int)pdev->type <= (unsigned int)type)
+ break;
+ }
+
+ list_add(&dev->list, p);
return 0;
}
-
EXPORT_SYMBOL(snd_device_new);
-/**
- * snd_device_free - release the device from the card
- * @card: the card instance
- * @device_data: the data pointer to release
- *
- * Removes the device from the list on the card and invokes the
- * callbacks, dev_disconnect and dev_free, corresponding to the state.
- * Then release the device.
- *
- * Returns zero if successful, or a negative error code on failure or if the
- * device not found.
- */
-int snd_device_free(struct snd_card *card, void *device_data)
+static void __snd_device_disconnect(struct snd_device *dev)
{
- struct snd_device *dev;
-
- if (snd_BUG_ON(!card || !device_data))
- return -ENXIO;
- list_for_each_entry(dev, &card->devices, list) {
- if (dev->device_data != device_data)
- continue;
- /* unlink */
- list_del(&dev->list);
- if (dev->state == SNDRV_DEV_REGISTERED &&
- dev->ops->dev_disconnect)
- if (dev->ops->dev_disconnect(dev))
- snd_printk(KERN_ERR
- "device disconnect failure\n");
- if (dev->ops->dev_free) {
- if (dev->ops->dev_free(dev))
- snd_printk(KERN_ERR "device free failure\n");
- }
- kfree(dev);
- return 0;
+ if (dev->state == SNDRV_DEV_REGISTERED) {
+ if (dev->ops->dev_disconnect &&
+ dev->ops->dev_disconnect(dev))
+ dev_err(dev->card->dev, "device disconnect failure\n");
+ dev->state = SNDRV_DEV_DISCONNECTED;
}
- snd_printd("device free %p (from %pF), not found\n", device_data,
- __builtin_return_address(0));
- return -ENXIO;
}
-EXPORT_SYMBOL(snd_device_free);
+static void __snd_device_free(struct snd_device *dev)
+{
+ /* unlink */
+ list_del(&dev->list);
+
+ __snd_device_disconnect(dev);
+ if (dev->ops->dev_free) {
+ if (dev->ops->dev_free(dev))
+ dev_err(dev->card->dev, "device free failure\n");
+ }
+ kfree(dev);
+}
+
+static struct snd_device *look_for_dev(struct snd_card *card, void *device_data)
+{
+ struct snd_device *dev;
+
+ list_for_each_entry(dev, &card->devices, list)
+ if (dev->device_data == device_data)
+ return dev;
+
+ return NULL;
+}
/**
* snd_device_disconnect - disconnect the device
@@ -115,29 +100,59 @@ EXPORT_SYMBOL(snd_device_free);
*
* Usually called from snd_card_disconnect().
*
- * Returns zero if successful, or a negative error code on failure or if the
+ * Return: Zero if successful, or a negative error code on failure or if the
* device not found.
*/
-int snd_device_disconnect(struct snd_card *card, void *device_data)
+void snd_device_disconnect(struct snd_card *card, void *device_data)
{
struct snd_device *dev;
if (snd_BUG_ON(!card || !device_data))
- return -ENXIO;
- list_for_each_entry(dev, &card->devices, list) {
- if (dev->device_data != device_data)
- continue;
- if (dev->state == SNDRV_DEV_REGISTERED &&
- dev->ops->dev_disconnect) {
- if (dev->ops->dev_disconnect(dev))
- snd_printk(KERN_ERR "device disconnect failure\n");
- dev->state = SNDRV_DEV_DISCONNECTED;
+ return;
+ dev = look_for_dev(card, device_data);
+ if (dev)
+ __snd_device_disconnect(dev);
+ else
+ dev_dbg(card->dev, "device disconnect %p (from %pS), 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
+ * @device_data: the data pointer to release
+ *
+ * Removes the device from the list on the card and invokes the
+ * callbacks, dev_disconnect and dev_free, corresponding to the state.
+ * Then release the device.
+ */
+void snd_device_free(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_free(dev);
+ else
+ dev_dbg(card->dev, "device free %p (from %pS), not found\n",
+ device_data, __builtin_return_address(0));
+}
+EXPORT_SYMBOL(snd_device_free);
+
+static int __snd_device_register(struct snd_device *dev)
+{
+ if (dev->state == SNDRV_DEV_BUILD) {
+ if (dev->ops->dev_register) {
+ int err = dev->ops->dev_register(dev);
+ if (err < 0)
+ return err;
}
- return 0;
+ dev->state = SNDRV_DEV_REGISTERED;
}
- snd_printd("device disconnect %p (from %pF), not found\n", device_data,
- __builtin_return_address(0));
- return -ENXIO;
+ return 0;
}
/**
@@ -150,32 +165,21 @@ int snd_device_disconnect(struct snd_card *card, void *device_data)
* but it can be called later if any new devices are created after
* invocation of snd_card_register().
*
- * Returns zero if successful, or a negative error code on failure or if the
+ * Return: Zero if successful, or a negative error code on failure or if the
* device not found.
*/
int snd_device_register(struct snd_card *card, void *device_data)
{
struct snd_device *dev;
- int err;
if (snd_BUG_ON(!card || !device_data))
return -ENXIO;
- list_for_each_entry(dev, &card->devices, list) {
- if (dev->device_data != device_data)
- continue;
- if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
- if ((err = dev->ops->dev_register(dev)) < 0)
- return err;
- dev->state = SNDRV_DEV_REGISTERED;
- return 0;
- }
- snd_printd("snd_device_register busy\n");
- return -EBUSY;
- }
+ dev = look_for_dev(card, device_data);
+ if (dev)
+ return __snd_device_register(dev);
snd_BUG();
return -ENXIO;
}
-
EXPORT_SYMBOL(snd_device_register);
/*
@@ -190,11 +194,9 @@ int snd_device_register_all(struct snd_card *card)
if (snd_BUG_ON(!card))
return -ENXIO;
list_for_each_entry(dev, &card->devices, list) {
- if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
- if ((err = dev->ops->dev_register(dev)) < 0)
- return err;
- dev->state = SNDRV_DEV_REGISTERED;
- }
+ err = __snd_device_register(dev);
+ if (err < 0)
+ return err;
}
return 0;
}
@@ -203,41 +205,35 @@ 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(dev, &card->devices, list) {
- if (snd_device_disconnect(card, dev->device_data) < 0)
- err = -ENXIO;
- }
- return err;
+ return;
+ list_for_each_entry_reverse(dev, &card->devices, list)
+ __snd_device_disconnect(dev);
}
/*
* release all the devices on the card.
* called from init.c
*/
-int snd_device_free_all(struct snd_card *card, snd_device_cmd_t cmd)
+void snd_device_free_all(struct snd_card *card)
{
- struct snd_device *dev;
- int err;
- unsigned int range_low, range_high;
+ struct snd_device *dev, *next;
if (snd_BUG_ON(!card))
- return -ENXIO;
- range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE;
- range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1;
- __again:
- list_for_each_entry(dev, &card->devices, list) {
- if (dev->type >= range_low && dev->type <= range_high) {
- if ((err = snd_device_free(card, dev->device_data)) < 0)
- return err;
- goto __again;
- }
+ return;
+ list_for_each_entry_safe_reverse(dev, next, &card->devices, list) {
+ /* exception: free ctl and lowlevel stuff later */
+ if (dev->type == SNDRV_DEV_CONTROL ||
+ dev->type == SNDRV_DEV_LOWLEVEL)
+ continue;
+ __snd_device_free(dev);
}
- return 0;
+
+ /* free all */
+ list_for_each_entry_safe_reverse(dev, next, &card->devices, list)
+ __snd_device_free(dev);
}
diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c
index 7730575bfadd..2d5f4d47071f 100644
--- a/sound/core/hrtimer.c
+++ b/sound/core/hrtimer.c
@@ -1,25 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA timer back-end using hrtimer
* Copyright (C) 2008 Takashi Iwai
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/hrtimer.h>
@@ -38,36 +25,50 @@ static unsigned int resolution;
struct snd_hrtimer {
struct snd_timer *timer;
struct hrtimer hrt;
- atomic_t running;
+ bool in_callback;
};
static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
{
struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt);
struct snd_timer *t = stime->timer;
+ ktime_t delta;
+ unsigned long ticks;
+ enum hrtimer_restart ret = HRTIMER_NORESTART;
+
+ scoped_guard(spinlock, &t->lock) {
+ if (!t->running)
+ return HRTIMER_NORESTART; /* fast path */
+ stime->in_callback = true;
+ ticks = t->sticks;
+ }
+
+ /* calculate the drift */
+ delta = ktime_sub(hrtimer_cb_get_time(hrt), hrtimer_get_expires(hrt));
+ if (delta > 0)
+ ticks += ktime_divns(delta, ticks * resolution);
- if (!atomic_read(&stime->running))
- return HRTIMER_NORESTART;
+ snd_timer_interrupt(stime->timer, ticks);
- hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution));
- snd_timer_interrupt(stime->timer, t->sticks);
+ guard(spinlock)(&t->lock);
+ if (t->running) {
+ hrtimer_add_expires_ns(hrt, t->sticks * resolution);
+ ret = HRTIMER_RESTART;
+ }
- if (!atomic_read(&stime->running))
- return HRTIMER_NORESTART;
- return HRTIMER_RESTART;
+ stime->in_callback = false;
+ return ret;
}
static int snd_hrtimer_open(struct snd_timer *t)
{
struct snd_hrtimer *stime;
- stime = kmalloc(sizeof(*stime), GFP_KERNEL);
+ stime = kzalloc(sizeof(*stime), GFP_KERNEL);
if (!stime)
return -ENOMEM;
- hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
stime->timer = t;
- stime->hrt.function = snd_hrtimer_callback;
- atomic_set(&stime->running, 0);
+ hrtimer_setup(&stime->hrt, snd_hrtimer_callback, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
t->private_data = stime;
return 0;
}
@@ -77,6 +78,11 @@ static int snd_hrtimer_close(struct snd_timer *t)
struct snd_hrtimer *stime = t->private_data;
if (stime) {
+ scoped_guard(spinlock_irq, &t->lock) {
+ t->running = 0; /* just to be sure */
+ stime->in_callback = 1; /* skip start/stop */
+ }
+
hrtimer_cancel(&stime->hrt);
kfree(stime);
t->private_data = NULL;
@@ -88,23 +94,25 @@ static int snd_hrtimer_start(struct snd_timer *t)
{
struct snd_hrtimer *stime = t->private_data;
- atomic_set(&stime->running, 0);
- hrtimer_cancel(&stime->hrt);
+ if (stime->in_callback)
+ return 0;
hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution),
HRTIMER_MODE_REL);
- atomic_set(&stime->running, 1);
return 0;
}
static int snd_hrtimer_stop(struct snd_timer *t)
{
struct snd_hrtimer *stime = t->private_data;
- atomic_set(&stime->running, 0);
+
+ if (stime->in_callback)
+ return 0;
+ hrtimer_try_to_cancel(&stime->hrt);
return 0;
}
-static struct snd_timer_hardware hrtimer_hw = {
- .flags = SNDRV_TIMER_HW_AUTO,
+static const struct snd_timer_hardware hrtimer_hw __initconst = {
+ .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_WORK,
.open = snd_hrtimer_open,
.close = snd_hrtimer_close,
.start = snd_hrtimer_start,
@@ -120,17 +128,9 @@ static struct snd_timer *mytimer;
static int __init snd_hrtimer_init(void)
{
struct snd_timer *timer;
- struct timespec tp;
int err;
- hrtimer_get_res(CLOCK_MONOTONIC, &tp);
- if (tp.tv_sec > 0 || !tp.tv_nsec) {
- snd_printk(KERN_ERR
- "snd-hrtimer: Invalid resolution %u.%09u",
- (unsigned)tp.tv_sec, (unsigned)tp.tv_nsec);
- return -EINVAL;
- }
- resolution = tp.tv_nsec;
+ resolution = hrtimer_resolution;
/* Create a new timer and set up the fields */
err = snd_timer_global_new("hrtimer", SNDRV_TIMER_GLOBAL_HRTIMER,
@@ -139,10 +139,11 @@ static int __init snd_hrtimer_init(void)
return err;
timer->module = THIS_MODULE;
- strcpy(timer->name, "HR timer");
+ strscpy(timer->name, "HR timer");
timer->hw = hrtimer_hw;
timer->hw.resolution = resolution;
timer->hw.ticks = NANO_SEC / resolution;
+ timer->max_instances = 100; /* lower the limit */
err = snd_timer_global_register(timer);
if (err < 0) {
diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c
index a70ee7f1ed98..09200df2932c 100644
--- a/sound/core/hwdep.c
+++ b/sound/core/hwdep.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Hardware dependent layer
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/major.h>
@@ -24,6 +9,8 @@
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/sched/signal.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/minors.h>
@@ -37,7 +24,6 @@ MODULE_LICENSE("GPL");
static LIST_HEAD(snd_hwdep_devices);
static DEFINE_MUTEX(register_mutex);
-static int snd_hwdep_free(struct snd_hwdep *hwdep);
static int snd_hwdep_dev_free(struct snd_device *device);
static int snd_hwdep_dev_register(struct snd_device *device);
static int snd_hwdep_dev_disconnect(struct snd_device *device);
@@ -84,7 +70,7 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
int major = imajor(inode);
struct snd_hwdep *hw;
int err;
- wait_queue_t wait;
+ wait_queue_entry_t wait;
if (major == snd_major) {
hw = snd_lookup_minor_data(iminor(inode),
@@ -99,8 +85,10 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
if (hw == NULL)
return -ENODEV;
- if (!try_module_get(hw->card->module))
+ if (!try_module_get(hw->card->module)) {
+ snd_card_unref(hw->card);
return -EFAULT;
+ }
init_waitqueue_entry(&wait, current);
add_wait_queue(&hw->open_wait, &wait);
@@ -128,6 +116,10 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
mutex_unlock(&hw->open_mutex);
schedule();
mutex_lock(&hw->open_mutex);
+ if (hw->card->shutdown) {
+ err = -ENODEV;
+ break;
+ }
if (signal_pending(current)) {
err = -ERESTARTSYS;
break;
@@ -147,6 +139,7 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
mutex_unlock(&hw->open_mutex);
if (err < 0)
module_put(hw->card->module);
+ snd_card_unref(hw->card);
return err;
}
@@ -156,12 +149,12 @@ static int snd_hwdep_release(struct inode *inode, struct file * file)
struct snd_hwdep *hw = file->private_data;
struct module *mod = hw->card->module;
- mutex_lock(&hw->open_mutex);
- if (hw->ops.release)
- err = hw->ops.release(hw, file);
- if (hw->used > 0)
- hw->used--;
- mutex_unlock(&hw->open_mutex);
+ scoped_guard(mutex, &hw->open_mutex) {
+ if (hw->ops.release)
+ err = hw->ops.release(hw, file);
+ if (hw->used > 0)
+ hw->used--;
+ }
wake_up(&hw->open_wait);
snd_card_file_remove(hw->card, file);
@@ -169,7 +162,7 @@ static int snd_hwdep_release(struct inode *inode, struct file * file)
return err;
}
-static unsigned int snd_hwdep_poll(struct file * file, poll_table * wait)
+static __poll_t snd_hwdep_poll(struct file * file, poll_table * wait)
{
struct snd_hwdep *hw = file->private_data;
if (hw->ops.poll)
@@ -184,8 +177,8 @@ static int snd_hwdep_info(struct snd_hwdep *hw,
memset(&info, 0, sizeof(info));
info.card = hw->card->number;
- strlcpy(info.id, hw->id, sizeof(info.id));
- strlcpy(info.name, hw->name, sizeof(info.name));
+ strscpy(info.id, hw->id, sizeof(info.id));
+ strscpy(info.name, hw->name, sizeof(info.name));
info.iface = hw->iface;
if (copy_to_user(_info, &info, sizeof(info)))
return -EFAULT;
@@ -202,7 +195,8 @@ static int snd_hwdep_dsp_status(struct snd_hwdep *hw,
return -ENXIO;
memset(&info, 0, sizeof(info));
info.dsp_loaded = hw->dsp_loaded;
- if ((err = hw->ops.dsp_status(hw, &info)) < 0)
+ err = hw->ops.dsp_status(hw, &info);
+ if (err < 0)
return err;
if (copy_to_user(_info, &info, sizeof(info)))
return -EFAULT;
@@ -210,28 +204,35 @@ static int snd_hwdep_dsp_status(struct snd_hwdep *hw,
}
static int snd_hwdep_dsp_load(struct snd_hwdep *hw,
- struct snd_hwdep_dsp_image __user *_info)
+ struct snd_hwdep_dsp_image *info)
{
- struct snd_hwdep_dsp_image info;
int err;
if (! hw->ops.dsp_load)
return -ENXIO;
- memset(&info, 0, sizeof(info));
- if (copy_from_user(&info, _info, sizeof(info)))
- return -EFAULT;
+ if (info->index >= 32)
+ return -EINVAL;
/* check whether the dsp was already loaded */
- if (hw->dsp_loaded & (1 << info.index))
+ if (hw->dsp_loaded & (1u << info->index))
return -EBUSY;
- if (!access_ok(VERIFY_READ, info.image, info.length))
- return -EFAULT;
- err = hw->ops.dsp_load(hw, &info);
+ err = hw->ops.dsp_load(hw, info);
if (err < 0)
return err;
- hw->dsp_loaded |= (1 << info.index);
+ hw->dsp_loaded |= (1u << info->index);
return 0;
}
+static int snd_hwdep_dsp_load_user(struct snd_hwdep *hw,
+ struct snd_hwdep_dsp_image __user *_info)
+{
+ struct snd_hwdep_dsp_image info = {};
+
+ if (copy_from_user(&info, _info, sizeof(info)))
+ return -EFAULT;
+ return snd_hwdep_dsp_load(hw, &info);
+}
+
+
static long snd_hwdep_ioctl(struct file * file, unsigned int cmd,
unsigned long arg)
{
@@ -245,7 +246,7 @@ static long snd_hwdep_ioctl(struct file * file, unsigned int cmd,
case SNDRV_HWDEP_IOCTL_DSP_STATUS:
return snd_hwdep_dsp_status(hw, argp);
case SNDRV_HWDEP_IOCTL_DSP_LOAD:
- return snd_hwdep_dsp_load(hw, argp);
+ return snd_hwdep_dsp_load_user(hw, argp);
}
if (hw->ops.ioctl)
return hw->ops.ioctl(hw, file, cmd, arg);
@@ -271,16 +272,23 @@ static int snd_hwdep_control_ioctl(struct snd_card *card,
if (get_user(device, (int __user *)arg))
return -EFAULT;
- mutex_lock(&register_mutex);
- device = device < 0 ? 0 : device + 1;
- while (device < SNDRV_MINOR_HWDEPS) {
- if (snd_hwdep_search(card, device))
- break;
- device++;
+
+ scoped_guard(mutex, &register_mutex) {
+ if (device < 0)
+ device = 0;
+ else if (device < SNDRV_MINOR_HWDEPS)
+ device++;
+ else
+ device = SNDRV_MINOR_HWDEPS;
+
+ while (device < SNDRV_MINOR_HWDEPS) {
+ if (snd_hwdep_search(card, device))
+ break;
+ device++;
+ }
+ if (device >= SNDRV_MINOR_HWDEPS)
+ device = -1;
}
- if (device >= SNDRV_MINOR_HWDEPS)
- device = -1;
- mutex_unlock(&register_mutex);
if (put_user(device, (int __user *)arg))
return -EFAULT;
return 0;
@@ -288,19 +296,18 @@ static int snd_hwdep_control_ioctl(struct snd_card *card,
case SNDRV_CTL_IOCTL_HWDEP_INFO:
{
struct snd_hwdep_info __user *info = (struct snd_hwdep_info __user *)arg;
- int device, err;
+ int device;
struct snd_hwdep *hwdep;
if (get_user(device, &info->device))
return -EFAULT;
- mutex_lock(&register_mutex);
- hwdep = snd_hwdep_search(card, device);
- if (hwdep)
- err = snd_hwdep_info(hwdep, info);
- else
- err = -ENXIO;
- mutex_unlock(&register_mutex);
- return err;
+ scoped_guard(mutex, &register_mutex) {
+ hwdep = snd_hwdep_search(card, device);
+ if (!hwdep)
+ return -ENXIO;
+ return snd_hwdep_info(hwdep, info);
+ }
+ break;
}
}
return -ENOIOCTLCMD;
@@ -330,6 +337,16 @@ static const struct file_operations snd_hwdep_f_ops =
.mmap = snd_hwdep_mmap,
};
+static void snd_hwdep_free(struct snd_hwdep *hwdep)
+{
+ if (!hwdep)
+ return;
+ if (hwdep->private_free)
+ hwdep->private_free(hwdep);
+ put_device(hwdep->dev);
+ kfree(hwdep);
+}
+
/**
* snd_hwdep_new - create a new hwdep instance
* @card: the card instance
@@ -341,14 +358,14 @@ static const struct file_operations snd_hwdep_f_ops =
* The callbacks (hwdep->ops) must be set on the returned instance
* after this call manually by the caller.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_hwdep_new(struct snd_card *card, char *id, int device,
struct snd_hwdep **rhwdep)
{
struct snd_hwdep *hwdep;
int err;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_hwdep_dev_free,
.dev_register = snd_hwdep_dev_register,
.dev_disconnect = snd_hwdep_dev_disconnect,
@@ -359,84 +376,80 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device,
if (rhwdep)
*rhwdep = NULL;
hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL);
- if (hwdep == NULL) {
- snd_printk(KERN_ERR "hwdep: cannot allocate\n");
+ if (!hwdep)
return -ENOMEM;
- }
+
+ init_waitqueue_head(&hwdep->open_wait);
+ mutex_init(&hwdep->open_mutex);
hwdep->card = card;
hwdep->device = device;
if (id)
- strlcpy(hwdep->id, id, sizeof(hwdep->id));
+ strscpy(hwdep->id, id, sizeof(hwdep->id));
+
+ err = snd_device_alloc(&hwdep->dev, card);
+ if (err < 0) {
+ snd_hwdep_free(hwdep);
+ return err;
+ }
+
+ dev_set_name(hwdep->dev, "hwC%iD%i", card->number, device);
#ifdef CONFIG_SND_OSSEMUL
hwdep->oss_type = -1;
#endif
- if ((err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops)) < 0) {
+
+ err = snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops);
+ if (err < 0) {
snd_hwdep_free(hwdep);
return err;
}
- init_waitqueue_head(&hwdep->open_wait);
- mutex_init(&hwdep->open_mutex);
+
if (rhwdep)
*rhwdep = hwdep;
return 0;
}
-
-static int snd_hwdep_free(struct snd_hwdep *hwdep)
-{
- if (!hwdep)
- return 0;
- if (hwdep->private_free)
- hwdep->private_free(hwdep);
- kfree(hwdep);
- return 0;
-}
+EXPORT_SYMBOL(snd_hwdep_new);
static int snd_hwdep_dev_free(struct snd_device *device)
{
- struct snd_hwdep *hwdep = device->device_data;
- return snd_hwdep_free(hwdep);
+ snd_hwdep_free(device->device_data);
+ return 0;
}
static int snd_hwdep_dev_register(struct snd_device *device)
{
struct snd_hwdep *hwdep = device->device_data;
+ struct snd_card *card = hwdep->card;
int err;
- char name[32];
- mutex_lock(&register_mutex);
- if (snd_hwdep_search(hwdep->card, hwdep->device)) {
- mutex_unlock(&register_mutex);
+ guard(mutex)(&register_mutex);
+ if (snd_hwdep_search(card, hwdep->device))
return -EBUSY;
- }
list_add_tail(&hwdep->list, &snd_hwdep_devices);
- sprintf(name, "hwC%iD%i", hwdep->card->number, hwdep->device);
- if ((err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
- hwdep->card, hwdep->device,
- &snd_hwdep_f_ops, hwdep, name)) < 0) {
- snd_printk(KERN_ERR "unable to register hardware dependent device %i:%i\n",
- hwdep->card->number, hwdep->device);
+ err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
+ hwdep->card, hwdep->device,
+ &snd_hwdep_f_ops, hwdep, hwdep->dev);
+ if (err < 0) {
+ dev_err(hwdep->dev, "unable to register\n");
list_del(&hwdep->list);
- mutex_unlock(&register_mutex);
return err;
}
+
#ifdef CONFIG_SND_OSSEMUL
hwdep->ossreg = 0;
if (hwdep->oss_type >= 0) {
- if ((hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM) && (hwdep->device != 0)) {
- snd_printk (KERN_WARNING "only hwdep device 0 can be registered as OSS direct FM device!\n");
- } else {
- if (snd_register_oss_device(hwdep->oss_type,
- hwdep->card, hwdep->device,
- &snd_hwdep_f_ops, hwdep,
- hwdep->oss_dev) < 0) {
- snd_printk(KERN_ERR "unable to register OSS compatibility device %i:%i\n",
- hwdep->card->number, hwdep->device);
- } else
- hwdep->ossreg = 1;
- }
+ if (hwdep->oss_type == SNDRV_OSS_DEVICE_TYPE_DMFM &&
+ hwdep->device)
+ dev_warn(hwdep->dev,
+ "only hwdep device 0 can be registered as OSS direct FM device!\n");
+ else if (snd_register_oss_device(hwdep->oss_type,
+ card, hwdep->device,
+ &snd_hwdep_f_ops, hwdep) < 0)
+ dev_warn(hwdep->dev,
+ "unable to register OSS compatibility device\n");
+ else
+ hwdep->ossreg = 1;
}
#endif
- mutex_unlock(&register_mutex);
return 0;
}
@@ -446,22 +459,21 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device)
if (snd_BUG_ON(!hwdep))
return -ENXIO;
- mutex_lock(&register_mutex);
- if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) {
- mutex_unlock(&register_mutex);
+ guard(mutex)(&register_mutex);
+ if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep)
return -EINVAL;
- }
+ guard(mutex)(&hwdep->open_mutex);
+ wake_up(&hwdep->open_wait);
#ifdef CONFIG_SND_OSSEMUL
if (hwdep->ossreg)
snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
#endif
- snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device);
+ snd_unregister_device(hwdep->dev);
list_del_init(&hwdep->list);
- mutex_unlock(&register_mutex);
return 0;
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* Info interface
*/
@@ -471,11 +483,10 @@ static void snd_hwdep_proc_read(struct snd_info_entry *entry,
{
struct snd_hwdep *hwdep;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
list_for_each_entry(hwdep, &snd_hwdep_devices, list)
snd_iprintf(buffer, "%02i-%02i: %s\n",
hwdep->card->number, hwdep->device, hwdep->name);
- mutex_unlock(&register_mutex);
}
static struct snd_info_entry *snd_hwdep_proc_entry;
@@ -484,7 +495,8 @@ static void __init snd_hwdep_proc_init(void)
{
struct snd_info_entry *entry;
- if ((entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL)) != NULL) {
+ entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL);
+ if (entry) {
entry->c.text.read = snd_hwdep_proc_read;
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
@@ -498,10 +510,10 @@ static void __exit snd_hwdep_proc_done(void)
{
snd_info_free_entry(snd_hwdep_proc_entry);
}
-#else /* !CONFIG_PROC_FS */
+#else /* !CONFIG_SND_PROC_FS */
#define snd_hwdep_proc_init()
#define snd_hwdep_proc_done()
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
/*
@@ -525,5 +537,3 @@ static void __exit alsa_hwdep_exit(void)
module_init(alsa_hwdep_init)
module_exit(alsa_hwdep_exit)
-
-EXPORT_SYMBOL(snd_hwdep_new);
diff --git a/sound/core/hwdep_compat.c b/sound/core/hwdep_compat.c
index 3827c0ceec8f..a0b76706c083 100644
--- a/sound/core/hwdep_compat.c
+++ b/sound/core/hwdep_compat.c
@@ -1,21 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 32bit -> 64bit ioctl wrapper for hwdep API
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/* This file is included from hwdep.c */
@@ -33,26 +19,17 @@ struct snd_hwdep_dsp_image32 {
static int snd_hwdep_dsp_load_compat(struct snd_hwdep *hw,
struct snd_hwdep_dsp_image32 __user *src)
{
- struct snd_hwdep_dsp_image __user *dst;
+ struct snd_hwdep_dsp_image info = {};
compat_caddr_t ptr;
- u32 val;
- dst = compat_alloc_user_space(sizeof(*dst));
-
- /* index and name */
- if (copy_in_user(dst, src, 4 + 64))
- return -EFAULT;
- if (get_user(ptr, &src->image) ||
- put_user(compat_ptr(ptr), &dst->image))
- return -EFAULT;
- if (get_user(val, &src->length) ||
- put_user(val, &dst->length))
- return -EFAULT;
- if (get_user(val, &src->driver_data) ||
- put_user(val, &dst->driver_data))
+ if (copy_from_user(&info, src, 4 + 64) ||
+ get_user(ptr, &src->image) ||
+ get_user(info.length, &src->length) ||
+ get_user(info.driver_data, &src->driver_data))
return -EFAULT;
+ info.image = compat_ptr(ptr);
- return snd_hwdep_dsp_load(hw, dst);
+ return snd_hwdep_dsp_load(hw, &info);
}
enum {
diff --git a/sound/core/info.c b/sound/core/info.c
index b70564ed8b37..1f5b8a3d9e3b 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -1,47 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Information interface for ALSA driver
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/time.h>
#include <linux/mm.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/string.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
-#include <sound/version.h>
+#include <linux/utsname.h>
#include <linux/proc_fs.h>
#include <linux/mutex.h>
-#include <stdarg.h>
-
-/*
- *
- */
-
-#ifdef CONFIG_PROC_FS
int snd_info_check_reserved_words(const char *str)
{
- static char *reserved[] =
+ static const char * const reserved[] =
{
"version",
"meminfo",
@@ -56,7 +34,7 @@ int snd_info_check_reserved_words(const char *str)
"seq",
NULL
};
- char **xstr = reserved;
+ const char * const *xstr = reserved;
while (*xstr) {
if (!strcmp(*xstr, str))
@@ -78,74 +56,13 @@ struct snd_info_private_data {
};
static int snd_info_version_init(void);
-static int snd_info_version_done(void);
-static void snd_info_disconnect(struct snd_info_entry *entry);
-
-
-/* resize the proc r/w buffer */
-static int resize_info_buffer(struct snd_info_buffer *buffer,
- unsigned int nsize)
-{
- char *nbuf;
-
- nsize = PAGE_ALIGN(nsize);
- nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL);
- if (! nbuf)
- return -ENOMEM;
-
- buffer->buffer = nbuf;
- buffer->len = nsize;
- return 0;
-}
-
-/**
- * snd_iprintf - printf on the procfs buffer
- * @buffer: the procfs buffer
- * @fmt: the printf format
- *
- * Outputs the string on the procfs buffer just like printf().
- *
- * Returns the size of output string.
- */
-int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...)
-{
- va_list args;
- int len, res;
- int err = 0;
-
- might_sleep();
- if (buffer->stop || buffer->error)
- return 0;
- len = buffer->len - buffer->size;
- va_start(args, fmt);
- for (;;) {
- va_list ap;
- va_copy(ap, args);
- res = vsnprintf(buffer->buffer + buffer->curr, len, fmt, ap);
- va_end(ap);
- if (res < len)
- break;
- err = resize_info_buffer(buffer, buffer->len + PAGE_SIZE);
- if (err < 0)
- break;
- len = buffer->len - buffer->size;
- }
- va_end(args);
-
- if (err < 0)
- return err;
- buffer->curr += res;
- buffer->size += res;
- return res;
-}
-
-EXPORT_SYMBOL(snd_iprintf);
+static void snd_info_clear_entries(struct snd_info_entry *entry);
/*
*/
-static struct proc_dir_entry *snd_proc_root;
+static struct snd_info_entry *snd_proc_root;
struct snd_info_entry *snd_seq_root;
EXPORT_SYMBOL(snd_seq_root);
@@ -153,33 +70,52 @@ EXPORT_SYMBOL(snd_seq_root);
struct snd_info_entry *snd_oss_root;
#endif
-static void snd_remove_proc_entry(struct proc_dir_entry *parent,
- struct proc_dir_entry *de)
+static int alloc_info_private(struct snd_info_entry *entry,
+ struct snd_info_private_data **ret)
+{
+ struct snd_info_private_data *data;
+
+ if (!entry || !entry->p)
+ return -ENODEV;
+ if (!try_module_get(entry->module))
+ return -EFAULT;
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ module_put(entry->module);
+ return -ENOMEM;
+ }
+ data->entry = entry;
+ *ret = data;
+ return 0;
+}
+
+static bool valid_pos(loff_t pos, size_t count)
{
- if (de)
- remove_proc_entry(de->name, parent);
+ if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
+ return false;
+ if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
+ return false;
+ return true;
}
+/*
+ * file ops for binary proc files
+ */
static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
{
struct snd_info_private_data *data;
struct snd_info_entry *entry;
- loff_t ret = -EINVAL, size;
+ loff_t size;
data = file->private_data;
entry = data->entry;
- mutex_lock(&entry->access);
- if (entry->content == SNDRV_INFO_CONTENT_DATA &&
- entry->c.ops->llseek) {
- offset = entry->c.ops->llseek(entry,
- data->file_private_data,
- file, offset, orig);
- goto out;
- }
- if (entry->content == SNDRV_INFO_CONTENT_DATA)
- size = entry->size;
- else
- size = 0;
+ guard(mutex)(&entry->access);
+ if (entry->c.ops->llseek)
+ return entry->c.ops->llseek(entry,
+ data->file_private_data,
+ file, offset, orig);
+
+ size = entry->size;
switch (orig) {
case SEEK_SET:
break;
@@ -188,65 +124,37 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
break;
case SEEK_END:
if (!size)
- goto out;
+ return -EINVAL;
offset += size;
break;
default:
- goto out;
+ return -EINVAL;
}
if (offset < 0)
- goto out;
+ return -EINVAL;
if (size && offset > size)
offset = size;
file->f_pos = offset;
- ret = offset;
- out:
- mutex_unlock(&entry->access);
- return ret;
+ return offset;
}
static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
size_t count, loff_t * offset)
{
- struct snd_info_private_data *data;
- struct snd_info_entry *entry;
- struct snd_info_buffer *buf;
- size_t size = 0;
+ struct snd_info_private_data *data = file->private_data;
+ struct snd_info_entry *entry = data->entry;
+ size_t size;
loff_t pos;
- data = file->private_data;
- if (snd_BUG_ON(!data))
- return -ENXIO;
pos = *offset;
- if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
- return -EIO;
- if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
+ if (!valid_pos(pos, count))
return -EIO;
- entry = data->entry;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_TEXT:
- buf = data->rbuffer;
- if (buf == NULL)
- return -EIO;
- if (pos >= buf->size)
- return 0;
- size = buf->size - pos;
- size = min(count, size);
- if (copy_to_user(buffer, buf->buffer + pos, size))
- return -EFAULT;
- break;
- case SNDRV_INFO_CONTENT_DATA:
- if (pos >= entry->size)
- return 0;
- if (entry->c.ops->read) {
- size = entry->size - pos;
- size = min(count, size);
- size = entry->c.ops->read(entry,
- data->file_private_data,
- file, buffer, size, pos);
- }
- break;
- }
+ if (pos >= entry->size)
+ return 0;
+ size = entry->size - pos;
+ size = min(count, size);
+ size = entry->c.ops->read(entry, data->file_private_data,
+ file, buffer, size, pos);
if ((ssize_t) size > 0)
*offset = pos + size;
return size;
@@ -255,342 +163,314 @@ static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer,
size_t count, loff_t * offset)
{
- struct snd_info_private_data *data;
- struct snd_info_entry *entry;
- struct snd_info_buffer *buf;
+ struct snd_info_private_data *data = file->private_data;
+ struct snd_info_entry *entry = data->entry;
ssize_t size = 0;
loff_t pos;
- data = file->private_data;
- if (snd_BUG_ON(!data))
- return -ENXIO;
- entry = data->entry;
pos = *offset;
- if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
+ if (!valid_pos(pos, count))
return -EIO;
- if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
- return -EIO;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_TEXT:
- buf = data->wbuffer;
- if (buf == NULL)
- return -EIO;
- mutex_lock(&entry->access);
- if (pos + count >= buf->len) {
- if (resize_info_buffer(buf, pos + count)) {
- mutex_unlock(&entry->access);
- return -ENOMEM;
- }
- }
- if (copy_from_user(buf->buffer + pos, buffer, count)) {
- mutex_unlock(&entry->access);
- return -EFAULT;
- }
- buf->size = pos + count;
- mutex_unlock(&entry->access);
- size = count;
- break;
- case SNDRV_INFO_CONTENT_DATA:
- if (entry->c.ops->write && count > 0) {
- size_t maxsize = entry->size - pos;
- count = min(count, maxsize);
- size = entry->c.ops->write(entry,
- data->file_private_data,
- file, buffer, count, pos);
- }
- break;
+ if (count > 0) {
+ size_t maxsize = entry->size - pos;
+ count = min(count, maxsize);
+ size = entry->c.ops->write(entry, data->file_private_data,
+ file, buffer, count, pos);
}
- if ((ssize_t) size > 0)
+ if (size > 0)
*offset = pos + size;
return size;
}
-static int snd_info_entry_open(struct inode *inode, struct file *file)
+static __poll_t snd_info_entry_poll(struct file *file, poll_table *wait)
+{
+ struct snd_info_private_data *data = file->private_data;
+ struct snd_info_entry *entry = data->entry;
+ __poll_t mask = 0;
+
+ if (entry->c.ops->poll)
+ return entry->c.ops->poll(entry,
+ data->file_private_data,
+ file, wait);
+ if (entry->c.ops->read)
+ mask |= EPOLLIN | EPOLLRDNORM;
+ if (entry->c.ops->write)
+ mask |= EPOLLOUT | EPOLLWRNORM;
+ return mask;
+}
+
+static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct snd_info_private_data *data = file->private_data;
+ struct snd_info_entry *entry = data->entry;
+
+ if (!entry->c.ops->ioctl)
+ return -ENOTTY;
+ return entry->c.ops->ioctl(entry, data->file_private_data,
+ file, cmd, arg);
+}
+
+static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
{
+ struct inode *inode = file_inode(file);
+ struct snd_info_private_data *data;
struct snd_info_entry *entry;
+
+ data = file->private_data;
+ if (data == NULL)
+ return 0;
+ entry = data->entry;
+ if (!entry->c.ops->mmap)
+ return -ENXIO;
+ return entry->c.ops->mmap(entry, data->file_private_data,
+ inode, file, vma);
+}
+
+static int snd_info_entry_open(struct inode *inode, struct file *file)
+{
+ struct snd_info_entry *entry = pde_data(inode);
struct snd_info_private_data *data;
- struct snd_info_buffer *buffer;
- struct proc_dir_entry *p;
int mode, err;
- mutex_lock(&info_mutex);
- p = PDE(inode);
- entry = p == NULL ? NULL : (struct snd_info_entry *)p->data;
- if (entry == NULL || ! entry->p) {
- mutex_unlock(&info_mutex);
- return -ENODEV;
- }
- if (!try_module_get(entry->module)) {
- err = -EFAULT;
- goto __error1;
- }
+ guard(mutex)(&info_mutex);
+ err = alloc_info_private(entry, &data);
+ if (err < 0)
+ return err;
+
mode = file->f_flags & O_ACCMODE;
- if (mode == O_RDONLY || mode == O_RDWR) {
- if ((entry->content == SNDRV_INFO_CONTENT_DATA &&
- entry->c.ops->read == NULL)) {
- err = -ENODEV;
- goto __error;
- }
+ if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) ||
+ ((mode == O_WRONLY || mode == O_RDWR) && !entry->c.ops->write)) {
+ err = -ENODEV;
+ goto error;
}
- if (mode == O_WRONLY || mode == O_RDWR) {
- if ((entry->content == SNDRV_INFO_CONTENT_DATA &&
- entry->c.ops->write == NULL)) {
- err = -ENODEV;
- goto __error;
- }
- }
- data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (data == NULL) {
- err = -ENOMEM;
- goto __error;
- }
- data->entry = entry;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_TEXT:
- if (mode == O_RDONLY || mode == O_RDWR) {
- buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
- if (buffer == NULL)
- goto __nomem;
- data->rbuffer = buffer;
- buffer->len = PAGE_SIZE;
- buffer->buffer = kmalloc(buffer->len, GFP_KERNEL);
- if (buffer->buffer == NULL)
- goto __nomem;
- }
- if (mode == O_WRONLY || mode == O_RDWR) {
- buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
- if (buffer == NULL)
- goto __nomem;
- data->wbuffer = buffer;
- buffer->len = PAGE_SIZE;
- buffer->buffer = kmalloc(buffer->len, GFP_KERNEL);
- if (buffer->buffer == NULL)
- goto __nomem;
- }
- break;
- case SNDRV_INFO_CONTENT_DATA: /* data */
- if (entry->c.ops->open) {
- if ((err = entry->c.ops->open(entry, mode,
- &data->file_private_data)) < 0) {
- kfree(data);
- goto __error;
- }
- }
- break;
+
+ if (entry->c.ops->open) {
+ err = entry->c.ops->open(entry, mode, &data->file_private_data);
+ if (err < 0)
+ goto error;
}
+
file->private_data = data;
- mutex_unlock(&info_mutex);
- if (entry->content == SNDRV_INFO_CONTENT_TEXT &&
- (mode == O_RDONLY || mode == O_RDWR)) {
- if (entry->c.text.read) {
- mutex_lock(&entry->access);
- entry->c.text.read(entry, data->rbuffer);
- mutex_unlock(&entry->access);
- }
- }
return 0;
- __nomem:
- if (data->rbuffer) {
- kfree(data->rbuffer->buffer);
- kfree(data->rbuffer);
- }
- if (data->wbuffer) {
- kfree(data->wbuffer->buffer);
- kfree(data->wbuffer);
- }
+ error:
kfree(data);
- err = -ENOMEM;
- __error:
module_put(entry->module);
- __error1:
- mutex_unlock(&info_mutex);
return err;
}
static int snd_info_entry_release(struct inode *inode, struct file *file)
{
- struct snd_info_entry *entry;
- struct snd_info_private_data *data;
- int mode;
+ struct snd_info_private_data *data = file->private_data;
+ struct snd_info_entry *entry = data->entry;
- mode = file->f_flags & O_ACCMODE;
- data = file->private_data;
- entry = data->entry;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_TEXT:
- if (data->rbuffer) {
- kfree(data->rbuffer->buffer);
- kfree(data->rbuffer);
- }
- if (data->wbuffer) {
- if (entry->c.text.write) {
- entry->c.text.write(entry, data->wbuffer);
- if (data->wbuffer->error) {
- snd_printk(KERN_WARNING "data write error to %s (%i)\n",
- entry->name,
- data->wbuffer->error);
- }
- }
- kfree(data->wbuffer->buffer);
- kfree(data->wbuffer);
- }
- break;
- case SNDRV_INFO_CONTENT_DATA:
- if (entry->c.ops->release)
- entry->c.ops->release(entry, mode,
- data->file_private_data);
- break;
- }
+ if (entry->c.ops->release)
+ entry->c.ops->release(entry, file->f_flags & O_ACCMODE,
+ data->file_private_data);
module_put(entry->module);
kfree(data);
return 0;
}
-static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait)
+static const struct proc_ops snd_info_entry_operations =
{
- struct snd_info_private_data *data;
- struct snd_info_entry *entry;
- unsigned int mask;
+ .proc_lseek = snd_info_entry_llseek,
+ .proc_read = snd_info_entry_read,
+ .proc_write = snd_info_entry_write,
+ .proc_poll = snd_info_entry_poll,
+ .proc_ioctl = snd_info_entry_ioctl,
+ .proc_mmap = snd_info_entry_mmap,
+ .proc_open = snd_info_entry_open,
+ .proc_release = snd_info_entry_release,
+};
- data = file->private_data;
- if (data == NULL)
- return 0;
- entry = data->entry;
- mask = 0;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_DATA:
- if (entry->c.ops->poll)
- return entry->c.ops->poll(entry,
- data->file_private_data,
- file, wait);
- if (entry->c.ops->read)
- mask |= POLLIN | POLLRDNORM;
- if (entry->c.ops->write)
- mask |= POLLOUT | POLLWRNORM;
- break;
+/*
+ * file ops for text proc files
+ */
+static ssize_t snd_info_text_entry_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *offset)
+{
+ struct seq_file *m = file->private_data;
+ struct snd_info_private_data *data = m->private;
+ struct snd_info_entry *entry = data->entry;
+ struct snd_info_buffer *buf;
+ loff_t pos;
+ size_t next;
+
+ if (!entry->c.text.write)
+ return -EIO;
+ pos = *offset;
+ if (!valid_pos(pos, count))
+ return -EIO;
+ next = pos + count;
+ /* don't handle too large text inputs */
+ if (next > 16 * 1024)
+ return -EIO;
+ guard(mutex)(&entry->access);
+ buf = data->wbuffer;
+ if (!buf) {
+ data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
}
- return mask;
+ if (next > buf->len) {
+ char *nbuf = kvzalloc(PAGE_ALIGN(next), GFP_KERNEL);
+ if (!nbuf)
+ return -ENOMEM;
+ kvfree(buf->buffer);
+ buf->buffer = nbuf;
+ buf->len = PAGE_ALIGN(next);
+ }
+ if (copy_from_user(buf->buffer + pos, buffer, count))
+ return -EFAULT;
+ buf->size = next;
+ *offset = next;
+ return count;
}
-static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+static int snd_info_seq_show(struct seq_file *seq, void *p)
{
- struct snd_info_private_data *data;
- struct snd_info_entry *entry;
+ struct snd_info_private_data *data = seq->private;
+ struct snd_info_entry *entry = data->entry;
- data = file->private_data;
- if (data == NULL)
- return 0;
- entry = data->entry;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_DATA:
- if (entry->c.ops->ioctl)
- return entry->c.ops->ioctl(entry,
- data->file_private_data,
- file, cmd, arg);
- break;
+ if (!entry->c.text.read) {
+ return -EIO;
+ } else {
+ data->rbuffer->buffer = (char *)seq; /* XXX hack! */
+ entry->c.text.read(entry, data->rbuffer);
}
- return -ENOTTY;
+ return 0;
}
-static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
+static int snd_info_text_entry_open(struct inode *inode, struct file *file)
{
- struct inode *inode = file->f_path.dentry->d_inode;
+ struct snd_info_entry *entry = pde_data(inode);
struct snd_info_private_data *data;
- struct snd_info_entry *entry;
+ int err;
- data = file->private_data;
- if (data == NULL)
- return 0;
- entry = data->entry;
- switch (entry->content) {
- case SNDRV_INFO_CONTENT_DATA:
- if (entry->c.ops->mmap)
- return entry->c.ops->mmap(entry,
- data->file_private_data,
- inode, file, vma);
- break;
+ guard(mutex)(&info_mutex);
+ err = alloc_info_private(entry, &data);
+ if (err < 0)
+ return err;
+
+ data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL);
+ if (!data->rbuffer) {
+ err = -ENOMEM;
+ goto error;
+ }
+ if (entry->size)
+ err = single_open_size(file, snd_info_seq_show, data,
+ entry->size);
+ else
+ err = single_open(file, snd_info_seq_show, data);
+ if (err < 0)
+ goto error;
+ return 0;
+
+ error:
+ kfree(data->rbuffer);
+ kfree(data);
+ module_put(entry->module);
+ return err;
+}
+
+static int snd_info_text_entry_release(struct inode *inode, struct file *file)
+{
+ struct seq_file *m = file->private_data;
+ struct snd_info_private_data *data = m->private;
+ struct snd_info_entry *entry = data->entry;
+
+ if (data->wbuffer && entry->c.text.write)
+ entry->c.text.write(entry, data->wbuffer);
+
+ single_release(inode, file);
+ kfree(data->rbuffer);
+ if (data->wbuffer) {
+ kvfree(data->wbuffer->buffer);
+ kfree(data->wbuffer);
}
- return -ENXIO;
+
+ module_put(entry->module);
+ kfree(data);
+ return 0;
}
-static const struct file_operations snd_info_entry_operations =
+static const struct proc_ops snd_info_text_entry_ops =
{
- .owner = THIS_MODULE,
- .llseek = snd_info_entry_llseek,
- .read = snd_info_entry_read,
- .write = snd_info_entry_write,
- .poll = snd_info_entry_poll,
- .unlocked_ioctl = snd_info_entry_ioctl,
- .mmap = snd_info_entry_mmap,
- .open = snd_info_entry_open,
- .release = snd_info_entry_release,
+ .proc_open = snd_info_text_entry_open,
+ .proc_release = snd_info_text_entry_release,
+ .proc_write = snd_info_text_entry_write,
+ .proc_lseek = seq_lseek,
+ .proc_read = seq_read,
};
-int __init snd_info_init(void)
+static struct snd_info_entry *create_subdir(struct module *mod,
+ const char *name)
{
- struct proc_dir_entry *p;
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_module_entry(mod, name, NULL);
+ if (!entry)
+ return NULL;
+ entry->mode = S_IFDIR | 0555;
+ if (snd_info_register(entry) < 0) {
+ snd_info_free_entry(entry);
+ return NULL;
+ }
+ return entry;
+}
+
+static struct snd_info_entry *
+snd_info_create_entry(const char *name, struct snd_info_entry *parent,
+ struct module *module);
- p = create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, NULL);
- if (p == NULL)
+int __init snd_info_init(void)
+{
+ snd_proc_root = snd_info_create_entry("asound", NULL, THIS_MODULE);
+ if (!snd_proc_root)
return -ENOMEM;
- snd_proc_root = p;
+ snd_proc_root->mode = S_IFDIR | 0555;
+ snd_proc_root->p = proc_mkdir("asound", NULL);
+ if (!snd_proc_root->p)
+ goto error;
#ifdef CONFIG_SND_OSSEMUL
- {
- struct snd_info_entry *entry;
- if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", NULL)) == NULL)
- return -ENOMEM;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- return -ENOMEM;
- }
- snd_oss_root = entry;
- }
+ snd_oss_root = create_subdir(THIS_MODULE, "oss");
+ if (!snd_oss_root)
+ goto error;
#endif
-#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
- {
- struct snd_info_entry *entry;
- if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL)
- return -ENOMEM;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- return -ENOMEM;
- }
- snd_seq_root = entry;
- }
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+ snd_seq_root = create_subdir(THIS_MODULE, "seq");
+ if (!snd_seq_root)
+ goto error;
#endif
- snd_info_version_init();
- snd_minor_info_init();
- snd_minor_info_oss_init();
- snd_card_info_init();
+ if (snd_info_version_init() < 0 ||
+ snd_minor_info_init() < 0 ||
+ snd_minor_info_oss_init() < 0 ||
+ snd_card_info_init() < 0 ||
+ snd_info_minor_register() < 0)
+ goto error;
return 0;
+
+ error:
+ snd_info_free_entry(snd_proc_root);
+ return -ENOMEM;
}
int __exit snd_info_done(void)
{
- snd_card_info_done();
- snd_minor_info_oss_done();
- snd_minor_info_done();
- snd_info_version_done();
- if (snd_proc_root) {
-#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
- snd_info_free_entry(snd_seq_root);
-#endif
-#ifdef CONFIG_SND_OSSEMUL
- snd_info_free_entry(snd_oss_root);
-#endif
- snd_remove_proc_entry(NULL, snd_proc_root);
- }
+ snd_info_free_entry(snd_proc_root);
return 0;
}
-/*
-
- */
+static void snd_card_id_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_card *card = entry->private_data;
+ snd_iprintf(buffer, "%s\n", card->id);
+}
/*
* create a card proc file
@@ -605,33 +485,38 @@ int snd_info_card_create(struct snd_card *card)
return -ENXIO;
sprintf(str, "card%i", card->number);
- if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
- return -ENOMEM;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
+ entry = create_subdir(card->module, str);
+ if (!entry)
return -ENOMEM;
- }
card->proc_root = entry;
- return 0;
+
+ return snd_card_ro_proc_new(card, "id", card, snd_card_id_read);
}
/*
* register the card proc file
* called from init.c
+ * can be called multiple times for reinitialization
*/
int snd_info_card_register(struct snd_card *card)
{
struct proc_dir_entry *p;
+ int err;
if (snd_BUG_ON(!card))
return -ENXIO;
+ err = snd_info_register(card->proc_root);
+ if (err < 0)
+ return err;
+
if (!strcmp(card->id, card->proc_root->name))
return 0;
- p = proc_symlink(card->id, snd_proc_root, card->proc_root->name);
- if (p == NULL)
+ if (card->proc_root_link)
+ return 0;
+ p = proc_symlink(card->id, snd_proc_root->p, card->proc_root->name);
+ if (!p)
return -ENOMEM;
card->proc_root_link = p;
return 0;
@@ -642,16 +527,15 @@ int snd_info_card_register(struct snd_card *card)
*/
void snd_info_card_id_change(struct snd_card *card)
{
- mutex_lock(&info_mutex);
+ guard(mutex)(&info_mutex);
if (card->proc_root_link) {
- snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
+ proc_remove(card->proc_root_link);
card->proc_root_link = NULL;
}
if (strcmp(card->id, card->proc_root->name))
card->proc_root_link = proc_symlink(card->id,
- snd_proc_root,
+ snd_proc_root->p,
card->proc_root->name);
- mutex_unlock(&info_mutex);
}
/*
@@ -662,14 +546,16 @@ void snd_info_card_disconnect(struct snd_card *card)
{
if (!card)
return;
- mutex_lock(&info_mutex);
- if (card->proc_root_link) {
- snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
- card->proc_root_link = NULL;
- }
+
+ proc_remove(card->proc_root_link);
+ if (card->proc_root)
+ proc_remove(card->proc_root->p);
+
+ guard(mutex)(&info_mutex);
if (card->proc_root)
- snd_info_disconnect(card->proc_root);
- mutex_unlock(&info_mutex);
+ snd_info_clear_entries(card->proc_root);
+ card->proc_root_link = NULL;
+ card->proc_root = NULL;
}
/*
@@ -690,40 +576,36 @@ int snd_info_card_free(struct snd_card *card)
* snd_info_get_line - read one line from the procfs buffer
* @buffer: the procfs buffer
* @line: the buffer to store
- * @len: the max. buffer size - 1
+ * @len: the max. buffer size
*
* Reads one line from the buffer and stores the string.
*
- * Returns zero if successful, or 1 if error or EOF.
+ * Return: Zero if successful, or 1 if error or EOF.
*/
int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len)
{
- int c = -1;
+ int c;
+ if (snd_BUG_ON(!buffer))
+ return 1;
+ if (!buffer->buffer)
+ return 1;
if (len <= 0 || buffer->stop || buffer->error)
return 1;
- while (--len > 0) {
+ while (!buffer->stop) {
c = buffer->buffer[buffer->curr++];
- if (c == '\n') {
- if (buffer->curr >= buffer->size)
- buffer->stop = 1;
- break;
- }
- *line++ = c;
- if (buffer->curr >= buffer->size) {
+ if (buffer->curr >= buffer->size)
buffer->stop = 1;
+ if (c == '\n')
break;
+ if (len > 1) {
+ len--;
+ *line++ = c;
}
}
- while (c != '\n' && !buffer->stop) {
- c = buffer->buffer[buffer->curr++];
- if (buffer->curr >= buffer->size)
- buffer->stop = 1;
- }
*line = '\0';
return 0;
}
-
EXPORT_SYMBOL(snd_info_get_line);
/**
@@ -735,7 +617,7 @@ EXPORT_SYMBOL(snd_info_get_line);
* Parses the original string and copy a token to the given
* string buffer.
*
- * Returns the updated pointer of the original string so that
+ * Return: The updated pointer of the original string so that
* it can be used for the next call.
*/
const char *snd_info_get_str(char *dest, const char *src, int len)
@@ -761,12 +643,12 @@ const char *snd_info_get_str(char *dest, const char *src, int len)
src++;
return src;
}
-
EXPORT_SYMBOL(snd_info_get_str);
-/**
+/*
* snd_info_create_entry - create an info entry
* @name: the proc file name
+ * @parent: the parent directory
*
* Creates an info entry with the given file name and initializes as
* the default state.
@@ -774,9 +656,11 @@ EXPORT_SYMBOL(snd_info_get_str);
* Usually called from other functions such as
* snd_info_create_card_entry().
*
- * Returns the pointer of the new instance, or NULL on failure.
+ * Return: The pointer of the new instance, or %NULL on failure.
*/
-static struct snd_info_entry *snd_info_create_entry(const char *name)
+static struct snd_info_entry *
+snd_info_create_entry(const char *name, struct snd_info_entry *parent,
+ struct module *module)
{
struct snd_info_entry *entry;
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
@@ -787,11 +671,17 @@ static struct snd_info_entry *snd_info_create_entry(const char *name)
kfree(entry);
return NULL;
}
- entry->mode = S_IFREG | S_IRUGO;
+ entry->mode = S_IFREG | 0444;
entry->content = SNDRV_INFO_CONTENT_TEXT;
mutex_init(&entry->access);
INIT_LIST_HEAD(&entry->children);
INIT_LIST_HEAD(&entry->list);
+ entry->parent = parent;
+ entry->module = module;
+ if (parent) {
+ guard(mutex)(&parent->access);
+ list_add_tail(&entry->list, &parent->children);
+ }
return entry;
}
@@ -803,20 +693,16 @@ static struct snd_info_entry *snd_info_create_entry(const char *name)
*
* Creates a new info entry and assigns it to the given module.
*
- * Returns the pointer of the new instance, or NULL on failure.
+ * Return: The pointer of the new instance, or %NULL on failure.
*/
struct snd_info_entry *snd_info_create_module_entry(struct module * module,
const char *name,
struct snd_info_entry *parent)
{
- struct snd_info_entry *entry = snd_info_create_entry(name);
- if (entry) {
- entry->module = module;
- entry->parent = parent;
- }
- return entry;
+ if (!parent)
+ parent = snd_proc_root;
+ return snd_info_create_entry(name, parent, module);
}
-
EXPORT_SYMBOL(snd_info_create_module_entry);
/**
@@ -827,168 +713,166 @@ EXPORT_SYMBOL(snd_info_create_module_entry);
*
* Creates a new info entry and assigns it to the given card.
*
- * Returns the pointer of the new instance, or NULL on failure.
+ * Return: The pointer of the new instance, or %NULL on failure.
*/
struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
const char *name,
struct snd_info_entry * parent)
{
- struct snd_info_entry *entry = snd_info_create_entry(name);
- if (entry) {
- entry->module = card->module;
- entry->card = card;
- entry->parent = parent;
- }
- return entry;
+ if (!parent)
+ parent = card->proc_root;
+ return snd_info_create_entry(name, parent, card->module);
}
-
EXPORT_SYMBOL(snd_info_create_card_entry);
-static void snd_info_disconnect(struct snd_info_entry *entry)
+static void snd_info_clear_entries(struct snd_info_entry *entry)
{
- struct list_head *p, *n;
- struct proc_dir_entry *root;
-
- list_for_each_safe(p, n, &entry->children) {
- snd_info_disconnect(list_entry(p, struct snd_info_entry, list));
- }
+ struct snd_info_entry *p;
- if (! entry->p)
+ if (!entry->p)
return;
- list_del_init(&entry->list);
- root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
- snd_BUG_ON(!root);
- snd_remove_proc_entry(root, entry->p);
+ list_for_each_entry(p, &entry->children, list)
+ snd_info_clear_entries(p);
entry->p = NULL;
}
-static int snd_info_dev_free_entry(struct snd_device *device)
-{
- struct snd_info_entry *entry = device->device_data;
- snd_info_free_entry(entry);
- return 0;
-}
-
-static int snd_info_dev_register_entry(struct snd_device *device)
-{
- struct snd_info_entry *entry = device->device_data;
- return snd_info_register(entry);
-}
-
-/**
- * snd_card_proc_new - create an info entry for the given card
- * @card: the card instance
- * @name: the file name
- * @entryp: the pointer to store the new info entry
- *
- * Creates a new info entry and assigns it to the given card.
- * Unlike snd_info_create_card_entry(), this function registers the
- * info entry as an ALSA device component, so that it can be
- * unregistered/released without explicit call.
- * Also, you don't have to register this entry via snd_info_register(),
- * since this will be registered by snd_card_register() automatically.
- *
- * The parent is assumed as card->proc_root.
- *
- * For releasing this entry, use snd_device_free() instead of
- * snd_info_free_entry().
- *
- * Returns zero if successful, or a negative error code on failure.
- */
-int snd_card_proc_new(struct snd_card *card, const char *name,
- struct snd_info_entry **entryp)
-{
- static struct snd_device_ops ops = {
- .dev_free = snd_info_dev_free_entry,
- .dev_register = snd_info_dev_register_entry,
- /* disconnect is done via snd_info_card_disconnect() */
- };
- struct snd_info_entry *entry;
- int err;
-
- entry = snd_info_create_card_entry(card, name, card->proc_root);
- if (! entry)
- return -ENOMEM;
- if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) {
- snd_info_free_entry(entry);
- return err;
- }
- if (entryp)
- *entryp = entry;
- return 0;
-}
-
-EXPORT_SYMBOL(snd_card_proc_new);
-
/**
* snd_info_free_entry - release the info entry
* @entry: the info entry
*
- * Releases the info entry. Don't call this after registered.
+ * Releases the info entry.
*/
void snd_info_free_entry(struct snd_info_entry * entry)
{
- if (entry == NULL)
+ struct snd_info_entry *p, *n;
+
+ if (!entry)
return;
if (entry->p) {
- mutex_lock(&info_mutex);
- snd_info_disconnect(entry);
- mutex_unlock(&info_mutex);
+ proc_remove(entry->p);
+ guard(mutex)(&info_mutex);
+ snd_info_clear_entries(entry);
+ }
+
+ /* free all children at first */
+ list_for_each_entry_safe(p, n, &entry->children, list)
+ snd_info_free_entry(p);
+
+ p = entry->parent;
+ if (p) {
+ guard(mutex)(&p->access);
+ list_del(&entry->list);
}
kfree(entry->name);
if (entry->private_free)
entry->private_free(entry);
kfree(entry);
}
-
EXPORT_SYMBOL(snd_info_free_entry);
+static int __snd_info_register(struct snd_info_entry *entry)
+{
+ struct proc_dir_entry *root, *p = NULL;
+
+ if (snd_BUG_ON(!entry))
+ return -ENXIO;
+ root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p;
+ guard(mutex)(&info_mutex);
+ if (entry->p || !root)
+ return 0;
+ if (S_ISDIR(entry->mode)) {
+ p = proc_mkdir_mode(entry->name, entry->mode, root);
+ if (!p)
+ return -ENOMEM;
+ } else {
+ const struct proc_ops *ops;
+ if (entry->content == SNDRV_INFO_CONTENT_DATA)
+ ops = &snd_info_entry_operations;
+ else
+ ops = &snd_info_text_entry_ops;
+ p = proc_create_data(entry->name, entry->mode, root,
+ ops, entry);
+ if (!p)
+ return -ENOMEM;
+ proc_set_size(p, entry->size);
+ }
+ entry->p = p;
+ return 0;
+}
+
/**
* snd_info_register - register the info entry
* @entry: the info entry
*
* Registers the proc info entry.
+ * The all children entries are registered recursively.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
-int snd_info_register(struct snd_info_entry * entry)
+int snd_info_register(struct snd_info_entry *entry)
{
- struct proc_dir_entry *root, *p = NULL;
+ struct snd_info_entry *p;
+ int err;
- if (snd_BUG_ON(!entry))
- return -ENXIO;
- root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
- mutex_lock(&info_mutex);
- p = create_proc_entry(entry->name, entry->mode, root);
- if (!p) {
- mutex_unlock(&info_mutex);
- return -ENOMEM;
+ if (!entry->p) {
+ err = __snd_info_register(entry);
+ if (err < 0)
+ return err;
}
- if (!S_ISDIR(entry->mode))
- p->proc_fops = &snd_info_entry_operations;
- p->size = entry->size;
- p->data = entry;
- entry->p = p;
- if (entry->parent)
- list_add_tail(&entry->list, &entry->parent->children);
- mutex_unlock(&info_mutex);
+
+ list_for_each_entry(p, &entry->children, list) {
+ err = snd_info_register(p);
+ if (err < 0)
+ return err;
+ }
+
return 0;
}
-
EXPORT_SYMBOL(snd_info_register);
+/**
+ * snd_card_rw_proc_new - Create a read/write text proc file entry for the card
+ * @card: the card instance
+ * @name: the file name
+ * @private_data: the arbitrary private data
+ * @read: the read callback
+ * @write: the write callback, NULL for read-only
+ *
+ * This proc file entry will be registered via snd_card_register() call, and
+ * it will be removed automatically at the card removal, too.
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_card_rw_proc_new(struct snd_card *card, const char *name,
+ void *private_data,
+ void (*read)(struct snd_info_entry *,
+ struct snd_info_buffer *),
+ void (*write)(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(card, name, card->proc_root);
+ if (!entry)
+ return -ENOMEM;
+ snd_info_set_text_ops(entry, private_data, read);
+ if (write) {
+ entry->mode |= 0200;
+ entry->c.text.write = write;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_card_rw_proc_new);
+
/*
*/
-static struct snd_info_entry *snd_info_version_entry;
-
static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
snd_iprintf(buffer,
- "Advanced Linux Sound Architecture Driver Version "
- CONFIG_SND_VERSION CONFIG_SND_DATE ".\n"
- );
+ "Advanced Linux Sound Architecture Driver Version k%s.\n",
+ init_utsname()->release);
}
static int __init snd_info_version_init(void)
@@ -999,18 +883,5 @@ static int __init snd_info_version_init(void)
if (entry == NULL)
return -ENOMEM;
entry->c.text.read = snd_info_version_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- return -ENOMEM;
- }
- snd_info_version_entry = entry;
- return 0;
+ return snd_info_register(entry); /* freed in error path */
}
-
-static int __exit snd_info_version_done(void)
-{
- snd_info_free_entry(snd_info_version_entry);
- return 0;
-}
-
-#endif /* CONFIG_PROC_FS */
diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c
index e4af138d651a..0dbbb8005570 100644
--- a/sound/core/info_oss.c
+++ b/sound/core/info_oss.c
@@ -1,43 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Information interface for ALSA driver
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/string.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
-#include <sound/version.h>
#include <linux/utsname.h>
#include <linux/mutex.h>
-#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
-
/*
* OSS compatible part
*/
static DEFINE_MUTEX(strings);
static char *snd_sndstat_strings[SNDRV_CARDS][SNDRV_OSS_INFO_DEV_COUNT];
-static struct snd_info_entry *snd_sndstat_proc_entry;
int snd_oss_info_register(int dev, int num, char *string)
{
@@ -47,24 +29,19 @@ int snd_oss_info_register(int dev, int num, char *string)
return -ENXIO;
if (snd_BUG_ON(num < 0 || num >= SNDRV_CARDS))
return -ENXIO;
- mutex_lock(&strings);
+ guard(mutex)(&strings);
if (string == NULL) {
- if ((x = snd_sndstat_strings[num][dev]) != NULL) {
- kfree(x);
- x = NULL;
- }
+ x = snd_sndstat_strings[num][dev];
+ kfree(x);
+ x = NULL;
} else {
x = kstrdup(string, GFP_KERNEL);
- if (x == NULL) {
- mutex_unlock(&strings);
+ if (x == NULL)
return -ENOMEM;
- }
}
snd_sndstat_strings[num][dev] = x;
- mutex_unlock(&strings);
return 0;
}
-
EXPORT_SYMBOL(snd_oss_info_register);
static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int dev)
@@ -73,7 +50,7 @@ static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int d
char *str;
snd_iprintf(buf, "\n%s:", id);
- mutex_lock(&strings);
+ guard(mutex)(&strings);
for (idx = 0; idx < SNDRV_CARDS; idx++) {
str = snd_sndstat_strings[idx][dev];
if (str) {
@@ -84,7 +61,6 @@ static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int d
snd_iprintf(buf, "%i: %s\n", idx, str);
}
}
- mutex_unlock(&strings);
if (ok < 0)
snd_iprintf(buf, " NOT ENABLED IN CONFIG\n");
return ok;
@@ -93,7 +69,7 @@ static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int d
static void snd_sndstat_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- snd_iprintf(buffer, "Sound Driver:3.8.1a-980706 (ALSA v" CONFIG_SND_VERSION " emulation code)\n");
+ snd_iprintf(buffer, "Sound Driver:3.8.1a-980706 (ALSA emulation code)\n");
snd_iprintf(buffer, "Kernel: %s %s %s %s %s\n",
init_utsname()->sysname,
init_utsname()->nodename,
@@ -112,27 +88,15 @@ static void snd_sndstat_proc_read(struct snd_info_entry *entry,
snd_sndstat_show_strings(buffer, "Mixers", SNDRV_OSS_INFO_DEV_MIXERS);
}
-int snd_info_minor_register(void)
+int __init snd_info_minor_register(void)
{
struct snd_info_entry *entry;
memset(snd_sndstat_strings, 0, sizeof(snd_sndstat_strings));
- if ((entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", snd_oss_root)) != NULL) {
- entry->c.text.read = snd_sndstat_proc_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- snd_sndstat_proc_entry = entry;
- return 0;
-}
-
-int snd_info_minor_unregister(void)
-{
- snd_info_free_entry(snd_sndstat_proc_entry);
- snd_sndstat_proc_entry = NULL;
- return 0;
+ entry = snd_info_create_module_entry(THIS_MODULE, "sndstat",
+ snd_oss_root);
+ if (!entry)
+ return -ENOMEM;
+ entry->c.text.read = snd_sndstat_proc_read;
+ return snd_info_register(entry); /* freed in error path */
}
-
-#endif /* CONFIG_SND_OSSEMUL */
diff --git a/sound/core/init.c b/sound/core/init.c
index 57b792e2439a..c372b3228785 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -1,31 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Initialization routines
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/device.h>
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/ctype.h>
#include <linux/pm.h>
+#include <linux/debugfs.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -44,9 +34,9 @@ static LIST_HEAD(shutdown_files);
static const struct file_operations snd_shutdown_f_ops;
-static unsigned int snd_cards_lock; /* locked for registering/using */
-struct snd_card *snd_cards[SNDRV_CARDS];
-EXPORT_SYMBOL(snd_cards);
+/* locked for registering/using */
+static DECLARE_BITMAP(snd_cards_lock, SNDRV_CARDS);
+static struct snd_card *snd_cards[SNDRV_CARDS];
static DEFINE_MUTEX(snd_card_mutex);
@@ -60,10 +50,10 @@ MODULE_PARM_DESC(slots, "Module names assigned to the slots.");
static int module_slot_match(struct module *module, int idx)
{
int match = 1;
-#ifdef MODULE
+#ifdef CONFIG_MODULES
const char *s1, *s2;
- if (!module || !module->name || !slots[idx])
+ if (!module || !*module->name || !slots[idx])
return 0;
s1 = module->name;
@@ -87,69 +77,103 @@ static int module_slot_match(struct module *module, int idx)
if (!c1)
break;
}
-#endif /* MODULE */
+#endif /* CONFIG_MODULES */
return match;
}
-#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag);
EXPORT_SYMBOL(snd_mixer_oss_notify_callback);
#endif
-#ifdef CONFIG_PROC_FS
-static void snd_card_id_read(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
+static int check_empty_slot(struct module *module, int slot)
{
- snd_iprintf(buffer, "%s\n", entry->card->id);
+ return !slots[slot] || !*slots[slot];
}
-static inline int init_info_for_card(struct snd_card *card)
+/* return an empty slot number (>= 0) found in the given bitmask @mask.
+ * @mask == -1 == 0xffffffff means: take any free slot up to 32
+ * when no slot is available, return the original @mask as is.
+ */
+static int get_slot_from_bitmask(int mask, int (*check)(struct module *, int),
+ struct module *module)
{
- int err;
- struct snd_info_entry *entry;
-
- if ((err = snd_info_card_register(card)) < 0) {
- snd_printd("unable to create card info\n");
- return err;
- }
- if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) {
- snd_printd("unable to create card entry\n");
- return err;
- }
- entry->c.text.read = snd_card_id_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
+ int slot;
+
+ for (slot = 0; slot < SNDRV_CARDS; slot++) {
+ if (slot < 32 && !(mask & (1U << slot)))
+ continue;
+ if (!test_bit(slot, snd_cards_lock)) {
+ if (check(module, slot))
+ return slot; /* found */
+ }
}
- card->proc_id = entry;
+ return mask; /* unchanged */
+}
+
+/* the default release callback set in snd_device_alloc() */
+static void default_release_alloc(struct device *dev)
+{
+ kfree(dev);
+}
+
+/**
+ * snd_device_alloc - Allocate and initialize struct device for sound devices
+ * @dev_p: pointer to store the allocated device
+ * @card: card to assign, optional
+ *
+ * For releasing the allocated device, call put_device().
+ */
+int snd_device_alloc(struct device **dev_p, struct snd_card *card)
+{
+ struct device *dev;
+
+ *dev_p = NULL;
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+ device_initialize(dev);
+ if (card)
+ dev->parent = &card->card_dev;
+ dev->class = &sound_class;
+ dev->release = default_release_alloc;
+ *dev_p = dev;
return 0;
}
-#else /* !CONFIG_PROC_FS */
-#define init_info_for_card(card)
-#endif
+EXPORT_SYMBOL_GPL(snd_device_alloc);
+
+static int snd_card_init(struct snd_card *card, struct device *parent,
+ int idx, const char *xid, struct module *module,
+ size_t extra_size);
+static int snd_card_do_free(struct snd_card *card);
+static const struct attribute_group card_dev_attr_group;
+
+static void release_card_device(struct device *dev)
+{
+ snd_card_do_free(dev_to_snd_card(dev));
+}
/**
- * snd_card_create - create and initialize a soundcard structure
+ * snd_card_new - create and initialize a soundcard structure
+ * @parent: the parent device object
* @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
* @xid: card identification (ASCII string)
* @module: top level module for locking
* @extra_size: allocate this extra size after the main soundcard structure
* @card_ret: the pointer to store the created card instance
*
- * Creates and initializes a soundcard structure.
- *
* The function allocates snd_card instance via kzalloc with the given
* space for the driver to use freely. The allocated struct is stored
* in the given card_ret pointer.
*
- * Returns zero if successful or a negative error code.
+ * Return: Zero if successful or a negative error code.
*/
-int snd_card_create(int idx, const char *xid,
+int snd_card_new(struct device *parent, int idx, const char *xid,
struct module *module, int extra_size,
struct snd_card **card_ret)
{
struct snd_card *card;
- int err, idx2;
+ int err;
if (snd_BUG_ON(!card_ret))
return -EINVAL;
@@ -160,95 +184,220 @@ int snd_card_create(int idx, const char *xid,
card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
if (!card)
return -ENOMEM;
+
+ err = snd_card_init(card, parent, idx, xid, module, extra_size);
+ if (err < 0)
+ return err; /* card is freed by error handler */
+
+ *card_ret = card;
+ return 0;
+}
+EXPORT_SYMBOL(snd_card_new);
+
+static void __snd_card_release(struct device *dev, void *data)
+{
+ snd_card_free(data);
+}
+
+/**
+ * snd_devm_card_new - managed snd_card object creation
+ * @parent: the parent device object
+ * @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
+ * @xid: card identification (ASCII string)
+ * @module: top level module for locking
+ * @extra_size: allocate this extra size after the main soundcard structure
+ * @card_ret: the pointer to store the created card instance
+ *
+ * This function works like snd_card_new() but manages the allocated resource
+ * via devres, i.e. you don't need to free explicitly.
+ *
+ * When a snd_card object is created with this function and registered via
+ * snd_card_register(), the very first devres action to call snd_card_free()
+ * is added automatically. In that way, the resource disconnection is assured
+ * at first, then released in the expected order.
+ *
+ * If an error happens at the probe before snd_card_register() is called and
+ * there have been other devres resources, you'd need to free the card manually
+ * via snd_card_free() call in the error; otherwise it may lead to UAF due to
+ * devres call orders. You can use snd_card_free_on_error() helper for
+ * handling it more easily.
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_devm_card_new(struct device *parent, int idx, const char *xid,
+ struct module *module, size_t extra_size,
+ struct snd_card **card_ret)
+{
+ struct snd_card *card;
+ int err;
+
+ *card_ret = NULL;
+ card = devres_alloc(__snd_card_release, sizeof(*card) + extra_size,
+ GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+ card->managed = true;
+ err = snd_card_init(card, parent, idx, xid, module, extra_size);
+ if (err < 0) {
+ devres_free(card); /* in managed mode, we need to free manually */
+ return err;
+ }
+
+ devres_add(parent, card);
+ *card_ret = card;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_devm_card_new);
+
+/**
+ * snd_card_free_on_error - a small helper for handling devm probe errors
+ * @dev: the managed device object
+ * @ret: the return code from the probe callback
+ *
+ * This function handles the explicit snd_card_free() call at the error from
+ * the probe callback. It's just a small helper for simplifying the error
+ * handling with the managed devices.
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_card_free_on_error(struct device *dev, int ret)
+{
+ struct snd_card *card;
+
+ if (!ret)
+ return 0;
+ card = devres_find(dev, __snd_card_release, NULL, NULL);
+ if (card)
+ snd_card_free(card);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_card_free_on_error);
+
+static int snd_card_init(struct snd_card *card, struct device *parent,
+ int idx, const char *xid, struct module *module,
+ size_t extra_size)
+{
+ int err;
+
+ if (extra_size > 0)
+ card->private_data = (char *)card + sizeof(struct snd_card);
if (xid)
- strlcpy(card->id, xid, sizeof(card->id));
+ strscpy(card->id, xid, sizeof(card->id));
err = 0;
- mutex_lock(&snd_card_mutex);
- if (idx < 0) {
- for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
- /* idx == -1 == 0xffff means: take any free slot */
- if (~snd_cards_lock & idx & 1<<idx2) {
- if (module_slot_match(module, idx2)) {
- idx = idx2;
- break;
- }
- }
- }
- if (idx < 0) {
- for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
- /* idx == -1 == 0xffff means: take any free slot */
- if (~snd_cards_lock & idx & 1<<idx2) {
- if (!slots[idx2] || !*slots[idx2]) {
- idx = idx2;
- break;
- }
- }
+ scoped_guard(mutex, &snd_card_mutex) {
+ if (idx < 0) /* first check the matching module-name slot */
+ idx = get_slot_from_bitmask(idx, module_slot_match, module);
+ if (idx < 0) /* if not matched, assign an empty slot */
+ idx = get_slot_from_bitmask(idx, check_empty_slot, module);
+ if (idx < 0)
+ err = -ENODEV;
+ else if (idx < snd_ecards_limit) {
+ if (test_bit(idx, snd_cards_lock))
+ err = -EBUSY; /* invalid */
+ } else if (idx >= SNDRV_CARDS)
+ err = -ENODEV;
+ if (!err) {
+ set_bit(idx, snd_cards_lock); /* lock it */
+ if (idx >= snd_ecards_limit)
+ snd_ecards_limit = idx + 1; /* increase the limit */
+ }
}
- if (idx < 0)
- err = -ENODEV;
- else if (idx < snd_ecards_limit) {
- if (snd_cards_lock & (1 << idx))
- err = -EBUSY; /* invalid */
- } else if (idx >= SNDRV_CARDS)
- err = -ENODEV;
if (err < 0) {
- mutex_unlock(&snd_card_mutex);
- snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i), error: %d\n",
- idx, snd_ecards_limit - 1, err);
- goto __error;
+ dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n",
+ idx, snd_ecards_limit - 1, err);
+ if (!card->managed)
+ kfree(card); /* manually free here, as no destructor called */
+ return err;
}
- snd_cards_lock |= 1 << idx; /* lock it */
- if (idx >= snd_ecards_limit)
- snd_ecards_limit = idx + 1; /* increase the limit */
- mutex_unlock(&snd_card_mutex);
+ card->dev = parent;
card->number = idx;
+ WARN_ON(IS_MODULE(CONFIG_SND) && !module);
card->module = module;
INIT_LIST_HEAD(&card->devices);
init_rwsem(&card->controls_rwsem);
- rwlock_init(&card->ctl_files_rwlock);
+ rwlock_init(&card->controls_rwlock);
INIT_LIST_HEAD(&card->controls);
INIT_LIST_HEAD(&card->ctl_files);
+#ifdef CONFIG_SND_CTL_FAST_LOOKUP
+ xa_init(&card->ctl_numids);
+ xa_init(&card->ctl_hash);
+#endif
spin_lock_init(&card->files_lock);
INIT_LIST_HEAD(&card->files_list);
- init_waitqueue_head(&card->shutdown_sleep);
+ mutex_init(&card->memory_mutex);
#ifdef CONFIG_PM
- mutex_init(&card->power_lock);
init_waitqueue_head(&card->power_sleep);
+ init_waitqueue_head(&card->power_ref_sleep);
+ atomic_set(&card->power_ref, 0);
#endif
+ init_waitqueue_head(&card->remove_sleep);
+ card->sync_irq = -1;
+
+ device_initialize(&card->card_dev);
+ card->card_dev.parent = parent;
+ card->card_dev.class = &sound_class;
+ card->card_dev.release = release_card_device;
+ card->card_dev.groups = card->dev_groups;
+ card->dev_groups[0] = &card_dev_attr_group;
+ err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);
+ if (err < 0)
+ goto __error;
+
+ snprintf(card->irq_descr, sizeof(card->irq_descr), "%s:%s",
+ dev_driver_string(card->dev), dev_name(&card->card_dev));
+
/* the control interface cannot be accessed from the user space until */
/* snd_cards_bitmask and snd_cards are set with snd_card_register */
err = snd_ctl_create(card);
if (err < 0) {
- snd_printk(KERN_ERR "unable to register control minors\n");
+ dev_err(parent, "unable to register control minors\n");
goto __error;
}
err = snd_info_card_create(card);
if (err < 0) {
- snd_printk(KERN_ERR "unable to create card info\n");
+ dev_err(parent, "unable to create card info\n");
goto __error_ctl;
}
- if (extra_size > 0)
- card->private_data = (char *)card + sizeof(struct snd_card);
- *card_ret = card;
+
+#ifdef CONFIG_SND_DEBUG
+ card->debugfs_root = debugfs_create_dir(dev_name(&card->card_dev),
+ sound_debugfs_root);
+#endif
return 0;
__error_ctl:
- snd_device_free_all(card, SNDRV_DEV_CMD_PRE);
+ snd_device_free_all(card);
__error:
- kfree(card);
+ put_device(&card->card_dev);
return err;
}
-EXPORT_SYMBOL(snd_card_create);
+
+/**
+ * snd_card_ref - Get the card object from the index
+ * @idx: the card index
+ *
+ * Returns a card object corresponding to the given index or NULL if not found.
+ * Release the object via snd_card_unref().
+ *
+ * Return: a card object or NULL
+ */
+struct snd_card *snd_card_ref(int idx)
+{
+ struct snd_card *card;
+
+ guard(mutex)(&snd_card_mutex);
+ card = snd_cards[idx];
+ if (card)
+ get_device(&card->card_dev);
+ return card;
+}
+EXPORT_SYMBOL_GPL(snd_card_ref);
/* return non-zero if a card is already locked */
int snd_card_locked(int card)
{
- int locked;
-
- mutex_lock(&snd_card_mutex);
- locked = snd_cards_lock & (1 << card);
- mutex_unlock(&snd_card_mutex);
- return locked;
+ guard(mutex)(&snd_card_mutex);
+ return test_bit(card, snd_cards_lock);
}
static loff_t snd_disconnect_llseek(struct file *file, loff_t offset, int orig)
@@ -272,15 +421,15 @@ static int snd_disconnect_release(struct inode *inode, struct file *file)
{
struct snd_monitor_file *df = NULL, *_df;
- spin_lock(&shutdown_lock);
- list_for_each_entry(_df, &shutdown_files, shutdown_list) {
- if (_df->file == file) {
- df = _df;
- list_del_init(&df->shutdown_list);
- break;
+ scoped_guard(spinlock, &shutdown_lock) {
+ list_for_each_entry(_df, &shutdown_files, shutdown_list) {
+ if (_df->file == file) {
+ df = _df;
+ list_del_init(&df->shutdown_list);
+ break;
+ }
}
}
- spin_unlock(&shutdown_lock);
if (likely(df)) {
if ((file->f_flags & FASYNC) && df->disconnected_f_op->fasync)
@@ -291,9 +440,9 @@ static int snd_disconnect_release(struct inode *inode, struct file *file)
panic("%s(%p, %p) failed!", __func__, inode, file);
}
-static unsigned int snd_disconnect_poll(struct file * file, poll_table * wait)
+static __poll_t snd_disconnect_poll(struct file * file, poll_table * wait)
{
- return POLLERR | POLLNVAL;
+ return EPOLLERR | EPOLLNVAL;
}
static long snd_disconnect_ioctl(struct file *file,
@@ -334,222 +483,275 @@ static const struct file_operations snd_shutdown_f_ops =
*
* Disconnects all APIs from the file-operations (user space).
*
- * Returns zero, otherwise a negative error code.
+ * Return: Zero, otherwise a negative error code.
*
* Note: The current implementation replaces all active file->f_op with special
* dummy file operations (they do nothing except release).
*/
-int snd_card_disconnect(struct snd_card *card)
+void snd_card_disconnect(struct snd_card *card)
{
struct snd_monitor_file *mfile;
- struct file *file;
- int err;
if (!card)
- return -EINVAL;
-
- spin_lock(&card->files_lock);
- if (card->shutdown) {
- spin_unlock(&card->files_lock);
- return 0;
- }
- card->shutdown = 1;
- spin_unlock(&card->files_lock);
+ return;
- /* phase 1: disable fops (user space) operations for ALSA API */
- mutex_lock(&snd_card_mutex);
- snd_cards[card->number] = NULL;
- snd_cards_lock &= ~(1 << card->number);
- mutex_unlock(&snd_card_mutex);
-
- /* phase 2: replace file->f_op with special dummy operations */
-
- spin_lock(&card->files_lock);
- list_for_each_entry(mfile, &card->files_list, list) {
- file = mfile->file;
+ scoped_guard(spinlock, &card->files_lock) {
+ if (card->shutdown)
+ return;
+ card->shutdown = 1;
- /* it's critical part, use endless loop */
- /* we have no room to fail */
- mfile->disconnected_f_op = mfile->file->f_op;
+ /* replace file->f_op with special dummy operations */
+ list_for_each_entry(mfile, &card->files_list, list) {
+ /* it's critical part, use endless loop */
+ /* we have no room to fail */
+ mfile->disconnected_f_op = mfile->file->f_op;
- spin_lock(&shutdown_lock);
- list_add(&mfile->shutdown_list, &shutdown_files);
- spin_unlock(&shutdown_lock);
+ scoped_guard(spinlock, &shutdown_lock)
+ list_add(&mfile->shutdown_list, &shutdown_files);
- mfile->file->f_op = &snd_shutdown_f_ops;
- fops_get(mfile->file->f_op);
+ mfile->file->f_op = &snd_shutdown_f_ops;
+ fops_get(mfile->file->f_op);
+ }
}
- spin_unlock(&card->files_lock);
- /* phase 3: notify all connected devices about disconnection */
+#ifdef CONFIG_PM
+ /* wake up sleepers here before other callbacks for avoiding potential
+ * deadlocks with other locks (e.g. in kctls);
+ * then this notifies the shutdown and sleepers would abort immediately
+ */
+ wake_up_all(&card->power_sleep);
+#endif
+
+ /* notify all connected devices about disconnection */
/* at this point, they cannot respond to any calls except release() */
-#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_DISCONNECT);
#endif
/* notify all devices that we are disconnected */
- err = snd_device_disconnect_all(card);
- if (err < 0)
- snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number);
+ snd_device_disconnect_all(card);
+
+ if (card->sync_irq > 0)
+ synchronize_irq(card->sync_irq);
snd_info_card_disconnect(card);
- if (card->card_dev) {
- device_unregister(card->card_dev);
- card->card_dev = NULL;
- }
-#ifdef CONFIG_PM
- wake_up(&card->power_sleep);
+#ifdef CONFIG_SND_DEBUG
+ debugfs_remove(card->debugfs_root);
+ card->debugfs_root = NULL;
#endif
- return 0;
-}
+ if (card->registered) {
+ device_del(&card->card_dev);
+ card->registered = false;
+ }
+
+ /* disable fops (user space) operations for ALSA API */
+ scoped_guard(mutex, &snd_card_mutex) {
+ snd_cards[card->number] = NULL;
+ clear_bit(card->number, snd_cards_lock);
+ }
+
+ snd_power_sync_ref(card);
+}
EXPORT_SYMBOL(snd_card_disconnect);
/**
- * snd_card_free - frees given soundcard structure
- * @card: soundcard structure
- *
- * This function releases the soundcard structure and the all assigned
- * devices automatically. That is, you don't have to release the devices
- * by yourself.
+ * snd_card_disconnect_sync - disconnect card and wait until files get closed
+ * @card: card object to disconnect
*
- * Returns zero. Frees all associated devices and frees the control
- * interface associated to given soundcard.
+ * This calls snd_card_disconnect() for disconnecting all belonging components
+ * and waits until all pending files get closed.
+ * It assures that all accesses from user-space finished so that the driver
+ * can release its resources gracefully.
*/
+void snd_card_disconnect_sync(struct snd_card *card)
+{
+ snd_card_disconnect(card);
+
+ guard(spinlock_irq)(&card->files_lock);
+ wait_event_lock_irq(card->remove_sleep,
+ list_empty(&card->files_list),
+ card->files_lock);
+}
+EXPORT_SYMBOL_GPL(snd_card_disconnect_sync);
+
static int snd_card_do_free(struct snd_card *card)
{
-#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+ card->releasing = true;
+#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
#endif
- if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) {
- snd_printk(KERN_ERR "unable to free all devices (pre)\n");
- /* Fatal, but this situation should never occur */
- }
- if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) {
- snd_printk(KERN_ERR "unable to free all devices (normal)\n");
- /* Fatal, but this situation should never occur */
- }
- if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) {
- snd_printk(KERN_ERR "unable to free all devices (post)\n");
- /* Fatal, but this situation should never occur */
- }
+ snd_device_free_all(card);
if (card->private_free)
card->private_free(card);
- snd_info_free_entry(card->proc_id);
if (snd_info_card_free(card) < 0) {
- snd_printk(KERN_WARNING "unable to free card info\n");
+ dev_warn(card->dev, "unable to free card info\n");
/* Not fatal error */
}
- kfree(card);
+ if (card->release_completion)
+ complete(card->release_completion);
+ if (!card->managed)
+ kfree(card);
return 0;
}
-int snd_card_free_when_closed(struct snd_card *card)
+/**
+ * snd_card_free_when_closed - Disconnect the card, free it later eventually
+ * @card: soundcard structure
+ *
+ * Unlike snd_card_free(), this function doesn't try to release the card
+ * resource immediately, but tries to disconnect at first. When the card
+ * is still in use, the function returns before freeing the resources.
+ * The card resources will be freed when the refcount gets to zero.
+ *
+ * Return: zero if successful, or a negative error code
+ */
+void snd_card_free_when_closed(struct snd_card *card)
{
- int free_now = 0;
- int ret = snd_card_disconnect(card);
- if (ret)
- return ret;
-
- spin_lock(&card->files_lock);
- if (list_empty(&card->files_list))
- free_now = 1;
- else
- card->free_on_last_close = 1;
- spin_unlock(&card->files_lock);
+ if (!card)
+ return;
- if (free_now)
- snd_card_do_free(card);
- return 0;
+ snd_card_disconnect(card);
+ put_device(&card->card_dev);
+ return;
}
-
EXPORT_SYMBOL(snd_card_free_when_closed);
-int snd_card_free(struct snd_card *card)
+/**
+ * snd_card_free - frees given soundcard structure
+ * @card: soundcard structure
+ *
+ * This function releases the soundcard structure and the all assigned
+ * devices automatically. That is, you don't have to release the devices
+ * by yourself.
+ *
+ * This function waits until the all resources are properly released.
+ *
+ * Return: Zero. Frees all associated devices and frees the control
+ * interface associated to given soundcard.
+ */
+void snd_card_free(struct snd_card *card)
{
- int ret = snd_card_disconnect(card);
- if (ret)
- return ret;
+ DECLARE_COMPLETION_ONSTACK(released);
+
+ /* The call of snd_card_free() is allowed from various code paths;
+ * a manual call from the driver and the call via devres_free, and
+ * we need to avoid double-free. Moreover, the release via devres
+ * may call snd_card_free() twice due to its nature, we need to have
+ * the check here at the beginning.
+ */
+ if (card->releasing)
+ return;
+
+ card->release_completion = &released;
+ snd_card_free_when_closed(card);
/* wait, until all devices are ready for the free operation */
- wait_event(card->shutdown_sleep, list_empty(&card->files_list));
- snd_card_do_free(card);
- return 0;
+ wait_for_completion(&released);
}
-
EXPORT_SYMBOL(snd_card_free);
-static void snd_card_set_id_no_lock(struct snd_card *card, const char *nid)
+/* check, if the character is in the valid ASCII range */
+static inline bool safe_ascii_char(char c)
{
- int i, len, idx_flag = 0, loops = SNDRV_CARDS;
- const char *spos, *src;
+ return isascii(c) && isalnum(c);
+}
+
+/* retrieve the last word of shortname or longname */
+static const char *retrieve_id_from_card_name(const char *name)
+{
+ const char *spos = name;
+
+ while (*name) {
+ if (isspace(*name) && safe_ascii_char(name[1]))
+ spos = name + 1;
+ name++;
+ }
+ return spos;
+}
+
+/* return true if the given id string doesn't conflict any other card ids */
+static bool card_id_ok(struct snd_card *card, const char *id)
+{
+ int i;
+ if (!snd_info_check_reserved_words(id))
+ return false;
+ for (i = 0; i < snd_ecards_limit; i++) {
+ if (snd_cards[i] && snd_cards[i] != card &&
+ !strcmp(snd_cards[i]->id, id))
+ return false;
+ }
+ return true;
+}
+
+/* copy to card->id only with valid letters from nid */
+static void copy_valid_id_string(struct snd_card *card, const char *src,
+ const char *nid)
+{
+ char *id = card->id;
+
+ while (*nid && !safe_ascii_char(*nid))
+ nid++;
+ if (isdigit(*nid))
+ *id++ = isalpha(*src) ? *src : 'D';
+ while (*nid && (size_t)(id - card->id) < sizeof(card->id) - 1) {
+ if (safe_ascii_char(*nid))
+ *id++ = *nid;
+ nid++;
+ }
+ *id = 0;
+}
+
+/* Set card->id from the given string
+ * If the string conflicts with other ids, add a suffix to make it unique.
+ */
+static void snd_card_set_id_no_lock(struct snd_card *card, const char *src,
+ const char *nid)
+{
+ int len, loops;
+ bool is_default = false;
char *id;
- if (nid == NULL) {
- id = card->shortname;
- spos = src = id;
- while (*id != '\0') {
- if (*id == ' ')
- spos = id + 1;
- id++;
- }
- } else {
- spos = src = nid;
- }
+ copy_valid_id_string(card, src, nid);
id = card->id;
- while (*spos != '\0' && !isalnum(*spos))
- spos++;
- if (isdigit(*spos))
- *id++ = isalpha(src[0]) ? src[0] : 'D';
- while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) {
- if (isalnum(*spos))
- *id++ = *spos;
- spos++;
+
+ again:
+ /* use "Default" for obviously invalid strings
+ * ("card" conflicts with proc directories)
+ */
+ if (!*id || !strncmp(id, "card", 4)) {
+ strscpy(card->id, "Default");
+ is_default = true;
}
- *id = '\0';
- id = card->id;
-
- if (*id == '\0')
- strcpy(id, "default");
-
- while (1) {
- if (loops-- == 0) {
- snd_printk(KERN_ERR "unable to set card id (%s)\n", id);
- strcpy(card->id, card->proc_root->name);
- return;
- }
- if (!snd_info_check_reserved_words(id))
- goto __change;
- for (i = 0; i < snd_ecards_limit; i++) {
- if (snd_cards[i] && !strcmp(snd_cards[i]->id, id))
- goto __change;
- }
- break;
-
- __change:
- len = strlen(id);
- if (idx_flag) {
- if (id[len-1] != '9')
- id[len-1]++;
- else
- id[len-1] = 'A';
- } else if ((size_t)len <= sizeof(card->id) - 3) {
- strcat(id, "_1");
- idx_flag++;
- } else {
- spos = id + len - 2;
- if ((size_t)len <= sizeof(card->id) - 2)
- spos++;
- *(char *)spos++ = '_';
- *(char *)spos++ = '1';
- *(char *)spos++ = '\0';
- idx_flag++;
- }
+ len = strlen(id);
+ for (loops = 0; loops < SNDRV_CARDS; loops++) {
+ char sfxstr[5]; /* "_012" */
+ int sfxlen, slen;
+
+ if (card_id_ok(card, id))
+ return; /* OK */
+
+ /* Add _XYZ suffix */
+ sfxlen = scnprintf(sfxstr, sizeof(sfxstr), "_%X", loops + 1);
+ if (len + sfxlen >= sizeof(card->id))
+ slen = sizeof(card->id) - sfxlen - 1;
+ else
+ slen = len;
+ strscpy(id + slen, sfxstr, sizeof(card->id) - slen);
}
+ /* fallback to the default id */
+ if (!is_default) {
+ *id = 0;
+ goto again;
+ }
+ /* last resort... */
+ dev_err(card->dev, "unable to set card id (%s)\n", id);
+ if (card->proc_root->name)
+ strscpy(card->id, card->proc_root->name, sizeof(card->id));
}
/**
@@ -565,25 +767,22 @@ void snd_card_set_id(struct snd_card *card, const char *nid)
/* check if user specified own card->id */
if (card->id[0] != '\0')
return;
- mutex_lock(&snd_card_mutex);
- snd_card_set_id_no_lock(card, nid);
- mutex_unlock(&snd_card_mutex);
+ guard(mutex)(&snd_card_mutex);
+ snd_card_set_id_no_lock(card, nid, nid);
}
EXPORT_SYMBOL(snd_card_set_id);
-static ssize_t
-card_id_show_attr(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- struct snd_card *card = dev_get_drvdata(dev);
- return snprintf(buf, PAGE_SIZE, "%s\n", card ? card->id : "(null)");
+ struct snd_card *card = container_of(dev, struct snd_card, card_dev);
+ return sysfs_emit(buf, "%s\n", card->id);
}
-static ssize_t
-card_id_store_attr(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t id_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
- struct snd_card *card = dev_get_drvdata(dev);
+ struct snd_card *card = container_of(dev, struct snd_card, card_dev);
char buf1[sizeof(card->id)];
size_t copy = count > sizeof(card->id) - 1 ?
sizeof(card->id) - 1 : count;
@@ -592,46 +791,70 @@ card_id_store_attr(struct device *dev, struct device_attribute *attr,
for (idx = 0; idx < copy; idx++) {
c = buf[idx];
- if (!isalnum(c) && c != '_' && c != '-')
+ if (!safe_ascii_char(c) && c != '_' && c != '-')
return -EINVAL;
}
memcpy(buf1, buf, copy);
buf1[copy] = '\0';
- mutex_lock(&snd_card_mutex);
- if (!snd_info_check_reserved_words(buf1)) {
- __exist:
- mutex_unlock(&snd_card_mutex);
+ guard(mutex)(&snd_card_mutex);
+ if (!card_id_ok(NULL, buf1))
return -EEXIST;
- }
- for (idx = 0; idx < snd_ecards_limit; idx++) {
- if (snd_cards[idx] && !strcmp(snd_cards[idx]->id, buf1)) {
- if (card == snd_cards[idx])
- goto __ok;
- else
- goto __exist;
- }
- }
- strcpy(card->id, buf1);
+ strscpy(card->id, buf1);
snd_info_card_id_change(card);
-__ok:
- mutex_unlock(&snd_card_mutex);
return count;
}
-static struct device_attribute card_id_attrs =
- __ATTR(id, S_IRUGO | S_IWUSR, card_id_show_attr, card_id_store_attr);
+static DEVICE_ATTR_RW(id);
+
+static ssize_t number_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_card *card = container_of(dev, struct snd_card, card_dev);
+ return sysfs_emit(buf, "%i\n", card->number);
+}
+
+static DEVICE_ATTR_RO(number);
+
+static struct attribute *card_dev_attrs[] = {
+ &dev_attr_id.attr,
+ &dev_attr_number.attr,
+ NULL
+};
+
+static const struct attribute_group card_dev_attr_group = {
+ .attrs = card_dev_attrs,
+};
-static ssize_t
-card_number_show_attr(struct device *dev,
- struct device_attribute *attr, char *buf)
+/**
+ * snd_card_add_dev_attr - Append a new sysfs attribute group to card
+ * @card: card instance
+ * @group: attribute group to append
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_card_add_dev_attr(struct snd_card *card,
+ const struct attribute_group *group)
{
- struct snd_card *card = dev_get_drvdata(dev);
- return snprintf(buf, PAGE_SIZE, "%i\n", card ? card->number : -1);
+ int i;
+
+ /* loop for (arraysize-1) here to keep NULL at the last entry */
+ for (i = 0; i < ARRAY_SIZE(card->dev_groups) - 1; i++) {
+ if (!card->dev_groups[i]) {
+ card->dev_groups[i] = group;
+ return 0;
+ }
+ }
+
+ dev_err(card->dev, "Too many groups assigned\n");
+ return -ENOSPC;
}
+EXPORT_SYMBOL_GPL(snd_card_add_dev_attr);
-static struct device_attribute card_number_attrs =
- __ATTR(number, S_IRUGO, card_number_show_attr, NULL);
+static void trigger_card_free(void *data)
+{
+ snd_card_free(data);
+}
/**
* snd_card_register - register the soundcard
@@ -642,7 +865,7 @@ static struct device_attribute card_number_attrs =
* external accesses. Thus, you should call this function at the end
* of the initialization of the card.
*
- * Returns zero otherwise a negative error code if the registrain failed.
+ * Return: Zero otherwise a negative error code if the registration failed.
*/
int snd_card_register(struct snd_card *card)
{
@@ -651,47 +874,59 @@ int snd_card_register(struct snd_card *card)
if (snd_BUG_ON(!card))
return -EINVAL;
- if (!card->card_dev) {
- card->card_dev = device_create(sound_class, card->dev,
- MKDEV(0, 0), card,
- "card%i", card->number);
- if (IS_ERR(card->card_dev))
- card->card_dev = NULL;
+ if (!card->registered) {
+ err = device_add(&card->card_dev);
+ if (err < 0)
+ return err;
+ card->registered = true;
+ } else {
+ if (card->managed)
+ devm_remove_action(card->dev, trigger_card_free, card);
+ }
+
+ if (card->managed) {
+ err = devm_add_action(card->dev, trigger_card_free, card);
+ if (err < 0)
+ return err;
}
- if ((err = snd_device_register_all(card)) < 0)
+ err = snd_device_register_all(card);
+ if (err < 0)
return err;
- mutex_lock(&snd_card_mutex);
- if (snd_cards[card->number]) {
- /* already registered */
- mutex_unlock(&snd_card_mutex);
- return 0;
+ scoped_guard(mutex, &snd_card_mutex) {
+ if (snd_cards[card->number]) {
+ /* already registered */
+ return snd_info_card_register(card); /* register pending info */
+ }
+ if (*card->id) {
+ /* make a unique id name from the given string */
+ char tmpid[sizeof(card->id)];
+
+ memcpy(tmpid, card->id, sizeof(card->id));
+ snd_card_set_id_no_lock(card, tmpid, tmpid);
+ } else {
+ /* create an id from either shortname or longname */
+ const char *src;
+
+ src = *card->shortname ? card->shortname : card->longname;
+ snd_card_set_id_no_lock(card, src,
+ retrieve_id_from_card_name(src));
+ }
+ snd_cards[card->number] = card;
}
- snd_card_set_id_no_lock(card, card->id[0] == '\0' ? NULL : card->id);
- snd_cards[card->number] = card;
- mutex_unlock(&snd_card_mutex);
- init_info_for_card(card);
-#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
+ err = snd_info_card_register(card);
+ if (err < 0)
+ return err;
+
+#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
#endif
- if (card->card_dev) {
- err = device_create_file(card->card_dev, &card_id_attrs);
- if (err < 0)
- return err;
- err = device_create_file(card->card_dev, &card_number_attrs);
- if (err < 0)
- return err;
- }
-
return 0;
}
-
EXPORT_SYMBOL(snd_card_register);
-#ifdef CONFIG_PROC_FS
-static struct snd_info_entry *snd_card_info_entry;
-
+#ifdef CONFIG_SND_PROC_FS
static void snd_card_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -699,8 +934,9 @@ static void snd_card_info_read(struct snd_info_entry *entry,
struct snd_card *card;
for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
- mutex_lock(&snd_card_mutex);
- if ((card = snd_cards[idx]) != NULL) {
+ guard(mutex)(&snd_card_mutex);
+ card = snd_cards[idx];
+ if (card) {
count++;
snd_iprintf(buffer, "%2i [%-15s]: %s - %s\n",
idx,
@@ -710,26 +946,24 @@ static void snd_card_info_read(struct snd_info_entry *entry,
snd_iprintf(buffer, " %s\n",
card->longname);
}
- mutex_unlock(&snd_card_mutex);
}
if (!count)
snd_iprintf(buffer, "--- no soundcards ---\n");
}
#ifdef CONFIG_SND_OSSEMUL
-
void snd_card_info_read_oss(struct snd_info_buffer *buffer)
{
int idx, count;
struct snd_card *card;
for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
- mutex_lock(&snd_card_mutex);
- if ((card = snd_cards[idx]) != NULL) {
+ guard(mutex)(&snd_card_mutex);
+ card = snd_cards[idx];
+ if (card) {
count++;
snd_iprintf(buffer, "%s\n", card->longname);
}
- mutex_unlock(&snd_card_mutex);
}
if (!count) {
snd_iprintf(buffer, "--- no soundcards ---\n");
@@ -738,8 +972,7 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer)
#endif
-#ifdef MODULE
-static struct snd_info_entry *snd_card_module_info_entry;
+#ifdef CONFIG_MODULES
static void snd_card_module_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -747,11 +980,11 @@ static void snd_card_module_info_read(struct snd_info_entry *entry,
struct snd_card *card;
for (idx = 0; idx < SNDRV_CARDS; idx++) {
- mutex_lock(&snd_card_mutex);
- if ((card = snd_cards[idx]) != NULL)
+ guard(mutex)(&snd_card_mutex);
+ card = snd_cards[idx];
+ if (card)
snd_iprintf(buffer, "%2i %s\n",
idx, card->module->name);
- mutex_unlock(&snd_card_mutex);
}
}
#endif
@@ -764,36 +997,21 @@ int __init snd_card_info_init(void)
if (! entry)
return -ENOMEM;
entry->c.text.read = snd_card_info_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- return -ENOMEM;
- }
- snd_card_info_entry = entry;
+ if (snd_info_register(entry) < 0)
+ return -ENOMEM; /* freed in error path */
-#ifdef MODULE
+#ifdef CONFIG_MODULES
entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL);
- if (entry) {
- entry->c.text.read = snd_card_module_info_read;
- if (snd_info_register(entry) < 0)
- snd_info_free_entry(entry);
- else
- snd_card_module_info_entry = entry;
- }
+ if (!entry)
+ return -ENOMEM;
+ entry->c.text.read = snd_card_module_info_read;
+ if (snd_info_register(entry) < 0)
+ return -ENOMEM; /* freed in error path */
#endif
return 0;
}
-
-int __exit snd_card_info_done(void)
-{
- snd_info_free_entry(snd_card_info_entry);
-#ifdef MODULE
- snd_info_free_entry(snd_card_module_info_entry);
-#endif
- return 0;
-}
-
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
/**
* snd_component_add - add a component string
@@ -803,7 +1021,7 @@ int __exit snd_card_info_done(void)
* This function adds the component id string to the supported list.
* The component can be referred from the alsa-lib.
*
- * Returns zero otherwise a negative error code.
+ * Return: Zero otherwise a negative error code.
*/
int snd_component_add(struct snd_card *card, const char *component)
@@ -825,7 +1043,6 @@ int snd_component_add(struct snd_card *card, const char *component)
strcat(card->components, component);
return 0;
}
-
EXPORT_SYMBOL(snd_component_add);
/**
@@ -837,7 +1054,7 @@ EXPORT_SYMBOL(snd_component_add);
* This linked-list is used to keep tracking the connection state,
* and to avoid the release of busy resources by hotplug.
*
- * Returns zero or a negative error code.
+ * Return: zero or a negative error code.
*/
int snd_card_file_add(struct snd_card *card, struct file *file)
{
@@ -848,17 +1065,16 @@ int snd_card_file_add(struct snd_card *card, struct file *file)
return -ENOMEM;
mfile->file = file;
mfile->disconnected_f_op = NULL;
- spin_lock(&card->files_lock);
+ INIT_LIST_HEAD(&mfile->shutdown_list);
+ guard(spinlock)(&card->files_lock);
if (card->shutdown) {
- spin_unlock(&card->files_lock);
kfree(mfile);
return -ENODEV;
}
list_add(&mfile->list, &card->files_list);
- spin_unlock(&card->files_lock);
+ get_device(&card->card_dev);
return 0;
}
-
EXPORT_SYMBOL(snd_card_file_add);
/**
@@ -872,76 +1088,81 @@ EXPORT_SYMBOL(snd_card_file_add);
* called beforehand, it processes the pending release of
* resources.
*
- * Returns zero or a negative error code.
+ * Return: Zero or a negative error code.
*/
int snd_card_file_remove(struct snd_card *card, struct file *file)
{
struct snd_monitor_file *mfile, *found = NULL;
- int last_close = 0;
-
- spin_lock(&card->files_lock);
- list_for_each_entry(mfile, &card->files_list, list) {
- if (mfile->file == file) {
- list_del(&mfile->list);
- if (mfile->disconnected_f_op)
- fops_put(mfile->disconnected_f_op);
- found = mfile;
- break;
+
+ scoped_guard(spinlock, &card->files_lock) {
+ list_for_each_entry(mfile, &card->files_list, list) {
+ if (mfile->file == file) {
+ list_del(&mfile->list);
+ scoped_guard(spinlock, &shutdown_lock)
+ list_del(&mfile->shutdown_list);
+ if (mfile->disconnected_f_op)
+ fops_put(mfile->disconnected_f_op);
+ found = mfile;
+ break;
+ }
}
- }
- if (list_empty(&card->files_list))
- last_close = 1;
- spin_unlock(&card->files_lock);
- if (last_close) {
- wake_up(&card->shutdown_sleep);
- if (card->free_on_last_close)
- snd_card_do_free(card);
+ if (list_empty(&card->files_list))
+ wake_up_all(&card->remove_sleep);
}
if (!found) {
- snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file);
+ dev_err(card->dev, "card file remove problem (%p)\n", file);
return -ENOENT;
}
kfree(found);
+ put_device(&card->card_dev);
return 0;
}
-
EXPORT_SYMBOL(snd_card_file_remove);
#ifdef CONFIG_PM
/**
- * snd_power_wait - wait until the power-state is changed.
- * @card: soundcard structure
- * @power_state: expected power state
+ * snd_power_ref_and_wait - wait until the card gets powered up
+ * @card: soundcard structure
+ *
+ * Take the power_ref reference count of the given card, and
+ * wait until the card gets powered up to SNDRV_CTL_POWER_D0 state.
+ * The refcount is down again while sleeping until power-up, hence this
+ * function can be used for syncing the floating control ops accesses,
+ * typically around calling control ops.
*
- * Waits until the power-state is changed.
+ * The caller needs to pull down the refcount via snd_power_unref() later
+ * no matter whether the error is returned from this function or not.
*
- * Note: the power lock must be active before call.
+ * Return: Zero if successful, or a negative error code.
*/
-int snd_power_wait(struct snd_card *card, unsigned int power_state)
+int snd_power_ref_and_wait(struct snd_card *card)
{
- wait_queue_t wait;
- int result = 0;
-
- /* fastpath */
- if (snd_power_get_state(card) == power_state)
+ snd_power_ref(card);
+ if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0)
return 0;
- init_waitqueue_entry(&wait, current);
- add_wait_queue(&card->power_sleep, &wait);
- while (1) {
- if (card->shutdown) {
- result = -ENODEV;
- break;
- }
- if (snd_power_get_state(card) == power_state)
- break;
- set_current_state(TASK_UNINTERRUPTIBLE);
- snd_power_unlock(card);
- schedule_timeout(30 * HZ);
- snd_power_lock(card);
- }
- remove_wait_queue(&card->power_sleep, &wait);
- return result;
+ wait_event_cmd(card->power_sleep,
+ card->shutdown ||
+ snd_power_get_state(card) == SNDRV_CTL_POWER_D0,
+ snd_power_unref(card), snd_power_ref(card));
+ return card->shutdown ? -ENODEV : 0;
}
+EXPORT_SYMBOL_GPL(snd_power_ref_and_wait);
+
+/**
+ * snd_power_wait - wait until the card gets powered up (old form)
+ * @card: soundcard structure
+ *
+ * Wait until the card gets powered up to SNDRV_CTL_POWER_D0 state.
+ *
+ * Return: Zero if successful, or a negative error code.
+ */
+int snd_power_wait(struct snd_card *card)
+{
+ int ret;
+ ret = snd_power_ref_and_wait(card);
+ snd_power_unref(card);
+ return ret;
+}
EXPORT_SYMBOL(snd_power_wait);
#endif /* CONFIG_PM */
diff --git a/sound/core/isadma.c b/sound/core/isadma.c
index 950e19ba91fc..28768061d769 100644
--- a/sound/core/isadma.c
+++ b/sound/core/isadma.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ISA DMA support functions
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -26,8 +11,9 @@
#undef HAVE_REALLY_SLOW_DMA_CONTROLLER
+#include <linux/export.h>
+#include <linux/isa-dma.h>
#include <sound/core.h>
-#include <asm/dma.h>
/**
* snd_dma_program - program an ISA DMA transfer
@@ -54,7 +40,6 @@ void snd_dma_program(unsigned long dma,
enable_dma(dma);
release_dma_lock(flags);
}
-
EXPORT_SYMBOL(snd_dma_program);
/**
@@ -72,7 +57,6 @@ void snd_dma_disable(unsigned long dma)
disable_dma(dma);
release_dma_lock(flags);
}
-
EXPORT_SYMBOL(snd_dma_disable);
/**
@@ -80,7 +64,7 @@ EXPORT_SYMBOL(snd_dma_disable);
* @dma: the dma number
* @size: the dma transfer size
*
- * Returns the current pointer in DMA tranfer buffer in bytes
+ * Return: The current pointer in DMA transfer buffer in bytes.
*/
unsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
{
@@ -105,12 +89,50 @@ unsigned int snd_dma_pointer(unsigned long dma, unsigned int size)
result = result1;
#ifdef CONFIG_SND_DEBUG
if (result > size)
- snd_printk(KERN_ERR "pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size);
+ pr_err("ALSA: pointer (0x%x) for DMA #%ld is greater than transfer size (0x%x)\n", result, dma, size);
#endif
if (result >= size || result == 0)
return 0;
else
return size - result;
}
-
EXPORT_SYMBOL(snd_dma_pointer);
+
+struct snd_dma_data {
+ int dma;
+};
+
+static void __snd_release_dma(struct device *dev, void *data)
+{
+ struct snd_dma_data *p = data;
+
+ snd_dma_disable(p->dma);
+ free_dma(p->dma);
+}
+
+/**
+ * snd_devm_request_dma - the managed version of request_dma()
+ * @dev: the device pointer
+ * @dma: the dma number
+ * @name: the name string of the requester
+ *
+ * The requested DMA will be automatically released at unbinding via devres.
+ *
+ * Return: zero on success, or a negative error code
+ */
+int snd_devm_request_dma(struct device *dev, int dma, const char *name)
+{
+ struct snd_dma_data *p;
+
+ if (request_dma(dma, name))
+ return -EBUSY;
+ p = devres_alloc(__snd_release_dma, sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ free_dma(dma);
+ return -ENOMEM;
+ }
+ p->dma = dma;
+ devres_add(dev, p);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_devm_request_dma);
diff --git a/sound/core/jack.c b/sound/core/jack.c
index 4902ae568730..93e357a23f17 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -1,43 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Jack abstraction layer
*
* Copyright 2008 Wolfson Microelectronics
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/input.h>
#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/debugfs.h>
#include <sound/jack.h>
#include <sound/core.h>
+#include <sound/control.h>
+
+struct snd_jack_kctl {
+ struct snd_kcontrol *kctl;
+ struct list_head list; /* list of controls belong to the same jack */
+ unsigned int mask_bits; /* only masked status bits are reported via kctl */
+ struct snd_jack *jack; /* pointer to struct snd_jack */
+ bool sw_inject_enable; /* allow to inject plug event via debugfs */
+#ifdef CONFIG_SND_JACK_INJECTION_DEBUG
+ struct dentry *jack_debugfs_root; /* jack_kctl debugfs root */
+#endif
+};
-static int jack_switch_types[] = {
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+static const int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
SW_HEADPHONE_INSERT,
SW_MICROPHONE_INSERT,
SW_LINEOUT_INSERT,
SW_JACK_PHYSICAL_INSERT,
SW_VIDEOOUT_INSERT,
+ SW_LINEIN_INSERT,
+ SW_USB_INSERT,
};
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
-static int snd_jack_dev_free(struct snd_device *device)
+static void snd_jack_remove_debugfs(struct snd_jack *jack);
+
+static int snd_jack_dev_disconnect(struct snd_device *device)
{
struct snd_jack *jack = device->device_data;
- if (jack->private_free)
- jack->private_free(jack);
+ snd_jack_remove_debugfs(jack);
+
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+ guard(mutex)(&jack->input_dev_lock);
+ if (!jack->input_dev)
+ return 0;
/* If the input device is registered with the input subsystem
* then we need to use a different deallocator. */
@@ -45,6 +57,26 @@ static int snd_jack_dev_free(struct snd_device *device)
input_unregister_device(jack->input_dev);
else
input_free_device(jack->input_dev);
+ jack->input_dev = NULL;
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
+ return 0;
+}
+
+static int snd_jack_dev_free(struct snd_device *device)
+{
+ struct snd_jack *jack = device->device_data;
+ struct snd_card *card = device->card;
+ struct snd_jack_kctl *jack_kctl, *tmp_jack_kctl;
+
+ list_for_each_entry_safe(jack_kctl, tmp_jack_kctl, &jack->kctl_list, list) {
+ list_del_init(&jack_kctl->list);
+ snd_ctl_remove(card, jack_kctl->kctl);
+ }
+
+ if (jack->private_free)
+ jack->private_free(jack);
+
+ snd_jack_dev_disconnect(device);
kfree(jack->id);
kfree(jack);
@@ -52,6 +84,7 @@ static int snd_jack_dev_free(struct snd_device *device)
return 0;
}
+#ifdef CONFIG_SND_JACK_INPUT_DEV
static int snd_jack_dev_register(struct snd_device *device)
{
struct snd_jack *jack = device->device_data;
@@ -60,6 +93,11 @@ static int snd_jack_dev_register(struct snd_device *device)
snprintf(jack->name, sizeof(jack->name), "%s %s",
card->shortname, jack->id);
+
+ guard(mutex)(&jack->input_dev_lock);
+ if (!jack->input_dev)
+ return 0;
+
jack->input_dev->name = jack->name;
/* Default to the sound card device. */
@@ -85,6 +123,363 @@ static int snd_jack_dev_register(struct snd_device *device)
return err;
}
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
+
+#ifdef CONFIG_SND_JACK_INJECTION_DEBUG
+static void snd_jack_inject_report(struct snd_jack_kctl *jack_kctl, int status)
+{
+ struct snd_jack *jack;
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+ int i;
+#endif
+ if (!jack_kctl)
+ return;
+
+ jack = jack_kctl->jack;
+
+ if (jack_kctl->sw_inject_enable)
+ snd_kctl_jack_report(jack->card, jack_kctl->kctl,
+ status & jack_kctl->mask_bits);
+
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+ if (!jack->input_dev)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
+ int testbit = ((SND_JACK_BTN_0 >> i) & jack_kctl->mask_bits);
+
+ if (jack->type & testbit)
+ input_report_key(jack->input_dev, jack->key[i],
+ status & testbit);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++) {
+ int testbit = ((1 << i) & jack_kctl->mask_bits);
+
+ if (jack->type & testbit)
+ input_report_switch(jack->input_dev,
+ jack_switch_types[i],
+ status & testbit);
+ }
+
+ input_sync(jack->input_dev);
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
+}
+
+static ssize_t sw_inject_enable_read(struct file *file,
+ char __user *to, size_t count, loff_t *ppos)
+{
+ struct snd_jack_kctl *jack_kctl = file->private_data;
+ int len, ret;
+ char buf[128];
+
+ len = scnprintf(buf, sizeof(buf), "%s: %s\t\t%s: %i\n", "Jack", jack_kctl->kctl->id.name,
+ "Inject Enabled", jack_kctl->sw_inject_enable);
+ ret = simple_read_from_buffer(to, count, ppos, buf, len);
+
+ return ret;
+}
+
+static ssize_t sw_inject_enable_write(struct file *file,
+ const char __user *from, size_t count, loff_t *ppos)
+{
+ struct snd_jack_kctl *jack_kctl = file->private_data;
+ int ret, err;
+ unsigned long enable;
+ char buf[8] = { 0 };
+
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, from, count);
+ err = kstrtoul(buf, 0, &enable);
+ if (err)
+ return err;
+
+ if (jack_kctl->sw_inject_enable == (!!enable))
+ return ret;
+
+ jack_kctl->sw_inject_enable = !!enable;
+
+ if (!jack_kctl->sw_inject_enable)
+ snd_jack_report(jack_kctl->jack, jack_kctl->jack->hw_status_cache);
+
+ return ret;
+}
+
+static ssize_t jackin_inject_write(struct file *file,
+ const char __user *from, size_t count, loff_t *ppos)
+{
+ struct snd_jack_kctl *jack_kctl = file->private_data;
+ int ret, err;
+ unsigned long enable;
+ char buf[8] = { 0 };
+
+ if (!jack_kctl->sw_inject_enable)
+ return -EINVAL;
+
+ ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, from, count);
+ err = kstrtoul(buf, 0, &enable);
+ if (err)
+ return err;
+
+ snd_jack_inject_report(jack_kctl, !!enable ? jack_kctl->mask_bits : 0);
+
+ return ret;
+}
+
+static ssize_t jack_kctl_id_read(struct file *file,
+ char __user *to, size_t count, loff_t *ppos)
+{
+ struct snd_jack_kctl *jack_kctl = file->private_data;
+ char buf[64];
+ int len, ret;
+
+ len = scnprintf(buf, sizeof(buf), "%s\n", jack_kctl->kctl->id.name);
+ ret = simple_read_from_buffer(to, count, ppos, buf, len);
+
+ return ret;
+}
+
+/* the bit definition is aligned with snd_jack_types in jack.h */
+static const char * const jack_events_name[] = {
+ "HEADPHONE(0x0001)", "MICROPHONE(0x0002)", "LINEOUT(0x0004)",
+ "MECHANICAL(0x0008)", "VIDEOOUT(0x0010)", "LINEIN(0x0020)",
+ "USB(0x0040)", "", "", "BTN_5(0x0200)", "BTN_4(0x0400)",
+ "BTN_3(0x0800)", "BTN_2(0x1000)", "BTN_1(0x2000)", "BTN_0(0x4000)",
+ "",
+};
+
+/* the recommended buffer size is 256 */
+static int parse_mask_bits(unsigned int mask_bits, char *buf, size_t buf_size)
+{
+ int i;
+
+ scnprintf(buf, buf_size, "0x%04x", mask_bits);
+
+ for (i = 0; i < ARRAY_SIZE(jack_events_name); i++)
+ if (mask_bits & (1 << i)) {
+ strlcat(buf, " ", buf_size);
+ strlcat(buf, jack_events_name[i], buf_size);
+ }
+ strlcat(buf, "\n", buf_size);
+
+ return strlen(buf);
+}
+
+static ssize_t jack_kctl_mask_bits_read(struct file *file,
+ char __user *to, size_t count, loff_t *ppos)
+{
+ struct snd_jack_kctl *jack_kctl = file->private_data;
+ char buf[256];
+ int len, ret;
+
+ len = parse_mask_bits(jack_kctl->mask_bits, buf, sizeof(buf));
+ ret = simple_read_from_buffer(to, count, ppos, buf, len);
+
+ return ret;
+}
+
+static ssize_t jack_kctl_status_read(struct file *file,
+ char __user *to, size_t count, loff_t *ppos)
+{
+ struct snd_jack_kctl *jack_kctl = file->private_data;
+ char buf[16];
+ int len, ret;
+
+ len = scnprintf(buf, sizeof(buf), "%s\n", jack_kctl->kctl->private_value ?
+ "Plugged" : "Unplugged");
+ ret = simple_read_from_buffer(to, count, ppos, buf, len);
+
+ return ret;
+}
+
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+static ssize_t jack_type_read(struct file *file,
+ char __user *to, size_t count, loff_t *ppos)
+{
+ struct snd_jack_kctl *jack_kctl = file->private_data;
+ char buf[256];
+ int len, ret;
+
+ len = parse_mask_bits(jack_kctl->jack->type, buf, sizeof(buf));
+ ret = simple_read_from_buffer(to, count, ppos, buf, len);
+
+ return ret;
+}
+
+static const struct file_operations jack_type_fops = {
+ .open = simple_open,
+ .read = jack_type_read,
+ .llseek = default_llseek,
+};
+#endif
+
+static const struct file_operations sw_inject_enable_fops = {
+ .open = simple_open,
+ .read = sw_inject_enable_read,
+ .write = sw_inject_enable_write,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations jackin_inject_fops = {
+ .open = simple_open,
+ .write = jackin_inject_write,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations jack_kctl_id_fops = {
+ .open = simple_open,
+ .read = jack_kctl_id_read,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations jack_kctl_mask_bits_fops = {
+ .open = simple_open,
+ .read = jack_kctl_mask_bits_read,
+ .llseek = default_llseek,
+};
+
+static const struct file_operations jack_kctl_status_fops = {
+ .open = simple_open,
+ .read = jack_kctl_status_read,
+ .llseek = default_llseek,
+};
+
+static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack,
+ struct snd_jack_kctl *jack_kctl)
+{
+ char *tname;
+ int i;
+
+ /* Don't create injection interface for Phantom jacks */
+ if (strstr(jack_kctl->kctl->id.name, "Phantom"))
+ return 0;
+
+ tname = kstrdup(jack_kctl->kctl->id.name, GFP_KERNEL);
+ if (!tname)
+ return -ENOMEM;
+
+ /* replace the chars which are not suitable for folder's name with _ */
+ for (i = 0; tname[i]; i++)
+ if (!isalnum(tname[i]))
+ tname[i] = '_';
+
+ jack_kctl->jack_debugfs_root = debugfs_create_dir(tname, jack->card->debugfs_root);
+ kfree(tname);
+
+ debugfs_create_file("sw_inject_enable", 0644, jack_kctl->jack_debugfs_root, jack_kctl,
+ &sw_inject_enable_fops);
+
+ debugfs_create_file("jackin_inject", 0200, jack_kctl->jack_debugfs_root, jack_kctl,
+ &jackin_inject_fops);
+
+ debugfs_create_file("kctl_id", 0444, jack_kctl->jack_debugfs_root, jack_kctl,
+ &jack_kctl_id_fops);
+
+ debugfs_create_file("mask_bits", 0444, jack_kctl->jack_debugfs_root, jack_kctl,
+ &jack_kctl_mask_bits_fops);
+
+ debugfs_create_file("status", 0444, jack_kctl->jack_debugfs_root, jack_kctl,
+ &jack_kctl_status_fops);
+
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+ debugfs_create_file("type", 0444, jack_kctl->jack_debugfs_root, jack_kctl,
+ &jack_type_fops);
+#endif
+ return 0;
+}
+
+static void snd_jack_remove_debugfs(struct snd_jack *jack)
+{
+ struct snd_jack_kctl *jack_kctl;
+
+ list_for_each_entry(jack_kctl, &jack->kctl_list, list) {
+ debugfs_remove(jack_kctl->jack_debugfs_root);
+ jack_kctl->jack_debugfs_root = NULL;
+ }
+}
+#else /* CONFIG_SND_JACK_INJECTION_DEBUG */
+static int snd_jack_debugfs_add_inject_node(struct snd_jack *jack,
+ struct snd_jack_kctl *jack_kctl)
+{
+ return 0;
+}
+
+static void snd_jack_remove_debugfs(struct snd_jack *jack)
+{
+}
+#endif /* CONFIG_SND_JACK_INJECTION_DEBUG */
+
+static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl)
+{
+ struct snd_jack_kctl *jack_kctl;
+
+ jack_kctl = kctl->private_data;
+ if (jack_kctl) {
+ list_del(&jack_kctl->list);
+ kfree(jack_kctl);
+ }
+}
+
+static void snd_jack_kctl_add(struct snd_jack *jack, struct snd_jack_kctl *jack_kctl)
+{
+ jack_kctl->jack = jack;
+ list_add_tail(&jack_kctl->list, &jack->kctl_list);
+ snd_jack_debugfs_add_inject_node(jack, jack_kctl);
+}
+
+static struct snd_jack_kctl * snd_jack_kctl_new(struct snd_card *card, const char *name, unsigned int mask)
+{
+ struct snd_kcontrol *kctl;
+ struct snd_jack_kctl *jack_kctl;
+ int err;
+
+ kctl = snd_kctl_jack_new(name, card);
+ if (!kctl)
+ return NULL;
+
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return NULL;
+
+ jack_kctl = kzalloc(sizeof(*jack_kctl), GFP_KERNEL);
+
+ if (!jack_kctl)
+ goto error;
+
+ jack_kctl->kctl = kctl;
+ jack_kctl->mask_bits = mask;
+
+ kctl->private_data = jack_kctl;
+ kctl->private_free = snd_jack_kctl_private_free;
+
+ return jack_kctl;
+error:
+ snd_ctl_free_one(kctl);
+ return NULL;
+}
+
+/**
+ * snd_jack_add_new_kctl - Create a new snd_jack_kctl and add it to jack
+ * @jack: the jack instance which the kctl will attaching to
+ * @name: the name for the snd_kcontrol object
+ * @mask: a bitmask of enum snd_jack_type values that can be detected
+ * by this snd_jack_kctl object.
+ *
+ * Creates a new snd_kcontrol object and adds it to the jack kctl_list.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name, int mask)
+{
+ struct snd_jack_kctl *jack_kctl;
+
+ jack_kctl = snd_jack_kctl_new(jack->card, name, mask);
+ if (!jack_kctl)
+ return -ENOMEM;
+
+ snd_jack_kctl_add(jack, jack_kctl);
+ return 0;
+}
+EXPORT_SYMBOL(snd_jack_add_new_kctl);
/**
* snd_jack_new - Create a new jack
@@ -93,77 +488,94 @@ static int snd_jack_dev_register(struct snd_device *device)
* @type: a bitmask of enum snd_jack_type values that can be detected by
* this jack
* @jjack: Used to provide the allocated jack object to the caller.
+ * @initial_kctl: if true, create a kcontrol and add it to the jack list.
+ * @phantom_jack: Don't create a input device for phantom jacks.
*
* Creates a new jack object.
*
- * Returns zero if successful, or a negative error code on failure.
- * On success jjack will be initialised.
+ * Return: Zero if successful, or a negative error code on failure.
+ * On success @jjack will be initialised.
*/
int snd_jack_new(struct snd_card *card, const char *id, int type,
- struct snd_jack **jjack)
+ struct snd_jack **jjack, bool initial_kctl, bool phantom_jack)
{
struct snd_jack *jack;
+ struct snd_jack_kctl *jack_kctl = NULL;
int err;
- int i;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_jack_dev_free,
+#ifdef CONFIG_SND_JACK_INPUT_DEV
.dev_register = snd_jack_dev_register,
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
+ .dev_disconnect = snd_jack_dev_disconnect,
};
+ if (initial_kctl) {
+ jack_kctl = snd_jack_kctl_new(card, id, type);
+ if (!jack_kctl)
+ return -ENOMEM;
+ }
+
jack = kzalloc(sizeof(struct snd_jack), GFP_KERNEL);
if (jack == NULL)
return -ENOMEM;
jack->id = kstrdup(id, GFP_KERNEL);
-
- jack->input_dev = input_allocate_device();
- if (jack->input_dev == NULL) {
- err = -ENOMEM;
- goto fail_input;
+ if (jack->id == NULL) {
+ kfree(jack);
+ return -ENOMEM;
}
- jack->input_dev->phys = "ALSA";
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+ mutex_init(&jack->input_dev_lock);
+
+ /* don't create input device for phantom jack */
+ if (!phantom_jack) {
+ int i;
+
+ jack->input_dev = input_allocate_device();
+ if (jack->input_dev == NULL) {
+ err = -ENOMEM;
+ goto fail_input;
+ }
+
+ jack->input_dev->phys = "ALSA";
+
+ jack->type = type;
- jack->type = type;
+ for (i = 0; i < SND_JACK_SWITCH_TYPES; i++)
+ if (type & (1 << i))
+ input_set_capability(jack->input_dev, EV_SW,
+ jack_switch_types[i]);
- for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++)
- if (type & (1 << i))
- input_set_capability(jack->input_dev, EV_SW,
- jack_switch_types[i]);
+ }
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
if (err < 0)
goto fail_input;
+ jack->card = card;
+ INIT_LIST_HEAD(&jack->kctl_list);
+
+ if (initial_kctl)
+ snd_jack_kctl_add(jack, jack_kctl);
+
*jjack = jack;
return 0;
fail_input:
+#ifdef CONFIG_SND_JACK_INPUT_DEV
input_free_device(jack->input_dev);
+#endif
+ kfree(jack->id);
kfree(jack);
return err;
}
EXPORT_SYMBOL(snd_jack_new);
-/**
- * snd_jack_set_parent - Set the parent device for a jack
- *
- * @jack: The jack to configure
- * @parent: The device to set as parent for the jack.
- *
- * Set the parent for the jack input device in the device tree. This
- * function is only valid prior to registration of the jack. If no
- * parent is configured then the parent device will be the sound card.
- */
-void snd_jack_set_parent(struct snd_jack *jack, struct device *parent)
-{
- WARN_ON(jack->registered);
-
- jack->input_dev->dev.parent = parent;
-}
-EXPORT_SYMBOL(snd_jack_set_parent);
-
+#ifdef CONFIG_SND_JACK_INPUT_DEV
/**
* snd_jack_set_key - Set a key mapping on a jack
*
@@ -171,11 +583,14 @@ EXPORT_SYMBOL(snd_jack_set_parent);
* @type: Jack report type for this key
* @keytype: Input layer key type to be reported
*
- * Map a SND_JACK_BTN_ button type to an input layer key, allowing
+ * Map a SND_JACK_BTN_* button type to an input layer key, allowing
* reporting of keys on accessories via the jack abstraction. If no
* mapping is provided but keys are enabled in the jack type then
* BTN_n numeric buttons will be reported.
*
+ * If jacks are not reporting via the input API this call will have no
+ * effect.
+ *
* Note that this is intended to be use by simple devices with small
* numbers of keys that can be reported. It is also possible to
* access the input device directly - devices with complex input
@@ -183,6 +598,8 @@ EXPORT_SYMBOL(snd_jack_set_parent);
* using this abstraction.
*
* This function may only be called prior to registration of the jack.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type,
int keytype)
@@ -196,44 +613,64 @@ int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type,
jack->type |= type;
jack->key[key] = keytype;
-
return 0;
}
EXPORT_SYMBOL(snd_jack_set_key);
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
/**
* snd_jack_report - Report the current status of a jack
+ * Note: This function uses mutexes and should be called from a
+ * context which can sleep (such as a workqueue).
*
* @jack: The jack to report status for
* @status: The current status of the jack
*/
void snd_jack_report(struct snd_jack *jack, int status)
{
+ struct snd_jack_kctl *jack_kctl;
+ unsigned int mask_bits = 0;
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+ struct input_dev *idev;
int i;
+#endif
if (!jack)
return;
+ jack->hw_status_cache = status;
+
+ list_for_each_entry(jack_kctl, &jack->kctl_list, list)
+ if (jack_kctl->sw_inject_enable)
+ mask_bits |= jack_kctl->mask_bits;
+ else
+ snd_kctl_jack_report(jack->card, jack_kctl->kctl,
+ status & jack_kctl->mask_bits);
+
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+ idev = input_get_device(jack->input_dev);
+ if (!idev)
+ return;
+
for (i = 0; i < ARRAY_SIZE(jack->key); i++) {
- int testbit = SND_JACK_BTN_0 >> i;
+ int testbit = ((SND_JACK_BTN_0 >> i) & ~mask_bits);
if (jack->type & testbit)
- input_report_key(jack->input_dev, jack->key[i],
+ input_report_key(idev, jack->key[i],
status & testbit);
}
for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++) {
- int testbit = 1 << i;
+ int testbit = ((1 << i) & ~mask_bits);
+
if (jack->type & testbit)
- input_report_switch(jack->input_dev,
+ input_report_switch(idev,
jack_switch_types[i],
status & testbit);
}
- input_sync(jack->input_dev);
+ input_sync(idev);
+ input_put_device(idev);
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
}
EXPORT_SYMBOL(snd_jack_report);
-
-MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
-MODULE_DESCRIPTION("Jack detection support for ALSA");
-MODULE_LICENSE("GPL");
diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c
index 9e92441f9b78..b3853583d2ae 100644
--- a/sound/core/memalloc.c
+++ b/sound/core/memalloc.c
@@ -1,221 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Takashi Iwai <tiwai@suse.de>
*
* Generic memory allocators
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <linux/module.h>
-#include <linux/proc_fs.h>
-#include <linux/init.h>
-#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/mm.h>
-#include <linux/seq_file.h>
-#include <asm/uaccess.h>
#include <linux/dma-mapping.h>
-#include <linux/moduleparam.h>
-#include <linux/mutex.h>
+#include <linux/dma-map-ops.h>
+#include <linux/genalloc.h>
+#include <linux/highmem.h>
+#include <linux/vmalloc.h>
+#ifdef CONFIG_X86
+#include <asm/set_memory.h>
+#endif
#include <sound/memalloc.h>
-
-MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@perex.cz>");
-MODULE_DESCRIPTION("Memory allocator for ALSA system.");
-MODULE_LICENSE("GPL");
-
-
-/*
- */
-
-static DEFINE_MUTEX(list_mutex);
-static LIST_HEAD(mem_list_head);
-
-/* buffer preservation list */
-struct snd_mem_list {
- struct snd_dma_buffer buffer;
- unsigned int id;
- struct list_head list;
+struct snd_malloc_ops {
+ void *(*alloc)(struct snd_dma_buffer *dmab, size_t size);
+ void (*free)(struct snd_dma_buffer *dmab);
+ dma_addr_t (*get_addr)(struct snd_dma_buffer *dmab, size_t offset);
+ struct page *(*get_page)(struct snd_dma_buffer *dmab, size_t offset);
+ unsigned int (*get_chunk_size)(struct snd_dma_buffer *dmab,
+ unsigned int ofs, unsigned int size);
+ int (*mmap)(struct snd_dma_buffer *dmab, struct vm_area_struct *area);
+ void (*sync)(struct snd_dma_buffer *dmab, enum snd_dma_sync_mode mode);
};
-/* id for pre-allocated buffers */
-#define SNDRV_DMA_DEVICE_UNUSED (unsigned int)-1
-
-/*
- *
- * Generic memory allocators
- *
- */
+#define DEFAULT_GFP \
+ (GFP_KERNEL | \
+ __GFP_RETRY_MAYFAIL | /* don't trigger OOM-killer */ \
+ __GFP_NOWARN) /* no stack trace print - this call is non-critical */
-static long snd_allocated_pages; /* holding the number of allocated pages */
+static const struct snd_malloc_ops *snd_dma_get_ops(struct snd_dma_buffer *dmab);
-static inline void inc_snd_pages(int order)
+static void *__snd_dma_alloc_pages(struct snd_dma_buffer *dmab, size_t size)
{
- snd_allocated_pages += 1 << order;
-}
+ const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
-static inline void dec_snd_pages(int order)
-{
- snd_allocated_pages -= 1 << order;
-}
-
-/**
- * snd_malloc_pages - allocate pages with the given size
- * @size: the size to allocate in bytes
- * @gfp_flags: the allocation conditions, GFP_XXX
- *
- * Allocates the physically contiguous pages with the given size.
- *
- * Returns the pointer of the buffer, or NULL if no enoguh memory.
- */
-void *snd_malloc_pages(size_t size, gfp_t gfp_flags)
-{
- int pg;
- void *res;
-
- if (WARN_ON(!size))
- return NULL;
- if (WARN_ON(!gfp_flags))
+ if (WARN_ON_ONCE(!ops || !ops->alloc))
return NULL;
- gfp_flags |= __GFP_COMP; /* compound page lets parts be mapped */
- pg = get_order(size);
- if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL)
- inc_snd_pages(pg);
- return res;
+ return ops->alloc(dmab, size);
}
/**
- * snd_free_pages - release the pages
- * @ptr: the buffer pointer to release
- * @size: the allocated buffer size
- *
- * Releases the buffer allocated via snd_malloc_pages().
- */
-void snd_free_pages(void *ptr, size_t size)
-{
- int pg;
-
- if (ptr == NULL)
- return;
- pg = get_order(size);
- dec_snd_pages(pg);
- free_pages((unsigned long) ptr, pg);
-}
-
-/*
- *
- * Bus-specific memory allocators
- *
- */
-
-#ifdef CONFIG_HAS_DMA
-/* allocate the coherent DMA pages */
-static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma)
-{
- int pg;
- void *res;
- gfp_t gfp_flags;
-
- if (WARN_ON(!dma))
- return NULL;
- pg = get_order(size);
- gfp_flags = GFP_KERNEL
- | __GFP_COMP /* compound page lets parts be mapped */
- | __GFP_NORETRY /* don't trigger OOM-killer */
- | __GFP_NOWARN; /* no stack trace print - this call is non-critical */
- res = dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, gfp_flags);
- if (res != NULL)
- inc_snd_pages(pg);
-
- return res;
-}
-
-/* free the coherent DMA pages */
-static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr,
- dma_addr_t dma)
-{
- int pg;
-
- if (ptr == NULL)
- return;
- pg = get_order(size);
- dec_snd_pages(pg);
- dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma);
-}
-#endif /* CONFIG_HAS_DMA */
-
-/*
- *
- * ALSA generic memory management
- *
- */
-
-
-/**
- * snd_dma_alloc_pages - allocate the buffer area according to the given type
+ * snd_dma_alloc_dir_pages - allocate the buffer area according to the given
+ * type and direction
* @type: the DMA buffer type
* @device: the device pointer
+ * @dir: DMA direction
* @size: the buffer size to allocate
* @dmab: buffer allocation record to store the allocated data
*
* Calls the memory-allocator function for the corresponding
* buffer type.
- *
- * Returns zero if the buffer with the given size is allocated successfuly,
- * other a negative value at error.
+ *
+ * Return: Zero if the buffer with the given size is allocated successfully,
+ * otherwise a negative value on error.
*/
-int snd_dma_alloc_pages(int type, struct device *device, size_t size,
- struct snd_dma_buffer *dmab)
+int snd_dma_alloc_dir_pages(int type, struct device *device,
+ enum dma_data_direction dir, size_t size,
+ struct snd_dma_buffer *dmab)
{
if (WARN_ON(!size))
return -ENXIO;
if (WARN_ON(!dmab))
return -ENXIO;
+ size = PAGE_ALIGN(size);
dmab->dev.type = type;
dmab->dev.dev = device;
+ dmab->dev.dir = dir;
dmab->bytes = 0;
- switch (type) {
- case SNDRV_DMA_TYPE_CONTINUOUS:
- dmab->area = snd_malloc_pages(size, (unsigned long)device);
- dmab->addr = 0;
- break;
-#ifdef CONFIG_HAS_DMA
- case SNDRV_DMA_TYPE_DEV:
- dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
- break;
-#endif
-#ifdef CONFIG_SND_DMA_SGBUF
- case SNDRV_DMA_TYPE_DEV_SG:
- snd_malloc_sgbuf_pages(device, size, dmab, NULL);
- break;
-#endif
- default:
- printk(KERN_ERR "snd-malloc: invalid device type %d\n", type);
- dmab->area = NULL;
- dmab->addr = 0;
- return -ENXIO;
- }
- if (! dmab->area)
+ dmab->addr = 0;
+ dmab->private_data = NULL;
+ dmab->area = __snd_dma_alloc_pages(dmab, size);
+ if (!dmab->area)
return -ENOMEM;
dmab->bytes = size;
return 0;
}
+EXPORT_SYMBOL(snd_dma_alloc_dir_pages);
/**
* snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback
@@ -228,9 +95,9 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
* buffer type. When no space is left, this function reduces the size and
* tries to allocate again. The size actually allocated is stored in
* res_size argument.
- *
- * Returns zero if the buffer with the given size is allocated successfuly,
- * other a negative value at error.
+ *
+ * Return: Zero if the buffer with the given size is allocated successfully,
+ * otherwise a negative value on error.
*/
int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,
struct snd_dma_buffer *dmab)
@@ -238,22 +105,18 @@ int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,
int err;
while ((err = snd_dma_alloc_pages(type, device, size, dmab)) < 0) {
- size_t aligned_size;
if (err != -ENOMEM)
return err;
if (size <= PAGE_SIZE)
return -ENOMEM;
- aligned_size = PAGE_SIZE << get_order(size);
- if (size != aligned_size)
- size = aligned_size;
- else
- size >>= 1;
+ size >>= 1;
+ size = PAGE_SIZE << get_order(size);
}
if (! dmab->area)
return -ENOMEM;
return 0;
}
-
+EXPORT_SYMBOL(snd_dma_alloc_pages_fallback);
/**
* snd_dma_free_pages - release the allocated buffer
@@ -263,284 +126,787 @@ int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,
*/
void snd_dma_free_pages(struct snd_dma_buffer *dmab)
{
- switch (dmab->dev.type) {
- case SNDRV_DMA_TYPE_CONTINUOUS:
- snd_free_pages(dmab->area, dmab->bytes);
- break;
-#ifdef CONFIG_HAS_DMA
- case SNDRV_DMA_TYPE_DEV:
- snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
- break;
-#endif
-#ifdef CONFIG_SND_DMA_SGBUF
- case SNDRV_DMA_TYPE_DEV_SG:
- snd_free_sgbuf_pages(dmab);
- break;
-#endif
- default:
- printk(KERN_ERR "snd-malloc: invalid device type %d\n", dmab->dev.type);
- }
+ const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
+
+ if (ops && ops->free)
+ ops->free(dmab);
}
+EXPORT_SYMBOL(snd_dma_free_pages);
+/* called by devres */
+static void __snd_release_pages(struct device *dev, void *res)
+{
+ snd_dma_free_pages(res);
+}
/**
- * snd_dma_get_reserved - get the reserved buffer for the given device
- * @dmab: the buffer allocation record to store
- * @id: the buffer id
+ * snd_devm_alloc_dir_pages - allocate the buffer and manage with devres
+ * @dev: the device pointer
+ * @type: the DMA buffer type
+ * @dir: DMA direction
+ * @size: the buffer size to allocate
+ *
+ * Allocate buffer pages depending on the given type and manage using devres.
+ * The pages will be released automatically at the device removal.
*
- * Looks for the reserved-buffer list and re-uses if the same buffer
- * is found in the list. When the buffer is found, it's removed from the free list.
+ * Unlike snd_dma_alloc_pages(), this function requires the real device pointer,
+ * hence it can't work with SNDRV_DMA_TYPE_CONTINUOUS or
+ * SNDRV_DMA_TYPE_VMALLOC type.
*
- * Returns the size of buffer if the buffer is found, or zero if not found.
+ * Return: the snd_dma_buffer object at success, or NULL if failed
*/
-size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id)
+struct snd_dma_buffer *
+snd_devm_alloc_dir_pages(struct device *dev, int type,
+ enum dma_data_direction dir, size_t size)
{
- struct snd_mem_list *mem;
+ struct snd_dma_buffer *dmab;
+ int err;
- if (WARN_ON(!dmab))
- return 0;
+ if (WARN_ON(type == SNDRV_DMA_TYPE_CONTINUOUS ||
+ type == SNDRV_DMA_TYPE_VMALLOC))
+ return NULL;
- mutex_lock(&list_mutex);
- list_for_each_entry(mem, &mem_list_head, list) {
- if (mem->id == id &&
- (mem->buffer.dev.dev == NULL || dmab->dev.dev == NULL ||
- ! memcmp(&mem->buffer.dev, &dmab->dev, sizeof(dmab->dev)))) {
- struct device *dev = dmab->dev.dev;
- list_del(&mem->list);
- *dmab = mem->buffer;
- if (dmab->dev.dev == NULL)
- dmab->dev.dev = dev;
- kfree(mem);
- mutex_unlock(&list_mutex);
- return dmab->bytes;
- }
+ dmab = devres_alloc(__snd_release_pages, sizeof(*dmab), GFP_KERNEL);
+ if (!dmab)
+ return NULL;
+
+ err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab);
+ if (err < 0) {
+ devres_free(dmab);
+ return NULL;
}
- mutex_unlock(&list_mutex);
- return 0;
+
+ devres_add(dev, dmab);
+ return dmab;
}
+EXPORT_SYMBOL_GPL(snd_devm_alloc_dir_pages);
/**
- * snd_dma_reserve_buf - reserve the buffer
- * @dmab: the buffer to reserve
- * @id: the buffer id
+ * snd_dma_buffer_mmap - perform mmap of the given DMA buffer
+ * @dmab: buffer allocation information
+ * @area: VM area information
*
- * Reserves the given buffer as a reserved buffer.
- *
- * Returns zero if successful, or a negative code at error.
+ * Return: zero if successful, or a negative error code
*/
-int snd_dma_reserve_buf(struct snd_dma_buffer *dmab, unsigned int id)
+int snd_dma_buffer_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
{
- struct snd_mem_list *mem;
+ const struct snd_malloc_ops *ops;
- if (WARN_ON(!dmab))
- return -EINVAL;
- mem = kmalloc(sizeof(*mem), GFP_KERNEL);
- if (! mem)
- return -ENOMEM;
- mutex_lock(&list_mutex);
- mem->buffer = *dmab;
- mem->id = id;
- list_add_tail(&mem->list, &mem_list_head);
- mutex_unlock(&list_mutex);
- return 0;
+ if (!dmab)
+ return -ENOENT;
+ ops = snd_dma_get_ops(dmab);
+ if (ops && ops->mmap)
+ return ops->mmap(dmab, area);
+ else
+ return -ENOENT;
+}
+EXPORT_SYMBOL(snd_dma_buffer_mmap);
+
+#ifdef CONFIG_HAS_DMA
+/**
+ * snd_dma_buffer_sync - sync DMA buffer between CPU and device
+ * @dmab: buffer allocation information
+ * @mode: sync mode
+ */
+void snd_dma_buffer_sync(struct snd_dma_buffer *dmab,
+ enum snd_dma_sync_mode mode)
+{
+ const struct snd_malloc_ops *ops;
+
+ if (!dmab || !dmab->dev.need_sync)
+ return;
+ ops = snd_dma_get_ops(dmab);
+ if (ops && ops->sync)
+ ops->sync(dmab, mode);
+}
+EXPORT_SYMBOL_GPL(snd_dma_buffer_sync);
+#endif /* CONFIG_HAS_DMA */
+
+/**
+ * snd_sgbuf_get_addr - return the physical address at the corresponding offset
+ * @dmab: buffer allocation information
+ * @offset: offset in the ring buffer
+ *
+ * Return: the physical address
+ */
+dma_addr_t snd_sgbuf_get_addr(struct snd_dma_buffer *dmab, size_t offset)
+{
+ const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
+
+ if (ops && ops->get_addr)
+ return ops->get_addr(dmab, offset);
+ else
+ return dmab->addr + offset;
+}
+EXPORT_SYMBOL(snd_sgbuf_get_addr);
+
+/**
+ * snd_sgbuf_get_page - return the physical page at the corresponding offset
+ * @dmab: buffer allocation information
+ * @offset: offset in the ring buffer
+ *
+ * Return: the page pointer
+ */
+struct page *snd_sgbuf_get_page(struct snd_dma_buffer *dmab, size_t offset)
+{
+ const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
+
+ if (ops && ops->get_page)
+ return ops->get_page(dmab, offset);
+ else
+ return virt_to_page(dmab->area + offset);
+}
+EXPORT_SYMBOL(snd_sgbuf_get_page);
+
+/**
+ * snd_sgbuf_get_chunk_size - compute the max chunk size with continuous pages
+ * on sg-buffer
+ * @dmab: buffer allocation information
+ * @ofs: offset in the ring buffer
+ * @size: the requested size
+ *
+ * Return: the chunk size
+ */
+unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
+ unsigned int ofs, unsigned int size)
+{
+ const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
+
+ if (ops && ops->get_chunk_size)
+ return ops->get_chunk_size(dmab, ofs, size);
+ else
+ return size;
+}
+EXPORT_SYMBOL(snd_sgbuf_get_chunk_size);
+
+/*
+ * Continuous pages allocator
+ */
+static void *do_alloc_pages(struct device *dev, size_t size, dma_addr_t *addr,
+ bool wc)
+{
+ void *p;
+ gfp_t gfp = GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN;
+
+ again:
+ p = alloc_pages_exact(size, gfp);
+ if (!p)
+ return NULL;
+ *addr = page_to_phys(virt_to_page(p));
+ if (!dev)
+ return p;
+ if ((*addr + size - 1) & ~dev->coherent_dma_mask) {
+ if (IS_ENABLED(CONFIG_ZONE_DMA32) && !(gfp & GFP_DMA32)) {
+ gfp |= GFP_DMA32;
+ goto again;
+ }
+ if (IS_ENABLED(CONFIG_ZONE_DMA) && !(gfp & GFP_DMA)) {
+ gfp = (gfp & ~GFP_DMA32) | GFP_DMA;
+ goto again;
+ }
+ }
+#ifdef CONFIG_X86
+ if (wc)
+ set_memory_wc((unsigned long)(p), size >> PAGE_SHIFT);
+#endif
+ return p;
+}
+
+static void do_free_pages(void *p, size_t size, bool wc)
+{
+#ifdef CONFIG_X86
+ if (wc)
+ set_memory_wb((unsigned long)(p), size >> PAGE_SHIFT);
+#endif
+ free_pages_exact(p, size);
+}
+
+
+static void *snd_dma_continuous_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ return do_alloc_pages(dmab->dev.dev, size, &dmab->addr, false);
+}
+
+static void snd_dma_continuous_free(struct snd_dma_buffer *dmab)
+{
+ do_free_pages(dmab->area, dmab->bytes, false);
+}
+
+static int snd_dma_continuous_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ return remap_pfn_range(area, area->vm_start,
+ dmab->addr >> PAGE_SHIFT,
+ area->vm_end - area->vm_start,
+ area->vm_page_prot);
}
+static const struct snd_malloc_ops snd_dma_continuous_ops = {
+ .alloc = snd_dma_continuous_alloc,
+ .free = snd_dma_continuous_free,
+ .mmap = snd_dma_continuous_mmap,
+};
+
/*
- * purge all reserved buffers
+ * VMALLOC allocator
*/
-static void free_all_reserved_pages(void)
-{
- struct list_head *p;
- struct snd_mem_list *mem;
-
- mutex_lock(&list_mutex);
- while (! list_empty(&mem_list_head)) {
- p = mem_list_head.next;
- mem = list_entry(p, struct snd_mem_list, list);
- list_del(p);
- snd_dma_free_pages(&mem->buffer);
- kfree(mem);
+static void *snd_dma_vmalloc_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ return vmalloc(size);
+}
+
+static void snd_dma_vmalloc_free(struct snd_dma_buffer *dmab)
+{
+ vfree(dmab->area);
+}
+
+static int snd_dma_vmalloc_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ return remap_vmalloc_range(area, dmab->area, 0);
+}
+
+#define get_vmalloc_page_addr(dmab, offset) \
+ page_to_phys(vmalloc_to_page((dmab)->area + (offset)))
+
+static dma_addr_t snd_dma_vmalloc_get_addr(struct snd_dma_buffer *dmab,
+ size_t offset)
+{
+ return get_vmalloc_page_addr(dmab, offset) + offset % PAGE_SIZE;
+}
+
+static struct page *snd_dma_vmalloc_get_page(struct snd_dma_buffer *dmab,
+ size_t offset)
+{
+ return vmalloc_to_page(dmab->area + offset);
+}
+
+static unsigned int
+snd_dma_vmalloc_get_chunk_size(struct snd_dma_buffer *dmab,
+ unsigned int ofs, unsigned int size)
+{
+ unsigned int start, end;
+ unsigned long addr;
+
+ start = ALIGN_DOWN(ofs, PAGE_SIZE);
+ end = ofs + size - 1; /* the last byte address */
+ /* check page continuity */
+ addr = get_vmalloc_page_addr(dmab, start);
+ for (;;) {
+ start += PAGE_SIZE;
+ if (start > end)
+ break;
+ addr += PAGE_SIZE;
+ if (get_vmalloc_page_addr(dmab, start) != addr)
+ return start - ofs;
}
- mutex_unlock(&list_mutex);
+ /* ok, all on continuous pages */
+ return size;
}
+static const struct snd_malloc_ops snd_dma_vmalloc_ops = {
+ .alloc = snd_dma_vmalloc_alloc,
+ .free = snd_dma_vmalloc_free,
+ .mmap = snd_dma_vmalloc_mmap,
+ .get_addr = snd_dma_vmalloc_get_addr,
+ .get_page = snd_dma_vmalloc_get_page,
+ .get_chunk_size = snd_dma_vmalloc_get_chunk_size,
+};
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_HAS_DMA
/*
- * proc file interface
+ * IRAM allocator
*/
-#define SND_MEM_PROC_FILE "driver/snd-page-alloc"
-static struct proc_dir_entry *snd_mem_proc;
-
-static int snd_mem_proc_read(struct seq_file *seq, void *offset)
-{
- long pages = snd_allocated_pages >> (PAGE_SHIFT-12);
- struct snd_mem_list *mem;
- int devno;
- static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG" };
-
- mutex_lock(&list_mutex);
- seq_printf(seq, "pages : %li bytes (%li pages per %likB)\n",
- pages * PAGE_SIZE, pages, PAGE_SIZE / 1024);
- devno = 0;
- list_for_each_entry(mem, &mem_list_head, list) {
- devno++;
- seq_printf(seq, "buffer %d : ID %08x : type %s\n",
- devno, mem->id, types[mem->buffer.dev.type]);
- seq_printf(seq, " addr = 0x%lx, size = %d bytes\n",
- (unsigned long)mem->buffer.addr,
- (int)mem->buffer.bytes);
+#ifdef CONFIG_GENERIC_ALLOCATOR
+static void *snd_dma_iram_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ struct device *dev = dmab->dev.dev;
+ struct gen_pool *pool;
+ void *p;
+
+ if (dev->of_node) {
+ pool = of_gen_pool_get(dev->of_node, "iram", 0);
+ /* Assign the pool into private_data field */
+ dmab->private_data = pool;
+
+ p = gen_pool_dma_alloc_align(pool, size, &dmab->addr, PAGE_SIZE);
+ if (p)
+ return p;
}
- mutex_unlock(&list_mutex);
- return 0;
+
+ /* Internal memory might have limited size and no enough space,
+ * so if we fail to malloc, try to fetch memory traditionally.
+ */
+ dmab->dev.type = SNDRV_DMA_TYPE_DEV;
+ return __snd_dma_alloc_pages(dmab, size);
}
-static int snd_mem_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, snd_mem_proc_read, NULL);
-}
-
-/* FIXME: for pci only - other bus? */
-#ifdef CONFIG_PCI
-#define gettoken(bufp) strsep(bufp, " \t\n")
-
-static ssize_t snd_mem_proc_write(struct file *file, const char __user * buffer,
- size_t count, loff_t * ppos)
-{
- char buf[128];
- char *token, *p;
-
- if (count > sizeof(buf) - 1)
- return -EINVAL;
- if (copy_from_user(buf, buffer, count))
- return -EFAULT;
- buf[count] = '\0';
-
- p = buf;
- token = gettoken(&p);
- if (! token || *token == '#')
- return count;
- if (strcmp(token, "add") == 0) {
- char *endp;
- int vendor, device, size, buffers;
- long mask;
- int i, alloced;
- struct pci_dev *pci;
-
- if ((token = gettoken(&p)) == NULL ||
- (vendor = simple_strtol(token, NULL, 0)) <= 0 ||
- (token = gettoken(&p)) == NULL ||
- (device = simple_strtol(token, NULL, 0)) <= 0 ||
- (token = gettoken(&p)) == NULL ||
- (mask = simple_strtol(token, NULL, 0)) < 0 ||
- (token = gettoken(&p)) == NULL ||
- (size = memparse(token, &endp)) < 64*1024 ||
- size > 16*1024*1024 /* too big */ ||
- (token = gettoken(&p)) == NULL ||
- (buffers = simple_strtol(token, NULL, 0)) <= 0 ||
- buffers > 4) {
- printk(KERN_ERR "snd-page-alloc: invalid proc write format\n");
- return count;
- }
- vendor &= 0xffff;
- device &= 0xffff;
-
- alloced = 0;
- pci = NULL;
- while ((pci = pci_get_device(vendor, device, pci)) != NULL) {
- if (mask > 0 && mask < 0xffffffff) {
- if (pci_set_dma_mask(pci, mask) < 0 ||
- pci_set_consistent_dma_mask(pci, mask) < 0) {
- printk(KERN_ERR "snd-page-alloc: cannot set DMA mask %lx for pci %04x:%04x\n", mask, vendor, device);
- pci_dev_put(pci);
- return count;
- }
- }
- for (i = 0; i < buffers; i++) {
- struct snd_dma_buffer dmab;
- memset(&dmab, 0, sizeof(dmab));
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- size, &dmab) < 0) {
- printk(KERN_ERR "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", size);
- pci_dev_put(pci);
- return count;
- }
- snd_dma_reserve_buf(&dmab, snd_dma_pci_buf_id(pci));
- }
- alloced++;
- }
- if (! alloced) {
- for (i = 0; i < buffers; i++) {
- struct snd_dma_buffer dmab;
- memset(&dmab, 0, sizeof(dmab));
- /* FIXME: We can allocate only in ZONE_DMA
- * without a device pointer!
- */
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, NULL,
- size, &dmab) < 0) {
- printk(KERN_ERR "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", size);
- break;
- }
- snd_dma_reserve_buf(&dmab, (unsigned int)((vendor << 16) | device));
- }
- }
- } else if (strcmp(token, "erase") == 0)
- /* FIXME: need for releasing each buffer chunk? */
- free_all_reserved_pages();
- else
- printk(KERN_ERR "snd-page-alloc: invalid proc cmd\n");
- return count;
+static void snd_dma_iram_free(struct snd_dma_buffer *dmab)
+{
+ struct gen_pool *pool = dmab->private_data;
+
+ if (pool && dmab->area)
+ gen_pool_free(pool, (unsigned long)dmab->area, dmab->bytes);
}
-#endif /* CONFIG_PCI */
-static const struct file_operations snd_mem_proc_fops = {
- .owner = THIS_MODULE,
- .open = snd_mem_proc_open,
- .read = seq_read,
-#ifdef CONFIG_PCI
- .write = snd_mem_proc_write,
-#endif
- .llseek = seq_lseek,
- .release = single_release,
+static int snd_dma_iram_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
+ return remap_pfn_range(area, area->vm_start,
+ dmab->addr >> PAGE_SHIFT,
+ area->vm_end - area->vm_start,
+ area->vm_page_prot);
+}
+
+static const struct snd_malloc_ops snd_dma_iram_ops = {
+ .alloc = snd_dma_iram_alloc,
+ .free = snd_dma_iram_free,
+ .mmap = snd_dma_iram_mmap,
};
+#endif /* CONFIG_GENERIC_ALLOCATOR */
+
+/*
+ * Coherent device pages allocator
+ */
+static void *snd_dma_dev_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ return dma_alloc_coherent(dmab->dev.dev, size, &dmab->addr, DEFAULT_GFP);
+}
-#endif /* CONFIG_PROC_FS */
+static void snd_dma_dev_free(struct snd_dma_buffer *dmab)
+{
+ dma_free_coherent(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
+}
+
+static int snd_dma_dev_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ return dma_mmap_coherent(dmab->dev.dev, area,
+ dmab->area, dmab->addr, dmab->bytes);
+}
+
+static const struct snd_malloc_ops snd_dma_dev_ops = {
+ .alloc = snd_dma_dev_alloc,
+ .free = snd_dma_dev_free,
+ .mmap = snd_dma_dev_mmap,
+};
/*
- * module entry
+ * Write-combined pages
*/
+#ifdef CONFIG_SND_DMA_SGBUF
+/* x86-specific allocations */
+static void *snd_dma_wc_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ void *p = do_alloc_pages(dmab->dev.dev, size, &dmab->addr, true);
+
+ if (!p)
+ return NULL;
+ dmab->addr = dma_map_single(dmab->dev.dev, p, size, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(dmab->dev.dev, dmab->addr)) {
+ do_free_pages(dmab->area, size, true);
+ return NULL;
+ }
+ return p;
+}
+
+static void snd_dma_wc_free(struct snd_dma_buffer *dmab)
+{
+ dma_unmap_single(dmab->dev.dev, dmab->addr, dmab->bytes,
+ DMA_BIDIRECTIONAL);
+ do_free_pages(dmab->area, dmab->bytes, true);
+}
+
+static int snd_dma_wc_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
+ return dma_mmap_coherent(dmab->dev.dev, area,
+ dmab->area, dmab->addr, dmab->bytes);
+}
+#else
+static void *snd_dma_wc_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ return dma_alloc_wc(dmab->dev.dev, size, &dmab->addr, DEFAULT_GFP);
+}
-static int __init snd_mem_init(void)
+static void snd_dma_wc_free(struct snd_dma_buffer *dmab)
{
-#ifdef CONFIG_PROC_FS
- snd_mem_proc = proc_create(SND_MEM_PROC_FILE, 0644, NULL,
- &snd_mem_proc_fops);
+ dma_free_wc(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
+}
+
+static int snd_dma_wc_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ return dma_mmap_wc(dmab->dev.dev, area,
+ dmab->area, dmab->addr, dmab->bytes);
+}
#endif
- return 0;
+
+static const struct snd_malloc_ops snd_dma_wc_ops = {
+ .alloc = snd_dma_wc_alloc,
+ .free = snd_dma_wc_free,
+ .mmap = snd_dma_wc_mmap,
+};
+
+/*
+ * Non-contiguous pages allocator
+ */
+static void *snd_dma_noncontig_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ struct sg_table *sgt;
+ void *p;
+
+ sgt = dma_alloc_noncontiguous(dmab->dev.dev, size, dmab->dev.dir,
+ DEFAULT_GFP, 0);
+ if (!sgt)
+ return NULL;
+
+ dmab->dev.need_sync = dma_need_sync(dmab->dev.dev,
+ sg_dma_address(sgt->sgl));
+ p = dma_vmap_noncontiguous(dmab->dev.dev, size, sgt);
+ if (p) {
+ dmab->private_data = sgt;
+ /* store the first page address for convenience */
+ dmab->addr = snd_sgbuf_get_addr(dmab, 0);
+ } else {
+ dma_free_noncontiguous(dmab->dev.dev, size, sgt, dmab->dev.dir);
+ }
+ return p;
}
-static void __exit snd_mem_exit(void)
+static void snd_dma_noncontig_free(struct snd_dma_buffer *dmab)
{
- remove_proc_entry(SND_MEM_PROC_FILE, NULL);
- free_all_reserved_pages();
- if (snd_allocated_pages > 0)
- printk(KERN_ERR "snd-malloc: Memory leak? pages not freed = %li\n", snd_allocated_pages);
+ dma_vunmap_noncontiguous(dmab->dev.dev, dmab->area);
+ dma_free_noncontiguous(dmab->dev.dev, dmab->bytes, dmab->private_data,
+ dmab->dev.dir);
}
+static int snd_dma_noncontig_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ return dma_mmap_noncontiguous(dmab->dev.dev, area,
+ dmab->bytes, dmab->private_data);
+}
-module_init(snd_mem_init)
-module_exit(snd_mem_exit)
+static void snd_dma_noncontig_sync(struct snd_dma_buffer *dmab,
+ enum snd_dma_sync_mode mode)
+{
+ if (mode == SNDRV_DMA_SYNC_CPU) {
+ if (dmab->dev.dir == DMA_TO_DEVICE)
+ return;
+ invalidate_kernel_vmap_range(dmab->area, dmab->bytes);
+ dma_sync_sgtable_for_cpu(dmab->dev.dev, dmab->private_data,
+ dmab->dev.dir);
+ } else {
+ if (dmab->dev.dir == DMA_FROM_DEVICE)
+ return;
+ flush_kernel_vmap_range(dmab->area, dmab->bytes);
+ dma_sync_sgtable_for_device(dmab->dev.dev, dmab->private_data,
+ dmab->dev.dir);
+ }
+}
+static inline void snd_dma_noncontig_iter_set(struct snd_dma_buffer *dmab,
+ struct sg_page_iter *piter,
+ size_t offset)
+{
+ struct sg_table *sgt = dmab->private_data;
+
+ __sg_page_iter_start(piter, sgt->sgl, sgt->orig_nents,
+ offset >> PAGE_SHIFT);
+}
+
+static dma_addr_t snd_dma_noncontig_get_addr(struct snd_dma_buffer *dmab,
+ size_t offset)
+{
+ struct sg_dma_page_iter iter;
+
+ snd_dma_noncontig_iter_set(dmab, &iter.base, offset);
+ __sg_page_iter_dma_next(&iter);
+ return sg_page_iter_dma_address(&iter) + offset % PAGE_SIZE;
+}
+
+static struct page *snd_dma_noncontig_get_page(struct snd_dma_buffer *dmab,
+ size_t offset)
+{
+ struct sg_page_iter iter;
+
+ snd_dma_noncontig_iter_set(dmab, &iter, offset);
+ __sg_page_iter_next(&iter);
+ return sg_page_iter_page(&iter);
+}
+
+static unsigned int
+snd_dma_noncontig_get_chunk_size(struct snd_dma_buffer *dmab,
+ unsigned int ofs, unsigned int size)
+{
+ struct sg_dma_page_iter iter;
+ unsigned int start, end;
+ unsigned long addr;
+
+ start = ALIGN_DOWN(ofs, PAGE_SIZE);
+ end = ofs + size - 1; /* the last byte address */
+ snd_dma_noncontig_iter_set(dmab, &iter.base, start);
+ if (!__sg_page_iter_dma_next(&iter))
+ return 0;
+ /* check page continuity */
+ addr = sg_page_iter_dma_address(&iter);
+ for (;;) {
+ start += PAGE_SIZE;
+ if (start > end)
+ break;
+ addr += PAGE_SIZE;
+ if (!__sg_page_iter_dma_next(&iter) ||
+ sg_page_iter_dma_address(&iter) != addr)
+ return start - ofs;
+ }
+ /* ok, all on continuous pages */
+ return size;
+}
+
+static const struct snd_malloc_ops snd_dma_noncontig_ops = {
+ .alloc = snd_dma_noncontig_alloc,
+ .free = snd_dma_noncontig_free,
+ .mmap = snd_dma_noncontig_mmap,
+ .sync = snd_dma_noncontig_sync,
+ .get_addr = snd_dma_noncontig_get_addr,
+ .get_page = snd_dma_noncontig_get_page,
+ .get_chunk_size = snd_dma_noncontig_get_chunk_size,
+};
+
+#ifdef CONFIG_SND_DMA_SGBUF
+/* Fallback SG-buffer allocations for x86 */
+struct snd_dma_sg_fallback {
+ struct sg_table sgt; /* used by get_addr - must be the first item */
+ size_t count;
+ struct page **pages;
+ unsigned int *npages;
+};
+
+static void __snd_dma_sg_fallback_free(struct snd_dma_buffer *dmab,
+ struct snd_dma_sg_fallback *sgbuf)
+{
+ bool wc = dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG;
+ size_t i, size;
+
+ if (sgbuf->pages && sgbuf->npages) {
+ i = 0;
+ while (i < sgbuf->count) {
+ size = sgbuf->npages[i];
+ if (!size)
+ break;
+ do_free_pages(page_address(sgbuf->pages[i]),
+ size << PAGE_SHIFT, wc);
+ i += size;
+ }
+ }
+ kvfree(sgbuf->pages);
+ kvfree(sgbuf->npages);
+ kfree(sgbuf);
+}
+
+/* fallback manual S/G buffer allocations */
+static void *snd_dma_sg_fallback_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ bool wc = dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG;
+ struct snd_dma_sg_fallback *sgbuf;
+ struct page **pagep, *curp;
+ size_t chunk;
+ dma_addr_t addr;
+ unsigned int idx, npages;
+ void *p;
+
+ sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL);
+ if (!sgbuf)
+ return NULL;
+ size = PAGE_ALIGN(size);
+ sgbuf->count = size >> PAGE_SHIFT;
+ sgbuf->pages = kvcalloc(sgbuf->count, sizeof(*sgbuf->pages), GFP_KERNEL);
+ sgbuf->npages = kvcalloc(sgbuf->count, sizeof(*sgbuf->npages), GFP_KERNEL);
+ if (!sgbuf->pages || !sgbuf->npages)
+ goto error;
+
+ pagep = sgbuf->pages;
+ chunk = size;
+ idx = 0;
+ while (size > 0) {
+ chunk = min(size, chunk);
+ p = do_alloc_pages(dmab->dev.dev, chunk, &addr, wc);
+ if (!p) {
+ if (chunk <= PAGE_SIZE)
+ goto error;
+ chunk >>= 1;
+ chunk = PAGE_SIZE << get_order(chunk);
+ continue;
+ }
+
+ size -= chunk;
+ /* fill pages */
+ npages = chunk >> PAGE_SHIFT;
+ sgbuf->npages[idx] = npages;
+ idx += npages;
+ curp = virt_to_page(p);
+ while (npages--)
+ *pagep++ = curp++;
+ }
+
+ if (sg_alloc_table_from_pages(&sgbuf->sgt, sgbuf->pages, sgbuf->count,
+ 0, sgbuf->count << PAGE_SHIFT, GFP_KERNEL))
+ goto error;
+
+ if (dma_map_sgtable(dmab->dev.dev, &sgbuf->sgt, DMA_BIDIRECTIONAL, 0))
+ goto error_dma_map;
+
+ p = vmap(sgbuf->pages, sgbuf->count, VM_MAP, PAGE_KERNEL);
+ if (!p)
+ goto error_vmap;
+
+ dmab->private_data = sgbuf;
+ /* store the first page address for convenience */
+ dmab->addr = snd_sgbuf_get_addr(dmab, 0);
+ return p;
+
+ error_vmap:
+ dma_unmap_sgtable(dmab->dev.dev, &sgbuf->sgt, DMA_BIDIRECTIONAL, 0);
+ error_dma_map:
+ sg_free_table(&sgbuf->sgt);
+ error:
+ __snd_dma_sg_fallback_free(dmab, sgbuf);
+ return NULL;
+}
+
+static void snd_dma_sg_fallback_free(struct snd_dma_buffer *dmab)
+{
+ struct snd_dma_sg_fallback *sgbuf = dmab->private_data;
+
+ vunmap(dmab->area);
+ dma_unmap_sgtable(dmab->dev.dev, &sgbuf->sgt, DMA_BIDIRECTIONAL, 0);
+ sg_free_table(&sgbuf->sgt);
+ __snd_dma_sg_fallback_free(dmab, dmab->private_data);
+}
+
+static int snd_dma_sg_fallback_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ struct snd_dma_sg_fallback *sgbuf = dmab->private_data;
+
+ if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG)
+ area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
+ return vm_map_pages(area, sgbuf->pages, sgbuf->count);
+}
+
+static void *snd_dma_sg_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ int type = dmab->dev.type;
+ void *p;
+
+ /* try the standard DMA API allocation at first */
+ if (type == SNDRV_DMA_TYPE_DEV_WC_SG)
+ dmab->dev.type = SNDRV_DMA_TYPE_DEV_WC;
+ else
+ dmab->dev.type = SNDRV_DMA_TYPE_DEV;
+ p = __snd_dma_alloc_pages(dmab, size);
+ if (p)
+ return p;
+
+ dmab->dev.type = type; /* restore the type */
+ return snd_dma_sg_fallback_alloc(dmab, size);
+}
+
+static const struct snd_malloc_ops snd_dma_sg_ops = {
+ .alloc = snd_dma_sg_alloc,
+ .free = snd_dma_sg_fallback_free,
+ .mmap = snd_dma_sg_fallback_mmap,
+ /* reuse noncontig helper */
+ .get_addr = snd_dma_noncontig_get_addr,
+ /* reuse vmalloc helpers */
+ .get_page = snd_dma_vmalloc_get_page,
+ .get_chunk_size = snd_dma_vmalloc_get_chunk_size,
+};
+#endif /* CONFIG_SND_DMA_SGBUF */
/*
- * exports
+ * Non-coherent pages allocator
*/
-EXPORT_SYMBOL(snd_dma_alloc_pages);
-EXPORT_SYMBOL(snd_dma_alloc_pages_fallback);
-EXPORT_SYMBOL(snd_dma_free_pages);
+static void *snd_dma_noncoherent_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+ void *p;
+
+ p = dma_alloc_noncoherent(dmab->dev.dev, size, &dmab->addr,
+ dmab->dev.dir, DEFAULT_GFP);
+ if (p)
+ dmab->dev.need_sync = dma_need_sync(dmab->dev.dev, dmab->addr);
+ return p;
+}
+
+static void snd_dma_noncoherent_free(struct snd_dma_buffer *dmab)
+{
+ dma_free_noncoherent(dmab->dev.dev, dmab->bytes, dmab->area,
+ dmab->addr, dmab->dev.dir);
+}
+
+static int snd_dma_noncoherent_mmap(struct snd_dma_buffer *dmab,
+ struct vm_area_struct *area)
+{
+ area->vm_page_prot = vm_get_page_prot(area->vm_flags);
+ return dma_mmap_pages(dmab->dev.dev, area,
+ area->vm_end - area->vm_start,
+ virt_to_page(dmab->area));
+}
+
+static void snd_dma_noncoherent_sync(struct snd_dma_buffer *dmab,
+ enum snd_dma_sync_mode mode)
+{
+ if (mode == SNDRV_DMA_SYNC_CPU) {
+ if (dmab->dev.dir != DMA_TO_DEVICE)
+ dma_sync_single_for_cpu(dmab->dev.dev, dmab->addr,
+ dmab->bytes, dmab->dev.dir);
+ } else {
+ if (dmab->dev.dir != DMA_FROM_DEVICE)
+ dma_sync_single_for_device(dmab->dev.dev, dmab->addr,
+ dmab->bytes, dmab->dev.dir);
+ }
+}
-EXPORT_SYMBOL(snd_dma_get_reserved_buf);
-EXPORT_SYMBOL(snd_dma_reserve_buf);
+static const struct snd_malloc_ops snd_dma_noncoherent_ops = {
+ .alloc = snd_dma_noncoherent_alloc,
+ .free = snd_dma_noncoherent_free,
+ .mmap = snd_dma_noncoherent_mmap,
+ .sync = snd_dma_noncoherent_sync,
+};
+
+#endif /* CONFIG_HAS_DMA */
+
+/*
+ * Entry points
+ */
+static const struct snd_malloc_ops *snd_dma_ops[] = {
+ [SNDRV_DMA_TYPE_CONTINUOUS] = &snd_dma_continuous_ops,
+ [SNDRV_DMA_TYPE_VMALLOC] = &snd_dma_vmalloc_ops,
+#ifdef CONFIG_HAS_DMA
+ [SNDRV_DMA_TYPE_DEV] = &snd_dma_dev_ops,
+ [SNDRV_DMA_TYPE_DEV_WC] = &snd_dma_wc_ops,
+ [SNDRV_DMA_TYPE_NONCONTIG] = &snd_dma_noncontig_ops,
+ [SNDRV_DMA_TYPE_NONCOHERENT] = &snd_dma_noncoherent_ops,
+#ifdef CONFIG_SND_DMA_SGBUF
+ [SNDRV_DMA_TYPE_DEV_SG] = &snd_dma_sg_ops,
+ [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_ops,
+#endif
+#ifdef CONFIG_GENERIC_ALLOCATOR
+ [SNDRV_DMA_TYPE_DEV_IRAM] = &snd_dma_iram_ops,
+#endif /* CONFIG_GENERIC_ALLOCATOR */
+#endif /* CONFIG_HAS_DMA */
+};
-EXPORT_SYMBOL(snd_malloc_pages);
-EXPORT_SYMBOL(snd_free_pages);
+static const struct snd_malloc_ops *snd_dma_get_ops(struct snd_dma_buffer *dmab)
+{
+ if (WARN_ON_ONCE(!dmab))
+ return NULL;
+ if (WARN_ON_ONCE(dmab->dev.type <= SNDRV_DMA_TYPE_UNKNOWN ||
+ dmab->dev.type >= ARRAY_SIZE(snd_dma_ops)))
+ return NULL;
+ return snd_dma_ops[dmab->dev.type];
+}
diff --git a/sound/core/memory.c b/sound/core/memory.c
index 1161158582a6..d683442b4c97 100644
--- a/sound/core/memory.c
+++ b/sound/core/memory.c
@@ -1,28 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
* Misc memory accessors
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
-#include <asm/uaccess.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
#include <sound/core.h>
+#include <sound/pcm.h>
/**
* copy_to_user_fromio - copy data from mmio-space to user-space
@@ -32,30 +19,54 @@
*
* Copies the data from mmio-space to user-space.
*
- * Returns zero if successful, or non-zero on failure.
+ * Return: Zero if successful, or non-zero on failure.
*/
int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size_t count)
{
+ struct iov_iter iter;
+
+ if (import_ubuf(ITER_DEST, dst, count, &iter))
+ return -EFAULT;
+ if (copy_to_iter_fromio((const void __iomem *)src, count, &iter) != count)
+ return -EFAULT;
+ return 0;
+}
+EXPORT_SYMBOL(copy_to_user_fromio);
+
+/**
+ * copy_to_iter_fromio - copy data from mmio-space to iov_iter
+ * @src: the source pointer on mmio
+ * @count: the data size to copy in bytes
+ * @dst: the destination iov_iter
+ *
+ * Copies the data from mmio-space to iov_iter.
+ *
+ * Return: number of bytes to be copied
+ */
+size_t copy_to_iter_fromio(const void __iomem *src, size_t count,
+ struct iov_iter *dst)
+{
#if defined(__i386__) || defined(CONFIG_SPARC32)
- return copy_to_user(dst, (const void __force*)src, count) ? -EFAULT : 0;
+ return copy_to_iter((const void __force *)src, count, dst);
#else
char buf[256];
+ size_t res = 0;
+
while (count) {
size_t c = count;
if (c > sizeof(buf))
c = sizeof(buf);
memcpy_fromio(buf, (void __iomem *)src, c);
- if (copy_to_user(dst, buf, c))
- return -EFAULT;
+ if (copy_to_iter(buf, c, dst) != c)
+ return res;
count -= c;
- dst += c;
src += c;
+ res += c;
}
- return 0;
+ return res;
#endif
}
-
-EXPORT_SYMBOL(copy_to_user_fromio);
+EXPORT_SYMBOL(copy_to_iter_fromio);
/**
* copy_from_user_toio - copy data from user-space to mmio-space
@@ -65,27 +76,51 @@ EXPORT_SYMBOL(copy_to_user_fromio);
*
* Copies the data from user-space to mmio-space.
*
- * Returns zero if successful, or non-zero on failure.
+ * Return: Zero if successful, or non-zero on failure.
*/
int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size_t count)
{
+ struct iov_iter iter;
+
+ if (import_ubuf(ITER_SOURCE, (void __user *)src, count, &iter))
+ return -EFAULT;
+ if (copy_from_iter_toio((void __iomem *)dst, count, &iter) != count)
+ return -EFAULT;
+ return 0;
+}
+EXPORT_SYMBOL(copy_from_user_toio);
+
+/**
+ * copy_from_iter_toio - copy data from iov_iter to mmio-space
+ * @dst: the destination pointer on mmio-space
+ * @count: the data size to copy in bytes
+ * @src: the source iov_iter
+ *
+ * Copies the data from iov_iter to mmio-space.
+ *
+ * Return: number of bytes to be copied
+ */
+size_t copy_from_iter_toio(void __iomem *dst, size_t count,
+ struct iov_iter *src)
+{
#if defined(__i386__) || defined(CONFIG_SPARC32)
- return copy_from_user((void __force *)dst, src, count) ? -EFAULT : 0;
+ return copy_from_iter((void __force *)dst, count, src);
#else
char buf[256];
+ size_t res = 0;
+
while (count) {
size_t c = count;
if (c > sizeof(buf))
c = sizeof(buf);
- if (copy_from_user(buf, src, c))
- return -EFAULT;
+ if (copy_from_iter(buf, c, src) != c)
+ return res;
memcpy_toio(dst, buf, c);
count -= c;
dst += c;
- src += c;
+ res += c;
}
- return 0;
+ return res;
#endif
}
-
-EXPORT_SYMBOL(copy_from_user_toio);
+EXPORT_SYMBOL(copy_from_iter_toio);
diff --git a/sound/core/misc.c b/sound/core/misc.c
index 2c41825c836e..88d9e1f9a6e9 100644
--- a/sound/core/misc.c
+++ b/sound/core/misc.c
@@ -1,44 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Misc and compatibility things
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/moduleparam.h>
#include <linux/time.h>
#include <linux/slab.h>
#include <linux/ioport.h>
+#include <linux/fs.h>
#include <sound/core.h>
-#ifdef CONFIG_SND_DEBUG
-
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-#define DEFAULT_DEBUG_LEVEL 2
-#else
-#define DEFAULT_DEBUG_LEVEL 1
-#endif
-
-static int debug = DEFAULT_DEBUG_LEVEL;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Debug level (0 = disable)");
-
-#endif /* CONFIG_SND_DEBUG */
-
void release_and_free_resource(struct resource *res)
{
if (res) {
@@ -46,59 +20,8 @@ void release_and_free_resource(struct resource *res)
kfree(res);
}
}
-
EXPORT_SYMBOL(release_and_free_resource);
-#ifdef CONFIG_SND_VERBOSE_PRINTK
-/* strip the leading path if the given path is absolute */
-static const char *sanity_file_name(const char *path)
-{
- if (*path == '/')
- return strrchr(path, '/') + 1;
- else
- return path;
-}
-
-/* print file and line with a certain printk prefix */
-static int print_snd_pfx(unsigned int level, const char *path, int line,
- const char *format)
-{
- const char *file = sanity_file_name(path);
- char tmp[] = "<0>";
- const char *pfx = level ? KERN_DEBUG : KERN_DEFAULT;
- int ret = 0;
-
- if (format[0] == '<' && format[2] == '>') {
- tmp[1] = format[1];
- pfx = tmp;
- ret = 1;
- }
- printk("%sALSA %s:%d: ", pfx, file, line);
- return ret;
-}
-#else
-#define print_snd_pfx(level, path, line, format) 0
-#endif
-
-#if defined(CONFIG_SND_DEBUG) || defined(CONFIG_SND_VERBOSE_PRINTK)
-void __snd_printk(unsigned int level, const char *path, int line,
- const char *format, ...)
-{
- va_list args;
-
-#ifdef CONFIG_SND_DEBUG
- if (debug < level)
- return;
-#endif
- va_start(args, format);
- if (print_snd_pfx(level, path, line, format))
- format += 3; /* skip the printk level-prefix */
- vprintk(format, args);
- va_end(args);
-}
-EXPORT_SYMBOL_GPL(__snd_printk);
-#endif
-
#ifdef CONFIG_PCI
#include <linux/pci.h>
/**
@@ -119,7 +42,7 @@ snd_pci_quirk_lookup_id(u16 vendor, u16 device,
{
const struct snd_pci_quirk *q;
- for (q = list; q->subvendor; q++) {
+ for (q = list; q->subvendor || q->subdevice; q++) {
if (q->subvendor != vendor)
continue;
if (!q->subdevice ||
@@ -144,9 +67,99 @@ EXPORT_SYMBOL(snd_pci_quirk_lookup_id);
const struct snd_pci_quirk *
snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list)
{
+ if (!pci)
+ return NULL;
return snd_pci_quirk_lookup_id(pci->subsystem_vendor,
pci->subsystem_device,
list);
}
EXPORT_SYMBOL(snd_pci_quirk_lookup);
#endif
+
+/*
+ * Deferred async signal helpers
+ *
+ * Below are a few helper functions to wrap the async signal handling
+ * in the deferred work. The main purpose is to avoid the messy deadlock
+ * around tasklist_lock and co at the kill_fasync() invocation.
+ * fasync_helper() and kill_fasync() are replaced with snd_fasync_helper()
+ * and snd_kill_fasync(), respectively. In addition, snd_fasync_free() has
+ * to be called at releasing the relevant file object.
+ */
+struct snd_fasync {
+ struct fasync_struct *fasync;
+ int signal;
+ int poll;
+ int on;
+ struct list_head list;
+};
+
+static DEFINE_SPINLOCK(snd_fasync_lock);
+static LIST_HEAD(snd_fasync_list);
+
+static void snd_fasync_work_fn(struct work_struct *work)
+{
+ struct snd_fasync *fasync;
+
+ spin_lock_irq(&snd_fasync_lock);
+ while (!list_empty(&snd_fasync_list)) {
+ fasync = list_first_entry(&snd_fasync_list, struct snd_fasync, list);
+ list_del_init(&fasync->list);
+ spin_unlock_irq(&snd_fasync_lock);
+ if (fasync->on)
+ kill_fasync(&fasync->fasync, fasync->signal, fasync->poll);
+ spin_lock_irq(&snd_fasync_lock);
+ }
+ spin_unlock_irq(&snd_fasync_lock);
+}
+
+static DECLARE_WORK(snd_fasync_work, snd_fasync_work_fn);
+
+int snd_fasync_helper(int fd, struct file *file, int on,
+ struct snd_fasync **fasyncp)
+{
+ struct snd_fasync *fasync = NULL;
+
+ if (on) {
+ fasync = kzalloc(sizeof(*fasync), GFP_KERNEL);
+ if (!fasync)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&fasync->list);
+ }
+
+ scoped_guard(spinlock_irq, &snd_fasync_lock) {
+ if (*fasyncp) {
+ kfree(fasync);
+ fasync = *fasyncp;
+ } else {
+ if (!fasync)
+ return 0;
+ *fasyncp = fasync;
+ }
+ fasync->on = on;
+ }
+ return fasync_helper(fd, file, on, &fasync->fasync);
+}
+EXPORT_SYMBOL_GPL(snd_fasync_helper);
+
+void snd_kill_fasync(struct snd_fasync *fasync, int signal, int poll)
+{
+ if (!fasync || !fasync->on)
+ return;
+ guard(spinlock_irqsave)(&snd_fasync_lock);
+ fasync->signal = signal;
+ fasync->poll = poll;
+ list_move(&fasync->list, &snd_fasync_list);
+ schedule_work(&snd_fasync_work);
+}
+EXPORT_SYMBOL_GPL(snd_kill_fasync);
+
+void snd_fasync_free(struct snd_fasync *fasync)
+{
+ if (!fasync)
+ return;
+ fasync->on = 0;
+ flush_work(&snd_fasync_work);
+ kfree(fasync);
+}
+EXPORT_SYMBOL_GPL(snd_fasync_free);
diff --git a/sound/core/oss/Makefile b/sound/core/oss/Makefile
index 10a79453245f..d5f48ae6ba96 100644
--- a/sound/core/oss/Makefile
+++ b/sound/core/oss/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
#
-snd-mixer-oss-objs := mixer_oss.o
+snd-mixer-oss-y := mixer_oss.o
snd-pcm-oss-y := pcm_oss.o
snd-pcm-oss-$(CONFIG_SND_PCM_OSS_PLUGINS) += pcm_plugin.o \
diff --git a/sound/core/oss/io.c b/sound/core/oss/io.c
index 6faa1d719206..d870b2d93135 100644
--- a/sound/core/oss/io.c
+++ b/sound/core/oss/io.c
@@ -26,9 +26,9 @@
#include "pcm_plugin.h"
#define pcm_write(plug,buf,count) snd_pcm_oss_write3(plug,buf,count,1)
-#define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count,1)
+#define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count)
#define pcm_read(plug,buf,count) snd_pcm_oss_read3(plug,buf,count,1)
-#define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count,1)
+#define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count)
/*
* Basic io plugin
diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c
index 4c1d16827199..797d838a2f9e 100644
--- a/sound/core/oss/linear.c
+++ b/sound/core/oss/linear.c
@@ -90,11 +90,8 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin,
struct snd_pcm_plugin_channel *dst_channels,
snd_pcm_uframes_t frames)
{
- struct linear_priv *data;
-
if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
return -ENXIO;
- data = (struct linear_priv *)plugin->extra_data;
if (frames == 0)
return 0;
#ifdef CONFIG_SND_DEBUG
@@ -110,11 +107,14 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin,
}
}
#endif
+ if (frames > dst_channels[0].frames)
+ frames = dst_channels[0].frames;
convert(plugin, src_channels, dst_channels, frames);
return frames;
}
-static void init_data(struct linear_priv *data, int src_format, int dst_format)
+static void init_data(struct linear_priv *data,
+ snd_pcm_format_t src_format, snd_pcm_format_t dst_format)
{
int src_le, dst_le, src_bytes, dst_bytes;
@@ -140,9 +140,9 @@ static void init_data(struct linear_priv *data, int src_format, int dst_format)
if (snd_pcm_format_signed(src_format) !=
snd_pcm_format_signed(dst_format)) {
if (dst_le)
- data->flip = cpu_to_le32(0x80000000);
+ data->flip = (__force u32)cpu_to_le32(0x80000000);
else
- data->flip = cpu_to_be32(0x80000000);
+ data->flip = (__force u32)cpu_to_be32(0x80000000);
}
}
diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
index 822dd56993ca..e839a4bb93f8 100644
--- a/sound/core/oss/mixer_oss.c
+++ b/sound/core/oss/mixer_oss.c
@@ -1,28 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS emulation layer for the mixer interface
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/compat.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/control.h>
@@ -51,14 +38,19 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file)
SNDRV_OSS_DEVICE_TYPE_MIXER);
if (card == NULL)
return -ENODEV;
- if (card->mixer_oss == NULL)
+ if (card->mixer_oss == NULL) {
+ snd_card_unref(card);
return -ENODEV;
+ }
err = snd_card_file_add(card, file);
- if (err < 0)
+ if (err < 0) {
+ snd_card_unref(card);
return err;
+ }
fmixer = kzalloc(sizeof(*fmixer), GFP_KERNEL);
if (fmixer == NULL) {
snd_card_file_remove(card, file);
+ snd_card_unref(card);
return -ENOMEM;
}
fmixer->card = card;
@@ -67,8 +59,10 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file)
if (!try_module_get(card->module)) {
kfree(fmixer);
snd_card_file_remove(card, file);
+ snd_card_unref(card);
return -EFAULT;
}
+ snd_card_unref(card);
return 0;
}
@@ -93,8 +87,8 @@ static int snd_mixer_oss_info(struct snd_mixer_oss_file *fmixer,
struct mixer_info info;
memset(&info, 0, sizeof(info));
- strlcpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id));
- strlcpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name));
+ strscpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id));
+ strscpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name));
info.modify_counter = card->mixer_oss_change_count;
if (copy_to_user(_info, &info, sizeof(info)))
return -EFAULT;
@@ -109,8 +103,8 @@ static int snd_mixer_oss_info_obsolete(struct snd_mixer_oss_file *fmixer,
_old_mixer_info info;
memset(&info, 0, sizeof(info));
- strlcpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id));
- strlcpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name));
+ strscpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id));
+ strscpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name));
if (copy_to_user(_info, &info, sizeof(info)))
return -EFAULT;
return 0;
@@ -136,6 +130,7 @@ static int snd_mixer_oss_devmask(struct snd_mixer_oss_file *fmixer)
if (mixer == NULL)
return -EIO;
+ guard(mutex)(&mixer->reg_mutex);
for (chn = 0; chn < 31; chn++) {
pslot = &mixer->slots[chn];
if (pslot->put_volume || pslot->put_recsrc)
@@ -152,6 +147,7 @@ static int snd_mixer_oss_stereodevs(struct snd_mixer_oss_file *fmixer)
if (mixer == NULL)
return -EIO;
+ guard(mutex)(&mixer->reg_mutex);
for (chn = 0; chn < 31; chn++) {
pslot = &mixer->slots[chn];
if (pslot->put_volume && pslot->stereo)
@@ -167,6 +163,7 @@ static int snd_mixer_oss_recmask(struct snd_mixer_oss_file *fmixer)
if (mixer == NULL)
return -EIO;
+ guard(mutex)(&mixer->reg_mutex);
if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */
result = mixer->mask_recsrc;
} else {
@@ -188,11 +185,13 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer)
if (mixer == NULL)
return -EIO;
+ guard(mutex)(&mixer->reg_mutex);
if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */
- int err;
- if ((err = mixer->get_recsrc(fmixer, &result)) < 0)
- return err;
- result = 1 << result;
+ unsigned int index;
+ result = mixer->get_recsrc(fmixer, &index);
+ if (result < 0)
+ return result;
+ result = 1 << index;
} else {
struct snd_mixer_oss_slot *pslot;
int chn;
@@ -206,7 +205,8 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer)
}
}
}
- return mixer->oss_recsrc = result;
+ mixer->oss_recsrc = result;
+ return result;
}
static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsrc)
@@ -214,16 +214,18 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr
struct snd_mixer_oss *mixer = fmixer->mixer;
struct snd_mixer_oss_slot *pslot;
int chn, active;
+ unsigned int index;
int result = 0;
if (mixer == NULL)
return -EIO;
+ guard(mutex)(&mixer->reg_mutex);
if (mixer->get_recsrc && mixer->put_recsrc) { /* exclusive input */
if (recsrc & ~mixer->oss_recsrc)
recsrc &= ~mixer->oss_recsrc;
mixer->put_recsrc(fmixer, ffz(~recsrc));
- mixer->get_recsrc(fmixer, &result);
- result = 1 << result;
+ mixer->get_recsrc(fmixer, &index);
+ result = 1 << index;
}
for (chn = 0; chn < 31; chn++) {
pslot = &mixer->slots[chn];
@@ -254,6 +256,7 @@ static int snd_mixer_oss_get_volume(struct snd_mixer_oss_file *fmixer, int slot)
if (mixer == NULL || slot > 30)
return -EIO;
+ guard(mutex)(&mixer->reg_mutex);
pslot = &mixer->slots[slot];
left = pslot->volume[0];
right = pslot->volume[1];
@@ -282,6 +285,7 @@ static int snd_mixer_oss_set_volume(struct snd_mixer_oss_file *fmixer,
if (mixer == NULL || slot > 30)
return -EIO;
+ guard(mutex)(&mixer->reg_mutex);
pslot = &mixer->slots[slot];
if (left > 100)
left = 100;
@@ -295,7 +299,8 @@ static int snd_mixer_oss_set_volume(struct snd_mixer_oss_file *fmixer,
return result;
pslot->volume[0] = left;
pslot->volume[1] = right;
- return (left & 0xff) | ((right & 0xff) << 8);
+ result = (left & 0xff) | ((right & 0xff) << 8);
+ return result;
}
static int snd_mixer_oss_ioctl1(struct snd_mixer_oss_file *fmixer, unsigned int cmd, unsigned long arg)
@@ -384,10 +389,16 @@ int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned l
fmixer.mixer = card->mixer_oss;
return snd_mixer_oss_ioctl1(&fmixer, cmd, arg);
}
+EXPORT_SYMBOL(snd_mixer_oss_ioctl_card);
#ifdef CONFIG_COMPAT
/* all compatible */
-#define snd_mixer_oss_ioctl_compat snd_mixer_oss_ioctl
+static long snd_mixer_oss_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return snd_mixer_oss_ioctl1(file->private_data, cmd,
+ (unsigned long)compat_ptr(arg));
+}
#else
#define snd_mixer_oss_ioctl_compat NULL
#endif
@@ -401,7 +412,6 @@ static const struct file_operations snd_mixer_oss_f_ops =
.owner = THIS_MODULE,
.open = snd_mixer_oss_open,
.release = snd_mixer_oss_release,
- .llseek = no_llseek,
.unlocked_ioctl = snd_mixer_oss_ioctl,
.compat_ioctl = snd_mixer_oss_ioctl_compat,
};
@@ -416,7 +426,7 @@ static long snd_mixer_oss_conv(long val, long omin, long omax, long nmin, long n
if (orange == 0)
return 0;
- return ((nrange * (val - omin)) + (orange / 2)) / orange + nmin;
+ return DIV_ROUND_CLOSEST(nrange * (val - omin), orange) + nmin;
}
/* convert from alsa native to oss values (0-100) */
@@ -484,7 +494,7 @@ struct slot {
unsigned int channels;
unsigned int numid[SNDRV_MIXER_OSS_ITEM_COUNT];
unsigned int capture_item;
- struct snd_mixer_oss_assign_table *assigned;
+ const struct snd_mixer_oss_assign_table *assigned;
unsigned int allocated: 1;
};
@@ -497,7 +507,7 @@ static struct snd_kcontrol *snd_mixer_oss_test_id(struct snd_mixer_oss *mixer, c
memset(&id, 0, sizeof(id));
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, name);
+ strscpy(id.name, name, sizeof(id.name));
id.index = index;
return snd_ctl_find_id(card, &id);
}
@@ -507,36 +517,31 @@ static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer,
unsigned int numid,
int *left, int *right)
{
- struct snd_ctl_elem_info *uinfo;
- struct snd_ctl_elem_value *uctl;
+ struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
+ struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
struct snd_kcontrol *kctl;
struct snd_card *card = fmixer->card;
if (numid == ID_UNKNOWN)
return;
- down_read(&card->controls_rwsem);
- if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
- up_read(&card->controls_rwsem);
+ guard(rwsem_read)(&card->controls_rwsem);
+ kctl = snd_ctl_find_numid(card, numid);
+ if (!kctl)
return;
- }
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL)
- goto __unalloc;
+ return;
if (kctl->info(kctl, uinfo))
- goto __unalloc;
+ return;
if (kctl->get(kctl, uctl))
- goto __unalloc;
+ return;
if (uinfo->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN &&
uinfo->value.integer.min == 0 && uinfo->value.integer.max == 1)
- goto __unalloc;
+ return;
*left = snd_mixer_oss_conv1(uctl->value.integer.value[0], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[0]);
if (uinfo->count > 1)
*right = snd_mixer_oss_conv1(uctl->value.integer.value[1], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[1]);
- __unalloc:
- up_read(&card->controls_rwsem);
- kfree(uctl);
- kfree(uinfo);
}
static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer,
@@ -545,26 +550,25 @@ static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer,
int *left, int *right,
int route)
{
- struct snd_ctl_elem_info *uinfo;
- struct snd_ctl_elem_value *uctl;
+ struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
+ struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
struct snd_kcontrol *kctl;
struct snd_card *card = fmixer->card;
if (numid == ID_UNKNOWN)
return;
- down_read(&card->controls_rwsem);
- if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
- up_read(&card->controls_rwsem);
+ guard(rwsem_read)(&card->controls_rwsem);
+ kctl = snd_ctl_find_numid(card, numid);
+ if (!kctl)
return;
- }
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL)
- goto __unalloc;
+ return;
if (kctl->info(kctl, uinfo))
- goto __unalloc;
+ return;
if (kctl->get(kctl, uctl))
- goto __unalloc;
+ return;
if (!uctl->value.integer.value[0]) {
*left = 0;
if (uinfo->count == 1)
@@ -572,10 +576,6 @@ static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer,
}
if (uinfo->count > 1 && !uctl->value.integer.value[route ? 3 : 1])
*right = 0;
- __unalloc:
- up_read(&card->controls_rwsem);
- kfree(uctl);
- kfree(uinfo);
}
static int snd_mixer_oss_get_volume1(struct snd_mixer_oss_file *fmixer,
@@ -609,39 +609,35 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer,
unsigned int numid,
int left, int right)
{
- struct snd_ctl_elem_info *uinfo;
- struct snd_ctl_elem_value *uctl;
+ struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
+ struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
struct snd_kcontrol *kctl;
struct snd_card *card = fmixer->card;
int res;
if (numid == ID_UNKNOWN)
return;
- down_read(&card->controls_rwsem);
- if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
- up_read(&card->controls_rwsem);
+ guard(rwsem_read)(&card->controls_rwsem);
+ kctl = snd_ctl_find_numid(card, numid);
+ if (!kctl)
return;
- }
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL)
- goto __unalloc;
+ return;
if (kctl->info(kctl, uinfo))
- goto __unalloc;
+ return;
if (uinfo->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN &&
uinfo->value.integer.min == 0 && uinfo->value.integer.max == 1)
- goto __unalloc;
+ return;
uctl->value.integer.value[0] = snd_mixer_oss_conv2(left, uinfo->value.integer.min, uinfo->value.integer.max);
if (uinfo->count > 1)
uctl->value.integer.value[1] = snd_mixer_oss_conv2(right, uinfo->value.integer.min, uinfo->value.integer.max);
- if ((res = kctl->put(kctl, uctl)) < 0)
- goto __unalloc;
+ res = kctl->put(kctl, uctl);
+ if (res < 0)
+ return;
if (res > 0)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
- __unalloc:
- up_read(&card->controls_rwsem);
- kfree(uctl);
- kfree(uinfo);
}
static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
@@ -650,25 +646,24 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
int left, int right,
int route)
{
- struct snd_ctl_elem_info *uinfo;
- struct snd_ctl_elem_value *uctl;
+ struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
+ struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
struct snd_kcontrol *kctl;
struct snd_card *card = fmixer->card;
int res;
if (numid == ID_UNKNOWN)
return;
- down_read(&card->controls_rwsem);
- if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
- up_read(&card->controls_rwsem);
+ guard(rwsem_read)(&card->controls_rwsem);
+ kctl = snd_ctl_find_numid(card, numid);
+ if (!kctl)
return;
- }
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (uinfo == NULL || uctl == NULL)
- goto __unalloc;
+ return;
if (kctl->info(kctl, uinfo))
- goto __unalloc;
+ return;
if (uinfo->count > 1) {
uctl->value.integer.value[0] = left > 0 ? 1 : 0;
uctl->value.integer.value[route ? 3 : 1] = right > 0 ? 1 : 0;
@@ -679,14 +674,11 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
} else {
uctl->value.integer.value[0] = (left > 0 || right > 0) ? 1 : 0;
}
- if ((res = kctl->put(kctl, uctl)) < 0)
- goto __unalloc;
+ res = kctl->put(kctl, uctl);
+ if (res < 0)
+ return;
if (res > 0)
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
- __unalloc:
- up_read(&card->controls_rwsem);
- kfree(uctl);
- kfree(uinfo);
}
static int snd_mixer_oss_put_volume1(struct snd_mixer_oss_file *fmixer,
@@ -791,26 +783,24 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
struct snd_kcontrol *kctl;
struct snd_mixer_oss_slot *pslot;
struct slot *slot;
- struct snd_ctl_elem_info *uinfo;
- struct snd_ctl_elem_value *uctl;
+ struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
+ struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
int err, idx;
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
- if (uinfo == NULL || uctl == NULL) {
- err = -ENOMEM;
- goto __free_only;
- }
- down_read(&card->controls_rwsem);
+ if (uinfo == NULL || uctl == NULL)
+ return -ENOMEM;
+ guard(rwsem_read)(&card->controls_rwsem);
kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
- if (! kctl) {
- err = -ENOENT;
- goto __unlock;
- }
- if ((err = kctl->info(kctl, uinfo)) < 0)
- goto __unlock;
- if ((err = kctl->get(kctl, uctl)) < 0)
- goto __unlock;
+ if (!kctl)
+ return -ENOENT;
+ err = kctl->info(kctl, uinfo);
+ if (err < 0)
+ return err;
+ err = kctl->get(kctl, uctl);
+ if (err < 0)
+ return err;
for (idx = 0; idx < 32; idx++) {
if (!(mixer->mask_recsrc & (1 << idx)))
continue;
@@ -825,13 +815,7 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
break;
}
}
- err = 0;
- __unlock:
- up_read(&card->controls_rwsem);
- __free_only:
- kfree(uctl);
- kfree(uinfo);
- return err;
+ return 0;
}
static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned int active_index)
@@ -841,25 +825,22 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
struct snd_kcontrol *kctl;
struct snd_mixer_oss_slot *pslot;
struct slot *slot = NULL;
- struct snd_ctl_elem_info *uinfo;
- struct snd_ctl_elem_value *uctl;
+ struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
+ struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
int err;
unsigned int idx;
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
- if (uinfo == NULL || uctl == NULL) {
- err = -ENOMEM;
- goto __free_only;
- }
- down_read(&card->controls_rwsem);
+ if (uinfo == NULL || uctl == NULL)
+ return -ENOMEM;
+ guard(rwsem_read)(&card->controls_rwsem);
kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
- if (! kctl) {
- err = -ENOENT;
- goto __unlock;
- }
- if ((err = kctl->info(kctl, uinfo)) < 0)
- goto __unlock;
+ if (!kctl)
+ return -ENOENT;
+ err = kctl->info(kctl, uinfo);
+ if (err < 0)
+ return err;
for (idx = 0; idx < 32; idx++) {
if (!(mixer->mask_recsrc & (1 << idx)))
continue;
@@ -873,20 +854,14 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
break;
slot = NULL;
}
- if (! slot)
- goto __unlock;
+ if (!slot)
+ return 0;
for (idx = 0; idx < uinfo->count; idx++)
uctl->value.enumerated.item[idx] = slot->capture_item;
err = kctl->put(kctl, uctl);
if (err > 0)
snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
- err = 0;
- __unlock:
- up_read(&card->controls_rwsem);
- __free_only:
- kfree(uctl);
- kfree(uinfo);
- return err;
+ return 0;
}
struct snd_mixer_oss_assign_table {
@@ -897,33 +872,26 @@ struct snd_mixer_oss_assign_table {
static int snd_mixer_oss_build_test(struct snd_mixer_oss *mixer, struct slot *slot, const char *name, int index, int item)
{
- struct snd_ctl_elem_info *info;
+ struct snd_ctl_elem_info *info __free(kfree) = NULL;
struct snd_kcontrol *kcontrol;
struct snd_card *card = mixer->card;
int err;
- down_read(&card->controls_rwsem);
- kcontrol = snd_mixer_oss_test_id(mixer, name, index);
- if (kcontrol == NULL) {
- up_read(&card->controls_rwsem);
- return 0;
- }
- info = kmalloc(sizeof(*info), GFP_KERNEL);
- if (! info) {
- up_read(&card->controls_rwsem);
- return -ENOMEM;
- }
- if ((err = kcontrol->info(kcontrol, info)) < 0) {
- up_read(&card->controls_rwsem);
- kfree(info);
- return err;
+ scoped_guard(rwsem_read, &card->controls_rwsem) {
+ kcontrol = snd_mixer_oss_test_id(mixer, name, index);
+ if (kcontrol == NULL)
+ return 0;
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ err = kcontrol->info(kcontrol, info);
+ if (err < 0)
+ return err;
+ slot->numid[item] = kcontrol->id.numid;
}
- slot->numid[item] = kcontrol->id.numid;
- up_read(&card->controls_rwsem);
if (info->count > slot->channels)
slot->channels = info->count;
slot->present |= 1 << item;
- kfree(info);
return 0;
}
@@ -951,7 +919,7 @@ static void mixer_slot_clear(struct snd_mixer_oss_slot *rslot)
/* In a separate function to keep gcc 3.2 happy - do NOT merge this in
snd_mixer_oss_build_input! */
static int snd_mixer_oss_build_test_all(struct snd_mixer_oss *mixer,
- struct snd_mixer_oss_assign_table *ptr,
+ const struct snd_mixer_oss_assign_table *ptr,
struct slot *slot)
{
char str[64];
@@ -1015,13 +983,15 @@ static int snd_mixer_oss_build_test_all(struct snd_mixer_oss *mixer,
* ptr_allocated means the entry is dynamically allocated (change via proc file).
* when replace_old = 1, the old entry is replaced with the new one.
*/
-static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mixer_oss_assign_table *ptr, int ptr_allocated, int replace_old)
+static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer,
+ const struct snd_mixer_oss_assign_table *ptr,
+ int ptr_allocated, int replace_old)
{
struct slot slot;
struct slot *pslot;
struct snd_kcontrol *kctl;
struct snd_mixer_oss_slot *rslot;
- char str[64];
+ const char *str;
/* check if already assigned */
if (mixer->slots[ptr->oss_id].get_volume && ! replace_old)
@@ -1031,44 +1001,39 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mix
memset(slot.numid, 0xff, sizeof(slot.numid)); /* ID_UNKNOWN */
if (snd_mixer_oss_build_test_all(mixer, ptr, &slot))
return 0;
- down_read(&mixer->card->controls_rwsem);
- if (ptr->index == 0 && (kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0)) != NULL) {
- struct snd_ctl_elem_info *uinfo;
+ guard(rwsem_read)(&mixer->card->controls_rwsem);
+ kctl = NULL;
+ if (!ptr->index)
+ kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
+ if (kctl) {
+ struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
- if (! uinfo) {
- up_read(&mixer->card->controls_rwsem);
+ if (!uinfo)
return -ENOMEM;
- }
- if (kctl->info(kctl, uinfo)) {
- up_read(&mixer->card->controls_rwsem);
+ if (kctl->info(kctl, uinfo))
return 0;
- }
- strcpy(str, ptr->name);
+ str = ptr->name;
if (!strcmp(str, "Master"))
- strcpy(str, "Mix");
- if (!strcmp(str, "Master Mono"))
- strcpy(str, "Mix Mono");
+ str = "Mix";
+ else if (!strcmp(str, "Master Mono"))
+ str = "Mix Mono";
slot.capture_item = 0;
if (!strcmp(uinfo->value.enumerated.name, str)) {
slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
} else {
for (slot.capture_item = 1; slot.capture_item < uinfo->value.enumerated.items; slot.capture_item++) {
uinfo->value.enumerated.item = slot.capture_item;
- if (kctl->info(kctl, uinfo)) {
- up_read(&mixer->card->controls_rwsem);
+ if (kctl->info(kctl, uinfo))
return 0;
- }
if (!strcmp(uinfo->value.enumerated.name, str)) {
slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
break;
}
}
}
- kfree(uinfo);
}
- up_read(&mixer->card->controls_rwsem);
if (slot.present != 0) {
pslot = kmalloc(sizeof(slot), GFP_KERNEL);
if (! pslot)
@@ -1099,11 +1064,11 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer, struct snd_mix
return 0;
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
*/
#define MIXER_VOL(name) [SOUND_MIXER_##name] = #name
-static char *oss_mixer_names[SNDRV_OSS_MAX_MIXERS] = {
+static const char * const oss_mixer_names[SNDRV_OSS_MAX_MIXERS] = {
MIXER_VOL(VOLUME),
MIXER_VOL(BASS),
MIXER_VOL(TREBLE),
@@ -1141,7 +1106,7 @@ static void snd_mixer_oss_proc_read(struct snd_info_entry *entry,
struct snd_mixer_oss *mixer = entry->private_data;
int i;
- mutex_lock(&mixer->reg_mutex);
+ guard(mutex)(&mixer->reg_mutex);
for (i = 0; i < SNDRV_OSS_MAX_MIXERS; i++) {
struct slot *p;
@@ -1156,7 +1121,6 @@ static void snd_mixer_oss_proc_read(struct snd_info_entry *entry,
else
snd_iprintf(buffer, "\"\" 0\n");
}
- mutex_unlock(&mixer->reg_mutex);
}
static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
@@ -1165,7 +1129,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
struct snd_mixer_oss *mixer = entry->private_data;
char line[128], str[32], idxstr[16];
const char *cptr;
- int ch, idx;
+ unsigned int idx;
+ int ch;
struct snd_mixer_oss_assign_table *tbl;
struct slot *slot;
@@ -1175,47 +1140,44 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
if (oss_mixer_names[ch] && strcmp(oss_mixer_names[ch], str) == 0)
break;
if (ch >= SNDRV_OSS_MAX_MIXERS) {
- snd_printk(KERN_ERR "mixer_oss: invalid OSS volume '%s'\n", str);
+ pr_err("ALSA: mixer_oss: invalid OSS volume '%s'\n",
+ str);
continue;
}
cptr = snd_info_get_str(str, cptr, sizeof(str));
if (! *str) {
/* remove the entry */
- mutex_lock(&mixer->reg_mutex);
- mixer_slot_clear(&mixer->slots[ch]);
- mutex_unlock(&mixer->reg_mutex);
+ scoped_guard(mutex, &mixer->reg_mutex)
+ mixer_slot_clear(&mixer->slots[ch]);
continue;
}
snd_info_get_str(idxstr, cptr, sizeof(idxstr));
idx = simple_strtoul(idxstr, NULL, 10);
if (idx >= 0x4000) { /* too big */
- snd_printk(KERN_ERR "mixer_oss: invalid index %d\n", idx);
+ pr_err("ALSA: mixer_oss: invalid index %d\n", idx);
continue;
}
- mutex_lock(&mixer->reg_mutex);
- slot = (struct slot *)mixer->slots[ch].private_data;
- if (slot && slot->assigned &&
- slot->assigned->index == idx && ! strcmp(slot->assigned->name, str))
- /* not changed */
- goto __unlock;
- tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
- if (! tbl) {
- snd_printk(KERN_ERR "mixer_oss: no memory\n");
- goto __unlock;
- }
- tbl->oss_id = ch;
- tbl->name = kstrdup(str, GFP_KERNEL);
- if (! tbl->name) {
- kfree(tbl);
- goto __unlock;
- }
- tbl->index = idx;
- if (snd_mixer_oss_build_input(mixer, tbl, 1, 1) <= 0) {
- kfree(tbl->name);
- kfree(tbl);
+ scoped_guard(mutex, &mixer->reg_mutex) {
+ slot = (struct slot *)mixer->slots[ch].private_data;
+ if (slot && slot->assigned &&
+ slot->assigned->index == idx && !strcmp(slot->assigned->name, str))
+ /* not changed */
+ break;
+ tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
+ if (!tbl)
+ break;
+ tbl->oss_id = ch;
+ tbl->name = kstrdup(str, GFP_KERNEL);
+ if (!tbl->name) {
+ kfree(tbl);
+ break;
+ }
+ tbl->index = idx;
+ if (snd_mixer_oss_build_input(mixer, tbl, 1, 1) <= 0) {
+ kfree(tbl->name);
+ kfree(tbl);
+ }
}
- __unlock:
- mutex_unlock(&mixer->reg_mutex);
}
}
@@ -1228,7 +1190,7 @@ static void snd_mixer_oss_proc_init(struct snd_mixer_oss *mixer)
if (! entry)
return;
entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->mode = S_IFREG | 0644;
entry->c.text.read = snd_mixer_oss_proc_read;
entry->c.text.write = snd_mixer_oss_proc_write;
entry->private_data = mixer;
@@ -1244,14 +1206,14 @@ static void snd_mixer_oss_proc_done(struct snd_mixer_oss *mixer)
snd_info_free_entry(mixer->proc_entry);
mixer->proc_entry = NULL;
}
-#else /* !CONFIG_PROC_FS */
+#else /* !CONFIG_SND_PROC_FS */
#define snd_mixer_oss_proc_init(mix)
#define snd_mixer_oss_proc_done(mix)
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
static void snd_mixer_oss_build(struct snd_mixer_oss *mixer)
{
- static struct snd_mixer_oss_assign_table table[] = {
+ static const struct snd_mixer_oss_assign_table table[] = {
{ SOUND_MIXER_VOLUME, "Master", 0 },
{ SOUND_MIXER_VOLUME, "Front", 0 }, /* fallback */
{ SOUND_MIXER_BASS, "Tone Control - Bass", 0 },
@@ -1331,29 +1293,29 @@ static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)
struct snd_mixer_oss *mixer;
if (cmd == SND_MIXER_OSS_NOTIFY_REGISTER) {
- char name[128];
int idx, err;
mixer = kcalloc(2, sizeof(*mixer), GFP_KERNEL);
if (mixer == NULL)
return -ENOMEM;
mutex_init(&mixer->reg_mutex);
- sprintf(name, "mixer%i%i", card->number, 0);
- if ((err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER,
- card, 0,
- &snd_mixer_oss_f_ops, card,
- name)) < 0) {
- snd_printk(KERN_ERR "unable to register OSS mixer device %i:%i\n",
- card->number, 0);
+ err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER,
+ card, 0,
+ &snd_mixer_oss_f_ops, card);
+ if (err < 0) {
+ dev_err(card->dev,
+ "unable to register OSS mixer device %i:%i\n",
+ card->number, 0);
kfree(mixer);
return err;
}
mixer->oss_dev_alloc = 1;
mixer->card = card;
if (*card->mixername)
- strlcpy(mixer->name, card->mixername, sizeof(mixer->name));
+ strscpy(mixer->name, card->mixername, sizeof(mixer->name));
else
- strlcpy(mixer->name, name, sizeof(mixer->name));
+ snprintf(mixer->name, sizeof(mixer->name),
+ "mixer%i", card->number);
#ifdef SNDRV_OSS_INFO_DEV_MIXERS
snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS,
card->number,
@@ -1385,28 +1347,34 @@ static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)
static int __init alsa_mixer_oss_init(void)
{
+ struct snd_card *card;
int idx;
snd_mixer_oss_notify_callback = snd_mixer_oss_notify_handler;
for (idx = 0; idx < SNDRV_CARDS; idx++) {
- if (snd_cards[idx])
- snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_REGISTER);
+ card = snd_card_ref(idx);
+ if (card) {
+ snd_mixer_oss_notify_handler(card, SND_MIXER_OSS_NOTIFY_REGISTER);
+ snd_card_unref(card);
+ }
}
return 0;
}
static void __exit alsa_mixer_oss_exit(void)
{
+ struct snd_card *card;
int idx;
snd_mixer_oss_notify_callback = NULL;
for (idx = 0; idx < SNDRV_CARDS; idx++) {
- if (snd_cards[idx])
- snd_mixer_oss_notify_handler(snd_cards[idx], SND_MIXER_OSS_NOTIFY_FREE);
+ card = snd_card_ref(idx);
+ if (card) {
+ snd_mixer_oss_notify_handler(card, SND_MIXER_OSS_NOTIFY_FREE);
+ snd_card_unref(card);
+ }
}
}
module_init(alsa_mixer_oss_init)
module_exit(alsa_mixer_oss_exit)
-
-EXPORT_SYMBOL(snd_mixer_oss_ioctl_card);
diff --git a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c
index f7649d4d950b..fe27034f2846 100644
--- a/sound/core/oss/mulaw.c
+++ b/sound/core/oss/mulaw.c
@@ -269,12 +269,14 @@ static snd_pcm_sframes_t mulaw_transfer(struct snd_pcm_plugin *plugin,
}
}
#endif
+ if (frames > dst_channels[0].frames)
+ frames = dst_channels[0].frames;
data = (struct mulaw_priv *)plugin->extra_data;
data->func(plugin, src_channels, dst_channels, frames);
return frames;
}
-static void init_data(struct mulaw_priv *data, int format)
+static void init_data(struct mulaw_priv *data, snd_pcm_format_t format)
{
#ifdef SNDRV_LITTLE_ENDIAN
data->cvt_endian = snd_pcm_format_big_endian(format) > 0;
@@ -327,8 +329,8 @@ int snd_pcm_plugin_build_mulaw(struct snd_pcm_substream *plug,
snd_BUG();
return -EINVAL;
}
- if (snd_BUG_ON(!snd_pcm_format_linear(format->format)))
- return -ENXIO;
+ if (!snd_pcm_format_linear(format->format))
+ return -EINVAL;
err = snd_pcm_plugin_build(plug, "Mu-Law<->linear conversion",
src_format, dst_format,
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index 5c8c7dff8ede..a82dd155e1d3 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Digital Audio (PCM) abstract layer / OSS compatible
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#if 0
@@ -28,11 +13,13 @@
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/sched/signal.h>
#include <linux/time.h>
#include <linux/vmalloc.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/math64.h>
#include <linux/string.h>
+#include <linux/compat.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/pcm.h>
@@ -41,12 +28,13 @@
#include <sound/info.h>
#include <linux/soundcard.h>
#include <sound/initval.h>
+#include <sound/mixer_oss.h>
#define OSS_ALSAEMULVER _SIOR ('M', 249, int)
static int dsp_map[SNDRV_CARDS];
static int adsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
-static int nonblock_open = 1;
+static bool nonblock_open = 1;
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>");
MODULE_DESCRIPTION("PCM OSS emulation for ALSA.");
@@ -60,23 +48,10 @@ MODULE_PARM_DESC(nonblock_open, "Don't block opening busy PCM devices.");
MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM);
MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM1);
-extern int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned long arg);
static int snd_pcm_oss_get_rate(struct snd_pcm_oss_file *pcm_oss_file);
static int snd_pcm_oss_get_channels(struct snd_pcm_oss_file *pcm_oss_file);
static int snd_pcm_oss_get_format(struct snd_pcm_oss_file *pcm_oss_file);
-static inline mm_segment_t snd_enter_user(void)
-{
- mm_segment_t fs = get_fs();
- set_fs(get_ds());
- return fs;
-}
-
-static inline void snd_leave_user(mm_segment_t fs)
-{
- set_fs(fs);
-}
-
/*
* helper functions to process hw_params
*/
@@ -172,7 +147,7 @@ snd_pcm_hw_param_value_min(const struct snd_pcm_hw_params *params,
*
* Return the maximum value for field PAR.
*/
-static unsigned int
+static int
snd_pcm_hw_param_value_max(const struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, int *dir)
{
@@ -196,7 +171,7 @@ static int _snd_pcm_hw_param_mask(struct snd_pcm_hw_params *params,
{
int changed;
changed = snd_mask_refine(hw_param_mask(params, var), val);
- if (changed) {
+ if (changed > 0) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
}
@@ -243,7 +218,7 @@ static int _snd_pcm_hw_param_min(struct snd_pcm_hw_params *params,
val, open);
else
return -EINVAL;
- if (changed) {
+ if (changed > 0) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
}
@@ -304,7 +279,7 @@ static int _snd_pcm_hw_param_max(struct snd_pcm_hw_params *params,
val, open);
else
return -EINVAL;
- if (changed) {
+ if (changed > 0) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
}
@@ -402,7 +377,7 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,
snd_pcm_hw_param_t var, unsigned int best,
int *dir)
{
- struct snd_pcm_hw_params *save = NULL;
+ struct snd_pcm_hw_params *save __free(kfree) = NULL;
int v;
unsigned int saved_min;
int last = 0;
@@ -429,27 +404,22 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,
saved_min = min;
min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir);
if (min >= 0) {
- struct snd_pcm_hw_params *params1;
+ struct snd_pcm_hw_params *params1 __free(kfree) = NULL;
if (max < 0)
goto _end;
if ((unsigned int)min == saved_min && mindir == valdir)
goto _end;
params1 = kmalloc(sizeof(*params1), GFP_KERNEL);
- if (params1 == NULL) {
- kfree(save);
+ if (params1 == NULL)
return -ENOMEM;
- }
*params1 = *save;
max = snd_pcm_hw_param_max(pcm, params1, var, max, &maxdir);
- if (max < 0) {
- kfree(params1);
+ if (max < 0)
goto _end;
- }
if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) {
*params = *params1;
last = 1;
}
- kfree(params1);
} else {
*params = *save;
max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir);
@@ -458,12 +428,10 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,
last = 1;
}
_end:
- kfree(save);
if (last)
v = snd_pcm_hw_param_last(pcm, params, var, dir);
else
v = snd_pcm_hw_param_first(pcm, params, var, dir);
- snd_BUG_ON(v < 0);
return v;
}
@@ -508,7 +476,7 @@ static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params,
}
} else
return -EINVAL;
- if (changed) {
+ if (changed > 0) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
}
@@ -548,7 +516,7 @@ static int _snd_pcm_hw_param_setinteger(struct snd_pcm_hw_params *params,
{
int changed;
changed = snd_interval_setinteger(hw_param_interval(params, var));
- if (changed) {
+ if (changed > 0) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
}
@@ -654,7 +622,7 @@ snd_pcm_uframes_t get_hw_ptr_period(struct snd_pcm_runtime *runtime)
#define AFMT_AC3 0x00000400
#define AFMT_VORBIS 0x00000800
-static int snd_pcm_oss_format_from(int format)
+static snd_pcm_format_t snd_pcm_oss_format_from(int format)
{
switch (format) {
case AFMT_MU_LAW: return SNDRV_PCM_FORMAT_MU_LAW;
@@ -678,7 +646,7 @@ static int snd_pcm_oss_format_from(int format)
}
}
-static int snd_pcm_oss_format_to(int format)
+static int snd_pcm_oss_format_to(snd_pcm_format_t format)
{
switch (format) {
case SNDRV_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW;
@@ -706,18 +674,26 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *oss_params,
struct snd_pcm_hw_params *slave_params)
{
- size_t s;
- size_t oss_buffer_size, oss_period_size, oss_periods;
- size_t min_period_size, max_period_size;
+ ssize_t s;
+ ssize_t oss_buffer_size;
+ ssize_t oss_period_size, oss_periods;
+ ssize_t min_period_size, max_period_size;
struct snd_pcm_runtime *runtime = substream->runtime;
size_t oss_frame_size;
oss_frame_size = snd_pcm_format_physical_width(params_format(oss_params)) *
params_channels(oss_params) / 8;
+ oss_buffer_size = snd_pcm_hw_param_value_max(slave_params,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ NULL);
+ if (oss_buffer_size <= 0)
+ return -EINVAL;
oss_buffer_size = snd_pcm_plug_client_size(substream,
- snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, NULL)) * oss_frame_size;
- oss_buffer_size = 1 << ld2(oss_buffer_size);
+ oss_buffer_size * oss_frame_size);
+ if (oss_buffer_size <= 0)
+ return -EINVAL;
+ oss_buffer_size = rounddown_pow_of_two(oss_buffer_size);
if (atomic_read(&substream->mmap_count)) {
if (oss_buffer_size > runtime->oss.mmap_bytes)
oss_buffer_size = runtime->oss.mmap_bytes;
@@ -752,17 +728,21 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
min_period_size = snd_pcm_plug_client_size(substream,
snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL));
- min_period_size *= oss_frame_size;
- min_period_size = 1 << (ld2(min_period_size - 1) + 1);
- if (oss_period_size < min_period_size)
- oss_period_size = min_period_size;
+ if (min_period_size > 0) {
+ min_period_size *= oss_frame_size;
+ min_period_size = roundup_pow_of_two(min_period_size);
+ if (oss_period_size < min_period_size)
+ oss_period_size = min_period_size;
+ }
max_period_size = snd_pcm_plug_client_size(substream,
snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL));
- max_period_size *= oss_frame_size;
- max_period_size = 1 << ld2(max_period_size);
- if (oss_period_size > max_period_size)
- oss_period_size = max_period_size;
+ if (max_period_size > 0) {
+ max_period_size *= oss_frame_size;
+ max_period_size = rounddown_pow_of_two(max_period_size);
+ if (oss_period_size > max_period_size)
+ oss_period_size = max_period_size;
+ }
oss_periods = oss_buffer_size / oss_period_size;
@@ -770,7 +750,7 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
oss_periods = substream->oss.setup.periods;
s = snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL);
- if (runtime->oss.maxfrags && s > runtime->oss.maxfrags)
+ if (s > 0 && runtime->oss.maxfrags && s > runtime->oss.maxfrags)
s = runtime->oss.maxfrags;
if (oss_periods > s)
oss_periods = s;
@@ -786,6 +766,11 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
if (oss_period_size < 16)
return -EINVAL;
+
+ /* don't allocate too large period; 1MB period must be enough */
+ if (oss_period_size > 1024 * 1024)
+ return -ENOMEM;
+
runtime->oss.period_bytes = oss_period_size;
runtime->oss.period_frames = 1;
runtime->oss.periods = oss_periods;
@@ -795,15 +780,15 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
static int choose_rate(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, unsigned int best_rate)
{
- struct snd_interval *it;
- struct snd_pcm_hw_params *save;
+ const struct snd_interval *it;
+ struct snd_pcm_hw_params *save __free(kfree) = NULL;
unsigned int rate, prev;
save = kmalloc(sizeof(*save), GFP_KERNEL);
if (save == NULL)
return -ENOMEM;
*save = *params;
- it = hw_param_interval(save, SNDRV_PCM_HW_PARAM_RATE);
+ it = hw_param_interval_c(save, SNDRV_PCM_HW_PARAM_RATE);
/* try multiples of the best rate */
rate = best_rate;
@@ -815,10 +800,8 @@ static int choose_rate(struct snd_pcm_substream *substream,
ret = snd_pcm_hw_param_set(substream, params,
SNDRV_PCM_HW_PARAM_RATE,
rate, 0);
- if (ret == (int)rate) {
- kfree(save);
+ if (ret == (int)rate)
return rate;
- }
*params = *save;
}
prev = rate;
@@ -828,11 +811,39 @@ static int choose_rate(struct snd_pcm_substream *substream,
}
/* not found, use the nearest rate */
- kfree(save);
return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL);
}
-static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
+/* parameter locking: returns immediately if tried during streaming */
+static int lock_params(struct snd_pcm_runtime *runtime)
+{
+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
+ return -ERESTARTSYS;
+ if (atomic_read(&runtime->oss.rw_ref)) {
+ mutex_unlock(&runtime->oss.params_lock);
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static void unlock_params(struct snd_pcm_runtime *runtime)
+{
+ mutex_unlock(&runtime->oss.params_lock);
+}
+
+static void snd_pcm_oss_release_buffers(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ kvfree(runtime->oss.buffer);
+ runtime->oss.buffer = NULL;
+#ifdef CONFIG_SND_PCM_OSS_PLUGINS
+ snd_pcm_oss_plugin_clear(substream);
+#endif
+}
+
+/* call with params_lock held */
+static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hw_params *params, *sparams;
@@ -841,17 +852,17 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
size_t oss_frame_size;
int err;
int direct;
- int format, sformat, n;
- struct snd_mask sformat_mask;
+ snd_pcm_format_t format, sformat;
+ int n;
+ const struct snd_mask *sformat_mask;
struct snd_mask mask;
- if (mutex_lock_interruptible(&runtime->oss.params_lock))
- return -EINTR;
- sw_params = kmalloc(sizeof(*sw_params), GFP_KERNEL);
+ if (!runtime->oss.params)
+ return 0;
+ sw_params = kzalloc(sizeof(*sw_params), GFP_KERNEL);
params = kmalloc(sizeof(*params), GFP_KERNEL);
sparams = kmalloc(sizeof(*sparams), GFP_KERNEL);
if (!sw_params || !params || !sparams) {
- snd_printd("No memory\n");
err = -ENOMEM;
goto failure;
}
@@ -866,42 +877,49 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
_snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0);
snd_mask_none(&mask);
if (atomic_read(&substream->mmap_count))
- snd_mask_set(&mask, SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
+ snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
else {
- snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_INTERLEAVED);
+ snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED);
if (!direct)
- snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
+ snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
}
err = snd_pcm_hw_param_mask(substream, sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask);
if (err < 0) {
- snd_printd("No usable accesses\n");
+ pcm_dbg(substream->pcm, "No usable accesses\n");
err = -EINVAL;
goto failure;
}
- choose_rate(substream, sparams, runtime->oss.rate);
- snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, NULL);
+
+ err = choose_rate(substream, sparams, runtime->oss.rate);
+ if (err < 0)
+ goto failure;
+ err = snd_pcm_hw_param_near(substream, sparams,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ runtime->oss.channels, NULL);
+ if (err < 0)
+ goto failure;
format = snd_pcm_oss_format_from(runtime->oss.format);
- sformat_mask = *hw_param_mask(sparams, SNDRV_PCM_HW_PARAM_FORMAT);
+ sformat_mask = hw_param_mask_c(sparams, SNDRV_PCM_HW_PARAM_FORMAT);
if (direct)
sformat = format;
else
- sformat = snd_pcm_plug_slave_format(format, &sformat_mask);
+ sformat = snd_pcm_plug_slave_format(format, sformat_mask);
- if (sformat < 0 || !snd_mask_test(&sformat_mask, sformat)) {
- for (sformat = 0; sformat <= SNDRV_PCM_FORMAT_LAST; sformat++) {
- if (snd_mask_test(&sformat_mask, sformat) &&
+ if ((__force int)sformat < 0 ||
+ !snd_mask_test_format(sformat_mask, sformat)) {
+ pcm_for_each_format(sformat) {
+ if (snd_mask_test_format(sformat_mask, sformat) &&
snd_pcm_oss_format_to(sformat) >= 0)
- break;
- }
- if (sformat > SNDRV_PCM_FORMAT_LAST) {
- snd_printd("Cannot find a format!!!\n");
- err = -EINVAL;
- goto failure;
+ goto format_found;
}
+ pcm_dbg(substream->pcm, "Cannot find a format!!!\n");
+ err = -EINVAL;
+ goto failure;
}
- err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0);
+ format_found:
+ err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, (__force int)sformat, 0);
if (err < 0)
goto failure;
@@ -910,9 +928,9 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
} else {
_snd_pcm_hw_params_any(params);
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
- SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0);
+ (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0);
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,
- snd_pcm_oss_format_from(runtime->oss.format), 0);
+ (__force int)snd_pcm_oss_format_from(runtime->oss.format), 0);
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
runtime->oss.channels, 0);
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
@@ -928,23 +946,44 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
oss_frame_size = snd_pcm_format_physical_width(params_format(params)) *
params_channels(params) / 8;
+ err = snd_pcm_oss_period_size(substream, params, sparams);
+ if (err < 0)
+ goto failure;
+
+ n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size);
+ err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, NULL);
+ if (err < 0)
+ goto failure;
+
+ err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIODS,
+ runtime->oss.periods, NULL);
+ if (err < 0)
+ goto failure;
+
+ snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+
+ err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, sparams);
+ if (err < 0) {
+ pcm_dbg(substream->pcm, "HW_PARAMS failed: %i\n", err);
+ goto failure;
+ }
+
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
snd_pcm_oss_plugin_clear(substream);
if (!direct) {
/* add necessary plugins */
- snd_pcm_oss_plugin_clear(substream);
- if ((err = snd_pcm_plug_format_plugins(substream,
- params,
- sparams)) < 0) {
- snd_printd("snd_pcm_plug_format_plugins failed: %i\n", err);
- snd_pcm_oss_plugin_clear(substream);
+ err = snd_pcm_plug_format_plugins(substream, params, sparams);
+ if (err < 0) {
+ pcm_dbg(substream->pcm,
+ "snd_pcm_plug_format_plugins failed: %i\n", err);
goto failure;
}
if (runtime->oss.plugin_first) {
struct snd_pcm_plugin *plugin;
- if ((err = snd_pcm_plugin_build_io(substream, sparams, &plugin)) < 0) {
- snd_printd("snd_pcm_plugin_build_io failed: %i\n", err);
- snd_pcm_oss_plugin_clear(substream);
+ err = snd_pcm_plugin_build_io(substream, sparams, &plugin);
+ if (err < 0) {
+ pcm_dbg(substream->pcm,
+ "snd_pcm_plugin_build_io failed: %i\n", err);
goto failure;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -952,36 +991,12 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
} else {
err = snd_pcm_plugin_insert(plugin);
}
- if (err < 0) {
- snd_pcm_oss_plugin_clear(substream);
+ if (err < 0)
goto failure;
- }
}
}
#endif
- err = snd_pcm_oss_period_size(substream, params, sparams);
- if (err < 0)
- goto failure;
-
- n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size);
- err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, NULL);
- if (err < 0)
- goto failure;
-
- err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIODS,
- runtime->oss.periods, NULL);
- if (err < 0)
- goto failure;
-
- snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
-
- if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, sparams)) < 0) {
- snd_printd("HW_PARAMS failed: %i\n", err);
- goto failure;
- }
-
- memset(sw_params, 0, sizeof(*sw_params));
if (runtime->oss.trigger) {
sw_params->start_threshold = 1;
} else {
@@ -1009,8 +1024,9 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
sw_params->silence_size = frames;
}
- if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params)) < 0) {
- snd_printd("SW_PARAMS failed: %i\n", err);
+ err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params);
+ if (err < 0) {
+ pcm_dbg(substream->pcm, "SW_PARAMS failed: %i\n", err);
goto failure;
}
@@ -1027,10 +1043,9 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
goto failure;
}
#endif
- oss_period_size *= oss_frame_size;
-
- oss_buffer_size = oss_period_size * runtime->oss.periods;
- if (oss_buffer_size < 0) {
+ oss_period_size = array_size(oss_period_size, oss_frame_size);
+ oss_buffer_size = array_size(oss_period_size, runtime->oss.periods);
+ if (oss_buffer_size <= 0) {
err = -EINVAL;
goto failure;
}
@@ -1049,8 +1064,8 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
runtime->oss.channels = params_channels(params);
runtime->oss.rate = params_rate(params);
- vfree(runtime->oss.buffer);
- runtime->oss.buffer = vmalloc(runtime->oss.period_bytes);
+ kvfree(runtime->oss.buffer);
+ runtime->oss.buffer = kvzalloc(runtime->oss.period_bytes, GFP_KERNEL);
if (!runtime->oss.buffer) {
err = -ENOMEM;
goto failure;
@@ -1059,16 +1074,34 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
runtime->oss.params = 0;
runtime->oss.prepare = 1;
runtime->oss.buffer_used = 0;
- if (runtime->dma_area)
- snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes));
+ snd_pcm_runtime_buffer_set_silence(runtime);
runtime->oss.period_frames = snd_pcm_alsa_frames(substream, oss_period_size);
err = 0;
failure:
+ if (err)
+ snd_pcm_oss_release_buffers(substream);
kfree(sw_params);
kfree(params);
kfree(sparams);
+ return err;
+}
+
+/* this one takes the lock by itself */
+static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
+ bool trylock)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ if (trylock) {
+ if (!(mutex_trylock(&runtime->oss.params_lock)))
+ return -EAGAIN;
+ } else if (mutex_lock_interruptible(&runtime->oss.params_lock))
+ return -ERESTARTSYS;
+
+ err = snd_pcm_oss_change_params_locked(substream);
mutex_unlock(&runtime->oss.params_lock);
return err;
}
@@ -1085,7 +1118,7 @@ static int snd_pcm_oss_get_active_substream(struct snd_pcm_oss_file *pcm_oss_fil
if (asubstream == NULL)
asubstream = substream;
if (substream->runtime->oss.params) {
- err = snd_pcm_oss_change_params(substream);
+ err = snd_pcm_oss_change_params(substream, false);
if (err < 0)
return err;
}
@@ -1097,6 +1130,10 @@ static int snd_pcm_oss_get_active_substream(struct snd_pcm_oss_file *pcm_oss_fil
return 0;
}
+/* call with params_lock held */
+/* NOTE: this always call PREPARE unconditionally no matter whether
+ * runtime->oss.prepare is set or not
+ */
static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream)
{
int err;
@@ -1104,7 +1141,8 @@ static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream)
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
if (err < 0) {
- snd_printd("snd_pcm_oss_prepare: SNDRV_PCM_IOCTL_PREPARE failed\n");
+ pcm_dbg(substream->pcm,
+ "snd_pcm_oss_prepare: SNDRV_PCM_IOCTL_PREPARE failed\n");
return err;
}
runtime->oss.prepare = 0;
@@ -1120,11 +1158,32 @@ static int snd_pcm_oss_make_ready(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime;
int err;
- if (substream == NULL)
- return 0;
runtime = substream->runtime;
if (runtime->oss.params) {
- err = snd_pcm_oss_change_params(substream);
+ err = snd_pcm_oss_change_params(substream, false);
+ if (err < 0)
+ return err;
+ }
+ if (runtime->oss.prepare) {
+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
+ return -ERESTARTSYS;
+ err = snd_pcm_oss_prepare(substream);
+ mutex_unlock(&runtime->oss.params_lock);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* call with params_lock held */
+static int snd_pcm_oss_make_ready_locked(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ int err;
+
+ runtime = substream->runtime;
+ if (runtime->oss.params) {
+ err = snd_pcm_oss_change_params_locked(substream);
if (err < 0)
return err;
}
@@ -1166,33 +1225,27 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const
struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
while (1) {
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
- runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+ if (runtime->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
- printk(KERN_DEBUG "pcm_oss: write: "
- "recovering from XRUN\n");
- else
- printk(KERN_DEBUG "pcm_oss: write: "
- "recovering from SUSPEND\n");
+ pcm_dbg(substream->pcm,
+ "pcm_oss: write: recovering from %s\n",
+ runtime->state == SNDRV_PCM_STATE_XRUN ?
+ "XRUN" : "SUSPEND");
#endif
ret = snd_pcm_oss_prepare(substream);
if (ret < 0)
break;
}
- if (in_kernel) {
- mm_segment_t fs;
- fs = snd_enter_user();
- ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames);
- snd_leave_user(fs);
- } else {
- ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames);
- }
+ mutex_unlock(&runtime->oss.params_lock);
+ ret = __snd_pcm_lib_xfer(substream, (void *)ptr, true,
+ frames, in_kernel);
+ mutex_lock(&runtime->oss.params_lock);
if (ret != -EPIPE && ret != -ESTRPIPE)
break;
/* test, if we can't store new data, because the stream */
/* has not been started */
- if (runtime->status->state == SNDRV_PCM_STATE_PREPARED)
+ if (runtime->state == SNDRV_PCM_STATE_PREPARED)
return -EAGAIN;
}
return ret;
@@ -1204,20 +1257,18 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p
snd_pcm_sframes_t delay;
int ret;
while (1) {
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
- runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+ if (runtime->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
- printk(KERN_DEBUG "pcm_oss: read: "
- "recovering from XRUN\n");
- else
- printk(KERN_DEBUG "pcm_oss: read: "
- "recovering from SUSPEND\n");
+ pcm_dbg(substream->pcm,
+ "pcm_oss: read: recovering from %s\n",
+ runtime->state == SNDRV_PCM_STATE_XRUN ?
+ "XRUN" : "SUSPEND");
#endif
ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
if (ret < 0)
break;
- } else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) {
+ } else if (runtime->state == SNDRV_PCM_STATE_SETUP) {
ret = snd_pcm_oss_prepare(substream);
if (ret < 0)
break;
@@ -1225,16 +1276,12 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p
ret = snd_pcm_oss_capture_position_fixup(substream, &delay);
if (ret < 0)
break;
- if (in_kernel) {
- mm_segment_t fs;
- fs = snd_enter_user();
- ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames);
- snd_leave_user(fs);
- } else {
- ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames);
- }
+ mutex_unlock(&runtime->oss.params_lock);
+ ret = __snd_pcm_lib_xfer(substream, (void *)ptr, true,
+ frames, in_kernel);
+ mutex_lock(&runtime->oss.params_lock);
if (ret == -EPIPE) {
- if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
+ if (runtime->state == SNDRV_PCM_STATE_DRAINING) {
ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
if (ret < 0)
break;
@@ -1247,80 +1294,64 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p
return ret;
}
-snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel)
+#ifdef CONFIG_SND_PCM_OSS_PLUGINS
+snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
while (1) {
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
- runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+ if (runtime->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
- printk(KERN_DEBUG "pcm_oss: writev: "
- "recovering from XRUN\n");
- else
- printk(KERN_DEBUG "pcm_oss: writev: "
- "recovering from SUSPEND\n");
+ pcm_dbg(substream->pcm,
+ "pcm_oss: writev: recovering from %s\n",
+ runtime->state == SNDRV_PCM_STATE_XRUN ?
+ "XRUN" : "SUSPEND");
#endif
ret = snd_pcm_oss_prepare(substream);
if (ret < 0)
break;
}
- if (in_kernel) {
- mm_segment_t fs;
- fs = snd_enter_user();
- ret = snd_pcm_lib_writev(substream, (void __user **)bufs, frames);
- snd_leave_user(fs);
- } else {
- ret = snd_pcm_lib_writev(substream, (void __user **)bufs, frames);
- }
+ ret = snd_pcm_kernel_writev(substream, bufs, frames);
if (ret != -EPIPE && ret != -ESTRPIPE)
break;
/* test, if we can't store new data, because the stream */
/* has not been started */
- if (runtime->status->state == SNDRV_PCM_STATE_PREPARED)
+ if (runtime->state == SNDRV_PCM_STATE_PREPARED)
return -EAGAIN;
}
return ret;
}
-snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel)
+snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int ret;
while (1) {
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
- runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+ if (runtime->state == SNDRV_PCM_STATE_XRUN ||
+ runtime->state == SNDRV_PCM_STATE_SUSPENDED) {
#ifdef OSS_DEBUG
- if (runtime->status->state == SNDRV_PCM_STATE_XRUN)
- printk(KERN_DEBUG "pcm_oss: readv: "
- "recovering from XRUN\n");
- else
- printk(KERN_DEBUG "pcm_oss: readv: "
- "recovering from SUSPEND\n");
+ pcm_dbg(substream->pcm,
+ "pcm_oss: readv: recovering from %s\n",
+ runtime->state == SNDRV_PCM_STATE_XRUN ?
+ "XRUN" : "SUSPEND");
#endif
ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
if (ret < 0)
break;
- } else if (runtime->status->state == SNDRV_PCM_STATE_SETUP) {
+ } else if (runtime->state == SNDRV_PCM_STATE_SETUP) {
ret = snd_pcm_oss_prepare(substream);
if (ret < 0)
break;
}
- if (in_kernel) {
- mm_segment_t fs;
- fs = snd_enter_user();
- ret = snd_pcm_lib_readv(substream, (void __user **)bufs, frames);
- snd_leave_user(fs);
- } else {
- ret = snd_pcm_lib_readv(substream, (void __user **)bufs, frames);
- }
+ ret = snd_pcm_kernel_readv(substream, bufs, frames);
if (ret != -EPIPE && ret != -ESTRPIPE)
break;
}
return ret;
}
+#endif /* CONFIG_SND_PCM_OSS_PLUGINS */
static ssize_t snd_pcm_oss_write2(struct snd_pcm_substream *substream, const char *buf, size_t bytes, int in_kernel)
{
@@ -1331,7 +1362,7 @@ static ssize_t snd_pcm_oss_write2(struct snd_pcm_substream *substream, const cha
struct snd_pcm_plugin_channel *channels;
size_t oss_frame_bytes = (runtime->oss.plugin_first->src_width * runtime->oss.plugin_first->src_format.channels) / 8;
if (!in_kernel) {
- if (copy_from_user(runtime->oss.buffer, (const char __user *)buf, bytes))
+ if (copy_from_user(runtime->oss.buffer, (const char __force __user *)buf, bytes))
return -EFAULT;
buf = runtime->oss.buffer;
}
@@ -1358,16 +1389,21 @@ static ssize_t snd_pcm_oss_write2(struct snd_pcm_substream *substream, const cha
static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const char __user *buf, size_t bytes)
{
size_t xfer = 0;
- ssize_t tmp;
+ ssize_t tmp = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
if (atomic_read(&substream->mmap_count))
return -ENXIO;
- if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
- return tmp;
- mutex_lock(&runtime->oss.params_lock);
+ atomic_inc(&runtime->oss.rw_ref);
while (bytes > 0) {
+ if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
+ tmp = -ERESTARTSYS;
+ break;
+ }
+ tmp = snd_pcm_oss_make_ready_locked(substream);
+ if (tmp < 0)
+ goto err;
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
tmp = bytes;
if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes)
@@ -1411,14 +1447,19 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha
xfer += tmp;
if ((substream->f_flags & O_NONBLOCK) != 0 &&
tmp != runtime->oss.period_bytes)
- break;
+ tmp = -EAGAIN;
}
- }
- mutex_unlock(&runtime->oss.params_lock);
- return xfer;
-
err:
- mutex_unlock(&runtime->oss.params_lock);
+ mutex_unlock(&runtime->oss.params_lock);
+ if (tmp < 0)
+ break;
+ if (signal_pending(current)) {
+ tmp = -ERESTARTSYS;
+ break;
+ }
+ tmp = 0;
+ }
+ atomic_dec(&runtime->oss.rw_ref);
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
}
@@ -1427,7 +1468,7 @@ static ssize_t snd_pcm_oss_read2(struct snd_pcm_substream *substream, char *buf,
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_sframes_t frames, frames1;
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
- char __user *final_dst = (char __user *)buf;
+ char __user *final_dst = (char __force __user *)buf;
if (runtime->oss.plugin_first) {
struct snd_pcm_plugin_channel *channels;
size_t oss_frame_bytes = (runtime->oss.plugin_last->dst_width * runtime->oss.plugin_last->dst_format.channels) / 8;
@@ -1458,16 +1499,21 @@ static ssize_t snd_pcm_oss_read2(struct snd_pcm_substream *substream, char *buf,
static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __user *buf, size_t bytes)
{
size_t xfer = 0;
- ssize_t tmp;
+ ssize_t tmp = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
if (atomic_read(&substream->mmap_count))
return -ENXIO;
- if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
- return tmp;
- mutex_lock(&runtime->oss.params_lock);
+ atomic_inc(&runtime->oss.rw_ref);
while (bytes > 0) {
+ if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
+ tmp = -ERESTARTSYS;
+ break;
+ }
+ tmp = snd_pcm_oss_make_ready_locked(substream);
+ if (tmp < 0)
+ goto err;
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
if (runtime->oss.buffer_used == 0) {
tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
@@ -1498,28 +1544,38 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use
bytes -= tmp;
xfer += tmp;
}
- }
- mutex_unlock(&runtime->oss.params_lock);
- return xfer;
-
err:
- mutex_unlock(&runtime->oss.params_lock);
+ mutex_unlock(&runtime->oss.params_lock);
+ if (tmp < 0)
+ break;
+ if (signal_pending(current)) {
+ tmp = -ERESTARTSYS;
+ break;
+ }
+ tmp = 0;
+ }
+ atomic_dec(&runtime->oss.rw_ref);
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
}
static int snd_pcm_oss_reset(struct snd_pcm_oss_file *pcm_oss_file)
{
struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ int i;
- substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
- if (substream != NULL) {
- snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
- substream->runtime->oss.prepare = 1;
- }
- substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
- if (substream != NULL) {
+ for (i = 0; i < 2; i++) {
+ substream = pcm_oss_file->streams[i];
+ if (!substream)
+ continue;
+ runtime = substream->runtime;
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
- substream->runtime->oss.prepare = 1;
+ mutex_lock(&runtime->oss.params_lock);
+ runtime->oss.prepare = 1;
+ runtime->oss.buffer_used = 0;
+ runtime->oss.prev_hw_ptr_period = 0;
+ runtime->oss.period_ptr = 0;
+ mutex_unlock(&runtime->oss.params_lock);
}
return 0;
}
@@ -1531,7 +1587,8 @@ static int snd_pcm_oss_post(struct snd_pcm_oss_file *pcm_oss_file)
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
if (substream != NULL) {
- if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+ err = snd_pcm_oss_make_ready(substream);
+ if (err < 0)
return err;
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_START, NULL);
}
@@ -1544,14 +1601,15 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size)
{
struct snd_pcm_runtime *runtime;
ssize_t result = 0;
+ snd_pcm_state_t state;
long res;
- wait_queue_t wait;
+ wait_queue_entry_t wait;
runtime = substream->runtime;
init_waitqueue_entry(&wait, current);
add_wait_queue(&runtime->sleep, &wait);
#ifdef OSS_DEBUG
- printk(KERN_DEBUG "sync1: size = %li\n", size);
+ pcm_dbg(substream->pcm, "sync1: size = %li\n", size);
#endif
while (1) {
result = snd_pcm_oss_write2(substream, runtime->oss.buffer, size, 1);
@@ -1564,10 +1622,9 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size)
break;
result = 0;
set_current_state(TASK_INTERRUPTIBLE);
- snd_pcm_stream_lock_irq(substream);
- res = runtime->status->state;
- snd_pcm_stream_unlock_irq(substream);
- if (res != SNDRV_PCM_STATE_RUNNING) {
+ scoped_guard(pcm_stream_lock_irq, substream)
+ state = runtime->state;
+ if (state != SNDRV_PCM_STATE_RUNNING) {
set_current_state(TASK_RUNNING);
break;
}
@@ -1577,7 +1634,8 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size)
break;
}
if (res == 0) {
- snd_printk(KERN_ERR "OSS sync error - DMA timeout\n");
+ pcm_err(substream->pcm,
+ "OSS sync error - DMA timeout\n");
result = -EIO;
break;
}
@@ -1601,37 +1659,38 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
runtime = substream->runtime;
if (atomic_read(&substream->mmap_count))
goto __direct;
- if ((err = snd_pcm_oss_make_ready(substream)) < 0)
- return err;
+ atomic_inc(&runtime->oss.rw_ref);
+ if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
+ atomic_dec(&runtime->oss.rw_ref);
+ return -ERESTARTSYS;
+ }
+ err = snd_pcm_oss_make_ready_locked(substream);
+ if (err < 0)
+ goto unlock;
format = snd_pcm_oss_format_from(runtime->oss.format);
width = snd_pcm_format_physical_width(format);
- mutex_lock(&runtime->oss.params_lock);
if (runtime->oss.buffer_used > 0) {
#ifdef OSS_DEBUG
- printk(KERN_DEBUG "sync: buffer_used\n");
+ pcm_dbg(substream->pcm, "sync: buffer_used\n");
#endif
size = (8 * (runtime->oss.period_bytes - runtime->oss.buffer_used) + 7) / width;
snd_pcm_format_set_silence(format,
runtime->oss.buffer + runtime->oss.buffer_used,
size);
err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes);
- if (err < 0) {
- mutex_unlock(&runtime->oss.params_lock);
- return err;
- }
+ if (err < 0)
+ goto unlock;
} else if (runtime->oss.period_ptr > 0) {
#ifdef OSS_DEBUG
- printk(KERN_DEBUG "sync: period_ptr\n");
+ pcm_dbg(substream->pcm, "sync: period_ptr\n");
#endif
size = runtime->oss.period_bytes - runtime->oss.period_ptr;
snd_pcm_format_set_silence(format,
runtime->oss.buffer,
size * 8 / width);
err = snd_pcm_oss_sync1(substream, size);
- if (err < 0) {
- mutex_unlock(&runtime->oss.params_lock);
- return err;
- }
+ if (err < 0)
+ goto unlock;
}
/*
* The ALSA's period might be a bit large than OSS one.
@@ -1640,29 +1699,16 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
size = runtime->control->appl_ptr % runtime->period_size;
if (size > 0) {
size = runtime->period_size - size;
- if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
- size = (runtime->frame_bits * size) / 8;
- while (size > 0) {
- mm_segment_t fs;
- size_t size1 = size < runtime->oss.period_bytes ? size : runtime->oss.period_bytes;
- size -= size1;
- size1 *= 8;
- size1 /= runtime->sample_bits;
- snd_pcm_format_set_silence(runtime->format,
- runtime->oss.buffer,
- size1);
- size1 /= runtime->channels; /* frames */
- fs = snd_enter_user();
- snd_pcm_lib_write(substream, (void __user *)runtime->oss.buffer, size1);
- snd_leave_user(fs);
- }
- } else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {
- void __user *buffers[runtime->channels];
- memset(buffers, 0, runtime->channels * sizeof(void *));
- snd_pcm_lib_writev(substream, buffers, size);
- }
+ if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED)
+ snd_pcm_lib_write(substream, NULL, size);
+ else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
+ snd_pcm_lib_writev(substream, NULL, size);
}
+unlock:
mutex_unlock(&runtime->oss.params_lock);
+ atomic_dec(&runtime->oss.rw_ref);
+ if (err < 0)
+ return err;
/*
* finish sync: drain the buffer
*/
@@ -1673,19 +1719,24 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
substream->f_flags = saved_f_flags;
if (err < 0)
return err;
+ mutex_lock(&runtime->oss.params_lock);
runtime->oss.prepare = 1;
+ mutex_unlock(&runtime->oss.params_lock);
}
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
if (substream != NULL) {
- if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+ err = snd_pcm_oss_make_ready(substream);
+ if (err < 0)
return err;
runtime = substream->runtime;
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
if (err < 0)
return err;
+ mutex_lock(&runtime->oss.params_lock);
runtime->oss.buffer_used = 0;
runtime->oss.prepare = 1;
+ mutex_unlock(&runtime->oss.params_lock);
}
return 0;
}
@@ -1697,6 +1748,8 @@ static int snd_pcm_oss_set_rate(struct snd_pcm_oss_file *pcm_oss_file, int rate)
for (idx = 1; idx >= 0; --idx) {
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
struct snd_pcm_runtime *runtime;
+ int err;
+
if (substream == NULL)
continue;
runtime = substream->runtime;
@@ -1704,10 +1757,14 @@ static int snd_pcm_oss_set_rate(struct snd_pcm_oss_file *pcm_oss_file, int rate)
rate = 1000;
else if (rate > 192000)
rate = 192000;
+ err = lock_params(runtime);
+ if (err < 0)
+ return err;
if (runtime->oss.rate != rate) {
runtime->oss.params = 1;
runtime->oss.rate = rate;
}
+ unlock_params(runtime);
}
return snd_pcm_oss_get_rate(pcm_oss_file);
}
@@ -1717,7 +1774,8 @@ static int snd_pcm_oss_get_rate(struct snd_pcm_oss_file *pcm_oss_file)
struct snd_pcm_substream *substream;
int err;
- if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
+ if (err < 0)
return err;
return substream->runtime->oss.rate;
}
@@ -1732,13 +1790,19 @@ static int snd_pcm_oss_set_channels(struct snd_pcm_oss_file *pcm_oss_file, unsig
for (idx = 1; idx >= 0; --idx) {
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
struct snd_pcm_runtime *runtime;
+ int err;
+
if (substream == NULL)
continue;
runtime = substream->runtime;
+ err = lock_params(runtime);
+ if (err < 0)
+ return err;
if (runtime->oss.channels != channels) {
runtime->oss.params = 1;
runtime->oss.channels = channels;
}
+ unlock_params(runtime);
}
return snd_pcm_oss_get_channels(pcm_oss_file);
}
@@ -1748,7 +1812,8 @@ static int snd_pcm_oss_get_channels(struct snd_pcm_oss_file *pcm_oss_file)
struct snd_pcm_substream *substream;
int err;
- if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
+ if (err < 0)
return err;
return substream->runtime->oss.channels;
}
@@ -1758,7 +1823,8 @@ static int snd_pcm_oss_get_block_size(struct snd_pcm_oss_file *pcm_oss_file)
struct snd_pcm_substream *substream;
int err;
- if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
+ if (err < 0)
return err;
return substream->runtime->oss.period_bytes;
}
@@ -1768,12 +1834,13 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)
struct snd_pcm_substream *substream;
int err;
int direct;
- struct snd_pcm_hw_params *params;
+ struct snd_pcm_hw_params *params __free(kfree) = NULL;
unsigned int formats = 0;
- struct snd_mask format_mask;
+ const struct snd_mask *format_mask;
int fmt;
- if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
+ if (err < 0)
return err;
if (atomic_read(&substream->mmap_count))
direct = 1;
@@ -1792,23 +1859,24 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)
return -ENOMEM;
_snd_pcm_hw_params_any(params);
err = snd_pcm_hw_refine(substream, params);
- format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
- kfree(params);
if (err < 0)
return err;
+ format_mask = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT);
for (fmt = 0; fmt < 32; ++fmt) {
- if (snd_mask_test(&format_mask, fmt)) {
- int f = snd_pcm_oss_format_to(fmt);
+ if (snd_mask_test(format_mask, fmt)) {
+ int f = snd_pcm_oss_format_to((__force snd_pcm_format_t)fmt);
if (f >= 0)
formats |= f;
}
}
+
return formats;
}
static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int format)
{
int formats, idx;
+ int err;
if (format != AFMT_QUERY) {
formats = snd_pcm_oss_get_formats(pcm_oss_file);
@@ -1822,10 +1890,14 @@ static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int for
if (substream == NULL)
continue;
runtime = substream->runtime;
+ err = lock_params(runtime);
+ if (err < 0)
+ return err;
if (runtime->oss.format != format) {
runtime->oss.params = 1;
runtime->oss.format = format;
}
+ unlock_params(runtime);
}
}
return snd_pcm_oss_get_format(pcm_oss_file);
@@ -1836,7 +1908,8 @@ static int snd_pcm_oss_get_format(struct snd_pcm_oss_file *pcm_oss_file)
struct snd_pcm_substream *substream;
int err;
- if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
+ err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
+ if (err < 0)
return err;
return substream->runtime->oss.format;
}
@@ -1845,8 +1918,6 @@ static int snd_pcm_oss_set_subdivide1(struct snd_pcm_substream *substream, int s
{
struct snd_pcm_runtime *runtime;
- if (substream == NULL)
- return 0;
runtime = substream->runtime;
if (subdivide == 0) {
subdivide = runtime->oss.subdivision;
@@ -1870,9 +1941,17 @@ static int snd_pcm_oss_set_subdivide(struct snd_pcm_oss_file *pcm_oss_file, int
for (idx = 1; idx >= 0; --idx) {
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
+ struct snd_pcm_runtime *runtime;
+
if (substream == NULL)
continue;
- if ((err = snd_pcm_oss_set_subdivide1(substream, subdivide)) < 0)
+ runtime = substream->runtime;
+ err = lock_params(runtime);
+ if (err < 0)
+ return err;
+ err = snd_pcm_oss_set_subdivide1(substream, subdivide);
+ unlock_params(runtime);
+ if (err < 0)
return err;
}
return err;
@@ -1881,13 +1960,15 @@ static int snd_pcm_oss_set_subdivide(struct snd_pcm_oss_file *pcm_oss_file, int
static int snd_pcm_oss_set_fragment1(struct snd_pcm_substream *substream, unsigned int val)
{
struct snd_pcm_runtime *runtime;
+ int fragshift;
- if (substream == NULL)
- return 0;
runtime = substream->runtime;
if (runtime->oss.subdivision || runtime->oss.fragshift)
return -EINVAL;
- runtime->oss.fragshift = val & 0xffff;
+ fragshift = val & 0xffff;
+ if (fragshift >= 25) /* should be large enough */
+ return -EINVAL;
+ runtime->oss.fragshift = fragshift;
runtime->oss.maxfrags = (val >> 16) & 0xffff;
if (runtime->oss.fragshift < 4) /* < 16 */
runtime->oss.fragshift = 4;
@@ -1903,9 +1984,17 @@ static int snd_pcm_oss_set_fragment(struct snd_pcm_oss_file *pcm_oss_file, unsig
for (idx = 1; idx >= 0; --idx) {
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
+ struct snd_pcm_runtime *runtime;
+
if (substream == NULL)
continue;
- if ((err = snd_pcm_oss_set_fragment1(substream, val)) < 0)
+ runtime = substream->runtime;
+ err = lock_params(runtime);
+ if (err < 0)
+ return err;
+ err = snd_pcm_oss_set_fragment1(substream, val);
+ unlock_params(runtime);
+ if (err < 0)
return err;
}
return err;
@@ -1913,9 +2002,8 @@ static int snd_pcm_oss_set_fragment(struct snd_pcm_oss_file *pcm_oss_file, unsig
static int snd_pcm_oss_nonblock(struct file * file)
{
- spin_lock(&file->f_lock);
+ guard(spinlock)(&file->f_lock);
file->f_flags |= O_NONBLOCK;
- spin_unlock(&file->f_lock);
return 0;
}
@@ -1973,22 +2061,27 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
int err, cmd;
#ifdef OSS_DEBUG
- printk(KERN_DEBUG "pcm_oss: trigger = 0x%x\n", trigger);
+ pr_debug("pcm_oss: trigger = 0x%x\n", trigger);
#endif
psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
if (psubstream) {
- if ((err = snd_pcm_oss_make_ready(psubstream)) < 0)
+ err = snd_pcm_oss_make_ready(psubstream);
+ if (err < 0)
return err;
}
if (csubstream) {
- if ((err = snd_pcm_oss_make_ready(csubstream)) < 0)
+ err = snd_pcm_oss_make_ready(csubstream);
+ if (err < 0)
return err;
}
if (psubstream) {
runtime = psubstream->runtime;
+ cmd = 0;
+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
+ return -ERESTARTSYS;
if (trigger & PCM_ENABLE_OUTPUT) {
if (runtime->oss.trigger)
goto _skip1;
@@ -2006,13 +2099,19 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
cmd = SNDRV_PCM_IOCTL_DROP;
runtime->oss.prepare = 1;
}
- err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL);
- if (err < 0)
- return err;
- }
_skip1:
+ mutex_unlock(&runtime->oss.params_lock);
+ if (cmd) {
+ err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL);
+ if (err < 0)
+ return err;
+ }
+ }
if (csubstream) {
runtime = csubstream->runtime;
+ cmd = 0;
+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
+ return -ERESTARTSYS;
if (trigger & PCM_ENABLE_INPUT) {
if (runtime->oss.trigger)
goto _skip2;
@@ -2027,11 +2126,14 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
cmd = SNDRV_PCM_IOCTL_DROP;
runtime->oss.prepare = 1;
}
- err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL);
- if (err < 0)
- return err;
- }
_skip2:
+ mutex_unlock(&runtime->oss.params_lock);
+ if (cmd) {
+ err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL);
+ if (err < 0)
+ return err;
+ }
+ }
return 0;
}
@@ -2059,7 +2161,8 @@ static int snd_pcm_oss_get_odelay(struct snd_pcm_oss_file *pcm_oss_file)
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
if (substream == NULL)
return -EINVAL;
- if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+ err = snd_pcm_oss_make_ready(substream);
+ if (err < 0)
return err;
runtime = substream->runtime;
if (runtime->oss.params || runtime->oss.prepare)
@@ -2086,7 +2189,8 @@ static int snd_pcm_oss_get_ptr(struct snd_pcm_oss_file *pcm_oss_file, int stream
substream = pcm_oss_file->streams[stream];
if (substream == NULL)
return -EINVAL;
- if ((err = snd_pcm_oss_make_ready(substream)) < 0)
+ err = snd_pcm_oss_make_ready(substream);
+ if (err < 0)
return err;
runtime = substream->runtime;
if (runtime->oss.params || runtime->oss.prepare) {
@@ -2157,9 +2261,11 @@ static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stre
return -EINVAL;
runtime = substream->runtime;
- if (runtime->oss.params &&
- (err = snd_pcm_oss_change_params(substream)) < 0)
- return err;
+ if (runtime->oss.params) {
+ err = snd_pcm_oss_change_params(substream, false);
+ if (err < 0)
+ return err;
+ }
info.fragsize = runtime->oss.period_bytes;
info.fragstotal = runtime->periods;
@@ -2193,9 +2299,9 @@ static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stre
}
#ifdef OSS_DEBUG
- printk(KERN_DEBUG "pcm_oss: space: bytes = %i, fragments = %i, "
- "fragstotal = %i, fragsize = %i\n",
- info.bytes, info.fragments, info.fragstotal, info.fragsize);
+ pcm_dbg(substream->pcm,
+ "pcm_oss: space: bytes = %i, fragments = %i, fragstotal = %i, fragsize = %i\n",
+ info.bytes, info.fragments, info.fragstotal, info.fragsize);
#endif
if (copy_to_user(_info, &info, sizeof(info)))
return -EFAULT;
@@ -2205,7 +2311,7 @@ static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stre
static int snd_pcm_oss_get_mapbuf(struct snd_pcm_oss_file *pcm_oss_file, int stream, struct buffmem_desc __user * _info)
{
// it won't be probably implemented
- // snd_printd("TODO: snd_pcm_oss_get_mapbuf\n");
+ // pr_debug("TODO: snd_pcm_oss_get_mapbuf\n");
return -EINVAL;
}
@@ -2225,7 +2331,7 @@ static void snd_pcm_oss_look_for_setup(struct snd_pcm *pcm, int stream,
{
struct snd_pcm_oss_setup *setup;
- mutex_lock(&pcm->streams[stream].oss.setup_mutex);
+ guard(mutex)(&pcm->streams[stream].oss.setup_mutex);
do {
for (setup = pcm->streams[stream].oss.setup_list; setup;
setup = setup->next) {
@@ -2236,18 +2342,11 @@ static void snd_pcm_oss_look_for_setup(struct snd_pcm *pcm, int stream,
out:
if (setup)
*rsetup = *setup;
- mutex_unlock(&pcm->streams[stream].oss.setup_mutex);
}
static void snd_pcm_oss_release_substream(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime;
- runtime = substream->runtime;
- vfree(runtime->oss.buffer);
- runtime->oss.buffer = NULL;
-#ifdef CONFIG_SND_PCM_OSS_PLUGINS
- snd_pcm_oss_plugin_clear(substream);
-#endif
+ snd_pcm_oss_release_buffers(substream);
substream->oss.oss = 0;
}
@@ -2283,6 +2382,7 @@ static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream,
runtime->oss.maxfrags = 0;
runtime->oss.subdivision = 0;
substream->pcm_release = snd_pcm_oss_release_substream;
+ atomic_set(&runtime->oss.rw_ref, 0);
}
static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file)
@@ -2341,7 +2441,6 @@ static int snd_pcm_oss_open_file(struct file *file,
}
pcm_oss_file->streams[idx] = substream;
- substream->file = pcm_oss_file;
snd_pcm_oss_init_substream(substream, &setup[idx], minor);
}
@@ -2377,7 +2476,7 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
struct snd_pcm_oss_file *pcm_oss_file;
struct snd_pcm_oss_setup setup[2];
int nonblock;
- wait_queue_t wait;
+ wait_queue_entry_t wait;
err = nonseekable_open(inode, file);
if (err < 0)
@@ -2431,6 +2530,10 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
mutex_unlock(&pcm->open_mutex);
schedule();
mutex_lock(&pcm->open_mutex);
+ if (pcm->card->shutdown) {
+ err = -ENODEV;
+ break;
+ }
if (signal_pending(current)) {
err = -ERESTARTSYS;
break;
@@ -2440,6 +2543,7 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
mutex_unlock(&pcm->open_mutex);
if (err < 0)
goto __error;
+ snd_card_unref(pcm->card);
return err;
__error:
@@ -2447,6 +2551,8 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
__error2:
snd_card_file_remove(pcm->card, file);
__error1:
+ if (pcm)
+ snd_card_unref(pcm->card);
return err;
}
@@ -2485,7 +2591,7 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long
return put_user(SNDRV_OSS_VERSION, p);
if (cmd == OSS_ALSAEMULVER)
return put_user(1, p);
-#if defined(CONFIG_SND_MIXER_OSS) || (defined(MODULE) && defined(CONFIG_SND_MIXER_OSS_MODULE))
+#if IS_REACHABLE(CONFIG_SND_MIXER_OSS)
if (((cmd >> 8) & 0xff) == 'M') { /* mixer ioctl - for OSS compatibility */
struct snd_pcm_substream *substream;
int idx;
@@ -2502,7 +2608,7 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long
if (((cmd >> 8) & 0xff) != 'P')
return -EINVAL;
#ifdef OSS_DEBUG
- printk(KERN_DEBUG "pcm_oss: ioctl = 0x%x\n", cmd);
+ pr_debug("pcm_oss: ioctl = 0x%x\n", cmd);
#endif
switch (cmd) {
case SNDCTL_DSP_RESET:
@@ -2512,7 +2618,8 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long
case SNDCTL_DSP_SPEED:
if (get_user(res, p))
return -EFAULT;
- if ((res = snd_pcm_oss_set_rate(pcm_oss_file, res))<0)
+ res = snd_pcm_oss_set_rate(pcm_oss_file, res);
+ if (res < 0)
return res;
return put_user(res, p);
case SOUND_PCM_READ_RATE:
@@ -2524,7 +2631,8 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long
if (get_user(res, p))
return -EFAULT;
res = res > 0 ? 2 : 1;
- if ((res = snd_pcm_oss_set_channels(pcm_oss_file, res)) < 0)
+ res = snd_pcm_oss_set_channels(pcm_oss_file, res);
+ if (res < 0)
return res;
return put_user(--res, p);
case SNDCTL_DSP_GETBLKSIZE:
@@ -2629,14 +2737,22 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long
case SNDCTL_DSP_PROFILE:
return 0; /* silently ignore */
default:
- snd_printd("pcm_oss: unknown command = 0x%x\n", cmd);
+ pr_debug("pcm_oss: unknown command = 0x%x\n", cmd);
}
return -EINVAL;
}
#ifdef CONFIG_COMPAT
/* all compatible */
-#define snd_pcm_oss_ioctl_compat snd_pcm_oss_ioctl
+static long snd_pcm_oss_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ /*
+ * Everything is compatbile except SNDCTL_DSP_MAPINBUF/SNDCTL_DSP_MAPOUTBUF,
+ * which are not implemented for the native case either
+ */
+ return snd_pcm_oss_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
#else
#define snd_pcm_oss_ioctl_compat NULL
#endif
@@ -2656,8 +2772,9 @@ static ssize_t snd_pcm_oss_read(struct file *file, char __user *buf, size_t coun
#else
{
ssize_t res = snd_pcm_oss_read1(substream, buf, count);
- printk(KERN_DEBUG "pcm_oss: read %li bytes "
- "(returned %li bytes)\n", (long)count, (long)res);
+ pcm_dbg(substream->pcm,
+ "pcm_oss: read %li bytes (returned %li bytes)\n",
+ (long)count, (long)res);
return res;
}
#endif
@@ -2676,7 +2793,7 @@ static ssize_t snd_pcm_oss_write(struct file *file, const char __user *buf, size
substream->f_flags = file->f_flags & O_NONBLOCK;
result = snd_pcm_oss_write1(substream, buf, count);
#ifdef OSS_DEBUG
- printk(KERN_DEBUG "pcm_oss: write %li bytes (wrote %li bytes)\n",
+ pcm_dbg(substream->pcm, "pcm_oss: write %li bytes (wrote %li bytes)\n",
(long)count, (long)result);
#endif
return result;
@@ -2704,10 +2821,10 @@ static int snd_pcm_oss_capture_ready(struct snd_pcm_substream *substream)
runtime->oss.period_frames;
}
-static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait)
+static __poll_t snd_pcm_oss_poll(struct file *file, poll_table * wait)
{
struct snd_pcm_oss_file *pcm_oss_file;
- unsigned int mask;
+ __poll_t mask;
struct snd_pcm_substream *psubstream = NULL, *csubstream = NULL;
pcm_oss_file = file->private_data;
@@ -2719,22 +2836,23 @@ static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait)
if (psubstream != NULL) {
struct snd_pcm_runtime *runtime = psubstream->runtime;
poll_wait(file, &runtime->sleep, wait);
- snd_pcm_stream_lock_irq(psubstream);
- if (runtime->status->state != SNDRV_PCM_STATE_DRAINING &&
- (runtime->status->state != SNDRV_PCM_STATE_RUNNING ||
- snd_pcm_oss_playback_ready(psubstream)))
- mask |= POLLOUT | POLLWRNORM;
- snd_pcm_stream_unlock_irq(psubstream);
+ scoped_guard(pcm_stream_lock_irq, psubstream) {
+ if (runtime->state != SNDRV_PCM_STATE_DRAINING &&
+ (runtime->state != SNDRV_PCM_STATE_RUNNING ||
+ snd_pcm_oss_playback_ready(psubstream)))
+ mask |= EPOLLOUT | EPOLLWRNORM;
+ }
}
if (csubstream != NULL) {
struct snd_pcm_runtime *runtime = csubstream->runtime;
snd_pcm_state_t ostate;
poll_wait(file, &runtime->sleep, wait);
- snd_pcm_stream_lock_irq(csubstream);
- if ((ostate = runtime->status->state) != SNDRV_PCM_STATE_RUNNING ||
- snd_pcm_oss_capture_ready(csubstream))
- mask |= POLLIN | POLLRDNORM;
- snd_pcm_stream_unlock_irq(csubstream);
+ scoped_guard(pcm_stream_lock_irq, csubstream) {
+ ostate = runtime->state;
+ if (ostate != SNDRV_PCM_STATE_RUNNING ||
+ snd_pcm_oss_capture_ready(csubstream))
+ mask |= EPOLLIN | EPOLLRDNORM;
+ }
if (ostate != SNDRV_PCM_STATE_RUNNING && runtime->oss.trigger) {
struct snd_pcm_oss_file ofile;
memset(&ofile, 0, sizeof(ofile));
@@ -2755,7 +2873,7 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
int err;
#ifdef OSS_DEBUG
- printk(KERN_DEBUG "pcm_oss: mmap begin\n");
+ pr_debug("pcm_oss: mmap begin\n");
#endif
pcm_oss_file = file->private_data;
switch ((area->vm_flags & (VM_READ | VM_WRITE))) {
@@ -2763,7 +2881,7 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
if (substream)
break;
- /* Fall through */
+ fallthrough;
case VM_READ:
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
break;
@@ -2775,7 +2893,7 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
}
/* set VM_READ access as well to fix memset() routines that do
reads before writes (to improve performance) */
- area->vm_flags |= VM_READ;
+ vm_flags_set(area, VM_READ);
if (substream == NULL)
return -ENXIO;
runtime = substream->runtime;
@@ -2787,7 +2905,12 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
return -EIO;
if (runtime->oss.params) {
- if ((err = snd_pcm_oss_change_params(substream)) < 0)
+ /* use mutex_trylock() for params_lock for avoiding a deadlock
+ * between mmap_lock and params_lock taken by
+ * copy_from/to_user() in snd_pcm_oss_write/read()
+ */
+ err = snd_pcm_oss_change_params(substream, true);
+ if (err < 0)
return err;
}
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
@@ -2805,7 +2928,7 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
runtime->silence_threshold = 0;
runtime->silence_size = 0;
#ifdef OSS_DEBUG
- printk(KERN_DEBUG "pcm_oss: mmap ok, bytes = 0x%x\n",
+ pr_debug("pcm_oss: mmap ok, bytes = 0x%x\n",
runtime->oss.mmap_bytes);
#endif
/* In mmap mode we never stop */
@@ -2824,7 +2947,7 @@ static void snd_pcm_oss_proc_read(struct snd_info_entry *entry,
{
struct snd_pcm_str *pstr = entry->private_data;
struct snd_pcm_oss_setup *setup = pstr->oss.setup_list;
- mutex_lock(&pstr->oss.setup_mutex);
+ guard(mutex)(&pstr->oss.setup_mutex);
while (setup) {
snd_iprintf(buffer, "%s %u %u%s%s%s%s%s%s\n",
setup->task_name,
@@ -2838,7 +2961,6 @@ static void snd_pcm_oss_proc_read(struct snd_info_entry *entry,
setup->nosilence ? " no-silence" : "");
setup = setup->next;
}
- mutex_unlock(&pstr->oss.setup_mutex);
}
static void snd_pcm_oss_proc_free_setup_list(struct snd_pcm_str * pstr)
@@ -2864,12 +2986,11 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
struct snd_pcm_oss_setup *setup, *setup1, template;
while (!snd_info_get_line(buffer, line, sizeof(line))) {
- mutex_lock(&pstr->oss.setup_mutex);
+ guard(mutex)(&pstr->oss.setup_mutex);
memset(&template, 0, sizeof(template));
ptr = snd_info_get_str(task_name, line, sizeof(task_name));
if (!strcmp(task_name, "clear") || !strcmp(task_name, "erase")) {
snd_pcm_oss_proc_free_setup_list(pstr);
- mutex_unlock(&pstr->oss.setup_mutex);
continue;
}
for (setup = pstr->oss.setup_list; setup; setup = setup->next) {
@@ -2909,7 +3030,6 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
setup = kmalloc(sizeof(*setup), GFP_KERNEL);
if (! setup) {
buffer->error = -ENOMEM;
- mutex_unlock(&pstr->oss.setup_mutex);
return;
}
if (pstr->oss.setup_list == NULL)
@@ -2923,12 +3043,10 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
if (! template.task_name) {
kfree(setup);
buffer->error = -ENOMEM;
- mutex_unlock(&pstr->oss.setup_mutex);
return;
}
}
*setup = template;
- mutex_unlock(&pstr->oss.setup_mutex);
}
}
@@ -2940,9 +3058,10 @@ static void snd_pcm_oss_proc_init(struct snd_pcm *pcm)
struct snd_pcm_str *pstr = &pcm->streams[stream];
if (pstr->substream_count == 0)
continue;
- if ((entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root)) != NULL) {
+ entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root);
+ if (entry) {
entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+ entry->mode = S_IFREG | 0644;
entry->c.text.read = snd_pcm_oss_proc_read;
entry->c.text.write = snd_pcm_oss_proc_write;
entry->private_data = pstr;
@@ -2966,8 +3085,12 @@ static void snd_pcm_oss_proc_done(struct snd_pcm *pcm)
}
}
#else /* !CONFIG_SND_VERBOSE_PROCFS */
-#define snd_pcm_oss_proc_init(pcm)
-#define snd_pcm_oss_proc_done(pcm)
+static inline void snd_pcm_oss_proc_init(struct snd_pcm *pcm)
+{
+}
+static inline void snd_pcm_oss_proc_done(struct snd_pcm *pcm)
+{
+}
#endif /* CONFIG_SND_VERBOSE_PROCFS */
/*
@@ -2981,7 +3104,6 @@ static const struct file_operations snd_pcm_oss_f_reg =
.write = snd_pcm_oss_write,
.open = snd_pcm_oss_open,
.release = snd_pcm_oss_release,
- .llseek = no_llseek,
.poll = snd_pcm_oss_poll,
.unlocked_ioctl = snd_pcm_oss_ioctl,
.compat_ioctl = snd_pcm_oss_ioctl_compat,
@@ -2990,12 +3112,10 @@ static const struct file_operations snd_pcm_oss_f_reg =
static void register_oss_dsp(struct snd_pcm *pcm, int index)
{
- char name[128];
- sprintf(name, "dsp%i%i", pcm->card->number, pcm->device);
if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,
pcm->card, index, &snd_pcm_oss_f_reg,
- pcm, name) < 0) {
- snd_printk(KERN_ERR "unable to register OSS PCM device %i:%i\n",
+ pcm) < 0) {
+ pcm_err(pcm, "unable to register OSS PCM device %i:%i\n",
pcm->card->number, pcm->device);
}
}
@@ -3076,17 +3196,18 @@ static int __init alsa_pcm_oss_init(void)
/* check device map table */
for (i = 0; i < SNDRV_CARDS; i++) {
if (dsp_map[i] < 0 || dsp_map[i] >= SNDRV_PCM_DEVICES) {
- snd_printk(KERN_ERR "invalid dsp_map[%d] = %d\n",
+ pr_err("ALSA: pcm_oss: invalid dsp_map[%d] = %d\n",
i, dsp_map[i]);
dsp_map[i] = 0;
}
if (adsp_map[i] < 0 || adsp_map[i] >= SNDRV_PCM_DEVICES) {
- snd_printk(KERN_ERR "invalid adsp_map[%d] = %d\n",
+ pr_err("ALSA: pcm_oss: invalid adsp_map[%d] = %d\n",
i, adsp_map[i]);
adsp_map[i] = 1;
}
}
- if ((err = snd_pcm_notify(&snd_pcm_oss_notify, 0)) < 0)
+ err = snd_pcm_notify(&snd_pcm_oss_notify, 0);
+ if (err < 0)
return err;
return 0;
}
diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c
index 6751daa3bb50..82e180c776ae 100644
--- a/sound/core/oss/pcm_plugin.c
+++ b/sound/core/oss/pcm_plugin.c
@@ -59,15 +59,19 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t
} else {
format = &plugin->dst_format;
}
- if ((width = snd_pcm_format_physical_width(format->format)) < 0)
+ width = snd_pcm_format_physical_width(format->format);
+ if (width < 0)
return width;
- size = frames * format->channels * width;
+ size = array3_size(frames, format->channels, width);
+ /* check for too large period size once again */
+ if (size > 1024 * 1024)
+ return -ENOMEM;
if (snd_BUG_ON(size % 8))
return -ENXIO;
size /= 8;
if (plugin->buf_frames < frames) {
- vfree(plugin->buf);
- plugin->buf = vmalloc(size);
+ kvfree(plugin->buf);
+ plugin->buf = kvzalloc(size, GFP_KERNEL);
plugin->buf_frames = frames;
}
if (!plugin->buf) {
@@ -111,7 +115,7 @@ int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames)
while (plugin->next) {
if (plugin->dst_frames)
frames = plugin->dst_frames(plugin, frames);
- if (snd_BUG_ON(frames <= 0))
+ if ((snd_pcm_sframes_t)frames <= 0)
return -ENXIO;
plugin = plugin->next;
err = snd_pcm_plugin_alloc(plugin, frames);
@@ -123,7 +127,7 @@ int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames)
while (plugin->prev) {
if (plugin->src_frames)
frames = plugin->src_frames(plugin, frames);
- if (snd_BUG_ON(frames <= 0))
+ if ((snd_pcm_sframes_t)frames <= 0)
return -ENXIO;
plugin = plugin->prev;
err = snd_pcm_plugin_alloc(plugin, frames);
@@ -191,80 +195,87 @@ int snd_pcm_plugin_free(struct snd_pcm_plugin *plugin)
if (plugin->private_free)
plugin->private_free(plugin);
kfree(plugin->buf_channels);
- vfree(plugin->buf);
+ kvfree(plugin->buf);
kfree(plugin);
return 0;
}
-snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t drv_frames)
+static snd_pcm_sframes_t calc_dst_frames(struct snd_pcm_substream *plug,
+ snd_pcm_sframes_t frames,
+ bool check_size)
{
- struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next;
- int stream = snd_pcm_plug_stream(plug);
+ struct snd_pcm_plugin *plugin, *plugin_next;
- if (snd_BUG_ON(!plug))
- return -ENXIO;
- if (drv_frames == 0)
- return 0;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- plugin = snd_pcm_plug_last(plug);
- while (plugin && drv_frames > 0) {
- plugin_prev = plugin->prev;
- if (plugin->src_frames)
- drv_frames = plugin->src_frames(plugin, drv_frames);
- plugin = plugin_prev;
+ plugin = snd_pcm_plug_first(plug);
+ while (plugin && frames > 0) {
+ plugin_next = plugin->next;
+ if (check_size && plugin->buf_frames &&
+ frames > plugin->buf_frames)
+ frames = plugin->buf_frames;
+ if (plugin->dst_frames) {
+ frames = plugin->dst_frames(plugin, frames);
+ if (frames < 0)
+ return frames;
}
- } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
- plugin = snd_pcm_plug_first(plug);
- while (plugin && drv_frames > 0) {
- plugin_next = plugin->next;
- if (plugin->dst_frames)
- drv_frames = plugin->dst_frames(plugin, drv_frames);
- plugin = plugin_next;
+ plugin = plugin_next;
+ }
+ return frames;
+}
+
+static snd_pcm_sframes_t calc_src_frames(struct snd_pcm_substream *plug,
+ snd_pcm_sframes_t frames,
+ bool check_size)
+{
+ struct snd_pcm_plugin *plugin, *plugin_prev;
+
+ plugin = snd_pcm_plug_last(plug);
+ while (plugin && frames > 0) {
+ plugin_prev = plugin->prev;
+ if (plugin->src_frames) {
+ frames = plugin->src_frames(plugin, frames);
+ if (frames < 0)
+ return frames;
}
- } else
+ if (check_size && plugin->buf_frames &&
+ frames > plugin->buf_frames)
+ frames = plugin->buf_frames;
+ plugin = plugin_prev;
+ }
+ return frames;
+}
+
+snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t drv_frames)
+{
+ if (snd_BUG_ON(!plug))
+ return -ENXIO;
+ switch (snd_pcm_plug_stream(plug)) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ return calc_src_frames(plug, drv_frames, false);
+ case SNDRV_PCM_STREAM_CAPTURE:
+ return calc_dst_frames(plug, drv_frames, false);
+ default:
snd_BUG();
- return drv_frames;
+ return -EINVAL;
+ }
}
snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pcm_uframes_t clt_frames)
{
- struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next;
- snd_pcm_sframes_t frames;
- int stream = snd_pcm_plug_stream(plug);
-
if (snd_BUG_ON(!plug))
return -ENXIO;
- if (clt_frames == 0)
- return 0;
- frames = clt_frames;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- plugin = snd_pcm_plug_first(plug);
- while (plugin && frames > 0) {
- plugin_next = plugin->next;
- if (plugin->dst_frames) {
- frames = plugin->dst_frames(plugin, frames);
- if (frames < 0)
- return frames;
- }
- plugin = plugin_next;
- }
- } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
- plugin = snd_pcm_plug_last(plug);
- while (plugin) {
- plugin_prev = plugin->prev;
- if (plugin->src_frames) {
- frames = plugin->src_frames(plugin, frames);
- if (frames < 0)
- return frames;
- }
- plugin = plugin_prev;
- }
- } else
+ switch (snd_pcm_plug_stream(plug)) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ return calc_dst_frames(plug, clt_frames, false);
+ case SNDRV_PCM_STREAM_CAPTURE:
+ return calc_src_frames(plug, clt_frames, false);
+ default:
snd_BUG();
- return frames;
+ return -EINVAL;
+ }
}
-static int snd_pcm_plug_formats(struct snd_mask *mask, int format)
+static int snd_pcm_plug_formats(const struct snd_mask *mask,
+ snd_pcm_format_t format)
{
struct snd_mask formats = *mask;
u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
@@ -276,16 +287,16 @@ static int snd_pcm_plug_formats(struct snd_mask *mask, int format)
SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE |
SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE);
- snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW);
+ snd_mask_set(&formats, (__force int)SNDRV_PCM_FORMAT_MU_LAW);
- if (formats.bits[0] & (u32)linfmts)
- formats.bits[0] |= (u32)linfmts;
- if (formats.bits[1] & (u32)(linfmts >> 32))
- formats.bits[1] |= (u32)(linfmts >> 32);
- return snd_mask_test(&formats, format);
+ if (formats.bits[0] & lower_32_bits(linfmts))
+ formats.bits[0] |= lower_32_bits(linfmts);
+ if (formats.bits[1] & upper_32_bits(linfmts))
+ formats.bits[1] |= upper_32_bits(linfmts);
+ return snd_mask_test(&formats, (__force int)format);
}
-static int preferred_formats[] = {
+static const snd_pcm_format_t preferred_formats[] = {
SNDRV_PCM_FORMAT_S16_LE,
SNDRV_PCM_FORMAT_S16_BE,
SNDRV_PCM_FORMAT_U16_LE,
@@ -306,24 +317,25 @@ static int preferred_formats[] = {
SNDRV_PCM_FORMAT_U8
};
-int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask)
+snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format,
+ const struct snd_mask *format_mask)
{
int i;
- if (snd_mask_test(format_mask, format))
+ if (snd_mask_test(format_mask, (__force int)format))
return format;
- if (! snd_pcm_plug_formats(format_mask, format))
- return -EINVAL;
+ if (!snd_pcm_plug_formats(format_mask, format))
+ return (__force snd_pcm_format_t)-EINVAL;
if (snd_pcm_format_linear(format)) {
unsigned int width = snd_pcm_format_width(format);
int unsignd = snd_pcm_format_unsigned(format) > 0;
int big = snd_pcm_format_big_endian(format) > 0;
unsigned int badness, best = -1;
- int best_format = -1;
+ snd_pcm_format_t best_format = (__force snd_pcm_format_t)-1;
for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) {
- int f = preferred_formats[i];
+ snd_pcm_format_t f = preferred_formats[i];
unsigned int w;
- if (!snd_mask_test(format_mask, f))
+ if (!snd_mask_test(format_mask, (__force int)f))
continue;
w = snd_pcm_format_width(f);
if (w >= width)
@@ -337,17 +349,21 @@ int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask)
best = badness;
}
}
- return best_format >= 0 ? best_format : -EINVAL;
+ if ((__force int)best_format >= 0)
+ return best_format;
+ else
+ return (__force snd_pcm_format_t)-EINVAL;
} else {
switch (format) {
case SNDRV_PCM_FORMAT_MU_LAW:
for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) {
- int format1 = preferred_formats[i];
- if (snd_mask_test(format_mask, format1))
+ snd_pcm_format_t format1 = preferred_formats[i];
+ if (snd_mask_test(format_mask, (__force int)format1))
return format1;
}
+ fallthrough;
default:
- return -EINVAL;
+ return (__force snd_pcm_format_t)-EINVAL;
}
}
}
@@ -359,7 +375,7 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *plug,
struct snd_pcm_plugin_format tmpformat;
struct snd_pcm_plugin_format dstformat;
struct snd_pcm_plugin_format srcformat;
- int src_access, dst_access;
+ snd_pcm_access_t src_access, dst_access;
struct snd_pcm_plugin *plugin = NULL;
int err;
int stream = snd_pcm_plug_stream(plug);
@@ -560,7 +576,8 @@ snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plu
}
v = plugin->buf_channels;
*channels = v;
- if ((width = snd_pcm_format_physical_width(format->format)) < 0)
+ width = snd_pcm_format_physical_width(format->format);
+ if (width < 0)
return width;
nchannels = format->channels;
if (snd_BUG_ON(plugin->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
@@ -585,28 +602,38 @@ snd_pcm_sframes_t snd_pcm_plug_write_transfer(struct snd_pcm_substream *plug, st
snd_pcm_sframes_t frames = size;
plugin = snd_pcm_plug_first(plug);
- while (plugin && frames > 0) {
- if ((next = plugin->next) != NULL) {
+ while (plugin) {
+ if (frames <= 0)
+ return frames;
+ next = plugin->next;
+ if (next) {
snd_pcm_sframes_t frames1 = frames;
- if (plugin->dst_frames)
+ if (plugin->dst_frames) {
frames1 = plugin->dst_frames(plugin, frames);
- if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) {
- return err;
+ if (frames1 <= 0)
+ return frames1;
}
+ err = next->client_channels(next, frames1, &dst_channels);
+ if (err < 0)
+ return err;
if (err != frames1) {
frames = err;
- if (plugin->src_frames)
+ if (plugin->src_frames) {
frames = plugin->src_frames(plugin, frames1);
+ if (frames <= 0)
+ return frames;
+ }
}
} else
dst_channels = NULL;
pdprintf("write plugin: %s, %li\n", plugin->name, frames);
- if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0)
+ frames = plugin->transfer(plugin, src_channels, dst_channels, frames);
+ if (frames < 0)
return frames;
src_channels = dst_channels;
plugin = next;
}
- return snd_pcm_plug_client_size(plug, frames);
+ return calc_src_frames(plug, frames, true);
}
snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, struct snd_pcm_plugin_channel *dst_channels_final, snd_pcm_uframes_t size)
@@ -616,23 +643,25 @@ snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, str
snd_pcm_sframes_t frames = size;
int err;
- frames = snd_pcm_plug_slave_size(plug, frames);
+ frames = calc_src_frames(plug, frames, true);
if (frames < 0)
return frames;
src_channels = NULL;
plugin = snd_pcm_plug_first(plug);
while (plugin && frames > 0) {
- if ((next = plugin->next) != NULL) {
- if ((err = plugin->client_channels(plugin, frames, &dst_channels)) < 0) {
+ next = plugin->next;
+ if (next) {
+ err = plugin->client_channels(plugin, frames, &dst_channels);
+ if (err < 0)
return err;
- }
frames = err;
} else {
dst_channels = dst_channels_final;
}
pdprintf("read plugin: %s, %li\n", plugin->name, frames);
- if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0)
+ frames = plugin->transfer(plugin, src_channels, dst_channels, frames);
+ if (frames < 0)
return frames;
plugin = next;
src_channels = dst_channels;
@@ -641,7 +670,7 @@ snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, str
}
int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst_offset,
- size_t samples, int format)
+ size_t samples, snd_pcm_format_t format)
{
/* FIXME: sub byte resolution and odd dst_offset */
unsigned char *dst;
@@ -688,7 +717,7 @@ int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst
int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_area, size_t src_offset,
const struct snd_pcm_channel_area *dst_area, size_t dst_offset,
- size_t samples, int format)
+ size_t samples, snd_pcm_format_t format)
{
/* FIXME: sub byte resolution and odd dst_offset */
char *src, *dst;
diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h
index b9afab603711..7b76cf64157e 100644
--- a/sound/core/oss/pcm_plugin.h
+++ b/sound/core/oss/pcm_plugin.h
@@ -1,25 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __PCM_PLUGIN_H
#define __PCM_PLUGIN_H
/*
* Digital Audio (Plugin interface) abstract layer
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
@@ -46,7 +31,7 @@ struct snd_pcm_plugin_channel {
};
struct snd_pcm_plugin_format {
- int format;
+ snd_pcm_format_t format;
unsigned int rate;
unsigned int channels;
};
@@ -58,7 +43,7 @@ struct snd_pcm_plugin {
struct snd_pcm_plugin_format dst_format; /* destination format */
int src_width; /* sample width in bits */
int dst_width; /* sample width in bits */
- int access;
+ snd_pcm_access_t access;
snd_pcm_sframes_t (*src_frames)(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t dst_frames);
snd_pcm_sframes_t (*dst_frames)(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t src_frames);
snd_pcm_sframes_t (*client_channels)(struct snd_pcm_plugin *plugin,
@@ -79,7 +64,7 @@ struct snd_pcm_plugin {
char *buf;
snd_pcm_uframes_t buf_frames;
struct snd_pcm_plugin_channel *buf_channels;
- char extra_data[0];
+ char extra_data[];
};
int snd_pcm_plugin_build(struct snd_pcm_substream *handle,
@@ -89,7 +74,6 @@ int snd_pcm_plugin_build(struct snd_pcm_substream *handle,
size_t extra,
struct snd_pcm_plugin **ret);
int snd_pcm_plugin_free(struct snd_pcm_plugin *plugin);
-int snd_pcm_plugin_clear(struct snd_pcm_plugin **first);
int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames);
snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t drv_size);
snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t clt_size);
@@ -125,7 +109,8 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_pcm_hw_params *slave_params);
-int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask);
+snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format,
+ const struct snd_mask *format_mask);
int snd_pcm_plugin_append(struct snd_pcm_plugin *plugin);
@@ -146,37 +131,33 @@ snd_pcm_sframes_t snd_pcm_plugin_client_channels(struct snd_pcm_plugin *plugin,
int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_channel,
size_t dst_offset,
- size_t samples, int format);
+ size_t samples, snd_pcm_format_t format);
int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_channel,
size_t src_offset,
const struct snd_pcm_channel_area *dst_channel,
size_t dst_offset,
- size_t samples, int format);
+ size_t samples, snd_pcm_format_t format);
+
+#else
+
+static inline snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t drv_size) { return drv_size; }
+static inline snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t clt_size) { return clt_size; }
+static inline int snd_pcm_plug_slave_format(int format, const struct snd_mask *format_mask) { return format; }
+
+#endif
-void *snd_pcm_plug_buf_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t size);
-void snd_pcm_plug_buf_unlock(struct snd_pcm_substream *plug, void *ptr);
snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream,
const char *ptr, snd_pcm_uframes_t size,
int in_kernel);
snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream,
char *ptr, snd_pcm_uframes_t size, int in_kernel);
snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream,
- void **bufs, snd_pcm_uframes_t frames,
- int in_kernel);
+ void **bufs, snd_pcm_uframes_t frames);
snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream,
- void **bufs, snd_pcm_uframes_t frames,
- int in_kernel);
-
-#else
-
-static inline snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t drv_size) { return drv_size; }
-static inline snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t clt_size) { return clt_size; }
-static inline int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask) { return format; }
-
-#endif
+ void **bufs, snd_pcm_uframes_t frames);
#ifdef PLUGIN_DEBUG
-#define pdprintf(fmt, args...) printk(KERN_DEBUG "plugin: " fmt, ##args)
+#define pdprintf(fmt, args...) pr_debug("plugin: " fmt, ##args)
#else
#define pdprintf(fmt, args...)
#endif
diff --git a/sound/core/oss/rate.c b/sound/core/oss/rate.c
index 2fa9299a440d..b56eeda5e30e 100644
--- a/sound/core/oss/rate.c
+++ b/sound/core/oss/rate.c
@@ -47,7 +47,7 @@ struct rate_priv {
unsigned int pos;
rate_f func;
snd_pcm_sframes_t old_src_frames, old_dst_frames;
- struct rate_channel channels[0];
+ struct rate_channel channels[];
};
static void rate_init(struct snd_pcm_plugin *plugin)
@@ -193,7 +193,7 @@ static snd_pcm_sframes_t rate_src_frames(struct snd_pcm_plugin *plugin, snd_pcm_
if (plugin->src_format.rate < plugin->dst_format.rate) {
res = (((frames * data->pitch) + (BITS/2)) >> SHIFT);
} else {
- res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch);
+ res = DIV_ROUND_CLOSEST(frames << SHIFT, data->pitch);
}
if (data->old_src_frames > 0) {
snd_pcm_sframes_t frames1 = frames, res1 = data->old_dst_frames;
@@ -224,7 +224,7 @@ static snd_pcm_sframes_t rate_dst_frames(struct snd_pcm_plugin *plugin, snd_pcm_
return 0;
data = (struct rate_priv *)plugin->extra_data;
if (plugin->src_format.rate < plugin->dst_format.rate) {
- res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch);
+ res = DIV_ROUND_CLOSEST(frames << SHIFT, data->pitch);
} else {
res = (((frames * data->pitch) + (BITS/2)) >> SHIFT);
}
@@ -294,7 +294,7 @@ static int rate_action(struct snd_pcm_plugin *plugin,
default:
break;
}
- return 0; /* silenty ignore other actions */
+ return 0; /* silently ignore other actions */
}
int snd_pcm_plugin_build_rate(struct snd_pcm_substream *plug,
@@ -323,8 +323,8 @@ int snd_pcm_plugin_build_rate(struct snd_pcm_substream *plug,
err = snd_pcm_plugin_build(plug, "rate conversion",
src_format, dst_format,
- sizeof(struct rate_priv) +
- src_format->channels * sizeof(struct rate_channel),
+ struct_size(data, channels,
+ src_format->channels),
&plugin);
if (err < 0)
return err;
diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c
index bbe25d8c450a..72dea04197ef 100644
--- a/sound/core/oss/route.c
+++ b/sound/core/oss/route.c
@@ -25,7 +25,7 @@
#include "pcm_plugin.h"
static void zero_areas(struct snd_pcm_plugin_channel *dvp, int ndsts,
- snd_pcm_uframes_t frames, int format)
+ snd_pcm_uframes_t frames, snd_pcm_format_t format)
{
int dst = 0;
for (; dst < ndsts; ++dst) {
@@ -38,7 +38,7 @@ static void zero_areas(struct snd_pcm_plugin_channel *dvp, int ndsts,
static inline void copy_area(const struct snd_pcm_plugin_channel *src_channel,
struct snd_pcm_plugin_channel *dst_channel,
- snd_pcm_uframes_t frames, int format)
+ snd_pcm_uframes_t frames, snd_pcm_format_t format)
{
dst_channel->enabled = 1;
snd_pcm_area_copy(&src_channel->area, 0, &dst_channel->area, 0, frames, format);
@@ -51,12 +51,14 @@ static snd_pcm_sframes_t route_transfer(struct snd_pcm_plugin *plugin,
{
int nsrcs, ndsts, dst;
struct snd_pcm_plugin_channel *dvp;
- int format;
+ snd_pcm_format_t format;
if (snd_BUG_ON(!plugin || !src_channels || !dst_channels))
return -ENXIO;
if (frames == 0)
return 0;
+ if (frames > dst_channels[0].frames)
+ frames = dst_channels[0].frames;
nsrcs = plugin->src_format.channels;
ndsts = plugin->dst_format.channels;
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 6b4b1287b314..283aac441fa0 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -1,41 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Digital Audio (PCM) abstract layer
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <linux/time.h>
#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/nospec.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/pcm.h>
+#include <sound/timer.h>
#include <sound/control.h>
#include <sound/info.h>
+#include "pcm_local.h"
+
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>");
MODULE_DESCRIPTION("Midlevel PCM code for ALSA.");
MODULE_LICENSE("GPL");
static LIST_HEAD(snd_pcm_devices);
-static LIST_HEAD(snd_pcm_notify_list);
static DEFINE_MUTEX(register_mutex);
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
+static LIST_HEAD(snd_pcm_notify_list);
+#endif
static int snd_pcm_free(struct snd_pcm *pcm);
static int snd_pcm_dev_free(struct snd_device *device);
@@ -70,6 +63,9 @@ static int snd_pcm_add(struct snd_pcm *newpcm)
{
struct snd_pcm *pcm;
+ if (newpcm->internal)
+ return 0;
+
list_for_each_entry(pcm, &snd_pcm_devices, list) {
if (pcm->card == newpcm->card && pcm->device == newpcm->device)
return -EBUSY;
@@ -95,9 +91,8 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
if (get_user(device, (int __user *)arg))
return -EFAULT;
- mutex_lock(&register_mutex);
- device = snd_pcm_next(card, device);
- mutex_unlock(&register_mutex);
+ scoped_guard(mutex, &register_mutex)
+ device = snd_pcm_next(card, device);
if (put_user(device, (int __user *)arg))
return -EFAULT;
return 0;
@@ -110,7 +105,6 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
struct snd_pcm *pcm;
struct snd_pcm_str *pstr;
struct snd_pcm_substream *substream;
- int err;
info = (struct snd_pcm_info __user *)arg;
if (get_user(device, &info->device))
@@ -119,35 +113,26 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
return -EFAULT;
if (stream < 0 || stream > 1)
return -EINVAL;
+ stream = array_index_nospec(stream, 2);
if (get_user(subdevice, &info->subdevice))
return -EFAULT;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
pcm = snd_pcm_get(card, device);
- if (pcm == NULL) {
- err = -ENXIO;
- goto _error;
- }
+ if (pcm == NULL)
+ return -ENXIO;
pstr = &pcm->streams[stream];
- if (pstr->substream_count == 0) {
- err = -ENOENT;
- goto _error;
- }
- if (subdevice >= pstr->substream_count) {
- err = -ENXIO;
- goto _error;
- }
+ if (pstr->substream_count == 0)
+ return -ENOENT;
+ if (subdevice >= pstr->substream_count)
+ return -ENXIO;
for (substream = pstr->substream; substream;
substream = substream->next)
if (substream->number == (int)subdevice)
break;
- if (substream == NULL) {
- err = -ENXIO;
- goto _error;
- }
- err = snd_pcm_info_user(substream, info);
- _error:
- mutex_unlock(&register_mutex);
- return err;
+ if (substream == NULL)
+ return -ENXIO;
+ guard(mutex)(&pcm->open_mutex);
+ return snd_pcm_info_user(substream, info);
}
case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE:
{
@@ -155,7 +140,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
if (get_user(val, (int __user *)arg))
return -EFAULT;
- control->prefer_pcm_subdevice = val;
+ control->preferred_subdevice[SND_CTL_SUBDEV_PCM] = val;
return 0;
}
}
@@ -164,7 +149,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
#define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v
-static char *snd_pcm_format_names[] = {
+static const char * const snd_pcm_format_names[] = {
FORMAT(S8),
FORMAT(U8),
FORMAT(S16_LE),
@@ -207,13 +192,30 @@ static char *snd_pcm_format_names[] = {
FORMAT(G723_24_1B),
FORMAT(G723_40),
FORMAT(G723_40_1B),
+ FORMAT(DSD_U8),
+ FORMAT(DSD_U16_LE),
+ FORMAT(DSD_U32_LE),
+ FORMAT(DSD_U16_BE),
+ FORMAT(DSD_U32_BE),
+ FORMAT(S20_LE),
+ FORMAT(S20_BE),
+ FORMAT(U20_LE),
+ FORMAT(U20_BE),
};
+/**
+ * snd_pcm_format_name - Return a name string for the given PCM format
+ * @format: PCM format
+ *
+ * Return: the format name string
+ */
const char *snd_pcm_format_name(snd_pcm_format_t format)
{
- if (format >= ARRAY_SIZE(snd_pcm_format_names))
+ unsigned int format_num = (__force unsigned int)format;
+
+ if (format_num >= ARRAY_SIZE(snd_pcm_format_names) || !snd_pcm_format_names[format_num])
return "Unknown";
- return snd_pcm_format_names[format];
+ return snd_pcm_format_names[format_num];
}
EXPORT_SYMBOL_GPL(snd_pcm_format_name);
@@ -229,12 +231,12 @@ EXPORT_SYMBOL_GPL(snd_pcm_format_name);
#define START(v) [SNDRV_PCM_START_##v] = #v
#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v
-static char *snd_pcm_stream_names[] = {
+static const char * const snd_pcm_stream_names[] = {
STREAM(PLAYBACK),
STREAM(CAPTURE),
};
-static char *snd_pcm_state_names[] = {
+static const char * const snd_pcm_state_names[] = {
STATE(OPEN),
STATE(SETUP),
STATE(PREPARED),
@@ -243,9 +245,10 @@ static char *snd_pcm_state_names[] = {
STATE(DRAINING),
STATE(PAUSED),
STATE(SUSPENDED),
+ STATE(DISCONNECTED),
};
-static char *snd_pcm_access_names[] = {
+static const char * const snd_pcm_access_names[] = {
ACCESS(MMAP_INTERLEAVED),
ACCESS(MMAP_NONINTERLEAVED),
ACCESS(MMAP_COMPLEX),
@@ -253,11 +256,14 @@ static char *snd_pcm_access_names[] = {
ACCESS(RW_NONINTERLEAVED),
};
-static char *snd_pcm_subformat_names[] = {
+static const char * const snd_pcm_subformat_names[] = {
SUBFORMAT(STD),
+ SUBFORMAT(MSBITS_MAX),
+ SUBFORMAT(MSBITS_20),
+ SUBFORMAT(MSBITS_24),
};
-static char *snd_pcm_tstamp_mode_names[] = {
+static const char * const snd_pcm_tstamp_mode_names[] = {
TSTAMP(NONE),
TSTAMP(ENABLE),
};
@@ -269,12 +275,12 @@ static const char *snd_pcm_stream_name(int stream)
static const char *snd_pcm_access_name(snd_pcm_access_t access)
{
- return snd_pcm_access_names[access];
+ return snd_pcm_access_names[(__force int)access];
}
static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat)
{
- return snd_pcm_subformat_names[subformat];
+ return snd_pcm_subformat_names[(__force int)subformat];
}
static const char *snd_pcm_tstamp_mode_name(int mode)
@@ -284,10 +290,10 @@ static const char *snd_pcm_tstamp_mode_name(int mode)
static const char *snd_pcm_state_name(snd_pcm_state_t state)
{
- return snd_pcm_state_names[state];
+ return snd_pcm_state_names[(__force int)state];
}
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
#include <linux/soundcard.h>
static const char *snd_pcm_oss_format_name(int format)
@@ -322,22 +328,19 @@ static const char *snd_pcm_oss_format_name(int format)
static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream,
struct snd_info_buffer *buffer)
{
- struct snd_pcm_info *info;
+ struct snd_pcm_info *info __free(kfree) = NULL;
int err;
if (! substream)
return;
info = kmalloc(sizeof(*info), GFP_KERNEL);
- if (! info) {
- printk(KERN_DEBUG "snd_pcm_proc_info_read: cannot malloc\n");
+ if (!info)
return;
- }
err = snd_pcm_info(substream, info);
if (err < 0) {
snd_iprintf(buffer, "error %d\n", err);
- kfree(info);
return;
}
snd_iprintf(buffer, "card: %d\n", info->card);
@@ -351,7 +354,6 @@ static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream,
snd_iprintf(buffer, "subclass: %d\n", info->dev_subclass);
snd_iprintf(buffer, "subdevices_count: %d\n", info->subdevices_count);
snd_iprintf(buffer, "subdevices_avail: %d\n", info->subdevices_avail);
- kfree(info);
}
static void snd_pcm_stream_proc_info_read(struct snd_info_entry *entry,
@@ -373,15 +375,15 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
struct snd_pcm_substream *substream = entry->private_data;
struct snd_pcm_runtime *runtime;
- mutex_lock(&substream->pcm->open_mutex);
+ guard(mutex)(&substream->pcm->open_mutex);
runtime = substream->runtime;
if (!runtime) {
snd_iprintf(buffer, "closed\n");
- goto unlock;
+ return;
}
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ if (runtime->state == SNDRV_PCM_STATE_OPEN) {
snd_iprintf(buffer, "no setup\n");
- goto unlock;
+ return;
}
snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access));
snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format));
@@ -390,7 +392,7 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den);
snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size);
snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size);
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
if (substream->oss.oss) {
snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format));
snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels);
@@ -400,8 +402,6 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames);
}
#endif
- unlock:
- mutex_unlock(&substream->pcm->open_mutex);
}
static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry,
@@ -410,15 +410,15 @@ static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry,
struct snd_pcm_substream *substream = entry->private_data;
struct snd_pcm_runtime *runtime;
- mutex_lock(&substream->pcm->open_mutex);
+ guard(mutex)(&substream->pcm->open_mutex);
runtime = substream->runtime;
if (!runtime) {
snd_iprintf(buffer, "closed\n");
- goto unlock;
+ return;
}
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ if (runtime->state == SNDRV_PCM_STATE_OPEN) {
snd_iprintf(buffer, "no setup\n");
- goto unlock;
+ return;
}
snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode));
snd_iprintf(buffer, "period_step: %u\n", runtime->period_step);
@@ -428,8 +428,6 @@ static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold);
snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size);
snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary);
- unlock:
- mutex_unlock(&substream->pcm->open_mutex);
}
static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
@@ -437,38 +435,47 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
{
struct snd_pcm_substream *substream = entry->private_data;
struct snd_pcm_runtime *runtime;
- struct snd_pcm_status status;
+ struct snd_pcm_status64 status;
int err;
- mutex_lock(&substream->pcm->open_mutex);
+ guard(mutex)(&substream->pcm->open_mutex);
runtime = substream->runtime;
if (!runtime) {
snd_iprintf(buffer, "closed\n");
- goto unlock;
+ return;
}
memset(&status, 0, sizeof(status));
- err = snd_pcm_status(substream, &status);
+ err = snd_pcm_status64(substream, &status);
if (err < 0) {
snd_iprintf(buffer, "error %d\n", err);
- goto unlock;
+ return;
}
snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state));
snd_iprintf(buffer, "owner_pid : %d\n", pid_vnr(substream->pid));
- snd_iprintf(buffer, "trigger_time: %ld.%09ld\n",
- status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec);
- snd_iprintf(buffer, "tstamp : %ld.%09ld\n",
- status.tstamp.tv_sec, status.tstamp.tv_nsec);
+ snd_iprintf(buffer, "trigger_time: %lld.%09lld\n",
+ status.trigger_tstamp_sec, status.trigger_tstamp_nsec);
+ snd_iprintf(buffer, "tstamp : %lld.%09lld\n",
+ status.tstamp_sec, status.tstamp_nsec);
snd_iprintf(buffer, "delay : %ld\n", status.delay);
snd_iprintf(buffer, "avail : %ld\n", status.avail);
snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max);
snd_iprintf(buffer, "-----\n");
snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr);
snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr);
- unlock:
- mutex_unlock(&substream->pcm->open_mutex);
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+ snd_iprintf(buffer, "xrun_counter: %d\n", substream->xrun_counter);
+#endif
}
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+static void snd_pcm_xrun_injection_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_pcm_substream *substream = entry->private_data;
+
+ snd_pcm_stop_xrun(substream);
+}
+
static void snd_pcm_xrun_debug_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -494,54 +501,49 @@ static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr)
sprintf(name, "pcm%i%c", pcm->device,
pstr->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c');
- if ((entry = snd_info_create_card_entry(pcm->card, name, pcm->card->proc_root)) == NULL)
- return -ENOMEM;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
+ entry = snd_info_create_card_entry(pcm->card, name,
+ pcm->card->proc_root);
+ if (!entry)
return -ENOMEM;
- }
+ entry->mode = S_IFDIR | 0555;
pstr->proc_root = entry;
-
- if ((entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root)) != NULL) {
+ entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root);
+ if (entry)
snd_info_set_text_ops(entry, pstr, snd_pcm_stream_proc_info_read);
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- pstr->proc_info_entry = entry;
-
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- if ((entry = snd_info_create_card_entry(pcm->card, "xrun_debug",
- pstr->proc_root)) != NULL) {
- entry->c.text.read = snd_pcm_xrun_debug_read;
+ entry = snd_info_create_card_entry(pcm->card, "xrun_debug",
+ pstr->proc_root);
+ if (entry) {
+ snd_info_set_text_ops(entry, pstr, snd_pcm_xrun_debug_read);
entry->c.text.write = snd_pcm_xrun_debug_write;
- entry->mode |= S_IWUSR;
- entry->private_data = pstr;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
+ entry->mode |= 0200;
}
- pstr->proc_xrun_debug_entry = entry;
#endif
return 0;
}
static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr)
{
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- snd_info_free_entry(pstr->proc_xrun_debug_entry);
- pstr->proc_xrun_debug_entry = NULL;
-#endif
- snd_info_free_entry(pstr->proc_info_entry);
- pstr->proc_info_entry = NULL;
snd_info_free_entry(pstr->proc_root);
pstr->proc_root = NULL;
return 0;
}
+static struct snd_info_entry *
+create_substream_info_entry(struct snd_pcm_substream *substream,
+ const char *name,
+ void (*read)(struct snd_info_entry *,
+ struct snd_info_buffer *))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(substream->pcm->card, name,
+ substream->proc_root);
+ if (entry)
+ snd_info_set_text_ops(entry, substream, read);
+ return entry;
+}
+
static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream)
{
struct snd_info_entry *entry;
@@ -551,79 +553,64 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream)
card = substream->pcm->card;
sprintf(name, "sub%i", substream->number);
- if ((entry = snd_info_create_card_entry(card, name, substream->pstr->proc_root)) == NULL)
- return -ENOMEM;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
+ entry = snd_info_create_card_entry(card, name,
+ substream->pstr->proc_root);
+ if (!entry)
return -ENOMEM;
- }
+ entry->mode = S_IFDIR | 0555;
substream->proc_root = entry;
- if ((entry = snd_info_create_card_entry(card, "info", substream->proc_root)) != NULL) {
- snd_info_set_text_ops(entry, substream,
- snd_pcm_substream_proc_info_read);
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- substream->proc_info_entry = entry;
-
- if ((entry = snd_info_create_card_entry(card, "hw_params", substream->proc_root)) != NULL) {
- snd_info_set_text_ops(entry, substream,
- snd_pcm_substream_proc_hw_params_read);
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- substream->proc_hw_params_entry = entry;
-
- if ((entry = snd_info_create_card_entry(card, "sw_params", substream->proc_root)) != NULL) {
- snd_info_set_text_ops(entry, substream,
- snd_pcm_substream_proc_sw_params_read);
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- substream->proc_sw_params_entry = entry;
+ create_substream_info_entry(substream, "info",
+ snd_pcm_substream_proc_info_read);
+ create_substream_info_entry(substream, "hw_params",
+ snd_pcm_substream_proc_hw_params_read);
+ create_substream_info_entry(substream, "sw_params",
+ snd_pcm_substream_proc_sw_params_read);
+ create_substream_info_entry(substream, "status",
+ snd_pcm_substream_proc_status_read);
- if ((entry = snd_info_create_card_entry(card, "status", substream->proc_root)) != NULL) {
- snd_info_set_text_ops(entry, substream,
- snd_pcm_substream_proc_status_read);
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+ entry = create_substream_info_entry(substream, "xrun_injection", NULL);
+ if (entry) {
+ entry->c.text.write = snd_pcm_xrun_injection_write;
+ entry->mode = S_IFREG | 0200;
}
- substream->proc_status_entry = entry;
+#endif /* CONFIG_SND_PCM_XRUN_DEBUG */
return 0;
}
-static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream)
-{
- snd_info_free_entry(substream->proc_info_entry);
- substream->proc_info_entry = NULL;
- snd_info_free_entry(substream->proc_hw_params_entry);
- substream->proc_hw_params_entry = NULL;
- snd_info_free_entry(substream->proc_sw_params_entry);
- substream->proc_sw_params_entry = NULL;
- snd_info_free_entry(substream->proc_status_entry);
- substream->proc_status_entry = NULL;
- snd_info_free_entry(substream->proc_root);
- substream->proc_root = NULL;
- return 0;
-}
#else /* !CONFIG_SND_VERBOSE_PROCFS */
static inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; }
static inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; }
static inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; }
-static inline int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) { return 0; }
#endif /* CONFIG_SND_VERBOSE_PROCFS */
+static const struct attribute_group *pcm_dev_attr_groups[];
+
+/*
+ * PM callbacks: we need to deal only with suspend here, as the resume is
+ * triggered either from user-space or the driver's resume callback
+ */
+static int do_pcm_suspend(struct device *dev)
+{
+ struct snd_pcm_str *pstr = dev_get_drvdata(dev);
+
+ if (!pstr->pcm->no_device_suspend)
+ snd_pcm_suspend_all(pstr->pcm);
+ return 0;
+}
+
+static const struct dev_pm_ops pcm_dev_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(do_pcm_suspend, NULL)
+};
+
+/* device type for PCM -- basically only for passing PM callbacks */
+static const struct device_type pcm_dev_type = {
+ .name = "pcm",
+ .pm = &pcm_dev_pm_ops,
+};
+
/**
* snd_pcm_new_stream - create a new PCM stream
* @pcm: the pcm instance
@@ -635,7 +622,7 @@ static inline int snd_pcm_substream_proc_done(struct snd_pcm_substream *substrea
* calling this, i.e. zero must be given to the argument of
* snd_pcm_new().
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
{
@@ -643,26 +630,36 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
struct snd_pcm_str *pstr = &pcm->streams[stream];
struct snd_pcm_substream *substream, *prev;
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
mutex_init(&pstr->oss.setup_mutex);
#endif
pstr->stream = stream;
pstr->pcm = pcm;
pstr->substream_count = substream_count;
- if (substream_count > 0) {
+ if (!substream_count)
+ return 0;
+
+ err = snd_device_alloc(&pstr->dev, pcm->card);
+ if (err < 0)
+ return err;
+ dev_set_name(pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device,
+ stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c');
+ pstr->dev->groups = pcm_dev_attr_groups;
+ pstr->dev->type = &pcm_dev_type;
+ dev_set_drvdata(pstr->dev, pstr);
+
+ if (!pcm->internal) {
err = snd_pcm_stream_proc_init(pstr);
if (err < 0) {
- snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n");
+ pcm_err(pcm, "Error in snd_pcm_stream_proc_init\n");
return err;
}
}
prev = NULL;
for (idx = 0, prev = NULL; idx < substream_count; idx++) {
substream = kzalloc(sizeof(*substream), GFP_KERNEL);
- if (substream == NULL) {
- snd_printk(KERN_ERR "Cannot allocate PCM substream\n");
+ if (!substream)
return -ENOMEM;
- }
substream->pcm = pcm;
substream->pstr = pstr;
substream->number = idx;
@@ -673,123 +670,193 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
pstr->substream = substream;
else
prev->next = substream;
- err = snd_pcm_substream_proc_init(substream);
- if (err < 0) {
- snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n");
- if (prev == NULL)
- pstr->substream = NULL;
- else
- prev->next = NULL;
- kfree(substream);
- return err;
+
+ if (!pcm->internal) {
+ err = snd_pcm_substream_proc_init(substream);
+ if (err < 0) {
+ pcm_err(pcm,
+ "Error in snd_pcm_stream_proc_init\n");
+ if (prev == NULL)
+ pstr->substream = NULL;
+ else
+ prev->next = NULL;
+ kfree(substream);
+ return err;
+ }
}
substream->group = &substream->self_group;
- spin_lock_init(&substream->self_group.lock);
- INIT_LIST_HEAD(&substream->self_group.substreams);
+ snd_pcm_group_init(&substream->self_group);
list_add_tail(&substream->link_list, &substream->self_group.substreams);
atomic_set(&substream->mmap_count, 0);
prev = substream;
}
return 0;
}
-
EXPORT_SYMBOL(snd_pcm_new_stream);
-/**
- * snd_pcm_new - create a new PCM instance
- * @card: the card instance
- * @id: the id string
- * @device: the device index (zero based)
- * @playback_count: the number of substreams for playback
- * @capture_count: the number of substreams for capture
- * @rpcm: the pointer to store the new pcm instance
- *
- * Creates a new PCM instance.
- *
- * The pcm operators have to be set afterwards to the new instance
- * via snd_pcm_set_ops().
- *
- * Returns zero if successful, or a negative error code on failure.
- */
-int snd_pcm_new(struct snd_card *card, const char *id, int device,
- int playback_count, int capture_count,
- struct snd_pcm ** rpcm)
+static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
+ int playback_count, int capture_count, bool internal,
+ struct snd_pcm **rpcm)
{
struct snd_pcm *pcm;
int err;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_pcm_dev_free,
.dev_register = snd_pcm_dev_register,
.dev_disconnect = snd_pcm_dev_disconnect,
};
+ static const struct snd_device_ops internal_ops = {
+ .dev_free = snd_pcm_dev_free,
+ };
if (snd_BUG_ON(!card))
return -ENXIO;
if (rpcm)
*rpcm = NULL;
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
- if (pcm == NULL) {
- snd_printk(KERN_ERR "Cannot allocate PCM\n");
+ if (!pcm)
return -ENOMEM;
- }
pcm->card = card;
pcm->device = device;
- if (id)
- strlcpy(pcm->id, id, sizeof(pcm->id));
- if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {
- snd_pcm_free(pcm);
- return err;
- }
- if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) {
- snd_pcm_free(pcm);
- return err;
- }
+ pcm->internal = internal;
mutex_init(&pcm->open_mutex);
init_waitqueue_head(&pcm->open_wait);
- if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {
- snd_pcm_free(pcm);
- return err;
- }
+ INIT_LIST_HEAD(&pcm->list);
+ if (id)
+ strscpy(pcm->id, id, sizeof(pcm->id));
+
+ err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ playback_count);
+ if (err < 0)
+ goto free_pcm;
+
+ err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count);
+ if (err < 0)
+ goto free_pcm;
+
+ err = snd_device_new(card, SNDRV_DEV_PCM, pcm,
+ internal ? &internal_ops : &ops);
+ if (err < 0)
+ goto free_pcm;
+
if (rpcm)
*rpcm = pcm;
return 0;
+
+free_pcm:
+ snd_pcm_free(pcm);
+ return err;
}
+/**
+ * snd_pcm_new - create a new PCM instance
+ * @card: the card instance
+ * @id: the id string
+ * @device: the device index (zero based)
+ * @playback_count: the number of substreams for playback
+ * @capture_count: the number of substreams for capture
+ * @rpcm: the pointer to store the new pcm instance
+ *
+ * Creates a new PCM instance.
+ *
+ * The pcm operators have to be set afterwards to the new instance
+ * via snd_pcm_set_ops().
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_new(struct snd_card *card, const char *id, int device,
+ int playback_count, int capture_count, struct snd_pcm **rpcm)
+{
+ return _snd_pcm_new(card, id, device, playback_count, capture_count,
+ false, rpcm);
+}
EXPORT_SYMBOL(snd_pcm_new);
+/**
+ * snd_pcm_new_internal - create a new internal PCM instance
+ * @card: the card instance
+ * @id: the id string
+ * @device: the device index (zero based - shared with normal PCMs)
+ * @playback_count: the number of substreams for playback
+ * @capture_count: the number of substreams for capture
+ * @rpcm: the pointer to store the new pcm instance
+ *
+ * Creates a new internal PCM instance with no userspace device or procfs
+ * entries. This is used by ASoC Back End PCMs in order to create a PCM that
+ * will only be used internally by kernel drivers. i.e. it cannot be opened
+ * by userspace. It provides existing ASoC components drivers with a substream
+ * and access to any private data.
+ *
+ * The pcm operators have to be set afterwards to the new instance
+ * via snd_pcm_set_ops().
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_new_internal(struct snd_card *card, const char *id, int device,
+ int playback_count, int capture_count,
+ struct snd_pcm **rpcm)
+{
+ return _snd_pcm_new(card, id, device, playback_count, capture_count,
+ true, rpcm);
+}
+EXPORT_SYMBOL(snd_pcm_new_internal);
+
+static void free_chmap(struct snd_pcm_str *pstr)
+{
+ if (pstr->chmap_kctl) {
+ struct snd_card *card = pstr->pcm->card;
+
+ snd_ctl_remove(card, pstr->chmap_kctl);
+ pstr->chmap_kctl = NULL;
+ }
+}
+
static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
{
struct snd_pcm_substream *substream, *substream_next;
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
struct snd_pcm_oss_setup *setup, *setupn;
#endif
+
+ /* free all proc files under the stream */
+ snd_pcm_stream_proc_done(pstr);
+
substream = pstr->substream;
while (substream) {
substream_next = substream->next;
snd_pcm_timer_done(substream);
- snd_pcm_substream_proc_done(substream);
kfree(substream);
substream = substream_next;
}
- snd_pcm_stream_proc_done(pstr);
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
for (setup = pstr->oss.setup_list; setup; setup = setupn) {
setupn = setup->next;
kfree(setup->task_name);
kfree(setup);
}
#endif
+ free_chmap(pstr);
+ if (pstr->substream_count)
+ put_device(pstr->dev);
}
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
+#define pcm_call_notify(pcm, call) \
+ do { \
+ struct snd_pcm_notify *_notify; \
+ list_for_each_entry(_notify, &snd_pcm_notify_list, list) \
+ _notify->call(pcm); \
+ } while (0)
+#else
+#define pcm_call_notify(pcm, call) do {} while (0)
+#endif
+
static int snd_pcm_free(struct snd_pcm *pcm)
{
- struct snd_pcm_notify *notify;
-
if (!pcm)
return 0;
- list_for_each_entry(notify, &snd_pcm_notify_list, list) {
- notify->n_unregister(pcm);
- }
+ if (!pcm->internal)
+ pcm_call_notify(pcm, n_unregister);
if (pcm->private_free)
pcm->private_free(pcm);
snd_pcm_lib_preallocate_free_for_all(pcm);
@@ -812,48 +879,31 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
struct snd_pcm_str * pstr;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
- struct snd_ctl_file *kctl;
struct snd_card *card;
- int prefer_subdevice = -1;
+ int prefer_subdevice;
size_t size;
if (snd_BUG_ON(!pcm || !rsubstream))
return -ENXIO;
+ if (snd_BUG_ON(stream != SNDRV_PCM_STREAM_PLAYBACK &&
+ stream != SNDRV_PCM_STREAM_CAPTURE))
+ return -EINVAL;
*rsubstream = NULL;
pstr = &pcm->streams[stream];
if (pstr->substream == NULL || pstr->substream_count == 0)
return -ENODEV;
card = pcm->card;
- read_lock(&card->ctl_files_rwlock);
- list_for_each_entry(kctl, &card->ctl_files, list) {
- if (kctl->pid == task_pid(current)) {
- prefer_subdevice = kctl->prefer_pcm_subdevice;
- if (prefer_subdevice != -1)
- break;
- }
- }
- read_unlock(&card->ctl_files_rwlock);
-
- switch (stream) {
- case SNDRV_PCM_STREAM_PLAYBACK:
- if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
- for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) {
- if (SUBSTREAM_BUSY(substream))
- return -EAGAIN;
- }
- }
- break;
- case SNDRV_PCM_STREAM_CAPTURE:
- if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
- for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) {
- if (SUBSTREAM_BUSY(substream))
- return -EAGAIN;
- }
+ prefer_subdevice = snd_ctl_get_preferred_subdevice(card, SND_CTL_SUBDEV_PCM);
+
+ if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
+ int opposite = !stream;
+
+ for (substream = pcm->streams[opposite].substream; substream;
+ substream = substream->next) {
+ if (SUBSTREAM_BUSY(substream))
+ return -EAGAIN;
}
- break;
- default:
- return -EINVAL;
}
if (file->f_flags & O_APPEND) {
@@ -876,15 +926,12 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
return 0;
}
- if (prefer_subdevice >= 0) {
- for (substream = pstr->substream; substream; substream = substream->next)
- if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice)
- goto __ok;
- }
- for (substream = pstr->substream; substream; substream = substream->next)
- if (!SUBSTREAM_BUSY(substream))
+ for (substream = pstr->substream; substream; substream = substream->next) {
+ if (!SUBSTREAM_BUSY(substream) &&
+ (prefer_subdevice == -1 ||
+ substream->number == prefer_subdevice))
break;
- __ok:
+ }
if (substream == NULL)
return -EAGAIN;
@@ -893,27 +940,29 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
return -ENOMEM;
size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status));
- runtime->status = snd_malloc_pages(size, GFP_KERNEL);
+ runtime->status = alloc_pages_exact(size, GFP_KERNEL);
if (runtime->status == NULL) {
kfree(runtime);
return -ENOMEM;
}
- memset((void*)runtime->status, 0, size);
+ memset(runtime->status, 0, size);
size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control));
- runtime->control = snd_malloc_pages(size, GFP_KERNEL);
+ runtime->control = alloc_pages_exact(size, GFP_KERNEL);
if (runtime->control == NULL) {
- snd_free_pages((void*)runtime->status,
+ free_pages_exact(runtime->status,
PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)));
kfree(runtime);
return -ENOMEM;
}
- memset((void*)runtime->control, 0, size);
+ memset(runtime->control, 0, size);
init_waitqueue_head(&runtime->sleep);
init_waitqueue_head(&runtime->tsleep);
- runtime->status->state = SNDRV_PCM_STATE_OPEN;
+ __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_OPEN);
+ mutex_init(&runtime->buffer_mutex);
+ atomic_set(&runtime->buffer_accessing, 0);
substream->runtime = runtime;
substream->private_data = pcm->private_data;
@@ -922,6 +971,9 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
substream->pid = get_pid(task_pid(current));
pstr->substream_opened++;
*rsubstream = substream;
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+ substream->xrun_counter = 0;
+#endif /* CONFIG_SND_PCM_XRUN_DEBUG */
return 0;
}
@@ -934,26 +986,31 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
runtime = substream->runtime;
if (runtime->private_free != NULL)
runtime->private_free(runtime);
- snd_free_pages((void*)runtime->status,
+ free_pages_exact(runtime->status,
PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)));
- snd_free_pages((void*)runtime->control,
+ free_pages_exact(runtime->control,
PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)));
kfree(runtime->hw_constraints.rules);
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- if (runtime->hwptr_log)
- kfree(runtime->hwptr_log);
-#endif
+ /* Avoid concurrent access to runtime via PCM timer interface */
+ if (substream->timer) {
+ scoped_guard(spinlock_irq, &substream->timer->lock)
+ substream->runtime = NULL;
+ } else {
+ substream->runtime = NULL;
+ }
+ mutex_destroy(&runtime->buffer_mutex);
+ snd_fasync_free(runtime->fasync);
kfree(runtime);
- substream->runtime = NULL;
put_pid(substream->pid);
substream->pid = NULL;
substream->pstr->substream_opened--;
}
-static ssize_t show_pcm_class(struct device *dev,
+static ssize_t pcm_class_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct snd_pcm *pcm;
+ struct snd_pcm_str *pstr = dev_get_drvdata(dev);
+ struct snd_pcm *pcm = pstr->pcm;
const char *str;
static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = {
[SNDRV_PCM_CLASS_GENERIC] = "generic",
@@ -962,114 +1019,120 @@ static ssize_t show_pcm_class(struct device *dev,
[SNDRV_PCM_CLASS_DIGITIZER] = "digitizer",
};
- if (! (pcm = dev_get_drvdata(dev)) ||
- pcm->dev_class > SNDRV_PCM_CLASS_LAST)
+ if (pcm->dev_class > SNDRV_PCM_CLASS_LAST)
str = "none";
else
str = strs[pcm->dev_class];
- return snprintf(buf, PAGE_SIZE, "%s\n", str);
+ return sysfs_emit(buf, "%s\n", str);
}
-static struct device_attribute pcm_attrs =
- __ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL);
+static DEVICE_ATTR_RO(pcm_class);
+static struct attribute *pcm_dev_attrs[] = {
+ &dev_attr_pcm_class.attr,
+ NULL
+};
+
+static const struct attribute_group pcm_dev_attr_group = {
+ .attrs = pcm_dev_attrs,
+};
+
+static const struct attribute_group *pcm_dev_attr_groups[] = {
+ &pcm_dev_attr_group,
+ NULL
+};
static int snd_pcm_dev_register(struct snd_device *device)
{
int cidx, err;
struct snd_pcm_substream *substream;
- struct snd_pcm_notify *notify;
- char str[16];
struct snd_pcm *pcm;
- struct device *dev;
if (snd_BUG_ON(!device || !device->device_data))
return -ENXIO;
pcm = device->device_data;
- mutex_lock(&register_mutex);
+
+ guard(mutex)(&register_mutex);
err = snd_pcm_add(pcm);
- if (err) {
- mutex_unlock(&register_mutex);
+ if (err)
return err;
- }
for (cidx = 0; cidx < 2; cidx++) {
int devtype = -1;
if (pcm->streams[cidx].substream == NULL)
continue;
switch (cidx) {
case SNDRV_PCM_STREAM_PLAYBACK:
- sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);
devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
break;
case SNDRV_PCM_STREAM_CAPTURE:
- sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);
devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
break;
}
- /* device pointer to use, pcm->dev takes precedence if
- * it is assigned, otherwise fall back to card's device
- * if possible */
- dev = pcm->dev;
- if (!dev)
- dev = snd_card_get_device_link(pcm->card);
/* register pcm */
- err = snd_register_device_for_dev(devtype, pcm->card,
- pcm->device,
- &snd_pcm_f_ops[cidx],
- pcm, str, dev);
+ err = snd_register_device(devtype, pcm->card, pcm->device,
+ &snd_pcm_f_ops[cidx], pcm,
+ pcm->streams[cidx].dev);
if (err < 0) {
- list_del(&pcm->list);
- mutex_unlock(&register_mutex);
+ list_del_init(&pcm->list);
return err;
}
- snd_add_device_sysfs_file(devtype, pcm->card, pcm->device,
- &pcm_attrs);
+
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
snd_pcm_timer_init(substream);
}
- list_for_each_entry(notify, &snd_pcm_notify_list, list)
- notify->n_register(pcm);
-
- mutex_unlock(&register_mutex);
- return 0;
+ pcm_call_notify(pcm, n_register);
+ return err;
}
static int snd_pcm_dev_disconnect(struct snd_device *device)
{
struct snd_pcm *pcm = device->device_data;
- struct snd_pcm_notify *notify;
struct snd_pcm_substream *substream;
- int cidx, devtype;
-
- mutex_lock(&register_mutex);
- if (list_empty(&pcm->list))
- goto unlock;
+ int cidx;
+ guard(mutex)(&register_mutex);
+ guard(mutex)(&pcm->open_mutex);
+ wake_up(&pcm->open_wait);
list_del_init(&pcm->list);
- for (cidx = 0; cidx < 2; cidx++)
- for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
- if (substream->runtime)
- substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED;
- list_for_each_entry(notify, &snd_pcm_notify_list, list) {
- notify->n_disconnect(pcm);
+
+ for_each_pcm_substream(pcm, cidx, substream) {
+ snd_pcm_stream_lock_irq(substream);
+ if (substream->runtime) {
+ if (snd_pcm_running(substream))
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
+ /* to be sure, set the state unconditionally */
+ __snd_pcm_set_state(substream->runtime,
+ SNDRV_PCM_STATE_DISCONNECTED);
+ wake_up(&substream->runtime->sleep);
+ wake_up(&substream->runtime->tsleep);
+ }
+ snd_pcm_stream_unlock_irq(substream);
}
+
+ for_each_pcm_substream(pcm, cidx, substream)
+ snd_pcm_sync_stop(substream, false);
+
+ pcm_call_notify(pcm, n_disconnect);
for (cidx = 0; cidx < 2; cidx++) {
- devtype = -1;
- switch (cidx) {
- case SNDRV_PCM_STREAM_PLAYBACK:
- devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
- break;
- case SNDRV_PCM_STREAM_CAPTURE:
- devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
- break;
- }
- snd_unregister_device(devtype, pcm->card, pcm->device);
+ if (pcm->streams[cidx].dev)
+ snd_unregister_device(pcm->streams[cidx].dev);
+ free_chmap(&pcm->streams[cidx]);
}
- unlock:
- mutex_unlock(&register_mutex);
return 0;
}
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
+/**
+ * snd_pcm_notify - Add/remove the notify list
+ * @notify: PCM notify list
+ * @nfree: 0 = register, 1 = unregister
+ *
+ * This adds the given notifier to the global list so that the callback is
+ * called for each registered PCM devices. This exists only for PCM OSS
+ * emulation, so far.
+ *
+ * Return: zero if successful, or a negative error code
+ */
int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
{
struct snd_pcm *pcm;
@@ -1079,7 +1142,7 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
!notify->n_unregister ||
!notify->n_disconnect))
return -EINVAL;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
if (nfree) {
list_del(&notify->list);
list_for_each_entry(pcm, &snd_pcm_devices, list)
@@ -1089,13 +1152,12 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
list_for_each_entry(pcm, &snd_pcm_devices, list)
notify->n_register(pcm);
}
- mutex_unlock(&register_mutex);
return 0;
}
-
EXPORT_SYMBOL(snd_pcm_notify);
+#endif /* CONFIG_SND_PCM_OSS */
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* Info interface
*/
@@ -1105,7 +1167,7 @@ static void snd_pcm_proc_read(struct snd_info_entry *entry,
{
struct snd_pcm *pcm;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
list_for_each_entry(pcm, &snd_pcm_devices, list) {
snd_iprintf(buffer, "%02i-%02i: %s : %s",
pcm->card->number, pcm->device, pcm->id, pcm->name);
@@ -1117,7 +1179,6 @@ static void snd_pcm_proc_read(struct snd_info_entry *entry,
pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count);
snd_iprintf(buffer, "\n");
}
- mutex_unlock(&register_mutex);
}
static struct snd_info_entry *snd_pcm_proc_entry;
@@ -1126,7 +1187,8 @@ static void snd_pcm_proc_init(void)
{
struct snd_info_entry *entry;
- if ((entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL)) != NULL) {
+ entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL);
+ if (entry) {
snd_info_set_text_ops(entry, NULL, snd_pcm_proc_read);
if (snd_info_register(entry) < 0) {
snd_info_free_entry(entry);
@@ -1141,10 +1203,10 @@ static void snd_pcm_proc_done(void)
snd_info_free_entry(snd_pcm_proc_entry);
}
-#else /* !CONFIG_PROC_FS */
+#else /* !CONFIG_SND_PROC_FS */
#define snd_pcm_proc_init()
#define snd_pcm_proc_done()
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
/*
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index 5fb2e28e796f..54eb9bd8eb21 100644
--- a/sound/core/pcm_compat.c
+++ b/sound/core/pcm_compat.c
@@ -1,21 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 32bit -> 64bit ioctl wrapper for PCM API
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/* This file included from pcm_native.c */
@@ -27,17 +13,14 @@ static int snd_pcm_ioctl_delay_compat(struct snd_pcm_substream *substream,
s32 __user *src)
{
snd_pcm_sframes_t delay;
- mm_segment_t fs;
int err;
- fs = snd_enter_user();
err = snd_pcm_delay(substream, &delay);
- snd_leave_user(fs);
- if (err < 0)
+ if (err)
return err;
if (put_user(delay, src))
return -EFAULT;
- return err;
+ return 0;
}
static int snd_pcm_ioctl_rewind_compat(struct snd_pcm_substream *substream,
@@ -48,10 +31,7 @@ static int snd_pcm_ioctl_rewind_compat(struct snd_pcm_substream *substream,
if (get_user(frames, src))
return -EFAULT;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- err = snd_pcm_playback_rewind(substream, frames);
- else
- err = snd_pcm_capture_rewind(substream, frames);
+ err = snd_pcm_rewind(substream, frames);
if (put_user(err, src))
return -EFAULT;
return err < 0 ? err : 0;
@@ -65,10 +45,7 @@ static int snd_pcm_ioctl_forward_compat(struct snd_pcm_substream *substream,
if (get_user(frames, src))
return -EFAULT;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- err = snd_pcm_playback_forward(substream, frames);
- else
- err = snd_pcm_capture_forward(substream, frames);
+ err = snd_pcm_forward(substream, frames);
if (put_user(err, src))
return -EFAULT;
return err < 0 ? err : 0;
@@ -101,22 +78,11 @@ struct snd_pcm_sw_params32 {
u32 silence_threshold;
u32 silence_size;
u32 boundary;
- unsigned char reserved[64];
+ u32 proto;
+ u32 tstamp_type;
+ unsigned char reserved[56];
};
-/* recalcuate the boundary within 32bit */
-static snd_pcm_uframes_t recalculate_boundary(struct snd_pcm_runtime *runtime)
-{
- snd_pcm_uframes_t boundary;
-
- if (! runtime->buffer_size)
- return 0;
- boundary = runtime->buffer_size;
- while (boundary * 2 <= 0x7fffffffUL - runtime->buffer_size)
- boundary *= 2;
- return boundary;
-}
-
static int snd_pcm_ioctl_sw_params_compat(struct snd_pcm_substream *substream,
struct snd_pcm_sw_params32 __user *src)
{
@@ -133,7 +99,9 @@ static int snd_pcm_ioctl_sw_params_compat(struct snd_pcm_substream *substream,
get_user(params.start_threshold, &src->start_threshold) ||
get_user(params.stop_threshold, &src->stop_threshold) ||
get_user(params.silence_threshold, &src->silence_threshold) ||
- get_user(params.silence_size, &src->silence_size))
+ get_user(params.silence_size, &src->silence_size) ||
+ get_user(params.tstamp_type, &src->tstamp_type) ||
+ get_user(params.proto, &src->proto))
return -EFAULT;
/*
* Check silent_size parameter. Since we have 64bit boundary,
@@ -179,43 +147,84 @@ static int snd_pcm_ioctl_channel_info_compat(struct snd_pcm_substream *substream
return err;
}
-struct snd_pcm_status32 {
- s32 state;
- struct compat_timespec trigger_tstamp;
- struct compat_timespec tstamp;
+#ifdef CONFIG_X86_X32_ABI
+/* X32 ABI has the same struct as x86-64 for snd_pcm_channel_info */
+static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream,
+ struct snd_pcm_channel_info __user *src);
+#define snd_pcm_ioctl_channel_info_x32(s, p) \
+ snd_pcm_channel_info_user(s, p)
+#endif /* CONFIG_X86_X32_ABI */
+
+struct compat_snd_pcm_status64 {
+ snd_pcm_state_t state;
+ u8 rsvd[4]; /* alignment */
+ s64 trigger_tstamp_sec;
+ s64 trigger_tstamp_nsec;
+ s64 tstamp_sec;
+ s64 tstamp_nsec;
u32 appl_ptr;
u32 hw_ptr;
s32 delay;
u32 avail;
u32 avail_max;
u32 overrange;
- s32 suspended_state;
- unsigned char reserved[60];
-} __attribute__((packed));
-
-
-static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
- struct snd_pcm_status32 __user *src)
+ snd_pcm_state_t suspended_state;
+ u32 audio_tstamp_data;
+ s64 audio_tstamp_sec;
+ s64 audio_tstamp_nsec;
+ s64 driver_tstamp_sec;
+ s64 driver_tstamp_nsec;
+ u32 audio_tstamp_accuracy;
+ unsigned char reserved[52-4*sizeof(s64)];
+} __packed;
+
+static int snd_pcm_status_user_compat64(struct snd_pcm_substream *substream,
+ struct compat_snd_pcm_status64 __user *src,
+ bool ext)
{
- struct snd_pcm_status status;
+ struct snd_pcm_status64 status;
+ struct compat_snd_pcm_status64 compat_status64;
int err;
- err = snd_pcm_status(substream, &status);
+ memset(&status, 0, sizeof(status));
+ memset(&compat_status64, 0, sizeof(compat_status64));
+ /*
+ * with extension, parameters are read/write,
+ * get audio_tstamp_data from user,
+ * ignore rest of status structure
+ */
+ if (ext && get_user(status.audio_tstamp_data,
+ (u32 __user *)(&src->audio_tstamp_data)))
+ return -EFAULT;
+ err = snd_pcm_status64(substream, &status);
if (err < 0)
return err;
- if (put_user(status.state, &src->state) ||
- put_user(status.trigger_tstamp.tv_sec, &src->trigger_tstamp.tv_sec) ||
- put_user(status.trigger_tstamp.tv_nsec, &src->trigger_tstamp.tv_nsec) ||
- put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) ||
- put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) ||
- put_user(status.appl_ptr, &src->appl_ptr) ||
- put_user(status.hw_ptr, &src->hw_ptr) ||
- put_user(status.delay, &src->delay) ||
- put_user(status.avail, &src->avail) ||
- put_user(status.avail_max, &src->avail_max) ||
- put_user(status.overrange, &src->overrange) ||
- put_user(status.suspended_state, &src->suspended_state))
+ if (clear_user(src, sizeof(*src)))
+ return -EFAULT;
+
+ compat_status64 = (struct compat_snd_pcm_status64) {
+ .state = status.state,
+ .trigger_tstamp_sec = status.trigger_tstamp_sec,
+ .trigger_tstamp_nsec = status.trigger_tstamp_nsec,
+ .tstamp_sec = status.tstamp_sec,
+ .tstamp_nsec = status.tstamp_nsec,
+ .appl_ptr = status.appl_ptr,
+ .hw_ptr = status.hw_ptr,
+ .delay = status.delay,
+ .avail = status.avail,
+ .avail_max = status.avail_max,
+ .overrange = status.overrange,
+ .suspended_state = status.suspended_state,
+ .audio_tstamp_data = status.audio_tstamp_data,
+ .audio_tstamp_sec = status.audio_tstamp_sec,
+ .audio_tstamp_nsec = status.audio_tstamp_nsec,
+ .driver_tstamp_sec = status.audio_tstamp_sec,
+ .driver_tstamp_nsec = status.audio_tstamp_nsec,
+ .audio_tstamp_accuracy = status.audio_tstamp_accuracy,
+ };
+
+ if (copy_to_user(src, &compat_status64, sizeof(compat_status64)))
return -EFAULT;
return err;
@@ -226,37 +235,41 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
int refine,
struct snd_pcm_hw_params32 __user *data32)
{
- struct snd_pcm_hw_params *data;
+ struct snd_pcm_hw_params *data __free(kfree) = NULL;
struct snd_pcm_runtime *runtime;
int err;
- if (! (runtime = substream->runtime))
+ runtime = substream->runtime;
+ if (!runtime)
return -ENOTTY;
- /* only fifo_size is different, so just copy all */
- data = memdup_user(data32, sizeof(*data32));
- if (IS_ERR(data))
- return PTR_ERR(data);
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* only fifo_size (RO from userspace) is different, so just copy all */
+ if (copy_from_user(data, data32, sizeof(*data32)))
+ return -EFAULT;
- if (refine)
+ if (refine) {
err = snd_pcm_hw_refine(substream, data);
- else
+ if (err < 0)
+ return err;
+ err = fixup_unreferenced_params(substream, data);
+ } else {
err = snd_pcm_hw_params(substream, data);
+ }
if (err < 0)
- goto error;
+ return err;
if (copy_to_user(data32, data, sizeof(*data32)) ||
- put_user(data->fifo_size, &data32->fifo_size)) {
- err = -EFAULT;
- goto error;
- }
+ put_user(data->fifo_size, &data32->fifo_size))
+ return -EFAULT;
if (! refine) {
unsigned int new_boundary = recalculate_boundary(runtime);
if (new_boundary)
runtime->boundary = new_boundary;
}
- error:
- kfree(data);
return err;
}
@@ -280,7 +293,7 @@ static int snd_pcm_ioctl_xferi_compat(struct snd_pcm_substream *substream,
return -ENOTTY;
if (substream->stream != dir)
return -EINVAL;
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
if (get_user(buf, &data32->buf) ||
@@ -319,30 +332,31 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
compat_caddr_t buf;
compat_caddr_t __user *bufptr;
u32 frames;
- void __user **bufs;
+ void __user **bufs __free(kfree) = NULL;
int err, ch, i;
if (! substream->runtime)
return -ENOTTY;
if (substream->stream != dir)
return -EINVAL;
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
- if ((ch = substream->runtime->channels) > 128)
+ ch = substream->runtime->channels;
+ if (ch > 128)
return -EINVAL;
if (get_user(buf, &data32->bufs) ||
get_user(frames, &data32->frames))
return -EFAULT;
bufptr = compat_ptr(buf);
- bufs = kmalloc(sizeof(void __user *) * ch, GFP_KERNEL);
+ bufs = kmalloc_array(ch, sizeof(void __user *), GFP_KERNEL);
if (bufs == NULL)
return -ENOMEM;
for (i = 0; i < ch; i++) {
u32 ptr;
- if (get_user(ptr, bufptr)) {
- kfree(bufs);
+ if (get_user(ptr, bufptr))
return -EFAULT;
- }
- bufs[ch] = compat_ptr(ptr);
+ bufs[i] = compat_ptr(ptr);
bufptr++;
}
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
@@ -351,40 +365,44 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
err = snd_pcm_lib_readv(substream, bufs, frames);
if (err >= 0) {
if (put_user(err, &data32->result))
- err = -EFAULT;
+ return -EFAULT;
}
- kfree(bufs);
return err;
}
-
-struct snd_pcm_mmap_status32 {
- s32 state;
+#ifdef CONFIG_X86_X32_ABI
+/* X32 ABI has 64bit timespec and 64bit alignment */
+struct snd_pcm_mmap_status_x32 {
+ snd_pcm_state_t state;
s32 pad1;
u32 hw_ptr;
- struct compat_timespec tstamp;
- s32 suspended_state;
-} __attribute__((packed));
-
-struct snd_pcm_mmap_control32 {
+ u32 pad2; /* alignment */
+ struct __snd_timespec64 tstamp;
+ snd_pcm_state_t suspended_state;
+ s32 pad3;
+ struct __snd_timespec64 audio_tstamp;
+} __packed;
+
+struct snd_pcm_mmap_control_x32 {
u32 appl_ptr;
u32 avail_min;
};
-struct snd_pcm_sync_ptr32 {
+struct snd_pcm_sync_ptr_x32 {
u32 flags;
+ u32 rsvd; /* alignment */
union {
- struct snd_pcm_mmap_status32 status;
+ struct snd_pcm_mmap_status_x32 status;
unsigned char reserved[64];
} s;
union {
- struct snd_pcm_mmap_control32 control;
+ struct snd_pcm_mmap_control_x32 control;
unsigned char reserved[64];
} c;
-} __attribute__((packed));
+} __packed;
-static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
- struct snd_pcm_sync_ptr32 __user *src)
+static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream,
+ struct snd_pcm_sync_ptr_x32 __user *src)
{
struct snd_pcm_runtime *runtime = substream->runtime;
volatile struct snd_pcm_mmap_status *status;
@@ -398,9 +416,7 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
if (snd_BUG_ON(!runtime))
return -EINVAL;
- if (get_user(sflags, &src->flags) ||
- get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
- get_user(scontrol.avail_min, &src->c.control.avail_min))
+ if (snd_pcm_sync_ptr_get_user(sflags, scontrol, src))
return -EFAULT;
if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
err = snd_pcm_hwsync(substream);
@@ -410,35 +426,102 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
status = runtime->status;
control = runtime->control;
boundary = recalculate_boundary(runtime);
- if (! boundary)
+ if (!boundary)
boundary = 0x7fffffff;
- snd_pcm_stream_lock_irq(substream);
- /* FIXME: we should consider the boundary for the sync from app */
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ /* FIXME: we should consider the boundary for the sync from app */
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
+ control->appl_ptr = scontrol.appl_ptr;
+ else
+ scontrol.appl_ptr = control->appl_ptr % boundary;
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
+ control->avail_min = scontrol.avail_min;
+ else
+ scontrol.avail_min = control->avail_min;
+ sstatus.state = status->state;
+ sstatus.hw_ptr = status->hw_ptr % boundary;
+ sstatus.tstamp = status->tstamp;
+ sstatus.suspended_state = status->suspended_state;
+ sstatus.audio_tstamp = status->audio_tstamp;
+ }
if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
- control->appl_ptr = scontrol.appl_ptr;
- else
- scontrol.appl_ptr = control->appl_ptr % boundary;
- if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
- control->avail_min = scontrol.avail_min;
- else
- scontrol.avail_min = control->avail_min;
- sstatus.state = status->state;
- sstatus.hw_ptr = status->hw_ptr % boundary;
- sstatus.tstamp = status->tstamp;
- sstatus.suspended_state = status->suspended_state;
- snd_pcm_stream_unlock_irq(substream);
- if (put_user(sstatus.state, &src->s.status.state) ||
- put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
- put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp.tv_sec) ||
- put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp.tv_nsec) ||
- put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
- put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
- put_user(scontrol.avail_min, &src->c.control.avail_min))
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
+ if (snd_pcm_sync_ptr_put_user(sstatus, scontrol, src))
return -EFAULT;
return 0;
}
+#endif /* CONFIG_X86_X32_ABI */
+
+#ifdef __BIG_ENDIAN
+typedef char __pad_before_u32[4];
+typedef char __pad_after_u32[0];
+#else
+typedef char __pad_before_u32[0];
+typedef char __pad_after_u32[4];
+#endif
+
+/* PCM 2.0.15 API definition had a bug in mmap control; it puts the avail_min
+ * at the wrong offset due to a typo in padding type.
+ * The bug hits only 32bit.
+ * A workaround for incorrect read/write is needed only in 32bit compat mode.
+ */
+struct __snd_pcm_mmap_control64_buggy {
+ __pad_before_u32 __pad1;
+ __u32 appl_ptr;
+ __pad_before_u32 __pad2; /* SiC! here is the bug */
+ __pad_before_u32 __pad3;
+ __u32 avail_min;
+ __pad_after_uframe __pad4;
+};
+static int snd_pcm_ioctl_sync_ptr_buggy(struct snd_pcm_substream *substream,
+ struct snd_pcm_sync_ptr __user *_sync_ptr)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_sync_ptr sync_ptr;
+ struct __snd_pcm_mmap_control64_buggy *sync_cp;
+ volatile struct snd_pcm_mmap_status *status;
+ volatile struct snd_pcm_mmap_control *control;
+ int err;
+
+ memset(&sync_ptr, 0, sizeof(sync_ptr));
+ sync_cp = (struct __snd_pcm_mmap_control64_buggy *)&sync_ptr.c.control;
+ if (get_user(sync_ptr.flags, (unsigned __user *)&(_sync_ptr->flags)))
+ return -EFAULT;
+ if (copy_from_user(sync_cp, &(_sync_ptr->c.control), sizeof(*sync_cp)))
+ return -EFAULT;
+ status = runtime->status;
+ control = runtime->control;
+ if (sync_ptr.flags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
+ err = snd_pcm_hwsync(substream);
+ if (err < 0)
+ return err;
+ }
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) {
+ err = pcm_lib_apply_appl_ptr(substream, sync_cp->appl_ptr);
+ if (err < 0)
+ return err;
+ } else {
+ sync_cp->appl_ptr = control->appl_ptr;
+ }
+ if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
+ control->avail_min = sync_cp->avail_min;
+ else
+ sync_cp->avail_min = control->avail_min;
+ sync_ptr.s.status.state = status->state;
+ sync_ptr.s.status.hw_ptr = status->hw_ptr;
+ sync_ptr.s.status.tstamp = status->tstamp;
+ sync_ptr.s.status.suspended_state = status->suspended_state;
+ sync_ptr.s.status.audio_tstamp = status->audio_tstamp;
+ }
+ if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL))
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
+ if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr)))
+ return -EFAULT;
+ return 0;
+}
/*
*/
@@ -446,7 +529,8 @@ enum {
SNDRV_PCM_IOCTL_HW_REFINE32 = _IOWR('A', 0x10, struct snd_pcm_hw_params32),
SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct snd_pcm_hw_params32),
SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct snd_pcm_sw_params32),
- SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct snd_pcm_status32),
+ SNDRV_PCM_IOCTL_STATUS_COMPAT32 = _IOR('A', 0x20, struct snd_pcm_status32),
+ SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT32 = _IOWR('A', 0x24, struct snd_pcm_status32),
SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32),
SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32),
SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32),
@@ -455,8 +539,12 @@ enum {
SNDRV_PCM_IOCTL_READI_FRAMES32 = _IOR('A', 0x51, struct snd_xferi32),
SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct snd_xfern32),
SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct snd_xfern32),
- SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr32),
-
+ SNDRV_PCM_IOCTL_STATUS_COMPAT64 = _IOR('A', 0x20, struct compat_snd_pcm_status64),
+ SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT64 = _IOWR('A', 0x24, struct compat_snd_pcm_status64),
+#ifdef CONFIG_X86_X32_ABI
+ SNDRV_PCM_IOCTL_CHANNEL_INFO_X32 = _IOR('A', 0x32, struct snd_pcm_channel_info),
+ SNDRV_PCM_IOCTL_SYNC_PTR_X32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr_x32),
+#endif /* CONFIG_X86_X32_ABI */
};
static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
@@ -474,8 +562,8 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
/*
* When PCM is used on 32bit mode, we need to disable
- * mmap of PCM status/control records because of the size
- * incompatibility.
+ * mmap of the old PCM status/control records because
+ * of the size incompatibility.
*/
pcm_file->no_compat_mmap = 1;
@@ -484,6 +572,7 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
case SNDRV_PCM_IOCTL_INFO:
case SNDRV_PCM_IOCTL_TSTAMP:
case SNDRV_PCM_IOCTL_TTSTAMP:
+ case SNDRV_PCM_IOCTL_USER_PVERSION:
case SNDRV_PCM_IOCTL_HWSYNC:
case SNDRV_PCM_IOCTL_PREPARE:
case SNDRV_PCM_IOCTL_RESET:
@@ -496,20 +585,24 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
case SNDRV_PCM_IOCTL_XRUN:
case SNDRV_PCM_IOCTL_LINK:
case SNDRV_PCM_IOCTL_UNLINK:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- return snd_pcm_playback_ioctl1(file, substream, cmd, argp);
- else
- return snd_pcm_capture_ioctl1(file, substream, cmd, argp);
+ case __SNDRV_PCM_IOCTL_SYNC_PTR32:
+ return snd_pcm_common_ioctl(file, substream, cmd, argp);
+ case __SNDRV_PCM_IOCTL_SYNC_PTR64:
+#ifdef CONFIG_X86_X32_ABI
+ if (in_x32_syscall())
+ return snd_pcm_ioctl_sync_ptr_x32(substream, argp);
+#endif /* CONFIG_X86_X32_ABI */
+ return snd_pcm_ioctl_sync_ptr_buggy(substream, argp);
case SNDRV_PCM_IOCTL_HW_REFINE32:
return snd_pcm_ioctl_hw_params_compat(substream, 1, argp);
case SNDRV_PCM_IOCTL_HW_PARAMS32:
return snd_pcm_ioctl_hw_params_compat(substream, 0, argp);
case SNDRV_PCM_IOCTL_SW_PARAMS32:
return snd_pcm_ioctl_sw_params_compat(substream, argp);
- case SNDRV_PCM_IOCTL_STATUS32:
- return snd_pcm_status_user_compat(substream, argp);
- case SNDRV_PCM_IOCTL_SYNC_PTR32:
- return snd_pcm_ioctl_sync_ptr_compat(substream, argp);
+ case SNDRV_PCM_IOCTL_STATUS_COMPAT32:
+ return snd_pcm_status_user32(substream, argp, false);
+ case SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT32:
+ return snd_pcm_status_user32(substream, argp, true);
case SNDRV_PCM_IOCTL_CHANNEL_INFO32:
return snd_pcm_ioctl_channel_info_compat(substream, argp);
case SNDRV_PCM_IOCTL_WRITEI_FRAMES32:
@@ -526,6 +619,14 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
return snd_pcm_ioctl_rewind_compat(substream, argp);
case SNDRV_PCM_IOCTL_FORWARD32:
return snd_pcm_ioctl_forward_compat(substream, argp);
+ case SNDRV_PCM_IOCTL_STATUS_COMPAT64:
+ return snd_pcm_status_user_compat64(substream, argp, false);
+ case SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT64:
+ return snd_pcm_status_user_compat64(substream, argp, true);
+#ifdef CONFIG_X86_X32_ABI
+ case SNDRV_PCM_IOCTL_CHANNEL_INFO_X32:
+ return snd_pcm_ioctl_channel_info_x32(substream, argp);
+#endif /* CONFIG_X86_X32_ABI */
}
return -ENOIOCTLCMD;
diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c
new file mode 100644
index 000000000000..f0c17503df42
--- /dev/null
+++ b/sound/core/pcm_dmaengine.c
@@ -0,0 +1,477 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2012, Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Based on:
+ * imx-pcm-dma-mx2.c, Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
+ * mxs-pcm.c, Copyright (C) 2011 Freescale Semiconductor, Inc.
+ * ep93xx-pcm.c, Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Copyright (C) 2006 Applied Data Systems
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/dmaengine.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <sound/dmaengine_pcm.h>
+
+struct dmaengine_pcm_runtime_data {
+ struct dma_chan *dma_chan;
+ dma_cookie_t cookie;
+
+ unsigned int pos;
+};
+
+static inline struct dmaengine_pcm_runtime_data *substream_to_prtd(
+ const struct snd_pcm_substream *substream)
+{
+ return substream->runtime->private_data;
+}
+
+struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+
+ return prtd->dma_chan;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_get_chan);
+
+/**
+ * snd_hwparams_to_dma_slave_config - Convert hw_params to dma_slave_config
+ * @substream: PCM substream
+ * @params: hw_params
+ * @slave_config: DMA slave config
+ *
+ * This function can be used to initialize a dma_slave_config from a substream
+ * and hw_params in a dmaengine based PCM driver implementation.
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream,
+ const struct snd_pcm_hw_params *params,
+ struct dma_slave_config *slave_config)
+{
+ enum dma_slave_buswidth buswidth;
+ int bits;
+
+ bits = params_physical_width(params);
+ if (bits < 8 || bits > 64)
+ return -EINVAL;
+ else if (bits == 8)
+ buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ else if (bits == 16)
+ buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ else if (bits == 24)
+ buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES;
+ else if (bits <= 32)
+ buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ else
+ buswidth = DMA_SLAVE_BUSWIDTH_8_BYTES;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ slave_config->direction = DMA_MEM_TO_DEV;
+ slave_config->dst_addr_width = buswidth;
+ } else {
+ slave_config->direction = DMA_DEV_TO_MEM;
+ slave_config->src_addr_width = buswidth;
+ }
+
+ slave_config->device_fc = false;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config);
+
+/**
+ * snd_dmaengine_pcm_set_config_from_dai_data() - Initializes a dma slave config
+ * using DAI DMA data.
+ * @substream: PCM substream
+ * @dma_data: DAI DMA data
+ * @slave_config: DMA slave configuration
+ *
+ * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width
+ * fields of the DMA slave config from the same fields of the DAI DMA
+ * data struct. The src and dst fields will be initialized depending on the
+ * direction of the substream. If the substream is a playback stream the dst
+ * fields will be initialized, if it is a capture stream the src fields will be
+ * initialized. The {dst,src}_addr_width field will only be initialized if the
+ * SND_DMAENGINE_PCM_DAI_FLAG_PACK flag is set or if the addr_width field of
+ * the DAI DMA data struct is not equal to DMA_SLAVE_BUSWIDTH_UNDEFINED. If
+ * both conditions are met the latter takes priority.
+ */
+void snd_dmaengine_pcm_set_config_from_dai_data(
+ const struct snd_pcm_substream *substream,
+ const struct snd_dmaengine_dai_dma_data *dma_data,
+ struct dma_slave_config *slave_config)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ slave_config->dst_addr = dma_data->addr;
+ slave_config->dst_maxburst = dma_data->maxburst;
+ slave_config->dst_port_window_size = dma_data->port_window_size;
+ if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
+ slave_config->dst_addr_width =
+ DMA_SLAVE_BUSWIDTH_UNDEFINED;
+ if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED)
+ slave_config->dst_addr_width = dma_data->addr_width;
+ } else {
+ slave_config->src_addr = dma_data->addr;
+ slave_config->src_maxburst = dma_data->maxburst;
+ slave_config->src_port_window_size = dma_data->port_window_size;
+ if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
+ slave_config->src_addr_width =
+ DMA_SLAVE_BUSWIDTH_UNDEFINED;
+ if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED)
+ slave_config->src_addr_width = dma_data->addr_width;
+ }
+
+ slave_config->peripheral_config = dma_data->peripheral_config;
+ slave_config->peripheral_size = dma_data->peripheral_size;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_set_config_from_dai_data);
+
+static void dmaengine_pcm_dma_complete(void *arg)
+{
+ unsigned int new_pos;
+ struct snd_pcm_substream *substream = arg;
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+
+ new_pos = prtd->pos + snd_pcm_lib_period_bytes(substream);
+ if (new_pos >= snd_pcm_lib_buffer_bytes(substream))
+ new_pos = 0;
+ prtd->pos = new_pos;
+
+ snd_pcm_period_elapsed(substream);
+}
+
+static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ struct dma_chan *chan = prtd->dma_chan;
+ struct dma_async_tx_descriptor *desc;
+ enum dma_transfer_direction direction;
+ unsigned long flags = DMA_CTRL_ACK;
+
+ direction = snd_pcm_substream_to_dma_direction(substream);
+
+ if (!substream->runtime->no_period_wakeup)
+ flags |= DMA_PREP_INTERRUPT;
+
+ prtd->pos = 0;
+ desc = dmaengine_prep_dma_cyclic(chan,
+ substream->runtime->dma_addr,
+ snd_pcm_lib_buffer_bytes(substream),
+ snd_pcm_lib_period_bytes(substream), direction, flags);
+
+ if (!desc)
+ return -ENOMEM;
+
+ desc->callback = dmaengine_pcm_dma_complete;
+ desc->callback_param = substream;
+ prtd->cookie = dmaengine_submit(desc);
+
+ return 0;
+}
+
+/**
+ * snd_dmaengine_pcm_trigger - dmaengine based PCM trigger implementation
+ * @substream: PCM substream
+ * @cmd: Trigger command
+ *
+ * This function can be used as the PCM trigger callback for dmaengine based PCM
+ * driver implementations.
+ *
+ * Return: 0 on success, a negative error code otherwise
+ */
+int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ ret = dmaengine_pcm_prepare_and_submit(substream);
+ if (ret)
+ return ret;
+ dma_async_issue_pending(prtd->dma_chan);
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ dmaengine_resume(prtd->dma_chan);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (runtime->info & SNDRV_PCM_INFO_PAUSE)
+ dmaengine_pause(prtd->dma_chan);
+ else
+ dmaengine_terminate_async(prtd->dma_chan);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ dmaengine_pause(prtd->dma_chan);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ dmaengine_terminate_async(prtd->dma_chan);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger);
+
+/**
+ * snd_dmaengine_pcm_pointer_no_residue - dmaengine based PCM pointer implementation
+ * @substream: PCM substream
+ *
+ * This function is deprecated and should not be used by new drivers, as its
+ * results may be unreliable.
+ *
+ * Return: PCM position in frames
+ */
+snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ return bytes_to_frames(substream->runtime, prtd->pos);
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue);
+
+/**
+ * snd_dmaengine_pcm_pointer - dmaengine based PCM pointer implementation
+ * @substream: PCM substream
+ *
+ * This function can be used as the PCM pointer callback for dmaengine based PCM
+ * driver implementations.
+ *
+ * Return: PCM position in frames
+ */
+snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct dma_tx_state state;
+ enum dma_status status;
+ unsigned int buf_size;
+ unsigned int pos = 0;
+
+ status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
+ if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) {
+ buf_size = snd_pcm_lib_buffer_bytes(substream);
+ if (state.residue > 0 && state.residue <= buf_size)
+ pos = buf_size - state.residue;
+
+ runtime->delay = bytes_to_frames(runtime,
+ state.in_flight_bytes);
+ }
+
+ return bytes_to_frames(runtime, pos);
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer);
+
+/**
+ * snd_dmaengine_pcm_request_channel - Request channel for the dmaengine PCM
+ * @filter_fn: Filter function used to request the DMA channel
+ * @filter_data: Data passed to the DMA filter function
+ *
+ * This function request a DMA channel for usage with dmaengine PCM.
+ *
+ * Return: NULL or the requested DMA channel
+ */
+struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn,
+ void *filter_data)
+{
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_CYCLIC, mask);
+
+ return dma_request_channel(mask, filter_fn, filter_data);
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel);
+
+/**
+ * snd_dmaengine_pcm_open - Open a dmaengine based PCM substream
+ * @substream: PCM substream
+ * @chan: DMA channel to use for data transfers
+ *
+ * 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 available to your pcm driver implementation.
+ *
+ * Return: 0 on success, a negative error code otherwise
+ */
+int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream,
+ struct dma_chan *chan)
+{
+ struct dmaengine_pcm_runtime_data *prtd;
+ int ret;
+
+ if (!chan)
+ return -ENXIO;
+
+ ret = snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
+
+ prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
+ if (!prtd)
+ return -ENOMEM;
+
+ prtd->dma_chan = chan;
+
+ substream->runtime->private_data = prtd;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open);
+
+int snd_dmaengine_pcm_sync_stop(struct snd_pcm_substream *substream)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
+ if (status != DMA_PAUSED)
+ dmaengine_synchronize(prtd->dma_chan);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_sync_stop);
+
+static void __snd_dmaengine_pcm_close(struct snd_pcm_substream *substream,
+ bool release_channel)
+{
+ struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
+ if (status == DMA_PAUSED)
+ dmaengine_terminate_async(prtd->dma_chan);
+
+ dmaengine_synchronize(prtd->dma_chan);
+ if (release_channel)
+ dma_release_channel(prtd->dma_chan);
+ kfree(prtd);
+}
+
+/**
+ * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream
+ * @substream: PCM substream
+ *
+ * Return: 0 on success, a negative error code otherwise
+ */
+int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream)
+{
+ __snd_dmaengine_pcm_close(substream, false);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close);
+
+/**
+ * snd_dmaengine_pcm_close_release_chan - Close a dmaengine based PCM
+ * substream and release channel
+ * @substream: PCM substream
+ *
+ * Releases the DMA channel associated with the PCM substream.
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream)
+{
+ __snd_dmaengine_pcm_close(substream, true);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan);
+
+/**
+ * snd_dmaengine_pcm_refine_runtime_hwparams - Refine runtime hw params
+ * @substream: PCM substream
+ * @dma_data: DAI DMA data
+ * @hw: PCM hw params
+ * @chan: DMA channel to use for data transfers
+ *
+ * This function will query DMA capability, then refine the pcm hardware
+ * parameters.
+ *
+ * Return: 0 on success, a negative error code otherwise
+ */
+int snd_dmaengine_pcm_refine_runtime_hwparams(
+ struct snd_pcm_substream *substream,
+ struct snd_dmaengine_dai_dma_data *dma_data,
+ struct snd_pcm_hardware *hw,
+ struct dma_chan *chan)
+{
+ struct dma_slave_caps dma_caps;
+ u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
+ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
+ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+ snd_pcm_format_t i;
+ int ret = 0;
+
+ if (!hw || !chan || !dma_data)
+ return -EINVAL;
+
+ ret = dma_get_slave_caps(chan, &dma_caps);
+ if (ret == 0) {
+ if (dma_caps.cmd_pause && dma_caps.cmd_resume)
+ hw->info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
+ if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
+ hw->info |= SNDRV_PCM_INFO_BATCH;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ addr_widths = dma_caps.dst_addr_widths;
+ else
+ addr_widths = dma_caps.src_addr_widths;
+ }
+
+ /*
+ * If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep
+ * hw.formats set to 0, meaning no restrictions are in place.
+ * In this case it's the responsibility of the DAI driver to
+ * provide the supported format information.
+ */
+ if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK))
+ /*
+ * Prepare formats mask for valid/allowed sample types. If the
+ * dma does not have support for the given physical word size,
+ * it needs to be masked out so user space can not use the
+ * format which produces corrupted audio.
+ * In case the dma driver does not implement the slave_caps the
+ * default assumption is that it supports 1, 2 and 4 bytes
+ * widths.
+ */
+ pcm_for_each_format(i) {
+ int bits = snd_pcm_format_physical_width(i);
+
+ /*
+ * Enable only samples with DMA supported physical
+ * widths
+ */
+ switch (bits) {
+ case 8:
+ case 16:
+ case 24:
+ case 32:
+ case 64:
+ if (addr_widths & (1 << (bits / 8)))
+ hw->formats |= pcm_format_to_bits(i);
+ break;
+ default:
+ /* Unsupported types */
+ break;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_refine_runtime_hwparams);
+
+MODULE_DESCRIPTION("PCM dmaengine helper APIs");
+MODULE_LICENSE("GPL");
diff --git a/sound/core/pcm_drm_eld.c b/sound/core/pcm_drm_eld.c
new file mode 100644
index 000000000000..cb2eebaac85f
--- /dev/null
+++ b/sound/core/pcm_drm_eld.c
@@ -0,0 +1,551 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PCM DRM helpers
+ */
+#include <linux/bitfield.h>
+#include <linux/export.h>
+#include <linux/hdmi.h>
+#include <linux/unaligned.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_eld.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_drm_eld.h>
+
+#define SAD0_CHANNELS_MASK GENMASK(2, 0) /* max number of channels - 1 */
+#define SAD0_FORMAT_MASK GENMASK(6, 3) /* audio format */
+
+#define SAD1_RATE_MASK GENMASK(6, 0) /* bitfield of supported rates */
+#define SAD1_RATE_32000_MASK BIT(0)
+#define SAD1_RATE_44100_MASK BIT(1)
+#define SAD1_RATE_48000_MASK BIT(2)
+#define SAD1_RATE_88200_MASK BIT(3)
+#define SAD1_RATE_96000_MASK BIT(4)
+#define SAD1_RATE_176400_MASK BIT(5)
+#define SAD1_RATE_192000_MASK BIT(6)
+
+static const unsigned int eld_rates[] = {
+ 32000,
+ 44100,
+ 48000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
+};
+
+static unsigned int map_rate_families(const u8 *sad,
+ unsigned int mask_32000,
+ unsigned int mask_44100,
+ unsigned int mask_48000)
+{
+ unsigned int rate_mask = 0;
+
+ if (sad[1] & SAD1_RATE_32000_MASK)
+ rate_mask |= mask_32000;
+ if (sad[1] & (SAD1_RATE_44100_MASK | SAD1_RATE_88200_MASK | SAD1_RATE_176400_MASK))
+ rate_mask |= mask_44100;
+ if (sad[1] & (SAD1_RATE_48000_MASK | SAD1_RATE_96000_MASK | SAD1_RATE_192000_MASK))
+ rate_mask |= mask_48000;
+ return rate_mask;
+}
+
+static unsigned int sad_rate_mask(const u8 *sad)
+{
+ switch (FIELD_GET(SAD0_FORMAT_MASK, sad[0])) {
+ case HDMI_AUDIO_CODING_TYPE_PCM:
+ return sad[1] & SAD1_RATE_MASK;
+ case HDMI_AUDIO_CODING_TYPE_AC3:
+ case HDMI_AUDIO_CODING_TYPE_DTS:
+ return map_rate_families(sad,
+ SAD1_RATE_32000_MASK,
+ SAD1_RATE_44100_MASK,
+ SAD1_RATE_48000_MASK);
+ case HDMI_AUDIO_CODING_TYPE_EAC3:
+ case HDMI_AUDIO_CODING_TYPE_DTS_HD:
+ case HDMI_AUDIO_CODING_TYPE_MLP:
+ return map_rate_families(sad,
+ 0,
+ SAD1_RATE_176400_MASK,
+ SAD1_RATE_192000_MASK);
+ default:
+ /* TODO adjust for other compressed formats as well */
+ return sad[1] & SAD1_RATE_MASK;
+ }
+}
+
+static unsigned int sad_max_channels(const u8 *sad)
+{
+ switch (FIELD_GET(SAD0_FORMAT_MASK, sad[0])) {
+ case HDMI_AUDIO_CODING_TYPE_PCM:
+ return 1 + FIELD_GET(SAD0_CHANNELS_MASK, sad[0]);
+ case HDMI_AUDIO_CODING_TYPE_AC3:
+ case HDMI_AUDIO_CODING_TYPE_DTS:
+ case HDMI_AUDIO_CODING_TYPE_EAC3:
+ return 2;
+ case HDMI_AUDIO_CODING_TYPE_DTS_HD:
+ case HDMI_AUDIO_CODING_TYPE_MLP:
+ return 8;
+ default:
+ /* TODO adjust for other compressed formats as well */
+ return 1 + FIELD_GET(SAD0_CHANNELS_MASK, sad[0]);
+ }
+}
+
+static int eld_limit_rates(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *r = hw_param_interval(params, rule->var);
+ const struct snd_interval *c;
+ unsigned int rate_mask = 7, i;
+ const u8 *sad, *eld = rule->private;
+
+ sad = drm_eld_sad(eld);
+ if (sad) {
+ c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3) {
+ unsigned max_channels = sad_max_channels(sad);
+
+ /*
+ * Exclude SADs which do not include the
+ * requested number of channels.
+ */
+ if (c->min <= max_channels)
+ rate_mask |= sad_rate_mask(sad);
+ }
+ }
+
+ return snd_interval_list(r, ARRAY_SIZE(eld_rates), eld_rates,
+ rate_mask);
+}
+
+static int eld_limit_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *c = hw_param_interval(params, rule->var);
+ const struct snd_interval *r;
+ struct snd_interval t = { .min = 1, .max = 2, .integer = 1, };
+ unsigned int i;
+ const u8 *sad, *eld = rule->private;
+
+ sad = drm_eld_sad(eld);
+ if (sad) {
+ unsigned int rate_mask = 0;
+
+ /* Convert the rate interval to a mask */
+ r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ for (i = 0; i < ARRAY_SIZE(eld_rates); i++)
+ if (r->min <= eld_rates[i] && r->max >= eld_rates[i])
+ rate_mask |= BIT(i);
+
+ for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3)
+ if (rate_mask & sad_rate_mask(sad))
+ t.max = max(t.max, sad_max_channels(sad));
+ }
+
+ return snd_interval_refine(c, &t);
+}
+
+int snd_pcm_hw_constraint_eld(struct snd_pcm_runtime *runtime, void *eld)
+{
+ int ret;
+
+ ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ eld_limit_rates, eld,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ eld_limit_channels, eld,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_hw_constraint_eld);
+
+#define SND_PRINT_RATES_ADVISED_BUFSIZE 80
+#define SND_PRINT_BITS_ADVISED_BUFSIZE 16
+#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
+
+static const char * const eld_connection_type_names[4] = {
+ "HDMI",
+ "DisplayPort",
+ "2-reserved",
+ "3-reserved"
+};
+
+static const char * const cea_audio_coding_type_names[] = {
+ /* 0 */ "undefined",
+ /* 1 */ "LPCM",
+ /* 2 */ "AC-3",
+ /* 3 */ "MPEG1",
+ /* 4 */ "MP3",
+ /* 5 */ "MPEG2",
+ /* 6 */ "AAC-LC",
+ /* 7 */ "DTS",
+ /* 8 */ "ATRAC",
+ /* 9 */ "DSD (One Bit Audio)",
+ /* 10 */ "E-AC-3/DD+ (Dolby Digital Plus)",
+ /* 11 */ "DTS-HD",
+ /* 12 */ "MLP (Dolby TrueHD)",
+ /* 13 */ "DST",
+ /* 14 */ "WMAPro",
+ /* 15 */ "HE-AAC",
+ /* 16 */ "HE-AACv2",
+ /* 17 */ "MPEG Surround",
+};
+
+static const char * const cea_speaker_allocation_names[] = {
+ /* 0 */ "FL/FR",
+ /* 1 */ "LFE",
+ /* 2 */ "FC",
+ /* 3 */ "RL/RR",
+ /* 4 */ "RC",
+ /* 5 */ "FLC/FRC",
+ /* 6 */ "RLC/RRC",
+ /* 7 */ "FLW/FRW",
+ /* 8 */ "FLH/FRH",
+ /* 9 */ "TC",
+ /* 10 */ "FCH",
+};
+
+/*
+ * SS1:SS0 index => sample size
+ */
+static const int cea_sample_sizes[4] = {
+ 0, /* 0: Refer to Stream Header */
+ ELD_PCM_BITS_16, /* 1: 16 bits */
+ ELD_PCM_BITS_20, /* 2: 20 bits */
+ ELD_PCM_BITS_24, /* 3: 24 bits */
+};
+
+/*
+ * SF2:SF1:SF0 index => sampling frequency
+ */
+static const int cea_sampling_frequencies[8] = {
+ 0, /* 0: Refer to Stream Header */
+ SNDRV_PCM_RATE_32000, /* 1: 32000Hz */
+ SNDRV_PCM_RATE_44100, /* 2: 44100Hz */
+ SNDRV_PCM_RATE_48000, /* 3: 48000Hz */
+ SNDRV_PCM_RATE_88200, /* 4: 88200Hz */
+ SNDRV_PCM_RATE_96000, /* 5: 96000Hz */
+ SNDRV_PCM_RATE_176400, /* 6: 176400Hz */
+ SNDRV_PCM_RATE_192000, /* 7: 192000Hz */
+};
+
+#define GRAB_BITS(buf, byte, lowbit, bits) \
+({ \
+ BUILD_BUG_ON(lowbit > 7); \
+ BUILD_BUG_ON(bits > 8); \
+ BUILD_BUG_ON(bits <= 0); \
+ \
+ (buf[byte] >> (lowbit)) & ((1 << (bits)) - 1); \
+})
+
+static void hdmi_update_short_audio_desc(struct device *dev,
+ struct snd_cea_sad *a,
+ const unsigned char *buf)
+{
+ int i;
+ int val;
+
+ val = GRAB_BITS(buf, 1, 0, 7);
+ a->rates = 0;
+ for (i = 0; i < 7; i++)
+ if (val & (1 << i))
+ a->rates |= cea_sampling_frequencies[i + 1];
+
+ a->channels = GRAB_BITS(buf, 0, 0, 3);
+ a->channels++;
+
+ a->sample_bits = 0;
+ a->max_bitrate = 0;
+
+ a->format = GRAB_BITS(buf, 0, 3, 4);
+ switch (a->format) {
+ case AUDIO_CODING_TYPE_REF_STREAM_HEADER:
+ dev_info(dev, "HDMI: audio coding type 0 not expected\n");
+ break;
+
+ case AUDIO_CODING_TYPE_LPCM:
+ val = GRAB_BITS(buf, 2, 0, 3);
+ for (i = 0; i < 3; i++)
+ if (val & (1 << i))
+ a->sample_bits |= cea_sample_sizes[i + 1];
+ break;
+
+ case AUDIO_CODING_TYPE_AC3:
+ case AUDIO_CODING_TYPE_MPEG1:
+ case AUDIO_CODING_TYPE_MP3:
+ case AUDIO_CODING_TYPE_MPEG2:
+ case AUDIO_CODING_TYPE_AACLC:
+ case AUDIO_CODING_TYPE_DTS:
+ case AUDIO_CODING_TYPE_ATRAC:
+ a->max_bitrate = GRAB_BITS(buf, 2, 0, 8);
+ a->max_bitrate *= 8000;
+ break;
+
+ case AUDIO_CODING_TYPE_SACD:
+ break;
+
+ case AUDIO_CODING_TYPE_EAC3:
+ break;
+
+ case AUDIO_CODING_TYPE_DTS_HD:
+ break;
+
+ case AUDIO_CODING_TYPE_MLP:
+ break;
+
+ case AUDIO_CODING_TYPE_DST:
+ break;
+
+ case AUDIO_CODING_TYPE_WMAPRO:
+ a->profile = GRAB_BITS(buf, 2, 0, 3);
+ break;
+
+ case AUDIO_CODING_TYPE_REF_CXT:
+ a->format = GRAB_BITS(buf, 2, 3, 5);
+ if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT ||
+ a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) {
+ dev_info(dev,
+ "HDMI: audio coding xtype %d not expected\n",
+ a->format);
+ a->format = 0;
+ } else
+ a->format += AUDIO_CODING_TYPE_HE_AAC -
+ AUDIO_CODING_XTYPE_HE_AAC;
+ break;
+ }
+}
+
+/*
+ * Be careful, ELD buf could be totally rubbish!
+ */
+int snd_parse_eld(struct device *dev, struct snd_parsed_hdmi_eld *e,
+ const unsigned char *buf, int size)
+{
+ int mnl;
+ int i;
+
+ memset(e, 0, sizeof(*e));
+ e->eld_ver = GRAB_BITS(buf, 0, 3, 5);
+ if (e->eld_ver != ELD_VER_CEA_861D &&
+ e->eld_ver != ELD_VER_PARTIAL) {
+ dev_info(dev, "HDMI: Unknown ELD version %d\n", e->eld_ver);
+ goto out_fail;
+ }
+
+ e->baseline_len = GRAB_BITS(buf, 2, 0, 8);
+ mnl = GRAB_BITS(buf, 4, 0, 5);
+ e->cea_edid_ver = GRAB_BITS(buf, 4, 5, 3);
+
+ e->support_hdcp = GRAB_BITS(buf, 5, 0, 1);
+ e->support_ai = GRAB_BITS(buf, 5, 1, 1);
+ e->conn_type = GRAB_BITS(buf, 5, 2, 2);
+ e->sad_count = GRAB_BITS(buf, 5, 4, 4);
+
+ e->aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2;
+ e->spk_alloc = GRAB_BITS(buf, 7, 0, 7);
+
+ e->port_id = get_unaligned_le64(buf + 8);
+
+ /* not specified, but the spec's tendency is little endian */
+ e->manufacture_id = get_unaligned_le16(buf + 16);
+ e->product_id = get_unaligned_le16(buf + 18);
+
+ if (mnl > ELD_MAX_MNL) {
+ dev_info(dev, "HDMI: MNL is reserved value %d\n", mnl);
+ goto out_fail;
+ } else if (ELD_FIXED_BYTES + mnl > size) {
+ dev_info(dev, "HDMI: out of range MNL %d\n", mnl);
+ goto out_fail;
+ } else
+ strscpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl + 1);
+
+ for (i = 0; i < e->sad_count; i++) {
+ if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) {
+ dev_info(dev, "HDMI: out of range SAD %d\n", i);
+ goto out_fail;
+ }
+ hdmi_update_short_audio_desc(dev, e->sad + i,
+ buf + ELD_FIXED_BYTES + mnl + 3 * i);
+ }
+
+ /*
+ * HDMI sink's ELD info cannot always be retrieved for now, e.g.
+ * in console or for audio devices. Assume the highest speakers
+ * configuration, to _not_ prohibit multi-channel audio playback.
+ */
+ if (!e->spk_alloc && e->sad_count)
+ e->spk_alloc = 0xffff;
+
+ return 0;
+
+out_fail:
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_parse_eld);
+
+/*
+ * SNDRV_PCM_RATE_* and AC_PAR_PCM values don't match, print correct rates with
+ * hdmi-specific routine.
+ */
+static void hdmi_print_pcm_rates(int pcm, char *buf, int buflen)
+{
+ static const unsigned int alsa_rates[] = {
+ 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000,
+ 88200, 96000, 176400, 192000, 384000
+ };
+ int i, j;
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(alsa_rates); i++)
+ if (pcm & (1 << i))
+ j += scnprintf(buf + j, buflen - j, " %d",
+ alsa_rates[i]);
+
+ buf[j] = '\0'; /* necessary when j == 0 */
+}
+
+static void eld_print_pcm_bits(int pcm, char *buf, int buflen)
+{
+ static const unsigned int bits[] = { 8, 16, 20, 24, 32 };
+ int i, j;
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(bits); i++)
+ if (pcm & (ELD_PCM_BITS_8 << i))
+ j += scnprintf(buf + j, buflen - j, " %d", bits[i]);
+
+ buf[j] = '\0'; /* necessary when j == 0 */
+}
+
+static void hdmi_show_short_audio_desc(struct device *dev,
+ struct snd_cea_sad *a)
+{
+ char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
+ char buf2[8 + SND_PRINT_BITS_ADVISED_BUFSIZE] = ", bits =";
+
+ if (!a->format)
+ return;
+
+ hdmi_print_pcm_rates(a->rates, buf, sizeof(buf));
+
+ if (a->format == AUDIO_CODING_TYPE_LPCM)
+ eld_print_pcm_bits(a->sample_bits, buf2 + 8, sizeof(buf2) - 8);
+ else if (a->max_bitrate)
+ snprintf(buf2, sizeof(buf2),
+ ", max bitrate = %d", a->max_bitrate);
+ else
+ buf2[0] = '\0';
+
+ dev_dbg(dev,
+ "HDMI: supports coding type %s: channels = %d, rates =%s%s\n",
+ cea_audio_coding_type_names[a->format],
+ a->channels, buf, buf2);
+}
+
+static void snd_eld_print_channel_allocation(int spk_alloc, char *buf, int buflen)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
+ if (spk_alloc & (1 << i))
+ j += scnprintf(buf + j, buflen - j, " %s",
+ cea_speaker_allocation_names[i]);
+ }
+ buf[j] = '\0'; /* necessary when j == 0 */
+}
+
+void snd_show_eld(struct device *dev, struct snd_parsed_hdmi_eld *e)
+{
+ int i;
+
+ dev_dbg(dev, "HDMI: detected monitor %s at connection type %s\n",
+ e->monitor_name,
+ eld_connection_type_names[e->conn_type]);
+
+ if (e->spk_alloc) {
+ char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+
+ snd_eld_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+ dev_dbg(dev, "HDMI: available speakers:%s\n", buf);
+ }
+
+ for (i = 0; i < e->sad_count; i++)
+ hdmi_show_short_audio_desc(dev, e->sad + i);
+}
+EXPORT_SYMBOL_GPL(snd_show_eld);
+
+#ifdef CONFIG_SND_PROC_FS
+static void hdmi_print_sad_info(int i, struct snd_cea_sad *a,
+ struct snd_info_buffer *buffer)
+{
+ char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
+
+ snd_iprintf(buffer, "sad%d_coding_type\t[0x%x] %s\n",
+ i, a->format, cea_audio_coding_type_names[a->format]);
+ snd_iprintf(buffer, "sad%d_channels\t\t%d\n", i, a->channels);
+
+ hdmi_print_pcm_rates(a->rates, buf, sizeof(buf));
+ snd_iprintf(buffer, "sad%d_rates\t\t[0x%x]%s\n", i, a->rates, buf);
+
+ if (a->format == AUDIO_CODING_TYPE_LPCM) {
+ eld_print_pcm_bits(a->sample_bits, buf, sizeof(buf));
+ snd_iprintf(buffer, "sad%d_bits\t\t[0x%x]%s\n",
+ i, a->sample_bits, buf);
+ }
+
+ if (a->max_bitrate)
+ snd_iprintf(buffer, "sad%d_max_bitrate\t%d\n",
+ i, a->max_bitrate);
+
+ if (a->profile)
+ snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile);
+}
+
+void snd_print_eld_info(struct snd_parsed_hdmi_eld *e,
+ struct snd_info_buffer *buffer)
+{
+ char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+ int i;
+ static const char * const eld_version_names[32] = {
+ "reserved",
+ "reserved",
+ "CEA-861D or below",
+ [3 ... 30] = "reserved",
+ [31] = "partial"
+ };
+ static const char * const cea_edid_version_names[8] = {
+ "no CEA EDID Timing Extension block present",
+ "CEA-861",
+ "CEA-861-A",
+ "CEA-861-B, C or D",
+ [4 ... 7] = "reserved"
+ };
+
+ snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name);
+ snd_iprintf(buffer, "connection_type\t\t%s\n",
+ eld_connection_type_names[e->conn_type]);
+ snd_iprintf(buffer, "eld_version\t\t[0x%x] %s\n", e->eld_ver,
+ eld_version_names[e->eld_ver]);
+ snd_iprintf(buffer, "edid_version\t\t[0x%x] %s\n", e->cea_edid_ver,
+ cea_edid_version_names[e->cea_edid_ver]);
+ snd_iprintf(buffer, "manufacture_id\t\t0x%x\n", e->manufacture_id);
+ snd_iprintf(buffer, "product_id\t\t0x%x\n", e->product_id);
+ snd_iprintf(buffer, "port_id\t\t\t0x%llx\n", (long long)e->port_id);
+ snd_iprintf(buffer, "support_hdcp\t\t%d\n", e->support_hdcp);
+ snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai);
+ snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay);
+
+ snd_eld_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+ snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf);
+
+ snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count);
+
+ for (i = 0; i < e->sad_count; i++)
+ hdmi_print_sad_info(i, e->sad + i, buffer);
+}
+EXPORT_SYMBOL_GPL(snd_print_eld_info);
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/pcm_iec958.c b/sound/core/pcm_iec958.c
new file mode 100644
index 000000000000..7a1b816f67cc
--- /dev/null
+++ b/sound/core/pcm_iec958.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PCM DRM helpers
+ */
+#include <linux/export.h>
+#include <linux/types.h>
+#include <sound/asoundef.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/pcm_iec958.h>
+
+/**
+ * snd_pcm_create_iec958_consumer_default - create default consumer format IEC958 channel status
+ * @cs: channel status buffer, at least four bytes
+ * @len: length of channel status buffer
+ *
+ * Create the consumer format channel status data in @cs of maximum size
+ * @len. When relevant, the configuration-dependant bits will be set as
+ * unspecified.
+ *
+ * Drivers should then call einter snd_pcm_fill_iec958_consumer() or
+ * snd_pcm_fill_iec958_consumer_hw_params() to replace these unspecified
+ * bits by their actual values.
+ *
+ * Drivers may wish to tweak the contents of the buffer after creation.
+ *
+ * Returns: length of buffer, or negative error code if something failed.
+ */
+int snd_pcm_create_iec958_consumer_default(u8 *cs, size_t len)
+{
+ if (len < 4)
+ return -EINVAL;
+
+ memset(cs, 0, len);
+
+ cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE;
+ cs[1] = IEC958_AES1_CON_GENERAL;
+ cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC;
+ cs[3] = IEC958_AES3_CON_CLOCK_1000PPM | IEC958_AES3_CON_FS_NOTID;
+
+ if (len > 4)
+ cs[4] = IEC958_AES4_CON_WORDLEN_NOTID;
+
+ return len;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_create_iec958_consumer_default);
+
+static int fill_iec958_consumer(uint rate, uint sample_width,
+ u8 *cs, size_t len)
+{
+ if (len < 4)
+ return -EINVAL;
+
+ if ((cs[3] & IEC958_AES3_CON_FS) == IEC958_AES3_CON_FS_NOTID) {
+ unsigned int fs;
+
+ switch (rate) {
+ case 32000:
+ fs = IEC958_AES3_CON_FS_32000;
+ break;
+ case 44100:
+ fs = IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ fs = IEC958_AES3_CON_FS_48000;
+ break;
+ case 88200:
+ fs = IEC958_AES3_CON_FS_88200;
+ break;
+ case 96000:
+ fs = IEC958_AES3_CON_FS_96000;
+ break;
+ case 176400:
+ fs = IEC958_AES3_CON_FS_176400;
+ break;
+ case 192000:
+ fs = IEC958_AES3_CON_FS_192000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ cs[3] &= ~IEC958_AES3_CON_FS;
+ cs[3] |= fs;
+ }
+
+ if (len > 4 &&
+ (cs[4] & IEC958_AES4_CON_WORDLEN) == IEC958_AES4_CON_WORDLEN_NOTID) {
+ unsigned int ws;
+
+ switch (sample_width) {
+ case 16:
+ ws = IEC958_AES4_CON_WORDLEN_20_16;
+ break;
+ case 18:
+ ws = IEC958_AES4_CON_WORDLEN_22_18;
+ break;
+ case 20:
+ ws = IEC958_AES4_CON_WORDLEN_20_16 |
+ IEC958_AES4_CON_MAX_WORDLEN_24;
+ break;
+ case 24:
+ case 32: /* Assume 24-bit width for 32-bit samples. */
+ ws = IEC958_AES4_CON_WORDLEN_24_20 |
+ IEC958_AES4_CON_MAX_WORDLEN_24;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ cs[4] &= ~IEC958_AES4_CON_WORDLEN;
+ cs[4] |= ws;
+ }
+
+ return len;
+}
+
+/**
+ * snd_pcm_fill_iec958_consumer - Fill consumer format IEC958 channel status
+ * @runtime: pcm runtime structure with ->rate filled in
+ * @cs: channel status buffer, at least four bytes
+ * @len: length of channel status buffer
+ *
+ * Fill the unspecified bits in an IEC958 status bits array using the
+ * parameters of the PCM runtime @runtime.
+ *
+ * Drivers may wish to tweak the contents of the buffer after its been
+ * filled.
+ *
+ * Returns: length of buffer, or negative error code if something failed.
+ */
+int snd_pcm_fill_iec958_consumer(struct snd_pcm_runtime *runtime,
+ u8 *cs, size_t len)
+{
+ return fill_iec958_consumer(runtime->rate,
+ snd_pcm_format_width(runtime->format),
+ cs, len);
+}
+EXPORT_SYMBOL_GPL(snd_pcm_fill_iec958_consumer);
+
+/**
+ * snd_pcm_fill_iec958_consumer_hw_params - Fill consumer format IEC958 channel status
+ * @params: the hw_params instance for extracting rate and sample format
+ * @cs: channel status buffer, at least four bytes
+ * @len: length of channel status buffer
+ *
+ * Fill the unspecified bits in an IEC958 status bits array using the
+ * parameters of the PCM hardware parameters @params.
+ *
+ * Drivers may wish to tweak the contents of the buffer after its been
+ * filled..
+ *
+ * Returns: length of buffer, or negative error code if something failed.
+ */
+int snd_pcm_fill_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
+ u8 *cs, size_t len)
+{
+ return fill_iec958_consumer(params_rate(params), params_width(params), cs, len);
+}
+EXPORT_SYMBOL_GPL(snd_pcm_fill_iec958_consumer_hw_params);
+
+/**
+ * snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status
+ * @runtime: pcm runtime structure with ->rate filled in
+ * @cs: channel status buffer, at least four bytes
+ * @len: length of channel status buffer
+ *
+ * Create the consumer format channel status data in @cs of maximum size
+ * @len corresponding to the parameters of the PCM runtime @runtime.
+ *
+ * Drivers may wish to tweak the contents of the buffer after creation.
+ *
+ * Returns: length of buffer, or negative error code if something failed.
+ */
+int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
+ size_t len)
+{
+ int ret;
+
+ ret = snd_pcm_create_iec958_consumer_default(cs, len);
+ if (ret < 0)
+ return ret;
+
+ return snd_pcm_fill_iec958_consumer(runtime, cs, len);
+}
+EXPORT_SYMBOL(snd_pcm_create_iec958_consumer);
+
+/**
+ * snd_pcm_create_iec958_consumer_hw_params - create IEC958 channel status
+ * @params: the hw_params instance for extracting rate and sample format
+ * @cs: channel status buffer, at least four bytes
+ * @len: length of channel status buffer
+ *
+ * Create the consumer format channel status data in @cs of maximum size
+ * @len corresponding to the parameters of the PCM runtime @runtime.
+ *
+ * Drivers may wish to tweak the contents of the buffer after creation.
+ *
+ * Returns: length of buffer, or negative error code if something failed.
+ */
+int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
+ u8 *cs, size_t len)
+{
+ int ret;
+
+ ret = snd_pcm_create_iec958_consumer_default(cs, len);
+ if (ret < 0)
+ return ret;
+
+ return fill_iec958_consumer(params_rate(params), params_width(params), cs, len);
+}
+EXPORT_SYMBOL(snd_pcm_create_iec958_consumer_hw_params);
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index a1707cca9c66..6eaa950504cf 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1,35 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Digital Audio (PCM) abstract layer
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Abramo Bagnara <abramo@alsa-project.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
+#include <linux/sched/signal.h>
#include <linux/time.h>
#include <linux/math64.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/control.h>
+#include <sound/tlv.h>
#include <sound/info.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/timer.h>
+#include "pcm_local.h"
+
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+#define CREATE_TRACE_POINTS
+#include "pcm_trace.h"
+#else
+#define trace_hwptr(substream, pos, in_interrupt)
+#define trace_xrun(substream)
+#define trace_hw_ptr_error(substream, reason)
+#define trace_applptr(substream, prev, curr)
+#endif
+
+static int fill_silence_frames(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t off, snd_pcm_uframes_t frames);
+
+
+static inline void update_silence_vars(struct snd_pcm_runtime *runtime,
+ snd_pcm_uframes_t ptr,
+ snd_pcm_uframes_t new_ptr)
+{
+ snd_pcm_sframes_t delta;
+
+ delta = new_ptr - ptr;
+ if (delta == 0)
+ return;
+ if (delta < 0)
+ delta += runtime->boundary;
+ if ((snd_pcm_uframes_t)delta < runtime->silence_filled)
+ runtime->silence_filled -= delta;
+ else
+ runtime->silence_filled = 0;
+ runtime->silence_start = new_ptr;
+}
+
/*
* fill ring buffer with silence
* runtime->silence_start: starting pointer to silence area
@@ -43,92 +65,77 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t frames, ofs, transfer;
+ int err;
if (runtime->silence_size < runtime->boundary) {
- snd_pcm_sframes_t noise_dist, n;
- if (runtime->silence_start != runtime->control->appl_ptr) {
- n = runtime->control->appl_ptr - runtime->silence_start;
- if (n < 0)
- n += runtime->boundary;
- if ((snd_pcm_uframes_t)n < runtime->silence_filled)
- runtime->silence_filled -= n;
- else
- runtime->silence_filled = 0;
- runtime->silence_start = runtime->control->appl_ptr;
- }
- if (runtime->silence_filled >= runtime->buffer_size)
- return;
- noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled;
+ snd_pcm_sframes_t noise_dist;
+ snd_pcm_uframes_t appl_ptr = READ_ONCE(runtime->control->appl_ptr);
+ update_silence_vars(runtime, runtime->silence_start, appl_ptr);
+ /* initialization outside pointer updates */
+ if (new_hw_ptr == ULONG_MAX)
+ new_hw_ptr = runtime->status->hw_ptr;
+ /* get hw_avail with the boundary crossing */
+ noise_dist = appl_ptr - new_hw_ptr;
+ if (noise_dist < 0)
+ noise_dist += runtime->boundary;
+ /* total noise distance */
+ noise_dist += runtime->silence_filled;
if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold)
return;
frames = runtime->silence_threshold - noise_dist;
if (frames > runtime->silence_size)
frames = runtime->silence_size;
} else {
- if (new_hw_ptr == ULONG_MAX) { /* initialization */
- snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime);
- if (avail > runtime->buffer_size)
- avail = runtime->buffer_size;
- runtime->silence_filled = avail > 0 ? avail : 0;
- runtime->silence_start = (runtime->status->hw_ptr +
- runtime->silence_filled) %
- runtime->boundary;
+ /*
+ * This filling mode aims at free-running mode (used for example by dmix),
+ * which doesn't update the application pointer.
+ */
+ snd_pcm_uframes_t hw_ptr = runtime->status->hw_ptr;
+ if (new_hw_ptr == ULONG_MAX) {
+ /*
+ * Initialization, fill the whole unused buffer with silence.
+ *
+ * Usually, this is entered while stopped, before data is queued,
+ * so both pointers are expected to be zero.
+ */
+ snd_pcm_sframes_t avail = runtime->control->appl_ptr - hw_ptr;
+ if (avail < 0)
+ avail += runtime->boundary;
+ /*
+ * In free-running mode, appl_ptr will be zero even while running,
+ * so we end up with a huge number. There is no useful way to
+ * handle this, so we just clear the whole buffer.
+ */
+ runtime->silence_filled = avail > runtime->buffer_size ? 0 : avail;
+ runtime->silence_start = hw_ptr;
} else {
- ofs = runtime->status->hw_ptr;
- frames = new_hw_ptr - ofs;
- if ((snd_pcm_sframes_t)frames < 0)
- frames += runtime->boundary;
- runtime->silence_filled -= frames;
- if ((snd_pcm_sframes_t)runtime->silence_filled < 0) {
- runtime->silence_filled = 0;
- runtime->silence_start = new_hw_ptr;
- } else {
- runtime->silence_start = ofs;
- }
+ /* Silence the just played area immediately */
+ update_silence_vars(runtime, hw_ptr, new_hw_ptr);
}
+ /*
+ * In this mode, silence_filled actually includes the valid
+ * sample data from the user.
+ */
frames = runtime->buffer_size - runtime->silence_filled;
}
if (snd_BUG_ON(frames > runtime->buffer_size))
return;
if (frames == 0)
return;
- ofs = runtime->silence_start % runtime->buffer_size;
- while (frames > 0) {
+ ofs = (runtime->silence_start + runtime->silence_filled) % runtime->buffer_size;
+ do {
transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames;
- if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
- runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
- if (substream->ops->silence) {
- int err;
- err = substream->ops->silence(substream, -1, ofs, transfer);
- snd_BUG_ON(err < 0);
- } else {
- char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs);
- snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels);
- }
- } else {
- unsigned int c;
- unsigned int channels = runtime->channels;
- if (substream->ops->silence) {
- for (c = 0; c < channels; ++c) {
- int err;
- err = substream->ops->silence(substream, c, ofs, transfer);
- snd_BUG_ON(err < 0);
- }
- } else {
- size_t dma_csize = runtime->dma_bytes / channels;
- for (c = 0; c < channels; ++c) {
- char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs);
- snd_pcm_format_set_silence(runtime->format, hwbuf, transfer);
- }
- }
- }
+ err = fill_silence_frames(substream, ofs, transfer);
+ snd_BUG_ON(err < 0);
runtime->silence_filled += transfer;
frames -= transfer;
ofs = 0;
- }
+ } while (frames > 0);
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
}
-static void pcm_debug_name(struct snd_pcm_substream *substream,
+#ifdef CONFIG_SND_DEBUG
+void snd_pcm_debug_name(struct snd_pcm_substream *substream,
char *name, size_t len)
{
snprintf(name, len, "pcmC%dD%d%c:%d",
@@ -137,14 +144,12 @@ static void pcm_debug_name(struct snd_pcm_substream *substream,
substream->stream ? 'c' : 'p',
substream->number);
}
+EXPORT_SYMBOL(snd_pcm_debug_name);
+#endif
#define XRUN_DEBUG_BASIC (1<<0)
#define XRUN_DEBUG_STACK (1<<1) /* dump also stack */
#define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */
-#define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */
-#define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */
-#define XRUN_DEBUG_LOG (1<<5) /* show last 10 positions on err */
-#define XRUN_DEBUG_LOGONCE (1<<6) /* do above only once */
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
@@ -159,111 +164,45 @@ static void pcm_debug_name(struct snd_pcm_substream *substream,
dump_stack(); \
} while (0)
-static void xrun(struct snd_pcm_substream *substream)
+/* call with stream lock held */
+void __snd_pcm_xrun(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
- snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
+ trace_xrun(substream);
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
+ struct timespec64 tstamp;
+
+ snd_pcm_gettime(runtime, &tstamp);
+ runtime->status->tstamp.tv_sec = tstamp.tv_sec;
+ runtime->status->tstamp.tv_nsec = tstamp.tv_nsec;
+ }
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {
char name[16];
- pcm_debug_name(substream, name, sizeof(name));
- snd_printd(KERN_DEBUG "XRUN: %s\n", name);
+ snd_pcm_debug_name(substream, name, sizeof(name));
+ pcm_warn(substream->pcm, "XRUN: %s\n", name);
dump_stack_on_xrun(substream);
}
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+ substream->xrun_counter++;
+#endif
}
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
-#define hw_ptr_error(substream, fmt, args...) \
+#define hw_ptr_error(substream, in_interrupt, reason, fmt, args...) \
do { \
+ trace_hw_ptr_error(substream, reason); \
if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \
- xrun_log_show(substream); \
- if (printk_ratelimit()) { \
- snd_printd("PCM: " fmt, ##args); \
- } \
+ pr_err_ratelimited("ALSA: PCM: [%c] " reason ": " fmt, \
+ (in_interrupt) ? 'Q' : 'P', ##args); \
dump_stack_on_xrun(substream); \
} \
} while (0)
-#define XRUN_LOG_CNT 10
-
-struct hwptr_log_entry {
- unsigned long jiffies;
- snd_pcm_uframes_t pos;
- snd_pcm_uframes_t period_size;
- snd_pcm_uframes_t buffer_size;
- snd_pcm_uframes_t old_hw_ptr;
- snd_pcm_uframes_t hw_ptr_base;
-};
-
-struct snd_pcm_hwptr_log {
- unsigned int idx;
- unsigned int hit: 1;
- struct hwptr_log_entry entries[XRUN_LOG_CNT];
-};
-
-static void xrun_log(struct snd_pcm_substream *substream,
- snd_pcm_uframes_t pos)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_pcm_hwptr_log *log = runtime->hwptr_log;
- struct hwptr_log_entry *entry;
-
- if (log == NULL) {
- log = kzalloc(sizeof(*log), GFP_ATOMIC);
- if (log == NULL)
- return;
- runtime->hwptr_log = log;
- } else {
- if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
- return;
- }
- entry = &log->entries[log->idx];
- entry->jiffies = jiffies;
- entry->pos = pos;
- entry->period_size = runtime->period_size;
- entry->buffer_size = runtime->buffer_size;;
- entry->old_hw_ptr = runtime->status->hw_ptr;
- entry->hw_ptr_base = runtime->hw_ptr_base;
- log->idx = (log->idx + 1) % XRUN_LOG_CNT;
-}
-
-static void xrun_log_show(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log;
- struct hwptr_log_entry *entry;
- char name[16];
- unsigned int idx;
- int cnt;
-
- if (log == NULL)
- return;
- if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
- return;
- pcm_debug_name(substream, name, sizeof(name));
- for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) {
- entry = &log->entries[idx];
- if (entry->period_size == 0)
- break;
- snd_printd("hwptr log: %s: j=%lu, pos=%ld/%ld/%ld, "
- "hwptr=%ld/%ld\n",
- name, entry->jiffies, (unsigned long)entry->pos,
- (unsigned long)entry->period_size,
- (unsigned long)entry->buffer_size,
- (unsigned long)entry->old_hw_ptr,
- (unsigned long)entry->hw_ptr_base);
- idx++;
- idx %= XRUN_LOG_CNT;
- }
- log->hit = 1;
-}
-
#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */
#define hw_ptr_error(substream, fmt, args...) do { } while (0)
-#define xrun_log(substream, pos) do { } while (0)
-#define xrun_log_show(substream) do { } while (0)
#endif
@@ -272,20 +211,17 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream,
{
snd_pcm_uframes_t avail;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- avail = snd_pcm_playback_avail(runtime);
- else
- avail = snd_pcm_capture_avail(runtime);
+ avail = snd_pcm_avail(substream);
if (avail > runtime->avail_max)
runtime->avail_max = avail;
- if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
+ if (runtime->state == SNDRV_PCM_STATE_DRAINING) {
if (avail >= runtime->buffer_size) {
snd_pcm_drain_done(substream);
return -EPIPE;
}
} else {
if (avail >= runtime->stop_threshold) {
- xrun(substream);
+ __snd_pcm_xrun(substream);
return -EPIPE;
}
}
@@ -297,6 +233,56 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream,
return 0;
}
+static void update_audio_tstamp(struct snd_pcm_substream *substream,
+ struct timespec64 *curr_tstamp,
+ struct timespec64 *audio_tstamp)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u64 audio_frames, audio_nsecs;
+ struct timespec64 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_timespec64(audio_nsecs);
+ }
+
+ if (runtime->status->audio_tstamp.tv_sec != audio_tstamp->tv_sec ||
+ runtime->status->audio_tstamp.tv_nsec != audio_tstamp->tv_nsec) {
+ runtime->status->audio_tstamp.tv_sec = audio_tstamp->tv_sec;
+ runtime->status->audio_tstamp.tv_nsec = audio_tstamp->tv_nsec;
+ runtime->status->tstamp.tv_sec = curr_tstamp->tv_sec;
+ runtime->status->tstamp.tv_nsec = curr_tstamp->tv_nsec;
+ }
+
+
+ /*
+ * 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, &driver_tstamp);
+ runtime->driver_tstamp = driver_tstamp;
+}
+
static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
unsigned int in_interrupt)
{
@@ -305,28 +291,53 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
snd_pcm_sframes_t hdelta, delta;
unsigned long jdelta;
+ unsigned long curr_jiffies;
+ struct timespec64 curr_tstamp;
+ struct timespec64 audio_tstamp;
+ int crossed_boundary = 0;
old_hw_ptr = runtime->status->hw_ptr;
+
+ /*
+ * group pointer, time and jiffies reads to allow for more
+ * accurate correlations/corrections.
+ * The values are stored at the end of this routine after
+ * corrections for hw_ptr position
+ */
pos = substream->ops->pointer(substream);
+ curr_jiffies = jiffies;
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
+ 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, &curr_tstamp);
+ } else
+ snd_pcm_gettime(runtime, &curr_tstamp);
+ }
+
if (pos == SNDRV_PCM_POS_XRUN) {
- xrun(substream);
+ __snd_pcm_xrun(substream);
return -EPIPE;
}
if (pos >= runtime->buffer_size) {
if (printk_ratelimit()) {
char name[16];
- pcm_debug_name(substream, name, sizeof(name));
- xrun_log_show(substream);
- snd_printd(KERN_ERR "BUG: %s, pos = %ld, "
- "buffer size = %ld, period size = %ld\n",
- name, pos, runtime->buffer_size,
- runtime->period_size);
+ snd_pcm_debug_name(substream, name, sizeof(name));
+ pcm_err(substream->pcm,
+ "invalid position: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
+ name, pos, runtime->buffer_size,
+ runtime->period_size);
}
pos = 0;
}
pos -= pos % runtime->min_align;
- if (xrun_debug(substream, XRUN_DEBUG_LOG))
- xrun_log(substream, pos);
+ trace_hwptr(substream, pos, in_interrupt);
hw_base = runtime->hw_ptr_base;
new_hw_ptr = hw_base + pos;
if (in_interrupt) {
@@ -335,11 +346,13 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
delta = runtime->hw_ptr_interrupt + runtime->period_size;
if (delta > new_hw_ptr) {
/* check for double acknowledged interrupts */
- hdelta = jiffies - runtime->hw_ptr_jiffies;
- if (hdelta > runtime->hw_ptr_buffer_jiffies/2) {
+ hdelta = curr_jiffies - runtime->hw_ptr_jiffies;
+ if (hdelta > runtime->hw_ptr_buffer_jiffies/2 + 1) {
hw_base += runtime->buffer_size;
- if (hw_base >= runtime->boundary)
+ if (hw_base >= runtime->boundary) {
hw_base = 0;
+ crossed_boundary++;
+ }
new_hw_ptr = hw_base + pos;
goto __delta;
}
@@ -349,39 +362,47 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
/* pointer crosses the end of the ring buffer */
if (new_hw_ptr < old_hw_ptr) {
hw_base += runtime->buffer_size;
- if (hw_base >= runtime->boundary)
+ if (hw_base >= runtime->boundary) {
hw_base = 0;
+ crossed_boundary++;
+ }
new_hw_ptr = hw_base + pos;
}
__delta:
delta = new_hw_ptr - old_hw_ptr;
if (delta < 0)
delta += runtime->boundary;
- if (xrun_debug(substream, in_interrupt ?
- XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) {
- char name[16];
- pcm_debug_name(substream, name, sizeof(name));
- snd_printd("%s_update: %s: pos=%u/%u/%u, "
- "hwptr=%ld/%ld/%ld/%ld\n",
- in_interrupt ? "period" : "hwptr",
- name,
- (unsigned int)pos,
- (unsigned int)runtime->period_size,
- (unsigned int)runtime->buffer_size,
- (unsigned long)delta,
- (unsigned long)old_hw_ptr,
- (unsigned long)new_hw_ptr,
- (unsigned long)runtime->hw_ptr_base);
+
+ if (runtime->no_period_wakeup) {
+ snd_pcm_sframes_t xrun_threshold;
+ /*
+ * Without regular period interrupts, we have to check
+ * the elapsed time to detect xruns.
+ */
+ jdelta = curr_jiffies - runtime->hw_ptr_jiffies;
+ if (jdelta < runtime->hw_ptr_buffer_jiffies / 2)
+ goto no_delta_check;
+ hdelta = jdelta - delta * HZ / runtime->rate;
+ xrun_threshold = runtime->hw_ptr_buffer_jiffies / 2 + 1;
+ while (hdelta > xrun_threshold) {
+ delta += runtime->buffer_size;
+ hw_base += runtime->buffer_size;
+ if (hw_base >= runtime->boundary) {
+ hw_base = 0;
+ crossed_boundary++;
+ }
+ new_hw_ptr = hw_base + pos;
+ hdelta -= runtime->hw_ptr_buffer_jiffies;
+ }
+ goto no_delta_check;
}
+
/* something must be really wrong */
if (delta >= runtime->buffer_size + runtime->period_size) {
- hw_ptr_error(substream,
- "Unexpected hw_pointer value %s"
- "(stream=%i, pos=%ld, new_hw_ptr=%ld, "
- "old_hw_ptr=%ld)\n",
- in_interrupt ? "[Q] " : "[P]",
- substream->stream, (long)pos,
- (long)new_hw_ptr, (long)old_hw_ptr);
+ hw_ptr_error(substream, in_interrupt, "Unexpected hw_ptr",
+ "(stream=%i, pos=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n",
+ substream->stream, (long)pos,
+ (long)new_hw_ptr, (long)old_hw_ptr);
return 0;
}
@@ -399,7 +420,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
if (hdelta < runtime->delay)
goto no_jiffies_check;
hdelta -= runtime->delay;
- jdelta = jiffies - runtime->hw_ptr_jiffies;
+ jdelta = curr_jiffies - runtime->hw_ptr_jiffies;
if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) {
delta = jdelta /
(((runtime->period_size * HZ) / runtime->rate)
@@ -411,16 +432,15 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
/* the delta value is small or zero in most cases */
while (delta > 0) {
new_hw_ptr += runtime->period_size;
- if (new_hw_ptr >= runtime->boundary)
+ if (new_hw_ptr >= runtime->boundary) {
new_hw_ptr -= runtime->boundary;
+ crossed_boundary--;
+ }
delta--;
}
/* align hw_base to buffer_size */
- hw_ptr_error(substream,
- "hw_ptr skipping! %s"
- "(pos=%ld, delta=%ld, period=%ld, "
- "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n",
- in_interrupt ? "[Q] " : "",
+ hw_ptr_error(substream, in_interrupt, "hw_ptr skipping",
+ "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n",
(long)pos, (long)hdelta,
(long)runtime->period_size, jdelta,
((hdelta * HZ) / runtime->rate), hw_base,
@@ -432,18 +452,20 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
}
no_jiffies_check:
if (delta > runtime->period_size + runtime->period_size / 2) {
- hw_ptr_error(substream,
- "Lost interrupts? %s"
- "(stream=%i, delta=%ld, new_hw_ptr=%ld, "
- "old_hw_ptr=%ld)\n",
- in_interrupt ? "[Q] " : "",
+ hw_ptr_error(substream, in_interrupt,
+ "Lost interrupts?",
+ "(stream=%i, delta=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n",
substream->stream, (long)delta,
(long)new_hw_ptr,
(long)old_hw_ptr);
}
- if (runtime->status->hw_ptr == new_hw_ptr)
+ no_delta_check:
+ if (runtime->status->hw_ptr == new_hw_ptr) {
+ runtime->hw_ptr_jiffies = curr_jiffies;
+ update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp);
return 0;
+ }
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
@@ -460,9 +482,13 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
}
runtime->hw_ptr_base = hw_base;
runtime->status->hw_ptr = new_hw_ptr;
- runtime->hw_ptr_jiffies = jiffies;
- if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
- snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
+ runtime->hw_ptr_jiffies = curr_jiffies;
+ if (crossed_boundary) {
+ snd_BUG_ON(crossed_boundary != 1);
+ runtime->hw_ptr_wrap += runtime->boundary;
+ }
+
+ update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp);
return snd_pcm_update_state(substream, runtime);
}
@@ -481,7 +507,8 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
*
* Sets the given PCM operators to the pcm instance.
*/
-void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops)
+void snd_pcm_set_ops(struct snd_pcm *pcm, int direction,
+ const struct snd_pcm_ops *ops)
{
struct snd_pcm_str *stream = &pcm->streams[direction];
struct snd_pcm_substream *substream;
@@ -489,26 +516,41 @@ void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops
for (substream = stream->substream; substream != NULL; substream = substream->next)
substream->ops = ops;
}
-
EXPORT_SYMBOL(snd_pcm_set_ops);
/**
- * snd_pcm_sync - set the PCM sync id
+ * snd_pcm_set_sync_per_card - set the PCM sync id with card number
* @substream: the pcm substream
+ * @params: modified hardware parameters
+ * @id: identifier (max 12 bytes)
+ * @len: identifier length (max 12 bytes)
+ *
+ * Sets the PCM sync identifier for the card with zero padding.
+ *
+ * User space or any user should use this 16-byte identifier for a comparison only
+ * to check if two IDs are similar or different. Special case is the identifier
+ * containing only zeros. Interpretation for this combination is - empty (not set).
+ * The contents of the identifier should not be interpreted in any other way.
+ *
+ * The synchronization ID must be unique per clock source (usually one sound card,
+ * but multiple soundcard may use one PCM word clock source which means that they
+ * are fully synchronized).
*
- * Sets the PCM sync identifier for the card.
+ * This routine composes this ID using card number in first four bytes and
+ * 12-byte additional ID. When other ID composition is used (e.g. for multiple
+ * sound cards), make sure that the composition does not clash with this
+ * composition scheme.
*/
-void snd_pcm_set_sync(struct snd_pcm_substream *substream)
+void snd_pcm_set_sync_per_card(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ const unsigned char *id, unsigned int len)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- runtime->sync.id32[0] = substream->pcm->card->number;
- runtime->sync.id32[1] = -1;
- runtime->sync.id32[2] = -1;
- runtime->sync.id32[3] = -1;
+ *(__u32 *)params->sync = cpu_to_le32(substream->pcm->card->number);
+ len = min(12, len);
+ memcpy(params->sync + 4, id, len);
+ memset(params->sync + 4 + len, 0, 12 - len);
}
-
-EXPORT_SYMBOL(snd_pcm_set_sync);
+EXPORT_SYMBOL_GPL(snd_pcm_set_sync_per_card);
/*
* Standard ioctl routine
@@ -558,7 +600,6 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b,
{
u_int64_t n = (u_int64_t) a * b;
if (c == 0) {
- snd_BUG_ON(!n);
*r = 0;
return UINT_MAX;
}
@@ -579,7 +620,8 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b,
* The interval is changed to the range satisfying both intervals.
* The interval status (min, max, integer, etc.) are evaluated.
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v)
{
@@ -623,32 +665,37 @@ int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v)
}
return changed;
}
-
EXPORT_SYMBOL(snd_interval_refine);
static int snd_interval_refine_first(struct snd_interval *i)
{
+ const unsigned int last_max = i->max;
+
if (snd_BUG_ON(snd_interval_empty(i)))
return -EINVAL;
if (snd_interval_single(i))
return 0;
i->max = i->min;
- i->openmax = i->openmin;
- if (i->openmax)
+ if (i->openmin)
i->max++;
+ /* only exclude max value if also excluded before refine */
+ i->openmax = (i->openmax && i->max >= last_max);
return 1;
}
static int snd_interval_refine_last(struct snd_interval *i)
{
+ const unsigned int last_min = i->min;
+
if (snd_BUG_ON(snd_interval_empty(i)))
return -EINVAL;
if (snd_interval_single(i))
return 0;
i->min = i->max;
- i->openmin = i->openmax;
- if (i->openmin)
+ if (i->openmax)
i->min--;
+ /* only exclude min value if also excluded before refine */
+ i->openmin = (i->openmin && i->min <= last_min);
return 1;
}
@@ -778,10 +825,11 @@ void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
* @nump: pointer to store the resultant numerator
* @denp: pointer to store the resultant denominator
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_interval_ratnum(struct snd_interval *i,
- unsigned int rats_count, struct snd_ratnum *rats,
+ unsigned int rats_count, const struct snd_ratnum *rats,
unsigned int *nump, unsigned int *denp)
{
unsigned int best_num, best_den;
@@ -885,7 +933,6 @@ int snd_interval_ratnum(struct snd_interval *i,
}
return err;
}
-
EXPORT_SYMBOL(snd_interval_ratnum);
/**
@@ -896,10 +943,12 @@ EXPORT_SYMBOL(snd_interval_ratnum);
* @nump: pointer to store the resultant numerator
* @denp: pointer to store the resultant denominator
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
static int snd_interval_ratden(struct snd_interval *i,
- unsigned int rats_count, struct snd_ratden *rats,
+ unsigned int rats_count,
+ const struct snd_ratden *rats,
unsigned int *nump, unsigned int *denp)
{
unsigned int best_num, best_diff, best_den;
@@ -995,9 +1044,11 @@ static int snd_interval_ratden(struct snd_interval *i,
* When mask is non-zero, only the elements corresponding to bit 1 are
* evaluated.
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
-int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask)
+int snd_interval_list(struct snd_interval *i, unsigned int count,
+ const unsigned int *list, unsigned int mask)
{
unsigned int k;
struct snd_interval list_range;
@@ -1019,21 +1070,76 @@ int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *
}
return snd_interval_refine(i, &list_range);
}
-
EXPORT_SYMBOL(snd_interval_list);
-static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned int step)
+/**
+ * snd_interval_ranges - refine the interval value from the list of ranges
+ * @i: the interval value to refine
+ * @count: the number of elements in the list of ranges
+ * @ranges: the ranges list
+ * @mask: the bit-mask to evaluate
+ *
+ * Refines the interval value from the list of ranges.
+ * When mask is non-zero, only the elements corresponding to bit 1 are
+ * evaluated.
+ *
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
+ */
+int snd_interval_ranges(struct snd_interval *i, unsigned int count,
+ const struct snd_interval *ranges, unsigned int mask)
+{
+ unsigned int k;
+ struct snd_interval range_union;
+ struct snd_interval range;
+
+ if (!count) {
+ snd_interval_none(i);
+ return -EINVAL;
+ }
+ snd_interval_any(&range_union);
+ range_union.min = UINT_MAX;
+ range_union.max = 0;
+ for (k = 0; k < count; k++) {
+ if (mask && !(mask & (1 << k)))
+ continue;
+ snd_interval_copy(&range, &ranges[k]);
+ if (snd_interval_refine(&range, i) < 0)
+ continue;
+ if (snd_interval_empty(&range))
+ continue;
+
+ if (range.min < range_union.min) {
+ range_union.min = range.min;
+ range_union.openmin = 1;
+ }
+ if (range.min == range_union.min && !range.openmin)
+ range_union.openmin = 0;
+ if (range.max > range_union.max) {
+ range_union.max = range.max;
+ range_union.openmax = 1;
+ }
+ if (range.max == range_union.max && !range.openmax)
+ range_union.openmax = 0;
+ }
+ return snd_interval_refine(i, &range_union);
+}
+EXPORT_SYMBOL(snd_interval_ranges);
+
+static int snd_interval_step(struct snd_interval *i, unsigned int step)
{
unsigned int n;
int changed = 0;
- n = (i->min - min) % step;
+ n = i->min % step;
if (n != 0 || i->openmin) {
i->min += step - n;
+ i->openmin = 0;
changed = 1;
}
- n = (i->max - min) % step;
+ n = i->max % step;
if (n != 0 || i->openmax) {
i->max -= n;
+ i->openmax = 0;
changed = 1;
}
if (snd_interval_checkempty(i)) {
@@ -1054,7 +1160,7 @@ static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned
* @private: the private data pointer passed to function
* @dep: the dependent variables
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
int var,
@@ -1069,13 +1175,11 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
if (constrs->rules_num >= constrs->rules_all) {
struct snd_pcm_hw_rule *new;
unsigned int new_rules = constrs->rules_all + 16;
- new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL);
- if (!new)
+ new = krealloc_array(constrs->rules, new_rules,
+ sizeof(*c), GFP_KERNEL);
+ if (!new) {
+ va_end(args);
return -ENOMEM;
- if (constrs->rules) {
- memcpy(new, constrs->rules,
- constrs->rules_num * sizeof(*c));
- kfree(constrs->rules);
}
constrs->rules = new;
constrs->rules_all = new_rules;
@@ -1087,8 +1191,10 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
c->private = private;
k = 0;
while (1) {
- if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps)))
+ if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps))) {
+ va_end(args);
return -EINVAL;
+ }
c->deps[k++] = dep;
if (dep < 0)
break;
@@ -1097,8 +1203,7 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
constrs->rules_num++;
va_end(args);
return 0;
-}
-
+}
EXPORT_SYMBOL(snd_pcm_hw_rule_add);
/**
@@ -1108,6 +1213,8 @@ EXPORT_SYMBOL(snd_pcm_hw_rule_add);
* @mask: the bitmap mask
*
* Apply the constraint of the given bitmap mask to a 32-bit mask parameter.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
u_int32_t mask)
@@ -1128,6 +1235,8 @@ int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param
* @mask: the 64bit bitmap mask
*
* Apply the constraint of the given bitmap mask to a 64-bit mask parameter.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
u_int64_t mask)
@@ -1141,6 +1250,7 @@ int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_par
return -EINVAL;
return 0;
}
+EXPORT_SYMBOL(snd_pcm_hw_constraint_mask64);
/**
* snd_pcm_hw_constraint_integer - apply an integer constraint to an interval
@@ -1148,13 +1258,15 @@ int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_par
* @var: hw_params variable to apply the integer constraint
*
* Apply the constraint of integer to an interval parameter.
+ *
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var)
{
struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
return snd_interval_setinteger(constrs_interval(constrs, var));
}
-
EXPORT_SYMBOL(snd_pcm_hw_constraint_integer);
/**
@@ -1165,6 +1277,9 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_integer);
* @max: the maximal value
*
* Apply the min/max range constraint to an interval parameter.
+ *
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
unsigned int min, unsigned int max)
@@ -1177,7 +1292,6 @@ int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_par
t.integer = 0;
return snd_interval_refine(constrs_interval(constrs, var), &t);
}
-
EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax);
static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params,
@@ -1196,23 +1310,55 @@ static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params,
* @l: list
*
* Apply the list of constraints to an interval parameter.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime,
unsigned int cond,
snd_pcm_hw_param_t var,
- struct snd_pcm_hw_constraint_list *l)
+ const struct snd_pcm_hw_constraint_list *l)
{
return snd_pcm_hw_rule_add(runtime, cond, var,
- snd_pcm_hw_rule_list, l,
+ snd_pcm_hw_rule_list, (void *)l,
var, -1);
}
-
EXPORT_SYMBOL(snd_pcm_hw_constraint_list);
+static int snd_pcm_hw_rule_ranges(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_pcm_hw_constraint_ranges *r = rule->private;
+ return snd_interval_ranges(hw_param_interval(params, rule->var),
+ r->count, r->ranges, r->mask);
+}
+
+
+/**
+ * snd_pcm_hw_constraint_ranges - apply list of range constraints to a parameter
+ * @runtime: PCM runtime instance
+ * @cond: condition bits
+ * @var: hw_params variable to apply the list of range constraints
+ * @r: ranges
+ *
+ * Apply the list of range constraints to an interval parameter.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime,
+ unsigned int cond,
+ snd_pcm_hw_param_t var,
+ const struct snd_pcm_hw_constraint_ranges *r)
+{
+ return snd_pcm_hw_rule_add(runtime, cond, var,
+ snd_pcm_hw_rule_ranges, (void *)r,
+ var, -1);
+}
+EXPORT_SYMBOL(snd_pcm_hw_constraint_ranges);
+
static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
- struct snd_pcm_hw_constraint_ratnums *r = rule->private;
+ const struct snd_pcm_hw_constraint_ratnums *r = rule->private;
unsigned int num = 0, den = 0;
int err;
err = snd_interval_ratnum(hw_param_interval(params, rule->var),
@@ -1230,23 +1376,24 @@ static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
* @cond: condition bits
* @var: hw_params variable to apply the ratnums constraint
* @r: struct snd_ratnums constriants
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime,
unsigned int cond,
snd_pcm_hw_param_t var,
- struct snd_pcm_hw_constraint_ratnums *r)
+ const struct snd_pcm_hw_constraint_ratnums *r)
{
return snd_pcm_hw_rule_add(runtime, cond, var,
- snd_pcm_hw_rule_ratnums, r,
+ snd_pcm_hw_rule_ratnums, (void *)r,
var, -1);
}
-
EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums);
static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
- struct snd_pcm_hw_constraint_ratdens *r = rule->private;
+ const struct snd_pcm_hw_constraint_ratdens *r = rule->private;
unsigned int num = 0, den = 0;
int err = snd_interval_ratden(hw_param_interval(params, rule->var),
r->nrats, r->rats, &num, &den);
@@ -1263,17 +1410,18 @@ static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params,
* @cond: condition bits
* @var: hw_params variable to apply the ratdens constraint
* @r: struct snd_ratdens constriants
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime,
unsigned int cond,
snd_pcm_hw_param_t var,
- struct snd_pcm_hw_constraint_ratdens *r)
+ const struct snd_pcm_hw_constraint_ratdens *r)
{
return snd_pcm_hw_rule_add(runtime, cond, var,
- snd_pcm_hw_rule_ratdens, r,
+ snd_pcm_hw_rule_ratdens, (void *)r,
var, -1);
}
-
EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens);
static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params,
@@ -1282,9 +1430,16 @@ static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params,
unsigned int l = (unsigned long) rule->private;
int width = l & 0xffff;
unsigned int msbits = l >> 16;
- struct snd_interval *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
- if (snd_interval_single(i) && snd_interval_value(i) == width)
- params->msbits = msbits;
+ const struct snd_interval *i =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
+
+ if (!snd_interval_single(i))
+ return 0;
+
+ if ((snd_interval_value(i) == width) ||
+ (width == 0 && snd_interval_value(i) > msbits))
+ params->msbits = min_not_zero(params->msbits, msbits);
+
return 0;
}
@@ -1294,6 +1449,13 @@ static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params,
* @cond: condition bits
* @width: sample bits width
* @msbits: msbits width
+ *
+ * This constraint will set the number of most significant bits (msbits) if a
+ * sample format with the specified width has been select. If width is set to 0
+ * the msbits will be set for any sample format with a width larger than the
+ * specified msbits.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime,
unsigned int cond,
@@ -1306,14 +1468,13 @@ int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime,
(void*) l,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1);
}
-
EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits);
static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
unsigned long step = (unsigned long) rule->private;
- return snd_interval_step(hw_param_interval(params, rule->var), 0, step);
+ return snd_interval_step(hw_param_interval(params, rule->var), step);
}
/**
@@ -1322,6 +1483,8 @@ static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params,
* @cond: condition bits
* @var: hw_params variable to apply the step constraint
* @step: step size
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime,
unsigned int cond,
@@ -1332,12 +1495,11 @@ int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime,
snd_pcm_hw_rule_step, (void *) step,
var, -1);
}
-
EXPORT_SYMBOL(snd_pcm_hw_constraint_step);
static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
{
- static unsigned int pow2_sizes[] = {
+ static const unsigned int pow2_sizes[] = {
1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7,
1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15,
1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23,
@@ -1352,6 +1514,8 @@ static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm
* @runtime: PCM runtime instance
* @cond: condition bits
* @var: hw_params variable to apply the power-of-2 constraint
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime,
unsigned int cond,
@@ -1361,9 +1525,36 @@ int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime,
snd_pcm_hw_rule_pow2, NULL,
var, -1);
}
-
EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2);
+static int snd_pcm_hw_rule_noresample_func(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ unsigned int base_rate = (unsigned int)(uintptr_t)rule->private;
+ struct snd_interval *rate;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ return snd_interval_list(rate, 1, &base_rate, 0);
+}
+
+/**
+ * snd_pcm_hw_rule_noresample - add a rule to allow disabling hw resampling
+ * @runtime: PCM runtime instance
+ * @base_rate: the rate at which the hardware does not resample
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_hw_rule_noresample(struct snd_pcm_runtime *runtime,
+ unsigned int base_rate)
+{
+ return snd_pcm_hw_rule_add(runtime, SNDRV_PCM_HW_PARAMS_NORESAMPLE,
+ SNDRV_PCM_HW_PARAM_RATE,
+ snd_pcm_hw_rule_noresample_func,
+ (void *)(uintptr_t)base_rate,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+}
+EXPORT_SYMBOL(snd_pcm_hw_rule_noresample);
+
static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var)
{
@@ -1392,7 +1583,6 @@ void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params)
_snd_pcm_hw_param_any(params, k);
params->info = ~0U;
}
-
EXPORT_SYMBOL(_snd_pcm_hw_params_any);
/**
@@ -1401,8 +1591,8 @@ EXPORT_SYMBOL(_snd_pcm_hw_params_any);
* @var: parameter to retrieve
* @dir: pointer to the direction (-1,0,1) or %NULL
*
- * Return the value for field @var if it's fixed in configuration space
- * defined by @params. Return -%EINVAL otherwise.
+ * Return: The value for field @var if it's fixed in configuration space
+ * defined by @params. -%EINVAL otherwise.
*/
int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, int *dir)
@@ -1425,7 +1615,6 @@ int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params,
}
return -EINVAL;
}
-
EXPORT_SYMBOL(snd_pcm_hw_param_value);
void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params,
@@ -1443,7 +1632,6 @@ void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params,
snd_BUG();
}
}
-
EXPORT_SYMBOL(_snd_pcm_hw_param_setempty);
static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params,
@@ -1456,7 +1644,7 @@ static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params,
changed = snd_interval_refine_first(hw_param_interval(params, var));
else
return -EINVAL;
- if (changed) {
+ if (changed > 0) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
}
@@ -1473,7 +1661,8 @@ static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params,
*
* Inside configuration space defined by @params remove from @var all
* values > minimum. Reduce configuration space accordingly.
- * Return the minimum.
+ *
+ * Return: The minimum, or a negative error code on failure.
*/
int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm,
struct snd_pcm_hw_params *params,
@@ -1484,12 +1673,11 @@ int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm,
return changed;
if (params->rmask) {
int err = snd_pcm_hw_refine(pcm, params);
- if (snd_BUG_ON(err < 0))
+ if (err < 0)
return err;
}
return snd_pcm_hw_param_value(params, var, dir);
}
-
EXPORT_SYMBOL(snd_pcm_hw_param_first);
static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params,
@@ -1502,7 +1690,7 @@ static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params,
changed = snd_interval_refine_last(hw_param_interval(params, var));
else
return -EINVAL;
- if (changed) {
+ if (changed > 0) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
}
@@ -1519,7 +1707,8 @@ static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params,
*
* Inside configuration space defined by @params remove from @var all
* values < maximum. Reduce configuration space accordingly.
- * Return the maximum.
+ *
+ * Return: The maximum, or a negative error code on failure.
*/
int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
struct snd_pcm_hw_params *params,
@@ -1530,63 +1719,60 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
return changed;
if (params->rmask) {
int err = snd_pcm_hw_refine(pcm, params);
- if (snd_BUG_ON(err < 0))
+ if (err < 0)
return err;
}
return snd_pcm_hw_param_value(params, var, dir);
}
-
EXPORT_SYMBOL(snd_pcm_hw_param_last);
/**
- * snd_pcm_hw_param_choose - choose a configuration defined by @params
- * @pcm: PCM instance
- * @params: the hw_params instance
+ * snd_pcm_hw_params_bits - Get the number of bits per the sample.
+ * @p: hardware parameters
*
- * Choose one configuration from configuration space defined by @params.
- * The configuration chosen is that obtained fixing in this order:
- * first access, first format, first subformat, min channels,
- * min rate, min period time, max buffer size, min tick time
+ * Return: The number of bits per sample based on the format,
+ * subformat and msbits the specified hw params has.
*/
-int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
- struct snd_pcm_hw_params *params)
-{
- static int vars[] = {
- SNDRV_PCM_HW_PARAM_ACCESS,
- SNDRV_PCM_HW_PARAM_FORMAT,
- SNDRV_PCM_HW_PARAM_SUBFORMAT,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- SNDRV_PCM_HW_PARAM_RATE,
- SNDRV_PCM_HW_PARAM_PERIOD_TIME,
- SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
- SNDRV_PCM_HW_PARAM_TICK_TIME,
- -1
- };
- int err, *v;
-
- for (v = vars; *v != -1; v++) {
- if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE)
- err = snd_pcm_hw_param_first(pcm, params, *v, NULL);
- else
- err = snd_pcm_hw_param_last(pcm, params, *v, NULL);
- if (snd_BUG_ON(err < 0))
- return err;
+int snd_pcm_hw_params_bits(const struct snd_pcm_hw_params *p)
+{
+ snd_pcm_subformat_t subformat = params_subformat(p);
+ snd_pcm_format_t format = params_format(p);
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_U32_LE:
+ case SNDRV_PCM_FORMAT_S32_BE:
+ case SNDRV_PCM_FORMAT_U32_BE:
+ switch (subformat) {
+ case SNDRV_PCM_SUBFORMAT_MSBITS_20:
+ return 20;
+ case SNDRV_PCM_SUBFORMAT_MSBITS_24:
+ return 24;
+ case SNDRV_PCM_SUBFORMAT_MSBITS_MAX:
+ case SNDRV_PCM_SUBFORMAT_STD:
+ default:
+ break;
+ }
+ fallthrough;
+ default:
+ return snd_pcm_format_width(format);
}
- return 0;
}
+EXPORT_SYMBOL(snd_pcm_hw_params_bits);
static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
void *arg)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long flags;
- snd_pcm_stream_lock_irqsave(substream, flags);
+
+ guard(pcm_stream_lock_irqsave)(substream);
if (snd_pcm_running(substream) &&
snd_pcm_update_hw_ptr(substream) >= 0)
runtime->status->hw_ptr %= runtime->buffer_size;
- else
+ else {
runtime->status->hw_ptr = 0;
- snd_pcm_stream_unlock_irqrestore(substream, flags);
+ runtime->hw_ptr_wrap = 0;
+ }
return 0;
}
@@ -1630,18 +1816,32 @@ static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream,
{
struct snd_pcm_hw_params *params = arg;
snd_pcm_format_t format;
- int channels, width;
+ int channels;
+ ssize_t frame_size;
params->fifo_size = substream->runtime->hw.fifo_size;
if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_FIFO_IN_FRAMES)) {
format = params_format(params);
channels = params_channels(params);
- width = snd_pcm_format_physical_width(format);
- params->fifo_size /= width * channels;
+ frame_size = snd_pcm_format_size(format, channels);
+ if (frame_size > 0)
+ params->fifo_size /= frame_size;
}
return 0;
}
+static int snd_pcm_lib_ioctl_sync_id(struct snd_pcm_substream *substream,
+ void *arg)
+{
+ static const unsigned char id[12] = { 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff };
+
+ if (substream->runtime->std_sync_id)
+ snd_pcm_set_sync_per_card(substream, arg, id, sizeof(id));
+ return 0;
+}
+
/**
* snd_pcm_lib_ioctl - a generic PCM ioctl callback
* @substream: the pcm substream instance
@@ -1651,63 +1851,93 @@ static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream,
* Processes the generic ioctl commands for PCM.
* Can be passed as the ioctl callback for PCM ops.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
switch (cmd) {
- case SNDRV_PCM_IOCTL1_INFO:
- return 0;
case SNDRV_PCM_IOCTL1_RESET:
return snd_pcm_lib_ioctl_reset(substream, arg);
case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
return snd_pcm_lib_ioctl_channel_info(substream, arg);
case SNDRV_PCM_IOCTL1_FIFO_SIZE:
return snd_pcm_lib_ioctl_fifo_size(substream, arg);
+ case SNDRV_PCM_IOCTL1_SYNC_ID:
+ return snd_pcm_lib_ioctl_sync_id(substream, arg);
}
return -ENXIO;
}
-
EXPORT_SYMBOL(snd_pcm_lib_ioctl);
/**
- * snd_pcm_period_elapsed - update the pcm status for the next period
- * @substream: the pcm substream instance
+ * snd_pcm_period_elapsed_under_stream_lock() - update the status of runtime for the next period
+ * under acquired lock of PCM substream.
+ * @substream: the instance of pcm substream.
+ *
+ * This function is called when the batch of audio data frames as the same size as the period of
+ * buffer is already processed in audio data transmission.
+ *
+ * The call of function updates the status of runtime with the latest position of audio data
+ * transmission, checks overrun and underrun over buffer, awaken user processes from waiting for
+ * available audio data frames, sampling audio timestamp, and performs stop or drain the PCM
+ * substream according to configured threshold.
+ *
+ * The function is intended to use for the case that PCM driver operates audio data frames under
+ * acquired lock of PCM substream; e.g. in callback of any operation of &snd_pcm_ops in process
+ * context. In any interrupt context, it's preferrable to use ``snd_pcm_period_elapsed()`` instead
+ * since lock of PCM substream should be acquired in advance.
+ *
+ * Developer should pay enough attention that some callbacks in &snd_pcm_ops are done by the call of
+ * function:
*
- * This function is called from the interrupt handler when the
- * PCM has processed the period size. It will update the current
- * pointer, wake up sleepers, etc.
+ * - .pointer - to retrieve current position of audio data transmission by frame count or XRUN state.
+ * - .trigger - with SNDRV_PCM_TRIGGER_STOP at XRUN or DRAINING state.
+ * - .get_time_info - to retrieve audio time stamp if needed.
*
- * Even if more than one periods have elapsed since the last call, you
- * have to call this only once.
+ * Even if more than one periods have elapsed since the last call, you have to call this only once.
*/
-void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
+void snd_pcm_period_elapsed_under_stream_lock(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
- unsigned long flags;
if (PCM_RUNTIME_CHECK(substream))
return;
runtime = substream->runtime;
- if (runtime->transfer_ack_begin)
- runtime->transfer_ack_begin(substream);
-
- snd_pcm_stream_lock_irqsave(substream, flags);
if (!snd_pcm_running(substream) ||
snd_pcm_update_hw_ptr0(substream, 1) < 0)
goto _end;
+#ifdef CONFIG_SND_PCM_TIMER
if (substream->timer_running)
snd_timer_interrupt(substream->timer, 1);
+#endif
_end:
- snd_pcm_stream_unlock_irqrestore(substream, flags);
- if (runtime->transfer_ack_end)
- runtime->transfer_ack_end(substream);
- kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
+ snd_kill_fasync(runtime->fasync, SIGIO, POLL_IN);
}
+EXPORT_SYMBOL(snd_pcm_period_elapsed_under_stream_lock);
+/**
+ * snd_pcm_period_elapsed() - update the status of runtime for the next period by acquiring lock of
+ * PCM substream.
+ * @substream: the instance of PCM substream.
+ *
+ * This function is mostly similar to ``snd_pcm_period_elapsed_under_stream_lock()`` except for
+ * acquiring lock of PCM substream voluntarily.
+ *
+ * It's typically called by any type of IRQ handler when hardware IRQ occurs to notify event that
+ * the batch of audio data frames as the same size as the period of buffer is already processed in
+ * audio data transmission.
+ */
+void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
+{
+ if (snd_BUG_ON(!substream))
+ return;
+
+ guard(pcm_stream_lock_irqsave)(substream);
+ snd_pcm_period_elapsed_under_stream_lock(substream);
+}
EXPORT_SYMBOL(snd_pcm_period_elapsed);
/*
@@ -1721,23 +1951,55 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
{
struct snd_pcm_runtime *runtime = substream->runtime;
int is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- wait_queue_t wait;
+ wait_queue_entry_t wait;
int err = 0;
snd_pcm_uframes_t avail = 0;
- long tout;
+ long wait_time, tout;
init_waitqueue_entry(&wait, current);
+ set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&runtime->tsleep, &wait);
+
+ if (runtime->no_period_wakeup)
+ wait_time = MAX_SCHEDULE_TIMEOUT;
+ else {
+ /* use wait time from substream if available */
+ if (substream->wait_time) {
+ wait_time = substream->wait_time;
+ } else {
+ wait_time = 100;
+
+ if (runtime->rate) {
+ long t = runtime->buffer_size * 1100 / runtime->rate;
+ wait_time = max(t, wait_time);
+ }
+ }
+ wait_time = msecs_to_jiffies(wait_time);
+ }
+
for (;;) {
if (signal_pending(current)) {
err = -ERESTARTSYS;
break;
}
- set_current_state(TASK_INTERRUPTIBLE);
+
+ /*
+ * We need to check if space became available already
+ * (and thus the wakeup happened already) first to close
+ * the race of space already having become available.
+ * This check must happen after been added to the waitqueue
+ * and having current state be INTERRUPTIBLE.
+ */
+ avail = snd_pcm_avail(substream);
+ if (avail >= runtime->twake)
+ break;
snd_pcm_stream_unlock_irq(substream);
- tout = schedule_timeout(msecs_to_jiffies(10000));
+
+ tout = schedule_timeout(wait_time);
+
snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ switch (runtime->state) {
case SNDRV_PCM_STATE_SUSPENDED:
err = -ESTRPIPE;
goto _endloop;
@@ -1755,148 +2017,178 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
case SNDRV_PCM_STATE_DISCONNECTED:
err = -EBADFD;
goto _endloop;
+ case SNDRV_PCM_STATE_PAUSED:
+ continue;
}
if (!tout) {
- snd_printd("%s write error (DMA or IRQ trouble?)\n",
- is_playback ? "playback" : "capture");
+ pcm_dbg(substream->pcm,
+ "%s timeout (DMA or IRQ trouble?)\n",
+ is_playback ? "playback write" : "capture read");
err = -EIO;
break;
}
- if (is_playback)
- avail = snd_pcm_playback_avail(runtime);
- else
- avail = snd_pcm_capture_avail(runtime);
- if (avail >= runtime->twake)
- break;
}
_endloop:
+ set_current_state(TASK_RUNNING);
remove_wait_queue(&runtime->tsleep, &wait);
*availp = avail;
return err;
}
-static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
- unsigned int hwoff,
- unsigned long data, unsigned int off,
- snd_pcm_uframes_t frames)
+typedef int (*pcm_transfer_f)(struct snd_pcm_substream *substream,
+ int channel, unsigned long hwoff,
+ struct iov_iter *iter, unsigned long bytes);
+
+typedef int (*pcm_copy_f)(struct snd_pcm_substream *, snd_pcm_uframes_t, void *,
+ snd_pcm_uframes_t, snd_pcm_uframes_t, pcm_transfer_f,
+ bool);
+
+/* calculate the target DMA-buffer position to be written/read */
+static void *get_dma_ptr(struct snd_pcm_runtime *runtime,
+ int channel, unsigned long hwoff)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int err;
- char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);
- if (substream->ops->copy) {
- if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
- return err;
- } else {
- char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
- if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))
- return -EFAULT;
- }
+ return runtime->dma_area + hwoff +
+ channel * (runtime->dma_bytes / runtime->channels);
+}
+
+/* default copy ops for write; used for both interleaved and non- modes */
+static int default_write_copy(struct snd_pcm_substream *substream,
+ int channel, unsigned long hwoff,
+ struct iov_iter *iter, unsigned long bytes)
+{
+ if (copy_from_iter(get_dma_ptr(substream->runtime, channel, hwoff),
+ bytes, iter) != bytes)
+ return -EFAULT;
return 0;
}
-
-typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff,
- unsigned long data, unsigned int off,
- snd_pcm_uframes_t size);
-static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
- unsigned long data,
- snd_pcm_uframes_t size,
- int nonblock,
- transfer_f transfer)
+/* fill silence instead of copy data; called as a transfer helper
+ * from __snd_pcm_lib_write() or directly from noninterleaved_copy() when
+ * a NULL buffer is passed
+ */
+static int fill_silence(struct snd_pcm_substream *substream, int channel,
+ unsigned long hwoff, struct iov_iter *iter,
+ unsigned long bytes)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_uframes_t xfer = 0;
- snd_pcm_uframes_t offset = 0;
- int err = 0;
- if (size == 0)
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
return 0;
+ if (substream->ops->fill_silence)
+ return substream->ops->fill_silence(substream, channel,
+ hwoff, bytes);
- snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_PREPARED:
- case SNDRV_PCM_STATE_RUNNING:
- case SNDRV_PCM_STATE_PAUSED:
- break;
- case SNDRV_PCM_STATE_XRUN:
- err = -EPIPE;
- goto _end_unlock;
- case SNDRV_PCM_STATE_SUSPENDED:
- err = -ESTRPIPE;
- goto _end_unlock;
- default:
- err = -EBADFD;
- goto _end_unlock;
+ snd_pcm_format_set_silence(runtime->format,
+ get_dma_ptr(runtime, channel, hwoff),
+ bytes_to_samples(runtime, bytes));
+ return 0;
+}
+
+/* default copy ops for read; used for both interleaved and non- modes */
+static int default_read_copy(struct snd_pcm_substream *substream,
+ int channel, unsigned long hwoff,
+ struct iov_iter *iter, unsigned long bytes)
+{
+ if (copy_to_iter(get_dma_ptr(substream->runtime, channel, hwoff),
+ bytes, iter) != bytes)
+ return -EFAULT;
+ return 0;
+}
+
+/* call transfer with the filled iov_iter */
+static int do_transfer(struct snd_pcm_substream *substream, int c,
+ unsigned long hwoff, void *data, unsigned long bytes,
+ pcm_transfer_f transfer, bool in_kernel)
+{
+ struct iov_iter iter;
+ int err, type;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ type = ITER_SOURCE;
+ else
+ type = ITER_DEST;
+
+ if (in_kernel) {
+ struct kvec kvec = { data, bytes };
+
+ iov_iter_kvec(&iter, type, &kvec, 1, bytes);
+ return transfer(substream, c, hwoff, &iter, bytes);
}
- runtime->twake = runtime->control->avail_min ? : 1;
- while (size > 0) {
- snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
- snd_pcm_uframes_t avail;
- snd_pcm_uframes_t cont;
- if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
- snd_pcm_update_hw_ptr(substream);
- avail = snd_pcm_playback_avail(runtime);
- if (!avail) {
- if (nonblock) {
- err = -EAGAIN;
- goto _end_unlock;
- }
- runtime->twake = min_t(snd_pcm_uframes_t, size,
- runtime->control->avail_min ? : 1);
- err = wait_for_avail(substream, &avail);
- if (err < 0)
- goto _end_unlock;
- }
- frames = size > avail ? avail : size;
- cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
- if (frames > cont)
- frames = cont;
- if (snd_BUG_ON(!frames)) {
- runtime->twake = 0;
- snd_pcm_stream_unlock_irq(substream);
- return -EINVAL;
- }
- appl_ptr = runtime->control->appl_ptr;
- appl_ofs = appl_ptr % runtime->buffer_size;
- snd_pcm_stream_unlock_irq(substream);
- err = transfer(substream, appl_ofs, data, offset, frames);
- snd_pcm_stream_lock_irq(substream);
- if (err < 0)
- goto _end_unlock;
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_XRUN:
- err = -EPIPE;
- goto _end_unlock;
- case SNDRV_PCM_STATE_SUSPENDED:
- err = -ESTRPIPE;
- goto _end_unlock;
- default:
- break;
- }
- appl_ptr += frames;
- if (appl_ptr >= runtime->boundary)
- appl_ptr -= runtime->boundary;
- runtime->control->appl_ptr = appl_ptr;
- if (substream->ops->ack)
- substream->ops->ack(substream);
+ err = import_ubuf(type, (__force void __user *)data, bytes, &iter);
+ if (err)
+ return err;
+ return transfer(substream, c, hwoff, &iter, bytes);
+}
- offset += frames;
- size -= frames;
- xfer += frames;
- if (runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
- snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {
- err = snd_pcm_start(substream);
- if (err < 0)
- goto _end_unlock;
- }
+/* call transfer function with the converted pointers and sizes;
+ * for interleaved mode, it's one shot for all samples
+ */
+static int interleaved_copy(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t hwoff, void *data,
+ snd_pcm_uframes_t off,
+ snd_pcm_uframes_t frames,
+ pcm_transfer_f transfer,
+ bool in_kernel)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ /* convert to bytes */
+ hwoff = frames_to_bytes(runtime, hwoff);
+ off = frames_to_bytes(runtime, off);
+ frames = frames_to_bytes(runtime, frames);
+
+ return do_transfer(substream, 0, hwoff, data + off, frames, transfer,
+ in_kernel);
+}
+
+/* call transfer function with the converted pointers and sizes for each
+ * non-interleaved channel; when buffer is NULL, silencing instead of copying
+ */
+static int noninterleaved_copy(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t hwoff, void *data,
+ snd_pcm_uframes_t off,
+ snd_pcm_uframes_t frames,
+ pcm_transfer_f transfer,
+ bool in_kernel)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int channels = runtime->channels;
+ void **bufs = data;
+ int c, err;
+
+ /* convert to bytes; note that it's not frames_to_bytes() here.
+ * in non-interleaved mode, we copy for each channel, thus
+ * each copy is n_samples bytes x channels = whole frames.
+ */
+ off = samples_to_bytes(runtime, off);
+ frames = samples_to_bytes(runtime, frames);
+ hwoff = samples_to_bytes(runtime, hwoff);
+ for (c = 0; c < channels; ++c, ++bufs) {
+ if (!data || !*bufs)
+ err = fill_silence(substream, c, hwoff, NULL, frames);
+ else
+ err = do_transfer(substream, c, hwoff, *bufs + off,
+ frames, transfer, in_kernel);
+ if (err < 0)
+ return err;
}
- _end_unlock:
- runtime->twake = 0;
- if (xfer > 0 && err >= 0)
- snd_pcm_update_state(substream, runtime);
- snd_pcm_stream_unlock_irq(substream);
- return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
+ return 0;
+}
+
+/* fill silence on the given buffer position;
+ * called from snd_pcm_playback_silence()
+ */
+static int fill_silence_frames(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t off, snd_pcm_uframes_t frames)
+{
+ if (substream->runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
+ substream->runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED)
+ return interleaved_copy(substream, off, NULL, 0, frames,
+ fill_silence, true);
+ else
+ return noninterleaved_copy(substream, off, NULL, 0, frames,
+ fill_silence, true);
}
/* sanity-check for read/write methods */
@@ -1908,162 +2200,151 @@ static int pcm_sanity_check(struct snd_pcm_substream *substream)
runtime = substream->runtime;
if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area))
return -EINVAL;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
return 0;
}
-snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size)
+static int pcm_accessible_state(struct snd_pcm_runtime *runtime)
{
- struct snd_pcm_runtime *runtime;
- int nonblock;
- int err;
-
- err = pcm_sanity_check(substream);
- if (err < 0)
- return err;
- runtime = substream->runtime;
- nonblock = !!(substream->f_flags & O_NONBLOCK);
-
- if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
- runtime->channels > 1)
- return -EINVAL;
- return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock,
- snd_pcm_lib_write_transfer);
+ switch (runtime->state) {
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_RUNNING:
+ case SNDRV_PCM_STATE_PAUSED:
+ return 0;
+ case SNDRV_PCM_STATE_XRUN:
+ return -EPIPE;
+ case SNDRV_PCM_STATE_SUSPENDED:
+ return -ESTRPIPE;
+ default:
+ return -EBADFD;
+ }
}
-EXPORT_SYMBOL(snd_pcm_lib_write);
-
-static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream,
- unsigned int hwoff,
- unsigned long data, unsigned int off,
- snd_pcm_uframes_t frames)
+/* update to the given appl_ptr and call ack callback if needed;
+ * when an error is returned, take back to the original value
+ */
+int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t appl_ptr)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- int err;
- void __user **bufs = (void __user **)data;
- int channels = runtime->channels;
- int c;
- if (substream->ops->copy) {
- if (snd_BUG_ON(!substream->ops->silence))
- return -EINVAL;
- for (c = 0; c < channels; ++c, ++bufs) {
- if (*bufs == NULL) {
- if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0)
- return err;
- } else {
- char __user *buf = *bufs + samples_to_bytes(runtime, off);
- if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0)
- return err;
- }
+ snd_pcm_uframes_t old_appl_ptr = runtime->control->appl_ptr;
+ snd_pcm_sframes_t diff;
+ int ret;
+
+ if (old_appl_ptr == appl_ptr)
+ return 0;
+
+ if (appl_ptr >= runtime->boundary)
+ return -EINVAL;
+ /*
+ * check if a rewind is requested by the application
+ */
+ if (substream->runtime->info & SNDRV_PCM_INFO_NO_REWINDS) {
+ diff = appl_ptr - old_appl_ptr;
+ if (diff >= 0) {
+ if (diff > runtime->buffer_size)
+ return -EINVAL;
+ } else {
+ if (runtime->boundary + diff > runtime->buffer_size)
+ return -EINVAL;
}
- } else {
- /* default transfer behaviour */
- size_t dma_csize = runtime->dma_bytes / channels;
- for (c = 0; c < channels; ++c, ++bufs) {
- char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
- if (*bufs == NULL) {
- snd_pcm_format_set_silence(runtime->format, hwbuf, frames);
- } else {
- char __user *buf = *bufs + samples_to_bytes(runtime, off);
- if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames)))
- return -EFAULT;
- }
+ }
+
+ runtime->control->appl_ptr = appl_ptr;
+ if (substream->ops->ack) {
+ ret = substream->ops->ack(substream);
+ if (ret < 0) {
+ runtime->control->appl_ptr = old_appl_ptr;
+ if (ret == -EPIPE)
+ __snd_pcm_xrun(substream);
+ return ret;
}
}
+
+ trace_applptr(substream, old_appl_ptr, appl_ptr);
+
return 0;
}
-
-snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream,
- void __user **bufs,
- snd_pcm_uframes_t frames)
+
+/* the common loop for read/write data */
+snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
+ void *data, bool interleaved,
+ snd_pcm_uframes_t size, bool in_kernel)
{
- struct snd_pcm_runtime *runtime;
- int nonblock;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_uframes_t xfer = 0;
+ snd_pcm_uframes_t offset = 0;
+ snd_pcm_uframes_t avail;
+ pcm_copy_f writer;
+ pcm_transfer_f transfer;
+ bool nonblock;
+ bool is_playback;
int err;
err = pcm_sanity_check(substream);
if (err < 0)
return err;
- runtime = substream->runtime;
- nonblock = !!(substream->f_flags & O_NONBLOCK);
- if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
- return -EINVAL;
- return snd_pcm_lib_write1(substream, (unsigned long)bufs, frames,
- nonblock, snd_pcm_lib_writev_transfer);
-}
-
-EXPORT_SYMBOL(snd_pcm_lib_writev);
-
-static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream,
- unsigned int hwoff,
- unsigned long data, unsigned int off,
- snd_pcm_uframes_t frames)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int err;
- char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);
- if (substream->ops->copy) {
- if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)
- return err;
+ is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ if (interleaved) {
+ if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
+ runtime->channels > 1)
+ return -EINVAL;
+ writer = interleaved_copy;
} else {
- char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
- if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))
- return -EFAULT;
+ if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
+ return -EINVAL;
+ writer = noninterleaved_copy;
}
- return 0;
-}
-static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
- unsigned long data,
- snd_pcm_uframes_t size,
- int nonblock,
- transfer_f transfer)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_uframes_t xfer = 0;
- snd_pcm_uframes_t offset = 0;
- int err = 0;
+ if (!data) {
+ if (is_playback)
+ transfer = fill_silence;
+ else
+ return -EINVAL;
+ } else {
+ if (substream->ops->copy)
+ transfer = substream->ops->copy;
+ else
+ transfer = is_playback ?
+ default_write_copy : default_read_copy;
+ }
if (size == 0)
return 0;
+ nonblock = !!(substream->f_flags & O_NONBLOCK);
+
snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_PREPARED:
- if (size >= runtime->start_threshold) {
- err = snd_pcm_start(substream);
- if (err < 0)
- goto _end_unlock;
- }
- break;
- case SNDRV_PCM_STATE_DRAINING:
- case SNDRV_PCM_STATE_RUNNING:
- case SNDRV_PCM_STATE_PAUSED:
- break;
- case SNDRV_PCM_STATE_XRUN:
- err = -EPIPE;
- goto _end_unlock;
- case SNDRV_PCM_STATE_SUSPENDED:
- err = -ESTRPIPE;
- goto _end_unlock;
- default:
- err = -EBADFD;
+ err = pcm_accessible_state(runtime);
+ if (err < 0)
goto _end_unlock;
- }
runtime->twake = runtime->control->avail_min ? : 1;
+ if (runtime->state == SNDRV_PCM_STATE_RUNNING)
+ snd_pcm_update_hw_ptr(substream);
+
+ /*
+ * If size < start_threshold, wait indefinitely. Another
+ * thread may start capture
+ */
+ if (!is_playback &&
+ runtime->state == SNDRV_PCM_STATE_PREPARED &&
+ size >= runtime->start_threshold) {
+ err = snd_pcm_start(substream);
+ if (err < 0)
+ goto _end_unlock;
+ }
+
+ avail = snd_pcm_avail(substream);
+
while (size > 0) {
snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
- snd_pcm_uframes_t avail;
snd_pcm_uframes_t cont;
- if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
- snd_pcm_update_hw_ptr(substream);
- avail = snd_pcm_capture_avail(runtime);
if (!avail) {
- if (runtime->status->state ==
- SNDRV_PCM_STATE_DRAINING) {
+ if (!is_playback &&
+ runtime->state == SNDRV_PCM_STATE_DRAINING) {
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
goto _end_unlock;
}
@@ -2080,41 +2361,51 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
continue; /* draining */
}
frames = size > avail ? avail : size;
- cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
+ appl_ptr = READ_ONCE(runtime->control->appl_ptr);
+ appl_ofs = appl_ptr % runtime->buffer_size;
+ cont = runtime->buffer_size - appl_ofs;
if (frames > cont)
frames = cont;
if (snd_BUG_ON(!frames)) {
- runtime->twake = 0;
- snd_pcm_stream_unlock_irq(substream);
- return -EINVAL;
+ err = -EINVAL;
+ goto _end_unlock;
+ }
+ if (!atomic_inc_unless_negative(&runtime->buffer_accessing)) {
+ err = -EBUSY;
+ goto _end_unlock;
}
- appl_ptr = runtime->control->appl_ptr;
- appl_ofs = appl_ptr % runtime->buffer_size;
snd_pcm_stream_unlock_irq(substream);
- err = transfer(substream, appl_ofs, data, offset, frames);
+ if (!is_playback)
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU);
+ err = writer(substream, appl_ofs, data, offset, frames,
+ transfer, in_kernel);
+ if (is_playback)
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
snd_pcm_stream_lock_irq(substream);
+ atomic_dec(&runtime->buffer_accessing);
if (err < 0)
goto _end_unlock;
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_XRUN:
- err = -EPIPE;
- goto _end_unlock;
- case SNDRV_PCM_STATE_SUSPENDED:
- err = -ESTRPIPE;
+ err = pcm_accessible_state(runtime);
+ if (err < 0)
goto _end_unlock;
- default:
- break;
- }
appl_ptr += frames;
if (appl_ptr >= runtime->boundary)
appl_ptr -= runtime->boundary;
- runtime->control->appl_ptr = appl_ptr;
- if (substream->ops->ack)
- substream->ops->ack(substream);
+ err = pcm_lib_apply_appl_ptr(substream, appl_ptr);
+ if (err < 0)
+ goto _end_unlock;
offset += frames;
size -= frames;
xfer += frames;
+ avail -= frames;
+ if (is_playback &&
+ runtime->state == SNDRV_PCM_STATE_PREPARED &&
+ snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {
+ err = snd_pcm_start(substream);
+ if (err < 0)
+ goto _end_unlock;
+ }
}
_end_unlock:
runtime->twake = 0;
@@ -2123,80 +2414,219 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
snd_pcm_stream_unlock_irq(substream);
return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
}
+EXPORT_SYMBOL(__snd_pcm_lib_xfer);
+
+/*
+ * standard channel mapping helpers
+ */
+
+/* default channel maps for multi-channel playbacks, up to 8 channels */
+const struct snd_pcm_chmap_elem snd_pcm_std_chmaps[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 6,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
+ { .channels = 8,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
+ { }
+};
+EXPORT_SYMBOL_GPL(snd_pcm_std_chmaps);
+
+/* alternative channel maps with CLFE <-> surround swapped for 6/8 channels */
+const struct snd_pcm_chmap_elem snd_pcm_alt_chmaps[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 6,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 8,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+ SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
+ { }
+};
+EXPORT_SYMBOL_GPL(snd_pcm_alt_chmaps);
-snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size)
+static bool valid_chmap_channels(const struct snd_pcm_chmap *info, int ch)
{
- struct snd_pcm_runtime *runtime;
- int nonblock;
- int err;
-
- err = pcm_sanity_check(substream);
- if (err < 0)
- return err;
- runtime = substream->runtime;
- nonblock = !!(substream->f_flags & O_NONBLOCK);
- if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)
- return -EINVAL;
- return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);
+ if (ch > info->max_channels)
+ return false;
+ return !info->channel_mask || (info->channel_mask & (1U << ch));
}
-EXPORT_SYMBOL(snd_pcm_lib_read);
+static int pcm_chmap_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = info->max_channels;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SNDRV_CHMAP_LAST;
+ return 0;
+}
-static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream,
- unsigned int hwoff,
- unsigned long data, unsigned int off,
- snd_pcm_uframes_t frames)
+/* get callback for channel map ctl element
+ * stores the channel position firstly matching with the current channels
+ */
+static int pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int err;
- void __user **bufs = (void __user **)data;
- int channels = runtime->channels;
- int c;
- if (substream->ops->copy) {
- for (c = 0; c < channels; ++c, ++bufs) {
- char __user *buf;
- if (*bufs == NULL)
- continue;
- buf = *bufs + samples_to_bytes(runtime, off);
- if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0)
- return err;
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ struct snd_pcm_substream *substream;
+ const struct snd_pcm_chmap_elem *map;
+
+ if (!info->chmap)
+ return -EINVAL;
+ substream = snd_pcm_chmap_substream(info, idx);
+ if (!substream)
+ return -ENODEV;
+ memset(ucontrol->value.integer.value, 0,
+ sizeof(long) * info->max_channels);
+ if (!substream->runtime)
+ return 0; /* no channels set */
+ for (map = info->chmap; map->channels; map++) {
+ int i;
+ if (map->channels == substream->runtime->channels &&
+ valid_chmap_channels(info, map->channels)) {
+ for (i = 0; i < map->channels; i++)
+ ucontrol->value.integer.value[i] = map->map[i];
+ return 0;
}
- } else {
- snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels;
- for (c = 0; c < channels; ++c, ++bufs) {
- char *hwbuf;
- char __user *buf;
- if (*bufs == NULL)
- continue;
-
- hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);
- buf = *bufs + samples_to_bytes(runtime, off);
- if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames)))
+ }
+ return -EINVAL;
+}
+
+/* tlv callback for channel map ctl element
+ * expands the pre-defined channel maps in a form of TLV
+ */
+static int pcm_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ const struct snd_pcm_chmap_elem *map;
+ unsigned int __user *dst;
+ int c, count = 0;
+
+ if (!info->chmap)
+ return -EINVAL;
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
+ return -EFAULT;
+ size -= 8;
+ dst = tlv + 2;
+ for (map = info->chmap; map->channels; map++) {
+ int chs_bytes = map->channels * 4;
+ if (!valid_chmap_channels(info, map->channels))
+ continue;
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CHMAP_FIXED, dst) ||
+ put_user(chs_bytes, dst + 1))
+ return -EFAULT;
+ dst += 2;
+ size -= 8;
+ count += 8;
+ if (size < chs_bytes)
+ return -ENOMEM;
+ size -= chs_bytes;
+ count += chs_bytes;
+ for (c = 0; c < map->channels; c++) {
+ if (put_user(map->map[c], dst))
return -EFAULT;
+ dst++;
}
}
+ if (put_user(count, tlv + 1))
+ return -EFAULT;
return 0;
}
-
-snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream,
- void __user **bufs,
- snd_pcm_uframes_t frames)
+
+static void pcm_chmap_ctl_private_free(struct snd_kcontrol *kcontrol)
{
- struct snd_pcm_runtime *runtime;
- int nonblock;
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ info->pcm->streams[info->stream].chmap_kctl = NULL;
+ kfree(info);
+}
+
+/**
+ * snd_pcm_add_chmap_ctls - create channel-mapping control elements
+ * @pcm: the assigned PCM instance
+ * @stream: stream direction
+ * @chmap: channel map elements (for query)
+ * @max_channels: the max number of channels for the stream
+ * @private_value: the value passed to each kcontrol's private_value field
+ * @info_ret: store struct snd_pcm_chmap instance if non-NULL
+ *
+ * Create channel-mapping control elements assigned to the given PCM stream(s).
+ * Return: Zero if successful, or a negative error value.
+ */
+int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
+ const struct snd_pcm_chmap_elem *chmap,
+ int max_channels,
+ unsigned long private_value,
+ struct snd_pcm_chmap **info_ret)
+{
+ struct snd_pcm_chmap *info;
+ struct snd_kcontrol_new knew = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
+ .info = pcm_chmap_ctl_info,
+ .get = pcm_chmap_ctl_get,
+ .tlv.c = pcm_chmap_ctl_tlv,
+ };
int err;
- err = pcm_sanity_check(substream);
+ if (WARN_ON(pcm->streams[stream].chmap_kctl))
+ return -EBUSY;
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->pcm = pcm;
+ info->stream = stream;
+ info->chmap = chmap;
+ info->max_channels = max_channels;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ knew.name = "Playback Channel Map";
+ else
+ knew.name = "Capture Channel Map";
+ knew.device = pcm->device;
+ knew.count = pcm->streams[stream].substream_count;
+ knew.private_value = private_value;
+ info->kctl = snd_ctl_new1(&knew, info);
+ if (!info->kctl) {
+ kfree(info);
+ return -ENOMEM;
+ }
+ info->kctl->private_free = pcm_chmap_ctl_private_free;
+ err = snd_ctl_add(pcm->card, info->kctl);
if (err < 0)
return err;
- runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
- return -EBADFD;
-
- nonblock = !!(substream->f_flags & O_NONBLOCK);
- if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
- return -EINVAL;
- return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer);
+ pcm->streams[stream].chmap_kctl = info->kctl;
+ if (info_ret)
+ *info_ret = info;
+ return 0;
}
-
-EXPORT_SYMBOL(snd_pcm_lib_readv);
+EXPORT_SYMBOL_GPL(snd_pcm_add_chmap_ctls);
diff --git a/sound/core/pcm_local.h b/sound/core/pcm_local.h
new file mode 100644
index 000000000000..ecb21697ae3a
--- /dev/null
+++ b/sound/core/pcm_local.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * pcm_local.h - a local header file for snd-pcm module.
+ *
+ * Copyright (c) Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#ifndef __SOUND_CORE_PCM_LOCAL_H
+#define __SOUND_CORE_PCM_LOCAL_H
+
+extern const struct snd_pcm_hw_constraint_list snd_pcm_known_rates;
+
+void snd_interval_mul(const struct snd_interval *a,
+ const struct snd_interval *b, struct snd_interval *c);
+void snd_interval_div(const struct snd_interval *a,
+ const struct snd_interval *b, struct snd_interval *c);
+void snd_interval_muldivk(const struct snd_interval *a,
+ const struct snd_interval *b,
+ unsigned int k, struct snd_interval *c);
+void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
+ const struct snd_interval *b, struct snd_interval *c);
+
+int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime,
+ snd_pcm_hw_param_t var, u_int32_t mask);
+
+int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t appl_ptr);
+int snd_pcm_update_state(struct snd_pcm_substream *substream,
+ struct snd_pcm_runtime *runtime);
+int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream);
+
+void snd_pcm_playback_silence(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t new_hw_ptr);
+
+static inline snd_pcm_uframes_t
+snd_pcm_avail(struct snd_pcm_substream *substream)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return snd_pcm_playback_avail(substream->runtime);
+ else
+ return snd_pcm_capture_avail(substream->runtime);
+}
+
+static inline snd_pcm_uframes_t
+snd_pcm_hw_avail(struct snd_pcm_substream *substream)
+{
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return snd_pcm_playback_hw_avail(substream->runtime);
+ else
+ return snd_pcm_capture_hw_avail(substream->runtime);
+}
+
+#ifdef CONFIG_SND_PCM_TIMER
+void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream);
+void snd_pcm_timer_init(struct snd_pcm_substream *substream);
+void snd_pcm_timer_done(struct snd_pcm_substream *substream);
+#else
+static inline void
+snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) {}
+static inline void snd_pcm_timer_init(struct snd_pcm_substream *substream) {}
+static inline void snd_pcm_timer_done(struct snd_pcm_substream *substream) {}
+#endif
+
+void __snd_pcm_xrun(struct snd_pcm_substream *substream);
+void snd_pcm_group_init(struct snd_pcm_group *group);
+void snd_pcm_sync_stop(struct snd_pcm_substream *substream, bool sync_irq);
+
+#define PCM_RUNTIME_CHECK(sub) snd_BUG_ON(!(sub) || !(sub)->runtime)
+
+/* loop over all PCM substreams */
+#define for_each_pcm_substream(pcm, str, subs) \
+ for ((str) = 0; (str) < 2; (str)++) \
+ for ((subs) = (pcm)->streams[str].substream; (subs); \
+ (subs) = (subs)->next)
+
+static inline void snd_pcm_dma_buffer_sync(struct snd_pcm_substream *substream,
+ enum snd_dma_sync_mode mode)
+{
+ if (substream->runtime->info & SNDRV_PCM_INFO_EXPLICIT_SYNC)
+ snd_dma_buffer_sync(snd_pcm_get_dma_buf(substream), mode);
+}
+
+#endif /* __SOUND_CORE_PCM_LOCAL_H */
diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
index 917e4055ee30..56725d36825b 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.c
@@ -1,34 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Digital Audio (PCM) abstract layer
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/time.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/moduleparam.h>
-#include <linux/vmalloc.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/info.h>
#include <sound/initval.h>
+#include "pcm_local.h"
static int preallocate_dma = 1;
module_param(preallocate_dma, int, 0444);
@@ -40,6 +26,68 @@ MODULE_PARM_DESC(maximum_substreams, "Maximum substreams with preallocated DMA m
static const size_t snd_minimum_buffer = 16384;
+static unsigned long max_alloc_per_card = 32UL * 1024UL * 1024UL;
+module_param(max_alloc_per_card, ulong, 0644);
+MODULE_PARM_DESC(max_alloc_per_card, "Max total allocation bytes per card.");
+
+static void __update_allocated_size(struct snd_card *card, ssize_t bytes)
+{
+ card->total_pcm_alloc_bytes += bytes;
+}
+
+static void update_allocated_size(struct snd_card *card, ssize_t bytes)
+{
+ guard(mutex)(&card->memory_mutex);
+ __update_allocated_size(card, bytes);
+}
+
+static void decrease_allocated_size(struct snd_card *card, size_t bytes)
+{
+ guard(mutex)(&card->memory_mutex);
+ WARN_ON(card->total_pcm_alloc_bytes < bytes);
+ __update_allocated_size(card, -(ssize_t)bytes);
+}
+
+static int do_alloc_pages(struct snd_card *card, int type, struct device *dev,
+ int str, size_t size, struct snd_dma_buffer *dmab)
+{
+ enum dma_data_direction dir;
+ int err;
+
+ /* check and reserve the requested size */
+ scoped_guard(mutex, &card->memory_mutex) {
+ if (max_alloc_per_card &&
+ card->total_pcm_alloc_bytes + size > max_alloc_per_card)
+ return -ENOMEM;
+ __update_allocated_size(card, size);
+ }
+
+ if (str == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = DMA_TO_DEVICE;
+ else
+ dir = DMA_FROM_DEVICE;
+ err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab);
+ if (!err) {
+ /* the actual allocation size might be bigger than requested,
+ * and we need to correct the account
+ */
+ if (dmab->bytes != size)
+ update_allocated_size(card, dmab->bytes - size);
+ } else {
+ /* take back on allocation failure */
+ decrease_allocated_size(card, size);
+ }
+ return err;
+}
+
+static void do_free_pages(struct snd_card *card, struct snd_dma_buffer *dmab)
+{
+ if (!dmab->area)
+ return;
+ decrease_allocated_size(card, dmab->bytes);
+ snd_dma_free_pages(dmab);
+ dmab->area = NULL;
+}
/*
* try to allocate as the large pages as possible.
@@ -47,45 +95,29 @@ static const size_t snd_minimum_buffer = 16384;
*
* the minimum size is snd_minimum_buffer. it should be power of 2.
*/
-static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t size)
+static int preallocate_pcm_pages(struct snd_pcm_substream *substream,
+ size_t size, bool no_fallback)
{
struct snd_dma_buffer *dmab = &substream->dma_buffer;
+ struct snd_card *card = substream->pcm->card;
+ size_t orig_size = size;
int err;
- /* already reserved? */
- if (snd_dma_get_reserved_buf(dmab, substream->dma_buf_id) > 0) {
- if (dmab->bytes >= size)
- return 0; /* yes */
- /* no, free the reserved block */
- snd_dma_free_pages(dmab);
- dmab->bytes = 0;
- }
-
do {
- if ((err = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev,
- size, dmab)) < 0) {
- if (err != -ENOMEM)
- return err; /* fatal error */
- } else
- return 0;
+ err = do_alloc_pages(card, dmab->dev.type, dmab->dev.dev,
+ substream->stream, size, dmab);
+ if (err != -ENOMEM)
+ return err;
+ if (no_fallback)
+ break;
size >>= 1;
} while (size >= snd_minimum_buffer);
dmab->bytes = 0; /* tell error */
- return 0;
-}
-
-/*
- * release the preallocated buffer if not yet done.
- */
-static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream)
-{
- if (substream->dma_buffer.area == NULL)
- return;
- if (substream->dma_buf_id)
- snd_dma_reserve_buf(&substream->dma_buffer, substream->dma_buf_id);
- else
- snd_dma_free_pages(&substream->dma_buffer);
- substream->dma_buffer.area = NULL;
+ pr_warn("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n",
+ substream->pcm->card->number, substream->pcm->device,
+ substream->stream ? 'c' : 'p', substream->number,
+ substream->pcm->name, orig_size);
+ return -ENOMEM;
}
/**
@@ -93,19 +125,10 @@ static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream
* @substream: the pcm substream instance
*
* Releases the pre-allocated buffer of the given substream.
- *
- * Returns zero if successful, or a negative error code on failure.
*/
-int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
+void snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
{
- snd_pcm_lib_preallocate_dma_free(substream);
-#ifdef CONFIG_SND_VERBOSE_PROCFS
- snd_info_free_entry(substream->proc_prealloc_max_entry);
- substream->proc_prealloc_max_entry = NULL;
- snd_info_free_entry(substream->proc_prealloc_entry);
- substream->proc_prealloc_entry = NULL;
-#endif
- return 0;
+ do_free_pages(substream->pcm->card, &substream->dma_buffer);
}
/**
@@ -113,20 +136,15 @@ int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
* @pcm: the pcm instance
*
* Releases all the pre-allocated buffers on the given pcm.
- *
- * Returns zero if successful, or a negative error code on failure.
*/
-int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm)
+void snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
int stream;
- for (stream = 0; stream < 2; stream++)
- for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
- snd_pcm_lib_preallocate_free(substream);
- return 0;
+ for_each_pcm_substream(pcm, stream, substream)
+ snd_pcm_lib_preallocate_free(substream);
}
-
EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all);
#ifdef CONFIG_SND_VERBOSE_PROCFS
@@ -163,17 +181,22 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_pcm_substream *substream = entry->private_data;
+ struct snd_card *card = substream->pcm->card;
char line[64], str[64];
- size_t size;
+ unsigned long size;
struct snd_dma_buffer new_dmab;
+ guard(mutex)(&substream->pcm->open_mutex);
if (substream->runtime) {
buffer->error = -EBUSY;
return;
}
if (!snd_info_get_line(buffer, line, sizeof(line))) {
snd_info_get_str(str, line, sizeof(str));
- size = simple_strtoul(str, NULL, 10) * 1024;
+ buffer->error = kstrtoul(str, 10, &size);
+ if (buffer->error != 0)
+ return;
+ size *= 1024;
if ((size != 0 && size < 8192) || size > substream->dma_max) {
buffer->error = -EINVAL;
return;
@@ -183,10 +206,16 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,
memset(&new_dmab, 0, sizeof(new_dmab));
new_dmab.dev = substream->dma_buffer.dev;
if (size > 0) {
- if (snd_dma_alloc_pages(substream->dma_buffer.dev.type,
- substream->dma_buffer.dev.dev,
- size, &new_dmab) < 0) {
+ if (do_alloc_pages(card,
+ substream->dma_buffer.dev.type,
+ substream->dma_buffer.dev.dev,
+ substream->stream,
+ size, &new_dmab) < 0) {
buffer->error = -ENOMEM;
+ pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %lu\n",
+ substream->pcm->card->number, substream->pcm->device,
+ substream->stream ? 'c' : 'p', substream->number,
+ substream->pcm->name, size);
return;
}
substream->buffer_bytes_max = size;
@@ -194,7 +223,7 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,
substream->buffer_bytes_max = UINT_MAX;
}
if (substream->dma_buffer.area)
- snd_dma_free_pages(&substream->dma_buffer);
+ do_free_pages(card, &substream->dma_buffer);
substream->dma_buffer = new_dmab;
} else {
buffer->error = -EINVAL;
@@ -205,154 +234,177 @@ static inline void preallocate_info_init(struct snd_pcm_substream *substream)
{
struct snd_info_entry *entry;
- if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc", substream->proc_root)) != NULL) {
- entry->c.text.read = snd_pcm_lib_preallocate_proc_read;
+ entry = snd_info_create_card_entry(substream->pcm->card, "prealloc",
+ substream->proc_root);
+ if (entry) {
+ snd_info_set_text_ops(entry, substream,
+ snd_pcm_lib_preallocate_proc_read);
entry->c.text.write = snd_pcm_lib_preallocate_proc_write;
- entry->mode |= S_IWUSR;
- entry->private_data = substream;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- substream->proc_prealloc_entry = entry;
- if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc_max", substream->proc_root)) != NULL) {
- entry->c.text.read = snd_pcm_lib_preallocate_max_proc_read;
- entry->private_data = substream;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
+ entry->mode |= 0200;
}
- substream->proc_prealloc_max_entry = entry;
+ entry = snd_info_create_card_entry(substream->pcm->card, "prealloc_max",
+ substream->proc_root);
+ if (entry)
+ snd_info_set_text_ops(entry, substream,
+ snd_pcm_lib_preallocate_max_proc_read);
}
#else /* !CONFIG_SND_VERBOSE_PROCFS */
-#define preallocate_info_init(s)
+static inline void preallocate_info_init(struct snd_pcm_substream *substream)
+{
+}
#endif /* CONFIG_SND_VERBOSE_PROCFS */
/*
* pre-allocate the buffer and create a proc file for the substream
*/
-static int snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream,
- size_t size, size_t max)
+static int preallocate_pages(struct snd_pcm_substream *substream,
+ int type, struct device *data,
+ size_t size, size_t max, bool managed)
{
+ int err;
+
+ if (snd_BUG_ON(substream->dma_buffer.dev.type))
+ return -EINVAL;
+
+ substream->dma_buffer.dev.type = type;
+ substream->dma_buffer.dev.dev = data;
- if (size > 0 && preallocate_dma && substream->number < maximum_substreams)
- preallocate_pcm_pages(substream, size);
+ if (size > 0) {
+ if (!max) {
+ /* no fallback, only also inform -ENOMEM */
+ err = preallocate_pcm_pages(substream, size, true);
+ if (err < 0)
+ return err;
+ } else if (preallocate_dma &&
+ substream->number < maximum_substreams) {
+ err = preallocate_pcm_pages(substream, size, false);
+ if (err < 0 && err != -ENOMEM)
+ return err;
+ }
+ }
if (substream->dma_buffer.bytes > 0)
substream->buffer_bytes_max = substream->dma_buffer.bytes;
substream->dma_max = max;
- preallocate_info_init(substream);
+ if (max > 0)
+ preallocate_info_init(substream);
+ if (managed)
+ substream->managed_buffer_alloc = 1;
return 0;
}
+static int preallocate_pages_for_all(struct snd_pcm *pcm, int type,
+ void *data, size_t size, size_t max,
+ bool managed)
+{
+ struct snd_pcm_substream *substream;
+ int stream, err;
+
+ for_each_pcm_substream(pcm, stream, substream) {
+ err = preallocate_pages(substream, type, data, size, max, managed);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
/**
* snd_pcm_lib_preallocate_pages - pre-allocation for the given DMA type
* @substream: the pcm substream instance
* @type: DMA type (SNDRV_DMA_TYPE_*)
- * @data: DMA type dependant data
+ * @data: DMA type dependent data
* @size: the requested pre-allocation size in bytes
* @max: the max. allowed pre-allocation size
*
* Do pre-allocation for the given DMA buffer type.
- *
- * When substream->dma_buf_id is set, the function tries to look for
- * the reserved buffer, and the buffer is not freed but reserved at
- * destruction time. The dma_buf_id must be unique for all systems
- * (in the same DMA buffer type) e.g. using snd_dma_pci_buf_id().
- *
- * Returns zero if successful, or a negative error code on failure.
*/
-int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
+void snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
int type, struct device *data,
size_t size, size_t max)
{
- substream->dma_buffer.dev.type = type;
- substream->dma_buffer.dev.dev = data;
- return snd_pcm_lib_preallocate_pages1(substream, size, max);
+ preallocate_pages(substream, type, data, size, max, false);
}
-
EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages);
/**
- * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continous memory type (all substreams)
+ * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continuous memory type (all substreams)
* @pcm: the pcm instance
* @type: DMA type (SNDRV_DMA_TYPE_*)
- * @data: DMA type dependant data
+ * @data: DMA type dependent data
* @size: the requested pre-allocation size in bytes
* @max: the max. allowed pre-allocation size
*
* Do pre-allocation to all substreams of the given pcm for the
* specified DMA type.
- *
- * Returns zero if successful, or a negative error code on failure.
*/
-int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
+void snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
int type, void *data,
size_t size, size_t max)
{
- struct snd_pcm_substream *substream;
- int stream, err;
-
- for (stream = 0; stream < 2; stream++)
- for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
- if ((err = snd_pcm_lib_preallocate_pages(substream, type, data, size, max)) < 0)
- return err;
- return 0;
+ preallocate_pages_for_all(pcm, type, data, size, max, false);
}
-
EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);
-#ifdef CONFIG_SND_DMA_SGBUF
/**
- * snd_pcm_sgbuf_ops_page - get the page struct at the given offset
+ * snd_pcm_set_managed_buffer - set up buffer management for a substream
* @substream: the pcm substream instance
- * @offset: the buffer offset
+ * @type: DMA type (SNDRV_DMA_TYPE_*)
+ * @data: DMA type dependent data
+ * @size: the requested pre-allocation size in bytes
+ * @max: the max. allowed pre-allocation size
+ *
+ * Do pre-allocation for the given DMA buffer type, and set the managed
+ * buffer allocation mode to the given substream.
+ * In this mode, PCM core will allocate a buffer automatically before PCM
+ * hw_params ops call, and release the buffer after PCM hw_free ops call
+ * as well, so that the driver doesn't need to invoke the allocation and
+ * the release explicitly in its callback.
+ * When a buffer is actually allocated before the PCM hw_params call, it
+ * turns on the runtime buffer_changed flag for drivers changing their h/w
+ * parameters accordingly.
+ *
+ * When @size is non-zero and @max is zero, this tries to allocate for only
+ * the exact buffer size without fallback, and may return -ENOMEM.
+ * Otherwise, the function tries to allocate smaller chunks if the allocation
+ * fails. This is the behavior of snd_pcm_set_fixed_buffer().
*
- * Returns the page struct at the given buffer offset.
- * Used as the page callback of PCM ops.
+ * When both @size and @max are zero, the function only sets up the buffer
+ * for later dynamic allocations. It's used typically for buffers with
+ * SNDRV_DMA_TYPE_VMALLOC type.
+ *
+ * Upon successful buffer allocation and setup, the function returns 0.
+ *
+ * Return: zero if successful, or a negative error code
*/
-struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigned long offset)
+int snd_pcm_set_managed_buffer(struct snd_pcm_substream *substream, int type,
+ struct device *data, size_t size, size_t max)
{
- struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
-
- unsigned int idx = offset >> PAGE_SHIFT;
- if (idx >= (unsigned int)sgbuf->pages)
- return NULL;
- return sgbuf->page_table[idx];
+ return preallocate_pages(substream, type, data, size, max, true);
}
+EXPORT_SYMBOL(snd_pcm_set_managed_buffer);
-EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
-
-/*
- * compute the max chunk size with continuous pages on sg-buffer
+/**
+ * snd_pcm_set_managed_buffer_all - set up buffer management for all substreams
+ * for all substreams
+ * @pcm: the pcm instance
+ * @type: DMA type (SNDRV_DMA_TYPE_*)
+ * @data: DMA type dependent data
+ * @size: the requested pre-allocation size in bytes
+ * @max: the max. allowed pre-allocation size
+ *
+ * Do pre-allocation to all substreams of the given pcm for the specified DMA
+ * type and size, and set the managed_buffer_alloc flag to each substream.
+ *
+ * Return: zero if successful, or a negative error code
*/
-unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
- unsigned int ofs, unsigned int size)
+int snd_pcm_set_managed_buffer_all(struct snd_pcm *pcm, int type,
+ struct device *data,
+ size_t size, size_t max)
{
- struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream);
- unsigned int start, end, pg;
-
- start = ofs >> PAGE_SHIFT;
- end = (ofs + size - 1) >> PAGE_SHIFT;
- /* check page continuity */
- pg = sg->table[start].addr >> PAGE_SHIFT;
- for (;;) {
- start++;
- if (start > end)
- break;
- pg++;
- if ((sg->table[start].addr >> PAGE_SHIFT) != pg)
- return (start << PAGE_SHIFT) - ofs;
- }
- /* ok, all on continuous pages */
- return size;
+ return preallocate_pages_for_all(pcm, type, data, size, max, true);
}
-EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size);
-#endif /* CONFIG_SND_DMA_SGBUF */
+EXPORT_SYMBOL(snd_pcm_set_managed_buffer_all);
/**
* snd_pcm_lib_malloc_pages - allocate the DMA buffer
@@ -362,11 +414,12 @@ EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size);
* Allocates the DMA buffer on the BUS type given earlier to
* snd_pcm_lib_preallocate_xxx_pages().
*
- * Returns 1 if the buffer is changed, 0 if not changed, or a negative
+ * Return: 1 if the buffer is changed, 0 if not changed, or a negative
* code on failure.
*/
int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
{
+ struct snd_card *card;
struct snd_pcm_runtime *runtime;
struct snd_dma_buffer *dmab = NULL;
@@ -376,6 +429,7 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
SNDRV_DMA_TYPE_UNKNOWN))
return -EINVAL;
runtime = substream->runtime;
+ card = substream->pcm->card;
if (runtime->dma_buffer_p) {
/* perphaps, we might free the large DMA memory region
@@ -391,14 +445,23 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
substream->dma_buffer.bytes >= size) {
dmab = &substream->dma_buffer; /* use the pre-allocated buffer */
} else {
+ /* dma_max=0 means the fixed size preallocation */
+ if (substream->dma_buffer.area && !substream->dma_max)
+ return -ENOMEM;
dmab = kzalloc(sizeof(*dmab), GFP_KERNEL);
if (! dmab)
return -ENOMEM;
dmab->dev = substream->dma_buffer.dev;
- if (snd_dma_alloc_pages(substream->dma_buffer.dev.type,
- substream->dma_buffer.dev.dev,
- size, dmab) < 0) {
+ if (do_alloc_pages(card,
+ substream->dma_buffer.dev.type,
+ substream->dma_buffer.dev.dev,
+ substream->stream,
+ size, dmab) < 0) {
kfree(dmab);
+ pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot allocate for size %zu\n",
+ substream->pcm->card->number, substream->pcm->device,
+ substream->stream ? 'c' : 'p', substream->number,
+ substream->pcm->name, size);
return -ENOMEM;
}
}
@@ -406,7 +469,6 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
runtime->dma_bytes = size;
return 1; /* area was changed */
}
-
EXPORT_SYMBOL(snd_pcm_lib_malloc_pages);
/**
@@ -415,7 +477,7 @@ EXPORT_SYMBOL(snd_pcm_lib_malloc_pages);
*
* Releases the DMA buffer allocated via snd_pcm_lib_malloc_pages().
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)
{
@@ -427,66 +489,13 @@ int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)
if (runtime->dma_area == NULL)
return 0;
if (runtime->dma_buffer_p != &substream->dma_buffer) {
+ struct snd_card *card = substream->pcm->card;
+
/* it's a newly allocated buffer. release it now. */
- snd_dma_free_pages(runtime->dma_buffer_p);
+ do_free_pages(card, runtime->dma_buffer_p);
kfree(runtime->dma_buffer_p);
}
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
-
EXPORT_SYMBOL(snd_pcm_lib_free_pages);
-
-int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream,
- size_t size, gfp_t gfp_flags)
-{
- struct snd_pcm_runtime *runtime;
-
- if (PCM_RUNTIME_CHECK(substream))
- return -EINVAL;
- runtime = substream->runtime;
- if (runtime->dma_area) {
- if (runtime->dma_bytes >= size)
- return 0; /* already large enough */
- vfree(runtime->dma_area);
- }
- runtime->dma_area = __vmalloc(size, gfp_flags, PAGE_KERNEL);
- if (!runtime->dma_area)
- return -ENOMEM;
- runtime->dma_bytes = size;
- return 1;
-}
-EXPORT_SYMBOL(_snd_pcm_lib_alloc_vmalloc_buffer);
-
-/**
- * snd_pcm_lib_free_vmalloc_buffer - free vmalloc buffer
- * @substream: the substream with a buffer allocated by
- * snd_pcm_lib_alloc_vmalloc_buffer()
- */
-int snd_pcm_lib_free_vmalloc_buffer(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime;
-
- if (PCM_RUNTIME_CHECK(substream))
- return -EINVAL;
- runtime = substream->runtime;
- vfree(runtime->dma_area);
- runtime->dma_area = NULL;
- return 0;
-}
-EXPORT_SYMBOL(snd_pcm_lib_free_vmalloc_buffer);
-
-/**
- * snd_pcm_lib_get_vmalloc_page - map vmalloc buffer offset to page struct
- * @substream: the substream with a buffer allocated by
- * snd_pcm_lib_alloc_vmalloc_buffer()
- * @offset: offset in the buffer
- *
- * This function is to be used as the page callback in the PCM ops.
- */
-struct page *snd_pcm_lib_get_vmalloc_page(struct snd_pcm_substream *substream,
- unsigned long offset)
-{
- return vmalloc_to_page(substream->runtime->dma_area + offset);
-}
-EXPORT_SYMBOL(snd_pcm_lib_get_vmalloc_page);
diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c
index 434af3c56d52..71eec32a7a0a 100644
--- a/sound/core/pcm_misc.c
+++ b/sound/core/pcm_misc.c
@@ -20,8 +20,12 @@
*/
#include <linux/time.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/pcm.h>
+
+#include "pcm_local.h"
+
#define SND_PCM_FORMAT_UNKNOWN (-1)
/* NOTE: "signed" prefix must be given below since the default char is
@@ -35,7 +39,15 @@ struct pcm_format_data {
unsigned char silence[8]; /* silence data to fill */
};
-static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = {
+/* we do lots of calculations on snd_pcm_format_t; shut up sparse */
+#define INT __force int
+
+static bool valid_format(snd_pcm_format_t format)
+{
+ return (INT)format >= 0 && (INT)format <= (INT)SNDRV_PCM_FORMAT_LAST;
+}
+
+static const struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = {
[SNDRV_PCM_FORMAT_S8] = {
.width = 8, .phys = 8, .le = -1, .signd = 1,
.silence = {},
@@ -136,13 +148,50 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = {
.width = 5, .phys = 5, .le = -1, .signd = -1,
.silence = {},
},
- /* FIXME: the following three formats are not defined properly yet */
+ [SNDRV_PCM_FORMAT_DSD_U8] = {
+ .width = 8, .phys = 8, .le = 1, .signd = 0,
+ .silence = { 0x69 },
+ },
+ [SNDRV_PCM_FORMAT_DSD_U16_LE] = {
+ .width = 16, .phys = 16, .le = 1, .signd = 0,
+ .silence = { 0x69, 0x69 },
+ },
+ [SNDRV_PCM_FORMAT_DSD_U32_LE] = {
+ .width = 32, .phys = 32, .le = 1, .signd = 0,
+ .silence = { 0x69, 0x69, 0x69, 0x69 },
+ },
+ [SNDRV_PCM_FORMAT_DSD_U16_BE] = {
+ .width = 16, .phys = 16, .le = 0, .signd = 0,
+ .silence = { 0x69, 0x69 },
+ },
+ [SNDRV_PCM_FORMAT_DSD_U32_BE] = {
+ .width = 32, .phys = 32, .le = 0, .signd = 0,
+ .silence = { 0x69, 0x69, 0x69, 0x69 },
+ },
+ /* FIXME: the following two formats are not defined properly yet */
[SNDRV_PCM_FORMAT_MPEG] = {
.le = -1, .signd = -1,
},
[SNDRV_PCM_FORMAT_GSM] = {
.le = -1, .signd = -1,
},
+ [SNDRV_PCM_FORMAT_S20_LE] = {
+ .width = 20, .phys = 32, .le = 1, .signd = 1,
+ .silence = {},
+ },
+ [SNDRV_PCM_FORMAT_S20_BE] = {
+ .width = 20, .phys = 32, .le = 0, .signd = 1,
+ .silence = {},
+ },
+ [SNDRV_PCM_FORMAT_U20_LE] = {
+ .width = 20, .phys = 32, .le = 1, .signd = 0,
+ .silence = { 0x00, 0x00, 0x08, 0x00 },
+ },
+ [SNDRV_PCM_FORMAT_U20_BE] = {
+ .width = 20, .phys = 32, .le = 0, .signd = 0,
+ .silence = { 0x00, 0x08, 0x00, 0x00 },
+ },
+ /* FIXME: the following format is not defined properly yet */
[SNDRV_PCM_FORMAT_SPECIAL] = {
.le = -1, .signd = -1,
},
@@ -209,26 +258,26 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = {
* snd_pcm_format_signed - Check the PCM format is signed linear
* @format: the format to check
*
- * Returns 1 if the given PCM format is signed linear, 0 if unsigned
+ * Return: 1 if the given PCM format is signed linear, 0 if unsigned
* linear, and a negative error code for non-linear formats.
*/
int snd_pcm_format_signed(snd_pcm_format_t format)
{
int val;
- if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+ if (!valid_format(format))
return -EINVAL;
- if ((val = pcm_formats[format].signd) < 0)
+ val = pcm_formats[(INT)format].signd;
+ if (val < 0)
return -EINVAL;
return val;
}
-
EXPORT_SYMBOL(snd_pcm_format_signed);
/**
* snd_pcm_format_unsigned - Check the PCM format is unsigned linear
* @format: the format to check
*
- * Returns 1 if the given PCM format is unsigned linear, 0 if signed
+ * Return: 1 if the given PCM format is unsigned linear, 0 if signed
* linear, and a negative error code for non-linear formats.
*/
int snd_pcm_format_unsigned(snd_pcm_format_t format)
@@ -240,46 +289,44 @@ int snd_pcm_format_unsigned(snd_pcm_format_t format)
return val;
return !val;
}
-
EXPORT_SYMBOL(snd_pcm_format_unsigned);
/**
* snd_pcm_format_linear - Check the PCM format is linear
* @format: the format to check
*
- * Returns 1 if the given PCM format is linear, 0 if not.
+ * Return: 1 if the given PCM format is linear, 0 if not.
*/
int snd_pcm_format_linear(snd_pcm_format_t format)
{
return snd_pcm_format_signed(format) >= 0;
}
-
EXPORT_SYMBOL(snd_pcm_format_linear);
/**
* snd_pcm_format_little_endian - Check the PCM format is little-endian
* @format: the format to check
*
- * Returns 1 if the given PCM format is little-endian, 0 if
+ * Return: 1 if the given PCM format is little-endian, 0 if
* big-endian, or a negative error code if endian not specified.
*/
int snd_pcm_format_little_endian(snd_pcm_format_t format)
{
int val;
- if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+ if (!valid_format(format))
return -EINVAL;
- if ((val = pcm_formats[format].le) < 0)
+ val = pcm_formats[(INT)format].le;
+ if (val < 0)
return -EINVAL;
return val;
}
-
EXPORT_SYMBOL(snd_pcm_format_little_endian);
/**
* snd_pcm_format_big_endian - Check the PCM format is big-endian
* @format: the format to check
*
- * Returns 1 if the given PCM format is big-endian, 0 if
+ * Return: 1 if the given PCM format is big-endian, 0 if
* little-endian, or a negative error code if endian not specified.
*/
int snd_pcm_format_big_endian(snd_pcm_format_t format)
@@ -291,45 +338,44 @@ int snd_pcm_format_big_endian(snd_pcm_format_t format)
return val;
return !val;
}
-
EXPORT_SYMBOL(snd_pcm_format_big_endian);
/**
* snd_pcm_format_width - return the bit-width of the format
* @format: the format to check
*
- * Returns the bit-width of the format, or a negative error code
+ * Return: The bit-width of the format, or a negative error code
* if unknown format.
*/
int snd_pcm_format_width(snd_pcm_format_t format)
{
int val;
- if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+ if (!valid_format(format))
return -EINVAL;
- if ((val = pcm_formats[format].width) == 0)
+ val = pcm_formats[(INT)format].width;
+ if (!val)
return -EINVAL;
return val;
}
-
EXPORT_SYMBOL(snd_pcm_format_width);
/**
* snd_pcm_format_physical_width - return the physical bit-width of the format
* @format: the format to check
*
- * Returns the physical bit-width of the format, or a negative error code
+ * Return: The physical bit-width of the format, or a negative error code
* if unknown format.
*/
int snd_pcm_format_physical_width(snd_pcm_format_t format)
{
int val;
- if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+ if (!valid_format(format))
return -EINVAL;
- if ((val = pcm_formats[format].phys) == 0)
+ val = pcm_formats[(INT)format].phys;
+ if (!val)
return -EINVAL;
return val;
}
-
EXPORT_SYMBOL(snd_pcm_format_physical_width);
/**
@@ -337,7 +383,7 @@ EXPORT_SYMBOL(snd_pcm_format_physical_width);
* @format: the format to check
* @samples: sampling rate
*
- * Returns the byte size of the given samples for the format, or a
+ * Return: The byte size of the given samples for the format, or a
* negative error code if unknown format.
*/
ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples)
@@ -347,24 +393,22 @@ ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples)
return -EINVAL;
return samples * phys_width / 8;
}
-
EXPORT_SYMBOL(snd_pcm_format_size);
/**
* snd_pcm_format_silence_64 - return the silent data in 8 bytes array
* @format: the format to check
*
- * Returns the format pattern to fill or NULL if error.
+ * Return: The format pattern to fill or %NULL if error.
*/
const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format)
{
- if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+ if (!valid_format(format))
return NULL;
- if (! pcm_formats[format].phys)
+ if (! pcm_formats[(INT)format].phys)
return NULL;
- return pcm_formats[format].silence;
+ return pcm_formats[(INT)format].silence;
}
-
EXPORT_SYMBOL(snd_pcm_format_silence_64);
/**
@@ -375,23 +419,24 @@ EXPORT_SYMBOL(snd_pcm_format_silence_64);
*
* Sets the silence data on the buffer for the given samples.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int samples)
{
int width;
- unsigned char *dst, *pat;
+ unsigned char *dst;
+ const unsigned char *pat;
- if (format < 0 || format > SNDRV_PCM_FORMAT_LAST)
+ if (!valid_format(format))
return -EINVAL;
if (samples == 0)
return 0;
- width = pcm_formats[format].phys; /* physical width */
- pat = pcm_formats[format].silence;
- if (! width)
+ width = pcm_formats[(INT)format].phys; /* physical width */
+ if (!width)
return -EINVAL;
+ pat = pcm_formats[(INT)format].silence;
/* signed or 1 byte data */
- if (pcm_formats[format].signd == 1 || width <= 8) {
+ if (pcm_formats[(INT)format].signd == 1 || width <= 8) {
unsigned int bytes = samples * width / 8;
memset(data, *pat, bytes);
return 0;
@@ -435,43 +480,43 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int
#endif
return 0;
}
-
EXPORT_SYMBOL(snd_pcm_format_set_silence);
/**
- * snd_pcm_limit_hw_rates - determine rate_min/rate_max fields
- * @runtime: the runtime instance
+ * snd_pcm_hw_limit_rates - determine rate_min/rate_max fields
+ * @hw: the pcm hw instance
*
* Determines the rate_min and rate_max fields from the rates bits of
- * the given runtime->hw.
+ * the given hw.
*
- * Returns zero if successful.
+ * Return: Zero if successful.
*/
-int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime)
+int snd_pcm_hw_limit_rates(struct snd_pcm_hardware *hw)
{
int i;
+ unsigned int rmin, rmax;
+
+ rmin = UINT_MAX;
+ rmax = 0;
for (i = 0; i < (int)snd_pcm_known_rates.count; i++) {
- if (runtime->hw.rates & (1 << i)) {
- runtime->hw.rate_min = snd_pcm_known_rates.list[i];
- break;
- }
- }
- for (i = (int)snd_pcm_known_rates.count - 1; i >= 0; i--) {
- if (runtime->hw.rates & (1 << i)) {
- runtime->hw.rate_max = snd_pcm_known_rates.list[i];
- break;
+ if (hw->rates & (1 << i)) {
+ rmin = min(rmin, snd_pcm_known_rates.list[i]);
+ rmax = max(rmax, snd_pcm_known_rates.list[i]);
}
}
+ if (rmin > rmax)
+ return -EINVAL;
+ hw->rate_min = rmin;
+ hw->rate_max = rmax;
return 0;
}
-
-EXPORT_SYMBOL(snd_pcm_limit_hw_rates);
+EXPORT_SYMBOL(snd_pcm_hw_limit_rates);
/**
* snd_pcm_rate_to_rate_bit - converts sample rate to SNDRV_PCM_RATE_xxx bit
* @rate: the sample rate to convert
*
- * Returns the SNDRV_PCM_RATE_xxx flag that corresponds to the given rate, or
+ * Return: The SNDRV_PCM_RATE_xxx flag that corresponds to the given rate, or
* SNDRV_PCM_RATE_KNOT for an unknown rate.
*/
unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate)
@@ -484,3 +529,60 @@ unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate)
return SNDRV_PCM_RATE_KNOT;
}
EXPORT_SYMBOL(snd_pcm_rate_to_rate_bit);
+
+/**
+ * snd_pcm_rate_bit_to_rate - converts SNDRV_PCM_RATE_xxx bit to sample rate
+ * @rate_bit: the rate bit to convert
+ *
+ * Return: The sample rate that corresponds to the given SNDRV_PCM_RATE_xxx flag
+ * or 0 for an unknown rate bit.
+ */
+unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit)
+{
+ unsigned int i;
+
+ for (i = 0; i < snd_pcm_known_rates.count; i++)
+ if ((1u << i) == rate_bit)
+ return snd_pcm_known_rates.list[i];
+ return 0;
+}
+EXPORT_SYMBOL(snd_pcm_rate_bit_to_rate);
+
+static unsigned int snd_pcm_rate_mask_sanitize(unsigned int rates)
+{
+ if (rates & SNDRV_PCM_RATE_CONTINUOUS)
+ return SNDRV_PCM_RATE_CONTINUOUS;
+ else if (rates & SNDRV_PCM_RATE_KNOT)
+ return SNDRV_PCM_RATE_KNOT;
+ return rates;
+}
+
+/**
+ * snd_pcm_rate_mask_intersect - computes the intersection between two rate masks
+ * @rates_a: The first rate mask
+ * @rates_b: The second rate mask
+ *
+ * This function computes the rates that are supported by both rate masks passed
+ * to the function. It will take care of the special handling of
+ * SNDRV_PCM_RATE_CONTINUOUS and SNDRV_PCM_RATE_KNOT.
+ *
+ * Return: A rate mask containing the rates that are supported by both rates_a
+ * and rates_b.
+ */
+unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a,
+ unsigned int rates_b)
+{
+ rates_a = snd_pcm_rate_mask_sanitize(rates_a);
+ rates_b = snd_pcm_rate_mask_sanitize(rates_b);
+
+ if (rates_a & SNDRV_PCM_RATE_CONTINUOUS)
+ return rates_b;
+ else if (rates_b & SNDRV_PCM_RATE_CONTINUOUS)
+ return rates_a;
+ else if (rates_a & SNDRV_PCM_RATE_KNOT)
+ return rates_b;
+ else if (rates_b & SNDRV_PCM_RATE_KNOT)
+ return rates_a;
+ return rates_a & rates_b;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_rate_mask_intersect);
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 8bc7cb3db330..68bee40c9ada 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1,32 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Digital Audio (PCM) abstract layer
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
+#include <linux/compat.h>
#include <linux/mm.h>
+#include <linux/module.h>
#include <linux/file.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
+#include <linux/sched/signal.h>
#include <linux/time.h>
-#include <linux/pm_qos_params.h>
-#include <linux/uio.h>
+#include <linux/pm_qos.h>
+#include <linux/io.h>
#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/info.h>
@@ -34,9 +22,20 @@
#include <sound/pcm_params.h>
#include <sound/timer.h>
#include <sound/minors.h>
-#include <asm/io.h>
-#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
-#include <dma-coherence.h>
+#include <linux/uio.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+
+#include "pcm_local.h"
+
+#ifdef CONFIG_SND_DEBUG
+#define CREATE_TRACE_POINTS
+#include "pcm_param_trace.h"
+#else
+#define trace_hw_mask_param_enabled() 0
+#define trace_hw_interval_param_enabled() 0
+#define trace_hw_mask_param(substream, type, index, prev, curr)
+#define trace_hw_interval_param(substream, type, index, prev, curr)
#endif
/*
@@ -74,28 +73,153 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream);
*
*/
-DEFINE_RWLOCK(snd_pcm_link_rwlock);
-EXPORT_SYMBOL(snd_pcm_link_rwlock);
-
static DECLARE_RWSEM(snd_pcm_link_rwsem);
-static inline mm_segment_t snd_enter_user(void)
+void snd_pcm_group_init(struct snd_pcm_group *group)
+{
+ spin_lock_init(&group->lock);
+ mutex_init(&group->mutex);
+ INIT_LIST_HEAD(&group->substreams);
+ refcount_set(&group->refs, 1);
+}
+
+/* define group lock helpers */
+#define DEFINE_PCM_GROUP_LOCK(action, bh_lock, bh_unlock, mutex_action) \
+static void snd_pcm_group_ ## action(struct snd_pcm_group *group, bool nonatomic) \
+{ \
+ if (nonatomic) { \
+ mutex_ ## mutex_action(&group->mutex); \
+ } else { \
+ if (IS_ENABLED(CONFIG_PREEMPT_RT) && bh_lock) \
+ local_bh_disable(); \
+ spin_ ## action(&group->lock); \
+ if (IS_ENABLED(CONFIG_PREEMPT_RT) && bh_unlock) \
+ local_bh_enable(); \
+ } \
+}
+
+DEFINE_PCM_GROUP_LOCK(lock, false, false, lock);
+DEFINE_PCM_GROUP_LOCK(unlock, false, false, unlock);
+DEFINE_PCM_GROUP_LOCK(lock_irq, true, false, lock);
+DEFINE_PCM_GROUP_LOCK(unlock_irq, false, true, unlock);
+
+/**
+ * snd_pcm_stream_lock - Lock the PCM stream
+ * @substream: PCM substream
+ *
+ * This locks the PCM stream's spinlock or mutex depending on the nonatomic
+ * flag of the given substream. This also takes the global link rw lock
+ * (or rw sem), too, for avoiding the race with linked streams.
+ */
+void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
{
- mm_segment_t fs = get_fs();
- set_fs(get_ds());
- return fs;
+ snd_pcm_group_lock(&substream->self_group, substream->pcm->nonatomic);
}
+EXPORT_SYMBOL_GPL(snd_pcm_stream_lock);
-static inline void snd_leave_user(mm_segment_t fs)
+/**
+ * snd_pcm_stream_unlock - Unlock the PCM stream
+ * @substream: PCM substream
+ *
+ * This unlocks the PCM stream that has been locked via snd_pcm_stream_lock().
+ */
+void snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
{
- set_fs(fs);
+ snd_pcm_group_unlock(&substream->self_group, substream->pcm->nonatomic);
}
+EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock);
+/**
+ * snd_pcm_stream_lock_irq - Lock the PCM stream
+ * @substream: PCM substream
+ *
+ * This locks the PCM stream like snd_pcm_stream_lock() and disables the local
+ * IRQ (only when nonatomic is false). In nonatomic case, this is identical
+ * as snd_pcm_stream_lock().
+ */
+void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
+{
+ snd_pcm_group_lock_irq(&substream->self_group,
+ substream->pcm->nonatomic);
+}
+EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
+
+static void snd_pcm_stream_lock_nested(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_group *group = &substream->self_group;
+ if (substream->pcm->nonatomic)
+ mutex_lock_nested(&group->mutex, SINGLE_DEPTH_NESTING);
+ else
+ spin_lock_nested(&group->lock, SINGLE_DEPTH_NESTING);
+}
+
+/**
+ * snd_pcm_stream_unlock_irq - Unlock the PCM stream
+ * @substream: PCM substream
+ *
+ * This is a counter-part of snd_pcm_stream_lock_irq().
+ */
+void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream)
+{
+ snd_pcm_group_unlock_irq(&substream->self_group,
+ substream->pcm->nonatomic);
+}
+EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irq);
+
+unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream)
+{
+ unsigned long flags = 0;
+ if (substream->pcm->nonatomic)
+ mutex_lock(&substream->self_group.mutex);
+ else
+ spin_lock_irqsave(&substream->self_group.lock, flags);
+ return flags;
+}
+EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave);
+
+unsigned long _snd_pcm_stream_lock_irqsave_nested(struct snd_pcm_substream *substream)
+{
+ unsigned long flags = 0;
+ if (substream->pcm->nonatomic)
+ mutex_lock_nested(&substream->self_group.mutex,
+ SINGLE_DEPTH_NESTING);
+ else
+ spin_lock_irqsave_nested(&substream->self_group.lock, flags,
+ SINGLE_DEPTH_NESTING);
+ return flags;
+}
+EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave_nested);
+
+/**
+ * snd_pcm_stream_unlock_irqrestore - Unlock the PCM stream
+ * @substream: PCM substream
+ * @flags: irq flags
+ *
+ * This is a counter-part of snd_pcm_stream_lock_irqsave().
+ */
+void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
+ unsigned long flags)
+{
+ if (substream->pcm->nonatomic)
+ mutex_unlock(&substream->self_group.mutex);
+ else
+ spin_unlock_irqrestore(&substream->self_group.lock, flags);
+}
+EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore);
+
+/* Run PCM ioctl ops */
+static int snd_pcm_ops_ioctl(struct snd_pcm_substream *substream,
+ unsigned cmd, void *arg)
+{
+ if (substream->ops->ioctl)
+ return substream->ops->ioctl(substream, cmd, arg);
+ else
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info)
{
- struct snd_pcm_runtime *runtime;
struct snd_pcm *pcm = substream->pcm;
struct snd_pcm_str *pstr = substream->pstr;
@@ -104,26 +228,21 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info)
info->device = pcm->device;
info->stream = substream->stream;
info->subdevice = substream->number;
- strlcpy(info->id, pcm->id, sizeof(info->id));
- strlcpy(info->name, pcm->name, sizeof(info->name));
+ strscpy(info->id, pcm->id, sizeof(info->id));
+ strscpy(info->name, pcm->name, sizeof(info->name));
info->dev_class = pcm->dev_class;
info->dev_subclass = pcm->dev_subclass;
info->subdevices_count = pstr->substream_count;
info->subdevices_avail = pstr->substream_count - pstr->substream_opened;
- strlcpy(info->subname, substream->name, sizeof(info->subname));
- runtime = substream->runtime;
- /* AB: FIXME!!! This is definitely nonsense */
- if (runtime) {
- info->sync = runtime->sync;
- substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info);
- }
+ strscpy(info->subname, substream->name, sizeof(info->subname));
+
return 0;
}
int snd_pcm_info_user(struct snd_pcm_substream *substream,
struct snd_pcm_info __user * _info)
{
- struct snd_pcm_info *info;
+ struct snd_pcm_info *info __free(kfree) = NULL;
int err;
info = kmalloc(sizeof(*info), GFP_KERNEL);
@@ -134,210 +253,337 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream,
if (copy_to_user(_info, info, sizeof(*info)))
err = -EFAULT;
}
- kfree(info);
return err;
}
-#undef RULES_DEBUG
-
-#ifdef RULES_DEBUG
-#define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v
-static const char * const snd_pcm_hw_param_names[] = {
- HW_PARAM(ACCESS),
- HW_PARAM(FORMAT),
- HW_PARAM(SUBFORMAT),
- HW_PARAM(SAMPLE_BITS),
- HW_PARAM(FRAME_BITS),
- HW_PARAM(CHANNELS),
- HW_PARAM(RATE),
- HW_PARAM(PERIOD_TIME),
- HW_PARAM(PERIOD_SIZE),
- HW_PARAM(PERIOD_BYTES),
- HW_PARAM(PERIODS),
- HW_PARAM(BUFFER_TIME),
- HW_PARAM(BUFFER_SIZE),
- HW_PARAM(BUFFER_BYTES),
- HW_PARAM(TICK_TIME),
-};
-#endif
+/* macro for simplified cast */
+#define PARAM_MASK_BIT(b) (1U << (__force int)(b))
-int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+static bool hw_support_mmap(struct snd_pcm_substream *substream)
{
- unsigned int k;
- struct snd_pcm_hardware *hw;
- struct snd_interval *i = NULL;
- struct snd_mask *m = NULL;
- struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints;
- unsigned int rstamps[constrs->rules_num];
- unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1];
- unsigned int stamp = 2;
- int changed, again;
+ struct snd_dma_buffer *dmabuf;
- params->info = 0;
- params->fifo_size = 0;
- if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS))
- params->msbits = 0;
- if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) {
- params->rate_num = 0;
- params->rate_den = 0;
+ if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_MMAP))
+ return false;
+
+ if (substream->ops->mmap || substream->ops->page)
+ return true;
+
+ dmabuf = snd_pcm_get_dma_buf(substream);
+ if (!dmabuf)
+ dmabuf = &substream->dma_buffer;
+ switch (dmabuf->dev.type) {
+ case SNDRV_DMA_TYPE_UNKNOWN:
+ /* we can't know the device, so just assume that the driver does
+ * everything right
+ */
+ return true;
+ case SNDRV_DMA_TYPE_CONTINUOUS:
+ case SNDRV_DMA_TYPE_VMALLOC:
+ return true;
+ default:
+ return dma_can_mmap(dmabuf->dev.dev);
}
+}
+
+static int constrain_mask_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_hw_constraints *constrs =
+ &substream->runtime->hw_constraints;
+ struct snd_mask *m;
+ unsigned int k;
+ struct snd_mask old_mask __maybe_unused;
+ int changed;
for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) {
m = hw_param_mask(params, k);
if (snd_mask_empty(m))
return -EINVAL;
- if (!(params->rmask & (1 << k)))
+
+ /* This parameter is not requested to change by a caller. */
+ if (!(params->rmask & PARAM_MASK_BIT(k)))
continue;
-#ifdef RULES_DEBUG
- printk(KERN_DEBUG "%s = ", snd_pcm_hw_param_names[k]);
- printk("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
-#endif
+
+ if (trace_hw_mask_param_enabled())
+ old_mask = *m;
+
changed = snd_mask_refine(m, constrs_mask(constrs, k));
-#ifdef RULES_DEBUG
- printk("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
-#endif
- if (changed)
- params->cmask |= 1 << k;
if (changed < 0)
return changed;
+ if (changed == 0)
+ continue;
+
+ /* Set corresponding flag so that the caller gets it. */
+ trace_hw_mask_param(substream, k, 0, &old_mask, m);
+ params->cmask |= PARAM_MASK_BIT(k);
}
+ return 0;
+}
+
+static int constrain_interval_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_hw_constraints *constrs =
+ &substream->runtime->hw_constraints;
+ struct snd_interval *i;
+ unsigned int k;
+ struct snd_interval old_interval __maybe_unused;
+ int changed;
+
for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) {
i = hw_param_interval(params, k);
if (snd_interval_empty(i))
return -EINVAL;
- if (!(params->rmask & (1 << k)))
+
+ /* This parameter is not requested to change by a caller. */
+ if (!(params->rmask & PARAM_MASK_BIT(k)))
continue;
-#ifdef RULES_DEBUG
- printk(KERN_DEBUG "%s = ", snd_pcm_hw_param_names[k]);
- if (i->empty)
- printk("empty");
- else
- printk("%c%u %u%c",
- i->openmin ? '(' : '[', i->min,
- i->max, i->openmax ? ')' : ']');
- printk(" -> ");
-#endif
+
+ if (trace_hw_interval_param_enabled())
+ old_interval = *i;
+
changed = snd_interval_refine(i, constrs_interval(constrs, k));
-#ifdef RULES_DEBUG
- if (i->empty)
- printk("empty\n");
- else
- printk("%c%u %u%c\n",
- i->openmin ? '(' : '[', i->min,
- i->max, i->openmax ? ')' : ']');
-#endif
- if (changed)
- params->cmask |= 1 << k;
if (changed < 0)
return changed;
+ if (changed == 0)
+ continue;
+
+ /* Set corresponding flag so that the caller gets it. */
+ trace_hw_interval_param(substream, k, 0, &old_interval, i);
+ params->cmask |= PARAM_MASK_BIT(k);
}
- for (k = 0; k < constrs->rules_num; k++)
- rstamps[k] = 0;
- for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++)
- vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0;
- do {
- again = 0;
- for (k = 0; k < constrs->rules_num; k++) {
- struct snd_pcm_hw_rule *r = &constrs->rules[k];
- unsigned int d;
- int doit = 0;
- if (r->cond && !(r->cond & params->flags))
- continue;
- for (d = 0; r->deps[d] >= 0; d++) {
- if (vstamps[r->deps[d]] > rstamps[k]) {
- doit = 1;
- break;
- }
- }
- if (!doit)
- continue;
-#ifdef RULES_DEBUG
- printk(KERN_DEBUG "Rule %d [%p]: ", k, r->func);
- if (r->var >= 0) {
- printk("%s = ", snd_pcm_hw_param_names[r->var]);
- if (hw_is_mask(r->var)) {
- m = hw_param_mask(params, r->var);
- printk("%x", *m->bits);
- } else {
- i = hw_param_interval(params, r->var);
- if (i->empty)
- printk("empty");
- else
- printk("%c%u %u%c",
- i->openmin ? '(' : '[', i->min,
- i->max, i->openmax ? ')' : ']');
- }
- }
-#endif
- changed = r->func(params, r);
-#ifdef RULES_DEBUG
- if (r->var >= 0) {
- printk(" -> ");
- if (hw_is_mask(r->var))
- printk("%x", *m->bits);
- else {
- if (i->empty)
- printk("empty");
- else
- printk("%c%u %u%c",
- i->openmin ? '(' : '[', i->min,
- i->max, i->openmax ? ')' : ']');
- }
+ return 0;
+}
+
+static int constrain_params_by_rules(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_hw_constraints *constrs =
+ &substream->runtime->hw_constraints;
+ unsigned int k;
+ unsigned int *rstamps __free(kfree) = NULL;
+ unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1];
+ unsigned int stamp;
+ struct snd_pcm_hw_rule *r;
+ unsigned int d;
+ struct snd_mask old_mask __maybe_unused;
+ struct snd_interval old_interval __maybe_unused;
+ bool again;
+ int changed, err = 0;
+
+ /*
+ * Each application of rule has own sequence number.
+ *
+ * Each member of 'rstamps' array represents the sequence number of
+ * recent application of corresponding rule.
+ */
+ rstamps = kcalloc(constrs->rules_num, sizeof(unsigned int), GFP_KERNEL);
+ if (!rstamps)
+ return -ENOMEM;
+
+ /*
+ * Each member of 'vstamps' array represents the sequence number of
+ * recent application of rule in which corresponding parameters were
+ * changed.
+ *
+ * In initial state, elements corresponding to parameters requested by
+ * a caller is 1. For unrequested parameters, corresponding members
+ * have 0 so that the parameters are never changed anymore.
+ */
+ for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++)
+ vstamps[k] = (params->rmask & PARAM_MASK_BIT(k)) ? 1 : 0;
+
+ /* Due to the above design, actual sequence number starts at 2. */
+ stamp = 2;
+retry:
+ /* Apply all rules in order. */
+ again = false;
+ for (k = 0; k < constrs->rules_num; k++) {
+ r = &constrs->rules[k];
+
+ /*
+ * Check condition bits of this rule. When the rule has
+ * some condition bits, parameter without the bits is
+ * never processed. SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP
+ * is an example of the condition bits.
+ */
+ if (r->cond && !(r->cond & params->flags))
+ continue;
+
+ /*
+ * The 'deps' array includes maximum four dependencies
+ * to SNDRV_PCM_HW_PARAM_XXXs for this rule. The fifth
+ * member of this array is a sentinel and should be
+ * negative value.
+ *
+ * This rule should be processed in this time when dependent
+ * parameters were changed at former applications of the other
+ * rules.
+ */
+ for (d = 0; r->deps[d] >= 0; d++) {
+ if (vstamps[r->deps[d]] > rstamps[k])
+ break;
+ }
+ if (r->deps[d] < 0)
+ continue;
+
+ if (trace_hw_mask_param_enabled()) {
+ if (hw_is_mask(r->var))
+ old_mask = *hw_param_mask(params, r->var);
+ }
+ if (trace_hw_interval_param_enabled()) {
+ if (hw_is_interval(r->var))
+ old_interval = *hw_param_interval(params, r->var);
+ }
+
+ changed = r->func(params, r);
+ if (changed < 0)
+ return changed;
+
+ /*
+ * When the parameter is changed, notify it to the caller
+ * by corresponding returned bit, then preparing for next
+ * iteration.
+ */
+ if (changed && r->var >= 0) {
+ if (hw_is_mask(r->var)) {
+ trace_hw_mask_param(substream, r->var,
+ k + 1, &old_mask,
+ hw_param_mask(params, r->var));
}
- printk("\n");
-#endif
- rstamps[k] = stamp;
- if (changed && r->var >= 0) {
- params->cmask |= (1 << r->var);
- vstamps[r->var] = stamp;
- again = 1;
+ if (hw_is_interval(r->var)) {
+ trace_hw_interval_param(substream, r->var,
+ k + 1, &old_interval,
+ hw_param_interval(params, r->var));
}
- if (changed < 0)
- return changed;
- stamp++;
+
+ params->cmask |= PARAM_MASK_BIT(r->var);
+ vstamps[r->var] = stamp;
+ again = true;
}
- } while (again);
+
+ rstamps[k] = stamp++;
+ }
+
+ /* Iterate to evaluate all rules till no parameters are changed. */
+ if (again)
+ goto retry;
+
+ return err;
+}
+
+static int fixup_unreferenced_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ const struct snd_interval *i;
+ const struct snd_mask *m;
+ struct snd_mask *m_rw;
+ int err;
+
if (!params->msbits) {
- i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
+ i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
if (snd_interval_single(i))
params->msbits = snd_interval_value(i);
+ m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ if (snd_mask_single(m)) {
+ snd_pcm_format_t format = (__force snd_pcm_format_t)snd_mask_min(m);
+ params->msbits = snd_pcm_format_width(format);
+ }
+ }
+
+ if (params->msbits) {
+ m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ if (snd_mask_single(m)) {
+ snd_pcm_format_t format = (__force snd_pcm_format_t)snd_mask_min(m);
+
+ if (snd_pcm_format_linear(format) &&
+ snd_pcm_format_width(format) != params->msbits) {
+ m_rw = hw_param_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT);
+ snd_mask_reset(m_rw,
+ (__force unsigned)SNDRV_PCM_SUBFORMAT_MSBITS_MAX);
+ if (snd_mask_empty(m_rw))
+ return -EINVAL;
+ }
+ }
}
if (!params->rate_den) {
- i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
if (snd_interval_single(i)) {
params->rate_num = snd_interval_value(i);
params->rate_den = 1;
}
}
- hw = &substream->runtime->hw;
- if (!params->info)
- params->info = hw->info & ~SNDRV_PCM_INFO_FIFO_IN_FRAMES;
if (!params->fifo_size) {
- m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
- i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
- if (snd_mask_min(m) == snd_mask_max(m) &&
- snd_interval_min(i) == snd_interval_max(i)) {
- changed = substream->ops->ioctl(substream,
- SNDRV_PCM_IOCTL1_FIFO_SIZE, params);
- if (changed < 0)
- return changed;
+ m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ if (snd_mask_single(m) && snd_interval_single(i)) {
+ err = snd_pcm_ops_ioctl(substream,
+ SNDRV_PCM_IOCTL1_FIFO_SIZE,
+ params);
+ if (err < 0)
+ return err;
}
}
- params->rmask = 0;
+
+ if (!params->info) {
+ params->info = substream->runtime->hw.info;
+ params->info &= ~(SNDRV_PCM_INFO_FIFO_IN_FRAMES |
+ SNDRV_PCM_INFO_DRAIN_TRIGGER);
+ if (!hw_support_mmap(substream))
+ params->info &= ~(SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID);
+ }
+
+ err = snd_pcm_ops_ioctl(substream,
+ SNDRV_PCM_IOCTL1_SYNC_ID,
+ params);
+ if (err < 0)
+ return err;
+
return 0;
}
+int snd_pcm_hw_refine(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ int err;
+
+ params->info = 0;
+ params->fifo_size = 0;
+ if (params->rmask & PARAM_MASK_BIT(SNDRV_PCM_HW_PARAM_SAMPLE_BITS))
+ params->msbits = 0;
+ if (params->rmask & PARAM_MASK_BIT(SNDRV_PCM_HW_PARAM_RATE)) {
+ params->rate_num = 0;
+ params->rate_den = 0;
+ }
+
+ err = constrain_mask_params(substream, params);
+ if (err < 0)
+ return err;
+
+ err = constrain_interval_params(substream, params);
+ if (err < 0)
+ return err;
+
+ err = constrain_params_by_rules(substream, params);
+ if (err < 0)
+ return err;
+
+ params->rmask = 0;
+
+ return 0;
+}
EXPORT_SYMBOL(snd_pcm_hw_refine);
static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params __user * _params)
{
- struct snd_pcm_hw_params *params;
+ struct snd_pcm_hw_params *params __free(kfree) = NULL;
int err;
params = memdup_user(_params, sizeof(*params));
@@ -345,13 +591,16 @@ static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream,
return PTR_ERR(params);
err = snd_pcm_hw_refine(substream, params);
- if (copy_to_user(_params, params, sizeof(*params))) {
- if (!err)
- err = -EFAULT;
- }
+ if (err < 0)
+ return err;
- kfree(params);
- return err;
+ err = fixup_unreferenced_params(substream, params);
+ if (err < 0)
+ return err;
+
+ if (copy_to_user(_params, params, sizeof(*params)))
+ return -EFAULT;
+ return 0;
}
static int period_to_usecs(struct snd_pcm_runtime *runtime)
@@ -369,6 +618,134 @@ static int period_to_usecs(struct snd_pcm_runtime *runtime)
return usecs;
}
+static void snd_pcm_set_state(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
+{
+ guard(pcm_stream_lock_irq)(substream);
+ if (substream->runtime->state != SNDRV_PCM_STATE_DISCONNECTED)
+ __snd_pcm_set_state(substream->runtime, state);
+}
+
+static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream,
+ int event)
+{
+#ifdef CONFIG_SND_PCM_TIMER
+ if (substream->timer)
+ snd_timer_notify(substream->timer, event,
+ &substream->runtime->trigger_tstamp);
+#endif
+}
+
+void snd_pcm_sync_stop(struct snd_pcm_substream *substream, bool sync_irq)
+{
+ if (substream->runtime && substream->runtime->stop_operating) {
+ substream->runtime->stop_operating = false;
+ if (substream->ops && substream->ops->sync_stop)
+ substream->ops->sync_stop(substream);
+ else if (sync_irq && substream->pcm->card->sync_irq > 0)
+ synchronize_irq(substream->pcm->card->sync_irq);
+ }
+}
+
+/**
+ * snd_pcm_hw_params_choose - choose a configuration defined by @params
+ * @pcm: PCM instance
+ * @params: the hw_params instance
+ *
+ * Choose one configuration from configuration space defined by @params.
+ * The configuration chosen is that obtained fixing in this order:
+ * first access, first format, first subformat, min channels,
+ * min rate, min period time, max buffer size, min tick time
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+static int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
+ struct snd_pcm_hw_params *params)
+{
+ static const int vars[] = {
+ SNDRV_PCM_HW_PARAM_ACCESS,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ SNDRV_PCM_HW_PARAM_SUBFORMAT,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ SNDRV_PCM_HW_PARAM_RATE,
+ SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ SNDRV_PCM_HW_PARAM_TICK_TIME,
+ -1
+ };
+ const int *v;
+ struct snd_mask old_mask __maybe_unused;
+ struct snd_interval old_interval __maybe_unused;
+ int changed;
+
+ for (v = vars; *v != -1; v++) {
+ /* Keep old parameter to trace. */
+ if (trace_hw_mask_param_enabled()) {
+ if (hw_is_mask(*v))
+ old_mask = *hw_param_mask(params, *v);
+ }
+ if (trace_hw_interval_param_enabled()) {
+ if (hw_is_interval(*v))
+ old_interval = *hw_param_interval(params, *v);
+ }
+ if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE)
+ changed = snd_pcm_hw_param_first(pcm, params, *v, NULL);
+ else
+ changed = snd_pcm_hw_param_last(pcm, params, *v, NULL);
+ if (changed < 0)
+ return changed;
+ if (changed == 0)
+ continue;
+
+ /* Trace the changed parameter. */
+ if (hw_is_mask(*v)) {
+ trace_hw_mask_param(pcm, *v, 0, &old_mask,
+ hw_param_mask(params, *v));
+ }
+ if (hw_is_interval(*v)) {
+ trace_hw_interval_param(pcm, *v, 0, &old_interval,
+ hw_param_interval(params, *v));
+ }
+ }
+
+ return 0;
+}
+
+/* acquire buffer_mutex; if it's in r/w operation, return -EBUSY, otherwise
+ * block the further r/w operations
+ */
+static int snd_pcm_buffer_access_lock(struct snd_pcm_runtime *runtime)
+{
+ if (!atomic_dec_unless_positive(&runtime->buffer_accessing))
+ return -EBUSY;
+ mutex_lock(&runtime->buffer_mutex);
+ return 0; /* keep buffer_mutex, unlocked by below */
+}
+
+/* release buffer_mutex and clear r/w access flag */
+static void snd_pcm_buffer_access_unlock(struct snd_pcm_runtime *runtime)
+{
+ mutex_unlock(&runtime->buffer_mutex);
+ atomic_inc(&runtime->buffer_accessing);
+}
+
+/* fill the PCM buffer with the current silence format; called from pcm_oss.c */
+void snd_pcm_runtime_buffer_set_silence(struct snd_pcm_runtime *runtime)
+{
+ snd_pcm_buffer_access_lock(runtime);
+ if (runtime->dma_area)
+ snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
+ bytes_to_samples(runtime, runtime->dma_bytes));
+ snd_pcm_buffer_access_unlock(runtime);
+}
+EXPORT_SYMBOL_GPL(snd_pcm_runtime_buffer_set_silence);
+
+#if IS_ENABLED(CONFIG_SND_PCM_OSS)
+#define is_oss_stream(substream) ((substream)->oss.oss)
+#else
+#define is_oss_stream(substream) false
+#endif
+
static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -380,22 +757,27 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_OPEN:
- case SNDRV_PCM_STATE_SETUP:
- case SNDRV_PCM_STATE_PREPARED:
- break;
- default:
- snd_pcm_stream_unlock_irq(substream);
- return -EBADFD;
+ err = snd_pcm_buffer_access_lock(runtime);
+ if (err < 0)
+ return err;
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ switch (runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_SETUP:
+ case SNDRV_PCM_STATE_PREPARED:
+ if (!is_oss_stream(substream) &&
+ atomic_read(&substream->mmap_count))
+ err = -EBADFD;
+ break;
+ default:
+ err = -EBADFD;
+ break;
+ }
}
- snd_pcm_stream_unlock_irq(substream);
-#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
- if (!substream->oss.oss)
-#endif
- if (atomic_read(&substream->mmap_count))
- return -EBADFD;
+ if (err)
+ goto unlock;
+
+ snd_pcm_sync_stop(substream, true);
params->rmask = ~0U;
err = snd_pcm_hw_refine(substream, params);
@@ -406,6 +788,18 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
if (err < 0)
goto _error;
+ err = fixup_unreferenced_params(substream, params);
+ if (err < 0)
+ goto _error;
+
+ if (substream->managed_buffer_alloc) {
+ err = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(params));
+ if (err < 0)
+ goto _error;
+ runtime->buffer_changed = err > 0;
+ }
+
if (substream->ops->hw_params != NULL) {
err = substream->ops->hw_params(substream, params);
if (err < 0)
@@ -423,6 +817,9 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
runtime->info = params->info;
runtime->rate_num = params->rate_num;
runtime->rate_den = params->rate_den;
+ runtime->no_period_wakeup =
+ (params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
+ (params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP);
bits = snd_pcm_format_physical_width(runtime->format);
runtime->sample_bits = bits;
@@ -448,29 +845,46 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size)
runtime->boundary *= 2;
- snd_pcm_timer_resolution_change(substream);
- runtime->status->state = SNDRV_PCM_STATE_SETUP;
+ /* clear the buffer for avoiding possible kernel info leaks */
+ if (runtime->dma_area && !substream->ops->copy) {
+ size_t size = runtime->dma_bytes;
- if (pm_qos_request_active(&substream->latency_pm_qos_req))
- pm_qos_remove_request(&substream->latency_pm_qos_req);
- if ((usecs = period_to_usecs(runtime)) >= 0)
- pm_qos_add_request(&substream->latency_pm_qos_req,
- PM_QOS_CPU_DMA_LATENCY, usecs);
- return 0;
+ if (runtime->info & SNDRV_PCM_INFO_MMAP)
+ size = PAGE_ALIGN(size);
+ memset(runtime->dma_area, 0, size);
+ }
+
+ snd_pcm_timer_resolution_change(substream);
+ snd_pcm_set_state(substream, SNDRV_PCM_STATE_SETUP);
+
+ if (cpu_latency_qos_request_active(&substream->latency_pm_qos_req))
+ cpu_latency_qos_remove_request(&substream->latency_pm_qos_req);
+ usecs = period_to_usecs(runtime);
+ if (usecs >= 0)
+ cpu_latency_qos_add_request(&substream->latency_pm_qos_req,
+ usecs);
+ err = 0;
_error:
- /* hardware might be unuseable from this time,
- so we force application to retry to set
- the correct hardware parameter settings */
- runtime->status->state = SNDRV_PCM_STATE_OPEN;
- if (substream->ops->hw_free != NULL)
- substream->ops->hw_free(substream);
+ if (err) {
+ /* hardware might be unusable from this time,
+ * so we force application to retry to set
+ * the correct hardware parameter settings
+ */
+ snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
+ if (substream->ops->hw_free != NULL)
+ substream->ops->hw_free(substream);
+ if (substream->managed_buffer_alloc)
+ snd_pcm_lib_free_pages(substream);
+ }
+ unlock:
+ snd_pcm_buffer_access_unlock(runtime);
return err;
}
static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params __user * _params)
{
- struct snd_pcm_hw_params *params;
+ struct snd_pcm_hw_params *params __free(kfree) = NULL;
int err;
params = memdup_user(_params, sizeof(*params));
@@ -478,15 +892,26 @@ static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream,
return PTR_ERR(params);
err = snd_pcm_hw_params(substream, params);
- if (copy_to_user(_params, params, sizeof(*params))) {
- if (!err)
- err = -EFAULT;
- }
+ if (err < 0)
+ return err;
- kfree(params);
+ if (copy_to_user(_params, params, sizeof(*params)))
+ return -EFAULT;
return err;
}
+static int do_hw_free(struct snd_pcm_substream *substream)
+{
+ int result = 0;
+
+ snd_pcm_sync_stop(substream, true);
+ if (substream->ops->hw_free)
+ result = substream->ops->hw_free(substream);
+ if (substream->managed_buffer_alloc)
+ snd_pcm_lib_free_pages(substream);
+ return result;
+}
+
static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
@@ -495,22 +920,28 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_SETUP:
- case SNDRV_PCM_STATE_PREPARED:
- break;
- default:
- snd_pcm_stream_unlock_irq(substream);
- return -EBADFD;
+ result = snd_pcm_buffer_access_lock(runtime);
+ if (result < 0)
+ return result;
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ switch (runtime->state) {
+ case SNDRV_PCM_STATE_SETUP:
+ case SNDRV_PCM_STATE_PREPARED:
+ if (atomic_read(&substream->mmap_count))
+ result = -EBADFD;
+ break;
+ default:
+ result = -EBADFD;
+ break;
+ }
}
- snd_pcm_stream_unlock_irq(substream);
- if (atomic_read(&substream->mmap_count))
- return -EBADFD;
- if (substream->ops->hw_free)
- result = substream->ops->hw_free(substream);
- runtime->status->state = SNDRV_PCM_STATE_OPEN;
- pm_qos_remove_request(&substream->latency_pm_qos_req);
+ if (result)
+ goto unlock;
+ result = do_hw_free(substream);
+ snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
+ cpu_latency_qos_remove_request(&substream->latency_pm_qos_req);
+ unlock:
+ snd_pcm_buffer_access_unlock(runtime);
return result;
}
@@ -523,14 +954,16 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- snd_pcm_stream_lock_irq(substream);
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- snd_pcm_stream_unlock_irq(substream);
- return -EBADFD;
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
}
- snd_pcm_stream_unlock_irq(substream);
- if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
+ if (params->tstamp_mode < 0 ||
+ params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
+ return -EINVAL;
+ if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12) &&
+ params->tstamp_type > SNDRV_PCM_TSTAMP_TYPE_LAST)
return -EINVAL;
if (params->avail_min == 0)
return -EINVAL;
@@ -544,22 +977,24 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
err = 0;
- snd_pcm_stream_lock_irq(substream);
- runtime->tstamp_mode = params->tstamp_mode;
- runtime->period_step = params->period_step;
- runtime->control->avail_min = params->avail_min;
- runtime->start_threshold = params->start_threshold;
- runtime->stop_threshold = params->stop_threshold;
- runtime->silence_threshold = params->silence_threshold;
- runtime->silence_size = params->silence_size;
- params->boundary = runtime->boundary;
- if (snd_pcm_running(substream)) {
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
- runtime->silence_size > 0)
- snd_pcm_playback_silence(substream, ULONG_MAX);
- err = snd_pcm_update_state(substream, runtime);
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ runtime->tstamp_mode = params->tstamp_mode;
+ if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12))
+ runtime->tstamp_type = params->tstamp_type;
+ runtime->period_step = params->period_step;
+ runtime->control->avail_min = params->avail_min;
+ runtime->start_threshold = params->start_threshold;
+ runtime->stop_threshold = params->stop_threshold;
+ runtime->silence_threshold = params->silence_threshold;
+ runtime->silence_size = params->silence_size;
+ params->boundary = runtime->boundary;
+ if (snd_pcm_running(substream)) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ runtime->silence_size > 0)
+ snd_pcm_playback_silence(substream, ULONG_MAX);
+ err = snd_pcm_update_state(substream, runtime);
+ }
}
- snd_pcm_stream_unlock_irq(substream);
return err;
}
@@ -576,60 +1011,109 @@ static int snd_pcm_sw_params_user(struct snd_pcm_substream *substream,
return err;
}
-int snd_pcm_status(struct snd_pcm_substream *substream,
- struct snd_pcm_status *status)
+static inline snd_pcm_uframes_t
+snd_pcm_calc_delay(struct snd_pcm_substream *substream)
+{
+ snd_pcm_uframes_t delay;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ delay = snd_pcm_playback_hw_avail(substream->runtime);
+ else
+ delay = snd_pcm_capture_avail(substream->runtime);
+ return delay + substream->runtime->delay;
+}
+
+int snd_pcm_status64(struct snd_pcm_substream *substream,
+ struct snd_pcm_status64 *status)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_stream_lock_irq(substream);
- status->state = runtime->status->state;
- status->suspended_state = runtime->status->suspended_state;
+ guard(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->state;
+ status->suspended_state = runtime->suspended_state;
if (status->state == SNDRV_PCM_STATE_OPEN)
- goto _end;
- status->trigger_tstamp = runtime->trigger_tstamp;
+ return 0;
+ status->trigger_tstamp_sec = runtime->trigger_tstamp.tv_sec;
+ status->trigger_tstamp_nsec = runtime->trigger_tstamp.tv_nsec;
if (snd_pcm_running(substream)) {
snd_pcm_update_hw_ptr(substream);
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
- status->tstamp = runtime->status->tstamp;
+ status->tstamp_sec = runtime->status->tstamp.tv_sec;
+ status->tstamp_nsec =
+ runtime->status->tstamp.tv_nsec;
+ status->driver_tstamp_sec =
+ runtime->driver_tstamp.tv_sec;
+ status->driver_tstamp_nsec =
+ runtime->driver_tstamp.tv_nsec;
+ status->audio_tstamp_sec =
+ runtime->status->audio_tstamp.tv_sec;
+ status->audio_tstamp_nsec =
+ runtime->status->audio_tstamp.tv_nsec;
+ 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 {
+ /* get tstamp only in fallback mode and only if enabled */
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
+ struct timespec64 tstamp;
+
+ snd_pcm_gettime(runtime, &tstamp);
+ status->tstamp_sec = tstamp.tv_sec;
+ status->tstamp_nsec = tstamp.tv_nsec;
+ }
}
- snd_pcm_gettime(runtime, &status->tstamp);
_tstamp_end:
status->appl_ptr = runtime->control->appl_ptr;
status->hw_ptr = runtime->status->hw_ptr;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- status->avail = snd_pcm_playback_avail(runtime);
- if (runtime->status->state == SNDRV_PCM_STATE_RUNNING ||
- runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
- status->delay = runtime->buffer_size - status->avail;
- status->delay += runtime->delay;
- } else
- status->delay = 0;
- } else {
- status->avail = snd_pcm_capture_avail(runtime);
- if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
- status->delay = status->avail + runtime->delay;
- else
- status->delay = 0;
- }
+ status->avail = snd_pcm_avail(substream);
+ status->delay = snd_pcm_running(substream) ?
+ snd_pcm_calc_delay(substream) : 0;
status->avail_max = runtime->avail_max;
status->overrange = runtime->overrange;
runtime->avail_max = 0;
runtime->overrange = 0;
- _end:
- snd_pcm_stream_unlock_irq(substream);
return 0;
}
-static int snd_pcm_status_user(struct snd_pcm_substream *substream,
- struct snd_pcm_status __user * _status)
+static int snd_pcm_status_user64(struct snd_pcm_substream *substream,
+ struct snd_pcm_status64 __user * _status,
+ bool ext)
{
- struct snd_pcm_status status;
+ struct snd_pcm_status64 status;
int res;
-
+
memset(&status, 0, sizeof(status));
- res = snd_pcm_status(substream, &status);
+ /*
+ * with extension, parameters are read/write,
+ * get audio_tstamp_data from user,
+ * ignore rest of status structure
+ */
+ if (ext && get_user(status.audio_tstamp_data,
+ (u32 __user *)(&_status->audio_tstamp_data)))
+ return -EFAULT;
+ res = snd_pcm_status64(substream, &status);
if (res < 0)
return res;
if (copy_to_user(_status, &status, sizeof(status)))
@@ -637,6 +1121,55 @@ static int snd_pcm_status_user(struct snd_pcm_substream *substream,
return 0;
}
+static int snd_pcm_status_user32(struct snd_pcm_substream *substream,
+ struct snd_pcm_status32 __user * _status,
+ bool ext)
+{
+ struct snd_pcm_status64 status64;
+ struct snd_pcm_status32 status32;
+ int res;
+
+ memset(&status64, 0, sizeof(status64));
+ memset(&status32, 0, sizeof(status32));
+ /*
+ * with extension, parameters are read/write,
+ * get audio_tstamp_data from user,
+ * ignore rest of status structure
+ */
+ if (ext && get_user(status64.audio_tstamp_data,
+ (u32 __user *)(&_status->audio_tstamp_data)))
+ return -EFAULT;
+ res = snd_pcm_status64(substream, &status64);
+ if (res < 0)
+ return res;
+
+ status32 = (struct snd_pcm_status32) {
+ .state = status64.state,
+ .trigger_tstamp_sec = status64.trigger_tstamp_sec,
+ .trigger_tstamp_nsec = status64.trigger_tstamp_nsec,
+ .tstamp_sec = status64.tstamp_sec,
+ .tstamp_nsec = status64.tstamp_nsec,
+ .appl_ptr = status64.appl_ptr,
+ .hw_ptr = status64.hw_ptr,
+ .delay = status64.delay,
+ .avail = status64.avail,
+ .avail_max = status64.avail_max,
+ .overrange = status64.overrange,
+ .suspended_state = status64.suspended_state,
+ .audio_tstamp_data = status64.audio_tstamp_data,
+ .audio_tstamp_sec = status64.audio_tstamp_sec,
+ .audio_tstamp_nsec = status64.audio_tstamp_nsec,
+ .driver_tstamp_sec = status64.audio_tstamp_sec,
+ .driver_tstamp_nsec = status64.audio_tstamp_nsec,
+ .audio_tstamp_accuracy = status64.audio_tstamp_accuracy,
+ };
+
+ if (copy_to_user(_status, &status32, sizeof(status32)))
+ return -EFAULT;
+
+ return 0;
+}
+
static int snd_pcm_channel_info(struct snd_pcm_substream *substream,
struct snd_pcm_channel_info * info)
{
@@ -645,17 +1178,15 @@ static int snd_pcm_channel_info(struct snd_pcm_substream *substream,
channel = info->channel;
runtime = substream->runtime;
- snd_pcm_stream_lock_irq(substream);
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
- snd_pcm_stream_unlock_irq(substream);
- return -EBADFD;
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
}
- snd_pcm_stream_unlock_irq(substream);
if (channel >= runtime->channels)
return -EINVAL;
memset(info, 0, sizeof(*info));
info->channel = channel;
- return substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, info);
+ return snd_pcm_ops_ioctl(substream, SNDRV_PCM_IOCTL1_CHANNEL_INFO, info);
}
static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream,
@@ -680,7 +1211,8 @@ static void snd_pcm_trigger_tstamp(struct snd_pcm_substream *substream)
if (runtime->trigger_master == NULL)
return;
if (runtime->trigger_master == substream) {
- snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
+ if (!runtime->trigger_tstamp_latched)
+ snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
} else {
snd_pcm_trigger_tstamp(runtime->trigger_master);
runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp;
@@ -688,11 +1220,17 @@ static void snd_pcm_trigger_tstamp(struct snd_pcm_substream *substream)
runtime->trigger_master = NULL;
}
+#define ACTION_ARG_IGNORE (__force snd_pcm_state_t)0
+
struct action_ops {
- int (*pre_action)(struct snd_pcm_substream *substream, int state);
- int (*do_action)(struct snd_pcm_substream *substream, int state);
- void (*undo_action)(struct snd_pcm_substream *substream, int state);
- void (*post_action)(struct snd_pcm_substream *substream, int state);
+ int (*pre_action)(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state);
+ int (*do_action)(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state);
+ void (*undo_action)(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state);
+ void (*post_action)(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state);
};
/*
@@ -700,18 +1238,25 @@ struct action_ops {
* Note: the stream state might be changed also on failure
* Note2: call with calling stream lock + link lock
*/
-static int snd_pcm_action_group(struct action_ops *ops,
+static int snd_pcm_action_group(const struct action_ops *ops,
struct snd_pcm_substream *substream,
- int state, int do_lock)
+ snd_pcm_state_t state,
+ bool stream_lock)
{
struct snd_pcm_substream *s = NULL;
struct snd_pcm_substream *s1;
- int res = 0;
+ int res = 0, depth = 1;
snd_pcm_group_for_each_entry(s, substream) {
- if (do_lock && s != substream)
- spin_lock_nested(&s->self_group.lock,
- SINGLE_DEPTH_NESTING);
+ if (s != substream) {
+ if (!stream_lock)
+ mutex_lock_nested(&s->runtime->buffer_mutex, depth);
+ else if (s->pcm->nonatomic)
+ mutex_lock_nested(&s->self_group.mutex, depth);
+ else
+ spin_lock_nested(&s->self_group.lock, depth);
+ depth++;
+ }
res = ops->pre_action(s, state);
if (res < 0)
goto _unlock;
@@ -734,14 +1279,18 @@ static int snd_pcm_action_group(struct action_ops *ops,
ops->post_action(s, state);
}
_unlock:
- if (do_lock) {
- /* unlock streams */
- snd_pcm_group_for_each_entry(s1, substream) {
- if (s1 != substream)
+ /* unlock streams */
+ snd_pcm_group_for_each_entry(s1, substream) {
+ if (s1 != substream) {
+ if (!stream_lock)
+ mutex_unlock(&s1->runtime->buffer_mutex);
+ else if (s1->pcm->nonatomic)
+ mutex_unlock(&s1->self_group.mutex);
+ else
spin_unlock(&s1->self_group.lock);
- if (s1 == s) /* end */
- break;
}
+ if (s1 == s) /* end */
+ break;
}
return res;
}
@@ -749,9 +1298,9 @@ static int snd_pcm_action_group(struct action_ops *ops,
/*
* Note: call with stream lock
*/
-static int snd_pcm_action_single(struct action_ops *ops,
+static int snd_pcm_action_single(const struct action_ops *ops,
struct snd_pcm_substream *substream,
- int state)
+ snd_pcm_state_t state)
{
int res;
@@ -766,116 +1315,174 @@ static int snd_pcm_action_single(struct action_ops *ops,
return res;
}
+static void snd_pcm_group_assign(struct snd_pcm_substream *substream,
+ struct snd_pcm_group *new_group)
+{
+ substream->group = new_group;
+ list_move(&substream->link_list, &new_group->substreams);
+}
+
+/*
+ * Unref and unlock the group, but keep the stream lock;
+ * when the group becomes empty and no longer referred, destroy itself
+ */
+static void snd_pcm_group_unref(struct snd_pcm_group *group,
+ struct snd_pcm_substream *substream)
+{
+ bool do_free;
+
+ if (!group)
+ return;
+ do_free = refcount_dec_and_test(&group->refs);
+ snd_pcm_group_unlock(group, substream->pcm->nonatomic);
+ if (do_free)
+ kfree(group);
+}
+
+/*
+ * Lock the group inside a stream lock and reference it;
+ * return the locked group object, or NULL if not linked
+ */
+static struct snd_pcm_group *
+snd_pcm_stream_group_ref(struct snd_pcm_substream *substream)
+{
+ bool nonatomic = substream->pcm->nonatomic;
+ struct snd_pcm_group *group;
+ bool trylock;
+
+ for (;;) {
+ if (!snd_pcm_stream_linked(substream))
+ return NULL;
+ group = substream->group;
+ /* block freeing the group object */
+ refcount_inc(&group->refs);
+
+ trylock = nonatomic ? mutex_trylock(&group->mutex) :
+ spin_trylock(&group->lock);
+ if (trylock)
+ break; /* OK */
+
+ /* re-lock for avoiding ABBA deadlock */
+ snd_pcm_stream_unlock(substream);
+ snd_pcm_group_lock(group, nonatomic);
+ snd_pcm_stream_lock(substream);
+
+ /* check the group again; the above opens a small race window */
+ if (substream->group == group)
+ break; /* OK */
+ /* group changed, try again */
+ snd_pcm_group_unref(group, substream);
+ }
+ return group;
+}
+
/*
* Note: call with stream lock
*/
-static int snd_pcm_action(struct action_ops *ops,
+static int snd_pcm_action(const struct action_ops *ops,
struct snd_pcm_substream *substream,
- int state)
+ snd_pcm_state_t state)
{
+ struct snd_pcm_group *group;
int res;
- if (snd_pcm_stream_linked(substream)) {
- if (!spin_trylock(&substream->group->lock)) {
- spin_unlock(&substream->self_group.lock);
- spin_lock(&substream->group->lock);
- spin_lock(&substream->self_group.lock);
- }
- res = snd_pcm_action_group(ops, substream, state, 1);
- spin_unlock(&substream->group->lock);
- } else {
+ group = snd_pcm_stream_group_ref(substream);
+ if (group)
+ res = snd_pcm_action_group(ops, substream, state, true);
+ else
res = snd_pcm_action_single(ops, substream, state);
- }
+ snd_pcm_group_unref(group, substream);
return res;
}
/*
* Note: don't use any locks before
*/
-static int snd_pcm_action_lock_irq(struct action_ops *ops,
+static int snd_pcm_action_lock_irq(const struct action_ops *ops,
struct snd_pcm_substream *substream,
- int state)
+ snd_pcm_state_t state)
{
- int res;
-
- read_lock_irq(&snd_pcm_link_rwlock);
- if (snd_pcm_stream_linked(substream)) {
- spin_lock(&substream->group->lock);
- spin_lock(&substream->self_group.lock);
- res = snd_pcm_action_group(ops, substream, state, 1);
- spin_unlock(&substream->self_group.lock);
- spin_unlock(&substream->group->lock);
- } else {
- spin_lock(&substream->self_group.lock);
- res = snd_pcm_action_single(ops, substream, state);
- spin_unlock(&substream->self_group.lock);
- }
- read_unlock_irq(&snd_pcm_link_rwlock);
- return res;
+ guard(pcm_stream_lock_irq)(substream);
+ return snd_pcm_action(ops, substream, state);
}
/*
*/
-static int snd_pcm_action_nonatomic(struct action_ops *ops,
+static int snd_pcm_action_nonatomic(const struct action_ops *ops,
struct snd_pcm_substream *substream,
- int state)
+ snd_pcm_state_t state)
{
int res;
- down_read(&snd_pcm_link_rwsem);
+ /* Guarantee the group members won't change during non-atomic action */
+ guard(rwsem_read)(&snd_pcm_link_rwsem);
+ res = snd_pcm_buffer_access_lock(substream->runtime);
+ if (res < 0)
+ return res;
if (snd_pcm_stream_linked(substream))
- res = snd_pcm_action_group(ops, substream, state, 0);
+ res = snd_pcm_action_group(ops, substream, state, false);
else
res = snd_pcm_action_single(ops, substream, state);
- up_read(&snd_pcm_link_rwsem);
+ snd_pcm_buffer_access_unlock(substream->runtime);
return res;
}
/*
* start callbacks
*/
-static int snd_pcm_pre_start(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_pre_start(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- if (runtime->status->state != SNDRV_PCM_STATE_PREPARED)
+ if (runtime->state != SNDRV_PCM_STATE_PREPARED)
return -EBADFD;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
!snd_pcm_playback_data(substream))
return -EPIPE;
+ runtime->trigger_tstamp_latched = false;
runtime->trigger_master = substream;
return 0;
}
-static int snd_pcm_do_start(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_do_start(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
+ int err;
+
if (substream->runtime->trigger_master != substream)
return 0;
- return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
+ err = substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);
+ /* XRUN happened during the start */
+ if (err == -EPIPE)
+ __snd_pcm_set_state(substream->runtime, SNDRV_PCM_STATE_XRUN);
+ return err;
}
-static void snd_pcm_undo_start(struct snd_pcm_substream *substream, int state)
+static void snd_pcm_undo_start(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
- if (substream->runtime->trigger_master == substream)
+ if (substream->runtime->trigger_master == substream) {
substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
+ substream->runtime->stop_operating = true;
+ }
}
-static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state)
+static void snd_pcm_post_start(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_trigger_tstamp(substream);
runtime->hw_ptr_jiffies = jiffies;
runtime->hw_ptr_buffer_jiffies = (runtime->buffer_size * HZ) /
runtime->rate;
- runtime->status->state = state;
+ __snd_pcm_set_state(runtime, state);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
snd_pcm_playback_silence(substream, ULONG_MAX);
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART,
- &runtime->trigger_tstamp);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTART);
}
-static struct action_ops snd_pcm_action_start = {
+static const struct action_ops snd_pcm_action_start = {
.pre_action = snd_pcm_pre_start,
.do_action = snd_pcm_do_start,
.undo_action = snd_pcm_undo_start,
@@ -885,6 +1492,9 @@ static struct action_ops snd_pcm_action_start = {
/**
* snd_pcm_start - start all linked streams
* @substream: the PCM substream instance
+ *
+ * Return: Zero if successful, or a negative error code.
+ * The stream lock must be acquired before calling this function.
*/
int snd_pcm_start(struct snd_pcm_substream *substream)
{
@@ -892,41 +1502,51 @@ int snd_pcm_start(struct snd_pcm_substream *substream)
SNDRV_PCM_STATE_RUNNING);
}
+/* take the stream lock and start the streams */
+static int snd_pcm_start_lock_irq(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream,
+ SNDRV_PCM_STATE_RUNNING);
+}
+
/*
* stop callbacks
*/
-static int snd_pcm_pre_stop(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_pre_stop(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
runtime->trigger_master = substream;
return 0;
}
-static int snd_pcm_do_stop(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_do_stop(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
if (substream->runtime->trigger_master == substream &&
- snd_pcm_running(substream))
+ snd_pcm_running(substream)) {
substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_STOP);
- return 0; /* unconditonally stop all substreams */
+ substream->runtime->stop_operating = true;
+ }
+ return 0; /* unconditionally stop all substreams */
}
-static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
+static void snd_pcm_post_stop(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- if (runtime->status->state != state) {
+ if (runtime->state != state) {
snd_pcm_trigger_tstamp(substream);
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP,
- &runtime->trigger_tstamp);
- runtime->status->state = state;
+ __snd_pcm_set_state(runtime, state);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTOP);
}
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
}
-static struct action_ops snd_pcm_action_stop = {
+static const struct action_ops snd_pcm_action_stop = {
.pre_action = snd_pcm_pre_stop,
.do_action = snd_pcm_do_stop,
.post_action = snd_pcm_post_stop
@@ -938,12 +1558,13 @@ static struct action_ops snd_pcm_action_stop = {
* @state: PCM state after stopping the stream
*
* The state of each stream is then changed to the given state unconditionally.
+ *
+ * Return: Zero if successful, or a negative error code.
*/
-int snd_pcm_stop(struct snd_pcm_substream *substream, int state)
+int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t state)
{
return snd_pcm_action(&snd_pcm_action_stop, substream, state);
}
-
EXPORT_SYMBOL(snd_pcm_stop);
/**
@@ -952,6 +1573,8 @@ EXPORT_SYMBOL(snd_pcm_stop);
*
* After stopping, the state is changed to SETUP.
* Unlike snd_pcm_stop(), this affects only the given stream.
+ *
+ * Return: Zero if successful, or a negative error code.
*/
int snd_pcm_drain_done(struct snd_pcm_substream *substream)
{
@@ -959,71 +1582,87 @@ int snd_pcm_drain_done(struct snd_pcm_substream *substream)
SNDRV_PCM_STATE_SETUP);
}
+/**
+ * snd_pcm_stop_xrun - stop the running streams as XRUN
+ * @substream: the PCM substream instance
+ *
+ * This stops the given running substream (and all linked substreams) as XRUN.
+ * Unlike snd_pcm_stop(), this function takes the substream lock by itself.
+ *
+ * Return: Zero if successful, or a negative error code.
+ */
+int snd_pcm_stop_xrun(struct snd_pcm_substream *substream)
+{
+ guard(pcm_stream_lock_irqsave)(substream);
+ if (substream->runtime && snd_pcm_running(substream))
+ __snd_pcm_xrun(substream);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_stop_xrun);
+
/*
- * pause callbacks
+ * pause callbacks: pass boolean (to start pause or resume) as state argument
*/
-static int snd_pcm_pre_pause(struct snd_pcm_substream *substream, int push)
+#define pause_pushed(state) (__force bool)(state)
+
+static int snd_pcm_pre_pause(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
if (!(runtime->info & SNDRV_PCM_INFO_PAUSE))
return -ENOSYS;
- if (push) {
- if (runtime->status->state != SNDRV_PCM_STATE_RUNNING)
+ if (pause_pushed(state)) {
+ if (runtime->state != SNDRV_PCM_STATE_RUNNING)
return -EBADFD;
- } else if (runtime->status->state != SNDRV_PCM_STATE_PAUSED)
+ } else if (runtime->state != SNDRV_PCM_STATE_PAUSED)
return -EBADFD;
runtime->trigger_master = substream;
return 0;
}
-static int snd_pcm_do_pause(struct snd_pcm_substream *substream, int push)
+static int snd_pcm_do_pause(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
if (substream->runtime->trigger_master != substream)
return 0;
- /* some drivers might use hw_ptr to recover from the pause -
- update the hw_ptr now */
- if (push)
- snd_pcm_update_hw_ptr(substream);
/* The jiffies check in snd_pcm_update_hw_ptr*() is done by
- * a delta betwen the current jiffies, this gives a large enough
+ * a delta between the current jiffies, this gives a large enough
* delta, effectively to skip the check once.
*/
substream->runtime->hw_ptr_jiffies = jiffies - HZ * 1000;
return substream->ops->trigger(substream,
- push ? SNDRV_PCM_TRIGGER_PAUSE_PUSH :
- SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
+ pause_pushed(state) ?
+ SNDRV_PCM_TRIGGER_PAUSE_PUSH :
+ SNDRV_PCM_TRIGGER_PAUSE_RELEASE);
}
-static void snd_pcm_undo_pause(struct snd_pcm_substream *substream, int push)
+static void snd_pcm_undo_pause(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
if (substream->runtime->trigger_master == substream)
substream->ops->trigger(substream,
- push ? SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
+ pause_pushed(state) ?
+ SNDRV_PCM_TRIGGER_PAUSE_RELEASE :
SNDRV_PCM_TRIGGER_PAUSE_PUSH);
}
-static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push)
+static void snd_pcm_post_pause(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_trigger_tstamp(substream);
- if (push) {
- runtime->status->state = SNDRV_PCM_STATE_PAUSED;
- if (substream->timer)
- snd_timer_notify(substream->timer,
- SNDRV_TIMER_EVENT_MPAUSE,
- &runtime->trigger_tstamp);
+ if (pause_pushed(state)) {
+ __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_PAUSED);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MPAUSE);
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
} else {
- runtime->status->state = SNDRV_PCM_STATE_RUNNING;
- if (substream->timer)
- snd_timer_notify(substream->timer,
- SNDRV_TIMER_EVENT_MCONTINUE,
- &runtime->trigger_tstamp);
+ __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_RUNNING);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MCONTINUE);
}
}
-static struct action_ops snd_pcm_action_pause = {
+static const struct action_ops snd_pcm_action_pause = {
.pre_action = snd_pcm_pre_pause,
.do_action = snd_pcm_do_pause,
.undo_action = snd_pcm_undo_pause,
@@ -1033,24 +1672,41 @@ static struct action_ops snd_pcm_action_pause = {
/*
* Push/release the pause for all linked streams.
*/
-static int snd_pcm_pause(struct snd_pcm_substream *substream, int push)
+static int snd_pcm_pause(struct snd_pcm_substream *substream, bool push)
+{
+ return snd_pcm_action(&snd_pcm_action_pause, substream,
+ (__force snd_pcm_state_t)push);
+}
+
+static int snd_pcm_pause_lock_irq(struct snd_pcm_substream *substream,
+ bool push)
{
- return snd_pcm_action(&snd_pcm_action_pause, substream, push);
+ return snd_pcm_action_lock_irq(&snd_pcm_action_pause, substream,
+ (__force snd_pcm_state_t)push);
}
#ifdef CONFIG_PM
-/* suspend */
+/* suspend callback: state argument ignored */
-static int snd_pcm_pre_suspend(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_pre_suspend(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
+ switch (runtime->state) {
+ case SNDRV_PCM_STATE_SUSPENDED:
+ return -EBUSY;
+ /* unresumable PCM state; return -EBUSY for skipping suspend */
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_SETUP:
+ case SNDRV_PCM_STATE_DISCONNECTED:
return -EBUSY;
+ }
runtime->trigger_master = substream;
return 0;
}
-static int snd_pcm_do_suspend(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_do_suspend(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
if (runtime->trigger_master != substream)
@@ -1058,55 +1714,51 @@ static int snd_pcm_do_suspend(struct snd_pcm_substream *substream, int state)
if (! snd_pcm_running(substream))
return 0;
substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND);
+ runtime->stop_operating = true;
return 0; /* suspend unconditionally */
}
-static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state)
+static void snd_pcm_post_suspend(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_trigger_tstamp(substream);
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSUSPEND,
- &runtime->trigger_tstamp);
- runtime->status->suspended_state = runtime->status->state;
- runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
+ runtime->suspended_state = runtime->state;
+ runtime->status->suspended_state = runtime->suspended_state;
+ __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_SUSPENDED);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSUSPEND);
wake_up(&runtime->sleep);
wake_up(&runtime->tsleep);
}
-static struct action_ops snd_pcm_action_suspend = {
+static const struct action_ops snd_pcm_action_suspend = {
.pre_action = snd_pcm_pre_suspend,
.do_action = snd_pcm_do_suspend,
.post_action = snd_pcm_post_suspend
};
-/**
+/*
* snd_pcm_suspend - trigger SUSPEND to all linked streams
* @substream: the PCM substream
*
* After this call, all streams are changed to SUSPENDED state.
+ *
+ * Return: Zero if successful, or a negative error code.
*/
-int snd_pcm_suspend(struct snd_pcm_substream *substream)
+static int snd_pcm_suspend(struct snd_pcm_substream *substream)
{
- int err;
- unsigned long flags;
-
- if (! substream)
- return 0;
-
- snd_pcm_stream_lock_irqsave(substream, flags);
- err = snd_pcm_action(&snd_pcm_action_suspend, substream, 0);
- snd_pcm_stream_unlock_irqrestore(substream, flags);
- return err;
+ guard(pcm_stream_lock_irqsave)(substream);
+ return snd_pcm_action(&snd_pcm_action_suspend, substream,
+ ACTION_ARG_IGNORE);
}
-EXPORT_SYMBOL(snd_pcm_suspend);
-
/**
* snd_pcm_suspend_all - trigger SUSPEND to all substreams in the given pcm
* @pcm: the PCM instance
*
* After this call, all streams are changed to SUSPENDED state.
+ *
+ * Return: Zero if successful (or @pcm is %NULL), or a negative error code.
*/
int snd_pcm_suspend_all(struct snd_pcm *pcm)
{
@@ -1116,64 +1768,76 @@ int snd_pcm_suspend_all(struct snd_pcm *pcm)
if (! pcm)
return 0;
- for (stream = 0; stream < 2; stream++) {
- for (substream = pcm->streams[stream].substream;
- substream; substream = substream->next) {
- /* FIXME: the open/close code should lock this as well */
- if (substream->runtime == NULL)
- continue;
- err = snd_pcm_suspend(substream);
- if (err < 0 && err != -EBUSY)
- return err;
- }
+ for_each_pcm_substream(pcm, stream, substream) {
+ /* FIXME: the open/close code should lock this as well */
+ if (!substream->runtime)
+ continue;
+
+ /*
+ * Skip BE dai link PCM's that are internal and may
+ * not have their substream ops set.
+ */
+ if (!substream->ops)
+ continue;
+
+ err = snd_pcm_suspend(substream);
+ if (err < 0 && err != -EBUSY)
+ return err;
}
+
+ for_each_pcm_substream(pcm, stream, substream)
+ snd_pcm_sync_stop(substream, false);
+
return 0;
}
-
EXPORT_SYMBOL(snd_pcm_suspend_all);
-/* resume */
+/* resume callbacks: state argument ignored */
-static int snd_pcm_pre_resume(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_pre_resume(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
+ if (runtime->state != SNDRV_PCM_STATE_SUSPENDED)
+ return -EBADFD;
if (!(runtime->info & SNDRV_PCM_INFO_RESUME))
return -ENOSYS;
runtime->trigger_master = substream;
return 0;
}
-static int snd_pcm_do_resume(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_do_resume(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
if (runtime->trigger_master != substream)
return 0;
/* DMA not running previously? */
- if (runtime->status->suspended_state != SNDRV_PCM_STATE_RUNNING &&
- (runtime->status->suspended_state != SNDRV_PCM_STATE_DRAINING ||
+ if (runtime->suspended_state != SNDRV_PCM_STATE_RUNNING &&
+ (runtime->suspended_state != SNDRV_PCM_STATE_DRAINING ||
substream->stream != SNDRV_PCM_STREAM_PLAYBACK))
return 0;
return substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_RESUME);
}
-static void snd_pcm_undo_resume(struct snd_pcm_substream *substream, int state)
+static void snd_pcm_undo_resume(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
if (substream->runtime->trigger_master == substream &&
snd_pcm_running(substream))
substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_SUSPEND);
}
-static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state)
+static void snd_pcm_post_resume(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_trigger_tstamp(substream);
- if (substream->timer)
- snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME,
- &runtime->trigger_tstamp);
- runtime->status->state = runtime->status->suspended_state;
+ __snd_pcm_set_state(runtime, runtime->suspended_state);
+ snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MRESUME);
}
-static struct action_ops snd_pcm_action_resume = {
+static const struct action_ops snd_pcm_action_resume = {
.pre_action = snd_pcm_pre_resume,
.do_action = snd_pcm_do_resume,
.undo_action = snd_pcm_undo_resume,
@@ -1182,14 +1846,8 @@ static struct action_ops snd_pcm_action_resume = {
static int snd_pcm_resume(struct snd_pcm_substream *substream)
{
- struct snd_card *card = substream->pcm->card;
- int res;
-
- snd_power_lock(card);
- if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0)
- res = snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0);
- snd_power_unlock(card);
- return res;
+ return snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream,
+ ACTION_ARG_IGNORE);
}
#else
@@ -1208,41 +1866,29 @@ static int snd_pcm_resume(struct snd_pcm_substream *substream)
*/
static int snd_pcm_xrun(struct snd_pcm_substream *substream)
{
- struct snd_card *card = substream->pcm->card;
struct snd_pcm_runtime *runtime = substream->runtime;
- int result;
- snd_power_lock(card);
- if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
- result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
- if (result < 0)
- goto _unlock;
- }
-
- snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
+ guard(pcm_stream_lock_irq)(substream);
+ switch (runtime->state) {
case SNDRV_PCM_STATE_XRUN:
- result = 0; /* already there */
- break;
+ return 0; /* already there */
case SNDRV_PCM_STATE_RUNNING:
- result = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
- break;
+ __snd_pcm_xrun(substream);
+ return 0;
default:
- result = -EBADFD;
+ return -EBADFD;
}
- snd_pcm_stream_unlock_irq(substream);
- _unlock:
- snd_power_unlock(card);
- return result;
}
/*
* reset ioctl
*/
-static int snd_pcm_pre_reset(struct snd_pcm_substream *substream, int state)
+/* reset callbacks: state argument ignored */
+static int snd_pcm_pre_reset(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- switch (runtime->status->state) {
+ switch (runtime->state) {
case SNDRV_PCM_STATE_RUNNING:
case SNDRV_PCM_STATE_PREPARED:
case SNDRV_PCM_STATE_PAUSED:
@@ -1253,12 +1899,14 @@ static int snd_pcm_pre_reset(struct snd_pcm_substream *substream, int state)
}
}
-static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_do_reset(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL);
+ int err = snd_pcm_ops_ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL);
if (err < 0)
return err;
+ guard(pcm_stream_lock_irq)(substream);
runtime->hw_ptr_base = 0;
runtime->hw_ptr_interrupt = runtime->status->hw_ptr -
runtime->status->hw_ptr % runtime->period_size;
@@ -1267,16 +1915,18 @@ static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state)
return 0;
}
-static void snd_pcm_post_reset(struct snd_pcm_substream *substream, int state)
+static void snd_pcm_post_reset(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
+ guard(pcm_stream_lock_irq)(substream);
runtime->control->appl_ptr = runtime->status->hw_ptr;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
runtime->silence_size > 0)
snd_pcm_playback_silence(substream, ULONG_MAX);
}
-static struct action_ops snd_pcm_action_reset = {
+static const struct action_ops snd_pcm_action_reset = {
.pre_action = snd_pcm_pre_reset,
.do_action = snd_pcm_do_reset,
.post_action = snd_pcm_post_reset
@@ -1284,19 +1934,22 @@ static struct action_ops snd_pcm_action_reset = {
static int snd_pcm_reset(struct snd_pcm_substream *substream)
{
- return snd_pcm_action_nonatomic(&snd_pcm_action_reset, substream, 0);
+ return snd_pcm_action_nonatomic(&snd_pcm_action_reset, substream,
+ ACTION_ARG_IGNORE);
}
/*
* prepare ioctl
*/
-/* we use the second argument for updating f_flags */
+/* pass f_flags as state argument */
static int snd_pcm_pre_prepare(struct snd_pcm_substream *substream,
- int f_flags)
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
- runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
+ int f_flags = (__force int)state;
+
+ if (runtime->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
return -EBADFD;
if (snd_pcm_running(substream))
return -EBUSY;
@@ -1304,23 +1957,26 @@ static int snd_pcm_pre_prepare(struct snd_pcm_substream *substream,
return 0;
}
-static int snd_pcm_do_prepare(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_do_prepare(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
int err;
+ snd_pcm_sync_stop(substream, true);
err = substream->ops->prepare(substream);
if (err < 0)
return err;
- return snd_pcm_do_reset(substream, 0);
+ return snd_pcm_do_reset(substream, state);
}
-static void snd_pcm_post_prepare(struct snd_pcm_substream *substream, int state)
+static void snd_pcm_post_prepare(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
runtime->control->appl_ptr = runtime->status->hw_ptr;
- runtime->status->state = SNDRV_PCM_STATE_PREPARED;
+ snd_pcm_set_state(substream, SNDRV_PCM_STATE_PREPARED);
}
-static struct action_ops snd_pcm_action_prepare = {
+static const struct action_ops snd_pcm_action_prepare = {
.pre_action = snd_pcm_pre_prepare,
.do_action = snd_pcm_do_prepare,
.post_action = snd_pcm_post_prepare
@@ -1330,12 +1986,12 @@ static struct action_ops snd_pcm_action_prepare = {
* snd_pcm_prepare - prepare the PCM substream to be triggerable
* @substream: the PCM substream instance
* @file: file to refer f_flags
+ *
+ * Return: Zero if successful, or a negative error code.
*/
static int snd_pcm_prepare(struct snd_pcm_substream *substream,
struct file *file)
{
- int res;
- struct snd_card *card = substream->pcm->card;
int f_flags;
if (file)
@@ -1343,66 +1999,97 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream,
else
f_flags = substream->f_flags;
- snd_power_lock(card);
- if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0)
- res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare,
- substream, f_flags);
- snd_power_unlock(card);
- return res;
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ switch (substream->runtime->state) {
+ case SNDRV_PCM_STATE_PAUSED:
+ snd_pcm_pause(substream, false);
+ fallthrough;
+ case SNDRV_PCM_STATE_SUSPENDED:
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+ break;
+ }
+ }
+
+ return snd_pcm_action_nonatomic(&snd_pcm_action_prepare,
+ substream,
+ (__force snd_pcm_state_t)f_flags);
}
/*
* drain ioctl
*/
-static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state)
+/* drain init callbacks: state argument ignored */
+static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
- substream->runtime->trigger_master = substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ switch (runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_DISCONNECTED:
+ case SNDRV_PCM_STATE_SUSPENDED:
+ return -EBADFD;
+ }
+ runtime->trigger_master = substream;
return 0;
}
-static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream, int state)
+static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
struct snd_pcm_runtime *runtime = substream->runtime;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- switch (runtime->status->state) {
+ switch (runtime->state) {
case SNDRV_PCM_STATE_PREPARED:
/* start playback stream if possible */
if (! snd_pcm_playback_empty(substream)) {
snd_pcm_do_start(substream, SNDRV_PCM_STATE_DRAINING);
snd_pcm_post_start(substream, SNDRV_PCM_STATE_DRAINING);
+ } else {
+ __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_SETUP);
}
break;
case SNDRV_PCM_STATE_RUNNING:
- runtime->status->state = SNDRV_PCM_STATE_DRAINING;
+ __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_DRAINING);
+ break;
+ case SNDRV_PCM_STATE_XRUN:
+ __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_SETUP);
break;
default:
break;
}
} else {
/* stop running stream */
- if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) {
- int new_state = snd_pcm_capture_avail(runtime) > 0 ?
+ if (runtime->state == SNDRV_PCM_STATE_RUNNING) {
+ snd_pcm_state_t new_state;
+
+ new_state = snd_pcm_capture_avail(runtime) > 0 ?
SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP;
snd_pcm_do_stop(substream, new_state);
snd_pcm_post_stop(substream, new_state);
}
}
+
+ if (runtime->state == SNDRV_PCM_STATE_DRAINING &&
+ runtime->trigger_master == substream &&
+ (runtime->hw.info & SNDRV_PCM_INFO_DRAIN_TRIGGER))
+ return substream->ops->trigger(substream,
+ SNDRV_PCM_TRIGGER_DRAIN);
+
return 0;
}
-static void snd_pcm_post_drain_init(struct snd_pcm_substream *substream, int state)
+static void snd_pcm_post_drain_init(struct snd_pcm_substream *substream,
+ snd_pcm_state_t state)
{
}
-static struct action_ops snd_pcm_action_drain_init = {
+static const struct action_ops snd_pcm_action_drain_init = {
.pre_action = snd_pcm_pre_drain_init,
.do_action = snd_pcm_do_drain_init,
.post_action = snd_pcm_post_drain_init
};
-static int snd_pcm_drop(struct snd_pcm_substream *substream);
-
/*
* Drain the stream(s).
* When the substream is linked, sync until the draining of all playback streams
@@ -1416,39 +2103,31 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
struct snd_card *card;
struct snd_pcm_runtime *runtime;
struct snd_pcm_substream *s;
- wait_queue_t wait;
+ struct snd_pcm_group *group;
+ wait_queue_entry_t wait;
int result = 0;
int nonblock = 0;
card = substream->pcm->card;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
- snd_power_lock(card);
- if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
- result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
- if (result < 0) {
- snd_power_unlock(card);
- return result;
- }
- }
-
if (file) {
if (file->f_flags & O_NONBLOCK)
nonblock = 1;
} else if (substream->f_flags & O_NONBLOCK)
nonblock = 1;
- down_read(&snd_pcm_link_rwsem);
snd_pcm_stream_lock_irq(substream);
/* resume pause */
- if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
- snd_pcm_pause(substream, 0);
+ if (runtime->state == SNDRV_PCM_STATE_PAUSED)
+ snd_pcm_pause(substream, false);
/* pre-start/stop - all running streams are changed to DRAINING state */
- result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0);
+ result = snd_pcm_action(&snd_pcm_action_drain_init, substream,
+ ACTION_ARG_IGNORE);
if (result < 0)
goto unlock;
/* in non-blocking, we don't wait in ioctl but let caller poll */
@@ -1466,33 +2145,55 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
}
/* find a substream to drain */
to_check = NULL;
+ group = snd_pcm_stream_group_ref(substream);
snd_pcm_group_for_each_entry(s, substream) {
if (s->stream != SNDRV_PCM_STREAM_PLAYBACK)
continue;
runtime = s->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
+ if (runtime->state == SNDRV_PCM_STATE_DRAINING) {
to_check = runtime;
break;
}
}
+ snd_pcm_group_unref(group, substream);
if (!to_check)
break; /* all drained */
init_waitqueue_entry(&wait, current);
- add_wait_queue(&to_check->sleep, &wait);
set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&to_check->sleep, &wait);
snd_pcm_stream_unlock_irq(substream);
- up_read(&snd_pcm_link_rwsem);
- snd_power_unlock(card);
- tout = schedule_timeout(10 * HZ);
- snd_power_lock(card);
- down_read(&snd_pcm_link_rwsem);
+ if (runtime->no_period_wakeup)
+ tout = MAX_SCHEDULE_TIMEOUT;
+ else {
+ tout = 100;
+ if (runtime->rate) {
+ long t = runtime->buffer_size * 1100 / runtime->rate;
+ tout = max(t, tout);
+ }
+ tout = msecs_to_jiffies(tout);
+ }
+ tout = schedule_timeout(tout);
+
snd_pcm_stream_lock_irq(substream);
- remove_wait_queue(&to_check->sleep, &wait);
+ group = snd_pcm_stream_group_ref(substream);
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->runtime == to_check) {
+ remove_wait_queue(&to_check->sleep, &wait);
+ break;
+ }
+ }
+ snd_pcm_group_unref(group, substream);
+
+ if (card->shutdown) {
+ result = -ENODEV;
+ break;
+ }
if (tout == 0) {
- if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
+ if (substream->runtime->state == SNDRV_PCM_STATE_SUSPENDED)
result = -ESTRPIPE;
else {
- snd_printd("playback drain error (DMA or IRQ trouble?)\n");
+ dev_dbg(substream->pcm->card->dev,
+ "playback drain timeout (DMA or IRQ trouble?)\n");
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
result = -EIO;
}
@@ -1502,8 +2203,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
unlock:
snd_pcm_stream_unlock_irq(substream);
- up_read(&snd_pcm_link_rwsem);
- snd_power_unlock(card);
return result;
}
@@ -1516,55 +2215,44 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
static int snd_pcm_drop(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
- struct snd_card *card;
int result = 0;
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- card = substream->pcm->card;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
- runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED ||
- runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
return -EBADFD;
- snd_pcm_stream_lock_irq(substream);
+ guard(pcm_stream_lock_irq)(substream);
/* resume pause */
- if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
- snd_pcm_pause(substream, 0);
+ if (runtime->state == SNDRV_PCM_STATE_PAUSED)
+ snd_pcm_pause(substream, false);
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
/* runtime->control->appl_ptr = runtime->status->hw_ptr; */
- snd_pcm_stream_unlock_irq(substream);
return result;
}
-/* WARNING: Don't forget to fput back the file */
-static struct file *snd_pcm_file_fd(int fd)
+static bool is_pcm_file(struct file *file)
{
- struct file *file;
- struct inode *inode;
+ struct inode *inode = file_inode(file);
+ struct snd_pcm *pcm;
unsigned int minor;
- file = fget(fd);
- if (!file)
- return NULL;
- inode = file->f_path.dentry->d_inode;
- if (!S_ISCHR(inode->i_mode) ||
- imajor(inode) != snd_major) {
- fput(file);
- return NULL;
- }
+ if (!S_ISCHR(inode->i_mode) || imajor(inode) != snd_major)
+ return false;
minor = iminor(inode);
- if (!snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK) &&
- !snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE)) {
- fput(file);
- return NULL;
- }
- return file;
+ pcm = snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
+ if (!pcm)
+ pcm = snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE);
+ if (!pcm)
+ return false;
+ snd_card_unref(pcm->card);
+ return true;
}
/*
@@ -1572,80 +2260,90 @@ static struct file *snd_pcm_file_fd(int fd)
*/
static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
{
- int res = 0;
- struct file *file;
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream1;
+ struct snd_pcm_group *group __free(kfree) = NULL;
+ struct snd_pcm_group *target_group;
+ bool nonatomic = substream->pcm->nonatomic;
+ CLASS(fd, f)(fd);
- file = snd_pcm_file_fd(fd);
- if (!file)
+ if (fd_empty(f))
return -EBADFD;
- pcm_file = file->private_data;
+ if (!is_pcm_file(fd_file(f)))
+ return -EBADFD;
+
+ pcm_file = fd_file(f)->private_data;
substream1 = pcm_file->substream;
- down_write(&snd_pcm_link_rwsem);
- write_lock_irq(&snd_pcm_link_rwlock);
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||
- substream->runtime->status->state != substream1->runtime->status->state) {
- res = -EBADFD;
- goto _end;
- }
- if (snd_pcm_stream_linked(substream1)) {
- res = -EALREADY;
- goto _end;
- }
- if (!snd_pcm_stream_linked(substream)) {
- substream->group = kmalloc(sizeof(struct snd_pcm_group), GFP_ATOMIC);
- if (substream->group == NULL) {
- res = -ENOMEM;
- goto _end;
+
+ if (substream == substream1)
+ return -EINVAL;
+
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group)
+ return -ENOMEM;
+ snd_pcm_group_init(group);
+
+ guard(rwsem_write)(&snd_pcm_link_rwsem);
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN ||
+ substream->runtime->state != substream1->runtime->state ||
+ substream->pcm->nonatomic != substream1->pcm->nonatomic)
+ return -EBADFD;
+ if (snd_pcm_stream_linked(substream1))
+ return -EALREADY;
+
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ if (!snd_pcm_stream_linked(substream)) {
+ snd_pcm_group_assign(substream, group);
+ group = NULL; /* assigned, don't free this one below */
}
- spin_lock_init(&substream->group->lock);
- INIT_LIST_HEAD(&substream->group->substreams);
- list_add_tail(&substream->link_list, &substream->group->substreams);
- substream->group->count = 1;
- }
- list_add_tail(&substream1->link_list, &substream->group->substreams);
- substream->group->count++;
- substream1->group = substream->group;
- _end:
- write_unlock_irq(&snd_pcm_link_rwlock);
- up_write(&snd_pcm_link_rwsem);
- fput(file);
- return res;
+ target_group = substream->group;
+ }
+
+ snd_pcm_group_lock_irq(target_group, nonatomic);
+ snd_pcm_stream_lock_nested(substream1);
+ snd_pcm_group_assign(substream1, target_group);
+ refcount_inc(&target_group->refs);
+ snd_pcm_stream_unlock(substream1);
+ snd_pcm_group_unlock_irq(target_group, nonatomic);
+ return 0;
}
static void relink_to_local(struct snd_pcm_substream *substream)
{
- substream->group = &substream->self_group;
- INIT_LIST_HEAD(&substream->self_group.substreams);
- list_add_tail(&substream->link_list, &substream->self_group.substreams);
+ snd_pcm_stream_lock_nested(substream);
+ snd_pcm_group_assign(substream, &substream->self_group);
+ snd_pcm_stream_unlock(substream);
}
static int snd_pcm_unlink(struct snd_pcm_substream *substream)
{
- struct snd_pcm_substream *s;
- int res = 0;
+ struct snd_pcm_group *group;
+ bool nonatomic = substream->pcm->nonatomic;
+ bool do_free = false;
+
+ guard(rwsem_write)(&snd_pcm_link_rwsem);
+
+ if (!snd_pcm_stream_linked(substream))
+ return -EALREADY;
+
+ group = substream->group;
+ snd_pcm_group_lock_irq(group, nonatomic);
- down_write(&snd_pcm_link_rwsem);
- write_lock_irq(&snd_pcm_link_rwlock);
- if (!snd_pcm_stream_linked(substream)) {
- res = -EALREADY;
- goto _end;
- }
- list_del(&substream->link_list);
- substream->group->count--;
- if (substream->group->count == 1) { /* detach the last stream, too */
- snd_pcm_group_for_each_entry(s, substream) {
- relink_to_local(s);
- break;
- }
- kfree(substream->group);
- }
relink_to_local(substream);
- _end:
- write_unlock_irq(&snd_pcm_link_rwlock);
- up_write(&snd_pcm_link_rwsem);
- return res;
+ refcount_dec(&group->refs);
+
+ /* detach the last stream, too */
+ if (list_is_singular(&group->substreams)) {
+ relink_to_local(list_first_entry(&group->substreams,
+ struct snd_pcm_substream,
+ link_list));
+ do_free = refcount_dec_and_test(&group->refs);
+ }
+
+ snd_pcm_group_unlock_irq(group, nonatomic);
+ if (do_free)
+ kfree(group);
+ return 0;
}
/*
@@ -1692,20 +2390,21 @@ static int snd_pcm_hw_rule_mulkdiv(struct snd_pcm_hw_params *params,
static int snd_pcm_hw_rule_format(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
- unsigned int k;
- struct snd_interval *i = hw_param_interval(params, rule->deps[0]);
+ snd_pcm_format_t k;
+ const struct snd_interval *i =
+ hw_param_interval_c(params, rule->deps[0]);
struct snd_mask m;
struct snd_mask *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
snd_mask_any(&m);
- for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) {
+ pcm_for_each_format(k) {
int bits;
- if (! snd_mask_test(mask, k))
+ if (!snd_mask_test_format(mask, k))
continue;
bits = snd_pcm_format_physical_width(k);
if (bits <= 0)
continue; /* ignore invalid formats */
if ((unsigned)bits < i->min || (unsigned)bits > i->max)
- snd_mask_reset(&m, k);
+ snd_mask_reset(&m, (__force unsigned)k);
}
return snd_mask_refine(mask, &m);
}
@@ -1714,14 +2413,15 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_interval t;
- unsigned int k;
+ snd_pcm_format_t k;
+
t.min = UINT_MAX;
t.max = 0;
t.openmin = 0;
t.openmax = 0;
- for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) {
+ pcm_for_each_format(k) {
int bits;
- if (! snd_mask_test(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), k))
+ if (!snd_mask_test_format(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), k))
continue;
bits = snd_pcm_format_physical_width(k);
if (bits <= 0)
@@ -1735,12 +2435,18 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params,
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
}
-#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12
+#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12 ||\
+ SNDRV_PCM_RATE_128000 != 1 << 19
#error "Change this table"
#endif
-static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100,
- 48000, 64000, 88200, 96000, 176400, 192000 };
+/* NOTE: the list is unsorted! */
+static const unsigned int rates[] = {
+ 5512, 8000, 11025, 16000, 22050, 32000, 44100,
+ 48000, 64000, 88200, 96000, 176400, 192000, 352800, 384000, 705600, 768000,
+ /* extended */
+ 12000, 24000, 128000
+};
const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = {
.count = ARRAY_SIZE(rates),
@@ -1769,7 +2475,42 @@ static int snd_pcm_hw_rule_buffer_bytes_max(struct snd_pcm_hw_params *params,
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
}
-int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream)
+static int snd_pcm_hw_rule_subformats(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_mask *sfmask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT);
+ struct snd_mask *fmask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ u32 *subformats = rule->private;
+ snd_pcm_format_t f;
+ struct snd_mask m;
+
+ snd_mask_none(&m);
+ /* All PCMs support at least the default STD subformat. */
+ snd_mask_set(&m, (__force unsigned)SNDRV_PCM_SUBFORMAT_STD);
+
+ pcm_for_each_format(f) {
+ if (!snd_mask_test(fmask, (__force unsigned)f))
+ continue;
+
+ if (f == SNDRV_PCM_FORMAT_S32_LE && *subformats)
+ m.bits[0] |= *subformats;
+ else if (snd_pcm_format_linear(f))
+ snd_mask_set(&m, (__force unsigned)SNDRV_PCM_SUBFORMAT_MSBITS_MAX);
+ }
+
+ return snd_mask_refine(sfmask, &m);
+}
+
+static int snd_pcm_hw_constraint_subformats(struct snd_pcm_runtime *runtime,
+ unsigned int cond, u32 *subformats)
+{
+ return snd_pcm_hw_rule_add(runtime, cond, -1,
+ snd_pcm_hw_rule_subformats, (void *)subformats,
+ SNDRV_PCM_HW_PARAM_SUBFORMAT,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1);
+}
+
+static int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints;
@@ -1893,7 +2634,7 @@ int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream)
return 0;
}
-int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
+static int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hardware *hw = &runtime->hw;
@@ -1901,16 +2642,16 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
unsigned int mask = 0;
if (hw->info & SNDRV_PCM_INFO_INTERLEAVED)
- mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+ mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_RW_INTERLEAVED);
if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)
- mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED;
- if (hw->info & SNDRV_PCM_INFO_MMAP) {
+ mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
+ if (hw_support_mmap(substream)) {
if (hw->info & SNDRV_PCM_INFO_INTERLEAVED)
- mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED;
+ mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_MMAP_INTERLEAVED);
if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED)
- mask |= 1 << SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED;
+ mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED);
if (hw->info & SNDRV_PCM_INFO_COMPLEX)
- mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX;
+ mask |= PARAM_MASK_BIT(SNDRV_PCM_ACCESS_MMAP_COMPLEX);
}
err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask);
if (err < 0)
@@ -1920,7 +2661,7 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
if (err < 0)
return err;
- err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD);
+ err = snd_pcm_hw_constraint_subformats(runtime, 0, &hw->subformats);
if (err < 0)
return err;
@@ -1959,7 +2700,7 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
if (runtime->dma_bytes) {
err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes);
if (err < 0)
- return -EINVAL;
+ return err;
}
if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) {
@@ -1978,7 +2719,8 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
static void pcm_release_private(struct snd_pcm_substream *substream)
{
- snd_pcm_unlink(substream);
+ if (snd_pcm_stream_linked(substream))
+ snd_pcm_unlink(substream);
}
void snd_pcm_release_substream(struct snd_pcm_substream *substream)
@@ -1989,20 +2731,19 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream)
snd_pcm_drop(substream);
if (substream->hw_opened) {
- if (substream->ops->hw_free != NULL)
- substream->ops->hw_free(substream);
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ do_hw_free(substream);
substream->ops->close(substream);
substream->hw_opened = 0;
}
- if (pm_qos_request_active(&substream->latency_pm_qos_req))
- pm_qos_remove_request(&substream->latency_pm_qos_req);
+ if (cpu_latency_qos_request_active(&substream->latency_pm_qos_req))
+ cpu_latency_qos_remove_request(&substream->latency_pm_qos_req);
if (substream->pcm_release) {
substream->pcm_release(substream);
substream->pcm_release = NULL;
}
snd_pcm_detach_substream(substream);
}
-
EXPORT_SYMBOL(snd_pcm_release_substream);
int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
@@ -2022,21 +2763,29 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
err = snd_pcm_hw_constraints_init(substream);
if (err < 0) {
- snd_printd("snd_pcm_hw_constraints_init failed\n");
+ pcm_dbg(pcm, "snd_pcm_hw_constraints_init failed\n");
goto error;
}
- if ((err = substream->ops->open(substream)) < 0)
+ err = substream->ops->open(substream);
+ if (err < 0)
goto error;
substream->hw_opened = 1;
err = snd_pcm_hw_constraints_complete(substream);
if (err < 0) {
- snd_printd("snd_pcm_hw_constraints_complete failed\n");
+ pcm_dbg(pcm, "snd_pcm_hw_constraints_complete failed\n");
goto error;
}
+ /* automatically set EXPLICIT_SYNC flag in the managed mode whenever
+ * the DMA buffer requires it
+ */
+ if (substream->managed_buffer_alloc &&
+ substream->dma_buffer.dev.need_sync)
+ substream->runtime->hw.info |= SNDRV_PCM_INFO_EXPLICIT_SYNC;
+
*rsubstream = substream;
return 0;
@@ -2044,22 +2793,16 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
snd_pcm_release_substream(substream);
return err;
}
-
EXPORT_SYMBOL(snd_pcm_open_substream);
static int snd_pcm_open_file(struct file *file,
struct snd_pcm *pcm,
- int stream,
- struct snd_pcm_file **rpcm_file)
+ int stream)
{
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream;
- struct snd_pcm_str *str;
int err;
- if (rpcm_file)
- *rpcm_file = NULL;
-
err = snd_pcm_open_substream(pcm, stream, file, &substream);
if (err < 0)
return err;
@@ -2070,14 +2813,10 @@ static int snd_pcm_open_file(struct file *file,
return -ENOMEM;
}
pcm_file->substream = substream;
- if (substream->ref_count == 1) {
- str = substream->pstr;
- substream->file = pcm_file;
+ if (substream->ref_count == 1)
substream->pcm_release = pcm_release_private;
- }
file->private_data = pcm_file;
- if (rpcm_file)
- *rpcm_file = pcm_file;
+
return 0;
}
@@ -2089,7 +2828,10 @@ static int snd_pcm_playback_open(struct inode *inode, struct file *file)
return err;
pcm = snd_lookup_minor_data(iminor(inode),
SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
- return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
+ err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
+ if (pcm)
+ snd_card_unref(pcm->card);
+ return err;
}
static int snd_pcm_capture_open(struct inode *inode, struct file *file)
@@ -2100,14 +2842,16 @@ static int snd_pcm_capture_open(struct inode *inode, struct file *file)
return err;
pcm = snd_lookup_minor_data(iminor(inode),
SNDRV_DEVICE_TYPE_PCM_CAPTURE);
- return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE);
+ err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE);
+ if (pcm)
+ snd_card_unref(pcm->card);
+ return err;
}
static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
{
int err;
- struct snd_pcm_file *pcm_file;
- wait_queue_t wait;
+ wait_queue_entry_t wait;
if (pcm == NULL) {
err = -ENODEV;
@@ -2124,7 +2868,7 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
add_wait_queue(&pcm->open_wait, &wait);
mutex_lock(&pcm->open_mutex);
while (1) {
- err = snd_pcm_open_file(file, pcm, stream, &pcm_file);
+ err = snd_pcm_open_file(file, pcm, stream);
if (err >= 0)
break;
if (err == -EAGAIN) {
@@ -2138,6 +2882,10 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
mutex_unlock(&pcm->open_mutex);
schedule();
mutex_lock(&pcm->open_mutex);
+ if (pcm->card->shutdown) {
+ err = -ENODEV;
+ break;
+ }
if (signal_pending(current)) {
err = -ERESTARTSYS;
break;
@@ -2168,319 +2916,335 @@ static int snd_pcm_release(struct inode *inode, struct file *file)
if (snd_BUG_ON(!substream))
return -ENXIO;
pcm = substream->pcm;
- mutex_lock(&pcm->open_mutex);
- snd_pcm_release_substream(substream);
- kfree(pcm_file);
- mutex_unlock(&pcm->open_mutex);
+
+ /* block until the device gets woken up as it may touch the hardware */
+ snd_power_wait(pcm->card);
+
+ scoped_guard(mutex, &pcm->open_mutex) {
+ snd_pcm_release_substream(substream);
+ kfree(pcm_file);
+ }
wake_up(&pcm->open_wait);
module_put(pcm->card->module);
snd_card_file_remove(pcm->card, file);
return 0;
}
-static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *substream,
- snd_pcm_uframes_t frames)
+/* check and update PCM state; return 0 or a negative error
+ * call this inside PCM lock
+ */
+static int do_pcm_hwsync(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_sframes_t appl_ptr;
- snd_pcm_sframes_t ret;
- snd_pcm_sframes_t hw_avail;
-
- if (frames == 0)
- return 0;
-
- snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_PREPARED:
- break;
+ switch (substream->runtime->state) {
case SNDRV_PCM_STATE_DRAINING:
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return -EBADFD;
+ fallthrough;
case SNDRV_PCM_STATE_RUNNING:
- if (snd_pcm_update_hw_ptr(substream) >= 0)
- break;
- /* Fall through */
- case SNDRV_PCM_STATE_XRUN:
- ret = -EPIPE;
- goto __end;
+ return snd_pcm_update_hw_ptr(substream);
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_PAUSED:
+ return 0;
case SNDRV_PCM_STATE_SUSPENDED:
- ret = -ESTRPIPE;
- goto __end;
+ return -ESTRPIPE;
+ case SNDRV_PCM_STATE_XRUN:
+ return -EPIPE;
default:
- ret = -EBADFD;
- goto __end;
- }
-
- hw_avail = snd_pcm_playback_hw_avail(runtime);
- if (hw_avail <= 0) {
- ret = 0;
- goto __end;
+ return -EBADFD;
}
- if (frames > (snd_pcm_uframes_t)hw_avail)
- frames = hw_avail;
- appl_ptr = runtime->control->appl_ptr - frames;
- if (appl_ptr < 0)
- appl_ptr += runtime->boundary;
- runtime->control->appl_ptr = appl_ptr;
- ret = frames;
- __end:
- snd_pcm_stream_unlock_irq(substream);
- return ret;
}
-static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substream,
- snd_pcm_uframes_t frames)
+/* increase the appl_ptr; returns the processed frames or a negative error */
+static snd_pcm_sframes_t forward_appl_ptr(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t frames,
+ snd_pcm_sframes_t avail)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_sframes_t appl_ptr;
- snd_pcm_sframes_t ret;
- snd_pcm_sframes_t hw_avail;
+ int ret;
- if (frames == 0)
+ if (avail <= 0)
return 0;
+ if (frames > (snd_pcm_uframes_t)avail)
+ frames = avail;
+ appl_ptr = runtime->control->appl_ptr + frames;
+ if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
+ appl_ptr -= runtime->boundary;
+ ret = pcm_lib_apply_appl_ptr(substream, appl_ptr);
+ return ret < 0 ? ret : frames;
+}
- snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_PREPARED:
- case SNDRV_PCM_STATE_DRAINING:
- break;
- case SNDRV_PCM_STATE_RUNNING:
- if (snd_pcm_update_hw_ptr(substream) >= 0)
- break;
- /* Fall through */
- case SNDRV_PCM_STATE_XRUN:
- ret = -EPIPE;
- goto __end;
- case SNDRV_PCM_STATE_SUSPENDED:
- ret = -ESTRPIPE;
- goto __end;
- default:
- ret = -EBADFD;
- goto __end;
- }
+/* decrease the appl_ptr; returns the processed frames or zero for error */
+static snd_pcm_sframes_t rewind_appl_ptr(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t frames,
+ snd_pcm_sframes_t avail)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_sframes_t appl_ptr;
+ int ret;
- hw_avail = snd_pcm_capture_hw_avail(runtime);
- if (hw_avail <= 0) {
- ret = 0;
- goto __end;
- }
- if (frames > (snd_pcm_uframes_t)hw_avail)
- frames = hw_avail;
+ if (avail <= 0)
+ return 0;
+ if (frames > (snd_pcm_uframes_t)avail)
+ frames = avail;
appl_ptr = runtime->control->appl_ptr - frames;
if (appl_ptr < 0)
appl_ptr += runtime->boundary;
- runtime->control->appl_ptr = appl_ptr;
- ret = frames;
- __end:
- snd_pcm_stream_unlock_irq(substream);
- return ret;
+ ret = pcm_lib_apply_appl_ptr(substream, appl_ptr);
+ /* NOTE: we return zero for errors because PulseAudio gets depressed
+ * upon receiving an error from rewind ioctl and stops processing
+ * any longer. Returning zero means that no rewind is done, so
+ * it's not absolutely wrong to answer like that.
+ */
+ return ret < 0 ? 0 : frames;
}
-static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *substream,
- snd_pcm_uframes_t frames)
+static snd_pcm_sframes_t snd_pcm_rewind(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t frames)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_sframes_t appl_ptr;
snd_pcm_sframes_t ret;
- snd_pcm_sframes_t avail;
if (frames == 0)
return 0;
- snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_PREPARED:
- case SNDRV_PCM_STATE_PAUSED:
- break;
- case SNDRV_PCM_STATE_DRAINING:
- case SNDRV_PCM_STATE_RUNNING:
- if (snd_pcm_update_hw_ptr(substream) >= 0)
- break;
- /* Fall through */
- case SNDRV_PCM_STATE_XRUN:
- ret = -EPIPE;
- goto __end;
- case SNDRV_PCM_STATE_SUSPENDED:
- ret = -ESTRPIPE;
- goto __end;
- default:
- ret = -EBADFD;
- goto __end;
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ ret = do_pcm_hwsync(substream);
+ if (!ret)
+ ret = rewind_appl_ptr(substream, frames,
+ snd_pcm_hw_avail(substream));
}
-
- avail = snd_pcm_playback_avail(runtime);
- if (avail <= 0) {
- ret = 0;
- goto __end;
- }
- if (frames > (snd_pcm_uframes_t)avail)
- frames = avail;
- appl_ptr = runtime->control->appl_ptr + frames;
- if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
- appl_ptr -= runtime->boundary;
- runtime->control->appl_ptr = appl_ptr;
- ret = frames;
- __end:
- snd_pcm_stream_unlock_irq(substream);
+ if (ret >= 0)
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
return ret;
}
-static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *substream,
- snd_pcm_uframes_t frames)
+static snd_pcm_sframes_t snd_pcm_forward(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t frames)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_sframes_t appl_ptr;
snd_pcm_sframes_t ret;
- snd_pcm_sframes_t avail;
if (frames == 0)
return 0;
- snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_PREPARED:
- case SNDRV_PCM_STATE_DRAINING:
- case SNDRV_PCM_STATE_PAUSED:
- break;
- case SNDRV_PCM_STATE_RUNNING:
- if (snd_pcm_update_hw_ptr(substream) >= 0)
- break;
- /* Fall through */
- case SNDRV_PCM_STATE_XRUN:
- ret = -EPIPE;
- goto __end;
- case SNDRV_PCM_STATE_SUSPENDED:
- ret = -ESTRPIPE;
- goto __end;
- default:
- ret = -EBADFD;
- goto __end;
- }
-
- avail = snd_pcm_capture_avail(runtime);
- if (avail <= 0) {
- ret = 0;
- goto __end;
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ ret = do_pcm_hwsync(substream);
+ if (!ret)
+ ret = forward_appl_ptr(substream, frames,
+ snd_pcm_avail(substream));
}
- if (frames > (snd_pcm_uframes_t)avail)
- frames = avail;
- appl_ptr = runtime->control->appl_ptr + frames;
- if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary)
- appl_ptr -= runtime->boundary;
- runtime->control->appl_ptr = appl_ptr;
- ret = frames;
- __end:
- snd_pcm_stream_unlock_irq(substream);
+ if (ret >= 0)
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
return ret;
}
-static int snd_pcm_hwsync(struct snd_pcm_substream *substream)
+static int snd_pcm_delay(struct snd_pcm_substream *substream,
+ snd_pcm_sframes_t *delay)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_DRAINING:
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- goto __badfd;
- case SNDRV_PCM_STATE_RUNNING:
- if ((err = snd_pcm_update_hw_ptr(substream)) < 0)
- break;
- /* Fall through */
- case SNDRV_PCM_STATE_PREPARED:
- case SNDRV_PCM_STATE_SUSPENDED:
- err = 0;
- break;
- case SNDRV_PCM_STATE_XRUN:
- err = -EPIPE;
- break;
- default:
- __badfd:
- err = -EBADFD;
- break;
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ err = do_pcm_hwsync(substream);
+ if (delay && !err)
+ *delay = snd_pcm_calc_delay(substream);
}
- snd_pcm_stream_unlock_irq(substream);
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU);
+
return err;
}
-static int snd_pcm_delay(struct snd_pcm_substream *substream,
- snd_pcm_sframes_t __user *res)
+static inline int snd_pcm_hwsync(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int err;
- snd_pcm_sframes_t n = 0;
-
- snd_pcm_stream_lock_irq(substream);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_DRAINING:
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- goto __badfd;
- case SNDRV_PCM_STATE_RUNNING:
- if ((err = snd_pcm_update_hw_ptr(substream)) < 0)
- break;
- /* Fall through */
- case SNDRV_PCM_STATE_PREPARED:
- case SNDRV_PCM_STATE_SUSPENDED:
- err = 0;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- n = snd_pcm_playback_hw_avail(runtime);
- else
- n = snd_pcm_capture_avail(runtime);
- n += runtime->delay;
- break;
- case SNDRV_PCM_STATE_XRUN:
- err = -EPIPE;
- break;
- default:
- __badfd:
- err = -EBADFD;
- break;
- }
- snd_pcm_stream_unlock_irq(substream);
- if (!err)
- if (put_user(n, res))
- err = -EFAULT;
- return err;
+ return snd_pcm_delay(substream, NULL);
}
-
+
+#define snd_pcm_sync_ptr_get_user(__f, __c, __ptr) ({ \
+ __label__ failed, failed_begin; \
+ int __err = -EFAULT; \
+ typeof(*(__ptr)) __user *__src = (__ptr); \
+ \
+ if (!user_read_access_begin(__src, sizeof(*__src))) \
+ goto failed_begin; \
+ unsafe_get_user(__f, &__src->flags, failed); \
+ unsafe_get_user(__c.appl_ptr, &__src->c.control.appl_ptr, failed); \
+ unsafe_get_user(__c.avail_min, &__src->c.control.avail_min, failed); \
+ __err = 0; \
+failed: \
+ user_read_access_end(); \
+failed_begin: \
+ __err; \
+})
+
+#define snd_pcm_sync_ptr_put_user(__s, __c, __ptr) ({ \
+ __label__ failed, failed_begin; \
+ int __err = -EFAULT; \
+ typeof(*(__ptr)) __user *__src = (__ptr); \
+ \
+ if (!user_write_access_begin(__src, sizeof(*__src))) \
+ goto failed_begin; \
+ unsafe_put_user(__s.state, &__src->s.status.state, failed); \
+ unsafe_put_user(__s.hw_ptr, &__src->s.status.hw_ptr, failed); \
+ unsafe_put_user(__s.tstamp.tv_sec, &__src->s.status.tstamp.tv_sec, failed); \
+ unsafe_put_user(__s.tstamp.tv_nsec, &__src->s.status.tstamp.tv_nsec, failed); \
+ unsafe_put_user(__s.suspended_state, &__src->s.status.suspended_state, failed); \
+ unsafe_put_user(__s.audio_tstamp.tv_sec, &__src->s.status.audio_tstamp.tv_sec, failed); \
+ unsafe_put_user(__s.audio_tstamp.tv_nsec, &__src->s.status.audio_tstamp.tv_nsec, failed);\
+ unsafe_put_user(__c.appl_ptr, &__src->c.control.appl_ptr, failed); \
+ unsafe_put_user(__c.avail_min, &__src->c.control.avail_min, failed); \
+ __err = 0; \
+failed: \
+ user_write_access_end(); \
+failed_begin: \
+ __err; \
+})
+
static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
struct snd_pcm_sync_ptr __user *_sync_ptr)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_pcm_sync_ptr sync_ptr;
volatile struct snd_pcm_mmap_status *status;
volatile struct snd_pcm_mmap_control *control;
+ u32 sflags;
+ struct snd_pcm_mmap_control scontrol;
+ struct snd_pcm_mmap_status sstatus;
int err;
- memset(&sync_ptr, 0, sizeof(sync_ptr));
- if (get_user(sync_ptr.flags, (unsigned __user *)&(_sync_ptr->flags)))
+ if (snd_pcm_sync_ptr_get_user(sflags, scontrol, _sync_ptr))
return -EFAULT;
- if (copy_from_user(&sync_ptr.c.control, &(_sync_ptr->c.control), sizeof(struct snd_pcm_mmap_control)))
- return -EFAULT;
status = runtime->status;
control = runtime->control;
- if (sync_ptr.flags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
+ if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
err = snd_pcm_hwsync(substream);
if (err < 0)
return err;
}
- snd_pcm_stream_lock_irq(substream);
- if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL))
- control->appl_ptr = sync_ptr.c.control.appl_ptr;
- else
- sync_ptr.c.control.appl_ptr = control->appl_ptr;
- if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
- control->avail_min = sync_ptr.c.control.avail_min;
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) {
+ err = pcm_lib_apply_appl_ptr(substream, scontrol.appl_ptr);
+ if (err < 0)
+ return err;
+ } else {
+ scontrol.appl_ptr = control->appl_ptr;
+ }
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
+ control->avail_min = scontrol.avail_min;
+ else
+ scontrol.avail_min = control->avail_min;
+ sstatus.state = status->state;
+ sstatus.hw_ptr = status->hw_ptr;
+ sstatus.tstamp = status->tstamp;
+ sstatus.suspended_state = status->suspended_state;
+ sstatus.audio_tstamp = status->audio_tstamp;
+ }
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
+ if (snd_pcm_sync_ptr_put_user(sstatus, scontrol, _sync_ptr))
+ return -EFAULT;
+ return 0;
+}
+
+struct snd_pcm_mmap_status32 {
+ snd_pcm_state_t state;
+ s32 pad1;
+ u32 hw_ptr;
+ struct __snd_timespec tstamp;
+ snd_pcm_state_t suspended_state;
+ struct __snd_timespec audio_tstamp;
+} __packed;
+
+struct snd_pcm_mmap_control32 {
+ u32 appl_ptr;
+ u32 avail_min;
+};
+
+struct snd_pcm_sync_ptr32 {
+ u32 flags;
+ union {
+ struct snd_pcm_mmap_status32 status;
+ unsigned char reserved[64];
+ } s;
+ union {
+ struct snd_pcm_mmap_control32 control;
+ unsigned char reserved[64];
+ } c;
+} __packed;
+
+/* recalculate the boundary within 32bit */
+static snd_pcm_uframes_t recalculate_boundary(struct snd_pcm_runtime *runtime)
+{
+ snd_pcm_uframes_t boundary;
+ snd_pcm_uframes_t border;
+ int order;
+
+ if (! runtime->buffer_size)
+ return 0;
+
+ border = 0x7fffffffUL - runtime->buffer_size;
+ if (runtime->buffer_size > border)
+ return runtime->buffer_size;
+
+ order = __fls(border) - __fls(runtime->buffer_size);
+ boundary = runtime->buffer_size << order;
+
+ if (boundary <= border)
+ return boundary;
else
- sync_ptr.c.control.avail_min = control->avail_min;
- sync_ptr.s.status.state = status->state;
- sync_ptr.s.status.hw_ptr = status->hw_ptr;
- sync_ptr.s.status.tstamp = status->tstamp;
- sync_ptr.s.status.suspended_state = status->suspended_state;
- snd_pcm_stream_unlock_irq(substream);
- if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr)))
+ return boundary / 2;
+}
+
+static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
+ struct snd_pcm_sync_ptr32 __user *src)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ volatile struct snd_pcm_mmap_status *status;
+ volatile struct snd_pcm_mmap_control *control;
+ u32 sflags;
+ struct snd_pcm_mmap_control scontrol;
+ struct snd_pcm_mmap_status sstatus;
+ snd_pcm_uframes_t boundary;
+ int err;
+
+ if (snd_BUG_ON(!runtime))
+ return -EINVAL;
+
+ if (snd_pcm_sync_ptr_get_user(sflags, scontrol, src))
+ return -EFAULT;
+ if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
+ err = snd_pcm_hwsync(substream);
+ if (err < 0)
+ return err;
+ }
+ status = runtime->status;
+ control = runtime->control;
+ boundary = recalculate_boundary(runtime);
+ if (! boundary)
+ boundary = 0x7fffffff;
+ scoped_guard(pcm_stream_lock_irq, substream) {
+ /* FIXME: we should consider the boundary for the sync from app */
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) {
+ err = pcm_lib_apply_appl_ptr(substream,
+ scontrol.appl_ptr);
+ if (err < 0)
+ return err;
+ } else
+ scontrol.appl_ptr = control->appl_ptr % boundary;
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
+ control->avail_min = scontrol.avail_min;
+ else
+ scontrol.avail_min = control->avail_min;
+ sstatus.state = status->state;
+ sstatus.hw_ptr = status->hw_ptr % boundary;
+ sstatus.tstamp = status->tstamp;
+ sstatus.suspended_state = status->suspended_state;
+ sstatus.audio_tstamp = status->audio_tstamp;
+ }
+ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
+ snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
+ if (snd_pcm_sync_ptr_put_user(sstatus, scontrol, src))
return -EFAULT;
+
return 0;
}
+#define __SNDRV_PCM_IOCTL_SYNC_PTR32 _IOWR('A', 0x23, struct snd_pcm_sync_ptr32)
static int snd_pcm_tstamp(struct snd_pcm_substream *substream, int __user *_arg)
{
@@ -2491,16 +3255,110 @@ static int snd_pcm_tstamp(struct snd_pcm_substream *substream, int __user *_arg)
return -EFAULT;
if (arg < 0 || arg > SNDRV_PCM_TSTAMP_TYPE_LAST)
return -EINVAL;
- runtime->tstamp_type = SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY;
- if (arg == SNDRV_PCM_TSTAMP_TYPE_MONOTONIC)
- runtime->tstamp_type = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
+ runtime->tstamp_type = arg;
return 0;
}
-
-static int snd_pcm_common_ioctl1(struct file *file,
+
+static int snd_pcm_xferi_frames_ioctl(struct snd_pcm_substream *substream,
+ struct snd_xferi __user *_xferi)
+{
+ struct snd_xferi xferi;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_sframes_t result;
+
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
+ if (put_user(0, &_xferi->result))
+ return -EFAULT;
+ if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
+ return -EFAULT;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
+ else
+ result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);
+ if (put_user(result, &_xferi->result))
+ return -EFAULT;
+ return result < 0 ? result : 0;
+}
+
+static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream,
+ struct snd_xfern __user *_xfern)
+{
+ struct snd_xfern xfern;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ void *bufs __free(kfree) = NULL;
+ snd_pcm_sframes_t result;
+
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
+ return -EBADFD;
+ if (runtime->channels > 128)
+ return -EINVAL;
+ if (put_user(0, &_xfern->result))
+ return -EFAULT;
+ if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
+ return -EFAULT;
+
+ bufs = memdup_array_user(xfern.bufs, runtime->channels, sizeof(void *));
+ if (IS_ERR(bufs))
+ return PTR_ERR(bufs);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ result = snd_pcm_lib_writev(substream, bufs, xfern.frames);
+ else
+ result = snd_pcm_lib_readv(substream, bufs, xfern.frames);
+ if (put_user(result, &_xfern->result))
+ return -EFAULT;
+ return result < 0 ? result : 0;
+}
+
+static int snd_pcm_rewind_ioctl(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t __user *_frames)
+{
+ snd_pcm_uframes_t frames;
+ snd_pcm_sframes_t result;
+
+ if (get_user(frames, _frames))
+ return -EFAULT;
+ if (put_user(0, _frames))
+ return -EFAULT;
+ result = snd_pcm_rewind(substream, frames);
+ if (put_user(result, _frames))
+ return -EFAULT;
+ return result < 0 ? result : 0;
+}
+
+static int snd_pcm_forward_ioctl(struct snd_pcm_substream *substream,
+ snd_pcm_uframes_t __user *_frames)
+{
+ snd_pcm_uframes_t frames;
+ snd_pcm_sframes_t result;
+
+ if (get_user(frames, _frames))
+ return -EFAULT;
+ if (put_user(0, _frames))
+ return -EFAULT;
+ result = snd_pcm_forward(substream, frames);
+ if (put_user(result, _frames))
+ return -EFAULT;
+ return result < 0 ? result : 0;
+}
+
+static int snd_pcm_common_ioctl(struct file *file,
struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg)
{
+ struct snd_pcm_file *pcm_file = file->private_data;
+ int res;
+
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return -EBADFD;
+
+ res = snd_power_wait(substream->pcm->card);
+ if (res < 0)
+ return res;
+
switch (cmd) {
case SNDRV_PCM_IOCTL_PVERSION:
return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0;
@@ -2510,6 +3368,11 @@ static int snd_pcm_common_ioctl1(struct file *file,
return 0;
case SNDRV_PCM_IOCTL_TTSTAMP:
return snd_pcm_tstamp(substream, arg);
+ case SNDRV_PCM_IOCTL_USER_PVERSION:
+ if (get_user(pcm_file->user_pversion,
+ (unsigned int __user *)arg))
+ return -EFAULT;
+ return 0;
case SNDRV_PCM_IOCTL_HW_REFINE:
return snd_pcm_hw_refine_user(substream, arg);
case SNDRV_PCM_IOCTL_HW_PARAMS:
@@ -2518,8 +3381,14 @@ static int snd_pcm_common_ioctl1(struct file *file,
return snd_pcm_hw_free(substream);
case SNDRV_PCM_IOCTL_SW_PARAMS:
return snd_pcm_sw_params_user(substream, arg);
- case SNDRV_PCM_IOCTL_STATUS:
- return snd_pcm_status_user(substream, arg);
+ case SNDRV_PCM_IOCTL_STATUS32:
+ return snd_pcm_status_user32(substream, arg, false);
+ case SNDRV_PCM_IOCTL_STATUS_EXT32:
+ return snd_pcm_status_user32(substream, arg, true);
+ case SNDRV_PCM_IOCTL_STATUS64:
+ return snd_pcm_status_user64(substream, arg, false);
+ case SNDRV_PCM_IOCTL_STATUS_EXT64:
+ return snd_pcm_status_user64(substream, arg, true);
case SNDRV_PCM_IOCTL_CHANNEL_INFO:
return snd_pcm_channel_info_user(substream, arg);
case SNDRV_PCM_IOCTL_PREPARE:
@@ -2527,7 +3396,7 @@ static int snd_pcm_common_ioctl1(struct file *file,
case SNDRV_PCM_IOCTL_RESET:
return snd_pcm_reset(substream);
case SNDRV_PCM_IOCTL_START:
- return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING);
+ return snd_pcm_start_lock_irq(substream);
case SNDRV_PCM_IOCTL_LINK:
return snd_pcm_link(substream, (int)(unsigned long) arg);
case SNDRV_PCM_IOCTL_UNLINK:
@@ -2539,8 +3408,21 @@ static int snd_pcm_common_ioctl1(struct file *file,
case SNDRV_PCM_IOCTL_HWSYNC:
return snd_pcm_hwsync(substream);
case SNDRV_PCM_IOCTL_DELAY:
- return snd_pcm_delay(substream, arg);
- case SNDRV_PCM_IOCTL_SYNC_PTR:
+ {
+ snd_pcm_sframes_t delay = 0;
+ snd_pcm_sframes_t __user *res = arg;
+ int err;
+
+ err = snd_pcm_delay(substream, &delay);
+ if (err)
+ return err;
+ if (put_user(delay, res))
+ return -EFAULT;
+ return 0;
+ }
+ case __SNDRV_PCM_IOCTL_SYNC_PTR32:
+ return snd_pcm_ioctl_sync_ptr_compat(substream, arg);
+ case __SNDRV_PCM_IOCTL_SYNC_PTR64:
return snd_pcm_sync_ptr(substream, arg);
#ifdef CONFIG_SND_SUPPORT_OLD_API
case SNDRV_PCM_IOCTL_HW_REFINE_OLD:
@@ -2553,194 +3435,24 @@ static int snd_pcm_common_ioctl1(struct file *file,
case SNDRV_PCM_IOCTL_DROP:
return snd_pcm_drop(substream);
case SNDRV_PCM_IOCTL_PAUSE:
- {
- int res;
- snd_pcm_stream_lock_irq(substream);
- res = snd_pcm_pause(substream, (int)(unsigned long)arg);
- snd_pcm_stream_unlock_irq(substream);
- return res;
- }
- }
- snd_printd("unknown ioctl = 0x%x\n", cmd);
- return -ENOTTY;
-}
-
-static int snd_pcm_playback_ioctl1(struct file *file,
- struct snd_pcm_substream *substream,
- unsigned int cmd, void __user *arg)
-{
- if (snd_BUG_ON(!substream))
- return -ENXIO;
- if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK))
- return -EINVAL;
- switch (cmd) {
+ return snd_pcm_pause_lock_irq(substream, (unsigned long)arg);
case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
- {
- struct snd_xferi xferi;
- struct snd_xferi __user *_xferi = arg;
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_sframes_t result;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
- return -EBADFD;
- if (put_user(0, &_xferi->result))
- return -EFAULT;
- if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
- return -EFAULT;
- result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
- __put_user(result, &_xferi->result);
- return result < 0 ? result : 0;
- }
- case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
- {
- struct snd_xfern xfern;
- struct snd_xfern __user *_xfern = arg;
- struct snd_pcm_runtime *runtime = substream->runtime;
- void __user **bufs;
- snd_pcm_sframes_t result;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
- return -EBADFD;
- if (runtime->channels > 128)
- return -EINVAL;
- if (put_user(0, &_xfern->result))
- return -EFAULT;
- if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
- return -EFAULT;
-
- bufs = memdup_user(xfern.bufs,
- sizeof(void *) * runtime->channels);
- if (IS_ERR(bufs))
- return PTR_ERR(bufs);
- result = snd_pcm_lib_writev(substream, bufs, xfern.frames);
- kfree(bufs);
- __put_user(result, &_xfern->result);
- return result < 0 ? result : 0;
- }
- case SNDRV_PCM_IOCTL_REWIND:
- {
- snd_pcm_uframes_t frames;
- snd_pcm_uframes_t __user *_frames = arg;
- snd_pcm_sframes_t result;
- if (get_user(frames, _frames))
- return -EFAULT;
- if (put_user(0, _frames))
- return -EFAULT;
- result = snd_pcm_playback_rewind(substream, frames);
- __put_user(result, _frames);
- return result < 0 ? result : 0;
- }
- case SNDRV_PCM_IOCTL_FORWARD:
- {
- snd_pcm_uframes_t frames;
- snd_pcm_uframes_t __user *_frames = arg;
- snd_pcm_sframes_t result;
- if (get_user(frames, _frames))
- return -EFAULT;
- if (put_user(0, _frames))
- return -EFAULT;
- result = snd_pcm_playback_forward(substream, frames);
- __put_user(result, _frames);
- return result < 0 ? result : 0;
- }
- }
- return snd_pcm_common_ioctl1(file, substream, cmd, arg);
-}
-
-static int snd_pcm_capture_ioctl1(struct file *file,
- struct snd_pcm_substream *substream,
- unsigned int cmd, void __user *arg)
-{
- if (snd_BUG_ON(!substream))
- return -ENXIO;
- if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_CAPTURE))
- return -EINVAL;
- switch (cmd) {
case SNDRV_PCM_IOCTL_READI_FRAMES:
- {
- struct snd_xferi xferi;
- struct snd_xferi __user *_xferi = arg;
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_sframes_t result;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
- return -EBADFD;
- if (put_user(0, &_xferi->result))
- return -EFAULT;
- if (copy_from_user(&xferi, _xferi, sizeof(xferi)))
- return -EFAULT;
- result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);
- __put_user(result, &_xferi->result);
- return result < 0 ? result : 0;
- }
+ return snd_pcm_xferi_frames_ioctl(substream, arg);
+ case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
case SNDRV_PCM_IOCTL_READN_FRAMES:
- {
- struct snd_xfern xfern;
- struct snd_xfern __user *_xfern = arg;
- struct snd_pcm_runtime *runtime = substream->runtime;
- void *bufs;
- snd_pcm_sframes_t result;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
- return -EBADFD;
- if (runtime->channels > 128)
- return -EINVAL;
- if (put_user(0, &_xfern->result))
- return -EFAULT;
- if (copy_from_user(&xfern, _xfern, sizeof(xfern)))
- return -EFAULT;
-
- bufs = memdup_user(xfern.bufs,
- sizeof(void *) * runtime->channels);
- if (IS_ERR(bufs))
- return PTR_ERR(bufs);
- result = snd_pcm_lib_readv(substream, bufs, xfern.frames);
- kfree(bufs);
- __put_user(result, &_xfern->result);
- return result < 0 ? result : 0;
- }
+ return snd_pcm_xfern_frames_ioctl(substream, arg);
case SNDRV_PCM_IOCTL_REWIND:
- {
- snd_pcm_uframes_t frames;
- snd_pcm_uframes_t __user *_frames = arg;
- snd_pcm_sframes_t result;
- if (get_user(frames, _frames))
- return -EFAULT;
- if (put_user(0, _frames))
- return -EFAULT;
- result = snd_pcm_capture_rewind(substream, frames);
- __put_user(result, _frames);
- return result < 0 ? result : 0;
- }
+ return snd_pcm_rewind_ioctl(substream, arg);
case SNDRV_PCM_IOCTL_FORWARD:
- {
- snd_pcm_uframes_t frames;
- snd_pcm_uframes_t __user *_frames = arg;
- snd_pcm_sframes_t result;
- if (get_user(frames, _frames))
- return -EFAULT;
- if (put_user(0, _frames))
- return -EFAULT;
- result = snd_pcm_capture_forward(substream, frames);
- __put_user(result, _frames);
- return result < 0 ? result : 0;
+ return snd_pcm_forward_ioctl(substream, arg);
}
- }
- return snd_pcm_common_ioctl1(file, substream, cmd, arg);
-}
-
-static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- struct snd_pcm_file *pcm_file;
-
- pcm_file = file->private_data;
-
- if (((cmd >> 8) & 0xff) != 'A')
- return -ENOTTY;
-
- return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd,
- (void __user *)arg);
+ pcm_dbg(substream->pcm, "unknown ioctl = 0x%x\n", cmd);
+ return -ENOTTY;
}
-static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+static long snd_pcm_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
struct snd_pcm_file *pcm_file;
@@ -2749,34 +3461,58 @@ static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd,
if (((cmd >> 8) & 0xff) != 'A')
return -ENOTTY;
- return snd_pcm_capture_ioctl1(file, pcm_file->substream, cmd,
- (void __user *)arg);
+ return snd_pcm_common_ioctl(file, pcm_file->substream, cmd,
+ (void __user *)arg);
}
+/**
+ * snd_pcm_kernel_ioctl - Execute PCM ioctl in the kernel-space
+ * @substream: PCM substream
+ * @cmd: IOCTL cmd
+ * @arg: IOCTL argument
+ *
+ * The function is provided primarily for OSS layer and USB gadget drivers,
+ * and it allows only the limited set of ioctls (hw_params, sw_params,
+ * prepare, start, drain, drop, forward).
+ *
+ * Return: zero if successful, or a negative error code
+ */
int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
- mm_segment_t fs;
- int result;
+ snd_pcm_uframes_t *frames = arg;
+ snd_pcm_sframes_t result;
- fs = snd_enter_user();
- switch (substream->stream) {
- case SNDRV_PCM_STREAM_PLAYBACK:
- result = snd_pcm_playback_ioctl1(NULL, substream, cmd,
- (void __user *)arg);
- break;
- case SNDRV_PCM_STREAM_CAPTURE:
- result = snd_pcm_capture_ioctl1(NULL, substream, cmd,
- (void __user *)arg);
- break;
+ if (substream->runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return -EBADFD;
+
+ switch (cmd) {
+ case SNDRV_PCM_IOCTL_FORWARD:
+ {
+ /* provided only for OSS; capture-only and no value returned */
+ if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
+ return -EINVAL;
+ result = snd_pcm_forward(substream, *frames);
+ return result < 0 ? result : 0;
+ }
+ case SNDRV_PCM_IOCTL_HW_PARAMS:
+ return snd_pcm_hw_params(substream, arg);
+ case SNDRV_PCM_IOCTL_SW_PARAMS:
+ return snd_pcm_sw_params(substream, arg);
+ case SNDRV_PCM_IOCTL_PREPARE:
+ return snd_pcm_prepare(substream, NULL);
+ case SNDRV_PCM_IOCTL_START:
+ return snd_pcm_start_lock_irq(substream);
+ case SNDRV_PCM_IOCTL_DRAIN:
+ return snd_pcm_drain(substream, NULL);
+ case SNDRV_PCM_IOCTL_DROP:
+ return snd_pcm_drop(substream);
+ case SNDRV_PCM_IOCTL_DELAY:
+ return snd_pcm_delay(substream, frames);
default:
- result = -EINVAL;
- break;
+ return -EINVAL;
}
- snd_leave_user(fs);
- return result;
}
-
EXPORT_SYMBOL(snd_pcm_kernel_ioctl);
static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count,
@@ -2792,7 +3528,8 @@ static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count,
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
return -EBADFD;
if (!frame_aligned(runtime, count))
return -EINVAL;
@@ -2816,7 +3553,8 @@ static ssize_t snd_pcm_write(struct file *file, const char __user *buf,
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
return -EBADFD;
if (!frame_aligned(runtime, count))
return -EINVAL;
@@ -2827,155 +3565,128 @@ static ssize_t snd_pcm_write(struct file *file, const char __user *buf,
return result;
}
-static ssize_t snd_pcm_aio_read(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t pos)
-
+static ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to)
{
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
snd_pcm_sframes_t result;
unsigned long i;
- void __user **bufs;
+ void __user **bufs __free(kfree) = NULL;
snd_pcm_uframes_t frames;
+ const struct iovec *iov = iter_iov(to);
pcm_file = iocb->ki_filp->private_data;
substream = pcm_file->substream;
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
return -EBADFD;
- if (nr_segs > 1024 || nr_segs != runtime->channels)
+ if (!user_backed_iter(to))
+ return -EINVAL;
+ if (to->nr_segs > 1024 || to->nr_segs != runtime->channels)
return -EINVAL;
if (!frame_aligned(runtime, iov->iov_len))
return -EINVAL;
frames = bytes_to_samples(runtime, iov->iov_len);
- bufs = kmalloc(sizeof(void *) * nr_segs, GFP_KERNEL);
+ bufs = kmalloc_array(to->nr_segs, sizeof(void *), GFP_KERNEL);
if (bufs == NULL)
return -ENOMEM;
- for (i = 0; i < nr_segs; ++i)
- bufs[i] = iov[i].iov_base;
+ for (i = 0; i < to->nr_segs; ++i) {
+ bufs[i] = iov->iov_base;
+ iov++;
+ }
result = snd_pcm_lib_readv(substream, bufs, frames);
if (result > 0)
result = frames_to_bytes(runtime, result);
- kfree(bufs);
return result;
}
-static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t pos)
+static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from)
{
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
snd_pcm_sframes_t result;
unsigned long i;
- void __user **bufs;
+ void __user **bufs __free(kfree) = NULL;
snd_pcm_uframes_t frames;
+ const struct iovec *iov = iter_iov(from);
pcm_file = iocb->ki_filp->private_data;
substream = pcm_file->substream;
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN ||
+ runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
return -EBADFD;
- if (nr_segs > 128 || nr_segs != runtime->channels ||
+ if (!user_backed_iter(from))
+ return -EINVAL;
+ if (from->nr_segs > 128 || from->nr_segs != runtime->channels ||
!frame_aligned(runtime, iov->iov_len))
return -EINVAL;
frames = bytes_to_samples(runtime, iov->iov_len);
- bufs = kmalloc(sizeof(void *) * nr_segs, GFP_KERNEL);
+ bufs = kmalloc_array(from->nr_segs, sizeof(void *), GFP_KERNEL);
if (bufs == NULL)
return -ENOMEM;
- for (i = 0; i < nr_segs; ++i)
- bufs[i] = iov[i].iov_base;
+ for (i = 0; i < from->nr_segs; ++i) {
+ bufs[i] = iov->iov_base;
+ iov++;
+ }
result = snd_pcm_lib_writev(substream, bufs, frames);
if (result > 0)
result = frames_to_bytes(runtime, result);
- kfree(bufs);
return result;
}
-static unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait)
+static __poll_t snd_pcm_poll(struct file *file, poll_table *wait)
{
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
- unsigned int mask;
+ __poll_t mask, ok;
snd_pcm_uframes_t avail;
pcm_file = file->private_data;
substream = pcm_file->substream;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ok = EPOLLOUT | EPOLLWRNORM;
+ else
+ ok = EPOLLIN | EPOLLRDNORM;
if (PCM_RUNTIME_CHECK(substream))
- return -ENXIO;
- runtime = substream->runtime;
-
- poll_wait(file, &runtime->sleep, wait);
-
- snd_pcm_stream_lock_irq(substream);
- avail = snd_pcm_playback_avail(runtime);
- switch (runtime->status->state) {
- case SNDRV_PCM_STATE_RUNNING:
- case SNDRV_PCM_STATE_PREPARED:
- case SNDRV_PCM_STATE_PAUSED:
- if (avail >= runtime->control->avail_min) {
- mask = POLLOUT | POLLWRNORM;
- break;
- }
- /* Fall through */
- case SNDRV_PCM_STATE_DRAINING:
- mask = 0;
- break;
- default:
- mask = POLLOUT | POLLWRNORM | POLLERR;
- break;
- }
- snd_pcm_stream_unlock_irq(substream);
- return mask;
-}
-
-static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait)
-{
- struct snd_pcm_file *pcm_file;
- struct snd_pcm_substream *substream;
- struct snd_pcm_runtime *runtime;
- unsigned int mask;
- snd_pcm_uframes_t avail;
+ return ok | EPOLLERR;
- pcm_file = file->private_data;
-
- substream = pcm_file->substream;
- if (PCM_RUNTIME_CHECK(substream))
- return -ENXIO;
runtime = substream->runtime;
+ if (runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return ok | EPOLLERR;
poll_wait(file, &runtime->sleep, wait);
- snd_pcm_stream_lock_irq(substream);
- avail = snd_pcm_capture_avail(runtime);
- switch (runtime->status->state) {
+ mask = 0;
+ guard(pcm_stream_lock_irq)(substream);
+ avail = snd_pcm_avail(substream);
+ switch (runtime->state) {
case SNDRV_PCM_STATE_RUNNING:
case SNDRV_PCM_STATE_PREPARED:
case SNDRV_PCM_STATE_PAUSED:
- if (avail >= runtime->control->avail_min) {
- mask = POLLIN | POLLRDNORM;
- break;
- }
- mask = 0;
+ if (avail >= runtime->control->avail_min)
+ mask = ok;
break;
case SNDRV_PCM_STATE_DRAINING:
- if (avail > 0) {
- mask = POLLIN | POLLRDNORM;
- break;
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mask = ok;
+ if (!avail)
+ mask |= EPOLLERR;
}
- /* Fall through */
+ break;
default:
- mask = POLLIN | POLLRDNORM | POLLERR;
+ mask = ok | EPOLLERR;
break;
}
- snd_pcm_stream_unlock_irq(substream);
return mask;
}
@@ -2991,10 +3702,9 @@ static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait)
/*
* mmap status record
*/
-static int snd_pcm_mmap_status_fault(struct vm_area_struct *area,
- struct vm_fault *vmf)
+static vm_fault_t snd_pcm_mmap_status_fault(struct vm_fault *vmf)
{
- struct snd_pcm_substream *substream = area->vm_private_data;
+ struct snd_pcm_substream *substream = vmf->vma->vm_private_data;
struct snd_pcm_runtime *runtime;
if (substream == NULL)
@@ -3013,27 +3723,26 @@ static const struct vm_operations_struct snd_pcm_vm_ops_status =
static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file,
struct vm_area_struct *area)
{
- struct snd_pcm_runtime *runtime;
long size;
if (!(area->vm_flags & VM_READ))
return -EINVAL;
- runtime = substream->runtime;
size = area->vm_end - area->vm_start;
if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)))
return -EINVAL;
area->vm_ops = &snd_pcm_vm_ops_status;
area->vm_private_data = substream;
- area->vm_flags |= VM_RESERVED;
+ vm_flags_mod(area, VM_DONTEXPAND | VM_DONTDUMP,
+ VM_WRITE | VM_MAYWRITE);
+
return 0;
}
/*
* mmap control record
*/
-static int snd_pcm_mmap_control_fault(struct vm_area_struct *area,
- struct vm_fault *vmf)
+static vm_fault_t snd_pcm_mmap_control_fault(struct vm_fault *vmf)
{
- struct snd_pcm_substream *substream = area->vm_private_data;
+ struct snd_pcm_substream *substream = vmf->vma->vm_private_data;
struct snd_pcm_runtime *runtime;
if (substream == NULL)
@@ -3052,23 +3761,59 @@ static const struct vm_operations_struct snd_pcm_vm_ops_control =
static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file *file,
struct vm_area_struct *area)
{
- struct snd_pcm_runtime *runtime;
long size;
if (!(area->vm_flags & VM_READ))
return -EINVAL;
- runtime = substream->runtime;
size = area->vm_end - area->vm_start;
if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)))
return -EINVAL;
area->vm_ops = &snd_pcm_vm_ops_control;
area->vm_private_data = substream;
- area->vm_flags |= VM_RESERVED;
+ vm_flags_set(area, VM_DONTEXPAND | VM_DONTDUMP);
return 0;
}
+
+static bool pcm_status_mmap_allowed(struct snd_pcm_file *pcm_file)
+{
+ /* If drivers require the explicit sync (typically for non-coherent
+ * pages), we have to disable the mmap of status and control data
+ * to enforce the control via SYNC_PTR ioctl.
+ */
+ if (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_EXPLICIT_SYNC)
+ return false;
+ /* See pcm_control_mmap_allowed() below.
+ * Since older alsa-lib requires both status and control mmaps to be
+ * coupled, we have to disable the status mmap for old alsa-lib, too.
+ */
+ if (pcm_file->user_pversion < SNDRV_PROTOCOL_VERSION(2, 0, 14) &&
+ (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_SYNC_APPLPTR))
+ return false;
+ return true;
+}
+
+static bool pcm_control_mmap_allowed(struct snd_pcm_file *pcm_file)
+{
+ if (pcm_file->no_compat_mmap)
+ return false;
+ /* see above */
+ if (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_EXPLICIT_SYNC)
+ return false;
+ /* Disallow the control mmap when SYNC_APPLPTR flag is set;
+ * it enforces the user-space to fall back to snd_pcm_sync_ptr(),
+ * thus it effectively assures the manual update of appl_ptr.
+ */
+ if (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_SYNC_APPLPTR)
+ return false;
+ return true;
+}
+
#else /* ! coherent mmap */
/*
* don't support mmap for status and control records.
*/
+#define pcm_status_mmap_allowed(pcm_file) false
+#define pcm_control_mmap_allowed(pcm_file) false
+
static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file,
struct vm_area_struct *area)
{
@@ -3081,34 +3826,32 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file
}
#endif /* coherent mmap */
-static inline struct page *
-snd_pcm_default_page_ops(struct snd_pcm_substream *substream, unsigned long ofs)
+/*
+ * snd_pcm_mmap_data_open - increase the mmap counter
+ */
+static void snd_pcm_mmap_data_open(struct vm_area_struct *area)
{
- void *vaddr = substream->runtime->dma_area + ofs;
-#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
- if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
- return virt_to_page(CAC_ADDR(vaddr));
-#endif
-#if defined(CONFIG_PPC32) && defined(CONFIG_NOT_COHERENT_CACHE)
- if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) {
- dma_addr_t addr = substream->runtime->dma_addr + ofs;
- addr -= get_dma_offset(substream->dma_buffer.dev.dev);
- /* assume dma_handle set via pfn_to_phys() in
- * mm/dma-noncoherent.c
- */
- return pfn_to_page(addr >> PAGE_SHIFT);
- }
-#endif
- return virt_to_page(vaddr);
+ struct snd_pcm_substream *substream = area->vm_private_data;
+
+ atomic_inc(&substream->mmap_count);
}
/*
- * fault callback for mmapping a RAM page
+ * snd_pcm_mmap_data_close - decrease the mmap counter
*/
-static int snd_pcm_mmap_data_fault(struct vm_area_struct *area,
- struct vm_fault *vmf)
+static void snd_pcm_mmap_data_close(struct vm_area_struct *area)
{
struct snd_pcm_substream *substream = area->vm_private_data;
+
+ atomic_dec(&substream->mmap_count);
+}
+
+/*
+ * fault callback for mmapping a RAM page
+ */
+static vm_fault_t snd_pcm_mmap_data_fault(struct vm_fault *vmf)
+{
+ struct snd_pcm_substream *substream = vmf->vma->vm_private_data;
struct snd_pcm_runtime *runtime;
unsigned long offset;
struct page * page;
@@ -3123,8 +3866,12 @@ static int snd_pcm_mmap_data_fault(struct vm_area_struct *area,
return VM_FAULT_SIGBUS;
if (substream->ops->page)
page = substream->ops->page(substream, offset);
- else
- page = snd_pcm_default_page_ops(substream, offset);
+ else if (!snd_pcm_get_dma_buf(substream)) {
+ if (WARN_ON_ONCE(!runtime->dma_area))
+ return VM_FAULT_SIGBUS;
+ page = virt_to_page(runtime->dma_area + offset);
+ } else
+ page = snd_sgbuf_get_page(snd_pcm_get_dma_buf(substream), offset);
if (!page)
return VM_FAULT_SIGBUS;
get_page(page);
@@ -3143,71 +3890,59 @@ static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = {
.fault = snd_pcm_mmap_data_fault,
};
-#ifndef ARCH_HAS_DMA_MMAP_COHERENT
-/* This should be defined / handled globally! */
-#ifdef CONFIG_ARM
-#define ARCH_HAS_DMA_MMAP_COHERENT
-#endif
-#endif
-
/*
* mmap the DMA buffer on RAM
*/
-static int snd_pcm_default_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *area)
+
+/**
+ * snd_pcm_lib_default_mmap - Default PCM data mmap function
+ * @substream: PCM substream
+ * @area: VMA
+ *
+ * This is the default mmap handler for PCM data. When mmap pcm_ops is NULL,
+ * this function is invoked implicitly.
+ *
+ * Return: zero if successful, or a negative error code
+ */
+int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *area)
{
- area->vm_flags |= VM_RESERVED;
-#ifdef ARCH_HAS_DMA_MMAP_COHERENT
+ vm_flags_set(area, VM_DONTEXPAND | VM_DONTDUMP);
if (!substream->ops->page &&
- substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV)
- return dma_mmap_coherent(substream->dma_buffer.dev.dev,
- area,
- substream->runtime->dma_area,
- substream->runtime->dma_addr,
- area->vm_end - area->vm_start);
-#elif defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT)
- if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV &&
- !plat_device_is_coherent(substream->dma_buffer.dev.dev))
- area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
-#endif /* ARCH_HAS_DMA_MMAP_COHERENT */
+ !snd_dma_buffer_mmap(snd_pcm_get_dma_buf(substream), area))
+ return 0;
/* mmap with fault handler */
area->vm_ops = &snd_pcm_vm_ops_data_fault;
return 0;
}
+EXPORT_SYMBOL_GPL(snd_pcm_lib_default_mmap);
/*
* mmap the DMA buffer on I/O memory area
*/
#if SNDRV_PCM_INFO_MMAP_IOMEM
+/**
+ * snd_pcm_lib_mmap_iomem - Default PCM data mmap function for I/O mem
+ * @substream: PCM substream
+ * @area: VMA
+ *
+ * When your hardware uses the iomapped pages as the hardware buffer and
+ * wants to mmap it, pass this function as mmap pcm_ops. Note that this
+ * is supposed to work only on limited architectures.
+ *
+ * Return: zero if successful, or a negative error code
+ */
int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
struct vm_area_struct *area)
{
- long size;
- unsigned long offset;
+ struct snd_pcm_runtime *runtime = substream->runtime;
area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
- area->vm_flags |= VM_IO;
- size = area->vm_end - area->vm_start;
- offset = area->vm_pgoff << PAGE_SHIFT;
- if (io_remap_pfn_range(area, area->vm_start,
- (substream->runtime->dma_addr + offset) >> PAGE_SHIFT,
- size, area->vm_page_prot))
- return -EAGAIN;
- return 0;
+ return vm_iomap_memory(area, runtime->dma_addr, runtime->dma_bytes);
}
-
EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem);
#endif /* SNDRV_PCM_INFO_MMAP */
-/* mmap callback with pgprot_noncached */
-int snd_pcm_lib_mmap_noncached(struct snd_pcm_substream *substream,
- struct vm_area_struct *area)
-{
- area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
- return snd_pcm_default_mmap(substream, area);
-}
-EXPORT_SYMBOL(snd_pcm_lib_mmap_noncached);
-
/*
* mmap DMA buffer
*/
@@ -3228,7 +3963,7 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
return -EINVAL;
}
runtime = substream->runtime;
- if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ if (runtime->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
if (!(runtime->info & SNDRV_PCM_INFO_MMAP))
return -ENXIO;
@@ -3248,12 +3983,11 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
if (substream->ops->mmap)
err = substream->ops->mmap(substream, area);
else
- err = snd_pcm_default_mmap(substream, area);
+ err = snd_pcm_lib_default_mmap(substream, area);
if (!err)
atomic_inc(&substream->mmap_count);
return err;
}
-
EXPORT_SYMBOL(snd_pcm_mmap_data);
static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
@@ -3266,15 +4000,25 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
substream = pcm_file->substream;
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
+ if (substream->runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return -EBADFD;
offset = area->vm_pgoff << PAGE_SHIFT;
switch (offset) {
- case SNDRV_PCM_MMAP_OFFSET_STATUS:
- if (pcm_file->no_compat_mmap)
+ case SNDRV_PCM_MMAP_OFFSET_STATUS_OLD:
+ if (pcm_file->no_compat_mmap || !IS_ENABLED(CONFIG_64BIT))
+ return -ENXIO;
+ fallthrough;
+ case SNDRV_PCM_MMAP_OFFSET_STATUS_NEW:
+ if (!pcm_status_mmap_allowed(pcm_file))
return -ENXIO;
return snd_pcm_mmap_status(substream, file, area);
- case SNDRV_PCM_MMAP_OFFSET_CONTROL:
- if (pcm_file->no_compat_mmap)
+ case SNDRV_PCM_MMAP_OFFSET_CONTROL_OLD:
+ if (pcm_file->no_compat_mmap || !IS_ENABLED(CONFIG_64BIT))
+ return -ENXIO;
+ fallthrough;
+ case SNDRV_PCM_MMAP_OFFSET_CONTROL_NEW:
+ if (!pcm_control_mmap_allowed(pcm_file))
return -ENXIO;
return snd_pcm_mmap_control(substream, file, area);
default:
@@ -3294,7 +4038,9 @@ static int snd_pcm_fasync(int fd, struct file * file, int on)
if (PCM_RUNTIME_CHECK(substream))
return -ENXIO;
runtime = substream->runtime;
- return fasync_helper(fd, file, on, &runtime->fasync);
+ if (runtime->state == SNDRV_PCM_STATE_DISCONNECTED)
+ return -EBADFD;
+ return snd_fasync_helper(fd, file, on, &runtime->fasync);
}
/*
@@ -3355,8 +4101,8 @@ static void snd_pcm_hw_convert_to_old_params(struct snd_pcm_hw_params_old *opara
static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params_old __user * _oparams)
{
- struct snd_pcm_hw_params *params;
- struct snd_pcm_hw_params_old *oparams = NULL;
+ struct snd_pcm_hw_params *params __free(kfree) = NULL;
+ struct snd_pcm_hw_params_old *oparams __free(kfree) = NULL;
int err;
params = kmalloc(sizeof(*params), GFP_KERNEL);
@@ -3364,29 +4110,28 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream,
return -ENOMEM;
oparams = memdup_user(_oparams, sizeof(*oparams));
- if (IS_ERR(oparams)) {
- err = PTR_ERR(oparams);
- goto out;
- }
+ if (IS_ERR(oparams))
+ return PTR_ERR(oparams);
snd_pcm_hw_convert_from_old_params(params, oparams);
err = snd_pcm_hw_refine(substream, params);
- snd_pcm_hw_convert_to_old_params(oparams, params);
- if (copy_to_user(_oparams, oparams, sizeof(*oparams))) {
- if (!err)
- err = -EFAULT;
- }
+ if (err < 0)
+ return err;
- kfree(oparams);
-out:
- kfree(params);
- return err;
+ err = fixup_unreferenced_params(substream, params);
+ if (err < 0)
+ return err;
+
+ snd_pcm_hw_convert_to_old_params(oparams, params);
+ if (copy_to_user(_oparams, oparams, sizeof(*oparams)))
+ return -EFAULT;
+ return 0;
}
static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params_old __user * _oparams)
{
- struct snd_pcm_hw_params *params;
- struct snd_pcm_hw_params_old *oparams = NULL;
+ struct snd_pcm_hw_params *params __free(kfree) = NULL;
+ struct snd_pcm_hw_params_old *oparams __free(kfree) = NULL;
int err;
params = kmalloc(sizeof(*params), GFP_KERNEL);
@@ -3394,22 +4139,18 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream,
return -ENOMEM;
oparams = memdup_user(_oparams, sizeof(*oparams));
- if (IS_ERR(oparams)) {
- err = PTR_ERR(oparams);
- goto out;
- }
+ if (IS_ERR(oparams))
+ return PTR_ERR(oparams);
+
snd_pcm_hw_convert_from_old_params(params, oparams);
err = snd_pcm_hw_params(substream, params);
- snd_pcm_hw_convert_to_old_params(oparams, params);
- if (copy_to_user(_oparams, oparams, sizeof(*oparams))) {
- if (!err)
- err = -EFAULT;
- }
+ if (err < 0)
+ return err;
- kfree(oparams);
-out:
- kfree(params);
- return err;
+ snd_pcm_hw_convert_to_old_params(oparams, params);
+ if (copy_to_user(_oparams, oparams, sizeof(*oparams)))
+ return -EFAULT;
+ return 0;
}
#endif /* CONFIG_SND_SUPPORT_OLD_API */
@@ -3426,9 +4167,9 @@ static unsigned long snd_pcm_get_unmapped_area(struct file *file,
unsigned long offset = pgoff << PAGE_SHIFT;
switch (offset) {
- case SNDRV_PCM_MMAP_OFFSET_STATUS:
+ case SNDRV_PCM_MMAP_OFFSET_STATUS_NEW:
return (unsigned long)runtime->status;
- case SNDRV_PCM_MMAP_OFFSET_CONTROL:
+ case SNDRV_PCM_MMAP_OFFSET_CONTROL_NEW:
return (unsigned long)runtime->control;
default:
return (unsigned long)runtime->dma_area + offset;
@@ -3446,12 +4187,11 @@ const struct file_operations snd_pcm_f_ops[2] = {
{
.owner = THIS_MODULE,
.write = snd_pcm_write,
- .aio_write = snd_pcm_aio_write,
+ .write_iter = snd_pcm_writev,
.open = snd_pcm_playback_open,
.release = snd_pcm_release,
- .llseek = no_llseek,
- .poll = snd_pcm_playback_poll,
- .unlocked_ioctl = snd_pcm_playback_ioctl,
+ .poll = snd_pcm_poll,
+ .unlocked_ioctl = snd_pcm_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
@@ -3460,12 +4200,11 @@ const struct file_operations snd_pcm_f_ops[2] = {
{
.owner = THIS_MODULE,
.read = snd_pcm_read,
- .aio_read = snd_pcm_aio_read,
+ .read_iter = snd_pcm_readv,
.open = snd_pcm_capture_open,
.release = snd_pcm_release,
- .llseek = no_llseek,
- .poll = snd_pcm_capture_poll,
- .unlocked_ioctl = snd_pcm_capture_ioctl,
+ .poll = snd_pcm_poll,
+ .unlocked_ioctl = snd_pcm_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
diff --git a/sound/core/pcm_param_trace.h b/sound/core/pcm_param_trace.h
new file mode 100644
index 000000000000..08abba3133ab
--- /dev/null
+++ b/sound/core/pcm_param_trace.h
@@ -0,0 +1,143 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM snd_pcm
+
+#if !defined(_PCM_PARAMS_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _PCM_PARAMS_TRACE_H
+
+#include <linux/tracepoint.h>
+
+#define HW_PARAM_ENTRY(param) {SNDRV_PCM_HW_PARAM_##param, #param}
+#define hw_param_labels \
+ HW_PARAM_ENTRY(ACCESS), \
+ HW_PARAM_ENTRY(FORMAT), \
+ HW_PARAM_ENTRY(SUBFORMAT), \
+ HW_PARAM_ENTRY(SAMPLE_BITS), \
+ HW_PARAM_ENTRY(FRAME_BITS), \
+ HW_PARAM_ENTRY(CHANNELS), \
+ HW_PARAM_ENTRY(RATE), \
+ HW_PARAM_ENTRY(PERIOD_TIME), \
+ HW_PARAM_ENTRY(PERIOD_SIZE), \
+ HW_PARAM_ENTRY(PERIOD_BYTES), \
+ HW_PARAM_ENTRY(PERIODS), \
+ HW_PARAM_ENTRY(BUFFER_TIME), \
+ HW_PARAM_ENTRY(BUFFER_SIZE), \
+ HW_PARAM_ENTRY(BUFFER_BYTES), \
+ HW_PARAM_ENTRY(TICK_TIME)
+
+TRACE_EVENT(hw_mask_param,
+ TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_hw_param_t type, int index, const struct snd_mask *prev, const struct snd_mask *curr),
+ TP_ARGS(substream, type, index, prev, curr),
+ TP_STRUCT__entry(
+ __field(int, card)
+ __field(int, device)
+ __field(int, subdevice)
+ __field(int, direction)
+ __field(snd_pcm_hw_param_t, type)
+ __field(int, index)
+ __field(int, total)
+ __array(__u32, prev_bits, 8)
+ __array(__u32, curr_bits, 8)
+ ),
+ TP_fast_assign(
+ __entry->card = substream->pcm->card->number;
+ __entry->device = substream->pcm->device;
+ __entry->subdevice = substream->number;
+ __entry->direction = substream->stream;
+ __entry->type = type;
+ __entry->index = index;
+ __entry->total = substream->runtime->hw_constraints.rules_num;
+ memcpy(__entry->prev_bits, prev->bits, sizeof(__u32) * 8);
+ memcpy(__entry->curr_bits, curr->bits, sizeof(__u32) * 8);
+ ),
+ TP_printk("pcmC%dD%d%s:%d %03d/%03d %s %08x%08x%08x%08x %08x%08x%08x%08x",
+ __entry->card,
+ __entry->device,
+ __entry->direction ? "c" : "p",
+ __entry->subdevice,
+ __entry->index,
+ __entry->total,
+ __print_symbolic(__entry->type, hw_param_labels),
+ __entry->prev_bits[3], __entry->prev_bits[2],
+ __entry->prev_bits[1], __entry->prev_bits[0],
+ __entry->curr_bits[3], __entry->curr_bits[2],
+ __entry->curr_bits[1], __entry->curr_bits[0]
+ )
+);
+
+TRACE_EVENT(hw_interval_param,
+ TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_hw_param_t type, int index, const struct snd_interval *prev, const struct snd_interval *curr),
+ TP_ARGS(substream, type, index, prev, curr),
+ TP_STRUCT__entry(
+ __field(int, card)
+ __field(int, device)
+ __field(int, subdevice)
+ __field(int, direction)
+ __field(snd_pcm_hw_param_t, type)
+ __field(int, index)
+ __field(int, total)
+ __field(unsigned int, prev_min)
+ __field(unsigned int, prev_max)
+ __field(unsigned int, prev_openmin)
+ __field(unsigned int, prev_openmax)
+ __field(unsigned int, prev_integer)
+ __field(unsigned int, prev_empty)
+ __field(unsigned int, curr_min)
+ __field(unsigned int, curr_max)
+ __field(unsigned int, curr_openmin)
+ __field(unsigned int, curr_openmax)
+ __field(unsigned int, curr_integer)
+ __field(unsigned int, curr_empty)
+ ),
+ TP_fast_assign(
+ __entry->card = substream->pcm->card->number;
+ __entry->device = substream->pcm->device;
+ __entry->subdevice = substream->number;
+ __entry->direction = substream->stream;
+ __entry->type = type;
+ __entry->index = index;
+ __entry->total = substream->runtime->hw_constraints.rules_num;
+ __entry->prev_min = prev->min;
+ __entry->prev_max = prev->max;
+ __entry->prev_openmin = prev->openmin;
+ __entry->prev_openmax = prev->openmax;
+ __entry->prev_integer = prev->integer;
+ __entry->prev_empty = prev->empty;
+ __entry->curr_min = curr->min;
+ __entry->curr_max = curr->max;
+ __entry->curr_openmin = curr->openmin;
+ __entry->curr_openmax = curr->openmax;
+ __entry->curr_integer = curr->integer;
+ __entry->curr_empty = curr->empty;
+ ),
+ TP_printk("pcmC%dD%d%s:%d %03d/%03d %s %d %d %s%u %u%s %d %d %s%u %u%s",
+ __entry->card,
+ __entry->device,
+ __entry->direction ? "c" : "p",
+ __entry->subdevice,
+ __entry->index,
+ __entry->total,
+ __print_symbolic(__entry->type, hw_param_labels),
+ __entry->prev_empty,
+ __entry->prev_integer,
+ __entry->prev_openmin ? "(" : "[",
+ __entry->prev_min,
+ __entry->prev_max,
+ __entry->prev_openmax ? ")" : "]",
+ __entry->curr_empty,
+ __entry->curr_integer,
+ __entry->curr_openmin ? "(" : "[",
+ __entry->curr_min,
+ __entry->curr_max,
+ __entry->curr_openmax ? ")" : "]"
+ )
+);
+
+#endif /* _PCM_PARAMS_TRACE_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE pcm_param_trace
+#include <trace/define_trace.h>
diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c
index b01d9481d632..ab0e5bd70f8f 100644
--- a/sound/core/pcm_timer.c
+++ b/sound/core/pcm_timer.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Digital Audio (PCM) abstract layer
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/time.h>
@@ -25,6 +10,8 @@
#include <sound/pcm.h>
#include <sound/timer.h>
+#include "pcm_local.h"
+
/*
* Timer functions
*/
@@ -33,8 +20,8 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream)
{
unsigned long rate, mult, fsize, l, post;
struct snd_pcm_runtime *runtime = substream->runtime;
-
- mult = 1000000000;
+
+ mult = 1000000000;
rate = runtime->rate;
if (snd_BUG_ON(!rate))
return;
@@ -53,7 +40,9 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream)
post *= 2;
}
if (rate == 0) {
- snd_printk(KERN_ERR "pcm timer resolution out of range (rate = %u, period_size = %lu)\n", runtime->rate, runtime->period_size);
+ pcm_err(substream->pcm,
+ "pcm timer resolution out of range (rate = %u, period_size = %lu)\n",
+ runtime->rate, runtime->period_size);
runtime->timer_resolution = -1;
return;
}
@@ -63,7 +52,7 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream)
static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer)
{
struct snd_pcm_substream *substream;
-
+
substream = timer->private_data;
return substream->runtime ? substream->runtime->timer_resolution : 0;
}
@@ -71,7 +60,7 @@ static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer)
static int snd_pcm_timer_start(struct snd_timer * timer)
{
struct snd_pcm_substream *substream;
-
+
substream = snd_timer_chip(timer);
substream->timer_running = 1;
return 0;
@@ -80,13 +69,13 @@ static int snd_pcm_timer_start(struct snd_timer * timer)
static int snd_pcm_timer_stop(struct snd_timer * timer)
{
struct snd_pcm_substream *substream;
-
+
substream = snd_timer_chip(timer);
substream->timer_running = 0;
return 0;
}
-static struct snd_timer_hardware snd_pcm_timer =
+static const struct snd_timer_hardware snd_pcm_timer =
{
.flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_SLAVE,
.resolution = 0,
@@ -110,7 +99,7 @@ void snd_pcm_timer_init(struct snd_pcm_substream *substream)
{
struct snd_timer_id tid;
struct snd_timer *timer;
-
+
tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
tid.dev_class = SNDRV_TIMER_CLASS_PCM;
tid.card = substream->pcm->card->number;
@@ -119,8 +108,7 @@ void snd_pcm_timer_init(struct snd_pcm_substream *substream)
if (snd_timer_new(substream->pcm->card, "PCM", &tid, &timer) < 0)
return;
sprintf(timer->name, "PCM %s %i-%i-%i",
- substream->stream == SNDRV_PCM_STREAM_CAPTURE ?
- "capture" : "playback",
+ snd_pcm_direction_name(substream->stream),
tid.card, tid.device, tid.subdevice);
timer->hw = snd_pcm_timer;
if (snd_device_register(timer->card, timer) < 0) {
diff --git a/sound/core/pcm_trace.h b/sound/core/pcm_trace.h
new file mode 100644
index 000000000000..adb9b1f3bbfa
--- /dev/null
+++ b/sound/core/pcm_trace.h
@@ -0,0 +1,149 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM snd_pcm
+#define TRACE_INCLUDE_FILE pcm_trace
+
+#if !defined(_PCM_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _PCM_TRACE_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(hwptr,
+ TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_uframes_t pos, bool irq),
+ TP_ARGS(substream, pos, irq),
+ TP_STRUCT__entry(
+ __field( bool, in_interrupt )
+ __field( unsigned int, card )
+ __field( unsigned int, device )
+ __field( unsigned int, number )
+ __field( unsigned int, stream )
+ __field( snd_pcm_uframes_t, pos )
+ __field( snd_pcm_uframes_t, period_size )
+ __field( snd_pcm_uframes_t, buffer_size )
+ __field( snd_pcm_uframes_t, old_hw_ptr )
+ __field( snd_pcm_uframes_t, hw_ptr_base )
+ ),
+ TP_fast_assign(
+ __entry->in_interrupt = (irq);
+ __entry->card = (substream)->pcm->card->number;
+ __entry->device = (substream)->pcm->device;
+ __entry->number = (substream)->number;
+ __entry->stream = (substream)->stream;
+ __entry->pos = (pos);
+ __entry->period_size = (substream)->runtime->period_size;
+ __entry->buffer_size = (substream)->runtime->buffer_size;
+ __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr;
+ __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base;
+ ),
+ TP_printk("pcmC%dD%d%s/sub%d: %s: pos=%lu, old=%lu, base=%lu, period=%lu, buf=%lu",
+ __entry->card, __entry->device,
+ __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? "p" : "c",
+ __entry->number,
+ __entry->in_interrupt ? "IRQ" : "POS",
+ (unsigned long)__entry->pos,
+ (unsigned long)__entry->old_hw_ptr,
+ (unsigned long)__entry->hw_ptr_base,
+ (unsigned long)__entry->period_size,
+ (unsigned long)__entry->buffer_size)
+);
+
+TRACE_EVENT(xrun,
+ TP_PROTO(struct snd_pcm_substream *substream),
+ TP_ARGS(substream),
+ TP_STRUCT__entry(
+ __field( unsigned int, card )
+ __field( unsigned int, device )
+ __field( unsigned int, number )
+ __field( unsigned int, stream )
+ __field( snd_pcm_uframes_t, period_size )
+ __field( snd_pcm_uframes_t, buffer_size )
+ __field( snd_pcm_uframes_t, old_hw_ptr )
+ __field( snd_pcm_uframes_t, hw_ptr_base )
+ ),
+ TP_fast_assign(
+ __entry->card = (substream)->pcm->card->number;
+ __entry->device = (substream)->pcm->device;
+ __entry->number = (substream)->number;
+ __entry->stream = (substream)->stream;
+ __entry->period_size = (substream)->runtime->period_size;
+ __entry->buffer_size = (substream)->runtime->buffer_size;
+ __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr;
+ __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base;
+ ),
+ TP_printk("pcmC%dD%d%s/sub%d: XRUN: old=%lu, base=%lu, period=%lu, buf=%lu",
+ __entry->card, __entry->device,
+ __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? "p" : "c",
+ __entry->number,
+ (unsigned long)__entry->old_hw_ptr,
+ (unsigned long)__entry->hw_ptr_base,
+ (unsigned long)__entry->period_size,
+ (unsigned long)__entry->buffer_size)
+);
+
+TRACE_EVENT(hw_ptr_error,
+ TP_PROTO(struct snd_pcm_substream *substream, const char *why),
+ TP_ARGS(substream, why),
+ TP_STRUCT__entry(
+ __field( unsigned int, card )
+ __field( unsigned int, device )
+ __field( unsigned int, number )
+ __field( unsigned int, stream )
+ __string( reason, why )
+ ),
+ TP_fast_assign(
+ __entry->card = (substream)->pcm->card->number;
+ __entry->device = (substream)->pcm->device;
+ __entry->number = (substream)->number;
+ __entry->stream = (substream)->stream;
+ __assign_str(reason);
+ ),
+ TP_printk("pcmC%dD%d%s/sub%d: ERROR: %s",
+ __entry->card, __entry->device,
+ __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? "p" : "c",
+ __entry->number, __get_str(reason))
+);
+
+TRACE_EVENT(applptr,
+ TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_uframes_t prev, snd_pcm_uframes_t curr),
+ TP_ARGS(substream, prev, curr),
+ TP_STRUCT__entry(
+ __field( unsigned int, card )
+ __field( unsigned int, device )
+ __field( unsigned int, number )
+ __field( unsigned int, stream )
+ __field( snd_pcm_uframes_t, prev )
+ __field( snd_pcm_uframes_t, curr )
+ __field( snd_pcm_uframes_t, avail )
+ __field( snd_pcm_uframes_t, period_size )
+ __field( snd_pcm_uframes_t, buffer_size )
+ ),
+ TP_fast_assign(
+ __entry->card = (substream)->pcm->card->number;
+ __entry->device = (substream)->pcm->device;
+ __entry->number = (substream)->number;
+ __entry->stream = (substream)->stream;
+ __entry->prev = (prev);
+ __entry->curr = (curr);
+ __entry->avail = (substream)->stream ? snd_pcm_capture_avail(substream->runtime) : snd_pcm_playback_avail(substream->runtime);
+ __entry->period_size = (substream)->runtime->period_size;
+ __entry->buffer_size = (substream)->runtime->buffer_size;
+ ),
+ TP_printk("pcmC%dD%d%s/sub%d: prev=%lu, curr=%lu, avail=%lu, period=%lu, buf=%lu",
+ __entry->card,
+ __entry->device,
+ __entry->stream ? "c" : "p",
+ __entry->number,
+ __entry->prev,
+ __entry->curr,
+ __entry->avail,
+ __entry->period_size,
+ __entry->buffer_size
+ )
+);
+
+#endif /* _PCM_TRACE_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index cbbed0db9e56..8969ee2757f1 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -1,39 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Abstract layer for MIDI v1.0 stream
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <sound/core.h>
#include <linux/major.h>
#include <linux/init.h>
-#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/mutex.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/nospec.h>
#include <sound/rawmidi.h>
#include <sound/info.h>
#include <sound/control.h>
#include <sound/minors.h>
#include <sound/initval.h>
+#include <sound/ump.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Midlevel RawMidi code for ALSA.");
@@ -48,7 +36,6 @@ module_param_array(amidi_map, int, NULL, 0444);
MODULE_PARM_DESC(amidi_map, "Raw MIDI device number assigned to 2nd OSS device.");
#endif /* CONFIG_SND_OSSEMUL */
-static int snd_rawmidi_free(struct snd_rawmidi *rawmidi);
static int snd_rawmidi_dev_free(struct snd_device *device);
static int snd_rawmidi_dev_register(struct snd_device *device);
static int snd_rawmidi_dev_disconnect(struct snd_device *device);
@@ -56,6 +43,39 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device);
static LIST_HEAD(snd_rawmidi_devices);
static DEFINE_MUTEX(register_mutex);
+#define rmidi_err(rmidi, fmt, args...) \
+ dev_err((rmidi)->dev, fmt, ##args)
+#define rmidi_warn(rmidi, fmt, args...) \
+ dev_warn((rmidi)->dev, fmt, ##args)
+#define rmidi_dbg(rmidi, fmt, args...) \
+ dev_dbg((rmidi)->dev, fmt, ##args)
+
+struct snd_rawmidi_status32 {
+ s32 stream;
+ s32 tstamp_sec; /* Timestamp */
+ s32 tstamp_nsec;
+ u32 avail; /* available bytes */
+ u32 xruns; /* count of overruns since last status (in bytes) */
+ unsigned char reserved[16]; /* reserved for future use */
+};
+
+#define SNDRV_RAWMIDI_IOCTL_STATUS32 _IOWR('W', 0x20, struct snd_rawmidi_status32)
+
+struct snd_rawmidi_status64 {
+ int stream;
+ u8 rsvd[4]; /* alignment */
+ s64 tstamp_sec; /* Timestamp */
+ s64 tstamp_nsec;
+ size_t avail; /* available bytes */
+ size_t xruns; /* count of overruns since last status (in bytes) */
+ unsigned char reserved[16]; /* reserved for future use */
+};
+
+#define SNDRV_RAWMIDI_IOCTL_STATUS64 _IOWR('W', 0x20, struct snd_rawmidi_status64)
+
+#define rawmidi_is_ump(rmidi) \
+ (IS_ENABLED(CONFIG_SND_UMP) && ((rmidi)->info_flags & SNDRV_RAWMIDI_INFO_UMP))
+
static struct snd_rawmidi *snd_rawmidi_search(struct snd_card *card, int device)
{
struct snd_rawmidi *rawmidi;
@@ -78,48 +98,73 @@ static inline unsigned short snd_rawmidi_file_flags(struct file *file)
}
}
-static inline int snd_rawmidi_ready(struct snd_rawmidi_substream *substream)
+static inline bool __snd_rawmidi_ready(struct snd_rawmidi_runtime *runtime)
{
- struct snd_rawmidi_runtime *runtime = substream->runtime;
return runtime->avail >= runtime->avail_min;
}
+static bool snd_rawmidi_ready(struct snd_rawmidi_substream *substream)
+{
+ guard(spinlock_irqsave)(&substream->lock);
+ return __snd_rawmidi_ready(substream->runtime);
+}
+
static inline int snd_rawmidi_ready_append(struct snd_rawmidi_substream *substream,
size_t count)
{
struct snd_rawmidi_runtime *runtime = substream->runtime;
+
return runtime->avail >= runtime->avail_min &&
(!substream->append || runtime->avail >= count);
}
-static void snd_rawmidi_input_event_tasklet(unsigned long data)
+static void snd_rawmidi_input_event_work(struct work_struct *work)
+{
+ struct snd_rawmidi_runtime *runtime =
+ container_of(work, struct snd_rawmidi_runtime, event_work);
+
+ if (runtime->event)
+ runtime->event(runtime->substream);
+}
+
+/* buffer refcount management: call with substream->lock held */
+static inline void snd_rawmidi_buffer_ref(struct snd_rawmidi_runtime *runtime)
+{
+ runtime->buffer_ref++;
+}
+
+static inline void snd_rawmidi_buffer_unref(struct snd_rawmidi_runtime *runtime)
{
- struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *)data;
- substream->runtime->event(substream);
+ runtime->buffer_ref--;
}
-static void snd_rawmidi_output_trigger_tasklet(unsigned long data)
+static void snd_rawmidi_buffer_ref_sync(struct snd_rawmidi_substream *substream)
{
- struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *)data;
- substream->ops->trigger(substream, 1);
+ int loop = HZ;
+
+ spin_lock_irq(&substream->lock);
+ while (substream->runtime->buffer_ref) {
+ spin_unlock_irq(&substream->lock);
+ if (!--loop) {
+ rmidi_err(substream->rmidi, "Buffer ref sync timeout\n");
+ return;
+ }
+ schedule_timeout_uninterruptible(1);
+ spin_lock_irq(&substream->lock);
+ }
+ spin_unlock_irq(&substream->lock);
}
static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)
{
struct snd_rawmidi_runtime *runtime;
- if ((runtime = kzalloc(sizeof(*runtime), GFP_KERNEL)) == NULL)
+ runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
+ if (!runtime)
return -ENOMEM;
- spin_lock_init(&runtime->lock);
+ runtime->substream = substream;
init_waitqueue_head(&runtime->sleep);
- if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT)
- tasklet_init(&runtime->tasklet,
- snd_rawmidi_input_event_tasklet,
- (unsigned long)substream);
- else
- tasklet_init(&runtime->tasklet,
- snd_rawmidi_output_trigger_tasklet,
- (unsigned long)substream);
+ INIT_WORK(&runtime->event_work, snd_rawmidi_input_event_work);
runtime->event = NULL;
runtime->buffer_size = PAGE_SIZE;
runtime->avail_min = 1;
@@ -127,35 +172,45 @@ static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream)
runtime->avail = 0;
else
runtime->avail = runtime->buffer_size;
- if ((runtime->buffer = kmalloc(runtime->buffer_size, GFP_KERNEL)) == NULL) {
+ runtime->buffer = kvzalloc(runtime->buffer_size, GFP_KERNEL);
+ if (!runtime->buffer) {
kfree(runtime);
return -ENOMEM;
}
runtime->appl_ptr = runtime->hw_ptr = 0;
substream->runtime = runtime;
+ if (rawmidi_is_ump(substream->rmidi))
+ runtime->align = 3;
return 0;
}
+/* get the current alignment (either 0 or 3) */
+static inline int get_align(struct snd_rawmidi_runtime *runtime)
+{
+ if (IS_ENABLED(CONFIG_SND_UMP))
+ return runtime->align;
+ else
+ return 0;
+}
+
+/* get the trimmed size with the current alignment */
+#define get_aligned_size(runtime, size) ((size) & ~get_align(runtime))
+
static int snd_rawmidi_runtime_free(struct snd_rawmidi_substream *substream)
{
struct snd_rawmidi_runtime *runtime = substream->runtime;
- kfree(runtime->buffer);
+ kvfree(runtime->buffer);
kfree(runtime);
substream->runtime = NULL;
return 0;
}
-static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *substream,int up)
+static inline void snd_rawmidi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
if (!substream->opened)
return;
- if (up) {
- tasklet_schedule(&substream->runtime->tasklet);
- } else {
- tasklet_kill(&substream->runtime->tasklet);
- substream->ops->trigger(substream, 0);
- }
+ substream->ops->trigger(substream, up);
}
static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, int up)
@@ -163,42 +218,64 @@ static void snd_rawmidi_input_trigger(struct snd_rawmidi_substream *substream, i
if (!substream->opened)
return;
substream->ops->trigger(substream, up);
- if (!up && substream->runtime->event)
- tasklet_kill(&substream->runtime->tasklet);
+ if (!up)
+ cancel_work_sync(&substream->runtime->event_work);
}
-int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream)
+static void __reset_runtime_ptrs(struct snd_rawmidi_runtime *runtime,
+ bool is_input)
{
- unsigned long flags;
- struct snd_rawmidi_runtime *runtime = substream->runtime;
-
- snd_rawmidi_output_trigger(substream, 0);
runtime->drain = 0;
- spin_lock_irqsave(&runtime->lock, flags);
runtime->appl_ptr = runtime->hw_ptr = 0;
- runtime->avail = runtime->buffer_size;
- spin_unlock_irqrestore(&runtime->lock, flags);
+ runtime->avail = is_input ? 0 : runtime->buffer_size;
+}
+
+static void reset_runtime_ptrs(struct snd_rawmidi_substream *substream,
+ bool is_input)
+{
+ guard(spinlock_irqsave)(&substream->lock);
+ if (substream->opened && substream->runtime)
+ __reset_runtime_ptrs(substream->runtime, is_input);
+}
+
+int snd_rawmidi_drop_output(struct snd_rawmidi_substream *substream)
+{
+ snd_rawmidi_output_trigger(substream, 0);
+ reset_runtime_ptrs(substream, false);
return 0;
}
+EXPORT_SYMBOL(snd_rawmidi_drop_output);
int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream)
{
- int err;
+ int err = 0;
long timeout;
- struct snd_rawmidi_runtime *runtime = substream->runtime;
+ struct snd_rawmidi_runtime *runtime;
+
+ scoped_guard(spinlock_irq, &substream->lock) {
+ runtime = substream->runtime;
+ if (!substream->opened || !runtime || !runtime->buffer)
+ return -EINVAL;
+ snd_rawmidi_buffer_ref(runtime);
+ runtime->drain = 1;
+ }
- err = 0;
- runtime->drain = 1;
timeout = wait_event_interruptible_timeout(runtime->sleep,
(runtime->avail >= runtime->buffer_size),
10*HZ);
- if (signal_pending(current))
- err = -ERESTARTSYS;
- if (runtime->avail < runtime->buffer_size && !timeout) {
- snd_printk(KERN_WARNING "rawmidi drain error (avail = %li, buffer_size = %li)\n", (long)runtime->avail, (long)runtime->buffer_size);
- err = -EIO;
+
+ scoped_guard(spinlock_irq, &substream->lock) {
+ if (signal_pending(current))
+ err = -ERESTARTSYS;
+ if (runtime->avail < runtime->buffer_size && !timeout) {
+ rmidi_warn(substream->rmidi,
+ "rawmidi drain error (avail = %li, buffer_size = %li)\n",
+ (long)runtime->avail, (long)runtime->buffer_size);
+ err = -EIO;
+ }
+ runtime->drain = 0;
}
- runtime->drain = 0;
+
if (err != -ERESTARTSYS) {
/* we need wait a while to make sure that Tx FIFOs are empty */
if (substream->ops->drain)
@@ -207,22 +284,21 @@ int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream)
msleep(50);
snd_rawmidi_drop_output(substream);
}
+
+ scoped_guard(spinlock_irq, &substream->lock)
+ snd_rawmidi_buffer_unref(runtime);
+
return err;
}
+EXPORT_SYMBOL(snd_rawmidi_drain_output);
int snd_rawmidi_drain_input(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
- struct snd_rawmidi_runtime *runtime = substream->runtime;
-
snd_rawmidi_input_trigger(substream, 0);
- runtime->drain = 0;
- spin_lock_irqsave(&runtime->lock, flags);
- runtime->appl_ptr = runtime->hw_ptr = 0;
- runtime->avail = 0;
- spin_unlock_irqrestore(&runtime->lock, flags);
+ reset_runtime_ptrs(substream, true);
return 0;
}
+EXPORT_SYMBOL(snd_rawmidi_drain_input);
/* look for an available substream for the given stream direction;
* if a specific subdevice is given, try to assign it
@@ -233,7 +309,7 @@ static int assign_substream(struct snd_rawmidi *rmidi, int subdevice,
{
struct snd_rawmidi_substream *substream;
struct snd_rawmidi_str *s = &rmidi->streams[stream];
- static unsigned int info_flags[2] = {
+ static const unsigned int info_flags[2] = {
[SNDRV_RAWMIDI_STREAM_OUTPUT] = SNDRV_RAWMIDI_INFO_OUTPUT,
[SNDRV_RAWMIDI_STREAM_INPUT] = SNDRV_RAWMIDI_INFO_INPUT,
};
@@ -274,6 +350,7 @@ static int open_substream(struct snd_rawmidi *rmidi,
snd_rawmidi_runtime_free(substream);
return err;
}
+ guard(spinlock_irq)(&substream->lock);
substream->opened = 1;
substream->active_sensing = 0;
if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
@@ -332,34 +409,23 @@ static int rawmidi_open_priv(struct snd_rawmidi *rmidi, int subdevice, int mode,
}
/* called from sound/core/seq/seq_midi.c */
-int snd_rawmidi_kernel_open(struct snd_card *card, int device, int subdevice,
- int mode, struct snd_rawmidi_file * rfile)
+int snd_rawmidi_kernel_open(struct snd_rawmidi *rmidi, int subdevice,
+ int mode, struct snd_rawmidi_file *rfile)
{
- struct snd_rawmidi *rmidi;
int err;
if (snd_BUG_ON(!rfile))
return -EINVAL;
-
- mutex_lock(&register_mutex);
- rmidi = snd_rawmidi_search(card, device);
- if (rmidi == NULL) {
- mutex_unlock(&register_mutex);
- return -ENODEV;
- }
- if (!try_module_get(rmidi->card->module)) {
- mutex_unlock(&register_mutex);
+ if (!try_module_get(rmidi->card->module))
return -ENXIO;
- }
- mutex_unlock(&register_mutex);
- mutex_lock(&rmidi->open_mutex);
+ guard(mutex)(&rmidi->open_mutex);
err = rawmidi_open_priv(rmidi, subdevice, mode, rfile);
- mutex_unlock(&rmidi->open_mutex);
if (err < 0)
module_put(rmidi->card->module);
return err;
}
+EXPORT_SYMBOL(snd_rawmidi_kernel_open);
static int snd_rawmidi_open(struct inode *inode, struct file *file)
{
@@ -370,13 +436,12 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
int err;
struct snd_rawmidi *rmidi;
struct snd_rawmidi_file *rawmidi_file = NULL;
- wait_queue_t wait;
- struct snd_ctl_file *kctl;
+ wait_queue_entry_t wait;
- if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK))
+ if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK))
return -EINVAL; /* invalid combination */
- err = nonseekable_open(inode, file);
+ err = stream_open(inode, file);
if (err < 0)
return err;
@@ -394,8 +459,10 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
if (rmidi == NULL)
return -ENODEV;
- if (!try_module_get(rmidi->card->module))
+ if (!try_module_get(rmidi->card->module)) {
+ snd_card_unref(rmidi->card);
return -ENXIO;
+ }
mutex_lock(&rmidi->open_mutex);
card = rmidi->card;
@@ -410,19 +477,11 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
err = -ENOMEM;
goto __error;
}
+ rawmidi_file->user_pversion = 0;
init_waitqueue_entry(&wait, current);
add_wait_queue(&rmidi->open_wait, &wait);
while (1) {
- subdevice = -1;
- read_lock(&card->ctl_files_rwlock);
- list_for_each_entry(kctl, &card->ctl_files, list) {
- if (kctl->pid == task_pid(current)) {
- subdevice = kctl->prefer_rawmidi_subdevice;
- if (subdevice != -1)
- break;
- }
- }
- read_unlock(&card->ctl_files_rwlock);
+ subdevice = snd_ctl_get_preferred_subdevice(card, SND_CTL_SUBDEV_RAWMIDI);
err = rawmidi_open_priv(rmidi, subdevice, fflags, rawmidi_file);
if (err >= 0)
break;
@@ -437,6 +496,10 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
mutex_unlock(&rmidi->open_mutex);
schedule();
mutex_lock(&rmidi->open_mutex);
+ if (rmidi->card->shutdown) {
+ err = -ENODEV;
+ break;
+ }
if (signal_pending(current)) {
err = -ERESTARTSYS;
break;
@@ -455,6 +518,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
#endif
file->private_data = rawmidi_file;
mutex_unlock(&rmidi->open_mutex);
+ snd_card_unref(rmidi->card);
return 0;
__error:
@@ -462,6 +526,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
__error_card:
mutex_unlock(&rmidi->open_mutex);
module_put(rmidi->card->module);
+ snd_card_unref(rmidi->card);
return err;
}
@@ -486,13 +551,16 @@ static void close_substream(struct snd_rawmidi *rmidi,
if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS)
snd_rawmidi_output_trigger(substream, 0);
}
+ snd_rawmidi_buffer_ref_sync(substream);
+ }
+ scoped_guard(spinlock_irq, &substream->lock) {
+ substream->opened = 0;
+ substream->append = 0;
}
substream->ops->close(substream);
if (substream->runtime->private_free)
substream->runtime->private_free(substream);
snd_rawmidi_runtime_free(substream);
- substream->opened = 0;
- substream->append = 0;
put_pid(substream->pid);
substream->pid = NULL;
rmidi->streams[substream->stream].substream_opened--;
@@ -503,7 +571,7 @@ static void rawmidi_release_priv(struct snd_rawmidi_file *rfile)
struct snd_rawmidi *rmidi;
rmidi = rfile->rmidi;
- mutex_lock(&rmidi->open_mutex);
+ guard(mutex)(&rmidi->open_mutex);
if (rfile->input) {
close_substream(rmidi, rfile->input, 1);
rfile->input = NULL;
@@ -513,7 +581,6 @@ static void rawmidi_release_priv(struct snd_rawmidi_file *rfile)
rfile->output = NULL;
}
rfile->rmidi = NULL;
- mutex_unlock(&rmidi->open_mutex);
wake_up(&rmidi->open_wait);
}
@@ -524,12 +591,13 @@ int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile)
if (snd_BUG_ON(!rfile))
return -ENXIO;
-
+
rmidi = rfile->rmidi;
rawmidi_release_priv(rfile);
module_put(rmidi->card->module);
return 0;
}
+EXPORT_SYMBOL(snd_rawmidi_kernel_release);
static int snd_rawmidi_release(struct inode *inode, struct file *file)
{
@@ -551,7 +619,7 @@ static int snd_rawmidi_info(struct snd_rawmidi_substream *substream,
struct snd_rawmidi_info *info)
{
struct snd_rawmidi *rmidi;
-
+
if (substream == NULL)
return -ENODEV;
rmidi = substream->rmidi;
@@ -561,40 +629,45 @@ static int snd_rawmidi_info(struct snd_rawmidi_substream *substream,
info->subdevice = substream->number;
info->stream = substream->stream;
info->flags = rmidi->info_flags;
- strcpy(info->id, rmidi->id);
- strcpy(info->name, rmidi->name);
- strcpy(info->subname, substream->name);
+ if (substream->inactive)
+ info->flags |= SNDRV_RAWMIDI_INFO_STREAM_INACTIVE;
+ strscpy(info->id, rmidi->id);
+ strscpy(info->name, rmidi->name);
+ strscpy(info->subname, substream->name);
info->subdevices_count = substream->pstr->substream_count;
info->subdevices_avail = (substream->pstr->substream_count -
substream->pstr->substream_opened);
+ info->tied_device = rmidi->tied_device;
return 0;
}
static int snd_rawmidi_info_user(struct snd_rawmidi_substream *substream,
- struct snd_rawmidi_info __user * _info)
+ struct snd_rawmidi_info __user *_info)
{
struct snd_rawmidi_info info;
int err;
- if ((err = snd_rawmidi_info(substream, &info)) < 0)
+
+ err = snd_rawmidi_info(substream, &info);
+ if (err < 0)
return err;
if (copy_to_user(_info, &info, sizeof(struct snd_rawmidi_info)))
return -EFAULT;
return 0;
}
-int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info)
+static int __snd_rawmidi_info_select(struct snd_card *card,
+ struct snd_rawmidi_info *info)
{
struct snd_rawmidi *rmidi;
struct snd_rawmidi_str *pstr;
struct snd_rawmidi_substream *substream;
- mutex_lock(&register_mutex);
rmidi = snd_rawmidi_search(card, info->device);
- mutex_unlock(&register_mutex);
if (!rmidi)
return -ENXIO;
if (info->stream < 0 || info->stream > 1)
return -EINVAL;
+ info->stream = array_index_nospec(info->stream, 2);
pstr = &rmidi->streams[info->stream];
if (pstr->substream_count == 0)
return -ENOENT;
@@ -607,109 +680,216 @@ int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info
return -ENXIO;
}
+int snd_rawmidi_info_select(struct snd_card *card, struct snd_rawmidi_info *info)
+{
+ guard(mutex)(&register_mutex);
+ return __snd_rawmidi_info_select(card, info);
+}
+EXPORT_SYMBOL(snd_rawmidi_info_select);
+
static int snd_rawmidi_info_select_user(struct snd_card *card,
struct snd_rawmidi_info __user *_info)
{
int err;
struct snd_rawmidi_info info;
+
if (get_user(info.device, &_info->device))
return -EFAULT;
if (get_user(info.stream, &_info->stream))
return -EFAULT;
if (get_user(info.subdevice, &_info->subdevice))
return -EFAULT;
- if ((err = snd_rawmidi_info_select(card, &info)) < 0)
+ err = snd_rawmidi_info_select(card, &info);
+ if (err < 0)
return err;
if (copy_to_user(_info, &info, sizeof(struct snd_rawmidi_info)))
return -EFAULT;
return 0;
}
-int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream,
- struct snd_rawmidi_params * params)
+static int resize_runtime_buffer(struct snd_rawmidi_substream *substream,
+ struct snd_rawmidi_params *params,
+ bool is_input)
{
- char *newbuf;
struct snd_rawmidi_runtime *runtime = substream->runtime;
-
- if (substream->append && substream->use_count > 1)
- return -EBUSY;
- snd_rawmidi_drain_output(substream);
- if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) {
+ char *newbuf, *oldbuf;
+ unsigned int framing = params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK;
+
+ if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L)
return -EINVAL;
- }
- if (params->avail_min < 1 || params->avail_min > params->buffer_size) {
+ if (framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP && (params->buffer_size & 0x1f) != 0)
+ return -EINVAL;
+ if (params->avail_min < 1 || params->avail_min > params->buffer_size)
+ return -EINVAL;
+ if (params->buffer_size & get_align(runtime))
return -EINVAL;
- }
if (params->buffer_size != runtime->buffer_size) {
- newbuf = kmalloc(params->buffer_size, GFP_KERNEL);
+ newbuf = kvzalloc(params->buffer_size, GFP_KERNEL);
if (!newbuf)
return -ENOMEM;
- kfree(runtime->buffer);
+ spin_lock_irq(&substream->lock);
+ if (runtime->buffer_ref) {
+ spin_unlock_irq(&substream->lock);
+ kvfree(newbuf);
+ return -EBUSY;
+ }
+ oldbuf = runtime->buffer;
runtime->buffer = newbuf;
runtime->buffer_size = params->buffer_size;
- runtime->avail = runtime->buffer_size;
+ __reset_runtime_ptrs(runtime, is_input);
+ spin_unlock_irq(&substream->lock);
+ kvfree(oldbuf);
}
runtime->avail_min = params->avail_min;
- substream->active_sensing = !params->no_active_sensing;
return 0;
}
+int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream,
+ struct snd_rawmidi_params *params)
+{
+ int err;
+
+ snd_rawmidi_drain_output(substream);
+ guard(mutex)(&substream->rmidi->open_mutex);
+ if (substream->append && substream->use_count > 1)
+ return -EBUSY;
+ err = resize_runtime_buffer(substream, params, false);
+ if (!err)
+ substream->active_sensing = !params->no_active_sensing;
+ return err;
+}
+EXPORT_SYMBOL(snd_rawmidi_output_params);
+
int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream,
- struct snd_rawmidi_params * params)
+ struct snd_rawmidi_params *params)
{
- char *newbuf;
- struct snd_rawmidi_runtime *runtime = substream->runtime;
+ unsigned int framing = params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK;
+ unsigned int clock_type = params->mode & SNDRV_RAWMIDI_MODE_CLOCK_MASK;
+ int err;
snd_rawmidi_drain_input(substream);
- if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) {
- return -EINVAL;
- }
- if (params->avail_min < 1 || params->avail_min > params->buffer_size) {
- return -EINVAL;
- }
- if (params->buffer_size != runtime->buffer_size) {
- newbuf = kmalloc(params->buffer_size, GFP_KERNEL);
- if (!newbuf)
- return -ENOMEM;
- kfree(runtime->buffer);
- runtime->buffer = newbuf;
- runtime->buffer_size = params->buffer_size;
+ guard(mutex)(&substream->rmidi->open_mutex);
+ if (framing == SNDRV_RAWMIDI_MODE_FRAMING_NONE && clock_type != SNDRV_RAWMIDI_MODE_CLOCK_NONE)
+ err = -EINVAL;
+ else if (clock_type > SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW)
+ err = -EINVAL;
+ else if (framing > SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP)
+ err = -EINVAL;
+ else
+ err = resize_runtime_buffer(substream, params, true);
+
+ if (!err) {
+ substream->framing = framing;
+ substream->clock_type = clock_type;
}
- runtime->avail_min = params->avail_min;
return 0;
}
+EXPORT_SYMBOL(snd_rawmidi_input_params);
static int snd_rawmidi_output_status(struct snd_rawmidi_substream *substream,
- struct snd_rawmidi_status * status)
+ struct snd_rawmidi_status64 *status)
{
struct snd_rawmidi_runtime *runtime = substream->runtime;
memset(status, 0, sizeof(*status));
status->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
- spin_lock_irq(&runtime->lock);
+ guard(spinlock_irq)(&substream->lock);
status->avail = runtime->avail;
- spin_unlock_irq(&runtime->lock);
return 0;
}
static int snd_rawmidi_input_status(struct snd_rawmidi_substream *substream,
- struct snd_rawmidi_status * status)
+ struct snd_rawmidi_status64 *status)
{
struct snd_rawmidi_runtime *runtime = substream->runtime;
memset(status, 0, sizeof(*status));
status->stream = SNDRV_RAWMIDI_STREAM_INPUT;
- spin_lock_irq(&runtime->lock);
+ guard(spinlock_irq)(&substream->lock);
status->avail = runtime->avail;
status->xruns = runtime->xruns;
runtime->xruns = 0;
- spin_unlock_irq(&runtime->lock);
+ return 0;
+}
+
+static int snd_rawmidi_ioctl_status32(struct snd_rawmidi_file *rfile,
+ struct snd_rawmidi_status32 __user *argp)
+{
+ int err = 0;
+ struct snd_rawmidi_status32 __user *status = argp;
+ struct snd_rawmidi_status32 status32;
+ struct snd_rawmidi_status64 status64;
+
+ if (copy_from_user(&status32, argp,
+ sizeof(struct snd_rawmidi_status32)))
+ return -EFAULT;
+
+ switch (status32.stream) {
+ case SNDRV_RAWMIDI_STREAM_OUTPUT:
+ if (rfile->output == NULL)
+ return -EINVAL;
+ err = snd_rawmidi_output_status(rfile->output, &status64);
+ break;
+ case SNDRV_RAWMIDI_STREAM_INPUT:
+ if (rfile->input == NULL)
+ return -EINVAL;
+ err = snd_rawmidi_input_status(rfile->input, &status64);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (err < 0)
+ return err;
+
+ status32 = (struct snd_rawmidi_status32) {
+ .stream = status64.stream,
+ .tstamp_sec = status64.tstamp_sec,
+ .tstamp_nsec = status64.tstamp_nsec,
+ .avail = status64.avail,
+ .xruns = status64.xruns,
+ };
+
+ if (copy_to_user(status, &status32, sizeof(*status)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int snd_rawmidi_ioctl_status64(struct snd_rawmidi_file *rfile,
+ struct snd_rawmidi_status64 __user *argp)
+{
+ int err = 0;
+ struct snd_rawmidi_status64 status;
+
+ if (copy_from_user(&status, argp, sizeof(struct snd_rawmidi_status64)))
+ return -EFAULT;
+
+ switch (status.stream) {
+ case SNDRV_RAWMIDI_STREAM_OUTPUT:
+ if (rfile->output == NULL)
+ return -EINVAL;
+ err = snd_rawmidi_output_status(rfile->output, &status);
+ break;
+ case SNDRV_RAWMIDI_STREAM_INPUT:
+ if (rfile->input == NULL)
+ return -EINVAL;
+ err = snd_rawmidi_input_status(rfile->input, &status);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (err < 0)
+ return err;
+ if (copy_to_user(argp, &status,
+ sizeof(struct snd_rawmidi_status64)))
+ return -EFAULT;
return 0;
}
static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct snd_rawmidi_file *rfile;
+ struct snd_rawmidi *rmidi;
void __user *argp = (void __user *)arg;
rfile = file->private_data;
@@ -722,6 +902,7 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long
{
int stream;
struct snd_rawmidi_info __user *info = argp;
+
if (get_user(stream, &info->stream))
return -EFAULT;
switch (stream) {
@@ -733,11 +914,21 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long
return -EINVAL;
}
}
+ case SNDRV_RAWMIDI_IOCTL_USER_PVERSION:
+ if (get_user(rfile->user_pversion, (unsigned int __user *)arg))
+ return -EFAULT;
+ return 0;
+
case SNDRV_RAWMIDI_IOCTL_PARAMS:
{
struct snd_rawmidi_params params;
+
if (copy_from_user(&params, argp, sizeof(struct snd_rawmidi_params)))
return -EFAULT;
+ if (rfile->user_pversion < SNDRV_PROTOCOL_VERSION(2, 0, 2)) {
+ params.mode = 0;
+ memset(params.reserved, 0, sizeof(params.reserved));
+ }
switch (params.stream) {
case SNDRV_RAWMIDI_STREAM_OUTPUT:
if (rfile->output == NULL)
@@ -751,35 +942,14 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long
return -EINVAL;
}
}
- case SNDRV_RAWMIDI_IOCTL_STATUS:
- {
- int err = 0;
- struct snd_rawmidi_status status;
- if (copy_from_user(&status, argp, sizeof(struct snd_rawmidi_status)))
- return -EFAULT;
- switch (status.stream) {
- case SNDRV_RAWMIDI_STREAM_OUTPUT:
- if (rfile->output == NULL)
- return -EINVAL;
- err = snd_rawmidi_output_status(rfile->output, &status);
- break;
- case SNDRV_RAWMIDI_STREAM_INPUT:
- if (rfile->input == NULL)
- return -EINVAL;
- err = snd_rawmidi_input_status(rfile->input, &status);
- break;
- default:
- return -EINVAL;
- }
- if (err < 0)
- return err;
- if (copy_to_user(argp, &status, sizeof(struct snd_rawmidi_status)))
- return -EFAULT;
- return 0;
- }
+ case SNDRV_RAWMIDI_IOCTL_STATUS32:
+ return snd_rawmidi_ioctl_status32(rfile, argp);
+ case SNDRV_RAWMIDI_IOCTL_STATUS64:
+ return snd_rawmidi_ioctl_status64(rfile, argp);
case SNDRV_RAWMIDI_IOCTL_DROP:
{
int val;
+
if (get_user(val, (int __user *) argp))
return -EFAULT;
switch (val) {
@@ -794,6 +964,7 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long
case SNDRV_RAWMIDI_IOCTL_DRAIN:
{
int val;
+
if (get_user(val, (int __user *) argp))
return -EFAULT;
switch (val) {
@@ -809,14 +980,66 @@ static long snd_rawmidi_ioctl(struct file *file, unsigned int cmd, unsigned long
return -EINVAL;
}
}
-#ifdef CONFIG_SND_DEBUG
default:
- snd_printk(KERN_WARNING "rawmidi: unknown command = 0x%x\n", cmd);
-#endif
+ rmidi = rfile->rmidi;
+ if (rmidi->ops && rmidi->ops->ioctl)
+ return rmidi->ops->ioctl(rmidi, cmd, argp);
+ rmidi_dbg(rmidi, "rawmidi: unknown command = 0x%x\n", cmd);
}
return -ENOTTY;
}
+/* ioctl to find the next device; either legacy or UMP depending on @find_ump */
+static int snd_rawmidi_next_device(struct snd_card *card, int __user *argp,
+ bool find_ump)
+
+{
+ struct snd_rawmidi *rmidi;
+ int device;
+ bool is_ump;
+
+ if (get_user(device, argp))
+ return -EFAULT;
+ if (device >= SNDRV_RAWMIDI_DEVICES) /* next device is -1 */
+ device = SNDRV_RAWMIDI_DEVICES - 1;
+ scoped_guard(mutex, &register_mutex) {
+ device = device < 0 ? 0 : device + 1;
+ for (; device < SNDRV_RAWMIDI_DEVICES; device++) {
+ rmidi = snd_rawmidi_search(card, device);
+ if (!rmidi)
+ continue;
+ is_ump = rawmidi_is_ump(rmidi);
+ if (find_ump == is_ump)
+ break;
+ }
+ if (device == SNDRV_RAWMIDI_DEVICES)
+ device = -1;
+ }
+ if (put_user(device, argp))
+ return -EFAULT;
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_SND_UMP)
+/* inquiry of UMP endpoint and block info via control API */
+static int snd_rawmidi_call_ump_ioctl(struct snd_card *card, int cmd,
+ void __user *argp)
+{
+ struct snd_ump_endpoint_info __user *info = argp;
+ struct snd_rawmidi *rmidi;
+ int device;
+
+ if (get_user(device, &info->device))
+ return -EFAULT;
+ guard(mutex)(&register_mutex);
+ rmidi = snd_rawmidi_search(card, device);
+ if (rmidi && rmidi->ops && rmidi->ops->ioctl)
+ return rmidi->ops->ioctl(rmidi, cmd, argp);
+ else
+ return -ENXIO;
+}
+#endif
+
static int snd_rawmidi_control_ioctl(struct snd_card *card,
struct snd_ctl_file *control,
unsigned int cmd,
@@ -826,34 +1049,22 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card,
switch (cmd) {
case SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE:
- {
- int device;
-
- if (get_user(device, (int __user *)argp))
- return -EFAULT;
- if (device >= SNDRV_RAWMIDI_DEVICES) /* next device is -1 */
- device = SNDRV_RAWMIDI_DEVICES - 1;
- mutex_lock(&register_mutex);
- device = device < 0 ? 0 : device + 1;
- while (device < SNDRV_RAWMIDI_DEVICES) {
- if (snd_rawmidi_search(card, device))
- break;
- device++;
- }
- if (device == SNDRV_RAWMIDI_DEVICES)
- device = -1;
- mutex_unlock(&register_mutex);
- if (put_user(device, (int __user *)argp))
- return -EFAULT;
- return 0;
- }
+ return snd_rawmidi_next_device(card, argp, false);
+#if IS_ENABLED(CONFIG_SND_UMP)
+ case SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE:
+ return snd_rawmidi_next_device(card, argp, true);
+ case SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO:
+ return snd_rawmidi_call_ump_ioctl(card, SNDRV_UMP_IOCTL_ENDPOINT_INFO, argp);
+ case SNDRV_CTL_IOCTL_UMP_BLOCK_INFO:
+ return snd_rawmidi_call_ump_ioctl(card, SNDRV_UMP_IOCTL_BLOCK_INFO, argp);
+#endif
case SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE:
{
int val;
-
+
if (get_user(val, (int __user *)argp))
return -EFAULT;
- control->prefer_rawmidi_subdevice = val;
+ control->preferred_subdevice[SND_CTL_SUBDEV_RAWMIDI] = val;
return 0;
}
case SNDRV_CTL_IOCTL_RAWMIDI_INFO:
@@ -862,6 +1073,63 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card,
return -ENOIOCTLCMD;
}
+static int receive_with_tstamp_framing(struct snd_rawmidi_substream *substream,
+ const unsigned char *buffer, int src_count, const struct timespec64 *tstamp)
+{
+ struct snd_rawmidi_runtime *runtime = substream->runtime;
+ struct snd_rawmidi_framing_tstamp *dest_ptr;
+ struct snd_rawmidi_framing_tstamp frame = { .tv_sec = tstamp->tv_sec, .tv_nsec = tstamp->tv_nsec };
+ int orig_count = src_count;
+ int frame_size = sizeof(struct snd_rawmidi_framing_tstamp);
+ int align = get_align(runtime);
+
+ BUILD_BUG_ON(frame_size != 0x20);
+ if (snd_BUG_ON((runtime->hw_ptr & 0x1f) != 0))
+ return -EINVAL;
+
+ while (src_count > align) {
+ if ((int)(runtime->buffer_size - runtime->avail) < frame_size) {
+ runtime->xruns += src_count;
+ break;
+ }
+ if (src_count >= SNDRV_RAWMIDI_FRAMING_DATA_LENGTH)
+ frame.length = SNDRV_RAWMIDI_FRAMING_DATA_LENGTH;
+ else {
+ frame.length = get_aligned_size(runtime, src_count);
+ if (!frame.length)
+ break;
+ memset(frame.data, 0, SNDRV_RAWMIDI_FRAMING_DATA_LENGTH);
+ }
+ memcpy(frame.data, buffer, frame.length);
+ buffer += frame.length;
+ src_count -= frame.length;
+ dest_ptr = (struct snd_rawmidi_framing_tstamp *) (runtime->buffer + runtime->hw_ptr);
+ *dest_ptr = frame;
+ runtime->avail += frame_size;
+ runtime->hw_ptr += frame_size;
+ runtime->hw_ptr %= runtime->buffer_size;
+ }
+ return orig_count - src_count;
+}
+
+static struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substream)
+{
+ struct timespec64 ts64 = {0, 0};
+
+ switch (substream->clock_type) {
+ case SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW:
+ ktime_get_raw_ts64(&ts64);
+ break;
+ case SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC:
+ ktime_get_ts64(&ts64);
+ break;
+ case SNDRV_RAWMIDI_MODE_CLOCK_REALTIME:
+ ktime_get_real_ts64(&ts64);
+ break;
+ }
+ return ts64;
+}
+
/**
* snd_rawmidi_receive - receive the input data from the device
* @substream: the rawmidi substream
@@ -870,23 +1138,32 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card,
*
* Reads the data from the internal buffer.
*
- * Returns the size of read data, or a negative error code on failure.
+ * Return: The size of read data, or a negative error code on failure.
*/
int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
const unsigned char *buffer, int count)
{
- unsigned long flags;
+ struct timespec64 ts64 = get_framing_tstamp(substream);
int result = 0, count1;
- struct snd_rawmidi_runtime *runtime = substream->runtime;
+ struct snd_rawmidi_runtime *runtime;
+ guard(spinlock_irqsave)(&substream->lock);
if (!substream->opened)
return -EBADFD;
- if (runtime->buffer == NULL) {
- snd_printd("snd_rawmidi_receive: input is not active!!!\n");
+ runtime = substream->runtime;
+ if (!runtime || !runtime->buffer) {
+ rmidi_dbg(substream->rmidi,
+ "snd_rawmidi_receive: input is not active!!!\n");
return -EINVAL;
}
- spin_lock_irqsave(&runtime->lock, flags);
- if (count == 1) { /* special case, faster code */
+
+ count = get_aligned_size(runtime, count);
+ if (!count)
+ return result;
+
+ if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) {
+ result = receive_with_tstamp_framing(substream, buffer, count, &ts64);
+ } else if (count == 1) { /* special case, faster code */
substream->bytes++;
if (runtime->avail < runtime->buffer_size) {
runtime->buffer[runtime->hw_ptr++] = buffer[0];
@@ -903,6 +1180,9 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
count1 = count;
if (count1 > (int)(runtime->buffer_size - runtime->avail))
count1 = runtime->buffer_size - runtime->avail;
+ count1 = get_aligned_size(runtime, count1);
+ if (!count1)
+ return result;
memcpy(runtime->buffer + runtime->hw_ptr, buffer, count1);
runtime->hw_ptr += count1;
runtime->hw_ptr %= runtime->buffer_size;
@@ -926,13 +1206,13 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
}
if (result > 0) {
if (runtime->event)
- tasklet_schedule(&runtime->tasklet);
- else if (snd_rawmidi_ready(substream))
+ schedule_work(&runtime->event_work);
+ else if (__snd_rawmidi_ready(runtime))
wake_up(&runtime->sleep);
}
- spin_unlock_irqrestore(&runtime->lock, flags);
return result;
}
+EXPORT_SYMBOL(snd_rawmidi_receive);
static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
unsigned char __user *userbuf,
@@ -941,32 +1221,42 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
unsigned long flags;
long result = 0, count1;
struct snd_rawmidi_runtime *runtime = substream->runtime;
+ unsigned long appl_ptr;
+ int err = 0;
+ spin_lock_irqsave(&substream->lock, flags);
+ snd_rawmidi_buffer_ref(runtime);
while (count > 0 && runtime->avail) {
count1 = runtime->buffer_size - runtime->appl_ptr;
if (count1 > count)
count1 = count;
- spin_lock_irqsave(&runtime->lock, flags);
if (count1 > (int)runtime->avail)
count1 = runtime->avail;
+
+ /* update runtime->appl_ptr before unlocking for userbuf */
+ appl_ptr = runtime->appl_ptr;
+ runtime->appl_ptr += count1;
+ runtime->appl_ptr %= runtime->buffer_size;
+ runtime->avail -= count1;
+
if (kernelbuf)
- memcpy(kernelbuf + result, runtime->buffer + runtime->appl_ptr, count1);
+ memcpy(kernelbuf + result, runtime->buffer + appl_ptr, count1);
if (userbuf) {
- spin_unlock_irqrestore(&runtime->lock, flags);
+ spin_unlock_irqrestore(&substream->lock, flags);
if (copy_to_user(userbuf + result,
- runtime->buffer + runtime->appl_ptr, count1)) {
- return result > 0 ? result : -EFAULT;
- }
- spin_lock_irqsave(&runtime->lock, flags);
+ runtime->buffer + appl_ptr, count1))
+ err = -EFAULT;
+ spin_lock_irqsave(&substream->lock, flags);
+ if (err)
+ goto out;
}
- runtime->appl_ptr += count1;
- runtime->appl_ptr %= runtime->buffer_size;
- runtime->avail -= count1;
- spin_unlock_irqrestore(&runtime->lock, flags);
result += count1;
count -= count1;
}
- return result;
+ out:
+ snd_rawmidi_buffer_unref(runtime);
+ spin_unlock_irqrestore(&substream->lock, flags);
+ return result > 0 ? result : err;
}
long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream,
@@ -975,6 +1265,7 @@ long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream,
snd_rawmidi_input_trigger(substream, 1);
return snd_rawmidi_kernel_read1(substream, NULL/*userbuf*/, buf, count);
}
+EXPORT_SYMBOL(snd_rawmidi_kernel_read);
static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t count,
loff_t *offset)
@@ -993,26 +1284,31 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun
snd_rawmidi_input_trigger(substream, 1);
result = 0;
while (count > 0) {
- spin_lock_irq(&runtime->lock);
- while (!snd_rawmidi_ready(substream)) {
- wait_queue_t wait;
+ spin_lock_irq(&substream->lock);
+ while (!__snd_rawmidi_ready(runtime)) {
+ wait_queue_entry_t wait;
+
if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
return result > 0 ? result : -EAGAIN;
}
init_waitqueue_entry(&wait, current);
add_wait_queue(&runtime->sleep, &wait);
set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
schedule();
remove_wait_queue(&runtime->sleep, &wait);
+ if (rfile->rmidi->card->shutdown)
+ return -ENODEV;
if (signal_pending(current))
return result > 0 ? result : -ERESTARTSYS;
- if (!runtime->avail)
+ spin_lock_irq(&substream->lock);
+ if (!runtime->avail) {
+ spin_unlock_irq(&substream->lock);
return result > 0 ? result : -EIO;
- spin_lock_irq(&runtime->lock);
+ }
}
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
count1 = snd_rawmidi_kernel_read1(substream,
(unsigned char __user *)buf,
NULL/*kernelbuf*/,
@@ -1029,52 +1325,44 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun
/**
* snd_rawmidi_transmit_empty - check whether the output buffer is empty
* @substream: the rawmidi substream
- *
- * Returns 1 if the internal output buffer is empty, 0 if not.
+ *
+ * Return: 1 if the internal output buffer is empty, 0 if not.
*/
int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream)
{
- struct snd_rawmidi_runtime *runtime = substream->runtime;
- int result;
- unsigned long flags;
+ struct snd_rawmidi_runtime *runtime;
- if (runtime->buffer == NULL) {
- snd_printd("snd_rawmidi_transmit_empty: output is not active!!!\n");
+ guard(spinlock_irqsave)(&substream->lock);
+ runtime = substream->runtime;
+ if (!substream->opened || !runtime || !runtime->buffer) {
+ rmidi_dbg(substream->rmidi,
+ "snd_rawmidi_transmit_empty: output is not active!!!\n");
return 1;
}
- spin_lock_irqsave(&runtime->lock, flags);
- result = runtime->avail >= runtime->buffer_size;
- spin_unlock_irqrestore(&runtime->lock, flags);
- return result;
+ return (runtime->avail >= runtime->buffer_size);
}
+EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
-/**
- * snd_rawmidi_transmit_peek - copy data from the internal buffer
+/*
+ * __snd_rawmidi_transmit_peek - copy data from the internal buffer
* @substream: the rawmidi substream
* @buffer: the buffer pointer
* @count: data size to transfer
*
- * Copies data from the internal output buffer to the given buffer.
- *
- * Call this in the interrupt handler when the midi output is ready,
- * and call snd_rawmidi_transmit_ack() after the transmission is
- * finished.
- *
- * Returns the size of copied data, or a negative error code on failure.
+ * This is a variant of snd_rawmidi_transmit_peek() without spinlock.
*/
-int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
- unsigned char *buffer, int count)
+static int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
+ unsigned char *buffer, int count)
{
- unsigned long flags;
int result, count1;
struct snd_rawmidi_runtime *runtime = substream->runtime;
if (runtime->buffer == NULL) {
- snd_printd("snd_rawmidi_transmit_peek: output is not active!!!\n");
+ rmidi_dbg(substream->rmidi,
+ "snd_rawmidi_transmit_peek: output is not active!!!\n");
return -EINVAL;
}
result = 0;
- spin_lock_irqsave(&runtime->lock, flags);
if (runtime->avail >= runtime->buffer_size) {
/* warning: lowlevel layer MUST trigger down the hardware */
goto __skip;
@@ -1088,75 +1376,144 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
count1 = count;
if (count1 > (int)(runtime->buffer_size - runtime->avail))
count1 = runtime->buffer_size - runtime->avail;
+ count1 = get_aligned_size(runtime, count1);
+ if (!count1)
+ goto __skip;
memcpy(buffer, runtime->buffer + runtime->hw_ptr, count1);
count -= count1;
result += count1;
if (count > 0) {
if (count > (int)(runtime->buffer_size - runtime->avail - count1))
count = runtime->buffer_size - runtime->avail - count1;
+ count = get_aligned_size(runtime, count);
+ if (!count)
+ goto __skip;
memcpy(buffer + count1, runtime->buffer, count);
result += count;
}
}
__skip:
- spin_unlock_irqrestore(&runtime->lock, flags);
return result;
}
/**
- * snd_rawmidi_transmit_ack - acknowledge the transmission
+ * snd_rawmidi_transmit_peek - copy data from the internal buffer
* @substream: the rawmidi substream
- * @count: the tranferred count
+ * @buffer: the buffer pointer
+ * @count: data size to transfer
*
- * Advances the hardware pointer for the internal output buffer with
- * the given size and updates the condition.
- * Call after the transmission is finished.
+ * Copies data from the internal output buffer to the given buffer.
+ *
+ * Call this in the interrupt handler when the midi output is ready,
+ * and call snd_rawmidi_transmit_ack() after the transmission is
+ * finished.
*
- * Returns the advanced size if successful, or a negative error code on failure.
+ * Return: The size of copied data, or a negative error code on failure.
*/
-int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
+int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
+ unsigned char *buffer, int count)
+{
+ guard(spinlock_irqsave)(&substream->lock);
+ if (!substream->opened || !substream->runtime)
+ return -EBADFD;
+ return __snd_rawmidi_transmit_peek(substream, buffer, count);
+}
+EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
+
+/*
+ * __snd_rawmidi_transmit_ack - acknowledge the transmission
+ * @substream: the rawmidi substream
+ * @count: the transferred count
+ *
+ * This is a variant of __snd_rawmidi_transmit_ack() without spinlock.
+ */
+static int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream,
+ int count)
{
- unsigned long flags;
struct snd_rawmidi_runtime *runtime = substream->runtime;
if (runtime->buffer == NULL) {
- snd_printd("snd_rawmidi_transmit_ack: output is not active!!!\n");
+ rmidi_dbg(substream->rmidi,
+ "snd_rawmidi_transmit_ack: output is not active!!!\n");
return -EINVAL;
}
- spin_lock_irqsave(&runtime->lock, flags);
snd_BUG_ON(runtime->avail + count > runtime->buffer_size);
+ count = get_aligned_size(runtime, count);
runtime->hw_ptr += count;
runtime->hw_ptr %= runtime->buffer_size;
runtime->avail += count;
substream->bytes += count;
if (count > 0) {
- if (runtime->drain || snd_rawmidi_ready(substream))
+ if (runtime->drain || __snd_rawmidi_ready(runtime))
wake_up(&runtime->sleep);
}
- spin_unlock_irqrestore(&runtime->lock, flags);
return count;
}
/**
+ * snd_rawmidi_transmit_ack - acknowledge the transmission
+ * @substream: the rawmidi substream
+ * @count: the transferred count
+ *
+ * Advances the hardware pointer for the internal output buffer with
+ * the given size and updates the condition.
+ * Call after the transmission is finished.
+ *
+ * Return: The advanced size if successful, or a negative error code on failure.
+ */
+int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
+{
+ guard(spinlock_irqsave)(&substream->lock);
+ if (!substream->opened || !substream->runtime)
+ return -EBADFD;
+ return __snd_rawmidi_transmit_ack(substream, count);
+}
+EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
+
+/**
* snd_rawmidi_transmit - copy from the buffer to the device
* @substream: the rawmidi substream
* @buffer: the buffer pointer
* @count: the data size to transfer
- *
+ *
* Copies data from the buffer to the device and advances the pointer.
*
- * Returns the copied size if successful, or a negative error code on failure.
+ * Return: The copied size if successful, or a negative error code on failure.
*/
int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
unsigned char *buffer, int count)
{
+ guard(spinlock_irqsave)(&substream->lock);
if (!substream->opened)
return -EBADFD;
- count = snd_rawmidi_transmit_peek(substream, buffer, count);
- if (count < 0)
+ count = __snd_rawmidi_transmit_peek(substream, buffer, count);
+ if (count <= 0)
return count;
- return snd_rawmidi_transmit_ack(substream, count);
+ return __snd_rawmidi_transmit_ack(substream, count);
+}
+EXPORT_SYMBOL(snd_rawmidi_transmit);
+
+/**
+ * snd_rawmidi_proceed - Discard the all pending bytes and proceed
+ * @substream: rawmidi substream
+ *
+ * Return: the number of discarded bytes
+ */
+int snd_rawmidi_proceed(struct snd_rawmidi_substream *substream)
+{
+ struct snd_rawmidi_runtime *runtime;
+ int count = 0;
+
+ guard(spinlock_irqsave)(&substream->lock);
+ runtime = substream->runtime;
+ if (substream->opened && runtime &&
+ runtime->avail < runtime->buffer_size) {
+ count = runtime->buffer_size - runtime->avail;
+ __snd_rawmidi_transmit_ack(substream, count);
+ }
+ return count;
}
+EXPORT_SYMBOL(snd_rawmidi_proceed);
static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
const unsigned char __user *userbuf,
@@ -1166,48 +1523,55 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
unsigned long flags;
long count1, result;
struct snd_rawmidi_runtime *runtime = substream->runtime;
+ unsigned long appl_ptr;
- if (snd_BUG_ON(!kernelbuf && !userbuf))
+ if (!kernelbuf && !userbuf)
return -EINVAL;
if (snd_BUG_ON(!runtime->buffer))
return -EINVAL;
result = 0;
- spin_lock_irqsave(&runtime->lock, flags);
+ spin_lock_irqsave(&substream->lock, flags);
if (substream->append) {
if ((long)runtime->avail < count) {
- spin_unlock_irqrestore(&runtime->lock, flags);
+ spin_unlock_irqrestore(&substream->lock, flags);
return -EAGAIN;
}
}
+ snd_rawmidi_buffer_ref(runtime);
while (count > 0 && runtime->avail > 0) {
count1 = runtime->buffer_size - runtime->appl_ptr;
if (count1 > count)
count1 = count;
if (count1 > (long)runtime->avail)
count1 = runtime->avail;
+
+ /* update runtime->appl_ptr before unlocking for userbuf */
+ appl_ptr = runtime->appl_ptr;
+ runtime->appl_ptr += count1;
+ runtime->appl_ptr %= runtime->buffer_size;
+ runtime->avail -= count1;
+
if (kernelbuf)
- memcpy(runtime->buffer + runtime->appl_ptr,
+ memcpy(runtime->buffer + appl_ptr,
kernelbuf + result, count1);
else if (userbuf) {
- spin_unlock_irqrestore(&runtime->lock, flags);
- if (copy_from_user(runtime->buffer + runtime->appl_ptr,
+ spin_unlock_irqrestore(&substream->lock, flags);
+ if (copy_from_user(runtime->buffer + appl_ptr,
userbuf + result, count1)) {
- spin_lock_irqsave(&runtime->lock, flags);
+ spin_lock_irqsave(&substream->lock, flags);
result = result > 0 ? result : -EFAULT;
goto __end;
}
- spin_lock_irqsave(&runtime->lock, flags);
+ spin_lock_irqsave(&substream->lock, flags);
}
- runtime->appl_ptr += count1;
- runtime->appl_ptr %= runtime->buffer_size;
- runtime->avail -= count1;
result += count1;
count -= count1;
}
__end:
count1 = runtime->avail < runtime->buffer_size;
- spin_unlock_irqrestore(&runtime->lock, flags);
+ snd_rawmidi_buffer_unref(runtime);
+ spin_unlock_irqrestore(&substream->lock, flags);
if (count1)
snd_rawmidi_output_trigger(substream, 1);
return result;
@@ -1218,6 +1582,7 @@ long snd_rawmidi_kernel_write(struct snd_rawmidi_substream *substream,
{
return snd_rawmidi_kernel_write1(substream, NULL, buf, count);
}
+EXPORT_SYMBOL(snd_rawmidi_kernel_write);
static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
size_t count, loff_t *offset)
@@ -1236,26 +1601,31 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
return -EIO;
result = 0;
while (count > 0) {
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
while (!snd_rawmidi_ready_append(substream, count)) {
- wait_queue_t wait;
+ wait_queue_entry_t wait;
+
if (file->f_flags & O_NONBLOCK) {
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
return result > 0 ? result : -EAGAIN;
}
init_waitqueue_entry(&wait, current);
add_wait_queue(&runtime->sleep, &wait);
set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
timeout = schedule_timeout(30 * HZ);
remove_wait_queue(&runtime->sleep, &wait);
+ if (rfile->rmidi->card->shutdown)
+ return -ENODEV;
if (signal_pending(current))
return result > 0 ? result : -ERESTARTSYS;
- if (!runtime->avail && !timeout)
+ spin_lock_irq(&substream->lock);
+ if (!runtime->avail && !timeout) {
+ spin_unlock_irq(&substream->lock);
return result > 0 ? result : -EIO;
- spin_lock_irq(&runtime->lock);
+ }
}
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
count1 = snd_rawmidi_kernel_write1(substream, buf, NULL, count);
if (count1 < 0)
return result > 0 ? result : count1;
@@ -1266,32 +1636,33 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf,
count -= count1;
}
if (file->f_flags & O_DSYNC) {
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
while (runtime->avail != runtime->buffer_size) {
- wait_queue_t wait;
+ wait_queue_entry_t wait;
unsigned int last_avail = runtime->avail;
+
init_waitqueue_entry(&wait, current);
add_wait_queue(&runtime->sleep, &wait);
set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
timeout = schedule_timeout(30 * HZ);
remove_wait_queue(&runtime->sleep, &wait);
if (signal_pending(current))
return result > 0 ? result : -ERESTARTSYS;
if (runtime->avail == last_avail && !timeout)
return result > 0 ? result : -EIO;
- spin_lock_irq(&runtime->lock);
+ spin_lock_irq(&substream->lock);
}
- spin_unlock_irq(&runtime->lock);
+ spin_unlock_irq(&substream->lock);
}
return result;
}
-static unsigned int snd_rawmidi_poll(struct file *file, poll_table * wait)
+static __poll_t snd_rawmidi_poll(struct file *file, poll_table *wait)
{
struct snd_rawmidi_file *rfile;
struct snd_rawmidi_runtime *runtime;
- unsigned int mask;
+ __poll_t mask;
rfile = file->private_data;
if (rfile->input != NULL) {
@@ -1306,11 +1677,11 @@ static unsigned int snd_rawmidi_poll(struct file *file, poll_table * wait)
mask = 0;
if (rfile->input != NULL) {
if (snd_rawmidi_ready(rfile->input))
- mask |= POLLIN | POLLRDNORM;
+ mask |= EPOLLIN | EPOLLRDNORM;
}
if (rfile->output != NULL) {
if (snd_rawmidi_ready(rfile->output))
- mask |= POLLOUT | POLLWRNORM;
+ mask |= EPOLLOUT | EPOLLWRNORM;
}
return mask;
}
@@ -1324,7 +1695,6 @@ static unsigned int snd_rawmidi_poll(struct file *file, poll_table * wait)
#endif
/*
-
*/
static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
@@ -1333,10 +1703,18 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
struct snd_rawmidi *rmidi;
struct snd_rawmidi_substream *substream;
struct snd_rawmidi_runtime *runtime;
+ unsigned long buffer_size, avail, xruns;
+ unsigned int clock_type;
+ static const char *clock_names[4] = { "none", "realtime", "monotonic", "monotonic raw" };
rmidi = entry->private_data;
snd_iprintf(buffer, "%s\n\n", rmidi->name);
- mutex_lock(&rmidi->open_mutex);
+ if (IS_ENABLED(CONFIG_SND_UMP))
+ snd_iprintf(buffer, "Type: %s\n",
+ rawmidi_is_ump(rmidi) ? "UMP" : "Legacy");
+ if (rmidi->ops && rmidi->ops->proc_read)
+ rmidi->ops->proc_read(entry, buffer);
+ guard(mutex)(&rmidi->open_mutex);
if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) {
list_for_each_entry(substream,
&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams,
@@ -1351,13 +1729,16 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
" Owner PID : %d\n",
pid_vnr(substream->pid));
runtime = substream->runtime;
+ scoped_guard(spinlock_irq, &substream->lock) {
+ buffer_size = runtime->buffer_size;
+ avail = runtime->avail;
+ }
snd_iprintf(buffer,
" Mode : %s\n"
" Buffer size : %lu\n"
" Avail : %lu\n",
runtime->oss ? "OSS compatible" : "native",
- (unsigned long) runtime->buffer_size,
- (unsigned long) runtime->avail);
+ buffer_size, avail);
}
}
}
@@ -1375,31 +1756,39 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
" Owner PID : %d\n",
pid_vnr(substream->pid));
runtime = substream->runtime;
+ scoped_guard(spinlock_irq, &substream->lock) {
+ buffer_size = runtime->buffer_size;
+ avail = runtime->avail;
+ xruns = runtime->xruns;
+ }
snd_iprintf(buffer,
" Buffer size : %lu\n"
" Avail : %lu\n"
" Overruns : %lu\n",
- (unsigned long) runtime->buffer_size,
- (unsigned long) runtime->avail,
- (unsigned long) runtime->xruns);
+ buffer_size, avail, xruns);
+ if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) {
+ clock_type = substream->clock_type >> SNDRV_RAWMIDI_MODE_CLOCK_SHIFT;
+ if (!snd_BUG_ON(clock_type >= ARRAY_SIZE(clock_names)))
+ snd_iprintf(buffer,
+ " Framing : tstamp\n"
+ " Clock type : %s\n",
+ clock_names[clock_type]);
+ }
}
}
}
- mutex_unlock(&rmidi->open_mutex);
}
/*
* Register functions
*/
-static const struct file_operations snd_rawmidi_f_ops =
-{
+static const struct file_operations snd_rawmidi_f_ops = {
.owner = THIS_MODULE,
.read = snd_rawmidi_read,
.write = snd_rawmidi_write,
.open = snd_rawmidi_open,
.release = snd_rawmidi_release,
- .llseek = no_llseek,
.poll = snd_rawmidi_poll,
.unlocked_ioctl = snd_rawmidi_ioctl,
.compat_ioctl = snd_rawmidi_ioctl_compat,
@@ -1415,20 +1804,70 @@ static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi,
for (idx = 0; idx < count; idx++) {
substream = kzalloc(sizeof(*substream), GFP_KERNEL);
- if (substream == NULL) {
- snd_printk(KERN_ERR "rawmidi: cannot allocate substream\n");
+ if (!substream)
return -ENOMEM;
- }
substream->stream = direction;
substream->number = idx;
substream->rmidi = rmidi;
substream->pstr = stream;
+ spin_lock_init(&substream->lock);
list_add_tail(&substream->list, &stream->substreams);
stream->substream_count++;
}
return 0;
}
+/* used for both rawmidi and ump */
+int snd_rawmidi_init(struct snd_rawmidi *rmidi,
+ struct snd_card *card, char *id, int device,
+ int output_count, int input_count,
+ unsigned int info_flags)
+{
+ int err;
+ static const struct snd_device_ops ops = {
+ .dev_free = snd_rawmidi_dev_free,
+ .dev_register = snd_rawmidi_dev_register,
+ .dev_disconnect = snd_rawmidi_dev_disconnect,
+ };
+
+ rmidi->card = card;
+ rmidi->device = device;
+ mutex_init(&rmidi->open_mutex);
+ init_waitqueue_head(&rmidi->open_wait);
+ INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams);
+ INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams);
+ rmidi->info_flags = info_flags;
+
+ if (id != NULL)
+ strscpy(rmidi->id, id, sizeof(rmidi->id));
+
+ err = snd_device_alloc(&rmidi->dev, card);
+ if (err < 0)
+ return err;
+ if (rawmidi_is_ump(rmidi))
+ dev_set_name(rmidi->dev, "umpC%iD%i", card->number, device);
+ else
+ dev_set_name(rmidi->dev, "midiC%iD%i", card->number, device);
+
+ err = snd_rawmidi_alloc_substreams(rmidi,
+ &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT],
+ SNDRV_RAWMIDI_STREAM_INPUT,
+ input_count);
+ if (err < 0)
+ return err;
+ err = snd_rawmidi_alloc_substreams(rmidi,
+ &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT],
+ SNDRV_RAWMIDI_STREAM_OUTPUT,
+ output_count);
+ if (err < 0)
+ return err;
+ err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops);
+ if (err < 0)
+ return err;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_rawmidi_init);
+
/**
* snd_rawmidi_new - create a rawmidi instance
* @card: the card instance
@@ -1441,53 +1880,23 @@ static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi,
* Creates a new rawmidi instance.
* Use snd_rawmidi_set_ops() to set the operators to the new instance.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_rawmidi_new(struct snd_card *card, char *id, int device,
int output_count, int input_count,
- struct snd_rawmidi ** rrawmidi)
+ struct snd_rawmidi **rrawmidi)
{
struct snd_rawmidi *rmidi;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_rawmidi_dev_free,
- .dev_register = snd_rawmidi_dev_register,
- .dev_disconnect = snd_rawmidi_dev_disconnect,
- };
- if (snd_BUG_ON(!card))
- return -ENXIO;
if (rrawmidi)
*rrawmidi = NULL;
rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL);
- if (rmidi == NULL) {
- snd_printk(KERN_ERR "rawmidi: cannot allocate\n");
+ if (!rmidi)
return -ENOMEM;
- }
- rmidi->card = card;
- rmidi->device = device;
- mutex_init(&rmidi->open_mutex);
- init_waitqueue_head(&rmidi->open_wait);
- INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams);
- INIT_LIST_HEAD(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams);
-
- if (id != NULL)
- strlcpy(rmidi->id, id, sizeof(rmidi->id));
- if ((err = snd_rawmidi_alloc_substreams(rmidi,
- &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT],
- SNDRV_RAWMIDI_STREAM_INPUT,
- input_count)) < 0) {
- snd_rawmidi_free(rmidi);
- return err;
- }
- if ((err = snd_rawmidi_alloc_substreams(rmidi,
- &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT],
- SNDRV_RAWMIDI_STREAM_OUTPUT,
- output_count)) < 0) {
- snd_rawmidi_free(rmidi);
- return err;
- }
- if ((err = snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops)) < 0) {
+ err = snd_rawmidi_init(rmidi, card, id, device,
+ output_count, input_count, 0);
+ if (err < 0) {
snd_rawmidi_free(rmidi);
return err;
}
@@ -1495,6 +1904,7 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,
*rrawmidi = rmidi;
return 0;
}
+EXPORT_SYMBOL(snd_rawmidi_new);
static void snd_rawmidi_free_substreams(struct snd_rawmidi_str *stream)
{
@@ -1507,36 +1917,39 @@ static void snd_rawmidi_free_substreams(struct snd_rawmidi_str *stream)
}
}
-static int snd_rawmidi_free(struct snd_rawmidi *rmidi)
+/* called from ump.c, too */
+int snd_rawmidi_free(struct snd_rawmidi *rmidi)
{
if (!rmidi)
return 0;
snd_info_free_entry(rmidi->proc_entry);
rmidi->proc_entry = NULL;
- mutex_lock(&register_mutex);
if (rmidi->ops && rmidi->ops->dev_unregister)
rmidi->ops->dev_unregister(rmidi);
- mutex_unlock(&register_mutex);
snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]);
snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]);
if (rmidi->private_free)
rmidi->private_free(rmidi);
+ put_device(rmidi->dev);
kfree(rmidi);
return 0;
}
+EXPORT_SYMBOL_GPL(snd_rawmidi_free);
static int snd_rawmidi_dev_free(struct snd_device *device)
{
struct snd_rawmidi *rmidi = device->device_data;
+
return snd_rawmidi_free(rmidi);
}
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
static void snd_rawmidi_dev_seq_free(struct snd_seq_device *device)
{
struct snd_rawmidi *rmidi = device->private_data;
+
rmidi->seq_dev = NULL;
}
#endif
@@ -1550,35 +1963,38 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
if (rmidi->device >= SNDRV_RAWMIDI_DEVICES)
return -ENOMEM;
- mutex_lock(&register_mutex);
- if (snd_rawmidi_search(rmidi->card, rmidi->device)) {
- mutex_unlock(&register_mutex);
- return -EBUSY;
+ err = 0;
+ scoped_guard(mutex, &register_mutex) {
+ if (snd_rawmidi_search(rmidi->card, rmidi->device))
+ err = -EBUSY;
+ else
+ list_add_tail(&rmidi->list, &snd_rawmidi_devices);
}
- list_add_tail(&rmidi->list, &snd_rawmidi_devices);
- sprintf(name, "midiC%iD%i", rmidi->card->number, rmidi->device);
- if ((err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI,
- rmidi->card, rmidi->device,
- &snd_rawmidi_f_ops, rmidi, name)) < 0) {
- snd_printk(KERN_ERR "unable to register rawmidi device %i:%i\n", rmidi->card->number, rmidi->device);
- list_del(&rmidi->list);
- mutex_unlock(&register_mutex);
+ if (err < 0)
return err;
+
+ err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI,
+ rmidi->card, rmidi->device,
+ &snd_rawmidi_f_ops, rmidi, rmidi->dev);
+ if (err < 0) {
+ rmidi_err(rmidi, "unable to register\n");
+ goto error;
}
- if (rmidi->ops && rmidi->ops->dev_register &&
- (err = rmidi->ops->dev_register(rmidi)) < 0) {
- snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device);
- list_del(&rmidi->list);
- mutex_unlock(&register_mutex);
- return err;
+ if (rmidi->ops && rmidi->ops->dev_register) {
+ err = rmidi->ops->dev_register(rmidi);
+ if (err < 0)
+ goto error_unregister;
}
#ifdef CONFIG_SND_OSSEMUL
rmidi->ossreg = 0;
- if ((int)rmidi->device == midi_map[rmidi->card->number]) {
+ if (!rawmidi_is_ump(rmidi) &&
+ (int)rmidi->device == midi_map[rmidi->card->number]) {
if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI,
rmidi->card, 0, &snd_rawmidi_f_ops,
- rmidi, name) < 0) {
- snd_printk(KERN_ERR "unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 0);
+ rmidi) < 0) {
+ rmidi_err(rmidi,
+ "unable to register OSS rawmidi device %i:%i\n",
+ rmidi->card->number, 0);
} else {
rmidi->ossreg++;
#ifdef SNDRV_OSS_INFO_DEV_MIDI
@@ -1586,17 +2002,19 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
#endif
}
}
- if ((int)rmidi->device == amidi_map[rmidi->card->number]) {
+ if (!rawmidi_is_ump(rmidi) &&
+ (int)rmidi->device == amidi_map[rmidi->card->number]) {
if (snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIDI,
rmidi->card, 1, &snd_rawmidi_f_ops,
- rmidi, name) < 0) {
- snd_printk(KERN_ERR "unable to register OSS rawmidi device %i:%i\n", rmidi->card->number, 1);
+ rmidi) < 0) {
+ rmidi_err(rmidi,
+ "unable to register OSS rawmidi device %i:%i\n",
+ rmidi->card->number, 1);
} else {
rmidi->ossreg++;
}
}
#endif /* CONFIG_SND_OSSEMUL */
- mutex_unlock(&register_mutex);
sprintf(name, "midi%d", rmidi->device);
entry = snd_info_create_card_entry(rmidi->card, name, rmidi->card->proc_root);
if (entry) {
@@ -1608,8 +2026,9 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
}
}
rmidi->proc_entry = entry;
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
- if (!rmidi->ops || !rmidi->ops->dev_register) { /* own registration mechanism */
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+ /* no own registration mechanism? */
+ if (!rmidi->ops || !rmidi->ops->dev_register) {
if (snd_seq_device_new(rmidi->card, rmidi->device, SNDRV_SEQ_DEV_ID_MIDISYNTH, 0, &rmidi->seq_dev) >= 0) {
rmidi->seq_dev->private_data = rmidi;
rmidi->seq_dev->private_free = snd_rawmidi_dev_seq_free;
@@ -1619,14 +2038,33 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
}
#endif
return 0;
+
+ error_unregister:
+ snd_unregister_device(rmidi->dev);
+ error:
+ scoped_guard(mutex, &register_mutex)
+ list_del(&rmidi->list);
+ return err;
}
static int snd_rawmidi_dev_disconnect(struct snd_device *device)
{
struct snd_rawmidi *rmidi = device->device_data;
+ int dir;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
+ guard(mutex)(&rmidi->open_mutex);
+ wake_up(&rmidi->open_wait);
list_del_init(&rmidi->list);
+ for (dir = 0; dir < 2; dir++) {
+ struct snd_rawmidi_substream *s;
+
+ list_for_each_entry(s, &rmidi->streams[dir].substreams, list) {
+ if (s->runtime)
+ wake_up(&s->runtime->sleep);
+ }
+ }
+
#ifdef CONFIG_SND_OSSEMUL
if (rmidi->ossreg) {
if ((int)rmidi->device == midi_map[rmidi->card->number]) {
@@ -1640,8 +2078,7 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device)
rmidi->ossreg = 0;
}
#endif /* CONFIG_SND_OSSEMUL */
- snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device);
- mutex_unlock(&register_mutex);
+ snd_unregister_device(rmidi->dev);
return 0;
}
@@ -1654,13 +2091,14 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device)
* Sets the rawmidi operators for the given stream direction.
*/
void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream,
- struct snd_rawmidi_ops *ops)
+ const struct snd_rawmidi_ops *ops)
{
struct snd_rawmidi_substream *substream;
-
+
list_for_each_entry(substream, &rmidi->streams[stream].substreams, list)
substream->ops = ops;
}
+EXPORT_SYMBOL(snd_rawmidi_set_ops);
/*
* ENTRY functions
@@ -1668,23 +2106,22 @@ void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream,
static int __init alsa_rawmidi_init(void)
{
-
snd_ctl_register_ioctl(snd_rawmidi_control_ioctl);
snd_ctl_register_ioctl_compat(snd_rawmidi_control_ioctl);
#ifdef CONFIG_SND_OSSEMUL
- { int i;
/* check device map table */
- for (i = 0; i < SNDRV_CARDS; i++) {
+ for (int i = 0; i < SNDRV_CARDS; i++) {
if (midi_map[i] < 0 || midi_map[i] >= SNDRV_RAWMIDI_DEVICES) {
- snd_printk(KERN_ERR "invalid midi_map[%d] = %d\n", i, midi_map[i]);
+ pr_err("ALSA: rawmidi: invalid midi_map[%d] = %d\n",
+ i, midi_map[i]);
midi_map[i] = 0;
}
if (amidi_map[i] < 0 || amidi_map[i] >= SNDRV_RAWMIDI_DEVICES) {
- snd_printk(KERN_ERR "invalid amidi_map[%d] = %d\n", i, amidi_map[i]);
+ pr_err("ALSA: rawmidi: invalid amidi_map[%d] = %d\n",
+ i, amidi_map[i]);
amidi_map[i] = 1;
}
}
- }
#endif /* CONFIG_SND_OSSEMUL */
return 0;
}
@@ -1697,21 +2134,3 @@ static void __exit alsa_rawmidi_exit(void)
module_init(alsa_rawmidi_init)
module_exit(alsa_rawmidi_exit)
-
-EXPORT_SYMBOL(snd_rawmidi_output_params);
-EXPORT_SYMBOL(snd_rawmidi_input_params);
-EXPORT_SYMBOL(snd_rawmidi_drop_output);
-EXPORT_SYMBOL(snd_rawmidi_drain_output);
-EXPORT_SYMBOL(snd_rawmidi_drain_input);
-EXPORT_SYMBOL(snd_rawmidi_receive);
-EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
-EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
-EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
-EXPORT_SYMBOL(snd_rawmidi_transmit);
-EXPORT_SYMBOL(snd_rawmidi_new);
-EXPORT_SYMBOL(snd_rawmidi_set_ops);
-EXPORT_SYMBOL(snd_rawmidi_info_select);
-EXPORT_SYMBOL(snd_rawmidi_kernel_open);
-EXPORT_SYMBOL(snd_rawmidi_kernel_release);
-EXPORT_SYMBOL(snd_rawmidi_kernel_read);
-EXPORT_SYMBOL(snd_rawmidi_kernel_write);
diff --git a/sound/core/rawmidi_compat.c b/sound/core/rawmidi_compat.c
index 5268c1f58c25..2c6de6e113e4 100644
--- a/sound/core/rawmidi_compat.c
+++ b/sound/core/rawmidi_compat.c
@@ -1,21 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 32bit -> 64bit ioctl wrapper for raw MIDI API
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/* This file included from rawmidi.c */
@@ -27,8 +13,9 @@ struct snd_rawmidi_params32 {
u32 buffer_size;
u32 avail_min;
unsigned int no_active_sensing; /* avoid bit-field */
- unsigned char reserved[16];
-} __attribute__((packed));
+ unsigned int mode;
+ unsigned char reserved[12];
+} __packed;
static int snd_rawmidi_ioctl_params_compat(struct snd_rawmidi_file *rfile,
struct snd_rawmidi_params32 __user *src)
@@ -36,47 +23,55 @@ static int snd_rawmidi_ioctl_params_compat(struct snd_rawmidi_file *rfile,
struct snd_rawmidi_params params;
unsigned int val;
- if (rfile->output == NULL)
- return -EINVAL;
if (get_user(params.stream, &src->stream) ||
get_user(params.buffer_size, &src->buffer_size) ||
get_user(params.avail_min, &src->avail_min) ||
+ get_user(params.mode, &src->mode) ||
get_user(val, &src->no_active_sensing))
return -EFAULT;
params.no_active_sensing = val;
switch (params.stream) {
case SNDRV_RAWMIDI_STREAM_OUTPUT:
+ if (!rfile->output)
+ return -EINVAL;
return snd_rawmidi_output_params(rfile->output, &params);
case SNDRV_RAWMIDI_STREAM_INPUT:
+ if (!rfile->input)
+ return -EINVAL;
return snd_rawmidi_input_params(rfile->input, &params);
}
return -EINVAL;
}
-struct snd_rawmidi_status32 {
+struct compat_snd_rawmidi_status64 {
s32 stream;
- struct compat_timespec tstamp;
+ u8 rsvd[4]; /* alignment */
+ s64 tstamp_sec;
+ s64 tstamp_nsec;
u32 avail;
u32 xruns;
unsigned char reserved[16];
-} __attribute__((packed));
+} __packed;
-static int snd_rawmidi_ioctl_status_compat(struct snd_rawmidi_file *rfile,
- struct snd_rawmidi_status32 __user *src)
+static int snd_rawmidi_ioctl_status_compat64(struct snd_rawmidi_file *rfile,
+ struct compat_snd_rawmidi_status64 __user *src)
{
int err;
- struct snd_rawmidi_status status;
+ struct snd_rawmidi_status64 status;
+ struct compat_snd_rawmidi_status64 compat_status;
- if (rfile->output == NULL)
- return -EINVAL;
if (get_user(status.stream, &src->stream))
return -EFAULT;
switch (status.stream) {
case SNDRV_RAWMIDI_STREAM_OUTPUT:
+ if (!rfile->output)
+ return -EINVAL;
err = snd_rawmidi_output_status(rfile->output, &status);
break;
case SNDRV_RAWMIDI_STREAM_INPUT:
+ if (!rfile->input)
+ return -EINVAL;
err = snd_rawmidi_input_status(rfile->input, &status);
break;
default:
@@ -85,10 +80,15 @@ static int snd_rawmidi_ioctl_status_compat(struct snd_rawmidi_file *rfile,
if (err < 0)
return err;
- if (put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) ||
- put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) ||
- put_user(status.avail, &src->avail) ||
- put_user(status.xruns, &src->xruns))
+ compat_status = (struct compat_snd_rawmidi_status64) {
+ .stream = status.stream,
+ .tstamp_sec = status.tstamp_sec,
+ .tstamp_nsec = status.tstamp_nsec,
+ .avail = status.avail,
+ .xruns = status.xruns,
+ };
+
+ if (copy_to_user(src, &compat_status, sizeof(*src)))
return -EFAULT;
return 0;
@@ -96,7 +96,8 @@ static int snd_rawmidi_ioctl_status_compat(struct snd_rawmidi_file *rfile,
enum {
SNDRV_RAWMIDI_IOCTL_PARAMS32 = _IOWR('W', 0x10, struct snd_rawmidi_params32),
- SNDRV_RAWMIDI_IOCTL_STATUS32 = _IOWR('W', 0x20, struct snd_rawmidi_status32),
+ SNDRV_RAWMIDI_IOCTL_STATUS_COMPAT32 = _IOWR('W', 0x20, struct snd_rawmidi_status32),
+ SNDRV_RAWMIDI_IOCTL_STATUS_COMPAT64 = _IOWR('W', 0x20, struct compat_snd_rawmidi_status64),
};
static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
@@ -110,11 +111,17 @@ static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsign
case SNDRV_RAWMIDI_IOCTL_INFO:
case SNDRV_RAWMIDI_IOCTL_DROP:
case SNDRV_RAWMIDI_IOCTL_DRAIN:
+#if IS_ENABLED(CONFIG_SND_UMP)
+ case SNDRV_UMP_IOCTL_ENDPOINT_INFO:
+ case SNDRV_UMP_IOCTL_BLOCK_INFO:
+#endif
return snd_rawmidi_ioctl(file, cmd, (unsigned long)argp);
case SNDRV_RAWMIDI_IOCTL_PARAMS32:
return snd_rawmidi_ioctl_params_compat(rfile, argp);
- case SNDRV_RAWMIDI_IOCTL_STATUS32:
- return snd_rawmidi_ioctl_status_compat(rfile, argp);
+ case SNDRV_RAWMIDI_IOCTL_STATUS_COMPAT32:
+ return snd_rawmidi_ioctl_status32(rfile, argp);
+ case SNDRV_RAWMIDI_IOCTL_STATUS_COMPAT64:
+ return snd_rawmidi_ioctl_status_compat64(rfile, argp);
}
return -ENOIOCTLCMD;
}
diff --git a/sound/core/rtctimer.c b/sound/core/rtctimer.c
deleted file mode 100644
index 0851cd13e303..000000000000
--- a/sound/core/rtctimer.c
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * RTC based high-frequency timer
- *
- * Copyright (C) 2000 Takashi Iwai
- * based on rtctimer.c by Steve Ratcliffe
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/moduleparam.h>
-#include <linux/log2.h>
-#include <sound/core.h>
-#include <sound/timer.h>
-
-#if defined(CONFIG_RTC) || defined(CONFIG_RTC_MODULE)
-
-#include <linux/mc146818rtc.h>
-
-#define RTC_FREQ 1024 /* default frequency */
-#define NANO_SEC 1000000000L /* 10^9 in sec */
-
-/*
- * prototypes
- */
-static int rtctimer_open(struct snd_timer *t);
-static int rtctimer_close(struct snd_timer *t);
-static int rtctimer_start(struct snd_timer *t);
-static int rtctimer_stop(struct snd_timer *t);
-
-
-/*
- * The hardware dependent description for this timer.
- */
-static struct snd_timer_hardware rtc_hw = {
- .flags = SNDRV_TIMER_HW_AUTO |
- SNDRV_TIMER_HW_FIRST |
- SNDRV_TIMER_HW_TASKLET,
- .ticks = 100000000L, /* FIXME: XXX */
- .open = rtctimer_open,
- .close = rtctimer_close,
- .start = rtctimer_start,
- .stop = rtctimer_stop,
-};
-
-static int rtctimer_freq = RTC_FREQ; /* frequency */
-static struct snd_timer *rtctimer;
-static struct tasklet_struct rtc_tasklet;
-static rtc_task_t rtc_task;
-
-
-static int
-rtctimer_open(struct snd_timer *t)
-{
- int err;
-
- err = rtc_register(&rtc_task);
- if (err < 0)
- return err;
- t->private_data = &rtc_task;
- return 0;
-}
-
-static int
-rtctimer_close(struct snd_timer *t)
-{
- rtc_task_t *rtc = t->private_data;
- if (rtc) {
- rtc_unregister(rtc);
- tasklet_kill(&rtc_tasklet);
- t->private_data = NULL;
- }
- return 0;
-}
-
-static int
-rtctimer_start(struct snd_timer *timer)
-{
- rtc_task_t *rtc = timer->private_data;
- if (snd_BUG_ON(!rtc))
- return -EINVAL;
- rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq);
- rtc_control(rtc, RTC_PIE_ON, 0);
- return 0;
-}
-
-static int
-rtctimer_stop(struct snd_timer *timer)
-{
- rtc_task_t *rtc = timer->private_data;
- if (snd_BUG_ON(!rtc))
- return -EINVAL;
- rtc_control(rtc, RTC_PIE_OFF, 0);
- return 0;
-}
-
-static void rtctimer_tasklet(unsigned long data)
-{
- snd_timer_interrupt((struct snd_timer *)data, 1);
-}
-
-/*
- * interrupt
- */
-static void rtctimer_interrupt(void *private_data)
-{
- tasklet_schedule(private_data);
-}
-
-
-/*
- * ENTRY functions
- */
-static int __init rtctimer_init(void)
-{
- int err;
- struct snd_timer *timer;
-
- if (rtctimer_freq < 2 || rtctimer_freq > 8192 ||
- !is_power_of_2(rtctimer_freq)) {
- snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n",
- rtctimer_freq);
- return -EINVAL;
- }
-
- /* Create a new timer and set up the fields */
- err = snd_timer_global_new("rtc", SNDRV_TIMER_GLOBAL_RTC, &timer);
- if (err < 0)
- return err;
-
- timer->module = THIS_MODULE;
- strcpy(timer->name, "RTC timer");
- timer->hw = rtc_hw;
- timer->hw.resolution = NANO_SEC / rtctimer_freq;
-
- tasklet_init(&rtc_tasklet, rtctimer_tasklet, (unsigned long)timer);
-
- /* set up RTC callback */
- rtc_task.func = rtctimer_interrupt;
- rtc_task.private_data = &rtc_tasklet;
-
- err = snd_timer_global_register(timer);
- if (err < 0) {
- snd_timer_global_free(timer);
- return err;
- }
- rtctimer = timer; /* remember this */
-
- return 0;
-}
-
-static void __exit rtctimer_exit(void)
-{
- if (rtctimer) {
- snd_timer_global_free(rtctimer);
- rtctimer = NULL;
- }
-}
-
-
-/*
- * exported stuff
- */
-module_init(rtctimer_init)
-module_exit(rtctimer_exit)
-
-module_param(rtctimer_freq, int, 0444);
-MODULE_PARM_DESC(rtctimer_freq, "timer frequency in Hz");
-
-MODULE_LICENSE("GPL");
-
-MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_RTC));
-
-#endif /* CONFIG_RTC || CONFIG_RTC_MODULE */
diff --git a/sound/core/seq/Kconfig b/sound/core/seq/Kconfig
index b851fd890a89..e4f58cb985d4 100644
--- a/sound/core/seq/Kconfig
+++ b/sound/core/seq/Kconfig
@@ -1,16 +1,76 @@
-# define SND_XXX_SEQ to min(SND_SEQUENCER,SND_XXX)
+# SPDX-License-Identifier: GPL-2.0-only
+config SND_SEQUENCER
+ tristate "Sequencer support"
+ select SND_TIMER
+ select SND_SEQ_DEVICE
+ help
+ Say Y or M to enable MIDI sequencer and router support. This
+ feature allows routing and enqueueing of MIDI events. Events
+ can be processed at a given time.
-config SND_RAWMIDI_SEQ
- def_tristate SND_SEQUENCER && SND_RAWMIDI
+ Many programs require this feature, so you should enable it
+ unless you know what you're doing.
-config SND_OPL3_LIB_SEQ
- def_tristate SND_SEQUENCER && SND_OPL3_LIB
+if SND_SEQUENCER
-config SND_OPL4_LIB_SEQ
- def_tristate SND_SEQUENCER && SND_OPL4_LIB
+config SND_SEQ_DUMMY
+ tristate "Sequencer dummy client"
+ help
+ Say Y here to enable the dummy sequencer client. This client
+ is a simple MIDI-through client: all normal input events are
+ redirected to the output port immediately.
-config SND_SBAWE_SEQ
- def_tristate SND_SEQUENCER && SND_SBAWE
+ You don't need this unless you want to connect many MIDI
+ devices or applications together.
-config SND_EMU10K1_SEQ
- def_tristate SND_SEQUENCER && SND_EMU10K1
+ To compile this driver as a module, choose M here: the module
+ will be called snd-seq-dummy.
+
+config SND_SEQUENCER_OSS
+ tristate "OSS Sequencer API"
+ depends on SND_OSSEMUL
+ select SND_SEQ_MIDI_EVENT
+ help
+ Say Y here to enable OSS sequencer emulation (both
+ /dev/sequencer and /dev/music interfaces).
+
+ Many programs still use the OSS API, so say Y.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-seq-oss.
+
+config SND_SEQ_HRTIMER_DEFAULT
+ bool "Use HR-timer as default sequencer timer"
+ depends on SND_HRTIMER
+ default y
+ help
+ Say Y here to use the HR-timer backend as the default sequencer
+ timer.
+
+config SND_SEQ_MIDI_EVENT
+ tristate
+
+config SND_SEQ_MIDI
+ def_tristate SND_RAWMIDI
+ select SND_SEQ_MIDI_EVENT
+
+config SND_SEQ_MIDI_EMUL
+ tristate
+
+config SND_SEQ_VIRMIDI
+ tristate
+
+config SND_SEQ_UMP
+ bool "Support for UMP events"
+ default SND_UMP
+ help
+ Say Y here to enable the support for handling UMP (Universal MIDI
+ Packet) events via ALSA sequencer infrastructure, which is an
+ essential feature for enabling MIDI 2.0 support.
+ It includes the automatic conversion of ALSA sequencer events
+ among legacy and UMP clients.
+
+config SND_SEQ_UMP_CLIENT
+ def_tristate SND_UMP && SND_SEQ_UMP
+
+endif # SND_SEQUENCER
diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile
index 941f64a853eb..0904aa48d88b 100644
--- a/sound/core/seq/Makefile
+++ b/sound/core/seq/Makefile
@@ -1,29 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
#
-snd-seq-device-objs := seq_device.o
-snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
+snd-seq-y := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \
seq_fifo.o seq_prioq.o seq_timer.o \
- seq_system.o seq_ports.o seq_info.o
-snd-seq-midi-objs := seq_midi.o
-snd-seq-midi-emul-objs := seq_midi_emul.o
-snd-seq-midi-event-objs := seq_midi_event.o
-snd-seq-dummy-objs := seq_dummy.o
-snd-seq-virmidi-objs := seq_virmidi.o
+ seq_system.o seq_ports.o
+snd-seq-$(CONFIG_SND_PROC_FS) += seq_info.o
+snd-seq-$(CONFIG_SND_SEQ_UMP) += seq_ump_convert.o
+snd-seq-midi-y := seq_midi.o
+snd-seq-midi-emul-y := seq_midi_emul.o
+snd-seq-midi-event-y := seq_midi_event.o
+snd-seq-dummy-y := seq_dummy.o
+snd-seq-virmidi-y := seq_virmidi.o
+snd-seq-ump-client-y := seq_ump_client.o
-obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o snd-seq-device.o
-ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
- obj-$(CONFIG_SND_SEQUENCER) += snd-seq-midi-event.o
- obj-$(CONFIG_SND_SEQUENCER) += oss/
-endif
-obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o
+obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o
+obj-$(CONFIG_SND_SEQUENCER_OSS) += oss/
-# Toplevel Module Dependency
-obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o
-obj-$(CONFIG_SND_RAWMIDI_SEQ) += snd-seq-midi.o snd-seq-midi-event.o
-obj-$(CONFIG_SND_OPL3_LIB_SEQ) += snd-seq-midi-event.o snd-seq-midi-emul.o
-obj-$(CONFIG_SND_OPL4_LIB_SEQ) += snd-seq-midi-event.o snd-seq-midi-emul.o
-obj-$(CONFIG_SND_SBAWE_SEQ) += snd-seq-midi-emul.o snd-seq-virmidi.o
-obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-seq-midi-emul.o snd-seq-virmidi.o
+obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o
+obj-$(CONFIG_SND_SEQ_MIDI) += snd-seq-midi.o
+obj-$(CONFIG_SND_SEQ_UMP_CLIENT) += snd-seq-ump-client.o
+obj-$(CONFIG_SND_SEQ_MIDI_EMUL) += snd-seq-midi-emul.o
+obj-$(CONFIG_SND_SEQ_MIDI_EVENT) += snd-seq-midi-event.o
+obj-$(CONFIG_SND_SEQ_VIRMIDI) += snd-seq-virmidi.o
diff --git a/sound/core/seq/oss/Makefile b/sound/core/seq/oss/Makefile
index b38406b8463c..4e4741834208 100644
--- a/sound/core/seq/oss/Makefile
+++ b/sound/core/seq/oss/Makefile
@@ -1,10 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz>
#
-snd-seq-oss-objs := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \
+snd-seq-oss-y := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \
seq_oss_event.o seq_oss_rw.o seq_oss_synth.o \
seq_oss_midi.o seq_oss_readq.o seq_oss_writeq.o
-obj-$(CONFIG_SND_SEQUENCER) += snd-seq-oss.o
+obj-$(CONFIG_SND_SEQUENCER_OSS) += snd-seq-oss.o
diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c
index a1f1a2f00ccb..02d30d8b6c3a 100644
--- a/sound/core/seq/oss/seq_oss.c
+++ b/sound/core/seq/oss/seq_oss.c
@@ -1,28 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* registration of device and proc
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/compat.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/initval.h>
@@ -39,19 +27,13 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_SEQUENCER);
MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MUSIC);
-#ifdef SNDRV_SEQ_OSS_DEBUG
-module_param(seq_oss_debug, int, 0644);
-MODULE_PARM_DESC(seq_oss_debug, "debug option");
-int seq_oss_debug = 0;
-#endif
-
/*
* prototypes
*/
static int register_device(void);
static void unregister_device(void);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static int register_proc(void);
static void unregister_proc(void);
#else
@@ -64,36 +46,44 @@ static int odev_release(struct inode *inode, struct file *file);
static ssize_t odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset);
static ssize_t odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offset);
static long odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
-static unsigned int odev_poll(struct file *file, poll_table * wait);
+static __poll_t 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)
+ rc = register_device();
+ if (rc < 0)
goto error;
- if ((rc = register_proc()) < 0) {
+ rc = register_proc();
+ if (rc < 0) {
unregister_device();
goto error;
}
- if ((rc = snd_seq_oss_create_client()) < 0) {
+ rc = snd_seq_oss_create_client();
+ if (rc < 0) {
unregister_proc();
unregister_device();
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,13 +94,12 @@ static int __init alsa_seq_oss_init(void)
snd_seq_oss_synth_init();
error:
- snd_seq_autoload_unlock();
return rc;
}
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();
@@ -128,18 +117,15 @@ static DEFINE_MUTEX(register_mutex);
static int
odev_open(struct inode *inode, struct file *file)
{
- int level, rc;
+ int level;
if (iminor(inode) == SNDRV_MINOR_OSS_MUSIC)
level = SNDRV_SEQ_OSS_MODE_MUSIC;
else
level = SNDRV_SEQ_OSS_MODE_SYNTH;
- mutex_lock(&register_mutex);
- rc = snd_seq_oss_open(file, level);
- mutex_unlock(&register_mutex);
-
- return rc;
+ guard(mutex)(&register_mutex);
+ return snd_seq_oss_open(file, level);
}
static int
@@ -147,15 +133,12 @@ odev_release(struct inode *inode, struct file *file)
{
struct seq_oss_devinfo *dp;
- if ((dp = file->private_data) == NULL)
+ dp = file->private_data;
+ if (!dp)
return 0;
- snd_seq_oss_drain_write(dp);
-
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
snd_seq_oss_release(dp);
- mutex_unlock(&register_mutex);
-
return 0;
}
@@ -184,25 +167,38 @@ static long
odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct seq_oss_devinfo *dp;
+ long rc;
+
dp = file->private_data;
if (snd_BUG_ON(!dp))
return -ENXIO;
- return snd_seq_oss_ioctl(dp, cmd, arg);
+
+ if (cmd != SNDCTL_SEQ_SYNC &&
+ mutex_lock_interruptible(&register_mutex))
+ return -ERESTARTSYS;
+ rc = snd_seq_oss_ioctl(dp, cmd, arg);
+ if (cmd != SNDCTL_SEQ_SYNC)
+ mutex_unlock(&register_mutex);
+ return rc;
}
#ifdef CONFIG_COMPAT
-#define odev_ioctl_compat odev_ioctl
+static long odev_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return odev_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
#else
#define odev_ioctl_compat NULL
#endif
-static unsigned int
+static __poll_t
odev_poll(struct file *file, poll_table * wait)
{
struct seq_oss_devinfo *dp;
dp = file->private_data;
if (snd_BUG_ON(!dp))
- return -ENXIO;
+ return EPOLLERR;
return snd_seq_oss_poll(dp, file, wait);
}
@@ -228,58 +224,51 @@ register_device(void)
{
int rc;
- mutex_lock(&register_mutex);
- if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER,
- NULL, 0,
- &seq_oss_f_ops, NULL,
- SNDRV_SEQ_OSS_DEVNAME)) < 0) {
- snd_printk(KERN_ERR "can't register device seq\n");
- mutex_unlock(&register_mutex);
+ guard(mutex)(&register_mutex);
+ rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER,
+ NULL, 0,
+ &seq_oss_f_ops, NULL);
+ if (rc < 0) {
+ pr_err("ALSA: seq_oss: can't register device seq\n");
return rc;
}
- if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC,
- NULL, 0,
- &seq_oss_f_ops, NULL,
- SNDRV_SEQ_OSS_DEVNAME)) < 0) {
- snd_printk(KERN_ERR "can't register device music\n");
+ rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC,
+ NULL, 0,
+ &seq_oss_f_ops, NULL);
+ if (rc < 0) {
+ pr_err("ALSA: seq_oss: can't register device music\n");
snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0);
- mutex_unlock(&register_mutex);
return rc;
}
- debug_printk(("device registered\n"));
- mutex_unlock(&register_mutex);
return 0;
}
static void
unregister_device(void)
{
- mutex_lock(&register_mutex);
- debug_printk(("device unregistered\n"));
+ guard(mutex)(&register_mutex);
if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC, NULL, 0) < 0)
- snd_printk(KERN_ERR "error unregister device music\n");
+ pr_err("ALSA: seq_oss: error unregister device music\n");
if (snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0) < 0)
- snd_printk(KERN_ERR "error unregister device seq\n");
- mutex_unlock(&register_mutex);
+ pr_err("ALSA: seq_oss: error unregister device seq\n");
}
/*
* /proc interface
*/
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static struct snd_info_entry *info_entry;
static void
info_read(struct snd_info_entry *entry, struct snd_info_buffer *buf)
{
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
snd_iprintf(buf, "OSS sequencer emulation version %s\n", SNDRV_SEQ_OSS_VERSION_STR);
snd_seq_oss_system_info_read(buf);
snd_seq_oss_synth_info_read(buf);
snd_seq_oss_midi_info_read(buf);
- mutex_unlock(&register_mutex);
}
@@ -309,4 +298,4 @@ unregister_proc(void)
snd_info_free_entry(info_entry);
info_entry = NULL;
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h
index c0154a959d55..935cf3df0b30 100644
--- a/sound/core/seq/oss/seq_oss_device.h
+++ b/sound/core/seq/oss/seq_oss_device.h
@@ -1,21 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* OSS compatible sequencer driver
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SEQ_OSS_DEVICE_H
@@ -24,15 +11,13 @@
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/slab.h>
-#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <sound/core.h>
#include <sound/seq_oss.h>
#include <sound/rawmidi.h>
#include <sound/seq_kernel.h>
#include <sound/info.h>
-
-/* enable debug print */
-#define SNDRV_SEQ_OSS_DEBUG
+#include "../seq_clientmgr.h"
/* max. applications */
#define SNDRV_SEQ_OSS_MAX_CLIENTS 16
@@ -46,7 +31,6 @@
#define SNDRV_SEQ_OSS_VERSION_STR "0.1.8"
/* device and proc interface name */
-#define SNDRV_SEQ_OSS_DEVNAME "seq_oss"
#define SNDRV_SEQ_OSS_PROCNAME "oss"
@@ -71,7 +55,6 @@ struct seq_oss_chinfo {
struct seq_oss_synthinfo {
struct snd_seq_oss_arg arg;
struct seq_oss_chinfo *ch;
- struct seq_oss_synth_sysex *sysex;
int nr_voices;
int opened;
int is_midi;
@@ -128,14 +111,9 @@ void snd_seq_oss_release(struct seq_oss_devinfo *dp);
int snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long arg);
int snd_seq_oss_read(struct seq_oss_devinfo *dev, char __user *buf, int count);
int snd_seq_oss_write(struct seq_oss_devinfo *dp, const char __user *buf, int count, struct file *opt);
-unsigned int snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_table * wait);
+__poll_t snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_table * wait);
void snd_seq_oss_reset(struct seq_oss_devinfo *dp);
-void snd_seq_oss_drain_write(struct seq_oss_devinfo *dp);
-
-/* */
-void snd_seq_oss_process_queue(struct seq_oss_devinfo *dp, abstime_t time);
-
/* proc interface */
void snd_seq_oss_system_info_read(struct snd_info_buffer *buf);
@@ -155,11 +133,11 @@ snd_seq_oss_dispatch(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, int a
return snd_seq_kernel_client_dispatch(dp->cseq, ev, atomic, hop);
}
-/* ioctl */
+/* ioctl for writeq */
static inline int
snd_seq_oss_control(struct seq_oss_devinfo *dp, unsigned int type, void *arg)
{
- return snd_seq_kernel_client_ctl(dp->cseq, type, arg);
+ return snd_seq_kernel_client_ioctl(dp->cseq, type, arg);
}
/* fill the addresses in header */
@@ -173,17 +151,4 @@ snd_seq_oss_fill_addr(struct seq_oss_devinfo *dp, struct snd_seq_event *ev,
ev->dest.port = dest_port;
}
-
-/* misc. functions for proc interface */
-char *enabled_str(int bool);
-
-
-/* for debug */
-#ifdef SNDRV_SEQ_OSS_DEBUG
-extern int seq_oss_debug;
-#define debug_printk(x) do { if (seq_oss_debug > 0) snd_printd x; } while (0)
-#else
-#define debug_printk(x) /**/
-#endif
-
#endif /* __SEQ_OSS_DEVICE_H */
diff --git a/sound/core/seq/oss/seq_oss_event.c b/sound/core/seq/oss/seq_oss_event.c
index 066f5f3e3f4c..76fb81077eef 100644
--- a/sound/core/seq/oss/seq_oss_event.c
+++ b/sound/core/seq/oss/seq_oss_event.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "seq_oss_device.h"
@@ -26,6 +13,7 @@
#include <sound/seq_oss_legacy.h>
#include "seq_oss_readq.h"
#include "seq_oss_writeq.h"
+#include <linux/nospec.h>
/*
@@ -285,7 +273,12 @@ local_event(struct seq_oss_devinfo *dp, union evrec *q, struct snd_seq_event *ev
static int
note_on_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev)
{
- struct seq_oss_synthinfo *info = &dp->synths[dev];
+ struct seq_oss_synthinfo *info;
+
+ info = snd_seq_oss_synth_info(dp, dev);
+ if (!info)
+ return -ENXIO;
+
switch (info->arg.event_passing) {
case SNDRV_SEQ_OSS_PROCESS_EVENTS:
if (! info->ch || ch < 0 || ch >= info->nr_voices) {
@@ -293,19 +286,18 @@ note_on_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, st
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
}
+ ch = array_index_nospec(ch, info->nr_voices);
if (note == 255 && info->ch[ch].note >= 0) {
/* volume control */
int type;
- //if (! vel)
- /* set volume to zero -- note off */
- // type = SNDRV_SEQ_EVENT_NOTEOFF;
- //else
- if (info->ch[ch].vel)
+
+ if (info->ch[ch].vel)
/* sample already started -- volume change */
type = SNDRV_SEQ_EVENT_KEYPRESS;
else
/* sample not started -- start now */
type = SNDRV_SEQ_EVENT_NOTEON;
+
info->ch[ch].vel = vel;
return set_note_event(dp, dev, type, ch, info->ch[ch].note, vel, ev);
} else if (note >= 128)
@@ -340,7 +332,12 @@ note_on_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, st
static int
note_off_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, struct snd_seq_event *ev)
{
- struct seq_oss_synthinfo *info = &dp->synths[dev];
+ struct seq_oss_synthinfo *info;
+
+ info = snd_seq_oss_synth_info(dp, dev);
+ if (!info)
+ return -ENXIO;
+
switch (info->arg.event_passing) {
case SNDRV_SEQ_OSS_PROCESS_EVENTS:
if (! info->ch || ch < 0 || ch >= info->nr_voices) {
@@ -348,6 +345,7 @@ note_off_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, s
return set_note_event(dp, dev, SNDRV_SEQ_EVENT_NOTEON, ch, note, vel, ev);
}
+ ch = array_index_nospec(ch, info->nr_voices);
if (info->ch[ch].note >= 0) {
note = info->ch[ch].note;
info->ch[ch].vel = 0;
@@ -371,7 +369,7 @@ note_off_event(struct seq_oss_devinfo *dp, int dev, int ch, int note, int vel, s
static int
set_note_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int note, int vel, struct snd_seq_event *ev)
{
- if (! snd_seq_oss_synth_is_valid(dp, dev))
+ if (!snd_seq_oss_synth_info(dp, dev))
return -ENXIO;
ev->type = type;
@@ -389,7 +387,7 @@ set_note_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int note,
static int
set_control_event(struct seq_oss_devinfo *dp, int dev, int type, int ch, int param, int val, struct snd_seq_event *ev)
{
- if (! snd_seq_oss_synth_is_valid(dp, dev))
+ if (!snd_seq_oss_synth_info(dp, dev))
return -ENXIO;
ev->type = type;
diff --git a/sound/core/seq/oss/seq_oss_event.h b/sound/core/seq/oss/seq_oss_event.h
index 9a4d9adb7b8c..b4f723949a17 100644
--- a/sound/core/seq/oss/seq_oss_event.h
+++ b/sound/core/seq/oss/seq_oss_event.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* OSS compatible sequencer driver
*
* seq_oss_event.h - OSS event queue record
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SEQ_OSS_EVENT_H
diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c
index 69cd7b3c362d..973f057eb731 100644
--- a/sound/core/seq/oss/seq_oss_init.c
+++ b/sound/core/seq/oss/seq_oss_init.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* open/close and reset interface
*
* Copyright (C) 1998-1999 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "seq_oss_device.h"
@@ -28,8 +15,10 @@
#include "seq_oss_timer.h"
#include "seq_oss_event.h"
#include <linux/init.h>
+#include <linux/export.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
/*
* common variables
@@ -59,6 +48,14 @@ static void free_devinfo(void *private);
#define call_ctl(type,rec) snd_seq_kernel_client_ctl(system_client, type, rec)
+/* call snd_seq_oss_midi_lookup_ports() asynchronously */
+static void async_call_lookup_ports(struct work_struct *work)
+{
+ snd_seq_oss_midi_lookup_ports(system_client);
+}
+
+static DECLARE_WORK(async_lookup_work, async_call_lookup_ports);
+
/*
* create sequencer client for OSS sequencer
*/
@@ -66,30 +63,23 @@ int __init
snd_seq_oss_create_client(void)
{
int rc;
- struct snd_seq_port_info *port;
+ struct snd_seq_port_info *port __free(kfree) = NULL;
struct snd_seq_port_callback port_callback;
- port = kmalloc(sizeof(*port), GFP_KERNEL);
- if (!port) {
- rc = -ENOMEM;
- goto __error;
- }
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
/* create ALSA client */
rc = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_OSS,
"OSS sequencer");
if (rc < 0)
- goto __error;
+ return rc;
system_client = rc;
- debug_printk(("new client = %d\n", rc));
- /* look up midi devices */
- snd_seq_oss_midi_lookup_ports(system_client);
-
- /* create annoucement receiver port */
- memset(port, 0, sizeof(*port));
- strcpy(port->name, "Receiver");
+ /* create announcement receiver port */
+ strscpy(port->name, "Receiver");
port->addr.client = system_client;
port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* receive only */
port->type = 0;
@@ -101,10 +91,10 @@ snd_seq_oss_create_client(void)
port_callback.event_input = receive_announce;
port->kernel = &port_callback;
- call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, port);
- if ((system_port = port->addr.port) >= 0) {
+ if (call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, port) >= 0) {
struct snd_seq_port_subscribe subs;
+ system_port = port->addr.port;
memset(&subs, 0, sizeof(subs));
subs.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
subs.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
@@ -112,16 +102,16 @@ snd_seq_oss_create_client(void)
subs.dest.port = system_port;
call_ctl(SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &subs);
}
- rc = 0;
- __error:
- kfree(port);
- return rc;
+ /* look up midi devices */
+ schedule_work(&async_lookup_work);
+
+ return 0;
}
/*
- * receive annoucement from system port, and check the midi device
+ * receive announcement from system port, and check the midi device
*/
static int
receive_announce(struct snd_seq_event *ev, int direct, void *private, int atomic, int hop)
@@ -159,6 +149,7 @@ receive_announce(struct snd_seq_event *ev, int direct, void *private, int atomic
int
snd_seq_oss_delete_client(void)
{
+ cancel_work_sync(&async_lookup_work);
if (system_client >= 0)
snd_seq_delete_kernel_client(system_client);
@@ -178,11 +169,8 @@ snd_seq_oss_open(struct file *file, int level)
struct seq_oss_devinfo *dp;
dp = kzalloc(sizeof(*dp), GFP_KERNEL);
- if (!dp) {
- snd_printk(KERN_ERR "can't malloc device info\n");
+ if (!dp)
return -ENOMEM;
- }
- debug_printk(("oss_open: dp = %p\n", dp));
dp->cseq = system_client;
dp->port = -1;
@@ -195,7 +183,7 @@ snd_seq_oss_open(struct file *file, int level)
dp->index = i;
if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) {
- snd_printk(KERN_ERR "too many applications\n");
+ pr_debug("ALSA: seq_oss: too many applications\n");
rc = -ENOMEM;
goto _error;
}
@@ -205,21 +193,19 @@ snd_seq_oss_open(struct file *file, int level)
snd_seq_oss_midi_setup(dp);
if (dp->synth_opened == 0 && dp->max_mididev == 0) {
- /* snd_printk(KERN_ERR "no device found\n"); */
+ /* pr_err("ALSA: seq_oss: no device found\n"); */
rc = -ENODEV;
goto _error;
}
/* create port */
- debug_printk(("create new port\n"));
rc = create_port(dp);
if (rc < 0) {
- snd_printk(KERN_ERR "can't create port\n");
+ pr_err("ALSA: seq_oss: can't create port\n");
goto _error;
}
/* allocate queue */
- debug_printk(("allocate queue\n"));
rc = alloc_seq_queue(dp);
if (rc < 0)
goto _error;
@@ -236,7 +222,6 @@ snd_seq_oss_open(struct file *file, int level)
dp->file_mode = translate_mode(file);
/* initialize read queue */
- debug_printk(("initialize read queue\n"));
if (is_read_mode(dp->file_mode)) {
dp->readq = snd_seq_oss_readq_new(dp, maxqlen);
if (!dp->readq) {
@@ -246,7 +231,6 @@ snd_seq_oss_open(struct file *file, int level)
}
/* initialize write queue */
- debug_printk(("initialize write queue\n"));
if (is_write_mode(dp->file_mode)) {
dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen);
if (!dp->writeq) {
@@ -256,14 +240,12 @@ snd_seq_oss_open(struct file *file, int level)
}
/* initialize timer */
- debug_printk(("initialize timer\n"));
dp->timer = snd_seq_oss_timer_new(dp);
if (!dp->timer) {
- snd_printk(KERN_ERR "can't alloc timer\n");
+ pr_err("ALSA: seq_oss: can't alloc timer\n");
rc = -ENOMEM;
goto _error;
}
- debug_printk(("timer initialized\n"));
/* set private data pointer */
file->private_data = dp;
@@ -277,7 +259,6 @@ snd_seq_oss_open(struct file *file, int level)
client_table[dp->index] = dp;
num_clients++;
- debug_printk(("open done\n"));
return 0;
_error:
@@ -336,7 +317,6 @@ create_port(struct seq_oss_devinfo *dp)
return rc;
dp->port = port.addr.port;
- debug_printk(("new port = %d\n", port.addr.port));
return 0;
}
@@ -352,7 +332,6 @@ delete_port(struct seq_oss_devinfo *dp)
return 0;
}
- debug_printk(("delete_port %i\n", dp->port));
return snd_seq_event_port_detach(dp->cseq, dp->port);
}
@@ -368,8 +347,9 @@ alloc_seq_queue(struct seq_oss_devinfo *dp)
memset(&qinfo, 0, sizeof(qinfo));
qinfo.owner = system_client;
qinfo.locked = 1;
- strcpy(qinfo.name, "OSS Sequencer Emulation");
- if ((rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo)) < 0)
+ strscpy(qinfo.name, "OSS Sequencer Emulation");
+ rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo);
+ if (rc < 0)
return rc;
dp->queue = qinfo.queue;
return 0;
@@ -390,7 +370,7 @@ delete_seq_queue(int queue)
qinfo.queue = queue;
rc = call_ctl(SNDRV_SEQ_IOCTL_DELETE_QUEUE, &qinfo);
if (rc < 0)
- printk(KERN_ERR "seq-oss: unable to delete queue %d (%d)\n", queue, rc);
+ pr_err("ALSA: seq_oss: unable to delete queue %d (%d)\n", queue, rc);
return rc;
}
@@ -403,14 +383,11 @@ free_devinfo(void *private)
{
struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private;
- if (dp->timer)
- snd_seq_oss_timer_delete(dp->timer);
+ snd_seq_oss_timer_delete(dp->timer);
- if (dp->writeq)
- snd_seq_oss_writeq_delete(dp->writeq);
+ snd_seq_oss_writeq_delete(dp->writeq);
- if (dp->readq)
- snd_seq_oss_readq_delete(dp->readq);
+ snd_seq_oss_readq_delete(dp->readq);
kfree(dp);
}
@@ -427,38 +404,16 @@ snd_seq_oss_release(struct seq_oss_devinfo *dp)
client_table[dp->index] = NULL;
num_clients--;
- debug_printk(("resetting..\n"));
snd_seq_oss_reset(dp);
- debug_printk(("cleaning up..\n"));
snd_seq_oss_synth_cleanup(dp);
snd_seq_oss_midi_cleanup(dp);
/* clear slot */
- debug_printk(("releasing resource..\n"));
queue = dp->queue;
if (dp->port >= 0)
delete_port(dp);
delete_seq_queue(queue);
-
- debug_printk(("release done\n"));
-}
-
-
-/*
- * Wait until the queue is empty (if we don't have nonblock)
- */
-void
-snd_seq_oss_drain_write(struct seq_oss_devinfo *dp)
-{
- if (! dp->timer->running)
- return;
- if (is_write_mode(dp->file_mode) && !is_nonblock_mode(dp->file_mode) &&
- dp->writeq) {
- debug_printk(("syncing..\n"));
- while (snd_seq_oss_writeq_sync(dp->writeq))
- ;
- }
}
@@ -490,21 +445,14 @@ snd_seq_oss_reset(struct seq_oss_devinfo *dp)
snd_seq_oss_timer_stop(dp->timer);
}
-
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* misc. functions for proc interface
*/
-char *
-enabled_str(int bool)
-{
- return bool ? "enabled" : "disabled";
-}
-
-static char *
+static const char *
filemode_str(int val)
{
- static char *str[] = {
+ static const char * const str[] = {
"none", "read", "write", "read/write",
};
return str[val & SNDRV_SEQ_OSS_FILE_ACMODE];
@@ -526,7 +474,8 @@ snd_seq_oss_system_info_read(struct snd_info_buffer *buf)
snd_iprintf(buf, "\nNumber of applications: %d\n", num_clients);
for (i = 0; i < num_clients; i++) {
snd_iprintf(buf, "\nApplication %d: ", i);
- if ((dp = client_table[i]) == NULL) {
+ dp = client_table[i];
+ if (!dp) {
snd_iprintf(buf, "*empty*\n");
continue;
}
@@ -542,4 +491,4 @@ snd_seq_oss_system_info_read(struct snd_info_buffer *buf)
snd_seq_oss_readq_info_read(dp->readq, buf);
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/oss/seq_oss_ioctl.c b/sound/core/seq/oss/seq_oss_ioctl.c
index 5ac701c903c1..ccf682689ec9 100644
--- a/sound/core/seq/oss/seq_oss_ioctl.c
+++ b/sound/core/seq/oss/seq_oss_ioctl.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* OSS compatible i/o control
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "seq_oss_device.h"
@@ -62,7 +49,7 @@ static int snd_seq_oss_oob_user(struct seq_oss_devinfo *dp, void __user *arg)
if (copy_from_user(ev, arg, 8))
return -EFAULT;
memset(&tmpev, 0, sizeof(tmpev));
- snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.port, dp->addr.client);
+ snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.client, dp->addr.port);
tmpev.time.tick = 0;
if (! snd_seq_oss_process_event(dp, (union evrec *)ev, &tmpev)) {
snd_seq_oss_dispatch(dp, &tmpev, 0, 0);
@@ -90,12 +77,10 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca
return snd_seq_oss_timer_ioctl(dp->timer, cmd, arg);
case SNDCTL_SEQ_PANIC:
- debug_printk(("panic\n"));
snd_seq_oss_reset(dp);
return -EINVAL;
case SNDCTL_SEQ_SYNC:
- debug_printk(("sync\n"));
if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
return 0;
while (snd_seq_oss_writeq_sync(dp->writeq))
@@ -105,55 +90,45 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca
return 0;
case SNDCTL_SEQ_RESET:
- debug_printk(("reset\n"));
snd_seq_oss_reset(dp);
return 0;
case SNDCTL_SEQ_TESTMIDI:
- debug_printk(("test midi\n"));
if (get_user(dev, p))
return -EFAULT;
return snd_seq_oss_midi_open(dp, dev, dp->file_mode);
case SNDCTL_SEQ_GETINCOUNT:
- debug_printk(("get in count\n"));
if (dp->readq == NULL || ! is_read_mode(dp->file_mode))
return 0;
return put_user(dp->readq->qlen, p) ? -EFAULT : 0;
case SNDCTL_SEQ_GETOUTCOUNT:
- debug_printk(("get out count\n"));
if (! is_write_mode(dp->file_mode) || dp->writeq == NULL)
return 0;
return put_user(snd_seq_oss_writeq_get_free_size(dp->writeq), p) ? -EFAULT : 0;
case SNDCTL_SEQ_GETTIME:
- debug_printk(("get time\n"));
return put_user(snd_seq_oss_timer_cur_tick(dp->timer), p) ? -EFAULT : 0;
case SNDCTL_SEQ_RESETSAMPLES:
- debug_printk(("reset samples\n"));
if (get_user(dev, p))
return -EFAULT;
return snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
case SNDCTL_SEQ_NRSYNTHS:
- debug_printk(("nr synths\n"));
return put_user(dp->max_synthdev, p) ? -EFAULT : 0;
case SNDCTL_SEQ_NRMIDIS:
- debug_printk(("nr midis\n"));
return put_user(dp->max_mididev, p) ? -EFAULT : 0;
case SNDCTL_SYNTH_MEMAVL:
- debug_printk(("mem avail\n"));
if (get_user(dev, p))
return -EFAULT;
val = snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
return put_user(val, p) ? -EFAULT : 0;
case SNDCTL_FM_4OP_ENABLE:
- debug_printk(("4op\n"));
if (get_user(dev, p))
return -EFAULT;
snd_seq_oss_synth_ioctl(dp, dev, cmd, carg);
@@ -161,19 +136,15 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca
case SNDCTL_SYNTH_INFO:
case SNDCTL_SYNTH_ID:
- debug_printk(("synth info\n"));
return snd_seq_oss_synth_info_user(dp, arg);
case SNDCTL_SEQ_OUTOFBAND:
- debug_printk(("out of band\n"));
return snd_seq_oss_oob_user(dp, arg);
case SNDCTL_MIDI_INFO:
- debug_printk(("midi info\n"));
return snd_seq_oss_midi_info_user(dp, arg);
case SNDCTL_SEQ_THRESHOLD:
- debug_printk(("threshold\n"));
if (! is_write_mode(dp->file_mode))
return 0;
if (get_user(val, p))
@@ -186,7 +157,6 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca
return 0;
case SNDCTL_MIDI_PRETIME:
- debug_printk(("pretime\n"));
if (dp->readq == NULL || !is_read_mode(dp->file_mode))
return 0;
if (get_user(val, p))
@@ -199,7 +169,6 @@ snd_seq_oss_ioctl(struct seq_oss_devinfo *dp, unsigned int cmd, unsigned long ca
return put_user(val, p) ? -EFAULT : 0;
default:
- debug_printk(("others\n"));
if (! is_write_mode(dp->file_mode))
return -EIO;
return snd_seq_oss_synth_ioctl(dp, 0, cmd, carg);
diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c
index 677dc84590c7..023e5d0a4351 100644
--- a/sound/core/seq/oss/seq_oss_midi.c
+++ b/sound/core/seq/oss/seq_oss_midi.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* MIDI device handlers
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sound/asoundef.h>
@@ -29,6 +16,7 @@
#include "../seq_lock.h"
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/nospec.h>
/*
@@ -49,8 +37,10 @@ struct seq_oss_midi {
struct snd_midi_event *coder; /* MIDI event coder */
struct seq_oss_devinfo *devinfo; /* assigned OSSseq device */
snd_use_lock_t use_lock;
+ struct mutex open_mutex;
};
+DEFINE_FREE(seq_oss_midi, struct seq_oss_midi *, if (!IS_ERR_OR_NULL(_T)) snd_use_lock_free(&(_T)->use_lock))
/*
* midi device table
@@ -72,19 +62,16 @@ static int send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev,
* look up the existing ports
* this looks a very exhausting job.
*/
-int __init
+int
snd_seq_oss_midi_lookup_ports(int client)
{
- struct snd_seq_client_info *clinfo;
- struct snd_seq_port_info *pinfo;
+ struct snd_seq_client_info *clinfo __free(kfree) = NULL;
+ struct snd_seq_port_info *pinfo __free(kfree) = NULL;
clinfo = kzalloc(sizeof(*clinfo), GFP_KERNEL);
pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
- if (! clinfo || ! pinfo) {
- kfree(clinfo);
- kfree(pinfo);
+ if (!clinfo || !pinfo)
return -ENOMEM;
- }
clinfo->client = -1;
while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, clinfo) == 0) {
if (clinfo->client == client)
@@ -94,8 +81,6 @@ snd_seq_oss_midi_lookup_ports(int client)
while (snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, pinfo) == 0)
snd_seq_oss_midi_check_new_port(pinfo);
}
- kfree(clinfo);
- kfree(pinfo);
return 0;
}
@@ -106,13 +91,11 @@ static struct seq_oss_midi *
get_mdev(int dev)
{
struct seq_oss_midi *mdev;
- unsigned long flags;
- spin_lock_irqsave(&register_lock, flags);
+ guard(spinlock_irqsave)(&register_lock);
mdev = midi_devs[dev];
if (mdev)
snd_use_lock_use(&mdev->use_lock);
- spin_unlock_irqrestore(&register_lock, flags);
return mdev;
}
@@ -124,19 +107,16 @@ find_slot(int client, int port)
{
int i;
struct seq_oss_midi *mdev;
- unsigned long flags;
- spin_lock_irqsave(&register_lock, flags);
+ guard(spinlock_irqsave)(&register_lock);
for (i = 0; i < max_midi_devs; i++) {
mdev = midi_devs[i];
if (mdev && mdev->client == client && mdev->port == port) {
/* found! */
snd_use_lock_use(&mdev->use_lock);
- spin_unlock_irqrestore(&register_lock, flags);
return mdev;
}
}
- spin_unlock_irqrestore(&register_lock, flags);
return NULL;
}
@@ -151,9 +131,7 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
{
int i;
struct seq_oss_midi *mdev;
- unsigned long flags;
- debug_printk(("check for MIDI client %d port %d\n", pinfo->addr.client, pinfo->addr.port));
/* the port must include generic midi */
if (! (pinfo->type & SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC))
return 0;
@@ -165,7 +143,8 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
/*
* look for the identical slot
*/
- if ((mdev = find_slot(pinfo->addr.client, pinfo->addr.port)) != NULL) {
+ mdev = find_slot(pinfo->addr.client, pinfo->addr.port);
+ if (mdev) {
/* already exists */
snd_use_lock_free(&mdev->use_lock);
return 0;
@@ -174,10 +153,9 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
/*
* allocate midi info record
*/
- if ((mdev = kzalloc(sizeof(*mdev), GFP_KERNEL)) == NULL) {
- snd_printk(KERN_ERR "can't malloc midi info\n");
+ mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
+ if (!mdev)
return -ENOMEM;
- }
/* copy the port information */
mdev->client = pinfo->addr.client;
@@ -185,13 +163,14 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
mdev->flags = pinfo->capability;
mdev->opened = 0;
snd_use_lock_init(&mdev->use_lock);
+ mutex_init(&mdev->open_mutex);
/* copy and truncate the name of synth device */
- strlcpy(mdev->name, pinfo->name, sizeof(mdev->name));
+ strscpy(mdev->name, pinfo->name, sizeof(mdev->name));
/* create MIDI coder */
if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &mdev->coder) < 0) {
- snd_printk(KERN_ERR "can't malloc midi coder\n");
+ pr_err("ALSA: seq_oss: can't malloc midi coder\n");
kfree(mdev);
return -ENOMEM;
}
@@ -201,14 +180,13 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
/*
* look for en empty slot
*/
- spin_lock_irqsave(&register_lock, flags);
+ guard(spinlock_irqsave)(&register_lock);
for (i = 0; i < max_midi_devs; i++) {
if (midi_devs[i] == NULL)
break;
}
if (i >= max_midi_devs) {
if (max_midi_devs >= SNDRV_SEQ_OSS_MAX_MIDI_DEVS) {
- spin_unlock_irqrestore(&register_lock, flags);
snd_midi_event_free(mdev->coder);
kfree(mdev);
return -ENOMEM;
@@ -217,7 +195,6 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
}
mdev->seq_device = i;
midi_devs[mdev->seq_device] = mdev;
- spin_unlock_irqrestore(&register_lock, flags);
return 0;
}
@@ -229,26 +206,24 @@ int
snd_seq_oss_midi_check_exit_port(int client, int port)
{
struct seq_oss_midi *mdev;
- unsigned long flags;
int index;
- if ((mdev = find_slot(client, port)) != NULL) {
- spin_lock_irqsave(&register_lock, flags);
- midi_devs[mdev->seq_device] = NULL;
- spin_unlock_irqrestore(&register_lock, flags);
+ mdev = find_slot(client, port);
+ if (mdev) {
+ scoped_guard(spinlock_irqsave, &register_lock) {
+ midi_devs[mdev->seq_device] = NULL;
+ }
snd_use_lock_free(&mdev->use_lock);
snd_use_lock_sync(&mdev->use_lock);
- if (mdev->coder)
- snd_midi_event_free(mdev->coder);
+ snd_midi_event_free(mdev->coder);
kfree(mdev);
}
- spin_lock_irqsave(&register_lock, flags);
+ guard(spinlock_irqsave)(&register_lock);
for (index = max_midi_devs - 1; index >= 0; index--) {
if (midi_devs[index])
break;
}
max_midi_devs = index + 1;
- spin_unlock_irqrestore(&register_lock, flags);
return 0;
}
@@ -261,19 +236,17 @@ snd_seq_oss_midi_clear_all(void)
{
int i;
struct seq_oss_midi *mdev;
- unsigned long flags;
- spin_lock_irqsave(&register_lock, flags);
+ guard(spinlock_irqsave)(&register_lock);
for (i = 0; i < max_midi_devs; i++) {
- if ((mdev = midi_devs[i]) != NULL) {
- if (mdev->coder)
- snd_midi_event_free(mdev->coder);
+ mdev = midi_devs[i];
+ if (mdev) {
+ snd_midi_event_free(mdev->coder);
kfree(mdev);
midi_devs[i] = NULL;
}
}
max_midi_devs = 0;
- spin_unlock_irqrestore(&register_lock, flags);
}
@@ -283,6 +256,7 @@ snd_seq_oss_midi_clear_all(void)
void
snd_seq_oss_midi_setup(struct seq_oss_devinfo *dp)
{
+ guard(spinlock_irq)(&register_lock);
dp->max_mididev = max_midi_devs;
}
@@ -319,6 +293,7 @@ get_mididev(struct seq_oss_devinfo *dp, int dev)
{
if (dev < 0 || dev >= dp->max_mididev)
return NULL;
+ dev = array_index_nospec(dev, dp->max_mididev);
return get_mdev(dev);
}
@@ -330,17 +305,17 @@ int
snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode)
{
int perm;
- struct seq_oss_midi *mdev;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
struct snd_seq_port_subscribe subs;
- if ((mdev = get_mididev(dp, dev)) == NULL)
+ mdev = get_mididev(dp, dev);
+ if (!mdev)
return -ENODEV;
+ guard(mutex)(&mdev->open_mutex);
/* already used? */
- if (mdev->opened && mdev->devinfo != dp) {
- snd_use_lock_free(&mdev->use_lock);
+ if (mdev->opened && mdev->devinfo != dp)
return -EBUSY;
- }
perm = 0;
if (is_write_mode(fmode))
@@ -348,16 +323,12 @@ snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode)
if (is_read_mode(fmode))
perm |= PERM_READ;
perm &= mdev->flags;
- if (perm == 0) {
- snd_use_lock_free(&mdev->use_lock);
+ if (perm == 0)
return -ENXIO;
- }
/* already opened? */
- if ((mdev->opened & perm) == perm) {
- snd_use_lock_free(&mdev->use_lock);
+ if ((mdev->opened & perm) == perm)
return 0;
- }
perm &= ~mdev->opened;
@@ -380,13 +351,10 @@ snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode)
mdev->opened |= PERM_READ;
}
- if (! mdev->opened) {
- snd_use_lock_free(&mdev->use_lock);
+ if (!mdev->opened)
return -ENXIO;
- }
mdev->devinfo = dp;
- snd_use_lock_free(&mdev->use_lock);
return 0;
}
@@ -396,17 +364,16 @@ snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode)
int
snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev)
{
- struct seq_oss_midi *mdev;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
struct snd_seq_port_subscribe subs;
- if ((mdev = get_mididev(dp, dev)) == NULL)
+ mdev = get_mididev(dp, dev);
+ if (!mdev)
return -ENODEV;
- if (! mdev->opened || mdev->devinfo != dp) {
- snd_use_lock_free(&mdev->use_lock);
+ guard(mutex)(&mdev->open_mutex);
+ if (!mdev->opened || mdev->devinfo != dp)
return 0;
- }
- debug_printk(("closing client %d port %d mode %d\n", mdev->client, mdev->port, mdev->opened));
memset(&subs, 0, sizeof(subs));
if (mdev->opened & PERM_WRITE) {
subs.sender = dp->addr;
@@ -423,8 +390,6 @@ snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev)
mdev->opened = 0;
mdev->devinfo = NULL;
-
- snd_use_lock_free(&mdev->use_lock);
return 0;
}
@@ -434,10 +399,11 @@ snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev)
int
snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev)
{
- struct seq_oss_midi *mdev;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
int mode;
- if ((mdev = get_mididev(dp, dev)) == NULL)
+ mdev = get_mididev(dp, dev);
+ if (!mdev)
return 0;
mode = 0;
@@ -446,7 +412,6 @@ snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev)
if (mdev->opened & PERM_READ)
mode |= SNDRV_SEQ_OSS_FILE_READ;
- snd_use_lock_free(&mdev->use_lock);
return mode;
}
@@ -457,20 +422,18 @@ snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev)
void
snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)
{
- struct seq_oss_midi *mdev;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
- if ((mdev = get_mididev(dp, dev)) == NULL)
+ mdev = get_mididev(dp, dev);
+ if (!mdev)
return;
- if (! mdev->opened) {
- snd_use_lock_free(&mdev->use_lock);
+ if (!mdev->opened)
return;
- }
if (mdev->opened & PERM_WRITE) {
struct snd_seq_event ev;
int c;
- debug_printk(("resetting client %d port %d\n", mdev->client, mdev->port));
memset(&ev, 0, sizeof(ev));
ev.dest.client = mdev->client;
ev.dest.port = mdev->port;
@@ -496,7 +459,6 @@ snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)
}
}
// snd_seq_oss_midi_close(dp, dev);
- snd_use_lock_free(&mdev->use_lock);
}
@@ -506,13 +468,13 @@ snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)
void
snd_seq_oss_midi_get_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_addr *addr)
{
- struct seq_oss_midi *mdev;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
- if ((mdev = get_mididev(dp, dev)) == NULL)
+ mdev = get_mididev(dp, dev);
+ if (!mdev)
return;
addr->client = mdev->client;
addr->port = mdev->port;
- snd_use_lock_free(&mdev->use_lock);
}
@@ -523,25 +485,20 @@ int
snd_seq_oss_midi_input(struct snd_seq_event *ev, int direct, void *private_data)
{
struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private_data;
- struct seq_oss_midi *mdev;
- int rc;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
if (dp->readq == NULL)
return 0;
- if ((mdev = find_slot(ev->source.client, ev->source.port)) == NULL)
+ mdev = find_slot(ev->source.client, ev->source.port);
+ if (!mdev)
return 0;
- if (! (mdev->opened & PERM_READ)) {
- snd_use_lock_free(&mdev->use_lock);
+ if (!(mdev->opened & PERM_READ))
return 0;
- }
if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC)
- rc = send_synth_event(dp, ev, mdev->seq_device);
+ return send_synth_event(dp, ev, mdev->seq_device);
else
- rc = send_midi_event(dp, ev, mdev);
-
- snd_use_lock_free(&mdev->use_lock);
- return rc;
+ return send_midi_event(dp, ev, mdev);
}
/*
@@ -618,9 +575,8 @@ send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq
if (!dp->timer->running)
len = snd_seq_oss_timer_start(dp->timer);
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
- if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
- snd_seq_oss_readq_puts(dp->readq, mdev->seq_device,
- ev->data.ext.ptr, ev->data.ext.len);
+ snd_seq_oss_readq_sysex(dp->readq, mdev->seq_device, ev);
+ snd_midi_event_reset_decode(mdev->coder);
} else {
len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev);
if (len > 0)
@@ -639,16 +595,15 @@ send_midi_event(struct seq_oss_devinfo *dp, struct snd_seq_event *ev, struct seq
int
snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, struct snd_seq_event *ev)
{
- struct seq_oss_midi *mdev;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
- if ((mdev = get_mididev(dp, dev)) == NULL)
+ mdev = get_mididev(dp, dev);
+ if (!mdev)
return -ENODEV;
- if (snd_midi_event_encode_byte(mdev->coder, c, ev) > 0) {
+ if (snd_midi_event_encode_byte(mdev->coder, c, ev)) {
snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port);
- snd_use_lock_free(&mdev->use_lock);
return 0;
}
- snd_use_lock_free(&mdev->use_lock);
return -EINVAL;
}
@@ -658,20 +613,20 @@ snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, stru
int
snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info *inf)
{
- struct seq_oss_midi *mdev;
+ struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
- if ((mdev = get_mididev(dp, dev)) == NULL)
+ mdev = get_mididev(dp, dev);
+ if (!mdev)
return -ENXIO;
inf->device = dev;
inf->dev_type = 0; /* FIXME: ?? */
inf->capabilities = 0; /* FIXME: ?? */
- strlcpy(inf->name, mdev->name, sizeof(inf->name));
- snd_use_lock_free(&mdev->use_lock);
+ strscpy(inf->name, mdev->name, sizeof(inf->name));
return 0;
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* proc interface
*/
@@ -693,10 +648,11 @@ void
snd_seq_oss_midi_info_read(struct snd_info_buffer *buf)
{
int i;
- struct seq_oss_midi *mdev;
snd_iprintf(buf, "\nNumber of MIDI devices: %d\n", max_midi_devs);
for (i = 0; i < max_midi_devs; i++) {
+ struct seq_oss_midi *mdev __free(seq_oss_midi) = NULL;
+
snd_iprintf(buf, "\nmidi %d: ", i);
mdev = get_mdev(i);
if (mdev == NULL) {
@@ -708,7 +664,6 @@ snd_seq_oss_midi_info_read(struct snd_info_buffer *buf)
snd_iprintf(buf, " capability %s / opened %s\n",
capmode_str(mdev->flags),
capmode_str(mdev->opened));
- snd_use_lock_free(&mdev->use_lock);
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/oss/seq_oss_midi.h b/sound/core/seq/oss/seq_oss_midi.h
index 84eb866ba58e..bcc1683773df 100644
--- a/sound/core/seq/oss/seq_oss_midi.h
+++ b/sound/core/seq/oss/seq_oss_midi.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* OSS compatible sequencer driver
*
* midi device information
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SEQ_OSS_MIDI_H
diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c
index 73661c4ab82a..bbaf72e70b35 100644
--- a/sound/core/seq/oss/seq_oss_readq.c
+++ b/sound/core/seq/oss/seq_oss_readq.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* seq_oss_readq.c - MIDI input queue
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "seq_oss_readq.h"
@@ -47,13 +34,12 @@ snd_seq_oss_readq_new(struct seq_oss_devinfo *dp, int maxlen)
{
struct seq_oss_readq *q;
- if ((q = kzalloc(sizeof(*q), GFP_KERNEL)) == NULL) {
- snd_printk(KERN_ERR "can't malloc read queue\n");
+ q = kzalloc(sizeof(*q), GFP_KERNEL);
+ if (!q)
return NULL;
- }
- if ((q->q = kcalloc(maxlen, sizeof(union evrec), GFP_KERNEL)) == NULL) {
- snd_printk(KERN_ERR "can't malloc read queue buffer\n");
+ q->q = kcalloc(maxlen, sizeof(union evrec), GFP_KERNEL);
+ if (!q->q) {
kfree(q);
return NULL;
}
@@ -92,8 +78,7 @@ snd_seq_oss_readq_clear(struct seq_oss_readq *q)
q->head = q->tail = 0;
}
/* if someone sleeping, wake'em up */
- if (waitqueue_active(&q->midi_sleep))
- wake_up(&q->midi_sleep);
+ wake_up(&q->midi_sleep);
q->input_time = (unsigned long)-1;
}
@@ -120,29 +105,51 @@ snd_seq_oss_readq_puts(struct seq_oss_readq *q, int dev, unsigned char *data, in
}
/*
+ * put MIDI sysex bytes; the event buffer may be chained, thus it has
+ * to be expanded via snd_seq_dump_var_event().
+ */
+struct readq_sysex_ctx {
+ struct seq_oss_readq *readq;
+ int dev;
+};
+
+static int readq_dump_sysex(void *ptr, void *buf, int count)
+{
+ struct readq_sysex_ctx *ctx = ptr;
+
+ return snd_seq_oss_readq_puts(ctx->readq, ctx->dev, buf, count);
+}
+
+int snd_seq_oss_readq_sysex(struct seq_oss_readq *q, int dev,
+ struct snd_seq_event *ev)
+{
+ struct readq_sysex_ctx ctx = {
+ .readq = q,
+ .dev = dev
+ };
+
+ if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
+ return 0;
+ return snd_seq_dump_var_event(ev, readq_dump_sysex, &ctx);
+}
+
+/*
* copy an event to input queue:
* return zero if enqueued
*/
int
snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev)
{
- unsigned long flags;
-
- spin_lock_irqsave(&q->lock, flags);
- if (q->qlen >= q->maxlen - 1) {
- spin_unlock_irqrestore(&q->lock, flags);
+ guard(spinlock_irqsave)(&q->lock);
+ if (q->qlen >= q->maxlen - 1)
return -ENOMEM;
- }
memcpy(&q->q[q->tail], ev, sizeof(*ev));
q->tail = (q->tail + 1) % q->maxlen;
q->qlen++;
/* wake up sleeper */
- if (waitqueue_active(&q->midi_sleep))
- wake_up(&q->midi_sleep);
-
- spin_unlock_irqrestore(&q->lock, flags);
+ wake_up(&q->midi_sleep);
return 0;
}
@@ -223,7 +230,7 @@ snd_seq_oss_readq_put_timestamp(struct seq_oss_readq *q, unsigned long curt, int
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* proc interface
*/
@@ -234,4 +241,4 @@ snd_seq_oss_readq_info_read(struct seq_oss_readq *q, struct snd_info_buffer *buf
(waitqueue_active(&q->midi_sleep) ? "sleeping":"running"),
q->qlen, q->input_time);
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/oss/seq_oss_readq.h b/sound/core/seq/oss/seq_oss_readq.h
index f1463f1f449e..38d0c4682b29 100644
--- a/sound/core/seq/oss/seq_oss_readq.h
+++ b/sound/core/seq/oss/seq_oss_readq.h
@@ -1,22 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* OSS compatible sequencer driver
* read fifo queue
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SEQ_OSS_READQ_H
@@ -44,6 +31,8 @@ void snd_seq_oss_readq_delete(struct seq_oss_readq *q);
void snd_seq_oss_readq_clear(struct seq_oss_readq *readq);
unsigned int snd_seq_oss_readq_poll(struct seq_oss_readq *readq, struct file *file, poll_table *wait);
int snd_seq_oss_readq_puts(struct seq_oss_readq *readq, int dev, unsigned char *data, int len);
+int snd_seq_oss_readq_sysex(struct seq_oss_readq *q, int dev,
+ struct snd_seq_event *ev);
int snd_seq_oss_readq_put_event(struct seq_oss_readq *readq, union evrec *ev);
int snd_seq_oss_readq_put_timestamp(struct seq_oss_readq *readq, unsigned long curt, int seq_mode);
int snd_seq_oss_readq_pick(struct seq_oss_readq *q, union evrec *rec);
diff --git a/sound/core/seq/oss/seq_oss_rw.c b/sound/core/seq/oss/seq_oss_rw.c
index 6a7b6aceeca9..8a142fd54a19 100644
--- a/sound/core/seq/oss/seq_oss_rw.c
+++ b/sound/core/seq/oss/seq_oss_rw.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* read/write/select interface to device file
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "seq_oss_device.h"
@@ -145,7 +132,8 @@ snd_seq_oss_write(struct seq_oss_devinfo *dp, const char __user *buf, int count,
}
/* insert queue */
- if ((err = insert_queue(dp, &rec, opt)) < 0)
+ err = insert_queue(dp, &rec, opt);
+ if (err < 0)
break;
result += ev_size;
@@ -174,20 +162,17 @@ insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt)
memset(&event, 0, sizeof(event));
/* set dummy -- to be sure */
event.type = SNDRV_SEQ_EVENT_NOTEOFF;
- snd_seq_oss_fill_addr(dp, &event, dp->addr.port, dp->addr.client);
+ snd_seq_oss_fill_addr(dp, &event, dp->addr.client, dp->addr.port);
if (snd_seq_oss_process_event(dp, rec, &event))
return 0; /* invalid event - no need to insert queue */
event.time.tick = snd_seq_oss_timer_cur_tick(dp->timer);
- if (dp->timer->realtime || !dp->timer->running) {
+ if (dp->timer->realtime || !dp->timer->running)
snd_seq_oss_dispatch(dp, &event, 0, 0);
- } else {
- if (is_nonblock_mode(dp->file_mode))
- rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, 0, 0);
- else
- rc = snd_seq_kernel_client_enqueue_blocking(dp->cseq, &event, opt, 0, 0);
- }
+ else
+ rc = snd_seq_kernel_client_enqueue(dp->cseq, &event, opt,
+ !is_nonblock_mode(dp->file_mode));
return rc;
}
@@ -196,21 +181,21 @@ insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt)
* select / poll
*/
-unsigned int
+__poll_t
snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_table * wait)
{
- unsigned int mask = 0;
+ __poll_t mask = 0;
/* input */
if (dp->readq && is_read_mode(dp->file_mode)) {
if (snd_seq_oss_readq_poll(dp->readq, file, wait))
- mask |= POLLIN | POLLRDNORM;
+ mask |= EPOLLIN | EPOLLRDNORM;
}
/* output */
if (dp->writeq && is_write_mode(dp->file_mode)) {
if (snd_seq_kernel_client_write_poll(dp->cseq, file, wait))
- mask |= POLLOUT | POLLWRNORM;
+ mask |= EPOLLOUT | EPOLLWRNORM;
}
return mask;
}
diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c
index ee44ab9593c0..8c4e5913c7e6 100644
--- a/sound/core/seq/oss/seq_oss_synth.c
+++ b/sound/core/seq/oss/seq_oss_synth.c
@@ -1,30 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* synth device handlers
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "seq_oss_synth.h"
#include "seq_oss_midi.h"
#include "../seq_lock.h"
#include <linux/init.h>
+#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/nospec.h>
/*
* constants
@@ -37,13 +26,6 @@
* definition of synth info records
*/
-/* sysex buffer */
-struct seq_oss_synth_sysex {
- int len;
- int skip;
- unsigned char buf[MAX_SYSEX_BUFLEN];
-};
-
/* synth info */
struct seq_oss_synth {
int seq_device;
@@ -62,6 +44,7 @@ struct seq_oss_synth {
snd_use_lock_t use_lock;
};
+DEFINE_FREE(seq_oss_synth, struct seq_oss_synth *, if (!IS_ERR_OR_NULL(_T)) snd_use_lock_free(&(_T)->use_lock))
/*
* device table
@@ -69,11 +52,11 @@ struct seq_oss_synth {
static int max_synth_devs;
static struct seq_oss_synth *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS];
static struct seq_oss_synth midi_synth_dev = {
- -1, /* seq_device */
- SYNTH_TYPE_MIDI, /* synth_type */
- 0, /* synth_subtype */
- 16, /* nr_voices */
- "MIDI", /* name */
+ .seq_device = -1,
+ .synth_type = SYNTH_TYPE_MIDI,
+ .synth_subtype = 0,
+ .nr_voices = 16,
+ .name = "MIDI",
};
static DEFINE_SPINLOCK(register_lock);
@@ -97,17 +80,16 @@ 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);
- unsigned long flags;
- if ((rec = kzalloc(sizeof(*rec), GFP_KERNEL)) == NULL) {
- snd_printk(KERN_ERR "can't malloc synth info\n");
+ rec = kzalloc(sizeof(*rec), GFP_KERNEL);
+ if (!rec)
return -ENOMEM;
- }
rec->seq_device = -1;
rec->synth_type = reg->type;
rec->synth_subtype = reg->subtype;
@@ -118,27 +100,25 @@ snd_seq_oss_synth_register(struct snd_seq_device *dev)
snd_use_lock_init(&rec->use_lock);
/* copy and truncate the name of synth device */
- strlcpy(rec->name, dev->name, sizeof(rec->name));
+ strscpy(rec->name, dev->name, sizeof(rec->name));
/* registration */
- spin_lock_irqsave(&register_lock, flags);
- for (i = 0; i < max_synth_devs; i++) {
- if (synth_devs[i] == NULL)
- break;
- }
- if (i >= max_synth_devs) {
- if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) {
- spin_unlock_irqrestore(&register_lock, flags);
- snd_printk(KERN_ERR "no more synth slot\n");
- kfree(rec);
- return -ENOMEM;
+ scoped_guard(spinlock_irqsave, &register_lock) {
+ for (i = 0; i < max_synth_devs; i++) {
+ if (synth_devs[i] == NULL)
+ break;
+ }
+ if (i >= max_synth_devs) {
+ if (max_synth_devs >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS) {
+ pr_err("ALSA: seq_oss: no more synth slot\n");
+ kfree(rec);
+ return -ENOMEM;
+ }
+ max_synth_devs++;
}
- max_synth_devs++;
+ rec->seq_device = i;
+ synth_devs[i] = rec;
}
- rec->seq_device = i;
- synth_devs[i] = rec;
- debug_printk(("synth %s registered %d\n", rec->name, i));
- spin_unlock_irqrestore(&register_lock, flags);
dev->driver_data = rec;
#ifdef SNDRV_OSS_INFO_DEV_SYNTH
if (i < SNDRV_CARDS)
@@ -149,31 +129,30 @@ 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;
- spin_lock_irqsave(&register_lock, flags);
- for (index = 0; index < max_synth_devs; index++) {
- if (synth_devs[index] == rec)
- break;
- }
- if (index >= max_synth_devs) {
- spin_unlock_irqrestore(&register_lock, flags);
- snd_printk(KERN_ERR "can't unregister synth\n");
- return -EINVAL;
- }
- synth_devs[index] = NULL;
- if (index == max_synth_devs - 1) {
- for (index--; index >= 0; index--) {
- if (synth_devs[index])
+ scoped_guard(spinlock_irqsave, &register_lock) {
+ for (index = 0; index < max_synth_devs; index++) {
+ if (synth_devs[index] == rec)
break;
}
- max_synth_devs = index + 1;
+ if (index >= max_synth_devs) {
+ pr_err("ALSA: seq_oss: can't unregister synth\n");
+ return -EINVAL;
+ }
+ synth_devs[index] = NULL;
+ if (index == max_synth_devs - 1) {
+ for (index--; index >= 0; index--) {
+ if (synth_devs[index])
+ break;
+ }
+ max_synth_devs = index + 1;
+ }
}
- spin_unlock_irqrestore(&register_lock, flags);
#ifdef SNDRV_OSS_INFO_DEV_SYNTH
if (rec->seq_device < SNDRV_CARDS)
snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_SYNTH, rec->seq_device);
@@ -192,13 +171,11 @@ static struct seq_oss_synth *
get_sdev(int dev)
{
struct seq_oss_synth *rec;
- unsigned long flags;
- spin_lock_irqsave(&register_lock, flags);
+ guard(spinlock_irqsave)(&register_lock);
rec = synth_devs[dev];
if (rec)
snd_use_lock_use(&rec->use_lock);
- spin_unlock_irqrestore(&register_lock, flags);
return rec;
}
@@ -211,20 +188,18 @@ void
snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp)
{
int i;
- struct seq_oss_synth *rec;
struct seq_oss_synthinfo *info;
dp->max_synthdev = max_synth_devs;
dp->synth_opened = 0;
memset(dp->synths, 0, sizeof(dp->synths));
for (i = 0; i < dp->max_synthdev; i++) {
- rec = get_sdev(i);
+ struct seq_oss_synth *rec __free(seq_oss_synth) = get_sdev(i);
+
if (rec == NULL)
continue;
- if (rec->oper.open == NULL || rec->oper.close == NULL) {
- snd_use_lock_free(&rec->use_lock);
+ if (rec->oper.open == NULL || rec->oper.close == NULL)
continue;
- }
info = &dp->synths[i];
info->arg.app_index = dp->port;
info->arg.file_mode = dp->file_mode;
@@ -234,32 +209,25 @@ snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp)
else
info->arg.event_passing = SNDRV_SEQ_OSS_PASS_EVENTS;
info->opened = 0;
- if (!try_module_get(rec->oper.owner)) {
- snd_use_lock_free(&rec->use_lock);
+ if (!try_module_get(rec->oper.owner))
continue;
- }
if (rec->oper.open(&info->arg, rec->private_data) < 0) {
module_put(rec->oper.owner);
- snd_use_lock_free(&rec->use_lock);
continue;
}
info->nr_voices = rec->nr_voices;
if (info->nr_voices > 0) {
info->ch = kcalloc(info->nr_voices, sizeof(struct seq_oss_chinfo), GFP_KERNEL);
if (!info->ch) {
- snd_printk(KERN_ERR "Cannot malloc\n");
rec->oper.close(&info->arg);
module_put(rec->oper.owner);
- snd_use_lock_free(&rec->use_lock);
continue;
}
reset_channels(info);
}
- debug_printk(("synth %d assigned\n", i));
info->opened++;
rec->opened++;
dp->synth_opened++;
- snd_use_lock_free(&rec->use_lock);
}
}
@@ -306,10 +274,9 @@ void
snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)
{
int i;
- struct seq_oss_synth *rec;
struct seq_oss_synthinfo *info;
- if (snd_BUG_ON(dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS))
+ if (snd_BUG_ON(dp->max_synthdev > SNDRV_SEQ_OSS_MAX_SYNTH_DEVS))
return;
for (i = 0; i < dp->max_synthdev; i++) {
info = &dp->synths[i];
@@ -321,19 +288,17 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)
midi_synth_dev.opened--;
}
} else {
- rec = get_sdev(i);
+ struct seq_oss_synth *rec __free(seq_oss_synth) =
+ get_sdev(i);
+
if (rec == NULL)
continue;
if (rec->opened > 0) {
- debug_printk(("synth %d closed\n", i));
rec->oper.close(&info->arg);
module_put(rec->oper.owner);
rec->opened = 0;
}
- snd_use_lock_free(&rec->use_lock);
}
- kfree(info->sysex);
- info->sysex = NULL;
kfree(info->ch);
info->ch = NULL;
}
@@ -341,17 +306,13 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)
dp->max_synthdev = 0;
}
-/*
- * check if the specified device is MIDI mapped device
- */
-static int
-is_midi_dev(struct seq_oss_devinfo *dp, int dev)
+static struct seq_oss_synthinfo *
+get_synthinfo_nospec(struct seq_oss_devinfo *dp, int dev)
{
if (dev < 0 || dev >= dp->max_synthdev)
- return 0;
- if (dp->synths[dev].is_midi)
- return 1;
- return 0;
+ return NULL;
+ dev = array_index_nospec(dev, SNDRV_SEQ_OSS_MAX_SYNTH_DEVS);
+ return &dp->synths[dev];
}
/*
@@ -361,14 +322,20 @@ static struct seq_oss_synth *
get_synthdev(struct seq_oss_devinfo *dp, int dev)
{
struct seq_oss_synth *rec;
- if (dev < 0 || dev >= dp->max_synthdev)
- return NULL;
- if (! dp->synths[dev].opened)
+ struct seq_oss_synthinfo *info = get_synthinfo_nospec(dp, dev);
+
+ if (!info)
return NULL;
- if (dp->synths[dev].is_midi)
- return &midi_synth_dev;
- if ((rec = get_sdev(dev)) == NULL)
+ if (!info->opened)
return NULL;
+ if (info->is_midi) {
+ rec = &midi_synth_dev;
+ snd_use_lock_use(&rec->use_lock);
+ } else {
+ rec = get_sdev(dev);
+ if (!rec)
+ return NULL;
+ }
if (! rec->opened) {
snd_use_lock_free(&rec->use_lock);
return NULL;
@@ -401,16 +368,12 @@ reset_channels(struct seq_oss_synthinfo *info)
void
snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev)
{
- struct seq_oss_synth *rec;
+ struct seq_oss_synth *rec __free(seq_oss_synth) = NULL;
struct seq_oss_synthinfo *info;
- if (snd_BUG_ON(dev < 0 || dev >= dp->max_synthdev))
+ info = get_synthinfo_nospec(dp, dev);
+ if (!info || !info->opened)
return;
- info = &dp->synths[dev];
- if (! info->opened)
- return;
- if (info->sysex)
- info->sysex->len = 0; /* reset sysex */
reset_channels(info);
if (info->is_midi) {
if (midi_synth_dev.opened <= 0)
@@ -422,8 +385,6 @@ snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev)
dp->file_mode) < 0) {
midi_synth_dev.opened--;
info->opened = 0;
- kfree(info->sysex);
- info->sysex = NULL;
kfree(info->ch);
info->ch = NULL;
}
@@ -443,7 +404,6 @@ snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev)
ev.type = SNDRV_SEQ_EVENT_RESET;
snd_seq_oss_dispatch(dp, &ev, 0, 0);
}
- snd_use_lock_free(&rec->use_lock);
}
@@ -455,98 +415,62 @@ int
snd_seq_oss_synth_load_patch(struct seq_oss_devinfo *dp, int dev, int fmt,
const char __user *buf, int p, int c)
{
- struct seq_oss_synth *rec;
- int rc;
+ struct seq_oss_synth *rec __free(seq_oss_synth) = NULL;
+ struct seq_oss_synthinfo *info;
- if (dev < 0 || dev >= dp->max_synthdev)
+ info = get_synthinfo_nospec(dp, dev);
+ if (!info)
return -ENXIO;
- if (is_midi_dev(dp, dev))
+ if (info->is_midi)
return 0;
- if ((rec = get_synthdev(dp, dev)) == NULL)
+ rec = get_synthdev(dp, dev);
+ if (!rec)
return -ENXIO;
if (rec->oper.load_patch == NULL)
- rc = -ENXIO;
+ return -ENXIO;
else
- rc = rec->oper.load_patch(&dp->synths[dev].arg, fmt, buf, p, c);
- snd_use_lock_free(&rec->use_lock);
- return rc;
+ return rec->oper.load_patch(&info->arg, fmt, buf, p, c);
}
/*
- * check if the device is valid synth device
+ * check if the device is valid synth device and return the synth info
*/
-int
-snd_seq_oss_synth_is_valid(struct seq_oss_devinfo *dp, int dev)
+struct seq_oss_synthinfo *
+snd_seq_oss_synth_info(struct seq_oss_devinfo *dp, int dev)
{
- struct seq_oss_synth *rec;
+ struct seq_oss_synth *rec __free(seq_oss_synth) = NULL;
+
rec = get_synthdev(dp, dev);
- if (rec) {
- snd_use_lock_free(&rec->use_lock);
- return 1;
- }
- return 0;
+ if (rec)
+ return get_synthinfo_nospec(dp, dev);
+ return NULL;
}
/*
* receive OSS 6 byte sysex packet:
- * the full sysex message will be sent if it reaches to the end of data
- * (0xff).
+ * the event is filled and prepared for sending immediately
+ * (i.e. sysex messages are fragmented)
*/
int
snd_seq_oss_synth_sysex(struct seq_oss_devinfo *dp, int dev, unsigned char *buf, struct snd_seq_event *ev)
{
- int i, send;
- unsigned char *dest;
- struct seq_oss_synth_sysex *sysex;
+ unsigned char *p;
+ int len = 6;
- if (! snd_seq_oss_synth_is_valid(dp, dev))
- return -ENXIO;
-
- sysex = dp->synths[dev].sysex;
- if (sysex == NULL) {
- sysex = kzalloc(sizeof(*sysex), GFP_KERNEL);
- if (sysex == NULL)
- return -ENOMEM;
- dp->synths[dev].sysex = sysex;
- }
+ p = memchr(buf, 0xff, 6);
+ if (p)
+ len = p - buf + 1;
- send = 0;
- dest = sysex->buf + sysex->len;
- /* copy 6 byte packet to the buffer */
- for (i = 0; i < 6; i++) {
- if (buf[i] == 0xff) {
- send = 1;
- break;
- }
- dest[i] = buf[i];
- sysex->len++;
- if (sysex->len >= MAX_SYSEX_BUFLEN) {
- sysex->len = 0;
- sysex->skip = 1;
- break;
- }
- }
-
- if (sysex->len && send) {
- if (sysex->skip) {
- sysex->skip = 0;
- sysex->len = 0;
- return -EINVAL; /* skip */
- }
- /* copy the data to event record and send it */
- ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
- if (snd_seq_oss_synth_addr(dp, dev, ev))
- return -EINVAL;
- ev->data.ext.len = sysex->len;
- ev->data.ext.ptr = sysex->buf;
- sysex->len = 0;
- return 0;
- }
-
- return -EINVAL; /* skip */
+ /* copy the data to event record and send it */
+ if (snd_seq_oss_synth_addr(dp, dev, ev))
+ return -EINVAL;
+ ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
+ ev->data.ext.len = len;
+ ev->data.ext.ptr = buf;
+ return 0;
}
/*
@@ -555,10 +479,12 @@ snd_seq_oss_synth_sysex(struct seq_oss_devinfo *dp, int dev, unsigned char *buf,
int
snd_seq_oss_synth_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_event *ev)
{
- if (! snd_seq_oss_synth_is_valid(dp, dev))
+ struct seq_oss_synthinfo *info = snd_seq_oss_synth_info(dp, dev);
+
+ if (!info)
return -EINVAL;
- snd_seq_oss_fill_addr(dp, ev, dp->synths[dev].arg.addr.client,
- dp->synths[dev].arg.addr.port);
+ snd_seq_oss_fill_addr(dp, ev, info->arg.addr.client,
+ info->arg.addr.port);
return 0;
}
@@ -569,19 +495,19 @@ snd_seq_oss_synth_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_event
int
snd_seq_oss_synth_ioctl(struct seq_oss_devinfo *dp, int dev, unsigned int cmd, unsigned long addr)
{
- struct seq_oss_synth *rec;
- int rc;
+ struct seq_oss_synth *rec __free(seq_oss_synth) = NULL;
+ struct seq_oss_synthinfo *info;
- if (is_midi_dev(dp, dev))
+ info = get_synthinfo_nospec(dp, dev);
+ if (!info || info->is_midi)
return -ENXIO;
- if ((rec = get_synthdev(dp, dev)) == NULL)
+ rec = get_synthdev(dp, dev);
+ if (!rec)
return -ENXIO;
if (rec->oper.ioctl == NULL)
- rc = -ENXIO;
+ return -ENXIO;
else
- rc = rec->oper.ioctl(&dp->synths[dev].arg, cmd, addr);
- snd_use_lock_free(&rec->use_lock);
- return rc;
+ return rec->oper.ioctl(&info->arg, cmd, addr);
}
@@ -591,7 +517,10 @@ snd_seq_oss_synth_ioctl(struct seq_oss_devinfo *dp, int dev, unsigned int cmd, u
int
snd_seq_oss_synth_raw_event(struct seq_oss_devinfo *dp, int dev, unsigned char *data, struct snd_seq_event *ev)
{
- if (! snd_seq_oss_synth_is_valid(dp, dev) || is_midi_dev(dp, dev))
+ struct seq_oss_synthinfo *info;
+
+ info = snd_seq_oss_synth_info(dp, dev);
+ if (!info || info->is_midi)
return -ENXIO;
ev->type = SNDRV_SEQ_EVENT_OSS;
memcpy(ev->data.raw8.d, data, 8);
@@ -605,34 +534,37 @@ snd_seq_oss_synth_raw_event(struct seq_oss_devinfo *dp, int dev, unsigned char *
int
snd_seq_oss_synth_make_info(struct seq_oss_devinfo *dp, int dev, struct synth_info *inf)
{
- struct seq_oss_synth *rec;
+ struct seq_oss_synthinfo *info = get_synthinfo_nospec(dp, dev);
- if (dev < 0 || dev >= dp->max_synthdev)
+ if (!info)
return -ENXIO;
- if (dp->synths[dev].is_midi) {
+ if (info->is_midi) {
struct midi_info minf;
- snd_seq_oss_midi_make_info(dp, dp->synths[dev].midi_mapped, &minf);
+ if (snd_seq_oss_midi_make_info(dp, info->midi_mapped, &minf))
+ return -ENXIO;
inf->synth_type = SYNTH_TYPE_MIDI;
inf->synth_subtype = 0;
inf->nr_voices = 16;
inf->device = dev;
- strlcpy(inf->name, minf.name, sizeof(inf->name));
+ strscpy(inf->name, minf.name, sizeof(inf->name));
} else {
- if ((rec = get_synthdev(dp, dev)) == NULL)
+ struct seq_oss_synth *rec __free(seq_oss_synth) =
+ get_synthdev(dp, dev);
+
+ if (!rec)
return -ENXIO;
inf->synth_type = rec->synth_type;
inf->synth_subtype = rec->synth_subtype;
inf->nr_voices = rec->nr_voices;
inf->device = dev;
- strlcpy(inf->name, rec->name, sizeof(inf->name));
- snd_use_lock_free(&rec->use_lock);
+ strscpy(inf->name, rec->name, sizeof(inf->name));
}
return 0;
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* proc interface
*/
@@ -640,10 +572,11 @@ void
snd_seq_oss_synth_info_read(struct snd_info_buffer *buf)
{
int i;
- struct seq_oss_synth *rec;
snd_iprintf(buf, "\nNumber of synth devices: %d\n", max_synth_devs);
for (i = 0; i < max_synth_devs; i++) {
+ struct seq_oss_synth *rec __free(seq_oss_synth) = NULL;
+
snd_iprintf(buf, "\nsynth %d: ", i);
rec = get_sdev(i);
if (rec == NULL) {
@@ -655,9 +588,8 @@ snd_seq_oss_synth_info_read(struct snd_info_buffer *buf)
rec->synth_type, rec->synth_subtype,
rec->nr_voices);
snd_iprintf(buf, " capabilities : ioctl %s / load_patch %s\n",
- enabled_str((long)rec->oper.ioctl),
- enabled_str((long)rec->oper.load_patch));
- snd_use_lock_free(&rec->use_lock);
+ str_enabled_disabled((long)rec->oper.ioctl),
+ str_enabled_disabled((long)rec->oper.load_patch));
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/oss/seq_oss_synth.h b/sound/core/seq/oss/seq_oss_synth.h
index dbdfcbb80eaa..ffc40d8a7ef1 100644
--- a/sound/core/seq/oss/seq_oss_synth.h
+++ b/sound/core/seq/oss/seq_oss_synth.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* OSS compatible sequencer driver
*
* synth device information
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SEQ_OSS_SYNTH_H
@@ -28,8 +15,8 @@
#include <sound/seq_device.h>
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);
@@ -37,7 +24,8 @@ void snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp);
void snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev);
int snd_seq_oss_synth_load_patch(struct seq_oss_devinfo *dp, int dev, int fmt,
const char __user *buf, int p, int c);
-int snd_seq_oss_synth_is_valid(struct seq_oss_devinfo *dp, int dev);
+struct seq_oss_synthinfo *snd_seq_oss_synth_info(struct seq_oss_devinfo *dp,
+ int dev);
int snd_seq_oss_synth_sysex(struct seq_oss_devinfo *dp, int dev, unsigned char *buf,
struct snd_seq_event *ev);
int snd_seq_oss_synth_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_event *ev);
diff --git a/sound/core/seq/oss/seq_oss_timer.c b/sound/core/seq/oss/seq_oss_timer.c
index ab59cbfbcaf2..f9f57232a83f 100644
--- a/sound/core/seq/oss/seq_oss_timer.c
+++ b/sound/core/seq/oss/seq_oss_timer.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* Timer control routines
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "seq_oss_timer.h"
@@ -92,7 +79,7 @@ snd_seq_oss_process_timer_event(struct seq_oss_timer *rec, union evrec *ev)
case TMR_WAIT_REL:
parm += rec->cur_tick;
rec->realtime = 0;
- /* continue to next */
+ fallthrough;
case TMR_WAIT_ABS:
if (parm == 0) {
rec->realtime = 1;
@@ -233,7 +220,6 @@ snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __use
int value;
if (cmd == SNDCTL_SEQ_CTRLRATE) {
- debug_printk(("ctrl rate\n"));
/* if *arg == 0, just return the current rate */
if (get_user(value, arg))
return -EFAULT;
@@ -248,21 +234,16 @@ snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __use
switch (cmd) {
case SNDCTL_TMR_START:
- debug_printk(("timer start\n"));
return snd_seq_oss_timer_start(timer);
case SNDCTL_TMR_STOP:
- debug_printk(("timer stop\n"));
return snd_seq_oss_timer_stop(timer);
case SNDCTL_TMR_CONTINUE:
- debug_printk(("timer continue\n"));
return snd_seq_oss_timer_continue(timer);
case SNDCTL_TMR_TEMPO:
- debug_printk(("timer tempo\n"));
if (get_user(value, arg))
return -EFAULT;
return snd_seq_oss_timer_tempo(timer, value);
case SNDCTL_TMR_TIMEBASE:
- debug_printk(("timer timebase\n"));
if (get_user(value, arg))
return -EFAULT;
if (value < MIN_OSS_TIMEBASE)
@@ -276,7 +257,6 @@ snd_seq_oss_timer_ioctl(struct seq_oss_timer *timer, unsigned int cmd, int __use
case SNDCTL_TMR_METRONOME:
case SNDCTL_TMR_SELECT:
case SNDCTL_TMR_SOURCE:
- debug_printk(("timer XXX\n"));
/* not supported */
return 0;
}
diff --git a/sound/core/seq/oss/seq_oss_timer.h b/sound/core/seq/oss/seq_oss_timer.h
index b995bd68ad1f..dee190b4ec6b 100644
--- a/sound/core/seq/oss/seq_oss_timer.h
+++ b/sound/core/seq/oss/seq_oss_timer.h
@@ -1,22 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* OSS compatible sequencer driver
* timer handling routines
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SEQ_OSS_TIMER_H
@@ -57,14 +44,4 @@ snd_seq_oss_timer_cur_tick(struct seq_oss_timer *timer)
return timer->cur_tick;
}
-
-/*
- * is realtime event?
- */
-static inline int
-snd_seq_oss_timer_is_realtime(struct seq_oss_timer *timer)
-{
- return timer->realtime;
-}
-
#endif
diff --git a/sound/core/seq/oss/seq_oss_writeq.c b/sound/core/seq/oss/seq_oss_writeq.c
index d50338bbc21f..a93ff8315b8e 100644
--- a/sound/core/seq/oss/seq_oss_writeq.c
+++ b/sound/core/seq/oss/seq_oss_writeq.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OSS compatible sequencer driver
*
* seq_oss_writeq.c - write queue and sync
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "seq_oss_writeq.h"
@@ -28,6 +15,7 @@
#include "../seq_clientmgr.h"
#include <linux/wait.h>
#include <linux/slab.h>
+#include <linux/sched/signal.h>
/*
@@ -39,7 +27,8 @@ snd_seq_oss_writeq_new(struct seq_oss_devinfo *dp, int maxlen)
struct seq_oss_writeq *q;
struct snd_seq_client_pool pool;
- if ((q = kzalloc(sizeof(*q), GFP_KERNEL)) == NULL)
+ q = kzalloc(sizeof(*q), GFP_KERNEL);
+ if (!q)
return NULL;
q->dp = dp;
q->maxlen = maxlen;
@@ -115,7 +104,7 @@ snd_seq_oss_writeq_sync(struct seq_oss_writeq *q)
rec->t.code = SEQ_SYNCTIMER;
rec->t.time = time;
q->sync_event_put = 1;
- snd_seq_kernel_client_enqueue_blocking(dp->cseq, &ev, NULL, 0, 0);
+ snd_seq_kernel_client_enqueue(dp->cseq, &ev, NULL, true);
}
wait_event_interruptible_timeout(q->sync_sleep, ! q->sync_event_put, HZ);
@@ -133,15 +122,10 @@ snd_seq_oss_writeq_sync(struct seq_oss_writeq *q)
void
snd_seq_oss_writeq_wakeup(struct seq_oss_writeq *q, abstime_t time)
{
- unsigned long flags;
-
- spin_lock_irqsave(&q->sync_lock, flags);
+ guard(spinlock_irqsave)(&q->sync_lock);
q->sync_time = time;
q->sync_event_put = 0;
- if (waitqueue_active(&q->sync_sleep)) {
- wake_up(&q->sync_sleep);
- }
- spin_unlock_irqrestore(&q->sync_lock, flags);
+ wake_up(&q->sync_sleep);
}
diff --git a/sound/core/seq/oss/seq_oss_writeq.h b/sound/core/seq/oss/seq_oss_writeq.h
index c469d2967566..490d27a7b29d 100644
--- a/sound/core/seq/oss/seq_oss_writeq.h
+++ b/sound/core/seq/oss/seq_oss_writeq.h
@@ -1,22 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* OSS compatible sequencer driver
* write priority queue
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SEQ_OSS_WRITEQ_H
diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c
index bf09a5ad1865..00f7342ee839 100644
--- a/sound/core/seq/seq.c
+++ b/sound/core/seq/seq.c
@@ -1,26 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer main module
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/device.h>
#include <sound/core.h>
#include <sound/initval.h>
@@ -32,6 +18,7 @@
#include "seq_timer.h"
#include "seq_system.h"
#include "seq_info.h"
+#include <sound/minors.h>
#include <sound/seq_device.h>
#if defined(CONFIG_SND_SEQ_DUMMY_MODULE)
@@ -45,8 +32,6 @@ int seq_default_timer_card = -1;
int seq_default_timer_device =
#ifdef CONFIG_SND_SEQ_HRTIMER_DEFAULT
SNDRV_TIMER_GLOBAL_HRTIMER
-#elif defined(CONFIG_SND_SEQ_RTCTIMER_DEFAULT)
- SNDRV_TIMER_GLOBAL_RTC
#else
SNDRV_TIMER_GLOBAL_SYSTEM
#endif
@@ -73,6 +58,9 @@ MODULE_PARM_DESC(seq_default_timer_subdevice, "The default timer subdevice numbe
module_param(seq_default_timer_resolution, int, 0644);
MODULE_PARM_DESC(seq_default_timer_resolution, "The default timer resolution in Hz.");
+MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_SEQUENCER);
+MODULE_ALIAS("devname:snd/seq");
+
/*
* INIT PART
*/
@@ -81,32 +69,33 @@ static int __init alsa_seq_init(void)
{
int err;
- snd_seq_autoload_lock();
- if ((err = client_init_data()) < 0)
- goto error;
-
- /* init memory, room for selected events */
- if ((err = snd_sequencer_memory_init()) < 0)
- goto error;
-
- /* init event queues */
- if ((err = snd_seq_queues_init()) < 0)
+ err = client_init_data();
+ if (err < 0)
goto error;
/* register sequencer device */
- if ((err = snd_sequencer_device_init()) < 0)
+ err = snd_sequencer_device_init();
+ if (err < 0)
goto error;
/* register proc interface */
- if ((err = snd_seq_info_init()) < 0)
- goto error;
+ err = snd_seq_info_init();
+ if (err < 0)
+ goto error_device;
/* register our internal client */
- if ((err = snd_seq_system_client_init()) < 0)
- goto error;
+ err = snd_seq_system_client_init();
+ if (err < 0)
+ goto error_info;
+
+ snd_seq_autoload_init();
+ return 0;
+ error_info:
+ snd_seq_info_done();
+ error_device:
+ snd_sequencer_device_done();
error:
- snd_seq_autoload_unlock();
return err;
}
@@ -124,8 +113,7 @@ static void __exit alsa_seq_exit(void)
/* unregister sequencer device */
snd_sequencer_device_done();
- /* release event memory */
- snd_sequencer_memory_done();
+ snd_seq_autoload_exit();
}
module_init(alsa_seq_init)
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 99a485f13648..f9a6e497f997 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -1,39 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer Client Manager
* Copyright (c) 1998-2001 by Frank van de Pol <fvdpol@coil.demon.nl>
* Jaroslav Kysela <perex@perex.cz>
* Takashi Iwai <tiwai@suse.de>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <linux/kmod.h>
#include <sound/seq_kernel.h>
+#include <sound/ump.h>
#include "seq_clientmgr.h"
#include "seq_memory.h"
#include "seq_queue.h"
#include "seq_timer.h"
#include "seq_info.h"
#include "seq_system.h"
+#include "seq_ump_convert.h"
#include <sound/seq_device.h>
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
@@ -82,22 +70,11 @@ static int bounce_error_event(struct snd_seq_client *client,
int err, int atomic, int hop);
static int snd_seq_deliver_single_event(struct snd_seq_client *client,
struct snd_seq_event *event,
- int filter, int atomic, int hop);
+ int atomic, int hop);
-/*
- */
-
-static inline mm_segment_t snd_enter_user(void)
-{
- mm_segment_t fs = get_fs();
- set_fs(get_ds());
- return fs;
-}
-
-static inline void snd_leave_user(mm_segment_t fs)
-{
- set_fs(fs);
-}
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+static void free_ump_info(struct snd_seq_client *client);
+#endif
/*
*/
@@ -122,41 +99,38 @@ static inline int snd_seq_write_pool_allocated(struct snd_seq_client *client)
static struct snd_seq_client *clientptr(int clientid)
{
if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) {
- snd_printd("Seq: oops. Trying to get pointer to client %d\n",
+ pr_debug("ALSA: seq: oops. Trying to get pointer to client %d\n",
clientid);
return NULL;
}
return clienttab[clientid];
}
-struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
+static struct snd_seq_client *client_use_ptr(int clientid, bool load_module)
{
- unsigned long flags;
struct snd_seq_client *client;
if (clientid < 0 || clientid >= SNDRV_SEQ_MAX_CLIENTS) {
- snd_printd("Seq: oops. Trying to get pointer to client %d\n",
+ pr_debug("ALSA: seq: oops. Trying to get pointer to client %d\n",
clientid);
return NULL;
}
- spin_lock_irqsave(&clients_lock, flags);
- client = clientptr(clientid);
- if (client)
- goto __lock;
- if (clienttablock[clientid]) {
- spin_unlock_irqrestore(&clients_lock, flags);
- return NULL;
+ scoped_guard(spinlock_irqsave, &clients_lock) {
+ client = clientptr(clientid);
+ if (client)
+ return snd_seq_client_ref(client);
+ if (clienttablock[clientid])
+ return NULL;
}
- spin_unlock_irqrestore(&clients_lock, flags);
#ifdef CONFIG_MODULES
- if (!in_interrupt()) {
- static char client_requested[SNDRV_SEQ_GLOBAL_CLIENTS];
- static char card_requested[SNDRV_CARDS];
+ if (load_module) {
+ static DECLARE_BITMAP(client_requested, SNDRV_SEQ_GLOBAL_CLIENTS);
+ static DECLARE_BITMAP(card_requested, SNDRV_CARDS);
+
if (clientid < SNDRV_SEQ_GLOBAL_CLIENTS) {
int idx;
- if (!client_requested[clientid]) {
- client_requested[clientid] = 1;
+ if (!test_and_set_bit(clientid, client_requested)) {
for (idx = 0; idx < 15; idx++) {
if (seq_client_load[idx] < 0)
break;
@@ -171,26 +145,33 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
int card = (clientid - SNDRV_SEQ_GLOBAL_CLIENTS) /
SNDRV_SEQ_CLIENTS_PER_CARD;
if (card < snd_ecards_limit) {
- if (! card_requested[card]) {
- card_requested[card] = 1;
+ if (!test_and_set_bit(card, card_requested))
snd_request_card(card);
- }
snd_seq_device_load_drivers();
}
}
- spin_lock_irqsave(&clients_lock, flags);
- client = clientptr(clientid);
- if (client)
- goto __lock;
- spin_unlock_irqrestore(&clients_lock, flags);
+ scoped_guard(spinlock_irqsave, &clients_lock) {
+ client = clientptr(clientid);
+ if (client)
+ return snd_seq_client_ref(client);
+ }
}
#endif
return NULL;
+}
- __lock:
- snd_use_lock_use(&client->use_lock);
- spin_unlock_irqrestore(&clients_lock, flags);
- return client;
+/* get snd_seq_client object for the given id quickly */
+struct snd_seq_client *snd_seq_client_use_ptr(int clientid)
+{
+ return client_use_ptr(clientid, false);
+}
+
+/* get snd_seq_client object for the given id;
+ * if not found, retry after loading the modules
+ */
+static struct snd_seq_client *client_load_and_use_ptr(int clientid)
+{
+ return client_use_ptr(clientid, IS_ENABLED(CONFIG_MODULES));
}
static void usage_alloc(struct snd_seq_usage *res, int num)
@@ -217,7 +198,6 @@ int __init client_init_data(void)
static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
{
- unsigned long flags;
int c;
struct snd_seq_client *client;
@@ -235,27 +215,28 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
rwlock_init(&client->ports_lock);
mutex_init(&client->ports_mutex);
INIT_LIST_HEAD(&client->ports_list_head);
+ mutex_init(&client->ioctl_mutex);
+ client->ump_endpoint_port = -1;
/* find free slot in the client table */
- spin_lock_irqsave(&clients_lock, flags);
- if (client_index < 0) {
- for (c = SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN;
- c < SNDRV_SEQ_MAX_CLIENTS;
- c++) {
- if (clienttab[c] || clienttablock[c])
- continue;
- clienttab[client->number = c] = client;
- spin_unlock_irqrestore(&clients_lock, flags);
- return client;
- }
- } else {
- if (clienttab[client_index] == NULL && !clienttablock[client_index]) {
- clienttab[client->number = client_index] = client;
- spin_unlock_irqrestore(&clients_lock, flags);
- return client;
+ scoped_guard(spinlock_irq, &clients_lock) {
+ if (client_index < 0) {
+ for (c = SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN;
+ c < SNDRV_SEQ_MAX_CLIENTS;
+ c++) {
+ if (clienttab[c] || clienttablock[c])
+ continue;
+ clienttab[client->number = c] = client;
+ return client;
+ }
+ } else {
+ if (clienttab[client_index] == NULL && !clienttablock[client_index]) {
+ clienttab[client->number = client_index] = client;
+ return client;
+ }
}
}
- spin_unlock_irqrestore(&clients_lock, flags);
+
snd_seq_pool_delete(&client->pool);
kfree(client);
return NULL; /* no free slot found or busy, return failure code */
@@ -264,46 +245,43 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
static int seq_free_client1(struct snd_seq_client *client)
{
- unsigned long flags;
-
if (!client)
return 0;
+ scoped_guard(spinlock_irq, &clients_lock) {
+ clienttablock[client->number] = 1;
+ clienttab[client->number] = NULL;
+ }
snd_seq_delete_all_ports(client);
snd_seq_queue_client_leave(client->number);
- spin_lock_irqsave(&clients_lock, flags);
- clienttablock[client->number] = 1;
- clienttab[client->number] = NULL;
- spin_unlock_irqrestore(&clients_lock, flags);
snd_use_lock_sync(&client->use_lock);
- snd_seq_queue_client_termination(client->number);
if (client->pool)
snd_seq_pool_delete(&client->pool);
- spin_lock_irqsave(&clients_lock, flags);
- clienttablock[client->number] = 0;
- spin_unlock_irqrestore(&clients_lock, flags);
+ scoped_guard(spinlock_irq, &clients_lock) {
+ clienttablock[client->number] = 0;
+ }
return 0;
}
static void seq_free_client(struct snd_seq_client * client)
{
- mutex_lock(&register_mutex);
- switch (client->type) {
- case NO_CLIENT:
- snd_printk(KERN_WARNING "Seq: Trying to free unused client %d\n",
- client->number);
- break;
- case USER_CLIENT:
- case KERNEL_CLIENT:
- seq_free_client1(client);
- usage_free(&client_usage, 1);
- break;
+ scoped_guard(mutex, &register_mutex) {
+ switch (client->type) {
+ case NO_CLIENT:
+ pr_warn("ALSA: seq: Trying to free unused client %d\n",
+ client->number);
+ break;
+ case USER_CLIENT:
+ case KERNEL_CLIENT:
+ seq_free_client1(client);
+ usage_free(&client_usage, 1);
+ break;
- default:
- snd_printk(KERN_ERR "Seq: Trying to free client %d with undefined type = %d\n",
- client->number, client->type);
+ default:
+ pr_err("ALSA: seq: Trying to free client %d with undefined type = %d\n",
+ client->number, client->type);
+ }
}
- mutex_unlock(&register_mutex);
snd_seq_system_client_ev_client_exit(client->number);
}
@@ -320,42 +298,38 @@ static int snd_seq_open(struct inode *inode, struct file *file)
struct snd_seq_user_client *user;
int err;
- err = nonseekable_open(inode, file);
+ err = stream_open(inode, file);
if (err < 0)
return err;
- if (mutex_lock_interruptible(&register_mutex))
- return -ERESTARTSYS;
- client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS);
- if (client == NULL) {
- mutex_unlock(&register_mutex);
- return -ENOMEM; /* failure code */
- }
-
- mode = snd_seq_file_flags(file);
- if (mode & SNDRV_SEQ_LFLG_INPUT)
- client->accept_input = 1;
- if (mode & SNDRV_SEQ_LFLG_OUTPUT)
- client->accept_output = 1;
-
- user = &client->data.user;
- user->fifo = NULL;
- user->fifo_pool_size = 0;
-
- if (mode & SNDRV_SEQ_LFLG_INPUT) {
- user->fifo_pool_size = SNDRV_SEQ_DEFAULT_CLIENT_EVENTS;
- user->fifo = snd_seq_fifo_new(user->fifo_pool_size);
- if (user->fifo == NULL) {
- seq_free_client1(client);
- kfree(client);
- mutex_unlock(&register_mutex);
- return -ENOMEM;
+ scoped_guard(mutex, &register_mutex) {
+ client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS);
+ if (!client)
+ return -ENOMEM; /* failure code */
+
+ mode = snd_seq_file_flags(file);
+ if (mode & SNDRV_SEQ_LFLG_INPUT)
+ client->accept_input = 1;
+ if (mode & SNDRV_SEQ_LFLG_OUTPUT)
+ client->accept_output = 1;
+
+ user = &client->data.user;
+ user->fifo = NULL;
+ user->fifo_pool_size = 0;
+
+ if (mode & SNDRV_SEQ_LFLG_INPUT) {
+ user->fifo_pool_size = SNDRV_SEQ_DEFAULT_CLIENT_EVENTS;
+ user->fifo = snd_seq_fifo_new(user->fifo_pool_size);
+ if (user->fifo == NULL) {
+ seq_free_client1(client);
+ kfree(client);
+ return -ENOMEM;
+ }
}
- }
- usage_alloc(&client_usage, 1);
- client->type = USER_CLIENT;
- mutex_unlock(&register_mutex);
+ usage_alloc(&client_usage, 1);
+ client->type = USER_CLIENT;
+ }
c = client->number;
file->private_data = client;
@@ -363,6 +337,7 @@ static int snd_seq_open(struct inode *inode, struct file *file)
/* fill client data */
user->file = file;
sprintf(client->name, "Client-%d", c);
+ client->data.user.owner = get_pid(task_pid(current));
/* make others aware this new client */
snd_seq_system_client_ev_client_start(c);
@@ -379,12 +354,25 @@ static int snd_seq_release(struct inode *inode, struct file *file)
seq_free_client(client);
if (client->data.user.fifo)
snd_seq_fifo_delete(&client->data.user.fifo);
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ free_ump_info(client);
+#endif
+ put_pid(client->data.user.owner);
kfree(client);
}
return 0;
}
+static bool event_is_compatible(const struct snd_seq_client *client,
+ const struct snd_seq_event *ev)
+{
+ if (snd_seq_ev_is_ump(ev) && !client->midi_version)
+ return false;
+ if (snd_seq_ev_is_ump(ev) && snd_seq_ev_is_variable(ev))
+ return false;
+ return true;
+}
/* handle client read() */
/* possible error values:
@@ -398,6 +386,7 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
{
struct snd_seq_client *client = file->private_data;
struct snd_seq_fifo *fifo;
+ size_t aligned_size;
int err;
long result = 0;
struct snd_seq_event_cell *cell;
@@ -405,14 +394,17 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT))
return -ENXIO;
- if (!access_ok(VERIFY_WRITE, buf, count))
+ if (!access_ok(buf, count))
return -EFAULT;
/* check client structures are in place */
if (snd_BUG_ON(!client))
return -ENXIO;
- if (!client->accept_input || (fifo = client->data.user.fifo) == NULL)
+ if (!client->accept_input)
+ return -ENXIO;
+ fifo = client->data.user.fifo;
+ if (!fifo)
return -ENXIO;
if (atomic_read(&fifo->overflow) > 0) {
@@ -424,45 +416,56 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
cell = NULL;
err = 0;
- snd_seq_fifo_lock(fifo);
+ guard(snd_seq_fifo)(fifo);
+
+ if (IS_ENABLED(CONFIG_SND_SEQ_UMP) && client->midi_version > 0)
+ aligned_size = sizeof(struct snd_seq_ump_event);
+ else
+ aligned_size = sizeof(struct snd_seq_event);
/* while data available in queue */
- while (count >= sizeof(struct snd_seq_event)) {
+ while (count >= aligned_size) {
int nonblock;
nonblock = (file->f_flags & O_NONBLOCK) || result > 0;
- if ((err = snd_seq_fifo_cell_out(fifo, &cell, nonblock)) < 0) {
+ err = snd_seq_fifo_cell_out(fifo, &cell, nonblock);
+ if (err < 0)
break;
+ if (!event_is_compatible(client, &cell->event)) {
+ snd_seq_cell_free(cell);
+ cell = NULL;
+ continue;
}
if (snd_seq_ev_is_variable(&cell->event)) {
- struct snd_seq_event tmpev;
- tmpev = cell->event;
+ struct snd_seq_ump_event tmpev;
+
+ memcpy(&tmpev, &cell->event, aligned_size);
tmpev.data.ext.len &= ~SNDRV_SEQ_EXT_MASK;
- if (copy_to_user(buf, &tmpev, sizeof(struct snd_seq_event))) {
+ if (copy_to_user(buf, &tmpev, aligned_size)) {
err = -EFAULT;
break;
}
- count -= sizeof(struct snd_seq_event);
- buf += sizeof(struct snd_seq_event);
+ count -= aligned_size;
+ buf += aligned_size;
err = snd_seq_expand_var_event(&cell->event, count,
(char __force *)buf, 0,
- sizeof(struct snd_seq_event));
+ aligned_size);
if (err < 0)
break;
result += err;
count -= err;
buf += err;
} else {
- if (copy_to_user(buf, &cell->event, sizeof(struct snd_seq_event))) {
+ if (copy_to_user(buf, &cell->event, aligned_size)) {
err = -EFAULT;
break;
}
- count -= sizeof(struct snd_seq_event);
- buf += sizeof(struct snd_seq_event);
+ count -= aligned_size;
+ buf += aligned_size;
}
snd_seq_cell_free(cell);
cell = NULL; /* to be sure */
- result += sizeof(struct snd_seq_event);
+ result += aligned_size;
}
if (err < 0) {
@@ -471,7 +474,6 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
if (err == -EAGAIN && result > 0)
err = 0;
}
- snd_seq_fifo_unlock(fifo);
return (err < 0) ? err : result;
}
@@ -489,28 +491,24 @@ static int check_port_perm(struct snd_seq_client_port *port, unsigned int flags)
/*
* check if the destination client is available, and return the pointer
- * if filter is non-zero, client filter bitmap is tested.
*/
-static struct snd_seq_client *get_event_dest_client(struct snd_seq_event *event,
- int filter)
+static struct snd_seq_client *get_event_dest_client(struct snd_seq_event *event)
{
- struct snd_seq_client *dest;
+ struct snd_seq_client *dest __free(snd_seq_client) = NULL;
dest = snd_seq_client_use_ptr(event->dest.client);
if (dest == NULL)
return NULL;
if (! dest->accept_input)
- goto __not_avail;
+ return NULL;
+ if (snd_seq_ev_is_ump(event))
+ return no_free_ptr(dest); /* ok - no filter checks */
+
if ((dest->filter & SNDRV_SEQ_FILTER_USE_EVENT) &&
! test_bit(event->type, dest->event_filter))
- goto __not_avail;
- if (filter && !(dest->filter & filter))
- goto __not_avail;
+ return NULL;
- return dest; /* ok - accessible */
-__not_avail:
- snd_seq_client_unlock(dest);
- return NULL;
+ return no_free_ptr(dest); /* ok - accessible */
}
@@ -549,7 +547,7 @@ static int bounce_error_event(struct snd_seq_client *client,
bounce_ev.data.quote.origin = event->dest;
bounce_ev.data.quote.event = event;
bounce_ev.data.quote.value = -err; /* use positive value */
- result = snd_seq_deliver_single_event(NULL, &bounce_ev, 0, atomic, hop + 1);
+ result = snd_seq_deliver_single_event(NULL, &bounce_ev, atomic, hop + 1);
if (result < 0) {
client->event_lost++;
return result;
@@ -567,7 +565,7 @@ static int bounce_error_event(struct snd_seq_client *client,
static int update_timestamp_of_queue(struct snd_seq_event *event,
int queue, int real_time)
{
- struct snd_seq_queue *q;
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
q = queueptr(queue);
if (! q)
@@ -575,78 +573,94 @@ static int update_timestamp_of_queue(struct snd_seq_event *event,
event->queue = queue;
event->flags &= ~SNDRV_SEQ_TIME_STAMP_MASK;
if (real_time) {
- event->time.time = snd_seq_timer_get_cur_time(q->timer);
+ event->time.time = snd_seq_timer_get_cur_time(q->timer, true);
event->flags |= SNDRV_SEQ_TIME_STAMP_REAL;
} else {
event->time.tick = snd_seq_timer_get_cur_tick(q->timer);
event->flags |= SNDRV_SEQ_TIME_STAMP_TICK;
}
- queuefree(q);
return 1;
}
-
-/*
- * deliver an event to the specified destination.
- * if filter is non-zero, client filter bitmap is tested.
- *
- * RETURN VALUE: 0 : if succeeded
- * <0 : error
- */
-static int snd_seq_deliver_single_event(struct snd_seq_client *client,
- struct snd_seq_event *event,
- int filter, int atomic, int hop)
+/* deliver a single event; called from below and UMP converter */
+int __snd_seq_deliver_single_event(struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ int atomic, int hop)
{
- struct snd_seq_client *dest = NULL;
- struct snd_seq_client_port *dest_port = NULL;
- int result = -ENOENT;
- int direct;
+ switch (dest->type) {
+ case USER_CLIENT:
+ if (!dest->data.user.fifo)
+ return 0;
+ return snd_seq_fifo_event_in(dest->data.user.fifo, event);
+ case KERNEL_CLIENT:
+ if (!dest_port->event_input)
+ return 0;
+ return dest_port->event_input(event,
+ snd_seq_ev_is_direct(event),
+ dest_port->private_data,
+ atomic, hop);
+ }
+ return 0;
+}
- direct = snd_seq_ev_is_direct(event);
+/* deliver a single event; called from snd_seq_deliver_single_event() */
+static int _snd_seq_deliver_single_event(struct snd_seq_client *client,
+ struct snd_seq_event *event,
+ int atomic, int hop)
+{
+ struct snd_seq_client *dest __free(snd_seq_client) = NULL;
+ struct snd_seq_client_port *dest_port __free(snd_seq_port) = NULL;
- dest = get_event_dest_client(event, filter);
+ dest = get_event_dest_client(event);
if (dest == NULL)
- goto __skip;
+ return -ENOENT;
dest_port = snd_seq_port_use_ptr(dest, event->dest.port);
if (dest_port == NULL)
- goto __skip;
+ return -ENOENT;
/* check permission */
- if (! check_port_perm(dest_port, SNDRV_SEQ_PORT_CAP_WRITE)) {
- result = -EPERM;
- goto __skip;
- }
-
+ if (!check_port_perm(dest_port, SNDRV_SEQ_PORT_CAP_WRITE))
+ return -EPERM;
+
if (dest_port->timestamping)
update_timestamp_of_queue(event, dest_port->time_queue,
dest_port->time_real);
- switch (dest->type) {
- case USER_CLIENT:
- if (dest->data.user.fifo)
- result = snd_seq_fifo_event_in(dest->data.user.fifo, event);
- break;
-
- case KERNEL_CLIENT:
- if (dest_port->event_input == NULL)
- break;
- result = dest_port->event_input(event, direct,
- dest_port->private_data,
- atomic, hop);
- break;
- default:
- break;
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ if (snd_seq_ev_is_ump(event)) {
+ if (!(dest->filter & SNDRV_SEQ_FILTER_NO_CONVERT))
+ return snd_seq_deliver_from_ump(client, dest, dest_port,
+ event, atomic, hop);
+ else if (dest->type == USER_CLIENT &&
+ !snd_seq_client_is_ump(dest))
+ return 0; // drop the event
+ } else if (snd_seq_client_is_ump(dest)) {
+ if (!(dest->filter & SNDRV_SEQ_FILTER_NO_CONVERT))
+ return snd_seq_deliver_to_ump(client, dest, dest_port,
+ event, atomic, hop);
}
+#endif /* CONFIG_SND_SEQ_UMP */
- __skip:
- if (dest_port)
- snd_seq_port_unlock(dest_port);
- if (dest)
- snd_seq_client_unlock(dest);
+ return __snd_seq_deliver_single_event(dest, dest_port, event,
+ atomic, hop);
+}
- if (result < 0 && !direct) {
- result = bounce_error_event(client, event, result, atomic, hop);
- }
+/*
+ * deliver an event to the specified destination.
+ * if filter is non-zero, client filter bitmap is tested.
+ *
+ * RETURN VALUE: 0 : if succeeded
+ * <0 : error
+ */
+static int snd_seq_deliver_single_event(struct snd_seq_client *client,
+ struct snd_seq_event *event,
+ int atomic, int hop)
+{
+ int result = _snd_seq_deliver_single_event(client, event, atomic, hop);
+
+ if (result < 0 && !snd_seq_ev_is_direct(event))
+ return bounce_error_event(client, event, result, atomic, hop);
return result;
}
@@ -654,129 +668,92 @@ static int snd_seq_deliver_single_event(struct snd_seq_client *client,
/*
* send the event to all subscribers:
*/
-static int deliver_to_subscribers(struct snd_seq_client *client,
- struct snd_seq_event *event,
- int atomic, int hop)
+static int __deliver_to_subscribers(struct snd_seq_client *client,
+ struct snd_seq_event *event,
+ int port, int atomic, int hop)
{
+ struct snd_seq_client_port *src_port __free(snd_seq_port) = NULL;
struct snd_seq_subscribers *subs;
- int err = 0, num_ev = 0;
- struct snd_seq_event event_saved;
- struct snd_seq_client_port *src_port;
+ int err, result = 0, num_ev = 0;
+ union __snd_seq_event event_saved;
+ size_t saved_size;
struct snd_seq_port_subs_info *grp;
- src_port = snd_seq_port_use_ptr(client, event->source.port);
- if (src_port == NULL)
- return -EINVAL; /* invalid source port */
+ if (port < 0)
+ return 0;
+ src_port = snd_seq_port_use_ptr(client, port);
+ if (!src_port)
+ return 0;
+
/* save original event record */
- event_saved = *event;
+ saved_size = snd_seq_event_packet_size(event);
+ memcpy(&event_saved, event, saved_size);
grp = &src_port->c_src;
/* lock list */
if (atomic)
read_lock(&grp->list_lock);
else
- down_read(&grp->list_mutex);
+ down_read_nested(&grp->list_mutex, hop);
list_for_each_entry(subs, &grp->list_head, src_list) {
+ /* both ports ready? */
+ if (atomic_read(&subs->ref_count) != 2)
+ continue;
event->dest = subs->info.dest;
if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
/* convert time according to flag with subscription */
update_timestamp_of_queue(event, subs->info.queue,
subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL);
- err = snd_seq_deliver_single_event(client, event,
- 0, atomic, hop);
- if (err < 0)
- break;
+ err = snd_seq_deliver_single_event(client, event, atomic, hop);
+ if (err < 0) {
+ /* save first error that occurs and continue */
+ if (!result)
+ result = err;
+ continue;
+ }
num_ev++;
/* restore original event record */
- *event = event_saved;
+ memcpy(event, &event_saved, saved_size);
}
if (atomic)
read_unlock(&grp->list_lock);
else
up_read(&grp->list_mutex);
- *event = event_saved; /* restore */
- snd_seq_port_unlock(src_port);
- return (err < 0) ? err : num_ev;
+ memcpy(event, &event_saved, saved_size);
+ return (result < 0) ? result : num_ev;
}
-
-#ifdef SUPPORT_BROADCAST
-/*
- * broadcast to all ports:
- */
-static int port_broadcast_event(struct snd_seq_client *client,
- struct snd_seq_event *event,
- int atomic, int hop)
-{
- int num_ev = 0, err = 0;
- struct snd_seq_client *dest_client;
- struct snd_seq_client_port *port;
-
- dest_client = get_event_dest_client(event, SNDRV_SEQ_FILTER_BROADCAST);
- if (dest_client == NULL)
- return 0; /* no matching destination */
-
- read_lock(&dest_client->ports_lock);
- list_for_each_entry(port, &dest_client->ports_list_head, list) {
- event->dest.port = port->addr.port;
- /* pass NULL as source client to avoid error bounce */
- err = snd_seq_deliver_single_event(NULL, event,
- SNDRV_SEQ_FILTER_BROADCAST,
- atomic, hop);
- if (err < 0)
- break;
- num_ev++;
- }
- read_unlock(&dest_client->ports_lock);
- snd_seq_client_unlock(dest_client);
- event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */
- return (err < 0) ? err : num_ev;
-}
-
-/*
- * send the event to all clients:
- * if destination port is also ADDRESS_BROADCAST, deliver to all ports.
- */
-static int broadcast_event(struct snd_seq_client *client,
- struct snd_seq_event *event, int atomic, int hop)
+static int deliver_to_subscribers(struct snd_seq_client *client,
+ struct snd_seq_event *event,
+ int atomic, int hop)
{
- int err = 0, num_ev = 0;
- int dest;
- struct snd_seq_addr addr;
-
- addr = event->dest; /* save */
-
- for (dest = 0; dest < SNDRV_SEQ_MAX_CLIENTS; dest++) {
- /* don't send to itself */
- if (dest == client->number)
- continue;
- event->dest.client = dest;
- event->dest.port = addr.port;
- if (addr.port == SNDRV_SEQ_ADDRESS_BROADCAST)
- err = port_broadcast_event(client, event, atomic, hop);
- else
- /* pass NULL as source client to avoid error bounce */
- err = snd_seq_deliver_single_event(NULL, event,
- SNDRV_SEQ_FILTER_BROADCAST,
- atomic, hop);
- if (err < 0)
- break;
- num_ev += err;
- }
- event->dest = addr; /* restore */
- return (err < 0) ? err : num_ev;
-}
-
+ int ret;
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ int ret2;
+#endif
-/* multicast - not supported yet */
-static int multicast_event(struct snd_seq_client *client, struct snd_seq_event *event,
- int atomic, int hop)
-{
- snd_printd("seq: multicast not supported yet.\n");
- return 0; /* ignored */
+ ret = __deliver_to_subscribers(client, event,
+ event->source.port, atomic, hop);
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ if (!snd_seq_client_is_ump(client) || client->ump_endpoint_port < 0)
+ return ret;
+ /* If it's an event from EP port (and with a UMP group),
+ * deliver to subscribers of the corresponding UMP group port, too.
+ * Or, if it's from non-EP port, deliver to subscribers of EP port, too.
+ */
+ if (event->source.port == client->ump_endpoint_port)
+ ret2 = __deliver_to_subscribers(client, event,
+ snd_seq_ump_group_port(event),
+ atomic, hop);
+ else
+ ret2 = __deliver_to_subscribers(client, event,
+ client->ump_endpoint_port,
+ atomic, hop);
+ if (ret2 < 0)
+ return ret2;
+#endif
+ return ret;
}
-#endif /* SUPPORT_BROADCAST */
-
/* deliver an event to the destination port(s).
* if the event is to subscribers or broadcast, the event is dispatched
@@ -793,26 +770,21 @@ static int snd_seq_deliver_event(struct snd_seq_client *client, struct snd_seq_e
hop++;
if (hop >= SNDRV_SEQ_MAX_HOPS) {
- snd_printd("too long delivery path (%d:%d->%d:%d)\n",
+ pr_debug("ALSA: seq: too long delivery path (%d:%d->%d:%d)\n",
event->source.client, event->source.port,
event->dest.client, event->dest.port);
return -EMLINK;
}
+ if (snd_seq_ev_is_variable(event) &&
+ snd_BUG_ON(atomic && (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR)))
+ return -EINVAL;
+
if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS ||
event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS)
result = deliver_to_subscribers(client, event, atomic, hop);
-#ifdef SUPPORT_BROADCAST
- else if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST ||
- event->dest.client == SNDRV_SEQ_ADDRESS_BROADCAST)
- result = broadcast_event(client, event, atomic, hop);
- else if (event->dest.client >= SNDRV_SEQ_MAX_CLIENTS)
- result = multicast_event(client, event, atomic, hop);
- else if (event->dest.port == SNDRV_SEQ_ADDRESS_BROADCAST)
- result = port_broadcast_event(client, event, atomic, hop);
-#endif
else
- result = snd_seq_deliver_single_event(client, event, 0, atomic, hop);
+ result = snd_seq_deliver_single_event(client, event, atomic, hop);
return result;
}
@@ -829,7 +801,7 @@ static int snd_seq_deliver_event(struct snd_seq_client *client, struct snd_seq_e
*/
int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
{
- struct snd_seq_client *client;
+ struct snd_seq_client *client __free(snd_seq_client) = NULL;
int result;
if (snd_BUG_ON(!cell))
@@ -841,7 +813,8 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
return -EINVAL;
}
- if (cell->event.type == SNDRV_SEQ_EVENT_NOTE) {
+ if (!snd_seq_ev_is_ump(&cell->event) &&
+ cell->event.type == SNDRV_SEQ_EVENT_NOTE) {
/* NOTE event:
* the event cell is re-used as a NOTE-OFF event and
* enqueued again.
@@ -865,7 +838,7 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
/* add the duration time */
switch (ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) {
case SNDRV_SEQ_TIME_STAMP_TICK:
- ev->time.tick += ev->data.note.duration;
+ cell->event.time.tick += ev->data.note.duration;
break;
case SNDRV_SEQ_TIME_STAMP_REAL:
/* unit for duration is ms */
@@ -890,7 +863,6 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
snd_seq_cell_free(cell);
}
- snd_seq_client_unlock(client);
return result;
}
@@ -902,7 +874,8 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)
static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
struct snd_seq_event *event,
struct file *file, int blocking,
- int atomic, int hop)
+ int atomic, int hop,
+ struct mutex *mutexp)
{
struct snd_seq_event_cell *cell;
int err;
@@ -911,24 +884,18 @@ static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) {
event->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
event->queue = SNDRV_SEQ_QUEUE_DIRECT;
- } else
-#ifdef SUPPORT_BROADCAST
- if (event->queue == SNDRV_SEQ_ADDRESS_BROADCAST) {
- event->dest.client = SNDRV_SEQ_ADDRESS_BROADCAST;
- event->queue = SNDRV_SEQ_QUEUE_DIRECT;
- }
-#endif
- if (event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) {
+ } else if (event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) {
/* check presence of source port */
- struct snd_seq_client_port *src_port = snd_seq_port_use_ptr(client, event->source.port);
- if (src_port == NULL)
+ struct snd_seq_client_port *src_port __free(snd_seq_port) =
+ snd_seq_port_use_ptr(client, event->source.port);
+ if (!src_port)
return -EINVAL;
- snd_seq_port_unlock(src_port);
}
/* direct event processing without enqueued */
if (snd_seq_ev_is_direct(event)) {
- if (event->type == SNDRV_SEQ_EVENT_NOTE)
+ if (!snd_seq_ev_is_ump(event) &&
+ event->type == SNDRV_SEQ_EVENT_NOTE)
return -EINVAL; /* this event must be enqueued! */
return snd_seq_deliver_event(client, event, atomic, hop);
}
@@ -940,12 +907,14 @@ static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
return -ENXIO; /* queue is not allocated */
/* allocate an event cell */
- err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic, file);
+ err = snd_seq_event_dup(client->pool, event, &cell, !blocking || atomic,
+ file, mutexp);
if (err < 0)
return err;
/* we got a cell. enqueue it. */
- if ((err = snd_seq_enqueue_event(cell, atomic, hop)) < 0) {
+ err = snd_seq_enqueue_event(cell, atomic, hop);
+ if (err < 0) {
snd_seq_cell_free(cell);
return err;
}
@@ -995,8 +964,9 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
{
struct snd_seq_client *client = file->private_data;
int written = 0, len;
- int err = -EINVAL;
- struct snd_seq_event event;
+ int err, handled;
+ union __snd_seq_event __event;
+ struct snd_seq_event *ev = &__event.legacy;
if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT))
return -ENXIO;
@@ -1008,70 +978,101 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
if (!client->accept_output || client->pool == NULL)
return -ENXIO;
+ repeat:
+ handled = 0;
/* allocate the pool now if the pool is not allocated yet */
+ mutex_lock(&client->ioctl_mutex);
if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) {
- if (snd_seq_pool_init(client->pool) < 0)
- return -ENOMEM;
+ err = snd_seq_pool_init(client->pool);
+ if (err < 0)
+ goto out;
}
/* only process whole events */
+ err = -EINVAL;
while (count >= sizeof(struct snd_seq_event)) {
/* Read in the event header from the user */
- len = sizeof(event);
- if (copy_from_user(&event, buf, len)) {
+ len = sizeof(struct snd_seq_event);
+ if (copy_from_user(ev, buf, len)) {
err = -EFAULT;
break;
}
- event.source.client = client->number; /* fill in client number */
+ /* read in the rest bytes for UMP events */
+ if (snd_seq_ev_is_ump(ev)) {
+ if (count < sizeof(struct snd_seq_ump_event))
+ break;
+ if (copy_from_user((char *)ev + len, buf + len,
+ sizeof(struct snd_seq_ump_event) - len)) {
+ err = -EFAULT;
+ break;
+ }
+ len = sizeof(struct snd_seq_ump_event);
+ }
+
+ ev->source.client = client->number; /* fill in client number */
/* Check for extension data length */
- if (check_event_type_and_length(&event)) {
+ if (check_event_type_and_length(ev)) {
err = -EINVAL;
break;
}
- /* check for special events */
- if (event.type == SNDRV_SEQ_EVENT_NONE)
- goto __skip_event;
- else if (snd_seq_ev_is_reserved(&event)) {
+ if (!event_is_compatible(client, ev)) {
err = -EINVAL;
break;
}
- if (snd_seq_ev_is_variable(&event)) {
- int extlen = event.data.ext.len & ~SNDRV_SEQ_EXT_MASK;
+ /* check for special events */
+ if (!snd_seq_ev_is_ump(ev)) {
+ if (ev->type == SNDRV_SEQ_EVENT_NONE)
+ goto __skip_event;
+ else if (snd_seq_ev_is_reserved(ev)) {
+ err = -EINVAL;
+ break;
+ }
+ }
+
+ if (snd_seq_ev_is_variable(ev)) {
+ int extlen = ev->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
if ((size_t)(extlen + len) > count) {
/* back out, will get an error this time or next */
err = -EINVAL;
break;
}
/* set user space pointer */
- event.data.ext.len = extlen | SNDRV_SEQ_EXT_USRPTR;
- event.data.ext.ptr = (char __force *)buf
- + sizeof(struct snd_seq_event);
+ ev->data.ext.len = extlen | SNDRV_SEQ_EXT_USRPTR;
+ ev->data.ext.ptr = (char __force *)buf + len;
len += extlen; /* increment data length */
} else {
#ifdef CONFIG_COMPAT
- if (client->convert32 && snd_seq_ev_is_varusr(&event)) {
- void *ptr = compat_ptr(event.data.raw32.d[1]);
- event.data.ext.ptr = ptr;
- }
+ if (client->convert32 && snd_seq_ev_is_varusr(ev))
+ ev->data.ext.ptr =
+ (void __force *)compat_ptr(ev->data.raw32.d[1]);
#endif
}
/* ok, enqueue it */
- err = snd_seq_client_enqueue_event(client, &event, file,
+ err = snd_seq_client_enqueue_event(client, ev, file,
!(file->f_flags & O_NONBLOCK),
- 0, 0);
+ 0, 0, &client->ioctl_mutex);
if (err < 0)
break;
+ handled++;
__skip_event:
/* Update pointers and counts */
count -= len;
buf += len;
written += len;
+
+ /* let's have a coffee break if too many events are queued */
+ if (++handled >= 200) {
+ mutex_unlock(&client->ioctl_mutex);
+ goto repeat;
+ }
}
+ out:
+ mutex_unlock(&client->ioctl_mutex);
return written ? written : err;
}
@@ -1079,29 +1080,28 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
/*
* handle polling
*/
-static unsigned int snd_seq_poll(struct file *file, poll_table * wait)
+static __poll_t snd_seq_poll(struct file *file, poll_table * wait)
{
struct snd_seq_client *client = file->private_data;
- unsigned int mask = 0;
+ __poll_t mask = 0;
/* check client structures are in place */
if (snd_BUG_ON(!client))
- return -ENXIO;
+ return EPOLLERR;
if ((snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT) &&
client->data.user.fifo) {
/* check if data is available in the outqueue */
if (snd_seq_fifo_poll_wait(client->data.user.fifo, file, wait))
- mask |= POLLIN | POLLRDNORM;
+ mask |= EPOLLIN | EPOLLRDNORM;
}
if (snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT) {
/* check if data is available in the pool */
- if (!snd_seq_write_pool_allocated(client) ||
- snd_seq_pool_poll_wait(client->pool, file, wait))
- mask |= POLLOUT | POLLWRNORM;
+ if (snd_seq_pool_poll_wait(client->pool, file, wait))
+ mask |= EPOLLOUT | EPOLLWRNORM;
}
return mask;
@@ -1110,62 +1110,68 @@ static unsigned int snd_seq_poll(struct file *file, poll_table * wait)
/*-----------------------------------------------------*/
+static int snd_seq_ioctl_pversion(struct snd_seq_client *client, void *arg)
+{
+ int *pversion = arg;
+
+ *pversion = SNDRV_SEQ_VERSION;
+ return 0;
+}
+
+static int snd_seq_ioctl_user_pversion(struct snd_seq_client *client, void *arg)
+{
+ client->user_pversion = *(unsigned int *)arg;
+ return 0;
+}
+
+static int snd_seq_ioctl_client_id(struct snd_seq_client *client, void *arg)
+{
+ int *client_id = arg;
+
+ *client_id = client->number;
+ return 0;
+}
/* SYSTEM_INFO ioctl() */
-static int snd_seq_ioctl_system_info(struct snd_seq_client *client, void __user *arg)
+static int snd_seq_ioctl_system_info(struct snd_seq_client *client, void *arg)
{
- struct snd_seq_system_info info;
+ struct snd_seq_system_info *info = arg;
- memset(&info, 0, sizeof(info));
+ memset(info, 0, sizeof(*info));
/* fill the info fields */
- info.queues = SNDRV_SEQ_MAX_QUEUES;
- info.clients = SNDRV_SEQ_MAX_CLIENTS;
- info.ports = 256; /* fixed limit */
- info.channels = 256; /* fixed limit */
- info.cur_clients = client_usage.cur;
- info.cur_queues = snd_seq_queue_get_cur_queues();
-
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
+ info->queues = SNDRV_SEQ_MAX_QUEUES;
+ info->clients = SNDRV_SEQ_MAX_CLIENTS;
+ info->ports = SNDRV_SEQ_MAX_PORTS;
+ info->channels = 256; /* fixed limit */
+ info->cur_clients = client_usage.cur;
+ info->cur_queues = snd_seq_queue_get_cur_queues();
+
return 0;
}
/* RUNNING_MODE ioctl() */
-static int snd_seq_ioctl_running_mode(struct snd_seq_client *client, void __user *arg)
+static int snd_seq_ioctl_running_mode(struct snd_seq_client *client, void *arg)
{
- struct snd_seq_running_info info;
- struct snd_seq_client *cptr;
- int err = 0;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
+ struct snd_seq_running_info *info = arg;
+ struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
/* requested client number */
- cptr = snd_seq_client_use_ptr(info.client);
+ cptr = client_load_and_use_ptr(info->client);
if (cptr == NULL)
return -ENOENT; /* don't change !!! */
#ifdef SNDRV_BIG_ENDIAN
- if (! info.big_endian) {
- err = -EINVAL;
- goto __err;
- }
+ if (!info->big_endian)
+ return -EINVAL;
#else
- if (info.big_endian) {
- err = -EINVAL;
- goto __err;
- }
-
+ if (info->big_endian)
+ return -EINVAL;
#endif
- if (info.cpu_mode > sizeof(long)) {
- err = -EINVAL;
- goto __err;
- }
- cptr->convert32 = (info.cpu_mode < sizeof(long));
- __err:
- snd_seq_client_unlock(cptr);
- return err;
+ if (info->cpu_mode > sizeof(long))
+ return -EINVAL;
+ cptr->convert32 = (info->cpu_mode < sizeof(long));
+ return 0;
}
/* CLIENT_INFO ioctl() */
@@ -1176,60 +1182,80 @@ static void get_client_info(struct snd_seq_client *cptr,
/* fill the info fields */
info->type = cptr->type;
- strcpy(info->name, cptr->name);
+ strscpy(info->name, cptr->name);
info->filter = cptr->filter;
info->event_lost = cptr->event_lost;
memcpy(info->event_filter, cptr->event_filter, 32);
+ info->group_filter = cptr->group_filter;
info->num_ports = cptr->num_ports;
+
+ if (cptr->type == USER_CLIENT)
+ info->pid = pid_vnr(cptr->data.user.owner);
+ else
+ info->pid = -1;
+
+ if (cptr->type == KERNEL_CLIENT)
+ info->card = cptr->data.kernel.card ? cptr->data.kernel.card->number : -1;
+ else
+ info->card = -1;
+
+ info->midi_version = cptr->midi_version;
memset(info->reserved, 0, sizeof(info->reserved));
}
static int snd_seq_ioctl_get_client_info(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_client *cptr;
- struct snd_seq_client_info client_info;
-
- if (copy_from_user(&client_info, arg, sizeof(client_info)))
- return -EFAULT;
+ struct snd_seq_client_info *client_info = arg;
+ struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
/* requested client number */
- cptr = snd_seq_client_use_ptr(client_info.client);
+ cptr = client_load_and_use_ptr(client_info->client);
if (cptr == NULL)
return -ENOENT; /* don't change !!! */
- get_client_info(cptr, &client_info);
- snd_seq_client_unlock(cptr);
-
- if (copy_to_user(arg, &client_info, sizeof(client_info)))
- return -EFAULT;
+ get_client_info(cptr, client_info);
return 0;
}
/* CLIENT_INFO ioctl() */
static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_client_info client_info;
-
- if (copy_from_user(&client_info, arg, sizeof(client_info)))
- return -EFAULT;
+ struct snd_seq_client_info *client_info = arg;
/* it is not allowed to set the info fields for an another client */
- if (client->number != client_info.client)
+ if (client->number != client_info->client)
return -EPERM;
/* also client type must be set now */
- if (client->type != client_info.type)
+ if (client->type != client_info->type)
return -EINVAL;
+ if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 3)) {
+ /* check validity of midi_version field */
+ if (client_info->midi_version > SNDRV_SEQ_CLIENT_UMP_MIDI_2_0)
+ return -EINVAL;
+
+ /* check if UMP is supported in kernel */
+ if (!IS_ENABLED(CONFIG_SND_SEQ_UMP) &&
+ client_info->midi_version > 0)
+ return -EINVAL;
+ }
+
/* fill the info fields */
- if (client_info.name[0])
- strlcpy(client->name, client_info.name, sizeof(client->name));
+ if (client_info->name[0])
+ strscpy(client->name, client_info->name, sizeof(client->name));
- client->filter = client_info.filter;
- client->event_lost = client_info.event_lost;
- memcpy(client->event_filter, client_info.event_filter, 32);
+ client->filter = client_info->filter;
+ client->event_lost = client_info->event_lost;
+ if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 3))
+ client->midi_version = client_info->midi_version;
+ memcpy(client->event_filter, client_info->event_filter, 32);
+ client->group_filter = client_info->group_filter;
+
+ /* notify the change */
+ snd_seq_system_client_ev_client_change(client->number);
return 0;
}
@@ -1238,35 +1264,39 @@ static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client,
/*
* CREATE PORT ioctl()
*/
-static int snd_seq_ioctl_create_port(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg)
{
+ struct snd_seq_port_info *info = arg;
struct snd_seq_client_port *port;
- struct snd_seq_port_info info;
struct snd_seq_port_callback *callback;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
+ int port_idx, err;
/* it is not allowed to create the port for an another client */
- if (info.addr.client != client->number)
+ if (info->addr.client != client->number)
return -EPERM;
+ if (client->type == USER_CLIENT && info->kernel)
+ return -EINVAL;
+ if ((info->capability & SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT) &&
+ client->ump_endpoint_port >= 0)
+ return -EBUSY;
- port = snd_seq_create_port(client, (info.flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT) ? info.addr.port : -1);
- if (port == NULL)
- return -ENOMEM;
-
- if (client->type == USER_CLIENT && info.kernel) {
- snd_seq_delete_port(client, port->addr.port);
+ if (info->flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT)
+ port_idx = info->addr.port;
+ else
+ port_idx = -1;
+ if (port_idx >= SNDRV_SEQ_ADDRESS_UNKNOWN)
return -EINVAL;
- }
+ err = snd_seq_create_port(client, port_idx, &port);
+ if (err < 0)
+ return err;
+
if (client->type == KERNEL_CLIENT) {
- if ((callback = info.kernel) != NULL) {
+ callback = info->kernel;
+ if (callback) {
if (callback->owner)
port->owner = callback->owner;
port->private_data = callback->private_data;
port->private_free = callback->private_free;
- port->callback_all = callback->callback_all;
port->event_input = callback->event_input;
port->c_src.open = callback->subscribe;
port->c_src.close = callback->unsubscribe;
@@ -1275,13 +1305,13 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client,
}
}
- info.addr = port->addr;
+ info->addr = port->addr;
- snd_seq_set_port_info(port, &info);
+ snd_seq_set_port_info(port, info);
+ if (info->capability & SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT)
+ client->ump_endpoint_port = port->addr.port;
snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port);
-
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
+ snd_seq_port_unlock(port);
return 0;
}
@@ -1289,23 +1319,21 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client,
/*
* DELETE PORT ioctl()
*/
-static int snd_seq_ioctl_delete_port(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_delete_port(struct snd_seq_client *client, void *arg)
{
- struct snd_seq_port_info info;
+ struct snd_seq_port_info *info = arg;
int err;
- /* set passed parameters */
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
/* it is not allowed to remove the port for an another client */
- if (info.addr.client != client->number)
+ if (info->addr.client != client->number)
return -EPERM;
- err = snd_seq_delete_port(client, info.addr.port);
- if (err >= 0)
- snd_seq_system_client_ev_port_exit(client->number, info.addr.port);
+ err = snd_seq_delete_port(client, info->addr.port);
+ if (err >= 0) {
+ if (client->ump_endpoint_port == info->addr.port)
+ client->ump_endpoint_port = -1;
+ snd_seq_system_client_ev_port_exit(client->number, info->addr.port);
+ }
return err;
}
@@ -1313,32 +1341,22 @@ static int snd_seq_ioctl_delete_port(struct snd_seq_client *client,
/*
* GET_PORT_INFO ioctl() (on any client)
*/
-static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client, void *arg)
{
- struct snd_seq_client *cptr;
- struct snd_seq_client_port *port;
- struct snd_seq_port_info info;
+ struct snd_seq_port_info *info = arg;
+ struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
+ struct snd_seq_client_port *port __free(snd_seq_port) = NULL;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
- cptr = snd_seq_client_use_ptr(info.addr.client);
+ cptr = client_load_and_use_ptr(info->addr.client);
if (cptr == NULL)
return -ENXIO;
- port = snd_seq_port_use_ptr(cptr, info.addr.port);
- if (port == NULL) {
- snd_seq_client_unlock(cptr);
+ port = snd_seq_port_use_ptr(cptr, info->addr.port);
+ if (port == NULL)
return -ENOENT; /* don't change */
- }
/* get port info */
- snd_seq_get_port_info(port, &info);
- snd_seq_port_unlock(port);
- snd_seq_client_unlock(cptr);
-
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
+ snd_seq_get_port_info(port, info);
return 0;
}
@@ -1346,21 +1364,19 @@ static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client,
/*
* SET_PORT_INFO ioctl() (only ports on this/own client)
*/
-static int snd_seq_ioctl_set_port_info(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_set_port_info(struct snd_seq_client *client, void *arg)
{
- struct snd_seq_client_port *port;
- struct snd_seq_port_info info;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
+ struct snd_seq_port_info *info = arg;
+ struct snd_seq_client_port *port __free(snd_seq_port) = NULL;
- if (info.addr.client != client->number) /* only set our own ports ! */
+ if (info->addr.client != client->number) /* only set our own ports ! */
return -EPERM;
- port = snd_seq_port_use_ptr(client, info.addr.port);
+ port = snd_seq_port_use_ptr(client, info->addr.port);
if (port) {
- snd_seq_set_port_info(port, &info);
- snd_seq_port_unlock(port);
+ snd_seq_set_port_info(port, info);
+ /* notify the change */
+ snd_seq_system_client_ev_port_change(info->addr.client,
+ info->addr.port);
}
return 0;
}
@@ -1417,7 +1433,7 @@ int snd_seq_client_notify_subscription(int client, int port,
event.data.connect.dest = info->dest;
event.data.connect.sender = info->sender;
- return snd_seq_system_notify(client, port, &event); /* non-atomic */
+ return snd_seq_system_notify(client, port, &event, false); /* non-atomic */
}
@@ -1425,43 +1441,37 @@ int snd_seq_client_notify_subscription(int client, int port,
* add to port's subscription list IOCTL interface
*/
static int snd_seq_ioctl_subscribe_port(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- int result = -EINVAL;
- struct snd_seq_client *receiver = NULL, *sender = NULL;
- struct snd_seq_client_port *sport = NULL, *dport = NULL;
- struct snd_seq_port_subscribe subs;
-
- if (copy_from_user(&subs, arg, sizeof(subs)))
- return -EFAULT;
+ struct snd_seq_port_subscribe *subs = arg;
+ struct snd_seq_client *receiver __free(snd_seq_client) = NULL;
+ struct snd_seq_client *sender __free(snd_seq_client) = NULL;
+ struct snd_seq_client_port *sport __free(snd_seq_port) = NULL;
+ struct snd_seq_client_port *dport __free(snd_seq_port) = NULL;
+ int result;
- if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL)
- goto __end;
- if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
- goto __end;
- if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
- goto __end;
- if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL)
- goto __end;
+ receiver = client_load_and_use_ptr(subs->dest.client);
+ if (!receiver)
+ return -EINVAL;
+ sender = client_load_and_use_ptr(subs->sender.client);
+ if (!sender)
+ return -EINVAL;
+ sport = snd_seq_port_use_ptr(sender, subs->sender.port);
+ if (!sport)
+ return -EINVAL;
+ dport = snd_seq_port_use_ptr(receiver, subs->dest.port);
+ if (!dport)
+ return -EINVAL;
- result = check_subscription_permission(client, sport, dport, &subs);
+ result = check_subscription_permission(client, sport, dport, subs);
if (result < 0)
- goto __end;
+ return result;
/* connect them */
- result = snd_seq_port_connect(client, sender, sport, receiver, dport, &subs);
+ result = snd_seq_port_connect(client, sender, sport, receiver, dport, subs);
if (! result) /* broadcast announce */
snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0,
- &subs, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED);
- __end:
- if (sport)
- snd_seq_port_unlock(sport);
- if (dport)
- snd_seq_port_unlock(dport);
- if (sender)
- snd_seq_client_unlock(sender);
- if (receiver)
- snd_seq_client_unlock(receiver);
+ subs, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED);
return result;
}
@@ -1470,240 +1480,188 @@ static int snd_seq_ioctl_subscribe_port(struct snd_seq_client *client,
* remove from port's subscription list
*/
static int snd_seq_ioctl_unsubscribe_port(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- int result = -ENXIO;
- struct snd_seq_client *receiver = NULL, *sender = NULL;
- struct snd_seq_client_port *sport = NULL, *dport = NULL;
- struct snd_seq_port_subscribe subs;
-
- if (copy_from_user(&subs, arg, sizeof(subs)))
- return -EFAULT;
+ struct snd_seq_port_subscribe *subs = arg;
+ struct snd_seq_client *receiver __free(snd_seq_client) = NULL;
+ struct snd_seq_client *sender __free(snd_seq_client) = NULL;
+ struct snd_seq_client_port *sport __free(snd_seq_port) = NULL;
+ struct snd_seq_client_port *dport __free(snd_seq_port) = NULL;
+ int result;
- if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL)
- goto __end;
- if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
- goto __end;
- if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
- goto __end;
- if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL)
- goto __end;
+ receiver = snd_seq_client_use_ptr(subs->dest.client);
+ if (!receiver)
+ return -ENXIO;
+ sender = snd_seq_client_use_ptr(subs->sender.client);
+ if (!sender)
+ return -ENXIO;
+ sport = snd_seq_port_use_ptr(sender, subs->sender.port);
+ if (!sport)
+ return -ENXIO;
+ dport = snd_seq_port_use_ptr(receiver, subs->dest.port);
+ if (!dport)
+ return -ENXIO;
- result = check_subscription_permission(client, sport, dport, &subs);
+ result = check_subscription_permission(client, sport, dport, subs);
if (result < 0)
- goto __end;
+ return result;
- result = snd_seq_port_disconnect(client, sender, sport, receiver, dport, &subs);
+ result = snd_seq_port_disconnect(client, sender, sport, receiver, dport, subs);
if (! result) /* broadcast announce */
snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0,
- &subs, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED);
- __end:
- if (sport)
- snd_seq_port_unlock(sport);
- if (dport)
- snd_seq_port_unlock(dport);
- if (sender)
- snd_seq_client_unlock(sender);
- if (receiver)
- snd_seq_client_unlock(receiver);
+ subs, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED);
return result;
}
/* CREATE_QUEUE ioctl() */
-static int snd_seq_ioctl_create_queue(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_create_queue(struct snd_seq_client *client, void *arg)
{
- struct snd_seq_queue_info info;
- int result;
- struct snd_seq_queue *q;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- result = snd_seq_queue_alloc(client->number, info.locked, info.flags);
- if (result < 0)
- return result;
+ struct snd_seq_queue_info *info = arg;
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
- q = queueptr(result);
- if (q == NULL)
- return -EINVAL;
+ q = snd_seq_queue_alloc(client->number, info->locked, info->flags);
+ if (IS_ERR(q))
+ return PTR_ERR(q);
- info.queue = q->queue;
- info.locked = q->locked;
- info.owner = q->owner;
+ info->queue = q->queue;
+ info->locked = q->locked;
+ info->owner = q->owner;
/* set queue name */
- if (! info.name[0])
- snprintf(info.name, sizeof(info.name), "Queue-%d", q->queue);
- strlcpy(q->name, info.name, sizeof(q->name));
- queuefree(q);
-
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
+ if (!info->name[0])
+ snprintf(info->name, sizeof(info->name), "Queue-%d", q->queue);
+ strscpy(q->name, info->name, sizeof(q->name));
return 0;
}
/* DELETE_QUEUE ioctl() */
-static int snd_seq_ioctl_delete_queue(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_delete_queue(struct snd_seq_client *client, void *arg)
{
- struct snd_seq_queue_info info;
+ struct snd_seq_queue_info *info = arg;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- return snd_seq_queue_delete(client->number, info.queue);
+ return snd_seq_queue_delete(client->number, info->queue);
}
/* GET_QUEUE_INFO ioctl() */
static int snd_seq_ioctl_get_queue_info(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_queue_info info;
- struct snd_seq_queue *q;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
+ struct snd_seq_queue_info *info = arg;
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
- q = queueptr(info.queue);
+ q = queueptr(info->queue);
if (q == NULL)
return -EINVAL;
- memset(&info, 0, sizeof(info));
- info.queue = q->queue;
- info.owner = q->owner;
- info.locked = q->locked;
- strlcpy(info.name, q->name, sizeof(info.name));
- queuefree(q);
-
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
+ memset(info, 0, sizeof(*info));
+ info->queue = q->queue;
+ info->owner = q->owner;
+ info->locked = q->locked;
+ strscpy(info->name, q->name, sizeof(info->name));
return 0;
}
/* SET_QUEUE_INFO ioctl() */
static int snd_seq_ioctl_set_queue_info(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_queue_info info;
- struct snd_seq_queue *q;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
+ struct snd_seq_queue_info *info = arg;
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
- if (info.owner != client->number)
+ if (info->owner != client->number)
return -EINVAL;
/* change owner/locked permission */
- if (snd_seq_queue_check_access(info.queue, client->number)) {
- if (snd_seq_queue_set_owner(info.queue, client->number, info.locked) < 0)
+ if (snd_seq_queue_check_access(info->queue, client->number)) {
+ if (snd_seq_queue_set_owner(info->queue, client->number, info->locked) < 0)
return -EPERM;
- if (info.locked)
- snd_seq_queue_use(info.queue, client->number, 1);
+ if (info->locked)
+ snd_seq_queue_use(info->queue, client->number, 1);
} else {
return -EPERM;
}
- q = queueptr(info.queue);
+ q = queueptr(info->queue);
if (! q)
return -EINVAL;
- if (q->owner != client->number) {
- queuefree(q);
+ if (q->owner != client->number)
return -EPERM;
- }
- strlcpy(q->name, info.name, sizeof(q->name));
- queuefree(q);
+ strscpy(q->name, info->name, sizeof(q->name));
return 0;
}
/* GET_NAMED_QUEUE ioctl() */
-static int snd_seq_ioctl_get_named_queue(struct snd_seq_client *client, void __user *arg)
+static int snd_seq_ioctl_get_named_queue(struct snd_seq_client *client,
+ void *arg)
{
- struct snd_seq_queue_info info;
- struct snd_seq_queue *q;
+ struct snd_seq_queue_info *info = arg;
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- q = snd_seq_queue_find_name(info.name);
+ q = snd_seq_queue_find_name(info->name);
if (q == NULL)
return -EINVAL;
- info.queue = q->queue;
- info.owner = q->owner;
- info.locked = q->locked;
- queuefree(q);
-
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
+ info->queue = q->queue;
+ info->owner = q->owner;
+ info->locked = q->locked;
return 0;
}
/* GET_QUEUE_STATUS ioctl() */
static int snd_seq_ioctl_get_queue_status(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_queue_status status;
- struct snd_seq_queue *queue;
+ struct snd_seq_queue_status *status = arg;
+ struct snd_seq_queue *queue __free(snd_seq_queue) = NULL;
struct snd_seq_timer *tmr;
- if (copy_from_user(&status, arg, sizeof(status)))
- return -EFAULT;
-
- queue = queueptr(status.queue);
+ queue = queueptr(status->queue);
if (queue == NULL)
return -EINVAL;
- memset(&status, 0, sizeof(status));
- status.queue = queue->queue;
+ memset(status, 0, sizeof(*status));
+ status->queue = queue->queue;
tmr = queue->timer;
- status.events = queue->tickq->cells + queue->timeq->cells;
+ status->events = queue->tickq->cells + queue->timeq->cells;
- status.time = snd_seq_timer_get_cur_time(tmr);
- status.tick = snd_seq_timer_get_cur_tick(tmr);
+ status->time = snd_seq_timer_get_cur_time(tmr, true);
+ status->tick = snd_seq_timer_get_cur_tick(tmr);
- status.running = tmr->running;
+ status->running = tmr->running;
- status.flags = queue->flags;
- queuefree(queue);
+ status->flags = queue->flags;
- if (copy_to_user(arg, &status, sizeof(status)))
- return -EFAULT;
return 0;
}
/* GET_QUEUE_TEMPO ioctl() */
static int snd_seq_ioctl_get_queue_tempo(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_queue_tempo tempo;
- struct snd_seq_queue *queue;
+ struct snd_seq_queue_tempo *tempo = arg;
+ struct snd_seq_queue *queue __free(snd_seq_queue) = NULL;
struct snd_seq_timer *tmr;
- if (copy_from_user(&tempo, arg, sizeof(tempo)))
- return -EFAULT;
-
- queue = queueptr(tempo.queue);
+ queue = queueptr(tempo->queue);
if (queue == NULL)
return -EINVAL;
- memset(&tempo, 0, sizeof(tempo));
- tempo.queue = queue->queue;
+ memset(tempo, 0, sizeof(*tempo));
+ tempo->queue = queue->queue;
tmr = queue->timer;
- tempo.tempo = tmr->tempo;
- tempo.ppq = tmr->ppq;
- tempo.skew_value = tmr->skew;
- tempo.skew_base = tmr->skew_base;
- queuefree(queue);
+ tempo->tempo = tmr->tempo;
+ tempo->ppq = tmr->ppq;
+ tempo->skew_value = tmr->skew;
+ tempo->skew_base = tmr->skew_base;
+ if (client->user_pversion >= SNDRV_PROTOCOL_VERSION(1, 0, 4))
+ tempo->tempo_base = tmr->tempo_base;
- if (copy_to_user(arg, &tempo, sizeof(tempo)))
- return -EFAULT;
return 0;
}
@@ -1715,94 +1673,74 @@ int snd_seq_set_queue_tempo(int client, struct snd_seq_queue_tempo *tempo)
return -EPERM;
return snd_seq_queue_timer_set_tempo(tempo->queue, client, tempo);
}
-
EXPORT_SYMBOL(snd_seq_set_queue_tempo);
static int snd_seq_ioctl_set_queue_tempo(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_queue_tempo *tempo = arg;
int result;
- struct snd_seq_queue_tempo tempo;
-
- if (copy_from_user(&tempo, arg, sizeof(tempo)))
- return -EFAULT;
- result = snd_seq_set_queue_tempo(client->number, &tempo);
+ if (client->user_pversion < SNDRV_PROTOCOL_VERSION(1, 0, 4))
+ tempo->tempo_base = 0;
+ result = snd_seq_set_queue_tempo(client->number, tempo);
return result < 0 ? result : 0;
}
/* GET_QUEUE_TIMER ioctl() */
static int snd_seq_ioctl_get_queue_timer(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_queue_timer timer;
- struct snd_seq_queue *queue;
+ struct snd_seq_queue_timer *timer = arg;
+ struct snd_seq_queue *queue __free(snd_seq_queue) = NULL;
struct snd_seq_timer *tmr;
- if (copy_from_user(&timer, arg, sizeof(timer)))
- return -EFAULT;
-
- queue = queueptr(timer.queue);
+ queue = queueptr(timer->queue);
if (queue == NULL)
return -EINVAL;
- if (mutex_lock_interruptible(&queue->timer_mutex)) {
- queuefree(queue);
- return -ERESTARTSYS;
- }
+ guard(mutex)(&queue->timer_mutex);
tmr = queue->timer;
- memset(&timer, 0, sizeof(timer));
- timer.queue = queue->queue;
+ memset(timer, 0, sizeof(*timer));
+ timer->queue = queue->queue;
- timer.type = tmr->type;
+ timer->type = tmr->type;
if (tmr->type == SNDRV_SEQ_TIMER_ALSA) {
- timer.u.alsa.id = tmr->alsa_id;
- timer.u.alsa.resolution = tmr->preferred_resolution;
+ timer->u.alsa.id = tmr->alsa_id;
+ timer->u.alsa.resolution = tmr->preferred_resolution;
}
- mutex_unlock(&queue->timer_mutex);
- queuefree(queue);
- if (copy_to_user(arg, &timer, sizeof(timer)))
- return -EFAULT;
return 0;
}
/* SET_QUEUE_TIMER ioctl() */
static int snd_seq_ioctl_set_queue_timer(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_queue_timer *timer = arg;
int result = 0;
- struct snd_seq_queue_timer timer;
- if (copy_from_user(&timer, arg, sizeof(timer)))
- return -EFAULT;
-
- if (timer.type != SNDRV_SEQ_TIMER_ALSA)
+ if (timer->type != SNDRV_SEQ_TIMER_ALSA)
return -EINVAL;
- if (snd_seq_queue_check_access(timer.queue, client->number)) {
- struct snd_seq_queue *q;
+ if (snd_seq_queue_check_access(timer->queue, client->number)) {
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
struct snd_seq_timer *tmr;
- q = queueptr(timer.queue);
+ q = queueptr(timer->queue);
if (q == NULL)
return -ENXIO;
- if (mutex_lock_interruptible(&q->timer_mutex)) {
- queuefree(q);
- return -ERESTARTSYS;
- }
+ guard(mutex)(&q->timer_mutex);
tmr = q->timer;
- snd_seq_queue_timer_close(timer.queue);
- tmr->type = timer.type;
+ snd_seq_queue_timer_close(timer->queue);
+ tmr->type = timer->type;
if (tmr->type == SNDRV_SEQ_TIMER_ALSA) {
- tmr->alsa_id = timer.u.alsa.id;
- tmr->preferred_resolution = timer.u.alsa.resolution;
+ tmr->alsa_id = timer->u.alsa.id;
+ tmr->preferred_resolution = timer->u.alsa.resolution;
}
- result = snd_seq_queue_timer_open(timer.queue);
- mutex_unlock(&q->timer_mutex);
- queuefree(q);
+ result = snd_seq_queue_timer_open(timer->queue);
} else {
return -EPERM;
}
@@ -1813,38 +1751,30 @@ static int snd_seq_ioctl_set_queue_timer(struct snd_seq_client *client,
/* GET_QUEUE_CLIENT ioctl() */
static int snd_seq_ioctl_get_queue_client(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_queue_client info;
+ struct snd_seq_queue_client *info = arg;
int used;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- used = snd_seq_queue_is_used(info.queue, client->number);
+ used = snd_seq_queue_is_used(info->queue, client->number);
if (used < 0)
return -EINVAL;
- info.used = used;
- info.client = client->number;
+ info->used = used;
+ info->client = client->number;
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
return 0;
}
/* SET_QUEUE_CLIENT ioctl() */
static int snd_seq_ioctl_set_queue_client(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_queue_client *info = arg;
int err;
- struct snd_seq_queue_client info;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
- if (info.used >= 0) {
- err = snd_seq_queue_use(info.queue, client->number, info.used);
+ if (info->used >= 0) {
+ err = snd_seq_queue_use(info->queue, client->number, info->used);
if (err < 0)
return err;
}
@@ -1855,77 +1785,71 @@ static int snd_seq_ioctl_set_queue_client(struct snd_seq_client *client,
/* GET_CLIENT_POOL ioctl() */
static int snd_seq_ioctl_get_client_pool(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_client_pool info;
- struct snd_seq_client *cptr;
+ struct snd_seq_client_pool *info = arg;
+ struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- cptr = snd_seq_client_use_ptr(info.client);
+ cptr = client_load_and_use_ptr(info->client);
if (cptr == NULL)
return -ENOENT;
- memset(&info, 0, sizeof(info));
- info.output_pool = cptr->pool->size;
- info.output_room = cptr->pool->room;
- info.output_free = info.output_pool;
- info.output_free = snd_seq_unused_cells(cptr->pool);
+ memset(info, 0, sizeof(*info));
+ info->client = cptr->number;
+ info->output_pool = cptr->pool->size;
+ info->output_room = cptr->pool->room;
+ info->output_free = info->output_pool;
+ info->output_free = snd_seq_unused_cells(cptr->pool);
if (cptr->type == USER_CLIENT) {
- info.input_pool = cptr->data.user.fifo_pool_size;
- info.input_free = info.input_pool;
- if (cptr->data.user.fifo)
- info.input_free = snd_seq_unused_cells(cptr->data.user.fifo->pool);
+ info->input_pool = cptr->data.user.fifo_pool_size;
+ info->input_free = info->input_pool;
+ info->input_free = snd_seq_fifo_unused_cells(cptr->data.user.fifo);
} else {
- info.input_pool = 0;
- info.input_free = 0;
+ info->input_pool = 0;
+ info->input_free = 0;
}
- snd_seq_client_unlock(cptr);
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
return 0;
}
/* SET_CLIENT_POOL ioctl() */
static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_client_pool info;
+ struct snd_seq_client_pool *info = arg;
int rc;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- if (client->number != info.client)
+ if (client->number != info->client)
return -EINVAL; /* can't change other clients */
- if (info.output_pool >= 1 && info.output_pool <= SNDRV_SEQ_MAX_EVENTS &&
+ if (info->output_pool >= 1 && info->output_pool <= SNDRV_SEQ_MAX_EVENTS &&
(! snd_seq_write_pool_allocated(client) ||
- info.output_pool != client->pool->size)) {
+ info->output_pool != client->pool->size)) {
if (snd_seq_write_pool_allocated(client)) {
+ /* is the pool in use? */
+ if (atomic_read(&client->pool->counter))
+ return -EBUSY;
/* remove all existing cells */
- snd_seq_queue_client_leave_cells(client->number);
+ snd_seq_pool_mark_closing(client->pool);
snd_seq_pool_done(client->pool);
}
- client->pool->size = info.output_pool;
+ client->pool->size = info->output_pool;
rc = snd_seq_pool_init(client->pool);
if (rc < 0)
return rc;
}
if (client->type == USER_CLIENT && client->data.user.fifo != NULL &&
- info.input_pool >= 1 &&
- info.input_pool <= SNDRV_SEQ_MAX_CLIENT_EVENTS &&
- info.input_pool != client->data.user.fifo_pool_size) {
+ info->input_pool >= 1 &&
+ info->input_pool <= SNDRV_SEQ_MAX_CLIENT_EVENTS &&
+ info->input_pool != client->data.user.fifo_pool_size) {
/* change pool size */
- rc = snd_seq_fifo_resize(client->data.user.fifo, info.input_pool);
+ rc = snd_seq_fifo_resize(client->data.user.fifo, info->input_pool);
if (rc < 0)
return rc;
- client->data.user.fifo_pool_size = info.input_pool;
+ client->data.user.fifo_pool_size = info->input_pool;
}
- if (info.output_room >= 1 &&
- info.output_room <= client->pool->size) {
- client->pool->room = info.output_room;
+ if (info->output_room >= 1 &&
+ info->output_room <= client->pool->size) {
+ client->pool->room = info->output_room;
}
return snd_seq_ioctl_get_client_pool(client, arg);
@@ -1934,27 +1858,24 @@ static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client,
/* REMOVE_EVENTS ioctl() */
static int snd_seq_ioctl_remove_events(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_remove_events info;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
+ struct snd_seq_remove_events *info = arg;
/*
* Input mostly not implemented XXX.
*/
- if (info.remove_mode & SNDRV_SEQ_REMOVE_INPUT) {
+ if (info->remove_mode & SNDRV_SEQ_REMOVE_INPUT) {
/*
* No restrictions so for a user client we can clear
* the whole fifo
*/
- if (client->type == USER_CLIENT)
+ if (client->type == USER_CLIENT && client->data.user.fifo)
snd_seq_fifo_clear(client->data.user.fifo);
}
- if (info.remove_mode & SNDRV_SEQ_REMOVE_OUTPUT)
- snd_seq_queue_remove_cells(client->number, &info);
+ if (info->remove_mode & SNDRV_SEQ_REMOVE_OUTPUT)
+ snd_seq_queue_remove_cells(client->number, info);
return 0;
}
@@ -1964,65 +1885,42 @@ static int snd_seq_ioctl_remove_events(struct snd_seq_client *client,
* get subscription info
*/
static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- int result;
- struct snd_seq_client *sender = NULL;
- struct snd_seq_client_port *sport = NULL;
- struct snd_seq_port_subscribe subs;
- struct snd_seq_subscribers *p;
-
- if (copy_from_user(&subs, arg, sizeof(subs)))
- return -EFAULT;
+ struct snd_seq_port_subscribe *subs = arg;
+ struct snd_seq_client *sender __free(snd_seq_client) = NULL;
+ struct snd_seq_client_port *sport __free(snd_seq_port) = NULL;
- result = -EINVAL;
- if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
- goto __end;
- if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
- goto __end;
- p = snd_seq_port_get_subscription(&sport->c_src, &subs.dest);
- if (p) {
- result = 0;
- subs = p->info;
- } else
- result = -ENOENT;
-
- __end:
- if (sport)
- snd_seq_port_unlock(sport);
- if (sender)
- snd_seq_client_unlock(sender);
- if (result >= 0) {
- if (copy_to_user(arg, &subs, sizeof(subs)))
- return -EFAULT;
- }
- return result;
+ sender = client_load_and_use_ptr(subs->sender.client);
+ if (!sender)
+ return -EINVAL;
+ sport = snd_seq_port_use_ptr(sender, subs->sender.port);
+ if (!sport)
+ return -EINVAL;
+ return snd_seq_port_get_subscription(&sport->c_src, &subs->dest, subs);
}
/*
* get subscription info - check only its presence
*/
-static int snd_seq_ioctl_query_subs(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg)
{
- int result = -ENXIO;
- struct snd_seq_client *cptr = NULL;
- struct snd_seq_client_port *port = NULL;
- struct snd_seq_query_subs subs;
+ struct snd_seq_query_subs *subs = arg;
+ struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
+ struct snd_seq_client_port *port __free(snd_seq_port) = NULL;
struct snd_seq_port_subs_info *group;
struct list_head *p;
int i;
- if (copy_from_user(&subs, arg, sizeof(subs)))
- return -EFAULT;
-
- if ((cptr = snd_seq_client_use_ptr(subs.root.client)) == NULL)
- goto __end;
- if ((port = snd_seq_port_use_ptr(cptr, subs.root.port)) == NULL)
- goto __end;
+ cptr = client_load_and_use_ptr(subs->root.client);
+ if (!cptr)
+ return -ENXIO;
+ port = snd_seq_port_use_ptr(cptr, subs->root.port);
+ if (!port)
+ return -ENXIO;
- switch (subs.type) {
+ switch (subs->type) {
case SNDRV_SEQ_QUERY_SUBS_READ:
group = &port->c_src;
break;
@@ -2030,43 +1928,31 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client,
group = &port->c_dest;
break;
default:
- goto __end;
+ return -ENXIO;
}
- down_read(&group->list_mutex);
+ guard(rwsem_read)(&group->list_mutex);
/* search for the subscriber */
- subs.num_subs = group->count;
+ subs->num_subs = group->count;
i = 0;
- result = -ENOENT;
list_for_each(p, &group->list_head) {
- if (i++ == subs.index) {
+ if (i++ == subs->index) {
/* found! */
struct snd_seq_subscribers *s;
- if (subs.type == SNDRV_SEQ_QUERY_SUBS_READ) {
+ if (subs->type == SNDRV_SEQ_QUERY_SUBS_READ) {
s = list_entry(p, struct snd_seq_subscribers, src_list);
- subs.addr = s->info.dest;
+ subs->addr = s->info.dest;
} else {
s = list_entry(p, struct snd_seq_subscribers, dest_list);
- subs.addr = s->info.sender;
+ subs->addr = s->info.sender;
}
- subs.flags = s->info.flags;
- subs.queue = s->info.queue;
- result = 0;
- break;
+ subs->flags = s->info.flags;
+ subs->queue = s->info.queue;
+ return 0;
}
}
- up_read(&group->list_mutex);
- __end:
- if (port)
- snd_seq_port_unlock(port);
- if (cptr)
- snd_seq_client_unlock(cptr);
- if (result >= 0) {
- if (copy_to_user(arg, &subs, sizeof(subs)))
- return -EFAULT;
- }
- return result;
+ return -ENOENT;
}
@@ -2074,75 +1960,199 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client,
* query next client
*/
static int snd_seq_ioctl_query_next_client(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_client *cptr = NULL;
- struct snd_seq_client_info info;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
+ struct snd_seq_client_info *info = arg;
+ struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
/* search for next client */
- info.client++;
- if (info.client < 0)
- info.client = 0;
- for (; info.client < SNDRV_SEQ_MAX_CLIENTS; info.client++) {
- cptr = snd_seq_client_use_ptr(info.client);
- if (cptr)
- break; /* found */
+ if (info->client < INT_MAX)
+ info->client++;
+ if (info->client < 0)
+ info->client = 0;
+ for (; info->client < SNDRV_SEQ_MAX_CLIENTS; info->client++) {
+ cptr = client_load_and_use_ptr(info->client);
+ if (cptr) {
+ get_client_info(cptr, info);
+ return 0; /* found */
+ }
}
- if (cptr == NULL)
- return -ENOENT;
-
- get_client_info(cptr, &info);
- snd_seq_client_unlock(cptr);
-
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
+ return -ENOENT;
}
/*
* query next port
*/
static int snd_seq_ioctl_query_next_port(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_client *cptr;
- struct snd_seq_client_port *port = NULL;
- struct snd_seq_port_info info;
+ struct snd_seq_port_info *info = arg;
+ struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
+ struct snd_seq_client_port *port __free(snd_seq_port) = NULL;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
- cptr = snd_seq_client_use_ptr(info.addr.client);
+ cptr = client_load_and_use_ptr(info->addr.client);
if (cptr == NULL)
return -ENXIO;
/* search for next port */
- info.addr.port++;
- port = snd_seq_port_query_nearest(cptr, &info);
- if (port == NULL) {
- snd_seq_client_unlock(cptr);
+ info->addr.port++;
+ port = snd_seq_port_query_nearest(cptr, info);
+ if (port == NULL)
return -ENOENT;
- }
/* get port info */
- info.addr = port->addr;
- snd_seq_get_port_info(port, &info);
- snd_seq_port_unlock(port);
- snd_seq_client_unlock(cptr);
+ info->addr = port->addr;
+ snd_seq_get_port_info(port, info);
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
return 0;
}
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+#define NUM_UMP_INFOS (SNDRV_UMP_MAX_BLOCKS + 1)
+
+static void free_ump_info(struct snd_seq_client *client)
+{
+ int i;
+
+ if (!client->ump_info)
+ return;
+ for (i = 0; i < NUM_UMP_INFOS; i++)
+ kfree(client->ump_info[i]);
+ kfree(client->ump_info);
+ client->ump_info = NULL;
+}
+
+static void terminate_ump_info_strings(void *p, int type)
+{
+ if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT) {
+ struct snd_ump_endpoint_info *ep = p;
+ ep->name[sizeof(ep->name) - 1] = 0;
+ } else {
+ struct snd_ump_block_info *bp = p;
+ bp->name[sizeof(bp->name) - 1] = 0;
+ }
+}
+
+#ifdef CONFIG_SND_PROC_FS
+static void dump_ump_info(struct snd_info_buffer *buffer,
+ struct snd_seq_client *client)
+{
+ struct snd_ump_endpoint_info *ep;
+ struct snd_ump_block_info *bp;
+ int i;
+
+ if (!client->ump_info)
+ return;
+ ep = client->ump_info[SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT];
+ if (ep && *ep->name)
+ snd_iprintf(buffer, " UMP Endpoint: \"%s\"\n", ep->name);
+ for (i = 0; i < SNDRV_UMP_MAX_BLOCKS; i++) {
+ bp = client->ump_info[i + 1];
+ if (bp && *bp->name) {
+ snd_iprintf(buffer, " UMP Block %d: \"%s\" [%s]\n",
+ i, bp->name,
+ bp->active ? "Active" : "Inactive");
+ snd_iprintf(buffer, " Groups: %d-%d\n",
+ bp->first_group + 1,
+ bp->first_group + bp->num_groups);
+ }
+ }
+}
+#endif
+
+/* UMP-specific ioctls -- called directly without data copy */
+static int snd_seq_ioctl_client_ump_info(struct snd_seq_client *caller,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct snd_seq_client_ump_info __user *argp =
+ (struct snd_seq_client_ump_info __user *)arg;
+ struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
+ int client, type, err = 0;
+ size_t size;
+ void *p;
+
+ if (get_user(client, &argp->client) || get_user(type, &argp->type))
+ return -EFAULT;
+ if (cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO &&
+ caller->number != client)
+ return -EPERM;
+ if (type < 0 || type >= NUM_UMP_INFOS)
+ return -EINVAL;
+ if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT)
+ size = sizeof(struct snd_ump_endpoint_info);
+ else
+ size = sizeof(struct snd_ump_block_info);
+ cptr = client_load_and_use_ptr(client);
+ if (!cptr)
+ return -ENOENT;
+
+ scoped_guard(mutex, &cptr->ioctl_mutex) {
+ if (!cptr->midi_version) {
+ err = -EBADFD;
+ break;
+ }
+
+ if (cmd == SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO) {
+ if (!cptr->ump_info)
+ p = NULL;
+ else
+ p = cptr->ump_info[type];
+ if (!p) {
+ err = -ENODEV;
+ break;
+ }
+ if (copy_to_user(argp->info, p, size)) {
+ err = -EFAULT;
+ break;
+ }
+ } else {
+ if (cptr->type != USER_CLIENT) {
+ err = -EBADFD;
+ break;
+ }
+ if (!cptr->ump_info) {
+ cptr->ump_info = kcalloc(NUM_UMP_INFOS,
+ sizeof(void *), GFP_KERNEL);
+ if (!cptr->ump_info) {
+ err = -ENOMEM;
+ break;
+ }
+ }
+ p = memdup_user(argp->info, size);
+ if (IS_ERR(p)) {
+ err = PTR_ERR(p);
+ break;
+ }
+ kfree(cptr->ump_info[type]);
+ terminate_ump_info_strings(p, type);
+ cptr->ump_info[type] = p;
+ }
+
+ }
+ if (!err && cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO) {
+ if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT)
+ snd_seq_system_ump_notify(client, 0,
+ SNDRV_SEQ_EVENT_UMP_EP_CHANGE,
+ false);
+ else
+ snd_seq_system_ump_notify(client, type - 1,
+ SNDRV_SEQ_EVENT_UMP_BLOCK_CHANGE,
+ false);
+ }
+ return err;
+}
+#endif
+
/* -------------------------------------------------------- */
-static struct seq_ioctl_table {
+static const struct ioctl_handler {
unsigned int cmd;
- int (*func)(struct snd_seq_client *client, void __user * arg);
-} ioctl_tables[] = {
+ int (*func)(struct snd_seq_client *client, void *arg);
+} ioctl_handlers[] = {
+ { SNDRV_SEQ_IOCTL_PVERSION, snd_seq_ioctl_pversion },
+ { SNDRV_SEQ_IOCTL_USER_PVERSION, snd_seq_ioctl_user_pversion },
+ { SNDRV_SEQ_IOCTL_CLIENT_ID, snd_seq_ioctl_client_id },
{ SNDRV_SEQ_IOCTL_SYSTEM_INFO, snd_seq_ioctl_system_info },
{ SNDRV_SEQ_IOCTL_RUNNING_MODE, snd_seq_ioctl_running_mode },
{ SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, snd_seq_ioctl_get_client_info },
@@ -2175,40 +2185,76 @@ static struct seq_ioctl_table {
{ 0, NULL },
};
-static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd,
- void __user *arg)
+static long snd_seq_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
- struct seq_ioctl_table *p;
+ struct snd_seq_client *client = file->private_data;
+ /* To use kernel stack for ioctl data. */
+ union {
+ int pversion;
+ int client_id;
+ struct snd_seq_system_info system_info;
+ struct snd_seq_running_info running_info;
+ struct snd_seq_client_info client_info;
+ struct snd_seq_port_info port_info;
+ struct snd_seq_port_subscribe port_subscribe;
+ struct snd_seq_queue_info queue_info;
+ struct snd_seq_queue_status queue_status;
+ struct snd_seq_queue_tempo tempo;
+ struct snd_seq_queue_timer queue_timer;
+ struct snd_seq_queue_client queue_client;
+ struct snd_seq_client_pool client_pool;
+ struct snd_seq_remove_events remove_events;
+ struct snd_seq_query_subs query_subs;
+ } buf;
+ const struct ioctl_handler *handler;
+ unsigned long size;
+ int err;
+
+ if (snd_BUG_ON(!client))
+ return -ENXIO;
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ /* exception - handling large data */
switch (cmd) {
- case SNDRV_SEQ_IOCTL_PVERSION:
- /* return sequencer version number */
- return put_user(SNDRV_SEQ_VERSION, (int __user *)arg) ? -EFAULT : 0;
- case SNDRV_SEQ_IOCTL_CLIENT_ID:
- /* return the id of this client */
- return put_user(client->number, (int __user *)arg) ? -EFAULT : 0;
+ case SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO:
+ case SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO:
+ return snd_seq_ioctl_client_ump_info(client, cmd, arg);
}
+#endif
- if (! arg)
- return -EFAULT;
- for (p = ioctl_tables; p->cmd; p++) {
- if (p->cmd == cmd)
- return p->func(client, arg);
+ for (handler = ioctl_handlers; handler->cmd > 0; ++handler) {
+ if (handler->cmd == cmd)
+ break;
}
- snd_printd("seq unknown ioctl() 0x%x (type='%c', number=0x%02x)\n",
- cmd, _IOC_TYPE(cmd), _IOC_NR(cmd));
- return -ENOTTY;
-}
+ if (handler->cmd == 0)
+ return -ENOTTY;
+ memset(&buf, 0, sizeof(buf));
-static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct snd_seq_client *client = file->private_data;
+ /*
+ * All of ioctl commands for ALSA sequencer get an argument of size
+ * within 13 bits. We can safely pick up the size from the command.
+ */
+ size = _IOC_SIZE(handler->cmd);
+ if (handler->cmd & IOC_IN) {
+ if (copy_from_user(&buf, (const void __user *)arg, size))
+ return -EFAULT;
+ }
- if (snd_BUG_ON(!client))
- return -ENXIO;
-
- return snd_seq_do_ioctl(client, cmd, (void __user *) arg);
+ scoped_guard(mutex, &client->ioctl_mutex) {
+ err = handler->func(client, &buf);
+ }
+ if (err >= 0) {
+ /* Some commands includes a bug in 'dir' field. */
+ if (handler->cmd == SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT ||
+ handler->cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_POOL ||
+ (handler->cmd & IOC_OUT))
+ if (copy_to_user((void __user *)arg, &buf, size))
+ return -EFAULT;
+ }
+
+ return err;
}
#ifdef CONFIG_COMPAT
@@ -2235,33 +2281,32 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
if (card == NULL && client_index >= SNDRV_SEQ_GLOBAL_CLIENTS)
return -EINVAL;
- if (mutex_lock_interruptible(&register_mutex))
- return -ERESTARTSYS;
+ scoped_guard(mutex, &register_mutex) {
- if (card) {
- client_index += SNDRV_SEQ_GLOBAL_CLIENTS
- + card->number * SNDRV_SEQ_CLIENTS_PER_CARD;
- if (client_index >= SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN)
- client_index = -1;
- }
+ if (card) {
+ client_index += SNDRV_SEQ_GLOBAL_CLIENTS
+ + card->number * SNDRV_SEQ_CLIENTS_PER_CARD;
+ if (client_index >= SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN)
+ client_index = -1;
+ }
- /* empty write queue as default */
- client = seq_create_client1(client_index, 0);
- if (client == NULL) {
- mutex_unlock(&register_mutex);
- return -EBUSY; /* failure code */
- }
- usage_alloc(&client_usage, 1);
+ /* empty write queue as default */
+ client = seq_create_client1(client_index, 0);
+ if (client == NULL)
+ return -EBUSY; /* failure code */
+ usage_alloc(&client_usage, 1);
- client->accept_input = 1;
- client->accept_output = 1;
+ client->accept_input = 1;
+ client->accept_output = 1;
+ client->data.kernel.card = card;
+ client->user_pversion = SNDRV_SEQ_VERSION;
- va_start(args, name_fmt);
- vsnprintf(client->name, sizeof(client->name), name_fmt, args);
- va_end(args);
+ va_start(args, name_fmt);
+ vsnprintf(client->name, sizeof(client->name), name_fmt, args);
+ va_end(args);
- client->type = KERNEL_CLIENT;
- mutex_unlock(&register_mutex);
+ client->type = KERNEL_CLIENT;
+ }
/* make others aware this new client */
snd_seq_system_client_ev_client_start(client->number);
@@ -2269,7 +2314,6 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
/* return client number to caller */
return client->number;
}
-
EXPORT_SYMBOL(snd_seq_create_kernel_client);
/* exported to kernel modules */
@@ -2288,26 +2332,27 @@ int snd_seq_delete_kernel_client(int client)
kfree(ptr);
return 0;
}
-
EXPORT_SYMBOL(snd_seq_delete_kernel_client);
-/* skeleton to enqueue event, called from snd_seq_kernel_client_enqueue
- * and snd_seq_kernel_client_enqueue_blocking
+/*
+ * exported, called by kernel clients to enqueue events (w/o blocking)
+ *
+ * RETURN VALUE: zero if succeed, negative if error
*/
-static int kernel_client_enqueue(int client, struct snd_seq_event *ev,
- struct file *file, int blocking,
- int atomic, int hop)
+int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev,
+ struct file *file, bool blocking)
{
- struct snd_seq_client *cptr;
- int result;
+ struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
if (snd_BUG_ON(!ev))
return -EINVAL;
- if (ev->type == SNDRV_SEQ_EVENT_NONE)
- return 0; /* ignore this */
- if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
- return -EINVAL; /* quoted events can't be enqueued */
+ if (!snd_seq_ev_is_ump(ev)) {
+ if (ev->type == SNDRV_SEQ_EVENT_NONE)
+ return 0; /* ignore this */
+ if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
+ return -EINVAL; /* quoted events can't be enqueued */
+ }
/* fill in client number */
ev->source.client = client;
@@ -2315,46 +2360,21 @@ static int kernel_client_enqueue(int client, struct snd_seq_event *ev,
if (check_event_type_and_length(ev))
return -EINVAL;
- cptr = snd_seq_client_use_ptr(client);
+ cptr = client_load_and_use_ptr(client);
if (cptr == NULL)
return -EINVAL;
- if (! cptr->accept_output)
- result = -EPERM;
- else /* send it */
- result = snd_seq_client_enqueue_event(cptr, ev, file, blocking, atomic, hop);
-
- snd_seq_client_unlock(cptr);
- return result;
-}
-
-/*
- * exported, called by kernel clients to enqueue events (w/o blocking)
- *
- * RETURN VALUE: zero if succeed, negative if error
- */
-int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event * ev,
- int atomic, int hop)
-{
- return kernel_client_enqueue(client, ev, NULL, 0, atomic, hop);
+ if (!cptr->accept_output) {
+ return -EPERM;
+ } else { /* send it */
+ guard(mutex)(&cptr->ioctl_mutex);
+ return snd_seq_client_enqueue_event(cptr, ev, file, blocking,
+ false, 0,
+ &cptr->ioctl_mutex);
+ }
}
-
EXPORT_SYMBOL(snd_seq_kernel_client_enqueue);
-/*
- * exported, called by kernel clients to enqueue events (with blocking)
- *
- * RETURN VALUE: zero if succeed, negative if error
- */
-int snd_seq_kernel_client_enqueue_blocking(int client, struct snd_seq_event * ev,
- struct file *file,
- int atomic, int hop)
-{
- return kernel_client_enqueue(client, ev, file, 1, atomic, hop);
-}
-
-EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking);
-
/*
* exported, called by kernel clients to dispatch events directly to other
* clients, bypassing the queues. Event time-stamp will be updated.
@@ -2365,8 +2385,7 @@ EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking);
int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev,
int atomic, int hop)
{
- struct snd_seq_client *cptr;
- int result;
+ struct snd_seq_client *cptr __free(snd_seq_client) = NULL;
if (snd_BUG_ON(!ev))
return -EINVAL;
@@ -2383,37 +2402,64 @@ int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev,
return -EINVAL;
if (!cptr->accept_output)
- result = -EPERM;
+ return -EPERM;
else
- result = snd_seq_deliver_event(cptr, ev, atomic, hop);
-
- snd_seq_client_unlock(cptr);
- return result;
+ return snd_seq_deliver_event(cptr, ev, atomic, hop);
}
-
EXPORT_SYMBOL(snd_seq_kernel_client_dispatch);
-/*
- * exported, called by kernel clients to perform same functions as with
- * userland ioctl()
+static int call_seq_client_ctl(struct snd_seq_client *client,
+ unsigned int cmd, void *arg)
+{
+ const struct ioctl_handler *handler;
+
+ for (handler = ioctl_handlers; handler->cmd > 0; ++handler) {
+ if (handler->cmd == cmd)
+ return handler->func(client, arg);
+ }
+
+ pr_debug("ALSA: seq unknown ioctl() 0x%x (type='%c', number=0x%02x)\n",
+ cmd, _IOC_TYPE(cmd), _IOC_NR(cmd));
+ return -ENOTTY;
+}
+
+/**
+ * snd_seq_kernel_client_ctl - operate a command for a client with data in
+ * kernel space.
+ * @clientid: A numerical ID for a client.
+ * @cmd: An ioctl(2) command for ALSA sequencer operation.
+ * @arg: A pointer to data in kernel space.
+ *
+ * Against its name, both kernel/application client can be handled by this
+ * kernel API. A pointer of 'arg' argument should be in kernel space.
+ *
+ * Return: 0 at success. Negative error code at failure.
*/
int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg)
{
struct snd_seq_client *client;
- mm_segment_t fs;
- int result;
client = clientptr(clientid);
if (client == NULL)
return -ENXIO;
- fs = snd_enter_user();
- result = snd_seq_do_ioctl(client, cmd, (void __user *)arg);
- snd_leave_user(fs);
- return result;
-}
+ return call_seq_client_ctl(client, cmd, arg);
+}
EXPORT_SYMBOL(snd_seq_kernel_client_ctl);
+/* a similar like above but taking locks; used only from OSS sequencer layer */
+int snd_seq_kernel_client_ioctl(int clientid, unsigned int cmd, void *arg)
+{
+ struct snd_seq_client *client __free(snd_seq_client) = NULL;
+
+ client = client_load_and_use_ptr(clientid);
+ if (!client)
+ return -ENXIO;
+ guard(mutex)(&client->ioctl_mutex);
+ return call_seq_client_ctl(client, cmd, arg);
+}
+EXPORT_SYMBOL_GPL(snd_seq_kernel_client_ioctl);
+
/* exported (for OSS emulator) */
int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait)
{
@@ -2423,18 +2469,30 @@ int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table
if (client == NULL)
return -ENXIO;
- if (! snd_seq_write_pool_allocated(client))
- return 1;
if (snd_seq_pool_poll_wait(client->pool, file, wait))
return 1;
return 0;
}
-
EXPORT_SYMBOL(snd_seq_kernel_client_write_poll);
+/* get a sequencer client object; for internal use from a kernel client */
+struct snd_seq_client *snd_seq_kernel_client_get(int id)
+{
+ return snd_seq_client_use_ptr(id);
+}
+EXPORT_SYMBOL_GPL(snd_seq_kernel_client_get);
+
+/* put a sequencer client object; for internal use from a kernel client */
+void snd_seq_kernel_client_put(struct snd_seq_client *cptr)
+{
+ if (cptr)
+ snd_seq_client_unref(cptr);
+}
+EXPORT_SYMBOL_GPL(snd_seq_kernel_client_put);
+
/*---------------------------------------------------------------------------*/
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* /proc interface
*/
@@ -2446,11 +2504,9 @@ static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer,
struct snd_seq_subscribers *s;
int count = 0;
- down_read(&group->list_mutex);
- if (list_empty(&group->list_head)) {
- up_read(&group->list_mutex);
+ guard(rwsem_read)(&group->list_mutex);
+ if (list_empty(&group->list_head))
return;
- }
snd_iprintf(buffer, msg);
list_for_each(p, &group->list_head) {
if (is_src)
@@ -2467,7 +2523,6 @@ static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer,
if (group->exclusive)
snd_iprintf(buffer, "[ex]");
}
- up_read(&group->list_mutex);
snd_iprintf(buffer, "\n");
}
@@ -2477,35 +2532,62 @@ static void snd_seq_info_dump_subscribers(struct snd_info_buffer *buffer,
#define FLAG_PERM_DUPLEX(perm) ((perm) & SNDRV_SEQ_PORT_CAP_DUPLEX ? 'X' : '-')
+static const char *port_direction_name(unsigned char dir)
+{
+ static const char *names[4] = {
+ "-", "In", "Out", "In/Out"
+ };
+
+ if (dir > SNDRV_SEQ_PORT_DIR_BIDIRECTION)
+ return "Invalid";
+ return names[dir];
+}
+
static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer,
struct snd_seq_client *client)
{
struct snd_seq_client_port *p;
- mutex_lock(&client->ports_mutex);
+ guard(mutex)(&client->ports_mutex);
list_for_each_entry(p, &client->ports_list_head, list) {
- snd_iprintf(buffer, " Port %3d : \"%s\" (%c%c%c%c)\n",
+ if (p->capability & SNDRV_SEQ_PORT_CAP_INACTIVE)
+ continue;
+ snd_iprintf(buffer, " Port %3d : \"%s\" (%c%c%c%c) [%s]",
p->addr.port, p->name,
FLAG_PERM_RD(p->capability),
FLAG_PERM_WR(p->capability),
FLAG_PERM_EX(p->capability),
- FLAG_PERM_DUPLEX(p->capability));
+ FLAG_PERM_DUPLEX(p->capability),
+ port_direction_name(p->direction));
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ if (snd_seq_client_is_midi2(client) && p->is_midi1)
+ snd_iprintf(buffer, " [MIDI1]");
+#endif
+ snd_iprintf(buffer, "\n");
snd_seq_info_dump_subscribers(buffer, &p->c_src, 1, " Connecting To: ");
snd_seq_info_dump_subscribers(buffer, &p->c_dest, 0, " Connected From: ");
}
- mutex_unlock(&client->ports_mutex);
}
-
-void snd_seq_info_pool(struct snd_info_buffer *buffer,
- struct snd_seq_pool *pool, char *space);
+static const char *midi_version_string(unsigned int version)
+{
+ switch (version) {
+ case SNDRV_SEQ_CLIENT_LEGACY_MIDI:
+ return "Legacy";
+ case SNDRV_SEQ_CLIENT_UMP_MIDI_1_0:
+ return "UMP MIDI1";
+ case SNDRV_SEQ_CLIENT_UMP_MIDI_2_0:
+ return "UMP MIDI2";
+ default:
+ return "Unknown";
+ }
+}
/* exported to seq_info.c */
void snd_seq_info_clients_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
int c;
- struct snd_seq_client *client;
snd_iprintf(buffer, "Client info\n");
snd_iprintf(buffer, " cur clients : %d\n", client_usage.cur);
@@ -2515,17 +2597,22 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry,
/* list the client table */
for (c = 0; c < SNDRV_SEQ_MAX_CLIENTS; c++) {
- client = snd_seq_client_use_ptr(c);
+ struct snd_seq_client *client __free(snd_seq_client) = NULL;
+
+ client = client_load_and_use_ptr(c);
if (client == NULL)
continue;
- if (client->type == NO_CLIENT) {
- snd_seq_client_unlock(client);
+ if (client->type == NO_CLIENT)
continue;
- }
- snd_iprintf(buffer, "Client %3d : \"%s\" [%s]\n",
+ guard(mutex)(&client->ioctl_mutex);
+ snd_iprintf(buffer, "Client %3d : \"%s\" [%s %s]\n",
c, client->name,
- client->type == USER_CLIENT ? "User" : "Kernel");
+ client->type == USER_CLIENT ? "User" : "Kernel",
+ midi_version_string(client->midi_version));
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ dump_ump_info(buffer, client);
+#endif
snd_seq_info_dump_ports(buffer, client);
if (snd_seq_write_pool_allocated(client)) {
snd_iprintf(buffer, " Output pool :\n");
@@ -2536,10 +2623,9 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry,
snd_iprintf(buffer, " Input pool :\n");
snd_seq_info_pool(buffer, client->data.user.fifo->pool, " ");
}
- snd_seq_client_unlock(client);
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
/*---------------------------------------------------------------------------*/
@@ -2555,12 +2641,13 @@ static const struct file_operations snd_seq_f_ops =
.write = snd_seq_write,
.open = snd_seq_open,
.release = snd_seq_release,
- .llseek = no_llseek,
.poll = snd_seq_poll,
.unlocked_ioctl = snd_seq_ioctl,
.compat_ioctl = snd_seq_ioctl_compat,
};
+static struct device *seq_dev;
+
/*
* register sequencer device
*/
@@ -2568,17 +2655,20 @@ int __init snd_sequencer_device_init(void)
{
int err;
- if (mutex_lock_interruptible(&register_mutex))
- return -ERESTARTSYS;
+ err = snd_device_alloc(&seq_dev, NULL);
+ if (err < 0)
+ return err;
+ dev_set_name(seq_dev, "seq");
- if ((err = snd_register_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0,
- &snd_seq_f_ops, NULL, "seq")) < 0) {
- mutex_unlock(&register_mutex);
+ scoped_guard(mutex, &register_mutex) {
+ err = snd_register_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0,
+ &snd_seq_f_ops, NULL, seq_dev);
+ }
+ if (err < 0) {
+ put_device(seq_dev);
return err;
}
- mutex_unlock(&register_mutex);
-
return 0;
}
@@ -2587,7 +2677,8 @@ int __init snd_sequencer_device_init(void)
/*
* unregister sequencer device
*/
-void __exit snd_sequencer_device_done(void)
+void snd_sequencer_device_done(void)
{
- snd_unregister_device(SNDRV_DEVICE_TYPE_SEQUENCER, NULL, 0);
+ snd_unregister_device(seq_dev);
+ put_device(seq_dev);
}
diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h
index 20f0a725ec7d..ece02c58db70 100644
--- a/sound/core/seq/seq_clientmgr.h
+++ b/sound/core/seq/seq_clientmgr.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA sequencer Client Manager
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __SND_SEQ_CLIENTMGR_H
#define __SND_SEQ_CLIENTMGR_H
@@ -27,12 +12,12 @@
#include "seq_ports.h"
#include "seq_lock.h"
-
/* client manager */
struct snd_seq_user_client {
struct file *file; /* file struct of client */
/* ... */
+ struct pid *owner;
/* fifo */
struct snd_seq_fifo *fifo; /* queue for incoming events */
@@ -41,6 +26,7 @@ struct snd_seq_user_client {
struct snd_seq_kernel_client {
/* ... */
+ struct snd_card *card;
};
@@ -48,10 +34,13 @@ struct snd_seq_client {
snd_seq_client_type_t type;
unsigned int accept_input: 1,
accept_output: 1;
+ unsigned int midi_version;
+ unsigned int user_pversion;
char name[64]; /* client name */
int number; /* client number */
unsigned int filter; /* filter flags */
DECLARE_BITMAP(event_filter, 256);
+ unsigned short group_filter;
snd_use_lock_t use_lock;
int event_lost;
/* ports */
@@ -59,7 +48,9 @@ struct snd_seq_client {
struct list_head ports_list_head;
rwlock_t ports_lock;
struct mutex ports_mutex;
+ struct mutex ioctl_mutex;
int convert32; /* convert 32->64bit */
+ int ump_endpoint_port;
/* output pool */
struct snd_seq_pool *pool; /* memory pool for this client */
@@ -68,6 +59,9 @@ struct snd_seq_client {
struct snd_seq_user_client user;
struct snd_seq_kernel_client kernel;
} data;
+
+ /* for UMP */
+ void **ump_info;
};
/* usage statistics */
@@ -84,20 +78,50 @@ void snd_sequencer_device_done(void);
/* get locked pointer to client */
struct snd_seq_client *snd_seq_client_use_ptr(int clientid);
+static inline struct snd_seq_client *
+snd_seq_client_ref(struct snd_seq_client *client)
+{
+ snd_use_lock_use(&client->use_lock);
+ return client;
+}
+
/* unlock pointer to client */
-#define snd_seq_client_unlock(client) snd_use_lock_free(&(client)->use_lock)
+static inline void snd_seq_client_unref(struct snd_seq_client *client)
+{
+ snd_use_lock_free(&client->use_lock);
+}
+
+DEFINE_FREE(snd_seq_client, struct snd_seq_client *, if (!IS_ERR_OR_NULL(_T)) snd_seq_client_unref(_T))
/* dispatch event to client(s) */
int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop);
-/* exported to other modules */
-int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event *ev, int atomic, int hop);
-int snd_seq_kernel_client_enqueue_blocking(int client, struct snd_seq_event * ev,
- struct file *file, int atomic, int hop);
int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait);
int snd_seq_client_notify_subscription(int client, int port,
struct snd_seq_port_subscribe *info, int evtype);
+int __snd_seq_deliver_single_event(struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ int atomic, int hop);
+
+/* only for OSS sequencer */
+int snd_seq_kernel_client_ioctl(int clientid, unsigned int cmd, void *arg);
+
extern int seq_client_load[15];
+/* for internal use between kernel sequencer clients */
+struct snd_seq_client *snd_seq_kernel_client_get(int client);
+void snd_seq_kernel_client_put(struct snd_seq_client *cptr);
+
+static inline bool snd_seq_client_is_ump(struct snd_seq_client *c)
+{
+ return c->midi_version != SNDRV_SEQ_CLIENT_LEGACY_MIDI;
+}
+
+static inline bool snd_seq_client_is_midi2(struct snd_seq_client *c)
+{
+ return c->midi_version == SNDRV_SEQ_CLIENT_UMP_MIDI_2_0;
+}
+
#endif
diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c
index 81f7c109dc46..643af4c1e838 100644
--- a/sound/core/seq/seq_compat.c
+++ b/sound/core/seq/seq_compat.c
@@ -1,21 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 32bit -> 64bit ioctl wrapper for sequencer API
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/* This file included from seq.c */
@@ -45,32 +31,28 @@ struct snd_seq_port_info32 {
static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned int cmd,
struct snd_seq_port_info32 __user *data32)
{
- int err = -EFAULT;
- struct snd_seq_port_info *data;
- mm_segment_t fs;
+ struct snd_seq_port_info *data __free(kfree) = NULL;
+ int err;
- data = memdup_user(data32, sizeof(*data32));
- if (IS_ERR(data))
- return PTR_ERR(data);
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
- if (get_user(data->flags, &data32->flags) ||
+ if (copy_from_user(data, data32, sizeof(*data32)) ||
+ get_user(data->flags, &data32->flags) ||
get_user(data->time_queue, &data32->time_queue))
- goto error;
+ return -EFAULT;
data->kernel = NULL;
- fs = snd_enter_user();
- err = snd_seq_do_ioctl(client, cmd, data);
- snd_leave_user(fs);
+ err = snd_seq_kernel_client_ctl(client->number, cmd, data);
if (err < 0)
- goto error;
+ return err;
if (copy_to_user(data32, data, sizeof(*data32)) ||
put_user(data->flags, &data32->flags) ||
put_user(data->time_queue, &data32->time_queue))
- err = -EFAULT;
+ return -EFAULT;
- error:
- kfree(data);
return err;
}
@@ -97,10 +79,13 @@ static long snd_seq_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
switch (cmd) {
case SNDRV_SEQ_IOCTL_PVERSION:
+ case SNDRV_SEQ_IOCTL_USER_PVERSION:
case SNDRV_SEQ_IOCTL_CLIENT_ID:
case SNDRV_SEQ_IOCTL_SYSTEM_INFO:
case SNDRV_SEQ_IOCTL_GET_CLIENT_INFO:
case SNDRV_SEQ_IOCTL_SET_CLIENT_INFO:
+ case SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO:
+ case SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO:
case SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT:
case SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT:
case SNDRV_SEQ_IOCTL_CREATE_QUEUE:
@@ -122,7 +107,7 @@ static long snd_seq_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
case SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION:
case SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT:
case SNDRV_SEQ_IOCTL_RUNNING_MODE:
- return snd_seq_do_ioctl(client, cmd, argp);
+ return snd_seq_ioctl(file, cmd, arg);
case SNDRV_SEQ_IOCTL_CREATE_PORT32:
return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, argp);
case SNDRV_SEQ_IOCTL_DELETE_PORT32:
diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c
deleted file mode 100644
index 1f997675c893..000000000000
--- a/sound/core/seq/seq_device.c
+++ /dev/null
@@ -1,572 +0,0 @@
-/*
- * ALSA sequencer device management
- * Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- *
- *----------------------------------------------------------------
- *
- * This device handler separates the card driver module from sequencer
- * stuff (sequencer core, synth drivers, etc), so that user can avoid
- * to spend unnecessary resources e.g. if he needs only listening to
- * MP3s.
- *
- * The card (or lowlevel) driver creates a sequencer device entry
- * via snd_seq_device_new(). This is an entry pointer to communicate
- * with the sequencer device "driver", which is involved with the
- * actual part to communicate with the sequencer core.
- * Each sequencer device entry has an id string and the corresponding
- * driver with the same id is loaded when required. For example,
- * lowlevel codes to access emu8000 chip on sbawe card are included in
- * emu8000-synth module. To activate this module, the hardware
- * resources like i/o port are passed via snd_seq_device argument.
- *
- */
-
-#include <linux/init.h>
-#include <sound/core.h>
-#include <sound/info.h>
-#include <sound/seq_device.h>
-#include <sound/seq_kernel.h>
-#include <sound/initval.h>
-#include <linux/kmod.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-
-MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
-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)
-
-struct ops_list {
- char id[ID_LEN]; /* driver id */
- int driver; /* driver state */
- int used; /* reference counter */
- int argsize; /* argument size */
-
- /* operators */
- struct snd_seq_dev_ops ops;
-
- /* registred 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;
-
- struct list_head list; /* next driver */
-};
-
-
-static LIST_HEAD(opslist);
-static int num_ops;
-static DEFINE_MUTEX(ops_mutex);
-#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);
-
-/*
- * show all drivers and their status
- */
-
-#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);
-}
-#endif
-
-/*
- * load all registered drivers (called from seq_clientmgr.c)
- */
-
-#ifdef CONFIG_MODULES
-/* avoid auto-loading during module_init() */
-static int snd_seq_in_init;
-void snd_seq_autoload_lock(void)
-{
- snd_seq_in_init++;
-}
-
-void snd_seq_autoload_unlock(void)
-{
- snd_seq_in_init--;
-}
-#endif
-
-void snd_seq_device_load_drivers(void)
-{
-#ifdef CONFIG_MODULES
- struct ops_list *ops;
-
- /* Calling request_module during module_init()
- * may cause blocking.
- */
- if (snd_seq_in_init)
- return;
-
- mutex_lock(&ops_mutex);
- list_for_each_entry(ops, &opslist, list) {
- if (! (ops->driver & DRIVER_LOADED) &&
- ! (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);
-#endif
-}
-
-/*
- * register a sequencer device
- * card = card info (NULL allowed)
- * device = device number (if any)
- * 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)
-{
- struct snd_seq_device *dev;
- struct ops_list *ops;
- int err;
- static struct snd_device_ops dops = {
- .dev_free = snd_seq_device_dev_free,
- .dev_register = snd_seq_device_dev_register,
- .dev_disconnect = snd_seq_device_dev_disconnect,
- };
-
- if (result)
- *result = NULL;
-
- 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);
- return -ENOMEM;
- }
-
- /* set up device info */
- dev->card = card;
- dev->device = device;
- strlcpy(dev->id, id, sizeof(dev->id));
- 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);
-
- unlock_driver(ops);
-
- if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) {
- snd_seq_device_free(dev);
- return err;
- }
-
- if (result)
- *result = dev;
-
- return 0;
-}
-
-/*
- * 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
- */
-static int snd_seq_device_dev_register(struct snd_device *device)
-{
- 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);
-
- unlock_driver(ops);
- return 0;
-}
-
-/*
- * disconnect the device
- */
-static int snd_seq_device_dev_disconnect(struct snd_device *device)
-{
- 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);
-
- unlock_driver(ops);
- return 0;
-}
-
-/*
- * register device driver
- * 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)
-{
- struct ops_list *ops;
- struct snd_seq_device *dev;
-
- if (id == NULL || entry == NULL ||
- entry->init_device == NULL || entry->free_device == NULL)
- return -EINVAL;
-
- snd_seq_autoload_lock();
- ops = find_driver(id, 1);
- if (ops == NULL) {
- snd_seq_autoload_unlock();
- return -ENOMEM;
- }
- if (ops->driver & DRIVER_LOADED) {
- snd_printk(KERN_WARNING "driver_register: driver '%s' already exists\n", id);
- unlock_driver(ops);
- snd_seq_autoload_unlock();
- 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);
- snd_seq_autoload_unlock();
-
- return 0;
-}
-
-
-/*
- * create driver record
- */
-static struct ops_list * create_driver(char *id)
-{
- 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;
-}
-
-
-/*
- * unregister the specified driver
- */
-int snd_seq_device_unregister_driver(char *id)
-{
- struct ops_list *ops;
- struct snd_seq_device *dev;
-
- ops = find_driver(id, 0);
- if (ops == NULL)
- return -ENXIO;
- if (! (ops->driver & DRIVER_LOADED) ||
- (ops->driver & DRIVER_LOCKED)) {
- snd_printk(KERN_ERR "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)
- snd_printk(KERN_ERR "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();
-
- 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) {
- snd_printk(KERN_ERR "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 {
- snd_printk(KERN_ERR "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) {
- snd_printk(KERN_ERR "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 {
- snd_printk(KERN_ERR "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);
-}
-
-
-/*
- * module part
- */
-
-static int __init alsa_seq_device_init(void)
-{
-#ifdef CONFIG_PROC_FS
- info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
- snd_seq_root);
- if (info_entry == NULL)
- return -ENOMEM;
- info_entry->content = SNDRV_INFO_CONTENT_TEXT;
- info_entry->c.text.read = snd_seq_device_info;
- if (snd_info_register(info_entry) < 0) {
- snd_info_free_entry(info_entry);
- return -ENOMEM;
- }
-#endif
- return 0;
-}
-
-static void __exit alsa_seq_device_exit(void)
-{
- remove_drivers();
-#ifdef CONFIG_PROC_FS
- snd_info_free_entry(info_entry);
-#endif
- if (num_ops)
- snd_printk(KERN_ERR "drivers not released (%d)\n", num_ops);
-}
-
-module_init(alsa_seq_device_init)
-module_exit(alsa_seq_device_exit)
-
-EXPORT_SYMBOL(snd_seq_device_load_drivers);
-EXPORT_SYMBOL(snd_seq_device_new);
-EXPORT_SYMBOL(snd_seq_device_register_driver);
-EXPORT_SYMBOL(snd_seq_device_unregister_driver);
-EXPORT_SYMBOL(snd_seq_autoload_lock);
-EXPORT_SYMBOL(snd_seq_autoload_unlock);
diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c
index f3bdc54b429a..783fc72c2ef6 100644
--- a/sound/core/seq/seq_dummy.c
+++ b/sound/core/seq/seq_dummy.c
@@ -1,26 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer MIDI-through client
* Copyright (c) 1999-2000 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include "seq_clientmgr.h"
#include <sound/initval.h>
@@ -34,23 +20,23 @@
are redirected to output port immediately.
The routing can be done via aconnect program in alsa-utils.
- Each client has a static client number 62 (= SNDRV_SEQ_CLIENT_DUMMY).
+ Each client has a static client number 14 (= SNDRV_SEQ_CLIENT_DUMMY).
If you want to auto-load this module, you may add the following alias
in your /etc/conf.modules file.
- alias snd-seq-client-62 snd-seq-dummy
+ alias snd-seq-client-14 snd-seq-dummy
- The module is loaded on demand for client 62, or /proc/asound/seq/
+ The module is loaded on demand for client 14, or /proc/asound/seq/
is accessed. If you don't need this module to be loaded, alias
- snd-seq-client-62 as "off". This will help modprobe.
+ snd-seq-client-14 as "off". This will help modprobe.
The number of ports to be created can be specified via the module
parameter "ports". For example, to create four ports, add the
- following option in /etc/modprobe.conf:
+ following option in a configuration file under /etc/modprobe.d/:
option snd-seq-dummy ports=4
- The modle option "duplex=1" enables duplex operation to the port.
+ The model option "duplex=1" enables duplex operation to the port.
In duplex mode, a pair of ports are created instead of single port,
and events are tunneled between pair-ports. For example, input to
port A is sent to output port of another port B and vice versa.
@@ -65,13 +51,19 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS("snd-seq-client-" __stringify(SNDRV_SEQ_CLIENT_DUMMY));
static int ports = 1;
-static int duplex;
+static bool duplex;
module_param(ports, int, 0444);
MODULE_PARM_DESC(ports, "number of ports to be created");
module_param(duplex, bool, 0444);
MODULE_PARM_DESC(duplex, "create DUPLEX ports");
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+static int ump;
+module_param(ump, int, 0444);
+MODULE_PARM_DESC(ump, "UMP conversion (0: no convert, 1: MIDI 1.0, 2: MIDI 2.0)");
+#endif
+
struct snd_seq_dummy_port {
int client;
int port;
@@ -82,36 +74,6 @@ struct snd_seq_dummy_port {
static int my_client = -1;
/*
- * unuse callback - send ALL_SOUNDS_OFF and RESET_CONTROLLERS events
- * to subscribers.
- * Note: this callback is called only after all subscribers are removed.
- */
-static int
-dummy_unuse(void *private_data, struct snd_seq_port_subscribe *info)
-{
- struct snd_seq_dummy_port *p;
- int i;
- struct snd_seq_event ev;
-
- p = private_data;
- memset(&ev, 0, sizeof(ev));
- if (p->duplex)
- ev.source.port = p->connect;
- else
- ev.source.port = p->port;
- ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
- ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
- for (i = 0; i < 16; i++) {
- ev.data.control.channel = i;
- ev.data.control.param = MIDI_CTL_ALL_SOUNDS_OFF;
- snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0);
- ev.data.control.param = MIDI_CTL_RESET_CONTROLLERS;
- snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0);
- }
- return 0;
-}
-
-/*
* event input callback - just redirect events to subscribers
*/
static int
@@ -153,7 +115,8 @@ create_port(int idx, int type)
struct snd_seq_port_callback pcb;
struct snd_seq_dummy_port *rec;
- if ((rec = kzalloc(sizeof(*rec), GFP_KERNEL)) == NULL)
+ rec = kzalloc(sizeof(*rec), GFP_KERNEL);
+ if (!rec)
return NULL;
rec->client = my_client;
@@ -170,12 +133,12 @@ create_port(int idx, int type)
pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
if (duplex)
pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+ pinfo.direction = SNDRV_SEQ_PORT_DIR_BIDIRECTION;
pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
| SNDRV_SEQ_PORT_TYPE_SOFTWARE
| SNDRV_SEQ_PORT_TYPE_PORT;
memset(&pcb, 0, sizeof(pcb));
pcb.owner = THIS_MODULE;
- pcb.unuse = dummy_unuse;
pcb.event_input = dummy_input;
pcb.private_free = dummy_free;
pcb.private_data = rec;
@@ -195,10 +158,13 @@ static int __init
register_client(void)
{
struct snd_seq_dummy_port *rec1, *rec2;
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ struct snd_seq_client *client;
+#endif
int i;
if (ports < 1) {
- snd_printk(KERN_ERR "invalid number of ports %d\n", ports);
+ pr_err("ALSA: seq_dummy: invalid number of ports %d\n", ports);
return -EINVAL;
}
@@ -208,6 +174,25 @@ register_client(void)
if (my_client < 0)
return my_client;
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ client = snd_seq_kernel_client_get(my_client);
+ if (!client)
+ return -EINVAL;
+ switch (ump) {
+ case 1:
+ client->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_1_0;
+ break;
+ case 2:
+ client->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_2_0;
+ break;
+ default:
+ /* don't convert events but just pass-through */
+ client->filter = SNDRV_SEQ_FILTER_NO_CONVERT;
+ break;
+ }
+ snd_seq_kernel_client_put(client);
+#endif
+
/* create ports */
for (i = 0; i < ports; i++) {
rec1 = create_port(i, 0);
@@ -245,11 +230,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_fifo.c b/sound/core/seq/seq_fifo.c
index 0d75afa786bc..91cce1890111 100644
--- a/sound/core/seq/seq_fifo.c
+++ b/sound/core/seq/seq_fifo.c
@@ -1,26 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer FIFO
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <sound/core.h>
#include <linux/slab.h>
+#include <linux/sched/signal.h>
+
#include "seq_fifo.h"
#include "seq_lock.h"
@@ -33,10 +20,8 @@ struct snd_seq_fifo *snd_seq_fifo_new(int poolsize)
struct snd_seq_fifo *f;
f = kzalloc(sizeof(*f), GFP_KERNEL);
- if (f == NULL) {
- snd_printd("malloc failed for snd_seq_fifo_new() \n");
+ if (!f)
return NULL;
- }
f->pool = snd_seq_pool_new(poolsize);
if (f->pool == NULL) {
@@ -72,6 +57,9 @@ void snd_seq_fifo_delete(struct snd_seq_fifo **fifo)
return;
*fifo = NULL;
+ if (f->pool)
+ snd_seq_pool_mark_closing(f->pool);
+
snd_seq_fifo_clear(f);
/* wake up clients if any */
@@ -95,18 +83,16 @@ static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f);
void snd_seq_fifo_clear(struct snd_seq_fifo *f)
{
struct snd_seq_event_cell *cell;
- unsigned long flags;
/* clear overflow flag */
atomic_set(&f->overflow, 0);
snd_use_lock_sync(&f->use_lock);
- spin_lock_irqsave(&f->lock, flags);
+ guard(spinlock_irq)(&f->lock);
/* drain the fifo */
while ((cell = fifo_cell_out(f)) != NULL) {
snd_seq_cell_free(cell);
}
- spin_unlock_irqrestore(&f->lock, flags);
}
@@ -115,37 +101,34 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f,
struct snd_seq_event *event)
{
struct snd_seq_event_cell *cell;
- unsigned long flags;
int err;
if (snd_BUG_ON(!f))
return -EINVAL;
- snd_use_lock_use(&f->use_lock);
- err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */
+ guard(snd_seq_fifo)(f);
+ err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL, NULL); /* always non-blocking */
if (err < 0) {
- if (err == -ENOMEM)
+ if ((err == -ENOMEM) || (err == -EAGAIN))
atomic_inc(&f->overflow);
- snd_use_lock_free(&f->use_lock);
return err;
}
/* append new cells to fifo */
- spin_lock_irqsave(&f->lock, flags);
- if (f->tail != NULL)
- f->tail->next = cell;
- f->tail = cell;
- if (f->head == NULL)
- f->head = cell;
- f->cells++;
- spin_unlock_irqrestore(&f->lock, flags);
+ scoped_guard(spinlock_irqsave, &f->lock) {
+ if (f->tail != NULL)
+ f->tail->next = cell;
+ f->tail = cell;
+ if (f->head == NULL)
+ f->head = cell;
+ cell->next = NULL;
+ f->cells++;
+ }
/* wakeup client */
if (waitqueue_active(&f->input_sleep))
wake_up(&f->input_sleep);
- snd_use_lock_free(&f->use_lock);
-
return 0; /* success */
}
@@ -155,7 +138,8 @@ static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f)
{
struct snd_seq_event_cell *cell;
- if ((cell = f->head) != NULL) {
+ cell = f->head;
+ if (cell) {
f->head = cell->next;
/* reset tail if this was the last element */
@@ -175,7 +159,7 @@ int snd_seq_fifo_cell_out(struct snd_seq_fifo *f,
{
struct snd_seq_event_cell *cell;
unsigned long flags;
- wait_queue_t wait;
+ wait_queue_entry_t wait;
if (snd_BUG_ON(!f))
return -EINVAL;
@@ -191,9 +175,9 @@ int snd_seq_fifo_cell_out(struct snd_seq_fifo *f,
}
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&f->input_sleep, &wait);
- spin_unlock_irq(&f->lock);
+ spin_unlock_irqrestore(&f->lock, flags);
schedule();
- spin_lock_irq(&f->lock);
+ spin_lock_irqsave(&f->lock, flags);
remove_wait_queue(&f->input_sleep, &wait);
if (signal_pending(current)) {
spin_unlock_irqrestore(&f->lock, flags);
@@ -210,14 +194,13 @@ int snd_seq_fifo_cell_out(struct snd_seq_fifo *f,
void snd_seq_fifo_cell_putback(struct snd_seq_fifo *f,
struct snd_seq_event_cell *cell)
{
- unsigned long flags;
-
if (cell) {
- spin_lock_irqsave(&f->lock, flags);
+ guard(spinlock_irqsave)(&f->lock);
cell->next = f->head;
f->head = cell;
+ if (!f->tail)
+ f->tail = cell;
f->cells++;
- spin_unlock_irqrestore(&f->lock, flags);
}
}
@@ -227,13 +210,13 @@ int snd_seq_fifo_poll_wait(struct snd_seq_fifo *f, struct file *file,
poll_table *wait)
{
poll_wait(file, &f->input_sleep, wait);
+ guard(spinlock_irq)(&f->lock);
return (f->cells > 0);
}
/* change the size of pool; all old events are removed */
int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
{
- unsigned long flags;
struct snd_seq_pool *newpool, *oldpool;
struct snd_seq_event_cell *cell, *next, *oldhead;
@@ -249,17 +232,21 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
return -ENOMEM;
}
- spin_lock_irqsave(&f->lock, flags);
- /* remember old pool */
- oldpool = f->pool;
- oldhead = f->head;
- /* exchange pools */
- f->pool = newpool;
- f->head = NULL;
- f->tail = NULL;
- f->cells = 0;
- /* NOTE: overflow flag is not cleared */
- spin_unlock_irqrestore(&f->lock, flags);
+ scoped_guard(spinlock_irq, &f->lock) {
+ /* remember old pool */
+ oldpool = f->pool;
+ oldhead = f->head;
+ /* exchange pools */
+ f->pool = newpool;
+ f->head = NULL;
+ f->tail = NULL;
+ f->cells = 0;
+ /* NOTE: overflow flag is not cleared */
+ }
+
+ /* close the old pool and wait until all users are gone */
+ snd_seq_pool_mark_closing(oldpool);
+ snd_use_lock_sync(&f->use_lock);
/* release cells in old pool */
for (cell = oldhead; cell; cell = next) {
@@ -270,3 +257,14 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)
return 0;
}
+
+/* get the number of unused cells safely */
+int snd_seq_fifo_unused_cells(struct snd_seq_fifo *f)
+{
+ if (!f)
+ return 0;
+
+ guard(snd_seq_fifo)(f);
+ guard(spinlock_irqsave)(&f->lock);
+ return snd_seq_unused_cells(f->pool);
+}
diff --git a/sound/core/seq/seq_fifo.h b/sound/core/seq/seq_fifo.h
index 062c446e7867..4c9c49127746 100644
--- a/sound/core/seq/seq_fifo.h
+++ b/sound/core/seq/seq_fifo.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA sequencer FIFO
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __SND_SEQ_FIFO_H
#define __SND_SEQ_FIFO_H
@@ -52,6 +37,7 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f, struct snd_seq_event *event);
/* lock fifo from release */
#define snd_seq_fifo_lock(fifo) snd_use_lock_use(&(fifo)->use_lock)
#define snd_seq_fifo_unlock(fifo) snd_use_lock_free(&(fifo)->use_lock)
+DEFINE_GUARD(snd_seq_fifo, struct snd_seq_fifo *, snd_seq_fifo_lock(_T), snd_seq_fifo_unlock(_T))
/* get a cell from fifo - fifo should be locked */
int snd_seq_fifo_cell_out(struct snd_seq_fifo *f, struct snd_seq_event_cell **cellp, int nonblock);
@@ -68,5 +54,7 @@ int snd_seq_fifo_poll_wait(struct snd_seq_fifo *f, struct file *file, poll_table
/* resize pool in fifo */
int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize);
+/* get the number of unused cells safely */
+int snd_seq_fifo_unused_cells(struct snd_seq_fifo *f);
#endif
diff --git a/sound/core/seq/seq_info.c b/sound/core/seq/seq_info.c
index 201f8106ffdd..3e9fce7bead8 100644
--- a/sound/core/seq/seq_info.c
+++ b/sound/core/seq/seq_info.c
@@ -1,32 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer /proc interface
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
+#include <linux/export.h>
#include <sound/core.h>
#include "seq_info.h"
#include "seq_clientmgr.h"
#include "seq_timer.h"
-#ifdef CONFIG_PROC_FS
static struct snd_info_entry *queues_entry;
static struct snd_info_entry *clients_entry;
static struct snd_info_entry *timer_entry;
@@ -50,6 +35,13 @@ create_info_entry(char *name, void (*read)(struct snd_info_entry *,
return entry;
}
+void snd_seq_info_done(void)
+{
+ snd_info_free_entry(queues_entry);
+ snd_info_free_entry(clients_entry);
+ snd_info_free_entry(timer_entry);
+}
+
/* create all our /proc entries */
int __init snd_seq_info_init(void)
{
@@ -58,14 +50,11 @@ int __init snd_seq_info_init(void)
clients_entry = create_info_entry("clients",
snd_seq_info_clients_read);
timer_entry = create_info_entry("timer", snd_seq_info_timer_read);
+ if (!queues_entry || !clients_entry || !timer_entry)
+ goto error;
return 0;
-}
-int __exit snd_seq_info_done(void)
-{
- snd_info_free_entry(queues_entry);
- snd_info_free_entry(clients_entry);
- snd_info_free_entry(timer_entry);
- return 0;
+ error:
+ snd_seq_info_done();
+ return -ENOMEM;
}
-#endif
diff --git a/sound/core/seq/seq_info.h b/sound/core/seq/seq_info.h
index 4892a7f35c08..576cf0522163 100644
--- a/sound/core/seq/seq_info.h
+++ b/sound/core/seq/seq_info.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA sequencer /proc info
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __SND_SEQ_INFO_H
#define __SND_SEQ_INFO_H
@@ -29,12 +14,12 @@ void snd_seq_info_timer_read(struct snd_info_entry *entry, struct snd_info_buffe
void snd_seq_info_queues_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer);
-#ifdef CONFIG_PROC_FS
-int snd_seq_info_init( void );
-int snd_seq_info_done( void );
+#ifdef CONFIG_SND_PROC_FS
+int snd_seq_info_init(void);
+void snd_seq_info_done(void);
#else
static inline int snd_seq_info_init(void) { return 0; }
-static inline int snd_seq_info_done(void) { return 0; }
+static inline void snd_seq_info_done(void) {}
#endif
#endif
diff --git a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c
index 54f921edda79..48b4ffb4b76e 100644
--- a/sound/core/seq/seq_lock.c
+++ b/sound/core/seq/seq_lock.c
@@ -1,48 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Do sleep inside a spin-lock
* Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
+#include <linux/export.h>
#include <sound/core.h>
#include "seq_lock.h"
-#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
-
/* wait until all locks are released */
void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line)
{
- int max_count = 5 * HZ;
+ int warn_count = 5 * HZ;
if (atomic_read(lockp) < 0) {
- printk(KERN_WARNING "seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line);
+ pr_warn("ALSA: seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line);
return;
}
while (atomic_read(lockp) > 0) {
- if (max_count == 0) {
- snd_printk(KERN_WARNING "seq_lock: timeout [%d left] in %s:%d\n", atomic_read(lockp), file, line);
- break;
- }
+ if (warn_count-- == 0)
+ pr_warn("ALSA: seq_lock: waiting [%d left] in %s:%d\n", atomic_read(lockp), file, line);
schedule_timeout_uninterruptible(1);
- max_count--;
}
}
-
EXPORT_SYMBOL(snd_use_lock_sync_helper);
-
-#endif
diff --git a/sound/core/seq/seq_lock.h b/sound/core/seq/seq_lock.h
index 54044bc2c9ef..a973860ebcd0 100644
--- a/sound/core/seq/seq_lock.h
+++ b/sound/core/seq/seq_lock.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SND_SEQ_LOCK_H
#define __SND_SEQ_LOCK_H
#include <linux/sched.h>
-#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
-
typedef atomic_t snd_use_lock_t;
/* initialize lock */
@@ -20,14 +19,4 @@ typedef atomic_t snd_use_lock_t;
void snd_use_lock_sync_helper(snd_use_lock_t *lock, const char *file, int line);
#define snd_use_lock_sync(lockp) snd_use_lock_sync_helper(lockp, __BASE_FILE__, __LINE__)
-#else /* SMP || CONFIG_SND_DEBUG */
-
-typedef spinlock_t snd_use_lock_t; /* dummy */
-#define snd_use_lock_init(lockp) /**/
-#define snd_use_lock_use(lockp) /**/
-#define snd_use_lock_free(lockp) /**/
-#define snd_use_lock_sync(lockp) /**/
-
-#endif /* SMP || CONFIG_SND_DEBUG */
-
#endif /* __SND_SEQ_LOCK_H */
diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
index 7fb55436287f..ccde0ca3d208 100644
--- a/sound/core/seq/seq_memory.c
+++ b/sound/core/seq/seq_memory.c
@@ -1,28 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer Memory Manager
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
* Jaroslav Kysela <perex@perex.cz>
* 2000 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
+#include <linux/export.h>
#include <linux/slab.h>
-#include <linux/vmalloc.h>
+#include <linux/sched/signal.h>
+#include <linux/mm.h>
#include <sound/core.h>
#include <sound/seq_kernel.h>
@@ -75,18 +63,26 @@ static int get_var_len(const struct snd_seq_event *event)
return event->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
}
-int snd_seq_dump_var_event(const struct snd_seq_event *event,
- snd_seq_dump_func_t func, void *private_data)
+static int dump_var_event(const struct snd_seq_event *event,
+ snd_seq_dump_func_t func, void *private_data,
+ int offset, int maxlen)
{
int len, err;
struct snd_seq_event_cell *cell;
- if ((len = get_var_len(event)) <= 0)
+ len = get_var_len(event);
+ if (len <= 0)
return len;
+ if (len <= offset)
+ return 0;
+ if (maxlen && len > offset + maxlen)
+ len = offset + maxlen;
if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
char buf[32];
- char __user *curptr = (char __user *)event->data.ext.ptr;
+ char __user *curptr = (char __force __user *)event->data.ext.ptr;
+ curptr += offset;
+ len -= offset;
while (len > 0) {
int size = sizeof(buf);
if (len < size)
@@ -100,23 +96,37 @@ int snd_seq_dump_var_event(const struct snd_seq_event *event,
len -= size;
}
return 0;
- } if (! (event->data.ext.len & SNDRV_SEQ_EXT_CHAINED)) {
- return func(private_data, event->data.ext.ptr, len);
}
+ if (!(event->data.ext.len & SNDRV_SEQ_EXT_CHAINED))
+ return func(private_data, event->data.ext.ptr + offset,
+ len - offset);
cell = (struct snd_seq_event_cell *)event->data.ext.ptr;
for (; len > 0 && cell; cell = cell->next) {
int size = sizeof(struct snd_seq_event);
+ char *curptr = (char *)&cell->event;
+
+ if (offset >= size) {
+ offset -= size;
+ len -= size;
+ continue;
+ }
if (len < size)
size = len;
- err = func(private_data, &cell->event, size);
+ err = func(private_data, curptr + offset, size - offset);
if (err < 0)
return err;
+ offset = 0;
len -= size;
}
return 0;
}
+int snd_seq_dump_var_event(const struct snd_seq_event *event,
+ snd_seq_dump_func_t func, void *private_data)
+{
+ return dump_var_event(event, func, private_data, 0, 0);
+}
EXPORT_SYMBOL(snd_seq_dump_var_event);
@@ -125,51 +135,89 @@ EXPORT_SYMBOL(snd_seq_dump_var_event);
* expand the variable length event to linear buffer space.
*/
-static int seq_copy_in_kernel(char **bufptr, const void *src, int size)
+static int seq_copy_in_kernel(void *ptr, void *src, int size)
{
+ char **bufptr = ptr;
+
memcpy(*bufptr, src, size);
*bufptr += size;
return 0;
}
-static int seq_copy_in_user(char __user **bufptr, const void *src, int size)
+static int seq_copy_in_user(void *ptr, void *src, int size)
{
+ char __user **bufptr = ptr;
+
if (copy_to_user(*bufptr, src, size))
return -EFAULT;
*bufptr += size;
return 0;
}
+static int expand_var_event(const struct snd_seq_event *event,
+ int offset, int size, char *buf, bool in_kernel)
+{
+ if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
+ if (! in_kernel)
+ return -EINVAL;
+ if (copy_from_user(buf,
+ (char __force __user *)event->data.ext.ptr + offset,
+ size))
+ return -EFAULT;
+ return 0;
+ }
+ return dump_var_event(event,
+ in_kernel ? seq_copy_in_kernel : seq_copy_in_user,
+ &buf, offset, size);
+}
+
int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char *buf,
int in_kernel, int size_aligned)
{
- int len, newlen;
- int err;
+ int len, newlen, err;
- if ((len = get_var_len(event)) < 0)
+ len = get_var_len(event);
+ if (len < 0)
return len;
newlen = len;
if (size_aligned > 0)
newlen = roundup(len, size_aligned);
if (count < newlen)
return -EAGAIN;
-
- if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
- if (! in_kernel)
- return -EINVAL;
- if (copy_from_user(buf, (void __user *)event->data.ext.ptr, len))
+ err = expand_var_event(event, 0, len, buf, in_kernel);
+ if (err < 0)
+ return err;
+ if (len != newlen) {
+ if (in_kernel)
+ memset(buf + len, 0, newlen - len);
+ else if (clear_user((__force void __user *)buf + len,
+ newlen - len))
return -EFAULT;
- return newlen;
}
- err = snd_seq_dump_var_event(event,
- in_kernel ? (snd_seq_dump_func_t)seq_copy_in_kernel :
- (snd_seq_dump_func_t)seq_copy_in_user,
- &buf);
- return err < 0 ? err : newlen;
+ return newlen;
}
-
EXPORT_SYMBOL(snd_seq_expand_var_event);
+int snd_seq_expand_var_event_at(const struct snd_seq_event *event, int count,
+ char *buf, int offset)
+{
+ int len, err;
+
+ len = get_var_len(event);
+ if (len < 0)
+ return len;
+ if (len <= offset)
+ return 0;
+ len -= offset;
+ if (len > count)
+ len = count;
+ err = expand_var_event(event, offset, count, buf, true);
+ if (err < 0)
+ return err;
+ return len;
+}
+EXPORT_SYMBOL_GPL(snd_seq_expand_var_event_at);
+
/*
* release this cell, free extended data if available
*/
@@ -184,7 +232,6 @@ static inline void free_cell(struct snd_seq_pool *pool,
void snd_seq_cell_free(struct snd_seq_event_cell * cell)
{
- unsigned long flags;
struct snd_seq_pool *pool;
if (snd_BUG_ON(!cell))
@@ -193,7 +240,7 @@ void snd_seq_cell_free(struct snd_seq_event_cell * cell)
if (snd_BUG_ON(!pool))
return;
- spin_lock_irqsave(&pool->lock, flags);
+ guard(spinlock_irqsave)(&pool->lock);
free_cell(pool, cell);
if (snd_seq_ev_is_variable(&cell->event)) {
if (cell->event.data.ext.len & SNDRV_SEQ_EXT_CHAINED) {
@@ -211,7 +258,6 @@ void snd_seq_cell_free(struct snd_seq_event_cell * cell)
if (snd_seq_output_ok(pool))
wake_up(&pool->output_sleep);
}
- spin_unlock_irqrestore(&pool->lock, flags);
}
@@ -220,12 +266,13 @@ void snd_seq_cell_free(struct snd_seq_event_cell * cell)
*/
static int snd_seq_cell_alloc(struct snd_seq_pool *pool,
struct snd_seq_event_cell **cellp,
- int nonblock, struct file *file)
+ int nonblock, struct file *file,
+ struct mutex *mutexp)
{
struct snd_seq_event_cell *cell;
unsigned long flags;
int err = -EAGAIN;
- wait_queue_t wait;
+ wait_queue_entry_t wait;
if (pool == NULL)
return -EINVAL;
@@ -235,7 +282,7 @@ static int snd_seq_cell_alloc(struct snd_seq_pool *pool,
init_waitqueue_entry(&wait, current);
spin_lock_irqsave(&pool->lock, flags);
if (pool->ptr == NULL) { /* not initialized */
- snd_printd("seq: pool is not initialized\n");
+ pr_debug("ALSA: seq: pool is not initialized\n");
err = -EINVAL;
goto __error;
}
@@ -243,9 +290,13 @@ static int snd_seq_cell_alloc(struct snd_seq_pool *pool,
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&pool->output_sleep, &wait);
- spin_unlock_irq(&pool->lock);
+ spin_unlock_irqrestore(&pool->lock, flags);
+ if (mutexp)
+ mutex_unlock(mutexp);
schedule();
- spin_lock_irq(&pool->lock);
+ if (mutexp)
+ mutex_lock(mutexp);
+ spin_lock_irqsave(&pool->lock, flags);
remove_wait_queue(&pool->output_sleep, &wait);
/* interrupted? */
if (signal_pending(current)) {
@@ -287,11 +338,12 @@ __error:
*/
int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
struct snd_seq_event_cell **cellp, int nonblock,
- struct file *file)
+ struct file *file, struct mutex *mutexp)
{
int ncells, err;
unsigned int extlen;
struct snd_seq_event_cell *cell;
+ int size;
*cellp = NULL;
@@ -299,17 +351,22 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
extlen = 0;
if (snd_seq_ev_is_variable(event)) {
extlen = event->data.ext.len & ~SNDRV_SEQ_EXT_MASK;
- ncells = (extlen + sizeof(struct snd_seq_event) - 1) / sizeof(struct snd_seq_event);
+ ncells = DIV_ROUND_UP(extlen, sizeof(struct snd_seq_event));
}
if (ncells >= pool->total_elements)
return -ENOMEM;
- err = snd_seq_cell_alloc(pool, &cell, nonblock, file);
+ err = snd_seq_cell_alloc(pool, &cell, nonblock, file, mutexp);
if (err < 0)
return err;
/* copy the event */
- cell->event = *event;
+ size = snd_seq_event_packet_size(event);
+ memcpy(&cell->ump, event, size);
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ if (size < sizeof(cell->event))
+ cell->ump.raw.extra = 0;
+#endif
/* decompose */
if (snd_seq_ev_is_variable(event)) {
@@ -327,10 +384,11 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
tail = NULL;
while (ncells-- > 0) {
- int size = sizeof(struct snd_seq_event);
+ size = sizeof(struct snd_seq_event);
if (len < size)
size = len;
- err = snd_seq_cell_alloc(pool, &tmp, nonblock, file);
+ err = snd_seq_cell_alloc(pool, &tmp, nonblock, file,
+ mutexp);
if (err < 0)
goto __error;
if (cell->event.data.ext.ptr == NULL)
@@ -343,7 +401,7 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
tmp->event = src->event;
src = src->next;
} else if (is_usrptr) {
- if (copy_from_user(&tmp->event, (char __user *)buf, size)) {
+ if (copy_from_user(&tmp->event, (char __force __user *)buf, size)) {
err = -EFAULT;
goto __error;
}
@@ -369,6 +427,7 @@ int snd_seq_pool_poll_wait(struct snd_seq_pool *pool, struct file *file,
poll_table *wait)
{
poll_wait(file, &pool->output_sleep, wait);
+ guard(spinlock_irq)(&pool->lock);
return snd_seq_output_ok(pool);
}
@@ -378,21 +437,24 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
{
int cell;
struct snd_seq_event_cell *cellptr;
- unsigned long flags;
if (snd_BUG_ON(!pool))
return -EINVAL;
- if (pool->ptr) /* should be atomic? */
- return 0;
- pool->ptr = vmalloc(sizeof(struct snd_seq_event_cell) * pool->size);
- if (pool->ptr == NULL) {
- snd_printd("seq: malloc for sequencer events failed\n");
+ cellptr = kvmalloc_array(pool->size,
+ sizeof(struct snd_seq_event_cell),
+ GFP_KERNEL);
+ if (!cellptr)
return -ENOMEM;
- }
/* add new cells to the free cell list */
- spin_lock_irqsave(&pool->lock, flags);
+ guard(spinlock_irq)(&pool->lock);
+ if (pool->ptr) {
+ kvfree(cellptr);
+ return 0;
+ }
+
+ pool->ptr = cellptr;
pool->free = NULL;
for (cell = 0; cell < pool->size; cell++) {
@@ -406,50 +468,45 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
/* init statistics */
pool->max_used = 0;
pool->total_elements = pool->size;
- spin_unlock_irqrestore(&pool->lock, flags);
return 0;
}
+/* refuse the further insertion to the pool */
+void snd_seq_pool_mark_closing(struct snd_seq_pool *pool)
+{
+ if (snd_BUG_ON(!pool))
+ return;
+ guard(spinlock_irqsave)(&pool->lock);
+ pool->closing = 1;
+}
+
/* remove events */
int snd_seq_pool_done(struct snd_seq_pool *pool)
{
- unsigned long flags;
struct snd_seq_event_cell *ptr;
- int max_count = 5 * HZ;
if (snd_BUG_ON(!pool))
return -EINVAL;
/* wait for closing all threads */
- spin_lock_irqsave(&pool->lock, flags);
- pool->closing = 1;
- spin_unlock_irqrestore(&pool->lock, flags);
-
if (waitqueue_active(&pool->output_sleep))
wake_up(&pool->output_sleep);
- while (atomic_read(&pool->counter) > 0) {
- if (max_count == 0) {
- snd_printk(KERN_WARNING "snd_seq_pool_done timeout: %d cells remain\n", atomic_read(&pool->counter));
- break;
- }
+ while (atomic_read(&pool->counter) > 0)
schedule_timeout_uninterruptible(1);
- max_count--;
- }
/* release all resources */
- spin_lock_irqsave(&pool->lock, flags);
- ptr = pool->ptr;
- pool->ptr = NULL;
- pool->free = NULL;
- pool->total_elements = 0;
- spin_unlock_irqrestore(&pool->lock, flags);
+ scoped_guard(spinlock_irq, &pool->lock) {
+ ptr = pool->ptr;
+ pool->ptr = NULL;
+ pool->free = NULL;
+ pool->total_elements = 0;
+ }
- vfree(ptr);
+ kvfree(ptr);
- spin_lock_irqsave(&pool->lock, flags);
+ guard(spinlock_irq)(&pool->lock);
pool->closing = 0;
- spin_unlock_irqrestore(&pool->lock, flags);
return 0;
}
@@ -462,10 +519,8 @@ struct snd_seq_pool *snd_seq_pool_new(int poolsize)
/* create pool block */
pool = kzalloc(sizeof(*pool), GFP_KERNEL);
- if (pool == NULL) {
- snd_printd("seq: malloc failed for pool\n");
+ if (!pool)
return NULL;
- }
spin_lock_init(&pool->lock);
pool->ptr = NULL;
pool->free = NULL;
@@ -489,23 +544,12 @@ int snd_seq_pool_delete(struct snd_seq_pool **ppool)
*ppool = NULL;
if (pool == NULL)
return 0;
+ snd_seq_pool_mark_closing(pool);
snd_seq_pool_done(pool);
kfree(pool);
return 0;
}
-/* initialize sequencer memory */
-int __init snd_sequencer_memory_init(void)
-{
- return 0;
-}
-
-/* release sequencer memory */
-void __exit snd_sequencer_memory_done(void)
-{
-}
-
-
/* exported to seq_clientmgr.c */
void snd_seq_info_pool(struct snd_info_buffer *buffer,
struct snd_seq_pool *pool, char *space)
diff --git a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h
index 63e91431a29f..7f7a2c0b187d 100644
--- a/sound/core/seq/seq_memory.h
+++ b/sound/core/seq/seq_memory.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA sequencer Memory Manager
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __SND_SEQ_MEMORYMGR_H
#define __SND_SEQ_MEMORYMGR_H
@@ -24,9 +9,28 @@
#include <sound/seq_kernel.h>
#include <linux/poll.h>
+struct snd_info_buffer;
+
+/* aliasing for legacy and UMP event packet handling */
+union __snd_seq_event {
+ struct snd_seq_event legacy;
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ struct snd_seq_ump_event ump;
+#endif
+ struct {
+ struct snd_seq_event event;
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ u32 extra;
+#endif
+ } __packed raw;
+};
+
/* container for sequencer event (internal use) */
struct snd_seq_event_cell {
- struct snd_seq_event event;
+ union {
+ struct snd_seq_event event;
+ union __snd_seq_event ump;
+ };
struct snd_seq_pool *pool; /* used pool */
struct snd_seq_event_cell *next; /* next cell */
};
@@ -64,7 +68,8 @@ struct snd_seq_pool {
void snd_seq_cell_free(struct snd_seq_event_cell *cell);
int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event,
- struct snd_seq_event_cell **cellp, int nonblock, struct file *file);
+ struct snd_seq_event_cell **cellp, int nonblock,
+ struct file *file, struct mutex *mutexp);
/* return number of unused (free) cells */
static inline int snd_seq_unused_cells(struct snd_seq_pool *pool)
@@ -82,6 +87,7 @@ static inline int snd_seq_total_cells(struct snd_seq_pool *pool)
int snd_seq_pool_init(struct snd_seq_pool *pool);
/* done pool - free events */
+void snd_seq_pool_mark_closing(struct snd_seq_pool *pool);
int snd_seq_pool_done(struct snd_seq_pool *pool);
/* create pool */
@@ -90,14 +96,10 @@ struct snd_seq_pool *snd_seq_pool_new(int poolsize);
/* remove pool */
int snd_seq_pool_delete(struct snd_seq_pool **pool);
-/* init memory */
-int snd_sequencer_memory_init(void);
-
-/* release event memory */
-void snd_sequencer_memory_done(void);
-
/* polling */
int snd_seq_pool_poll_wait(struct snd_seq_pool *pool, struct file *file, poll_table *wait);
+void snd_seq_info_pool(struct snd_info_buffer *buffer,
+ struct snd_seq_pool *pool, char *space);
#endif
diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c
index ebaf1b541dcd..581e138a3115 100644
--- a/sound/core/seq/seq_midi.c
+++ b/sound/core/seq/seq_midi.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Generic MIDI synth driver for ALSA sequencer
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
* Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -30,7 +16,7 @@ Possible options for midisynth module:
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/string.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/rawmidi.h>
@@ -52,6 +38,7 @@ MODULE_PARM_DESC(input_buffer_size, "Input buffer size in bytes.");
/* data for this midi synth driver */
struct seq_midisynth {
struct snd_card *card;
+ struct snd_rawmidi *rmidi;
int device;
int subdevice;
struct snd_rawmidi_file input_rfile;
@@ -78,7 +65,7 @@ static void snd_midi_input_event(struct snd_rawmidi_substream *substream)
struct seq_midisynth *msynth;
struct snd_seq_event ev;
char buf[16], *pbuf;
- long res, count;
+ long res;
if (substream == NULL)
return;
@@ -94,19 +81,15 @@ static void snd_midi_input_event(struct snd_rawmidi_substream *substream)
if (msynth->parser == NULL)
continue;
pbuf = buf;
- while (res > 0) {
- count = snd_midi_event_encode(msynth->parser, pbuf, res, &ev);
- if (count < 0)
- break;
- pbuf += count;
- res -= count;
- if (ev.type != SNDRV_SEQ_EVENT_NONE) {
- ev.source.port = msynth->seq_port;
- ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
- snd_seq_kernel_client_dispatch(msynth->seq_client, &ev, 1, 0);
- /* clear event and reset header */
- memset(&ev, 0, sizeof(ev));
- }
+ while (res-- > 0) {
+ if (!snd_midi_event_encode_byte(msynth->parser,
+ *pbuf++, &ev))
+ continue;
+ ev.source.port = msynth->seq_port;
+ ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+ snd_seq_kernel_client_dispatch(msynth->seq_client, &ev, 1, 0);
+ /* clear event and reset header */
+ memset(&ev, 0, sizeof(ev));
}
}
}
@@ -119,9 +102,10 @@ static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, i
if (snd_BUG_ON(!substream || !buf))
return -EINVAL;
runtime = substream->runtime;
- if ((tmp = runtime->avail) < count) {
+ tmp = runtime->avail;
+ if (tmp < count) {
if (printk_ratelimit())
- snd_printk(KERN_ERR "MIDI output buffer overrun\n");
+ pr_err("ALSA: seq_midi: MIDI output buffer overrun\n");
return -ENOMEM;
}
if (snd_rawmidi_kernel_write(substream, buf, count) < count)
@@ -129,6 +113,12 @@ static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, i
return 0;
}
+/* callback for snd_seq_dump_var_event(), bridging to dump_midi() */
+static int __dump_midi(void *ptr, void *buf, int count)
+{
+ return dump_midi(ptr, buf, count);
+}
+
static int event_process_midi(struct snd_seq_event *ev, int direct,
void *private_data, int atomic, int hop)
{
@@ -145,10 +135,10 @@ static int event_process_midi(struct snd_seq_event *ev, int direct,
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) { /* special case, to save space */
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) {
/* invalid event */
- snd_printd("seq_midi: invalid sysex event flags = 0x%x\n", ev->flags);
+ pr_debug("ALSA: seq_midi: invalid sysex event flags = 0x%x\n", ev->flags);
return 0;
}
- snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream);
+ snd_seq_dump_var_event(ev, __dump_midi, substream);
snd_midi_event_reset_decode(msynth->parser);
} else {
if (msynth->parser == NULL)
@@ -185,18 +175,19 @@ static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribe
struct snd_rawmidi_params params;
/* open midi port */
- if ((err = snd_rawmidi_kernel_open(msynth->card, msynth->device,
- msynth->subdevice,
- SNDRV_RAWMIDI_LFLG_INPUT,
- &msynth->input_rfile)) < 0) {
- snd_printd("midi input open failed!!!\n");
+ err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice,
+ SNDRV_RAWMIDI_LFLG_INPUT,
+ &msynth->input_rfile);
+ if (err < 0) {
+ pr_debug("ALSA: seq_midi: midi input open failed!!!\n");
return err;
}
runtime = msynth->input_rfile.input->runtime;
memset(&params, 0, sizeof(params));
params.avail_min = 1;
params.buffer_size = input_buffer_size;
- if ((err = snd_rawmidi_input_params(msynth->input_rfile.input, &params)) < 0) {
+ err = snd_rawmidi_input_params(msynth->input_rfile.input, &params);
+ if (err < 0) {
snd_rawmidi_kernel_release(&msynth->input_rfile);
return err;
}
@@ -227,18 +218,19 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
struct snd_rawmidi_params params;
/* open midi port */
- if ((err = snd_rawmidi_kernel_open(msynth->card, msynth->device,
- msynth->subdevice,
- SNDRV_RAWMIDI_LFLG_OUTPUT,
- &msynth->output_rfile)) < 0) {
- snd_printd("midi output open failed!!!\n");
+ err = snd_rawmidi_kernel_open(msynth->rmidi, msynth->subdevice,
+ SNDRV_RAWMIDI_LFLG_OUTPUT,
+ &msynth->output_rfile);
+ if (err < 0) {
+ pr_debug("ALSA: seq_midi: midi output open failed!!!\n");
return err;
}
memset(&params, 0, sizeof(params));
params.avail_min = 1;
params.buffer_size = output_buffer_size;
params.no_active_sensing = 1;
- if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, &params)) < 0) {
+ err = snd_rawmidi_output_params(msynth->output_rfile.output, &params);
+ if (err < 0) {
snd_rawmidi_kernel_release(&msynth->output_rfile);
return err;
}
@@ -268,18 +260,18 @@ static void snd_seq_midisynth_delete(struct seq_midisynth *msynth)
snd_seq_event_port_detach(msynth->seq_client, msynth->seq_port);
}
- if (msynth->parser)
- snd_midi_event_free(msynth->parser);
+ snd_midi_event_free(msynth->parser);
}
/* 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;
- struct snd_rawmidi_info *info;
+ struct snd_seq_port_info *port __free(kfree) = NULL;
+ struct snd_rawmidi_info *info __free(kfree) = NULL;
struct snd_rawmidi *rmidi = dev->private_data;
int newclient = 0;
unsigned int p, ports;
@@ -305,31 +297,24 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev)
ports = output_count;
if (ports < input_count)
ports = input_count;
- if (ports == 0) {
- kfree(info);
+ if (ports == 0)
return -ENODEV;
- }
if (ports > (256 / SNDRV_RAWMIDI_DEVICES))
ports = 256 / SNDRV_RAWMIDI_DEVICES;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
client = synths[card->number];
if (client == NULL) {
newclient = 1;
client = kzalloc(sizeof(*client), GFP_KERNEL);
- if (client == NULL) {
- mutex_unlock(&register_mutex);
- kfree(info);
+ if (client == NULL)
return -ENOMEM;
- }
client->seq_client =
snd_seq_create_kernel_client(
card, 0, "%s", card->shortname[0] ?
(const char *)card->shortname : "External MIDI");
if (client->seq_client < 0) {
kfree(client);
- mutex_unlock(&register_mutex);
- kfree(info);
return -ENOMEM;
}
}
@@ -341,6 +326,7 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev)
for (p = 0; p < ports; p++) {
ms = &msynth[p];
+ ms->rmidi = rmidi;
if (snd_seq_midisynth_new(ms, card, device, p) < 0)
goto __nomem;
@@ -358,17 +344,17 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev)
info->stream = SNDRV_RAWMIDI_STREAM_INPUT;
info->subdevice = p;
if (snd_rawmidi_info_select(card, info) >= 0)
- strcpy(port->name, info->subname);
+ strscpy(port->name, info->subname);
if (! port->name[0]) {
if (info->name[0]) {
if (ports > 1)
- snprintf(port->name, sizeof(port->name), "%s-%d", info->name, p);
+ scnprintf(port->name, sizeof(port->name), "%s-%u", info->name, p);
else
- snprintf(port->name, sizeof(port->name), "%s", info->name);
+ scnprintf(port->name, sizeof(port->name), "%s", info->name);
} else {
/* last resort */
if (ports > 1)
- sprintf(port->name, "MIDI %d-%d-%d", card->number, device, p);
+ sprintf(port->name, "MIDI %d-%d-%u", card->number, device, p);
else
sprintf(port->name, "MIDI %d-%d", card->number, device);
}
@@ -380,6 +366,10 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev)
if ((port->capability & (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ)) == (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ) &&
info->flags & SNDRV_RAWMIDI_INFO_DUPLEX)
port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+ if (port->capability & SNDRV_SEQ_PORT_CAP_READ)
+ port->direction |= SNDRV_SEQ_PORT_DIR_INPUT;
+ if (port->capability & SNDRV_SEQ_PORT_CAP_WRITE)
+ port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT;
port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
| SNDRV_SEQ_PORT_TYPE_HARDWARE
| SNDRV_SEQ_PORT_TYPE_PORT;
@@ -405,9 +395,6 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev)
client->num_ports++;
if (newclient)
synths[card->number] = client;
- mutex_unlock(&register_mutex);
- kfree(info);
- kfree(port);
return 0; /* success */
__nomem:
@@ -420,27 +407,23 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev)
snd_seq_delete_kernel_client(client->seq_client);
kfree(client);
}
- kfree(info);
- kfree(port);
- mutex_unlock(&register_mutex);
return -ENOMEM;
}
/* 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;
int device = dev->device, p, ports;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
client = synths[card->number];
- if (client == NULL || client->ports[device] == NULL) {
- mutex_unlock(&register_mutex);
+ if (client == NULL || client->ports[device] == NULL)
return -ENODEV;
- }
ports = client->ports_per_device[device];
client->ports_per_device[device] = 0;
msynth = client->ports[device];
@@ -454,28 +437,17 @@ snd_seq_midisynth_unregister_port(struct snd_seq_device *dev)
synths[card->number] = NULL;
kfree(client);
}
- mutex_unlock(&register_mutex);
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));
- snd_seq_autoload_lock();
- snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0);
- snd_seq_autoload_unlock();
- return 0;
-}
-
-static void __exit alsa_seq_midi_exit(void)
-{
- snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH);
-}
-
-module_init(alsa_seq_midi_init)
-module_exit(alsa_seq_midi_exit)
+module_snd_seq_driver(seq_midisynth_driver);
diff --git a/sound/core/seq/seq_midi_emul.c b/sound/core/seq/seq_midi_emul.c
index 07c663135c62..81d2ef5e5811 100644
--- a/sound/core/seq/seq_midi_emul.c
+++ b/sound/core/seq/seq_midi_emul.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* GM/GS/XG midi module.
*
* Copyright (C) 1999 Steve Ratcliffe
*
* Based on awe_wave.c by Takashi Iwai
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
* This module is used to keep track of the current midi state.
@@ -32,6 +18,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/seq_kernel.h>
#include <sound/seq_midi_emul.h>
@@ -43,22 +30,25 @@ MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI emulation."
MODULE_LICENSE("GPL");
/* Prototypes for static functions */
-static void note_off(struct snd_midi_op *ops, void *drv,
+static void note_off(const struct snd_midi_op *ops, void *drv,
struct snd_midi_channel *chan,
int note, int vel);
-static void do_control(struct snd_midi_op *ops, void *private,
+static void do_control(const struct snd_midi_op *ops, void *private,
struct snd_midi_channel_set *chset,
struct snd_midi_channel *chan,
int control, int value);
-static void rpn(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
+static void rpn(const struct snd_midi_op *ops, void *drv,
+ struct snd_midi_channel *chan,
struct snd_midi_channel_set *chset);
-static void nrpn(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
+static void nrpn(const struct snd_midi_op *ops, void *drv,
+ struct snd_midi_channel *chan,
struct snd_midi_channel_set *chset);
-static void sysex(struct snd_midi_op *ops, void *private, unsigned char *sysex,
+static void sysex(const struct snd_midi_op *ops, void *private,
+ unsigned char *sysex,
int len, struct snd_midi_channel_set *chset);
-static void all_sounds_off(struct snd_midi_op *ops, void *private,
+static void all_sounds_off(const struct snd_midi_op *ops, void *private,
struct snd_midi_channel *chan);
-static void all_notes_off(struct snd_midi_op *ops, void *private,
+static void all_notes_off(const struct snd_midi_op *ops, void *private,
struct snd_midi_channel *chan);
static void snd_midi_reset_controllers(struct snd_midi_channel *chan);
static void reset_all_channels(struct snd_midi_channel_set *chset);
@@ -79,7 +69,7 @@ static void reset_all_channels(struct snd_midi_channel_set *chset);
* be interpreted.
*/
void
-snd_midi_process_event(struct snd_midi_op *ops,
+snd_midi_process_event(const struct snd_midi_op *ops,
struct snd_seq_event *ev,
struct snd_midi_channel_set *chanset)
{
@@ -88,7 +78,7 @@ snd_midi_process_event(struct snd_midi_op *ops,
int dest_channel = 0;
if (ev == NULL || chanset == NULL) {
- snd_printd("ev or chanbase NULL (snd_midi_process_event)\n");
+ pr_debug("ALSA: seq_midi_emul: ev or chanbase NULL (snd_midi_process_event)\n");
return;
}
if (chanset->channels == NULL)
@@ -97,7 +87,7 @@ snd_midi_process_event(struct snd_midi_op *ops,
if (snd_seq_ev_is_channel_type(ev)) {
dest_channel = ev->data.note.channel;
if (dest_channel >= chanset->max_channels) {
- snd_printd("dest channel is %d, max is %d\n",
+ pr_debug("ALSA: seq_midi_emul: dest channel is %d, max is %d\n",
dest_channel, chanset->max_channels);
return;
}
@@ -231,17 +221,19 @@ snd_midi_process_event(struct snd_midi_op *ops,
case SNDRV_SEQ_EVENT_ECHO:
not_yet:
default:
- /*snd_printd("Unimplemented event %d\n", ev->type);*/
+ /*pr_debug("ALSA: seq_midi_emul: Unimplemented event %d\n", ev->type);*/
break;
}
}
+EXPORT_SYMBOL(snd_midi_process_event);
/*
* release note
*/
static void
-note_off(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
+note_off(const struct snd_midi_op *ops, void *drv,
+ struct snd_midi_channel *chan,
int note, int vel)
{
if (chan->gm_hold) {
@@ -263,11 +255,15 @@ note_off(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
* events that need to take place immediately to the driver.
*/
static void
-do_control(struct snd_midi_op *ops, void *drv, struct snd_midi_channel_set *chset,
+do_control(const struct snd_midi_op *ops, void *drv,
+ struct snd_midi_channel_set *chset,
struct snd_midi_channel *chan, int control, int value)
{
int i;
+ if (control >= ARRAY_SIZE(chan->control))
+ return;
+
/* Switches */
if ((control >=64 && control <=69) || (control >= 80 && control <= 83)) {
/* These are all switches; either off or on so set to 0 or 127 */
@@ -313,7 +309,7 @@ do_control(struct snd_midi_op *ops, void *drv, struct snd_midi_channel_set *chse
break;
case MIDI_CTL_MSB_DATA_ENTRY:
chan->control[MIDI_CTL_LSB_DATA_ENTRY] = 0;
- /* go through here */
+ fallthrough;
case MIDI_CTL_LSB_DATA_ENTRY:
if (chan->param_type == SNDRV_MIDI_PARAM_TYPE_REGISTERED)
rpn(ops, drv, chan, chset);
@@ -405,12 +401,13 @@ snd_midi_channel_set_clear(struct snd_midi_channel_set *chset)
chan->drum_channel = 0;
}
}
+EXPORT_SYMBOL(snd_midi_channel_set_clear);
/*
* Process a rpn message.
*/
static void
-rpn(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
+rpn(const struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
struct snd_midi_channel_set *chset)
{
int type;
@@ -450,7 +447,7 @@ rpn(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
* Process an nrpn message.
*/
static void
-nrpn(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
+nrpn(const struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan,
struct snd_midi_channel_set *chset)
{
/* parse XG NRPNs here if possible */
@@ -478,15 +475,15 @@ get_channel(unsigned char cmd)
* Process a sysex message.
*/
static void
-sysex(struct snd_midi_op *ops, void *private, unsigned char *buf, int len,
+sysex(const struct snd_midi_op *ops, void *private, unsigned char *buf, int len,
struct snd_midi_channel_set *chset)
{
/* GM on */
- static unsigned char gm_on_macro[] = {
+ static const unsigned char gm_on_macro[] = {
0x7e,0x7f,0x09,0x01,
};
/* XG on */
- static unsigned char xg_on_macro[] = {
+ static const unsigned char xg_on_macro[] = {
0x43,0x10,0x4c,0x00,0x00,0x7e,0x00,
};
/* GS prefix
@@ -495,7 +492,7 @@ sysex(struct snd_midi_op *ops, void *private, unsigned char *buf, int len,
* chorus mode: XX=0x01, YY=0x38, ZZ=0-7
* master vol: XX=0x00, YY=0x04, ZZ=0-127
*/
- static unsigned char gs_pfx_macro[] = {
+ static const unsigned char gs_pfx_macro[] = {
0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/
};
@@ -592,7 +589,8 @@ sysex(struct snd_midi_op *ops, void *private, unsigned char *buf, int len,
* all sound off
*/
static void
-all_sounds_off(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan)
+all_sounds_off(const struct snd_midi_op *ops, void *drv,
+ struct snd_midi_channel *chan)
{
int n;
@@ -610,7 +608,8 @@ all_sounds_off(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan
* all notes off
*/
static void
-all_notes_off(struct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan)
+all_notes_off(const struct snd_midi_op *ops, void *drv,
+ struct snd_midi_channel *chan)
{
int n;
@@ -651,7 +650,7 @@ static struct snd_midi_channel *snd_midi_channel_init_set(int n)
struct snd_midi_channel *chan;
int i;
- chan = kmalloc(n * sizeof(struct snd_midi_channel), GFP_KERNEL);
+ chan = kmalloc_array(n, sizeof(struct snd_midi_channel), GFP_KERNEL);
if (chan) {
for (i = 0; i < n; i++)
snd_midi_channel_init(chan+i, i);
@@ -697,6 +696,7 @@ struct snd_midi_channel_set *snd_midi_channel_alloc_set(int n)
}
return chset;
}
+EXPORT_SYMBOL(snd_midi_channel_alloc_set);
/*
* Reset the midi controllers on a particular channel to default values.
@@ -720,20 +720,4 @@ void snd_midi_channel_free_set(struct snd_midi_channel_set *chset)
kfree(chset->channels);
kfree(chset);
}
-
-static int __init alsa_seq_midi_emul_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_seq_midi_emul_exit(void)
-{
-}
-
-module_init(alsa_seq_midi_emul_init)
-module_exit(alsa_seq_midi_emul_exit)
-
-EXPORT_SYMBOL(snd_midi_process_event);
-EXPORT_SYMBOL(snd_midi_channel_set_clear);
-EXPORT_SYMBOL(snd_midi_channel_alloc_set);
EXPORT_SYMBOL(snd_midi_channel_free_set);
diff --git a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c
index b5d6ea4904c0..fa9dfc53c3fc 100644
--- a/sound/core/seq/seq_midi_event.c
+++ b/sound/core/seq/seq_midi_event.c
@@ -1,27 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* MIDI byte <-> sequencer event coder
*
* Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>,
* Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/string.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/seq_kernel.h>
#include <sound/seq_midi_event.h>
@@ -133,6 +121,7 @@ int snd_midi_event_new(int bufsize, struct snd_midi_event **rdev)
*rdev = dev;
return 0;
}
+EXPORT_SYMBOL(snd_midi_event_new);
void snd_midi_event_free(struct snd_midi_event *dev)
{
@@ -141,6 +130,7 @@ void snd_midi_event_free(struct snd_midi_event *dev)
kfree(dev);
}
}
+EXPORT_SYMBOL(snd_midi_event_free);
/*
* initialize record
@@ -154,97 +144,33 @@ static inline void reset_encode(struct snd_midi_event *dev)
void snd_midi_event_reset_encode(struct snd_midi_event *dev)
{
- unsigned long flags;
-
- spin_lock_irqsave(&dev->lock, flags);
+ guard(spinlock_irqsave)(&dev->lock);
reset_encode(dev);
- spin_unlock_irqrestore(&dev->lock, flags);
}
+EXPORT_SYMBOL(snd_midi_event_reset_encode);
void snd_midi_event_reset_decode(struct snd_midi_event *dev)
{
- unsigned long flags;
-
- spin_lock_irqsave(&dev->lock, flags);
+ guard(spinlock_irqsave)(&dev->lock);
dev->lastcmd = 0xff;
- spin_unlock_irqrestore(&dev->lock, flags);
-}
-
-#if 0
-void snd_midi_event_init(struct snd_midi_event *dev)
-{
- snd_midi_event_reset_encode(dev);
- snd_midi_event_reset_decode(dev);
}
-#endif /* 0 */
+EXPORT_SYMBOL(snd_midi_event_reset_decode);
void snd_midi_event_no_status(struct snd_midi_event *dev, int on)
{
dev->nostat = on ? 1 : 0;
}
-
-/*
- * resize buffer
- */
-#if 0
-int snd_midi_event_resize_buffer(struct snd_midi_event *dev, int bufsize)
-{
- unsigned char *new_buf, *old_buf;
- unsigned long flags;
-
- if (bufsize == dev->bufsize)
- return 0;
- new_buf = kmalloc(bufsize, GFP_KERNEL);
- if (new_buf == NULL)
- return -ENOMEM;
- spin_lock_irqsave(&dev->lock, flags);
- old_buf = dev->buf;
- dev->buf = new_buf;
- dev->bufsize = bufsize;
- reset_encode(dev);
- spin_unlock_irqrestore(&dev->lock, flags);
- kfree(old_buf);
- return 0;
-}
-#endif /* 0 */
-
-/*
- * read bytes and encode to sequencer event if finished
- * return the size of encoded bytes
- */
-long snd_midi_event_encode(struct snd_midi_event *dev, unsigned char *buf, long count,
- struct snd_seq_event *ev)
-{
- long result = 0;
- int rc;
-
- ev->type = SNDRV_SEQ_EVENT_NONE;
-
- while (count-- > 0) {
- rc = snd_midi_event_encode_byte(dev, *buf++, ev);
- result++;
- if (rc < 0)
- return rc;
- else if (rc > 0)
- return result;
- }
-
- return result;
-}
+EXPORT_SYMBOL(snd_midi_event_no_status);
/*
* read one byte and encode to sequencer event:
- * return 1 if MIDI bytes are encoded to an event
- * 0 data is not finished
- * negative for error
+ * return true if MIDI bytes are encoded to an event
+ * false data is not finished
*/
-int snd_midi_event_encode_byte(struct snd_midi_event *dev, int c,
- struct snd_seq_event *ev)
+bool snd_midi_event_encode_byte(struct snd_midi_event *dev, unsigned char c,
+ struct snd_seq_event *ev)
{
- int rc = 0;
- unsigned long flags;
-
- c &= 0xff;
+ bool rc = false;
if (c >= MIDI_CMD_COMMON_CLOCK) {
/* real-time event */
@@ -254,7 +180,7 @@ int snd_midi_event_encode_byte(struct snd_midi_event *dev, int c,
return ev->type != SNDRV_SEQ_EVENT_NONE;
}
- spin_lock_irqsave(&dev->lock, flags);
+ guard(spinlock_irqsave)(&dev->lock);
if ((c & 0x80) &&
(c != MIDI_CMD_COMMON_SYSEX_END || dev->type != ST_SYSEX)) {
/* new command */
@@ -286,7 +212,7 @@ int snd_midi_event_encode_byte(struct snd_midi_event *dev, int c,
status_event[dev->type].encode(dev, ev);
if (dev->type >= ST_SPECIAL)
dev->type = ST_INVALID;
- rc = 1;
+ rc = true;
} else if (dev->type == ST_SYSEX) {
if (c == MIDI_CMD_COMMON_SYSEX_END ||
dev->read >= dev->bufsize) {
@@ -299,13 +225,13 @@ int snd_midi_event_encode_byte(struct snd_midi_event *dev, int c,
dev->read = 0; /* continue to parse */
else
reset_encode(dev); /* all parsed */
- rc = 1;
+ rc = true;
}
}
- spin_unlock_irqrestore(&dev->lock, flags);
return rc;
}
+EXPORT_SYMBOL(snd_midi_event_encode_byte);
/* encode note event */
static void note_event(struct snd_midi_event *dev, struct snd_seq_event *ev)
@@ -407,6 +333,7 @@ long snd_midi_event_decode(struct snd_midi_event *dev, unsigned char *buf, long
return qlen;
}
}
+EXPORT_SYMBOL(snd_midi_event_decode);
/* decode note event */
@@ -487,12 +414,12 @@ static int extra_decode_xrpn(struct snd_midi_event *dev, unsigned char *buf,
int count, struct snd_seq_event *ev)
{
unsigned char cmd;
- char *cbytes;
- static char cbytes_nrpn[4] = { MIDI_CTL_NONREG_PARM_NUM_MSB,
+ const char *cbytes;
+ static const char cbytes_nrpn[4] = { MIDI_CTL_NONREG_PARM_NUM_MSB,
MIDI_CTL_NONREG_PARM_NUM_LSB,
MIDI_CTL_MSB_DATA_ENTRY,
MIDI_CTL_LSB_DATA_ENTRY };
- static char cbytes_rpn[4] = { MIDI_CTL_REGIST_PARM_NUM_MSB,
+ static const char cbytes_rpn[4] = { MIDI_CTL_REGIST_PARM_NUM_MSB,
MIDI_CTL_REGIST_PARM_NUM_LSB,
MIDI_CTL_MSB_DATA_ENTRY,
MIDI_CTL_LSB_DATA_ENTRY };
@@ -522,28 +449,3 @@ static int extra_decode_xrpn(struct snd_midi_event *dev, unsigned char *buf,
}
return idx;
}
-
-/*
- * exports
- */
-
-EXPORT_SYMBOL(snd_midi_event_new);
-EXPORT_SYMBOL(snd_midi_event_free);
-EXPORT_SYMBOL(snd_midi_event_reset_encode);
-EXPORT_SYMBOL(snd_midi_event_reset_decode);
-EXPORT_SYMBOL(snd_midi_event_no_status);
-EXPORT_SYMBOL(snd_midi_event_encode);
-EXPORT_SYMBOL(snd_midi_event_encode_byte);
-EXPORT_SYMBOL(snd_midi_event_decode);
-
-static int __init alsa_seq_midi_event_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_seq_midi_event_exit(void)
-{
-}
-
-module_init(alsa_seq_midi_event_init)
-module_exit(alsa_seq_midi_event_exit)
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
index 3bf7d73ac52e..40fa379847e5 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -1,27 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer Ports
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
* Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <sound/core.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include "seq_system.h"
#include "seq_ports.h"
#include "seq_clientmgr.h"
@@ -62,17 +48,15 @@ struct snd_seq_client_port *snd_seq_port_use_ptr(struct snd_seq_client *client,
if (client == NULL)
return NULL;
- read_lock(&client->ports_lock);
+ guard(read_lock)(&client->ports_lock);
list_for_each_entry(port, &client->ports_list_head, list) {
if (port->addr.port == num) {
if (port->closing)
break; /* deleting now */
snd_use_lock_use(&port->use_lock);
- read_unlock(&client->ports_lock);
return port;
}
}
- read_unlock(&client->ports_lock);
return NULL; /* not found */
}
@@ -83,11 +67,15 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *cl
{
int num;
struct snd_seq_client_port *port, *found;
+ bool check_inactive = (pinfo->capability & SNDRV_SEQ_PORT_CAP_INACTIVE);
num = pinfo->addr.port;
found = NULL;
- read_lock(&client->ports_lock);
+ guard(read_lock)(&client->ports_lock);
list_for_each_entry(port, &client->ports_list_head, list) {
+ if ((port->capability & SNDRV_SEQ_PORT_CAP_INACTIVE) &&
+ !check_inactive)
+ continue; /* skip inactive ports */
if (port->addr.port < num)
continue;
if (port->addr.port == num) {
@@ -103,7 +91,6 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *cl
else
snd_use_lock_use(&found->use_lock);
}
- read_unlock(&client->ports_lock);
return found;
}
@@ -121,42 +108,47 @@ static void port_subs_info_init(struct snd_seq_port_subs_info *grp)
}
-/* create a port, port number is returned (-1 on failure) */
-struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
- int port)
+/* create a port, port number or a negative error code is returned
+ * the caller needs to unref the port via snd_seq_port_unlock() appropriately
+ */
+int snd_seq_create_port(struct snd_seq_client *client, int port,
+ struct snd_seq_client_port **port_ret)
{
- unsigned long flags;
struct snd_seq_client_port *new_port, *p;
- int num = -1;
+ int num;
+ *port_ret = NULL;
+
/* sanity check */
if (snd_BUG_ON(!client))
- return NULL;
+ return -EINVAL;
- if (client->num_ports >= SNDRV_SEQ_MAX_PORTS - 1) {
- snd_printk(KERN_WARNING "too many ports for client %d\n", client->number);
- return NULL;
+ if (client->num_ports >= SNDRV_SEQ_MAX_PORTS) {
+ pr_warn("ALSA: seq: too many ports for client %d\n", client->number);
+ return -EINVAL;
}
/* create a new port */
new_port = kzalloc(sizeof(*new_port), GFP_KERNEL);
- if (! new_port) {
- snd_printd("malloc failed for registering client port\n");
- return NULL; /* failure, out of memory */
- }
+ if (!new_port)
+ return -ENOMEM; /* failure, out of memory */
/* init port data */
new_port->addr.client = client->number;
new_port->addr.port = -1;
new_port->owner = THIS_MODULE;
- sprintf(new_port->name, "port-%d", num);
snd_use_lock_init(&new_port->use_lock);
port_subs_info_init(&new_port->c_src);
port_subs_info_init(&new_port->c_dest);
+ snd_use_lock_use(&new_port->use_lock);
- num = port >= 0 ? port : 0;
- mutex_lock(&client->ports_mutex);
- write_lock_irqsave(&client->ports_lock, flags);
+ num = max(port, 0);
+ guard(mutex)(&client->ports_mutex);
+ guard(write_lock_irq)(&client->ports_lock);
list_for_each_entry(p, &client->ports_list_head, list) {
+ if (p->addr.port == port) {
+ kfree(new_port);
+ return -EBUSY;
+ }
if (p->addr.port > num)
break;
if (port < 0) /* auto-probe mode */
@@ -166,18 +158,13 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
list_add_tail(&new_port->list, &p->list);
client->num_ports++;
new_port->addr.port = num; /* store the port number in the port */
- write_unlock_irqrestore(&client->ports_lock, flags);
- mutex_unlock(&client->ports_mutex);
sprintf(new_port->name, "port-%d", num);
+ *port_ret = new_port;
- return new_port;
+ return num;
}
/* */
-enum group_type {
- SRC_LIST, DEST_LIST
-};
-
static int subscribe_port(struct snd_seq_client *client,
struct snd_seq_client_port *port,
struct snd_seq_port_subs_info *grp,
@@ -191,17 +178,24 @@ static int unsubscribe_port(struct snd_seq_client *client,
static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr,
struct snd_seq_client **cp)
{
- struct snd_seq_client_port *p;
*cp = snd_seq_client_use_ptr(addr->client);
- if (*cp) {
- p = snd_seq_port_use_ptr(*cp, addr->port);
- if (! p) {
- snd_seq_client_unlock(*cp);
- *cp = NULL;
- }
- return p;
- }
- return NULL;
+ if (!*cp)
+ return NULL;
+ return snd_seq_port_use_ptr(*cp, addr->port);
+}
+
+static void delete_and_unsubscribe_port(struct snd_seq_client *client,
+ struct snd_seq_client_port *port,
+ struct snd_seq_subscribers *subs,
+ bool is_src, bool ack);
+
+static inline struct snd_seq_subscribers *
+get_subscriber(struct list_head *p, bool is_src)
+{
+ if (is_src)
+ return list_entry(p, struct snd_seq_subscribers, src_list);
+ else
+ return list_entry(p, struct snd_seq_subscribers, dest_list);
}
/*
@@ -211,24 +205,22 @@ static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr,
static void clear_subscriber_list(struct snd_seq_client *client,
struct snd_seq_client_port *port,
struct snd_seq_port_subs_info *grp,
- int grptype)
+ int is_src)
{
struct list_head *p, *n;
list_for_each_safe(p, n, &grp->list_head) {
struct snd_seq_subscribers *subs;
- struct snd_seq_client *c;
- struct snd_seq_client_port *aport;
+ struct snd_seq_client *c __free(snd_seq_client) = NULL;
+ struct snd_seq_client_port *aport __free(snd_seq_port) = NULL;
- if (grptype == SRC_LIST) {
- subs = list_entry(p, struct snd_seq_subscribers, src_list);
+ subs = get_subscriber(p, is_src);
+ if (is_src)
aport = get_client_port(&subs->info.dest, &c);
- } else {
- subs = list_entry(p, struct snd_seq_subscribers, dest_list);
+ else
aport = get_client_port(&subs->info.sender, &c);
- }
- list_del(p);
- unsubscribe_port(client, port, grp, &subs->info, 0);
+ delete_and_unsubscribe_port(client, port, subs, is_src, false);
+
if (!aport) {
/* looks like the connected port is being deleted.
* we decrease the counter, and when both ports are deleted
@@ -236,21 +228,12 @@ static void clear_subscriber_list(struct snd_seq_client *client,
*/
if (atomic_dec_and_test(&subs->ref_count))
kfree(subs);
- } else {
- /* ok we got the connected port */
- struct snd_seq_port_subs_info *agrp;
- agrp = (grptype == SRC_LIST) ? &aport->c_dest : &aport->c_src;
- down_write(&agrp->list_mutex);
- if (grptype == SRC_LIST)
- list_del(&subs->dest_list);
- else
- list_del(&subs->src_list);
- up_write(&agrp->list_mutex);
- unsubscribe_port(c, aport, agrp, &subs->info, 1);
- kfree(subs);
- snd_seq_port_unlock(aport);
- snd_seq_client_unlock(c);
+ continue;
}
+
+ /* ok we got the connected port */
+ delete_and_unsubscribe_port(c, aport, subs, !is_src, true);
+ kfree(subs);
}
}
@@ -263,8 +246,8 @@ static int port_delete(struct snd_seq_client *client,
snd_use_lock_sync(&port->use_lock);
/* clear subscribers info */
- clear_subscriber_list(client, port, &port->c_src, SRC_LIST);
- clear_subscriber_list(client, port, &port->c_dest, DEST_LIST);
+ clear_subscriber_list(client, port, &port->c_src, true);
+ clear_subscriber_list(client, port, &port->c_dest, false);
if (port->private_free)
port->private_free(port->private_data);
@@ -280,22 +263,20 @@ static int port_delete(struct snd_seq_client *client,
/* delete a port with the given port id */
int snd_seq_delete_port(struct snd_seq_client *client, int port)
{
- unsigned long flags;
struct snd_seq_client_port *found = NULL, *p;
- mutex_lock(&client->ports_mutex);
- write_lock_irqsave(&client->ports_lock, flags);
- list_for_each_entry(p, &client->ports_list_head, list) {
- if (p->addr.port == port) {
- /* ok found. delete from the list at first */
- list_del(&p->list);
- client->num_ports--;
- found = p;
- break;
+ scoped_guard(mutex, &client->ports_mutex) {
+ guard(write_lock_irq)(&client->ports_lock);
+ list_for_each_entry(p, &client->ports_list_head, list) {
+ if (p->addr.port == port) {
+ /* ok found. delete from the list at first */
+ list_del(&p->list);
+ client->num_ports--;
+ found = p;
+ break;
+ }
}
}
- write_unlock_irqrestore(&client->ports_lock, flags);
- mutex_unlock(&client->ports_mutex);
if (found)
return port_delete(client, found);
else
@@ -305,23 +286,22 @@ int snd_seq_delete_port(struct snd_seq_client *client, int port)
/* delete the all ports belonging to the given client */
int snd_seq_delete_all_ports(struct snd_seq_client *client)
{
- unsigned long flags;
struct list_head deleted_list;
struct snd_seq_client_port *port, *tmp;
/* move the port list to deleted_list, and
* clear the port list in the client data.
*/
- mutex_lock(&client->ports_mutex);
- write_lock_irqsave(&client->ports_lock, flags);
- if (! list_empty(&client->ports_list_head)) {
- list_add(&deleted_list, &client->ports_list_head);
- list_del_init(&client->ports_list_head);
- } else {
- INIT_LIST_HEAD(&deleted_list);
+ guard(mutex)(&client->ports_mutex);
+ scoped_guard(write_lock_irq, &client->ports_lock) {
+ if (!list_empty(&client->ports_list_head)) {
+ list_add(&deleted_list, &client->ports_list_head);
+ list_del_init(&client->ports_list_head);
+ } else {
+ INIT_LIST_HEAD(&deleted_list);
+ }
+ client->num_ports = 0;
}
- client->num_ports = 0;
- write_unlock_irqrestore(&client->ports_lock, flags);
/* remove each port in deleted_list */
list_for_each_entry_safe(port, tmp, &deleted_list, list) {
@@ -329,7 +309,6 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client)
snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port);
port_delete(client, port);
}
- mutex_unlock(&client->ports_mutex);
return 0;
}
@@ -342,7 +321,7 @@ int snd_seq_set_port_info(struct snd_seq_client_port * port,
/* set port name */
if (info->name[0])
- strlcpy(port->name, info->name, sizeof(port->name));
+ strscpy(port->name, info->name, sizeof(port->name));
/* set capabilities */
port->capability = info->capability;
@@ -360,6 +339,22 @@ int snd_seq_set_port_info(struct snd_seq_client_port * port,
port->time_real = (info->flags & SNDRV_SEQ_PORT_FLG_TIME_REAL) ? 1 : 0;
port->time_queue = info->time_queue;
+ /* UMP direction and group */
+ port->direction = info->direction;
+ port->ump_group = info->ump_group;
+ if (port->ump_group > SNDRV_UMP_MAX_GROUPS)
+ port->ump_group = 0;
+
+ /* fill default port direction */
+ if (!port->direction) {
+ if (info->capability & SNDRV_SEQ_PORT_CAP_READ)
+ port->direction |= SNDRV_SEQ_PORT_DIR_INPUT;
+ if (info->capability & SNDRV_SEQ_PORT_CAP_WRITE)
+ port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT;
+ }
+
+ port->is_midi1 = !!(info->flags & SNDRV_SEQ_PORT_FLG_IS_MIDI1);
+
return 0;
}
@@ -371,7 +366,7 @@ int snd_seq_get_port_info(struct snd_seq_client_port * port,
return -EINVAL;
/* get port name */
- strlcpy(info->name, port->name, sizeof(info->name));
+ strscpy(info->name, port->name, sizeof(info->name));
/* get capabilities */
info->capability = port->capability;
@@ -397,6 +392,13 @@ int snd_seq_get_port_info(struct snd_seq_client_port * port,
info->time_queue = port->time_queue;
}
+ if (port->is_midi1)
+ info->flags |= SNDRV_SEQ_PORT_FLG_IS_MIDI1;
+
+ /* UMP direction and group */
+ info->direction = port->direction;
+ info->ump_group = port->ump_group;
+
return 0;
}
@@ -410,9 +412,6 @@ int snd_seq_get_port_info(struct snd_seq_client_port * port,
* invoked.
* This feature is useful if these callbacks are associated with
* initialization or termination of devices (see seq_midi.c).
- *
- * If callback_all option is set, the callback function is invoked
- * at each connnection/disconnection.
*/
static int subscribe_port(struct snd_seq_client *client,
@@ -426,7 +425,7 @@ static int subscribe_port(struct snd_seq_client *client,
if (!try_module_get(port->owner))
return -EFAULT;
grp->count++;
- if (grp->open && (port->callback_all || grp->count == 1)) {
+ if (grp->open && grp->count == 1) {
err = grp->open(port->private_data, info);
if (err < 0) {
module_put(port->owner);
@@ -451,7 +450,7 @@ static int unsubscribe_port(struct snd_seq_client *client,
if (! grp->count)
return -EINVAL;
grp->count--;
- if (grp->close && (port->callback_all || grp->count == 0))
+ if (grp->close && grp->count == 0)
err = grp->close(port->private_data, info);
if (send_ack && client->type == USER_CLIENT)
snd_seq_client_notify_subscription(port->addr.client, port->addr.port,
@@ -483,6 +482,84 @@ static int match_subs_info(struct snd_seq_port_subscribe *r,
return 0;
}
+static int check_and_subscribe_port(struct snd_seq_client *client,
+ struct snd_seq_client_port *port,
+ struct snd_seq_subscribers *subs,
+ bool is_src, bool exclusive, bool ack)
+{
+ struct snd_seq_port_subs_info *grp;
+ struct list_head *p;
+ struct snd_seq_subscribers *s;
+ int err;
+
+ grp = is_src ? &port->c_src : &port->c_dest;
+ guard(rwsem_write)(&grp->list_mutex);
+ if (exclusive) {
+ if (!list_empty(&grp->list_head))
+ return -EBUSY;
+ } else {
+ if (grp->exclusive)
+ return -EBUSY;
+ /* check whether already exists */
+ list_for_each(p, &grp->list_head) {
+ s = get_subscriber(p, is_src);
+ if (match_subs_info(&subs->info, &s->info))
+ return -EBUSY;
+ }
+ }
+
+ err = subscribe_port(client, port, grp, &subs->info, ack);
+ if (err < 0) {
+ grp->exclusive = 0;
+ return err;
+ }
+
+ /* add to list */
+ guard(write_lock_irq)(&grp->list_lock);
+ if (is_src)
+ list_add_tail(&subs->src_list, &grp->list_head);
+ else
+ list_add_tail(&subs->dest_list, &grp->list_head);
+ grp->exclusive = exclusive;
+ atomic_inc(&subs->ref_count);
+
+ return 0;
+}
+
+/* called with grp->list_mutex held */
+static void __delete_and_unsubscribe_port(struct snd_seq_client *client,
+ struct snd_seq_client_port *port,
+ struct snd_seq_subscribers *subs,
+ bool is_src, bool ack)
+{
+ struct snd_seq_port_subs_info *grp;
+ struct list_head *list;
+ bool empty;
+
+ grp = is_src ? &port->c_src : &port->c_dest;
+ list = is_src ? &subs->src_list : &subs->dest_list;
+ scoped_guard(write_lock_irq, &grp->list_lock) {
+ empty = list_empty(list);
+ if (!empty)
+ list_del_init(list);
+ grp->exclusive = 0;
+ }
+
+ if (!empty)
+ unsubscribe_port(client, port, grp, &subs->info, ack);
+}
+
+static void delete_and_unsubscribe_port(struct snd_seq_client *client,
+ struct snd_seq_client_port *port,
+ struct snd_seq_subscribers *subs,
+ bool is_src, bool ack)
+{
+ struct snd_seq_port_subs_info *grp;
+
+ grp = is_src ? &port->c_src : &port->c_dest;
+ guard(rwsem_write)(&grp->list_mutex);
+ __delete_and_unsubscribe_port(client, port, subs, is_src, ack);
+}
/* connect two ports */
int snd_seq_port_connect(struct snd_seq_client *connector,
@@ -492,76 +569,42 @@ int snd_seq_port_connect(struct snd_seq_client *connector,
struct snd_seq_client_port *dest_port,
struct snd_seq_port_subscribe *info)
{
- struct snd_seq_port_subs_info *src = &src_port->c_src;
- struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
- struct snd_seq_subscribers *subs, *s;
- int err, src_called = 0;
- unsigned long flags;
- int exclusive;
+ struct snd_seq_subscribers *subs;
+ bool exclusive;
+ int err;
subs = kzalloc(sizeof(*subs), GFP_KERNEL);
- if (! subs)
+ if (!subs)
return -ENOMEM;
subs->info = *info;
- atomic_set(&subs->ref_count, 2);
+ atomic_set(&subs->ref_count, 0);
+ INIT_LIST_HEAD(&subs->src_list);
+ INIT_LIST_HEAD(&subs->dest_list);
+
+ exclusive = !!(info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE);
+
+ err = check_and_subscribe_port(src_client, src_port, subs, true,
+ exclusive,
+ connector->number != src_client->number);
+ if (err < 0)
+ goto error;
+ err = check_and_subscribe_port(dest_client, dest_port, subs, false,
+ exclusive,
+ connector->number != dest_client->number);
+ if (err < 0)
+ goto error_dest;
- down_write(&src->list_mutex);
- down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
-
- exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0;
- err = -EBUSY;
- if (exclusive) {
- if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head))
- goto __error;
- } else {
- if (src->exclusive || dest->exclusive)
- goto __error;
- /* check whether already exists */
- list_for_each_entry(s, &src->list_head, src_list) {
- if (match_subs_info(info, &s->info))
- goto __error;
- }
- list_for_each_entry(s, &dest->list_head, dest_list) {
- if (match_subs_info(info, &s->info))
- goto __error;
- }
- }
-
- if ((err = subscribe_port(src_client, src_port, src, info,
- connector->number != src_client->number)) < 0)
- goto __error;
- src_called = 1;
-
- if ((err = subscribe_port(dest_client, dest_port, dest, info,
- connector->number != dest_client->number)) < 0)
- goto __error;
-
- /* add to list */
- write_lock_irqsave(&src->list_lock, flags);
- // write_lock(&dest->list_lock); // no other lock yet
- list_add_tail(&subs->src_list, &src->list_head);
- list_add_tail(&subs->dest_list, &dest->list_head);
- // write_unlock(&dest->list_lock); // no other lock yet
- write_unlock_irqrestore(&src->list_lock, flags);
-
- src->exclusive = dest->exclusive = exclusive;
-
- up_write(&dest->list_mutex);
- up_write(&src->list_mutex);
return 0;
- __error:
- if (src_called)
- unsubscribe_port(src_client, src_port, src, info,
- connector->number != src_client->number);
+ error_dest:
+ delete_and_unsubscribe_port(src_client, src_port, subs, true,
+ connector->number != src_client->number);
+ error:
kfree(subs);
- up_write(&dest->list_mutex);
- up_write(&src->list_mutex);
return err;
}
-
/* remove the connection */
int snd_seq_port_disconnect(struct snd_seq_client *connector,
struct snd_seq_client *src_client,
@@ -570,56 +613,52 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector,
struct snd_seq_client_port *dest_port,
struct snd_seq_port_subscribe *info)
{
- struct snd_seq_port_subs_info *src = &src_port->c_src;
struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
struct snd_seq_subscribers *subs;
int err = -ENOENT;
- unsigned long flags;
-
- down_write(&src->list_mutex);
- down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
-
- /* look for the connection */
- list_for_each_entry(subs, &src->list_head, src_list) {
- if (match_subs_info(info, &subs->info)) {
- write_lock_irqsave(&src->list_lock, flags);
- // write_lock(&dest->list_lock); // no lock yet
- list_del(&subs->src_list);
- list_del(&subs->dest_list);
- // write_unlock(&dest->list_lock);
- write_unlock_irqrestore(&src->list_lock, flags);
- src->exclusive = dest->exclusive = 0;
- unsubscribe_port(src_client, src_port, src, info,
- connector->number != src_client->number);
- unsubscribe_port(dest_client, dest_port, dest, info,
- connector->number != dest_client->number);
- kfree(subs);
- err = 0;
- break;
+
+ /* always start from deleting the dest port for avoiding concurrent
+ * deletions
+ */
+ scoped_guard(rwsem_write, &dest->list_mutex) {
+ /* look for the connection */
+ list_for_each_entry(subs, &dest->list_head, dest_list) {
+ if (match_subs_info(info, &subs->info)) {
+ __delete_and_unsubscribe_port(dest_client, dest_port,
+ subs, false,
+ connector->number != dest_client->number);
+ err = 0;
+ break;
+ }
}
}
+ if (err < 0)
+ return err;
- up_write(&dest->list_mutex);
- up_write(&src->list_mutex);
- return err;
+ delete_and_unsubscribe_port(src_client, src_port, subs, true,
+ connector->number != src_client->number);
+ kfree(subs);
+ return 0;
}
/* get matched subscriber */
-struct snd_seq_subscribers *snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
- struct snd_seq_addr *dest_addr)
+int snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
+ struct snd_seq_addr *dest_addr,
+ struct snd_seq_port_subscribe *subs)
{
- struct snd_seq_subscribers *s, *found = NULL;
+ struct snd_seq_subscribers *s;
+ int err = -ENOENT;
- down_read(&src_grp->list_mutex);
+ guard(rwsem_read)(&src_grp->list_mutex);
list_for_each_entry(s, &src_grp->list_head, src_list) {
if (addr_match(dest_addr, &s->info.dest)) {
- found = s;
+ *subs = s->info;
+ err = 0;
break;
}
}
- up_read(&src_grp->list_mutex);
- return found;
+ return err;
}
/*
@@ -640,7 +679,7 @@ int snd_seq_event_port_attach(int client,
/* Set up the port */
memset(&portinfo, 0, sizeof(portinfo));
portinfo.addr.client = client;
- strlcpy(portinfo.name, portname ? portname : "Unamed port",
+ strscpy(portinfo.name, portname ? portname : "Unnamed port",
sizeof(portinfo.name));
portinfo.capability = cap;
@@ -659,7 +698,6 @@ int snd_seq_event_port_attach(int client,
return ret;
}
-
EXPORT_SYMBOL(snd_seq_event_port_attach);
/*
@@ -680,5 +718,4 @@ int snd_seq_event_port_detach(int client, int port)
return err;
}
-
EXPORT_SYMBOL(snd_seq_event_port_detach);
diff --git a/sound/core/seq/seq_ports.h b/sound/core/seq/seq_ports.h
index 9d7117118ba4..40ed6cf7cb90 100644
--- a/sound/core/seq/seq_ports.h
+++ b/sound/core/seq/seq_ports.h
@@ -1,27 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA sequencer Ports
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __SND_SEQ_PORTS_H
#define __SND_SEQ_PORTS_H
#include <sound/seq_kernel.h>
+#include <sound/ump_convert.h>
#include "seq_lock.h"
/* list of 'exported' ports */
@@ -73,7 +59,6 @@ struct snd_seq_client_port {
int atomic, int hop);
void (*private_free)(void *private_data);
void *private_data;
- unsigned int callback_all : 1;
unsigned int closing : 1;
unsigned int timestamping: 1;
unsigned int time_real: 1;
@@ -88,6 +73,15 @@ struct snd_seq_client_port {
int midi_voices;
int synth_voices;
+ /* UMP direction and group */
+ unsigned char direction;
+ unsigned char ump_group;
+
+ bool is_midi1; /* keep MIDI 1.0 protocol */
+
+#if IS_ENABLED(CONFIG_SND_SEQ_UMP)
+ struct ump_cvt_to_ump_bank midi2_bank[16]; /* per channel */
+#endif
};
struct snd_seq_client;
@@ -102,8 +96,11 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(struct snd_seq_client *cl
/* unlock the port */
#define snd_seq_port_unlock(port) snd_use_lock_free(&(port)->use_lock)
-/* create a port, port number is returned (-1 on failure) */
-struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client, int port_index);
+DEFINE_FREE(snd_seq_port, struct snd_seq_client_port *, if (!IS_ERR_OR_NULL(_T)) snd_seq_port_unlock(_T))
+
+/* create a port, port number or a negative error code is returned */
+int snd_seq_create_port(struct snd_seq_client *client, int port_index,
+ struct snd_seq_client_port **port_ret);
/* delete a port */
int snd_seq_delete_port(struct snd_seq_client *client, int port);
@@ -136,7 +133,8 @@ int snd_seq_port_subscribe(struct snd_seq_client_port *port,
struct snd_seq_port_subscribe *info);
/* get matched subscriber */
-struct snd_seq_subscribers *snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
- struct snd_seq_addr *dest_addr);
+int snd_seq_port_get_subscription(struct snd_seq_port_subs_info *src_grp,
+ struct snd_seq_addr *dest_addr,
+ struct snd_seq_port_subscribe *subs);
#endif
diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c
index 29896ab23403..e649485a8772 100644
--- a/sound/core/seq/seq_prioq.c
+++ b/sound/core/seq/seq_prioq.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer Priority Queue
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/time.h>
@@ -59,10 +44,8 @@ struct snd_seq_prioq *snd_seq_prioq_new(void)
struct snd_seq_prioq *f;
f = kzalloc(sizeof(*f), GFP_KERNEL);
- if (f == NULL) {
- snd_printd("oops: malloc failed for snd_seq_prioq_new()\n");
+ if (!f)
return NULL;
- }
spin_lock_init(&f->lock);
f->head = NULL;
@@ -79,7 +62,7 @@ void snd_seq_prioq_delete(struct snd_seq_prioq **fifo)
*fifo = NULL;
if (f == NULL) {
- snd_printd("oops: snd_seq_prioq_delete() called with NULL prioq\n");
+ pr_debug("ALSA: seq: snd_seq_prioq_delete() called with NULL prioq\n");
return;
}
@@ -89,7 +72,7 @@ void snd_seq_prioq_delete(struct snd_seq_prioq **fifo)
if (f->cells > 0) {
/* drain prioQ */
while (f->cells > 0)
- snd_seq_cell_free(snd_seq_prioq_cell_out(f));
+ snd_seq_cell_free(snd_seq_prioq_cell_out(f, NULL));
}
kfree(f);
@@ -149,7 +132,6 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
struct snd_seq_event_cell * cell)
{
struct snd_seq_event_cell *cur, *prev;
- unsigned long flags;
int count;
int prior;
@@ -159,7 +141,7 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
/* check flags */
prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK);
- spin_lock_irqsave(&f->lock, flags);
+ guard(spinlock_irqsave)(&f->lock);
/* check if this element needs to inserted at the end (ie. ordered
data is inserted) This will be very likeley if a sequencer
@@ -171,7 +153,6 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
f->tail = cell;
cell->next = NULL;
f->cells++;
- spin_unlock_irqrestore(&f->lock, flags);
return 0;
}
}
@@ -196,8 +177,7 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
prev = cur;
cur = cur->next;
if (! --count) {
- spin_unlock_irqrestore(&f->lock, flags);
- snd_printk(KERN_ERR "cannot find a pointer.. infinite loop?\n");
+ pr_err("ALSA: seq: cannot find a pointer.. infinite loop?\n");
return -EINVAL;
}
}
@@ -212,23 +192,33 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,
if (cur == NULL) /* reached end of the list */
f->tail = cell;
f->cells++;
- spin_unlock_irqrestore(&f->lock, flags);
return 0;
}
+/* return 1 if the current time >= event timestamp */
+static int event_is_ready(struct snd_seq_event *ev, void *current_time)
+{
+ if ((ev->flags & SNDRV_SEQ_TIME_STAMP_MASK) == SNDRV_SEQ_TIME_STAMP_TICK)
+ return snd_seq_compare_tick_time(current_time, &ev->time.tick);
+ else
+ return snd_seq_compare_real_time(current_time, &ev->time.time);
+}
+
/* dequeue cell from prioq */
-struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f)
+struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f,
+ void *current_time)
{
struct snd_seq_event_cell *cell;
- unsigned long flags;
if (f == NULL) {
- snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
+ pr_debug("ALSA: seq: snd_seq_prioq_cell_in() called with NULL prioq\n");
return NULL;
}
- spin_lock_irqsave(&f->lock, flags);
+ guard(spinlock_irqsave)(&f->lock);
cell = f->head;
+ if (cell && current_time && !event_is_ready(&cell->event, current_time))
+ cell = NULL;
if (cell) {
f->head = cell->next;
@@ -240,7 +230,6 @@ struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f)
f->cells--;
}
- spin_unlock_irqrestore(&f->lock, flags);
return cell;
}
@@ -248,91 +237,49 @@ struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f)
int snd_seq_prioq_avail(struct snd_seq_prioq * f)
{
if (f == NULL) {
- snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
+ pr_debug("ALSA: seq: snd_seq_prioq_cell_in() called with NULL prioq\n");
return 0;
}
return f->cells;
}
-
-/* peek at cell at the head of the prioq */
-struct snd_seq_event_cell *snd_seq_prioq_cell_peek(struct snd_seq_prioq * f)
-{
- if (f == NULL) {
- snd_printd("oops: snd_seq_prioq_cell_in() called with NULL prioq\n");
- return NULL;
- }
- return f->head;
-}
-
-
-static inline int prioq_match(struct snd_seq_event_cell *cell,
- int client, int timestamp)
-{
- if (cell->event.source.client == client ||
- cell->event.dest.client == client)
- return 1;
- if (!timestamp)
- return 0;
- switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
- case SNDRV_SEQ_TIME_STAMP_TICK:
- if (cell->event.time.tick)
- return 1;
- break;
- case SNDRV_SEQ_TIME_STAMP_REAL:
- if (cell->event.time.time.tv_sec ||
- cell->event.time.time.tv_nsec)
- return 1;
- break;
- }
- return 0;
-}
-
-/* remove cells for left client */
-void snd_seq_prioq_leave(struct snd_seq_prioq * f, int client, int timestamp)
+/* remove cells matching with the condition */
+static void prioq_remove_cells(struct snd_seq_prioq *f,
+ bool (*match)(struct snd_seq_event_cell *cell,
+ void *arg),
+ void *arg)
{
register struct snd_seq_event_cell *cell, *next;
- unsigned long flags;
struct snd_seq_event_cell *prev = NULL;
struct snd_seq_event_cell *freefirst = NULL, *freeprev = NULL, *freenext;
/* collect all removed cells */
- spin_lock_irqsave(&f->lock, flags);
- cell = f->head;
- while (cell) {
- next = cell->next;
- if (prioq_match(cell, client, timestamp)) {
+ scoped_guard(spinlock_irqsave, &f->lock) {
+ for (cell = f->head; cell; cell = next) {
+ next = cell->next;
+ if (!match(cell, arg)) {
+ prev = cell;
+ continue;
+ }
+
/* remove cell from prioq */
- if (cell == f->head) {
+ if (cell == f->head)
f->head = cell->next;
- } else {
+ else
prev->next = cell->next;
- }
if (cell == f->tail)
f->tail = cell->next;
f->cells--;
+
/* add cell to free list */
cell->next = NULL;
- if (freefirst == NULL) {
+ if (freefirst == NULL)
freefirst = cell;
- } else {
+ else
freeprev->next = cell;
- }
freeprev = cell;
- } else {
-#if 0
- printk(KERN_DEBUG "type = %i, source = %i, dest = %i, "
- "client = %i\n",
- cell->event.type,
- cell->event.source.client,
- cell->event.dest.client,
- client);
-#endif
- prev = cell;
}
- cell = next;
}
- spin_unlock_irqrestore(&f->lock, flags);
/* remove selected cells */
while (freefirst) {
@@ -342,22 +289,68 @@ void snd_seq_prioq_leave(struct snd_seq_prioq * f, int client, int timestamp)
}
}
-static int prioq_remove_match(struct snd_seq_remove_events *info,
- struct snd_seq_event *ev)
+struct prioq_match_arg {
+ int client;
+ int timestamp;
+};
+
+static inline bool prioq_match(struct snd_seq_event_cell *cell, void *arg)
+{
+ struct prioq_match_arg *v = arg;
+
+ if (cell->event.source.client == v->client ||
+ cell->event.dest.client == v->client)
+ return true;
+ if (!v->timestamp)
+ return false;
+ switch (cell->event.flags & SNDRV_SEQ_TIME_STAMP_MASK) {
+ case SNDRV_SEQ_TIME_STAMP_TICK:
+ if (cell->event.time.tick)
+ return true;
+ break;
+ case SNDRV_SEQ_TIME_STAMP_REAL:
+ if (cell->event.time.time.tv_sec ||
+ cell->event.time.time.tv_nsec)
+ return true;
+ break;
+ }
+ return false;
+}
+
+/* remove cells for left client */
+void snd_seq_prioq_leave(struct snd_seq_prioq *f, int client, int timestamp)
{
+ struct prioq_match_arg arg = { client, timestamp };
+
+ return prioq_remove_cells(f, prioq_match, &arg);
+}
+
+struct prioq_remove_match_arg {
+ int client;
+ struct snd_seq_remove_events *info;
+};
+
+static bool prioq_remove_match(struct snd_seq_event_cell *cell, void *arg)
+{
+ struct prioq_remove_match_arg *v = arg;
+ struct snd_seq_event *ev = &cell->event;
+ struct snd_seq_remove_events *info = v->info;
int res;
+ if (ev->source.client != v->client)
+ return false;
+
if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) {
if (ev->dest.client != info->dest.client ||
ev->dest.port != info->dest.port)
- return 0;
+ return false;
}
if (info->remove_mode & SNDRV_SEQ_REMOVE_DEST_CHANNEL) {
if (! snd_seq_ev_is_channel_type(ev))
- return 0;
+ return false;
/* data.note.channel and data.control.channel are identical */
if (ev->data.note.channel != info->channel)
- return 0;
+ return false;
}
if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_AFTER) {
if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
@@ -365,7 +358,7 @@ static int prioq_remove_match(struct snd_seq_remove_events *info,
else
res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
if (!res)
- return 0;
+ return false;
}
if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_BEFORE) {
if (info->remove_mode & SNDRV_SEQ_REMOVE_TIME_TICK)
@@ -373,81 +366,35 @@ static int prioq_remove_match(struct snd_seq_remove_events *info,
else
res = snd_seq_compare_real_time(&ev->time.time, &info->time.time);
if (res)
- return 0;
+ return false;
}
if (info->remove_mode & SNDRV_SEQ_REMOVE_EVENT_TYPE) {
if (ev->type != info->type)
- return 0;
+ return false;
}
if (info->remove_mode & SNDRV_SEQ_REMOVE_IGNORE_OFF) {
/* Do not remove off events */
switch (ev->type) {
case SNDRV_SEQ_EVENT_NOTEOFF:
/* case SNDRV_SEQ_EVENT_SAMPLE_STOP: */
- return 0;
+ return false;
default:
break;
}
}
if (info->remove_mode & SNDRV_SEQ_REMOVE_TAG_MATCH) {
if (info->tag != ev->tag)
- return 0;
+ return false;
}
- return 1;
+ return true;
}
/* remove cells matching remove criteria */
void snd_seq_prioq_remove_events(struct snd_seq_prioq * f, int client,
struct snd_seq_remove_events *info)
{
- struct snd_seq_event_cell *cell, *next;
- unsigned long flags;
- struct snd_seq_event_cell *prev = NULL;
- struct snd_seq_event_cell *freefirst = NULL, *freeprev = NULL, *freenext;
-
- /* collect all removed cells */
- spin_lock_irqsave(&f->lock, flags);
- cell = f->head;
-
- while (cell) {
- next = cell->next;
- if (cell->event.source.client == client &&
- prioq_remove_match(info, &cell->event)) {
-
- /* remove cell from prioq */
- if (cell == f->head) {
- f->head = cell->next;
- } else {
- prev->next = cell->next;
- }
-
- if (cell == f->tail)
- f->tail = cell->next;
- f->cells--;
-
- /* add cell to free list */
- cell->next = NULL;
- if (freefirst == NULL) {
- freefirst = cell;
- } else {
- freeprev->next = cell;
- }
+ struct prioq_remove_match_arg arg = { client, info };
- freeprev = cell;
- } else {
- prev = cell;
- }
- cell = next;
- }
- spin_unlock_irqrestore(&f->lock, flags);
-
- /* remove selected cells */
- while (freefirst) {
- freenext = freefirst->next;
- snd_seq_cell_free(freefirst);
- freefirst = freenext;
- }
+ return prioq_remove_cells(f, prioq_remove_match, &arg);
}
-
-
diff --git a/sound/core/seq/seq_prioq.h b/sound/core/seq/seq_prioq.h
index d38bb78d9345..5811a87deb8a 100644
--- a/sound/core/seq/seq_prioq.h
+++ b/sound/core/seq/seq_prioq.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA sequencer Priority Queue
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __SND_SEQ_PRIOQ_H
#define __SND_SEQ_PRIOQ_H
@@ -44,14 +29,12 @@ void snd_seq_prioq_delete(struct snd_seq_prioq **fifo);
int snd_seq_prioq_cell_in(struct snd_seq_prioq *f, struct snd_seq_event_cell *cell);
/* dequeue cell from prioq */
-struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f);
+struct snd_seq_event_cell *snd_seq_prioq_cell_out(struct snd_seq_prioq *f,
+ void *current_time);
/* return number of events available in prioq */
int snd_seq_prioq_avail(struct snd_seq_prioq *f);
-/* peek at cell at the head of the prioq */
-struct snd_seq_event_cell *snd_seq_prioq_cell_peek(struct snd_seq_prioq *f);
-
/* client left queue */
void snd_seq_prioq_leave(struct snd_seq_prioq *f, int client, int timestamp);
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index e7a8e9e4edb2..f5c0e401c8ae 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer Timing queue handling
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
* MAJOR CHANGES
* Nov. 13, 1999 Takashi Iwai <iwai@ww.uni-erlangen.de>
* - Queues are allocated dynamically via ioctl.
@@ -63,43 +50,35 @@ int snd_seq_queue_get_cur_queues(void)
static int queue_list_add(struct snd_seq_queue *q)
{
int i;
- unsigned long flags;
- spin_lock_irqsave(&queue_list_lock, flags);
+ guard(spinlock_irqsave)(&queue_list_lock);
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
if (! queue_list[i]) {
queue_list[i] = q;
q->queue = i;
num_queues++;
- spin_unlock_irqrestore(&queue_list_lock, flags);
return i;
}
}
- spin_unlock_irqrestore(&queue_list_lock, flags);
return -1;
}
static struct snd_seq_queue *queue_list_remove(int id, int client)
{
struct snd_seq_queue *q;
- unsigned long flags;
- spin_lock_irqsave(&queue_list_lock, flags);
+ guard(spinlock_irqsave)(&queue_list_lock);
q = queue_list[id];
if (q) {
- spin_lock(&q->owner_lock);
+ guard(spinlock)(&q->owner_lock);
if (q->owner == client) {
/* found */
q->klocked = 1;
- spin_unlock(&q->owner_lock);
queue_list[id] = NULL;
num_queues--;
- spin_unlock_irqrestore(&queue_list_lock, flags);
return q;
}
- spin_unlock(&q->owner_lock);
}
- spin_unlock_irqrestore(&queue_list_lock, flags);
return NULL;
}
@@ -111,10 +90,8 @@ static struct snd_seq_queue *queue_new(int owner, int locked)
struct snd_seq_queue *q;
q = kzalloc(sizeof(*q), GFP_KERNEL);
- if (q == NULL) {
- snd_printd("malloc failed for snd_seq_queue_new()\n");
+ if (!q)
return NULL;
- }
spin_lock_init(&q->owner_lock);
spin_lock_init(&q->check_lock);
@@ -144,8 +121,10 @@ static struct snd_seq_queue *queue_new(int owner, int locked)
static void queue_delete(struct snd_seq_queue *q)
{
/* stop and release the timer */
+ mutex_lock(&q->timer_mutex);
snd_seq_timer_stop(q->timer);
snd_seq_timer_close(q);
+ mutex_unlock(&q->timer_mutex);
/* wait until access free */
snd_use_lock_sync(&q->use_lock);
/* release resources... */
@@ -159,18 +138,8 @@ static void queue_delete(struct snd_seq_queue *q)
/*----------------------------------------------------------------*/
-/* setup queues */
-int __init snd_seq_queues_init(void)
-{
- /*
- memset(queue_list, 0, sizeof(queue_list));
- num_queues = 0;
- */
- return 0;
-}
-
/* delete all existing queues */
-void __exit snd_seq_queues_delete(void)
+void snd_seq_queues_delete(void)
{
int i;
@@ -181,23 +150,29 @@ void __exit snd_seq_queues_delete(void)
}
}
+static void queue_use(struct snd_seq_queue *queue, int client, int use);
+
/* allocate a new queue -
- * return queue index value or negative value for error
+ * return pointer to new queue or ERR_PTR(-errno) for error
+ * The new queue's use_lock is set to 1. It is the caller's responsibility to
+ * call snd_use_lock_free(&q->use_lock).
*/
-int snd_seq_queue_alloc(int client, int locked, unsigned int info_flags)
+struct snd_seq_queue *snd_seq_queue_alloc(int client, int locked, unsigned int info_flags)
{
struct snd_seq_queue *q;
q = queue_new(client, locked);
if (q == NULL)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
q->info_flags = info_flags;
+ queue_use(q, client, 1);
+ snd_use_lock_use(&q->use_lock);
if (queue_list_add(q) < 0) {
+ snd_use_lock_free(&q->use_lock);
queue_delete(q);
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
}
- snd_seq_queue_use(q->queue, client, 1); /* use this queue */
- return q->queue;
+ return q;
}
/* delete a queue - queue must be owned by the client */
@@ -220,15 +195,13 @@ int snd_seq_queue_delete(int client, int queueid)
struct snd_seq_queue *queueptr(int queueid)
{
struct snd_seq_queue *q;
- unsigned long flags;
if (queueid < 0 || queueid >= SNDRV_SEQ_MAX_QUEUES)
return NULL;
- spin_lock_irqsave(&queue_list_lock, flags);
+ guard(spinlock_irqsave)(&queue_list_lock);
q = queue_list[queueid];
if (q)
snd_use_lock_use(&q->use_lock);
- spin_unlock_irqrestore(&queue_list_lock, flags);
return q;
}
@@ -236,13 +209,13 @@ struct snd_seq_queue *queueptr(int queueid)
struct snd_seq_queue *snd_seq_queue_find_name(char *name)
{
int i;
- struct snd_seq_queue *q;
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- if ((q = queueptr(i)) != NULL) {
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
+ q = queueptr(i);
+ if (q) {
if (strncmp(q->name, name, sizeof(q->name)) == 0)
- return q;
- queuefree(q);
+ return no_free_ptr(q);
}
}
return NULL;
@@ -251,61 +224,60 @@ struct snd_seq_queue *snd_seq_queue_find_name(char *name)
/* -------------------------------------------------------- */
+#define MAX_CELL_PROCESSES_IN_QUEUE 1000
+
void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
{
- unsigned long flags;
struct snd_seq_event_cell *cell;
+ snd_seq_tick_time_t cur_tick;
+ snd_seq_real_time_t cur_time;
+ int processed = 0;
if (q == NULL)
return;
/* make this function non-reentrant */
- spin_lock_irqsave(&q->check_lock, flags);
- if (q->check_blocked) {
- q->check_again = 1;
- spin_unlock_irqrestore(&q->check_lock, flags);
- return; /* other thread is already checking queues */
+ scoped_guard(spinlock_irqsave, &q->check_lock) {
+ if (q->check_blocked) {
+ q->check_again = 1;
+ return; /* other thread is already checking queues */
+ }
+ q->check_blocked = 1;
}
- q->check_blocked = 1;
- spin_unlock_irqrestore(&q->check_lock, flags);
__again:
/* Process tick queue... */
- while ((cell = snd_seq_prioq_cell_peek(q->tickq)) != NULL) {
- if (snd_seq_compare_tick_time(&q->timer->tick.cur_tick,
- &cell->event.time.tick)) {
- cell = snd_seq_prioq_cell_out(q->tickq);
- if (cell)
- snd_seq_dispatch_event(cell, atomic, hop);
- } else {
- /* event remains in the queue */
+ cur_tick = snd_seq_timer_get_cur_tick(q->timer);
+ for (;;) {
+ cell = snd_seq_prioq_cell_out(q->tickq, &cur_tick);
+ if (!cell)
break;
- }
+ snd_seq_dispatch_event(cell, atomic, hop);
+ if (++processed >= MAX_CELL_PROCESSES_IN_QUEUE)
+ goto out; /* the rest processed at the next batch */
}
-
/* Process time queue... */
- while ((cell = snd_seq_prioq_cell_peek(q->timeq)) != NULL) {
- if (snd_seq_compare_real_time(&q->timer->cur_time,
- &cell->event.time.time)) {
- cell = snd_seq_prioq_cell_out(q->timeq);
- if (cell)
- snd_seq_dispatch_event(cell, atomic, hop);
- } else {
- /* event remains in the queue */
+ cur_time = snd_seq_timer_get_cur_time(q->timer, false);
+ for (;;) {
+ cell = snd_seq_prioq_cell_out(q->timeq, &cur_time);
+ if (!cell)
break;
- }
+ snd_seq_dispatch_event(cell, atomic, hop);
+ if (++processed >= MAX_CELL_PROCESSES_IN_QUEUE)
+ goto out; /* the rest processed at the next batch */
}
+ out:
/* free lock */
- spin_lock_irqsave(&q->check_lock, flags);
- if (q->check_again) {
- q->check_again = 0;
- spin_unlock_irqrestore(&q->check_lock, flags);
- goto __again;
+ scoped_guard(spinlock_irqsave, &q->check_lock) {
+ if (q->check_again) {
+ q->check_again = 0;
+ if (processed < MAX_CELL_PROCESSES_IN_QUEUE)
+ goto __again;
+ }
+ q->check_blocked = 0;
}
- q->check_blocked = 0;
- spin_unlock_irqrestore(&q->check_lock, flags);
}
@@ -313,7 +285,7 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop)
int snd_seq_enqueue_event(struct snd_seq_event_cell *cell, int atomic, int hop)
{
int dest, err;
- struct snd_seq_queue *q;
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
if (snd_BUG_ON(!cell))
return -EINVAL;
@@ -348,16 +320,12 @@ int snd_seq_enqueue_event(struct snd_seq_event_cell *cell, int atomic, int hop)
break;
}
- if (err < 0) {
- queuefree(q); /* unlock */
+ if (err < 0)
return err;
- }
/* trigger dispatching */
snd_seq_check_queue(q, atomic, hop);
- queuefree(q); /* unlock */
-
return 0;
}
@@ -374,41 +342,31 @@ static inline int check_access(struct snd_seq_queue *q, int client)
*/
static int queue_access_lock(struct snd_seq_queue *q, int client)
{
- unsigned long flags;
int access_ok;
- spin_lock_irqsave(&q->owner_lock, flags);
+ guard(spinlock_irqsave)(&q->owner_lock);
access_ok = check_access(q, client);
if (access_ok)
q->klocked = 1;
- spin_unlock_irqrestore(&q->owner_lock, flags);
return access_ok;
}
/* unlock the queue */
static inline void queue_access_unlock(struct snd_seq_queue *q)
{
- unsigned long flags;
-
- spin_lock_irqsave(&q->owner_lock, flags);
+ guard(spinlock_irqsave)(&q->owner_lock);
q->klocked = 0;
- spin_unlock_irqrestore(&q->owner_lock, flags);
}
/* exported - only checking permission */
int snd_seq_queue_check_access(int queueid, int client)
{
- struct snd_seq_queue *q = queueptr(queueid);
- int access_ok;
- unsigned long flags;
+ struct snd_seq_queue *q __free(snd_seq_queue) = queueptr(queueid);
if (! q)
return 0;
- spin_lock_irqsave(&q->owner_lock, flags);
- access_ok = check_access(q, client);
- spin_unlock_irqrestore(&q->owner_lock, flags);
- queuefree(q);
- return access_ok;
+ guard(spinlock_irqsave)(&q->owner_lock);
+ return check_access(q, client);
}
/*----------------------------------------------------------------*/
@@ -418,20 +376,19 @@ int snd_seq_queue_check_access(int queueid, int client)
*/
int snd_seq_queue_set_owner(int queueid, int client, int locked)
{
- struct snd_seq_queue *q = queueptr(queueid);
+ struct snd_seq_queue *q __free(snd_seq_queue) = queueptr(queueid);
if (q == NULL)
return -EINVAL;
- if (! queue_access_lock(q, client)) {
- queuefree(q);
+ if (!queue_access_lock(q, client))
return -EPERM;
- }
- q->locked = locked ? 1 : 0;
- q->owner = client;
+ scoped_guard(spinlock_irqsave, &q->owner_lock) {
+ q->locked = locked ? 1 : 0;
+ q->owner = client;
+ }
queue_access_unlock(q);
- queuefree(q);
return 0;
}
@@ -446,18 +403,18 @@ int snd_seq_queue_set_owner(int queueid, int client, int locked)
int snd_seq_queue_timer_open(int queueid)
{
int result = 0;
- struct snd_seq_queue *queue;
+ struct snd_seq_queue *queue __free(snd_seq_queue) = NULL;
struct snd_seq_timer *tmr;
queue = queueptr(queueid);
if (queue == NULL)
return -EINVAL;
tmr = queue->timer;
- if ((result = snd_seq_timer_open(queue)) < 0) {
+ result = snd_seq_timer_open(queue);
+ if (result < 0) {
snd_seq_timer_defaults(tmr);
result = snd_seq_timer_open(queue);
}
- queuefree(queue);
return result;
}
@@ -466,16 +423,13 @@ int snd_seq_queue_timer_open(int queueid)
*/
int snd_seq_queue_timer_close(int queueid)
{
- struct snd_seq_queue *queue;
- struct snd_seq_timer *tmr;
+ struct snd_seq_queue *queue __free(snd_seq_queue) = NULL;
int result = 0;
queue = queueptr(queueid);
if (queue == NULL)
return -EINVAL;
- tmr = queue->timer;
snd_seq_timer_close(queue);
- queuefree(queue);
return result;
}
@@ -483,40 +437,26 @@ int snd_seq_queue_timer_close(int queueid)
int snd_seq_queue_timer_set_tempo(int queueid, int client,
struct snd_seq_queue_tempo *info)
{
- struct snd_seq_queue *q = queueptr(queueid);
+ struct snd_seq_queue *q __free(snd_seq_queue) = queueptr(queueid);
int result;
if (q == NULL)
return -EINVAL;
- if (! queue_access_lock(q, client)) {
- queuefree(q);
+ if (!queue_access_lock(q, client))
return -EPERM;
- }
- result = snd_seq_timer_set_tempo(q->timer, info->tempo);
- if (result >= 0)
- result = snd_seq_timer_set_ppq(q->timer, info->ppq);
+ result = snd_seq_timer_set_tempo_ppq(q->timer, info->tempo, info->ppq,
+ info->tempo_base);
if (result >= 0 && info->skew_base > 0)
result = snd_seq_timer_set_skew(q->timer, info->skew_value,
info->skew_base);
queue_access_unlock(q);
- queuefree(q);
return result;
}
-
-/* use or unuse this queue -
- * if it is the first client, starts the timer.
- * if it is not longer used by any clients, stop the timer.
- */
-int snd_seq_queue_use(int queueid, int client, int use)
+/* use or unuse this queue */
+static void queue_use(struct snd_seq_queue *queue, int client, int use)
{
- struct snd_seq_queue *queue;
-
- queue = queueptr(queueid);
- if (queue == NULL)
- return -EINVAL;
- mutex_lock(&queue->timer_mutex);
if (use) {
if (!test_and_set_bit(client, queue->clients_bitmap))
queue->clients++;
@@ -531,8 +471,21 @@ int snd_seq_queue_use(int queueid, int client, int use)
} else {
snd_seq_timer_close(queue);
}
- mutex_unlock(&queue->timer_mutex);
- queuefree(queue);
+}
+
+/* use or unuse this queue -
+ * if it is the first client, starts the timer.
+ * if it is not longer used by any clients, stop the timer.
+ */
+int snd_seq_queue_use(int queueid, int client, int use)
+{
+ struct snd_seq_queue *queue __free(snd_seq_queue) = NULL;
+
+ queue = queueptr(queueid);
+ if (queue == NULL)
+ return -EINVAL;
+ guard(mutex)(&queue->timer_mutex);
+ queue_use(queue, client, use);
return 0;
}
@@ -543,45 +496,17 @@ int snd_seq_queue_use(int queueid, int client, int use)
*/
int snd_seq_queue_is_used(int queueid, int client)
{
- struct snd_seq_queue *q;
- int result;
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
q = queueptr(queueid);
if (q == NULL)
return -EINVAL; /* invalid queue */
- result = test_bit(client, q->clients_bitmap) ? 1 : 0;
- queuefree(q);
- return result;
+ return test_bit(client, q->clients_bitmap) ? 1 : 0;
}
/*----------------------------------------------------------------*/
-/* notification that client has left the system -
- * stop the timer on all queues owned by this client
- */
-void snd_seq_queue_client_termination(int client)
-{
- unsigned long flags;
- int i;
- struct snd_seq_queue *q;
-
- for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- if ((q = queueptr(i)) == NULL)
- continue;
- spin_lock_irqsave(&q->owner_lock, flags);
- if (q->owner == client)
- q->klocked = 1;
- spin_unlock_irqrestore(&q->owner_lock, flags);
- if (q->owner == client) {
- if (q->timer->running)
- snd_seq_timer_stop(q->timer);
- snd_seq_timer_reset(q->timer);
- }
- queuefree(q);
- }
-}
-
/* final stage notification -
* remove cells for no longer exist client (for non-owned queue)
* or delete this queue (for owned queue)
@@ -589,11 +514,11 @@ void snd_seq_queue_client_termination(int client)
void snd_seq_queue_client_leave(int client)
{
int i;
- struct snd_seq_queue *q;
/* delete own queues from queue list */
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- if ((q = queue_list_remove(i, client)) != NULL)
+ struct snd_seq_queue *q = queue_list_remove(i, client);
+ if (q)
queue_delete(q);
}
@@ -601,14 +526,14 @@ void snd_seq_queue_client_leave(int client)
* they are not owned by this client
*/
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- if ((q = queueptr(i)) == NULL)
+ struct snd_seq_queue *q __free(snd_seq_queue) = queueptr(i);
+ if (!q)
continue;
if (test_bit(client, q->clients_bitmap)) {
snd_seq_prioq_leave(q->tickq, client, 0);
snd_seq_prioq_leave(q->timeq, client, 0);
snd_seq_queue_use(q->queue, client, 0);
}
- queuefree(q);
}
}
@@ -616,29 +541,14 @@ void snd_seq_queue_client_leave(int client)
/*----------------------------------------------------------------*/
-/* remove cells from all queues */
-void snd_seq_queue_client_leave_cells(int client)
-{
- int i;
- struct snd_seq_queue *q;
-
- for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- if ((q = queueptr(i)) == NULL)
- continue;
- snd_seq_prioq_leave(q->tickq, client, 0);
- snd_seq_prioq_leave(q->timeq, client, 0);
- queuefree(q);
- }
-}
-
/* remove cells based on flush criteria */
void snd_seq_queue_remove_cells(int client, struct snd_seq_remove_events *info)
{
int i;
- struct snd_seq_queue *q;
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- if ((q = queueptr(i)) == NULL)
+ struct snd_seq_queue *q __free(snd_seq_queue) = queueptr(i);
+ if (!q)
continue;
if (test_bit(client, q->clients_bitmap) &&
(! (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) ||
@@ -646,7 +556,6 @@ void snd_seq_queue_remove_cells(int client, struct snd_seq_remove_events *info)
snd_seq_prioq_remove_events(q->tickq, client, info);
snd_seq_prioq_remove_events(q->timeq, client, info);
}
- queuefree(q);
}
}
@@ -733,7 +642,7 @@ static void snd_seq_queue_process_event(struct snd_seq_queue *q,
*/
int snd_seq_control_queue(struct snd_seq_event *ev, int atomic, int hop)
{
- struct snd_seq_queue *q;
+ struct snd_seq_queue *q __free(snd_seq_queue) = NULL;
if (snd_BUG_ON(!ev))
return -EINVAL;
@@ -742,54 +651,58 @@ int snd_seq_control_queue(struct snd_seq_event *ev, int atomic, int hop)
if (q == NULL)
return -EINVAL;
- if (! queue_access_lock(q, ev->source.client)) {
- queuefree(q);
+ if (!queue_access_lock(q, ev->source.client))
return -EPERM;
- }
snd_seq_queue_process_event(q, ev, atomic, hop);
queue_access_unlock(q);
- queuefree(q);
return 0;
}
/*----------------------------------------------------------------*/
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/* exported to seq_info.c */
void snd_seq_info_queues_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
int i, bpm;
- struct snd_seq_queue *q;
struct snd_seq_timer *tmr;
+ bool locked;
+ int owner;
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
- if ((q = queueptr(i)) == NULL)
+ struct snd_seq_queue *q __free(snd_seq_queue) = queueptr(i);
+ if (!q)
continue;
tmr = q->timer;
if (tmr->tempo)
- bpm = 60000000 / tmr->tempo;
+ bpm = (60000 * tmr->tempo_base) / tmr->tempo;
else
bpm = 0;
+ scoped_guard(spinlock_irq, &q->owner_lock) {
+ locked = q->locked;
+ owner = q->owner;
+ }
+
snd_iprintf(buffer, "queue %d: [%s]\n", q->queue, q->name);
- snd_iprintf(buffer, "owned by client : %d\n", q->owner);
- snd_iprintf(buffer, "lock status : %s\n", q->locked ? "Locked" : "Free");
+ snd_iprintf(buffer, "owned by client : %d\n", owner);
+ snd_iprintf(buffer, "lock status : %s\n", locked ? "Locked" : "Free");
snd_iprintf(buffer, "queued time events : %d\n", snd_seq_prioq_avail(q->timeq));
snd_iprintf(buffer, "queued tick events : %d\n", snd_seq_prioq_avail(q->tickq));
snd_iprintf(buffer, "timer state : %s\n", tmr->running ? "Running" : "Stopped");
snd_iprintf(buffer, "timer PPQ : %d\n", tmr->ppq);
snd_iprintf(buffer, "current tempo : %d\n", tmr->tempo);
+ snd_iprintf(buffer, "tempo base : %d ns\n", tmr->tempo_base);
snd_iprintf(buffer, "current BPM : %d\n", bpm);
snd_iprintf(buffer, "current time : %d.%09d s\n", tmr->cur_time.tv_sec, tmr->cur_time.tv_nsec);
snd_iprintf(buffer, "current tick : %d\n", tmr->tick.cur_tick);
snd_iprintf(buffer, "\n");
- queuefree(q);
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/seq_queue.h b/sound/core/seq/seq_queue.h
index 30c8111477f6..afcd3c5484a6 100644
--- a/sound/core/seq/seq_queue.h
+++ b/sound/core/seq/seq_queue.h
@@ -1,21 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA sequencer Queue handling
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __SND_SEQ_QUEUE_H
#define __SND_SEQ_QUEUE_H
@@ -40,10 +26,10 @@ struct snd_seq_queue {
struct snd_seq_timer *timer; /* time keeper for this queue */
int owner; /* client that 'owns' the timer */
- unsigned int locked:1, /* timer is only accesibble by owner if set */
- klocked:1, /* kernel lock (after START) */
- check_again:1,
- check_blocked:1;
+ bool locked; /* timer is only accesibble by owner if set */
+ bool klocked; /* kernel lock (after START) */
+ bool check_again; /* concurrent access happened during check */
+ bool check_blocked; /* queue being checked */
unsigned int flags; /* status flags */
unsigned int info_flags; /* info for sync */
@@ -63,22 +49,16 @@ struct snd_seq_queue {
/* get the number of current queues */
int snd_seq_queue_get_cur_queues(void);
-/* init queues structure */
-int snd_seq_queues_init(void);
-
/* delete queues */
void snd_seq_queues_delete(void);
/* create new queue (constructor) */
-int snd_seq_queue_alloc(int client, int locked, unsigned int flags);
+struct snd_seq_queue *snd_seq_queue_alloc(int client, int locked, unsigned int flags);
/* delete queue (destructor) */
int snd_seq_queue_delete(int client, int queueid);
-/* notification that client has left the system */
-void snd_seq_queue_client_termination(int client);
-
/* final stage */
void snd_seq_queue_client_leave(int client);
@@ -86,7 +66,6 @@ void snd_seq_queue_client_leave(int client);
int snd_seq_enqueue_event(struct snd_seq_event_cell *cell, int atomic, int hop);
/* Remove events */
-void snd_seq_queue_client_leave_cells(int client);
void snd_seq_queue_remove_cells(int client, struct snd_seq_remove_events *info);
/* return pointer to queue structure for specified id */
@@ -94,6 +73,8 @@ struct snd_seq_queue *queueptr(int queueid);
/* unlock */
#define queuefree(q) snd_use_lock_free(&(q)->use_lock)
+DEFINE_FREE(snd_seq_queue, struct snd_seq_queue *, if (!IS_ERR_OR_NULL(_T)) queuefree(_T))
+
/* return the (first) queue matching with the specified name */
struct snd_seq_queue *snd_seq_queue_find_name(char *name);
@@ -104,7 +85,6 @@ void snd_seq_check_queue(struct snd_seq_queue *q, int atomic, int hop);
int snd_seq_queue_check_access(int queueid, int client);
int snd_seq_queue_timer_set_tempo(int queueid, int client, struct snd_seq_queue_tempo *info);
int snd_seq_queue_set_owner(int queueid, int client, int locked);
-int snd_seq_queue_set_locked(int queueid, int client, int locked);
int snd_seq_queue_timer_open(int queueid);
int snd_seq_queue_timer_close(int queueid);
int snd_seq_queue_use(int queueid, int client, int use);
@@ -112,28 +92,4 @@ int snd_seq_queue_is_used(int queueid, int client);
int snd_seq_control_queue(struct snd_seq_event *ev, int atomic, int hop);
-/*
- * 64bit division - for sync stuff..
- */
-#if defined(i386) || defined(i486)
-
-#define udiv_qrnnd(q, r, n1, n0, d) \
- __asm__ ("divl %4" \
- : "=a" ((u32)(q)), \
- "=d" ((u32)(r)) \
- : "0" ((u32)(n0)), \
- "1" ((u32)(n1)), \
- "rm" ((u32)(d)))
-
-#define u64_div(x,y,q) do {u32 __tmp; udiv_qrnnd(q, __tmp, (x)>>32, x, y);} while (0)
-#define u64_mod(x,y,r) do {u32 __tmp; udiv_qrnnd(__tmp, q, (x)>>32, x, y);} while (0)
-#define u64_divmod(x,y,q,r) udiv_qrnnd(q, r, (x)>>32, x, y)
-
-#else
-#define u64_div(x,y,q) ((q) = (u32)((u64)(x) / (u64)(y)))
-#define u64_mod(x,y,r) ((r) = (u32)((u64)(x) % (u64)(y)))
-#define u64_divmod(x,y,q,r) (u64_div(x,y,q), u64_mod(x,y,r))
-#endif
-
-
#endif
diff --git a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c
index c38b90cf3cb0..5b5603e5970b 100644
--- a/sound/core/seq/seq_system.c
+++ b/sound/core/seq/seq_system.c
@@ -1,25 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer System services Client
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <sound/core.h>
#include "seq_system.h"
@@ -63,12 +49,14 @@ static int sysclient = -1;
/* port id numbers for this client */
static int announce_port = -1;
+/* number of subscriptions to announce port */
+static int announce_subscribed;
/* fill standard header data, source port & channel are filled in */
static int setheader(struct snd_seq_event * ev, int client, int port)
{
- if (announce_port < 0)
+ if (announce_port < 0 || !announce_subscribed)
return -ENODEV;
memset(ev, 0, sizeof(struct snd_seq_event));
@@ -90,25 +78,27 @@ static int setheader(struct snd_seq_event * ev, int client, int port)
/* entry points for broadcasting system events */
-void snd_seq_system_broadcast(int client, int port, int type)
+void snd_seq_system_broadcast(int client, int port, int type, bool atomic)
{
struct snd_seq_event ev;
if (setheader(&ev, client, port) < 0)
return;
ev.type = type;
- snd_seq_kernel_client_dispatch(sysclient, &ev, 0, 0);
+ snd_seq_kernel_client_dispatch(sysclient, &ev, atomic, 0);
}
+EXPORT_SYMBOL_GPL(snd_seq_system_broadcast);
/* entry points for broadcasting system events */
-int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev)
+int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev,
+ bool atomic)
{
ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
ev->source.client = sysclient;
ev->source.port = announce_port;
ev->dest.client = client;
ev->dest.port = port;
- return snd_seq_kernel_client_dispatch(sysclient, ev, 0, 0);
+ return snd_seq_kernel_client_dispatch(sysclient, ev, atomic, 0);
}
/* call-back handler for timer events */
@@ -117,11 +107,28 @@ static int event_input_timer(struct snd_seq_event * ev, int direct, void *privat
return snd_seq_control_queue(ev, atomic, hop);
}
+static int sys_announce_subscribe(void *private_data,
+ struct snd_seq_port_subscribe *info)
+{
+ announce_subscribed++;
+ return 0;
+}
+
+static int sys_announce_unsubscribe(void *private_data,
+ struct snd_seq_port_subscribe *info)
+{
+ if (snd_BUG_ON(!announce_subscribed))
+ return 0;
+ announce_subscribed--;
+ return 0;
+}
+
/* register our internal client */
int __init snd_seq_system_client_init(void)
{
struct snd_seq_port_callback pcallbacks;
struct snd_seq_port_info *port;
+ int err;
port = kzalloc(sizeof(*port), GFP_KERNEL);
if (!port)
@@ -133,9 +140,13 @@ int __init snd_seq_system_client_init(void)
/* register client */
sysclient = snd_seq_create_kernel_client(NULL, 0, "System");
+ if (sysclient < 0) {
+ kfree(port);
+ return sysclient;
+ }
/* register timer */
- strcpy(port->name, "Timer");
+ strscpy(port->name, "Timer");
port->capability = SNDRV_SEQ_PORT_CAP_WRITE; /* accept queue control */
port->capability |= SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast */
port->kernel = &pcallbacks;
@@ -143,26 +154,40 @@ int __init snd_seq_system_client_init(void)
port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
port->addr.client = sysclient;
port->addr.port = SNDRV_SEQ_PORT_SYSTEM_TIMER;
- snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port);
+ err = snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT,
+ port);
+ if (err < 0)
+ goto error_port;
/* register announcement port */
- strcpy(port->name, "Announce");
+ strscpy(port->name, "Announce");
port->capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */
- port->kernel = NULL;
+ pcallbacks.event_input = NULL;
+ pcallbacks.subscribe = sys_announce_subscribe;
+ pcallbacks.unsubscribe = sys_announce_unsubscribe;
+ port->kernel = &pcallbacks;
port->type = 0;
port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
port->addr.client = sysclient;
port->addr.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
- snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port);
+ err = snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT,
+ port);
+ if (err < 0)
+ goto error_port;
announce_port = port->addr.port;
kfree(port);
return 0;
+
+ error_port:
+ snd_seq_system_client_done();
+ kfree(port);
+ return err;
}
/* unregister our internal client */
-void __exit snd_seq_system_client_done(void)
+void snd_seq_system_client_done(void)
{
int oldsysclient = sysclient;
diff --git a/sound/core/seq/seq_system.h b/sound/core/seq/seq_system.h
index cf2cfa23430e..62e513f74871 100644
--- a/sound/core/seq/seq_system.h
+++ b/sound/core/seq/seq_system.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA sequencer System Client
* Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __SND_SEQ_SYSTEM_H
#define __SND_SEQ_SYSTEM_H
@@ -25,16 +10,31 @@
/* entry points for broadcasting system events */
-void snd_seq_system_broadcast(int client, int port, int type);
-
-#define snd_seq_system_client_ev_client_start(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_START)
-#define snd_seq_system_client_ev_client_exit(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_EXIT)
-#define snd_seq_system_client_ev_client_change(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_CHANGE)
-#define snd_seq_system_client_ev_port_start(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_START)
-#define snd_seq_system_client_ev_port_exit(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_EXIT)
-#define snd_seq_system_client_ev_port_change(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_CHANGE)
-
-int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev);
+void snd_seq_system_broadcast(int client, int port, int type, bool atomic);
+
+/* normal system notification event broadcast */
+#define notify_event(client, port, type) \
+ snd_seq_system_broadcast(client, port, type, false)
+
+/* notify UMP EP/FB change event */
+static inline void snd_seq_system_ump_notify(int client, int block, int type,
+ bool atomic)
+{
+ /* reuse the existing snd_seq_system_broadcast():
+ * struct snd_seq_ev_ump_notify is compatible with struct snd_seq_addr
+ */
+ snd_seq_system_broadcast(client, block, type, atomic);
+}
+
+#define snd_seq_system_client_ev_client_start(client) notify_event(client, 0, SNDRV_SEQ_EVENT_CLIENT_START)
+#define snd_seq_system_client_ev_client_exit(client) notify_event(client, 0, SNDRV_SEQ_EVENT_CLIENT_EXIT)
+#define snd_seq_system_client_ev_client_change(client) notify_event(client, 0, SNDRV_SEQ_EVENT_CLIENT_CHANGE)
+#define snd_seq_system_client_ev_port_start(client, port) notify_event(client, port, SNDRV_SEQ_EVENT_PORT_START)
+#define snd_seq_system_client_ev_port_exit(client, port) notify_event(client, port, SNDRV_SEQ_EVENT_PORT_EXIT)
+#define snd_seq_system_client_ev_port_change(client, port) notify_event(client, port, SNDRV_SEQ_EVENT_PORT_CHANGE)
+
+int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev,
+ bool atomic);
/* register our internal client */
int snd_seq_system_client_init(void);
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index 160b1bd0cd62..29b018a212fc 100644
--- a/sound/core/seq/seq_timer.c
+++ b/sound/core/seq/seq_timer.c
@@ -1,23 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA sequencer Timer
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
* Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <sound/core.h>
@@ -35,14 +20,17 @@
static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer *tmr)
{
- if (tmr->tempo < 1000000)
- tmr->tick.resolution = (tmr->tempo * 1000) / tmr->ppq;
+ unsigned int threshold =
+ tmr->tempo_base == 1000 ? 1000000 : 10000;
+
+ if (tmr->tempo < threshold)
+ tmr->tick.resolution = (tmr->tempo * tmr->tempo_base) / tmr->ppq;
else {
/* might overflow.. */
unsigned int s;
s = tmr->tempo % tmr->ppq;
- s = (s * 1000) / tmr->ppq;
- tmr->tick.resolution = (tmr->tempo / tmr->ppq) * 1000;
+ s = (s * tmr->tempo_base) / tmr->ppq;
+ tmr->tick.resolution = (tmr->tempo / tmr->ppq) * tmr->tempo_base;
tmr->tick.resolution += s;
}
if (tmr->tick.resolution <= 0)
@@ -56,10 +44,8 @@ struct snd_seq_timer *snd_seq_timer_new(void)
struct snd_seq_timer *tmr;
tmr = kzalloc(sizeof(*tmr), GFP_KERNEL);
- if (tmr == NULL) {
- snd_printd("malloc failed for snd_seq_timer_new() \n");
+ if (!tmr)
return NULL;
- }
spin_lock_init(&tmr->lock);
/* reset setup to defaults */
@@ -78,7 +64,7 @@ void snd_seq_timer_delete(struct snd_seq_timer **tmr)
*tmr = NULL;
if (t == NULL) {
- snd_printd("oops: snd_seq_timer_delete() called with NULL timer\n");
+ pr_debug("ALSA: seq: snd_seq_timer_delete() called with NULL timer\n");
return;
}
t->running = 0;
@@ -92,9 +78,11 @@ void snd_seq_timer_delete(struct snd_seq_timer **tmr)
void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
{
+ guard(spinlock_irqsave)(&tmr->lock);
/* setup defaults */
tmr->ppq = 96; /* 96 PPQ */
tmr->tempo = 500000; /* 120 BPM */
+ tmr->tempo_base = 1000; /* 1us */
snd_seq_timer_set_tick_resolution(tmr);
tmr->running = 0;
@@ -109,20 +97,20 @@ void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
tmr->skew = tmr->skew_base = SKEW_BASE;
}
-void snd_seq_timer_reset(struct snd_seq_timer * tmr)
+static void seq_timer_reset(struct snd_seq_timer *tmr)
{
- unsigned long flags;
-
- spin_lock_irqsave(&tmr->lock, flags);
-
/* reset time & songposition */
tmr->cur_time.tv_sec = 0;
tmr->cur_time.tv_nsec = 0;
tmr->tick.cur_tick = 0;
tmr->tick.fraction = 0;
+}
- spin_unlock_irqrestore(&tmr->lock, flags);
+void snd_seq_timer_reset(struct snd_seq_timer *tmr)
+{
+ guard(spinlock_irqsave)(&tmr->lock);
+ seq_timer_reset(tmr);
}
@@ -131,7 +119,6 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
unsigned long resolution,
unsigned long ticks)
{
- unsigned long flags;
struct snd_seq_queue *q = timeri->callback_data;
struct snd_seq_timer *tmr;
@@ -140,28 +127,27 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
tmr = q->timer;
if (tmr == NULL)
return;
- if (!tmr->running)
- return;
-
- resolution *= ticks;
- if (tmr->skew != tmr->skew_base) {
- /* FIXME: assuming skew_base = 0x10000 */
- resolution = (resolution >> 16) * tmr->skew +
- (((resolution & 0xffff) * tmr->skew) >> 16);
- }
- spin_lock_irqsave(&tmr->lock, flags);
+ scoped_guard(spinlock_irqsave, &tmr->lock) {
+ if (!tmr->running)
+ return;
- /* update timer */
- snd_seq_inc_time_nsec(&tmr->cur_time, resolution);
+ resolution *= ticks;
+ if (tmr->skew != tmr->skew_base) {
+ /* FIXME: assuming skew_base = 0x10000 */
+ resolution = (resolution >> 16) * tmr->skew +
+ (((resolution & 0xffff) * tmr->skew) >> 16);
+ }
- /* calculate current tick */
- snd_seq_timer_update_tick(&tmr->tick, resolution);
+ /* update timer */
+ snd_seq_inc_time_nsec(&tmr->cur_time, resolution);
- /* register actual time of this timer update */
- do_gettimeofday(&tmr->last_update);
+ /* calculate current tick */
+ snd_seq_timer_update_tick(&tmr->tick, resolution);
- spin_unlock_irqrestore(&tmr->lock, flags);
+ /* register actual time of this timer update */
+ ktime_get_ts64(&tmr->last_update);
+ }
/* check queues and dispatch events */
snd_seq_check_queue(q, 1, 0);
@@ -170,42 +156,44 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
/* set current tempo */
int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo)
{
- unsigned long flags;
-
if (snd_BUG_ON(!tmr))
return -EINVAL;
if (tempo <= 0)
return -EINVAL;
- spin_lock_irqsave(&tmr->lock, flags);
+ guard(spinlock_irqsave)(&tmr->lock);
if ((unsigned int)tempo != tmr->tempo) {
tmr->tempo = tempo;
snd_seq_timer_set_tick_resolution(tmr);
}
- spin_unlock_irqrestore(&tmr->lock, flags);
return 0;
}
-/* set current ppq */
-int snd_seq_timer_set_ppq(struct snd_seq_timer * tmr, int ppq)
+/* set current tempo, ppq and base in a shot */
+int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq,
+ unsigned int tempo_base)
{
- unsigned long flags;
+ int changed;
if (snd_BUG_ON(!tmr))
return -EINVAL;
- if (ppq <= 0)
+ if (tempo <= 0 || ppq <= 0)
+ return -EINVAL;
+ /* allow only 10ns or 1us tempo base for now */
+ if (tempo_base && tempo_base != 10 && tempo_base != 1000)
return -EINVAL;
- spin_lock_irqsave(&tmr->lock, flags);
+ guard(spinlock_irqsave)(&tmr->lock);
if (tmr->running && (ppq != tmr->ppq)) {
/* refuse to change ppq on running timers */
/* because it will upset the song position (ticks) */
- spin_unlock_irqrestore(&tmr->lock, flags);
- snd_printd("seq: cannot change ppq of a running timer\n");
+ pr_debug("ALSA: seq: cannot change ppq of a running timer\n");
return -EBUSY;
}
-
+ changed = (tempo != tmr->tempo) || (ppq != tmr->ppq);
+ tmr->tempo = tempo;
tmr->ppq = ppq;
- snd_seq_timer_set_tick_resolution(tmr);
- spin_unlock_irqrestore(&tmr->lock, flags);
+ tmr->tempo_base = tempo_base ? tempo_base : 1000;
+ if (changed)
+ snd_seq_timer_set_tick_resolution(tmr);
return 0;
}
@@ -213,15 +201,12 @@ int snd_seq_timer_set_ppq(struct snd_seq_timer * tmr, int ppq)
int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr,
snd_seq_tick_time_t position)
{
- unsigned long flags;
-
if (snd_BUG_ON(!tmr))
return -EINVAL;
- spin_lock_irqsave(&tmr->lock, flags);
+ guard(spinlock_irqsave)(&tmr->lock);
tmr->tick.cur_tick = position;
tmr->tick.fraction = 0;
- spin_unlock_irqrestore(&tmr->lock, flags);
return 0;
}
@@ -229,15 +214,12 @@ int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr,
int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr,
snd_seq_real_time_t position)
{
- unsigned long flags;
-
if (snd_BUG_ON(!tmr))
return -EINVAL;
snd_seq_sanity_real_time(&position);
- spin_lock_irqsave(&tmr->lock, flags);
+ guard(spinlock_irqsave)(&tmr->lock);
tmr->cur_time = position;
- spin_unlock_irqrestore(&tmr->lock, flags);
return 0;
}
@@ -245,19 +227,16 @@ int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr,
int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew,
unsigned int base)
{
- unsigned long flags;
-
if (snd_BUG_ON(!tmr))
return -EINVAL;
/* FIXME */
if (base != SKEW_BASE) {
- snd_printd("invalid skew base 0x%x\n", base);
+ pr_debug("ALSA: seq: invalid skew base 0x%x\n", base);
return -EINVAL;
}
- spin_lock_irqsave(&tmr->lock, flags);
+ guard(spinlock_irqsave)(&tmr->lock);
tmr->skew = skew;
- spin_unlock_irqrestore(&tmr->lock, flags);
return 0;
}
@@ -278,7 +257,13 @@ int snd_seq_timer_open(struct snd_seq_queue *q)
return -EINVAL;
if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
tmr->alsa_id.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
- err = snd_timer_open(&t, str, &tmr->alsa_id, q->queue);
+ t = snd_timer_instance_new(str);
+ if (!t)
+ return -ENOMEM;
+ t->callback = snd_seq_timer_interrupt;
+ t->callback_data = q;
+ t->flags |= SNDRV_TIMER_IFLG_AUTO;
+ err = snd_timer_open(t, &tmr->alsa_id, q->queue);
if (err < 0 && tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_SLAVE) {
if (tmr->alsa_id.dev_class != SNDRV_TIMER_CLASS_GLOBAL ||
tmr->alsa_id.device != SNDRV_TIMER_GLOBAL_SYSTEM) {
@@ -288,36 +273,48 @@ int snd_seq_timer_open(struct snd_seq_queue *q)
tid.dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER;
tid.card = -1;
tid.device = SNDRV_TIMER_GLOBAL_SYSTEM;
- err = snd_timer_open(&t, str, &tid, q->queue);
- }
- if (err < 0) {
- snd_printk(KERN_ERR "seq fatal error: cannot create timer (%i)\n", err);
- return err;
+ err = snd_timer_open(t, &tid, q->queue);
}
}
- t->callback = snd_seq_timer_interrupt;
- t->callback_data = q;
- t->flags |= SNDRV_TIMER_IFLG_AUTO;
- tmr->timeri = t;
+ if (err < 0) {
+ pr_err("ALSA: seq fatal error: cannot create timer (%i)\n", err);
+ snd_timer_instance_free(t);
+ return err;
+ }
+ scoped_guard(spinlock_irq, &tmr->lock) {
+ if (tmr->timeri)
+ err = -EBUSY;
+ else
+ tmr->timeri = t;
+ }
+ if (err < 0) {
+ snd_timer_close(t);
+ snd_timer_instance_free(t);
+ return err;
+ }
return 0;
}
int snd_seq_timer_close(struct snd_seq_queue *q)
{
struct snd_seq_timer *tmr;
+ struct snd_timer_instance *t;
tmr = q->timer;
if (snd_BUG_ON(!tmr))
return -EINVAL;
- if (tmr->timeri) {
- snd_timer_stop(tmr->timeri);
- snd_timer_close(tmr->timeri);
+ scoped_guard(spinlock_irq, &tmr->lock) {
+ t = tmr->timeri;
tmr->timeri = NULL;
}
+ if (t) {
+ snd_timer_close(t);
+ snd_timer_instance_free(t);
+ }
return 0;
}
-int snd_seq_timer_stop(struct snd_seq_timer * tmr)
+static int seq_timer_stop(struct snd_seq_timer *tmr)
{
if (! tmr->timeri)
return -EINVAL;
@@ -328,13 +325,19 @@ int snd_seq_timer_stop(struct snd_seq_timer * tmr)
return 0;
}
+int snd_seq_timer_stop(struct snd_seq_timer *tmr)
+{
+ guard(spinlock_irqsave)(&tmr->lock);
+ return seq_timer_stop(tmr);
+}
+
static int initialize_timer(struct snd_seq_timer *tmr)
{
struct snd_timer *t;
unsigned long freq;
t = tmr->timeri->timer;
- if (snd_BUG_ON(!t))
+ if (!t)
return -EINVAL;
freq = tmr->preferred_resolution;
@@ -347,9 +350,7 @@ static int initialize_timer(struct snd_seq_timer *tmr)
tmr->ticks = 1;
if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
- unsigned long r = t->hw.resolution;
- if (! r && t->hw.c_resolution)
- r = t->hw.c_resolution(t);
+ unsigned long r = snd_timer_resolution(tmr->timeri);
if (r) {
tmr->ticks = (unsigned int)(1000000000uL / (r * freq));
if (! tmr->ticks)
@@ -360,59 +361,67 @@ static int initialize_timer(struct snd_seq_timer *tmr)
return 0;
}
-int snd_seq_timer_start(struct snd_seq_timer * tmr)
+static int seq_timer_start(struct snd_seq_timer *tmr)
{
if (! tmr->timeri)
return -EINVAL;
if (tmr->running)
- snd_seq_timer_stop(tmr);
- snd_seq_timer_reset(tmr);
+ seq_timer_stop(tmr);
+ seq_timer_reset(tmr);
if (initialize_timer(tmr) < 0)
return -EINVAL;
snd_timer_start(tmr->timeri, tmr->ticks);
tmr->running = 1;
- do_gettimeofday(&tmr->last_update);
+ ktime_get_ts64(&tmr->last_update);
return 0;
}
-int snd_seq_timer_continue(struct snd_seq_timer * tmr)
+int snd_seq_timer_start(struct snd_seq_timer *tmr)
+{
+ guard(spinlock_irqsave)(&tmr->lock);
+ return seq_timer_start(tmr);
+}
+
+static int seq_timer_continue(struct snd_seq_timer *tmr)
{
if (! tmr->timeri)
return -EINVAL;
if (tmr->running)
return -EBUSY;
if (! tmr->initialized) {
- snd_seq_timer_reset(tmr);
+ seq_timer_reset(tmr);
if (initialize_timer(tmr) < 0)
return -EINVAL;
}
snd_timer_start(tmr->timeri, tmr->ticks);
tmr->running = 1;
- do_gettimeofday(&tmr->last_update);
+ ktime_get_ts64(&tmr->last_update);
return 0;
}
+int snd_seq_timer_continue(struct snd_seq_timer *tmr)
+{
+ guard(spinlock_irqsave)(&tmr->lock);
+ return seq_timer_continue(tmr);
+}
+
/* return current 'real' time. use timeofday() to get better granularity. */
-snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
+snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr,
+ bool adjust_ktime)
{
snd_seq_real_time_t cur_time;
+ guard(spinlock_irqsave)(&tmr->lock);
cur_time = tmr->cur_time;
- if (tmr->running) {
- struct timeval tm;
- int usec;
- do_gettimeofday(&tm);
- usec = (int)(tm.tv_usec - tmr->last_update.tv_usec);
- if (usec < 0) {
- cur_time.tv_nsec += (1000000 + usec) * 1000;
- cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec - 1;
- } else {
- cur_time.tv_nsec += usec * 1000;
- cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec;
- }
+ if (adjust_ktime && tmr->running) {
+ struct timespec64 tm;
+
+ ktime_get_ts64(&tm);
+ tm = timespec64_sub(tm, tmr->last_update);
+ cur_time.tv_nsec += tm.tv_nsec;
+ cur_time.tv_sec += tm.tv_sec;
snd_seq_sanity_real_time(&cur_time);
}
-
return cur_time;
}
@@ -420,36 +429,39 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
high PPQ values) */
snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr)
{
+ guard(spinlock_irqsave)(&tmr->lock);
return tmr->tick.cur_tick;
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/* exported to seq_info.c */
void snd_seq_info_timer_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
int idx;
- struct snd_seq_queue *q;
struct snd_seq_timer *tmr;
struct snd_timer_instance *ti;
unsigned long resolution;
for (idx = 0; idx < SNDRV_SEQ_MAX_QUEUES; idx++) {
- q = queueptr(idx);
+ struct snd_seq_queue *q __free(snd_seq_queue) = queueptr(idx);
+
if (q == NULL)
continue;
- if ((tmr = q->timer) == NULL ||
- (ti = tmr->timeri) == NULL) {
- queuefree(q);
- continue;
+ scoped_guard(mutex, &q->timer_mutex) {
+ tmr = q->timer;
+ if (!tmr)
+ break;
+ ti = tmr->timeri;
+ if (!ti)
+ break;
+ snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name);
+ resolution = snd_timer_resolution(ti) * tmr->ticks;
+ snd_iprintf(buffer, " Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000);
+ snd_iprintf(buffer, " Skew : %u / %u\n", tmr->skew, tmr->skew_base);
}
- snd_iprintf(buffer, "Timer for queue %i : %s\n", q->queue, ti->timer->name);
- resolution = snd_timer_resolution(ti) * tmr->ticks;
- snd_iprintf(buffer, " Period time : %lu.%09lu\n", resolution / 1000000000, resolution % 1000000000);
- snd_iprintf(buffer, " Skew : %u / %u\n", tmr->skew, tmr->skew_base);
- queuefree(q);
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/seq/seq_timer.h b/sound/core/seq/seq_timer.h
index 88dfb71805ae..c8803216a3a4 100644
--- a/sound/core/seq/seq_timer.h
+++ b/sound/core/seq/seq_timer.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA sequencer Timer
* Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __SND_SEQ_TIMER_H
#define __SND_SEQ_TIMER_H
@@ -51,8 +36,9 @@ struct snd_seq_timer {
unsigned int skew;
unsigned int skew_base;
+ unsigned int tempo_base;
- struct timeval last_update; /* time of last clock update, used for interpolation */
+ struct timespec64 last_update; /* time of last clock update, used for interpolation */
spinlock_t lock;
};
@@ -123,19 +109,19 @@ static inline void snd_seq_inc_time_nsec(snd_seq_real_time_t *tm, unsigned long
struct snd_seq_queue;
int snd_seq_timer_open(struct snd_seq_queue *q);
int snd_seq_timer_close(struct snd_seq_queue *q);
-int snd_seq_timer_midi_open(struct snd_seq_queue *q);
-int snd_seq_timer_midi_close(struct snd_seq_queue *q);
void snd_seq_timer_defaults(struct snd_seq_timer *tmr);
void snd_seq_timer_reset(struct snd_seq_timer *tmr);
int snd_seq_timer_stop(struct snd_seq_timer *tmr);
int snd_seq_timer_start(struct snd_seq_timer *tmr);
int snd_seq_timer_continue(struct snd_seq_timer *tmr);
int snd_seq_timer_set_tempo(struct snd_seq_timer *tmr, int tempo);
-int snd_seq_timer_set_ppq(struct snd_seq_timer *tmr, int ppq);
+int snd_seq_timer_set_tempo_ppq(struct snd_seq_timer *tmr, int tempo, int ppq,
+ unsigned int tempo_base);
int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr, snd_seq_tick_time_t position);
int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr, snd_seq_real_time_t position);
int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, unsigned int base);
-snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr);
+snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr,
+ bool adjust_ktime);
snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr);
extern int seq_default_timer_class;
diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c
new file mode 100644
index 000000000000..27247babb16d
--- /dev/null
+++ b/sound/core/seq/seq_ump_client.c
@@ -0,0 +1,539 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* ALSA sequencer binding for UMP device */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <asm/byteorder.h>
+#include <sound/core.h>
+#include <sound/ump.h>
+#include <sound/seq_kernel.h>
+#include <sound/seq_device.h>
+#include "seq_clientmgr.h"
+#include "seq_system.h"
+
+struct seq_ump_client;
+struct seq_ump_group;
+
+enum {
+ STR_IN = SNDRV_RAWMIDI_STREAM_INPUT,
+ STR_OUT = SNDRV_RAWMIDI_STREAM_OUTPUT
+};
+
+/* context for UMP input parsing, per EP */
+struct seq_ump_input_buffer {
+ unsigned char len; /* total length in words */
+ unsigned char pending; /* pending words */
+ unsigned char type; /* parsed UMP packet type */
+ unsigned char group; /* parsed UMP packet group */
+ u32 buf[4]; /* incoming UMP packet */
+};
+
+/* sequencer client, per UMP EP (rawmidi) */
+struct seq_ump_client {
+ struct snd_ump_endpoint *ump; /* assigned endpoint */
+ int seq_client; /* sequencer client id */
+ int opened[2]; /* current opens for each direction */
+ struct snd_rawmidi_file out_rfile; /* rawmidi for output */
+ struct seq_ump_input_buffer input; /* input parser context */
+ void *ump_info[SNDRV_UMP_MAX_BLOCKS + 1]; /* shadow of seq client ump_info */
+ struct work_struct group_notify_work; /* FB change notification */
+};
+
+/* number of 32bit words for each UMP message type */
+static unsigned char ump_packet_words[0x10] = {
+ 1, 1, 1, 2, 2, 4, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4
+};
+
+/* conversion between UMP group and seq port;
+ * assume the port number is equal with UMP group number (1-based)
+ */
+static unsigned char ump_group_to_seq_port(unsigned char group)
+{
+ return group + 1;
+}
+
+/* process the incoming rawmidi stream */
+static void seq_ump_input_receive(struct snd_ump_endpoint *ump,
+ const u32 *val, int words)
+{
+ struct seq_ump_client *client = ump->seq_client;
+ struct snd_seq_ump_event ev = {};
+
+ if (!client->opened[STR_IN])
+ return;
+
+ if (ump_is_groupless_msg(ump_message_type(*val)))
+ ev.source.port = 0; /* UMP EP port */
+ else
+ ev.source.port = ump_group_to_seq_port(ump_message_group(*val));
+ ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+ ev.flags = SNDRV_SEQ_EVENT_UMP;
+ memcpy(ev.ump, val, words << 2);
+ snd_seq_kernel_client_dispatch(client->seq_client,
+ (struct snd_seq_event *)&ev,
+ true, 0);
+}
+
+/* process an input sequencer event; only deal with UMP types */
+static int seq_ump_process_event(struct snd_seq_event *ev, int direct,
+ void *private_data, int atomic, int hop)
+{
+ struct seq_ump_client *client = private_data;
+ struct snd_rawmidi_substream *substream;
+ struct snd_seq_ump_event *ump_ev;
+ unsigned char type;
+ int len;
+
+ substream = client->out_rfile.output;
+ if (!substream)
+ return -ENODEV;
+ if (!snd_seq_ev_is_ump(ev))
+ return 0; /* invalid event, skip */
+ ump_ev = (struct snd_seq_ump_event *)ev;
+ type = ump_message_type(ump_ev->ump[0]);
+ len = ump_packet_words[type];
+ if (len > 4)
+ return 0; // invalid - skip
+ snd_rawmidi_kernel_write(substream, ev->data.raw8.d, len << 2);
+ return 0;
+}
+
+/* open the rawmidi */
+static int seq_ump_client_open(struct seq_ump_client *client, int dir)
+{
+ struct snd_ump_endpoint *ump = client->ump;
+ int err;
+
+ guard(mutex)(&ump->open_mutex);
+ if (dir == STR_OUT && !client->opened[dir]) {
+ err = snd_rawmidi_kernel_open(&ump->core, 0,
+ SNDRV_RAWMIDI_LFLG_OUTPUT |
+ SNDRV_RAWMIDI_LFLG_APPEND,
+ &client->out_rfile);
+ if (err < 0)
+ return err;
+ }
+ client->opened[dir]++;
+ return 0;
+}
+
+/* close the rawmidi */
+static int seq_ump_client_close(struct seq_ump_client *client, int dir)
+{
+ struct snd_ump_endpoint *ump = client->ump;
+
+ guard(mutex)(&ump->open_mutex);
+ if (!--client->opened[dir])
+ if (dir == STR_OUT)
+ snd_rawmidi_kernel_release(&client->out_rfile);
+ return 0;
+}
+
+/* sequencer subscription ops for each client */
+static int seq_ump_subscribe(void *pdata, struct snd_seq_port_subscribe *info)
+{
+ struct seq_ump_client *client = pdata;
+
+ return seq_ump_client_open(client, STR_IN);
+}
+
+static int seq_ump_unsubscribe(void *pdata, struct snd_seq_port_subscribe *info)
+{
+ struct seq_ump_client *client = pdata;
+
+ return seq_ump_client_close(client, STR_IN);
+}
+
+static int seq_ump_use(void *pdata, struct snd_seq_port_subscribe *info)
+{
+ struct seq_ump_client *client = pdata;
+
+ return seq_ump_client_open(client, STR_OUT);
+}
+
+static int seq_ump_unuse(void *pdata, struct snd_seq_port_subscribe *info)
+{
+ struct seq_ump_client *client = pdata;
+
+ return seq_ump_client_close(client, STR_OUT);
+}
+
+/* fill port_info from the given UMP EP and group info */
+static void fill_port_info(struct snd_seq_port_info *port,
+ struct seq_ump_client *client,
+ struct snd_ump_group *group)
+{
+ unsigned int rawmidi_info = client->ump->core.info_flags;
+
+ port->addr.client = client->seq_client;
+ port->addr.port = ump_group_to_seq_port(group->group);
+ port->capability = 0;
+ if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT)
+ port->capability |= SNDRV_SEQ_PORT_CAP_WRITE |
+ SNDRV_SEQ_PORT_CAP_SYNC_WRITE |
+ SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
+ if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT)
+ port->capability |= SNDRV_SEQ_PORT_CAP_READ |
+ SNDRV_SEQ_PORT_CAP_SYNC_READ |
+ SNDRV_SEQ_PORT_CAP_SUBS_READ;
+ if (rawmidi_info & SNDRV_RAWMIDI_INFO_DUPLEX)
+ port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+ if (group->dir_bits & (1 << STR_IN))
+ port->direction |= SNDRV_SEQ_PORT_DIR_INPUT;
+ if (group->dir_bits & (1 << STR_OUT))
+ port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT;
+ port->ump_group = group->group + 1;
+ if (!group->active)
+ port->capability |= SNDRV_SEQ_PORT_CAP_INACTIVE;
+ if (group->is_midi1)
+ port->flags |= SNDRV_SEQ_PORT_FLG_IS_MIDI1;
+ port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
+ SNDRV_SEQ_PORT_TYPE_MIDI_UMP |
+ SNDRV_SEQ_PORT_TYPE_HARDWARE |
+ SNDRV_SEQ_PORT_TYPE_PORT;
+ port->midi_channels = 16;
+ if (*group->name)
+ snprintf(port->name, sizeof(port->name), "Group %d (%.53s)",
+ group->group + 1, group->name);
+ else
+ sprintf(port->name, "Group %d", group->group + 1);
+}
+
+/* skip non-existing group for static blocks */
+static bool skip_group(struct seq_ump_client *client, struct snd_ump_group *group)
+{
+ return !group->valid &&
+ (client->ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS);
+}
+
+/* create a new sequencer port per UMP group */
+static int seq_ump_group_init(struct seq_ump_client *client, int group_index)
+{
+ struct snd_ump_group *group = &client->ump->groups[group_index];
+ struct snd_seq_port_info *port __free(kfree) = NULL;
+ struct snd_seq_port_callback pcallbacks;
+
+ if (skip_group(client, group))
+ return 0;
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ fill_port_info(port, client, group);
+ port->flags |= SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
+ memset(&pcallbacks, 0, sizeof(pcallbacks));
+ pcallbacks.owner = THIS_MODULE;
+ pcallbacks.private_data = client;
+ pcallbacks.subscribe = seq_ump_subscribe;
+ pcallbacks.unsubscribe = seq_ump_unsubscribe;
+ pcallbacks.use = seq_ump_use;
+ pcallbacks.unuse = seq_ump_unuse;
+ pcallbacks.event_input = seq_ump_process_event;
+ port->kernel = &pcallbacks;
+ return snd_seq_kernel_client_ctl(client->seq_client,
+ SNDRV_SEQ_IOCTL_CREATE_PORT,
+ port);
+}
+
+/* update the sequencer ports; called from notify_fb_change callback */
+static void update_port_infos(struct seq_ump_client *client)
+{
+ struct snd_seq_port_info *old __free(kfree) = NULL;
+ struct snd_seq_port_info *new __free(kfree) = NULL;
+ int i, err;
+
+ old = kzalloc(sizeof(*old), GFP_KERNEL);
+ new = kzalloc(sizeof(*new), GFP_KERNEL);
+ if (!old || !new)
+ return;
+
+ for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) {
+ if (skip_group(client, &client->ump->groups[i]))
+ continue;
+
+ old->addr.client = client->seq_client;
+ old->addr.port = ump_group_to_seq_port(i);
+ err = snd_seq_kernel_client_ctl(client->seq_client,
+ SNDRV_SEQ_IOCTL_GET_PORT_INFO,
+ old);
+ if (err < 0)
+ continue;
+ fill_port_info(new, client, &client->ump->groups[i]);
+ if (old->capability == new->capability &&
+ !strcmp(old->name, new->name))
+ continue;
+ err = snd_seq_kernel_client_ctl(client->seq_client,
+ SNDRV_SEQ_IOCTL_SET_PORT_INFO,
+ new);
+ if (err < 0)
+ continue;
+ }
+}
+
+/* create a UMP Endpoint port */
+static int create_ump_endpoint_port(struct seq_ump_client *client)
+{
+ struct snd_seq_port_info *port __free(kfree) = NULL;
+ struct snd_seq_port_callback pcallbacks;
+ unsigned int rawmidi_info = client->ump->core.info_flags;
+ int err;
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->addr.client = client->seq_client;
+ port->addr.port = 0; /* fixed */
+ port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
+ port->capability = SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT;
+ if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT) {
+ port->capability |= SNDRV_SEQ_PORT_CAP_READ |
+ SNDRV_SEQ_PORT_CAP_SYNC_READ |
+ SNDRV_SEQ_PORT_CAP_SUBS_READ;
+ port->direction |= SNDRV_SEQ_PORT_DIR_INPUT;
+ }
+ if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT) {
+ port->capability |= SNDRV_SEQ_PORT_CAP_WRITE |
+ SNDRV_SEQ_PORT_CAP_SYNC_WRITE |
+ SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
+ port->direction |= SNDRV_SEQ_PORT_DIR_OUTPUT;
+ }
+ if (rawmidi_info & SNDRV_RAWMIDI_INFO_DUPLEX)
+ port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+ port->ump_group = 0; /* no associated group, no conversion */
+ port->type = SNDRV_SEQ_PORT_TYPE_MIDI_UMP |
+ SNDRV_SEQ_PORT_TYPE_HARDWARE |
+ SNDRV_SEQ_PORT_TYPE_PORT;
+ port->midi_channels = 16;
+ strscpy(port->name, "MIDI 2.0");
+ memset(&pcallbacks, 0, sizeof(pcallbacks));
+ pcallbacks.owner = THIS_MODULE;
+ pcallbacks.private_data = client;
+ if (rawmidi_info & SNDRV_RAWMIDI_INFO_INPUT) {
+ pcallbacks.subscribe = seq_ump_subscribe;
+ pcallbacks.unsubscribe = seq_ump_unsubscribe;
+ }
+ if (rawmidi_info & SNDRV_RAWMIDI_INFO_OUTPUT) {
+ pcallbacks.use = seq_ump_use;
+ pcallbacks.unuse = seq_ump_unuse;
+ pcallbacks.event_input = seq_ump_process_event;
+ }
+ port->kernel = &pcallbacks;
+ err = snd_seq_kernel_client_ctl(client->seq_client,
+ SNDRV_SEQ_IOCTL_CREATE_PORT,
+ port);
+ return err;
+}
+
+/* release the client resources */
+static void seq_ump_client_free(struct seq_ump_client *client)
+{
+ cancel_work_sync(&client->group_notify_work);
+
+ if (client->seq_client >= 0)
+ snd_seq_delete_kernel_client(client->seq_client);
+
+ client->ump->seq_ops = NULL;
+ client->ump->seq_client = NULL;
+
+ kfree(client);
+}
+
+/* update the MIDI version for the given client */
+static void setup_client_midi_version(struct seq_ump_client *client)
+{
+ struct snd_seq_client *cptr;
+
+ cptr = snd_seq_kernel_client_get(client->seq_client);
+ if (!cptr)
+ return;
+ if (client->ump->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2)
+ cptr->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_2_0;
+ else
+ cptr->midi_version = SNDRV_SEQ_CLIENT_UMP_MIDI_1_0;
+ snd_seq_kernel_client_put(cptr);
+}
+
+/* set up client's group_filter bitmap */
+static void setup_client_group_filter(struct seq_ump_client *client)
+{
+ struct snd_seq_client *cptr;
+ unsigned int filter;
+ int p;
+
+ cptr = snd_seq_kernel_client_get(client->seq_client);
+ if (!cptr)
+ return;
+ filter = ~(1U << 0); /* always allow groupless messages */
+ for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) {
+ if (client->ump->groups[p].active)
+ filter &= ~(1U << (p + 1));
+ }
+ cptr->group_filter = filter;
+ snd_seq_kernel_client_put(cptr);
+}
+
+/* UMP group change notification */
+static void handle_group_notify(struct work_struct *work)
+{
+ struct seq_ump_client *client =
+ container_of(work, struct seq_ump_client, group_notify_work);
+
+ update_port_infos(client);
+ setup_client_group_filter(client);
+}
+
+/* UMP EP change notification */
+static int seq_ump_notify_ep_change(struct snd_ump_endpoint *ump)
+{
+ struct seq_ump_client *client = ump->seq_client;
+ struct snd_seq_client *cptr;
+ int client_id;
+
+ if (!client)
+ return -ENODEV;
+ client_id = client->seq_client;
+ cptr = snd_seq_kernel_client_get(client_id);
+ if (!cptr)
+ return -ENODEV;
+
+ snd_seq_system_ump_notify(client_id, 0, SNDRV_SEQ_EVENT_UMP_EP_CHANGE,
+ true);
+
+ /* update sequencer client name if needed */
+ if (*ump->core.name && strcmp(ump->core.name, cptr->name)) {
+ strscpy(cptr->name, ump->core.name, sizeof(cptr->name));
+ snd_seq_system_client_ev_client_change(client_id);
+ }
+
+ snd_seq_kernel_client_put(cptr);
+ return 0;
+}
+
+/* UMP FB change notification */
+static int seq_ump_notify_fb_change(struct snd_ump_endpoint *ump,
+ struct snd_ump_block *fb)
+{
+ struct seq_ump_client *client = ump->seq_client;
+
+ if (!client)
+ return -ENODEV;
+ schedule_work(&client->group_notify_work);
+ snd_seq_system_ump_notify(client->seq_client, fb->info.block_id,
+ SNDRV_SEQ_EVENT_UMP_BLOCK_CHANGE,
+ true);
+ return 0;
+}
+
+/* UMP protocol change notification; just update the midi_version field */
+static int seq_ump_switch_protocol(struct snd_ump_endpoint *ump)
+{
+ struct seq_ump_client *client = ump->seq_client;
+
+ if (!client)
+ return -ENODEV;
+ setup_client_midi_version(client);
+ snd_seq_system_ump_notify(client->seq_client, 0,
+ SNDRV_SEQ_EVENT_UMP_EP_CHANGE,
+ true);
+ return 0;
+}
+
+static const struct snd_seq_ump_ops seq_ump_ops = {
+ .input_receive = seq_ump_input_receive,
+ .notify_ep_change = seq_ump_notify_ep_change,
+ .notify_fb_change = seq_ump_notify_fb_change,
+ .switch_protocol = seq_ump_switch_protocol,
+};
+
+/* create a sequencer client and ports for the given UMP endpoint */
+static int snd_seq_ump_probe(struct device *_dev)
+{
+ struct snd_seq_device *dev = to_seq_dev(_dev);
+ struct snd_ump_endpoint *ump = dev->private_data;
+ struct snd_card *card = dev->card;
+ struct seq_ump_client *client;
+ struct snd_ump_block *fb;
+ struct snd_seq_client *cptr;
+ int p, err;
+
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
+ if (!client)
+ return -ENOMEM;
+
+ INIT_WORK(&client->group_notify_work, handle_group_notify);
+ client->ump = ump;
+
+ client->seq_client =
+ snd_seq_create_kernel_client(card, ump->core.device,
+ ump->core.name);
+ if (client->seq_client < 0) {
+ err = client->seq_client;
+ goto error;
+ }
+
+ client->ump_info[0] = &ump->info;
+ list_for_each_entry(fb, &ump->block_list, list)
+ client->ump_info[fb->info.block_id + 1] = &fb->info;
+
+ setup_client_midi_version(client);
+
+ for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) {
+ err = seq_ump_group_init(client, p);
+ if (err < 0)
+ goto error;
+ }
+
+ setup_client_group_filter(client);
+
+ err = create_ump_endpoint_port(client);
+ if (err < 0)
+ goto error;
+
+ cptr = snd_seq_kernel_client_get(client->seq_client);
+ if (!cptr) {
+ err = -EINVAL;
+ goto error;
+ }
+ cptr->ump_info = client->ump_info;
+ snd_seq_kernel_client_put(cptr);
+
+ ump->seq_client = client;
+ ump->seq_ops = &seq_ump_ops;
+ return 0;
+
+ error:
+ seq_ump_client_free(client);
+ return err;
+}
+
+/* remove a sequencer client */
+static int snd_seq_ump_remove(struct device *_dev)
+{
+ struct snd_seq_device *dev = to_seq_dev(_dev);
+ struct snd_ump_endpoint *ump = dev->private_data;
+
+ if (ump->seq_client)
+ seq_ump_client_free(ump->seq_client);
+ return 0;
+}
+
+static struct snd_seq_driver seq_ump_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .probe = snd_seq_ump_probe,
+ .remove = snd_seq_ump_remove,
+ },
+ .id = SNDRV_SEQ_DEV_ID_UMP,
+ .argsize = 0,
+};
+
+module_snd_seq_driver(seq_ump_driver);
+
+MODULE_DESCRIPTION("ALSA sequencer client for UMP rawmidi");
+MODULE_LICENSE("GPL");
diff --git a/sound/core/seq/seq_ump_convert.c b/sound/core/seq/seq_ump_convert.c
new file mode 100644
index 000000000000..db2f169cae11
--- /dev/null
+++ b/sound/core/seq/seq_ump_convert.c
@@ -0,0 +1,1305 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ALSA sequencer event conversion between UMP and legacy clients
+ */
+
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <sound/core.h>
+#include <sound/ump.h>
+#include <sound/ump_msg.h>
+#include "seq_ump_convert.h"
+
+/*
+ * Upgrade / downgrade value bits
+ */
+static u8 downscale_32_to_7bit(u32 src)
+{
+ return src >> 25;
+}
+
+static u16 downscale_32_to_14bit(u32 src)
+{
+ return src >> 18;
+}
+
+static u8 downscale_16_to_7bit(u16 src)
+{
+ return src >> 9;
+}
+
+static u16 upscale_7_to_16bit(u8 src)
+{
+ u16 val, repeat;
+
+ val = (u16)src << 9;
+ if (src <= 0x40)
+ return val;
+ repeat = src & 0x3f;
+ return val | (repeat << 3) | (repeat >> 3);
+}
+
+static u32 upscale_7_to_32bit(u8 src)
+{
+ u32 val, repeat;
+
+ val = src << 25;
+ if (src <= 0x40)
+ return val;
+ repeat = src & 0x3f;
+ return val | (repeat << 19) | (repeat << 13) |
+ (repeat << 7) | (repeat << 1) | (repeat >> 5);
+}
+
+static u32 upscale_14_to_32bit(u16 src)
+{
+ u32 val, repeat;
+
+ val = src << 18;
+ if (src <= 0x2000)
+ return val;
+ repeat = src & 0x1fff;
+ return val | (repeat << 5) | (repeat >> 8);
+}
+
+static unsigned char get_ump_group(struct snd_seq_client_port *port)
+{
+ return port->ump_group ? (port->ump_group - 1) : 0;
+}
+
+/* create a UMP header */
+#define make_raw_ump(port, type) \
+ ump_compose(type, get_ump_group(port), 0, 0)
+
+/*
+ * UMP -> MIDI1 sequencer event
+ */
+
+/* MIDI 1.0 CVM */
+
+/* encode note event */
+static void ump_midi1_to_note_ev(const union snd_ump_midi1_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.note.channel = val->note.channel;
+ ev->data.note.note = val->note.note;
+ ev->data.note.velocity = val->note.velocity;
+}
+
+/* encode one parameter controls */
+static void ump_midi1_to_ctrl_ev(const union snd_ump_midi1_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.control.channel = val->caf.channel;
+ ev->data.control.value = val->caf.data;
+}
+
+/* encode pitch wheel change */
+static void ump_midi1_to_pitchbend_ev(const union snd_ump_midi1_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.control.channel = val->pb.channel;
+ ev->data.control.value = (val->pb.data_msb << 7) | val->pb.data_lsb;
+ ev->data.control.value -= 8192;
+}
+
+/* encode midi control change */
+static void ump_midi1_to_cc_ev(const union snd_ump_midi1_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.control.channel = val->cc.channel;
+ ev->data.control.param = val->cc.index;
+ ev->data.control.value = val->cc.data;
+}
+
+/* Encoding MIDI 1.0 UMP packet */
+struct seq_ump_midi1_to_ev {
+ int seq_type;
+ void (*encode)(const union snd_ump_midi1_msg *val, struct snd_seq_event *ev);
+};
+
+/* Encoders for MIDI1 status 0x80-0xe0 */
+static struct seq_ump_midi1_to_ev midi1_msg_encoders[] = {
+ {SNDRV_SEQ_EVENT_NOTEOFF, ump_midi1_to_note_ev}, /* 0x80 */
+ {SNDRV_SEQ_EVENT_NOTEON, ump_midi1_to_note_ev}, /* 0x90 */
+ {SNDRV_SEQ_EVENT_KEYPRESS, ump_midi1_to_note_ev}, /* 0xa0 */
+ {SNDRV_SEQ_EVENT_CONTROLLER, ump_midi1_to_cc_ev}, /* 0xb0 */
+ {SNDRV_SEQ_EVENT_PGMCHANGE, ump_midi1_to_ctrl_ev}, /* 0xc0 */
+ {SNDRV_SEQ_EVENT_CHANPRESS, ump_midi1_to_ctrl_ev}, /* 0xd0 */
+ {SNDRV_SEQ_EVENT_PITCHBEND, ump_midi1_to_pitchbend_ev}, /* 0xe0 */
+};
+
+static int cvt_ump_midi1_to_event(const union snd_ump_midi1_msg *val,
+ struct snd_seq_event *ev)
+{
+ unsigned char status = val->note.status;
+
+ if (status < 0x8 || status > 0xe)
+ return 0; /* invalid - skip */
+ status -= 8;
+ ev->type = midi1_msg_encoders[status].seq_type;
+ ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
+ midi1_msg_encoders[status].encode(val, ev);
+ return 1;
+}
+
+/* MIDI System message */
+
+/* encode one parameter value*/
+static void ump_system_to_one_param_ev(const union snd_ump_midi1_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.control.value = val->system.parm1;
+}
+
+/* encode song position */
+static void ump_system_to_songpos_ev(const union snd_ump_midi1_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.control.value = (val->system.parm2 << 7) | val->system.parm1;
+}
+
+/* Encoders for 0xf0 - 0xff */
+static struct seq_ump_midi1_to_ev system_msg_encoders[] = {
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xf0 */
+ {SNDRV_SEQ_EVENT_QFRAME, ump_system_to_one_param_ev}, /* 0xf1 */
+ {SNDRV_SEQ_EVENT_SONGPOS, ump_system_to_songpos_ev}, /* 0xf2 */
+ {SNDRV_SEQ_EVENT_SONGSEL, ump_system_to_one_param_ev}, /* 0xf3 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xf4 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xf5 */
+ {SNDRV_SEQ_EVENT_TUNE_REQUEST, NULL}, /* 0xf6 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xf7 */
+ {SNDRV_SEQ_EVENT_CLOCK, NULL}, /* 0xf8 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xf9 */
+ {SNDRV_SEQ_EVENT_START, NULL}, /* 0xfa */
+ {SNDRV_SEQ_EVENT_CONTINUE, NULL}, /* 0xfb */
+ {SNDRV_SEQ_EVENT_STOP, NULL}, /* 0xfc */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xfd */
+ {SNDRV_SEQ_EVENT_SENSING, NULL}, /* 0xfe */
+ {SNDRV_SEQ_EVENT_RESET, NULL}, /* 0xff */
+};
+
+static int cvt_ump_system_to_event(const union snd_ump_midi1_msg *val,
+ struct snd_seq_event *ev)
+{
+ unsigned char status = val->system.status;
+
+ if ((status & 0xf0) != UMP_MIDI1_MSG_REALTIME)
+ return 0; /* invalid status - skip */
+ status &= 0x0f;
+ ev->type = system_msg_encoders[status].seq_type;
+ ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
+ if (ev->type == SNDRV_SEQ_EVENT_NONE)
+ return 0;
+ if (system_msg_encoders[status].encode)
+ system_msg_encoders[status].encode(val, ev);
+ return 1;
+}
+
+/* MIDI 2.0 CVM */
+
+/* encode note event */
+static int ump_midi2_to_note_ev(const union snd_ump_midi2_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.note.channel = val->note.channel;
+ ev->data.note.note = val->note.note;
+ ev->data.note.velocity = downscale_16_to_7bit(val->note.velocity);
+ /* correct note-on velocity 0 to 1;
+ * it's no longer equivalent as not-off for MIDI 2.0
+ */
+ if (ev->type == SNDRV_SEQ_EVENT_NOTEON &&
+ !ev->data.note.velocity)
+ ev->data.note.velocity = 1;
+ return 1;
+}
+
+/* encode pitch wheel change */
+static int ump_midi2_to_pitchbend_ev(const union snd_ump_midi2_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.control.channel = val->pb.channel;
+ ev->data.control.value = downscale_32_to_14bit(val->pb.data);
+ ev->data.control.value -= 8192;
+ return 1;
+}
+
+/* encode midi control change */
+static int ump_midi2_to_cc_ev(const union snd_ump_midi2_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.control.channel = val->cc.channel;
+ ev->data.control.param = val->cc.index;
+ ev->data.control.value = downscale_32_to_7bit(val->cc.data);
+ return 1;
+}
+
+/* encode midi program change */
+static int ump_midi2_to_pgm_ev(const union snd_ump_midi2_msg *val,
+ struct snd_seq_event *ev)
+{
+ int size = 1;
+
+ ev->data.control.channel = val->pg.channel;
+ if (val->pg.bank_valid) {
+ ev->type = SNDRV_SEQ_EVENT_CONTROL14;
+ ev->data.control.param = UMP_CC_BANK_SELECT;
+ ev->data.control.value = (val->pg.bank_msb << 7) | val->pg.bank_lsb;
+ ev[1] = ev[0];
+ ev++;
+ ev->type = SNDRV_SEQ_EVENT_PGMCHANGE;
+ size = 2;
+ }
+ ev->data.control.value = val->pg.program;
+ return size;
+}
+
+/* encode one parameter controls */
+static int ump_midi2_to_ctrl_ev(const union snd_ump_midi2_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.control.channel = val->caf.channel;
+ ev->data.control.value = downscale_32_to_7bit(val->caf.data);
+ return 1;
+}
+
+/* encode RPN/NRPN */
+static int ump_midi2_to_rpn_ev(const union snd_ump_midi2_msg *val,
+ struct snd_seq_event *ev)
+{
+ ev->data.control.channel = val->rpn.channel;
+ ev->data.control.param = (val->rpn.bank << 7) | val->rpn.index;
+ ev->data.control.value = downscale_32_to_14bit(val->rpn.data);
+ return 1;
+}
+
+/* Encoding MIDI 2.0 UMP Packet */
+struct seq_ump_midi2_to_ev {
+ int seq_type;
+ int (*encode)(const union snd_ump_midi2_msg *val, struct snd_seq_event *ev);
+};
+
+/* Encoders for MIDI2 status 0x00-0xf0 */
+static struct seq_ump_midi2_to_ev midi2_msg_encoders[] = {
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0x00 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0x10 */
+ {SNDRV_SEQ_EVENT_REGPARAM, ump_midi2_to_rpn_ev}, /* 0x20 */
+ {SNDRV_SEQ_EVENT_NONREGPARAM, ump_midi2_to_rpn_ev}, /* 0x30 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0x40 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0x50 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0x60 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0x70 */
+ {SNDRV_SEQ_EVENT_NOTEOFF, ump_midi2_to_note_ev}, /* 0x80 */
+ {SNDRV_SEQ_EVENT_NOTEON, ump_midi2_to_note_ev}, /* 0x90 */
+ {SNDRV_SEQ_EVENT_KEYPRESS, ump_midi2_to_note_ev}, /* 0xa0 */
+ {SNDRV_SEQ_EVENT_CONTROLLER, ump_midi2_to_cc_ev}, /* 0xb0 */
+ {SNDRV_SEQ_EVENT_PGMCHANGE, ump_midi2_to_pgm_ev}, /* 0xc0 */
+ {SNDRV_SEQ_EVENT_CHANPRESS, ump_midi2_to_ctrl_ev}, /* 0xd0 */
+ {SNDRV_SEQ_EVENT_PITCHBEND, ump_midi2_to_pitchbend_ev}, /* 0xe0 */
+ {SNDRV_SEQ_EVENT_NONE, NULL}, /* 0xf0 */
+};
+
+static int cvt_ump_midi2_to_event(const union snd_ump_midi2_msg *val,
+ struct snd_seq_event *ev)
+{
+ unsigned char status = val->note.status;
+
+ ev->type = midi2_msg_encoders[status].seq_type;
+ if (ev->type == SNDRV_SEQ_EVENT_NONE)
+ return 0; /* skip */
+ ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
+ return midi2_msg_encoders[status].encode(val, ev);
+}
+
+/* parse and compose for a sysex var-length event */
+static int cvt_ump_sysex7_to_event(const u32 *data, unsigned char *buf,
+ struct snd_seq_event *ev)
+{
+ unsigned char status;
+ unsigned char bytes;
+ u32 val;
+ int size = 0;
+
+ val = data[0];
+ status = ump_sysex_message_status(val);
+ bytes = ump_sysex_message_length(val);
+ if (bytes > 6)
+ return 0; // skip
+
+ if (status == UMP_SYSEX_STATUS_SINGLE ||
+ status == UMP_SYSEX_STATUS_START) {
+ buf[0] = UMP_MIDI1_MSG_SYSEX_START;
+ size = 1;
+ }
+
+ if (bytes > 0)
+ buf[size++] = (val >> 8) & 0x7f;
+ if (bytes > 1)
+ buf[size++] = val & 0x7f;
+ val = data[1];
+ if (bytes > 2)
+ buf[size++] = (val >> 24) & 0x7f;
+ if (bytes > 3)
+ buf[size++] = (val >> 16) & 0x7f;
+ if (bytes > 4)
+ buf[size++] = (val >> 8) & 0x7f;
+ if (bytes > 5)
+ buf[size++] = val & 0x7f;
+
+ if (status == UMP_SYSEX_STATUS_SINGLE ||
+ status == UMP_SYSEX_STATUS_END)
+ buf[size++] = UMP_MIDI1_MSG_SYSEX_END;
+
+ ev->type = SNDRV_SEQ_EVENT_SYSEX;
+ ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
+ ev->data.ext.len = size;
+ ev->data.ext.ptr = buf;
+ return 1;
+}
+
+/* convert UMP packet from MIDI 1.0 to MIDI 2.0 and deliver it */
+static int cvt_ump_midi1_to_midi2(struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *__event,
+ int atomic, int hop)
+{
+ struct snd_seq_ump_event *event = (struct snd_seq_ump_event *)__event;
+ struct snd_seq_ump_event ev_cvt;
+ const union snd_ump_midi1_msg *midi1 = (const union snd_ump_midi1_msg *)event->ump;
+ union snd_ump_midi2_msg *midi2 = (union snd_ump_midi2_msg *)ev_cvt.ump;
+ struct ump_cvt_to_ump_bank *cc;
+
+ ev_cvt = *event;
+ memset(&ev_cvt.ump, 0, sizeof(ev_cvt.ump));
+
+ midi2->note.type = UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE;
+ midi2->note.group = midi1->note.group;
+ midi2->note.status = midi1->note.status;
+ midi2->note.channel = midi1->note.channel;
+ switch (midi1->note.status) {
+ case UMP_MSG_STATUS_NOTE_ON:
+ case UMP_MSG_STATUS_NOTE_OFF:
+ midi2->note.note = midi1->note.note;
+ midi2->note.velocity = upscale_7_to_16bit(midi1->note.velocity);
+ break;
+ case UMP_MSG_STATUS_POLY_PRESSURE:
+ midi2->paf.note = midi1->paf.note;
+ midi2->paf.data = upscale_7_to_32bit(midi1->paf.data);
+ break;
+ case UMP_MSG_STATUS_CC:
+ cc = &dest_port->midi2_bank[midi1->note.channel];
+ switch (midi1->cc.index) {
+ case UMP_CC_BANK_SELECT:
+ cc->bank_set = 1;
+ cc->cc_bank_msb = midi1->cc.data;
+ return 0; // skip
+ case UMP_CC_BANK_SELECT_LSB:
+ cc->bank_set = 1;
+ cc->cc_bank_lsb = midi1->cc.data;
+ return 0; // skip
+ }
+ midi2->cc.index = midi1->cc.index;
+ midi2->cc.data = upscale_7_to_32bit(midi1->cc.data);
+ break;
+ case UMP_MSG_STATUS_PROGRAM:
+ midi2->pg.program = midi1->pg.program;
+ cc = &dest_port->midi2_bank[midi1->note.channel];
+ if (cc->bank_set) {
+ midi2->pg.bank_valid = 1;
+ midi2->pg.bank_msb = cc->cc_bank_msb;
+ midi2->pg.bank_lsb = cc->cc_bank_lsb;
+ cc->bank_set = 0;
+ }
+ break;
+ case UMP_MSG_STATUS_CHANNEL_PRESSURE:
+ midi2->caf.data = upscale_7_to_32bit(midi1->caf.data);
+ break;
+ case UMP_MSG_STATUS_PITCH_BEND:
+ midi2->pb.data = upscale_14_to_32bit((midi1->pb.data_msb << 7) |
+ midi1->pb.data_lsb);
+ break;
+ default:
+ return 0;
+ }
+
+ return __snd_seq_deliver_single_event(dest, dest_port,
+ (struct snd_seq_event *)&ev_cvt,
+ atomic, hop);
+}
+
+/* convert UMP packet from MIDI 2.0 to MIDI 1.0 and deliver it */
+static int cvt_ump_midi2_to_midi1(struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *__event,
+ int atomic, int hop)
+{
+ struct snd_seq_ump_event *event = (struct snd_seq_ump_event *)__event;
+ struct snd_seq_ump_event ev_cvt;
+ union snd_ump_midi1_msg *midi1 = (union snd_ump_midi1_msg *)ev_cvt.ump;
+ const union snd_ump_midi2_msg *midi2 = (const union snd_ump_midi2_msg *)event->ump;
+ int err;
+ u16 v;
+
+ ev_cvt = *event;
+ memset(&ev_cvt.ump, 0, sizeof(ev_cvt.ump));
+
+ midi1->note.type = UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE;
+ midi1->note.group = midi2->note.group;
+ midi1->note.status = midi2->note.status;
+ midi1->note.channel = midi2->note.channel;
+ switch (midi2->note.status) {
+ case UMP_MSG_STATUS_NOTE_ON:
+ case UMP_MSG_STATUS_NOTE_OFF:
+ midi1->note.note = midi2->note.note;
+ midi1->note.velocity = downscale_16_to_7bit(midi2->note.velocity);
+ break;
+ case UMP_MSG_STATUS_POLY_PRESSURE:
+ midi1->paf.note = midi2->paf.note;
+ midi1->paf.data = downscale_32_to_7bit(midi2->paf.data);
+ break;
+ case UMP_MSG_STATUS_CC:
+ midi1->cc.index = midi2->cc.index;
+ midi1->cc.data = downscale_32_to_7bit(midi2->cc.data);
+ break;
+ case UMP_MSG_STATUS_PROGRAM:
+ if (midi2->pg.bank_valid) {
+ midi1->cc.status = UMP_MSG_STATUS_CC;
+ midi1->cc.index = UMP_CC_BANK_SELECT;
+ midi1->cc.data = midi2->pg.bank_msb;
+ err = __snd_seq_deliver_single_event(dest, dest_port,
+ (struct snd_seq_event *)&ev_cvt,
+ atomic, hop);
+ if (err < 0)
+ return err;
+ midi1->cc.index = UMP_CC_BANK_SELECT_LSB;
+ midi1->cc.data = midi2->pg.bank_lsb;
+ err = __snd_seq_deliver_single_event(dest, dest_port,
+ (struct snd_seq_event *)&ev_cvt,
+ atomic, hop);
+ if (err < 0)
+ return err;
+ midi1->note.status = midi2->note.status;
+ }
+ midi1->pg.program = midi2->pg.program;
+ break;
+ case UMP_MSG_STATUS_CHANNEL_PRESSURE:
+ midi1->caf.data = downscale_32_to_7bit(midi2->caf.data);
+ break;
+ case UMP_MSG_STATUS_PITCH_BEND:
+ v = downscale_32_to_14bit(midi2->pb.data);
+ midi1->pb.data_msb = v >> 7;
+ midi1->pb.data_lsb = v & 0x7f;
+ break;
+ default:
+ return 0;
+ }
+
+ return __snd_seq_deliver_single_event(dest, dest_port,
+ (struct snd_seq_event *)&ev_cvt,
+ atomic, hop);
+}
+
+/* convert UMP to a legacy ALSA seq event and deliver it */
+static int cvt_ump_to_any(struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ unsigned char type,
+ int atomic, int hop)
+{
+ struct snd_seq_event ev_cvt[2]; /* up to two events */
+ struct snd_seq_ump_event *ump_ev = (struct snd_seq_ump_event *)event;
+ /* use the second event as a temp buffer for saving stack usage */
+ unsigned char *sysex_buf = (unsigned char *)(ev_cvt + 1);
+ unsigned char flags = event->flags & ~SNDRV_SEQ_EVENT_UMP;
+ int i, len, err;
+
+ ev_cvt[0] = ev_cvt[1] = *event;
+ ev_cvt[0].flags = flags;
+ ev_cvt[1].flags = flags;
+ switch (type) {
+ case UMP_MSG_TYPE_SYSTEM:
+ len = cvt_ump_system_to_event((union snd_ump_midi1_msg *)ump_ev->ump,
+ ev_cvt);
+ break;
+ case UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE:
+ len = cvt_ump_midi1_to_event((union snd_ump_midi1_msg *)ump_ev->ump,
+ ev_cvt);
+ break;
+ case UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE:
+ len = cvt_ump_midi2_to_event((union snd_ump_midi2_msg *)ump_ev->ump,
+ ev_cvt);
+ break;
+ case UMP_MSG_TYPE_DATA:
+ len = cvt_ump_sysex7_to_event(ump_ev->ump, sysex_buf, ev_cvt);
+ break;
+ default:
+ return 0;
+ }
+
+ for (i = 0; i < len; i++) {
+ err = __snd_seq_deliver_single_event(dest, dest_port,
+ &ev_cvt[i], atomic, hop);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Replace UMP group field with the destination and deliver */
+static int deliver_with_group_convert(struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_ump_event *ump_ev,
+ int atomic, int hop)
+{
+ struct snd_seq_ump_event ev = *ump_ev;
+
+ /* rewrite the group to the destination port */
+ ev.ump[0] &= ~(0xfU << 24);
+ /* fill with the new group; the dest_port->ump_group field is 1-based */
+ ev.ump[0] |= ((dest_port->ump_group - 1) << 24);
+
+ return __snd_seq_deliver_single_event(dest, dest_port,
+ (struct snd_seq_event *)&ev,
+ atomic, hop);
+}
+
+/* apply the UMP event filter; return true to skip the event */
+static bool ump_event_filtered(struct snd_seq_client *dest,
+ const struct snd_seq_ump_event *ev)
+{
+ unsigned char group;
+
+ group = ump_message_group(ev->ump[0]);
+ if (ump_is_groupless_msg(ump_message_type(ev->ump[0])))
+ return dest->group_filter & (1U << 0);
+ /* check the bitmap for 1-based group number */
+ return dest->group_filter & (1U << (group + 1));
+}
+
+/* Convert from UMP packet and deliver */
+int snd_seq_deliver_from_ump(struct snd_seq_client *source,
+ struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ int atomic, int hop)
+{
+ struct snd_seq_ump_event *ump_ev = (struct snd_seq_ump_event *)event;
+ unsigned char type;
+
+ if (snd_seq_ev_is_variable(event))
+ return 0; // skip, no variable event for UMP, so far
+ if (ump_event_filtered(dest, ump_ev))
+ return 0; // skip if group filter is set and matching
+ type = ump_message_type(ump_ev->ump[0]);
+
+ if (snd_seq_client_is_ump(dest)) {
+ bool is_midi2 = snd_seq_client_is_midi2(dest) &&
+ !dest_port->is_midi1;
+
+ if (is_midi2 && type == UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE)
+ return cvt_ump_midi1_to_midi2(dest, dest_port,
+ event, atomic, hop);
+ else if (!is_midi2 && type == UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE)
+ return cvt_ump_midi2_to_midi1(dest, dest_port,
+ event, atomic, hop);
+ /* non-EP port and different group is set? */
+ if (dest_port->ump_group &&
+ !ump_is_groupless_msg(type) &&
+ ump_message_group(*ump_ev->ump) + 1 != dest_port->ump_group)
+ return deliver_with_group_convert(dest, dest_port,
+ ump_ev, atomic, hop);
+ /* copy as-is */
+ return __snd_seq_deliver_single_event(dest, dest_port,
+ event, atomic, hop);
+ }
+
+ return cvt_ump_to_any(dest, dest_port, event, type, atomic, hop);
+}
+
+/*
+ * MIDI1 sequencer event -> UMP conversion
+ */
+
+/* Conversion to UMP MIDI 1.0 */
+
+/* convert note on/off event to MIDI 1.0 UMP */
+static int note_ev_to_ump_midi1(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status)
+{
+ if (!event->data.note.velocity)
+ status = UMP_MSG_STATUS_NOTE_OFF;
+ data->note.status = status;
+ data->note.channel = event->data.note.channel & 0x0f;
+ data->note.velocity = event->data.note.velocity & 0x7f;
+ data->note.note = event->data.note.note & 0x7f;
+ return 1;
+}
+
+/* convert CC event to MIDI 1.0 UMP */
+static int cc_ev_to_ump_midi1(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status)
+{
+ data->cc.status = status;
+ data->cc.channel = event->data.control.channel & 0x0f;
+ data->cc.index = event->data.control.param;
+ data->cc.data = event->data.control.value;
+ return 1;
+}
+
+/* convert one-parameter control event to MIDI 1.0 UMP */
+static int ctrl_ev_to_ump_midi1(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status)
+{
+ data->caf.status = status;
+ data->caf.channel = event->data.control.channel & 0x0f;
+ data->caf.data = event->data.control.value & 0x7f;
+ return 1;
+}
+
+/* convert pitchbend event to MIDI 1.0 UMP */
+static int pitchbend_ev_to_ump_midi1(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status)
+{
+ int val = event->data.control.value + 8192;
+
+ val = clamp(val, 0, 0x3fff);
+ data->pb.status = status;
+ data->pb.channel = event->data.control.channel & 0x0f;
+ data->pb.data_msb = (val >> 7) & 0x7f;
+ data->pb.data_lsb = val & 0x7f;
+ return 1;
+}
+
+/* convert 14bit control event to MIDI 1.0 UMP; split to two events */
+static int ctrl14_ev_to_ump_midi1(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status)
+{
+ data->cc.status = UMP_MSG_STATUS_CC;
+ data->cc.channel = event->data.control.channel & 0x0f;
+ data->cc.index = event->data.control.param & 0x7f;
+ if (event->data.control.param < 0x20) {
+ data->cc.data = (event->data.control.value >> 7) & 0x7f;
+ data[1] = data[0];
+ data[1].cc.index = event->data.control.param | 0x20;
+ data[1].cc.data = event->data.control.value & 0x7f;
+ return 2;
+ }
+
+ data->cc.data = event->data.control.value & 0x7f;
+ return 1;
+}
+
+/* convert RPN/NRPN event to MIDI 1.0 UMP; split to four events */
+static int rpn_ev_to_ump_midi1(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status)
+{
+ bool is_rpn = (status == UMP_MSG_STATUS_RPN);
+
+ data->cc.status = UMP_MSG_STATUS_CC;
+ data->cc.channel = event->data.control.channel & 0x0f;
+ data[1] = data[2] = data[3] = data[0];
+
+ data[0].cc.index = is_rpn ? UMP_CC_RPN_MSB : UMP_CC_NRPN_MSB;
+ data[0].cc.data = (event->data.control.param >> 7) & 0x7f;
+ data[1].cc.index = is_rpn ? UMP_CC_RPN_LSB : UMP_CC_NRPN_LSB;
+ data[1].cc.data = event->data.control.param & 0x7f;
+ data[2].cc.index = UMP_CC_DATA;
+ data[2].cc.data = (event->data.control.value >> 7) & 0x7f;
+ data[3].cc.index = UMP_CC_DATA_LSB;
+ data[3].cc.data = event->data.control.value & 0x7f;
+ return 4;
+}
+
+/* convert system / RT message to UMP */
+static int system_ev_to_ump_midi1(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status)
+{
+ data->system.type = UMP_MSG_TYPE_SYSTEM; // override
+ data->system.status = status;
+ return 1;
+}
+
+/* convert system / RT message with 1 parameter to UMP */
+static int system_1p_ev_to_ump_midi1(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status)
+{
+ data->system.type = UMP_MSG_TYPE_SYSTEM; // override
+ data->system.status = status;
+ data->system.parm1 = event->data.control.value & 0x7f;
+ return 1;
+}
+
+/* convert system / RT message with two parameters to UMP */
+static int system_2p_ev_to_ump_midi1(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status)
+{
+ data->system.type = UMP_MSG_TYPE_SYSTEM; // override
+ data->system.status = status;
+ data->system.parm1 = event->data.control.value & 0x7f;
+ data->system.parm2 = (event->data.control.value >> 7) & 0x7f;
+ return 1;
+}
+
+/* Conversion to UMP MIDI 2.0 */
+
+/* convert note on/off event to MIDI 2.0 UMP */
+static int note_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ if (!event->data.note.velocity)
+ status = UMP_MSG_STATUS_NOTE_OFF;
+ data->note.status = status;
+ data->note.channel = event->data.note.channel & 0x0f;
+ data->note.note = event->data.note.note & 0x7f;
+ data->note.velocity = upscale_7_to_16bit(event->data.note.velocity & 0x7f);
+ return 1;
+}
+
+/* convert PAF event to MIDI 2.0 UMP */
+static int paf_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ data->paf.status = status;
+ data->paf.channel = event->data.note.channel & 0x0f;
+ data->paf.note = event->data.note.note & 0x7f;
+ data->paf.data = upscale_7_to_32bit(event->data.note.velocity & 0x7f);
+ return 1;
+}
+
+static void reset_rpn(struct ump_cvt_to_ump_bank *cc)
+{
+ cc->rpn_set = 0;
+ cc->nrpn_set = 0;
+ cc->cc_rpn_msb = cc->cc_rpn_lsb = 0;
+ cc->cc_data_msb = cc->cc_data_lsb = 0;
+ cc->cc_data_msb_set = cc->cc_data_lsb_set = 0;
+}
+
+/* set up the MIDI2 RPN/NRPN packet data from the parsed info */
+static int fill_rpn(struct ump_cvt_to_ump_bank *cc,
+ union snd_ump_midi2_msg *data,
+ unsigned char channel,
+ bool flush)
+{
+ if (!(cc->cc_data_lsb_set || cc->cc_data_msb_set))
+ return 0; // skip
+ /* when not flushing, wait for complete data set */
+ if (!flush && (!cc->cc_data_lsb_set || !cc->cc_data_msb_set))
+ return 0; // skip
+
+ if (cc->rpn_set) {
+ data->rpn.status = UMP_MSG_STATUS_RPN;
+ data->rpn.bank = cc->cc_rpn_msb;
+ data->rpn.index = cc->cc_rpn_lsb;
+ } else if (cc->nrpn_set) {
+ data->rpn.status = UMP_MSG_STATUS_NRPN;
+ data->rpn.bank = cc->cc_nrpn_msb;
+ data->rpn.index = cc->cc_nrpn_lsb;
+ } else {
+ return 0; // skip
+ }
+
+ data->rpn.data = upscale_14_to_32bit((cc->cc_data_msb << 7) |
+ cc->cc_data_lsb);
+ data->rpn.channel = channel;
+
+ reset_rpn(cc);
+ return 1;
+}
+
+/* convert CC event to MIDI 2.0 UMP */
+static int cc_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ unsigned char channel = event->data.control.channel & 0x0f;
+ unsigned char index = event->data.control.param & 0x7f;
+ unsigned char val = event->data.control.value & 0x7f;
+ struct ump_cvt_to_ump_bank *cc = &dest_port->midi2_bank[channel];
+ int ret;
+
+ /* process special CC's (bank/rpn/nrpn) */
+ switch (index) {
+ case UMP_CC_RPN_MSB:
+ ret = fill_rpn(cc, data, channel, true);
+ cc->rpn_set = 1;
+ cc->cc_rpn_msb = val;
+ if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
+ reset_rpn(cc);
+ return ret;
+ case UMP_CC_RPN_LSB:
+ ret = fill_rpn(cc, data, channel, true);
+ cc->rpn_set = 1;
+ cc->cc_rpn_lsb = val;
+ if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
+ reset_rpn(cc);
+ return ret;
+ case UMP_CC_NRPN_MSB:
+ ret = fill_rpn(cc, data, channel, true);
+ cc->nrpn_set = 1;
+ cc->cc_nrpn_msb = val;
+ return ret;
+ case UMP_CC_NRPN_LSB:
+ ret = fill_rpn(cc, data, channel, true);
+ cc->nrpn_set = 1;
+ cc->cc_nrpn_lsb = val;
+ return ret;
+ case UMP_CC_DATA:
+ cc->cc_data_msb_set = 1;
+ cc->cc_data_msb = val;
+ return fill_rpn(cc, data, channel, false);
+ case UMP_CC_BANK_SELECT:
+ cc->bank_set = 1;
+ cc->cc_bank_msb = val;
+ return 0; // skip
+ case UMP_CC_BANK_SELECT_LSB:
+ cc->bank_set = 1;
+ cc->cc_bank_lsb = val;
+ return 0; // skip
+ case UMP_CC_DATA_LSB:
+ cc->cc_data_lsb_set = 1;
+ cc->cc_data_lsb = val;
+ return fill_rpn(cc, data, channel, false);
+ }
+
+ data->cc.status = status;
+ data->cc.channel = channel;
+ data->cc.index = index;
+ data->cc.data = upscale_7_to_32bit(event->data.control.value & 0x7f);
+ return 1;
+}
+
+/* convert one-parameter control event to MIDI 2.0 UMP */
+static int ctrl_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ data->caf.status = status;
+ data->caf.channel = event->data.control.channel & 0x0f;
+ data->caf.data = upscale_7_to_32bit(event->data.control.value & 0x7f);
+ return 1;
+}
+
+/* convert program change event to MIDI 2.0 UMP */
+static int pgm_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ unsigned char channel = event->data.control.channel & 0x0f;
+ struct ump_cvt_to_ump_bank *cc = &dest_port->midi2_bank[channel];
+
+ data->pg.status = status;
+ data->pg.channel = channel;
+ data->pg.program = event->data.control.value & 0x7f;
+ if (cc->bank_set) {
+ data->pg.bank_valid = 1;
+ data->pg.bank_msb = cc->cc_bank_msb;
+ data->pg.bank_lsb = cc->cc_bank_lsb;
+ cc->bank_set = 0;
+ }
+ return 1;
+}
+
+/* convert pitchbend event to MIDI 2.0 UMP */
+static int pitchbend_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ int val = event->data.control.value + 8192;
+
+ val = clamp(val, 0, 0x3fff);
+ data->pb.status = status;
+ data->pb.channel = event->data.control.channel & 0x0f;
+ data->pb.data = upscale_14_to_32bit(val);
+ return 1;
+}
+
+/* convert 14bit control event to MIDI 2.0 UMP; split to two events */
+static int ctrl14_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ unsigned char channel = event->data.control.channel & 0x0f;
+ unsigned char index = event->data.control.param & 0x7f;
+ struct ump_cvt_to_ump_bank *cc = &dest_port->midi2_bank[channel];
+ unsigned char msb, lsb;
+ int ret;
+
+ msb = (event->data.control.value >> 7) & 0x7f;
+ lsb = event->data.control.value & 0x7f;
+ /* process special CC's (bank/rpn/nrpn) */
+ switch (index) {
+ case UMP_CC_BANK_SELECT:
+ cc->cc_bank_msb = msb;
+ fallthrough;
+ case UMP_CC_BANK_SELECT_LSB:
+ cc->bank_set = 1;
+ cc->cc_bank_lsb = lsb;
+ return 0; // skip
+ case UMP_CC_RPN_MSB:
+ case UMP_CC_RPN_LSB:
+ ret = fill_rpn(cc, data, channel, true);
+ cc->cc_rpn_msb = msb;
+ cc->cc_rpn_lsb = lsb;
+ cc->rpn_set = 1;
+ if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
+ reset_rpn(cc);
+ return ret;
+ case UMP_CC_NRPN_MSB:
+ case UMP_CC_NRPN_LSB:
+ ret = fill_rpn(cc, data, channel, true);
+ cc->cc_nrpn_msb = msb;
+ cc->nrpn_set = 1;
+ cc->cc_nrpn_lsb = lsb;
+ return ret;
+ case UMP_CC_DATA:
+ case UMP_CC_DATA_LSB:
+ cc->cc_data_msb_set = cc->cc_data_lsb_set = 1;
+ cc->cc_data_msb = msb;
+ cc->cc_data_lsb = lsb;
+ return fill_rpn(cc, data, channel, false);
+ }
+
+ data->cc.status = UMP_MSG_STATUS_CC;
+ data->cc.channel = channel;
+ data->cc.index = index;
+ if (event->data.control.param < 0x20) {
+ data->cc.data = upscale_7_to_32bit(msb);
+ data[1] = data[0];
+ data[1].cc.index = event->data.control.param | 0x20;
+ data[1].cc.data = upscale_7_to_32bit(lsb);
+ return 2;
+ }
+
+ data->cc.data = upscale_7_to_32bit(lsb);
+ return 1;
+}
+
+/* convert RPN/NRPN event to MIDI 2.0 UMP */
+static int rpn_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ data->rpn.status = status;
+ data->rpn.channel = event->data.control.channel;
+ data->rpn.bank = (event->data.control.param >> 7) & 0x7f;
+ data->rpn.index = event->data.control.param & 0x7f;
+ data->rpn.data = upscale_14_to_32bit(event->data.control.value & 0x3fff);
+ return 1;
+}
+
+/* convert system / RT message to UMP */
+static int system_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ return system_ev_to_ump_midi1(event, dest_port,
+ (union snd_ump_midi1_msg *)data,
+ status);
+}
+
+/* convert system / RT message with 1 parameter to UMP */
+static int system_1p_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ return system_1p_ev_to_ump_midi1(event, dest_port,
+ (union snd_ump_midi1_msg *)data,
+ status);
+}
+
+/* convert system / RT message with two parameters to UMP */
+static int system_2p_ev_to_ump_midi2(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status)
+{
+ return system_2p_ev_to_ump_midi1(event, dest_port,
+ (union snd_ump_midi1_msg *)data,
+ status);
+}
+
+struct seq_ev_to_ump {
+ int seq_type;
+ unsigned char status;
+ int (*midi1_encode)(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi1_msg *data,
+ unsigned char status);
+ int (*midi2_encode)(const struct snd_seq_event *event,
+ struct snd_seq_client_port *dest_port,
+ union snd_ump_midi2_msg *data,
+ unsigned char status);
+};
+
+static const struct seq_ev_to_ump seq_ev_ump_encoders[] = {
+ { SNDRV_SEQ_EVENT_NOTEON, UMP_MSG_STATUS_NOTE_ON,
+ note_ev_to_ump_midi1, note_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_NOTEOFF, UMP_MSG_STATUS_NOTE_OFF,
+ note_ev_to_ump_midi1, note_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_KEYPRESS, UMP_MSG_STATUS_POLY_PRESSURE,
+ note_ev_to_ump_midi1, paf_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_CONTROLLER, UMP_MSG_STATUS_CC,
+ cc_ev_to_ump_midi1, cc_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_PGMCHANGE, UMP_MSG_STATUS_PROGRAM,
+ ctrl_ev_to_ump_midi1, pgm_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_CHANPRESS, UMP_MSG_STATUS_CHANNEL_PRESSURE,
+ ctrl_ev_to_ump_midi1, ctrl_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_PITCHBEND, UMP_MSG_STATUS_PITCH_BEND,
+ pitchbend_ev_to_ump_midi1, pitchbend_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_CONTROL14, 0,
+ ctrl14_ev_to_ump_midi1, ctrl14_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_NONREGPARAM, UMP_MSG_STATUS_NRPN,
+ rpn_ev_to_ump_midi1, rpn_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_REGPARAM, UMP_MSG_STATUS_RPN,
+ rpn_ev_to_ump_midi1, rpn_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_QFRAME, UMP_SYSTEM_STATUS_MIDI_TIME_CODE,
+ system_1p_ev_to_ump_midi1, system_1p_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_SONGPOS, UMP_SYSTEM_STATUS_SONG_POSITION,
+ system_2p_ev_to_ump_midi1, system_2p_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_SONGSEL, UMP_SYSTEM_STATUS_SONG_SELECT,
+ system_1p_ev_to_ump_midi1, system_1p_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_TUNE_REQUEST, UMP_SYSTEM_STATUS_TUNE_REQUEST,
+ system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_CLOCK, UMP_SYSTEM_STATUS_TIMING_CLOCK,
+ system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_START, UMP_SYSTEM_STATUS_START,
+ system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_CONTINUE, UMP_SYSTEM_STATUS_CONTINUE,
+ system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_STOP, UMP_SYSTEM_STATUS_STOP,
+ system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_SENSING, UMP_SYSTEM_STATUS_ACTIVE_SENSING,
+ system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
+ { SNDRV_SEQ_EVENT_RESET, UMP_SYSTEM_STATUS_RESET,
+ system_ev_to_ump_midi1, system_ev_to_ump_midi2 },
+};
+
+static const struct seq_ev_to_ump *find_ump_encoder(int type)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(seq_ev_ump_encoders); i++)
+ if (seq_ev_ump_encoders[i].seq_type == type)
+ return &seq_ev_ump_encoders[i];
+
+ return NULL;
+}
+
+static void setup_ump_event(struct snd_seq_ump_event *dest,
+ const struct snd_seq_event *src)
+{
+ memcpy(dest, src, sizeof(*src));
+ dest->type = 0;
+ dest->flags |= SNDRV_SEQ_EVENT_UMP;
+ dest->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+ memset(dest->ump, 0, sizeof(dest->ump));
+}
+
+/* Convert ALSA seq event to UMP MIDI 1.0 and deliver it */
+static int cvt_to_ump_midi1(struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ int atomic, int hop)
+{
+ const struct seq_ev_to_ump *encoder;
+ struct snd_seq_ump_event ev_cvt;
+ union snd_ump_midi1_msg data[4];
+ int i, n, err;
+
+ encoder = find_ump_encoder(event->type);
+ if (!encoder)
+ return __snd_seq_deliver_single_event(dest, dest_port,
+ event, atomic, hop);
+
+ data->raw = make_raw_ump(dest_port, UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE);
+ n = encoder->midi1_encode(event, dest_port, data, encoder->status);
+ if (!n)
+ return 0;
+
+ setup_ump_event(&ev_cvt, event);
+ for (i = 0; i < n; i++) {
+ ev_cvt.ump[0] = data[i].raw;
+ err = __snd_seq_deliver_single_event(dest, dest_port,
+ (struct snd_seq_event *)&ev_cvt,
+ atomic, hop);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Convert ALSA seq event to UMP MIDI 2.0 and deliver it */
+static int cvt_to_ump_midi2(struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ int atomic, int hop)
+{
+ const struct seq_ev_to_ump *encoder;
+ struct snd_seq_ump_event ev_cvt;
+ union snd_ump_midi2_msg data[2];
+ int i, n, err;
+
+ encoder = find_ump_encoder(event->type);
+ if (!encoder)
+ return __snd_seq_deliver_single_event(dest, dest_port,
+ event, atomic, hop);
+
+ data->raw[0] = make_raw_ump(dest_port, UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE);
+ data->raw[1] = 0;
+ n = encoder->midi2_encode(event, dest_port, data, encoder->status);
+ if (!n)
+ return 0;
+
+ setup_ump_event(&ev_cvt, event);
+ for (i = 0; i < n; i++) {
+ memcpy(ev_cvt.ump, &data[i], sizeof(data[i]));
+ err = __snd_seq_deliver_single_event(dest, dest_port,
+ (struct snd_seq_event *)&ev_cvt,
+ atomic, hop);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Fill up a sysex7 UMP from the byte stream */
+static void fill_sysex7_ump(struct snd_seq_client_port *dest_port,
+ u32 *val, u8 status, u8 *buf, int len)
+{
+ memset(val, 0, 8);
+ memcpy((u8 *)val + 2, buf, len);
+#ifdef __LITTLE_ENDIAN
+ swab32_array(val, 2);
+#endif
+ val[0] |= ump_compose(UMP_MSG_TYPE_DATA, get_ump_group(dest_port),
+ status, len);
+}
+
+/* Convert sysex var event to UMP sysex7 packets and deliver them */
+static int cvt_sysex_to_ump(struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ int atomic, int hop)
+{
+ struct snd_seq_ump_event ev_cvt;
+ unsigned char status;
+ u8 buf[8], *xbuf;
+ int offset = 0;
+ int len, err;
+ bool finished = false;
+
+ if (!snd_seq_ev_is_variable(event))
+ return 0;
+
+ setup_ump_event(&ev_cvt, event);
+ while (!finished) {
+ len = snd_seq_expand_var_event_at(event, sizeof(buf), buf, offset);
+ if (len <= 0)
+ break;
+ if (WARN_ON(len > sizeof(buf)))
+ break;
+
+ xbuf = buf;
+ status = UMP_SYSEX_STATUS_CONTINUE;
+ /* truncate the sysex start-marker */
+ if (*xbuf == UMP_MIDI1_MSG_SYSEX_START) {
+ status = UMP_SYSEX_STATUS_START;
+ len--;
+ offset++;
+ xbuf++;
+ }
+
+ /* if the last of this packet or the 1st byte of the next packet
+ * is the end-marker, finish the transfer with this packet
+ */
+ if (len > 0 && len < 8 &&
+ xbuf[len - 1] == UMP_MIDI1_MSG_SYSEX_END) {
+ if (status == UMP_SYSEX_STATUS_START)
+ status = UMP_SYSEX_STATUS_SINGLE;
+ else
+ status = UMP_SYSEX_STATUS_END;
+ len--;
+ finished = true;
+ }
+
+ len = min(len, 6);
+ fill_sysex7_ump(dest_port, ev_cvt.ump, status, xbuf, len);
+ err = __snd_seq_deliver_single_event(dest, dest_port,
+ (struct snd_seq_event *)&ev_cvt,
+ atomic, hop);
+ if (err < 0)
+ return err;
+ offset += len;
+ }
+ return 0;
+}
+
+/* Convert to UMP packet and deliver */
+int snd_seq_deliver_to_ump(struct snd_seq_client *source,
+ struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ int atomic, int hop)
+{
+ if (dest->group_filter & (1U << dest_port->ump_group))
+ return 0; /* group filtered - skip the event */
+ if (event->type == SNDRV_SEQ_EVENT_SYSEX)
+ return cvt_sysex_to_ump(dest, dest_port, event, atomic, hop);
+ else if (snd_seq_client_is_midi2(dest) && !dest_port->is_midi1)
+ return cvt_to_ump_midi2(dest, dest_port, event, atomic, hop);
+ else
+ return cvt_to_ump_midi1(dest, dest_port, event, atomic, hop);
+}
+
+/* return the UMP group-port number of the event;
+ * return -1 if groupless or non-UMP event
+ */
+int snd_seq_ump_group_port(const struct snd_seq_event *event)
+{
+ const struct snd_seq_ump_event *ump_ev =
+ (const struct snd_seq_ump_event *)event;
+ unsigned char type;
+
+ if (!snd_seq_ev_is_ump(event))
+ return -1;
+ type = ump_message_type(ump_ev->ump[0]);
+ if (ump_is_groupless_msg(type))
+ return -1;
+ /* group-port number starts from 1 */
+ return ump_message_group(ump_ev->ump[0]) + 1;
+}
diff --git a/sound/core/seq/seq_ump_convert.h b/sound/core/seq/seq_ump_convert.h
new file mode 100644
index 000000000000..4abf0a7637d7
--- /dev/null
+++ b/sound/core/seq/seq_ump_convert.h
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ALSA sequencer event conversion between UMP and legacy clients
+ */
+#ifndef __SEQ_UMP_CONVERT_H
+#define __SEQ_UMP_CONVERT_H
+
+#include "seq_clientmgr.h"
+#include "seq_ports.h"
+
+int snd_seq_deliver_from_ump(struct snd_seq_client *source,
+ struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ int atomic, int hop);
+int snd_seq_deliver_to_ump(struct snd_seq_client *source,
+ struct snd_seq_client *dest,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_event *event,
+ int atomic, int hop);
+int snd_seq_ump_group_port(const struct snd_seq_event *event);
+
+#endif /* __SEQ_UMP_CONVERT_H */
diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
index 86e7739269ca..9e7fd4993a10 100644
--- a/sound/core/seq/seq_virmidi.c
+++ b/sound/core/seq/seq_virmidi.c
@@ -1,23 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Virtual Raw MIDI client on Sequencer
*
* Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>,
* Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -37,6 +23,7 @@
#include <linux/init.h>
#include <linux/wait.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/rawmidi.h>
@@ -75,50 +62,48 @@ static void snd_virmidi_init_event(struct snd_virmidi *vmidi,
/*
* decode input event and put to read buffer of each opened file
*/
+
+/* callback for snd_seq_dump_var_event(), bridging to snd_rawmidi_receive() */
+static int dump_to_rawmidi(void *ptr, void *buf, int count)
+{
+ return snd_rawmidi_receive(ptr, buf, count);
+}
+
static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev,
- struct snd_seq_event *ev)
+ struct snd_seq_event *ev,
+ bool atomic)
{
struct snd_virmidi *vmidi;
unsigned char msg[4];
int len;
- read_lock(&rdev->filelist_lock);
+ if (atomic)
+ read_lock(&rdev->filelist_lock);
+ else
+ down_read(&rdev->filelist_sem);
list_for_each_entry(vmidi, &rdev->filelist, list) {
- if (!vmidi->trigger)
+ if (!READ_ONCE(vmidi->trigger))
continue;
if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
continue;
- snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream);
+ snd_seq_dump_var_event(ev, dump_to_rawmidi, vmidi->substream);
+ snd_midi_event_reset_decode(vmidi->parser);
} else {
len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev);
if (len > 0)
snd_rawmidi_receive(vmidi->substream, msg, len);
}
}
- read_unlock(&rdev->filelist_lock);
+ if (atomic)
+ read_unlock(&rdev->filelist_lock);
+ else
+ up_read(&rdev->filelist_sem);
return 0;
}
/*
- * receive an event from the remote virmidi port
- *
- * for rawmidi inputs, you can call this function from the event
- * handler of a remote port which is attached to the virmidi via
- * SNDRV_VIRMIDI_SEQ_ATTACH.
- */
-#if 0
-int snd_virmidi_receive(struct snd_rawmidi *rmidi, struct snd_seq_event *ev)
-{
- struct snd_virmidi_dev *rdev;
-
- rdev = rmidi->private_data;
- return snd_virmidi_dev_receive_event(rdev, ev);
-}
-#endif /* 0 */
-
-/*
* event handler of virmidi port
*/
static int snd_virmidi_event_input(struct snd_seq_event *ev, int direct,
@@ -129,7 +114,7 @@ static int snd_virmidi_event_input(struct snd_seq_event *ev, int direct,
rdev = private_data;
if (!(rdev->flags & SNDRV_VIRMIDI_USE))
return 0; /* ignored */
- return snd_virmidi_dev_receive_event(rdev, ev);
+ return snd_virmidi_dev_receive_event(rdev, ev, atomic);
}
/*
@@ -139,61 +124,61 @@ static void snd_virmidi_input_trigger(struct snd_rawmidi_substream *substream, i
{
struct snd_virmidi *vmidi = substream->runtime->private_data;
- if (up) {
- vmidi->trigger = 1;
- } else {
- vmidi->trigger = 0;
- }
+ WRITE_ONCE(vmidi->trigger, !!up);
}
-/*
- * trigger rawmidi stream for output
+/* process rawmidi bytes and send events;
+ * we need no lock here for vmidi->event since it's handled only in this work
*/
-static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream, int up)
+static void snd_vmidi_output_work(struct work_struct *work)
{
- struct snd_virmidi *vmidi = substream->runtime->private_data;
- int count, res;
- unsigned char buf[32], *pbuf;
-
- if (up) {
- vmidi->trigger = 1;
- if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
- !(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
- snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail);
- return; /* ignored */
- }
+ struct snd_virmidi *vmidi;
+ struct snd_rawmidi_substream *substream;
+ unsigned char input;
+ int ret;
+
+ vmidi = container_of(work, struct snd_virmidi, output_work);
+ substream = vmidi->substream;
+
+ /* discard the outputs in dispatch mode unless subscribed */
+ if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
+ !(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
+ snd_rawmidi_proceed(substream);
+ return;
+ }
+
+ while (READ_ONCE(vmidi->trigger)) {
+ if (snd_rawmidi_transmit(substream, &input, 1) != 1)
+ break;
+ if (!snd_midi_event_encode_byte(vmidi->parser, input,
+ &vmidi->event))
+ continue;
if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
- if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0)
- return;
+ ret = snd_seq_kernel_client_dispatch(vmidi->client,
+ &vmidi->event,
+ false, 0);
vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
- }
- while (1) {
- count = snd_rawmidi_transmit_peek(substream, buf, sizeof(buf));
- if (count <= 0)
+ if (ret < 0)
break;
- pbuf = buf;
- while (count > 0) {
- res = snd_midi_event_encode(vmidi->parser, pbuf, count, &vmidi->event);
- if (res < 0) {
- snd_midi_event_reset_encode(vmidi->parser);
- continue;
- }
- snd_rawmidi_transmit_ack(substream, res);
- pbuf += res;
- count -= res;
- if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
- if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0)
- return;
- vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
- }
- }
}
- } else {
- vmidi->trigger = 0;
+ /* rawmidi input might be huge, allow to have a break */
+ cond_resched();
}
}
/*
+ * trigger rawmidi stream for output
+ */
+static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+ struct snd_virmidi *vmidi = substream->runtime->private_data;
+
+ WRITE_ONCE(vmidi->trigger, !!up);
+ if (up)
+ queue_work(system_highpri_wq, &vmidi->output_work);
+}
+
+/*
* open rawmidi handle for input
*/
static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream)
@@ -201,7 +186,6 @@ static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream)
struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
struct snd_rawmidi_runtime *runtime = substream->runtime;
struct snd_virmidi *vmidi;
- unsigned long flags;
vmidi = kzalloc(sizeof(*vmidi), GFP_KERNEL);
if (vmidi == NULL)
@@ -215,9 +199,10 @@ static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream)
vmidi->client = rdev->client;
vmidi->port = rdev->port;
runtime->private_data = vmidi;
- write_lock_irqsave(&rdev->filelist_lock, flags);
- list_add_tail(&vmidi->list, &rdev->filelist);
- write_unlock_irqrestore(&rdev->filelist_lock, flags);
+ scoped_guard(rwsem_write, &rdev->filelist_sem) {
+ guard(write_lock_irq)(&rdev->filelist_lock);
+ list_add_tail(&vmidi->list, &rdev->filelist);
+ }
vmidi->rdev = rdev;
return 0;
}
@@ -244,6 +229,7 @@ static int snd_virmidi_output_open(struct snd_rawmidi_substream *substream)
vmidi->port = rdev->port;
snd_virmidi_init_event(vmidi, &vmidi->event);
vmidi->rdev = rdev;
+ INIT_WORK(&vmidi->output_work, snd_vmidi_output_work);
runtime->private_data = vmidi;
return 0;
}
@@ -253,9 +239,14 @@ static int snd_virmidi_output_open(struct snd_rawmidi_substream *substream)
*/
static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream)
{
+ struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
struct snd_virmidi *vmidi = substream->runtime->private_data;
+
+ scoped_guard(rwsem_write, &rdev->filelist_sem) {
+ guard(write_lock_irq)(&rdev->filelist_lock);
+ list_del(&vmidi->list);
+ }
snd_midi_event_free(vmidi->parser);
- list_del(&vmidi->list);
substream->runtime->private_data = NULL;
kfree(vmidi);
return 0;
@@ -267,6 +258,9 @@ static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream)
static int snd_virmidi_output_close(struct snd_rawmidi_substream *substream)
{
struct snd_virmidi *vmidi = substream->runtime->private_data;
+
+ WRITE_ONCE(vmidi->trigger, false); /* to be sure */
+ cancel_work_sync(&vmidi->output_work);
snd_midi_event_free(vmidi->parser);
substream->runtime->private_data = NULL;
kfree(vmidi);
@@ -274,6 +268,16 @@ static int snd_virmidi_output_close(struct snd_rawmidi_substream *substream)
}
/*
+ * drain output work queue
+ */
+static void snd_virmidi_output_drain(struct snd_rawmidi_substream *substream)
+{
+ struct snd_virmidi *vmidi = substream->runtime->private_data;
+
+ flush_work(&vmidi->output_work);
+}
+
+/*
* subscribe callback - allow output to rawmidi device
*/
static int snd_virmidi_subscribe(void *private_data,
@@ -337,16 +341,17 @@ static int snd_virmidi_unuse(void *private_data,
* Register functions
*/
-static struct snd_rawmidi_ops snd_virmidi_input_ops = {
+static const struct snd_rawmidi_ops snd_virmidi_input_ops = {
.open = snd_virmidi_input_open,
.close = snd_virmidi_input_close,
.trigger = snd_virmidi_input_trigger,
};
-static struct snd_rawmidi_ops snd_virmidi_output_ops = {
+static const struct snd_rawmidi_ops snd_virmidi_output_ops = {
.open = snd_virmidi_output_open,
.close = snd_virmidi_output_close,
.trigger = snd_virmidi_output_trigger,
+ .drain = snd_virmidi_output_drain,
};
/*
@@ -356,26 +361,22 @@ static int snd_virmidi_dev_attach_seq(struct snd_virmidi_dev *rdev)
{
int client;
struct snd_seq_port_callback pcallbacks;
- struct snd_seq_port_info *pinfo;
+ struct snd_seq_port_info *pinfo __free(kfree) = NULL;
int err;
if (rdev->client >= 0)
return 0;
pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
- if (!pinfo) {
- err = -ENOMEM;
- goto __error;
- }
+ if (!pinfo)
+ return -ENOMEM;
client = snd_seq_create_kernel_client(rdev->card, rdev->device,
"%s %d-%d", rdev->rmidi->name,
rdev->card->number,
rdev->device);
- if (client < 0) {
- err = client;
- goto __error;
- }
+ if (client < 0)
+ return client;
rdev->client = client;
/* create a port */
@@ -385,6 +386,7 @@ static int snd_virmidi_dev_attach_seq(struct snd_virmidi_dev *rdev)
pinfo->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
pinfo->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
pinfo->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+ pinfo->direction = SNDRV_SEQ_PORT_DIR_BIDIRECTION;
pinfo->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
| SNDRV_SEQ_PORT_TYPE_SOFTWARE
| SNDRV_SEQ_PORT_TYPE_PORT;
@@ -402,15 +404,11 @@ static int snd_virmidi_dev_attach_seq(struct snd_virmidi_dev *rdev)
if (err < 0) {
snd_seq_delete_kernel_client(client);
rdev->client = -1;
- goto __error;
+ return err;
}
rdev->port = pinfo->addr.port;
- err = 0; /* success */
-
- __error:
- kfree(pinfo);
- return err;
+ return 0; /* success */
}
@@ -445,7 +443,7 @@ static int snd_virmidi_dev_register(struct snd_rawmidi *rmidi)
/* should check presence of port more strictly.. */
break;
default:
- snd_printk(KERN_ERR "seq_mode is not set: %d\n", rdev->seq_mode);
+ pr_err("ALSA: seq_virmidi: seq_mode is not set: %d\n", rdev->seq_mode);
return -EINVAL;
}
return 0;
@@ -467,7 +465,7 @@ static int snd_virmidi_dev_unregister(struct snd_rawmidi *rmidi)
/*
*
*/
-static struct snd_rawmidi_global_ops snd_virmidi_global_ops = {
+static const struct snd_rawmidi_global_ops snd_virmidi_global_ops = {
.dev_register = snd_virmidi_dev_register,
.dev_unregister = snd_virmidi_dev_unregister,
};
@@ -493,12 +491,13 @@ int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmi
int err;
*rrmidi = NULL;
- if ((err = snd_rawmidi_new(card, "VirMidi", device,
- 16, /* may be configurable */
- 16, /* may be configurable */
- &rmidi)) < 0)
+ err = snd_rawmidi_new(card, "VirMidi", device,
+ 16, /* may be configurable */
+ 16, /* may be configurable */
+ &rmidi);
+ if (err < 0)
return err;
- strcpy(rmidi->name, rmidi->id);
+ strscpy(rmidi->name, rmidi->id);
rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
if (rdev == NULL) {
snd_device_free(card, rmidi);
@@ -508,6 +507,7 @@ int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmi
rdev->rmidi = rmidi;
rdev->device = device;
rdev->client = -1;
+ init_rwsem(&rdev->filelist_sem);
rwlock_init(&rdev->filelist_lock);
INIT_LIST_HEAD(&rdev->filelist);
rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
@@ -522,21 +522,4 @@ int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmi
*rrmidi = rmidi;
return 0;
}
-
-/*
- * ENTRY functions
- */
-
-static int __init alsa_virmidi_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_virmidi_exit(void)
-{
-}
-
-module_init(alsa_virmidi_init)
-module_exit(alsa_virmidi_exit)
-
EXPORT_SYMBOL(snd_virmidi_new);
diff --git a/sound/core/seq_device.c b/sound/core/seq_device.c
new file mode 100644
index 000000000000..bac9f8603734
--- /dev/null
+++ b/sound/core/seq_device.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ALSA sequencer device management
+ * Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
+ *
+ *----------------------------------------------------------------
+ *
+ * This device handler separates the card driver module from sequencer
+ * stuff (sequencer core, synth drivers, etc), so that user can avoid
+ * to spend unnecessary resources e.g. if he needs only listening to
+ * MP3s.
+ *
+ * The card (or lowlevel) driver creates a sequencer device entry
+ * via snd_seq_device_new(). This is an entry pointer to communicate
+ * with the sequencer device "driver", which is involved with the
+ * actual part to communicate with the sequencer core.
+ * Each sequencer device entry has an id string and the corresponding
+ * driver with the same id is loaded when required. For example,
+ * lowlevel codes to access emu8000 chip on sbawe card are included in
+ * emu8000-synth module. To activate this module, the hardware
+ * resources like i/o port are passed via snd_seq_device argument.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/seq_device.h>
+#include <sound/seq_kernel.h>
+#include <sound/initval.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("ALSA sequencer device management");
+MODULE_LICENSE("GPL");
+
+/*
+ * bus definition
+ */
+static int snd_seq_bus_match(struct device *dev, const struct device_driver *drv)
+{
+ struct snd_seq_device *sdev = to_seq_dev(dev);
+ const struct snd_seq_driver *sdrv = to_seq_drv(drv);
+
+ return strcmp(sdrv->id, sdev->id) == 0 &&
+ sdrv->argsize == sdev->argsize;
+}
+
+static const struct bus_type snd_seq_bus_type = {
+ .name = "snd_seq",
+ .match = snd_seq_bus_match,
+};
+
+/*
+ * proc interface -- just for compatibility
+ */
+#ifdef CONFIG_SND_PROC_FS
+static struct snd_info_entry *info_entry;
+
+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;
+
+ snd_iprintf(buffer, "snd-%s,%s,%d\n", sdev->id,
+ dev->driver ? "loaded" : "empty",
+ dev->driver ? 1 : 0);
+ return 0;
+}
+
+static void snd_seq_device_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ bus_for_each_dev(&snd_seq_bus_type, NULL, buffer, print_dev_info);
+}
+#endif
+
+/*
+ * load all registered drivers (called from seq_clientmgr.c)
+ */
+
+#ifdef CONFIG_MODULES
+/* flag to block auto-loading */
+static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */
+
+static int request_seq_drv(struct device *dev, void *data)
+{
+ struct snd_seq_device *sdev = to_seq_dev(dev);
+
+ if (!dev->driver)
+ request_module("snd-%s", sdev->id);
+ return 0;
+}
+
+static void autoload_drivers(struct work_struct *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)
+{
+ schedule_work(&autoload_work);
+}
+
+void snd_seq_autoload_init(void)
+{
+ atomic_dec(&snd_seq_in_init);
+#ifdef CONFIG_SND_SEQUENCER_MODULE
+ /* initial autoload only when snd-seq is a module */
+ queue_autoload_drivers();
+#endif
+}
+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();
+ flush_work(&autoload_work);
+}
+EXPORT_SYMBOL(snd_seq_device_load_drivers);
+
+static inline void cancel_autoload_drivers(void)
+{
+ cancel_work_sync(&autoload_work);
+}
+#else
+static inline void queue_autoload_drivers(void)
+{
+}
+
+static inline void cancel_autoload_drivers(void)
+{
+}
+#endif
+
+/*
+ * device management
+ */
+static int snd_seq_device_dev_free(struct snd_device *device)
+{
+ struct snd_seq_device *dev = device->device_data;
+
+ cancel_autoload_drivers();
+ if (dev->private_free)
+ dev->private_free(dev);
+ 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)
+{
+ kfree(to_seq_dev(dev));
+}
+
+/*
+ * register a sequencer device
+ * card = card info
+ * device = device number (if any)
+ * id = id of driver
+ * result = return pointer (NULL allowed if unnecessary)
+ */
+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;
+ static const struct snd_device_ops dops = {
+ .dev_free = snd_seq_device_dev_free,
+ .dev_register = snd_seq_device_dev_register,
+ .dev_disconnect = snd_seq_device_dev_disconnect,
+ };
+
+ if (result)
+ *result = NULL;
+
+ if (snd_BUG_ON(!id))
+ return -EINVAL;
+
+ dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ /* set up device info */
+ dev->card = card;
+ dev->device = device;
+ dev->id = id;
+ dev->argsize = argsize;
+
+ 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);
+
+ /* 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;
+ }
+
+ if (result)
+ *result = dev;
+
+ return 0;
+}
+EXPORT_SYMBOL(snd_seq_device_new);
+
+/*
+ * driver registration
+ */
+int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod)
+{
+ if (WARN_ON(!drv->driver.name || !drv->id))
+ return -EINVAL;
+ drv->driver.bus = &snd_seq_bus_type;
+ drv->driver.owner = mod;
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(__snd_seq_driver_register);
+
+void snd_seq_driver_unregister(struct snd_seq_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(snd_seq_driver_unregister);
+
+/*
+ * module part
+ */
+
+static int __init seq_dev_proc_init(void)
+{
+#ifdef CONFIG_SND_PROC_FS
+ info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
+ snd_seq_root);
+ if (info_entry == NULL)
+ return -ENOMEM;
+ info_entry->content = SNDRV_INFO_CONTENT_TEXT;
+ info_entry->c.text.read = snd_seq_device_info;
+ if (snd_info_register(info_entry) < 0) {
+ snd_info_free_entry(info_entry);
+ return -ENOMEM;
+ }
+#endif
+ 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
+#ifdef CONFIG_SND_PROC_FS
+ snd_info_free_entry(info_entry);
+#endif
+ bus_unregister(&snd_seq_bus_type);
+}
+
+subsys_initcall(alsa_seq_device_init)
+module_exit(alsa_seq_device_exit)
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
deleted file mode 100644
index 4e7ec2b49873..000000000000
--- a/sound/core/sgbuf.c
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Scatter-Gather buffer
- *
- * Copyright (c) by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/vmalloc.h>
-#include <sound/memalloc.h>
-
-
-/* table entries are align to 32 */
-#define SGBUF_TBL_ALIGN 32
-#define sgbuf_align_table(tbl) ALIGN((tbl), SGBUF_TBL_ALIGN)
-
-int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
-{
- struct snd_sg_buf *sgbuf = dmab->private_data;
- struct snd_dma_buffer tmpb;
- int i;
-
- if (! sgbuf)
- return -EINVAL;
-
- if (dmab->area)
- vunmap(dmab->area);
- dmab->area = NULL;
-
- tmpb.dev.type = SNDRV_DMA_TYPE_DEV;
- tmpb.dev.dev = sgbuf->dev;
- for (i = 0; i < sgbuf->pages; i++) {
- if (!(sgbuf->table[i].addr & ~PAGE_MASK))
- continue; /* continuous pages */
- tmpb.area = sgbuf->table[i].buf;
- tmpb.addr = sgbuf->table[i].addr & PAGE_MASK;
- tmpb.bytes = (sgbuf->table[i].addr & ~PAGE_MASK) << PAGE_SHIFT;
- snd_dma_free_pages(&tmpb);
- }
-
- kfree(sgbuf->table);
- kfree(sgbuf->page_table);
- kfree(sgbuf);
- dmab->private_data = NULL;
-
- return 0;
-}
-
-#define MAX_ALLOC_PAGES 32
-
-void *snd_malloc_sgbuf_pages(struct device *device,
- size_t size, struct snd_dma_buffer *dmab,
- size_t *res_size)
-{
- struct snd_sg_buf *sgbuf;
- unsigned int i, pages, chunk, maxpages;
- struct snd_dma_buffer tmpb;
- struct snd_sg_page *table;
- struct page **pgtable;
-
- dmab->area = NULL;
- dmab->addr = 0;
- dmab->private_data = sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL);
- if (! sgbuf)
- return NULL;
- sgbuf->dev = device;
- pages = snd_sgbuf_aligned_pages(size);
- sgbuf->tblsize = sgbuf_align_table(pages);
- table = kcalloc(sgbuf->tblsize, sizeof(*table), GFP_KERNEL);
- if (!table)
- goto _failed;
- sgbuf->table = table;
- pgtable = kcalloc(sgbuf->tblsize, sizeof(*pgtable), GFP_KERNEL);
- if (!pgtable)
- goto _failed;
- sgbuf->page_table = pgtable;
-
- /* allocate pages */
- maxpages = MAX_ALLOC_PAGES;
- while (pages > 0) {
- chunk = pages;
- /* don't be too eager to take a huge chunk */
- if (chunk > maxpages)
- chunk = maxpages;
- chunk <<= PAGE_SHIFT;
- if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, device,
- chunk, &tmpb) < 0) {
- if (!sgbuf->pages)
- return NULL;
- if (!res_size)
- goto _failed;
- size = sgbuf->pages * PAGE_SIZE;
- break;
- }
- chunk = tmpb.bytes >> PAGE_SHIFT;
- for (i = 0; i < chunk; i++) {
- table->buf = tmpb.area;
- table->addr = tmpb.addr;
- if (!i)
- table->addr |= chunk; /* mark head */
- table++;
- *pgtable++ = virt_to_page(tmpb.area);
- tmpb.area += PAGE_SIZE;
- tmpb.addr += PAGE_SIZE;
- }
- sgbuf->pages += chunk;
- pages -= chunk;
- if (chunk < maxpages)
- maxpages = chunk;
- }
-
- sgbuf->size = size;
- dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, PAGE_KERNEL);
- if (! dmab->area)
- goto _failed;
- if (res_size)
- *res_size = sgbuf->size;
- return dmab->area;
-
- _failed:
- snd_free_sgbuf_pages(dmab); /* free the table */
- return NULL;
-}
diff --git a/sound/core/sound.c b/sound/core/sound.c
index 62a093efb453..6531a67f13b3 100644
--- a/sound/core/sound.c
+++ b/sound/core/sound.c
@@ -1,34 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Advanced Linux Sound Architecture
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
#include <linux/time.h>
#include <linux/device.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
-#include <sound/version.h>
#include <sound/control.h>
#include <sound/initval.h>
#include <linux/kmod.h>
@@ -56,6 +40,11 @@ MODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MAJOR);
int snd_ecards_limit;
EXPORT_SYMBOL(snd_ecards_limit);
+#ifdef CONFIG_SND_DEBUG
+struct dentry *sound_debugfs_root;
+EXPORT_SYMBOL_GPL(sound_debugfs_root);
+#endif
+
static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
static DEFINE_MUTEX(sound_mutex);
@@ -76,7 +65,6 @@ void snd_request_card(int card)
return;
request_module("snd-card-%i", card);
}
-
EXPORT_SYMBOL(snd_request_card);
static void snd_request_other(int minor)
@@ -100,6 +88,13 @@ static void snd_request_other(int minor)
*
* Checks that a minor device with the specified type is registered, and returns
* its user data pointer.
+ *
+ * This function increments the reference counter of the card instance
+ * if an associated instance with the given minor number and type is found.
+ * The caller must call snd_card_unref() appropriately later.
+ *
+ * Return: The user data pointer if the specified device is found. %NULL
+ * otherwise.
*/
void *snd_lookup_minor_data(unsigned int minor, int type)
{
@@ -108,16 +103,16 @@ void *snd_lookup_minor_data(unsigned int minor, int type)
if (minor >= ARRAY_SIZE(snd_minors))
return NULL;
- mutex_lock(&sound_mutex);
+ guard(mutex)(&sound_mutex);
mreg = snd_minors[minor];
- if (mreg && mreg->type == type)
+ if (mreg && mreg->type == type) {
private_data = mreg->private_data;
- else
+ if (private_data && mreg->card_ptr)
+ get_device(&mreg->card_ptr->card_dev);
+ } else
private_data = NULL;
- mutex_unlock(&sound_mutex);
return private_data;
}
-
EXPORT_SYMBOL(snd_lookup_minor_data);
#ifdef CONFIG_MODULES
@@ -129,13 +124,16 @@ static struct snd_minor *autoload_device(unsigned int minor)
if (dev == SNDRV_MINOR_CONTROL) {
/* /dev/aloadC? */
int card = SNDRV_MINOR_CARD(minor);
- if (snd_cards[card] == NULL)
+ struct snd_card *ref = snd_card_ref(card);
+ if (!ref)
snd_request_card(card);
+ else
+ snd_card_unref(ref);
} else if (dev == SNDRV_MINOR_GLOBAL) {
/* /dev/aloadSEQ */
snd_request_other(minor);
}
- mutex_lock(&sound_mutex); /* reacuire lock */
+ mutex_lock(&sound_mutex); /* reacquire lock */
return snd_minors[minor];
}
#else /* !CONFIG_MODULES */
@@ -146,38 +144,26 @@ static int snd_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct snd_minor *mptr = NULL;
- const struct file_operations *old_fops;
+ const struct file_operations *new_fops;
int err = 0;
if (minor >= ARRAY_SIZE(snd_minors))
return -ENODEV;
- mutex_lock(&sound_mutex);
- mptr = snd_minors[minor];
- if (mptr == NULL) {
- mptr = autoload_device(minor);
- if (!mptr) {
- mutex_unlock(&sound_mutex);
- return -ENODEV;
+ scoped_guard(mutex, &sound_mutex) {
+ mptr = snd_minors[minor];
+ if (mptr == NULL) {
+ mptr = autoload_device(minor);
+ if (!mptr)
+ return -ENODEV;
}
+ new_fops = fops_get(mptr->f_ops);
}
- old_fops = file->f_op;
- file->f_op = fops_get(mptr->f_ops);
- if (file->f_op == NULL) {
- file->f_op = old_fops;
- err = -ENODEV;
- }
- mutex_unlock(&sound_mutex);
- if (err < 0)
- return err;
+ if (!new_fops)
+ return -ENODEV;
+ replace_fops(file, new_fops);
- if (file->f_op->open) {
+ if (file->f_op->open)
err = file->f_op->open(inode, file);
- if (err) {
- fops_put(file->f_op);
- file->f_op = fops_get(old_fops);
- }
- }
- fops_put(old_fops);
return err;
}
@@ -189,14 +175,22 @@ static const struct file_operations snd_fops =
};
#ifdef CONFIG_SND_DYNAMIC_MINORS
-static int snd_find_free_minor(void)
+static int snd_find_free_minor(int type, struct snd_card *card, int dev)
{
int minor;
+ /* static minors for module auto loading */
+ if (type == SNDRV_DEVICE_TYPE_SEQUENCER)
+ return SNDRV_MINOR_SEQUENCER;
+ if (type == SNDRV_DEVICE_TYPE_TIMER)
+ return SNDRV_MINOR_TIMER;
+
for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) {
- /* skip minors still used statically for autoloading devices */
- if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL ||
- minor == SNDRV_MINOR_SEQUENCER)
+ /* skip static minors still used for module auto loading */
+ if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL)
+ continue;
+ if (minor == SNDRV_MINOR_SEQUENCER ||
+ minor == SNDRV_MINOR_TIMER)
continue;
if (!snd_minors[minor])
return minor;
@@ -204,7 +198,7 @@ static int snd_find_free_minor(void)
return -EBUSY;
}
#else
-static int snd_kernel_minor(int type, struct snd_card *card, int dev)
+static int snd_find_free_minor(int type, struct snd_card *card, int dev)
{
int minor;
@@ -222,6 +216,7 @@ static int snd_kernel_minor(int type, struct snd_card *card, int dev)
case SNDRV_DEVICE_TYPE_RAWMIDI:
case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:
case SNDRV_DEVICE_TYPE_PCM_CAPTURE:
+ case SNDRV_DEVICE_TYPE_COMPRESS:
if (snd_BUG_ON(!card))
return -EINVAL;
minor = SNDRV_MINOR(card->number, type + dev);
@@ -231,35 +226,37 @@ static int snd_kernel_minor(int type, struct snd_card *card, int dev)
}
if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS))
return -EINVAL;
+ if (snd_minors[minor])
+ return -EBUSY;
return minor;
}
#endif
/**
- * snd_register_device_for_dev - Register the ALSA device file for the card
+ * snd_register_device - Register the ALSA device file for the card
* @type: the device type, SNDRV_DEVICE_TYPE_XXX
* @card: the card instance
* @dev: the device index
* @f_ops: the file operations
* @private_data: user pointer for f_ops->open()
- * @name: the device file name
- * @device: the &struct device to link this new device to
+ * @device: the device to register
*
* Registers an ALSA device file for the given card.
* The operators have to be set in reg parameter.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
-int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
- const struct file_operations *f_ops,
- void *private_data,
- const char *name, struct device *device)
+int snd_register_device(int type, struct snd_card *card, int dev,
+ const struct file_operations *f_ops,
+ void *private_data, struct device *device)
{
int minor;
+ int err = 0;
struct snd_minor *preg;
- if (snd_BUG_ON(!name))
+ if (snd_BUG_ON(!device))
return -EINVAL;
+
preg = kmalloc(sizeof *preg, GFP_KERNEL);
if (preg == NULL)
return -ENOMEM;
@@ -268,110 +265,62 @@ int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
preg->device = dev;
preg->f_ops = f_ops;
preg->private_data = private_data;
- mutex_lock(&sound_mutex);
-#ifdef CONFIG_SND_DYNAMIC_MINORS
- minor = snd_find_free_minor();
-#else
- minor = snd_kernel_minor(type, card, dev);
- if (minor >= 0 && snd_minors[minor])
- minor = -EBUSY;
-#endif
+ preg->card_ptr = card;
+ guard(mutex)(&sound_mutex);
+ minor = snd_find_free_minor(type, card, dev);
if (minor < 0) {
- mutex_unlock(&sound_mutex);
- kfree(preg);
- return minor;
- }
- snd_minors[minor] = preg;
- preg->dev = device_create(sound_class, device, MKDEV(major, minor),
- private_data, "%s", name);
- if (IS_ERR(preg->dev)) {
- snd_minors[minor] = NULL;
- mutex_unlock(&sound_mutex);
- minor = PTR_ERR(preg->dev);
- kfree(preg);
- return minor;
+ err = minor;
+ goto error;
}
- mutex_unlock(&sound_mutex);
- return 0;
-}
-
-EXPORT_SYMBOL(snd_register_device_for_dev);
-
-/* find the matching minor record
- * return the index of snd_minor, or -1 if not found
- */
-static int find_snd_minor(int type, struct snd_card *card, int dev)
-{
- int cardnum, minor;
- struct snd_minor *mptr;
+ preg->dev = device;
+ device->devt = MKDEV(major, minor);
+ err = device_add(device);
+ if (err < 0)
+ goto error;
- cardnum = card ? card->number : -1;
- for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor)
- if ((mptr = snd_minors[minor]) != NULL &&
- mptr->type == type &&
- mptr->card == cardnum &&
- mptr->device == dev)
- return minor;
- return -1;
+ snd_minors[minor] = preg;
+ error:
+ if (err < 0)
+ kfree(preg);
+ return err;
}
+EXPORT_SYMBOL(snd_register_device);
/**
* snd_unregister_device - unregister the device on the given card
- * @type: the device type, SNDRV_DEVICE_TYPE_XXX
- * @card: the card instance
- * @dev: the device index
+ * @dev: the device instance
*
* Unregisters the device file already registered via
* snd_register_device().
*
- * Returns zero if sucecessful, or a negative error code on failure
+ * Return: Zero if successful, or a negative error code on failure.
*/
-int snd_unregister_device(int type, struct snd_card *card, int dev)
+int snd_unregister_device(struct device *dev)
{
int minor;
+ struct snd_minor *preg;
- mutex_lock(&sound_mutex);
- minor = find_snd_minor(type, card, dev);
- if (minor < 0) {
- mutex_unlock(&sound_mutex);
- return -EINVAL;
+ guard(mutex)(&sound_mutex);
+ for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) {
+ preg = snd_minors[minor];
+ if (preg && preg->dev == dev) {
+ snd_minors[minor] = NULL;
+ device_del(dev);
+ kfree(preg);
+ break;
+ }
}
-
- device_destroy(sound_class, MKDEV(major, minor));
-
- kfree(snd_minors[minor]);
- snd_minors[minor] = NULL;
- mutex_unlock(&sound_mutex);
+ if (minor >= ARRAY_SIZE(snd_minors))
+ return -ENOENT;
return 0;
}
-
EXPORT_SYMBOL(snd_unregister_device);
-int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev,
- struct device_attribute *attr)
-{
- int minor, ret = -EINVAL;
- struct device *d;
-
- mutex_lock(&sound_mutex);
- minor = find_snd_minor(type, card, dev);
- if (minor >= 0 && (d = snd_minors[minor]->dev) != NULL)
- ret = device_create_file(d, attr);
- mutex_unlock(&sound_mutex);
- return ret;
-
-}
-
-EXPORT_SYMBOL(snd_add_device_sysfs_file);
-
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* INFO PART
*/
-
-static struct snd_info_entry *snd_minor_info_entry;
-
static const char *snd_device_type_name(int type)
{
switch (type) {
@@ -389,6 +338,8 @@ static const char *snd_device_type_name(int type)
return "sequencer";
case SNDRV_DEVICE_TYPE_TIMER:
return "timer";
+ case SNDRV_DEVICE_TYPE_COMPRESS:
+ return "compress";
default:
return "?";
}
@@ -399,9 +350,10 @@ static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_bu
int minor;
struct snd_minor *mptr;
- mutex_lock(&sound_mutex);
+ guard(mutex)(&sound_mutex);
for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) {
- if (!(mptr = snd_minors[minor]))
+ mptr = snd_minors[minor];
+ if (!mptr)
continue;
if (mptr->card >= 0) {
if (mptr->device >= 0)
@@ -416,7 +368,6 @@ static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_bu
snd_iprintf(buffer, "%3i: : %s\n", minor,
snd_device_type_name(mptr->type));
}
- mutex_unlock(&sound_mutex);
}
int __init snd_minor_info_init(void)
@@ -424,23 +375,12 @@ int __init snd_minor_info_init(void)
struct snd_info_entry *entry;
entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL);
- if (entry) {
- entry->c.text.read = snd_minor_info_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- snd_minor_info_entry = entry;
- return 0;
-}
-
-int __exit snd_minor_info_done(void)
-{
- snd_info_free_entry(snd_minor_info_entry);
- return 0;
+ if (!entry)
+ return -ENOMEM;
+ entry->c.text.read = snd_minor_info_read;
+ return snd_info_register(entry); /* freed in error path */
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
/*
* INIT PART
@@ -451,23 +391,28 @@ static int __init alsa_sound_init(void)
snd_major = major;
snd_ecards_limit = cards_limit;
if (register_chrdev(major, "alsa", &snd_fops)) {
- snd_printk(KERN_ERR "unable to register native major device number %d\n", major);
+ pr_err("ALSA core: unable to register native major device number %d\n", major);
return -EIO;
}
if (snd_info_init() < 0) {
unregister_chrdev(major, "alsa");
return -ENOMEM;
}
- snd_info_minor_register();
+
+#ifdef CONFIG_SND_DEBUG
+ sound_debugfs_root = debugfs_create_dir("sound", NULL);
+#endif
#ifndef MODULE
- printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n");
+ pr_info("Advanced Linux Sound Architecture Driver Initialized.\n");
#endif
return 0;
}
static void __exit alsa_sound_exit(void)
{
- snd_info_minor_unregister();
+#ifdef CONFIG_SND_DEBUG
+ debugfs_remove(sound_debugfs_root);
+#endif
snd_info_done();
unregister_chrdev(major, "alsa");
}
diff --git a/sound/core/sound_kunit.c b/sound/core/sound_kunit.c
new file mode 100644
index 000000000000..84e337ecbddd
--- /dev/null
+++ b/sound/core/sound_kunit.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Sound core KUnit test
+ * Author: Ivan Orlov <ivan.orlov0322@gmail.com>
+ */
+
+#include <kunit/test.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#define SILENCE_BUFFER_MAX_FRAMES 260
+#define SILENCE_BUFFER_SIZE (sizeof(u64) * SILENCE_BUFFER_MAX_FRAMES)
+#define SILENCE(...) { __VA_ARGS__ }
+#define DEFINE_FORMAT(fmt, pbits, wd, endianness, signd, silence_arr) { \
+ .format = SNDRV_PCM_FORMAT_##fmt, .physical_bits = pbits, \
+ .width = wd, .le = endianness, .sd = signd, .silence = silence_arr, \
+ .name = #fmt, \
+}
+
+#define WRONG_FORMAT_1 (__force snd_pcm_format_t)((__force int)SNDRV_PCM_FORMAT_LAST + 1)
+#define WRONG_FORMAT_2 (__force snd_pcm_format_t)-1
+
+#define VALID_NAME "ValidName"
+#define NAME_W_SPEC_CHARS "In%v@1id name"
+#define NAME_W_SPACE "Test name"
+#define NAME_W_SPACE_REMOVED "Testname"
+
+#define TEST_FIRST_COMPONENT "Component1"
+#define TEST_SECOND_COMPONENT "Component2"
+
+struct snd_format_test_data {
+ snd_pcm_format_t format;
+ int physical_bits;
+ int width;
+ int le;
+ int sd;
+ unsigned char silence[8];
+ unsigned char *name;
+};
+
+struct avail_test_data {
+ snd_pcm_uframes_t buffer_size;
+ snd_pcm_uframes_t hw_ptr;
+ snd_pcm_uframes_t appl_ptr;
+ snd_pcm_uframes_t expected_avail;
+};
+
+static const struct snd_format_test_data valid_fmt[] = {
+ DEFINE_FORMAT(S8, 8, 8, -1, 1, SILENCE()),
+ DEFINE_FORMAT(U8, 8, 8, -1, 0, SILENCE(0x80)),
+ DEFINE_FORMAT(S16_LE, 16, 16, 1, 1, SILENCE()),
+ DEFINE_FORMAT(S16_BE, 16, 16, 0, 1, SILENCE()),
+ DEFINE_FORMAT(U16_LE, 16, 16, 1, 0, SILENCE(0x00, 0x80)),
+ DEFINE_FORMAT(U16_BE, 16, 16, 0, 0, SILENCE(0x80, 0x00)),
+ DEFINE_FORMAT(S24_LE, 32, 24, 1, 1, SILENCE()),
+ DEFINE_FORMAT(S24_BE, 32, 24, 0, 1, SILENCE()),
+ DEFINE_FORMAT(U24_LE, 32, 24, 1, 0, SILENCE(0x00, 0x00, 0x80)),
+ DEFINE_FORMAT(U24_BE, 32, 24, 0, 0, SILENCE(0x00, 0x80, 0x00, 0x00)),
+ DEFINE_FORMAT(S32_LE, 32, 32, 1, 1, SILENCE()),
+ DEFINE_FORMAT(S32_BE, 32, 32, 0, 1, SILENCE()),
+ DEFINE_FORMAT(U32_LE, 32, 32, 1, 0, SILENCE(0x00, 0x00, 0x00, 0x80)),
+ DEFINE_FORMAT(U32_BE, 32, 32, 0, 0, SILENCE(0x80, 0x00, 0x00, 0x00)),
+ DEFINE_FORMAT(FLOAT_LE, 32, 32, 1, -1, SILENCE()),
+ DEFINE_FORMAT(FLOAT_BE, 32, 32, 0, -1, SILENCE()),
+ DEFINE_FORMAT(FLOAT64_LE, 64, 64, 1, -1, SILENCE()),
+ DEFINE_FORMAT(FLOAT64_BE, 64, 64, 0, -1, SILENCE()),
+ DEFINE_FORMAT(IEC958_SUBFRAME_LE, 32, 32, 1, -1, SILENCE()),
+ DEFINE_FORMAT(IEC958_SUBFRAME_BE, 32, 32, 0, -1, SILENCE()),
+ DEFINE_FORMAT(MU_LAW, 8, 8, -1, -1, SILENCE(0x7f)),
+ DEFINE_FORMAT(A_LAW, 8, 8, -1, -1, SILENCE(0x55)),
+ DEFINE_FORMAT(IMA_ADPCM, 4, 4, -1, -1, SILENCE()),
+ DEFINE_FORMAT(G723_24, 3, 3, -1, -1, SILENCE()),
+ DEFINE_FORMAT(G723_40, 5, 5, -1, -1, SILENCE()),
+ DEFINE_FORMAT(DSD_U8, 8, 8, 1, 0, SILENCE(0x69)),
+ DEFINE_FORMAT(DSD_U16_LE, 16, 16, 1, 0, SILENCE(0x69, 0x69)),
+ DEFINE_FORMAT(DSD_U32_LE, 32, 32, 1, 0, SILENCE(0x69, 0x69, 0x69, 0x69)),
+ DEFINE_FORMAT(DSD_U16_BE, 16, 16, 0, 0, SILENCE(0x69, 0x69)),
+ DEFINE_FORMAT(DSD_U32_BE, 32, 32, 0, 0, SILENCE(0x69, 0x69, 0x69, 0x69)),
+ DEFINE_FORMAT(S20_LE, 32, 20, 1, 1, SILENCE()),
+ DEFINE_FORMAT(S20_BE, 32, 20, 0, 1, SILENCE()),
+ DEFINE_FORMAT(U20_LE, 32, 20, 1, 0, SILENCE(0x00, 0x00, 0x08, 0x00)),
+ DEFINE_FORMAT(U20_BE, 32, 20, 0, 0, SILENCE(0x00, 0x08, 0x00, 0x00)),
+ DEFINE_FORMAT(S24_3LE, 24, 24, 1, 1, SILENCE()),
+ DEFINE_FORMAT(S24_3BE, 24, 24, 0, 1, SILENCE()),
+ DEFINE_FORMAT(U24_3LE, 24, 24, 1, 0, SILENCE(0x00, 0x00, 0x80)),
+ DEFINE_FORMAT(U24_3BE, 24, 24, 0, 0, SILENCE(0x80, 0x00, 0x00)),
+ DEFINE_FORMAT(S20_3LE, 24, 20, 1, 1, SILENCE()),
+ DEFINE_FORMAT(S20_3BE, 24, 20, 0, 1, SILENCE()),
+ DEFINE_FORMAT(U20_3LE, 24, 20, 1, 0, SILENCE(0x00, 0x00, 0x08)),
+ DEFINE_FORMAT(U20_3BE, 24, 20, 0, 0, SILENCE(0x08, 0x00, 0x00)),
+ DEFINE_FORMAT(S18_3LE, 24, 18, 1, 1, SILENCE()),
+ DEFINE_FORMAT(S18_3BE, 24, 18, 0, 1, SILENCE()),
+ DEFINE_FORMAT(U18_3LE, 24, 18, 1, 0, SILENCE(0x00, 0x00, 0x02)),
+ DEFINE_FORMAT(U18_3BE, 24, 18, 0, 0, SILENCE(0x02, 0x00, 0x00)),
+ DEFINE_FORMAT(G723_24_1B, 8, 3, -1, -1, SILENCE()),
+ DEFINE_FORMAT(G723_40_1B, 8, 5, -1, -1, SILENCE()),
+};
+
+static void test_phys_format_size(struct kunit *test)
+{
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) {
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_physical_width(valid_fmt[i].format),
+ valid_fmt[i].physical_bits);
+ }
+
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_physical_width(WRONG_FORMAT_1), -EINVAL);
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_physical_width(WRONG_FORMAT_2), -EINVAL);
+}
+
+static void test_format_width(struct kunit *test)
+{
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) {
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_width(valid_fmt[i].format),
+ valid_fmt[i].width);
+ }
+
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_width(WRONG_FORMAT_1), -EINVAL);
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_width(WRONG_FORMAT_2), -EINVAL);
+}
+
+static void test_format_signed(struct kunit *test)
+{
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) {
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_signed(valid_fmt[i].format),
+ valid_fmt[i].sd < 0 ? -EINVAL : valid_fmt[i].sd);
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_unsigned(valid_fmt[i].format),
+ valid_fmt[i].sd < 0 ? -EINVAL : 1 - valid_fmt[i].sd);
+ }
+
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_width(WRONG_FORMAT_1), -EINVAL);
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_width(WRONG_FORMAT_2), -EINVAL);
+}
+
+static void test_format_endianness(struct kunit *test)
+{
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) {
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_little_endian(valid_fmt[i].format),
+ valid_fmt[i].le < 0 ? -EINVAL : valid_fmt[i].le);
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_big_endian(valid_fmt[i].format),
+ valid_fmt[i].le < 0 ? -EINVAL : 1 - valid_fmt[i].le);
+ }
+
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_little_endian(WRONG_FORMAT_1), -EINVAL);
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_little_endian(WRONG_FORMAT_2), -EINVAL);
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_big_endian(WRONG_FORMAT_1), -EINVAL);
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_big_endian(WRONG_FORMAT_2), -EINVAL);
+}
+
+static void _test_fill_silence(struct kunit *test, const struct snd_format_test_data *data,
+ u8 *buffer, size_t samples_count)
+{
+ size_t sample_bytes = data->physical_bits >> 3;
+ u32 i;
+
+ KUNIT_ASSERT_EQ(test, snd_pcm_format_set_silence(data->format, buffer, samples_count), 0);
+ for (i = 0; i < samples_count * sample_bytes; i++)
+ KUNIT_EXPECT_EQ(test, buffer[i], data->silence[i % sample_bytes]);
+}
+
+static void test_format_fill_silence(struct kunit *test)
+{
+ static const u32 buf_samples[] = { 10, 20, 32, 64, 129, SILENCE_BUFFER_MAX_FRAMES };
+ u8 *buffer;
+ u32 i, j;
+
+ buffer = kunit_kzalloc(test, SILENCE_BUFFER_SIZE, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buffer);
+
+ for (i = 0; i < ARRAY_SIZE(buf_samples); i++) {
+ for (j = 0; j < ARRAY_SIZE(valid_fmt); j++)
+ _test_fill_silence(test, &valid_fmt[j], buffer, buf_samples[i]);
+ }
+
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_set_silence(WRONG_FORMAT_1, buffer, 20), -EINVAL);
+ KUNIT_EXPECT_EQ(test, snd_pcm_format_set_silence(SNDRV_PCM_FORMAT_LAST, buffer, 0), 0);
+}
+
+static snd_pcm_uframes_t calculate_boundary(snd_pcm_uframes_t buffer_size)
+{
+ snd_pcm_uframes_t boundary = buffer_size;
+
+ while (boundary * 2 <= 0x7fffffffUL - buffer_size)
+ boundary *= 2;
+ return boundary;
+}
+
+static const struct avail_test_data p_avail_data[] = {
+ /* buf_size + hw_ptr < appl_ptr => avail = buf_size + hw_ptr - appl_ptr + boundary */
+ { 128, 1000, 1129, 1073741824UL - 1 },
+ /*
+ * buf_size + hw_ptr - appl_ptr >= boundary =>
+ * => avail = buf_size + hw_ptr - appl_ptr - boundary
+ */
+ { 128, 1073741824UL, 10, 118 },
+ /* standard case: avail = buf_size + hw_ptr - appl_ptr */
+ { 128, 1000, 1001, 127 },
+};
+
+static void test_playback_avail(struct kunit *test)
+{
+ struct snd_pcm_runtime *r = kunit_kzalloc(test, sizeof(*r), GFP_KERNEL);
+ u32 i;
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r);
+
+ r->status = kunit_kzalloc(test, sizeof(*r->status), GFP_KERNEL);
+ r->control = kunit_kzalloc(test, sizeof(*r->control), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->status);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->control);
+
+ for (i = 0; i < ARRAY_SIZE(p_avail_data); i++) {
+ r->buffer_size = p_avail_data[i].buffer_size;
+ r->boundary = calculate_boundary(r->buffer_size);
+ r->status->hw_ptr = p_avail_data[i].hw_ptr;
+ r->control->appl_ptr = p_avail_data[i].appl_ptr;
+ KUNIT_EXPECT_EQ(test, snd_pcm_playback_avail(r), p_avail_data[i].expected_avail);
+ }
+}
+
+static const struct avail_test_data c_avail_data[] = {
+ /* hw_ptr - appl_ptr < 0 => avail = hw_ptr - appl_ptr + boundary */
+ { 128, 1000, 1001, 1073741824UL - 1 },
+ /* standard case: avail = hw_ptr - appl_ptr */
+ { 128, 1001, 1000, 1 },
+};
+
+static void test_capture_avail(struct kunit *test)
+{
+ struct snd_pcm_runtime *r = kunit_kzalloc(test, sizeof(*r), GFP_KERNEL);
+ u32 i;
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r);
+
+ r->status = kunit_kzalloc(test, sizeof(*r->status), GFP_KERNEL);
+ r->control = kunit_kzalloc(test, sizeof(*r->control), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->status);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, r->control);
+
+ for (i = 0; i < ARRAY_SIZE(c_avail_data); i++) {
+ r->buffer_size = c_avail_data[i].buffer_size;
+ r->boundary = calculate_boundary(r->buffer_size);
+ r->status->hw_ptr = c_avail_data[i].hw_ptr;
+ r->control->appl_ptr = c_avail_data[i].appl_ptr;
+ KUNIT_EXPECT_EQ(test, snd_pcm_capture_avail(r), c_avail_data[i].expected_avail);
+ }
+}
+
+static void test_card_set_id(struct kunit *test)
+{
+ struct snd_card *card = kunit_kzalloc(test, sizeof(*card), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, card);
+
+ snd_card_set_id(card, VALID_NAME);
+ KUNIT_EXPECT_STREQ(test, card->id, VALID_NAME);
+
+ /* clear the first id character so we can set it again */
+ card->id[0] = '\0';
+ snd_card_set_id(card, NAME_W_SPEC_CHARS);
+ KUNIT_EXPECT_STRNEQ(test, card->id, NAME_W_SPEC_CHARS);
+
+ card->id[0] = '\0';
+ snd_card_set_id(card, NAME_W_SPACE);
+ kunit_info(test, "%s", card->id);
+ KUNIT_EXPECT_STREQ(test, card->id, NAME_W_SPACE_REMOVED);
+}
+
+static void test_pcm_format_name(struct kunit *test)
+{
+ u32 i;
+ const char *name;
+
+ for (i = 0; i < ARRAY_SIZE(valid_fmt); i++) {
+ name = snd_pcm_format_name(valid_fmt[i].format);
+ KUNIT_ASSERT_NOT_NULL_MSG(test, name, "Don't have name for %s", valid_fmt[i].name);
+ KUNIT_EXPECT_STREQ(test, name, valid_fmt[i].name);
+ }
+
+ KUNIT_ASSERT_STREQ(test, snd_pcm_format_name(WRONG_FORMAT_1), "Unknown");
+ KUNIT_ASSERT_STREQ(test, snd_pcm_format_name(WRONG_FORMAT_2), "Unknown");
+}
+
+static void test_card_add_component(struct kunit *test)
+{
+ struct snd_card *card = kunit_kzalloc(test, sizeof(*card), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, card);
+
+ snd_component_add(card, TEST_FIRST_COMPONENT);
+ KUNIT_ASSERT_STREQ(test, card->components, TEST_FIRST_COMPONENT);
+
+ snd_component_add(card, TEST_SECOND_COMPONENT);
+ KUNIT_ASSERT_STREQ(test, card->components, TEST_FIRST_COMPONENT " " TEST_SECOND_COMPONENT);
+}
+
+static struct kunit_case sound_utils_cases[] = {
+ KUNIT_CASE(test_phys_format_size),
+ KUNIT_CASE(test_format_width),
+ KUNIT_CASE(test_format_endianness),
+ KUNIT_CASE(test_format_signed),
+ KUNIT_CASE(test_format_fill_silence),
+ KUNIT_CASE(test_playback_avail),
+ KUNIT_CASE(test_capture_avail),
+ KUNIT_CASE(test_card_set_id),
+ KUNIT_CASE(test_pcm_format_name),
+ KUNIT_CASE(test_card_add_component),
+ {},
+};
+
+static struct kunit_suite sound_utils_suite = {
+ .name = "sound-core-test",
+ .test_cases = sound_utils_cases,
+};
+
+kunit_test_suite(sound_utils_suite);
+MODULE_DESCRIPTION("Sound core KUnit test");
+MODULE_AUTHOR("Ivan Orlov");
+MODULE_LICENSE("GPL");
diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c
index 0c164e5e4322..d65cc6fee2e6 100644
--- a/sound/core/sound_oss.c
+++ b/sound/core/sound_oss.c
@@ -1,31 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Advanced Linux Sound Architecture
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#ifdef CONFIG_SND_OSSEMUL
-
-#if !defined(CONFIG_SOUND) && !(defined(MODULE) && defined(CONFIG_SOUND_MODULE))
-#error "Enable the OSS soundcore multiplexer (CONFIG_SOUND) in the kernel."
-#endif
-
#include <linux/init.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <sound/core.h>
@@ -34,11 +14,14 @@
#include <linux/sound.h>
#include <linux/mutex.h>
-#define SNDRV_OSS_MINORS 128
+#define SNDRV_OSS_MINORS 256
static struct snd_minor *snd_oss_minors[SNDRV_OSS_MINORS];
static DEFINE_MUTEX(sound_oss_mutex);
+/* NOTE: This function increments the refcount of the associated card like
+ * snd_lookup_minor_data(); the caller must call snd_card_unref() appropriately
+ */
void *snd_lookup_oss_minor_data(unsigned int minor, int type)
{
struct snd_minor *mreg;
@@ -46,16 +29,16 @@ void *snd_lookup_oss_minor_data(unsigned int minor, int type)
if (minor >= ARRAY_SIZE(snd_oss_minors))
return NULL;
- mutex_lock(&sound_oss_mutex);
+ guard(mutex)(&sound_oss_mutex);
mreg = snd_oss_minors[minor];
- if (mreg && mreg->type == type)
+ if (mreg && mreg->type == type) {
private_data = mreg->private_data;
- else
+ if (private_data && mreg->card_ptr)
+ get_device(&mreg->card_ptr->card_dev);
+ } else
private_data = NULL;
- mutex_unlock(&sound_oss_mutex);
return private_data;
}
-
EXPORT_SYMBOL(snd_lookup_oss_minor_data);
static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)
@@ -99,8 +82,7 @@ static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)
}
int snd_register_oss_device(int type, struct snd_card *card, int dev,
- const struct file_operations *f_ops, void *private_data,
- const char *name)
+ const struct file_operations *f_ops, void *private_data)
{
int minor = snd_oss_kernel_minor(type, card, dev);
int minor_unit;
@@ -110,7 +92,7 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev,
int register1 = -1, register2 = -1;
struct device *carddev = snd_card_get_device_link(card);
- if (card && card->number >= 8)
+ if (card && card->number >= SNDRV_MINOR_OSS_DEVICES)
return 0; /* ignore silently */
if (minor < 0)
return minor;
@@ -122,7 +104,8 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev,
preg->device = dev;
preg->f_ops = f_ops;
preg->private_data = private_data;
- mutex_lock(&sound_oss_mutex);
+ preg->card_ptr = card;
+ guard(mutex)(&sound_oss_mutex);
snd_oss_minors[minor] = preg;
minor_unit = SNDRV_MINOR_OSS_DEVICE(minor);
switch (minor_unit) {
@@ -146,7 +129,6 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev,
goto __end;
snd_oss_minors[track2] = preg;
}
- mutex_unlock(&sound_oss_mutex);
return 0;
__end:
@@ -155,11 +137,9 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev,
if (register1 >= 0)
unregister_sound_special(register1);
snd_oss_minors[minor] = NULL;
- mutex_unlock(&sound_oss_mutex);
kfree(preg);
return -EBUSY;
}
-
EXPORT_SYMBOL(snd_register_oss_device);
int snd_unregister_oss_device(int type, struct snd_card *card, int dev)
@@ -169,17 +149,14 @@ int snd_unregister_oss_device(int type, struct snd_card *card, int dev)
int track2 = -1;
struct snd_minor *mptr;
- if (card && card->number >= 8)
+ if (card && card->number >= SNDRV_MINOR_OSS_DEVICES)
return 0;
if (minor < 0)
return minor;
- mutex_lock(&sound_oss_mutex);
+ guard(mutex)(&sound_oss_mutex);
mptr = snd_oss_minors[minor];
- if (mptr == NULL) {
- mutex_unlock(&sound_oss_mutex);
+ if (mptr == NULL)
return -ENOENT;
- }
- unregister_sound_special(minor);
switch (SNDRV_MINOR_OSS_DEVICE(minor)) {
case SNDRV_MINOR_OSS_PCM:
track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_AUDIO);
@@ -191,26 +168,27 @@ int snd_unregister_oss_device(int type, struct snd_card *card, int dev)
track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1);
break;
}
- if (track2 >= 0) {
- unregister_sound_special(track2);
+ if (track2 >= 0)
snd_oss_minors[track2] = NULL;
- }
snd_oss_minors[minor] = NULL;
- mutex_unlock(&sound_oss_mutex);
+
+ /* call unregister_sound_special() outside sound_oss_mutex;
+ * otherwise may deadlock, as it can trigger the release of a card
+ */
+ unregister_sound_special(minor);
+ if (track2 >= 0)
+ unregister_sound_special(track2);
+
kfree(mptr);
return 0;
}
-
EXPORT_SYMBOL(snd_unregister_oss_device);
/*
* INFO PART
*/
-#ifdef CONFIG_PROC_FS
-
-static struct snd_info_entry *snd_minor_info_oss_entry;
-
+#ifdef CONFIG_SND_PROC_FS
static const char *snd_oss_device_type_name(int type)
{
switch (type) {
@@ -236,9 +214,10 @@ static void snd_minor_info_oss_read(struct snd_info_entry *entry,
int minor;
struct snd_minor *mptr;
- mutex_lock(&sound_oss_mutex);
+ guard(mutex)(&sound_oss_mutex);
for (minor = 0; minor < SNDRV_OSS_MINORS; ++minor) {
- if (!(mptr = snd_oss_minors[minor]))
+ mptr = snd_oss_minors[minor];
+ if (!mptr)
continue;
if (mptr->card >= 0)
snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", minor,
@@ -248,7 +227,6 @@ static void snd_minor_info_oss_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "%3i: : %s\n", minor,
snd_oss_device_type_name(mptr->type));
}
- mutex_unlock(&sound_oss_mutex);
}
@@ -257,22 +235,9 @@ int __init snd_minor_info_oss_init(void)
struct snd_info_entry *entry;
entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root);
- if (entry) {
- entry->c.text.read = snd_minor_info_oss_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- snd_minor_info_oss_entry = entry;
- return 0;
-}
-
-int __exit snd_minor_info_oss_done(void)
-{
- snd_info_free_entry(snd_minor_info_oss_entry);
- return 0;
+ if (!entry)
+ return -ENOMEM;
+ entry->c.text.read = snd_minor_info_oss_read;
+ return snd_info_register(entry); /* freed in error path */
}
-#endif /* CONFIG_PROC_FS */
-
-#endif /* CONFIG_SND_OSSEMUL */
+#endif /* CONFIG_SND_PROC_FS */
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 13afb60999b9..d9fff5c87613 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Timers abstract layer
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
@@ -24,8 +9,12 @@
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/mutex.h>
-#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/module.h>
#include <linux/string.h>
+#include <linux/sched/signal.h>
+#include <linux/anon_inodes.h>
+#include <linux/idr.h>
#include <sound/core.h>
#include <sound/timer.h>
#include <sound/control.h>
@@ -34,10 +23,12 @@
#include <sound/initval.h>
#include <linux/kmod.h>
-#if defined(CONFIG_SND_HPET) || defined(CONFIG_SND_HPET_MODULE)
-#define DEFAULT_TIMER_LIMIT 3
-#elif defined(CONFIG_SND_RTCTIMER) || defined(CONFIG_SND_RTCTIMER_MODULE)
-#define DEFAULT_TIMER_LIMIT 2
+/* internal flags */
+#define SNDRV_TIMER_IFLG_PAUSED 0x00010000
+#define SNDRV_TIMER_IFLG_DEAD 0x00020000
+
+#if IS_ENABLED(CONFIG_SND_HRTIMER)
+#define DEFAULT_TIMER_LIMIT 4
#else
#define DEFAULT_TIMER_LIMIT 1
#endif
@@ -52,6 +43,31 @@ MODULE_PARM_DESC(timer_limit, "Maximum global timers in system.");
module_param(timer_tstamp_monotonic, int, 0444);
MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default).");
+MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_TIMER);
+MODULE_ALIAS("devname:snd/timer");
+
+enum timer_tread_format {
+ TREAD_FORMAT_NONE = 0,
+ TREAD_FORMAT_TIME64,
+ TREAD_FORMAT_TIME32,
+};
+
+struct snd_timer_tread32 {
+ int event;
+ s32 tstamp_sec;
+ s32 tstamp_nsec;
+ unsigned int val;
+};
+
+struct snd_timer_tread64 {
+ int event;
+ u8 pad1[4];
+ s64 tstamp_sec;
+ s64 tstamp_nsec;
+ unsigned int val;
+ u8 pad2[4];
+};
+
struct snd_timer_user {
struct snd_timer_instance *timeri;
int tread; /* enhanced read with timestamps and events */
@@ -61,16 +77,51 @@ struct snd_timer_user {
int qtail;
int qused;
int queue_size;
+ bool disconnected;
struct snd_timer_read *queue;
- struct snd_timer_tread *tqueue;
+ struct snd_timer_tread64 *tqueue;
spinlock_t qlock;
unsigned long last_resolution;
unsigned int filter;
- struct timespec tstamp; /* trigger tstamp */
+ struct timespec64 tstamp; /* trigger tstamp */
wait_queue_head_t qchange_sleep;
- struct fasync_struct *fasync;
- struct mutex tread_sem;
+ struct snd_fasync *fasync;
+ struct mutex ioctl_lock;
+};
+
+struct snd_timer_status32 {
+ s32 tstamp_sec; /* Timestamp - last update */
+ s32 tstamp_nsec;
+ unsigned int resolution; /* current period resolution in ns */
+ unsigned int lost; /* counter of master tick lost */
+ unsigned int overrun; /* count of read queue overruns */
+ unsigned int queue; /* used queue size */
+ unsigned char reserved[64]; /* reserved */
+};
+
+#define SNDRV_TIMER_IOCTL_STATUS32 _IOR('T', 0x14, struct snd_timer_status32)
+
+struct snd_timer_status64 {
+ s64 tstamp_sec; /* Timestamp - last update */
+ s64 tstamp_nsec;
+ unsigned int resolution; /* current period resolution in ns */
+ unsigned int lost; /* counter of master tick lost */
+ unsigned int overrun; /* count of read queue overruns */
+ unsigned int queue; /* used queue size */
+ unsigned char reserved[64]; /* reserved */
+};
+
+#ifdef CONFIG_SND_UTIMER
+#define SNDRV_UTIMERS_MAX_COUNT 128
+/* Internal data structure for keeping the state of the userspace-driven timer */
+struct snd_utimer {
+ char *name;
+ struct snd_timer *timer;
+ unsigned int id;
};
+#endif
+
+#define SNDRV_TIMER_IOCTL_STATUS64 _IOR('T', 0x14, struct snd_timer_status64)
/* list of timers */
static LIST_HEAD(snd_timer_list);
@@ -81,6 +132,9 @@ static LIST_HEAD(snd_timer_slave_list);
/* lock for slave active lists */
static DEFINE_SPINLOCK(slave_active_lock);
+#define MAX_SLAVE_INSTANCES 1000
+static int num_slaves;
+
static DEFINE_MUTEX(register_mutex);
static int snd_timer_free(struct snd_timer *timer);
@@ -92,12 +146,11 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l
/*
* create a timer instance with the given owner string.
- * when timer is not NULL, increments the module counter
*/
-static struct snd_timer_instance *snd_timer_instance_new(char *owner,
- struct snd_timer *timer)
+struct snd_timer_instance *snd_timer_instance_new(const char *owner)
{
struct snd_timer_instance *timeri;
+
timeri = kzalloc(sizeof(*timeri), GFP_KERNEL);
if (timeri == NULL)
return NULL;
@@ -112,22 +165,27 @@ static struct snd_timer_instance *snd_timer_instance_new(char *owner,
INIT_LIST_HEAD(&timeri->slave_list_head);
INIT_LIST_HEAD(&timeri->slave_active_head);
- timeri->timer = timer;
- if (timer && !try_module_get(timer->module)) {
+ return timeri;
+}
+EXPORT_SYMBOL(snd_timer_instance_new);
+
+void snd_timer_instance_free(struct snd_timer_instance *timeri)
+{
+ if (timeri) {
+ if (timeri->private_free)
+ timeri->private_free(timeri);
kfree(timeri->owner);
kfree(timeri);
- return NULL;
}
-
- return timeri;
}
+EXPORT_SYMBOL(snd_timer_instance_free);
/*
* find a timer instance from the given timer id
*/
static struct snd_timer *snd_timer_find(struct snd_timer_id *tid)
{
- struct snd_timer *timer = NULL;
+ struct snd_timer *timer;
list_for_each_entry(timer, &snd_timer_list, device_list) {
if (timer->tmr_class != tid->dev_class)
@@ -167,33 +225,48 @@ static void snd_timer_request(struct snd_timer_id *tid)
#endif
+/* move the slave if it belongs to the master; return 1 if match */
+static int check_matching_master_slave(struct snd_timer_instance *master,
+ struct snd_timer_instance *slave)
+{
+ if (slave->slave_class != master->slave_class ||
+ slave->slave_id != master->slave_id)
+ return 0;
+ if (master->timer->num_instances >= master->timer->max_instances)
+ return -EBUSY;
+ list_move_tail(&slave->open_list, &master->slave_list_head);
+ master->timer->num_instances++;
+ guard(spinlock_irq)(&slave_active_lock);
+ guard(spinlock)(&master->timer->lock);
+ slave->master = master;
+ slave->timer = master->timer;
+ if (slave->flags & SNDRV_TIMER_IFLG_RUNNING)
+ list_add_tail(&slave->active_list, &master->slave_active_head);
+ return 1;
+}
+
/*
* look for a master instance matching with the slave id of the given slave.
* when found, relink the open_link of the slave.
*
* call this with register_mutex down.
*/
-static void snd_timer_check_slave(struct snd_timer_instance *slave)
+static int snd_timer_check_slave(struct snd_timer_instance *slave)
{
struct snd_timer *timer;
struct snd_timer_instance *master;
+ int err = 0;
/* FIXME: it's really dumb to look up all entries.. */
list_for_each_entry(timer, &snd_timer_list, device_list) {
list_for_each_entry(master, &timer->open_list_head, open_list) {
- if (slave->slave_class == master->slave_class &&
- slave->slave_id == master->slave_id) {
- list_del(&slave->open_list);
- list_add_tail(&slave->open_list,
- &master->slave_list_head);
- spin_lock_irq(&slave_active_lock);
- slave->master = master;
- slave->timer = master->timer;
- spin_unlock_irq(&slave_active_lock);
- return;
- }
+ err = check_matching_master_slave(master, slave);
+ if (err != 0) /* match found or error */
+ goto out;
}
}
+ out:
+ return err < 0 ? err : 0;
}
/*
@@ -202,62 +275,59 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave)
*
* call this with register_mutex down.
*/
-static void snd_timer_check_master(struct snd_timer_instance *master)
+static int snd_timer_check_master(struct snd_timer_instance *master)
{
struct snd_timer_instance *slave, *tmp;
+ int err = 0;
/* check all pending slaves */
list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) {
- if (slave->slave_class == master->slave_class &&
- slave->slave_id == master->slave_id) {
- list_move_tail(&slave->open_list, &master->slave_list_head);
- spin_lock_irq(&slave_active_lock);
- slave->master = master;
- slave->timer = master->timer;
- if (slave->flags & SNDRV_TIMER_IFLG_RUNNING)
- list_add_tail(&slave->active_list,
- &master->slave_active_head);
- spin_unlock_irq(&slave_active_lock);
- }
+ err = check_matching_master_slave(master, slave);
+ if (err < 0)
+ break;
}
+ return err < 0 ? err : 0;
}
+static void snd_timer_close_locked(struct snd_timer_instance *timeri,
+ struct device **card_devp_to_put);
+
/*
* open a timer instance
* when opening a master, the slave id must be here given.
*/
-int snd_timer_open(struct snd_timer_instance **ti,
- char *owner, struct snd_timer_id *tid,
+int snd_timer_open(struct snd_timer_instance *timeri,
+ struct snd_timer_id *tid,
unsigned int slave_id)
{
struct snd_timer *timer;
- struct snd_timer_instance *timeri = NULL;
+ struct device *card_dev_to_put = NULL;
+ int err;
+ mutex_lock(&register_mutex);
if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) {
/* open a slave instance */
if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE ||
tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) {
- snd_printd("invalid slave class %i\n", tid->dev_sclass);
- return -EINVAL;
+ pr_debug("ALSA: timer: invalid slave class %i\n",
+ tid->dev_sclass);
+ err = -EINVAL;
+ goto unlock;
}
- mutex_lock(&register_mutex);
- timeri = snd_timer_instance_new(owner, NULL);
- if (!timeri) {
- mutex_unlock(&register_mutex);
- return -ENOMEM;
+ if (num_slaves >= MAX_SLAVE_INSTANCES) {
+ err = -EBUSY;
+ goto unlock;
}
timeri->slave_class = tid->dev_sclass;
timeri->slave_id = tid->device;
timeri->flags |= SNDRV_TIMER_IFLG_SLAVE;
list_add_tail(&timeri->open_list, &snd_timer_slave_list);
- snd_timer_check_slave(timeri);
- mutex_unlock(&register_mutex);
- *ti = timeri;
- return 0;
+ num_slaves++;
+ err = snd_timer_check_slave(timeri);
+ goto list_added;
}
/* open a master instance */
- mutex_lock(&register_mutex);
timer = snd_timer_find(tid);
#ifdef CONFIG_MODULES
if (!timer) {
@@ -268,64 +338,104 @@ int snd_timer_open(struct snd_timer_instance **ti,
}
#endif
if (!timer) {
- mutex_unlock(&register_mutex);
- return -ENODEV;
+ err = -ENODEV;
+ goto unlock;
}
if (!list_empty(&timer->open_list_head)) {
- timeri = list_entry(timer->open_list_head.next,
+ struct snd_timer_instance *t =
+ list_entry(timer->open_list_head.next,
struct snd_timer_instance, open_list);
- if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) {
- mutex_unlock(&register_mutex);
- return -EBUSY;
+ if (t->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) {
+ err = -EBUSY;
+ goto unlock;
}
}
- timeri = snd_timer_instance_new(owner, timer);
- if (!timeri) {
- mutex_unlock(&register_mutex);
- return -ENOMEM;
+ if (timer->num_instances >= timer->max_instances) {
+ err = -EBUSY;
+ goto unlock;
}
+ if (!try_module_get(timer->module)) {
+ err = -EBUSY;
+ goto unlock;
+ }
+ /* take a card refcount for safe disconnection */
+ if (timer->card) {
+ get_device(&timer->card->card_dev);
+ card_dev_to_put = &timer->card->card_dev;
+ }
+
+ if (list_empty(&timer->open_list_head) && timer->hw.open) {
+ err = timer->hw.open(timer);
+ if (err) {
+ module_put(timer->module);
+ goto unlock;
+ }
+ }
+
+ timeri->timer = timer;
timeri->slave_class = tid->dev_sclass;
timeri->slave_id = slave_id;
- if (list_empty(&timer->open_list_head) && timer->hw.open)
- timer->hw.open(timer);
+
list_add_tail(&timeri->open_list, &timer->open_list_head);
- snd_timer_check_master(timeri);
+ timer->num_instances++;
+ err = snd_timer_check_master(timeri);
+list_added:
+ if (err < 0)
+ snd_timer_close_locked(timeri, &card_dev_to_put);
+
+ unlock:
mutex_unlock(&register_mutex);
- *ti = timeri;
- return 0;
+ /* put_device() is called after unlock for avoiding deadlock */
+ if (err < 0 && card_dev_to_put)
+ put_device(card_dev_to_put);
+ return err;
}
+EXPORT_SYMBOL(snd_timer_open);
-static int _snd_timer_stop(struct snd_timer_instance *timeri,
- int keep_flag, int event);
+/* remove slave links, called from snd_timer_close_locked() below */
+static void remove_slave_links(struct snd_timer_instance *timeri,
+ struct snd_timer *timer)
+{
+ struct snd_timer_instance *slave, *tmp;
+
+ guard(spinlock_irq)(&slave_active_lock);
+ guard(spinlock)(&timer->lock);
+ timeri->timer = NULL;
+ list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head, open_list) {
+ list_move_tail(&slave->open_list, &snd_timer_slave_list);
+ timer->num_instances--;
+ slave->master = NULL;
+ slave->timer = NULL;
+ list_del_init(&slave->ack_list);
+ list_del_init(&slave->active_list);
+ }
+}
/*
* close a timer instance
+ * call this with register_mutex down.
*/
-int snd_timer_close(struct snd_timer_instance *timeri)
+static void snd_timer_close_locked(struct snd_timer_instance *timeri,
+ struct device **card_devp_to_put)
{
- struct snd_timer *timer = NULL;
- struct snd_timer_instance *slave, *tmp;
+ struct snd_timer *timer = timeri->timer;
- if (snd_BUG_ON(!timeri))
- return -ENXIO;
+ if (timer) {
+ guard(spinlock_irq)(&timer->lock);
+ timeri->flags |= SNDRV_TIMER_IFLG_DEAD;
+ }
+
+ if (!list_empty(&timeri->open_list)) {
+ list_del_init(&timeri->open_list);
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ num_slaves--;
+ }
/* force to stop the timer */
snd_timer_stop(timeri);
- if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
- /* wait, until the active callback is finished */
- spin_lock_irq(&slave_active_lock);
- while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
- spin_unlock_irq(&slave_active_lock);
- udelay(10);
- spin_lock_irq(&slave_active_lock);
- }
- spin_unlock_irq(&slave_active_lock);
- mutex_lock(&register_mutex);
- list_del(&timeri->open_list);
- mutex_unlock(&register_mutex);
- } else {
- timer = timeri->timer;
+ if (timer) {
+ timer->num_instances--;
/* wait, until the active callback is finished */
spin_lock_irq(&timer->lock);
while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
@@ -334,165 +444,193 @@ int snd_timer_close(struct snd_timer_instance *timeri)
spin_lock_irq(&timer->lock);
}
spin_unlock_irq(&timer->lock);
- mutex_lock(&register_mutex);
- list_del(&timeri->open_list);
- if (timer && list_empty(&timer->open_list_head) &&
- timer->hw.close)
- timer->hw.close(timer);
- /* remove slave links */
- list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
- open_list) {
- spin_lock_irq(&slave_active_lock);
- _snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION);
- list_move_tail(&slave->open_list, &snd_timer_slave_list);
- slave->master = NULL;
- slave->timer = NULL;
- spin_unlock_irq(&slave_active_lock);
- }
- mutex_unlock(&register_mutex);
+
+ remove_slave_links(timeri, timer);
+
+ /* slave doesn't need to release timer resources below */
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ timer = NULL;
}
- if (timeri->private_free)
- timeri->private_free(timeri);
- kfree(timeri->owner);
- kfree(timeri);
- if (timer)
+
+ if (timer) {
+ if (list_empty(&timer->open_list_head) && timer->hw.close)
+ timer->hw.close(timer);
+ /* release a card refcount for safe disconnection */
+ if (timer->card)
+ *card_devp_to_put = &timer->card->card_dev;
module_put(timer->module);
- return 0;
+ }
+}
+
+/*
+ * close a timer instance
+ */
+void snd_timer_close(struct snd_timer_instance *timeri)
+{
+ struct device *card_dev_to_put = NULL;
+
+ if (snd_BUG_ON(!timeri))
+ return;
+
+ scoped_guard(mutex, &register_mutex)
+ snd_timer_close_locked(timeri, &card_dev_to_put);
+ /* put_device() is called after unlock for avoiding deadlock */
+ if (card_dev_to_put)
+ put_device(card_dev_to_put);
+}
+EXPORT_SYMBOL(snd_timer_close);
+
+static unsigned long snd_timer_hw_resolution(struct snd_timer *timer)
+{
+ if (timer->hw.c_resolution)
+ return timer->hw.c_resolution(timer);
+ else
+ return timer->hw.resolution;
}
unsigned long snd_timer_resolution(struct snd_timer_instance *timeri)
{
struct snd_timer * timer;
+ unsigned long ret = 0;
if (timeri == NULL)
return 0;
- if ((timer = timeri->timer) != NULL) {
- if (timer->hw.c_resolution)
- return timer->hw.c_resolution(timer);
- return timer->hw.resolution;
+ timer = timeri->timer;
+ if (timer) {
+ guard(spinlock_irqsave)(&timer->lock);
+ ret = snd_timer_hw_resolution(timer);
}
- return 0;
+ return ret;
}
+EXPORT_SYMBOL(snd_timer_resolution);
static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
{
- struct snd_timer *timer;
- unsigned long flags;
+ struct snd_timer *timer = ti->timer;
unsigned long resolution = 0;
struct snd_timer_instance *ts;
- struct timespec tstamp;
+ struct timespec64 tstamp;
if (timer_tstamp_monotonic)
- do_posix_clock_monotonic_gettime(&tstamp);
+ ktime_get_ts64(&tstamp);
else
- getnstimeofday(&tstamp);
+ ktime_get_real_ts64(&tstamp);
if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START ||
event > SNDRV_TIMER_EVENT_PAUSE))
return;
- if (event == SNDRV_TIMER_EVENT_START ||
- event == SNDRV_TIMER_EVENT_CONTINUE)
- resolution = snd_timer_resolution(ti);
+ if (timer &&
+ (event == SNDRV_TIMER_EVENT_START ||
+ event == SNDRV_TIMER_EVENT_CONTINUE))
+ resolution = snd_timer_hw_resolution(timer);
if (ti->ccallback)
ti->ccallback(ti, event, &tstamp, resolution);
if (ti->flags & SNDRV_TIMER_IFLG_SLAVE)
return;
- timer = ti->timer;
if (timer == NULL)
return;
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
return;
- spin_lock_irqsave(&timer->lock, flags);
+ event += 10; /* convert to SNDRV_TIMER_EVENT_MXXX */
list_for_each_entry(ts, &ti->slave_active_head, active_list)
if (ts->ccallback)
- ts->ccallback(ti, event + 100, &tstamp, resolution);
- spin_unlock_irqrestore(&timer->lock, flags);
+ ts->ccallback(ts, event, &tstamp, resolution);
}
-static int snd_timer_start1(struct snd_timer *timer, struct snd_timer_instance *timeri,
- unsigned long sticks)
+/* start/continue a master timer */
+static int snd_timer_start1(struct snd_timer_instance *timeri,
+ bool start, unsigned long ticks)
{
- list_del(&timeri->active_list);
- list_add_tail(&timeri->active_list, &timer->active_list_head);
+ struct snd_timer *timer;
+ int result;
+
+ timer = timeri->timer;
+ if (!timer)
+ return -EINVAL;
+
+ guard(spinlock_irqsave)(&timer->lock);
+ if (timeri->flags & SNDRV_TIMER_IFLG_DEAD)
+ return -EINVAL;
+ if (timer->card && timer->card->shutdown)
+ return -ENODEV;
+ if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
+ SNDRV_TIMER_IFLG_START))
+ return -EBUSY;
+
+ /* check the actual time for the start tick;
+ * bail out as error if it's way too low (< 100us)
+ */
+ if (start && !(timer->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
+ if ((u64)snd_timer_hw_resolution(timer) * ticks < 100000)
+ return -EINVAL;
+ }
+
+ if (start)
+ timeri->ticks = timeri->cticks = ticks;
+ else if (!timeri->cticks)
+ timeri->cticks = 1;
+ timeri->pticks = 0;
+
+ list_move_tail(&timeri->active_list, &timer->active_list_head);
if (timer->running) {
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
goto __start_now;
timer->flags |= SNDRV_TIMER_FLG_RESCHED;
timeri->flags |= SNDRV_TIMER_IFLG_START;
- return 1; /* delayed start */
+ result = 1; /* delayed start */
} else {
- timer->sticks = sticks;
+ if (start)
+ timer->sticks = ticks;
timer->hw.start(timer);
__start_now:
timer->running++;
timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
- return 0;
+ result = 0;
}
+ snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
+ SNDRV_TIMER_EVENT_CONTINUE);
+ return result;
}
-static int snd_timer_start_slave(struct snd_timer_instance *timeri)
+/* start/continue a slave timer */
+static int snd_timer_start_slave(struct snd_timer_instance *timeri,
+ bool start)
{
- unsigned long flags;
-
- spin_lock_irqsave(&slave_active_lock, flags);
+ guard(spinlock_irqsave)(&slave_active_lock);
+ if (timeri->flags & SNDRV_TIMER_IFLG_DEAD)
+ return -EINVAL;
+ if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING)
+ return -EBUSY;
timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
- if (timeri->master)
+ if (timeri->master && timeri->timer) {
+ guard(spinlock)(&timeri->timer->lock);
list_add_tail(&timeri->active_list,
&timeri->master->slave_active_head);
- spin_unlock_irqrestore(&slave_active_lock, flags);
- return 1; /* delayed start */
-}
-
-/*
- * start the timer instance
- */
-int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
-{
- struct snd_timer *timer;
- int result = -EINVAL;
- unsigned long flags;
-
- if (timeri == NULL || ticks < 1)
- return -EINVAL;
- if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
- result = snd_timer_start_slave(timeri);
- snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
- return result;
+ snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
+ SNDRV_TIMER_EVENT_CONTINUE);
}
- timer = timeri->timer;
- if (timer == NULL)
- return -EINVAL;
- spin_lock_irqsave(&timer->lock, flags);
- timeri->ticks = timeri->cticks = ticks;
- timeri->pticks = 0;
- result = snd_timer_start1(timer, timeri, ticks);
- spin_unlock_irqrestore(&timer->lock, flags);
- snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
- return result;
+ return 1; /* delayed start */
}
-static int _snd_timer_stop(struct snd_timer_instance * timeri,
- int keep_flag, int event)
+/* stop/pause a master timer */
+static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
{
struct snd_timer *timer;
- unsigned long flags;
-
- if (snd_BUG_ON(!timeri))
- return -ENXIO;
- if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
- if (!keep_flag) {
- spin_lock_irqsave(&slave_active_lock, flags);
- timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
- spin_unlock_irqrestore(&slave_active_lock, flags);
- }
- goto __end;
- }
timer = timeri->timer;
if (!timer)
return -EINVAL;
- spin_lock_irqsave(&timer->lock, flags);
+ guard(spinlock_irqsave)(&timer->lock);
list_del_init(&timeri->ack_list);
list_del_init(&timeri->active_list);
+ if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
+ SNDRV_TIMER_IFLG_START)))
+ return -EBUSY;
+ if (timer->card && timer->card->shutdown)
+ return 0;
+ if (stop) {
+ timeri->cticks = timeri->ticks;
+ timeri->pticks = 0;
+ }
if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
!(--timer->running)) {
timer->hw.stop(timer);
@@ -505,16 +643,49 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri,
}
}
}
- if (!keep_flag)
- timeri->flags &=
- ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
- spin_unlock_irqrestore(&timer->lock, flags);
- __end:
- if (event != SNDRV_TIMER_EVENT_RESOLUTION)
- snd_timer_notify1(timeri, event);
+ timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
+ if (stop)
+ timeri->flags &= ~SNDRV_TIMER_IFLG_PAUSED;
+ else
+ timeri->flags |= SNDRV_TIMER_IFLG_PAUSED;
+ snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
+ SNDRV_TIMER_EVENT_PAUSE);
return 0;
}
+/* stop/pause a slave timer */
+static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop)
+{
+ bool running;
+
+ guard(spinlock_irqsave)(&slave_active_lock);
+ running = timeri->flags & SNDRV_TIMER_IFLG_RUNNING;
+ timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
+ if (timeri->timer) {
+ guard(spinlock)(&timeri->timer->lock);
+ list_del_init(&timeri->ack_list);
+ list_del_init(&timeri->active_list);
+ if (running)
+ snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
+ SNDRV_TIMER_EVENT_PAUSE);
+ }
+ return running ? 0 : -EBUSY;
+}
+
+/*
+ * start the timer instance
+ */
+int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
+{
+ if (timeri == NULL || ticks < 1)
+ return -EINVAL;
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ return snd_timer_start_slave(timeri, true);
+ else
+ return snd_timer_start1(timeri, true, ticks);
+}
+EXPORT_SYMBOL(snd_timer_start);
+
/*
* stop the timer instance.
*
@@ -522,54 +693,40 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri,
*/
int snd_timer_stop(struct snd_timer_instance *timeri)
{
- struct snd_timer *timer;
- unsigned long flags;
- int err;
-
- err = _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_STOP);
- if (err < 0)
- return err;
- timer = timeri->timer;
- spin_lock_irqsave(&timer->lock, flags);
- timeri->cticks = timeri->ticks;
- timeri->pticks = 0;
- spin_unlock_irqrestore(&timer->lock, flags);
- return 0;
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ return snd_timer_stop_slave(timeri, true);
+ else
+ return snd_timer_stop1(timeri, true);
}
+EXPORT_SYMBOL(snd_timer_stop);
/*
* start again.. the tick is kept.
*/
int snd_timer_continue(struct snd_timer_instance *timeri)
{
- struct snd_timer *timer;
- int result = -EINVAL;
- unsigned long flags;
+ /* timer can continue only after pause */
+ if (!(timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
+ return -EINVAL;
- if (timeri == NULL)
- return result;
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
- return snd_timer_start_slave(timeri);
- timer = timeri->timer;
- if (! timer)
- return -EINVAL;
- spin_lock_irqsave(&timer->lock, flags);
- if (!timeri->cticks)
- timeri->cticks = 1;
- timeri->pticks = 0;
- result = snd_timer_start1(timer, timeri, timer->sticks);
- spin_unlock_irqrestore(&timer->lock, flags);
- snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE);
- return result;
+ return snd_timer_start_slave(timeri, false);
+ else
+ return snd_timer_start1(timeri, false, 0);
}
+EXPORT_SYMBOL(snd_timer_continue);
/*
* pause.. remember the ticks left
*/
int snd_timer_pause(struct snd_timer_instance * timeri)
{
- return _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_PAUSE);
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ return snd_timer_stop_slave(timeri, false);
+ else
+ return snd_timer_stop1(timeri, false);
}
+EXPORT_SYMBOL(snd_timer_pause);
/*
* reschedule the timer
@@ -604,39 +761,58 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l
timer->sticks = ticks;
}
-/*
- * timer tasklet
- *
- */
-static void snd_timer_tasklet(unsigned long arg)
+/* call callbacks in timer ack list */
+static void snd_timer_process_callbacks(struct snd_timer *timer,
+ struct list_head *head)
{
- struct snd_timer *timer = (struct snd_timer *) arg;
struct snd_timer_instance *ti;
- struct list_head *p;
unsigned long resolution, ticks;
- unsigned long flags;
- spin_lock_irqsave(&timer->lock, flags);
- /* now process all callbacks */
- while (!list_empty(&timer->sack_list_head)) {
- p = timer->sack_list_head.next; /* get first item */
- ti = list_entry(p, struct snd_timer_instance, ack_list);
+ while (!list_empty(head)) {
+ ti = list_first_entry(head, struct snd_timer_instance,
+ ack_list);
/* remove from ack_list and make empty */
- list_del_init(p);
+ list_del_init(&ti->ack_list);
+
+ if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) {
+ ticks = ti->pticks;
+ ti->pticks = 0;
+ resolution = ti->resolution;
+ ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
+ spin_unlock(&timer->lock);
+ if (ti->callback)
+ ti->callback(ti, resolution, ticks);
+ spin_lock(&timer->lock);
+ ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
+ }
+ }
+}
+
+/* clear pending instances from ack list */
+static void snd_timer_clear_callbacks(struct snd_timer *timer,
+ struct list_head *head)
+{
+ guard(spinlock_irqsave)(&timer->lock);
+ while (!list_empty(head))
+ list_del_init(head->next);
+}
- ticks = ti->pticks;
- ti->pticks = 0;
- resolution = ti->resolution;
+/*
+ * timer work
+ *
+ */
+static void snd_timer_work(struct work_struct *work)
+{
+ struct snd_timer *timer = container_of(work, struct snd_timer, task_work);
- ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
- spin_unlock(&timer->lock);
- if (ti->callback)
- ti->callback(ti, resolution, ticks);
- spin_lock(&timer->lock);
- ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
+ if (timer->card && timer->card->shutdown) {
+ snd_timer_clear_callbacks(timer, &timer->sack_list_head);
+ return;
}
- spin_unlock_irqrestore(&timer->lock, flags);
+
+ guard(spinlock_irqsave)(&timer->lock);
+ snd_timer_process_callbacks(timer, &timer->sack_list_head);
}
/*
@@ -648,21 +824,21 @@ static void snd_timer_tasklet(unsigned long arg)
void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
{
struct snd_timer_instance *ti, *ts, *tmp;
- unsigned long resolution, ticks;
- struct list_head *p, *ack_list_head;
- unsigned long flags;
- int use_tasklet = 0;
+ unsigned long resolution;
+ struct list_head *ack_list_head;
if (timer == NULL)
return;
- spin_lock_irqsave(&timer->lock, flags);
+ if (timer->card && timer->card->shutdown) {
+ snd_timer_clear_callbacks(timer, &timer->ack_list_head);
+ return;
+ }
+
+ guard(spinlock_irqsave)(&timer->lock);
/* remember the current resolution */
- if (timer->hw.c_resolution)
- resolution = timer->hw.c_resolution(timer);
- else
- resolution = timer->hw.resolution;
+ resolution = snd_timer_hw_resolution(timer);
/* loop for all active instances
* Here we cannot use list_for_each_entry because the active_list of a
@@ -671,6 +847,8 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
*/
list_for_each_entry_safe(ti, tmp, &timer->active_list_head,
active_list) {
+ if (ti->flags & SNDRV_TIMER_IFLG_DEAD)
+ continue;
if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING))
continue;
ti->pticks += ticks_left;
@@ -685,10 +863,10 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
ti->cticks = ti->ticks;
} else {
ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
- if (--timer->running)
- list_del(&ti->active_list);
+ --timer->running;
+ list_del_init(&ti->active_list);
}
- if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) ||
+ if ((timer->hw.flags & SNDRV_TIMER_HW_WORK) ||
(ti->flags & SNDRV_TIMER_IFLG_FAST))
ack_list_head = &timer->ack_list_head;
else
@@ -720,31 +898,13 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
}
/* now process all fast callbacks */
- while (!list_empty(&timer->ack_list_head)) {
- p = timer->ack_list_head.next; /* get first item */
- ti = list_entry(p, struct snd_timer_instance, ack_list);
-
- /* remove from ack_list and make empty */
- list_del_init(p);
-
- ticks = ti->pticks;
- ti->pticks = 0;
-
- ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
- spin_unlock(&timer->lock);
- if (ti->callback)
- ti->callback(ti, resolution, ticks);
- spin_lock(&timer->lock);
- ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
- }
+ snd_timer_process_callbacks(timer, &timer->ack_list_head);
/* do we have any slow callbacks? */
- use_tasklet = !list_empty(&timer->sack_list_head);
- spin_unlock_irqrestore(&timer->lock, flags);
-
- if (use_tasklet)
- tasklet_schedule(&timer->task_queue);
+ if (!list_empty(&timer->sack_list_head))
+ queue_work(system_highpri_wq, &timer->task_work);
}
+EXPORT_SYMBOL(snd_timer_interrupt);
/*
@@ -755,7 +915,7 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
{
struct snd_timer *timer;
int err;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_timer_dev_free,
.dev_register = snd_timer_dev_register,
.dev_disconnect = snd_timer_dev_disconnect,
@@ -763,27 +923,31 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
if (snd_BUG_ON(!tid))
return -EINVAL;
+ if (tid->dev_class == SNDRV_TIMER_CLASS_CARD ||
+ tid->dev_class == SNDRV_TIMER_CLASS_PCM) {
+ if (WARN_ON(!card))
+ return -EINVAL;
+ }
if (rtimer)
*rtimer = NULL;
timer = kzalloc(sizeof(*timer), GFP_KERNEL);
- if (timer == NULL) {
- snd_printk(KERN_ERR "timer: cannot allocate\n");
+ if (!timer)
return -ENOMEM;
- }
timer->tmr_class = tid->dev_class;
timer->card = card;
timer->tmr_device = tid->device;
timer->tmr_subdevice = tid->subdevice;
if (id)
- strlcpy(timer->id, id, sizeof(timer->id));
+ strscpy(timer->id, id, sizeof(timer->id));
+ timer->sticks = 1;
INIT_LIST_HEAD(&timer->device_list);
INIT_LIST_HEAD(&timer->open_list_head);
INIT_LIST_HEAD(&timer->active_list_head);
INIT_LIST_HEAD(&timer->ack_list_head);
INIT_LIST_HEAD(&timer->sack_list_head);
spin_lock_init(&timer->lock);
- tasklet_init(&timer->task_queue, snd_timer_tasklet,
- (unsigned long)timer);
+ INIT_WORK(&timer->task_work, snd_timer_work);
+ timer->max_instances = 1000; /* default limit per timer */
if (card != NULL) {
timer->module = card->module;
err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops);
@@ -796,17 +960,18 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
*rtimer = timer;
return 0;
}
+EXPORT_SYMBOL(snd_timer_new);
static int snd_timer_free(struct snd_timer *timer)
{
if (!timer)
return 0;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
if (! list_empty(&timer->open_list_head)) {
struct list_head *p, *n;
struct snd_timer_instance *ti;
- snd_printk(KERN_WARNING "timer %p is busy?\n", timer);
+ pr_warn("ALSA: timer %p is busy?\n", timer);
list_for_each_safe(p, n, &timer->open_list_head) {
list_del_init(p);
ti = list_entry(p, struct snd_timer_instance, open_list);
@@ -814,7 +979,6 @@ static int snd_timer_free(struct snd_timer *timer)
}
}
list_del(&timer->device_list);
- mutex_unlock(&register_mutex);
if (timer->private_free)
timer->private_free(timer);
@@ -839,7 +1003,7 @@ static int snd_timer_dev_register(struct snd_device *dev)
!timer->hw.resolution && timer->hw.c_resolution == NULL)
return -EINVAL;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
list_for_each_entry(timer1, &snd_timer_list, device_list) {
if (timer1->tmr_class > timer->tmr_class)
break;
@@ -860,43 +1024,44 @@ static int snd_timer_dev_register(struct snd_device *dev)
if (timer1->tmr_subdevice < timer->tmr_subdevice)
continue;
/* conflicts.. */
- mutex_unlock(&register_mutex);
return -EBUSY;
}
list_add_tail(&timer->device_list, &timer1->device_list);
- mutex_unlock(&register_mutex);
return 0;
}
static int snd_timer_dev_disconnect(struct snd_device *device)
{
struct snd_timer *timer = device->device_data;
- mutex_lock(&register_mutex);
+ struct snd_timer_instance *ti;
+
+ guard(mutex)(&register_mutex);
list_del_init(&timer->device_list);
- mutex_unlock(&register_mutex);
+ /* wake up pending sleepers */
+ list_for_each_entry(ti, &timer->open_list_head, open_list) {
+ if (ti->disconnect)
+ ti->disconnect(ti);
+ }
return 0;
}
-void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstamp)
+void snd_timer_notify(struct snd_timer *timer, int event, struct timespec64 *tstamp)
{
- unsigned long flags;
unsigned long resolution = 0;
struct snd_timer_instance *ti, *ts;
+ if (timer->card && timer->card->shutdown)
+ return;
if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))
return;
if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
event > SNDRV_TIMER_EVENT_MRESUME))
return;
- spin_lock_irqsave(&timer->lock, flags);
+ guard(spinlock_irqsave)(&timer->lock);
if (event == SNDRV_TIMER_EVENT_MSTART ||
event == SNDRV_TIMER_EVENT_MCONTINUE ||
- event == SNDRV_TIMER_EVENT_MRESUME) {
- if (timer->hw.c_resolution)
- resolution = timer->hw.c_resolution(timer);
- else
- resolution = timer->hw.resolution;
- }
+ event == SNDRV_TIMER_EVENT_MRESUME)
+ resolution = snd_timer_hw_resolution(timer);
list_for_each_entry(ti, &timer->active_list_head, active_list) {
if (ti->ccallback)
ti->ccallback(ti, event, tstamp, resolution);
@@ -904,8 +1069,8 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
if (ts->ccallback)
ts->ccallback(ts, event, tstamp, resolution);
}
- spin_unlock_irqrestore(&timer->lock, flags);
}
+EXPORT_SYMBOL(snd_timer_notify);
/*
* exported functions for global timers
@@ -921,11 +1086,13 @@ int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer)
tid.subdevice = 0;
return snd_timer_new(NULL, id, &tid, rtimer);
}
+EXPORT_SYMBOL(snd_timer_global_new);
int snd_timer_global_free(struct snd_timer *timer)
{
return snd_timer_free(timer);
}
+EXPORT_SYMBOL(snd_timer_global_free);
int snd_timer_global_register(struct snd_timer *timer)
{
@@ -935,6 +1102,7 @@ int snd_timer_global_register(struct snd_timer *timer)
dev.device_data = timer;
return snd_timer_dev_register(&dev);
}
+EXPORT_SYMBOL(snd_timer_global_register);
/*
* System timer
@@ -942,15 +1110,17 @@ int snd_timer_global_register(struct snd_timer *timer)
struct snd_timer_system_private {
struct timer_list tlist;
+ struct snd_timer *snd_timer;
unsigned long last_expires;
unsigned long last_jiffies;
unsigned long correction;
};
-static void snd_timer_s_function(unsigned long data)
+static void snd_timer_s_function(struct timer_list *t)
{
- struct snd_timer *timer = (struct snd_timer *)data;
- struct snd_timer_system_private *priv = timer->private_data;
+ struct snd_timer_system_private *priv = timer_container_of(priv, t,
+ tlist);
+ struct snd_timer *timer = priv->snd_timer;
unsigned long jiff = jiffies;
if (time_after(jiff, priv->last_expires))
priv->correction += (long)jiff - (long)priv->last_expires;
@@ -971,8 +1141,8 @@ static int snd_timer_s_start(struct snd_timer * timer)
njiff += timer->sticks - priv->correction;
priv->correction = 0;
}
- priv->last_expires = priv->tlist.expires = njiff;
- add_timer(&priv->tlist);
+ priv->last_expires = njiff;
+ mod_timer(&priv->tlist, njiff);
return 0;
}
@@ -982,7 +1152,7 @@ static int snd_timer_s_stop(struct snd_timer * timer)
unsigned long jiff;
priv = (struct snd_timer_system_private *) timer->private_data;
- del_timer(&priv->tlist);
+ timer_delete(&priv->tlist);
jiff = jiffies;
if (time_before(jiff, priv->last_expires))
timer->sticks = priv->last_expires - jiff;
@@ -992,11 +1162,21 @@ static int snd_timer_s_stop(struct snd_timer * timer)
return 0;
}
-static struct snd_timer_hardware snd_timer_system =
+static int snd_timer_s_close(struct snd_timer *timer)
{
- .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET,
- .resolution = 1000000000L / HZ,
+ struct snd_timer_system_private *priv;
+
+ priv = (struct snd_timer_system_private *)timer->private_data;
+ timer_delete_sync(&priv->tlist);
+ return 0;
+}
+
+static const struct snd_timer_hardware snd_timer_system =
+{
+ .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_WORK,
+ .resolution = NSEC_PER_SEC / HZ,
.ticks = 10000000L,
+ .close = snd_timer_s_close,
.start = snd_timer_s_start,
.stop = snd_timer_s_stop
};
@@ -1015,22 +1195,21 @@ static int snd_timer_register_system(void)
err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer);
if (err < 0)
return err;
- strcpy(timer->name, "system timer");
+ strscpy(timer->name, "system timer");
timer->hw = snd_timer_system;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (priv == NULL) {
snd_timer_free(timer);
return -ENOMEM;
}
- init_timer(&priv->tlist);
- priv->tlist.function = snd_timer_s_function;
- priv->tlist.data = (unsigned long) timer;
+ priv->snd_timer = timer;
+ timer_setup(&priv->tlist, snd_timer_s_function, 0);
timer->private_data = priv;
timer->private_free = snd_timer_free_system;
return snd_timer_global_register(timer);
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* Info interface
*/
@@ -1040,9 +1219,12 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
{
struct snd_timer *timer;
struct snd_timer_instance *ti;
+ unsigned long resolution;
- mutex_lock(&register_mutex);
+ guard(mutex)(&register_mutex);
list_for_each_entry(timer, &snd_timer_list, device_list) {
+ if (timer->card && timer->card->shutdown)
+ continue;
switch (timer->tmr_class) {
case SNDRV_TIMER_CLASS_GLOBAL:
snd_iprintf(buffer, "G%i: ", timer->tmr_device);
@@ -1061,10 +1243,12 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
timer->tmr_device, timer->tmr_subdevice);
}
snd_iprintf(buffer, "%s :", timer->name);
- if (timer->hw.resolution)
+ scoped_guard(spinlock_irq, &timer->lock)
+ resolution = snd_timer_hw_resolution(timer);
+ if (resolution)
snd_iprintf(buffer, " %lu.%03luus (%lu ticks)",
- timer->hw.resolution / 1000,
- timer->hw.resolution % 1000,
+ resolution / 1000,
+ resolution % 1000,
timer->hw.ticks);
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
snd_iprintf(buffer, " SLAVE");
@@ -1072,11 +1256,10 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
list_for_each_entry(ti, &timer->open_list_head, open_list)
snd_iprintf(buffer, " Client %s : %s\n",
ti->owner ? ti->owner : "unknown",
- ti->flags & (SNDRV_TIMER_IFLG_START |
- SNDRV_TIMER_IFLG_RUNNING)
+ (ti->flags & (SNDRV_TIMER_IFLG_START |
+ SNDRV_TIMER_IFLG_RUNNING))
? "running" : "stopped");
}
- mutex_unlock(&register_mutex);
}
static struct snd_info_entry *snd_timer_proc_entry;
@@ -1100,7 +1283,7 @@ static void __exit snd_timer_proc_done(void)
{
snd_info_free_entry(snd_timer_proc_entry);
}
-#else /* !CONFIG_PROC_FS */
+#else /* !CONFIG_SND_PROC_FS */
#define snd_timer_proc_init()
#define snd_timer_proc_done()
#endif
@@ -1117,7 +1300,7 @@ static void snd_timer_user_interrupt(struct snd_timer_instance *timeri,
struct snd_timer_read *r;
int prev;
- spin_lock(&tu->qlock);
+ guard(spinlock)(&tu->qlock);
if (tu->qused > 0) {
prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
r = &tu->queue[prev];
@@ -1136,13 +1319,12 @@ static void snd_timer_user_interrupt(struct snd_timer_instance *timeri,
tu->qused++;
}
__wake:
- spin_unlock(&tu->qlock);
- kill_fasync(&tu->fasync, SIGIO, POLL_IN);
+ snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
wake_up(&tu->qchange_sleep);
}
static void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu,
- struct snd_timer_tread *tread)
+ struct snd_timer_tread64 *tread)
{
if (tu->qused >= tu->queue_size) {
tu->overrun++;
@@ -1155,25 +1337,33 @@ static void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu,
static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
int event,
- struct timespec *tstamp,
+ struct timespec64 *tstamp,
unsigned long resolution)
{
struct snd_timer_user *tu = timeri->callback_data;
- struct snd_timer_tread r1;
- unsigned long flags;
+ struct snd_timer_tread64 r1;
if (event >= SNDRV_TIMER_EVENT_START &&
event <= SNDRV_TIMER_EVENT_PAUSE)
tu->tstamp = *tstamp;
if ((tu->filter & (1 << event)) == 0 || !tu->tread)
return;
+ memset(&r1, 0, sizeof(r1));
r1.event = event;
- r1.tstamp = *tstamp;
+ r1.tstamp_sec = tstamp->tv_sec;
+ r1.tstamp_nsec = tstamp->tv_nsec;
r1.val = resolution;
- spin_lock_irqsave(&tu->qlock, flags);
- snd_timer_user_append_to_tqueue(tu, &r1);
- spin_unlock_irqrestore(&tu->qlock, flags);
- kill_fasync(&tu->fasync, SIGIO, POLL_IN);
+ scoped_guard(spinlock_irqsave, &tu->qlock)
+ snd_timer_user_append_to_tqueue(tu, &r1);
+ snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
+ wake_up(&tu->qchange_sleep);
+}
+
+static void snd_timer_user_disconnect(struct snd_timer_instance *timeri)
+{
+ struct snd_timer_user *tu = timeri->callback_data;
+
+ tu->disconnected = true;
wake_up(&tu->qchange_sleep);
}
@@ -1182,65 +1372,92 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
unsigned long ticks)
{
struct snd_timer_user *tu = timeri->callback_data;
- struct snd_timer_tread *r, r1;
- struct timespec tstamp;
+ struct snd_timer_tread64 *r, r1;
+ struct timespec64 tstamp;
int prev, append = 0;
+ memset(&r1, 0, sizeof(r1));
memset(&tstamp, 0, sizeof(tstamp));
- spin_lock(&tu->qlock);
- if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) |
- (1 << SNDRV_TIMER_EVENT_TICK))) == 0) {
- spin_unlock(&tu->qlock);
- return;
- }
- if (tu->last_resolution != resolution || ticks > 0) {
- if (timer_tstamp_monotonic)
- do_posix_clock_monotonic_gettime(&tstamp);
- else
- getnstimeofday(&tstamp);
- }
- if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
- tu->last_resolution != resolution) {
- r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
- r1.tstamp = tstamp;
- r1.val = resolution;
- snd_timer_user_append_to_tqueue(tu, &r1);
- tu->last_resolution = resolution;
- append++;
- }
- if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0)
- goto __wake;
- if (ticks == 0)
- goto __wake;
- if (tu->qused > 0) {
- prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
- r = &tu->tqueue[prev];
- if (r->event == SNDRV_TIMER_EVENT_TICK) {
- r->tstamp = tstamp;
- r->val += ticks;
+ scoped_guard(spinlock, &tu->qlock) {
+ if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) |
+ (1 << SNDRV_TIMER_EVENT_TICK))) == 0)
+ return;
+ if (tu->last_resolution != resolution || ticks > 0) {
+ if (timer_tstamp_monotonic)
+ ktime_get_ts64(&tstamp);
+ else
+ ktime_get_real_ts64(&tstamp);
+ }
+ if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
+ tu->last_resolution != resolution) {
+ r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
+ r1.tstamp_sec = tstamp.tv_sec;
+ r1.tstamp_nsec = tstamp.tv_nsec;
+ r1.val = resolution;
+ snd_timer_user_append_to_tqueue(tu, &r1);
+ tu->last_resolution = resolution;
append++;
- goto __wake;
}
+ if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0)
+ break;
+ if (ticks == 0)
+ break;
+ if (tu->qused > 0) {
+ prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
+ r = &tu->tqueue[prev];
+ if (r->event == SNDRV_TIMER_EVENT_TICK) {
+ r->tstamp_sec = tstamp.tv_sec;
+ r->tstamp_nsec = tstamp.tv_nsec;
+ r->val += ticks;
+ append++;
+ break;
+ }
+ }
+ r1.event = SNDRV_TIMER_EVENT_TICK;
+ r1.tstamp_sec = tstamp.tv_sec;
+ r1.tstamp_nsec = tstamp.tv_nsec;
+ r1.val = ticks;
+ snd_timer_user_append_to_tqueue(tu, &r1);
+ append++;
}
- r1.event = SNDRV_TIMER_EVENT_TICK;
- r1.tstamp = tstamp;
- r1.val = ticks;
- snd_timer_user_append_to_tqueue(tu, &r1);
- append++;
- __wake:
- spin_unlock(&tu->qlock);
if (append == 0)
return;
- kill_fasync(&tu->fasync, SIGIO, POLL_IN);
+ snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
wake_up(&tu->qchange_sleep);
}
+static int realloc_user_queue(struct snd_timer_user *tu, int size)
+{
+ struct snd_timer_read *queue = NULL;
+ struct snd_timer_tread64 *tqueue = NULL;
+
+ if (tu->tread) {
+ tqueue = kcalloc(size, sizeof(*tqueue), GFP_KERNEL);
+ if (!tqueue)
+ return -ENOMEM;
+ } else {
+ queue = kcalloc(size, sizeof(*queue), GFP_KERNEL);
+ if (!queue)
+ return -ENOMEM;
+ }
+
+ guard(spinlock_irq)(&tu->qlock);
+ kfree(tu->queue);
+ kfree(tu->tqueue);
+ tu->queue_size = size;
+ tu->queue = queue;
+ tu->tqueue = tqueue;
+ tu->qhead = tu->qtail = tu->qused = 0;
+
+ return 0;
+}
+
static int snd_timer_user_open(struct inode *inode, struct file *file)
{
struct snd_timer_user *tu;
int err;
- err = nonseekable_open(inode, file);
+ err = stream_open(inode, file);
if (err < 0)
return err;
@@ -1249,12 +1466,9 @@ static int snd_timer_user_open(struct inode *inode, struct file *file)
return -ENOMEM;
spin_lock_init(&tu->qlock);
init_waitqueue_head(&tu->qchange_sleep);
- mutex_init(&tu->tread_sem);
+ mutex_init(&tu->ioctl_lock);
tu->ticks = 1;
- tu->queue_size = 128;
- tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read),
- GFP_KERNEL);
- if (tu->queue == NULL) {
+ if (realloc_user_queue(tu, 128) < 0) {
kfree(tu);
return -ENOMEM;
}
@@ -1269,8 +1483,13 @@ static int snd_timer_user_release(struct inode *inode, struct file *file)
if (file->private_data) {
tu = file->private_data;
file->private_data = NULL;
- if (tu->timeri)
- snd_timer_close(tu->timeri);
+ scoped_guard(mutex, &tu->ioctl_lock) {
+ if (tu->timeri) {
+ snd_timer_close(tu->timeri);
+ snd_timer_instance_free(tu->timeri);
+ }
+ }
+ snd_fasync_free(tu->fasync);
kfree(tu->queue);
kfree(tu->tqueue);
kfree(tu);
@@ -1296,97 +1515,97 @@ static void snd_timer_user_copy_id(struct snd_timer_id *id, struct snd_timer *ti
id->subdevice = timer->tmr_subdevice;
}
-static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
+static void get_next_device(struct snd_timer_id *id)
{
- struct snd_timer_id id;
struct snd_timer *timer;
struct list_head *p;
- if (copy_from_user(&id, _tid, sizeof(id)))
- return -EFAULT;
- mutex_lock(&register_mutex);
- if (id.dev_class < 0) { /* first item */
+ if (id->dev_class < 0) { /* first item */
if (list_empty(&snd_timer_list))
- snd_timer_user_zero_id(&id);
+ snd_timer_user_zero_id(id);
else {
timer = list_entry(snd_timer_list.next,
struct snd_timer, device_list);
- snd_timer_user_copy_id(&id, timer);
+ snd_timer_user_copy_id(id, timer);
}
} else {
- switch (id.dev_class) {
+ switch (id->dev_class) {
case SNDRV_TIMER_CLASS_GLOBAL:
- id.device = id.device < 0 ? 0 : id.device + 1;
+ id->device = id->device < 0 ? 0 : id->device + 1;
list_for_each(p, &snd_timer_list) {
timer = list_entry(p, struct snd_timer, device_list);
if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) {
- snd_timer_user_copy_id(&id, timer);
+ snd_timer_user_copy_id(id, timer);
break;
}
- if (timer->tmr_device >= id.device) {
- snd_timer_user_copy_id(&id, timer);
+ if (timer->tmr_device >= id->device) {
+ snd_timer_user_copy_id(id, timer);
break;
}
}
if (p == &snd_timer_list)
- snd_timer_user_zero_id(&id);
+ snd_timer_user_zero_id(id);
break;
case SNDRV_TIMER_CLASS_CARD:
case SNDRV_TIMER_CLASS_PCM:
- if (id.card < 0) {
- id.card = 0;
+ if (id->card < 0) {
+ id->card = 0;
} else {
- if (id.card < 0) {
- id.card = 0;
+ if (id->device < 0) {
+ id->device = 0;
} else {
- if (id.device < 0) {
- id.device = 0;
- } else {
- if (id.subdevice < 0) {
- id.subdevice = 0;
- } else {
- id.subdevice++;
- }
- }
+ if (id->subdevice < 0)
+ id->subdevice = 0;
+ else if (id->subdevice < INT_MAX)
+ id->subdevice++;
}
}
list_for_each(p, &snd_timer_list) {
timer = list_entry(p, struct snd_timer, device_list);
- if (timer->tmr_class > id.dev_class) {
- snd_timer_user_copy_id(&id, timer);
+ if (timer->tmr_class > id->dev_class) {
+ snd_timer_user_copy_id(id, timer);
break;
}
- if (timer->tmr_class < id.dev_class)
+ if (timer->tmr_class < id->dev_class)
continue;
- if (timer->card->number > id.card) {
- snd_timer_user_copy_id(&id, timer);
+ if (timer->card->number > id->card) {
+ snd_timer_user_copy_id(id, timer);
break;
}
- if (timer->card->number < id.card)
+ if (timer->card->number < id->card)
continue;
- if (timer->tmr_device > id.device) {
- snd_timer_user_copy_id(&id, timer);
+ if (timer->tmr_device > id->device) {
+ snd_timer_user_copy_id(id, timer);
break;
}
- if (timer->tmr_device < id.device)
+ if (timer->tmr_device < id->device)
continue;
- if (timer->tmr_subdevice > id.subdevice) {
- snd_timer_user_copy_id(&id, timer);
+ if (timer->tmr_subdevice > id->subdevice) {
+ snd_timer_user_copy_id(id, timer);
break;
}
- if (timer->tmr_subdevice < id.subdevice)
+ if (timer->tmr_subdevice < id->subdevice)
continue;
- snd_timer_user_copy_id(&id, timer);
+ snd_timer_user_copy_id(id, timer);
break;
}
if (p == &snd_timer_list)
- snd_timer_user_zero_id(&id);
+ snd_timer_user_zero_id(id);
break;
default:
- snd_timer_user_zero_id(&id);
+ snd_timer_user_zero_id(id);
}
}
- mutex_unlock(&register_mutex);
+}
+
+static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
+{
+ struct snd_timer_id id;
+
+ if (copy_from_user(&id, _tid, sizeof(id)))
+ return -EFAULT;
+ scoped_guard(mutex, &register_mutex)
+ get_next_device(&id);
if (copy_to_user(_tid, &id, sizeof(*_tid)))
return -EFAULT;
return 0;
@@ -1395,11 +1614,10 @@ static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
static int snd_timer_user_ginfo(struct file *file,
struct snd_timer_ginfo __user *_ginfo)
{
- struct snd_timer_ginfo *ginfo;
+ struct snd_timer_ginfo *ginfo __free(kfree) = NULL;
struct snd_timer_id tid;
struct snd_timer *t;
struct list_head *p;
- int err = 0;
ginfo = memdup_user(_ginfo, sizeof(*ginfo));
if (IS_ERR(ginfo))
@@ -1408,15 +1626,17 @@ static int snd_timer_user_ginfo(struct file *file,
tid = ginfo->tid;
memset(ginfo, 0, sizeof(*ginfo));
ginfo->tid = tid;
- mutex_lock(&register_mutex);
- t = snd_timer_find(&tid);
- if (t != NULL) {
+ scoped_guard(mutex, &register_mutex) {
+ t = snd_timer_find(&tid);
+ if (!t)
+ return -ENODEV;
ginfo->card = t->card ? t->card->number : -1;
if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
ginfo->flags |= SNDRV_TIMER_FLG_SLAVE;
- strlcpy(ginfo->id, t->id, sizeof(ginfo->id));
- strlcpy(ginfo->name, t->name, sizeof(ginfo->name));
- ginfo->resolution = t->hw.resolution;
+ strscpy(ginfo->id, t->id, sizeof(ginfo->id));
+ strscpy(ginfo->name, t->name, sizeof(ginfo->name));
+ scoped_guard(spinlock_irq, &t->lock)
+ ginfo->resolution = snd_timer_hw_resolution(t);
if (t->hw.resolution_min > 0) {
ginfo->resolution_min = t->hw.resolution_min;
ginfo->resolution_max = t->hw.resolution_max;
@@ -1424,43 +1644,35 @@ static int snd_timer_user_ginfo(struct file *file,
list_for_each(p, &t->open_list_head) {
ginfo->clients++;
}
- } else {
- err = -ENODEV;
}
- mutex_unlock(&register_mutex);
- if (err >= 0 && copy_to_user(_ginfo, ginfo, sizeof(*ginfo)))
- err = -EFAULT;
- kfree(ginfo);
- return err;
+ if (copy_to_user(_ginfo, ginfo, sizeof(*ginfo)))
+ return -EFAULT;
+ return 0;
+}
+
+static int timer_set_gparams(struct snd_timer_gparams *gparams)
+{
+ struct snd_timer *t;
+
+ guard(mutex)(&register_mutex);
+ t = snd_timer_find(&gparams->tid);
+ if (!t)
+ return -ENODEV;
+ if (!list_empty(&t->open_list_head))
+ return -EBUSY;
+ if (!t->hw.set_period)
+ return -ENOSYS;
+ return t->hw.set_period(t, gparams->period_num, gparams->period_den);
}
static int snd_timer_user_gparams(struct file *file,
struct snd_timer_gparams __user *_gparams)
{
struct snd_timer_gparams gparams;
- struct snd_timer *t;
- int err;
if (copy_from_user(&gparams, _gparams, sizeof(gparams)))
return -EFAULT;
- mutex_lock(&register_mutex);
- t = snd_timer_find(&gparams.tid);
- if (!t) {
- err = -ENODEV;
- goto _error;
- }
- if (!list_empty(&t->open_list_head)) {
- err = -EBUSY;
- goto _error;
- }
- if (!t->hw.set_period) {
- err = -ENOSYS;
- goto _error;
- }
- err = t->hw.set_period(t, gparams.period_num, gparams.period_den);
-_error:
- mutex_unlock(&register_mutex);
- return err;
+ return timer_set_gparams(&gparams);
}
static int snd_timer_user_gstatus(struct file *file,
@@ -1469,34 +1681,31 @@ static int snd_timer_user_gstatus(struct file *file,
struct snd_timer_gstatus gstatus;
struct snd_timer_id tid;
struct snd_timer *t;
- int err = 0;
if (copy_from_user(&gstatus, _gstatus, sizeof(gstatus)))
return -EFAULT;
tid = gstatus.tid;
memset(&gstatus, 0, sizeof(gstatus));
gstatus.tid = tid;
- mutex_lock(&register_mutex);
- t = snd_timer_find(&tid);
- if (t != NULL) {
- if (t->hw.c_resolution)
- gstatus.resolution = t->hw.c_resolution(t);
- else
- gstatus.resolution = t->hw.resolution;
- if (t->hw.precise_resolution) {
- t->hw.precise_resolution(t, &gstatus.resolution_num,
- &gstatus.resolution_den);
+ scoped_guard(mutex, &register_mutex) {
+ t = snd_timer_find(&tid);
+ if (t != NULL) {
+ guard(spinlock_irq)(&t->lock);
+ gstatus.resolution = snd_timer_hw_resolution(t);
+ if (t->hw.precise_resolution) {
+ t->hw.precise_resolution(t, &gstatus.resolution_num,
+ &gstatus.resolution_den);
+ } else {
+ gstatus.resolution_num = gstatus.resolution;
+ gstatus.resolution_den = 1000000000uL;
+ }
} else {
- gstatus.resolution_num = gstatus.resolution;
- gstatus.resolution_den = 1000000000uL;
+ return -ENODEV;
}
- } else {
- err = -ENODEV;
}
- mutex_unlock(&register_mutex);
- if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus)))
- err = -EFAULT;
- return err;
+ if (copy_to_user(_gstatus, &gstatus, sizeof(gstatus)))
+ return -EFAULT;
+ return 0;
}
static int snd_timer_user_tselect(struct file *file,
@@ -1508,9 +1717,9 @@ static int snd_timer_user_tselect(struct file *file,
int err = 0;
tu = file->private_data;
- mutex_lock(&tu->tread_sem);
if (tu->timeri) {
snd_timer_close(tu->timeri);
+ snd_timer_instance_free(tu->timeri);
tu->timeri = NULL;
}
if (copy_from_user(&tselect, _tselect, sizeof(tselect))) {
@@ -1520,39 +1729,26 @@ static int snd_timer_user_tselect(struct file *file,
sprintf(str, "application %i", current->pid);
if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION;
- err = snd_timer_open(&tu->timeri, str, &tselect.id, current->pid);
- if (err < 0)
+ tu->timeri = snd_timer_instance_new(str);
+ if (!tu->timeri) {
+ err = -ENOMEM;
goto __err;
-
- kfree(tu->queue);
- tu->queue = NULL;
- kfree(tu->tqueue);
- tu->tqueue = NULL;
- if (tu->tread) {
- tu->tqueue = kmalloc(tu->queue_size * sizeof(struct snd_timer_tread),
- GFP_KERNEL);
- if (tu->tqueue == NULL)
- err = -ENOMEM;
- } else {
- tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read),
- GFP_KERNEL);
- if (tu->queue == NULL)
- err = -ENOMEM;
}
- if (err < 0) {
- snd_timer_close(tu->timeri);
- tu->timeri = NULL;
- } else {
- tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST;
- tu->timeri->callback = tu->tread
+ tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST;
+ tu->timeri->callback = tu->tread
? snd_timer_user_tinterrupt : snd_timer_user_interrupt;
- tu->timeri->ccallback = snd_timer_user_ccallback;
- tu->timeri->callback_data = (void *)tu;
+ tu->timeri->ccallback = snd_timer_user_ccallback;
+ tu->timeri->callback_data = (void *)tu;
+ tu->timeri->disconnect = snd_timer_user_disconnect;
+
+ err = snd_timer_open(tu->timeri, &tselect.id, current->pid);
+ if (err < 0) {
+ snd_timer_instance_free(tu->timeri);
+ tu->timeri = NULL;
}
__err:
- mutex_unlock(&tu->tread_sem);
return err;
}
@@ -1560,9 +1756,8 @@ static int snd_timer_user_info(struct file *file,
struct snd_timer_info __user *_info)
{
struct snd_timer_user *tu;
- struct snd_timer_info *info;
+ struct snd_timer_info *info __free(kfree) = NULL;
struct snd_timer *t;
- int err = 0;
tu = file->private_data;
if (!tu->timeri)
@@ -1577,13 +1772,13 @@ static int snd_timer_user_info(struct file *file,
info->card = t->card ? t->card->number : -1;
if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
info->flags |= SNDRV_TIMER_FLG_SLAVE;
- strlcpy(info->id, t->id, sizeof(info->id));
- strlcpy(info->name, t->name, sizeof(info->name));
- info->resolution = t->hw.resolution;
+ strscpy(info->id, t->id, sizeof(info->id));
+ strscpy(info->name, t->name, sizeof(info->name));
+ scoped_guard(spinlock_irq, &t->lock)
+ info->resolution = snd_timer_hw_resolution(t);
if (copy_to_user(_info, info, sizeof(*_info)))
- err = -EFAULT;
- kfree(info);
- return err;
+ return -EFAULT;
+ return 0;
}
static int snd_timer_user_params(struct file *file,
@@ -1592,8 +1787,6 @@ static int snd_timer_user_params(struct file *file,
struct snd_timer_user *tu;
struct snd_timer_params params;
struct snd_timer *t;
- struct snd_timer_read *tr;
- struct snd_timer_tread *ttr;
int err;
tu = file->private_data;
@@ -1604,9 +1797,21 @@ static int snd_timer_user_params(struct file *file,
return -EBADFD;
if (copy_from_user(&params, _params, sizeof(params)))
return -EFAULT;
- if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) {
- err = -EINVAL;
- goto _end;
+ if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
+ u64 resolution;
+
+ if (params.ticks < 1) {
+ err = -EINVAL;
+ goto _end;
+ }
+
+ /* Don't allow resolution less than 1ms */
+ resolution = snd_timer_resolution(tu->timeri);
+ resolution *= params.ticks;
+ if (resolution < 1000000) {
+ err = -EINVAL;
+ goto _end;
+ }
}
if (params.queue_size > 0 &&
(params.queue_size < 32 || params.queue_size > 1024)) {
@@ -1631,56 +1836,47 @@ static int snd_timer_user_params(struct file *file,
goto _end;
}
snd_timer_stop(tu->timeri);
- spin_lock_irq(&t->lock);
- tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO|
- SNDRV_TIMER_IFLG_EXCLUSIVE|
- SNDRV_TIMER_IFLG_EARLY_EVENT);
- if (params.flags & SNDRV_TIMER_PSFLG_AUTO)
- tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO;
- if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE)
- tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE;
- if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT)
- tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT;
- spin_unlock_irq(&t->lock);
+ scoped_guard(spinlock_irq, &t->lock) {
+ tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO|
+ SNDRV_TIMER_IFLG_EXCLUSIVE|
+ SNDRV_TIMER_IFLG_EARLY_EVENT);
+ if (params.flags & SNDRV_TIMER_PSFLG_AUTO)
+ tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO;
+ if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE)
+ tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE;
+ if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT)
+ tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT;
+ }
if (params.queue_size > 0 &&
(unsigned int)tu->queue_size != params.queue_size) {
- if (tu->tread) {
- ttr = kmalloc(params.queue_size * sizeof(*ttr),
- GFP_KERNEL);
- if (ttr) {
- kfree(tu->tqueue);
- tu->queue_size = params.queue_size;
- tu->tqueue = ttr;
- }
- } else {
- tr = kmalloc(params.queue_size * sizeof(*tr),
- GFP_KERNEL);
- if (tr) {
- kfree(tu->queue);
- tu->queue_size = params.queue_size;
- tu->queue = tr;
- }
- }
+ err = realloc_user_queue(tu, params.queue_size);
+ if (err < 0)
+ goto _end;
}
- tu->qhead = tu->qtail = tu->qused = 0;
- if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) {
- if (tu->tread) {
- struct snd_timer_tread tread;
- tread.event = SNDRV_TIMER_EVENT_EARLY;
- tread.tstamp.tv_sec = 0;
- tread.tstamp.tv_nsec = 0;
- tread.val = 0;
- snd_timer_user_append_to_tqueue(tu, &tread);
- } else {
- struct snd_timer_read *r = &tu->queue[0];
- r->resolution = 0;
- r->ticks = 0;
- tu->qused++;
- tu->qtail++;
+ scoped_guard(spinlock_irq, &tu->qlock) {
+ tu->qhead = tu->qtail = tu->qused = 0;
+ if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) {
+ if (tu->tread) {
+ struct snd_timer_tread64 tread;
+
+ memset(&tread, 0, sizeof(tread));
+ tread.event = SNDRV_TIMER_EVENT_EARLY;
+ tread.tstamp_sec = 0;
+ tread.tstamp_nsec = 0;
+ tread.val = 0;
+ snd_timer_user_append_to_tqueue(tu, &tread);
+ } else {
+ struct snd_timer_read *r = &tu->queue[0];
+
+ r->resolution = 0;
+ r->ticks = 0;
+ tu->qused++;
+ tu->qtail++;
+ }
}
+ tu->filter = params.filter;
+ tu->ticks = params.ticks;
}
- tu->filter = params.filter;
- tu->ticks = params.ticks;
err = 0;
_end:
if (copy_to_user(_params, &params, sizeof(params)))
@@ -1688,23 +1884,45 @@ static int snd_timer_user_params(struct file *file,
return err;
}
-static int snd_timer_user_status(struct file *file,
- struct snd_timer_status __user *_status)
+static int snd_timer_user_status32(struct file *file,
+ struct snd_timer_status32 __user *_status)
+ {
+ struct snd_timer_user *tu;
+ struct snd_timer_status32 status;
+
+ tu = file->private_data;
+ if (!tu->timeri)
+ return -EBADFD;
+ memset(&status, 0, sizeof(status));
+ status.tstamp_sec = tu->tstamp.tv_sec;
+ status.tstamp_nsec = tu->tstamp.tv_nsec;
+ status.resolution = snd_timer_resolution(tu->timeri);
+ status.lost = tu->timeri->lost;
+ status.overrun = tu->overrun;
+ scoped_guard(spinlock_irq, &tu->qlock)
+ status.queue = tu->qused;
+ if (copy_to_user(_status, &status, sizeof(status)))
+ return -EFAULT;
+ return 0;
+}
+
+static int snd_timer_user_status64(struct file *file,
+ struct snd_timer_status64 __user *_status)
{
struct snd_timer_user *tu;
- struct snd_timer_status status;
+ struct snd_timer_status64 status;
tu = file->private_data;
if (!tu->timeri)
return -EBADFD;
memset(&status, 0, sizeof(status));
- status.tstamp = tu->tstamp;
+ status.tstamp_sec = tu->tstamp.tv_sec;
+ status.tstamp_nsec = tu->tstamp.tv_nsec;
status.resolution = snd_timer_resolution(tu->timeri);
status.lost = tu->timeri->lost;
status.overrun = tu->overrun;
- spin_lock_irq(&tu->qlock);
- status.queue = tu->qused;
- spin_unlock_irq(&tu->qlock);
+ scoped_guard(spinlock_irq, &tu->qlock)
+ status.queue = tu->qused;
if (copy_to_user(_status, &status, sizeof(status)))
return -EFAULT;
return 0;
@@ -1721,7 +1939,10 @@ static int snd_timer_user_start(struct file *file)
snd_timer_stop(tu->timeri);
tu->timeri->lost = 0;
tu->last_resolution = 0;
- return (err = snd_timer_start(tu->timeri, tu->ticks)) < 0 ? err : 0;
+ err = snd_timer_start(tu->timeri, tu->ticks);
+ if (err < 0)
+ return err;
+ return 0;
}
static int snd_timer_user_stop(struct file *file)
@@ -1732,7 +1953,10 @@ static int snd_timer_user_stop(struct file *file)
tu = file->private_data;
if (!tu->timeri)
return -EBADFD;
- return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0;
+ err = snd_timer_stop(tu->timeri);
+ if (err < 0)
+ return err;
+ return 0;
}
static int snd_timer_user_continue(struct file *file)
@@ -1743,8 +1967,14 @@ static int snd_timer_user_continue(struct file *file)
tu = file->private_data;
if (!tu->timeri)
return -EBADFD;
+ /* start timer instead of continue if it's not used before */
+ if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
+ return snd_timer_user_start(file);
tu->timeri->lost = 0;
- return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
+ err = snd_timer_continue(tu->timeri);
+ if (err < 0)
+ return err;
+ return 0;
}
static int snd_timer_user_pause(struct file *file)
@@ -1755,7 +1985,40 @@ static int snd_timer_user_pause(struct file *file)
tu = file->private_data;
if (!tu->timeri)
return -EBADFD;
- return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;
+ err = snd_timer_pause(tu->timeri);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int snd_timer_user_tread(void __user *argp, struct snd_timer_user *tu,
+ unsigned int cmd, bool compat)
+{
+ int __user *p = argp;
+ int xarg, old_tread;
+
+ if (tu->timeri) /* too late */
+ return -EBUSY;
+ if (get_user(xarg, p))
+ return -EFAULT;
+
+ old_tread = tu->tread;
+
+ if (!xarg)
+ tu->tread = TREAD_FORMAT_NONE;
+ else if (cmd == SNDRV_TIMER_IOCTL_TREAD64 ||
+ (IS_ENABLED(CONFIG_64BIT) && !compat))
+ tu->tread = TREAD_FORMAT_TIME64;
+ else
+ tu->tread = TREAD_FORMAT_TIME32;
+
+ if (tu->tread != old_tread &&
+ realloc_user_queue(tu, tu->queue_size) < 0) {
+ tu->tread = old_tread;
+ return -ENOMEM;
+ }
+
+ return 0;
}
enum {
@@ -1765,8 +2028,219 @@ enum {
SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23),
};
-static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+#ifdef CONFIG_SND_UTIMER
+/*
+ * Since userspace-driven timers are passed to userspace, we need to have an identifier
+ * which will allow us to use them (basically, the subdevice number of udriven timer).
+ */
+static DEFINE_IDA(snd_utimer_ids);
+
+static void snd_utimer_put_id(struct snd_utimer *utimer)
+{
+ int timer_id = utimer->id;
+
+ snd_BUG_ON(timer_id < 0 || timer_id >= SNDRV_UTIMERS_MAX_COUNT);
+ ida_free(&snd_utimer_ids, timer_id);
+}
+
+static int snd_utimer_take_id(void)
+{
+ return ida_alloc_max(&snd_utimer_ids, SNDRV_UTIMERS_MAX_COUNT - 1, GFP_KERNEL);
+}
+
+static void snd_utimer_free(struct snd_utimer *utimer)
+{
+ snd_timer_free(utimer->timer);
+ snd_utimer_put_id(utimer);
+ kfree(utimer->name);
+ kfree(utimer);
+}
+
+static int snd_utimer_release(struct inode *inode, struct file *file)
+{
+ struct snd_utimer *utimer = (struct snd_utimer *)file->private_data;
+
+ snd_utimer_free(utimer);
+ return 0;
+}
+
+static int snd_utimer_trigger(struct file *file)
+{
+ struct snd_utimer *utimer = (struct snd_utimer *)file->private_data;
+
+ snd_timer_interrupt(utimer->timer, utimer->timer->sticks);
+ return 0;
+}
+
+static long snd_utimer_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
+{
+ switch (ioctl) {
+ case SNDRV_TIMER_IOCTL_TRIGGER:
+ return snd_utimer_trigger(file);
+ }
+
+ return -ENOTTY;
+}
+
+static const struct file_operations snd_utimer_fops = {
+ .llseek = noop_llseek,
+ .release = snd_utimer_release,
+ .unlocked_ioctl = snd_utimer_ioctl,
+};
+
+static int snd_utimer_start(struct snd_timer *t)
+{
+ return 0;
+}
+
+static int snd_utimer_stop(struct snd_timer *t)
+{
+ return 0;
+}
+
+static int snd_utimer_open(struct snd_timer *t)
+{
+ return 0;
+}
+
+static int snd_utimer_close(struct snd_timer *t)
+{
+ return 0;
+}
+
+static const struct snd_timer_hardware timer_hw = {
+ .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_WORK,
+ .open = snd_utimer_open,
+ .close = snd_utimer_close,
+ .start = snd_utimer_start,
+ .stop = snd_utimer_stop,
+};
+
+static int snd_utimer_create(struct snd_timer_uinfo *utimer_info,
+ struct snd_utimer **r_utimer)
+{
+ struct snd_utimer *utimer;
+ struct snd_timer *timer;
+ struct snd_timer_id tid;
+ int utimer_id;
+ int err = 0;
+
+ if (!utimer_info || utimer_info->resolution == 0)
+ return -EINVAL;
+
+ utimer = kzalloc(sizeof(*utimer), GFP_KERNEL);
+ if (!utimer)
+ return -ENOMEM;
+
+ /* We hold the ioctl lock here so we won't get a race condition when allocating id */
+ utimer_id = snd_utimer_take_id();
+ if (utimer_id < 0) {
+ err = utimer_id;
+ goto err_take_id;
+ }
+
+ utimer->id = utimer_id;
+
+ utimer->name = kasprintf(GFP_KERNEL, "snd-utimer%d", utimer_id);
+ if (!utimer->name) {
+ err = -ENOMEM;
+ goto err_get_name;
+ }
+
+ tid.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION;
+ tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
+ tid.card = -1;
+ tid.device = SNDRV_TIMER_GLOBAL_UDRIVEN;
+ tid.subdevice = utimer_id;
+
+ err = snd_timer_new(NULL, utimer->name, &tid, &timer);
+ if (err < 0) {
+ pr_err("Can't create userspace-driven timer\n");
+ goto err_timer_new;
+ }
+
+ timer->module = THIS_MODULE;
+ timer->hw = timer_hw;
+ timer->hw.resolution = utimer_info->resolution;
+ timer->hw.ticks = 1;
+ timer->max_instances = MAX_SLAVE_INSTANCES;
+
+ utimer->timer = timer;
+
+ err = snd_timer_global_register(timer);
+ if (err < 0) {
+ pr_err("Can't register a userspace-driven timer\n");
+ goto err_timer_reg;
+ }
+
+ *r_utimer = utimer;
+ return 0;
+
+err_timer_reg:
+ snd_timer_free(timer);
+err_timer_new:
+ kfree(utimer->name);
+err_get_name:
+ snd_utimer_put_id(utimer);
+err_take_id:
+ kfree(utimer);
+
+ return err;
+}
+
+static int snd_utimer_ioctl_create(struct file *file,
+ struct snd_timer_uinfo __user *_utimer_info)
+{
+ struct snd_utimer *utimer;
+ struct snd_timer_uinfo *utimer_info __free(kfree) = NULL;
+ int err, timer_fd;
+
+ utimer_info = memdup_user(_utimer_info, sizeof(*utimer_info));
+ if (IS_ERR(utimer_info))
+ return PTR_ERR(utimer_info);
+
+ err = snd_utimer_create(utimer_info, &utimer);
+ if (err < 0)
+ return err;
+
+ utimer_info->id = utimer->id;
+
+ timer_fd = anon_inode_getfd(utimer->name, &snd_utimer_fops, utimer, O_RDWR | O_CLOEXEC);
+ if (timer_fd < 0) {
+ snd_utimer_free(utimer);
+ return timer_fd;
+ }
+
+ utimer_info->fd = timer_fd;
+
+ err = copy_to_user(_utimer_info, utimer_info, sizeof(*utimer_info));
+ if (err) {
+ /*
+ * "Leak" the fd, as there is nothing we can do about it.
+ * It might have been closed already since anon_inode_getfd
+ * makes it available for userspace.
+ *
+ * We have to rely on the process exit path to do any
+ * necessary cleanup (e.g. releasing the file).
+ */
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+#else
+
+static int snd_utimer_ioctl_create(struct file *file,
+ struct snd_timer_uinfo __user *_utimer_info)
+{
+ return -ENOTTY;
+}
+
+#endif
+
+static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg, bool compat)
{
struct snd_timer_user *tu;
void __user *argp = (void __user *)arg;
@@ -1778,23 +2252,9 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0;
case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
return snd_timer_user_next_device(argp);
- case SNDRV_TIMER_IOCTL_TREAD:
- {
- int xarg;
-
- mutex_lock(&tu->tread_sem);
- if (tu->timeri) { /* too late */
- mutex_unlock(&tu->tread_sem);
- return -EBUSY;
- }
- if (get_user(xarg, p)) {
- mutex_unlock(&tu->tread_sem);
- return -EFAULT;
- }
- tu->tread = xarg ? 1 : 0;
- mutex_unlock(&tu->tread_sem);
- return 0;
- }
+ case SNDRV_TIMER_IOCTL_TREAD_OLD:
+ case SNDRV_TIMER_IOCTL_TREAD64:
+ return snd_timer_user_tread(argp, tu, cmd, compat);
case SNDRV_TIMER_IOCTL_GINFO:
return snd_timer_user_ginfo(file, argp);
case SNDRV_TIMER_IOCTL_GPARAMS:
@@ -1807,8 +2267,10 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
return snd_timer_user_info(file, argp);
case SNDRV_TIMER_IOCTL_PARAMS:
return snd_timer_user_params(file, argp);
- case SNDRV_TIMER_IOCTL_STATUS:
- return snd_timer_user_status(file, argp);
+ case SNDRV_TIMER_IOCTL_STATUS32:
+ return snd_timer_user_status32(file, argp);
+ case SNDRV_TIMER_IOCTL_STATUS64:
+ return snd_timer_user_status64(file, argp);
case SNDRV_TIMER_IOCTL_START:
case SNDRV_TIMER_IOCTL_START_OLD:
return snd_timer_user_start(file);
@@ -1821,35 +2283,64 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
case SNDRV_TIMER_IOCTL_PAUSE:
case SNDRV_TIMER_IOCTL_PAUSE_OLD:
return snd_timer_user_pause(file);
+ case SNDRV_TIMER_IOCTL_CREATE:
+ return snd_utimer_ioctl_create(file, argp);
}
return -ENOTTY;
}
+static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct snd_timer_user *tu = file->private_data;
+
+ guard(mutex)(&tu->ioctl_lock);
+ return __snd_timer_user_ioctl(file, cmd, arg, false);
+}
+
static int snd_timer_user_fasync(int fd, struct file * file, int on)
{
struct snd_timer_user *tu;
tu = file->private_data;
- return fasync_helper(fd, file, on, &tu->fasync);
+ return snd_fasync_helper(fd, file, on, &tu->fasync);
}
static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
size_t count, loff_t *offset)
{
+ struct snd_timer_tread64 *tread;
+ struct snd_timer_tread32 tread32;
struct snd_timer_user *tu;
long result = 0, unit;
+ int qhead;
int err = 0;
tu = file->private_data;
- unit = tu->tread ? sizeof(struct snd_timer_tread) : sizeof(struct snd_timer_read);
+ switch (tu->tread) {
+ case TREAD_FORMAT_TIME64:
+ unit = sizeof(struct snd_timer_tread64);
+ break;
+ case TREAD_FORMAT_TIME32:
+ unit = sizeof(struct snd_timer_tread32);
+ break;
+ case TREAD_FORMAT_NONE:
+ unit = sizeof(struct snd_timer_read);
+ break;
+ default:
+ WARN_ONCE(1, "Corrupt snd_timer_user\n");
+ return -ENOTSUPP;
+ }
+
+ mutex_lock(&tu->ioctl_lock);
spin_lock_irq(&tu->qlock);
while ((long)count - result >= unit) {
while (!tu->qused) {
- wait_queue_t wait;
+ wait_queue_entry_t wait;
if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
err = -EAGAIN;
- break;
+ goto _error;
}
set_current_state(TASK_INTERRUPTIBLE);
@@ -1857,51 +2348,73 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
add_wait_queue(&tu->qchange_sleep, &wait);
spin_unlock_irq(&tu->qlock);
+ mutex_unlock(&tu->ioctl_lock);
schedule();
+ mutex_lock(&tu->ioctl_lock);
spin_lock_irq(&tu->qlock);
remove_wait_queue(&tu->qchange_sleep, &wait);
+ if (tu->disconnected) {
+ err = -ENODEV;
+ goto _error;
+ }
if (signal_pending(current)) {
err = -ERESTARTSYS;
- break;
+ goto _error;
}
}
+ qhead = tu->qhead++;
+ tu->qhead %= tu->queue_size;
+ tu->qused--;
spin_unlock_irq(&tu->qlock);
- if (err < 0)
- goto _error;
- if (tu->tread) {
- if (copy_to_user(buffer, &tu->tqueue[tu->qhead++],
- sizeof(struct snd_timer_tread))) {
+ tread = &tu->tqueue[qhead];
+
+ switch (tu->tread) {
+ case TREAD_FORMAT_TIME64:
+ if (copy_to_user(buffer, tread,
+ sizeof(struct snd_timer_tread64)))
err = -EFAULT;
- goto _error;
- }
- } else {
- if (copy_to_user(buffer, &tu->queue[tu->qhead++],
- sizeof(struct snd_timer_read))) {
+ break;
+ case TREAD_FORMAT_TIME32:
+ memset(&tread32, 0, sizeof(tread32));
+ tread32 = (struct snd_timer_tread32) {
+ .event = tread->event,
+ .tstamp_sec = tread->tstamp_sec,
+ .tstamp_nsec = tread->tstamp_nsec,
+ .val = tread->val,
+ };
+
+ if (copy_to_user(buffer, &tread32, sizeof(tread32)))
err = -EFAULT;
- goto _error;
- }
+ break;
+ case TREAD_FORMAT_NONE:
+ if (copy_to_user(buffer, &tu->queue[qhead],
+ sizeof(struct snd_timer_read)))
+ err = -EFAULT;
+ break;
+ default:
+ err = -ENOTSUPP;
+ break;
}
- tu->qhead %= tu->queue_size;
-
+ spin_lock_irq(&tu->qlock);
+ if (err < 0)
+ goto _error;
result += unit;
buffer += unit;
-
- spin_lock_irq(&tu->qlock);
- tu->qused--;
}
- spin_unlock_irq(&tu->qlock);
_error:
+ spin_unlock_irq(&tu->qlock);
+ mutex_unlock(&tu->ioctl_lock);
return result > 0 ? result : err;
}
-static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait)
+static __poll_t snd_timer_user_poll(struct file *file, poll_table * wait)
{
- unsigned int mask;
+ __poll_t mask;
struct snd_timer_user *tu;
tu = file->private_data;
@@ -1909,8 +2422,11 @@ static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait)
poll_wait(file, &tu->qchange_sleep, wait);
mask = 0;
+ guard(spinlock_irq)(&tu->qlock);
if (tu->qused)
- mask |= POLLIN | POLLRDNORM;
+ mask |= EPOLLIN | EPOLLRDNORM;
+ if (tu->disconnected)
+ mask |= EPOLLERR;
return mask;
}
@@ -1927,13 +2443,23 @@ static const struct file_operations snd_timer_f_ops =
.read = snd_timer_user_read,
.open = snd_timer_user_open,
.release = snd_timer_user_release,
- .llseek = no_llseek,
.poll = snd_timer_user_poll,
.unlocked_ioctl = snd_timer_user_ioctl,
.compat_ioctl = snd_timer_user_ioctl_compat,
.fasync = snd_timer_user_fasync,
};
+/* unregister the system timer */
+static void snd_timer_free_all(void)
+{
+ struct snd_timer *timer, *n;
+
+ list_for_each_entry_safe(timer, n, &snd_timer_list, device_list)
+ snd_timer_free(timer);
+}
+
+static struct device *timer_dev;
+
/*
* ENTRY functions
*/
@@ -1942,32 +2468,43 @@ static int __init alsa_timer_init(void)
{
int err;
+ err = snd_device_alloc(&timer_dev, NULL);
+ if (err < 0)
+ return err;
+ dev_set_name(timer_dev, "timer");
+
#ifdef SNDRV_OSS_INFO_DEV_TIMERS
snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1,
"system timer");
#endif
- if ((err = snd_timer_register_system()) < 0)
- snd_printk(KERN_ERR "unable to register system timer (%i)\n",
- err);
- if ((err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0,
- &snd_timer_f_ops, NULL, "timer")) < 0)
- snd_printk(KERN_ERR "unable to register timer device (%i)\n",
- err);
+ err = snd_timer_register_system();
+ if (err < 0) {
+ pr_err("ALSA: unable to register system timer (%i)\n", err);
+ goto put_timer;
+ }
+
+ err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0,
+ &snd_timer_f_ops, NULL, timer_dev);
+ if (err < 0) {
+ pr_err("ALSA: unable to register timer device (%i)\n", err);
+ snd_timer_free_all();
+ goto put_timer;
+ }
+
snd_timer_proc_init();
return 0;
+
+put_timer:
+ put_device(timer_dev);
+ return err;
}
static void __exit alsa_timer_exit(void)
{
- struct list_head *p, *n;
-
- snd_unregister_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0);
- /* unregister the system timer */
- list_for_each_safe(p, n, &snd_timer_list) {
- struct snd_timer *timer = list_entry(p, struct snd_timer, device_list);
- snd_timer_free(timer);
- }
+ snd_unregister_device(timer_dev);
+ snd_timer_free_all();
+ put_device(timer_dev);
snd_timer_proc_done();
#ifdef SNDRV_OSS_INFO_DEV_TIMERS
snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1);
@@ -1976,17 +2513,3 @@ static void __exit alsa_timer_exit(void)
module_init(alsa_timer_init)
module_exit(alsa_timer_exit)
-
-EXPORT_SYMBOL(snd_timer_open);
-EXPORT_SYMBOL(snd_timer_close);
-EXPORT_SYMBOL(snd_timer_resolution);
-EXPORT_SYMBOL(snd_timer_start);
-EXPORT_SYMBOL(snd_timer_stop);
-EXPORT_SYMBOL(snd_timer_continue);
-EXPORT_SYMBOL(snd_timer_pause);
-EXPORT_SYMBOL(snd_timer_new);
-EXPORT_SYMBOL(snd_timer_notify);
-EXPORT_SYMBOL(snd_timer_global_new);
-EXPORT_SYMBOL(snd_timer_global_free);
-EXPORT_SYMBOL(snd_timer_global_register);
-EXPORT_SYMBOL(snd_timer_interrupt);
diff --git a/sound/core/timer_compat.c b/sound/core/timer_compat.c
index e05802ae6e1b..4ae9eaeb5afb 100644
--- a/sound/core/timer_compat.c
+++ b/sound/core/timer_compat.c
@@ -1,27 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 32bit -> 64bit ioctl wrapper for timer API
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/* This file included from timer.c */
#include <linux/compat.h>
+/*
+ * ILP32/LP64 has different size for 'long' type. Additionally, the size
+ * of storage alignment differs depending on architectures. Here, '__packed'
+ * qualifier is used so that the size of this structure is multiple of 4 and
+ * it fits to any architectures with 32 bit storage alignment.
+ */
+struct snd_timer_gparams32 {
+ struct snd_timer_id tid;
+ u32 period_num;
+ u32 period_den;
+ unsigned char reserved[32];
+} __packed;
+
struct snd_timer_info32 {
u32 flags;
s32 card;
@@ -32,6 +31,19 @@ struct snd_timer_info32 {
unsigned char reserved[64];
};
+static int snd_timer_user_gparams_compat(struct file *file,
+ struct snd_timer_gparams32 __user *user)
+{
+ struct snd_timer_gparams gparams;
+
+ if (copy_from_user(&gparams.tid, &user->tid, sizeof(gparams.tid)) ||
+ get_user(gparams.period_num, &user->period_num) ||
+ get_user(gparams.period_den, &user->period_den))
+ return -EFAULT;
+
+ return timer_set_gparams(&gparams);
+}
+
static int snd_timer_user_info_compat(struct file *file,
struct snd_timer_info32 __user *_info)
{
@@ -40,71 +52,40 @@ static int snd_timer_user_info_compat(struct file *file,
struct snd_timer *t;
tu = file->private_data;
- if (snd_BUG_ON(!tu->timeri))
- return -ENXIO;
+ if (!tu->timeri)
+ return -EBADFD;
t = tu->timeri->timer;
- if (snd_BUG_ON(!t))
- return -ENXIO;
+ if (!t)
+ return -EBADFD;
memset(&info, 0, sizeof(info));
info.card = t->card ? t->card->number : -1;
if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
info.flags |= SNDRV_TIMER_FLG_SLAVE;
- strlcpy(info.id, t->id, sizeof(info.id));
- strlcpy(info.name, t->name, sizeof(info.name));
+ strscpy(info.id, t->id, sizeof(info.id));
+ strscpy(info.name, t->name, sizeof(info.name));
info.resolution = t->hw.resolution;
if (copy_to_user(_info, &info, sizeof(*_info)))
return -EFAULT;
return 0;
}
-struct snd_timer_status32 {
- struct compat_timespec tstamp;
- u32 resolution;
- u32 lost;
- u32 overrun;
- u32 queue;
- unsigned char reserved[64];
-};
-
-static int snd_timer_user_status_compat(struct file *file,
- struct snd_timer_status32 __user *_status)
-{
- struct snd_timer_user *tu;
- struct snd_timer_status status;
-
- tu = file->private_data;
- if (snd_BUG_ON(!tu->timeri))
- return -ENXIO;
- memset(&status, 0, sizeof(status));
- status.tstamp = tu->tstamp;
- status.resolution = snd_timer_resolution(tu->timeri);
- status.lost = tu->timeri->lost;
- status.overrun = tu->overrun;
- spin_lock_irq(&tu->qlock);
- status.queue = tu->qused;
- spin_unlock_irq(&tu->qlock);
- if (copy_to_user(_status, &status, sizeof(status)))
- return -EFAULT;
- return 0;
-}
-
-/*
- */
-
enum {
+ SNDRV_TIMER_IOCTL_GPARAMS32 = _IOW('T', 0x04, struct snd_timer_gparams32),
SNDRV_TIMER_IOCTL_INFO32 = _IOR('T', 0x11, struct snd_timer_info32),
- SNDRV_TIMER_IOCTL_STATUS32 = _IOW('T', 0x14, struct snd_timer_status32),
+ SNDRV_TIMER_IOCTL_STATUS_COMPAT32 = _IOW('T', 0x14, struct snd_timer_status32),
+ SNDRV_TIMER_IOCTL_STATUS_COMPAT64 = _IOW('T', 0x14, struct snd_timer_status64),
};
-static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
+static long __snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
void __user *argp = compat_ptr(arg);
switch (cmd) {
case SNDRV_TIMER_IOCTL_PVERSION:
- case SNDRV_TIMER_IOCTL_TREAD:
+ case SNDRV_TIMER_IOCTL_TREAD_OLD:
+ case SNDRV_TIMER_IOCTL_TREAD64:
case SNDRV_TIMER_IOCTL_GINFO:
- case SNDRV_TIMER_IOCTL_GPARAMS:
case SNDRV_TIMER_IOCTL_GSTATUS:
case SNDRV_TIMER_IOCTL_SELECT:
case SNDRV_TIMER_IOCTL_PARAMS:
@@ -117,11 +98,24 @@ static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, uns
case SNDRV_TIMER_IOCTL_PAUSE:
case SNDRV_TIMER_IOCTL_PAUSE_OLD:
case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
- return snd_timer_user_ioctl(file, cmd, (unsigned long)argp);
+ return __snd_timer_user_ioctl(file, cmd, (unsigned long)argp, true);
+ case SNDRV_TIMER_IOCTL_GPARAMS32:
+ return snd_timer_user_gparams_compat(file, argp);
case SNDRV_TIMER_IOCTL_INFO32:
return snd_timer_user_info_compat(file, argp);
- case SNDRV_TIMER_IOCTL_STATUS32:
- return snd_timer_user_status_compat(file, argp);
+ case SNDRV_TIMER_IOCTL_STATUS_COMPAT32:
+ return snd_timer_user_status32(file, argp);
+ case SNDRV_TIMER_IOCTL_STATUS_COMPAT64:
+ return snd_timer_user_status64(file, argp);
}
return -ENOIOCTLCMD;
}
+
+static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct snd_timer_user *tu = file->private_data;
+
+ guard(mutex)(&tu->ioctl_lock);
+ return __snd_timer_user_ioctl_compat(file, cmd, arg);
+}
diff --git a/sound/core/ump.c b/sound/core/ump.c
new file mode 100644
index 000000000000..8d8681a42ca5
--- /dev/null
+++ b/sound/core/ump.c
@@ -0,0 +1,1394 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Universal MIDI Packet (UMP) support
+ */
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/mm.h>
+#include <sound/core.h>
+#include <sound/rawmidi.h>
+#include <sound/ump.h>
+#include <sound/ump_convert.h>
+
+#define ump_err(ump, fmt, args...) dev_err((ump)->core.dev, fmt, ##args)
+#define ump_warn(ump, fmt, args...) dev_warn((ump)->core.dev, fmt, ##args)
+#define ump_info(ump, fmt, args...) dev_info((ump)->core.dev, fmt, ##args)
+#define ump_dbg(ump, fmt, args...) dev_dbg((ump)->core.dev, fmt, ##args)
+
+static int snd_ump_dev_register(struct snd_rawmidi *rmidi);
+static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi);
+static long snd_ump_ioctl(struct snd_rawmidi *rmidi, unsigned int cmd,
+ void __user *argp);
+static void snd_ump_proc_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer);
+static int snd_ump_rawmidi_open(struct snd_rawmidi_substream *substream);
+static int snd_ump_rawmidi_close(struct snd_rawmidi_substream *substream);
+static void snd_ump_rawmidi_trigger(struct snd_rawmidi_substream *substream,
+ int up);
+static void snd_ump_rawmidi_drain(struct snd_rawmidi_substream *substream);
+
+static void ump_handle_stream_msg(struct snd_ump_endpoint *ump,
+ const u32 *buf, int size);
+#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
+static int process_legacy_output(struct snd_ump_endpoint *ump,
+ u32 *buffer, int count);
+static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src,
+ int words);
+static void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump);
+static void update_legacy_names(struct snd_ump_endpoint *ump);
+#else
+static inline int process_legacy_output(struct snd_ump_endpoint *ump,
+ u32 *buffer, int count)
+{
+ return 0;
+}
+static inline void process_legacy_input(struct snd_ump_endpoint *ump,
+ const u32 *src, int words)
+{
+}
+static inline void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump)
+{
+}
+static inline void update_legacy_names(struct snd_ump_endpoint *ump)
+{
+}
+#endif
+
+/* copy a string safely with stripping non-printable letters */
+static void safe_copy_string(void *dst, size_t max_dst_size,
+ const void *src, size_t max_src_size)
+{
+ const unsigned char *s = src;
+ unsigned char *d = dst;
+
+ if (!max_dst_size--)
+ return;
+ for (s = src; max_dst_size && *s && max_src_size--; s++) {
+ if (!isascii(*s) || !isprint(*s))
+ continue;
+ *d++ = *s;
+ max_dst_size--;
+ }
+ *d = 0;
+}
+
+/* append a string safely with stripping non-printable letters */
+static void safe_append_string(void *dst, size_t max_dst_size,
+ const void *src, size_t max_src_size)
+{
+ unsigned char *d = dst;
+ size_t len = strlen(d);
+
+ safe_copy_string(d + len, max_dst_size - len, src, max_src_size);
+}
+
+static const struct snd_rawmidi_global_ops snd_ump_rawmidi_ops = {
+ .dev_register = snd_ump_dev_register,
+ .dev_unregister = snd_ump_dev_unregister,
+ .ioctl = snd_ump_ioctl,
+ .proc_read = snd_ump_proc_read,
+};
+
+static const struct snd_rawmidi_ops snd_ump_rawmidi_input_ops = {
+ .open = snd_ump_rawmidi_open,
+ .close = snd_ump_rawmidi_close,
+ .trigger = snd_ump_rawmidi_trigger,
+};
+
+static const struct snd_rawmidi_ops snd_ump_rawmidi_output_ops = {
+ .open = snd_ump_rawmidi_open,
+ .close = snd_ump_rawmidi_close,
+ .trigger = snd_ump_rawmidi_trigger,
+ .drain = snd_ump_rawmidi_drain,
+};
+
+static void snd_ump_endpoint_free(struct snd_rawmidi *rmidi)
+{
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
+ struct snd_ump_block *fb;
+
+ while (!list_empty(&ump->block_list)) {
+ fb = list_first_entry(&ump->block_list, struct snd_ump_block,
+ list);
+ list_del(&fb->list);
+ if (fb->private_free)
+ fb->private_free(fb);
+ kfree(fb);
+ }
+
+ if (ump->private_free)
+ ump->private_free(ump);
+
+#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
+ kfree(ump->out_cvts);
+#endif
+}
+
+/**
+ * snd_ump_endpoint_new - create a UMP Endpoint object
+ * @card: the card instance
+ * @id: the id string for rawmidi
+ * @device: the device index for rawmidi
+ * @output: 1 for enabling output
+ * @input: 1 for enabling input
+ * @ump_ret: the pointer to store the new UMP instance
+ *
+ * Creates a new UMP Endpoint object. A UMP Endpoint is tied with one rawmidi
+ * instance with one input and/or one output rawmidi stream (either uni-
+ * or bi-directional). A UMP Endpoint may contain one or multiple UMP Blocks
+ * that consist of one or multiple UMP Groups.
+ *
+ * Use snd_rawmidi_set_ops() to set the operators to the new instance.
+ * Unlike snd_rawmidi_new(), this function sets up the info_flags by itself
+ * depending on the given @output and @input.
+ *
+ * The device has SNDRV_RAWMIDI_INFO_UMP flag set and a different device
+ * file ("umpCxDx") than a standard MIDI 1.x device ("midiCxDx") is
+ * created.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_ump_endpoint_new(struct snd_card *card, char *id, int device,
+ int output, int input,
+ struct snd_ump_endpoint **ump_ret)
+{
+ unsigned int info_flags = SNDRV_RAWMIDI_INFO_UMP;
+ struct snd_ump_endpoint *ump;
+ int err;
+
+ if (input)
+ info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+ if (output)
+ info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+ if (input && output)
+ info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ ump = kzalloc(sizeof(*ump), GFP_KERNEL);
+ if (!ump)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&ump->block_list);
+ mutex_init(&ump->open_mutex);
+ init_waitqueue_head(&ump->stream_wait);
+#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
+ spin_lock_init(&ump->legacy_locks[0]);
+ spin_lock_init(&ump->legacy_locks[1]);
+#endif
+ err = snd_rawmidi_init(&ump->core, card, id, device,
+ output, input, info_flags);
+ if (err < 0) {
+ snd_rawmidi_free(&ump->core);
+ return err;
+ }
+
+ ump->info.card = card->number;
+ ump->info.device = device;
+
+ ump->core.private_free = snd_ump_endpoint_free;
+ ump->core.ops = &snd_ump_rawmidi_ops;
+ if (input)
+ snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_INPUT,
+ &snd_ump_rawmidi_input_ops);
+ if (output)
+ snd_rawmidi_set_ops(&ump->core, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &snd_ump_rawmidi_output_ops);
+
+ ump_dbg(ump, "Created a UMP EP #%d (%s)\n", device, id);
+ *ump_ret = ump;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ump_endpoint_new);
+
+/*
+ * Device register / unregister hooks;
+ * do nothing, placeholders for avoiding the default rawmidi handling
+ */
+
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+static void snd_ump_dev_seq_free(struct snd_seq_device *device)
+{
+ struct snd_ump_endpoint *ump = device->private_data;
+
+ ump->seq_dev = NULL;
+}
+#endif
+
+static int snd_ump_dev_register(struct snd_rawmidi *rmidi)
+{
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
+ int err;
+
+ err = snd_seq_device_new(ump->core.card, ump->core.device,
+ SNDRV_SEQ_DEV_ID_UMP, 0, &ump->seq_dev);
+ if (err < 0)
+ return err;
+ ump->seq_dev->private_data = ump;
+ ump->seq_dev->private_free = snd_ump_dev_seq_free;
+ snd_device_register(ump->core.card, ump->seq_dev);
+#endif
+ return 0;
+}
+
+static int snd_ump_dev_unregister(struct snd_rawmidi *rmidi)
+{
+ return 0;
+}
+
+static struct snd_ump_block *
+snd_ump_get_block(struct snd_ump_endpoint *ump, unsigned char id)
+{
+ struct snd_ump_block *fb;
+
+ list_for_each_entry(fb, &ump->block_list, list) {
+ if (fb->info.block_id == id)
+ return fb;
+ }
+ return NULL;
+}
+
+/*
+ * rawmidi ops for UMP endpoint
+ */
+static int snd_ump_rawmidi_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
+ int dir = substream->stream;
+ int err;
+
+ if (ump->substreams[dir])
+ return -EBUSY;
+ err = ump->ops->open(ump, dir);
+ if (err < 0)
+ return err;
+ ump->substreams[dir] = substream;
+ return 0;
+}
+
+static int snd_ump_rawmidi_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
+ int dir = substream->stream;
+
+ ump->substreams[dir] = NULL;
+ ump->ops->close(ump, dir);
+ return 0;
+}
+
+static void snd_ump_rawmidi_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
+ int dir = substream->stream;
+
+ ump->ops->trigger(ump, dir, up);
+}
+
+static void snd_ump_rawmidi_drain(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(substream->rmidi);
+
+ if (ump->ops->drain)
+ ump->ops->drain(ump, SNDRV_RAWMIDI_STREAM_OUTPUT);
+}
+
+/* number of 32bit words per message type */
+static unsigned char ump_packet_words[0x10] = {
+ 1, 1, 1, 2, 2, 4, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4
+};
+
+/**
+ * snd_ump_receive_ump_val - parse the UMP packet data
+ * @ump: UMP endpoint
+ * @val: UMP packet data
+ *
+ * The data is copied onto ump->input_buf[].
+ * When a full packet is completed, returns the number of words (from 1 to 4).
+ * OTOH, if the packet is incomplete, returns 0.
+ */
+int snd_ump_receive_ump_val(struct snd_ump_endpoint *ump, u32 val)
+{
+ int words;
+
+ if (!ump->input_pending)
+ ump->input_pending = ump_packet_words[ump_message_type(val)];
+
+ ump->input_buf[ump->input_buf_head++] = val;
+ ump->input_pending--;
+ if (!ump->input_pending) {
+ words = ump->input_buf_head;
+ ump->input_buf_head = 0;
+ return words;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ump_receive_ump_val);
+
+/**
+ * snd_ump_receive - transfer UMP packets from the device
+ * @ump: the UMP endpoint
+ * @buffer: the buffer pointer to transfer
+ * @count: byte size to transfer
+ *
+ * Called from the driver to submit the received UMP packets from the device
+ * to user-space. It's essentially a wrapper of rawmidi_receive().
+ * The data to receive is in CPU-native endianness.
+ */
+int snd_ump_receive(struct snd_ump_endpoint *ump, const u32 *buffer, int count)
+{
+ struct snd_rawmidi_substream *substream;
+ const u32 *p = buffer;
+ int n, words = count >> 2;
+
+ while (words--) {
+ n = snd_ump_receive_ump_val(ump, *p++);
+ if (!n)
+ continue;
+ ump_handle_stream_msg(ump, ump->input_buf, n);
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+ if (ump->seq_ops)
+ ump->seq_ops->input_receive(ump, ump->input_buf, n);
+#endif
+ process_legacy_input(ump, ump->input_buf, n);
+ }
+
+ substream = ump->substreams[SNDRV_RAWMIDI_STREAM_INPUT];
+ if (!substream)
+ return 0;
+ return snd_rawmidi_receive(substream, (const char *)buffer, count);
+}
+EXPORT_SYMBOL_GPL(snd_ump_receive);
+
+/**
+ * snd_ump_transmit - transmit UMP packets
+ * @ump: the UMP endpoint
+ * @buffer: the buffer pointer to transfer
+ * @count: byte size to transfer
+ *
+ * Called from the driver to obtain the UMP packets from user-space to the
+ * device. It's essentially a wrapper of rawmidi_transmit().
+ * The data to transmit is in CPU-native endianness.
+ */
+int snd_ump_transmit(struct snd_ump_endpoint *ump, u32 *buffer, int count)
+{
+ struct snd_rawmidi_substream *substream =
+ ump->substreams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+ int err;
+
+ if (!substream)
+ return -ENODEV;
+ err = snd_rawmidi_transmit(substream, (char *)buffer, count);
+ /* received either data or an error? */
+ if (err)
+ return err;
+ return process_legacy_output(ump, buffer, count);
+}
+EXPORT_SYMBOL_GPL(snd_ump_transmit);
+
+/**
+ * snd_ump_block_new - Create a UMP block
+ * @ump: UMP object
+ * @blk: block ID number to create
+ * @direction: direction (in/out/bidirection)
+ * @first_group: the first group ID (0-based)
+ * @num_groups: the number of groups in this block
+ * @blk_ret: the pointer to store the resultant block object
+ */
+int snd_ump_block_new(struct snd_ump_endpoint *ump, unsigned int blk,
+ unsigned int direction, unsigned int first_group,
+ unsigned int num_groups, struct snd_ump_block **blk_ret)
+{
+ struct snd_ump_block *fb, *p;
+
+ if (blk >= SNDRV_UMP_MAX_BLOCKS)
+ return -EINVAL;
+
+ if (snd_ump_get_block(ump, blk))
+ return -EBUSY;
+
+ fb = kzalloc(sizeof(*fb), GFP_KERNEL);
+ if (!fb)
+ return -ENOMEM;
+
+ fb->ump = ump;
+ fb->info.card = ump->info.card;
+ fb->info.device = ump->info.device;
+ fb->info.block_id = blk;
+ if (blk >= ump->info.num_blocks)
+ ump->info.num_blocks = blk + 1;
+ fb->info.direction = direction;
+ fb->info.active = 1;
+ fb->info.first_group = first_group;
+ fb->info.num_groups = num_groups;
+ /* fill the default name, may be overwritten to a better name */
+ snprintf(fb->info.name, sizeof(fb->info.name), "Group %u-%u",
+ first_group + 1, first_group + num_groups);
+
+ /* put the entry in the ordered list */
+ list_for_each_entry(p, &ump->block_list, list) {
+ if (p->info.block_id > blk) {
+ list_add_tail(&fb->list, &p->list);
+ goto added;
+ }
+ }
+ list_add_tail(&fb->list, &ump->block_list);
+
+ added:
+ ump_dbg(ump, "Created a UMP Block #%d (%s)\n", blk, fb->info.name);
+ *blk_ret = fb;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ump_block_new);
+
+static int snd_ump_ioctl_block(struct snd_ump_endpoint *ump,
+ struct snd_ump_block_info __user *argp)
+{
+ struct snd_ump_block *fb;
+ unsigned char id;
+
+ if (get_user(id, &argp->block_id))
+ return -EFAULT;
+ fb = snd_ump_get_block(ump, id);
+ if (!fb)
+ return -ENOENT;
+ if (copy_to_user(argp, &fb->info, sizeof(fb->info)))
+ return -EFAULT;
+ return 0;
+}
+
+/*
+ * Handle UMP-specific ioctls; called from snd_rawmidi_ioctl()
+ */
+static long snd_ump_ioctl(struct snd_rawmidi *rmidi, unsigned int cmd,
+ void __user *argp)
+{
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
+
+ switch (cmd) {
+ case SNDRV_UMP_IOCTL_ENDPOINT_INFO:
+ if (copy_to_user(argp, &ump->info, sizeof(ump->info)))
+ return -EFAULT;
+ return 0;
+ case SNDRV_UMP_IOCTL_BLOCK_INFO:
+ return snd_ump_ioctl_block(ump, argp);
+ default:
+ ump_dbg(ump, "rawmidi: unknown command = 0x%x\n", cmd);
+ return -ENOTTY;
+ }
+}
+
+static const char *ump_direction_string(int dir)
+{
+ switch (dir) {
+ case SNDRV_UMP_DIR_INPUT:
+ return "input";
+ case SNDRV_UMP_DIR_OUTPUT:
+ return "output";
+ case SNDRV_UMP_DIR_BIDIRECTION:
+ return "bidirection";
+ default:
+ return "unknown";
+ }
+}
+
+static const char *ump_ui_hint_string(int dir)
+{
+ switch (dir) {
+ case SNDRV_UMP_BLOCK_UI_HINT_RECEIVER:
+ return "receiver";
+ case SNDRV_UMP_BLOCK_UI_HINT_SENDER:
+ return "sender";
+ case SNDRV_UMP_BLOCK_UI_HINT_BOTH:
+ return "both";
+ default:
+ return "unknown";
+ }
+}
+
+/* Additional proc file output */
+static void snd_ump_proc_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_rawmidi *rmidi = entry->private_data;
+ struct snd_ump_endpoint *ump = rawmidi_to_ump(rmidi);
+ struct snd_ump_block *fb;
+
+ snd_iprintf(buffer, "EP Name: %s\n", ump->info.name);
+ snd_iprintf(buffer, "EP Product ID: %s\n", ump->info.product_id);
+ snd_iprintf(buffer, "UMP Version: 0x%04x\n", ump->info.version);
+ snd_iprintf(buffer, "Protocol Caps: 0x%08x\n", ump->info.protocol_caps);
+ snd_iprintf(buffer, "Protocol: 0x%08x\n", ump->info.protocol);
+ if (ump->info.version) {
+ snd_iprintf(buffer, "Manufacturer ID: 0x%08x\n",
+ ump->info.manufacturer_id);
+ snd_iprintf(buffer, "Family ID: 0x%04x\n", ump->info.family_id);
+ snd_iprintf(buffer, "Model ID: 0x%04x\n", ump->info.model_id);
+ snd_iprintf(buffer, "SW Revision: 0x%4phN\n", ump->info.sw_revision);
+ }
+ snd_iprintf(buffer, "Static Blocks: %s\n",
+ (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS) ? "Yes" : "No");
+ snd_iprintf(buffer, "Num Blocks: %d\n\n", ump->info.num_blocks);
+
+ list_for_each_entry(fb, &ump->block_list, list) {
+ snd_iprintf(buffer, "Block %d (%s)\n", fb->info.block_id,
+ fb->info.name);
+ snd_iprintf(buffer, " Direction: %s\n",
+ ump_direction_string(fb->info.direction));
+ snd_iprintf(buffer, " Active: %s\n",
+ fb->info.active ? "Yes" : "No");
+ snd_iprintf(buffer, " Groups: %d-%d\n",
+ fb->info.first_group + 1,
+ fb->info.first_group + fb->info.num_groups);
+ snd_iprintf(buffer, " Is MIDI1: %s%s\n",
+ (fb->info.flags & SNDRV_UMP_BLOCK_IS_MIDI1) ? "Yes" : "No",
+ (fb->info.flags & SNDRV_UMP_BLOCK_IS_LOWSPEED) ? " (Low Speed)" : "");
+ if (ump->info.version) {
+ snd_iprintf(buffer, " MIDI-CI Version: %d\n",
+ fb->info.midi_ci_version);
+ snd_iprintf(buffer, " Sysex8 Streams: %d\n",
+ fb->info.sysex8_streams);
+ snd_iprintf(buffer, " UI Hint: %s\n",
+ ump_ui_hint_string(fb->info.ui_hint));
+ }
+ snd_iprintf(buffer, "\n");
+ }
+}
+
+/* update dir_bits and active flag for all groups in the client */
+void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump)
+{
+ struct snd_ump_block *fb;
+ struct snd_ump_group *group;
+ int i;
+
+ for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++) {
+ group = &ump->groups[i];
+ *group->name = 0;
+ group->dir_bits = 0;
+ group->active = 0;
+ group->group = i;
+ group->valid = false;
+ group->is_midi1 = false;
+ }
+
+ list_for_each_entry(fb, &ump->block_list, list) {
+ if (fb->info.first_group + fb->info.num_groups > SNDRV_UMP_MAX_GROUPS)
+ break;
+ group = &ump->groups[fb->info.first_group];
+ for (i = 0; i < fb->info.num_groups; i++, group++) {
+ group->valid = true;
+ if (fb->info.active)
+ group->active = 1;
+ if (fb->info.flags & SNDRV_UMP_BLOCK_IS_MIDI1)
+ group->is_midi1 = true;
+ switch (fb->info.direction) {
+ case SNDRV_UMP_DIR_INPUT:
+ group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_INPUT);
+ break;
+ case SNDRV_UMP_DIR_OUTPUT:
+ group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_OUTPUT);
+ break;
+ case SNDRV_UMP_DIR_BIDIRECTION:
+ group->dir_bits |= (1 << SNDRV_RAWMIDI_STREAM_INPUT) |
+ (1 << SNDRV_RAWMIDI_STREAM_OUTPUT);
+ break;
+ }
+ if (!*fb->info.name)
+ continue;
+ if (*group->name)
+ strlcat(group->name, ", ", sizeof(group->name));
+ safe_append_string(group->name, sizeof(group->name),
+ fb->info.name, sizeof(fb->info.name));
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(snd_ump_update_group_attrs);
+
+/*
+ * UMP endpoint and function block handling
+ */
+
+/* open / close UMP streams for the internal stream msg communication */
+static int ump_request_open(struct snd_ump_endpoint *ump)
+{
+ return snd_rawmidi_kernel_open(&ump->core, 0,
+ SNDRV_RAWMIDI_LFLG_OUTPUT,
+ &ump->stream_rfile);
+}
+
+static void ump_request_close(struct snd_ump_endpoint *ump)
+{
+ snd_rawmidi_kernel_release(&ump->stream_rfile);
+}
+
+/* request a command and wait for the given response;
+ * @req1 and @req2 are u32 commands
+ * @reply is the expected UMP stream status
+ */
+static int ump_req_msg(struct snd_ump_endpoint *ump, u32 req1, u32 req2,
+ u32 reply)
+{
+ u32 buf[4];
+
+ ump_dbg(ump, "%s: request %08x %08x, wait-for %08x\n",
+ __func__, req1, req2, reply);
+ memset(buf, 0, sizeof(buf));
+ buf[0] = req1;
+ buf[1] = req2;
+ ump->stream_finished = 0;
+ ump->stream_wait_for = reply;
+ snd_rawmidi_kernel_write(ump->stream_rfile.output,
+ (unsigned char *)&buf, 16);
+ wait_event_timeout(ump->stream_wait, ump->stream_finished,
+ msecs_to_jiffies(500));
+ if (!READ_ONCE(ump->stream_finished)) {
+ ump_dbg(ump, "%s: request timed out\n", __func__);
+ return -ETIMEDOUT;
+ }
+ ump->stream_finished = 0;
+ ump_dbg(ump, "%s: reply: %08x %08x %08x %08x\n",
+ __func__, buf[0], buf[1], buf[2], buf[3]);
+ return 0;
+}
+
+/* append the received letters via UMP packet to the given string buffer;
+ * return 1 if the full string is received or 0 to continue
+ */
+static int ump_append_string(struct snd_ump_endpoint *ump, char *dest,
+ int maxsize, const u32 *buf, int offset)
+{
+ unsigned char format;
+ int c;
+
+ format = ump_stream_message_format(buf[0]);
+ if (format == UMP_STREAM_MSG_FORMAT_SINGLE ||
+ format == UMP_STREAM_MSG_FORMAT_START) {
+ c = 0;
+ } else {
+ c = strlen(dest);
+ if (c >= maxsize - 1)
+ return 1;
+ }
+
+ for (; offset < 16; offset++) {
+ dest[c] = buf[offset / 4] >> (3 - (offset % 4)) * 8;
+ if (!dest[c])
+ break;
+ if (++c >= maxsize - 1)
+ break;
+ }
+ dest[c] = 0;
+ return (format == UMP_STREAM_MSG_FORMAT_SINGLE ||
+ format == UMP_STREAM_MSG_FORMAT_END);
+}
+
+/* Choose the default protocol */
+static void choose_default_protocol(struct snd_ump_endpoint *ump)
+{
+ if (ump->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK)
+ return;
+ if (ump->info.protocol_caps & SNDRV_UMP_EP_INFO_PROTO_MIDI2)
+ ump->info.protocol |= SNDRV_UMP_EP_INFO_PROTO_MIDI2;
+ else
+ ump->info.protocol |= SNDRV_UMP_EP_INFO_PROTO_MIDI1;
+}
+
+/* notify the EP info/name change to sequencer */
+static void seq_notify_ep_change(struct snd_ump_endpoint *ump)
+{
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+ if (ump->parsed && ump->seq_ops && ump->seq_ops->notify_ep_change)
+ ump->seq_ops->notify_ep_change(ump);
+#endif
+}
+
+/* handle EP info stream message; update the UMP attributes */
+static int ump_handle_ep_info_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ ump->info.version = (buf->ep_info.ump_version_major << 8) |
+ buf->ep_info.ump_version_minor;
+ ump->info.num_blocks = buf->ep_info.num_function_blocks;
+ if (ump->info.num_blocks > SNDRV_UMP_MAX_BLOCKS) {
+ ump_info(ump, "Invalid function blocks %d, fallback to 1\n",
+ ump->info.num_blocks);
+ ump->info.num_blocks = 1;
+ }
+
+ if (buf->ep_info.static_function_block)
+ ump->info.flags |= SNDRV_UMP_EP_INFO_STATIC_BLOCKS;
+
+ ump->info.protocol_caps = (buf->ep_info.protocol << 8) |
+ buf->ep_info.jrts;
+
+ ump_dbg(ump, "EP info: version=%x, num_blocks=%x, proto_caps=%x\n",
+ ump->info.version, ump->info.num_blocks, ump->info.protocol_caps);
+
+ ump->info.protocol &= ump->info.protocol_caps;
+ choose_default_protocol(ump);
+ seq_notify_ep_change(ump);
+
+ return 1; /* finished */
+}
+
+/* handle EP device info stream message; update the UMP attributes */
+static int ump_handle_device_info_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ ump->info.manufacturer_id = buf->device_info.manufacture_id & 0x7f7f7f;
+ ump->info.family_id = (buf->device_info.family_msb << 8) |
+ buf->device_info.family_lsb;
+ ump->info.model_id = (buf->device_info.model_msb << 8) |
+ buf->device_info.model_lsb;
+ ump->info.sw_revision[0] = (buf->device_info.sw_revision >> 24) & 0x7f;
+ ump->info.sw_revision[1] = (buf->device_info.sw_revision >> 16) & 0x7f;
+ ump->info.sw_revision[2] = (buf->device_info.sw_revision >> 8) & 0x7f;
+ ump->info.sw_revision[3] = buf->device_info.sw_revision & 0x7f;
+ ump_dbg(ump, "EP devinfo: manid=%08x, family=%04x, model=%04x, sw=%4phN\n",
+ ump->info.manufacturer_id,
+ ump->info.family_id,
+ ump->info.model_id,
+ ump->info.sw_revision);
+ seq_notify_ep_change(ump);
+ return 1; /* finished */
+}
+
+/* set up the core rawmidi name from UMP EP name string */
+static void ump_set_rawmidi_name(struct snd_ump_endpoint *ump)
+{
+ safe_copy_string(ump->core.name, sizeof(ump->core.name),
+ ump->info.name, sizeof(ump->info.name));
+}
+
+/* handle EP name stream message; update the UMP name string */
+static int ump_handle_ep_name_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ int ret;
+
+ ret = ump_append_string(ump, ump->info.name, sizeof(ump->info.name),
+ buf->raw, 2);
+ if (ret && ump->parsed) {
+ ump_set_rawmidi_name(ump);
+ ump_legacy_set_rawmidi_name(ump);
+ seq_notify_ep_change(ump);
+ }
+
+ return ret;
+}
+
+/* handle EP product id stream message; update the UMP product_id string */
+static int ump_handle_product_id_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ int ret;
+
+ ret = ump_append_string(ump, ump->info.product_id,
+ sizeof(ump->info.product_id),
+ buf->raw, 2);
+ if (ret)
+ seq_notify_ep_change(ump);
+ return ret;
+}
+
+/* notify the protocol change to sequencer */
+static void seq_notify_protocol(struct snd_ump_endpoint *ump)
+{
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+ if (ump->seq_ops && ump->seq_ops->switch_protocol)
+ ump->seq_ops->switch_protocol(ump);
+#endif /* CONFIG_SND_SEQUENCER */
+}
+
+/**
+ * snd_ump_switch_protocol - switch MIDI protocol
+ * @ump: UMP endpoint
+ * @protocol: protocol to switch to
+ *
+ * Returns 1 if the protocol is actually switched, 0 if unchanged
+ */
+int snd_ump_switch_protocol(struct snd_ump_endpoint *ump, unsigned int protocol)
+{
+ unsigned int type;
+
+ protocol &= ump->info.protocol_caps;
+ if (protocol == ump->info.protocol)
+ return 0;
+
+ type = protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK;
+ if (type != SNDRV_UMP_EP_INFO_PROTO_MIDI1 &&
+ type != SNDRV_UMP_EP_INFO_PROTO_MIDI2)
+ return 0;
+
+ ump->info.protocol = protocol;
+ ump_dbg(ump, "New protocol = %x (caps = %x)\n",
+ protocol, ump->info.protocol_caps);
+ seq_notify_protocol(ump);
+ return 1;
+}
+EXPORT_SYMBOL_GPL(snd_ump_switch_protocol);
+
+/* handle EP stream config message; update the UMP protocol */
+static int ump_handle_stream_cfg_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ unsigned int protocol =
+ (buf->stream_cfg.protocol << 8) | buf->stream_cfg.jrts;
+
+ snd_ump_switch_protocol(ump, protocol);
+ return 1; /* finished */
+}
+
+/* Extract Function Block info from UMP packet */
+static void fill_fb_info(struct snd_ump_endpoint *ump,
+ struct snd_ump_block_info *info,
+ const union snd_ump_stream_msg *buf)
+{
+ info->direction = buf->fb_info.direction;
+ info->ui_hint = buf->fb_info.ui_hint;
+ info->first_group = buf->fb_info.first_group;
+ info->num_groups = buf->fb_info.num_groups;
+ if (buf->fb_info.midi_10 < 2)
+ info->flags = buf->fb_info.midi_10;
+ else
+ info->flags = SNDRV_UMP_BLOCK_IS_MIDI1 | SNDRV_UMP_BLOCK_IS_LOWSPEED;
+ info->active = buf->fb_info.active;
+ info->midi_ci_version = buf->fb_info.midi_ci_version;
+ info->sysex8_streams = buf->fb_info.sysex8_streams;
+
+ ump_dbg(ump, "FB %d: dir=%d, active=%d, first_gp=%d, num_gp=%d, midici=%d, sysex8=%d, flags=0x%x\n",
+ info->block_id, info->direction, info->active,
+ info->first_group, info->num_groups, info->midi_ci_version,
+ info->sysex8_streams, info->flags);
+
+ if ((info->flags & SNDRV_UMP_BLOCK_IS_MIDI1) && info->num_groups != 1) {
+ info->num_groups = 1;
+ ump_dbg(ump, "FB %d: corrected groups to 1 for MIDI1\n",
+ info->block_id);
+ }
+}
+
+/* check whether the FB info gets updated by the current message */
+static bool is_fb_info_updated(struct snd_ump_endpoint *ump,
+ struct snd_ump_block *fb,
+ const union snd_ump_stream_msg *buf)
+{
+ char tmpbuf[offsetof(struct snd_ump_block_info, name)];
+
+ if (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS) {
+ ump_info(ump, "Skipping static FB info update (blk#%d)\n",
+ fb->info.block_id);
+ return 0;
+ }
+
+ memcpy(tmpbuf, &fb->info, sizeof(tmpbuf));
+ fill_fb_info(ump, (struct snd_ump_block_info *)tmpbuf, buf);
+ return memcmp(&fb->info, tmpbuf, sizeof(tmpbuf)) != 0;
+}
+
+/* notify the FB info/name change to sequencer */
+static void seq_notify_fb_change(struct snd_ump_endpoint *ump,
+ struct snd_ump_block *fb)
+{
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+ if (ump->seq_ops && ump->seq_ops->notify_fb_change)
+ ump->seq_ops->notify_fb_change(ump, fb);
+#endif
+}
+
+/* handle FB info message; update FB info if the block is present */
+static int ump_handle_fb_info_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ unsigned char blk;
+ struct snd_ump_block *fb;
+
+ blk = buf->fb_info.function_block_id;
+ fb = snd_ump_get_block(ump, blk);
+
+ /* complain only if updated after parsing */
+ if (!fb && ump->parsed) {
+ ump_info(ump, "Function Block Info Update for non-existing block %d\n",
+ blk);
+ return -ENODEV;
+ }
+
+ /* When updated after the initial parse, check the FB info update */
+ if (ump->parsed && !is_fb_info_updated(ump, fb, buf))
+ return 1; /* no content change */
+
+ if (fb) {
+ fill_fb_info(ump, &fb->info, buf);
+ if (ump->parsed) {
+ snd_ump_update_group_attrs(ump);
+ update_legacy_names(ump);
+ seq_notify_fb_change(ump, fb);
+ }
+ }
+
+ return 1; /* finished */
+}
+
+/* handle FB name message; update the FB name string */
+static int ump_handle_fb_name_msg(struct snd_ump_endpoint *ump,
+ const union snd_ump_stream_msg *buf)
+{
+ unsigned char blk;
+ struct snd_ump_block *fb;
+ int ret;
+
+ blk = buf->fb_name.function_block_id;
+ fb = snd_ump_get_block(ump, blk);
+ if (!fb)
+ return -ENODEV;
+
+ if (ump->parsed &&
+ (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS)) {
+ ump_dbg(ump, "Skipping static FB name update (blk#%d)\n",
+ fb->info.block_id);
+ return 0;
+ }
+
+ ret = ump_append_string(ump, fb->info.name, sizeof(fb->info.name),
+ buf->raw, 3);
+ /* notify the FB name update to sequencer, too */
+ if (ret > 0 && ump->parsed) {
+ snd_ump_update_group_attrs(ump);
+ update_legacy_names(ump);
+ seq_notify_fb_change(ump, fb);
+ }
+ return ret;
+}
+
+static int create_block_from_fb_info(struct snd_ump_endpoint *ump, int blk)
+{
+ struct snd_ump_block *fb;
+ unsigned char direction, first_group, num_groups;
+ const union snd_ump_stream_msg *buf =
+ (const union snd_ump_stream_msg *)ump->input_buf;
+ u32 msg;
+ int err;
+
+ /* query the FB info once */
+ msg = ump_stream_compose(UMP_STREAM_MSG_STATUS_FB_DISCOVERY, 0) |
+ (blk << 8) | UMP_STREAM_MSG_REQUEST_FB_INFO;
+ err = ump_req_msg(ump, msg, 0, UMP_STREAM_MSG_STATUS_FB_INFO);
+ if (err < 0) {
+ ump_dbg(ump, "Unable to get FB info for block %d\n", blk);
+ return err;
+ }
+
+ /* the last input must be the FB info */
+ if (buf->fb_info.status != UMP_STREAM_MSG_STATUS_FB_INFO) {
+ ump_dbg(ump, "Inconsistent input: 0x%x\n", *buf->raw);
+ return -EINVAL;
+ }
+
+ direction = buf->fb_info.direction;
+ first_group = buf->fb_info.first_group;
+ num_groups = buf->fb_info.num_groups;
+
+ err = snd_ump_block_new(ump, blk, direction, first_group, num_groups,
+ &fb);
+ if (err < 0)
+ return err;
+
+ fill_fb_info(ump, &fb->info, buf);
+
+ msg = ump_stream_compose(UMP_STREAM_MSG_STATUS_FB_DISCOVERY, 0) |
+ (blk << 8) | UMP_STREAM_MSG_REQUEST_FB_NAME;
+ err = ump_req_msg(ump, msg, 0, UMP_STREAM_MSG_STATUS_FB_NAME);
+ if (err)
+ ump_dbg(ump, "Unable to get UMP FB name string #%d\n", blk);
+
+ return 0;
+}
+
+/* handle stream messages, called from snd_ump_receive() */
+static void ump_handle_stream_msg(struct snd_ump_endpoint *ump,
+ const u32 *buf, int size)
+{
+ const union snd_ump_stream_msg *msg;
+ unsigned int status;
+ int ret;
+
+ /* UMP stream message suppressed (for gadget UMP)? */
+ if (ump->no_process_stream)
+ return;
+
+ BUILD_BUG_ON(sizeof(*msg) != 16);
+ ump_dbg(ump, "Stream msg: %08x %08x %08x %08x\n",
+ buf[0], buf[1], buf[2], buf[3]);
+
+ if (size != 4 || ump_message_type(*buf) != UMP_MSG_TYPE_STREAM)
+ return;
+
+ msg = (const union snd_ump_stream_msg *)buf;
+ status = ump_stream_message_status(*buf);
+ switch (status) {
+ case UMP_STREAM_MSG_STATUS_EP_INFO:
+ ret = ump_handle_ep_info_msg(ump, msg);
+ break;
+ case UMP_STREAM_MSG_STATUS_DEVICE_INFO:
+ ret = ump_handle_device_info_msg(ump, msg);
+ break;
+ case UMP_STREAM_MSG_STATUS_EP_NAME:
+ ret = ump_handle_ep_name_msg(ump, msg);
+ break;
+ case UMP_STREAM_MSG_STATUS_PRODUCT_ID:
+ ret = ump_handle_product_id_msg(ump, msg);
+ break;
+ case UMP_STREAM_MSG_STATUS_STREAM_CFG:
+ ret = ump_handle_stream_cfg_msg(ump, msg);
+ break;
+ case UMP_STREAM_MSG_STATUS_FB_INFO:
+ ret = ump_handle_fb_info_msg(ump, msg);
+ break;
+ case UMP_STREAM_MSG_STATUS_FB_NAME:
+ ret = ump_handle_fb_name_msg(ump, msg);
+ break;
+ default:
+ return;
+ }
+
+ /* when the message has been processed fully, wake up */
+ if (ret > 0 && ump->stream_wait_for == status) {
+ WRITE_ONCE(ump->stream_finished, 1);
+ wake_up(&ump->stream_wait);
+ }
+}
+
+/**
+ * snd_ump_parse_endpoint - parse endpoint and create function blocks
+ * @ump: UMP object
+ *
+ * Returns 0 for successful parse, -ENODEV if device doesn't respond
+ * (or the query is unsupported), or other error code for serious errors.
+ */
+int snd_ump_parse_endpoint(struct snd_ump_endpoint *ump)
+{
+ int blk, err;
+ u32 msg;
+
+ if (!(ump->core.info_flags & SNDRV_RAWMIDI_INFO_DUPLEX))
+ return -ENODEV;
+
+ err = ump_request_open(ump);
+ if (err < 0) {
+ ump_dbg(ump, "Unable to open rawmidi device: %d\n", err);
+ return err;
+ }
+
+ /* Check Endpoint Information */
+ msg = ump_stream_compose(UMP_STREAM_MSG_STATUS_EP_DISCOVERY, 0) |
+ 0x0101; /* UMP version 1.1 */
+ err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_EP_INFO,
+ UMP_STREAM_MSG_STATUS_EP_INFO);
+ if (err < 0) {
+ ump_dbg(ump, "Unable to get UMP EP info\n");
+ goto error;
+ }
+
+ /* Request Endpoint Device Info */
+ err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_DEVICE_INFO,
+ UMP_STREAM_MSG_STATUS_DEVICE_INFO);
+ if (err < 0)
+ ump_dbg(ump, "Unable to get UMP EP device info\n");
+
+ /* Request Endpoint Name */
+ err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_EP_NAME,
+ UMP_STREAM_MSG_STATUS_EP_NAME);
+ if (err < 0)
+ ump_dbg(ump, "Unable to get UMP EP name string\n");
+
+ ump_set_rawmidi_name(ump);
+
+ /* Request Endpoint Product ID */
+ err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_PRODUCT_ID,
+ UMP_STREAM_MSG_STATUS_PRODUCT_ID);
+ if (err < 0)
+ ump_dbg(ump, "Unable to get UMP EP product ID string\n");
+
+ /* Get the current stream configuration */
+ err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_STREAM_CFG,
+ UMP_STREAM_MSG_STATUS_STREAM_CFG);
+ if (err < 0)
+ ump_dbg(ump, "Unable to get UMP EP stream config\n");
+
+ /* If no protocol is set by some reason, assume the valid one */
+ choose_default_protocol(ump);
+
+ /* Query and create blocks from Function Blocks */
+ for (blk = 0; blk < ump->info.num_blocks; blk++) {
+ err = create_block_from_fb_info(ump, blk);
+ if (err < 0)
+ continue;
+ }
+
+ /* initialize group attributions */
+ snd_ump_update_group_attrs(ump);
+
+ error:
+ ump->parsed = true;
+ ump_request_close(ump);
+ if (err == -ETIMEDOUT)
+ err = -ENODEV;
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_ump_parse_endpoint);
+
+#if IS_ENABLED(CONFIG_SND_UMP_LEGACY_RAWMIDI)
+/*
+ * Legacy rawmidi support
+ */
+static int snd_ump_legacy_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ump_endpoint *ump = substream->rmidi->private_data;
+ int dir = substream->stream;
+ int group = ump->legacy_mapping[substream->number];
+ int err;
+
+ guard(mutex)(&ump->open_mutex);
+ if (ump->legacy_substreams[dir][group])
+ return -EBUSY;
+ if (!ump->groups[group].active)
+ return -ENODEV;
+ if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) {
+ if (!ump->legacy_out_opens) {
+ err = snd_rawmidi_kernel_open(&ump->core, 0,
+ SNDRV_RAWMIDI_LFLG_OUTPUT |
+ SNDRV_RAWMIDI_LFLG_APPEND,
+ &ump->legacy_out_rfile);
+ if (err < 0)
+ return err;
+ }
+ ump->legacy_out_opens++;
+ snd_ump_convert_reset(&ump->out_cvts[group]);
+ }
+ guard(spinlock_irq)(&ump->legacy_locks[dir]);
+ ump->legacy_substreams[dir][group] = substream;
+ return 0;
+}
+
+static int snd_ump_legacy_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ump_endpoint *ump = substream->rmidi->private_data;
+ int dir = substream->stream;
+ int group = ump->legacy_mapping[substream->number];
+
+ guard(mutex)(&ump->open_mutex);
+ scoped_guard(spinlock_irq, &ump->legacy_locks[dir])
+ ump->legacy_substreams[dir][group] = NULL;
+ if (dir == SNDRV_RAWMIDI_STREAM_OUTPUT) {
+ if (!--ump->legacy_out_opens)
+ snd_rawmidi_kernel_release(&ump->legacy_out_rfile);
+ }
+ return 0;
+}
+
+static void snd_ump_legacy_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_ump_endpoint *ump = substream->rmidi->private_data;
+ int dir = substream->stream;
+
+ ump->ops->trigger(ump, dir, up);
+}
+
+static void snd_ump_legacy_drain(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ump_endpoint *ump = substream->rmidi->private_data;
+
+ if (ump->ops->drain)
+ ump->ops->drain(ump, SNDRV_RAWMIDI_STREAM_OUTPUT);
+}
+
+static int snd_ump_legacy_dev_register(struct snd_rawmidi *rmidi)
+{
+ /* dummy, just for avoiding create superfluous seq clients */
+ return 0;
+}
+
+static const struct snd_rawmidi_ops snd_ump_legacy_input_ops = {
+ .open = snd_ump_legacy_open,
+ .close = snd_ump_legacy_close,
+ .trigger = snd_ump_legacy_trigger,
+};
+
+static const struct snd_rawmidi_ops snd_ump_legacy_output_ops = {
+ .open = snd_ump_legacy_open,
+ .close = snd_ump_legacy_close,
+ .trigger = snd_ump_legacy_trigger,
+ .drain = snd_ump_legacy_drain,
+};
+
+static const struct snd_rawmidi_global_ops snd_ump_legacy_ops = {
+ .dev_register = snd_ump_legacy_dev_register,
+};
+
+static int process_legacy_output(struct snd_ump_endpoint *ump,
+ u32 *buffer, int count)
+{
+ struct snd_rawmidi_substream *substream;
+ struct ump_cvt_to_ump *ctx;
+ const int dir = SNDRV_RAWMIDI_STREAM_OUTPUT;
+ unsigned int protocol;
+ unsigned char c;
+ int group, size = 0;
+
+ if (!ump->out_cvts || !ump->legacy_out_opens)
+ return 0;
+
+ guard(spinlock_irqsave)(&ump->legacy_locks[dir]);
+ for (group = 0; group < SNDRV_UMP_MAX_GROUPS; group++) {
+ substream = ump->legacy_substreams[dir][group];
+ if (!substream)
+ continue;
+ ctx = &ump->out_cvts[group];
+ protocol = ump->info.protocol;
+ if ((protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2) &&
+ ump->groups[group].is_midi1)
+ protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI1;
+ while (!ctx->ump_bytes &&
+ snd_rawmidi_transmit(substream, &c, 1) > 0)
+ snd_ump_convert_to_ump(ctx, group, protocol, c);
+ if (ctx->ump_bytes && ctx->ump_bytes <= count) {
+ size = ctx->ump_bytes;
+ memcpy(buffer, ctx->ump, size);
+ ctx->ump_bytes = 0;
+ break;
+ }
+ }
+ return size;
+}
+
+static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src,
+ int words)
+{
+ struct snd_rawmidi_substream *substream;
+ unsigned char buf[16];
+ unsigned char group;
+ const int dir = SNDRV_RAWMIDI_STREAM_INPUT;
+ int size;
+
+ size = snd_ump_convert_from_ump(src, buf, &group);
+ if (size <= 0)
+ return;
+ guard(spinlock_irqsave)(&ump->legacy_locks[dir]);
+ substream = ump->legacy_substreams[dir][group];
+ if (substream)
+ snd_rawmidi_receive(substream, buf, size);
+}
+
+/* Fill ump->legacy_mapping[] for groups to be used for legacy rawmidi */
+static int fill_legacy_mapping(struct snd_ump_endpoint *ump)
+{
+ struct snd_ump_block *fb;
+ unsigned int group_maps = 0;
+ int i, num;
+
+ if (ump->info.flags & SNDRV_UMP_EP_INFO_STATIC_BLOCKS) {
+ list_for_each_entry(fb, &ump->block_list, list) {
+ for (i = 0; i < fb->info.num_groups; i++)
+ group_maps |= 1U << (fb->info.first_group + i);
+ }
+ if (!group_maps)
+ ump_info(ump, "No UMP Group is found in FB\n");
+ }
+
+ /* use all groups for non-static case */
+ if (!group_maps)
+ group_maps = (1U << SNDRV_UMP_MAX_GROUPS) - 1;
+
+ num = 0;
+ for (i = 0; i < SNDRV_UMP_MAX_GROUPS; i++)
+ if (group_maps & (1U << i))
+ ump->legacy_mapping[num++] = i;
+
+ return num;
+}
+
+static void update_legacy_substreams(struct snd_ump_endpoint *ump,
+ struct snd_rawmidi *rmidi, int dir)
+{
+ struct snd_rawmidi_substream *s;
+ const char *name;
+ int idx;
+
+ list_for_each_entry(s, &rmidi->streams[dir].substreams, list) {
+ idx = ump->legacy_mapping[s->number];
+ name = ump->groups[idx].name;
+ if (!*name)
+ name = ump->core.name;
+ scnprintf(s->name, sizeof(s->name), "Group %d (%.16s)%s",
+ idx + 1, name,
+ ump->groups[idx].active ? "" : " [Inactive]");
+ s->inactive = !ump->groups[idx].active;
+ }
+}
+
+static void update_legacy_names(struct snd_ump_endpoint *ump)
+{
+ struct snd_rawmidi *rmidi = ump->legacy_rmidi;
+
+ update_legacy_substreams(ump, rmidi, SNDRV_RAWMIDI_STREAM_INPUT);
+ update_legacy_substreams(ump, rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT);
+}
+
+static void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump)
+{
+ struct snd_rawmidi *rmidi = ump->legacy_rmidi;
+
+ snprintf(rmidi->name, sizeof(rmidi->name), "%.68s (MIDI 1.0)",
+ ump->core.name);
+}
+
+int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,
+ char *id, int device)
+{
+ struct snd_rawmidi *rmidi;
+ bool input, output;
+ int err, num;
+
+ ump->out_cvts = kcalloc(SNDRV_UMP_MAX_GROUPS,
+ sizeof(*ump->out_cvts), GFP_KERNEL);
+ if (!ump->out_cvts)
+ return -ENOMEM;
+
+ num = fill_legacy_mapping(ump);
+
+ input = ump->core.info_flags & SNDRV_RAWMIDI_INFO_INPUT;
+ output = ump->core.info_flags & SNDRV_RAWMIDI_INFO_OUTPUT;
+ err = snd_rawmidi_new(ump->core.card, id, device,
+ output ? num : 0, input ? num : 0,
+ &rmidi);
+ if (err < 0) {
+ kfree(ump->out_cvts);
+ return err;
+ }
+
+ if (input)
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &snd_ump_legacy_input_ops);
+ if (output)
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &snd_ump_legacy_output_ops);
+ rmidi->info_flags = ump->core.info_flags & ~SNDRV_RAWMIDI_INFO_UMP;
+ rmidi->ops = &snd_ump_legacy_ops;
+ rmidi->private_data = ump;
+ ump->legacy_rmidi = rmidi;
+ ump_legacy_set_rawmidi_name(ump);
+ update_legacy_names(ump);
+
+ snd_rawmidi_tie_devices(rmidi, &ump->core);
+
+ ump_dbg(ump, "Created a legacy rawmidi #%d (%s)\n", device, id);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ump_attach_legacy_rawmidi);
+#endif /* CONFIG_SND_UMP_LEGACY_RAWMIDI */
+
+MODULE_DESCRIPTION("Universal MIDI Packet (UMP) Core Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/core/ump_convert.c b/sound/core/ump_convert.c
new file mode 100644
index 000000000000..0fe13d031656
--- /dev/null
+++ b/sound/core/ump_convert.c
@@ -0,0 +1,528 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Helpers for UMP <-> MIDI 1.0 byte stream conversion
+ */
+
+#include <linux/module.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include <sound/asound.h>
+#include <sound/ump.h>
+#include <sound/ump_convert.h>
+
+/*
+ * Upgrade / downgrade value bits
+ */
+static u8 downscale_32_to_7bit(u32 src)
+{
+ return src >> 25;
+}
+
+static u16 downscale_32_to_14bit(u32 src)
+{
+ return src >> 18;
+}
+
+static u8 downscale_16_to_7bit(u16 src)
+{
+ return src >> 9;
+}
+
+static u16 upscale_7_to_16bit(u8 src)
+{
+ u16 val, repeat;
+
+ val = (u16)src << 9;
+ if (src <= 0x40)
+ return val;
+ repeat = src & 0x3f;
+ return val | (repeat << 3) | (repeat >> 3);
+}
+
+static u32 upscale_7_to_32bit(u8 src)
+{
+ u32 val, repeat;
+
+ val = src << 25;
+ if (src <= 0x40)
+ return val;
+ repeat = src & 0x3f;
+ return val | (repeat << 19) | (repeat << 13) |
+ (repeat << 7) | (repeat << 1) | (repeat >> 5);
+}
+
+static u32 upscale_14_to_32bit(u16 src)
+{
+ u32 val, repeat;
+
+ val = src << 18;
+ if (src <= 0x2000)
+ return val;
+ repeat = src & 0x1fff;
+ return val | (repeat << 5) | (repeat >> 8);
+}
+
+/*
+ * UMP -> MIDI 1 byte stream conversion
+ */
+/* convert a UMP System message to MIDI 1.0 byte stream */
+static int cvt_ump_system_to_legacy(u32 data, unsigned char *buf)
+{
+ buf[0] = ump_message_status_channel(data);
+ switch (ump_message_status_code(data)) {
+ case UMP_SYSTEM_STATUS_MIDI_TIME_CODE:
+ case UMP_SYSTEM_STATUS_SONG_SELECT:
+ buf[1] = (data >> 8) & 0x7f;
+ return 2;
+ case UMP_SYSTEM_STATUS_SONG_POSITION:
+ buf[1] = (data >> 8) & 0x7f;
+ buf[2] = data & 0x7f;
+ return 3;
+ default:
+ return 1;
+ }
+}
+
+/* convert a UMP MIDI 1.0 Channel Voice message to MIDI 1.0 byte stream */
+static int cvt_ump_midi1_to_legacy(u32 data, unsigned char *buf)
+{
+ buf[0] = ump_message_status_channel(data);
+ buf[1] = (data >> 8) & 0xff;
+ switch (ump_message_status_code(data)) {
+ case UMP_MSG_STATUS_PROGRAM:
+ case UMP_MSG_STATUS_CHANNEL_PRESSURE:
+ return 2;
+ default:
+ buf[2] = data & 0xff;
+ return 3;
+ }
+}
+
+/* convert a UMP MIDI 2.0 Channel Voice message to MIDI 1.0 byte stream */
+static int cvt_ump_midi2_to_legacy(const union snd_ump_midi2_msg *midi2,
+ unsigned char *buf)
+{
+ unsigned char status = midi2->note.status;
+ unsigned char channel = midi2->note.channel;
+ u16 v;
+
+ buf[0] = (status << 4) | channel;
+ switch (status) {
+ case UMP_MSG_STATUS_NOTE_OFF:
+ case UMP_MSG_STATUS_NOTE_ON:
+ buf[1] = midi2->note.note;
+ buf[2] = downscale_16_to_7bit(midi2->note.velocity);
+ if (status == UMP_MSG_STATUS_NOTE_ON && !buf[2])
+ buf[2] = 1;
+ return 3;
+ case UMP_MSG_STATUS_POLY_PRESSURE:
+ buf[1] = midi2->paf.note;
+ buf[2] = downscale_32_to_7bit(midi2->paf.data);
+ return 3;
+ case UMP_MSG_STATUS_CC:
+ buf[1] = midi2->cc.index;
+ buf[2] = downscale_32_to_7bit(midi2->cc.data);
+ return 3;
+ case UMP_MSG_STATUS_CHANNEL_PRESSURE:
+ buf[1] = downscale_32_to_7bit(midi2->caf.data);
+ return 2;
+ case UMP_MSG_STATUS_PROGRAM:
+ if (midi2->pg.bank_valid) {
+ buf[0] = channel | (UMP_MSG_STATUS_CC << 4);
+ buf[1] = UMP_CC_BANK_SELECT;
+ buf[2] = midi2->pg.bank_msb;
+ buf[3] = channel | (UMP_MSG_STATUS_CC << 4);
+ buf[4] = UMP_CC_BANK_SELECT_LSB;
+ buf[5] = midi2->pg.bank_lsb;
+ buf[6] = channel | (UMP_MSG_STATUS_PROGRAM << 4);
+ buf[7] = midi2->pg.program;
+ return 8;
+ }
+ buf[1] = midi2->pg.program;
+ return 2;
+ case UMP_MSG_STATUS_PITCH_BEND:
+ v = downscale_32_to_14bit(midi2->pb.data);
+ buf[1] = v & 0x7f;
+ buf[2] = v >> 7;
+ return 3;
+ case UMP_MSG_STATUS_RPN:
+ case UMP_MSG_STATUS_NRPN:
+ buf[0] = channel | (UMP_MSG_STATUS_CC << 4);
+ buf[1] = status == UMP_MSG_STATUS_RPN ? UMP_CC_RPN_MSB : UMP_CC_NRPN_MSB;
+ buf[2] = midi2->rpn.bank;
+ buf[3] = buf[0];
+ buf[4] = status == UMP_MSG_STATUS_RPN ? UMP_CC_RPN_LSB : UMP_CC_NRPN_LSB;
+ buf[5] = midi2->rpn.index;
+ buf[6] = buf[0];
+ buf[7] = UMP_CC_DATA;
+ v = downscale_32_to_14bit(midi2->rpn.data);
+ buf[8] = v >> 7;
+ buf[9] = buf[0];
+ buf[10] = UMP_CC_DATA_LSB;
+ buf[11] = v & 0x7f;
+ return 12;
+ default:
+ return 0;
+ }
+}
+
+/* convert a UMP 7-bit SysEx message to MIDI 1.0 byte stream */
+static int cvt_ump_sysex7_to_legacy(const u32 *data, unsigned char *buf)
+{
+ unsigned char status;
+ unsigned char bytes;
+ int size, offset;
+
+ status = ump_sysex_message_status(*data);
+ if (status > UMP_SYSEX_STATUS_END)
+ return 0; // unsupported, skip
+ bytes = ump_sysex_message_length(*data);
+ if (bytes > 6)
+ return 0; // skip
+
+ size = 0;
+ if (status == UMP_SYSEX_STATUS_SINGLE ||
+ status == UMP_SYSEX_STATUS_START) {
+ buf[0] = UMP_MIDI1_MSG_SYSEX_START;
+ size = 1;
+ }
+
+ offset = 8;
+ for (; bytes; bytes--, size++) {
+ buf[size] = (*data >> offset) & 0x7f;
+ if (!offset) {
+ offset = 24;
+ data++;
+ } else {
+ offset -= 8;
+ }
+ }
+
+ if (status == UMP_SYSEX_STATUS_SINGLE ||
+ status == UMP_SYSEX_STATUS_END)
+ buf[size++] = UMP_MIDI1_MSG_SYSEX_END;
+
+ return size;
+}
+
+/**
+ * snd_ump_convert_from_ump - convert from UMP to legacy MIDI
+ * @data: UMP packet
+ * @buf: buffer to store legacy MIDI data
+ * @group_ret: pointer to store the target group
+ *
+ * Convert from a UMP packet @data to MIDI 1.0 bytes at @buf.
+ * The target group is stored at @group_ret.
+ *
+ * The function returns the number of bytes of MIDI 1.0 stream.
+ */
+int snd_ump_convert_from_ump(const u32 *data,
+ unsigned char *buf,
+ unsigned char *group_ret)
+{
+ *group_ret = ump_message_group(*data);
+
+ switch (ump_message_type(*data)) {
+ case UMP_MSG_TYPE_SYSTEM:
+ return cvt_ump_system_to_legacy(*data, buf);
+ case UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE:
+ return cvt_ump_midi1_to_legacy(*data, buf);
+ case UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE:
+ return cvt_ump_midi2_to_legacy((const union snd_ump_midi2_msg *)data,
+ buf);
+ case UMP_MSG_TYPE_DATA:
+ return cvt_ump_sysex7_to_legacy(data, buf);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ump_convert_from_ump);
+
+/*
+ * MIDI 1 byte stream -> UMP conversion
+ */
+/* convert MIDI 1.0 SysEx to a UMP packet */
+static int cvt_legacy_sysex_to_ump(struct ump_cvt_to_ump *cvt,
+ unsigned char group, u32 *data, bool finish)
+{
+ unsigned char status;
+ bool start = cvt->in_sysex == 1;
+ int i, offset;
+
+ if (start && finish)
+ status = UMP_SYSEX_STATUS_SINGLE;
+ else if (start)
+ status = UMP_SYSEX_STATUS_START;
+ else if (finish)
+ status = UMP_SYSEX_STATUS_END;
+ else
+ status = UMP_SYSEX_STATUS_CONTINUE;
+ *data = ump_compose(UMP_MSG_TYPE_DATA, group, status, cvt->len);
+ offset = 8;
+ for (i = 0; i < cvt->len; i++) {
+ *data |= cvt->buf[i] << offset;
+ if (!offset) {
+ offset = 24;
+ data++;
+ } else
+ offset -= 8;
+ }
+ cvt->len = 0;
+ if (finish)
+ cvt->in_sysex = 0;
+ else
+ cvt->in_sysex++;
+ return 8;
+}
+
+/* convert to a UMP System message */
+static int cvt_legacy_system_to_ump(struct ump_cvt_to_ump *cvt,
+ unsigned char group, u32 *data)
+{
+ data[0] = ump_compose(UMP_MSG_TYPE_SYSTEM, group, 0, cvt->buf[0]);
+ if (cvt->cmd_bytes > 1)
+ data[0] |= cvt->buf[1] << 8;
+ if (cvt->cmd_bytes > 2)
+ data[0] |= cvt->buf[2];
+ return 4;
+}
+
+static void reset_rpn(struct ump_cvt_to_ump_bank *cc)
+{
+ cc->rpn_set = 0;
+ cc->nrpn_set = 0;
+ cc->cc_rpn_msb = cc->cc_rpn_lsb = 0;
+ cc->cc_data_msb = cc->cc_data_lsb = 0;
+ cc->cc_data_msb_set = cc->cc_data_lsb_set = 0;
+}
+
+static int fill_rpn(struct ump_cvt_to_ump_bank *cc,
+ union snd_ump_midi2_msg *midi2,
+ bool flush)
+{
+ if (!(cc->cc_data_lsb_set || cc->cc_data_msb_set))
+ return 0; // skip
+ /* when not flushing, wait for complete data set */
+ if (!flush && (!cc->cc_data_lsb_set || !cc->cc_data_msb_set))
+ return 0; // skip
+
+ if (cc->rpn_set) {
+ midi2->rpn.status = UMP_MSG_STATUS_RPN;
+ midi2->rpn.bank = cc->cc_rpn_msb;
+ midi2->rpn.index = cc->cc_rpn_lsb;
+ } else if (cc->nrpn_set) {
+ midi2->rpn.status = UMP_MSG_STATUS_NRPN;
+ midi2->rpn.bank = cc->cc_nrpn_msb;
+ midi2->rpn.index = cc->cc_nrpn_lsb;
+ } else {
+ return 0; // skip
+ }
+
+ midi2->rpn.data = upscale_14_to_32bit((cc->cc_data_msb << 7) |
+ cc->cc_data_lsb);
+
+ reset_rpn(cc);
+ return 1;
+}
+
+/* convert to a MIDI 1.0 Channel Voice message */
+static int cvt_legacy_cmd_to_ump(struct ump_cvt_to_ump *cvt,
+ unsigned char group,
+ unsigned int protocol,
+ u32 *data, unsigned char bytes)
+{
+ const unsigned char *buf = cvt->buf;
+ struct ump_cvt_to_ump_bank *cc;
+ union snd_ump_midi2_msg *midi2 = (union snd_ump_midi2_msg *)data;
+ unsigned char status, channel;
+ int ret;
+
+ BUILD_BUG_ON(sizeof(union snd_ump_midi1_msg) != 4);
+ BUILD_BUG_ON(sizeof(union snd_ump_midi2_msg) != 8);
+
+ /* for MIDI 1.0 UMP, it's easy, just pack it into UMP */
+ if (protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI1) {
+ data[0] = ump_compose(UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE,
+ group, 0, buf[0]);
+ data[0] |= buf[1] << 8;
+ if (bytes > 2)
+ data[0] |= buf[2];
+ return 4;
+ }
+
+ status = *buf >> 4;
+ channel = *buf & 0x0f;
+ cc = &cvt->bank[channel];
+
+ /* special handling: treat note-on with 0 velocity as note-off */
+ if (status == UMP_MSG_STATUS_NOTE_ON && !buf[2])
+ status = UMP_MSG_STATUS_NOTE_OFF;
+
+ /* initialize the packet */
+ data[0] = ump_compose(UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE,
+ group, status, channel);
+ data[1] = 0;
+
+ switch (status) {
+ case UMP_MSG_STATUS_NOTE_ON:
+ case UMP_MSG_STATUS_NOTE_OFF:
+ midi2->note.note = buf[1];
+ midi2->note.velocity = upscale_7_to_16bit(buf[2]);
+ break;
+ case UMP_MSG_STATUS_POLY_PRESSURE:
+ midi2->paf.note = buf[1];
+ midi2->paf.data = upscale_7_to_32bit(buf[2]);
+ break;
+ case UMP_MSG_STATUS_CC:
+ switch (buf[1]) {
+ case UMP_CC_RPN_MSB:
+ ret = fill_rpn(cc, midi2, true);
+ cc->rpn_set = 1;
+ cc->cc_rpn_msb = buf[2];
+ if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
+ reset_rpn(cc);
+ return ret;
+ case UMP_CC_RPN_LSB:
+ ret = fill_rpn(cc, midi2, true);
+ cc->rpn_set = 1;
+ cc->cc_rpn_lsb = buf[2];
+ if (cc->cc_rpn_msb == 0x7f && cc->cc_rpn_lsb == 0x7f)
+ reset_rpn(cc);
+ return ret;
+ case UMP_CC_NRPN_MSB:
+ ret = fill_rpn(cc, midi2, true);
+ cc->nrpn_set = 1;
+ cc->cc_nrpn_msb = buf[2];
+ return ret;
+ case UMP_CC_NRPN_LSB:
+ ret = fill_rpn(cc, midi2, true);
+ cc->nrpn_set = 1;
+ cc->cc_nrpn_lsb = buf[2];
+ return ret;
+ case UMP_CC_DATA:
+ cc->cc_data_msb_set = 1;
+ cc->cc_data_msb = buf[2];
+ return fill_rpn(cc, midi2, false);
+ case UMP_CC_BANK_SELECT:
+ cc->bank_set = 1;
+ cc->cc_bank_msb = buf[2];
+ return 0; // skip
+ case UMP_CC_BANK_SELECT_LSB:
+ cc->bank_set = 1;
+ cc->cc_bank_lsb = buf[2];
+ return 0; // skip
+ case UMP_CC_DATA_LSB:
+ cc->cc_data_lsb_set = 1;
+ cc->cc_data_lsb = buf[2];
+ return fill_rpn(cc, midi2, false);
+ default:
+ midi2->cc.index = buf[1];
+ midi2->cc.data = upscale_7_to_32bit(buf[2]);
+ break;
+ }
+ break;
+ case UMP_MSG_STATUS_PROGRAM:
+ midi2->pg.program = buf[1];
+ if (cc->bank_set) {
+ midi2->pg.bank_valid = 1;
+ midi2->pg.bank_msb = cc->cc_bank_msb;
+ midi2->pg.bank_lsb = cc->cc_bank_lsb;
+ cc->bank_set = 0;
+ }
+ break;
+ case UMP_MSG_STATUS_CHANNEL_PRESSURE:
+ midi2->caf.data = upscale_7_to_32bit(buf[1]);
+ break;
+ case UMP_MSG_STATUS_PITCH_BEND:
+ midi2->pb.data = upscale_14_to_32bit(buf[1] | (buf[2] << 7));
+ break;
+ default:
+ return 0;
+ }
+
+ return 8;
+}
+
+static int do_convert_to_ump(struct ump_cvt_to_ump *cvt, unsigned char group,
+ unsigned int protocol, unsigned char c, u32 *data)
+{
+ /* bytes for 0x80-0xf0 */
+ static unsigned char cmd_bytes[8] = {
+ 3, 3, 3, 3, 2, 2, 3, 0
+ };
+ /* bytes for 0xf0-0xff */
+ static unsigned char system_bytes[16] = {
+ 0, 2, 3, 2, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1
+ };
+ unsigned char bytes;
+
+ if (c == UMP_MIDI1_MSG_SYSEX_START) {
+ cvt->in_sysex = 1;
+ cvt->len = 0;
+ return 0;
+ }
+ if (c == UMP_MIDI1_MSG_SYSEX_END) {
+ if (!cvt->in_sysex)
+ return 0; /* skip */
+ return cvt_legacy_sysex_to_ump(cvt, group, data, true);
+ }
+
+ if ((c & 0xf0) == UMP_MIDI1_MSG_REALTIME) {
+ bytes = system_bytes[c & 0x0f];
+ if (!bytes)
+ return 0; /* skip */
+ if (bytes == 1) {
+ data[0] = ump_compose(UMP_MSG_TYPE_SYSTEM, group, 0, c);
+ return 4;
+ }
+ cvt->buf[0] = c;
+ cvt->len = 1;
+ cvt->cmd_bytes = bytes;
+ cvt->in_sysex = 0; /* abort SysEx */
+ return 0;
+ }
+
+ if (c & 0x80) {
+ bytes = cmd_bytes[(c >> 4) & 7];
+ cvt->buf[0] = c;
+ cvt->len = 1;
+ cvt->cmd_bytes = bytes;
+ cvt->in_sysex = 0; /* abort SysEx */
+ return 0;
+ }
+
+ if (cvt->in_sysex) {
+ cvt->buf[cvt->len++] = c;
+ if (cvt->len == 6)
+ return cvt_legacy_sysex_to_ump(cvt, group, data, false);
+ return 0;
+ }
+
+ if (!cvt->len)
+ return 0;
+
+ cvt->buf[cvt->len++] = c;
+ if (cvt->len < cvt->cmd_bytes)
+ return 0;
+ cvt->len = 1;
+ if ((cvt->buf[0] & 0xf0) == UMP_MIDI1_MSG_REALTIME)
+ return cvt_legacy_system_to_ump(cvt, group, data);
+ return cvt_legacy_cmd_to_ump(cvt, group, protocol, data, cvt->cmd_bytes);
+}
+
+/**
+ * snd_ump_convert_to_ump - convert legacy MIDI byte to UMP packet
+ * @cvt: converter context
+ * @group: target UMP group
+ * @protocol: target UMP protocol
+ * @c: MIDI 1.0 byte data
+ *
+ * Feed a MIDI 1.0 byte @c and convert to a UMP packet if completed.
+ * The result is stored in the buffer in @cvt.
+ */
+void snd_ump_convert_to_ump(struct ump_cvt_to_ump *cvt, unsigned char group,
+ unsigned int protocol, unsigned char c)
+{
+ cvt->ump_bytes = do_convert_to_ump(cvt, group, protocol, c, cvt->ump);
+}
+EXPORT_SYMBOL_GPL(snd_ump_convert_to_ump);
diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c
index 3b9b550109cb..c657659b236c 100644
--- a/sound/core/vmaster.c
+++ b/sound/core/vmaster.c
@@ -1,15 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Virtual master and slave controls
+ * Virtual master and follower controls
*
* Copyright (c) 2008 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
- *
*/
#include <linux/slab.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/tlv.h>
@@ -18,222 +15,233 @@
* a subset of information returned via ctl info callback
*/
struct link_ctl_info {
- int type; /* value type */
+ snd_ctl_elem_type_t type; /* value type */
int count; /* item count */
int min_val, max_val; /* min, max values */
};
/*
- * link master - this contains a list of slave controls that are
+ * link master - this contains a list of follower controls that are
* identical types, i.e. info returns the same value type and value
* ranges, but may have different number of counts.
*
* The master control is so far only mono volume/switch for simplicity.
- * The same value will be applied to all slaves.
+ * The same value will be applied to all followers.
*/
struct link_master {
- struct list_head slaves;
+ struct list_head followers;
struct link_ctl_info info;
int val; /* the master value */
unsigned int tlv[4];
+ void (*hook)(void *private_data, int);
+ void *hook_private_data;
};
/*
- * link slave - this contains a slave control element
+ * link follower - this contains a follower control element
*
- * It fakes the control callbacsk with additional attenuation by the
- * master control. A slave may have either one or two channels.
+ * It fakes the control callbacks with additional attenuation by the
+ * master control. A follower may have either one or two channels.
*/
-struct link_slave {
+struct link_follower {
struct list_head list;
struct link_master *master;
struct link_ctl_info info;
int vals[2]; /* current values */
unsigned int flags;
- struct snd_kcontrol slave; /* the copy of original control entry */
+ struct snd_kcontrol *kctl; /* original kcontrol pointer */
+ struct snd_kcontrol follower; /* the copy of original control entry */
};
-static int slave_update(struct link_slave *slave)
+static int follower_update(struct link_follower *follower)
{
- struct snd_ctl_elem_value *uctl;
+ struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
int err, ch;
- uctl = kmalloc(sizeof(*uctl), GFP_KERNEL);
+ uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
if (!uctl)
return -ENOMEM;
- uctl->id = slave->slave.id;
- err = slave->slave.get(&slave->slave, uctl);
- for (ch = 0; ch < slave->info.count; ch++)
- slave->vals[ch] = uctl->value.integer.value[ch];
- kfree(uctl);
+ uctl->id = follower->follower.id;
+ err = follower->follower.get(&follower->follower, uctl);
+ if (err < 0)
+ return err;
+ for (ch = 0; ch < follower->info.count; ch++)
+ follower->vals[ch] = uctl->value.integer.value[ch];
return 0;
}
-/* get the slave ctl info and save the initial values */
-static int slave_init(struct link_slave *slave)
+/* get the follower ctl info and save the initial values */
+static int follower_init(struct link_follower *follower)
{
- struct snd_ctl_elem_info *uinfo;
+ struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;
int err;
- if (slave->info.count) {
+ if (follower->info.count) {
/* already initialized */
- if (slave->flags & SND_CTL_SLAVE_NEED_UPDATE)
- return slave_update(slave);
+ if (follower->flags & SND_CTL_FOLLOWER_NEED_UPDATE)
+ return follower_update(follower);
return 0;
}
uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL);
if (!uinfo)
return -ENOMEM;
- uinfo->id = slave->slave.id;
- err = slave->slave.info(&slave->slave, uinfo);
- if (err < 0) {
- kfree(uinfo);
+ uinfo->id = follower->follower.id;
+ err = follower->follower.info(&follower->follower, uinfo);
+ if (err < 0)
return err;
- }
- slave->info.type = uinfo->type;
- slave->info.count = uinfo->count;
- if (slave->info.count > 2 ||
- (slave->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER &&
- slave->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) {
- snd_printk(KERN_ERR "invalid slave element\n");
- kfree(uinfo);
+ follower->info.type = uinfo->type;
+ follower->info.count = uinfo->count;
+ if (follower->info.count > 2 ||
+ (follower->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER &&
+ follower->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) {
+ pr_err("ALSA: vmaster: invalid follower element\n");
return -EINVAL;
}
- slave->info.min_val = uinfo->value.integer.min;
- slave->info.max_val = uinfo->value.integer.max;
- kfree(uinfo);
+ follower->info.min_val = uinfo->value.integer.min;
+ follower->info.max_val = uinfo->value.integer.max;
- return slave_update(slave);
+ return follower_update(follower);
}
/* initialize master volume */
static int master_init(struct link_master *master)
{
- struct link_slave *slave;
+ struct link_follower *follower;
if (master->info.count)
return 0; /* already initialized */
- list_for_each_entry(slave, &master->slaves, list) {
- int err = slave_init(slave);
+ list_for_each_entry(follower, &master->followers, list) {
+ int err = follower_init(follower);
if (err < 0)
return err;
- master->info = slave->info;
+ master->info = follower->info;
master->info.count = 1; /* always mono */
/* set full volume as default (= no attenuation) */
master->val = master->info.max_val;
- return 0;
+ if (master->hook)
+ master->hook(master->hook_private_data, master->val);
+ return 1;
}
return -ENOENT;
}
-static int slave_get_val(struct link_slave *slave,
- struct snd_ctl_elem_value *ucontrol)
+static int follower_get_val(struct link_follower *follower,
+ struct snd_ctl_elem_value *ucontrol)
{
int err, ch;
- err = slave_init(slave);
+ err = follower_init(follower);
if (err < 0)
return err;
- for (ch = 0; ch < slave->info.count; ch++)
- ucontrol->value.integer.value[ch] = slave->vals[ch];
+ for (ch = 0; ch < follower->info.count; ch++)
+ ucontrol->value.integer.value[ch] = follower->vals[ch];
return 0;
}
-static int slave_put_val(struct link_slave *slave,
- struct snd_ctl_elem_value *ucontrol)
+static int follower_put_val(struct link_follower *follower,
+ struct snd_ctl_elem_value *ucontrol)
{
int err, ch, vol;
- err = master_init(slave->master);
+ err = master_init(follower->master);
if (err < 0)
return err;
- switch (slave->info.type) {
+ switch (follower->info.type) {
case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
- for (ch = 0; ch < slave->info.count; ch++)
+ for (ch = 0; ch < follower->info.count; ch++)
ucontrol->value.integer.value[ch] &=
- !!slave->master->val;
+ !!follower->master->val;
break;
case SNDRV_CTL_ELEM_TYPE_INTEGER:
- for (ch = 0; ch < slave->info.count; ch++) {
+ for (ch = 0; ch < follower->info.count; ch++) {
/* max master volume is supposed to be 0 dB */
vol = ucontrol->value.integer.value[ch];
- vol += slave->master->val - slave->master->info.max_val;
- if (vol < slave->info.min_val)
- vol = slave->info.min_val;
- else if (vol > slave->info.max_val)
- vol = slave->info.max_val;
+ vol += follower->master->val - follower->master->info.max_val;
+ if (vol < follower->info.min_val)
+ vol = follower->info.min_val;
+ else if (vol > follower->info.max_val)
+ vol = follower->info.max_val;
ucontrol->value.integer.value[ch] = vol;
}
break;
}
- return slave->slave.put(&slave->slave, ucontrol);
+ return follower->follower.put(&follower->follower, ucontrol);
}
/*
- * ctl callbacks for slaves
+ * ctl callbacks for followers
*/
-static int slave_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static int follower_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- struct link_slave *slave = snd_kcontrol_chip(kcontrol);
- return slave->slave.info(&slave->slave, uinfo);
+ struct link_follower *follower = snd_kcontrol_chip(kcontrol);
+ return follower->follower.info(&follower->follower, uinfo);
}
-static int slave_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int follower_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct link_slave *slave = snd_kcontrol_chip(kcontrol);
- return slave_get_val(slave, ucontrol);
+ struct link_follower *follower = snd_kcontrol_chip(kcontrol);
+ return follower_get_val(follower, ucontrol);
}
-static int slave_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int follower_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ struct link_follower *follower = snd_kcontrol_chip(kcontrol);
int err, ch, changed = 0;
- err = slave_init(slave);
+ err = follower_init(follower);
if (err < 0)
return err;
- for (ch = 0; ch < slave->info.count; ch++) {
- if (slave->vals[ch] != ucontrol->value.integer.value[ch]) {
+ for (ch = 0; ch < follower->info.count; ch++) {
+ if (ucontrol->value.integer.value[ch] < follower->info.min_val ||
+ ucontrol->value.integer.value[ch] > follower->info.max_val)
+ return -EINVAL;
+ }
+
+ for (ch = 0; ch < follower->info.count; ch++) {
+ if (follower->vals[ch] != ucontrol->value.integer.value[ch]) {
changed = 1;
- slave->vals[ch] = ucontrol->value.integer.value[ch];
+ follower->vals[ch] = ucontrol->value.integer.value[ch];
}
}
if (!changed)
return 0;
- return slave_put_val(slave, ucontrol);
+ err = follower_put_val(follower, ucontrol);
+ if (err < 0)
+ return err;
+ return 1;
}
-static int slave_tlv_cmd(struct snd_kcontrol *kcontrol,
- int op_flag, unsigned int size,
- unsigned int __user *tlv)
+static int follower_tlv_cmd(struct snd_kcontrol *kcontrol,
+ int op_flag, unsigned int size,
+ unsigned int __user *tlv)
{
- struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ struct link_follower *follower = snd_kcontrol_chip(kcontrol);
/* FIXME: this assumes that the max volume is 0 dB */
- return slave->slave.tlv.c(&slave->slave, op_flag, size, tlv);
+ return follower->follower.tlv.c(&follower->follower, op_flag, size, tlv);
}
-static void slave_free(struct snd_kcontrol *kcontrol)
+static void follower_free(struct snd_kcontrol *kcontrol)
{
- struct link_slave *slave = snd_kcontrol_chip(kcontrol);
- if (slave->slave.private_free)
- slave->slave.private_free(&slave->slave);
- if (slave->master)
- list_del(&slave->list);
- kfree(slave);
+ struct link_follower *follower = snd_kcontrol_chip(kcontrol);
+ if (follower->follower.private_free)
+ follower->follower.private_free(&follower->follower);
+ if (follower->master)
+ list_del(&follower->list);
+ kfree(follower);
}
/*
- * Add a slave control to the group with the given master control
+ * Add a follower control to the group with the given master control
*
- * All slaves must be the same type (returning the same information
- * via info callback). The fucntion doesn't check it, so it's your
+ * All followers must be the same type (returning the same information
+ * via info callback). The function doesn't check it, so it's your
* responsibility.
*
* Also, some additional limitations:
@@ -241,34 +249,64 @@ static void slave_free(struct snd_kcontrol *kcontrol)
* - logarithmic volume control (dB level), no linear volume
* - master can only attenuate the volume, no gain
*/
-int _snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave,
- unsigned int flags)
+int _snd_ctl_add_follower(struct snd_kcontrol *master,
+ struct snd_kcontrol *follower,
+ unsigned int flags)
{
struct link_master *master_link = snd_kcontrol_chip(master);
- struct link_slave *srec;
+ struct link_follower *srec;
- srec = kzalloc(sizeof(*srec) +
- slave->count * sizeof(*slave->vd), GFP_KERNEL);
+ srec = kzalloc(struct_size(srec, follower.vd, follower->count),
+ GFP_KERNEL);
if (!srec)
return -ENOMEM;
- srec->slave = *slave;
- memcpy(srec->slave.vd, slave->vd, slave->count * sizeof(*slave->vd));
+ srec->kctl = follower;
+ srec->follower = *follower;
+ memcpy(srec->follower.vd, follower->vd, follower->count * sizeof(*follower->vd));
srec->master = master_link;
srec->flags = flags;
/* override callbacks */
- slave->info = slave_info;
- slave->get = slave_get;
- slave->put = slave_put;
- if (slave->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)
- slave->tlv.c = slave_tlv_cmd;
- slave->private_data = srec;
- slave->private_free = slave_free;
-
- list_add_tail(&srec->list, &master_link->slaves);
+ follower->info = follower_info;
+ follower->get = follower_get;
+ follower->put = follower_put;
+ if (follower->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)
+ follower->tlv.c = follower_tlv_cmd;
+ follower->private_data = srec;
+ follower->private_free = follower_free;
+
+ list_add_tail(&srec->list, &master_link->followers);
return 0;
}
-EXPORT_SYMBOL(_snd_ctl_add_slave);
+EXPORT_SYMBOL(_snd_ctl_add_follower);
+
+/**
+ * snd_ctl_add_followers - add multiple followers to vmaster
+ * @card: card instance
+ * @master: the target vmaster kcontrol object
+ * @list: NULL-terminated list of name strings of followers to be added
+ *
+ * Adds the multiple follower kcontrols with the given names.
+ * Returns 0 for success or a negative error code.
+ */
+int snd_ctl_add_followers(struct snd_card *card, struct snd_kcontrol *master,
+ const char * const *list)
+{
+ struct snd_kcontrol *follower;
+ int err;
+
+ for (; *list; list++) {
+ follower = snd_ctl_find_id_mixer(card, *list);
+ if (follower) {
+ err = snd_ctl_add_follower(master, follower);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ctl_add_followers);
/*
* ctl callbacks for master controls
@@ -300,42 +338,65 @@ static int master_get(struct snd_kcontrol *kcontrol,
return 0;
}
+static int sync_followers(struct link_master *master, int old_val, int new_val)
+{
+ struct link_follower *follower;
+ struct snd_ctl_elem_value *uval __free(kfree) = NULL;
+
+ uval = kmalloc(sizeof(*uval), GFP_KERNEL);
+ if (!uval)
+ return -ENOMEM;
+ list_for_each_entry(follower, &master->followers, list) {
+ master->val = old_val;
+ uval->id = follower->follower.id;
+ follower_get_val(follower, uval);
+ master->val = new_val;
+ follower_put_val(follower, uval);
+ }
+ return 0;
+}
+
static int master_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct link_master *master = snd_kcontrol_chip(kcontrol);
- struct link_slave *slave;
- struct snd_ctl_elem_value *uval;
- int err, old_val;
+ int err, new_val, old_val;
+ bool first_init;
err = master_init(master);
if (err < 0)
return err;
+ first_init = err;
old_val = master->val;
- if (ucontrol->value.integer.value[0] == old_val)
+ new_val = ucontrol->value.integer.value[0];
+ if (new_val == old_val)
return 0;
+ if (new_val < master->info.min_val || new_val > master->info.max_val)
+ return -EINVAL;
- uval = kmalloc(sizeof(*uval), GFP_KERNEL);
- if (!uval)
- return -ENOMEM;
- list_for_each_entry(slave, &master->slaves, list) {
- master->val = old_val;
- uval->id = slave->slave.id;
- slave_get_val(slave, uval);
- master->val = ucontrol->value.integer.value[0];
- slave_put_val(slave, uval);
- }
- kfree(uval);
+ err = sync_followers(master, old_val, new_val);
+ if (err < 0)
+ return err;
+ if (master->hook && !first_init)
+ master->hook(master->hook_private_data, master->val);
return 1;
}
static void master_free(struct snd_kcontrol *kcontrol)
{
struct link_master *master = snd_kcontrol_chip(kcontrol);
- struct link_slave *slave;
-
- list_for_each_entry(slave, &master->slaves, list)
- slave->master = NULL;
+ struct link_follower *follower, *n;
+
+ /* free all follower links and retore the original follower kctls */
+ list_for_each_entry_safe(follower, n, &master->followers, list) {
+ struct snd_kcontrol *sctl = follower->kctl;
+ struct list_head olist = sctl->list;
+ memcpy(sctl, &follower->follower, sizeof(*sctl));
+ memcpy(sctl->vd, follower->follower.vd,
+ sctl->count * sizeof(*sctl->vd));
+ sctl->list = olist; /* keep the current linked-list */
+ kfree(follower);
+ }
kfree(master);
}
@@ -345,16 +406,17 @@ static void master_free(struct snd_kcontrol *kcontrol)
* @name: name string of the control element to create
* @tlv: optional TLV int array for dB information
*
- * Creates a virtual matster control with the given name string.
- * Returns the created control element, or NULL for errors (ENOMEM).
+ * Creates a virtual master control with the given name string.
*
- * After creating a vmaster element, you can add the slave controls
- * via snd_ctl_add_slave() or snd_ctl_add_slave_uncached().
+ * After creating a vmaster element, you can add the follower controls
+ * via snd_ctl_add_follower() or snd_ctl_add_follower_uncached().
*
* The optional argument @tlv can be used to specify the TLV information
* for dB scale of the master control. It should be a single element
* with #SNDRV_CTL_TLVT_DB_SCALE, #SNDRV_CTL_TLV_DB_MINMAX or
* #SNDRV_CTL_TLVT_DB_MINMAX_MUTE type, and should be the max 0dB.
+ *
+ * Return: The created control element, or %NULL for errors (ENOMEM).
*/
struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
const unsigned int *tlv)
@@ -371,7 +433,7 @@ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master)
return NULL;
- INIT_LIST_HEAD(&master->slaves);
+ INIT_LIST_HEAD(&master->followers);
kctl = snd_ctl_new1(&knew, master);
if (!kctl) {
@@ -385,15 +447,105 @@ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
kctl->private_free = master_free;
/* additional (constant) TLV read */
- if (tlv &&
- (tlv[0] == SNDRV_CTL_TLVT_DB_SCALE ||
- tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX ||
- tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX_MUTE)) {
- kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
- memcpy(master->tlv, tlv, sizeof(master->tlv));
- kctl->tlv.p = master->tlv;
+ if (tlv) {
+ unsigned int type = tlv[SNDRV_CTL_TLVO_TYPE];
+ if (type == SNDRV_CTL_TLVT_DB_SCALE ||
+ type == SNDRV_CTL_TLVT_DB_MINMAX ||
+ type == SNDRV_CTL_TLVT_DB_MINMAX_MUTE) {
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ memcpy(master->tlv, tlv, sizeof(master->tlv));
+ kctl->tlv.p = master->tlv;
+ }
}
return kctl;
}
EXPORT_SYMBOL(snd_ctl_make_virtual_master);
+
+/**
+ * snd_ctl_add_vmaster_hook - Add a hook to a vmaster control
+ * @kcontrol: vmaster kctl element
+ * @hook: the hook function
+ * @private_data: the private_data pointer to be saved
+ *
+ * Adds the given hook to the vmaster control element so that it's called
+ * at each time when the value is changed.
+ *
+ * Return: Zero.
+ */
+int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kcontrol,
+ void (*hook)(void *private_data, int),
+ void *private_data)
+{
+ struct link_master *master = snd_kcontrol_chip(kcontrol);
+ master->hook = hook;
+ master->hook_private_data = private_data;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ctl_add_vmaster_hook);
+
+/**
+ * snd_ctl_sync_vmaster - Sync the vmaster followers and hook
+ * @kcontrol: vmaster kctl element
+ * @hook_only: sync only the hook
+ *
+ * Forcibly call the put callback of each follower and call the hook function
+ * to synchronize with the current value of the given vmaster element.
+ * NOP when NULL is passed to @kcontrol.
+ */
+void snd_ctl_sync_vmaster(struct snd_kcontrol *kcontrol, bool hook_only)
+{
+ struct link_master *master;
+ bool first_init = false;
+
+ if (!kcontrol)
+ return;
+ master = snd_kcontrol_chip(kcontrol);
+ if (!hook_only) {
+ int err = master_init(master);
+ if (err < 0)
+ return;
+ first_init = err;
+ err = sync_followers(master, master->val, master->val);
+ if (err < 0)
+ return;
+ }
+
+ if (master->hook && !first_init)
+ master->hook(master->hook_private_data, master->val);
+}
+EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster);
+
+/**
+ * snd_ctl_apply_vmaster_followers - Apply function to each vmaster follower
+ * @kctl: vmaster kctl element
+ * @func: function to apply
+ * @arg: optional function argument
+ *
+ * Apply the function @func to each follower kctl of the given vmaster kctl.
+ *
+ * Return: 0 if successful, or a negative error code
+ */
+int snd_ctl_apply_vmaster_followers(struct snd_kcontrol *kctl,
+ int (*func)(struct snd_kcontrol *vfollower,
+ struct snd_kcontrol *follower,
+ void *arg),
+ void *arg)
+{
+ struct link_master *master;
+ struct link_follower *follower;
+ int err;
+
+ master = snd_kcontrol_chip(kctl);
+ err = master_init(master);
+ if (err < 0)
+ return err;
+ list_for_each_entry(follower, &master->followers, list) {
+ err = func(follower->kctl, &follower->follower, arg);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ctl_apply_vmaster_followers);
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig
index c8961165277c..6debd8e95cb7 100644
--- a/sound/drivers/Kconfig
+++ b/sound/drivers/Kconfig
@@ -1,19 +1,34 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SND_MPU401_UART
- tristate
- select SND_RAWMIDI
+ tristate
+ select SND_RAWMIDI
config SND_OPL3_LIB
tristate
select SND_TIMER
select SND_HWDEP
+ select SND_SEQ_DEVICE if SND_SEQUENCER != n
config SND_OPL4_LIB
tristate
select SND_TIMER
select SND_HWDEP
+ select SND_SEQ_DEVICE if SND_SEQUENCER != n
+
+# select SEQ stuff to min(SND_SEQUENCER,SND_XXX)
+config SND_OPL3_LIB_SEQ
+ def_tristate SND_SEQUENCER && SND_OPL3_LIB
+ select SND_SEQ_MIDI_EMUL
+ select SND_SEQ_MIDI_EVENT
+
+config SND_OPL4_LIB_SEQ
+ def_tristate SND_SEQUENCER && SND_OPL4_LIB
+ select SND_SEQ_MIDI_EMUL
+ select SND_SEQ_MIDI_EVENT
config SND_VX_LIB
tristate
+ select FW_LOADER
select SND_HWDEP
select SND_PCM
@@ -35,7 +50,6 @@ config SND_PCSP
tristate "PC-Speaker support (READ HELP!)"
depends on PCSPKR_PLATFORM && X86 && HIGH_RES_TIMERS
depends on INPUT
- depends on EXPERIMENTAL
select SND_PCM
help
If you don't have a sound card in your computer, you can include a
@@ -50,7 +64,8 @@ config SND_PCSP
before the other sound driver of yours, making the
pc-speaker a default sound device. Which is likely not
what you want. To make this driver play nicely with other
- sound driver, you can add this into your /etc/modprobe.conf:
+ sound driver, you can add this in a configuration file under
+ /etc/modprobe.d/ directory:
options snd-pcsp index=2
You don't need this driver if you only want your pc-speaker to beep.
@@ -75,29 +90,49 @@ config SND_DUMMY
will be called snd-dummy.
config SND_ALOOP
- tristate "Generic loopback driver (PCM)"
- select SND_PCM
- help
- Say 'Y' or 'M' to include support for the PCM loopback device.
+ tristate "Generic loopback driver (PCM)"
+ select SND_PCM
+ select SND_TIMER
+ help
+ Say 'Y' or 'M' to include support for the PCM loopback device.
This module returns played samples back to the user space using
the standard ALSA PCM device. The devices are routed 0->1 and
- 1->0, where first number is the playback PCM device and second
+ 1->0, where first number is the playback PCM device and second
number is the capture device. Module creates two PCM devices and
configured number of substreams (see the pcm_substreams module
- parameter).
+ parameter).
- The looback device allow time sychronization with an external
+ The loopback device allows time synchronization with an external
timing source using the time shift universal control (+-20%
of system time).
To compile this driver as a module, choose M here: the module
will be called snd-aloop.
+config SND_PCMTEST
+ tristate "Virtual PCM test driver"
+ depends on DEBUG_FS
+ select SND_PCM
+ help
+ Say 'Y' or 'M' to include support for the Virtual PCM test driver.
+ This driver is aimed at extended testing of the userspace applications
+ which use the ALSA API, as well as the PCM middle layer testing.
+
+ It can generate random or pattern-based data into the capture stream,
+ check the playback stream for containing the selected pattern, inject
+ time delays during capture/playback, redefine the RESET ioctl operation
+ to perform the PCM middle layer testing and inject errors during the
+ PCM callbacks. It supports both interleaved and non-interleaved access
+ modes. You can find the corresponding selftest in the 'alsa'
+ selftests folder.
+
config SND_VIRMIDI
tristate "Virtual MIDI soundcard"
depends on SND_SEQUENCER
select SND_TIMER
select SND_RAWMIDI
+ select SND_SEQ_VIRMIDI
+ select SND_SEQ_MIDI_EVENT
help
Say Y here to include the virtual MIDI driver. This driver
allows to connect applications using raw MIDI devices to
@@ -110,6 +145,7 @@ config SND_VIRMIDI
config SND_MTPAV
tristate "MOTU MidiTimePiece AV multiport MIDI"
+ depends on HAS_IOPORT
select SND_RAWMIDI
help
To use a MOTU MidiTimePiece AV multiport MIDI adapter
@@ -125,19 +161,20 @@ config SND_MTS64
select SND_RAWMIDI
help
The ESI Miditerminal 4140 is a 4 In 4 Out MIDI Interface with
- additional SMPTE Timecode capabilities for the parallel port.
+ additional SMPTE Timecode capabilities for the parallel port.
Say 'Y' to include support for this device.
To compile this driver as a module, chose 'M' here: the module
- will be called snd-mts64.
+ will be called snd-mts64.
config SND_SERIAL_U16550
tristate "UART16550 serial MIDI driver"
+ depends on HAS_IOPORT
select SND_RAWMIDI
help
To include support for MIDI serial port interfaces, say Y here
- and read <file:Documentation/sound/alsa/serial-u16550.txt>.
+ and read <file:Documentation/sound/cards/serial-u16550.rst>.
This driver works with serial UARTs 16550 and better.
This driver accesses the serial port hardware directly, so
@@ -147,8 +184,27 @@ config SND_SERIAL_U16550
To compile this driver as a module, choose M here: the module
will be called snd-serial-u16550.
+config SND_SERIAL_GENERIC
+ tristate "Generic serial MIDI driver"
+ depends on SERIAL_DEV_BUS
+ depends on OF
+ select SND_RAWMIDI
+ help
+ To include support for mapping generic serial devices as raw
+ ALSA MIDI devices, say Y here. The driver only supports setting
+ the serial port to standard baudrates. To attain the standard MIDI
+ baudrate of 31.25 kBaud, configure the clock of the underlying serial
+ device so that a requested 38.4 kBaud will result in the standard speed.
+
+ Use this devicetree binding to configure serial port mapping
+ <file:Documentation/devicetree/bindings/sound/serial-midi.yaml>
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-serial-generic.
+
config SND_MPU401
tristate "Generic MPU-401 UART driver"
+ depends on HAS_IOPORT
select SND_MPU401_UART
help
Say Y here to include support for MIDI ports compatible with
@@ -168,18 +224,6 @@ config SND_PORTMAN2X4
To compile this driver as a module, choose M here: the module
will be called snd-portman2x4.
-config SND_ML403_AC97CR
- tristate "Xilinx ML403 AC97 Controller Reference"
- depends on XILINX_VIRTEX
- select SND_AC97_CODEC
- help
- Say Y here to include support for the
- opb_ac97_controller_ref_v1_00_a ip core found in Xilinx's ML403
- reference design.
-
- To compile this driver as a module, choose M here: the module
- will be called snd-ml403_ac97cr.
-
config SND_AC97_POWER_SAVE
bool "AC97 Power-Saving Mode"
depends on SND_AC97_CODEC
@@ -207,7 +251,7 @@ config SND_AC97_POWER_SAVE
the device frequently. A value of 10 seconds would be a
good choice for normal operations.
- See Documentation/sound/alsa/powersave.txt for more details.
+ See Documentation/sound/designs/powersave.rst for more details.
config SND_AC97_POWER_SAVE_DEFAULT
int "Default time-out for AC97 power-save mode"
diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile
index 1a8440c8b138..a08bdd70ec9c 100644
--- a/sound/drivers/Makefile
+++ b/sound/drivers/Makefile
@@ -1,25 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-dummy-objs := dummy.o
-snd-aloop-objs := aloop.o
-snd-mtpav-objs := mtpav.o
-snd-mts64-objs := mts64.o
-snd-portman2x4-objs := portman2x4.o
-snd-serial-u16550-objs := serial-u16550.o
-snd-virmidi-objs := virmidi.o
-snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o
+snd-dummy-y := dummy.o
+snd-aloop-y := aloop.o
+snd-mtpav-y := mtpav.o
+snd-mts64-y := mts64.o
+snd-pcmtest-y := pcmtest.o
+snd-portman2x4-y := portman2x4.o
+snd-serial-u16550-y := serial-u16550.o
+snd-serial-generic-y := serial-generic.o
+snd-virmidi-y := virmidi.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
obj-$(CONFIG_SND_ALOOP) += snd-aloop.o
obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o
+obj-$(CONFIG_SND_PCMTEST) += snd-pcmtest.o
obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
+obj-$(CONFIG_SND_SERIAL_GENERIC) += snd-serial-generic.o
obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
obj-$(CONFIG_SND_MTS64) += snd-mts64.o
obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o
-obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o
obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index 12b44b0b6777..64ef03b2d579 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Loopback soundcard
*
@@ -12,48 +13,36 @@
*
* A next major update in 2010 (separate timers for playback and capture):
* Copyright (c) Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <linux/time.h>
#include <linux/wait.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include <sound/info.h>
#include <sound/initval.h>
+#include <sound/timer.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("A loopback soundcard");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ALSA,Loopback soundcard}}");
#define MAX_PCM_SUBSTREAMS 8
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
+static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
static int pcm_notify[SNDRV_CARDS];
+static char *timer_source[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for loopback soundcard.");
@@ -65,11 +54,48 @@ module_param_array(pcm_substreams, int, NULL, 0444);
MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-8) for loopback driver.");
module_param_array(pcm_notify, int, NULL, 0444);
MODULE_PARM_DESC(pcm_notify, "Break capture when PCM format/rate/channels changes.");
+module_param_array(timer_source, charp, NULL, 0444);
+MODULE_PARM_DESC(timer_source, "Sound card name or number and device/subdevice number of timer to be used. Empty string for jiffies timer [default].");
#define NO_PITCH 100000
+#define CABLE_VALID_PLAYBACK BIT(SNDRV_PCM_STREAM_PLAYBACK)
+#define CABLE_VALID_CAPTURE BIT(SNDRV_PCM_STREAM_CAPTURE)
+#define CABLE_VALID_BOTH (CABLE_VALID_PLAYBACK | CABLE_VALID_CAPTURE)
+
+struct loopback_cable;
struct loopback_pcm;
+struct loopback_ops {
+ /* optional
+ * call in loopback->cable_lock
+ */
+ int (*open)(struct loopback_pcm *dpcm);
+ /* required
+ * call in cable->lock
+ */
+ int (*start)(struct loopback_pcm *dpcm);
+ /* required
+ * call in cable->lock
+ */
+ int (*stop)(struct loopback_pcm *dpcm);
+ /* optional */
+ int (*stop_sync)(struct loopback_pcm *dpcm);
+ /* optional */
+ int (*close_substream)(struct loopback_pcm *dpcm);
+ /* optional
+ * call in loopback->cable_lock
+ */
+ int (*close_cable)(struct loopback_pcm *dpcm);
+ /* optional
+ * call in cable->lock
+ */
+ unsigned int (*pos_update)(struct loopback_cable *cable);
+ /* optional */
+ void (*dpcm_info)(struct loopback_pcm *dpcm,
+ struct snd_info_buffer *buffer);
+};
+
struct loopback_cable {
spinlock_t lock;
struct loopback_pcm *streams[2];
@@ -78,18 +104,29 @@ struct loopback_cable {
unsigned int valid;
unsigned int running;
unsigned int pause;
+ /* timer specific */
+ const struct loopback_ops *ops;
+ /* If sound timer is used */
+ struct {
+ int stream;
+ struct snd_timer_id id;
+ struct work_struct event_work;
+ struct snd_timer_instance *instance;
+ } snd_timer;
};
struct loopback_setup {
unsigned int notify: 1;
unsigned int rate_shift;
- unsigned int format;
+ snd_pcm_format_t format;
unsigned int rate;
+ snd_pcm_access_t access;
unsigned int channels;
struct snd_ctl_elem_id active_id;
struct snd_ctl_elem_id format_id;
struct snd_ctl_elem_id rate_id;
struct snd_ctl_elem_id channels_id;
+ struct snd_ctl_elem_id access_id;
};
struct loopback {
@@ -98,6 +135,7 @@ struct loopback {
struct loopback_cable *cables[MAX_PCM_SUBSTREAMS][2];
struct snd_pcm *pcm[2];
struct loopback_setup setup[MAX_PCM_SUBSTREAMS][2];
+ const char *timer_source;
};
struct loopback_pcm {
@@ -115,10 +153,17 @@ struct loopback_pcm {
/* flags */
unsigned int period_update_pending :1;
/* timer stuff */
- unsigned int irq_pos; /* fractional IRQ position */
- unsigned int period_size_frac;
+ unsigned int irq_pos; /* fractional IRQ position in jiffies
+ * ticks
+ */
+ unsigned int period_size_frac; /* period size in jiffies ticks */
+ unsigned int last_drift;
unsigned long last_jiffies;
+ /* If jiffies timer is used */
struct timer_list timer;
+
+ /* size of per channel buffer in case of non-interleaved access */
+ unsigned int channel_buf_n;
};
static struct platform_device *devices[SNDRV_CARDS];
@@ -164,7 +209,8 @@ static inline unsigned int get_rate_shift(struct loopback_pcm *dpcm)
return get_setup(dpcm)->rate_shift;
}
-static void loopback_timer_start(struct loopback_pcm *dpcm)
+/* call in cable->lock */
+static int loopback_jiffies_timer_start(struct loopback_pcm *dpcm)
{
unsigned long tick;
unsigned int rate_shift = get_rate_shift(dpcm);
@@ -178,20 +224,115 @@ static void loopback_timer_start(struct loopback_pcm *dpcm)
dpcm->period_update_pending = 1;
}
tick = dpcm->period_size_frac - dpcm->irq_pos;
- tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps;
- dpcm->timer.expires = jiffies + tick;
- add_timer(&dpcm->timer);
+ tick = DIV_ROUND_UP(tick, dpcm->pcm_bps);
+ mod_timer(&dpcm->timer, jiffies + tick);
+
+ return 0;
+}
+
+/* call in cable->lock */
+static int loopback_snd_timer_start(struct loopback_pcm *dpcm)
+{
+ struct loopback_cable *cable = dpcm->cable;
+ int err;
+
+ /* Loopback device has to use same period as timer card. Therefore
+ * wake up for each snd_pcm_period_elapsed() call of timer card.
+ */
+ err = snd_timer_start(cable->snd_timer.instance, 1);
+ if (err < 0) {
+ /* do not report error if trying to start but already
+ * running. For example called by opposite substream
+ * of the same cable
+ */
+ if (err == -EBUSY)
+ return 0;
+
+ pcm_err(dpcm->substream->pcm,
+ "snd_timer_start(%d,%d,%d) failed with %d",
+ cable->snd_timer.id.card,
+ cable->snd_timer.id.device,
+ cable->snd_timer.id.subdevice,
+ err);
+ }
+
+ return err;
}
-static inline void loopback_timer_stop(struct loopback_pcm *dpcm)
+/* call in cable->lock */
+static inline int loopback_jiffies_timer_stop(struct loopback_pcm *dpcm)
{
- del_timer(&dpcm->timer);
+ timer_delete(&dpcm->timer);
dpcm->timer.expires = 0;
+
+ return 0;
}
-#define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK)
-#define CABLE_VALID_CAPTURE (1 << SNDRV_PCM_STREAM_CAPTURE)
-#define CABLE_VALID_BOTH (CABLE_VALID_PLAYBACK|CABLE_VALID_CAPTURE)
+/* call in cable->lock */
+static int loopback_snd_timer_stop(struct loopback_pcm *dpcm)
+{
+ struct loopback_cable *cable = dpcm->cable;
+ int err;
+
+ /* only stop if both devices (playback and capture) are not running */
+ if (cable->running ^ cable->pause)
+ return 0;
+
+ err = snd_timer_stop(cable->snd_timer.instance);
+ if (err < 0) {
+ pcm_err(dpcm->substream->pcm,
+ "snd_timer_stop(%d,%d,%d) failed with %d",
+ cable->snd_timer.id.card,
+ cable->snd_timer.id.device,
+ cable->snd_timer.id.subdevice,
+ err);
+ }
+
+ return err;
+}
+
+static inline int loopback_jiffies_timer_stop_sync(struct loopback_pcm *dpcm)
+{
+ timer_delete_sync(&dpcm->timer);
+
+ return 0;
+}
+
+/* call in loopback->cable_lock */
+static int loopback_snd_timer_close_cable(struct loopback_pcm *dpcm)
+{
+ struct loopback_cable *cable = dpcm->cable;
+
+ /* snd_timer was not opened */
+ if (!cable->snd_timer.instance)
+ return 0;
+
+ /* will only be called from free_cable() when other stream was
+ * already closed. Other stream cannot be reopened as long as
+ * loopback->cable_lock is locked. Therefore no need to lock
+ * cable->lock;
+ */
+ snd_timer_close(cable->snd_timer.instance);
+
+ /* wait till drain work has finished if requested */
+ cancel_work_sync(&cable->snd_timer.event_work);
+
+ snd_timer_instance_free(cable->snd_timer.instance);
+ memset(&cable->snd_timer, 0, sizeof(cable->snd_timer));
+
+ return 0;
+}
+
+static bool is_access_interleaved(snd_pcm_access_t access)
+{
+ switch (access) {
+ case SNDRV_PCM_ACCESS_MMAP_INTERLEAVED:
+ case SNDRV_PCM_ACCESS_RW_INTERLEAVED:
+ return true;
+ default:
+ return false;
+ }
+};
static int loopback_check_format(struct loopback_cable *cable, int stream)
{
@@ -211,7 +352,9 @@ static int loopback_check_format(struct loopback_cable *cable, int stream)
substream->runtime;
check = runtime->format != cruntime->format ||
runtime->rate != cruntime->rate ||
- runtime->channels != cruntime->channels;
+ runtime->channels != cruntime->channels ||
+ is_access_interleaved(runtime->access) !=
+ is_access_interleaved(cruntime->access);
if (!check)
return 0;
if (stream == SNDRV_PCM_STREAM_CAPTURE) {
@@ -239,6 +382,12 @@ static int loopback_check_format(struct loopback_cable *cable, int stream)
&setup->channels_id);
setup->channels = runtime->channels;
}
+ if (is_access_interleaved(setup->access) !=
+ is_access_interleaved(runtime->access)) {
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &setup->access_id);
+ setup->access = runtime->access;
+ }
}
return 0;
}
@@ -255,7 +404,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
struct snd_pcm_runtime *runtime = substream->runtime;
struct loopback_pcm *dpcm = runtime->private_data;
struct loopback_cable *cable = dpcm->cable;
- int err, stream = 1 << substream->stream;
+ int err = 0, stream = 1 << substream->stream;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -264,53 +413,47 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
return err;
dpcm->last_jiffies = jiffies;
dpcm->pcm_rate_shift = 0;
- spin_lock(&cable->lock);
- cable->running |= stream;
- cable->pause &= ~stream;
- spin_unlock(&cable->lock);
- loopback_timer_start(dpcm);
+ dpcm->last_drift = 0;
+ scoped_guard(spinlock, &cable->lock) {
+ cable->running |= stream;
+ cable->pause &= ~stream;
+ err = cable->ops->start(dpcm);
+ }
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
loopback_active_notify(dpcm);
break;
case SNDRV_PCM_TRIGGER_STOP:
- spin_lock(&cable->lock);
- cable->running &= ~stream;
- cable->pause &= ~stream;
- spin_unlock(&cable->lock);
- loopback_timer_stop(dpcm);
+ scoped_guard(spinlock, &cable->lock) {
+ cable->running &= ~stream;
+ cable->pause &= ~stream;
+ err = cable->ops->stop(dpcm);
+ }
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
loopback_active_notify(dpcm);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- spin_lock(&cable->lock);
- cable->pause |= stream;
- spin_unlock(&cable->lock);
- loopback_timer_stop(dpcm);
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ scoped_guard(spinlock, &cable->lock) {
+ cable->pause |= stream;
+ err = cable->ops->stop(dpcm);
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ loopback_active_notify(dpcm);
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- spin_lock(&cable->lock);
- dpcm->last_jiffies = jiffies;
- cable->pause &= ~stream;
- spin_unlock(&cable->lock);
- loopback_timer_start(dpcm);
+ case SNDRV_PCM_TRIGGER_RESUME:
+ scoped_guard(spinlock, &cable->lock) {
+ dpcm->last_jiffies = jiffies;
+ cable->pause &= ~stream;
+ err = cable->ops->start(dpcm);
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ loopback_active_notify(dpcm);
break;
default:
return -EINVAL;
}
- return 0;
-}
-
-static void params_change_substream(struct loopback_pcm *dpcm,
- struct snd_pcm_runtime *runtime)
-{
- struct snd_pcm_runtime *dst_runtime;
-
- if (dpcm == NULL || dpcm->substream == NULL)
- return;
- dst_runtime = dpcm->substream->runtime;
- if (dst_runtime == NULL)
- return;
- dst_runtime->hw = dpcm->cable->hw;
+ return err;
}
static void params_change(struct snd_pcm_substream *substream)
@@ -319,15 +462,18 @@ static void params_change(struct snd_pcm_substream *substream)
struct loopback_pcm *dpcm = runtime->private_data;
struct loopback_cable *cable = dpcm->cable;
- cable->hw.formats = (1ULL << runtime->format);
+ cable->hw.formats = pcm_format_to_bits(runtime->format);
cable->hw.rate_min = runtime->rate;
cable->hw.rate_max = runtime->rate;
cable->hw.channels_min = runtime->channels;
cable->hw.channels_max = runtime->channels;
- params_change_substream(cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
- runtime);
- params_change_substream(cable->streams[SNDRV_PCM_STREAM_CAPTURE],
- runtime);
+
+ if (cable->snd_timer.instance) {
+ cable->hw.period_bytes_min =
+ frames_to_bytes(runtime, runtime->period_size);
+ cable->hw.period_bytes_max = cable->hw.period_bytes_min;
+ }
+
}
static int loopback_prepare(struct snd_pcm_substream *substream)
@@ -335,9 +481,15 @@ static int loopback_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct loopback_pcm *dpcm = runtime->private_data;
struct loopback_cable *cable = dpcm->cable;
- int bps, salign;
+ int err, bps, salign;
- salign = (snd_pcm_format_width(runtime->format) *
+ if (cable->ops->stop_sync) {
+ err = cable->ops->stop_sync(dpcm);
+ if (err < 0)
+ return err;
+ }
+
+ salign = (snd_pcm_format_physical_width(runtime->format) *
runtime->channels) / 8;
bps = salign * runtime->rate;
if (bps <= 0 || salign <= 0)
@@ -345,6 +497,7 @@ static int loopback_prepare(struct snd_pcm_substream *substream)
dpcm->buf_pos = 0;
dpcm->pcm_buffer_size = frames_to_bytes(runtime, runtime->buffer_size);
+ dpcm->channel_buf_n = dpcm->pcm_buffer_size / runtime->channels;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
/* clear capture buffer */
dpcm->silent_size = dpcm->pcm_buffer_size;
@@ -358,13 +511,12 @@ static int loopback_prepare(struct snd_pcm_substream *substream)
dpcm->pcm_salign = salign;
dpcm->pcm_period_size = frames_to_bytes(runtime, runtime->period_size);
- mutex_lock(&dpcm->loopback->cable_lock);
+ guard(mutex)(&dpcm->loopback->cable_lock);
if (!(cable->valid & ~(1 << substream->stream)) ||
(get_setup(dpcm)->notify &&
substream->stream == SNDRV_PCM_STREAM_PLAYBACK))
params_change(substream);
cable->valid |= 1 << substream->stream;
- mutex_unlock(&dpcm->loopback->cable_lock);
return 0;
}
@@ -395,6 +547,22 @@ static void clear_capture_buf(struct loopback_pcm *dpcm, unsigned int bytes)
}
}
+static void copy_play_buf_part_n(struct loopback_pcm *play, struct loopback_pcm *capt,
+ unsigned int size, unsigned int src_off, unsigned int dst_off)
+{
+ unsigned int channels = capt->substream->runtime->channels;
+ unsigned int size_p_ch = size / channels;
+ unsigned int src_off_ch = src_off / channels;
+ unsigned int dst_off_ch = dst_off / channels;
+ int i;
+
+ for (i = 0; i < channels; i++) {
+ memcpy(capt->substream->runtime->dma_area + capt->channel_buf_n * i + dst_off_ch,
+ play->substream->runtime->dma_area + play->channel_buf_n * i + src_off_ch,
+ size_p_ch);
+ }
+}
+
static void copy_play_buf(struct loopback_pcm *play,
struct loopback_pcm *capt,
unsigned int bytes)
@@ -408,7 +576,7 @@ static void copy_play_buf(struct loopback_pcm *play,
/* check if playback is draining, trim the capture copy size
* when our pointer is at the end of playback ring buffer */
- if (runtime->status->state == SNDRV_PCM_STATE_DRAINING &&
+ if (runtime->state == SNDRV_PCM_STATE_DRAINING &&
snd_pcm_playback_hw_avail(runtime) < runtime->buffer_size) {
snd_pcm_uframes_t appl_ptr, appl_ptr1, diff;
appl_ptr = appl_ptr1 = runtime->control->appl_ptr;
@@ -429,7 +597,10 @@ static void copy_play_buf(struct loopback_pcm *play,
size = play->pcm_buffer_size - src_off;
if (dst_off + size > capt->pcm_buffer_size)
size = capt->pcm_buffer_size - dst_off;
- memcpy(dst + dst_off, src + src_off, size);
+ if (!is_access_interleaved(runtime->access))
+ copy_play_buf_part_n(play, capt, size, src_off, dst_off);
+ else
+ memcpy(dst + dst_off, src + src_off, size);
capt->silent_size = 0;
bytes -= size;
if (!bytes)
@@ -444,117 +615,324 @@ static void copy_play_buf(struct loopback_pcm *play,
}
}
-#define BYTEPOS_UPDATE_POSONLY 0
-#define BYTEPOS_UPDATE_CLEAR 1
-#define BYTEPOS_UPDATE_COPY 2
-
-static void loopback_bytepos_update(struct loopback_pcm *dpcm,
- unsigned int delta,
- unsigned int cmd)
+static inline unsigned int bytepos_delta(struct loopback_pcm *dpcm,
+ unsigned int jiffies_delta)
{
- unsigned int count;
unsigned long last_pos;
+ unsigned int delta;
last_pos = byte_pos(dpcm, dpcm->irq_pos);
- dpcm->irq_pos += delta * dpcm->pcm_bps;
- count = byte_pos(dpcm, dpcm->irq_pos) - last_pos;
- if (!count)
- return;
- if (cmd == BYTEPOS_UPDATE_CLEAR)
- clear_capture_buf(dpcm, count);
- else if (cmd == BYTEPOS_UPDATE_COPY)
- copy_play_buf(dpcm->cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
- dpcm->cable->streams[SNDRV_PCM_STREAM_CAPTURE],
- count);
- dpcm->buf_pos += count;
- dpcm->buf_pos %= dpcm->pcm_buffer_size;
+ dpcm->irq_pos += jiffies_delta * dpcm->pcm_bps;
+ delta = byte_pos(dpcm, dpcm->irq_pos) - last_pos;
+ if (delta >= dpcm->last_drift)
+ delta -= dpcm->last_drift;
+ dpcm->last_drift = 0;
if (dpcm->irq_pos >= dpcm->period_size_frac) {
dpcm->irq_pos %= dpcm->period_size_frac;
dpcm->period_update_pending = 1;
}
+ return delta;
+}
+
+static inline void bytepos_finish(struct loopback_pcm *dpcm,
+ unsigned int delta)
+{
+ dpcm->buf_pos += delta;
+ dpcm->buf_pos %= dpcm->pcm_buffer_size;
}
-static unsigned int loopback_pos_update(struct loopback_cable *cable)
+/* call in cable->lock */
+static unsigned int loopback_jiffies_timer_pos_update
+ (struct loopback_cable *cable)
{
struct loopback_pcm *dpcm_play =
cable->streams[SNDRV_PCM_STREAM_PLAYBACK];
struct loopback_pcm *dpcm_capt =
cable->streams[SNDRV_PCM_STREAM_CAPTURE];
- unsigned long delta_play = 0, delta_capt = 0;
- unsigned int running;
+ unsigned long delta_play = 0, delta_capt = 0, cur_jiffies;
+ unsigned int running, count1, count2;
- spin_lock(&cable->lock);
+ cur_jiffies = jiffies;
running = cable->running ^ cable->pause;
if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) {
- delta_play = jiffies - dpcm_play->last_jiffies;
+ delta_play = cur_jiffies - dpcm_play->last_jiffies;
dpcm_play->last_jiffies += delta_play;
}
if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) {
- delta_capt = jiffies - dpcm_capt->last_jiffies;
+ delta_capt = cur_jiffies - dpcm_capt->last_jiffies;
dpcm_capt->last_jiffies += delta_capt;
}
- if (delta_play == 0 && delta_capt == 0) {
- spin_unlock(&cable->lock);
- return running;
- }
+ if (delta_play == 0 && delta_capt == 0)
+ goto unlock;
if (delta_play > delta_capt) {
- loopback_bytepos_update(dpcm_play, delta_play - delta_capt,
- BYTEPOS_UPDATE_POSONLY);
+ count1 = bytepos_delta(dpcm_play, delta_play - delta_capt);
+ bytepos_finish(dpcm_play, count1);
delta_play = delta_capt;
} else if (delta_play < delta_capt) {
- loopback_bytepos_update(dpcm_capt, delta_capt - delta_play,
- BYTEPOS_UPDATE_CLEAR);
+ count1 = bytepos_delta(dpcm_capt, delta_capt - delta_play);
+ clear_capture_buf(dpcm_capt, count1);
+ bytepos_finish(dpcm_capt, count1);
delta_capt = delta_play;
}
- if (delta_play == 0 && delta_capt == 0) {
- spin_unlock(&cable->lock);
- return running;
- }
+ if (delta_play == 0 && delta_capt == 0)
+ goto unlock;
+
/* note delta_capt == delta_play at this moment */
- loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY);
- loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY);
- spin_unlock(&cable->lock);
+ count1 = bytepos_delta(dpcm_play, delta_play);
+ count2 = bytepos_delta(dpcm_capt, delta_capt);
+ if (count1 < count2) {
+ dpcm_capt->last_drift = count2 - count1;
+ count1 = count2;
+ } else if (count1 > count2) {
+ dpcm_play->last_drift = count1 - count2;
+ }
+ copy_play_buf(dpcm_play, dpcm_capt, count1);
+ bytepos_finish(dpcm_play, count1);
+ bytepos_finish(dpcm_capt, count1);
+ unlock:
return running;
}
-static void loopback_timer_function(unsigned long data)
+static void loopback_jiffies_timer_function(struct timer_list *t)
{
- struct loopback_pcm *dpcm = (struct loopback_pcm *)data;
- unsigned int running;
+ struct loopback_pcm *dpcm = timer_container_of(dpcm, t, timer);
+ bool period_elapsed = false;
+
+ scoped_guard(spinlock_irqsave, &dpcm->cable->lock) {
+ if (loopback_jiffies_timer_pos_update(dpcm->cable) &
+ (1 << dpcm->substream->stream)) {
+ loopback_jiffies_timer_start(dpcm);
+ if (dpcm->period_update_pending) {
+ dpcm->period_update_pending = 0;
+ period_elapsed = true;
+ break;
+ }
+ }
+ }
+
+ if (period_elapsed)
+ snd_pcm_period_elapsed(dpcm->substream);
+}
- running = loopback_pos_update(dpcm->cable);
- if (running & (1 << dpcm->substream->stream)) {
- loopback_timer_start(dpcm);
- if (dpcm->period_update_pending) {
- dpcm->period_update_pending = 0;
- snd_pcm_period_elapsed(dpcm->substream);
+/* call in cable->lock */
+static int loopback_snd_timer_check_resolution(struct snd_pcm_runtime *runtime,
+ unsigned long resolution)
+{
+ if (resolution != runtime->timer_resolution) {
+ struct loopback_pcm *dpcm = runtime->private_data;
+ struct loopback_cable *cable = dpcm->cable;
+ /* Worst case estimation of possible values for resolution
+ * resolution <= (512 * 1024) frames / 8kHz in nsec
+ * resolution <= 65.536.000.000 nsec
+ *
+ * period_size <= 65.536.000.000 nsec / 1000nsec/usec * 192kHz +
+ * 500.000
+ * period_size <= 12.582.912.000.000 <64bit
+ * / 1.000.000 usec/sec
+ */
+ snd_pcm_uframes_t period_size_usec =
+ resolution / 1000 * runtime->rate;
+ /* round to nearest sample rate */
+ snd_pcm_uframes_t period_size =
+ (period_size_usec + 500 * 1000) / (1000 * 1000);
+
+ pcm_err(dpcm->substream->pcm,
+ "Period size (%lu frames) of loopback device is not corresponding to timer resolution (%lu nsec = %lu frames) of card timer %d,%d,%d. Use period size of %lu frames for loopback device.",
+ runtime->period_size, resolution, period_size,
+ cable->snd_timer.id.card,
+ cable->snd_timer.id.device,
+ cable->snd_timer.id.subdevice,
+ period_size);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void loopback_snd_timer_period_elapsed(struct loopback_cable *cable,
+ int event,
+ unsigned long resolution)
+{
+ struct loopback_pcm *dpcm_play, *dpcm_capt;
+ struct snd_pcm_substream *substream_play, *substream_capt;
+ struct snd_pcm_runtime *valid_runtime;
+ unsigned int running, elapsed_bytes;
+ bool xrun = false;
+
+ scoped_guard(spinlock_irqsave, &cable->lock) {
+ running = cable->running ^ cable->pause;
+ /* no need to do anything if no stream is running */
+ if (!running)
+ return;
+
+ dpcm_play = cable->streams[SNDRV_PCM_STREAM_PLAYBACK];
+ dpcm_capt = cable->streams[SNDRV_PCM_STREAM_CAPTURE];
+
+ if (event == SNDRV_TIMER_EVENT_MSTOP) {
+ if (!dpcm_play ||
+ dpcm_play->substream->runtime->state !=
+ SNDRV_PCM_STATE_DRAINING)
+ return;
+ }
+
+ substream_play = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
+ dpcm_play->substream : NULL;
+ substream_capt = (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) ?
+ dpcm_capt->substream : NULL;
+ valid_runtime = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
+ dpcm_play->substream->runtime :
+ dpcm_capt->substream->runtime;
+
+ /* resolution is only valid for SNDRV_TIMER_EVENT_TICK events */
+ if (event == SNDRV_TIMER_EVENT_TICK) {
+ /* The hardware rules guarantee that playback and capture period
+ * are the same. Therefore only one device has to be checked
+ * here.
+ */
+ if (loopback_snd_timer_check_resolution(valid_runtime,
+ resolution) < 0) {
+ xrun = true;
+ break;
+ }
+ }
+
+ elapsed_bytes = frames_to_bytes(valid_runtime,
+ valid_runtime->period_size);
+ /* The same timer interrupt is used for playback and capture device */
+ if ((running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) &&
+ (running & (1 << SNDRV_PCM_STREAM_CAPTURE))) {
+ copy_play_buf(dpcm_play, dpcm_capt, elapsed_bytes);
+ bytepos_finish(dpcm_play, elapsed_bytes);
+ bytepos_finish(dpcm_capt, elapsed_bytes);
+ } else if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) {
+ bytepos_finish(dpcm_play, elapsed_bytes);
+ } else if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) {
+ clear_capture_buf(dpcm_capt, elapsed_bytes);
+ bytepos_finish(dpcm_capt, elapsed_bytes);
}
}
+
+ if (xrun) {
+ if (substream_play)
+ snd_pcm_stop_xrun(substream_play);
+ if (substream_capt)
+ snd_pcm_stop_xrun(substream_capt);
+ return;
+ }
+
+ if (substream_play)
+ snd_pcm_period_elapsed(substream_play);
+ if (substream_capt)
+ snd_pcm_period_elapsed(substream_capt);
+}
+
+static void loopback_snd_timer_function(struct snd_timer_instance *timeri,
+ unsigned long resolution,
+ unsigned long ticks)
+{
+ struct loopback_cable *cable = timeri->callback_data;
+
+ loopback_snd_timer_period_elapsed(cable, SNDRV_TIMER_EVENT_TICK,
+ resolution);
+}
+
+static void loopback_snd_timer_work(struct work_struct *work)
+{
+ struct loopback_cable *cable;
+
+ cable = container_of(work, struct loopback_cable, snd_timer.event_work);
+ loopback_snd_timer_period_elapsed(cable, SNDRV_TIMER_EVENT_MSTOP, 0);
+}
+
+static void loopback_snd_timer_event(struct snd_timer_instance *timeri,
+ int event,
+ struct timespec64 *tstamp,
+ unsigned long resolution)
+{
+ /* Do not lock cable->lock here because timer->lock is already hold.
+ * There are other functions which first lock cable->lock and than
+ * timer->lock e.g.
+ * loopback_trigger()
+ * spin_lock(&cable->lock)
+ * loopback_snd_timer_start()
+ * snd_timer_start()
+ * spin_lock(&timer->lock)
+ * Therefore when using the oposit order of locks here it could result
+ * in a deadlock.
+ */
+
+ if (event == SNDRV_TIMER_EVENT_MSTOP) {
+ struct loopback_cable *cable = timeri->callback_data;
+
+ /* sound card of the timer was stopped. Therefore there will not
+ * be any further timer callbacks. Due to this forward audio
+ * data from here if in draining state. When still in running
+ * state the streaming will be aborted by the usual timeout. It
+ * should not be aborted here because may be the timer sound
+ * card does only a recovery and the timer is back soon.
+ * This work triggers loopback_snd_timer_work()
+ */
+ schedule_work(&cable->snd_timer.event_work);
+ }
+}
+
+static void loopback_jiffies_timer_dpcm_info(struct loopback_pcm *dpcm,
+ struct snd_info_buffer *buffer)
+{
+ snd_iprintf(buffer, " update_pending:\t%u\n",
+ dpcm->period_update_pending);
+ snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos);
+ snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac);
+ snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n",
+ dpcm->last_jiffies, jiffies);
+ snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires);
+}
+
+static void loopback_snd_timer_dpcm_info(struct loopback_pcm *dpcm,
+ struct snd_info_buffer *buffer)
+{
+ struct loopback_cable *cable = dpcm->cable;
+
+ snd_iprintf(buffer, " sound timer:\thw:%d,%d,%d\n",
+ cable->snd_timer.id.card,
+ cable->snd_timer.id.device,
+ cable->snd_timer.id.subdevice);
+ snd_iprintf(buffer, " timer open:\t\t%s\n",
+ snd_pcm_direction_name(cable->snd_timer.stream));
}
static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct loopback_pcm *dpcm = runtime->private_data;
+ snd_pcm_uframes_t pos;
- loopback_pos_update(dpcm->cable);
- return bytes_to_frames(runtime, dpcm->buf_pos);
+ guard(spinlock)(&dpcm->cable->lock);
+ if (dpcm->cable->ops->pos_update)
+ dpcm->cable->ops->pos_update(dpcm->cable);
+ pos = dpcm->buf_pos;
+ return bytes_to_frames(runtime, pos);
}
-static struct snd_pcm_hardware loopback_pcm_hardware =
+static const struct snd_pcm_hardware loopback_pcm_hardware =
{
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE),
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_NONINTERLEAVED),
.formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |
- SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE),
- .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000,
+ SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE |
+ SNDRV_PCM_FMTBIT_DSD_U8 |
+ SNDRV_PCM_FMTBIT_DSD_U16_LE | SNDRV_PCM_FMTBIT_DSD_U16_BE |
+ SNDRV_PCM_FMTBIT_DSD_U32_LE | SNDRV_PCM_FMTBIT_DSD_U32_BE),
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_768000,
.rate_min = 8000,
- .rate_max = 192000,
+ .rate_max = 768000,
.channels_min = 1,
.channels_max = 32,
.buffer_bytes_max = 2 * 1024 * 1024,
@@ -573,22 +951,15 @@ static void loopback_runtime_free(struct snd_pcm_runtime *runtime)
kfree(dpcm);
}
-static int loopback_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
-}
-
static int loopback_hw_free(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct loopback_pcm *dpcm = runtime->private_data;
struct loopback_cable *cable = dpcm->cable;
- mutex_lock(&dpcm->loopback->cable_lock);
+ guard(mutex)(&dpcm->loopback->cable_lock);
cable->valid &= ~(1 << substream->stream);
- mutex_unlock(&dpcm->loopback->cable_lock);
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static unsigned int get_cable_index(struct snd_pcm_substream *substream)
@@ -602,26 +973,29 @@ static unsigned int get_cable_index(struct snd_pcm_substream *substream)
static int rule_format(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
+ struct loopback_pcm *dpcm = rule->private;
+ struct loopback_cable *cable = dpcm->cable;
+ struct snd_mask m;
- struct snd_pcm_hardware *hw = rule->private;
- struct snd_mask *maskp = hw_param_mask(params, rule->var);
-
- maskp->bits[0] &= (u_int32_t)hw->formats;
- maskp->bits[1] &= (u_int32_t)(hw->formats >> 32);
- memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */
- if (! maskp->bits[0] && ! maskp->bits[1])
- return -EINVAL;
- return 0;
+ snd_mask_none(&m);
+ scoped_guard(mutex, &dpcm->loopback->cable_lock) {
+ m.bits[0] = (u_int32_t)cable->hw.formats;
+ m.bits[1] = (u_int32_t)(cable->hw.formats >> 32);
+ }
+ return snd_mask_refine(hw_param_mask(params, rule->var), &m);
}
static int rule_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
- struct snd_pcm_hardware *hw = rule->private;
+ struct loopback_pcm *dpcm = rule->private;
+ struct loopback_cable *cable = dpcm->cable;
struct snd_interval t;
- t.min = hw->rate_min;
- t.max = hw->rate_max;
+ scoped_guard(mutex, &dpcm->loopback->cable_lock) {
+ t.min = cable->hw.rate_min;
+ t.max = cable->hw.rate_max;
+ }
t.openmin = t.openmax = 0;
t.integer = 0;
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
@@ -630,50 +1004,267 @@ static int rule_rate(struct snd_pcm_hw_params *params,
static int rule_channels(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
- struct snd_pcm_hardware *hw = rule->private;
+ struct loopback_pcm *dpcm = rule->private;
+ struct loopback_cable *cable = dpcm->cable;
struct snd_interval t;
- t.min = hw->channels_min;
- t.max = hw->channels_max;
+ scoped_guard(mutex, &dpcm->loopback->cable_lock) {
+ t.min = cable->hw.channels_min;
+ t.max = cable->hw.channels_max;
+ }
t.openmin = t.openmax = 0;
t.integer = 0;
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
}
+static int rule_period_bytes(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct loopback_pcm *dpcm = rule->private;
+ struct loopback_cable *cable = dpcm->cable;
+ struct snd_interval t;
+
+ scoped_guard(mutex, &dpcm->loopback->cable_lock) {
+ t.min = cable->hw.period_bytes_min;
+ t.max = cable->hw.period_bytes_max;
+ }
+ t.openmin = 0;
+ t.openmax = 0;
+ t.integer = 0;
+ return snd_interval_refine(hw_param_interval(params, rule->var), &t);
+}
+
+static void free_cable(struct snd_pcm_substream *substream)
+{
+ struct loopback *loopback = substream->private_data;
+ int dev = get_cable_index(substream);
+ struct loopback_cable *cable;
+
+ cable = loopback->cables[substream->number][dev];
+ if (!cable)
+ return;
+ if (cable->streams[!substream->stream]) {
+ /* other stream is still alive */
+ guard(spinlock_irq)(&cable->lock);
+ cable->streams[substream->stream] = NULL;
+ } else {
+ struct loopback_pcm *dpcm = substream->runtime->private_data;
+
+ if (cable->ops && cable->ops->close_cable && dpcm)
+ cable->ops->close_cable(dpcm);
+ /* free the cable */
+ loopback->cables[substream->number][dev] = NULL;
+ kfree(cable);
+ }
+}
+
+static int loopback_jiffies_timer_open(struct loopback_pcm *dpcm)
+{
+ timer_setup(&dpcm->timer, loopback_jiffies_timer_function, 0);
+
+ return 0;
+}
+
+static const struct loopback_ops loopback_jiffies_timer_ops = {
+ .open = loopback_jiffies_timer_open,
+ .start = loopback_jiffies_timer_start,
+ .stop = loopback_jiffies_timer_stop,
+ .stop_sync = loopback_jiffies_timer_stop_sync,
+ .close_substream = loopback_jiffies_timer_stop_sync,
+ .pos_update = loopback_jiffies_timer_pos_update,
+ .dpcm_info = loopback_jiffies_timer_dpcm_info,
+};
+
+static int loopback_parse_timer_id(const char *str,
+ struct snd_timer_id *tid)
+{
+ /* [<pref>:](<card name>|<card idx>)[{.,}<dev idx>[{.,}<subdev idx>]] */
+ const char * const sep_dev = ".,";
+ const char * const sep_pref = ":";
+ const char *name = str;
+ char *sep, save = '\0';
+ int card_idx = 0, dev = 0, subdev = 0;
+ int err;
+
+ sep = strpbrk(str, sep_pref);
+ if (sep)
+ name = sep + 1;
+ sep = strpbrk(name, sep_dev);
+ if (sep) {
+ save = *sep;
+ *sep = '\0';
+ }
+ err = kstrtoint(name, 0, &card_idx);
+ if (err == -EINVAL) {
+ /* Must be the name, not number */
+ for (card_idx = 0; card_idx < snd_ecards_limit; card_idx++) {
+ struct snd_card *card = snd_card_ref(card_idx);
+
+ if (card) {
+ if (!strcmp(card->id, name))
+ err = 0;
+ snd_card_unref(card);
+ }
+ if (!err)
+ break;
+ }
+ }
+ if (sep) {
+ *sep = save;
+ if (!err) {
+ char *sep2, save2 = '\0';
+
+ sep2 = strpbrk(sep + 1, sep_dev);
+ if (sep2) {
+ save2 = *sep2;
+ *sep2 = '\0';
+ }
+ err = kstrtoint(sep + 1, 0, &dev);
+ if (sep2) {
+ *sep2 = save2;
+ if (!err)
+ err = kstrtoint(sep2 + 1, 0, &subdev);
+ }
+ }
+ }
+ if (card_idx == -1)
+ tid->dev_class = SNDRV_TIMER_CLASS_GLOBAL;
+ if (!err && tid) {
+ tid->card = card_idx;
+ tid->device = dev;
+ tid->subdevice = subdev;
+ }
+ return err;
+}
+
+/* call in loopback->cable_lock */
+static int loopback_snd_timer_open(struct loopback_pcm *dpcm)
+{
+ int err = 0;
+ struct snd_timer_id tid = {
+ .dev_class = SNDRV_TIMER_CLASS_PCM,
+ .dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION,
+ };
+ struct snd_timer_instance *timeri;
+ struct loopback_cable *cable = dpcm->cable;
+
+ /* check if timer was already opened. It is only opened once
+ * per playback and capture subdevice (aka cable).
+ */
+ if (cable->snd_timer.instance)
+ goto exit;
+
+ err = loopback_parse_timer_id(dpcm->loopback->timer_source, &tid);
+ if (err < 0) {
+ pcm_err(dpcm->substream->pcm,
+ "Parsing timer source \'%s\' failed with %d",
+ dpcm->loopback->timer_source, err);
+ goto exit;
+ }
+
+ cable->snd_timer.stream = dpcm->substream->stream;
+ cable->snd_timer.id = tid;
+
+ timeri = snd_timer_instance_new(dpcm->loopback->card->id);
+ if (!timeri) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ /* The callback has to be called from another work. If
+ * SNDRV_TIMER_IFLG_FAST is specified it will be called from the
+ * snd_pcm_period_elapsed() call of the selected sound card.
+ * snd_pcm_period_elapsed() helds snd_pcm_stream_lock_irqsave().
+ * Due to our callback loopback_snd_timer_function() also calls
+ * snd_pcm_period_elapsed() which calls snd_pcm_stream_lock_irqsave().
+ * This would end up in a dead lock.
+ */
+ timeri->flags |= SNDRV_TIMER_IFLG_AUTO;
+ timeri->callback = loopback_snd_timer_function;
+ timeri->callback_data = (void *)cable;
+ timeri->ccallback = loopback_snd_timer_event;
+
+ /* initialise a work used for draining */
+ INIT_WORK(&cable->snd_timer.event_work, loopback_snd_timer_work);
+
+ /* The mutex loopback->cable_lock is kept locked.
+ * Therefore snd_timer_open() cannot be called a second time
+ * by the other device of the same cable.
+ * Therefore the following issue cannot happen:
+ * [proc1] Call loopback_timer_open() ->
+ * Unlock cable->lock for snd_timer_close/open() call
+ * [proc2] Call loopback_timer_open() -> snd_timer_open(),
+ * snd_timer_start()
+ * [proc1] Call snd_timer_open() and overwrite running timer
+ * instance
+ */
+ err = snd_timer_open(timeri, &cable->snd_timer.id, current->pid);
+ if (err < 0) {
+ pcm_err(dpcm->substream->pcm,
+ "snd_timer_open (%d,%d,%d) failed with %d",
+ cable->snd_timer.id.card,
+ cable->snd_timer.id.device,
+ cable->snd_timer.id.subdevice,
+ err);
+ snd_timer_instance_free(timeri);
+ goto exit;
+ }
+
+ cable->snd_timer.instance = timeri;
+
+exit:
+ return err;
+}
+
+/* stop_sync() is not required for sound timer because it does not need to be
+ * restarted in loopback_prepare() on Xrun recovery
+ */
+static const struct loopback_ops loopback_snd_timer_ops = {
+ .open = loopback_snd_timer_open,
+ .start = loopback_snd_timer_start,
+ .stop = loopback_snd_timer_stop,
+ .close_cable = loopback_snd_timer_close_cable,
+ .dpcm_info = loopback_snd_timer_dpcm_info,
+};
+
static int loopback_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct loopback *loopback = substream->private_data;
struct loopback_pcm *dpcm;
- struct loopback_cable *cable;
+ struct loopback_cable *cable = NULL;
int err = 0;
int dev = get_cable_index(substream);
- mutex_lock(&loopback->cable_lock);
+ guard(mutex)(&loopback->cable_lock);
dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
- if (!dpcm) {
- err = -ENOMEM;
- goto unlock;
- }
+ if (!dpcm)
+ return -ENOMEM;
dpcm->loopback = loopback;
dpcm->substream = substream;
- setup_timer(&dpcm->timer, loopback_timer_function,
- (unsigned long)dpcm);
cable = loopback->cables[substream->number][dev];
if (!cable) {
cable = kzalloc(sizeof(*cable), GFP_KERNEL);
if (!cable) {
- kfree(dpcm);
err = -ENOMEM;
goto unlock;
}
spin_lock_init(&cable->lock);
cable->hw = loopback_pcm_hardware;
+ if (loopback->timer_source)
+ cable->ops = &loopback_snd_timer_ops;
+ else
+ cable->ops = &loopback_jiffies_timer_ops;
loopback->cables[substream->number][dev] = cable;
}
dpcm->cable = cable;
- cable->streams[substream->stream] = dpcm;
+ runtime->private_data = dpcm;
+
+ if (cable->ops->open) {
+ err = cable->ops->open(dpcm);
+ if (err < 0)
+ goto unlock;
+ }
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
@@ -682,31 +1273,54 @@ static int loopback_open(struct snd_pcm_substream *substream)
/* are cached -> they do not reflect the actual state */
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_FORMAT,
- rule_format, &runtime->hw,
+ rule_format, dpcm,
SNDRV_PCM_HW_PARAM_FORMAT, -1);
if (err < 0)
goto unlock;
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
- rule_rate, &runtime->hw,
+ rule_rate, dpcm,
SNDRV_PCM_HW_PARAM_RATE, -1);
if (err < 0)
goto unlock;
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS,
- rule_channels, &runtime->hw,
+ rule_channels, dpcm,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (err < 0)
goto unlock;
- runtime->private_data = dpcm;
+ /* In case of sound timer the period time of both devices of the same
+ * loop has to be the same.
+ * This rule only takes effect if a sound timer was chosen
+ */
+ if (cable->snd_timer.instance) {
+ err = snd_pcm_hw_rule_add(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ rule_period_bytes, dpcm,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -1);
+ if (err < 0)
+ goto unlock;
+ }
+
+ /* loopback_runtime_free() has not to be called if kfree(dpcm) was
+ * already called here. Otherwise it will end up with a double free.
+ */
runtime->private_free = loopback_runtime_free;
if (get_notify(dpcm))
runtime->hw = loopback_pcm_hardware;
else
runtime->hw = cable->hw;
+
+ scoped_guard(spinlock_irq, &cable->lock) {
+ cable->streams[substream->stream] = dpcm;
+ }
+
unlock:
- mutex_unlock(&loopback->cable_lock);
+ if (err < 0) {
+ free_cable(substream);
+ kfree(dpcm);
+ }
return err;
}
@@ -714,48 +1328,26 @@ static int loopback_close(struct snd_pcm_substream *substream)
{
struct loopback *loopback = substream->private_data;
struct loopback_pcm *dpcm = substream->runtime->private_data;
- struct loopback_cable *cable;
- int dev = get_cable_index(substream);
+ int err = 0;
- loopback_timer_stop(dpcm);
- mutex_lock(&loopback->cable_lock);
- cable = loopback->cables[substream->number][dev];
- if (cable->streams[!substream->stream]) {
- /* other stream is still alive */
- cable->streams[substream->stream] = NULL;
- } else {
- /* free the cable */
- loopback->cables[substream->number][dev] = NULL;
- kfree(cable);
- }
- mutex_unlock(&loopback->cable_lock);
- return 0;
+ if (dpcm->cable->ops->close_substream)
+ err = dpcm->cable->ops->close_substream(dpcm);
+ guard(mutex)(&loopback->cable_lock);
+ free_cable(substream);
+ return err;
}
-static struct snd_pcm_ops loopback_playback_ops = {
- .open = loopback_open,
- .close = loopback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = loopback_hw_params,
- .hw_free = loopback_hw_free,
- .prepare = loopback_prepare,
- .trigger = loopback_trigger,
- .pointer = loopback_pointer,
-};
-
-static struct snd_pcm_ops loopback_capture_ops = {
+static const struct snd_pcm_ops loopback_pcm_ops = {
.open = loopback_open,
.close = loopback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = loopback_hw_params,
.hw_free = loopback_hw_free,
.prepare = loopback_prepare,
.trigger = loopback_trigger,
.pointer = loopback_pointer,
};
-static int __devinit loopback_pcm_new(struct loopback *loopback,
- int device, int substreams)
+static int loopback_pcm_new(struct loopback *loopback,
+ int device, int substreams)
{
struct snd_pcm *pcm;
int err;
@@ -764,18 +1356,15 @@ static int __devinit loopback_pcm_new(struct loopback *loopback,
substreams, substreams, &pcm);
if (err < 0)
return err;
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_capture_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_pcm_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_pcm_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
pcm->private_data = loopback;
pcm->info_flags = 0;
- strcpy(pcm->name, "Loopback PCM");
+ strscpy(pcm->name, "Loopback PCM");
loopback->pcm[device] = pcm;
-
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
- 0, 2 * 1024 * 1024);
return 0;
}
@@ -795,6 +1384,7 @@ static int loopback_rate_shift_get(struct snd_kcontrol *kcontrol,
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ guard(mutex)(&loopback->cable_lock);
ucontrol->value.integer.value[0] =
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].rate_shift;
@@ -813,14 +1403,13 @@ static int loopback_rate_shift_put(struct snd_kcontrol *kcontrol,
val = 80000;
if (val > 120000)
val = 120000;
- mutex_lock(&loopback->cable_lock);
+ guard(mutex)(&loopback->cable_lock);
if (val != loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].rate_shift) {
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].rate_shift = val;
change = 1;
}
- mutex_unlock(&loopback->cable_lock);
return change;
}
@@ -829,6 +1418,7 @@ static int loopback_notify_get(struct snd_kcontrol *kcontrol,
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ guard(mutex)(&loopback->cable_lock);
ucontrol->value.integer.value[0] =
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].notify;
@@ -843,6 +1433,7 @@ static int loopback_notify_put(struct snd_kcontrol *kcontrol,
int change = 0;
val = ucontrol->value.integer.value[0] ? 1 : 0;
+ guard(mutex)(&loopback->cable_lock);
if (val != loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].notify) {
loopback->setup[kcontrol->id.subdevice]
@@ -856,13 +1447,17 @@ static int loopback_active_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
- struct loopback_cable *cable = loopback->cables
- [kcontrol->id.subdevice][kcontrol->id.device ^ 1];
+ struct loopback_cable *cable;
+
unsigned int val = 0;
- if (cable != NULL)
- val = (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
- 1 : 0;
+ guard(mutex)(&loopback->cable_lock);
+ cable = loopback->cables[kcontrol->id.subdevice][kcontrol->id.device ^ 1];
+ if (cable != NULL) {
+ unsigned int running = cable->running ^ cable->pause;
+
+ val = (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ? 1 : 0;
+ }
ucontrol->value.integer.value[0] = val;
return 0;
}
@@ -873,7 +1468,7 @@ static int loopback_format_info(struct snd_kcontrol *kcontrol,
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
- uinfo->value.integer.max = SNDRV_PCM_FORMAT_LAST;
+ uinfo->value.integer.max = (__force int)SNDRV_PCM_FORMAT_LAST;
uinfo->value.integer.step = 1;
return 0;
}
@@ -884,7 +1479,7 @@ static int loopback_format_get(struct snd_kcontrol *kcontrol,
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] =
- loopback->setup[kcontrol->id.subdevice]
+ (__force int)loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].format;
return 0;
}
@@ -905,6 +1500,7 @@ static int loopback_rate_get(struct snd_kcontrol *kcontrol,
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ guard(mutex)(&loopback->cable_lock);
ucontrol->value.integer.value[0] =
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].rate;
@@ -927,13 +1523,36 @@ static int loopback_channels_get(struct snd_kcontrol *kcontrol,
{
struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ guard(mutex)(&loopback->cable_lock);
ucontrol->value.integer.value[0] =
loopback->setup[kcontrol->id.subdevice]
[kcontrol->id.device].channels;
return 0;
}
-static struct snd_kcontrol_new loopback_controls[] __devinitdata = {
+static int loopback_access_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ const char * const texts[] = {"Interleaved", "Non-interleaved"};
+
+ return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
+}
+
+static int loopback_access_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+ snd_pcm_access_t access;
+
+ guard(mutex)(&loopback->cable_lock);
+ access = loopback->setup[kcontrol->id.subdevice][kcontrol->id.device].access;
+
+ ucontrol->value.enumerated.item[0] = !is_access_interleaved(access);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new loopback_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "PCM Rate Shift 100000",
@@ -979,10 +1598,18 @@ static struct snd_kcontrol_new loopback_controls[] __devinitdata = {
.name = "PCM Slave Channels",
.info = loopback_channels_info,
.get = loopback_channels_get
-}
+},
+#define ACCESS_IDX 6
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Slave Access Mode",
+ .info = loopback_access_info,
+ .get = loopback_access_get,
+},
};
-static int __devinit loopback_mixer_new(struct loopback *loopback, int notify)
+static int loopback_mixer_new(struct loopback *loopback, int notify)
{
struct snd_card *card = loopback->card;
struct snd_pcm *pcm;
@@ -990,7 +1617,7 @@ static int __devinit loopback_mixer_new(struct loopback *loopback, int notify)
struct loopback_setup *setup;
int err, dev, substr, substr_count, idx;
- strcpy(card->mixername, "Loopback Mixer");
+ strscpy(card->mixername, "Loopback Mixer");
for (dev = 0; dev < 2; dev++) {
pcm = loopback->pcm[dev];
substr_count =
@@ -1000,6 +1627,7 @@ static int __devinit loopback_mixer_new(struct loopback *loopback, int notify)
setup->notify = notify;
setup->rate_shift = NO_PITCH;
setup->format = SNDRV_PCM_FORMAT_S16_LE;
+ setup->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
setup->rate = 48000;
setup->channels = 2;
for (idx = 0; idx < ARRAY_SIZE(loopback_controls);
@@ -1010,6 +1638,14 @@ static int __devinit loopback_mixer_new(struct loopback *loopback, int notify)
return -ENOMEM;
kctl->id.device = dev;
kctl->id.subdevice = substr;
+
+ /* Add the control before copying the id so that
+ * the numid field of the id is set in the copy.
+ */
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
+
switch (idx) {
case ACTIVE_IDX:
setup->active_id = kctl->id;
@@ -1023,20 +1659,18 @@ static int __devinit loopback_mixer_new(struct loopback *loopback, int notify)
case CHANNELS_IDX:
setup->channels_id = kctl->id;
break;
+ case ACCESS_IDX:
+ setup->access_id = kctl->id;
+ break;
default:
break;
}
- err = snd_ctl_add(card, kctl);
- if (err < 0)
- return err;
}
}
}
return 0;
}
-#ifdef CONFIG_PROC_FS
-
static void print_dpcm_info(struct snd_info_buffer *buffer,
struct loopback_pcm *dpcm,
const char *id)
@@ -1053,13 +1687,8 @@ static void print_dpcm_info(struct snd_info_buffer *buffer,
snd_iprintf(buffer, " bytes_per_sec:\t%u\n", dpcm->pcm_bps);
snd_iprintf(buffer, " sample_align:\t%u\n", dpcm->pcm_salign);
snd_iprintf(buffer, " rate_shift:\t\t%u\n", dpcm->pcm_rate_shift);
- snd_iprintf(buffer, " update_pending:\t%u\n",
- dpcm->period_update_pending);
- snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos);
- snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac);
- snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n",
- dpcm->last_jiffies, jiffies);
- snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires);
+ if (dpcm->cable->ops->dpcm_info)
+ dpcm->cable->ops->dpcm_info(dpcm, buffer);
}
static void print_substream_info(struct snd_info_buffer *buffer,
@@ -1087,44 +1716,71 @@ static void print_cable_info(struct snd_info_entry *entry,
struct loopback *loopback = entry->private_data;
int sub, num;
- mutex_lock(&loopback->cable_lock);
+ guard(mutex)(&loopback->cable_lock);
num = entry->name[strlen(entry->name)-1];
num = num == '0' ? 0 : 1;
for (sub = 0; sub < MAX_PCM_SUBSTREAMS; sub++)
print_substream_info(buffer, loopback, sub, num);
- mutex_unlock(&loopback->cable_lock);
}
-static int __devinit loopback_proc_new(struct loopback *loopback, int cidx)
+static int loopback_cable_proc_new(struct loopback *loopback, int cidx)
{
char name[32];
- struct snd_info_entry *entry;
- int err;
snprintf(name, sizeof(name), "cable#%d", cidx);
- err = snd_card_proc_new(loopback->card, name, &entry);
- if (err < 0)
- return err;
+ return snd_card_ro_proc_new(loopback->card, name, loopback,
+ print_cable_info);
+}
- snd_info_set_text_ops(entry, loopback, print_cable_info);
- return 0;
+static void loopback_set_timer_source(struct loopback *loopback,
+ const char *value)
+{
+ if (loopback->timer_source) {
+ devm_kfree(loopback->card->dev, loopback->timer_source);
+ loopback->timer_source = NULL;
+ }
+ if (value && *value)
+ loopback->timer_source = devm_kstrdup(loopback->card->dev,
+ value, GFP_KERNEL);
}
-#else /* !CONFIG_PROC_FS */
+static void print_timer_source_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct loopback *loopback = entry->private_data;
-#define loopback_proc_new(loopback, cidx) do { } while (0)
+ guard(mutex)(&loopback->cable_lock);
+ snd_iprintf(buffer, "%s\n",
+ loopback->timer_source ? loopback->timer_source : "");
+}
-#endif
+static void change_timer_source_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct loopback *loopback = entry->private_data;
+ char line[64];
-static int __devinit loopback_probe(struct platform_device *devptr)
+ guard(mutex)(&loopback->cable_lock);
+ if (!snd_info_get_line(buffer, line, sizeof(line)))
+ loopback_set_timer_source(loopback, strim(line));
+}
+
+static int loopback_timer_source_proc_new(struct loopback *loopback)
+{
+ return snd_card_rw_proc_new(loopback->card, "timer_source", loopback,
+ print_timer_source_info,
+ change_timer_source_info);
+}
+
+static int loopback_probe(struct platform_device *devptr)
{
struct snd_card *card;
struct loopback *loopback;
int dev = devptr->id;
int err;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct loopback), &card);
+ err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct loopback), &card);
if (err < 0)
return err;
loopback = card->private_data;
@@ -1135,73 +1791,57 @@ static int __devinit loopback_probe(struct platform_device *devptr)
pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
loopback->card = card;
+ loopback_set_timer_source(loopback, timer_source[dev]);
+
mutex_init(&loopback->cable_lock);
err = loopback_pcm_new(loopback, 0, pcm_substreams[dev]);
if (err < 0)
- goto __nodev;
+ return err;
err = loopback_pcm_new(loopback, 1, pcm_substreams[dev]);
if (err < 0)
- goto __nodev;
+ return err;
err = loopback_mixer_new(loopback, pcm_notify[dev] ? 1 : 0);
if (err < 0)
- goto __nodev;
- loopback_proc_new(loopback, 0);
- loopback_proc_new(loopback, 1);
- strcpy(card->driver, "Loopback");
- strcpy(card->shortname, "Loopback");
+ return err;
+ loopback_cable_proc_new(loopback, 0);
+ loopback_cable_proc_new(loopback, 1);
+ loopback_timer_source_proc_new(loopback);
+ strscpy(card->driver, "Loopback");
+ strscpy(card->shortname, "Loopback");
sprintf(card->longname, "Loopback %i", dev + 1);
err = snd_card_register(card);
- if (!err) {
- platform_set_drvdata(devptr, card);
- return 0;
- }
- __nodev:
- snd_card_free(card);
- return err;
-}
-
-static int __devexit loopback_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
+ if (err < 0)
+ return err;
+ platform_set_drvdata(devptr, card);
return 0;
}
-#ifdef CONFIG_PM
-static int loopback_suspend(struct platform_device *pdev,
- pm_message_t state)
+static int loopback_suspend(struct device *pdev)
{
- struct snd_card *card = platform_get_drvdata(pdev);
- struct loopback *loopback = card->private_data;
+ struct snd_card *card = dev_get_drvdata(pdev);
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
-
- snd_pcm_suspend_all(loopback->pcm[0]);
- snd_pcm_suspend_all(loopback->pcm[1]);
return 0;
}
-static int loopback_resume(struct platform_device *pdev)
+static int loopback_resume(struct device *pdev)
{
- struct snd_card *card = platform_get_drvdata(pdev);
+ struct snd_card *card = dev_get_drvdata(pdev);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif
+
+static DEFINE_SIMPLE_DEV_PM_OPS(loopback_pm, loopback_suspend, loopback_resume);
#define SND_LOOPBACK_DRIVER "snd_aloop"
static struct platform_driver loopback_driver = {
.probe = loopback_probe,
- .remove = __devexit_p(loopback_remove),
-#ifdef CONFIG_PM
- .suspend = loopback_suspend,
- .resume = loopback_resume,
-#endif
.driver = {
- .name = SND_LOOPBACK_DRIVER
+ .name = SND_LOOPBACK_DRIVER,
+ .pm = &loopback_pm,
},
};
@@ -1241,7 +1881,7 @@ static int __init alsa_card_loopback_init(void)
}
if (!cards) {
#ifdef MODULE
- printk(KERN_ERR "aloop: No loopback enabled\n");
+ pr_err("aloop: No loopback enabled\n");
#endif
loopback_unregister_all();
return -ENODEV;
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index 7f41990ed68b..1860ff75fe15 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -1,21 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Dummy soundcard
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -23,11 +9,12 @@
#include <linux/platform_device.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/hrtimer.h>
#include <linux/math64.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/tlv.h>
@@ -39,7 +26,6 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Dummy soundcard (/dev/null)");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}");
#define MAX_PCM_DEVICES 4
#define MAX_PCM_SUBSTREAMS 128
@@ -57,18 +43,22 @@ MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}");
#define USE_CHANNELS_MAX 2
#define USE_PERIODS_MIN 1
#define USE_PERIODS_MAX 1024
+#define USE_MIXER_VOLUME_LEVEL_MIN -50
+#define USE_MIXER_VOLUME_LEVEL_MAX 100
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
+static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
static char *model[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = NULL};
static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
//static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
+static int mixer_volume_level_min = USE_MIXER_VOLUME_LEVEL_MIN;
+static int mixer_volume_level_max = USE_MIXER_VOLUME_LEVEL_MAX;
#ifdef CONFIG_HIGH_RES_TIMERS
-static int hrtimer = 1;
+static bool hrtimer = 1;
#endif
-static int fake_buffer = 1;
+static bool fake_buffer = 1;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for dummy soundcard.");
@@ -84,6 +74,10 @@ module_param_array(pcm_substreams, int, NULL, 0444);
MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-128) for dummy driver.");
//module_param_array(midi_devs, int, NULL, 0444);
//MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver.");
+module_param(mixer_volume_level_min, int, 0444);
+MODULE_PARM_DESC(mixer_volume_level_min, "Minimum mixer volume level for dummy driver. Default: -50");
+module_param(mixer_volume_level_max, int, 0444);
+MODULE_PARM_DESC(mixer_volume_level_max, "Maximum mixer volume level for dummy driver. Default: 100");
module_param(fake_buffer, bool, 0444);
MODULE_PARM_DESC(fake_buffer, "Fake buffer allocations.");
#ifdef CONFIG_HIGH_RES_TIMERS
@@ -109,6 +103,9 @@ struct dummy_timer_ops {
snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *);
};
+#define get_dummy_ops(substream) \
+ (*(const struct dummy_timer_ops **)(substream)->runtime->private_data)
+
struct dummy_model {
const char *name;
int (*playback_constraints)(struct snd_pcm_runtime *runtime);
@@ -128,13 +125,15 @@ struct dummy_model {
struct snd_dummy {
struct snd_card *card;
- struct dummy_model *model;
+ const struct dummy_model *model;
struct snd_pcm *pcm;
struct snd_pcm_hardware pcm_hw;
spinlock_t mixer_lock;
int mixer_volume[MIXER_ADDR_LAST+1][2];
int capture_source[MIXER_ADDR_LAST+1][2];
- const struct dummy_timer_ops *timer_ops;
+ int iobox;
+ struct snd_kcontrol *cd_volume_ctl;
+ struct snd_kcontrol *cd_switch_ctl;
};
/*
@@ -153,13 +152,13 @@ static int emu10k1_playback_constraints(struct snd_pcm_runtime *runtime)
return 0;
}
-struct dummy_model model_emu10k1 = {
+static const struct dummy_model model_emu10k1 = {
.name = "emu10k1",
.playback_constraints = emu10k1_playback_constraints,
.buffer_bytes_max = 128 * 1024,
};
-struct dummy_model model_rme9652 = {
+static const struct dummy_model model_rme9652 = {
.name = "rme9652",
.buffer_bytes_max = 26 * 64 * 1024,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
@@ -169,7 +168,7 @@ struct dummy_model model_rme9652 = {
.periods_max = 2,
};
-struct dummy_model model_ice1712 = {
+static const struct dummy_model model_ice1712 = {
.name = "ice1712",
.buffer_bytes_max = 256 * 1024,
.formats = SNDRV_PCM_FMTBIT_S32_LE,
@@ -179,7 +178,7 @@ struct dummy_model model_ice1712 = {
.periods_max = 1024,
};
-struct dummy_model model_uda1341 = {
+static const struct dummy_model model_uda1341 = {
.name = "uda1341",
.buffer_bytes_max = 16380,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
@@ -189,7 +188,7 @@ struct dummy_model model_uda1341 = {
.periods_max = 255,
};
-struct dummy_model model_ac97 = {
+static const struct dummy_model model_ac97 = {
.name = "ac97",
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 2,
@@ -199,7 +198,7 @@ struct dummy_model model_ac97 = {
.rate_max = 48000,
};
-struct dummy_model model_ca0106 = {
+static const struct dummy_model model_ca0106 = {
.name = "ca0106",
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.buffer_bytes_max = ((65536-64)*8),
@@ -213,7 +212,7 @@ struct dummy_model model_ca0106 = {
.rate_max = 192000,
};
-struct dummy_model *dummy_models[] = {
+static const struct dummy_model *dummy_models[] = {
&model_emu10k1,
&model_rme9652,
&model_ice1712,
@@ -228,6 +227,8 @@ struct dummy_model *dummy_models[] = {
*/
struct dummy_systimer_pcm {
+ /* ops must be the first item */
+ const struct dummy_timer_ops *timer_ops;
spinlock_t lock;
struct timer_list timer;
unsigned long base_time;
@@ -242,9 +243,8 @@ struct dummy_systimer_pcm {
static void dummy_systimer_rearm(struct dummy_systimer_pcm *dpcm)
{
- dpcm->timer.expires = jiffies +
- (dpcm->frac_period_rest + dpcm->rate - 1) / dpcm->rate;
- add_timer(&dpcm->timer);
+ mod_timer(&dpcm->timer, jiffies +
+ DIV_ROUND_UP(dpcm->frac_period_rest, dpcm->rate));
}
static void dummy_systimer_update(struct dummy_systimer_pcm *dpcm)
@@ -269,19 +269,19 @@ static void dummy_systimer_update(struct dummy_systimer_pcm *dpcm)
static int dummy_systimer_start(struct snd_pcm_substream *substream)
{
struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
- spin_lock(&dpcm->lock);
+
+ guard(spinlock)(&dpcm->lock);
dpcm->base_time = jiffies;
dummy_systimer_rearm(dpcm);
- spin_unlock(&dpcm->lock);
return 0;
}
static int dummy_systimer_stop(struct snd_pcm_substream *substream)
{
struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
- spin_lock(&dpcm->lock);
- del_timer(&dpcm->timer);
- spin_unlock(&dpcm->lock);
+
+ guard(spinlock)(&dpcm->lock);
+ timer_delete(&dpcm->timer);
return 0;
}
@@ -300,18 +300,17 @@ static int dummy_systimer_prepare(struct snd_pcm_substream *substream)
return 0;
}
-static void dummy_systimer_callback(unsigned long data)
+static void dummy_systimer_callback(struct timer_list *t)
{
- struct dummy_systimer_pcm *dpcm = (struct dummy_systimer_pcm *)data;
- unsigned long flags;
+ struct dummy_systimer_pcm *dpcm = timer_container_of(dpcm, t, timer);
int elapsed = 0;
-
- spin_lock_irqsave(&dpcm->lock, flags);
- dummy_systimer_update(dpcm);
- dummy_systimer_rearm(dpcm);
- elapsed = dpcm->elapsed;
- dpcm->elapsed = 0;
- spin_unlock_irqrestore(&dpcm->lock, flags);
+
+ scoped_guard(spinlock_irqsave, &dpcm->lock) {
+ dummy_systimer_update(dpcm);
+ dummy_systimer_rearm(dpcm);
+ elapsed = dpcm->elapsed;
+ dpcm->elapsed = 0;
+ }
if (elapsed)
snd_pcm_period_elapsed(dpcm->substream);
}
@@ -320,13 +319,10 @@ static snd_pcm_uframes_t
dummy_systimer_pointer(struct snd_pcm_substream *substream)
{
struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
- snd_pcm_uframes_t pos;
- spin_lock(&dpcm->lock);
+ guard(spinlock)(&dpcm->lock);
dummy_systimer_update(dpcm);
- pos = dpcm->frac_pos / HZ;
- spin_unlock(&dpcm->lock);
- return pos;
+ return dpcm->frac_pos / HZ;
}
static int dummy_systimer_create(struct snd_pcm_substream *substream)
@@ -337,9 +333,7 @@ static int dummy_systimer_create(struct snd_pcm_substream *substream)
if (!dpcm)
return -ENOMEM;
substream->runtime->private_data = dpcm;
- init_timer(&dpcm->timer);
- dpcm->timer.data = (unsigned long) dpcm;
- dpcm->timer.function = dummy_systimer_callback;
+ timer_setup(&dpcm->timer, dummy_systimer_callback, 0);
spin_lock_init(&dpcm->lock);
dpcm->substream = substream;
return 0;
@@ -350,7 +344,7 @@ static void dummy_systimer_free(struct snd_pcm_substream *substream)
kfree(substream->runtime->private_data);
}
-static struct dummy_timer_ops dummy_systimer_ops = {
+static const struct dummy_timer_ops dummy_systimer_ops = {
.create = dummy_systimer_create,
.free = dummy_systimer_free,
.prepare = dummy_systimer_prepare,
@@ -365,21 +359,15 @@ static struct dummy_timer_ops dummy_systimer_ops = {
*/
struct dummy_hrtimer_pcm {
+ /* ops must be the first item */
+ const struct dummy_timer_ops *timer_ops;
ktime_t base_time;
ktime_t period_time;
atomic_t running;
struct hrtimer timer;
- struct tasklet_struct tasklet;
struct snd_pcm_substream *substream;
};
-static void dummy_hrtimer_pcm_elapsed(unsigned long priv)
-{
- struct dummy_hrtimer_pcm *dpcm = (struct dummy_hrtimer_pcm *)priv;
- if (atomic_read(&dpcm->running))
- snd_pcm_period_elapsed(dpcm->substream);
-}
-
static enum hrtimer_restart dummy_hrtimer_callback(struct hrtimer *timer)
{
struct dummy_hrtimer_pcm *dpcm;
@@ -387,7 +375,14 @@ static enum hrtimer_restart dummy_hrtimer_callback(struct hrtimer *timer)
dpcm = container_of(timer, struct dummy_hrtimer_pcm, timer);
if (!atomic_read(&dpcm->running))
return HRTIMER_NORESTART;
- tasklet_schedule(&dpcm->tasklet);
+ /*
+ * In cases of XRUN and draining, this calls .trigger to stop PCM
+ * substream.
+ */
+ snd_pcm_period_elapsed(dpcm->substream);
+ if (!atomic_read(&dpcm->running))
+ return HRTIMER_NORESTART;
+
hrtimer_forward_now(timer, dpcm->period_time);
return HRTIMER_RESTART;
}
@@ -397,7 +392,7 @@ static int dummy_hrtimer_start(struct snd_pcm_substream *substream)
struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
dpcm->base_time = hrtimer_cb_get_time(&dpcm->timer);
- hrtimer_start(&dpcm->timer, dpcm->period_time, HRTIMER_MODE_REL);
+ hrtimer_start(&dpcm->timer, dpcm->period_time, HRTIMER_MODE_REL_SOFT);
atomic_set(&dpcm->running, 1);
return 0;
}
@@ -407,13 +402,14 @@ static int dummy_hrtimer_stop(struct snd_pcm_substream *substream)
struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
atomic_set(&dpcm->running, 0);
- hrtimer_cancel(&dpcm->timer);
+ if (!hrtimer_callback_running(&dpcm->timer))
+ hrtimer_cancel(&dpcm->timer);
return 0;
}
static inline void dummy_hrtimer_sync(struct dummy_hrtimer_pcm *dpcm)
{
- tasklet_kill(&dpcm->tasklet);
+ hrtimer_cancel(&dpcm->timer);
}
static snd_pcm_uframes_t
@@ -458,12 +454,9 @@ static int dummy_hrtimer_create(struct snd_pcm_substream *substream)
if (!dpcm)
return -ENOMEM;
substream->runtime->private_data = dpcm;
- hrtimer_init(&dpcm->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- dpcm->timer.function = dummy_hrtimer_callback;
+ hrtimer_setup(&dpcm->timer, dummy_hrtimer_callback, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
dpcm->substream = substream;
atomic_set(&dpcm->running, 0);
- tasklet_init(&dpcm->tasklet, dummy_hrtimer_pcm_elapsed,
- (unsigned long)dpcm);
return 0;
}
@@ -474,7 +467,7 @@ static void dummy_hrtimer_free(struct snd_pcm_substream *substream)
kfree(dpcm);
}
-static struct dummy_timer_ops dummy_hrtimer_ops = {
+static const struct dummy_timer_ops dummy_hrtimer_ops = {
.create = dummy_hrtimer_create,
.free = dummy_hrtimer_free,
.prepare = dummy_hrtimer_prepare,
@@ -491,34 +484,28 @@ static struct dummy_timer_ops dummy_hrtimer_ops = {
static int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
- struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
-
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
- return dummy->timer_ops->start(substream);
+ return get_dummy_ops(substream)->start(substream);
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
- return dummy->timer_ops->stop(substream);
+ return get_dummy_ops(substream)->stop(substream);
}
return -EINVAL;
}
static int dummy_pcm_prepare(struct snd_pcm_substream *substream)
{
- struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
-
- return dummy->timer_ops->prepare(substream);
+ return get_dummy_ops(substream)->prepare(substream);
}
static snd_pcm_uframes_t dummy_pcm_pointer(struct snd_pcm_substream *substream)
{
- struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
-
- return dummy->timer_ops->pointer(substream);
+ return get_dummy_ops(substream)->pointer(substream);
}
-static struct snd_pcm_hardware dummy_pcm_hardware = {
+static const struct snd_pcm_hardware dummy_pcm_hardware = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
@@ -545,33 +532,27 @@ static int dummy_pcm_hw_params(struct snd_pcm_substream *substream,
substream->runtime->dma_bytes = params_buffer_bytes(hw_params);
return 0;
}
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-static int dummy_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- if (fake_buffer)
- return 0;
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static int dummy_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
- struct dummy_model *model = dummy->model;
+ const struct dummy_model *model = dummy->model;
struct snd_pcm_runtime *runtime = substream->runtime;
+ const struct dummy_timer_ops *ops;
int err;
- dummy->timer_ops = &dummy_systimer_ops;
+ ops = &dummy_systimer_ops;
#ifdef CONFIG_HIGH_RES_TIMERS
if (hrtimer)
- dummy->timer_ops = &dummy_hrtimer_ops;
+ ops = &dummy_hrtimer_ops;
#endif
- err = dummy->timer_ops->create(substream);
+ err = ops->create(substream);
if (err < 0)
return err;
+ get_dummy_ops(substream) = ops;
runtime->hw = dummy->pcm_hw;
if (substream->pcm->device & 1) {
@@ -593,7 +574,7 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream)
err = model->capture_constraints(substream->runtime);
}
if (err < 0) {
- dummy->timer_ops->free(substream);
+ get_dummy_ops(substream)->free(substream);
return err;
}
return 0;
@@ -601,8 +582,7 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream)
static int dummy_pcm_close(struct snd_pcm_substream *substream)
{
- struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
- dummy->timer_ops->free(substream);
+ get_dummy_ops(substream)->free(substream);
return 0;
}
@@ -641,15 +621,15 @@ static int alloc_fake_buffer(void)
}
static int dummy_pcm_copy(struct snd_pcm_substream *substream,
- int channel, snd_pcm_uframes_t pos,
- void __user *dst, snd_pcm_uframes_t count)
+ int channel, unsigned long pos,
+ struct iov_iter *iter, unsigned long bytes)
{
return 0; /* do nothing */
}
static int dummy_pcm_silence(struct snd_pcm_substream *substream,
- int channel, snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
+ int channel, unsigned long pos,
+ unsigned long bytes)
{
return 0; /* do nothing */
}
@@ -660,36 +640,32 @@ static struct page *dummy_pcm_page(struct snd_pcm_substream *substream,
return virt_to_page(dummy_page[substream->stream]); /* the same page */
}
-static struct snd_pcm_ops dummy_pcm_ops = {
+static const struct snd_pcm_ops dummy_pcm_ops = {
.open = dummy_pcm_open,
.close = dummy_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = dummy_pcm_hw_params,
- .hw_free = dummy_pcm_hw_free,
.prepare = dummy_pcm_prepare,
.trigger = dummy_pcm_trigger,
.pointer = dummy_pcm_pointer,
};
-static struct snd_pcm_ops dummy_pcm_ops_no_buf = {
+static const struct snd_pcm_ops dummy_pcm_ops_no_buf = {
.open = dummy_pcm_open,
.close = dummy_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = dummy_pcm_hw_params,
- .hw_free = dummy_pcm_hw_free,
.prepare = dummy_pcm_prepare,
.trigger = dummy_pcm_trigger,
.pointer = dummy_pcm_pointer,
.copy = dummy_pcm_copy,
- .silence = dummy_pcm_silence,
+ .fill_silence = dummy_pcm_silence,
.page = dummy_pcm_page,
};
-static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
- int substreams)
+static int snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
+ int substreams)
{
struct snd_pcm *pcm;
- struct snd_pcm_ops *ops;
+ const struct snd_pcm_ops *ops;
int err;
err = snd_pcm_new(dummy->card, "Dummy PCM", device,
@@ -705,11 +681,11 @@ static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, ops);
pcm->private_data = dummy;
pcm->info_flags = 0;
- strcpy(pcm->name, "Dummy PCM");
+ strscpy(pcm->name, "Dummy PCM");
if (!fake_buffer) {
- snd_pcm_lib_preallocate_pages_for_all(pcm,
+ snd_pcm_set_managed_buffer_all(pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
+ NULL,
0, 64*1024);
}
return 0;
@@ -733,21 +709,20 @@ static int snd_dummy_volume_info(struct snd_kcontrol *kcontrol,
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
- uinfo->value.integer.min = -50;
- uinfo->value.integer.max = 100;
+ uinfo->value.integer.min = mixer_volume_level_min;
+ uinfo->value.integer.max = mixer_volume_level_max;
return 0;
}
-
+
static int snd_dummy_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol);
int addr = kcontrol->private_value;
- spin_lock_irq(&dummy->mixer_lock);
+ guard(spinlock_irq)(&dummy->mixer_lock);
ucontrol->value.integer.value[0] = dummy->mixer_volume[addr][0];
ucontrol->value.integer.value[1] = dummy->mixer_volume[addr][1];
- spin_unlock_irq(&dummy->mixer_lock);
return 0;
}
@@ -759,21 +734,20 @@ static int snd_dummy_volume_put(struct snd_kcontrol *kcontrol,
int left, right;
left = ucontrol->value.integer.value[0];
- if (left < -50)
- left = -50;
- if (left > 100)
- left = 100;
+ if (left < mixer_volume_level_min)
+ left = mixer_volume_level_min;
+ if (left > mixer_volume_level_max)
+ left = mixer_volume_level_max;
right = ucontrol->value.integer.value[1];
- if (right < -50)
- right = -50;
- if (right > 100)
- right = 100;
- spin_lock_irq(&dummy->mixer_lock);
+ if (right < mixer_volume_level_min)
+ right = mixer_volume_level_min;
+ if (right > mixer_volume_level_max)
+ right = mixer_volume_level_max;
+ guard(spinlock_irq)(&dummy->mixer_lock);
change = dummy->mixer_volume[addr][0] != left ||
dummy->mixer_volume[addr][1] != right;
dummy->mixer_volume[addr][0] = left;
dummy->mixer_volume[addr][1] = right;
- spin_unlock_irq(&dummy->mixer_lock);
return change;
}
@@ -786,17 +760,16 @@ static const DECLARE_TLV_DB_SCALE(db_scale_dummy, -4500, 30, 0);
.private_value = addr }
#define snd_dummy_capsrc_info snd_ctl_boolean_stereo_info
-
+
static int snd_dummy_capsrc_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol);
int addr = kcontrol->private_value;
- spin_lock_irq(&dummy->mixer_lock);
+ guard(spinlock_irq)(&dummy->mixer_lock);
ucontrol->value.integer.value[0] = dummy->capture_source[addr][0];
ucontrol->value.integer.value[1] = dummy->capture_source[addr][1];
- spin_unlock_irq(&dummy->mixer_lock);
return 0;
}
@@ -808,16 +781,66 @@ static int snd_dummy_capsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
left = ucontrol->value.integer.value[0] & 1;
right = ucontrol->value.integer.value[1] & 1;
- spin_lock_irq(&dummy->mixer_lock);
+ guard(spinlock_irq)(&dummy->mixer_lock);
change = dummy->capture_source[addr][0] != left &&
dummy->capture_source[addr][1] != right;
dummy->capture_source[addr][0] = left;
dummy->capture_source[addr][1] = right;
- spin_unlock_irq(&dummy->mixer_lock);
return change;
}
-static struct snd_kcontrol_new snd_dummy_controls[] = {
+static int snd_dummy_iobox_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[] = { "None", "CD Player" };
+
+ return snd_ctl_enum_info(info, 1, 2, names);
+}
+
+static int snd_dummy_iobox_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol);
+
+ value->value.enumerated.item[0] = dummy->iobox;
+ return 0;
+}
+
+static int snd_dummy_iobox_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol);
+ int changed;
+
+ if (value->value.enumerated.item[0] > 1)
+ return -EINVAL;
+
+ changed = value->value.enumerated.item[0] != dummy->iobox;
+ if (changed) {
+ dummy->iobox = value->value.enumerated.item[0];
+
+ if (dummy->iobox) {
+ dummy->cd_volume_ctl->vd[0].access &=
+ ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ dummy->cd_switch_ctl->vd[0].access &=
+ ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ } else {
+ dummy->cd_volume_ctl->vd[0].access |=
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ dummy->cd_switch_ctl->vd[0].access |=
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ }
+
+ snd_ctl_notify(dummy->card, SNDRV_CTL_EVENT_MASK_INFO,
+ &dummy->cd_volume_ctl->id);
+ snd_ctl_notify(dummy->card, SNDRV_CTL_EVENT_MASK_INFO,
+ &dummy->cd_switch_ctl->id);
+ }
+
+ return changed;
+}
+
+static const struct snd_kcontrol_new snd_dummy_controls[] = {
DUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER),
DUMMY_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER),
DUMMY_VOLUME("Synth Volume", 0, MIXER_ADDR_SYNTH),
@@ -827,37 +850,52 @@ DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_LINE),
DUMMY_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC),
DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MIC),
DUMMY_VOLUME("CD Volume", 0, MIXER_ADDR_CD),
-DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_CD)
+DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_CD),
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "External I/O Box",
+ .info = snd_dummy_iobox_info,
+ .get = snd_dummy_iobox_get,
+ .put = snd_dummy_iobox_put,
+},
};
-static int __devinit snd_card_dummy_new_mixer(struct snd_dummy *dummy)
+static int snd_card_dummy_new_mixer(struct snd_dummy *dummy)
{
struct snd_card *card = dummy->card;
+ struct snd_kcontrol *kcontrol;
unsigned int idx;
int err;
spin_lock_init(&dummy->mixer_lock);
- strcpy(card->mixername, "Dummy Mixer");
+ strscpy(card->mixername, "Dummy Mixer");
+ dummy->iobox = 1;
for (idx = 0; idx < ARRAY_SIZE(snd_dummy_controls); idx++) {
- err = snd_ctl_add(card, snd_ctl_new1(&snd_dummy_controls[idx], dummy));
+ kcontrol = snd_ctl_new1(&snd_dummy_controls[idx], dummy);
+ err = snd_ctl_add(card, kcontrol);
if (err < 0)
return err;
+ if (!strcmp(kcontrol->id.name, "CD Volume"))
+ dummy->cd_volume_ctl = kcontrol;
+ else if (!strcmp(kcontrol->id.name, "CD Capture Switch"))
+ dummy->cd_switch_ctl = kcontrol;
+
}
return 0;
}
-#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_PROC_FS)
+#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_PROC_FS)
/*
* proc interface
*/
static void print_formats(struct snd_dummy *dummy,
struct snd_info_buffer *buffer)
{
- int i;
+ snd_pcm_format_t i;
- for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
- if (dummy->pcm_hw.formats & (1ULL << i))
+ pcm_for_each_format(i) {
+ if (dummy->pcm_hw.formats & pcm_format_to_bits(i))
snd_iprintf(buffer, " %s", snd_pcm_format_name(i));
}
}
@@ -865,7 +903,7 @@ static void print_formats(struct snd_dummy *dummy,
static void print_rates(struct snd_dummy *dummy,
struct snd_info_buffer *buffer)
{
- static int rates[] = {
+ static const int rates[] = {
5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
64000, 88200, 96000, 176400, 192000,
};
@@ -897,7 +935,7 @@ struct dummy_hw_field {
.offset = offsetof(struct snd_pcm_hardware, item), \
.size = sizeof(dummy_pcm_hardware.item) }
-static struct dummy_hw_field fields[] = {
+static const struct dummy_hw_field fields[] = {
FIELD_ENTRY(formats, "%#llx"),
FIELD_ENTRY(rates, "%#x"),
FIELD_ENTRY(rate_min, "%d"),
@@ -953,7 +991,7 @@ static void dummy_proc_write(struct snd_info_entry *entry,
if (i >= ARRAY_SIZE(fields))
continue;
snd_info_get_str(item, ptr, sizeof(item));
- if (strict_strtoull(item, 0, &val))
+ if (kstrtoull(item, 0, &val))
continue;
if (fields[i].size == sizeof(int))
*get_dummy_int_ptr(dummy, fields[i].offset) = val;
@@ -962,39 +1000,32 @@ static void dummy_proc_write(struct snd_info_entry *entry,
}
}
-static void __devinit dummy_proc_init(struct snd_dummy *chip)
+static void dummy_proc_init(struct snd_dummy *chip)
{
- struct snd_info_entry *entry;
-
- if (!snd_card_proc_new(chip->card, "dummy_pcm", &entry)) {
- snd_info_set_text_ops(entry, chip, dummy_proc_read);
- entry->c.text.write = dummy_proc_write;
- entry->mode |= S_IWUSR;
- entry->private_data = chip;
- }
+ snd_card_rw_proc_new(chip->card, "dummy_pcm", chip,
+ dummy_proc_read, dummy_proc_write);
}
#else
#define dummy_proc_init(x)
-#endif /* CONFIG_SND_DEBUG && CONFIG_PROC_FS */
+#endif /* CONFIG_SND_DEBUG && CONFIG_SND_PROC_FS */
-static int __devinit snd_dummy_probe(struct platform_device *devptr)
+static int snd_dummy_probe(struct platform_device *devptr)
{
struct snd_card *card;
struct snd_dummy *dummy;
- struct dummy_model *m = NULL, **mdl;
+ const struct dummy_model *m = NULL, **mdl;
int idx, err;
int dev = devptr->id;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_dummy), &card);
+ err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_dummy), &card);
if (err < 0)
return err;
dummy = card->private_data;
dummy->card = card;
for (mdl = dummy_models; *mdl && model[dev]; mdl++) {
if (strcmp(model[dev], (*mdl)->name) == 0) {
- printk(KERN_INFO
- "snd-dummy: Using model '%s' for card %i\n",
+ pr_info("snd-dummy: Using model '%s' for card %i\n",
(*mdl)->name, card->number);
m = dummy->model = *mdl;
break;
@@ -1007,7 +1038,7 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)
pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
err = snd_card_dummy_pcm(dummy, idx, pcm_substreams[dev]);
if (err < 0)
- goto __nodev;
+ return err;
}
dummy->pcm_hw = dummy_pcm_hardware;
@@ -1036,65 +1067,53 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)
dummy->pcm_hw.channels_max = m->channels_max;
}
+ if (mixer_volume_level_min > mixer_volume_level_max) {
+ pr_warn("snd-dummy: Invalid mixer volume level: min=%d, max=%d. Fall back to default value.\n",
+ mixer_volume_level_min, mixer_volume_level_max);
+ mixer_volume_level_min = USE_MIXER_VOLUME_LEVEL_MIN;
+ mixer_volume_level_max = USE_MIXER_VOLUME_LEVEL_MAX;
+ }
err = snd_card_dummy_new_mixer(dummy);
if (err < 0)
- goto __nodev;
- strcpy(card->driver, "Dummy");
- strcpy(card->shortname, "Dummy");
+ return err;
+ strscpy(card->driver, "Dummy");
+ strscpy(card->shortname, "Dummy");
sprintf(card->longname, "Dummy %i", dev + 1);
dummy_proc_init(dummy);
- snd_card_set_dev(card, &devptr->dev);
-
err = snd_card_register(card);
- if (err == 0) {
- platform_set_drvdata(devptr, card);
- return 0;
- }
- __nodev:
- snd_card_free(card);
- return err;
-}
-
-static int __devexit snd_dummy_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
+ if (err < 0)
+ return err;
+ platform_set_drvdata(devptr, card);
return 0;
}
-#ifdef CONFIG_PM
-static int snd_dummy_suspend(struct platform_device *pdev, pm_message_t state)
+static int snd_dummy_suspend(struct device *pdev)
{
- struct snd_card *card = platform_get_drvdata(pdev);
- struct snd_dummy *dummy = card->private_data;
+ struct snd_card *card = dev_get_drvdata(pdev);
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(dummy->pcm);
return 0;
}
-
-static int snd_dummy_resume(struct platform_device *pdev)
+
+static int snd_dummy_resume(struct device *pdev)
{
- struct snd_card *card = platform_get_drvdata(pdev);
+ struct snd_card *card = dev_get_drvdata(pdev);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif
+
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_dummy_pm, snd_dummy_suspend, snd_dummy_resume);
#define SND_DUMMY_DRIVER "snd_dummy"
static struct platform_driver snd_dummy_driver = {
.probe = snd_dummy_probe,
- .remove = __devexit_p(snd_dummy_remove),
-#ifdef CONFIG_PM
- .suspend = snd_dummy_suspend,
- .resume = snd_dummy_resume,
-#endif
.driver = {
- .name = SND_DUMMY_DRIVER
+ .name = SND_DUMMY_DRIVER,
+ .pm = &snd_dummy_pm,
},
};
@@ -1140,7 +1159,7 @@ static int __init alsa_card_dummy_init(void)
}
if (!cards) {
#ifdef MODULE
- printk(KERN_ERR "Dummy soundcard not found or device busy\n");
+ pr_err("Dummy soundcard not found or device busy\n");
#endif
snd_dummy_unregister_all();
return -ENODEV;
diff --git a/sound/drivers/ml403-ac97cr.c b/sound/drivers/ml403-ac97cr.c
deleted file mode 100644
index a1282c1c0591..000000000000
--- a/sound/drivers/ml403-ac97cr.c
+++ /dev/null
@@ -1,1355 +0,0 @@
-/*
- * ALSA driver for Xilinx ML403 AC97 Controller Reference
- * IP: opb_ac97_controller_ref_v1_00_a (EDK 8.1i)
- * IP: opb_ac97_controller_ref_v1_00_a (EDK 9.1i)
- *
- * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-/* Some notes / status of this driver:
- *
- * - Don't wonder about some strange implementations of things - especially the
- * (heavy) shadowing of codec registers, with which I tried to reduce read
- * accesses to a minimum, because after a variable amount of accesses, the AC97
- * controller doesn't raise the register access finished bit anymore ...
- *
- * - Playback support seems to be pretty stable - no issues here.
- * - Capture support "works" now, too. Overruns don't happen any longer so often.
- * But there might still be some ...
- */
-
-#include <linux/init.h>
-#include <linux/moduleparam.h>
-
-#include <linux/platform_device.h>
-
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-
-/* HZ */
-#include <linux/param.h>
-/* jiffies, time_*() */
-#include <linux/jiffies.h>
-/* schedule_timeout*() */
-#include <linux/sched.h>
-/* spin_lock*() */
-#include <linux/spinlock.h>
-/* struct mutex, mutex_init(), mutex_*lock() */
-#include <linux/mutex.h>
-
-/* snd_printk(), snd_printd() */
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/ac97_codec.h>
-
-#include "pcm-indirect2.h"
-
-
-#define SND_ML403_AC97CR_DRIVER "ml403-ac97cr"
-
-MODULE_AUTHOR("Joachim Foerster <JOFT@gmx.de>");
-MODULE_DESCRIPTION("Xilinx ML403 AC97 Controller Reference");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Xilinx,ML403 AC97 Controller Reference}}");
-
-static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
-static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
-
-module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "Index value for ML403 AC97 Controller Reference.");
-module_param_array(id, charp, NULL, 0444);
-MODULE_PARM_DESC(id, "ID string for ML403 AC97 Controller Reference.");
-module_param_array(enable, bool, NULL, 0444);
-MODULE_PARM_DESC(enable, "Enable this ML403 AC97 Controller Reference.");
-
-/* Special feature options */
-/*#define CODEC_WRITE_CHECK_RAF*/ /* don't return after a write to a codec
- * register, while RAF bit is not set
- */
-/* Debug options for code which may be removed completely in a final version */
-#ifdef CONFIG_SND_DEBUG
-/*#define CODEC_STAT*/ /* turn on some minimal "statistics"
- * about codec register usage
- */
-#define SND_PCM_INDIRECT2_STAT /* turn on some "statistics" about the
- * process of copying bytes from the
- * intermediate buffer to the hardware
- * fifo and the other way round
- */
-#endif
-
-/* Definition of a "level/facility dependent" printk(); may be removed
- * completely in a final version
- */
-#undef PDEBUG
-#ifdef CONFIG_SND_DEBUG
-/* "facilities" for PDEBUG */
-#define UNKNOWN (1<<0)
-#define CODEC_SUCCESS (1<<1)
-#define CODEC_FAKE (1<<2)
-#define INIT_INFO (1<<3)
-#define INIT_FAILURE (1<<4)
-#define WORK_INFO (1<<5)
-#define WORK_FAILURE (1<<6)
-
-#define PDEBUG_FACILITIES (UNKNOWN | INIT_FAILURE | WORK_FAILURE)
-
-#define PDEBUG(fac, fmt, args...) do { \
- if (fac & PDEBUG_FACILITIES) \
- snd_printd(KERN_DEBUG SND_ML403_AC97CR_DRIVER ": " \
- fmt, ##args); \
- } while (0)
-#else
-#define PDEBUG(fac, fmt, args...) /* nothing */
-#endif
-
-
-
-/* Defines for "waits"/timeouts (portions of HZ=250 on arch/ppc by default) */
-#define CODEC_TIMEOUT_ON_INIT 5 /* timeout for checking for codec
- * readiness (after insmod)
- */
-#ifndef CODEC_WRITE_CHECK_RAF
-#define CODEC_WAIT_AFTER_WRITE 100 /* general, static wait after a write
- * access to a codec register, may be
- * 0 to completely remove wait
- */
-#else
-#define CODEC_TIMEOUT_AFTER_WRITE 5 /* timeout after a write access to a
- * codec register, if RAF bit is used
- */
-#endif
-#define CODEC_TIMEOUT_AFTER_READ 5 /* timeout after a read access to a
- * codec register (checking RAF bit)
- */
-
-/* Infrastructure for codec register shadowing */
-#define LM4550_REG_OK (1<<0) /* register exists */
-#define LM4550_REG_DONEREAD (1<<1) /* read register once, value should be
- * the same currently in the register
- */
-#define LM4550_REG_NOSAVE (1<<2) /* values written to this register will
- * not be saved in the register
- */
-#define LM4550_REG_NOSHADOW (1<<3) /* don't do register shadowing, use plain
- * hardware access
- */
-#define LM4550_REG_READONLY (1<<4) /* register is read only */
-#define LM4550_REG_FAKEPROBE (1<<5) /* fake write _and_ read actions during
- * probe() correctly
- */
-#define LM4550_REG_FAKEREAD (1<<6) /* fake read access, always return
- * default value
- */
-#define LM4550_REG_ALLFAKE (LM4550_REG_FAKEREAD | LM4550_REG_FAKEPROBE)
-
-struct lm4550_reg {
- u16 value;
- u16 flag;
- u16 wmask;
- u16 def;
-};
-
-struct lm4550_reg lm4550_regfile[64] = {
- [AC97_RESET / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_NOSAVE \
- | LM4550_REG_FAKEREAD,
- .def = 0x0D50},
- [AC97_MASTER / 2] = {.flag = LM4550_REG_OK
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8000},
- [AC97_HEADPHONE / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8000},
- [AC97_MASTER_MONO / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x801F,
- .def = 0x8000},
- [AC97_PC_BEEP / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x801E,
- .def = 0x0},
- [AC97_PHONE / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x801F,
- .def = 0x8008},
- [AC97_MIC / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x805F,
- .def = 0x8008},
- [AC97_LINE / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8808},
- [AC97_CD / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8808},
- [AC97_VIDEO / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8808},
- [AC97_AUX / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8808},
- [AC97_PCM / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x9F1F,
- .def = 0x8008},
- [AC97_REC_SEL / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x707,
- .def = 0x0},
- [AC97_REC_GAIN / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .wmask = 0x8F0F,
- .def = 0x8000},
- [AC97_GENERAL_PURPOSE / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .def = 0x0,
- .wmask = 0xA380},
- [AC97_3D_CONTROL / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEREAD \
- | LM4550_REG_READONLY,
- .def = 0x0101},
- [AC97_POWERDOWN / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_NOSHADOW \
- | LM4550_REG_NOSAVE,
- .wmask = 0xFF00},
- /* may not write ones to
- * REF/ANL/DAC/ADC bits
- * FIXME: Is this ok?
- */
- [AC97_EXTENDED_ID / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEREAD \
- | LM4550_REG_READONLY,
- .def = 0x0201}, /* primary codec */
- [AC97_EXTENDED_STATUS / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_NOSHADOW \
- | LM4550_REG_NOSAVE,
- .wmask = 0x1},
- [AC97_PCM_FRONT_DAC_RATE / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .def = 0xBB80,
- .wmask = 0xFFFF},
- [AC97_PCM_LR_ADC_RATE / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_FAKEPROBE,
- .def = 0xBB80,
- .wmask = 0xFFFF},
- [AC97_VENDOR_ID1 / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_READONLY \
- | LM4550_REG_FAKEREAD,
- .def = 0x4E53},
- [AC97_VENDOR_ID2 / 2] = {.flag = LM4550_REG_OK \
- | LM4550_REG_READONLY \
- | LM4550_REG_FAKEREAD,
- .def = 0x4350}
-};
-
-#define LM4550_RF_OK(reg) (lm4550_regfile[reg / 2].flag & LM4550_REG_OK)
-
-static void lm4550_regfile_init(void)
-{
- int i;
- for (i = 0; i < 64; i++)
- if (lm4550_regfile[i].flag & LM4550_REG_FAKEPROBE)
- lm4550_regfile[i].value = lm4550_regfile[i].def;
-}
-
-static void lm4550_regfile_write_values_after_init(struct snd_ac97 *ac97)
-{
- int i;
- for (i = 0; i < 64; i++)
- if ((lm4550_regfile[i].flag & LM4550_REG_FAKEPROBE) &&
- (lm4550_regfile[i].value != lm4550_regfile[i].def)) {
- PDEBUG(CODEC_FAKE, "lm4550_regfile_write_values_after_"
- "init(): reg=0x%x value=0x%x / %d is different "
- "from def=0x%x / %d\n",
- i, lm4550_regfile[i].value,
- lm4550_regfile[i].value, lm4550_regfile[i].def,
- lm4550_regfile[i].def);
- snd_ac97_write(ac97, i * 2, lm4550_regfile[i].value);
- lm4550_regfile[i].flag |= LM4550_REG_DONEREAD;
- }
-}
-
-
-/* direct registers */
-#define CR_REG(ml403_ac97cr, x) ((ml403_ac97cr)->port + CR_REG_##x)
-
-#define CR_REG_PLAYFIFO 0x00
-#define CR_PLAYDATA(a) ((a) & 0xFFFF)
-
-#define CR_REG_RECFIFO 0x04
-#define CR_RECDATA(a) ((a) & 0xFFFF)
-
-#define CR_REG_STATUS 0x08
-#define CR_RECOVER (1<<7)
-#define CR_PLAYUNDER (1<<6)
-#define CR_CODECREADY (1<<5)
-#define CR_RAF (1<<4)
-#define CR_RECEMPTY (1<<3)
-#define CR_RECFULL (1<<2)
-#define CR_PLAYHALF (1<<1)
-#define CR_PLAYFULL (1<<0)
-
-#define CR_REG_RESETFIFO 0x0C
-#define CR_RECRESET (1<<1)
-#define CR_PLAYRESET (1<<0)
-
-#define CR_REG_CODEC_ADDR 0x10
-/* UG082 says:
- * #define CR_CODEC_ADDR(a) ((a) << 1)
- * #define CR_CODEC_READ (1<<0)
- * #define CR_CODEC_WRITE (0<<0)
- */
-/* RefDesign example says: */
-#define CR_CODEC_ADDR(a) ((a) << 0)
-#define CR_CODEC_READ (1<<7)
-#define CR_CODEC_WRITE (0<<7)
-
-#define CR_REG_CODEC_DATAREAD 0x14
-#define CR_CODEC_DATAREAD(v) ((v) & 0xFFFF)
-
-#define CR_REG_CODEC_DATAWRITE 0x18
-#define CR_CODEC_DATAWRITE(v) ((v) & 0xFFFF)
-
-#define CR_FIFO_SIZE 32
-
-struct snd_ml403_ac97cr {
- /* lock for access to (controller) registers */
- spinlock_t reg_lock;
- /* mutex for the whole sequence of accesses to (controller) registers
- * which affect codec registers
- */
- struct mutex cdc_mutex;
-
- int irq; /* for playback */
- int enable_irq; /* for playback */
-
- int capture_irq;
- int enable_capture_irq;
-
- struct resource *res_port;
- void *port;
-
- struct snd_ac97 *ac97;
- int ac97_fake;
-#ifdef CODEC_STAT
- int ac97_read;
- int ac97_write;
-#endif
-
- struct platform_device *pfdev;
- struct snd_card *card;
- struct snd_pcm *pcm;
- struct snd_pcm_substream *playback_substream;
- struct snd_pcm_substream *capture_substream;
-
- struct snd_pcm_indirect2 ind_rec; /* for playback */
- struct snd_pcm_indirect2 capture_ind2_rec;
-};
-
-static struct snd_pcm_hardware snd_ml403_ac97cr_playback = {
- .info = (SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID),
- .formats = SNDRV_PCM_FMTBIT_S16_BE,
- .rates = (SNDRV_PCM_RATE_CONTINUOUS |
- SNDRV_PCM_RATE_8000_48000),
- .rate_min = 4000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = (128*1024),
- .period_bytes_min = CR_FIFO_SIZE/2,
- .period_bytes_max = (64*1024),
- .periods_min = 2,
- .periods_max = (128*1024)/(CR_FIFO_SIZE/2),
- .fifo_size = 0,
-};
-
-static struct snd_pcm_hardware snd_ml403_ac97cr_capture = {
- .info = (SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID),
- .formats = SNDRV_PCM_FMTBIT_S16_BE,
- .rates = (SNDRV_PCM_RATE_CONTINUOUS |
- SNDRV_PCM_RATE_8000_48000),
- .rate_min = 4000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = (128*1024),
- .period_bytes_min = CR_FIFO_SIZE/2,
- .period_bytes_max = (64*1024),
- .periods_min = 2,
- .periods_max = (128*1024)/(CR_FIFO_SIZE/2),
- .fifo_size = 0,
-};
-
-static size_t
-snd_ml403_ac97cr_playback_ind2_zero(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- int copied_words = 0;
- u32 full = 0;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- spin_lock(&ml403_ac97cr->reg_lock);
- while ((full = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
- CR_PLAYFULL)) != CR_PLAYFULL) {
- out_be32(CR_REG(ml403_ac97cr, PLAYFIFO), 0);
- copied_words++;
- }
- rec->hw_ready = 0;
- spin_unlock(&ml403_ac97cr->reg_lock);
-
- return (size_t) (copied_words * 2);
-}
-
-static size_t
-snd_ml403_ac97cr_playback_ind2_copy(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- size_t bytes)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- u16 *src;
- int copied_words = 0;
- u32 full = 0;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
- src = (u16 *)(substream->runtime->dma_area + rec->sw_data);
-
- spin_lock(&ml403_ac97cr->reg_lock);
- while (((full = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
- CR_PLAYFULL)) != CR_PLAYFULL) && (bytes > 1)) {
- out_be32(CR_REG(ml403_ac97cr, PLAYFIFO),
- CR_PLAYDATA(src[copied_words]));
- copied_words++;
- bytes = bytes - 2;
- }
- if (full != CR_PLAYFULL)
- rec->hw_ready = 1;
- else
- rec->hw_ready = 0;
- spin_unlock(&ml403_ac97cr->reg_lock);
-
- return (size_t) (copied_words * 2);
-}
-
-static size_t
-snd_ml403_ac97cr_capture_ind2_null(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- int copied_words = 0;
- u32 empty = 0;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- spin_lock(&ml403_ac97cr->reg_lock);
- while ((empty = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
- CR_RECEMPTY)) != CR_RECEMPTY) {
- volatile u32 trash;
-
- trash = CR_RECDATA(in_be32(CR_REG(ml403_ac97cr, RECFIFO)));
- /* Hmmmm, really necessary? Don't want call to in_be32()
- * to be optimised away!
- */
- trash++;
- copied_words++;
- }
- rec->hw_ready = 0;
- spin_unlock(&ml403_ac97cr->reg_lock);
-
- return (size_t) (copied_words * 2);
-}
-
-static size_t
-snd_ml403_ac97cr_capture_ind2_copy(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec, size_t bytes)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- u16 *dst;
- int copied_words = 0;
- u32 empty = 0;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
- dst = (u16 *)(substream->runtime->dma_area + rec->sw_data);
-
- spin_lock(&ml403_ac97cr->reg_lock);
- while (((empty = (in_be32(CR_REG(ml403_ac97cr, STATUS)) &
- CR_RECEMPTY)) != CR_RECEMPTY) && (bytes > 1)) {
- dst[copied_words] = CR_RECDATA(in_be32(CR_REG(ml403_ac97cr,
- RECFIFO)));
- copied_words++;
- bytes = bytes - 2;
- }
- if (empty != CR_RECEMPTY)
- rec->hw_ready = 1;
- else
- rec->hw_ready = 0;
- spin_unlock(&ml403_ac97cr->reg_lock);
-
- return (size_t) (copied_words * 2);
-}
-
-static snd_pcm_uframes_t
-snd_ml403_ac97cr_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- struct snd_pcm_indirect2 *ind2_rec = NULL;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- if (substream == ml403_ac97cr->playback_substream)
- ind2_rec = &ml403_ac97cr->ind_rec;
- if (substream == ml403_ac97cr->capture_substream)
- ind2_rec = &ml403_ac97cr->capture_ind2_rec;
-
- if (ind2_rec != NULL)
- return snd_pcm_indirect2_pointer(substream, ind2_rec);
- return (snd_pcm_uframes_t) 0;
-}
-
-static int
-snd_ml403_ac97cr_pcm_playback_trigger(struct snd_pcm_substream *substream,
- int cmd)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- int err = 0;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- PDEBUG(WORK_INFO, "trigger(playback): START\n");
- ml403_ac97cr->ind_rec.hw_ready = 1;
-
- /* clear play FIFO */
- out_be32(CR_REG(ml403_ac97cr, RESETFIFO), CR_PLAYRESET);
-
- /* enable play irq */
- ml403_ac97cr->enable_irq = 1;
- enable_irq(ml403_ac97cr->irq);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- PDEBUG(WORK_INFO, "trigger(playback): STOP\n");
- ml403_ac97cr->ind_rec.hw_ready = 0;
-#ifdef SND_PCM_INDIRECT2_STAT
- snd_pcm_indirect2_stat(substream, &ml403_ac97cr->ind_rec);
-#endif
- /* disable play irq */
- disable_irq_nosync(ml403_ac97cr->irq);
- ml403_ac97cr->enable_irq = 0;
- break;
- default:
- err = -EINVAL;
- break;
- }
- PDEBUG(WORK_INFO, "trigger(playback): (done)\n");
- return err;
-}
-
-static int
-snd_ml403_ac97cr_pcm_capture_trigger(struct snd_pcm_substream *substream,
- int cmd)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- int err = 0;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- PDEBUG(WORK_INFO, "trigger(capture): START\n");
- ml403_ac97cr->capture_ind2_rec.hw_ready = 0;
-
- /* clear record FIFO */
- out_be32(CR_REG(ml403_ac97cr, RESETFIFO), CR_RECRESET);
-
- /* enable record irq */
- ml403_ac97cr->enable_capture_irq = 1;
- enable_irq(ml403_ac97cr->capture_irq);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- PDEBUG(WORK_INFO, "trigger(capture): STOP\n");
- ml403_ac97cr->capture_ind2_rec.hw_ready = 0;
-#ifdef SND_PCM_INDIRECT2_STAT
- snd_pcm_indirect2_stat(substream,
- &ml403_ac97cr->capture_ind2_rec);
-#endif
- /* disable capture irq */
- disable_irq_nosync(ml403_ac97cr->capture_irq);
- ml403_ac97cr->enable_capture_irq = 0;
- break;
- default:
- err = -EINVAL;
- break;
- }
- PDEBUG(WORK_INFO, "trigger(capture): (done)\n");
- return err;
-}
-
-static int
-snd_ml403_ac97cr_pcm_playback_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- struct snd_pcm_runtime *runtime;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
- runtime = substream->runtime;
-
- PDEBUG(WORK_INFO,
- "prepare(): period_bytes=%d, minperiod_bytes=%d\n",
- snd_pcm_lib_period_bytes(substream), CR_FIFO_SIZE / 2);
-
- /* set sampling rate */
- snd_ac97_set_rate(ml403_ac97cr->ac97, AC97_PCM_FRONT_DAC_RATE,
- runtime->rate);
- PDEBUG(WORK_INFO, "prepare(): rate=%d\n", runtime->rate);
-
- /* init struct for intermediate buffer */
- memset(&ml403_ac97cr->ind_rec, 0,
- sizeof(struct snd_pcm_indirect2));
- ml403_ac97cr->ind_rec.hw_buffer_size = CR_FIFO_SIZE;
- ml403_ac97cr->ind_rec.sw_buffer_size =
- snd_pcm_lib_buffer_bytes(substream);
- ml403_ac97cr->ind_rec.min_periods = -1;
- ml403_ac97cr->ind_rec.min_multiple =
- snd_pcm_lib_period_bytes(substream) / (CR_FIFO_SIZE / 2);
- PDEBUG(WORK_INFO, "prepare(): hw_buffer_size=%d, "
- "sw_buffer_size=%d, min_multiple=%d\n",
- CR_FIFO_SIZE, ml403_ac97cr->ind_rec.sw_buffer_size,
- ml403_ac97cr->ind_rec.min_multiple);
- return 0;
-}
-
-static int
-snd_ml403_ac97cr_pcm_capture_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- struct snd_pcm_runtime *runtime;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
- runtime = substream->runtime;
-
- PDEBUG(WORK_INFO,
- "prepare(capture): period_bytes=%d, minperiod_bytes=%d\n",
- snd_pcm_lib_period_bytes(substream), CR_FIFO_SIZE / 2);
-
- /* set sampling rate */
- snd_ac97_set_rate(ml403_ac97cr->ac97, AC97_PCM_LR_ADC_RATE,
- runtime->rate);
- PDEBUG(WORK_INFO, "prepare(capture): rate=%d\n", runtime->rate);
-
- /* init struct for intermediate buffer */
- memset(&ml403_ac97cr->capture_ind2_rec, 0,
- sizeof(struct snd_pcm_indirect2));
- ml403_ac97cr->capture_ind2_rec.hw_buffer_size = CR_FIFO_SIZE;
- ml403_ac97cr->capture_ind2_rec.sw_buffer_size =
- snd_pcm_lib_buffer_bytes(substream);
- ml403_ac97cr->capture_ind2_rec.min_multiple =
- snd_pcm_lib_period_bytes(substream) / (CR_FIFO_SIZE / 2);
- PDEBUG(WORK_INFO, "prepare(capture): hw_buffer_size=%d, "
- "sw_buffer_size=%d, min_multiple=%d\n", CR_FIFO_SIZE,
- ml403_ac97cr->capture_ind2_rec.sw_buffer_size,
- ml403_ac97cr->capture_ind2_rec.min_multiple);
- return 0;
-}
-
-static int snd_ml403_ac97cr_hw_free(struct snd_pcm_substream *substream)
-{
- PDEBUG(WORK_INFO, "hw_free()\n");
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int
-snd_ml403_ac97cr_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- PDEBUG(WORK_INFO, "hw_params(): desired buffer bytes=%d, desired "
- "period bytes=%d\n",
- params_buffer_bytes(hw_params), params_period_bytes(hw_params));
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-static int snd_ml403_ac97cr_playback_open(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- struct snd_pcm_runtime *runtime;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
- runtime = substream->runtime;
-
- PDEBUG(WORK_INFO, "open(playback)\n");
- ml403_ac97cr->playback_substream = substream;
- runtime->hw = snd_ml403_ac97cr_playback;
-
- snd_pcm_hw_constraint_step(runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
- CR_FIFO_SIZE / 2);
- return 0;
-}
-
-static int snd_ml403_ac97cr_capture_open(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- struct snd_pcm_runtime *runtime;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
- runtime = substream->runtime;
-
- PDEBUG(WORK_INFO, "open(capture)\n");
- ml403_ac97cr->capture_substream = substream;
- runtime->hw = snd_ml403_ac97cr_capture;
-
- snd_pcm_hw_constraint_step(runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
- CR_FIFO_SIZE / 2);
- return 0;
-}
-
-static int snd_ml403_ac97cr_playback_close(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- PDEBUG(WORK_INFO, "close(playback)\n");
- ml403_ac97cr->playback_substream = NULL;
- return 0;
-}
-
-static int snd_ml403_ac97cr_capture_close(struct snd_pcm_substream *substream)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
-
- ml403_ac97cr = snd_pcm_substream_chip(substream);
-
- PDEBUG(WORK_INFO, "close(capture)\n");
- ml403_ac97cr->capture_substream = NULL;
- return 0;
-}
-
-static struct snd_pcm_ops snd_ml403_ac97cr_playback_ops = {
- .open = snd_ml403_ac97cr_playback_open,
- .close = snd_ml403_ac97cr_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ml403_ac97cr_hw_params,
- .hw_free = snd_ml403_ac97cr_hw_free,
- .prepare = snd_ml403_ac97cr_pcm_playback_prepare,
- .trigger = snd_ml403_ac97cr_pcm_playback_trigger,
- .pointer = snd_ml403_ac97cr_pcm_pointer,
-};
-
-static struct snd_pcm_ops snd_ml403_ac97cr_capture_ops = {
- .open = snd_ml403_ac97cr_capture_open,
- .close = snd_ml403_ac97cr_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ml403_ac97cr_hw_params,
- .hw_free = snd_ml403_ac97cr_hw_free,
- .prepare = snd_ml403_ac97cr_pcm_capture_prepare,
- .trigger = snd_ml403_ac97cr_pcm_capture_trigger,
- .pointer = snd_ml403_ac97cr_pcm_pointer,
-};
-
-static irqreturn_t snd_ml403_ac97cr_irq(int irq, void *dev_id)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- struct platform_device *pfdev;
- int cmp_irq;
-
- ml403_ac97cr = (struct snd_ml403_ac97cr *)dev_id;
- if (ml403_ac97cr == NULL)
- return IRQ_NONE;
-
- pfdev = ml403_ac97cr->pfdev;
-
- /* playback interrupt */
- cmp_irq = platform_get_irq(pfdev, 0);
- if (irq == cmp_irq) {
- if (ml403_ac97cr->enable_irq)
- snd_pcm_indirect2_playback_interrupt(
- ml403_ac97cr->playback_substream,
- &ml403_ac97cr->ind_rec,
- snd_ml403_ac97cr_playback_ind2_copy,
- snd_ml403_ac97cr_playback_ind2_zero);
- else
- goto __disable_irq;
- } else {
- /* record interrupt */
- cmp_irq = platform_get_irq(pfdev, 1);
- if (irq == cmp_irq) {
- if (ml403_ac97cr->enable_capture_irq)
- snd_pcm_indirect2_capture_interrupt(
- ml403_ac97cr->capture_substream,
- &ml403_ac97cr->capture_ind2_rec,
- snd_ml403_ac97cr_capture_ind2_copy,
- snd_ml403_ac97cr_capture_ind2_null);
- else
- goto __disable_irq;
- } else
- return IRQ_NONE;
- }
- return IRQ_HANDLED;
-
-__disable_irq:
- PDEBUG(INIT_INFO, "irq(): irq %d is meant to be disabled! So, now try "
- "to disable it _really_!\n", irq);
- disable_irq_nosync(irq);
- return IRQ_HANDLED;
-}
-
-static unsigned short
-snd_ml403_ac97cr_codec_read(struct snd_ac97 *ac97, unsigned short reg)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data;
-#ifdef CODEC_STAT
- u32 stat;
- u32 rafaccess = 0;
-#endif
- unsigned long end_time;
- u16 value = 0;
-
- if (!LM4550_RF_OK(reg)) {
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "access to unknown/unused codec register 0x%x "
- "ignored!\n", reg);
- return 0;
- }
- /* check if we can fake/answer this access from our shadow register */
- if ((lm4550_regfile[reg / 2].flag &
- (LM4550_REG_DONEREAD | LM4550_REG_ALLFAKE)) &&
- !(lm4550_regfile[reg / 2].flag & LM4550_REG_NOSHADOW)) {
- if (lm4550_regfile[reg / 2].flag & LM4550_REG_FAKEREAD) {
- PDEBUG(CODEC_FAKE, "codec_read(): faking read from "
- "reg=0x%x, val=0x%x / %d\n",
- reg, lm4550_regfile[reg / 2].def,
- lm4550_regfile[reg / 2].def);
- return lm4550_regfile[reg / 2].def;
- } else if ((lm4550_regfile[reg / 2].flag &
- LM4550_REG_FAKEPROBE) &&
- ml403_ac97cr->ac97_fake) {
- PDEBUG(CODEC_FAKE, "codec_read(): faking read from "
- "reg=0x%x, val=0x%x / %d (probe)\n",
- reg, lm4550_regfile[reg / 2].value,
- lm4550_regfile[reg / 2].value);
- return lm4550_regfile[reg / 2].value;
- } else {
-#ifdef CODEC_STAT
- PDEBUG(CODEC_FAKE, "codec_read(): read access "
- "answered by shadow register 0x%x (value=0x%x "
- "/ %d) (cw=%d cr=%d)\n",
- reg, lm4550_regfile[reg / 2].value,
- lm4550_regfile[reg / 2].value,
- ml403_ac97cr->ac97_write,
- ml403_ac97cr->ac97_read);
-#else
- PDEBUG(CODEC_FAKE, "codec_read(): read access "
- "answered by shadow register 0x%x (value=0x%x "
- "/ %d)\n",
- reg, lm4550_regfile[reg / 2].value,
- lm4550_regfile[reg / 2].value);
-#endif
- return lm4550_regfile[reg / 2].value;
- }
- }
- /* if we are here, we _have_ to access the codec really, no faking */
- if (mutex_lock_interruptible(&ml403_ac97cr->cdc_mutex) != 0)
- return 0;
-#ifdef CODEC_STAT
- ml403_ac97cr->ac97_read++;
-#endif
- spin_lock(&ml403_ac97cr->reg_lock);
- out_be32(CR_REG(ml403_ac97cr, CODEC_ADDR),
- CR_CODEC_ADDR(reg) | CR_CODEC_READ);
- spin_unlock(&ml403_ac97cr->reg_lock);
- end_time = jiffies + (HZ / CODEC_TIMEOUT_AFTER_READ);
- do {
- spin_lock(&ml403_ac97cr->reg_lock);
-#ifdef CODEC_STAT
- rafaccess++;
- stat = in_be32(CR_REG(ml403_ac97cr, STATUS));
- if ((stat & CR_RAF) == CR_RAF) {
- value = CR_CODEC_DATAREAD(
- in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD)));
- PDEBUG(CODEC_SUCCESS, "codec_read(): (done) reg=0x%x, "
- "value=0x%x / %d (STATUS=0x%x)\n",
- reg, value, value, stat);
-#else
- if ((in_be32(CR_REG(ml403_ac97cr, STATUS)) &
- CR_RAF) == CR_RAF) {
- value = CR_CODEC_DATAREAD(
- in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD)));
- PDEBUG(CODEC_SUCCESS, "codec_read(): (done) "
- "reg=0x%x, value=0x%x / %d\n",
- reg, value, value);
-#endif
- lm4550_regfile[reg / 2].value = value;
- lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD;
- spin_unlock(&ml403_ac97cr->reg_lock);
- mutex_unlock(&ml403_ac97cr->cdc_mutex);
- return value;
- }
- spin_unlock(&ml403_ac97cr->reg_lock);
- schedule_timeout_uninterruptible(1);
- } while (time_after(end_time, jiffies));
- /* read the DATAREAD register anyway, see comment below */
- spin_lock(&ml403_ac97cr->reg_lock);
- value =
- CR_CODEC_DATAREAD(in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD)));
- spin_unlock(&ml403_ac97cr->reg_lock);
-#ifdef CODEC_STAT
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "timeout while codec read! "
- "(reg=0x%x, last STATUS=0x%x, DATAREAD=0x%x / %d, %d) "
- "(cw=%d, cr=%d)\n",
- reg, stat, value, value, rafaccess,
- ml403_ac97cr->ac97_write, ml403_ac97cr->ac97_read);
-#else
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "timeout while codec read! "
- "(reg=0x%x, DATAREAD=0x%x / %d)\n",
- reg, value, value);
-#endif
- /* BUG: This is PURE speculation! But after _most_ read timeouts the
- * value in the register is ok!
- */
- lm4550_regfile[reg / 2].value = value;
- lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD;
- mutex_unlock(&ml403_ac97cr->cdc_mutex);
- return value;
-}
-
-static void
-snd_ml403_ac97cr_codec_write(struct snd_ac97 *ac97, unsigned short reg,
- unsigned short val)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data;
-
-#ifdef CODEC_STAT
- u32 stat;
- u32 rafaccess = 0;
-#endif
-#ifdef CODEC_WRITE_CHECK_RAF
- unsigned long end_time;
-#endif
-
- if (!LM4550_RF_OK(reg)) {
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "access to unknown/unused codec register 0x%x "
- "ignored!\n", reg);
- return;
- }
- if (lm4550_regfile[reg / 2].flag & LM4550_REG_READONLY) {
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "write access to read only codec register 0x%x "
- "ignored!\n", reg);
- return;
- }
- if ((val & lm4550_regfile[reg / 2].wmask) != val) {
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "write access to codec register 0x%x "
- "with bad value 0x%x / %d!\n",
- reg, val, val);
- val = val & lm4550_regfile[reg / 2].wmask;
- }
- if (((lm4550_regfile[reg / 2].flag & LM4550_REG_FAKEPROBE) &&
- ml403_ac97cr->ac97_fake) &&
- !(lm4550_regfile[reg / 2].flag & LM4550_REG_NOSHADOW)) {
- PDEBUG(CODEC_FAKE, "codec_write(): faking write to reg=0x%x, "
- "val=0x%x / %d\n", reg, val, val);
- lm4550_regfile[reg / 2].value = (val &
- lm4550_regfile[reg / 2].wmask);
- return;
- }
- if (mutex_lock_interruptible(&ml403_ac97cr->cdc_mutex) != 0)
- return;
-#ifdef CODEC_STAT
- ml403_ac97cr->ac97_write++;
-#endif
- spin_lock(&ml403_ac97cr->reg_lock);
- out_be32(CR_REG(ml403_ac97cr, CODEC_DATAWRITE),
- CR_CODEC_DATAWRITE(val));
- out_be32(CR_REG(ml403_ac97cr, CODEC_ADDR),
- CR_CODEC_ADDR(reg) | CR_CODEC_WRITE);
- spin_unlock(&ml403_ac97cr->reg_lock);
-#ifdef CODEC_WRITE_CHECK_RAF
- /* check CR_CODEC_RAF bit to see if write access to register is done;
- * loop until bit is set or timeout happens
- */
- end_time = jiffies + HZ / CODEC_TIMEOUT_AFTER_WRITE;
- do {
- spin_lock(&ml403_ac97cr->reg_lock);
-#ifdef CODEC_STAT
- rafaccess++;
- stat = in_be32(CR_REG(ml403_ac97cr, STATUS))
- if ((stat & CR_RAF) == CR_RAF) {
-#else
- if ((in_be32(CR_REG(ml403_ac97cr, STATUS)) &
- CR_RAF) == CR_RAF) {
-#endif
- PDEBUG(CODEC_SUCCESS, "codec_write(): (done) "
- "reg=0x%x, value=%d / 0x%x\n",
- reg, val, val);
- if (!(lm4550_regfile[reg / 2].flag &
- LM4550_REG_NOSHADOW) &&
- !(lm4550_regfile[reg / 2].flag &
- LM4550_REG_NOSAVE))
- lm4550_regfile[reg / 2].value = val;
- lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD;
- spin_unlock(&ml403_ac97cr->reg_lock);
- mutex_unlock(&ml403_ac97cr->cdc_mutex);
- return;
- }
- spin_unlock(&ml403_ac97cr->reg_lock);
- schedule_timeout_uninterruptible(1);
- } while (time_after(end_time, jiffies));
-#ifdef CODEC_STAT
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "timeout while codec write "
- "(reg=0x%x, val=0x%x / %d, last STATUS=0x%x, %d) "
- "(cw=%d, cr=%d)\n",
- reg, val, val, stat, rafaccess, ml403_ac97cr->ac97_write,
- ml403_ac97cr->ac97_read);
-#else
- snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": "
- "timeout while codec write (reg=0x%x, val=0x%x / %d)\n",
- reg, val, val);
-#endif
-#else /* CODEC_WRITE_CHECK_RAF */
-#if CODEC_WAIT_AFTER_WRITE > 0
- /* officially, in AC97 spec there is no possibility for a AC97
- * controller to determine, if write access is done or not - so: How
- * is Xilinx able to provide a RAF bit for write access?
- * => very strange, thus just don't check RAF bit (compare with
- * Xilinx's example app in EDK 8.1i) and wait
- */
- schedule_timeout_uninterruptible(HZ / CODEC_WAIT_AFTER_WRITE);
-#endif
- PDEBUG(CODEC_SUCCESS, "codec_write(): (done) "
- "reg=0x%x, value=%d / 0x%x (no RAF check)\n",
- reg, val, val);
-#endif
- mutex_unlock(&ml403_ac97cr->cdc_mutex);
- return;
-}
-
-static int __devinit
-snd_ml403_ac97cr_chip_init(struct snd_ml403_ac97cr *ml403_ac97cr)
-{
- unsigned long end_time;
- PDEBUG(INIT_INFO, "chip_init():\n");
- end_time = jiffies + HZ / CODEC_TIMEOUT_ON_INIT;
- do {
- if (in_be32(CR_REG(ml403_ac97cr, STATUS)) & CR_CODECREADY) {
- /* clear both hardware FIFOs */
- out_be32(CR_REG(ml403_ac97cr, RESETFIFO),
- CR_RECRESET | CR_PLAYRESET);
- PDEBUG(INIT_INFO, "chip_init(): (done)\n");
- return 0;
- }
- schedule_timeout_uninterruptible(1);
- } while (time_after(end_time, jiffies));
- snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
- "timeout while waiting for codec, "
- "not ready!\n");
- return -EBUSY;
-}
-
-static int snd_ml403_ac97cr_free(struct snd_ml403_ac97cr *ml403_ac97cr)
-{
- PDEBUG(INIT_INFO, "free():\n");
- /* irq release */
- if (ml403_ac97cr->irq >= 0)
- free_irq(ml403_ac97cr->irq, ml403_ac97cr);
- if (ml403_ac97cr->capture_irq >= 0)
- free_irq(ml403_ac97cr->capture_irq, ml403_ac97cr);
- /* give back "port" */
- if (ml403_ac97cr->port != NULL)
- iounmap(ml403_ac97cr->port);
- kfree(ml403_ac97cr);
- PDEBUG(INIT_INFO, "free(): (done)\n");
- return 0;
-}
-
-static int snd_ml403_ac97cr_dev_free(struct snd_device *snddev)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr = snddev->device_data;
- PDEBUG(INIT_INFO, "dev_free():\n");
- return snd_ml403_ac97cr_free(ml403_ac97cr);
-}
-
-static int __devinit
-snd_ml403_ac97cr_create(struct snd_card *card, struct platform_device *pfdev,
- struct snd_ml403_ac97cr **rml403_ac97cr)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr;
- int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_ml403_ac97cr_dev_free,
- };
- struct resource *resource;
- int irq;
-
- *rml403_ac97cr = NULL;
- ml403_ac97cr = kzalloc(sizeof(*ml403_ac97cr), GFP_KERNEL);
- if (ml403_ac97cr == NULL)
- return -ENOMEM;
- spin_lock_init(&ml403_ac97cr->reg_lock);
- mutex_init(&ml403_ac97cr->cdc_mutex);
- ml403_ac97cr->card = card;
- ml403_ac97cr->pfdev = pfdev;
- ml403_ac97cr->irq = -1;
- ml403_ac97cr->enable_irq = 0;
- ml403_ac97cr->capture_irq = -1;
- ml403_ac97cr->enable_capture_irq = 0;
- ml403_ac97cr->port = NULL;
- ml403_ac97cr->res_port = NULL;
-
- PDEBUG(INIT_INFO, "Trying to reserve resources now ...\n");
- resource = platform_get_resource(pfdev, IORESOURCE_MEM, 0);
- /* get "port" */
- ml403_ac97cr->port = ioremap_nocache(resource->start,
- (resource->end) -
- (resource->start) + 1);
- if (ml403_ac97cr->port == NULL) {
- snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
- "unable to remap memory region (%x to %x)\n",
- resource->start, resource->end);
- snd_ml403_ac97cr_free(ml403_ac97cr);
- return -EBUSY;
- }
- snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": "
- "remap controller memory region to "
- "0x%x done\n", (unsigned int)ml403_ac97cr->port);
- /* get irq */
- irq = platform_get_irq(pfdev, 0);
- if (request_irq(irq, snd_ml403_ac97cr_irq, IRQF_DISABLED,
- dev_name(&pfdev->dev), (void *)ml403_ac97cr)) {
- snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
- "unable to grab IRQ %d\n",
- irq);
- snd_ml403_ac97cr_free(ml403_ac97cr);
- return -EBUSY;
- }
- ml403_ac97cr->irq = irq;
- snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": "
- "request (playback) irq %d done\n",
- ml403_ac97cr->irq);
- irq = platform_get_irq(pfdev, 1);
- if (request_irq(irq, snd_ml403_ac97cr_irq, IRQF_DISABLED,
- dev_name(&pfdev->dev), (void *)ml403_ac97cr)) {
- snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
- "unable to grab IRQ %d\n",
- irq);
- snd_ml403_ac97cr_free(ml403_ac97cr);
- return -EBUSY;
- }
- ml403_ac97cr->capture_irq = irq;
- snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": "
- "request (capture) irq %d done\n",
- ml403_ac97cr->capture_irq);
-
- err = snd_ml403_ac97cr_chip_init(ml403_ac97cr);
- if (err < 0) {
- snd_ml403_ac97cr_free(ml403_ac97cr);
- return err;
- }
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ml403_ac97cr, &ops);
- if (err < 0) {
- PDEBUG(INIT_FAILURE, "probe(): snd_device_new() failed!\n");
- snd_ml403_ac97cr_free(ml403_ac97cr);
- return err;
- }
-
- *rml403_ac97cr = ml403_ac97cr;
- return 0;
-}
-
-static void snd_ml403_ac97cr_mixer_free(struct snd_ac97 *ac97)
-{
- struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data;
- PDEBUG(INIT_INFO, "mixer_free():\n");
- ml403_ac97cr->ac97 = NULL;
- PDEBUG(INIT_INFO, "mixer_free(): (done)\n");
-}
-
-static int __devinit
-snd_ml403_ac97cr_mixer(struct snd_ml403_ac97cr *ml403_ac97cr)
-{
- struct snd_ac97_bus *bus;
- struct snd_ac97_template ac97;
- int err;
- static struct snd_ac97_bus_ops ops = {
- .write = snd_ml403_ac97cr_codec_write,
- .read = snd_ml403_ac97cr_codec_read,
- };
- PDEBUG(INIT_INFO, "mixer():\n");
- err = snd_ac97_bus(ml403_ac97cr->card, 0, &ops, NULL, &bus);
- if (err < 0)
- return err;
-
- memset(&ac97, 0, sizeof(ac97));
- ml403_ac97cr->ac97_fake = 1;
- lm4550_regfile_init();
-#ifdef CODEC_STAT
- ml403_ac97cr->ac97_read = 0;
- ml403_ac97cr->ac97_write = 0;
-#endif
- ac97.private_data = ml403_ac97cr;
- ac97.private_free = snd_ml403_ac97cr_mixer_free;
- ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM |
- AC97_SCAP_NO_SPDIF;
- err = snd_ac97_mixer(bus, &ac97, &ml403_ac97cr->ac97);
- ml403_ac97cr->ac97_fake = 0;
- lm4550_regfile_write_values_after_init(ml403_ac97cr->ac97);
- PDEBUG(INIT_INFO, "mixer(): (done) snd_ac97_mixer()=%d\n", err);
- return err;
-}
-
-static int __devinit
-snd_ml403_ac97cr_pcm(struct snd_ml403_ac97cr *ml403_ac97cr, int device,
- struct snd_pcm **rpcm)
-{
- struct snd_pcm *pcm;
- int err;
-
- if (rpcm)
- *rpcm = NULL;
- err = snd_pcm_new(ml403_ac97cr->card, "ML403AC97CR/1", device, 1, 1,
- &pcm);
- if (err < 0)
- return err;
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- &snd_ml403_ac97cr_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &snd_ml403_ac97cr_capture_ops);
- pcm->private_data = ml403_ac97cr;
- pcm->info_flags = 0;
- strcpy(pcm->name, "ML403AC97CR DAC/ADC");
- ml403_ac97cr->pcm = pcm;
-
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
- 64 * 1024,
- 128 * 1024);
- if (rpcm)
- *rpcm = pcm;
- return 0;
-}
-
-static int __devinit snd_ml403_ac97cr_probe(struct platform_device *pfdev)
-{
- struct snd_card *card;
- struct snd_ml403_ac97cr *ml403_ac97cr = NULL;
- int err;
- int dev = pfdev->id;
-
- if (dev >= SNDRV_CARDS)
- return -ENODEV;
- if (!enable[dev])
- return -ENOENT;
-
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
- if (err < 0)
- return err;
- err = snd_ml403_ac97cr_create(card, pfdev, &ml403_ac97cr);
- if (err < 0) {
- PDEBUG(INIT_FAILURE, "probe(): create failed!\n");
- snd_card_free(card);
- return err;
- }
- PDEBUG(INIT_INFO, "probe(): create done\n");
- card->private_data = ml403_ac97cr;
- err = snd_ml403_ac97cr_mixer(ml403_ac97cr);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
- PDEBUG(INIT_INFO, "probe(): mixer done\n");
- err = snd_ml403_ac97cr_pcm(ml403_ac97cr, 0, NULL);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
- PDEBUG(INIT_INFO, "probe(): PCM done\n");
- strcpy(card->driver, SND_ML403_AC97CR_DRIVER);
- strcpy(card->shortname, "ML403 AC97 Controller Reference");
- sprintf(card->longname, "%s %s at 0x%lx, irq %i & %i, device %i",
- card->shortname, card->driver,
- (unsigned long)ml403_ac97cr->port, ml403_ac97cr->irq,
- ml403_ac97cr->capture_irq, dev + 1);
-
- snd_card_set_dev(card, &pfdev->dev);
-
- err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
- platform_set_drvdata(pfdev, card);
- PDEBUG(INIT_INFO, "probe(): (done)\n");
- return 0;
-}
-
-static int snd_ml403_ac97cr_remove(struct platform_device *pfdev)
-{
- snd_card_free(platform_get_drvdata(pfdev));
- platform_set_drvdata(pfdev, NULL);
- return 0;
-}
-
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:" SND_ML403_AC97CR_DRIVER);
-
-static struct platform_driver snd_ml403_ac97cr_driver = {
- .probe = snd_ml403_ac97cr_probe,
- .remove = snd_ml403_ac97cr_remove,
- .driver = {
- .name = SND_ML403_AC97CR_DRIVER,
- .owner = THIS_MODULE,
- },
-};
-
-static int __init alsa_card_ml403_ac97cr_init(void)
-{
- return platform_driver_register(&snd_ml403_ac97cr_driver);
-}
-
-static void __exit alsa_card_ml403_ac97cr_exit(void)
-{
- platform_driver_unregister(&snd_ml403_ac97cr_driver);
-}
-
-module_init(alsa_card_ml403_ac97cr_init)
-module_exit(alsa_card_ml403_ac97cr_exit)
diff --git a/sound/drivers/mpu401/Makefile b/sound/drivers/mpu401/Makefile
index 918f83f34c11..0a96e238ee92 100644
--- a/sound/drivers/mpu401/Makefile
+++ b/sound/drivers/mpu401/Makefile
@@ -1,10 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-mpu401-objs := mpu401.o
-snd-mpu401-uart-objs := mpu401_uart.o
+snd-mpu401-y := mpu401.o
+snd-mpu401-uart-y := mpu401_uart.o
obj-$(CONFIG_SND_MPU401_UART) += snd-mpu401-uart.o
diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c
index 149d05a8202d..d3f9424088d4 100644
--- a/sound/drivers/mpu401/mpu401.c
+++ b/sound/drivers/mpu401/mpu401.c
@@ -1,30 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for generic MPU-401 boards (UART mode only)
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Copyright (c) 2004 by Castet Matthieu <castet.matthieu@free.fr>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/pnp.h>
#include <linux/err.h>
#include <linux/platform_device.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/mpu401.h>
#include <sound/initval.h>
@@ -35,13 +20,13 @@ MODULE_LICENSE("GPL");
static int index[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -2}; /* exclude the first card */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
#ifdef CONFIG_PNP
-static int pnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool pnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
#endif
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* MPU-401 port number */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* MPU-401 IRQ */
-static int uart_enter[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool uart_enter[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for MPU-401 device.");
@@ -53,9 +38,9 @@ MODULE_PARM_DESC(enable, "Enable MPU-401 device.");
module_param_array(pnp, bool, NULL, 0444);
MODULE_PARM_DESC(pnp, "PnP detection for MPU-401 device.");
#endif
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for MPU-401 device.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for MPU-401 device.");
module_param_array(uart_enter, bool, NULL, 0444);
MODULE_PARM_DESC(uart_enter, "Issue UART_ENTER command at open.");
@@ -64,20 +49,22 @@ static struct platform_device *platform_devices[SNDRV_CARDS];
static int pnp_registered;
static unsigned int snd_mpu401_devices;
-static int snd_mpu401_create(int dev, struct snd_card **rcard)
+static int snd_mpu401_create(struct device *devptr, int dev,
+ struct snd_card **rcard)
{
struct snd_card *card;
int err;
if (!uart_enter[dev])
- snd_printk(KERN_ERR "the uart_enter option is obsolete; remove it\n");
+ dev_err(devptr, "the uart_enter option is obsolete; remove it\n");
*rcard = NULL;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(devptr, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
if (err < 0)
return err;
- strcpy(card->driver, "MPU-401 UART");
- strcpy(card->shortname, card->driver);
+ strscpy(card->driver, "MPU-401 UART");
+ strscpy(card->shortname, card->driver);
sprintf(card->longname, "%s at %#lx, ", card->shortname, port[dev]);
if (irq[dev] >= 0) {
sprintf(card->longname + strlen(card->longname), "irq %d", irq[dev]);
@@ -86,61 +73,46 @@ static int snd_mpu401_create(int dev, struct snd_card **rcard)
}
err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, port[dev], 0,
- irq[dev], irq[dev] >= 0 ? IRQF_DISABLED : 0,
- NULL);
+ irq[dev], NULL);
if (err < 0) {
- printk(KERN_ERR "MPU401 not detected at 0x%lx\n", port[dev]);
- goto _err;
+ dev_err(devptr, "MPU401 not detected at 0x%lx\n", port[dev]);
+ return err;
}
*rcard = card;
return 0;
-
- _err:
- snd_card_free(card);
- return err;
}
-static int __devinit snd_mpu401_probe(struct platform_device *devptr)
+static int snd_mpu401_probe(struct platform_device *devptr)
{
int dev = devptr->id;
int err;
struct snd_card *card;
if (port[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR "specify port\n");
+ dev_err(&devptr->dev, "specify port\n");
return -EINVAL;
}
if (irq[dev] == SNDRV_AUTO_IRQ) {
- snd_printk(KERN_ERR "specify or disable IRQ\n");
+ dev_err(&devptr->dev, "specify or disable IRQ\n");
return -EINVAL;
}
- err = snd_mpu401_create(dev, &card);
+ err = snd_mpu401_create(&devptr->dev, dev, &card);
if (err < 0)
return err;
- snd_card_set_dev(card, &devptr->dev);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
platform_set_drvdata(devptr, card);
return 0;
}
-static int __devexit snd_mpu401_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
- return 0;
-}
-
#define SND_MPU401_DRIVER "snd_mpu401"
static struct platform_driver snd_mpu401_driver = {
.probe = snd_mpu401_probe,
- .remove = __devexit_p(snd_mpu401_remove),
.driver = {
- .name = SND_MPU401_DRIVER
+ .name = SND_MPU401_DRIVER,
},
};
@@ -149,23 +121,23 @@ static struct platform_driver snd_mpu401_driver = {
#define IO_EXTENT 2
-static struct pnp_device_id snd_mpu401_pnpids[] = {
+static const struct pnp_device_id snd_mpu401_pnpids[] = {
{ .id = "PNPb006" },
{ .id = "" }
};
MODULE_DEVICE_TABLE(pnp, snd_mpu401_pnpids);
-static int __devinit snd_mpu401_pnp(int dev, struct pnp_dev *device,
- const struct pnp_device_id *id)
+static int snd_mpu401_pnp(int dev, struct pnp_dev *device,
+ const struct pnp_device_id *id)
{
if (!pnp_port_valid(device, 0) ||
pnp_port_flags(device, 0) & IORESOURCE_DISABLED) {
- snd_printk(KERN_ERR "no PnP port\n");
+ dev_err(&device->dev, "no PnP port\n");
return -ENODEV;
}
if (pnp_port_len(device, 0) < IO_EXTENT) {
- snd_printk(KERN_ERR "PnP port length is %llu, expected %d\n",
+ dev_err(&device->dev, "PnP port length is %llu, expected %d\n",
(unsigned long long)pnp_port_len(device, 0),
IO_EXTENT);
return -ENODEV;
@@ -174,7 +146,7 @@ static int __devinit snd_mpu401_pnp(int dev, struct pnp_dev *device,
if (!pnp_irq_valid(device, 0) ||
pnp_irq_flags(device, 0) & IORESOURCE_DISABLED) {
- snd_printk(KERN_WARNING "no PnP irq, using polling\n");
+ dev_warn(&device->dev, "no PnP irq, using polling\n");
irq[dev] = -1;
} else {
irq[dev] = pnp_irq(device, 0);
@@ -182,8 +154,8 @@ static int __devinit snd_mpu401_pnp(int dev, struct pnp_dev *device,
return 0;
}
-static int __devinit snd_mpu401_pnp_probe(struct pnp_dev *pnp_dev,
- const struct pnp_device_id *id)
+static int snd_mpu401_pnp_probe(struct pnp_dev *pnp_dev,
+ const struct pnp_device_id *id)
{
static int dev;
struct snd_card *card;
@@ -195,14 +167,12 @@ static int __devinit snd_mpu401_pnp_probe(struct pnp_dev *pnp_dev,
err = snd_mpu401_pnp(dev, pnp_dev, id);
if (err < 0)
return err;
- err = snd_mpu401_create(dev, &card);
+ err = snd_mpu401_create(&pnp_dev->dev, dev, &card);
if (err < 0)
return err;
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
- snd_card_set_dev(card, &pnp_dev->dev);
pnp_set_drvdata(pnp_dev, card);
snd_mpu401_devices++;
++dev;
@@ -211,19 +181,10 @@ static int __devinit snd_mpu401_pnp_probe(struct pnp_dev *pnp_dev,
return -ENODEV;
}
-static void __devexit snd_mpu401_pnp_remove(struct pnp_dev *dev)
-{
- struct snd_card *card = (struct snd_card *) pnp_get_drvdata(dev);
-
- snd_card_disconnect(card);
- snd_card_free_when_closed(card);
-}
-
static struct pnp_driver snd_mpu401_pnp_driver = {
.name = "mpu401",
.id_table = snd_mpu401_pnpids,
.probe = snd_mpu401_pnp_probe,
- .remove = __devexit_p(snd_mpu401_pnp_remove),
};
#else
static struct pnp_driver snd_mpu401_pnp_driver;
@@ -244,7 +205,8 @@ static int __init alsa_card_mpu401_init(void)
{
int i, err;
- if ((err = platform_driver_register(&snd_mpu401_driver)) < 0)
+ err = platform_driver_register(&snd_mpu401_driver);
+ if (err < 0)
return err;
for (i = 0; i < SNDRV_CARDS; i++) {
@@ -272,7 +234,7 @@ static int __init alsa_card_mpu401_init(void)
if (!snd_mpu401_devices) {
#ifdef MODULE
- printk(KERN_ERR "MPU-401 device not found or device busy\n");
+ pr_err("MPU-401 device not found or device busy\n");
#endif
snd_mpu401_unregister_all();
return -ENODEV;
diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c
index 2af09996a3d0..4af89822bf32 100644
--- a/sound/drivers/mpu401/mpu401_uart.c
+++ b/sound/drivers/mpu401/mpu401_uart.c
@@ -1,38 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of MPU-401 in UART mode
*
* MPU-401 supports UART mode which is not capable generate transmit
- * interrupts thus output is done via polling. Also, if irq < 0, then
+ * interrupts thus output is done via polling. Without interrupt,
* input is done also via polling. Do not expect good performance.
*
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
* 13-03-2003:
* Added support for different kind of hardware I/O. Build in choices
* are port and mmio. For other kind of I/O, set mpu->read and
* mpu->write to your own I/O functions.
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/ioport.h>
+#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <sound/core.h>
@@ -87,34 +73,29 @@ static void snd_mpu401_uart_clear_rx(struct snd_mpu401 *mpu)
mpu->read(mpu, MPU401D(mpu));
#ifdef CONFIG_SND_DEBUG
if (timeout <= 0)
- snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n",
- mpu->read(mpu, MPU401C(mpu)));
+ dev_err(mpu->rmidi->dev,
+ "cmd: clear rx timeout (status = 0x%x)\n",
+ mpu->read(mpu, MPU401C(mpu)));
#endif
}
static void uart_interrupt_tx(struct snd_mpu401 *mpu)
{
- unsigned long flags;
-
if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode) &&
test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) {
- spin_lock_irqsave(&mpu->output_lock, flags);
+ guard(spinlock_irqsave)(&mpu->output_lock);
snd_mpu401_uart_output_write(mpu);
- spin_unlock_irqrestore(&mpu->output_lock, flags);
}
}
static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu)
{
- unsigned long flags;
-
if (mpu->info_flags & MPU401_INFO_INPUT) {
- spin_lock_irqsave(&mpu->input_lock, flags);
+ guard(spinlock_irqsave)(&mpu->input_lock);
if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode))
snd_mpu401_uart_input_read(mpu);
else
snd_mpu401_uart_clear_rx(mpu);
- spin_unlock_irqrestore(&mpu->input_lock, flags);
}
if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))
/* ok. for better Tx performance try do some output
@@ -128,12 +109,14 @@ static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu)
* @dev_id: mpu401 instance
*
* Processes the interrupt for MPU401-UART i/o.
+ *
+ * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise.
*/
irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id)
{
struct snd_mpu401 *mpu = dev_id;
- if (mpu == NULL)
+ if (!mpu)
return IRQ_NONE;
_snd_mpu401_uart_interrupt(mpu);
return IRQ_HANDLED;
@@ -147,12 +130,14 @@ EXPORT_SYMBOL(snd_mpu401_uart_interrupt);
* @dev_id: mpu401 instance
*
* Processes the interrupt for MPU401-UART output.
+ *
+ * Return: %IRQ_HANDLED if the interrupt was handled. %IRQ_NONE otherwise.
*/
irqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id)
{
struct snd_mpu401 *mpu = dev_id;
- if (mpu == NULL)
+ if (!mpu)
return IRQ_NONE;
uart_interrupt_tx(mpu);
return IRQ_HANDLED;
@@ -164,16 +149,14 @@ EXPORT_SYMBOL(snd_mpu401_uart_interrupt_tx);
* timer callback
* reprogram the timer and call the interrupt job
*/
-static void snd_mpu401_uart_timer(unsigned long data)
+static void snd_mpu401_uart_timer(struct timer_list *t)
{
- struct snd_mpu401 *mpu = (struct snd_mpu401 *)data;
- unsigned long flags;
-
- spin_lock_irqsave(&mpu->timer_lock, flags);
- /*mpu->mode |= MPU401_MODE_TIMER;*/
- mpu->timer.expires = 1 + jiffies;
- add_timer(&mpu->timer);
- spin_unlock_irqrestore(&mpu->timer_lock, flags);
+ struct snd_mpu401 *mpu = timer_container_of(mpu, t, timer);
+
+ scoped_guard(spinlock_irqsave, &mpu->timer_lock) {
+ /*mpu->mode |= MPU401_MODE_TIMER;*/
+ mod_timer(&mpu->timer, 1 + jiffies);
+ }
if (mpu->rmidi)
_snd_mpu401_uart_interrupt(mpu);
}
@@ -183,19 +166,13 @@ static void snd_mpu401_uart_timer(unsigned long data)
*/
static void snd_mpu401_uart_add_timer (struct snd_mpu401 *mpu, int input)
{
- unsigned long flags;
-
- spin_lock_irqsave (&mpu->timer_lock, flags);
+ guard(spinlock_irqsave)(&mpu->timer_lock);
if (mpu->timer_invoked == 0) {
- init_timer(&mpu->timer);
- mpu->timer.data = (unsigned long)mpu;
- mpu->timer.function = snd_mpu401_uart_timer;
- mpu->timer.expires = 1 + jiffies;
- add_timer(&mpu->timer);
+ timer_setup(&mpu->timer, snd_mpu401_uart_timer, 0);
+ mod_timer(&mpu->timer, 1 + jiffies);
}
mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER :
MPU401_MODE_OUTPUT_TIMER;
- spin_unlock_irqrestore (&mpu->timer_lock, flags);
}
/*
@@ -203,16 +180,13 @@ static void snd_mpu401_uart_add_timer (struct snd_mpu401 *mpu, int input)
*/
static void snd_mpu401_uart_remove_timer (struct snd_mpu401 *mpu, int input)
{
- unsigned long flags;
-
- spin_lock_irqsave (&mpu->timer_lock, flags);
+ guard(spinlock_irqsave)(&mpu->timer_lock);
if (mpu->timer_invoked) {
mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER :
~MPU401_MODE_OUTPUT_TIMER;
if (! mpu->timer_invoked)
- del_timer(&mpu->timer);
+ timer_delete(&mpu->timer);
}
- spin_unlock_irqrestore (&mpu->timer_lock, flags);
}
/*
@@ -223,10 +197,9 @@ static void snd_mpu401_uart_remove_timer (struct snd_mpu401 *mpu, int input)
static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd,
int ack)
{
- unsigned long flags;
int timeout, ok;
- spin_lock_irqsave(&mpu->input_lock, flags);
+ guard(spinlock_irqsave)(&mpu->input_lock);
if (mpu->hardware != MPU401_HW_TRID4DWAVE) {
mpu->write(mpu, 0x00, MPU401D(mpu));
/*snd_mpu401_uart_clear_rx(mpu);*/
@@ -238,8 +211,9 @@ static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd,
udelay(10);
#ifdef CONFIG_SND_DEBUG
if (!timeout)
- snd_printk(KERN_ERR "cmd: tx timeout (status = 0x%x)\n",
- mpu->read(mpu, MPU401C(mpu)));
+ dev_err(mpu->rmidi->dev,
+ "cmd: tx timeout (status = 0x%x)\n",
+ mpu->read(mpu, MPU401C(mpu)));
#endif
}
mpu->write(mpu, cmd, MPU401C(mpu));
@@ -256,12 +230,12 @@ static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd,
ok = 1;
} else
ok = 1;
- spin_unlock_irqrestore(&mpu->input_lock, flags);
if (!ok) {
- snd_printk(KERN_ERR "cmd: 0x%x failed at 0x%lx "
- "(status = 0x%x, data = 0x%x)\n", cmd, mpu->port,
- mpu->read(mpu, MPU401C(mpu)),
- mpu->read(mpu, MPU401D(mpu)));
+ dev_err(mpu->rmidi->dev,
+ "cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)\n",
+ cmd, mpu->port,
+ mpu->read(mpu, MPU401C(mpu)),
+ mpu->read(mpu, MPU401D(mpu)));
return 1;
}
return 0;
@@ -285,8 +259,11 @@ static int snd_mpu401_uart_input_open(struct snd_rawmidi_substream *substream)
int err;
mpu = substream->rmidi->private_data;
- if (mpu->open_input && (err = mpu->open_input(mpu)) < 0)
- return err;
+ if (mpu->open_input) {
+ err = mpu->open_input(mpu);
+ if (err < 0)
+ return err;
+ }
if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) {
if (snd_mpu401_do_reset(mpu) < 0)
goto error_out;
@@ -307,8 +284,11 @@ static int snd_mpu401_uart_output_open(struct snd_rawmidi_substream *substream)
int err;
mpu = substream->rmidi->private_data;
- if (mpu->open_output && (err = mpu->open_output(mpu)) < 0)
- return err;
+ if (mpu->open_output) {
+ err = mpu->open_output(mpu);
+ if (err < 0)
+ return err;
+ }
if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) {
if (snd_mpu401_do_reset(mpu) < 0)
goto error_out;
@@ -363,7 +343,6 @@ static int snd_mpu401_uart_output_close(struct snd_rawmidi_substream *substream)
static void
snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct snd_mpu401 *mpu;
int max = 64;
@@ -374,16 +353,15 @@ snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
/* first time - flush FIFO */
while (max-- > 0)
mpu->read(mpu, MPU401D(mpu));
- if (mpu->irq < 0)
+ if (mpu->info_flags & MPU401_INFO_USE_TIMER)
snd_mpu401_uart_add_timer(mpu, 1);
}
/* read data in advance */
- spin_lock_irqsave(&mpu->input_lock, flags);
+ guard(spinlock_irqsave)(&mpu->input_lock);
snd_mpu401_uart_input_read(mpu);
- spin_unlock_irqrestore(&mpu->input_lock, flags);
} else {
- if (mpu->irq < 0)
+ if (mpu->info_flags & MPU401_INFO_USE_TIMER)
snd_mpu401_uart_remove_timer(mpu, 1);
clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode);
}
@@ -450,7 +428,6 @@ static void snd_mpu401_uart_output_write(struct snd_mpu401 * mpu)
static void
snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct snd_mpu401 *mpu;
mpu = substream->rmidi->private_data;
@@ -465,9 +442,8 @@ snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
snd_mpu401_uart_add_timer(mpu, 0);
/* output pending data */
- spin_lock_irqsave(&mpu->output_lock, flags);
+ guard(spinlock_irqsave)(&mpu->output_lock);
snd_mpu401_uart_output_write(mpu);
- spin_unlock_irqrestore(&mpu->output_lock, flags);
} else {
if (! (mpu->info_flags & MPU401_INFO_TX_IRQ))
snd_mpu401_uart_remove_timer(mpu, 0);
@@ -479,14 +455,14 @@ snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
*/
-static struct snd_rawmidi_ops snd_mpu401_uart_output =
+static const struct snd_rawmidi_ops snd_mpu401_uart_output =
{
.open = snd_mpu401_uart_output_open,
.close = snd_mpu401_uart_output_close,
.trigger = snd_mpu401_uart_output_trigger,
};
-static struct snd_rawmidi_ops snd_mpu401_uart_input =
+static const struct snd_rawmidi_ops snd_mpu401_uart_input =
{
.open = snd_mpu401_uart_input_open,
.close = snd_mpu401_uart_input_close,
@@ -496,7 +472,7 @@ static struct snd_rawmidi_ops snd_mpu401_uart_input =
static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi)
{
struct snd_mpu401 *mpu = rmidi->private_data;
- if (mpu->irq_flags && mpu->irq >= 0)
+ if (mpu->irq >= 0)
free_irq(mpu->irq, (void *) mpu);
release_and_free_resource(mpu->res);
kfree(mpu);
@@ -509,8 +485,7 @@ static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi)
* @hardware: the hardware type, MPU401_HW_XXXX
* @port: the base address of MPU401 port
* @info_flags: bitflags MPU401_INFO_XXX
- * @irq: the irq number, -1 if no interrupt for mpu
- * @irq_flags: the irq request flags (SA_XXX), 0 if irq was already reserved.
+ * @irq: the ISA irq number, -1 if not to be allocated
* @rrawmidi: the pointer to store the new rawmidi instance
*
* Creates a new MPU-401 instance.
@@ -519,13 +494,13 @@ static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi)
* not the mpu401 instance itself. To access to the mpu401 instance,
* cast from rawmidi->private_data (with struct snd_mpu401 magic-cast).
*
- * Returns zero if successful, or a negative error code.
+ * Return: Zero if successful, or a negative error code.
*/
int snd_mpu401_uart_new(struct snd_card *card, int device,
unsigned short hardware,
unsigned long port,
unsigned int info_flags,
- int irq, int irq_flags,
+ int irq,
struct snd_rawmidi ** rrawmidi)
{
struct snd_mpu401 *mpu;
@@ -539,14 +514,14 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
info_flags |= MPU401_INFO_INPUT | MPU401_INFO_OUTPUT;
in_enable = (info_flags & MPU401_INFO_INPUT) ? 1 : 0;
out_enable = (info_flags & MPU401_INFO_OUTPUT) ? 1 : 0;
- if ((err = snd_rawmidi_new(card, "MPU-401U", device,
- out_enable, in_enable, &rmidi)) < 0)
+ err = snd_rawmidi_new(card, "MPU-401U", device,
+ out_enable, in_enable, &rmidi);
+ if (err < 0)
return err;
mpu = kzalloc(sizeof(*mpu), GFP_KERNEL);
- if (mpu == NULL) {
- snd_printk(KERN_ERR "mpu401_uart: cannot allocate\n");
- snd_device_free(card, rmidi);
- return -ENOMEM;
+ if (!mpu) {
+ err = -ENOMEM;
+ goto free_device;
}
rmidi->private_data = mpu;
rmidi->private_free = snd_mpu401_uart_free;
@@ -554,15 +529,17 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
spin_lock_init(&mpu->output_lock);
spin_lock_init(&mpu->timer_lock);
mpu->hardware = hardware;
+ mpu->irq = -1;
+ mpu->rmidi = rmidi;
if (! (info_flags & MPU401_INFO_INTEGRATED)) {
int res_size = hardware == MPU401_HW_PC98II ? 4 : 2;
mpu->res = request_region(port, res_size, "MPU401 UART");
- if (mpu->res == NULL) {
- snd_printk(KERN_ERR "mpu401_uart: "
- "unable to grab port 0x%lx size %d\n",
- port, res_size);
- snd_device_free(card, rmidi);
- return -EBUSY;
+ if (!mpu->res) {
+ dev_err(rmidi->dev,
+ "mpu401_uart: unable to grab port 0x%lx size %d\n",
+ port, res_size);
+ err = -EBUSY;
+ goto free_device;
}
}
if (info_flags & MPU401_INFO_MMIO) {
@@ -577,18 +554,19 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
mpu->cport = port + 2;
else
mpu->cport = port + 1;
- if (irq >= 0 && irq_flags) {
- if (request_irq(irq, snd_mpu401_uart_interrupt, irq_flags,
+ if (irq >= 0) {
+ if (request_irq(irq, snd_mpu401_uart_interrupt, 0,
"MPU401 UART", (void *) mpu)) {
- snd_printk(KERN_ERR "mpu401_uart: "
- "unable to grab IRQ %d\n", irq);
- snd_device_free(card, rmidi);
- return -EBUSY;
+ dev_err(rmidi->dev,
+ "mpu401_uart: unable to grab IRQ %d\n", irq);
+ err = -EBUSY;
+ goto free_device;
}
}
+ if (irq < 0 && !(info_flags & MPU401_INFO_IRQ_HOOK))
+ info_flags |= MPU401_INFO_USE_TIMER;
mpu->info_flags = info_flags;
mpu->irq = irq;
- mpu->irq_flags = irq_flags;
if (card->shortname[0])
snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI",
card->shortname);
@@ -606,26 +584,12 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
if (out_enable)
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
}
- mpu->rmidi = rmidi;
if (rrawmidi)
*rrawmidi = rmidi;
return 0;
+free_device:
+ snd_device_free(card, rmidi);
+ return err;
}
EXPORT_SYMBOL(snd_mpu401_uart_new);
-
-/*
- * INIT part
- */
-
-static int __init alsa_mpu401_uart_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_mpu401_uart_exit(void)
-{
-}
-
-module_init(alsa_mpu401_uart_init)
-module_exit(alsa_mpu401_uart_exit)
diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c
index da03597fc893..d31eadf4be5f 100644
--- a/sound/drivers/mtpav.c
+++ b/sound/drivers/mtpav.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* MOTU Midi Timepiece ALSA Main routines
* Copyright by Michael T. Mayers (c) Jan 09, 2000
* mail: michael@tweakoz.com
* Thanks to John Galbraith
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- *
* This driver is for the 'Mark Of The Unicorn' (MOTU)
* MidiTimePiece AV multiport MIDI interface
*
@@ -39,7 +25,6 @@
* MIDI Synchronization to Video, ADAT, SMPTE and other Clock sources
* 128 'scene' memories, recallable from MIDI program change
*
- *
* ChangeLog
* Jun 11 2001 Takashi Iwai <tiwai@suse.de>
* - Recoded & debugged
@@ -47,21 +32,21 @@
* - hwports is between 1 and 8, which specifies the number of hardware ports.
* The three global ports, computer, adat and broadcast ports, are created
* always after h/w and remote ports.
- *
*/
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/module.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
+#include <linux/io.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/rawmidi.h>
#include <linux/delay.h>
-
-#include <asm/io.h>
+#include <linux/string.h>
/*
* globals
@@ -69,7 +54,6 @@
MODULE_AUTHOR("Michael T. Mayers");
MODULE_DESCRIPTION("MOTU MidiTimePiece AV multiport MIDI");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{MOTU,MidiTimePiece AV multiport MIDI}}");
// io resources
#define MTPAV_IOBASE 0x378
@@ -86,9 +70,9 @@ module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for MotuMTPAV MIDI.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for MotuMTPAV MIDI.");
-module_param(port, long, 0444);
+module_param_hw(port, long, ioport, 0444);
MODULE_PARM_DESC(port, "Parallel port # for MotuMTPAV MIDI.");
-module_param(irq, int, 0444);
+module_param_hw(irq, int, irq, 0444);
MODULE_PARM_DESC(irq, "Parallel IRQ # for MotuMTPAV MIDI.");
module_param(hwports, int, 0444);
MODULE_PARM_DESC(hwports, "Hardware ports # for MotuMTPAV MIDI.");
@@ -302,10 +286,6 @@ static void snd_mtpav_output_port_write(struct mtpav *mtp_card,
snd_mtpav_send_byte(mtp_card, 0xf5);
snd_mtpav_send_byte(mtp_card, portp->hwport);
- /*
- snd_printk(KERN_DEBUG "new outport: 0x%x\n",
- (unsigned int) portp->hwport);
- */
if (!(outbyte & 0x80) && portp->running_status)
snd_mtpav_send_byte(mtp_card, portp->running_status);
}
@@ -324,11 +304,9 @@ static void snd_mtpav_output_write(struct snd_rawmidi_substream *substream)
{
struct mtpav *mtp_card = substream->rmidi->private_data;
struct mtpav_port *portp = &mtp_card->ports[substream->number];
- unsigned long flags;
- spin_lock_irqsave(&mtp_card->spinlock, flags);
+ guard(spinlock_irqsave)(&mtp_card->spinlock);
snd_mtpav_output_port_write(mtp_card, portp, substream);
- spin_unlock_irqrestore(&mtp_card->spinlock, flags);
}
@@ -354,14 +332,12 @@ static int snd_mtpav_input_open(struct snd_rawmidi_substream *substream)
{
struct mtpav *mtp_card = substream->rmidi->private_data;
struct mtpav_port *portp = &mtp_card->ports[substream->number];
- unsigned long flags;
- spin_lock_irqsave(&mtp_card->spinlock, flags);
+ guard(spinlock_irqsave)(&mtp_card->spinlock);
portp->mode |= MTPAV_MODE_INPUT_OPENED;
portp->input = substream;
if (mtp_card->share_irq++ == 0)
snd_mtpav_mputreg(mtp_card, CREG, (SIGC_INTEN | SIGC_WRITE)); // enable pport interrupts
- spin_unlock_irqrestore(&mtp_card->spinlock, flags);
return 0;
}
@@ -372,14 +348,12 @@ static int snd_mtpav_input_close(struct snd_rawmidi_substream *substream)
{
struct mtpav *mtp_card = substream->rmidi->private_data;
struct mtpav_port *portp = &mtp_card->ports[substream->number];
- unsigned long flags;
- spin_lock_irqsave(&mtp_card->spinlock, flags);
+ guard(spinlock_irqsave)(&mtp_card->spinlock);
portp->mode &= ~MTPAV_MODE_INPUT_OPENED;
portp->input = NULL;
if (--mtp_card->share_irq == 0)
snd_mtpav_mputreg(mtp_card, CREG, 0); // disable pport interrupts
- spin_unlock_irqrestore(&mtp_card->spinlock, flags);
return 0;
}
@@ -390,15 +364,12 @@ static void snd_mtpav_input_trigger(struct snd_rawmidi_substream *substream, int
{
struct mtpav *mtp_card = substream->rmidi->private_data;
struct mtpav_port *portp = &mtp_card->ports[substream->number];
- unsigned long flags;
- spin_lock_irqsave(&mtp_card->spinlock, flags);
+ guard(spinlock_irqsave)(&mtp_card->spinlock);
if (up)
portp->mode |= MTPAV_MODE_INPUT_TRIGGERED;
else
portp->mode &= ~MTPAV_MODE_INPUT_TRIGGERED;
- spin_unlock_irqrestore(&mtp_card->spinlock, flags);
-
}
@@ -406,36 +377,32 @@ static void snd_mtpav_input_trigger(struct snd_rawmidi_substream *substream, int
* timer interrupt for outputs
*/
-static void snd_mtpav_output_timer(unsigned long data)
+static void snd_mtpav_output_timer(struct timer_list *t)
{
- unsigned long flags;
- struct mtpav *chip = (struct mtpav *)data;
+ struct mtpav *chip = timer_container_of(chip, t, timer);
int p;
- spin_lock_irqsave(&chip->spinlock, flags);
+ guard(spinlock_irqsave)(&chip->spinlock);
/* reprogram timer */
- chip->timer.expires = 1 + jiffies;
- add_timer(&chip->timer);
+ mod_timer(&chip->timer, 1 + jiffies);
/* process each port */
for (p = 0; p <= chip->num_ports * 2 + MTPAV_PIDX_BROADCAST; p++) {
struct mtpav_port *portp = &chip->ports[p];
if ((portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED) && portp->output)
snd_mtpav_output_port_write(chip, portp, portp->output);
}
- spin_unlock_irqrestore(&chip->spinlock, flags);
}
/* spinlock held! */
static void snd_mtpav_add_output_timer(struct mtpav *chip)
{
- chip->timer.expires = 1 + jiffies;
- add_timer(&chip->timer);
+ mod_timer(&chip->timer, 1 + jiffies);
}
/* spinlock held! */
static void snd_mtpav_remove_output_timer(struct mtpav *chip)
{
- del_timer(&chip->timer);
+ timer_delete(&chip->timer);
}
/*
@@ -445,12 +412,10 @@ static int snd_mtpav_output_open(struct snd_rawmidi_substream *substream)
{
struct mtpav *mtp_card = substream->rmidi->private_data;
struct mtpav_port *portp = &mtp_card->ports[substream->number];
- unsigned long flags;
- spin_lock_irqsave(&mtp_card->spinlock, flags);
+ guard(spinlock_irqsave)(&mtp_card->spinlock);
portp->mode |= MTPAV_MODE_OUTPUT_OPENED;
portp->output = substream;
- spin_unlock_irqrestore(&mtp_card->spinlock, flags);
return 0;
};
@@ -461,12 +426,10 @@ static int snd_mtpav_output_close(struct snd_rawmidi_substream *substream)
{
struct mtpav *mtp_card = substream->rmidi->private_data;
struct mtpav_port *portp = &mtp_card->ports[substream->number];
- unsigned long flags;
- spin_lock_irqsave(&mtp_card->spinlock, flags);
+ guard(spinlock_irqsave)(&mtp_card->spinlock);
portp->mode &= ~MTPAV_MODE_OUTPUT_OPENED;
portp->output = NULL;
- spin_unlock_irqrestore(&mtp_card->spinlock, flags);
return 0;
};
@@ -477,21 +440,20 @@ static void snd_mtpav_output_trigger(struct snd_rawmidi_substream *substream, in
{
struct mtpav *mtp_card = substream->rmidi->private_data;
struct mtpav_port *portp = &mtp_card->ports[substream->number];
- unsigned long flags;
-
- spin_lock_irqsave(&mtp_card->spinlock, flags);
- if (up) {
- if (! (portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED)) {
- if (mtp_card->istimer++ == 0)
- snd_mtpav_add_output_timer(mtp_card);
- portp->mode |= MTPAV_MODE_OUTPUT_TRIGGERED;
+
+ scoped_guard(spinlock_irqsave, &mtp_card->spinlock) {
+ if (up) {
+ if ((portp->mode & MTPAV_MODE_OUTPUT_TRIGGERED)) {
+ if (mtp_card->istimer++ == 0)
+ snd_mtpav_add_output_timer(mtp_card);
+ portp->mode |= MTPAV_MODE_OUTPUT_TRIGGERED;
+ }
+ } else {
+ portp->mode &= ~MTPAV_MODE_OUTPUT_TRIGGERED;
+ if (--mtp_card->istimer == 0)
+ snd_mtpav_remove_output_timer(mtp_card);
}
- } else {
- portp->mode &= ~MTPAV_MODE_OUTPUT_TRIGGERED;
- if (--mtp_card->istimer == 0)
- snd_mtpav_remove_output_timer(mtp_card);
}
- spin_unlock_irqrestore(&mtp_card->spinlock, flags);
if (up)
snd_mtpav_output_write(substream);
@@ -541,8 +503,6 @@ static void snd_mtpav_read_bytes(struct mtpav *mcrd)
u8 sbyt = snd_mtpav_getreg(mcrd, SREG);
- /* printk(KERN_DEBUG "snd_mtpav_read_bytes() sbyt: 0x%x\n", sbyt); */
-
if (!(sbyt & SIGS_BYTE))
return;
@@ -574,24 +534,26 @@ static irqreturn_t snd_mtpav_irqh(int irq, void *dev_id)
{
struct mtpav *mcard = dev_id;
- spin_lock(&mcard->spinlock);
+ guard(spinlock)(&mcard->spinlock);
snd_mtpav_read_bytes(mcard);
- spin_unlock(&mcard->spinlock);
return IRQ_HANDLED;
}
/*
* get ISA resources
*/
-static int __devinit snd_mtpav_get_ISA(struct mtpav * mcard)
+static int snd_mtpav_get_ISA(struct mtpav *mcard)
{
- if ((mcard->res_port = request_region(port, 3, "MotuMTPAV MIDI")) == NULL) {
- snd_printk(KERN_ERR "MTVAP port 0x%lx is busy\n", port);
+ mcard->res_port = devm_request_region(mcard->card->dev, port, 3,
+ "MotuMTPAV MIDI");
+ if (!mcard->res_port) {
+ dev_err(mcard->card->dev, "MTVAP port 0x%lx is busy\n", port);
return -EBUSY;
}
mcard->port = port;
- if (request_irq(irq, snd_mtpav_irqh, IRQF_DISABLED, "MOTU MTPAV", mcard)) {
- snd_printk(KERN_ERR "MTVAP IRQ %d busy\n", irq);
+ if (devm_request_irq(mcard->card->dev, irq, snd_mtpav_irqh, 0,
+ "MOTU MTPAV", mcard)) {
+ dev_err(mcard->card->dev, "MTVAP IRQ %d busy\n", irq);
return -EBUSY;
}
mcard->irq = irq;
@@ -602,13 +564,13 @@ static int __devinit snd_mtpav_get_ISA(struct mtpav * mcard)
/*
*/
-static struct snd_rawmidi_ops snd_mtpav_output = {
+static const struct snd_rawmidi_ops snd_mtpav_output = {
.open = snd_mtpav_output_open,
.close = snd_mtpav_output_close,
.trigger = snd_mtpav_output_trigger,
};
-static struct snd_rawmidi_ops snd_mtpav_input = {
+static const struct snd_rawmidi_ops snd_mtpav_input = {
.open = snd_mtpav_input_open,
.close = snd_mtpav_input_close,
.trigger = snd_mtpav_input_trigger,
@@ -619,22 +581,22 @@ static struct snd_rawmidi_ops snd_mtpav_input = {
* get RAWMIDI resources
*/
-static void __devinit snd_mtpav_set_name(struct mtpav *chip,
- struct snd_rawmidi_substream *substream)
+static void snd_mtpav_set_name(struct mtpav *chip,
+ struct snd_rawmidi_substream *substream)
{
if (substream->number >= 0 && substream->number < chip->num_ports)
sprintf(substream->name, "MTP direct %d", (substream->number % chip->num_ports) + 1);
else if (substream->number >= 8 && substream->number < chip->num_ports * 2)
sprintf(substream->name, "MTP remote %d", (substream->number % chip->num_ports) + 1);
else if (substream->number == chip->num_ports * 2)
- strcpy(substream->name, "MTP computer");
+ strscpy(substream->name, "MTP computer");
else if (substream->number == chip->num_ports * 2 + 1)
- strcpy(substream->name, "MTP ADAT");
+ strscpy(substream->name, "MTP ADAT");
else
- strcpy(substream->name, "MTP broadcast");
+ strscpy(substream->name, "MTP broadcast");
}
-static int __devinit snd_mtpav_get_RAWMIDI(struct mtpav *mcard)
+static int snd_mtpav_get_RAWMIDI(struct mtpav *mcard)
{
int rval;
struct snd_rawmidi *rawmidi;
@@ -647,10 +609,11 @@ static int __devinit snd_mtpav_get_RAWMIDI(struct mtpav *mcard)
hwports = 8;
mcard->num_ports = hwports;
- if ((rval = snd_rawmidi_new(mcard->card, "MotuMIDI", 0,
- mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
- mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
- &mcard->rmidi)) < 0)
+ rval = snd_rawmidi_new(mcard->card, "MotuMIDI", 0,
+ mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
+ mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
+ &mcard->rmidi);
+ if (rval < 0)
return rval;
rawmidi = mcard->rmidi;
rawmidi->private_data = mcard;
@@ -678,78 +641,61 @@ static int __devinit snd_mtpav_get_RAWMIDI(struct mtpav *mcard)
static void snd_mtpav_free(struct snd_card *card)
{
struct mtpav *crd = card->private_data;
- unsigned long flags;
- spin_lock_irqsave(&crd->spinlock, flags);
+ guard(spinlock_irqsave)(&crd->spinlock);
if (crd->istimer > 0)
snd_mtpav_remove_output_timer(crd);
- spin_unlock_irqrestore(&crd->spinlock, flags);
- if (crd->irq >= 0)
- free_irq(crd->irq, (void *)crd);
- release_and_free_resource(crd->res_port);
}
/*
*/
-static int __devinit snd_mtpav_probe(struct platform_device *dev)
+static int snd_mtpav_probe(struct platform_device *dev)
{
struct snd_card *card;
int err;
struct mtpav *mtp_card;
- err = snd_card_create(index, id, THIS_MODULE, sizeof(*mtp_card), &card);
+ err = snd_devm_card_new(&dev->dev, index, id, THIS_MODULE,
+ sizeof(*mtp_card), &card);
if (err < 0)
return err;
mtp_card = card->private_data;
spin_lock_init(&mtp_card->spinlock);
- init_timer(&mtp_card->timer);
mtp_card->card = card;
mtp_card->irq = -1;
mtp_card->share_irq = 0;
mtp_card->inmidistate = 0;
mtp_card->outmidihwport = 0xffffffff;
- init_timer(&mtp_card->timer);
- mtp_card->timer.function = snd_mtpav_output_timer;
- mtp_card->timer.data = (unsigned long) mtp_card;
-
- card->private_free = snd_mtpav_free;
+ timer_setup(&mtp_card->timer, snd_mtpav_output_timer, 0);
err = snd_mtpav_get_RAWMIDI(mtp_card);
if (err < 0)
- goto __error;
+ return err;
mtp_card->inmidiport = mtp_card->num_ports + MTPAV_PIDX_BROADCAST;
err = snd_mtpav_get_ISA(mtp_card);
if (err < 0)
- goto __error;
+ return err;
- strcpy(card->driver, "MTPAV");
- strcpy(card->shortname, "MTPAV on parallel port");
+ strscpy(card->driver, "MTPAV");
+ strscpy(card->shortname, "MTPAV on parallel port");
snprintf(card->longname, sizeof(card->longname),
"MTPAV on parallel port at 0x%lx", port);
snd_mtpav_portscan(mtp_card);
- snd_card_set_dev(card, &dev->dev);
err = snd_card_register(mtp_card->card);
if (err < 0)
- goto __error;
-
- platform_set_drvdata(dev, card);
- printk(KERN_INFO "Motu MidiTimePiece on parallel port irq: %d ioport: 0x%lx\n", irq, port);
- return 0;
+ return err;
- __error:
- snd_card_free(card);
- return err;
-}
+ card->private_free = snd_mtpav_free;
-static int __devexit snd_mtpav_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
+ platform_set_drvdata(dev, card);
+ dev_info(card->dev,
+ "Motu MidiTimePiece on parallel port irq: %d ioport: 0x%lx\n",
+ irq, port);
return 0;
}
@@ -757,9 +703,8 @@ static int __devexit snd_mtpav_remove(struct platform_device *devptr)
static struct platform_driver snd_mtpav_driver = {
.probe = snd_mtpav_probe,
- .remove = __devexit_p(snd_mtpav_remove),
.driver = {
- .name = SND_MTPAV_DRIVER
+ .name = SND_MTPAV_DRIVER,
},
};
@@ -767,7 +712,8 @@ static int __init alsa_card_mtpav_init(void)
{
int err;
- if ((err = platform_driver_register(&snd_mtpav_driver)) < 0)
+ err = platform_driver_register(&snd_mtpav_driver);
+ if (err < 0)
return err;
device = platform_device_register_simple(SND_MTPAV_DRIVER, -1, NULL, 0);
diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c
index 8539ab0a0893..fe50b48c10e7 100644
--- a/sound/drivers/mts64.c
+++ b/sound/drivers/mts64.c
@@ -1,29 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA Driver for Ego Systems Inc. (ESI) Miditerminal 4140
* Copyright (c) 2006 by Matthias König <mk@phasorlab.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
*/
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/parport.h>
#include <linux/spinlock.h>
+#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/rawmidi.h>
@@ -35,22 +23,21 @@
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
static struct platform_device *platform_devices[SNDRV_CARDS];
static int device_count;
-module_param_array(index, int, NULL, S_IRUGO);
+module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
-module_param_array(id, charp, NULL, S_IRUGO);
+module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
-module_param_array(enable, bool, NULL, S_IRUGO);
+module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
MODULE_AUTHOR("Matthias Koenig <mk@phasorlab.de>");
MODULE_DESCRIPTION("ESI Miditerminal 4140");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ESI,Miditerminal 4140}}");
/*********************************************************************
* Chip specific
@@ -64,8 +51,6 @@ struct mts64 {
struct snd_card *card;
struct snd_rawmidi *rmidi;
struct pardevice *pardev;
- int pardev_claimed;
-
int open_count;
int current_midi_output_port;
int current_midi_input_port;
@@ -82,9 +67,9 @@ static int snd_mts64_free(struct mts64 *mts)
return 0;
}
-static int __devinit snd_mts64_create(struct snd_card *card,
- struct pardevice *pardev,
- struct mts64 **rchip)
+static int snd_mts64_create(struct snd_card *card,
+ struct pardevice *pardev,
+ struct mts64 **rchip)
{
struct mts64 *mts;
@@ -213,7 +198,7 @@ static int mts64_device_ready(struct parport *p)
* 0 init ok
* -EIO failure
*/
-static int __devinit mts64_device_init(struct parport *p)
+static int mts64_device_init(struct parport *p)
{
int i;
@@ -276,7 +261,7 @@ static int mts64_device_close(struct mts64 *mts)
*/
static u8 mts64_map_midi_input(u8 c)
{
- static u8 map[] = { 0, 1, 4, 2, 3 };
+ static const u8 map[] = { 0, 1, 4, 2, 3 };
return map[c];
}
@@ -289,7 +274,7 @@ static u8 mts64_map_midi_input(u8 c)
* 0 device found
* -ENODEV no device
*/
-static int __devinit mts64_probe(struct parport *p)
+static int mts64_probe(struct parport *p)
{
u8 c;
@@ -368,7 +353,7 @@ static void mts64_smpte_start(struct parport *p,
u8 seconds, u8 frames,
u8 idx)
{
- static u8 fps[5] = { MTS64_CMD_SMPTE_FPS_24,
+ static const u8 fps[5] = { MTS64_CMD_SMPTE_FPS_24,
MTS64_CMD_SMPTE_FPS_25,
MTS64_CMD_SMPTE_FPS_2997,
MTS64_CMD_SMPTE_FPS_30D,
@@ -447,9 +432,8 @@ static int snd_mts64_ctl_smpte_switch_get(struct snd_kcontrol* kctl,
{
struct mts64 *mts = snd_kcontrol_chip(kctl);
- spin_lock_irq(&mts->lock);
+ guard(spinlock_irq)(&mts->lock);
uctl->value.integer.value[0] = mts->smpte_switch;
- spin_unlock_irq(&mts->lock);
return 0;
}
@@ -460,14 +444,12 @@ static int snd_mts64_ctl_smpte_switch_put(struct snd_kcontrol* kctl,
struct snd_ctl_elem_value *uctl)
{
struct mts64 *mts = snd_kcontrol_chip(kctl);
- int changed = 0;
int val = !!uctl->value.integer.value[0];
- spin_lock_irq(&mts->lock);
+ guard(spinlock_irq)(&mts->lock);
if (mts->smpte_switch == val)
- goto __out;
+ return 0;
- changed = 1;
mts->smpte_switch = val;
if (mts->smpte_switch) {
mts64_smpte_start(mts->pardev->port,
@@ -477,12 +459,10 @@ static int snd_mts64_ctl_smpte_switch_put(struct snd_kcontrol* kctl,
} else {
mts64_smpte_stop(mts->pardev->port);
}
-__out:
- spin_unlock_irq(&mts->lock);
- return changed;
+ return 1;
}
-static struct snd_kcontrol_new mts64_ctl_smpte_switch __devinitdata = {
+static const struct snd_kcontrol_new mts64_ctl_smpte_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
.name = "SMPTE Playback Switch",
.index = 0,
@@ -530,9 +510,8 @@ static int snd_mts64_ctl_smpte_time_get(struct snd_kcontrol *kctl,
struct mts64 *mts = snd_kcontrol_chip(kctl);
int idx = kctl->private_value;
- spin_lock_irq(&mts->lock);
+ guard(spinlock_irq)(&mts->lock);
uctl->value.integer.value[0] = mts->time[idx];
- spin_unlock_irq(&mts->lock);
return 0;
}
@@ -543,19 +522,17 @@ static int snd_mts64_ctl_smpte_time_put(struct snd_kcontrol *kctl,
struct mts64 *mts = snd_kcontrol_chip(kctl);
int idx = kctl->private_value;
unsigned int time = uctl->value.integer.value[0] % 60;
- int changed = 0;
- spin_lock_irq(&mts->lock);
+ guard(spinlock_irq)(&mts->lock);
if (mts->time[idx] != time) {
- changed = 1;
mts->time[idx] = time;
+ return 1;
}
- spin_unlock_irq(&mts->lock);
- return changed;
+ return 0;
}
-static struct snd_kcontrol_new mts64_ctl_smpte_time_hours __devinitdata = {
+static const struct snd_kcontrol_new mts64_ctl_smpte_time_hours = {
.iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
.name = "SMPTE Time Hours",
.index = 0,
@@ -566,7 +543,7 @@ static struct snd_kcontrol_new mts64_ctl_smpte_time_hours __devinitdata = {
.put = snd_mts64_ctl_smpte_time_put
};
-static struct snd_kcontrol_new mts64_ctl_smpte_time_minutes __devinitdata = {
+static const struct snd_kcontrol_new mts64_ctl_smpte_time_minutes = {
.iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
.name = "SMPTE Time Minutes",
.index = 0,
@@ -577,7 +554,7 @@ static struct snd_kcontrol_new mts64_ctl_smpte_time_minutes __devinitdata = {
.put = snd_mts64_ctl_smpte_time_put
};
-static struct snd_kcontrol_new mts64_ctl_smpte_time_seconds __devinitdata = {
+static const struct snd_kcontrol_new mts64_ctl_smpte_time_seconds = {
.iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
.name = "SMPTE Time Seconds",
.index = 0,
@@ -588,7 +565,7 @@ static struct snd_kcontrol_new mts64_ctl_smpte_time_seconds __devinitdata = {
.put = snd_mts64_ctl_smpte_time_put
};
-static struct snd_kcontrol_new mts64_ctl_smpte_time_frames __devinitdata = {
+static const struct snd_kcontrol_new mts64_ctl_smpte_time_frames = {
.iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
.name = "SMPTE Time Frames",
.index = 0,
@@ -603,21 +580,11 @@ static struct snd_kcontrol_new mts64_ctl_smpte_time_frames __devinitdata = {
static int snd_mts64_ctl_smpte_fps_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[5] = { "24",
- "25",
- "29.97",
- "30D",
- "30" };
+ static const char * const texts[5] = {
+ "24", "25", "29.97", "30D", "30"
+ };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 5;
- if (uinfo->value.enumerated.item > 4)
- uinfo->value.enumerated.item = 4;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 5, texts);
}
static int snd_mts64_ctl_smpte_fps_get(struct snd_kcontrol *kctl,
@@ -625,9 +592,8 @@ static int snd_mts64_ctl_smpte_fps_get(struct snd_kcontrol *kctl,
{
struct mts64 *mts = snd_kcontrol_chip(kctl);
- spin_lock_irq(&mts->lock);
+ guard(spinlock_irq)(&mts->lock);
uctl->value.enumerated.item[0] = mts->fps;
- spin_unlock_irq(&mts->lock);
return 0;
}
@@ -636,21 +602,19 @@ static int snd_mts64_ctl_smpte_fps_put(struct snd_kcontrol *kctl,
struct snd_ctl_elem_value *uctl)
{
struct mts64 *mts = snd_kcontrol_chip(kctl);
- int changed = 0;
if (uctl->value.enumerated.item[0] >= 5)
return -EINVAL;
- spin_lock_irq(&mts->lock);
+ guard(spinlock_irq)(&mts->lock);
if (mts->fps != uctl->value.enumerated.item[0]) {
- changed = 1;
mts->fps = uctl->value.enumerated.item[0];
+ return 1;
}
- spin_unlock_irq(&mts->lock);
- return changed;
+ return 0;
}
-static struct snd_kcontrol_new mts64_ctl_smpte_fps __devinitdata = {
+static const struct snd_kcontrol_new mts64_ctl_smpte_fps = {
.iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
.name = "SMPTE Fps",
.index = 0,
@@ -662,11 +626,11 @@ static struct snd_kcontrol_new mts64_ctl_smpte_fps __devinitdata = {
};
-static int __devinit snd_mts64_ctl_create(struct snd_card *card,
- struct mts64 *mts)
+static int snd_mts64_ctl_create(struct snd_card *card,
+ struct mts64 *mts)
{
int err, i;
- static struct snd_kcontrol_new *control[] __devinitdata = {
+ static const struct snd_kcontrol_new *control[] = {
&mts64_ctl_smpte_switch,
&mts64_ctl_smpte_time_hours,
&mts64_ctl_smpte_time_minutes,
@@ -678,8 +642,8 @@ static int __devinit snd_mts64_ctl_create(struct snd_card *card,
for (i = 0; control[i]; ++i) {
err = snd_ctl_add(card, snd_ctl_new1(control[i], mts));
if (err < 0) {
- snd_printd("Cannot create control: %s\n",
- control[i]->name);
+ dev_dbg(card->dev, "Cannot create control: %s\n",
+ control[i]->name);
return err;
}
}
@@ -712,15 +676,14 @@ static int snd_mts64_rawmidi_open(struct snd_rawmidi_substream *substream)
static int snd_mts64_rawmidi_close(struct snd_rawmidi_substream *substream)
{
struct mts64 *mts = substream->rmidi->private_data;
- unsigned long flags;
--(mts->open_count);
if (mts->open_count == 0) {
/* We need the spinlock_irqsave here because we can still
have IRQs at this point */
- spin_lock_irqsave(&mts->lock, flags);
- mts64_device_close(mts);
- spin_unlock_irqrestore(&mts->lock, flags);
+ scoped_guard(spinlock_irqsave, &mts->lock) {
+ mts64_device_close(mts);
+ }
msleep(500);
@@ -735,45 +698,40 @@ static void snd_mts64_rawmidi_output_trigger(struct snd_rawmidi_substream *subst
{
struct mts64 *mts = substream->rmidi->private_data;
u8 data;
- unsigned long flags;
- spin_lock_irqsave(&mts->lock, flags);
+ guard(spinlock_irqsave)(&mts->lock);
while (snd_rawmidi_transmit_peek(substream, &data, 1) == 1) {
mts64_write_midi(mts, data, substream->number+1);
snd_rawmidi_transmit_ack(substream, 1);
}
- spin_unlock_irqrestore(&mts->lock, flags);
}
static void snd_mts64_rawmidi_input_trigger(struct snd_rawmidi_substream *substream,
int up)
{
struct mts64 *mts = substream->rmidi->private_data;
- unsigned long flags;
- spin_lock_irqsave(&mts->lock, flags);
+ guard(spinlock_irqsave)(&mts->lock);
if (up)
mts->mode[substream->number] |= MTS64_MODE_INPUT_TRIGGERED;
else
mts->mode[substream->number] &= ~MTS64_MODE_INPUT_TRIGGERED;
-
- spin_unlock_irqrestore(&mts->lock, flags);
}
-static struct snd_rawmidi_ops snd_mts64_rawmidi_output_ops = {
+static const struct snd_rawmidi_ops snd_mts64_rawmidi_output_ops = {
.open = snd_mts64_rawmidi_open,
.close = snd_mts64_rawmidi_close,
.trigger = snd_mts64_rawmidi_output_trigger
};
-static struct snd_rawmidi_ops snd_mts64_rawmidi_input_ops = {
+static const struct snd_rawmidi_ops snd_mts64_rawmidi_input_ops = {
.open = snd_mts64_rawmidi_open,
.close = snd_mts64_rawmidi_close,
.trigger = snd_mts64_rawmidi_input_trigger
};
/* Create and initialize the rawmidi component */
-static int __devinit snd_mts64_rawmidi_create(struct snd_card *card)
+static int snd_mts64_rawmidi_create(struct snd_card *card)
{
struct mts64 *mts = card->private_data;
struct snd_rawmidi *rmidi;
@@ -789,7 +747,7 @@ static int __devinit snd_mts64_rawmidi_create(struct snd_card *card)
return err;
rmidi->private_data = mts;
- strcpy(rmidi->name, CARD_NAME);
+ strscpy(rmidi->name, CARD_NAME);
rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_DUPLEX;
@@ -817,7 +775,7 @@ static int __devinit snd_mts64_rawmidi_create(struct snd_card *card)
mts->midi_input_substream[substream->number] = substream;
switch(substream->number) {
case MTS64_SMPTE_SUBSTREAM:
- strcpy(substream->name, "Miditerminal SMPTE");
+ strscpy(substream->name, "Miditerminal SMPTE");
break;
default:
sprintf(substream->name,
@@ -841,7 +799,10 @@ static void snd_mts64_interrupt(void *private)
u8 status, data;
struct snd_rawmidi_substream *substream;
- spin_lock(&mts->lock);
+ if (!mts)
+ return;
+
+ guard(spinlock)(&mts->lock);
ret = mts64_read(mts->pardev->port);
data = ret & 0x00ff;
status = ret >> 8;
@@ -850,40 +811,14 @@ static void snd_mts64_interrupt(void *private)
mts->current_midi_input_port = mts64_map_midi_input(data);
} else {
if (mts->current_midi_input_port == -1)
- goto __out;
+ return;
substream = mts->midi_input_substream[mts->current_midi_input_port];
if (mts->mode[substream->number] & MTS64_MODE_INPUT_TRIGGERED)
snd_rawmidi_receive(substream, &data, 1);
}
-__out:
- spin_unlock(&mts->lock);
-}
-
-static int __devinit snd_mts64_probe_port(struct parport *p)
-{
- struct pardevice *pardev;
- int res;
-
- pardev = parport_register_device(p, DRIVER_NAME,
- NULL, NULL, NULL,
- 0, NULL);
- if (!pardev)
- return -EIO;
-
- if (parport_claim(pardev)) {
- parport_unregister_device(pardev);
- return -EIO;
- }
-
- res = mts64_probe(p);
-
- parport_release(pardev);
- parport_unregister_device(pardev);
-
- return res;
}
-static void __devinit snd_mts64_attach(struct parport *p)
+static void snd_mts64_attach(struct parport *p)
{
struct platform_device *device;
@@ -916,10 +851,19 @@ static void snd_mts64_detach(struct parport *p)
/* nothing to do here */
}
+static int snd_mts64_dev_probe(struct pardevice *pardev)
+{
+ if (strcmp(pardev->name, DRIVER_NAME))
+ return -ENODEV;
+
+ return 0;
+}
+
static struct parport_driver mts64_parport_driver = {
- .name = "mts64",
- .attach = snd_mts64_attach,
- .detach = snd_mts64_detach
+ .name = "mts64",
+ .probe = snd_mts64_dev_probe,
+ .match_port = snd_mts64_attach,
+ .detach = snd_mts64_detach,
};
/*********************************************************************
@@ -931,15 +875,14 @@ static void snd_mts64_card_private_free(struct snd_card *card)
struct pardevice *pardev = mts->pardev;
if (pardev) {
- if (mts->pardev_claimed)
- parport_release(pardev);
+ parport_release(pardev);
parport_unregister_device(pardev);
}
snd_mts64_free(mts);
}
-static int __devinit snd_mts64_probe(struct platform_device *pdev)
+static int snd_mts64_probe(struct platform_device *pdev)
{
struct pardevice *pardev;
struct parport *p;
@@ -947,6 +890,12 @@ static int __devinit snd_mts64_probe(struct platform_device *pdev)
struct snd_card *card = NULL;
struct mts64 *mts = NULL;
int err;
+ struct pardev_cb mts64_cb = {
+ .preempt = NULL,
+ .wakeup = NULL,
+ .irq_func = snd_mts64_interrupt, /* ISR */
+ .flags = PARPORT_DEV_EXCL, /* flags */
+ };
p = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
@@ -955,91 +904,95 @@ static int __devinit snd_mts64_probe(struct platform_device *pdev)
return -ENODEV;
if (!enable[dev])
return -ENOENT;
- if ((err = snd_mts64_probe_port(p)) < 0)
- return err;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
if (err < 0) {
- snd_printd("Cannot create card\n");
+ dev_dbg(&pdev->dev, "Cannot create card\n");
return err;
}
- strcpy(card->driver, DRIVER_NAME);
- strcpy(card->shortname, "ESI " CARD_NAME);
+ strscpy(card->driver, DRIVER_NAME);
+ strscpy(card->shortname, "ESI " CARD_NAME);
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, p->base, p->irq);
- pardev = parport_register_device(p, /* port */
- DRIVER_NAME, /* name */
- NULL, /* preempt */
- NULL, /* wakeup */
- snd_mts64_interrupt, /* ISR */
- PARPORT_DEV_EXCL, /* flags */
- (void *)card); /* private */
- if (pardev == NULL) {
- snd_printd("Cannot register pardevice\n");
+ mts64_cb.private = card; /* private */
+ pardev = parport_register_dev_model(p, /* port */
+ DRIVER_NAME, /* name */
+ &mts64_cb, /* callbacks */
+ pdev->id); /* device number */
+ if (!pardev) {
+ dev_dbg(card->dev, "Cannot register pardevice\n");
err = -EIO;
goto __err;
}
- if ((err = snd_mts64_create(card, pardev, &mts)) < 0) {
- snd_printd("Cannot create main component\n");
- parport_unregister_device(pardev);
- goto __err;
+ /* claim parport */
+ if (parport_claim(pardev)) {
+ dev_dbg(card->dev, "Cannot claim parport 0x%lx\n", pardev->port->base);
+ err = -EIO;
+ goto free_pardev;
+ }
+
+ err = snd_mts64_create(card, pardev, &mts);
+ if (err < 0) {
+ dev_dbg(card->dev, "Cannot create main component\n");
+ goto release_pardev;
}
card->private_data = mts;
card->private_free = snd_mts64_card_private_free;
-
- if ((err = snd_mts64_rawmidi_create(card)) < 0) {
- snd_printd("Creating Rawmidi component failed\n");
- goto __err;
- }
- /* claim parport */
- if (parport_claim(pardev)) {
- snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
+ err = mts64_probe(p);
+ if (err) {
err = -EIO;
goto __err;
}
- mts->pardev_claimed = 1;
+
+ err = snd_mts64_rawmidi_create(card);
+ if (err < 0) {
+ dev_dbg(card->dev, "Creating Rawmidi component failed\n");
+ goto __err;
+ }
/* init device */
- if ((err = mts64_device_init(p)) < 0)
+ err = mts64_device_init(p);
+ if (err < 0)
goto __err;
platform_set_drvdata(pdev, card);
- snd_card_set_dev(card, &pdev->dev);
-
/* At this point card will be usable */
- if ((err = snd_card_register(card)) < 0) {
- snd_printd("Cannot register card\n");
+ err = snd_card_register(card);
+ if (err < 0) {
+ dev_dbg(card->dev, "Cannot register card\n");
goto __err;
}
- snd_printk(KERN_INFO "ESI Miditerminal 4140 on 0x%lx\n", p->base);
+ dev_info(card->dev, "ESI Miditerminal 4140 on 0x%lx\n", p->base);
return 0;
+release_pardev:
+ parport_release(pardev);
+free_pardev:
+ parport_unregister_device(pardev);
__err:
snd_card_free(card);
return err;
}
-static int __devexit snd_mts64_remove(struct platform_device *pdev)
+static void snd_mts64_remove(struct platform_device *pdev)
{
struct snd_card *card = platform_get_drvdata(pdev);
if (card)
snd_card_free(card);
-
- return 0;
}
-
static struct platform_driver snd_mts64_driver = {
.probe = snd_mts64_probe,
- .remove = __devexit_p(snd_mts64_remove),
+ .remove = snd_mts64_remove,
.driver = {
- .name = PLATFORM_DRIVER
+ .name = PLATFORM_DRIVER,
}
};
@@ -1064,7 +1017,8 @@ static int __init snd_mts64_module_init(void)
{
int err;
- if ((err = platform_driver_register(&snd_mts64_driver)) < 0)
+ err = platform_driver_register(&snd_mts64_driver);
+ if (err < 0)
return err;
if (parport_register_driver(&mts64_parport_driver) != 0) {
diff --git a/sound/drivers/opl3/Makefile b/sound/drivers/opl3/Makefile
index 7f2c2a10c4e5..cf4826308365 100644
--- a/sound/drivers/opl3/Makefile
+++ b/sound/drivers/opl3/Makefile
@@ -1,11 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-opl3-lib-objs := opl3_lib.o opl3_synth.o
+snd-opl3-lib-y := opl3_lib.o opl3_synth.o
snd-opl3-synth-y := opl3_seq.o opl3_midi.o opl3_drums.o
-snd-opl3-synth-$(CONFIG_SND_SEQUENCER_OSS) += opl3_oss.o
+ifneq ($(CONFIG_SND_SEQUENCER_OSS),)
+snd-opl3-synth-y += opl3_oss.o
+endif
obj-$(CONFIG_SND_OPL3_LIB) += snd-opl3-lib.o
obj-$(CONFIG_SND_OPL4_LIB) += snd-opl3-lib.o
diff --git a/sound/drivers/opl3/opl3_drums.c b/sound/drivers/opl3/opl3_drums.c
index 73694380734a..ccc49f39404d 100644
--- a/sound/drivers/opl3/opl3_drums.c
+++ b/sound/drivers/opl3/opl3_drums.c
@@ -1,29 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Uros Bizjak <uros@kss-loka.si>
*
* OPL2/OPL3/OPL4 FM routines for internal percussion channels
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include "opl3_voice.h"
-extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];
-
-static char snd_opl3_drum_table[47] =
+static const char snd_opl3_drum_table[47] =
{
OPL3_BASSDRUM_ON, OPL3_BASSDRUM_ON, OPL3_HIHAT_ON, /* 35 - 37 */
OPL3_SNAREDRUM_ON, OPL3_HIHAT_ON, OPL3_SNAREDRUM_ON, /* 38 - 40 */
@@ -63,25 +47,25 @@ struct snd_opl3_drum_note {
unsigned char feedback_connection;
};
-static struct snd_opl3_drum_voice bass_op0 = {6, 0, 0x00, 0x32, 0xf8, 0x66, 0x30, 0x00};
-static struct snd_opl3_drum_voice bass_op1 = {6, 1, 0x00, 0x03, 0xf6, 0x57, 0x30, 0x00};
-static struct snd_opl3_drum_note bass_note = {6, 0x90, 0x09};
+static const struct snd_opl3_drum_voice bass_op0 = {6, 0, 0x00, 0x32, 0xf8, 0x66, 0x30, 0x00};
+static const struct snd_opl3_drum_voice bass_op1 = {6, 1, 0x00, 0x03, 0xf6, 0x57, 0x30, 0x00};
+static const struct snd_opl3_drum_note bass_note = {6, 0x90, 0x09};
-static struct snd_opl3_drum_voice hihat = {7, 0, 0x00, 0x03, 0xf0, 0x06, 0x20, 0x00};
+static const struct snd_opl3_drum_voice hihat = {7, 0, 0x00, 0x03, 0xf0, 0x06, 0x20, 0x00};
-static struct snd_opl3_drum_voice snare = {7, 1, 0x00, 0x03, 0xf0, 0x07, 0x20, 0x02};
-static struct snd_opl3_drum_note snare_note = {7, 0xf4, 0x0d};
+static const struct snd_opl3_drum_voice snare = {7, 1, 0x00, 0x03, 0xf0, 0x07, 0x20, 0x02};
+static const struct snd_opl3_drum_note snare_note = {7, 0xf4, 0x0d};
-static struct snd_opl3_drum_voice tomtom = {8, 0, 0x02, 0x03, 0xf0, 0x06, 0x10, 0x00};
-static struct snd_opl3_drum_note tomtom_note = {8, 0xf4, 0x09};
+static const struct snd_opl3_drum_voice tomtom = {8, 0, 0x02, 0x03, 0xf0, 0x06, 0x10, 0x00};
+static const struct snd_opl3_drum_note tomtom_note = {8, 0xf4, 0x09};
-static struct snd_opl3_drum_voice cymbal = {8, 1, 0x04, 0x03, 0xf0, 0x06, 0x10, 0x00};
+static const struct snd_opl3_drum_voice cymbal = {8, 1, 0x04, 0x03, 0xf0, 0x06, 0x10, 0x00};
/*
* set drum voice characteristics
*/
static void snd_opl3_drum_voice_set(struct snd_opl3 *opl3,
- struct snd_opl3_drum_voice *data)
+ const struct snd_opl3_drum_voice *data)
{
unsigned char op_offset = snd_opl3_regmap[data->voice][data->op];
unsigned char voice_offset = data->voice;
@@ -116,7 +100,7 @@ static void snd_opl3_drum_voice_set(struct snd_opl3 *opl3,
* Set drum voice pitch
*/
static void snd_opl3_drum_note_set(struct snd_opl3 *opl3,
- struct snd_opl3_drum_note *data)
+ const struct snd_opl3_drum_note *data)
{
unsigned char voice_offset = data->voice;
unsigned short opl3_reg;
@@ -134,7 +118,7 @@ static void snd_opl3_drum_note_set(struct snd_opl3 *opl3,
* Set drum voice volume and position
*/
static void snd_opl3_drum_vol_set(struct snd_opl3 *opl3,
- struct snd_opl3_drum_voice *data,
+ const struct snd_opl3_drum_voice *data,
int vel, struct snd_midi_channel *chan)
{
unsigned char op_offset = snd_opl3_regmap[data->voice][data->op];
@@ -186,7 +170,7 @@ void snd_opl3_drum_switch(struct snd_opl3 *opl3, int note, int vel, int on_off,
struct snd_midi_channel *chan)
{
unsigned char drum_mask;
- struct snd_opl3_drum_voice *drum_voice;
+ const struct snd_opl3_drum_voice *drum_voice;
if (!(opl3->drum_reg & OPL3_PERCUSSION_ENABLE))
return;
diff --git a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c
index 6e31e46ca393..fa8a2ccbbd51 100644
--- a/sound/drivers/opl3/opl3_lib.c
+++ b/sound/drivers/opl3/opl3_lib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
* Hannu Savolainen 1993-1996,
@@ -6,40 +7,24 @@
* Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips)
*
* Most if code is ported from OSS/Lite.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <sound/opl3.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <sound/minors.h>
+#include "opl3_voice.h"
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Hannu Savolainen 1993-1996, Rob Hooft");
MODULE_DESCRIPTION("Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips)");
MODULE_LICENSE("GPL");
-extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];
-
static void snd_opl2_command(struct snd_opl3 * opl3, unsigned short cmd, unsigned char val)
{
- unsigned long flags;
unsigned long port;
/*
@@ -49,20 +34,17 @@ static void snd_opl2_command(struct snd_opl3 * opl3, unsigned short cmd, unsigne
port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port;
- spin_lock_irqsave(&opl3->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl3->reg_lock);
outb((unsigned char) cmd, port);
udelay(10);
outb((unsigned char) val, port + 1);
udelay(30);
-
- spin_unlock_irqrestore(&opl3->reg_lock, flags);
}
static void snd_opl3_command(struct snd_opl3 * opl3, unsigned short cmd, unsigned char val)
{
- unsigned long flags;
unsigned long port;
/*
@@ -72,7 +54,7 @@ static void snd_opl3_command(struct snd_opl3 * opl3, unsigned short cmd, unsigne
port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port;
- spin_lock_irqsave(&opl3->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl3->reg_lock);
outb((unsigned char) cmd, port);
inb(opl3->l_port);
@@ -81,8 +63,6 @@ static void snd_opl3_command(struct snd_opl3 * opl3, unsigned short cmd, unsigne
outb((unsigned char) val, port + 1);
inb(opl3->l_port);
inb(opl3->l_port);
-
- spin_unlock_irqrestore(&opl3->reg_lock, flags);
}
static int snd_opl3_detect(struct snd_opl3 * opl3)
@@ -106,7 +86,7 @@ static int snd_opl3_detect(struct snd_opl3 * opl3)
opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET);
signature = stat1 = inb(opl3->l_port); /* Status register */
if ((stat1 & 0xe0) != 0x00) { /* Should be 0x00 */
- snd_printd("OPL3: stat1 = 0x%x\n", stat1);
+ dev_dbg(opl3->card->dev, "OPL3: stat1 = 0x%x\n", stat1);
return -ENODEV;
}
/* Set timer1 to 0xff */
@@ -122,7 +102,7 @@ static int snd_opl3_detect(struct snd_opl3 * opl3)
/* Reset the IRQ of the FM chip */
opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET);
if ((stat2 & 0xe0) != 0xc0) { /* There is no YM3812 */
- snd_printd("OPL3: stat2 = 0x%x\n", stat2);
+ dev_dbg(opl3->card->dev, "OPL3: stat2 = 0x%x\n", stat2);
return -ENODEV;
}
@@ -156,34 +136,30 @@ static int snd_opl3_detect(struct snd_opl3 * opl3)
static int snd_opl3_timer1_start(struct snd_timer * timer)
{
- unsigned long flags;
unsigned char tmp;
unsigned int ticks;
struct snd_opl3 *opl3;
opl3 = snd_timer_chip(timer);
- spin_lock_irqsave(&opl3->timer_lock, flags);
+ guard(spinlock_irqsave)(&opl3->timer_lock);
ticks = timer->sticks;
tmp = (opl3->timer_enable | OPL3_TIMER1_START) & ~OPL3_TIMER1_MASK;
opl3->timer_enable = tmp;
opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 256 - ticks); /* timer 1 count */
opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* enable timer 1 IRQ */
- spin_unlock_irqrestore(&opl3->timer_lock, flags);
return 0;
}
static int snd_opl3_timer1_stop(struct snd_timer * timer)
{
- unsigned long flags;
unsigned char tmp;
struct snd_opl3 *opl3;
opl3 = snd_timer_chip(timer);
- spin_lock_irqsave(&opl3->timer_lock, flags);
+ guard(spinlock_irqsave)(&opl3->timer_lock);
tmp = (opl3->timer_enable | OPL3_TIMER1_MASK) & ~OPL3_TIMER1_START;
opl3->timer_enable = tmp;
opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* disable timer #1 */
- spin_unlock_irqrestore(&opl3->timer_lock, flags);
return 0;
}
@@ -193,34 +169,30 @@ static int snd_opl3_timer1_stop(struct snd_timer * timer)
static int snd_opl3_timer2_start(struct snd_timer * timer)
{
- unsigned long flags;
unsigned char tmp;
unsigned int ticks;
struct snd_opl3 *opl3;
opl3 = snd_timer_chip(timer);
- spin_lock_irqsave(&opl3->timer_lock, flags);
+ guard(spinlock_irqsave)(&opl3->timer_lock);
ticks = timer->sticks;
tmp = (opl3->timer_enable | OPL3_TIMER2_START) & ~OPL3_TIMER2_MASK;
opl3->timer_enable = tmp;
opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER2, 256 - ticks); /* timer 1 count */
opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* enable timer 1 IRQ */
- spin_unlock_irqrestore(&opl3->timer_lock, flags);
return 0;
}
static int snd_opl3_timer2_stop(struct snd_timer * timer)
{
- unsigned long flags;
unsigned char tmp;
struct snd_opl3 *opl3;
opl3 = snd_timer_chip(timer);
- spin_lock_irqsave(&opl3->timer_lock, flags);
+ guard(spinlock_irqsave)(&opl3->timer_lock);
tmp = (opl3->timer_enable | OPL3_TIMER2_MASK) & ~OPL3_TIMER2_START;
opl3->timer_enable = tmp;
opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp); /* disable timer #1 */
- spin_unlock_irqrestore(&opl3->timer_lock, flags);
return 0;
}
@@ -228,7 +200,7 @@ static int snd_opl3_timer2_stop(struct snd_timer * timer)
*/
-static struct snd_timer_hardware snd_opl3_timer1 =
+static const struct snd_timer_hardware snd_opl3_timer1 =
{
.flags = SNDRV_TIMER_HW_STOP,
.resolution = 80000,
@@ -237,7 +209,7 @@ static struct snd_timer_hardware snd_opl3_timer1 =
.stop = snd_opl3_timer1_stop,
};
-static struct snd_timer_hardware snd_opl3_timer2 =
+static const struct snd_timer_hardware snd_opl3_timer2 =
{
.flags = SNDRV_TIMER_HW_STOP,
.resolution = 320000,
@@ -257,8 +229,9 @@ static int snd_opl3_timer1_init(struct snd_opl3 * opl3, int timer_no)
tid.card = opl3->card->number;
tid.device = timer_no;
tid.subdevice = 0;
- if ((err = snd_timer_new(opl3->card, "AdLib timer #1", &tid, &timer)) >= 0) {
- strcpy(timer->name, "AdLib timer #1");
+ err = snd_timer_new(opl3->card, "AdLib timer #1", &tid, &timer);
+ if (err >= 0) {
+ strscpy(timer->name, "AdLib timer #1");
timer->private_data = opl3;
timer->hw = snd_opl3_timer1;
}
@@ -277,8 +250,9 @@ static int snd_opl3_timer2_init(struct snd_opl3 * opl3, int timer_no)
tid.card = opl3->card->number;
tid.device = timer_no;
tid.subdevice = 0;
- if ((err = snd_timer_new(opl3->card, "AdLib timer #2", &tid, &timer)) >= 0) {
- strcpy(timer->name, "AdLib timer #2");
+ err = snd_timer_new(opl3->card, "AdLib timer #2", &tid, &timer);
+ if (err >= 0) {
+ strscpy(timer->name, "AdLib timer #2");
timer->private_data = opl3;
timer->hw = snd_opl3_timer2;
}
@@ -301,9 +275,6 @@ void snd_opl3_interrupt(struct snd_hwdep * hw)
opl3 = hw->private_data;
status = inb(opl3->l_port);
-#if 0
- snd_printk(KERN_DEBUG "AdLib IRQ status = 0x%x\n", status);
-#endif
if (!(status & 0x80))
return;
@@ -346,7 +317,7 @@ int snd_opl3_new(struct snd_card *card,
unsigned short hardware,
struct snd_opl3 **ropl3)
{
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_opl3_dev_free,
};
struct snd_opl3 *opl3;
@@ -354,17 +325,16 @@ int snd_opl3_new(struct snd_card *card,
*ropl3 = NULL;
opl3 = kzalloc(sizeof(*opl3), GFP_KERNEL);
- if (opl3 == NULL) {
- snd_printk(KERN_ERR "opl3: cannot allocate\n");
+ if (!opl3)
return -ENOMEM;
- }
opl3->card = card;
opl3->hardware = hardware;
spin_lock_init(&opl3->reg_lock);
spin_lock_init(&opl3->timer_lock);
- if ((err = snd_device_new(card, SNDRV_DEV_CODEC, opl3, &ops)) < 0) {
+ err = snd_device_new(card, SNDRV_DEV_CODEC, opl3, &ops);
+ if (err < 0) {
snd_opl3_free(opl3);
return err;
}
@@ -378,7 +348,8 @@ EXPORT_SYMBOL(snd_opl3_new);
int snd_opl3_init(struct snd_opl3 *opl3)
{
if (! opl3->command) {
- printk(KERN_ERR "snd_opl3_init: command not defined!\n");
+ dev_err(opl3->card->dev,
+ "snd_opl3_init: command not defined!\n");
return -EINVAL;
}
@@ -412,19 +383,23 @@ int snd_opl3_create(struct snd_card *card,
int err;
*ropl3 = NULL;
- if ((err = snd_opl3_new(card, hardware, &opl3)) < 0)
+ err = snd_opl3_new(card, hardware, &opl3);
+ if (err < 0)
return err;
if (! integrated) {
- if ((opl3->res_l_port = request_region(l_port, 2, "OPL2/3 (left)")) == NULL) {
- snd_printk(KERN_ERR "opl3: can't grab left port 0x%lx\n", l_port);
+ opl3->res_l_port = request_region(l_port, 2, "OPL2/3 (left)");
+ if (!opl3->res_l_port) {
+ dev_err(card->dev, "opl3: can't grab left port 0x%lx\n", l_port);
snd_device_free(card, opl3);
return -EBUSY;
}
- if (r_port != 0 &&
- (opl3->res_r_port = request_region(r_port, 2, "OPL2/3 (right)")) == NULL) {
- snd_printk(KERN_ERR "opl3: can't grab right port 0x%lx\n", r_port);
- snd_device_free(card, opl3);
- return -EBUSY;
+ if (r_port != 0) {
+ opl3->res_r_port = request_region(r_port, 2, "OPL2/3 (right)");
+ if (!opl3->res_r_port) {
+ dev_err(card->dev, "opl3: can't grab right port 0x%lx\n", r_port);
+ snd_device_free(card, opl3);
+ return -EBUSY;
+ }
}
}
opl3->l_port = l_port;
@@ -439,9 +414,10 @@ int snd_opl3_create(struct snd_card *card,
break;
default:
opl3->command = &snd_opl2_command;
- if ((err = snd_opl3_detect(opl3)) < 0) {
- snd_printd("OPL2/3 chip not detected at 0x%lx/0x%lx\n",
- opl3->l_port, opl3->r_port);
+ err = snd_opl3_detect(opl3);
+ if (err < 0) {
+ dev_dbg(card->dev, "OPL2/3 chip not detected at 0x%lx/0x%lx\n",
+ opl3->l_port, opl3->r_port);
snd_device_free(card, opl3);
return err;
}
@@ -465,11 +441,14 @@ int snd_opl3_timer_new(struct snd_opl3 * opl3, int timer1_dev, int timer2_dev)
{
int err;
- if (timer1_dev >= 0)
- if ((err = snd_opl3_timer1_init(opl3, timer1_dev)) < 0)
+ if (timer1_dev >= 0) {
+ err = snd_opl3_timer1_init(opl3, timer1_dev);
+ if (err < 0)
return err;
+ }
if (timer2_dev >= 0) {
- if ((err = snd_opl3_timer2_init(opl3, timer2_dev)) < 0) {
+ err = snd_opl3_timer2_init(opl3, timer2_dev);
+ if (err < 0) {
snd_device_free(opl3->card, opl3->timer1);
opl3->timer1 = NULL;
return err;
@@ -493,30 +472,29 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
/* create hardware dependent device (direct FM) */
- if ((err = snd_hwdep_new(card, "OPL2/OPL3", device, &hw)) < 0) {
+ err = snd_hwdep_new(card, "OPL2/OPL3", device, &hw);
+ if (err < 0) {
snd_device_free(card, opl3);
return err;
}
hw->private_data = opl3;
hw->exclusive = 1;
#ifdef CONFIG_SND_OSSEMUL
- if (device == 0) {
+ if (device == 0)
hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM;
- sprintf(hw->oss_dev, "dmfm%i", card->number);
- }
#endif
- strcpy(hw->name, hw->id);
+ strscpy(hw->name, hw->id);
switch (opl3->hardware & OPL3_HW_MASK) {
case OPL3_HW_OPL2:
- strcpy(hw->name, "OPL2 FM");
+ strscpy(hw->name, "OPL2 FM");
hw->iface = SNDRV_HWDEP_IFACE_OPL2;
break;
case OPL3_HW_OPL3:
- strcpy(hw->name, "OPL3 FM");
+ strscpy(hw->name, "OPL3 FM");
hw->iface = SNDRV_HWDEP_IFACE_OPL3;
break;
case OPL3_HW_OPL4:
- strcpy(hw->name, "OPL4 FM");
+ strscpy(hw->name, "OPL4 FM");
hw->iface = SNDRV_HWDEP_IFACE_OPL4;
break;
}
@@ -529,10 +507,10 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
opl3->hwdep = hw;
opl3->seq_dev_num = seq_device;
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3,
sizeof(struct snd_opl3 *), &opl3->seq_dev) >= 0) {
- strcpy(opl3->seq_dev->name, hw->name);
+ strscpy(opl3->seq_dev->name, hw->name);
*(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(opl3->seq_dev) = opl3;
}
#endif
@@ -542,19 +520,3 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
}
EXPORT_SYMBOL(snd_opl3_hwdep_new);
-
-/*
- * INIT part
- */
-
-static int __init alsa_opl3_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_opl3_exit(void)
-{
-}
-
-module_init(alsa_opl3_init)
-module_exit(alsa_opl3_exit)
diff --git a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c
index 7d722a025d0d..6d3c5b5a35ff 100644
--- a/sound/drivers/opl3/opl3_midi.c
+++ b/sound/drivers/opl3/opl3_midi.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Uros Bizjak <uros@kss-loka.si>
*
* Midi synth routines for OPL2/OPL3/OPL4 FM
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#undef DEBUG_ALLOC
@@ -25,9 +11,12 @@
#include "opl3_voice.h"
#include <sound/asoundef.h>
-extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];
-
-extern int use_internal_drums;
+#ifdef DEBUG_MIDI
+#define opl3_dbg(opl3, fmt, ...) \
+ dev_dbg(((struct snd_opl3 *)(opl3))->card->dev, fmt, ##__VA_ARGS__)
+#else
+#define opl3_dbg(opl3, fmt, ...) do {} while (0)
+#endif
static void snd_opl3_note_off_unsafe(void *p, int note, int vel,
struct snd_midi_channel *chan);
@@ -41,7 +30,7 @@ static void snd_opl3_note_off_unsafe(void *p, int note, int vel,
* it saves a lot of log() calculations. (Rob Hooft <hooft@chem.ruu.nl>)
*/
-static char opl3_volume_table[128] =
+static const char opl3_volume_table[128] =
{
-63, -48, -40, -35, -32, -29, -27, -26,
-24, -23, -21, -20, -19, -18, -18, -17,
@@ -87,7 +76,7 @@ void snd_opl3_calc_volume(unsigned char *volbyte, int vel,
/*
* Converts the note frequency to block and fnum values for the FM chip
*/
-static short opl3_note_table[16] =
+static const short opl3_note_table[16] =
{
305, 323, /* for pitch bending, -2 semitones */
343, 363, 385, 408, 432, 458, 485, 514, 544, 577, 611, 647,
@@ -105,6 +94,8 @@ static void snd_opl3_calc_pitch(unsigned char *fnum, unsigned char *blocknum,
int pitchbend = chan->midi_pitchbend;
int segment;
+ if (pitchbend < -0x2000)
+ pitchbend = -0x2000;
if (pitchbend > 0x1FFF)
pitchbend = 0x1FFF;
@@ -123,14 +114,17 @@ static void snd_opl3_calc_pitch(unsigned char *fnum, unsigned char *blocknum,
#ifdef DEBUG_ALLOC
-static void debug_alloc(struct snd_opl3 *opl3, char *s, int voice) {
+static void debug_alloc(struct snd_opl3 *opl3, char *s, int voice)
+{
int i;
- char *str = "x.24";
+ const char *str = "x.24";
+ char buf[MAX_OPL3_VOICES + 1];
- printk(KERN_DEBUG "time %.5i: %s [%.2i]: ", opl3->use_time, s, voice);
for (i = 0; i < opl3->max_voices; i++)
- printk("%c", *(str + opl3->voices[i].state + 1));
- printk("\n");
+ buf[i] = str[opl3->voices[i].state + 1];
+ buf[i] = 0;
+ dev_dbg(opl3->card->dev, "time %.5i: %s [%.2i]: %s\n",
+ opl3->use_time, s, voice, buf);
}
#endif
@@ -163,7 +157,7 @@ static int opl3_get_voice(struct snd_opl3 *opl3, int instr_4op,
struct best *bp;
for (i = 0; i < END; i++) {
- best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */;
+ best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */
best[i].voice = -1;
}
@@ -196,8 +190,7 @@ static int opl3_get_voice(struct snd_opl3 *opl3, int instr_4op,
if (vp2->state == SNDRV_OPL3_ST_ON_2OP) {
/* kill two voices, EXPENSIVE */
bp++;
- voice_time = (voice_time > vp->time) ?
- voice_time : vp->time;
+ voice_time = max(voice_time, vp2->time);
}
} else {
/* allocate 2op voice */
@@ -220,9 +213,10 @@ static int opl3_get_voice(struct snd_opl3 *opl3, int instr_4op,
for (i = 0; i < END; i++) {
if (best[i].voice >= 0) {
#ifdef DEBUG_ALLOC
- printk(KERN_DEBUG "%s %iop allocation on voice %i\n",
- alloc_type[i], instr_4op ? 4 : 2,
- best[i].voice);
+ dev_dbg(opl3->card->dev,
+ "%s %iop allocation on voice %i\n",
+ alloc_type[i], instr_4op ? 4 : 2,
+ best[i].voice);
#endif
return best[i].voice;
}
@@ -236,35 +230,31 @@ static int opl3_get_voice(struct snd_opl3 *opl3, int instr_4op,
/*
* System timer interrupt function
*/
-void snd_opl3_timer_func(unsigned long data)
+void snd_opl3_timer_func(struct timer_list *t)
{
- struct snd_opl3 *opl3 = (struct snd_opl3 *)data;
- unsigned long flags;
+ struct snd_opl3 *opl3 = timer_container_of(opl3, t, tlist);
int again = 0;
int i;
- spin_lock_irqsave(&opl3->voice_lock, flags);
- for (i = 0; i < opl3->max_voices; i++) {
- struct snd_opl3_voice *vp = &opl3->voices[i];
- if (vp->state > 0 && vp->note_off_check) {
- if (vp->note_off == jiffies)
- snd_opl3_note_off_unsafe(opl3, vp->note, 0,
- vp->chan);
- else
- again++;
+ scoped_guard(spinlock_irqsave, &opl3->voice_lock) {
+ for (i = 0; i < opl3->max_voices; i++) {
+ struct snd_opl3_voice *vp = &opl3->voices[i];
+ if (vp->state > 0 && vp->note_off_check) {
+ if (vp->note_off == jiffies)
+ snd_opl3_note_off_unsafe(opl3, vp->note, 0,
+ vp->chan);
+ else
+ again++;
+ }
}
}
- spin_unlock_irqrestore(&opl3->voice_lock, flags);
- spin_lock_irqsave(&opl3->sys_timer_lock, flags);
- if (again) {
- opl3->tlist.expires = jiffies + 1; /* invoke again */
- add_timer(&opl3->tlist);
- } else {
+ guard(spinlock_irqsave)(&opl3->sys_timer_lock);
+ if (again)
+ mod_timer(&opl3->tlist, jiffies + 1); /* invoke again */
+ else
opl3->sys_timer_status = 0;
- }
- spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
}
/*
@@ -272,20 +262,17 @@ void snd_opl3_timer_func(unsigned long data)
*/
static void snd_opl3_start_timer(struct snd_opl3 *opl3)
{
- unsigned long flags;
- spin_lock_irqsave(&opl3->sys_timer_lock, flags);
+ guard(spinlock_irqsave)(&opl3->sys_timer_lock);
if (! opl3->sys_timer_status) {
- opl3->tlist.expires = jiffies + 1;
- add_timer(&opl3->tlist);
+ mod_timer(&opl3->tlist, jiffies + 1);
opl3->sys_timer_status = 1;
}
- spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
}
/* ------------------------------ */
-static int snd_opl3_oss_map[MAX_OPL3_VOICES] = {
+static const int snd_opl3_oss_map[MAX_OPL3_VOICES] = {
0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17, 3, 4 ,5, 12, 13, 14
};
@@ -318,14 +305,11 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
struct fm_patch *patch;
struct fm_instrument *fm;
- unsigned long flags;
opl3 = p;
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG "Note on, ch %i, inst %i, note %i, vel %i\n",
- chan->number, chan->midi_program, note, vel);
-#endif
+ opl3_dbg(opl3, "Note on, ch %i, inst %i, note %i, vel %i\n",
+ chan->number, chan->midi_program, note, vel);
/* in SYNTH mode, application takes care of voices */
/* in SEQ mode, drum voice numbers are notes on drum channel */
@@ -348,20 +332,17 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
prg = chan->midi_program;
}
- spin_lock_irqsave(&opl3->voice_lock, flags);
+ guard(spinlock_irqsave)(&opl3->voice_lock);
if (use_internal_drums) {
snd_opl3_drum_switch(opl3, note, vel, 1, chan);
- spin_unlock_irqrestore(&opl3->voice_lock, flags);
return;
}
__extra_prg:
patch = snd_opl3_find_patch(opl3, prg, bank, 0);
- if (!patch) {
- spin_unlock_irqrestore(&opl3->voice_lock, flags);
+ if (!patch)
return;
- }
fm = &patch->inst;
switch (patch->type) {
@@ -373,14 +354,12 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
instr_4op = 1;
break;
}
+ fallthrough;
default:
- spin_unlock_irqrestore(&opl3->voice_lock, flags);
return;
}
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG " --> OPL%i instrument: %s\n",
- instr_4op ? 3 : 2, patch->name);
-#endif
+ opl3_dbg(opl3, " --> OPL%i instrument: %s\n",
+ instr_4op ? 3 : 2, patch->name);
/* in SYNTH mode, application takes care of voices */
/* in SEQ mode, allocate voice on free OPL3 channel */
if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
@@ -390,6 +369,9 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
voice = snd_opl3_oss_map[chan->number];
}
+ if (voice < 0)
+ return;
+
if (voice < MAX_OPL2_VOICES) {
/* Left register block for voices 0 .. 8 */
reg_side = OPL3_LEFT;
@@ -411,7 +393,7 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
}
if (instr_4op) {
vp2 = &opl3->voices[voice + 3];
- if (vp->state > 0) {
+ if (vp2->state > 0) {
opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK +
voice_offset + 3);
reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT;
@@ -436,10 +418,8 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
}
}
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG " --> setting OPL3 connection: 0x%x\n",
- opl3->connection_reg);
-#endif
+ opl3_dbg(opl3, " --> setting OPL3 connection: 0x%x\n",
+ opl3->connection_reg);
/*
* calculate volume depending on connection
* between FM operators (see include/opl3.h)
@@ -456,7 +436,7 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
switch (connection) {
case 0x03:
snd_opl3_calc_volume(&vol_op[2], vel, chan);
- /* fallthru */
+ fallthrough;
case 0x02:
snd_opl3_calc_volume(&vol_op[0], vel, chan);
break;
@@ -471,9 +451,7 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
/* Program the FM voice characteristics */
for (i = 0; i < (instr_4op ? 4 : 2); i++) {
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG " --> programming operator %i\n", i);
-#endif
+ opl3_dbg(opl3, " --> programming operator %i\n", i);
op_offset = snd_opl3_regmap[voice_offset][i];
/* Set OPL3 AM_VIB register of requested voice/operator */
@@ -551,9 +529,7 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
/* Set output sound flag */
blocknum |= OPL3_KEYON_BIT;
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG " --> trigger voice %i\n", voice);
-#endif
+ opl3_dbg(opl3, " --> trigger voice %i\n", voice);
/* Set OPL3 KEYON_BLOCK register of requested voice */
opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
opl3->command(opl3, opl3_reg, blocknum);
@@ -607,12 +583,9 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
bank = 0;
prg = extra_prg - 1;
}
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG " *** allocating extra program\n");
-#endif
+ opl3_dbg(opl3, " *** allocating extra program\n");
goto __extra_prg;
}
- spin_unlock_irqrestore(&opl3->voice_lock, flags);
}
static void snd_opl3_kill_voice(struct snd_opl3 *opl3, int voice)
@@ -638,9 +611,7 @@ static void snd_opl3_kill_voice(struct snd_opl3 *opl3, int voice)
}
/* kill voice */
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG " --> kill voice %i\n", voice);
-#endif
+ opl3_dbg(opl3, " --> kill voice %i\n", voice);
opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset);
/* clear Key ON bit */
opl3->command(opl3, opl3_reg, vp->keyon_reg);
@@ -674,10 +645,8 @@ static void snd_opl3_note_off_unsafe(void *p, int note, int vel,
opl3 = p;
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG "Note off, ch %i, inst %i, note %i\n",
- chan->number, chan->midi_program, note);
-#endif
+ opl3_dbg(opl3, "Note off, ch %i, inst %i, note %i\n",
+ chan->number, chan->midi_program, note);
if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
if (chan->drum_channel && use_internal_drums) {
@@ -705,11 +674,9 @@ void snd_opl3_note_off(void *p, int note, int vel,
struct snd_midi_channel *chan)
{
struct snd_opl3 *opl3 = p;
- unsigned long flags;
- spin_lock_irqsave(&opl3->voice_lock, flags);
+ guard(spinlock_irqsave)(&opl3->voice_lock);
snd_opl3_note_off_unsafe(p, note, vel, chan);
- spin_unlock_irqrestore(&opl3->voice_lock, flags);
}
/*
@@ -717,13 +684,8 @@ void snd_opl3_note_off(void *p, int note, int vel,
*/
void snd_opl3_key_press(void *p, int note, int vel, struct snd_midi_channel *chan)
{
- struct snd_opl3 *opl3;
-
- opl3 = p;
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG "Key pressure, ch#: %i, inst#: %i\n",
- chan->number, chan->midi_program);
-#endif
+ opl3_dbg(p, "Key pressure, ch#: %i, inst#: %i\n",
+ chan->number, chan->midi_program);
}
/*
@@ -731,13 +693,8 @@ void snd_opl3_key_press(void *p, int note, int vel, struct snd_midi_channel *cha
*/
void snd_opl3_terminate_note(void *p, int note, struct snd_midi_channel *chan)
{
- struct snd_opl3 *opl3;
-
- opl3 = p;
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG "Terminate note, ch#: %i, inst#: %i\n",
- chan->number, chan->midi_program);
-#endif
+ opl3_dbg(p, "Terminate note, ch#: %i, inst#: %i\n",
+ chan->number, chan->midi_program);
}
static void snd_opl3_update_pitch(struct snd_opl3 *opl3, int voice)
@@ -793,9 +750,7 @@ static void snd_opl3_pitch_ctrl(struct snd_opl3 *opl3, struct snd_midi_channel *
int voice;
struct snd_opl3_voice *vp;
- unsigned long flags;
-
- spin_lock_irqsave(&opl3->voice_lock, flags);
+ guard(spinlock_irqsave)(&opl3->voice_lock);
if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) {
for (voice = 0; voice < opl3->max_voices; voice++) {
@@ -811,7 +766,6 @@ static void snd_opl3_pitch_ctrl(struct snd_opl3 *opl3, struct snd_midi_channel *
snd_opl3_update_pitch(opl3, voice);
}
}
- spin_unlock_irqrestore(&opl3->voice_lock, flags);
}
/*
@@ -823,10 +777,8 @@ void snd_opl3_control(void *p, int type, struct snd_midi_channel *chan)
struct snd_opl3 *opl3;
opl3 = p;
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG "Controller, TYPE = %i, ch#: %i, inst#: %i\n",
- type, chan->number, chan->midi_program);
-#endif
+ opl3_dbg(opl3, "Controller, TYPE = %i, ch#: %i, inst#: %i\n",
+ type, chan->number, chan->midi_program);
switch (type) {
case MIDI_CTL_MSB_MODWHEEL:
@@ -857,13 +809,8 @@ void snd_opl3_control(void *p, int type, struct snd_midi_channel *chan)
void snd_opl3_nrpn(void *p, struct snd_midi_channel *chan,
struct snd_midi_channel_set *chset)
{
- struct snd_opl3 *opl3;
-
- opl3 = p;
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG "NRPN, ch#: %i, inst#: %i\n",
- chan->number, chan->midi_program);
-#endif
+ opl3_dbg(p, "NRPN, ch#: %i, inst#: %i\n",
+ chan->number, chan->midi_program);
}
/*
@@ -872,10 +819,5 @@ void snd_opl3_nrpn(void *p, struct snd_midi_channel *chan,
void snd_opl3_sysex(void *p, unsigned char *buf, int len,
int parsed, struct snd_midi_channel_set *chset)
{
- struct snd_opl3 *opl3;
-
- opl3 = p;
-#ifdef DEBUG_MIDI
- snd_printk(KERN_DEBUG "SYSEX\n");
-#endif
+ opl3_dbg(p, "SYSEX\n");
}
diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c
index ade3ca52422e..6d39b2b77b80 100644
--- a/sound/drivers/opl3/opl3_oss.c
+++ b/sound/drivers/opl3/opl3_oss.c
@@ -1,23 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Interface for OSS sequencer emulation
*
* Copyright (C) 2000 Uros Bizjak <uros@kss-loka.si>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/export.h>
#include "opl3_voice.h"
static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure);
@@ -26,25 +14,9 @@ static int snd_opl3_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd,
static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format, const char __user *buf, int offs, int count);
static int snd_opl3_reset_seq_oss(struct snd_seq_oss_arg *arg);
-/* */
-
-static inline mm_segment_t snd_enter_user(void)
-{
- mm_segment_t fs = get_fs();
- set_fs(get_ds());
- return fs;
-}
-
-static inline void snd_leave_user(mm_segment_t fs)
-{
- set_fs(fs);
-}
-
/* operators */
-extern struct snd_midi_op opl3_ops;
-
-static struct snd_seq_oss_callback oss_callback = {
+static const struct snd_seq_oss_callback oss_callback = {
.owner = THIS_MODULE,
.open = snd_opl3_open_seq_oss,
.close = snd_opl3_close_seq_oss,
@@ -125,7 +97,7 @@ void snd_opl3_init_seq_oss(struct snd_opl3 *opl3, char *name)
return;
opl3->oss_seq_dev = dev;
- strlcpy(dev->name, name, sizeof(dev->name));
+ strscpy(dev->name, name, sizeof(dev->name));
arg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
arg->type = SYNTH_TYPE_FM;
if (opl3->hardware < OPL3_HW_OPL3) {
@@ -164,7 +136,8 @@ static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure)
if (snd_BUG_ON(!arg))
return -ENXIO;
- if ((err = snd_opl3_synth_setup(opl3)) < 0)
+ err = snd_opl3_synth_setup(opl3);
+ if (err < 0)
return err;
/* fill the argument data */
@@ -172,7 +145,8 @@ static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure)
arg->addr.client = opl3->oss_chset->client;
arg->addr.port = opl3->oss_chset->port;
- if ((err = snd_opl3_synth_use_inc(opl3)) < 0)
+ err = snd_opl3_synth_use_inc(opl3);
+ if (err < 0)
return err;
opl3->synth_mode = SNDRV_OPL3_MODE_SYNTH;
@@ -219,14 +193,14 @@ static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
return -EINVAL;
if (count < (int)sizeof(sbi)) {
- snd_printk(KERN_ERR "FM Error: Patch record too short\n");
+ dev_err(opl3->card->dev, "FM Error: Patch record too short\n");
return -EINVAL;
}
if (copy_from_user(&sbi, buf, sizeof(sbi)))
return -EFAULT;
if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) {
- snd_printk(KERN_ERR "FM Error: Invalid instrument number %d\n",
+ dev_err(opl3->card->dev, "FM Error: Invalid instrument number %d\n",
sbi.channel);
return -EINVAL;
}
@@ -253,9 +227,8 @@ static int snd_opl3_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd,
opl3 = arg->private_data;
switch (cmd) {
case SNDCTL_FM_LOAD_INSTR:
- snd_printk(KERN_ERR "OPL3: "
- "Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. "
- "Fix the program.\n");
+ dev_err(opl3->card->dev,
+ "OPL3: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n");
return -EINVAL;
case SNDCTL_SYNTH_MEMAVL:
@@ -274,11 +247,8 @@ static int snd_opl3_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd,
/* reset device */
static int snd_opl3_reset_seq_oss(struct snd_seq_oss_arg *arg)
{
- struct snd_opl3 *opl3;
-
if (snd_BUG_ON(!arg))
return -ENXIO;
- opl3 = arg->private_data;
return 0;
}
diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c
index 2d33f53d36b8..d3278428d360 100644
--- a/sound/drivers/opl3/opl3_seq.c
+++ b/sound/drivers/opl3/opl3_seq.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Uros Bizjak <uros@kss-loka.si>
*
@@ -5,33 +6,19 @@
*
* OPL2/3 FM instrument loader:
* alsa-tools/seq/sbiload/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include "opl3_voice.h"
#include <linux/init.h>
#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/initval.h>
MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ALSA driver for OPL3 FM synth");
-int use_internal_drums = 0;
+bool use_internal_drums = 0;
module_param(use_internal_drums, bool, 0444);
MODULE_PARM_DESC(use_internal_drums, "Enable internal OPL2/3 drums.");
@@ -53,13 +40,11 @@ int snd_opl3_synth_setup(struct snd_opl3 * opl3)
int idx;
struct snd_hwdep *hwdep = opl3->hwdep;
- mutex_lock(&hwdep->open_mutex);
- if (hwdep->used) {
- mutex_unlock(&hwdep->open_mutex);
- return -EBUSY;
+ scoped_guard(mutex, &hwdep->open_mutex) {
+ if (hwdep->used)
+ return -EBUSY;
+ hwdep->used++;
}
- hwdep->used++;
- mutex_unlock(&hwdep->open_mutex);
snd_opl3_reset(opl3);
@@ -81,22 +66,21 @@ int snd_opl3_synth_setup(struct snd_opl3 * opl3)
void snd_opl3_synth_cleanup(struct snd_opl3 * opl3)
{
- unsigned long flags;
struct snd_hwdep *hwdep;
/* Stop system timer */
- spin_lock_irqsave(&opl3->sys_timer_lock, flags);
- if (opl3->sys_timer_status) {
- del_timer(&opl3->tlist);
- opl3->sys_timer_status = 0;
+ scoped_guard(spinlock_irq, &opl3->sys_timer_lock) {
+ if (opl3->sys_timer_status) {
+ timer_delete(&opl3->tlist);
+ opl3->sys_timer_status = 0;
+ }
}
- spin_unlock_irqrestore(&opl3->sys_timer_lock, flags);
snd_opl3_reset(opl3);
hwdep = opl3->hwdep;
- mutex_lock(&hwdep->open_mutex);
- hwdep->used--;
- mutex_unlock(&hwdep->open_mutex);
+ scoped_guard(mutex, &hwdep->open_mutex) {
+ hwdep->used--;
+ }
wake_up(&hwdep->open_wait);
}
@@ -105,7 +89,8 @@ static int snd_opl3_synth_use(void *private_data, struct snd_seq_port_subscribe
struct snd_opl3 *opl3 = private_data;
int err;
- if ((err = snd_opl3_synth_setup(opl3)) < 0)
+ err = snd_opl3_synth_setup(opl3);
+ if (err < 0)
return err;
if (use_internal_drums) {
@@ -120,7 +105,8 @@ static int snd_opl3_synth_use(void *private_data, struct snd_seq_port_subscribe
}
if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) {
- if ((err = snd_opl3_synth_use_inc(opl3)) < 0)
+ err = snd_opl3_synth_use_inc(opl3);
+ if (err < 0)
return err;
}
opl3->synth_mode = SNDRV_OPL3_MODE_SEQ;
@@ -141,7 +127,7 @@ static int snd_opl3_synth_unuse(void *private_data, struct snd_seq_port_subscrib
/*
* MIDI emulation operators
*/
-struct snd_midi_op opl3_ops = {
+const struct snd_midi_op opl3_ops = {
.note_on = snd_opl3_note_on,
.note_off = snd_opl3_note_off,
.key_press = snd_opl3_key_press,
@@ -215,8 +201,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];
@@ -239,34 +226,34 @@ static int snd_opl3_seq_new_device(struct snd_seq_device *dev)
if (client < 0)
return client;
- if ((err = snd_opl3_synth_create_port(opl3)) < 0) {
+ err = snd_opl3_synth_create_port(opl3);
+ if (err < 0) {
snd_seq_delete_kernel_client(client);
opl3->seq_client = -1;
return err;
}
/* setup system timer */
- init_timer(&opl3->tlist);
- opl3->tlist.function = snd_opl3_timer_func;
- opl3->tlist.data = (unsigned long) opl3;
+ timer_setup(&opl3->tlist, snd_opl3_timer_func, 0);
spin_lock_init(&opl3->sys_timer_lock);
opl3->sys_timer_status = 0;
-#ifdef CONFIG_SND_SEQUENCER_OSS
+#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
snd_opl3_init_seq_oss(opl3, name);
#endif
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);
if (opl3 == NULL)
return -EINVAL;
-#ifdef CONFIG_SND_SEQUENCER_OSS
+#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
snd_opl3_free_seq_oss(opl3);
#endif
if (opl3->seq_client >= 0) {
@@ -276,22 +263,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/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c
index 301acb6b9cf9..10f622b439a0 100644
--- a/sound/drivers/opl3/opl3_synth.c
+++ b/sound/drivers/opl3/opl3_synth.c
@@ -1,29 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Uros Bizjak <uros@kss-loka.si>
*
* Routines for OPL2/OPL3/OPL4 control
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/nospec.h>
#include <sound/opl3.h>
#include <sound/asound_fm.h>
+#include "opl3_voice.h"
-#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
#define OPL3_SUPPORT_SYNTH
#endif
@@ -102,6 +91,8 @@ int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file,
{
struct snd_dm_fm_info info;
+ memset(&info, 0, sizeof(info));
+
info.fm_mode = opl3->fm_mode;
info.rhythm = opl3->rhythm;
if (copy_to_user(argp, &info, sizeof(struct snd_dm_fm_info)))
@@ -167,10 +158,8 @@ int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file,
return 0;
#endif
-#ifdef CONFIG_SND_DEBUG
default:
- snd_printk(KERN_WARNING "unknown IOCTL: 0x%x\n", cmd);
-#endif
+ dev_dbg(opl3->card->dev, "unknown IOCTL: 0x%x\n", cmd);
}
return -ENOTTY;
}
@@ -299,7 +288,7 @@ int snd_opl3_load_patch(struct snd_opl3 *opl3,
}
if (name)
- strlcpy(patch->name, name, sizeof(patch->name));
+ strscpy(patch->name, name, sizeof(patch->name));
return 0;
}
@@ -447,7 +436,7 @@ static int snd_opl3_set_voice(struct snd_opl3 * opl3, struct snd_dm_fm_voice * v
{
unsigned short reg_side;
unsigned char op_offset;
- unsigned char voice_offset;
+ unsigned char voice_offset, voice_op;
unsigned short opl3_reg;
unsigned char reg_val;
@@ -472,7 +461,9 @@ static int snd_opl3_set_voice(struct snd_opl3 * opl3, struct snd_dm_fm_voice * v
voice_offset = voice->voice - MAX_OPL2_VOICES;
}
/* Get register offset of operator */
- op_offset = snd_opl3_regmap[voice_offset][voice->op];
+ voice_offset = array_index_nospec(voice_offset, MAX_OPL2_VOICES);
+ voice_op = array_index_nospec(voice->op, 4);
+ op_offset = snd_opl3_regmap[voice_offset][voice_op];
reg_val = 0x00;
/* Set amplitude modulation (tremolo) effect */
diff --git a/sound/drivers/opl3/opl3_voice.h b/sound/drivers/opl3/opl3_voice.h
index a371c075ac87..be9ccca2d952 100644
--- a/sound/drivers/opl3/opl3_voice.h
+++ b/sound/drivers/opl3/opl3_voice.h
@@ -1,22 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __OPL3_VOICE_H
#define __OPL3_VOICE_H
/*
* Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sound/opl3.h>
@@ -37,16 +24,23 @@ void snd_opl3_nrpn(void *p, struct snd_midi_channel *chan, struct snd_midi_chann
void snd_opl3_sysex(void *p, unsigned char *buf, int len, int parsed, struct snd_midi_channel_set *chset);
void snd_opl3_calc_volume(unsigned char *reg, int vel, struct snd_midi_channel *chan);
-void snd_opl3_timer_func(unsigned long data);
+void snd_opl3_timer_func(struct timer_list *t);
/* Prototypes for opl3_drums.c */
void snd_opl3_load_drums(struct snd_opl3 *opl3);
-void snd_opl3_drum_switch(struct snd_opl3 *opl3, int note, int on_off, int vel, struct snd_midi_channel *chan);
+void snd_opl3_drum_switch(struct snd_opl3 *opl3, int note, int vel, int on_off, struct snd_midi_channel *chan);
/* Prototypes for opl3_oss.c */
-#ifdef CONFIG_SND_SEQUENCER_OSS
+#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
void snd_opl3_init_seq_oss(struct snd_opl3 *opl3, char *name);
void snd_opl3_free_seq_oss(struct snd_opl3 *opl3);
+#else
+#define snd_opl3_init_seq_oss(opl3, name) /* NOP */
+#define snd_opl3_free_seq_oss(opl3) /* NOP */
#endif
+extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];
+extern bool use_internal_drums;
+extern const struct snd_midi_op opl3_ops;
+
#endif
diff --git a/sound/drivers/opl4/Makefile b/sound/drivers/opl4/Makefile
index b94009b0b19f..a841630b45c2 100644
--- a/sound/drivers/opl4/Makefile
+++ b/sound/drivers/opl4/Makefile
@@ -1,10 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-opl4-lib-objs := opl4_lib.o opl4_mixer.o opl4_proc.o
-snd-opl4-synth-objs := opl4_seq.o opl4_synth.o yrw801.o
+snd-opl4-lib-y := opl4_lib.o opl4_mixer.o
+snd-opl4-lib-$(CONFIG_SND_PROC_FS) += opl4_proc.o
+snd-opl4-synth-y := opl4_seq.o opl4_synth.o yrw801.o
obj-$(CONFIG_SND_OPL4_LIB) += snd-opl4-lib.o
obj-$(CONFIG_SND_OPL4_LIB_SEQ) += snd-opl4-synth.o
diff --git a/sound/drivers/opl4/opl4_lib.c b/sound/drivers/opl4/opl4_lib.c
index f07e38da59b8..44fbc6bf0654 100644
--- a/sound/drivers/opl4/opl4_lib.c
+++ b/sound/drivers/opl4/opl4_lib.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Functions for accessing OPL4 devices
* Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "opl4_local.h"
@@ -22,13 +9,14 @@
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/init.h>
-#include <asm/io.h>
+#include <linux/module.h>
+#include <linux/io.h>
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("OPL4 driver");
MODULE_LICENSE("GPL");
-static void inline snd_opl4_wait(struct snd_opl4 *opl4)
+static inline void snd_opl4_wait(struct snd_opl4 *opl4)
{
int timeout = 10;
while ((inb(opl4->fm_port) & OPL4_STATUS_BUSY) && --timeout > 0)
@@ -59,10 +47,9 @@ EXPORT_SYMBOL(snd_opl4_read);
void snd_opl4_read_memory(struct snd_opl4 *opl4, char *buf, int offset, int size)
{
- unsigned long flags;
u8 memcfg;
- spin_lock_irqsave(&opl4->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl4->reg_lock);
memcfg = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION);
snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg | OPL4_MODE_BIT);
@@ -77,18 +64,15 @@ void snd_opl4_read_memory(struct snd_opl4 *opl4, char *buf, int offset, int size
insb(opl4->pcm_port + 1, buf, size);
snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg);
-
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
EXPORT_SYMBOL(snd_opl4_read_memory);
void snd_opl4_write_memory(struct snd_opl4 *opl4, const char *buf, int offset, int size)
{
- unsigned long flags;
u8 memcfg;
- spin_lock_irqsave(&opl4->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl4->reg_lock);
memcfg = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION);
snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg | OPL4_MODE_BIT);
@@ -103,8 +87,6 @@ void snd_opl4_write_memory(struct snd_opl4 *opl4, const char *buf, int offset, i
outsb(opl4->pcm_port + 1, buf, size);
snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg);
-
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
EXPORT_SYMBOL(snd_opl4_write_memory);
@@ -126,7 +108,7 @@ static int snd_opl4_detect(struct snd_opl4 *opl4)
snd_opl4_enable_opl4(opl4);
id1 = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION);
- snd_printdd("OPL4[02]=%02x\n", id1);
+ dev_dbg(opl4->card->dev, "OPL4[02]=%02x\n", id1);
switch (id1 & OPL4_DEVICE_ID_MASK) {
case 0x20:
opl4->hardware = OPL3_HW_OPL4;
@@ -142,7 +124,7 @@ static int snd_opl4_detect(struct snd_opl4 *opl4)
snd_opl4_write(opl4, OPL4_REG_MIX_CONTROL_PCM, 0xff);
id1 = snd_opl4_read(opl4, OPL4_REG_MIX_CONTROL_FM);
id2 = snd_opl4_read(opl4, OPL4_REG_MIX_CONTROL_PCM);
- snd_printdd("OPL4 id1=%02x id2=%02x\n", id1, id2);
+ dev_dbg(opl4->card->dev, "OPL4 id1=%02x id2=%02x\n", id1, id2);
if (id1 != 0x00 || id2 != 0xff)
return -ENODEV;
@@ -152,7 +134,7 @@ static int snd_opl4_detect(struct snd_opl4 *opl4)
return 0;
}
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
static void snd_opl4_seq_dev_free(struct snd_seq_device *seq_dev)
{
struct snd_opl4 *opl4 = seq_dev->private_data;
@@ -164,7 +146,7 @@ static int snd_opl4_create_seq_dev(struct snd_opl4 *opl4, int seq_device)
opl4->seq_dev_num = seq_device;
if (snd_seq_device_new(opl4->card, seq_device, SNDRV_SEQ_DEV_ID_OPL4,
sizeof(struct snd_opl4 *), &opl4->seq_dev) >= 0) {
- strcpy(opl4->seq_dev->name, "OPL4 Wavetable");
+ strscpy(opl4->seq_dev->name, "OPL4 Wavetable");
*(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(opl4->seq_dev) = opl4;
opl4->seq_dev->private_data = opl4;
opl4->seq_dev->private_free = snd_opl4_seq_dev_free;
@@ -175,9 +157,7 @@ static int snd_opl4_create_seq_dev(struct snd_opl4 *opl4, int seq_device)
static void snd_opl4_free(struct snd_opl4 *opl4)
{
-#ifdef CONFIG_PROC_FS
snd_opl4_free_proc(opl4);
-#endif
release_and_free_resource(opl4->res_fm_port);
release_and_free_resource(opl4->res_pcm_port);
kfree(opl4);
@@ -198,7 +178,7 @@ int snd_opl4_create(struct snd_card *card,
struct snd_opl4 *opl4;
struct snd_opl3 *opl3;
int err;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_opl4_dev_free
};
@@ -214,7 +194,7 @@ int snd_opl4_create(struct snd_card *card,
opl4->res_fm_port = request_region(fm_port, 8, "OPL4 FM");
opl4->res_pcm_port = request_region(pcm_port, 8, "OPL4 PCM/MIX");
if (!opl4->res_fm_port || !opl4->res_pcm_port) {
- snd_printk(KERN_ERR "opl4: can't grab ports 0x%lx, 0x%lx\n", fm_port, pcm_port);
+ dev_err(card->dev, "opl4: can't grab ports 0x%lx, 0x%lx\n", fm_port, pcm_port);
snd_opl4_free(opl4);
return -EBUSY;
}
@@ -228,7 +208,7 @@ int snd_opl4_create(struct snd_card *card,
err = snd_opl4_detect(opl4);
if (err < 0) {
snd_opl4_free(opl4);
- snd_printd("OPL4 chip not detected at %#lx/%#lx\n", fm_port, pcm_port);
+ dev_dbg(card->dev, "OPL4 chip not detected at %#lx/%#lx\n", fm_port, pcm_port);
return err;
}
@@ -248,11 +228,9 @@ int snd_opl4_create(struct snd_card *card,
snd_opl4_enable_opl4(opl4);
snd_opl4_create_mixer(opl4);
-#ifdef CONFIG_PROC_FS
snd_opl4_create_proc(opl4);
-#endif
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
opl4->seq_client = -1;
if (opl4->hardware < OPL3_HW_OPL4_ML)
snd_opl4_create_seq_dev(opl4, seq_device);
@@ -266,15 +244,3 @@ int snd_opl4_create(struct snd_card *card,
}
EXPORT_SYMBOL(snd_opl4_create);
-
-static int __init alsa_opl4_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_opl4_exit(void)
-{
-}
-
-module_init(alsa_opl4_init)
-module_exit(alsa_opl4_exit)
diff --git a/sound/drivers/opl4/opl4_local.h b/sound/drivers/opl4/opl4_local.h
index 470e5a758a02..a16b4677c1e9 100644
--- a/sound/drivers/opl4/opl4_local.h
+++ b/sound/drivers/opl4/opl4_local.h
@@ -178,13 +178,13 @@ struct snd_opl4 {
spinlock_t reg_lock;
struct snd_card *card;
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
struct snd_info_entry *proc_entry;
int memory_access;
#endif
struct mutex access_mutex;
-#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
int used;
int seq_dev_num;
@@ -207,10 +207,13 @@ void snd_opl4_write_memory(struct snd_opl4 *opl4, const char *buf, int offset, i
/* opl4_mixer.c */
int snd_opl4_create_mixer(struct snd_opl4 *opl4);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/* opl4_proc.c */
int snd_opl4_create_proc(struct snd_opl4 *opl4);
void snd_opl4_free_proc(struct snd_opl4 *opl4);
+#else
+static inline int snd_opl4_create_proc(struct snd_opl4 *opl4) { return 0; }
+static inline void snd_opl4_free_proc(struct snd_opl4 *opl4) {}
#endif
/* opl4_seq.c */
diff --git a/sound/drivers/opl4/opl4_mixer.c b/sound/drivers/opl4/opl4_mixer.c
index 04079de4c35f..deebb8636437 100644
--- a/sound/drivers/opl4/opl4_mixer.c
+++ b/sound/drivers/opl4/opl4_mixer.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OPL4 mixer functions
* Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "opl4_local.h"
@@ -32,13 +19,11 @@ static int snd_opl4_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
static int snd_opl4_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_opl4 *opl4 = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
u8 reg = kcontrol->private_value;
u8 value;
- spin_lock_irqsave(&opl4->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl4->reg_lock);
value = snd_opl4_read(opl4, reg);
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
ucontrol->value.integer.value[0] = 7 - (value & 7);
ucontrol->value.integer.value[1] = 7 - ((value >> 3) & 7);
return 0;
@@ -47,20 +32,18 @@ static int snd_opl4_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
static int snd_opl4_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_opl4 *opl4 = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
u8 reg = kcontrol->private_value;
u8 value, old_value;
value = (7 - (ucontrol->value.integer.value[0] & 7)) |
((7 - (ucontrol->value.integer.value[1] & 7)) << 3);
- spin_lock_irqsave(&opl4->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl4->reg_lock);
old_value = snd_opl4_read(opl4, reg);
snd_opl4_write(opl4, reg, value);
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
return value != old_value;
}
-static struct snd_kcontrol_new snd_opl4_controls[] = {
+static const struct snd_kcontrol_new snd_opl4_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "FM Playback Volume",
diff --git a/sound/drivers/opl4/opl4_proc.c b/sound/drivers/opl4/opl4_proc.c
index df850b8830a5..fd0ba4704d9f 100644
--- a/sound/drivers/opl4/opl4_proc.c
+++ b/sound/drivers/opl4/opl4_proc.c
@@ -1,40 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Functions for the OPL4 proc file
* Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "opl4_local.h"
#include <linux/vmalloc.h>
+#include <linux/export.h>
#include <sound/info.h>
-#ifdef CONFIG_PROC_FS
-
static int snd_opl4_mem_proc_open(struct snd_info_entry *entry,
unsigned short mode, void **file_private_data)
{
struct snd_opl4 *opl4 = entry->private_data;
- mutex_lock(&opl4->access_mutex);
- if (opl4->memory_access) {
- mutex_unlock(&opl4->access_mutex);
+ guard(mutex)(&opl4->access_mutex);
+ if (opl4->memory_access)
return -EBUSY;
- }
opl4->memory_access++;
- mutex_unlock(&opl4->access_mutex);
return 0;
}
@@ -43,9 +26,8 @@ static int snd_opl4_mem_proc_release(struct snd_info_entry *entry,
{
struct snd_opl4 *opl4 = entry->private_data;
- mutex_lock(&opl4->access_mutex);
+ guard(mutex)(&opl4->access_mutex);
opl4->memory_access--;
- mutex_unlock(&opl4->access_mutex);
return 0;
}
@@ -90,7 +72,7 @@ static ssize_t snd_opl4_mem_proc_write(struct snd_info_entry *entry,
return count;
}
-static struct snd_info_entry_ops snd_opl4_mem_proc_ops = {
+static const struct snd_info_entry_ops snd_opl4_mem_proc_ops = {
.open = snd_opl4_mem_proc_open,
.release = snd_opl4_mem_proc_release,
.read = snd_opl4_mem_proc_read,
@@ -105,7 +87,7 @@ int snd_opl4_create_proc(struct snd_opl4 *opl4)
if (entry) {
if (opl4->hardware < OPL3_HW_OPL4_ML) {
/* OPL4 can access 4 MB external ROM/SRAM */
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
entry->size = 4 * 1024 * 1024;
} else {
/* OPL4-ML has 1 MB internal ROM */
@@ -115,10 +97,6 @@ int snd_opl4_create_proc(struct snd_opl4 *opl4)
entry->c.ops = &snd_opl4_mem_proc_ops;
entry->module = THIS_MODULE;
entry->private_data = opl4;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
}
opl4->proc_entry = entry;
return 0;
@@ -128,5 +106,3 @@ void snd_opl4_free_proc(struct snd_opl4 *opl4)
{
snd_info_free_entry(opl4->proc_entry);
}
-
-#endif /* CONFIG_PROC_FS */
diff --git a/sound/drivers/opl4/opl4_seq.c b/sound/drivers/opl4/opl4_seq.c
index 43d8a2bdd280..7bb22089a093 100644
--- a/sound/drivers/opl4/opl4_seq.c
+++ b/sound/drivers/opl4/opl4_seq.c
@@ -34,6 +34,7 @@
#include "opl4_local.h"
#include <linux/init.h>
#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/initval.h>
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
@@ -62,24 +63,18 @@ static int snd_opl4_seq_use(void *private_data, struct snd_seq_port_subscribe *i
struct snd_opl4 *opl4 = private_data;
int err;
- mutex_lock(&opl4->access_mutex);
+ scoped_guard(mutex, &opl4->access_mutex) {
+ if (opl4->used)
+ return -EBUSY;
+ opl4->used++;
- if (opl4->used) {
- mutex_unlock(&opl4->access_mutex);
- return -EBUSY;
- }
- opl4->used++;
-
- if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) {
- err = snd_opl4_seq_use_inc(opl4);
- if (err < 0) {
- mutex_unlock(&opl4->access_mutex);
- return err;
+ if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) {
+ err = snd_opl4_seq_use_inc(opl4);
+ if (err < 0)
+ return err;
}
}
- mutex_unlock(&opl4->access_mutex);
-
snd_opl4_synth_reset(opl4);
return 0;
}
@@ -90,16 +85,16 @@ static int snd_opl4_seq_unuse(void *private_data, struct snd_seq_port_subscribe
snd_opl4_synth_shutdown(opl4);
- mutex_lock(&opl4->access_mutex);
- opl4->used--;
- mutex_unlock(&opl4->access_mutex);
+ scoped_guard(mutex, &opl4->access_mutex) {
+ opl4->used--;
+ }
if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM)
snd_opl4_seq_use_dec(opl4);
return 0;
}
-static struct snd_midi_op opl4_ops = {
+static const struct snd_midi_op opl4_ops = {
.note_on = snd_opl4_note_on,
.note_off = snd_opl4_note_off,
.note_terminate = snd_opl4_terminate_note,
@@ -123,8 +118,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;
@@ -179,8 +175,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);
@@ -194,21 +191,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/drivers/opl4/opl4_synth.c b/sound/drivers/opl4/opl4_synth.c
index 49b9e240915c..82dbb8519ab1 100644
--- a/sound/drivers/opl4/opl4_synth.c
+++ b/sound/drivers/opl4/opl4_synth.c
@@ -33,7 +33,7 @@
#include "opl4_local.h"
#include <linux/delay.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <sound/asoundef.h>
/* GM2 controllers */
@@ -248,7 +248,7 @@ static const s16 snd_opl4_pitch_map[0x600] = {
* Attenuation according to GM recommendations, in -0.375 dB units.
* table[v] = 40 * log(v / 127) / -0.375
*/
-static unsigned char snd_opl4_volume_table[128] = {
+static const unsigned char snd_opl4_volume_table[128] = {
255,224,192,173,160,150,141,134,
128,122,117,113,109,105,102, 99,
96, 93, 90, 88, 85, 83, 81, 79,
@@ -272,13 +272,12 @@ static unsigned char snd_opl4_volume_table[128] = {
*/
void snd_opl4_synth_reset(struct snd_opl4 *opl4)
{
- unsigned long flags;
int i;
- spin_lock_irqsave(&opl4->reg_lock, flags);
- for (i = 0; i < OPL4_MAX_VOICES; i++)
- snd_opl4_write(opl4, OPL4_REG_MISC + i, OPL4_DAMP_BIT);
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &opl4->reg_lock) {
+ for (i = 0; i < OPL4_MAX_VOICES; i++)
+ snd_opl4_write(opl4, OPL4_REG_MISC + i, OPL4_DAMP_BIT);
+ }
INIT_LIST_HEAD(&opl4->off_voices);
INIT_LIST_HEAD(&opl4->on_voices);
@@ -296,14 +295,12 @@ void snd_opl4_synth_reset(struct snd_opl4 *opl4)
*/
void snd_opl4_synth_shutdown(struct snd_opl4 *opl4)
{
- unsigned long flags;
int i;
- spin_lock_irqsave(&opl4->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl4->reg_lock);
for (i = 0; i < OPL4_MAX_VOICES; i++)
snd_opl4_write(opl4, OPL4_REG_MISC + i,
opl4->voices[i].reg_misc & ~OPL4_KEY_ON_BIT);
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
/*
@@ -313,17 +310,15 @@ static void snd_opl4_do_for_note(struct snd_opl4 *opl4, int note, struct snd_mid
void (*func)(struct snd_opl4 *opl4, struct opl4_voice *voice))
{
int i;
- unsigned long flags;
struct opl4_voice *voice;
- spin_lock_irqsave(&opl4->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl4->reg_lock);
for (i = 0; i < OPL4_MAX_VOICES; i++) {
voice = &opl4->voices[i];
if (voice->chan == chan && voice->note == note) {
func(opl4, voice);
}
}
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
/*
@@ -334,17 +329,15 @@ static void snd_opl4_do_for_channel(struct snd_opl4 *opl4,
void (*func)(struct snd_opl4 *opl4, struct opl4_voice *voice))
{
int i;
- unsigned long flags;
struct opl4_voice *voice;
- spin_lock_irqsave(&opl4->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl4->reg_lock);
for (i = 0; i < OPL4_MAX_VOICES; i++) {
voice = &opl4->voices[i];
if (voice->chan == chan) {
func(opl4, voice);
}
}
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
/*
@@ -354,16 +347,14 @@ static void snd_opl4_do_for_all(struct snd_opl4 *opl4,
void (*func)(struct snd_opl4 *opl4, struct opl4_voice *voice))
{
int i;
- unsigned long flags;
struct opl4_voice *voice;
- spin_lock_irqsave(&opl4->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl4->reg_lock);
for (i = 0; i < OPL4_MAX_VOICES; i++) {
voice = &opl4->voices[i];
if (voice->chan)
func(opl4, voice);
}
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
static void snd_opl4_update_volume(struct snd_opl4 *opl4, struct opl4_voice *voice)
@@ -486,7 +477,6 @@ void snd_opl4_note_on(void *private_data, int note, int vel, struct snd_midi_cha
struct opl4_voice *voice[2];
const struct opl4_sound *sound[2];
int voices = 0, i;
- unsigned long flags;
/* determine the number of voices and voice parameters */
i = chan->drum_channel ? 0x80 : (chan->midi_program & 0x7f);
@@ -501,42 +491,41 @@ void snd_opl4_note_on(void *private_data, int note, int vel, struct snd_midi_cha
}
/* allocate and initialize the needed voices */
- spin_lock_irqsave(&opl4->reg_lock, flags);
- for (i = 0; i < voices; i++) {
- voice[i] = snd_opl4_get_voice(opl4);
- list_del(&voice[i]->list);
- list_add_tail(&voice[i]->list, &opl4->on_voices);
- voice[i]->chan = chan;
- voice[i]->note = note;
- voice[i]->velocity = vel & 0x7f;
- voice[i]->sound = sound[i];
- }
+ scoped_guard(spinlock_irqsave, &opl4->reg_lock) {
+ for (i = 0; i < voices; i++) {
+ voice[i] = snd_opl4_get_voice(opl4);
+ list_move_tail(&voice[i]->list, &opl4->on_voices);
+ voice[i]->chan = chan;
+ voice[i]->note = note;
+ voice[i]->velocity = vel & 0x7f;
+ voice[i]->sound = sound[i];
+ }
- /* set tone number (triggers header loading) */
- for (i = 0; i < voices; i++) {
- voice[i]->reg_f_number =
- (sound[i]->tone >> 8) & OPL4_TONE_NUMBER_BIT8;
- snd_opl4_write(opl4, OPL4_REG_F_NUMBER + voice[i]->number,
- voice[i]->reg_f_number);
- snd_opl4_write(opl4, OPL4_REG_TONE_NUMBER + voice[i]->number,
- sound[i]->tone & 0xff);
- }
+ /* set tone number (triggers header loading) */
+ for (i = 0; i < voices; i++) {
+ voice[i]->reg_f_number =
+ (sound[i]->tone >> 8) & OPL4_TONE_NUMBER_BIT8;
+ snd_opl4_write(opl4, OPL4_REG_F_NUMBER + voice[i]->number,
+ voice[i]->reg_f_number);
+ snd_opl4_write(opl4, OPL4_REG_TONE_NUMBER + voice[i]->number,
+ sound[i]->tone & 0xff);
+ }
- /* set parameters which can be set while loading */
- for (i = 0; i < voices; i++) {
- voice[i]->reg_misc = OPL4_LFO_RESET_BIT;
- snd_opl4_update_pan(opl4, voice[i]);
- snd_opl4_update_pitch(opl4, voice[i]);
- voice[i]->level_direct = OPL4_LEVEL_DIRECT_BIT;
- snd_opl4_update_volume(opl4, voice[i]);
+ /* set parameters which can be set while loading */
+ for (i = 0; i < voices; i++) {
+ voice[i]->reg_misc = OPL4_LFO_RESET_BIT;
+ snd_opl4_update_pan(opl4, voice[i]);
+ snd_opl4_update_pitch(opl4, voice[i]);
+ voice[i]->level_direct = OPL4_LEVEL_DIRECT_BIT;
+ snd_opl4_update_volume(opl4, voice[i]);
+ }
}
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
/* wait for completion of loading */
snd_opl4_wait_for_wave_headers(opl4);
/* set remaining parameters */
- spin_lock_irqsave(&opl4->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl4->reg_lock);
for (i = 0; i < voices; i++) {
snd_opl4_update_tone_parameters(opl4, voice[i]);
voice[i]->reg_lfo_vibrato = voice[i]->sound->reg_lfo_vibrato;
@@ -550,13 +539,11 @@ void snd_opl4_note_on(void *private_data, int note, int vel, struct snd_midi_cha
snd_opl4_write(opl4, OPL4_REG_MISC + voice[i]->number,
voice[i]->reg_misc);
}
- spin_unlock_irqrestore(&opl4->reg_lock, flags);
}
static void snd_opl4_voice_off(struct snd_opl4 *opl4, struct opl4_voice *voice)
{
- list_del(&voice->list);
- list_add_tail(&voice->list, &opl4->off_voices);
+ list_move_tail(&voice->list, &opl4->off_voices);
voice->reg_misc &= ~OPL4_KEY_ON_BIT;
snd_opl4_write(opl4, OPL4_REG_MISC + voice->number, voice->reg_misc);
@@ -571,8 +558,7 @@ void snd_opl4_note_off(void *private_data, int note, int vel, struct snd_midi_ch
static void snd_opl4_terminate_voice(struct snd_opl4 *opl4, struct opl4_voice *voice)
{
- list_del(&voice->list);
- list_add_tail(&voice->list, &opl4->off_voices);
+ list_move_tail(&voice->list, &opl4->off_voices);
voice->reg_misc = (voice->reg_misc & ~OPL4_KEY_ON_BIT) | OPL4_DAMP_BIT;
snd_opl4_write(opl4, OPL4_REG_MISC + voice->number, voice->reg_misc);
diff --git a/sound/drivers/opl4/yrw801.c b/sound/drivers/opl4/yrw801.c
index 6c335492d082..9e464b84b905 100644
--- a/sound/drivers/opl4/yrw801.c
+++ b/sound/drivers/opl4/yrw801.c
@@ -43,7 +43,7 @@ int snd_yrw801_detect(struct snd_opl4 *opl4)
snd_opl4_read_memory(opl4, buf, 0x1ffffe, 2);
if (buf[0] != 0x01)
return -ENODEV;
- snd_printdd("YRW801 ROM version %02x.%02x\n", buf[0], buf[1]);
+ dev_dbg(opl4->card->dev, "YRW801 ROM version %02x.%02x\n", buf[0], buf[1]);
return 0;
}
diff --git a/sound/drivers/pcm-indirect2.c b/sound/drivers/pcm-indirect2.c
deleted file mode 100644
index 3c93c23e4883..000000000000
--- a/sound/drivers/pcm-indirect2.c
+++ /dev/null
@@ -1,573 +0,0 @@
-/*
- * Helper functions for indirect PCM data transfer to a simple FIFO in
- * hardware (small, no possibility to read "hardware io position",
- * updating position done by interrupt, ...)
- *
- * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de>
- *
- * Based on "pcm-indirect.h" (alsa-driver-1.0.13) by
- *
- * Copyright (c) by Takashi Iwai <tiwai@suse.de>
- * Jaroslav Kysela <perex@suse.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/* snd_printk/d() */
-#include <sound/core.h>
-/* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t
- * snd_pcm_period_elapsed() */
-#include <sound/pcm.h>
-
-#include "pcm-indirect2.h"
-
-#ifdef SND_PCM_INDIRECT2_STAT
-/* jiffies */
-#include <linux/jiffies.h>
-
-void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int i;
- int j;
- int k;
- int seconds = (rec->lastbytetime - rec->firstbytetime) / HZ;
-
- snd_printk(KERN_DEBUG "STAT: mul_elapsed: %u, mul_elapsed_real: %d, "
- "irq_occured: %d\n",
- rec->mul_elapsed, rec->mul_elapsed_real, rec->irq_occured);
- snd_printk(KERN_DEBUG "STAT: min_multiple: %d (irqs/period)\n",
- rec->min_multiple);
- snd_printk(KERN_DEBUG "STAT: firstbytetime: %lu, lastbytetime: %lu, "
- "firstzerotime: %lu\n",
- rec->firstbytetime, rec->lastbytetime, rec->firstzerotime);
- snd_printk(KERN_DEBUG "STAT: bytes2hw: %u Bytes => (by runtime->rate) "
- "length: %d s\n",
- rec->bytes2hw, rec->bytes2hw / 2 / 2 / runtime->rate);
- snd_printk(KERN_DEBUG "STAT: (by measurement) length: %d => "
- "rate: %d Bytes/s = %d Frames/s|Hz\n",
- seconds, rec->bytes2hw / seconds,
- rec->bytes2hw / 2 / 2 / seconds);
- snd_printk(KERN_DEBUG
- "STAT: zeros2hw: %u = %d ms ~ %d * %d zero copies\n",
- rec->zeros2hw, ((rec->zeros2hw / 2 / 2) * 1000) /
- runtime->rate,
- rec->zeros2hw / (rec->hw_buffer_size / 2),
- (rec->hw_buffer_size / 2));
- snd_printk(KERN_DEBUG "STAT: pointer_calls: %u, lastdifftime: %u\n",
- rec->pointer_calls, rec->lastdifftime);
- snd_printk(KERN_DEBUG "STAT: sw_io: %d, sw_data: %d\n", rec->sw_io,
- rec->sw_data);
- snd_printk(KERN_DEBUG "STAT: byte_sizes[]:\n");
- k = 0;
- for (j = 0; j < 8; j++) {
- for (i = j * 8; i < (j + 1) * 8; i++)
- if (rec->byte_sizes[i] != 0) {
- snd_printk(KERN_DEBUG "%u: %u",
- i, rec->byte_sizes[i]);
- k++;
- }
- if (((k % 8) == 0) && (k != 0)) {
- snd_printk(KERN_DEBUG "\n");
- k = 0;
- }
- }
- snd_printk(KERN_DEBUG "\n");
- snd_printk(KERN_DEBUG "STAT: zero_sizes[]:\n");
- for (j = 0; j < 8; j++) {
- k = 0;
- for (i = j * 8; i < (j + 1) * 8; i++)
- if (rec->zero_sizes[i] != 0)
- snd_printk(KERN_DEBUG "%u: %u",
- i, rec->zero_sizes[i]);
- else
- k++;
- if (!k)
- snd_printk(KERN_DEBUG "\n");
- }
- snd_printk(KERN_DEBUG "\n");
- snd_printk(KERN_DEBUG "STAT: min_adds[]:\n");
- for (j = 0; j < 8; j++) {
- if (rec->min_adds[j] != 0)
- snd_printk(KERN_DEBUG "%u: %u", j, rec->min_adds[j]);
- }
- snd_printk(KERN_DEBUG "\n");
- snd_printk(KERN_DEBUG "STAT: mul_adds[]:\n");
- for (j = 0; j < 8; j++) {
- if (rec->mul_adds[j] != 0)
- snd_printk(KERN_DEBUG "%u: %u", j, rec->mul_adds[j]);
- }
- snd_printk(KERN_DEBUG "\n");
- snd_printk(KERN_DEBUG
- "STAT: zero_times_saved: %d, zero_times_notsaved: %d\n",
- rec->zero_times_saved, rec->zero_times_notsaved);
- /* snd_printk(KERN_DEBUG "STAT: zero_times[]\n");
- i = 0;
- for (j = 0; j < 3750; j++) {
- if (rec->zero_times[j] != 0) {
- snd_printk(KERN_DEBUG "%u: %u", j, rec->zero_times[j]);
- i++;
- }
- if (((i % 8) == 0) && (i != 0))
- snd_printk(KERN_DEBUG "\n");
- }
- snd_printk(KERN_DEBUG "\n"); */
- return;
-}
-#endif
-
-/*
- * _internal_ helper function for playback/capture transfer function
- */
-static void
-snd_pcm_indirect2_increase_min_periods(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- int isplay, int iscopy,
- unsigned int bytes)
-{
- if (rec->min_periods >= 0) {
- if (iscopy) {
- rec->sw_io += bytes;
- if (rec->sw_io >= rec->sw_buffer_size)
- rec->sw_io -= rec->sw_buffer_size;
- } else if (isplay) {
- /* If application does not write data in multiples of
- * a period, move sw_data to the next correctly aligned
- * position, so that sw_io can converge to it (in the
- * next step).
- */
- if (!rec->check_alignment) {
- if (rec->bytes2hw %
- snd_pcm_lib_period_bytes(substream)) {
- unsigned bytes2hw_aligned =
- (1 +
- (rec->bytes2hw /
- snd_pcm_lib_period_bytes
- (substream))) *
- snd_pcm_lib_period_bytes
- (substream);
- rec->sw_data =
- bytes2hw_aligned %
- rec->sw_buffer_size;
-#ifdef SND_PCM_INDIRECT2_STAT
- snd_printk(KERN_DEBUG
- "STAT: @re-align: aligned "
- "bytes2hw to next period "
- "size boundary: %d "
- "(instead of %d)\n",
- bytes2hw_aligned,
- rec->bytes2hw);
- snd_printk(KERN_DEBUG
- "STAT: @re-align: sw_data "
- "moves to: %d\n",
- rec->sw_data);
-#endif
- }
- rec->check_alignment = 1;
- }
- /* We are at the end and are copying zeros into the
- * fifo.
- * Now, we have to make sure that sw_io is increased
- * until the position of sw_data: Filling the fifo with
- * the first zeros means, the last bytes were played.
- */
- if (rec->sw_io != rec->sw_data) {
- unsigned int diff;
- if (rec->sw_data > rec->sw_io)
- diff = rec->sw_data - rec->sw_io;
- else
- diff = (rec->sw_buffer_size -
- rec->sw_io) +
- rec->sw_data;
- if (bytes >= diff)
- rec->sw_io = rec->sw_data;
- else {
- rec->sw_io += bytes;
- if (rec->sw_io >= rec->sw_buffer_size)
- rec->sw_io -=
- rec->sw_buffer_size;
- }
- }
- }
- rec->min_period_count += bytes;
- if (rec->min_period_count >= (rec->hw_buffer_size / 2)) {
- rec->min_periods += (rec->min_period_count /
- (rec->hw_buffer_size / 2));
-#ifdef SND_PCM_INDIRECT2_STAT
- if ((rec->min_period_count /
- (rec->hw_buffer_size / 2)) > 7)
- snd_printk(KERN_DEBUG
- "STAT: more than 7 (%d) min_adds "
- "at once - too big to save!\n",
- (rec->min_period_count /
- (rec->hw_buffer_size / 2)));
- else
- rec->min_adds[(rec->min_period_count /
- (rec->hw_buffer_size / 2))]++;
-#endif
- rec->min_period_count = (rec->min_period_count %
- (rec->hw_buffer_size / 2));
- }
- } else if (isplay && iscopy)
- rec->min_periods = 0;
-}
-
-/*
- * helper function for playback/capture pointer callback
- */
-snd_pcm_uframes_t
-snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec)
-{
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->pointer_calls++;
-#endif
- return bytes_to_frames(substream->runtime, rec->sw_io);
-}
-
-/*
- * _internal_ helper function for playback interrupt callback
- */
-static void
-snd_pcm_indirect2_playback_transfer(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- snd_pcm_indirect2_copy_t copy,
- snd_pcm_indirect2_zero_t zero)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
-
- /* runtime->control->appl_ptr: position where ALSA will write next time
- * rec->appl_ptr: position where ALSA was last time
- * diff: obviously ALSA wrote that much bytes into the intermediate
- * buffer since we checked last time
- */
- snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
-
- if (diff) {
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->lastdifftime = jiffies;
-#endif
- if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
- diff += runtime->boundary;
- /* number of bytes "added" by ALSA increases the number of
- * bytes which are ready to "be transfered to HW"/"played"
- * Then, set rec->appl_ptr to not count bytes twice next time.
- */
- rec->sw_ready += (int)frames_to_bytes(runtime, diff);
- rec->appl_ptr = appl_ptr;
- }
- if (rec->hw_ready && (rec->sw_ready <= 0)) {
- unsigned int bytes;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if (rec->firstzerotime == 0) {
- rec->firstzerotime = jiffies;
- snd_printk(KERN_DEBUG
- "STAT: @firstzerotime: mul_elapsed: %d, "
- "min_period_count: %d\n",
- rec->mul_elapsed, rec->min_period_count);
- snd_printk(KERN_DEBUG
- "STAT: @firstzerotime: sw_io: %d, "
- "sw_data: %d, appl_ptr: %u\n",
- rec->sw_io, rec->sw_data,
- (unsigned int)appl_ptr);
- }
- if ((jiffies - rec->firstzerotime) < 3750) {
- rec->zero_times[(jiffies - rec->firstzerotime)]++;
- rec->zero_times_saved++;
- } else
- rec->zero_times_notsaved++;
-#endif
- bytes = zero(substream, rec);
-
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->zeros2hw += bytes;
- if (bytes < 64)
- rec->zero_sizes[bytes]++;
- else
- snd_printk(KERN_DEBUG
- "STAT: %d zero Bytes copied to hardware at "
- "once - too big to save!\n",
- bytes);
-#endif
- snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 0,
- bytes);
- return;
- }
- while (rec->hw_ready && (rec->sw_ready > 0)) {
- /* sw_to_end: max. number of bytes that can be read/take from
- * the current position (sw_data) in _one_ step
- */
- unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
-
- /* bytes: number of bytes we have available (for reading) */
- unsigned int bytes = rec->sw_ready;
-
- if (sw_to_end < bytes)
- bytes = sw_to_end;
- if (!bytes)
- break;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if (rec->firstbytetime == 0)
- rec->firstbytetime = jiffies;
- rec->lastbytetime = jiffies;
-#endif
- /* copy bytes from intermediate buffer position sw_data to the
- * HW and return number of bytes actually written
- * Furthermore, set hw_ready to 0, if the fifo isn't empty
- * now => more could be transfered to fifo
- */
- bytes = copy(substream, rec, bytes);
- rec->bytes2hw += bytes;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if (bytes < 64)
- rec->byte_sizes[bytes]++;
- else
- snd_printk(KERN_DEBUG
- "STAT: %d Bytes copied to hardware at once "
- "- too big to save!\n",
- bytes);
-#endif
- /* increase sw_data by the number of actually written bytes
- * (= number of taken bytes from intermediate buffer)
- */
- rec->sw_data += bytes;
- if (rec->sw_data == rec->sw_buffer_size)
- rec->sw_data = 0;
- /* now sw_data is the position where ALSA is going to write
- * in the intermediate buffer next time = position we are going
- * to read from next time
- */
-
- snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 1,
- bytes);
-
- /* we read bytes from intermediate buffer, so we need to say
- * that the number of bytes ready for transfer are decreased
- * now
- */
- rec->sw_ready -= bytes;
- }
- return;
-}
-
-/*
- * helper function for playback interrupt routine
- */
-void
-snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- snd_pcm_indirect2_copy_t copy,
- snd_pcm_indirect2_zero_t zero)
-{
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->irq_occured++;
-#endif
- /* hardware played some bytes, so there is room again (in fifo) */
- rec->hw_ready = 1;
-
- /* don't call ack() now, instead call transfer() function directly
- * (normally called by ack() )
- */
- snd_pcm_indirect2_playback_transfer(substream, rec, copy, zero);
-
- if (rec->min_periods >= rec->min_multiple) {
-#ifdef SND_PCM_INDIRECT2_STAT
- if ((rec->min_periods / rec->min_multiple) > 7)
- snd_printk(KERN_DEBUG
- "STAT: more than 7 (%d) mul_adds - too big "
- "to save!\n",
- (rec->min_periods / rec->min_multiple));
- else
- rec->mul_adds[(rec->min_periods /
- rec->min_multiple)]++;
- rec->mul_elapsed_real += (rec->min_periods /
- rec->min_multiple);
- rec->mul_elapsed++;
-#endif
- rec->min_periods = (rec->min_periods % rec->min_multiple);
- snd_pcm_period_elapsed(substream);
- }
-}
-
-/*
- * _internal_ helper function for capture interrupt callback
- */
-static void
-snd_pcm_indirect2_capture_transfer(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- snd_pcm_indirect2_copy_t copy,
- snd_pcm_indirect2_zero_t null)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
- snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
-
- if (diff) {
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->lastdifftime = jiffies;
-#endif
- if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
- diff += runtime->boundary;
- rec->sw_ready -= frames_to_bytes(runtime, diff);
- rec->appl_ptr = appl_ptr;
- }
- /* if hardware has something, but the intermediate buffer is full
- * => skip contents of buffer
- */
- if (rec->hw_ready && (rec->sw_ready >= (int)rec->sw_buffer_size)) {
- unsigned int bytes;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if (rec->firstzerotime == 0) {
- rec->firstzerotime = jiffies;
- snd_printk(KERN_DEBUG "STAT: (capture) "
- "@firstzerotime: mul_elapsed: %d, "
- "min_period_count: %d\n",
- rec->mul_elapsed, rec->min_period_count);
- snd_printk(KERN_DEBUG "STAT: (capture) "
- "@firstzerotime: sw_io: %d, sw_data: %d, "
- "appl_ptr: %u\n",
- rec->sw_io, rec->sw_data,
- (unsigned int)appl_ptr);
- }
- if ((jiffies - rec->firstzerotime) < 3750) {
- rec->zero_times[(jiffies - rec->firstzerotime)]++;
- rec->zero_times_saved++;
- } else
- rec->zero_times_notsaved++;
-#endif
- bytes = null(substream, rec);
-
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->zeros2hw += bytes;
- if (bytes < 64)
- rec->zero_sizes[bytes]++;
- else
- snd_printk(KERN_DEBUG
- "STAT: (capture) %d zero Bytes copied to "
- "hardware at once - too big to save!\n",
- bytes);
-#endif
- snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 0,
- bytes);
- /* report an overrun */
- rec->sw_io = SNDRV_PCM_POS_XRUN;
- return;
- }
- while (rec->hw_ready && (rec->sw_ready < (int)rec->sw_buffer_size)) {
- /* sw_to_end: max. number of bytes that we can write to the
- * intermediate buffer (until it's end)
- */
- size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
-
- /* bytes: max. number of bytes, which may be copied to the
- * intermediate buffer without overflow (in _one_ step)
- */
- size_t bytes = rec->sw_buffer_size - rec->sw_ready;
-
- /* limit number of bytes (for transfer) by available room in
- * the intermediate buffer
- */
- if (sw_to_end < bytes)
- bytes = sw_to_end;
- if (!bytes)
- break;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if (rec->firstbytetime == 0)
- rec->firstbytetime = jiffies;
- rec->lastbytetime = jiffies;
-#endif
- /* copy bytes from the intermediate buffer (position sw_data)
- * to the HW at most and return number of bytes actually copied
- * from HW
- * Furthermore, set hw_ready to 0, if the fifo is empty now.
- */
- bytes = copy(substream, rec, bytes);
- rec->bytes2hw += bytes;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if (bytes < 64)
- rec->byte_sizes[bytes]++;
- else
- snd_printk(KERN_DEBUG
- "STAT: (capture) %d Bytes copied to "
- "hardware at once - too big to save!\n",
- bytes);
-#endif
- /* increase sw_data by the number of actually copied bytes from
- * HW
- */
- rec->sw_data += bytes;
- if (rec->sw_data == rec->sw_buffer_size)
- rec->sw_data = 0;
-
- snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 1,
- bytes);
-
- /* number of bytes in the intermediate buffer, which haven't
- * been fetched by ALSA yet.
- */
- rec->sw_ready += bytes;
- }
- return;
-}
-
-/*
- * helper function for capture interrupt routine
- */
-void
-snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- snd_pcm_indirect2_copy_t copy,
- snd_pcm_indirect2_zero_t null)
-{
-#ifdef SND_PCM_INDIRECT2_STAT
- rec->irq_occured++;
-#endif
- /* hardware recorded some bytes, so there is something to read from the
- * record fifo:
- */
- rec->hw_ready = 1;
-
- /* don't call ack() now, instead call transfer() function directly
- * (normally called by ack() )
- */
- snd_pcm_indirect2_capture_transfer(substream, rec, copy, null);
-
- if (rec->min_periods >= rec->min_multiple) {
-
-#ifdef SND_PCM_INDIRECT2_STAT
- if ((rec->min_periods / rec->min_multiple) > 7)
- snd_printk(KERN_DEBUG
- "STAT: more than 7 (%d) mul_adds - "
- "too big to save!\n",
- (rec->min_periods / rec->min_multiple));
- else
- rec->mul_adds[(rec->min_periods /
- rec->min_multiple)]++;
- rec->mul_elapsed_real += (rec->min_periods /
- rec->min_multiple);
- rec->mul_elapsed++;
-#endif
- rec->min_periods = (rec->min_periods % rec->min_multiple);
- snd_pcm_period_elapsed(substream);
- }
-}
diff --git a/sound/drivers/pcm-indirect2.h b/sound/drivers/pcm-indirect2.h
deleted file mode 100644
index 2ea6e460f348..000000000000
--- a/sound/drivers/pcm-indirect2.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Helper functions for indirect PCM data transfer to a simple FIFO in
- * hardware (small, no possibility to read "hardware io position",
- * updating position done by interrupt, ...)
- *
- * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de>
- *
- * Based on "pcm-indirect.h" (alsa-driver-1.0.13) by
- *
- * Copyright (c) by Takashi Iwai <tiwai@suse.de>
- * Jaroslav Kysela <perex@suse.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef __SOUND_PCM_INDIRECT2_H
-#define __SOUND_PCM_INDIRECT2_H
-
-/* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t */
-#include <sound/pcm.h>
-
-/* Debug options for code which may be removed completely in a final version */
-#ifdef CONFIG_SND_DEBUG
-#define SND_PCM_INDIRECT2_STAT /* turn on some "statistics" about the
- * process of copying bytes from the
- * intermediate buffer to the hardware
- * fifo and the other way round
- */
-#endif
-
-struct snd_pcm_indirect2 {
- unsigned int hw_buffer_size; /* Byte size of hardware buffer */
- int hw_ready; /* playback: 1 = hw fifo has room left,
- * 0 = hw fifo is full
- */
- unsigned int min_multiple;
- int min_periods; /* counts number of min. periods until
- * min_multiple is reached
- */
- int min_period_count; /* counts bytes to count number of
- * min. periods
- */
-
- unsigned int sw_buffer_size; /* Byte size of software buffer */
-
- /* sw_data: position in intermediate buffer, where we will read (or
- * write) from/to next time (to transfer data to/from HW)
- */
- unsigned int sw_data; /* Offset to next dst (or src) in sw
- * ring buffer
- */
- /* easiest case (playback):
- * sw_data is nearly the same as ~ runtime->control->appl_ptr, with the
- * exception that sw_data is "behind" by the number if bytes ALSA wrote
- * to the intermediate buffer last time.
- * A call to ack() callback synchronizes both indirectly.
- */
-
- /* We have no real sw_io pointer here. Usually sw_io is pointing to the
- * current playback/capture position _inside_ the hardware. Devices
- * with plain FIFOs often have no possibility to publish this position.
- * So we say: if sw_data is updated, that means bytes were copied to
- * the hardware, we increase sw_io by that amount, because there have
- * to be as much bytes which were played. So sw_io will stay behind
- * sw_data all the time and has to converge to sw_data at the end of
- * playback.
- */
- unsigned int sw_io; /* Current software pointer in bytes */
-
- /* sw_ready: number of bytes ALSA copied to the intermediate buffer, so
- * it represents the number of bytes which wait for transfer to the HW
- */
- int sw_ready; /* Bytes ready to be transferred to/from hw */
-
- /* appl_ptr: last known position of ALSA (where ALSA is going to write
- * next time into the intermediate buffer
- */
- snd_pcm_uframes_t appl_ptr; /* Last seen appl_ptr */
-
- unsigned int bytes2hw;
- int check_alignment;
-
-#ifdef SND_PCM_INDIRECT2_STAT
- unsigned int zeros2hw;
- unsigned int mul_elapsed;
- unsigned int mul_elapsed_real;
- unsigned long firstbytetime;
- unsigned long lastbytetime;
- unsigned long firstzerotime;
- unsigned int byte_sizes[64];
- unsigned int zero_sizes[64];
- unsigned int min_adds[8];
- unsigned int mul_adds[8];
- unsigned int zero_times[3750]; /* = 15s */
- unsigned int zero_times_saved;
- unsigned int zero_times_notsaved;
- unsigned int irq_occured;
- unsigned int pointer_calls;
- unsigned int lastdifftime;
-#endif
-};
-
-typedef size_t (*snd_pcm_indirect2_copy_t) (struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- size_t bytes);
-typedef size_t (*snd_pcm_indirect2_zero_t) (struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec);
-
-#ifdef SND_PCM_INDIRECT2_STAT
-void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec);
-#endif
-
-snd_pcm_uframes_t
-snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec);
-void
-snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- snd_pcm_indirect2_copy_t copy,
- snd_pcm_indirect2_zero_t zero);
-void
-snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream,
- struct snd_pcm_indirect2 *rec,
- snd_pcm_indirect2_copy_t copy,
- snd_pcm_indirect2_zero_t null);
-
-#endif /* __SOUND_PCM_INDIRECT2_H */
diff --git a/sound/drivers/pcmtest.c b/sound/drivers/pcmtest.c
new file mode 100644
index 000000000000..b8474631f0b5
--- /dev/null
+++ b/sound/drivers/pcmtest.c
@@ -0,0 +1,780 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Virtual ALSA driver for PCM testing/fuzzing
+ *
+ * Copyright 2023 Ivan Orlov <ivan.orlov0322@gmail.com>
+ *
+ * This is a simple virtual ALSA driver, which can be used for audio applications/PCM middle layer
+ * testing or fuzzing.
+ * It can:
+ * - Simulate 'playback' and 'capture' actions
+ * - Generate random or pattern-based capture data
+ * - Check playback buffer for containing looped template, and notify about the results
+ * through the debugfs entry
+ * - Inject delays into the playback and capturing processes. See 'inject_delay' parameter.
+ * - Inject errors during the PCM callbacks.
+ * - Register custom RESET ioctl and notify when it is called through the debugfs entry
+ * - Work in interleaved and non-interleaved modes
+ * - Support up to 8 substreams
+ * - Support up to 4 channels
+ * - Support framerates from 8 kHz to 48 kHz
+ *
+ * When driver works in the capture mode with multiple channels, it duplicates the looped
+ * pattern to each separate channel. For example, if we have 2 channels, format = U8, interleaved
+ * access mode and pattern 'abacaba', the DMA buffer will look like aabbccaabbaaaa..., so buffer for
+ * each channel will contain abacabaabacaba... Same for the non-interleaved mode.
+ *
+ * However, it may break the capturing on the higher framerates with small period size, so it is
+ * better to choose larger period sizes.
+ *
+ * You can find the corresponding selftest in the 'alsa' selftests folder.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <sound/pcm.h>
+#include <sound/core.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/random.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+
+#define TIMER_PER_SEC 5
+#define TIMER_INTERVAL (HZ / TIMER_PER_SEC)
+#define DELAY_JIFFIES HZ
+#define PLAYBACK_SUBSTREAM_CNT 8
+#define CAPTURE_SUBSTREAM_CNT 8
+#define MAX_CHANNELS_NUM 4
+
+#define DEFAULT_PATTERN "abacaba"
+#define DEFAULT_PATTERN_LEN 7
+
+#define FILL_MODE_RAND 0
+#define FILL_MODE_PAT 1
+
+#define MAX_PATTERN_LEN 4096
+
+static int index = -1;
+static char *id = "pcmtest";
+static bool enable = true;
+static int inject_delay;
+static bool inject_hwpars_err;
+static bool inject_prepare_err;
+static bool inject_trigger_err;
+static bool inject_open_err;
+
+static short fill_mode = FILL_MODE_PAT;
+
+static u8 playback_capture_test;
+static u8 ioctl_reset_test;
+static struct dentry *driver_debug_dir;
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for pcmtest soundcard");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for pcmtest soundcard");
+module_param(enable, bool, 0444);
+MODULE_PARM_DESC(enable, "Enable pcmtest soundcard.");
+module_param(fill_mode, short, 0600);
+MODULE_PARM_DESC(fill_mode, "Buffer fill mode: rand(0) or pattern(1)");
+module_param(inject_delay, int, 0600);
+MODULE_PARM_DESC(inject_delay, "Inject delays during playback/capture (in jiffies)");
+module_param(inject_hwpars_err, bool, 0600);
+MODULE_PARM_DESC(inject_hwpars_err, "Inject EBUSY error in the 'hw_params' callback");
+module_param(inject_prepare_err, bool, 0600);
+MODULE_PARM_DESC(inject_prepare_err, "Inject EINVAL error in the 'prepare' callback");
+module_param(inject_trigger_err, bool, 0600);
+MODULE_PARM_DESC(inject_trigger_err, "Inject EINVAL error in the 'trigger' callback");
+module_param(inject_open_err, bool, 0600);
+MODULE_PARM_DESC(inject_open_err, "Inject EBUSY error in the 'open' callback");
+
+struct pcmtst {
+ struct snd_pcm *pcm;
+ struct snd_card *card;
+ struct platform_device *pdev;
+};
+
+struct pcmtst_buf_iter {
+ size_t buf_pos; // position in the DMA buffer
+ size_t period_pos; // period-relative position
+ size_t b_rw; // Bytes to write on every timer tick
+ size_t s_rw_ch; // Samples to write to one channel on every tick
+ unsigned int sample_bytes; // sample_bits / 8
+ bool is_buf_corrupted; // playback test result indicator
+ size_t period_bytes; // bytes in a one period
+ bool interleaved; // Interleaved/Non-interleaved mode
+ size_t total_bytes; // Total bytes read/written
+ size_t chan_block; // Bytes in one channel buffer when non-interleaved
+ struct snd_pcm_substream *substream;
+ bool suspend; // We need to pause timer without shutting it down
+ struct timer_list timer_instance;
+};
+
+static struct snd_pcm_hardware snd_pcmtst_hw = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_NONINTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = MAX_CHANNELS_NUM,
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 4096,
+ .period_bytes_max = 32768,
+ .periods_min = 1,
+ .periods_max = 1024,
+};
+
+struct pattern_buf {
+ char *buf;
+ u32 len;
+};
+
+static int buf_allocated;
+static struct pattern_buf patt_bufs[MAX_CHANNELS_NUM];
+
+static inline void inc_buf_pos(struct pcmtst_buf_iter *v_iter, size_t by, size_t bytes)
+{
+ v_iter->total_bytes += by;
+ v_iter->buf_pos += by;
+ if (v_iter->buf_pos >= bytes)
+ v_iter->buf_pos %= bytes;
+}
+
+/*
+ * Position in the DMA buffer when we are in the non-interleaved mode. We increment buf_pos
+ * every time we write a byte to any channel, so the position in the current channel buffer is
+ * (position in the DMA buffer) / count_of_channels + size_of_channel_buf * current_channel
+ */
+static inline size_t buf_pos_n(struct pcmtst_buf_iter *v_iter, unsigned int channels,
+ unsigned int chan_num)
+{
+ return v_iter->buf_pos / channels + v_iter->chan_block * chan_num;
+}
+
+/*
+ * Get the count of bytes written for the current channel in the interleaved mode.
+ * This is (count of samples written for the current channel) * bytes_in_sample +
+ * (relative position in the current sample)
+ */
+static inline size_t ch_pos_i(size_t b_total, unsigned int channels, unsigned int b_sample)
+{
+ return b_total / channels / b_sample * b_sample + (b_total % b_sample);
+}
+
+static void check_buf_block_i(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ size_t i;
+ short ch_num;
+ u8 current_byte;
+
+ for (i = 0; i < v_iter->b_rw; i++) {
+ current_byte = runtime->dma_area[v_iter->buf_pos];
+ if (!current_byte)
+ break;
+ ch_num = (v_iter->total_bytes / v_iter->sample_bytes) % runtime->channels;
+ if (current_byte != patt_bufs[ch_num].buf[ch_pos_i(v_iter->total_bytes,
+ runtime->channels,
+ v_iter->sample_bytes)
+ % patt_bufs[ch_num].len]) {
+ v_iter->is_buf_corrupted = true;
+ break;
+ }
+ inc_buf_pos(v_iter, 1, runtime->dma_bytes);
+ }
+ // If we broke during the loop, add remaining bytes to the buffer position.
+ inc_buf_pos(v_iter, v_iter->b_rw - i, runtime->dma_bytes);
+}
+
+static void check_buf_block_ni(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ unsigned int channels = runtime->channels;
+ size_t i;
+ short ch_num;
+ u8 current_byte;
+
+ for (i = 0; i < v_iter->b_rw; i++) {
+ ch_num = i % channels;
+ current_byte = runtime->dma_area[buf_pos_n(v_iter, channels, ch_num)];
+ if (!current_byte)
+ break;
+ if (current_byte != patt_bufs[ch_num].buf[(v_iter->total_bytes / channels)
+ % patt_bufs[ch_num].len]) {
+ v_iter->is_buf_corrupted = true;
+ break;
+ }
+ inc_buf_pos(v_iter, 1, runtime->dma_bytes);
+ }
+ inc_buf_pos(v_iter, v_iter->b_rw - i, runtime->dma_bytes);
+}
+
+/*
+ * Check one block of the buffer. Here we iterate the buffer until we find '0'. This condition is
+ * necessary because we need to detect when the reading/writing ends, so we assume that the pattern
+ * doesn't contain zeros.
+ */
+static void check_buf_block(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ if (v_iter->interleaved)
+ check_buf_block_i(v_iter, runtime);
+ else
+ check_buf_block_ni(v_iter, runtime);
+}
+
+/*
+ * Fill buffer in the non-interleaved mode. The order of samples is C0, ..., C0, C1, ..., C1, C2...
+ * The channel buffers lay in the DMA buffer continuously (see default copy
+ * handlers in the pcm_lib.c file).
+ *
+ * Here we increment the DMA buffer position every time we write a byte to any channel 'buffer'.
+ * We need this to simulate the correct hardware pointer moving.
+ */
+static void fill_block_pattern_n(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ size_t i;
+ unsigned int channels = runtime->channels;
+ short ch_num;
+
+ for (i = 0; i < v_iter->b_rw; i++) {
+ ch_num = i % channels;
+ runtime->dma_area[buf_pos_n(v_iter, channels, ch_num)] =
+ patt_bufs[ch_num].buf[(v_iter->total_bytes / channels)
+ % patt_bufs[ch_num].len];
+ inc_buf_pos(v_iter, 1, runtime->dma_bytes);
+ }
+}
+
+// Fill buffer in the interleaved mode. The order of samples is C0, C1, C2, C0, C1, C2, ...
+static void fill_block_pattern_i(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ size_t sample;
+ size_t pos_in_ch, pos_pattern;
+ short ch, pos_sample;
+
+ pos_in_ch = ch_pos_i(v_iter->total_bytes, runtime->channels, v_iter->sample_bytes);
+
+ for (sample = 0; sample < v_iter->s_rw_ch; sample++) {
+ for (ch = 0; ch < runtime->channels; ch++) {
+ for (pos_sample = 0; pos_sample < v_iter->sample_bytes; pos_sample++) {
+ pos_pattern = (pos_in_ch + sample * v_iter->sample_bytes
+ + pos_sample) % patt_bufs[ch].len;
+ runtime->dma_area[v_iter->buf_pos] = patt_bufs[ch].buf[pos_pattern];
+ inc_buf_pos(v_iter, 1, runtime->dma_bytes);
+ }
+ }
+ }
+}
+
+static void fill_block_pattern(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ if (v_iter->interleaved)
+ fill_block_pattern_i(v_iter, runtime);
+ else
+ fill_block_pattern_n(v_iter, runtime);
+}
+
+static void fill_block_rand_n(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ unsigned int channels = runtime->channels;
+ // Remaining space in all channel buffers
+ size_t bytes_remain = runtime->dma_bytes - v_iter->buf_pos;
+ unsigned int i;
+
+ for (i = 0; i < channels; i++) {
+ if (v_iter->b_rw <= bytes_remain) {
+ //b_rw - count of bytes must be written for all channels at each timer tick
+ get_random_bytes(runtime->dma_area + buf_pos_n(v_iter, channels, i),
+ v_iter->b_rw / channels);
+ } else {
+ // Write to the end of buffer and start from the beginning of it
+ get_random_bytes(runtime->dma_area + buf_pos_n(v_iter, channels, i),
+ bytes_remain / channels);
+ get_random_bytes(runtime->dma_area + v_iter->chan_block * i,
+ (v_iter->b_rw - bytes_remain) / channels);
+ }
+ }
+ inc_buf_pos(v_iter, v_iter->b_rw, runtime->dma_bytes);
+}
+
+static void fill_block_rand_i(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ size_t in_cur_block = runtime->dma_bytes - v_iter->buf_pos;
+
+ if (v_iter->b_rw <= in_cur_block) {
+ get_random_bytes(&runtime->dma_area[v_iter->buf_pos], v_iter->b_rw);
+ } else {
+ get_random_bytes(&runtime->dma_area[v_iter->buf_pos], in_cur_block);
+ get_random_bytes(runtime->dma_area, v_iter->b_rw - in_cur_block);
+ }
+ inc_buf_pos(v_iter, v_iter->b_rw, runtime->dma_bytes);
+}
+
+static void fill_block_random(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ if (v_iter->interleaved)
+ fill_block_rand_i(v_iter, runtime);
+ else
+ fill_block_rand_n(v_iter, runtime);
+}
+
+static void fill_block(struct pcmtst_buf_iter *v_iter, struct snd_pcm_runtime *runtime)
+{
+ switch (fill_mode) {
+ case FILL_MODE_RAND:
+ fill_block_random(v_iter, runtime);
+ break;
+ case FILL_MODE_PAT:
+ fill_block_pattern(v_iter, runtime);
+ break;
+ }
+}
+
+/*
+ * Here we iterate through the buffer by (buffer_size / iterates_per_second) bytes.
+ * The driver uses timer to simulate the hardware pointer moving, and notify the PCM middle layer
+ * about period elapsed.
+ */
+static void timer_timeout(struct timer_list *data)
+{
+ struct pcmtst_buf_iter *v_iter;
+ struct snd_pcm_substream *substream;
+
+ v_iter = timer_container_of(v_iter, data, timer_instance);
+ substream = v_iter->substream;
+
+ if (v_iter->suspend)
+ return;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !v_iter->is_buf_corrupted)
+ check_buf_block(v_iter, substream->runtime);
+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ fill_block(v_iter, substream->runtime);
+ else
+ inc_buf_pos(v_iter, v_iter->b_rw, substream->runtime->dma_bytes);
+
+ v_iter->period_pos += v_iter->b_rw;
+ if (v_iter->period_pos >= v_iter->period_bytes) {
+ v_iter->period_pos %= v_iter->period_bytes;
+ snd_pcm_period_elapsed(substream);
+ }
+
+ if (!v_iter->suspend)
+ mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL + inject_delay);
+}
+
+static int snd_pcmtst_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcmtst_buf_iter *v_iter;
+
+ if (inject_open_err)
+ return -EBUSY;
+
+ v_iter = kzalloc(sizeof(*v_iter), GFP_KERNEL);
+ if (!v_iter)
+ return -ENOMEM;
+
+ v_iter->substream = substream;
+ runtime->hw = snd_pcmtst_hw;
+ runtime->private_data = v_iter;
+
+ playback_capture_test = 0;
+ ioctl_reset_test = 0;
+
+ timer_setup(&v_iter->timer_instance, timer_timeout, 0);
+
+ return 0;
+}
+
+static int snd_pcmtst_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct pcmtst_buf_iter *v_iter = substream->runtime->private_data;
+
+ timer_shutdown_sync(&v_iter->timer_instance);
+ playback_capture_test = !v_iter->is_buf_corrupted;
+ kfree(v_iter);
+ return 0;
+}
+
+static inline void reset_buf_iterator(struct pcmtst_buf_iter *v_iter)
+{
+ v_iter->buf_pos = 0;
+ v_iter->is_buf_corrupted = false;
+ v_iter->period_pos = 0;
+ v_iter->total_bytes = 0;
+}
+
+static inline void start_pcmtest_timer(struct pcmtst_buf_iter *v_iter)
+{
+ v_iter->suspend = false;
+ mod_timer(&v_iter->timer_instance, jiffies + TIMER_INTERVAL);
+}
+
+static int snd_pcmtst_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct pcmtst_buf_iter *v_iter = substream->runtime->private_data;
+
+ if (inject_trigger_err)
+ return -EINVAL;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ reset_buf_iterator(v_iter);
+ start_pcmtest_timer(v_iter);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ start_pcmtest_timer(v_iter);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ // We can't call timer_shutdown_sync here, as it is forbidden to sleep here
+ v_iter->suspend = true;
+ timer_delete(&v_iter->timer_instance);
+ break;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t snd_pcmtst_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct pcmtst_buf_iter *v_iter = substream->runtime->private_data;
+
+ return bytes_to_frames(substream->runtime, v_iter->buf_pos);
+}
+
+static int snd_pcmtst_free(struct pcmtst *pcmtst)
+{
+ if (!pcmtst)
+ return 0;
+ kfree(pcmtst);
+ return 0;
+}
+
+// These callbacks are required, but empty - all freeing occurs in pdev_remove
+static int snd_pcmtst_dev_free(struct snd_device *device)
+{
+ return 0;
+}
+
+static void pcmtst_pdev_release(struct device *dev)
+{
+}
+
+static int snd_pcmtst_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct pcmtst_buf_iter *v_iter = runtime->private_data;
+
+ if (inject_prepare_err)
+ return -EINVAL;
+
+ v_iter->sample_bytes = samples_to_bytes(runtime, 1);
+ v_iter->period_bytes = snd_pcm_lib_period_bytes(substream);
+ v_iter->interleaved = true;
+ if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ||
+ runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) {
+ v_iter->chan_block = snd_pcm_lib_buffer_bytes(substream) / runtime->channels;
+ v_iter->interleaved = false;
+ }
+ // We want to record RATE * ch_cnt samples per sec, it is rate * sample_bytes * ch_cnt bytes
+ v_iter->s_rw_ch = runtime->rate / TIMER_PER_SEC;
+ v_iter->b_rw = v_iter->s_rw_ch * v_iter->sample_bytes * runtime->channels;
+
+ return 0;
+}
+
+static int snd_pcmtst_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ if (inject_hwpars_err)
+ return -EBUSY;
+ return 0;
+}
+
+static int snd_pcmtst_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+static int snd_pcmtst_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg)
+{
+ switch (cmd) {
+ case SNDRV_PCM_IOCTL1_RESET:
+ ioctl_reset_test = 1;
+ break;
+ }
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_pcmtst_sync_stop(struct snd_pcm_substream *substream)
+{
+ struct pcmtst_buf_iter *v_iter = substream->runtime->private_data;
+
+ timer_delete_sync(&v_iter->timer_instance);
+
+ return 0;
+}
+
+static const struct snd_pcm_ops snd_pcmtst_playback_ops = {
+ .open = snd_pcmtst_pcm_open,
+ .close = snd_pcmtst_pcm_close,
+ .trigger = snd_pcmtst_pcm_trigger,
+ .hw_params = snd_pcmtst_pcm_hw_params,
+ .ioctl = snd_pcmtst_ioctl,
+ .sync_stop = snd_pcmtst_sync_stop,
+ .hw_free = snd_pcmtst_pcm_hw_free,
+ .prepare = snd_pcmtst_pcm_prepare,
+ .pointer = snd_pcmtst_pcm_pointer,
+};
+
+static const struct snd_pcm_ops snd_pcmtst_capture_ops = {
+ .open = snd_pcmtst_pcm_open,
+ .close = snd_pcmtst_pcm_close,
+ .trigger = snd_pcmtst_pcm_trigger,
+ .hw_params = snd_pcmtst_pcm_hw_params,
+ .hw_free = snd_pcmtst_pcm_hw_free,
+ .ioctl = snd_pcmtst_ioctl,
+ .sync_stop = snd_pcmtst_sync_stop,
+ .prepare = snd_pcmtst_pcm_prepare,
+ .pointer = snd_pcmtst_pcm_pointer,
+};
+
+static int snd_pcmtst_new_pcm(struct pcmtst *pcmtst)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(pcmtst->card, "PCMTest", 0, PLAYBACK_SUBSTREAM_CNT,
+ CAPTURE_SUBSTREAM_CNT, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = pcmtst;
+ strscpy(pcm->name, "PCMTest");
+ pcmtst->pcm = pcm;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_pcmtst_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_pcmtst_capture_ops);
+
+ err = snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &pcmtst->pdev->dev,
+ 0, 128 * 1024);
+ return err;
+}
+
+static int snd_pcmtst_create(struct snd_card *card, struct platform_device *pdev,
+ struct pcmtst **r_pcmtst)
+{
+ struct pcmtst *pcmtst;
+ int err;
+ static const struct snd_device_ops ops = {
+ .dev_free = snd_pcmtst_dev_free,
+ };
+
+ pcmtst = kzalloc(sizeof(*pcmtst), GFP_KERNEL);
+ if (!pcmtst)
+ return -ENOMEM;
+ pcmtst->card = card;
+ pcmtst->pdev = pdev;
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, pcmtst, &ops);
+ if (err < 0)
+ goto _err_free_chip;
+
+ err = snd_pcmtst_new_pcm(pcmtst);
+ if (err < 0)
+ goto _err_free_chip;
+
+ *r_pcmtst = pcmtst;
+ return 0;
+
+_err_free_chip:
+ snd_pcmtst_free(pcmtst);
+ return err;
+}
+
+static int pcmtst_probe(struct platform_device *pdev)
+{
+ struct snd_card *card;
+ struct pcmtst *pcmtst;
+ int err;
+
+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (err)
+ return err;
+
+ err = snd_devm_card_new(&pdev->dev, index, id, THIS_MODULE, 0, &card);
+ if (err < 0)
+ return err;
+ err = snd_pcmtst_create(card, pdev, &pcmtst);
+ if (err < 0)
+ return err;
+
+ strscpy(card->driver, "PCM-TEST Driver");
+ strscpy(card->shortname, "PCM-Test");
+ strscpy(card->longname, "PCM-Test virtual driver");
+
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
+
+ platform_set_drvdata(pdev, pcmtst);
+
+ return 0;
+}
+
+static void pdev_remove(struct platform_device *pdev)
+{
+ struct pcmtst *pcmtst = platform_get_drvdata(pdev);
+
+ snd_pcmtst_free(pcmtst);
+}
+
+static struct platform_device pcmtst_pdev = {
+ .name = "pcmtest",
+ .dev.release = pcmtst_pdev_release,
+};
+
+static struct platform_driver pcmtst_pdrv = {
+ .probe = pcmtst_probe,
+ .remove = pdev_remove,
+ .driver = {
+ .name = "pcmtest",
+ },
+};
+
+static ssize_t pattern_write(struct file *file, const char __user *u_buff, size_t len, loff_t *off)
+{
+ struct pattern_buf *patt_buf = file->f_inode->i_private;
+ ssize_t to_write = len;
+
+ if (*off + to_write > MAX_PATTERN_LEN)
+ to_write = MAX_PATTERN_LEN - *off;
+
+ // Crop silently everything over the buffer
+ if (to_write <= 0)
+ return len;
+
+ if (copy_from_user(patt_buf->buf + *off, u_buff, to_write))
+ return -EFAULT;
+
+ patt_buf->len = *off + to_write;
+ *off += to_write;
+
+ return to_write;
+}
+
+static ssize_t pattern_read(struct file *file, char __user *u_buff, size_t len, loff_t *off)
+{
+ struct pattern_buf *patt_buf = file->f_inode->i_private;
+ ssize_t to_read = len;
+
+ if (*off + to_read >= MAX_PATTERN_LEN)
+ to_read = MAX_PATTERN_LEN - *off;
+ if (to_read <= 0)
+ return 0;
+
+ if (copy_to_user(u_buff, patt_buf->buf + *off, to_read))
+ to_read = 0;
+ else
+ *off += to_read;
+
+ return to_read;
+}
+
+static const struct file_operations fill_pattern_fops = {
+ .read = pattern_read,
+ .write = pattern_write,
+};
+
+static int setup_patt_bufs(void)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(patt_bufs); i++) {
+ patt_bufs[i].buf = kmalloc(MAX_PATTERN_LEN, GFP_KERNEL);
+ if (!patt_bufs[i].buf)
+ break;
+ strscpy_pad(patt_bufs[i].buf, DEFAULT_PATTERN, MAX_PATTERN_LEN);
+ patt_bufs[i].len = DEFAULT_PATTERN_LEN;
+ }
+
+ return i;
+}
+
+static const char * const pattern_files[] = { "fill_pattern0", "fill_pattern1",
+ "fill_pattern2", "fill_pattern3"};
+static int init_debug_files(int buf_count)
+{
+ size_t i;
+ char len_file_name[32];
+
+ driver_debug_dir = debugfs_create_dir("pcmtest", NULL);
+ if (IS_ERR(driver_debug_dir))
+ return PTR_ERR(driver_debug_dir);
+ debugfs_create_u8("pc_test", 0444, driver_debug_dir, &playback_capture_test);
+ debugfs_create_u8("ioctl_test", 0444, driver_debug_dir, &ioctl_reset_test);
+
+ for (i = 0; i < buf_count; i++) {
+ debugfs_create_file(pattern_files[i], 0600, driver_debug_dir,
+ &patt_bufs[i], &fill_pattern_fops);
+ snprintf(len_file_name, sizeof(len_file_name), "%s_len", pattern_files[i]);
+ debugfs_create_u32(len_file_name, 0444, driver_debug_dir, &patt_bufs[i].len);
+ }
+
+ return 0;
+}
+
+static void free_pattern_buffers(void)
+{
+ int i;
+
+ for (i = 0; i < buf_allocated; i++)
+ kfree(patt_bufs[i].buf);
+}
+
+static void clear_debug_files(void)
+{
+ debugfs_remove_recursive(driver_debug_dir);
+}
+
+static int __init mod_init(void)
+{
+ int err = 0;
+
+ buf_allocated = setup_patt_bufs();
+ if (!buf_allocated)
+ return -ENOMEM;
+
+ snd_pcmtst_hw.channels_max = buf_allocated;
+
+ err = init_debug_files(buf_allocated);
+ if (err)
+ return err;
+ err = platform_device_register(&pcmtst_pdev);
+ if (err)
+ return err;
+ err = platform_driver_register(&pcmtst_pdrv);
+ if (err)
+ platform_device_unregister(&pcmtst_pdev);
+ return err;
+}
+
+static void __exit mod_exit(void)
+{
+ clear_debug_files();
+ free_pattern_buffers();
+
+ platform_driver_unregister(&pcmtst_pdrv);
+ platform_device_unregister(&pcmtst_pdev);
+}
+
+MODULE_DESCRIPTION("Virtual ALSA driver for PCM testing/fuzzing");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ivan Orlov");
+module_init(mod_init);
+module_exit(mod_exit);
diff --git a/sound/drivers/pcsp/Makefile b/sound/drivers/pcsp/Makefile
index b19555b440da..309c09497261 100644
--- a/sound/drivers/pcsp/Makefile
+++ b/sound/drivers/pcsp/Makefile
@@ -1,2 +1,3 @@
-snd-pcsp-objs := pcsp.o pcsp_lib.o pcsp_mixer.o pcsp_input.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-pcsp-y := pcsp.o pcsp_lib.o pcsp_mixer.o pcsp_input.o
obj-$(CONFIG_SND_PCSP) += snd-pcsp.o
diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c
index f165c77d6273..ff6bb375c900 100644
--- a/sound/drivers/pcsp/pcsp.c
+++ b/sound/drivers/pcsp/pcsp.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* PC-Speaker driver for Linux
*
@@ -6,27 +7,27 @@
*/
#include <linux/init.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <linux/input.h>
#include <linux/delay.h>
-#include <asm/bitops.h>
+#include <linux/bitops.h>
+#include <linux/mm.h>
#include "pcsp_input.h"
#include "pcsp.h"
MODULE_AUTHOR("Stas Sergeev <stsp@users.sourceforge.net>");
MODULE_DESCRIPTION("PC-Speaker driver");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{PC-Speaker, pcsp}}");
MODULE_ALIAS("platform:pcspkr");
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
-static int enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */
-static int nopcm; /* Disable PCM capability of the driver */
+static bool enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */
+static bool nopcm; /* Disable PCM capability of the driver */
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for pcsp soundcard.");
@@ -39,32 +40,30 @@ MODULE_PARM_DESC(nopcm, "Disable PC-Speaker PCM sound. Only beeps remain.");
struct snd_pcsp pcsp_chip;
-static int __devinit snd_pcsp_create(struct snd_card *card)
+static int snd_pcsp_create(struct snd_card *card)
{
- static struct snd_device_ops ops = { };
- struct timespec tp;
- int err;
+ unsigned int resolution = hrtimer_resolution;
int div, min_div, order;
if (!nopcm) {
- hrtimer_get_res(CLOCK_MONOTONIC, &tp);
- if (tp.tv_sec || tp.tv_nsec > PCSP_MAX_PERIOD_NS) {
- printk(KERN_ERR "PCSP: Timer resolution is not sufficient "
- "(%linS)\n", tp.tv_nsec);
- printk(KERN_ERR "PCSP: Make sure you have HPET and ACPI "
- "enabled.\n");
- printk(KERN_ERR "PCSP: Turned into nopcm mode.\n");
+ if (resolution > PCSP_MAX_PERIOD_NS) {
+ dev_err(card->dev,
+ "PCSP: Timer resolution is not sufficient (%unS)\n",
+ resolution);
+ dev_err(card->dev,
+ "PCSP: Make sure you have HPET and ACPI enabled.\n");
+ dev_err(card->dev, "PCSP: Turned into nopcm mode.\n");
nopcm = 1;
}
}
- if (loops_per_jiffy >= PCSP_MIN_LPJ && tp.tv_nsec <= PCSP_MIN_PERIOD_NS)
+ if (loops_per_jiffy >= PCSP_MIN_LPJ && resolution <= PCSP_MIN_PERIOD_NS)
min_div = MIN_DIV;
else
min_div = MAX_DIV;
#if PCSP_DEBUG
- printk(KERN_DEBUG "PCSP: lpj=%li, min_div=%i, res=%li\n",
- loops_per_jiffy, min_div, tp.tv_nsec);
+ dev_dbg(card->dev, "PCSP: lpj=%li, min_div=%i, res=%u\n",
+ loops_per_jiffy, min_div, resolution);
#endif
div = MAX_DIV / min_div;
@@ -84,16 +83,19 @@ static int __devinit snd_pcsp_create(struct snd_card *card)
pcsp_chip.port = 0x61;
pcsp_chip.irq = -1;
pcsp_chip.dma = -1;
-
- /* Register device */
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, &pcsp_chip, &ops);
- if (err < 0)
- return err;
+ card->private_data = &pcsp_chip;
return 0;
}
-static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev)
+static void pcsp_stop_beep(struct snd_pcsp *chip);
+
+static void alsa_card_pcsp_free(struct snd_card *card)
+{
+ pcsp_stop_beep(card->private_data);
+}
+
+static int snd_card_pcsp_probe(int devnum, struct device *dev)
{
struct snd_card *card;
int err;
@@ -101,72 +103,58 @@ static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev)
if (devnum != 0)
return -EINVAL;
- hrtimer_init(&pcsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- pcsp_chip.timer.function = pcsp_do_timer;
+ hrtimer_setup(&pcsp_chip.timer, pcsp_do_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(dev, index, id, THIS_MODULE, 0, &card);
if (err < 0)
return err;
err = snd_pcsp_create(card);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
+
if (!nopcm) {
err = snd_pcsp_new_pcm(&pcsp_chip);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
}
err = snd_pcsp_new_mixer(&pcsp_chip, nopcm);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
- snd_card_set_dev(pcsp_chip.card, dev);
-
- strcpy(card->driver, "PC-Speaker");
- strcpy(card->shortname, "pcsp");
+ strscpy(card->driver, "PC-Speaker");
+ strscpy(card->shortname, "pcsp");
sprintf(card->longname, "Internal PC-Speaker at port 0x%x",
pcsp_chip.port);
err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
+ card->private_free = alsa_card_pcsp_free;
return 0;
}
-static int __devinit alsa_card_pcsp_init(struct device *dev)
+static int alsa_card_pcsp_init(struct device *dev)
{
int err;
err = snd_card_pcsp_probe(0, dev);
if (err) {
- printk(KERN_ERR "PC-Speaker initialization failed.\n");
+ dev_err(dev, "PC-Speaker initialization failed.\n");
return err;
}
-#ifdef CONFIG_DEBUG_PAGEALLOC
/* Well, CONFIG_DEBUG_PAGEALLOC makes the sound horrible. Lets alert */
- printk(KERN_WARNING "PCSP: CONFIG_DEBUG_PAGEALLOC is enabled, "
- "which may make the sound noisy.\n");
-#endif
+ if (debug_pagealloc_enabled()) {
+ dev_warn(dev,
+ "PCSP: CONFIG_DEBUG_PAGEALLOC is enabled, which may make the sound noisy.\n");
+ }
return 0;
}
-static void __devexit alsa_card_pcsp_exit(struct snd_pcsp *chip)
-{
- snd_card_free(chip->card);
-}
-
-static int __devinit pcsp_probe(struct platform_device *dev)
+static int pcsp_probe(struct platform_device *dev)
{
int err;
@@ -175,41 +163,27 @@ static int __devinit pcsp_probe(struct platform_device *dev)
return err;
err = alsa_card_pcsp_init(&dev->dev);
- if (err < 0) {
- pcspkr_input_remove(pcsp_chip.input_dev);
+ if (err < 0)
return err;
- }
platform_set_drvdata(dev, &pcsp_chip);
return 0;
}
-static int __devexit pcsp_remove(struct platform_device *dev)
-{
- struct snd_pcsp *chip = platform_get_drvdata(dev);
- alsa_card_pcsp_exit(chip);
- pcspkr_input_remove(chip->input_dev);
- platform_set_drvdata(dev, NULL);
- return 0;
-}
-
static void pcsp_stop_beep(struct snd_pcsp *chip)
{
pcsp_sync_stop(chip);
pcspkr_stop_sound();
}
-#ifdef CONFIG_PM
-static int pcsp_suspend(struct platform_device *dev, pm_message_t state)
+static int pcsp_suspend(struct device *dev)
{
- struct snd_pcsp *chip = platform_get_drvdata(dev);
+ struct snd_pcsp *chip = dev_get_drvdata(dev);
pcsp_stop_beep(chip);
- snd_pcm_suspend_all(chip->pcm);
return 0;
}
-#else
-#define pcsp_suspend NULL
-#endif /* CONFIG_PM */
+
+static DEFINE_SIMPLE_DEV_PM_OPS(pcsp_pm, pcsp_suspend, NULL);
static void pcsp_shutdown(struct platform_device *dev)
{
@@ -220,11 +194,9 @@ static void pcsp_shutdown(struct platform_device *dev)
static struct platform_driver pcsp_platform_driver = {
.driver = {
.name = "pcspkr",
- .owner = THIS_MODULE,
+ .pm = &pcsp_pm,
},
.probe = pcsp_probe,
- .remove = __devexit_p(pcsp_remove),
- .suspend = pcsp_suspend,
.shutdown = pcsp_shutdown,
};
diff --git a/sound/drivers/pcsp/pcsp.h b/sound/drivers/pcsp/pcsp.h
index 4ff6c8cc5077..036ad3c99a43 100644
--- a/sound/drivers/pcsp/pcsp.h
+++ b/sound/drivers/pcsp/pcsp.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* PC-Speaker driver for Linux
*
@@ -10,14 +11,8 @@
#define __PCSP_H__
#include <linux/hrtimer.h>
+#include <linux/i8253.h>
#include <linux/timex.h>
-#if defined(CONFIG_MIPS) || defined(CONFIG_X86)
-/* Use the global PIT lock ! */
-#include <asm/i8253.h>
-#else
-#include <asm/8253pit.h>
-static DEFINE_RAW_SPINLOCK(i8253_lock);
-#endif
#define PCSP_SOUND_VERSION 0x400 /* read 4.00 */
#define PCSP_DEBUG 0
diff --git a/sound/drivers/pcsp/pcsp_input.c b/sound/drivers/pcsp/pcsp_input.c
index b5e2b54c2604..5a799f7f00a2 100644
--- a/sound/drivers/pcsp/pcsp_input.c
+++ b/sound/drivers/pcsp/pcsp_input.c
@@ -1,21 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* PC Speaker beeper driver for Linux
*
* Copyright (c) 2002 Vojtech Pavlik
* Copyright (c) 1992 Orest Zborowski
- *
*/
-/*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation
- */
#include <linux/init.h>
#include <linux/input.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include "pcsp.h"
+#include "pcsp_input.h"
static void pcspkr_do_sound(unsigned int count)
{
@@ -58,6 +54,7 @@ static int pcspkr_input_event(struct input_dev *dev, unsigned int type,
case SND_BELL:
if (value)
value = 1000;
+ break;
case SND_TONE:
break;
default:
@@ -77,11 +74,11 @@ static int pcspkr_input_event(struct input_dev *dev, unsigned int type,
return 0;
}
-int __devinit pcspkr_input_init(struct input_dev **rdev, struct device *dev)
+int pcspkr_input_init(struct input_dev **rdev, struct device *dev)
{
int err;
- struct input_dev *input_dev = input_allocate_device();
+ struct input_dev *input_dev = devm_input_allocate_device(dev);
if (!input_dev)
return -ENOMEM;
@@ -98,19 +95,9 @@ int __devinit pcspkr_input_init(struct input_dev **rdev, struct device *dev)
input_dev->event = pcspkr_input_event;
err = input_register_device(input_dev);
- if (err) {
- input_free_device(input_dev);
+ if (err)
return err;
- }
*rdev = input_dev;
return 0;
}
-
-int pcspkr_input_remove(struct input_dev *dev)
-{
- pcspkr_stop_sound();
- input_unregister_device(dev); /* this also does kfree() */
-
- return 0;
-}
diff --git a/sound/drivers/pcsp/pcsp_input.h b/sound/drivers/pcsp/pcsp_input.h
index e66738c78333..42bfc9eab6eb 100644
--- a/sound/drivers/pcsp/pcsp_input.h
+++ b/sound/drivers/pcsp/pcsp_input.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* PC-Speaker driver for Linux
*
@@ -7,8 +8,7 @@
#ifndef __PCSP_INPUT_H__
#define __PCSP_INPUT_H__
-int __devinit pcspkr_input_init(struct input_dev **rdev, struct device *dev);
-int pcspkr_input_remove(struct input_dev *dev);
+int pcspkr_input_init(struct input_dev **rdev, struct device *dev);
void pcspkr_stop_sound(void);
#endif
diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c
index ce9e7d170c0d..80b313f4fcd3 100644
--- a/sound/drivers/pcsp/pcsp_lib.c
+++ b/sound/drivers/pcsp/pcsp_lib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* PC-Speaker driver for Linux
*
@@ -10,11 +11,12 @@
#include <linux/gfp.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <sound/core.h>
#include <sound/pcm.h>
-#include <asm/io.h>
#include "pcsp.h"
-static int nforce_wa;
+static bool nforce_wa;
module_param(nforce_wa, bool, 0444);
MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround "
"(expect bad sound)");
@@ -22,10 +24,10 @@ MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround "
#define DMIX_WANTS_S16 1
/*
- * Call snd_pcm_period_elapsed in a tasklet
+ * Call snd_pcm_period_elapsed in a work
* This avoids spinlock messes and long-running irq contexts
*/
-static void pcsp_call_pcm_elapsed(unsigned long priv)
+static void pcsp_call_pcm_elapsed(struct work_struct *work)
{
if (atomic_read(&pcsp_chip.timer_active)) {
struct snd_pcm_substream *substream;
@@ -35,7 +37,7 @@ static void pcsp_call_pcm_elapsed(unsigned long priv)
}
}
-static DECLARE_TASKLET(pcsp_pcm_tasklet, pcsp_call_pcm_elapsed, 0);
+static DECLARE_WORK(pcsp_pcm_work, pcsp_call_pcm_elapsed);
/* write the port and returns the next expire time in ns;
* called at the trigger-start and in hrtimer callback
@@ -104,8 +106,8 @@ static void pcsp_pointer_update(struct snd_pcsp *chip)
periods_elapsed = chip->playback_ptr - chip->period_ptr;
if (periods_elapsed < 0) {
#if PCSP_DEBUG
- printk(KERN_INFO "PCSP: buffer_bytes mod period_bytes != 0 ? "
- "(%zi %zi %zi)\n",
+ dev_dbg(chip->card->dev,
+ "PCSP: buffer_bytes mod period_bytes != 0 ? (%zi %zi %zi)\n",
chip->playback_ptr, period_bytes, buffer_bytes);
#endif
periods_elapsed += buffer_bytes;
@@ -118,11 +120,9 @@ static void pcsp_pointer_update(struct snd_pcsp *chip)
if (periods_elapsed) {
chip->period_ptr += periods_elapsed * period_bytes;
chip->period_ptr %= buffer_bytes;
+ queue_work(system_highpri_wq, &pcsp_pcm_work);
}
spin_unlock_irqrestore(&chip->substream_lock, flags);
-
- if (periods_elapsed)
- tasklet_schedule(&pcsp_pcm_tasklet);
}
enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
@@ -137,14 +137,14 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
pointer_update = !chip->thalf;
ns = pcsp_timer_update(chip);
if (!ns) {
- printk(KERN_WARNING "PCSP: unexpected stop\n");
+ dev_warn(chip->card->dev, "PCSP: unexpected stop\n");
return HRTIMER_NORESTART;
}
if (pointer_update)
pcsp_pointer_update(chip);
- hrtimer_forward(handle, hrtimer_get_expires(handle), ns_to_ktime(ns));
+ hrtimer_forward_now(handle, ns_to_ktime(ns));
return HRTIMER_RESTART;
}
@@ -152,10 +152,10 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
static int pcsp_start_playing(struct snd_pcsp *chip)
{
#if PCSP_DEBUG
- printk(KERN_INFO "PCSP: start_playing called\n");
+ dev_dbg(chip->card->dev, "PCSP: start_playing called\n");
#endif
if (atomic_read(&chip->timer_active)) {
- printk(KERN_ERR "PCSP: Timer already active\n");
+ dev_err(chip->card->dev, "PCSP: Timer already active\n");
return -EIO;
}
@@ -166,14 +166,14 @@ static int pcsp_start_playing(struct snd_pcsp *chip)
atomic_set(&chip->timer_active, 1);
chip->thalf = 0;
- hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+ hrtimer_start(&pcsp_chip.timer, 0, HRTIMER_MODE_REL);
return 0;
}
static void pcsp_stop_playing(struct snd_pcsp *chip)
{
#if PCSP_DEBUG
- printk(KERN_INFO "PCSP: stop_playing called\n");
+ dev_dbg(chip->card->dev, "PCSP: stop_playing called\n");
#endif
if (!atomic_read(&chip->timer_active))
return;
@@ -195,14 +195,14 @@ void pcsp_sync_stop(struct snd_pcsp *chip)
pcsp_stop_playing(chip);
local_irq_enable();
hrtimer_cancel(&chip->timer);
- tasklet_kill(&pcsp_pcm_tasklet);
+ cancel_work_sync(&pcsp_pcm_work);
}
static int snd_pcsp_playback_close(struct snd_pcm_substream *substream)
{
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
#if PCSP_DEBUG
- printk(KERN_INFO "PCSP: close called\n");
+ dev_dbg(chip->card->dev, "PCSP: close called\n");
#endif
pcsp_sync_stop(chip);
chip->playback_substream = NULL;
@@ -213,12 +213,7 @@ static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
- int err;
pcsp_sync_stop(chip);
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
return 0;
}
@@ -226,10 +221,10 @@ static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream)
{
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
#if PCSP_DEBUG
- printk(KERN_INFO "PCSP: hw_free called\n");
+ dev_dbg(chip->card->dev, "PCSP: hw_free called\n");
#endif
pcsp_sync_stop(chip);
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream)
@@ -242,14 +237,13 @@ static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream)
snd_pcm_format_physical_width(substream->runtime->format) >> 3;
chip->is_signed = snd_pcm_format_signed(substream->runtime->format);
#if PCSP_DEBUG
- printk(KERN_INFO "PCSP: prepare called, "
- "size=%zi psize=%zi f=%zi f1=%i fsize=%i\n",
- snd_pcm_lib_buffer_bytes(substream),
- snd_pcm_lib_period_bytes(substream),
- snd_pcm_lib_buffer_bytes(substream) /
- snd_pcm_lib_period_bytes(substream),
- substream->runtime->periods,
- chip->fmt_size);
+ dev_dbg(chip->card->dev, "PCSP: prepare called, size=%zi psize=%zi f=%zi f1=%i fsize=%i\n",
+ snd_pcm_lib_buffer_bytes(substream),
+ snd_pcm_lib_period_bytes(substream),
+ snd_pcm_lib_buffer_bytes(substream) /
+ snd_pcm_lib_period_bytes(substream),
+ substream->runtime->periods,
+ chip->fmt_size);
#endif
return 0;
}
@@ -258,7 +252,7 @@ static int snd_pcsp_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
#if PCSP_DEBUG
- printk(KERN_INFO "PCSP: trigger called\n");
+ dev_dbg(chip->card->dev, "PCSP: trigger called\n");
#endif
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -285,7 +279,7 @@ static snd_pcm_uframes_t snd_pcsp_playback_pointer(struct snd_pcm_substream
return bytes_to_frames(substream->runtime, pos);
}
-static struct snd_pcm_hardware snd_pcsp_playback = {
+static const struct snd_pcm_hardware snd_pcsp_playback = {
.info = (SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_HALF_DUPLEX |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
@@ -312,10 +306,10 @@ static int snd_pcsp_playback_open(struct snd_pcm_substream *substream)
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
#if PCSP_DEBUG
- printk(KERN_INFO "PCSP: open called\n");
+ dev_dbg(chip->card->dev, "PCSP: open called\n");
#endif
if (atomic_read(&chip->timer_active)) {
- printk(KERN_ERR "PCSP: still active!!\n");
+ dev_err(chip->card->dev, "PCSP: still active!!\n");
return -EBUSY;
}
runtime->hw = snd_pcsp_playback;
@@ -323,10 +317,9 @@ static int snd_pcsp_playback_open(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_pcsp_playback_ops = {
+static const struct snd_pcm_ops snd_pcsp_playback_ops = {
.open = snd_pcsp_playback_open,
.close = snd_pcsp_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_pcsp_playback_hw_params,
.hw_free = snd_pcsp_playback_hw_free,
.prepare = snd_pcsp_playback_prepare,
@@ -334,7 +327,7 @@ static struct snd_pcm_ops snd_pcsp_playback_ops = {
.pointer = snd_pcsp_playback_pointer,
};
-int __devinit snd_pcsp_new_pcm(struct snd_pcsp *chip)
+int snd_pcsp_new_pcm(struct snd_pcsp *chip)
{
int err;
@@ -347,13 +340,13 @@ int __devinit snd_pcsp_new_pcm(struct snd_pcsp *chip)
chip->pcm->private_data = chip;
chip->pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
- strcpy(chip->pcm->name, "pcsp");
+ strscpy(chip->pcm->name, "pcsp");
- snd_pcm_lib_preallocate_pages_for_all(chip->pcm,
- SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data
- (GFP_KERNEL), PCSP_BUFFER_SIZE,
- PCSP_BUFFER_SIZE);
+ snd_pcm_set_managed_buffer_all(chip->pcm,
+ SNDRV_DMA_TYPE_CONTINUOUS,
+ NULL,
+ PCSP_BUFFER_SIZE,
+ PCSP_BUFFER_SIZE);
return 0;
}
diff --git a/sound/drivers/pcsp/pcsp_mixer.c b/sound/drivers/pcsp/pcsp_mixer.c
index 6f633f4f3b96..27d6150329a8 100644
--- a/sound/drivers/pcsp/pcsp_mixer.c
+++ b/sound/drivers/pcsp/pcsp_mixer.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* PC-Speaker driver for Linux
*
@@ -72,7 +73,7 @@ static int pcsp_treble_put(struct snd_kcontrol *kcontrol,
if (treble != chip->treble) {
chip->treble = treble;
#if PCSP_DEBUG
- printk(KERN_INFO "PCSP: rate set to %li\n", PCSP_RATE());
+ dev_dbg(chip->card->dev, "PCSP: rate set to %li\n", PCSP_RATE());
#endif
changed = 1;
}
@@ -119,17 +120,17 @@ static int pcsp_pcspkr_put(struct snd_kcontrol *kcontrol,
.put = pcsp_##ctl_type##_put, \
}
-static struct snd_kcontrol_new __devinitdata snd_pcsp_controls_pcm[] = {
+static const struct snd_kcontrol_new snd_pcsp_controls_pcm[] = {
PCSP_MIXER_CONTROL(enable, "Master Playback Switch"),
PCSP_MIXER_CONTROL(treble, "BaseFRQ Playback Volume"),
};
-static struct snd_kcontrol_new __devinitdata snd_pcsp_controls_spkr[] = {
+static const struct snd_kcontrol_new snd_pcsp_controls_spkr[] = {
PCSP_MIXER_CONTROL(pcspkr, "Beep Playback Switch"),
};
-static int __devinit snd_pcsp_ctls_add(struct snd_pcsp *chip,
- struct snd_kcontrol_new *ctls, int num)
+static int snd_pcsp_ctls_add(struct snd_pcsp *chip,
+ const struct snd_kcontrol_new *ctls, int num)
{
int i, err;
struct snd_card *card = chip->card;
@@ -141,7 +142,7 @@ static int __devinit snd_pcsp_ctls_add(struct snd_pcsp *chip,
return 0;
}
-int __devinit snd_pcsp_new_mixer(struct snd_pcsp *chip, int nopcm)
+int snd_pcsp_new_mixer(struct snd_pcsp *chip, int nopcm)
{
int err;
struct snd_card *card = chip->card;
@@ -157,7 +158,7 @@ int __devinit snd_pcsp_new_mixer(struct snd_pcsp *chip, int nopcm)
if (err < 0)
return err;
- strcpy(card->mixername, "PC-Speaker");
+ strscpy(card->mixername, "PC-Speaker");
return 0;
}
diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c
index f2b0ba22d9ce..b903a138fc2a 100644
--- a/sound/drivers/portman2x4.c
+++ b/sound/drivers/portman2x4.c
@@ -1,22 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Midiman Portman2x4 parallel port midi interface
*
* Copyright (c) by Levent Guendogdu <levon@feature-it.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
* ChangeLog
* Jan 24 2007 Matthias Koenig <mkoenig@suse.de>
* - cleanup and rewrite
@@ -43,6 +30,7 @@
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/rawmidi.h>
@@ -54,22 +42,21 @@
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
static struct platform_device *platform_devices[SNDRV_CARDS];
static int device_count;
-module_param_array(index, int, NULL, S_IRUGO);
+module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
-module_param_array(id, charp, NULL, S_IRUGO);
+module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
-module_param_array(enable, bool, NULL, S_IRUGO);
+module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
MODULE_AUTHOR("Levent Guendogdu, Tobias Gehrig, Matthias Koenig");
MODULE_DESCRIPTION("Midiman Portman2x4");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Midiman,Portman2x4}}");
/*********************************************************************
* Chip specific
@@ -82,8 +69,6 @@ struct portman {
struct snd_card *card;
struct snd_rawmidi *rmidi;
struct pardevice *pardev;
- int pardev_claimed;
-
int open_count;
int mode[PORTMAN_NUM_INPUT_PORTS];
struct snd_rawmidi_substream *midi_input[PORTMAN_NUM_INPUT_PORTS];
@@ -95,9 +80,9 @@ static int portman_free(struct portman *pm)
return 0;
}
-static int __devinit portman_create(struct snd_card *card,
- struct pardevice *pardev,
- struct portman **rchip)
+static int portman_create(struct snd_card *card,
+ struct pardevice *pardev,
+ struct portman **rchip)
{
struct portman *pm;
@@ -197,21 +182,11 @@ static inline void portman_write_command(struct portman *pm, u8 value)
parport_write_control(pm->pardev->port, value);
}
-static inline u8 portman_read_command(struct portman *pm)
-{
- return parport_read_control(pm->pardev->port);
-}
-
static inline u8 portman_read_status(struct portman *pm)
{
return parport_read_status(pm->pardev->port);
}
-static inline u8 portman_read_data(struct portman *pm)
-{
- return parport_read_data(pm->pardev->port);
-}
-
static inline void portman_write_data(struct portman *pm, u8 value)
{
parport_write_data(pm->pardev->port, value);
@@ -410,9 +385,8 @@ static void portman_flush_input(struct portman *pm, unsigned char port)
command = RXDATA1;
break;
default:
- snd_printk(KERN_WARNING
- "portman_flush_input() Won't flush port %i\n",
- port);
+ dev_warn(pm->card->dev, "%s Won't flush port %i\n",
+ __func__, port);
return;
}
@@ -471,7 +445,7 @@ static int portman_probe(struct parport *p)
/* Set for RXDATA0 where no damage will be done. */
/* 5 */
- parport_write_control(p, RXDATA0 + STROBE); /* Write Strobe=1 to command reg. */
+ parport_write_control(p, RXDATA0 | STROBE); /* Write Strobe=1 to command reg. */
/* 6 */
if ((parport_read_status(p) & ESTB) != ESTB)
@@ -481,7 +455,7 @@ static int portman_probe(struct parport *p)
parport_write_control(p, 0); /* Reset Strobe=0. */
/* Check if Tx circuitry is functioning properly. If initialized
- * unit TxEmpty is false, send out char and see if if goes true.
+ * unit TxEmpty is false, send out char and see if it goes true.
*/
/* 8 */
parport_write_control(p, TXDATA0); /* Tx channel 0, strobe off. */
@@ -522,45 +496,41 @@ static void snd_portman_midi_input_trigger(struct snd_rawmidi_substream *substre
int up)
{
struct portman *pm = substream->rmidi->private_data;
- unsigned long flags;
- spin_lock_irqsave(&pm->reg_lock, flags);
+ guard(spinlock_irqsave)(&pm->reg_lock);
if (up)
pm->mode[substream->number] |= PORTMAN2X4_MODE_INPUT_TRIGGERED;
else
pm->mode[substream->number] &= ~PORTMAN2X4_MODE_INPUT_TRIGGERED;
- spin_unlock_irqrestore(&pm->reg_lock, flags);
}
static void snd_portman_midi_output_trigger(struct snd_rawmidi_substream *substream,
int up)
{
struct portman *pm = substream->rmidi->private_data;
- unsigned long flags;
unsigned char byte;
- spin_lock_irqsave(&pm->reg_lock, flags);
+ guard(spinlock_irqsave)(&pm->reg_lock);
if (up) {
while ((snd_rawmidi_transmit(substream, &byte, 1) == 1))
portman_write_midi(pm, substream->number, byte);
}
- spin_unlock_irqrestore(&pm->reg_lock, flags);
}
-static struct snd_rawmidi_ops snd_portman_midi_output = {
+static const struct snd_rawmidi_ops snd_portman_midi_output = {
.open = snd_portman_midi_open,
.close = snd_portman_midi_close,
.trigger = snd_portman_midi_output_trigger,
};
-static struct snd_rawmidi_ops snd_portman_midi_input = {
+static const struct snd_rawmidi_ops snd_portman_midi_input = {
.open = snd_portman_midi_open,
.close = snd_portman_midi_close,
.trigger = snd_portman_midi_input_trigger,
};
/* Create and initialize the rawmidi component */
-static int __devinit snd_portman_rawmidi_create(struct snd_card *card)
+static int snd_portman_rawmidi_create(struct snd_card *card)
{
struct portman *pm = card->private_data;
struct snd_rawmidi *rmidi;
@@ -575,7 +545,7 @@ static int __devinit snd_portman_rawmidi_create(struct snd_card *card)
return err;
rmidi->private_data = pm;
- strcpy(rmidi->name, CARD_NAME);
+ strscpy(rmidi->name, CARD_NAME);
rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_DUPLEX;
@@ -616,7 +586,7 @@ static void snd_portman_interrupt(void *userdata)
unsigned char midivalue = 0;
struct portman *pm = ((struct snd_card*)userdata)->private_data;
- spin_lock(&pm->reg_lock);
+ guard(spinlock)(&pm->reg_lock);
/* While any input data is waiting */
while ((portman_read_status(pm) & INT_REQ) == INT_REQ) {
@@ -643,35 +613,9 @@ static void snd_portman_interrupt(void *userdata)
}
}
-
- spin_unlock(&pm->reg_lock);
}
-static int __devinit snd_portman_probe_port(struct parport *p)
-{
- struct pardevice *pardev;
- int res;
-
- pardev = parport_register_device(p, DRIVER_NAME,
- NULL, NULL, NULL,
- 0, NULL);
- if (!pardev)
- return -EIO;
-
- if (parport_claim(pardev)) {
- parport_unregister_device(pardev);
- return -EIO;
- }
-
- res = portman_probe(p);
-
- parport_release(pardev);
- parport_unregister_device(pardev);
-
- return res ? -EIO : 0;
-}
-
-static void __devinit snd_portman_attach(struct parport *p)
+static void snd_portman_attach(struct parport *p)
{
struct platform_device *device;
@@ -704,10 +648,19 @@ static void snd_portman_detach(struct parport *p)
/* nothing to do here */
}
+static int snd_portman_dev_probe(struct pardevice *pardev)
+{
+ if (strcmp(pardev->name, DRIVER_NAME))
+ return -ENODEV;
+
+ return 0;
+}
+
static struct parport_driver portman_parport_driver = {
- .name = "portman2x4",
- .attach = snd_portman_attach,
- .detach = snd_portman_detach
+ .name = "portman2x4",
+ .probe = snd_portman_dev_probe,
+ .match_port = snd_portman_attach,
+ .detach = snd_portman_detach,
};
/*********************************************************************
@@ -719,15 +672,14 @@ static void snd_portman_card_private_free(struct snd_card *card)
struct pardevice *pardev = pm->pardev;
if (pardev) {
- if (pm->pardev_claimed)
- parport_release(pardev);
+ parport_release(pardev);
parport_unregister_device(pardev);
}
portman_free(pm);
}
-static int __devinit snd_portman_probe(struct platform_device *pdev)
+static int snd_portman_probe(struct platform_device *pdev)
{
struct pardevice *pardev;
struct parport *p;
@@ -735,6 +687,12 @@ static int __devinit snd_portman_probe(struct platform_device *pdev)
struct snd_card *card = NULL;
struct portman *pm = NULL;
int err;
+ struct pardev_cb portman_cb = {
+ .preempt = NULL,
+ .wakeup = NULL,
+ .irq_func = snd_portman_interrupt, /* ISR */
+ .flags = PARPORT_DEV_EXCL, /* flags */
+ };
p = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
@@ -744,91 +702,95 @@ static int __devinit snd_portman_probe(struct platform_device *pdev)
if (!enable[dev])
return -ENOENT;
- if ((err = snd_portman_probe_port(p)) < 0)
- return err;
-
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
if (err < 0) {
- snd_printd("Cannot create card\n");
+ dev_dbg(&pdev->dev, "Cannot create card\n");
return err;
}
- strcpy(card->driver, DRIVER_NAME);
- strcpy(card->shortname, CARD_NAME);
+ strscpy(card->driver, DRIVER_NAME);
+ strscpy(card->shortname, CARD_NAME);
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, p->base, p->irq);
- pardev = parport_register_device(p, /* port */
- DRIVER_NAME, /* name */
- NULL, /* preempt */
- NULL, /* wakeup */
- snd_portman_interrupt, /* ISR */
- PARPORT_DEV_EXCL, /* flags */
- (void *)card); /* private */
+ portman_cb.private = card; /* private */
+ pardev = parport_register_dev_model(p, /* port */
+ DRIVER_NAME, /* name */
+ &portman_cb, /* callbacks */
+ pdev->id); /* device number */
if (pardev == NULL) {
- snd_printd("Cannot register pardevice\n");
+ dev_dbg(card->dev, "Cannot register pardevice\n");
err = -EIO;
goto __err;
}
- if ((err = portman_create(card, pardev, &pm)) < 0) {
- snd_printd("Cannot create main component\n");
- parport_unregister_device(pardev);
- goto __err;
+ /* claim parport */
+ if (parport_claim(pardev)) {
+ dev_dbg(card->dev, "Cannot claim parport 0x%lx\n", pardev->port->base);
+ err = -EIO;
+ goto free_pardev;
+ }
+
+ err = portman_create(card, pardev, &pm);
+ if (err < 0) {
+ dev_dbg(card->dev, "Cannot create main component\n");
+ goto release_pardev;
}
card->private_data = pm;
card->private_free = snd_portman_card_private_free;
-
- if ((err = snd_portman_rawmidi_create(card)) < 0) {
- snd_printd("Creating Rawmidi component failed\n");
- goto __err;
- }
- /* claim parport */
- if (parport_claim(pardev)) {
- snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
+ err = portman_probe(p);
+ if (err) {
err = -EIO;
goto __err;
}
- pm->pardev_claimed = 1;
+
+ err = snd_portman_rawmidi_create(card);
+ if (err < 0) {
+ dev_dbg(card->dev, "Creating Rawmidi component failed\n");
+ goto __err;
+ }
/* init device */
- if ((err = portman_device_init(pm)) < 0)
+ err = portman_device_init(pm);
+ if (err < 0)
goto __err;
platform_set_drvdata(pdev, card);
- snd_card_set_dev(card, &pdev->dev);
-
/* At this point card will be usable */
- if ((err = snd_card_register(card)) < 0) {
- snd_printd("Cannot register card\n");
+ err = snd_card_register(card);
+ if (err < 0) {
+ dev_dbg(card->dev, "Cannot register card\n");
goto __err;
}
- snd_printk(KERN_INFO "Portman 2x4 on 0x%lx\n", p->base);
+ dev_info(card->dev, "Portman 2x4 on 0x%lx\n", p->base);
return 0;
+release_pardev:
+ parport_release(pardev);
+free_pardev:
+ parport_unregister_device(pardev);
__err:
snd_card_free(card);
return err;
}
-static int __devexit snd_portman_remove(struct platform_device *pdev)
+static void snd_portman_remove(struct platform_device *pdev)
{
struct snd_card *card = platform_get_drvdata(pdev);
if (card)
snd_card_free(card);
-
- return 0;
}
static struct platform_driver snd_portman_driver = {
.probe = snd_portman_probe,
- .remove = __devexit_p(snd_portman_remove),
+ .remove = snd_portman_remove,
.driver = {
- .name = PLATFORM_DRIVER
+ .name = PLATFORM_DRIVER,
}
};
@@ -853,7 +815,8 @@ static int __init snd_portman_module_init(void)
{
int err;
- if ((err = platform_driver_register(&snd_portman_driver)) < 0)
+ err = platform_driver_register(&snd_portman_driver);
+ if (err < 0)
return err;
if (parport_register_driver(&portman_parport_driver) != 0) {
diff --git a/sound/drivers/serial-generic.c b/sound/drivers/serial-generic.c
new file mode 100644
index 000000000000..766206c6ca75
--- /dev/null
+++ b/sound/drivers/serial-generic.c
@@ -0,0 +1,376 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * serial-generic.c
+ * Copyright (c) by Daniel Kaehn <kaehndan@gmail.com
+ * Based on serial-u16550.c by Jaroslav Kysela <perex@perex.cz>,
+ * Isaku Yamahata <yamahata@private.email.ne.jp>,
+ * George Hansper <ghansper@apana.org.au>,
+ * Hannu Savolainen
+ *
+ * Generic serial MIDI driver using the serdev serial bus API for hardware interaction
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/serdev.h>
+#include <linux/serial_reg.h>
+#include <linux/slab.h>
+#include <linux/dev_printk.h>
+
+#include <sound/core.h>
+#include <sound/rawmidi.h>
+#include <sound/initval.h>
+
+MODULE_DESCRIPTION("Generic serial MIDI driver");
+MODULE_LICENSE("GPL");
+
+#define SERIAL_MODE_INPUT_OPEN 1
+#define SERIAL_MODE_OUTPUT_OPEN 2
+#define SERIAL_MODE_INPUT_TRIGGERED 3
+#define SERIAL_MODE_OUTPUT_TRIGGERED 4
+
+#define SERIAL_TX_STATE_ACTIVE 1
+#define SERIAL_TX_STATE_WAKEUP 2
+
+#define INTERNAL_BUF_SIZE 256
+
+struct snd_serial_generic {
+ struct serdev_device *serdev;
+
+ struct snd_card *card;
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_substream *midi_output;
+ struct snd_rawmidi_substream *midi_input;
+
+ unsigned int baudrate;
+
+ unsigned long filemode; /* open status of file */
+ struct work_struct tx_work;
+ unsigned long tx_state;
+
+ char tx_buf[INTERNAL_BUF_SIZE];
+};
+
+static void snd_serial_generic_tx_wakeup(struct snd_serial_generic *drvdata)
+{
+ if (test_and_set_bit(SERIAL_TX_STATE_ACTIVE, &drvdata->tx_state))
+ set_bit(SERIAL_TX_STATE_WAKEUP, &drvdata->tx_state);
+
+ schedule_work(&drvdata->tx_work);
+}
+
+static void snd_serial_generic_tx_work(struct work_struct *work)
+{
+ int num_bytes;
+ struct snd_serial_generic *drvdata = container_of(work, struct snd_serial_generic,
+ tx_work);
+ struct snd_rawmidi_substream *substream = drvdata->midi_output;
+
+ clear_bit(SERIAL_TX_STATE_WAKEUP, &drvdata->tx_state);
+
+ while (!snd_rawmidi_transmit_empty(substream)) {
+
+ if (!test_bit(SERIAL_MODE_OUTPUT_OPEN, &drvdata->filemode))
+ break;
+
+ num_bytes = snd_rawmidi_transmit_peek(substream, drvdata->tx_buf,
+ INTERNAL_BUF_SIZE);
+ num_bytes = serdev_device_write_buf(drvdata->serdev, drvdata->tx_buf,
+ num_bytes);
+
+ if (!num_bytes)
+ break;
+
+ snd_rawmidi_transmit_ack(substream, num_bytes);
+
+ if (!test_bit(SERIAL_TX_STATE_WAKEUP, &drvdata->tx_state))
+ break;
+ }
+
+ clear_bit(SERIAL_TX_STATE_ACTIVE, &drvdata->tx_state);
+}
+
+static void snd_serial_generic_write_wakeup(struct serdev_device *serdev)
+{
+ struct snd_serial_generic *drvdata = serdev_device_get_drvdata(serdev);
+
+ snd_serial_generic_tx_wakeup(drvdata);
+}
+
+static size_t snd_serial_generic_receive_buf(struct serdev_device *serdev,
+ const u8 *buf, size_t count)
+{
+ int ret;
+ struct snd_serial_generic *drvdata = serdev_device_get_drvdata(serdev);
+
+ if (!test_bit(SERIAL_MODE_INPUT_OPEN, &drvdata->filemode))
+ return 0;
+
+ ret = snd_rawmidi_receive(drvdata->midi_input, buf, count);
+ return ret < 0 ? 0 : ret;
+}
+
+static const struct serdev_device_ops snd_serial_generic_serdev_device_ops = {
+ .receive_buf = snd_serial_generic_receive_buf,
+ .write_wakeup = snd_serial_generic_write_wakeup
+};
+
+static int snd_serial_generic_ensure_serdev_open(struct snd_serial_generic *drvdata)
+{
+ int err;
+ unsigned int actual_baud;
+
+ if (drvdata->filemode)
+ return 0;
+
+ dev_dbg(drvdata->card->dev, "Opening serial port for card %s\n",
+ drvdata->card->shortname);
+ err = serdev_device_open(drvdata->serdev);
+ if (err < 0)
+ return err;
+
+ actual_baud = serdev_device_set_baudrate(drvdata->serdev,
+ drvdata->baudrate);
+ if (actual_baud != drvdata->baudrate) {
+ dev_warn(drvdata->card->dev, "requested %d baud for card %s but it was actually set to %d\n",
+ drvdata->baudrate, drvdata->card->shortname, actual_baud);
+ }
+
+ return 0;
+}
+
+static int snd_serial_generic_input_open(struct snd_rawmidi_substream *substream)
+{
+ int err;
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+
+ dev_dbg(drvdata->card->dev, "Opening input for card %s\n",
+ drvdata->card->shortname);
+
+ err = snd_serial_generic_ensure_serdev_open(drvdata);
+ if (err < 0)
+ return err;
+
+ set_bit(SERIAL_MODE_INPUT_OPEN, &drvdata->filemode);
+ drvdata->midi_input = substream;
+ return 0;
+}
+
+static int snd_serial_generic_input_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+
+ dev_dbg(drvdata->card->dev, "Closing input for card %s\n",
+ drvdata->card->shortname);
+
+ clear_bit(SERIAL_MODE_INPUT_OPEN, &drvdata->filemode);
+ clear_bit(SERIAL_MODE_INPUT_TRIGGERED, &drvdata->filemode);
+
+ drvdata->midi_input = NULL;
+
+ if (!drvdata->filemode)
+ serdev_device_close(drvdata->serdev);
+ return 0;
+}
+
+static void snd_serial_generic_input_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+
+ if (up)
+ set_bit(SERIAL_MODE_INPUT_TRIGGERED, &drvdata->filemode);
+ else
+ clear_bit(SERIAL_MODE_INPUT_TRIGGERED, &drvdata->filemode);
+}
+
+static int snd_serial_generic_output_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+ int err;
+
+ dev_dbg(drvdata->card->dev, "Opening output for card %s\n",
+ drvdata->card->shortname);
+
+ err = snd_serial_generic_ensure_serdev_open(drvdata);
+ if (err < 0)
+ return err;
+
+ set_bit(SERIAL_MODE_OUTPUT_OPEN, &drvdata->filemode);
+
+ drvdata->midi_output = substream;
+ return 0;
+};
+
+static int snd_serial_generic_output_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+
+ dev_dbg(drvdata->card->dev, "Closing output for card %s\n",
+ drvdata->card->shortname);
+
+ clear_bit(SERIAL_MODE_OUTPUT_OPEN, &drvdata->filemode);
+ clear_bit(SERIAL_MODE_OUTPUT_TRIGGERED, &drvdata->filemode);
+
+ if (!drvdata->filemode)
+ serdev_device_close(drvdata->serdev);
+
+ drvdata->midi_output = NULL;
+
+ return 0;
+};
+
+static void snd_serial_generic_output_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+
+ if (up)
+ set_bit(SERIAL_MODE_OUTPUT_TRIGGERED, &drvdata->filemode);
+ else
+ clear_bit(SERIAL_MODE_OUTPUT_TRIGGERED, &drvdata->filemode);
+
+ if (up)
+ snd_serial_generic_tx_wakeup(drvdata);
+}
+
+static void snd_serial_generic_output_drain(struct snd_rawmidi_substream *substream)
+{
+ struct snd_serial_generic *drvdata = substream->rmidi->card->private_data;
+
+ /* Flush any pending characters */
+ serdev_device_write_flush(drvdata->serdev);
+ cancel_work_sync(&drvdata->tx_work);
+}
+
+static const struct snd_rawmidi_ops snd_serial_generic_output = {
+ .open = snd_serial_generic_output_open,
+ .close = snd_serial_generic_output_close,
+ .trigger = snd_serial_generic_output_trigger,
+ .drain = snd_serial_generic_output_drain,
+};
+
+static const struct snd_rawmidi_ops snd_serial_generic_input = {
+ .open = snd_serial_generic_input_open,
+ .close = snd_serial_generic_input_close,
+ .trigger = snd_serial_generic_input_trigger,
+};
+
+static void snd_serial_generic_parse_dt(struct serdev_device *serdev,
+ struct snd_serial_generic *drvdata)
+{
+ int err;
+
+ err = of_property_read_u32(serdev->dev.of_node, "current-speed",
+ &drvdata->baudrate);
+ if (err < 0) {
+ dev_dbg(drvdata->card->dev,
+ "MIDI device reading of current-speed DT param failed with error %d, using default of 38400\n",
+ err);
+ drvdata->baudrate = 38400;
+ }
+
+}
+
+static void snd_serial_generic_substreams(struct snd_rawmidi_str *stream, int dev_num)
+{
+ struct snd_rawmidi_substream *substream;
+
+ list_for_each_entry(substream, &stream->substreams, list) {
+ sprintf(substream->name, "Serial MIDI %d-%d", dev_num, substream->number);
+ }
+}
+
+static int snd_serial_generic_rmidi(struct snd_serial_generic *drvdata,
+ int outs, int ins, struct snd_rawmidi **rmidi)
+{
+ struct snd_rawmidi *rrawmidi;
+ int err;
+
+ err = snd_rawmidi_new(drvdata->card, drvdata->card->driver, 0,
+ outs, ins, &rrawmidi);
+
+ if (err < 0)
+ return err;
+
+ snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &snd_serial_generic_input);
+ snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &snd_serial_generic_output);
+ strscpy(rrawmidi->name, drvdata->card->shortname);
+
+ snd_serial_generic_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT],
+ drvdata->serdev->ctrl->nr);
+ snd_serial_generic_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT],
+ drvdata->serdev->ctrl->nr);
+
+ rrawmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ if (rmidi)
+ *rmidi = rrawmidi;
+ return 0;
+}
+
+static int snd_serial_generic_probe(struct serdev_device *serdev)
+{
+ struct snd_card *card;
+ struct snd_serial_generic *drvdata;
+ int err;
+
+ err = snd_devm_card_new(&serdev->dev, SNDRV_DEFAULT_IDX1,
+ SNDRV_DEFAULT_STR1, THIS_MODULE,
+ sizeof(struct snd_serial_generic), &card);
+
+ if (err < 0)
+ return err;
+
+ strscpy(card->driver, "SerialMIDI");
+ sprintf(card->shortname, "SerialMIDI-%d", serdev->ctrl->nr);
+ sprintf(card->longname, "Serial MIDI device at serial%d", serdev->ctrl->nr);
+
+ drvdata = card->private_data;
+
+ drvdata->serdev = serdev;
+ drvdata->card = card;
+
+ snd_serial_generic_parse_dt(serdev, drvdata);
+
+ INIT_WORK(&drvdata->tx_work, snd_serial_generic_tx_work);
+
+ err = snd_serial_generic_rmidi(drvdata, 1, 1, &drvdata->rmidi);
+ if (err < 0)
+ return err;
+
+ serdev_device_set_client_ops(serdev, &snd_serial_generic_serdev_device_ops);
+ serdev_device_set_drvdata(drvdata->serdev, drvdata);
+
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static const struct of_device_id snd_serial_generic_dt_ids[] = {
+ { .compatible = "serial-midi" },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, snd_serial_generic_dt_ids);
+
+static struct serdev_device_driver snd_serial_generic_driver = {
+ .driver = {
+ .name = "snd-serial-generic",
+ .of_match_table = snd_serial_generic_dt_ids,
+ },
+ .probe = snd_serial_generic_probe,
+};
+
+module_serdev_device_driver(snd_serial_generic_driver);
diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c
index a25fb7b1f441..3c28961091b1 100644
--- a/sound/drivers/serial-u16550.c
+++ b/sound/drivers/serial-u16550.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* serial.c
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
@@ -7,20 +8,6 @@
*
* This code is based on the code from ALSA 0.5.9, but heavily rewritten.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
* Sat Mar 31 17:27:57 PST 2001 tim.mann@compaq.com
* Added support for the Midiator MS-124T and for the MS-124W in
* Single Addressed (S/A) or Multiple Burst (M/B) mode, with
@@ -36,7 +23,8 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/ioport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/rawmidi.h>
#include <sound/initval.h>
@@ -44,11 +32,8 @@
#include <linux/serial_reg.h>
#include <linux/jiffies.h>
-#include <asm/io.h>
-
MODULE_DESCRIPTION("MIDI serial u16550");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ALSA, MIDI serial u16550}}");
#define SNDRV_SERIAL_SOUNDCANVAS 0 /* Roland Soundcanvas; F5 NN selects part */
#define SNDRV_SERIAL_MS124T 1 /* Midiator MS-124T */
@@ -56,7 +41,7 @@ MODULE_SUPPORTED_DEVICE("{{ALSA, MIDI serial u16550}}");
#define SNDRV_SERIAL_MS124W_MB 3 /* Midiator MS-124W in M/B mode */
#define SNDRV_SERIAL_GENERIC 4 /* Generic Interface */
#define SNDRV_SERIAL_MAX_ADAPTOR SNDRV_SERIAL_GENERIC
-static char *adaptor_names[] = {
+static const char * const adaptor_names[] = {
"Soundcanvas",
"MS-124T",
"MS-124W S/A",
@@ -69,7 +54,7 @@ static char *adaptor_names[] = {
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x3f8,0x2f8,0x3e8,0x2e8 */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 3,4,5,7,9,10,11,14,15 */
static int speed[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 38400}; /* 9600,19200,38400,57600,115200 */
@@ -77,7 +62,7 @@ static int base[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 115200}; /* baud bas
static int outs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; /* 1 to 16 */
static int ins[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; /* 1 to 16 */
static int adaptor[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = SNDRV_SERIAL_SOUNDCANVAS};
-static int droponfull[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS -1)] = SNDRV_SERIAL_NORMALBUFF };
+static bool droponfull[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS -1)] = SNDRV_SERIAL_NORMALBUFF };
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Serial MIDI.");
@@ -85,9 +70,9 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for Serial MIDI.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable UART16550A chip.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for UART16550A chip.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for UART16550A chip.");
module_param_array(speed, int, NULL, 0444);
MODULE_PARM_DESC(speed, "Speed in bauds.");
@@ -130,7 +115,6 @@ struct snd_uart16550 {
int irq;
unsigned long base;
- struct resource *res_base;
unsigned int speed;
unsigned int speed_base;
@@ -174,16 +158,15 @@ static inline void snd_uart16550_add_timer(struct snd_uart16550 *uart)
{
if (!uart->timer_running) {
/* timer 38600bps * 10bit * 16byte */
- uart->buffer_timer.expires = jiffies + (HZ+255)/256;
+ mod_timer(&uart->buffer_timer, jiffies + (HZ + 255) / 256);
uart->timer_running = 1;
- add_timer(&uart->buffer_timer);
}
}
static inline void snd_uart16550_del_timer(struct snd_uart16550 *uart)
{
if (uart->timer_running) {
- del_timer(&uart->buffer_timer);
+ timer_delete(&uart->buffer_timer);
uart->timer_running = 0;
}
}
@@ -241,9 +224,9 @@ static void snd_uart16550_io_loop(struct snd_uart16550 * uart)
snd_rawmidi_receive(uart->midi_input[substream], &c, 1);
if (status & UART_LSR_OE)
- snd_printk(KERN_WARNING
- "%s: Overrun on device at 0x%lx\n",
- uart->rmidi->name, uart->base);
+ dev_warn(uart->card->dev,
+ "%s: Overrun on device at 0x%lx\n",
+ uart->rmidi->name, uart->base);
}
/* remember the last stream */
@@ -298,29 +281,24 @@ static irqreturn_t snd_uart16550_interrupt(int irq, void *dev_id)
struct snd_uart16550 *uart;
uart = dev_id;
- spin_lock(&uart->open_lock);
- if (uart->filemode == SERIAL_MODE_NOT_OPENED) {
- spin_unlock(&uart->open_lock);
+ guard(spinlock)(&uart->open_lock);
+ if (uart->filemode == SERIAL_MODE_NOT_OPENED)
return IRQ_NONE;
- }
/* indicate to the UART that the interrupt has been serviced */
inb(uart->base + UART_IIR);
snd_uart16550_io_loop(uart);
- spin_unlock(&uart->open_lock);
return IRQ_HANDLED;
}
/* When the polling mode, this function calls snd_uart16550_io_loop. */
-static void snd_uart16550_buffer_timer(unsigned long data)
+static void snd_uart16550_buffer_timer(struct timer_list *t)
{
- unsigned long flags;
struct snd_uart16550 *uart;
- uart = (struct snd_uart16550 *)data;
- spin_lock_irqsave(&uart->open_lock, flags);
+ uart = timer_container_of(uart, t, buffer_timer);
+ guard(spinlock_irqsave)(&uart->open_lock);
snd_uart16550_del_timer(uart);
snd_uart16550_io_loop(uart);
- spin_unlock_irqrestore(&uart->open_lock, flags);
}
/*
@@ -328,7 +306,7 @@ static void snd_uart16550_buffer_timer(unsigned long data)
* return 0 if found
* return negative error if not found
*/
-static int __devinit snd_uart16550_detect(struct snd_uart16550 *uart)
+static int snd_uart16550_detect(struct snd_uart16550 *uart)
{
unsigned long io_base = uart->base;
int ok;
@@ -339,9 +317,9 @@ static int __devinit snd_uart16550_detect(struct snd_uart16550 *uart)
return -ENODEV; /* Not configured */
}
- uart->res_base = request_region(io_base, 8, "Serial MIDI");
- if (uart->res_base == NULL) {
- snd_printk(KERN_ERR "u16550: can't grab port 0x%lx\n", io_base);
+ if (!devm_request_region(uart->card->dev, io_base, 8, "Serial MIDI")) {
+ dev_err(uart->card->dev,
+ "u16550: can't grab port 0x%lx\n", io_base);
return -EBUSY;
}
@@ -516,71 +494,61 @@ static void snd_uart16550_do_close(struct snd_uart16550 * uart)
static int snd_uart16550_input_open(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_uart16550 *uart = substream->rmidi->private_data;
- spin_lock_irqsave(&uart->open_lock, flags);
+ guard(spinlock_irqsave)(&uart->open_lock);
if (uart->filemode == SERIAL_MODE_NOT_OPENED)
snd_uart16550_do_open(uart);
uart->filemode |= SERIAL_MODE_INPUT_OPEN;
uart->midi_input[substream->number] = substream;
- spin_unlock_irqrestore(&uart->open_lock, flags);
return 0;
}
static int snd_uart16550_input_close(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_uart16550 *uart = substream->rmidi->private_data;
- spin_lock_irqsave(&uart->open_lock, flags);
+ guard(spinlock_irqsave)(&uart->open_lock);
uart->filemode &= ~SERIAL_MODE_INPUT_OPEN;
uart->midi_input[substream->number] = NULL;
if (uart->filemode == SERIAL_MODE_NOT_OPENED)
snd_uart16550_do_close(uart);
- spin_unlock_irqrestore(&uart->open_lock, flags);
return 0;
}
static void snd_uart16550_input_trigger(struct snd_rawmidi_substream *substream,
int up)
{
- unsigned long flags;
struct snd_uart16550 *uart = substream->rmidi->private_data;
- spin_lock_irqsave(&uart->open_lock, flags);
+ guard(spinlock_irqsave)(&uart->open_lock);
if (up)
uart->filemode |= SERIAL_MODE_INPUT_TRIGGERED;
else
uart->filemode &= ~SERIAL_MODE_INPUT_TRIGGERED;
- spin_unlock_irqrestore(&uart->open_lock, flags);
}
static int snd_uart16550_output_open(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_uart16550 *uart = substream->rmidi->private_data;
- spin_lock_irqsave(&uart->open_lock, flags);
+ guard(spinlock_irqsave)(&uart->open_lock);
if (uart->filemode == SERIAL_MODE_NOT_OPENED)
snd_uart16550_do_open(uart);
uart->filemode |= SERIAL_MODE_OUTPUT_OPEN;
uart->midi_output[substream->number] = substream;
- spin_unlock_irqrestore(&uart->open_lock, flags);
return 0;
};
static int snd_uart16550_output_close(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_uart16550 *uart = substream->rmidi->private_data;
- spin_lock_irqsave(&uart->open_lock, flags);
+ guard(spinlock_irqsave)(&uart->open_lock);
uart->filemode &= ~SERIAL_MODE_OUTPUT_OPEN;
uart->midi_output[substream->number] = NULL;
if (uart->filemode == SERIAL_MODE_NOT_OPENED)
snd_uart16550_do_close(uart);
- spin_unlock_irqrestore(&uart->open_lock, flags);
return 0;
};
@@ -637,9 +605,9 @@ static int snd_uart16550_output_byte(struct snd_uart16550 *uart,
}
} else {
if (!snd_uart16550_write_buffer(uart, midi_byte)) {
- snd_printk(KERN_WARNING
- "%s: Buffer overrun on device at 0x%lx\n",
- uart->rmidi->name, uart->base);
+ dev_warn(uart->card->dev,
+ "%s: Buffer overrun on device at 0x%lx\n",
+ uart->rmidi->name, uart->base);
return 0;
}
}
@@ -649,7 +617,6 @@ static int snd_uart16550_output_byte(struct snd_uart16550 *uart,
static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
unsigned char midi_byte, addr_byte;
struct snd_uart16550 *uart = substream->rmidi->private_data;
char first;
@@ -660,7 +627,7 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream)
* variables (ie buff_in & buff_out)
*/
- spin_lock_irqsave(&uart->open_lock, flags);
+ guard(spinlock_irqsave)(&uart->open_lock);
if (uart->irq < 0) /* polling */
snd_uart16550_io_loop(uart);
@@ -735,71 +702,52 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream)
}
lasttime = jiffies;
}
- spin_unlock_irqrestore(&uart->open_lock, flags);
}
static void snd_uart16550_output_trigger(struct snd_rawmidi_substream *substream,
int up)
{
- unsigned long flags;
struct snd_uart16550 *uart = substream->rmidi->private_data;
- spin_lock_irqsave(&uart->open_lock, flags);
- if (up)
- uart->filemode |= SERIAL_MODE_OUTPUT_TRIGGERED;
- else
- uart->filemode &= ~SERIAL_MODE_OUTPUT_TRIGGERED;
- spin_unlock_irqrestore(&uart->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &uart->open_lock) {
+ if (up)
+ uart->filemode |= SERIAL_MODE_OUTPUT_TRIGGERED;
+ else
+ uart->filemode &= ~SERIAL_MODE_OUTPUT_TRIGGERED;
+ }
if (up)
snd_uart16550_output_write(substream);
}
-static struct snd_rawmidi_ops snd_uart16550_output =
+static const struct snd_rawmidi_ops snd_uart16550_output =
{
.open = snd_uart16550_output_open,
.close = snd_uart16550_output_close,
.trigger = snd_uart16550_output_trigger,
};
-static struct snd_rawmidi_ops snd_uart16550_input =
+static const struct snd_rawmidi_ops snd_uart16550_input =
{
.open = snd_uart16550_input_open,
.close = snd_uart16550_input_close,
.trigger = snd_uart16550_input_trigger,
};
-static int snd_uart16550_free(struct snd_uart16550 *uart)
-{
- if (uart->irq >= 0)
- free_irq(uart->irq, uart);
- release_and_free_resource(uart->res_base);
- kfree(uart);
- return 0;
-};
-
-static int snd_uart16550_dev_free(struct snd_device *device)
-{
- struct snd_uart16550 *uart = device->device_data;
- return snd_uart16550_free(uart);
-}
-
-static int __devinit snd_uart16550_create(struct snd_card *card,
- unsigned long iobase,
- int irq,
- unsigned int speed,
- unsigned int base,
- int adaptor,
- int droponfull,
- struct snd_uart16550 **ruart)
+static int snd_uart16550_create(struct snd_card *card,
+ unsigned long iobase,
+ int irq,
+ unsigned int speed,
+ unsigned int base,
+ int adaptor,
+ int droponfull,
+ struct snd_uart16550 **ruart)
{
- static struct snd_device_ops ops = {
- .dev_free = snd_uart16550_dev_free,
- };
struct snd_uart16550 *uart;
int err;
- if ((uart = kzalloc(sizeof(*uart), GFP_KERNEL)) == NULL)
+ uart = devm_kzalloc(card->dev, sizeof(*uart), GFP_KERNEL);
+ if (!uart)
return -ENOMEM;
uart->adaptor = adaptor;
uart->card = card;
@@ -808,17 +756,17 @@ static int __devinit snd_uart16550_create(struct snd_card *card,
uart->base = iobase;
uart->drop_on_full = droponfull;
- if ((err = snd_uart16550_detect(uart)) <= 0) {
- printk(KERN_ERR "no UART detected at 0x%lx\n", iobase);
- snd_uart16550_free(uart);
+ err = snd_uart16550_detect(uart);
+ if (err <= 0) {
+ dev_err(card->dev, "no UART detected at 0x%lx\n", iobase);
return -ENODEV;
}
if (irq >= 0 && irq != SNDRV_AUTO_IRQ) {
- if (request_irq(irq, snd_uart16550_interrupt,
- IRQF_DISABLED, "Serial MIDI", uart)) {
- snd_printk(KERN_WARNING
- "irq %d busy. Using Polling.\n", irq);
+ if (devm_request_irq(card->dev, irq, snd_uart16550_interrupt,
+ 0, "Serial MIDI", uart)) {
+ dev_warn(card->dev,
+ "irq %d busy. Using Polling.\n", irq);
} else {
uart->irq = irq;
}
@@ -830,17 +778,9 @@ static int __devinit snd_uart16550_create(struct snd_card *card,
uart->prev_in = 0;
uart->rstatus = 0;
memset(uart->prev_status, 0x80, sizeof(unsigned char) * SNDRV_SERIAL_MAX_OUTS);
- init_timer(&uart->buffer_timer);
- uart->buffer_timer.function = snd_uart16550_buffer_timer;
- uart->buffer_timer.data = (unsigned long)uart;
+ timer_setup(&uart->buffer_timer, snd_uart16550_buffer_timer, 0);
uart->timer_running = 0;
- /* Register device */
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, uart, &ops)) < 0) {
- snd_uart16550_free(uart);
- return err;
- }
-
switch (uart->adaptor) {
case SNDRV_SERIAL_MS124W_SA:
case SNDRV_SERIAL_MS124W_MB:
@@ -863,7 +803,7 @@ static int __devinit snd_uart16550_create(struct snd_card *card,
return 0;
}
-static void __devinit snd_uart16550_substreams(struct snd_rawmidi_str *stream)
+static void snd_uart16550_substreams(struct snd_rawmidi_str *stream)
{
struct snd_rawmidi_substream *substream;
@@ -872,9 +812,9 @@ static void __devinit snd_uart16550_substreams(struct snd_rawmidi_str *stream)
}
}
-static int __devinit snd_uart16550_rmidi(struct snd_uart16550 *uart, int device,
- int outs, int ins,
- struct snd_rawmidi **rmidi)
+static int snd_uart16550_rmidi(struct snd_uart16550 *uart, int device,
+ int outs, int ins,
+ struct snd_rawmidi **rmidi)
{
struct snd_rawmidi *rrawmidi;
int err;
@@ -887,7 +827,7 @@ static int __devinit snd_uart16550_rmidi(struct snd_uart16550 *uart, int device,
&snd_uart16550_input);
snd_rawmidi_set_ops(rrawmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
&snd_uart16550_output);
- strcpy(rrawmidi->name, "Serial MIDI");
+ strscpy(rrawmidi->name, "Serial MIDI");
snd_uart16550_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]);
snd_uart16550_substreams(&rrawmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]);
rrawmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
@@ -899,7 +839,7 @@ static int __devinit snd_uart16550_rmidi(struct snd_uart16550 *uart, int device,
return 0;
}
-static int __devinit snd_serial_probe(struct platform_device *devptr)
+static int snd_serial_probe(struct platform_device *devptr)
{
struct snd_card *card;
struct snd_uart16550 *uart;
@@ -922,46 +862,43 @@ static int __devinit snd_serial_probe(struct platform_device *devptr)
case SNDRV_SERIAL_GENERIC:
break;
default:
- snd_printk(KERN_ERR
- "Adaptor type is out of range 0-%d (%d)\n",
- SNDRV_SERIAL_MAX_ADAPTOR, adaptor[dev]);
+ dev_err(&devptr->dev,
+ "Adaptor type is out of range 0-%d (%d)\n",
+ SNDRV_SERIAL_MAX_ADAPTOR, adaptor[dev]);
return -ENODEV;
}
if (outs[dev] < 1 || outs[dev] > SNDRV_SERIAL_MAX_OUTS) {
- snd_printk(KERN_ERR
- "Count of outputs is out of range 1-%d (%d)\n",
- SNDRV_SERIAL_MAX_OUTS, outs[dev]);
+ dev_err(&devptr->dev,
+ "Count of outputs is out of range 1-%d (%d)\n",
+ SNDRV_SERIAL_MAX_OUTS, outs[dev]);
return -ENODEV;
}
if (ins[dev] < 1 || ins[dev] > SNDRV_SERIAL_MAX_INS) {
- snd_printk(KERN_ERR
- "Count of inputs is out of range 1-%d (%d)\n",
- SNDRV_SERIAL_MAX_INS, ins[dev]);
+ dev_err(&devptr->dev,
+ "Count of inputs is out of range 1-%d (%d)\n",
+ SNDRV_SERIAL_MAX_INS, ins[dev]);
return -ENODEV;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
if (err < 0)
return err;
- strcpy(card->driver, "Serial");
- strcpy(card->shortname, "Serial MIDI (UART16550A)");
+ strscpy(card->driver, "Serial");
+ strscpy(card->shortname, "Serial MIDI (UART16550A)");
- if ((err = snd_uart16550_create(card,
- port[dev],
- irq[dev],
- speed[dev],
- base[dev],
- adaptor[dev],
- droponfull[dev],
- &uart)) < 0)
- goto _err;
+ err = snd_uart16550_create(card, port[dev], irq[dev], speed[dev],
+ base[dev], adaptor[dev], droponfull[dev],
+ &uart);
+ if (err < 0)
+ return err;
err = snd_uart16550_rmidi(uart, 0, outs[dev], ins[dev], &uart->rmidi);
if (err < 0)
- goto _err;
+ return err;
sprintf(card->longname, "%s [%s] at %#lx, irq %d",
card->shortname,
@@ -969,33 +906,20 @@ static int __devinit snd_serial_probe(struct platform_device *devptr)
uart->base,
uart->irq);
- snd_card_set_dev(card, &devptr->dev);
-
- if ((err = snd_card_register(card)) < 0)
- goto _err;
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
platform_set_drvdata(devptr, card);
return 0;
-
- _err:
- snd_card_free(card);
- return err;
-}
-
-static int __devexit snd_serial_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
- return 0;
}
#define SND_SERIAL_DRIVER "snd_serial_u16550"
static struct platform_driver snd_serial_driver = {
.probe = snd_serial_probe,
- .remove = __devexit_p( snd_serial_remove),
.driver = {
- .name = SND_SERIAL_DRIVER
+ .name = SND_SERIAL_DRIVER,
},
};
@@ -1012,7 +936,8 @@ static int __init alsa_card_serial_init(void)
{
int i, cards, err;
- if ((err = platform_driver_register(&snd_serial_driver)) < 0)
+ err = platform_driver_register(&snd_serial_driver);
+ if (err < 0)
return err;
cards = 0;
@@ -1033,7 +958,7 @@ static int __init alsa_card_serial_init(void)
}
if (! cards) {
#ifdef MODULE
- printk(KERN_ERR "serial midi soundcard not found or device busy\n");
+ pr_err("serial midi soundcard not found or device busy\n");
#endif
snd_serial_unregister_all();
return -ENODEV;
diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c
index f4cd49336f33..a204f42d1026 100644
--- a/sound/drivers/virmidi.c
+++ b/sound/drivers/virmidi.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Dummy soundcard for virtual rawmidi devices
*
* Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -45,7 +31,7 @@
#include <linux/wait.h>
#include <linux/err.h>
#include <linux/platform_device.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/seq_kernel.h>
#include <sound/seq_virmidi.h>
@@ -57,13 +43,12 @@
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("Dummy soundcard for virtual rawmidi devices");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ALSA,Virtual rawmidi device}}");
#define MAX_MIDI_DEVICES 4
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
+static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4};
module_param_array(index, int, NULL, 0444);
@@ -83,56 +68,45 @@ struct snd_card_virmidi {
static struct platform_device *devices[SNDRV_CARDS];
-static int __devinit snd_virmidi_probe(struct platform_device *devptr)
+static int snd_virmidi_probe(struct platform_device *devptr)
{
struct snd_card *card;
struct snd_card_virmidi *vmidi;
int idx, err;
int dev = devptr->id;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_virmidi), &card);
+ err = snd_devm_card_new(&devptr->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_virmidi), &card);
if (err < 0)
return err;
vmidi = card->private_data;
vmidi->card = card;
if (midi_devs[dev] > MAX_MIDI_DEVICES) {
- snd_printk(KERN_WARNING
- "too much midi devices for virmidi %d: "
- "force to use %d\n", dev, MAX_MIDI_DEVICES);
+ dev_warn(&devptr->dev,
+ "too much midi devices for virmidi %d: force to use %d\n",
+ dev, MAX_MIDI_DEVICES);
midi_devs[dev] = MAX_MIDI_DEVICES;
}
for (idx = 0; idx < midi_devs[dev]; idx++) {
struct snd_rawmidi *rmidi;
- struct snd_virmidi_dev *rdev;
- if ((err = snd_virmidi_new(card, idx, &rmidi)) < 0)
- goto __nodev;
- rdev = rmidi->private_data;
+
+ err = snd_virmidi_new(card, idx, &rmidi);
+ if (err < 0)
+ return err;
vmidi->midi[idx] = rmidi;
- strcpy(rmidi->name, "Virtual Raw MIDI");
- rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
+ strscpy(rmidi->name, "Virtual Raw MIDI");
}
-
- strcpy(card->driver, "VirMIDI");
- strcpy(card->shortname, "VirMIDI");
- sprintf(card->longname, "Virtual MIDI Card %i", dev + 1);
- snd_card_set_dev(card, &devptr->dev);
+ strscpy(card->driver, "VirMIDI");
+ strscpy(card->shortname, "VirMIDI");
+ sprintf(card->longname, "Virtual MIDI Card %i", dev + 1);
- if ((err = snd_card_register(card)) == 0) {
- platform_set_drvdata(devptr, card);
- return 0;
- }
- __nodev:
- snd_card_free(card);
- return err;
-}
+ err = snd_card_register(card);
+ if (err)
+ return err;
-static int __devexit snd_virmidi_remove(struct platform_device *devptr)
-{
- snd_card_free(platform_get_drvdata(devptr));
- platform_set_drvdata(devptr, NULL);
+ platform_set_drvdata(devptr, card);
return 0;
}
@@ -140,9 +114,8 @@ static int __devexit snd_virmidi_remove(struct platform_device *devptr)
static struct platform_driver snd_virmidi_driver = {
.probe = snd_virmidi_probe,
- .remove = __devexit_p(snd_virmidi_remove),
.driver = {
- .name = SND_VIRMIDI_DRIVER
+ .name = SND_VIRMIDI_DRIVER,
},
};
@@ -159,13 +132,15 @@ static int __init alsa_card_virmidi_init(void)
{
int i, cards, err;
- if ((err = platform_driver_register(&snd_virmidi_driver)) < 0)
+ err = platform_driver_register(&snd_virmidi_driver);
+ if (err < 0)
return err;
cards = 0;
for (i = 0; i < SNDRV_CARDS; i++) {
struct platform_device *device;
- if (! enable[i])
+
+ if (!enable[i])
continue;
device = platform_device_register_simple(SND_VIRMIDI_DRIVER,
i, NULL, 0);
@@ -180,7 +155,7 @@ static int __init alsa_card_virmidi_init(void)
}
if (!cards) {
#ifdef MODULE
- printk(KERN_ERR "Card-VirMIDI soundcard not found or device busy\n");
+ pr_err("Card-VirMIDI soundcard not found or device busy\n");
#endif
snd_virmidi_unregister_all();
return -ENODEV;
diff --git a/sound/drivers/vx/Makefile b/sound/drivers/vx/Makefile
index 9a168a3c1560..ae1b3e09283f 100644
--- a/sound/drivers/vx/Makefile
+++ b/sound/drivers/vx/Makefile
@@ -1,8 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-vx-lib-objs := vx_core.o vx_hwdep.o vx_pcm.o vx_mixer.o vx_cmd.o vx_uer.o
+snd-vx-lib-y := vx_core.o vx_hwdep.o vx_pcm.o vx_mixer.o vx_cmd.o vx_uer.o
obj-$(CONFIG_SND_VX_LIB) += snd-vx-lib.o
diff --git a/sound/drivers/vx/vx_cmd.c b/sound/drivers/vx/vx_cmd.c
index 23f4857f02c8..b0970a04883e 100644
--- a/sound/drivers/vx/vx_cmd.c
+++ b/sound/drivers/vx/vx_cmd.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram VX soundcards
*
* DSP commands
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sound/core.h>
@@ -28,7 +15,7 @@
/*
* Array of DSP commands
*/
-static struct vx_cmd_info vx_dsp_cmds[] = {
+static const struct vx_cmd_info vx_dsp_cmds[] = {
[CMD_VERSION] = { 0x010000, 2, RMH_SSIZE_FIXED, 1 },
[CMD_SUPPORTED] = { 0x020000, 1, RMH_SSIZE_FIXED, 2 },
[CMD_TEST_IT] = { 0x040000, 1, RMH_SSIZE_FIXED, 1 },
diff --git a/sound/drivers/vx/vx_cmd.h b/sound/drivers/vx/vx_cmd.h
index a85248ba3cc5..c2a520274493 100644
--- a/sound/drivers/vx/vx_cmd.h
+++ b/sound/drivers/vx/vx_cmd.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Driver for Digigram VX soundcards
*
* Definitions of DSP commands
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __VX_CMD_H
diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c
index 19c6e376c7c7..52b93407bfe3 100644
--- a/sound/drivers/vx/vx_core.c
+++ b/sound/drivers/vx/vx_core.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram VX soundcards
*
* Hardware core part
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/delay.h>
@@ -26,11 +13,12 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/asoundef.h>
#include <sound/info.h>
-#include <asm/io.h>
#include <sound/vx_core.h>
#include "vx_cmd.h"
@@ -51,21 +39,22 @@ MODULE_LICENSE("GPL");
int snd_vx_check_reg_bit(struct vx_core *chip, int reg, int mask, int bit, int time)
{
unsigned long end_time = jiffies + (time * HZ + 999) / 1000;
-#ifdef CONFIG_SND_DEBUG
- static char *reg_names[VX_REG_MAX] = {
+ static const char * const reg_names[VX_REG_MAX] = {
"ICR", "CVR", "ISR", "IVR", "RXH", "RXM", "RXL",
"DMA", "CDSP", "RFREQ", "RUER/V2", "DATA", "MEMIRQ",
"ACQ", "BIT0", "BIT1", "MIC0", "MIC1", "MIC2",
"MIC3", "INTCSR", "CNTRL", "GPIOC",
"LOFREQ", "HIFREQ", "CSUER", "RUER"
};
-#endif
+
do {
if ((snd_vx_inb(chip, reg) & mask) == bit)
return 0;
//msleep(10);
} while (time_after_eq(end_time, jiffies));
- snd_printd(KERN_DEBUG "vx_check_reg_bit: timeout, reg=%s, mask=0x%x, val=0x%x\n", reg_names[reg], mask, snd_vx_inb(chip, reg));
+ dev_dbg(chip->card->dev,
+ "vx_check_reg_bit: timeout, reg=%s, mask=0x%x, val=0x%x\n",
+ reg_names[reg], mask, snd_vx_inb(chip, reg));
return -EIO;
}
@@ -117,33 +106,39 @@ static int vx_reset_chk(struct vx_core *chip)
*
* returns 0 if successful, or a negative error code.
* the error code can be VX-specific, retrieved via vx_get_error().
- * NB: call with spinlock held!
+ * NB: call with mutex held!
*/
static int vx_transfer_end(struct vx_core *chip, int cmd)
{
int err;
- if ((err = vx_reset_chk(chip)) < 0)
+ err = vx_reset_chk(chip);
+ if (err < 0)
return err;
/* irq MESS_READ/WRITE_END */
- if ((err = vx_send_irq_dsp(chip, cmd)) < 0)
+ err = vx_send_irq_dsp(chip, cmd);
+ if (err < 0)
return err;
/* Wait CHK = 1 */
- if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0)
+ err = vx_wait_isr_bit(chip, ISR_CHK);
+ if (err < 0)
return err;
/* If error, Read RX */
- if ((err = vx_inb(chip, ISR)) & ISR_ERR) {
- if ((err = vx_wait_for_rx_full(chip)) < 0) {
- snd_printd(KERN_DEBUG "transfer_end: error in rx_full\n");
+ err = vx_inb(chip, ISR);
+ if (err & ISR_ERR) {
+ err = vx_wait_for_rx_full(chip);
+ if (err < 0) {
+ dev_dbg(chip->card->dev,
+ "transfer_end: error in rx_full\n");
return err;
}
err = vx_inb(chip, RXH) << 16;
err |= vx_inb(chip, RXM) << 8;
err |= vx_inb(chip, RXL);
- snd_printd(KERN_DEBUG "transfer_end: error = 0x%x\n", err);
+ dev_dbg(chip->card->dev, "transfer_end: error = 0x%x\n", err);
return -(VX_ERR_MASK | err);
}
return 0;
@@ -155,7 +150,7 @@ static int vx_transfer_end(struct vx_core *chip, int cmd)
*
* returns 0 if successful, or a negative error code.
* the error code can be VX-specific, retrieved via vx_get_error().
- * NB: call with spinlock held!
+ * NB: call with mutex held!
*/
static int vx_read_status(struct vx_core *chip, struct vx_rmh *rmh)
{
@@ -205,7 +200,7 @@ static int vx_read_status(struct vx_core *chip, struct vx_rmh *rmh)
if (size < 1)
return 0;
- if (snd_BUG_ON(size > SIZE_MAX_STATUS))
+ if (snd_BUG_ON(size >= SIZE_MAX_STATUS))
return -EINVAL;
for (i = 1; i <= size; i++) {
@@ -236,7 +231,7 @@ static int vx_read_status(struct vx_core *chip, struct vx_rmh *rmh)
* returns 0 if successful, or a negative error code.
* the error code can be VX-specific, retrieved via vx_get_error().
*
- * this function doesn't call spinlock at all.
+ * this function doesn't call mutex lock at all.
*/
int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
{
@@ -245,21 +240,12 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
if (chip->chip_status & VX_STAT_IS_STALE)
return -EBUSY;
- if ((err = vx_reset_chk(chip)) < 0) {
- snd_printd(KERN_DEBUG "vx_send_msg: vx_reset_chk error\n");
+ err = vx_reset_chk(chip);
+ if (err < 0) {
+ dev_dbg(chip->card->dev, "vx_send_msg: vx_reset_chk error\n");
return err;
}
-#if 0
- printk(KERN_DEBUG "rmh: cmd = 0x%06x, length = %d, stype = %d\n",
- rmh->Cmd[0], rmh->LgCmd, rmh->DspStat);
- if (rmh->LgCmd > 1) {
- printk(KERN_DEBUG " ");
- for (i = 1; i < rmh->LgCmd; i++)
- printk("0x%06x ", rmh->Cmd[i]);
- printk("\n");
- }
-#endif
/* Check bit M is set according to length of the command */
if (rmh->LgCmd > 1)
rmh->Cmd[0] |= MASK_MORE_THAN_1_WORD_COMMAND;
@@ -267,8 +253,9 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
rmh->Cmd[0] &= MASK_1_WORD_COMMAND;
/* Wait for TX empty */
- if ((err = vx_wait_isr_bit(chip, ISR_TX_EMPTY)) < 0) {
- snd_printd(KERN_DEBUG "vx_send_msg: wait tx empty error\n");
+ err = vx_wait_isr_bit(chip, ISR_TX_EMPTY);
+ if (err < 0) {
+ dev_dbg(chip->card->dev, "vx_send_msg: wait tx empty error\n");
return err;
}
@@ -278,25 +265,31 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
vx_outb(chip, TXL, rmh->Cmd[0] & 0xff);
/* Trigger irq MESSAGE */
- if ((err = vx_send_irq_dsp(chip, IRQ_MESSAGE)) < 0) {
- snd_printd(KERN_DEBUG "vx_send_msg: send IRQ_MESSAGE error\n");
+ err = vx_send_irq_dsp(chip, IRQ_MESSAGE);
+ if (err < 0) {
+ dev_dbg(chip->card->dev,
+ "vx_send_msg: send IRQ_MESSAGE error\n");
return err;
}
/* Wait for CHK = 1 */
- if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0)
+ err = vx_wait_isr_bit(chip, ISR_CHK);
+ if (err < 0)
return err;
/* If error, get error value from RX */
if (vx_inb(chip, ISR) & ISR_ERR) {
- if ((err = vx_wait_for_rx_full(chip)) < 0) {
- snd_printd(KERN_DEBUG "vx_send_msg: rx_full read error\n");
+ err = vx_wait_for_rx_full(chip);
+ if (err < 0) {
+ dev_dbg(chip->card->dev,
+ "vx_send_msg: rx_full read error\n");
return err;
}
err = vx_inb(chip, RXH) << 16;
err |= vx_inb(chip, RXM) << 8;
err |= vx_inb(chip, RXL);
- snd_printd(KERN_DEBUG "msg got error = 0x%x at cmd[0]\n", err);
+ dev_dbg(chip->card->dev,
+ "msg got error = 0x%x at cmd[0]\n", err);
err = -(VX_ERR_MASK | err);
return err;
}
@@ -305,8 +298,10 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
if (rmh->LgCmd > 1) {
for (i = 1; i < rmh->LgCmd; i++) {
/* Wait for TX ready */
- if ((err = vx_wait_isr_bit(chip, ISR_TX_READY)) < 0) {
- snd_printd(KERN_DEBUG "vx_send_msg: tx_ready error\n");
+ err = vx_wait_isr_bit(chip, ISR_TX_READY);
+ if (err < 0) {
+ dev_dbg(chip->card->dev,
+ "vx_send_msg: tx_ready error\n");
return err;
}
@@ -316,14 +311,18 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
vx_outb(chip, TXL, rmh->Cmd[i] & 0xff);
/* Trigger irq MESS_READ_NEXT */
- if ((err = vx_send_irq_dsp(chip, IRQ_MESS_READ_NEXT)) < 0) {
- snd_printd(KERN_DEBUG "vx_send_msg: IRQ_READ_NEXT error\n");
+ err = vx_send_irq_dsp(chip, IRQ_MESS_READ_NEXT);
+ if (err < 0) {
+ dev_dbg(chip->card->dev,
+ "vx_send_msg: IRQ_READ_NEXT error\n");
return err;
}
}
/* Wait for TX empty */
- if ((err = vx_wait_isr_bit(chip, ISR_TX_READY)) < 0) {
- snd_printd(KERN_DEBUG "vx_send_msg: TX_READY error\n");
+ err = vx_wait_isr_bit(chip, ISR_TX_READY);
+ if (err < 0) {
+ dev_dbg(chip->card->dev,
+ "vx_send_msg: TX_READY error\n");
return err;
}
/* End of transfer */
@@ -337,7 +336,7 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
/*
- * vx_send_msg - send a DSP message with spinlock
+ * vx_send_msg - send a DSP message with mutex
* @rmh: the rmh record to send and receive
*
* returns 0 if successful, or a negative error code.
@@ -345,13 +344,8 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
*/
int vx_send_msg(struct vx_core *chip, struct vx_rmh *rmh)
{
- unsigned long flags;
- int err;
-
- spin_lock_irqsave(&chip->lock, flags);
- err = vx_send_msg_nolock(chip, rmh);
- spin_unlock_irqrestore(&chip->lock, flags);
- return err;
+ guard(mutex)(&chip->lock);
+ return vx_send_msg_nolock(chip, rmh);
}
@@ -362,7 +356,7 @@ int vx_send_msg(struct vx_core *chip, struct vx_rmh *rmh)
* returns 0 if successful, or a negative error code.
* the error code can be VX-specific, retrieved via vx_get_error().
*
- * this function doesn't call spinlock at all.
+ * this function doesn't call mutex at all.
*
* unlike RMH, no command is sent to DSP.
*/
@@ -373,20 +367,21 @@ int vx_send_rih_nolock(struct vx_core *chip, int cmd)
if (chip->chip_status & VX_STAT_IS_STALE)
return -EBUSY;
-#if 0
- printk(KERN_DEBUG "send_rih: cmd = 0x%x\n", cmd);
-#endif
- if ((err = vx_reset_chk(chip)) < 0)
+ err = vx_reset_chk(chip);
+ if (err < 0)
return err;
/* send the IRQ */
- if ((err = vx_send_irq_dsp(chip, cmd)) < 0)
+ err = vx_send_irq_dsp(chip, cmd);
+ if (err < 0)
return err;
/* Wait CHK = 1 */
- if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0)
+ err = vx_wait_isr_bit(chip, ISR_CHK);
+ if (err < 0)
return err;
/* If error, read RX */
if (vx_inb(chip, ISR) & ISR_ERR) {
- if ((err = vx_wait_for_rx_full(chip)) < 0)
+ err = vx_wait_for_rx_full(chip);
+ if (err < 0)
return err;
err = vx_inb(chip, RXH) << 16;
err |= vx_inb(chip, RXM) << 8;
@@ -398,26 +393,22 @@ int vx_send_rih_nolock(struct vx_core *chip, int cmd)
/*
- * vx_send_rih - send an RIH with spinlock
+ * vx_send_rih - send an RIH with mutex
* @cmd: the command to send
*
* see vx_send_rih_nolock().
*/
int vx_send_rih(struct vx_core *chip, int cmd)
{
- unsigned long flags;
- int err;
-
- spin_lock_irqsave(&chip->lock, flags);
- err = vx_send_rih_nolock(chip, cmd);
- spin_unlock_irqrestore(&chip->lock, flags);
- return err;
+ guard(mutex)(&chip->lock);
+ return vx_send_rih_nolock(chip, cmd);
}
#define END_OF_RESET_WAIT_TIME 500 /* us */
/**
- * snd_vx_boot_xilinx - boot up the xilinx interface
+ * snd_vx_load_boot_image - boot up the xilinx interface
+ * @chip: VX core instance
* @boot: the boot record to load
*/
int snd_vx_load_boot_image(struct vx_core *chip, const struct firmware *boot)
@@ -450,7 +441,7 @@ int snd_vx_load_boot_image(struct vx_core *chip, const struct firmware *boot)
if (no_fillup)
break;
if (vx_wait_isr_bit(chip, ISR_TX_EMPTY) < 0) {
- snd_printk(KERN_ERR "dsp boot failed at %d\n", i);
+ dev_err(chip->card->dev, "dsp boot failed at %d\n", i);
return -EIO;
}
vx_outb(chip, TXH, 0);
@@ -459,7 +450,7 @@ int snd_vx_load_boot_image(struct vx_core *chip, const struct firmware *boot)
} else {
const unsigned char *image = boot->data + i;
if (vx_wait_isr_bit(chip, ISR_TX_EMPTY) < 0) {
- snd_printk(KERN_ERR "dsp boot failed at %d\n", i);
+ dev_err(chip->card->dev, "dsp boot failed at %d\n", i);
return -EIO;
}
vx_outb(chip, TXH, image[0]);
@@ -482,51 +473,45 @@ static int vx_test_irq_src(struct vx_core *chip, unsigned int *ret)
int err;
vx_init_rmh(&chip->irq_rmh, CMD_TEST_IT);
- spin_lock(&chip->lock);
+ guard(mutex)(&chip->lock);
err = vx_send_msg_nolock(chip, &chip->irq_rmh);
if (err < 0)
*ret = 0;
else
*ret = chip->irq_rmh.Stat[0];
- spin_unlock(&chip->lock);
return err;
}
/*
- * vx_interrupt - soft irq handler
+ * snd_vx_threaded_irq_handler - threaded irq handler
*/
-static void vx_interrupt(unsigned long private_data)
+irqreturn_t snd_vx_threaded_irq_handler(int irq, void *dev)
{
- struct vx_core *chip = (struct vx_core *) private_data;
+ struct vx_core *chip = dev;
unsigned int events;
if (chip->chip_status & VX_STAT_IS_STALE)
- return;
+ return IRQ_HANDLED;
if (vx_test_irq_src(chip, &events) < 0)
- return;
+ return IRQ_HANDLED;
-#if 0
- if (events & 0x000800)
- printk(KERN_ERR "DSP Stream underrun ! IRQ events = 0x%x\n", events);
-#endif
- // printk(KERN_DEBUG "IRQ events = 0x%x\n", events);
-
/* We must prevent any application using this DSP
* and block any further request until the application
* either unregisters or reloads the DSP
*/
if (events & FATAL_DSP_ERROR) {
- snd_printk(KERN_ERR "vx_core: fatal DSP error!!\n");
- return;
+ dev_err(chip->card->dev, "vx_core: fatal DSP error!!\n");
+ return IRQ_HANDLED;
}
/* The start on time code conditions are filled (ie the time code
* received by the board is equal to one of those given to it).
*/
- if (events & TIME_CODE_EVENT_PENDING)
+ if (events & TIME_CODE_EVENT_PENDING) {
; /* so far, nothing to do yet */
+ }
/* The frequency has changed on the board (UER mode). */
if (events & FREQUENCY_CHANGE_EVENT_PENDING)
@@ -534,11 +519,14 @@ static void vx_interrupt(unsigned long private_data)
/* update the pcm streams */
vx_pcm_update_intr(chip, events);
+ return IRQ_HANDLED;
}
-
+EXPORT_SYMBOL(snd_vx_threaded_irq_handler);
/**
* snd_vx_irq_handler - interrupt handler
+ * @irq: irq number
+ * @dev: VX core instance
*/
irqreturn_t snd_vx_irq_handler(int irq, void *dev)
{
@@ -548,8 +536,8 @@ irqreturn_t snd_vx_irq_handler(int irq, void *dev)
(chip->chip_status & VX_STAT_IS_STALE))
return IRQ_NONE;
if (! vx_test_and_ack(chip))
- tasklet_schedule(&chip->tq);
- return IRQ_HANDLED;
+ return IRQ_WAKE_THREAD;
+ return IRQ_NONE;
}
EXPORT_SYMBOL(snd_vx_irq_handler);
@@ -599,17 +587,17 @@ static void vx_reset_board(struct vx_core *chip, int cold_reset)
static void vx_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
{
struct vx_core *chip = entry->private_data;
- static char *audio_src_vxp[] = { "Line", "Mic", "Digital" };
- static char *audio_src_vx2[] = { "Analog", "Analog", "Digital" };
- static char *clock_mode[] = { "Auto", "Internal", "External" };
- static char *clock_src[] = { "Internal", "External" };
- static char *uer_type[] = { "Consumer", "Professional", "Not Present" };
+ static const char * const audio_src_vxp[] = { "Line", "Mic", "Digital" };
+ static const char * const audio_src_vx2[] = { "Analog", "Analog", "Digital" };
+ static const char * const clock_mode[] = { "Auto", "Internal", "External" };
+ static const char * const clock_src[] = { "Internal", "External" };
+ static const char * const uer_type[] = { "Consumer", "Professional", "Not Present" };
snd_iprintf(buffer, "%s\n", chip->card->longname);
snd_iprintf(buffer, "Xilinx Firmware: %s\n",
- chip->chip_status & VX_STAT_XILINX_LOADED ? "Loaded" : "No");
+ (chip->chip_status & VX_STAT_XILINX_LOADED) ? "Loaded" : "No");
snd_iprintf(buffer, "Device Initialized: %s\n",
- chip->chip_status & VX_STAT_DEVICE_INIT ? "Yes" : "No");
+ (chip->chip_status & VX_STAT_DEVICE_INIT) ? "Yes" : "No");
snd_iprintf(buffer, "DSP audio info:");
if (chip->audio_info & VX_AUDIO_INFO_REAL_TIME)
snd_iprintf(buffer, " realtime");
@@ -641,15 +629,14 @@ static void vx_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *b
static void vx_proc_init(struct vx_core *chip)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(chip->card, "vx-status", &entry))
- snd_info_set_text_ops(entry, chip, vx_proc_read);
+ snd_card_ro_proc_new(chip->card, "vx-status", chip, vx_proc_read);
}
/**
* snd_vx_dsp_boot - load the DSP boot
+ * @chip: VX core instance
+ * @boot: firmware data
*/
int snd_vx_dsp_boot(struct vx_core *chip, const struct firmware *boot)
{
@@ -659,7 +646,8 @@ int snd_vx_dsp_boot(struct vx_core *chip, const struct firmware *boot)
vx_reset_board(chip, cold_reset);
vx_validate_irq(chip, 0);
- if ((err = snd_vx_load_boot_image(chip, boot)) < 0)
+ err = snd_vx_load_boot_image(chip, boot);
+ if (err < 0)
return err;
msleep(10);
@@ -670,6 +658,8 @@ EXPORT_SYMBOL(snd_vx_dsp_boot);
/**
* snd_vx_dsp_load - load the DSP image
+ * @chip: VX core instance
+ * @dsp: firmware data
*/
int snd_vx_dsp_load(struct vx_core *chip, const struct firmware *dsp)
{
@@ -687,9 +677,10 @@ int snd_vx_dsp_load(struct vx_core *chip, const struct firmware *dsp)
for (i = 0; i < dsp->size; i += 3) {
image = dsp->data + i;
/* Wait DSP ready for a new read */
- if ((err = vx_wait_isr_bit(chip, ISR_TX_EMPTY)) < 0) {
- printk(KERN_ERR
- "dsp loading error at position %d\n", i);
+ err = vx_wait_isr_bit(chip, ISR_TX_EMPTY);
+ if (err < 0) {
+ dev_err(chip->card->dev,
+ "dsp loading error at position %d\n", i);
return err;
}
cptr = image;
@@ -703,11 +694,11 @@ int snd_vx_dsp_load(struct vx_core *chip, const struct firmware *dsp)
csum = (csum >> 24) | (csum << 8);
vx_outb(chip, TXL, *cptr++);
}
- snd_printdd(KERN_DEBUG "checksum = 0x%08x\n", csum);
msleep(200);
- if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0)
+ err = vx_wait_isr_bit(chip, ISR_CHK);
+ if (err < 0)
return err;
vx_toggle_dac_mute(chip, 0);
@@ -724,14 +715,10 @@ EXPORT_SYMBOL(snd_vx_dsp_load);
/*
* suspend
*/
-int snd_vx_suspend(struct vx_core *chip, pm_message_t state)
+int snd_vx_suspend(struct vx_core *chip)
{
- unsigned int i;
-
snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
chip->chip_status |= VX_STAT_IN_SUSPEND;
- for (i = 0; i < chip->hw->num_codecs; i++)
- snd_pcm_suspend_all(chip->pcm[i]);
return 0;
}
@@ -752,7 +739,8 @@ int snd_vx_resume(struct vx_core *chip)
continue;
err = chip->ops->load_dsp(chip, i, chip->firmware[i]);
if (err < 0) {
- snd_printk(KERN_ERR "vx: firmware resume error at DSP %d\n", i);
+ dev_err(chip->card->dev,
+ "vx: firmware resume error at DSP %d\n", i);
return -EIO;
}
}
@@ -767,17 +755,28 @@ int snd_vx_resume(struct vx_core *chip)
EXPORT_SYMBOL(snd_vx_resume);
#endif
+static void snd_vx_release(struct device *dev, void *data)
+{
+ snd_vx_free_firmware(data);
+}
+
/**
* snd_vx_create - constructor for struct vx_core
+ * @card: card instance
* @hw: hardware specific record
+ * @ops: VX ops pointer
+ * @extra_size: extra byte size to allocate appending to chip
*
* this function allocates the instance and prepare for the hardware
* initialization.
*
+ * The object is managed via devres, and will be automatically released.
+ *
* return the instance pointer if successful, NULL in error.
*/
-struct vx_core *snd_vx_create(struct snd_card *card, struct snd_vx_hardware *hw,
- struct snd_vx_ops *ops,
+struct vx_core *snd_vx_create(struct snd_card *card,
+ const struct snd_vx_hardware *hw,
+ const struct snd_vx_ops *ops,
int extra_size)
{
struct vx_core *chip;
@@ -785,23 +784,20 @@ struct vx_core *snd_vx_create(struct snd_card *card, struct snd_vx_hardware *hw,
if (snd_BUG_ON(!card || !hw || !ops))
return NULL;
- chip = kzalloc(sizeof(*chip) + extra_size, GFP_KERNEL);
- if (! chip) {
- snd_printk(KERN_ERR "vx_core: no memory\n");
+ chip = devres_alloc(snd_vx_release, sizeof(*chip) + extra_size,
+ GFP_KERNEL);
+ if (!chip)
return NULL;
- }
- spin_lock_init(&chip->lock);
- spin_lock_init(&chip->irq_lock);
+ mutex_init(&chip->lock);
chip->irq = -1;
chip->hw = hw;
chip->type = hw->type;
chip->ops = ops;
- tasklet_init(&chip->tq, vx_interrupt, (unsigned long)chip);
mutex_init(&chip->mixer_mutex);
chip->card = card;
card->private_data = chip;
- strcpy(card->driver, hw->name);
+ strscpy(card->driver, hw->name);
sprintf(card->shortname, "Digigram %s", hw->name);
vx_proc_init(chip);
@@ -810,18 +806,3 @@ struct vx_core *snd_vx_create(struct snd_card *card, struct snd_vx_hardware *hw,
}
EXPORT_SYMBOL(snd_vx_create);
-
-/*
- * module entries
- */
-static int __init alsa_vx_core_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_vx_core_exit(void)
-{
-}
-
-module_init(alsa_vx_core_init)
-module_exit(alsa_vx_core_exit)
diff --git a/sound/drivers/vx/vx_hwdep.c b/sound/drivers/vx/vx_hwdep.c
index f7a6fbd313e3..a7f8ddf4df5a 100644
--- a/sound/drivers/vx/vx_hwdep.c
+++ b/sound/drivers/vx/vx_hwdep.c
@@ -1,35 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram VX soundcards
*
* DSP firmware management
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/hwdep.h>
#include <sound/vx_core.h>
-#ifdef SND_VX_FW_LOADER
-
MODULE_FIRMWARE("vx/bx_1_vxp.b56");
MODULE_FIRMWARE("vx/bx_1_vp4.b56");
MODULE_FIRMWARE("vx/x1_1_vx2.xlx");
@@ -46,7 +32,7 @@ MODULE_FIRMWARE("vx/l_1_vp4.d56");
int snd_vx_setup_firmware(struct vx_core *chip)
{
- static char *fw_files[VX_TYPE_NUMS][4] = {
+ static const char * const fw_files[VX_TYPE_NUMS][4] = {
[VX_TYPE_BOARD] = {
NULL, "x1_1_vx2.xlx", "bd56002.boot", "l_1_vx2.d56",
},
@@ -72,8 +58,8 @@ int snd_vx_setup_firmware(struct vx_core *chip)
if (! fw_files[chip->type][i])
continue;
sprintf(path, "vx/%s", fw_files[chip->type][i]);
- if (request_firmware(&fw, path, chip->dev)) {
- snd_printk(KERN_ERR "vx: can't load firmware %s\n", path);
+ if (request_firmware(&fw, path, chip->card->dev)) {
+ dev_err(chip->card->dev, "vx: can't load firmware %s\n", path);
return -ENOENT;
}
err = chip->ops->load_dsp(chip, i, fw);
@@ -92,15 +78,19 @@ int snd_vx_setup_firmware(struct vx_core *chip)
/* ok, we reached to the last one */
/* create the devices if not built yet */
- if ((err = snd_vx_pcm_new(chip)) < 0)
+ err = snd_vx_pcm_new(chip);
+ if (err < 0)
return err;
- if ((err = snd_vx_mixer_new(chip)) < 0)
+ err = snd_vx_mixer_new(chip);
+ if (err < 0)
return err;
- if (chip->ops->add_controls)
- if ((err = chip->ops->add_controls(chip)) < 0)
+ if (chip->ops->add_controls) {
+ err = chip->ops->add_controls(chip);
+ if (err < 0)
return err;
+ }
chip->chip_status |= VX_STAT_DEVICE_INIT;
chip->chip_status |= VX_STAT_CHIP_INIT;
@@ -118,142 +108,5 @@ void snd_vx_free_firmware(struct vx_core *chip)
#endif
}
-#else /* old style firmware loading */
-
-static int vx_hwdep_dsp_status(struct snd_hwdep *hw,
- struct snd_hwdep_dsp_status *info)
-{
- static char *type_ids[VX_TYPE_NUMS] = {
- [VX_TYPE_BOARD] = "vxboard",
- [VX_TYPE_V2] = "vx222",
- [VX_TYPE_MIC] = "vx222",
- [VX_TYPE_VXPOCKET] = "vxpocket",
- [VX_TYPE_VXP440] = "vxp440",
- };
- struct vx_core *vx = hw->private_data;
-
- if (snd_BUG_ON(!type_ids[vx->type]))
- return -EINVAL;
- strcpy(info->id, type_ids[vx->type]);
- if (vx_is_pcmcia(vx))
- info->num_dsps = 4;
- else
- info->num_dsps = 3;
- if (vx->chip_status & VX_STAT_CHIP_INIT)
- info->chip_ready = 1;
- info->version = VX_DRIVER_VERSION;
- return 0;
-}
-
-static void free_fw(const struct firmware *fw)
-{
- if (fw) {
- vfree(fw->data);
- kfree(fw);
- }
-}
-
-static int vx_hwdep_dsp_load(struct snd_hwdep *hw,
- struct snd_hwdep_dsp_image *dsp)
-{
- struct vx_core *vx = hw->private_data;
- int index, err;
- struct firmware *fw;
-
- if (snd_BUG_ON(!vx->ops->load_dsp))
- return -ENXIO;
-
- fw = kmalloc(sizeof(*fw), GFP_KERNEL);
- if (! fw) {
- snd_printk(KERN_ERR "cannot allocate firmware\n");
- return -ENOMEM;
- }
- fw->size = dsp->length;
- fw->data = vmalloc(fw->size);
- if (! fw->data) {
- snd_printk(KERN_ERR "cannot allocate firmware image (length=%d)\n",
- (int)fw->size);
- kfree(fw);
- return -ENOMEM;
- }
- if (copy_from_user((void *)fw->data, dsp->image, dsp->length)) {
- free_fw(fw);
- return -EFAULT;
- }
-
- index = dsp->index;
- if (! vx_is_pcmcia(vx))
- index++;
- err = vx->ops->load_dsp(vx, index, fw);
- if (err < 0) {
- free_fw(fw);
- return err;
- }
-#ifdef CONFIG_PM
- vx->firmware[index] = fw;
-#else
- free_fw(fw);
-#endif
-
- if (index == 1)
- vx->chip_status |= VX_STAT_XILINX_LOADED;
- if (index < 3)
- return 0;
-
- /* ok, we reached to the last one */
- /* create the devices if not built yet */
- if (! (vx->chip_status & VX_STAT_DEVICE_INIT)) {
- if ((err = snd_vx_pcm_new(vx)) < 0)
- return err;
-
- if ((err = snd_vx_mixer_new(vx)) < 0)
- return err;
-
- if (vx->ops->add_controls)
- if ((err = vx->ops->add_controls(vx)) < 0)
- return err;
-
- if ((err = snd_card_register(vx->card)) < 0)
- return err;
-
- vx->chip_status |= VX_STAT_DEVICE_INIT;
- }
- vx->chip_status |= VX_STAT_CHIP_INIT;
- return 0;
-}
-
-
-/* exported */
-int snd_vx_setup_firmware(struct vx_core *chip)
-{
- int err;
- struct snd_hwdep *hw;
-
- if ((err = snd_hwdep_new(chip->card, SND_VX_HWDEP_ID, 0, &hw)) < 0)
- return err;
-
- hw->iface = SNDRV_HWDEP_IFACE_VX;
- hw->private_data = chip;
- hw->ops.dsp_status = vx_hwdep_dsp_status;
- hw->ops.dsp_load = vx_hwdep_dsp_load;
- hw->exclusive = 1;
- sprintf(hw->name, "VX Loader (%s)", chip->card->driver);
- chip->hwdep = hw;
-
- return snd_card_register(chip->card);
-}
-
-/* exported */
-void snd_vx_free_firmware(struct vx_core *chip)
-{
-#ifdef CONFIG_PM
- int i;
- for (i = 0; i < 4; i++)
- free_fw(chip->firmware[i]);
-#endif
-}
-
-#endif /* SND_VX_FW_LOADER */
-
EXPORT_SYMBOL(snd_vx_setup_firmware);
EXPORT_SYMBOL(snd_vx_free_firmware);
diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c
index c71b8d148d7f..9dc5cecaa86a 100644
--- a/sound/drivers/vx/vx_mixer.c
+++ b/sound/drivers/vx/vx_mixer.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram VX soundcards
*
* Common mixer part
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sound/core.h>
@@ -32,17 +19,14 @@
*/
static void vx_write_codec_reg(struct vx_core *chip, int codec, unsigned int data)
{
- unsigned long flags;
-
if (snd_BUG_ON(!chip->ops->write_codec))
return;
if (chip->chip_status & VX_STAT_IS_STALE)
return;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(mutex)(&chip->lock);
chip->ops->write_codec(chip, codec, data);
- spin_unlock_irqrestore(&chip->lock, flags);
}
/*
@@ -178,14 +162,11 @@ void vx_reset_codec(struct vx_core *chip, int cold_reset)
*/
static void vx_change_audio_source(struct vx_core *chip, int src)
{
- unsigned long flags;
-
if (chip->chip_status & VX_STAT_IS_STALE)
return;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(mutex)(&chip->lock);
chip->ops->change_audio_source(chip, src);
- spin_unlock_irqrestore(&chip->lock, flags);
}
@@ -428,10 +409,10 @@ static int vx_output_level_get(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
{
struct vx_core *chip = snd_kcontrol_chip(kcontrol);
int codec = kcontrol->id.index;
- mutex_lock(&chip->mixer_mutex);
+
+ guard(mutex)(&chip->mixer_mutex);
ucontrol->value.integer.value[0] = chip->output_level[codec][0];
ucontrol->value.integer.value[1] = chip->output_level[codec][1];
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -446,20 +427,18 @@ static int vx_output_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
val[1] = ucontrol->value.integer.value[1];
if (val[0] > vmax || val[1] > vmax)
return -EINVAL;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
if (val[0] != chip->output_level[codec][0] ||
val[1] != chip->output_level[codec][1]) {
vx_set_analog_output_level(chip, codec, val[0], val[1]);
chip->output_level[codec][0] = val[0];
chip->output_level[codec][1] = val[1];
- mutex_unlock(&chip->mixer_mutex);
return 1;
}
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
-static struct snd_kcontrol_new vx_control_output_level = {
+static const struct snd_kcontrol_new vx_control_output_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -475,30 +454,18 @@ static struct snd_kcontrol_new vx_control_output_level = {
*/
static int vx_audio_src_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts_mic[3] = {
+ static const char * const texts_mic[3] = {
"Digital", "Line", "Mic"
};
- static char *texts_vx2[2] = {
+ static const char * const texts_vx2[2] = {
"Digital", "Analog"
};
struct vx_core *chip = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- if (chip->type >= VX_TYPE_VXPOCKET) {
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name,
- texts_mic[uinfo->value.enumerated.item]);
- } else {
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name,
- texts_vx2[uinfo->value.enumerated.item]);
- }
- return 0;
+ if (chip->type >= VX_TYPE_VXPOCKET)
+ return snd_ctl_enum_info(uinfo, 1, 3, texts_mic);
+ else
+ return snd_ctl_enum_info(uinfo, 1, 2, texts_vx2);
}
static int vx_audio_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -519,18 +486,16 @@ static int vx_audio_src_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
if (ucontrol->value.enumerated.item[0] > 1)
return -EINVAL;
}
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
if (chip->audio_source_target != ucontrol->value.enumerated.item[0]) {
chip->audio_source_target = ucontrol->value.enumerated.item[0];
vx_sync_audio_source(chip);
- mutex_unlock(&chip->mixer_mutex);
return 1;
}
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
-static struct snd_kcontrol_new vx_control_audio_src = {
+static const struct snd_kcontrol_new vx_control_audio_src = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.info = vx_audio_src_info,
@@ -543,18 +508,11 @@ static struct snd_kcontrol_new vx_control_audio_src = {
*/
static int vx_clock_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {
+ static const char * const texts[3] = {
"Auto", "Internal", "External"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int vx_clock_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -570,18 +528,16 @@ static int vx_clock_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
if (ucontrol->value.enumerated.item[0] > 2)
return -EINVAL;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
if (chip->clock_mode != ucontrol->value.enumerated.item[0]) {
chip->clock_mode = ucontrol->value.enumerated.item[0];
vx_set_clock(chip, chip->freq);
- mutex_unlock(&chip->mixer_mutex);
return 1;
}
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
-static struct snd_kcontrol_new vx_control_clock_mode = {
+static const struct snd_kcontrol_new vx_control_clock_mode = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Clock Mode",
.info = vx_clock_mode_info,
@@ -607,10 +563,9 @@ static int vx_audio_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
int audio = kcontrol->private_value & 0xff;
int capture = (kcontrol->private_value >> 8) & 1;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
ucontrol->value.integer.value[0] = chip->audio_gain[capture][audio];
ucontrol->value.integer.value[1] = chip->audio_gain[capture][audio+1];
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -625,15 +580,13 @@ static int vx_audio_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
val[1] = ucontrol->value.integer.value[1];
if (val[0] > CVAL_MAX || val[1] > CVAL_MAX)
return -EINVAL;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
if (val[0] != chip->audio_gain[capture][audio] ||
val[1] != chip->audio_gain[capture][audio+1]) {
vx_set_audio_gain(chip, audio, capture, val[0]);
vx_set_audio_gain(chip, audio+1, capture, val[1]);
- mutex_unlock(&chip->mixer_mutex);
return 1;
}
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -642,10 +595,9 @@ static int vx_audio_monitor_get(struct snd_kcontrol *kcontrol, struct snd_ctl_el
struct vx_core *chip = snd_kcontrol_chip(kcontrol);
int audio = kcontrol->private_value & 0xff;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
ucontrol->value.integer.value[0] = chip->audio_monitor[audio];
ucontrol->value.integer.value[1] = chip->audio_monitor[audio+1];
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -660,17 +612,15 @@ static int vx_audio_monitor_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
if (val[0] > CVAL_MAX || val[1] > CVAL_MAX)
return -EINVAL;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
if (val[0] != chip->audio_monitor[audio] ||
val[1] != chip->audio_monitor[audio+1]) {
vx_set_monitor_level(chip, audio, val[0],
chip->audio_monitor_active[audio]);
vx_set_monitor_level(chip, audio+1, val[1],
chip->audio_monitor_active[audio+1]);
- mutex_unlock(&chip->mixer_mutex);
return 1;
}
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -681,10 +631,9 @@ static int vx_audio_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
struct vx_core *chip = snd_kcontrol_chip(kcontrol);
int audio = kcontrol->private_value & 0xff;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
ucontrol->value.integer.value[0] = chip->audio_active[audio];
ucontrol->value.integer.value[1] = chip->audio_active[audio+1];
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -693,17 +642,15 @@ static int vx_audio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
struct vx_core *chip = snd_kcontrol_chip(kcontrol);
int audio = kcontrol->private_value & 0xff;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
if (ucontrol->value.integer.value[0] != chip->audio_active[audio] ||
ucontrol->value.integer.value[1] != chip->audio_active[audio+1]) {
vx_set_audio_switch(chip, audio,
!!ucontrol->value.integer.value[0]);
vx_set_audio_switch(chip, audio+1,
!!ucontrol->value.integer.value[1]);
- mutex_unlock(&chip->mixer_mutex);
return 1;
}
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -712,10 +659,9 @@ static int vx_monitor_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
struct vx_core *chip = snd_kcontrol_chip(kcontrol);
int audio = kcontrol->private_value & 0xff;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
ucontrol->value.integer.value[0] = chip->audio_monitor_active[audio];
ucontrol->value.integer.value[1] = chip->audio_monitor_active[audio+1];
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -724,23 +670,21 @@ static int vx_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
struct vx_core *chip = snd_kcontrol_chip(kcontrol);
int audio = kcontrol->private_value & 0xff;
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
if (ucontrol->value.integer.value[0] != chip->audio_monitor_active[audio] ||
ucontrol->value.integer.value[1] != chip->audio_monitor_active[audio+1]) {
vx_set_monitor_level(chip, audio, chip->audio_monitor[audio],
!!ucontrol->value.integer.value[0]);
vx_set_monitor_level(chip, audio+1, chip->audio_monitor[audio+1],
!!ucontrol->value.integer.value[1]);
- mutex_unlock(&chip->mixer_mutex);
return 1;
}
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
static const DECLARE_TLV_DB_SCALE(db_scale_audio_gain, -10975, 25, 0);
-static struct snd_kcontrol_new vx_control_audio_gain = {
+static const struct snd_kcontrol_new vx_control_audio_gain = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -750,14 +694,14 @@ static struct snd_kcontrol_new vx_control_audio_gain = {
.put = vx_audio_gain_put,
.tlv = { .p = db_scale_audio_gain },
};
-static struct snd_kcontrol_new vx_control_output_switch = {
+static const struct snd_kcontrol_new vx_control_output_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
.info = vx_audio_sw_info,
.get = vx_audio_sw_get,
.put = vx_audio_sw_put
};
-static struct snd_kcontrol_new vx_control_monitor_gain = {
+static const struct snd_kcontrol_new vx_control_monitor_gain = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitoring Volume",
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -767,7 +711,7 @@ static struct snd_kcontrol_new vx_control_monitor_gain = {
.put = vx_audio_monitor_put,
.tlv = { .p = db_scale_audio_gain },
};
-static struct snd_kcontrol_new vx_control_monitor_switch = {
+static const struct snd_kcontrol_new vx_control_monitor_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitoring Switch",
.info = vx_audio_sw_info, /* shared */
@@ -790,12 +734,11 @@ static int vx_iec958_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
{
struct vx_core *chip = snd_kcontrol_chip(kcontrol);
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
ucontrol->value.iec958.status[0] = (chip->uer_bits >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (chip->uer_bits >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (chip->uer_bits >> 16) & 0xff;
ucontrol->value.iec958.status[3] = (chip->uer_bits >> 24) & 0xff;
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
@@ -817,18 +760,16 @@ static int vx_iec958_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
(ucontrol->value.iec958.status[1] << 8) |
(ucontrol->value.iec958.status[2] << 16) |
(ucontrol->value.iec958.status[3] << 24);
- mutex_lock(&chip->mixer_mutex);
+ guard(mutex)(&chip->mixer_mutex);
if (chip->uer_bits != val) {
chip->uer_bits = val;
vx_set_iec958_status(chip, val);
- mutex_unlock(&chip->mixer_mutex);
return 1;
}
- mutex_unlock(&chip->mixer_mutex);
return 0;
}
-static struct snd_kcontrol_new vx_control_iec958_mask = {
+static const struct snd_kcontrol_new vx_control_iec958_mask = {
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
@@ -836,7 +777,7 @@ static struct snd_kcontrol_new vx_control_iec958_mask = {
.get = vx_iec958_mask_get,
};
-static struct snd_kcontrol_new vx_control_iec958 = {
+static const struct snd_kcontrol_new vx_control_iec958 = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
.info = vx_iec958_info,
@@ -901,7 +842,7 @@ static int vx_saturation_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
return 0;
}
-static struct snd_kcontrol_new vx_control_vu_meter = {
+static const struct snd_kcontrol_new vx_control_vu_meter = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
/* name will be filled later */
@@ -909,7 +850,7 @@ static struct snd_kcontrol_new vx_control_vu_meter = {
.get = vx_vu_meter_get,
};
-static struct snd_kcontrol_new vx_control_peak_meter = {
+static const struct snd_kcontrol_new vx_control_peak_meter = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
/* name will be filled later */
@@ -917,7 +858,7 @@ static struct snd_kcontrol_new vx_control_peak_meter = {
.get = vx_peak_meter_get,
};
-static struct snd_kcontrol_new vx_control_saturation = {
+static const struct snd_kcontrol_new vx_control_saturation = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Saturation",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
@@ -939,14 +880,15 @@ int snd_vx_mixer_new(struct vx_core *chip)
struct snd_card *card = chip->card;
char name[32];
- strcpy(card->mixername, card->driver);
+ strscpy(card->mixername, card->driver);
/* output level controls */
for (i = 0; i < chip->hw->num_outs; i++) {
temp = vx_control_output_level;
temp.index = i;
temp.tlv.p = chip->hw->output_level_db_scale;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
}
@@ -957,22 +899,26 @@ int snd_vx_mixer_new(struct vx_core *chip)
temp.index = i;
temp.name = "PCM Playback Volume";
temp.private_value = val;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
temp = vx_control_output_switch;
temp.index = i;
temp.private_value = val;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
temp = vx_control_monitor_gain;
temp.index = i;
temp.private_value = val;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
temp = vx_control_monitor_switch;
temp.index = i;
temp.private_value = val;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
}
for (i = 0; i < chip->hw->num_outs; i++) {
@@ -980,31 +926,37 @@ int snd_vx_mixer_new(struct vx_core *chip)
temp.index = i;
temp.name = "PCM Capture Volume";
temp.private_value = (i * 2) | (1 << 8);
- if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
}
/* Audio source */
- if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_audio_src, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&vx_control_audio_src, chip));
+ if (err < 0)
return err;
/* clock mode */
- if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_clock_mode, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&vx_control_clock_mode, chip));
+ if (err < 0)
return err;
/* IEC958 controls */
- if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958_mask, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958_mask, chip));
+ if (err < 0)
return err;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958, chip));
+ if (err < 0)
return err;
/* VU, peak, saturation meters */
for (c = 0; c < 2; c++) {
- static char *dir[2] = { "Output", "Input" };
+ static const char * const dir[2] = { "Output", "Input" };
for (i = 0; i < chip->hw->num_ins; i++) {
int val = (i * 2) | (c << 8);
if (c == 1) {
temp = vx_control_saturation;
temp.index = i;
temp.private_value = val;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
}
sprintf(name, "%s VU Meter", dir[c]);
@@ -1012,14 +964,16 @@ int snd_vx_mixer_new(struct vx_core *chip)
temp.index = i;
temp.name = name;
temp.private_value = val;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
sprintf(name, "%s Peak Meter", dir[c]);
temp = vx_control_peak_meter;
temp.index = i;
temp.name = name;
temp.private_value = val;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
+ if (err < 0)
return err;
}
}
diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c
index 35a2f71a6af5..7fd8f413d6cf 100644
--- a/sound/drivers/vx/vx_pcm.c
+++ b/sound/drivers/vx/vx_pcm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram VX soundcards
*
@@ -5,21 +6,6 @@
*
* Copyright (c) 2002,2003 by Takashi Iwai <tiwai@suse.de>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- *
* STRATEGY
* for playback, we send series of "chunks", which size is equal with the
* IBL size, typically 126 samples. at each end of chunk, the end-of-buffer
@@ -39,7 +25,6 @@
* the current point of read buffer is kept in pipe->hw_ptr. note that
* this is in bytes.
*
- *
* TODO
* - linked trigger for full-duplex mode.
* - scheduled action on the stream.
@@ -75,7 +60,6 @@ static void vx_pcm_read_per_bytes(struct vx_core *chip, struct snd_pcm_runtime *
*buf++ = vx_inb(chip, RXL);
if (++offset >= pipe->buffer_bytes) {
offset = 0;
- buf = (unsigned char *)runtime->dma_area;
}
pipe->hw_ptr = offset;
}
@@ -184,7 +168,7 @@ static int vx_set_format(struct vx_core *chip, struct vx_pipe *pipe,
default :
snd_BUG();
return -EINVAL;
- };
+ }
return vx_set_stream_format(chip, pipe, header);
}
@@ -206,8 +190,10 @@ static int vx_set_ibl(struct vx_core *chip, struct vx_ibl_info *info)
info->max_size = rmh.Stat[1];
info->min_size = rmh.Stat[2];
info->granularity = rmh.Stat[3];
- snd_printdd(KERN_DEBUG "vx_set_ibl: size = %d, max = %d, min = %d, gran = %d\n",
- info->size, info->max_size, info->min_size, info->granularity);
+ dev_dbg(chip->card->dev,
+ "%s: size = %d, max = %d, min = %d, gran = %d\n",
+ __func__, info->size, info->max_size, info->min_size,
+ info->granularity);
return 0;
}
@@ -229,7 +215,7 @@ static int vx_get_pipe_state(struct vx_core *chip, struct vx_pipe *pipe, int *st
vx_init_rmh(&rmh, CMD_PIPE_STATE);
vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0);
- err = vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */
+ err = vx_send_msg(chip, &rmh);
if (! err)
*state = (rmh.Stat[0] & (1 << pipe->number)) ? 1 : 0;
return err;
@@ -280,7 +266,7 @@ static int vx_pipe_can_start(struct vx_core *chip, struct vx_pipe *pipe)
vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0);
rmh.Cmd[0] |= 1;
- err = vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */
+ err = vx_send_msg(chip, &rmh);
if (! err) {
if (rmh.Stat[0])
err = 1;
@@ -300,7 +286,7 @@ static int vx_conf_pipe(struct vx_core *chip, struct vx_pipe *pipe)
if (pipe->is_capture)
rmh.Cmd[0] |= COMMAND_RECORD_MASK;
rmh.Cmd[1] = 1 << pipe->number;
- return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */
+ return vx_send_msg(chip, &rmh);
}
/*
@@ -311,7 +297,7 @@ static int vx_send_irqa(struct vx_core *chip)
struct vx_rmh rmh;
vx_init_rmh(&rmh, CMD_SEND_IRQA);
- return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */
+ return vx_send_msg(chip, &rmh);
}
@@ -357,10 +343,12 @@ static int vx_toggle_pipe(struct vx_core *chip, struct vx_pipe *pipe, int state)
}
}
- if ((err = vx_conf_pipe(chip, pipe)) < 0)
+ err = vx_conf_pipe(chip, pipe);
+ if (err < 0)
return err;
- if ((err = vx_send_irqa(chip)) < 0)
+ err = vx_send_irqa(chip);
+ if (err < 0)
return err;
/* If it completes successfully, wait for the pipes
@@ -389,7 +377,7 @@ static int vx_stop_pipe(struct vx_core *chip, struct vx_pipe *pipe)
struct vx_rmh rmh;
vx_init_rmh(&rmh, CMD_STOP_PIPE);
vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0);
- return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */
+ return vx_send_msg(chip, &rmh);
}
@@ -477,7 +465,7 @@ static int vx_start_stream(struct vx_core *chip, struct vx_pipe *pipe)
vx_init_rmh(&rmh, CMD_START_ONE_STREAM);
vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number);
vx_set_differed_time(chip, &rmh, pipe);
- return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */
+ return vx_send_msg(chip, &rmh);
}
@@ -492,7 +480,7 @@ static int vx_stop_stream(struct vx_core *chip, struct vx_pipe *pipe)
vx_init_rmh(&rmh, CMD_STOP_STREAM);
vx_set_stream_cmd_params(&rmh, pipe->is_capture, pipe->number);
- return vx_send_msg_nolock(chip, &rmh); /* no lock needed for trigger */
+ return vx_send_msg(chip, &rmh);
}
@@ -500,7 +488,7 @@ static int vx_stop_stream(struct vx_core *chip, struct vx_pipe *pipe)
* playback hw information
*/
-static struct snd_pcm_hardware vx_pcm_playback_hw = {
+static const struct snd_pcm_hardware vx_pcm_playback_hw = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID /*|*/
/*SNDRV_PCM_INFO_RESUME*/),
@@ -520,8 +508,6 @@ static struct snd_pcm_hardware vx_pcm_playback_hw = {
};
-static void vx_pcm_delayed_start(unsigned long arg);
-
/*
* vx_pcm_playback_open - open callback for playback
*/
@@ -547,13 +533,11 @@ static int vx_pcm_playback_open(struct snd_pcm_substream *subs)
err = vx_alloc_pipe(chip, 0, audio, 2, &pipe); /* stereo playback */
if (err < 0)
return err;
- chip->playback_pipes[audio] = pipe;
}
/* open for playback */
pipe->references++;
pipe->substream = subs;
- tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs);
chip->playback_pipes[audio] = pipe;
runtime->hw = vx_pcm_playback_hw;
@@ -634,24 +618,23 @@ static int vx_pcm_playback_transfer_chunk(struct vx_core *chip,
if (space < 0) {
/* disconnect the host, SIZE_HBUF command always switches to the stream mode */
vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT);
- snd_printd("error hbuffer\n");
+ dev_dbg(chip->card->dev, "error hbuffer\n");
return space;
}
if (space < size) {
vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT);
- snd_printd("no enough hbuffer space %d\n", space);
+ dev_dbg(chip->card->dev, "no enough hbuffer space %d\n", space);
return -EIO; /* XRUN */
}
/* we don't need irqsave here, because this function
* is called from either trigger callback or irq handler
*/
- spin_lock(&chip->lock);
+ guard(mutex)(&chip->lock);
vx_pseudo_dma_write(chip, runtime, pipe, size);
err = vx_notify_end_of_buffer(chip, pipe);
/* disconnect the host, SIZE_HBUF command always switches to the stream mode */
vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT);
- spin_unlock(&chip->lock);
return err;
}
@@ -700,8 +683,9 @@ static void vx_pcm_playback_transfer(struct vx_core *chip,
if (! pipe->prepared || (chip->chip_status & VX_STAT_IS_STALE))
return;
for (i = 0; i < nchunks; i++) {
- if ((err = vx_pcm_playback_transfer_chunk(chip, runtime, pipe,
- chip->ibl.size)) < 0)
+ err = vx_pcm_playback_transfer_chunk(chip, runtime, pipe,
+ chip->ibl.size);
+ if (err < 0)
return;
}
}
@@ -718,7 +702,8 @@ static void vx_pcm_playback_update(struct vx_core *chip,
struct snd_pcm_runtime *runtime = subs->runtime;
if (pipe->running && ! (chip->chip_status & VX_STAT_IS_STALE)) {
- if ((err = vx_update_pipe_position(chip, runtime, pipe)) < 0)
+ err = vx_update_pipe_position(chip, runtime, pipe);
+ if (err < 0)
return;
if (pipe->transferred >= (int)runtime->period_size) {
pipe->transferred %= runtime->period_size;
@@ -728,31 +713,6 @@ static void vx_pcm_playback_update(struct vx_core *chip,
}
/*
- * start the stream and pipe.
- * this function is called from tasklet, which is invoked by the trigger
- * START callback.
- */
-static void vx_pcm_delayed_start(unsigned long arg)
-{
- struct snd_pcm_substream *subs = (struct snd_pcm_substream *)arg;
- struct vx_core *chip = subs->pcm->private_data;
- struct vx_pipe *pipe = subs->runtime->private_data;
- int err;
-
- /* printk( KERN_DEBUG "DDDD tasklet delayed start jiffies = %ld\n", jiffies);*/
-
- if ((err = vx_start_stream(chip, pipe)) < 0) {
- snd_printk(KERN_ERR "vx: cannot start stream\n");
- return;
- }
- if ((err = vx_toggle_pipe(chip, pipe, 1)) < 0) {
- snd_printk(KERN_ERR "vx: cannot start pipe\n");
- return;
- }
- /* printk( KERN_DEBUG "dddd tasklet delayed start jiffies = %ld \n", jiffies);*/
-}
-
-/*
* vx_pcm_playback_trigger - trigger callback for playback
*/
static int vx_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
@@ -769,11 +729,17 @@ static int vx_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
case SNDRV_PCM_TRIGGER_RESUME:
if (! pipe->is_capture)
vx_pcm_playback_transfer(chip, subs, pipe, 2);
- /* FIXME:
- * we trigger the pipe using tasklet, so that the interrupts are
- * issued surely after the trigger is completed.
- */
- tasklet_schedule(&pipe->start_tq);
+ err = vx_start_stream(chip, pipe);
+ if (err < 0) {
+ pr_debug("vx: cannot start stream\n");
+ return err;
+ }
+ err = vx_toggle_pipe(chip, pipe, 1);
+ if (err < 0) {
+ pr_debug("vx: cannot start pipe\n");
+ vx_stop_stream(chip, pipe);
+ return err;
+ }
chip->pcm_running++;
pipe->running = 1;
break;
@@ -786,11 +752,13 @@ static int vx_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
pipe->running = 0;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if ((err = vx_toggle_pipe(chip, pipe, 0)) < 0)
+ err = vx_toggle_pipe(chip, pipe, 0);
+ if (err < 0)
return err;
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if ((err = vx_toggle_pipe(chip, pipe, 1)) < 0)
+ err = vx_toggle_pipe(chip, pipe, 1);
+ if (err < 0)
return err;
break;
default:
@@ -810,24 +778,6 @@ static snd_pcm_uframes_t vx_pcm_playback_pointer(struct snd_pcm_substream *subs)
}
/*
- * vx_pcm_hw_params - hw_params callback for playback and capture
- */
-static int vx_pcm_hw_params(struct snd_pcm_substream *subs,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_alloc_vmalloc_32_buffer
- (subs, params_buffer_bytes(hw_params));
-}
-
-/*
- * vx_pcm_hw_free - hw_free callback for playback and capture
- */
-static int vx_pcm_hw_free(struct snd_pcm_substream *subs)
-{
- return snd_pcm_lib_free_vmalloc_buffer(subs);
-}
-
-/*
* vx_pcm_prepare - prepare callback for playback and capture
*/
static int vx_pcm_prepare(struct snd_pcm_substream *subs)
@@ -846,28 +796,33 @@ static int vx_pcm_prepare(struct snd_pcm_substream *subs)
/* IEC958 status (raw-mode) was changed */
/* we reopen the pipe */
struct vx_rmh rmh;
- snd_printdd(KERN_DEBUG "reopen the pipe with data_mode = %d\n", data_mode);
+ dev_dbg(chip->card->dev,
+ "reopen the pipe with data_mode = %d\n", data_mode);
vx_init_rmh(&rmh, CMD_FREE_PIPE);
vx_set_pipe_cmd_params(&rmh, 0, pipe->number, 0);
- if ((err = vx_send_msg(chip, &rmh)) < 0)
+ err = vx_send_msg(chip, &rmh);
+ if (err < 0)
return err;
vx_init_rmh(&rmh, CMD_RES_PIPE);
vx_set_pipe_cmd_params(&rmh, 0, pipe->number, pipe->channels);
if (data_mode)
rmh.Cmd[0] |= BIT_DATA_MODE;
- if ((err = vx_send_msg(chip, &rmh)) < 0)
+ err = vx_send_msg(chip, &rmh);
+ if (err < 0)
return err;
pipe->data_mode = data_mode;
}
if (chip->pcm_running && chip->freq != runtime->rate) {
- snd_printk(KERN_ERR "vx: cannot set different clock %d "
- "from the current %d\n", runtime->rate, chip->freq);
+ dev_err(chip->card->dev,
+ "vx: cannot set different clock %d from the current %d\n",
+ runtime->rate, chip->freq);
return -EINVAL;
}
vx_set_clock(chip, runtime->rate);
- if ((err = vx_set_format(chip, pipe, runtime)) < 0)
+ err = vx_set_format(chip, pipe, runtime);
+ if (err < 0)
return err;
if (vx_is_pcmcia(chip)) {
@@ -895,17 +850,12 @@ static int vx_pcm_prepare(struct snd_pcm_substream *subs)
/*
* operators for PCM playback
*/
-static struct snd_pcm_ops vx_pcm_playback_ops = {
+static const struct snd_pcm_ops vx_pcm_playback_ops = {
.open = vx_pcm_playback_open,
.close = vx_pcm_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = vx_pcm_hw_params,
- .hw_free = vx_pcm_hw_free,
.prepare = vx_pcm_prepare,
.trigger = vx_pcm_trigger,
.pointer = vx_pcm_playback_pointer,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
@@ -913,7 +863,7 @@ static struct snd_pcm_ops vx_pcm_playback_ops = {
* playback hw information
*/
-static struct snd_pcm_hardware vx_pcm_capture_hw = {
+static const struct snd_pcm_hardware vx_pcm_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID /*|*/
/*SNDRV_PCM_INFO_RESUME*/),
@@ -955,7 +905,6 @@ static int vx_pcm_capture_open(struct snd_pcm_substream *subs)
if (err < 0)
return err;
pipe->substream = subs;
- tasklet_init(&pipe->start_tq, vx_pcm_delayed_start, (unsigned long)subs);
chip->capture_pipes[audio] = pipe;
/* check if monitoring is needed */
@@ -1038,7 +987,7 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream
int size, space, count;
struct snd_pcm_runtime *runtime = subs->runtime;
- if (! pipe->prepared || (chip->chip_status & VX_STAT_IS_STALE))
+ if (!pipe->running || (chip->chip_status & VX_STAT_IS_STALE))
return;
size = runtime->buffer_size - snd_pcm_capture_avail(runtime);
@@ -1071,8 +1020,10 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream
/* ok, let's accelerate! */
int align = pipe->align * 3;
space = (count / align) * align;
- vx_pseudo_dma_read(chip, runtime, pipe, space);
- count -= space;
+ if (space > 0) {
+ vx_pseudo_dma_read(chip, runtime, pipe, space);
+ count -= space;
+ }
}
/* read the rest of bytes */
while (count > 0) {
@@ -1082,7 +1033,7 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream
count -= 3;
}
/* disconnect the host, SIZE_HBUF command always switches to the stream mode */
- vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT);
+ vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT);
/* read the last pending 6 bytes */
count = DMA_READ_ALIGN;
while (count > 0) {
@@ -1099,7 +1050,7 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream
_error:
/* disconnect the host, SIZE_HBUF command always switches to the stream mode */
- vx_send_rih_nolock(chip, IRQ_CONNECT_STREAM_NEXT);
+ vx_send_rih(chip, IRQ_CONNECT_STREAM_NEXT);
return;
}
@@ -1116,17 +1067,12 @@ static snd_pcm_uframes_t vx_pcm_capture_pointer(struct snd_pcm_substream *subs)
/*
* operators for PCM capture
*/
-static struct snd_pcm_ops vx_pcm_capture_ops = {
+static const struct snd_pcm_ops vx_pcm_capture_ops = {
.open = vx_pcm_capture_open,
.close = vx_pcm_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = vx_pcm_hw_params,
- .hw_free = vx_pcm_hw_free,
.prepare = vx_pcm_prepare,
.trigger = vx_pcm_trigger,
.pointer = vx_pcm_capture_pointer,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
@@ -1148,7 +1094,7 @@ void vx_pcm_update_intr(struct vx_core *chip, unsigned int events)
chip->irq_rmh.Cmd[0] |= 0x00000002; /* SEL_END_OF_BUF_EVENTS */
if (vx_send_msg(chip, &chip->irq_rmh) < 0) {
- snd_printdd(KERN_ERR "msg send error!!\n");
+ dev_dbg(chip->card->dev, "msg send error!!\n");
return;
}
@@ -1189,7 +1135,7 @@ void vx_pcm_update_intr(struct vx_core *chip, unsigned int events)
/*
- * vx_init_audio_io - check the availabe audio i/o and allocate pipe arrays
+ * vx_init_audio_io - check the available audio i/o and allocate pipe arrays
*/
static int vx_init_audio_io(struct vx_core *chip)
{
@@ -1198,7 +1144,8 @@ static int vx_init_audio_io(struct vx_core *chip)
vx_init_rmh(&rmh, CMD_SUPPORTED);
if (vx_send_msg(chip, &rmh) < 0) {
- snd_printk(KERN_ERR "vx: cannot get the supported audio data\n");
+ dev_err(chip->card->dev,
+ "vx: cannot get the supported audio data\n");
return -ENXIO;
}
@@ -1220,8 +1167,7 @@ static int vx_init_audio_io(struct vx_core *chip)
chip->ibl.size = 0;
vx_set_ibl(chip, &chip->ibl); /* query the info */
if (preferred > 0) {
- chip->ibl.size = ((preferred + chip->ibl.granularity - 1) /
- chip->ibl.granularity) * chip->ibl.granularity;
+ chip->ibl.size = roundup(preferred, chip->ibl.granularity);
if (chip->ibl.size > chip->ibl.max_size)
chip->ibl.size = chip->ibl.max_size;
} else
@@ -1254,7 +1200,8 @@ int snd_vx_pcm_new(struct vx_core *chip)
unsigned int i;
int err;
- if ((err = vx_init_audio_io(chip)) < 0)
+ err = vx_init_audio_io(chip);
+ if (err < 0)
return err;
for (i = 0; i < chip->hw->num_codecs; i++) {
@@ -1271,11 +1218,14 @@ int snd_vx_pcm_new(struct vx_core *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &vx_pcm_playback_ops);
if (ins)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &vx_pcm_capture_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
+ NULL, 0, 0);
pcm->private_data = chip;
pcm->private_free = snd_vx_pcm_free;
pcm->info_flags = 0;
- strcpy(pcm->name, chip->card->shortname);
+ pcm->nonatomic = true;
+ strscpy(pcm->name, chip->card->shortname);
chip->pcm[i] = pcm;
}
diff --git a/sound/drivers/vx/vx_uer.c b/sound/drivers/vx/vx_uer.c
index b0560fec6bba..1d90db3b0abd 100644
--- a/sound/drivers/vx/vx_uer.c
+++ b/sound/drivers/vx/vx_uer.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Digigram VX soundcards
*
* IEC958 stuff
*
* Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/delay.h>
@@ -60,9 +47,9 @@ static int vx_modify_board_inputs(struct vx_core *chip)
*/
static int vx_read_one_cbit(struct vx_core *chip, int index)
{
- unsigned long flags;
int val;
- spin_lock_irqsave(&chip->lock, flags);
+
+ guard(mutex)(&chip->lock);
if (chip->type >= VX_TYPE_VXPOCKET) {
vx_outb(chip, CSUER, 1); /* read */
vx_outb(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK);
@@ -72,7 +59,6 @@ static int vx_read_one_cbit(struct vx_core *chip, int index)
vx_outl(chip, RUER, index & XX_UER_CBITS_OFFSET_MASK);
val = (vx_inl(chip, RUER) >> 7) & 0x01;
}
- spin_unlock_irqrestore(&chip->lock, flags);
return val;
}
@@ -83,9 +69,8 @@ static int vx_read_one_cbit(struct vx_core *chip, int index)
*/
static void vx_write_one_cbit(struct vx_core *chip, int index, int val)
{
- unsigned long flags;
val = !!val; /* 0 or 1 */
- spin_lock_irqsave(&chip->lock, flags);
+ guard(mutex)(&chip->lock);
if (vx_is_pcmcia(chip)) {
vx_outb(chip, CSUER, 0); /* write */
vx_outb(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK));
@@ -93,7 +78,6 @@ static void vx_write_one_cbit(struct vx_core *chip, int index, int val)
vx_outl(chip, CSUER, 0); /* write */
vx_outl(chip, RUER, (val << 7) | (index & XX_UER_CBITS_OFFSET_MASK));
}
- spin_unlock_irqrestore(&chip->lock, flags);
}
/*
@@ -190,14 +174,12 @@ static int vx_calc_clock_from_freq(struct vx_core *chip, int freq)
*/
static void vx_change_clock_source(struct vx_core *chip, int source)
{
- unsigned long flags;
-
/* we mute DAC to prevent clicks */
vx_toggle_dac_mute(chip, 1);
- spin_lock_irqsave(&chip->lock, flags);
- chip->ops->set_clock_source(chip, source);
- chip->clock_source = source;
- spin_unlock_irqrestore(&chip->lock, flags);
+ scoped_guard(mutex, &chip->lock) {
+ chip->ops->set_clock_source(chip, source);
+ chip->clock_source = source;
+ }
/* unmute */
vx_toggle_dac_mute(chip, 0);
}
@@ -209,11 +191,12 @@ static void vx_change_clock_source(struct vx_core *chip, int source)
void vx_set_internal_clock(struct vx_core *chip, unsigned int freq)
{
int clock;
- unsigned long flags;
+
/* Get real clock value */
clock = vx_calc_clock_from_freq(chip, freq);
- snd_printdd(KERN_DEBUG "set internal clock to 0x%x from freq %d\n", clock, freq);
- spin_lock_irqsave(&chip->lock, flags);
+ dev_dbg(chip->card->dev,
+ "set internal clock to 0x%x from freq %d\n", clock, freq);
+ guard(mutex)(&chip->lock);
if (vx_is_pcmcia(chip)) {
vx_outb(chip, HIFREQ, (clock >> 8) & 0x0f);
vx_outb(chip, LOFREQ, clock & 0xff);
@@ -221,7 +204,6 @@ void vx_set_internal_clock(struct vx_core *chip, unsigned int freq)
vx_outl(chip, HIFREQ, (clock >> 8) & 0x0f);
vx_outl(chip, LOFREQ, clock & 0xff);
}
- spin_unlock_irqrestore(&chip->lock, flags);
}
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
new file mode 100644
index 000000000000..5973c25c2add
--- /dev/null
+++ b/sound/firewire/Kconfig
@@ -0,0 +1,202 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menuconfig SND_FIREWIRE
+ bool "FireWire sound devices"
+ depends on FIREWIRE
+ default y
+ help
+ Support for IEEE-1394/FireWire/iLink sound devices.
+
+if SND_FIREWIRE && FIREWIRE
+
+config SND_FIREWIRE_LIB
+ tristate
+ select SND_PCM
+ select SND_RAWMIDI
+
+config SND_DICE
+ tristate "DICE-based DACs support"
+ select SND_HWDEP
+ select SND_FIREWIRE_LIB
+ help
+ Say Y here to include support for devices based on the DICE chip family
+ (DICE-II/TCD2210(Mini)/TCD2220(Jr.)) which TC Applied Technologies (TCAT) produced.
+ * Allen and Heath Zed R16
+ * Alesis iO 14/26 FireWire, MasterControl, MultiMix 8/12/16 FireWire
+ * Avid Mbox 3 Pro
+ * FlexRadio Systems FLEX-3000, FLEX-5000
+ * Focusrite Liquid Saffire 56
+ * Focusrite Saffire Pro 14, Pro 24, Pro 24 DSP, Pro 26, Pro 40(TCD2220)
+ * Harman Music Group Lexicon I-ONIX FW810S
+ * Loud Technologies Mackie Onyx Blackbird, Onyx 820i/1220i/1620i/1640i (latter models)
+ * M-Audio ProFire 610/2626
+ * Mytek Stereo192-DSD DAC
+ * Midas Klark Teknik VeniceF series
+ * PreSonus FireStudio, FireStudio Mobile, FireStudio Project, FireStudio Tube
+ * PreSonus StudioLive 16.4.2, 16.0.2, 24.4.2, 32.4.2
+ * Solid State Logic Duende Classic, Duende Mini
+ * TC Electronic Studio Konnekt 48, Konnekt 24D, Konnekt Live, Impact Twin
+ * TC Electronic Digital Konnekt x32, Desktop Konnekt 6
+ * Weiss Engineering ADC2, Vesta, Minerva, AFI1, DAC1, INT202, DAC202
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-dice.
+
+config SND_OXFW
+ tristate "Oxford Semiconductor FW970/971 chipset support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for FireWire devices based on
+ Oxford Semiconductor FW970/971 chipset.
+ * Griffin Firewave
+ * LaCie Firewire Speakers
+ * Behringer F-Control Audio 202
+ * Mackie(Loud) Onyx-i series (former models)
+ * Mackie(Loud) Onyx 1640i (former model)
+ * Mackie(Loud) Onyx Satellite
+ * Mackie(Loud) Tapco Link.Firewire
+ * Mackie(Loud) d.2 pro/d.4 pro (built-in FireWire card with OXFW971 ASIC)
+ * Mackie(Loud) U.420/U.420d
+ * TASCAM FireOne
+ * Stanton Controllers & Systems 1 Deck/Mixer
+ * APOGEE duet FireWire
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-oxfw.
+
+config SND_ISIGHT
+ tristate "Apple iSight microphone"
+ select SND_FIREWIRE_LIB
+ help
+ Say Y here to include support for the front and rear microphones
+ of the Apple iSight web camera.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-isight.
+
+config SND_FIREWORKS
+ tristate "Echo Fireworks board module support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for FireWire devices based
+ on Echo Digital Audio Fireworks board:
+ * Mackie Onyx 400F/1200F
+ * Echo AudioFire12/8(until 2009 July)
+ * Echo AudioFire2/4/Pre8/8(since 2009 July)
+ * Echo Fireworks 8/HDMI
+ * Gibson Robot Interface Pack/GoldTop
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-fireworks.
+
+config SND_BEBOB
+ tristate "BridgeCo DM1000/DM1100/DM1500 with BeBoB firmware"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for FireWire devices based
+ on BridgeCo DM1000/DM1100/DM1500 with BeBoB firmware:
+ * Edirol FA-66/FA-101
+ * PreSonus FIREBOX/FIREPOD/FP10/Inspire1394
+ * BridgeCo RDAudio1/Audio5
+ * Mackie Onyx 1220/1620/1640 (FireWire I/O Card)
+ * Mackie d.2 (optional FireWire card with DM1000 ASIC)
+ * Stanton FinalScratch 2 (ScratchAmp)
+ * Tascam IF-FW/DM
+ * Behringer XENIX UFX 1204/1604
+ * Behringer Digital Mixer X32 series (X-UF Card)
+ * Behringer FCA610/1616
+ * Apogee Rosetta 200/400 (X-FireWire card)
+ * Apogee DA/AD/DD-16X (X-FireWire card)
+ * Apogee Ensemble
+ * ESI QuataFire 610
+ * AcousticReality eARMasterOne
+ * CME MatrixKFW
+ * Phonic Helix Board 12 MkII/18 MkII/24 MkII
+ * Phonic Helix Board 12 Universal/18 Universal/24 Universal
+ * Lynx Aurora 8/16 (LT-FW)
+ * ICON FireXon
+ * PrismSound Orpheus/ADA-8XR
+ * TerraTec PHASE 24 FW/PHASE X24 FW/PHASE 88 Rack FW
+ * TerraTec EWS MIC2/EWS MIC8
+ * TerraTec Aureon 7.1 FireWire
+ * Yamaha GO44/GO46
+ * Focusrite Saffire/Saffire LE/SaffirePro10 IO/SaffirePro26 IO
+ * M-Audio FireWire410/AudioPhile/Solo
+ * M-Audio Ozonic/NRV10/ProfireLightBridge
+ * M-Audio FireWire 1814/ProjectMix IO
+ * Digidesign Mbox 2 Pro
+ * ToneWeal FW66
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-bebob.
+
+config SND_FIREWIRE_DIGI00X
+ tristate "Digidesign Digi 002/003 family support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for Digidesign Digi 002/003 family.
+ * Digi 002 Console
+ * Digi 002 Rack
+ * Digi 003 Console
+ * Digi 003 Rack
+ * Digi 003 Rack+
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-firewire-digi00x.
+
+config SND_FIREWIRE_TASCAM
+ tristate "TASCAM FireWire series support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for TASCAM.
+ * FW-1884
+ * FW-1082
+ * FW-1804
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-firewire-tascam.
+
+config SND_FIREWIRE_MOTU
+ tristate "Mark of the unicorn FireWire series support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to enable support for FireWire devices which MOTU produced:
+ * 828
+ * 896
+ * 828mk2
+ * 896hd
+ * Traveler
+ * Ultralite
+ * 8pre
+ * 828mk3 (FireWire only)
+ * 828mk3 (Hybrid)
+ * 896mk3 (FireWire only)
+ * 896mk3 (Hybrid)
+ * Ultralite mk3 (FireWire only)
+ * Ultralite mk3 (Hybrid)
+ * Traveler mk3
+ * Audio Express
+ * Track 16
+ * 4pre
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-firewire-motu.
+
+config SND_FIREFACE
+ tristate "RME Fireface series support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for RME fireface series.
+ * Fireface 400
+ * Fireface 800
+ * Fireface UFX
+ * Fireface UCX
+ * Fireface 802
+
+endif # SND_FIREWIRE
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
new file mode 100644
index 000000000000..45018a5c224f
--- /dev/null
+++ b/sound/firewire/Makefile
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+# To find a header included by define_trace.h.
+CFLAGS_amdtp-stream.o := -I$(src)
+
+snd-firewire-lib-y := lib.o iso-resources.o packets-buffer.o \
+ fcp.o cmp.o amdtp-stream.o amdtp-am824.o
+snd-isight-y := isight.o
+
+obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
+obj-$(CONFIG_SND_DICE) += dice/
+obj-$(CONFIG_SND_OXFW) += oxfw/
+obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
+obj-$(CONFIG_SND_FIREWORKS) += fireworks/
+obj-$(CONFIG_SND_BEBOB) += bebob/
+obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += digi00x/
+obj-$(CONFIG_SND_FIREWIRE_TASCAM) += tascam/
+obj-$(CONFIG_SND_FIREWIRE_MOTU) += motu/
+obj-$(CONFIG_SND_FIREFACE) += fireface/
diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c
new file mode 100644
index 000000000000..3660c312bf33
--- /dev/null
+++ b/sound/firewire/amdtp-am824.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AM824 format in Audio and Music Data Transmission Protocol (IEC 61883-6)
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2015 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include <linux/slab.h>
+
+#include "amdtp-am824.h"
+
+#define CIP_FMT_AM 0x10
+
+/* "Clock-based rate control mode" is just supported. */
+#define AMDTP_FDF_AM824 0x00
+
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND 3093
+
+/*
+ * Several devices look only at the first eight data blocks.
+ * In any case, this is more than enough for the MIDI data rate.
+ */
+#define MAX_MIDI_RX_BLOCKS 8
+
+struct amdtp_am824 {
+ struct snd_rawmidi_substream *midi[AM824_MAX_CHANNELS_FOR_MIDI * 8];
+ int midi_fifo_limit;
+ int midi_fifo_used[AM824_MAX_CHANNELS_FOR_MIDI * 8];
+ unsigned int pcm_channels;
+ unsigned int midi_ports;
+
+ u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM];
+ u8 midi_position;
+};
+
+/**
+ * amdtp_am824_set_parameters - set stream parameters
+ * @s: the AMDTP stream to configure
+ * @rate: the sample rate
+ * @pcm_channels: the number of PCM samples in each data block, to be encoded
+ * as AM824 multi-bit linear audio
+ * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
+ * @double_pcm_frames: one data block transfers two PCM frames
+ *
+ * The parameters must be set before the stream is started, and must not be
+ * changed while the stream is running.
+ */
+int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels,
+ unsigned int midi_ports,
+ bool double_pcm_frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int midi_channels;
+ unsigned int pcm_frame_multiplier;
+ int i, err;
+
+ if (amdtp_stream_running(s))
+ return -EINVAL;
+
+ if (pcm_channels > AM824_MAX_CHANNELS_FOR_PCM)
+ return -EINVAL;
+
+ midi_channels = DIV_ROUND_UP(midi_ports, 8);
+ if (midi_channels > AM824_MAX_CHANNELS_FOR_MIDI)
+ return -EINVAL;
+
+ if (WARN_ON(amdtp_stream_running(s)) ||
+ WARN_ON(pcm_channels > AM824_MAX_CHANNELS_FOR_PCM) ||
+ WARN_ON(midi_channels > AM824_MAX_CHANNELS_FOR_MIDI))
+ return -EINVAL;
+
+ /*
+ * In IEC 61883-6, one data block represents one event. In ALSA, one
+ * event equals to one PCM frame. But Dice has a quirk at higher
+ * sampling rate to transfer two PCM frames in one data block.
+ */
+ if (double_pcm_frames)
+ pcm_frame_multiplier = 2;
+ else
+ pcm_frame_multiplier = 1;
+
+ err = amdtp_stream_set_parameters(s, rate, pcm_channels + midi_channels,
+ pcm_frame_multiplier);
+ if (err < 0)
+ return err;
+
+ if (s->direction == AMDTP_OUT_STREAM)
+ s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc;
+
+ p->pcm_channels = pcm_channels;
+ p->midi_ports = midi_ports;
+
+ /* init the position map for PCM and MIDI channels */
+ for (i = 0; i < pcm_channels; i++)
+ p->pcm_positions[i] = i;
+ p->midi_position = p->pcm_channels;
+
+ /*
+ * We do not know the actual MIDI FIFO size of most devices. Just
+ * assume two bytes, i.e., one byte can be received over the bus while
+ * the previous one is transmitted over MIDI.
+ * (The value here is adjusted for midi_ratelimit_per_packet().)
+ */
+ p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_parameters);
+
+/**
+ * amdtp_am824_set_pcm_position - set an index of data channel for a channel
+ * of PCM frame
+ * @s: the AMDTP stream
+ * @index: the index of data channel in an data block
+ * @position: the channel of PCM frame
+ */
+void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index,
+ unsigned int position)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ if (index < p->pcm_channels)
+ p->pcm_positions[index] = position;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_position);
+
+/**
+ * amdtp_am824_set_midi_position - set a index of data channel for MIDI
+ * conformant data channel
+ * @s: the AMDTP stream
+ * @position: the index of data channel in an data block
+ */
+void amdtp_am824_set_midi_position(struct amdtp_stream *s,
+ unsigned int position)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ p->midi_position = position;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position);
+
+static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames,
+ unsigned int pcm_frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int channels = p->pcm_channels;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ const u32 *src;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[p->pcm_positions[c]] =
+ cpu_to_be32((*src >> 8) | 0x40000000);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames,
+ unsigned int pcm_frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int channels = p->pcm_channels;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ u32 *dst;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ *dst = be32_to_cpu(buffer[p->pcm_positions[c]]) << 8;
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int i, c, channels = p->pcm_channels;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c)
+ buffer[p->pcm_positions[c]] = cpu_to_be32(0x40000000);
+ buffer += s->data_block_quadlets;
+ }
+}
+
+/**
+ * amdtp_am824_add_pcm_hw_constraints - add hw constraints for PCM substream
+ * @s: the AMDTP stream for AM824 data block, must be initialized.
+ * @runtime: the PCM substream runtime
+ *
+ */
+int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+ if (err < 0)
+ return err;
+
+ /* AM824 in IEC 61883-6 can deliver 24bit data. */
+ return snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_add_pcm_hw_constraints);
+
+/**
+ * amdtp_am824_midi_trigger - start/stop playback/capture with a MIDI device
+ * @s: the AMDTP stream
+ * @port: index of MIDI port
+ * @midi: the MIDI device to be started, or %NULL to stop the current device
+ *
+ * Call this function on a running isochronous stream to enable the actual
+ * transmission of MIDI data. This function should be called from the MIDI
+ * device's .trigger callback.
+ */
+void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ if (port < p->midi_ports)
+ WRITE_ONCE(p->midi[port], midi);
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_midi_trigger);
+
+/*
+ * To avoid sending MIDI bytes at too high a rate, assume that the receiving
+ * device has a FIFO, and track how much it is filled. This values increases
+ * by one whenever we send one byte in a packet, but the FIFO empties at
+ * a constant rate independent of our packet rate. One packet has syt_interval
+ * samples, so the number of bytes that empty out of the FIFO, per packet(!),
+ * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing
+ * fractional values, the values in midi_fifo_used[] are measured in bytes
+ * multiplied by the sample rate.
+ */
+static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
+{
+ struct amdtp_am824 *p = s->protocol;
+ int used;
+
+ used = p->midi_fifo_used[port];
+ if (used == 0) /* common shortcut */
+ return true;
+
+ used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
+ used = max(used, 0);
+ p->midi_fifo_used[port] = used;
+
+ return used < p->midi_fifo_limit;
+}
+
+static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
+{
+ struct amdtp_am824 *p = s->protocol;
+
+ p->midi_fifo_used[port] += amdtp_rate_table[s->sfc];
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int frames, unsigned int data_block_counter)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int f, port;
+ u8 *b;
+
+ for (f = 0; f < frames; f++) {
+ b = (u8 *)&buffer[p->midi_position];
+
+ port = (data_block_counter + f) % 8;
+ if (f < MAX_MIDI_RX_BLOCKS &&
+ midi_ratelimit_per_packet(s, port) &&
+ p->midi[port] != NULL &&
+ snd_rawmidi_transmit(p->midi[port], &b[1], 1) == 1) {
+ midi_rate_use_one_byte(s, port);
+ b[0] = 0x81;
+ } else {
+ b[0] = 0x80;
+ b[1] = 0;
+ }
+ b[2] = 0;
+ b[3] = 0;
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int frames, unsigned int data_block_counter)
+{
+ struct amdtp_am824 *p = s->protocol;
+ int len;
+ u8 *b;
+ int f;
+
+ for (f = 0; f < frames; f++) {
+ unsigned int port = f;
+
+ if (!(s->flags & CIP_UNALIGHED_DBC))
+ port += data_block_counter;
+ port %= 8;
+ b = (u8 *)&buffer[p->midi_position];
+
+ len = b[0] - 0x80;
+ if ((1 <= len) && (len <= 3) && (p->midi[port]))
+ snd_rawmidi_receive(p->midi[port], b + 1, len);
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int pcm_frames = 0;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ if (pcm) {
+ write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks * s->pcm_frame_multiplier;
+ } else {
+ write_pcm_silence(s, buf, data_blocks);
+ }
+
+ if (p->midi_ports) {
+ write_midi_messages(s, buf, data_blocks,
+ desc->data_block_counter);
+ }
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+}
+
+static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ struct amdtp_am824 *p = s->protocol;
+ unsigned int pcm_frames = 0;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ if (pcm) {
+ read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks * s->pcm_frame_multiplier;
+ }
+
+ if (p->midi_ports) {
+ read_midi_messages(s, buf, data_blocks,
+ desc->data_block_counter);
+ }
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+}
+
+/**
+ * amdtp_am824_init - initialize an AMDTP stream structure to handle AM824
+ * data block
+ * @s: the AMDTP stream to initialize
+ * @unit: the target of the stream
+ * @dir: the direction of stream
+ * @flags: the details of the streaming protocol consist of cip_flags enumeration-constants.
+ */
+int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, unsigned int flags)
+{
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
+
+ if (dir == AMDTP_IN_STREAM)
+ process_ctx_payloads = process_ir_ctx_payloads;
+ else
+ process_ctx_payloads = process_it_ctx_payloads;
+
+ return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
+ process_ctx_payloads, sizeof(struct amdtp_am824));
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_init);
diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h
new file mode 100644
index 000000000000..2b092b1061ba
--- /dev/null
+++ b/sound/firewire/amdtp-am824.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED
+#define SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED
+
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+
+#include "amdtp-stream.h"
+
+#define AM824_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32
+
+#define AM824_OUT_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32
+
+/*
+ * This module supports maximum 64 PCM channels for one PCM stream
+ * This is for our convenience.
+ */
+#define AM824_MAX_CHANNELS_FOR_PCM 64
+
+/*
+ * AMDTP packet can include channels for MIDI conformant data.
+ * Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
+ * Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
+ *
+ * This module supports maximum 1 MIDI conformant data channels.
+ * Then this AMDTP packets can transfer maximum 8 MIDI data streams.
+ */
+#define AM824_MAX_CHANNELS_FOR_MIDI 1
+
+int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels,
+ unsigned int midi_ports,
+ bool double_pcm_frames);
+
+void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index,
+ unsigned int position);
+
+void amdtp_am824_set_midi_position(struct amdtp_stream *s,
+ unsigned int position);
+
+int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+
+void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi);
+
+int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, unsigned int flags);
+#endif
diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h
new file mode 100644
index 000000000000..208f97cf8de6
--- /dev/null
+++ b/sound/firewire/amdtp-stream-trace.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * amdtp-stream-trace.h - tracepoint definitions to dump a part of packet data
+ *
+ * Copyright (c) 2016 Takashi Sakamoto
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM snd_firewire_lib
+
+#if !defined(_AMDTP_STREAM_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _AMDTP_STREAM_TRACE_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(amdtp_packet,
+ TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int data_block_counter, unsigned int packet_index, unsigned int index, u32 curr_cycle_time),
+ TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, data_block_counter, packet_index, index, curr_cycle_time),
+ TP_STRUCT__entry(
+ __field(unsigned int, cycle_time)
+ __field(unsigned int, second)
+ __field(unsigned int, cycle)
+ __field(int, channel)
+ __field(int, src)
+ __field(int, dest)
+ __dynamic_array(u8, cip_header, cip_header ? 8 : 0)
+ __field(unsigned int, payload_quadlets)
+ __field(unsigned int, data_blocks)
+ __field(unsigned int, data_block_counter)
+ __field(unsigned int, packet_index)
+ __field(unsigned int, irq)
+ __field(unsigned int, index)
+ ),
+ TP_fast_assign(
+ __entry->cycle_time = curr_cycle_time;
+ __entry->second = cycles / CYCLES_PER_SECOND;
+ __entry->cycle = cycles % CYCLES_PER_SECOND;
+ __entry->channel = s->context->channel;
+ if (s->direction == AMDTP_IN_STREAM) {
+ __entry->src = fw_parent_device(s->unit)->node_id;
+ __entry->dest = fw_parent_device(s->unit)->card->node_id;
+ } else {
+ __entry->src = fw_parent_device(s->unit)->card->node_id;
+ __entry->dest = fw_parent_device(s->unit)->node_id;
+ }
+ if (cip_header) {
+ memcpy(__get_dynamic_array(cip_header), cip_header,
+ __get_dynamic_array_len(cip_header));
+ }
+ __entry->payload_quadlets = payload_length / sizeof(__be32);
+ __entry->data_blocks = data_blocks;
+ __entry->data_block_counter = data_block_counter,
+ __entry->packet_index = packet_index;
+ __entry->irq = !!in_softirq();
+ __entry->index = index;
+ ),
+ TP_printk(
+ "%08x %02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u %s",
+ __entry->cycle_time,
+ __entry->second,
+ __entry->cycle,
+ __entry->src,
+ __entry->dest,
+ __entry->channel,
+ __entry->payload_quadlets,
+ __entry->data_blocks,
+ __entry->data_block_counter,
+ __entry->packet_index,
+ __entry->irq,
+ __entry->index,
+ __print_array(__get_dynamic_array(cip_header),
+ __get_dynamic_array_len(cip_header), 1))
+);
+
+#endif
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE amdtp-stream-trace
+#include <trace/define_trace.h>
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
new file mode 100644
index 000000000000..5cdc34877fc1
--- /dev/null
+++ b/sound/firewire/amdtp-stream.c
@@ -0,0 +1,2160 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Audio and Music Data Transmission Protocol (IEC 61883-6) streams
+ * with Common Isochronous Packet (IEC 61883-1) headers
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "amdtp-stream.h"
+
+#define TICKS_PER_CYCLE 3072
+#define CYCLES_PER_SECOND 8000
+#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
+
+#define OHCI_SECOND_MODULUS 8
+
+/* Always support Linux tracing subsystem. */
+#define CREATE_TRACE_POINTS
+#include "amdtp-stream-trace.h"
+
+#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */
+
+/* isochronous header parameters */
+#define ISO_DATA_LENGTH_SHIFT 16
+#define TAG_NO_CIP_HEADER 0
+#define TAG_CIP 1
+
+// Common Isochronous Packet (CIP) header parameters. Use two quadlets CIP header when supported.
+#define CIP_HEADER_QUADLETS 2
+#define CIP_EOH_SHIFT 31
+#define CIP_EOH (1u << CIP_EOH_SHIFT)
+#define CIP_EOH_MASK 0x80000000
+#define CIP_SID_SHIFT 24
+#define CIP_SID_MASK 0x3f000000
+#define CIP_DBS_MASK 0x00ff0000
+#define CIP_DBS_SHIFT 16
+#define CIP_SPH_MASK 0x00000400
+#define CIP_SPH_SHIFT 10
+#define CIP_DBC_MASK 0x000000ff
+#define CIP_FMT_SHIFT 24
+#define CIP_FMT_MASK 0x3f000000
+#define CIP_FDF_MASK 0x00ff0000
+#define CIP_FDF_SHIFT 16
+#define CIP_FDF_NO_DATA 0xff
+#define CIP_SYT_MASK 0x0000ffff
+#define CIP_SYT_NO_INFO 0xffff
+#define CIP_SYT_CYCLE_MODULUS 16
+#define CIP_NO_DATA ((CIP_FDF_NO_DATA << CIP_FDF_SHIFT) | CIP_SYT_NO_INFO)
+
+#define CIP_HEADER_SIZE (sizeof(__be32) * CIP_HEADER_QUADLETS)
+
+/* Audio and Music transfer protocol specific parameters */
+#define CIP_FMT_AM 0x10
+#define AMDTP_FDF_NO_DATA 0xff
+
+// For iso header and tstamp.
+#define IR_CTX_HEADER_DEFAULT_QUADLETS 2
+// Add nothing.
+#define IR_CTX_HEADER_SIZE_NO_CIP (sizeof(__be32) * IR_CTX_HEADER_DEFAULT_QUADLETS)
+// Add two quadlets CIP header.
+#define IR_CTX_HEADER_SIZE_CIP (IR_CTX_HEADER_SIZE_NO_CIP + CIP_HEADER_SIZE)
+#define HEADER_TSTAMP_MASK 0x0000ffff
+
+#define IT_PKT_HEADER_SIZE_CIP CIP_HEADER_SIZE
+#define IT_PKT_HEADER_SIZE_NO_CIP 0 // Nothing.
+
+// The initial firmware of OXFW970 can postpone transmission of packet during finishing
+// asynchronous transaction. This module accepts 5 cycles to skip as maximum to avoid buffer
+// overrun. Actual device can skip more, then this module stops the packet streaming.
+#define IR_JUMBO_PAYLOAD_MAX_SKIP_CYCLES 5
+
+static void pcm_period_work(struct work_struct *work);
+
+/**
+ * amdtp_stream_init - initialize an AMDTP stream structure
+ * @s: the AMDTP stream to initialize
+ * @unit: the target of the stream
+ * @dir: the direction of stream
+ * @flags: the details of the streaming protocol consist of cip_flags enumeration-constants.
+ * @fmt: the value of fmt field in CIP header
+ * @process_ctx_payloads: callback handler to process payloads of isoc context
+ * @protocol_size: the size to allocate newly for protocol
+ */
+int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, unsigned int flags,
+ unsigned int fmt,
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads,
+ unsigned int protocol_size)
+{
+ if (process_ctx_payloads == NULL)
+ return -EINVAL;
+
+ s->protocol = kzalloc(protocol_size, GFP_KERNEL);
+ if (!s->protocol)
+ return -ENOMEM;
+
+ s->unit = unit;
+ s->direction = dir;
+ s->flags = flags;
+ s->context = ERR_PTR(-1);
+ mutex_init(&s->mutex);
+ INIT_WORK(&s->period_work, pcm_period_work);
+ s->packet_index = 0;
+
+ init_waitqueue_head(&s->ready_wait);
+
+ s->fmt = fmt;
+ s->process_ctx_payloads = process_ctx_payloads;
+
+ return 0;
+}
+EXPORT_SYMBOL(amdtp_stream_init);
+
+/**
+ * amdtp_stream_destroy - free stream resources
+ * @s: the AMDTP stream to destroy
+ */
+void amdtp_stream_destroy(struct amdtp_stream *s)
+{
+ /* Not initialized. */
+ if (s->protocol == NULL)
+ return;
+
+ WARN_ON(amdtp_stream_running(s));
+ kfree(s->protocol);
+ mutex_destroy(&s->mutex);
+}
+EXPORT_SYMBOL(amdtp_stream_destroy);
+
+const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
+ [CIP_SFC_32000] = 8,
+ [CIP_SFC_44100] = 8,
+ [CIP_SFC_48000] = 8,
+ [CIP_SFC_88200] = 16,
+ [CIP_SFC_96000] = 16,
+ [CIP_SFC_176400] = 32,
+ [CIP_SFC_192000] = 32,
+};
+EXPORT_SYMBOL(amdtp_syt_intervals);
+
+const unsigned int amdtp_rate_table[CIP_SFC_COUNT] = {
+ [CIP_SFC_32000] = 32000,
+ [CIP_SFC_44100] = 44100,
+ [CIP_SFC_48000] = 48000,
+ [CIP_SFC_88200] = 88200,
+ [CIP_SFC_96000] = 96000,
+ [CIP_SFC_176400] = 176400,
+ [CIP_SFC_192000] = 192000,
+};
+EXPORT_SYMBOL(amdtp_rate_table);
+
+static int apply_constraint_to_size(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *s = hw_param_interval(params, rule->var);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval t = {0};
+ unsigned int step = 0;
+ int i;
+
+ for (i = 0; i < CIP_SFC_COUNT; ++i) {
+ if (snd_interval_test(r, amdtp_rate_table[i]))
+ step = max(step, amdtp_syt_intervals[i]);
+ }
+
+ if (step == 0)
+ return -EINVAL;
+
+ t.min = roundup(s->min, step);
+ t.max = rounddown(s->max, step);
+ t.integer = 1;
+
+ return snd_interval_refine(s, &t);
+}
+
+/**
+ * amdtp_stream_add_pcm_hw_constraints - add hw constraints for PCM substream
+ * @s: the AMDTP stream, which must be initialized.
+ * @runtime: the PCM substream runtime
+ */
+int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ unsigned int ctx_header_size;
+ unsigned int maximum_usec_per_period;
+ int err;
+
+ hw->info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_JOINT_DUPLEX |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP;
+
+ hw->periods_min = 2;
+ hw->periods_max = UINT_MAX;
+
+ /* bytes for a frame */
+ hw->period_bytes_min = 4 * hw->channels_max;
+
+ /* Just to prevent from allocating much pages. */
+ hw->period_bytes_max = hw->period_bytes_min * 2048;
+ hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
+
+ // Linux driver for 1394 OHCI controller voluntarily flushes isoc
+ // context when total size of accumulated context header reaches
+ // PAGE_SIZE. This kicks work for the isoc context and brings
+ // callback in the middle of scheduled interrupts.
+ // Although AMDTP streams in the same domain use the same events per
+ // IRQ, use the largest size of context header between IT/IR contexts.
+ // Here, use the value of context header in IR context is for both
+ // contexts.
+ if (!(s->flags & CIP_NO_HEADER))
+ ctx_header_size = IR_CTX_HEADER_SIZE_CIP;
+ else
+ ctx_header_size = IR_CTX_HEADER_SIZE_NO_CIP;
+ maximum_usec_per_period = USEC_PER_SEC * PAGE_SIZE /
+ CYCLES_PER_SECOND / ctx_header_size;
+
+ // In IEC 61883-6, one isoc packet can transfer events up to the value
+ // of syt interval. This comes from the interval of isoc cycle. As 1394
+ // OHCI controller can generate hardware IRQ per isoc packet, the
+ // interval is 125 usec.
+ // However, there are two ways of transmission in IEC 61883-6; blocking
+ // and non-blocking modes. In blocking mode, the sequence of isoc packet
+ // includes 'empty' or 'NODATA' packets which include no event. In
+ // non-blocking mode, the number of events per packet is variable up to
+ // the syt interval.
+ // Due to the above protocol design, the minimum PCM frames per
+ // interrupt should be double of the value of syt interval, thus it is
+ // 250 usec.
+ err = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+ 250, maximum_usec_per_period);
+ if (err < 0)
+ goto end;
+
+ /* Non-Blocking stream has no more constraints */
+ if (!(s->flags & CIP_BLOCKING))
+ goto end;
+
+ /*
+ * One AMDTP packet can include some frames. In blocking mode, the
+ * number equals to SYT_INTERVAL. So the number is 8, 16 or 32,
+ * depending on its sampling rate. For accurate period interrupt, it's
+ * preferrable to align period/buffer sizes to current SYT_INTERVAL.
+ */
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ apply_constraint_to_size, NULL,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ goto end;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ apply_constraint_to_size, NULL,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ goto end;
+end:
+ return err;
+}
+EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints);
+
+/**
+ * amdtp_stream_set_parameters - set stream parameters
+ * @s: the AMDTP stream to configure
+ * @rate: the sample rate
+ * @data_block_quadlets: the size of a data block in quadlet unit
+ * @pcm_frame_multiplier: the multiplier to compute the number of PCM frames by the number of AMDTP
+ * events.
+ *
+ * The parameters must be set before the stream is started, and must not be
+ * changed while the stream is running.
+ */
+int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int data_block_quadlets, unsigned int pcm_frame_multiplier)
+{
+ unsigned int sfc;
+
+ for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) {
+ if (amdtp_rate_table[sfc] == rate)
+ break;
+ }
+ if (sfc == ARRAY_SIZE(amdtp_rate_table))
+ return -EINVAL;
+
+ s->sfc = sfc;
+ s->data_block_quadlets = data_block_quadlets;
+ s->syt_interval = amdtp_syt_intervals[sfc];
+
+ // default buffering in the device.
+ s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
+
+ // additional buffering needed to adjust for no-data packets.
+ if (s->flags & CIP_BLOCKING)
+ s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
+
+ s->pcm_frame_multiplier = pcm_frame_multiplier;
+
+ return 0;
+}
+EXPORT_SYMBOL(amdtp_stream_set_parameters);
+
+// The CIP header is processed in context header apart from context payload.
+static int amdtp_stream_get_max_ctx_payload_size(struct amdtp_stream *s)
+{
+ unsigned int multiplier;
+
+ if (s->flags & CIP_JUMBO_PAYLOAD)
+ multiplier = IR_JUMBO_PAYLOAD_MAX_SKIP_CYCLES;
+ else
+ multiplier = 1;
+
+ return s->syt_interval * s->data_block_quadlets * sizeof(__be32) * multiplier;
+}
+
+/**
+ * amdtp_stream_get_max_payload - get the stream's packet size
+ * @s: the AMDTP stream
+ *
+ * This function must not be called before the stream has been configured
+ * with amdtp_stream_set_parameters().
+ */
+unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
+{
+ unsigned int cip_header_size;
+
+ if (!(s->flags & CIP_NO_HEADER))
+ cip_header_size = CIP_HEADER_SIZE;
+ else
+ cip_header_size = 0;
+
+ return cip_header_size + amdtp_stream_get_max_ctx_payload_size(s);
+}
+EXPORT_SYMBOL(amdtp_stream_get_max_payload);
+
+/**
+ * amdtp_stream_pcm_prepare - prepare PCM device for running
+ * @s: the AMDTP stream
+ *
+ * This function should be called from the PCM device's .prepare callback.
+ */
+void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
+{
+ cancel_work_sync(&s->period_work);
+ s->pcm_buffer_pointer = 0;
+ s->pcm_period_pointer = 0;
+}
+EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
+
+#define prev_packet_desc(s, desc) \
+ list_prev_entry_circular(desc, &s->packet_descs_list, link)
+
+static void pool_blocking_data_blocks(struct amdtp_stream *s, struct seq_desc *descs,
+ unsigned int size, unsigned int pos, unsigned int count)
+{
+ const unsigned int syt_interval = s->syt_interval;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ struct seq_desc *desc = descs + pos;
+
+ if (desc->syt_offset != CIP_SYT_NO_INFO)
+ desc->data_blocks = syt_interval;
+ else
+ desc->data_blocks = 0;
+
+ pos = (pos + 1) % size;
+ }
+}
+
+static void pool_ideal_nonblocking_data_blocks(struct amdtp_stream *s, struct seq_desc *descs,
+ unsigned int size, unsigned int pos,
+ unsigned int count)
+{
+ const enum cip_sfc sfc = s->sfc;
+ unsigned int state = s->ctx_data.rx.data_block_state;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ struct seq_desc *desc = descs + pos;
+
+ if (!cip_sfc_is_base_44100(sfc)) {
+ // Sample_rate / 8000 is an integer, and precomputed.
+ desc->data_blocks = state;
+ } else {
+ unsigned int phase = state;
+
+ /*
+ * This calculates the number of data blocks per packet so that
+ * 1) the overall rate is correct and exactly synchronized to
+ * the bus clock, and
+ * 2) packets with a rounded-up number of blocks occur as early
+ * as possible in the sequence (to prevent underruns of the
+ * device's buffer).
+ */
+ if (sfc == CIP_SFC_44100)
+ /* 6 6 5 6 5 6 5 ... */
+ desc->data_blocks = 5 + ((phase & 1) ^ (phase == 0 || phase >= 40));
+ else
+ /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */
+ desc->data_blocks = 11 * (sfc >> 1) + (phase == 0);
+ if (++phase >= (80 >> (sfc >> 1)))
+ phase = 0;
+ state = phase;
+ }
+
+ pos = (pos + 1) % size;
+ }
+
+ s->ctx_data.rx.data_block_state = state;
+}
+
+static unsigned int calculate_syt_offset(unsigned int *last_syt_offset,
+ unsigned int *syt_offset_state, enum cip_sfc sfc)
+{
+ unsigned int syt_offset;
+
+ if (*last_syt_offset < TICKS_PER_CYCLE) {
+ if (!cip_sfc_is_base_44100(sfc))
+ syt_offset = *last_syt_offset + *syt_offset_state;
+ else {
+ /*
+ * The time, in ticks, of the n'th SYT_INTERVAL sample is:
+ * n * SYT_INTERVAL * 24576000 / sample_rate
+ * Modulo TICKS_PER_CYCLE, the difference between successive
+ * elements is about 1386.23. Rounding the results of this
+ * formula to the SYT precision results in a sequence of
+ * differences that begins with:
+ * 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ...
+ * This code generates _exactly_ the same sequence.
+ */
+ unsigned int phase = *syt_offset_state;
+ unsigned int index = phase % 13;
+
+ syt_offset = *last_syt_offset;
+ syt_offset += 1386 + ((index && !(index & 3)) ||
+ phase == 146);
+ if (++phase >= 147)
+ phase = 0;
+ *syt_offset_state = phase;
+ }
+ } else
+ syt_offset = *last_syt_offset - TICKS_PER_CYCLE;
+ *last_syt_offset = syt_offset;
+
+ if (syt_offset >= TICKS_PER_CYCLE)
+ syt_offset = CIP_SYT_NO_INFO;
+
+ return syt_offset;
+}
+
+static void pool_ideal_syt_offsets(struct amdtp_stream *s, struct seq_desc *descs,
+ unsigned int size, unsigned int pos, unsigned int count)
+{
+ const enum cip_sfc sfc = s->sfc;
+ unsigned int last = s->ctx_data.rx.last_syt_offset;
+ unsigned int state = s->ctx_data.rx.syt_offset_state;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ struct seq_desc *desc = descs + pos;
+
+ desc->syt_offset = calculate_syt_offset(&last, &state, sfc);
+
+ pos = (pos + 1) % size;
+ }
+
+ s->ctx_data.rx.last_syt_offset = last;
+ s->ctx_data.rx.syt_offset_state = state;
+}
+
+static unsigned int compute_syt_offset(unsigned int syt, unsigned int cycle,
+ unsigned int transfer_delay)
+{
+ unsigned int cycle_lo = (cycle % CYCLES_PER_SECOND) & 0x0f;
+ unsigned int syt_cycle_lo = (syt & 0xf000) >> 12;
+ unsigned int syt_offset;
+
+ // Round up.
+ if (syt_cycle_lo < cycle_lo)
+ syt_cycle_lo += CIP_SYT_CYCLE_MODULUS;
+ syt_cycle_lo -= cycle_lo;
+
+ // Subtract transfer delay so that the synchronization offset is not so large
+ // at transmission.
+ syt_offset = syt_cycle_lo * TICKS_PER_CYCLE + (syt & 0x0fff);
+ if (syt_offset < transfer_delay)
+ syt_offset += CIP_SYT_CYCLE_MODULUS * TICKS_PER_CYCLE;
+
+ return syt_offset - transfer_delay;
+}
+
+// Both of the producer and consumer of the queue runs in the same clock of IEEE 1394 bus.
+// Additionally, the sequence of tx packets is severely checked against any discontinuity
+// before filling entries in the queue. The calculation is safe even if it looks fragile by
+// overrun.
+static unsigned int calculate_cached_cycle_count(struct amdtp_stream *s, unsigned int head)
+{
+ const unsigned int cache_size = s->ctx_data.tx.cache.size;
+ unsigned int cycles = s->ctx_data.tx.cache.pos;
+
+ if (cycles < head)
+ cycles += cache_size;
+ cycles -= head;
+
+ return cycles;
+}
+
+static void cache_seq(struct amdtp_stream *s, const struct pkt_desc *src, unsigned int desc_count)
+{
+ const unsigned int transfer_delay = s->transfer_delay;
+ const unsigned int cache_size = s->ctx_data.tx.cache.size;
+ struct seq_desc *cache = s->ctx_data.tx.cache.descs;
+ unsigned int cache_pos = s->ctx_data.tx.cache.pos;
+ bool aware_syt = !(s->flags & CIP_UNAWARE_SYT);
+ int i;
+
+ for (i = 0; i < desc_count; ++i) {
+ struct seq_desc *dst = cache + cache_pos;
+
+ if (aware_syt && src->syt != CIP_SYT_NO_INFO)
+ dst->syt_offset = compute_syt_offset(src->syt, src->cycle, transfer_delay);
+ else
+ dst->syt_offset = CIP_SYT_NO_INFO;
+ dst->data_blocks = src->data_blocks;
+
+ cache_pos = (cache_pos + 1) % cache_size;
+ src = amdtp_stream_next_packet_desc(s, src);
+ }
+
+ s->ctx_data.tx.cache.pos = cache_pos;
+}
+
+static void pool_ideal_seq_descs(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size,
+ unsigned int pos, unsigned int count)
+{
+ pool_ideal_syt_offsets(s, descs, size, pos, count);
+
+ if (s->flags & CIP_BLOCKING)
+ pool_blocking_data_blocks(s, descs, size, pos, count);
+ else
+ pool_ideal_nonblocking_data_blocks(s, descs, size, pos, count);
+}
+
+static void pool_replayed_seq(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size,
+ unsigned int pos, unsigned int count)
+{
+ struct amdtp_stream *target = s->ctx_data.rx.replay_target;
+ const struct seq_desc *cache = target->ctx_data.tx.cache.descs;
+ const unsigned int cache_size = target->ctx_data.tx.cache.size;
+ unsigned int cache_pos = s->ctx_data.rx.cache_pos;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ descs[pos] = cache[cache_pos];
+ cache_pos = (cache_pos + 1) % cache_size;
+ pos = (pos + 1) % size;
+ }
+
+ s->ctx_data.rx.cache_pos = cache_pos;
+}
+
+static void pool_seq_descs(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size,
+ unsigned int pos, unsigned int count)
+{
+ struct amdtp_domain *d = s->domain;
+ void (*pool_seq_descs)(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size,
+ unsigned int pos, unsigned int count);
+
+ if (!d->replay.enable || !s->ctx_data.rx.replay_target) {
+ pool_seq_descs = pool_ideal_seq_descs;
+ } else {
+ if (!d->replay.on_the_fly) {
+ pool_seq_descs = pool_replayed_seq;
+ } else {
+ struct amdtp_stream *tx = s->ctx_data.rx.replay_target;
+ const unsigned int cache_size = tx->ctx_data.tx.cache.size;
+ const unsigned int cache_pos = s->ctx_data.rx.cache_pos;
+ unsigned int cached_cycles = calculate_cached_cycle_count(tx, cache_pos);
+
+ if (cached_cycles > count && cached_cycles > cache_size / 2)
+ pool_seq_descs = pool_replayed_seq;
+ else
+ pool_seq_descs = pool_ideal_seq_descs;
+ }
+ }
+
+ pool_seq_descs(s, descs, size, pos, count);
+}
+
+static void update_pcm_pointers(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ unsigned int frames)
+{
+ unsigned int ptr;
+
+ ptr = s->pcm_buffer_pointer + frames;
+ if (ptr >= pcm->runtime->buffer_size)
+ ptr -= pcm->runtime->buffer_size;
+ WRITE_ONCE(s->pcm_buffer_pointer, ptr);
+
+ s->pcm_period_pointer += frames;
+ if (s->pcm_period_pointer >= pcm->runtime->period_size) {
+ s->pcm_period_pointer -= pcm->runtime->period_size;
+
+ // The program in user process should periodically check the status of intermediate
+ // buffer associated to PCM substream to process PCM frames in the buffer, instead
+ // of receiving notification of period elapsed by poll wait.
+ //
+ // Use another work item for period elapsed event to prevent the following AB/BA
+ // deadlock:
+ //
+ // thread 1 thread 2
+ // ================================= =================================
+ // A.work item (process) pcm ioctl (process)
+ // v v
+ // process_rx_packets() B.PCM stream lock
+ // process_tx_packets() v
+ // v callbacks in snd_pcm_ops
+ // update_pcm_pointers() v
+ // snd_pcm_elapsed() fw_iso_context_flush_completions()
+ // snd_pcm_stream_lock_irqsave() disable_work_sync()
+ // v v
+ // wait until release of B wait until A exits
+ if (!pcm->runtime->no_period_wakeup)
+ queue_work(system_highpri_wq, &s->period_work);
+ }
+}
+
+static void pcm_period_work(struct work_struct *work)
+{
+ struct amdtp_stream *s = container_of(work, struct amdtp_stream,
+ period_work);
+ struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
+
+ if (pcm)
+ snd_pcm_period_elapsed(pcm);
+}
+
+static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params,
+ bool sched_irq)
+{
+ int err;
+
+ params->interrupt = sched_irq;
+ params->tag = s->tag;
+ params->sy = 0;
+
+ err = fw_iso_context_queue(s->context, params, &s->buffer.iso_buffer,
+ s->buffer.packets[s->packet_index].offset);
+ if (err < 0) {
+ dev_err(&s->unit->device, "queueing error: %d\n", err);
+ goto end;
+ }
+
+ if (++s->packet_index >= s->queue_size)
+ s->packet_index = 0;
+end:
+ return err;
+}
+
+static inline int queue_out_packet(struct amdtp_stream *s,
+ struct fw_iso_packet *params, bool sched_irq)
+{
+ params->skip =
+ !!(params->header_length == 0 && params->payload_length == 0);
+ return queue_packet(s, params, sched_irq);
+}
+
+static inline int queue_in_packet(struct amdtp_stream *s,
+ struct fw_iso_packet *params)
+{
+ // Queue one packet for IR context.
+ params->header_length = s->ctx_data.tx.ctx_header_size;
+ params->payload_length = s->ctx_data.tx.max_ctx_payload_length;
+ params->skip = false;
+ return queue_packet(s, params, false);
+}
+
+static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2],
+ unsigned int data_block_counter, unsigned int syt)
+{
+ cip_header[0] = cpu_to_be32(READ_ONCE(s->source_node_id_field) |
+ (s->data_block_quadlets << CIP_DBS_SHIFT) |
+ ((s->sph << CIP_SPH_SHIFT) & CIP_SPH_MASK) |
+ data_block_counter);
+ cip_header[1] = cpu_to_be32(CIP_EOH |
+ ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
+ ((s->ctx_data.rx.fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
+ (syt & CIP_SYT_MASK));
+}
+
+static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle,
+ struct fw_iso_packet *params, unsigned int header_length,
+ unsigned int data_blocks,
+ unsigned int data_block_counter,
+ unsigned int syt, unsigned int index, u32 curr_cycle_time)
+{
+ unsigned int payload_length;
+ __be32 *cip_header;
+
+ payload_length = data_blocks * sizeof(__be32) * s->data_block_quadlets;
+ params->payload_length = payload_length;
+
+ if (header_length > 0) {
+ cip_header = (__be32 *)params->header;
+ generate_cip_header(s, cip_header, data_block_counter, syt);
+ params->header_length = header_length;
+ } else {
+ cip_header = NULL;
+ }
+
+ trace_amdtp_packet(s, cycle, cip_header, payload_length + header_length, data_blocks,
+ data_block_counter, s->packet_index, index, curr_cycle_time);
+}
+
+static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
+ unsigned int payload_length,
+ unsigned int *data_blocks,
+ unsigned int *data_block_counter, unsigned int *syt)
+{
+ u32 cip_header[2];
+ unsigned int sph;
+ unsigned int fmt;
+ unsigned int fdf;
+ unsigned int dbc;
+ bool lost;
+
+ cip_header[0] = be32_to_cpu(buf[0]);
+ cip_header[1] = be32_to_cpu(buf[1]);
+
+ /*
+ * This module supports 'Two-quadlet CIP header with SYT field'.
+ * For convenience, also check FMT field is AM824 or not.
+ */
+ if ((((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
+ ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) &&
+ (!(s->flags & CIP_HEADER_WITHOUT_EOH))) {
+ dev_info_ratelimited(&s->unit->device,
+ "Invalid CIP header for AMDTP: %08X:%08X\n",
+ cip_header[0], cip_header[1]);
+ return -EAGAIN;
+ }
+
+ /* Check valid protocol or not. */
+ sph = (cip_header[0] & CIP_SPH_MASK) >> CIP_SPH_SHIFT;
+ fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT;
+ if (sph != s->sph || fmt != s->fmt) {
+ dev_info_ratelimited(&s->unit->device,
+ "Detect unexpected protocol: %08x %08x\n",
+ cip_header[0], cip_header[1]);
+ return -EAGAIN;
+ }
+
+ /* Calculate data blocks */
+ fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
+ if (payload_length == 0 || (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
+ *data_blocks = 0;
+ } else {
+ unsigned int data_block_quadlets =
+ (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
+ /* avoid division by zero */
+ if (data_block_quadlets == 0) {
+ dev_err(&s->unit->device,
+ "Detect invalid value in dbs field: %08X\n",
+ cip_header[0]);
+ return -EPROTO;
+ }
+ if (s->flags & CIP_WRONG_DBS)
+ data_block_quadlets = s->data_block_quadlets;
+
+ *data_blocks = payload_length / sizeof(__be32) / data_block_quadlets;
+ }
+
+ /* Check data block counter continuity */
+ dbc = cip_header[0] & CIP_DBC_MASK;
+ if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
+ *data_block_counter != UINT_MAX)
+ dbc = *data_block_counter;
+
+ if ((dbc == 0x00 && (s->flags & CIP_SKIP_DBC_ZERO_CHECK)) ||
+ *data_block_counter == UINT_MAX) {
+ lost = false;
+ } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
+ lost = dbc != *data_block_counter;
+ } else {
+ unsigned int dbc_interval;
+
+ if (!(s->flags & CIP_DBC_IS_PAYLOAD_QUADLETS)) {
+ if (*data_blocks > 0 && s->ctx_data.tx.dbc_interval > 0)
+ dbc_interval = s->ctx_data.tx.dbc_interval;
+ else
+ dbc_interval = *data_blocks;
+ } else {
+ dbc_interval = payload_length / sizeof(__be32);
+ }
+
+ lost = dbc != ((*data_block_counter + dbc_interval) & 0xff);
+ }
+
+ if (lost) {
+ dev_err(&s->unit->device,
+ "Detect discontinuity of CIP: %02X %02X\n",
+ *data_block_counter, dbc);
+ return -EIO;
+ }
+
+ *data_block_counter = dbc;
+
+ if (!(s->flags & CIP_UNAWARE_SYT))
+ *syt = cip_header[1] & CIP_SYT_MASK;
+
+ return 0;
+}
+
+static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
+ const __be32 *ctx_header,
+ unsigned int *data_blocks,
+ unsigned int *data_block_counter,
+ unsigned int *syt, unsigned int packet_index, unsigned int index,
+ u32 curr_cycle_time)
+{
+ unsigned int payload_length;
+ const __be32 *cip_header;
+ unsigned int cip_header_size;
+
+ payload_length = be32_to_cpu(ctx_header[0]) >> ISO_DATA_LENGTH_SHIFT;
+
+ if (!(s->flags & CIP_NO_HEADER))
+ cip_header_size = CIP_HEADER_SIZE;
+ else
+ cip_header_size = 0;
+
+ if (payload_length > cip_header_size + s->ctx_data.tx.max_ctx_payload_length) {
+ dev_err(&s->unit->device,
+ "Detect jumbo payload: %04x %04x\n",
+ payload_length, cip_header_size + s->ctx_data.tx.max_ctx_payload_length);
+ return -EIO;
+ }
+
+ if (cip_header_size > 0) {
+ if (payload_length >= cip_header_size) {
+ int err;
+
+ cip_header = ctx_header + IR_CTX_HEADER_DEFAULT_QUADLETS;
+ err = check_cip_header(s, cip_header, payload_length - cip_header_size,
+ data_blocks, data_block_counter, syt);
+ if (err < 0)
+ return err;
+ } else {
+ // Handle the cycle so that empty packet arrives.
+ cip_header = NULL;
+ *data_blocks = 0;
+ *syt = 0;
+ }
+ } else {
+ cip_header = NULL;
+ *data_blocks = payload_length / sizeof(__be32) / s->data_block_quadlets;
+ *syt = 0;
+
+ if (*data_block_counter == UINT_MAX)
+ *data_block_counter = 0;
+ }
+
+ trace_amdtp_packet(s, cycle, cip_header, payload_length, *data_blocks,
+ *data_block_counter, packet_index, index, curr_cycle_time);
+
+ return 0;
+}
+
+// In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On
+// the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent
+// it. Thus, via Linux firewire subsystem, we can get the 3 bits for second.
+static inline u32 compute_ohci_iso_ctx_cycle_count(u32 tstamp)
+{
+ return (((tstamp >> 13) & 0x07) * CYCLES_PER_SECOND) + (tstamp & 0x1fff);
+}
+
+static inline u32 compute_ohci_cycle_count(__be32 ctx_header_tstamp)
+{
+ u32 tstamp = be32_to_cpu(ctx_header_tstamp) & HEADER_TSTAMP_MASK;
+ return compute_ohci_iso_ctx_cycle_count(tstamp);
+}
+
+static inline u32 increment_ohci_cycle_count(u32 cycle, unsigned int addend)
+{
+ cycle += addend;
+ if (cycle >= OHCI_SECOND_MODULUS * CYCLES_PER_SECOND)
+ cycle -= OHCI_SECOND_MODULUS * CYCLES_PER_SECOND;
+ return cycle;
+}
+
+static inline u32 decrement_ohci_cycle_count(u32 minuend, u32 subtrahend)
+{
+ if (minuend < subtrahend)
+ minuend += OHCI_SECOND_MODULUS * CYCLES_PER_SECOND;
+
+ return minuend - subtrahend;
+}
+
+static int compare_ohci_cycle_count(u32 lval, u32 rval)
+{
+ if (lval == rval)
+ return 0;
+ else if (lval < rval && rval - lval < OHCI_SECOND_MODULUS * CYCLES_PER_SECOND / 2)
+ return -1;
+ else
+ return 1;
+}
+
+// Align to actual cycle count for the packet which is going to be scheduled.
+// This module queued the same number of isochronous cycle as the size of queue
+// to kip isochronous cycle, therefore it's OK to just increment the cycle by
+// the size of queue for scheduled cycle.
+static inline u32 compute_ohci_it_cycle(const __be32 ctx_header_tstamp,
+ unsigned int queue_size)
+{
+ u32 cycle = compute_ohci_cycle_count(ctx_header_tstamp);
+ return increment_ohci_cycle_count(cycle, queue_size);
+}
+
+static int generate_tx_packet_descs(struct amdtp_stream *s, struct pkt_desc *desc,
+ const __be32 *ctx_header, unsigned int packet_count,
+ unsigned int *desc_count)
+{
+ unsigned int next_cycle = s->next_cycle;
+ unsigned int dbc = s->data_block_counter;
+ unsigned int packet_index = s->packet_index;
+ unsigned int queue_size = s->queue_size;
+ u32 curr_cycle_time = 0;
+ int i;
+ int err;
+
+ if (trace_amdtp_packet_enabled())
+ (void)fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &curr_cycle_time);
+
+ *desc_count = 0;
+ for (i = 0; i < packet_count; ++i) {
+ unsigned int cycle;
+ bool lost;
+ unsigned int data_blocks;
+ unsigned int syt;
+
+ cycle = compute_ohci_cycle_count(ctx_header[1]);
+ lost = (next_cycle != cycle);
+ if (lost) {
+ if (s->flags & CIP_NO_HEADER) {
+ // Fireface skips transmission just for an isoc cycle corresponding
+ // to empty packet.
+ unsigned int prev_cycle = next_cycle;
+
+ next_cycle = increment_ohci_cycle_count(next_cycle, 1);
+ lost = (next_cycle != cycle);
+ if (!lost) {
+ // Prepare a description for the skipped cycle for
+ // sequence replay.
+ desc->cycle = prev_cycle;
+ desc->syt = 0;
+ desc->data_blocks = 0;
+ desc->data_block_counter = dbc;
+ desc->ctx_payload = NULL;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ ++(*desc_count);
+ }
+ } else if (s->flags & CIP_JUMBO_PAYLOAD) {
+ // OXFW970 skips transmission for several isoc cycles during
+ // asynchronous transaction. The sequence replay is impossible due
+ // to the reason.
+ unsigned int safe_cycle = increment_ohci_cycle_count(next_cycle,
+ IR_JUMBO_PAYLOAD_MAX_SKIP_CYCLES);
+ lost = (compare_ohci_cycle_count(safe_cycle, cycle) < 0);
+ }
+ if (lost) {
+ dev_err(&s->unit->device, "Detect discontinuity of cycle: %d %d\n",
+ next_cycle, cycle);
+ return -EIO;
+ }
+ }
+
+ err = parse_ir_ctx_header(s, cycle, ctx_header, &data_blocks, &dbc, &syt,
+ packet_index, i, curr_cycle_time);
+ if (err < 0)
+ return err;
+
+ desc->cycle = cycle;
+ desc->syt = syt;
+ desc->data_blocks = data_blocks;
+ desc->data_block_counter = dbc;
+ desc->ctx_payload = s->buffer.packets[packet_index].buffer;
+
+ if (!(s->flags & CIP_DBC_IS_END_EVENT))
+ dbc = (dbc + desc->data_blocks) & 0xff;
+
+ next_cycle = increment_ohci_cycle_count(next_cycle, 1);
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ ++(*desc_count);
+ ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header);
+ packet_index = (packet_index + 1) % queue_size;
+ }
+
+ s->next_cycle = next_cycle;
+ s->data_block_counter = dbc;
+
+ return 0;
+}
+
+static unsigned int compute_syt(unsigned int syt_offset, unsigned int cycle,
+ unsigned int transfer_delay)
+{
+ unsigned int syt;
+
+ syt_offset += transfer_delay;
+ syt = ((cycle + syt_offset / TICKS_PER_CYCLE) << 12) |
+ (syt_offset % TICKS_PER_CYCLE);
+ return syt & CIP_SYT_MASK;
+}
+
+static void generate_rx_packet_descs(struct amdtp_stream *s, struct pkt_desc *desc,
+ const __be32 *ctx_header, unsigned int packet_count)
+{
+ struct seq_desc *seq_descs = s->ctx_data.rx.seq.descs;
+ unsigned int seq_size = s->ctx_data.rx.seq.size;
+ unsigned int seq_pos = s->ctx_data.rx.seq.pos;
+ unsigned int dbc = s->data_block_counter;
+ bool aware_syt = !(s->flags & CIP_UNAWARE_SYT);
+ int i;
+
+ pool_seq_descs(s, seq_descs, seq_size, seq_pos, packet_count);
+
+ for (i = 0; i < packet_count; ++i) {
+ unsigned int index = (s->packet_index + i) % s->queue_size;
+ const struct seq_desc *seq = seq_descs + seq_pos;
+
+ desc->cycle = compute_ohci_it_cycle(*ctx_header, s->queue_size);
+
+ if (aware_syt && seq->syt_offset != CIP_SYT_NO_INFO)
+ desc->syt = compute_syt(seq->syt_offset, desc->cycle, s->transfer_delay);
+ else
+ desc->syt = CIP_SYT_NO_INFO;
+
+ desc->data_blocks = seq->data_blocks;
+
+ if (s->flags & CIP_DBC_IS_END_EVENT)
+ dbc = (dbc + desc->data_blocks) & 0xff;
+
+ desc->data_block_counter = dbc;
+
+ if (!(s->flags & CIP_DBC_IS_END_EVENT))
+ dbc = (dbc + desc->data_blocks) & 0xff;
+
+ desc->ctx_payload = s->buffer.packets[index].buffer;
+
+ seq_pos = (seq_pos + 1) % seq_size;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+
+ ++ctx_header;
+ }
+
+ s->data_block_counter = dbc;
+ s->ctx_data.rx.seq.pos = seq_pos;
+}
+
+static inline void cancel_stream(struct amdtp_stream *s)
+{
+ struct work_struct *work = current_work();
+
+ s->packet_index = -1;
+
+ // Detect work items for any isochronous context. The work item for pcm_period_work()
+ // should be avoided since the call of snd_pcm_period_elapsed() can reach via
+ // snd_pcm_ops.pointer() under acquiring PCM stream(group) lock and causes dead lock at
+ // snd_pcm_stop_xrun().
+ if (work && work != &s->period_work)
+ amdtp_stream_pcm_abort(s);
+ WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN);
+}
+
+static snd_pcm_sframes_t compute_pcm_extra_delay(struct amdtp_stream *s,
+ const struct pkt_desc *desc, unsigned int count)
+{
+ unsigned int data_block_count = 0;
+ u32 latest_cycle;
+ u32 cycle_time;
+ u32 curr_cycle;
+ u32 cycle_gap;
+ int i, err;
+
+ if (count == 0)
+ goto end;
+
+ // Forward to the latest record.
+ for (i = 0; i < count - 1; ++i)
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ latest_cycle = desc->cycle;
+
+ err = fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &cycle_time);
+ if (err < 0)
+ goto end;
+
+ // Compute cycle count with lower 3 bits of second field and cycle field like timestamp
+ // format of 1394 OHCI isochronous context.
+ curr_cycle = compute_ohci_iso_ctx_cycle_count((cycle_time >> 12) & 0x0000ffff);
+
+ if (s->direction == AMDTP_IN_STREAM) {
+ // NOTE: The AMDTP packet descriptor should be for the past isochronous cycle since
+ // it corresponds to arrived isochronous packet.
+ if (compare_ohci_cycle_count(latest_cycle, curr_cycle) > 0)
+ goto end;
+ cycle_gap = decrement_ohci_cycle_count(curr_cycle, latest_cycle);
+
+ // NOTE: estimate delay by recent history of arrived AMDTP packets. The estimated
+ // value expectedly corresponds to a few packets (0-2) since the packet arrived at
+ // the most recent isochronous cycle has been already processed.
+ for (i = 0; i < cycle_gap; ++i) {
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ data_block_count += desc->data_blocks;
+ }
+ } else {
+ // NOTE: The AMDTP packet descriptor should be for the future isochronous cycle
+ // since it was already scheduled.
+ if (compare_ohci_cycle_count(latest_cycle, curr_cycle) < 0)
+ goto end;
+ cycle_gap = decrement_ohci_cycle_count(latest_cycle, curr_cycle);
+
+ // NOTE: use history of scheduled packets.
+ for (i = 0; i < cycle_gap; ++i) {
+ data_block_count += desc->data_blocks;
+ desc = prev_packet_desc(s, desc);
+ }
+ }
+end:
+ return data_block_count * s->pcm_frame_multiplier;
+}
+
+static void process_ctx_payloads(struct amdtp_stream *s,
+ const struct pkt_desc *desc,
+ unsigned int count)
+{
+ struct snd_pcm_substream *pcm;
+ int i;
+
+ pcm = READ_ONCE(s->pcm);
+ s->process_ctx_payloads(s, desc, count, pcm);
+
+ if (pcm) {
+ unsigned int data_block_count = 0;
+
+ pcm->runtime->delay = compute_pcm_extra_delay(s, desc, count);
+
+ for (i = 0; i < count; ++i) {
+ data_block_count += desc->data_blocks;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+
+ update_pcm_pointers(s, pcm, data_block_count * s->pcm_frame_multiplier);
+ }
+}
+
+static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length,
+ void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ const struct amdtp_domain *d = s->domain;
+ const __be32 *ctx_header = header;
+ const unsigned int events_per_period = d->events_per_period;
+ unsigned int event_count = s->ctx_data.rx.event_count;
+ struct pkt_desc *desc = s->packet_descs_cursor;
+ unsigned int pkt_header_length;
+ unsigned int packets;
+ u32 curr_cycle_time;
+ bool need_hw_irq;
+ int i;
+
+ if (s->packet_index < 0)
+ return;
+
+ // Calculate the number of packets in buffer and check XRUN.
+ packets = header_length / sizeof(*ctx_header);
+
+ generate_rx_packet_descs(s, desc, ctx_header, packets);
+
+ process_ctx_payloads(s, desc, packets);
+
+ if (!(s->flags & CIP_NO_HEADER))
+ pkt_header_length = IT_PKT_HEADER_SIZE_CIP;
+ else
+ pkt_header_length = 0;
+
+ if (s == d->irq_target) {
+ // At NO_PERIOD_WAKEUP mode, the packets for all IT/IR contexts are processed by
+ // the tasks of user process operating ALSA PCM character device by calling ioctl(2)
+ // with some requests, instead of scheduled hardware IRQ of an IT context.
+ struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
+ need_hw_irq = !pcm || !pcm->runtime->no_period_wakeup;
+ } else {
+ need_hw_irq = false;
+ }
+
+ if (trace_amdtp_packet_enabled())
+ (void)fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &curr_cycle_time);
+
+ for (i = 0; i < packets; ++i) {
+ DEFINE_RAW_FLEX(struct fw_iso_packet, template, header, CIP_HEADER_QUADLETS);
+ bool sched_irq = false;
+
+ build_it_pkt_header(s, desc->cycle, template, pkt_header_length,
+ desc->data_blocks, desc->data_block_counter,
+ desc->syt, i, curr_cycle_time);
+
+ if (s == s->domain->irq_target) {
+ event_count += desc->data_blocks;
+ if (event_count >= events_per_period) {
+ event_count -= events_per_period;
+ sched_irq = need_hw_irq;
+ }
+ }
+
+ if (queue_out_packet(s, template, sched_irq) < 0) {
+ cancel_stream(s);
+ return;
+ }
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+
+ s->ctx_data.rx.event_count = event_count;
+ s->packet_descs_cursor = desc;
+}
+
+static void skip_rx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length,
+ void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ struct amdtp_domain *d = s->domain;
+ const __be32 *ctx_header = header;
+ unsigned int packets;
+ unsigned int cycle;
+ int i;
+
+ if (s->packet_index < 0)
+ return;
+
+ packets = header_length / sizeof(*ctx_header);
+
+ cycle = compute_ohci_it_cycle(ctx_header[packets - 1], s->queue_size);
+ s->next_cycle = increment_ohci_cycle_count(cycle, 1);
+
+ for (i = 0; i < packets; ++i) {
+ struct fw_iso_packet params = {
+ .header_length = 0,
+ .payload_length = 0,
+ };
+ bool sched_irq = (s == d->irq_target && i == packets - 1);
+
+ if (queue_out_packet(s, &params, sched_irq) < 0) {
+ cancel_stream(s);
+ return;
+ }
+ }
+}
+
+static void irq_target_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length,
+ void *header, void *private_data);
+
+static void process_rx_packets_intermediately(struct fw_iso_context *context, u32 tstamp,
+ size_t header_length, void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ struct amdtp_domain *d = s->domain;
+ __be32 *ctx_header = header;
+ const unsigned int queue_size = s->queue_size;
+ unsigned int packets;
+ unsigned int offset;
+
+ if (s->packet_index < 0)
+ return;
+
+ packets = header_length / sizeof(*ctx_header);
+
+ offset = 0;
+ while (offset < packets) {
+ unsigned int cycle = compute_ohci_it_cycle(ctx_header[offset], queue_size);
+
+ if (compare_ohci_cycle_count(cycle, d->processing_cycle.rx_start) >= 0)
+ break;
+
+ ++offset;
+ }
+
+ if (offset > 0) {
+ unsigned int length = sizeof(*ctx_header) * offset;
+
+ skip_rx_packets(context, tstamp, length, ctx_header, private_data);
+ if (amdtp_streaming_error(s))
+ return;
+
+ ctx_header += offset;
+ header_length -= length;
+ }
+
+ if (offset < packets) {
+ s->ready_processing = true;
+ wake_up(&s->ready_wait);
+
+ if (d->replay.enable)
+ s->ctx_data.rx.cache_pos = 0;
+
+ process_rx_packets(context, tstamp, header_length, ctx_header, private_data);
+ if (amdtp_streaming_error(s))
+ return;
+
+ if (s == d->irq_target)
+ s->context->callback.sc = irq_target_callback;
+ else
+ s->context->callback.sc = process_rx_packets;
+ }
+}
+
+static void process_tx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length,
+ void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ __be32 *ctx_header = header;
+ struct pkt_desc *desc = s->packet_descs_cursor;
+ unsigned int packet_count;
+ unsigned int desc_count;
+ int i;
+ int err;
+
+ if (s->packet_index < 0)
+ return;
+
+ // Calculate the number of packets in buffer and check XRUN.
+ packet_count = header_length / s->ctx_data.tx.ctx_header_size;
+
+ desc_count = 0;
+ err = generate_tx_packet_descs(s, desc, ctx_header, packet_count, &desc_count);
+ if (err < 0) {
+ if (err != -EAGAIN) {
+ cancel_stream(s);
+ return;
+ }
+ } else {
+ struct amdtp_domain *d = s->domain;
+
+ process_ctx_payloads(s, desc, desc_count);
+
+ if (d->replay.enable)
+ cache_seq(s, desc, desc_count);
+
+ for (i = 0; i < desc_count; ++i)
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ s->packet_descs_cursor = desc;
+ }
+
+ for (i = 0; i < packet_count; ++i) {
+ struct fw_iso_packet params = {0};
+
+ if (queue_in_packet(s, &params) < 0) {
+ cancel_stream(s);
+ return;
+ }
+ }
+}
+
+static void drop_tx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length,
+ void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ const __be32 *ctx_header = header;
+ unsigned int packets;
+ unsigned int cycle;
+ int i;
+
+ if (s->packet_index < 0)
+ return;
+
+ packets = header_length / s->ctx_data.tx.ctx_header_size;
+
+ ctx_header += (packets - 1) * s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header);
+ cycle = compute_ohci_cycle_count(ctx_header[1]);
+ s->next_cycle = increment_ohci_cycle_count(cycle, 1);
+
+ for (i = 0; i < packets; ++i) {
+ struct fw_iso_packet params = {0};
+
+ if (queue_in_packet(s, &params) < 0) {
+ cancel_stream(s);
+ return;
+ }
+ }
+}
+
+static void process_tx_packets_intermediately(struct fw_iso_context *context, u32 tstamp,
+ size_t header_length, void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ struct amdtp_domain *d = s->domain;
+ __be32 *ctx_header;
+ unsigned int packets;
+ unsigned int offset;
+
+ if (s->packet_index < 0)
+ return;
+
+ packets = header_length / s->ctx_data.tx.ctx_header_size;
+
+ offset = 0;
+ ctx_header = header;
+ while (offset < packets) {
+ unsigned int cycle = compute_ohci_cycle_count(ctx_header[1]);
+
+ if (compare_ohci_cycle_count(cycle, d->processing_cycle.tx_start) >= 0)
+ break;
+
+ ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(__be32);
+ ++offset;
+ }
+
+ ctx_header = header;
+
+ if (offset > 0) {
+ size_t length = s->ctx_data.tx.ctx_header_size * offset;
+
+ drop_tx_packets(context, tstamp, length, ctx_header, s);
+ if (amdtp_streaming_error(s))
+ return;
+
+ ctx_header += length / sizeof(*ctx_header);
+ header_length -= length;
+ }
+
+ if (offset < packets) {
+ s->ready_processing = true;
+ wake_up(&s->ready_wait);
+
+ process_tx_packets(context, tstamp, header_length, ctx_header, s);
+ if (amdtp_streaming_error(s))
+ return;
+
+ context->callback.sc = process_tx_packets;
+ }
+}
+
+static void drop_tx_packets_initially(struct fw_iso_context *context, u32 tstamp,
+ size_t header_length, void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ struct amdtp_domain *d = s->domain;
+ __be32 *ctx_header;
+ unsigned int count;
+ unsigned int events;
+ int i;
+
+ if (s->packet_index < 0)
+ return;
+
+ count = header_length / s->ctx_data.tx.ctx_header_size;
+
+ // Attempt to detect any event in the batch of packets.
+ events = 0;
+ ctx_header = header;
+ for (i = 0; i < count; ++i) {
+ unsigned int payload_quads =
+ (be32_to_cpu(*ctx_header) >> ISO_DATA_LENGTH_SHIFT) / sizeof(__be32);
+ unsigned int data_blocks;
+
+ if (s->flags & CIP_NO_HEADER) {
+ data_blocks = payload_quads / s->data_block_quadlets;
+ } else {
+ __be32 *cip_headers = ctx_header + IR_CTX_HEADER_DEFAULT_QUADLETS;
+
+ if (payload_quads < CIP_HEADER_QUADLETS) {
+ data_blocks = 0;
+ } else {
+ payload_quads -= CIP_HEADER_QUADLETS;
+
+ if (s->flags & CIP_UNAWARE_SYT) {
+ data_blocks = payload_quads / s->data_block_quadlets;
+ } else {
+ u32 cip1 = be32_to_cpu(cip_headers[1]);
+
+ // NODATA packet can includes any data blocks but they are
+ // not available as event.
+ if ((cip1 & CIP_NO_DATA) == CIP_NO_DATA)
+ data_blocks = 0;
+ else
+ data_blocks = payload_quads / s->data_block_quadlets;
+ }
+ }
+ }
+
+ events += data_blocks;
+
+ ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(__be32);
+ }
+
+ drop_tx_packets(context, tstamp, header_length, header, s);
+
+ if (events > 0)
+ s->ctx_data.tx.event_starts = true;
+
+ // Decide the cycle count to begin processing content of packet in IR contexts.
+ {
+ unsigned int stream_count = 0;
+ unsigned int event_starts_count = 0;
+ unsigned int cycle = UINT_MAX;
+
+ list_for_each_entry(s, &d->streams, list) {
+ if (s->direction == AMDTP_IN_STREAM) {
+ ++stream_count;
+ if (s->ctx_data.tx.event_starts)
+ ++event_starts_count;
+ }
+ }
+
+ if (stream_count == event_starts_count) {
+ unsigned int next_cycle;
+
+ list_for_each_entry(s, &d->streams, list) {
+ if (s->direction != AMDTP_IN_STREAM)
+ continue;
+
+ next_cycle = increment_ohci_cycle_count(s->next_cycle,
+ d->processing_cycle.tx_init_skip);
+ if (cycle == UINT_MAX ||
+ compare_ohci_cycle_count(next_cycle, cycle) > 0)
+ cycle = next_cycle;
+
+ s->context->callback.sc = process_tx_packets_intermediately;
+ }
+
+ d->processing_cycle.tx_start = cycle;
+ }
+ }
+}
+
+static void process_ctxs_in_domain(struct amdtp_domain *d)
+{
+ struct amdtp_stream *s;
+
+ list_for_each_entry(s, &d->streams, list) {
+ if (s != d->irq_target && amdtp_stream_running(s))
+ fw_iso_context_flush_completions(s->context);
+
+ if (amdtp_streaming_error(s))
+ goto error;
+ }
+
+ return;
+error:
+ if (amdtp_stream_running(d->irq_target))
+ cancel_stream(d->irq_target);
+
+ list_for_each_entry(s, &d->streams, list) {
+ if (amdtp_stream_running(s))
+ cancel_stream(s);
+ }
+}
+
+static void irq_target_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length,
+ void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ struct amdtp_domain *d = s->domain;
+
+ process_rx_packets(context, tstamp, header_length, header, private_data);
+ process_ctxs_in_domain(d);
+}
+
+static void irq_target_callback_intermediately(struct fw_iso_context *context, u32 tstamp,
+ size_t header_length, void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ struct amdtp_domain *d = s->domain;
+
+ process_rx_packets_intermediately(context, tstamp, header_length, header, private_data);
+ process_ctxs_in_domain(d);
+}
+
+static void irq_target_callback_skip(struct fw_iso_context *context, u32 tstamp,
+ size_t header_length, void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ struct amdtp_domain *d = s->domain;
+ bool ready_to_start;
+
+ skip_rx_packets(context, tstamp, header_length, header, private_data);
+ process_ctxs_in_domain(d);
+
+ if (d->replay.enable && !d->replay.on_the_fly) {
+ unsigned int rx_count = 0;
+ unsigned int rx_ready_count = 0;
+ struct amdtp_stream *rx;
+
+ list_for_each_entry(rx, &d->streams, list) {
+ struct amdtp_stream *tx;
+ unsigned int cached_cycles;
+
+ if (rx->direction != AMDTP_OUT_STREAM)
+ continue;
+ ++rx_count;
+
+ tx = rx->ctx_data.rx.replay_target;
+ cached_cycles = calculate_cached_cycle_count(tx, 0);
+ if (cached_cycles > tx->ctx_data.tx.cache.size / 2)
+ ++rx_ready_count;
+ }
+
+ ready_to_start = (rx_count == rx_ready_count);
+ } else {
+ ready_to_start = true;
+ }
+
+ // Decide the cycle count to begin processing content of packet in IT contexts. All of IT
+ // contexts are expected to start and get callback when reaching here.
+ if (ready_to_start) {
+ unsigned int cycle = s->next_cycle;
+ list_for_each_entry(s, &d->streams, list) {
+ if (s->direction != AMDTP_OUT_STREAM)
+ continue;
+
+ if (compare_ohci_cycle_count(s->next_cycle, cycle) > 0)
+ cycle = s->next_cycle;
+
+ if (s == d->irq_target)
+ s->context->callback.sc = irq_target_callback_intermediately;
+ else
+ s->context->callback.sc = process_rx_packets_intermediately;
+ }
+
+ d->processing_cycle.rx_start = cycle;
+ }
+}
+
+// This is executed one time. For in-stream, first packet has come. For out-stream, prepared to
+// transmit first packet.
+static void amdtp_stream_first_callback(struct fw_iso_context *context,
+ u32 tstamp, size_t header_length,
+ void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+ struct amdtp_domain *d = s->domain;
+
+ if (s->direction == AMDTP_IN_STREAM) {
+ context->callback.sc = drop_tx_packets_initially;
+ } else {
+ if (s == d->irq_target)
+ context->callback.sc = irq_target_callback_skip;
+ else
+ context->callback.sc = skip_rx_packets;
+ }
+
+ context->callback.sc(context, tstamp, header_length, header, s);
+}
+
+/**
+ * amdtp_stream_start - start transferring packets
+ * @s: the AMDTP stream to start
+ * @channel: the isochronous channel on the bus
+ * @speed: firewire speed code
+ * @queue_size: The number of packets in the queue.
+ * @idle_irq_interval: the interval to queue packet during initial state.
+ *
+ * The stream cannot be started until it has been configured with
+ * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
+ * device can be started.
+ */
+static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
+ unsigned int queue_size, unsigned int idle_irq_interval)
+{
+ bool is_irq_target = (s == s->domain->irq_target);
+ unsigned int ctx_header_size;
+ unsigned int max_ctx_payload_size;
+ enum dma_data_direction dir;
+ struct pkt_desc *descs;
+ int i, type, tag, err;
+
+ guard(mutex)(&s->mutex);
+
+ if (WARN_ON(amdtp_stream_running(s) ||
+ (s->data_block_quadlets < 1)))
+ return -EBADFD;
+
+ if (s->direction == AMDTP_IN_STREAM) {
+ // NOTE: IT context should be used for constant IRQ.
+ if (is_irq_target)
+ return -EINVAL;
+
+ s->data_block_counter = UINT_MAX;
+ } else {
+ s->data_block_counter = 0;
+ }
+
+ // initialize packet buffer.
+ if (s->direction == AMDTP_IN_STREAM) {
+ dir = DMA_FROM_DEVICE;
+ type = FW_ISO_CONTEXT_RECEIVE;
+ if (!(s->flags & CIP_NO_HEADER))
+ ctx_header_size = IR_CTX_HEADER_SIZE_CIP;
+ else
+ ctx_header_size = IR_CTX_HEADER_SIZE_NO_CIP;
+ } else {
+ dir = DMA_TO_DEVICE;
+ type = FW_ISO_CONTEXT_TRANSMIT;
+ ctx_header_size = 0; // No effect for IT context.
+ }
+ max_ctx_payload_size = amdtp_stream_get_max_ctx_payload_size(s);
+
+ err = iso_packets_buffer_init(&s->buffer, s->unit, queue_size, max_ctx_payload_size, dir);
+ if (err < 0)
+ return err;
+ s->queue_size = queue_size;
+
+ s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
+ type, channel, speed, ctx_header_size,
+ amdtp_stream_first_callback, s);
+ if (IS_ERR(s->context)) {
+ err = PTR_ERR(s->context);
+ if (err == -EBUSY)
+ dev_err(&s->unit->device,
+ "no free stream on this controller\n");
+ goto err_buffer;
+ }
+
+ amdtp_stream_update(s);
+
+ if (s->direction == AMDTP_IN_STREAM) {
+ s->ctx_data.tx.max_ctx_payload_length = max_ctx_payload_size;
+ s->ctx_data.tx.ctx_header_size = ctx_header_size;
+ s->ctx_data.tx.event_starts = false;
+
+ if (s->domain->replay.enable) {
+ // struct fw_iso_context.drop_overflow_headers is false therefore it's
+ // possible to cache much unexpectedly.
+ s->ctx_data.tx.cache.size = max_t(unsigned int, s->syt_interval * 2,
+ queue_size * 3 / 2);
+ s->ctx_data.tx.cache.pos = 0;
+ s->ctx_data.tx.cache.descs = kcalloc(s->ctx_data.tx.cache.size,
+ sizeof(*s->ctx_data.tx.cache.descs), GFP_KERNEL);
+ if (!s->ctx_data.tx.cache.descs) {
+ err = -ENOMEM;
+ goto err_context;
+ }
+ }
+ } else {
+ static const struct {
+ unsigned int data_block;
+ unsigned int syt_offset;
+ } *entry, initial_state[] = {
+ [CIP_SFC_32000] = { 4, 3072 },
+ [CIP_SFC_48000] = { 6, 1024 },
+ [CIP_SFC_96000] = { 12, 1024 },
+ [CIP_SFC_192000] = { 24, 1024 },
+ [CIP_SFC_44100] = { 0, 67 },
+ [CIP_SFC_88200] = { 0, 67 },
+ [CIP_SFC_176400] = { 0, 67 },
+ };
+
+ s->ctx_data.rx.seq.descs = kcalloc(queue_size, sizeof(*s->ctx_data.rx.seq.descs), GFP_KERNEL);
+ if (!s->ctx_data.rx.seq.descs) {
+ err = -ENOMEM;
+ goto err_context;
+ }
+ s->ctx_data.rx.seq.size = queue_size;
+ s->ctx_data.rx.seq.pos = 0;
+
+ entry = &initial_state[s->sfc];
+ s->ctx_data.rx.data_block_state = entry->data_block;
+ s->ctx_data.rx.syt_offset_state = entry->syt_offset;
+ s->ctx_data.rx.last_syt_offset = TICKS_PER_CYCLE;
+
+ s->ctx_data.rx.event_count = 0;
+ }
+
+ if (s->flags & CIP_NO_HEADER)
+ s->tag = TAG_NO_CIP_HEADER;
+ else
+ s->tag = TAG_CIP;
+
+ // NOTE: When operating without hardIRQ/softIRQ, applications tends to call ioctl request
+ // for runtime of PCM substream in the interval equivalent to the size of PCM buffer. It
+ // could take a round over queue of AMDTP packet descriptors and small loss of history. For
+ // safe, keep more 8 elements for the queue, equivalent to 1 ms.
+ descs = kcalloc(s->queue_size + 8, sizeof(*descs), GFP_KERNEL);
+ if (!descs) {
+ err = -ENOMEM;
+ goto err_context;
+ }
+ s->packet_descs = descs;
+
+ INIT_LIST_HEAD(&s->packet_descs_list);
+ for (i = 0; i < s->queue_size; ++i) {
+ INIT_LIST_HEAD(&descs->link);
+ list_add_tail(&descs->link, &s->packet_descs_list);
+ ++descs;
+ }
+ s->packet_descs_cursor = list_first_entry(&s->packet_descs_list, struct pkt_desc, link);
+
+ s->packet_index = 0;
+ do {
+ struct fw_iso_packet params;
+
+ if (s->direction == AMDTP_IN_STREAM) {
+ err = queue_in_packet(s, &params);
+ } else {
+ bool sched_irq = false;
+
+ params.header_length = 0;
+ params.payload_length = 0;
+
+ if (is_irq_target) {
+ sched_irq = !((s->packet_index + 1) %
+ idle_irq_interval);
+ }
+
+ err = queue_out_packet(s, &params, sched_irq);
+ }
+ if (err < 0)
+ goto err_pkt_descs;
+ } while (s->packet_index > 0);
+
+ /* NOTE: TAG1 matches CIP. This just affects in stream. */
+ tag = FW_ISO_CONTEXT_MATCH_TAG1;
+ if ((s->flags & CIP_EMPTY_WITH_TAG0) || (s->flags & CIP_NO_HEADER))
+ tag |= FW_ISO_CONTEXT_MATCH_TAG0;
+
+ s->ready_processing = false;
+ err = fw_iso_context_start(s->context, -1, 0, tag);
+ if (err < 0)
+ goto err_pkt_descs;
+
+ return 0;
+err_pkt_descs:
+ kfree(s->packet_descs);
+ s->packet_descs = NULL;
+err_context:
+ if (s->direction == AMDTP_OUT_STREAM) {
+ kfree(s->ctx_data.rx.seq.descs);
+ } else {
+ if (s->domain->replay.enable)
+ kfree(s->ctx_data.tx.cache.descs);
+ }
+ fw_iso_context_destroy(s->context);
+ s->context = ERR_PTR(-1);
+err_buffer:
+ iso_packets_buffer_destroy(&s->buffer, s->unit);
+
+ return err;
+}
+
+/**
+ * amdtp_domain_stream_pcm_pointer - get the PCM buffer position
+ * @d: the AMDTP domain.
+ * @s: the AMDTP stream that transports the PCM data
+ *
+ * Returns the current buffer position, in frames.
+ */
+unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d,
+ struct amdtp_stream *s)
+{
+ struct amdtp_stream *irq_target = d->irq_target;
+
+ if (irq_target && amdtp_stream_running(irq_target)) {
+ // The work item to call snd_pcm_period_elapsed() can reach here by the call of
+ // snd_pcm_ops.pointer(), however less packets would be available then. Therefore
+ // the following call is just for user process contexts.
+ if (current_work() != &s->period_work)
+ fw_iso_context_flush_completions(irq_target->context);
+ }
+
+ return READ_ONCE(s->pcm_buffer_pointer);
+}
+EXPORT_SYMBOL_GPL(amdtp_domain_stream_pcm_pointer);
+
+/**
+ * amdtp_domain_stream_pcm_ack - acknowledge queued PCM frames
+ * @d: the AMDTP domain.
+ * @s: the AMDTP stream that transfers the PCM frames
+ *
+ * Returns zero always.
+ */
+int amdtp_domain_stream_pcm_ack(struct amdtp_domain *d, struct amdtp_stream *s)
+{
+ struct amdtp_stream *irq_target = d->irq_target;
+
+ // Process isochronous packets for recent isochronous cycle to handle
+ // queued PCM frames.
+ if (irq_target && amdtp_stream_running(irq_target))
+ fw_iso_context_flush_completions(irq_target->context);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(amdtp_domain_stream_pcm_ack);
+
+/**
+ * amdtp_stream_update - update the stream after a bus reset
+ * @s: the AMDTP stream
+ */
+void amdtp_stream_update(struct amdtp_stream *s)
+{
+ /* Precomputing. */
+ WRITE_ONCE(s->source_node_id_field,
+ (fw_parent_device(s->unit)->card->node_id << CIP_SID_SHIFT) & CIP_SID_MASK);
+}
+EXPORT_SYMBOL(amdtp_stream_update);
+
+/**
+ * amdtp_stream_stop - stop sending packets
+ * @s: the AMDTP stream to stop
+ *
+ * All PCM and MIDI devices of the stream must be stopped before the stream
+ * itself can be stopped.
+ */
+static void amdtp_stream_stop(struct amdtp_stream *s)
+{
+ guard(mutex)(&s->mutex);
+
+ if (!amdtp_stream_running(s))
+ return;
+
+ cancel_work_sync(&s->period_work);
+ fw_iso_context_stop(s->context);
+ fw_iso_context_destroy(s->context);
+ s->context = ERR_PTR(-1);
+ iso_packets_buffer_destroy(&s->buffer, s->unit);
+ kfree(s->packet_descs);
+ s->packet_descs = NULL;
+
+ if (s->direction == AMDTP_OUT_STREAM) {
+ kfree(s->ctx_data.rx.seq.descs);
+ } else {
+ if (s->domain->replay.enable)
+ kfree(s->ctx_data.tx.cache.descs);
+ }
+}
+
+/**
+ * amdtp_stream_pcm_abort - abort the running PCM device
+ * @s: the AMDTP stream about to be stopped
+ *
+ * If the isochronous stream needs to be stopped asynchronously, call this
+ * function first to stop the PCM device.
+ */
+void amdtp_stream_pcm_abort(struct amdtp_stream *s)
+{
+ struct snd_pcm_substream *pcm;
+
+ pcm = READ_ONCE(s->pcm);
+ if (pcm)
+ snd_pcm_stop_xrun(pcm);
+}
+EXPORT_SYMBOL(amdtp_stream_pcm_abort);
+
+/**
+ * amdtp_domain_init - initialize an AMDTP domain structure
+ * @d: the AMDTP domain to initialize.
+ */
+int amdtp_domain_init(struct amdtp_domain *d)
+{
+ INIT_LIST_HEAD(&d->streams);
+
+ d->events_per_period = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(amdtp_domain_init);
+
+/**
+ * amdtp_domain_destroy - destroy an AMDTP domain structure
+ * @d: the AMDTP domain to destroy.
+ */
+void amdtp_domain_destroy(struct amdtp_domain *d)
+{
+ // At present nothing to do.
+ return;
+}
+EXPORT_SYMBOL_GPL(amdtp_domain_destroy);
+
+/**
+ * amdtp_domain_add_stream - register isoc context into the domain.
+ * @d: the AMDTP domain.
+ * @s: the AMDTP stream.
+ * @channel: the isochronous channel on the bus.
+ * @speed: firewire speed code.
+ */
+int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
+ int channel, int speed)
+{
+ struct amdtp_stream *tmp;
+
+ list_for_each_entry(tmp, &d->streams, list) {
+ if (s == tmp)
+ return -EBUSY;
+ }
+
+ list_add(&s->list, &d->streams);
+
+ s->channel = channel;
+ s->speed = speed;
+ s->domain = d;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(amdtp_domain_add_stream);
+
+// Make the reference from rx stream to tx stream for sequence replay. When the number of tx streams
+// is less than the number of rx streams, the first tx stream is selected.
+static int make_association(struct amdtp_domain *d)
+{
+ unsigned int dst_index = 0;
+ struct amdtp_stream *rx;
+
+ // Make association to replay target.
+ list_for_each_entry(rx, &d->streams, list) {
+ if (rx->direction == AMDTP_OUT_STREAM) {
+ unsigned int src_index = 0;
+ struct amdtp_stream *tx = NULL;
+ struct amdtp_stream *s;
+
+ list_for_each_entry(s, &d->streams, list) {
+ if (s->direction == AMDTP_IN_STREAM) {
+ if (dst_index == src_index) {
+ tx = s;
+ break;
+ }
+
+ ++src_index;
+ }
+ }
+ if (!tx) {
+ // Select the first entry.
+ list_for_each_entry(s, &d->streams, list) {
+ if (s->direction == AMDTP_IN_STREAM) {
+ tx = s;
+ break;
+ }
+ }
+ // No target is available to replay sequence.
+ if (!tx)
+ return -EINVAL;
+ }
+
+ rx->ctx_data.rx.replay_target = tx;
+
+ ++dst_index;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * amdtp_domain_start - start sending packets for isoc context in the domain.
+ * @d: the AMDTP domain.
+ * @tx_init_skip_cycles: the number of cycles to skip processing packets at initial stage of IR
+ * contexts.
+ * @replay_seq: whether to replay the sequence of packet in IR context for the sequence of packet in
+ * IT context.
+ * @replay_on_the_fly: transfer rx packets according to nominal frequency, then begin to replay
+ * according to arrival of events in tx packets.
+ */
+int amdtp_domain_start(struct amdtp_domain *d, unsigned int tx_init_skip_cycles, bool replay_seq,
+ bool replay_on_the_fly)
+{
+ unsigned int events_per_buffer = d->events_per_buffer;
+ unsigned int events_per_period = d->events_per_period;
+ unsigned int queue_size;
+ struct amdtp_stream *s;
+ bool found = false;
+ int err;
+
+ if (replay_seq) {
+ err = make_association(d);
+ if (err < 0)
+ return err;
+ }
+ d->replay.enable = replay_seq;
+ d->replay.on_the_fly = replay_on_the_fly;
+
+ // Select an IT context as IRQ target.
+ list_for_each_entry(s, &d->streams, list) {
+ if (s->direction == AMDTP_OUT_STREAM) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return -ENXIO;
+ d->irq_target = s;
+
+ d->processing_cycle.tx_init_skip = tx_init_skip_cycles;
+
+ // This is a case that AMDTP streams in domain run just for MIDI
+ // substream. Use the number of events equivalent to 10 msec as
+ // interval of hardware IRQ.
+ if (events_per_period == 0)
+ events_per_period = amdtp_rate_table[d->irq_target->sfc] / 100;
+ if (events_per_buffer == 0)
+ events_per_buffer = events_per_period * 3;
+
+ queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer,
+ amdtp_rate_table[d->irq_target->sfc]);
+
+ list_for_each_entry(s, &d->streams, list) {
+ unsigned int idle_irq_interval = 0;
+
+ if (s->direction == AMDTP_OUT_STREAM && s == d->irq_target) {
+ idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period,
+ amdtp_rate_table[d->irq_target->sfc]);
+ }
+
+ // Starts immediately but actually DMA context starts several hundred cycles later.
+ err = amdtp_stream_start(s, s->channel, s->speed, queue_size, idle_irq_interval);
+ if (err < 0)
+ goto error;
+ }
+
+ return 0;
+error:
+ list_for_each_entry(s, &d->streams, list)
+ amdtp_stream_stop(s);
+ return err;
+}
+EXPORT_SYMBOL_GPL(amdtp_domain_start);
+
+/**
+ * amdtp_domain_stop - stop sending packets for isoc context in the same domain.
+ * @d: the AMDTP domain to which the isoc contexts belong.
+ */
+void amdtp_domain_stop(struct amdtp_domain *d)
+{
+ struct amdtp_stream *s, *next;
+
+ if (d->irq_target)
+ amdtp_stream_stop(d->irq_target);
+
+ list_for_each_entry_safe(s, next, &d->streams, list) {
+ list_del(&s->list);
+
+ if (s != d->irq_target)
+ amdtp_stream_stop(s);
+ }
+
+ d->events_per_period = 0;
+ d->irq_target = NULL;
+}
+EXPORT_SYMBOL_GPL(amdtp_domain_stop);
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h
new file mode 100644
index 000000000000..ec10270c2cce
--- /dev/null
+++ b/sound/firewire/amdtp-stream.h
@@ -0,0 +1,372 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED
+#define SOUND_FIREWIRE_AMDTP_H_INCLUDED
+
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <sound/asound.h>
+#include "packets-buffer.h"
+
+/**
+ * enum cip_flags - describes details of the streaming protocol
+ * @CIP_NONBLOCKING: In non-blocking mode, each packet contains
+ * sample_rate/8000 samples, with rounding up or down to adjust
+ * for clock skew and left-over fractional samples. This should
+ * be used if supported by the device.
+ * @CIP_BLOCKING: In blocking mode, each packet contains either zero or
+ * SYT_INTERVAL samples, with these two types alternating so that
+ * the overall sample rate comes out right.
+ * @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0.
+ * @CIP_DBC_IS_END_EVENT: The value of dbc in an packet corresponds to the end
+ * of event in the packet. Out of IEC 61883.
+ * @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets.
+ * The value of data_block_quadlets is used instead of reported value.
+ * @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is
+ * skipped for detecting discontinuity.
+ * @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
+ * packet is wrong but the others are correct.
+ * @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
+ * packet is larger than IEC 61883-6 defines. Current implementation
+ * allows 5 times as large as IEC 61883-6 defines.
+ * @CIP_HEADER_WITHOUT_EOH: Only for in-stream. CIP Header doesn't include
+ * valid EOH.
+ * @CIP_NO_HEADER: a lack of headers in packets
+ * @CIP_UNALIGHED_DBC: Only for in-stream. The value of dbc is not alighed to
+ * the value of current SYT_INTERVAL; e.g. initial value is not zero.
+ * @CIP_UNAWARE_SYT: For outgoing packet, the value in SYT field of CIP is 0xffff.
+ * For incoming packet, the value in SYT field of CIP is not handled.
+ * @CIP_DBC_IS_PAYLOAD_QUADLETS: Available for incoming packet, and only effective with
+ * CIP_DBC_IS_END_EVENT flag. The value of dbc field is the number of accumulated quadlets
+ * in CIP payload, instead of the number of accumulated data blocks.
+ */
+enum cip_flags {
+ CIP_NONBLOCKING = 0x00,
+ CIP_BLOCKING = 0x01,
+ CIP_EMPTY_WITH_TAG0 = 0x02,
+ CIP_DBC_IS_END_EVENT = 0x04,
+ CIP_WRONG_DBS = 0x08,
+ CIP_SKIP_DBC_ZERO_CHECK = 0x10,
+ CIP_EMPTY_HAS_WRONG_DBC = 0x20,
+ CIP_JUMBO_PAYLOAD = 0x40,
+ CIP_HEADER_WITHOUT_EOH = 0x80,
+ CIP_NO_HEADER = 0x100,
+ CIP_UNALIGHED_DBC = 0x200,
+ CIP_UNAWARE_SYT = 0x400,
+ CIP_DBC_IS_PAYLOAD_QUADLETS = 0x800,
+};
+
+/**
+ * enum cip_sfc - supported Sampling Frequency Codes (SFCs)
+ * @CIP_SFC_32000: 32,000 data blocks
+ * @CIP_SFC_44100: 44,100 data blocks
+ * @CIP_SFC_48000: 48,000 data blocks
+ * @CIP_SFC_88200: 88,200 data blocks
+ * @CIP_SFC_96000: 96,000 data blocks
+ * @CIP_SFC_176400: 176,400 data blocks
+ * @CIP_SFC_192000: 192,000 data blocks
+ * @CIP_SFC_COUNT: the number of supported SFCs
+ *
+ * These values are used to show nominal Sampling Frequency Code in
+ * Format Dependent Field (FDF) of AMDTP packet header. In IEC 61883-6:2002,
+ * this code means the number of events per second. Actually the code
+ * represents the number of data blocks transferred per second in an AMDTP
+ * stream.
+ *
+ * In IEC 61883-6:2005, some extensions were added to support more types of
+ * data such as 'One Bit LInear Audio', therefore the meaning of SFC became
+ * different depending on the types.
+ *
+ * Currently our implementation is compatible with IEC 61883-6:2002.
+ */
+enum cip_sfc {
+ CIP_SFC_32000 = 0,
+ CIP_SFC_44100 = 1,
+ CIP_SFC_48000 = 2,
+ CIP_SFC_88200 = 3,
+ CIP_SFC_96000 = 4,
+ CIP_SFC_176400 = 5,
+ CIP_SFC_192000 = 6,
+ CIP_SFC_COUNT
+};
+
+struct fw_unit;
+struct fw_iso_context;
+struct snd_pcm_substream;
+struct snd_pcm_runtime;
+
+enum amdtp_stream_direction {
+ AMDTP_OUT_STREAM = 0,
+ AMDTP_IN_STREAM
+};
+
+struct pkt_desc {
+ u32 cycle;
+ u32 syt;
+ unsigned int data_blocks;
+ unsigned int data_block_counter;
+ __be32 *ctx_payload;
+ struct list_head link;
+};
+
+struct amdtp_stream;
+typedef void (*amdtp_stream_process_ctx_payloads_t)(struct amdtp_stream *s,
+ const struct pkt_desc *desc,
+ unsigned int count,
+ struct snd_pcm_substream *pcm);
+
+struct amdtp_domain;
+struct amdtp_stream {
+ struct fw_unit *unit;
+ // The combination of cip_flags enumeration-constants.
+ unsigned int flags;
+ enum amdtp_stream_direction direction;
+ struct mutex mutex;
+
+ /* For packet processing. */
+ struct fw_iso_context *context;
+ struct iso_packets_buffer buffer;
+ unsigned int queue_size;
+ int packet_index;
+ struct pkt_desc *packet_descs;
+ struct list_head packet_descs_list;
+ struct pkt_desc *packet_descs_cursor;
+ int tag;
+ union {
+ struct {
+ unsigned int ctx_header_size;
+
+ // limit for payload of iso packet.
+ unsigned int max_ctx_payload_length;
+
+ // For quirks of CIP headers.
+ // Fixed interval of dbc between previos/current
+ // packets.
+ unsigned int dbc_interval;
+
+ // The device starts multiplexing events to the packet.
+ bool event_starts;
+
+ struct {
+ struct seq_desc *descs;
+ unsigned int size;
+ unsigned int pos;
+ } cache;
+ } tx;
+ struct {
+ // To generate CIP header.
+ unsigned int fdf;
+
+ // To generate constant hardware IRQ.
+ unsigned int event_count;
+
+ // To calculate CIP data blocks and tstamp.
+ struct {
+ struct seq_desc *descs;
+ unsigned int size;
+ unsigned int pos;
+ } seq;
+
+ unsigned int data_block_state;
+ unsigned int syt_offset_state;
+ unsigned int last_syt_offset;
+
+ struct amdtp_stream *replay_target;
+ unsigned int cache_pos;
+ } rx;
+ } ctx_data;
+
+ /* For CIP headers. */
+ unsigned int source_node_id_field;
+ unsigned int data_block_quadlets;
+ unsigned int data_block_counter;
+ unsigned int sph;
+ unsigned int fmt;
+
+ // Internal flags.
+ unsigned int transfer_delay;
+ enum cip_sfc sfc;
+ unsigned int syt_interval;
+
+ /* For a PCM substream processing. */
+ struct snd_pcm_substream *pcm;
+ struct work_struct period_work;
+ snd_pcm_uframes_t pcm_buffer_pointer;
+ unsigned int pcm_period_pointer;
+ unsigned int pcm_frame_multiplier;
+
+ // To start processing content of packets at the same cycle in several contexts for
+ // each direction.
+ bool ready_processing;
+ wait_queue_head_t ready_wait;
+ unsigned int next_cycle;
+
+ /* For backends to process data blocks. */
+ void *protocol;
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
+
+ // For domain.
+ int channel;
+ int speed;
+ struct list_head list;
+ struct amdtp_domain *domain;
+};
+
+int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, unsigned int flags,
+ unsigned int fmt,
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads,
+ unsigned int protocol_size);
+void amdtp_stream_destroy(struct amdtp_stream *s);
+
+int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int data_block_quadlets, unsigned int pcm_frame_multiplier);
+unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
+
+void amdtp_stream_update(struct amdtp_stream *s);
+
+int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+
+void amdtp_stream_pcm_prepare(struct amdtp_stream *s);
+void amdtp_stream_pcm_abort(struct amdtp_stream *s);
+
+extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
+extern const unsigned int amdtp_rate_table[CIP_SFC_COUNT];
+
+/**
+ * amdtp_stream_running - check stream is running or not
+ * @s: the AMDTP stream
+ *
+ * If this function returns true, the stream is running.
+ */
+static inline bool amdtp_stream_running(struct amdtp_stream *s)
+{
+ return !IS_ERR(s->context);
+}
+
+/**
+ * amdtp_streaming_error - check for streaming error
+ * @s: the AMDTP stream
+ *
+ * If this function returns true, the stream's packet queue has stopped due to
+ * an asynchronous error.
+ */
+static inline bool amdtp_streaming_error(struct amdtp_stream *s)
+{
+ return s->packet_index < 0;
+}
+
+/**
+ * amdtp_stream_pcm_running - check PCM substream is running or not
+ * @s: the AMDTP stream
+ *
+ * If this function returns true, PCM substream in the AMDTP stream is running.
+ */
+static inline bool amdtp_stream_pcm_running(struct amdtp_stream *s)
+{
+ return !!s->pcm;
+}
+
+/**
+ * amdtp_stream_pcm_trigger - start/stop playback from a PCM device
+ * @s: the AMDTP stream
+ * @pcm: the PCM device to be started, or %NULL to stop the current device
+ *
+ * Call this function on a running isochronous stream to enable the actual
+ * transmission of PCM data. This function should be called from the PCM
+ * device's .trigger callback.
+ */
+static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm)
+{
+ WRITE_ONCE(s->pcm, pcm);
+}
+
+/**
+ * amdtp_stream_next_packet_desc - retrieve next descriptor for amdtp packet.
+ * @s: the AMDTP stream
+ * @desc: the descriptor of packet
+ *
+ * This macro computes next descriptor so that the list of descriptors behaves circular queue.
+ */
+#define amdtp_stream_next_packet_desc(s, desc) \
+ list_next_entry_circular(desc, &s->packet_descs_list, link)
+
+static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
+{
+ return sfc & 1;
+}
+
+struct seq_desc {
+ unsigned int syt_offset;
+ unsigned int data_blocks;
+};
+
+struct amdtp_domain {
+ struct list_head streams;
+
+ unsigned int events_per_period;
+ unsigned int events_per_buffer;
+
+ struct amdtp_stream *irq_target;
+
+ struct {
+ unsigned int tx_init_skip;
+ unsigned int tx_start;
+ unsigned int rx_start;
+ } processing_cycle;
+
+ struct {
+ bool enable:1;
+ bool on_the_fly:1;
+ } replay;
+};
+
+int amdtp_domain_init(struct amdtp_domain *d);
+void amdtp_domain_destroy(struct amdtp_domain *d);
+
+int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
+ int channel, int speed);
+
+int amdtp_domain_start(struct amdtp_domain *d, unsigned int tx_init_skip_cycles, bool replay_seq,
+ bool replay_on_the_fly);
+void amdtp_domain_stop(struct amdtp_domain *d);
+
+static inline int amdtp_domain_set_events_per_period(struct amdtp_domain *d,
+ unsigned int events_per_period,
+ unsigned int events_per_buffer)
+{
+ d->events_per_period = events_per_period;
+ d->events_per_buffer = events_per_buffer;
+
+ return 0;
+}
+
+unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d,
+ struct amdtp_stream *s);
+int amdtp_domain_stream_pcm_ack(struct amdtp_domain *d, struct amdtp_stream *s);
+
+/**
+ * amdtp_domain_wait_ready - sleep till being ready to process packets or timeout
+ * @d: the AMDTP domain
+ * @timeout_ms: msec till timeout
+ *
+ * If this function return false, the AMDTP domain should be stopped.
+ */
+static inline bool amdtp_domain_wait_ready(struct amdtp_domain *d, unsigned int timeout_ms)
+{
+ struct amdtp_stream *s;
+
+ list_for_each_entry(s, &d->streams, list) {
+ unsigned int j = msecs_to_jiffies(timeout_ms);
+
+ if (wait_event_interruptible_timeout(s->ready_wait, s->ready_processing, j) <= 0)
+ return false;
+ }
+
+ return true;
+}
+
+#endif
diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
new file mode 100644
index 000000000000..b913e805bd7a
--- /dev/null
+++ b/sound/firewire/bebob/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+snd-bebob-y := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
+ bebob_pcm.o bebob_hwdep.o bebob_terratec.o \
+ bebob_yamaha_terratec.o bebob_focusrite.o bebob_maudio.o \
+ bebob.o
+obj-$(CONFIG_SND_BEBOB) += snd-bebob.o
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
new file mode 100644
index 000000000000..01e2c4cc03d4
--- /dev/null
+++ b/sound/firewire/bebob/bebob.c
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+/*
+ * BeBoB is 'BridgeCo enhanced Breakout Box'. This is installed to firewire
+ * devices with DM1000/DM1100/DM1500 chipset. It gives common way for host
+ * system to handle BeBoB based devices.
+ */
+
+#include "bebob.h"
+
+MODULE_DESCRIPTION("BridgeCo BeBoB driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "card index");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "enable BeBoB sound card");
+
+static DEFINE_MUTEX(devices_mutex);
+static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
+
+/* Offsets from information register. */
+#define INFO_OFFSET_BEBOB_VERSION 0x08
+#define INFO_OFFSET_GUID 0x10
+#define INFO_OFFSET_HW_MODEL_ID 0x18
+#define INFO_OFFSET_HW_MODEL_REVISION 0x1c
+
+#define VEN_EDIROL 0x000040ab
+#define VEN_PRESONUS 0x00000a92
+#define VEN_BRIDGECO 0x000007f5
+#define VEN_MACKIE 0x00000ff2
+#define VEN_STANTON 0x00001260
+#define VEN_TASCAM 0x0000022e
+#define VEN_BEHRINGER 0x00001564
+#define VEN_APOGEE 0x000003db
+#define VEN_ESI 0x00000f1b
+#define VEN_CME 0x0000000a
+#define VEN_PHONIC 0x00001496
+#define VEN_LYNX 0x000019e5
+#define VEN_ICON 0x00001a9e
+#define VEN_PRISMSOUND 0x00001198
+#define VEN_TERRATEC 0x00000aac
+#define VEN_YAMAHA 0x0000a0de
+#define VEN_FOCUSRITE 0x0000130e
+#define VEN_MAUDIO 0x00000d6c
+#define VEN_DIGIDESIGN 0x00a07e
+#define OUI_SHOUYO 0x002327
+
+#define MODEL_FOCUSRITE_SAFFIRE_BOTH 0x00000000
+#define MODEL_MAUDIO_AUDIOPHILE_BOTH 0x00010060
+#define MODEL_MAUDIO_FW1814 0x00010071
+#define MODEL_MAUDIO_PROJECTMIX 0x00010091
+#define MODEL_MAUDIO_PROFIRELIGHTBRIDGE 0x000100a1
+
+static int
+name_device(struct snd_bebob *bebob)
+{
+ struct fw_device *fw_dev = fw_parent_device(bebob->unit);
+ char vendor[24] = {0};
+ char model[32] = {0};
+ u32 hw_id;
+ u32 data[2] = {0};
+ u32 revision;
+ int err;
+
+ /* get vendor name from root directory */
+ err = fw_csr_string(fw_dev->config_rom + 5, CSR_VENDOR,
+ vendor, sizeof(vendor));
+ if (err < 0)
+ goto end;
+
+ /* get model name from unit directory */
+ err = fw_csr_string(bebob->unit->directory, CSR_MODEL,
+ model, sizeof(model));
+ if (err < 0)
+ goto end;
+
+ /* get hardware id */
+ err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_HW_MODEL_ID,
+ &hw_id);
+ if (err < 0)
+ goto end;
+
+ /* get hardware revision */
+ err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_HW_MODEL_REVISION,
+ &revision);
+ if (err < 0)
+ goto end;
+
+ /* get GUID */
+ err = snd_bebob_read_block(bebob->unit, INFO_OFFSET_GUID,
+ data, sizeof(data));
+ if (err < 0)
+ goto end;
+
+ strscpy(bebob->card->driver, "BeBoB");
+ strscpy(bebob->card->shortname, model);
+ strscpy(bebob->card->mixername, model);
+ snprintf(bebob->card->longname, sizeof(bebob->card->longname),
+ "%s %s (id:%d, rev:%d), GUID %08x%08x at %s, S%d",
+ vendor, model, hw_id, revision,
+ data[0], data[1], dev_name(&bebob->unit->device),
+ 100 << fw_dev->max_speed);
+end:
+ return err;
+}
+
+static void
+bebob_card_free(struct snd_card *card)
+{
+ struct snd_bebob *bebob = card->private_data;
+
+ scoped_guard(mutex, &devices_mutex) {
+ clear_bit(bebob->card_index, devices_used);
+ }
+
+ snd_bebob_stream_destroy_duplex(bebob);
+
+ mutex_destroy(&bebob->mutex);
+ fw_unit_put(bebob->unit);
+}
+
+static const struct snd_bebob_spec *
+get_saffire_spec(struct fw_unit *unit)
+{
+ char name[24] = {0};
+
+ if (fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)) < 0)
+ return NULL;
+
+ if (strcmp(name, "SaffireLE") == 0)
+ return &saffire_le_spec;
+ else
+ return &saffire_spec;
+}
+
+static bool
+check_audiophile_booted(struct fw_unit *unit)
+{
+ char name[28] = {0};
+
+ if (fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)) < 0)
+ return false;
+
+ return strncmp(name, "FW Audiophile Bootloader", 24) != 0;
+}
+
+static int detect_quirks(struct snd_bebob *bebob, const struct ieee1394_device_id *entry)
+{
+ if (entry->vendor_id == VEN_MAUDIO) {
+ switch (entry->model_id) {
+ case MODEL_MAUDIO_PROFIRELIGHTBRIDGE:
+ // M-Audio ProFire Lightbridge has a quirk to transfer packets with
+ // discontinuous cycle or data block counter in early stage of packet
+ // streaming. The cycle span from the first packet with event is variable.
+ bebob->quirks |= SND_BEBOB_QUIRK_INITIAL_DISCONTINUOUS_DBC;
+ break;
+ case MODEL_MAUDIO_FW1814:
+ case MODEL_MAUDIO_PROJECTMIX:
+ // At high sampling rate, M-Audio special firmware transmits empty packet
+ // with the value of dbc incremented by 8.
+ bebob->quirks |= SND_BEBOB_QUIRK_WRONG_DBC;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int bebob_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+ unsigned int card_index;
+ struct snd_card *card;
+ struct snd_bebob *bebob;
+ const struct snd_bebob_spec *spec;
+ int err;
+
+ if (entry->vendor_id == VEN_FOCUSRITE &&
+ entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH)
+ spec = get_saffire_spec(unit);
+ else if (entry->vendor_id == VEN_MAUDIO &&
+ entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH &&
+ !check_audiophile_booted(unit))
+ spec = NULL;
+ else
+ spec = (const struct snd_bebob_spec *)entry->driver_data;
+
+ if (spec == NULL) {
+ // To boot up M-Audio models.
+ if (entry->vendor_id == VEN_MAUDIO || entry->vendor_id == VEN_BRIDGECO)
+ return snd_bebob_maudio_load_firmware(unit);
+ else
+ return -ENODEV;
+ }
+
+ scoped_guard(mutex, &devices_mutex) {
+ for (card_index = 0; card_index < SNDRV_CARDS; card_index++) {
+ if (!test_bit(card_index, devices_used) && enable[card_index])
+ break;
+ }
+ if (card_index >= SNDRV_CARDS)
+ return -ENOENT;
+
+ err = snd_card_new(&unit->device, index[card_index], id[card_index], THIS_MODULE,
+ sizeof(*bebob), &card);
+ if (err < 0)
+ return err;
+ card->private_free = bebob_card_free;
+ set_bit(card_index, devices_used);
+ }
+
+ bebob = card->private_data;
+ bebob->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, bebob);
+ bebob->card = card;
+ bebob->card_index = card_index;
+
+ bebob->spec = spec;
+ mutex_init(&bebob->mutex);
+ spin_lock_init(&bebob->lock);
+ init_waitqueue_head(&bebob->hwdep_wait);
+
+ err = name_device(bebob);
+ if (err < 0)
+ goto error;
+
+ err = detect_quirks(bebob, entry);
+ if (err < 0)
+ goto error;
+
+ if (bebob->spec == &maudio_special_spec) {
+ if (entry->model_id == MODEL_MAUDIO_FW1814)
+ err = snd_bebob_maudio_special_discover(bebob, true);
+ else
+ err = snd_bebob_maudio_special_discover(bebob, false);
+ } else {
+ err = snd_bebob_stream_discover(bebob);
+ }
+ if (err < 0)
+ goto error;
+
+ err = snd_bebob_stream_init_duplex(bebob);
+ if (err < 0)
+ goto error;
+
+ snd_bebob_proc_init(bebob);
+
+ if (bebob->midi_input_ports > 0 || bebob->midi_output_ports > 0) {
+ err = snd_bebob_create_midi_devices(bebob);
+ if (err < 0)
+ goto error;
+ }
+
+ err = snd_bebob_create_pcm_devices(bebob);
+ if (err < 0)
+ goto error;
+
+ err = snd_bebob_create_hwdep_device(bebob);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ if (entry->vendor_id == VEN_MAUDIO &&
+ (entry->model_id == MODEL_MAUDIO_FW1814 || entry->model_id == MODEL_MAUDIO_PROJECTMIX)) {
+ // This is a workaround. This bus reset seems to have an effect to make devices
+ // correctly handling transactions. Without this, the devices have gap_count
+ // mismatch. This causes much failure of transaction.
+ //
+ // Just after registration, user-land application receive signals from dbus and
+ // starts I/Os. To avoid I/Os till the future bus reset, registration is done in
+ // next update().
+ fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card, false, true);
+ }
+
+ return 0;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+/*
+ * This driver doesn't update streams in bus reset handler.
+ *
+ * DM1000/ DM1100/DM1500 chipsets with BeBoB firmware transfer packets with
+ * discontinued counter at bus reset. This discontinuity is immediately
+ * detected in packet streaming layer, then it sets XRUN to PCM substream.
+ *
+ * ALSA PCM applications can know the XRUN by getting -EPIPE from PCM operation.
+ * Then, they can recover the PCM substream by executing ioctl(2) with
+ * SNDRV_PCM_IOCTL_PREPARE. 'struct snd_pcm_ops.prepare' is called and drivers
+ * restart packet streaming.
+ *
+ * The above processing may be executed before this bus-reset handler is
+ * executed. When this handler updates streams with current isochronous
+ * channels, the streams already have the current ones.
+ */
+static void
+bebob_update(struct fw_unit *unit)
+{
+ struct snd_bebob *bebob = dev_get_drvdata(&unit->device);
+
+ if (bebob == NULL)
+ return;
+
+ fcp_bus_reset(bebob->unit);
+}
+
+static void bebob_remove(struct fw_unit *unit)
+{
+ struct snd_bebob *bebob = dev_get_drvdata(&unit->device);
+
+ if (bebob == NULL)
+ return;
+
+ // Block till all of ALSA character devices are released.
+ snd_card_free(bebob->card);
+}
+
+static const struct snd_bebob_rate_spec normal_rate_spec = {
+ .get = &snd_bebob_stream_get_rate,
+ .set = &snd_bebob_stream_set_rate
+};
+static const struct snd_bebob_spec spec_normal = {
+ .clock = NULL,
+ .rate = &normal_rate_spec,
+ .meter = NULL
+};
+
+#define SPECIFIER_1394TA 0x00a02d
+
+// The immediate entry for version in unit directory differs depending on models:
+// * 0x010001
+// * 0x014001
+#define SND_BEBOB_DEV_ENTRY(vendor, model, data) \
+{ \
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | \
+ IEEE1394_MATCH_MODEL_ID | \
+ IEEE1394_MATCH_SPECIFIER_ID, \
+ .vendor_id = vendor, \
+ .model_id = model, \
+ .specifier_id = SPECIFIER_1394TA, \
+ .driver_data = (kernel_ulong_t)data \
+}
+
+static const struct ieee1394_device_id bebob_id_table[] = {
+ /* Edirol, FA-66 */
+ SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010049, &spec_normal),
+ /* Edirol, FA-101 */
+ SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010048, &spec_normal),
+ /* Presonus, FIREBOX */
+ SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010000, &spec_normal),
+ /* PreSonus, FIREPOD/FP10 */
+ SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010066, &spec_normal),
+ /* PreSonus, Inspire1394 */
+ SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010001, &spec_normal),
+ /* BridgeCo, RDAudio1 */
+ SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010048, &spec_normal),
+ /* BridgeCo, Audio5 */
+ SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049, &spec_normal),
+ /* Mackie, Onyx 1220/1620/1640 (Firewire I/O Card) */
+ SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065, &spec_normal),
+ // Mackie, d.2 (optional Firewire card with DM1000).
+ SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067, &spec_normal),
+ /* Stanton, ScratchAmp */
+ SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001, &spec_normal),
+ /* Tascam, IF-FW DM */
+ SND_BEBOB_DEV_ENTRY(VEN_TASCAM, 0x00010067, &spec_normal),
+ /* Behringer, XENIX UFX 1204 */
+ SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001204, &spec_normal),
+ /* Behringer, XENIX UFX 1604 */
+ SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001604, &spec_normal),
+ /* Behringer, Digital Mixer X32 series (X-UF Card) */
+ SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00000006, &spec_normal),
+ /* Behringer, F-Control Audio 1616 */
+ SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x001616, &spec_normal),
+ /* Behringer, F-Control Audio 610 */
+ SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x000610, &spec_normal),
+ /* Apogee Electronics, Rosetta 200/400 (X-FireWire card) */
+ /* Apogee Electronics, DA/AD/DD-16X (X-FireWire card) */
+ SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00010048, &spec_normal),
+ /* Apogee Electronics, Ensemble */
+ SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x01eeee, &spec_normal),
+ /* ESI, Quatafire610 */
+ SND_BEBOB_DEV_ENTRY(VEN_ESI, 0x00010064, &spec_normal),
+ /* CME, MatrixKFW */
+ SND_BEBOB_DEV_ENTRY(VEN_CME, 0x00030000, &spec_normal),
+ // Phonic Helix Board 12 FireWire MkII.
+ SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00050000, &spec_normal),
+ // Phonic Helix Board 18 FireWire MkII.
+ SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00060000, &spec_normal),
+ // Phonic Helix Board 24 FireWire MkII.
+ SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00070000, &spec_normal),
+ // Phonic FireFly 808 FireWire.
+ SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00080000, &spec_normal),
+ // Phonic FireFly 202, 302, 808 Universal.
+ // Phinic Helix Board 12/18/24 FireWire, 12/18/24 Universal
+ SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00000000, &spec_normal),
+ /* Lynx, Aurora 8/16 (LT-FW) */
+ SND_BEBOB_DEV_ENTRY(VEN_LYNX, 0x00000001, &spec_normal),
+ /* ICON, FireXon */
+ SND_BEBOB_DEV_ENTRY(VEN_ICON, 0x00000001, &spec_normal),
+ /* PrismSound, Orpheus */
+ SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x00010048, &spec_normal),
+ /* PrismSound, ADA-8XR */
+ SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x0000ada8, &spec_normal),
+ /* TerraTec Electronic GmbH, PHASE 88 Rack FW */
+ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000003, &phase88_rack_spec),
+ /* TerraTec Electronic GmbH, PHASE 24 FW */
+ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000004, &yamaha_terratec_spec),
+ /* TerraTec Electronic GmbH, Phase X24 FW */
+ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000007, &yamaha_terratec_spec),
+ /* TerraTec Electronic GmbH, EWS MIC2/MIC8 */
+ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000005, &spec_normal),
+ // Terratec Electronic GmbH, Aureon 7.1 Firewire.
+ // AcousticReality, eAR Master One, Eroica, Figaro, and Ciaccona. Perhaps Terratec OEM.
+ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000002, &spec_normal),
+ /* Yamaha, GO44 */
+ SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000b, &yamaha_terratec_spec),
+ /* YAMAHA, GO46 */
+ SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000c, &yamaha_terratec_spec),
+ /* Focusrite, SaffirePro 26 I/O */
+ SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000003, &saffirepro_26_spec),
+ /* Focusrite, SaffirePro 10 I/O */
+ SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x000006, &saffirepro_10_spec),
+ /* Focusrite, Saffire(no label and LE) */
+ SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, MODEL_FOCUSRITE_SAFFIRE_BOTH,
+ &saffire_spec),
+ // M-Audio, Firewire 410. The vendor field is left as BridgeCo. AG.
+ SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010058, NULL),
+ SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010046, &maudio_fw410_spec),
+ /* M-Audio, Firewire Audiophile */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, MODEL_MAUDIO_AUDIOPHILE_BOTH,
+ &maudio_audiophile_spec),
+ /* M-Audio, Firewire Solo */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, 0x00010062, &maudio_solo_spec),
+ /* M-Audio, Ozonic */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, 0x0000000a, &maudio_ozonic_spec),
+ /* M-Audio NRV10 */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, 0x00010081, &maudio_nrv10_spec),
+ /* M-Audio, ProFireLightbridge */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, MODEL_MAUDIO_PROFIRELIGHTBRIDGE, &spec_normal),
+ /* Firewire 1814 */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, 0x00010070, NULL), /* bootloader */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, MODEL_MAUDIO_FW1814,
+ &maudio_special_spec),
+ /* M-Audio ProjectMix */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, MODEL_MAUDIO_PROJECTMIX,
+ &maudio_special_spec),
+ /* Digidesign Mbox 2 Pro */
+ SND_BEBOB_DEV_ENTRY(VEN_DIGIDESIGN, 0x0000a9, &spec_normal),
+ // Toneweal FW66.
+ SND_BEBOB_DEV_ENTRY(OUI_SHOUYO, 0x020002, &spec_normal),
+ /* IDs are unknown but able to be supported */
+ /* Apogee, Mini-ME Firewire */
+ /* Apogee, Mini-DAC Firewire */
+ /* Cakawalk, Sonar Power Studio 66 */
+ /* CME, UF400e */
+ /* ESI, Quotafire XL */
+ /* Infrasonic, DewX */
+ /* Infrasonic, Windy6 */
+ /* Mackie, Digital X Bus x.200 */
+ /* Mackie, Digital X Bus x.400 */
+ /* Rolf Spuler, Firewire Guitar */
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, bebob_id_table);
+
+static struct fw_driver bebob_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = bebob_probe,
+ .update = bebob_update,
+ .remove = bebob_remove,
+ .id_table = bebob_id_table,
+};
+
+static int __init
+snd_bebob_init(void)
+{
+ return driver_register(&bebob_driver.driver);
+}
+
+static void __exit
+snd_bebob_exit(void)
+{
+ driver_unregister(&bebob_driver.driver);
+}
+
+module_init(snd_bebob_init);
+module_exit(snd_bebob_exit);
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
new file mode 100644
index 000000000000..4d73ecb30d79
--- /dev/null
+++ b/sound/firewire/bebob/bebob.h
@@ -0,0 +1,256 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * bebob.h - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#ifndef SOUND_BEBOB_H_INCLUDED
+#define SOUND_BEBOB_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/sched/signal.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+
+#include "../lib.h"
+#include "../fcp.h"
+#include "../packets-buffer.h"
+#include "../iso-resources.h"
+#include "../amdtp-am824.h"
+#include "../cmp.h"
+
+/* basic register addresses on DM1000/DM1100/DM1500 */
+#define BEBOB_ADDR_REG_INFO 0xffffc8020000ULL
+#define BEBOB_ADDR_REG_REQ 0xffffc8021000ULL
+
+struct snd_bebob;
+
+#define SND_BEBOB_STRM_FMT_ENTRIES 7
+struct snd_bebob_stream_formation {
+ unsigned int pcm;
+ unsigned int midi;
+};
+/* this is a lookup table for index of stream formations */
+extern const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES];
+
+/* device specific operations */
+enum snd_bebob_clock_type {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL = 0,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL,
+ SND_BEBOB_CLOCK_TYPE_SYT,
+};
+struct snd_bebob_clock_spec {
+ unsigned int num;
+ const char *const *labels;
+ const enum snd_bebob_clock_type *types;
+ int (*get)(struct snd_bebob *bebob, unsigned int *id);
+};
+struct snd_bebob_rate_spec {
+ int (*get)(struct snd_bebob *bebob, unsigned int *rate);
+ int (*set)(struct snd_bebob *bebob, unsigned int rate);
+};
+struct snd_bebob_meter_spec {
+ unsigned int num;
+ const char *const *labels;
+ int (*get)(struct snd_bebob *bebob, u32 *target, unsigned int size);
+};
+struct snd_bebob_spec {
+ const struct snd_bebob_clock_spec *clock;
+ const struct snd_bebob_rate_spec *rate;
+ const struct snd_bebob_meter_spec *meter;
+};
+
+enum snd_bebob_quirk {
+ SND_BEBOB_QUIRK_INITIAL_DISCONTINUOUS_DBC = (1 << 0),
+ SND_BEBOB_QUIRK_WRONG_DBC = (1 << 1),
+};
+
+struct snd_bebob {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ int card_index;
+
+ struct mutex mutex;
+ spinlock_t lock;
+
+ const struct snd_bebob_spec *spec;
+ unsigned int quirks; // Combination of snd_bebob_quirk enumerations.
+
+ unsigned int midi_input_ports;
+ unsigned int midi_output_ports;
+
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ struct cmp_connection out_conn;
+ struct cmp_connection in_conn;
+ unsigned int substreams_counter;
+
+ struct snd_bebob_stream_formation
+ tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
+ struct snd_bebob_stream_formation
+ rx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
+
+ int sync_input_plug;
+
+ /* for uapi */
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* for M-Audio special devices */
+ void *maudio_special_quirk;
+
+ struct amdtp_domain domain;
+};
+
+static inline int
+snd_bebob_read_block(struct fw_unit *unit, u64 addr, void *buf, int size)
+{
+ return snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
+ BEBOB_ADDR_REG_INFO + addr,
+ buf, size, 0);
+}
+
+static inline int
+snd_bebob_read_quad(struct fw_unit *unit, u64 addr, u32 *buf)
+{
+ return snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
+ BEBOB_ADDR_REG_INFO + addr,
+ (void *)buf, sizeof(u32), 0);
+}
+
+/* AV/C Audio Subunit Specification 1.0 (Oct 2000, 1394TA) */
+int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
+ unsigned int fb_id, unsigned int num);
+int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
+ unsigned int fb_id, unsigned int *num);
+
+/*
+ * AVC command extensions, AV/C Unit and Subunit, Revision 17
+ * (Nov 2003, BridgeCo)
+ */
+#define AVC_BRIDGECO_ADDR_BYTES 6
+enum avc_bridgeco_plug_dir {
+ AVC_BRIDGECO_PLUG_DIR_IN = 0x00,
+ AVC_BRIDGECO_PLUG_DIR_OUT = 0x01
+};
+enum avc_bridgeco_plug_mode {
+ AVC_BRIDGECO_PLUG_MODE_UNIT = 0x00,
+ AVC_BRIDGECO_PLUG_MODE_SUBUNIT = 0x01,
+ AVC_BRIDGECO_PLUG_MODE_FUNCTION_BLOCK = 0x02
+};
+enum avc_bridgeco_plug_unit {
+ AVC_BRIDGECO_PLUG_UNIT_ISOC = 0x00,
+ AVC_BRIDGECO_PLUG_UNIT_EXT = 0x01,
+ AVC_BRIDGECO_PLUG_UNIT_ASYNC = 0x02
+};
+enum avc_bridgeco_plug_type {
+ AVC_BRIDGECO_PLUG_TYPE_ISOC = 0x00,
+ AVC_BRIDGECO_PLUG_TYPE_ASYNC = 0x01,
+ AVC_BRIDGECO_PLUG_TYPE_MIDI = 0x02,
+ AVC_BRIDGECO_PLUG_TYPE_SYNC = 0x03,
+ AVC_BRIDGECO_PLUG_TYPE_ANA = 0x04,
+ AVC_BRIDGECO_PLUG_TYPE_DIG = 0x05,
+ AVC_BRIDGECO_PLUG_TYPE_ADDITION = 0x06
+};
+static inline void
+avc_bridgeco_fill_unit_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES],
+ enum avc_bridgeco_plug_dir dir,
+ enum avc_bridgeco_plug_unit unit,
+ unsigned int pid)
+{
+ buf[0] = 0xff; /* Unit */
+ buf[1] = dir;
+ buf[2] = AVC_BRIDGECO_PLUG_MODE_UNIT;
+ buf[3] = unit;
+ buf[4] = 0xff & pid;
+ buf[5] = 0xff; /* reserved */
+}
+static inline void
+avc_bridgeco_fill_msu_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES],
+ enum avc_bridgeco_plug_dir dir,
+ unsigned int pid)
+{
+ buf[0] = 0x60; /* Music subunit */
+ buf[1] = dir;
+ buf[2] = AVC_BRIDGECO_PLUG_MODE_SUBUNIT;
+ buf[3] = 0xff & pid;
+ buf[4] = 0xff; /* reserved */
+ buf[5] = 0xff; /* reserved */
+}
+int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ u8 *buf, unsigned int len);
+int avc_bridgeco_get_plug_type(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ enum avc_bridgeco_plug_type *type);
+int avc_bridgeco_get_plug_ch_count(struct fw_unit *unit, u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ unsigned int *ch_count);
+int avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ unsigned int id, u8 *type);
+int avc_bridgeco_get_plug_input(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ u8 input[7]);
+int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 *buf,
+ unsigned int *len, unsigned int eid);
+
+/* for AMDTP streaming */
+int snd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *rate);
+int snd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate);
+int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
+ enum snd_bebob_clock_type *src);
+int snd_bebob_stream_discover(struct snd_bebob *bebob);
+int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
+int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer);
+int snd_bebob_stream_start_duplex(struct snd_bebob *bebob);
+void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
+void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);
+
+void snd_bebob_stream_lock_changed(struct snd_bebob *bebob);
+int snd_bebob_stream_lock_try(struct snd_bebob *bebob);
+void snd_bebob_stream_lock_release(struct snd_bebob *bebob);
+
+void snd_bebob_proc_init(struct snd_bebob *bebob);
+
+int snd_bebob_create_midi_devices(struct snd_bebob *bebob);
+
+int snd_bebob_create_pcm_devices(struct snd_bebob *bebob);
+
+int snd_bebob_create_hwdep_device(struct snd_bebob *bebob);
+
+/* model specific operations */
+extern const struct snd_bebob_spec phase88_rack_spec;
+extern const struct snd_bebob_spec yamaha_terratec_spec;
+extern const struct snd_bebob_spec saffirepro_26_spec;
+extern const struct snd_bebob_spec saffirepro_10_spec;
+extern const struct snd_bebob_spec saffire_le_spec;
+extern const struct snd_bebob_spec saffire_spec;
+extern const struct snd_bebob_spec maudio_fw410_spec;
+extern const struct snd_bebob_spec maudio_audiophile_spec;
+extern const struct snd_bebob_spec maudio_solo_spec;
+extern const struct snd_bebob_spec maudio_ozonic_spec;
+extern const struct snd_bebob_spec maudio_nrv10_spec;
+extern const struct snd_bebob_spec maudio_special_spec;
+int snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814);
+int snd_bebob_maudio_load_firmware(struct fw_unit *unit);
+
+#endif
diff --git a/sound/firewire/bebob/bebob_command.c b/sound/firewire/bebob/bebob_command.c
new file mode 100644
index 000000000000..022df09c68ff
--- /dev/null
+++ b/sound/firewire/bebob/bebob_command.c
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_command.c - driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./bebob.h"
+
+int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
+ unsigned int fb_id, unsigned int num)
+{
+ u8 *buf;
+ int err;
+
+ buf = kzalloc(12, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x00; /* AV/C CONTROL */
+ buf[1] = 0x08 | (0x07 & subunit_id); /* AUDIO SUBUNIT ID */
+ buf[2] = 0xb8; /* FUNCTION BLOCK */
+ buf[3] = 0x80; /* type is 'selector'*/
+ buf[4] = 0xff & fb_id; /* function block id */
+ buf[5] = 0x10; /* control attribute is CURRENT */
+ buf[6] = 0x02; /* selector length is 2 */
+ buf[7] = 0xff & num; /* input function block plug number */
+ buf[8] = 0x01; /* control selector is SELECTOR_CONTROL */
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(8));
+ if (err < 0)
+ ;
+ else if (err < 9)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else
+ err = 0;
+
+ kfree(buf);
+ return err;
+}
+
+int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
+ unsigned int fb_id, unsigned int *num)
+{
+ u8 *buf;
+ int err;
+
+ buf = kzalloc(12, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x01; /* AV/C STATUS */
+ buf[1] = 0x08 | (0x07 & subunit_id); /* AUDIO SUBUNIT ID */
+ buf[2] = 0xb8; /* FUNCTION BLOCK */
+ buf[3] = 0x80; /* type is 'selector'*/
+ buf[4] = 0xff & fb_id; /* function block id */
+ buf[5] = 0x10; /* control attribute is CURRENT */
+ buf[6] = 0x02; /* selector length is 2 */
+ buf[7] = 0xff; /* input function block plug number */
+ buf[8] = 0x01; /* control selector is SELECTOR_CONTROL */
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(8));
+ if (err < 0)
+ ;
+ else if (err < 9)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ *num = buf[7];
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+
+static inline void
+avc_bridgeco_fill_extension_addr(u8 *buf, u8 *addr)
+{
+ buf[1] = addr[0];
+ memcpy(buf + 4, addr + 1, 5);
+}
+
+static inline void
+avc_bridgeco_fill_plug_info_extension_command(u8 *buf, u8 *addr,
+ unsigned int itype)
+{
+ buf[0] = 0x01; /* AV/C STATUS */
+ buf[2] = 0x02; /* AV/C GENERAL PLUG INFO */
+ buf[3] = 0xc0; /* BridgeCo extension */
+ avc_bridgeco_fill_extension_addr(buf, addr);
+ buf[9] = itype; /* info type */
+}
+
+int avc_bridgeco_get_plug_type(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ enum avc_bridgeco_plug_type *type)
+{
+ u8 *buf;
+ int err;
+
+ buf = kzalloc(12, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* Info type is 'plug type'. */
+ avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x00);
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(9));
+ if (err < 0)
+ ;
+ else if (err < 11)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ *type = buf[10];
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+
+int avc_bridgeco_get_plug_ch_count(struct fw_unit *unit, u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ unsigned int *ch_count)
+{
+ u8 *buf;
+ int err;
+
+ buf = kzalloc(12, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ // Info type is 'plug type'.
+ avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x02);
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(9));
+ if (err < 0)
+ ;
+ else if (err < 11)
+ err = -EIO;
+ else if (buf[0] == 0x08) // NOT IMPLEMENTED
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) // REJECTED
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) // IN TRANSITION
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ *ch_count = buf[10];
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+
+int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ u8 *buf, unsigned int len)
+{
+ int err;
+
+ /* Info type is 'channel position'. */
+ avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x03);
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 256,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) |
+ BIT(5) | BIT(6) | BIT(7) | BIT(9));
+ if (err < 0)
+ ;
+ else if (err < 11)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ /* Pick up specific data. */
+ memmove(buf, buf + 10, err - 10);
+ err = 0;
+end:
+ return err;
+}
+
+int avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ unsigned int id, u8 *type)
+{
+ u8 *buf;
+ int err;
+
+ /* section info includes charactors but this module don't need it */
+ buf = kzalloc(12, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* Info type is 'section info'. */
+ avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x07);
+ buf[10] = 0xff & ++id; /* section id */
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(9) | BIT(10));
+ if (err < 0)
+ ;
+ else if (err < 12)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ *type = buf[11];
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+
+int avc_bridgeco_get_plug_input(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 input[7])
+{
+ int err;
+ u8 *buf;
+
+ buf = kzalloc(18, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* Info type is 'plug input'. */
+ avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x05);
+
+ err = fcp_avc_transaction(unit, buf, 16, buf, 16,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7));
+ if (err < 0)
+ ;
+ else if (err < 16)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ memcpy(input, buf + 10, 5);
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+
+int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 *buf,
+ unsigned int *len, unsigned int eid)
+{
+ int err;
+
+ /* check given buffer */
+ if ((buf == NULL) || (*len < 12)) {
+ err = -EINVAL;
+ goto end;
+ }
+
+ buf[0] = 0x01; /* AV/C STATUS */
+ buf[2] = 0x2f; /* AV/C STREAM FORMAT SUPPORT */
+ buf[3] = 0xc1; /* Bridgeco extension - List Request */
+ avc_bridgeco_fill_extension_addr(buf, addr);
+ buf[10] = 0xff & eid; /* Entry ID */
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, *len,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(10));
+ if (err < 0)
+ ;
+ else if (err < 12)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ else if (buf[10] != eid)
+ err = -EIO;
+ if (err < 0)
+ goto end;
+
+ /* Pick up 'stream format info'. */
+ memmove(buf, buf + 11, err - 11);
+ *len = err - 11;
+ err = 0;
+end:
+ return err;
+}
diff --git a/sound/firewire/bebob/bebob_focusrite.c b/sound/firewire/bebob/bebob_focusrite.c
new file mode 100644
index 000000000000..06d6a37cd853
--- /dev/null
+++ b/sound/firewire/bebob/bebob_focusrite.c
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_focusrite.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./bebob.h"
+
+#define ANA_IN "Analog In"
+#define DIG_IN "Digital In"
+#define ANA_OUT "Analog Out"
+#define DIG_OUT "Digital Out"
+#define STM_IN "Stream In"
+
+#define SAFFIRE_ADDRESS_BASE 0x000100000000ULL
+
+#define SAFFIRE_OFFSET_CLOCK_SOURCE 0x00f8
+#define SAFFIREPRO_OFFSET_CLOCK_SOURCE 0x0174
+
+/* whether sync to external device or not */
+#define SAFFIRE_OFFSET_CLOCK_SYNC_EXT 0x013c
+#define SAFFIRE_LE_OFFSET_CLOCK_SYNC_EXT 0x0432
+#define SAFFIREPRO_OFFSET_CLOCK_SYNC_EXT 0x0164
+
+#define SAFFIRE_CLOCK_SOURCE_INTERNAL 0
+#define SAFFIRE_CLOCK_SOURCE_SPDIF 1
+
+/* clock sources as returned from register of Saffire Pro 10 and 26 */
+#define SAFFIREPRO_CLOCK_SOURCE_SELECT_MASK 0x000000ff
+#define SAFFIREPRO_CLOCK_SOURCE_DETECT_MASK 0x0000ff00
+#define SAFFIREPRO_CLOCK_SOURCE_INTERNAL 0
+#define SAFFIREPRO_CLOCK_SOURCE_SKIP 1 /* never used on hardware */
+#define SAFFIREPRO_CLOCK_SOURCE_SPDIF 2
+#define SAFFIREPRO_CLOCK_SOURCE_ADAT1 3 /* not used on s.pro. 10 */
+#define SAFFIREPRO_CLOCK_SOURCE_ADAT2 4 /* not used on s.pro. 10 */
+#define SAFFIREPRO_CLOCK_SOURCE_WORDCLOCK 5
+#define SAFFIREPRO_CLOCK_SOURCE_COUNT 6
+
+/* S/PDIF, ADAT1, ADAT2 is enabled or not. three quadlets */
+#define SAFFIREPRO_ENABLE_DIG_IFACES 0x01a4
+
+/* saffirepro has its own parameter for sampling frequency */
+#define SAFFIREPRO_RATE_NOREBOOT 0x01cc
+/* index is the value for this register */
+static const unsigned int rates[] = {
+ [0] = 0,
+ [1] = 44100,
+ [2] = 48000,
+ [3] = 88200,
+ [4] = 96000,
+ [5] = 176400,
+ [6] = 192000
+};
+
+/* saffire(no label)/saffire LE has metering */
+#define SAFFIRE_OFFSET_METER 0x0100
+#define SAFFIRE_LE_OFFSET_METER 0x0168
+
+static inline int
+saffire_read_block(struct snd_bebob *bebob, u64 offset,
+ u32 *buf, unsigned int size)
+{
+ unsigned int i;
+ int err;
+ __be32 *tmp = (__be32 *)buf;
+
+ err = snd_fw_transaction(bebob->unit, TCODE_READ_BLOCK_REQUEST,
+ SAFFIRE_ADDRESS_BASE + offset,
+ tmp, size, 0);
+ if (err < 0)
+ goto end;
+
+ for (i = 0; i < size / sizeof(u32); i++)
+ buf[i] = be32_to_cpu(tmp[i]);
+end:
+ return err;
+}
+
+static inline int
+saffire_read_quad(struct snd_bebob *bebob, u64 offset, u32 *value)
+{
+ int err;
+ __be32 tmp;
+
+ err = snd_fw_transaction(bebob->unit, TCODE_READ_QUADLET_REQUEST,
+ SAFFIRE_ADDRESS_BASE + offset,
+ &tmp, sizeof(__be32), 0);
+ if (err < 0)
+ goto end;
+
+ *value = be32_to_cpu(tmp);
+end:
+ return err;
+}
+
+static inline int
+saffire_write_quad(struct snd_bebob *bebob, u64 offset, u32 value)
+{
+ __be32 data = cpu_to_be32(value);
+
+ return snd_fw_transaction(bebob->unit, TCODE_WRITE_QUADLET_REQUEST,
+ SAFFIRE_ADDRESS_BASE + offset,
+ &data, sizeof(__be32), 0);
+}
+
+static const enum snd_bebob_clock_type saffirepro_10_clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
+};
+static const enum snd_bebob_clock_type saffirepro_26_clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* ADAT1 */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* ADAT2 */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
+};
+/* Value maps between registers and labels for SaffirePro 10/26. */
+static const signed char saffirepro_clk_maps[][SAFFIREPRO_CLOCK_SOURCE_COUNT] = {
+ /* SaffirePro 10 */
+ [0] = {
+ [SAFFIREPRO_CLOCK_SOURCE_INTERNAL] = 0,
+ [SAFFIREPRO_CLOCK_SOURCE_SKIP] = -1, /* not supported */
+ [SAFFIREPRO_CLOCK_SOURCE_SPDIF] = 1,
+ [SAFFIREPRO_CLOCK_SOURCE_ADAT1] = -1, /* not supported */
+ [SAFFIREPRO_CLOCK_SOURCE_ADAT2] = -1, /* not supported */
+ [SAFFIREPRO_CLOCK_SOURCE_WORDCLOCK] = 2,
+ },
+ /* SaffirePro 26 */
+ [1] = {
+ [SAFFIREPRO_CLOCK_SOURCE_INTERNAL] = 0,
+ [SAFFIREPRO_CLOCK_SOURCE_SKIP] = -1, /* not supported */
+ [SAFFIREPRO_CLOCK_SOURCE_SPDIF] = 1,
+ [SAFFIREPRO_CLOCK_SOURCE_ADAT1] = 2,
+ [SAFFIREPRO_CLOCK_SOURCE_ADAT2] = 3,
+ [SAFFIREPRO_CLOCK_SOURCE_WORDCLOCK] = 4,
+ }
+};
+
+static int
+saffirepro_both_clk_freq_get(struct snd_bebob *bebob, unsigned int *rate)
+{
+ u32 id;
+ int err;
+
+ err = saffire_read_quad(bebob, SAFFIREPRO_RATE_NOREBOOT, &id);
+ if (err < 0)
+ goto end;
+ if (id >= ARRAY_SIZE(rates))
+ err = -EIO;
+ else
+ *rate = rates[id];
+end:
+ return err;
+}
+static int
+saffirepro_both_clk_freq_set(struct snd_bebob *bebob, unsigned int rate)
+{
+ u32 id;
+
+ for (id = 0; id < ARRAY_SIZE(rates); id++) {
+ if (rates[id] == rate)
+ break;
+ }
+ if (id == ARRAY_SIZE(rates))
+ return -EINVAL;
+
+ return saffire_write_quad(bebob, SAFFIREPRO_RATE_NOREBOOT, id);
+}
+
+/*
+ * query hardware for current clock source, return our internally
+ * used clock index in *id, depending on hardware.
+ */
+static int
+saffirepro_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+ int err;
+ u32 value; /* clock source read from hw register */
+ const signed char *map;
+
+ err = saffire_read_quad(bebob, SAFFIREPRO_OFFSET_CLOCK_SOURCE, &value);
+ if (err < 0)
+ goto end;
+
+ /* depending on hardware, use a different mapping */
+ if (bebob->spec->clock->types == saffirepro_10_clk_src_types)
+ map = saffirepro_clk_maps[0];
+ else
+ map = saffirepro_clk_maps[1];
+
+ /* In a case that this driver cannot handle the value of register. */
+ value &= SAFFIREPRO_CLOCK_SOURCE_SELECT_MASK;
+ if (value >= SAFFIREPRO_CLOCK_SOURCE_COUNT || map[value] < 0) {
+ err = -EIO;
+ goto end;
+ }
+
+ *id = (unsigned int)map[value];
+end:
+ return err;
+}
+
+const struct snd_bebob_spec saffire_le_spec;
+static const enum snd_bebob_clock_type saffire_both_clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL,
+};
+static int
+saffire_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+ int err;
+ u32 value;
+
+ err = saffire_read_quad(bebob, SAFFIRE_OFFSET_CLOCK_SOURCE, &value);
+ if (err >= 0)
+ *id = 0xff & value;
+
+ return err;
+};
+static const char *const saffire_le_meter_labels[] = {
+ ANA_IN, ANA_IN, DIG_IN,
+ ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT,
+ STM_IN, STM_IN
+};
+static const char *const saffire_meter_labels[] = {
+ ANA_IN, ANA_IN,
+ STM_IN, STM_IN, STM_IN, STM_IN, STM_IN,
+};
+static int
+saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
+{
+ const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+ unsigned int channels;
+ u64 offset;
+ int err;
+
+ if (spec->labels == saffire_le_meter_labels)
+ offset = SAFFIRE_LE_OFFSET_METER;
+ else
+ offset = SAFFIRE_OFFSET_METER;
+
+ channels = spec->num * 2;
+ if (size < channels * sizeof(u32))
+ return -EIO;
+
+ err = saffire_read_block(bebob, offset, buf, size);
+ if (err >= 0 && spec->labels == saffire_le_meter_labels) {
+ swap(buf[1], buf[3]);
+ swap(buf[2], buf[3]);
+ swap(buf[3], buf[4]);
+
+ swap(buf[7], buf[10]);
+ swap(buf[8], buf[10]);
+ swap(buf[9], buf[11]);
+ swap(buf[11], buf[12]);
+
+ swap(buf[15], buf[16]);
+ }
+
+ return err;
+}
+
+static const struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
+ .get = &saffirepro_both_clk_freq_get,
+ .set = &saffirepro_both_clk_freq_set,
+};
+/* Saffire Pro 26 I/O */
+static const struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
+ .num = ARRAY_SIZE(saffirepro_26_clk_src_types),
+ .types = saffirepro_26_clk_src_types,
+ .get = &saffirepro_both_clk_src_get,
+};
+const struct snd_bebob_spec saffirepro_26_spec = {
+ .clock = &saffirepro_26_clk_spec,
+ .rate = &saffirepro_both_rate_spec,
+ .meter = NULL
+};
+/* Saffire Pro 10 I/O */
+static const struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
+ .num = ARRAY_SIZE(saffirepro_10_clk_src_types),
+ .types = saffirepro_10_clk_src_types,
+ .get = &saffirepro_both_clk_src_get,
+};
+const struct snd_bebob_spec saffirepro_10_spec = {
+ .clock = &saffirepro_10_clk_spec,
+ .rate = &saffirepro_both_rate_spec,
+ .meter = NULL
+};
+
+static const struct snd_bebob_rate_spec saffire_both_rate_spec = {
+ .get = &snd_bebob_stream_get_rate,
+ .set = &snd_bebob_stream_set_rate,
+};
+static const struct snd_bebob_clock_spec saffire_both_clk_spec = {
+ .num = ARRAY_SIZE(saffire_both_clk_src_types),
+ .types = saffire_both_clk_src_types,
+ .get = &saffire_both_clk_src_get,
+};
+/* Saffire LE */
+static const struct snd_bebob_meter_spec saffire_le_meter_spec = {
+ .num = ARRAY_SIZE(saffire_le_meter_labels),
+ .labels = saffire_le_meter_labels,
+ .get = &saffire_meter_get,
+};
+const struct snd_bebob_spec saffire_le_spec = {
+ .clock = &saffire_both_clk_spec,
+ .rate = &saffire_both_rate_spec,
+ .meter = &saffire_le_meter_spec
+};
+/* Saffire */
+static const struct snd_bebob_meter_spec saffire_meter_spec = {
+ .num = ARRAY_SIZE(saffire_meter_labels),
+ .labels = saffire_meter_labels,
+ .get = &saffire_meter_get,
+};
+const struct snd_bebob_spec saffire_spec = {
+ .clock = &saffire_both_clk_spec,
+ .rate = &saffire_both_rate_spec,
+ .meter = &saffire_meter_spec
+};
diff --git a/sound/firewire/bebob/bebob_hwdep.c b/sound/firewire/bebob/bebob_hwdep.c
new file mode 100644
index 000000000000..216d1fceb6e7
--- /dev/null
+++ b/sound/firewire/bebob/bebob_hwdep.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_hwdep.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node infomation
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "bebob.h"
+
+static long
+hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_bebob *bebob = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&bebob->lock);
+
+ while (!bebob->dev_lock_changed) {
+ prepare_to_wait(&bebob->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&bebob->lock);
+ schedule();
+ finish_wait(&bebob->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&bebob->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ count = min_t(long, count, sizeof(event.lock_status));
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (bebob->dev_lock_count > 0);
+ bebob->dev_lock_changed = false;
+
+ spin_unlock_irq(&bebob->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static __poll_t
+hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
+{
+ struct snd_bebob *bebob = hwdep->private_data;
+
+ poll_wait(file, &bebob->hwdep_wait, wait);
+
+ guard(spinlock_irq)(&bebob->lock);
+ if (bebob->dev_lock_changed)
+ return EPOLLIN | EPOLLRDNORM;
+ else
+ return 0;
+}
+
+static int
+hwdep_get_info(struct snd_bebob *bebob, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(bebob->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_BEBOB;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strscpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int
+hwdep_lock(struct snd_bebob *bebob)
+{
+ guard(spinlock_irq)(&bebob->lock);
+
+ if (bebob->dev_lock_count == 0) {
+ bebob->dev_lock_count = -1;
+ return 0;
+ } else {
+ return -EBUSY;
+ }
+}
+
+static int
+hwdep_unlock(struct snd_bebob *bebob)
+{
+ guard(spinlock_irq)(&bebob->lock);
+
+ if (bebob->dev_lock_count == -1) {
+ bebob->dev_lock_count = 0;
+ return 0;
+ } else {
+ return -EBADFD;
+ }
+}
+
+static int
+hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_bebob *bebob = hwdep->private_data;
+
+ guard(spinlock_irq)(&bebob->lock);
+ if (bebob->dev_lock_count == -1)
+ bebob->dev_lock_count = 0;
+
+ return 0;
+}
+
+static int
+hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_bebob *bebob = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(bebob, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(bebob);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(bebob);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int
+hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_bebob_create_hwdep_device(struct snd_bebob *bebob)
+{
+ static const struct snd_hwdep_ops ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(bebob->card, "BeBoB", 0, &hwdep);
+ if (err < 0)
+ goto end;
+ strscpy(hwdep->name, "BeBoB");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_BEBOB;
+ hwdep->ops = ops;
+ hwdep->private_data = bebob;
+ hwdep->exclusive = true;
+end:
+ return err;
+}
+
diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c
new file mode 100644
index 000000000000..376a9a175479
--- /dev/null
+++ b/sound/firewire/bebob/bebob_maudio.c
@@ -0,0 +1,785 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_maudio.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./bebob.h"
+#include <sound/control.h>
+
+/*
+ * Just powering on, Firewire 410/Audiophile/1814 and ProjectMix I/O wait to
+ * download firmware blob. To enable these devices, drivers should upload
+ * firmware blob and send a command to initialize configuration to factory
+ * settings when completing uploading. Then these devices generate bus reset
+ * and are recognized as new devices with the firmware.
+ *
+ * But with firmware version 5058 or later, the firmware is stored to flash
+ * memory in the device and drivers can tell bootloader to load the firmware
+ * by sending a cue. This cue must be sent one time.
+ *
+ * For streaming, both of output and input streams are needed for Firewire 410
+ * and Ozonic. The single stream is OK for the other devices even if the clock
+ * source is not SYT-Match (I note no devices use SYT-Match).
+ *
+ * Without streaming, the devices except for Firewire Audiophile can mix any
+ * input and output. For this reason, Audiophile cannot be used as standalone
+ * mixer.
+ *
+ * Firewire 1814 and ProjectMix I/O uses special firmware. It will be freezed
+ * when receiving any commands which the firmware can't understand. These
+ * devices utilize completely different system to control. It is some
+ * write-transaction directly into a certain address. All of addresses for mixer
+ * functionality is between 0xffc700700000 to 0xffc70070009c.
+ */
+
+/* Offset from information register */
+#define INFO_OFFSET_SW_DATE 0x20
+
+/* Bootloader Protocol Version 1 */
+#define MAUDIO_BOOTLOADER_CUE1 0x00000001
+/*
+ * Initializing configuration to factory settings (= 0x1101), (swapped in line),
+ * Command code is zero (= 0x00),
+ * the number of operands is zero (= 0x00)(at least significant byte)
+ */
+#define MAUDIO_BOOTLOADER_CUE2 0x01110000
+/* padding */
+#define MAUDIO_BOOTLOADER_CUE3 0x00000000
+
+#define MAUDIO_SPECIFIC_ADDRESS 0xffc700000000ULL
+
+#define METER_OFFSET 0x00600000
+
+/* some device has sync info after metering data */
+#define METER_SIZE_SPECIAL 84 /* with sync info */
+#define METER_SIZE_FW410 76 /* with sync info */
+#define METER_SIZE_AUDIOPHILE 60 /* with sync info */
+#define METER_SIZE_SOLO 52 /* with sync info */
+#define METER_SIZE_OZONIC 48
+#define METER_SIZE_NRV10 80
+
+/* labels for metering */
+#define ANA_IN "Analog In"
+#define ANA_OUT "Analog Out"
+#define DIG_IN "Digital In"
+#define SPDIF_IN "S/PDIF In"
+#define ADAT_IN "ADAT In"
+#define DIG_OUT "Digital Out"
+#define SPDIF_OUT "S/PDIF Out"
+#define ADAT_OUT "ADAT Out"
+#define STRM_IN "Stream In"
+#define AUX_OUT "Aux Out"
+#define HP_OUT "HP Out"
+/* for NRV */
+#define UNKNOWN_METER "Unknown"
+
+struct special_params {
+ bool is1814;
+ unsigned int clk_src;
+ unsigned int dig_in_fmt;
+ unsigned int dig_out_fmt;
+ unsigned int clk_lock;
+ struct snd_ctl_elem_id *ctl_id_sync;
+};
+
+/*
+ * For some M-Audio devices, this module just send cue to load firmware. After
+ * loading, the device generates bus reset and newly detected.
+ *
+ * If we make any transactions to load firmware, the operation may failed.
+ */
+int snd_bebob_maudio_load_firmware(struct fw_unit *unit)
+{
+ struct fw_device *device = fw_parent_device(unit);
+ int err, rcode;
+ u64 date;
+ __le32 *cues;
+
+ /* check date of software used to build */
+ err = snd_bebob_read_block(unit, INFO_OFFSET_SW_DATE,
+ &date, sizeof(u64));
+ if (err < 0)
+ return err;
+ /*
+ * firmware version 5058 or later has date later than "20070401", but
+ * 'date' is not null-terminated.
+ */
+ if (date < 0x3230303730343031LL) {
+ dev_err(&unit->device,
+ "Use firmware version 5058 or later\n");
+ return -ENXIO;
+ }
+
+ cues = kmalloc_array(3, sizeof(*cues), GFP_KERNEL);
+ if (!cues)
+ return -ENOMEM;
+
+ cues[0] = cpu_to_le32(MAUDIO_BOOTLOADER_CUE1);
+ cues[1] = cpu_to_le32(MAUDIO_BOOTLOADER_CUE2);
+ cues[2] = cpu_to_le32(MAUDIO_BOOTLOADER_CUE3);
+
+ rcode = fw_run_transaction(device->card, TCODE_WRITE_BLOCK_REQUEST,
+ device->node_id, device->generation,
+ device->max_speed, BEBOB_ADDR_REG_REQ,
+ cues, 3 * sizeof(*cues));
+ kfree(cues);
+ if (rcode != RCODE_COMPLETE) {
+ dev_err(&unit->device,
+ "Failed to send a cue to load firmware\n");
+ err = -EIO;
+ }
+
+ return err;
+}
+
+static inline int
+get_meter(struct snd_bebob *bebob, void *buf, unsigned int size)
+{
+ return snd_fw_transaction(bebob->unit, TCODE_READ_BLOCK_REQUEST,
+ MAUDIO_SPECIFIC_ADDRESS + METER_OFFSET,
+ buf, size, 0);
+}
+
+static int
+check_clk_sync(struct snd_bebob *bebob, unsigned int size, bool *sync)
+{
+ int err;
+ u8 *buf;
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ err = get_meter(bebob, buf, size);
+ if (err < 0)
+ goto end;
+
+ /* if synced, this value is the same as SFC of FDF in CIP header */
+ *sync = (buf[size - 2] != 0xff);
+end:
+ kfree(buf);
+ return err;
+}
+
+/*
+ * dig_fmt: 0x00:S/PDIF, 0x01:ADAT
+ * clk_lock: 0x00:unlock, 0x01:lock
+ */
+static int
+avc_maudio_set_special_clk(struct snd_bebob *bebob, unsigned int clk_src,
+ unsigned int dig_in_fmt, unsigned int dig_out_fmt,
+ unsigned int clk_lock)
+{
+ struct special_params *params = bebob->maudio_special_quirk;
+ int err;
+ u8 *buf;
+
+ if (amdtp_stream_running(&bebob->rx_stream) ||
+ amdtp_stream_running(&bebob->tx_stream))
+ return -EBUSY;
+
+ buf = kmalloc(12, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x00; /* CONTROL */
+ buf[1] = 0xff; /* UNIT */
+ buf[2] = 0x00; /* vendor dependent */
+ buf[3] = 0x04; /* company ID high */
+ buf[4] = 0x00; /* company ID middle */
+ buf[5] = 0x04; /* company ID low */
+ buf[6] = 0xff & clk_src; /* clock source */
+ buf[7] = 0xff & dig_in_fmt; /* input digital format */
+ buf[8] = 0xff & dig_out_fmt; /* output digital format */
+ buf[9] = 0xff & clk_lock; /* lock these settings */
+ buf[10] = 0x00; /* padding */
+ buf[11] = 0x00; /* padding */
+
+ err = fcp_avc_transaction(bebob->unit, buf, 12, buf, 12,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) |
+ BIT(5) | BIT(6) | BIT(7) | BIT(8) |
+ BIT(9));
+ if ((err > 0) && (err < 10))
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ if (err < 0)
+ goto end;
+
+ params->clk_src = buf[6];
+ params->dig_in_fmt = buf[7];
+ params->dig_out_fmt = buf[8];
+ params->clk_lock = buf[9];
+
+ if (params->ctl_id_sync)
+ snd_ctl_notify(bebob->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ params->ctl_id_sync);
+
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+static void
+special_stream_formation_set(struct snd_bebob *bebob)
+{
+ static const unsigned int ch_table[2][2][3] = {
+ /* AMDTP_OUT_STREAM */
+ { { 6, 6, 4 }, /* SPDIF */
+ { 12, 8, 4 } }, /* ADAT */
+ /* AMDTP_IN_STREAM */
+ { { 10, 10, 2 }, /* SPDIF */
+ { 16, 12, 2 } } /* ADAT */
+ };
+ struct special_params *params = bebob->maudio_special_quirk;
+ unsigned int i, max;
+
+ max = SND_BEBOB_STRM_FMT_ENTRIES - 1;
+ if (!params->is1814)
+ max -= 2;
+
+ for (i = 0; i < max; i++) {
+ bebob->tx_stream_formations[i + 1].pcm =
+ ch_table[AMDTP_IN_STREAM][params->dig_in_fmt][i / 2];
+ bebob->tx_stream_formations[i + 1].midi = 1;
+
+ bebob->rx_stream_formations[i + 1].pcm =
+ ch_table[AMDTP_OUT_STREAM][params->dig_out_fmt][i / 2];
+ bebob->rx_stream_formations[i + 1].midi = 1;
+ }
+}
+
+static int add_special_controls(struct snd_bebob *bebob);
+int
+snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814)
+{
+ struct special_params *params;
+ int err;
+
+ params = devm_kzalloc(&bebob->card->card_dev,
+ sizeof(struct special_params), GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+
+ guard(mutex)(&bebob->mutex);
+
+ bebob->maudio_special_quirk = (void *)params;
+ params->is1814 = is1814;
+
+ /* initialize these parameters because driver is not allowed to ask */
+ bebob->rx_stream.context = ERR_PTR(-1);
+ bebob->tx_stream.context = ERR_PTR(-1);
+ err = avc_maudio_set_special_clk(bebob, 0x03, 0x00, 0x00, 0x00);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to initialize clock params: %d\n", err);
+ return err;
+ }
+
+ err = add_special_controls(bebob);
+ if (err < 0)
+ return err;
+
+ special_stream_formation_set(bebob);
+
+ if (params->is1814) {
+ bebob->midi_input_ports = 1;
+ bebob->midi_output_ports = 1;
+ } else {
+ bebob->midi_input_ports = 2;
+ bebob->midi_output_ports = 2;
+ }
+ return err;
+}
+
+/* Input plug shows actual rate. Output plug is needless for this purpose. */
+static int special_get_rate(struct snd_bebob *bebob, unsigned int *rate)
+{
+ int err, trials;
+
+ trials = 0;
+ do {
+ err = avc_general_get_sig_fmt(bebob->unit, rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+ } while (err == -EAGAIN && ++trials < 3);
+
+ return err;
+}
+static int special_set_rate(struct snd_bebob *bebob, unsigned int rate)
+{
+ struct special_params *params = bebob->maudio_special_quirk;
+ int err;
+
+ err = avc_general_set_sig_fmt(bebob->unit, rate,
+ AVC_GENERAL_PLUG_DIR_OUT, 0);
+ if (err < 0)
+ goto end;
+
+ /*
+ * Just after changing sampling rate for output, a followed command
+ * for input is easy to fail. This is a workaround fot this issue.
+ */
+ msleep(100);
+
+ err = avc_general_set_sig_fmt(bebob->unit, rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+ if (err < 0)
+ goto end;
+
+ if (params->ctl_id_sync)
+ snd_ctl_notify(bebob->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ params->ctl_id_sync);
+end:
+ return err;
+}
+
+/* Clock source control for special firmware */
+static const enum snd_bebob_clock_type special_clk_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL, /* With digital mute */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* SPDIF/ADAT */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+};
+static int special_clk_get(struct snd_bebob *bebob, unsigned int *id)
+{
+ struct special_params *params = bebob->maudio_special_quirk;
+ *id = params->clk_src;
+ return 0;
+}
+static int special_clk_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *einf)
+{
+ static const char *const special_clk_labels[] = {
+ "Internal with Digital Mute",
+ "Digital",
+ "Word Clock",
+ "Internal"
+ };
+ return snd_ctl_enum_info(einf, 1, ARRAY_SIZE(special_clk_types),
+ special_clk_labels);
+}
+static int special_clk_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ struct special_params *params = bebob->maudio_special_quirk;
+ uval->value.enumerated.item[0] = params->clk_src;
+ return 0;
+}
+static int special_clk_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ struct special_params *params = bebob->maudio_special_quirk;
+ int err, id;
+
+ id = uval->value.enumerated.item[0];
+ if (id >= ARRAY_SIZE(special_clk_types))
+ return -EINVAL;
+
+ guard(mutex)(&bebob->mutex);
+
+ err = avc_maudio_set_special_clk(bebob, id,
+ params->dig_in_fmt,
+ params->dig_out_fmt,
+ params->clk_lock);
+ if (err >= 0)
+ err = 1;
+
+ return err;
+}
+static const struct snd_kcontrol_new special_clk_ctl = {
+ .name = "Clock Source",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = special_clk_ctl_info,
+ .get = special_clk_ctl_get,
+ .put = special_clk_ctl_put
+};
+
+/* Clock synchronization control for special firmware */
+static int special_sync_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *einf)
+{
+ einf->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ einf->count = 1;
+ einf->value.integer.min = 0;
+ einf->value.integer.max = 1;
+
+ return 0;
+}
+static int special_sync_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ int err;
+ bool synced = 0;
+
+ err = check_clk_sync(bebob, METER_SIZE_SPECIAL, &synced);
+ if (err >= 0)
+ uval->value.integer.value[0] = synced;
+
+ return 0;
+}
+static const struct snd_kcontrol_new special_sync_ctl = {
+ .name = "Sync Status",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = special_sync_ctl_info,
+ .get = special_sync_ctl_get,
+};
+
+/* Digital input interface control for special firmware */
+static const char *const special_dig_in_iface_labels[] = {
+ "S/PDIF Optical", "S/PDIF Coaxial", "ADAT Optical"
+};
+static int special_dig_in_iface_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *einf)
+{
+ return snd_ctl_enum_info(einf, 1,
+ ARRAY_SIZE(special_dig_in_iface_labels),
+ special_dig_in_iface_labels);
+}
+static int special_dig_in_iface_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ struct special_params *params = bebob->maudio_special_quirk;
+ unsigned int dig_in_iface;
+ int err, val;
+
+ guard(mutex)(&bebob->mutex);
+
+ err = avc_audio_get_selector(bebob->unit, 0x00, 0x04,
+ &dig_in_iface);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get digital input interface: %d\n", err);
+ return err;
+ }
+
+ /* encoded id for user value */
+ val = (params->dig_in_fmt << 1) | (dig_in_iface & 0x01);
+
+ /* for ADAT Optical */
+ if (val > 2)
+ val = 2;
+
+ uval->value.enumerated.item[0] = val;
+ return 0;
+}
+static int special_dig_in_iface_ctl_set(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ struct special_params *params = bebob->maudio_special_quirk;
+ unsigned int id, dig_in_fmt, dig_in_iface;
+ int err;
+
+ id = uval->value.enumerated.item[0];
+ if (id >= ARRAY_SIZE(special_dig_in_iface_labels))
+ return -EINVAL;
+
+ /* decode user value */
+ dig_in_fmt = (id >> 1) & 0x01;
+ dig_in_iface = id & 0x01;
+
+ guard(mutex)(&bebob->mutex);
+
+ err = avc_maudio_set_special_clk(bebob,
+ params->clk_src,
+ dig_in_fmt,
+ params->dig_out_fmt,
+ params->clk_lock);
+ if (err < 0)
+ return err;
+
+ /* For ADAT, optical interface is only available. */
+ if (params->dig_in_fmt > 0)
+ return 1;
+
+ /* For S/PDIF, optical/coaxial interfaces are selectable. */
+ err = avc_audio_set_selector(bebob->unit, 0x00, 0x04, dig_in_iface);
+ if (err < 0)
+ dev_err(&bebob->unit->device,
+ "fail to set digital input interface: %d\n", err);
+ special_stream_formation_set(bebob);
+ return 1;
+}
+static const struct snd_kcontrol_new special_dig_in_iface_ctl = {
+ .name = "Digital Input Interface",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = special_dig_in_iface_ctl_info,
+ .get = special_dig_in_iface_ctl_get,
+ .put = special_dig_in_iface_ctl_set
+};
+
+/* Digital output interface control for special firmware */
+static const char *const special_dig_out_iface_labels[] = {
+ "S/PDIF Optical and Coaxial", "ADAT Optical"
+};
+static int special_dig_out_iface_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *einf)
+{
+ return snd_ctl_enum_info(einf, 1,
+ ARRAY_SIZE(special_dig_out_iface_labels),
+ special_dig_out_iface_labels);
+}
+static int special_dig_out_iface_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ struct special_params *params = bebob->maudio_special_quirk;
+
+ guard(mutex)(&bebob->mutex);
+ uval->value.enumerated.item[0] = params->dig_out_fmt;
+ return 0;
+}
+static int special_dig_out_iface_ctl_set(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ struct special_params *params = bebob->maudio_special_quirk;
+ unsigned int id;
+ int err;
+
+ id = uval->value.enumerated.item[0];
+ if (id >= ARRAY_SIZE(special_dig_out_iface_labels))
+ return -EINVAL;
+
+ guard(mutex)(&bebob->mutex);
+
+ err = avc_maudio_set_special_clk(bebob,
+ params->clk_src,
+ params->dig_in_fmt,
+ id, params->clk_lock);
+ if (err >= 0) {
+ special_stream_formation_set(bebob);
+ err = 1;
+ }
+
+ return err;
+}
+static const struct snd_kcontrol_new special_dig_out_iface_ctl = {
+ .name = "Digital Output Interface",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = special_dig_out_iface_ctl_info,
+ .get = special_dig_out_iface_ctl_get,
+ .put = special_dig_out_iface_ctl_set
+};
+
+static int add_special_controls(struct snd_bebob *bebob)
+{
+ struct snd_kcontrol *kctl;
+ struct special_params *params = bebob->maudio_special_quirk;
+ int err;
+
+ kctl = snd_ctl_new1(&special_clk_ctl, bebob);
+ err = snd_ctl_add(bebob->card, kctl);
+ if (err < 0)
+ goto end;
+
+ kctl = snd_ctl_new1(&special_sync_ctl, bebob);
+ err = snd_ctl_add(bebob->card, kctl);
+ if (err < 0)
+ goto end;
+ params->ctl_id_sync = &kctl->id;
+
+ kctl = snd_ctl_new1(&special_dig_in_iface_ctl, bebob);
+ err = snd_ctl_add(bebob->card, kctl);
+ if (err < 0)
+ goto end;
+
+ kctl = snd_ctl_new1(&special_dig_out_iface_ctl, bebob);
+ err = snd_ctl_add(bebob->card, kctl);
+end:
+ return err;
+}
+
+/* Hardware metering for special firmware */
+static const char *const special_meter_labels[] = {
+ ANA_IN, ANA_IN, ANA_IN, ANA_IN,
+ SPDIF_IN,
+ ADAT_IN, ADAT_IN, ADAT_IN, ADAT_IN,
+ ANA_OUT, ANA_OUT,
+ SPDIF_OUT,
+ ADAT_OUT, ADAT_OUT, ADAT_OUT, ADAT_OUT,
+ HP_OUT, HP_OUT,
+ AUX_OUT
+};
+static int
+special_meter_get(struct snd_bebob *bebob, u32 *target, unsigned int size)
+{
+ __be16 *buf;
+ unsigned int i, c, channels;
+ int err;
+
+ channels = ARRAY_SIZE(special_meter_labels) * 2;
+ if (size < channels * sizeof(u32))
+ return -EINVAL;
+
+ /* omit last 4 bytes because it's clock info. */
+ buf = kmalloc(METER_SIZE_SPECIAL - 4, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ err = get_meter(bebob, (void *)buf, METER_SIZE_SPECIAL - 4);
+ if (err < 0)
+ goto end;
+
+ /* Its format is u16 and some channels are unknown. */
+ i = 0;
+ for (c = 2; c < channels + 2; c++)
+ target[i++] = be16_to_cpu(buf[c]) << 16;
+end:
+ kfree(buf);
+ return err;
+}
+
+/* last 4 bytes are omitted because it's clock info. */
+static const char *const fw410_meter_labels[] = {
+ ANA_IN, DIG_IN,
+ ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, DIG_OUT,
+ HP_OUT
+};
+static const char *const audiophile_meter_labels[] = {
+ ANA_IN, DIG_IN,
+ ANA_OUT, ANA_OUT, DIG_OUT,
+ HP_OUT, AUX_OUT,
+};
+static const char *const solo_meter_labels[] = {
+ ANA_IN, DIG_IN,
+ STRM_IN, STRM_IN,
+ ANA_OUT, DIG_OUT
+};
+
+/* no clock info */
+static const char *const ozonic_meter_labels[] = {
+ ANA_IN, ANA_IN,
+ STRM_IN, STRM_IN,
+ ANA_OUT, ANA_OUT
+};
+/* TODO: need testers. these positions are based on authour's assumption */
+static const char *const nrv10_meter_labels[] = {
+ ANA_IN, ANA_IN, ANA_IN, ANA_IN,
+ DIG_IN,
+ ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT,
+ DIG_IN
+};
+static int
+normal_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
+{
+ const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+ unsigned int c, channels;
+ int err;
+
+ channels = spec->num * 2;
+ if (size < channels * sizeof(u32))
+ return -EINVAL;
+
+ err = get_meter(bebob, (void *)buf, size);
+ if (err < 0)
+ goto end;
+
+ for (c = 0; c < channels; c++)
+ be32_to_cpus(&buf[c]);
+
+ /* swap stream channels because inverted */
+ if (spec->labels == solo_meter_labels) {
+ swap(buf[4], buf[6]);
+ swap(buf[5], buf[7]);
+ }
+end:
+ return err;
+}
+
+/* for special customized devices */
+static const struct snd_bebob_rate_spec special_rate_spec = {
+ .get = &special_get_rate,
+ .set = &special_set_rate,
+};
+static const struct snd_bebob_clock_spec special_clk_spec = {
+ .num = ARRAY_SIZE(special_clk_types),
+ .types = special_clk_types,
+ .get = &special_clk_get,
+};
+static const struct snd_bebob_meter_spec special_meter_spec = {
+ .num = ARRAY_SIZE(special_meter_labels),
+ .labels = special_meter_labels,
+ .get = &special_meter_get
+};
+const struct snd_bebob_spec maudio_special_spec = {
+ .clock = &special_clk_spec,
+ .rate = &special_rate_spec,
+ .meter = &special_meter_spec
+};
+
+/* Firewire 410 specification */
+static const struct snd_bebob_rate_spec usual_rate_spec = {
+ .get = &snd_bebob_stream_get_rate,
+ .set = &snd_bebob_stream_set_rate,
+};
+static const struct snd_bebob_meter_spec fw410_meter_spec = {
+ .num = ARRAY_SIZE(fw410_meter_labels),
+ .labels = fw410_meter_labels,
+ .get = &normal_meter_get
+};
+const struct snd_bebob_spec maudio_fw410_spec = {
+ .clock = NULL,
+ .rate = &usual_rate_spec,
+ .meter = &fw410_meter_spec
+};
+
+/* Firewire Audiophile specification */
+static const struct snd_bebob_meter_spec audiophile_meter_spec = {
+ .num = ARRAY_SIZE(audiophile_meter_labels),
+ .labels = audiophile_meter_labels,
+ .get = &normal_meter_get
+};
+const struct snd_bebob_spec maudio_audiophile_spec = {
+ .clock = NULL,
+ .rate = &usual_rate_spec,
+ .meter = &audiophile_meter_spec
+};
+
+/* Firewire Solo specification */
+static const struct snd_bebob_meter_spec solo_meter_spec = {
+ .num = ARRAY_SIZE(solo_meter_labels),
+ .labels = solo_meter_labels,
+ .get = &normal_meter_get
+};
+const struct snd_bebob_spec maudio_solo_spec = {
+ .clock = NULL,
+ .rate = &usual_rate_spec,
+ .meter = &solo_meter_spec
+};
+
+/* Ozonic specification */
+static const struct snd_bebob_meter_spec ozonic_meter_spec = {
+ .num = ARRAY_SIZE(ozonic_meter_labels),
+ .labels = ozonic_meter_labels,
+ .get = &normal_meter_get
+};
+const struct snd_bebob_spec maudio_ozonic_spec = {
+ .clock = NULL,
+ .rate = &usual_rate_spec,
+ .meter = &ozonic_meter_spec
+};
+
+/* NRV10 specification */
+static const struct snd_bebob_meter_spec nrv10_meter_spec = {
+ .num = ARRAY_SIZE(nrv10_meter_labels),
+ .labels = nrv10_meter_labels,
+ .get = &normal_meter_get
+};
+const struct snd_bebob_spec maudio_nrv10_spec = {
+ .clock = NULL,
+ .rate = &usual_rate_spec,
+ .meter = &nrv10_meter_spec
+};
diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c
new file mode 100644
index 000000000000..678631f31d3c
--- /dev/null
+++ b/sound/firewire/bebob/bebob_midi.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_midi.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "bebob.h"
+
+static int midi_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_bebob *bebob = substream->rmidi->private_data;
+ int err;
+
+ err = snd_bebob_stream_lock_try(bebob);
+ if (err < 0)
+ return err;
+
+ scoped_guard(mutex, &bebob->mutex) {
+ err = snd_bebob_stream_reserve_duplex(bebob, 0, 0, 0);
+ if (err >= 0) {
+ ++bebob->substreams_counter;
+ err = snd_bebob_stream_start_duplex(bebob);
+ if (err < 0)
+ --bebob->substreams_counter;
+ }
+ }
+ if (err < 0)
+ snd_bebob_stream_lock_release(bebob);
+
+ return err;
+}
+
+static int midi_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_bebob *bebob = substream->rmidi->private_data;
+
+ scoped_guard(mutex, &bebob->mutex) {
+ bebob->substreams_counter--;
+ snd_bebob_stream_stop_duplex(bebob);
+ }
+
+ snd_bebob_stream_lock_release(bebob);
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_bebob *bebob = substrm->rmidi->private_data;
+
+ guard(spinlock_irqsave)(&bebob->lock);
+
+ if (up)
+ amdtp_am824_midi_trigger(&bebob->tx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_am824_midi_trigger(&bebob->tx_stream,
+ substrm->number, NULL);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_bebob *bebob = substrm->rmidi->private_data;
+
+ guard(spinlock_irqsave)(&bebob->lock);
+
+ if (up)
+ amdtp_am824_midi_trigger(&bebob->rx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_am824_midi_trigger(&bebob->rx_stream,
+ substrm->number, NULL);
+}
+
+static void set_midi_substream_names(struct snd_bebob *bebob,
+ struct snd_rawmidi_str *str)
+{
+ struct snd_rawmidi_substream *subs;
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ scnprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ bebob->card->shortname, subs->number + 1);
+ }
+}
+
+int snd_bebob_create_midi_devices(struct snd_bebob *bebob)
+{
+ static const struct snd_rawmidi_ops capture_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_capture_trigger,
+ };
+ static const struct snd_rawmidi_ops playback_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_playback_trigger,
+ };
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *str;
+ int err;
+
+ /* create midi ports */
+ err = snd_rawmidi_new(bebob->card, bebob->card->driver, 0,
+ bebob->midi_output_ports, bebob->midi_input_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", bebob->card->shortname);
+ rmidi->private_data = bebob;
+
+ if (bebob->midi_input_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &capture_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+ set_midi_substream_names(bebob, str);
+ }
+
+ if (bebob->midi_output_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &playback_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+ set_midi_substream_names(bebob, str);
+ }
+
+ if ((bebob->midi_output_ports > 0) && (bebob->midi_input_ports > 0))
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c
new file mode 100644
index 000000000000..692d33bac2d2
--- /dev/null
+++ b/sound/firewire/bebob/bebob_pcm.c
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_pcm.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./bebob.h"
+
+static int
+hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+ struct snd_bebob_stream_formation *formations = rule->private;
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i;
+
+ for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+ /* entry is invalid */
+ if (formations[i].pcm == 0)
+ continue;
+
+ if (!snd_interval_test(c, formations[i].pcm))
+ continue;
+
+ t.min = min(t.min, snd_bebob_rate_table[i]);
+ t.max = max(t.max, snd_bebob_rate_table[i]);
+
+ }
+ return snd_interval_refine(r, &t);
+}
+
+static int
+hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+ struct snd_bebob_stream_formation *formations = rule->private;
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+
+ unsigned int i;
+
+ for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+ /* entry is invalid */
+ if (formations[i].pcm == 0)
+ continue;
+
+ if (!snd_interval_test(r, snd_bebob_rate_table[i]))
+ continue;
+
+ t.min = min(t.min, formations[i].pcm);
+ t.max = max(t.max, formations[i].pcm);
+ }
+
+ return snd_interval_refine(c, &t);
+}
+
+static void
+limit_channels_and_rates(struct snd_pcm_hardware *hw,
+ struct snd_bebob_stream_formation *formations)
+{
+ unsigned int i;
+
+ hw->channels_min = UINT_MAX;
+ hw->channels_max = 0;
+
+ hw->rate_min = UINT_MAX;
+ hw->rate_max = 0;
+ hw->rates = 0;
+
+ for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+ /* entry has no PCM channels */
+ if (formations[i].pcm == 0)
+ continue;
+
+ hw->channels_min = min(hw->channels_min, formations[i].pcm);
+ hw->channels_max = max(hw->channels_max, formations[i].pcm);
+
+ hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]);
+ hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]);
+ hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]);
+ }
+}
+
+static int
+pcm_init_hw_params(struct snd_bebob *bebob,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct amdtp_stream *s;
+ struct snd_bebob_stream_formation *formations;
+ int err;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
+ s = &bebob->tx_stream;
+ formations = bebob->tx_stream_formations;
+ } else {
+ runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
+ s = &bebob->rx_stream;
+ formations = bebob->rx_stream_formations;
+ }
+
+ limit_channels_and_rates(&runtime->hw, formations);
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels, formations,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ goto end;
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_rate, formations,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ goto end;
+
+ err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
+end:
+ return err;
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+ const struct snd_bebob_rate_spec *spec = bebob->spec->rate;
+ struct amdtp_domain *d = &bebob->domain;
+ enum snd_bebob_clock_type src;
+ int err;
+
+ err = snd_bebob_stream_lock_try(bebob);
+ if (err < 0)
+ return err;
+
+ err = pcm_init_hw_params(bebob, substream);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_bebob_stream_get_clock_src(bebob, &src);
+ if (err < 0)
+ goto err_locked;
+
+ scoped_guard(mutex, &bebob->mutex) {
+ // When source of clock is not internal or any stream is reserved for
+ // transmission of PCM frames, the available sampling rate is limited
+ // at current one.
+ if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL ||
+ (bebob->substreams_counter > 0 && d->events_per_period > 0)) {
+ unsigned int frames_per_period = d->events_per_period;
+ unsigned int frames_per_buffer = d->events_per_buffer;
+ unsigned int sampling_rate;
+
+ err = spec->get(bebob, &sampling_rate);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get sampling rate: %d\n", err);
+ goto err_locked;
+ }
+
+ substream->runtime->hw.rate_min = sampling_rate;
+ substream->runtime->hw.rate_max = sampling_rate;
+
+ if (frames_per_period > 0) {
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ frames_per_period, frames_per_period);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ frames_per_buffer, frames_per_buffer);
+ if (err < 0)
+ goto err_locked;
+ }
+ }
+ }
+
+ snd_pcm_set_sync(substream);
+
+ return 0;
+err_locked:
+ snd_bebob_stream_lock_release(bebob);
+ return err;
+}
+
+static int
+pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+ snd_bebob_stream_lock_release(bebob);
+ return 0;
+}
+
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_bebob *bebob = substream->private_data;
+ int err = 0;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ unsigned int rate = params_rate(hw_params);
+ unsigned int frames_per_period = params_period_size(hw_params);
+ unsigned int frames_per_buffer = params_buffer_size(hw_params);
+
+ guard(mutex)(&bebob->mutex);
+ err = snd_bebob_stream_reserve_duplex(bebob, rate,
+ frames_per_period, frames_per_buffer);
+ if (err >= 0)
+ ++bebob->substreams_counter;
+ }
+
+ return err;
+}
+
+static int pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+
+ guard(mutex)(&bebob->mutex);
+
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ bebob->substreams_counter--;
+
+ snd_bebob_stream_stop_duplex(bebob);
+
+ return 0;
+}
+
+static int
+pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+ int err;
+
+ err = snd_bebob_stream_start_duplex(bebob);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&bebob->tx_stream);
+
+ return err;
+}
+static int
+pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+ int err;
+
+ err = snd_bebob_stream_start_duplex(bebob);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&bebob->rx_stream);
+
+ return err;
+}
+
+static int
+pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_bebob *bebob = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&bebob->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+static int
+pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_bebob *bebob = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&bebob->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_bebob *bebob = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&bebob->domain,
+ &bebob->tx_stream);
+}
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_bebob *bebob = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&bebob->domain,
+ &bebob->rx_stream);
+}
+
+static int pcm_capture_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->tx_stream);
+}
+
+static int pcm_playback_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->rx_stream);
+}
+
+int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
+{
+ static const struct snd_pcm_ops capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .ack = pcm_capture_ack,
+ };
+ static const struct snd_pcm_ops playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .ack = pcm_playback_ack,
+ };
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ goto end;
+
+ pcm->private_data = bebob;
+ pcm->nonatomic = true;
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s PCM", bebob->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
+end:
+ return err;
+}
diff --git a/sound/firewire/bebob/bebob_proc.c b/sound/firewire/bebob/bebob_proc.c
new file mode 100644
index 000000000000..f659b888a6d1
--- /dev/null
+++ b/sound/firewire/bebob/bebob_proc.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_proc.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./bebob.h"
+
+/* contents of information register */
+struct hw_info {
+ u64 manufacturer;
+ u32 protocol_ver;
+ u32 bld_ver;
+ u32 guid[2];
+ u32 model_id;
+ u32 model_rev;
+ u64 fw_date;
+ u64 fw_time;
+ u32 fw_id;
+ u32 fw_ver;
+ u32 base_addr;
+ u32 max_size;
+ u64 bld_date;
+ u64 bld_time;
+/* may not used in product
+ u64 dbg_date;
+ u64 dbg_time;
+ u32 dbg_id;
+ u32 dbg_version;
+*/
+} __packed;
+
+static void
+proc_read_hw_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_bebob *bebob = entry->private_data;
+ struct hw_info *info;
+
+ info = kzalloc(sizeof(struct hw_info), GFP_KERNEL);
+ if (info == NULL)
+ return;
+
+ if (snd_bebob_read_block(bebob->unit, 0,
+ info, sizeof(struct hw_info)) < 0)
+ goto end;
+
+ snd_iprintf(buffer, "Manufacturer:\t%.8s\n",
+ (char *)&info->manufacturer);
+ snd_iprintf(buffer, "Protocol Ver:\t%d\n", info->protocol_ver);
+ snd_iprintf(buffer, "Build Ver:\t%d\n", info->bld_ver);
+ snd_iprintf(buffer, "GUID:\t\t0x%.8X%.8X\n",
+ info->guid[0], info->guid[1]);
+ snd_iprintf(buffer, "Model ID:\t0x%02X\n", info->model_id);
+ snd_iprintf(buffer, "Model Rev:\t%d\n", info->model_rev);
+ snd_iprintf(buffer, "Firmware Date:\t%.8s\n", (char *)&info->fw_date);
+ snd_iprintf(buffer, "Firmware Time:\t%.8s\n", (char *)&info->fw_time);
+ snd_iprintf(buffer, "Firmware ID:\t0x%X\n", info->fw_id);
+ snd_iprintf(buffer, "Firmware Ver:\t%d\n", info->fw_ver);
+ snd_iprintf(buffer, "Base Addr:\t0x%X\n", info->base_addr);
+ snd_iprintf(buffer, "Max Size:\t%d\n", info->max_size);
+ snd_iprintf(buffer, "Loader Date:\t%.8s\n", (char *)&info->bld_date);
+ snd_iprintf(buffer, "Loader Time:\t%.8s\n", (char *)&info->bld_time);
+
+end:
+ kfree(info);
+}
+
+static void
+proc_read_meters(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_bebob *bebob = entry->private_data;
+ const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+ u32 *buf;
+ unsigned int i, c, channels, size;
+
+ if (spec == NULL)
+ return;
+
+ channels = spec->num * 2;
+ size = channels * sizeof(u32);
+ buf = kmalloc(size, GFP_KERNEL);
+ if (buf == NULL)
+ return;
+
+ if (spec->get(bebob, buf, size) < 0)
+ goto end;
+
+ for (i = 0, c = 1; i < channels; i++) {
+ snd_iprintf(buffer, "%s %d:\t%d\n",
+ spec->labels[i / 2], c++, buf[i]);
+ if ((i + 1 < channels - 1) &&
+ (strcmp(spec->labels[i / 2],
+ spec->labels[(i + 1) / 2]) != 0))
+ c = 1;
+ }
+end:
+ kfree(buf);
+}
+
+static void
+proc_read_formation(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_bebob *bebob = entry->private_data;
+ struct snd_bebob_stream_formation *formation;
+ unsigned int i;
+
+ snd_iprintf(buffer, "Output Stream from device:\n");
+ snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
+ formation = bebob->tx_stream_formations;
+ for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+ snd_iprintf(buffer,
+ "\t%d\t%d\t%d\n", snd_bebob_rate_table[i],
+ formation[i].pcm, formation[i].midi);
+ }
+
+ snd_iprintf(buffer, "Input Stream to device:\n");
+ snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
+ formation = bebob->rx_stream_formations;
+ for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+ snd_iprintf(buffer,
+ "\t%d\t%d\t%d\n", snd_bebob_rate_table[i],
+ formation[i].pcm, formation[i].midi);
+ }
+}
+
+static void
+proc_read_clock(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ static const char *const clk_labels[] = {
+ "Internal",
+ "External",
+ "SYT-Match",
+ };
+ struct snd_bebob *bebob = entry->private_data;
+ const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
+ const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+ enum snd_bebob_clock_type src;
+ unsigned int rate;
+
+ if (rate_spec->get(bebob, &rate) >= 0)
+ snd_iprintf(buffer, "Sampling rate: %d\n", rate);
+
+ if (snd_bebob_stream_get_clock_src(bebob, &src) >= 0) {
+ if (clk_spec)
+ snd_iprintf(buffer, "Clock Source: %s\n",
+ clk_labels[src]);
+ else
+ snd_iprintf(buffer, "Clock Source: %s (MSU-dest: %d)\n",
+ clk_labels[src], bebob->sync_input_plug);
+ }
+}
+
+static void
+add_node(struct snd_bebob *bebob, struct snd_info_entry *root, const char *name,
+ void (*op)(struct snd_info_entry *e, struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(bebob->card, name, root);
+ if (entry)
+ snd_info_set_text_ops(entry, bebob, op);
+}
+
+void snd_bebob_proc_init(struct snd_bebob *bebob)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(bebob->card, "firewire",
+ bebob->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | 0555;
+
+ add_node(bebob, root, "clock", proc_read_clock);
+ add_node(bebob, root, "firmware", proc_read_hw_info);
+ add_node(bebob, root, "formation", proc_read_formation);
+
+ if (bebob->spec->meter != NULL)
+ add_node(bebob, root, "meter", proc_read_meters);
+}
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
new file mode 100644
index 000000000000..449cb17717f0
--- /dev/null
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -0,0 +1,987 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_stream.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./bebob.h"
+
+#define READY_TIMEOUT_MS 4000
+
+/*
+ * NOTE;
+ * For BeBoB streams, Both of input and output CMP connection are important.
+ *
+ * For most devices, each CMP connection starts to transmit/receive a
+ * corresponding stream. But for a few devices, both of CMP connection needs
+ * to start transmitting stream. An example is 'M-Audio Firewire 410'.
+ */
+
+/* 128 is an arbitrary length but it seems to be enough */
+#define FORMAT_MAXIMUM_LENGTH 128
+
+const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES] = {
+ [0] = 32000,
+ [1] = 44100,
+ [2] = 48000,
+ [3] = 88200,
+ [4] = 96000,
+ [5] = 176400,
+ [6] = 192000,
+};
+
+/*
+ * See: Table 51: Extended Stream Format Info ‘Sampling Frequency’
+ * in Additional AVC commands (Nov 2003, BridgeCo)
+ */
+static const unsigned int bridgeco_freq_table[] = {
+ [0] = 0x02,
+ [1] = 0x03,
+ [2] = 0x04,
+ [3] = 0x0a,
+ [4] = 0x05,
+ [5] = 0x06,
+ [6] = 0x07,
+};
+
+static int
+get_formation_index(unsigned int rate, unsigned int *index)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(snd_bebob_rate_table); i++) {
+ if (snd_bebob_rate_table[i] == rate) {
+ *index = i;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+int
+snd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *curr_rate)
+{
+ unsigned int tx_rate, rx_rate, trials;
+ int err;
+
+ trials = 0;
+ do {
+ err = avc_general_get_sig_fmt(bebob->unit, &tx_rate,
+ AVC_GENERAL_PLUG_DIR_OUT, 0);
+ } while (err == -EAGAIN && ++trials < 3);
+ if (err < 0)
+ goto end;
+
+ trials = 0;
+ do {
+ err = avc_general_get_sig_fmt(bebob->unit, &rx_rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+ } while (err == -EAGAIN && ++trials < 3);
+ if (err < 0)
+ goto end;
+
+ *curr_rate = rx_rate;
+ if (rx_rate == tx_rate)
+ goto end;
+
+ /* synchronize receive stream rate to transmit stream rate */
+ err = avc_general_set_sig_fmt(bebob->unit, rx_rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+end:
+ return err;
+}
+
+int
+snd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate)
+{
+ int err;
+
+ err = avc_general_set_sig_fmt(bebob->unit, rate,
+ AVC_GENERAL_PLUG_DIR_OUT, 0);
+ if (err < 0)
+ goto end;
+
+ err = avc_general_set_sig_fmt(bebob->unit, rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+ if (err < 0)
+ goto end;
+
+ /*
+ * Some devices need a bit time for transition.
+ * 300msec is got by some experiments.
+ */
+ msleep(300);
+end:
+ return err;
+}
+
+int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
+ enum snd_bebob_clock_type *src)
+{
+ const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7];
+ unsigned int id;
+ enum avc_bridgeco_plug_type type;
+ int err = 0;
+
+ /* 1.The device has its own operation to switch source of clock */
+ if (clk_spec) {
+ err = clk_spec->get(bebob, &id);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get clock source: %d\n", err);
+ goto end;
+ }
+
+ if (id >= clk_spec->num) {
+ dev_err(&bebob->unit->device,
+ "clock source %d out of range 0..%d\n",
+ id, clk_spec->num - 1);
+ err = -EIO;
+ goto end;
+ }
+
+ *src = clk_spec->types[id];
+ goto end;
+ }
+
+ /*
+ * 2.The device don't support to switch source of clock then assumed
+ * to use internal clock always
+ */
+ if (bebob->sync_input_plug < 0) {
+ *src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
+ goto end;
+ }
+
+ /*
+ * 3.The device supports to switch source of clock by an usual way.
+ * Let's check input for 'Music Sub Unit Sync Input' plug.
+ */
+ avc_bridgeco_fill_msu_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN,
+ bebob->sync_input_plug);
+ err = avc_bridgeco_get_plug_input(bebob->unit, addr, input);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get an input for MSU in plug %d: %d\n",
+ bebob->sync_input_plug, err);
+ goto end;
+ }
+
+ /*
+ * If there are no input plugs, all of fields are 0xff.
+ * Here check the first field. This field is used for direction.
+ */
+ if (input[0] == 0xff) {
+ *src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
+ goto end;
+ }
+
+ /* The source from any output plugs is for one purpose only. */
+ if (input[0] == AVC_BRIDGECO_PLUG_DIR_OUT) {
+ /*
+ * In BeBoB architecture, the source from music subunit may
+ * bypass from oPCR[0]. This means that this source gives
+ * synchronization to IEEE 1394 cycle start packet.
+ */
+ if (input[1] == AVC_BRIDGECO_PLUG_MODE_SUBUNIT &&
+ input[2] == 0x0c) {
+ *src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
+ goto end;
+ }
+ /* The source from any input units is for several purposes. */
+ } else if (input[1] == AVC_BRIDGECO_PLUG_MODE_UNIT) {
+ if (input[2] == AVC_BRIDGECO_PLUG_UNIT_ISOC) {
+ if (input[3] == 0x00) {
+ /*
+ * This source comes from iPCR[0]. This means
+ * that presentation timestamp calculated by
+ * SYT series of the received packets. In
+ * short, this driver is the master of
+ * synchronization.
+ */
+ *src = SND_BEBOB_CLOCK_TYPE_SYT;
+ goto end;
+ } else {
+ /*
+ * This source comes from iPCR[1-29]. This
+ * means that the synchronization stream is not
+ * the Audio/MIDI compound stream.
+ */
+ *src = SND_BEBOB_CLOCK_TYPE_EXTERNAL;
+ goto end;
+ }
+ } else if (input[2] == AVC_BRIDGECO_PLUG_UNIT_EXT) {
+ /* Check type of this plug. */
+ avc_bridgeco_fill_unit_addr(addr,
+ AVC_BRIDGECO_PLUG_DIR_IN,
+ AVC_BRIDGECO_PLUG_UNIT_EXT,
+ input[3]);
+ err = avc_bridgeco_get_plug_type(bebob->unit, addr,
+ &type);
+ if (err < 0)
+ goto end;
+
+ if (type == AVC_BRIDGECO_PLUG_TYPE_DIG) {
+ /*
+ * SPDIF/ADAT or sometimes (not always) word
+ * clock.
+ */
+ *src = SND_BEBOB_CLOCK_TYPE_EXTERNAL;
+ goto end;
+ } else if (type == AVC_BRIDGECO_PLUG_TYPE_SYNC) {
+ /* Often word clock. */
+ *src = SND_BEBOB_CLOCK_TYPE_EXTERNAL;
+ goto end;
+ } else if (type == AVC_BRIDGECO_PLUG_TYPE_ADDITION) {
+ /*
+ * Not standard.
+ * Mostly, additional internal clock.
+ */
+ *src = SND_BEBOB_CLOCK_TYPE_INTERNAL;
+ goto end;
+ }
+ }
+ }
+
+ /* Not supported. */
+ err = -EIO;
+end:
+ return err;
+}
+
+static int map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
+{
+ unsigned int sec, sections, ch, channels;
+ unsigned int pcm, midi, location;
+ unsigned int stm_pos, sec_loc, pos;
+ u8 *buf, addr[AVC_BRIDGECO_ADDR_BYTES], type;
+ enum avc_bridgeco_plug_dir dir;
+ int err;
+
+ /*
+ * The length of return value of this command cannot be expected. Here
+ * use the maximum length of FCP.
+ */
+ buf = kzalloc(256, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ if (s == &bebob->tx_stream)
+ dir = AVC_BRIDGECO_PLUG_DIR_OUT;
+ else
+ dir = AVC_BRIDGECO_PLUG_DIR_IN;
+
+ avc_bridgeco_fill_unit_addr(addr, dir, AVC_BRIDGECO_PLUG_UNIT_ISOC, 0);
+ err = avc_bridgeco_get_plug_ch_pos(bebob->unit, addr, buf, 256);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get channel position for isoc %s plug 0: %d\n",
+ (dir == AVC_BRIDGECO_PLUG_DIR_IN) ? "in" : "out",
+ err);
+ goto end;
+ }
+ pos = 0;
+
+ /* positions in I/O buffer */
+ pcm = 0;
+ midi = 0;
+
+ /* the number of sections in AMDTP packet */
+ sections = buf[pos++];
+
+ for (sec = 0; sec < sections; sec++) {
+ /* type of this section */
+ avc_bridgeco_fill_unit_addr(addr, dir,
+ AVC_BRIDGECO_PLUG_UNIT_ISOC, 0);
+ err = avc_bridgeco_get_plug_section_type(bebob->unit, addr,
+ sec, &type);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get section type for isoc %s plug 0: %d\n",
+ (dir == AVC_BRIDGECO_PLUG_DIR_IN) ? "in" :
+ "out",
+ err);
+ goto end;
+ }
+ /* NoType */
+ if (type == 0xff) {
+ err = -ENOSYS;
+ goto end;
+ }
+
+ /* the number of channels in this section */
+ channels = buf[pos++];
+
+ for (ch = 0; ch < channels; ch++) {
+ /* position of this channel in AMDTP packet */
+ stm_pos = buf[pos++] - 1;
+ /* location of this channel in this section */
+ sec_loc = buf[pos++] - 1;
+
+ /*
+ * Basically the number of location is within the
+ * number of channels in this section. But some models
+ * of M-Audio don't follow this. Its location for MIDI
+ * is the position of MIDI channels in AMDTP packet.
+ */
+ if (sec_loc >= channels)
+ sec_loc = ch;
+
+ switch (type) {
+ /* for MIDI conformant data channel */
+ case 0x0a:
+ /* AMDTP_MAX_CHANNELS_FOR_MIDI is 1. */
+ if ((midi > 0) && (stm_pos != midi)) {
+ err = -ENOSYS;
+ goto end;
+ }
+ amdtp_am824_set_midi_position(s, stm_pos);
+ midi = stm_pos;
+ break;
+ /* for PCM data channel */
+ case 0x01: /* Headphone */
+ case 0x02: /* Microphone */
+ case 0x03: /* Line */
+ case 0x04: /* SPDIF */
+ case 0x05: /* ADAT */
+ case 0x06: /* TDIF */
+ case 0x07: /* MADI */
+ /* for undefined/changeable signal */
+ case 0x08: /* Analog */
+ case 0x09: /* Digital */
+ default:
+ location = pcm + sec_loc;
+ if (location >= AM824_MAX_CHANNELS_FOR_PCM) {
+ err = -ENOSYS;
+ goto end;
+ }
+ amdtp_am824_set_pcm_position(s, location,
+ stm_pos);
+ break;
+ }
+ }
+
+ if (type != 0x0a)
+ pcm += channels;
+ else
+ midi += channels;
+ }
+end:
+ kfree(buf);
+ return err;
+}
+
+static int
+check_connection_used_by_others(struct snd_bebob *bebob, struct amdtp_stream *s)
+{
+ struct cmp_connection *conn;
+ bool used;
+ int err;
+
+ if (s == &bebob->tx_stream)
+ conn = &bebob->out_conn;
+ else
+ conn = &bebob->in_conn;
+
+ err = cmp_connection_check_used(conn, &used);
+ if ((err >= 0) && used && !amdtp_stream_running(s)) {
+ dev_err(&bebob->unit->device,
+ "Connection established by others: %cPCR[%d]\n",
+ (conn->direction == CMP_OUTPUT) ? 'o' : 'i',
+ conn->pcr_index);
+ err = -EBUSY;
+ }
+
+ return err;
+}
+
+static void break_both_connections(struct snd_bebob *bebob)
+{
+ cmp_connection_break(&bebob->in_conn);
+ cmp_connection_break(&bebob->out_conn);
+}
+
+static int start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
+{
+ struct cmp_connection *conn;
+ int err = 0;
+
+ if (stream == &bebob->rx_stream)
+ conn = &bebob->in_conn;
+ else
+ conn = &bebob->out_conn;
+
+ // channel mapping.
+ if (bebob->maudio_special_quirk == NULL) {
+ err = map_data_channels(bebob, stream);
+ if (err < 0)
+ return err;
+ }
+
+ err = cmp_connection_establish(conn);
+ if (err < 0)
+ return err;
+
+ return amdtp_domain_add_stream(&bebob->domain, stream,
+ conn->resources.channel, conn->speed);
+}
+
+static int init_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
+{
+ unsigned int flags = CIP_BLOCKING;
+ enum amdtp_stream_direction dir_stream;
+ struct cmp_connection *conn;
+ enum cmp_direction dir_conn;
+ int err;
+
+ if (stream == &bebob->tx_stream) {
+ dir_stream = AMDTP_IN_STREAM;
+ conn = &bebob->out_conn;
+ dir_conn = CMP_OUTPUT;
+ } else {
+ dir_stream = AMDTP_OUT_STREAM;
+ conn = &bebob->in_conn;
+ dir_conn = CMP_INPUT;
+ }
+
+ if (stream == &bebob->tx_stream) {
+ if (bebob->quirks & SND_BEBOB_QUIRK_WRONG_DBC)
+ flags |= CIP_EMPTY_HAS_WRONG_DBC;
+ }
+
+ err = cmp_connection_init(conn, bebob->unit, dir_conn, 0);
+ if (err < 0)
+ return err;
+
+ err = amdtp_am824_init(stream, bebob->unit, dir_stream, flags);
+ if (err < 0) {
+ cmp_connection_destroy(conn);
+ return err;
+ }
+
+ return 0;
+}
+
+static void destroy_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
+{
+ amdtp_stream_destroy(stream);
+
+ if (stream == &bebob->tx_stream)
+ cmp_connection_destroy(&bebob->out_conn);
+ else
+ cmp_connection_destroy(&bebob->in_conn);
+}
+
+int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
+{
+ int err;
+
+ err = init_stream(bebob, &bebob->tx_stream);
+ if (err < 0)
+ return err;
+
+ err = init_stream(bebob, &bebob->rx_stream);
+ if (err < 0) {
+ destroy_stream(bebob, &bebob->tx_stream);
+ return err;
+ }
+
+ err = amdtp_domain_init(&bebob->domain);
+ if (err < 0) {
+ destroy_stream(bebob, &bebob->tx_stream);
+ destroy_stream(bebob, &bebob->rx_stream);
+ }
+
+ return err;
+}
+
+static int keep_resources(struct snd_bebob *bebob, struct amdtp_stream *stream,
+ unsigned int rate, unsigned int index)
+{
+ unsigned int pcm_channels;
+ unsigned int midi_ports;
+ struct cmp_connection *conn;
+ int err;
+
+ if (stream == &bebob->tx_stream) {
+ pcm_channels = bebob->tx_stream_formations[index].pcm;
+ midi_ports = bebob->midi_input_ports;
+ conn = &bebob->out_conn;
+ } else {
+ pcm_channels = bebob->rx_stream_formations[index].pcm;
+ midi_ports = bebob->midi_output_ports;
+ conn = &bebob->in_conn;
+ }
+
+ err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports, false);
+ if (err < 0)
+ return err;
+
+ return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
+}
+
+int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer)
+{
+ unsigned int curr_rate;
+ int err;
+
+ // Considering JACK/FFADO streaming:
+ // TODO: This can be removed hwdep functionality becomes popular.
+ err = check_connection_used_by_others(bebob, &bebob->rx_stream);
+ if (err < 0)
+ return err;
+
+ err = bebob->spec->rate->get(bebob, &curr_rate);
+ if (err < 0)
+ return err;
+ if (rate == 0)
+ rate = curr_rate;
+ if (curr_rate != rate) {
+ amdtp_domain_stop(&bebob->domain);
+ break_both_connections(bebob);
+
+ cmp_connection_release(&bebob->out_conn);
+ cmp_connection_release(&bebob->in_conn);
+ }
+
+ if (bebob->substreams_counter == 0 || curr_rate != rate) {
+ unsigned int index;
+
+ // NOTE:
+ // If establishing connections at first, Yamaha GO46
+ // (and maybe Terratec X24) don't generate sound.
+ //
+ // For firmware customized by M-Audio, refer to next NOTE.
+ err = bebob->spec->rate->set(bebob, rate);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to set sampling rate: %d\n",
+ err);
+ return err;
+ }
+
+ err = get_formation_index(rate, &index);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(bebob, &bebob->tx_stream, rate, index);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(bebob, &bebob->rx_stream, rate, index);
+ if (err < 0) {
+ cmp_connection_release(&bebob->out_conn);
+ return err;
+ }
+
+ err = amdtp_domain_set_events_per_period(&bebob->domain,
+ frames_per_period, frames_per_buffer);
+ if (err < 0) {
+ cmp_connection_release(&bebob->out_conn);
+ cmp_connection_release(&bebob->in_conn);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
+{
+ int err;
+
+ // Need no substreams.
+ if (bebob->substreams_counter == 0)
+ return -EIO;
+
+ // packet queueing error or detecting discontinuity
+ if (amdtp_streaming_error(&bebob->rx_stream) ||
+ amdtp_streaming_error(&bebob->tx_stream)) {
+ amdtp_domain_stop(&bebob->domain);
+ break_both_connections(bebob);
+ }
+
+ if (!amdtp_stream_running(&bebob->rx_stream)) {
+ enum snd_bebob_clock_type src;
+ unsigned int curr_rate;
+ unsigned int tx_init_skip_cycles;
+
+ if (bebob->maudio_special_quirk) {
+ err = bebob->spec->rate->get(bebob, &curr_rate);
+ if (err < 0)
+ return err;
+ }
+
+ err = snd_bebob_stream_get_clock_src(bebob, &src);
+ if (err < 0)
+ return err;
+
+ err = start_stream(bebob, &bebob->rx_stream);
+ if (err < 0)
+ goto error;
+
+ err = start_stream(bebob, &bebob->tx_stream);
+ if (err < 0)
+ goto error;
+
+ if (!(bebob->quirks & SND_BEBOB_QUIRK_INITIAL_DISCONTINUOUS_DBC))
+ tx_init_skip_cycles = 0;
+ else
+ tx_init_skip_cycles = 16000;
+
+ // MEMO: Some devices start packet transmission long enough after establishment of
+ // CMP connection. In the early stage of packet streaming, any device transfers
+ // NODATA packets. After several hundred cycles, it begins to multiplex event into
+ // the packet with adequate value of syt field in CIP header. Some devices are
+ // strictly to generate any discontinuity in the sequence of tx packet when they
+ // receives inadequate sequence of value in syt field of CIP header. In the case,
+ // the request to break CMP connection is often corrupted, then any transaction
+ // results in unrecoverable error, sometimes generate bus-reset.
+ err = amdtp_domain_start(&bebob->domain, tx_init_skip_cycles, true, false);
+ if (err < 0)
+ goto error;
+
+ // NOTE:
+ // The firmware customized by M-Audio uses these commands to
+ // start transmitting stream. This is not usual way.
+ if (bebob->maudio_special_quirk) {
+ err = bebob->spec->rate->set(bebob, curr_rate);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to ensure sampling rate: %d\n",
+ err);
+ goto error;
+ }
+ }
+
+ // Some devices postpone start of transmission mostly for 1 sec after receives
+ // packets firstly.
+ if (!amdtp_domain_wait_ready(&bebob->domain, READY_TIMEOUT_MS)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ amdtp_domain_stop(&bebob->domain);
+ break_both_connections(bebob);
+ return err;
+}
+
+void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
+{
+ if (bebob->substreams_counter == 0) {
+ amdtp_domain_stop(&bebob->domain);
+ break_both_connections(bebob);
+
+ cmp_connection_release(&bebob->out_conn);
+ cmp_connection_release(&bebob->in_conn);
+ }
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob)
+{
+ amdtp_domain_destroy(&bebob->domain);
+
+ destroy_stream(bebob, &bebob->tx_stream);
+ destroy_stream(bebob, &bebob->rx_stream);
+}
+
+/*
+ * See: Table 50: Extended Stream Format Info Format Hierarchy Level 2’
+ * in Additional AVC commands (Nov 2003, BridgeCo)
+ * Also 'Clause 12 AM824 sequence adaption layers' in IEC 61883-6:2005
+ */
+static int
+parse_stream_formation(u8 *buf, unsigned int len,
+ struct snd_bebob_stream_formation *formation)
+{
+ unsigned int i, e, channels, format;
+
+ /*
+ * this module can support a hierarchy combination that:
+ * Root: Audio and Music (0x90)
+ * Level 1: AM824 Compound (0x40)
+ */
+ if ((buf[0] != 0x90) || (buf[1] != 0x40))
+ return -ENOSYS;
+
+ /* check sampling rate */
+ for (i = 0; i < ARRAY_SIZE(bridgeco_freq_table); i++) {
+ if (buf[2] == bridgeco_freq_table[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(bridgeco_freq_table))
+ return -ENOSYS;
+
+ /* Avoid double count by different entries for the same rate. */
+ memset(&formation[i], 0, sizeof(struct snd_bebob_stream_formation));
+
+ for (e = 0; e < buf[4]; e++) {
+ channels = buf[5 + e * 2];
+ format = buf[6 + e * 2];
+
+ switch (format) {
+ /* IEC 60958 Conformant, currently handled as MBLA */
+ case 0x00:
+ /* Multi bit linear audio */
+ case 0x06: /* Raw */
+ formation[i].pcm += channels;
+ break;
+ /* MIDI Conformant */
+ case 0x0d:
+ formation[i].midi += channels;
+ break;
+ /* IEC 61937-3 to 7 */
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ /* Multi bit linear audio */
+ case 0x07: /* DVD-Audio */
+ case 0x0c: /* High Precision */
+ /* One Bit Audio */
+ case 0x08: /* (Plain) Raw */
+ case 0x09: /* (Plain) SACD */
+ case 0x0a: /* (Encoded) Raw */
+ case 0x0b: /* (Encoded) SACD */
+ /* Synchronization Stream (Stereo Raw audio) */
+ case 0x40:
+ /* Don't care */
+ case 0xff:
+ default:
+ return -ENOSYS; /* not supported */
+ }
+ }
+
+ if (formation[i].pcm > AM824_MAX_CHANNELS_FOR_PCM ||
+ formation[i].midi > AM824_MAX_CHANNELS_FOR_MIDI)
+ return -ENOSYS;
+
+ return 0;
+}
+
+static int fill_stream_formations(struct snd_bebob *bebob, u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ enum avc_bridgeco_plug_dir plug_dir, unsigned int plug_id,
+ struct snd_bebob_stream_formation *formations)
+{
+ enum avc_bridgeco_plug_type plug_type;
+ u8 *buf;
+ unsigned int len, eid;
+ int err;
+
+ avc_bridgeco_fill_unit_addr(addr, plug_dir, AVC_BRIDGECO_PLUG_UNIT_ISOC, plug_id);
+
+ err = avc_bridgeco_get_plug_type(bebob->unit, addr, &plug_type);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "Fail to get type for isoc %d plug 0: %d\n", plug_dir, err);
+ return err;
+ } else if (plug_type != AVC_BRIDGECO_PLUG_TYPE_ISOC)
+ return -ENXIO;
+
+ buf = kmalloc(FORMAT_MAXIMUM_LENGTH, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ for (eid = 0; eid < SND_BEBOB_STRM_FMT_ENTRIES; ++eid) {
+ avc_bridgeco_fill_unit_addr(addr, plug_dir, AVC_BRIDGECO_PLUG_UNIT_ISOC, plug_id);
+
+ len = FORMAT_MAXIMUM_LENGTH;
+ err = avc_bridgeco_get_plug_strm_fmt(bebob->unit, addr, buf, &len, eid);
+ // No entries remained.
+ if (err == -EINVAL && eid > 0) {
+ err = 0;
+ break;
+ } else if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get stream format %d for isoc %d plug %d:%d\n",
+ eid, plug_dir, plug_id, err);
+ break;
+ }
+
+ err = parse_stream_formation(buf, len, formations);
+ if (err < 0)
+ break;
+ }
+
+ kfree(buf);
+ return err;
+}
+
+static int detect_midi_ports(struct snd_bebob *bebob,
+ const struct snd_bebob_stream_formation *formats,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES], enum avc_bridgeco_plug_dir plug_dir,
+ unsigned int plug_count, unsigned int *midi_ports)
+{
+ int i;
+ int err = 0;
+
+ *midi_ports = 0;
+
+ /// Detect the number of available MIDI ports when packet has MIDI conformant data channel.
+ for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; ++i) {
+ if (formats[i].midi > 0)
+ break;
+ }
+ if (i >= SND_BEBOB_STRM_FMT_ENTRIES)
+ return 0;
+
+ for (i = 0; i < plug_count; ++i) {
+ enum avc_bridgeco_plug_type plug_type;
+ unsigned int ch_count;
+
+ avc_bridgeco_fill_unit_addr(addr, plug_dir, AVC_BRIDGECO_PLUG_UNIT_EXT, i);
+
+ err = avc_bridgeco_get_plug_type(bebob->unit, addr, &plug_type);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get type for external %d plug %d: %d\n",
+ plug_dir, i, err);
+ break;
+ } else if (plug_type != AVC_BRIDGECO_PLUG_TYPE_MIDI) {
+ continue;
+ }
+
+ err = avc_bridgeco_get_plug_ch_count(bebob->unit, addr, &ch_count);
+ if (err < 0)
+ break;
+ // Yamaha GO44, GO46, Terratec Phase 24, Phase x24 reports 0 for the number of
+ // channels in external output plug 3 (MIDI type) even if it has a pair of physical
+ // MIDI jacks. As a workaround, assume it as one.
+ if (ch_count == 0)
+ ch_count = 1;
+ *midi_ports += ch_count;
+ }
+
+ return err;
+}
+
+static int
+seek_msu_sync_input_plug(struct snd_bebob *bebob)
+{
+ u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES];
+ unsigned int i;
+ enum avc_bridgeco_plug_type type;
+ int err;
+
+ /* Get the number of Music Sub Unit for both direction. */
+ err = avc_general_get_plug_info(bebob->unit, 0x0c, 0x00, 0x00, plugs);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get info for MSU in/out plugs: %d\n",
+ err);
+ goto end;
+ }
+
+ /* seek destination plugs for 'MSU sync input' */
+ bebob->sync_input_plug = -1;
+ for (i = 0; i < plugs[0]; i++) {
+ avc_bridgeco_fill_msu_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN, i);
+ err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get type for MSU in plug %d: %d\n",
+ i, err);
+ goto end;
+ }
+
+ if (type == AVC_BRIDGECO_PLUG_TYPE_SYNC) {
+ bebob->sync_input_plug = i;
+ break;
+ }
+ }
+end:
+ return err;
+}
+
+int snd_bebob_stream_discover(struct snd_bebob *bebob)
+{
+ const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+ u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES];
+ int err;
+
+ /* the number of plugs for isoc in/out, ext in/out */
+ err = avc_general_get_plug_info(bebob->unit, 0x1f, 0x07, 0x00, plugs);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get info for isoc/external in/out plugs: %d\n",
+ err);
+ goto end;
+ }
+
+ /*
+ * This module supports at least one isoc input plug and one isoc
+ * output plug.
+ */
+ if ((plugs[0] == 0) || (plugs[1] == 0)) {
+ err = -ENOSYS;
+ goto end;
+ }
+
+ err = fill_stream_formations(bebob, addr, AVC_BRIDGECO_PLUG_DIR_IN, 0,
+ bebob->rx_stream_formations);
+ if (err < 0)
+ goto end;
+
+ err = fill_stream_formations(bebob, addr, AVC_BRIDGECO_PLUG_DIR_OUT, 0,
+ bebob->tx_stream_formations);
+ if (err < 0)
+ goto end;
+
+ err = detect_midi_ports(bebob, bebob->tx_stream_formations, addr, AVC_BRIDGECO_PLUG_DIR_IN,
+ plugs[2], &bebob->midi_input_ports);
+ if (err < 0)
+ goto end;
+
+ err = detect_midi_ports(bebob, bebob->rx_stream_formations, addr, AVC_BRIDGECO_PLUG_DIR_OUT,
+ plugs[3], &bebob->midi_output_ports);
+ if (err < 0)
+ goto end;
+
+ /* for check source of clock later */
+ if (!clk_spec)
+ err = seek_msu_sync_input_plug(bebob);
+end:
+ return err;
+}
+
+void snd_bebob_stream_lock_changed(struct snd_bebob *bebob)
+{
+ bebob->dev_lock_changed = true;
+ wake_up(&bebob->hwdep_wait);
+}
+
+int snd_bebob_stream_lock_try(struct snd_bebob *bebob)
+{
+ guard(spinlock_irq)(&bebob->lock);
+
+ /* user land lock this */
+ if (bebob->dev_lock_count < 0)
+ return -EBUSY;
+
+ /* this is the first time */
+ if (bebob->dev_lock_count++ == 0)
+ snd_bebob_stream_lock_changed(bebob);
+ return 0;
+}
+
+void snd_bebob_stream_lock_release(struct snd_bebob *bebob)
+{
+ guard(spinlock_irq)(&bebob->lock);
+
+ if (WARN_ON(bebob->dev_lock_count <= 0))
+ return;
+ if (--bebob->dev_lock_count == 0)
+ snd_bebob_stream_lock_changed(bebob);
+}
diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c
new file mode 100644
index 000000000000..c2dd074eca32
--- /dev/null
+++ b/sound/firewire/bebob/bebob_terratec.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_terratec.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./bebob.h"
+
+static const enum snd_bebob_clock_type phase88_rack_clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* Word Clock */
+};
+static int
+phase88_rack_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+ unsigned int enable_ext, enable_word;
+ int err;
+
+ err = avc_audio_get_selector(bebob->unit, 0, 9, &enable_ext);
+ if (err < 0)
+ goto end;
+ err = avc_audio_get_selector(bebob->unit, 0, 8, &enable_word);
+ if (err < 0)
+ goto end;
+
+ if (enable_ext == 0)
+ *id = 0;
+ else if (enable_word == 0)
+ *id = 1;
+ else
+ *id = 2;
+end:
+ return err;
+}
+
+static const struct snd_bebob_rate_spec phase_series_rate_spec = {
+ .get = &snd_bebob_stream_get_rate,
+ .set = &snd_bebob_stream_set_rate,
+};
+
+/* PHASE 88 Rack FW */
+static const struct snd_bebob_clock_spec phase88_rack_clk = {
+ .num = ARRAY_SIZE(phase88_rack_clk_src_types),
+ .types = phase88_rack_clk_src_types,
+ .get = &phase88_rack_clk_src_get,
+};
+const struct snd_bebob_spec phase88_rack_spec = {
+ .clock = &phase88_rack_clk,
+ .rate = &phase_series_rate_spec,
+ .meter = NULL
+};
diff --git a/sound/firewire/bebob/bebob_yamaha_terratec.c b/sound/firewire/bebob/bebob_yamaha_terratec.c
new file mode 100644
index 000000000000..ce1975e6ab86
--- /dev/null
+++ b/sound/firewire/bebob/bebob_yamaha_terratec.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * bebob_yamaha.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./bebob.h"
+
+/*
+ * NOTE:
+ * Yamaha GO44 is not designed to be used as stand-alone mixer. So any streams
+ * must be accompanied. If changing the state, a LED on the device starts to
+ * blink and its sync status is false. In this state, the device sounds nothing
+ * even if streaming. To start streaming at the current sampling rate is only
+ * way to recover this state. GO46 is better for stand-alone mixer.
+ *
+ * Both of them have a capability to change its sampling rate up to 192.0kHz.
+ * At 192.0kHz, the device reports 4 PCM-in, 1 MIDI-in, 6 PCM-out, 1 MIDI-out.
+ * But Yamaha's driver reduce 2 PCM-in, 1 MIDI-in, 2 PCM-out, 1 MIDI-out to use
+ * 'Extended Stream Format Information Command - Single Request' in 'Additional
+ * AVC commands' defined by BridgeCo.
+ * This ALSA driver don't do this because a bit tiresome. Then isochronous
+ * streaming with many asynchronous transactions brings sounds with noises.
+ * Unfortunately current 'ffado-mixer' generated many asynchronous transaction
+ * to observe device's state, mainly check cmp connection and signal format. I
+ * recommend users to close ffado-mixer at 192.0kHz if mixer is needless.
+ *
+ * Terratec PHASE 24 FW and PHASE X24 FW are internally the same as
+ * Yamaha GO 44 and GO 46. Yamaha and Terratec had cooperated for these models.
+ */
+
+static const enum snd_bebob_clock_type clk_src_types[] = {
+ SND_BEBOB_CLOCK_TYPE_INTERNAL,
+ SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
+};
+static int
+clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+ int err;
+
+ err = avc_audio_get_selector(bebob->unit, 0, 4, id);
+ if (err < 0)
+ return err;
+
+ if (*id >= ARRAY_SIZE(clk_src_types))
+ return -EIO;
+
+ return 0;
+}
+static const struct snd_bebob_clock_spec clock_spec = {
+ .num = ARRAY_SIZE(clk_src_types),
+ .types = clk_src_types,
+ .get = &clk_src_get,
+};
+static const struct snd_bebob_rate_spec rate_spec = {
+ .get = &snd_bebob_stream_get_rate,
+ .set = &snd_bebob_stream_set_rate,
+};
+const struct snd_bebob_spec yamaha_terratec_spec = {
+ .clock = &clock_spec,
+ .rate = &rate_spec,
+ .meter = NULL
+};
diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c
new file mode 100644
index 000000000000..b2b76c7c71b3
--- /dev/null
+++ b/sound/firewire/cmp.c
@@ -0,0 +1,351 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Connection Management Procedures (IEC 61883-1) helper functions
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include "lib.h"
+#include "iso-resources.h"
+#include "cmp.h"
+
+/* MPR common fields */
+#define MPR_SPEED_MASK 0xc0000000
+#define MPR_SPEED_SHIFT 30
+#define MPR_XSPEED_MASK 0x00000060
+#define MPR_XSPEED_SHIFT 5
+#define MPR_PLUGS_MASK 0x0000001f
+
+/* PCR common fields */
+#define PCR_ONLINE 0x80000000
+#define PCR_BCAST_CONN 0x40000000
+#define PCR_P2P_CONN_MASK 0x3f000000
+#define PCR_P2P_CONN_SHIFT 24
+#define PCR_CHANNEL_MASK 0x003f0000
+#define PCR_CHANNEL_SHIFT 16
+
+/* oPCR specific fields */
+#define OPCR_XSPEED_MASK 0x00C00000
+#define OPCR_XSPEED_SHIFT 22
+#define OPCR_SPEED_MASK 0x0000C000
+#define OPCR_SPEED_SHIFT 14
+#define OPCR_OVERHEAD_ID_MASK 0x00003C00
+#define OPCR_OVERHEAD_ID_SHIFT 10
+
+enum bus_reset_handling {
+ ABORT_ON_BUS_RESET,
+ SUCCEED_ON_BUS_RESET,
+};
+
+static __printf(2, 3)
+void cmp_error(struct cmp_connection *c, const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ dev_err(&c->resources.unit->device, "%cPCR%u: %pV",
+ (c->direction == CMP_INPUT) ? 'i' : 'o',
+ c->pcr_index, &(struct va_format){ fmt, &va });
+ va_end(va);
+}
+
+static u64 mpr_address(struct cmp_connection *c)
+{
+ if (c->direction == CMP_INPUT)
+ return CSR_REGISTER_BASE + CSR_IMPR;
+ else
+ return CSR_REGISTER_BASE + CSR_OMPR;
+}
+
+static u64 pcr_address(struct cmp_connection *c)
+{
+ if (c->direction == CMP_INPUT)
+ return CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index);
+ else
+ return CSR_REGISTER_BASE + CSR_OPCR(c->pcr_index);
+}
+
+static int pcr_modify(struct cmp_connection *c,
+ __be32 (*modify)(struct cmp_connection *c, __be32 old),
+ int (*check)(struct cmp_connection *c, __be32 pcr),
+ enum bus_reset_handling bus_reset_handling)
+{
+ __be32 old_arg, buffer[2];
+ int err;
+
+ buffer[0] = c->last_pcr_value;
+ for (;;) {
+ old_arg = buffer[0];
+ buffer[1] = modify(c, buffer[0]);
+
+ err = snd_fw_transaction(
+ c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
+ pcr_address(c), buffer, 8,
+ FW_FIXED_GENERATION | c->resources.generation);
+
+ if (err < 0) {
+ if (err == -EAGAIN &&
+ bus_reset_handling == SUCCEED_ON_BUS_RESET)
+ err = 0;
+ return err;
+ }
+
+ if (buffer[0] == old_arg) /* success? */
+ break;
+
+ if (check) {
+ err = check(c, buffer[0]);
+ if (err < 0)
+ return err;
+ }
+ }
+ c->last_pcr_value = buffer[1];
+
+ return 0;
+}
+
+
+/**
+ * cmp_connection_init - initializes a connection manager
+ * @c: the connection manager to initialize
+ * @unit: a unit of the target device
+ * @direction: input or output
+ * @pcr_index: the index of the iPCR/oPCR on the target device
+ */
+int cmp_connection_init(struct cmp_connection *c,
+ struct fw_unit *unit,
+ enum cmp_direction direction,
+ unsigned int pcr_index)
+{
+ __be32 mpr_be;
+ u32 mpr;
+ int err;
+
+ c->direction = direction;
+ err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
+ mpr_address(c), &mpr_be, 4, 0);
+ if (err < 0)
+ return err;
+ mpr = be32_to_cpu(mpr_be);
+
+ if (pcr_index >= (mpr & MPR_PLUGS_MASK))
+ return -EINVAL;
+
+ err = fw_iso_resources_init(&c->resources, unit);
+ if (err < 0)
+ return err;
+
+ c->connected = false;
+ mutex_init(&c->mutex);
+ c->last_pcr_value = cpu_to_be32(0x80000000);
+ c->pcr_index = pcr_index;
+ c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT;
+ if (c->max_speed == SCODE_BETA)
+ c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT;
+
+ return 0;
+}
+EXPORT_SYMBOL(cmp_connection_init);
+
+/**
+ * cmp_connection_check_used - check connection is already esablished or not
+ * @c: the connection manager to be checked
+ * @used: the pointer to store the result of checking the connection
+ */
+int cmp_connection_check_used(struct cmp_connection *c, bool *used)
+{
+ __be32 pcr;
+ int err;
+
+ err = snd_fw_transaction(
+ c->resources.unit, TCODE_READ_QUADLET_REQUEST,
+ pcr_address(c), &pcr, 4, 0);
+ if (err >= 0)
+ *used = !!(pcr & cpu_to_be32(PCR_BCAST_CONN |
+ PCR_P2P_CONN_MASK));
+
+ return err;
+}
+EXPORT_SYMBOL(cmp_connection_check_used);
+
+/**
+ * cmp_connection_destroy - free connection manager resources
+ * @c: the connection manager
+ */
+void cmp_connection_destroy(struct cmp_connection *c)
+{
+ WARN_ON(c->connected);
+ mutex_destroy(&c->mutex);
+ fw_iso_resources_destroy(&c->resources);
+}
+EXPORT_SYMBOL(cmp_connection_destroy);
+
+int cmp_connection_reserve(struct cmp_connection *c,
+ unsigned int max_payload_bytes)
+{
+ guard(mutex)(&c->mutex);
+
+ if (WARN_ON(c->resources.allocated))
+ return -EBUSY;
+
+ c->speed = min(c->max_speed,
+ fw_parent_device(c->resources.unit)->max_speed);
+
+ return fw_iso_resources_allocate(&c->resources, max_payload_bytes,
+ c->speed);
+}
+EXPORT_SYMBOL(cmp_connection_reserve);
+
+void cmp_connection_release(struct cmp_connection *c)
+{
+ guard(mutex)(&c->mutex);
+ fw_iso_resources_free(&c->resources);
+}
+EXPORT_SYMBOL(cmp_connection_release);
+
+static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
+{
+ ipcr &= ~cpu_to_be32(PCR_BCAST_CONN |
+ PCR_P2P_CONN_MASK |
+ PCR_CHANNEL_MASK);
+ ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
+ ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
+
+ return ipcr;
+}
+
+static int get_overhead_id(struct cmp_connection *c)
+{
+ int id;
+
+ /*
+ * apply "oPCR overhead ID encoding"
+ * the encoding table can convert up to 512.
+ * here the value over 512 is converted as the same way as 512.
+ */
+ for (id = 1; id < 16; id++) {
+ if (c->resources.bandwidth_overhead < (id << 5))
+ break;
+ }
+ if (id == 16)
+ id = 0;
+
+ return id;
+}
+
+static __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr)
+{
+ unsigned int spd, xspd;
+
+ /* generate speed and extended speed field value */
+ if (c->speed > SCODE_400) {
+ spd = SCODE_800;
+ xspd = c->speed - SCODE_800;
+ } else {
+ spd = c->speed;
+ xspd = 0;
+ }
+
+ opcr &= ~cpu_to_be32(PCR_BCAST_CONN |
+ PCR_P2P_CONN_MASK |
+ OPCR_XSPEED_MASK |
+ PCR_CHANNEL_MASK |
+ OPCR_SPEED_MASK |
+ OPCR_OVERHEAD_ID_MASK);
+ opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
+ opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT);
+ opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
+ opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT);
+ opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT);
+
+ return opcr;
+}
+
+static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
+{
+ if (pcr & cpu_to_be32(PCR_BCAST_CONN |
+ PCR_P2P_CONN_MASK)) {
+ cmp_error(c, "plug is already in use\n");
+ return -EBUSY;
+ }
+ if (!(pcr & cpu_to_be32(PCR_ONLINE))) {
+ cmp_error(c, "plug is not on-line\n");
+ return -ECONNREFUSED;
+ }
+
+ return 0;
+}
+
+/**
+ * cmp_connection_establish - establish a connection to the target
+ * @c: the connection manager
+ *
+ * This function establishes a point-to-point connection from the local
+ * computer to the target by allocating isochronous resources (channel and
+ * bandwidth) and setting the target's input/output plug control register.
+ * When this function succeeds, the caller is responsible for starting
+ * transmitting packets.
+ */
+int cmp_connection_establish(struct cmp_connection *c)
+{
+ int err;
+
+ guard(mutex)(&c->mutex);
+
+ if (WARN_ON(c->connected))
+ return -EISCONN;
+
+retry_after_bus_reset:
+ if (c->direction == CMP_OUTPUT)
+ err = pcr_modify(c, opcr_set_modify, pcr_set_check,
+ ABORT_ON_BUS_RESET);
+ else
+ err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
+ ABORT_ON_BUS_RESET);
+
+ if (err == -EAGAIN) {
+ err = fw_iso_resources_update(&c->resources);
+ if (err >= 0)
+ goto retry_after_bus_reset;
+ }
+ if (err >= 0)
+ c->connected = true;
+
+ return err;
+}
+EXPORT_SYMBOL(cmp_connection_establish);
+
+static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr)
+{
+ return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK);
+}
+
+/**
+ * cmp_connection_break - break the connection to the target
+ * @c: the connection manager
+ *
+ * This function deactives the connection in the target's input/output plug
+ * control register, and frees the isochronous resources of the connection.
+ * Before calling this function, the caller should cease transmitting packets.
+ */
+void cmp_connection_break(struct cmp_connection *c)
+{
+ int err;
+
+ guard(mutex)(&c->mutex);
+
+ if (!c->connected)
+ return;
+
+ err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
+ if (err < 0)
+ cmp_error(c, "plug is still connected\n");
+
+ c->connected = false;
+}
+EXPORT_SYMBOL(cmp_connection_break);
diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h
new file mode 100644
index 000000000000..66fc08b742d2
--- /dev/null
+++ b/sound/firewire/cmp.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef SOUND_FIREWIRE_CMP_H_INCLUDED
+#define SOUND_FIREWIRE_CMP_H_INCLUDED
+
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include "iso-resources.h"
+
+struct fw_unit;
+
+enum cmp_direction {
+ CMP_INPUT = 0,
+ CMP_OUTPUT,
+};
+
+/**
+ * struct cmp_connection - manages an isochronous connection to a device
+ * @speed: the connection's actual speed
+ *
+ * This structure manages (using CMP) an isochronous stream between the local
+ * computer and a device's input plug (iPCR) and output plug (oPCR).
+ *
+ * There is no corresponding oPCR created on the local computer, so it is not
+ * possible to overlay connections on top of this one.
+ */
+struct cmp_connection {
+ int speed;
+ /* private: */
+ bool connected;
+ struct mutex mutex;
+ struct fw_iso_resources resources;
+ __be32 last_pcr_value;
+ unsigned int pcr_index;
+ unsigned int max_speed;
+ enum cmp_direction direction;
+};
+
+int cmp_connection_init(struct cmp_connection *connection,
+ struct fw_unit *unit,
+ enum cmp_direction direction,
+ unsigned int pcr_index);
+int cmp_connection_check_used(struct cmp_connection *connection, bool *used);
+void cmp_connection_destroy(struct cmp_connection *connection);
+
+int cmp_connection_reserve(struct cmp_connection *connection,
+ unsigned int max_payload);
+void cmp_connection_release(struct cmp_connection *connection);
+
+int cmp_connection_establish(struct cmp_connection *connection);
+void cmp_connection_break(struct cmp_connection *connection);
+
+#endif
diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile
new file mode 100644
index 000000000000..478cd7a08fb5
--- /dev/null
+++ b/sound/firewire/dice/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-dice-y := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \
+ dice-pcm.o dice-hwdep.o dice.o dice-tcelectronic.o \
+ dice-alesis.o dice-extension.o dice-mytek.o dice-presonus.o \
+ dice-harman.o dice-focusrite.o dice-weiss.o dice-teac.o
+obj-$(CONFIG_SND_DICE) += snd-dice.o
diff --git a/sound/firewire/dice/dice-alesis.c b/sound/firewire/dice/dice-alesis.c
new file mode 100644
index 000000000000..27c13b9cc9ef
--- /dev/null
+++ b/sound/firewire/dice/dice-alesis.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dice-alesis.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) 2018 Takashi Sakamoto
+ */
+
+#include "dice.h"
+
+static const unsigned int
+alesis_io14_tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT] = {
+ {6, 6, 4}, /* Tx0 = Analog + S/PDIF. */
+ {8, 4, 0}, /* Tx1 = ADAT1. */
+};
+
+static const unsigned int
+alesis_io26_tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT] = {
+ {10, 10, 4}, /* Tx0 = Analog + S/PDIF. */
+ {16, 4, 0}, /* Tx1 = ADAT1 + ADAT2 (available at low rate). */
+};
+
+int snd_dice_detect_alesis_formats(struct snd_dice *dice)
+{
+ __be32 reg;
+ u32 data;
+ int i;
+ int err;
+
+ err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ if (data == 4 || data == 6) {
+ memcpy(dice->tx_pcm_chs, alesis_io14_tx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT *
+ sizeof(unsigned int));
+ } else {
+ memcpy(dice->tx_pcm_chs, alesis_io26_tx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT *
+ sizeof(unsigned int));
+ }
+
+ for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i)
+ dice->rx_pcm_chs[0][i] = 8;
+
+ dice->tx_midi_ports[0] = 1;
+ dice->rx_midi_ports[0] = 1;
+
+ return 0;
+}
+
+int snd_dice_detect_alesis_mastercontrol_formats(struct snd_dice *dice)
+{
+ int i;
+
+ dice->tx_pcm_chs[0][SND_DICE_RATE_MODE_LOW] = 16;
+ dice->tx_pcm_chs[1][SND_DICE_RATE_MODE_LOW] = 12;
+ dice->tx_pcm_chs[0][SND_DICE_RATE_MODE_MIDDLE] = 12;
+ dice->tx_pcm_chs[1][SND_DICE_RATE_MODE_MIDDLE] = 4;
+ dice->tx_pcm_chs[0][SND_DICE_RATE_MODE_HIGH] = 8;
+ dice->tx_pcm_chs[1][SND_DICE_RATE_MODE_HIGH] = 0;
+
+ for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i) {
+ dice->rx_pcm_chs[0][i] = 6;
+ dice->rx_pcm_chs[1][i] = 0;
+ }
+
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ dice->tx_midi_ports[i] = 2;
+ dice->rx_midi_ports[i] = 2;
+ }
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-extension.c b/sound/firewire/dice/dice-extension.c
new file mode 100644
index 000000000000..48bfb3ad93ce
--- /dev/null
+++ b/sound/firewire/dice/dice-extension.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dice-extension.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) 2018 Takashi Sakamoto
+ */
+
+#include "dice.h"
+
+/* For TCD2210/2220, TCAT defines extension of application protocol. */
+
+#define DICE_EXT_APP_SPACE 0xffffe0200000uLL
+
+#define DICE_EXT_APP_CAPS_OFFSET 0x00
+#define DICE_EXT_APP_CAPS_SIZE 0x04
+#define DICE_EXT_APP_CMD_OFFSET 0x08
+#define DICE_EXT_APP_CMD_SIZE 0x0c
+#define DICE_EXT_APP_MIXER_OFFSET 0x10
+#define DICE_EXT_APP_MIXER_SIZE 0x14
+#define DICE_EXT_APP_PEAK_OFFSET 0x18
+#define DICE_EXT_APP_PEAK_SIZE 0x1c
+#define DICE_EXT_APP_ROUTER_OFFSET 0x20
+#define DICE_EXT_APP_ROUTER_SIZE 0x24
+#define DICE_EXT_APP_STREAM_OFFSET 0x28
+#define DICE_EXT_APP_STREAM_SIZE 0x2c
+#define DICE_EXT_APP_CURRENT_OFFSET 0x30
+#define DICE_EXT_APP_CURRENT_SIZE 0x34
+#define DICE_EXT_APP_STANDALONE_OFFSET 0x38
+#define DICE_EXT_APP_STANDALONE_SIZE 0x3c
+#define DICE_EXT_APP_APPLICATION_OFFSET 0x40
+#define DICE_EXT_APP_APPLICATION_SIZE 0x44
+
+#define EXT_APP_STREAM_TX_NUMBER 0x0000
+#define EXT_APP_STREAM_RX_NUMBER 0x0004
+#define EXT_APP_STREAM_ENTRIES 0x0008
+#define EXT_APP_STREAM_ENTRY_SIZE 0x010c
+#define EXT_APP_NUMBER_AUDIO 0x0000
+#define EXT_APP_NUMBER_MIDI 0x0004
+#define EXT_APP_NAMES 0x0008
+#define EXT_APP_NAMES_SIZE 256
+#define EXT_APP_AC3 0x0108
+
+#define EXT_APP_CONFIG_LOW_ROUTER 0x0000
+#define EXT_APP_CONFIG_LOW_STREAM 0x1000
+#define EXT_APP_CONFIG_MIDDLE_ROUTER 0x2000
+#define EXT_APP_CONFIG_MIDDLE_STREAM 0x3000
+#define EXT_APP_CONFIG_HIGH_ROUTER 0x4000
+#define EXT_APP_CONFIG_HIGH_STREAM 0x5000
+
+static inline int read_transaction(struct snd_dice *dice, u64 section_addr,
+ u32 offset, void *buf, size_t len)
+{
+ return snd_fw_transaction(dice->unit,
+ len == 4 ? TCODE_READ_QUADLET_REQUEST :
+ TCODE_READ_BLOCK_REQUEST,
+ section_addr + offset, buf, len, 0);
+}
+
+static int read_stream_entries(struct snd_dice *dice, u64 section_addr,
+ u32 base_offset, unsigned int stream_count,
+ unsigned int mode,
+ unsigned int pcm_channels[MAX_STREAMS][3],
+ unsigned int midi_ports[MAX_STREAMS])
+{
+ u32 entry_offset;
+ __be32 reg[2];
+ int err;
+ int i;
+
+ for (i = 0; i < stream_count; ++i) {
+ entry_offset = base_offset + i * EXT_APP_STREAM_ENTRY_SIZE;
+ err = read_transaction(dice, section_addr,
+ entry_offset + EXT_APP_NUMBER_AUDIO,
+ reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ pcm_channels[i][mode] = be32_to_cpu(reg[0]);
+ midi_ports[i] = max(midi_ports[i], be32_to_cpu(reg[1]));
+ }
+
+ return 0;
+}
+
+static int detect_stream_formats(struct snd_dice *dice, u64 section_addr)
+{
+ u32 base_offset;
+ __be32 reg[2];
+ unsigned int stream_count;
+ int mode;
+ int err = 0;
+
+ for (mode = 0; mode < SND_DICE_RATE_MODE_COUNT; ++mode) {
+ unsigned int cap;
+
+ /*
+ * Some models report stream formats at highest mode, however
+ * they don't support the mode. Check clock capabilities.
+ */
+ if (mode == 2) {
+ cap = CLOCK_CAP_RATE_176400 | CLOCK_CAP_RATE_192000;
+ } else if (mode == 1) {
+ cap = CLOCK_CAP_RATE_88200 | CLOCK_CAP_RATE_96000;
+ } else {
+ cap = CLOCK_CAP_RATE_32000 | CLOCK_CAP_RATE_44100 |
+ CLOCK_CAP_RATE_48000;
+ }
+ if (!(cap & dice->clock_caps))
+ continue;
+
+ base_offset = 0x2000 * mode + 0x1000;
+
+ err = read_transaction(dice, section_addr,
+ base_offset + EXT_APP_STREAM_TX_NUMBER,
+ &reg, sizeof(reg));
+ if (err < 0)
+ break;
+
+ base_offset += EXT_APP_STREAM_ENTRIES;
+ stream_count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
+ err = read_stream_entries(dice, section_addr, base_offset,
+ stream_count, mode,
+ dice->tx_pcm_chs,
+ dice->tx_midi_ports);
+ if (err < 0)
+ break;
+
+ base_offset += stream_count * EXT_APP_STREAM_ENTRY_SIZE;
+ stream_count = min_t(unsigned int, be32_to_cpu(reg[1]), MAX_STREAMS);
+ err = read_stream_entries(dice, section_addr, base_offset,
+ stream_count,
+ mode, dice->rx_pcm_chs,
+ dice->rx_midi_ports);
+ if (err < 0)
+ break;
+ }
+
+ return err;
+}
+
+int snd_dice_detect_extension_formats(struct snd_dice *dice)
+{
+ __be32 *pointers;
+ unsigned int i;
+ u64 section_addr;
+ int err;
+
+ pointers = kmalloc_array(9, sizeof(__be32) * 2, GFP_KERNEL);
+ if (pointers == NULL)
+ return -ENOMEM;
+
+ err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+ DICE_EXT_APP_SPACE, pointers,
+ 9 * sizeof(__be32) * 2, 0);
+ if (err < 0)
+ goto end;
+
+ /* Check two of them for offset have the same value or not. */
+ for (i = 0; i < 9; ++i) {
+ int j;
+
+ for (j = i + 1; j < 9; ++j) {
+ if (pointers[i * 2] == pointers[j * 2]) {
+ // Fallback to limited functionality.
+ err = -ENXIO;
+ goto end;
+ }
+ }
+ }
+
+ section_addr = DICE_EXT_APP_SPACE + be32_to_cpu(pointers[12]) * 4;
+ err = detect_stream_formats(dice, section_addr);
+end:
+ kfree(pointers);
+ return err;
+}
diff --git a/sound/firewire/dice/dice-focusrite.c b/sound/firewire/dice/dice-focusrite.c
new file mode 100644
index 000000000000..ea27cfb01cc0
--- /dev/null
+++ b/sound/firewire/dice/dice-focusrite.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0
+// dice-focusrite.c - a part of driver for DICE based devices
+//
+// Copyright (c) 2022 Takashi Sakamoto
+
+#include "dice.h"
+
+int snd_dice_detect_focusrite_pro40_tcd3070_formats(struct snd_dice *dice)
+{
+ // Focusrite shipped several variants of Saffire Pro 40. One of them is based on TCD3070-CH
+ // apart from the others with TCD2220. It doesn't support TCAT protocol extension.
+ dice->tx_pcm_chs[0][0] = 20;
+ dice->tx_midi_ports[0] = 1;
+ dice->rx_pcm_chs[0][0] = 20;
+ dice->rx_midi_ports[0] = 1;
+
+ dice->tx_pcm_chs[0][1] = 16;
+ dice->tx_midi_ports[1] = 1;
+ dice->rx_pcm_chs[0][1] = 16;
+ dice->rx_midi_ports[1] = 1;
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-harman.c b/sound/firewire/dice/dice-harman.c
new file mode 100644
index 000000000000..212ae77dfca2
--- /dev/null
+++ b/sound/firewire/dice/dice-harman.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+// dice-harman.c - a part of driver for DICE based devices
+//
+// Copyright (c) 2021 Takashi Sakamoto
+
+#include "dice.h"
+
+int snd_dice_detect_harman_formats(struct snd_dice *dice)
+{
+ int i;
+
+ // Lexicon I-ONYX FW810s supports sampling transfer frequency up to
+ // 96.0 kHz, 12 PCM channels and 1 MIDI channel in its first tx stream
+ // , 10 PCM channels and 1 MIDI channel in its first rx stream for all
+ // of the frequencies.
+ for (i = 0; i < 2; ++i) {
+ dice->tx_pcm_chs[0][i] = 12;
+ dice->tx_midi_ports[0] = 1;
+ dice->rx_pcm_chs[0][i] = 10;
+ dice->rx_midi_ports[0] = 1;
+ }
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-hwdep.c b/sound/firewire/dice/dice-hwdep.c
new file mode 100644
index 000000000000..747ff0952483
--- /dev/null
+++ b/sound/firewire/dice/dice-hwdep.c
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * dice_hwdep.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include "dice.h"
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,
+ long count, loff_t *offset)
+{
+ struct snd_dice *dice = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&dice->lock);
+
+ while (!dice->dev_lock_changed && dice->notification_bits == 0) {
+ prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&dice->lock);
+ schedule();
+ finish_wait(&dice->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&dice->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ if (dice->dev_lock_changed) {
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = dice->dev_lock_count > 0;
+ dice->dev_lock_changed = false;
+
+ count = min_t(long, count, sizeof(event.lock_status));
+ } else {
+ event.dice_notification.type =
+ SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION;
+ event.dice_notification.notification = dice->notification_bits;
+ dice->notification_bits = 0;
+
+ count = min_t(long, count, sizeof(event.dice_notification));
+ }
+
+ spin_unlock_irq(&dice->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_dice *dice = hwdep->private_data;
+
+ poll_wait(file, &dice->hwdep_wait, wait);
+
+ guard(spinlock_irq)(&dice->lock);
+ if (dice->dev_lock_changed || dice->notification_bits != 0)
+ return EPOLLIN | EPOLLRDNORM;
+ else
+ return 0;
+}
+
+static int hwdep_get_info(struct snd_dice *dice, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(dice->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_DICE;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strscpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_dice *dice)
+{
+ guard(spinlock_irq)(&dice->lock);
+
+ if (dice->dev_lock_count == 0) {
+ dice->dev_lock_count = -1;
+ return 0;
+ } else {
+ return -EBUSY;
+ }
+}
+
+static int hwdep_unlock(struct snd_dice *dice)
+{
+ guard(spinlock_irq)(&dice->lock);
+
+ if (dice->dev_lock_count == -1) {
+ dice->dev_lock_count = 0;
+ return 0;
+ } else {
+ return -EBADFD;
+ }
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_dice *dice = hwdep->private_data;
+
+ guard(spinlock_irq)(&dice->lock);
+ if (dice->dev_lock_count == -1)
+ dice->dev_lock_count = 0;
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_dice *dice = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(dice, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(dice);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(dice);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_dice_create_hwdep(struct snd_dice *dice)
+{
+ static const struct snd_hwdep_ops ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep);
+ if (err < 0)
+ return err;
+ strscpy(hwdep->name, "DICE");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE;
+ hwdep->ops = ops;
+ hwdep->private_data = dice;
+ hwdep->exclusive = true;
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-interface.h b/sound/firewire/dice/dice-interface.h
new file mode 100644
index 000000000000..9cad3d608229
--- /dev/null
+++ b/sound/firewire/dice/dice-interface.h
@@ -0,0 +1,378 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef SOUND_FIREWIRE_DICE_INTERFACE_H_INCLUDED
+#define SOUND_FIREWIRE_DICE_INTERFACE_H_INCLUDED
+
+/*
+ * DICE device interface definitions
+ */
+
+/*
+ * Generally, all registers can be read like memory, i.e., with quadlet read or
+ * block read transactions with at least quadlet-aligned offset and length.
+ * Writes are not allowed except where noted; quadlet-sized registers must be
+ * written with a quadlet write transaction.
+ *
+ * All values are in big endian. The DICE firmware runs on a little-endian CPU
+ * and just byte-swaps _all_ quadlets on the bus, so values without endianness
+ * (e.g. strings) get scrambled and must be byte-swapped again by the driver.
+ */
+
+/*
+ * Streaming is handled by the "DICE driver" interface. Its registers are
+ * located in this private address space.
+ */
+#define DICE_PRIVATE_SPACE 0xffffe0000000uLL
+
+/*
+ * The registers are organized in several sections, which are organized
+ * separately to allow them to be extended individually. Whether a register is
+ * supported can be detected by checking its offset against its section's size.
+ *
+ * The section offset values are relative to DICE_PRIVATE_SPACE; the offset/
+ * size values are measured in quadlets. Read-only.
+ */
+#define DICE_GLOBAL_OFFSET 0x00
+#define DICE_GLOBAL_SIZE 0x04
+#define DICE_TX_OFFSET 0x08
+#define DICE_TX_SIZE 0x0c
+#define DICE_RX_OFFSET 0x10
+#define DICE_RX_SIZE 0x14
+#define DICE_EXT_SYNC_OFFSET 0x18
+#define DICE_EXT_SYNC_SIZE 0x1c
+#define DICE_UNUSED2_OFFSET 0x20
+#define DICE_UNUSED2_SIZE 0x24
+
+/*
+ * Global settings.
+ */
+
+/*
+ * Stores the full 64-bit address (node ID and offset in the node's address
+ * space) where the device will send notifications. Must be changed with
+ * a compare/swap transaction by the owner. This register is automatically
+ * cleared on a bus reset.
+ */
+#define GLOBAL_OWNER 0x000
+#define OWNER_NO_OWNER 0xffff000000000000uLL
+#define OWNER_NODE_SHIFT 48
+
+/*
+ * A bitmask with asynchronous events; read-only. When any event(s) happen,
+ * the bits of previous events are cleared, and the value of this register is
+ * also written to the address stored in the owner register.
+ */
+#define GLOBAL_NOTIFICATION 0x008
+/* Some registers in the Rx/Tx sections may have changed. */
+#define NOTIFY_RX_CFG_CHG 0x00000001
+#define NOTIFY_TX_CFG_CHG 0x00000002
+/* Lock status of the current clock source may have changed. */
+#define NOTIFY_LOCK_CHG 0x00000010
+/* Write to the clock select register has been finished. */
+#define NOTIFY_CLOCK_ACCEPTED 0x00000020
+/* Lock status of some clock source has changed. */
+#define NOTIFY_EXT_STATUS 0x00000040
+/* Other bits may be used for device-specific events. */
+
+/*
+ * A name that can be customized for each device; read/write. Padded with zero
+ * bytes. Quadlets are byte-swapped. The encoding is whatever the host driver
+ * happens to be using.
+ */
+#define GLOBAL_NICK_NAME 0x00c
+#define NICK_NAME_SIZE 64
+
+/*
+ * The current sample rate and clock source; read/write. Whether a clock
+ * source or sample rate is supported is device-specific; the internal clock
+ * source is always available. Low/mid/high = up to 48/96/192 kHz. This
+ * register can be changed even while streams are running.
+ */
+#define GLOBAL_CLOCK_SELECT 0x04c
+#define CLOCK_SOURCE_MASK 0x000000ff
+#define CLOCK_SOURCE_AES1 0x00000000
+#define CLOCK_SOURCE_AES2 0x00000001
+#define CLOCK_SOURCE_AES3 0x00000002
+#define CLOCK_SOURCE_AES4 0x00000003
+#define CLOCK_SOURCE_AES_ANY 0x00000004
+#define CLOCK_SOURCE_ADAT 0x00000005
+#define CLOCK_SOURCE_TDIF 0x00000006
+#define CLOCK_SOURCE_WC 0x00000007
+#define CLOCK_SOURCE_ARX1 0x00000008
+#define CLOCK_SOURCE_ARX2 0x00000009
+#define CLOCK_SOURCE_ARX3 0x0000000a
+#define CLOCK_SOURCE_ARX4 0x0000000b
+#define CLOCK_SOURCE_INTERNAL 0x0000000c
+#define CLOCK_RATE_MASK 0x0000ff00
+#define CLOCK_RATE_32000 0x00000000
+#define CLOCK_RATE_44100 0x00000100
+#define CLOCK_RATE_48000 0x00000200
+#define CLOCK_RATE_88200 0x00000300
+#define CLOCK_RATE_96000 0x00000400
+#define CLOCK_RATE_176400 0x00000500
+#define CLOCK_RATE_192000 0x00000600
+#define CLOCK_RATE_ANY_LOW 0x00000700
+#define CLOCK_RATE_ANY_MID 0x00000800
+#define CLOCK_RATE_ANY_HIGH 0x00000900
+#define CLOCK_RATE_NONE 0x00000a00
+#define CLOCK_RATE_SHIFT 8
+
+/*
+ * Enable streaming; read/write. Writing a non-zero value (re)starts all
+ * streams that have a valid iso channel set; zero stops all streams. The
+ * streams' parameters must be configured before starting. This register is
+ * automatically cleared on a bus reset.
+ */
+#define GLOBAL_ENABLE 0x050
+
+/*
+ * Status of the sample clock; read-only.
+ */
+#define GLOBAL_STATUS 0x054
+/* The current clock source is locked. */
+#define STATUS_SOURCE_LOCKED 0x00000001
+/* The actual sample rate; CLOCK_RATE_32000-_192000 or _NONE. */
+#define STATUS_NOMINAL_RATE_MASK 0x0000ff00
+
+/*
+ * Status of all clock sources; read-only.
+ */
+#define GLOBAL_EXTENDED_STATUS 0x058
+/*
+ * The _LOCKED bits always show the current status; any change generates
+ * a notification.
+ */
+#define EXT_STATUS_AES1_LOCKED 0x00000001
+#define EXT_STATUS_AES2_LOCKED 0x00000002
+#define EXT_STATUS_AES3_LOCKED 0x00000004
+#define EXT_STATUS_AES4_LOCKED 0x00000008
+#define EXT_STATUS_ADAT_LOCKED 0x00000010
+#define EXT_STATUS_TDIF_LOCKED 0x00000020
+#define EXT_STATUS_ARX1_LOCKED 0x00000040
+#define EXT_STATUS_ARX2_LOCKED 0x00000080
+#define EXT_STATUS_ARX3_LOCKED 0x00000100
+#define EXT_STATUS_ARX4_LOCKED 0x00000200
+#define EXT_STATUS_WC_LOCKED 0x00000400
+/*
+ * The _SLIP bits do not generate notifications; a set bit indicates that an
+ * error occurred since the last time when this register was read with
+ * a quadlet read transaction.
+ */
+#define EXT_STATUS_AES1_SLIP 0x00010000
+#define EXT_STATUS_AES2_SLIP 0x00020000
+#define EXT_STATUS_AES3_SLIP 0x00040000
+#define EXT_STATUS_AES4_SLIP 0x00080000
+#define EXT_STATUS_ADAT_SLIP 0x00100000
+#define EXT_STATUS_TDIF_SLIP 0x00200000
+#define EXT_STATUS_ARX1_SLIP 0x00400000
+#define EXT_STATUS_ARX2_SLIP 0x00800000
+#define EXT_STATUS_ARX3_SLIP 0x01000000
+#define EXT_STATUS_ARX4_SLIP 0x02000000
+#define EXT_STATUS_WC_SLIP 0x04000000
+
+/*
+ * The measured rate of the current clock source, in Hz; read-only.
+ */
+#define GLOBAL_SAMPLE_RATE 0x05c
+
+/*
+ * Some old firmware versions do not have the following global registers.
+ * Windows drivers produced by TCAT lost backward compatibility in its
+ * early release because they can handle firmware only which supports the
+ * following registers.
+ */
+
+/*
+ * The version of the DICE driver specification that this device conforms to;
+ * read-only.
+ */
+#define GLOBAL_VERSION 0x060
+
+/*
+ * Supported sample rates and clock sources; read-only.
+ */
+#define GLOBAL_CLOCK_CAPABILITIES 0x064
+#define CLOCK_CAP_RATE_32000 0x00000001
+#define CLOCK_CAP_RATE_44100 0x00000002
+#define CLOCK_CAP_RATE_48000 0x00000004
+#define CLOCK_CAP_RATE_88200 0x00000008
+#define CLOCK_CAP_RATE_96000 0x00000010
+#define CLOCK_CAP_RATE_176400 0x00000020
+#define CLOCK_CAP_RATE_192000 0x00000040
+#define CLOCK_CAP_SOURCE_AES1 0x00010000
+#define CLOCK_CAP_SOURCE_AES2 0x00020000
+#define CLOCK_CAP_SOURCE_AES3 0x00040000
+#define CLOCK_CAP_SOURCE_AES4 0x00080000
+#define CLOCK_CAP_SOURCE_AES_ANY 0x00100000
+#define CLOCK_CAP_SOURCE_ADAT 0x00200000
+#define CLOCK_CAP_SOURCE_TDIF 0x00400000
+#define CLOCK_CAP_SOURCE_WC 0x00800000
+#define CLOCK_CAP_SOURCE_ARX1 0x01000000
+#define CLOCK_CAP_SOURCE_ARX2 0x02000000
+#define CLOCK_CAP_SOURCE_ARX3 0x04000000
+#define CLOCK_CAP_SOURCE_ARX4 0x08000000
+#define CLOCK_CAP_SOURCE_INTERNAL 0x10000000
+
+/*
+ * Names of all clock sources; read-only. Quadlets are byte-swapped. Names
+ * are separated with one backslash, the list is terminated with two
+ * backslashes. Unused clock sources are included.
+ */
+#define GLOBAL_CLOCK_SOURCE_NAMES 0x068
+#define CLOCK_SOURCE_NAMES_SIZE 256
+
+/*
+ * Capture stream settings. This section includes the number/size registers
+ * and the registers of all streams.
+ */
+
+/*
+ * The number of supported capture streams; read-only.
+ */
+#define TX_NUMBER 0x000
+
+/*
+ * The size of one stream's register block, in quadlets; read-only. The
+ * registers of the first stream follow immediately afterwards; the registers
+ * of the following streams are offset by this register's value.
+ */
+#define TX_SIZE 0x004
+
+/*
+ * The isochronous channel number on which packets are sent, or -1 if the
+ * stream is not to be used; read/write.
+ */
+#define TX_ISOCHRONOUS 0x008
+
+/*
+ * The number of audio channels; read-only. There will be one quadlet per
+ * channel; the first channel is the first quadlet in a data block.
+ */
+#define TX_NUMBER_AUDIO 0x00c
+
+/*
+ * The number of MIDI ports, 0-8; read-only. If > 0, there will be one
+ * additional quadlet in each data block, following the audio quadlets.
+ */
+#define TX_NUMBER_MIDI 0x010
+
+/*
+ * The speed at which the packets are sent, SCODE_100-_400; read/write.
+ * SCODE_800 is only available in Dice III.
+ */
+#define TX_SPEED 0x014
+
+/*
+ * Names of all audio channels; read-only. Quadlets are byte-swapped. Names
+ * are separated with one backslash, the list is terminated with two
+ * backslashes.
+ */
+#define TX_NAMES 0x018
+#define TX_NAMES_SIZE 256
+
+/*
+ * Audio IEC60958 capabilities; read-only. Bitmask with one bit per audio
+ * channel.
+ */
+#define TX_AC3_CAPABILITIES 0x118
+
+/*
+ * Send audio data with IEC60958 label; read/write. Bitmask with one bit per
+ * audio channel. This register can be changed even while the stream is
+ * running.
+ */
+#define TX_AC3_ENABLE 0x11c
+
+/*
+ * Playback stream settings. This section includes the number/size registers
+ * and the registers of all streams.
+ */
+
+/*
+ * The number of supported playback streams; read-only.
+ */
+#define RX_NUMBER 0x000
+
+/*
+ * The size of one stream's register block, in quadlets; read-only. The
+ * registers of the first stream follow immediately afterwards; the registers
+ * of the following streams are offset by this register's value.
+ */
+#define RX_SIZE 0x004
+
+/*
+ * The isochronous channel number on which packets are received, or -1 if the
+ * stream is not to be used; read/write.
+ */
+#define RX_ISOCHRONOUS 0x008
+
+/*
+ * Index of first quadlet to be interpreted; read/write. If > 0, that many
+ * quadlets at the beginning of each data block will be ignored, and all the
+ * audio and MIDI quadlets will follow.
+ */
+#define RX_SEQ_START 0x00c
+
+/*
+ * The number of audio channels; read-only. There will be one quadlet per
+ * channel.
+ */
+#define RX_NUMBER_AUDIO 0x010
+
+/*
+ * The number of MIDI ports, 0-8; read-only. If > 0, there will be one
+ * additional quadlet in each data block, following the audio quadlets.
+ */
+#define RX_NUMBER_MIDI 0x014
+
+/*
+ * Names of all audio channels; read-only. Quadlets are byte-swapped. Names
+ * are separated with one backslash, the list is terminated with two
+ * backslashes.
+ */
+#define RX_NAMES 0x018
+#define RX_NAMES_SIZE 256
+
+/*
+ * Audio IEC60958 capabilities; read-only. Bitmask with one bit per audio
+ * channel.
+ */
+#define RX_AC3_CAPABILITIES 0x118
+
+/*
+ * Receive audio data with IEC60958 label; read/write. Bitmask with one bit
+ * per audio channel. This register can be changed even while the stream is
+ * running.
+ */
+#define RX_AC3_ENABLE 0x11c
+
+/*
+ * Extended synchronization information.
+ * This section can be read completely with a block read request.
+ */
+
+/*
+ * Current clock source; read-only.
+ */
+#define EXT_SYNC_CLOCK_SOURCE 0x000
+
+/*
+ * Clock source is locked (boolean); read-only.
+ */
+#define EXT_SYNC_LOCKED 0x004
+
+/*
+ * Current sample rate (CLOCK_RATE_* >> CLOCK_RATE_SHIFT), _32000-_192000 or
+ * _NONE; read-only.
+ */
+#define EXT_SYNC_RATE 0x008
+
+/*
+ * ADAT user data bits; read-only.
+ */
+#define EXT_SYNC_ADAT_USER_DATA 0x00c
+/* The data bits, if available. */
+#define ADAT_USER_DATA_MASK 0x0f
+/* The data bits are not available. */
+#define ADAT_USER_DATA_NO_DATA 0x10
+
+#endif
diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c
new file mode 100644
index 000000000000..722bce379345
--- /dev/null
+++ b/sound/firewire/dice/dice-midi.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * dice_midi.c - a part of driver for Dice based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ */
+#include "dice.h"
+
+static int midi_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_dice *dice = substream->rmidi->private_data;
+ int err;
+
+ err = snd_dice_stream_lock_try(dice);
+ if (err < 0)
+ return err;
+
+ scoped_guard(mutex, &dice->mutex) {
+ err = snd_dice_stream_reserve_duplex(dice, 0, 0, 0);
+ if (err >= 0) {
+ ++dice->substreams_counter;
+ err = snd_dice_stream_start_duplex(dice);
+ if (err < 0)
+ --dice->substreams_counter;
+ }
+ }
+
+ if (err < 0)
+ snd_dice_stream_lock_release(dice);
+
+ return err;
+}
+
+static int midi_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_dice *dice = substream->rmidi->private_data;
+
+ scoped_guard(mutex, &dice->mutex) {
+ --dice->substreams_counter;
+ snd_dice_stream_stop_duplex(dice);
+ }
+
+ snd_dice_stream_lock_release(dice);
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_dice *dice = substrm->rmidi->private_data;
+
+ guard(spinlock_irqsave)(&dice->lock);
+
+ if (up)
+ amdtp_am824_midi_trigger(&dice->tx_stream[0],
+ substrm->number, substrm);
+ else
+ amdtp_am824_midi_trigger(&dice->tx_stream[0],
+ substrm->number, NULL);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_dice *dice = substrm->rmidi->private_data;
+
+ guard(spinlock_irqsave)(&dice->lock);
+
+ if (up)
+ amdtp_am824_midi_trigger(&dice->rx_stream[0],
+ substrm->number, substrm);
+ else
+ amdtp_am824_midi_trigger(&dice->rx_stream[0],
+ substrm->number, NULL);
+}
+
+static void set_midi_substream_names(struct snd_dice *dice,
+ struct snd_rawmidi_str *str)
+{
+ struct snd_rawmidi_substream *subs;
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ scnprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d", dice->card->shortname, subs->number + 1);
+ }
+}
+
+int snd_dice_create_midi(struct snd_dice *dice)
+{
+ static const struct snd_rawmidi_ops capture_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_capture_trigger,
+ };
+ static const struct snd_rawmidi_ops playback_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_playback_trigger,
+ };
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *str;
+ unsigned int midi_in_ports, midi_out_ports;
+ int i;
+ int err;
+
+ midi_in_ports = 0;
+ midi_out_ports = 0;
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ midi_in_ports = max(midi_in_ports, dice->tx_midi_ports[i]);
+ midi_out_ports = max(midi_out_ports, dice->rx_midi_ports[i]);
+ }
+
+ if (midi_in_ports + midi_out_ports == 0)
+ return 0;
+
+ /* create midi ports */
+ err = snd_rawmidi_new(dice->card, dice->card->driver, 0,
+ midi_out_ports, midi_in_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", dice->card->shortname);
+ rmidi->private_data = dice;
+
+ if (midi_in_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &capture_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+ set_midi_substream_names(dice, str);
+ }
+
+ if (midi_out_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &playback_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+ set_midi_substream_names(dice, str);
+ }
+
+ if ((midi_out_ports > 0) && (midi_in_ports > 0))
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-mytek.c b/sound/firewire/dice/dice-mytek.c
new file mode 100644
index 000000000000..eb7d5492d10b
--- /dev/null
+++ b/sound/firewire/dice/dice-mytek.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dice-mytek.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) 2018 Melvin Vermeeren
+ */
+
+#include "dice.h"
+
+struct dice_mytek_spec {
+ unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+ unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+};
+
+static const struct dice_mytek_spec stereo_192_dsd_dac = {
+ /* AES, TOSLINK, SPDIF, ADAT inputs on device */
+ .tx_pcm_chs = {{8, 8, 8}, {0, 0, 0} },
+ /* PCM 44.1-192, native DSD64/DSD128 to device */
+ .rx_pcm_chs = {{4, 4, 4}, {0, 0, 0} }
+};
+
+/*
+ * Mytek has a few other firewire-capable devices, though newer models appear
+ * to lack the port more often than not. As I don't have access to any of them
+ * they are missing here. An example is the Mytek 8x192 ADDA, which is DICE.
+ */
+
+int snd_dice_detect_mytek_formats(struct snd_dice *dice)
+{
+ int i;
+ const struct dice_mytek_spec *dev;
+
+ dev = &stereo_192_dsd_dac;
+
+ memcpy(dice->tx_pcm_chs, dev->tx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
+ memcpy(dice->rx_pcm_chs, dev->rx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
+
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ dice->tx_midi_ports[i] = 0;
+ dice->rx_midi_ports[i] = 0;
+ }
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c
new file mode 100644
index 000000000000..d5319cd2cc6f
--- /dev/null
+++ b/sound/firewire/dice/dice-pcm.c
@@ -0,0 +1,449 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * dice_pcm.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include "dice.h"
+
+static int dice_rate_constraint(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_pcm_substream *substream = rule->private;
+ struct snd_dice *dice = substream->private_data;
+ unsigned int index = substream->pcm->device;
+
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval rates = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int *pcm_channels;
+ enum snd_dice_rate_mode mode;
+ unsigned int i, rate;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ pcm_channels = dice->tx_pcm_chs[index];
+ else
+ pcm_channels = dice->rx_pcm_chs[index];
+
+ for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
+ rate = snd_dice_rates[i];
+ if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
+ continue;
+
+ if (!snd_interval_test(c, pcm_channels[mode]))
+ continue;
+
+ rates.min = min(rates.min, rate);
+ rates.max = max(rates.max, rate);
+ }
+
+ return snd_interval_refine(r, &rates);
+}
+
+static int dice_channels_constraint(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_pcm_substream *substream = rule->private;
+ struct snd_dice *dice = substream->private_data;
+ unsigned int index = substream->pcm->device;
+
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval channels = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int *pcm_channels;
+ enum snd_dice_rate_mode mode;
+ unsigned int i, rate;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ pcm_channels = dice->tx_pcm_chs[index];
+ else
+ pcm_channels = dice->rx_pcm_chs[index];
+
+ for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
+ rate = snd_dice_rates[i];
+ if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
+ continue;
+
+ if (!snd_interval_test(r, rate))
+ continue;
+
+ channels.min = min(channels.min, pcm_channels[mode]);
+ channels.max = max(channels.max, pcm_channels[mode]);
+ }
+
+ return snd_interval_refine(c, &channels);
+}
+
+static int limit_channels_and_rates(struct snd_dice *dice,
+ struct snd_pcm_runtime *runtime,
+ enum amdtp_stream_direction dir,
+ unsigned int index)
+{
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ unsigned int *pcm_channels;
+ unsigned int i;
+
+ if (dir == AMDTP_IN_STREAM)
+ pcm_channels = dice->tx_pcm_chs[index];
+ else
+ pcm_channels = dice->rx_pcm_chs[index];
+
+ hw->channels_min = UINT_MAX;
+ hw->channels_max = 0;
+
+ for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
+ enum snd_dice_rate_mode mode;
+ unsigned int rate, channels;
+
+ rate = snd_dice_rates[i];
+ if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
+ continue;
+ hw->rates |= snd_pcm_rate_to_rate_bit(rate);
+
+ channels = pcm_channels[mode];
+ if (channels == 0)
+ continue;
+ hw->channels_min = min(hw->channels_min, channels);
+ hw->channels_max = max(hw->channels_max, channels);
+ }
+
+ snd_pcm_limit_hw_rates(runtime);
+
+ return 0;
+}
+
+static int init_hw_info(struct snd_dice *dice,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ unsigned int index = substream->pcm->device;
+ enum amdtp_stream_direction dir;
+ struct amdtp_stream *stream;
+ int err;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ hw->formats = AM824_IN_PCM_FORMAT_BITS;
+ dir = AMDTP_IN_STREAM;
+ stream = &dice->tx_stream[index];
+ } else {
+ hw->formats = AM824_OUT_PCM_FORMAT_BITS;
+ dir = AMDTP_OUT_STREAM;
+ stream = &dice->rx_stream[index];
+ }
+
+ err = limit_channels_and_rates(dice, substream->runtime, dir,
+ index);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ dice_rate_constraint, substream,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ dice_channels_constraint, substream,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ return err;
+
+ return amdtp_am824_add_pcm_hw_constraints(stream, runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+ struct amdtp_domain *d = &dice->domain;
+ unsigned int source;
+ bool internal;
+ int err;
+
+ err = snd_dice_stream_lock_try(dice);
+ if (err < 0)
+ return err;
+
+ err = init_hw_info(dice, substream);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_dice_transaction_get_clock_source(dice, &source);
+ if (err < 0)
+ goto err_locked;
+ switch (source) {
+ case CLOCK_SOURCE_AES1:
+ case CLOCK_SOURCE_AES2:
+ case CLOCK_SOURCE_AES3:
+ case CLOCK_SOURCE_AES4:
+ case CLOCK_SOURCE_AES_ANY:
+ case CLOCK_SOURCE_ADAT:
+ case CLOCK_SOURCE_TDIF:
+ case CLOCK_SOURCE_WC:
+ internal = false;
+ break;
+ default:
+ internal = true;
+ break;
+ }
+
+ scoped_guard(mutex, &dice->mutex) {
+ // When source of clock is not internal or any stream is reserved for
+ // transmission of PCM frames, the available sampling rate is limited
+ // at current one.
+ if (!internal ||
+ (dice->substreams_counter > 0 && d->events_per_period > 0)) {
+ unsigned int frames_per_period = d->events_per_period;
+ unsigned int frames_per_buffer = d->events_per_buffer;
+ unsigned int rate;
+
+ err = snd_dice_transaction_get_rate(dice, &rate);
+ if (err < 0)
+ goto err_locked;
+
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+
+ if (frames_per_period > 0) {
+ // For double_pcm_frame quirk.
+ if (rate > 96000 && !dice->disable_double_pcm_frames) {
+ frames_per_period *= 2;
+ frames_per_buffer *= 2;
+ }
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ frames_per_period, frames_per_period);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ frames_per_buffer, frames_per_buffer);
+ if (err < 0)
+ goto err_locked;
+ }
+ }
+ }
+
+ snd_pcm_set_sync(substream);
+
+ return 0;
+err_locked:
+ snd_dice_stream_lock_release(dice);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+
+ snd_dice_stream_lock_release(dice);
+
+ return 0;
+}
+
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_dice *dice = substream->private_data;
+ int err = 0;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ unsigned int rate = params_rate(hw_params);
+ unsigned int events_per_period = params_period_size(hw_params);
+ unsigned int events_per_buffer = params_buffer_size(hw_params);
+
+ guard(mutex)(&dice->mutex);
+ // For double_pcm_frame quirk.
+ if (rate > 96000 && !dice->disable_double_pcm_frames) {
+ events_per_period /= 2;
+ events_per_buffer /= 2;
+ }
+ err = snd_dice_stream_reserve_duplex(dice, rate,
+ events_per_period, events_per_buffer);
+ if (err >= 0)
+ ++dice->substreams_counter;
+ }
+
+ return err;
+}
+
+static int pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+
+ guard(mutex)(&dice->mutex);
+
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --dice->substreams_counter;
+
+ snd_dice_stream_stop_duplex(dice);
+
+ return 0;
+}
+
+static int capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
+ int err;
+
+ scoped_guard(mutex, &dice->mutex) {
+ err = snd_dice_stream_start_duplex(dice);
+ }
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(stream);
+
+ return 0;
+}
+static int playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
+ int err;
+
+ scoped_guard(mutex, &dice->mutex) {
+ err = snd_dice_stream_start_duplex(dice);
+ }
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(stream);
+
+ return err;
+}
+
+static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
+
+ return amdtp_domain_stream_pcm_pointer(&dice->domain, stream);
+}
+static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
+
+ return amdtp_domain_stream_pcm_pointer(&dice->domain, stream);
+}
+
+static int capture_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
+
+ return amdtp_domain_stream_pcm_ack(&dice->domain, stream);
+}
+
+static int playback_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
+
+ return amdtp_domain_stream_pcm_ack(&dice->domain, stream);
+}
+
+int snd_dice_create_pcm(struct snd_dice *dice)
+{
+ static const struct snd_pcm_ops capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = capture_prepare,
+ .trigger = capture_trigger,
+ .pointer = capture_pointer,
+ .ack = capture_ack,
+ };
+ static const struct snd_pcm_ops playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = playback_prepare,
+ .trigger = playback_trigger,
+ .pointer = playback_pointer,
+ .ack = playback_ack,
+ };
+ struct snd_pcm *pcm;
+ unsigned int capture, playback;
+ int i, j;
+ int err;
+
+ for (i = 0; i < MAX_STREAMS; i++) {
+ capture = playback = 0;
+ for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j) {
+ if (dice->tx_pcm_chs[i][j] > 0)
+ capture = 1;
+ if (dice->rx_pcm_chs[i][j] > 0)
+ playback = 1;
+ }
+
+ err = snd_pcm_new(dice->card, "DICE", i, playback, capture,
+ &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = dice;
+ pcm->nonatomic = true;
+ strscpy(pcm->name, dice->card->shortname);
+
+ if (capture > 0)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &capture_ops);
+
+ if (playback > 0)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &playback_ops);
+
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
+ NULL, 0, 0);
+ }
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-presonus.c b/sound/firewire/dice/dice-presonus.c
new file mode 100644
index 000000000000..967cc3119a64
--- /dev/null
+++ b/sound/firewire/dice/dice-presonus.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+// dice-presonus.c - a part of driver for DICE based devices
+//
+// Copyright (c) 2019 Takashi Sakamoto
+
+#include "dice.h"
+
+struct dice_presonus_spec {
+ unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+ unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+ bool has_midi;
+};
+
+static const struct dice_presonus_spec dice_presonus_firesutio = {
+ .tx_pcm_chs = {{16, 16, 0}, {10, 2, 0} },
+ .rx_pcm_chs = {{16, 16, 0}, {10, 2, 0} },
+ .has_midi = true,
+};
+
+int snd_dice_detect_presonus_formats(struct snd_dice *dice)
+{
+ static const struct {
+ u32 model_id;
+ const struct dice_presonus_spec *spec;
+ } *entry, entries[] = {
+ {0x000008, &dice_presonus_firesutio},
+ };
+ struct fw_csr_iterator it;
+ int key, val, model_id;
+ int i;
+
+ model_id = 0;
+ fw_csr_iterator_init(&it, dice->unit->directory);
+ while (fw_csr_iterator_next(&it, &key, &val)) {
+ if (key == CSR_MODEL) {
+ model_id = val;
+ break;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(entries); ++i) {
+ entry = entries + i;
+ if (entry->model_id == model_id)
+ break;
+ }
+ if (i == ARRAY_SIZE(entries))
+ return -ENODEV;
+
+ memcpy(dice->tx_pcm_chs, entry->spec->tx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
+ memcpy(dice->rx_pcm_chs, entry->spec->rx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
+
+ if (entry->spec->has_midi) {
+ dice->tx_midi_ports[0] = 1;
+ dice->rx_midi_ports[0] = 1;
+ }
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-proc.c b/sound/firewire/dice/dice-proc.c
new file mode 100644
index 000000000000..db0a03123c4f
--- /dev/null
+++ b/sound/firewire/dice/dice-proc.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * dice_proc.c - a part of driver for Dice based devices
+ *
+ * Copyright (c) Clemens Ladisch
+ * Copyright (c) 2014 Takashi Sakamoto
+ */
+
+#include "dice.h"
+
+static int dice_proc_read_mem(struct snd_dice *dice, void *buffer,
+ unsigned int offset_q, unsigned int quadlets)
+{
+ unsigned int i;
+ int err;
+
+ err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+ DICE_PRIVATE_SPACE + 4 * offset_q,
+ buffer, 4 * quadlets, 0);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < quadlets; ++i)
+ be32_to_cpus(&((u32 *)buffer)[i]);
+
+ return 0;
+}
+
+static const char *str_from_array(const char *const strs[], unsigned int count,
+ unsigned int i)
+{
+ if (i < count)
+ return strs[i];
+
+ return "(unknown)";
+}
+
+static void dice_proc_fixup_string(char *s, unsigned int size)
+{
+ unsigned int i;
+
+ for (i = 0; i < size; i += 4)
+ cpu_to_le32s((u32 *)(s + i));
+
+ for (i = 0; i < size - 2; ++i) {
+ if (s[i] == '\0')
+ return;
+ if (s[i] == '\\' && s[i + 1] == '\\') {
+ s[i + 2] = '\0';
+ return;
+ }
+ }
+ s[size - 1] = '\0';
+}
+
+static void dice_proc_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ static const char *const section_names[5] = {
+ "global", "tx", "rx", "ext_sync", "unused2"
+ };
+ static const char *const clock_sources[] = {
+ "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif",
+ "wc", "arx1", "arx2", "arx3", "arx4", "internal"
+ };
+ static const char *const rates[] = {
+ "32000", "44100", "48000", "88200", "96000", "176400", "192000",
+ "any low", "any mid", "any high", "none"
+ };
+ struct snd_dice *dice = entry->private_data;
+ u32 sections[ARRAY_SIZE(section_names) * 2];
+ struct {
+ u32 number;
+ u32 size;
+ } tx_rx_header;
+ union {
+ struct {
+ u32 owner_hi, owner_lo;
+ u32 notification;
+ char nick_name[NICK_NAME_SIZE];
+ u32 clock_select;
+ u32 enable;
+ u32 status;
+ u32 extended_status;
+ u32 sample_rate;
+ u32 version;
+ u32 clock_caps;
+ char clock_source_names[CLOCK_SOURCE_NAMES_SIZE];
+ } global;
+ struct {
+ u32 iso;
+ u32 number_audio;
+ u32 number_midi;
+ u32 speed;
+ char names[TX_NAMES_SIZE];
+ u32 ac3_caps;
+ u32 ac3_enable;
+ } tx;
+ struct {
+ u32 iso;
+ u32 seq_start;
+ u32 number_audio;
+ u32 number_midi;
+ char names[RX_NAMES_SIZE];
+ u32 ac3_caps;
+ u32 ac3_enable;
+ } rx;
+ struct {
+ u32 clock_source;
+ u32 locked;
+ u32 rate;
+ u32 adat_user_data;
+ } ext_sync;
+ } buf;
+ unsigned int quadlets, stream, i;
+
+ if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0)
+ return;
+ snd_iprintf(buffer, "sections:\n");
+ for (i = 0; i < ARRAY_SIZE(section_names); ++i)
+ snd_iprintf(buffer, " %s: offset %u, size %u\n",
+ section_names[i],
+ sections[i * 2], sections[i * 2 + 1]);
+
+ quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4);
+ if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0)
+ return;
+ snd_iprintf(buffer, "global:\n");
+ snd_iprintf(buffer, " owner: %04x:%04x%08x\n",
+ buf.global.owner_hi >> 16,
+ buf.global.owner_hi & 0xffff, buf.global.owner_lo);
+ snd_iprintf(buffer, " notification: %08x\n", buf.global.notification);
+ dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE);
+ snd_iprintf(buffer, " nick name: %s\n", buf.global.nick_name);
+ snd_iprintf(buffer, " clock select: %s %s\n",
+ str_from_array(clock_sources, ARRAY_SIZE(clock_sources),
+ buf.global.clock_select & CLOCK_SOURCE_MASK),
+ str_from_array(rates, ARRAY_SIZE(rates),
+ (buf.global.clock_select & CLOCK_RATE_MASK)
+ >> CLOCK_RATE_SHIFT));
+ snd_iprintf(buffer, " enable: %u\n", buf.global.enable);
+ snd_iprintf(buffer, " status: %slocked %s\n",
+ buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un",
+ str_from_array(rates, ARRAY_SIZE(rates),
+ (buf.global.status &
+ STATUS_NOMINAL_RATE_MASK)
+ >> CLOCK_RATE_SHIFT));
+ snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status);
+ snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate);
+ if (quadlets >= 90) {
+ snd_iprintf(buffer, " version: %u.%u.%u.%u\n",
+ (buf.global.version >> 24) & 0xff,
+ (buf.global.version >> 16) & 0xff,
+ (buf.global.version >> 8) & 0xff,
+ (buf.global.version >> 0) & 0xff);
+ snd_iprintf(buffer, " clock caps:");
+ for (i = 0; i <= 6; ++i)
+ if (buf.global.clock_caps & (1 << i))
+ snd_iprintf(buffer, " %s", rates[i]);
+ for (i = 0; i <= 12; ++i)
+ if (buf.global.clock_caps & (1 << (16 + i)))
+ snd_iprintf(buffer, " %s", clock_sources[i]);
+ snd_iprintf(buffer, "\n");
+ dice_proc_fixup_string(buf.global.clock_source_names,
+ CLOCK_SOURCE_NAMES_SIZE);
+ snd_iprintf(buffer, " clock source names: %s\n",
+ buf.global.clock_source_names);
+ }
+
+ if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0)
+ return;
+ quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx) / 4);
+ for (stream = 0; stream < tx_rx_header.number; ++stream) {
+ if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 +
+ stream * tx_rx_header.size,
+ quadlets) < 0)
+ break;
+ snd_iprintf(buffer, "tx %u:\n", stream);
+ snd_iprintf(buffer, " iso channel: %d\n", (int)buf.tx.iso);
+ snd_iprintf(buffer, " audio channels: %u\n",
+ buf.tx.number_audio);
+ snd_iprintf(buffer, " midi ports: %u\n", buf.tx.number_midi);
+ snd_iprintf(buffer, " speed: S%u\n", 100u << buf.tx.speed);
+ if (quadlets >= 68) {
+ dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE);
+ snd_iprintf(buffer, " names: %s\n", buf.tx.names);
+ }
+ if (quadlets >= 70) {
+ snd_iprintf(buffer, " ac3 caps: %08x\n",
+ buf.tx.ac3_caps);
+ snd_iprintf(buffer, " ac3 enable: %08x\n",
+ buf.tx.ac3_enable);
+ }
+ }
+
+ if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0)
+ return;
+ quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx) / 4);
+ for (stream = 0; stream < tx_rx_header.number; ++stream) {
+ if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 +
+ stream * tx_rx_header.size,
+ quadlets) < 0)
+ break;
+ snd_iprintf(buffer, "rx %u:\n", stream);
+ snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso);
+ snd_iprintf(buffer, " sequence start: %u\n", buf.rx.seq_start);
+ snd_iprintf(buffer, " audio channels: %u\n",
+ buf.rx.number_audio);
+ snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi);
+ if (quadlets >= 68) {
+ dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE);
+ snd_iprintf(buffer, " names: %s\n", buf.rx.names);
+ }
+ if (quadlets >= 70) {
+ snd_iprintf(buffer, " ac3 caps: %08x\n",
+ buf.rx.ac3_caps);
+ snd_iprintf(buffer, " ac3 enable: %08x\n",
+ buf.rx.ac3_enable);
+ }
+ }
+
+ quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4);
+ if (quadlets >= 4) {
+ if (dice_proc_read_mem(dice, &buf.ext_sync,
+ sections[6], 4) < 0)
+ return;
+ snd_iprintf(buffer, "ext status:\n");
+ snd_iprintf(buffer, " clock source: %s\n",
+ str_from_array(clock_sources,
+ ARRAY_SIZE(clock_sources),
+ buf.ext_sync.clock_source));
+ snd_iprintf(buffer, " locked: %u\n", buf.ext_sync.locked);
+ snd_iprintf(buffer, " rate: %s\n",
+ str_from_array(rates, ARRAY_SIZE(rates),
+ buf.ext_sync.rate));
+ snd_iprintf(buffer, " adat user data: ");
+ if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA)
+ snd_iprintf(buffer, "-\n");
+ else
+ snd_iprintf(buffer, "%x\n",
+ buf.ext_sync.adat_user_data);
+ }
+}
+
+static void dice_proc_read_formation(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ static const char *const rate_labels[] = {
+ [SND_DICE_RATE_MODE_LOW] = "low",
+ [SND_DICE_RATE_MODE_MIDDLE] = "middle",
+ [SND_DICE_RATE_MODE_HIGH] = "high",
+ };
+ struct snd_dice *dice = entry->private_data;
+ int i, j;
+
+ snd_iprintf(buffer, "Output stream from unit:\n");
+ for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i)
+ snd_iprintf(buffer, "\t%s", rate_labels[i]);
+ snd_iprintf(buffer, "\tMIDI\n");
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ snd_iprintf(buffer, "Tx %u:", i);
+ for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j)
+ snd_iprintf(buffer, "\t%u", dice->tx_pcm_chs[i][j]);
+ snd_iprintf(buffer, "\t%u\n", dice->tx_midi_ports[i]);
+ }
+
+ snd_iprintf(buffer, "Input stream to unit:\n");
+ for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i)
+ snd_iprintf(buffer, "\t%s", rate_labels[i]);
+ snd_iprintf(buffer, "\n");
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ snd_iprintf(buffer, "Rx %u:", i);
+ for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j)
+ snd_iprintf(buffer, "\t%u", dice->rx_pcm_chs[i][j]);
+ snd_iprintf(buffer, "\t%u\n", dice->rx_midi_ports[i]);
+ }
+}
+
+static void add_node(struct snd_dice *dice, struct snd_info_entry *root,
+ const char *name,
+ void (*op)(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(dice->card, name, root);
+ if (entry)
+ snd_info_set_text_ops(entry, dice, op);
+}
+
+void snd_dice_create_proc(struct snd_dice *dice)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(dice->card, "firewire",
+ dice->card->proc_root);
+ if (!root)
+ return;
+ root->mode = S_IFDIR | 0555;
+
+ add_node(dice, root, "dice", dice_proc_read);
+ add_node(dice, root, "formation", dice_proc_read_formation);
+}
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
new file mode 100644
index 000000000000..d5ffe7c82993
--- /dev/null
+++ b/sound/firewire/dice/dice-stream.c
@@ -0,0 +1,699 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * dice_stream.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include "dice.h"
+
+#define READY_TIMEOUT_MS 200
+#define NOTIFICATION_TIMEOUT_MS 100
+
+struct reg_params {
+ unsigned int count;
+ unsigned int size;
+};
+
+const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
+ /* mode 0 */
+ [0] = 32000,
+ [1] = 44100,
+ [2] = 48000,
+ /* mode 1 */
+ [3] = 88200,
+ [4] = 96000,
+ /* mode 2 */
+ [5] = 176400,
+ [6] = 192000,
+};
+
+int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
+ enum snd_dice_rate_mode *mode)
+{
+ /* Corresponding to each entry in snd_dice_rates. */
+ static const enum snd_dice_rate_mode modes[] = {
+ [0] = SND_DICE_RATE_MODE_LOW,
+ [1] = SND_DICE_RATE_MODE_LOW,
+ [2] = SND_DICE_RATE_MODE_LOW,
+ [3] = SND_DICE_RATE_MODE_MIDDLE,
+ [4] = SND_DICE_RATE_MODE_MIDDLE,
+ [5] = SND_DICE_RATE_MODE_HIGH,
+ [6] = SND_DICE_RATE_MODE_HIGH,
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
+ if (!(dice->clock_caps & BIT(i)))
+ continue;
+ if (snd_dice_rates[i] != rate)
+ continue;
+
+ *mode = modes[i];
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int select_clock(struct snd_dice *dice, unsigned int rate)
+{
+ __be32 reg, new;
+ u32 data;
+ int i;
+ int err;
+
+ err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
+ &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+
+ data = be32_to_cpu(reg);
+
+ data &= ~CLOCK_RATE_MASK;
+ for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
+ if (snd_dice_rates[i] == rate)
+ break;
+ }
+ if (i == ARRAY_SIZE(snd_dice_rates))
+ return -EINVAL;
+ data |= i << CLOCK_RATE_SHIFT;
+
+ if (completion_done(&dice->clock_accepted))
+ reinit_completion(&dice->clock_accepted);
+
+ new = cpu_to_be32(data);
+ err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
+ &new, sizeof(new));
+ if (err < 0)
+ return err;
+
+ if (wait_for_completion_timeout(&dice->clock_accepted,
+ msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
+ if (reg != new)
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int get_register_params(struct snd_dice *dice,
+ struct reg_params *tx_params,
+ struct reg_params *rx_params)
+{
+ __be32 reg[2];
+ int err;
+
+ err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ tx_params->count =
+ min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
+ tx_params->size = be32_to_cpu(reg[1]) * 4;
+
+ err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ rx_params->count =
+ min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
+ rx_params->size = be32_to_cpu(reg[1]) * 4;
+
+ return 0;
+}
+
+static void release_resources(struct snd_dice *dice)
+{
+ int i;
+
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ fw_iso_resources_free(&dice->tx_resources[i]);
+ fw_iso_resources_free(&dice->rx_resources[i]);
+ }
+}
+
+static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
+ struct reg_params *params)
+{
+ __be32 reg;
+ unsigned int i;
+
+ for (i = 0; i < params->count; i++) {
+ reg = cpu_to_be32((u32)-1);
+ if (dir == AMDTP_IN_STREAM) {
+ snd_dice_transaction_write_tx(dice,
+ params->size * i + TX_ISOCHRONOUS,
+ &reg, sizeof(reg));
+ } else {
+ snd_dice_transaction_write_rx(dice,
+ params->size * i + RX_ISOCHRONOUS,
+ &reg, sizeof(reg));
+ }
+ }
+}
+
+static int keep_resources(struct snd_dice *dice, struct amdtp_stream *stream,
+ struct fw_iso_resources *resources, unsigned int rate,
+ unsigned int pcm_chs, unsigned int midi_ports)
+{
+ bool double_pcm_frames;
+ unsigned int i;
+ int err;
+
+ // At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
+ // one data block of AMDTP packet. Thus sampling transfer frequency is
+ // a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
+ // transferred on AMDTP packets at 96 kHz. Two successive samples of a
+ // channel are stored consecutively in the packet. This quirk is called
+ // as 'Dual Wire'.
+ // For this quirk, blocking mode is required and PCM buffer size should
+ // be aligned to SYT_INTERVAL.
+ double_pcm_frames = (rate > 96000 && !dice->disable_double_pcm_frames);
+ if (double_pcm_frames) {
+ rate /= 2;
+ pcm_chs *= 2;
+ }
+
+ err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports,
+ double_pcm_frames);
+ if (err < 0)
+ return err;
+
+ if (double_pcm_frames) {
+ pcm_chs /= 2;
+
+ for (i = 0; i < pcm_chs; i++) {
+ amdtp_am824_set_pcm_position(stream, i, i * 2);
+ amdtp_am824_set_pcm_position(stream, i + pcm_chs,
+ i * 2 + 1);
+ }
+ }
+
+ return fw_iso_resources_allocate(resources,
+ amdtp_stream_get_max_payload(stream),
+ fw_parent_device(dice->unit)->max_speed);
+}
+
+static int keep_dual_resources(struct snd_dice *dice, unsigned int rate,
+ enum amdtp_stream_direction dir,
+ struct reg_params *params)
+{
+ enum snd_dice_rate_mode mode;
+ int i;
+ int err;
+
+ err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < params->count; ++i) {
+ __be32 reg[2];
+ struct amdtp_stream *stream;
+ struct fw_iso_resources *resources;
+ unsigned int pcm_cache;
+ unsigned int pcm_chs;
+ unsigned int midi_ports;
+
+ if (dir == AMDTP_IN_STREAM) {
+ stream = &dice->tx_stream[i];
+ resources = &dice->tx_resources[i];
+
+ pcm_cache = dice->tx_pcm_chs[i][mode];
+ err = snd_dice_transaction_read_tx(dice,
+ params->size * i + TX_NUMBER_AUDIO,
+ reg, sizeof(reg));
+ } else {
+ stream = &dice->rx_stream[i];
+ resources = &dice->rx_resources[i];
+
+ pcm_cache = dice->rx_pcm_chs[i][mode];
+ err = snd_dice_transaction_read_rx(dice,
+ params->size * i + RX_NUMBER_AUDIO,
+ reg, sizeof(reg));
+ }
+ if (err < 0)
+ return err;
+ pcm_chs = be32_to_cpu(reg[0]);
+ midi_ports = be32_to_cpu(reg[1]);
+
+ // These are important for developer of this driver.
+ if (pcm_chs != pcm_cache) {
+ dev_info(&dice->unit->device,
+ "cache mismatch: pcm: %u:%u, midi: %u\n",
+ pcm_chs, pcm_cache, midi_ports);
+ return -EPROTO;
+ }
+
+ err = keep_resources(dice, stream, resources, rate, pcm_chs,
+ midi_ports);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void finish_session(struct snd_dice *dice, struct reg_params *tx_params,
+ struct reg_params *rx_params)
+{
+ stop_streams(dice, AMDTP_IN_STREAM, tx_params);
+ stop_streams(dice, AMDTP_OUT_STREAM, rx_params);
+
+ snd_dice_transaction_clear_enable(dice);
+}
+
+int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate,
+ unsigned int events_per_period,
+ unsigned int events_per_buffer)
+{
+ unsigned int curr_rate;
+ int err;
+
+ // Check sampling transmission frequency.
+ err = snd_dice_transaction_get_rate(dice, &curr_rate);
+ if (err < 0)
+ return err;
+ if (rate == 0)
+ rate = curr_rate;
+
+ if (dice->substreams_counter == 0 || curr_rate != rate) {
+ struct reg_params tx_params, rx_params;
+
+ amdtp_domain_stop(&dice->domain);
+
+ err = get_register_params(dice, &tx_params, &rx_params);
+ if (err < 0)
+ return err;
+ finish_session(dice, &tx_params, &rx_params);
+
+ release_resources(dice);
+
+ // Just after owning the unit (GLOBAL_OWNER), the unit can
+ // return invalid stream formats. Selecting clock parameters
+ // have an effect for the unit to refine it.
+ err = select_clock(dice, rate);
+ if (err < 0)
+ return err;
+
+ // After changing sampling transfer frequency, the value of
+ // register can be changed.
+ err = get_register_params(dice, &tx_params, &rx_params);
+ if (err < 0)
+ return err;
+
+ err = keep_dual_resources(dice, rate, AMDTP_IN_STREAM,
+ &tx_params);
+ if (err < 0)
+ goto error;
+
+ err = keep_dual_resources(dice, rate, AMDTP_OUT_STREAM,
+ &rx_params);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_domain_set_events_per_period(&dice->domain,
+ events_per_period, events_per_buffer);
+ if (err < 0)
+ goto error;
+ }
+
+ return 0;
+error:
+ release_resources(dice);
+ return err;
+}
+
+static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
+ unsigned int rate, struct reg_params *params)
+{
+ unsigned int max_speed = fw_parent_device(dice->unit)->max_speed;
+ int i;
+ int err;
+
+ for (i = 0; i < params->count; i++) {
+ struct amdtp_stream *stream;
+ struct fw_iso_resources *resources;
+ __be32 reg;
+
+ if (dir == AMDTP_IN_STREAM) {
+ stream = dice->tx_stream + i;
+ resources = dice->tx_resources + i;
+ } else {
+ stream = dice->rx_stream + i;
+ resources = dice->rx_resources + i;
+ }
+
+ reg = cpu_to_be32(resources->channel);
+ if (dir == AMDTP_IN_STREAM) {
+ err = snd_dice_transaction_write_tx(dice,
+ params->size * i + TX_ISOCHRONOUS,
+ &reg, sizeof(reg));
+ } else {
+ err = snd_dice_transaction_write_rx(dice,
+ params->size * i + RX_ISOCHRONOUS,
+ &reg, sizeof(reg));
+ }
+ if (err < 0)
+ return err;
+
+ if (dir == AMDTP_IN_STREAM) {
+ reg = cpu_to_be32(max_speed);
+ err = snd_dice_transaction_write_tx(dice,
+ params->size * i + TX_SPEED,
+ &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ }
+
+ err = amdtp_domain_add_stream(&dice->domain, stream,
+ resources->channel, max_speed);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * MEMO: After this function, there're two states of streams:
+ * - None streams are running.
+ * - All streams are running.
+ */
+int snd_dice_stream_start_duplex(struct snd_dice *dice)
+{
+ unsigned int generation = dice->rx_resources[0].generation;
+ struct reg_params tx_params, rx_params;
+ unsigned int i;
+ unsigned int rate;
+ enum snd_dice_rate_mode mode;
+ int err;
+
+ if (dice->substreams_counter == 0)
+ return -EIO;
+
+ err = get_register_params(dice, &tx_params, &rx_params);
+ if (err < 0)
+ return err;
+
+ // Check error of packet streaming.
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ if (amdtp_streaming_error(&dice->tx_stream[i]) ||
+ amdtp_streaming_error(&dice->rx_stream[i])) {
+ amdtp_domain_stop(&dice->domain);
+ finish_session(dice, &tx_params, &rx_params);
+ break;
+ }
+ }
+
+ if (generation != fw_parent_device(dice->unit)->card->generation) {
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ if (i < tx_params.count)
+ fw_iso_resources_update(dice->tx_resources + i);
+ if (i < rx_params.count)
+ fw_iso_resources_update(dice->rx_resources + i);
+ }
+ }
+
+ // Check required streams are running or not.
+ err = snd_dice_transaction_get_rate(dice, &rate);
+ if (err < 0)
+ return err;
+ err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
+ if (err < 0)
+ return err;
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ if (dice->tx_pcm_chs[i][mode] > 0 &&
+ !amdtp_stream_running(&dice->tx_stream[i]))
+ break;
+ if (dice->rx_pcm_chs[i][mode] > 0 &&
+ !amdtp_stream_running(&dice->rx_stream[i]))
+ break;
+ }
+ if (i < MAX_STREAMS) {
+ // Start both streams.
+ err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
+ if (err < 0)
+ goto error;
+
+ err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
+ if (err < 0)
+ goto error;
+
+ err = snd_dice_transaction_set_enable(dice);
+ if (err < 0) {
+ dev_err(&dice->unit->device,
+ "fail to enable interface\n");
+ goto error;
+ }
+
+ // MEMO: The device immediately starts packet transmission when enabled. Some
+ // devices are strictly to generate any discontinuity in the sequence of tx packet
+ // when they receives invalid sequence of presentation time in CIP header. The
+ // sequence replay for media clock recovery can suppress the behaviour.
+ err = amdtp_domain_start(&dice->domain, 0, true, false);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_domain_wait_ready(&dice->domain, READY_TIMEOUT_MS)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ amdtp_domain_stop(&dice->domain);
+ finish_session(dice, &tx_params, &rx_params);
+ return err;
+}
+
+/*
+ * MEMO: After this function, there're two states of streams:
+ * - None streams are running.
+ * - All streams are running.
+ */
+void snd_dice_stream_stop_duplex(struct snd_dice *dice)
+{
+ struct reg_params tx_params, rx_params;
+
+ if (dice->substreams_counter == 0) {
+ if (get_register_params(dice, &tx_params, &rx_params) >= 0)
+ finish_session(dice, &tx_params, &rx_params);
+
+ amdtp_domain_stop(&dice->domain);
+ release_resources(dice);
+ }
+}
+
+static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir,
+ unsigned int index)
+{
+ struct amdtp_stream *stream;
+ struct fw_iso_resources *resources;
+ int err;
+
+ if (dir == AMDTP_IN_STREAM) {
+ stream = &dice->tx_stream[index];
+ resources = &dice->tx_resources[index];
+ } else {
+ stream = &dice->rx_stream[index];
+ resources = &dice->rx_resources[index];
+ }
+
+ err = fw_iso_resources_init(resources, dice->unit);
+ if (err < 0)
+ goto end;
+ resources->channels_mask = 0x00000000ffffffffuLL;
+
+ err = amdtp_am824_init(stream, dice->unit, dir, CIP_BLOCKING);
+ if (err < 0) {
+ amdtp_stream_destroy(stream);
+ fw_iso_resources_destroy(resources);
+ }
+end:
+ return err;
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+static void destroy_stream(struct snd_dice *dice,
+ enum amdtp_stream_direction dir,
+ unsigned int index)
+{
+ struct amdtp_stream *stream;
+ struct fw_iso_resources *resources;
+
+ if (dir == AMDTP_IN_STREAM) {
+ stream = &dice->tx_stream[index];
+ resources = &dice->tx_resources[index];
+ } else {
+ stream = &dice->rx_stream[index];
+ resources = &dice->rx_resources[index];
+ }
+
+ amdtp_stream_destroy(stream);
+ fw_iso_resources_destroy(resources);
+}
+
+int snd_dice_stream_init_duplex(struct snd_dice *dice)
+{
+ int i, err;
+
+ for (i = 0; i < MAX_STREAMS; i++) {
+ err = init_stream(dice, AMDTP_IN_STREAM, i);
+ if (err < 0) {
+ for (; i >= 0; i--)
+ destroy_stream(dice, AMDTP_IN_STREAM, i);
+ goto end;
+ }
+ }
+
+ for (i = 0; i < MAX_STREAMS; i++) {
+ err = init_stream(dice, AMDTP_OUT_STREAM, i);
+ if (err < 0) {
+ for (; i >= 0; i--)
+ destroy_stream(dice, AMDTP_OUT_STREAM, i);
+ for (i = 0; i < MAX_STREAMS; i++)
+ destroy_stream(dice, AMDTP_IN_STREAM, i);
+ goto end;
+ }
+ }
+
+ err = amdtp_domain_init(&dice->domain);
+ if (err < 0) {
+ for (i = 0; i < MAX_STREAMS; ++i) {
+ destroy_stream(dice, AMDTP_OUT_STREAM, i);
+ destroy_stream(dice, AMDTP_IN_STREAM, i);
+ }
+ }
+end:
+ return err;
+}
+
+void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAX_STREAMS; i++) {
+ destroy_stream(dice, AMDTP_IN_STREAM, i);
+ destroy_stream(dice, AMDTP_OUT_STREAM, i);
+ }
+
+ amdtp_domain_destroy(&dice->domain);
+}
+
+void snd_dice_stream_update_duplex(struct snd_dice *dice)
+{
+ struct reg_params tx_params, rx_params;
+
+ /*
+ * On a bus reset, the DICE firmware disables streaming and then goes
+ * off contemplating its own navel for hundreds of milliseconds before
+ * it can react to any of our attempts to reenable streaming. This
+ * means that we lose synchronization anyway, so we force our streams
+ * to stop so that the application can restart them in an orderly
+ * manner.
+ */
+ dice->global_enabled = false;
+
+ if (get_register_params(dice, &tx_params, &rx_params) == 0) {
+ amdtp_domain_stop(&dice->domain);
+
+ stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
+ stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
+ }
+}
+
+int snd_dice_stream_detect_current_formats(struct snd_dice *dice)
+{
+ unsigned int rate;
+ enum snd_dice_rate_mode mode;
+ __be32 reg[2];
+ struct reg_params tx_params, rx_params;
+ int i;
+ int err;
+
+ /* If extended protocol is available, detect detail spec. */
+ err = snd_dice_detect_extension_formats(dice);
+ if (err >= 0)
+ return err;
+
+ /*
+ * Available stream format is restricted at current mode of sampling
+ * clock.
+ */
+ err = snd_dice_transaction_get_rate(dice, &rate);
+ if (err < 0)
+ return err;
+
+ err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
+ if (err < 0)
+ return err;
+
+ /*
+ * Just after owning the unit (GLOBAL_OWNER), the unit can return
+ * invalid stream formats. Selecting clock parameters have an effect
+ * for the unit to refine it.
+ */
+ err = select_clock(dice, rate);
+ if (err < 0)
+ return err;
+
+ err = get_register_params(dice, &tx_params, &rx_params);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < tx_params.count; ++i) {
+ err = snd_dice_transaction_read_tx(dice,
+ tx_params.size * i + TX_NUMBER_AUDIO,
+ reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ dice->tx_pcm_chs[i][mode] = be32_to_cpu(reg[0]);
+ dice->tx_midi_ports[i] = max_t(unsigned int,
+ be32_to_cpu(reg[1]), dice->tx_midi_ports[i]);
+ }
+ for (i = 0; i < rx_params.count; ++i) {
+ err = snd_dice_transaction_read_rx(dice,
+ rx_params.size * i + RX_NUMBER_AUDIO,
+ reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ dice->rx_pcm_chs[i][mode] = be32_to_cpu(reg[0]);
+ dice->rx_midi_ports[i] = max_t(unsigned int,
+ be32_to_cpu(reg[1]), dice->rx_midi_ports[i]);
+ }
+
+ return 0;
+}
+
+static void dice_lock_changed(struct snd_dice *dice)
+{
+ dice->dev_lock_changed = true;
+ wake_up(&dice->hwdep_wait);
+}
+
+int snd_dice_stream_lock_try(struct snd_dice *dice)
+{
+ guard(spinlock_irq)(&dice->lock);
+
+ if (dice->dev_lock_count < 0)
+ return -EBUSY;
+
+ if (dice->dev_lock_count++ == 0)
+ dice_lock_changed(dice);
+ return 0;
+}
+
+void snd_dice_stream_lock_release(struct snd_dice *dice)
+{
+ guard(spinlock_irq)(&dice->lock);
+
+ if (WARN_ON(dice->dev_lock_count <= 0))
+ return;
+
+ if (--dice->dev_lock_count == 0)
+ dice_lock_changed(dice);
+}
diff --git a/sound/firewire/dice/dice-tcelectronic.c b/sound/firewire/dice/dice-tcelectronic.c
new file mode 100644
index 000000000000..43a3bcb15b3d
--- /dev/null
+++ b/sound/firewire/dice/dice-tcelectronic.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dice-tc_electronic.c - a part of driver for DICE based devices
+ *
+ * Copyright (c) 2018 Takashi Sakamoto
+ */
+
+#include "dice.h"
+
+struct dice_tc_spec {
+ unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+ unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+ bool has_midi;
+};
+
+static const struct dice_tc_spec desktop_konnekt6 = {
+ .tx_pcm_chs = {{6, 6, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{6, 6, 4}, {0, 0, 0} },
+ .has_midi = false,
+};
+
+static const struct dice_tc_spec impact_twin = {
+ .tx_pcm_chs = {{14, 10, 6}, {0, 0, 0} },
+ .rx_pcm_chs = {{14, 10, 6}, {0, 0, 0} },
+ .has_midi = true,
+};
+
+static const struct dice_tc_spec konnekt_8 = {
+ .tx_pcm_chs = {{4, 4, 3}, {0, 0, 0} },
+ .rx_pcm_chs = {{4, 4, 3}, {0, 0, 0} },
+ .has_midi = true,
+};
+
+static const struct dice_tc_spec konnekt_24d = {
+ .tx_pcm_chs = {{16, 16, 6}, {0, 0, 0} },
+ .rx_pcm_chs = {{16, 16, 6}, {0, 0, 0} },
+ .has_midi = true,
+};
+
+static const struct dice_tc_spec konnekt_live = {
+ .tx_pcm_chs = {{16, 16, 6}, {0, 0, 0} },
+ .rx_pcm_chs = {{16, 16, 6}, {0, 0, 0} },
+ .has_midi = true,
+};
+
+static const struct dice_tc_spec studio_konnekt_48 = {
+ .tx_pcm_chs = {{16, 16, 8}, {16, 16, 7} },
+ .rx_pcm_chs = {{16, 16, 8}, {14, 14, 7} },
+ .has_midi = true,
+};
+
+static const struct dice_tc_spec digital_konnekt_x32 = {
+ .tx_pcm_chs = {{16, 16, 4}, {0, 0, 0} },
+ .rx_pcm_chs = {{16, 16, 4}, {0, 0, 0} },
+ .has_midi = false,
+};
+
+int snd_dice_detect_tcelectronic_formats(struct snd_dice *dice)
+{
+ static const struct {
+ u32 model_id;
+ const struct dice_tc_spec *spec;
+ } *entry, entries[] = {
+ {0x00000020, &konnekt_24d},
+ {0x00000021, &konnekt_8},
+ {0x00000022, &studio_konnekt_48},
+ {0x00000023, &konnekt_live},
+ {0x00000024, &desktop_konnekt6},
+ {0x00000027, &impact_twin},
+ {0x00000030, &digital_konnekt_x32},
+ };
+ struct fw_csr_iterator it;
+ int key, val, model_id;
+ int i;
+
+ model_id = 0;
+ fw_csr_iterator_init(&it, dice->unit->directory);
+ while (fw_csr_iterator_next(&it, &key, &val)) {
+ if (key == CSR_MODEL) {
+ model_id = val;
+ break;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(entries); ++i) {
+ entry = entries + i;
+ if (entry->model_id == model_id)
+ break;
+ }
+ if (i == ARRAY_SIZE(entries))
+ return -ENODEV;
+
+ memcpy(dice->tx_pcm_chs, entry->spec->tx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
+ memcpy(dice->rx_pcm_chs, entry->spec->rx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
+
+ if (entry->spec->has_midi) {
+ dice->tx_midi_ports[0] = 1;
+ dice->rx_midi_ports[0] = 1;
+ }
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-teac.c b/sound/firewire/dice/dice-teac.c
new file mode 100644
index 000000000000..29febddfe3a5
--- /dev/null
+++ b/sound/firewire/dice/dice-teac.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+// dice-teac.c - a part of driver for DICE based devices
+//
+// Copyright (c) 2025 Takashi Sakamoto
+
+#include "dice.h"
+
+int snd_dice_detect_teac_formats(struct snd_dice *dice)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_dice_transaction_read_tx(dice, TX_NUMBER, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+
+ dice->tx_pcm_chs[0][SND_DICE_RATE_MODE_LOW] = 16;
+ dice->tx_pcm_chs[0][SND_DICE_RATE_MODE_MIDDLE] = 16;
+ dice->tx_midi_ports[0] = 1;
+
+ data = be32_to_cpu(reg);
+ if (data > 1) {
+ dice->tx_pcm_chs[1][SND_DICE_RATE_MODE_LOW] = 16;
+ dice->tx_pcm_chs[1][SND_DICE_RATE_MODE_MIDDLE] = 16;
+ }
+
+ err = snd_dice_transaction_read_rx(dice, RX_NUMBER, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+
+ dice->rx_pcm_chs[0][SND_DICE_RATE_MODE_LOW] = 16;
+ dice->rx_pcm_chs[0][SND_DICE_RATE_MODE_MIDDLE] = 16;
+ dice->rx_midi_ports[0] = 1;
+
+ data = be32_to_cpu(reg);
+ if (data > 1) {
+ dice->rx_pcm_chs[1][SND_DICE_RATE_MODE_LOW] = 16;
+ dice->rx_pcm_chs[1][SND_DICE_RATE_MODE_MIDDLE] = 16;
+ }
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c
new file mode 100644
index 000000000000..a3f7dfa990a4
--- /dev/null
+++ b/sound/firewire/dice/dice-transaction.c
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * dice_transaction.c - a part of driver for Dice based devices
+ *
+ * Copyright (c) Clemens Ladisch
+ * Copyright (c) 2014 Takashi Sakamoto
+ */
+
+#include "dice.h"
+
+static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type,
+ u64 offset)
+{
+ switch (type) {
+ case SND_DICE_ADDR_TYPE_TX:
+ offset += dice->tx_offset;
+ break;
+ case SND_DICE_ADDR_TYPE_RX:
+ offset += dice->rx_offset;
+ break;
+ case SND_DICE_ADDR_TYPE_SYNC:
+ offset += dice->sync_offset;
+ break;
+ case SND_DICE_ADDR_TYPE_RSRV:
+ offset += dice->rsrv_offset;
+ break;
+ case SND_DICE_ADDR_TYPE_GLOBAL:
+ default:
+ offset += dice->global_offset;
+ break;
+ }
+ offset += DICE_PRIVATE_SPACE;
+ return offset;
+}
+
+int snd_dice_transaction_write(struct snd_dice *dice,
+ enum snd_dice_addr_type type,
+ unsigned int offset, void *buf, unsigned int len)
+{
+ return snd_fw_transaction(dice->unit,
+ (len == 4) ? TCODE_WRITE_QUADLET_REQUEST :
+ TCODE_WRITE_BLOCK_REQUEST,
+ get_subaddr(dice, type, offset), buf, len, 0);
+}
+
+int snd_dice_transaction_read(struct snd_dice *dice,
+ enum snd_dice_addr_type type, unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_fw_transaction(dice->unit,
+ (len == 4) ? TCODE_READ_QUADLET_REQUEST :
+ TCODE_READ_BLOCK_REQUEST,
+ get_subaddr(dice, type, offset), buf, len, 0);
+}
+
+static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info)
+{
+ return snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
+ info, 4);
+}
+
+int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
+ unsigned int *source)
+{
+ __be32 info;
+ int err;
+
+ err = get_clock_info(dice, &info);
+ if (err >= 0)
+ *source = be32_to_cpu(info) & CLOCK_SOURCE_MASK;
+
+ return err;
+}
+
+int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)
+{
+ __be32 info;
+ unsigned int index;
+ int err;
+
+ err = get_clock_info(dice, &info);
+ if (err < 0)
+ goto end;
+
+ index = (be32_to_cpu(info) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT;
+ if (index >= SND_DICE_RATES_COUNT) {
+ err = -ENOSYS;
+ goto end;
+ }
+
+ *rate = snd_dice_rates[index];
+end:
+ return err;
+}
+
+int snd_dice_transaction_set_enable(struct snd_dice *dice)
+{
+ __be32 value;
+ int err = 0;
+
+ if (dice->global_enabled)
+ goto end;
+
+ value = cpu_to_be32(1);
+ err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+ get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
+ GLOBAL_ENABLE),
+ &value, 4,
+ FW_FIXED_GENERATION | dice->owner_generation);
+ if (err < 0)
+ goto end;
+
+ dice->global_enabled = true;
+end:
+ return err;
+}
+
+void snd_dice_transaction_clear_enable(struct snd_dice *dice)
+{
+ __be32 value;
+
+ value = 0;
+ snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
+ get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
+ GLOBAL_ENABLE),
+ &value, 4, FW_QUIET |
+ FW_FIXED_GENERATION | dice->owner_generation);
+
+ dice->global_enabled = false;
+}
+
+static void dice_notification(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_dice *dice = callback_data;
+ u32 bits;
+
+ if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
+ fw_send_response(card, request, RCODE_TYPE_ERROR);
+ return;
+ }
+ if ((offset & 3) != 0) {
+ fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+ return;
+ }
+
+ bits = be32_to_cpup(data);
+
+ scoped_guard(spinlock_irqsave, &dice->lock) {
+ dice->notification_bits |= bits;
+ }
+
+ fw_send_response(card, request, RCODE_COMPLETE);
+
+ if (bits & NOTIFY_CLOCK_ACCEPTED)
+ complete(&dice->clock_accepted);
+ wake_up(&dice->hwdep_wait);
+}
+
+static int register_notification_address(struct snd_dice *dice, bool retry)
+{
+ struct fw_device *device = fw_parent_device(dice->unit);
+ __be64 *buffer;
+ unsigned int retries;
+ int err;
+
+ retries = (retry) ? 3 : 0;
+
+ buffer = kmalloc(2 * 8, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ for (;;) {
+ buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
+ buffer[1] = cpu_to_be64(
+ ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
+ dice->notification_handler.offset);
+
+ dice->owner_generation = device->generation;
+ smp_rmb(); /* node_id vs. generation */
+ err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
+ get_subaddr(dice,
+ SND_DICE_ADDR_TYPE_GLOBAL,
+ GLOBAL_OWNER),
+ buffer, 2 * 8,
+ FW_FIXED_GENERATION |
+ dice->owner_generation);
+ if (err == 0) {
+ /* success */
+ if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER))
+ break;
+ /* The address seems to be already registered. */
+ if (buffer[0] == buffer[1])
+ break;
+
+ dev_err(&dice->unit->device,
+ "device is already in use\n");
+ err = -EBUSY;
+ }
+ if (err != -EAGAIN || retries-- > 0)
+ break;
+
+ msleep(20);
+ }
+
+ kfree(buffer);
+
+ if (err < 0)
+ dice->owner_generation = -1;
+
+ return err;
+}
+
+static void unregister_notification_address(struct snd_dice *dice)
+{
+ struct fw_device *device = fw_parent_device(dice->unit);
+ __be64 *buffer;
+
+ buffer = kmalloc(2 * 8, GFP_KERNEL);
+ if (buffer == NULL)
+ return;
+
+ buffer[0] = cpu_to_be64(
+ ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
+ dice->notification_handler.offset);
+ buffer[1] = cpu_to_be64(OWNER_NO_OWNER);
+ snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
+ get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
+ GLOBAL_OWNER),
+ buffer, 2 * 8, FW_QUIET |
+ FW_FIXED_GENERATION | dice->owner_generation);
+
+ kfree(buffer);
+
+ dice->owner_generation = -1;
+}
+
+void snd_dice_transaction_destroy(struct snd_dice *dice)
+{
+ struct fw_address_handler *handler = &dice->notification_handler;
+
+ if (handler->callback_data == NULL)
+ return;
+
+ unregister_notification_address(dice);
+
+ fw_core_remove_address_handler(handler);
+ handler->callback_data = NULL;
+}
+
+int snd_dice_transaction_reinit(struct snd_dice *dice)
+{
+ struct fw_address_handler *handler = &dice->notification_handler;
+
+ if (handler->callback_data == NULL)
+ return -EINVAL;
+
+ return register_notification_address(dice, false);
+}
+
+static int get_subaddrs(struct snd_dice *dice)
+{
+ static const int min_values[10] = {
+ 10, 0x60 / 4,
+ 10, 0x18 / 4,
+ 10, 0x18 / 4,
+ 0, 0,
+ 0, 0,
+ };
+ __be32 *pointers;
+ __be32 version;
+ u32 data;
+ unsigned int i;
+ int err;
+
+ pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
+ GFP_KERNEL);
+ if (pointers == NULL)
+ return -ENOMEM;
+
+ /*
+ * Check that the sub address spaces exist and are located inside the
+ * private address space. The minimum values are chosen so that all
+ * minimally required registers are included.
+ */
+ err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
+ DICE_PRIVATE_SPACE, pointers,
+ sizeof(__be32) * ARRAY_SIZE(min_values), 0);
+ if (err < 0)
+ goto end;
+
+ for (i = 0; i < ARRAY_SIZE(min_values); ++i) {
+ data = be32_to_cpu(pointers[i]);
+ if (data < min_values[i] || data >= 0x40000) {
+ err = -ENODEV;
+ goto end;
+ }
+ }
+
+ if (be32_to_cpu(pointers[1]) > 0x18) {
+ /*
+ * Check that the implemented DICE driver specification major
+ * version number matches.
+ */
+ err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
+ DICE_PRIVATE_SPACE +
+ be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
+ &version, sizeof(version), 0);
+ if (err < 0)
+ goto end;
+
+ if ((version & cpu_to_be32(0xff000000)) !=
+ cpu_to_be32(0x01000000)) {
+ dev_err(&dice->unit->device,
+ "unknown DICE version: 0x%08x\n",
+ be32_to_cpu(version));
+ err = -ENODEV;
+ goto end;
+ }
+
+ /* Set up later. */
+ dice->clock_caps = 1;
+ }
+
+ dice->global_offset = be32_to_cpu(pointers[0]) * 4;
+ dice->tx_offset = be32_to_cpu(pointers[2]) * 4;
+ dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
+
+ /* Old firmware doesn't support these fields. */
+ if (pointers[7])
+ dice->sync_offset = be32_to_cpu(pointers[6]) * 4;
+ if (pointers[9])
+ dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4;
+end:
+ kfree(pointers);
+ return err;
+}
+
+int snd_dice_transaction_init(struct snd_dice *dice)
+{
+ struct fw_address_handler *handler = &dice->notification_handler;
+ int err;
+
+ err = get_subaddrs(dice);
+ if (err < 0)
+ return err;
+
+ /* Allocation callback in address space over host controller */
+ handler->length = 4;
+ handler->address_callback = dice_notification;
+ handler->callback_data = dice;
+ err = fw_core_add_address_handler(handler, &fw_high_memory_region);
+ if (err < 0) {
+ handler->callback_data = NULL;
+ return err;
+ }
+
+ /* Register the address space */
+ err = register_notification_address(dice, true);
+ if (err < 0) {
+ fw_core_remove_address_handler(handler);
+ handler->callback_data = NULL;
+ }
+
+ return err;
+}
diff --git a/sound/firewire/dice/dice-weiss.c b/sound/firewire/dice/dice-weiss.c
new file mode 100644
index 000000000000..129d43408956
--- /dev/null
+++ b/sound/firewire/dice/dice-weiss.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0
+// dice-weiss.c - a part of driver for DICE based devices
+//
+// Copyright (c) 2023 Rolf Anderegg and Michele Perrone
+
+#include "dice.h"
+
+struct dice_weiss_spec {
+ unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+ unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+};
+
+// Weiss DAC202: 192kHz 2-channel DAC
+static const struct dice_weiss_spec dac202 = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss MAN301: 192kHz 2-channel music archive network player
+static const struct dice_weiss_spec man301 = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss INT202: 192kHz unidirectional 2-channel digital Firewire nterface
+static const struct dice_weiss_spec int202 = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss INT203: 192kHz bidirectional 2-channel digital Firewire nterface
+static const struct dice_weiss_spec int203 = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss ADC2: 192kHz A/D converter with microphone preamps and line nputs
+static const struct dice_weiss_spec adc2 = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss DAC2/Minerva: 192kHz 2-channel DAC
+static const struct dice_weiss_spec dac2_minerva = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss Vesta: 192kHz 2-channel Firewire to AES/EBU interface
+static const struct dice_weiss_spec vesta = {
+ .tx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+ .rx_pcm_chs = {{2, 2, 2}, {0, 0, 0} },
+};
+
+// Weiss AFI1: 192kHz 24-channel Firewire to ADAT or AES/EBU interface
+static const struct dice_weiss_spec afi1 = {
+ .tx_pcm_chs = {{24, 16, 8}, {0, 0, 0} },
+ .rx_pcm_chs = {{24, 16, 8}, {0, 0, 0} },
+};
+
+int snd_dice_detect_weiss_formats(struct snd_dice *dice)
+{
+ static const struct {
+ u32 model_id;
+ const struct dice_weiss_spec *spec;
+ } *entry, entries[] = {
+ {0x000007, &dac202},
+ {0x000008, &dac202}, // Maya edition: same audio I/O as DAC202.
+ {0x000006, &int202},
+ {0x00000a, &int203},
+ {0x00000b, &man301},
+ {0x000001, &adc2},
+ {0x000003, &dac2_minerva},
+ {0x000002, &vesta},
+ {0x000004, &afi1},
+ };
+ struct fw_csr_iterator it;
+ int key, val, model_id;
+ int i;
+
+ model_id = 0;
+ fw_csr_iterator_init(&it, dice->unit->directory);
+ while (fw_csr_iterator_next(&it, &key, &val)) {
+ if (key == CSR_MODEL) {
+ model_id = val;
+ break;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(entries); ++i) {
+ entry = entries + i;
+ if (entry->model_id == model_id)
+ break;
+ }
+ if (i == ARRAY_SIZE(entries))
+ return -ENODEV;
+
+ memcpy(dice->tx_pcm_chs, entry->spec->tx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
+ memcpy(dice->rx_pcm_chs, entry->spec->rx_pcm_chs,
+ MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
+
+ return 0;
+}
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
new file mode 100644
index 000000000000..85d265c7d544
--- /dev/null
+++ b/sound/firewire/dice/dice.c
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TC Applied Technologies Digital Interface Communications Engine driver
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include "dice.h"
+
+MODULE_DESCRIPTION("DICE driver");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL");
+
+#define OUI_WEISS 0x001c6a
+#define OUI_LOUD 0x000ff2
+#define OUI_FOCUSRITE 0x00130e
+#define OUI_TCELECTRONIC 0x000166
+#define OUI_ALESIS 0x000595
+#define OUI_MAUDIO 0x000d6c
+#define OUI_MYTEK 0x001ee8
+#define OUI_SSL 0x0050c2 // Actually ID reserved by IEEE.
+#define OUI_PRESONUS 0x000a92
+#define OUI_HARMAN 0x000fd7
+#define OUI_AVID 0x00a07e
+#define OUI_TEAC 0x00022e
+
+#define DICE_CATEGORY_ID 0x04
+#define WEISS_CATEGORY_ID 0x00
+#define LOUD_CATEGORY_ID 0x10
+#define HARMAN_CATEGORY_ID 0x20
+
+#define MODEL_ALESIS_IO_BOTH 0x000001
+
+static int check_dice_category(struct fw_unit *unit)
+{
+ struct fw_device *device = fw_parent_device(unit);
+ struct fw_csr_iterator it;
+ int key, val, vendor = -1, model = -1;
+ unsigned int category;
+
+ /*
+ * Check that GUID and unit directory are constructed according to DICE
+ * rules, i.e., that the specifier ID is the GUID's OUI, and that the
+ * GUID chip ID consists of the 8-bit category ID, the 10-bit product
+ * ID, and a 22-bit serial number.
+ */
+ fw_csr_iterator_init(&it, unit->directory);
+ while (fw_csr_iterator_next(&it, &key, &val)) {
+ switch (key) {
+ case CSR_SPECIFIER_ID:
+ vendor = val;
+ break;
+ case CSR_MODEL:
+ model = val;
+ break;
+ }
+ }
+
+ if (vendor == OUI_WEISS)
+ category = WEISS_CATEGORY_ID;
+ else if (vendor == OUI_LOUD)
+ category = LOUD_CATEGORY_ID;
+ else if (vendor == OUI_HARMAN)
+ category = HARMAN_CATEGORY_ID;
+ else
+ category = DICE_CATEGORY_ID;
+ if (device->config_rom[3] != ((vendor << 8) | category) ||
+ device->config_rom[4] >> 22 != model)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int check_clock_caps(struct snd_dice *dice)
+{
+ __be32 value;
+ int err;
+
+ /* some very old firmwares don't tell about their clock support */
+ if (dice->clock_caps > 0) {
+ err = snd_dice_transaction_read_global(dice,
+ GLOBAL_CLOCK_CAPABILITIES,
+ &value, 4);
+ if (err < 0)
+ return err;
+ dice->clock_caps = be32_to_cpu(value);
+ } else {
+ /* this should be supported by any device */
+ dice->clock_caps = CLOCK_CAP_RATE_44100 |
+ CLOCK_CAP_RATE_48000 |
+ CLOCK_CAP_SOURCE_ARX1 |
+ CLOCK_CAP_SOURCE_INTERNAL;
+ }
+
+ return 0;
+}
+
+static void dice_card_strings(struct snd_dice *dice)
+{
+ struct snd_card *card = dice->card;
+ struct fw_device *dev = fw_parent_device(dice->unit);
+ char vendor[32], model[32];
+ unsigned int i;
+ int err;
+
+ strscpy(card->driver, "DICE");
+
+ strscpy(card->shortname, "DICE");
+ BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname));
+ err = snd_dice_transaction_read_global(dice, GLOBAL_NICK_NAME,
+ card->shortname,
+ sizeof(card->shortname));
+ if (err >= 0) {
+ /* DICE strings are returned in "always-wrong" endianness */
+ BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0);
+ for (i = 0; i < sizeof(card->shortname); i += 4)
+ swab32s((u32 *)&card->shortname[i]);
+ card->shortname[sizeof(card->shortname) - 1] = '\0';
+ }
+
+ strscpy(vendor, "?");
+ fw_csr_string(dev->config_rom + 5, CSR_VENDOR, vendor, sizeof(vendor));
+ strscpy(model, "?");
+ fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model));
+ snprintf(card->longname, sizeof(card->longname),
+ "%s %s (serial %u) at %s, S%d",
+ vendor, model, dev->config_rom[4] & 0x3fffff,
+ dev_name(&dice->unit->device), 100 << dev->max_speed);
+
+ strscpy(card->mixername, "DICE");
+}
+
+static void dice_card_free(struct snd_card *card)
+{
+ struct snd_dice *dice = card->private_data;
+
+ snd_dice_stream_destroy_duplex(dice);
+ snd_dice_transaction_destroy(dice);
+
+ mutex_destroy(&dice->mutex);
+ fw_unit_put(dice->unit);
+}
+
+static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_dice *dice;
+ snd_dice_detect_formats_t detect_formats;
+ int err;
+
+ if (!entry->driver_data && entry->vendor_id != OUI_SSL) {
+ err = check_dice_category(unit);
+ if (err < 0)
+ return -ENODEV;
+ }
+
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*dice), &card);
+ if (err < 0)
+ return err;
+ card->private_free = dice_card_free;
+
+ dice = card->private_data;
+ dice->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, dice);
+ dice->card = card;
+
+ if (!entry->driver_data)
+ detect_formats = snd_dice_stream_detect_current_formats;
+ else
+ detect_formats = (snd_dice_detect_formats_t)entry->driver_data;
+
+ // Below models are compliant to IEC 61883-1/6 and have no quirk at high sampling transfer
+ // frequency.
+ // * Avid M-Box 3 Pro
+ // * M-Audio Profire 610
+ // * M-Audio Profire 2626
+ if (entry->vendor_id == OUI_MAUDIO || entry->vendor_id == OUI_AVID)
+ dice->disable_double_pcm_frames = true;
+
+ spin_lock_init(&dice->lock);
+ mutex_init(&dice->mutex);
+ init_completion(&dice->clock_accepted);
+ init_waitqueue_head(&dice->hwdep_wait);
+
+ err = snd_dice_transaction_init(dice);
+ if (err < 0)
+ goto error;
+
+ err = check_clock_caps(dice);
+ if (err < 0)
+ goto error;
+
+ dice_card_strings(dice);
+
+ err = detect_formats(dice);
+ if (err < 0)
+ goto error;
+
+ err = snd_dice_stream_init_duplex(dice);
+ if (err < 0)
+ goto error;
+
+ snd_dice_create_proc(dice);
+
+ err = snd_dice_create_pcm(dice);
+ if (err < 0)
+ goto error;
+
+ err = snd_dice_create_midi(dice);
+ if (err < 0)
+ goto error;
+
+ err = snd_dice_create_hwdep(dice);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void dice_remove(struct fw_unit *unit)
+{
+ struct snd_dice *dice = dev_get_drvdata(&unit->device);
+
+ // Block till all of ALSA character devices are released.
+ snd_card_free(dice->card);
+}
+
+static void dice_bus_reset(struct fw_unit *unit)
+{
+ struct snd_dice *dice = dev_get_drvdata(&unit->device);
+
+ /* The handler address register becomes initialized. */
+ snd_dice_transaction_reinit(dice);
+
+ guard(mutex)(&dice->mutex);
+ snd_dice_stream_update_duplex(dice);
+}
+
+#define DICE_INTERFACE 0x000001
+
+#define DICE_DEV_ENTRY_TYPICAL(vendor, model, data) \
+ { \
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | \
+ IEEE1394_MATCH_MODEL_ID | \
+ IEEE1394_MATCH_SPECIFIER_ID | \
+ IEEE1394_MATCH_VERSION, \
+ .vendor_id = (vendor), \
+ .model_id = (model), \
+ .specifier_id = (vendor), \
+ .version = DICE_INTERFACE, \
+ .driver_data = (kernel_ulong_t)(data), \
+ }
+
+static const struct ieee1394_device_id dice_id_table[] = {
+ // Avid M-Box 3 Pro. To match in probe function.
+ DICE_DEV_ENTRY_TYPICAL(OUI_AVID, 0x000004, snd_dice_detect_extension_formats),
+ /* M-Audio Profire 2626 has a different value in version field. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_MAUDIO,
+ .model_id = 0x000010,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_extension_formats,
+ },
+ /* M-Audio Profire 610 has a different value in version field. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_MAUDIO,
+ .model_id = 0x000011,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_extension_formats,
+ },
+ /* TC Electronic Konnekt 24D. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_TCELECTRONIC,
+ .model_id = 0x000020,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
+ },
+ /* TC Electronic Konnekt 8. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_TCELECTRONIC,
+ .model_id = 0x000021,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
+ },
+ /* TC Electronic Studio Konnekt 48. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_TCELECTRONIC,
+ .model_id = 0x000022,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
+ },
+ /* TC Electronic Konnekt Live. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_TCELECTRONIC,
+ .model_id = 0x000023,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
+ },
+ /* TC Electronic Desktop Konnekt 6. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_TCELECTRONIC,
+ .model_id = 0x000024,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
+ },
+ /* TC Electronic Impact Twin. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_TCELECTRONIC,
+ .model_id = 0x000027,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
+ },
+ /* TC Electronic Digital Konnekt x32. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_TCELECTRONIC,
+ .model_id = 0x000030,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats,
+ },
+ /* Alesis iO14/iO26. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_ALESIS,
+ .model_id = MODEL_ALESIS_IO_BOTH,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_alesis_formats,
+ },
+ // Alesis MasterControl.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_ALESIS,
+ .model_id = 0x000002,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_alesis_mastercontrol_formats,
+ },
+ /* Mytek Stereo 192 DSD-DAC. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_MYTEK,
+ .model_id = 0x000002,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_mytek_formats,
+ },
+ // Solid State Logic, Duende Classic and Mini.
+ // NOTE: each field of GUID in config ROM is not compliant to standard
+ // DICE scheme.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_SSL,
+ .model_id = 0x000070,
+ },
+ // Presonus FireStudio.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_PRESONUS,
+ .model_id = 0x000008,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_presonus_formats,
+ },
+ // Lexicon I-ONYX FW810S.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_HARMAN,
+ .model_id = 0x000001,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_harman_formats,
+ },
+ // Focusrite Saffire Pro 40 with TCD3070-CH.
+ // The model has quirk in its GUID, in which model field is 0x000013 and different from
+ // model ID (0x0000de) in its root/unit directory.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_FOCUSRITE,
+ .model_id = 0x0000de,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_focusrite_pro40_tcd3070_formats,
+ },
+ // Weiss DAC202: 192kHz 2-channel DAC
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000007,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss DAC202: 192kHz 2-channel DAC (Maya edition)
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000008,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss MAN301: 192kHz 2-channel music archive network player
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x00000b,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss INT202: 192kHz unidirectional 2-channel digital Firewire face
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000006,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss INT203: 192kHz bidirectional 2-channel digital Firewire face
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x00000a,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss ADC2: 192kHz A/D converter with microphone preamps and inputs
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000001,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss DAC2/Minerva: 192kHz 2-channel DAC
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000003,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss Vesta: 192kHz 2-channel Firewire to AES/EBU interface
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000002,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ // Weiss AFI1: 192kHz 24-channel Firewire to ADAT or AES/EBU face
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_WEISS,
+ .model_id = 0x000004,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_weiss_formats,
+ },
+ {
+ .match_flags = IEEE1394_MATCH_VERSION,
+ .version = DICE_INTERFACE,
+ },
+ // Tascam IF-FW/DM MkII for DM-3200 and DM-4800.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
+ .vendor_id = OUI_TEAC,
+ .model_id = OUI_TEAC,
+ .specifier_id = OUI_TEAC,
+ .version = 0x800006,
+ .driver_data = (kernel_ulong_t)snd_dice_detect_teac_formats,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(ieee1394, dice_id_table);
+
+static struct fw_driver dice_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = dice_probe,
+ .update = dice_bus_reset,
+ .remove = dice_remove,
+ .id_table = dice_id_table,
+};
+
+static int __init alsa_dice_init(void)
+{
+ return driver_register(&dice_driver.driver);
+}
+
+static void __exit alsa_dice_exit(void)
+{
+ driver_unregister(&dice_driver.driver);
+}
+
+module_init(alsa_dice_init);
+module_exit(alsa_dice_exit);
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
new file mode 100644
index 000000000000..7744ea6a0791
--- /dev/null
+++ b/sound/firewire/dice/dice.h
@@ -0,0 +1,238 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * dice.h - a part of driver for Dice based devices
+ *
+ * Copyright (c) Clemens Ladisch
+ * Copyright (c) 2014 Takashi Sakamoto
+ */
+
+#ifndef SOUND_DICE_H_INCLUDED
+#define SOUND_DICE_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/sched/signal.h>
+
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/rawmidi.h>
+
+#include "../amdtp-am824.h"
+#include "../iso-resources.h"
+#include "../lib.h"
+#include "dice-interface.h"
+
+/*
+ * This module support maximum 2 pairs of tx/rx isochronous streams for
+ * our convinience.
+ *
+ * In documents for ASICs called with a name of 'DICE':
+ * - ASIC for DICE II:
+ * - Maximum 2 tx and 4 rx are supported.
+ * - A packet supports maximum 16 data channels.
+ * - TCD2210/2210-E (so-called 'Dice Mini'):
+ * - Maximum 2 tx and 2 rx are supported.
+ * - A packet supports maximum 16 data channels.
+ * - TCD2220/2220-E (so-called 'Dice Jr.')
+ * - 2 tx and 2 rx are supported.
+ * - A packet supports maximum 16 data channels.
+ * - TCD3070-CH (so-called 'Dice III')
+ * - Maximum 2 tx and 2 rx are supported.
+ * - A packet supports maximum 32 data channels.
+ *
+ * For the above, MIDI conformant data channel is just on the first isochronous
+ * stream.
+ */
+#define MAX_STREAMS 2
+
+enum snd_dice_rate_mode {
+ SND_DICE_RATE_MODE_LOW = 0,
+ SND_DICE_RATE_MODE_MIDDLE,
+ SND_DICE_RATE_MODE_HIGH,
+ SND_DICE_RATE_MODE_COUNT,
+};
+
+struct snd_dice;
+typedef int (*snd_dice_detect_formats_t)(struct snd_dice *dice);
+
+struct snd_dice {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ spinlock_t lock;
+ struct mutex mutex;
+
+ /* Offsets for sub-addresses */
+ unsigned int global_offset;
+ unsigned int rx_offset;
+ unsigned int tx_offset;
+ unsigned int sync_offset;
+ unsigned int rsrv_offset;
+
+ unsigned int clock_caps;
+ unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+ unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+ unsigned int tx_midi_ports[MAX_STREAMS];
+ unsigned int rx_midi_ports[MAX_STREAMS];
+
+ struct fw_address_handler notification_handler;
+ int owner_generation;
+ u32 notification_bits;
+
+ /* For uapi */
+ int dev_lock_count; /* > 0 driver, < 0 userspace */
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* For streaming */
+ struct fw_iso_resources tx_resources[MAX_STREAMS];
+ struct fw_iso_resources rx_resources[MAX_STREAMS];
+ struct amdtp_stream tx_stream[MAX_STREAMS];
+ struct amdtp_stream rx_stream[MAX_STREAMS];
+ bool global_enabled:1;
+ bool disable_double_pcm_frames:1;
+ struct completion clock_accepted;
+ unsigned int substreams_counter;
+
+ struct amdtp_domain domain;
+};
+
+enum snd_dice_addr_type {
+ SND_DICE_ADDR_TYPE_PRIVATE,
+ SND_DICE_ADDR_TYPE_GLOBAL,
+ SND_DICE_ADDR_TYPE_TX,
+ SND_DICE_ADDR_TYPE_RX,
+ SND_DICE_ADDR_TYPE_SYNC,
+ SND_DICE_ADDR_TYPE_RSRV,
+};
+
+int snd_dice_transaction_write(struct snd_dice *dice,
+ enum snd_dice_addr_type type,
+ unsigned int offset,
+ void *buf, unsigned int len);
+int snd_dice_transaction_read(struct snd_dice *dice,
+ enum snd_dice_addr_type type, unsigned int offset,
+ void *buf, unsigned int len);
+
+static inline int snd_dice_transaction_write_global(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_write(dice,
+ SND_DICE_ADDR_TYPE_GLOBAL, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_read_global(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_read(dice,
+ SND_DICE_ADDR_TYPE_GLOBAL, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_write_tx(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_TX, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_read_tx(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_TX, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_write_rx(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_RX, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_read_rx(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_RX, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_write_sync(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_SYNC, offset,
+ buf, len);
+}
+static inline int snd_dice_transaction_read_sync(struct snd_dice *dice,
+ unsigned int offset,
+ void *buf, unsigned int len)
+{
+ return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_SYNC, offset,
+ buf, len);
+}
+
+int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
+ unsigned int *source);
+int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate);
+int snd_dice_transaction_set_enable(struct snd_dice *dice);
+void snd_dice_transaction_clear_enable(struct snd_dice *dice);
+int snd_dice_transaction_init(struct snd_dice *dice);
+int snd_dice_transaction_reinit(struct snd_dice *dice);
+void snd_dice_transaction_destroy(struct snd_dice *dice);
+
+#define SND_DICE_RATES_COUNT 7
+extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT];
+
+int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
+ enum snd_dice_rate_mode *mode);
+int snd_dice_stream_start_duplex(struct snd_dice *dice);
+void snd_dice_stream_stop_duplex(struct snd_dice *dice);
+int snd_dice_stream_init_duplex(struct snd_dice *dice);
+void snd_dice_stream_destroy_duplex(struct snd_dice *dice);
+int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate,
+ unsigned int events_per_period,
+ unsigned int events_per_buffer);
+void snd_dice_stream_update_duplex(struct snd_dice *dice);
+int snd_dice_stream_detect_current_formats(struct snd_dice *dice);
+
+int snd_dice_stream_lock_try(struct snd_dice *dice);
+void snd_dice_stream_lock_release(struct snd_dice *dice);
+
+int snd_dice_create_pcm(struct snd_dice *dice);
+
+int snd_dice_create_hwdep(struct snd_dice *dice);
+
+void snd_dice_create_proc(struct snd_dice *dice);
+
+int snd_dice_create_midi(struct snd_dice *dice);
+
+int snd_dice_detect_tcelectronic_formats(struct snd_dice *dice);
+int snd_dice_detect_alesis_formats(struct snd_dice *dice);
+int snd_dice_detect_alesis_mastercontrol_formats(struct snd_dice *dice);
+int snd_dice_detect_extension_formats(struct snd_dice *dice);
+int snd_dice_detect_mytek_formats(struct snd_dice *dice);
+int snd_dice_detect_presonus_formats(struct snd_dice *dice);
+int snd_dice_detect_harman_formats(struct snd_dice *dice);
+int snd_dice_detect_focusrite_pro40_tcd3070_formats(struct snd_dice *dice);
+int snd_dice_detect_weiss_formats(struct snd_dice *dice);
+int snd_dice_detect_teac_formats(struct snd_dice *dice);
+
+#endif
diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile
new file mode 100644
index 000000000000..6dc18bd2e186
--- /dev/null
+++ b/sound/firewire/digi00x/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-firewire-digi00x-y := amdtp-dot.o digi00x-stream.o digi00x-proc.o \
+ digi00x-pcm.o digi00x-hwdep.o \
+ digi00x-transaction.o digi00x-midi.o digi00x.o
+obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o
diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c
new file mode 100644
index 000000000000..7db0024495b7
--- /dev/null
+++ b/sound/firewire/digi00x/amdtp-dot.c
@@ -0,0 +1,412 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * amdtp-dot.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ * Copyright (C) 2012 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2012 Damien Zammit <damien@zamaudio.com>
+ */
+
+#include <sound/pcm.h>
+#include "digi00x.h"
+
+#define CIP_FMT_AM 0x10
+
+/* 'Clock-based rate control mode' is just supported. */
+#define AMDTP_FDF_AM824 0x00
+
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND 3093
+
+/*
+ * Several devices look only at the first eight data blocks.
+ * In any case, this is more than enough for the MIDI data rate.
+ */
+#define MAX_MIDI_RX_BLOCKS 8
+
+/* 3 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) + 1. */
+#define MAX_MIDI_PORTS 3
+
+/*
+ * The double-oh-three algorithm was discovered by Robin Gareus and Damien
+ * Zammit in 2012, with reverse-engineering for Digi 003 Rack.
+ */
+struct dot_state {
+ u8 carry;
+ u8 idx;
+ unsigned int off;
+};
+
+struct amdtp_dot {
+ unsigned int pcm_channels;
+ struct dot_state state;
+
+ struct snd_rawmidi_substream *midi[MAX_MIDI_PORTS];
+ int midi_fifo_used[MAX_MIDI_PORTS];
+ int midi_fifo_limit;
+};
+
+/*
+ * double-oh-three look up table
+ *
+ * @param idx index byte (audio-sample data) 0x00..0xff
+ * @param off channel offset shift
+ * @return salt to XOR with given data
+ */
+#define BYTE_PER_SAMPLE (4)
+#define MAGIC_DOT_BYTE (2)
+#define MAGIC_BYTE_OFF(x) (((x) * BYTE_PER_SAMPLE) + MAGIC_DOT_BYTE)
+static u8 dot_scrt(const u8 idx, const unsigned int off)
+{
+ /*
+ * the length of the added pattern only depends on the lower nibble
+ * of the last non-zero data
+ */
+ static const u8 len[16] = {0, 1, 3, 5, 7, 9, 11, 13, 14,
+ 12, 10, 8, 6, 4, 2, 0};
+
+ /*
+ * the lower nibble of the salt. Interleaved sequence.
+ * this is walked backwards according to len[]
+ */
+ static const u8 nib[15] = {0x8, 0x7, 0x9, 0x6, 0xa, 0x5, 0xb, 0x4,
+ 0xc, 0x3, 0xd, 0x2, 0xe, 0x1, 0xf};
+
+ /* circular list for the salt's hi nibble. */
+ static const u8 hir[15] = {0x0, 0x6, 0xf, 0x8, 0x7, 0x5, 0x3, 0x4,
+ 0xc, 0xd, 0xe, 0x1, 0x2, 0xb, 0xa};
+
+ /*
+ * start offset for upper nibble mapping.
+ * note: 9 is /special/. In the case where the high nibble == 0x9,
+ * hir[] is not used and - coincidentally - the salt's hi nibble is
+ * 0x09 regardless of the offset.
+ */
+ static const u8 hio[16] = {0, 11, 12, 6, 7, 5, 1, 4,
+ 3, 0x00, 14, 13, 8, 9, 10, 2};
+
+ const u8 ln = idx & 0xf;
+ const u8 hn = (idx >> 4) & 0xf;
+ const u8 hr = (hn == 0x9) ? 0x9 : hir[(hio[hn] + off) % 15];
+
+ if (len[ln] < off)
+ return 0x00;
+
+ return ((nib[14 + off - len[ln]]) | (hr << 4));
+}
+
+static void dot_encode_step(struct dot_state *state, __be32 *const buffer)
+{
+ u8 * const data = (u8 *) buffer;
+
+ if (data[MAGIC_DOT_BYTE] != 0x00) {
+ state->off = 0;
+ state->idx = data[MAGIC_DOT_BYTE] ^ state->carry;
+ }
+ data[MAGIC_DOT_BYTE] ^= state->carry;
+ state->carry = dot_scrt(state->idx, ++(state->off));
+}
+
+int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels)
+{
+ struct amdtp_dot *p = s->protocol;
+ int err;
+
+ if (amdtp_stream_running(s))
+ return -EBUSY;
+
+ /*
+ * A first data channel is for MIDI messages, the rest is Multi Bit
+ * Linear Audio data channel.
+ */
+ err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1, 1);
+ if (err < 0)
+ return err;
+
+ s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc;
+
+ p->pcm_channels = pcm_channels;
+
+ /*
+ * We do not know the actual MIDI FIFO size of most devices. Just
+ * assume two bytes, i.e., one byte can be received over the bus while
+ * the previous one is transmitted over MIDI.
+ * (The value here is adjusted for midi_ratelimit_per_packet().)
+ */
+ p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
+
+ return 0;
+}
+
+static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames,
+ unsigned int pcm_frames)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int channels = p->pcm_channels;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ const u32 *src;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ buffer++;
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_be32((*src >> 8) | 0x40000000);
+ dot_encode_step(&p->state, &buffer[c]);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames,
+ unsigned int pcm_frames)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int channels = p->pcm_channels;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ u32 *dst;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ buffer++;
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ *dst = be32_to_cpu(buffer[c]) << 8;
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int channels, i, c;
+
+ channels = p->pcm_channels;
+
+ buffer++;
+ for (i = 0; i < data_blocks; ++i) {
+ for (c = 0; c < channels; ++c)
+ buffer[c] = cpu_to_be32(0x40000000);
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
+{
+ struct amdtp_dot *p = s->protocol;
+ int used;
+
+ used = p->midi_fifo_used[port];
+ if (used == 0)
+ return true;
+
+ used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
+ used = max(used, 0);
+ p->midi_fifo_used[port] = used;
+
+ return used < p->midi_fifo_limit;
+}
+
+static inline void midi_use_bytes(struct amdtp_stream *s,
+ unsigned int port, unsigned int count)
+{
+ struct amdtp_dot *p = s->protocol;
+
+ p->midi_fifo_used[port] += amdtp_rate_table[s->sfc] * count;
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks, unsigned int data_block_counter)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int f, port;
+ int len;
+ u8 *b;
+
+ for (f = 0; f < data_blocks; f++) {
+ port = (data_block_counter + f) % 8;
+ b = (u8 *)&buffer[0];
+
+ len = 0;
+ if (port < MAX_MIDI_PORTS &&
+ midi_ratelimit_per_packet(s, port) &&
+ p->midi[port] != NULL)
+ len = snd_rawmidi_transmit(p->midi[port], b + 1, 2);
+
+ if (len > 0) {
+ /*
+ * Upper 4 bits of LSB represent port number.
+ * - 0000b: physical MIDI port 1.
+ * - 0010b: physical MIDI port 2.
+ * - 1110b: console MIDI port.
+ */
+ if (port == 2)
+ b[3] = 0xe0;
+ else if (port == 1)
+ b[3] = 0x20;
+ else
+ b[3] = 0x00;
+ b[3] |= len;
+ midi_use_bytes(s, port, len);
+ } else {
+ b[1] = 0;
+ b[2] = 0;
+ b[3] = 0;
+ }
+ b[0] = 0x80;
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_dot *p = s->protocol;
+ unsigned int f, port, len;
+ u8 *b;
+
+ for (f = 0; f < data_blocks; f++) {
+ b = (u8 *)&buffer[0];
+
+ len = b[3] & 0x0f;
+ if (len > 0) {
+ /*
+ * Upper 4 bits of LSB represent port number.
+ * - 0000b: physical MIDI port 1. Use port 0.
+ * - 1110b: console MIDI port. Use port 2.
+ */
+ if (b[3] >> 4 > 0)
+ port = 2;
+ else
+ port = 0;
+
+ if (port < MAX_MIDI_PORTS && p->midi[port])
+ snd_rawmidi_receive(p->midi[port], b + 1, len);
+ }
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ /* This protocol delivers 24 bit data in 32bit data channel. */
+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (err < 0)
+ return err;
+
+ return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi)
+{
+ struct amdtp_dot *p = s->protocol;
+
+ if (port < MAX_MIDI_PORTS)
+ WRITE_ONCE(p->midi[port], midi);
+}
+
+static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ unsigned int pcm_frames = 0;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ if (pcm) {
+ read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks;
+ }
+
+ read_midi_messages(s, buf, data_blocks);
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+}
+
+static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ unsigned int pcm_frames = 0;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ if (pcm) {
+ write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks;
+ } else {
+ write_pcm_silence(s, buf, data_blocks);
+ }
+
+ write_midi_messages(s, buf, data_blocks,
+ desc->data_block_counter);
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+}
+
+int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir)
+{
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
+ unsigned int flags = CIP_NONBLOCKING | CIP_UNAWARE_SYT;
+
+ // Use different mode between incoming/outgoing.
+ if (dir == AMDTP_IN_STREAM)
+ process_ctx_payloads = process_ir_ctx_payloads;
+ else
+ process_ctx_payloads = process_it_ctx_payloads;
+
+ return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
+ process_ctx_payloads, sizeof(struct amdtp_dot));
+}
+
+void amdtp_dot_reset(struct amdtp_stream *s)
+{
+ struct amdtp_dot *p = s->protocol;
+
+ p->state.carry = 0x00;
+ p->state.idx = 0x00;
+ p->state.off = 0;
+}
diff --git a/sound/firewire/digi00x/digi00x-hwdep.c b/sound/firewire/digi00x/digi00x-hwdep.c
new file mode 100644
index 000000000000..435d18417cf0
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-hwdep.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * digi00x-hwdep.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ * 4.get asynchronous messaging
+ */
+
+#include "digi00x.h"
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&dg00x->lock);
+
+ while (!dg00x->dev_lock_changed && dg00x->msg == 0) {
+ prepare_to_wait(&dg00x->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&dg00x->lock);
+ schedule();
+ finish_wait(&dg00x->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&dg00x->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ if (dg00x->dev_lock_changed) {
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (dg00x->dev_lock_count > 0);
+ dg00x->dev_lock_changed = false;
+
+ count = min_t(long, count, sizeof(event.lock_status));
+ } else {
+ event.digi00x_message.type =
+ SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE;
+ event.digi00x_message.message = dg00x->msg;
+ dg00x->msg = 0;
+
+ count = min_t(long, count, sizeof(event.digi00x_message));
+ }
+
+ spin_unlock_irq(&dg00x->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+
+ poll_wait(file, &dg00x->hwdep_wait, wait);
+
+ guard(spinlock_irq)(&dg00x->lock);
+ if (dg00x->dev_lock_changed || dg00x->msg)
+ return EPOLLIN | EPOLLRDNORM;
+ else
+ return 0;
+}
+
+static int hwdep_get_info(struct snd_dg00x *dg00x, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(dg00x->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_DIGI00X;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strscpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_dg00x *dg00x)
+{
+ guard(spinlock_irq)(&dg00x->lock);
+
+ if (dg00x->dev_lock_count == 0) {
+ dg00x->dev_lock_count = -1;
+ return 0;
+ } else {
+ return -EBUSY;
+ }
+}
+
+static int hwdep_unlock(struct snd_dg00x *dg00x)
+{
+ guard(spinlock_irq)(&dg00x->lock);
+
+ if (dg00x->dev_lock_count == -1) {
+ dg00x->dev_lock_count = 0;
+ return 0;
+ } else {
+ return -EBADFD;
+ }
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+
+ guard(spinlock_irq)(&dg00x->lock);
+ if (dg00x->dev_lock_count == -1)
+ dg00x->dev_lock_count = 0;
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_dg00x *dg00x = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(dg00x, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(dg00x);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(dg00x);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x)
+{
+ static const struct snd_hwdep_ops ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(dg00x->card, "Digi00x", 0, &hwdep);
+ if (err < 0)
+ return err;
+
+ strscpy(hwdep->name, "Digi00x");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X;
+ hwdep->ops = ops;
+ hwdep->private_data = dg00x;
+ hwdep->exclusive = true;
+
+ return err;
+}
diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c
new file mode 100644
index 000000000000..bcdaf003514b
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-midi.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * digi00x-midi.h - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ */
+
+#include "digi00x.h"
+
+static int midi_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ int err;
+
+ err = snd_dg00x_stream_lock_try(dg00x);
+ if (err < 0)
+ return err;
+
+ scoped_guard(mutex, &dg00x->mutex) {
+ err = snd_dg00x_stream_reserve_duplex(dg00x, 0, 0, 0);
+ if (err >= 0) {
+ ++dg00x->substreams_counter;
+ err = snd_dg00x_stream_start_duplex(dg00x);
+ if (err < 0)
+ --dg00x->substreams_counter;
+ }
+ }
+ if (err < 0)
+ snd_dg00x_stream_lock_release(dg00x);
+
+ return err;
+}
+
+static int midi_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+
+ scoped_guard(mutex, &dg00x->mutex) {
+ --dg00x->substreams_counter;
+ snd_dg00x_stream_stop_duplex(dg00x);
+ }
+
+ snd_dg00x_stream_lock_release(dg00x);
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ unsigned int port;
+
+ if (substream->rmidi->device == 0)
+ port = substream->number;
+ else
+ port = 2;
+
+ guard(spinlock_irqsave)(&dg00x->lock);
+
+ if (up)
+ amdtp_dot_midi_trigger(&dg00x->tx_stream, port, substream);
+ else
+ amdtp_dot_midi_trigger(&dg00x->tx_stream, port, NULL);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ unsigned int port;
+
+ if (substream->rmidi->device == 0)
+ port = substream->number;
+ else
+ port = 2;
+
+ guard(spinlock_irqsave)(&dg00x->lock);
+
+ if (up)
+ amdtp_dot_midi_trigger(&dg00x->rx_stream, port, substream);
+ else
+ amdtp_dot_midi_trigger(&dg00x->rx_stream, port, NULL);
+}
+
+static void set_substream_names(struct snd_dg00x *dg00x,
+ struct snd_rawmidi *rmidi, bool is_console)
+{
+ struct snd_rawmidi_substream *subs;
+ struct snd_rawmidi_str *str;
+ int i;
+
+ for (i = 0; i < 2; ++i) {
+ str = &rmidi->streams[i];
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ if (!is_console) {
+ scnprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ dg00x->card->shortname,
+ subs->number + 1);
+ } else {
+ scnprintf(subs->name, sizeof(subs->name),
+ "%s control",
+ dg00x->card->shortname);
+ }
+ }
+ }
+}
+
+static int add_substream_pair(struct snd_dg00x *dg00x, unsigned int out_ports,
+ unsigned int in_ports, bool is_console)
+{
+ static const struct snd_rawmidi_ops capture_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_capture_trigger,
+ };
+ static const struct snd_rawmidi_ops playback_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_playback_trigger,
+ };
+ const char *label;
+ struct snd_rawmidi *rmidi;
+ int err;
+
+ /* Add physical midi ports. */
+ err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, is_console,
+ out_ports, in_ports, &rmidi);
+ if (err < 0)
+ return err;
+ rmidi->private_data = dg00x;
+
+ if (!is_console)
+ label = "%s control";
+ else
+ label = "%s MIDI";
+ snprintf(rmidi->name, sizeof(rmidi->name), label,
+ dg00x->card->shortname);
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &playback_ops);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &capture_ops);
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ set_substream_names(dg00x, rmidi, is_console);
+
+ return 0;
+}
+
+int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ /* Add physical midi ports. */
+ err = add_substream_pair(dg00x, DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS,
+ false);
+ if (err < 0)
+ return err;
+
+ if (dg00x->is_console)
+ err = add_substream_pair(dg00x, 1, 1, true);
+
+ return err;
+}
diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c
new file mode 100644
index 000000000000..75f81545d50c
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-pcm.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * digi00x-pcm.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ */
+
+#include "digi00x.h"
+
+static int hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1,
+ };
+ unsigned int i;
+
+ for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+ if (!snd_interval_test(c,
+ snd_dg00x_stream_pcm_channels[i]))
+ continue;
+
+ t.min = min(t.min, snd_dg00x_stream_rates[i]);
+ t.max = max(t.max, snd_dg00x_stream_rates[i]);
+ }
+
+ return snd_interval_refine(r, &t);
+}
+
+static int hw_rule_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1,
+ };
+ unsigned int i;
+
+ for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+ if (!snd_interval_test(r, snd_dg00x_stream_rates[i]))
+ continue;
+
+ t.min = min(t.min, snd_dg00x_stream_pcm_channels[i]);
+ t.max = max(t.max, snd_dg00x_stream_pcm_channels[i]);
+ }
+
+ return snd_interval_refine(c, &t);
+}
+
+static int pcm_init_hw_params(struct snd_dg00x *dg00x,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ struct amdtp_stream *s;
+ int err;
+
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ s = &dg00x->tx_stream;
+ } else {
+ substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ s = &dg00x->rx_stream;
+ }
+
+ hw->channels_min = 10;
+ hw->channels_max = 18;
+
+ hw->rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000;
+ snd_pcm_limit_hw_rates(runtime);
+
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels, NULL,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_rate, NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ return err;
+
+ return amdtp_dot_add_pcm_hw_constraints(s, substream->runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ struct amdtp_domain *d = &dg00x->domain;
+ enum snd_dg00x_clock clock;
+ bool detect;
+ int err;
+
+ err = snd_dg00x_stream_lock_try(dg00x);
+ if (err < 0)
+ return err;
+
+ err = pcm_init_hw_params(dg00x, substream);
+ if (err < 0)
+ goto err_locked;
+
+ /* Check current clock source. */
+ err = snd_dg00x_stream_get_clock(dg00x, &clock);
+ if (err < 0)
+ goto err_locked;
+ if (clock != SND_DG00X_CLOCK_INTERNAL) {
+ err = snd_dg00x_stream_check_external_clock(dg00x, &detect);
+ if (err < 0)
+ goto err_locked;
+ if (!detect) {
+ err = -EBUSY;
+ goto err_locked;
+ }
+ }
+
+ scoped_guard(mutex, &dg00x->mutex) {
+ // When source of clock is not internal or any stream is reserved for
+ // transmission of PCM frames, the available sampling rate is limited
+ // at current one.
+ if ((clock != SND_DG00X_CLOCK_INTERNAL) ||
+ (dg00x->substreams_counter > 0 && d->events_per_period > 0)) {
+ unsigned int frames_per_period = d->events_per_period;
+ unsigned int frames_per_buffer = d->events_per_buffer;
+ unsigned int rate;
+
+ err = snd_dg00x_stream_get_external_rate(dg00x, &rate);
+ if (err < 0)
+ goto err_locked;
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+
+ if (frames_per_period > 0) {
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ frames_per_period, frames_per_period);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ frames_per_buffer, frames_per_buffer);
+ if (err < 0)
+ goto err_locked;
+ }
+ }
+ }
+
+ snd_pcm_set_sync(substream);
+
+ return 0;
+err_locked:
+ snd_dg00x_stream_lock_release(dg00x);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ snd_dg00x_stream_lock_release(dg00x);
+
+ return 0;
+}
+
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ int err = 0;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ unsigned int rate = params_rate(hw_params);
+ unsigned int frames_per_period = params_period_size(hw_params);
+ unsigned int frames_per_buffer = params_buffer_size(hw_params);
+
+ guard(mutex)(&dg00x->mutex);
+ err = snd_dg00x_stream_reserve_duplex(dg00x, rate,
+ frames_per_period, frames_per_buffer);
+ if (err >= 0)
+ ++dg00x->substreams_counter;
+ }
+
+ return err;
+}
+
+static int pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ guard(mutex)(&dg00x->mutex);
+
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --dg00x->substreams_counter;
+
+ snd_dg00x_stream_stop_duplex(dg00x);
+
+ return 0;
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ int err;
+
+ guard(mutex)(&dg00x->mutex);
+
+ err = snd_dg00x_stream_start_duplex(dg00x);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&dg00x->tx_stream);
+
+ return err;
+}
+
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+ int err;
+
+ guard(mutex)(&dg00x->mutex);
+
+ err = snd_dg00x_stream_start_duplex(dg00x);
+ if (err >= 0) {
+ amdtp_stream_pcm_prepare(&dg00x->rx_stream);
+ amdtp_dot_reset(&dg00x->rx_stream);
+ }
+
+ return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&dg00x->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&dg00x->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&dg00x->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&dg00x->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_dg00x *dg00x = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&dg00x->domain, &dg00x->tx_stream);
+}
+
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_dg00x *dg00x = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&dg00x->domain, &dg00x->rx_stream);
+}
+
+static int pcm_capture_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&dg00x->domain, &dg00x->tx_stream);
+}
+
+static int pcm_playback_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_dg00x *dg00x = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&dg00x->domain, &dg00x->rx_stream);
+}
+
+int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
+{
+ static const struct snd_pcm_ops capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .ack = pcm_capture_ack,
+ };
+ static const struct snd_pcm_ops playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .ack = pcm_playback_ack,
+ };
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(dg00x->card, dg00x->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = dg00x;
+ pcm->nonatomic = true;
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s PCM", dg00x->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
+
+ return 0;
+}
diff --git a/sound/firewire/digi00x/digi00x-proc.c b/sound/firewire/digi00x/digi00x-proc.c
new file mode 100644
index 000000000000..00b047fefb8d
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-proc.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * digi00x-proc.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ */
+
+#include "digi00x.h"
+
+static int get_optical_iface_mode(struct snd_dg00x *dg00x,
+ enum snd_dg00x_optical_mode *mode)
+{
+ __be32 data;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_OPT_IFACE_MODE,
+ &data, sizeof(data), 0);
+ if (err >= 0)
+ *mode = be32_to_cpu(data) & 0x01;
+
+ return err;
+}
+
+static void proc_read_clock(struct snd_info_entry *entry,
+ struct snd_info_buffer *buf)
+{
+ static const char *const source_name[] = {
+ [SND_DG00X_CLOCK_INTERNAL] = "internal",
+ [SND_DG00X_CLOCK_SPDIF] = "s/pdif",
+ [SND_DG00X_CLOCK_ADAT] = "adat",
+ [SND_DG00X_CLOCK_WORD] = "word clock",
+ };
+ static const char *const optical_name[] = {
+ [SND_DG00X_OPT_IFACE_MODE_ADAT] = "adat",
+ [SND_DG00X_OPT_IFACE_MODE_SPDIF] = "s/pdif",
+ };
+ struct snd_dg00x *dg00x = entry->private_data;
+ enum snd_dg00x_optical_mode mode;
+ unsigned int rate;
+ enum snd_dg00x_clock clock;
+ bool detect;
+
+ if (get_optical_iface_mode(dg00x, &mode) < 0)
+ return;
+ if (snd_dg00x_stream_get_local_rate(dg00x, &rate) < 0)
+ return;
+ if (snd_dg00x_stream_get_clock(dg00x, &clock) < 0)
+ return;
+
+ snd_iprintf(buf, "Optical mode: %s\n", optical_name[mode]);
+ snd_iprintf(buf, "Sampling Rate: %d\n", rate);
+ snd_iprintf(buf, "Clock Source: %s\n", source_name[clock]);
+
+ if (clock == SND_DG00X_CLOCK_INTERNAL)
+ return;
+
+ if (snd_dg00x_stream_check_external_clock(dg00x, &detect) < 0)
+ return;
+ snd_iprintf(buf, "External source: %s\n", detect ? "detected" : "not");
+ if (!detect)
+ return;
+
+ if (snd_dg00x_stream_get_external_rate(dg00x, &rate) >= 0)
+ snd_iprintf(buf, "External sampling rate: %d\n", rate);
+}
+
+void snd_dg00x_proc_init(struct snd_dg00x *dg00x)
+{
+ struct snd_info_entry *root, *entry;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(dg00x->card, "firewire",
+ dg00x->card->proc_root);
+ if (root == NULL)
+ return;
+
+ root->mode = S_IFDIR | 0555;
+
+ entry = snd_info_create_card_entry(dg00x->card, "clock", root);
+ if (entry)
+ snd_info_set_text_ops(entry, dg00x, proc_read_clock);
+}
diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c
new file mode 100644
index 000000000000..250ffdb26ebd
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-stream.c
@@ -0,0 +1,450 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ */
+
+#include "digi00x.h"
+
+#define READY_TIMEOUT_MS 200
+
+const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {
+ [SND_DG00X_RATE_44100] = 44100,
+ [SND_DG00X_RATE_48000] = 48000,
+ [SND_DG00X_RATE_88200] = 88200,
+ [SND_DG00X_RATE_96000] = 96000,
+};
+
+/* Multi Bit Linear Audio data channels for each sampling transfer frequency. */
+const unsigned int
+snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = {
+ /* Analog/ADAT/SPDIF */
+ [SND_DG00X_RATE_44100] = (8 + 8 + 2),
+ [SND_DG00X_RATE_48000] = (8 + 8 + 2),
+ /* Analog/SPDIF */
+ [SND_DG00X_RATE_88200] = (8 + 2),
+ [SND_DG00X_RATE_96000] = (8 + 2),
+};
+
+int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate)
+{
+ u32 data;
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ data = be32_to_cpu(reg) & 0x0f;
+ if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
+ *rate = snd_dg00x_stream_rates[data];
+ else
+ err = -EIO;
+
+ return err;
+}
+
+int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate)
+{
+ __be32 reg;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) {
+ if (rate == snd_dg00x_stream_rates[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(snd_dg00x_stream_rates))
+ return -EINVAL;
+
+ reg = cpu_to_be32(i);
+ return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
+ &reg, sizeof(reg), 0);
+}
+
+int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
+ enum snd_dg00x_clock *clock)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ *clock = be32_to_cpu(reg) & 0x0f;
+ if (*clock >= SND_DG00X_CLOCK_COUNT)
+ err = -EIO;
+
+ return err;
+}
+
+int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL,
+ &reg, sizeof(reg), 0);
+ if (err >= 0)
+ *detect = be32_to_cpu(reg) > 0;
+
+ return err;
+}
+
+int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
+ unsigned int *rate)
+{
+ u32 data;
+ __be32 reg;
+ int err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ data = be32_to_cpu(reg) & 0x0f;
+ if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
+ *rate = snd_dg00x_stream_rates[data];
+ /* This means desync. */
+ else
+ err = -EBUSY;
+
+ return err;
+}
+
+static void finish_session(struct snd_dg00x *dg00x)
+{
+ __be32 data;
+
+ data = cpu_to_be32(0x00000003);
+ snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
+ &data, sizeof(data), 0);
+
+ // Unregister isochronous channels for both direction.
+ data = 0;
+ snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
+ &data, sizeof(data), 0);
+
+ // Just after finishing the session, the device may lost transmitting
+ // functionality for a short time.
+ msleep(50);
+}
+
+static int begin_session(struct snd_dg00x *dg00x)
+{
+ __be32 data;
+ u32 curr;
+ int err;
+
+ // Register isochronous channels for both direction.
+ data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
+ dg00x->rx_resources.channel);
+ err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return err;
+
+ err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return err;
+ curr = be32_to_cpu(data);
+
+ if (curr == 0)
+ curr = 2;
+
+ curr--;
+ while (curr > 0) {
+ data = cpu_to_be32(curr);
+ err = snd_fw_transaction(dg00x->unit,
+ TCODE_WRITE_QUADLET_REQUEST,
+ DG00X_ADDR_BASE +
+ DG00X_OFFSET_STREAMING_SET,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ break;
+
+ msleep(20);
+ curr--;
+ }
+
+ return err;
+}
+
+static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream,
+ unsigned int rate)
+{
+ struct fw_iso_resources *resources;
+ int i;
+ int err;
+
+ // Check sampling rate.
+ for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+ if (snd_dg00x_stream_rates[i] == rate)
+ break;
+ }
+ if (i == SND_DG00X_RATE_COUNT)
+ return -EINVAL;
+
+ if (stream == &dg00x->tx_stream)
+ resources = &dg00x->tx_resources;
+ else
+ resources = &dg00x->rx_resources;
+
+ err = amdtp_dot_set_parameters(stream, rate,
+ snd_dg00x_stream_pcm_channels[i]);
+ if (err < 0)
+ return err;
+
+ return fw_iso_resources_allocate(resources,
+ amdtp_stream_get_max_payload(stream),
+ fw_parent_device(dg00x->unit)->max_speed);
+}
+
+static int init_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
+{
+ struct fw_iso_resources *resources;
+ enum amdtp_stream_direction dir;
+ int err;
+
+ if (s == &dg00x->tx_stream) {
+ resources = &dg00x->tx_resources;
+ dir = AMDTP_IN_STREAM;
+ } else {
+ resources = &dg00x->rx_resources;
+ dir = AMDTP_OUT_STREAM;
+ }
+
+ err = fw_iso_resources_init(resources, dg00x->unit);
+ if (err < 0)
+ return err;
+
+ err = amdtp_dot_init(s, dg00x->unit, dir);
+ if (err < 0)
+ fw_iso_resources_destroy(resources);
+
+ return err;
+}
+
+static void destroy_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s)
+{
+ amdtp_stream_destroy(s);
+
+ if (s == &dg00x->tx_stream)
+ fw_iso_resources_destroy(&dg00x->tx_resources);
+ else
+ fw_iso_resources_destroy(&dg00x->rx_resources);
+}
+
+int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
+{
+ int err;
+
+ err = init_stream(dg00x, &dg00x->rx_stream);
+ if (err < 0)
+ return err;
+
+ err = init_stream(dg00x, &dg00x->tx_stream);
+ if (err < 0) {
+ destroy_stream(dg00x, &dg00x->rx_stream);
+ return err;
+ }
+
+ err = amdtp_domain_init(&dg00x->domain);
+ if (err < 0) {
+ destroy_stream(dg00x, &dg00x->rx_stream);
+ destroy_stream(dg00x, &dg00x->tx_stream);
+ }
+
+ return err;
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
+{
+ amdtp_domain_destroy(&dg00x->domain);
+
+ destroy_stream(dg00x, &dg00x->rx_stream);
+ destroy_stream(dg00x, &dg00x->tx_stream);
+}
+
+int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer)
+{
+ unsigned int curr_rate;
+ int err;
+
+ err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
+ if (err < 0)
+ return err;
+ if (rate == 0)
+ rate = curr_rate;
+
+ if (dg00x->substreams_counter == 0 || curr_rate != rate) {
+ amdtp_domain_stop(&dg00x->domain);
+
+ finish_session(dg00x);
+
+ fw_iso_resources_free(&dg00x->tx_resources);
+ fw_iso_resources_free(&dg00x->rx_resources);
+
+ err = snd_dg00x_stream_set_local_rate(dg00x, rate);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(dg00x, &dg00x->rx_stream, rate);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(dg00x, &dg00x->tx_stream, rate);
+ if (err < 0) {
+ fw_iso_resources_free(&dg00x->rx_resources);
+ return err;
+ }
+
+ err = amdtp_domain_set_events_per_period(&dg00x->domain,
+ frames_per_period, frames_per_buffer);
+ if (err < 0) {
+ fw_iso_resources_free(&dg00x->rx_resources);
+ fw_iso_resources_free(&dg00x->tx_resources);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
+{
+ unsigned int generation = dg00x->rx_resources.generation;
+ int err = 0;
+
+ if (dg00x->substreams_counter == 0)
+ return 0;
+
+ if (amdtp_streaming_error(&dg00x->tx_stream) ||
+ amdtp_streaming_error(&dg00x->rx_stream)) {
+ amdtp_domain_stop(&dg00x->domain);
+ finish_session(dg00x);
+ }
+
+ if (generation != fw_parent_device(dg00x->unit)->card->generation) {
+ err = fw_iso_resources_update(&dg00x->tx_resources);
+ if (err < 0)
+ goto error;
+
+ err = fw_iso_resources_update(&dg00x->rx_resources);
+ if (err < 0)
+ goto error;
+ }
+
+ /*
+ * No packets are transmitted without receiving packets, reagardless of
+ * which source of clock is used.
+ */
+ if (!amdtp_stream_running(&dg00x->rx_stream)) {
+ int spd = fw_parent_device(dg00x->unit)->max_speed;
+
+ err = begin_session(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream,
+ dg00x->rx_resources.channel, spd);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream,
+ dg00x->tx_resources.channel, spd);
+ if (err < 0)
+ goto error;
+
+ // NOTE: The device doesn't start packet transmission till receiving any packet.
+ // It ignores presentation time expressed by the value of syt field of CIP header
+ // in received packets. The sequence of the number of data blocks per packet is
+ // important for media clock recovery.
+ err = amdtp_domain_start(&dg00x->domain, 0, true, true);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_domain_wait_ready(&dg00x->domain, READY_TIMEOUT_MS)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ amdtp_domain_stop(&dg00x->domain);
+ finish_session(dg00x);
+
+ return err;
+}
+
+void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
+{
+ if (dg00x->substreams_counter == 0) {
+ amdtp_domain_stop(&dg00x->domain);
+ finish_session(dg00x);
+
+ fw_iso_resources_free(&dg00x->tx_resources);
+ fw_iso_resources_free(&dg00x->rx_resources);
+ }
+}
+
+void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
+{
+ fw_iso_resources_update(&dg00x->tx_resources);
+ fw_iso_resources_update(&dg00x->rx_resources);
+
+ amdtp_stream_update(&dg00x->tx_stream);
+ amdtp_stream_update(&dg00x->rx_stream);
+}
+
+void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)
+{
+ dg00x->dev_lock_changed = true;
+ wake_up(&dg00x->hwdep_wait);
+}
+
+int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)
+{
+ guard(spinlock_irq)(&dg00x->lock);
+
+ /* user land lock this */
+ if (dg00x->dev_lock_count < 0)
+ return -EBUSY;
+
+ /* this is the first time */
+ if (dg00x->dev_lock_count++ == 0)
+ snd_dg00x_stream_lock_changed(dg00x);
+ return 0;
+}
+
+void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)
+{
+ guard(spinlock_irq)(&dg00x->lock);
+
+ if (WARN_ON(dg00x->dev_lock_count <= 0))
+ return;
+ if (--dg00x->dev_lock_count == 0)
+ snd_dg00x_stream_lock_changed(dg00x);
+}
diff --git a/sound/firewire/digi00x/digi00x-transaction.c b/sound/firewire/digi00x/digi00x-transaction.c
new file mode 100644
index 000000000000..8a1667159930
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x-transaction.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * digi00x-transaction.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ */
+
+#include <sound/asound.h>
+#include "digi00x.h"
+
+static void handle_unknown_message(struct snd_dg00x *dg00x,
+ unsigned long long offset, __be32 *buf)
+{
+ scoped_guard(spinlock_irqsave, &dg00x->lock) {
+ dg00x->msg = be32_to_cpu(*buf);
+ }
+
+ wake_up(&dg00x->hwdep_wait);
+}
+
+static void handle_message(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_dg00x *dg00x = callback_data;
+ __be32 *buf = (__be32 *)data;
+
+ fw_send_response(card, request, RCODE_COMPLETE);
+
+ if (offset == dg00x->async_handler.offset)
+ handle_unknown_message(dg00x, offset, buf);
+}
+
+int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x)
+{
+ struct fw_device *device = fw_parent_device(dg00x->unit);
+ __be32 data[2];
+
+ /* Unknown. 4bytes. */
+ data[0] = cpu_to_be32((device->card->node_id << 16) |
+ (dg00x->async_handler.offset >> 32));
+ data[1] = cpu_to_be32(dg00x->async_handler.offset);
+ return snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
+ DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR,
+ &data, sizeof(data), 0);
+}
+
+void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
+{
+ if (dg00x->async_handler.callback_data == NULL)
+ return;
+
+ fw_core_remove_address_handler(&dg00x->async_handler);
+
+ dg00x->async_handler.callback_data = NULL;
+}
+
+int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
+{
+ static const struct fw_address_region resp_register_region = {
+ .start = 0xffffe0000000ull,
+ .end = 0xffffe000ffffull,
+ };
+ int err;
+
+ dg00x->async_handler.length = 4;
+ dg00x->async_handler.address_callback = handle_message;
+ dg00x->async_handler.callback_data = dg00x;
+
+ err = fw_core_add_address_handler(&dg00x->async_handler,
+ &resp_register_region);
+ if (err < 0)
+ return err;
+
+ err = snd_dg00x_transaction_reregister(dg00x);
+ if (err < 0)
+ snd_dg00x_transaction_unregister(dg00x);
+
+ return err;
+}
diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c
new file mode 100644
index 000000000000..f73a9fc8adb1
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * digi00x.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ */
+
+#include "digi00x.h"
+
+MODULE_DESCRIPTION("Digidesign Digi 002/003 family Driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL");
+
+#define VENDOR_DIGIDESIGN 0x00a07e
+#define MODEL_CONSOLE 0x000001
+#define MODEL_RACK 0x000002
+#define SPEC_VERSION 0x000001
+
+static int name_card(struct snd_dg00x *dg00x)
+{
+ struct fw_device *fw_dev = fw_parent_device(dg00x->unit);
+ char name[32] = {0};
+ char *model;
+ int err;
+
+ err = fw_csr_string(dg00x->unit->directory, CSR_MODEL, name,
+ sizeof(name));
+ if (err < 0)
+ return err;
+
+ model = skip_spaces(name);
+
+ strscpy(dg00x->card->driver, "Digi00x");
+ strscpy(dg00x->card->shortname, model);
+ strscpy(dg00x->card->mixername, model);
+ snprintf(dg00x->card->longname, sizeof(dg00x->card->longname),
+ "Digidesign %s, GUID %08x%08x at %s, S%d", model,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&dg00x->unit->device), 100 << fw_dev->max_speed);
+
+ return 0;
+}
+
+static void dg00x_card_free(struct snd_card *card)
+{
+ struct snd_dg00x *dg00x = card->private_data;
+
+ snd_dg00x_stream_destroy_duplex(dg00x);
+ snd_dg00x_transaction_unregister(dg00x);
+
+ mutex_destroy(&dg00x->mutex);
+ fw_unit_put(dg00x->unit);
+}
+
+static int snd_dg00x_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_dg00x *dg00x;
+ int err;
+
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*dg00x), &card);
+ if (err < 0)
+ return err;
+ card->private_free = dg00x_card_free;
+
+ dg00x = card->private_data;
+ dg00x->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, dg00x);
+ dg00x->card = card;
+
+ mutex_init(&dg00x->mutex);
+ spin_lock_init(&dg00x->lock);
+ init_waitqueue_head(&dg00x->hwdep_wait);
+
+ dg00x->is_console = entry->model_id == MODEL_CONSOLE;
+
+ err = name_card(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_stream_init_duplex(dg00x);
+ if (err < 0)
+ goto error;
+
+ snd_dg00x_proc_init(dg00x);
+
+ err = snd_dg00x_create_pcm_devices(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_create_midi_devices(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_create_hwdep_device(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_dg00x_transaction_register(dg00x);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void snd_dg00x_update(struct fw_unit *unit)
+{
+ struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
+
+ snd_dg00x_transaction_reregister(dg00x);
+
+ guard(mutex)(&dg00x->mutex);
+ snd_dg00x_stream_update_duplex(dg00x);
+}
+
+static void snd_dg00x_remove(struct fw_unit *unit)
+{
+ struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
+
+ // Block till all of ALSA character devices are released.
+ snd_card_free(dg00x->card);
+}
+
+static const struct ieee1394_device_id snd_dg00x_id_table[] = {
+ /* Both of 002/003 use the same ID. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_VERSION |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = VENDOR_DIGIDESIGN,
+ .version = SPEC_VERSION,
+ .model_id = MODEL_CONSOLE,
+ },
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_VERSION |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = VENDOR_DIGIDESIGN,
+ .version = SPEC_VERSION,
+ .model_id = MODEL_RACK,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, snd_dg00x_id_table);
+
+static struct fw_driver dg00x_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = snd_dg00x_probe,
+ .update = snd_dg00x_update,
+ .remove = snd_dg00x_remove,
+ .id_table = snd_dg00x_id_table,
+};
+
+static int __init snd_dg00x_init(void)
+{
+ return driver_register(&dg00x_driver.driver);
+}
+
+static void __exit snd_dg00x_exit(void)
+{
+ driver_unregister(&dg00x_driver.driver);
+}
+
+module_init(snd_dg00x_init);
+module_exit(snd_dg00x_exit);
diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h
new file mode 100644
index 000000000000..82b647d383c5
--- /dev/null
+++ b/sound/firewire/digi00x/digi00x.h
@@ -0,0 +1,160 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * digi00x.h - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ */
+
+#ifndef SOUND_DIGI00X_H_INCLUDED
+#define SOUND_DIGI00X_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/sched/signal.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+#include <sound/rawmidi.h>
+
+#include "../lib.h"
+#include "../iso-resources.h"
+#include "../amdtp-stream.h"
+
+struct snd_dg00x {
+ struct snd_card *card;
+ struct fw_unit *unit;
+
+ struct mutex mutex;
+ spinlock_t lock;
+
+ struct amdtp_stream tx_stream;
+ struct fw_iso_resources tx_resources;
+
+ struct amdtp_stream rx_stream;
+ struct fw_iso_resources rx_resources;
+
+ unsigned int substreams_counter;
+
+ /* for uapi */
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* For asynchronous messages. */
+ struct fw_address_handler async_handler;
+ u32 msg;
+
+ /* Console models have additional MIDI ports for control surface. */
+ bool is_console;
+
+ struct amdtp_domain domain;
+};
+
+#define DG00X_ADDR_BASE 0xffffe0000000ull
+
+#define DG00X_OFFSET_STREAMING_STATE 0x0000
+#define DG00X_OFFSET_STREAMING_SET 0x0004
+/* unknown but address in host space 0x0008 */
+/* For LSB of the address 0x000c */
+/* unknown 0x0010 */
+#define DG00X_OFFSET_MESSAGE_ADDR 0x0014
+/* For LSB of the address 0x0018 */
+/* unknown 0x001c */
+/* unknown 0x0020 */
+/* not used 0x0024--0x00ff */
+#define DG00X_OFFSET_ISOC_CHANNELS 0x0100
+/* unknown 0x0104 */
+/* unknown 0x0108 */
+/* unknown 0x010c */
+#define DG00X_OFFSET_LOCAL_RATE 0x0110
+#define DG00X_OFFSET_EXTERNAL_RATE 0x0114
+#define DG00X_OFFSET_CLOCK_SOURCE 0x0118
+#define DG00X_OFFSET_OPT_IFACE_MODE 0x011c
+/* unknown 0x0120 */
+/* Mixer control on/off 0x0124 */
+/* unknown 0x0128 */
+#define DG00X_OFFSET_DETECT_EXTERNAL 0x012c
+/* unknown 0x0138 */
+#define DG00X_OFFSET_MMC 0x0400
+
+enum snd_dg00x_rate {
+ SND_DG00X_RATE_44100 = 0,
+ SND_DG00X_RATE_48000,
+ SND_DG00X_RATE_88200,
+ SND_DG00X_RATE_96000,
+ SND_DG00X_RATE_COUNT,
+};
+
+enum snd_dg00x_clock {
+ SND_DG00X_CLOCK_INTERNAL = 0,
+ SND_DG00X_CLOCK_SPDIF,
+ SND_DG00X_CLOCK_ADAT,
+ SND_DG00X_CLOCK_WORD,
+ SND_DG00X_CLOCK_COUNT,
+};
+
+enum snd_dg00x_optical_mode {
+ SND_DG00X_OPT_IFACE_MODE_ADAT = 0,
+ SND_DG00X_OPT_IFACE_MODE_SPDIF,
+ SND_DG00X_OPT_IFACE_MODE_COUNT,
+};
+
+#define DOT_MIDI_IN_PORTS 1
+#define DOT_MIDI_OUT_PORTS 2
+
+int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir);
+int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels);
+void amdtp_dot_reset(struct amdtp_stream *s);
+int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi);
+
+int snd_dg00x_transaction_register(struct snd_dg00x *dg00x);
+int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x);
+void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x);
+
+extern const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT];
+extern const unsigned int snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT];
+int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
+ unsigned int *rate);
+int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x,
+ unsigned int *rate);
+int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate);
+int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
+ enum snd_dg00x_clock *clock);
+int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x,
+ bool *detect);
+int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x);
+int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer);
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x);
+
+void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x);
+int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x);
+
+void snd_dg00x_proc_init(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x);
+#endif
diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c
new file mode 100644
index 000000000000..e60bfd0ee4ac
--- /dev/null
+++ b/sound/firewire/fcp.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Function Control Protocol (IEC 61883-1) helper functions
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include "fcp.h"
+#include "lib.h"
+#include "amdtp-stream.h"
+
+#define CTS_AVC 0x00
+
+#define ERROR_RETRIES 3
+#define ERROR_DELAY_MS 5
+#define FCP_TIMEOUT_MS 125
+
+int avc_general_set_sig_fmt(struct fw_unit *unit, unsigned int rate,
+ enum avc_general_plug_dir dir,
+ unsigned short pid)
+{
+ unsigned int sfc;
+ u8 *buf;
+ bool flag;
+ int err;
+
+ flag = false;
+ for (sfc = 0; sfc < CIP_SFC_COUNT; sfc++) {
+ if (amdtp_rate_table[sfc] == rate) {
+ flag = true;
+ break;
+ }
+ }
+ if (!flag)
+ return -EINVAL;
+
+ buf = kzalloc(8, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x00; /* AV/C CONTROL */
+ buf[1] = 0xff; /* UNIT */
+ if (dir == AVC_GENERAL_PLUG_DIR_IN)
+ buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */
+ else
+ buf[2] = 0x18; /* OUTPUT PLUG SIGNAL FORMAT */
+ buf[3] = 0xff & pid; /* plug id */
+ buf[4] = 0x90; /* EOH_1, Form_1, FMT. AM824 */
+ buf[5] = 0x07 & sfc; /* FDF-hi. AM824, frequency */
+ buf[6] = 0xff; /* FDF-mid. AM824, SYT hi (not used)*/
+ buf[7] = 0xff; /* FDF-low. AM824, SYT lo (not used) */
+
+ /* do transaction and check buf[1-5] are the same against command */
+ err = fcp_avc_transaction(unit, buf, 8, buf, 8,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
+ if (err < 0)
+ ;
+ else if (err < 8)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ if (err < 0)
+ goto end;
+
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+EXPORT_SYMBOL(avc_general_set_sig_fmt);
+
+int avc_general_get_sig_fmt(struct fw_unit *unit, unsigned int *rate,
+ enum avc_general_plug_dir dir,
+ unsigned short pid)
+{
+ unsigned int sfc;
+ u8 *buf;
+ int err;
+
+ buf = kzalloc(8, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x01; /* AV/C STATUS */
+ buf[1] = 0xff; /* Unit */
+ if (dir == AVC_GENERAL_PLUG_DIR_IN)
+ buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */
+ else
+ buf[2] = 0x18; /* OUTPUT PLUG SIGNAL FORMAT */
+ buf[3] = 0xff & pid; /* plug id */
+ buf[4] = 0x90; /* EOH_1, Form_1, FMT. AM824 */
+ buf[5] = 0xff; /* FDF-hi. AM824, frequency */
+ buf[6] = 0xff; /* FDF-mid. AM824, SYT hi (not used) */
+ buf[7] = 0xff; /* FDF-low. AM824, SYT lo (not used) */
+
+ /* do transaction and check buf[1-4] are the same against command */
+ err = fcp_avc_transaction(unit, buf, 8, buf, 8,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4));
+ if (err < 0)
+ ;
+ else if (err < 8)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ /* check sfc field and pick up rate */
+ sfc = 0x07 & buf[5];
+ if (sfc >= CIP_SFC_COUNT) {
+ err = -EAGAIN; /* also in transition */
+ goto end;
+ }
+
+ *rate = amdtp_rate_table[sfc];
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+EXPORT_SYMBOL(avc_general_get_sig_fmt);
+
+int avc_general_get_plug_info(struct fw_unit *unit, unsigned int subunit_type,
+ unsigned int subunit_id, unsigned int subfunction,
+ u8 info[AVC_PLUG_INFO_BUF_BYTES])
+{
+ u8 *buf;
+ int err;
+
+ /* extended subunit in spec.4.2 is not supported */
+ if ((subunit_type == 0x1E) || (subunit_id == 5))
+ return -EINVAL;
+
+ buf = kzalloc(8, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x01; /* AV/C STATUS */
+ /* UNIT or Subunit, Functionblock */
+ buf[1] = ((subunit_type & 0x1f) << 3) | (subunit_id & 0x7);
+ buf[2] = 0x02; /* PLUG INFO */
+ buf[3] = 0xff & subfunction;
+
+ err = fcp_avc_transaction(unit, buf, 8, buf, 8, BIT(1) | BIT(2));
+ if (err < 0)
+ ;
+ else if (err < 8)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ info[0] = buf[4];
+ info[1] = buf[5];
+ info[2] = buf[6];
+ info[3] = buf[7];
+
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+EXPORT_SYMBOL(avc_general_get_plug_info);
+
+static DEFINE_SPINLOCK(transactions_lock);
+static LIST_HEAD(transactions);
+
+enum fcp_state {
+ STATE_PENDING,
+ STATE_BUS_RESET,
+ STATE_COMPLETE,
+ STATE_DEFERRED,
+};
+
+struct fcp_transaction {
+ struct list_head list;
+ struct fw_unit *unit;
+ void *response_buffer;
+ unsigned int response_size;
+ unsigned int response_match_bytes;
+ enum fcp_state state;
+ wait_queue_head_t wait;
+ bool deferrable;
+};
+
+/**
+ * fcp_avc_transaction - send an AV/C command and wait for its response
+ * @unit: a unit on the target device
+ * @command: a buffer containing the command frame; must be DMA-able
+ * @command_size: the size of @command
+ * @response: a buffer for the response frame
+ * @response_size: the maximum size of @response
+ * @response_match_bytes: a bitmap specifying the bytes used to detect the
+ * correct response frame
+ *
+ * This function sends a FCP command frame to the target and waits for the
+ * corresponding response frame to be returned.
+ *
+ * Because it is possible for multiple FCP transactions to be active at the
+ * same time, the correct response frame is detected by the value of certain
+ * bytes. These bytes must be set in @response before calling this function,
+ * and the corresponding bits must be set in @response_match_bytes.
+ *
+ * @command and @response can point to the same buffer.
+ *
+ * Returns the actual size of the response frame, or a negative error code.
+ */
+int fcp_avc_transaction(struct fw_unit *unit,
+ const void *command, unsigned int command_size,
+ void *response, unsigned int response_size,
+ unsigned int response_match_bytes)
+{
+ struct fcp_transaction t;
+ int tcode, ret, tries = 0;
+
+ t.unit = unit;
+ t.response_buffer = response;
+ t.response_size = response_size;
+ t.response_match_bytes = response_match_bytes;
+ t.state = STATE_PENDING;
+ init_waitqueue_head(&t.wait);
+ t.deferrable = (*(const u8 *)command == 0x00 || *(const u8 *)command == 0x03);
+
+ scoped_guard(spinlock_irq, &transactions_lock) {
+ list_add_tail(&t.list, &transactions);
+ }
+
+ for (;;) {
+ tcode = command_size == 4 ? TCODE_WRITE_QUADLET_REQUEST
+ : TCODE_WRITE_BLOCK_REQUEST;
+ ret = snd_fw_transaction(t.unit, tcode,
+ CSR_REGISTER_BASE + CSR_FCP_COMMAND,
+ (void *)command, command_size, 0);
+ if (ret < 0)
+ break;
+deferred:
+ wait_event_timeout(t.wait, t.state != STATE_PENDING,
+ msecs_to_jiffies(FCP_TIMEOUT_MS));
+
+ if (t.state == STATE_DEFERRED) {
+ /*
+ * 'AV/C General Specification' define no time limit
+ * on command completion once an INTERIM response has
+ * been sent. but we promise to finish this function
+ * for a caller. Here we use FCP_TIMEOUT_MS for next
+ * interval. This is not in the specification.
+ */
+ t.state = STATE_PENDING;
+ goto deferred;
+ } else if (t.state == STATE_COMPLETE) {
+ ret = t.response_size;
+ break;
+ } else if (t.state == STATE_BUS_RESET) {
+ msleep(ERROR_DELAY_MS);
+ } else if (++tries >= ERROR_RETRIES) {
+ dev_err(&t.unit->device, "FCP command timed out\n");
+ ret = -EIO;
+ break;
+ }
+ }
+
+ scoped_guard(spinlock_irq, &transactions_lock) {
+ list_del(&t.list);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(fcp_avc_transaction);
+
+/**
+ * fcp_bus_reset - inform the target handler about a bus reset
+ * @unit: the unit that might be used by fcp_avc_transaction()
+ *
+ * This function must be called from the driver's .update handler to inform
+ * the FCP transaction handler that a bus reset has happened. Any pending FCP
+ * transactions are retried.
+ */
+void fcp_bus_reset(struct fw_unit *unit)
+{
+ struct fcp_transaction *t;
+
+ guard(spinlock_irq)(&transactions_lock);
+ list_for_each_entry(t, &transactions, list) {
+ if (t->unit == unit &&
+ (t->state == STATE_PENDING ||
+ t->state == STATE_DEFERRED)) {
+ t->state = STATE_BUS_RESET;
+ wake_up(&t->wait);
+ }
+ }
+}
+EXPORT_SYMBOL(fcp_bus_reset);
+
+/* checks whether the response matches the masked bytes in response_buffer */
+static bool is_matching_response(struct fcp_transaction *transaction,
+ const void *response, size_t length)
+{
+ const u8 *p1, *p2;
+ unsigned int mask, i;
+
+ p1 = response;
+ p2 = transaction->response_buffer;
+ mask = transaction->response_match_bytes;
+
+ for (i = 0; ; ++i) {
+ if ((mask & 1) && p1[i] != p2[i])
+ return false;
+ mask >>= 1;
+ if (!mask)
+ return true;
+ if (--length == 0)
+ return false;
+ }
+}
+
+static void fcp_response(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct fcp_transaction *t;
+
+ if (length < 1 || (*(const u8 *)data & 0xf0) != CTS_AVC)
+ return;
+
+ guard(spinlock_irqsave)(&transactions_lock);
+ list_for_each_entry(t, &transactions, list) {
+ struct fw_device *device = fw_parent_device(t->unit);
+ if (device->card != card ||
+ device->generation != generation)
+ continue;
+ smp_rmb(); /* node_id vs. generation */
+ if (device->node_id != source)
+ continue;
+
+ if (t->state == STATE_PENDING &&
+ is_matching_response(t, data, length)) {
+ if (t->deferrable && *(const u8 *)data == 0x0f) {
+ t->state = STATE_DEFERRED;
+ } else {
+ t->state = STATE_COMPLETE;
+ t->response_size = min_t(unsigned int, length,
+ t->response_size);
+ memcpy(t->response_buffer, data,
+ t->response_size);
+ }
+ wake_up(&t->wait);
+ }
+ }
+}
+
+static struct fw_address_handler response_register_handler = {
+ .length = 0x200,
+ .address_callback = fcp_response,
+};
+
+static int __init fcp_module_init(void)
+{
+ static const struct fw_address_region response_register_region = {
+ .start = CSR_REGISTER_BASE + CSR_FCP_RESPONSE,
+ .end = CSR_REGISTER_BASE + CSR_FCP_END,
+ };
+
+ fw_core_add_address_handler(&response_register_handler,
+ &response_register_region);
+
+ return 0;
+}
+
+static void __exit fcp_module_exit(void)
+{
+ WARN_ON(!list_empty(&transactions));
+ fw_core_remove_address_handler(&response_register_handler);
+}
+
+module_init(fcp_module_init);
+module_exit(fcp_module_exit);
diff --git a/sound/firewire/fcp.h b/sound/firewire/fcp.h
new file mode 100644
index 000000000000..512f7c40903a
--- /dev/null
+++ b/sound/firewire/fcp.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef SOUND_FIREWIRE_FCP_H_INCLUDED
+#define SOUND_FIREWIRE_FCP_H_INCLUDED
+
+#define AVC_PLUG_INFO_BUF_BYTES 4
+
+struct fw_unit;
+
+/*
+ * AV/C Digital Interface Command Set General Specification 4.2
+ * (Sep 2004, 1394TA)
+ */
+enum avc_general_plug_dir {
+ AVC_GENERAL_PLUG_DIR_IN = 0,
+ AVC_GENERAL_PLUG_DIR_OUT = 1,
+ AVC_GENERAL_PLUG_DIR_COUNT
+};
+int avc_general_set_sig_fmt(struct fw_unit *unit, unsigned int rate,
+ enum avc_general_plug_dir dir,
+ unsigned short plug);
+int avc_general_get_sig_fmt(struct fw_unit *unit, unsigned int *rate,
+ enum avc_general_plug_dir dir,
+ unsigned short plug);
+int avc_general_get_plug_info(struct fw_unit *unit, unsigned int subunit_type,
+ unsigned int subunit_id, unsigned int subfunction,
+ u8 info[AVC_PLUG_INFO_BUF_BYTES]);
+
+int fcp_avc_transaction(struct fw_unit *unit,
+ const void *command, unsigned int command_size,
+ void *response, unsigned int response_size,
+ unsigned int response_match_bytes);
+void fcp_bus_reset(struct fw_unit *unit);
+
+#endif
diff --git a/sound/firewire/fireface/Makefile b/sound/firewire/fireface/Makefile
new file mode 100644
index 000000000000..b397d95877a0
--- /dev/null
+++ b/sound/firewire/fireface/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-fireface-y := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \
+ ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-former.o \
+ ff-protocol-latter.o
+obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o
diff --git a/sound/firewire/fireface/amdtp-ff.c b/sound/firewire/fireface/amdtp-ff.c
new file mode 100644
index 000000000000..76c9d33ed572
--- /dev/null
+++ b/sound/firewire/fireface/amdtp-ff.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * amdtp-ff.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ */
+
+#include <sound/pcm.h>
+#include "ff.h"
+
+struct amdtp_ff {
+ unsigned int pcm_channels;
+};
+
+int amdtp_ff_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels)
+{
+ struct amdtp_ff *p = s->protocol;
+ unsigned int data_channels;
+
+ if (amdtp_stream_running(s))
+ return -EBUSY;
+
+ p->pcm_channels = pcm_channels;
+ data_channels = pcm_channels;
+
+ return amdtp_stream_set_parameters(s, rate, data_channels, 1);
+}
+
+static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __le32 *buffer, unsigned int frames,
+ unsigned int pcm_frames)
+{
+ struct amdtp_ff *p = s->protocol;
+ unsigned int channels = p->pcm_channels;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ const u32 *src;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_le32(*src);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __le32 *buffer, unsigned int frames,
+ unsigned int pcm_frames)
+{
+ struct amdtp_ff *p = s->protocol;
+ unsigned int channels = p->pcm_channels;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ u32 *dst;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ *dst = le32_to_cpu(buffer[c]) & 0xffffff00;
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s,
+ __le32 *buffer, unsigned int frames)
+{
+ struct amdtp_ff *p = s->protocol;
+ unsigned int i, c, channels = p->pcm_channels;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c)
+ buffer[c] = cpu_to_le32(0x00000000);
+ buffer += s->data_block_quadlets;
+ }
+}
+
+int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (err < 0)
+ return err;
+
+ return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ unsigned int pcm_frames = 0;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ __le32 *buf = (__le32 *)desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ if (pcm) {
+ write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks;
+ } else {
+ write_pcm_silence(s, buf, data_blocks);
+ }
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+}
+
+static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ unsigned int pcm_frames = 0;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ __le32 *buf = (__le32 *)desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ if (pcm) {
+ read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks;
+ }
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+}
+
+int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir)
+{
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
+
+ if (dir == AMDTP_IN_STREAM)
+ process_ctx_payloads = process_ir_ctx_payloads;
+ else
+ process_ctx_payloads = process_it_ctx_payloads;
+
+ return amdtp_stream_init(s, unit, dir, CIP_BLOCKING | CIP_UNAWARE_SYT | CIP_NO_HEADER, 0,
+ process_ctx_payloads, sizeof(struct amdtp_ff));
+}
diff --git a/sound/firewire/fireface/ff-hwdep.c b/sound/firewire/fireface/ff-hwdep.c
new file mode 100644
index 000000000000..5976abf2e1ab
--- /dev/null
+++ b/sound/firewire/fireface/ff-hwdep.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ff-hwdep.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "ff.h"
+
+static bool has_msg(struct snd_ff *ff)
+{
+ if (ff->spec->protocol->has_msg)
+ return ff->spec->protocol->has_msg(ff);
+ else
+ return 0;
+}
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_ff *ff = hwdep->private_data;
+ DEFINE_WAIT(wait);
+
+ spin_lock_irq(&ff->lock);
+
+ while (!ff->dev_lock_changed && !has_msg(ff)) {
+ prepare_to_wait(&ff->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&ff->lock);
+ schedule();
+ finish_wait(&ff->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&ff->lock);
+ }
+
+ if (ff->dev_lock_changed && count >= sizeof(struct snd_firewire_event_lock_status)) {
+ struct snd_firewire_event_lock_status ev = {
+ .type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
+ .status = (ff->dev_lock_count > 0),
+ };
+
+ ff->dev_lock_changed = false;
+
+ spin_unlock_irq(&ff->lock);
+
+ if (copy_to_user(buf, &ev, sizeof(ev)))
+ return -EFAULT;
+ count = sizeof(ev);
+ } else if (has_msg(ff)) {
+ // NOTE: Acquired spin lock should be released before accessing to user space in the
+ // callback since the access can cause page fault.
+ count = ff->spec->protocol->copy_msg_to_user(ff, buf, count);
+ spin_unlock_irq(&ff->lock);
+ } else {
+ spin_unlock_irq(&ff->lock);
+
+ count = 0;
+ }
+
+ return count;
+}
+
+static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_ff *ff = hwdep->private_data;
+
+ poll_wait(file, &ff->hwdep_wait, wait);
+
+ guard(spinlock_irq)(&ff->lock);
+ if (ff->dev_lock_changed || has_msg(ff))
+ return EPOLLIN | EPOLLRDNORM;
+ else
+ return 0;
+}
+
+static int hwdep_get_info(struct snd_ff *ff, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(ff->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_FIREFACE;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strscpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_ff *ff)
+{
+ guard(spinlock_irq)(&ff->lock);
+
+ if (ff->dev_lock_count == 0) {
+ ff->dev_lock_count = -1;
+ return 0;
+ } else {
+ return -EBUSY;
+ }
+}
+
+static int hwdep_unlock(struct snd_ff *ff)
+{
+ guard(spinlock_irq)(&ff->lock);
+
+ if (ff->dev_lock_count == -1) {
+ ff->dev_lock_count = 0;
+ return 0;
+ } else {
+ return -EBADFD;
+ }
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_ff *ff = hwdep->private_data;
+
+ guard(spinlock_irq)(&ff->lock);
+ if (ff->dev_lock_count == -1)
+ ff->dev_lock_count = 0;
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_ff *ff = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(ff, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(ff);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(ff);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_ff_create_hwdep_devices(struct snd_ff *ff)
+{
+ static const struct snd_hwdep_ops hwdep_ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(ff->card, ff->card->driver, 0, &hwdep);
+ if (err < 0)
+ return err;
+
+ strscpy(hwdep->name, ff->card->driver);
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREFACE;
+ hwdep->ops = hwdep_ops;
+ hwdep->private_data = ff;
+ hwdep->exclusive = true;
+
+ return 0;
+}
diff --git a/sound/firewire/fireface/ff-midi.c b/sound/firewire/fireface/ff-midi.c
new file mode 100644
index 000000000000..9f6aa490e5bf
--- /dev/null
+++ b/sound/firewire/fireface/ff-midi.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ff-midi.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ */
+
+#include "ff.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ff *ff = substream->rmidi->private_data;
+
+ /* Initialize internal status. */
+ ff->on_sysex[substream->number] = 0;
+ ff->rx_midi_error[substream->number] = false;
+
+ WRITE_ONCE(ff->rx_midi_substreams[substream->number], substream);
+
+ return 0;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ff *ff = substream->rmidi->private_data;
+
+ cancel_work_sync(&ff->rx_midi_work[substream->number]);
+ WRITE_ONCE(ff->rx_midi_substreams[substream->number], NULL);
+
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_ff *ff = substream->rmidi->private_data;
+
+ guard(spinlock_irqsave)(&ff->lock);
+
+ if (up)
+ WRITE_ONCE(ff->tx_midi_substreams[substream->number],
+ substream);
+ else
+ WRITE_ONCE(ff->tx_midi_substreams[substream->number], NULL);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_ff *ff = substream->rmidi->private_data;
+
+ guard(spinlock_irqsave)(&ff->lock);
+
+ if (up || !ff->rx_midi_error[substream->number])
+ schedule_work(&ff->rx_midi_work[substream->number]);
+}
+
+static void set_midi_substream_names(struct snd_rawmidi_str *stream,
+ const char *const name)
+{
+ struct snd_rawmidi_substream *substream;
+
+ list_for_each_entry(substream, &stream->substreams, list) {
+ scnprintf(substream->name, sizeof(substream->name),
+ "%s MIDI %d", name, substream->number + 1);
+ }
+}
+
+int snd_ff_create_midi_devices(struct snd_ff *ff)
+{
+ static const struct snd_rawmidi_ops midi_capture_ops = {
+ .open = midi_capture_open,
+ .close = midi_capture_close,
+ .trigger = midi_capture_trigger,
+ };
+ static const struct snd_rawmidi_ops midi_playback_ops = {
+ .open = midi_playback_open,
+ .close = midi_playback_close,
+ .trigger = midi_playback_trigger,
+ };
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *stream;
+ int err;
+
+ err = snd_rawmidi_new(ff->card, ff->card->driver, 0,
+ ff->spec->midi_out_ports, ff->spec->midi_in_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", ff->card->shortname);
+ rmidi->private_data = ff;
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &midi_capture_ops);
+ stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+ set_midi_substream_names(stream, ff->card->shortname);
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &midi_playback_ops);
+ stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+ set_midi_substream_names(stream, ff->card->shortname);
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c
new file mode 100644
index 000000000000..7ad8204fbfe8
--- /dev/null
+++ b/sound/firewire/fireface/ff-pcm.c
@@ -0,0 +1,387 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ff-pcm.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ */
+
+#include "ff.h"
+
+static int hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ const unsigned int *pcm_channels = rule->private;
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
+ enum snd_ff_stream_mode mode;
+ int err;
+
+ err = snd_ff_stream_get_multiplier_mode(i, &mode);
+ if (err < 0)
+ continue;
+
+ if (!snd_interval_test(c, pcm_channels[mode]))
+ continue;
+
+ t.min = min(t.min, amdtp_rate_table[i]);
+ t.max = max(t.max, amdtp_rate_table[i]);
+ }
+
+ return snd_interval_refine(r, &t);
+}
+
+static int hw_rule_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ const unsigned int *pcm_channels = rule->private;
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
+ enum snd_ff_stream_mode mode;
+ int err;
+
+ err = snd_ff_stream_get_multiplier_mode(i, &mode);
+ if (err < 0)
+ continue;
+
+ if (!snd_interval_test(r, amdtp_rate_table[i]))
+ continue;
+
+ t.min = min(t.min, pcm_channels[mode]);
+ t.max = max(t.max, pcm_channels[mode]);
+ }
+
+ return snd_interval_refine(c, &t);
+}
+
+static void limit_channels_and_rates(struct snd_pcm_hardware *hw,
+ const unsigned int *pcm_channels)
+{
+ unsigned int rate, channels;
+ int i;
+
+ hw->channels_min = UINT_MAX;
+ hw->channels_max = 0;
+ hw->rate_min = UINT_MAX;
+ hw->rate_max = 0;
+
+ for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
+ enum snd_ff_stream_mode mode;
+ int err;
+
+ err = snd_ff_stream_get_multiplier_mode(i, &mode);
+ if (err < 0)
+ continue;
+
+ channels = pcm_channels[mode];
+ if (pcm_channels[mode] == 0)
+ continue;
+ hw->channels_min = min(hw->channels_min, channels);
+ hw->channels_max = max(hw->channels_max, channels);
+
+ rate = amdtp_rate_table[i];
+ hw->rates |= snd_pcm_rate_to_rate_bit(rate);
+ hw->rate_min = min(hw->rate_min, rate);
+ hw->rate_max = max(hw->rate_max, rate);
+ }
+}
+
+static int pcm_init_hw_params(struct snd_ff *ff,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct amdtp_stream *s;
+ const unsigned int *pcm_channels;
+ int err;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ s = &ff->tx_stream;
+ pcm_channels = ff->spec->pcm_capture_channels;
+ } else {
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ s = &ff->rx_stream;
+ pcm_channels = ff->spec->pcm_playback_channels;
+ }
+
+ limit_channels_and_rates(&runtime->hw, pcm_channels);
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels, (void *)pcm_channels,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_rate, (void *)pcm_channels,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ return err;
+
+ return amdtp_ff_add_pcm_hw_constraints(s, runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+ struct amdtp_domain *d = &ff->domain;
+ unsigned int rate;
+ enum snd_ff_clock_src src;
+ int i, err;
+
+ err = snd_ff_stream_lock_try(ff);
+ if (err < 0)
+ return err;
+
+ err = pcm_init_hw_params(ff, substream);
+ if (err < 0)
+ goto release_lock;
+
+ err = ff->spec->protocol->get_clock(ff, &rate, &src);
+ if (err < 0)
+ goto release_lock;
+
+ scoped_guard(mutex, &ff->mutex) {
+ // When source of clock is not internal or any stream is reserved for
+ // transmission of PCM frames, the available sampling rate is limited
+ // at current one.
+ if (src != SND_FF_CLOCK_SRC_INTERNAL) {
+ for (i = 0; i < CIP_SFC_COUNT; ++i) {
+ if (amdtp_rate_table[i] == rate)
+ break;
+ }
+
+ // The unit is configured at sampling frequency which packet
+ // streaming engine can't support.
+ if (i >= CIP_SFC_COUNT) {
+ err = -EIO;
+ goto release_lock;
+ }
+
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+ } else {
+ if (ff->substreams_counter > 0) {
+ unsigned int frames_per_period = d->events_per_period;
+ unsigned int frames_per_buffer = d->events_per_buffer;
+
+ rate = amdtp_rate_table[ff->rx_stream.sfc];
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ frames_per_period, frames_per_period);
+ if (err < 0)
+ goto release_lock;
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ frames_per_buffer, frames_per_buffer);
+ if (err < 0)
+ goto release_lock;
+ }
+ }
+ }
+
+ snd_pcm_set_sync(substream);
+
+ return 0;
+
+release_lock:
+ snd_ff_stream_lock_release(ff);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+
+ snd_ff_stream_lock_release(ff);
+
+ return 0;
+}
+
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_ff *ff = substream->private_data;
+ int err = 0;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ unsigned int rate = params_rate(hw_params);
+ unsigned int frames_per_period = params_period_size(hw_params);
+ unsigned int frames_per_buffer = params_buffer_size(hw_params);
+
+ guard(mutex)(&ff->mutex);
+ err = snd_ff_stream_reserve_duplex(ff, rate, frames_per_period,
+ frames_per_buffer);
+ if (err >= 0)
+ ++ff->substreams_counter;
+ }
+
+ return err;
+}
+
+static int pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+
+ guard(mutex)(&ff->mutex);
+
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --ff->substreams_counter;
+
+ snd_ff_stream_stop_duplex(ff);
+
+ return 0;
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ guard(mutex)(&ff->mutex);
+
+ err = snd_ff_stream_start_duplex(ff, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&ff->tx_stream);
+
+ return err;
+}
+
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ guard(mutex)(&ff->mutex);
+
+ err = snd_ff_stream_start_duplex(ff, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&ff->rx_stream);
+
+ return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_ff *ff = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&ff->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&ff->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_ff *ff = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&ff->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&ff->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_ff *ff = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&ff->domain, &ff->tx_stream);
+}
+
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_ff *ff = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&ff->domain, &ff->rx_stream);
+}
+
+static int pcm_capture_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&ff->domain, &ff->tx_stream);
+}
+
+static int pcm_playback_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&ff->domain, &ff->rx_stream);
+}
+
+int snd_ff_create_pcm_devices(struct snd_ff *ff)
+{
+ static const struct snd_pcm_ops pcm_capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .ack = pcm_capture_ack,
+ };
+ static const struct snd_pcm_ops pcm_playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .ack = pcm_playback_ack,
+ };
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(ff->card, ff->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = ff;
+ pcm->nonatomic = true;
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s PCM", ff->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
+
+ return 0;
+}
diff --git a/sound/firewire/fireface/ff-proc.c b/sound/firewire/fireface/ff-proc.c
new file mode 100644
index 000000000000..4aecc8dcbb99
--- /dev/null
+++ b/sound/firewire/fireface/ff-proc.c
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ff-proc.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ */
+
+#include "./ff.h"
+
+const char *snd_ff_proc_get_clk_label(enum snd_ff_clock_src src)
+{
+ static const char *const labels[] = {
+ "Internal",
+ "S/PDIF",
+ "ADAT1",
+ "ADAT2",
+ "Word",
+ "LTC",
+ };
+
+ if (src >= ARRAY_SIZE(labels))
+ return NULL;
+
+ return labels[src];
+}
+
+static void proc_dump_status(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_ff *ff = entry->private_data;
+
+ ff->spec->protocol->dump_status(ff, buffer);
+}
+
+static void add_node(struct snd_ff *ff, struct snd_info_entry *root,
+ const char *name,
+ void (*op)(struct snd_info_entry *e,
+ struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(ff->card, name, root);
+ if (entry)
+ snd_info_set_text_ops(entry, ff, op);
+}
+
+void snd_ff_proc_init(struct snd_ff *ff)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(ff->card, "firewire",
+ ff->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | 0555;
+
+ add_node(ff, root, "status", proc_dump_status);
+}
diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c
new file mode 100644
index 000000000000..0907d0a2296f
--- /dev/null
+++ b/sound/firewire/fireface/ff-protocol-former.c
@@ -0,0 +1,733 @@
+// SPDX-License-Identifier: GPL-2.0
+// ff-protocol-former.c - a part of driver for RME Fireface series
+//
+// Copyright (c) 2019 Takashi Sakamoto
+
+#include <linux/delay.h>
+
+#include "ff.h"
+
+#define FORMER_REG_SYNC_STATUS 0x0000801c0000ull
+/* For block write request. */
+#define FORMER_REG_FETCH_PCM_FRAMES 0x0000801c0000ull
+#define FORMER_REG_CLOCK_CONFIG 0x0000801c0004ull
+
+static int parse_clock_bits(u32 data, unsigned int *rate,
+ enum snd_ff_clock_src *src)
+{
+ static const struct {
+ unsigned int rate;
+ u32 mask;
+ } *rate_entry, rate_entries[] = {
+ { 32000, 0x00000002, },
+ { 44100, 0x00000000, },
+ { 48000, 0x00000006, },
+ { 64000, 0x0000000a, },
+ { 88200, 0x00000008, },
+ { 96000, 0x0000000e, },
+ { 128000, 0x00000012, },
+ { 176400, 0x00000010, },
+ { 192000, 0x00000016, },
+ };
+ static const struct {
+ enum snd_ff_clock_src src;
+ u32 mask;
+ } *clk_entry, clk_entries[] = {
+ { SND_FF_CLOCK_SRC_ADAT1, 0x00000000, },
+ { SND_FF_CLOCK_SRC_ADAT2, 0x00000400, },
+ { SND_FF_CLOCK_SRC_SPDIF, 0x00000c00, },
+ { SND_FF_CLOCK_SRC_WORD, 0x00001000, },
+ { SND_FF_CLOCK_SRC_LTC, 0x00001800, },
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) {
+ rate_entry = rate_entries + i;
+ if ((data & 0x0000001e) == rate_entry->mask) {
+ *rate = rate_entry->rate;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(rate_entries))
+ return -EIO;
+
+ if (data & 0x00000001) {
+ *src = SND_FF_CLOCK_SRC_INTERNAL;
+ } else {
+ for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) {
+ clk_entry = clk_entries + i;
+ if ((data & 0x00001c00) == clk_entry->mask) {
+ *src = clk_entry->src;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(clk_entries))
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int former_get_clock(struct snd_ff *ff, unsigned int *rate,
+ enum snd_ff_clock_src *src)
+{
+ __le32 reg;
+ u32 data;
+ int err;
+
+ err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
+ FORMER_REG_CLOCK_CONFIG, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+ data = le32_to_cpu(reg);
+
+ return parse_clock_bits(data, rate, src);
+}
+
+static int former_switch_fetching_mode(struct snd_ff *ff, bool enable)
+{
+ unsigned int count;
+ __le32 *reg;
+ int i;
+ int err;
+
+ count = 0;
+ for (i = 0; i < SND_FF_STREAM_MODE_COUNT; ++i)
+ count = max(count, ff->spec->pcm_playback_channels[i]);
+
+ reg = kcalloc(count, sizeof(__le32), GFP_KERNEL);
+ if (!reg)
+ return -ENOMEM;
+
+ if (!enable) {
+ /*
+ * Each quadlet is corresponding to data channels in a data
+ * blocks in reverse order. Precisely, quadlets for available
+ * data channels should be enabled. Here, I take second best
+ * to fetch PCM frames from all of data channels regardless of
+ * stf.
+ */
+ for (i = 0; i < count; ++i)
+ reg[i] = cpu_to_le32(0x00000001);
+ }
+
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST,
+ FORMER_REG_FETCH_PCM_FRAMES, reg,
+ sizeof(__le32) * count, 0);
+ kfree(reg);
+ return err;
+}
+
+static void dump_clock_config(struct snd_ff *ff, struct snd_info_buffer *buffer)
+{
+ __le32 reg;
+ u32 data;
+ unsigned int rate;
+ enum snd_ff_clock_src src;
+ const char *label;
+ int err;
+
+ err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST,
+ FORMER_REG_CLOCK_CONFIG, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return;
+ data = le32_to_cpu(reg);
+
+ snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n",
+ (data & 0x00000020) ? "Professional" : "Consumer",
+ str_on_off(data & 0x00000040));
+
+ snd_iprintf(buffer, "Optical output interface format: %s\n",
+ (data & 0x00000100) ? "S/PDIF" : "ADAT");
+
+ snd_iprintf(buffer, "Word output single speed: %s\n",
+ str_on_off(data & 0x00002000));
+
+ snd_iprintf(buffer, "S/PDIF input interface: %s\n",
+ (data & 0x00000200) ? "Optical" : "Coaxial");
+
+ err = parse_clock_bits(data, &rate, &src);
+ if (err < 0)
+ return;
+ label = snd_ff_proc_get_clk_label(src);
+ if (!label)
+ return;
+
+ snd_iprintf(buffer, "Clock configuration: %d %s\n", rate, label);
+}
+
+static void dump_sync_status(struct snd_ff *ff, struct snd_info_buffer *buffer)
+{
+ static const struct {
+ char *const label;
+ u32 locked_mask;
+ u32 synced_mask;
+ } *clk_entry, clk_entries[] = {
+ { "WDClk", 0x40000000, 0x20000000, },
+ { "S/PDIF", 0x00080000, 0x00040000, },
+ { "ADAT1", 0x00000400, 0x00001000, },
+ { "ADAT2", 0x00000800, 0x00002000, },
+ };
+ static const struct {
+ char *const label;
+ u32 mask;
+ } *referred_entry, referred_entries[] = {
+ { "ADAT1", 0x00000000, },
+ { "ADAT2", 0x00400000, },
+ { "S/PDIF", 0x00c00000, },
+ { "WDclk", 0x01000000, },
+ { "TCO", 0x01400000, },
+ };
+ static const struct {
+ unsigned int rate;
+ u32 mask;
+ } *rate_entry, rate_entries[] = {
+ { 32000, 0x02000000, },
+ { 44100, 0x04000000, },
+ { 48000, 0x06000000, },
+ { 64000, 0x08000000, },
+ { 88200, 0x0a000000, },
+ { 96000, 0x0c000000, },
+ { 128000, 0x0e000000, },
+ { 176400, 0x10000000, },
+ { 192000, 0x12000000, },
+ };
+ __le32 reg[2];
+ u32 data[2];
+ int i;
+ int err;
+
+ err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST,
+ FORMER_REG_SYNC_STATUS, reg, sizeof(reg), 0);
+ if (err < 0)
+ return;
+ data[0] = le32_to_cpu(reg[0]);
+ data[1] = le32_to_cpu(reg[1]);
+
+ snd_iprintf(buffer, "External source detection:\n");
+
+ for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) {
+ const char *state;
+
+ clk_entry = clk_entries + i;
+ if (data[0] & clk_entry->locked_mask) {
+ if (data[0] & clk_entry->synced_mask)
+ state = "sync";
+ else
+ state = "lock";
+ } else {
+ state = "none";
+ }
+
+ snd_iprintf(buffer, "%s: %s\n", clk_entry->label, state);
+ }
+
+ snd_iprintf(buffer, "Referred clock:\n");
+
+ if (data[1] & 0x00000001) {
+ snd_iprintf(buffer, "Internal\n");
+ } else {
+ unsigned int rate;
+ const char *label;
+
+ for (i = 0; i < ARRAY_SIZE(referred_entries); ++i) {
+ referred_entry = referred_entries + i;
+ if ((data[0] & 0x1e0000) == referred_entry->mask) {
+ label = referred_entry->label;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(referred_entries))
+ label = "none";
+
+ for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) {
+ rate_entry = rate_entries + i;
+ if ((data[0] & 0x1e000000) == rate_entry->mask) {
+ rate = rate_entry->rate;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(rate_entries))
+ rate = 0;
+
+ snd_iprintf(buffer, "%s %d\n", label, rate);
+ }
+}
+
+static void former_dump_status(struct snd_ff *ff,
+ struct snd_info_buffer *buffer)
+{
+ dump_clock_config(ff, buffer);
+ dump_sync_status(ff, buffer);
+}
+
+static int former_fill_midi_msg(struct snd_ff *ff,
+ struct snd_rawmidi_substream *substream,
+ unsigned int port)
+{
+ u8 *buf = (u8 *)ff->msg_buf[port];
+ int len;
+ int i;
+
+ len = snd_rawmidi_transmit_peek(substream, buf,
+ SND_FF_MAXIMIM_MIDI_QUADS);
+ if (len <= 0)
+ return len;
+
+ // One quadlet includes one byte.
+ for (i = len - 1; i >= 0; --i)
+ ff->msg_buf[port][i] = cpu_to_le32(buf[i]);
+ ff->rx_bytes[port] = len;
+
+ return len;
+}
+
+#define FF800_STF 0x0000fc88f000
+#define FF800_RX_PACKET_FORMAT 0x0000fc88f004
+#define FF800_ALLOC_TX_STREAM 0x0000fc88f008
+#define FF800_ISOC_COMM_START 0x0000fc88f00c
+#define FF800_TX_S800_FLAG 0x00000800
+#define FF800_ISOC_COMM_STOP 0x0000fc88f010
+
+#define FF800_TX_PACKET_ISOC_CH 0x0000801c0008
+
+static int allocate_tx_resources(struct snd_ff *ff)
+{
+ __le32 reg;
+ unsigned int count;
+ unsigned int tx_isoc_channel;
+ int err;
+
+ reg = cpu_to_le32(ff->tx_stream.data_block_quadlets);
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF800_ALLOC_TX_STREAM, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // Wait till the format of tx packet is available.
+ count = 0;
+ while (count++ < 10) {
+ u32 data;
+ err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
+ FF800_TX_PACKET_ISOC_CH, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ data = le32_to_cpu(reg);
+ if (data != 0xffffffff) {
+ tx_isoc_channel = data;
+ break;
+ }
+
+ msleep(50);
+ }
+ if (count >= 10)
+ return -ETIMEDOUT;
+
+ // NOTE: this is a makeshift to start OHCI 1394 IR context in the
+ // channel. On the other hand, 'struct fw_iso_resources.allocated' is
+ // not true and it's not deallocated at stop.
+ ff->tx_resources.channel = tx_isoc_channel;
+
+ return 0;
+}
+
+static int ff800_allocate_resources(struct snd_ff *ff, unsigned int rate)
+{
+ u32 data;
+ __le32 reg;
+ int err;
+
+ reg = cpu_to_le32(rate);
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF800_STF, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // If starting isochronous communication immediately, change of STF has
+ // no effect. In this case, the communication runs based on former STF.
+ // Let's sleep for a bit.
+ msleep(100);
+
+ // Controllers should allocate isochronous resources for rx stream.
+ err = fw_iso_resources_allocate(&ff->rx_resources,
+ amdtp_stream_get_max_payload(&ff->rx_stream),
+ fw_parent_device(ff->unit)->max_speed);
+ if (err < 0)
+ return err;
+
+ // Set isochronous channel and the number of quadlets of rx packets.
+ // This should be done before the allocation of tx resources to avoid
+ // periodical noise.
+ data = ff->rx_stream.data_block_quadlets << 3;
+ data = (data << 8) | ff->rx_resources.channel;
+ reg = cpu_to_le32(data);
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF800_RX_PACKET_FORMAT, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ return allocate_tx_resources(ff);
+}
+
+static int ff800_begin_session(struct snd_ff *ff, unsigned int rate)
+{
+ unsigned int generation = ff->rx_resources.generation;
+ __le32 reg;
+
+ if (generation != fw_parent_device(ff->unit)->card->generation) {
+ int err = fw_iso_resources_update(&ff->rx_resources);
+ if (err < 0)
+ return err;
+ }
+
+ reg = cpu_to_le32(0x80000000);
+ reg |= cpu_to_le32(ff->tx_stream.data_block_quadlets);
+ if (fw_parent_device(ff->unit)->max_speed == SCODE_800)
+ reg |= cpu_to_le32(FF800_TX_S800_FLAG);
+ return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF800_ISOC_COMM_START, &reg, sizeof(reg), 0);
+}
+
+static void ff800_finish_session(struct snd_ff *ff)
+{
+ __le32 reg;
+
+ reg = cpu_to_le32(0x80000000);
+ snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF800_ISOC_COMM_STOP, &reg, sizeof(reg), 0);
+}
+
+// Fireface 800 doesn't allow drivers to register lower 4 bytes of destination
+// address.
+// A write transaction to clear registered higher 4 bytes of destination address
+// has an effect to suppress asynchronous transaction from device.
+static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
+ size_t length, u32 tstamp)
+{
+ int i;
+
+ for (i = 0; i < length / 4; i++) {
+ u8 byte = le32_to_cpu(buf[i]) & 0xff;
+ struct snd_rawmidi_substream *substream;
+
+ substream = READ_ONCE(ff->tx_midi_substreams[0]);
+ if (substream)
+ snd_rawmidi_receive(substream, &byte, 1);
+ }
+}
+
+const struct snd_ff_protocol snd_ff_protocol_ff800 = {
+ .handle_msg = ff800_handle_midi_msg,
+ .fill_midi_msg = former_fill_midi_msg,
+ .get_clock = former_get_clock,
+ .switch_fetching_mode = former_switch_fetching_mode,
+ .allocate_resources = ff800_allocate_resources,
+ .begin_session = ff800_begin_session,
+ .finish_session = ff800_finish_session,
+ .dump_status = former_dump_status,
+};
+
+#define FF400_STF 0x000080100500ull
+#define FF400_RX_PACKET_FORMAT 0x000080100504ull
+#define FF400_ISOC_COMM_START 0x000080100508ull
+#define FF400_TX_PACKET_FORMAT 0x00008010050cull
+#define FF400_ISOC_COMM_STOP 0x000080100510ull
+
+// Fireface 400 manages isochronous channel number in 3 bit field. Therefore,
+// we can allocate between 0 and 7 channel.
+static int ff400_allocate_resources(struct snd_ff *ff, unsigned int rate)
+{
+ __le32 reg;
+ enum snd_ff_stream_mode mode;
+ int i;
+ int err;
+
+ // Check whether the given value is supported or not.
+ for (i = 0; i < CIP_SFC_COUNT; i++) {
+ if (amdtp_rate_table[i] == rate)
+ break;
+ }
+ if (i >= CIP_SFC_COUNT)
+ return -EINVAL;
+
+ // Set the number of data blocks transferred in a second.
+ reg = cpu_to_le32(rate);
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF400_STF, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ msleep(100);
+
+ err = snd_ff_stream_get_multiplier_mode(i, &mode);
+ if (err < 0)
+ return err;
+
+ // Keep resources for in-stream.
+ ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
+ err = fw_iso_resources_allocate(&ff->tx_resources,
+ amdtp_stream_get_max_payload(&ff->tx_stream),
+ fw_parent_device(ff->unit)->max_speed);
+ if (err < 0)
+ return err;
+
+ // Keep resources for out-stream.
+ ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
+ err = fw_iso_resources_allocate(&ff->rx_resources,
+ amdtp_stream_get_max_payload(&ff->rx_stream),
+ fw_parent_device(ff->unit)->max_speed);
+ if (err < 0)
+ fw_iso_resources_free(&ff->tx_resources);
+
+ return err;
+}
+
+static int ff400_begin_session(struct snd_ff *ff, unsigned int rate)
+{
+ unsigned int generation = ff->rx_resources.generation;
+ __le32 reg;
+ int err;
+
+ if (generation != fw_parent_device(ff->unit)->card->generation) {
+ err = fw_iso_resources_update(&ff->tx_resources);
+ if (err < 0)
+ return err;
+
+ err = fw_iso_resources_update(&ff->rx_resources);
+ if (err < 0)
+ return err;
+ }
+
+ // Set isochronous channel and the number of quadlets of received
+ // packets.
+ reg = cpu_to_le32(((ff->rx_stream.data_block_quadlets << 3) << 8) |
+ ff->rx_resources.channel);
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF400_RX_PACKET_FORMAT, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // Set isochronous channel and the number of quadlets of transmitted
+ // packet.
+ // TODO: investigate the purpose of this 0x80.
+ reg = cpu_to_le32((0x80 << 24) |
+ (ff->tx_resources.channel << 5) |
+ (ff->tx_stream.data_block_quadlets));
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF400_TX_PACKET_FORMAT, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // Allow to transmit packets.
+ reg = cpu_to_le32(0x00000001);
+ return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF400_ISOC_COMM_START, &reg, sizeof(reg), 0);
+}
+
+static void ff400_finish_session(struct snd_ff *ff)
+{
+ __le32 reg;
+
+ reg = cpu_to_le32(0x80000000);
+ snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF400_ISOC_COMM_STOP, &reg, sizeof(reg), 0);
+}
+
+static void parse_midi_msg(struct snd_ff *ff, u32 quad, unsigned int port)
+{
+ struct snd_rawmidi_substream *substream = READ_ONCE(ff->tx_midi_substreams[port]);
+
+ if (substream != NULL) {
+ u8 byte = (quad >> (16 * port)) & 0x000000ff;
+
+ snd_rawmidi_receive(substream, &byte, 1);
+ }
+}
+
+#define FF400_QUEUE_SIZE 32
+
+struct ff400_msg_parser {
+ struct {
+ u32 msg;
+ u32 tstamp;
+ } msgs[FF400_QUEUE_SIZE];
+ size_t push_pos;
+ size_t pull_pos;
+};
+
+static bool ff400_has_msg(struct snd_ff *ff)
+{
+ struct ff400_msg_parser *parser = ff->msg_parser;
+
+ return (parser->push_pos != parser->pull_pos);
+}
+
+// For Fireface 400, lower 4 bytes of destination address is configured by bit
+// flag in quadlet register (little endian) at 0x'0000'801'0051c. Drivers can
+// select one of 4 options:
+//
+// bit flags: offset of destination address
+// - 0x04000000: 0x'....'....'0000'0000
+// - 0x08000000: 0x'....'....'0000'0080
+// - 0x10000000: 0x'....'....'0000'0100
+// - 0x20000000: 0x'....'....'0000'0180
+//
+// Drivers can suppress the device to transfer asynchronous transactions by
+// using below 2 bits.
+// - 0x01000000: suppress transmission
+// - 0x02000000: suppress transmission
+//
+// Actually, the register is write-only and includes the other options such as
+// input attenuation. This driver allocates destination address with '0000'0000
+// in its lower offset and expects userspace application to configure the
+// register for it.
+
+// When the message is for signal level operation, the upper 4 bits in MSB expresses the pair of
+// stereo physical port.
+// - 0: Microphone input 0/1
+// - 1: Line input 0/1
+// - [2-4]: Line output 0-5
+// - 5: Headphone output 0/1
+// - 6: S/PDIF output 0/1
+// - [7-10]: ADAT output 0-7
+//
+// The value of signal level can be detected by mask of 0x00fffc00. For signal level of microphone
+// input:
+//
+// - 0: 0.0 dB
+// - 10: +10.0 dB
+// - 11: +11.0 dB
+// - 12: +12.0 dB
+// - ...
+// - 63: +63.0 dB:
+// - 64: +64.0 dB:
+// - 65: +65.0 dB:
+//
+// For signal level of line input:
+//
+// - 0: 0.0 dB
+// - 1: +0.5 dB
+// - 2: +1.0 dB
+// - 3: +1.5 dB
+// - ...
+// - 34: +17.0 dB:
+// - 35: +17.5 dB:
+// - 36: +18.0 dB:
+//
+// For signal level of any type of output:
+//
+// - 63: -infinite
+// - 62: -58.0 dB
+// - 61: -56.0 dB
+// - 60: -54.0 dB
+// - 59: -53.0 dB
+// - 58: -52.0 dB
+// - ...
+// - 7: -1.0 dB
+// - 6: 0.0 dB
+// - 5: +1.0 dB
+// - ...
+// - 2: +4.0 dB
+// - 1: +5.0 dB
+// - 0: +6.0 dB
+//
+// When the message is not for signal level operation, it's for MIDI bytes. When matching to
+// FF400_MSG_FLAG_IS_MIDI_PORT_0, one MIDI byte can be detected by mask of 0x000000ff. When
+// matching to FF400_MSG_FLAG_IS_MIDI_PORT_1, one MIDI byte can be detected by mask of 0x00ff0000.
+#define FF400_MSG_FLAG_IS_SIGNAL_LEVEL 0x04000000
+#define FF400_MSG_FLAG_IS_RIGHT_CHANNEL 0x08000000
+#define FF400_MSG_FLAG_IS_STEREO_PAIRED 0x02000000
+#define FF400_MSG_MASK_STEREO_PAIR 0xf0000000
+#define FF400_MSG_MASK_SIGNAL_LEVEL 0x00fffc00
+#define FF400_MSG_FLAG_IS_MIDI_PORT_0 0x00000100
+#define FF400_MSG_MASK_MIDI_PORT_0 0x000000ff
+#define FF400_MSG_FLAG_IS_MIDI_PORT_1 0x01000000
+#define FF400_MSG_MASK_MIDI_PORT_1 0x00ff0000
+
+static void ff400_handle_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
+ size_t length, u32 tstamp)
+{
+ bool need_hwdep_wake_up = false;
+ int i;
+
+ for (i = 0; i < length / 4; i++) {
+ u32 quad = le32_to_cpu(buf[i]);
+
+ if (quad & FF400_MSG_FLAG_IS_SIGNAL_LEVEL) {
+ struct ff400_msg_parser *parser = ff->msg_parser;
+
+ parser->msgs[parser->push_pos].msg = quad;
+ parser->msgs[parser->push_pos].tstamp = tstamp;
+ ++parser->push_pos;
+ if (parser->push_pos >= FF400_QUEUE_SIZE)
+ parser->push_pos = 0;
+
+ need_hwdep_wake_up = true;
+ } else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_0) {
+ parse_midi_msg(ff, quad, 0);
+ } else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_1) {
+ parse_midi_msg(ff, quad, 1);
+ }
+ }
+
+ if (need_hwdep_wake_up)
+ wake_up(&ff->hwdep_wait);
+}
+
+static long ff400_copy_msg_to_user(struct snd_ff *ff, char __user *buf, long count)
+{
+ struct snd_firewire_event_ff400_message ev = {
+ .type = SNDRV_FIREWIRE_EVENT_FF400_MESSAGE,
+ .message_count = 0,
+ };
+ struct ff400_msg_parser *parser = ff->msg_parser;
+ long consumed = 0;
+ long ret = 0;
+
+ if (count < sizeof(ev) || parser->pull_pos == parser->push_pos)
+ return 0;
+
+ count -= sizeof(ev);
+ consumed += sizeof(ev);
+
+ while (count >= sizeof(*parser->msgs) && parser->pull_pos != parser->push_pos) {
+ spin_unlock_irq(&ff->lock);
+ if (copy_to_user(buf + consumed, parser->msgs + parser->pull_pos,
+ sizeof(*parser->msgs)))
+ ret = -EFAULT;
+ spin_lock_irq(&ff->lock);
+ if (ret)
+ return ret;
+
+ ++parser->pull_pos;
+ if (parser->pull_pos >= FF400_QUEUE_SIZE)
+ parser->pull_pos = 0;
+ ++ev.message_count;
+ count -= sizeof(*parser->msgs);
+ consumed += sizeof(*parser->msgs);
+ }
+
+ spin_unlock_irq(&ff->lock);
+ if (copy_to_user(buf, &ev, sizeof(ev)))
+ ret = -EFAULT;
+ spin_lock_irq(&ff->lock);
+ if (ret)
+ return ret;
+
+ return consumed;
+}
+
+const struct snd_ff_protocol snd_ff_protocol_ff400 = {
+ .msg_parser_size = sizeof(struct ff400_msg_parser),
+ .has_msg = ff400_has_msg,
+ .copy_msg_to_user = ff400_copy_msg_to_user,
+ .handle_msg = ff400_handle_msg,
+ .fill_midi_msg = former_fill_midi_msg,
+ .get_clock = former_get_clock,
+ .switch_fetching_mode = former_switch_fetching_mode,
+ .allocate_resources = ff400_allocate_resources,
+ .begin_session = ff400_begin_session,
+ .finish_session = ff400_finish_session,
+ .dump_status = former_dump_status,
+};
diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c
new file mode 100644
index 000000000000..9947e0c2e0aa
--- /dev/null
+++ b/sound/firewire/fireface/ff-protocol-latter.c
@@ -0,0 +1,540 @@
+// SPDX-License-Identifier: GPL-2.0
+// ff-protocol-latter.c - a part of driver for RME Fireface series
+//
+// Copyright (c) 2019 Takashi Sakamoto
+
+#include <linux/delay.h>
+
+#include "ff.h"
+
+#define LATTER_STF 0xffff00000004ULL
+#define LATTER_ISOC_CHANNELS 0xffff00000008ULL
+#define LATTER_ISOC_START 0xffff0000000cULL
+#define LATTER_FETCH_MODE 0xffff00000010ULL
+#define LATTER_SYNC_STATUS 0x0000801c0000ULL
+
+// The content of sync status register differs between models.
+//
+// Fireface UCX:
+// 0xf0000000: (unidentified)
+// 0x0f000000: effective rate of sampling clock
+// 0x00f00000: detected rate of word clock on BNC interface
+// 0x000f0000: detected rate of ADAT or S/PDIF on optical interface
+// 0x0000f000: detected rate of S/PDIF on coaxial interface
+// 0x00000e00: effective source of sampling clock
+// 0x00000e00: Internal
+// 0x00000800: (unidentified)
+// 0x00000600: Word clock on BNC interface
+// 0x00000400: ADAT on optical interface
+// 0x00000200: S/PDIF on coaxial or optical interface
+// 0x00000100: Optical interface is used for ADAT signal
+// 0x00000080: (unidentified)
+// 0x00000040: Synchronized to word clock on BNC interface
+// 0x00000020: Synchronized to ADAT or S/PDIF on optical interface
+// 0x00000010: Synchronized to S/PDIF on coaxial interface
+// 0x00000008: (unidentified)
+// 0x00000004: Lock word clock on BNC interface
+// 0x00000002: Lock ADAT or S/PDIF on optical interface
+// 0x00000001: Lock S/PDIF on coaxial interface
+//
+// Fireface 802 (and perhaps UFX):
+// 0xf0000000: effective rate of sampling clock
+// 0x0f000000: detected rate of ADAT-B on 2nd optical interface
+// 0x00f00000: detected rate of ADAT-A on 1st optical interface
+// 0x000f0000: detected rate of AES/EBU on XLR or coaxial interface
+// 0x0000f000: detected rate of word clock on BNC interface
+// 0x00000e00: effective source of sampling clock
+// 0x00000e00: internal
+// 0x00000800: ADAT-B
+// 0x00000600: ADAT-A
+// 0x00000400: AES/EBU
+// 0x00000200: Word clock
+// 0x00000080: Synchronized to ADAT-B on 2nd optical interface
+// 0x00000040: Synchronized to ADAT-A on 1st optical interface
+// 0x00000020: Synchronized to AES/EBU on XLR or 2nd optical interface
+// 0x00000010: Synchronized to word clock on BNC interface
+// 0x00000008: Lock ADAT-B on 2nd optical interface
+// 0x00000004: Lock ADAT-A on 1st optical interface
+// 0x00000002: Lock AES/EBU on XLR or 2nd optical interface
+// 0x00000001: Lock word clock on BNC interface
+//
+// The pattern for rate bits:
+// 0x00: 32.0 kHz
+// 0x01: 44.1 kHz
+// 0x02: 48.0 kHz
+// 0x04: 64.0 kHz
+// 0x05: 88.2 kHz
+// 0x06: 96.0 kHz
+// 0x08: 128.0 kHz
+// 0x09: 176.4 kHz
+// 0x0a: 192.0 kHz
+static int parse_clock_bits(u32 data, unsigned int *rate,
+ enum snd_ff_clock_src *src,
+ enum snd_ff_unit_version unit_version)
+{
+ static const struct {
+ unsigned int rate;
+ u32 flag;
+ } *rate_entry, rate_entries[] = {
+ { 32000, 0x00, },
+ { 44100, 0x01, },
+ { 48000, 0x02, },
+ { 64000, 0x04, },
+ { 88200, 0x05, },
+ { 96000, 0x06, },
+ { 128000, 0x08, },
+ { 176400, 0x09, },
+ { 192000, 0x0a, },
+ };
+ static const struct {
+ enum snd_ff_clock_src src;
+ u32 flag;
+ } *clk_entry, *clk_entries, ucx_clk_entries[] = {
+ { SND_FF_CLOCK_SRC_SPDIF, 0x00000200, },
+ { SND_FF_CLOCK_SRC_ADAT1, 0x00000400, },
+ { SND_FF_CLOCK_SRC_WORD, 0x00000600, },
+ { SND_FF_CLOCK_SRC_INTERNAL, 0x00000e00, },
+ }, ufx_ff802_clk_entries[] = {
+ { SND_FF_CLOCK_SRC_WORD, 0x00000200, },
+ { SND_FF_CLOCK_SRC_SPDIF, 0x00000400, },
+ { SND_FF_CLOCK_SRC_ADAT1, 0x00000600, },
+ { SND_FF_CLOCK_SRC_ADAT2, 0x00000800, },
+ { SND_FF_CLOCK_SRC_INTERNAL, 0x00000e00, },
+ };
+ u32 rate_bits;
+ unsigned int clk_entry_count;
+ int i;
+
+ if (unit_version == SND_FF_UNIT_VERSION_UCX) {
+ rate_bits = (data & 0x0f000000) >> 24;
+ clk_entries = ucx_clk_entries;
+ clk_entry_count = ARRAY_SIZE(ucx_clk_entries);
+ } else {
+ rate_bits = (data & 0xf0000000) >> 28;
+ clk_entries = ufx_ff802_clk_entries;
+ clk_entry_count = ARRAY_SIZE(ufx_ff802_clk_entries);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) {
+ rate_entry = rate_entries + i;
+ if (rate_bits == rate_entry->flag) {
+ *rate = rate_entry->rate;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(rate_entries))
+ return -EIO;
+
+ for (i = 0; i < clk_entry_count; ++i) {
+ clk_entry = clk_entries + i;
+ if ((data & 0x000e00) == clk_entry->flag) {
+ *src = clk_entry->src;
+ break;
+ }
+ }
+ if (i == clk_entry_count)
+ return -EIO;
+
+ return 0;
+}
+
+static int latter_get_clock(struct snd_ff *ff, unsigned int *rate,
+ enum snd_ff_clock_src *src)
+{
+ __le32 reg;
+ u32 data;
+ int err;
+
+ err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
+ LATTER_SYNC_STATUS, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+ data = le32_to_cpu(reg);
+
+ return parse_clock_bits(data, rate, src, ff->unit_version);
+}
+
+static int latter_switch_fetching_mode(struct snd_ff *ff, bool enable)
+{
+ u32 data;
+ __le32 reg;
+
+ if (enable)
+ data = 0x00000000;
+ else
+ data = 0xffffffff;
+ reg = cpu_to_le32(data);
+
+ return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ LATTER_FETCH_MODE, &reg, sizeof(reg), 0);
+}
+
+static int latter_allocate_resources(struct snd_ff *ff, unsigned int rate)
+{
+ enum snd_ff_stream_mode mode;
+ unsigned int code;
+ __le32 reg;
+ unsigned int count;
+ int i;
+ int err;
+
+ // Set the number of data blocks transferred in a second.
+ if (rate % 48000 == 0)
+ code = 0x04;
+ else if (rate % 44100 == 0)
+ code = 0x02;
+ else if (rate % 32000 == 0)
+ code = 0x00;
+ else
+ return -EINVAL;
+
+ if (rate >= 64000 && rate < 128000)
+ code |= 0x08;
+ else if (rate >= 128000)
+ code |= 0x10;
+
+ reg = cpu_to_le32(code);
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ LATTER_STF, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // Confirm to shift transmission clock.
+ count = 0;
+ while (count++ < 10) {
+ unsigned int curr_rate;
+ enum snd_ff_clock_src src;
+
+ err = latter_get_clock(ff, &curr_rate, &src);
+ if (err < 0)
+ return err;
+
+ if (curr_rate == rate)
+ break;
+ }
+ if (count > 10)
+ return -ETIMEDOUT;
+
+ for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); ++i) {
+ if (rate == amdtp_rate_table[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(amdtp_rate_table))
+ return -EINVAL;
+
+ err = snd_ff_stream_get_multiplier_mode(i, &mode);
+ if (err < 0)
+ return err;
+
+ // Keep resources for in-stream.
+ ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
+ err = fw_iso_resources_allocate(&ff->tx_resources,
+ amdtp_stream_get_max_payload(&ff->tx_stream),
+ fw_parent_device(ff->unit)->max_speed);
+ if (err < 0)
+ return err;
+
+ // Keep resources for out-stream.
+ ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
+ err = fw_iso_resources_allocate(&ff->rx_resources,
+ amdtp_stream_get_max_payload(&ff->rx_stream),
+ fw_parent_device(ff->unit)->max_speed);
+ if (err < 0)
+ fw_iso_resources_free(&ff->tx_resources);
+
+ return err;
+}
+
+static int latter_begin_session(struct snd_ff *ff, unsigned int rate)
+{
+ unsigned int generation = ff->rx_resources.generation;
+ unsigned int flag;
+ u32 data;
+ __le32 reg;
+ int err;
+
+ if (ff->unit_version == SND_FF_UNIT_VERSION_UCX) {
+ // For Fireface UCX. Always use the maximum number of data
+ // channels in data block of packet.
+ if (rate >= 32000 && rate <= 48000)
+ flag = 0x92;
+ else if (rate >= 64000 && rate <= 96000)
+ flag = 0x8e;
+ else if (rate >= 128000 && rate <= 192000)
+ flag = 0x8c;
+ else
+ return -EINVAL;
+ } else {
+ // For Fireface UFX and 802. Due to bandwidth limitation on
+ // IEEE 1394a (400 Mbps), Analog 1-12 and AES are available
+ // without any ADAT at quadruple speed.
+ if (rate >= 32000 && rate <= 48000)
+ flag = 0x9e;
+ else if (rate >= 64000 && rate <= 96000)
+ flag = 0x96;
+ else if (rate >= 128000 && rate <= 192000)
+ flag = 0x8e;
+ else
+ return -EINVAL;
+ }
+
+ if (generation != fw_parent_device(ff->unit)->card->generation) {
+ err = fw_iso_resources_update(&ff->tx_resources);
+ if (err < 0)
+ return err;
+
+ err = fw_iso_resources_update(&ff->rx_resources);
+ if (err < 0)
+ return err;
+ }
+
+ data = (ff->tx_resources.channel << 8) | ff->rx_resources.channel;
+ reg = cpu_to_le32(data);
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ LATTER_ISOC_CHANNELS, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ reg = cpu_to_le32(flag);
+ return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ LATTER_ISOC_START, &reg, sizeof(reg), 0);
+}
+
+static void latter_finish_session(struct snd_ff *ff)
+{
+ __le32 reg;
+
+ reg = cpu_to_le32(0x00000000);
+ snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ LATTER_ISOC_START, &reg, sizeof(reg), 0);
+}
+
+static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer)
+{
+ static const struct {
+ char *const label;
+ u32 locked_mask;
+ u32 synced_mask;
+ } *clk_entry, *clk_entries, ucx_clk_entries[] = {
+ { "S/PDIF", 0x00000001, 0x00000010, },
+ { "ADAT", 0x00000002, 0x00000020, },
+ { "WDClk", 0x00000004, 0x00000040, },
+ }, ufx_ff802_clk_entries[] = {
+ { "WDClk", 0x00000001, 0x00000010, },
+ { "AES/EBU", 0x00000002, 0x00000020, },
+ { "ADAT-A", 0x00000004, 0x00000040, },
+ { "ADAT-B", 0x00000008, 0x00000080, },
+ };
+ __le32 reg;
+ u32 data;
+ unsigned int rate;
+ enum snd_ff_clock_src src;
+ const char *label;
+ unsigned int clk_entry_count;
+ int i;
+ int err;
+
+ err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
+ LATTER_SYNC_STATUS, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return;
+ data = le32_to_cpu(reg);
+
+ snd_iprintf(buffer, "External source detection:\n");
+
+ if (ff->unit_version == SND_FF_UNIT_VERSION_UCX) {
+ clk_entries = ucx_clk_entries;
+ clk_entry_count = ARRAY_SIZE(ucx_clk_entries);
+ } else {
+ clk_entries = ufx_ff802_clk_entries;
+ clk_entry_count = ARRAY_SIZE(ufx_ff802_clk_entries);
+ }
+
+ for (i = 0; i < clk_entry_count; ++i) {
+ clk_entry = clk_entries + i;
+ snd_iprintf(buffer, "%s: ", clk_entry->label);
+ if (data & clk_entry->locked_mask) {
+ if (data & clk_entry->synced_mask)
+ snd_iprintf(buffer, "sync\n");
+ else
+ snd_iprintf(buffer, "lock\n");
+ } else {
+ snd_iprintf(buffer, "none\n");
+ }
+ }
+
+ err = parse_clock_bits(data, &rate, &src, ff->unit_version);
+ if (err < 0)
+ return;
+ label = snd_ff_proc_get_clk_label(src);
+ if (!label)
+ return;
+
+ snd_iprintf(buffer, "Referred clock: %s %d\n", label, rate);
+}
+
+// NOTE: transactions are transferred within 0x00-0x7f in allocated range of
+// address. This seems to be for check of discontinuity in receiver side.
+//
+// Like Fireface 400, drivers can select one of 4 options for lower 4 bytes of
+// destination address by bit flags in quadlet register (little endian) at
+// 0x'ffff'0000'0014:
+//
+// bit flags: offset of destination address
+// - 0x00002000: 0x'....'....'0000'0000
+// - 0x00004000: 0x'....'....'0000'0080
+// - 0x00008000: 0x'....'....'0000'0100
+// - 0x00010000: 0x'....'....'0000'0180
+//
+// Drivers can suppress the device to transfer asynchronous transactions by
+// clear these bit flags.
+//
+// Actually, the register is write-only and includes the other settings such as
+// input attenuation. This driver allocates for the first option
+// (0x'....'....'0000'0000) and expects userspace application to configure the
+// register for it.
+static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
+ size_t length, u32 tstamp)
+{
+ u32 data = le32_to_cpu(*buf);
+ unsigned int index = (data & 0x000000f0) >> 4;
+ u8 byte[3];
+ struct snd_rawmidi_substream *substream;
+ unsigned int len;
+
+ if (index >= ff->spec->midi_in_ports)
+ return;
+
+ switch (data & 0x0000000f) {
+ case 0x00000008:
+ case 0x00000009:
+ case 0x0000000a:
+ case 0x0000000b:
+ case 0x0000000e:
+ len = 3;
+ break;
+ case 0x0000000c:
+ case 0x0000000d:
+ len = 2;
+ break;
+ default:
+ len = data & 0x00000003;
+ if (len == 0)
+ len = 3;
+ break;
+ }
+
+ byte[0] = (data & 0x0000ff00) >> 8;
+ byte[1] = (data & 0x00ff0000) >> 16;
+ byte[2] = (data & 0xff000000) >> 24;
+
+ substream = READ_ONCE(ff->tx_midi_substreams[index]);
+ if (substream)
+ snd_rawmidi_receive(substream, byte, len);
+}
+
+/*
+ * When return minus value, given argument is not MIDI status.
+ * When return 0, given argument is a beginning of system exclusive.
+ * When return the others, given argument is MIDI data.
+ */
+static inline int calculate_message_bytes(u8 status)
+{
+ switch (status) {
+ case 0xf6: /* Tune request. */
+ case 0xf8: /* Timing clock. */
+ case 0xfa: /* Start. */
+ case 0xfb: /* Continue. */
+ case 0xfc: /* Stop. */
+ case 0xfe: /* Active sensing. */
+ case 0xff: /* System reset. */
+ return 1;
+ case 0xf1: /* MIDI time code quarter frame. */
+ case 0xf3: /* Song select. */
+ return 2;
+ case 0xf2: /* Song position pointer. */
+ return 3;
+ case 0xf0: /* Exclusive. */
+ return 0;
+ case 0xf7: /* End of exclusive. */
+ break;
+ case 0xf4: /* Undefined. */
+ case 0xf5: /* Undefined. */
+ case 0xf9: /* Undefined. */
+ case 0xfd: /* Undefined. */
+ break;
+ default:
+ switch (status & 0xf0) {
+ case 0x80: /* Note on. */
+ case 0x90: /* Note off. */
+ case 0xa0: /* Polyphonic key pressure. */
+ case 0xb0: /* Control change and Mode change. */
+ case 0xe0: /* Pitch bend change. */
+ return 3;
+ case 0xc0: /* Program change. */
+ case 0xd0: /* Channel pressure. */
+ return 2;
+ default:
+ break;
+ }
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int latter_fill_midi_msg(struct snd_ff *ff,
+ struct snd_rawmidi_substream *substream,
+ unsigned int port)
+{
+ u32 data = {0};
+ u8 *buf = (u8 *)&data;
+ int consumed;
+
+ buf[0] = port << 4;
+ consumed = snd_rawmidi_transmit_peek(substream, buf + 1, 3);
+ if (consumed <= 0)
+ return consumed;
+
+ if (!ff->on_sysex[port]) {
+ if (buf[1] != 0xf0) {
+ if (consumed < calculate_message_bytes(buf[1]))
+ return 0;
+ } else {
+ // The beginning of exclusives.
+ ff->on_sysex[port] = true;
+ }
+
+ buf[0] |= consumed;
+ } else {
+ if (buf[1] != 0xf7) {
+ if (buf[2] == 0xf7 || buf[3] == 0xf7) {
+ // Transfer end code at next time.
+ consumed -= 1;
+ }
+
+ buf[0] |= consumed;
+ } else {
+ // The end of exclusives.
+ ff->on_sysex[port] = false;
+ consumed = 1;
+ buf[0] |= 0x0f;
+ }
+ }
+
+ ff->msg_buf[port][0] = cpu_to_le32(data);
+ ff->rx_bytes[port] = consumed;
+
+ return 1;
+}
+
+const struct snd_ff_protocol snd_ff_protocol_latter = {
+ .handle_msg = latter_handle_midi_msg,
+ .fill_midi_msg = latter_fill_midi_msg,
+ .get_clock = latter_get_clock,
+ .switch_fetching_mode = latter_switch_fetching_mode,
+ .allocate_resources = latter_allocate_resources,
+ .begin_session = latter_begin_session,
+ .finish_session = latter_finish_session,
+ .dump_status = latter_dump_status,
+};
diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c
new file mode 100644
index 000000000000..ba42490f2b0e
--- /dev/null
+++ b/sound/firewire/fireface/ff-stream.c
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ff-stream.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ */
+
+#include "ff.h"
+
+#define READY_TIMEOUT_MS 200
+
+int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
+ enum snd_ff_stream_mode *mode)
+{
+ static const enum snd_ff_stream_mode modes[] = {
+ [CIP_SFC_32000] = SND_FF_STREAM_MODE_LOW,
+ [CIP_SFC_44100] = SND_FF_STREAM_MODE_LOW,
+ [CIP_SFC_48000] = SND_FF_STREAM_MODE_LOW,
+ [CIP_SFC_88200] = SND_FF_STREAM_MODE_MID,
+ [CIP_SFC_96000] = SND_FF_STREAM_MODE_MID,
+ [CIP_SFC_176400] = SND_FF_STREAM_MODE_HIGH,
+ [CIP_SFC_192000] = SND_FF_STREAM_MODE_HIGH,
+ };
+
+ if (sfc >= CIP_SFC_COUNT)
+ return -EINVAL;
+
+ *mode = modes[sfc];
+
+ return 0;
+}
+
+static inline void finish_session(struct snd_ff *ff)
+{
+ ff->spec->protocol->finish_session(ff);
+ ff->spec->protocol->switch_fetching_mode(ff, false);
+}
+
+static int init_stream(struct snd_ff *ff, struct amdtp_stream *s)
+{
+ struct fw_iso_resources *resources;
+ enum amdtp_stream_direction dir;
+ int err;
+
+ if (s == &ff->tx_stream) {
+ resources = &ff->tx_resources;
+ dir = AMDTP_IN_STREAM;
+ } else {
+ resources = &ff->rx_resources;
+ dir = AMDTP_OUT_STREAM;
+ }
+
+ err = fw_iso_resources_init(resources, ff->unit);
+ if (err < 0)
+ return err;
+
+ err = amdtp_ff_init(s, ff->unit, dir);
+ if (err < 0)
+ fw_iso_resources_destroy(resources);
+
+ return err;
+}
+
+static void destroy_stream(struct snd_ff *ff, struct amdtp_stream *s)
+{
+ amdtp_stream_destroy(s);
+
+ if (s == &ff->tx_stream)
+ fw_iso_resources_destroy(&ff->tx_resources);
+ else
+ fw_iso_resources_destroy(&ff->rx_resources);
+}
+
+int snd_ff_stream_init_duplex(struct snd_ff *ff)
+{
+ int err;
+
+ err = init_stream(ff, &ff->rx_stream);
+ if (err < 0)
+ return err;
+
+ err = init_stream(ff, &ff->tx_stream);
+ if (err < 0) {
+ destroy_stream(ff, &ff->rx_stream);
+ return err;
+ }
+
+ err = amdtp_domain_init(&ff->domain);
+ if (err < 0) {
+ destroy_stream(ff, &ff->rx_stream);
+ destroy_stream(ff, &ff->tx_stream);
+ }
+
+ return err;
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
+{
+ amdtp_domain_destroy(&ff->domain);
+
+ destroy_stream(ff, &ff->rx_stream);
+ destroy_stream(ff, &ff->tx_stream);
+}
+
+int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer)
+{
+ unsigned int curr_rate;
+ enum snd_ff_clock_src src;
+ int err;
+
+ err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
+ if (err < 0)
+ return err;
+
+ if (ff->substreams_counter == 0 || curr_rate != rate) {
+ enum snd_ff_stream_mode mode;
+ int i;
+
+ amdtp_domain_stop(&ff->domain);
+ finish_session(ff);
+
+ fw_iso_resources_free(&ff->tx_resources);
+ fw_iso_resources_free(&ff->rx_resources);
+
+ for (i = 0; i < CIP_SFC_COUNT; ++i) {
+ if (amdtp_rate_table[i] == rate)
+ break;
+ }
+ if (i >= CIP_SFC_COUNT)
+ return -EINVAL;
+
+ err = snd_ff_stream_get_multiplier_mode(i, &mode);
+ if (err < 0)
+ return err;
+
+ err = amdtp_ff_set_parameters(&ff->tx_stream, rate,
+ ff->spec->pcm_capture_channels[mode]);
+ if (err < 0)
+ return err;
+
+ err = amdtp_ff_set_parameters(&ff->rx_stream, rate,
+ ff->spec->pcm_playback_channels[mode]);
+ if (err < 0)
+ return err;
+
+ err = ff->spec->protocol->allocate_resources(ff, rate);
+ if (err < 0)
+ return err;
+
+ err = amdtp_domain_set_events_per_period(&ff->domain,
+ frames_per_period, frames_per_buffer);
+ if (err < 0) {
+ fw_iso_resources_free(&ff->tx_resources);
+ fw_iso_resources_free(&ff->rx_resources);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
+{
+ int err;
+
+ if (ff->substreams_counter == 0)
+ return 0;
+
+ if (amdtp_streaming_error(&ff->tx_stream) ||
+ amdtp_streaming_error(&ff->rx_stream)) {
+ amdtp_domain_stop(&ff->domain);
+ finish_session(ff);
+ }
+
+ /*
+ * Regardless of current source of clock signal, drivers transfer some
+ * packets. Then, the device transfers packets.
+ */
+ if (!amdtp_stream_running(&ff->rx_stream)) {
+ int spd = fw_parent_device(ff->unit)->max_speed;
+
+ err = ff->spec->protocol->begin_session(ff, rate);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_domain_add_stream(&ff->domain, &ff->rx_stream,
+ ff->rx_resources.channel, spd);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_domain_add_stream(&ff->domain, &ff->tx_stream,
+ ff->tx_resources.channel, spd);
+ if (err < 0)
+ goto error;
+
+ // NOTE: The device doesn't transfer packets unless receiving any packet. The
+ // sequence of tx packets includes cycle skip corresponding to empty packet or
+ // NODATA packet in IEC 61883-1/6. The sequence of the number of data blocks per
+ // packet is important for media clock recovery.
+ err = amdtp_domain_start(&ff->domain, 0, true, true);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_domain_wait_ready(&ff->domain, READY_TIMEOUT_MS)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+
+ err = ff->spec->protocol->switch_fetching_mode(ff, true);
+ if (err < 0)
+ goto error;
+ }
+
+ return 0;
+error:
+ amdtp_domain_stop(&ff->domain);
+ finish_session(ff);
+
+ return err;
+}
+
+void snd_ff_stream_stop_duplex(struct snd_ff *ff)
+{
+ if (ff->substreams_counter == 0) {
+ amdtp_domain_stop(&ff->domain);
+ finish_session(ff);
+
+ fw_iso_resources_free(&ff->tx_resources);
+ fw_iso_resources_free(&ff->rx_resources);
+ }
+}
+
+void snd_ff_stream_update_duplex(struct snd_ff *ff)
+{
+ amdtp_domain_stop(&ff->domain);
+
+ // The device discontinue to transfer packets.
+ amdtp_stream_pcm_abort(&ff->tx_stream);
+ amdtp_stream_pcm_abort(&ff->rx_stream);
+}
+
+void snd_ff_stream_lock_changed(struct snd_ff *ff)
+{
+ ff->dev_lock_changed = true;
+ wake_up(&ff->hwdep_wait);
+}
+
+int snd_ff_stream_lock_try(struct snd_ff *ff)
+{
+ guard(spinlock_irq)(&ff->lock);
+
+ /* user land lock this */
+ if (ff->dev_lock_count < 0)
+ return -EBUSY;
+
+ /* this is the first time */
+ if (ff->dev_lock_count++ == 0)
+ snd_ff_stream_lock_changed(ff);
+ return 0;
+}
+
+void snd_ff_stream_lock_release(struct snd_ff *ff)
+{
+ guard(spinlock_irq)(&ff->lock);
+
+ if (WARN_ON(ff->dev_lock_count <= 0))
+ return;
+ if (--ff->dev_lock_count == 0)
+ snd_ff_stream_lock_changed(ff);
+}
diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c
new file mode 100644
index 000000000000..436da0a3bdcc
--- /dev/null
+++ b/sound/firewire/fireface/ff-transaction.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ff-transaction.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ */
+
+#include "ff.h"
+
+static void finish_transmit_midi_msg(struct snd_ff *ff, unsigned int port,
+ int rcode)
+{
+ struct snd_rawmidi_substream *substream =
+ READ_ONCE(ff->rx_midi_substreams[port]);
+
+ if (rcode_is_permanent_error(rcode)) {
+ ff->rx_midi_error[port] = true;
+ return;
+ }
+
+ if (rcode != RCODE_COMPLETE) {
+ /* Transfer the message again, immediately. */
+ ff->next_ktime[port] = 0;
+ schedule_work(&ff->rx_midi_work[port]);
+ return;
+ }
+
+ snd_rawmidi_transmit_ack(substream, ff->rx_bytes[port]);
+ ff->rx_bytes[port] = 0;
+
+ if (!snd_rawmidi_transmit_empty(substream))
+ schedule_work(&ff->rx_midi_work[port]);
+}
+
+static void finish_transmit_midi0_msg(struct fw_card *card, int rcode,
+ void *data, size_t length,
+ void *callback_data)
+{
+ struct snd_ff *ff =
+ container_of(callback_data, struct snd_ff, transactions[0]);
+ finish_transmit_midi_msg(ff, 0, rcode);
+}
+
+static void finish_transmit_midi1_msg(struct fw_card *card, int rcode,
+ void *data, size_t length,
+ void *callback_data)
+{
+ struct snd_ff *ff =
+ container_of(callback_data, struct snd_ff, transactions[1]);
+ finish_transmit_midi_msg(ff, 1, rcode);
+}
+
+static void transmit_midi_msg(struct snd_ff *ff, unsigned int port)
+{
+ struct snd_rawmidi_substream *substream =
+ READ_ONCE(ff->rx_midi_substreams[port]);
+ int quad_count;
+
+ struct fw_device *fw_dev = fw_parent_device(ff->unit);
+ unsigned long long addr;
+ int generation;
+ fw_transaction_callback_t callback;
+ int tcode;
+
+ if (substream == NULL || snd_rawmidi_transmit_empty(substream))
+ return;
+
+ if (ff->rx_bytes[port] > 0 || ff->rx_midi_error[port])
+ return;
+
+ /* Do it in next chance. */
+ if (ktime_after(ff->next_ktime[port], ktime_get())) {
+ schedule_work(&ff->rx_midi_work[port]);
+ return;
+ }
+
+ quad_count = ff->spec->protocol->fill_midi_msg(ff, substream, port);
+ if (quad_count <= 0)
+ return;
+
+ if (port == 0) {
+ addr = ff->spec->midi_rx_addrs[0];
+ callback = finish_transmit_midi0_msg;
+ } else {
+ addr = ff->spec->midi_rx_addrs[1];
+ callback = finish_transmit_midi1_msg;
+ }
+
+ /* Set interval to next transaction. */
+ ff->next_ktime[port] = ktime_add_ns(ktime_get(),
+ ff->rx_bytes[port] * 8 * (NSEC_PER_SEC / 31250));
+
+ if (quad_count == 1)
+ tcode = TCODE_WRITE_QUADLET_REQUEST;
+ else
+ tcode = TCODE_WRITE_BLOCK_REQUEST;
+
+ /*
+ * In Linux FireWire core, when generation is updated with memory
+ * barrier, node id has already been updated. In this module, After
+ * this smp_rmb(), load/store instructions to memory are completed.
+ * Thus, both of generation and node id are available with recent
+ * values. This is a light-serialization solution to handle bus reset
+ * events on IEEE 1394 bus.
+ */
+ generation = fw_dev->generation;
+ smp_rmb();
+ fw_send_request(fw_dev->card, &ff->transactions[port], tcode,
+ fw_dev->node_id, generation, fw_dev->max_speed,
+ addr, &ff->msg_buf[port], quad_count * 4,
+ callback, &ff->transactions[port]);
+}
+
+static void transmit_midi0_msg(struct work_struct *work)
+{
+ struct snd_ff *ff = container_of(work, struct snd_ff, rx_midi_work[0]);
+
+ transmit_midi_msg(ff, 0);
+}
+
+static void transmit_midi1_msg(struct work_struct *work)
+{
+ struct snd_ff *ff = container_of(work, struct snd_ff, rx_midi_work[1]);
+
+ transmit_midi_msg(ff, 1);
+}
+
+static void handle_msg(struct fw_card *card, struct fw_request *request, int tcode,
+ int destination, int source, int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_ff *ff = callback_data;
+ __le32 *buf = data;
+ u32 tstamp = fw_request_get_timestamp(request);
+
+ fw_send_response(card, request, RCODE_COMPLETE);
+
+ offset -= ff->async_handler.offset;
+
+ guard(spinlock_irqsave)(&ff->lock);
+ ff->spec->protocol->handle_msg(ff, (unsigned int)offset, buf, length, tstamp);
+}
+
+static int allocate_own_address(struct snd_ff *ff, int i)
+{
+ struct fw_address_region midi_msg_region;
+ int err;
+
+ ff->async_handler.length = ff->spec->midi_addr_range;
+ ff->async_handler.address_callback = handle_msg;
+ ff->async_handler.callback_data = ff;
+
+ midi_msg_region.start = 0x000100000000ull * i;
+ midi_msg_region.end = midi_msg_region.start + ff->async_handler.length;
+
+ err = fw_core_add_address_handler(&ff->async_handler, &midi_msg_region);
+ if (err >= 0) {
+ /* Controllers are allowed to register this region. */
+ if (ff->async_handler.offset & 0x0000ffffffff) {
+ fw_core_remove_address_handler(&ff->async_handler);
+ err = -EAGAIN;
+ }
+ }
+
+ return err;
+}
+
+// Controllers are allowed to register higher 4 bytes of destination address to
+// receive asynchronous transactions for MIDI messages, while the way to
+// register lower 4 bytes of address is different depending on protocols. For
+// details, please refer to comments in protocol implementations.
+//
+// This driver expects userspace applications to configure registers for the
+// lower address because in most cases such registers has the other settings.
+int snd_ff_transaction_reregister(struct snd_ff *ff)
+{
+ struct fw_card *fw_card = fw_parent_device(ff->unit)->card;
+ u32 addr;
+ __le32 reg;
+
+ /*
+ * Controllers are allowed to register its node ID and upper 2 byte of
+ * local address to listen asynchronous transactions.
+ */
+ addr = (fw_card->node_id << 16) | (ff->async_handler.offset >> 32);
+ reg = cpu_to_le32(addr);
+ return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ ff->spec->midi_high_addr,
+ &reg, sizeof(reg), 0);
+}
+
+int snd_ff_transaction_register(struct snd_ff *ff)
+{
+ int i, err;
+
+ /*
+ * Allocate in Memory Space of IEC 13213, but lower 4 byte in LSB should
+ * be zero due to device specification.
+ */
+ for (i = 0; i < 0xffff; i++) {
+ err = allocate_own_address(ff, i);
+ if (err != -EBUSY && err != -EAGAIN)
+ break;
+ }
+ if (err < 0)
+ return err;
+
+ err = snd_ff_transaction_reregister(ff);
+ if (err < 0)
+ return err;
+
+ INIT_WORK(&ff->rx_midi_work[0], transmit_midi0_msg);
+ INIT_WORK(&ff->rx_midi_work[1], transmit_midi1_msg);
+
+ return 0;
+}
+
+void snd_ff_transaction_unregister(struct snd_ff *ff)
+{
+ __le32 reg;
+
+ if (ff->async_handler.callback_data == NULL)
+ return;
+ ff->async_handler.callback_data = NULL;
+
+ /* Release higher 4 bytes of address. */
+ reg = cpu_to_le32(0x00000000);
+ snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ ff->spec->midi_high_addr,
+ &reg, sizeof(reg), 0);
+
+ fw_core_remove_address_handler(&ff->async_handler);
+}
diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c
new file mode 100644
index 000000000000..5d2c4fbf4434
--- /dev/null
+++ b/sound/firewire/fireface/ff.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ff.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ */
+
+#include "ff.h"
+
+#define OUI_RME 0x000a35
+
+MODULE_DESCRIPTION("RME Fireface series Driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL");
+
+static void name_card(struct snd_ff *ff)
+{
+ struct fw_device *fw_dev = fw_parent_device(ff->unit);
+ static const char *const names[] = {
+ [SND_FF_UNIT_VERSION_FF800] = "Fireface800",
+ [SND_FF_UNIT_VERSION_FF400] = "Fireface400",
+ [SND_FF_UNIT_VERSION_UFX] = "FirefaceUFX",
+ [SND_FF_UNIT_VERSION_UCX] = "FirefaceUCX",
+ [SND_FF_UNIT_VERSION_802] = "Fireface802",
+ };
+ const char *name;
+
+ name = names[ff->unit_version];
+
+ strscpy(ff->card->driver, "Fireface");
+ strscpy(ff->card->shortname, name);
+ strscpy(ff->card->mixername, name);
+ snprintf(ff->card->longname, sizeof(ff->card->longname),
+ "RME %s, GUID %08x%08x at %s, S%d", name,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&ff->unit->device), 100 << fw_dev->max_speed);
+}
+
+static void ff_card_free(struct snd_card *card)
+{
+ struct snd_ff *ff = card->private_data;
+
+ snd_ff_stream_destroy_duplex(ff);
+ snd_ff_transaction_unregister(ff);
+
+ kfree(ff->msg_parser);
+
+ mutex_destroy(&ff->mutex);
+ fw_unit_put(ff->unit);
+}
+
+static int snd_ff_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_ff *ff;
+ int err;
+
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*ff), &card);
+ if (err < 0)
+ return err;
+ card->private_free = ff_card_free;
+
+ ff = card->private_data;
+ ff->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, ff);
+ ff->card = card;
+
+ mutex_init(&ff->mutex);
+ spin_lock_init(&ff->lock);
+ init_waitqueue_head(&ff->hwdep_wait);
+
+ ff->unit_version = entry->version;
+ ff->spec = (const struct snd_ff_spec *)entry->driver_data;
+
+ err = snd_ff_transaction_register(ff);
+ if (err < 0)
+ goto error;
+
+ name_card(ff);
+
+ err = snd_ff_stream_init_duplex(ff);
+ if (err < 0)
+ goto error;
+
+ snd_ff_proc_init(ff);
+
+ err = snd_ff_create_midi_devices(ff);
+ if (err < 0)
+ goto error;
+
+ err = snd_ff_create_pcm_devices(ff);
+ if (err < 0)
+ goto error;
+
+ err = snd_ff_create_hwdep_devices(ff);
+ if (err < 0)
+ goto error;
+
+ if (ff->spec->protocol->msg_parser_size > 0) {
+ ff->msg_parser = kzalloc(ff->spec->protocol->msg_parser_size, GFP_KERNEL);
+ if (!ff->msg_parser) {
+ err = -ENOMEM;
+ goto error;
+ }
+ }
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void snd_ff_update(struct fw_unit *unit)
+{
+ struct snd_ff *ff = dev_get_drvdata(&unit->device);
+
+ snd_ff_transaction_reregister(ff);
+
+ snd_ff_stream_update_duplex(ff);
+}
+
+static void snd_ff_remove(struct fw_unit *unit)
+{
+ struct snd_ff *ff = dev_get_drvdata(&unit->device);
+
+ // Block till all of ALSA character devices are released.
+ snd_card_free(ff->card);
+}
+
+static const struct snd_ff_spec spec_ff800 = {
+ .pcm_capture_channels = {28, 20, 12},
+ .pcm_playback_channels = {28, 20, 12},
+ .midi_in_ports = 1,
+ .midi_out_ports = 1,
+ .protocol = &snd_ff_protocol_ff800,
+ .midi_high_addr = 0x000200000320ull,
+ .midi_addr_range = 12,
+ .midi_rx_addrs = {0x000080180000ull, 0},
+};
+
+static const struct snd_ff_spec spec_ff400 = {
+ .pcm_capture_channels = {18, 14, 10},
+ .pcm_playback_channels = {18, 14, 10},
+ .midi_in_ports = 2,
+ .midi_out_ports = 2,
+ .protocol = &snd_ff_protocol_ff400,
+ .midi_high_addr = 0x0000801003f4ull,
+ .midi_addr_range = SND_FF_MAXIMIM_MIDI_QUADS * 4,
+ .midi_rx_addrs = {0x000080180000ull, 0x000080190000ull},
+};
+
+static const struct snd_ff_spec spec_ucx = {
+ .pcm_capture_channels = {18, 14, 12},
+ .pcm_playback_channels = {18, 14, 12},
+ .midi_in_ports = 2,
+ .midi_out_ports = 2,
+ .protocol = &snd_ff_protocol_latter,
+ .midi_high_addr = 0xffff00000034ull,
+ .midi_addr_range = 0x80,
+ .midi_rx_addrs = {0xffff00000030ull, 0xffff00000030ull},
+};
+
+static const struct snd_ff_spec spec_ufx_802 = {
+ .pcm_capture_channels = {30, 22, 14},
+ .pcm_playback_channels = {30, 22, 14},
+ .midi_in_ports = 1,
+ .midi_out_ports = 1,
+ .protocol = &snd_ff_protocol_latter,
+ .midi_high_addr = 0xffff00000034ull,
+ .midi_addr_range = 0x80,
+ .midi_rx_addrs = {0xffff00000030ull, 0xffff00000030ull},
+};
+
+static const struct ieee1394_device_id snd_ff_id_table[] = {
+ /* Fireface 800 */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_RME,
+ .specifier_id = OUI_RME,
+ .version = SND_FF_UNIT_VERSION_FF800,
+ .model_id = 0x101800,
+ .driver_data = (kernel_ulong_t)&spec_ff800,
+ },
+ /* Fireface 400 */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_RME,
+ .specifier_id = OUI_RME,
+ .version = SND_FF_UNIT_VERSION_FF400,
+ .model_id = 0x101800,
+ .driver_data = (kernel_ulong_t)&spec_ff400,
+ },
+ // Fireface UFX.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_RME,
+ .specifier_id = OUI_RME,
+ .version = SND_FF_UNIT_VERSION_UFX,
+ .model_id = 0x101800,
+ .driver_data = (kernel_ulong_t)&spec_ufx_802,
+ },
+ // Fireface UCX.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_RME,
+ .specifier_id = OUI_RME,
+ .version = SND_FF_UNIT_VERSION_UCX,
+ .model_id = 0x101800,
+ .driver_data = (kernel_ulong_t)&spec_ucx,
+ },
+ // Fireface 802.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_RME,
+ .specifier_id = OUI_RME,
+ .version = SND_FF_UNIT_VERSION_802,
+ .model_id = 0x101800,
+ .driver_data = (kernel_ulong_t)&spec_ufx_802,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, snd_ff_id_table);
+
+static struct fw_driver ff_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = snd_ff_probe,
+ .update = snd_ff_update,
+ .remove = snd_ff_remove,
+ .id_table = snd_ff_id_table,
+};
+
+static int __init snd_ff_init(void)
+{
+ return driver_register(&ff_driver.driver);
+}
+
+static void __exit snd_ff_exit(void)
+{
+ driver_unregister(&ff_driver.driver);
+}
+
+module_init(snd_ff_init);
+module_exit(snd_ff_exit);
diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h
new file mode 100644
index 000000000000..7e42f5778a8a
--- /dev/null
+++ b/sound/firewire/fireface/ff.h
@@ -0,0 +1,171 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ff.h - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ */
+
+#ifndef SOUND_FIREFACE_H_INCLUDED
+#define SOUND_FIREFACE_H_INCLUDED
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/sched/signal.h>
+
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/hwdep.h>
+#include <sound/firewire.h>
+
+#include "../lib.h"
+#include "../amdtp-stream.h"
+#include "../iso-resources.h"
+
+#define SND_FF_MAXIMIM_MIDI_QUADS 9
+#define SND_FF_IN_MIDI_PORTS 2
+#define SND_FF_OUT_MIDI_PORTS 2
+
+enum snd_ff_unit_version {
+ SND_FF_UNIT_VERSION_FF800 = 0x000001,
+ SND_FF_UNIT_VERSION_FF400 = 0x000002,
+ SND_FF_UNIT_VERSION_UFX = 0x000003,
+ SND_FF_UNIT_VERSION_UCX = 0x000004,
+ SND_FF_UNIT_VERSION_802 = 0x000005,
+};
+
+enum snd_ff_stream_mode {
+ SND_FF_STREAM_MODE_LOW = 0,
+ SND_FF_STREAM_MODE_MID,
+ SND_FF_STREAM_MODE_HIGH,
+ SND_FF_STREAM_MODE_COUNT,
+};
+
+struct snd_ff_protocol;
+struct snd_ff_spec {
+ const unsigned int pcm_capture_channels[SND_FF_STREAM_MODE_COUNT];
+ const unsigned int pcm_playback_channels[SND_FF_STREAM_MODE_COUNT];
+
+ unsigned int midi_in_ports;
+ unsigned int midi_out_ports;
+
+ const struct snd_ff_protocol *protocol;
+ u64 midi_high_addr;
+ u8 midi_addr_range;
+ u64 midi_rx_addrs[SND_FF_OUT_MIDI_PORTS];
+};
+
+struct snd_ff {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ struct mutex mutex;
+ spinlock_t lock;
+
+ enum snd_ff_unit_version unit_version;
+ const struct snd_ff_spec *spec;
+
+ /* To handle MIDI tx. */
+ struct snd_rawmidi_substream *tx_midi_substreams[SND_FF_IN_MIDI_PORTS];
+ struct fw_address_handler async_handler;
+
+ /* TO handle MIDI rx. */
+ struct snd_rawmidi_substream *rx_midi_substreams[SND_FF_OUT_MIDI_PORTS];
+ bool on_sysex[SND_FF_OUT_MIDI_PORTS];
+ __le32 msg_buf[SND_FF_OUT_MIDI_PORTS][SND_FF_MAXIMIM_MIDI_QUADS];
+ struct work_struct rx_midi_work[SND_FF_OUT_MIDI_PORTS];
+ struct fw_transaction transactions[SND_FF_OUT_MIDI_PORTS];
+ ktime_t next_ktime[SND_FF_OUT_MIDI_PORTS];
+ bool rx_midi_error[SND_FF_OUT_MIDI_PORTS];
+ unsigned int rx_bytes[SND_FF_OUT_MIDI_PORTS];
+
+ unsigned int substreams_counter;
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ struct fw_iso_resources tx_resources;
+ struct fw_iso_resources rx_resources;
+
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ struct amdtp_domain domain;
+
+ void *msg_parser;
+};
+
+enum snd_ff_clock_src {
+ SND_FF_CLOCK_SRC_INTERNAL,
+ SND_FF_CLOCK_SRC_SPDIF,
+ SND_FF_CLOCK_SRC_ADAT1,
+ SND_FF_CLOCK_SRC_ADAT2,
+ SND_FF_CLOCK_SRC_WORD,
+ SND_FF_CLOCK_SRC_LTC,
+ /* TODO: perhaps TCO exists. */
+};
+
+struct snd_ff_protocol {
+ size_t msg_parser_size;
+ bool (*has_msg)(struct snd_ff *ff);
+ long (*copy_msg_to_user)(struct snd_ff *ff, char __user *buf, long count);
+ void (*handle_msg)(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
+ size_t length, u32 tstamp);
+ int (*fill_midi_msg)(struct snd_ff *ff,
+ struct snd_rawmidi_substream *substream,
+ unsigned int port);
+ int (*get_clock)(struct snd_ff *ff, unsigned int *rate,
+ enum snd_ff_clock_src *src);
+ int (*switch_fetching_mode)(struct snd_ff *ff, bool enable);
+ int (*allocate_resources)(struct snd_ff *ff, unsigned int rate);
+ int (*begin_session)(struct snd_ff *ff, unsigned int rate);
+ void (*finish_session)(struct snd_ff *ff);
+ void (*dump_status)(struct snd_ff *ff, struct snd_info_buffer *buffer);
+};
+
+extern const struct snd_ff_protocol snd_ff_protocol_ff800;
+extern const struct snd_ff_protocol snd_ff_protocol_ff400;
+extern const struct snd_ff_protocol snd_ff_protocol_latter;
+
+int snd_ff_transaction_register(struct snd_ff *ff);
+int snd_ff_transaction_reregister(struct snd_ff *ff);
+void snd_ff_transaction_unregister(struct snd_ff *ff);
+
+int amdtp_ff_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels);
+int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir);
+
+int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
+ enum snd_ff_stream_mode *mode);
+int snd_ff_stream_init_duplex(struct snd_ff *ff);
+void snd_ff_stream_destroy_duplex(struct snd_ff *ff);
+int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer);
+int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate);
+void snd_ff_stream_stop_duplex(struct snd_ff *ff);
+void snd_ff_stream_update_duplex(struct snd_ff *ff);
+
+void snd_ff_stream_lock_changed(struct snd_ff *ff);
+int snd_ff_stream_lock_try(struct snd_ff *ff);
+void snd_ff_stream_lock_release(struct snd_ff *ff);
+
+void snd_ff_proc_init(struct snd_ff *ff);
+const char *snd_ff_proc_get_clk_label(enum snd_ff_clock_src src);
+
+int snd_ff_create_midi_devices(struct snd_ff *ff);
+
+int snd_ff_create_pcm_devices(struct snd_ff *ff);
+
+int snd_ff_create_hwdep_devices(struct snd_ff *ff);
+
+#endif
diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
new file mode 100644
index 000000000000..baaf3066c9b1
--- /dev/null
+++ b/sound/firewire/fireworks/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-fireworks-y := fireworks_transaction.o fireworks_command.o \
+ fireworks_stream.o fireworks_proc.o fireworks_midi.o \
+ fireworks_pcm.o fireworks_hwdep.o fireworks.o
+obj-$(CONFIG_SND_FIREWORKS) += snd-fireworks.o
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
new file mode 100644
index 000000000000..3378c7dce88a
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks.c
@@ -0,0 +1,366 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * fireworks.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+/*
+ * Fireworks is a board module which Echo Audio produced. This module consists
+ * of three chipsets:
+ * - Communication chipset for IEEE1394 PHY/Link and IEC 61883-1/6
+ * - DSP or/and FPGA for signal processing
+ * - Flash Memory to store firmwares
+ */
+
+#include "fireworks.h"
+
+MODULE_DESCRIPTION("Echo Fireworks driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+unsigned int snd_efw_resp_buf_size = 1024;
+bool snd_efw_resp_buf_debug = false;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "card index");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "enable Fireworks sound card");
+module_param_named(resp_buf_size, snd_efw_resp_buf_size, uint, 0444);
+MODULE_PARM_DESC(resp_buf_size,
+ "response buffer size (max 4096, default 1024)");
+module_param_named(resp_buf_debug, snd_efw_resp_buf_debug, bool, 0444);
+MODULE_PARM_DESC(resp_buf_debug, "store all responses to buffer");
+
+static DEFINE_MUTEX(devices_mutex);
+static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
+
+#define VENDOR_LOUD 0x000ff2
+#define MODEL_MACKIE_400F 0x00400f
+#define MODEL_MACKIE_1200F 0x01200f
+
+#define VENDOR_ECHO 0x001486
+#define MODEL_ECHO_AUDIOFIRE_12 0x00af12
+#define MODEL_ECHO_AUDIOFIRE_12HD 0x0af12d
+#define MODEL_ECHO_AUDIOFIRE_12_APPLE 0x0af12a
+/* This is applied for AudioFire8 (until 2009 July) */
+#define MODEL_ECHO_AUDIOFIRE_8 0x000af8
+#define MODEL_ECHO_AUDIOFIRE_2 0x000af2
+#define MODEL_ECHO_AUDIOFIRE_4 0x000af4
+/* AudioFire9 is applied for AudioFire8(since 2009 July) and AudioFirePre8 */
+#define MODEL_ECHO_AUDIOFIRE_9 0x000af9
+/* unknown as product */
+#define MODEL_ECHO_FIREWORKS_8 0x0000f8
+#define MODEL_ECHO_FIREWORKS_HDMI 0x00afd1
+
+#define VENDOR_GIBSON 0x00075b
+/* for Robot Interface Pack of Dark Fire, Dusk Tiger, Les Paul Standard 2010 */
+#define MODEL_GIBSON_RIP 0x00afb2
+/* unknown as product */
+#define MODEL_GIBSON_GOLDTOP 0x00afb9
+
+/* part of hardware capability flags */
+#define FLAG_RESP_ADDR_CHANGABLE 0
+
+static int
+get_hardware_info(struct snd_efw *efw)
+{
+ struct fw_device *fw_dev = fw_parent_device(efw->unit);
+ struct snd_efw_hwinfo *hwinfo;
+ char version[12] = {0};
+ int err;
+
+ hwinfo = kzalloc(sizeof(struct snd_efw_hwinfo), GFP_KERNEL);
+ if (hwinfo == NULL)
+ return -ENOMEM;
+
+ err = snd_efw_command_get_hwinfo(efw, hwinfo);
+ if (err < 0)
+ goto end;
+
+ /* firmware version for communication chipset */
+ snprintf(version, sizeof(version), "%u.%u",
+ (hwinfo->arm_version >> 24) & 0xff,
+ (hwinfo->arm_version >> 16) & 0xff);
+ efw->firmware_version = hwinfo->arm_version;
+
+ strscpy(efw->card->driver, "Fireworks");
+ strscpy(efw->card->shortname, hwinfo->model_name);
+ strscpy(efw->card->mixername, hwinfo->model_name);
+ scnprintf(efw->card->longname, sizeof(efw->card->longname),
+ "%s %s v%s, GUID %08x%08x at %s, S%d",
+ hwinfo->vendor_name, hwinfo->model_name, version,
+ hwinfo->guid_hi, hwinfo->guid_lo,
+ dev_name(&efw->unit->device), 100 << fw_dev->max_speed);
+
+ if (hwinfo->flags & BIT(FLAG_RESP_ADDR_CHANGABLE))
+ efw->resp_addr_changable = true;
+
+ efw->supported_sampling_rate = 0;
+ if ((hwinfo->min_sample_rate <= 22050)
+ && (22050 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_22050;
+ if ((hwinfo->min_sample_rate <= 32000)
+ && (32000 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_32000;
+ if ((hwinfo->min_sample_rate <= 44100)
+ && (44100 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_44100;
+ if ((hwinfo->min_sample_rate <= 48000)
+ && (48000 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_48000;
+ if ((hwinfo->min_sample_rate <= 88200)
+ && (88200 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_88200;
+ if ((hwinfo->min_sample_rate <= 96000)
+ && (96000 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_96000;
+ if ((hwinfo->min_sample_rate <= 176400)
+ && (176400 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_176400;
+ if ((hwinfo->min_sample_rate <= 192000)
+ && (192000 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_192000;
+
+ /* the number of MIDI ports, not of MIDI conformant data channels */
+ if (hwinfo->midi_out_ports > SND_EFW_MAX_MIDI_OUT_PORTS ||
+ hwinfo->midi_in_ports > SND_EFW_MAX_MIDI_IN_PORTS) {
+ err = -EIO;
+ goto end;
+ }
+ efw->midi_out_ports = hwinfo->midi_out_ports;
+ efw->midi_in_ports = hwinfo->midi_in_ports;
+
+ if (hwinfo->amdtp_tx_pcm_channels > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_tx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_tx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_rx_pcm_channels > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_rx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_rx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM) {
+ err = -ENOSYS;
+ goto end;
+ }
+ efw->pcm_capture_channels[0] = hwinfo->amdtp_tx_pcm_channels;
+ efw->pcm_capture_channels[1] = hwinfo->amdtp_tx_pcm_channels_2x;
+ efw->pcm_capture_channels[2] = hwinfo->amdtp_tx_pcm_channels_4x;
+ efw->pcm_playback_channels[0] = hwinfo->amdtp_rx_pcm_channels;
+ efw->pcm_playback_channels[1] = hwinfo->amdtp_rx_pcm_channels_2x;
+ efw->pcm_playback_channels[2] = hwinfo->amdtp_rx_pcm_channels_4x;
+
+ /* Hardware metering. */
+ if (hwinfo->phys_in_grp_count > HWINFO_MAX_CAPS_GROUPS ||
+ hwinfo->phys_out_grp_count > HWINFO_MAX_CAPS_GROUPS) {
+ err = -EIO;
+ goto end;
+ }
+ efw->phys_in = hwinfo->phys_in;
+ efw->phys_out = hwinfo->phys_out;
+ efw->phys_in_grp_count = hwinfo->phys_in_grp_count;
+ efw->phys_out_grp_count = hwinfo->phys_out_grp_count;
+ memcpy(&efw->phys_in_grps, hwinfo->phys_in_grps,
+ sizeof(struct snd_efw_phys_grp) * hwinfo->phys_in_grp_count);
+ memcpy(&efw->phys_out_grps, hwinfo->phys_out_grps,
+ sizeof(struct snd_efw_phys_grp) * hwinfo->phys_out_grp_count);
+
+ /* AudioFire8 (since 2009) and AudioFirePre8 */
+ if (hwinfo->type == MODEL_ECHO_AUDIOFIRE_9)
+ efw->is_af9 = true;
+ /* These models uses the same firmware. */
+ if (hwinfo->type == MODEL_ECHO_AUDIOFIRE_2 ||
+ hwinfo->type == MODEL_ECHO_AUDIOFIRE_4 ||
+ hwinfo->type == MODEL_ECHO_AUDIOFIRE_9 ||
+ hwinfo->type == MODEL_GIBSON_RIP ||
+ hwinfo->type == MODEL_GIBSON_GOLDTOP)
+ efw->is_fireworks3 = true;
+end:
+ kfree(hwinfo);
+ return err;
+}
+
+static void
+efw_card_free(struct snd_card *card)
+{
+ struct snd_efw *efw = card->private_data;
+
+ scoped_guard(mutex, &devices_mutex) {
+ clear_bit(efw->card_index, devices_used);
+ }
+
+ snd_efw_stream_destroy_duplex(efw);
+ snd_efw_transaction_remove_instance(efw);
+
+ mutex_destroy(&efw->mutex);
+ fw_unit_put(efw->unit);
+}
+
+static int efw_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+ unsigned int card_index;
+ struct snd_card *card;
+ struct snd_efw *efw;
+ int err;
+
+ // check registered cards.
+ scoped_guard(mutex, &devices_mutex) {
+ for (card_index = 0; card_index < SNDRV_CARDS; ++card_index) {
+ if (!test_bit(card_index, devices_used) && enable[card_index])
+ break;
+ }
+ if (card_index >= SNDRV_CARDS)
+ return -ENOENT;
+
+ err = snd_card_new(&unit->device, index[card_index], id[card_index], THIS_MODULE,
+ sizeof(*efw), &card);
+ if (err < 0)
+ return err;
+ card->private_free = efw_card_free;
+ set_bit(card_index, devices_used);
+ }
+
+ efw = card->private_data;
+ efw->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, efw);
+ efw->card = card;
+ efw->card_index = card_index;
+
+ mutex_init(&efw->mutex);
+ spin_lock_init(&efw->lock);
+ init_waitqueue_head(&efw->hwdep_wait);
+
+ // prepare response buffer.
+ snd_efw_resp_buf_size = clamp(snd_efw_resp_buf_size, SND_EFW_RESPONSE_MAXIMUM_BYTES, 4096U);
+ efw->resp_buf = devm_kzalloc(&card->card_dev, snd_efw_resp_buf_size, GFP_KERNEL);
+ if (!efw->resp_buf) {
+ err = -ENOMEM;
+ goto error;
+ }
+ efw->pull_ptr = efw->push_ptr = efw->resp_buf;
+ snd_efw_transaction_add_instance(efw);
+
+ err = get_hardware_info(efw);
+ if (err < 0)
+ goto error;
+
+ err = snd_efw_stream_init_duplex(efw);
+ if (err < 0)
+ goto error;
+
+ snd_efw_proc_init(efw);
+
+ if (efw->midi_out_ports || efw->midi_in_ports) {
+ err = snd_efw_create_midi_devices(efw);
+ if (err < 0)
+ goto error;
+ }
+
+ err = snd_efw_create_pcm_devices(efw);
+ if (err < 0)
+ goto error;
+
+ err = snd_efw_create_hwdep_device(efw);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void efw_update(struct fw_unit *unit)
+{
+ struct snd_efw *efw = dev_get_drvdata(&unit->device);
+
+ snd_efw_transaction_bus_reset(efw->unit);
+
+ guard(mutex)(&efw->mutex);
+ snd_efw_stream_update_duplex(efw);
+}
+
+static void efw_remove(struct fw_unit *unit)
+{
+ struct snd_efw *efw = dev_get_drvdata(&unit->device);
+
+ // Block till all of ALSA character devices are released.
+ snd_card_free(efw->card);
+}
+
+#define SPECIFIER_1394TA 0x00a02d
+#define VERSION_EFW 0x010000
+
+#define SND_EFW_DEV_ENTRY(vendor, model) \
+{ \
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | \
+ IEEE1394_MATCH_MODEL_ID | \
+ IEEE1394_MATCH_SPECIFIER_ID | \
+ IEEE1394_MATCH_VERSION, \
+ .vendor_id = vendor,\
+ .model_id = model, \
+ .specifier_id = SPECIFIER_1394TA, \
+ .version = VERSION_EFW, \
+}
+
+static const struct ieee1394_device_id efw_id_table[] = {
+ SND_EFW_DEV_ENTRY(VENDOR_LOUD, MODEL_MACKIE_400F),
+ SND_EFW_DEV_ENTRY(VENDOR_LOUD, MODEL_MACKIE_1200F),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_8),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12HD),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12_APPLE),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_2),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_4),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_9),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_FIREWORKS_8),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_FIREWORKS_HDMI),
+ SND_EFW_DEV_ENTRY(VENDOR_GIBSON, MODEL_GIBSON_RIP),
+ SND_EFW_DEV_ENTRY(VENDOR_GIBSON, MODEL_GIBSON_GOLDTOP),
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, efw_id_table);
+
+static struct fw_driver efw_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = efw_probe,
+ .update = efw_update,
+ .remove = efw_remove,
+ .id_table = efw_id_table,
+};
+
+static int __init snd_efw_init(void)
+{
+ int err;
+
+ err = snd_efw_transaction_register();
+ if (err < 0)
+ goto end;
+
+ err = driver_register(&efw_driver.driver);
+ if (err < 0)
+ snd_efw_transaction_unregister();
+
+end:
+ return err;
+}
+
+static void __exit snd_efw_exit(void)
+{
+ snd_efw_transaction_unregister();
+ driver_unregister(&efw_driver.driver);
+}
+
+module_init(snd_efw_init);
+module_exit(snd_efw_exit);
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
new file mode 100644
index 000000000000..c8d5879efe28
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks.h
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * fireworks.h - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+#ifndef SOUND_FIREWORKS_H_INCLUDED
+#define SOUND_FIREWORKS_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/sched/signal.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+
+#include "../packets-buffer.h"
+#include "../iso-resources.h"
+#include "../amdtp-am824.h"
+#include "../cmp.h"
+#include "../lib.h"
+
+#define SND_EFW_MAX_MIDI_OUT_PORTS 2
+#define SND_EFW_MAX_MIDI_IN_PORTS 2
+
+#define SND_EFW_MULTIPLIER_MODES 3
+#define HWINFO_NAME_SIZE_BYTES 32
+#define HWINFO_MAX_CAPS_GROUPS 8
+
+/*
+ * This should be greater than maximum bytes for EFW response content.
+ * Currently response against command for isochronous channel mapping is
+ * confirmed to be the maximum one. But for flexibility, use maximum data
+ * payload for asynchronous primary packets at S100 (Cable base rate) in
+ * IEEE Std 1394-1995.
+ */
+#define SND_EFW_RESPONSE_MAXIMUM_BYTES 0x200U
+
+extern unsigned int snd_efw_resp_buf_size;
+extern bool snd_efw_resp_buf_debug;
+
+struct snd_efw_phys_grp {
+ u8 type; /* see enum snd_efw_grp_type */
+ u8 count;
+} __packed;
+
+struct snd_efw {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ int card_index;
+
+ struct mutex mutex;
+ spinlock_t lock;
+
+ /* for transaction */
+ u32 seqnum;
+ bool resp_addr_changable;
+
+ /* for quirks */
+ bool is_af9;
+ bool is_fireworks3;
+ u32 firmware_version;
+
+ unsigned int midi_in_ports;
+ unsigned int midi_out_ports;
+
+ unsigned int supported_sampling_rate;
+ unsigned int pcm_capture_channels[SND_EFW_MULTIPLIER_MODES];
+ unsigned int pcm_playback_channels[SND_EFW_MULTIPLIER_MODES];
+
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ struct cmp_connection out_conn;
+ struct cmp_connection in_conn;
+ unsigned int substreams_counter;
+
+ /* hardware metering parameters */
+ unsigned int phys_out;
+ unsigned int phys_in;
+ unsigned int phys_out_grp_count;
+ unsigned int phys_in_grp_count;
+ struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS];
+ struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS];
+
+ /* for uapi */
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* response queue */
+ u8 *resp_buf;
+ u8 *pull_ptr;
+ u8 *push_ptr;
+
+ struct amdtp_domain domain;
+};
+
+int snd_efw_transaction_cmd(struct fw_unit *unit,
+ const void *cmd, unsigned int size);
+int snd_efw_transaction_run(struct fw_unit *unit,
+ const void *cmd, unsigned int cmd_size,
+ void *resp, unsigned int resp_size);
+int snd_efw_transaction_register(void);
+void snd_efw_transaction_unregister(void);
+void snd_efw_transaction_bus_reset(struct fw_unit *unit);
+void snd_efw_transaction_add_instance(struct snd_efw *efw);
+void snd_efw_transaction_remove_instance(struct snd_efw *efw);
+
+struct snd_efw_hwinfo {
+ u32 flags;
+ u32 guid_hi;
+ u32 guid_lo;
+ u32 type;
+ u32 version;
+ char vendor_name[HWINFO_NAME_SIZE_BYTES];
+ char model_name[HWINFO_NAME_SIZE_BYTES];
+ u32 supported_clocks;
+ u32 amdtp_rx_pcm_channels;
+ u32 amdtp_tx_pcm_channels;
+ u32 phys_out;
+ u32 phys_in;
+ u32 phys_out_grp_count;
+ struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS];
+ u32 phys_in_grp_count;
+ struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS];
+ u32 midi_out_ports;
+ u32 midi_in_ports;
+ u32 max_sample_rate;
+ u32 min_sample_rate;
+ u32 dsp_version;
+ u32 arm_version;
+ u32 mixer_playback_channels;
+ u32 mixer_capture_channels;
+ u32 fpga_version;
+ u32 amdtp_rx_pcm_channels_2x;
+ u32 amdtp_tx_pcm_channels_2x;
+ u32 amdtp_rx_pcm_channels_4x;
+ u32 amdtp_tx_pcm_channels_4x;
+ u32 reserved[16];
+} __packed;
+enum snd_efw_grp_type {
+ SND_EFW_CH_TYPE_ANALOG = 0,
+ SND_EFW_CH_TYPE_SPDIF = 1,
+ SND_EFW_CH_TYPE_ADAT = 2,
+ SND_EFW_CH_TYPE_SPDIF_OR_ADAT = 3,
+ SND_EFW_CH_TYPE_ANALOG_MIRRORING = 4,
+ SND_EFW_CH_TYPE_HEADPHONES = 5,
+ SND_EFW_CH_TYPE_I2S = 6,
+ SND_EFW_CH_TYPE_GUITAR = 7,
+ SND_EFW_CH_TYPE_PIEZO_GUITAR = 8,
+ SND_EFW_CH_TYPE_GUITAR_STRING = 9,
+ SND_EFW_CH_TYPE_DUMMY
+};
+struct snd_efw_phys_meters {
+ u32 status; /* guitar state/midi signal/clock input detect */
+ u32 reserved0;
+ u32 reserved1;
+ u32 reserved2;
+ u32 reserved3;
+ u32 out_meters;
+ u32 in_meters;
+ u32 reserved4;
+ u32 reserved5;
+ u32 values[];
+} __packed;
+enum snd_efw_clock_source {
+ SND_EFW_CLOCK_SOURCE_INTERNAL = 0,
+ // Unused.
+ SND_EFW_CLOCK_SOURCE_WORDCLOCK = 2,
+ SND_EFW_CLOCK_SOURCE_SPDIF = 3,
+ SND_EFW_CLOCK_SOURCE_ADAT_1 = 4,
+ SND_EFW_CLOCK_SOURCE_ADAT_2 = 5,
+ SND_EFW_CLOCK_SOURCE_CONTINUOUS = 6 /* internal variable clock */
+};
+enum snd_efw_transport_mode {
+ SND_EFW_TRANSPORT_MODE_WINDOWS = 0,
+ SND_EFW_TRANSPORT_MODE_IEC61883 = 1,
+};
+int snd_efw_command_set_resp_addr(struct snd_efw *efw,
+ u16 addr_high, u32 addr_low);
+int snd_efw_command_set_tx_mode(struct snd_efw *efw,
+ enum snd_efw_transport_mode mode);
+int snd_efw_command_get_hwinfo(struct snd_efw *efw,
+ struct snd_efw_hwinfo *hwinfo);
+int snd_efw_command_get_phys_meters(struct snd_efw *efw,
+ struct snd_efw_phys_meters *meters,
+ unsigned int len);
+int snd_efw_command_get_clock_source(struct snd_efw *efw,
+ enum snd_efw_clock_source *source);
+int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate);
+int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate);
+
+int snd_efw_stream_init_duplex(struct snd_efw *efw);
+int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer);
+int snd_efw_stream_start_duplex(struct snd_efw *efw);
+void snd_efw_stream_stop_duplex(struct snd_efw *efw);
+void snd_efw_stream_update_duplex(struct snd_efw *efw);
+void snd_efw_stream_destroy_duplex(struct snd_efw *efw);
+void snd_efw_stream_lock_changed(struct snd_efw *efw);
+int snd_efw_stream_lock_try(struct snd_efw *efw);
+void snd_efw_stream_lock_release(struct snd_efw *efw);
+
+void snd_efw_proc_init(struct snd_efw *efw);
+
+int snd_efw_create_midi_devices(struct snd_efw *efw);
+
+int snd_efw_create_pcm_devices(struct snd_efw *efw);
+int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode);
+
+int snd_efw_create_hwdep_device(struct snd_efw *efw);
+
+#endif
diff --git a/sound/firewire/fireworks/fireworks_command.c b/sound/firewire/fireworks/fireworks_command.c
new file mode 100644
index 000000000000..2b595ee0bc35
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_command.c
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * fireworks_command.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./fireworks.h"
+
+/*
+ * This driver uses transaction version 1 or later to use extended hardware
+ * information. Then too old devices are not available.
+ *
+ * Each commands are not required to have continuous sequence numbers. This
+ * number is just used to match command and response.
+ *
+ * This module support a part of commands. Please see FFADO if you want to see
+ * whole commands. But there are some commands which FFADO don't implement.
+ *
+ * Fireworks also supports AV/C general commands and AV/C Stream Format
+ * Information commands. But this module don't use them.
+ */
+
+#define KERNEL_SEQNUM_MIN (SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 2)
+#define KERNEL_SEQNUM_MAX ((u32)~0)
+
+/* for clock source and sampling rate */
+struct efc_clock {
+ u32 source;
+ u32 sampling_rate;
+ u32 index;
+};
+
+/* command categories */
+enum efc_category {
+ EFC_CAT_HWINFO = 0,
+ EFC_CAT_TRANSPORT = 2,
+ EFC_CAT_HWCTL = 3,
+};
+
+/* hardware info category commands */
+enum efc_cmd_hwinfo {
+ EFC_CMD_HWINFO_GET_CAPS = 0,
+ EFC_CMD_HWINFO_GET_POLLED = 1,
+ EFC_CMD_HWINFO_SET_RESP_ADDR = 2
+};
+
+enum efc_cmd_transport {
+ EFC_CMD_TRANSPORT_SET_TX_MODE = 0
+};
+
+/* hardware control category commands */
+enum efc_cmd_hwctl {
+ EFC_CMD_HWCTL_SET_CLOCK = 0,
+ EFC_CMD_HWCTL_GET_CLOCK = 1,
+ EFC_CMD_HWCTL_IDENTIFY = 5
+};
+
+/* return values in response */
+enum efr_status {
+ EFR_STATUS_OK = 0,
+ EFR_STATUS_BAD = 1,
+ EFR_STATUS_BAD_COMMAND = 2,
+ EFR_STATUS_COMM_ERR = 3,
+ EFR_STATUS_BAD_QUAD_COUNT = 4,
+ EFR_STATUS_UNSUPPORTED = 5,
+ EFR_STATUS_1394_TIMEOUT = 6,
+ EFR_STATUS_DSP_TIMEOUT = 7,
+ EFR_STATUS_BAD_RATE = 8,
+ EFR_STATUS_BAD_CLOCK = 9,
+ EFR_STATUS_BAD_CHANNEL = 10,
+ EFR_STATUS_BAD_PAN = 11,
+ EFR_STATUS_FLASH_BUSY = 12,
+ EFR_STATUS_BAD_MIRROR = 13,
+ EFR_STATUS_BAD_LED = 14,
+ EFR_STATUS_BAD_PARAMETER = 15,
+ EFR_STATUS_INCOMPLETE = 0x80000000
+};
+
+static const char *const efr_status_names[] = {
+ [EFR_STATUS_OK] = "OK",
+ [EFR_STATUS_BAD] = "bad",
+ [EFR_STATUS_BAD_COMMAND] = "bad command",
+ [EFR_STATUS_COMM_ERR] = "comm err",
+ [EFR_STATUS_BAD_QUAD_COUNT] = "bad quad count",
+ [EFR_STATUS_UNSUPPORTED] = "unsupported",
+ [EFR_STATUS_1394_TIMEOUT] = "1394 timeout",
+ [EFR_STATUS_DSP_TIMEOUT] = "DSP timeout",
+ [EFR_STATUS_BAD_RATE] = "bad rate",
+ [EFR_STATUS_BAD_CLOCK] = "bad clock",
+ [EFR_STATUS_BAD_CHANNEL] = "bad channel",
+ [EFR_STATUS_BAD_PAN] = "bad pan",
+ [EFR_STATUS_FLASH_BUSY] = "flash busy",
+ [EFR_STATUS_BAD_MIRROR] = "bad mirror",
+ [EFR_STATUS_BAD_LED] = "bad LED",
+ [EFR_STATUS_BAD_PARAMETER] = "bad parameter",
+ [EFR_STATUS_BAD_PARAMETER + 1] = "incomplete"
+};
+
+static int
+efw_transaction(struct snd_efw *efw, unsigned int category,
+ unsigned int command,
+ const __be32 *params, unsigned int param_bytes,
+ const __be32 *resp, unsigned int resp_bytes)
+{
+ struct snd_efw_transaction *header;
+ __be32 *buf;
+ u32 seqnum;
+ unsigned int buf_bytes, cmd_bytes;
+ int err;
+
+ /* calculate buffer size*/
+ buf_bytes = sizeof(struct snd_efw_transaction) +
+ max(param_bytes, resp_bytes);
+
+ /* keep buffer */
+ buf = kzalloc(buf_bytes, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* to keep consistency of sequence number */
+ scoped_guard(spinlock, &efw->lock) {
+ if ((efw->seqnum < KERNEL_SEQNUM_MIN) ||
+ (efw->seqnum >= KERNEL_SEQNUM_MAX - 2))
+ efw->seqnum = KERNEL_SEQNUM_MIN;
+ else
+ efw->seqnum += 2;
+ seqnum = efw->seqnum;
+ }
+
+ /* fill transaction header fields */
+ cmd_bytes = sizeof(struct snd_efw_transaction) + param_bytes;
+ header = (struct snd_efw_transaction *)buf;
+ header->length = cpu_to_be32(cmd_bytes / sizeof(__be32));
+ header->version = cpu_to_be32(1);
+ header->seqnum = cpu_to_be32(seqnum);
+ header->category = cpu_to_be32(category);
+ header->command = cpu_to_be32(command);
+ header->status = 0;
+
+ /* fill transaction command parameters */
+ memcpy(header->params, params, param_bytes);
+
+ err = snd_efw_transaction_run(efw->unit, buf, cmd_bytes,
+ buf, buf_bytes);
+ if (err < 0)
+ goto end;
+
+ /* check transaction header fields */
+ if ((be32_to_cpu(header->version) < 1) ||
+ (be32_to_cpu(header->category) != category) ||
+ (be32_to_cpu(header->command) != command) ||
+ (be32_to_cpu(header->status) != EFR_STATUS_OK)) {
+ dev_err(&efw->unit->device, "EFW command failed [%u/%u]: %s\n",
+ be32_to_cpu(header->category),
+ be32_to_cpu(header->command),
+ efr_status_names[be32_to_cpu(header->status)]);
+ err = -EIO;
+ goto end;
+ }
+
+ if (resp == NULL)
+ goto end;
+
+ /* fill transaction response parameters */
+ memset((void *)resp, 0, resp_bytes);
+ resp_bytes = min_t(unsigned int, resp_bytes,
+ be32_to_cpu(header->length) * sizeof(__be32) -
+ sizeof(struct snd_efw_transaction));
+ memcpy((void *)resp, &buf[6], resp_bytes);
+end:
+ kfree(buf);
+ return err;
+}
+
+/*
+ * The address in host system for transaction response is changable when the
+ * device supports. struct hwinfo.flags includes its flag. The default is
+ * MEMORY_SPACE_EFW_RESPONSE.
+ */
+int snd_efw_command_set_resp_addr(struct snd_efw *efw,
+ u16 addr_high, u32 addr_low)
+{
+ __be32 addr[2];
+
+ addr[0] = cpu_to_be32(addr_high);
+ addr[1] = cpu_to_be32(addr_low);
+
+ if (!efw->resp_addr_changable)
+ return -ENOSYS;
+
+ return efw_transaction(efw, EFC_CAT_HWCTL,
+ EFC_CMD_HWINFO_SET_RESP_ADDR,
+ addr, sizeof(addr), NULL, 0);
+}
+
+/*
+ * This is for timestamp processing. In Windows mode, all 32bit fields of second
+ * CIP header in AMDTP transmit packet is used for 'presentation timestamp'. In
+ * 'no data' packet the value of this field is 0x90ffffff.
+ */
+int snd_efw_command_set_tx_mode(struct snd_efw *efw,
+ enum snd_efw_transport_mode mode)
+{
+ __be32 param = cpu_to_be32(mode);
+ return efw_transaction(efw, EFC_CAT_TRANSPORT,
+ EFC_CMD_TRANSPORT_SET_TX_MODE,
+ &param, sizeof(param), NULL, 0);
+}
+
+int snd_efw_command_get_hwinfo(struct snd_efw *efw,
+ struct snd_efw_hwinfo *hwinfo)
+{
+ int err;
+
+ err = efw_transaction(efw, EFC_CAT_HWINFO,
+ EFC_CMD_HWINFO_GET_CAPS,
+ NULL, 0, (__be32 *)hwinfo, sizeof(*hwinfo));
+ if (err < 0)
+ goto end;
+
+ be32_to_cpus(&hwinfo->flags);
+ be32_to_cpus(&hwinfo->guid_hi);
+ be32_to_cpus(&hwinfo->guid_lo);
+ be32_to_cpus(&hwinfo->type);
+ be32_to_cpus(&hwinfo->version);
+ be32_to_cpus(&hwinfo->supported_clocks);
+ be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels);
+ be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels);
+ be32_to_cpus(&hwinfo->phys_out);
+ be32_to_cpus(&hwinfo->phys_in);
+ be32_to_cpus(&hwinfo->phys_out_grp_count);
+ be32_to_cpus(&hwinfo->phys_in_grp_count);
+ be32_to_cpus(&hwinfo->midi_out_ports);
+ be32_to_cpus(&hwinfo->midi_in_ports);
+ be32_to_cpus(&hwinfo->max_sample_rate);
+ be32_to_cpus(&hwinfo->min_sample_rate);
+ be32_to_cpus(&hwinfo->dsp_version);
+ be32_to_cpus(&hwinfo->arm_version);
+ be32_to_cpus(&hwinfo->mixer_playback_channels);
+ be32_to_cpus(&hwinfo->mixer_capture_channels);
+ be32_to_cpus(&hwinfo->fpga_version);
+ be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels_2x);
+ be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels_2x);
+ be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels_4x);
+ be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels_4x);
+
+ /* ensure terminated */
+ hwinfo->vendor_name[HWINFO_NAME_SIZE_BYTES - 1] = '\0';
+ hwinfo->model_name[HWINFO_NAME_SIZE_BYTES - 1] = '\0';
+end:
+ return err;
+}
+
+int snd_efw_command_get_phys_meters(struct snd_efw *efw,
+ struct snd_efw_phys_meters *meters,
+ unsigned int len)
+{
+ u32 *buf = (u32 *)meters;
+ unsigned int i;
+ int err;
+
+ err = efw_transaction(efw, EFC_CAT_HWINFO,
+ EFC_CMD_HWINFO_GET_POLLED,
+ NULL, 0, (__be32 *)meters, len);
+ if (err >= 0)
+ for (i = 0; i < len / sizeof(u32); i++)
+ be32_to_cpus(&buf[i]);
+
+ return err;
+}
+
+static int
+command_get_clock(struct snd_efw *efw, struct efc_clock *clock)
+{
+ int err;
+
+ err = efw_transaction(efw, EFC_CAT_HWCTL,
+ EFC_CMD_HWCTL_GET_CLOCK,
+ NULL, 0,
+ (__be32 *)clock, sizeof(struct efc_clock));
+ if (err >= 0) {
+ be32_to_cpus(&clock->source);
+ be32_to_cpus(&clock->sampling_rate);
+ be32_to_cpus(&clock->index);
+ }
+
+ return err;
+}
+
+/* give UINT_MAX if set nothing */
+static int
+command_set_clock(struct snd_efw *efw,
+ unsigned int source, unsigned int rate)
+{
+ struct efc_clock clock = {0};
+ int err;
+
+ /* check arguments */
+ if ((source == UINT_MAX) && (rate == UINT_MAX)) {
+ err = -EINVAL;
+ goto end;
+ }
+
+ /* get current status */
+ err = command_get_clock(efw, &clock);
+ if (err < 0)
+ goto end;
+
+ /* no need */
+ if ((clock.source == source) && (clock.sampling_rate == rate))
+ goto end;
+
+ /* set params */
+ if ((source != UINT_MAX) && (clock.source != source))
+ clock.source = source;
+ if ((rate != UINT_MAX) && (clock.sampling_rate != rate))
+ clock.sampling_rate = rate;
+ clock.index = 0;
+
+ cpu_to_be32s(&clock.source);
+ cpu_to_be32s(&clock.sampling_rate);
+ cpu_to_be32s(&clock.index);
+
+ err = efw_transaction(efw, EFC_CAT_HWCTL,
+ EFC_CMD_HWCTL_SET_CLOCK,
+ (__be32 *)&clock, sizeof(struct efc_clock),
+ NULL, 0);
+ if (err < 0)
+ goto end;
+
+ /*
+ * With firmware version 5.8, just after changing clock state, these
+ * parameters are not immediately retrieved by get command. In my
+ * trial, there needs to be 100msec to get changed parameters.
+ */
+ msleep(150);
+end:
+ return err;
+}
+
+int snd_efw_command_get_clock_source(struct snd_efw *efw,
+ enum snd_efw_clock_source *source)
+{
+ int err;
+ struct efc_clock clock = {0};
+
+ err = command_get_clock(efw, &clock);
+ if (err >= 0)
+ *source = clock.source;
+
+ return err;
+}
+
+int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate)
+{
+ int err;
+ struct efc_clock clock = {0};
+
+ err = command_get_clock(efw, &clock);
+ if (err >= 0)
+ *rate = clock.sampling_rate;
+
+ return err;
+}
+
+int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate)
+{
+ return command_set_clock(efw, UINT_MAX, rate);
+}
+
diff --git a/sound/firewire/fireworks/fireworks_hwdep.c b/sound/firewire/fireworks/fireworks_hwdep.c
new file mode 100644
index 000000000000..7d6bd8ceeab3
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_hwdep.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * fireworks_hwdep.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+/*
+ * This codes have five functionalities.
+ *
+ * 1.get information about firewire node
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock streaming
+ * 4.transmit command of EFW transaction
+ * 5.receive response of EFW transaction
+ *
+ */
+
+#include "fireworks.h"
+
+static long
+hwdep_read_resp_buf(struct snd_efw *efw, char __user *buf, long remained,
+ loff_t *offset)
+{
+ unsigned int length, till_end, type;
+ struct snd_efw_transaction *t;
+ u8 *pull_ptr;
+ long count = 0;
+
+ if (remained < sizeof(type) + sizeof(struct snd_efw_transaction))
+ return -ENOSPC;
+
+ /* data type is SNDRV_FIREWIRE_EVENT_EFW_RESPONSE */
+ type = SNDRV_FIREWIRE_EVENT_EFW_RESPONSE;
+ if (copy_to_user(buf, &type, sizeof(type)))
+ return -EFAULT;
+ count += sizeof(type);
+ remained -= sizeof(type);
+ buf += sizeof(type);
+
+ /* write into buffer as many responses as possible */
+ spin_lock_irq(&efw->lock);
+
+ /*
+ * When another task reaches here during this task's access to user
+ * space, it picks up current position in buffer and can read the same
+ * series of responses.
+ */
+ pull_ptr = efw->pull_ptr;
+
+ while (efw->push_ptr != pull_ptr) {
+ t = (struct snd_efw_transaction *)(pull_ptr);
+ length = be32_to_cpu(t->length) * sizeof(__be32);
+
+ /* confirm enough space for this response */
+ if (remained < length)
+ break;
+
+ /* copy from ring buffer to user buffer */
+ while (length > 0) {
+ till_end = snd_efw_resp_buf_size -
+ (unsigned int)(pull_ptr - efw->resp_buf);
+ till_end = min_t(unsigned int, length, till_end);
+
+ spin_unlock_irq(&efw->lock);
+
+ if (copy_to_user(buf, pull_ptr, till_end))
+ return -EFAULT;
+
+ spin_lock_irq(&efw->lock);
+
+ pull_ptr += till_end;
+ if (pull_ptr >= efw->resp_buf + snd_efw_resp_buf_size)
+ pull_ptr -= snd_efw_resp_buf_size;
+
+ length -= till_end;
+ buf += till_end;
+ count += till_end;
+ remained -= till_end;
+ }
+ }
+
+ /*
+ * All of tasks can read from the buffer nearly simultaneously, but the
+ * last position for each task is different depending on the length of
+ * given buffer. Here, for simplicity, a position of buffer is set by
+ * the latest task. It's better for a listening application to allow one
+ * thread to read from the buffer. Unless, each task can read different
+ * sequence of responses depending on variation of buffer length.
+ */
+ efw->pull_ptr = pull_ptr;
+
+ spin_unlock_irq(&efw->lock);
+
+ return count;
+}
+
+static long
+hwdep_read_locked(struct snd_efw *efw, char __user *buf, long count,
+ loff_t *offset)
+{
+ union snd_firewire_event event = {
+ .lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
+ };
+
+ scoped_guard(spinlock_irq, &efw->lock) {
+ event.lock_status.status = (efw->dev_lock_count > 0);
+ efw->dev_lock_changed = false;
+ }
+
+ count = min_t(long, count, sizeof(event.lock_status));
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static long
+hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_efw *efw = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ bool dev_lock_changed;
+ bool queued;
+
+ spin_lock_irq(&efw->lock);
+
+ dev_lock_changed = efw->dev_lock_changed;
+ queued = efw->push_ptr != efw->pull_ptr;
+
+ while (!dev_lock_changed && !queued) {
+ prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&efw->lock);
+ schedule();
+ finish_wait(&efw->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&efw->lock);
+ dev_lock_changed = efw->dev_lock_changed;
+ queued = efw->push_ptr != efw->pull_ptr;
+ }
+
+ spin_unlock_irq(&efw->lock);
+
+ if (dev_lock_changed)
+ count = hwdep_read_locked(efw, buf, count, offset);
+ else if (queued)
+ count = hwdep_read_resp_buf(efw, buf, count, offset);
+
+ return count;
+}
+
+static long
+hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
+ loff_t *offset)
+{
+ struct snd_efw *efw = hwdep->private_data;
+ u32 seqnum;
+ u8 *buf;
+
+ if (count < sizeof(struct snd_efw_transaction) ||
+ SND_EFW_RESPONSE_MAXIMUM_BYTES < count)
+ return -EINVAL;
+
+ buf = memdup_user(data, count);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ /* check seqnum is not for kernel-land */
+ seqnum = be32_to_cpu(((struct snd_efw_transaction *)buf)->seqnum);
+ if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX) {
+ count = -EINVAL;
+ goto end;
+ }
+
+ if (snd_efw_transaction_cmd(efw->unit, buf, count) < 0)
+ count = -EIO;
+end:
+ kfree(buf);
+ return count;
+}
+
+static __poll_t
+hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
+{
+ struct snd_efw *efw = hwdep->private_data;
+ __poll_t events;
+
+ poll_wait(file, &efw->hwdep_wait, wait);
+
+ guard(spinlock_irq)(&efw->lock);
+ if (efw->dev_lock_changed || efw->pull_ptr != efw->push_ptr)
+ events = EPOLLIN | EPOLLRDNORM;
+ else
+ events = 0;
+ return events | EPOLLOUT;
+}
+
+static int
+hwdep_get_info(struct snd_efw *efw, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(efw->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_FIREWORKS;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strscpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int
+hwdep_lock(struct snd_efw *efw)
+{
+ guard(spinlock_irq)(&efw->lock);
+
+ if (efw->dev_lock_count == 0) {
+ efw->dev_lock_count = -1;
+ return 0;
+ } else {
+ return -EBUSY;
+ }
+}
+
+static int
+hwdep_unlock(struct snd_efw *efw)
+{
+ guard(spinlock_irq)(&efw->lock);
+
+ if (efw->dev_lock_count == -1) {
+ efw->dev_lock_count = 0;
+ return 0;
+ } else {
+ return -EBADFD;
+ }
+}
+
+static int
+hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_efw *efw = hwdep->private_data;
+
+ guard(spinlock_irq)(&efw->lock);
+ if (efw->dev_lock_count == -1)
+ efw->dev_lock_count = 0;
+
+ return 0;
+}
+
+static int
+hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_efw *efw = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(efw, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(efw);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(efw);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int
+hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_efw_create_hwdep_device(struct snd_efw *efw)
+{
+ static const struct snd_hwdep_ops ops = {
+ .read = hwdep_read,
+ .write = hwdep_write,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(efw->card, "Fireworks", 0, &hwdep);
+ if (err < 0)
+ goto end;
+ strscpy(hwdep->name, "Fireworks");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREWORKS;
+ hwdep->ops = ops;
+ hwdep->private_data = efw;
+ hwdep->exclusive = true;
+end:
+ return err;
+}
+
diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c
new file mode 100644
index 000000000000..405106a6aef9
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_midi.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * fireworks_midi.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+#include "fireworks.h"
+
+static int midi_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_efw *efw = substream->rmidi->private_data;
+ int err;
+
+ err = snd_efw_stream_lock_try(efw);
+ if (err < 0)
+ return err;
+
+ scoped_guard(mutex, &efw->mutex) {
+ err = snd_efw_stream_reserve_duplex(efw, 0, 0, 0);
+ if (err >= 0) {
+ ++efw->substreams_counter;
+ err = snd_efw_stream_start_duplex(efw);
+ if (err < 0)
+ --efw->substreams_counter;
+ }
+ }
+ if (err < 0)
+ snd_efw_stream_lock_release(efw);
+ return err;
+}
+
+static int midi_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_efw *efw = substream->rmidi->private_data;
+
+ scoped_guard(mutex, &efw->mutex) {
+ --efw->substreams_counter;
+ snd_efw_stream_stop_duplex(efw);
+ }
+
+ snd_efw_stream_lock_release(efw);
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_efw *efw = substrm->rmidi->private_data;
+
+ guard(spinlock_irqsave)(&efw->lock);
+
+ if (up)
+ amdtp_am824_midi_trigger(&efw->tx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_am824_midi_trigger(&efw->tx_stream,
+ substrm->number, NULL);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_efw *efw = substrm->rmidi->private_data;
+
+ guard(spinlock_irqsave)(&efw->lock);
+
+ if (up)
+ amdtp_am824_midi_trigger(&efw->rx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_am824_midi_trigger(&efw->rx_stream,
+ substrm->number, NULL);
+}
+
+static void set_midi_substream_names(struct snd_efw *efw,
+ struct snd_rawmidi_str *str)
+{
+ struct snd_rawmidi_substream *subs;
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ scnprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d", efw->card->shortname, subs->number + 1);
+ }
+}
+
+int snd_efw_create_midi_devices(struct snd_efw *efw)
+{
+ static const struct snd_rawmidi_ops capture_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_capture_trigger,
+ };
+ static const struct snd_rawmidi_ops playback_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_playback_trigger,
+ };
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *str;
+ int err;
+
+ /* create midi ports */
+ err = snd_rawmidi_new(efw->card, efw->card->driver, 0,
+ efw->midi_out_ports, efw->midi_in_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", efw->card->shortname);
+ rmidi->private_data = efw;
+
+ if (efw->midi_in_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &capture_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+ set_midi_substream_names(efw, str);
+ }
+
+ if (efw->midi_out_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &playback_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+ set_midi_substream_names(efw, str);
+ }
+
+ if ((efw->midi_out_ports > 0) && (efw->midi_in_ports > 0))
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c
new file mode 100644
index 000000000000..9399293a9fe9
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_pcm.c
@@ -0,0 +1,397 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * fireworks_pcm.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+#include "./fireworks.h"
+
+/*
+ * NOTE:
+ * Fireworks changes its AMDTP channels for PCM data according to its sampling
+ * rate. There are three modes. Here _XX is either _rx or _tx.
+ * 0: 32.0- 48.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels applied
+ * 1: 88.2- 96.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels_2x applied
+ * 2: 176.4-192.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels_4x applied
+ *
+ * The number of PCM channels for analog input and output are always fixed but
+ * the number of PCM channels for digital input and output are differed.
+ *
+ * Additionally, according to "AudioFire Owner's Manual Version 2.2", in some
+ * model, the number of PCM channels for digital input has more restriction
+ * depending on which digital interface is selected.
+ * - S/PDIF coaxial and optical : use input 1-2
+ * - ADAT optical at 32.0-48.0 kHz : use input 1-8
+ * - ADAT optical at 88.2-96.0 kHz : use input 1-4 (S/MUX format)
+ *
+ * The data in AMDTP channels for blank PCM channels are zero.
+ */
+static const unsigned int freq_table[] = {
+ /* multiplier mode 0 */
+ [0] = 32000,
+ [1] = 44100,
+ [2] = 48000,
+ /* multiplier mode 1 */
+ [3] = 88200,
+ [4] = 96000,
+ /* multiplier mode 2 */
+ [5] = 176400,
+ [6] = 192000,
+};
+
+static inline unsigned int
+get_multiplier_mode_with_index(unsigned int index)
+{
+ return ((int)index - 1) / 2;
+}
+
+int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+ if (freq_table[i] == sampling_rate) {
+ *mode = get_multiplier_mode_with_index(i);
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int
+hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+ unsigned int *pcm_channels = rule->private;
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i, mode;
+
+ for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+ mode = get_multiplier_mode_with_index(i);
+ if (!snd_interval_test(c, pcm_channels[mode]))
+ continue;
+
+ t.min = min(t.min, freq_table[i]);
+ t.max = max(t.max, freq_table[i]);
+ }
+
+ return snd_interval_refine(r, &t);
+}
+
+static int
+hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+ unsigned int *pcm_channels = rule->private;
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i, mode;
+
+ for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+ mode = get_multiplier_mode_with_index(i);
+ if (!snd_interval_test(r, freq_table[i]))
+ continue;
+
+ t.min = min(t.min, pcm_channels[mode]);
+ t.max = max(t.max, pcm_channels[mode]);
+ }
+
+ return snd_interval_refine(c, &t);
+}
+
+static void
+limit_channels(struct snd_pcm_hardware *hw, unsigned int *pcm_channels)
+{
+ unsigned int i, mode;
+
+ hw->channels_min = UINT_MAX;
+ hw->channels_max = 0;
+
+ for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+ mode = get_multiplier_mode_with_index(i);
+ if (pcm_channels[mode] == 0)
+ continue;
+
+ hw->channels_min = min(hw->channels_min, pcm_channels[mode]);
+ hw->channels_max = max(hw->channels_max, pcm_channels[mode]);
+ }
+}
+
+static int
+pcm_init_hw_params(struct snd_efw *efw,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct amdtp_stream *s;
+ unsigned int *pcm_channels;
+ int err;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
+ s = &efw->tx_stream;
+ pcm_channels = efw->pcm_capture_channels;
+ } else {
+ runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
+ s = &efw->rx_stream;
+ pcm_channels = efw->pcm_playback_channels;
+ }
+
+ /* limit rates */
+ runtime->hw.rates = efw->supported_sampling_rate;
+ snd_pcm_limit_hw_rates(runtime);
+
+ limit_channels(&runtime->hw, pcm_channels);
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels, pcm_channels,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ goto end;
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_rate, pcm_channels,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ goto end;
+
+ err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
+end:
+ return err;
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+ struct amdtp_domain *d = &efw->domain;
+ enum snd_efw_clock_source clock_source;
+ int err;
+
+ err = snd_efw_stream_lock_try(efw);
+ if (err < 0)
+ return err;
+
+ err = pcm_init_hw_params(efw, substream);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_efw_command_get_clock_source(efw, &clock_source);
+ if (err < 0)
+ goto err_locked;
+
+ scoped_guard(mutex, &efw->mutex) {
+ // When source of clock is not internal or any stream is reserved for
+ // transmission of PCM frames, the available sampling rate is limited
+ // at current one.
+ if ((clock_source != SND_EFW_CLOCK_SOURCE_INTERNAL) ||
+ (efw->substreams_counter > 0 && d->events_per_period > 0)) {
+ unsigned int frames_per_period = d->events_per_period;
+ unsigned int frames_per_buffer = d->events_per_buffer;
+ unsigned int sampling_rate;
+
+ err = snd_efw_command_get_sampling_rate(efw, &sampling_rate);
+ if (err < 0)
+ goto err_locked;
+ substream->runtime->hw.rate_min = sampling_rate;
+ substream->runtime->hw.rate_max = sampling_rate;
+
+ if (frames_per_period > 0) {
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ frames_per_period, frames_per_period);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ frames_per_buffer, frames_per_buffer);
+ if (err < 0)
+ goto err_locked;
+ }
+ }
+ }
+
+ snd_pcm_set_sync(substream);
+
+ return 0;
+err_locked:
+ snd_efw_stream_lock_release(efw);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+ snd_efw_stream_lock_release(efw);
+ return 0;
+}
+
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_efw *efw = substream->private_data;
+ int err = 0;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ unsigned int rate = params_rate(hw_params);
+ unsigned int frames_per_period = params_period_size(hw_params);
+ unsigned int frames_per_buffer = params_buffer_size(hw_params);
+
+ guard(mutex)(&efw->mutex);
+ err = snd_efw_stream_reserve_duplex(efw, rate,
+ frames_per_period, frames_per_buffer);
+ if (err >= 0)
+ ++efw->substreams_counter;
+ }
+
+ return err;
+}
+
+static int pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+
+ guard(mutex)(&efw->mutex);
+
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --efw->substreams_counter;
+
+ snd_efw_stream_stop_duplex(efw);
+
+ return 0;
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+ int err;
+
+ err = snd_efw_stream_start_duplex(efw);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&efw->tx_stream);
+
+ return err;
+}
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+ int err;
+
+ err = snd_efw_stream_start_duplex(efw);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&efw->rx_stream);
+
+ return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_efw *efw = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&efw->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&efw->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_efw *efw = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&efw->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&efw->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_efw *efw = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&efw->domain, &efw->tx_stream);
+}
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_efw *efw = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&efw->domain, &efw->rx_stream);
+}
+
+static int pcm_capture_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&efw->domain, &efw->tx_stream);
+}
+
+static int pcm_playback_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&efw->domain, &efw->rx_stream);
+}
+
+int snd_efw_create_pcm_devices(struct snd_efw *efw)
+{
+ static const struct snd_pcm_ops capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .ack = pcm_capture_ack,
+ };
+ static const struct snd_pcm_ops playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .ack = pcm_playback_ack,
+ };
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(efw->card, efw->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ goto end;
+
+ pcm->private_data = efw;
+ pcm->nonatomic = true;
+ snprintf(pcm->name, sizeof(pcm->name), "%s PCM", efw->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
+end:
+ return err;
+}
+
diff --git a/sound/firewire/fireworks/fireworks_proc.c b/sound/firewire/fireworks/fireworks_proc.c
new file mode 100644
index 000000000000..12288567b0cd
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_proc.c
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * fireworks_proc.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+#include "./fireworks.h"
+
+static inline const char*
+get_phys_name(struct snd_efw_phys_grp *grp, bool input)
+{
+ static const char *const ch_type[] = {
+ "Analog", "S/PDIF", "ADAT", "S/PDIF or ADAT", "Mirroring",
+ "Headphones", "I2S", "Guitar", "Pirzo Guitar", "Guitar String",
+ };
+
+ if (grp->type < ARRAY_SIZE(ch_type))
+ return ch_type[grp->type];
+ else if (input)
+ return "Input";
+ else
+ return "Output";
+}
+
+static void
+proc_read_hwinfo(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+ struct snd_efw *efw = entry->private_data;
+ unsigned short i;
+ struct snd_efw_hwinfo *hwinfo;
+
+ hwinfo = kmalloc(sizeof(struct snd_efw_hwinfo), GFP_KERNEL);
+ if (hwinfo == NULL)
+ return;
+
+ if (snd_efw_command_get_hwinfo(efw, hwinfo) < 0)
+ goto end;
+
+ snd_iprintf(buffer, "guid_hi: 0x%X\n", hwinfo->guid_hi);
+ snd_iprintf(buffer, "guid_lo: 0x%X\n", hwinfo->guid_lo);
+ snd_iprintf(buffer, "type: 0x%X\n", hwinfo->type);
+ snd_iprintf(buffer, "version: 0x%X\n", hwinfo->version);
+ snd_iprintf(buffer, "vendor_name: %s\n", hwinfo->vendor_name);
+ snd_iprintf(buffer, "model_name: %s\n", hwinfo->model_name);
+
+ snd_iprintf(buffer, "dsp_version: 0x%X\n", hwinfo->dsp_version);
+ snd_iprintf(buffer, "arm_version: 0x%X\n", hwinfo->arm_version);
+ snd_iprintf(buffer, "fpga_version: 0x%X\n", hwinfo->fpga_version);
+
+ snd_iprintf(buffer, "flags: 0x%X\n", hwinfo->flags);
+
+ snd_iprintf(buffer, "max_sample_rate: 0x%X\n", hwinfo->max_sample_rate);
+ snd_iprintf(buffer, "min_sample_rate: 0x%X\n", hwinfo->min_sample_rate);
+ snd_iprintf(buffer, "supported_clock: 0x%X\n",
+ hwinfo->supported_clocks);
+
+ snd_iprintf(buffer, "phys out: 0x%X\n", hwinfo->phys_out);
+ snd_iprintf(buffer, "phys in: 0x%X\n", hwinfo->phys_in);
+
+ snd_iprintf(buffer, "phys in grps: 0x%X\n",
+ hwinfo->phys_in_grp_count);
+ for (i = 0; i < hwinfo->phys_in_grp_count; i++) {
+ snd_iprintf(buffer,
+ "phys in grp[%d]: type 0x%X, count 0x%X\n",
+ i, hwinfo->phys_out_grps[i].type,
+ hwinfo->phys_out_grps[i].count);
+ }
+
+ snd_iprintf(buffer, "phys out grps: 0x%X\n",
+ hwinfo->phys_out_grp_count);
+ for (i = 0; i < hwinfo->phys_out_grp_count; i++) {
+ snd_iprintf(buffer,
+ "phys out grps[%d]: type 0x%X, count 0x%X\n",
+ i, hwinfo->phys_out_grps[i].type,
+ hwinfo->phys_out_grps[i].count);
+ }
+
+ snd_iprintf(buffer, "amdtp rx pcm channels 1x: 0x%X\n",
+ hwinfo->amdtp_rx_pcm_channels);
+ snd_iprintf(buffer, "amdtp tx pcm channels 1x: 0x%X\n",
+ hwinfo->amdtp_tx_pcm_channels);
+ snd_iprintf(buffer, "amdtp rx pcm channels 2x: 0x%X\n",
+ hwinfo->amdtp_rx_pcm_channels_2x);
+ snd_iprintf(buffer, "amdtp tx pcm channels 2x: 0x%X\n",
+ hwinfo->amdtp_tx_pcm_channels_2x);
+ snd_iprintf(buffer, "amdtp rx pcm channels 4x: 0x%X\n",
+ hwinfo->amdtp_rx_pcm_channels_4x);
+ snd_iprintf(buffer, "amdtp tx pcm channels 4x: 0x%X\n",
+ hwinfo->amdtp_tx_pcm_channels_4x);
+
+ snd_iprintf(buffer, "midi out ports: 0x%X\n", hwinfo->midi_out_ports);
+ snd_iprintf(buffer, "midi in ports: 0x%X\n", hwinfo->midi_in_ports);
+
+ snd_iprintf(buffer, "mixer playback channels: 0x%X\n",
+ hwinfo->mixer_playback_channels);
+ snd_iprintf(buffer, "mixer capture channels: 0x%X\n",
+ hwinfo->mixer_capture_channels);
+end:
+ kfree(hwinfo);
+}
+
+static void
+proc_read_clock(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+ struct snd_efw *efw = entry->private_data;
+ enum snd_efw_clock_source clock_source;
+ unsigned int sampling_rate;
+
+ if (snd_efw_command_get_clock_source(efw, &clock_source) < 0)
+ return;
+
+ if (snd_efw_command_get_sampling_rate(efw, &sampling_rate) < 0)
+ return;
+
+ snd_iprintf(buffer, "Clock Source: %d\n", clock_source);
+ snd_iprintf(buffer, "Sampling Rate: %d\n", sampling_rate);
+}
+
+/*
+ * NOTE:
+ * dB = 20 * log10(linear / 0x01000000)
+ * -144.0 dB when linear is 0
+ */
+static void
+proc_read_phys_meters(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_efw *efw = entry->private_data;
+ struct snd_efw_phys_meters *meters;
+ unsigned int g, c, m, max, size;
+ const char *name;
+ u32 *linear;
+ int err;
+
+ size = sizeof(struct snd_efw_phys_meters) +
+ (efw->phys_in + efw->phys_out) * sizeof(u32);
+ meters = kzalloc(size, GFP_KERNEL);
+ if (meters == NULL)
+ return;
+
+ err = snd_efw_command_get_phys_meters(efw, meters, size);
+ if (err < 0)
+ goto end;
+
+ snd_iprintf(buffer, "Physical Meters:\n");
+
+ m = 0;
+ max = min(efw->phys_out, meters->out_meters);
+ linear = meters->values;
+ snd_iprintf(buffer, " %d Outputs:\n", max);
+ for (g = 0; g < efw->phys_out_grp_count; g++) {
+ name = get_phys_name(&efw->phys_out_grps[g], false);
+ for (c = 0; c < efw->phys_out_grps[g].count; c++) {
+ if (m < max)
+ snd_iprintf(buffer, "\t%s [%d]: %d\n",
+ name, c, linear[m++]);
+ }
+ }
+
+ m = 0;
+ max = min(efw->phys_in, meters->in_meters);
+ linear = meters->values + meters->out_meters;
+ snd_iprintf(buffer, " %d Inputs:\n", max);
+ for (g = 0; g < efw->phys_in_grp_count; g++) {
+ name = get_phys_name(&efw->phys_in_grps[g], true);
+ for (c = 0; c < efw->phys_in_grps[g].count; c++)
+ if (m < max)
+ snd_iprintf(buffer, "\t%s [%d]: %d\n",
+ name, c, linear[m++]);
+ }
+end:
+ kfree(meters);
+}
+
+static void
+proc_read_queues_state(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_efw *efw = entry->private_data;
+ unsigned int consumed;
+
+ if (efw->pull_ptr > efw->push_ptr)
+ consumed = snd_efw_resp_buf_size -
+ (unsigned int)(efw->pull_ptr - efw->push_ptr);
+ else
+ consumed = (unsigned int)(efw->push_ptr - efw->pull_ptr);
+
+ snd_iprintf(buffer, "%d/%d\n",
+ consumed, snd_efw_resp_buf_size);
+}
+
+static void
+add_node(struct snd_efw *efw, struct snd_info_entry *root, const char *name,
+ void (*op)(struct snd_info_entry *e, struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(efw->card, name, root);
+ if (entry)
+ snd_info_set_text_ops(entry, efw, op);
+}
+
+void snd_efw_proc_init(struct snd_efw *efw)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(efw->card, "firewire",
+ efw->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | 0555;
+
+ add_node(efw, root, "clock", proc_read_clock);
+ add_node(efw, root, "firmware", proc_read_hwinfo);
+ add_node(efw, root, "meters", proc_read_phys_meters);
+ add_node(efw, root, "queues", proc_read_queues_state);
+}
diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c
new file mode 100644
index 000000000000..974084e1c083
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_stream.c
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * fireworks_stream.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+#include "./fireworks.h"
+
+#define READY_TIMEOUT_MS 1000
+
+static int init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
+{
+ struct cmp_connection *conn;
+ enum cmp_direction c_dir;
+ enum amdtp_stream_direction s_dir;
+ int err;
+
+ if (stream == &efw->tx_stream) {
+ conn = &efw->out_conn;
+ c_dir = CMP_OUTPUT;
+ s_dir = AMDTP_IN_STREAM;
+ } else {
+ conn = &efw->in_conn;
+ c_dir = CMP_INPUT;
+ s_dir = AMDTP_OUT_STREAM;
+ }
+
+ err = cmp_connection_init(conn, efw->unit, c_dir, 0);
+ if (err < 0)
+ return err;
+
+ err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING | CIP_UNAWARE_SYT);
+ if (err < 0) {
+ amdtp_stream_destroy(stream);
+ cmp_connection_destroy(conn);
+ return err;
+ }
+
+ if (stream == &efw->tx_stream) {
+ // Fireworks transmits NODATA packets with TAG0.
+ efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0;
+ // Fireworks has its own meaning for dbc.
+ efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT;
+ // Fireworks reset dbc at bus reset.
+ efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK;
+ // But Recent firmwares starts packets with non-zero dbc.
+ // Driver version 5.7.6 installs firmware version 5.7.3.
+ if (efw->is_fireworks3 &&
+ (efw->firmware_version == 0x5070000 ||
+ efw->firmware_version == 0x5070300 ||
+ efw->firmware_version == 0x5080000))
+ efw->tx_stream.flags |= CIP_UNALIGHED_DBC;
+ // AudioFire9 always reports wrong dbs. Onyx 1200F with the latest firmware (v4.6.0)
+ // also report wrong dbs at 88.2 kHz or greater.
+ if (efw->is_af9 || efw->firmware_version == 0x4060000)
+ efw->tx_stream.flags |= CIP_WRONG_DBS;
+ // Firmware version 5.5 reports fixed interval for dbc.
+ if (efw->firmware_version == 0x5050000)
+ efw->tx_stream.ctx_data.tx.dbc_interval = 8;
+ }
+
+ return err;
+}
+
+static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
+ unsigned int rate)
+{
+ struct cmp_connection *conn;
+ int err;
+
+ if (stream == &efw->tx_stream)
+ conn = &efw->out_conn;
+ else
+ conn = &efw->in_conn;
+
+ // Establish connection via CMP.
+ err = cmp_connection_establish(conn);
+ if (err < 0)
+ return err;
+
+ // Start amdtp stream.
+ err = amdtp_domain_add_stream(&efw->domain, stream,
+ conn->resources.channel, conn->speed);
+ if (err < 0) {
+ cmp_connection_break(conn);
+ return err;
+ }
+
+ return 0;
+}
+
+// This function should be called before starting the stream or after stopping
+// the streams.
+static void destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
+{
+ amdtp_stream_destroy(stream);
+
+ if (stream == &efw->tx_stream)
+ cmp_connection_destroy(&efw->out_conn);
+ else
+ cmp_connection_destroy(&efw->in_conn);
+}
+
+static int
+check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s)
+{
+ struct cmp_connection *conn;
+ bool used;
+ int err;
+
+ if (s == &efw->tx_stream)
+ conn = &efw->out_conn;
+ else
+ conn = &efw->in_conn;
+
+ err = cmp_connection_check_used(conn, &used);
+ if ((err >= 0) && used && !amdtp_stream_running(s)) {
+ dev_err(&efw->unit->device,
+ "Connection established by others: %cPCR[%d]\n",
+ (conn->direction == CMP_OUTPUT) ? 'o' : 'i',
+ conn->pcr_index);
+ err = -EBUSY;
+ }
+
+ return err;
+}
+
+int snd_efw_stream_init_duplex(struct snd_efw *efw)
+{
+ int err;
+
+ err = init_stream(efw, &efw->tx_stream);
+ if (err < 0)
+ return err;
+
+ err = init_stream(efw, &efw->rx_stream);
+ if (err < 0) {
+ destroy_stream(efw, &efw->tx_stream);
+ return err;
+ }
+
+ err = amdtp_domain_init(&efw->domain);
+ if (err < 0) {
+ destroy_stream(efw, &efw->tx_stream);
+ destroy_stream(efw, &efw->rx_stream);
+ return err;
+ }
+
+ // set IEC61883 compliant mode (actually not fully compliant...).
+ err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883);
+ if (err < 0) {
+ destroy_stream(efw, &efw->tx_stream);
+ destroy_stream(efw, &efw->rx_stream);
+ }
+
+ return err;
+}
+
+static int keep_resources(struct snd_efw *efw, struct amdtp_stream *stream,
+ unsigned int rate, unsigned int mode)
+{
+ unsigned int pcm_channels;
+ unsigned int midi_ports;
+ struct cmp_connection *conn;
+ int err;
+
+ if (stream == &efw->tx_stream) {
+ pcm_channels = efw->pcm_capture_channels[mode];
+ midi_ports = efw->midi_out_ports;
+ conn = &efw->out_conn;
+ } else {
+ pcm_channels = efw->pcm_playback_channels[mode];
+ midi_ports = efw->midi_in_ports;
+ conn = &efw->in_conn;
+ }
+
+ err = amdtp_am824_set_parameters(stream, rate, pcm_channels,
+ midi_ports, false);
+ if (err < 0)
+ return err;
+
+ return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
+}
+
+int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer)
+{
+ unsigned int curr_rate;
+ int err;
+
+ // Considering JACK/FFADO streaming:
+ // TODO: This can be removed hwdep functionality becomes popular.
+ err = check_connection_used_by_others(efw, &efw->rx_stream);
+ if (err < 0)
+ return err;
+
+ // stop streams if rate is different.
+ err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
+ if (err < 0)
+ return err;
+ if (rate == 0)
+ rate = curr_rate;
+ if (rate != curr_rate) {
+ amdtp_domain_stop(&efw->domain);
+
+ cmp_connection_break(&efw->out_conn);
+ cmp_connection_break(&efw->in_conn);
+
+ cmp_connection_release(&efw->out_conn);
+ cmp_connection_release(&efw->in_conn);
+ }
+
+ if (efw->substreams_counter == 0 || rate != curr_rate) {
+ unsigned int mode;
+
+ err = snd_efw_command_set_sampling_rate(efw, rate);
+ if (err < 0)
+ return err;
+
+ err = snd_efw_get_multiplier_mode(rate, &mode);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(efw, &efw->tx_stream, rate, mode);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(efw, &efw->rx_stream, rate, mode);
+ if (err < 0) {
+ cmp_connection_release(&efw->in_conn);
+ return err;
+ }
+
+ err = amdtp_domain_set_events_per_period(&efw->domain,
+ frames_per_period, frames_per_buffer);
+ if (err < 0) {
+ cmp_connection_release(&efw->in_conn);
+ cmp_connection_release(&efw->out_conn);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int snd_efw_stream_start_duplex(struct snd_efw *efw)
+{
+ unsigned int rate;
+ int err = 0;
+
+ // Need no substreams.
+ if (efw->substreams_counter == 0)
+ return -EIO;
+
+ if (amdtp_streaming_error(&efw->rx_stream) ||
+ amdtp_streaming_error(&efw->tx_stream)) {
+ amdtp_domain_stop(&efw->domain);
+ cmp_connection_break(&efw->out_conn);
+ cmp_connection_break(&efw->in_conn);
+ }
+
+ err = snd_efw_command_get_sampling_rate(efw, &rate);
+ if (err < 0)
+ return err;
+
+ if (!amdtp_stream_running(&efw->rx_stream)) {
+ unsigned int tx_init_skip_cycles;
+
+ // Audiofire 2/4 skip an isochronous cycle several thousands after starting
+ // packet transmission.
+ if (efw->is_fireworks3 && !efw->is_af9)
+ tx_init_skip_cycles = 6000;
+ else
+ tx_init_skip_cycles = 0;
+
+ err = start_stream(efw, &efw->rx_stream, rate);
+ if (err < 0)
+ goto error;
+
+ err = start_stream(efw, &efw->tx_stream, rate);
+ if (err < 0)
+ goto error;
+
+ // NOTE: The device ignores presentation time expressed by the value of syt field
+ // of CIP header in received packets. The sequence of the number of data blocks per
+ // packet is important for media clock recovery.
+ err = amdtp_domain_start(&efw->domain, tx_init_skip_cycles, true, false);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_domain_wait_ready(&efw->domain, READY_TIMEOUT_MS)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ amdtp_domain_stop(&efw->domain);
+
+ cmp_connection_break(&efw->out_conn);
+ cmp_connection_break(&efw->in_conn);
+
+ return err;
+}
+
+void snd_efw_stream_stop_duplex(struct snd_efw *efw)
+{
+ if (efw->substreams_counter == 0) {
+ amdtp_domain_stop(&efw->domain);
+
+ cmp_connection_break(&efw->out_conn);
+ cmp_connection_break(&efw->in_conn);
+
+ cmp_connection_release(&efw->out_conn);
+ cmp_connection_release(&efw->in_conn);
+ }
+}
+
+void snd_efw_stream_update_duplex(struct snd_efw *efw)
+{
+ amdtp_domain_stop(&efw->domain);
+
+ cmp_connection_break(&efw->out_conn);
+ cmp_connection_break(&efw->in_conn);
+
+ amdtp_stream_pcm_abort(&efw->rx_stream);
+ amdtp_stream_pcm_abort(&efw->tx_stream);
+}
+
+void snd_efw_stream_destroy_duplex(struct snd_efw *efw)
+{
+ amdtp_domain_destroy(&efw->domain);
+
+ destroy_stream(efw, &efw->rx_stream);
+ destroy_stream(efw, &efw->tx_stream);
+}
+
+void snd_efw_stream_lock_changed(struct snd_efw *efw)
+{
+ efw->dev_lock_changed = true;
+ wake_up(&efw->hwdep_wait);
+}
+
+int snd_efw_stream_lock_try(struct snd_efw *efw)
+{
+ guard(spinlock_irq)(&efw->lock);
+
+ /* user land lock this */
+ if (efw->dev_lock_count < 0)
+ return -EBUSY;
+
+ /* this is the first time */
+ if (efw->dev_lock_count++ == 0)
+ snd_efw_stream_lock_changed(efw);
+ return 0;
+}
+
+void snd_efw_stream_lock_release(struct snd_efw *efw)
+{
+ guard(spinlock_irq)(&efw->lock);
+
+ if (WARN_ON(efw->dev_lock_count <= 0))
+ return;
+ if (--efw->dev_lock_count == 0)
+ snd_efw_stream_lock_changed(efw);
+}
diff --git a/sound/firewire/fireworks/fireworks_transaction.c b/sound/firewire/fireworks/fireworks_transaction.c
new file mode 100644
index 000000000000..5c859773fe06
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_transaction.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * fireworks_transaction.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ */
+
+/*
+ * Fireworks have its own transaction. The transaction can be delivered by AV/C
+ * Vendor Specific command frame or usual asynchronous transaction. At least,
+ * Windows driver and firmware version 5.5 or later don't use AV/C command.
+ *
+ * Transaction substance:
+ * At first, 6 data exist. Following to the data, parameters for each command
+ * exist. All of the parameters are 32 bit aligned to big endian.
+ * data[0]: Length of transaction substance
+ * data[1]: Transaction version
+ * data[2]: Sequence number. This is incremented by the device
+ * data[3]: Transaction category
+ * data[4]: Transaction command
+ * data[5]: Return value in response.
+ * data[6-]: Parameters
+ *
+ * Transaction address:
+ * command: 0xecc000000000
+ * response: 0xecc080000000 (default)
+ *
+ * I note that the address for response can be changed by command. But this
+ * module uses the default address.
+ */
+#include "./fireworks.h"
+
+#define MEMORY_SPACE_EFW_COMMAND 0xecc000000000ULL
+#define MEMORY_SPACE_EFW_RESPONSE 0xecc080000000ULL
+
+#define ERROR_RETRIES 3
+#define ERROR_DELAY_MS 5
+#define EFC_TIMEOUT_MS 125
+
+static DEFINE_SPINLOCK(instances_lock);
+static struct snd_efw *instances[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+static DEFINE_SPINLOCK(transaction_queues_lock);
+static LIST_HEAD(transaction_queues);
+
+enum transaction_queue_state {
+ STATE_PENDING,
+ STATE_BUS_RESET,
+ STATE_COMPLETE
+};
+
+struct transaction_queue {
+ struct list_head list;
+ struct fw_unit *unit;
+ void *buf;
+ unsigned int size;
+ u32 seqnum;
+ enum transaction_queue_state state;
+ wait_queue_head_t wait;
+};
+
+int snd_efw_transaction_cmd(struct fw_unit *unit,
+ const void *cmd, unsigned int size)
+{
+ return snd_fw_transaction(unit, TCODE_WRITE_BLOCK_REQUEST,
+ MEMORY_SPACE_EFW_COMMAND,
+ (void *)cmd, size, 0);
+}
+
+int snd_efw_transaction_run(struct fw_unit *unit,
+ const void *cmd, unsigned int cmd_size,
+ void *resp, unsigned int resp_size)
+{
+ struct transaction_queue t;
+ unsigned int tries;
+ int ret;
+
+ t.unit = unit;
+ t.buf = resp;
+ t.size = resp_size;
+ t.seqnum = be32_to_cpu(((struct snd_efw_transaction *)cmd)->seqnum) + 1;
+ t.state = STATE_PENDING;
+ init_waitqueue_head(&t.wait);
+
+ scoped_guard(spinlock_irq, &transaction_queues_lock) {
+ list_add_tail(&t.list, &transaction_queues);
+ }
+
+ tries = 0;
+ do {
+ ret = snd_efw_transaction_cmd(t.unit, (void *)cmd, cmd_size);
+ if (ret < 0)
+ break;
+
+ wait_event_timeout(t.wait, t.state != STATE_PENDING,
+ msecs_to_jiffies(EFC_TIMEOUT_MS));
+
+ if (t.state == STATE_COMPLETE) {
+ ret = t.size;
+ break;
+ } else if (t.state == STATE_BUS_RESET) {
+ msleep(ERROR_DELAY_MS);
+ } else if (++tries >= ERROR_RETRIES) {
+ dev_err(&t.unit->device, "EFW transaction timed out\n");
+ ret = -EIO;
+ break;
+ }
+ } while (1);
+
+ scoped_guard(spinlock_irq, &transaction_queues_lock) {
+ list_del(&t.list);
+ }
+
+ return ret;
+}
+
+static void
+copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode)
+{
+ size_t capacity, till_end;
+ struct snd_efw_transaction *t;
+
+ t = (struct snd_efw_transaction *)data;
+ length = min_t(size_t, be32_to_cpu(t->length) * sizeof(u32), length);
+
+ guard(spinlock)(&efw->lock);
+
+ if (efw->push_ptr < efw->pull_ptr)
+ capacity = (unsigned int)(efw->pull_ptr - efw->push_ptr);
+ else
+ capacity = snd_efw_resp_buf_size -
+ (unsigned int)(efw->push_ptr - efw->pull_ptr);
+
+ /* confirm enough space for this response */
+ if (capacity < length) {
+ *rcode = RCODE_CONFLICT_ERROR;
+ return;
+ }
+
+ /* copy to ring buffer */
+ while (length > 0) {
+ till_end = snd_efw_resp_buf_size -
+ (unsigned int)(efw->push_ptr - efw->resp_buf);
+ till_end = min_t(unsigned int, length, till_end);
+
+ memcpy(efw->push_ptr, data, till_end);
+
+ efw->push_ptr += till_end;
+ if (efw->push_ptr >= efw->resp_buf + snd_efw_resp_buf_size)
+ efw->push_ptr -= snd_efw_resp_buf_size;
+
+ length -= till_end;
+ data += till_end;
+ }
+
+ /* for hwdep */
+ wake_up(&efw->hwdep_wait);
+
+ *rcode = RCODE_COMPLETE;
+}
+
+static void
+handle_resp_for_user(struct fw_card *card, int generation, int source,
+ void *data, size_t length, int *rcode)
+{
+ struct fw_device *device;
+ struct snd_efw *efw;
+ unsigned int i;
+
+ guard(spinlock_irq)(&instances_lock);
+
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ efw = instances[i];
+ if (efw == NULL)
+ continue;
+ device = fw_parent_device(efw->unit);
+ if ((device->card != card) ||
+ (device->generation != generation))
+ continue;
+ smp_rmb(); /* node id vs. generation */
+ if (device->node_id != source)
+ continue;
+
+ break;
+ }
+ if (i == SNDRV_CARDS)
+ return;
+
+ copy_resp_to_buf(efw, data, length, rcode);
+}
+
+static void
+handle_resp_for_kernel(struct fw_card *card, int generation, int source,
+ void *data, size_t length, int *rcode, u32 seqnum)
+{
+ struct fw_device *device;
+ struct transaction_queue *t;
+
+ guard(spinlock_irqsave)(&transaction_queues_lock);
+ list_for_each_entry(t, &transaction_queues, list) {
+ device = fw_parent_device(t->unit);
+ if ((device->card != card) ||
+ (device->generation != generation))
+ continue;
+ smp_rmb(); /* node_id vs. generation */
+ if (device->node_id != source)
+ continue;
+
+ if ((t->state == STATE_PENDING) && (t->seqnum == seqnum)) {
+ t->state = STATE_COMPLETE;
+ t->size = min_t(unsigned int, length, t->size);
+ memcpy(t->buf, data, t->size);
+ wake_up(&t->wait);
+ *rcode = RCODE_COMPLETE;
+ }
+ }
+}
+
+static void
+efw_response(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ int rcode, dummy;
+ u32 seqnum;
+
+ rcode = RCODE_TYPE_ERROR;
+ if (length < sizeof(struct snd_efw_transaction)) {
+ rcode = RCODE_DATA_ERROR;
+ goto end;
+ } else if (offset != MEMORY_SPACE_EFW_RESPONSE) {
+ rcode = RCODE_ADDRESS_ERROR;
+ goto end;
+ }
+
+ seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum);
+ if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 1) {
+ handle_resp_for_kernel(card, generation, source,
+ data, length, &rcode, seqnum);
+ if (snd_efw_resp_buf_debug)
+ handle_resp_for_user(card, generation, source,
+ data, length, &dummy);
+ } else {
+ handle_resp_for_user(card, generation, source,
+ data, length, &rcode);
+ }
+end:
+ fw_send_response(card, request, rcode);
+}
+
+void snd_efw_transaction_add_instance(struct snd_efw *efw)
+{
+ unsigned int i;
+
+ guard(spinlock_irq)(&instances_lock);
+
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ if (instances[i] != NULL)
+ continue;
+ instances[i] = efw;
+ break;
+ }
+}
+
+void snd_efw_transaction_remove_instance(struct snd_efw *efw)
+{
+ unsigned int i;
+
+ guard(spinlock_irq)(&instances_lock);
+
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ if (instances[i] != efw)
+ continue;
+ instances[i] = NULL;
+ }
+}
+
+void snd_efw_transaction_bus_reset(struct fw_unit *unit)
+{
+ struct transaction_queue *t;
+
+ guard(spinlock_irq)(&transaction_queues_lock);
+ list_for_each_entry(t, &transaction_queues, list) {
+ if ((t->unit == unit) &&
+ (t->state == STATE_PENDING)) {
+ t->state = STATE_BUS_RESET;
+ wake_up(&t->wait);
+ }
+ }
+}
+
+static struct fw_address_handler resp_register_handler = {
+ .length = SND_EFW_RESPONSE_MAXIMUM_BYTES,
+ .address_callback = efw_response
+};
+
+int snd_efw_transaction_register(void)
+{
+ static const struct fw_address_region resp_register_region = {
+ .start = MEMORY_SPACE_EFW_RESPONSE,
+ .end = MEMORY_SPACE_EFW_RESPONSE +
+ SND_EFW_RESPONSE_MAXIMUM_BYTES
+ };
+ return fw_core_add_address_handler(&resp_register_handler,
+ &resp_register_region);
+}
+
+void snd_efw_transaction_unregister(void)
+{
+ WARN_ON(!list_empty(&transaction_queues));
+ fw_core_remove_address_handler(&resp_register_handler);
+}
diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c
new file mode 100644
index 000000000000..2b7f071d593b
--- /dev/null
+++ b/sound/firewire/isight.c
@@ -0,0 +1,733 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Apple iSight audio driver
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include <asm/byteorder.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include "lib.h"
+#include "iso-resources.h"
+#include "packets-buffer.h"
+
+#define OUI_APPLE 0x000a27
+#define MODEL_APPLE_ISIGHT 0x000008
+#define SW_ISIGHT_AUDIO 0x000010
+
+#define REG_AUDIO_ENABLE 0x000
+#define AUDIO_ENABLE 0x80000000
+#define REG_DEF_AUDIO_GAIN 0x204
+#define REG_GAIN_RAW_START 0x210
+#define REG_GAIN_RAW_END 0x214
+#define REG_GAIN_DB_START 0x218
+#define REG_GAIN_DB_END 0x21c
+#define REG_SAMPLE_RATE_INQUIRY 0x280
+#define REG_ISO_TX_CONFIG 0x300
+#define SPEED_SHIFT 16
+#define REG_SAMPLE_RATE 0x400
+#define RATE_48000 0x80000000
+#define REG_GAIN 0x500
+#define REG_MUTE 0x504
+
+#define MAX_FRAMES_PER_PACKET 475
+
+#define QUEUE_LENGTH 20
+
+struct isight {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ struct fw_device *device;
+ u64 audio_base;
+ struct snd_pcm_substream *pcm;
+ struct mutex mutex;
+ struct iso_packets_buffer buffer;
+ struct fw_iso_resources resources;
+ struct fw_iso_context *context;
+ bool pcm_active;
+ bool pcm_running;
+ bool first_packet;
+ int packet_index;
+ u32 total_samples;
+ unsigned int buffer_pointer;
+ unsigned int period_counter;
+ s32 gain_min, gain_max;
+ unsigned int gain_tlv[4];
+};
+
+struct audio_payload {
+ __be32 sample_count;
+ __be32 signature;
+ __be32 sample_total;
+ __be32 reserved;
+ __be16 samples[2 * MAX_FRAMES_PER_PACKET];
+};
+
+MODULE_DESCRIPTION("iSight audio driver");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL");
+
+static struct fw_iso_packet audio_packet = {
+ .payload_length = sizeof(struct audio_payload),
+ .interrupt = 1,
+ .header_length = 4,
+};
+
+static void isight_update_pointers(struct isight *isight, unsigned int count)
+{
+ struct snd_pcm_runtime *runtime = isight->pcm->runtime;
+ unsigned int ptr;
+
+ smp_wmb(); /* update buffer data before buffer pointer */
+
+ ptr = isight->buffer_pointer;
+ ptr += count;
+ if (ptr >= runtime->buffer_size)
+ ptr -= runtime->buffer_size;
+ WRITE_ONCE(isight->buffer_pointer, ptr);
+
+ isight->period_counter += count;
+ if (isight->period_counter >= runtime->period_size) {
+ isight->period_counter -= runtime->period_size;
+ snd_pcm_period_elapsed(isight->pcm);
+ }
+}
+
+static void isight_samples(struct isight *isight,
+ const __be16 *samples, unsigned int count)
+{
+ struct snd_pcm_runtime *runtime;
+ unsigned int count1;
+
+ if (!READ_ONCE(isight->pcm_running))
+ return;
+
+ runtime = isight->pcm->runtime;
+ if (isight->buffer_pointer + count <= runtime->buffer_size) {
+ memcpy(runtime->dma_area + isight->buffer_pointer * 4,
+ samples, count * 4);
+ } else {
+ count1 = runtime->buffer_size - isight->buffer_pointer;
+ memcpy(runtime->dma_area + isight->buffer_pointer * 4,
+ samples, count1 * 4);
+ samples += count1 * 2;
+ memcpy(runtime->dma_area, samples, (count - count1) * 4);
+ }
+
+ isight_update_pointers(isight, count);
+}
+
+static void isight_pcm_abort(struct isight *isight)
+{
+ if (READ_ONCE(isight->pcm_active))
+ snd_pcm_stop_xrun(isight->pcm);
+}
+
+static void isight_dropped_samples(struct isight *isight, unsigned int total)
+{
+ struct snd_pcm_runtime *runtime;
+ u32 dropped;
+ unsigned int count1;
+
+ if (!READ_ONCE(isight->pcm_running))
+ return;
+
+ runtime = isight->pcm->runtime;
+ dropped = total - isight->total_samples;
+ if (dropped < runtime->buffer_size) {
+ if (isight->buffer_pointer + dropped <= runtime->buffer_size) {
+ memset(runtime->dma_area + isight->buffer_pointer * 4,
+ 0, dropped * 4);
+ } else {
+ count1 = runtime->buffer_size - isight->buffer_pointer;
+ memset(runtime->dma_area + isight->buffer_pointer * 4,
+ 0, count1 * 4);
+ memset(runtime->dma_area, 0, (dropped - count1) * 4);
+ }
+ isight_update_pointers(isight, dropped);
+ } else {
+ isight_pcm_abort(isight);
+ }
+}
+
+static void isight_packet(struct fw_iso_context *context, u32 cycle,
+ size_t header_length, void *header, void *data)
+{
+ struct isight *isight = data;
+ const struct audio_payload *payload;
+ unsigned int index, length, count, total;
+ int err;
+
+ if (isight->packet_index < 0)
+ return;
+ index = isight->packet_index;
+ payload = isight->buffer.packets[index].buffer;
+ length = be32_to_cpup(header) >> 16;
+
+ if (likely(length >= 16 &&
+ payload->signature == cpu_to_be32(0x73676874/*"sght"*/))) {
+ count = be32_to_cpu(payload->sample_count);
+ if (likely(count <= (length - 16) / 4)) {
+ total = be32_to_cpu(payload->sample_total);
+ if (unlikely(total != isight->total_samples)) {
+ if (!isight->first_packet)
+ isight_dropped_samples(isight, total);
+ isight->first_packet = false;
+ isight->total_samples = total;
+ }
+
+ isight_samples(isight, payload->samples, count);
+ isight->total_samples += count;
+ }
+ }
+
+ err = fw_iso_context_queue(isight->context, &audio_packet,
+ &isight->buffer.iso_buffer,
+ isight->buffer.packets[index].offset);
+ if (err < 0) {
+ dev_err(&isight->unit->device, "queueing error: %d\n", err);
+ isight_pcm_abort(isight);
+ isight->packet_index = -1;
+ return;
+ }
+ fw_iso_context_queue_flush(isight->context);
+
+ if (++index >= QUEUE_LENGTH)
+ index = 0;
+ isight->packet_index = index;
+}
+
+static int isight_connect(struct isight *isight)
+{
+ int ch, err;
+ __be32 value;
+
+retry_after_bus_reset:
+ ch = fw_iso_resources_allocate(&isight->resources,
+ sizeof(struct audio_payload),
+ isight->device->max_speed);
+ if (ch < 0) {
+ err = ch;
+ goto error;
+ }
+
+ value = cpu_to_be32(ch | (isight->device->max_speed << SPEED_SHIFT));
+ err = snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
+ isight->audio_base + REG_ISO_TX_CONFIG,
+ &value, 4, FW_FIXED_GENERATION |
+ isight->resources.generation);
+ if (err == -EAGAIN) {
+ fw_iso_resources_free(&isight->resources);
+ goto retry_after_bus_reset;
+ } else if (err < 0) {
+ goto err_resources;
+ }
+
+ return 0;
+
+err_resources:
+ fw_iso_resources_free(&isight->resources);
+error:
+ return err;
+}
+
+static int isight_open(struct snd_pcm_substream *substream)
+{
+ static const struct snd_pcm_hardware hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
+ .formats = SNDRV_PCM_FMTBIT_S16_BE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 4 * 1024 * 1024,
+ .period_bytes_min = MAX_FRAMES_PER_PACKET * 4,
+ .period_bytes_max = 1024 * 1024,
+ .periods_min = 2,
+ .periods_max = UINT_MAX,
+ };
+ struct isight *isight = substream->private_data;
+
+ substream->runtime->hw = hardware;
+
+ return iso_packets_buffer_init(&isight->buffer, isight->unit,
+ QUEUE_LENGTH,
+ sizeof(struct audio_payload),
+ DMA_FROM_DEVICE);
+}
+
+static int isight_close(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+
+ iso_packets_buffer_destroy(&isight->buffer, isight->unit);
+
+ return 0;
+}
+
+static int isight_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct isight *isight = substream->private_data;
+
+ WRITE_ONCE(isight->pcm_active, true);
+
+ return 0;
+}
+
+static int reg_read(struct isight *isight, int offset, __be32 *value)
+{
+ return snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST,
+ isight->audio_base + offset, value, 4, 0);
+}
+
+static int reg_write(struct isight *isight, int offset, __be32 value)
+{
+ return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
+ isight->audio_base + offset, &value, 4, 0);
+}
+
+static void isight_stop_streaming(struct isight *isight)
+{
+ __be32 value;
+
+ if (!isight->context)
+ return;
+
+ fw_iso_context_stop(isight->context);
+ fw_iso_context_destroy(isight->context);
+ isight->context = NULL;
+ fw_iso_resources_free(&isight->resources);
+ value = 0;
+ snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
+ isight->audio_base + REG_AUDIO_ENABLE,
+ &value, 4, FW_QUIET);
+}
+
+static int isight_hw_free(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+
+ WRITE_ONCE(isight->pcm_active, false);
+
+ guard(mutex)(&isight->mutex);
+ isight_stop_streaming(isight);
+
+ return 0;
+}
+
+static int isight_start_streaming(struct isight *isight)
+{
+ unsigned int i;
+ int err;
+
+ if (isight->context) {
+ if (isight->packet_index < 0)
+ isight_stop_streaming(isight);
+ else
+ return 0;
+ }
+
+ err = reg_write(isight, REG_SAMPLE_RATE, cpu_to_be32(RATE_48000));
+ if (err < 0)
+ goto error;
+
+ err = isight_connect(isight);
+ if (err < 0)
+ goto error;
+
+ err = reg_write(isight, REG_AUDIO_ENABLE, cpu_to_be32(AUDIO_ENABLE));
+ if (err < 0)
+ goto err_resources;
+
+ isight->context = fw_iso_context_create(isight->device->card,
+ FW_ISO_CONTEXT_RECEIVE,
+ isight->resources.channel,
+ isight->device->max_speed,
+ 4, isight_packet, isight);
+ if (IS_ERR(isight->context)) {
+ err = PTR_ERR(isight->context);
+ isight->context = NULL;
+ goto err_resources;
+ }
+
+ for (i = 0; i < QUEUE_LENGTH; ++i) {
+ err = fw_iso_context_queue(isight->context, &audio_packet,
+ &isight->buffer.iso_buffer,
+ isight->buffer.packets[i].offset);
+ if (err < 0)
+ goto err_context;
+ }
+
+ isight->first_packet = true;
+ isight->packet_index = 0;
+
+ err = fw_iso_context_start(isight->context, -1, 0,
+ FW_ISO_CONTEXT_MATCH_ALL_TAGS/*?*/);
+ if (err < 0)
+ goto err_context;
+
+ return 0;
+
+err_context:
+ fw_iso_context_destroy(isight->context);
+ isight->context = NULL;
+err_resources:
+ fw_iso_resources_free(&isight->resources);
+ reg_write(isight, REG_AUDIO_ENABLE, 0);
+error:
+ return err;
+}
+
+static int isight_prepare(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+
+ isight->buffer_pointer = 0;
+ isight->period_counter = 0;
+
+ guard(mutex)(&isight->mutex);
+ return isight_start_streaming(isight);
+}
+
+static int isight_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct isight *isight = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ WRITE_ONCE(isight->pcm_running, true);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ WRITE_ONCE(isight->pcm_running, false);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static snd_pcm_uframes_t isight_pointer(struct snd_pcm_substream *substream)
+{
+ struct isight *isight = substream->private_data;
+
+ return READ_ONCE(isight->buffer_pointer);
+}
+
+static int isight_create_pcm(struct isight *isight)
+{
+ static const struct snd_pcm_ops ops = {
+ .open = isight_open,
+ .close = isight_close,
+ .hw_params = isight_hw_params,
+ .hw_free = isight_hw_free,
+ .prepare = isight_prepare,
+ .trigger = isight_trigger,
+ .pointer = isight_pointer,
+ };
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(isight->card, "iSight", 0, 0, 1, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = isight;
+ pcm->nonatomic = true;
+ strscpy(pcm->name, "iSight");
+ isight->pcm = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ isight->pcm->ops = &ops;
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
+
+ return 0;
+}
+
+static int isight_gain_info(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_info *info)
+{
+ struct isight *isight = ctl->private_data;
+
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = 1;
+ info->value.integer.min = isight->gain_min;
+ info->value.integer.max = isight->gain_max;
+
+ return 0;
+}
+
+static int isight_gain_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+ __be32 gain;
+ int err;
+
+ err = reg_read(isight, REG_GAIN, &gain);
+ if (err < 0)
+ return err;
+
+ value->value.integer.value[0] = (s32)be32_to_cpu(gain);
+
+ return 0;
+}
+
+static int isight_gain_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+
+ if (value->value.integer.value[0] < isight->gain_min ||
+ value->value.integer.value[0] > isight->gain_max)
+ return -EINVAL;
+
+ return reg_write(isight, REG_GAIN,
+ cpu_to_be32(value->value.integer.value[0]));
+}
+
+static int isight_mute_get(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+ __be32 mute;
+ int err;
+
+ err = reg_read(isight, REG_MUTE, &mute);
+ if (err < 0)
+ return err;
+
+ value->value.integer.value[0] = !mute;
+
+ return 0;
+}
+
+static int isight_mute_put(struct snd_kcontrol *ctl,
+ struct snd_ctl_elem_value *value)
+{
+ struct isight *isight = ctl->private_data;
+
+ return reg_write(isight, REG_MUTE,
+ (__force __be32)!value->value.integer.value[0]);
+}
+
+static int isight_create_mixer(struct isight *isight)
+{
+ static const struct snd_kcontrol_new gain_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Capture Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = isight_gain_info,
+ .get = isight_gain_get,
+ .put = isight_gain_put,
+ };
+ static const struct snd_kcontrol_new mute_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Capture Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = isight_mute_get,
+ .put = isight_mute_put,
+ };
+ __be32 value;
+ struct snd_kcontrol *ctl;
+ int err;
+
+ err = reg_read(isight, REG_GAIN_RAW_START, &value);
+ if (err < 0)
+ return err;
+ isight->gain_min = be32_to_cpu(value);
+
+ err = reg_read(isight, REG_GAIN_RAW_END, &value);
+ if (err < 0)
+ return err;
+ isight->gain_max = be32_to_cpu(value);
+
+ isight->gain_tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_MINMAX;
+ isight->gain_tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int);
+
+ err = reg_read(isight, REG_GAIN_DB_START, &value);
+ if (err < 0)
+ return err;
+ isight->gain_tlv[SNDRV_CTL_TLVO_DB_MINMAX_MIN] =
+ (s32)be32_to_cpu(value) * 100;
+
+ err = reg_read(isight, REG_GAIN_DB_END, &value);
+ if (err < 0)
+ return err;
+ isight->gain_tlv[SNDRV_CTL_TLVO_DB_MINMAX_MAX] =
+ (s32)be32_to_cpu(value) * 100;
+
+ ctl = snd_ctl_new1(&gain_control, isight);
+ if (ctl)
+ ctl->tlv.p = isight->gain_tlv;
+ err = snd_ctl_add(isight->card, ctl);
+ if (err < 0)
+ return err;
+
+ err = snd_ctl_add(isight->card, snd_ctl_new1(&mute_control, isight));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static void isight_card_free(struct snd_card *card)
+{
+ struct isight *isight = card->private_data;
+
+ fw_iso_resources_destroy(&isight->resources);
+}
+
+static u64 get_unit_base(struct fw_unit *unit)
+{
+ struct fw_csr_iterator i;
+ int key, value;
+
+ fw_csr_iterator_init(&i, unit->directory);
+ while (fw_csr_iterator_next(&i, &key, &value))
+ if (key == CSR_OFFSET)
+ return CSR_REGISTER_BASE + value * 4;
+ return 0;
+}
+
+static int isight_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *id)
+{
+ struct fw_device *fw_dev = fw_parent_device(unit);
+ struct snd_card *card;
+ struct isight *isight;
+ int err;
+
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
+ sizeof(*isight), &card);
+ if (err < 0)
+ return err;
+
+ isight = card->private_data;
+ isight->card = card;
+ mutex_init(&isight->mutex);
+ isight->unit = fw_unit_get(unit);
+ isight->device = fw_dev;
+ isight->audio_base = get_unit_base(unit);
+ if (!isight->audio_base) {
+ dev_err(&unit->device, "audio unit base not found\n");
+ err = -ENXIO;
+ goto error;
+ }
+ fw_iso_resources_init(&isight->resources, unit);
+
+ card->private_free = isight_card_free;
+
+ strscpy(card->driver, "iSight");
+ strscpy(card->shortname, "Apple iSight");
+ snprintf(card->longname, sizeof(card->longname),
+ "Apple iSight (GUID %08x%08x) at %s, S%d",
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&unit->device), 100 << fw_dev->max_speed);
+ strscpy(card->mixername, "iSight");
+
+ err = isight_create_pcm(isight);
+ if (err < 0)
+ goto error;
+
+ err = isight_create_mixer(isight);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ dev_set_drvdata(&unit->device, isight);
+
+ return 0;
+error:
+ snd_card_free(card);
+
+ mutex_destroy(&isight->mutex);
+ fw_unit_put(isight->unit);
+
+ return err;
+}
+
+static void isight_bus_reset(struct fw_unit *unit)
+{
+ struct isight *isight = dev_get_drvdata(&unit->device);
+
+ if (fw_iso_resources_update(&isight->resources) < 0) {
+ isight_pcm_abort(isight);
+
+ guard(mutex)(&isight->mutex);
+ isight_stop_streaming(isight);
+ }
+}
+
+static void isight_remove(struct fw_unit *unit)
+{
+ struct isight *isight = dev_get_drvdata(&unit->device);
+
+ isight_pcm_abort(isight);
+
+ snd_card_disconnect(isight->card);
+
+ scoped_guard(mutex, &isight->mutex) {
+ isight_stop_streaming(isight);
+ }
+
+ // Block till all of ALSA character devices are released.
+ snd_card_free(isight->card);
+
+ mutex_destroy(&isight->mutex);
+ fw_unit_put(isight->unit);
+}
+
+static const struct ieee1394_device_id isight_id_table[] = {
+ {
+ .match_flags = IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
+ .specifier_id = OUI_APPLE,
+ .version = SW_ISIGHT_AUDIO,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(ieee1394, isight_id_table);
+
+static struct fw_driver isight_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = isight_probe,
+ .update = isight_bus_reset,
+ .remove = isight_remove,
+ .id_table = isight_id_table,
+};
+
+static int __init alsa_isight_init(void)
+{
+ return driver_register(&isight_driver.driver);
+}
+
+static void __exit alsa_isight_exit(void)
+{
+ driver_unregister(&isight_driver.driver);
+}
+
+module_init(alsa_isight_init);
+module_exit(alsa_isight_exit);
diff --git a/sound/firewire/iso-resources.c b/sound/firewire/iso-resources.c
new file mode 100644
index 000000000000..4f63279225c5
--- /dev/null
+++ b/sound/firewire/iso-resources.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * isochronous resources helper functions
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/export.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include "iso-resources.h"
+
+/**
+ * fw_iso_resources_init - initializes a &struct fw_iso_resources
+ * @r: the resource manager to initialize
+ * @unit: the device unit for which the resources will be needed
+ *
+ * If the device does not support all channel numbers, change @r->channels_mask
+ * after calling this function.
+ */
+int fw_iso_resources_init(struct fw_iso_resources *r, struct fw_unit *unit)
+{
+ r->channels_mask = ~0uLL;
+ r->unit = unit;
+ mutex_init(&r->mutex);
+ r->allocated = false;
+
+ return 0;
+}
+EXPORT_SYMBOL(fw_iso_resources_init);
+
+/**
+ * fw_iso_resources_destroy - destroy a resource manager
+ * @r: the resource manager that is no longer needed
+ */
+void fw_iso_resources_destroy(struct fw_iso_resources *r)
+{
+ WARN_ON(r->allocated);
+ mutex_destroy(&r->mutex);
+}
+EXPORT_SYMBOL(fw_iso_resources_destroy);
+
+static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed)
+{
+ unsigned int bytes, s400_bytes;
+
+ /* iso packets have three header quadlets and quadlet-aligned payload */
+ bytes = 3 * 4 + ALIGN(max_payload_bytes, 4);
+
+ /* convert to bandwidth units (quadlets at S1600 = bytes at S400) */
+ if (speed <= SCODE_400)
+ s400_bytes = bytes * (1 << (SCODE_400 - speed));
+ else
+ s400_bytes = DIV_ROUND_UP(bytes, 1 << (speed - SCODE_400));
+
+ return s400_bytes;
+}
+
+static int current_bandwidth_overhead(struct fw_card *card)
+{
+ /*
+ * Under the usual pessimistic assumption (cable length 4.5 m), the
+ * isochronous overhead for N cables is 1.797 µs + N * 0.494 µs, or
+ * 88.3 + N * 24.3 in bandwidth units.
+ *
+ * The calculation below tries to deduce N from the current gap count.
+ * If the gap count has been optimized by measuring the actual packet
+ * transmission time, this derived overhead should be near the actual
+ * overhead as well.
+ */
+ return card->gap_count < 63 ? card->gap_count * 97 / 10 + 89 : 512;
+}
+
+static int wait_isoch_resource_delay_after_bus_reset(struct fw_card *card)
+{
+ for (;;) {
+ s64 delay = (card->reset_jiffies + HZ) - get_jiffies_64();
+ if (delay <= 0)
+ return 0;
+ if (schedule_timeout_interruptible(delay) > 0)
+ return -ERESTARTSYS;
+ }
+}
+
+/**
+ * fw_iso_resources_allocate - allocate isochronous channel and bandwidth
+ * @r: the resource manager
+ * @max_payload_bytes: the amount of data (including CIP headers) per packet
+ * @speed: the speed (e.g., SCODE_400) at which the packets will be sent
+ *
+ * This function allocates one isochronous channel and enough bandwidth for the
+ * specified packet size.
+ *
+ * Returns the channel number that the caller must use for streaming, or
+ * a negative error code. Due to potentionally long delays, this function is
+ * interruptible and can return -ERESTARTSYS. On success, the caller is
+ * responsible for calling fw_iso_resources_update() on bus resets, and
+ * fw_iso_resources_free() when the resources are not longer needed.
+ */
+int fw_iso_resources_allocate(struct fw_iso_resources *r,
+ unsigned int max_payload_bytes, int speed)
+{
+ struct fw_card *card = fw_parent_device(r->unit)->card;
+ int bandwidth, channel, err;
+
+ if (WARN_ON(r->allocated))
+ return -EBADFD;
+
+ r->bandwidth = packet_bandwidth(max_payload_bytes, speed);
+
+retry_after_bus_reset:
+ scoped_guard(spinlock_irq, &card->lock) {
+ r->generation = card->generation;
+ r->bandwidth_overhead = current_bandwidth_overhead(card);
+ }
+
+ err = wait_isoch_resource_delay_after_bus_reset(card);
+ if (err < 0)
+ return err;
+
+ scoped_guard(mutex, &r->mutex) {
+ bandwidth = r->bandwidth + r->bandwidth_overhead;
+ fw_iso_resource_manage(card, r->generation, r->channels_mask,
+ &channel, &bandwidth, true);
+ if (channel == -EAGAIN)
+ goto retry_after_bus_reset;
+ if (channel >= 0) {
+ r->channel = channel;
+ r->allocated = true;
+ } else {
+ if (channel == -EBUSY)
+ dev_err(&r->unit->device,
+ "isochronous resources exhausted\n");
+ else
+ dev_err(&r->unit->device,
+ "isochronous resource allocation failed\n");
+ }
+ }
+
+ return channel;
+}
+EXPORT_SYMBOL(fw_iso_resources_allocate);
+
+/**
+ * fw_iso_resources_update - update resource allocations after a bus reset
+ * @r: the resource manager
+ *
+ * This function must be called from the driver's .update handler to reallocate
+ * any resources that were allocated before the bus reset. It is safe to call
+ * this function if no resources are currently allocated.
+ *
+ * Returns a negative error code on failure. If this happens, the caller must
+ * stop streaming.
+ */
+int fw_iso_resources_update(struct fw_iso_resources *r)
+{
+ struct fw_card *card = fw_parent_device(r->unit)->card;
+ int bandwidth, channel;
+
+ guard(mutex)(&r->mutex);
+
+ if (!r->allocated)
+ return 0;
+
+ scoped_guard(spinlock_irq, &card->lock) {
+ r->generation = card->generation;
+ r->bandwidth_overhead = current_bandwidth_overhead(card);
+ }
+
+ bandwidth = r->bandwidth + r->bandwidth_overhead;
+
+ fw_iso_resource_manage(card, r->generation, 1uLL << r->channel,
+ &channel, &bandwidth, true);
+ /*
+ * When another bus reset happens, pretend that the allocation
+ * succeeded; we will try again for the new generation later.
+ */
+ if (channel < 0 && channel != -EAGAIN) {
+ r->allocated = false;
+ if (channel == -EBUSY)
+ dev_err(&r->unit->device,
+ "isochronous resources exhausted\n");
+ else
+ dev_err(&r->unit->device,
+ "isochronous resource allocation failed\n");
+ }
+
+ return channel;
+}
+EXPORT_SYMBOL(fw_iso_resources_update);
+
+/**
+ * fw_iso_resources_free - frees allocated resources
+ * @r: the resource manager
+ *
+ * This function deallocates the channel and bandwidth, if allocated.
+ */
+void fw_iso_resources_free(struct fw_iso_resources *r)
+{
+ struct fw_card *card;
+ int bandwidth, channel;
+
+ /* Not initialized. */
+ if (r->unit == NULL)
+ return;
+ card = fw_parent_device(r->unit)->card;
+
+ guard(mutex)(&r->mutex);
+
+ if (r->allocated) {
+ bandwidth = r->bandwidth + r->bandwidth_overhead;
+ fw_iso_resource_manage(card, r->generation, 1uLL << r->channel,
+ &channel, &bandwidth, false);
+ if (channel < 0)
+ dev_err(&r->unit->device,
+ "isochronous resource deallocation failed\n");
+
+ r->allocated = false;
+ }
+}
+EXPORT_SYMBOL(fw_iso_resources_free);
diff --git a/sound/firewire/iso-resources.h b/sound/firewire/iso-resources.h
new file mode 100644
index 000000000000..34f85e9e8830
--- /dev/null
+++ b/sound/firewire/iso-resources.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef SOUND_FIREWIRE_ISO_RESOURCES_H_INCLUDED
+#define SOUND_FIREWIRE_ISO_RESOURCES_H_INCLUDED
+
+#include <linux/mutex.h>
+#include <linux/types.h>
+
+struct fw_unit;
+
+/**
+ * struct fw_iso_resources - manages channel/bandwidth allocation
+ * @channels_mask: if the device does not support all channel numbers, set this
+ * bit mask to something else than the default (all ones)
+ *
+ * This structure manages (de)allocation of isochronous resources (channel and
+ * bandwidth) for one isochronous stream.
+ */
+struct fw_iso_resources {
+ u64 channels_mask;
+ /* private: */
+ struct fw_unit *unit;
+ struct mutex mutex;
+ unsigned int channel;
+ unsigned int bandwidth; /* in bandwidth units, without overhead */
+ unsigned int bandwidth_overhead;
+ int generation; /* in which allocation is valid */
+ bool allocated;
+};
+
+int fw_iso_resources_init(struct fw_iso_resources *r,
+ struct fw_unit *unit);
+void fw_iso_resources_destroy(struct fw_iso_resources *r);
+
+int fw_iso_resources_allocate(struct fw_iso_resources *r,
+ unsigned int max_payload_bytes, int speed);
+int fw_iso_resources_update(struct fw_iso_resources *r);
+void fw_iso_resources_free(struct fw_iso_resources *r);
+
+#endif
diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c
new file mode 100644
index 000000000000..654e1a6050a9
--- /dev/null
+++ b/sound/firewire/lib.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * miscellaneous helper functions
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "lib.h"
+
+#define ERROR_RETRY_DELAY_MS 20
+
+/**
+ * snd_fw_transaction - send a request and wait for its completion
+ * @unit: the driver's unit on the target device
+ * @tcode: the transaction code
+ * @offset: the address in the target's address space
+ * @buffer: input/output data
+ * @length: length of @buffer
+ * @flags: use %FW_FIXED_GENERATION and add the generation value to attempt the
+ * request only in that generation; use %FW_QUIET to suppress error
+ * messages
+ *
+ * Submits an asynchronous request to the target device, and waits for the
+ * response. The node ID and the current generation are derived from @unit.
+ * On a bus reset or an error, the transaction is retried a few times.
+ * Returns zero on success, or a negative error code.
+ */
+int snd_fw_transaction(struct fw_unit *unit, int tcode,
+ u64 offset, void *buffer, size_t length,
+ unsigned int flags)
+{
+ struct fw_device *device = fw_parent_device(unit);
+ int generation, rcode, tries = 0;
+
+ generation = flags & FW_GENERATION_MASK;
+ for (;;) {
+ if (!(flags & FW_FIXED_GENERATION)) {
+ generation = device->generation;
+ smp_rmb(); /* node_id vs. generation */
+ }
+ rcode = fw_run_transaction(device->card, tcode,
+ device->node_id, generation,
+ device->max_speed, offset,
+ buffer, length);
+
+ if (rcode == RCODE_COMPLETE)
+ return 0;
+
+ if (rcode == RCODE_GENERATION && (flags & FW_FIXED_GENERATION))
+ return -EAGAIN;
+
+ if (rcode_is_permanent_error(rcode) || ++tries >= 3) {
+ if (!(flags & FW_QUIET))
+ dev_err(&unit->device,
+ "transaction failed: %s\n",
+ fw_rcode_string(rcode));
+ return -EIO;
+ }
+
+ msleep(ERROR_RETRY_DELAY_MS);
+ }
+}
+EXPORT_SYMBOL(snd_fw_transaction);
+
+MODULE_DESCRIPTION("FireWire audio helper functions");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h
new file mode 100644
index 000000000000..664dfdb9e58d
--- /dev/null
+++ b/sound/firewire/lib.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef SOUND_FIREWIRE_LIB_H_INCLUDED
+#define SOUND_FIREWIRE_LIB_H_INCLUDED
+
+#include <linux/firewire-constants.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <sound/rawmidi.h>
+
+struct fw_unit;
+
+#define FW_GENERATION_MASK 0x00ff
+#define FW_FIXED_GENERATION 0x0100
+#define FW_QUIET 0x0200
+
+int snd_fw_transaction(struct fw_unit *unit, int tcode,
+ u64 offset, void *buffer, size_t length,
+ unsigned int flags);
+
+/* returns true if retrying the transaction would not make sense */
+static inline bool rcode_is_permanent_error(int rcode)
+{
+ return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR;
+}
+
+#endif
diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
new file mode 100644
index 000000000000..df0fe886dbc0
--- /dev/null
+++ b/sound/firewire/motu/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+CFLAGS_amdtp-motu.o := -I$(src)
+
+snd-firewire-motu-y := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \
+ motu-proc.o motu-pcm.o motu-midi.o motu-hwdep.o \
+ motu-protocol-v2.o motu-protocol-v3.o \
+ motu-protocol-v1.o motu-register-dsp-message-parser.o \
+ motu-command-dsp-message-parser.o
+obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/amdtp-motu-trace.h b/sound/firewire/motu/amdtp-motu-trace.h
new file mode 100644
index 000000000000..3d36f125cf6a
--- /dev/null
+++ b/sound/firewire/motu/amdtp-motu-trace.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * amdtp-motu-trace.h - tracepoint definitions to dump a part of packet data
+ *
+ * Copyright (c) 2017 Takashi Sakamoto
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM snd_firewire_motu
+
+#if !defined(_SND_FIREWIRE_MOTU_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _SND_FIREWIRE_MOTU_TRACE_H
+
+#include <linux/tracepoint.h>
+
+static void copy_sph(u32 *frame, __be32 *buffer, unsigned int data_blocks,
+ unsigned int data_block_quadlets);
+static void copy_message(u64 *frames, __be32 *buffer, unsigned int data_blocks,
+ unsigned int data_block_quadlets);
+
+TRACE_EVENT(data_block_sph,
+ TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
+ TP_ARGS(s, data_blocks, buffer),
+ TP_STRUCT__entry(
+ __field(int, src)
+ __field(int, dst)
+ __field(unsigned int, data_blocks)
+ __dynamic_array(u32, tstamps, data_blocks)
+ ),
+ TP_fast_assign(
+ if (s->direction == AMDTP_IN_STREAM) {
+ __entry->src = fw_parent_device(s->unit)->node_id;
+ __entry->dst = fw_parent_device(s->unit)->card->node_id;
+ } else {
+ __entry->src = fw_parent_device(s->unit)->card->node_id;
+ __entry->dst = fw_parent_device(s->unit)->node_id;
+ }
+ __entry->data_blocks = data_blocks;
+ copy_sph(__get_dynamic_array(tstamps), buffer, data_blocks, s->data_block_quadlets);
+ ),
+ TP_printk(
+ "%04x %04x %u %s",
+ __entry->src,
+ __entry->dst,
+ __entry->data_blocks,
+ __print_array(__get_dynamic_array(tstamps), __entry->data_blocks, 4)
+ )
+);
+
+TRACE_EVENT(data_block_message,
+ TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
+ TP_ARGS(s, data_blocks, buffer),
+ TP_STRUCT__entry(
+ __field(int, src)
+ __field(int, dst)
+ __field(unsigned int, data_blocks)
+ __dynamic_array(u64, messages, data_blocks)
+ ),
+ TP_fast_assign(
+ if (s->direction == AMDTP_IN_STREAM) {
+ __entry->src = fw_parent_device(s->unit)->node_id;
+ __entry->dst = fw_parent_device(s->unit)->card->node_id;
+ } else {
+ __entry->src = fw_parent_device(s->unit)->card->node_id;
+ __entry->dst = fw_parent_device(s->unit)->node_id;
+ }
+ __entry->data_blocks = data_blocks;
+ copy_message(__get_dynamic_array(messages), buffer, data_blocks, s->data_block_quadlets);
+ ),
+ TP_printk(
+ "%04x %04x %u %s",
+ __entry->src,
+ __entry->dst,
+ __entry->data_blocks,
+ __print_array(__get_dynamic_array(messages), __entry->data_blocks, 8)
+ )
+);
+
+#endif
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE amdtp-motu-trace
+#include <trace/define_trace.h>
diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c
new file mode 100644
index 000000000000..39ed57d2c5a0
--- /dev/null
+++ b/sound/firewire/motu/amdtp-motu.c
@@ -0,0 +1,485 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * amdtp-motu.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include "motu.h"
+
+#define CREATE_TRACE_POINTS
+#include "amdtp-motu-trace.h"
+
+#define CIP_FMT_MOTU 0x02
+#define CIP_FMT_MOTU_TX_V3 0x22
+#define MOTU_FDF_AM824 0x22
+
+#define TICKS_PER_CYCLE 3072
+#define CYCLES_PER_SECOND 8000
+#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
+
+#define CIP_SPH_CYCLE_SHIFT 12
+#define CIP_SPH_CYCLE_MASK 0x01fff000
+#define CIP_SPH_OFFSET_MASK 0x00000fff
+
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND 3093
+
+struct amdtp_motu {
+ unsigned int pcm_chunks;
+ unsigned int pcm_byte_offset;
+
+ struct snd_rawmidi_substream *midi;
+ unsigned int midi_ports;
+ unsigned int midi_flag_offset;
+ unsigned int midi_byte_offset;
+
+ int midi_db_count;
+ unsigned int midi_db_interval;
+
+ struct amdtp_motu_cache *cache;
+};
+
+int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int midi_ports,
+ struct snd_motu_packet_format *formats)
+{
+ struct amdtp_motu *p = s->protocol;
+ unsigned int pcm_chunks, data_chunks, data_block_quadlets;
+ unsigned int mode;
+ int i, err;
+
+ if (amdtp_stream_running(s))
+ return -EBUSY;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ if (snd_motu_clock_rates[i] == rate) {
+ mode = i >> 1;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(snd_motu_clock_rates))
+ return -EINVAL;
+
+ // Each data block includes SPH in its head. Data chunks follow with
+ // 3 byte alignment. Padding follows with zero to conform to quadlet
+ // alignment.
+ pcm_chunks = formats->pcm_chunks[mode];
+ data_chunks = formats->msg_chunks + pcm_chunks;
+ data_block_quadlets = 1 + DIV_ROUND_UP(data_chunks * 3, 4);
+
+ err = amdtp_stream_set_parameters(s, rate, data_block_quadlets, 1);
+ if (err < 0)
+ return err;
+
+ p->pcm_chunks = pcm_chunks;
+ p->pcm_byte_offset = formats->pcm_byte_offset;
+
+ p->midi_ports = midi_ports;
+ p->midi_flag_offset = formats->midi_flag_offset;
+ p->midi_byte_offset = formats->midi_byte_offset;
+
+ p->midi_db_count = 0;
+ p->midi_db_interval = rate / MIDI_BYTES_PER_SECOND;
+
+ return 0;
+}
+
+static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int data_blocks,
+ unsigned int pcm_frames)
+{
+ struct amdtp_motu *p = s->protocol;
+ unsigned int channels = p->pcm_chunks;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ u8 *byte;
+ u32 *dst;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ for (i = 0; i < data_blocks; ++i) {
+ byte = (u8 *)buffer + p->pcm_byte_offset;
+
+ for (c = 0; c < channels; ++c) {
+ *dst = (byte[0] << 24) |
+ (byte[1] << 16) |
+ (byte[2] << 8);
+ byte += 3;
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int data_blocks,
+ unsigned int pcm_frames)
+{
+ struct amdtp_motu *p = s->protocol;
+ unsigned int channels = p->pcm_chunks;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ u8 *byte;
+ const u32 *src;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ for (i = 0; i < data_blocks; ++i) {
+ byte = (u8 *)buffer + p->pcm_byte_offset;
+
+ for (c = 0; c < channels; ++c) {
+ byte[0] = (*src >> 24) & 0xff;
+ byte[1] = (*src >> 16) & 0xff;
+ byte[2] = (*src >> 8) & 0xff;
+ byte += 3;
+ src++;
+ }
+
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_motu *p = s->protocol;
+ unsigned int channels, i, c;
+ u8 *byte;
+
+ channels = p->pcm_chunks;
+
+ for (i = 0; i < data_blocks; ++i) {
+ byte = (u8 *)buffer + p->pcm_byte_offset;
+
+ for (c = 0; c < channels; ++c) {
+ byte[0] = 0;
+ byte[1] = 0;
+ byte[2] = 0;
+ byte += 3;
+ }
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ /* TODO: how to set an constraint for exactly 24bit PCM sample? */
+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (err < 0)
+ return err;
+
+ return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+void amdtp_motu_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi)
+{
+ struct amdtp_motu *p = s->protocol;
+
+ if (port < p->midi_ports)
+ WRITE_ONCE(p->midi, midi);
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_motu *p = s->protocol;
+ struct snd_rawmidi_substream *midi = READ_ONCE(p->midi);
+ u8 *b;
+ int i;
+
+ for (i = 0; i < data_blocks; i++) {
+ b = (u8 *)buffer;
+
+ if (midi && p->midi_db_count == 0 &&
+ snd_rawmidi_transmit(midi, b + p->midi_byte_offset, 1) == 1) {
+ b[p->midi_flag_offset] = 0x01;
+ } else {
+ b[p->midi_byte_offset] = 0x00;
+ b[p->midi_flag_offset] = 0x00;
+ }
+
+ buffer += s->data_block_quadlets;
+
+ if (--p->midi_db_count < 0)
+ p->midi_db_count = p->midi_db_interval;
+ }
+}
+
+static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_motu *p = s->protocol;
+ struct snd_rawmidi_substream *midi;
+ u8 *b;
+ int i;
+
+ for (i = 0; i < data_blocks; i++) {
+ b = (u8 *)buffer;
+ midi = READ_ONCE(p->midi);
+
+ if (midi && (b[p->midi_flag_offset] & 0x01))
+ snd_rawmidi_receive(midi, b + p->midi_byte_offset, 1);
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+/* For tracepoints. */
+static void __maybe_unused copy_sph(u32 *frames, __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int data_block_quadlets)
+{
+ unsigned int i;
+
+ for (i = 0; i < data_blocks; ++i) {
+ *frames = be32_to_cpu(*buffer);
+ buffer += data_block_quadlets;
+ frames++;
+ }
+}
+
+/* For tracepoints. */
+static void __maybe_unused copy_message(u64 *frames, __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int data_block_quadlets)
+{
+ unsigned int i;
+
+ /* This is just for v2/v3 protocol. */
+ for (i = 0; i < data_blocks; ++i) {
+ *frames = be32_to_cpu(buffer[1]);
+ *frames <<= 16;
+ *frames |= be32_to_cpu(buffer[2]) >> 16;
+ ++frames;
+ buffer += data_block_quadlets;
+ }
+}
+
+static void probe_tracepoints_events(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count)
+{
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ trace_data_block_sph(s, data_blocks, buf);
+ trace_data_block_message(s, data_blocks, buf);
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+}
+
+static void cache_event_offsets(struct amdtp_motu_cache *cache, const __be32 *buf,
+ unsigned int data_blocks, unsigned int data_block_quadlets)
+{
+ unsigned int *event_offsets = cache->event_offsets;
+ const unsigned int cache_size = cache->size;
+ unsigned int cache_tail = cache->tail;
+ unsigned int base_tick = cache->tx_cycle_count * TICKS_PER_CYCLE;
+ int i;
+
+ for (i = 0; i < data_blocks; ++i) {
+ u32 sph = be32_to_cpu(*buf);
+ unsigned int tick;
+
+ tick = ((sph & CIP_SPH_CYCLE_MASK) >> CIP_SPH_CYCLE_SHIFT) * TICKS_PER_CYCLE +
+ (sph & CIP_SPH_OFFSET_MASK);
+
+ if (tick < base_tick)
+ tick += TICKS_PER_SECOND;
+ event_offsets[cache_tail] = tick - base_tick;
+
+ cache_tail = (cache_tail + 1) % cache_size;
+ buf += data_block_quadlets;
+ }
+
+ cache->tail = cache_tail;
+ cache->tx_cycle_count = (cache->tx_cycle_count + 1) % CYCLES_PER_SECOND;
+}
+
+static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream);
+ struct amdtp_motu *p = s->protocol;
+ const struct pkt_desc *cursor = desc;
+ unsigned int pcm_frames = 0;
+ int i;
+
+ if (p->cache->tx_cycle_count == UINT_MAX)
+ p->cache->tx_cycle_count = (s->domain->processing_cycle.tx_start % CYCLES_PER_SECOND);
+
+ // For data block processing.
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ cache_event_offsets(p->cache, buf, data_blocks, s->data_block_quadlets);
+
+ if (pcm) {
+ read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks;
+ }
+
+ if (p->midi_ports)
+ read_midi_messages(s, buf, data_blocks);
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+
+ desc = cursor;
+ if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)
+ snd_motu_register_dsp_message_parser_parse(s, desc, count);
+ else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP)
+ snd_motu_command_dsp_message_parser_parse(s, desc, count);
+
+ // For tracepoints.
+ if (trace_data_block_sph_enabled() ||
+ trace_data_block_message_enabled())
+ probe_tracepoints_events(s, desc, count);
+}
+
+static void write_sph(struct amdtp_motu_cache *cache, __be32 *buffer, unsigned int data_blocks,
+ unsigned int data_block_quadlets)
+{
+ unsigned int *event_offsets = cache->event_offsets;
+ const unsigned int cache_size = cache->size;
+ unsigned int cache_head = cache->head;
+ unsigned int base_tick = cache->rx_cycle_count * TICKS_PER_CYCLE;
+ int i;
+
+ for (i = 0; i < data_blocks; i++) {
+ unsigned int tick = (base_tick + event_offsets[cache_head]) % TICKS_PER_SECOND;
+ u32 sph = ((tick / TICKS_PER_CYCLE) << CIP_SPH_CYCLE_SHIFT) | (tick % TICKS_PER_CYCLE);
+ *buffer = cpu_to_be32(sph);
+
+ cache_head = (cache_head + 1) % cache_size;
+ buffer += data_block_quadlets;
+ }
+
+ cache->head = cache_head;
+ cache->rx_cycle_count = (cache->rx_cycle_count + 1) % CYCLES_PER_SECOND;
+}
+
+static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ struct amdtp_motu *p = s->protocol;
+ const struct pkt_desc *cursor = desc;
+ unsigned int pcm_frames = 0;
+ int i;
+
+ if (p->cache->rx_cycle_count == UINT_MAX)
+ p->cache->rx_cycle_count = (s->domain->processing_cycle.rx_start % CYCLES_PER_SECOND);
+
+ // For data block processing.
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ if (pcm) {
+ write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks;
+ } else {
+ write_pcm_silence(s, buf, data_blocks);
+ }
+
+ if (p->midi_ports)
+ write_midi_messages(s, buf, data_blocks);
+
+ write_sph(p->cache, buf, data_blocks, s->data_block_quadlets);
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+
+ desc = cursor;
+
+ // For tracepoints.
+ if (trace_data_block_sph_enabled() ||
+ trace_data_block_message_enabled())
+ probe_tracepoints_events(s, desc, count);
+}
+
+int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir,
+ const struct snd_motu_spec *spec, struct amdtp_motu_cache *cache)
+{
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
+ int fmt = CIP_FMT_MOTU;
+ unsigned int flags = CIP_BLOCKING | CIP_UNAWARE_SYT;
+ struct amdtp_motu *p;
+ int err;
+
+ if (dir == AMDTP_IN_STREAM) {
+ process_ctx_payloads = process_ir_ctx_payloads;
+
+ /*
+ * Units of version 3 transmits packets with invalid CIP header
+ * against IEC 61883-1.
+ */
+ if (spec->protocol_version == SND_MOTU_PROTOCOL_V3) {
+ flags |= CIP_WRONG_DBS |
+ CIP_SKIP_DBC_ZERO_CHECK |
+ CIP_HEADER_WITHOUT_EOH;
+ fmt = CIP_FMT_MOTU_TX_V3;
+ }
+
+ if (spec == &snd_motu_spec_8pre ||
+ spec == &snd_motu_spec_ultralite) {
+ // 8pre has some quirks.
+ flags |= CIP_WRONG_DBS |
+ CIP_SKIP_DBC_ZERO_CHECK;
+ }
+ } else {
+ process_ctx_payloads = process_it_ctx_payloads;
+ flags |= CIP_DBC_IS_END_EVENT;
+ }
+
+ err = amdtp_stream_init(s, unit, dir, flags, fmt, process_ctx_payloads,
+ sizeof(struct amdtp_motu));
+ if (err < 0)
+ return err;
+
+ s->sph = 1;
+
+ if (dir == AMDTP_OUT_STREAM) {
+ // Use fixed value for FDF field.
+ s->ctx_data.rx.fdf = MOTU_FDF_AM824;
+ }
+
+ p = s->protocol;
+ p->cache = cache;
+
+ return 0;
+}
diff --git a/sound/firewire/motu/motu-command-dsp-message-parser.c b/sound/firewire/motu/motu-command-dsp-message-parser.c
new file mode 100644
index 000000000000..c6440e6e360b
--- /dev/null
+++ b/sound/firewire/motu/motu-command-dsp-message-parser.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// motu-command-dsp-message-parser.c - a part of driver for MOTU FireWire series
+//
+// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+
+// Below models allow software to configure their DSP function by command transferred in
+// asynchronous transaction:
+// * 828 mk3 (FireWire only and Hybrid)
+// * 896 mk3 (FireWire only and Hybrid)
+// * Ultralite mk3 (FireWire only and Hybrid)
+// * Traveler mk3
+// * Track 16
+//
+// Isochronous packets from the above models includes messages to report state of hardware meter.
+
+#include "motu.h"
+
+enum msg_parser_state {
+ INITIALIZED,
+ FRAGMENT_DETECTED,
+ AVAILABLE,
+};
+
+struct msg_parser {
+ spinlock_t lock;
+ enum msg_parser_state state;
+ unsigned int interval;
+ unsigned int message_count;
+ unsigned int fragment_pos;
+ unsigned int value_index;
+ u64 value;
+ struct snd_firewire_motu_command_dsp_meter meter;
+};
+
+int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu)
+{
+ struct msg_parser *parser;
+
+ parser = devm_kzalloc(&motu->card->card_dev, sizeof(*parser), GFP_KERNEL);
+ if (!parser)
+ return -ENOMEM;
+ spin_lock_init(&parser->lock);
+ motu->message_parser = parser;
+
+ return 0;
+}
+
+int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc)
+{
+ struct msg_parser *parser = motu->message_parser;
+
+ parser->state = INITIALIZED;
+
+ // All of data blocks don't have messages with meaningful information.
+ switch (sfc) {
+ case CIP_SFC_176400:
+ case CIP_SFC_192000:
+ parser->interval = 4;
+ break;
+ case CIP_SFC_88200:
+ case CIP_SFC_96000:
+ parser->interval = 2;
+ break;
+ case CIP_SFC_32000:
+ case CIP_SFC_44100:
+ case CIP_SFC_48000:
+ default:
+ parser->interval = 1;
+ break;
+ }
+
+ return 0;
+}
+
+#define FRAGMENT_POS 6
+#define MIDI_BYTE_POS 7
+#define MIDI_FLAG_POS 8
+// One value of hardware meter consists of 4 messages.
+#define FRAGMENTS_PER_VALUE 4
+#define VALUES_AT_IMAGE_END 0xffffffffffffffff
+
+void snd_motu_command_dsp_message_parser_parse(const struct amdtp_stream *s,
+ const struct pkt_desc *desc, unsigned int count)
+{
+ struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream);
+ unsigned int data_block_quadlets = s->data_block_quadlets;
+ struct msg_parser *parser = motu->message_parser;
+ unsigned int interval = parser->interval;
+ int i;
+
+ guard(spinlock_irqsave)(&parser->lock);
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buffer = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+ int j;
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+
+ for (j = 0; j < data_blocks; ++j) {
+ u8 *b = (u8 *)buffer;
+ buffer += data_block_quadlets;
+
+ switch (parser->state) {
+ case INITIALIZED:
+ {
+ u8 fragment = b[FRAGMENT_POS];
+
+ if (fragment > 0) {
+ parser->value = fragment;
+ parser->message_count = 1;
+ parser->state = FRAGMENT_DETECTED;
+ }
+ break;
+ }
+ case FRAGMENT_DETECTED:
+ {
+ if (parser->message_count % interval == 0) {
+ u8 fragment = b[FRAGMENT_POS];
+
+ parser->value >>= 8;
+ parser->value |= (u64)fragment << 56;
+
+ if (parser->value == VALUES_AT_IMAGE_END) {
+ parser->state = AVAILABLE;
+ parser->fragment_pos = 0;
+ parser->value_index = 0;
+ parser->message_count = 0;
+ }
+ }
+ ++parser->message_count;
+ break;
+ }
+ case AVAILABLE:
+ default:
+ {
+ if (parser->message_count % interval == 0) {
+ u8 fragment = b[FRAGMENT_POS];
+
+ parser->value >>= 8;
+ parser->value |= (u64)fragment << 56;
+ ++parser->fragment_pos;
+
+ if (parser->fragment_pos == 4) {
+ // Skip the last two quadlets since they could be
+ // invalid value (0xffffffff) as floating point
+ // number.
+ if (parser->value_index <
+ SNDRV_FIREWIRE_MOTU_COMMAND_DSP_METER_COUNT - 2) {
+ u32 val = (u32)(parser->value >> 32);
+ parser->meter.data[parser->value_index] = val;
+ }
+ ++parser->value_index;
+ parser->fragment_pos = 0;
+ }
+
+ if (parser->value == VALUES_AT_IMAGE_END) {
+ parser->value_index = 0;
+ parser->fragment_pos = 0;
+ parser->message_count = 0;
+ }
+ }
+ ++parser->message_count;
+ break;
+ }
+ }
+ }
+ }
+}
+
+void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu,
+ struct snd_firewire_motu_command_dsp_meter *meter)
+{
+ struct msg_parser *parser = motu->message_parser;
+
+ guard(spinlock_irqsave)(&parser->lock);
+ memcpy(meter, &parser->meter, sizeof(*meter));
+}
diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c
new file mode 100644
index 000000000000..981c19430cb0
--- /dev/null
+++ b/sound/firewire/motu/motu-hwdep.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * motu-hwdep.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+/*
+ * This codes have five functionalities.
+ *
+ * 1.get information about firewire node
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock streaming
+ *
+ */
+
+#include "motu.h"
+
+static bool has_dsp_event(struct snd_motu *motu)
+{
+ if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)
+ return (snd_motu_register_dsp_message_parser_count_event(motu) > 0);
+ else
+ return false;
+}
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_motu *motu = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&motu->lock);
+
+ while (!motu->dev_lock_changed && motu->msg == 0 && !has_dsp_event(motu)) {
+ prepare_to_wait(&motu->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&motu->lock);
+ schedule();
+ finish_wait(&motu->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&motu->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ if (motu->dev_lock_changed) {
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (motu->dev_lock_count > 0);
+ motu->dev_lock_changed = false;
+ spin_unlock_irq(&motu->lock);
+
+ count = min_t(long, count, sizeof(event));
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+ } else if (motu->msg > 0) {
+ event.motu_notification.type = SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION;
+ event.motu_notification.message = motu->msg;
+ motu->msg = 0;
+ spin_unlock_irq(&motu->lock);
+
+ count = min_t(long, count, sizeof(event));
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+ } else if (has_dsp_event(motu)) {
+ size_t consumed = 0;
+ u32 __user *ptr;
+ u32 ev;
+
+ spin_unlock_irq(&motu->lock);
+
+ // Header is filled later.
+ consumed += sizeof(event.motu_register_dsp_change);
+
+ while (consumed < count &&
+ snd_motu_register_dsp_message_parser_copy_event(motu, &ev)) {
+ ptr = (u32 __user *)(buf + consumed);
+ if (put_user(ev, ptr))
+ return -EFAULT;
+ consumed += sizeof(ev);
+ }
+
+ event.motu_register_dsp_change.type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE;
+ event.motu_register_dsp_change.count =
+ (consumed - sizeof(event.motu_register_dsp_change)) / 4;
+ if (copy_to_user(buf, &event, sizeof(event.motu_register_dsp_change)))
+ return -EFAULT;
+
+ count = consumed;
+ } else {
+ spin_unlock_irq(&motu->lock);
+
+ count = 0;
+ }
+
+ return count;
+}
+
+static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_motu *motu = hwdep->private_data;
+
+ poll_wait(file, &motu->hwdep_wait, wait);
+
+ guard(spinlock_irq)(&motu->lock);
+ if (motu->dev_lock_changed || motu->msg || has_dsp_event(motu))
+ return EPOLLIN | EPOLLRDNORM;
+ else
+ return 0;
+}
+
+static int hwdep_get_info(struct snd_motu *motu, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(motu->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_MOTU;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strscpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_motu *motu)
+{
+ guard(spinlock_irq)(&motu->lock);
+
+ if (motu->dev_lock_count == 0) {
+ motu->dev_lock_count = -1;
+ return 0;
+ } else {
+ return -EBUSY;
+ }
+}
+
+static int hwdep_unlock(struct snd_motu *motu)
+{
+ guard(spinlock_irq)(&motu->lock);
+
+ if (motu->dev_lock_count == -1) {
+ motu->dev_lock_count = 0;
+ return 0;
+ } else {
+ return -EBADFD;
+ }
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_motu *motu = hwdep->private_data;
+
+ guard(spinlock_irq)(&motu->lock);
+ if (motu->dev_lock_count == -1)
+ motu->dev_lock_count = 0;
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_motu *motu = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(motu, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(motu);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(motu);
+ case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_METER:
+ {
+ struct snd_firewire_motu_register_dsp_meter *meter;
+ int err;
+
+ if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP))
+ return -ENXIO;
+
+ meter = kzalloc(sizeof(*meter), GFP_KERNEL);
+ if (!meter)
+ return -ENOMEM;
+
+ snd_motu_register_dsp_message_parser_copy_meter(motu, meter);
+
+ err = copy_to_user((void __user *)arg, meter, sizeof(*meter));
+ kfree(meter);
+
+ if (err)
+ return -EFAULT;
+
+ return 0;
+ }
+ case SNDRV_FIREWIRE_IOCTL_MOTU_COMMAND_DSP_METER:
+ {
+ struct snd_firewire_motu_command_dsp_meter *meter;
+ int err;
+
+ if (!(motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP))
+ return -ENXIO;
+
+ meter = kzalloc(sizeof(*meter), GFP_KERNEL);
+ if (!meter)
+ return -ENOMEM;
+
+ snd_motu_command_dsp_message_parser_copy_meter(motu, meter);
+
+ err = copy_to_user((void __user *)arg, meter, sizeof(*meter));
+ kfree(meter);
+
+ if (err)
+ return -EFAULT;
+
+ return 0;
+ }
+ case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_PARAMETER:
+ {
+ struct snd_firewire_motu_register_dsp_parameter *param;
+ int err;
+
+ if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP))
+ return -ENXIO;
+
+ param = kzalloc(sizeof(*param), GFP_KERNEL);
+ if (!param)
+ return -ENOMEM;
+
+ snd_motu_register_dsp_message_parser_copy_parameter(motu, param);
+
+ err = copy_to_user((void __user *)arg, param, sizeof(*param));
+ kfree(param);
+ if (err)
+ return -EFAULT;
+
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_motu_create_hwdep_device(struct snd_motu *motu)
+{
+ static const struct snd_hwdep_ops ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(motu->card, motu->card->driver, 0, &hwdep);
+ if (err < 0)
+ return err;
+
+ strscpy(hwdep->name, "MOTU");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_MOTU;
+ hwdep->ops = ops;
+ hwdep->private_data = motu;
+ hwdep->exclusive = true;
+
+ motu->hwdep = hwdep;
+
+ return 0;
+}
diff --git a/sound/firewire/motu/motu-midi.c b/sound/firewire/motu/motu-midi.c
new file mode 100644
index 000000000000..85e3260f9349
--- /dev/null
+++ b/sound/firewire/motu/motu-midi.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * motu-midi.h - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+#include "motu.h"
+
+static int midi_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_motu *motu = substream->rmidi->private_data;
+ int err;
+
+ err = snd_motu_stream_lock_try(motu);
+ if (err < 0)
+ return err;
+
+ scoped_guard(mutex, &motu->mutex) {
+ err = snd_motu_stream_reserve_duplex(motu, 0, 0, 0);
+ if (err >= 0) {
+ ++motu->substreams_counter;
+ err = snd_motu_stream_start_duplex(motu);
+ if (err < 0)
+ --motu->substreams_counter;
+ }
+ }
+
+ if (err < 0)
+ snd_motu_stream_lock_release(motu);
+
+ return err;
+}
+
+static int midi_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_motu *motu = substream->rmidi->private_data;
+
+ scoped_guard(mutex, &motu->mutex) {
+ --motu->substreams_counter;
+ snd_motu_stream_stop_duplex(motu);
+ }
+
+ snd_motu_stream_lock_release(motu);
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_motu *motu = substrm->rmidi->private_data;
+
+ guard(spinlock_irqsave)(&motu->lock);
+
+ if (up)
+ amdtp_motu_midi_trigger(&motu->tx_stream, substrm->number,
+ substrm);
+ else
+ amdtp_motu_midi_trigger(&motu->tx_stream, substrm->number,
+ NULL);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_motu *motu = substrm->rmidi->private_data;
+
+ guard(spinlock_irqsave)(&motu->lock);
+
+ if (up)
+ amdtp_motu_midi_trigger(&motu->rx_stream, substrm->number,
+ substrm);
+ else
+ amdtp_motu_midi_trigger(&motu->rx_stream, substrm->number,
+ NULL);
+}
+
+static void set_midi_substream_names(struct snd_motu *motu,
+ struct snd_rawmidi_str *str)
+{
+ struct snd_rawmidi_substream *subs;
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ scnprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d", motu->card->shortname, subs->number + 1);
+ }
+}
+
+int snd_motu_create_midi_devices(struct snd_motu *motu)
+{
+ static const struct snd_rawmidi_ops capture_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_capture_trigger,
+ };
+ static const struct snd_rawmidi_ops playback_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_playback_trigger,
+ };
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *str;
+ int err;
+
+ /* create midi ports */
+ err = snd_rawmidi_new(motu->card, motu->card->driver, 0, 1, 1, &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", motu->card->shortname);
+ rmidi->private_data = motu;
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &capture_ops);
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+ set_midi_substream_names(motu, str);
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &playback_ops);
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+ set_midi_substream_names(motu, str);
+
+ return 0;
+}
diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c
new file mode 100644
index 000000000000..600c571edf02
--- /dev/null
+++ b/sound/firewire/motu/motu-pcm.c
@@ -0,0 +1,365 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * motu-pcm.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include <sound/pcm_params.h>
+#include "motu.h"
+
+static int motu_rate_constraint(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_motu_packet_format *formats = rule->private;
+
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval rates = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i, pcm_channels, rate, mode;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ rate = snd_motu_clock_rates[i];
+ mode = i / 2;
+
+ pcm_channels = formats->pcm_chunks[mode];
+ if (!snd_interval_test(c, pcm_channels))
+ continue;
+
+ rates.min = min(rates.min, rate);
+ rates.max = max(rates.max, rate);
+ }
+
+ return snd_interval_refine(r, &rates);
+}
+
+static int motu_channels_constraint(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_motu_packet_format *formats = rule->private;
+
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval channels = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i, pcm_channels, rate, mode;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ rate = snd_motu_clock_rates[i];
+ mode = i / 2;
+
+ if (!snd_interval_test(r, rate))
+ continue;
+
+ pcm_channels = formats->pcm_chunks[mode];
+ channels.min = min(channels.min, pcm_channels);
+ channels.max = max(channels.max, pcm_channels);
+ }
+
+ return snd_interval_refine(c, &channels);
+}
+
+static void limit_channels_and_rates(struct snd_motu *motu,
+ struct snd_pcm_runtime *runtime,
+ struct snd_motu_packet_format *formats)
+{
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ unsigned int i, pcm_channels, rate, mode;
+
+ hw->channels_min = UINT_MAX;
+ hw->channels_max = 0;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ rate = snd_motu_clock_rates[i];
+ mode = i / 2;
+
+ pcm_channels = formats->pcm_chunks[mode];
+ if (pcm_channels == 0)
+ continue;
+
+ hw->rates |= snd_pcm_rate_to_rate_bit(rate);
+ hw->channels_min = min(hw->channels_min, pcm_channels);
+ hw->channels_max = max(hw->channels_max, pcm_channels);
+ }
+
+ snd_pcm_limit_hw_rates(runtime);
+}
+
+static int init_hw_info(struct snd_motu *motu,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ struct amdtp_stream *stream;
+ struct snd_motu_packet_format *formats;
+ int err;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ hw->formats = SNDRV_PCM_FMTBIT_S32;
+ stream = &motu->tx_stream;
+ formats = &motu->tx_packet_formats;
+ } else {
+ hw->formats = SNDRV_PCM_FMTBIT_S32;
+ stream = &motu->rx_stream;
+ formats = &motu->rx_packet_formats;
+ }
+
+ limit_channels_and_rates(motu, runtime, formats);
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ motu_rate_constraint, formats,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ motu_channels_constraint, formats,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ return err;
+
+ return amdtp_motu_add_pcm_hw_constraints(stream, runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+ struct amdtp_domain *d = &motu->domain;
+ enum snd_motu_clock_source src;
+ int err;
+
+ err = snd_motu_stream_lock_try(motu);
+ if (err < 0)
+ return err;
+
+ scoped_guard(mutex, &motu->mutex) {
+ err = snd_motu_stream_cache_packet_formats(motu);
+ if (err < 0)
+ goto err_locked;
+
+ err = init_hw_info(motu, substream);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_motu_protocol_get_clock_source(motu, &src);
+ if (err < 0)
+ goto err_locked;
+
+ // When source of clock is not internal or any stream is reserved for
+ // transmission of PCM frames, the available sampling rate is limited
+ // at current one.
+ if ((src != SND_MOTU_CLOCK_SOURCE_INTERNAL &&
+ src != SND_MOTU_CLOCK_SOURCE_SPH) ||
+ (motu->substreams_counter > 0 && d->events_per_period > 0)) {
+ unsigned int frames_per_period = d->events_per_period;
+ unsigned int frames_per_buffer = d->events_per_buffer;
+ unsigned int rate;
+
+ err = snd_motu_protocol_get_clock_rate(motu, &rate);
+ if (err < 0)
+ goto err_locked;
+
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+
+ if (frames_per_period > 0) {
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ frames_per_period, frames_per_period);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ frames_per_buffer, frames_per_buffer);
+ if (err < 0)
+ goto err_locked;
+ }
+ }
+ }
+
+ snd_pcm_set_sync(substream);
+
+ return 0;
+err_locked:
+ snd_motu_stream_lock_release(motu);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ snd_motu_stream_lock_release(motu);
+
+ return 0;
+}
+
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_motu *motu = substream->private_data;
+ int err = 0;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ unsigned int rate = params_rate(hw_params);
+ unsigned int frames_per_period = params_period_size(hw_params);
+ unsigned int frames_per_buffer = params_buffer_size(hw_params);
+
+ guard(mutex)(&motu->mutex);
+ err = snd_motu_stream_reserve_duplex(motu, rate,
+ frames_per_period, frames_per_buffer);
+ if (err >= 0)
+ ++motu->substreams_counter;
+ }
+
+ return err;
+}
+
+static int pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ guard(mutex)(&motu->mutex);
+
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --motu->substreams_counter;
+
+ snd_motu_stream_stop_duplex(motu);
+
+ return 0;
+}
+
+static int capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+ int err;
+
+ scoped_guard(mutex, &motu->mutex) {
+ err = snd_motu_stream_start_duplex(motu);
+ }
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&motu->tx_stream);
+
+ return 0;
+}
+static int playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+ int err;
+
+ scoped_guard(mutex, &motu->mutex) {
+ err = snd_motu_stream_start_duplex(motu);
+ }
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&motu->rx_stream);
+
+ return err;
+}
+
+static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&motu->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&motu->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&motu->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&motu->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&motu->domain, &motu->tx_stream);
+}
+static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&motu->domain, &motu->rx_stream);
+}
+
+static int capture_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->tx_stream);
+}
+
+static int playback_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->rx_stream);
+}
+
+int snd_motu_create_pcm_devices(struct snd_motu *motu)
+{
+ static const struct snd_pcm_ops capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = capture_prepare,
+ .trigger = capture_trigger,
+ .pointer = capture_pointer,
+ .ack = capture_ack,
+ };
+ static const struct snd_pcm_ops playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = playback_prepare,
+ .trigger = playback_trigger,
+ .pointer = playback_pointer,
+ .ack = playback_ack,
+ };
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(motu->card, motu->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = motu;
+ pcm->nonatomic = true;
+ strscpy(pcm->name, motu->card->shortname);
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
+
+ return 0;
+}
diff --git a/sound/firewire/motu/motu-proc.c b/sound/firewire/motu/motu-proc.c
new file mode 100644
index 000000000000..f009cf7aa074
--- /dev/null
+++ b/sound/firewire/motu/motu-proc.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * motu-proc.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include "./motu.h"
+
+static const char *const clock_names[] = {
+ [SND_MOTU_CLOCK_SOURCE_INTERNAL] = "Internal",
+ [SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB] = "ADAT on Dsub-9pin interface",
+ [SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT] = "ADAT on optical interface",
+ [SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A] = "ADAT on optical interface A",
+ [SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B] = "ADAT on optical interface B",
+ [SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT] = "S/PDIF on optical interface",
+ [SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A] = "S/PDIF on optical interface A",
+ [SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B] = "S/PDIF on optical interface B",
+ [SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX] = "S/PDIF on coaxial interface",
+ [SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR] = "AESEBU on XLR interface",
+ [SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC] = "Word clock on BNC interface",
+ [SND_MOTU_CLOCK_SOURCE_SPH] = "Source packet header",
+ [SND_MOTU_CLOCK_SOURCE_UNKNOWN] = "Unknown",
+};
+
+static void proc_read_clock(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+
+ struct snd_motu *motu = entry->private_data;
+ unsigned int rate;
+ enum snd_motu_clock_source source;
+
+ if (snd_motu_protocol_get_clock_rate(motu, &rate) < 0)
+ return;
+ if (snd_motu_protocol_get_clock_source(motu, &source) < 0)
+ return;
+
+ snd_iprintf(buffer, "Rate:\t%d\n", rate);
+ snd_iprintf(buffer, "Source:\t%s\n", clock_names[source]);
+}
+
+static void proc_read_format(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_motu *motu = entry->private_data;
+ unsigned int mode;
+ struct snd_motu_packet_format *formats;
+ int i;
+
+ if (snd_motu_protocol_cache_packet_formats(motu) < 0)
+ return;
+
+ snd_iprintf(buffer, "tx:\tmsg\tfixed\ttotal\n");
+ for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) {
+ mode = i >> 1;
+
+ formats = &motu->tx_packet_formats;
+ snd_iprintf(buffer,
+ "%u:\t%u\t%u\t%u\n",
+ snd_motu_clock_rates[i],
+ formats->msg_chunks,
+ motu->spec->tx_fixed_pcm_chunks[mode],
+ formats->pcm_chunks[mode]);
+ }
+
+ snd_iprintf(buffer, "rx:\tmsg\tfixed\ttotal\n");
+ for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) {
+ mode = i >> 1;
+
+ formats = &motu->rx_packet_formats;
+ snd_iprintf(buffer,
+ "%u:\t%u\t%u\t%u\n",
+ snd_motu_clock_rates[i],
+ formats->msg_chunks,
+ motu->spec->rx_fixed_pcm_chunks[mode],
+ formats->pcm_chunks[mode]);
+ }
+}
+
+static void add_node(struct snd_motu *motu, struct snd_info_entry *root,
+ const char *name,
+ void (*op)(struct snd_info_entry *e,
+ struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(motu->card, name, root);
+ if (entry)
+ snd_info_set_text_ops(entry, motu, op);
+}
+
+void snd_motu_proc_init(struct snd_motu *motu)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(motu->card, "firewire",
+ motu->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | 0555;
+
+ add_node(motu, root, "clock", proc_read_clock);
+ add_node(motu, root, "format", proc_read_format);
+}
diff --git a/sound/firewire/motu/motu-protocol-v1.c b/sound/firewire/motu/motu-protocol-v1.c
new file mode 100644
index 000000000000..e811629f167b
--- /dev/null
+++ b/sound/firewire/motu/motu-protocol-v1.c
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// motu-protocol-v1.c - a part of driver for MOTU FireWire series
+//
+// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+
+#include "motu.h"
+
+#include <linux/delay.h>
+
+// Status register for MOTU 828 (0x'ffff'f000'0b00).
+//
+// 0xffff0000: ISOC_COMM_CONTROL_MASK in motu-stream.c.
+// 0x00008000: mode of optical input interface.
+// 0x00008000: for S/PDIF signal.
+// 0x00000000: disabled or for ADAT signal.
+// 0x00004000: mode of optical output interface.
+// 0x00004000: for S/PDIF signal.
+// 0x00000000: disabled or for ADAT signal.
+// 0x00003f00: monitor input mode.
+// 0x00000800: analog-1/2
+// 0x00001a00: analog-3/4
+// 0x00002c00: analog-5/6
+// 0x00003e00: analog-7/8
+// 0x00000000: analog-1
+// 0x00000900: analog-2
+// 0x00001200: analog-3
+// 0x00001b00: analog-4
+// 0x00002400: analog-5
+// 0x00002d00: analog-6
+// 0x00003600: analog-7
+// 0x00003f00: analog-8
+// 0x00000080: enable stream input.
+// 0x00000040: disable monitor input.
+// 0x00000008: enable main out.
+// 0x00000004: rate of sampling clock.
+// 0x00000004: 48.0 kHz
+// 0x00000000: 44.1 kHz
+// 0x00000023: source of sampling clock.
+// 0x00000003: source packet header (SPH)
+// 0x00000002: S/PDIF on optical/coaxial interface.
+// 0x00000021: ADAT on optical interface
+// 0x00000001: ADAT on Dsub 9pin
+// 0x00000000: internal
+
+#define CLK_828_STATUS_OFFSET 0x0b00
+#define CLK_828_STATUS_MASK 0x0000ffff
+#define CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF 0x00008000
+#define CLK_828_STATUS_FLAG_OPT_OUT_IFACE_IS_SPDIF 0x00004000
+#define CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES 0x00000080
+#define CLK_828_STATUS_FLAG_ENABLE_OUTPUT 0x00000008
+#define CLK_828_STATUS_FLAG_RATE_48000 0x00000004
+#define CLK_828_STATUS_MASK_SRC 0x00000023
+#define CLK_828_STATUS_FLAG_SRC_ADAT_ON_OPT 0x00000021
+#define CLK_828_STATUS_FLAG_SRC_SPH 0x00000003
+#define CLK_828_STATUS_FLAG_SRC_SPDIF 0x00000002
+#define CLK_828_STATUS_FLAG_SRC_ADAT_ON_DSUB 0x00000001
+#define CLK_828_STATUS_FLAG_SRC_INTERNAL 0x00000000
+
+// Status register for MOTU 896 (0x'ffff'f000'0b14).
+//
+// 0xf0000000: enable physical and stream input to DAC.
+// 0x80000000: disable
+// 0x40000000: disable
+// 0x20000000: enable (prior to the other bits)
+// 0x10000000: disable
+// 0x00000000: disable
+// 0x08000000: speed of word clock signal output on BNC interface.
+// 0x00000000: force to low rate (44.1/48.0 kHz).
+// 0x08000000: follow to system clock.
+// 0x04000000: something relevant to clock.
+// 0x03000000: enable output.
+// 0x02000000: enabled irreversibly once standing unless the device voluntarily disables it.
+// 0x01000000: enabled irreversibly once standing unless the device voluntarily disables it.
+// 0x00ffff00: monitor input mode.
+// 0x00000000: disabled
+// 0x00004800: analog-1/2
+// 0x00005a00: analog-3/4
+// 0x00006c00: analog-5/6
+// 0x00007e00: analog-7/8
+// 0x00104800: AES/EBU-1/2
+// 0x00004000: analog-1
+// 0x00004900: analog-2
+// 0x00005200: analog-3
+// 0x00005b00: analog-4
+// 0x00006400: analog-5
+// 0x00006d00: analog-6
+// 0x00007600: analog-7
+// 0x00007f00: analog-8
+// 0x00104000: AES/EBU-1
+// 0x00104900: AES/EBU-2
+// 0x00000060: sample rate conversion for AES/EBU input/output.
+// 0x00000000: None
+// 0x00000020: input signal is converted to system rate
+// 0x00000040: output is slave to input, ignoring system rate
+// 0x00000060: output is double rate than system rate
+// 0x00000018: nominal rate of sampling clock.
+// 0x00000000: 44.1 kHz
+// 0x00000008: 48.0 kHz
+// 0x00000010: 88.2 kHz
+// 0x00000018: 96.0 kHz
+// 0x00000007: source of sampling clock.
+// 0x00000000: internal
+// 0x00000001: ADAT on optical interface
+// 0x00000002: AES/EBU on XLR
+// 0x00000003: source packet header (SPH)
+// 0x00000004: word clock on BNC
+// 0x00000005: ADAT on Dsub 9pin
+
+#define CLK_896_STATUS_OFFSET 0x0b14
+#define CLK_896_STATUS_FLAG_FETCH_ENABLE 0x20000000
+#define CLK_896_STATUS_FLAG_OUTPUT_ON 0x03000000
+#define CLK_896_STATUS_MASK_SRC 0x00000007
+#define CLK_896_STATUS_FLAG_SRC_INTERNAL 0x00000000
+#define CLK_896_STATUS_FLAG_SRC_ADAT_ON_OPT 0x00000001
+#define CLK_896_STATUS_FLAG_SRC_AESEBU 0x00000002
+#define CLK_896_STATUS_FLAG_SRC_SPH 0x00000003
+#define CLK_896_STATUS_FLAG_SRC_WORD 0x00000004
+#define CLK_896_STATUS_FLAG_SRC_ADAT_ON_DSUB 0x00000005
+#define CLK_896_STATUS_MASK_RATE 0x00000018
+#define CLK_896_STATUS_FLAG_RATE_44100 0x00000000
+#define CLK_896_STATUS_FLAG_RATE_48000 0x00000008
+#define CLK_896_STATUS_FLAG_RATE_88200 0x00000010
+#define CLK_896_STATUS_FLAG_RATE_96000 0x00000018
+
+static void parse_clock_rate_828(u32 data, unsigned int *rate)
+{
+ if (data & CLK_828_STATUS_FLAG_RATE_48000)
+ *rate = 48000;
+ else
+ *rate = 44100;
+}
+
+static int get_clock_rate_828(struct snd_motu *motu, unsigned int *rate)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ parse_clock_rate_828(be32_to_cpu(reg), rate);
+
+ return 0;
+}
+
+static int parse_clock_rate_896(u32 data, unsigned int *rate)
+{
+ switch (data & CLK_896_STATUS_MASK_RATE) {
+ case CLK_896_STATUS_FLAG_RATE_44100:
+ *rate = 44100;
+ break;
+ case CLK_896_STATUS_FLAG_RATE_48000:
+ *rate = 48000;
+ break;
+ case CLK_896_STATUS_FLAG_RATE_88200:
+ *rate = 88200;
+ break;
+ case CLK_896_STATUS_FLAG_RATE_96000:
+ *rate = 96000;
+ break;
+ default:
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int get_clock_rate_896(struct snd_motu *motu, unsigned int *rate)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ return parse_clock_rate_896(be32_to_cpu(reg), rate);
+}
+
+int snd_motu_protocol_v1_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
+{
+ if (motu->spec == &snd_motu_spec_828)
+ return get_clock_rate_828(motu, rate);
+ else if (motu->spec == &snd_motu_spec_896)
+ return get_clock_rate_896(motu, rate);
+ else
+ return -ENXIO;
+}
+
+static int set_clock_rate_828(struct snd_motu *motu, unsigned int rate)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;
+
+ data &= ~CLK_828_STATUS_FLAG_RATE_48000;
+ if (rate == 48000)
+ data |= CLK_828_STATUS_FLAG_RATE_48000;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+}
+
+static int set_clock_rate_896(struct snd_motu *motu, unsigned int rate)
+{
+ unsigned int flag;
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ switch (rate) {
+ case 44100:
+ flag = CLK_896_STATUS_FLAG_RATE_44100;
+ break;
+ case 48000:
+ flag = CLK_896_STATUS_FLAG_RATE_48000;
+ break;
+ case 88200:
+ flag = CLK_896_STATUS_FLAG_RATE_88200;
+ break;
+ case 96000:
+ flag = CLK_896_STATUS_FLAG_RATE_96000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ data &= ~CLK_896_STATUS_MASK_RATE;
+ data |= flag;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
+}
+
+int snd_motu_protocol_v1_set_clock_rate(struct snd_motu *motu, unsigned int rate)
+{
+ if (motu->spec == &snd_motu_spec_828)
+ return set_clock_rate_828(motu, rate);
+ else if (motu->spec == &snd_motu_spec_896)
+ return set_clock_rate_896(motu, rate);
+ else
+ return -ENXIO;
+}
+
+static int get_clock_source_828(struct snd_motu *motu, enum snd_motu_clock_source *src)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;
+
+ switch (data & CLK_828_STATUS_MASK_SRC) {
+ case CLK_828_STATUS_FLAG_SRC_ADAT_ON_OPT:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
+ break;
+ case CLK_828_STATUS_FLAG_SRC_SPH:
+ *src = SND_MOTU_CLOCK_SOURCE_SPH;
+ break;
+ case CLK_828_STATUS_FLAG_SRC_SPDIF:
+ {
+ if (data & CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF)
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+ else
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
+ break;
+ }
+ case CLK_828_STATUS_FLAG_SRC_ADAT_ON_DSUB:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
+ break;
+ case CLK_828_STATUS_FLAG_SRC_INTERNAL:
+ *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
+ break;
+ default:
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int get_clock_source_896(struct snd_motu *motu, enum snd_motu_clock_source *src)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ switch (data & CLK_896_STATUS_MASK_SRC) {
+ case CLK_896_STATUS_FLAG_SRC_INTERNAL:
+ *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
+ break;
+ case CLK_896_STATUS_FLAG_SRC_ADAT_ON_OPT:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
+ break;
+ case CLK_896_STATUS_FLAG_SRC_AESEBU:
+ *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
+ break;
+ case CLK_896_STATUS_FLAG_SRC_SPH:
+ *src = SND_MOTU_CLOCK_SOURCE_SPH;
+ break;
+ case CLK_896_STATUS_FLAG_SRC_WORD:
+ *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
+ break;
+ case CLK_896_STATUS_FLAG_SRC_ADAT_ON_DSUB:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
+ break;
+ default:
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+int snd_motu_protocol_v1_get_clock_source(struct snd_motu *motu, enum snd_motu_clock_source *src)
+{
+ if (motu->spec == &snd_motu_spec_828)
+ return get_clock_source_828(motu, src);
+ else if (motu->spec == &snd_motu_spec_896)
+ return get_clock_source_896(motu, src);
+ else
+ return -ENXIO;
+}
+
+static int switch_fetching_mode_828(struct snd_motu *motu, bool enable)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;
+
+ data &= ~(CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES | CLK_828_STATUS_FLAG_ENABLE_OUTPUT);
+ if (enable) {
+ // This transaction should be initiated after the device receives batch of packets
+ // since the device voluntarily mutes outputs. As a workaround, yield processor over
+ // 100 msec.
+ msleep(100);
+ data |= CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES | CLK_828_STATUS_FLAG_ENABLE_OUTPUT;
+ }
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+}
+
+static int switch_fetching_mode_896(struct snd_motu *motu, bool enable)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ data &= ~CLK_896_STATUS_FLAG_FETCH_ENABLE;
+ if (enable)
+ data |= CLK_896_STATUS_FLAG_FETCH_ENABLE | CLK_896_STATUS_FLAG_OUTPUT_ON;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
+}
+
+int snd_motu_protocol_v1_switch_fetching_mode(struct snd_motu *motu, bool enable)
+{
+ if (motu->spec == &snd_motu_spec_828)
+ return switch_fetching_mode_828(motu, enable);
+ else if (motu->spec == &snd_motu_spec_896)
+ return switch_fetching_mode_896(motu, enable);
+ else
+ return -ENXIO;
+}
+
+static int detect_packet_formats_828(struct snd_motu *motu)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ motu->tx_packet_formats.pcm_byte_offset = 4;
+ motu->tx_packet_formats.msg_chunks = 2;
+
+ motu->rx_packet_formats.pcm_byte_offset = 4;
+ motu->rx_packet_formats.msg_chunks = 0;
+
+ err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;
+
+ // The number of chunks is just reduced when SPDIF is activated.
+ if (!(data & CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF))
+ motu->tx_packet_formats.pcm_chunks[0] += 8;
+
+ if (!(data & CLK_828_STATUS_FLAG_OPT_OUT_IFACE_IS_SPDIF))
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+
+ return 0;
+}
+
+static int detect_packet_formats_896(struct snd_motu *motu)
+{
+ // 24bit PCM frames follow to source packet header without message chunk.
+ motu->tx_packet_formats.pcm_byte_offset = 4;
+ motu->rx_packet_formats.pcm_byte_offset = 4;
+
+ // No message chunk in data block.
+ motu->tx_packet_formats.msg_chunks = 0;
+ motu->rx_packet_formats.msg_chunks = 0;
+
+ // Always enable optical interface for ADAT signal since the device have no registers
+ // to refer to current configuration.
+ motu->tx_packet_formats.pcm_chunks[0] += 8;
+ motu->tx_packet_formats.pcm_chunks[1] += 8;
+
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+ motu->rx_packet_formats.pcm_chunks[1] += 8;
+
+ return 0;
+}
+
+int snd_motu_protocol_v1_cache_packet_formats(struct snd_motu *motu)
+{
+ memcpy(motu->tx_packet_formats.pcm_chunks, motu->spec->tx_fixed_pcm_chunks,
+ sizeof(motu->tx_packet_formats.pcm_chunks));
+ memcpy(motu->rx_packet_formats.pcm_chunks, motu->spec->rx_fixed_pcm_chunks,
+ sizeof(motu->rx_packet_formats.pcm_chunks));
+
+ if (motu->spec == &snd_motu_spec_828)
+ return detect_packet_formats_828(motu);
+ else if (motu->spec == &snd_motu_spec_896)
+ return detect_packet_formats_896(motu);
+ else
+ return 0;
+}
+
+const struct snd_motu_spec snd_motu_spec_828 = {
+ .name = "828",
+ .protocol_version = SND_MOTU_PROTOCOL_V1,
+ .tx_fixed_pcm_chunks = {10, 0, 0},
+ .rx_fixed_pcm_chunks = {10, 0, 0},
+};
+
+const struct snd_motu_spec snd_motu_spec_896 = {
+ .name = "896",
+ .tx_fixed_pcm_chunks = {10, 10, 0},
+ .rx_fixed_pcm_chunks = {10, 10, 0},
+};
diff --git a/sound/firewire/motu/motu-protocol-v2.c b/sound/firewire/motu/motu-protocol-v2.c
new file mode 100644
index 000000000000..a5f70efa2e88
--- /dev/null
+++ b/sound/firewire/motu/motu-protocol-v2.c
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * motu-protocol-v2.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include "motu.h"
+
+#define V2_CLOCK_STATUS_OFFSET 0x0b14
+#define V2_CLOCK_RATE_MASK 0x00000038
+#define V2_CLOCK_RATE_SHIFT 3
+#define V2_CLOCK_SRC_MASK 0x00000007
+#define V2_CLOCK_SRC_SHIFT 0
+#define V2_CLOCK_SRC_AESEBU_ON_XLR 0x07 // In Traveler.
+#define V2_CLOCK_SRC_ADAT_ON_DSUB 0x05
+#define V2_CLOCK_SRC_WORD_ON_BNC 0x04
+#define V2_CLOCK_SRC_SPH 0x03
+#define V2_CLOCK_SRC_SPDIF 0x02 // on either coaxial or optical. AES/EBU in 896HD.
+#define V2_CLOCK_SRC_ADAT_ON_OPT 0x01
+#define V2_CLOCK_SRC_INTERNAL 0x00
+#define V2_CLOCK_FETCH_ENABLE 0x02000000
+#define V2_CLOCK_MODEL_SPECIFIC 0x04000000
+
+#define V2_IN_OUT_CONF_OFFSET 0x0c04
+#define V2_OPT_OUT_IFACE_MASK 0x00000c00
+#define V2_OPT_OUT_IFACE_SHIFT 10
+#define V2_OPT_IN_IFACE_MASK 0x00000300
+#define V2_OPT_IN_IFACE_SHIFT 8
+#define V2_OPT_IFACE_MODE_NONE 0
+#define V2_OPT_IFACE_MODE_ADAT 1
+#define V2_OPT_IFACE_MODE_SPDIF 2
+
+static int get_clock_rate(u32 data, unsigned int *rate)
+{
+ unsigned int index = (data & V2_CLOCK_RATE_MASK) >> V2_CLOCK_RATE_SHIFT;
+ if (index >= ARRAY_SIZE(snd_motu_clock_rates))
+ return -EIO;
+
+ *rate = snd_motu_clock_rates[index];
+
+ return 0;
+}
+
+int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu,
+ unsigned int *rate)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+
+ return get_clock_rate(be32_to_cpu(reg), rate);
+}
+
+int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate)
+{
+ __be32 reg;
+ u32 data;
+ int i;
+ int err;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ if (snd_motu_clock_rates[i] == rate)
+ break;
+ }
+ if (i == ARRAY_SIZE(snd_motu_clock_rates))
+ return -EINVAL;
+
+ err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ data &= ~V2_CLOCK_RATE_MASK;
+ data |= i << V2_CLOCK_RATE_SHIFT;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+}
+
+static int get_clock_source(struct snd_motu *motu, u32 data,
+ enum snd_motu_clock_source *src)
+{
+ switch (data & V2_CLOCK_SRC_MASK) {
+ case V2_CLOCK_SRC_INTERNAL:
+ *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
+ break;
+ case V2_CLOCK_SRC_ADAT_ON_OPT:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
+ break;
+ case V2_CLOCK_SRC_SPDIF:
+ {
+ bool support_iec60958_on_opt = (motu->spec == &snd_motu_spec_828mk2 ||
+ motu->spec == &snd_motu_spec_traveler);
+
+ if (motu->spec == &snd_motu_spec_896hd) {
+ *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
+ } else if (!support_iec60958_on_opt) {
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+ } else {
+ __be32 reg;
+
+ // To check the configuration of optical interface.
+ int err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+
+ if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==
+ V2_OPT_IFACE_MODE_SPDIF)
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
+ else
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+ }
+ break;
+ }
+ case V2_CLOCK_SRC_SPH:
+ *src = SND_MOTU_CLOCK_SOURCE_SPH;
+ break;
+ case V2_CLOCK_SRC_WORD_ON_BNC:
+ *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
+ break;
+ case V2_CLOCK_SRC_ADAT_ON_DSUB:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
+ break;
+ case V2_CLOCK_SRC_AESEBU_ON_XLR:
+ // For Traveler.
+ *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
+ break;
+ default:
+ *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
+
+int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+
+ return get_clock_source(motu, be32_to_cpu(reg), src);
+}
+
+// Expected for Traveler, which implements Altera Cyclone EP1C3.
+static int switch_fetching_mode_cyclone(struct snd_motu *motu, u32 *data,
+ bool enable)
+{
+ *data |= V2_CLOCK_MODEL_SPECIFIC;
+
+ return 0;
+}
+
+// For UltraLite and 8pre, which implements Xilinx Spartan XC3S200.
+static int switch_fetching_mode_spartan(struct snd_motu *motu, u32 *data,
+ bool enable)
+{
+ unsigned int rate;
+ enum snd_motu_clock_source src;
+ int err;
+
+ err = get_clock_source(motu, *data, &src);
+ if (err < 0)
+ return err;
+
+ err = get_clock_rate(*data, &rate);
+ if (err < 0)
+ return err;
+
+ if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000)
+ *data |= V2_CLOCK_MODEL_SPECIFIC;
+
+ return 0;
+}
+
+int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu,
+ bool enable)
+{
+ if (motu->spec == &snd_motu_spec_828mk2) {
+ // 828mkII implements Altera ACEX 1K EP1K30. Nothing to do.
+ return 0;
+ } else if (motu->spec == &snd_motu_spec_896hd) {
+ // 896HD implements Altera Cyclone EP1C3 but nothing to do.
+ return 0;
+ } else {
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET,
+ &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC);
+ if (enable)
+ data |= V2_CLOCK_FETCH_ENABLE;
+
+ if (motu->spec == &snd_motu_spec_traveler)
+ err = switch_fetching_mode_cyclone(motu, &data, enable);
+ else
+ err = switch_fetching_mode_spartan(motu, &data, enable);
+ if (err < 0)
+ return err;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET,
+ &reg, sizeof(reg));
+ }
+}
+
+int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu)
+{
+ bool has_two_opt_ifaces = (motu->spec == &snd_motu_spec_8pre);
+ __be32 reg;
+ u32 data;
+ int err;
+
+ motu->tx_packet_formats.pcm_byte_offset = 10;
+ motu->rx_packet_formats.pcm_byte_offset = 10;
+
+ motu->tx_packet_formats.msg_chunks = 2;
+ motu->rx_packet_formats.msg_chunks = 2;
+
+ err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ memcpy(motu->tx_packet_formats.pcm_chunks,
+ motu->spec->tx_fixed_pcm_chunks,
+ sizeof(motu->tx_packet_formats.pcm_chunks));
+ memcpy(motu->rx_packet_formats.pcm_chunks,
+ motu->spec->rx_fixed_pcm_chunks,
+ sizeof(motu->rx_packet_formats.pcm_chunks));
+
+ if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == V2_OPT_IFACE_MODE_ADAT) {
+ motu->tx_packet_formats.pcm_chunks[0] += 8;
+
+ if (!has_two_opt_ifaces)
+ motu->tx_packet_formats.pcm_chunks[1] += 4;
+ else
+ motu->tx_packet_formats.pcm_chunks[1] += 8;
+ }
+
+ if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) == V2_OPT_IFACE_MODE_ADAT) {
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+
+ if (!has_two_opt_ifaces)
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
+ else
+ motu->rx_packet_formats.pcm_chunks[1] += 8;
+ }
+
+ return 0;
+}
+
+const struct snd_motu_spec snd_motu_spec_828mk2 = {
+ .name = "828mk2",
+ .protocol_version = SND_MOTU_PROTOCOL_V2,
+ .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
+ .tx_fixed_pcm_chunks = {14, 14, 0},
+ .rx_fixed_pcm_chunks = {14, 14, 0},
+};
+
+const struct snd_motu_spec snd_motu_spec_896hd = {
+ .name = "896HD",
+ .protocol_version = SND_MOTU_PROTOCOL_V2,
+ .flags = SND_MOTU_SPEC_REGISTER_DSP,
+ .tx_fixed_pcm_chunks = {14, 14, 8},
+ .rx_fixed_pcm_chunks = {14, 14, 8},
+};
+
+const struct snd_motu_spec snd_motu_spec_traveler = {
+ .name = "Traveler",
+ .protocol_version = SND_MOTU_PROTOCOL_V2,
+ .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
+ .tx_fixed_pcm_chunks = {14, 14, 8},
+ .rx_fixed_pcm_chunks = {14, 14, 8},
+};
+
+const struct snd_motu_spec snd_motu_spec_ultralite = {
+ .name = "UltraLite",
+ .protocol_version = SND_MOTU_PROTOCOL_V2,
+ .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
+ .tx_fixed_pcm_chunks = {14, 14, 0},
+ .rx_fixed_pcm_chunks = {14, 14, 0},
+};
+
+const struct snd_motu_spec snd_motu_spec_8pre = {
+ .name = "8pre",
+ .protocol_version = SND_MOTU_PROTOCOL_V2,
+ .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
+ // Two dummy chunks always in the end of data block.
+ .tx_fixed_pcm_chunks = {10, 10, 0},
+ .rx_fixed_pcm_chunks = {6, 6, 0},
+};
diff --git a/sound/firewire/motu/motu-protocol-v3.c b/sound/firewire/motu/motu-protocol-v3.c
new file mode 100644
index 000000000000..7254fdfe046a
--- /dev/null
+++ b/sound/firewire/motu/motu-protocol-v3.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * motu-protocol-v3.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include <linux/delay.h>
+#include "motu.h"
+
+#define V3_CLOCK_STATUS_OFFSET 0x0b14
+#define V3_FETCH_PCM_FRAMES 0x02000000
+#define V3_CLOCK_RATE_MASK 0x0000ff00
+#define V3_CLOCK_RATE_SHIFT 8
+#define V3_CLOCK_SOURCE_MASK 0x000000ff
+#define V3_CLOCK_SRC_INTERNAL 0x00
+#define V3_CLOCK_SRC_WORD_ON_BNC 0x01
+#define V3_CLOCK_SRC_SPH 0x02
+#define V3_CLOCK_SRC_AESEBU_ON_XLR 0x08
+#define V3_CLOCK_SRC_SPDIF_ON_COAX 0x10
+#define V3_CLOCK_SRC_OPT_IFACE_A 0x18
+#define V3_CLOCK_SRC_OPT_IFACE_B 0x19
+
+#define V3_OPT_IFACE_MODE_OFFSET 0x0c94
+#define V3_ENABLE_OPT_IN_IFACE_A 0x00000001
+#define V3_ENABLE_OPT_IN_IFACE_B 0x00000002
+#define V3_ENABLE_OPT_OUT_IFACE_A 0x00000100
+#define V3_ENABLE_OPT_OUT_IFACE_B 0x00000200
+#define V3_NO_ADAT_OPT_IN_IFACE_A 0x00010000
+#define V3_NO_ADAT_OPT_IN_IFACE_B 0x00100000
+#define V3_NO_ADAT_OPT_OUT_IFACE_A 0x00040000
+#define V3_NO_ADAT_OPT_OUT_IFACE_B 0x00400000
+
+#define V3_MSG_FLAG_CLK_CHANGED 0x00000002
+#define V3_CLK_WAIT_MSEC 4000
+
+int snd_motu_protocol_v3_get_clock_rate(struct snd_motu *motu,
+ unsigned int *rate)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ data = (data & V3_CLOCK_RATE_MASK) >> V3_CLOCK_RATE_SHIFT;
+ if (data >= ARRAY_SIZE(snd_motu_clock_rates))
+ return -EIO;
+
+ *rate = snd_motu_clock_rates[data];
+
+ return 0;
+}
+
+int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate)
+{
+ __be32 reg;
+ u32 data;
+ bool need_to_wait;
+ int i, err;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ if (snd_motu_clock_rates[i] == rate)
+ break;
+ }
+ if (i == ARRAY_SIZE(snd_motu_clock_rates))
+ return -EINVAL;
+
+ err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ data &= ~(V3_CLOCK_RATE_MASK | V3_FETCH_PCM_FRAMES);
+ data |= i << V3_CLOCK_RATE_SHIFT;
+
+ need_to_wait = data != be32_to_cpu(reg);
+
+ reg = cpu_to_be32(data);
+ err = snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+
+ if (need_to_wait) {
+ int result;
+
+ motu->msg = 0;
+ result = wait_event_interruptible_timeout(motu->hwdep_wait,
+ motu->msg & V3_MSG_FLAG_CLK_CHANGED,
+ msecs_to_jiffies(V3_CLK_WAIT_MSEC));
+ if (result < 0)
+ return result;
+ if (result == 0)
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & V3_CLOCK_SOURCE_MASK;
+
+ switch (data) {
+ case V3_CLOCK_SRC_INTERNAL:
+ *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
+ break;
+ case V3_CLOCK_SRC_WORD_ON_BNC:
+ *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
+ break;
+ case V3_CLOCK_SRC_SPH:
+ *src = SND_MOTU_CLOCK_SOURCE_SPH;
+ break;
+ case V3_CLOCK_SRC_AESEBU_ON_XLR:
+ *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
+ break;
+ case V3_CLOCK_SRC_SPDIF_ON_COAX:
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+ break;
+ case V3_CLOCK_SRC_OPT_IFACE_A:
+ case V3_CLOCK_SRC_OPT_IFACE_B:
+ {
+ __be32 reg;
+ u32 options;
+
+ err = snd_motu_transaction_read(motu,
+ V3_OPT_IFACE_MODE_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ options = be32_to_cpu(reg);
+
+ if (data == V3_CLOCK_SRC_OPT_IFACE_A) {
+ if (options & V3_NO_ADAT_OPT_IN_IFACE_A)
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A;
+ else
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A;
+ } else {
+ if (options & V3_NO_ADAT_OPT_IN_IFACE_B)
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B;
+ else
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B;
+ }
+ break;
+ }
+ default:
+ *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
+
+int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu,
+ bool enable)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return 0;
+ data = be32_to_cpu(reg);
+
+ if (enable)
+ data |= V3_FETCH_PCM_FRAMES;
+ else
+ data &= ~V3_FETCH_PCM_FRAMES;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+}
+
+static int detect_packet_formats_with_opt_ifaces(struct snd_motu *motu, u32 data)
+{
+ if (data & V3_ENABLE_OPT_IN_IFACE_A) {
+ if (data & V3_NO_ADAT_OPT_IN_IFACE_A) {
+ motu->tx_packet_formats.pcm_chunks[0] += 4;
+ motu->tx_packet_formats.pcm_chunks[1] += 4;
+ } else {
+ motu->tx_packet_formats.pcm_chunks[0] += 8;
+ motu->tx_packet_formats.pcm_chunks[1] += 4;
+ }
+ }
+
+ if (data & V3_ENABLE_OPT_IN_IFACE_B) {
+ if (data & V3_NO_ADAT_OPT_IN_IFACE_B) {
+ motu->tx_packet_formats.pcm_chunks[0] += 4;
+ motu->tx_packet_formats.pcm_chunks[1] += 4;
+ } else {
+ motu->tx_packet_formats.pcm_chunks[0] += 8;
+ motu->tx_packet_formats.pcm_chunks[1] += 4;
+ }
+ }
+
+ if (data & V3_ENABLE_OPT_OUT_IFACE_A) {
+ if (data & V3_NO_ADAT_OPT_OUT_IFACE_A) {
+ motu->rx_packet_formats.pcm_chunks[0] += 4;
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
+ } else {
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
+ }
+ }
+
+ if (data & V3_ENABLE_OPT_OUT_IFACE_B) {
+ if (data & V3_NO_ADAT_OPT_OUT_IFACE_B) {
+ motu->rx_packet_formats.pcm_chunks[0] += 4;
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
+ } else {
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
+ }
+ }
+
+ return 0;
+}
+
+int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ motu->tx_packet_formats.pcm_byte_offset = 10;
+ motu->rx_packet_formats.pcm_byte_offset = 10;
+
+ motu->tx_packet_formats.msg_chunks = 2;
+ motu->rx_packet_formats.msg_chunks = 2;
+
+ err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ memcpy(motu->tx_packet_formats.pcm_chunks,
+ motu->spec->tx_fixed_pcm_chunks,
+ sizeof(motu->tx_packet_formats.pcm_chunks));
+ memcpy(motu->rx_packet_formats.pcm_chunks,
+ motu->spec->rx_fixed_pcm_chunks,
+ sizeof(motu->rx_packet_formats.pcm_chunks));
+
+ if (motu->spec == &snd_motu_spec_828mk3_fw ||
+ motu->spec == &snd_motu_spec_828mk3_hybrid ||
+ motu->spec == &snd_motu_spec_896mk3 ||
+ motu->spec == &snd_motu_spec_traveler_mk3 ||
+ motu->spec == &snd_motu_spec_track16)
+ return detect_packet_formats_with_opt_ifaces(motu, data);
+ else
+ return 0;
+}
+
+const struct snd_motu_spec snd_motu_spec_828mk3_fw = {
+ .name = "828mk3",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_COMMAND_DSP,
+ .tx_fixed_pcm_chunks = {18, 18, 14},
+ .rx_fixed_pcm_chunks = {14, 14, 10},
+};
+
+const struct snd_motu_spec snd_motu_spec_828mk3_hybrid = {
+ .name = "828mk3",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_COMMAND_DSP,
+ .tx_fixed_pcm_chunks = {18, 18, 14},
+ .rx_fixed_pcm_chunks = {14, 14, 14}, // Additional 4 dummy chunks at higher rate.
+};
+
+const struct snd_motu_spec snd_motu_spec_896mk3 = {
+ .name = "896mk3",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_COMMAND_DSP,
+ .tx_fixed_pcm_chunks = {18, 14, 10},
+ .rx_fixed_pcm_chunks = {18, 14, 10},
+};
+
+const struct snd_motu_spec snd_motu_spec_traveler_mk3 = {
+ .name = "TravelerMk3",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_COMMAND_DSP,
+ .tx_fixed_pcm_chunks = {18, 14, 10},
+ .rx_fixed_pcm_chunks = {14, 14, 10},
+};
+
+const struct snd_motu_spec snd_motu_spec_ultralite_mk3 = {
+ .name = "UltraLiteMk3",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_COMMAND_DSP,
+ .tx_fixed_pcm_chunks = {18, 14, 10},
+ .rx_fixed_pcm_chunks = {14, 14, 14},
+};
+
+const struct snd_motu_spec snd_motu_spec_audio_express = {
+ .name = "AudioExpress",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
+ .tx_fixed_pcm_chunks = {10, 10, 0},
+ .rx_fixed_pcm_chunks = {10, 10, 0},
+};
+
+const struct snd_motu_spec snd_motu_spec_track16 = {
+ .name = "Track16",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_COMMAND_DSP,
+ .tx_fixed_pcm_chunks = {14, 14, 14},
+ .rx_fixed_pcm_chunks = {6, 6, 6},
+};
+
+const struct snd_motu_spec snd_motu_spec_4pre = {
+ .name = "4pre",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_REGISTER_DSP,
+ .tx_fixed_pcm_chunks = {10, 10, 0},
+ .rx_fixed_pcm_chunks = {10, 10, 0},
+};
diff --git a/sound/firewire/motu/motu-register-dsp-message-parser.c b/sound/firewire/motu/motu-register-dsp-message-parser.c
new file mode 100644
index 000000000000..a8053e3ef065
--- /dev/null
+++ b/sound/firewire/motu/motu-register-dsp-message-parser.c
@@ -0,0 +1,413 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// motu-register-dsp-message-parser.c - a part of driver for MOTU FireWire series
+//
+// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+
+// Below models allow software to configure their DSP functions by asynchronous transaction
+// to access their internal registers.
+// * 828 mk2
+// * 896hd
+// * Traveler
+// * 8 pre
+// * Ultralite
+// * 4 pre
+// * Audio Express
+//
+// Additionally, isochronous packets from the above models include messages to notify state of
+// DSP. The messages are two set of 3 byte data in 2nd and 3rd quadlet of data block. When user
+// operates hardware components such as dial and switch, corresponding messages are transferred.
+// The messages include Hardware metering and MIDI messages as well.
+
+#include "motu.h"
+
+#define MSG_FLAG_POS 4
+#define MSG_FLAG_TYPE_MASK 0xf8
+#define MSG_FLAG_MIDI_MASK 0x01
+#define MSG_FLAG_MODEL_SPECIFIC_MASK 0x06
+#define MSG_FLAG_8PRE 0x00
+#define MSG_FLAG_ULTRALITE 0x04
+#define MSG_FLAG_TRAVELER 0x04
+#define MSG_FLAG_828MK2 0x04
+#define MSG_FLAG_896HD 0x04
+#define MSG_FLAG_4PRE 0x05 // MIDI mask is in 8th byte.
+#define MSG_FLAG_AUDIOEXPRESS 0x05 // MIDI mask is in 8th byte.
+#define MSG_FLAG_TYPE_SHIFT 3
+#define MSG_VALUE_POS 5
+#define MSG_MIDI_BYTE_POS 6
+#define MSG_METER_IDX_POS 7
+
+// In 4 pre and Audio express, meter index is in 6th byte. MIDI flag is in 8th byte and MIDI byte
+// is in 7th byte.
+#define MSG_METER_IDX_POS_4PRE_AE 6
+#define MSG_MIDI_BYTE_POS_4PRE_AE 7
+#define MSG_FLAG_MIDI_POS_4PRE_AE 8
+
+enum register_dsp_msg_type {
+ // Used for messages with no information.
+ INVALID = 0x00,
+ MIXER_SELECT = 0x01,
+ MIXER_SRC_GAIN = 0x02,
+ MIXER_SRC_PAN = 0x03,
+ MIXER_SRC_FLAG = 0x04,
+ MIXER_OUTPUT_PAIRED_VOLUME = 0x05,
+ MIXER_OUTPUT_PAIRED_FLAG = 0x06,
+ MAIN_OUTPUT_PAIRED_VOLUME = 0x07,
+ HP_OUTPUT_PAIRED_VOLUME = 0x08,
+ HP_OUTPUT_PAIRED_ASSIGNMENT = 0x09,
+ // Transferred by all models but the purpose is still unknown.
+ UNKNOWN_0 = 0x0a,
+ // Specific to 828mk2, 896hd, Traveler.
+ UNKNOWN_2 = 0x0c,
+ // Specific to 828mk2, Traveler, and 896hd (not functional).
+ LINE_INPUT_BOOST = 0x0d,
+ // Specific to 828mk2, Traveler, and 896hd (not functional).
+ LINE_INPUT_NOMINAL_LEVEL = 0x0e,
+ // Specific to Ultralite, 4 pre, Audio express, and 8 pre (not functional).
+ INPUT_GAIN_AND_INVERT = 0x15,
+ // Specific to 4 pre, and Audio express.
+ INPUT_FLAG = 0x16,
+ // Specific to 4 pre, and Audio express.
+ MIXER_SRC_PAIRED_BALANCE = 0x17,
+ // Specific to 4 pre, and Audio express.
+ MIXER_SRC_PAIRED_WIDTH = 0x18,
+ // Transferred by all models. This type of message interposes the series of the other
+ // messages. The message delivers signal level up to 96.0 kHz. In 828mk2, 896hd, and
+ // Traveler, one of physical outputs is selected for the message. The selection is done
+ // by LSB one byte in asynchronous write quadlet transaction to 0x'ffff'f000'0b2c.
+ METER = 0x1f,
+};
+
+#define EVENT_QUEUE_SIZE 16
+
+struct msg_parser {
+ spinlock_t lock;
+ struct snd_firewire_motu_register_dsp_meter meter;
+ bool meter_pos_quirk;
+
+ struct snd_firewire_motu_register_dsp_parameter param;
+ u8 prev_mixer_src_type;
+ u8 mixer_ch;
+ u8 mixer_src_ch;
+
+ u8 input_ch;
+ u8 prev_msg_type;
+
+ u32 event_queue[EVENT_QUEUE_SIZE];
+ unsigned int push_pos;
+ unsigned int pull_pos;
+};
+
+int snd_motu_register_dsp_message_parser_new(struct snd_motu *motu)
+{
+ struct msg_parser *parser;
+ parser = devm_kzalloc(&motu->card->card_dev, sizeof(*parser), GFP_KERNEL);
+ if (!parser)
+ return -ENOMEM;
+ spin_lock_init(&parser->lock);
+ if (motu->spec == &snd_motu_spec_4pre || motu->spec == &snd_motu_spec_audio_express)
+ parser->meter_pos_quirk = true;
+ motu->message_parser = parser;
+ return 0;
+}
+
+int snd_motu_register_dsp_message_parser_init(struct snd_motu *motu)
+{
+ struct msg_parser *parser = motu->message_parser;
+
+ parser->prev_mixer_src_type = INVALID;
+ parser->mixer_ch = 0xff;
+ parser->mixer_src_ch = 0xff;
+ parser->prev_msg_type = INVALID;
+
+ return 0;
+}
+
+// Rough implementaion of queue without overrun check.
+static void queue_event(struct snd_motu *motu, u8 msg_type, u8 identifier0, u8 identifier1, u8 val)
+{
+ struct msg_parser *parser = motu->message_parser;
+ unsigned int pos = parser->push_pos;
+ u32 entry;
+
+ if (!motu->hwdep || motu->hwdep->used == 0)
+ return;
+
+ entry = (msg_type << 24) | (identifier0 << 16) | (identifier1 << 8) | val;
+ parser->event_queue[pos] = entry;
+
+ ++pos;
+ if (pos >= EVENT_QUEUE_SIZE)
+ pos = 0;
+ parser->push_pos = pos;
+}
+
+void snd_motu_register_dsp_message_parser_parse(const struct amdtp_stream *s,
+ const struct pkt_desc *desc, unsigned int count)
+{
+ struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream);
+ unsigned int data_block_quadlets = s->data_block_quadlets;
+ struct msg_parser *parser = motu->message_parser;
+ bool meter_pos_quirk = parser->meter_pos_quirk;
+ unsigned int pos = parser->push_pos;
+ int i;
+
+ guard(spinlock_irqsave)(&parser->lock);
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buffer = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+ int j;
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+
+ for (j = 0; j < data_blocks; ++j) {
+ u8 *b = (u8 *)buffer;
+ u8 msg_type = (b[MSG_FLAG_POS] & MSG_FLAG_TYPE_MASK) >> MSG_FLAG_TYPE_SHIFT;
+ u8 val = b[MSG_VALUE_POS];
+
+ buffer += data_block_quadlets;
+
+ switch (msg_type) {
+ case MIXER_SELECT:
+ {
+ u8 mixer_ch = val / 0x20;
+ if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT) {
+ parser->mixer_src_ch = 0;
+ parser->mixer_ch = mixer_ch;
+ }
+ break;
+ }
+ case MIXER_SRC_GAIN:
+ case MIXER_SRC_PAN:
+ case MIXER_SRC_FLAG:
+ case MIXER_SRC_PAIRED_BALANCE:
+ case MIXER_SRC_PAIRED_WIDTH:
+ {
+ struct snd_firewire_motu_register_dsp_parameter *param = &parser->param;
+ u8 mixer_ch = parser->mixer_ch;
+ u8 mixer_src_ch = parser->mixer_src_ch;
+
+ if (msg_type != parser->prev_mixer_src_type)
+ mixer_src_ch = 0;
+ else
+ ++mixer_src_ch;
+ parser->prev_mixer_src_type = msg_type;
+
+ if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT &&
+ mixer_src_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_SRC_COUNT) {
+ u8 mixer_ch = parser->mixer_ch;
+
+ switch (msg_type) {
+ case MIXER_SRC_GAIN:
+ if (param->mixer.source[mixer_ch].gain[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].gain[mixer_src_ch] = val;
+ }
+ break;
+ case MIXER_SRC_PAN:
+ if (param->mixer.source[mixer_ch].pan[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].pan[mixer_src_ch] = val;
+ }
+ break;
+ case MIXER_SRC_FLAG:
+ if (param->mixer.source[mixer_ch].flag[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].flag[mixer_src_ch] = val;
+ }
+ break;
+ case MIXER_SRC_PAIRED_BALANCE:
+ if (param->mixer.source[mixer_ch].paired_balance[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].paired_balance[mixer_src_ch] = val;
+ }
+ break;
+ case MIXER_SRC_PAIRED_WIDTH:
+ if (param->mixer.source[mixer_ch].paired_width[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].paired_width[mixer_src_ch] = val;
+ }
+ break;
+ default:
+ break;
+ }
+
+ parser->mixer_src_ch = mixer_src_ch;
+ }
+ break;
+ }
+ case MIXER_OUTPUT_PAIRED_VOLUME:
+ case MIXER_OUTPUT_PAIRED_FLAG:
+ {
+ struct snd_firewire_motu_register_dsp_parameter *param = &parser->param;
+ u8 mixer_ch = parser->mixer_ch;
+
+ if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT) {
+ switch (msg_type) {
+ case MIXER_OUTPUT_PAIRED_VOLUME:
+ if (param->mixer.output.paired_volume[mixer_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, 0, val);
+ param->mixer.output.paired_volume[mixer_ch] = val;
+ }
+ break;
+ case MIXER_OUTPUT_PAIRED_FLAG:
+ if (param->mixer.output.paired_flag[mixer_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, 0, val);
+ param->mixer.output.paired_flag[mixer_ch] = val;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ case MAIN_OUTPUT_PAIRED_VOLUME:
+ if (parser->param.output.main_paired_volume != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.output.main_paired_volume = val;
+ }
+ break;
+ case HP_OUTPUT_PAIRED_VOLUME:
+ if (parser->param.output.hp_paired_volume != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.output.hp_paired_volume = val;
+ }
+ break;
+ case HP_OUTPUT_PAIRED_ASSIGNMENT:
+ if (parser->param.output.hp_paired_assignment != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.output.hp_paired_assignment = val;
+ }
+ break;
+ case LINE_INPUT_BOOST:
+ if (parser->param.line_input.boost_flag != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.line_input.boost_flag = val;
+ }
+ break;
+ case LINE_INPUT_NOMINAL_LEVEL:
+ if (parser->param.line_input.nominal_level_flag != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.line_input.nominal_level_flag = val;
+ }
+ break;
+ case INPUT_GAIN_AND_INVERT:
+ case INPUT_FLAG:
+ {
+ struct snd_firewire_motu_register_dsp_parameter *param = &parser->param;
+ u8 input_ch = parser->input_ch;
+
+ if (parser->prev_msg_type != msg_type)
+ input_ch = 0;
+ else
+ ++input_ch;
+
+ if (input_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_INPUT_COUNT) {
+ switch (msg_type) {
+ case INPUT_GAIN_AND_INVERT:
+ if (param->input.gain_and_invert[input_ch] != val) {
+ queue_event(motu, msg_type, input_ch, 0, val);
+ param->input.gain_and_invert[input_ch] = val;
+ }
+ break;
+ case INPUT_FLAG:
+ if (param->input.flag[input_ch] != val) {
+ queue_event(motu, msg_type, input_ch, 0, val);
+ param->input.flag[input_ch] = val;
+ }
+ break;
+ default:
+ break;
+ }
+ parser->input_ch = input_ch;
+ }
+ break;
+ }
+ case UNKNOWN_0:
+ case UNKNOWN_2:
+ break;
+ case METER:
+ {
+ u8 pos;
+
+ if (!meter_pos_quirk)
+ pos = b[MSG_METER_IDX_POS];
+ else
+ pos = b[MSG_METER_IDX_POS_4PRE_AE];
+
+ if (pos < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_INPUT_COUNT) {
+ parser->meter.data[pos] = val;
+ } else if (pos >= 0x80) {
+ pos -= (0x80 - SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_INPUT_COUNT);
+
+ if (pos < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_COUNT)
+ parser->meter.data[pos] = val;
+ }
+
+ // The message for meter is interruptible to the series of other
+ // types of messages. Don't cache it.
+ fallthrough;
+ }
+ case INVALID:
+ default:
+ // Don't cache it.
+ continue;
+ }
+
+ parser->prev_msg_type = msg_type;
+ }
+ }
+
+ if (pos != parser->push_pos)
+ wake_up(&motu->hwdep_wait);
+}
+
+void snd_motu_register_dsp_message_parser_copy_meter(struct snd_motu *motu,
+ struct snd_firewire_motu_register_dsp_meter *meter)
+{
+ struct msg_parser *parser = motu->message_parser;
+
+ guard(spinlock_irqsave)(&parser->lock);
+ memcpy(meter, &parser->meter, sizeof(*meter));
+}
+
+void snd_motu_register_dsp_message_parser_copy_parameter(struct snd_motu *motu,
+ struct snd_firewire_motu_register_dsp_parameter *param)
+{
+ struct msg_parser *parser = motu->message_parser;
+
+ guard(spinlock_irqsave)(&parser->lock);
+ memcpy(param, &parser->param, sizeof(*param));
+}
+
+unsigned int snd_motu_register_dsp_message_parser_count_event(struct snd_motu *motu)
+{
+ struct msg_parser *parser = motu->message_parser;
+
+ if (parser->pull_pos > parser->push_pos)
+ return EVENT_QUEUE_SIZE - parser->pull_pos + parser->push_pos;
+ else
+ return parser->push_pos - parser->pull_pos;
+}
+
+bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32 *event)
+{
+ struct msg_parser *parser = motu->message_parser;
+ unsigned int pos = parser->pull_pos;
+
+ if (pos == parser->push_pos)
+ return false;
+
+ guard(spinlock_irqsave)(&parser->lock);
+
+ *event = parser->event_queue[pos];
+
+ ++pos;
+ if (pos >= EVENT_QUEUE_SIZE)
+ pos = 0;
+ parser->pull_pos = pos;
+
+ return true;
+}
diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c
new file mode 100644
index 000000000000..e5f21360cfb7
--- /dev/null
+++ b/sound/firewire/motu/motu-stream.c
@@ -0,0 +1,429 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * motu-stream.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include "motu.h"
+
+#define READY_TIMEOUT_MS 200
+
+#define ISOC_COMM_CONTROL_OFFSET 0x0b00
+#define ISOC_COMM_CONTROL_MASK 0xffff0000
+#define CHANGE_RX_ISOC_COMM_STATE 0x80000000
+#define RX_ISOC_COMM_IS_ACTIVATED 0x40000000
+#define RX_ISOC_COMM_CHANNEL_MASK 0x3f000000
+#define RX_ISOC_COMM_CHANNEL_SHIFT 24
+#define CHANGE_TX_ISOC_COMM_STATE 0x00800000
+#define TX_ISOC_COMM_IS_ACTIVATED 0x00400000
+#define TX_ISOC_COMM_CHANNEL_MASK 0x003f0000
+#define TX_ISOC_COMM_CHANNEL_SHIFT 16
+
+#define PACKET_FORMAT_OFFSET 0x0b10
+#define TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000080
+#define RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000040
+#define TX_PACKET_TRANSMISSION_SPEED_MASK 0x0000000f
+
+static int keep_resources(struct snd_motu *motu, unsigned int rate,
+ struct amdtp_stream *stream)
+{
+ struct fw_iso_resources *resources;
+ struct snd_motu_packet_format *packet_format;
+ unsigned int midi_ports = 0;
+ int err;
+
+ if (stream == &motu->rx_stream) {
+ resources = &motu->rx_resources;
+ packet_format = &motu->rx_packet_formats;
+
+ if ((motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) ||
+ (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q))
+ midi_ports = 1;
+ } else {
+ resources = &motu->tx_resources;
+ packet_format = &motu->tx_packet_formats;
+
+ if ((motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) ||
+ (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q))
+ midi_ports = 1;
+ }
+
+ err = amdtp_motu_set_parameters(stream, rate, midi_ports,
+ packet_format);
+ if (err < 0)
+ return err;
+
+ return fw_iso_resources_allocate(resources,
+ amdtp_stream_get_max_payload(stream),
+ fw_parent_device(motu->unit)->max_speed);
+}
+
+static int begin_session(struct snd_motu *motu)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ // Configure the unit to start isochronous communication.
+ err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & ~ISOC_COMM_CONTROL_MASK;
+
+ data |= CHANGE_RX_ISOC_COMM_STATE | RX_ISOC_COMM_IS_ACTIVATED |
+ (motu->rx_resources.channel << RX_ISOC_COMM_CHANNEL_SHIFT) |
+ CHANGE_TX_ISOC_COMM_STATE | TX_ISOC_COMM_IS_ACTIVATED |
+ (motu->tx_resources.channel << TX_ISOC_COMM_CHANNEL_SHIFT);
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
+ sizeof(reg));
+}
+
+static void finish_session(struct snd_motu *motu)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_protocol_switch_fetching_mode(motu, false);
+ if (err < 0)
+ return;
+
+ err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return;
+ data = be32_to_cpu(reg);
+
+ data &= ~(RX_ISOC_COMM_IS_ACTIVATED | TX_ISOC_COMM_IS_ACTIVATED);
+ data |= CHANGE_RX_ISOC_COMM_STATE | CHANGE_TX_ISOC_COMM_STATE;
+
+ reg = cpu_to_be32(data);
+ snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
+ sizeof(reg));
+}
+
+int snd_motu_stream_cache_packet_formats(struct snd_motu *motu)
+{
+ int err;
+
+ err = snd_motu_protocol_cache_packet_formats(motu);
+ if (err < 0)
+ return err;
+
+ if (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) {
+ motu->tx_packet_formats.midi_flag_offset = 4;
+ motu->tx_packet_formats.midi_byte_offset = 6;
+ } else if (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q) {
+ motu->tx_packet_formats.midi_flag_offset = 8;
+ motu->tx_packet_formats.midi_byte_offset = 7;
+ }
+
+ if (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) {
+ motu->rx_packet_formats.midi_flag_offset = 4;
+ motu->rx_packet_formats.midi_byte_offset = 6;
+ } else if (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q) {
+ motu->rx_packet_formats.midi_flag_offset = 8;
+ motu->rx_packet_formats.midi_byte_offset = 7;
+ }
+
+ return 0;
+}
+
+int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer)
+{
+ unsigned int curr_rate;
+ int err;
+
+ err = snd_motu_protocol_get_clock_rate(motu, &curr_rate);
+ if (err < 0)
+ return err;
+ if (rate == 0)
+ rate = curr_rate;
+
+ if (motu->substreams_counter == 0 || curr_rate != rate) {
+ amdtp_domain_stop(&motu->domain);
+ finish_session(motu);
+
+ fw_iso_resources_free(&motu->tx_resources);
+ fw_iso_resources_free(&motu->rx_resources);
+
+ kfree(motu->cache.event_offsets);
+ motu->cache.event_offsets = NULL;
+
+ err = snd_motu_protocol_set_clock_rate(motu, rate);
+ if (err < 0) {
+ dev_err(&motu->unit->device,
+ "fail to set sampling rate: %d\n", err);
+ return err;
+ }
+
+ err = snd_motu_stream_cache_packet_formats(motu);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(motu, rate, &motu->tx_stream);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(motu, rate, &motu->rx_stream);
+ if (err < 0) {
+ fw_iso_resources_free(&motu->tx_resources);
+ return err;
+ }
+
+ err = amdtp_domain_set_events_per_period(&motu->domain,
+ frames_per_period, frames_per_buffer);
+ if (err < 0) {
+ fw_iso_resources_free(&motu->tx_resources);
+ fw_iso_resources_free(&motu->rx_resources);
+ return err;
+ }
+
+ motu->cache.size = motu->tx_stream.syt_interval * frames_per_buffer;
+ motu->cache.event_offsets = kcalloc(motu->cache.size, sizeof(*motu->cache.event_offsets),
+ GFP_KERNEL);
+ if (!motu->cache.event_offsets) {
+ fw_iso_resources_free(&motu->tx_resources);
+ fw_iso_resources_free(&motu->rx_resources);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static int ensure_packet_formats(struct snd_motu *motu)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, PACKET_FORMAT_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ data &= ~(TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS |
+ RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS|
+ TX_PACKET_TRANSMISSION_SPEED_MASK);
+ if (motu->spec->tx_fixed_pcm_chunks[0] == motu->tx_packet_formats.pcm_chunks[0])
+ data |= TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
+ if (motu->spec->rx_fixed_pcm_chunks[0] == motu->rx_packet_formats.pcm_chunks[0])
+ data |= RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
+ data |= fw_parent_device(motu->unit)->max_speed;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, PACKET_FORMAT_OFFSET, &reg,
+ sizeof(reg));
+}
+
+int snd_motu_stream_start_duplex(struct snd_motu *motu)
+{
+ unsigned int generation = motu->rx_resources.generation;
+ int err = 0;
+
+ if (motu->substreams_counter == 0)
+ return 0;
+
+ if (amdtp_streaming_error(&motu->rx_stream) ||
+ amdtp_streaming_error(&motu->tx_stream)) {
+ amdtp_domain_stop(&motu->domain);
+ finish_session(motu);
+ }
+
+ if (generation != fw_parent_device(motu->unit)->card->generation) {
+ err = fw_iso_resources_update(&motu->rx_resources);
+ if (err < 0)
+ return err;
+
+ err = fw_iso_resources_update(&motu->tx_resources);
+ if (err < 0)
+ return err;
+ }
+
+ if (!amdtp_stream_running(&motu->rx_stream)) {
+ int spd = fw_parent_device(motu->unit)->max_speed;
+
+ err = ensure_packet_formats(motu);
+ if (err < 0)
+ return err;
+
+ if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) {
+ err = snd_motu_register_dsp_message_parser_init(motu);
+ if (err < 0)
+ return err;
+ } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) {
+ err = snd_motu_command_dsp_message_parser_init(motu, motu->tx_stream.sfc);
+ if (err < 0)
+ return err;
+ }
+
+ err = begin_session(motu);
+ if (err < 0) {
+ dev_err(&motu->unit->device,
+ "fail to start isochronous comm: %d\n", err);
+ goto stop_streams;
+ }
+
+ err = amdtp_domain_add_stream(&motu->domain, &motu->tx_stream,
+ motu->tx_resources.channel, spd);
+ if (err < 0)
+ goto stop_streams;
+
+ err = amdtp_domain_add_stream(&motu->domain, &motu->rx_stream,
+ motu->rx_resources.channel, spd);
+ if (err < 0)
+ goto stop_streams;
+
+ motu->cache.tail = 0;
+ motu->cache.tx_cycle_count = UINT_MAX;
+ motu->cache.head = 0;
+ motu->cache.rx_cycle_count = UINT_MAX;
+
+ // NOTE: The device requires both of replay; the sequence of the number of data
+ // blocks per packet, and the sequence of source packet header per data block as
+ // presentation time.
+ err = amdtp_domain_start(&motu->domain, 0, true, false);
+ if (err < 0)
+ goto stop_streams;
+
+ if (!amdtp_domain_wait_ready(&motu->domain, READY_TIMEOUT_MS)) {
+ err = -ETIMEDOUT;
+ goto stop_streams;
+ }
+
+ err = snd_motu_protocol_switch_fetching_mode(motu, true);
+ if (err < 0) {
+ dev_err(&motu->unit->device,
+ "fail to enable frame fetching: %d\n", err);
+ goto stop_streams;
+ }
+ }
+
+ return 0;
+
+stop_streams:
+ amdtp_domain_stop(&motu->domain);
+ finish_session(motu);
+ return err;
+}
+
+void snd_motu_stream_stop_duplex(struct snd_motu *motu)
+{
+ if (motu->substreams_counter == 0) {
+ amdtp_domain_stop(&motu->domain);
+ finish_session(motu);
+
+ fw_iso_resources_free(&motu->tx_resources);
+ fw_iso_resources_free(&motu->rx_resources);
+
+ kfree(motu->cache.event_offsets);
+ motu->cache.event_offsets = NULL;
+ }
+}
+
+static int init_stream(struct snd_motu *motu, struct amdtp_stream *s)
+{
+ struct fw_iso_resources *resources;
+ enum amdtp_stream_direction dir;
+ int err;
+
+ if (s == &motu->tx_stream) {
+ resources = &motu->tx_resources;
+ dir = AMDTP_IN_STREAM;
+ } else {
+ resources = &motu->rx_resources;
+ dir = AMDTP_OUT_STREAM;
+ }
+
+ err = fw_iso_resources_init(resources, motu->unit);
+ if (err < 0)
+ return err;
+
+ err = amdtp_motu_init(s, motu->unit, dir, motu->spec, &motu->cache);
+ if (err < 0)
+ fw_iso_resources_destroy(resources);
+
+ return err;
+}
+
+static void destroy_stream(struct snd_motu *motu, struct amdtp_stream *s)
+{
+ amdtp_stream_destroy(s);
+
+ if (s == &motu->tx_stream)
+ fw_iso_resources_destroy(&motu->tx_resources);
+ else
+ fw_iso_resources_destroy(&motu->rx_resources);
+}
+
+int snd_motu_stream_init_duplex(struct snd_motu *motu)
+{
+ int err;
+
+ err = init_stream(motu, &motu->tx_stream);
+ if (err < 0)
+ return err;
+
+ err = init_stream(motu, &motu->rx_stream);
+ if (err < 0) {
+ destroy_stream(motu, &motu->tx_stream);
+ return err;
+ }
+
+ err = amdtp_domain_init(&motu->domain);
+ if (err < 0) {
+ destroy_stream(motu, &motu->tx_stream);
+ destroy_stream(motu, &motu->rx_stream);
+ }
+
+ return err;
+}
+
+// This function should be called before starting streams or after stopping
+// streams.
+void snd_motu_stream_destroy_duplex(struct snd_motu *motu)
+{
+ amdtp_domain_destroy(&motu->domain);
+
+ destroy_stream(motu, &motu->rx_stream);
+ destroy_stream(motu, &motu->tx_stream);
+
+ motu->substreams_counter = 0;
+}
+
+static void motu_lock_changed(struct snd_motu *motu)
+{
+ motu->dev_lock_changed = true;
+ wake_up(&motu->hwdep_wait);
+}
+
+int snd_motu_stream_lock_try(struct snd_motu *motu)
+{
+ guard(spinlock_irq)(&motu->lock);
+
+ if (motu->dev_lock_count < 0)
+ return -EBUSY;
+
+ if (motu->dev_lock_count++ == 0)
+ motu_lock_changed(motu);
+ return 0;
+}
+
+void snd_motu_stream_lock_release(struct snd_motu *motu)
+{
+ guard(spinlock_irq)(&motu->lock);
+
+ if (WARN_ON(motu->dev_lock_count <= 0))
+ return;
+
+ if (--motu->dev_lock_count == 0)
+ motu_lock_changed(motu);
+}
diff --git a/sound/firewire/motu/motu-transaction.c b/sound/firewire/motu/motu-transaction.c
new file mode 100644
index 000000000000..804f4208cf81
--- /dev/null
+++ b/sound/firewire/motu/motu-transaction.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * motu-transaction.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+
+#include "motu.h"
+
+#define SND_MOTU_ADDR_BASE 0xfffff0000000ULL
+#define ASYNC_ADDR_HI 0x0b04
+#define ASYNC_ADDR_LO 0x0b08
+
+int snd_motu_transaction_read(struct snd_motu *motu, u32 offset, __be32 *reg,
+ size_t size)
+{
+ int tcode;
+
+ if (size % sizeof(__be32) > 0 || size <= 0)
+ return -EINVAL;
+ if (size == sizeof(__be32))
+ tcode = TCODE_READ_QUADLET_REQUEST;
+ else
+ tcode = TCODE_READ_BLOCK_REQUEST;
+
+ return snd_fw_transaction(motu->unit, tcode,
+ SND_MOTU_ADDR_BASE + offset, reg, size, 0);
+}
+
+int snd_motu_transaction_write(struct snd_motu *motu, u32 offset, __be32 *reg,
+ size_t size)
+{
+ int tcode;
+
+ if (size % sizeof(__be32) > 0 || size <= 0)
+ return -EINVAL;
+ if (size == sizeof(__be32))
+ tcode = TCODE_WRITE_QUADLET_REQUEST;
+ else
+ tcode = TCODE_WRITE_BLOCK_REQUEST;
+
+ return snd_fw_transaction(motu->unit, tcode,
+ SND_MOTU_ADDR_BASE + offset, reg, size, 0);
+}
+
+static void handle_message(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_motu *motu = callback_data;
+ __be32 *buf = (__be32 *)data;
+
+ if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
+ fw_send_response(card, request, RCODE_COMPLETE);
+ return;
+ }
+
+ if (offset != motu->async_handler.offset || length != 4) {
+ fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+ return;
+ }
+
+ scoped_guard(spinlock_irqsave, &motu->lock) {
+ motu->msg = be32_to_cpu(*buf);
+ }
+
+ fw_send_response(card, request, RCODE_COMPLETE);
+
+ wake_up(&motu->hwdep_wait);
+}
+
+int snd_motu_transaction_reregister(struct snd_motu *motu)
+{
+ struct fw_device *device = fw_parent_device(motu->unit);
+ __be32 data;
+ int err;
+
+ if (motu->async_handler.callback_data == NULL)
+ return -EINVAL;
+
+ /* Register messaging address. Block transaction is not allowed. */
+ data = cpu_to_be32((device->card->node_id << 16) |
+ (motu->async_handler.offset >> 32));
+ err = snd_motu_transaction_write(motu, ASYNC_ADDR_HI, &data,
+ sizeof(data));
+ if (err < 0)
+ return err;
+
+ data = cpu_to_be32(motu->async_handler.offset);
+ return snd_motu_transaction_write(motu, ASYNC_ADDR_LO, &data,
+ sizeof(data));
+}
+
+int snd_motu_transaction_register(struct snd_motu *motu)
+{
+ static const struct fw_address_region resp_register_region = {
+ .start = 0xffffe0000000ull,
+ .end = 0xffffe000ffffull,
+ };
+ int err;
+
+ /* Perhaps, 4 byte messages are transferred. */
+ motu->async_handler.length = 4;
+ motu->async_handler.address_callback = handle_message;
+ motu->async_handler.callback_data = motu;
+
+ err = fw_core_add_address_handler(&motu->async_handler,
+ &resp_register_region);
+ if (err < 0)
+ return err;
+
+ err = snd_motu_transaction_reregister(motu);
+ if (err < 0) {
+ fw_core_remove_address_handler(&motu->async_handler);
+ motu->async_handler.address_callback = NULL;
+ }
+
+ return err;
+}
+
+void snd_motu_transaction_unregister(struct snd_motu *motu)
+{
+ __be32 data;
+
+ if (motu->async_handler.address_callback != NULL)
+ fw_core_remove_address_handler(&motu->async_handler);
+ motu->async_handler.address_callback = NULL;
+
+ /* Unregister the address. */
+ data = cpu_to_be32(0x00000000);
+ snd_motu_transaction_write(motu, ASYNC_ADDR_HI, &data, sizeof(data));
+ snd_motu_transaction_write(motu, ASYNC_ADDR_LO, &data, sizeof(data));
+}
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
new file mode 100644
index 000000000000..fd2a9dddbfa6
--- /dev/null
+++ b/sound/firewire/motu/motu.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * motu.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include "motu.h"
+
+#define OUI_MOTU 0x0001f2
+
+MODULE_DESCRIPTION("MOTU FireWire driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL");
+
+const unsigned int snd_motu_clock_rates[SND_MOTU_CLOCK_RATE_COUNT] = {
+ /* mode 0 */
+ [0] = 44100,
+ [1] = 48000,
+ /* mode 1 */
+ [2] = 88200,
+ [3] = 96000,
+ /* mode 2 */
+ [4] = 176400,
+ [5] = 192000,
+};
+
+static void name_card(struct snd_motu *motu)
+{
+ struct fw_device *fw_dev = fw_parent_device(motu->unit);
+ struct fw_csr_iterator it;
+ int key, val;
+ u32 version = 0;
+
+ fw_csr_iterator_init(&it, motu->unit->directory);
+ while (fw_csr_iterator_next(&it, &key, &val)) {
+ switch (key) {
+ case CSR_MODEL:
+ version = val;
+ break;
+ }
+ }
+
+ strscpy(motu->card->driver, "FW-MOTU");
+ strscpy(motu->card->shortname, motu->spec->name);
+ strscpy(motu->card->mixername, motu->spec->name);
+ snprintf(motu->card->longname, sizeof(motu->card->longname),
+ "MOTU %s (version:%06x), GUID %08x%08x at %s, S%d",
+ motu->spec->name, version,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&motu->unit->device), 100 << fw_dev->max_speed);
+}
+
+static void motu_card_free(struct snd_card *card)
+{
+ struct snd_motu *motu = card->private_data;
+
+ snd_motu_transaction_unregister(motu);
+ snd_motu_stream_destroy_duplex(motu);
+
+ mutex_destroy(&motu->mutex);
+ fw_unit_put(motu->unit);
+}
+
+static int motu_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_motu *motu;
+ int err;
+
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*motu), &card);
+ if (err < 0)
+ return err;
+ card->private_free = motu_card_free;
+
+ motu = card->private_data;
+ motu->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, motu);
+ motu->card = card;
+
+ motu->spec = (const struct snd_motu_spec *)entry->driver_data;
+ mutex_init(&motu->mutex);
+ spin_lock_init(&motu->lock);
+ init_waitqueue_head(&motu->hwdep_wait);
+
+ name_card(motu);
+
+ err = snd_motu_transaction_register(motu);
+ if (err < 0)
+ goto error;
+
+ err = snd_motu_stream_init_duplex(motu);
+ if (err < 0)
+ goto error;
+
+ snd_motu_proc_init(motu);
+
+ err = snd_motu_create_pcm_devices(motu);
+ if (err < 0)
+ goto error;
+
+ if ((motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) ||
+ (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q) ||
+ (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) ||
+ (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q)) {
+ err = snd_motu_create_midi_devices(motu);
+ if (err < 0)
+ goto error;
+ }
+
+ err = snd_motu_create_hwdep_device(motu);
+ if (err < 0)
+ goto error;
+
+ if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) {
+ err = snd_motu_register_dsp_message_parser_new(motu);
+ if (err < 0)
+ goto error;
+ } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) {
+ err = snd_motu_command_dsp_message_parser_new(motu);
+ if (err < 0)
+ goto error;
+ }
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void motu_remove(struct fw_unit *unit)
+{
+ struct snd_motu *motu = dev_get_drvdata(&unit->device);
+
+ // Block till all of ALSA character devices are released.
+ snd_card_free(motu->card);
+}
+
+static void motu_bus_update(struct fw_unit *unit)
+{
+ struct snd_motu *motu = dev_get_drvdata(&unit->device);
+
+ /* The handler address register becomes initialized. */
+ snd_motu_transaction_reregister(motu);
+}
+
+#define SND_MOTU_DEV_ENTRY(model, data) \
+{ \
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | \
+ IEEE1394_MATCH_SPECIFIER_ID | \
+ IEEE1394_MATCH_VERSION, \
+ .vendor_id = OUI_MOTU, \
+ .specifier_id = OUI_MOTU, \
+ .version = model, \
+ .driver_data = (kernel_ulong_t)data, \
+}
+
+static const struct ieee1394_device_id motu_id_table[] = {
+ SND_MOTU_DEV_ENTRY(0x000001, &snd_motu_spec_828),
+ SND_MOTU_DEV_ENTRY(0x000002, &snd_motu_spec_896),
+ SND_MOTU_DEV_ENTRY(0x000003, &snd_motu_spec_828mk2),
+ SND_MOTU_DEV_ENTRY(0x000005, &snd_motu_spec_896hd),
+ SND_MOTU_DEV_ENTRY(0x000009, &snd_motu_spec_traveler),
+ SND_MOTU_DEV_ENTRY(0x00000d, &snd_motu_spec_ultralite),
+ SND_MOTU_DEV_ENTRY(0x00000f, &snd_motu_spec_8pre),
+ SND_MOTU_DEV_ENTRY(0x000015, &snd_motu_spec_828mk3_fw), // FireWire only.
+ SND_MOTU_DEV_ENTRY(0x000017, &snd_motu_spec_896mk3), // FireWire only.
+ SND_MOTU_DEV_ENTRY(0x000019, &snd_motu_spec_ultralite_mk3), // FireWire only.
+ SND_MOTU_DEV_ENTRY(0x00001b, &snd_motu_spec_traveler_mk3),
+ SND_MOTU_DEV_ENTRY(0x000030, &snd_motu_spec_ultralite_mk3), // Hybrid.
+ SND_MOTU_DEV_ENTRY(0x000035, &snd_motu_spec_828mk3_hybrid), // Hybrid.
+ SND_MOTU_DEV_ENTRY(0x000037, &snd_motu_spec_896mk3), // Hybrid.
+ SND_MOTU_DEV_ENTRY(0x000033, &snd_motu_spec_audio_express),
+ SND_MOTU_DEV_ENTRY(0x000039, &snd_motu_spec_track16),
+ SND_MOTU_DEV_ENTRY(0x000045, &snd_motu_spec_4pre),
+ { }
+};
+MODULE_DEVICE_TABLE(ieee1394, motu_id_table);
+
+static struct fw_driver motu_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = motu_probe,
+ .update = motu_bus_update,
+ .remove = motu_remove,
+ .id_table = motu_id_table,
+};
+
+static int __init alsa_motu_init(void)
+{
+ return driver_register(&motu_driver.driver);
+}
+
+static void __exit alsa_motu_exit(void)
+{
+ driver_unregister(&motu_driver.driver);
+}
+
+module_init(alsa_motu_init);
+module_exit(alsa_motu_exit);
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
new file mode 100644
index 000000000000..c66be0a89ccf
--- /dev/null
+++ b/sound/firewire/motu/motu.h
@@ -0,0 +1,299 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * motu.h - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#ifndef SOUND_FIREWIRE_MOTU_H_INCLUDED
+#define SOUND_FIREWIRE_MOTU_H_INCLUDED
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/sched/signal.h>
+
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+
+#include "../lib.h"
+#include "../amdtp-stream.h"
+#include "../iso-resources.h"
+
+struct snd_motu_packet_format {
+ unsigned char midi_flag_offset;
+ unsigned char midi_byte_offset;
+ unsigned char pcm_byte_offset;
+
+ unsigned char msg_chunks;
+ unsigned char pcm_chunks[3];
+};
+
+struct amdtp_motu_cache {
+ unsigned int *event_offsets;
+ unsigned int size;
+ unsigned int tail;
+ unsigned int tx_cycle_count;
+ unsigned int head;
+ unsigned int rx_cycle_count;
+};
+
+struct snd_motu {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ struct mutex mutex;
+ spinlock_t lock;
+
+ /* Model dependent information. */
+ const struct snd_motu_spec *spec;
+
+ /* For packet streaming */
+ struct snd_motu_packet_format tx_packet_formats;
+ struct snd_motu_packet_format rx_packet_formats;
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ struct fw_iso_resources tx_resources;
+ struct fw_iso_resources rx_resources;
+ unsigned int substreams_counter;
+
+ /* For notification. */
+ struct fw_address_handler async_handler;
+ u32 msg;
+
+ /* For uapi */
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+ struct snd_hwdep *hwdep;
+
+ struct amdtp_domain domain;
+
+ struct amdtp_motu_cache cache;
+
+ void *message_parser;
+};
+
+enum snd_motu_spec_flags {
+ SND_MOTU_SPEC_RX_MIDI_2ND_Q = 0x0001,
+ SND_MOTU_SPEC_RX_MIDI_3RD_Q = 0x0002,
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q = 0x0004,
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q = 0x0008,
+ SND_MOTU_SPEC_REGISTER_DSP = 0x0010,
+ SND_MOTU_SPEC_COMMAND_DSP = 0x0020,
+};
+
+#define SND_MOTU_CLOCK_RATE_COUNT 6
+extern const unsigned int snd_motu_clock_rates[SND_MOTU_CLOCK_RATE_COUNT];
+
+enum snd_motu_clock_source {
+ SND_MOTU_CLOCK_SOURCE_INTERNAL,
+ SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB,
+ SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT,
+ SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A,
+ SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B,
+ SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT,
+ SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A,
+ SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B,
+ SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX,
+ SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR,
+ SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC,
+ SND_MOTU_CLOCK_SOURCE_SPH,
+ SND_MOTU_CLOCK_SOURCE_UNKNOWN,
+};
+
+enum snd_motu_protocol_version {
+ SND_MOTU_PROTOCOL_V1,
+ SND_MOTU_PROTOCOL_V2,
+ SND_MOTU_PROTOCOL_V3,
+};
+
+struct snd_motu_spec {
+ const char *const name;
+ enum snd_motu_protocol_version protocol_version;
+ // The combination of snd_motu_spec_flags enumeration-constants.
+ unsigned int flags;
+
+ unsigned char tx_fixed_pcm_chunks[3];
+ unsigned char rx_fixed_pcm_chunks[3];
+};
+
+extern const struct snd_motu_spec snd_motu_spec_828;
+extern const struct snd_motu_spec snd_motu_spec_896;
+
+extern const struct snd_motu_spec snd_motu_spec_828mk2;
+extern const struct snd_motu_spec snd_motu_spec_896hd;
+extern const struct snd_motu_spec snd_motu_spec_traveler;
+extern const struct snd_motu_spec snd_motu_spec_ultralite;
+extern const struct snd_motu_spec snd_motu_spec_8pre;
+
+extern const struct snd_motu_spec snd_motu_spec_828mk3_fw;
+extern const struct snd_motu_spec snd_motu_spec_828mk3_hybrid;
+extern const struct snd_motu_spec snd_motu_spec_896mk3;
+extern const struct snd_motu_spec snd_motu_spec_traveler_mk3;
+extern const struct snd_motu_spec snd_motu_spec_ultralite_mk3;
+extern const struct snd_motu_spec snd_motu_spec_audio_express;
+extern const struct snd_motu_spec snd_motu_spec_track16;
+extern const struct snd_motu_spec snd_motu_spec_4pre;
+
+int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir,
+ const struct snd_motu_spec *spec,
+ struct amdtp_motu_cache *cache);
+int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int midi_ports,
+ struct snd_motu_packet_format *formats);
+int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+void amdtp_motu_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi);
+
+int snd_motu_transaction_read(struct snd_motu *motu, u32 offset, __be32 *reg,
+ size_t size);
+int snd_motu_transaction_write(struct snd_motu *motu, u32 offset, __be32 *reg,
+ size_t size);
+int snd_motu_transaction_register(struct snd_motu *motu);
+int snd_motu_transaction_reregister(struct snd_motu *motu);
+void snd_motu_transaction_unregister(struct snd_motu *motu);
+
+int snd_motu_stream_init_duplex(struct snd_motu *motu);
+void snd_motu_stream_destroy_duplex(struct snd_motu *motu);
+int snd_motu_stream_cache_packet_formats(struct snd_motu *motu);
+int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer);
+int snd_motu_stream_start_duplex(struct snd_motu *motu);
+void snd_motu_stream_stop_duplex(struct snd_motu *motu);
+int snd_motu_stream_lock_try(struct snd_motu *motu);
+void snd_motu_stream_lock_release(struct snd_motu *motu);
+
+void snd_motu_proc_init(struct snd_motu *motu);
+
+int snd_motu_create_pcm_devices(struct snd_motu *motu);
+
+int snd_motu_create_midi_devices(struct snd_motu *motu);
+
+int snd_motu_create_hwdep_device(struct snd_motu *motu);
+
+int snd_motu_protocol_v1_get_clock_rate(struct snd_motu *motu,
+ unsigned int *rate);
+int snd_motu_protocol_v1_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate);
+int snd_motu_protocol_v1_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src);
+int snd_motu_protocol_v1_switch_fetching_mode(struct snd_motu *motu,
+ bool enable);
+int snd_motu_protocol_v1_cache_packet_formats(struct snd_motu *motu);
+
+int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu,
+ unsigned int *rate);
+int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate);
+int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src);
+int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu,
+ bool enable);
+int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu);
+
+int snd_motu_protocol_v3_get_clock_rate(struct snd_motu *motu,
+ unsigned int *rate);
+int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate);
+int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src);
+int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu,
+ bool enable);
+int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu);
+
+static inline int snd_motu_protocol_get_clock_rate(struct snd_motu *motu,
+ unsigned int *rate)
+{
+ if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
+ return snd_motu_protocol_v2_get_clock_rate(motu, rate);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
+ return snd_motu_protocol_v3_get_clock_rate(motu, rate);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
+ return snd_motu_protocol_v1_get_clock_rate(motu, rate);
+ else
+ return -ENXIO;
+}
+
+static inline int snd_motu_protocol_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate)
+{
+ if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
+ return snd_motu_protocol_v2_set_clock_rate(motu, rate);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
+ return snd_motu_protocol_v3_set_clock_rate(motu, rate);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
+ return snd_motu_protocol_v1_set_clock_rate(motu, rate);
+ else
+ return -ENXIO;
+}
+
+static inline int snd_motu_protocol_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *source)
+{
+ if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
+ return snd_motu_protocol_v2_get_clock_source(motu, source);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
+ return snd_motu_protocol_v3_get_clock_source(motu, source);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
+ return snd_motu_protocol_v1_get_clock_source(motu, source);
+ else
+ return -ENXIO;
+}
+
+static inline int snd_motu_protocol_switch_fetching_mode(struct snd_motu *motu,
+ bool enable)
+{
+ if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
+ return snd_motu_protocol_v2_switch_fetching_mode(motu, enable);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
+ return snd_motu_protocol_v3_switch_fetching_mode(motu, enable);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
+ return snd_motu_protocol_v1_switch_fetching_mode(motu, enable);
+ else
+ return -ENXIO;
+}
+
+static inline int snd_motu_protocol_cache_packet_formats(struct snd_motu *motu)
+{
+ if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
+ return snd_motu_protocol_v2_cache_packet_formats(motu);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
+ return snd_motu_protocol_v3_cache_packet_formats(motu);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
+ return snd_motu_protocol_v1_cache_packet_formats(motu);
+ else
+ return -ENXIO;
+}
+
+int snd_motu_register_dsp_message_parser_new(struct snd_motu *motu);
+int snd_motu_register_dsp_message_parser_init(struct snd_motu *motu);
+void snd_motu_register_dsp_message_parser_parse(const struct amdtp_stream *s,
+ const struct pkt_desc *descs, unsigned int count);
+void snd_motu_register_dsp_message_parser_copy_meter(struct snd_motu *motu,
+ struct snd_firewire_motu_register_dsp_meter *meter);
+void snd_motu_register_dsp_message_parser_copy_parameter(struct snd_motu *motu,
+ struct snd_firewire_motu_register_dsp_parameter *params);
+unsigned int snd_motu_register_dsp_message_parser_count_event(struct snd_motu *motu);
+bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32 *event);
+
+int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu);
+int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc);
+void snd_motu_command_dsp_message_parser_parse(const struct amdtp_stream *s,
+ const struct pkt_desc *descs, unsigned int count);
+void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu,
+ struct snd_firewire_motu_command_dsp_meter *meter);
+
+#endif
diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile
new file mode 100644
index 000000000000..9ac8893a926f
--- /dev/null
+++ b/sound/firewire/oxfw/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-oxfw-y := oxfw-command.o oxfw-stream.o oxfw-pcm.o oxfw-proc.o \
+ oxfw-midi.o oxfw-hwdep.o oxfw-spkr.o oxfw-scs1x.o oxfw.o
+obj-$(CONFIG_SND_OXFW) += snd-oxfw.o
diff --git a/sound/firewire/oxfw/oxfw-command.c b/sound/firewire/oxfw/oxfw-command.c
new file mode 100644
index 000000000000..d2e57c76070d
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-command.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * oxfw_command.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ */
+
+#include "oxfw.h"
+
+int avc_stream_set_format(struct fw_unit *unit, enum avc_general_plug_dir dir,
+ unsigned int pid, u8 *format, unsigned int len)
+{
+ u8 *buf;
+ int err;
+
+ buf = kmalloc(len + 10, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x00; /* CONTROL */
+ buf[1] = 0xff; /* UNIT */
+ buf[2] = 0xbf; /* EXTENDED STREAM FORMAT INFORMATION */
+ buf[3] = 0xc0; /* SINGLE subfunction */
+ buf[4] = dir; /* Plug Direction */
+ buf[5] = 0x00; /* UNIT */
+ buf[6] = 0x00; /* PCR (Isochronous Plug) */
+ buf[7] = 0xff & pid; /* Plug ID */
+ buf[8] = 0xff; /* Padding */
+ buf[9] = 0xff; /* Support status in response */
+ memcpy(buf + 10, format, len);
+
+ /* do transaction and check buf[1-8] are the same against command */
+ err = fcp_avc_transaction(unit, buf, len + 10, buf, len + 10,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(8));
+ if (err < 0)
+ ;
+ else if (err < len + 10)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENXIO;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else
+ err = 0;
+
+ kfree(buf);
+
+ return err;
+}
+
+int avc_stream_get_format(struct fw_unit *unit,
+ enum avc_general_plug_dir dir, unsigned int pid,
+ u8 *buf, unsigned int *len, unsigned int eid)
+{
+ unsigned int subfunc;
+ int err;
+
+ if (eid == 0xff)
+ subfunc = 0xc0; /* SINGLE */
+ else
+ subfunc = 0xc1; /* LIST */
+
+ buf[0] = 0x01; /* STATUS */
+ buf[1] = 0xff; /* UNIT */
+ buf[2] = 0xbf; /* EXTENDED STREAM FORMAT INFORMATION */
+ buf[3] = subfunc; /* SINGLE or LIST */
+ buf[4] = dir; /* Plug Direction */
+ buf[5] = 0x00; /* Unit */
+ buf[6] = 0x00; /* PCR (Isochronous Plug) */
+ buf[7] = 0xff & pid; /* Plug ID */
+ buf[8] = 0xff; /* Padding */
+ buf[9] = 0xff; /* support status in response */
+ buf[10] = 0xff & eid; /* entry ID for LIST subfunction */
+ buf[11] = 0xff; /* padding */
+
+ /* do transaction and check buf[1-7] are the same against command */
+ err = fcp_avc_transaction(unit, buf, 12, buf, *len,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7));
+ if (err < 0)
+ ;
+ else if (err < 12)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENXIO;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ /* LIST subfunction has entry ID */
+ else if ((subfunc == 0xc1) && (buf[10] != eid))
+ err = -EIO;
+ if (err < 0)
+ goto end;
+
+ /* keep just stream format information */
+ if (subfunc == 0xc0) {
+ memmove(buf, buf + 10, err - 10);
+ *len = err - 10;
+ } else {
+ memmove(buf, buf + 11, err - 11);
+ *len = err - 11;
+ }
+
+ err = 0;
+end:
+ return err;
+}
+
+int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate,
+ enum avc_general_plug_dir dir,
+ unsigned short pid)
+{
+ unsigned int sfc;
+ u8 *buf;
+ int err;
+
+ for (sfc = 0; sfc < CIP_SFC_COUNT; sfc++) {
+ if (amdtp_rate_table[sfc] == rate)
+ break;
+ }
+ if (sfc == CIP_SFC_COUNT)
+ return -EINVAL;
+
+ buf = kzalloc(8, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x02; /* SPECIFIC INQUIRY */
+ buf[1] = 0xff; /* UNIT */
+ if (dir == AVC_GENERAL_PLUG_DIR_IN)
+ buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */
+ else
+ buf[2] = 0x18; /* OUTPUT PLUG SIGNAL FORMAT */
+ buf[3] = 0xff & pid; /* plug id */
+ buf[4] = 0x90; /* EOH_1, Form_1, FMT. AM824 */
+ buf[5] = 0x07 & sfc; /* FDF-hi. AM824, frequency */
+ buf[6] = 0xff; /* FDF-mid. AM824, SYT hi (not used) */
+ buf[7] = 0xff; /* FDF-low. AM824, SYT lo (not used) */
+
+ /* do transaction and check buf[1-5] are the same against command */
+ err = fcp_avc_transaction(unit, buf, 8, buf, 8,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
+ if (err < 0)
+ ;
+ else if (err < 8)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENXIO;
+ if (err < 0)
+ goto end;
+
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
diff --git a/sound/firewire/oxfw/oxfw-hwdep.c b/sound/firewire/oxfw/oxfw-hwdep.c
new file mode 100644
index 000000000000..f8ac362fc73a
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-hwdep.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * oxfw_hwdep.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "oxfw.h"
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_oxfw *oxfw = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&oxfw->lock);
+
+ while (!oxfw->dev_lock_changed) {
+ prepare_to_wait(&oxfw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&oxfw->lock);
+ schedule();
+ finish_wait(&oxfw->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&oxfw->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (oxfw->dev_lock_count > 0);
+ oxfw->dev_lock_changed = false;
+
+ count = min_t(long, count, sizeof(event.lock_status));
+
+ spin_unlock_irq(&oxfw->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_oxfw *oxfw = hwdep->private_data;
+
+ poll_wait(file, &oxfw->hwdep_wait, wait);
+
+ guard(spinlock_irq)(&oxfw->lock);
+ if (oxfw->dev_lock_changed)
+ return EPOLLIN | EPOLLRDNORM;
+ else
+ return 0;
+}
+
+static int hwdep_get_info(struct snd_oxfw *oxfw, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(oxfw->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_OXFW;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strscpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_oxfw *oxfw)
+{
+ guard(spinlock_irq)(&oxfw->lock);
+
+ if (oxfw->dev_lock_count == 0) {
+ oxfw->dev_lock_count = -1;
+ return 0;
+ } else {
+ return -EBUSY;
+ }
+}
+
+static int hwdep_unlock(struct snd_oxfw *oxfw)
+{
+ guard(spinlock_irq)(&oxfw->lock);
+
+ if (oxfw->dev_lock_count == -1) {
+ oxfw->dev_lock_count = 0;
+ return 0;
+ } else {
+ return -EBADFD;
+ }
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_oxfw *oxfw = hwdep->private_data;
+
+ guard(spinlock_irq)(&oxfw->lock);
+ if (oxfw->dev_lock_count == -1)
+ oxfw->dev_lock_count = 0;
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_oxfw *oxfw = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(oxfw, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(oxfw);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(oxfw);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw)
+{
+ static const struct snd_hwdep_ops hwdep_ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(oxfw->card, oxfw->card->driver, 0, &hwdep);
+ if (err < 0)
+ goto end;
+ strscpy(hwdep->name, oxfw->card->driver);
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_OXFW;
+ hwdep->ops = hwdep_ops;
+ hwdep->private_data = oxfw;
+ hwdep->exclusive = true;
+end:
+ return err;
+}
diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c
new file mode 100644
index 000000000000..a16bf885f918
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-midi.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * oxfw_midi.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ */
+
+#include "oxfw.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->rmidi->private_data;
+ int err;
+
+ err = snd_oxfw_stream_lock_try(oxfw);
+ if (err < 0)
+ return err;
+
+ scoped_guard(mutex, &oxfw->mutex) {
+ err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, 0, 0, 0, 0);
+ if (err >= 0) {
+ ++oxfw->substreams_count;
+ err = snd_oxfw_stream_start_duplex(oxfw);
+ if (err < 0)
+ --oxfw->substreams_count;
+ }
+ }
+
+ if (err < 0)
+ snd_oxfw_stream_lock_release(oxfw);
+
+ return err;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->rmidi->private_data;
+ int err;
+
+ err = snd_oxfw_stream_lock_try(oxfw);
+ if (err < 0)
+ return err;
+
+ scoped_guard(mutex, &oxfw->mutex) {
+ err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, 0, 0, 0, 0);
+ if (err >= 0) {
+ ++oxfw->substreams_count;
+ err = snd_oxfw_stream_start_duplex(oxfw);
+ }
+ }
+
+ if (err < 0)
+ snd_oxfw_stream_lock_release(oxfw);
+
+ return err;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->rmidi->private_data;
+
+ scoped_guard(mutex, &oxfw->mutex) {
+ --oxfw->substreams_count;
+ snd_oxfw_stream_stop_duplex(oxfw);
+ }
+
+ snd_oxfw_stream_lock_release(oxfw);
+ return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->rmidi->private_data;
+
+ scoped_guard(mutex, &oxfw->mutex) {
+ --oxfw->substreams_count;
+ snd_oxfw_stream_stop_duplex(oxfw);
+ }
+
+ snd_oxfw_stream_lock_release(oxfw);
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_oxfw *oxfw = substrm->rmidi->private_data;
+
+ guard(spinlock_irqsave)(&oxfw->lock);
+
+ if (up)
+ amdtp_am824_midi_trigger(&oxfw->tx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_am824_midi_trigger(&oxfw->tx_stream,
+ substrm->number, NULL);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_oxfw *oxfw = substrm->rmidi->private_data;
+
+ guard(spinlock_irqsave)(&oxfw->lock);
+
+ if (up)
+ amdtp_am824_midi_trigger(&oxfw->rx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_am824_midi_trigger(&oxfw->rx_stream,
+ substrm->number, NULL);
+}
+
+static void set_midi_substream_names(struct snd_oxfw *oxfw,
+ struct snd_rawmidi_str *str)
+{
+ struct snd_rawmidi_substream *subs;
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ scnprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ oxfw->card->shortname, subs->number + 1);
+ }
+}
+
+int snd_oxfw_create_midi(struct snd_oxfw *oxfw)
+{
+ static const struct snd_rawmidi_ops capture_ops = {
+ .open = midi_capture_open,
+ .close = midi_capture_close,
+ .trigger = midi_capture_trigger,
+ };
+ static const struct snd_rawmidi_ops playback_ops = {
+ .open = midi_playback_open,
+ .close = midi_playback_close,
+ .trigger = midi_playback_trigger,
+ };
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *str;
+ int err;
+
+ if (oxfw->midi_input_ports == 0 && oxfw->midi_output_ports == 0)
+ return 0;
+
+ /* create midi ports */
+ err = snd_rawmidi_new(oxfw->card, oxfw->card->driver, 0,
+ oxfw->midi_output_ports, oxfw->midi_input_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", oxfw->card->shortname);
+ rmidi->private_data = oxfw;
+
+ if (oxfw->midi_input_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &capture_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+ set_midi_substream_names(oxfw, str);
+ }
+
+ if (oxfw->midi_output_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &playback_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+ set_midi_substream_names(oxfw, str);
+ }
+
+ if ((oxfw->midi_output_ports > 0) && (oxfw->midi_input_ports > 0))
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c
new file mode 100644
index 000000000000..774b8a763795
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-pcm.c
@@ -0,0 +1,435 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * oxfw_pcm.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include "oxfw.h"
+
+static int hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ u8 **formats = rule->private;
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ struct snd_oxfw_stream_formation formation;
+ int i, err;
+
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ if (formats[i] == NULL)
+ continue;
+
+ err = snd_oxfw_stream_parse_format(formats[i], &formation);
+ if (err < 0)
+ continue;
+ if (!snd_interval_test(c, formation.pcm))
+ continue;
+
+ t.min = min(t.min, formation.rate);
+ t.max = max(t.max, formation.rate);
+
+ }
+ return snd_interval_refine(r, &t);
+}
+
+static int hw_rule_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ u8 **formats = rule->private;
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_oxfw_stream_formation formation;
+ int i, j, err;
+ unsigned int count, list[SND_OXFW_STREAM_FORMAT_ENTRIES] = {0};
+
+ count = 0;
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ if (formats[i] == NULL)
+ break;
+
+ err = snd_oxfw_stream_parse_format(formats[i], &formation);
+ if (err < 0)
+ continue;
+ if (!snd_interval_test(r, formation.rate))
+ continue;
+ if (list[count] == formation.pcm)
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(list); j++) {
+ if (list[j] == formation.pcm)
+ break;
+ }
+ if (j == ARRAY_SIZE(list)) {
+ list[count] = formation.pcm;
+ if (++count == ARRAY_SIZE(list))
+ break;
+ }
+ }
+
+ return snd_interval_list(c, count, list, 0);
+}
+
+static void limit_channels_and_rates(struct snd_pcm_hardware *hw, u8 **formats)
+{
+ struct snd_oxfw_stream_formation formation;
+ int i, err;
+
+ hw->channels_min = UINT_MAX;
+ hw->channels_max = 0;
+
+ hw->rate_min = UINT_MAX;
+ hw->rate_max = 0;
+ hw->rates = 0;
+
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ if (formats[i] == NULL)
+ break;
+
+ err = snd_oxfw_stream_parse_format(formats[i], &formation);
+ if (err < 0)
+ continue;
+
+ hw->channels_min = min(hw->channels_min, formation.pcm);
+ hw->channels_max = max(hw->channels_max, formation.pcm);
+
+ hw->rate_min = min(hw->rate_min, formation.rate);
+ hw->rate_max = max(hw->rate_max, formation.rate);
+ hw->rates |= snd_pcm_rate_to_rate_bit(formation.rate);
+ }
+}
+
+static int init_hw_params(struct snd_oxfw *oxfw,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u8 **formats;
+ struct amdtp_stream *stream;
+ int err;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
+ stream = &oxfw->tx_stream;
+ formats = oxfw->tx_stream_formats;
+ } else {
+ runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
+ stream = &oxfw->rx_stream;
+ formats = oxfw->rx_stream_formats;
+ }
+
+ limit_channels_and_rates(&runtime->hw, formats);
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels, formats,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ goto end;
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_rate, formats,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ goto end;
+
+ err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
+end:
+ return err;
+}
+
+static int limit_to_current_params(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ struct snd_oxfw_stream_formation formation;
+ enum avc_general_plug_dir dir;
+ int err;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ dir = AVC_GENERAL_PLUG_DIR_OUT;
+ else
+ dir = AVC_GENERAL_PLUG_DIR_IN;
+
+ err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
+ if (err < 0)
+ goto end;
+
+ substream->runtime->hw.channels_min = formation.pcm;
+ substream->runtime->hw.channels_max = formation.pcm;
+ substream->runtime->hw.rate_min = formation.rate;
+ substream->runtime->hw.rate_max = formation.rate;
+end:
+ return err;
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ struct amdtp_domain *d = &oxfw->domain;
+ int err;
+
+ err = snd_oxfw_stream_lock_try(oxfw);
+ if (err < 0)
+ return err;
+
+ err = init_hw_params(oxfw, substream);
+ if (err < 0)
+ goto err_locked;
+
+ scoped_guard(mutex, &oxfw->mutex) {
+ // When source of clock is not internal or any stream is reserved for
+ // transmission of PCM frames, the available sampling rate is limited
+ // at current one.
+ if (oxfw->substreams_count > 0 && d->events_per_period > 0) {
+ unsigned int frames_per_period = d->events_per_period;
+ unsigned int frames_per_buffer = d->events_per_buffer;
+
+ err = limit_to_current_params(substream);
+ if (err < 0)
+ goto err_locked;
+
+ if (frames_per_period > 0) {
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ frames_per_period, frames_per_period);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ frames_per_buffer, frames_per_buffer);
+ if (err < 0)
+ goto err_locked;
+ }
+ }
+ }
+
+ snd_pcm_set_sync(substream);
+
+ return 0;
+err_locked:
+ snd_oxfw_stream_lock_release(oxfw);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+
+ snd_oxfw_stream_lock_release(oxfw);
+ return 0;
+}
+
+static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ int err = 0;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ unsigned int rate = params_rate(hw_params);
+ unsigned int channels = params_channels(hw_params);
+ unsigned int frames_per_period = params_period_size(hw_params);
+ unsigned int frames_per_buffer = params_buffer_size(hw_params);
+
+ guard(mutex)(&oxfw->mutex);
+ err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream,
+ rate, channels, frames_per_period,
+ frames_per_buffer);
+ if (err >= 0)
+ ++oxfw->substreams_count;
+ }
+
+ return err;
+}
+static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ int err = 0;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ unsigned int rate = params_rate(hw_params);
+ unsigned int channels = params_channels(hw_params);
+ unsigned int frames_per_period = params_period_size(hw_params);
+ unsigned int frames_per_buffer = params_buffer_size(hw_params);
+
+ guard(mutex)(&oxfw->mutex);
+ err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream,
+ rate, channels, frames_per_period,
+ frames_per_buffer);
+ if (err >= 0)
+ ++oxfw->substreams_count;
+ }
+
+ return err;
+}
+
+static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+
+ guard(mutex)(&oxfw->mutex);
+
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --oxfw->substreams_count;
+
+ snd_oxfw_stream_stop_duplex(oxfw);
+
+ return 0;
+}
+static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+
+ guard(mutex)(&oxfw->mutex);
+
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --oxfw->substreams_count;
+
+ snd_oxfw_stream_stop_duplex(oxfw);
+
+ return 0;
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ int err;
+
+ scoped_guard(mutex, &oxfw->mutex) {
+ err = snd_oxfw_stream_start_duplex(oxfw);
+ if (err < 0)
+ return err;
+ }
+
+ amdtp_stream_pcm_prepare(&oxfw->tx_stream);
+ return 0;
+}
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ int err;
+
+ scoped_guard(mutex, &oxfw->mutex) {
+ err = snd_oxfw_stream_start_duplex(oxfw);
+ if (err < 0)
+ return err;
+ }
+
+ amdtp_stream_pcm_prepare(&oxfw->rx_stream);
+ return 0;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ struct snd_pcm_substream *pcm;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ pcm = substream;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pcm = NULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ amdtp_stream_pcm_trigger(&oxfw->tx_stream, pcm);
+ return 0;
+}
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+ struct snd_pcm_substream *pcm;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ pcm = substream;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pcm = NULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm);
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm)
+{
+ struct snd_oxfw *oxfw = sbstm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->tx_stream);
+}
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm)
+{
+ struct snd_oxfw *oxfw = sbstm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->rx_stream);
+}
+
+static int pcm_capture_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->tx_stream);
+}
+
+static int pcm_playback_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_oxfw *oxfw = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->rx_stream);
+}
+
+int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
+{
+ static const struct snd_pcm_ops capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_capture_hw_params,
+ .hw_free = pcm_capture_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .ack = pcm_capture_ack,
+ };
+ static const struct snd_pcm_ops playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_playback_hw_params,
+ .hw_free = pcm_playback_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .ack = pcm_playback_ack,
+ };
+ struct snd_pcm *pcm;
+ unsigned int cap = 0;
+ int err;
+
+ if (oxfw->has_output)
+ cap = 1;
+
+ err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, cap, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = oxfw;
+ pcm->nonatomic = true;
+ strscpy(pcm->name, oxfw->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+ if (cap > 0)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
+
+ return 0;
+}
diff --git a/sound/firewire/oxfw/oxfw-proc.c b/sound/firewire/oxfw/oxfw-proc.c
new file mode 100644
index 000000000000..260c60364f39
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-proc.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * oxfw_proc.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ */
+
+#include "./oxfw.h"
+
+static void proc_read_formation(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_oxfw *oxfw = entry->private_data;
+ struct snd_oxfw_stream_formation formation, curr;
+ u8 *format;
+ char flag;
+ int i, err;
+
+ /* Show input. */
+ err = snd_oxfw_stream_get_current_formation(oxfw,
+ AVC_GENERAL_PLUG_DIR_IN,
+ &curr);
+ if (err < 0)
+ return;
+
+ snd_iprintf(buffer, "Input Stream to device:\n");
+ snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ format = oxfw->rx_stream_formats[i];
+ if (format == NULL)
+ continue;
+
+ err = snd_oxfw_stream_parse_format(format, &formation);
+ if (err < 0)
+ continue;
+
+ if (memcmp(&formation, &curr, sizeof(curr)) == 0)
+ flag = '*';
+ else
+ flag = ' ';
+
+ snd_iprintf(buffer, "%c\t%d\t%d\t%d\n", flag,
+ formation.rate, formation.pcm, formation.midi);
+ }
+
+ if (!oxfw->has_output)
+ return;
+
+ /* Show output. */
+ err = snd_oxfw_stream_get_current_formation(oxfw,
+ AVC_GENERAL_PLUG_DIR_OUT,
+ &curr);
+ if (err < 0)
+ return;
+
+ snd_iprintf(buffer, "Output Stream from device:\n");
+ snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ format = oxfw->tx_stream_formats[i];
+ if (format == NULL)
+ continue;
+
+ err = snd_oxfw_stream_parse_format(format, &formation);
+ if (err < 0)
+ continue;
+
+ if (memcmp(&formation, &curr, sizeof(curr)) == 0)
+ flag = '*';
+ else
+ flag = ' ';
+
+ snd_iprintf(buffer, "%c\t%d\t%d\t%d\n", flag,
+ formation.rate, formation.pcm, formation.midi);
+ }
+}
+
+static void add_node(struct snd_oxfw *oxfw, struct snd_info_entry *root,
+ const char *name,
+ void (*op)(struct snd_info_entry *e,
+ struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(oxfw->card, name, root);
+ if (entry)
+ snd_info_set_text_ops(entry, oxfw, op);
+}
+
+void snd_oxfw_proc_init(struct snd_oxfw *oxfw)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(oxfw->card, "firewire",
+ oxfw->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | 0555;
+
+ add_node(oxfw, root, "formation", proc_read_formation);
+}
diff --git a/sound/firewire/oxfw/oxfw-scs1x.c b/sound/firewire/oxfw/oxfw-scs1x.c
new file mode 100644
index 000000000000..21412a3ca9f4
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-scs1x.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * oxfw-scs1x.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2015 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ */
+
+#include "oxfw.h"
+
+#define HSS1394_ADDRESS 0xc007dedadadaULL
+#define HSS1394_MAX_PACKET_SIZE 64
+#define HSS1394_TAG_USER_DATA 0x00
+#define HSS1394_TAG_CHANGE_ADDRESS 0xf1
+
+struct fw_scs1x {
+ struct fw_address_handler hss_handler;
+ u8 input_escape_count;
+ struct snd_rawmidi_substream *input;
+
+ /* For MIDI playback. */
+ struct snd_rawmidi_substream *output;
+ bool output_idle;
+ u8 output_status;
+ u8 output_bytes;
+ bool output_escaped;
+ bool output_escape_high_nibble;
+ struct work_struct work;
+ wait_queue_head_t idle_wait;
+ u8 buffer[HSS1394_MAX_PACKET_SIZE];
+ bool transaction_running;
+ struct fw_transaction transaction;
+ unsigned int transaction_bytes;
+ bool error;
+ struct fw_device *fw_dev;
+};
+
+static const u8 sysex_escape_prefix[] = {
+ 0xf0, /* SysEx begin */
+ 0x00, 0x01, 0x60, /* Stanton DJ */
+ 0x48, 0x53, 0x53, /* "HSS" */
+};
+
+static void midi_input_escaped_byte(struct snd_rawmidi_substream *stream,
+ u8 byte)
+{
+ u8 nibbles[2];
+
+ nibbles[0] = byte >> 4;
+ nibbles[1] = byte & 0x0f;
+ snd_rawmidi_receive(stream, nibbles, 2);
+}
+
+static void midi_input_byte(struct fw_scs1x *scs,
+ struct snd_rawmidi_substream *stream, u8 byte)
+{
+ const u8 eox = 0xf7;
+
+ if (scs->input_escape_count > 0) {
+ midi_input_escaped_byte(stream, byte);
+ scs->input_escape_count--;
+ if (scs->input_escape_count == 0)
+ snd_rawmidi_receive(stream, &eox, sizeof(eox));
+ } else if (byte == 0xf9) {
+ snd_rawmidi_receive(stream, sysex_escape_prefix,
+ ARRAY_SIZE(sysex_escape_prefix));
+ midi_input_escaped_byte(stream, 0x00);
+ midi_input_escaped_byte(stream, 0xf9);
+ scs->input_escape_count = 3;
+ } else {
+ snd_rawmidi_receive(stream, &byte, 1);
+ }
+}
+
+static void midi_input_packet(struct fw_scs1x *scs,
+ struct snd_rawmidi_substream *stream,
+ const u8 *data, unsigned int bytes)
+{
+ unsigned int i;
+ const u8 eox = 0xf7;
+
+ if (data[0] == HSS1394_TAG_USER_DATA) {
+ for (i = 1; i < bytes; ++i)
+ midi_input_byte(scs, stream, data[i]);
+ } else {
+ snd_rawmidi_receive(stream, sysex_escape_prefix,
+ ARRAY_SIZE(sysex_escape_prefix));
+ for (i = 0; i < bytes; ++i)
+ midi_input_escaped_byte(stream, data[i]);
+ snd_rawmidi_receive(stream, &eox, sizeof(eox));
+ }
+}
+
+static void handle_hss(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source, int generation,
+ unsigned long long offset, void *data, size_t length,
+ void *callback_data)
+{
+ struct fw_scs1x *scs = callback_data;
+ struct snd_rawmidi_substream *stream;
+ int rcode;
+
+ if (offset != scs->hss_handler.offset) {
+ rcode = RCODE_ADDRESS_ERROR;
+ goto end;
+ }
+ if (tcode != TCODE_WRITE_QUADLET_REQUEST &&
+ tcode != TCODE_WRITE_BLOCK_REQUEST) {
+ rcode = RCODE_TYPE_ERROR;
+ goto end;
+ }
+
+ if (length >= 1) {
+ stream = READ_ONCE(scs->input);
+ if (stream)
+ midi_input_packet(scs, stream, data, length);
+ }
+
+ rcode = RCODE_COMPLETE;
+end:
+ fw_send_response(card, request, rcode);
+}
+
+static void scs_write_callback(struct fw_card *card, int rcode,
+ void *data, size_t length, void *callback_data)
+{
+ struct fw_scs1x *scs = callback_data;
+
+ if (!rcode_is_permanent_error(rcode)) {
+ /* Don't retry for this data. */
+ if (rcode == RCODE_COMPLETE)
+ scs->transaction_bytes = 0;
+ } else {
+ scs->error = true;
+ }
+
+ scs->transaction_running = false;
+ schedule_work(&scs->work);
+}
+
+static bool is_valid_running_status(u8 status)
+{
+ return status >= 0x80 && status <= 0xef;
+}
+
+static bool is_one_byte_cmd(u8 status)
+{
+ return status == 0xf6 ||
+ status >= 0xf8;
+}
+
+static bool is_two_bytes_cmd(u8 status)
+{
+ return (status >= 0xc0 && status <= 0xdf) ||
+ status == 0xf1 ||
+ status == 0xf3;
+}
+
+static bool is_three_bytes_cmd(u8 status)
+{
+ return (status >= 0x80 && status <= 0xbf) ||
+ (status >= 0xe0 && status <= 0xef) ||
+ status == 0xf2;
+}
+
+static bool is_invalid_cmd(u8 status)
+{
+ return status == 0xf4 ||
+ status == 0xf5 ||
+ status == 0xf9 ||
+ status == 0xfd;
+}
+
+static void scs_output_work(struct work_struct *work)
+{
+ struct fw_scs1x *scs = container_of(work, struct fw_scs1x, work);
+ struct snd_rawmidi_substream *stream;
+ unsigned int i;
+ u8 byte;
+ int generation;
+
+ if (scs->transaction_running)
+ return;
+
+ stream = READ_ONCE(scs->output);
+ if (!stream || scs->error) {
+ scs->output_idle = true;
+ wake_up(&scs->idle_wait);
+ return;
+ }
+
+ if (scs->transaction_bytes > 0)
+ goto retry;
+
+ i = scs->output_bytes;
+ for (;;) {
+ if (snd_rawmidi_transmit(stream, &byte, 1) != 1) {
+ scs->output_bytes = i;
+ scs->output_idle = true;
+ wake_up(&scs->idle_wait);
+ return;
+ }
+ /*
+ * Convert from real MIDI to what I think the device expects (no
+ * running status, one command per packet, unescaped SysExs).
+ */
+ if (scs->output_escaped && byte < 0x80) {
+ if (scs->output_escape_high_nibble) {
+ if (i < HSS1394_MAX_PACKET_SIZE) {
+ scs->buffer[i] = byte << 4;
+ scs->output_escape_high_nibble = false;
+ }
+ } else {
+ scs->buffer[i++] |= byte & 0x0f;
+ scs->output_escape_high_nibble = true;
+ }
+ } else if (byte < 0x80) {
+ if (i == 1) {
+ if (!is_valid_running_status(
+ scs->output_status))
+ continue;
+ scs->buffer[0] = HSS1394_TAG_USER_DATA;
+ scs->buffer[i++] = scs->output_status;
+ }
+ scs->buffer[i++] = byte;
+ if ((i == 3 && is_two_bytes_cmd(scs->output_status)) ||
+ (i == 4 && is_three_bytes_cmd(scs->output_status)))
+ break;
+ if (i == 1 + ARRAY_SIZE(sysex_escape_prefix) &&
+ !memcmp(scs->buffer + 1, sysex_escape_prefix,
+ ARRAY_SIZE(sysex_escape_prefix))) {
+ scs->output_escaped = true;
+ scs->output_escape_high_nibble = true;
+ i = 0;
+ }
+ if (i >= HSS1394_MAX_PACKET_SIZE)
+ i = 1;
+ } else if (byte == 0xf7) {
+ if (scs->output_escaped) {
+ if (i >= 1 && scs->output_escape_high_nibble &&
+ scs->buffer[0] !=
+ HSS1394_TAG_CHANGE_ADDRESS)
+ break;
+ } else {
+ if (i > 1 && scs->output_status == 0xf0) {
+ scs->buffer[i++] = 0xf7;
+ break;
+ }
+ }
+ i = 1;
+ scs->output_escaped = false;
+ } else if (!is_invalid_cmd(byte) && byte < 0xf8) {
+ i = 1;
+ scs->buffer[0] = HSS1394_TAG_USER_DATA;
+ scs->buffer[i++] = byte;
+ scs->output_status = byte;
+ scs->output_escaped = false;
+ if (is_one_byte_cmd(byte))
+ break;
+ }
+ }
+ scs->output_bytes = 1;
+ scs->output_escaped = false;
+
+ scs->transaction_bytes = i;
+retry:
+ scs->transaction_running = true;
+ generation = scs->fw_dev->generation;
+ smp_rmb(); /* node_id vs. generation */
+ fw_send_request(scs->fw_dev->card, &scs->transaction,
+ TCODE_WRITE_BLOCK_REQUEST, scs->fw_dev->node_id,
+ generation, scs->fw_dev->max_speed, HSS1394_ADDRESS,
+ scs->buffer, scs->transaction_bytes,
+ scs_write_callback, scs);
+}
+
+static int midi_capture_open(struct snd_rawmidi_substream *stream)
+{
+ return 0;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *stream)
+{
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *stream, int up)
+{
+ struct fw_scs1x *scs = stream->rmidi->private_data;
+
+ if (up) {
+ scs->input_escape_count = 0;
+ WRITE_ONCE(scs->input, stream);
+ } else {
+ WRITE_ONCE(scs->input, NULL);
+ }
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *stream)
+{
+ return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *stream)
+{
+ return 0;
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up)
+{
+ struct fw_scs1x *scs = stream->rmidi->private_data;
+
+ if (up) {
+ scs->output_status = 0;
+ scs->output_bytes = 1;
+ scs->output_escaped = false;
+ scs->output_idle = false;
+ scs->transaction_bytes = 0;
+ scs->error = false;
+
+ WRITE_ONCE(scs->output, stream);
+ schedule_work(&scs->work);
+ } else {
+ WRITE_ONCE(scs->output, NULL);
+ }
+}
+static void midi_playback_drain(struct snd_rawmidi_substream *stream)
+{
+ struct fw_scs1x *scs = stream->rmidi->private_data;
+
+ wait_event(scs->idle_wait, scs->output_idle);
+}
+
+static int register_address(struct snd_oxfw *oxfw)
+{
+ struct fw_scs1x *scs = oxfw->spec;
+ __be64 data;
+
+ data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) |
+ scs->hss_handler.offset);
+ return snd_fw_transaction(oxfw->unit, TCODE_WRITE_BLOCK_REQUEST,
+ HSS1394_ADDRESS, &data, sizeof(data), 0);
+}
+
+static void remove_scs1x(struct snd_rawmidi *rmidi)
+{
+ struct fw_scs1x *scs = rmidi->private_data;
+
+ fw_core_remove_address_handler(&scs->hss_handler);
+}
+
+void snd_oxfw_scs1x_update(struct snd_oxfw *oxfw)
+{
+ register_address(oxfw);
+}
+
+int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw)
+{
+ static const struct snd_rawmidi_ops midi_capture_ops = {
+ .open = midi_capture_open,
+ .close = midi_capture_close,
+ .trigger = midi_capture_trigger,
+ };
+ static const struct snd_rawmidi_ops midi_playback_ops = {
+ .open = midi_playback_open,
+ .close = midi_playback_close,
+ .trigger = midi_playback_trigger,
+ .drain = midi_playback_drain,
+ };
+ struct snd_rawmidi *rmidi;
+ struct fw_scs1x *scs;
+ int err;
+
+ scs = devm_kzalloc(&oxfw->card->card_dev, sizeof(struct fw_scs1x),
+ GFP_KERNEL);
+ if (!scs)
+ return -ENOMEM;
+ scs->fw_dev = fw_parent_device(oxfw->unit);
+ oxfw->spec = scs;
+
+ /* Allocate own handler for imcoming asynchronous transaction. */
+ scs->hss_handler.length = HSS1394_MAX_PACKET_SIZE;
+ scs->hss_handler.address_callback = handle_hss;
+ scs->hss_handler.callback_data = scs;
+ err = fw_core_add_address_handler(&scs->hss_handler,
+ &fw_high_memory_region);
+ if (err < 0)
+ return err;
+
+ err = register_address(oxfw);
+ if (err < 0)
+ goto err_allocated;
+
+ /* Use unique name for backward compatibility to scs1x module. */
+ err = snd_rawmidi_new(oxfw->card, "SCS.1x", 0, 1, 1, &rmidi);
+ if (err < 0)
+ goto err_allocated;
+ rmidi->private_data = scs;
+ rmidi->private_free = remove_scs1x;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", oxfw->card->shortname);
+
+ rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &midi_capture_ops);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &midi_playback_ops);
+
+ INIT_WORK(&scs->work, scs_output_work);
+ init_waitqueue_head(&scs->idle_wait);
+ scs->output_idle = true;
+
+ return 0;
+err_allocated:
+ fw_core_remove_address_handler(&scs->hss_handler);
+ return err;
+}
diff --git a/sound/firewire/oxfw/oxfw-spkr.c b/sound/firewire/oxfw/oxfw-spkr.c
new file mode 100644
index 000000000000..f2767fb1965c
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-spkr.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * oxfw-spkr.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include "oxfw.h"
+
+struct fw_spkr {
+ bool mute;
+ s16 volume[6];
+ s16 volume_min;
+ s16 volume_max;
+
+ unsigned int mixer_channels;
+ u8 mute_fb_id;
+ u8 volume_fb_id;
+};
+
+enum control_action { CTL_READ, CTL_WRITE };
+enum control_attribute {
+ CTL_MIN = 0x02,
+ CTL_MAX = 0x03,
+ CTL_CURRENT = 0x10,
+};
+
+static int avc_audio_feature_mute(struct fw_unit *unit, u8 fb_id, bool *value,
+ enum control_action action)
+{
+ u8 *buf;
+ u8 response_ok;
+ int err;
+
+ buf = kmalloc(11, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (action == CTL_READ) {
+ buf[0] = 0x01; /* AV/C, STATUS */
+ response_ok = 0x0c; /* STABLE */
+ } else {
+ buf[0] = 0x00; /* AV/C, CONTROL */
+ response_ok = 0x09; /* ACCEPTED */
+ }
+ buf[1] = 0x08; /* audio unit 0 */
+ buf[2] = 0xb8; /* FUNCTION BLOCK */
+ buf[3] = 0x81; /* function block type: feature */
+ buf[4] = fb_id; /* function block ID */
+ buf[5] = 0x10; /* control attribute: current */
+ buf[6] = 0x02; /* selector length */
+ buf[7] = 0x00; /* audio channel number */
+ buf[8] = 0x01; /* control selector: mute */
+ buf[9] = 0x01; /* control data length */
+ if (action == CTL_READ)
+ buf[10] = 0xff;
+ else
+ buf[10] = *value ? 0x70 : 0x60;
+
+ err = fcp_avc_transaction(unit, buf, 11, buf, 11, 0x3fe);
+ if (err < 0)
+ goto error;
+ if (err < 11) {
+ dev_err(&unit->device, "short FCP response\n");
+ err = -EIO;
+ goto error;
+ }
+ if (buf[0] != response_ok) {
+ dev_err(&unit->device, "mute command failed\n");
+ err = -EIO;
+ goto error;
+ }
+ if (action == CTL_READ)
+ *value = buf[10] == 0x70;
+
+ err = 0;
+
+error:
+ kfree(buf);
+
+ return err;
+}
+
+static int avc_audio_feature_volume(struct fw_unit *unit, u8 fb_id, s16 *value,
+ unsigned int channel,
+ enum control_attribute attribute,
+ enum control_action action)
+{
+ u8 *buf;
+ u8 response_ok;
+ int err;
+
+ buf = kmalloc(12, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ if (action == CTL_READ) {
+ buf[0] = 0x01; /* AV/C, STATUS */
+ response_ok = 0x0c; /* STABLE */
+ } else {
+ buf[0] = 0x00; /* AV/C, CONTROL */
+ response_ok = 0x09; /* ACCEPTED */
+ }
+ buf[1] = 0x08; /* audio unit 0 */
+ buf[2] = 0xb8; /* FUNCTION BLOCK */
+ buf[3] = 0x81; /* function block type: feature */
+ buf[4] = fb_id; /* function block ID */
+ buf[5] = attribute; /* control attribute */
+ buf[6] = 0x02; /* selector length */
+ buf[7] = channel; /* audio channel number */
+ buf[8] = 0x02; /* control selector: volume */
+ buf[9] = 0x02; /* control data length */
+ if (action == CTL_READ) {
+ buf[10] = 0xff;
+ buf[11] = 0xff;
+ } else {
+ buf[10] = *value >> 8;
+ buf[11] = *value;
+ }
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 12, 0x3fe);
+ if (err < 0)
+ goto error;
+ if (err < 12) {
+ dev_err(&unit->device, "short FCP response\n");
+ err = -EIO;
+ goto error;
+ }
+ if (buf[0] != response_ok) {
+ dev_err(&unit->device, "volume command failed\n");
+ err = -EIO;
+ goto error;
+ }
+ if (action == CTL_READ)
+ *value = (buf[10] << 8) | buf[11];
+
+ err = 0;
+
+error:
+ kfree(buf);
+
+ return err;
+}
+
+static int spkr_mute_get(struct snd_kcontrol *control,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_oxfw *oxfw = control->private_data;
+ struct fw_spkr *spkr = oxfw->spec;
+
+ value->value.integer.value[0] = !spkr->mute;
+
+ return 0;
+}
+
+static int spkr_mute_put(struct snd_kcontrol *control,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_oxfw *oxfw = control->private_data;
+ struct fw_spkr *spkr = oxfw->spec;
+ bool mute;
+ int err;
+
+ mute = !value->value.integer.value[0];
+
+ if (mute == spkr->mute)
+ return 0;
+
+ err = avc_audio_feature_mute(oxfw->unit, spkr->mute_fb_id, &mute,
+ CTL_WRITE);
+ if (err < 0)
+ return err;
+ spkr->mute = mute;
+
+ return 1;
+}
+
+static int spkr_volume_info(struct snd_kcontrol *control,
+ struct snd_ctl_elem_info *info)
+{
+ struct snd_oxfw *oxfw = control->private_data;
+ struct fw_spkr *spkr = oxfw->spec;
+
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = spkr->mixer_channels;
+ info->value.integer.min = spkr->volume_min;
+ info->value.integer.max = spkr->volume_max;
+
+ return 0;
+}
+
+static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 };
+
+static int spkr_volume_get(struct snd_kcontrol *control,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_oxfw *oxfw = control->private_data;
+ struct fw_spkr *spkr = oxfw->spec;
+ unsigned int i;
+
+ for (i = 0; i < spkr->mixer_channels; ++i)
+ value->value.integer.value[channel_map[i]] = spkr->volume[i];
+
+ return 0;
+}
+
+static int spkr_volume_put(struct snd_kcontrol *control,
+ struct snd_ctl_elem_value *value)
+{
+ struct snd_oxfw *oxfw = control->private_data;
+ struct fw_spkr *spkr = oxfw->spec;
+ unsigned int i, changed_channels;
+ bool equal_values = true;
+ s16 volume;
+ int err;
+
+ for (i = 0; i < spkr->mixer_channels; ++i) {
+ if (value->value.integer.value[i] < spkr->volume_min ||
+ value->value.integer.value[i] > spkr->volume_max)
+ return -EINVAL;
+ if (value->value.integer.value[i] !=
+ value->value.integer.value[0])
+ equal_values = false;
+ }
+
+ changed_channels = 0;
+ for (i = 0; i < spkr->mixer_channels; ++i)
+ if (value->value.integer.value[channel_map[i]] !=
+ spkr->volume[i])
+ changed_channels |= 1 << (i + 1);
+
+ if (equal_values && changed_channels != 0)
+ changed_channels = 1 << 0;
+
+ for (i = 0; i <= spkr->mixer_channels; ++i) {
+ volume = value->value.integer.value[channel_map[i ? i - 1 : 0]];
+ if (changed_channels & (1 << i)) {
+ err = avc_audio_feature_volume(oxfw->unit,
+ spkr->volume_fb_id, &volume,
+ i, CTL_CURRENT, CTL_WRITE);
+ if (err < 0)
+ return err;
+ }
+ if (i > 0)
+ spkr->volume[i - 1] = volume;
+ }
+
+ return changed_channels != 0;
+}
+
+int snd_oxfw_add_spkr(struct snd_oxfw *oxfw, bool is_lacie)
+{
+ static const struct snd_kcontrol_new controls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = spkr_mute_get,
+ .put = spkr_mute_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "PCM Playback Volume",
+ .info = spkr_volume_info,
+ .get = spkr_volume_get,
+ .put = spkr_volume_put,
+ },
+ };
+ struct fw_spkr *spkr;
+ unsigned int i, first_ch;
+ int err;
+
+ spkr = devm_kzalloc(&oxfw->card->card_dev, sizeof(struct fw_spkr),
+ GFP_KERNEL);
+ if (!spkr)
+ return -ENOMEM;
+ oxfw->spec = spkr;
+
+ if (is_lacie) {
+ spkr->mixer_channels = 1;
+ spkr->mute_fb_id = 0x01;
+ spkr->volume_fb_id = 0x01;
+ } else {
+ spkr->mixer_channels = 6;
+ spkr->mute_fb_id = 0x01;
+ spkr->volume_fb_id = 0x02;
+ }
+
+ err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
+ &spkr->volume_min, 0, CTL_MIN, CTL_READ);
+ if (err < 0)
+ return err;
+ err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
+ &spkr->volume_max, 0, CTL_MAX, CTL_READ);
+ if (err < 0)
+ return err;
+
+ err = avc_audio_feature_mute(oxfw->unit, spkr->mute_fb_id, &spkr->mute,
+ CTL_READ);
+ if (err < 0)
+ return err;
+
+ first_ch = spkr->mixer_channels == 1 ? 0 : 1;
+ for (i = 0; i < spkr->mixer_channels; ++i) {
+ err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
+ &spkr->volume[i], first_ch + i,
+ CTL_CURRENT, CTL_READ);
+ if (err < 0)
+ return err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(controls); ++i) {
+ err = snd_ctl_add(oxfw->card,
+ snd_ctl_new1(&controls[i], oxfw));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
new file mode 100644
index 000000000000..5e36d7153a7b
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -0,0 +1,889 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * oxfw_stream.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) 2014 Takashi Sakamoto
+ */
+
+#include "oxfw.h"
+#include <linux/delay.h>
+
+#define AVC_GENERIC_FRAME_MAXIMUM_BYTES 512
+#define READY_TIMEOUT_MS 600
+
+/*
+ * According to datasheet of Oxford Semiconductor:
+ * OXFW970: 32.0/44.1/48.0/96.0 Khz, 8 audio channels I/O
+ * OXFW971: 32.0/44.1/48.0/88.2/96.0/192.0 kHz, 16 audio channels I/O, MIDI I/O
+ */
+static const unsigned int oxfw_rate_table[] = {
+ [0] = 32000,
+ [1] = 44100,
+ [2] = 48000,
+ [3] = 88200,
+ [4] = 96000,
+ [5] = 192000,
+};
+
+/*
+ * See Table 5.7 – Sampling frequency for Multi-bit Audio
+ * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA)
+ */
+static const unsigned int avc_stream_rate_table[] = {
+ [0] = 0x02,
+ [1] = 0x03,
+ [2] = 0x04,
+ [3] = 0x0a,
+ [4] = 0x05,
+ [5] = 0x07,
+};
+
+static int set_rate(struct snd_oxfw *oxfw, unsigned int rate)
+{
+ int err;
+
+ err = avc_general_set_sig_fmt(oxfw->unit, rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+ if (err < 0)
+ goto end;
+
+ if (oxfw->has_output)
+ err = avc_general_set_sig_fmt(oxfw->unit, rate,
+ AVC_GENERAL_PLUG_DIR_OUT, 0);
+end:
+ return err;
+}
+
+static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
+ unsigned int rate, unsigned int pcm_channels)
+{
+ u8 **formats;
+ struct snd_oxfw_stream_formation formation;
+ enum avc_general_plug_dir dir;
+ unsigned int len;
+ int i, err;
+
+ if (s == &oxfw->tx_stream) {
+ formats = oxfw->tx_stream_formats;
+ dir = AVC_GENERAL_PLUG_DIR_OUT;
+ } else {
+ formats = oxfw->rx_stream_formats;
+ dir = AVC_GENERAL_PLUG_DIR_IN;
+ }
+
+ /* Seek stream format for requirements. */
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ err = snd_oxfw_stream_parse_format(formats[i], &formation);
+ if (err < 0)
+ return err;
+
+ if ((formation.rate == rate) && (formation.pcm == pcm_channels))
+ break;
+ }
+ if (i == SND_OXFW_STREAM_FORMAT_ENTRIES)
+ return -EINVAL;
+
+ /* If assumed, just change rate. */
+ if (oxfw->assumed)
+ return set_rate(oxfw, rate);
+
+ /* Calculate format length. */
+ len = 5 + formats[i][4] * 2;
+
+ err = avc_stream_set_format(oxfw->unit, dir, 0, formats[i], len);
+ if (err < 0)
+ return err;
+
+ /* Some requests just after changing format causes freezing. */
+ msleep(100);
+
+ return 0;
+}
+
+static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
+{
+ struct cmp_connection *conn;
+ int err;
+
+ if (stream == &oxfw->rx_stream)
+ conn = &oxfw->in_conn;
+ else
+ conn = &oxfw->out_conn;
+
+ err = cmp_connection_establish(conn);
+ if (err < 0)
+ return err;
+
+ err = amdtp_domain_add_stream(&oxfw->domain, stream,
+ conn->resources.channel, conn->speed);
+ if (err < 0) {
+ cmp_connection_break(conn);
+ return err;
+ }
+
+ return 0;
+}
+
+static int check_connection_used_by_others(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream)
+{
+ struct cmp_connection *conn;
+ bool used;
+ int err;
+
+ if (stream == &oxfw->tx_stream)
+ conn = &oxfw->out_conn;
+ else
+ conn = &oxfw->in_conn;
+
+ err = cmp_connection_check_used(conn, &used);
+ if ((err >= 0) && used && !amdtp_stream_running(stream)) {
+ dev_err(&oxfw->unit->device,
+ "Connection established by others: %cPCR[%d]\n",
+ (conn->direction == CMP_OUTPUT) ? 'o' : 'i',
+ conn->pcr_index);
+ err = -EBUSY;
+ }
+
+ return err;
+}
+
+static int init_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
+{
+ struct cmp_connection *conn;
+ enum cmp_direction c_dir;
+ enum amdtp_stream_direction s_dir;
+ unsigned int flags = 0;
+ int err;
+
+ if (!(oxfw->quirks & SND_OXFW_QUIRK_BLOCKING_TRANSMISSION))
+ flags |= CIP_NONBLOCKING;
+ else
+ flags |= CIP_BLOCKING;
+
+ // OXFW 970/971 has no function to generate playback timing according to the sequence
+ // of value in syt field, thus the packet should include NO_INFO value in the field.
+ // However, some models just ignore data blocks in packet with NO_INFO for audio data
+ // processing.
+ if (!(oxfw->quirks & SND_OXFW_QUIRK_IGNORE_NO_INFO_PACKET))
+ flags |= CIP_UNAWARE_SYT;
+
+ if (stream == &oxfw->tx_stream) {
+ conn = &oxfw->out_conn;
+ c_dir = CMP_OUTPUT;
+ s_dir = AMDTP_IN_STREAM;
+
+ if (oxfw->quirks & SND_OXFW_QUIRK_JUMBO_PAYLOAD)
+ flags |= CIP_JUMBO_PAYLOAD;
+ if (oxfw->quirks & SND_OXFW_QUIRK_WRONG_DBS)
+ flags |= CIP_WRONG_DBS;
+ if (oxfw->quirks & SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS)
+ flags |= CIP_DBC_IS_END_EVENT | CIP_DBC_IS_PAYLOAD_QUADLETS;
+ } else {
+ conn = &oxfw->in_conn;
+ c_dir = CMP_INPUT;
+ s_dir = AMDTP_OUT_STREAM;
+ }
+
+ err = cmp_connection_init(conn, oxfw->unit, c_dir, 0);
+ if (err < 0)
+ return err;
+
+ err = amdtp_am824_init(stream, oxfw->unit, s_dir, flags);
+ if (err < 0) {
+ cmp_connection_destroy(conn);
+ return err;
+ }
+
+ return 0;
+}
+
+static int keep_resources(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
+{
+ enum avc_general_plug_dir dir;
+ u8 **formats;
+ struct snd_oxfw_stream_formation formation;
+ struct cmp_connection *conn;
+ int i;
+ int err;
+
+ if (stream == &oxfw->rx_stream) {
+ dir = AVC_GENERAL_PLUG_DIR_IN;
+ formats = oxfw->rx_stream_formats;
+ conn = &oxfw->in_conn;
+ } else {
+ dir = AVC_GENERAL_PLUG_DIR_OUT;
+ formats = oxfw->tx_stream_formats;
+ conn = &oxfw->out_conn;
+ }
+
+ err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ struct snd_oxfw_stream_formation fmt;
+
+ if (formats[i] == NULL)
+ break;
+
+ err = snd_oxfw_stream_parse_format(formats[i], &fmt);
+ if (err < 0)
+ return err;
+
+ if (fmt.rate == formation.rate && fmt.pcm == formation.pcm &&
+ fmt.midi == formation.midi)
+ break;
+ }
+ if (i == SND_OXFW_STREAM_FORMAT_ENTRIES)
+ return -EINVAL;
+
+ // The stream should have one pcm channels at least.
+ if (formation.pcm == 0)
+ return -EINVAL;
+
+ err = amdtp_am824_set_parameters(stream, formation.rate, formation.pcm,
+ formation.midi * 8, false);
+ if (err < 0)
+ return err;
+
+ return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
+}
+
+int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream,
+ unsigned int rate, unsigned int pcm_channels,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer)
+{
+ struct snd_oxfw_stream_formation formation;
+ enum avc_general_plug_dir dir;
+ int err;
+
+ // Considering JACK/FFADO streaming:
+ // TODO: This can be removed hwdep functionality becomes popular.
+ err = check_connection_used_by_others(oxfw, &oxfw->rx_stream);
+ if (err < 0)
+ return err;
+ if (oxfw->has_output) {
+ err = check_connection_used_by_others(oxfw, &oxfw->tx_stream);
+ if (err < 0)
+ return err;
+ }
+
+ if (stream == &oxfw->tx_stream)
+ dir = AVC_GENERAL_PLUG_DIR_OUT;
+ else
+ dir = AVC_GENERAL_PLUG_DIR_IN;
+
+ err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
+ if (err < 0)
+ return err;
+ if (rate == 0) {
+ rate = formation.rate;
+ pcm_channels = formation.pcm;
+ }
+ if (formation.rate != rate || formation.pcm != pcm_channels) {
+ amdtp_domain_stop(&oxfw->domain);
+
+ cmp_connection_break(&oxfw->in_conn);
+ cmp_connection_release(&oxfw->in_conn);
+
+ if (oxfw->has_output) {
+ cmp_connection_break(&oxfw->out_conn);
+ cmp_connection_release(&oxfw->out_conn);
+ }
+ }
+
+ if (oxfw->substreams_count == 0 ||
+ formation.rate != rate || formation.pcm != pcm_channels) {
+ err = set_stream_format(oxfw, stream, rate, pcm_channels);
+ if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to set stream format: %d\n", err);
+ return err;
+ }
+
+ err = keep_resources(oxfw, &oxfw->rx_stream);
+ if (err < 0)
+ return err;
+
+ if (oxfw->has_output) {
+ err = keep_resources(oxfw, &oxfw->tx_stream);
+ if (err < 0) {
+ cmp_connection_release(&oxfw->in_conn);
+ return err;
+ }
+ }
+
+ err = amdtp_domain_set_events_per_period(&oxfw->domain,
+ frames_per_period, frames_per_buffer);
+ if (err < 0) {
+ cmp_connection_release(&oxfw->in_conn);
+ if (oxfw->has_output)
+ cmp_connection_release(&oxfw->out_conn);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
+{
+ int err;
+
+ if (oxfw->substreams_count == 0)
+ return -EIO;
+
+ if (amdtp_streaming_error(&oxfw->rx_stream) ||
+ amdtp_streaming_error(&oxfw->tx_stream)) {
+ amdtp_domain_stop(&oxfw->domain);
+
+ cmp_connection_break(&oxfw->in_conn);
+ if (oxfw->has_output)
+ cmp_connection_break(&oxfw->out_conn);
+ }
+
+ if (!amdtp_stream_running(&oxfw->rx_stream)) {
+ unsigned int tx_init_skip_cycles = 0;
+ bool replay_seq = false;
+
+ err = start_stream(oxfw, &oxfw->rx_stream);
+ if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to prepare rx stream: %d\n", err);
+ goto error;
+ }
+
+ if (oxfw->has_output &&
+ !amdtp_stream_running(&oxfw->tx_stream)) {
+ err = start_stream(oxfw, &oxfw->tx_stream);
+ if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to prepare tx stream: %d\n", err);
+ goto error;
+ }
+
+ if (oxfw->quirks & SND_OXFW_QUIRK_JUMBO_PAYLOAD) {
+ // Just after changing sampling transfer frequency, many cycles are
+ // skipped for packet transmission.
+ tx_init_skip_cycles = 400;
+ } else if (oxfw->quirks & SND_OXFW_QUIRK_VOLUNTARY_RECOVERY) {
+ // It takes a bit time for target device to adjust event frequency
+ // according to nominal event frequency in isochronous packets from
+ // ALSA oxfw driver.
+ tx_init_skip_cycles = 4000;
+ } else {
+ replay_seq = true;
+ }
+ }
+
+ // NOTE: The device ignores presentation time expressed by the value of syt field
+ // of CIP header in received packets. The sequence of the number of data blocks per
+ // packet is important for media clock recovery.
+ err = amdtp_domain_start(&oxfw->domain, tx_init_skip_cycles, replay_seq, false);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_domain_wait_ready(&oxfw->domain, READY_TIMEOUT_MS)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ amdtp_domain_stop(&oxfw->domain);
+
+ cmp_connection_break(&oxfw->in_conn);
+ if (oxfw->has_output)
+ cmp_connection_break(&oxfw->out_conn);
+
+ return err;
+}
+
+void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw)
+{
+ if (oxfw->substreams_count == 0) {
+ amdtp_domain_stop(&oxfw->domain);
+
+ cmp_connection_break(&oxfw->in_conn);
+ cmp_connection_release(&oxfw->in_conn);
+
+ if (oxfw->has_output) {
+ cmp_connection_break(&oxfw->out_conn);
+ cmp_connection_release(&oxfw->out_conn);
+ }
+ }
+}
+
+static void destroy_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
+{
+ struct cmp_connection *conn;
+
+ if (stream == &oxfw->tx_stream)
+ conn = &oxfw->out_conn;
+ else
+ conn = &oxfw->in_conn;
+
+ amdtp_stream_destroy(stream);
+ cmp_connection_destroy(conn);
+}
+
+int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw)
+{
+ int err;
+
+ err = init_stream(oxfw, &oxfw->rx_stream);
+ if (err < 0)
+ return err;
+
+ if (oxfw->has_output) {
+ err = init_stream(oxfw, &oxfw->tx_stream);
+ if (err < 0) {
+ destroy_stream(oxfw, &oxfw->rx_stream);
+ return err;
+ }
+ }
+
+ err = amdtp_domain_init(&oxfw->domain);
+ if (err < 0) {
+ destroy_stream(oxfw, &oxfw->rx_stream);
+ if (oxfw->has_output)
+ destroy_stream(oxfw, &oxfw->tx_stream);
+ }
+
+ return err;
+}
+
+// This function should be called before starting the stream or after stopping
+// the streams.
+void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw)
+{
+ amdtp_domain_destroy(&oxfw->domain);
+
+ destroy_stream(oxfw, &oxfw->rx_stream);
+
+ if (oxfw->has_output)
+ destroy_stream(oxfw, &oxfw->tx_stream);
+}
+
+void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw)
+{
+ amdtp_domain_stop(&oxfw->domain);
+
+ cmp_connection_break(&oxfw->in_conn);
+
+ amdtp_stream_pcm_abort(&oxfw->rx_stream);
+
+ if (oxfw->has_output) {
+ cmp_connection_break(&oxfw->out_conn);
+
+ amdtp_stream_pcm_abort(&oxfw->tx_stream);
+ }
+}
+
+int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
+ enum avc_general_plug_dir dir,
+ struct snd_oxfw_stream_formation *formation)
+{
+ int err;
+
+ if (!(oxfw->quirks & SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED)) {
+ u8 *format;
+ unsigned int len;
+
+ len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
+ format = kmalloc(len, GFP_KERNEL);
+ if (format == NULL)
+ return -ENOMEM;
+
+ err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len);
+ if (err >= 0) {
+ if (len < 3)
+ err = -EIO;
+ else
+ err = snd_oxfw_stream_parse_format(format, formation);
+ }
+
+ kfree(format);
+ } else {
+ // Miglia Harmony Audio does not support Extended Stream Format Information
+ // command. Use the duplicated hard-coded format, instead.
+ unsigned int rate;
+ u8 *const *formats;
+ int i;
+
+ err = avc_general_get_sig_fmt(oxfw->unit, &rate, dir, 0);
+ if (err < 0)
+ return err;
+
+ if (dir == AVC_GENERAL_PLUG_DIR_IN)
+ formats = oxfw->rx_stream_formats;
+ else
+ formats = oxfw->tx_stream_formats;
+
+ for (i = 0; (i < SND_OXFW_STREAM_FORMAT_ENTRIES); ++i) {
+ if (!formats[i])
+ continue;
+
+ err = snd_oxfw_stream_parse_format(formats[i], formation);
+ if (err < 0)
+ continue;
+
+ if (formation->rate == rate)
+ break;
+ }
+ if (i == SND_OXFW_STREAM_FORMAT_ENTRIES)
+ return -EIO;
+ }
+
+ return err;
+}
+
+/*
+ * See Table 6.16 - AM824 Stream Format
+ * Figure 6.19 - format_information field for AM824 Compound
+ * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA)
+ * Also 'Clause 12 AM824 sequence adaption layers' in IEC 61883-6:2005
+ */
+int snd_oxfw_stream_parse_format(const u8 *format,
+ struct snd_oxfw_stream_formation *formation)
+{
+ unsigned int i, e, channels, type;
+
+ memset(formation, 0, sizeof(struct snd_oxfw_stream_formation));
+
+ /*
+ * this module can support a hierarchy combination that:
+ * Root: Audio and Music (0x90)
+ * Level 1: AM824 Compound (0x40)
+ */
+ if ((format[0] != 0x90) || (format[1] != 0x40))
+ return -ENXIO;
+
+ /* check the sampling rate */
+ for (i = 0; i < ARRAY_SIZE(avc_stream_rate_table); i++) {
+ if (format[2] == avc_stream_rate_table[i])
+ break;
+ }
+ if (i == ARRAY_SIZE(avc_stream_rate_table))
+ return -ENXIO;
+
+ formation->rate = oxfw_rate_table[i];
+
+ for (e = 0; e < format[4]; e++) {
+ channels = format[5 + e * 2];
+ type = format[6 + e * 2];
+
+ switch (type) {
+ /* IEC 60958 Conformant, currently handled as MBLA */
+ case 0x00:
+ /* Multi Bit Linear Audio (Raw) */
+ case 0x06:
+ formation->pcm += channels;
+ break;
+ /* MIDI Conformant */
+ case 0x0d:
+ formation->midi = channels;
+ break;
+ /* IEC 61937-3 to 7 */
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ /* Multi Bit Linear Audio */
+ case 0x07: /* DVD-Audio */
+ case 0x0c: /* High Precision */
+ /* One Bit Audio */
+ case 0x08: /* (Plain) Raw */
+ case 0x09: /* (Plain) SACD */
+ case 0x0a: /* (Encoded) Raw */
+ case 0x0b: /* (Encoded) SACD */
+ /* SMPTE Time-Code conformant */
+ case 0x0e:
+ /* Sample Count */
+ case 0x0f:
+ /* Anciliary Data */
+ case 0x10:
+ /* Synchronization Stream (Stereo Raw audio) */
+ case 0x40:
+ /* Don't care */
+ case 0xff:
+ default:
+ return -ENXIO; /* not supported */
+ }
+ }
+
+ if (formation->pcm > AM824_MAX_CHANNELS_FOR_PCM ||
+ formation->midi > AM824_MAX_CHANNELS_FOR_MIDI)
+ return -ENXIO;
+
+ return 0;
+}
+
+static int
+assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir,
+ unsigned int pid, u8 *buf, unsigned int *len,
+ u8 **formats)
+{
+ struct snd_oxfw_stream_formation formation;
+ unsigned int i, eid;
+ int err;
+
+ // get format at current sampling rate.
+ if (!(oxfw->quirks & SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED)) {
+ err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len);
+ if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to get current stream format for isoc %s plug %d:%d\n",
+ (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out",
+ pid, err);
+ goto end;
+ }
+ } else {
+ // Miglia Harmony Audio does not support Extended Stream Format Information
+ // command. Use the hard-coded format, instead.
+ buf[0] = 0x90;
+ buf[1] = 0x40;
+ buf[2] = avc_stream_rate_table[0];
+ buf[3] = 0x00;
+ buf[4] = 0x01;
+
+ if (dir == AVC_GENERAL_PLUG_DIR_IN)
+ buf[5] = 0x08;
+ else
+ buf[5] = 0x02;
+
+ buf[6] = 0x06;
+
+ *len = 7;
+ }
+
+ /* parse and set stream format */
+ eid = 0;
+ err = snd_oxfw_stream_parse_format(buf, &formation);
+ if (err < 0)
+ goto end;
+
+ formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, *len,
+ GFP_KERNEL);
+ if (!formats[eid]) {
+ err = -ENOMEM;
+ goto end;
+ }
+
+ /* apply the format for each available sampling rate */
+ for (i = 0; i < ARRAY_SIZE(oxfw_rate_table); i++) {
+ if (formation.rate == oxfw_rate_table[i])
+ continue;
+
+ err = avc_general_inquiry_sig_fmt(oxfw->unit,
+ oxfw_rate_table[i],
+ dir, pid);
+ if (err < 0)
+ continue;
+
+ eid++;
+ formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, *len,
+ GFP_KERNEL);
+ if (formats[eid] == NULL) {
+ err = -ENOMEM;
+ goto end;
+ }
+ formats[eid][2] = avc_stream_rate_table[i];
+ }
+
+ err = 0;
+ oxfw->assumed = true;
+end:
+ return err;
+}
+
+static int fill_stream_formats(struct snd_oxfw *oxfw,
+ enum avc_general_plug_dir dir,
+ unsigned short pid)
+{
+ u8 *buf, **formats;
+ unsigned int len, eid = 0;
+ struct snd_oxfw_stream_formation dummy;
+ int err;
+
+ buf = kmalloc(AVC_GENERIC_FRAME_MAXIMUM_BYTES, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ if (dir == AVC_GENERAL_PLUG_DIR_OUT)
+ formats = oxfw->tx_stream_formats;
+ else
+ formats = oxfw->rx_stream_formats;
+
+ /* get first entry */
+ len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
+ err = avc_stream_get_format_list(oxfw->unit, dir, 0, buf, &len, 0);
+ if (err == -ENXIO) {
+ /* LIST subfunction is not implemented */
+ len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
+ err = assume_stream_formats(oxfw, dir, pid, buf, &len,
+ formats);
+ goto end;
+ } else if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to get stream format %d for isoc %s plug %d:%d\n",
+ eid, (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out",
+ pid, err);
+ goto end;
+ }
+
+ /* LIST subfunction is implemented */
+ while (eid < SND_OXFW_STREAM_FORMAT_ENTRIES) {
+ /* The format is too short. */
+ if (len < 3) {
+ err = -EIO;
+ break;
+ }
+
+ /* parse and set stream format */
+ err = snd_oxfw_stream_parse_format(buf, &dummy);
+ if (err < 0)
+ break;
+
+ formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, len,
+ GFP_KERNEL);
+ if (!formats[eid]) {
+ err = -ENOMEM;
+ break;
+ }
+
+ /* get next entry */
+ len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
+ err = avc_stream_get_format_list(oxfw->unit, dir, 0,
+ buf, &len, ++eid);
+ /* No entries remained. */
+ if (err == -EINVAL) {
+ err = 0;
+ break;
+ } else if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to get stream format %d for isoc %s plug %d:%d\n",
+ eid, (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" :
+ "out",
+ pid, err);
+ break;
+ }
+ }
+end:
+ kfree(buf);
+ return err;
+}
+
+int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
+{
+ u8 plugs[AVC_PLUG_INFO_BUF_BYTES];
+ struct snd_oxfw_stream_formation formation;
+ u8 *format;
+ unsigned int i;
+ int err;
+
+ /* the number of plugs for isoc in/out, ext in/out */
+ err = avc_general_get_plug_info(oxfw->unit, 0x1f, 0x07, 0x00, plugs);
+ if (err < 0) {
+ dev_err(&oxfw->unit->device,
+ "fail to get info for isoc/external in/out plugs: %d\n",
+ err);
+ goto end;
+ } else if ((plugs[0] == 0) && (plugs[1] == 0)) {
+ err = -ENXIO;
+ goto end;
+ }
+
+ /* use oPCR[0] if exists */
+ if (plugs[1] > 0) {
+ err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0);
+ if (err < 0) {
+ if (err != -ENXIO)
+ return err;
+
+ // The oPCR is not available for isoc communication.
+ err = 0;
+ } else {
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ format = oxfw->tx_stream_formats[i];
+ if (format == NULL)
+ continue;
+ err = snd_oxfw_stream_parse_format(format,
+ &formation);
+ if (err < 0)
+ continue;
+
+ /* Add one MIDI port. */
+ if (formation.midi > 0)
+ oxfw->midi_input_ports = 1;
+ }
+
+ oxfw->has_output = true;
+ }
+ }
+
+ /* use iPCR[0] if exists */
+ if (plugs[0] > 0) {
+ err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0);
+ if (err < 0) {
+ if (err != -ENXIO)
+ return err;
+
+ // The iPCR is not available for isoc communication.
+ err = 0;
+ } else {
+ for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+ format = oxfw->rx_stream_formats[i];
+ if (format == NULL)
+ continue;
+ err = snd_oxfw_stream_parse_format(format,
+ &formation);
+ if (err < 0)
+ continue;
+
+ /* Add one MIDI port. */
+ if (formation.midi > 0)
+ oxfw->midi_output_ports = 1;
+ }
+
+ oxfw->has_input = true;
+ }
+ }
+end:
+ return err;
+}
+
+void snd_oxfw_stream_lock_changed(struct snd_oxfw *oxfw)
+{
+ oxfw->dev_lock_changed = true;
+ wake_up(&oxfw->hwdep_wait);
+}
+
+int snd_oxfw_stream_lock_try(struct snd_oxfw *oxfw)
+{
+ guard(spinlock_irq)(&oxfw->lock);
+
+ /* user land lock this */
+ if (oxfw->dev_lock_count < 0)
+ return -EBUSY;
+
+ /* this is the first time */
+ if (oxfw->dev_lock_count++ == 0)
+ snd_oxfw_stream_lock_changed(oxfw);
+ return 0;
+}
+
+void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw)
+{
+ guard(spinlock_irq)(&oxfw->lock);
+
+ if (WARN_ON(oxfw->dev_lock_count <= 0))
+ return;
+ if (--oxfw->dev_lock_count == 0)
+ snd_oxfw_stream_lock_changed(oxfw);
+}
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
new file mode 100644
index 000000000000..5039bd79b18e
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw.c
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * oxfw.c - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include "oxfw.h"
+
+#define OXFORD_FIRMWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x50000)
+/* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */
+
+#define OXFORD_HARDWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x90020)
+#define OXFORD_HARDWARE_ID_OXFW970 0x39443841
+#define OXFORD_HARDWARE_ID_OXFW971 0x39373100
+
+#define VENDOR_LOUD 0x000ff2
+#define VENDOR_GRIFFIN 0x001292
+#define VENDOR_BEHRINGER 0x001564
+#define VENDOR_LACIE 0x00d04b
+#define VENDOR_TASCAM 0x00022e
+#define OUI_STANTON 0x001260
+#define OUI_APOGEE 0x0003db
+#define OUI_OXFORD 0x0030e0
+
+#define MODEL_SATELLITE 0x00200f
+#define MODEL_SCS1M 0x001000
+#define MODEL_DUET_FW 0x01dddd
+#define MODEL_ONYX_1640I 0x001640
+
+#define SPECIFIER_1394TA 0x00a02d
+#define VERSION_AVC 0x010001
+
+MODULE_DESCRIPTION("Oxford Semiconductor FW970/971 driver");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("snd-firewire-speakers");
+MODULE_ALIAS("snd-scs1x");
+
+struct compat_info {
+ const char *driver_name;
+ const char *vendor_name;
+ const char *model_name;
+};
+
+static bool detect_loud_models(struct fw_unit *unit)
+{
+ static const char *const models[] = {
+ "Onyxi",
+ "Onyx-i",
+ "Onyx 1640i",
+ "d.Pro",
+ "U.420"};
+ char model[32];
+ int err;
+
+ err = fw_csr_string(unit->directory, CSR_MODEL,
+ model, sizeof(model));
+ if (err < 0)
+ return false;
+
+ return match_string(models, ARRAY_SIZE(models), model) >= 0;
+}
+
+static int name_card(struct snd_oxfw *oxfw, const struct ieee1394_device_id *entry)
+{
+ struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
+ const struct compat_info *info;
+ char vendor[24];
+ char model[32];
+ const char *d, *v, *m;
+ u32 firmware;
+ int err;
+
+ /* get vendor name from root directory */
+ err = fw_csr_string(fw_dev->config_rom + 5, CSR_VENDOR,
+ vendor, sizeof(vendor));
+ if (err < 0)
+ goto end;
+
+ /* get model name from unit directory */
+ err = fw_csr_string(oxfw->unit->directory, CSR_MODEL,
+ model, sizeof(model));
+ if (err < 0)
+ goto end;
+
+ err = snd_fw_transaction(oxfw->unit, TCODE_READ_QUADLET_REQUEST,
+ OXFORD_FIRMWARE_ID_ADDRESS, &firmware, 4, 0);
+ if (err < 0)
+ goto end;
+ be32_to_cpus(&firmware);
+
+ if (firmware >> 20 == 0x970)
+ oxfw->quirks |= SND_OXFW_QUIRK_JUMBO_PAYLOAD;
+
+ /* to apply card definitions */
+ if (entry->vendor_id == VENDOR_GRIFFIN || entry->vendor_id == VENDOR_LACIE) {
+ info = (const struct compat_info *)entry->driver_data;
+ d = info->driver_name;
+ v = info->vendor_name;
+ m = info->model_name;
+ } else {
+ d = "OXFW";
+ v = vendor;
+ m = model;
+ }
+
+ strscpy(oxfw->card->driver, d);
+ strscpy(oxfw->card->mixername, m);
+ strscpy(oxfw->card->shortname, m);
+
+ scnprintf(oxfw->card->longname, sizeof(oxfw->card->longname),
+ "%s %s (OXFW%x %04x), GUID %08x%08x at %s, S%d",
+ v, m, firmware >> 20, firmware & 0xffff,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&oxfw->unit->device), 100 << fw_dev->max_speed);
+end:
+ return err;
+}
+
+static void oxfw_card_free(struct snd_card *card)
+{
+ struct snd_oxfw *oxfw = card->private_data;
+
+ if (oxfw->has_output || oxfw->has_input)
+ snd_oxfw_stream_destroy_duplex(oxfw);
+
+ mutex_destroy(&oxfw->mutex);
+ fw_unit_put(oxfw->unit);
+}
+
+static int detect_quirks(struct snd_oxfw *oxfw, const struct ieee1394_device_id *entry)
+{
+ struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
+ struct fw_csr_iterator it;
+ int key, val;
+ int vendor, model;
+
+ /*
+ * Add ALSA control elements for two models to keep compatibility to
+ * old firewire-speaker module.
+ */
+ if (entry->vendor_id == VENDOR_GRIFFIN)
+ return snd_oxfw_add_spkr(oxfw, false);
+ if (entry->vendor_id == VENDOR_LACIE)
+ return snd_oxfw_add_spkr(oxfw, true);
+
+ /*
+ * Stanton models supports asynchronous transactions for unique MIDI
+ * messages.
+ */
+ if (entry->vendor_id == OUI_STANTON) {
+ oxfw->quirks |= SND_OXFW_QUIRK_SCS_TRANSACTION;
+ if (entry->model_id == MODEL_SCS1M)
+ oxfw->quirks |= SND_OXFW_QUIRK_BLOCKING_TRANSMISSION;
+
+ // No physical MIDI ports.
+ oxfw->midi_input_ports = 0;
+ oxfw->midi_output_ports = 0;
+
+ return snd_oxfw_scs1x_add(oxfw);
+ }
+
+ if (entry->vendor_id == OUI_APOGEE && entry->model_id == MODEL_DUET_FW) {
+ oxfw->quirks |= SND_OXFW_QUIRK_BLOCKING_TRANSMISSION |
+ SND_OXFW_QUIRK_IGNORE_NO_INFO_PACKET;
+ }
+
+ /*
+ * TASCAM FireOne has physical control and requires a pair of additional
+ * MIDI ports.
+ */
+ if (entry->vendor_id == VENDOR_TASCAM) {
+ oxfw->midi_input_ports++;
+ oxfw->midi_output_ports++;
+ return 0;
+ }
+
+ /* Seek from Root Directory of Config ROM. */
+ vendor = model = 0;
+ fw_csr_iterator_init(&it, fw_dev->config_rom + 5);
+ while (fw_csr_iterator_next(&it, &key, &val)) {
+ if (key == CSR_VENDOR)
+ vendor = val;
+ else if (key == CSR_MODEL)
+ model = val;
+ }
+
+ if (vendor == VENDOR_LOUD) {
+ // Mackie Onyx Satellite with base station has a quirk to report a wrong
+ // value in 'dbs' field of CIP header against its format information.
+ oxfw->quirks |= SND_OXFW_QUIRK_WRONG_DBS;
+
+ // OXFW971-based models may transfer events by blocking method.
+ if (!(oxfw->quirks & SND_OXFW_QUIRK_JUMBO_PAYLOAD))
+ oxfw->quirks |= SND_OXFW_QUIRK_BLOCKING_TRANSMISSION;
+
+ if (model == MODEL_ONYX_1640I) {
+ //Unless receiving packets without NOINFO packet, the device transfers
+ //mostly half of events in packets than expected.
+ oxfw->quirks |= SND_OXFW_QUIRK_IGNORE_NO_INFO_PACKET |
+ SND_OXFW_QUIRK_VOLUNTARY_RECOVERY;
+ }
+ }
+
+ return 0;
+}
+
+static int oxfw_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_oxfw *oxfw;
+ int err;
+
+ if (entry->vendor_id == VENDOR_LOUD && entry->model_id == 0 && !detect_loud_models(unit))
+ return -ENODEV;
+
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*oxfw), &card);
+ if (err < 0)
+ return err;
+ card->private_free = oxfw_card_free;
+
+ oxfw = card->private_data;
+ oxfw->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, oxfw);
+ oxfw->card = card;
+
+ mutex_init(&oxfw->mutex);
+ spin_lock_init(&oxfw->lock);
+ init_waitqueue_head(&oxfw->hwdep_wait);
+
+ err = name_card(oxfw, entry);
+ if (err < 0)
+ goto error;
+
+ if (entry->vendor_id == OUI_OXFORD && entry->model_id == 0x00f970) {
+ oxfw->quirks |= SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED |
+ SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS;
+ }
+
+ err = snd_oxfw_stream_discover(oxfw);
+ if (err < 0)
+ goto error;
+
+ err = detect_quirks(oxfw, entry);
+ if (err < 0)
+ goto error;
+
+ if (oxfw->has_output || oxfw->has_input) {
+ err = snd_oxfw_stream_init_duplex(oxfw);
+ if (err < 0)
+ goto error;
+
+ err = snd_oxfw_create_pcm(oxfw);
+ if (err < 0)
+ goto error;
+
+ snd_oxfw_proc_init(oxfw);
+
+ err = snd_oxfw_create_midi(oxfw);
+ if (err < 0)
+ goto error;
+
+ err = snd_oxfw_create_hwdep(oxfw);
+ if (err < 0)
+ goto error;
+ }
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void oxfw_bus_reset(struct fw_unit *unit)
+{
+ struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
+
+ fcp_bus_reset(oxfw->unit);
+
+ if (oxfw->has_output || oxfw->has_input) {
+ guard(mutex)(&oxfw->mutex);
+ snd_oxfw_stream_update_duplex(oxfw);
+ }
+
+ if (oxfw->quirks & SND_OXFW_QUIRK_SCS_TRANSACTION)
+ snd_oxfw_scs1x_update(oxfw);
+}
+
+static void oxfw_remove(struct fw_unit *unit)
+{
+ struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
+
+ // Block till all of ALSA character devices are released.
+ snd_card_free(oxfw->card);
+}
+
+static const struct compat_info griffin_firewave = {
+ .driver_name = "FireWave",
+ .vendor_name = "Griffin",
+ .model_name = "FireWave",
+};
+
+static const struct compat_info lacie_speakers = {
+ .driver_name = "FWSpeakers",
+ .vendor_name = "LaCie",
+ .model_name = "FireWire Speakers",
+};
+
+#define OXFW_DEV_ENTRY(vendor, model, data) \
+{ \
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | \
+ IEEE1394_MATCH_MODEL_ID | \
+ IEEE1394_MATCH_SPECIFIER_ID | \
+ IEEE1394_MATCH_VERSION, \
+ .vendor_id = vendor, \
+ .model_id = model, \
+ .specifier_id = SPECIFIER_1394TA, \
+ .version = VERSION_AVC, \
+ .driver_data = (kernel_ulong_t)data, \
+}
+
+static const struct ieee1394_device_id oxfw_id_table[] = {
+ //
+ // OXFW970 devices:
+ // Initial firmware has a quirk to postpone isoc packet transmission during finishing async
+ // transaction. As a result, several isochronous cycles are skipped to transfer the packets
+ // and the audio data frames which should have been transferred during the cycles are put
+ // into packet at the first isoc cycle after the postpone. Furthermore, the value of SYT
+ // field in CIP header is not reliable as synchronization timing,
+ //
+ OXFW_DEV_ENTRY(VENDOR_GRIFFIN, 0x00f970, &griffin_firewave),
+ OXFW_DEV_ENTRY(VENDOR_LACIE, 0x00f970, &lacie_speakers),
+ // Miglia HarmonyAudio (HA02). The numeric vendor ID is ASIC vendor and the model ID is the
+ // default value of ASIC.
+ OXFW_DEV_ENTRY(OUI_OXFORD, 0x00f970, NULL),
+ // Behringer,F-Control Audio 202. The value of SYT field is not reliable at all.
+ OXFW_DEV_ENTRY(VENDOR_BEHRINGER, 0x00fc22, NULL),
+ // Loud Technologies, Tapco Link.FireWire 4x6. The value of SYT field is always 0xffff.
+ OXFW_DEV_ENTRY(VENDOR_LOUD, 0x000460, NULL),
+ // Loud Technologies, Mackie Onyx Satellite. Although revised version of firmware is
+ // installed to avoid the postpone, the value of SYT field is always 0xffff.
+ OXFW_DEV_ENTRY(VENDOR_LOUD, MODEL_SATELLITE, NULL),
+
+ //
+ // OXFW971 devices:
+ // The value of SYT field in CIP header is enough reliable. Both of blocking and non-blocking
+ // transmission methods are available.
+ //
+ // Any Mackie(Loud) models (name string/model id):
+ // Onyx-i series (former models): 0x081216
+ // Onyx 1640i: 0x001640
+ // d.2 pro/d.4 pro (built-in card): Unknown
+ // U.420: Unknown
+ // U.420d: Unknown
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
+ .vendor_id = VENDOR_LOUD,
+ .model_id = 0,
+ .specifier_id = SPECIFIER_1394TA,
+ .version = VERSION_AVC,
+ },
+ // TASCAM, FireOne.
+ OXFW_DEV_ENTRY(VENDOR_TASCAM, 0x800007, NULL),
+ // Stanton, Stanton Controllers & Systems 1 Mixer (SCS.1m).
+ OXFW_DEV_ENTRY(OUI_STANTON, MODEL_SCS1M, NULL),
+ // Stanton, Stanton Controllers & Systems 1 Deck (SCS.1d).
+ OXFW_DEV_ENTRY(OUI_STANTON, 0x002000, NULL),
+ // APOGEE, duet FireWire.
+ OXFW_DEV_ENTRY(OUI_APOGEE, MODEL_DUET_FW, NULL),
+ { }
+};
+MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table);
+
+static struct fw_driver oxfw_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = oxfw_probe,
+ .update = oxfw_bus_reset,
+ .remove = oxfw_remove,
+ .id_table = oxfw_id_table,
+};
+
+static int __init snd_oxfw_init(void)
+{
+ return driver_register(&oxfw_driver.driver);
+}
+
+static void __exit snd_oxfw_exit(void)
+{
+ driver_unregister(&oxfw_driver.driver);
+}
+
+module_init(snd_oxfw_init);
+module_exit(snd_oxfw_exit);
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
new file mode 100644
index 000000000000..39ea9a6dde33
--- /dev/null
+++ b/sound/firewire/oxfw/oxfw.h
@@ -0,0 +1,166 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * oxfw.h - a part of driver for OXFW970/971 based devices
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/sched/signal.h>
+
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+
+#include "../lib.h"
+#include "../fcp.h"
+#include "../packets-buffer.h"
+#include "../iso-resources.h"
+#include "../amdtp-am824.h"
+#include "../cmp.h"
+
+enum snd_oxfw_quirk {
+ // Postpone transferring packets during handling asynchronous transaction. As a result,
+ // next isochronous packet includes more events than one packet can include.
+ SND_OXFW_QUIRK_JUMBO_PAYLOAD = 0x01,
+ // The dbs field of CIP header in tx packet is wrong.
+ SND_OXFW_QUIRK_WRONG_DBS = 0x02,
+ // Blocking transmission mode is used.
+ SND_OXFW_QUIRK_BLOCKING_TRANSMISSION = 0x04,
+ // Stanton SCS1.d and SCS1.m support unique transaction.
+ SND_OXFW_QUIRK_SCS_TRANSACTION = 0x08,
+ // Apogee Duet FireWire ignores data blocks in packet with NO_INFO for audio data
+ // processing, while output level meter moves. Any value in syt field of packet takes
+ // the device to process audio data even if the value is invalid in a point of
+ // IEC 61883-1/6.
+ SND_OXFW_QUIRK_IGNORE_NO_INFO_PACKET = 0x10,
+ // Loud Technologies Mackie Onyx 1640i seems to configure OXFW971 ASIC so that it decides
+ // event frequency according to events in received isochronous packets. The device looks to
+ // performs media clock recovery voluntarily. In the recovery, the packets with NO_INFO
+ // are ignored, thus driver should transfer packets with timestamp.
+ SND_OXFW_QUIRK_VOLUNTARY_RECOVERY = 0x20,
+ // Miglia Harmony Audio does not support AV/C Stream Format Information command.
+ SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED = 0x40,
+ // Miglia Harmony Audio transmits CIP in which the value of dbc field expresses the number
+ // of accumulated payload quadlets including the packet.
+ SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS = 0x80,
+};
+
+/* This is an arbitrary number for convinience. */
+#define SND_OXFW_STREAM_FORMAT_ENTRIES 10
+struct snd_oxfw {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ struct mutex mutex;
+ spinlock_t lock;
+
+ // The combination of snd_oxfw_quirk enumeration-constants.
+ unsigned int quirks;
+ bool has_output;
+ bool has_input;
+ u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
+ u8 *rx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
+ bool assumed;
+ struct cmp_connection out_conn;
+ struct cmp_connection in_conn;
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ unsigned int substreams_count;
+
+ unsigned int midi_input_ports;
+ unsigned int midi_output_ports;
+
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ void *spec;
+
+ struct amdtp_domain domain;
+};
+
+/*
+ * AV/C Stream Format Information Specification 1.1 Working Draft
+ * (Apr 2005, 1394TA)
+ */
+int avc_stream_set_format(struct fw_unit *unit, enum avc_general_plug_dir dir,
+ unsigned int pid, u8 *format, unsigned int len);
+int avc_stream_get_format(struct fw_unit *unit,
+ enum avc_general_plug_dir dir, unsigned int pid,
+ u8 *buf, unsigned int *len, unsigned int eid);
+static inline int
+avc_stream_get_format_single(struct fw_unit *unit,
+ enum avc_general_plug_dir dir, unsigned int pid,
+ u8 *buf, unsigned int *len)
+{
+ return avc_stream_get_format(unit, dir, pid, buf, len, 0xff);
+}
+static inline int
+avc_stream_get_format_list(struct fw_unit *unit,
+ enum avc_general_plug_dir dir, unsigned int pid,
+ u8 *buf, unsigned int *len,
+ unsigned int eid)
+{
+ return avc_stream_get_format(unit, dir, pid, buf, len, eid);
+}
+
+/*
+ * AV/C Digital Interface Command Set General Specification 4.2
+ * (Sep 2004, 1394TA)
+ */
+int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate,
+ enum avc_general_plug_dir dir,
+ unsigned short pid);
+
+int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw);
+int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw,
+ struct amdtp_stream *stream,
+ unsigned int rate, unsigned int pcm_channels,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer);
+int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw);
+void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw);
+void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw);
+void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw);
+
+struct snd_oxfw_stream_formation {
+ unsigned int rate;
+ unsigned int pcm;
+ unsigned int midi;
+};
+int snd_oxfw_stream_parse_format(const u8 *format,
+ struct snd_oxfw_stream_formation *formation);
+int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
+ enum avc_general_plug_dir dir,
+ struct snd_oxfw_stream_formation *formation);
+
+int snd_oxfw_stream_discover(struct snd_oxfw *oxfw);
+
+void snd_oxfw_stream_lock_changed(struct snd_oxfw *oxfw);
+int snd_oxfw_stream_lock_try(struct snd_oxfw *oxfw);
+void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw);
+
+int snd_oxfw_create_pcm(struct snd_oxfw *oxfw);
+
+void snd_oxfw_proc_init(struct snd_oxfw *oxfw);
+
+int snd_oxfw_create_midi(struct snd_oxfw *oxfw);
+
+int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw);
+
+int snd_oxfw_add_spkr(struct snd_oxfw *oxfw, bool is_lacie);
+int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw);
+void snd_oxfw_scs1x_update(struct snd_oxfw *oxfw);
diff --git a/sound/firewire/packets-buffer.c b/sound/firewire/packets-buffer.c
new file mode 100644
index 000000000000..0ecafd0c6722
--- /dev/null
+++ b/sound/firewire/packets-buffer.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * helpers for managing a buffer for many packets
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ */
+
+#include <linux/firewire.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include "packets-buffer.h"
+
+/**
+ * iso_packets_buffer_init - allocates the memory for packets
+ * @b: the buffer structure to initialize
+ * @unit: the device at the other end of the stream
+ * @count: the number of packets
+ * @packet_size: the (maximum) size of a packet, in bytes
+ * @direction: %DMA_TO_DEVICE or %DMA_FROM_DEVICE
+ */
+int iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit,
+ unsigned int count, unsigned int packet_size,
+ enum dma_data_direction direction)
+{
+ unsigned int packets_per_page, pages;
+ unsigned int i, page_index, offset_in_page;
+ void *p;
+ int err;
+
+ b->packets = kmalloc_array(count, sizeof(*b->packets), GFP_KERNEL);
+ if (!b->packets) {
+ err = -ENOMEM;
+ goto error;
+ }
+
+ packet_size = L1_CACHE_ALIGN(packet_size);
+ packets_per_page = PAGE_SIZE / packet_size;
+ if (WARN_ON(!packets_per_page)) {
+ err = -EINVAL;
+ goto err_packets;
+ }
+ pages = DIV_ROUND_UP(count, packets_per_page);
+
+ err = fw_iso_buffer_init(&b->iso_buffer, fw_parent_device(unit)->card,
+ pages, direction);
+ if (err < 0)
+ goto err_packets;
+
+ for (i = 0; i < count; ++i) {
+ page_index = i / packets_per_page;
+ p = page_address(b->iso_buffer.pages[page_index]);
+ offset_in_page = (i % packets_per_page) * packet_size;
+ b->packets[i].buffer = p + offset_in_page;
+ b->packets[i].offset = page_index * PAGE_SIZE + offset_in_page;
+ }
+
+ return 0;
+
+err_packets:
+ kfree(b->packets);
+error:
+ return err;
+}
+EXPORT_SYMBOL(iso_packets_buffer_init);
+
+/**
+ * iso_packets_buffer_destroy - frees packet buffer resources
+ * @b: the buffer structure to free
+ * @unit: the device at the other end of the stream
+ */
+void iso_packets_buffer_destroy(struct iso_packets_buffer *b,
+ struct fw_unit *unit)
+{
+ fw_iso_buffer_destroy(&b->iso_buffer, fw_parent_device(unit)->card);
+ kfree(b->packets);
+}
+EXPORT_SYMBOL(iso_packets_buffer_destroy);
diff --git a/sound/firewire/packets-buffer.h b/sound/firewire/packets-buffer.h
new file mode 100644
index 000000000000..99e963c271e1
--- /dev/null
+++ b/sound/firewire/packets-buffer.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef SOUND_FIREWIRE_PACKETS_BUFFER_H_INCLUDED
+#define SOUND_FIREWIRE_PACKETS_BUFFER_H_INCLUDED
+
+#include <linux/dma-mapping.h>
+#include <linux/firewire.h>
+
+/**
+ * struct iso_packets_buffer - manages a buffer for many packets
+ * @iso_buffer: the memory containing the packets
+ * @packets: an array, with each element pointing to one packet
+ */
+struct iso_packets_buffer {
+ struct fw_iso_buffer iso_buffer;
+ struct {
+ void *buffer;
+ unsigned int offset;
+ } *packets;
+};
+
+int iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit,
+ unsigned int count, unsigned int packet_size,
+ enum dma_data_direction direction);
+void iso_packets_buffer_destroy(struct iso_packets_buffer *b,
+ struct fw_unit *unit);
+
+#endif
diff --git a/sound/firewire/tascam/Makefile b/sound/firewire/tascam/Makefile
new file mode 100644
index 000000000000..43fed14cf172
--- /dev/null
+++ b/sound/firewire/tascam/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-firewire-tascam-y := tascam-proc.o amdtp-tascam.o tascam-stream.o \
+ tascam-pcm.o tascam-hwdep.o tascam-transaction.o \
+ tascam-midi.o tascam.o
+obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o
diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c
new file mode 100644
index 000000000000..59c339d9b5fb
--- /dev/null
+++ b/sound/firewire/tascam/amdtp-tascam.c
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * amdtp-tascam.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ */
+
+#include <sound/pcm.h>
+#include "tascam.h"
+
+#define AMDTP_FMT_TSCM_TX 0x1e
+#define AMDTP_FMT_TSCM_RX 0x3e
+
+struct amdtp_tscm {
+ unsigned int pcm_channels;
+};
+
+int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate)
+{
+ struct amdtp_tscm *p = s->protocol;
+ unsigned int data_channels;
+
+ if (amdtp_stream_running(s))
+ return -EBUSY;
+
+ data_channels = p->pcm_channels;
+
+ /* Packets in in-stream have extra 2 data channels. */
+ if (s->direction == AMDTP_IN_STREAM)
+ data_channels += 2;
+
+ return amdtp_stream_set_parameters(s, rate, data_channels, 1);
+}
+
+static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames,
+ unsigned int pcm_frames)
+{
+ struct amdtp_tscm *p = s->protocol;
+ unsigned int channels = p->pcm_channels;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ const u32 *src;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_be32(*src);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames,
+ unsigned int pcm_frames)
+{
+ struct amdtp_tscm *p = s->protocol;
+ unsigned int channels = p->pcm_channels;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int pcm_buffer_pointer;
+ int remaining_frames;
+ u32 *dst;
+ int i, c;
+
+ pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
+ pcm_buffer_pointer %= runtime->buffer_size;
+
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
+
+ /* The first data channel is for event counter. */
+ buffer += 1;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ *dst = be32_to_cpu(buffer[c]);
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_tscm *p = s->protocol;
+ unsigned int channels, i, c;
+
+ channels = p->pcm_channels;
+
+ for (i = 0; i < data_blocks; ++i) {
+ for (c = 0; c < channels; ++c)
+ buffer[c] = 0x00000000;
+ buffer += s->data_block_quadlets;
+ }
+}
+
+int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ /*
+ * Our implementation allows this protocol to deliver 24 bit sample in
+ * 32bit data channel.
+ */
+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (err < 0)
+ return err;
+
+ return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+static void read_status_messages(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int data_blocks)
+{
+ struct snd_tscm *tscm = container_of(s, struct snd_tscm, tx_stream);
+ bool used = READ_ONCE(tscm->hwdep->used);
+ int i;
+
+ for (i = 0; i < data_blocks; i++) {
+ unsigned int index;
+ __be32 before;
+ __be32 after;
+
+ index = be32_to_cpu(buffer[0]) % SNDRV_FIREWIRE_TASCAM_STATE_COUNT;
+ before = tscm->state[index];
+ after = buffer[s->data_block_quadlets - 1];
+
+ if (used && index > 4 && index < 16) {
+ __be32 mask;
+
+ if (index == 5)
+ mask = cpu_to_be32(~0x0000ffff);
+ else if (index == 6)
+ mask = cpu_to_be32(~0x0000ffff);
+ else if (index == 8)
+ mask = cpu_to_be32(~0x000f0f00);
+ else
+ mask = cpu_to_be32(~0x00000000);
+
+ if ((before ^ after) & mask) {
+ struct snd_firewire_tascam_change *entry =
+ &tscm->queue[tscm->push_pos];
+
+ scoped_guard(spinlock_irqsave, &tscm->lock) {
+ entry->index = index;
+ entry->before = before;
+ entry->after = after;
+ if (++tscm->push_pos >= SND_TSCM_QUEUE_COUNT)
+ tscm->push_pos = 0;
+ }
+
+ wake_up(&tscm->hwdep_wait);
+ }
+ }
+
+ tscm->state[index] = after;
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ unsigned int pcm_frames = 0;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ if (pcm) {
+ read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks;
+ }
+
+ read_status_messages(s, buf, data_blocks);
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+}
+
+static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ unsigned int pcm_frames = 0;
+ int i;
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buf = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+
+ if (pcm) {
+ write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
+ pcm_frames += data_blocks;
+ } else {
+ write_pcm_silence(s, buf, data_blocks);
+ }
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+}
+
+int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, unsigned int pcm_channels)
+{
+ amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
+ unsigned int flags = CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK | CIP_UNAWARE_SYT;
+ struct amdtp_tscm *p;
+ unsigned int fmt;
+ int err;
+
+ if (dir == AMDTP_IN_STREAM) {
+ fmt = AMDTP_FMT_TSCM_TX;
+ process_ctx_payloads = process_ir_ctx_payloads;
+ } else {
+ fmt = AMDTP_FMT_TSCM_RX;
+ process_ctx_payloads = process_it_ctx_payloads;
+ }
+
+ err = amdtp_stream_init(s, unit, dir, flags, fmt,
+ process_ctx_payloads, sizeof(struct amdtp_tscm));
+ if (err < 0)
+ return err;
+
+ if (dir == AMDTP_OUT_STREAM) {
+ // Use fixed value for FDF field.
+ s->ctx_data.rx.fdf = 0x00;
+ }
+
+ /* This protocol uses fixed number of data channels for PCM samples. */
+ p = s->protocol;
+ p->pcm_channels = pcm_channels;
+
+ return 0;
+}
diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c
new file mode 100644
index 000000000000..867b4ea1096e
--- /dev/null
+++ b/sound/firewire/tascam/tascam-hwdep.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tascam-hwdep.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "tascam.h"
+
+static long tscm_hwdep_read_locked(struct snd_tscm *tscm, char __user *buf,
+ long count, loff_t *offset)
+ __releases(&tscm->lock)
+{
+ struct snd_firewire_event_lock_status event = {
+ .type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
+ };
+
+ event.status = (tscm->dev_lock_count > 0);
+ tscm->dev_lock_changed = false;
+ count = min_t(long, count, sizeof(event));
+
+ spin_unlock_irq(&tscm->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static long tscm_hwdep_read_queue(struct snd_tscm *tscm, char __user *buf,
+ long remained, loff_t *offset)
+ __releases(&tscm->lock)
+{
+ char __user *pos = buf;
+ unsigned int type = SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL;
+ struct snd_firewire_tascam_change *entries = tscm->queue;
+ long count;
+
+ // At least, one control event can be copied.
+ if (remained < sizeof(type) + sizeof(*entries)) {
+ spin_unlock_irq(&tscm->lock);
+ return -EINVAL;
+ }
+
+ // Copy the type field later.
+ count = sizeof(type);
+ remained -= sizeof(type);
+ pos += sizeof(type);
+
+ while (true) {
+ unsigned int head_pos;
+ unsigned int tail_pos;
+ unsigned int length;
+
+ if (tscm->pull_pos == tscm->push_pos)
+ break;
+ else if (tscm->pull_pos < tscm->push_pos)
+ tail_pos = tscm->push_pos;
+ else
+ tail_pos = SND_TSCM_QUEUE_COUNT;
+ head_pos = tscm->pull_pos;
+
+ length = (tail_pos - head_pos) * sizeof(*entries);
+ if (remained < length)
+ length = rounddown(remained, sizeof(*entries));
+ if (length == 0)
+ break;
+
+ spin_unlock_irq(&tscm->lock);
+ if (copy_to_user(pos, &entries[head_pos], length))
+ return -EFAULT;
+
+ spin_lock_irq(&tscm->lock);
+
+ tscm->pull_pos = tail_pos % SND_TSCM_QUEUE_COUNT;
+
+ count += length;
+ remained -= length;
+ pos += length;
+ }
+
+ spin_unlock_irq(&tscm->lock);
+
+ if (copy_to_user(buf, &type, sizeof(type)))
+ return -EFAULT;
+
+ return count;
+}
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+ DEFINE_WAIT(wait);
+
+ spin_lock_irq(&tscm->lock);
+
+ while (!tscm->dev_lock_changed && tscm->push_pos == tscm->pull_pos) {
+ prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&tscm->lock);
+ schedule();
+ finish_wait(&tscm->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&tscm->lock);
+ }
+
+ // NOTE: The acquired lock should be released in callee side.
+ if (tscm->dev_lock_changed) {
+ count = tscm_hwdep_read_locked(tscm, buf, count, offset);
+ } else if (tscm->push_pos != tscm->pull_pos) {
+ count = tscm_hwdep_read_queue(tscm, buf, count, offset);
+ } else {
+ spin_unlock_irq(&tscm->lock);
+ count = 0;
+ }
+
+ return count;
+}
+
+static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+
+ poll_wait(file, &tscm->hwdep_wait, wait);
+
+ guard(spinlock_irq)(&tscm->lock);
+ if (tscm->dev_lock_changed || tscm->push_pos != tscm->pull_pos)
+ return EPOLLIN | EPOLLRDNORM;
+ else
+ return 0;
+}
+
+static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(tscm->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_TASCAM;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strscpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_tscm *tscm)
+{
+ guard(spinlock_irq)(&tscm->lock);
+
+ if (tscm->dev_lock_count == 0) {
+ tscm->dev_lock_count = -1;
+ return 0;
+ } else {
+ return -EBUSY;
+ }
+}
+
+static int hwdep_unlock(struct snd_tscm *tscm)
+{
+ guard(spinlock_irq)(&tscm->lock);
+
+ if (tscm->dev_lock_count == -1) {
+ tscm->dev_lock_count = 0;
+ return 0;
+ } else {
+ return -EBADFD;
+ }
+}
+
+static int tscm_hwdep_state(struct snd_tscm *tscm, void __user *arg)
+{
+ if (copy_to_user(arg, tscm->state, sizeof(tscm->state)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+
+ guard(spinlock_irq)(&tscm->lock);
+ if (tscm->dev_lock_count == -1)
+ tscm->dev_lock_count = 0;
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_tscm *tscm = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(tscm, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(tscm);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(tscm);
+ case SNDRV_FIREWIRE_IOCTL_TASCAM_STATE:
+ return tscm_hwdep_state(tscm, (void __user *)arg);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_tscm_create_hwdep_device(struct snd_tscm *tscm)
+{
+ static const struct snd_hwdep_ops ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(tscm->card, "Tascam", 0, &hwdep);
+ if (err < 0)
+ return err;
+
+ strscpy(hwdep->name, "Tascam");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM;
+ hwdep->ops = ops;
+ hwdep->private_data = tscm;
+ hwdep->exclusive = true;
+
+ tscm->hwdep = hwdep;
+
+ return err;
+}
diff --git a/sound/firewire/tascam/tascam-midi.c b/sound/firewire/tascam/tascam-midi.c
new file mode 100644
index 000000000000..1bf9d7b3da33
--- /dev/null
+++ b/sound/firewire/tascam/tascam-midi.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tascam-midi.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ */
+
+#include "tascam.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_tscm *tscm = substream->rmidi->private_data;
+
+ snd_fw_async_midi_port_init(&tscm->out_ports[substream->number]);
+
+ return 0;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+ return 0;
+}
+
+static void midi_playback_drain(struct snd_rawmidi_substream *substream)
+{
+ struct snd_tscm *tscm = substream->rmidi->private_data;
+
+ snd_fw_async_midi_port_finish(&tscm->out_ports[substream->number]);
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_tscm *tscm = substrm->rmidi->private_data;
+
+ guard(spinlock_irqsave)(&tscm->lock);
+
+ if (up)
+ tscm->tx_midi_substreams[substrm->number] = substrm;
+ else
+ tscm->tx_midi_substreams[substrm->number] = NULL;
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_tscm *tscm = substrm->rmidi->private_data;
+
+ guard(spinlock_irqsave)(&tscm->lock);
+
+ if (up)
+ snd_fw_async_midi_port_run(&tscm->out_ports[substrm->number],
+ substrm);
+}
+
+int snd_tscm_create_midi_devices(struct snd_tscm *tscm)
+{
+ static const struct snd_rawmidi_ops capture_ops = {
+ .open = midi_capture_open,
+ .close = midi_capture_close,
+ .trigger = midi_capture_trigger,
+ };
+ static const struct snd_rawmidi_ops playback_ops = {
+ .open = midi_playback_open,
+ .close = midi_playback_close,
+ .drain = midi_playback_drain,
+ .trigger = midi_playback_trigger,
+ };
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *stream;
+ struct snd_rawmidi_substream *subs;
+ int err;
+
+ err = snd_rawmidi_new(tscm->card, tscm->card->driver, 0,
+ tscm->spec->midi_playback_ports,
+ tscm->spec->midi_capture_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", tscm->card->shortname);
+ rmidi->private_data = tscm;
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &capture_ops);
+ stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+ /* Set port names for MIDI input. */
+ list_for_each_entry(subs, &stream->substreams, list) {
+ /* TODO: support virtual MIDI ports. */
+ if (subs->number < tscm->spec->midi_capture_ports) {
+ /* Hardware MIDI ports. */
+ scnprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ tscm->card->shortname, subs->number + 1);
+ }
+ }
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &playback_ops);
+ stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+ /* Set port names for MIDI ourput. */
+ list_for_each_entry(subs, &stream->substreams, list) {
+ if (subs->number < tscm->spec->midi_playback_ports) {
+ /* Hardware MIDI ports only. */
+ scnprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ tscm->card->shortname, subs->number + 1);
+ }
+ }
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c
new file mode 100644
index 000000000000..d885fef0c8ca
--- /dev/null
+++ b/sound/firewire/tascam/tascam-pcm.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tascam-pcm.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ */
+
+#include "tascam.h"
+
+static int pcm_init_hw_params(struct snd_tscm *tscm,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ struct amdtp_stream *stream;
+ unsigned int pcm_channels;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ stream = &tscm->tx_stream;
+ pcm_channels = tscm->spec->pcm_capture_analog_channels;
+ } else {
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ stream = &tscm->rx_stream;
+ pcm_channels = tscm->spec->pcm_playback_analog_channels;
+ }
+
+ if (tscm->spec->has_adat)
+ pcm_channels += 8;
+ if (tscm->spec->has_spdif)
+ pcm_channels += 2;
+ runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels;
+
+ hw->rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000;
+ snd_pcm_limit_hw_rates(runtime);
+
+ return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ struct amdtp_domain *d = &tscm->domain;
+ enum snd_tscm_clock clock;
+ int err;
+
+ err = snd_tscm_stream_lock_try(tscm);
+ if (err < 0)
+ return err;
+
+ err = pcm_init_hw_params(tscm, substream);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_tscm_stream_get_clock(tscm, &clock);
+ if (err < 0)
+ goto err_locked;
+
+ scoped_guard(mutex, &tscm->mutex) {
+ // When source of clock is not internal or any stream is reserved for
+ // transmission of PCM frames, the available sampling rate is limited
+ // at current one.
+ if (clock != SND_TSCM_CLOCK_INTERNAL || tscm->substreams_counter > 0) {
+ unsigned int frames_per_period = d->events_per_period;
+ unsigned int frames_per_buffer = d->events_per_buffer;
+ unsigned int rate;
+
+ err = snd_tscm_stream_get_rate(tscm, &rate);
+ if (err < 0)
+ goto err_locked;
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ frames_per_period, frames_per_period);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ frames_per_buffer, frames_per_buffer);
+ if (err < 0)
+ goto err_locked;
+ }
+ }
+
+ snd_pcm_set_sync(substream);
+
+ return 0;
+err_locked:
+ snd_tscm_stream_lock_release(tscm);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ snd_tscm_stream_lock_release(tscm);
+
+ return 0;
+}
+
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ int err = 0;
+
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+ unsigned int rate = params_rate(hw_params);
+ unsigned int frames_per_period = params_period_size(hw_params);
+ unsigned int frames_per_buffer = params_buffer_size(hw_params);
+
+ guard(mutex)(&tscm->mutex);
+ err = snd_tscm_stream_reserve_duplex(tscm, rate,
+ frames_per_period, frames_per_buffer);
+ if (err >= 0)
+ ++tscm->substreams_counter;
+ }
+
+ return err;
+}
+
+static int pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ guard(mutex)(&tscm->mutex);
+
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
+ --tscm->substreams_counter;
+
+ snd_tscm_stream_stop_duplex(tscm);
+
+ return 0;
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ guard(mutex)(&tscm->mutex);
+
+ err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&tscm->tx_stream);
+
+ return err;
+}
+
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ guard(mutex)(&tscm->mutex);
+
+ err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&tscm->rx_stream);
+
+ return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_tscm *tscm = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->tx_stream);
+}
+
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_tscm *tscm = sbstrm->private_data;
+
+ return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->rx_stream);
+}
+
+static int pcm_capture_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->tx_stream);
+}
+
+static int pcm_playback_ack(struct snd_pcm_substream *substream)
+{
+ struct snd_tscm *tscm = substream->private_data;
+
+ return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->rx_stream);
+}
+
+int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
+{
+ static const struct snd_pcm_ops capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .ack = pcm_capture_ack,
+ };
+ static const struct snd_pcm_ops playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .hw_params = pcm_hw_params,
+ .hw_free = pcm_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .ack = pcm_playback_ack,
+ };
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = tscm;
+ pcm->nonatomic = true;
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s PCM", tscm->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
+
+ return 0;
+}
diff --git a/sound/firewire/tascam/tascam-proc.c b/sound/firewire/tascam/tascam-proc.c
new file mode 100644
index 000000000000..53846aeff342
--- /dev/null
+++ b/sound/firewire/tascam/tascam-proc.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tascam-proc.h - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ */
+
+#include "./tascam.h"
+
+static void proc_read_firmware(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_tscm *tscm = entry->private_data;
+ __be32 data;
+ unsigned int reg, fpga, arm, hw;
+ int err;
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_REGISTER,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ reg = be32_to_cpu(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_FPGA,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ fpga = be32_to_cpu(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_ARM,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ arm = be32_to_cpu(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_HW,
+ &data, sizeof(data), 0);
+ if (err < 0)
+ return;
+ hw = be32_to_cpu(data);
+
+ snd_iprintf(buffer, "Register: %d (0x%08x)\n", reg & 0xffff, reg);
+ snd_iprintf(buffer, "FPGA: %d (0x%08x)\n", fpga & 0xffff, fpga);
+ snd_iprintf(buffer, "ARM: %d (0x%08x)\n", arm & 0xffff, arm);
+ snd_iprintf(buffer, "Hardware: %d (0x%08x)\n", hw >> 16, hw);
+}
+
+static void add_node(struct snd_tscm *tscm, struct snd_info_entry *root,
+ const char *name,
+ void (*op)(struct snd_info_entry *e,
+ struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(tscm->card, name, root);
+ if (entry)
+ snd_info_set_text_ops(entry, tscm, op);
+}
+
+void snd_tscm_proc_init(struct snd_tscm *tscm)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(tscm->card, "firewire",
+ tscm->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | 0555;
+
+ add_node(tscm, root, "firmware", proc_read_firmware);
+}
diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c
new file mode 100644
index 000000000000..4ecd151a46c1
--- /dev/null
+++ b/sound/firewire/tascam/tascam-stream.c
@@ -0,0 +1,551 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tascam-stream.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ */
+
+#include <linux/delay.h>
+#include "tascam.h"
+
+#define CLOCK_STATUS_MASK 0xffff0000
+#define CLOCK_CONFIG_MASK 0x0000ffff
+
+#define READY_TIMEOUT_MS 4000
+
+static int get_clock(struct snd_tscm *tscm, u32 *data)
+{
+ int trial = 0;
+ __be32 reg;
+ int err;
+
+ while (trial++ < 5) {
+ err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ *data = be32_to_cpu(reg);
+ if (*data & CLOCK_STATUS_MASK)
+ break;
+
+ // In intermediate state after changing clock status.
+ msleep(50);
+ }
+
+ // Still in the intermediate state.
+ if (trial >= 5)
+ return -EAGAIN;
+
+ return 0;
+}
+
+static int set_clock(struct snd_tscm *tscm, unsigned int rate,
+ enum snd_tscm_clock clock)
+{
+ u32 data;
+ __be32 reg;
+ int err;
+
+ err = get_clock(tscm, &data);
+ if (err < 0)
+ return err;
+ data &= CLOCK_CONFIG_MASK;
+
+ if (rate > 0) {
+ data &= 0x000000ff;
+ /* Base rate. */
+ if ((rate % 44100) == 0) {
+ data |= 0x00000100;
+ /* Multiplier. */
+ if (rate / 44100 == 2)
+ data |= 0x00008000;
+ } else if ((rate % 48000) == 0) {
+ data |= 0x00000200;
+ /* Multiplier. */
+ if (rate / 48000 == 2)
+ data |= 0x00008000;
+ } else {
+ return -EAGAIN;
+ }
+ }
+
+ if (clock != INT_MAX) {
+ data &= 0x0000ff00;
+ data |= clock + 1;
+ }
+
+ reg = cpu_to_be32(data);
+
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ if (data & 0x00008000)
+ reg = cpu_to_be32(0x0000001a);
+ else
+ reg = cpu_to_be32(0x0000000d);
+
+ return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MULTIPLEX_MODE,
+ &reg, sizeof(reg), 0);
+}
+
+int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate)
+{
+ u32 data;
+ int err;
+
+ err = get_clock(tscm, &data);
+ if (err < 0)
+ return err;
+
+ data = (data & 0xff000000) >> 24;
+
+ /* Check base rate. */
+ if ((data & 0x0f) == 0x01)
+ *rate = 44100;
+ else if ((data & 0x0f) == 0x02)
+ *rate = 48000;
+ else
+ return -EAGAIN;
+
+ /* Check multiplier. */
+ if ((data & 0xf0) == 0x80)
+ *rate *= 2;
+ else if ((data & 0xf0) != 0x00)
+ return -EAGAIN;
+
+ return err;
+}
+
+int snd_tscm_stream_get_clock(struct snd_tscm *tscm, enum snd_tscm_clock *clock)
+{
+ u32 data;
+ int err;
+
+ err = get_clock(tscm, &data);
+ if (err < 0)
+ return err;
+
+ *clock = ((data & 0x00ff0000) >> 16) - 1;
+ if (*clock < 0 || *clock > SND_TSCM_CLOCK_ADAT)
+ return -EIO;
+
+ return 0;
+}
+
+static int enable_data_channels(struct snd_tscm *tscm)
+{
+ __be32 reg;
+ u32 data;
+ unsigned int i;
+ int err;
+
+ data = 0;
+ for (i = 0; i < tscm->spec->pcm_capture_analog_channels; ++i)
+ data |= BIT(i);
+ if (tscm->spec->has_adat)
+ data |= 0x0000ff00;
+ if (tscm->spec->has_spdif)
+ data |= 0x00030000;
+
+ reg = cpu_to_be32(data);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_TX_PCM_CHANNELS,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ data = 0;
+ for (i = 0; i < tscm->spec->pcm_playback_analog_channels; ++i)
+ data |= BIT(i);
+ if (tscm->spec->has_adat)
+ data |= 0x0000ff00;
+ if (tscm->spec->has_spdif)
+ data |= 0x00030000;
+
+ reg = cpu_to_be32(data);
+ return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_RX_PCM_CHANNELS,
+ &reg, sizeof(reg), 0);
+}
+
+static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate)
+{
+ __be32 reg;
+ int err;
+
+ // Set an option for unknown purpose.
+ reg = cpu_to_be32(0x00200000);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ return enable_data_channels(tscm);
+}
+
+static void finish_session(struct snd_tscm *tscm)
+{
+ __be32 reg;
+
+ reg = 0;
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
+ &reg, sizeof(reg), 0);
+
+ reg = 0;
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
+ &reg, sizeof(reg), 0);
+
+ // Unregister channels.
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
+ &reg, sizeof(reg), 0);
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
+ &reg, sizeof(reg), 0);
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
+ &reg, sizeof(reg), 0);
+}
+
+static int begin_session(struct snd_tscm *tscm)
+{
+ __be32 reg;
+ int err;
+
+ // Register the isochronous channel for transmitting stream.
+ reg = cpu_to_be32(tscm->tx_resources.channel);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // Unknown.
+ reg = cpu_to_be32(0x00000002);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // Register the isochronous channel for receiving stream.
+ reg = cpu_to_be32(tscm->rx_resources.channel);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ reg = cpu_to_be32(0x00000001);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ reg = cpu_to_be32(0x00000001);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // Set an option for unknown purpose.
+ reg = cpu_to_be32(0x00002000);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ // Start multiplexing PCM samples on packets.
+ reg = cpu_to_be32(0x00000001);
+ return snd_fw_transaction(tscm->unit,
+ TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_ON,
+ &reg, sizeof(reg), 0);
+}
+
+static int keep_resources(struct snd_tscm *tscm, unsigned int rate,
+ struct amdtp_stream *stream)
+{
+ struct fw_iso_resources *resources;
+ int speed;
+ int err;
+
+ if (stream == &tscm->tx_stream) {
+ resources = &tscm->tx_resources;
+ speed = fw_parent_device(tscm->unit)->max_speed;
+ } else {
+ resources = &tscm->rx_resources;
+ speed = SCODE_400;
+ }
+
+ err = amdtp_tscm_set_parameters(stream, rate);
+ if (err < 0)
+ return err;
+
+ return fw_iso_resources_allocate(resources, amdtp_stream_get_max_payload(stream), speed);
+}
+
+static int init_stream(struct snd_tscm *tscm, struct amdtp_stream *s)
+{
+ struct fw_iso_resources *resources;
+ enum amdtp_stream_direction dir;
+ unsigned int pcm_channels;
+ int err;
+
+ if (s == &tscm->tx_stream) {
+ resources = &tscm->tx_resources;
+ dir = AMDTP_IN_STREAM;
+ pcm_channels = tscm->spec->pcm_capture_analog_channels;
+ } else {
+ resources = &tscm->rx_resources;
+ dir = AMDTP_OUT_STREAM;
+ pcm_channels = tscm->spec->pcm_playback_analog_channels;
+ }
+
+ if (tscm->spec->has_adat)
+ pcm_channels += 8;
+ if (tscm->spec->has_spdif)
+ pcm_channels += 2;
+
+ err = fw_iso_resources_init(resources, tscm->unit);
+ if (err < 0)
+ return err;
+
+ err = amdtp_tscm_init(s, tscm->unit, dir, pcm_channels);
+ if (err < 0)
+ fw_iso_resources_free(resources);
+
+ return err;
+}
+
+static void destroy_stream(struct snd_tscm *tscm, struct amdtp_stream *s)
+{
+ amdtp_stream_destroy(s);
+
+ if (s == &tscm->tx_stream)
+ fw_iso_resources_destroy(&tscm->tx_resources);
+ else
+ fw_iso_resources_destroy(&tscm->rx_resources);
+}
+
+int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
+{
+ int err;
+
+ err = init_stream(tscm, &tscm->tx_stream);
+ if (err < 0)
+ return err;
+
+ err = init_stream(tscm, &tscm->rx_stream);
+ if (err < 0) {
+ destroy_stream(tscm, &tscm->tx_stream);
+ return err;
+ }
+
+ err = amdtp_domain_init(&tscm->domain);
+ if (err < 0) {
+ destroy_stream(tscm, &tscm->tx_stream);
+ destroy_stream(tscm, &tscm->rx_stream);
+ }
+
+ return err;
+}
+
+// At bus reset, streaming is stopped and some registers are clear.
+void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
+{
+ amdtp_domain_stop(&tscm->domain);
+
+ amdtp_stream_pcm_abort(&tscm->tx_stream);
+ amdtp_stream_pcm_abort(&tscm->rx_stream);
+}
+
+// This function should be called before starting streams or after stopping
+// streams.
+void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
+{
+ amdtp_domain_destroy(&tscm->domain);
+
+ destroy_stream(tscm, &tscm->rx_stream);
+ destroy_stream(tscm, &tscm->tx_stream);
+}
+
+int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer)
+{
+ unsigned int curr_rate;
+ int err;
+
+ err = snd_tscm_stream_get_rate(tscm, &curr_rate);
+ if (err < 0)
+ return err;
+
+ if (tscm->substreams_counter == 0 || rate != curr_rate) {
+ amdtp_domain_stop(&tscm->domain);
+
+ finish_session(tscm);
+
+ fw_iso_resources_free(&tscm->tx_resources);
+ fw_iso_resources_free(&tscm->rx_resources);
+
+ err = set_clock(tscm, rate, INT_MAX);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(tscm, rate, &tscm->tx_stream);
+ if (err < 0)
+ return err;
+
+ err = keep_resources(tscm, rate, &tscm->rx_stream);
+ if (err < 0) {
+ fw_iso_resources_free(&tscm->tx_resources);
+ return err;
+ }
+
+ err = amdtp_domain_set_events_per_period(&tscm->domain,
+ frames_per_period, frames_per_buffer);
+ if (err < 0) {
+ fw_iso_resources_free(&tscm->tx_resources);
+ fw_iso_resources_free(&tscm->rx_resources);
+ return err;
+ }
+
+ tscm->need_long_tx_init_skip = (rate != curr_rate);
+ }
+
+ return 0;
+}
+
+int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
+{
+ unsigned int generation = tscm->rx_resources.generation;
+ int err;
+
+ if (tscm->substreams_counter == 0)
+ return 0;
+
+ if (amdtp_streaming_error(&tscm->rx_stream) ||
+ amdtp_streaming_error(&tscm->tx_stream)) {
+ amdtp_domain_stop(&tscm->domain);
+ finish_session(tscm);
+ }
+
+ if (generation != fw_parent_device(tscm->unit)->card->generation) {
+ err = fw_iso_resources_update(&tscm->tx_resources);
+ if (err < 0)
+ goto error;
+
+ err = fw_iso_resources_update(&tscm->rx_resources);
+ if (err < 0)
+ goto error;
+ }
+
+ if (!amdtp_stream_running(&tscm->rx_stream)) {
+ unsigned int tx_init_skip_cycles;
+
+ err = set_stream_formats(tscm, rate);
+ if (err < 0)
+ goto error;
+
+ err = begin_session(tscm);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_domain_add_stream(&tscm->domain, &tscm->rx_stream, tscm->rx_resources.channel,
+ fw_parent_device(tscm->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_domain_add_stream(&tscm->domain, &tscm->tx_stream, tscm->tx_resources.channel,
+ SCODE_400);
+ if (err < 0)
+ goto error;
+
+ if (tscm->need_long_tx_init_skip)
+ tx_init_skip_cycles = 16000;
+ else
+ tx_init_skip_cycles = 0;
+
+ // MEMO: Just after starting packet streaming, it transfers packets without any
+ // event. Enough after receiving the sequence of packets, it multiplexes events into
+ // the packet. However, just after changing sampling transfer frequency, it stops
+ // multiplexing during packet transmission. Enough after, it restarts multiplexing
+ // again. The device ignores presentation time expressed by the value of syt field
+ // of CIP header in received packets. The sequence of the number of data blocks per
+ // packet is important for media clock recovery.
+ err = amdtp_domain_start(&tscm->domain, tx_init_skip_cycles, true, true);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_domain_wait_ready(&tscm->domain, READY_TIMEOUT_MS)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ amdtp_domain_stop(&tscm->domain);
+ finish_session(tscm);
+
+ return err;
+}
+
+void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
+{
+ if (tscm->substreams_counter == 0) {
+ amdtp_domain_stop(&tscm->domain);
+ finish_session(tscm);
+
+ fw_iso_resources_free(&tscm->tx_resources);
+ fw_iso_resources_free(&tscm->rx_resources);
+
+ tscm->need_long_tx_init_skip = false;
+ }
+}
+
+void snd_tscm_stream_lock_changed(struct snd_tscm *tscm)
+{
+ tscm->dev_lock_changed = true;
+ wake_up(&tscm->hwdep_wait);
+}
+
+int snd_tscm_stream_lock_try(struct snd_tscm *tscm)
+{
+ guard(spinlock_irq)(&tscm->lock);
+
+ /* user land lock this */
+ if (tscm->dev_lock_count < 0)
+ return -EBUSY;
+
+ /* this is the first time */
+ if (tscm->dev_lock_count++ == 0)
+ snd_tscm_stream_lock_changed(tscm);
+ return 0;
+}
+
+void snd_tscm_stream_lock_release(struct snd_tscm *tscm)
+{
+ guard(spinlock_irq)(&tscm->lock);
+
+ if (WARN_ON(tscm->dev_lock_count <= 0))
+ return;
+ if (--tscm->dev_lock_count == 0)
+ snd_tscm_stream_lock_changed(tscm);
+}
diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c
new file mode 100644
index 000000000000..a073cece4a7d
--- /dev/null
+++ b/sound/firewire/tascam/tascam-transaction.c
@@ -0,0 +1,399 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tascam-transaction.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ */
+
+#include "tascam.h"
+
+/*
+ * When return minus value, given argument is not MIDI status.
+ * When return 0, given argument is a beginning of system exclusive.
+ * When return the others, given argument is MIDI data.
+ */
+static inline int calculate_message_bytes(u8 status)
+{
+ switch (status) {
+ case 0xf6: /* Tune request. */
+ case 0xf8: /* Timing clock. */
+ case 0xfa: /* Start. */
+ case 0xfb: /* Continue. */
+ case 0xfc: /* Stop. */
+ case 0xfe: /* Active sensing. */
+ case 0xff: /* System reset. */
+ return 1;
+ case 0xf1: /* MIDI time code quarter frame. */
+ case 0xf3: /* Song select. */
+ return 2;
+ case 0xf2: /* Song position pointer. */
+ return 3;
+ case 0xf0: /* Exclusive. */
+ return 0;
+ case 0xf7: /* End of exclusive. */
+ break;
+ case 0xf4: /* Undefined. */
+ case 0xf5: /* Undefined. */
+ case 0xf9: /* Undefined. */
+ case 0xfd: /* Undefined. */
+ break;
+ default:
+ switch (status & 0xf0) {
+ case 0x80: /* Note on. */
+ case 0x90: /* Note off. */
+ case 0xa0: /* Polyphonic key pressure. */
+ case 0xb0: /* Control change and Mode change. */
+ case 0xe0: /* Pitch bend change. */
+ return 3;
+ case 0xc0: /* Program change. */
+ case 0xd0: /* Channel pressure. */
+ return 2;
+ default:
+ break;
+ }
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int fill_message(struct snd_fw_async_midi_port *port,
+ struct snd_rawmidi_substream *substream)
+{
+ int i, len, consume;
+ u8 *label, *msg;
+ u8 status;
+
+ /* The first byte is used for label, the rest for MIDI bytes. */
+ label = port->buf;
+ msg = port->buf + 1;
+
+ consume = snd_rawmidi_transmit_peek(substream, msg, 3);
+ if (consume == 0)
+ return 0;
+
+ /* On exclusive message. */
+ if (port->on_sysex) {
+ /* Seek the end of exclusives. */
+ for (i = 0; i < consume; ++i) {
+ if (msg[i] == 0xf7) {
+ port->on_sysex = false;
+ break;
+ }
+ }
+
+ /* At the end of exclusive message, use label 0x07. */
+ if (!port->on_sysex) {
+ consume = i + 1;
+ *label = (substream->number << 4) | 0x07;
+ /* During exclusive message, use label 0x04. */
+ } else if (consume == 3) {
+ *label = (substream->number << 4) | 0x04;
+ /* We need to fill whole 3 bytes. Go to next change. */
+ } else {
+ return 0;
+ }
+
+ len = consume;
+ } else {
+ /* The beginning of exclusives. */
+ if (msg[0] == 0xf0) {
+ /* Transfer it in next chance in another condition. */
+ port->on_sysex = true;
+ return 0;
+ } else {
+ /* On running-status. */
+ if ((msg[0] & 0x80) != 0x80)
+ status = port->running_status;
+ else
+ status = msg[0];
+
+ /* Calculate consume bytes. */
+ len = calculate_message_bytes(status);
+ if (len <= 0)
+ return 0;
+
+ /* On running-status. */
+ if ((msg[0] & 0x80) != 0x80) {
+ /* Enough MIDI bytes were not retrieved. */
+ if (consume < len - 1)
+ return 0;
+ consume = len - 1;
+
+ msg[2] = msg[1];
+ msg[1] = msg[0];
+ msg[0] = port->running_status;
+ } else {
+ /* Enough MIDI bytes were not retrieved. */
+ if (consume < len)
+ return 0;
+ consume = len;
+
+ port->running_status = msg[0];
+ }
+ }
+
+ *label = (substream->number << 4) | (msg[0] >> 4);
+ }
+
+ if (len > 0 && len < 3)
+ memset(msg + len, 0, 3 - len);
+
+ return consume;
+}
+
+static void async_midi_port_callback(struct fw_card *card, int rcode,
+ void *data, size_t length,
+ void *callback_data)
+{
+ struct snd_fw_async_midi_port *port = callback_data;
+ struct snd_rawmidi_substream *substream = READ_ONCE(port->substream);
+
+ /* This port is closed. */
+ if (substream == NULL)
+ return;
+
+ if (rcode == RCODE_COMPLETE)
+ snd_rawmidi_transmit_ack(substream, port->consume_bytes);
+ else if (!rcode_is_permanent_error(rcode))
+ /* To start next transaction immediately for recovery. */
+ port->next_ktime = 0;
+ else
+ /* Don't continue processing. */
+ port->error = true;
+
+ port->idling = true;
+
+ if (!snd_rawmidi_transmit_empty(substream))
+ schedule_work(&port->work);
+}
+
+static void midi_port_work(struct work_struct *work)
+{
+ struct snd_fw_async_midi_port *port =
+ container_of(work, struct snd_fw_async_midi_port, work);
+ struct snd_rawmidi_substream *substream = READ_ONCE(port->substream);
+ int generation;
+
+ /* Under transacting or error state. */
+ if (!port->idling || port->error)
+ return;
+
+ /* Nothing to do. */
+ if (substream == NULL || snd_rawmidi_transmit_empty(substream))
+ return;
+
+ /* Do it in next chance. */
+ if (ktime_after(port->next_ktime, ktime_get())) {
+ schedule_work(&port->work);
+ return;
+ }
+
+ /*
+ * Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
+ * Later, snd_rawmidi_transmit_ack() is called.
+ */
+ memset(port->buf, 0, 4);
+ port->consume_bytes = fill_message(port, substream);
+ if (port->consume_bytes <= 0) {
+ /* Do it in next chance, immediately. */
+ if (port->consume_bytes == 0) {
+ port->next_ktime = 0;
+ schedule_work(&port->work);
+ } else {
+ /* Fatal error. */
+ port->error = true;
+ }
+ return;
+ }
+
+ /* Set interval to next transaction. */
+ port->next_ktime = ktime_add_ns(ktime_get(),
+ port->consume_bytes * 8 * (NSEC_PER_SEC / 31250));
+
+ /* Start this transaction. */
+ port->idling = false;
+
+ /*
+ * In Linux FireWire core, when generation is updated with memory
+ * barrier, node id has already been updated. In this module, After
+ * this smp_rmb(), load/store instructions to memory are completed.
+ * Thus, both of generation and node id are available with recent
+ * values. This is a light-serialization solution to handle bus reset
+ * events on IEEE 1394 bus.
+ */
+ generation = port->parent->generation;
+ smp_rmb();
+
+ fw_send_request(port->parent->card, &port->transaction,
+ TCODE_WRITE_QUADLET_REQUEST,
+ port->parent->node_id, generation,
+ port->parent->max_speed,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_RX_QUAD,
+ port->buf, 4, async_midi_port_callback,
+ port);
+}
+
+void snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port)
+{
+ port->idling = true;
+ port->error = false;
+ port->running_status = 0;
+ port->on_sysex = false;
+}
+
+static void handle_midi_tx(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_tscm *tscm = callback_data;
+ u32 *buf = (u32 *)data;
+ unsigned int messages;
+ unsigned int i;
+ unsigned int port;
+ struct snd_rawmidi_substream *substream;
+ u8 *b;
+ int bytes;
+
+ if (offset != tscm->async_handler.offset)
+ goto end;
+
+ messages = length / 8;
+ for (i = 0; i < messages; i++) {
+ b = (u8 *)(buf + i * 2);
+
+ port = b[0] >> 4;
+ /* TODO: support virtual MIDI ports. */
+ if (port >= tscm->spec->midi_capture_ports)
+ goto end;
+
+ /* Assume the message length. */
+ bytes = calculate_message_bytes(b[1]);
+ /* On MIDI data or exclusives. */
+ if (bytes <= 0) {
+ /* Seek the end of exclusives. */
+ for (bytes = 1; bytes < 4; bytes++) {
+ if (b[bytes] == 0xf7)
+ break;
+ }
+ if (bytes == 4)
+ bytes = 3;
+ }
+
+ substream = READ_ONCE(tscm->tx_midi_substreams[port]);
+ if (substream != NULL)
+ snd_rawmidi_receive(substream, b + 1, bytes);
+ }
+end:
+ fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+int snd_tscm_transaction_register(struct snd_tscm *tscm)
+{
+ static const struct fw_address_region resp_register_region = {
+ .start = 0xffffe0000000ull,
+ .end = 0xffffe000ffffull,
+ };
+ unsigned int i;
+ int err;
+
+ /*
+ * Usually, two quadlets are transferred by one transaction. The first
+ * quadlet has MIDI messages, the rest includes timestamp.
+ * Sometimes, 8 set of the data is transferred by a block transaction.
+ */
+ tscm->async_handler.length = 8 * 8;
+ tscm->async_handler.address_callback = handle_midi_tx;
+ tscm->async_handler.callback_data = tscm;
+
+ err = fw_core_add_address_handler(&tscm->async_handler,
+ &resp_register_region);
+ if (err < 0)
+ return err;
+
+ err = snd_tscm_transaction_reregister(tscm);
+ if (err < 0)
+ goto error;
+
+ for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++) {
+ tscm->out_ports[i].parent = fw_parent_device(tscm->unit);
+ tscm->out_ports[i].next_ktime = 0;
+ INIT_WORK(&tscm->out_ports[i].work, midi_port_work);
+ }
+
+ return err;
+error:
+ fw_core_remove_address_handler(&tscm->async_handler);
+ tscm->async_handler.callback_data = NULL;
+ return err;
+}
+
+/* At bus reset, these registers are cleared. */
+int snd_tscm_transaction_reregister(struct snd_tscm *tscm)
+{
+ struct fw_device *device = fw_parent_device(tscm->unit);
+ __be32 reg;
+ int err;
+
+ /* Register messaging address. Block transaction is not allowed. */
+ reg = cpu_to_be32((device->card->node_id << 16) |
+ (tscm->async_handler.offset >> 32));
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ reg = cpu_to_be32(tscm->async_handler.offset);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ /* Turn on messaging. */
+ reg = cpu_to_be32(0x00000001);
+ err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
+ &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ /* Turn on FireWire LED. */
+ reg = cpu_to_be32(0x0001008e);
+ return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
+ &reg, sizeof(reg), 0);
+}
+
+void snd_tscm_transaction_unregister(struct snd_tscm *tscm)
+{
+ __be32 reg;
+
+ if (tscm->async_handler.callback_data == NULL)
+ return;
+
+ /* Turn off FireWire LED. */
+ reg = cpu_to_be32(0x0000008e);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
+ &reg, sizeof(reg), 0);
+
+ /* Turn off messaging. */
+ reg = cpu_to_be32(0x00000000);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
+ &reg, sizeof(reg), 0);
+
+ /* Unregister the address. */
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
+ &reg, sizeof(reg), 0);
+ snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
+ &reg, sizeof(reg), 0);
+
+ fw_core_remove_address_handler(&tscm->async_handler);
+ tscm->async_handler.callback_data = NULL;
+}
diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c
new file mode 100644
index 000000000000..f4092df8650c
--- /dev/null
+++ b/sound/firewire/tascam/tascam.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * tascam.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ */
+
+#include "tascam.h"
+
+MODULE_DESCRIPTION("TASCAM FireWire series Driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL");
+
+static const struct snd_tscm_spec model_specs[] = {
+ {
+ .name = "FW-1884",
+ .has_adat = true,
+ .has_spdif = true,
+ .pcm_capture_analog_channels = 8,
+ .pcm_playback_analog_channels = 8,
+ .midi_capture_ports = 4,
+ .midi_playback_ports = 4,
+ },
+ {
+ .name = "FW-1082",
+ .has_adat = false,
+ .has_spdif = true,
+ .pcm_capture_analog_channels = 8,
+ .pcm_playback_analog_channels = 2,
+ .midi_capture_ports = 2,
+ .midi_playback_ports = 2,
+ },
+ {
+ .name = "FW-1804",
+ .has_adat = true,
+ .has_spdif = true,
+ .pcm_capture_analog_channels = 8,
+ .pcm_playback_analog_channels = 2,
+ .midi_capture_ports = 2,
+ .midi_playback_ports = 4,
+ },
+};
+
+static int identify_model(struct snd_tscm *tscm)
+{
+ struct fw_device *fw_dev = fw_parent_device(tscm->unit);
+ const u32 *config_rom = fw_dev->config_rom;
+ char model[9];
+ unsigned int i;
+ u8 c;
+
+ if (fw_dev->config_rom_length < 30) {
+ dev_err(&tscm->unit->device,
+ "Configuration ROM is too short.\n");
+ return -ENODEV;
+ }
+
+ /* Pick up model name from certain addresses. */
+ for (i = 0; i < 8; i++) {
+ c = config_rom[28 + i / 4] >> (24 - 8 * (i % 4));
+ if (c == '\0')
+ break;
+ model[i] = c;
+ }
+ model[i] = '\0';
+
+ for (i = 0; i < ARRAY_SIZE(model_specs); i++) {
+ if (strcmp(model, model_specs[i].name) == 0) {
+ tscm->spec = &model_specs[i];
+ break;
+ }
+ }
+ if (tscm->spec == NULL)
+ return -ENODEV;
+
+ strscpy(tscm->card->driver, "FW-TASCAM");
+ strscpy(tscm->card->shortname, model);
+ strscpy(tscm->card->mixername, model);
+ snprintf(tscm->card->longname, sizeof(tscm->card->longname),
+ "TASCAM %s, GUID %08x%08x at %s, S%d", model,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&tscm->unit->device), 100 << fw_dev->max_speed);
+
+ return 0;
+}
+
+static void tscm_card_free(struct snd_card *card)
+{
+ struct snd_tscm *tscm = card->private_data;
+
+ snd_tscm_transaction_unregister(tscm);
+ snd_tscm_stream_destroy_duplex(tscm);
+
+ mutex_destroy(&tscm->mutex);
+ fw_unit_put(tscm->unit);
+}
+
+static int snd_tscm_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_tscm *tscm;
+ int err;
+
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*tscm), &card);
+ if (err < 0)
+ return err;
+ card->private_free = tscm_card_free;
+
+ tscm = card->private_data;
+ tscm->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, tscm);
+ tscm->card = card;
+
+ mutex_init(&tscm->mutex);
+ spin_lock_init(&tscm->lock);
+ init_waitqueue_head(&tscm->hwdep_wait);
+
+ err = identify_model(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_transaction_register(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_stream_init_duplex(tscm);
+ if (err < 0)
+ goto error;
+
+ snd_tscm_proc_init(tscm);
+
+ err = snd_tscm_create_pcm_devices(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_create_midi_devices(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_tscm_create_hwdep_device(tscm);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
+ return 0;
+error:
+ snd_card_free(card);
+ return err;
+}
+
+static void snd_tscm_update(struct fw_unit *unit)
+{
+ struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
+
+ snd_tscm_transaction_reregister(tscm);
+
+ guard(mutex)(&tscm->mutex);
+ snd_tscm_stream_update_duplex(tscm);
+}
+
+static void snd_tscm_remove(struct fw_unit *unit)
+{
+ struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
+
+ // Block till all of ALSA character devices are released.
+ snd_card_free(tscm->card);
+}
+
+static const struct ieee1394_device_id snd_tscm_id_table[] = {
+ // Tascam, FW-1884.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
+ .vendor_id = 0x00022e,
+ .specifier_id = 0x00022e,
+ .version = 0x800000,
+ },
+ // Tascam, FE-8 (.version = 0x800001)
+ // This kernel module doesn't support FE-8 because the most of features
+ // can be implemented in userspace without any specific support of this
+ // module.
+ //
+ // .version = 0x800002 is unknown.
+ //
+ // Tascam, FW-1082.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
+ .vendor_id = 0x00022e,
+ .specifier_id = 0x00022e,
+ .version = 0x800003,
+ },
+ // Tascam, FW-1804.
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION,
+ .vendor_id = 0x00022e,
+ .specifier_id = 0x00022e,
+ .version = 0x800004,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table);
+
+static struct fw_driver tscm_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = snd_tscm_probe,
+ .update = snd_tscm_update,
+ .remove = snd_tscm_remove,
+ .id_table = snd_tscm_id_table,
+};
+
+static int __init snd_tscm_init(void)
+{
+ return driver_register(&tscm_driver.driver);
+}
+
+static void __exit snd_tscm_exit(void)
+{
+ driver_unregister(&tscm_driver.driver);
+}
+
+module_init(snd_tscm_init);
+module_exit(snd_tscm_exit);
diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h
new file mode 100644
index 000000000000..d07ffcb27be6
--- /dev/null
+++ b/sound/firewire/tascam/tascam.h
@@ -0,0 +1,212 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tascam.h - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ */
+
+#ifndef SOUND_TASCAM_H_INCLUDED
+#define SOUND_TASCAM_H_INCLUDED
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/sched/signal.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+#include <sound/rawmidi.h>
+
+#include "../lib.h"
+#include "../amdtp-stream.h"
+#include "../iso-resources.h"
+
+struct snd_tscm_spec {
+ const char *const name;
+ bool has_adat;
+ bool has_spdif;
+ unsigned int pcm_capture_analog_channels;
+ unsigned int pcm_playback_analog_channels;
+ unsigned int midi_capture_ports;
+ unsigned int midi_playback_ports;
+};
+
+#define TSCM_MIDI_IN_PORT_MAX 4
+#define TSCM_MIDI_OUT_PORT_MAX 4
+
+struct snd_fw_async_midi_port {
+ struct fw_device *parent;
+ struct work_struct work;
+ bool idling;
+ ktime_t next_ktime;
+ bool error;
+
+ struct fw_transaction transaction;
+
+ u8 buf[4];
+ u8 running_status;
+ bool on_sysex;
+
+ struct snd_rawmidi_substream *substream;
+ int consume_bytes;
+};
+
+#define SND_TSCM_QUEUE_COUNT 16
+
+struct snd_tscm {
+ struct snd_card *card;
+ struct fw_unit *unit;
+
+ struct mutex mutex;
+ spinlock_t lock;
+
+ const struct snd_tscm_spec *spec;
+
+ struct fw_iso_resources tx_resources;
+ struct fw_iso_resources rx_resources;
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ unsigned int substreams_counter;
+
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* For MIDI message incoming transactions. */
+ struct fw_address_handler async_handler;
+ struct snd_rawmidi_substream *tx_midi_substreams[TSCM_MIDI_IN_PORT_MAX];
+
+ /* For MIDI message outgoing transactions. */
+ struct snd_fw_async_midi_port out_ports[TSCM_MIDI_OUT_PORT_MAX];
+
+ // A cache of status information in tx isoc packets.
+ __be32 state[SNDRV_FIREWIRE_TASCAM_STATE_COUNT];
+ struct snd_hwdep *hwdep;
+ struct snd_firewire_tascam_change queue[SND_TSCM_QUEUE_COUNT];
+ unsigned int pull_pos;
+ unsigned int push_pos;
+
+ struct amdtp_domain domain;
+ bool need_long_tx_init_skip;
+};
+
+#define TSCM_ADDR_BASE 0xffff00000000ull
+
+#define TSCM_OFFSET_FIRMWARE_REGISTER 0x0000
+#define TSCM_OFFSET_FIRMWARE_FPGA 0x0004
+#define TSCM_OFFSET_FIRMWARE_ARM 0x0008
+#define TSCM_OFFSET_FIRMWARE_HW 0x000c
+
+#define TSCM_OFFSET_ISOC_TX_CH 0x0200
+#define TSCM_OFFSET_UNKNOWN 0x0204
+#define TSCM_OFFSET_START_STREAMING 0x0208
+#define TSCM_OFFSET_ISOC_RX_CH 0x020c
+#define TSCM_OFFSET_ISOC_RX_ON 0x0210 /* Little conviction. */
+#define TSCM_OFFSET_TX_PCM_CHANNELS 0x0214
+#define TSCM_OFFSET_RX_PCM_CHANNELS 0x0218
+#define TSCM_OFFSET_MULTIPLEX_MODE 0x021c
+#define TSCM_OFFSET_ISOC_TX_ON 0x0220
+/* Unknown 0x0224 */
+#define TSCM_OFFSET_CLOCK_STATUS 0x0228
+#define TSCM_OFFSET_SET_OPTION 0x022c
+
+#define TSCM_OFFSET_MIDI_TX_ON 0x0300
+#define TSCM_OFFSET_MIDI_TX_ADDR_HI 0x0304
+#define TSCM_OFFSET_MIDI_TX_ADDR_LO 0x0308
+
+#define TSCM_OFFSET_LED_POWER 0x0404
+
+#define TSCM_OFFSET_MIDI_RX_QUAD 0x4000
+
+// Although FE-8 supports the above registers, it has no I/O interfaces for
+// audio samples and music messages. Otherwise it supports another notification
+// for status and control message as well as LED brightening. The message
+// consists of quadlet-aligned data up to 32 quadlets. The first byte of message
+// is fixed to 0x40. The second byte is between 0x00 to 0x1f and represent each
+// control:
+// fader: 0x00-0x07
+// button: 0x0d, 0x0e
+// knob: 0x14-0x1b
+// sensing: 0x0b
+//
+// The rest two bytes represent state of the controls; e.g. current value for
+// fader and knob, bitmasks for button and sensing.
+// Just after turning on, 32 quadlets messages with 0x00-0x1f are immediately
+// sent in one transaction. After, several quadlets are sent in one transaction.
+//
+// TSCM_OFFSET_FE8_CTL_TX_ON 0x0310
+// TSCM_OFFSET_FE8_CTL_TX_ADDR_HI 0x0314
+// TSCM_OFFSET_FE8_CTL_TX_ADDR_LO 0x0318
+
+enum snd_tscm_clock {
+ SND_TSCM_CLOCK_INTERNAL = 0,
+ SND_TSCM_CLOCK_WORD = 1,
+ SND_TSCM_CLOCK_SPDIF = 2,
+ SND_TSCM_CLOCK_ADAT = 3,
+};
+
+int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, unsigned int pcm_channels);
+int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate);
+int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+
+int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate);
+int snd_tscm_stream_get_clock(struct snd_tscm *tscm,
+ enum snd_tscm_clock *clock);
+int snd_tscm_stream_init_duplex(struct snd_tscm *tscm);
+void snd_tscm_stream_update_duplex(struct snd_tscm *tscm);
+void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm);
+int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate,
+ unsigned int frames_per_period,
+ unsigned int frames_per_buffer);
+int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate);
+void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm);
+
+void snd_tscm_stream_lock_changed(struct snd_tscm *tscm);
+int snd_tscm_stream_lock_try(struct snd_tscm *tscm);
+void snd_tscm_stream_lock_release(struct snd_tscm *tscm);
+
+void snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port);
+
+static inline void
+snd_fw_async_midi_port_run(struct snd_fw_async_midi_port *port,
+ struct snd_rawmidi_substream *substream)
+{
+ if (!port->error) {
+ port->substream = substream;
+ schedule_work(&port->work);
+ }
+}
+
+static inline void
+snd_fw_async_midi_port_finish(struct snd_fw_async_midi_port *port)
+{
+ port->substream = NULL;
+ cancel_work_sync(&port->work);
+ port->error = false;
+}
+
+int snd_tscm_transaction_register(struct snd_tscm *tscm);
+int snd_tscm_transaction_reregister(struct snd_tscm *tscm);
+void snd_tscm_transaction_unregister(struct snd_tscm *tscm);
+
+void snd_tscm_proc_init(struct snd_tscm *tscm);
+
+int snd_tscm_create_pcm_devices(struct snd_tscm *tscm);
+
+int snd_tscm_create_midi_devices(struct snd_tscm *tscm);
+
+int snd_tscm_create_hwdep_device(struct snd_tscm *tscm);
+
+#endif
diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig
new file mode 100644
index 000000000000..7797f44b3d0c
--- /dev/null
+++ b/sound/hda/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "HD-Audio"
+
+source "sound/hda/common/Kconfig"
+source "sound/hda/controllers/Kconfig"
+source "sound/hda/codecs/Kconfig"
+source "sound/hda/core/Kconfig"
+
+endmenu
diff --git a/sound/hda/Makefile b/sound/hda/Makefile
new file mode 100644
index 000000000000..d9a6def582ef
--- /dev/null
+++ b/sound/hda/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-y += core/
+obj-$(CONFIG_SND_HDA) += common/
+obj-$(CONFIG_SND_HDA) += codecs/
+# this must be the last entry after codec drivers;
+# otherwise the codec drivers won't be hooked before the PCI probe
+# when built in kernel
+obj-$(CONFIG_SND_HDA) += controllers/
diff --git a/sound/hda/codecs/Kconfig b/sound/hda/codecs/Kconfig
new file mode 100644
index 000000000000..addbc9424336
--- /dev/null
+++ b/sound/hda/codecs/Kconfig
@@ -0,0 +1,137 @@
+# SPDX-License-Identifier: GPL-2.0-only
+if SND_HDA
+
+config SND_HDA_GENERIC_LEDS
+ bool
+
+config SND_HDA_CODEC_ANALOG
+ tristate "Build Analog Devices HD-audio codec support"
+ select SND_HDA_GENERIC
+ help
+ Say Y or M here to include Analog Devices HD-audio codec support in
+ snd-hda-intel driver, such as AD1986A.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_ANALOG=m
+
+config SND_HDA_CODEC_SIGMATEL
+ tristate "Build IDT/Sigmatel HD-audio codec support"
+ select SND_HDA_GENERIC
+ select SND_HDA_GENERIC_LEDS
+ help
+ Say Y or M here to include IDT (Sigmatel) HD-audio codec support in
+ snd-hda-intel driver, such as STAC9200.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_SIGMATEL=m
+
+config SND_HDA_CODEC_VIA
+ tristate "Build VIA HD-audio codec support"
+ select SND_HDA_GENERIC
+ help
+ Say Y or M here to include VIA HD-audio codec support in
+ snd-hda-intel driver, such as VT1708.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_VIA=m
+
+config SND_HDA_CODEC_CONEXANT
+ tristate "Build Conexant HD-audio codec support"
+ select SND_HDA_GENERIC
+ select SND_HDA_GENERIC_LEDS
+ help
+ Say Y or M here to include Conexant HD-audio codec support in
+ snd-hda-intel driver, such as CX20549.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CONEXANT=m
+
+config SND_HDA_CODEC_SENARYTECH
+ tristate "Build Senarytech HD-audio codec support"
+ select SND_HDA_GENERIC
+ select SND_HDA_GENERIC_LEDS
+ help
+ Say Y or M here to include Senarytech HD-audio codec support in
+ snd-hda-intel driver, such as SN6186.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_SENARYTECH=m
+
+config SND_HDA_CODEC_CA0110
+ tristate "Build Creative CA0110-IBG codec support"
+ select SND_HDA_GENERIC
+ help
+ Say Y or M here to include Creative CA0110-IBG codec support in
+ snd-hda-intel driver, found on some Creative X-Fi cards.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CA0110=m
+
+config SND_HDA_CODEC_CA0132
+ tristate "Build Creative CA0132 codec support"
+ help
+ Say Y or M here to include Creative CA0132 codec support in
+ snd-hda-intel driver.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CA0132=m
+
+config SND_HDA_CODEC_CA0132_DSP
+ bool "Support new DSP code for CA0132 codec"
+ depends on SND_HDA_CODEC_CA0132
+ default y
+ select SND_HDA_DSP_LOADER
+ select FW_LOADER
+ help
+ Say Y here to enable the DSP for Creative CA0132 for extended
+ features like equalizer or echo cancellation.
+
+ Note that this option requires the external firmware file
+ (ctefx.bin).
+
+config SND_HDA_CODEC_CMEDIA
+ tristate "Build C-Media HD-audio codec support"
+ select SND_HDA_GENERIC
+ help
+ Say Y or M here to include C-Media HD-audio codec support in
+ snd-hda-intel driver, such as CMI9880.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CMEDIA=m
+
+config SND_HDA_CODEC_CM9825
+ tristate "Build C-Media CM9825 HD-audio codec support"
+ select SND_HDA_GENERIC
+ help
+ Say Y or M here to include C-Media CM9825 HD-audio codec support in
+ snd-hda-intel driver
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CM9825=m
+
+config SND_HDA_CODEC_SI3054
+ tristate "Build Silicon Labs 3054 HD-modem codec support"
+ help
+ Say Y or M here to include Silicon Labs 3054 HD-modem codec
+ (and compatibles) support in snd-hda-intel driver.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_SI3054=m
+
+config SND_HDA_GENERIC
+ tristate "Enable generic HD-audio codec parser"
+ select SND_CTL_LED if SND_HDA_GENERIC_LEDS
+ select LEDS_CLASS if SND_HDA_GENERIC_LEDS
+ help
+ Say Y or M here to enable the generic HD-audio codec parser
+ in snd-hda-intel driver.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_GENERIC=m
+
+source "sound/hda/codecs/realtek/Kconfig"
+source "sound/hda/codecs/cirrus/Kconfig"
+source "sound/hda/codecs/hdmi/Kconfig"
+source "sound/hda/codecs/side-codecs/Kconfig"
+
+endif # SND_HDA
diff --git a/sound/hda/codecs/Makefile b/sound/hda/codecs/Makefile
new file mode 100644
index 000000000000..e7f03e281999
--- /dev/null
+++ b/sound/hda/codecs/Makefile
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: GPL-2.0
+subdir-ccflags-y += -I$(src)/../common
+
+snd-hda-codec-generic-y := generic.o
+snd-hda-codec-cmedia-y := cmedia.o
+snd-hda-codec-cm9825-y := cm9825.o
+snd-hda-codec-analog-y := analog.o
+snd-hda-codec-ca0110-y := ca0110.o
+snd-hda-codec-ca0132-y := ca0132.o
+snd-hda-codec-cmedia-y := cmedia.o
+snd-hda-codec-conexant-y := conexant.o
+snd-hda-codec-idt-y := sigmatel.o
+snd-hda-codec-senarytech-y := senarytech.o
+snd-hda-codec-si3054-y := si3054.o
+snd-hda-codec-via-y := via.o
+
+obj-y += cirrus/
+obj-y += hdmi/
+obj-y += realtek/
+obj-y += side-codecs/
+
+# codec drivers
+obj-$(CONFIG_SND_HDA_GENERIC) += snd-hda-codec-generic.o
+obj-$(CONFIG_SND_HDA_CODEC_CMEDIA) += snd-hda-codec-cmedia.o
+obj-$(CONFIG_SND_HDA_CODEC_CM9825) += snd-hda-codec-cm9825.o
+obj-$(CONFIG_SND_HDA_CODEC_ANALOG) += snd-hda-codec-analog.o
+obj-$(CONFIG_SND_HDA_CODEC_CA0110) += snd-hda-codec-ca0110.o
+obj-$(CONFIG_SND_HDA_CODEC_CA0132) += snd-hda-codec-ca0132.o
+obj-$(CONFIG_SND_HDA_CODEC_CMEDIA) += snd-hda-codec-cmedia.o
+obj-$(CONFIG_SND_HDA_CODEC_CONEXANT) += snd-hda-codec-conexant.o
+obj-$(CONFIG_SND_HDA_CODEC_SIGMATEL) += snd-hda-codec-idt.o
+obj-$(CONFIG_SND_HDA_CODEC_SENARYTECH) += snd-hda-codec-senarytech.o
+obj-$(CONFIG_SND_HDA_CODEC_SI3054) += snd-hda-codec-si3054.o
+obj-$(CONFIG_SND_HDA_CODEC_VIA) += snd-hda-codec-via.o
diff --git a/sound/hda/codecs/analog.c b/sound/hda/codecs/analog.c
new file mode 100644
index 000000000000..357ad5a6c0db
--- /dev/null
+++ b/sound/hda/codecs/analog.c
@@ -0,0 +1,1176 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HD audio codec driver for AD1882, AD1884, AD1981HD, AD1983, AD1984,
+ * AD1986A, AD1988
+ *
+ * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_beep.h"
+#include "hda_jack.h"
+#include "generic.h"
+
+enum {
+ MODEL_AD1882,
+ MODEL_AD1884,
+ MODEL_AD1981,
+ MODEL_AD1983,
+ MODEL_AD1986A,
+ MODEL_AD1988,
+};
+
+struct ad198x_spec {
+ struct hda_gen_spec gen;
+ int model;
+
+ /* for auto parser */
+ int smux_paths[4];
+ unsigned int cur_smux;
+ hda_nid_t eapd_nid;
+
+ unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
+ int num_smux_conns;
+};
+
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/* additional beep mixers; the actual parameters are overwritten at build */
+static const struct snd_kcontrol_new ad_beep_mixer[] = {
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
+ { } /* end */
+};
+
+#define set_beep_amp(spec, nid, idx, dir) \
+ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
+#else
+#define set_beep_amp(spec, nid, idx, dir) /* NOP */
+#endif
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+static int create_beep_ctls(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+ const struct snd_kcontrol_new *knew;
+
+ if (!spec->beep_amp)
+ return 0;
+
+ for (knew = ad_beep_mixer ; knew->name; knew++) {
+ int err;
+ struct snd_kcontrol *kctl;
+ kctl = snd_ctl_new1(knew, codec);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->private_value = spec->beep_amp;
+ err = snd_hda_ctl_add(codec, 0, kctl);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+#else
+#define create_beep_ctls(codec) 0
+#endif
+
+static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
+ hda_nid_t hp)
+{
+ if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD)
+ snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
+ !codec->inv_eapd ? 0x00 : 0x02);
+ if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD)
+ snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
+ !codec->inv_eapd ? 0x00 : 0x02);
+}
+
+static void ad198x_power_eapd(struct hda_codec *codec)
+{
+ /* We currently only handle front, HP */
+ switch (codec->core.vendor_id) {
+ case 0x11d41882:
+ case 0x11d4882a:
+ case 0x11d41884:
+ case 0x11d41984:
+ case 0x11d41883:
+ case 0x11d4184a:
+ case 0x11d4194a:
+ case 0x11d4194b:
+ case 0x11d41988:
+ case 0x11d4198b:
+ case 0x11d4989a:
+ case 0x11d4989b:
+ ad198x_power_eapd_write(codec, 0x12, 0x11);
+ break;
+ case 0x11d41981:
+ case 0x11d41983:
+ ad198x_power_eapd_write(codec, 0x05, 0x06);
+ break;
+ case 0x11d41986:
+ ad198x_power_eapd_write(codec, 0x1b, 0x1a);
+ break;
+ }
+}
+
+static int ad_codec_suspend(struct hda_codec *codec)
+{
+ snd_hda_shutup_pins(codec);
+ ad198x_power_eapd(codec);
+ return 0;
+}
+
+/* follow EAPD via vmaster hook */
+static void ad_vmaster_eapd_hook(void *private_data, int enabled)
+{
+ struct hda_codec *codec = private_data;
+ struct ad198x_spec *spec = codec->spec;
+
+ if (!spec->eapd_nid)
+ return;
+ if (codec->inv_eapd)
+ enabled = !enabled;
+ snd_hda_codec_write_cache(codec, spec->eapd_nid, 0,
+ AC_VERB_SET_EAPD_BTLENABLE,
+ enabled ? 0x02 : 0x00);
+}
+
+/*
+ * Automatic parse of I/O pins from the BIOS configuration
+ */
+
+static int ad_codec_build_controls(struct hda_codec *codec)
+{
+ int err;
+
+ err = snd_hda_gen_build_controls(codec);
+ if (err < 0)
+ return err;
+ err = create_beep_ctls(codec);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int ad198x_parse_auto_config(struct hda_codec *codec, bool indep_hp)
+{
+ struct ad198x_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ int err;
+
+ codec->spdif_status_reset = 1;
+ codec->no_trigger_sense = 1;
+ codec->no_sticky_stream = 1;
+
+ spec->gen.indep_hp = indep_hp;
+ if (!spec->gen.add_stereo_mix_input)
+ spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
+
+ err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
+ if (err < 0)
+ return err;
+ err = snd_hda_gen_parse_auto_config(codec, cfg);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/*
+ * AD1986A specific
+ */
+
+static int alloc_ad_spec(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ codec->spec = spec;
+ snd_hda_gen_spec_init(&spec->gen);
+ return 0;
+}
+
+/*
+ * AD1986A fixup codes
+ */
+
+/* Lenovo N100 seems to report the reversed bit for HP jack-sensing */
+static void ad_fixup_inv_jack_detect(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct ad198x_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ codec->inv_jack_detect = 1;
+ spec->gen.keep_eapd_on = 1;
+ spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
+ spec->eapd_nid = 0x1b;
+ }
+}
+
+/* Toshiba Satellite L40 implements EAPD in a standard way unlike others */
+static void ad1986a_fixup_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct ad198x_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ codec->inv_eapd = 0;
+ spec->gen.keep_eapd_on = 1;
+ spec->eapd_nid = 0x1b;
+ }
+}
+
+/* enable stereo-mix input for avoiding regression on KDE (bko#88251) */
+static void ad1986a_fixup_eapd_mix_in(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct ad198x_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ ad1986a_fixup_eapd(codec, fix, action);
+ spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_ENABLE;
+ }
+}
+
+enum {
+ AD1986A_FIXUP_INV_JACK_DETECT,
+ AD1986A_FIXUP_ULTRA,
+ AD1986A_FIXUP_SAMSUNG,
+ AD1986A_FIXUP_3STACK,
+ AD1986A_FIXUP_LAPTOP,
+ AD1986A_FIXUP_LAPTOP_IMIC,
+ AD1986A_FIXUP_EAPD,
+ AD1986A_FIXUP_EAPD_MIX_IN,
+ AD1986A_FIXUP_EASYNOTE,
+};
+
+static const struct hda_fixup ad1986a_fixups[] = {
+ [AD1986A_FIXUP_INV_JACK_DETECT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad_fixup_inv_jack_detect,
+ },
+ [AD1986A_FIXUP_ULTRA] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x90170110 }, /* speaker */
+ { 0x1d, 0x90a7013e }, /* int mic */
+ {}
+ },
+ },
+ [AD1986A_FIXUP_SAMSUNG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x90170110 }, /* speaker */
+ { 0x1d, 0x90a7013e }, /* int mic */
+ { 0x20, 0x411111f0 }, /* N/A */
+ { 0x24, 0x411111f0 }, /* N/A */
+ {}
+ },
+ },
+ [AD1986A_FIXUP_3STACK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x02214021 }, /* headphone */
+ { 0x1b, 0x01014011 }, /* front */
+ { 0x1c, 0x01813030 }, /* line-in */
+ { 0x1d, 0x01a19020 }, /* rear mic */
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { 0x1f, 0x02a190f0 }, /* mic */
+ { 0x20, 0x411111f0 }, /* N/A */
+ {}
+ },
+ },
+ [AD1986A_FIXUP_LAPTOP] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x02214021 }, /* headphone */
+ { 0x1b, 0x90170110 }, /* speaker */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { 0x1f, 0x02a191f0 }, /* mic */
+ { 0x20, 0x411111f0 }, /* N/A */
+ {}
+ },
+ },
+ [AD1986A_FIXUP_LAPTOP_IMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1d, 0x90a7013e }, /* int mic */
+ {}
+ },
+ .chained_before = 1,
+ .chain_id = AD1986A_FIXUP_LAPTOP,
+ },
+ [AD1986A_FIXUP_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1986a_fixup_eapd,
+ },
+ [AD1986A_FIXUP_EAPD_MIX_IN] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1986a_fixup_eapd_mix_in,
+ },
+ [AD1986A_FIXUP_EASYNOTE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x0421402f }, /* headphone */
+ { 0x1b, 0x90170110 }, /* speaker */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x90a70130 }, /* int mic */
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { 0x1f, 0x04a19040 }, /* mic */
+ { 0x20, 0x411111f0 }, /* N/A */
+ { 0x21, 0x411111f0 }, /* N/A */
+ { 0x22, 0x411111f0 }, /* N/A */
+ { 0x23, 0x411111f0 }, /* N/A */
+ { 0x24, 0x411111f0 }, /* N/A */
+ { 0x25, 0x411111f0 }, /* N/A */
+ {}
+ },
+ .chained = true,
+ .chain_id = AD1986A_FIXUP_EAPD_MIX_IN,
+ },
+};
+
+static const struct hda_quirk ad1986a_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_FIXUP_LAPTOP_IMIC),
+ SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9V", AD1986A_FIXUP_LAPTOP_IMIC),
+ SND_PCI_QUIRK(0x1043, 0x1443, "ASUS Z99He", AD1986A_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8JN", AD1986A_FIXUP_EAPD),
+ SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8100, "ASUS P5", AD1986A_FIXUP_3STACK),
+ SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8200, "ASUS M2", AD1986A_FIXUP_3STACK),
+ SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_FIXUP_3STACK),
+ SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40", AD1986A_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_FIXUP_LAPTOP),
+ SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_FIXUP_SAMSUNG),
+ SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_FIXUP_ULTRA),
+ SND_PCI_QUIRK(0x1631, 0xc022, "PackardBell EasyNote MX65", AD1986A_FIXUP_EASYNOTE),
+ SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_FIXUP_INV_JACK_DETECT),
+ SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_FIXUP_3STACK),
+ SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_FIXUP_3STACK),
+ {}
+};
+
+static const struct hda_model_fixup ad1986a_fixup_models[] = {
+ { .id = AD1986A_FIXUP_3STACK, .name = "3stack" },
+ { .id = AD1986A_FIXUP_LAPTOP, .name = "laptop" },
+ { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-imic" },
+ { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-eapd" }, /* alias */
+ { .id = AD1986A_FIXUP_EAPD, .name = "eapd" },
+ {}
+};
+
+/*
+ */
+static int ad1986a_probe(struct hda_codec *codec)
+{
+ int err;
+ struct ad198x_spec *spec = codec->spec;
+ static const hda_nid_t preferred_pairs[] = {
+ 0x1a, 0x03,
+ 0x1b, 0x03,
+ 0x1c, 0x04,
+ 0x1d, 0x05,
+ 0x1e, 0x03,
+ 0
+ };
+
+ /* AD1986A has the inverted EAPD implementation */
+ codec->inv_eapd = 1;
+
+ spec->gen.mixer_nid = 0x07;
+ spec->gen.beep_nid = 0x19;
+ set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
+
+ /* AD1986A has a hardware problem that it can't share a stream
+ * with multiple output pins. The copy of front to surrounds
+ * causes noisy or silent outputs at a certain timing, e.g.
+ * changing the volume.
+ * So, let's disable the shared stream.
+ */
+ spec->gen.multiout.no_share_stream = 1;
+ /* give fixed DAC/pin pairs */
+ spec->gen.preferred_dacs = preferred_pairs;
+
+ /* AD1986A can't manage the dynamic pin on/off smoothly */
+ spec->gen.auto_mute_via_amp = 1;
+
+ snd_hda_pick_fixup(codec, ad1986a_fixup_models, ad1986a_fixup_tbl,
+ ad1986a_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = ad198x_parse_auto_config(codec, false);
+ if (err < 0)
+ return err;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+}
+
+
+/*
+ * AD1983 specific
+ */
+
+/*
+ * SPDIF mux control for AD1983 auto-parser
+ */
+static int ad1983_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad198x_spec *spec = codec->spec;
+ static const char * const texts2[] = { "PCM", "ADC" };
+ static const char * const texts3[] = { "PCM", "ADC1", "ADC2" };
+ int num_conns = spec->num_smux_conns;
+
+ if (num_conns == 2)
+ return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts2);
+ else if (num_conns == 3)
+ return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3);
+ else
+ return -EINVAL;
+}
+
+static int ad1983_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad198x_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->cur_smux;
+ return 0;
+}
+
+static int ad1983_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad198x_spec *spec = codec->spec;
+ unsigned int val = ucontrol->value.enumerated.item[0];
+ hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
+ int num_conns = spec->num_smux_conns;
+
+ if (val >= num_conns)
+ return -EINVAL;
+ if (spec->cur_smux == val)
+ return 0;
+ spec->cur_smux = val;
+ snd_hda_codec_write_cache(codec, dig_out, 0,
+ AC_VERB_SET_CONNECT_SEL, val);
+ return 1;
+}
+
+static const struct snd_kcontrol_new ad1983_auto_smux_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Playback Source",
+ .info = ad1983_auto_smux_enum_info,
+ .get = ad1983_auto_smux_enum_get,
+ .put = ad1983_auto_smux_enum_put,
+};
+
+static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+ hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
+ int num_conns;
+
+ if (!dig_out)
+ return 0;
+ num_conns = snd_hda_get_num_conns(codec, dig_out);
+ if (num_conns != 2 && num_conns != 3)
+ return 0;
+ spec->num_smux_conns = num_conns;
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1983_auto_smux_mixer))
+ return -ENOMEM;
+ return 0;
+}
+
+static int ad1983_probe(struct hda_codec *codec)
+{
+ static const hda_nid_t conn_0c[] = { 0x08 };
+ static const hda_nid_t conn_0d[] = { 0x09 };
+ struct ad198x_spec *spec = codec->spec;
+ int err;
+
+ spec->gen.mixer_nid = 0x0e;
+ spec->gen.beep_nid = 0x10;
+ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
+ /* limit the loopback routes not to confuse the parser */
+ snd_hda_override_conn_list(codec, 0x0c, ARRAY_SIZE(conn_0c), conn_0c);
+ snd_hda_override_conn_list(codec, 0x0d, ARRAY_SIZE(conn_0d), conn_0d);
+
+ err = ad198x_parse_auto_config(codec, false);
+ if (err < 0)
+ return err;
+ err = ad1983_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+
+/*
+ * AD1981 HD specific
+ */
+
+static void ad1981_fixup_hp_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct ad198x_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
+ spec->eapd_nid = 0x05;
+ }
+}
+
+/* set the upper-limit for mixer amp to 0dB for avoiding the possible
+ * damage by overloading
+ */
+static void ad1981_fixup_amp_override(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
+ (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (1 << AC_AMPCAP_MUTE_SHIFT));
+}
+
+enum {
+ AD1981_FIXUP_AMP_OVERRIDE,
+ AD1981_FIXUP_HP_EAPD,
+};
+
+static const struct hda_fixup ad1981_fixups[] = {
+ [AD1981_FIXUP_AMP_OVERRIDE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1981_fixup_amp_override,
+ },
+ [AD1981_FIXUP_HP_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1981_fixup_hp_eapd,
+ .chained = true,
+ .chain_id = AD1981_FIXUP_AMP_OVERRIDE,
+ },
+};
+
+static const struct hda_quirk ad1981_fixup_tbl[] = {
+ SND_PCI_QUIRK_VENDOR(0x1014, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE),
+ SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1981_FIXUP_HP_EAPD),
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE),
+ /* HP nx6320 (reversed SSID, H/W bug) */
+ SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_FIXUP_HP_EAPD),
+ {}
+};
+
+static int ad1981_probe(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+ int err;
+
+ spec->gen.mixer_nid = 0x0e;
+ spec->gen.beep_nid = 0x10;
+ set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
+
+ snd_hda_pick_fixup(codec, NULL, ad1981_fixup_tbl, ad1981_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = ad198x_parse_auto_config(codec, false);
+ if (err < 0)
+ return err;
+ err = ad1983_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ return err;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+}
+
+
+/*
+ * AD1988
+ *
+ * Output pins and routes
+ *
+ * Pin Mix Sel DAC (*)
+ * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
+ * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
+ * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
+ * port-D 0x12 (mute/hp) <- 0x29 <- 04
+ * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
+ * port-F 0x16 (mute) <- 0x2a <- 06
+ * port-G 0x24 (mute) <- 0x27 <- 05
+ * port-H 0x25 (mute) <- 0x28 <- 0a
+ * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
+ *
+ * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
+ * (*) DAC2/3/4 are swapped to DAC3/4/2 on AD198A rev.2 due to a h/w bug.
+ *
+ * Input pins and routes
+ *
+ * pin boost mix input # / adc input #
+ * port-A 0x11 -> 0x38 -> mix 2, ADC 0
+ * port-B 0x14 -> 0x39 -> mix 0, ADC 1
+ * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
+ * port-D 0x12 -> 0x3d -> mix 3, ADC 8
+ * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
+ * port-F 0x16 -> 0x3b -> mix 5, ADC 3
+ * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
+ * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
+ *
+ *
+ * DAC assignment
+ * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
+ * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
+ *
+ * Inputs of Analog Mix (0x20)
+ * 0:Port-B (front mic)
+ * 1:Port-C/G/H (line-in)
+ * 2:Port-A
+ * 3:Port-D (line-in/2)
+ * 4:Port-E/G/H (mic-in)
+ * 5:Port-F (mic2-in)
+ * 6:CD
+ * 7:Beep
+ *
+ * ADC selection
+ * 0:Port-A
+ * 1:Port-B (front mic-in)
+ * 2:Port-C (line-in)
+ * 3:Port-F (mic2-in)
+ * 4:Port-E (mic-in)
+ * 5:CD
+ * 6:Port-G
+ * 7:Port-H
+ * 8:Port-D (line-in/2)
+ * 9:Mix
+ *
+ * Proposed pin assignments by the datasheet
+ *
+ * 6-stack
+ * Port-A front headphone
+ * B front mic-in
+ * C rear line-in
+ * D rear front-out
+ * E rear mic-in
+ * F rear surround
+ * G rear CLFE
+ * H rear side
+ *
+ * 3-stack
+ * Port-A front headphone
+ * B front mic
+ * C rear line-in/surround
+ * D rear front-out
+ * E rear mic-in/CLFE
+ *
+ * laptop
+ * Port-A headphone
+ * B mic-in
+ * C docking station
+ * D internal speaker (with EAPD)
+ * E/F quad mic array
+ */
+
+static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad198x_spec *spec = codec->spec;
+ static const char * const texts[] = {
+ "PCM", "ADC1", "ADC2", "ADC3",
+ };
+ int num_conns = spec->num_smux_conns;
+
+ if (num_conns > 4)
+ num_conns = 4;
+ return snd_hda_enum_helper_info(kcontrol, uinfo, num_conns, texts);
+}
+
+static int ad1988_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad198x_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->cur_smux;
+ return 0;
+}
+
+static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad198x_spec *spec = codec->spec;
+ unsigned int val = ucontrol->value.enumerated.item[0];
+ struct nid_path *path;
+ int num_conns = spec->num_smux_conns;
+
+ if (val >= num_conns)
+ return -EINVAL;
+ if (spec->cur_smux == val)
+ return 0;
+
+ guard(mutex)(&codec->control_mutex);
+ path = snd_hda_get_path_from_idx(codec,
+ spec->smux_paths[spec->cur_smux]);
+ if (path)
+ snd_hda_activate_path(codec, path, false, true);
+ path = snd_hda_get_path_from_idx(codec, spec->smux_paths[val]);
+ if (path)
+ snd_hda_activate_path(codec, path, true, true);
+ spec->cur_smux = val;
+ return 1;
+}
+
+static const struct snd_kcontrol_new ad1988_auto_smux_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Playback Source",
+ .info = ad1988_auto_smux_enum_info,
+ .get = ad1988_auto_smux_enum_get,
+ .put = ad1988_auto_smux_enum_put,
+};
+
+static int ad_codec_init(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+ int i, err;
+
+ err = snd_hda_gen_init(codec);
+ if (err < 0)
+ return err;
+ if (spec->model != MODEL_AD1988)
+ return 0;
+ if (!spec->gen.autocfg.dig_outs)
+ return 0;
+
+ for (i = 0; i < 4; i++) {
+ struct nid_path *path;
+ path = snd_hda_get_path_from_idx(codec, spec->smux_paths[i]);
+ if (path)
+ snd_hda_activate_path(codec, path, path->active, false);
+ }
+
+ return 0;
+}
+
+static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+ int i, num_conns;
+ /* we create four static faked paths, since AD codecs have odd
+ * widget connections regarding the SPDIF out source
+ */
+ static const struct nid_path fake_paths[4] = {
+ {
+ .depth = 3,
+ .path = { 0x02, 0x1d, 0x1b },
+ .idx = { 0, 0, 0 },
+ .multi = { 0, 0, 0 },
+ },
+ {
+ .depth = 4,
+ .path = { 0x08, 0x0b, 0x1d, 0x1b },
+ .idx = { 0, 0, 1, 0 },
+ .multi = { 0, 1, 0, 0 },
+ },
+ {
+ .depth = 4,
+ .path = { 0x09, 0x0b, 0x1d, 0x1b },
+ .idx = { 0, 1, 1, 0 },
+ .multi = { 0, 1, 0, 0 },
+ },
+ {
+ .depth = 4,
+ .path = { 0x0f, 0x0b, 0x1d, 0x1b },
+ .idx = { 0, 2, 1, 0 },
+ .multi = { 0, 1, 0, 0 },
+ },
+ };
+
+ /* SPDIF source mux appears to be present only on AD1988A */
+ if (!spec->gen.autocfg.dig_outs ||
+ get_wcaps_type(get_wcaps(codec, 0x1d)) != AC_WID_AUD_MIX)
+ return 0;
+
+ num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
+ if (num_conns != 3 && num_conns != 4)
+ return 0;
+ spec->num_smux_conns = num_conns;
+
+ for (i = 0; i < num_conns; i++) {
+ struct nid_path *path = snd_array_new(&spec->gen.paths);
+ if (!path)
+ return -ENOMEM;
+ *path = fake_paths[i];
+ if (!i)
+ path->active = 1;
+ spec->smux_paths[i] = snd_hda_get_path_idx(codec, path);
+ }
+
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1988_auto_smux_mixer))
+ return -ENOMEM;
+
+ return 0;
+}
+
+/*
+ */
+
+enum {
+ AD1988_FIXUP_6STACK_DIG,
+};
+
+static const struct hda_fixup ad1988_fixups[] = {
+ [AD1988_FIXUP_6STACK_DIG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x11, 0x02214130 }, /* front-hp */
+ { 0x12, 0x01014010 }, /* line-out */
+ { 0x14, 0x02a19122 }, /* front-mic */
+ { 0x15, 0x01813021 }, /* line-in */
+ { 0x16, 0x01011012 }, /* line-out */
+ { 0x17, 0x01a19020 }, /* mic */
+ { 0x1b, 0x0145f1f0 }, /* SPDIF */
+ { 0x24, 0x01016011 }, /* line-out */
+ { 0x25, 0x01012013 }, /* line-out */
+ { }
+ }
+ },
+};
+
+static const struct hda_model_fixup ad1988_fixup_models[] = {
+ { .id = AD1988_FIXUP_6STACK_DIG, .name = "6stack-dig" },
+ {}
+};
+
+static int ad1988_probe(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+ int err;
+
+ spec->gen.mixer_nid = 0x20;
+ spec->gen.mixer_merge_nid = 0x21;
+ spec->gen.beep_nid = 0x10;
+ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
+ snd_hda_pick_fixup(codec, ad1988_fixup_models, NULL, ad1988_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = ad198x_parse_auto_config(codec, true);
+ if (err < 0)
+ return err;
+ err = ad1988_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ return err;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+}
+
+
+/*
+ * AD1884 / AD1984
+ *
+ * port-B - front line/mic-in
+ * port-E - aux in/out
+ * port-F - aux in/out
+ * port-C - rear line/mic-in
+ * port-D - rear line/hp-out
+ * port-A - front line/hp-out
+ *
+ * AD1984 = AD1884 + two digital mic-ins
+ *
+ * AD1883 / AD1884A / AD1984A / AD1984B
+ *
+ * port-B (0x14) - front mic-in
+ * port-E (0x1c) - rear mic-in
+ * port-F (0x16) - CD / ext out
+ * port-C (0x15) - rear line-in
+ * port-D (0x12) - rear line-out
+ * port-A (0x11) - front hp-out
+ *
+ * AD1984A = AD1884A + digital-mic
+ * AD1883 = equivalent with AD1984A
+ * AD1984B = AD1984A + extra SPDIF-out
+ */
+
+/* set the upper-limit for mixer amp to 0dB for avoiding the possible
+ * damage by overloading
+ */
+static void ad1884_fixup_amp_override(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
+ (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (1 << AC_AMPCAP_MUTE_SHIFT));
+}
+
+/* toggle GPIO1 according to the mute state */
+static void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled)
+{
+ struct hda_codec *codec = private_data;
+ struct ad198x_spec *spec = codec->spec;
+
+ if (spec->eapd_nid)
+ ad_vmaster_eapd_hook(private_data, enabled);
+ snd_hda_codec_write_cache(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA,
+ enabled ? 0x00 : 0x02);
+}
+
+static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct ad198x_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook;
+ spec->gen.own_eapd_ctl = 1;
+ snd_hda_codec_write_cache(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_MASK, 0x02);
+ snd_hda_codec_write_cache(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DIRECTION, 0x02);
+ snd_hda_codec_write_cache(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, 0x02);
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
+ spec->eapd_nid = spec->gen.autocfg.line_out_pins[0];
+ else
+ spec->eapd_nid = spec->gen.autocfg.speaker_pins[0];
+ break;
+ }
+}
+
+static void ad1884_fixup_thinkpad(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct ad198x_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.keep_eapd_on = 1;
+ spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
+ spec->eapd_nid = 0x12;
+ /* Analog PC Beeper - allow firmware/ACPI beeps */
+ spec->beep_amp = HDA_COMPOSE_AMP_VAL(0x20, 3, 3, HDA_INPUT);
+ spec->gen.beep_nid = 0; /* no digital beep */
+ }
+}
+
+/* set magic COEFs for dmic */
+static const struct hda_verb ad1884_dmic_init_verbs[] = {
+ {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
+ {0x01, AC_VERB_SET_PROC_COEF, 0x08},
+ {}
+};
+
+enum {
+ AD1884_FIXUP_AMP_OVERRIDE,
+ AD1884_FIXUP_HP_EAPD,
+ AD1884_FIXUP_DMIC_COEF,
+ AD1884_FIXUP_THINKPAD,
+ AD1884_FIXUP_HP_TOUCHSMART,
+};
+
+static const struct hda_fixup ad1884_fixups[] = {
+ [AD1884_FIXUP_AMP_OVERRIDE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1884_fixup_amp_override,
+ },
+ [AD1884_FIXUP_HP_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1884_fixup_hp_eapd,
+ .chained = true,
+ .chain_id = AD1884_FIXUP_AMP_OVERRIDE,
+ },
+ [AD1884_FIXUP_DMIC_COEF] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = ad1884_dmic_init_verbs,
+ },
+ [AD1884_FIXUP_THINKPAD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = ad1884_fixup_thinkpad,
+ .chained = true,
+ .chain_id = AD1884_FIXUP_DMIC_COEF,
+ },
+ [AD1884_FIXUP_HP_TOUCHSMART] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = ad1884_dmic_init_verbs,
+ .chained = true,
+ .chain_id = AD1884_FIXUP_HP_EAPD,
+ },
+};
+
+static const struct hda_quirk ad1884_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x2a82, "HP Touchsmart", AD1884_FIXUP_HP_TOUCHSMART),
+ SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD),
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_THINKPAD),
+ {}
+};
+
+
+static int ad1884_probe(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+ int err;
+
+ spec->gen.mixer_nid = 0x20;
+ spec->gen.mixer_merge_nid = 0x21;
+ spec->gen.beep_nid = 0x10;
+ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
+ snd_hda_pick_fixup(codec, NULL, ad1884_fixup_tbl, ad1884_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = ad198x_parse_auto_config(codec, true);
+ if (err < 0)
+ return err;
+ err = ad1983_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ return err;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+}
+
+/*
+ * AD1882 / AD1882A
+ *
+ * port-A - front hp-out
+ * port-B - front mic-in
+ * port-C - rear line-in, shared surr-out (3stack)
+ * port-D - rear line-out
+ * port-E - rear mic-in, shared clfe-out (3stack)
+ * port-F - rear surr-out (6stack)
+ * port-G - rear clfe-out (6stack)
+ */
+
+static int ad1882_probe(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+ int err;
+
+ spec->gen.mixer_nid = 0x20;
+ spec->gen.mixer_merge_nid = 0x21;
+ spec->gen.beep_nid = 0x10;
+ set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+ err = ad198x_parse_auto_config(codec, true);
+ if (err < 0)
+ return err;
+ err = ad1988_add_spdif_mux_ctl(codec);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/*
+ * driver entries
+ */
+static int ad_codec_probe(struct hda_codec *codec,
+ const struct hda_device_id *id)
+{
+ struct ad198x_spec *spec;
+ int err;
+
+ err = alloc_ad_spec(codec);
+ if (err < 0)
+ return -ENOMEM;
+ spec = codec->spec;
+ spec->model = id->driver_data;
+
+ switch (spec->model) {
+ case MODEL_AD1882:
+ err = ad1882_probe(codec);
+ break;
+ case MODEL_AD1884:
+ err = ad1884_probe(codec);
+ break;
+ case MODEL_AD1981:
+ err = ad1981_probe(codec);
+ break;
+ case MODEL_AD1983:
+ err = ad1983_probe(codec);
+ break;
+ case MODEL_AD1986A:
+ err = ad1986a_probe(codec);
+ break;
+ case MODEL_AD1988:
+ err = ad1988_probe(codec);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ if (err < 0) {
+ snd_hda_gen_remove(codec);
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct hda_codec_ops ad_codec_ops = {
+ .probe = ad_codec_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = ad_codec_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = ad_codec_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = ad_codec_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+static const struct hda_device_id snd_hda_id_analog[] = {
+ HDA_CODEC_ID_MODEL(0x11d4184a, "AD1884A", MODEL_AD1884),
+ HDA_CODEC_ID_MODEL(0x11d41882, "AD1882", MODEL_AD1882),
+ HDA_CODEC_ID_MODEL(0x11d41883, "AD1883", MODEL_AD1884),
+ HDA_CODEC_ID_MODEL(0x11d41884, "AD1884", MODEL_AD1884),
+ HDA_CODEC_ID_MODEL(0x11d4194a, "AD1984A", MODEL_AD1884),
+ HDA_CODEC_ID_MODEL(0x11d4194b, "AD1984B", MODEL_AD1884),
+ HDA_CODEC_ID_MODEL(0x11d41981, "AD1981", MODEL_AD1981),
+ HDA_CODEC_ID_MODEL(0x11d41983, "AD1983", MODEL_AD1983),
+ HDA_CODEC_ID_MODEL(0x11d41984, "AD1984", MODEL_AD1884),
+ HDA_CODEC_ID_MODEL(0x11d41986, "AD1986A", MODEL_AD1986A),
+ HDA_CODEC_ID_MODEL(0x11d41988, "AD1988", MODEL_AD1988),
+ HDA_CODEC_ID_MODEL(0x11d4198b, "AD1988B", MODEL_AD1988),
+ HDA_CODEC_ID_MODEL(0x11d4882a, "AD1882A", MODEL_AD1882),
+ HDA_CODEC_ID_MODEL(0x11d4989a, "AD1989A", MODEL_AD1988),
+ HDA_CODEC_ID_MODEL(0x11d4989b, "AD1989B", MODEL_AD1988),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_analog);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Analog Devices HD-audio codec");
+
+static struct hda_codec_driver analog_driver = {
+ .id = snd_hda_id_analog,
+ .ops = &ad_codec_ops,
+};
+
+module_hda_codec_driver(analog_driver);
diff --git a/sound/hda/codecs/ca0110.c b/sound/hda/codecs/ca0110.c
new file mode 100644
index 000000000000..c75a9ff9460d
--- /dev/null
+++ b/sound/hda/codecs/ca0110.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HD audio codec driver for Creative X-Fi CA0110-IBG chip
+ *
+ * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "generic.h"
+
+static int ca0110_parse_auto_config(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0);
+ if (err < 0)
+ return err;
+ err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int ca0110_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct hda_gen_spec *spec;
+ int err;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ snd_hda_gen_spec_init(spec);
+ codec->spec = spec;
+
+ spec->multi_cap_vol = 1;
+ codec->bus->core.needs_damn_long_delay = 1;
+
+ err = ca0110_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+
+static const struct hda_codec_ops ca0110_codec_ops = {
+ .probe = ca0110_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = snd_hda_gen_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_ca0110[] = {
+ HDA_CODEC_ID(0x1102000a, "CA0110-IBG"),
+ HDA_CODEC_ID(0x1102000b, "CA0110-IBG"),
+ HDA_CODEC_ID(0x1102000d, "SB0880 X-Fi"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0110);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
+
+static struct hda_codec_driver ca0110_driver = {
+ .id = snd_hda_id_ca0110,
+ .ops = &ca0110_codec_ops,
+};
+
+module_hda_codec_driver(ca0110_driver);
diff --git a/sound/hda/codecs/ca0132.c b/sound/hda/codecs/ca0132.c
new file mode 100644
index 000000000000..dd054aedd501
--- /dev/null
+++ b/sound/hda/codecs/ca0132.c
@@ -0,0 +1,10078 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HD audio codec driver for Creative CA0132 chip
+ *
+ * Copyright (c) 2011, Creative Technology Ltd.
+ *
+ * Based on ca0110.c
+ * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+
+#include "ca0132_regs.h"
+
+/* Enable this to see controls for tuning purpose. */
+#define ENABLE_TUNING_CONTROLS
+
+#ifdef ENABLE_TUNING_CONTROLS
+#include <sound/tlv.h>
+#endif
+
+#define FLOAT_ZERO 0x00000000
+#define FLOAT_ONE 0x3f800000
+#define FLOAT_TWO 0x40000000
+#define FLOAT_THREE 0x40400000
+#define FLOAT_FIVE 0x40a00000
+#define FLOAT_SIX 0x40c00000
+#define FLOAT_EIGHT 0x41000000
+#define FLOAT_MINUS_5 0xc0a00000
+
+#define UNSOL_TAG_DSP 0x16
+
+#define DSP_DMA_WRITE_BUFLEN_INIT (1UL<<18)
+#define DSP_DMA_WRITE_BUFLEN_OVLY (1UL<<15)
+
+#define DMA_TRANSFER_FRAME_SIZE_NWORDS 8
+#define DMA_TRANSFER_MAX_FRAME_SIZE_NWORDS 32
+#define DMA_OVERLAY_FRAME_SIZE_NWORDS 2
+
+#define MASTERCONTROL 0x80
+#define MASTERCONTROL_ALLOC_DMA_CHAN 10
+#define MASTERCONTROL_QUERY_SPEAKER_EQ_ADDRESS 60
+
+#define WIDGET_CHIP_CTRL 0x15
+#define WIDGET_DSP_CTRL 0x16
+
+#define MEM_CONNID_MICIN1 3
+#define MEM_CONNID_MICIN2 5
+#define MEM_CONNID_MICOUT1 12
+#define MEM_CONNID_MICOUT2 14
+#define MEM_CONNID_WUH 10
+#define MEM_CONNID_DSP 16
+#define MEM_CONNID_DMIC 100
+
+#define SCP_SET 0
+#define SCP_GET 1
+
+#define EFX_FILE "ctefx.bin"
+#define DESKTOP_EFX_FILE "ctefx-desktop.bin"
+#define R3DI_EFX_FILE "ctefx-r3di.bin"
+
+#ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP
+MODULE_FIRMWARE(EFX_FILE);
+MODULE_FIRMWARE(DESKTOP_EFX_FILE);
+MODULE_FIRMWARE(R3DI_EFX_FILE);
+#endif
+
+static const char *const dirstr[2] = { "Playback", "Capture" };
+
+#define NUM_OF_OUTPUTS 2
+static const char *const out_type_str[2] = { "Speakers", "Headphone" };
+enum {
+ SPEAKER_OUT,
+ HEADPHONE_OUT,
+};
+
+enum {
+ DIGITAL_MIC,
+ LINE_MIC_IN
+};
+
+/* Strings for Input Source Enum Control */
+static const char *const in_src_str[3] = { "Microphone", "Line In", "Front Microphone" };
+#define IN_SRC_NUM_OF_INPUTS 3
+enum {
+ REAR_MIC,
+ REAR_LINE_IN,
+ FRONT_MIC,
+};
+
+enum {
+#define VNODE_START_NID 0x80
+ VNID_SPK = VNODE_START_NID, /* Speaker vnid */
+ VNID_MIC,
+ VNID_HP_SEL,
+ VNID_AMIC1_SEL,
+ VNID_HP_ASEL,
+ VNID_AMIC1_ASEL,
+ VNODE_END_NID,
+#define VNODES_COUNT (VNODE_END_NID - VNODE_START_NID)
+
+#define EFFECT_START_NID 0x90
+#define OUT_EFFECT_START_NID EFFECT_START_NID
+ SURROUND = OUT_EFFECT_START_NID,
+ CRYSTALIZER,
+ DIALOG_PLUS,
+ SMART_VOLUME,
+ X_BASS,
+ EQUALIZER,
+ OUT_EFFECT_END_NID,
+#define OUT_EFFECTS_COUNT (OUT_EFFECT_END_NID - OUT_EFFECT_START_NID)
+
+#define IN_EFFECT_START_NID OUT_EFFECT_END_NID
+ ECHO_CANCELLATION = IN_EFFECT_START_NID,
+ VOICE_FOCUS,
+ MIC_SVM,
+ NOISE_REDUCTION,
+ IN_EFFECT_END_NID,
+#define IN_EFFECTS_COUNT (IN_EFFECT_END_NID - IN_EFFECT_START_NID)
+
+ VOICEFX = IN_EFFECT_END_NID,
+ PLAY_ENHANCEMENT,
+ CRYSTAL_VOICE,
+ EFFECT_END_NID,
+ OUTPUT_SOURCE_ENUM,
+ INPUT_SOURCE_ENUM,
+ XBASS_XOVER,
+ EQ_PRESET_ENUM,
+ SMART_VOLUME_ENUM,
+ MIC_BOOST_ENUM,
+ AE5_HEADPHONE_GAIN_ENUM,
+ AE5_SOUND_FILTER_ENUM,
+ ZXR_HEADPHONE_GAIN,
+ SPEAKER_CHANNEL_CFG_ENUM,
+ SPEAKER_FULL_RANGE_FRONT,
+ SPEAKER_FULL_RANGE_REAR,
+ BASS_REDIRECTION,
+ BASS_REDIRECTION_XOVER,
+#define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID)
+};
+
+/* Effects values size*/
+#define EFFECT_VALS_MAX_COUNT 12
+
+/*
+ * Default values for the effect slider controls, they are in order of their
+ * effect NID's. Surround, Crystalizer, Dialog Plus, Smart Volume, and then
+ * X-bass.
+ */
+static const unsigned int effect_slider_defaults[] = {67, 65, 50, 74, 50};
+/* Amount of effect level sliders for ca0132_alt controls. */
+#define EFFECT_LEVEL_SLIDERS 5
+
+/* Latency introduced by DSP blocks in milliseconds. */
+#define DSP_CAPTURE_INIT_LATENCY 0
+#define DSP_CRYSTAL_VOICE_LATENCY 124
+#define DSP_PLAYBACK_INIT_LATENCY 13
+#define DSP_PLAY_ENHANCEMENT_LATENCY 30
+#define DSP_SPEAKER_OUT_LATENCY 7
+
+struct ct_effect {
+ const char *name;
+ hda_nid_t nid;
+ int mid; /*effect module ID*/
+ int reqs[EFFECT_VALS_MAX_COUNT]; /*effect module request*/
+ int direct; /* 0:output; 1:input*/
+ int params; /* number of default non-on/off params */
+ /*effect default values, 1st is on/off. */
+ unsigned int def_vals[EFFECT_VALS_MAX_COUNT];
+};
+
+#define EFX_DIR_OUT 0
+#define EFX_DIR_IN 1
+
+static const struct ct_effect ca0132_effects[EFFECTS_COUNT] = {
+ { .name = "Surround",
+ .nid = SURROUND,
+ .mid = 0x96,
+ .reqs = {0, 1},
+ .direct = EFX_DIR_OUT,
+ .params = 1,
+ .def_vals = {0x3F800000, 0x3F2B851F}
+ },
+ { .name = "Crystalizer",
+ .nid = CRYSTALIZER,
+ .mid = 0x96,
+ .reqs = {7, 8},
+ .direct = EFX_DIR_OUT,
+ .params = 1,
+ .def_vals = {0x3F800000, 0x3F266666}
+ },
+ { .name = "Dialog Plus",
+ .nid = DIALOG_PLUS,
+ .mid = 0x96,
+ .reqs = {2, 3},
+ .direct = EFX_DIR_OUT,
+ .params = 1,
+ .def_vals = {0x00000000, 0x3F000000}
+ },
+ { .name = "Smart Volume",
+ .nid = SMART_VOLUME,
+ .mid = 0x96,
+ .reqs = {4, 5, 6},
+ .direct = EFX_DIR_OUT,
+ .params = 2,
+ .def_vals = {0x3F800000, 0x3F3D70A4, 0x00000000}
+ },
+ { .name = "X-Bass",
+ .nid = X_BASS,
+ .mid = 0x96,
+ .reqs = {24, 23, 25},
+ .direct = EFX_DIR_OUT,
+ .params = 2,
+ .def_vals = {0x3F800000, 0x42A00000, 0x3F000000}
+ },
+ { .name = "Equalizer",
+ .nid = EQUALIZER,
+ .mid = 0x96,
+ .reqs = {9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20},
+ .direct = EFX_DIR_OUT,
+ .params = 11,
+ .def_vals = {0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000}
+ },
+ { .name = "Echo Cancellation",
+ .nid = ECHO_CANCELLATION,
+ .mid = 0x95,
+ .reqs = {0, 1, 2, 3},
+ .direct = EFX_DIR_IN,
+ .params = 3,
+ .def_vals = {0x00000000, 0x3F3A9692, 0x00000000, 0x00000000}
+ },
+ { .name = "Voice Focus",
+ .nid = VOICE_FOCUS,
+ .mid = 0x95,
+ .reqs = {6, 7, 8, 9},
+ .direct = EFX_DIR_IN,
+ .params = 3,
+ .def_vals = {0x3F800000, 0x3D7DF3B6, 0x41F00000, 0x41F00000}
+ },
+ { .name = "Mic SVM",
+ .nid = MIC_SVM,
+ .mid = 0x95,
+ .reqs = {44, 45},
+ .direct = EFX_DIR_IN,
+ .params = 1,
+ .def_vals = {0x00000000, 0x3F3D70A4}
+ },
+ { .name = "Noise Reduction",
+ .nid = NOISE_REDUCTION,
+ .mid = 0x95,
+ .reqs = {4, 5},
+ .direct = EFX_DIR_IN,
+ .params = 1,
+ .def_vals = {0x3F800000, 0x3F000000}
+ },
+ { .name = "VoiceFX",
+ .nid = VOICEFX,
+ .mid = 0x95,
+ .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18},
+ .direct = EFX_DIR_IN,
+ .params = 8,
+ .def_vals = {0x00000000, 0x43C80000, 0x44AF0000, 0x44FA0000,
+ 0x3F800000, 0x3F800000, 0x3F800000, 0x00000000,
+ 0x00000000}
+ }
+};
+
+/* Tuning controls */
+#ifdef ENABLE_TUNING_CONTROLS
+
+enum {
+#define TUNING_CTL_START_NID 0xC0
+ WEDGE_ANGLE = TUNING_CTL_START_NID,
+ SVM_LEVEL,
+ EQUALIZER_BAND_0,
+ EQUALIZER_BAND_1,
+ EQUALIZER_BAND_2,
+ EQUALIZER_BAND_3,
+ EQUALIZER_BAND_4,
+ EQUALIZER_BAND_5,
+ EQUALIZER_BAND_6,
+ EQUALIZER_BAND_7,
+ EQUALIZER_BAND_8,
+ EQUALIZER_BAND_9,
+ TUNING_CTL_END_NID
+#define TUNING_CTLS_COUNT (TUNING_CTL_END_NID - TUNING_CTL_START_NID)
+};
+
+struct ct_tuning_ctl {
+ const char *name;
+ hda_nid_t parent_nid;
+ hda_nid_t nid;
+ int mid; /*effect module ID*/
+ int req; /*effect module request*/
+ int direct; /* 0:output; 1:input*/
+ unsigned int def_val;/*effect default values*/
+};
+
+static const struct ct_tuning_ctl ca0132_tuning_ctls[] = {
+ { .name = "Wedge Angle",
+ .parent_nid = VOICE_FOCUS,
+ .nid = WEDGE_ANGLE,
+ .mid = 0x95,
+ .req = 8,
+ .direct = EFX_DIR_IN,
+ .def_val = 0x41F00000
+ },
+ { .name = "SVM Level",
+ .parent_nid = MIC_SVM,
+ .nid = SVM_LEVEL,
+ .mid = 0x95,
+ .req = 45,
+ .direct = EFX_DIR_IN,
+ .def_val = 0x3F3D70A4
+ },
+ { .name = "EQ Band0",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_0,
+ .mid = 0x96,
+ .req = 11,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band1",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_1,
+ .mid = 0x96,
+ .req = 12,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band2",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_2,
+ .mid = 0x96,
+ .req = 13,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band3",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_3,
+ .mid = 0x96,
+ .req = 14,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band4",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_4,
+ .mid = 0x96,
+ .req = 15,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band5",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_5,
+ .mid = 0x96,
+ .req = 16,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band6",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_6,
+ .mid = 0x96,
+ .req = 17,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band7",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_7,
+ .mid = 0x96,
+ .req = 18,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band8",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_8,
+ .mid = 0x96,
+ .req = 19,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ },
+ { .name = "EQ Band9",
+ .parent_nid = EQUALIZER,
+ .nid = EQUALIZER_BAND_9,
+ .mid = 0x96,
+ .req = 20,
+ .direct = EFX_DIR_OUT,
+ .def_val = 0x00000000
+ }
+};
+#endif
+
+/* Voice FX Presets */
+#define VOICEFX_MAX_PARAM_COUNT 9
+
+struct ct_voicefx {
+ const char *name;
+ hda_nid_t nid;
+ int mid;
+ int reqs[VOICEFX_MAX_PARAM_COUNT]; /*effect module request*/
+};
+
+struct ct_voicefx_preset {
+ const char *name; /*preset name*/
+ unsigned int vals[VOICEFX_MAX_PARAM_COUNT];
+};
+
+static const struct ct_voicefx ca0132_voicefx = {
+ .name = "VoiceFX Capture Switch",
+ .nid = VOICEFX,
+ .mid = 0x95,
+ .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18}
+};
+
+static const struct ct_voicefx_preset ca0132_voicefx_presets[] = {
+ { .name = "Neutral",
+ .vals = { 0x00000000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3F800000, 0x3F800000,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "Female2Male",
+ .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3F19999A, 0x3F866666,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "Male2Female",
+ .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x450AC000, 0x4017AE14, 0x3F6B851F,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "ScrappyKid",
+ .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x40400000, 0x3F28F5C3,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "Elderly",
+ .vals = { 0x3F800000, 0x44324000, 0x44BB8000,
+ 0x44E10000, 0x3FB33333, 0x3FB9999A,
+ 0x3F800000, 0x3E3A2E43, 0x00000000 }
+ },
+ { .name = "Orc",
+ .vals = { 0x3F800000, 0x43EA0000, 0x44A52000,
+ 0x45098000, 0x3F266666, 0x3FC00000,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "Elf",
+ .vals = { 0x3F800000, 0x43C70000, 0x44AE6000,
+ 0x45193000, 0x3F8E147B, 0x3F75C28F,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "Dwarf",
+ .vals = { 0x3F800000, 0x43930000, 0x44BEE000,
+ 0x45007000, 0x3F451EB8, 0x3F7851EC,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "AlienBrute",
+ .vals = { 0x3F800000, 0x43BFC5AC, 0x44B28FDF,
+ 0x451F6000, 0x3F266666, 0x3FA7D945,
+ 0x3F800000, 0x3CF5C28F, 0x00000000 }
+ },
+ { .name = "Robot",
+ .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3FB2718B, 0x3F800000,
+ 0xBC07010E, 0x00000000, 0x00000000 }
+ },
+ { .name = "Marine",
+ .vals = { 0x3F800000, 0x43C20000, 0x44906000,
+ 0x44E70000, 0x3F4CCCCD, 0x3F8A3D71,
+ 0x3F0A3D71, 0x00000000, 0x00000000 }
+ },
+ { .name = "Emo",
+ .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3F800000, 0x3F800000,
+ 0x3E4CCCCD, 0x00000000, 0x00000000 }
+ },
+ { .name = "DeepVoice",
+ .vals = { 0x3F800000, 0x43A9C5AC, 0x44AA4FDF,
+ 0x44FFC000, 0x3EDBB56F, 0x3F99C4CA,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ },
+ { .name = "Munchkin",
+ .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
+ 0x44FA0000, 0x3F800000, 0x3F1A043C,
+ 0x3F800000, 0x00000000, 0x00000000 }
+ }
+};
+
+/* ca0132 EQ presets, taken from Windows Sound Blaster Z Driver */
+
+#define EQ_PRESET_MAX_PARAM_COUNT 11
+
+struct ct_eq {
+ const char *name;
+ hda_nid_t nid;
+ int mid;
+ int reqs[EQ_PRESET_MAX_PARAM_COUNT]; /*effect module request*/
+};
+
+struct ct_eq_preset {
+ const char *name; /*preset name*/
+ unsigned int vals[EQ_PRESET_MAX_PARAM_COUNT];
+};
+
+static const struct ct_eq ca0132_alt_eq_enum = {
+ .name = "FX: Equalizer Preset Switch",
+ .nid = EQ_PRESET_ENUM,
+ .mid = 0x96,
+ .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
+};
+
+
+static const struct ct_eq_preset ca0132_alt_eq_presets[] = {
+ { .name = "Flat",
+ .vals = { 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000 }
+ },
+ { .name = "Acoustic",
+ .vals = { 0x00000000, 0x00000000, 0x3F8CCCCD,
+ 0x40000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x40000000,
+ 0x40000000, 0x40000000 }
+ },
+ { .name = "Classical",
+ .vals = { 0x00000000, 0x00000000, 0x40C00000,
+ 0x40C00000, 0x40466666, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x40466666, 0x40466666 }
+ },
+ { .name = "Country",
+ .vals = { 0x00000000, 0xBF99999A, 0x00000000,
+ 0x3FA66666, 0x3FA66666, 0x3F8CCCCD,
+ 0x00000000, 0x00000000, 0x40000000,
+ 0x40466666, 0x40800000 }
+ },
+ { .name = "Dance",
+ .vals = { 0x00000000, 0xBF99999A, 0x40000000,
+ 0x40466666, 0x40866666, 0xBF99999A,
+ 0xBF99999A, 0x00000000, 0x00000000,
+ 0x40800000, 0x40800000 }
+ },
+ { .name = "Jazz",
+ .vals = { 0x00000000, 0x00000000, 0x00000000,
+ 0x3F8CCCCD, 0x40800000, 0x40800000,
+ 0x40800000, 0x00000000, 0x3F8CCCCD,
+ 0x40466666, 0x40466666 }
+ },
+ { .name = "New Age",
+ .vals = { 0x00000000, 0x00000000, 0x40000000,
+ 0x40000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x3F8CCCCD, 0x40000000,
+ 0x40000000, 0x40000000 }
+ },
+ { .name = "Pop",
+ .vals = { 0x00000000, 0xBFCCCCCD, 0x00000000,
+ 0x40000000, 0x40000000, 0x00000000,
+ 0xBF99999A, 0xBF99999A, 0x00000000,
+ 0x40466666, 0x40C00000 }
+ },
+ { .name = "Rock",
+ .vals = { 0x00000000, 0xBF99999A, 0xBF99999A,
+ 0x3F8CCCCD, 0x40000000, 0xBF99999A,
+ 0xBF99999A, 0x00000000, 0x00000000,
+ 0x40800000, 0x40800000 }
+ },
+ { .name = "Vocal",
+ .vals = { 0x00000000, 0xC0000000, 0xBF99999A,
+ 0xBF99999A, 0x00000000, 0x40466666,
+ 0x40800000, 0x40466666, 0x00000000,
+ 0x00000000, 0x3F8CCCCD }
+ }
+};
+
+/*
+ * DSP reqs for handling full-range speakers/bass redirection. If a speaker is
+ * set as not being full range, and bass redirection is enabled, all
+ * frequencies below the crossover frequency are redirected to the LFE
+ * channel. If the surround configuration has no LFE channel, this can't be
+ * enabled. X-Bass must be disabled when using these.
+ */
+enum speaker_range_reqs {
+ SPEAKER_BASS_REDIRECT = 0x15,
+ SPEAKER_BASS_REDIRECT_XOVER_FREQ = 0x16,
+ /* Between 0x16-0x1a are the X-Bass reqs. */
+ SPEAKER_FULL_RANGE_FRONT_L_R = 0x1a,
+ SPEAKER_FULL_RANGE_CENTER_LFE = 0x1b,
+ SPEAKER_FULL_RANGE_REAR_L_R = 0x1c,
+ SPEAKER_FULL_RANGE_SURROUND_L_R = 0x1d,
+ SPEAKER_BASS_REDIRECT_SUB_GAIN = 0x1e,
+};
+
+/*
+ * Definitions for the DSP req's to handle speaker tuning. These all belong to
+ * module ID 0x96, the output effects module.
+ */
+enum speaker_tuning_reqs {
+ /*
+ * Currently, this value is always set to 0.0f. However, on Windows,
+ * when selecting certain headphone profiles on the new Sound Blaster
+ * connect software, the QUERY_SPEAKER_EQ_ADDRESS req on mid 0x80 is
+ * sent. This gets the speaker EQ address area, which is then used to
+ * send over (presumably) an equalizer profile for the specific
+ * headphone setup. It is sent using the same method the DSP
+ * firmware is uploaded with, which I believe is why the 'ctspeq.bin'
+ * file exists in linux firmware tree but goes unused. It would also
+ * explain why the QUERY_SPEAKER_EQ_ADDRESS req is defined but unused.
+ * Once this profile is sent over, SPEAKER_TUNING_USE_SPEAKER_EQ is
+ * set to 1.0f.
+ */
+ SPEAKER_TUNING_USE_SPEAKER_EQ = 0x1f,
+ SPEAKER_TUNING_ENABLE_CENTER_EQ = 0x20,
+ SPEAKER_TUNING_FRONT_LEFT_VOL_LEVEL = 0x21,
+ SPEAKER_TUNING_FRONT_RIGHT_VOL_LEVEL = 0x22,
+ SPEAKER_TUNING_CENTER_VOL_LEVEL = 0x23,
+ SPEAKER_TUNING_LFE_VOL_LEVEL = 0x24,
+ SPEAKER_TUNING_REAR_LEFT_VOL_LEVEL = 0x25,
+ SPEAKER_TUNING_REAR_RIGHT_VOL_LEVEL = 0x26,
+ SPEAKER_TUNING_SURROUND_LEFT_VOL_LEVEL = 0x27,
+ SPEAKER_TUNING_SURROUND_RIGHT_VOL_LEVEL = 0x28,
+ /*
+ * Inversion is used when setting headphone virtualization to line
+ * out. Not sure why this is, but it's the only place it's ever used.
+ */
+ SPEAKER_TUNING_FRONT_LEFT_INVERT = 0x29,
+ SPEAKER_TUNING_FRONT_RIGHT_INVERT = 0x2a,
+ SPEAKER_TUNING_CENTER_INVERT = 0x2b,
+ SPEAKER_TUNING_LFE_INVERT = 0x2c,
+ SPEAKER_TUNING_REAR_LEFT_INVERT = 0x2d,
+ SPEAKER_TUNING_REAR_RIGHT_INVERT = 0x2e,
+ SPEAKER_TUNING_SURROUND_LEFT_INVERT = 0x2f,
+ SPEAKER_TUNING_SURROUND_RIGHT_INVERT = 0x30,
+ /* Delay is used when setting surround speaker distance in Windows. */
+ SPEAKER_TUNING_FRONT_LEFT_DELAY = 0x31,
+ SPEAKER_TUNING_FRONT_RIGHT_DELAY = 0x32,
+ SPEAKER_TUNING_CENTER_DELAY = 0x33,
+ SPEAKER_TUNING_LFE_DELAY = 0x34,
+ SPEAKER_TUNING_REAR_LEFT_DELAY = 0x35,
+ SPEAKER_TUNING_REAR_RIGHT_DELAY = 0x36,
+ SPEAKER_TUNING_SURROUND_LEFT_DELAY = 0x37,
+ SPEAKER_TUNING_SURROUND_RIGHT_DELAY = 0x38,
+ /* Of these two, only mute seems to ever be used. */
+ SPEAKER_TUNING_MAIN_VOLUME = 0x39,
+ SPEAKER_TUNING_MUTE = 0x3a,
+};
+
+/* Surround output channel count configuration structures. */
+#define SPEAKER_CHANNEL_CFG_COUNT 5
+enum {
+ SPEAKER_CHANNELS_2_0,
+ SPEAKER_CHANNELS_2_1,
+ SPEAKER_CHANNELS_4_0,
+ SPEAKER_CHANNELS_4_1,
+ SPEAKER_CHANNELS_5_1,
+};
+
+struct ca0132_alt_speaker_channel_cfg {
+ const char *name;
+ unsigned int val;
+};
+
+static const struct ca0132_alt_speaker_channel_cfg speaker_channel_cfgs[] = {
+ { .name = "2.0",
+ .val = FLOAT_ONE
+ },
+ { .name = "2.1",
+ .val = FLOAT_TWO
+ },
+ { .name = "4.0",
+ .val = FLOAT_FIVE
+ },
+ { .name = "4.1",
+ .val = FLOAT_SIX
+ },
+ { .name = "5.1",
+ .val = FLOAT_EIGHT
+ }
+};
+
+/*
+ * DSP volume setting structs. Req 1 is left volume, req 2 is right volume,
+ * and I don't know what the third req is, but it's always zero. I assume it's
+ * some sort of update or set command to tell the DSP there's new volume info.
+ */
+#define DSP_VOL_OUT 0
+#define DSP_VOL_IN 1
+
+struct ct_dsp_volume_ctl {
+ hda_nid_t vnid;
+ int mid; /* module ID*/
+ unsigned int reqs[3]; /* scp req ID */
+};
+
+static const struct ct_dsp_volume_ctl ca0132_alt_vol_ctls[] = {
+ { .vnid = VNID_SPK,
+ .mid = 0x32,
+ .reqs = {3, 4, 2}
+ },
+ { .vnid = VNID_MIC,
+ .mid = 0x37,
+ .reqs = {2, 3, 1}
+ }
+};
+
+/* Values for ca0113_mmio_command_set for selecting output. */
+#define AE_CA0113_OUT_SET_COMMANDS 6
+struct ae_ca0113_output_set {
+ unsigned int group[AE_CA0113_OUT_SET_COMMANDS];
+ unsigned int target[AE_CA0113_OUT_SET_COMMANDS];
+ unsigned int vals[NUM_OF_OUTPUTS][AE_CA0113_OUT_SET_COMMANDS];
+};
+
+static const struct ae_ca0113_output_set ae5_ca0113_output_presets = {
+ .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 },
+ .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 },
+ /* Speakers. */
+ .vals = { { 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f },
+ /* Headphones. */
+ { 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00 } },
+};
+
+static const struct ae_ca0113_output_set ae7_ca0113_output_presets = {
+ .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 },
+ .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 },
+ /* Speakers. */
+ .vals = { { 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f },
+ /* Headphones. */
+ { 0x3f, 0x3f, 0x00, 0x00, 0x02, 0x00 } },
+};
+
+/* ae5 ca0113 command sequences to set headphone gain levels. */
+#define AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS 4
+struct ae5_headphone_gain_set {
+ const char *name;
+ unsigned int vals[AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS];
+};
+
+static const struct ae5_headphone_gain_set ae5_headphone_gain_presets[] = {
+ { .name = "Low (16-31",
+ .vals = { 0xff, 0x2c, 0xf5, 0x32 }
+ },
+ { .name = "Medium (32-149",
+ .vals = { 0x38, 0xa8, 0x3e, 0x4c }
+ },
+ { .name = "High (150-600",
+ .vals = { 0xff, 0xff, 0xff, 0x7f }
+ }
+};
+
+struct ae5_filter_set {
+ const char *name;
+ unsigned int val;
+};
+
+static const struct ae5_filter_set ae5_filter_presets[] = {
+ { .name = "Slow Roll Off",
+ .val = 0xa0
+ },
+ { .name = "Minimum Phase",
+ .val = 0xc0
+ },
+ { .name = "Fast Roll Off",
+ .val = 0x80
+ }
+};
+
+/*
+ * Data structures for storing audio router remapping data. These are used to
+ * remap a currently active streams ports.
+ */
+struct chipio_stream_remap_data {
+ unsigned int stream_id;
+ unsigned int count;
+
+ unsigned int offset[16];
+ unsigned int value[16];
+};
+
+static const struct chipio_stream_remap_data stream_remap_data[] = {
+ { .stream_id = 0x14,
+ .count = 0x04,
+ .offset = { 0x00, 0x04, 0x08, 0x0c },
+ .value = { 0x0001f8c0, 0x0001f9c1, 0x0001fac6, 0x0001fbc7 },
+ },
+ { .stream_id = 0x0c,
+ .count = 0x0c,
+ .offset = { 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
+ 0x20, 0x24, 0x28, 0x2c },
+ .value = { 0x0001e0c0, 0x0001e1c1, 0x0001e4c2, 0x0001e5c3,
+ 0x0001e2c4, 0x0001e3c5, 0x0001e8c6, 0x0001e9c7,
+ 0x0001ecc8, 0x0001edc9, 0x0001eaca, 0x0001ebcb },
+ },
+ { .stream_id = 0x0c,
+ .count = 0x08,
+ .offset = { 0x08, 0x0c, 0x10, 0x14, 0x20, 0x24, 0x28, 0x2c },
+ .value = { 0x000140c2, 0x000141c3, 0x000150c4, 0x000151c5,
+ 0x000142c8, 0x000143c9, 0x000152ca, 0x000153cb },
+ }
+};
+
+enum hda_cmd_vendor_io {
+ /* for DspIO node */
+ VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000,
+ VENDOR_DSPIO_SCP_WRITE_DATA_HIGH = 0x100,
+
+ VENDOR_DSPIO_STATUS = 0xF01,
+ VENDOR_DSPIO_SCP_POST_READ_DATA = 0x702,
+ VENDOR_DSPIO_SCP_READ_DATA = 0xF02,
+ VENDOR_DSPIO_DSP_INIT = 0x703,
+ VENDOR_DSPIO_SCP_POST_COUNT_QUERY = 0x704,
+ VENDOR_DSPIO_SCP_READ_COUNT = 0xF04,
+
+ /* for ChipIO node */
+ VENDOR_CHIPIO_ADDRESS_LOW = 0x000,
+ VENDOR_CHIPIO_ADDRESS_HIGH = 0x100,
+ VENDOR_CHIPIO_STREAM_FORMAT = 0x200,
+ VENDOR_CHIPIO_DATA_LOW = 0x300,
+ VENDOR_CHIPIO_DATA_HIGH = 0x400,
+
+ VENDOR_CHIPIO_8051_WRITE_DIRECT = 0x500,
+ VENDOR_CHIPIO_8051_READ_DIRECT = 0xD00,
+
+ VENDOR_CHIPIO_GET_PARAMETER = 0xF00,
+ VENDOR_CHIPIO_STATUS = 0xF01,
+ VENDOR_CHIPIO_HIC_POST_READ = 0x702,
+ VENDOR_CHIPIO_HIC_READ_DATA = 0xF03,
+
+ VENDOR_CHIPIO_8051_DATA_WRITE = 0x707,
+ VENDOR_CHIPIO_8051_DATA_READ = 0xF07,
+ VENDOR_CHIPIO_8051_PMEM_READ = 0xF08,
+ VENDOR_CHIPIO_8051_IRAM_WRITE = 0x709,
+ VENDOR_CHIPIO_8051_IRAM_READ = 0xF09,
+
+ VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE = 0x70A,
+ VENDOR_CHIPIO_CT_EXTENSIONS_GET = 0xF0A,
+
+ VENDOR_CHIPIO_PLL_PMU_WRITE = 0x70C,
+ VENDOR_CHIPIO_PLL_PMU_READ = 0xF0C,
+ VENDOR_CHIPIO_8051_ADDRESS_LOW = 0x70D,
+ VENDOR_CHIPIO_8051_ADDRESS_HIGH = 0x70E,
+ VENDOR_CHIPIO_FLAG_SET = 0x70F,
+ VENDOR_CHIPIO_FLAGS_GET = 0xF0F,
+ VENDOR_CHIPIO_PARAM_SET = 0x710,
+ VENDOR_CHIPIO_PARAM_GET = 0xF10,
+
+ VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET = 0x711,
+ VENDOR_CHIPIO_PORT_ALLOC_SET = 0x712,
+ VENDOR_CHIPIO_PORT_ALLOC_GET = 0xF12,
+ VENDOR_CHIPIO_PORT_FREE_SET = 0x713,
+
+ VENDOR_CHIPIO_PARAM_EX_ID_GET = 0xF17,
+ VENDOR_CHIPIO_PARAM_EX_ID_SET = 0x717,
+ VENDOR_CHIPIO_PARAM_EX_VALUE_GET = 0xF18,
+ VENDOR_CHIPIO_PARAM_EX_VALUE_SET = 0x718,
+
+ VENDOR_CHIPIO_DMIC_CTL_SET = 0x788,
+ VENDOR_CHIPIO_DMIC_CTL_GET = 0xF88,
+ VENDOR_CHIPIO_DMIC_PIN_SET = 0x789,
+ VENDOR_CHIPIO_DMIC_PIN_GET = 0xF89,
+ VENDOR_CHIPIO_DMIC_MCLK_SET = 0x78A,
+ VENDOR_CHIPIO_DMIC_MCLK_GET = 0xF8A,
+
+ VENDOR_CHIPIO_EAPD_SEL_SET = 0x78D
+};
+
+/*
+ * Control flag IDs
+ */
+enum control_flag_id {
+ /* Connection manager stream setup is bypassed/enabled */
+ CONTROL_FLAG_C_MGR = 0,
+ /* DSP DMA is bypassed/enabled */
+ CONTROL_FLAG_DMA = 1,
+ /* 8051 'idle' mode is disabled/enabled */
+ CONTROL_FLAG_IDLE_ENABLE = 2,
+ /* Tracker for the SPDIF-in path is bypassed/enabled */
+ CONTROL_FLAG_TRACKER = 3,
+ /* DigitalOut to Spdif2Out connection is disabled/enabled */
+ CONTROL_FLAG_SPDIF2OUT = 4,
+ /* Digital Microphone is disabled/enabled */
+ CONTROL_FLAG_DMIC = 5,
+ /* ADC_B rate is 48 kHz/96 kHz */
+ CONTROL_FLAG_ADC_B_96KHZ = 6,
+ /* ADC_C rate is 48 kHz/96 kHz */
+ CONTROL_FLAG_ADC_C_96KHZ = 7,
+ /* DAC rate is 48 kHz/96 kHz (affects all DACs) */
+ CONTROL_FLAG_DAC_96KHZ = 8,
+ /* DSP rate is 48 kHz/96 kHz */
+ CONTROL_FLAG_DSP_96KHZ = 9,
+ /* SRC clock is 98 MHz/196 MHz (196 MHz forces rate to 96 KHz) */
+ CONTROL_FLAG_SRC_CLOCK_196MHZ = 10,
+ /* SRC rate is 48 kHz/96 kHz (48 kHz disabled when clock is 196 MHz) */
+ CONTROL_FLAG_SRC_RATE_96KHZ = 11,
+ /* Decode Loop (DSP->SRC->DSP) is disabled/enabled */
+ CONTROL_FLAG_DECODE_LOOP = 12,
+ /* De-emphasis filter on DAC-1 disabled/enabled */
+ CONTROL_FLAG_DAC1_DEEMPHASIS = 13,
+ /* De-emphasis filter on DAC-2 disabled/enabled */
+ CONTROL_FLAG_DAC2_DEEMPHASIS = 14,
+ /* De-emphasis filter on DAC-3 disabled/enabled */
+ CONTROL_FLAG_DAC3_DEEMPHASIS = 15,
+ /* High-pass filter on ADC_B disabled/enabled */
+ CONTROL_FLAG_ADC_B_HIGH_PASS = 16,
+ /* High-pass filter on ADC_C disabled/enabled */
+ CONTROL_FLAG_ADC_C_HIGH_PASS = 17,
+ /* Common mode on Port_A disabled/enabled */
+ CONTROL_FLAG_PORT_A_COMMON_MODE = 18,
+ /* Common mode on Port_D disabled/enabled */
+ CONTROL_FLAG_PORT_D_COMMON_MODE = 19,
+ /* Impedance for ramp generator on Port_A 16 Ohm/10K Ohm */
+ CONTROL_FLAG_PORT_A_10KOHM_LOAD = 20,
+ /* Impedance for ramp generator on Port_D, 16 Ohm/10K Ohm */
+ CONTROL_FLAG_PORT_D_10KOHM_LOAD = 21,
+ /* ASI rate is 48kHz/96kHz */
+ CONTROL_FLAG_ASI_96KHZ = 22,
+ /* DAC power settings able to control attached ports no/yes */
+ CONTROL_FLAG_DACS_CONTROL_PORTS = 23,
+ /* Clock Stop OK reporting is disabled/enabled */
+ CONTROL_FLAG_CONTROL_STOP_OK_ENABLE = 24,
+ /* Number of control flags */
+ CONTROL_FLAGS_MAX = (CONTROL_FLAG_CONTROL_STOP_OK_ENABLE+1)
+};
+
+/*
+ * Control parameter IDs
+ */
+enum control_param_id {
+ /* 0: None, 1: Mic1In*/
+ CONTROL_PARAM_VIP_SOURCE = 1,
+ /* 0: force HDA, 1: allow DSP if HDA Spdif1Out stream is idle */
+ CONTROL_PARAM_SPDIF1_SOURCE = 2,
+ /* Port A output stage gain setting to use when 16 Ohm output
+ * impedance is selected*/
+ CONTROL_PARAM_PORTA_160OHM_GAIN = 8,
+ /* Port D output stage gain setting to use when 16 Ohm output
+ * impedance is selected*/
+ CONTROL_PARAM_PORTD_160OHM_GAIN = 10,
+
+ /*
+ * This control param name was found in the 8051 memory, and makes
+ * sense given the fact the AE-5 uses it and has the ASI flag set.
+ */
+ CONTROL_PARAM_ASI = 23,
+
+ /* Stream Control */
+
+ /* Select stream with the given ID */
+ CONTROL_PARAM_STREAM_ID = 24,
+ /* Source connection point for the selected stream */
+ CONTROL_PARAM_STREAM_SOURCE_CONN_POINT = 25,
+ /* Destination connection point for the selected stream */
+ CONTROL_PARAM_STREAM_DEST_CONN_POINT = 26,
+ /* Number of audio channels in the selected stream */
+ CONTROL_PARAM_STREAMS_CHANNELS = 27,
+ /*Enable control for the selected stream */
+ CONTROL_PARAM_STREAM_CONTROL = 28,
+
+ /* Connection Point Control */
+
+ /* Select connection point with the given ID */
+ CONTROL_PARAM_CONN_POINT_ID = 29,
+ /* Connection point sample rate */
+ CONTROL_PARAM_CONN_POINT_SAMPLE_RATE = 30,
+
+ /* Node Control */
+
+ /* Select HDA node with the given ID */
+ CONTROL_PARAM_NODE_ID = 31
+};
+
+/*
+ * Dsp Io Status codes
+ */
+enum hda_vendor_status_dspio {
+ /* Success */
+ VENDOR_STATUS_DSPIO_OK = 0x00,
+ /* Busy, unable to accept new command, the host must retry */
+ VENDOR_STATUS_DSPIO_BUSY = 0x01,
+ /* SCP command queue is full */
+ VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL = 0x02,
+ /* SCP response queue is empty */
+ VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY = 0x03
+};
+
+/*
+ * Chip Io Status codes
+ */
+enum hda_vendor_status_chipio {
+ /* Success */
+ VENDOR_STATUS_CHIPIO_OK = 0x00,
+ /* Busy, unable to accept new command, the host must retry */
+ VENDOR_STATUS_CHIPIO_BUSY = 0x01
+};
+
+/*
+ * CA0132 sample rate
+ */
+enum ca0132_sample_rate {
+ SR_6_000 = 0x00,
+ SR_8_000 = 0x01,
+ SR_9_600 = 0x02,
+ SR_11_025 = 0x03,
+ SR_16_000 = 0x04,
+ SR_22_050 = 0x05,
+ SR_24_000 = 0x06,
+ SR_32_000 = 0x07,
+ SR_44_100 = 0x08,
+ SR_48_000 = 0x09,
+ SR_88_200 = 0x0A,
+ SR_96_000 = 0x0B,
+ SR_144_000 = 0x0C,
+ SR_176_400 = 0x0D,
+ SR_192_000 = 0x0E,
+ SR_384_000 = 0x0F,
+
+ SR_COUNT = 0x10,
+
+ SR_RATE_UNKNOWN = 0x1F
+};
+
+enum dsp_download_state {
+ DSP_DOWNLOAD_FAILED = -1,
+ DSP_DOWNLOAD_INIT = 0,
+ DSP_DOWNLOADING = 1,
+ DSP_DOWNLOADED = 2
+};
+
+/* retrieve parameters from hda format */
+#define get_hdafmt_chs(fmt) (fmt & 0xf)
+#define get_hdafmt_bits(fmt) ((fmt >> 4) & 0x7)
+#define get_hdafmt_rate(fmt) ((fmt >> 8) & 0x7f)
+#define get_hdafmt_type(fmt) ((fmt >> 15) & 0x1)
+
+/*
+ * CA0132 specific
+ */
+
+struct ca0132_spec {
+ const struct snd_kcontrol_new *mixers[5];
+ unsigned int num_mixers;
+ const struct hda_verb *base_init_verbs;
+ const struct hda_verb *base_exit_verbs;
+ const struct hda_verb *chip_init_verbs;
+ const struct hda_verb *desktop_init_verbs;
+ struct hda_verb *spec_init_verbs;
+ struct auto_pin_cfg autocfg;
+
+ /* Nodes configurations */
+ struct hda_multi_out multiout;
+ hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
+ hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
+ unsigned int num_outputs;
+ hda_nid_t input_pins[AUTO_PIN_LAST];
+ hda_nid_t adcs[AUTO_PIN_LAST];
+ hda_nid_t dig_out;
+ hda_nid_t dig_in;
+ unsigned int num_inputs;
+ hda_nid_t shared_mic_nid;
+ hda_nid_t shared_out_nid;
+ hda_nid_t unsol_tag_hp;
+ hda_nid_t unsol_tag_front_hp; /* for desktop ca0132 codecs */
+ hda_nid_t unsol_tag_amic1;
+
+ /* chip access */
+ struct mutex chipio_mutex; /* chip access mutex */
+ u32 curr_chip_addx;
+
+ /* DSP download related */
+ enum dsp_download_state dsp_state;
+ unsigned int dsp_stream_id;
+ unsigned int wait_scp;
+ unsigned int wait_scp_header;
+ unsigned int wait_num_data;
+ unsigned int scp_resp_header;
+ unsigned int scp_resp_data[4];
+ unsigned int scp_resp_count;
+ bool startup_check_entered;
+ bool dsp_reload;
+
+ /* mixer and effects related */
+ unsigned char dmic_ctl;
+ int cur_out_type;
+ int cur_mic_type;
+ long vnode_lvol[VNODES_COUNT];
+ long vnode_rvol[VNODES_COUNT];
+ long vnode_lswitch[VNODES_COUNT];
+ long vnode_rswitch[VNODES_COUNT];
+ long effects_switch[EFFECTS_COUNT];
+ long voicefx_val;
+ long cur_mic_boost;
+ /* ca0132_alt control related values */
+ unsigned char in_enum_val;
+ unsigned char out_enum_val;
+ unsigned char channel_cfg_val;
+ unsigned char speaker_range_val[2];
+ unsigned char mic_boost_enum_val;
+ unsigned char smart_volume_setting;
+ unsigned char bass_redirection_val;
+ long bass_redirect_xover_freq;
+ long fx_ctl_val[EFFECT_LEVEL_SLIDERS];
+ long xbass_xover_freq;
+ long eq_preset_val;
+ unsigned int tlv[4];
+ struct hda_vmaster_mute_hook vmaster_mute;
+ /* AE-5 Control values */
+ unsigned char ae5_headphone_gain_val;
+ unsigned char ae5_filter_val;
+ /* ZxR Control Values */
+ unsigned char zxr_gain_set;
+
+ struct hda_codec *codec;
+ struct delayed_work unsol_hp_work;
+
+#ifdef ENABLE_TUNING_CONTROLS
+ long cur_ctl_vals[TUNING_CTLS_COUNT];
+#endif
+ /*
+ * The Recon3D, Sound Blaster Z, Sound Blaster ZxR, and Sound Blaster
+ * AE-5 all use PCI region 2 to toggle GPIO and other currently unknown
+ * things.
+ */
+ bool use_pci_mmio;
+ void __iomem *mem_base;
+
+ /*
+ * Whether or not to use the alt functions like alt_select_out,
+ * alt_select_in, etc. Only used on desktop codecs for now, because of
+ * surround sound support.
+ */
+ bool use_alt_functions;
+
+ /*
+ * Whether or not to use alt controls: volume effect sliders, EQ
+ * presets, smart volume presets, and new control names with FX prefix.
+ * Renames PlayEnhancement and CrystalVoice too.
+ */
+ bool use_alt_controls;
+};
+
+/*
+ * CA0132 quirks table
+ */
+enum {
+ QUIRK_ALIENWARE,
+ QUIRK_ALIENWARE_M17XR4,
+ QUIRK_SBZ,
+ QUIRK_ZXR,
+ QUIRK_ZXR_DBPRO,
+ QUIRK_R3DI,
+ QUIRK_R3D,
+ QUIRK_AE5,
+ QUIRK_AE7,
+ QUIRK_NONE = HDA_FIXUP_ID_NOT_SET,
+};
+
+#ifdef CONFIG_PCI
+#define ca0132_quirk(spec) ((spec)->codec->fixup_id)
+#define ca0132_use_pci_mmio(spec) ((spec)->use_pci_mmio)
+#define ca0132_use_alt_functions(spec) ((spec)->use_alt_functions)
+#define ca0132_use_alt_controls(spec) ((spec)->use_alt_controls)
+#else
+#define ca0132_quirk(spec) ({ (void)(spec); QUIRK_NONE; })
+#define ca0132_use_alt_functions(spec) ({ (void)(spec); false; })
+#define ca0132_use_pci_mmio(spec) ({ (void)(spec); false; })
+#define ca0132_use_alt_controls(spec) ({ (void)(spec); false; })
+#endif
+
+static const struct hda_pintbl alienware_pincfgs[] = {
+ { 0x0b, 0x90170110 }, /* Builtin Speaker */
+ { 0x0c, 0x411111f0 }, /* N/A */
+ { 0x0d, 0x411111f0 }, /* N/A */
+ { 0x0e, 0x411111f0 }, /* N/A */
+ { 0x0f, 0x0321101f }, /* HP */
+ { 0x10, 0x411111f0 }, /* Headset? disabled for now */
+ { 0x11, 0x03a11021 }, /* Mic */
+ { 0x12, 0xd5a30140 }, /* Builtin Mic */
+ { 0x13, 0x411111f0 }, /* N/A */
+ { 0x18, 0x411111f0 }, /* N/A */
+ {}
+};
+
+/* Sound Blaster Z pin configs taken from Windows Driver */
+static const struct hda_pintbl sbz_pincfgs[] = {
+ { 0x0b, 0x01017010 }, /* Port G -- Lineout FRONT L/R */
+ { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */
+ { 0x0d, 0x014510f0 }, /* Digital Out */
+ { 0x0e, 0x01c510f0 }, /* SPDIF In */
+ { 0x0f, 0x0221701f }, /* Port A -- BackPanel HP */
+ { 0x10, 0x01017012 }, /* Port D -- Center/LFE or FP Hp */
+ { 0x11, 0x01017014 }, /* Port B -- LineMicIn2 / Rear L/R */
+ { 0x12, 0x01a170f0 }, /* Port C -- LineIn1 */
+ { 0x13, 0x908700f0 }, /* What U Hear In*/
+ { 0x18, 0x50d000f0 }, /* N/A */
+ {}
+};
+
+/* Sound Blaster ZxR pin configs taken from Windows Driver */
+static const struct hda_pintbl zxr_pincfgs[] = {
+ { 0x0b, 0x01047110 }, /* Port G -- Lineout FRONT L/R */
+ { 0x0c, 0x414510f0 }, /* SPDIF Out 1 - Disabled*/
+ { 0x0d, 0x014510f0 }, /* Digital Out */
+ { 0x0e, 0x41c520f0 }, /* SPDIF In - Disabled*/
+ { 0x0f, 0x0122711f }, /* Port A -- BackPanel HP */
+ { 0x10, 0x01017111 }, /* Port D -- Center/LFE */
+ { 0x11, 0x01017114 }, /* Port B -- LineMicIn2 / Rear L/R */
+ { 0x12, 0x01a271f0 }, /* Port C -- LineIn1 */
+ { 0x13, 0x908700f0 }, /* What U Hear In*/
+ { 0x18, 0x50d000f0 }, /* N/A */
+ {}
+};
+
+/* Recon3D pin configs taken from Windows Driver */
+static const struct hda_pintbl r3d_pincfgs[] = {
+ { 0x0b, 0x01014110 }, /* Port G -- Lineout FRONT L/R */
+ { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */
+ { 0x0d, 0x014510f0 }, /* Digital Out */
+ { 0x0e, 0x01c520f0 }, /* SPDIF In */
+ { 0x0f, 0x0221401f }, /* Port A -- BackPanel HP */
+ { 0x10, 0x01016011 }, /* Port D -- Center/LFE or FP Hp */
+ { 0x11, 0x01011014 }, /* Port B -- LineMicIn2 / Rear L/R */
+ { 0x12, 0x02a090f0 }, /* Port C -- LineIn1 */
+ { 0x13, 0x908700f0 }, /* What U Hear In*/
+ { 0x18, 0x50d000f0 }, /* N/A */
+ {}
+};
+
+/* Sound Blaster AE-5 pin configs taken from Windows Driver */
+static const struct hda_pintbl ae5_pincfgs[] = {
+ { 0x0b, 0x01017010 }, /* Port G -- Lineout FRONT L/R */
+ { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */
+ { 0x0d, 0x014510f0 }, /* Digital Out */
+ { 0x0e, 0x01c510f0 }, /* SPDIF In */
+ { 0x0f, 0x01017114 }, /* Port A -- Rear L/R. */
+ { 0x10, 0x01017012 }, /* Port D -- Center/LFE or FP Hp */
+ { 0x11, 0x012170ff }, /* Port B -- LineMicIn2 / Rear Headphone */
+ { 0x12, 0x01a170f0 }, /* Port C -- LineIn1 */
+ { 0x13, 0x908700f0 }, /* What U Hear In*/
+ { 0x18, 0x50d000f0 }, /* N/A */
+ {}
+};
+
+/* Recon3D integrated pin configs taken from Windows Driver */
+static const struct hda_pintbl r3di_pincfgs[] = {
+ { 0x0b, 0x01014110 }, /* Port G -- Lineout FRONT L/R */
+ { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */
+ { 0x0d, 0x014510f0 }, /* Digital Out */
+ { 0x0e, 0x41c520f0 }, /* SPDIF In */
+ { 0x0f, 0x0221401f }, /* Port A -- BackPanel HP */
+ { 0x10, 0x01016011 }, /* Port D -- Center/LFE or FP Hp */
+ { 0x11, 0x01011014 }, /* Port B -- LineMicIn2 / Rear L/R */
+ { 0x12, 0x02a090f0 }, /* Port C -- LineIn1 */
+ { 0x13, 0x908700f0 }, /* What U Hear In*/
+ { 0x18, 0x500000f0 }, /* N/A */
+ {}
+};
+
+static const struct hda_pintbl ae7_pincfgs[] = {
+ { 0x0b, 0x01017010 },
+ { 0x0c, 0x014510f0 },
+ { 0x0d, 0x414510f0 },
+ { 0x0e, 0x01c520f0 },
+ { 0x0f, 0x01017114 },
+ { 0x10, 0x01017011 },
+ { 0x11, 0x018170ff },
+ { 0x12, 0x01a170f0 },
+ { 0x13, 0x908700f0 },
+ { 0x18, 0x500000f0 },
+ {}
+};
+
+static const struct hda_quirk ca0132_quirks[] = {
+ SND_PCI_QUIRK(0x1028, 0x057b, "Alienware M17x R4", QUIRK_ALIENWARE_M17XR4),
+ SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15 2015", QUIRK_ALIENWARE),
+ SND_PCI_QUIRK(0x1028, 0x0688, "Alienware 17 2015", QUIRK_ALIENWARE),
+ SND_PCI_QUIRK(0x1028, 0x0708, "Alienware 15 R2 2016", QUIRK_ALIENWARE),
+ SND_PCI_QUIRK(0x1102, 0x0010, "Sound Blaster Z", QUIRK_SBZ),
+ SND_PCI_QUIRK(0x1102, 0x0023, "Sound Blaster Z", QUIRK_SBZ),
+ SND_PCI_QUIRK(0x1102, 0x0027, "Sound Blaster Z", QUIRK_SBZ),
+ SND_PCI_QUIRK(0x1102, 0x0033, "Sound Blaster ZxR", QUIRK_SBZ),
+ SND_PCI_QUIRK(0x1458, 0xA016, "Recon3Di", QUIRK_R3DI),
+ SND_PCI_QUIRK(0x1458, 0xA026, "Gigabyte G1.Sniper Z97", QUIRK_R3DI),
+ SND_PCI_QUIRK(0x1458, 0xA036, "Gigabyte GA-Z170X-Gaming 7", QUIRK_R3DI),
+ SND_PCI_QUIRK(0x3842, 0x1038, "EVGA X99 Classified", QUIRK_R3DI),
+ SND_PCI_QUIRK(0x3842, 0x104b, "EVGA X299 Dark", QUIRK_R3DI),
+ SND_PCI_QUIRK(0x3842, 0x1055, "EVGA Z390 DARK", QUIRK_R3DI),
+ SND_PCI_QUIRK(0x1102, 0x0013, "Recon3D", QUIRK_R3D),
+ SND_PCI_QUIRK(0x1102, 0x0018, "Recon3D", QUIRK_R3D),
+ SND_PCI_QUIRK(0x1102, 0x0051, "Sound Blaster AE-5", QUIRK_AE5),
+ SND_PCI_QUIRK(0x1102, 0x0191, "Sound Blaster AE-5 Plus", QUIRK_AE5),
+ SND_PCI_QUIRK(0x1102, 0x0081, "Sound Blaster AE-7", QUIRK_AE7),
+ {}
+};
+
+static const struct hda_model_fixup ca0132_quirk_models[] = {
+ { .id = QUIRK_ALIENWARE, .name = "alienware" },
+ { .id = QUIRK_ALIENWARE_M17XR4, .name = "alienware-m17xr4" },
+ { .id = QUIRK_SBZ, .name = "sbz" },
+ { .id = QUIRK_ZXR, .name = "zxr" },
+ { .id = QUIRK_ZXR_DBPRO, .name = "zxr-dbpro" },
+ { .id = QUIRK_R3DI, .name = "r3di" },
+ { .id = QUIRK_R3D, .name = "r3d" },
+ { .id = QUIRK_AE5, .name = "ae5" },
+ { .id = QUIRK_AE7, .name = "ae7" },
+ {}
+};
+
+/* Output selection quirk info structures. */
+#define MAX_QUIRK_MMIO_GPIO_SET_VALS 3
+#define MAX_QUIRK_SCP_SET_VALS 2
+struct ca0132_alt_out_set_info {
+ unsigned int dac2port; /* ParamID 0x0d value. */
+
+ bool has_hda_gpio;
+ char hda_gpio_pin;
+ char hda_gpio_set;
+
+ unsigned int mmio_gpio_count;
+ char mmio_gpio_pin[MAX_QUIRK_MMIO_GPIO_SET_VALS];
+ char mmio_gpio_set[MAX_QUIRK_MMIO_GPIO_SET_VALS];
+
+ unsigned int scp_cmds_count;
+ unsigned int scp_cmd_mid[MAX_QUIRK_SCP_SET_VALS];
+ unsigned int scp_cmd_req[MAX_QUIRK_SCP_SET_VALS];
+ unsigned int scp_cmd_val[MAX_QUIRK_SCP_SET_VALS];
+
+ bool has_chipio_write;
+ unsigned int chipio_write_addr;
+ unsigned int chipio_write_data;
+};
+
+struct ca0132_alt_out_set_quirk_data {
+ int quirk_id;
+
+ bool has_headphone_gain;
+ bool is_ae_series;
+
+ struct ca0132_alt_out_set_info out_set_info[NUM_OF_OUTPUTS];
+};
+
+static const struct ca0132_alt_out_set_quirk_data quirk_out_set_data[] = {
+ { .quirk_id = QUIRK_R3DI,
+ .has_headphone_gain = false,
+ .is_ae_series = false,
+ .out_set_info = {
+ /* Speakers. */
+ { .dac2port = 0x24,
+ .has_hda_gpio = true,
+ .hda_gpio_pin = 2,
+ .hda_gpio_set = 1,
+ .mmio_gpio_count = 0,
+ .scp_cmds_count = 0,
+ .has_chipio_write = false,
+ },
+ /* Headphones. */
+ { .dac2port = 0x21,
+ .has_hda_gpio = true,
+ .hda_gpio_pin = 2,
+ .hda_gpio_set = 0,
+ .mmio_gpio_count = 0,
+ .scp_cmds_count = 0,
+ .has_chipio_write = false,
+ } },
+ },
+ { .quirk_id = QUIRK_R3D,
+ .has_headphone_gain = false,
+ .is_ae_series = false,
+ .out_set_info = {
+ /* Speakers. */
+ { .dac2port = 0x24,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 1,
+ .mmio_gpio_pin = { 1 },
+ .mmio_gpio_set = { 1 },
+ .scp_cmds_count = 0,
+ .has_chipio_write = false,
+ },
+ /* Headphones. */
+ { .dac2port = 0x21,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 1,
+ .mmio_gpio_pin = { 1 },
+ .mmio_gpio_set = { 0 },
+ .scp_cmds_count = 0,
+ .has_chipio_write = false,
+ } },
+ },
+ { .quirk_id = QUIRK_SBZ,
+ .has_headphone_gain = false,
+ .is_ae_series = false,
+ .out_set_info = {
+ /* Speakers. */
+ { .dac2port = 0x18,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 3,
+ .mmio_gpio_pin = { 7, 4, 1 },
+ .mmio_gpio_set = { 0, 1, 1 },
+ .scp_cmds_count = 0,
+ .has_chipio_write = false, },
+ /* Headphones. */
+ { .dac2port = 0x12,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 3,
+ .mmio_gpio_pin = { 7, 4, 1 },
+ .mmio_gpio_set = { 1, 1, 0 },
+ .scp_cmds_count = 0,
+ .has_chipio_write = false,
+ } },
+ },
+ { .quirk_id = QUIRK_ZXR,
+ .has_headphone_gain = true,
+ .is_ae_series = false,
+ .out_set_info = {
+ /* Speakers. */
+ { .dac2port = 0x24,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 3,
+ .mmio_gpio_pin = { 2, 3, 5 },
+ .mmio_gpio_set = { 1, 1, 0 },
+ .scp_cmds_count = 0,
+ .has_chipio_write = false,
+ },
+ /* Headphones. */
+ { .dac2port = 0x21,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 3,
+ .mmio_gpio_pin = { 2, 3, 5 },
+ .mmio_gpio_set = { 0, 1, 1 },
+ .scp_cmds_count = 0,
+ .has_chipio_write = false,
+ } },
+ },
+ { .quirk_id = QUIRK_AE5,
+ .has_headphone_gain = true,
+ .is_ae_series = true,
+ .out_set_info = {
+ /* Speakers. */
+ { .dac2port = 0xa4,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 0,
+ .scp_cmds_count = 2,
+ .scp_cmd_mid = { 0x96, 0x96 },
+ .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT,
+ SPEAKER_TUNING_FRONT_RIGHT_INVERT },
+ .scp_cmd_val = { FLOAT_ZERO, FLOAT_ZERO },
+ .has_chipio_write = true,
+ .chipio_write_addr = 0x0018b03c,
+ .chipio_write_data = 0x00000012
+ },
+ /* Headphones. */
+ { .dac2port = 0xa1,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 0,
+ .scp_cmds_count = 2,
+ .scp_cmd_mid = { 0x96, 0x96 },
+ .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT,
+ SPEAKER_TUNING_FRONT_RIGHT_INVERT },
+ .scp_cmd_val = { FLOAT_ONE, FLOAT_ONE },
+ .has_chipio_write = true,
+ .chipio_write_addr = 0x0018b03c,
+ .chipio_write_data = 0x00000012
+ } },
+ },
+ { .quirk_id = QUIRK_AE7,
+ .has_headphone_gain = true,
+ .is_ae_series = true,
+ .out_set_info = {
+ /* Speakers. */
+ { .dac2port = 0x58,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 1,
+ .mmio_gpio_pin = { 0 },
+ .mmio_gpio_set = { 1 },
+ .scp_cmds_count = 2,
+ .scp_cmd_mid = { 0x96, 0x96 },
+ .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT,
+ SPEAKER_TUNING_FRONT_RIGHT_INVERT },
+ .scp_cmd_val = { FLOAT_ZERO, FLOAT_ZERO },
+ .has_chipio_write = true,
+ .chipio_write_addr = 0x0018b03c,
+ .chipio_write_data = 0x00000000
+ },
+ /* Headphones. */
+ { .dac2port = 0x58,
+ .has_hda_gpio = false,
+ .mmio_gpio_count = 1,
+ .mmio_gpio_pin = { 0 },
+ .mmio_gpio_set = { 1 },
+ .scp_cmds_count = 2,
+ .scp_cmd_mid = { 0x96, 0x96 },
+ .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT,
+ SPEAKER_TUNING_FRONT_RIGHT_INVERT },
+ .scp_cmd_val = { FLOAT_ONE, FLOAT_ONE },
+ .has_chipio_write = true,
+ .chipio_write_addr = 0x0018b03c,
+ .chipio_write_data = 0x00000010
+ } },
+ }
+};
+
+/*
+ * CA0132 codec access
+ */
+static unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int verb, unsigned int parm, unsigned int *res)
+{
+ unsigned int response;
+ response = snd_hda_codec_read(codec, nid, 0, verb, parm);
+ *res = response;
+
+ return ((response == -1) ? -1 : 0);
+}
+
+static int codec_set_converter_format(struct hda_codec *codec, hda_nid_t nid,
+ unsigned short converter_format, unsigned int *res)
+{
+ return codec_send_command(codec, nid, VENDOR_CHIPIO_STREAM_FORMAT,
+ converter_format & 0xffff, res);
+}
+
+static int codec_set_converter_stream_channel(struct hda_codec *codec,
+ hda_nid_t nid, unsigned char stream,
+ unsigned char channel, unsigned int *res)
+{
+ unsigned char converter_stream_channel = 0;
+
+ converter_stream_channel = (stream << 4) | (channel & 0x0f);
+ return codec_send_command(codec, nid, AC_VERB_SET_CHANNEL_STREAMID,
+ converter_stream_channel, res);
+}
+
+/* Chip access helper function */
+static int chipio_send(struct hda_codec *codec,
+ unsigned int reg,
+ unsigned int data)
+{
+ unsigned int res;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+ /* send bits of data specified by reg */
+ do {
+ res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+ reg, data);
+ if (res == VENDOR_STATUS_CHIPIO_OK)
+ return 0;
+ msleep(20);
+ } while (time_before(jiffies, timeout));
+
+ return -EIO;
+}
+
+/*
+ * Write chip address through the vendor widget -- NOT protected by the Mutex!
+ */
+static int chipio_write_address(struct hda_codec *codec,
+ unsigned int chip_addx)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int res;
+
+ if (spec->curr_chip_addx == chip_addx)
+ return 0;
+
+ /* send low 16 bits of the address */
+ res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW,
+ chip_addx & 0xffff);
+
+ if (res != -EIO) {
+ /* send high 16 bits of the address */
+ res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH,
+ chip_addx >> 16);
+ }
+
+ spec->curr_chip_addx = (res < 0) ? ~0U : chip_addx;
+
+ return res;
+}
+
+/*
+ * Write data through the vendor widget -- NOT protected by the Mutex!
+ */
+static int chipio_write_data(struct hda_codec *codec, unsigned int data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int res;
+
+ /* send low 16 bits of the data */
+ res = chipio_send(codec, VENDOR_CHIPIO_DATA_LOW, data & 0xffff);
+
+ if (res != -EIO) {
+ /* send high 16 bits of the data */
+ res = chipio_send(codec, VENDOR_CHIPIO_DATA_HIGH,
+ data >> 16);
+ }
+
+ /*If no error encountered, automatically increment the address
+ as per chip behaviour*/
+ spec->curr_chip_addx = (res != -EIO) ?
+ (spec->curr_chip_addx + 4) : ~0U;
+ return res;
+}
+
+/*
+ * Write multiple data through the vendor widget -- NOT protected by the Mutex!
+ */
+static int chipio_write_data_multiple(struct hda_codec *codec,
+ const u32 *data,
+ unsigned int count)
+{
+ int status = 0;
+
+ if (data == NULL) {
+ codec_dbg(codec, "chipio_write_data null ptr\n");
+ return -EINVAL;
+ }
+
+ while ((count-- != 0) && (status == 0))
+ status = chipio_write_data(codec, *data++);
+
+ return status;
+}
+
+
+/*
+ * Read data through the vendor widget -- NOT protected by the Mutex!
+ */
+static int chipio_read_data(struct hda_codec *codec, unsigned int *data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int res;
+
+ /* post read */
+ res = chipio_send(codec, VENDOR_CHIPIO_HIC_POST_READ, 0);
+
+ if (res != -EIO) {
+ /* read status */
+ res = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+ }
+
+ if (res != -EIO) {
+ /* read data */
+ *data = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_HIC_READ_DATA,
+ 0);
+ }
+
+ /*If no error encountered, automatically increment the address
+ as per chip behaviour*/
+ spec->curr_chip_addx = (res != -EIO) ?
+ (spec->curr_chip_addx + 4) : ~0U;
+ return res;
+}
+
+/*
+ * Write given value to the given address through the chip I/O widget.
+ * protected by the Mutex
+ */
+static int chipio_write(struct hda_codec *codec,
+ unsigned int chip_addx, const unsigned int data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int err;
+
+ guard(mutex)(&spec->chipio_mutex);
+
+ /* write the address, and if successful proceed to write data */
+ err = chipio_write_address(codec, chip_addx);
+ if (err < 0)
+ return err;
+
+ return chipio_write_data(codec, data);
+}
+
+/*
+ * Write given value to the given address through the chip I/O widget.
+ * not protected by the Mutex
+ */
+static int chipio_write_no_mutex(struct hda_codec *codec,
+ unsigned int chip_addx, const unsigned int data)
+{
+ int err;
+
+
+ /* write the address, and if successful proceed to write data */
+ err = chipio_write_address(codec, chip_addx);
+ if (err < 0)
+ goto exit;
+
+ err = chipio_write_data(codec, data);
+ if (err < 0)
+ goto exit;
+
+exit:
+ return err;
+}
+
+/*
+ * Write multiple values to the given address through the chip I/O widget.
+ * protected by the Mutex
+ */
+static int chipio_write_multiple(struct hda_codec *codec,
+ u32 chip_addx,
+ const u32 *data,
+ unsigned int count)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int status;
+
+ guard(mutex)(&spec->chipio_mutex);
+ status = chipio_write_address(codec, chip_addx);
+ if (status < 0)
+ return status;
+
+ return chipio_write_data_multiple(codec, data, count);
+}
+
+/*
+ * Read the given address through the chip I/O widget
+ * protected by the Mutex
+ */
+static int chipio_read(struct hda_codec *codec,
+ unsigned int chip_addx, unsigned int *data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int err;
+
+ guard(mutex)(&spec->chipio_mutex);
+
+ /* write the address, and if successful proceed to write data */
+ err = chipio_write_address(codec, chip_addx);
+ if (err < 0)
+ return err;
+
+ return chipio_read_data(codec, data);
+}
+
+/*
+ * Set chip control flags through the chip I/O widget.
+ */
+static void chipio_set_control_flag(struct hda_codec *codec,
+ enum control_flag_id flag_id,
+ bool flag_state)
+{
+ unsigned int val;
+ unsigned int flag_bit;
+
+ flag_bit = (flag_state ? 1 : 0);
+ val = (flag_bit << 7) | (flag_id);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_FLAG_SET, val);
+}
+
+/*
+ * Set chip parameters through the chip I/O widget.
+ */
+static void chipio_set_control_param(struct hda_codec *codec,
+ enum control_param_id param_id, int param_val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int val;
+
+ if ((param_id < 32) && (param_val < 8)) {
+ val = (param_val << 5) | (param_id);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_SET, val);
+ } else {
+ guard(mutex)(&spec->chipio_mutex);
+ if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) {
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_ID_SET,
+ param_id);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_VALUE_SET,
+ param_val);
+ }
+ }
+}
+
+/*
+ * Set chip parameters through the chip I/O widget. NO MUTEX.
+ */
+static void chipio_set_control_param_no_mutex(struct hda_codec *codec,
+ enum control_param_id param_id, int param_val)
+{
+ int val;
+
+ if ((param_id < 32) && (param_val < 8)) {
+ val = (param_val << 5) | (param_id);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_SET, val);
+ } else {
+ if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) {
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_ID_SET,
+ param_id);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_VALUE_SET,
+ param_val);
+ }
+ }
+}
+/*
+ * Connect stream to a source point, and then connect
+ * that source point to a destination point.
+ */
+static void chipio_set_stream_source_dest(struct hda_codec *codec,
+ int streamid, int source_point, int dest_point)
+{
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_ID, streamid);
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_SOURCE_CONN_POINT, source_point);
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_DEST_CONN_POINT, dest_point);
+}
+
+/*
+ * Set number of channels in the selected stream.
+ */
+static void chipio_set_stream_channels(struct hda_codec *codec,
+ int streamid, unsigned int channels)
+{
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_ID, streamid);
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAMS_CHANNELS, channels);
+}
+
+/*
+ * Enable/Disable audio stream.
+ */
+static void chipio_set_stream_control(struct hda_codec *codec,
+ int streamid, int enable)
+{
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_ID, streamid);
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_CONTROL, enable);
+}
+
+/*
+ * Get ChipIO audio stream's status.
+ */
+static void chipio_get_stream_control(struct hda_codec *codec,
+ int streamid, unsigned int *enable)
+{
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_STREAM_ID, streamid);
+ *enable = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_GET,
+ CONTROL_PARAM_STREAM_CONTROL);
+}
+
+/*
+ * Set sampling rate of the connection point. NO MUTEX.
+ */
+static void chipio_set_conn_rate_no_mutex(struct hda_codec *codec,
+ int connid, enum ca0132_sample_rate rate)
+{
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_CONN_POINT_ID, connid);
+ chipio_set_control_param_no_mutex(codec,
+ CONTROL_PARAM_CONN_POINT_SAMPLE_RATE, rate);
+}
+
+/*
+ * Set sampling rate of the connection point.
+ */
+static void chipio_set_conn_rate(struct hda_codec *codec,
+ int connid, enum ca0132_sample_rate rate)
+{
+ chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_ID, connid);
+ chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_SAMPLE_RATE,
+ rate);
+}
+
+/*
+ * Writes to the 8051's internal address space directly instead of indirectly,
+ * giving access to the special function registers located at addresses
+ * 0x80-0xFF.
+ */
+static void chipio_8051_write_direct(struct hda_codec *codec,
+ unsigned int addr, unsigned int data)
+{
+ unsigned int verb;
+
+ verb = VENDOR_CHIPIO_8051_WRITE_DIRECT | data;
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, verb, addr);
+}
+
+/*
+ * Writes to the 8051's exram, which has 16-bits of address space.
+ * Data at addresses 0x2000-0x7fff is mirrored to 0x8000-0xdfff.
+ * Data at 0x8000-0xdfff can also be used as program memory for the 8051 by
+ * setting the pmem bank selection SFR.
+ * 0xe000-0xffff is always mapped as program memory, with only 0xf000-0xffff
+ * being writable.
+ */
+static void chipio_8051_set_address(struct hda_codec *codec, unsigned int addr)
+{
+ unsigned int tmp;
+
+ /* Lower 8-bits. */
+ tmp = addr & 0xff;
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_LOW, tmp);
+
+ /* Upper 8-bits. */
+ tmp = (addr >> 8) & 0xff;
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_ADDRESS_HIGH, tmp);
+}
+
+static void chipio_8051_set_data(struct hda_codec *codec, unsigned int data)
+{
+ /* 8-bits of data. */
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_DATA_WRITE, data & 0xff);
+}
+
+static unsigned int chipio_8051_get_data(struct hda_codec *codec)
+{
+ return snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_8051_DATA_READ, 0);
+}
+
+/* PLL_PMU writes share the lower address register of the 8051 exram writes. */
+static void chipio_8051_set_data_pll(struct hda_codec *codec, unsigned int data)
+{
+ /* 8-bits of data. */
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PLL_PMU_WRITE, data & 0xff);
+}
+
+static void chipio_8051_write_exram(struct hda_codec *codec,
+ unsigned int addr, unsigned int data)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ guard(mutex)(&spec->chipio_mutex);
+
+ chipio_8051_set_address(codec, addr);
+ chipio_8051_set_data(codec, data);
+}
+
+static void chipio_8051_write_exram_no_mutex(struct hda_codec *codec,
+ unsigned int addr, unsigned int data)
+{
+ chipio_8051_set_address(codec, addr);
+ chipio_8051_set_data(codec, data);
+}
+
+/* Readback data from the 8051's exram. No mutex. */
+static void chipio_8051_read_exram(struct hda_codec *codec,
+ unsigned int addr, unsigned int *data)
+{
+ chipio_8051_set_address(codec, addr);
+ *data = chipio_8051_get_data(codec);
+}
+
+static void chipio_8051_write_pll_pmu(struct hda_codec *codec,
+ unsigned int addr, unsigned int data)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ guard(mutex)(&spec->chipio_mutex);
+
+ chipio_8051_set_address(codec, addr & 0xff);
+ chipio_8051_set_data_pll(codec, data);
+}
+
+static void chipio_8051_write_pll_pmu_no_mutex(struct hda_codec *codec,
+ unsigned int addr, unsigned int data)
+{
+ chipio_8051_set_address(codec, addr & 0xff);
+ chipio_8051_set_data_pll(codec, data);
+}
+
+/*
+ * Enable clocks.
+ */
+static void chipio_enable_clocks(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ guard(mutex)(&spec->chipio_mutex);
+
+ chipio_8051_write_pll_pmu_no_mutex(codec, 0x00, 0xff);
+ chipio_8051_write_pll_pmu_no_mutex(codec, 0x05, 0x0b);
+ chipio_8051_write_pll_pmu_no_mutex(codec, 0x06, 0xff);
+}
+
+/*
+ * CA0132 DSP IO stuffs
+ */
+static int dspio_send(struct hda_codec *codec, unsigned int reg,
+ unsigned int data)
+{
+ int res;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+ /* send bits of data specified by reg to dsp */
+ do {
+ res = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, reg, data);
+ if ((res >= 0) && (res != VENDOR_STATUS_DSPIO_BUSY))
+ return res;
+ msleep(20);
+ } while (time_before(jiffies, timeout));
+
+ return -EIO;
+}
+
+/*
+ * Wait for DSP to be ready for commands
+ */
+static void dspio_write_wait(struct hda_codec *codec)
+{
+ int status;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+ do {
+ status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
+ VENDOR_DSPIO_STATUS, 0);
+ if ((status == VENDOR_STATUS_DSPIO_OK) ||
+ (status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY))
+ break;
+ msleep(1);
+ } while (time_before(jiffies, timeout));
+}
+
+/*
+ * Write SCP data to DSP
+ */
+static int dspio_write(struct hda_codec *codec, unsigned int scp_data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int status;
+
+ dspio_write_wait(codec);
+
+ guard(mutex)(&spec->chipio_mutex);
+ status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_LOW,
+ scp_data & 0xffff);
+ if (status < 0)
+ return status;
+
+ status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_HIGH,
+ scp_data >> 16);
+ if (status < 0)
+ return status;
+
+ /* OK, now check if the write itself has executed*/
+ status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
+ VENDOR_DSPIO_STATUS, 0);
+
+ return (status == VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL) ?
+ -EIO : 0;
+}
+
+/*
+ * Write multiple SCP data to DSP
+ */
+static int dspio_write_multiple(struct hda_codec *codec,
+ unsigned int *buffer, unsigned int size)
+{
+ int status = 0;
+ unsigned int count;
+
+ if (buffer == NULL)
+ return -EINVAL;
+
+ count = 0;
+ while (count < size) {
+ status = dspio_write(codec, *buffer++);
+ if (status != 0)
+ break;
+ count++;
+ }
+
+ return status;
+}
+
+static int dspio_read(struct hda_codec *codec, unsigned int *data)
+{
+ int status;
+
+ status = dspio_send(codec, VENDOR_DSPIO_SCP_POST_READ_DATA, 0);
+ if (status == -EIO)
+ return status;
+
+ status = dspio_send(codec, VENDOR_DSPIO_STATUS, 0);
+ if (status == -EIO ||
+ status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY)
+ return -EIO;
+
+ *data = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
+ VENDOR_DSPIO_SCP_READ_DATA, 0);
+
+ return 0;
+}
+
+static int dspio_read_multiple(struct hda_codec *codec, unsigned int *buffer,
+ unsigned int *buf_size, unsigned int size_count)
+{
+ int status = 0;
+ unsigned int size = *buf_size;
+ unsigned int count;
+ unsigned int skip_count;
+ unsigned int dummy;
+
+ if (buffer == NULL)
+ return -1;
+
+ count = 0;
+ while (count < size && count < size_count) {
+ status = dspio_read(codec, buffer++);
+ if (status != 0)
+ break;
+ count++;
+ }
+
+ skip_count = count;
+ if (status == 0) {
+ while (skip_count < size) {
+ status = dspio_read(codec, &dummy);
+ if (status != 0)
+ break;
+ skip_count++;
+ }
+ }
+ *buf_size = count;
+
+ return status;
+}
+
+/*
+ * Construct the SCP header using corresponding fields
+ */
+static inline unsigned int
+make_scp_header(unsigned int target_id, unsigned int source_id,
+ unsigned int get_flag, unsigned int req,
+ unsigned int device_flag, unsigned int resp_flag,
+ unsigned int error_flag, unsigned int data_size)
+{
+ unsigned int header = 0;
+
+ header = (data_size & 0x1f) << 27;
+ header |= (error_flag & 0x01) << 26;
+ header |= (resp_flag & 0x01) << 25;
+ header |= (device_flag & 0x01) << 24;
+ header |= (req & 0x7f) << 17;
+ header |= (get_flag & 0x01) << 16;
+ header |= (source_id & 0xff) << 8;
+ header |= target_id & 0xff;
+
+ return header;
+}
+
+/*
+ * Extract corresponding fields from SCP header
+ */
+static inline void
+extract_scp_header(unsigned int header,
+ unsigned int *target_id, unsigned int *source_id,
+ unsigned int *get_flag, unsigned int *req,
+ unsigned int *device_flag, unsigned int *resp_flag,
+ unsigned int *error_flag, unsigned int *data_size)
+{
+ if (data_size)
+ *data_size = (header >> 27) & 0x1f;
+ if (error_flag)
+ *error_flag = (header >> 26) & 0x01;
+ if (resp_flag)
+ *resp_flag = (header >> 25) & 0x01;
+ if (device_flag)
+ *device_flag = (header >> 24) & 0x01;
+ if (req)
+ *req = (header >> 17) & 0x7f;
+ if (get_flag)
+ *get_flag = (header >> 16) & 0x01;
+ if (source_id)
+ *source_id = (header >> 8) & 0xff;
+ if (target_id)
+ *target_id = header & 0xff;
+}
+
+#define SCP_MAX_DATA_WORDS (16)
+
+/* Structure to contain any SCP message */
+struct scp_msg {
+ unsigned int hdr;
+ unsigned int data[SCP_MAX_DATA_WORDS];
+};
+
+static void dspio_clear_response_queue(struct hda_codec *codec)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+ unsigned int dummy = 0;
+ int status;
+
+ /* clear all from the response queue */
+ do {
+ status = dspio_read(codec, &dummy);
+ } while (status == 0 && time_before(jiffies, timeout));
+}
+
+static int dspio_get_response_data(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int data = 0;
+ unsigned int count;
+
+ if (dspio_read(codec, &data) < 0)
+ return -EIO;
+
+ if ((data & 0x00ffffff) == spec->wait_scp_header) {
+ spec->scp_resp_header = data;
+ spec->scp_resp_count = data >> 27;
+ count = spec->wait_num_data;
+ dspio_read_multiple(codec, spec->scp_resp_data,
+ &spec->scp_resp_count, count);
+ return 0;
+ }
+
+ return -EIO;
+}
+
+/*
+ * Send SCP message to DSP
+ */
+static int dspio_send_scp_message(struct hda_codec *codec,
+ unsigned char *send_buf,
+ unsigned int send_buf_size,
+ unsigned char *return_buf,
+ unsigned int return_buf_size,
+ unsigned int *bytes_returned)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int status;
+ unsigned int scp_send_size = 0;
+ unsigned int total_size;
+ bool waiting_for_resp = false;
+ unsigned int header;
+ struct scp_msg *ret_msg;
+ unsigned int resp_src_id, resp_target_id;
+ unsigned int data_size, src_id, target_id, get_flag, device_flag;
+
+ if (bytes_returned)
+ *bytes_returned = 0;
+
+ /* get scp header from buffer */
+ header = *((unsigned int *)send_buf);
+ extract_scp_header(header, &target_id, &src_id, &get_flag, NULL,
+ &device_flag, NULL, NULL, &data_size);
+ scp_send_size = data_size + 1;
+ total_size = (scp_send_size * 4);
+
+ if (send_buf_size < total_size)
+ return -EINVAL;
+
+ if (get_flag || device_flag) {
+ if (!return_buf || return_buf_size < 4 || !bytes_returned)
+ return -EINVAL;
+
+ spec->wait_scp_header = *((unsigned int *)send_buf);
+
+ /* swap source id with target id */
+ resp_target_id = src_id;
+ resp_src_id = target_id;
+ spec->wait_scp_header &= 0xffff0000;
+ spec->wait_scp_header |= (resp_src_id << 8) | (resp_target_id);
+ spec->wait_num_data = return_buf_size/sizeof(unsigned int) - 1;
+ spec->wait_scp = 1;
+ waiting_for_resp = true;
+ }
+
+ status = dspio_write_multiple(codec, (unsigned int *)send_buf,
+ scp_send_size);
+ if (status < 0) {
+ spec->wait_scp = 0;
+ return status;
+ }
+
+ if (waiting_for_resp) {
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+ memset(return_buf, 0, return_buf_size);
+ do {
+ msleep(20);
+ } while (spec->wait_scp && time_before(jiffies, timeout));
+ waiting_for_resp = false;
+ if (!spec->wait_scp) {
+ ret_msg = (struct scp_msg *)return_buf;
+ memcpy(&ret_msg->hdr, &spec->scp_resp_header, 4);
+ memcpy(&ret_msg->data, spec->scp_resp_data,
+ spec->wait_num_data);
+ *bytes_returned = (spec->scp_resp_count + 1) * 4;
+ status = 0;
+ } else {
+ status = -EIO;
+ }
+ spec->wait_scp = 0;
+ }
+
+ return status;
+}
+
+/**
+ * dspio_scp - Prepare and send the SCP message to DSP
+ * @codec: the HDA codec
+ * @mod_id: ID of the DSP module to send the command
+ * @src_id: ID of the source
+ * @req: ID of request to send to the DSP module
+ * @dir: SET or GET
+ * @data: pointer to the data to send with the request, request specific
+ * @len: length of the data, in bytes
+ * @reply: point to the buffer to hold data returned for a reply
+ * @reply_len: length of the reply buffer returned from GET
+ *
+ * Returns zero or a negative error code.
+ */
+static int dspio_scp(struct hda_codec *codec,
+ int mod_id, int src_id, int req, int dir, const void *data,
+ unsigned int len, void *reply, unsigned int *reply_len)
+{
+ int status = 0;
+ struct scp_msg scp_send, scp_reply;
+ unsigned int ret_bytes, send_size, ret_size;
+ unsigned int send_get_flag, reply_resp_flag, reply_error_flag;
+ unsigned int reply_data_size;
+
+ memset(&scp_send, 0, sizeof(scp_send));
+ memset(&scp_reply, 0, sizeof(scp_reply));
+
+ if ((len != 0 && data == NULL) || (len > SCP_MAX_DATA_WORDS))
+ return -EINVAL;
+
+ if (dir == SCP_GET && reply == NULL) {
+ codec_dbg(codec, "dspio_scp get but has no buffer\n");
+ return -EINVAL;
+ }
+
+ if (reply != NULL && (reply_len == NULL || (*reply_len == 0))) {
+ codec_dbg(codec, "dspio_scp bad resp buf len parms\n");
+ return -EINVAL;
+ }
+
+ scp_send.hdr = make_scp_header(mod_id, src_id, (dir == SCP_GET), req,
+ 0, 0, 0, len/sizeof(unsigned int));
+ if (data != NULL && len > 0) {
+ len = min((unsigned int)(sizeof(scp_send.data)), len);
+ memcpy(scp_send.data, data, len);
+ }
+
+ ret_bytes = 0;
+ send_size = sizeof(unsigned int) + len;
+ status = dspio_send_scp_message(codec, (unsigned char *)&scp_send,
+ send_size, (unsigned char *)&scp_reply,
+ sizeof(scp_reply), &ret_bytes);
+
+ if (status < 0) {
+ codec_dbg(codec, "dspio_scp: send scp msg failed\n");
+ return status;
+ }
+
+ /* extract send and reply headers members */
+ extract_scp_header(scp_send.hdr, NULL, NULL, &send_get_flag,
+ NULL, NULL, NULL, NULL, NULL);
+ extract_scp_header(scp_reply.hdr, NULL, NULL, NULL, NULL, NULL,
+ &reply_resp_flag, &reply_error_flag,
+ &reply_data_size);
+
+ if (!send_get_flag)
+ return 0;
+
+ if (reply_resp_flag && !reply_error_flag) {
+ ret_size = (ret_bytes - sizeof(scp_reply.hdr))
+ / sizeof(unsigned int);
+
+ if (*reply_len < ret_size*sizeof(unsigned int)) {
+ codec_dbg(codec, "reply too long for buf\n");
+ return -EINVAL;
+ } else if (ret_size != reply_data_size) {
+ codec_dbg(codec, "RetLen and HdrLen .NE.\n");
+ return -EINVAL;
+ } else if (!reply) {
+ codec_dbg(codec, "NULL reply\n");
+ return -EINVAL;
+ } else {
+ *reply_len = ret_size*sizeof(unsigned int);
+ memcpy(reply, scp_reply.data, *reply_len);
+ }
+ } else {
+ codec_dbg(codec, "reply ill-formed or errflag set\n");
+ return -EIO;
+ }
+
+ return status;
+}
+
+/*
+ * Set DSP parameters
+ */
+static int dspio_set_param(struct hda_codec *codec, int mod_id,
+ int src_id, int req, const void *data, unsigned int len)
+{
+ return dspio_scp(codec, mod_id, src_id, req, SCP_SET, data, len, NULL,
+ NULL);
+}
+
+static int dspio_set_uint_param(struct hda_codec *codec, int mod_id,
+ int req, const unsigned int data)
+{
+ return dspio_set_param(codec, mod_id, 0x20, req, &data,
+ sizeof(unsigned int));
+}
+
+/*
+ * Allocate a DSP DMA channel via an SCP message
+ */
+static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan)
+{
+ int status = 0;
+ unsigned int size = sizeof(*dma_chan);
+
+ codec_dbg(codec, " dspio_alloc_dma_chan() -- begin\n");
+ status = dspio_scp(codec, MASTERCONTROL, 0x20,
+ MASTERCONTROL_ALLOC_DMA_CHAN, SCP_GET, NULL, 0,
+ dma_chan, &size);
+
+ if (status < 0) {
+ codec_dbg(codec, "dspio_alloc_dma_chan: SCP Failed\n");
+ return status;
+ }
+
+ if ((*dma_chan + 1) == 0) {
+ codec_dbg(codec, "no free dma channels to allocate\n");
+ return -EBUSY;
+ }
+
+ codec_dbg(codec, "dspio_alloc_dma_chan: chan=%d\n", *dma_chan);
+ codec_dbg(codec, " dspio_alloc_dma_chan() -- complete\n");
+
+ return status;
+}
+
+/*
+ * Free a DSP DMA via an SCP message
+ */
+static int dspio_free_dma_chan(struct hda_codec *codec, unsigned int dma_chan)
+{
+ int status = 0;
+ unsigned int dummy = 0;
+
+ codec_dbg(codec, " dspio_free_dma_chan() -- begin\n");
+ codec_dbg(codec, "dspio_free_dma_chan: chan=%d\n", dma_chan);
+
+ status = dspio_scp(codec, MASTERCONTROL, 0x20,
+ MASTERCONTROL_ALLOC_DMA_CHAN, SCP_SET, &dma_chan,
+ sizeof(dma_chan), NULL, &dummy);
+
+ if (status < 0) {
+ codec_dbg(codec, "dspio_free_dma_chan: SCP Failed\n");
+ return status;
+ }
+
+ codec_dbg(codec, " dspio_free_dma_chan() -- complete\n");
+
+ return status;
+}
+
+/*
+ * (Re)start the DSP
+ */
+static int dsp_set_run_state(struct hda_codec *codec)
+{
+ unsigned int dbg_ctrl_reg;
+ unsigned int halt_state;
+ int err;
+
+ err = chipio_read(codec, DSP_DBGCNTL_INST_OFFSET, &dbg_ctrl_reg);
+ if (err < 0)
+ return err;
+
+ halt_state = (dbg_ctrl_reg & DSP_DBGCNTL_STATE_MASK) >>
+ DSP_DBGCNTL_STATE_LOBIT;
+
+ if (halt_state != 0) {
+ dbg_ctrl_reg &= ~((halt_state << DSP_DBGCNTL_SS_LOBIT) &
+ DSP_DBGCNTL_SS_MASK);
+ err = chipio_write(codec, DSP_DBGCNTL_INST_OFFSET,
+ dbg_ctrl_reg);
+ if (err < 0)
+ return err;
+
+ dbg_ctrl_reg |= (halt_state << DSP_DBGCNTL_EXEC_LOBIT) &
+ DSP_DBGCNTL_EXEC_MASK;
+ err = chipio_write(codec, DSP_DBGCNTL_INST_OFFSET,
+ dbg_ctrl_reg);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * Reset the DSP
+ */
+static int dsp_reset(struct hda_codec *codec)
+{
+ unsigned int res;
+ int retry = 20;
+
+ codec_dbg(codec, "dsp_reset\n");
+ do {
+ res = dspio_send(codec, VENDOR_DSPIO_DSP_INIT, 0);
+ retry--;
+ } while (res == -EIO && retry);
+
+ if (!retry) {
+ codec_dbg(codec, "dsp_reset timeout\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * Convert chip address to DSP address
+ */
+static unsigned int dsp_chip_to_dsp_addx(unsigned int chip_addx,
+ bool *code, bool *yram)
+{
+ *code = *yram = false;
+
+ if (UC_RANGE(chip_addx, 1)) {
+ *code = true;
+ return UC_OFF(chip_addx);
+ } else if (X_RANGE_ALL(chip_addx, 1)) {
+ return X_OFF(chip_addx);
+ } else if (Y_RANGE_ALL(chip_addx, 1)) {
+ *yram = true;
+ return Y_OFF(chip_addx);
+ }
+
+ return INVALID_CHIP_ADDRESS;
+}
+
+/*
+ * Check if the DSP DMA is active
+ */
+static bool dsp_is_dma_active(struct hda_codec *codec, unsigned int dma_chan)
+{
+ unsigned int dma_chnlstart_reg;
+
+ chipio_read(codec, DSPDMAC_CHNLSTART_INST_OFFSET, &dma_chnlstart_reg);
+
+ return ((dma_chnlstart_reg & (1 <<
+ (DSPDMAC_CHNLSTART_EN_LOBIT + dma_chan))) != 0);
+}
+
+static int dsp_dma_setup_common(struct hda_codec *codec,
+ unsigned int chip_addx,
+ unsigned int dma_chan,
+ unsigned int port_map_mask,
+ bool ovly)
+{
+ int status = 0;
+ unsigned int chnl_prop;
+ unsigned int dsp_addx;
+ unsigned int active;
+ bool code, yram;
+
+ codec_dbg(codec, "-- dsp_dma_setup_common() -- Begin ---------\n");
+
+ if (dma_chan >= DSPDMAC_DMA_CFG_CHANNEL_COUNT) {
+ codec_dbg(codec, "dma chan num invalid\n");
+ return -EINVAL;
+ }
+
+ if (dsp_is_dma_active(codec, dma_chan)) {
+ codec_dbg(codec, "dma already active\n");
+ return -EBUSY;
+ }
+
+ dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram);
+
+ if (dsp_addx == INVALID_CHIP_ADDRESS) {
+ codec_dbg(codec, "invalid chip addr\n");
+ return -ENXIO;
+ }
+
+ chnl_prop = DSPDMAC_CHNLPROP_AC_MASK;
+ active = 0;
+
+ codec_dbg(codec, " dsp_dma_setup_common() start reg pgm\n");
+
+ if (ovly) {
+ status = chipio_read(codec, DSPDMAC_CHNLPROP_INST_OFFSET,
+ &chnl_prop);
+
+ if (status < 0) {
+ codec_dbg(codec, "read CHNLPROP Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, "dsp_dma_setup_common() Read CHNLPROP\n");
+ }
+
+ if (!code)
+ chnl_prop &= ~(1 << (DSPDMAC_CHNLPROP_MSPCE_LOBIT + dma_chan));
+ else
+ chnl_prop |= (1 << (DSPDMAC_CHNLPROP_MSPCE_LOBIT + dma_chan));
+
+ chnl_prop &= ~(1 << (DSPDMAC_CHNLPROP_DCON_LOBIT + dma_chan));
+
+ status = chipio_write(codec, DSPDMAC_CHNLPROP_INST_OFFSET, chnl_prop);
+ if (status < 0) {
+ codec_dbg(codec, "write CHNLPROP Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_dma_setup_common() Write CHNLPROP\n");
+
+ if (ovly) {
+ status = chipio_read(codec, DSPDMAC_ACTIVE_INST_OFFSET,
+ &active);
+
+ if (status < 0) {
+ codec_dbg(codec, "read ACTIVE Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, "dsp_dma_setup_common() Read ACTIVE\n");
+ }
+
+ active &= (~(1 << (DSPDMAC_ACTIVE_AAR_LOBIT + dma_chan))) &
+ DSPDMAC_ACTIVE_AAR_MASK;
+
+ status = chipio_write(codec, DSPDMAC_ACTIVE_INST_OFFSET, active);
+ if (status < 0) {
+ codec_dbg(codec, "write ACTIVE Reg fail\n");
+ return status;
+ }
+
+ codec_dbg(codec, " dsp_dma_setup_common() Write ACTIVE\n");
+
+ status = chipio_write(codec, DSPDMAC_AUDCHSEL_INST_OFFSET(dma_chan),
+ port_map_mask);
+ if (status < 0) {
+ codec_dbg(codec, "write AUDCHSEL Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_dma_setup_common() Write AUDCHSEL\n");
+
+ status = chipio_write(codec, DSPDMAC_IRQCNT_INST_OFFSET(dma_chan),
+ DSPDMAC_IRQCNT_BICNT_MASK | DSPDMAC_IRQCNT_CICNT_MASK);
+ if (status < 0) {
+ codec_dbg(codec, "write IRQCNT Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_dma_setup_common() Write IRQCNT\n");
+
+ codec_dbg(codec,
+ "ChipA=0x%x,DspA=0x%x,dmaCh=%u, "
+ "CHSEL=0x%x,CHPROP=0x%x,Active=0x%x\n",
+ chip_addx, dsp_addx, dma_chan,
+ port_map_mask, chnl_prop, active);
+
+ codec_dbg(codec, "-- dsp_dma_setup_common() -- Complete ------\n");
+
+ return 0;
+}
+
+/*
+ * Setup the DSP DMA per-transfer-specific registers
+ */
+static int dsp_dma_setup(struct hda_codec *codec,
+ unsigned int chip_addx,
+ unsigned int count,
+ unsigned int dma_chan)
+{
+ int status = 0;
+ bool code, yram;
+ unsigned int dsp_addx;
+ unsigned int addr_field;
+ unsigned int incr_field;
+ unsigned int base_cnt;
+ unsigned int cur_cnt;
+ unsigned int dma_cfg = 0;
+ unsigned int adr_ofs = 0;
+ unsigned int xfr_cnt = 0;
+ const unsigned int max_dma_count = 1 << (DSPDMAC_XFRCNT_BCNT_HIBIT -
+ DSPDMAC_XFRCNT_BCNT_LOBIT + 1);
+
+ codec_dbg(codec, "-- dsp_dma_setup() -- Begin ---------\n");
+
+ if (count > max_dma_count) {
+ codec_dbg(codec, "count too big\n");
+ return -EINVAL;
+ }
+
+ dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram);
+ if (dsp_addx == INVALID_CHIP_ADDRESS) {
+ codec_dbg(codec, "invalid chip addr\n");
+ return -ENXIO;
+ }
+
+ codec_dbg(codec, " dsp_dma_setup() start reg pgm\n");
+
+ addr_field = dsp_addx << DSPDMAC_DMACFG_DBADR_LOBIT;
+ incr_field = 0;
+
+ if (!code) {
+ addr_field <<= 1;
+ if (yram)
+ addr_field |= (1 << DSPDMAC_DMACFG_DBADR_LOBIT);
+
+ incr_field = (1 << DSPDMAC_DMACFG_AINCR_LOBIT);
+ }
+
+ dma_cfg = addr_field + incr_field;
+ status = chipio_write(codec, DSPDMAC_DMACFG_INST_OFFSET(dma_chan),
+ dma_cfg);
+ if (status < 0) {
+ codec_dbg(codec, "write DMACFG Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_dma_setup() Write DMACFG\n");
+
+ adr_ofs = (count - 1) << (DSPDMAC_DSPADROFS_BOFS_LOBIT +
+ (code ? 0 : 1));
+
+ status = chipio_write(codec, DSPDMAC_DSPADROFS_INST_OFFSET(dma_chan),
+ adr_ofs);
+ if (status < 0) {
+ codec_dbg(codec, "write DSPADROFS Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_dma_setup() Write DSPADROFS\n");
+
+ base_cnt = (count - 1) << DSPDMAC_XFRCNT_BCNT_LOBIT;
+
+ cur_cnt = (count - 1) << DSPDMAC_XFRCNT_CCNT_LOBIT;
+
+ xfr_cnt = base_cnt | cur_cnt;
+
+ status = chipio_write(codec,
+ DSPDMAC_XFRCNT_INST_OFFSET(dma_chan), xfr_cnt);
+ if (status < 0) {
+ codec_dbg(codec, "write XFRCNT Reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_dma_setup() Write XFRCNT\n");
+
+ codec_dbg(codec,
+ "ChipA=0x%x, cnt=0x%x, DMACFG=0x%x, "
+ "ADROFS=0x%x, XFRCNT=0x%x\n",
+ chip_addx, count, dma_cfg, adr_ofs, xfr_cnt);
+
+ codec_dbg(codec, "-- dsp_dma_setup() -- Complete ---------\n");
+
+ return 0;
+}
+
+/*
+ * Start the DSP DMA
+ */
+static int dsp_dma_start(struct hda_codec *codec,
+ unsigned int dma_chan, bool ovly)
+{
+ unsigned int reg = 0;
+ int status = 0;
+
+ codec_dbg(codec, "-- dsp_dma_start() -- Begin ---------\n");
+
+ if (ovly) {
+ status = chipio_read(codec,
+ DSPDMAC_CHNLSTART_INST_OFFSET, &reg);
+
+ if (status < 0) {
+ codec_dbg(codec, "read CHNLSTART reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, "-- dsp_dma_start() Read CHNLSTART\n");
+
+ reg &= ~(DSPDMAC_CHNLSTART_EN_MASK |
+ DSPDMAC_CHNLSTART_DIS_MASK);
+ }
+
+ status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET,
+ reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_EN_LOBIT)));
+ if (status < 0) {
+ codec_dbg(codec, "write CHNLSTART reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, "-- dsp_dma_start() -- Complete ---------\n");
+
+ return status;
+}
+
+/*
+ * Stop the DSP DMA
+ */
+static int dsp_dma_stop(struct hda_codec *codec,
+ unsigned int dma_chan, bool ovly)
+{
+ unsigned int reg = 0;
+ int status = 0;
+
+ codec_dbg(codec, "-- dsp_dma_stop() -- Begin ---------\n");
+
+ if (ovly) {
+ status = chipio_read(codec,
+ DSPDMAC_CHNLSTART_INST_OFFSET, &reg);
+
+ if (status < 0) {
+ codec_dbg(codec, "read CHNLSTART reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, "-- dsp_dma_stop() Read CHNLSTART\n");
+ reg &= ~(DSPDMAC_CHNLSTART_EN_MASK |
+ DSPDMAC_CHNLSTART_DIS_MASK);
+ }
+
+ status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET,
+ reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_DIS_LOBIT)));
+ if (status < 0) {
+ codec_dbg(codec, "write CHNLSTART reg fail\n");
+ return status;
+ }
+ codec_dbg(codec, "-- dsp_dma_stop() -- Complete ---------\n");
+
+ return status;
+}
+
+/**
+ * dsp_allocate_router_ports - Allocate router ports
+ *
+ * @codec: the HDA codec
+ * @num_chans: number of channels in the stream
+ * @ports_per_channel: number of ports per channel
+ * @start_device: start device
+ * @port_map: pointer to the port list to hold the allocated ports
+ *
+ * Returns zero or a negative error code.
+ */
+static int dsp_allocate_router_ports(struct hda_codec *codec,
+ unsigned int num_chans,
+ unsigned int ports_per_channel,
+ unsigned int start_device,
+ unsigned int *port_map)
+{
+ int status = 0;
+ int res;
+ u8 val;
+
+ status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+ if (status < 0)
+ return status;
+
+ val = start_device << 6;
+ val |= (ports_per_channel - 1) << 4;
+ val |= num_chans - 1;
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET,
+ val);
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PORT_ALLOC_SET,
+ MEM_CONNID_DSP);
+
+ status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+ if (status < 0)
+ return status;
+
+ res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PORT_ALLOC_GET, 0);
+
+ *port_map = res;
+
+ return (res < 0) ? res : 0;
+}
+
+/*
+ * Free router ports
+ */
+static int dsp_free_router_ports(struct hda_codec *codec)
+{
+ int status = 0;
+
+ status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+ if (status < 0)
+ return status;
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PORT_FREE_SET,
+ MEM_CONNID_DSP);
+
+ status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+
+ return status;
+}
+
+/*
+ * Allocate DSP ports for the download stream
+ */
+static int dsp_allocate_ports(struct hda_codec *codec,
+ unsigned int num_chans,
+ unsigned int rate_multi, unsigned int *port_map)
+{
+ int status;
+
+ codec_dbg(codec, " dsp_allocate_ports() -- begin\n");
+
+ if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4)) {
+ codec_dbg(codec, "bad rate multiple\n");
+ return -EINVAL;
+ }
+
+ status = dsp_allocate_router_ports(codec, num_chans,
+ rate_multi, 0, port_map);
+
+ codec_dbg(codec, " dsp_allocate_ports() -- complete\n");
+
+ return status;
+}
+
+static int dsp_allocate_ports_format(struct hda_codec *codec,
+ const unsigned short fmt,
+ unsigned int *port_map)
+{
+ unsigned int num_chans;
+
+ unsigned int sample_rate_div = ((get_hdafmt_rate(fmt) >> 0) & 3) + 1;
+ unsigned int sample_rate_mul = ((get_hdafmt_rate(fmt) >> 3) & 3) + 1;
+ unsigned int rate_multi = sample_rate_mul / sample_rate_div;
+
+ if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4)) {
+ codec_dbg(codec, "bad rate multiple\n");
+ return -EINVAL;
+ }
+
+ num_chans = get_hdafmt_chs(fmt) + 1;
+
+ return dsp_allocate_ports(codec, num_chans, rate_multi, port_map);
+}
+
+/*
+ * free DSP ports
+ */
+static int dsp_free_ports(struct hda_codec *codec)
+{
+ int status;
+
+ codec_dbg(codec, " dsp_free_ports() -- begin\n");
+
+ status = dsp_free_router_ports(codec);
+ if (status < 0) {
+ codec_dbg(codec, "free router ports fail\n");
+ return status;
+ }
+ codec_dbg(codec, " dsp_free_ports() -- complete\n");
+
+ return status;
+}
+
+/*
+ * HDA DMA engine stuffs for DSP code download
+ */
+struct dma_engine {
+ struct hda_codec *codec;
+ unsigned short m_converter_format;
+ struct snd_dma_buffer *dmab;
+ unsigned int buf_size;
+};
+
+
+enum dma_state {
+ DMA_STATE_STOP = 0,
+ DMA_STATE_RUN = 1
+};
+
+static int dma_convert_to_hda_format(struct hda_codec *codec,
+ unsigned int sample_rate,
+ unsigned short channels,
+ unsigned short *hda_format)
+{
+ unsigned int format_val;
+
+ format_val = snd_hdac_stream_format(channels, 32, sample_rate);
+
+ if (hda_format)
+ *hda_format = (unsigned short)format_val;
+
+ return 0;
+}
+
+/*
+ * Reset DMA for DSP download
+ */
+static int dma_reset(struct dma_engine *dma)
+{
+ struct hda_codec *codec = dma->codec;
+ struct ca0132_spec *spec = codec->spec;
+ int status;
+
+ if (dma->dmab->area)
+ snd_hda_codec_load_dsp_cleanup(codec, dma->dmab);
+
+ status = snd_hda_codec_load_dsp_prepare(codec,
+ dma->m_converter_format,
+ dma->buf_size,
+ dma->dmab);
+ if (status < 0)
+ return status;
+ spec->dsp_stream_id = status;
+ return 0;
+}
+
+static int dma_set_state(struct dma_engine *dma, enum dma_state state)
+{
+ bool cmd;
+
+ switch (state) {
+ case DMA_STATE_STOP:
+ cmd = false;
+ break;
+ case DMA_STATE_RUN:
+ cmd = true;
+ break;
+ default:
+ return 0;
+ }
+
+ snd_hda_codec_load_dsp_trigger(dma->codec, cmd);
+ return 0;
+}
+
+static unsigned int dma_get_buffer_size(struct dma_engine *dma)
+{
+ return dma->dmab->bytes;
+}
+
+static unsigned char *dma_get_buffer_addr(struct dma_engine *dma)
+{
+ return dma->dmab->area;
+}
+
+static int dma_xfer(struct dma_engine *dma,
+ const unsigned int *data,
+ unsigned int count)
+{
+ memcpy(dma->dmab->area, data, count);
+ return 0;
+}
+
+static void dma_get_converter_format(
+ struct dma_engine *dma,
+ unsigned short *format)
+{
+ if (format)
+ *format = dma->m_converter_format;
+}
+
+static unsigned int dma_get_stream_id(struct dma_engine *dma)
+{
+ struct ca0132_spec *spec = dma->codec->spec;
+
+ return spec->dsp_stream_id;
+}
+
+struct dsp_image_seg {
+ u32 magic;
+ u32 chip_addr;
+ u32 count;
+ u32 data[];
+};
+
+static const u32 g_magic_value = 0x4c46584d;
+static const u32 g_chip_addr_magic_value = 0xFFFFFF01;
+
+static bool is_valid(const struct dsp_image_seg *p)
+{
+ return p->magic == g_magic_value;
+}
+
+static bool is_hci_prog_list_seg(const struct dsp_image_seg *p)
+{
+ return g_chip_addr_magic_value == p->chip_addr;
+}
+
+static bool is_last(const struct dsp_image_seg *p)
+{
+ return p->count == 0;
+}
+
+static size_t dsp_sizeof(const struct dsp_image_seg *p)
+{
+ return struct_size(p, data, p->count);
+}
+
+static const struct dsp_image_seg *get_next_seg_ptr(
+ const struct dsp_image_seg *p)
+{
+ return (struct dsp_image_seg *)((unsigned char *)(p) + dsp_sizeof(p));
+}
+
+/*
+ * CA0132 chip DSP transfer stuffs. For DSP download.
+ */
+#define INVALID_DMA_CHANNEL (~0U)
+
+/*
+ * Program a list of address/data pairs via the ChipIO widget.
+ * The segment data is in the format of successive pairs of words.
+ * These are repeated as indicated by the segment's count field.
+ */
+static int dspxfr_hci_write(struct hda_codec *codec,
+ const struct dsp_image_seg *fls)
+{
+ int status;
+ const u32 *data;
+ unsigned int count;
+
+ if (fls == NULL || fls->chip_addr != g_chip_addr_magic_value) {
+ codec_dbg(codec, "hci_write invalid params\n");
+ return -EINVAL;
+ }
+
+ count = fls->count;
+ data = (u32 *)(fls->data);
+ while (count >= 2) {
+ status = chipio_write(codec, data[0], data[1]);
+ if (status < 0) {
+ codec_dbg(codec, "hci_write chipio failed\n");
+ return status;
+ }
+ count -= 2;
+ data += 2;
+ }
+ return 0;
+}
+
+/**
+ * dspxfr_one_seg - Write a block of data into DSP code or data RAM using pre-allocated DMA engine.
+ *
+ * @codec: the HDA codec
+ * @fls: pointer to a fast load image
+ * @reloc: Relocation address for loading single-segment overlays, or 0 for
+ * no relocation
+ * @dma_engine: pointer to DMA engine to be used for DSP download
+ * @dma_chan: The number of DMA channels used for DSP download
+ * @port_map_mask: port mapping
+ * @ovly: TRUE if overlay format is required
+ *
+ * Returns zero or a negative error code.
+ */
+static int dspxfr_one_seg(struct hda_codec *codec,
+ const struct dsp_image_seg *fls,
+ unsigned int reloc,
+ struct dma_engine *dma_engine,
+ unsigned int dma_chan,
+ unsigned int port_map_mask,
+ bool ovly)
+{
+ int status = 0;
+ bool comm_dma_setup_done = false;
+ const unsigned int *data;
+ unsigned int chip_addx;
+ unsigned int words_to_write;
+ unsigned int buffer_size_words;
+ unsigned char *buffer_addx;
+ unsigned short hda_format;
+ unsigned int sample_rate_div;
+ unsigned int sample_rate_mul;
+ unsigned int num_chans;
+ unsigned int hda_frame_size_words;
+ unsigned int remainder_words;
+ const u32 *data_remainder;
+ u32 chip_addx_remainder;
+ unsigned int run_size_words;
+ const struct dsp_image_seg *hci_write = NULL;
+ unsigned long timeout;
+ bool dma_active;
+
+ if (fls == NULL)
+ return -EINVAL;
+ if (is_hci_prog_list_seg(fls)) {
+ hci_write = fls;
+ fls = get_next_seg_ptr(fls);
+ }
+
+ if (hci_write && (!fls || is_last(fls))) {
+ codec_dbg(codec, "hci_write\n");
+ return dspxfr_hci_write(codec, hci_write);
+ }
+
+ if (fls == NULL || dma_engine == NULL || port_map_mask == 0) {
+ codec_dbg(codec, "Invalid Params\n");
+ return -EINVAL;
+ }
+
+ data = fls->data;
+ chip_addx = fls->chip_addr;
+ words_to_write = fls->count;
+
+ if (!words_to_write)
+ return hci_write ? dspxfr_hci_write(codec, hci_write) : 0;
+ if (reloc)
+ chip_addx = (chip_addx & (0xFFFF0000 << 2)) + (reloc << 2);
+
+ if (!UC_RANGE(chip_addx, words_to_write) &&
+ !X_RANGE_ALL(chip_addx, words_to_write) &&
+ !Y_RANGE_ALL(chip_addx, words_to_write)) {
+ codec_dbg(codec, "Invalid chip_addx Params\n");
+ return -EINVAL;
+ }
+
+ buffer_size_words = (unsigned int)dma_get_buffer_size(dma_engine) /
+ sizeof(u32);
+
+ buffer_addx = dma_get_buffer_addr(dma_engine);
+
+ if (buffer_addx == NULL) {
+ codec_dbg(codec, "dma_engine buffer NULL\n");
+ return -EINVAL;
+ }
+
+ dma_get_converter_format(dma_engine, &hda_format);
+ sample_rate_div = ((get_hdafmt_rate(hda_format) >> 0) & 3) + 1;
+ sample_rate_mul = ((get_hdafmt_rate(hda_format) >> 3) & 3) + 1;
+ num_chans = get_hdafmt_chs(hda_format) + 1;
+
+ hda_frame_size_words = ((sample_rate_div == 0) ? 0 :
+ (num_chans * sample_rate_mul / sample_rate_div));
+
+ if (hda_frame_size_words == 0) {
+ codec_dbg(codec, "frmsz zero\n");
+ return -EINVAL;
+ }
+
+ buffer_size_words = min(buffer_size_words,
+ (unsigned int)(UC_RANGE(chip_addx, 1) ?
+ 65536 : 32768));
+ buffer_size_words -= buffer_size_words % hda_frame_size_words;
+ codec_dbg(codec,
+ "chpadr=0x%08x frmsz=%u nchan=%u "
+ "rate_mul=%u div=%u bufsz=%u\n",
+ chip_addx, hda_frame_size_words, num_chans,
+ sample_rate_mul, sample_rate_div, buffer_size_words);
+
+ if (buffer_size_words < hda_frame_size_words) {
+ codec_dbg(codec, "dspxfr_one_seg:failed\n");
+ return -EINVAL;
+ }
+
+ remainder_words = words_to_write % hda_frame_size_words;
+ data_remainder = data;
+ chip_addx_remainder = chip_addx;
+
+ data += remainder_words;
+ chip_addx += remainder_words*sizeof(u32);
+ words_to_write -= remainder_words;
+
+ while (words_to_write != 0) {
+ run_size_words = min(buffer_size_words, words_to_write);
+ codec_dbg(codec, "dspxfr (seg loop)cnt=%u rs=%u remainder=%u\n",
+ words_to_write, run_size_words, remainder_words);
+ dma_xfer(dma_engine, data, run_size_words*sizeof(u32));
+ if (!comm_dma_setup_done) {
+ status = dsp_dma_stop(codec, dma_chan, ovly);
+ if (status < 0)
+ return status;
+ status = dsp_dma_setup_common(codec, chip_addx,
+ dma_chan, port_map_mask, ovly);
+ if (status < 0)
+ return status;
+ comm_dma_setup_done = true;
+ }
+
+ status = dsp_dma_setup(codec, chip_addx,
+ run_size_words, dma_chan);
+ if (status < 0)
+ return status;
+ status = dsp_dma_start(codec, dma_chan, ovly);
+ if (status < 0)
+ return status;
+ if (!dsp_is_dma_active(codec, dma_chan)) {
+ codec_dbg(codec, "dspxfr:DMA did not start\n");
+ return -EIO;
+ }
+ status = dma_set_state(dma_engine, DMA_STATE_RUN);
+ if (status < 0)
+ return status;
+ if (remainder_words != 0) {
+ status = chipio_write_multiple(codec,
+ chip_addx_remainder,
+ data_remainder,
+ remainder_words);
+ if (status < 0)
+ return status;
+ remainder_words = 0;
+ }
+ if (hci_write) {
+ status = dspxfr_hci_write(codec, hci_write);
+ if (status < 0)
+ return status;
+ hci_write = NULL;
+ }
+
+ timeout = jiffies + msecs_to_jiffies(2000);
+ do {
+ dma_active = dsp_is_dma_active(codec, dma_chan);
+ if (!dma_active)
+ break;
+ msleep(20);
+ } while (time_before(jiffies, timeout));
+ if (dma_active)
+ break;
+
+ codec_dbg(codec, "+++++ DMA complete\n");
+ dma_set_state(dma_engine, DMA_STATE_STOP);
+ status = dma_reset(dma_engine);
+
+ if (status < 0)
+ return status;
+
+ data += run_size_words;
+ chip_addx += run_size_words*sizeof(u32);
+ words_to_write -= run_size_words;
+ }
+
+ if (remainder_words != 0) {
+ status = chipio_write_multiple(codec, chip_addx_remainder,
+ data_remainder, remainder_words);
+ }
+
+ return status;
+}
+
+/**
+ * dspxfr_image - Write the entire DSP image of a DSP code/data overlay to DSP memories
+ *
+ * @codec: the HDA codec
+ * @fls_data: pointer to a fast load image
+ * @reloc: Relocation address for loading single-segment overlays, or 0 for
+ * no relocation
+ * @sample_rate: sampling rate of the stream used for DSP download
+ * @channels: channels of the stream used for DSP download
+ * @ovly: TRUE if overlay format is required
+ *
+ * Returns zero or a negative error code.
+ */
+static int dspxfr_image(struct hda_codec *codec,
+ const struct dsp_image_seg *fls_data,
+ unsigned int reloc,
+ unsigned int sample_rate,
+ unsigned short channels,
+ bool ovly)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int status;
+ unsigned short hda_format = 0;
+ unsigned int response;
+ unsigned char stream_id = 0;
+ struct dma_engine *dma_engine;
+ unsigned int dma_chan;
+ unsigned int port_map_mask;
+
+ if (fls_data == NULL)
+ return -EINVAL;
+
+ dma_engine = kzalloc(sizeof(*dma_engine), GFP_KERNEL);
+ if (!dma_engine)
+ return -ENOMEM;
+
+ dma_engine->dmab = kzalloc(sizeof(*dma_engine->dmab), GFP_KERNEL);
+ if (!dma_engine->dmab) {
+ kfree(dma_engine);
+ return -ENOMEM;
+ }
+
+ dma_engine->codec = codec;
+ dma_convert_to_hda_format(codec, sample_rate, channels, &hda_format);
+ dma_engine->m_converter_format = hda_format;
+ dma_engine->buf_size = (ovly ? DSP_DMA_WRITE_BUFLEN_OVLY :
+ DSP_DMA_WRITE_BUFLEN_INIT) * 2;
+
+ dma_chan = ovly ? INVALID_DMA_CHANNEL : 0;
+
+ status = codec_set_converter_format(codec, WIDGET_CHIP_CTRL,
+ hda_format, &response);
+
+ if (status < 0) {
+ codec_dbg(codec, "set converter format fail\n");
+ goto exit;
+ }
+
+ status = snd_hda_codec_load_dsp_prepare(codec,
+ dma_engine->m_converter_format,
+ dma_engine->buf_size,
+ dma_engine->dmab);
+ if (status < 0)
+ goto exit;
+ spec->dsp_stream_id = status;
+
+ if (ovly) {
+ status = dspio_alloc_dma_chan(codec, &dma_chan);
+ if (status < 0) {
+ codec_dbg(codec, "alloc dmachan fail\n");
+ dma_chan = INVALID_DMA_CHANNEL;
+ goto exit;
+ }
+ }
+
+ port_map_mask = 0;
+ status = dsp_allocate_ports_format(codec, hda_format,
+ &port_map_mask);
+ if (status < 0) {
+ codec_dbg(codec, "alloc ports fail\n");
+ goto exit;
+ }
+
+ stream_id = dma_get_stream_id(dma_engine);
+ status = codec_set_converter_stream_channel(codec,
+ WIDGET_CHIP_CTRL, stream_id, 0, &response);
+ if (status < 0) {
+ codec_dbg(codec, "set stream chan fail\n");
+ goto exit;
+ }
+
+ while ((fls_data != NULL) && !is_last(fls_data)) {
+ if (!is_valid(fls_data)) {
+ codec_dbg(codec, "FLS check fail\n");
+ status = -EINVAL;
+ goto exit;
+ }
+ status = dspxfr_one_seg(codec, fls_data, reloc,
+ dma_engine, dma_chan,
+ port_map_mask, ovly);
+ if (status < 0)
+ break;
+
+ if (is_hci_prog_list_seg(fls_data))
+ fls_data = get_next_seg_ptr(fls_data);
+
+ if ((fls_data != NULL) && !is_last(fls_data))
+ fls_data = get_next_seg_ptr(fls_data);
+ }
+
+ if (port_map_mask != 0)
+ status = dsp_free_ports(codec);
+
+ if (status < 0)
+ goto exit;
+
+ status = codec_set_converter_stream_channel(codec,
+ WIDGET_CHIP_CTRL, 0, 0, &response);
+
+exit:
+ if (ovly && (dma_chan != INVALID_DMA_CHANNEL))
+ dspio_free_dma_chan(codec, dma_chan);
+
+ if (dma_engine->dmab->area)
+ snd_hda_codec_load_dsp_cleanup(codec, dma_engine->dmab);
+ kfree(dma_engine->dmab);
+ kfree(dma_engine);
+
+ return status;
+}
+
+/*
+ * CA0132 DSP download stuffs.
+ */
+static void dspload_post_setup(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ codec_dbg(codec, "---- dspload_post_setup ------\n");
+ if (!ca0132_use_alt_functions(spec)) {
+ /*set DSP speaker to 2.0 configuration*/
+ chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x18), 0x08080080);
+ chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x19), 0x3f800000);
+
+ /*update write pointer*/
+ chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x29), 0x00000002);
+ }
+}
+
+/**
+ * dspload_image - Download DSP from a DSP Image Fast Load structure.
+ *
+ * @codec: the HDA codec
+ * @fls: pointer to a fast load image
+ * @ovly: TRUE if overlay format is required
+ * @reloc: Relocation address for loading single-segment overlays, or 0 for
+ * no relocation
+ * @autostart: TRUE if DSP starts after loading; ignored if ovly is TRUE
+ * @router_chans: number of audio router channels to be allocated (0 means use
+ * internal defaults; max is 32)
+ *
+ * Download DSP from a DSP Image Fast Load structure. This structure is a
+ * linear, non-constant sized element array of structures, each of which
+ * contain the count of the data to be loaded, the data itself, and the
+ * corresponding starting chip address of the starting data location.
+ * Returns zero or a negative error code.
+ */
+static int dspload_image(struct hda_codec *codec,
+ const struct dsp_image_seg *fls,
+ bool ovly,
+ unsigned int reloc,
+ bool autostart,
+ int router_chans)
+{
+ int status = 0;
+ unsigned int sample_rate;
+ unsigned short channels;
+
+ codec_dbg(codec, "---- dspload_image begin ------\n");
+ if (router_chans == 0) {
+ if (!ovly)
+ router_chans = DMA_TRANSFER_FRAME_SIZE_NWORDS;
+ else
+ router_chans = DMA_OVERLAY_FRAME_SIZE_NWORDS;
+ }
+
+ sample_rate = 48000;
+ channels = (unsigned short)router_chans;
+
+ while (channels > 16) {
+ sample_rate *= 2;
+ channels /= 2;
+ }
+
+ do {
+ codec_dbg(codec, "Ready to program DMA\n");
+ if (!ovly)
+ status = dsp_reset(codec);
+
+ if (status < 0)
+ break;
+
+ codec_dbg(codec, "dsp_reset() complete\n");
+ status = dspxfr_image(codec, fls, reloc, sample_rate, channels,
+ ovly);
+
+ if (status < 0)
+ break;
+
+ codec_dbg(codec, "dspxfr_image() complete\n");
+ if (autostart && !ovly) {
+ dspload_post_setup(codec);
+ status = dsp_set_run_state(codec);
+ }
+
+ codec_dbg(codec, "LOAD FINISHED\n");
+ } while (0);
+
+ return status;
+}
+
+#ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP
+static bool dspload_is_loaded(struct hda_codec *codec)
+{
+ unsigned int data = 0;
+ int status = 0;
+
+ status = chipio_read(codec, 0x40004, &data);
+ if ((status < 0) || (data != 1))
+ return false;
+
+ return true;
+}
+#else
+#define dspload_is_loaded(codec) false
+#endif
+
+static bool dspload_wait_loaded(struct hda_codec *codec)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(2000);
+
+ do {
+ if (dspload_is_loaded(codec)) {
+ codec_info(codec, "ca0132 DSP downloaded and running\n");
+ return true;
+ }
+ msleep(20);
+ } while (time_before(jiffies, timeout));
+
+ codec_err(codec, "ca0132 failed to download DSP\n");
+ return false;
+}
+
+/*
+ * ca0113 related functions. The ca0113 acts as the HDA bus for the pci-e
+ * based cards, and has a second mmio region, region2, that's used for special
+ * commands.
+ */
+
+/*
+ * For cards with PCI-E region2 (Sound Blaster Z/ZxR, Recon3D, and AE-5)
+ * the mmio address 0x320 is used to set GPIO pins. The format for the data
+ * The first eight bits are just the number of the pin. So far, I've only seen
+ * this number go to 7.
+ * AE-5 note: The AE-5 seems to use pins 2 and 3 to somehow set the color value
+ * of the on-card LED. It seems to use pin 2 for data, then toggles 3 to on and
+ * then off to send that bit.
+ */
+static void ca0113_mmio_gpio_set(struct hda_codec *codec, unsigned int gpio_pin,
+ bool enable)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned short gpio_data;
+
+ gpio_data = gpio_pin & 0xF;
+ gpio_data |= ((enable << 8) & 0x100);
+
+ writew(gpio_data, spec->mem_base + 0x320);
+}
+
+/*
+ * Special pci region2 commands that are only used by the AE-5. They follow
+ * a set format, and require reads at certain points to seemingly 'clear'
+ * the response data. My first tests didn't do these reads, and would cause
+ * the card to get locked up until the memory was read. These commands
+ * seem to work with three distinct values that I've taken to calling group,
+ * target-id, and value.
+ */
+static void ca0113_mmio_command_set(struct hda_codec *codec, unsigned int group,
+ unsigned int target, unsigned int value)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int write_val;
+
+ writel(0x0000007e, spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+ writel(0x0000005a, spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+
+ writel(0x00800005, spec->mem_base + 0x20c);
+ writel(group, spec->mem_base + 0x804);
+
+ writel(0x00800005, spec->mem_base + 0x20c);
+ write_val = (target & 0xff);
+ write_val |= (value << 8);
+
+
+ writel(write_val, spec->mem_base + 0x204);
+ /*
+ * Need delay here or else it goes too fast and works inconsistently.
+ */
+ msleep(20);
+
+ readl(spec->mem_base + 0x860);
+ readl(spec->mem_base + 0x854);
+ readl(spec->mem_base + 0x840);
+
+ writel(0x00800004, spec->mem_base + 0x20c);
+ writel(0x00000000, spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+}
+
+/*
+ * This second type of command is used for setting the sound filter type.
+ */
+static void ca0113_mmio_command_set_type2(struct hda_codec *codec,
+ unsigned int group, unsigned int target, unsigned int value)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int write_val;
+
+ writel(0x0000007e, spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+ writel(0x0000005a, spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+
+ writel(0x00800003, spec->mem_base + 0x20c);
+ writel(group, spec->mem_base + 0x804);
+
+ writel(0x00800005, spec->mem_base + 0x20c);
+ write_val = (target & 0xff);
+ write_val |= (value << 8);
+
+
+ writel(write_val, spec->mem_base + 0x204);
+ msleep(20);
+ readl(spec->mem_base + 0x860);
+ readl(spec->mem_base + 0x854);
+ readl(spec->mem_base + 0x840);
+
+ writel(0x00800004, spec->mem_base + 0x20c);
+ writel(0x00000000, spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+ readl(spec->mem_base + 0x210);
+}
+
+/*
+ * Setup GPIO for the other variants of Core3D.
+ */
+
+/*
+ * Sets up the GPIO pins so that they are discoverable. If this isn't done,
+ * the card shows as having no GPIO pins.
+ */
+static void ca0132_gpio_init(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ case QUIRK_AE5:
+ case QUIRK_AE7:
+ snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00);
+ snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53);
+ snd_hda_codec_write(codec, 0x01, 0, 0x790, 0x23);
+ break;
+ case QUIRK_R3DI:
+ snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00);
+ snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x5B);
+ break;
+ default:
+ break;
+ }
+
+}
+
+/* Sets the GPIO for audio output. */
+static void ca0132_gpio_setup(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DIRECTION, 0x07);
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_MASK, 0x07);
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, 0x04);
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, 0x06);
+ break;
+ case QUIRK_R3DI:
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DIRECTION, 0x1E);
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_MASK, 0x1F);
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, 0x0C);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * GPIO control functions for the Recon3D integrated.
+ */
+
+enum r3di_gpio_bit {
+ /* Bit 1 - Switch between front/rear mic. 0 = rear, 1 = front */
+ R3DI_MIC_SELECT_BIT = 1,
+ /* Bit 2 - Switch between headphone/line out. 0 = Headphone, 1 = Line */
+ R3DI_OUT_SELECT_BIT = 2,
+ /*
+ * I dunno what this actually does, but it stays on until the dsp
+ * is downloaded.
+ */
+ R3DI_GPIO_DSP_DOWNLOADING = 3,
+ /*
+ * Same as above, no clue what it does, but it comes on after the dsp
+ * is downloaded.
+ */
+ R3DI_GPIO_DSP_DOWNLOADED = 4
+};
+
+enum r3di_mic_select {
+ /* Set GPIO bit 1 to 0 for rear mic */
+ R3DI_REAR_MIC = 0,
+ /* Set GPIO bit 1 to 1 for front microphone*/
+ R3DI_FRONT_MIC = 1
+};
+
+enum r3di_out_select {
+ /* Set GPIO bit 2 to 0 for headphone */
+ R3DI_HEADPHONE_OUT = 0,
+ /* Set GPIO bit 2 to 1 for speaker */
+ R3DI_LINE_OUT = 1
+};
+enum r3di_dsp_status {
+ /* Set GPIO bit 3 to 1 until DSP is downloaded */
+ R3DI_DSP_DOWNLOADING = 0,
+ /* Set GPIO bit 4 to 1 once DSP is downloaded */
+ R3DI_DSP_DOWNLOADED = 1
+};
+
+
+static void r3di_gpio_mic_set(struct hda_codec *codec,
+ enum r3di_mic_select cur_mic)
+{
+ unsigned int cur_gpio;
+
+ /* Get the current GPIO Data setup */
+ cur_gpio = snd_hda_codec_read(codec, 0x01, 0, AC_VERB_GET_GPIO_DATA, 0);
+
+ switch (cur_mic) {
+ case R3DI_REAR_MIC:
+ cur_gpio &= ~(1 << R3DI_MIC_SELECT_BIT);
+ break;
+ case R3DI_FRONT_MIC:
+ cur_gpio |= (1 << R3DI_MIC_SELECT_BIT);
+ break;
+ }
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_DATA, cur_gpio);
+}
+
+static void r3di_gpio_dsp_status_set(struct hda_codec *codec,
+ enum r3di_dsp_status dsp_status)
+{
+ unsigned int cur_gpio;
+
+ /* Get the current GPIO Data setup */
+ cur_gpio = snd_hda_codec_read(codec, 0x01, 0, AC_VERB_GET_GPIO_DATA, 0);
+
+ switch (dsp_status) {
+ case R3DI_DSP_DOWNLOADING:
+ cur_gpio |= (1 << R3DI_GPIO_DSP_DOWNLOADING);
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_DATA, cur_gpio);
+ break;
+ case R3DI_DSP_DOWNLOADED:
+ /* Set DOWNLOADING bit to 0. */
+ cur_gpio &= ~(1 << R3DI_GPIO_DSP_DOWNLOADING);
+
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_DATA, cur_gpio);
+
+ cur_gpio |= (1 << R3DI_GPIO_DSP_DOWNLOADED);
+ break;
+ }
+
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_DATA, cur_gpio);
+}
+
+/*
+ * PCM callbacks
+ */
+static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ snd_hda_codec_setup_stream(codec, spec->dacs[0], stream_tag, 0, format);
+
+ return 0;
+}
+
+static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (spec->dsp_state == DSP_DOWNLOADING)
+ return 0;
+
+ /*If Playback effects are on, allow stream some time to flush
+ *effects tail*/
+ if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
+ msleep(50);
+
+ snd_hda_codec_cleanup_stream(codec, spec->dacs[0]);
+
+ return 0;
+}
+
+static unsigned int ca0132_playback_pcm_delay(struct hda_pcm_stream *info,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int latency = DSP_PLAYBACK_INIT_LATENCY;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return 0;
+
+ /* Add latency if playback enhancement and either effect is enabled. */
+ if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) {
+ if ((spec->effects_switch[SURROUND - EFFECT_START_NID]) ||
+ (spec->effects_switch[DIALOG_PLUS - EFFECT_START_NID]))
+ latency += DSP_PLAY_ENHANCEMENT_LATENCY;
+ }
+
+ /* Applying Speaker EQ adds latency as well. */
+ if (spec->cur_out_type == SPEAKER_OUT)
+ latency += DSP_SPEAKER_OUT_LATENCY;
+
+ return (latency * runtime->rate) / 1000;
+}
+
+/*
+ * Digital out
+ */
+static int ca0132_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int ca0132_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static int ca0132_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
+}
+
+static int ca0132_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int ca0132_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_setup_stream(codec, hinfo->nid,
+ stream_tag, 0, format);
+
+ return 0;
+}
+
+static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (spec->dsp_state == DSP_DOWNLOADING)
+ return 0;
+
+ snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+ return 0;
+}
+
+static unsigned int ca0132_capture_pcm_delay(struct hda_pcm_stream *info,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int latency = DSP_CAPTURE_INIT_LATENCY;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return 0;
+
+ if (spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID])
+ latency += DSP_CRYSTAL_VOICE_LATENCY;
+
+ return (latency * runtime->rate) / 1000;
+}
+
+/*
+ * Controls stuffs.
+ */
+
+/*
+ * Mixer controls helpers.
+ */
+#define CA0132_CODEC_VOL_MONO(xname, nid, channel, dir) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .subdevice = HDA_SUBDEV_AMP_FLAG, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
+ .info = ca0132_volume_info, \
+ .get = ca0132_volume_get, \
+ .put = ca0132_volume_put, \
+ .tlv = { .c = ca0132_volume_tlv }, \
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
+
+/*
+ * Creates a mixer control that uses defaults of HDA_CODEC_VOL except for the
+ * volume put, which is used for setting the DSP volume. This was done because
+ * the ca0132 functions were taking too much time and causing lag.
+ */
+#define CA0132_ALT_CODEC_VOL_MONO(xname, nid, channel, dir) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .subdevice = HDA_SUBDEV_AMP_FLAG, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
+ .info = snd_hda_mixer_amp_volume_info, \
+ .get = snd_hda_mixer_amp_volume_get, \
+ .put = ca0132_alt_volume_put, \
+ .tlv = { .c = snd_hda_mixer_amp_tlv }, \
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
+
+#define CA0132_CODEC_MUTE_MONO(xname, nid, channel, dir) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .subdevice = HDA_SUBDEV_AMP_FLAG, \
+ .info = snd_hda_mixer_amp_switch_info, \
+ .get = ca0132_switch_get, \
+ .put = ca0132_switch_put, \
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
+
+/* stereo */
+#define CA0132_CODEC_VOL(xname, nid, dir) \
+ CA0132_CODEC_VOL_MONO(xname, nid, 3, dir)
+#define CA0132_ALT_CODEC_VOL(xname, nid, dir) \
+ CA0132_ALT_CODEC_VOL_MONO(xname, nid, 3, dir)
+#define CA0132_CODEC_MUTE(xname, nid, dir) \
+ CA0132_CODEC_MUTE_MONO(xname, nid, 3, dir)
+
+/* lookup tables */
+/*
+ * Lookup table with decibel values for the DSP. When volume is changed in
+ * Windows, the DSP is also sent the dB value in floating point. In Windows,
+ * these values have decimal points, probably because the Windows driver
+ * actually uses floating point. We can't here, so I made a lookup table of
+ * values -90 to 9. -90 is the lowest decibel value for both the ADC's and the
+ * DAC's, and 9 is the maximum.
+ */
+static const unsigned int float_vol_db_lookup[] = {
+0xC2B40000, 0xC2B20000, 0xC2B00000, 0xC2AE0000, 0xC2AC0000, 0xC2AA0000,
+0xC2A80000, 0xC2A60000, 0xC2A40000, 0xC2A20000, 0xC2A00000, 0xC29E0000,
+0xC29C0000, 0xC29A0000, 0xC2980000, 0xC2960000, 0xC2940000, 0xC2920000,
+0xC2900000, 0xC28E0000, 0xC28C0000, 0xC28A0000, 0xC2880000, 0xC2860000,
+0xC2840000, 0xC2820000, 0xC2800000, 0xC27C0000, 0xC2780000, 0xC2740000,
+0xC2700000, 0xC26C0000, 0xC2680000, 0xC2640000, 0xC2600000, 0xC25C0000,
+0xC2580000, 0xC2540000, 0xC2500000, 0xC24C0000, 0xC2480000, 0xC2440000,
+0xC2400000, 0xC23C0000, 0xC2380000, 0xC2340000, 0xC2300000, 0xC22C0000,
+0xC2280000, 0xC2240000, 0xC2200000, 0xC21C0000, 0xC2180000, 0xC2140000,
+0xC2100000, 0xC20C0000, 0xC2080000, 0xC2040000, 0xC2000000, 0xC1F80000,
+0xC1F00000, 0xC1E80000, 0xC1E00000, 0xC1D80000, 0xC1D00000, 0xC1C80000,
+0xC1C00000, 0xC1B80000, 0xC1B00000, 0xC1A80000, 0xC1A00000, 0xC1980000,
+0xC1900000, 0xC1880000, 0xC1800000, 0xC1700000, 0xC1600000, 0xC1500000,
+0xC1400000, 0xC1300000, 0xC1200000, 0xC1100000, 0xC1000000, 0xC0E00000,
+0xC0C00000, 0xC0A00000, 0xC0800000, 0xC0400000, 0xC0000000, 0xBF800000,
+0x00000000, 0x3F800000, 0x40000000, 0x40400000, 0x40800000, 0x40A00000,
+0x40C00000, 0x40E00000, 0x41000000, 0x41100000
+};
+
+/*
+ * This table counts from float 0 to 1 in increments of .01, which is
+ * useful for a few different sliders.
+ */
+static const unsigned int float_zero_to_one_lookup[] = {
+0x00000000, 0x3C23D70A, 0x3CA3D70A, 0x3CF5C28F, 0x3D23D70A, 0x3D4CCCCD,
+0x3D75C28F, 0x3D8F5C29, 0x3DA3D70A, 0x3DB851EC, 0x3DCCCCCD, 0x3DE147AE,
+0x3DF5C28F, 0x3E051EB8, 0x3E0F5C29, 0x3E19999A, 0x3E23D70A, 0x3E2E147B,
+0x3E3851EC, 0x3E428F5C, 0x3E4CCCCD, 0x3E570A3D, 0x3E6147AE, 0x3E6B851F,
+0x3E75C28F, 0x3E800000, 0x3E851EB8, 0x3E8A3D71, 0x3E8F5C29, 0x3E947AE1,
+0x3E99999A, 0x3E9EB852, 0x3EA3D70A, 0x3EA8F5C3, 0x3EAE147B, 0x3EB33333,
+0x3EB851EC, 0x3EBD70A4, 0x3EC28F5C, 0x3EC7AE14, 0x3ECCCCCD, 0x3ED1EB85,
+0x3ED70A3D, 0x3EDC28F6, 0x3EE147AE, 0x3EE66666, 0x3EEB851F, 0x3EF0A3D7,
+0x3EF5C28F, 0x3EFAE148, 0x3F000000, 0x3F028F5C, 0x3F051EB8, 0x3F07AE14,
+0x3F0A3D71, 0x3F0CCCCD, 0x3F0F5C29, 0x3F11EB85, 0x3F147AE1, 0x3F170A3D,
+0x3F19999A, 0x3F1C28F6, 0x3F1EB852, 0x3F2147AE, 0x3F23D70A, 0x3F266666,
+0x3F28F5C3, 0x3F2B851F, 0x3F2E147B, 0x3F30A3D7, 0x3F333333, 0x3F35C28F,
+0x3F3851EC, 0x3F3AE148, 0x3F3D70A4, 0x3F400000, 0x3F428F5C, 0x3F451EB8,
+0x3F47AE14, 0x3F4A3D71, 0x3F4CCCCD, 0x3F4F5C29, 0x3F51EB85, 0x3F547AE1,
+0x3F570A3D, 0x3F59999A, 0x3F5C28F6, 0x3F5EB852, 0x3F6147AE, 0x3F63D70A,
+0x3F666666, 0x3F68F5C3, 0x3F6B851F, 0x3F6E147B, 0x3F70A3D7, 0x3F733333,
+0x3F75C28F, 0x3F7851EC, 0x3F7AE148, 0x3F7D70A4, 0x3F800000
+};
+
+/*
+ * This table counts from float 10 to 1000, which is the range of the x-bass
+ * crossover slider in Windows.
+ */
+static const unsigned int float_xbass_xover_lookup[] = {
+0x41200000, 0x41A00000, 0x41F00000, 0x42200000, 0x42480000, 0x42700000,
+0x428C0000, 0x42A00000, 0x42B40000, 0x42C80000, 0x42DC0000, 0x42F00000,
+0x43020000, 0x430C0000, 0x43160000, 0x43200000, 0x432A0000, 0x43340000,
+0x433E0000, 0x43480000, 0x43520000, 0x435C0000, 0x43660000, 0x43700000,
+0x437A0000, 0x43820000, 0x43870000, 0x438C0000, 0x43910000, 0x43960000,
+0x439B0000, 0x43A00000, 0x43A50000, 0x43AA0000, 0x43AF0000, 0x43B40000,
+0x43B90000, 0x43BE0000, 0x43C30000, 0x43C80000, 0x43CD0000, 0x43D20000,
+0x43D70000, 0x43DC0000, 0x43E10000, 0x43E60000, 0x43EB0000, 0x43F00000,
+0x43F50000, 0x43FA0000, 0x43FF0000, 0x44020000, 0x44048000, 0x44070000,
+0x44098000, 0x440C0000, 0x440E8000, 0x44110000, 0x44138000, 0x44160000,
+0x44188000, 0x441B0000, 0x441D8000, 0x44200000, 0x44228000, 0x44250000,
+0x44278000, 0x442A0000, 0x442C8000, 0x442F0000, 0x44318000, 0x44340000,
+0x44368000, 0x44390000, 0x443B8000, 0x443E0000, 0x44408000, 0x44430000,
+0x44458000, 0x44480000, 0x444A8000, 0x444D0000, 0x444F8000, 0x44520000,
+0x44548000, 0x44570000, 0x44598000, 0x445C0000, 0x445E8000, 0x44610000,
+0x44638000, 0x44660000, 0x44688000, 0x446B0000, 0x446D8000, 0x44700000,
+0x44728000, 0x44750000, 0x44778000, 0x447A0000
+};
+
+/* The following are for tuning of products */
+#ifdef ENABLE_TUNING_CONTROLS
+
+static const unsigned int voice_focus_vals_lookup[] = {
+0x41A00000, 0x41A80000, 0x41B00000, 0x41B80000, 0x41C00000, 0x41C80000,
+0x41D00000, 0x41D80000, 0x41E00000, 0x41E80000, 0x41F00000, 0x41F80000,
+0x42000000, 0x42040000, 0x42080000, 0x420C0000, 0x42100000, 0x42140000,
+0x42180000, 0x421C0000, 0x42200000, 0x42240000, 0x42280000, 0x422C0000,
+0x42300000, 0x42340000, 0x42380000, 0x423C0000, 0x42400000, 0x42440000,
+0x42480000, 0x424C0000, 0x42500000, 0x42540000, 0x42580000, 0x425C0000,
+0x42600000, 0x42640000, 0x42680000, 0x426C0000, 0x42700000, 0x42740000,
+0x42780000, 0x427C0000, 0x42800000, 0x42820000, 0x42840000, 0x42860000,
+0x42880000, 0x428A0000, 0x428C0000, 0x428E0000, 0x42900000, 0x42920000,
+0x42940000, 0x42960000, 0x42980000, 0x429A0000, 0x429C0000, 0x429E0000,
+0x42A00000, 0x42A20000, 0x42A40000, 0x42A60000, 0x42A80000, 0x42AA0000,
+0x42AC0000, 0x42AE0000, 0x42B00000, 0x42B20000, 0x42B40000, 0x42B60000,
+0x42B80000, 0x42BA0000, 0x42BC0000, 0x42BE0000, 0x42C00000, 0x42C20000,
+0x42C40000, 0x42C60000, 0x42C80000, 0x42CA0000, 0x42CC0000, 0x42CE0000,
+0x42D00000, 0x42D20000, 0x42D40000, 0x42D60000, 0x42D80000, 0x42DA0000,
+0x42DC0000, 0x42DE0000, 0x42E00000, 0x42E20000, 0x42E40000, 0x42E60000,
+0x42E80000, 0x42EA0000, 0x42EC0000, 0x42EE0000, 0x42F00000, 0x42F20000,
+0x42F40000, 0x42F60000, 0x42F80000, 0x42FA0000, 0x42FC0000, 0x42FE0000,
+0x43000000, 0x43010000, 0x43020000, 0x43030000, 0x43040000, 0x43050000,
+0x43060000, 0x43070000, 0x43080000, 0x43090000, 0x430A0000, 0x430B0000,
+0x430C0000, 0x430D0000, 0x430E0000, 0x430F0000, 0x43100000, 0x43110000,
+0x43120000, 0x43130000, 0x43140000, 0x43150000, 0x43160000, 0x43170000,
+0x43180000, 0x43190000, 0x431A0000, 0x431B0000, 0x431C0000, 0x431D0000,
+0x431E0000, 0x431F0000, 0x43200000, 0x43210000, 0x43220000, 0x43230000,
+0x43240000, 0x43250000, 0x43260000, 0x43270000, 0x43280000, 0x43290000,
+0x432A0000, 0x432B0000, 0x432C0000, 0x432D0000, 0x432E0000, 0x432F0000,
+0x43300000, 0x43310000, 0x43320000, 0x43330000, 0x43340000
+};
+
+static const unsigned int mic_svm_vals_lookup[] = {
+0x00000000, 0x3C23D70A, 0x3CA3D70A, 0x3CF5C28F, 0x3D23D70A, 0x3D4CCCCD,
+0x3D75C28F, 0x3D8F5C29, 0x3DA3D70A, 0x3DB851EC, 0x3DCCCCCD, 0x3DE147AE,
+0x3DF5C28F, 0x3E051EB8, 0x3E0F5C29, 0x3E19999A, 0x3E23D70A, 0x3E2E147B,
+0x3E3851EC, 0x3E428F5C, 0x3E4CCCCD, 0x3E570A3D, 0x3E6147AE, 0x3E6B851F,
+0x3E75C28F, 0x3E800000, 0x3E851EB8, 0x3E8A3D71, 0x3E8F5C29, 0x3E947AE1,
+0x3E99999A, 0x3E9EB852, 0x3EA3D70A, 0x3EA8F5C3, 0x3EAE147B, 0x3EB33333,
+0x3EB851EC, 0x3EBD70A4, 0x3EC28F5C, 0x3EC7AE14, 0x3ECCCCCD, 0x3ED1EB85,
+0x3ED70A3D, 0x3EDC28F6, 0x3EE147AE, 0x3EE66666, 0x3EEB851F, 0x3EF0A3D7,
+0x3EF5C28F, 0x3EFAE148, 0x3F000000, 0x3F028F5C, 0x3F051EB8, 0x3F07AE14,
+0x3F0A3D71, 0x3F0CCCCD, 0x3F0F5C29, 0x3F11EB85, 0x3F147AE1, 0x3F170A3D,
+0x3F19999A, 0x3F1C28F6, 0x3F1EB852, 0x3F2147AE, 0x3F23D70A, 0x3F266666,
+0x3F28F5C3, 0x3F2B851F, 0x3F2E147B, 0x3F30A3D7, 0x3F333333, 0x3F35C28F,
+0x3F3851EC, 0x3F3AE148, 0x3F3D70A4, 0x3F400000, 0x3F428F5C, 0x3F451EB8,
+0x3F47AE14, 0x3F4A3D71, 0x3F4CCCCD, 0x3F4F5C29, 0x3F51EB85, 0x3F547AE1,
+0x3F570A3D, 0x3F59999A, 0x3F5C28F6, 0x3F5EB852, 0x3F6147AE, 0x3F63D70A,
+0x3F666666, 0x3F68F5C3, 0x3F6B851F, 0x3F6E147B, 0x3F70A3D7, 0x3F733333,
+0x3F75C28F, 0x3F7851EC, 0x3F7AE148, 0x3F7D70A4, 0x3F800000
+};
+
+static const unsigned int equalizer_vals_lookup[] = {
+0xC1C00000, 0xC1B80000, 0xC1B00000, 0xC1A80000, 0xC1A00000, 0xC1980000,
+0xC1900000, 0xC1880000, 0xC1800000, 0xC1700000, 0xC1600000, 0xC1500000,
+0xC1400000, 0xC1300000, 0xC1200000, 0xC1100000, 0xC1000000, 0xC0E00000,
+0xC0C00000, 0xC0A00000, 0xC0800000, 0xC0400000, 0xC0000000, 0xBF800000,
+0x00000000, 0x3F800000, 0x40000000, 0x40400000, 0x40800000, 0x40A00000,
+0x40C00000, 0x40E00000, 0x41000000, 0x41100000, 0x41200000, 0x41300000,
+0x41400000, 0x41500000, 0x41600000, 0x41700000, 0x41800000, 0x41880000,
+0x41900000, 0x41980000, 0x41A00000, 0x41A80000, 0x41B00000, 0x41B80000,
+0x41C00000
+};
+
+static int tuning_ctl_set(struct hda_codec *codec, hda_nid_t nid,
+ const unsigned int *lookup, int idx)
+{
+ int i;
+
+ for (i = 0; i < TUNING_CTLS_COUNT; i++) {
+ if (nid == ca0132_tuning_ctls[i].nid) {
+ CLASS(snd_hda_power, pm)(codec);
+ dspio_set_param(codec, ca0132_tuning_ctls[i].mid, 0x20,
+ ca0132_tuning_ctls[i].req,
+ &(lookup[idx]), sizeof(unsigned int));
+ return 1;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int tuning_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx = nid - TUNING_CTL_START_NID;
+
+ *valp = spec->cur_ctl_vals[idx];
+ return 0;
+}
+
+static int voice_focus_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int chs = get_amp_channels(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 20;
+ uinfo->value.integer.max = 180;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int voice_focus_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx;
+
+ idx = nid - TUNING_CTL_START_NID;
+ /* any change? */
+ if (spec->cur_ctl_vals[idx] == *valp)
+ return 0;
+
+ spec->cur_ctl_vals[idx] = *valp;
+
+ idx = *valp - 20;
+ tuning_ctl_set(codec, nid, voice_focus_vals_lookup, idx);
+
+ return 1;
+}
+
+static int mic_svm_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int chs = get_amp_channels(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 100;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int mic_svm_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx;
+
+ idx = nid - TUNING_CTL_START_NID;
+ /* any change? */
+ if (spec->cur_ctl_vals[idx] == *valp)
+ return 0;
+
+ spec->cur_ctl_vals[idx] = *valp;
+
+ idx = *valp;
+ tuning_ctl_set(codec, nid, mic_svm_vals_lookup, idx);
+
+ return 0;
+}
+
+static int equalizer_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int chs = get_amp_channels(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 48;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int equalizer_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx;
+
+ idx = nid - TUNING_CTL_START_NID;
+ /* any change? */
+ if (spec->cur_ctl_vals[idx] == *valp)
+ return 0;
+
+ spec->cur_ctl_vals[idx] = *valp;
+
+ idx = *valp;
+ tuning_ctl_set(codec, nid, equalizer_vals_lookup, idx);
+
+ return 1;
+}
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(voice_focus_db_scale, 2000, 100, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(eq_db_scale, -2400, 100, 0);
+
+static int add_tuning_control(struct hda_codec *codec,
+ hda_nid_t pnid, hda_nid_t nid,
+ const char *name, int dir)
+{
+ char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int type = dir ? HDA_INPUT : HDA_OUTPUT;
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_VOLUME_MONO(namestr, nid, 1, 0, type);
+
+ knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ knew.tlv.c = NULL;
+ knew.tlv.p = NULL;
+ switch (pnid) {
+ case VOICE_FOCUS:
+ knew.info = voice_focus_ctl_info;
+ knew.get = tuning_ctl_get;
+ knew.put = voice_focus_ctl_put;
+ knew.tlv.p = voice_focus_db_scale;
+ break;
+ case MIC_SVM:
+ knew.info = mic_svm_ctl_info;
+ knew.get = tuning_ctl_get;
+ knew.put = mic_svm_ctl_put;
+ break;
+ case EQUALIZER:
+ knew.info = equalizer_ctl_info;
+ knew.get = tuning_ctl_get;
+ knew.put = equalizer_ctl_put;
+ knew.tlv.p = eq_db_scale;
+ break;
+ default:
+ return 0;
+ }
+ knew.private_value =
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, type);
+ snprintf(namestr, sizeof(namestr), "%s %s Volume", name, dirstr[dir]);
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int add_tuning_ctls(struct hda_codec *codec)
+{
+ int i;
+ int err;
+
+ for (i = 0; i < TUNING_CTLS_COUNT; i++) {
+ err = add_tuning_control(codec,
+ ca0132_tuning_ctls[i].parent_nid,
+ ca0132_tuning_ctls[i].nid,
+ ca0132_tuning_ctls[i].name,
+ ca0132_tuning_ctls[i].direct);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void ca0132_init_tuning_defaults(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int i;
+
+ /* Wedge Angle defaults to 30. 10 below is 30 - 20. 20 is min. */
+ spec->cur_ctl_vals[WEDGE_ANGLE - TUNING_CTL_START_NID] = 10;
+ /* SVM level defaults to 0.74. */
+ spec->cur_ctl_vals[SVM_LEVEL - TUNING_CTL_START_NID] = 74;
+
+ /* EQ defaults to 0dB. */
+ for (i = 2; i < TUNING_CTLS_COUNT; i++)
+ spec->cur_ctl_vals[i] = 24;
+}
+#endif /*ENABLE_TUNING_CONTROLS*/
+
+/*
+ * Select the active output.
+ * If autodetect is enabled, output will be selected based on jack detection.
+ * If jack inserted, headphone will be selected, else built-in speakers
+ * If autodetect is disabled, output will be selected based on selection.
+ */
+static int ca0132_select_out(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int pin_ctl;
+ int jack_present;
+ int auto_jack;
+ unsigned int tmp;
+ int err;
+
+ codec_dbg(codec, "ca0132_select_out\n");
+
+ CLASS(snd_hda_power_pm, pm)(codec);
+
+ auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
+
+ if (auto_jack)
+ jack_present = snd_hda_jack_detect(codec, spec->unsol_tag_hp);
+ else
+ jack_present =
+ spec->vnode_lswitch[VNID_HP_SEL - VNODE_START_NID];
+
+ if (jack_present)
+ spec->cur_out_type = HEADPHONE_OUT;
+ else
+ spec->cur_out_type = SPEAKER_OUT;
+
+ if (spec->cur_out_type == SPEAKER_OUT) {
+ codec_dbg(codec, "ca0132_select_out speaker\n");
+ /*speaker out config*/
+ tmp = FLOAT_ONE;
+ err = dspio_set_uint_param(codec, 0x80, 0x04, tmp);
+ if (err < 0)
+ return err;
+ /*enable speaker EQ*/
+ tmp = FLOAT_ONE;
+ err = dspio_set_uint_param(codec, 0x8f, 0x00, tmp);
+ if (err < 0)
+ return err;
+
+ /* Setup EAPD */
+ snd_hda_codec_write(codec, spec->out_pins[1], 0,
+ VENDOR_CHIPIO_EAPD_SEL_SET, 0x02);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ VENDOR_CHIPIO_EAPD_SEL_SET, 0x00);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+
+ /* disable headphone node */
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[1],
+ pin_ctl & ~PIN_HP);
+ /* enable speaker node */
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[0],
+ pin_ctl | PIN_OUT);
+ } else {
+ codec_dbg(codec, "ca0132_select_out hp\n");
+ /*headphone out config*/
+ tmp = FLOAT_ZERO;
+ err = dspio_set_uint_param(codec, 0x80, 0x04, tmp);
+ if (err < 0)
+ return err;
+ /*disable speaker EQ*/
+ tmp = FLOAT_ZERO;
+ err = dspio_set_uint_param(codec, 0x8f, 0x00, tmp);
+ if (err < 0)
+ return err;
+
+ /* Setup EAPD */
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ VENDOR_CHIPIO_EAPD_SEL_SET, 0x00);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+ snd_hda_codec_write(codec, spec->out_pins[1], 0,
+ VENDOR_CHIPIO_EAPD_SEL_SET, 0x02);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+
+ /* disable speaker*/
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[0],
+ pin_ctl & ~PIN_HP);
+ /* enable headphone*/
+ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_set_pin_ctl(codec, spec->out_pins[1],
+ pin_ctl | PIN_HP);
+ }
+
+ return 0;
+}
+
+static int ae5_headphone_gain_set(struct hda_codec *codec, long val);
+static int zxr_headphone_gain_set(struct hda_codec *codec, long val);
+static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val);
+
+static void ae5_mmio_select_out(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ const struct ae_ca0113_output_set *out_cmds;
+ unsigned int i;
+
+ if (ca0132_quirk(spec) == QUIRK_AE5)
+ out_cmds = &ae5_ca0113_output_presets;
+ else
+ out_cmds = &ae7_ca0113_output_presets;
+
+ for (i = 0; i < AE_CA0113_OUT_SET_COMMANDS; i++)
+ ca0113_mmio_command_set(codec, out_cmds->group[i],
+ out_cmds->target[i],
+ out_cmds->vals[spec->cur_out_type][i]);
+}
+
+static int ca0132_alt_set_full_range_speaker(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int quirk = ca0132_quirk(spec);
+ unsigned int tmp;
+ int err;
+
+ /* 2.0/4.0 setup has no LFE channel, so setting full-range does nothing. */
+ if (spec->channel_cfg_val == SPEAKER_CHANNELS_4_0
+ || spec->channel_cfg_val == SPEAKER_CHANNELS_2_0)
+ return 0;
+
+ /* Set front L/R full range. Zero for full-range, one for redirection. */
+ tmp = spec->speaker_range_val[0] ? FLOAT_ZERO : FLOAT_ONE;
+ err = dspio_set_uint_param(codec, 0x96,
+ SPEAKER_FULL_RANGE_FRONT_L_R, tmp);
+ if (err < 0)
+ return err;
+
+ /* When setting full-range rear, both rear and center/lfe are set. */
+ tmp = spec->speaker_range_val[1] ? FLOAT_ZERO : FLOAT_ONE;
+ err = dspio_set_uint_param(codec, 0x96,
+ SPEAKER_FULL_RANGE_CENTER_LFE, tmp);
+ if (err < 0)
+ return err;
+
+ err = dspio_set_uint_param(codec, 0x96,
+ SPEAKER_FULL_RANGE_REAR_L_R, tmp);
+ if (err < 0)
+ return err;
+
+ /*
+ * Only the AE series cards set this value when setting full-range,
+ * and it's always 1.0f.
+ */
+ if (quirk == QUIRK_AE5 || quirk == QUIRK_AE7) {
+ err = dspio_set_uint_param(codec, 0x96,
+ SPEAKER_FULL_RANGE_SURROUND_L_R, FLOAT_ONE);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int ca0132_alt_surround_set_bass_redirection(struct hda_codec *codec,
+ bool val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+ int err;
+
+ if (val && spec->channel_cfg_val != SPEAKER_CHANNELS_4_0 &&
+ spec->channel_cfg_val != SPEAKER_CHANNELS_2_0)
+ tmp = FLOAT_ONE;
+ else
+ tmp = FLOAT_ZERO;
+
+ err = dspio_set_uint_param(codec, 0x96, SPEAKER_BASS_REDIRECT, tmp);
+ if (err < 0)
+ return err;
+
+ /* If it is enabled, make sure to set the crossover frequency. */
+ if (tmp) {
+ tmp = float_xbass_xover_lookup[spec->xbass_xover_freq];
+ err = dspio_set_uint_param(codec, 0x96,
+ SPEAKER_BASS_REDIRECT_XOVER_FREQ, tmp);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * These are the commands needed to setup output on each of the different card
+ * types.
+ */
+static void ca0132_alt_select_out_get_quirk_data(struct hda_codec *codec,
+ const struct ca0132_alt_out_set_quirk_data **quirk_data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int quirk = ca0132_quirk(spec);
+ unsigned int i;
+
+ *quirk_data = NULL;
+ for (i = 0; i < ARRAY_SIZE(quirk_out_set_data); i++) {
+ if (quirk_out_set_data[i].quirk_id == quirk) {
+ *quirk_data = &quirk_out_set_data[i];
+ return;
+ }
+ }
+}
+
+static int ca0132_alt_select_out_quirk_set(struct hda_codec *codec)
+{
+ const struct ca0132_alt_out_set_quirk_data *quirk_data;
+ const struct ca0132_alt_out_set_info *out_info;
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int i, gpio_data;
+ int err;
+
+ ca0132_alt_select_out_get_quirk_data(codec, &quirk_data);
+ if (!quirk_data)
+ return 0;
+
+ out_info = &quirk_data->out_set_info[spec->cur_out_type];
+ if (quirk_data->is_ae_series)
+ ae5_mmio_select_out(codec);
+
+ if (out_info->has_hda_gpio) {
+ gpio_data = snd_hda_codec_read(codec, codec->core.afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0);
+
+ if (out_info->hda_gpio_set)
+ gpio_data |= (1 << out_info->hda_gpio_pin);
+ else
+ gpio_data &= ~(1 << out_info->hda_gpio_pin);
+
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_DATA, gpio_data);
+ }
+
+ if (out_info->mmio_gpio_count) {
+ for (i = 0; i < out_info->mmio_gpio_count; i++) {
+ ca0113_mmio_gpio_set(codec, out_info->mmio_gpio_pin[i],
+ out_info->mmio_gpio_set[i]);
+ }
+ }
+
+ if (out_info->scp_cmds_count) {
+ for (i = 0; i < out_info->scp_cmds_count; i++) {
+ err = dspio_set_uint_param(codec,
+ out_info->scp_cmd_mid[i],
+ out_info->scp_cmd_req[i],
+ out_info->scp_cmd_val[i]);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ chipio_set_control_param(codec, 0x0d, out_info->dac2port);
+
+ if (out_info->has_chipio_write) {
+ chipio_write(codec, out_info->chipio_write_addr,
+ out_info->chipio_write_data);
+ }
+
+ if (quirk_data->has_headphone_gain) {
+ if (spec->cur_out_type != HEADPHONE_OUT) {
+ if (quirk_data->is_ae_series)
+ ae5_headphone_gain_set(codec, 2);
+ else
+ zxr_headphone_gain_set(codec, 0);
+ } else {
+ if (quirk_data->is_ae_series)
+ ae5_headphone_gain_set(codec,
+ spec->ae5_headphone_gain_val);
+ else
+ zxr_headphone_gain_set(codec,
+ spec->zxr_gain_set);
+ }
+ }
+
+ return 0;
+}
+
+static void ca0132_set_out_node_pincfg(struct hda_codec *codec, hda_nid_t nid,
+ bool out_enable, bool hp_enable)
+{
+ unsigned int pin_ctl;
+
+ pin_ctl = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+
+ pin_ctl = hp_enable ? pin_ctl | PIN_HP_AMP : pin_ctl & ~PIN_HP_AMP;
+ pin_ctl = out_enable ? pin_ctl | PIN_OUT : pin_ctl & ~PIN_OUT;
+ snd_hda_set_pin_ctl(codec, nid, pin_ctl);
+}
+
+/*
+ * This function behaves similarly to the ca0132_select_out funciton above,
+ * except with a few differences. It adds the ability to select the current
+ * output with an enumerated control "output source" if the auto detect
+ * mute switch is set to off. If the auto detect mute switch is enabled, it
+ * will detect either headphone or lineout(SPEAKER_OUT) from jack detection.
+ * It also adds the ability to auto-detect the front headphone port.
+ */
+static int ca0132_alt_select_out(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp, outfx_set;
+ int jack_present;
+ int auto_jack;
+ int err;
+ /* Default Headphone is rear headphone */
+ hda_nid_t headphone_nid = spec->out_pins[1];
+
+ codec_dbg(codec, "%s\n", __func__);
+
+ CLASS(snd_hda_power_pm, pm)(codec);
+
+ auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
+
+ /*
+ * If headphone rear or front is plugged in, set to headphone.
+ * If neither is plugged in, set to rear line out. Only if
+ * hp/speaker auto detect is enabled.
+ */
+ if (auto_jack) {
+ jack_present = snd_hda_jack_detect(codec, spec->unsol_tag_hp) ||
+ snd_hda_jack_detect(codec, spec->unsol_tag_front_hp);
+
+ if (jack_present)
+ spec->cur_out_type = HEADPHONE_OUT;
+ else
+ spec->cur_out_type = SPEAKER_OUT;
+ } else
+ spec->cur_out_type = spec->out_enum_val;
+
+ outfx_set = spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID];
+
+ /* Begin DSP output switch, mute DSP volume. */
+ err = dspio_set_uint_param(codec, 0x96, SPEAKER_TUNING_MUTE, FLOAT_ONE);
+ if (err < 0)
+ return err;
+
+ err = ca0132_alt_select_out_quirk_set(codec);
+ if (err < 0)
+ return err;
+
+ switch (spec->cur_out_type) {
+ case SPEAKER_OUT:
+ codec_dbg(codec, "%s speaker\n", __func__);
+
+ /* Enable EAPD */
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x01);
+
+ /* Disable headphone node. */
+ ca0132_set_out_node_pincfg(codec, spec->out_pins[1], 0, 0);
+ /* Set front L-R to output. */
+ ca0132_set_out_node_pincfg(codec, spec->out_pins[0], 1, 0);
+ /* Set Center/LFE to output. */
+ ca0132_set_out_node_pincfg(codec, spec->out_pins[2], 1, 0);
+ /* Set rear surround to output. */
+ ca0132_set_out_node_pincfg(codec, spec->out_pins[3], 1, 0);
+
+ /*
+ * Without PlayEnhancement being enabled, if we've got a 2.0
+ * setup, set it to floating point eight to disable any DSP
+ * processing effects.
+ */
+ if (!outfx_set && spec->channel_cfg_val == SPEAKER_CHANNELS_2_0)
+ tmp = FLOAT_EIGHT;
+ else
+ tmp = speaker_channel_cfgs[spec->channel_cfg_val].val;
+
+ err = dspio_set_uint_param(codec, 0x80, 0x04, tmp);
+ if (err < 0)
+ return err;
+
+ break;
+ case HEADPHONE_OUT:
+ codec_dbg(codec, "%s hp\n", __func__);
+ snd_hda_codec_write(codec, spec->out_pins[0], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+
+ /* Disable all speaker nodes. */
+ ca0132_set_out_node_pincfg(codec, spec->out_pins[0], 0, 0);
+ ca0132_set_out_node_pincfg(codec, spec->out_pins[2], 0, 0);
+ ca0132_set_out_node_pincfg(codec, spec->out_pins[3], 0, 0);
+
+ /* enable headphone, either front or rear */
+ if (snd_hda_jack_detect(codec, spec->unsol_tag_front_hp))
+ headphone_nid = spec->out_pins[2];
+ else if (snd_hda_jack_detect(codec, spec->unsol_tag_hp))
+ headphone_nid = spec->out_pins[1];
+
+ ca0132_set_out_node_pincfg(codec, headphone_nid, 1, 1);
+
+ if (outfx_set)
+ err = dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE);
+ else
+ err = dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ZERO);
+
+ if (err < 0)
+ return err;
+ break;
+ }
+ /*
+ * If output effects are enabled, set the X-Bass effect value again to
+ * make sure that it's properly enabled/disabled for speaker
+ * configurations with an LFE channel.
+ */
+ if (outfx_set)
+ ca0132_effects_set(codec, X_BASS,
+ spec->effects_switch[X_BASS - EFFECT_START_NID]);
+
+ /* Set speaker EQ bypass attenuation to 0. */
+ err = dspio_set_uint_param(codec, 0x8f, 0x01, FLOAT_ZERO);
+ if (err < 0)
+ return err;
+
+ /*
+ * Although unused on all cards but the AE series, this is always set
+ * to zero when setting the output.
+ */
+ err = dspio_set_uint_param(codec, 0x96,
+ SPEAKER_TUNING_USE_SPEAKER_EQ, FLOAT_ZERO);
+ if (err < 0)
+ return err;
+
+ if (spec->cur_out_type == SPEAKER_OUT)
+ err = ca0132_alt_surround_set_bass_redirection(codec,
+ spec->bass_redirection_val);
+ else
+ err = ca0132_alt_surround_set_bass_redirection(codec, 0);
+ if (err < 0)
+ return err;
+
+ /* Unmute DSP now that we're done with output selection. */
+ err = dspio_set_uint_param(codec, 0x96,
+ SPEAKER_TUNING_MUTE, FLOAT_ZERO);
+ if (err < 0)
+ return err;
+
+ if (spec->cur_out_type == SPEAKER_OUT) {
+ err = ca0132_alt_set_full_range_speaker(codec);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void ca0132_unsol_hp_delayed(struct work_struct *work)
+{
+ struct ca0132_spec *spec = container_of(
+ to_delayed_work(work), struct ca0132_spec, unsol_hp_work);
+ struct hda_jack_tbl *jack;
+
+ if (ca0132_use_alt_functions(spec))
+ ca0132_alt_select_out(spec->codec);
+ else
+ ca0132_select_out(spec->codec);
+
+ jack = snd_hda_jack_tbl_get(spec->codec, spec->unsol_tag_hp);
+ if (jack) {
+ jack->block_report = 0;
+ snd_hda_jack_report_sync(spec->codec);
+ }
+}
+
+static void ca0132_set_dmic(struct hda_codec *codec, int enable);
+static int ca0132_mic_boost_set(struct hda_codec *codec, long val);
+static void resume_mic1(struct hda_codec *codec, unsigned int oldval);
+static int stop_mic1(struct hda_codec *codec);
+static int ca0132_cvoice_switch_set(struct hda_codec *codec);
+static int ca0132_alt_mic_boost_set(struct hda_codec *codec, long val);
+
+/*
+ * Select the active VIP source
+ */
+static int ca0132_set_vipsource(struct hda_codec *codec, int val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return 0;
+
+ /* if CrystalVoice if off, vipsource should be 0 */
+ if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] ||
+ (val == 0)) {
+ chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ if (spec->cur_mic_type == DIGITAL_MIC)
+ tmp = FLOAT_TWO;
+ else
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+ } else {
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000);
+ if (spec->cur_mic_type == DIGITAL_MIC)
+ tmp = FLOAT_TWO;
+ else
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+ msleep(20);
+ chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, val);
+ }
+
+ return 1;
+}
+
+static int ca0132_alt_set_vipsource(struct hda_codec *codec, int val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return 0;
+
+ codec_dbg(codec, "%s\n", __func__);
+
+ chipio_set_stream_control(codec, 0x03, 0);
+ chipio_set_stream_control(codec, 0x04, 0);
+
+ /* if CrystalVoice is off, vipsource should be 0 */
+ if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] ||
+ (val == 0) || spec->in_enum_val == REAR_LINE_IN) {
+ codec_dbg(codec, "%s: off.", __func__);
+ chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0);
+
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ if (ca0132_quirk(spec) == QUIRK_R3DI)
+ chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+
+
+ if (spec->in_enum_val == REAR_LINE_IN)
+ tmp = FLOAT_ZERO;
+ else {
+ if (ca0132_quirk(spec) == QUIRK_SBZ)
+ tmp = FLOAT_THREE;
+ else
+ tmp = FLOAT_ONE;
+ }
+
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ } else {
+ codec_dbg(codec, "%s: on.", __func__);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000);
+ if (ca0132_quirk(spec) == QUIRK_R3DI)
+ chipio_set_conn_rate(codec, 0x0F, SR_16_000);
+
+ if (spec->effects_switch[VOICE_FOCUS - EFFECT_START_NID])
+ tmp = FLOAT_TWO;
+ else
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+
+ msleep(20);
+ chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, val);
+ }
+
+ chipio_set_stream_control(codec, 0x03, 1);
+ chipio_set_stream_control(codec, 0x04, 1);
+
+ return 1;
+}
+
+/*
+ * Select the active microphone.
+ * If autodetect is enabled, mic will be selected based on jack detection.
+ * If jack inserted, ext.mic will be selected, else built-in mic
+ * If autodetect is disabled, mic will be selected based on selection.
+ */
+static int ca0132_select_mic(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int jack_present;
+ int auto_jack;
+
+ codec_dbg(codec, "ca0132_select_mic\n");
+
+ CLASS(snd_hda_power_pm, pm)(codec);
+
+ auto_jack = spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID];
+
+ if (auto_jack)
+ jack_present = snd_hda_jack_detect(codec, spec->unsol_tag_amic1);
+ else
+ jack_present =
+ spec->vnode_lswitch[VNID_AMIC1_SEL - VNODE_START_NID];
+
+ if (jack_present)
+ spec->cur_mic_type = LINE_MIC_IN;
+ else
+ spec->cur_mic_type = DIGITAL_MIC;
+
+ if (spec->cur_mic_type == DIGITAL_MIC) {
+ /* enable digital Mic */
+ chipio_set_conn_rate(codec, MEM_CONNID_DMIC, SR_32_000);
+ ca0132_set_dmic(codec, 1);
+ ca0132_mic_boost_set(codec, 0);
+ /* set voice focus */
+ ca0132_effects_set(codec, VOICE_FOCUS,
+ spec->effects_switch
+ [VOICE_FOCUS - EFFECT_START_NID]);
+ } else {
+ /* disable digital Mic */
+ chipio_set_conn_rate(codec, MEM_CONNID_DMIC, SR_96_000);
+ ca0132_set_dmic(codec, 0);
+ ca0132_mic_boost_set(codec, spec->cur_mic_boost);
+ /* disable voice focus */
+ ca0132_effects_set(codec, VOICE_FOCUS, 0);
+ }
+
+ return 0;
+}
+
+/*
+ * Select the active input.
+ * Mic detection isn't used, because it's kind of pointless on the SBZ.
+ * The front mic has no jack-detection, so the only way to switch to it
+ * is to do it manually in alsamixer.
+ */
+static int ca0132_alt_select_in(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+
+ codec_dbg(codec, "%s\n", __func__);
+
+ CLASS(snd_hda_power_pm, pm)(codec);
+
+ chipio_set_stream_control(codec, 0x03, 0);
+ chipio_set_stream_control(codec, 0x04, 0);
+
+ spec->cur_mic_type = spec->in_enum_val;
+
+ switch (spec->cur_mic_type) {
+ case REAR_MIC:
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ case QUIRK_R3D:
+ ca0113_mmio_gpio_set(codec, 0, false);
+ tmp = FLOAT_THREE;
+ break;
+ case QUIRK_ZXR:
+ tmp = FLOAT_THREE;
+ break;
+ case QUIRK_R3DI:
+ r3di_gpio_mic_set(codec, R3DI_REAR_MIC);
+ tmp = FLOAT_ONE;
+ break;
+ case QUIRK_AE5:
+ ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00);
+ tmp = FLOAT_THREE;
+ break;
+ case QUIRK_AE7:
+ ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00);
+ tmp = FLOAT_THREE;
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN2,
+ SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2,
+ SR_96_000);
+ dspio_set_uint_param(codec, 0x80, 0x01, FLOAT_ZERO);
+ break;
+ default:
+ tmp = FLOAT_ONE;
+ break;
+ }
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ if (ca0132_quirk(spec) == QUIRK_R3DI)
+ chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ chipio_set_stream_control(codec, 0x03, 1);
+ chipio_set_stream_control(codec, 0x04, 1);
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ chipio_write(codec, 0x18B098, 0x0000000C);
+ chipio_write(codec, 0x18B09C, 0x0000000C);
+ break;
+ case QUIRK_ZXR:
+ chipio_write(codec, 0x18B098, 0x0000000C);
+ chipio_write(codec, 0x18B09C, 0x000000CC);
+ break;
+ case QUIRK_AE5:
+ chipio_write(codec, 0x18B098, 0x0000000C);
+ chipio_write(codec, 0x18B09C, 0x0000004C);
+ break;
+ default:
+ break;
+ }
+ ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val);
+ break;
+ case REAR_LINE_IN:
+ ca0132_mic_boost_set(codec, 0);
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ case QUIRK_R3D:
+ ca0113_mmio_gpio_set(codec, 0, false);
+ break;
+ case QUIRK_R3DI:
+ r3di_gpio_mic_set(codec, R3DI_REAR_MIC);
+ break;
+ case QUIRK_AE5:
+ ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00);
+ break;
+ case QUIRK_AE7:
+ ca0113_mmio_command_set(codec, 0x30, 0x28, 0x3f);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN2,
+ SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2,
+ SR_96_000);
+ dspio_set_uint_param(codec, 0x80, 0x01, FLOAT_ZERO);
+ break;
+ default:
+ break;
+ }
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ if (ca0132_quirk(spec) == QUIRK_R3DI)
+ chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+
+ if (ca0132_quirk(spec) == QUIRK_AE7)
+ tmp = FLOAT_THREE;
+ else
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ case QUIRK_AE5:
+ chipio_write(codec, 0x18B098, 0x00000000);
+ chipio_write(codec, 0x18B09C, 0x00000000);
+ break;
+ default:
+ break;
+ }
+ chipio_set_stream_control(codec, 0x03, 1);
+ chipio_set_stream_control(codec, 0x04, 1);
+ break;
+ case FRONT_MIC:
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ case QUIRK_R3D:
+ ca0113_mmio_gpio_set(codec, 0, true);
+ ca0113_mmio_gpio_set(codec, 5, false);
+ tmp = FLOAT_THREE;
+ break;
+ case QUIRK_R3DI:
+ r3di_gpio_mic_set(codec, R3DI_FRONT_MIC);
+ tmp = FLOAT_ONE;
+ break;
+ case QUIRK_AE5:
+ ca0113_mmio_command_set(codec, 0x30, 0x28, 0x3f);
+ tmp = FLOAT_THREE;
+ break;
+ default:
+ tmp = FLOAT_ONE;
+ break;
+ }
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ if (ca0132_quirk(spec) == QUIRK_R3DI)
+ chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ chipio_set_stream_control(codec, 0x03, 1);
+ chipio_set_stream_control(codec, 0x04, 1);
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ chipio_write(codec, 0x18B098, 0x0000000C);
+ chipio_write(codec, 0x18B09C, 0x000000CC);
+ break;
+ case QUIRK_AE5:
+ chipio_write(codec, 0x18B098, 0x0000000C);
+ chipio_write(codec, 0x18B09C, 0x0000004C);
+ break;
+ default:
+ break;
+ }
+ ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val);
+ break;
+ }
+ ca0132_cvoice_switch_set(codec);
+
+ return 0;
+}
+
+/*
+ * Check if VNODE settings take effect immediately.
+ */
+static bool ca0132_is_vnode_effective(struct hda_codec *codec,
+ hda_nid_t vnid,
+ hda_nid_t *shared_nid)
+{
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid;
+
+ switch (vnid) {
+ case VNID_SPK:
+ nid = spec->shared_out_nid;
+ break;
+ case VNID_MIC:
+ nid = spec->shared_mic_nid;
+ break;
+ default:
+ return false;
+ }
+
+ if (shared_nid)
+ *shared_nid = nid;
+
+ return true;
+}
+
+/*
+* The following functions are control change helpers.
+* They return 0 if no changed. Return 1 if changed.
+*/
+static int ca0132_voicefx_set(struct hda_codec *codec, int enable)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+
+ /* based on CrystalVoice state to enable VoiceFX. */
+ if (enable) {
+ tmp = spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] ?
+ FLOAT_ONE : FLOAT_ZERO;
+ } else {
+ tmp = FLOAT_ZERO;
+ }
+
+ dspio_set_uint_param(codec, ca0132_voicefx.mid,
+ ca0132_voicefx.reqs[0], tmp);
+
+ return 1;
+}
+
+/*
+ * Set the effects parameters
+ */
+static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int on, tmp, channel_cfg;
+ int num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
+ int err = 0;
+ int idx = nid - EFFECT_START_NID;
+
+ if ((idx < 0) || (idx >= num_fx))
+ return 0; /* no changed */
+
+ /* for out effect, qualify with PE */
+ if ((nid >= OUT_EFFECT_START_NID) && (nid < OUT_EFFECT_END_NID)) {
+ /* if PE if off, turn off out effects. */
+ if (!spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
+ val = 0;
+ if (spec->cur_out_type == SPEAKER_OUT && nid == X_BASS) {
+ channel_cfg = spec->channel_cfg_val;
+ if (channel_cfg != SPEAKER_CHANNELS_2_0 &&
+ channel_cfg != SPEAKER_CHANNELS_4_0)
+ val = 0;
+ }
+ }
+
+ /* for in effect, qualify with CrystalVoice */
+ if ((nid >= IN_EFFECT_START_NID) && (nid < IN_EFFECT_END_NID)) {
+ /* if CrystalVoice if off, turn off in effects. */
+ if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID])
+ val = 0;
+
+ /* Voice Focus applies to 2-ch Mic, Digital Mic */
+ if ((nid == VOICE_FOCUS) && (spec->cur_mic_type != DIGITAL_MIC))
+ val = 0;
+
+ /* If Voice Focus on SBZ, set to two channel. */
+ if ((nid == VOICE_FOCUS) && ca0132_use_pci_mmio(spec)
+ && (spec->cur_mic_type != REAR_LINE_IN)) {
+ if (spec->effects_switch[CRYSTAL_VOICE -
+ EFFECT_START_NID]) {
+
+ if (spec->effects_switch[VOICE_FOCUS -
+ EFFECT_START_NID]) {
+ tmp = FLOAT_TWO;
+ val = 1;
+ } else
+ tmp = FLOAT_ONE;
+
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+ }
+ }
+ /*
+ * For SBZ noise reduction, there's an extra command
+ * to module ID 0x47. No clue why.
+ */
+ if ((nid == NOISE_REDUCTION) && ca0132_use_pci_mmio(spec)
+ && (spec->cur_mic_type != REAR_LINE_IN)) {
+ if (spec->effects_switch[CRYSTAL_VOICE -
+ EFFECT_START_NID]) {
+ if (spec->effects_switch[NOISE_REDUCTION -
+ EFFECT_START_NID])
+ tmp = FLOAT_ONE;
+ else
+ tmp = FLOAT_ZERO;
+ } else
+ tmp = FLOAT_ZERO;
+
+ dspio_set_uint_param(codec, 0x47, 0x00, tmp);
+ }
+
+ /* If rear line in disable effects. */
+ if (ca0132_use_alt_functions(spec) &&
+ spec->in_enum_val == REAR_LINE_IN)
+ val = 0;
+ }
+
+ codec_dbg(codec, "ca0132_effect_set: nid=0x%x, val=%ld\n",
+ nid, val);
+
+ on = (val == 0) ? FLOAT_ZERO : FLOAT_ONE;
+ err = dspio_set_uint_param(codec, ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[0], on);
+
+ if (err < 0)
+ return 0; /* no changed */
+
+ return 1;
+}
+
+/*
+ * Turn on/off Playback Enhancements
+ */
+static int ca0132_pe_switch_set(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid;
+ int i, ret = 0;
+
+ codec_dbg(codec, "ca0132_pe_switch_set: val=%ld\n",
+ spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]);
+
+ if (ca0132_use_alt_functions(spec))
+ ca0132_alt_select_out(codec);
+
+ i = OUT_EFFECT_START_NID - EFFECT_START_NID;
+ nid = OUT_EFFECT_START_NID;
+ /* PE affects all out effects */
+ for (; nid < OUT_EFFECT_END_NID; nid++, i++)
+ ret |= ca0132_effects_set(codec, nid, spec->effects_switch[i]);
+
+ return ret;
+}
+
+/* Check if Mic1 is streaming, if so, stop streaming */
+static int stop_mic1(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int oldval = snd_hda_codec_read(codec, spec->adcs[0], 0,
+ AC_VERB_GET_CONV, 0);
+ if (oldval != 0)
+ snd_hda_codec_write(codec, spec->adcs[0], 0,
+ AC_VERB_SET_CHANNEL_STREAMID,
+ 0);
+ return oldval;
+}
+
+/* Resume Mic1 streaming if it was stopped. */
+static void resume_mic1(struct hda_codec *codec, unsigned int oldval)
+{
+ struct ca0132_spec *spec = codec->spec;
+ /* Restore the previous stream and channel */
+ if (oldval != 0)
+ snd_hda_codec_write(codec, spec->adcs[0], 0,
+ AC_VERB_SET_CHANNEL_STREAMID,
+ oldval);
+}
+
+/*
+ * Turn on/off CrystalVoice
+ */
+static int ca0132_cvoice_switch_set(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid;
+ int i, ret = 0;
+ unsigned int oldval;
+
+ codec_dbg(codec, "ca0132_cvoice_switch_set: val=%ld\n",
+ spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]);
+
+ i = IN_EFFECT_START_NID - EFFECT_START_NID;
+ nid = IN_EFFECT_START_NID;
+ /* CrystalVoice affects all in effects */
+ for (; nid < IN_EFFECT_END_NID; nid++, i++)
+ ret |= ca0132_effects_set(codec, nid, spec->effects_switch[i]);
+
+ /* including VoiceFX */
+ ret |= ca0132_voicefx_set(codec, (spec->voicefx_val ? 1 : 0));
+
+ /* set correct vipsource */
+ oldval = stop_mic1(codec);
+ if (ca0132_use_alt_functions(spec))
+ ret |= ca0132_alt_set_vipsource(codec, 1);
+ else
+ ret |= ca0132_set_vipsource(codec, 1);
+ resume_mic1(codec, oldval);
+ return ret;
+}
+
+static int ca0132_mic_boost_set(struct hda_codec *codec, long val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int ret = 0;
+
+ if (val) /* on */
+ ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0,
+ HDA_INPUT, 0, HDA_AMP_VOLMASK, 3);
+ else /* off */
+ ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0,
+ HDA_INPUT, 0, HDA_AMP_VOLMASK, 0);
+
+ return ret;
+}
+
+static int ca0132_alt_mic_boost_set(struct hda_codec *codec, long val)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int ret = 0;
+
+ ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0,
+ HDA_INPUT, 0, HDA_AMP_VOLMASK, val);
+ return ret;
+}
+
+static int ae5_headphone_gain_set(struct hda_codec *codec, long val)
+{
+ unsigned int i;
+
+ for (i = 0; i < 4; i++)
+ ca0113_mmio_command_set(codec, 0x48, 0x11 + i,
+ ae5_headphone_gain_presets[val].vals[i]);
+ return 0;
+}
+
+/*
+ * gpio pin 1 is a relay that switches on/off, apparently setting the headphone
+ * amplifier to handle a 600 ohm load.
+ */
+static int zxr_headphone_gain_set(struct hda_codec *codec, long val)
+{
+ ca0113_mmio_gpio_set(codec, 1, val);
+
+ return 0;
+}
+
+static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ hda_nid_t shared_nid = 0;
+ bool effective;
+ int ret = 0;
+ struct ca0132_spec *spec = codec->spec;
+ int auto_jack;
+
+ if (nid == VNID_HP_SEL) {
+ auto_jack =
+ spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
+ if (!auto_jack) {
+ if (ca0132_use_alt_functions(spec))
+ ca0132_alt_select_out(codec);
+ else
+ ca0132_select_out(codec);
+ }
+ return 1;
+ }
+
+ if (nid == VNID_AMIC1_SEL) {
+ auto_jack =
+ spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID];
+ if (!auto_jack)
+ ca0132_select_mic(codec);
+ return 1;
+ }
+
+ if (nid == VNID_HP_ASEL) {
+ if (ca0132_use_alt_functions(spec))
+ ca0132_alt_select_out(codec);
+ else
+ ca0132_select_out(codec);
+ return 1;
+ }
+
+ if (nid == VNID_AMIC1_ASEL) {
+ ca0132_select_mic(codec);
+ return 1;
+ }
+
+ /* if effective conditions, then update hw immediately. */
+ effective = ca0132_is_vnode_effective(codec, nid, &shared_nid);
+ if (effective) {
+ int dir = get_amp_direction(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ unsigned long pval;
+
+ guard(mutex)(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(shared_nid, ch,
+ 0, dir);
+ ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ kcontrol->private_value = pval;
+ }
+
+ return ret;
+}
+/* End of control change helpers. */
+
+static void ca0132_alt_bass_redirection_xover_set(struct hda_codec *codec,
+ long idx)
+{
+ CLASS(snd_hda_power, pm)(codec);
+
+ dspio_set_param(codec, 0x96, 0x20, SPEAKER_BASS_REDIRECT_XOVER_FREQ,
+ &(float_xbass_xover_lookup[idx]), sizeof(unsigned int));
+}
+
+/*
+ * Below I've added controls to mess with the effect levels, I've only enabled
+ * them on the Sound Blaster Z, but they would probably also work on the
+ * Chromebook. I figured they were probably tuned specifically for it, and left
+ * out for a reason.
+ */
+
+/* Sets DSP effect level from the sliders above the controls */
+
+static int ca0132_alt_slider_ctl_set(struct hda_codec *codec, hda_nid_t nid,
+ const unsigned int *lookup, int idx)
+{
+ int i = 0;
+ unsigned int y;
+ /*
+ * For X_BASS, req 2 is actually crossover freq instead of
+ * effect level
+ */
+ if (nid == X_BASS)
+ y = 2;
+ else
+ y = 1;
+
+ CLASS(snd_hda_power, pm)(codec);
+ if (nid == XBASS_XOVER) {
+ for (i = 0; i < OUT_EFFECTS_COUNT; i++)
+ if (ca0132_effects[i].nid == X_BASS)
+ break;
+
+ dspio_set_param(codec, ca0132_effects[i].mid, 0x20,
+ ca0132_effects[i].reqs[1],
+ &(lookup[idx - 1]), sizeof(unsigned int));
+ } else {
+ /* Find the actual effect structure */
+ for (i = 0; i < OUT_EFFECTS_COUNT; i++)
+ if (nid == ca0132_effects[i].nid)
+ break;
+
+ dspio_set_param(codec, ca0132_effects[i].mid, 0x20,
+ ca0132_effects[i].reqs[y],
+ &(lookup[idx]), sizeof(unsigned int));
+ }
+
+ return 0;
+}
+
+static int ca0132_alt_xbass_xover_slider_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ long *valp = ucontrol->value.integer.value;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+
+ if (nid == BASS_REDIRECTION_XOVER)
+ *valp = spec->bass_redirect_xover_freq;
+ else
+ *valp = spec->xbass_xover_freq;
+
+ return 0;
+}
+
+static int ca0132_alt_slider_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx = nid - OUT_EFFECT_START_NID;
+
+ *valp = spec->fx_ctl_val[idx];
+ return 0;
+}
+
+/*
+ * The X-bass crossover starts at 10hz, so the min is 1. The
+ * frequency is set in multiples of 10.
+ */
+static int ca0132_alt_xbass_xover_slider_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 1;
+ uinfo->value.integer.max = 100;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int ca0132_alt_effect_slider_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int chs = get_amp_channels(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 100;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int ca0132_alt_xbass_xover_slider_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ long *cur_val;
+ int idx;
+
+ if (nid == BASS_REDIRECTION_XOVER)
+ cur_val = &spec->bass_redirect_xover_freq;
+ else
+ cur_val = &spec->xbass_xover_freq;
+
+ /* any change? */
+ if (*cur_val == *valp)
+ return 0;
+
+ *cur_val = *valp;
+
+ idx = *valp;
+ if (nid == BASS_REDIRECTION_XOVER)
+ ca0132_alt_bass_redirection_xover_set(codec, *cur_val);
+ else
+ ca0132_alt_slider_ctl_set(codec, nid, float_xbass_xover_lookup, idx);
+
+ return 0;
+}
+
+static int ca0132_alt_effect_slider_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int idx;
+
+ idx = nid - EFFECT_START_NID;
+ /* any change? */
+ if (spec->fx_ctl_val[idx] == *valp)
+ return 0;
+
+ spec->fx_ctl_val[idx] = *valp;
+
+ idx = *valp;
+ ca0132_alt_slider_ctl_set(codec, nid, float_zero_to_one_lookup, idx);
+
+ return 0;
+}
+
+
+/*
+ * Mic Boost Enum for alternative ca0132 codecs. I didn't like that the original
+ * only has off or full 30 dB, and didn't like making a volume slider that has
+ * traditional 0-100 in alsamixer that goes in big steps. I like enum better.
+ */
+#define MIC_BOOST_NUM_OF_STEPS 4
+#define MIC_BOOST_ENUM_MAX_STRLEN 10
+
+static int ca0132_alt_mic_boost_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ const char *sfx = "dB";
+ char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = MIC_BOOST_NUM_OF_STEPS;
+ if (uinfo->value.enumerated.item >= MIC_BOOST_NUM_OF_STEPS)
+ uinfo->value.enumerated.item = MIC_BOOST_NUM_OF_STEPS - 1;
+ sprintf(namestr, "%d %s", (uinfo->value.enumerated.item * 10), sfx);
+ strscpy(uinfo->value.enumerated.name, namestr);
+ return 0;
+}
+
+static int ca0132_alt_mic_boost_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->mic_boost_enum_val;
+ return 0;
+}
+
+static int ca0132_alt_mic_boost_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = MIC_BOOST_NUM_OF_STEPS;
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ca0132_alt_mic_boost: boost=%d\n",
+ sel);
+
+ spec->mic_boost_enum_val = sel;
+
+ if (spec->in_enum_val != REAR_LINE_IN)
+ ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val);
+
+ return 1;
+}
+
+/*
+ * Sound BlasterX AE-5 Headphone Gain Controls.
+ */
+#define AE5_HEADPHONE_GAIN_MAX 3
+static int ae5_headphone_gain_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ const char *sfx = " Ohms)";
+ char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = AE5_HEADPHONE_GAIN_MAX;
+ if (uinfo->value.enumerated.item >= AE5_HEADPHONE_GAIN_MAX)
+ uinfo->value.enumerated.item = AE5_HEADPHONE_GAIN_MAX - 1;
+ sprintf(namestr, "%s %s",
+ ae5_headphone_gain_presets[uinfo->value.enumerated.item].name,
+ sfx);
+ strscpy(uinfo->value.enumerated.name, namestr);
+ return 0;
+}
+
+static int ae5_headphone_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->ae5_headphone_gain_val;
+ return 0;
+}
+
+static int ae5_headphone_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = AE5_HEADPHONE_GAIN_MAX;
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ae5_headphone_gain: boost=%d\n",
+ sel);
+
+ spec->ae5_headphone_gain_val = sel;
+
+ if (spec->out_enum_val == HEADPHONE_OUT)
+ ae5_headphone_gain_set(codec, spec->ae5_headphone_gain_val);
+
+ return 1;
+}
+
+/*
+ * Sound BlasterX AE-5 sound filter enumerated control.
+ */
+#define AE5_SOUND_FILTER_MAX 3
+
+static int ae5_sound_filter_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = AE5_SOUND_FILTER_MAX;
+ if (uinfo->value.enumerated.item >= AE5_SOUND_FILTER_MAX)
+ uinfo->value.enumerated.item = AE5_SOUND_FILTER_MAX - 1;
+ sprintf(namestr, "%s",
+ ae5_filter_presets[uinfo->value.enumerated.item].name);
+ strscpy(uinfo->value.enumerated.name, namestr);
+ return 0;
+}
+
+static int ae5_sound_filter_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->ae5_filter_val;
+ return 0;
+}
+
+static int ae5_sound_filter_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = AE5_SOUND_FILTER_MAX;
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ae5_sound_filter: %s\n",
+ ae5_filter_presets[sel].name);
+
+ spec->ae5_filter_val = sel;
+
+ ca0113_mmio_command_set_type2(codec, 0x48, 0x07,
+ ae5_filter_presets[sel].val);
+
+ return 1;
+}
+
+/*
+ * Input Select Control for alternative ca0132 codecs. This exists because
+ * front microphone has no auto-detect, and we need a way to set the rear
+ * as line-in
+ */
+static int ca0132_alt_input_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = IN_SRC_NUM_OF_INPUTS;
+ if (uinfo->value.enumerated.item >= IN_SRC_NUM_OF_INPUTS)
+ uinfo->value.enumerated.item = IN_SRC_NUM_OF_INPUTS - 1;
+ strscpy(uinfo->value.enumerated.name,
+ in_src_str[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int ca0132_alt_input_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->in_enum_val;
+ return 0;
+}
+
+static int ca0132_alt_input_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = IN_SRC_NUM_OF_INPUTS;
+
+ /*
+ * The AE-7 has no front microphone, so limit items to 2: rear mic and
+ * line-in.
+ */
+ if (ca0132_quirk(spec) == QUIRK_AE7)
+ items = 2;
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ca0132_alt_input_select: sel=%d, preset=%s\n",
+ sel, in_src_str[sel]);
+
+ spec->in_enum_val = sel;
+
+ ca0132_alt_select_in(codec);
+
+ return 1;
+}
+
+/* Sound Blaster Z Output Select Control */
+static int ca0132_alt_output_select_get_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = NUM_OF_OUTPUTS;
+ if (uinfo->value.enumerated.item >= NUM_OF_OUTPUTS)
+ uinfo->value.enumerated.item = NUM_OF_OUTPUTS - 1;
+ strscpy(uinfo->value.enumerated.name,
+ out_type_str[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int ca0132_alt_output_select_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->out_enum_val;
+ return 0;
+}
+
+static int ca0132_alt_output_select_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = NUM_OF_OUTPUTS;
+ unsigned int auto_jack;
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ca0132_alt_output_select: sel=%d, preset=%s\n",
+ sel, out_type_str[sel]);
+
+ spec->out_enum_val = sel;
+
+ auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
+
+ if (!auto_jack)
+ ca0132_alt_select_out(codec);
+
+ return 1;
+}
+
+/* Select surround output type: 2.1, 4.0, 4.1, or 5.1. */
+static int ca0132_alt_speaker_channel_cfg_get_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int items = SPEAKER_CHANNEL_CFG_COUNT;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = items;
+ if (uinfo->value.enumerated.item >= items)
+ uinfo->value.enumerated.item = items - 1;
+ strscpy(uinfo->value.enumerated.name,
+ speaker_channel_cfgs[uinfo->value.enumerated.item].name);
+ return 0;
+}
+
+static int ca0132_alt_speaker_channel_cfg_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->channel_cfg_val;
+ return 0;
+}
+
+static int ca0132_alt_speaker_channel_cfg_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = SPEAKER_CHANNEL_CFG_COUNT;
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ca0132_alt_speaker_channels: sel=%d, channels=%s\n",
+ sel, speaker_channel_cfgs[sel].name);
+
+ spec->channel_cfg_val = sel;
+
+ if (spec->out_enum_val == SPEAKER_OUT)
+ ca0132_alt_select_out(codec);
+
+ return 1;
+}
+
+/*
+ * Smart Volume output setting control. Three different settings, Normal,
+ * which takes the value from the smart volume slider. The two others, loud
+ * and night, disregard the slider value and have uneditable values.
+ */
+#define NUM_OF_SVM_SETTINGS 3
+static const char *const out_svm_set_enum_str[3] = {"Normal", "Loud", "Night" };
+
+static int ca0132_alt_svm_setting_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = NUM_OF_SVM_SETTINGS;
+ if (uinfo->value.enumerated.item >= NUM_OF_SVM_SETTINGS)
+ uinfo->value.enumerated.item = NUM_OF_SVM_SETTINGS - 1;
+ strscpy(uinfo->value.enumerated.name,
+ out_svm_set_enum_str[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int ca0132_alt_svm_setting_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->smart_volume_setting;
+ return 0;
+}
+
+static int ca0132_alt_svm_setting_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = NUM_OF_SVM_SETTINGS;
+ unsigned int idx = SMART_VOLUME - EFFECT_START_NID;
+ unsigned int tmp;
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "ca0132_alt_svm_setting: sel=%d, preset=%s\n",
+ sel, out_svm_set_enum_str[sel]);
+
+ spec->smart_volume_setting = sel;
+
+ switch (sel) {
+ case 0:
+ tmp = FLOAT_ZERO;
+ break;
+ case 1:
+ tmp = FLOAT_ONE;
+ break;
+ case 2:
+ tmp = FLOAT_TWO;
+ break;
+ default:
+ tmp = FLOAT_ZERO;
+ break;
+ }
+ /* Req 2 is the Smart Volume Setting req. */
+ dspio_set_uint_param(codec, ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[2], tmp);
+ return 1;
+}
+
+/* Sound Blaster Z EQ preset controls */
+static int ca0132_alt_eq_preset_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int items = ARRAY_SIZE(ca0132_alt_eq_presets);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = items;
+ if (uinfo->value.enumerated.item >= items)
+ uinfo->value.enumerated.item = items - 1;
+ strscpy(uinfo->value.enumerated.name,
+ ca0132_alt_eq_presets[uinfo->value.enumerated.item].name);
+ return 0;
+}
+
+static int ca0132_alt_eq_preset_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->eq_preset_val;
+ return 0;
+}
+
+static int ca0132_alt_eq_preset_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int i, err = 0;
+ int sel = ucontrol->value.enumerated.item[0];
+ unsigned int items = ARRAY_SIZE(ca0132_alt_eq_presets);
+
+ if (sel >= items)
+ return 0;
+
+ codec_dbg(codec, "%s: sel=%d, preset=%s\n", __func__, sel,
+ ca0132_alt_eq_presets[sel].name);
+ /*
+ * Idx 0 is default.
+ * Default needs to qualify with CrystalVoice state.
+ */
+ for (i = 0; i < EQ_PRESET_MAX_PARAM_COUNT; i++) {
+ err = dspio_set_uint_param(codec, ca0132_alt_eq_enum.mid,
+ ca0132_alt_eq_enum.reqs[i],
+ ca0132_alt_eq_presets[sel].vals[i]);
+ if (err < 0)
+ break;
+ }
+
+ if (err >= 0)
+ spec->eq_preset_val = sel;
+
+ return 1;
+}
+
+static int ca0132_voicefx_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int items = ARRAY_SIZE(ca0132_voicefx_presets);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = items;
+ if (uinfo->value.enumerated.item >= items)
+ uinfo->value.enumerated.item = items - 1;
+ strscpy(uinfo->value.enumerated.name,
+ ca0132_voicefx_presets[uinfo->value.enumerated.item].name);
+ return 0;
+}
+
+static int ca0132_voicefx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->voicefx_val;
+ return 0;
+}
+
+static int ca0132_voicefx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ int i, err = 0;
+ int sel = ucontrol->value.enumerated.item[0];
+
+ if (sel >= ARRAY_SIZE(ca0132_voicefx_presets))
+ return 0;
+
+ codec_dbg(codec, "ca0132_voicefx_put: sel=%d, preset=%s\n",
+ sel, ca0132_voicefx_presets[sel].name);
+
+ /*
+ * Idx 0 is default.
+ * Default needs to qualify with CrystalVoice state.
+ */
+ for (i = 0; i < VOICEFX_MAX_PARAM_COUNT; i++) {
+ err = dspio_set_uint_param(codec, ca0132_voicefx.mid,
+ ca0132_voicefx.reqs[i],
+ ca0132_voicefx_presets[sel].vals[i]);
+ if (err < 0)
+ break;
+ }
+
+ if (err >= 0) {
+ spec->voicefx_val = sel;
+ /* enable voice fx */
+ ca0132_voicefx_set(codec, (sel ? 1 : 0));
+ }
+
+ return 1;
+}
+
+static int ca0132_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+
+ /* vnode */
+ if ((nid >= VNODE_START_NID) && (nid < VNODE_END_NID)) {
+ if (ch & 1) {
+ *valp = spec->vnode_lswitch[nid - VNODE_START_NID];
+ valp++;
+ }
+ if (ch & 2) {
+ *valp = spec->vnode_rswitch[nid - VNODE_START_NID];
+ valp++;
+ }
+ return 0;
+ }
+
+ /* effects, include PE and CrystalVoice */
+ if ((nid >= EFFECT_START_NID) && (nid < EFFECT_END_NID)) {
+ *valp = spec->effects_switch[nid - EFFECT_START_NID];
+ return 0;
+ }
+
+ /* mic boost */
+ if (nid == spec->input_pins[0]) {
+ *valp = spec->cur_mic_boost;
+ return 0;
+ }
+
+ if (nid == ZXR_HEADPHONE_GAIN) {
+ *valp = spec->zxr_gain_set;
+ return 0;
+ }
+
+ if (nid == SPEAKER_FULL_RANGE_FRONT || nid == SPEAKER_FULL_RANGE_REAR) {
+ *valp = spec->speaker_range_val[nid - SPEAKER_FULL_RANGE_FRONT];
+ return 0;
+ }
+
+ if (nid == BASS_REDIRECTION) {
+ *valp = spec->bass_redirection_val;
+ return 0;
+ }
+
+ return 0;
+}
+
+static int ca0132_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+
+ codec_dbg(codec, "ca0132_switch_put: nid=0x%x, val=%ld\n",
+ nid, *valp);
+
+ CLASS(snd_hda_power, pm)(codec);
+ /* vnode */
+ if ((nid >= VNODE_START_NID) && (nid < VNODE_END_NID)) {
+ if (ch & 1) {
+ spec->vnode_lswitch[nid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+ if (ch & 2) {
+ spec->vnode_rswitch[nid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+ return ca0132_vnode_switch_set(kcontrol, ucontrol);
+ }
+
+ /* PE */
+ if (nid == PLAY_ENHANCEMENT) {
+ spec->effects_switch[nid - EFFECT_START_NID] = *valp;
+ return ca0132_pe_switch_set(codec);
+ }
+
+ /* CrystalVoice */
+ if (nid == CRYSTAL_VOICE) {
+ spec->effects_switch[nid - EFFECT_START_NID] = *valp;
+ return ca0132_cvoice_switch_set(codec);
+ }
+
+ /* out and in effects */
+ if (((nid >= OUT_EFFECT_START_NID) && (nid < OUT_EFFECT_END_NID)) ||
+ ((nid >= IN_EFFECT_START_NID) && (nid < IN_EFFECT_END_NID))) {
+ spec->effects_switch[nid - EFFECT_START_NID] = *valp;
+ return ca0132_effects_set(codec, nid, *valp);
+ }
+
+ /* mic boost */
+ if (nid == spec->input_pins[0]) {
+ spec->cur_mic_boost = *valp;
+ if (ca0132_use_alt_functions(spec)) {
+ if (spec->in_enum_val != REAR_LINE_IN)
+ return ca0132_mic_boost_set(codec, *valp);
+ } else {
+ /* Mic boost does not apply to Digital Mic */
+ if (spec->cur_mic_type != DIGITAL_MIC)
+ return ca0132_mic_boost_set(codec, *valp);
+ }
+
+ return 1;
+ }
+
+ if (nid == ZXR_HEADPHONE_GAIN) {
+ spec->zxr_gain_set = *valp;
+ if (spec->cur_out_type == HEADPHONE_OUT)
+ return zxr_headphone_gain_set(codec, *valp);
+ else
+ return 0;
+ }
+
+ if (nid == SPEAKER_FULL_RANGE_FRONT || nid == SPEAKER_FULL_RANGE_REAR) {
+ spec->speaker_range_val[nid - SPEAKER_FULL_RANGE_FRONT] = *valp;
+ if (spec->cur_out_type == SPEAKER_OUT)
+ ca0132_alt_set_full_range_speaker(codec);
+
+ return 0;
+ }
+
+ if (nid == BASS_REDIRECTION) {
+ spec->bass_redirection_val = *valp;
+ if (spec->cur_out_type == SPEAKER_OUT)
+ ca0132_alt_surround_set_bass_redirection(codec, *valp);
+
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Volume related
+ */
+/*
+ * Sets the internal DSP decibel level to match the DAC for output, and the
+ * ADC for input. Currently only the SBZ sets dsp capture volume level, and
+ * all alternative codecs set DSP playback volume.
+ */
+static void ca0132_alt_dsp_volume_put(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int dsp_dir;
+ unsigned int lookup_val;
+
+ if (nid == VNID_SPK)
+ dsp_dir = DSP_VOL_OUT;
+ else
+ dsp_dir = DSP_VOL_IN;
+
+ lookup_val = spec->vnode_lvol[nid - VNODE_START_NID];
+
+ dspio_set_uint_param(codec,
+ ca0132_alt_vol_ctls[dsp_dir].mid,
+ ca0132_alt_vol_ctls[dsp_dir].reqs[0],
+ float_vol_db_lookup[lookup_val]);
+
+ lookup_val = spec->vnode_rvol[nid - VNODE_START_NID];
+
+ dspio_set_uint_param(codec,
+ ca0132_alt_vol_ctls[dsp_dir].mid,
+ ca0132_alt_vol_ctls[dsp_dir].reqs[1],
+ float_vol_db_lookup[lookup_val]);
+
+ dspio_set_uint_param(codec,
+ ca0132_alt_vol_ctls[dsp_dir].mid,
+ ca0132_alt_vol_ctls[dsp_dir].reqs[2], FLOAT_ZERO);
+}
+
+static int ca0132_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ unsigned long pval;
+ int err;
+
+ switch (nid) {
+ case VNID_SPK:
+ /* follow shared_out info */
+ nid = spec->shared_out_nid;
+ scoped_guard(mutex, &codec->control_mutex) {
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
+ err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
+ kcontrol->private_value = pval;
+ }
+ break;
+ case VNID_MIC:
+ /* follow shared_mic info */
+ nid = spec->shared_mic_nid;
+ scoped_guard(mutex, &codec->control_mutex) {
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
+ err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
+ kcontrol->private_value = pval;
+ }
+ break;
+ default:
+ err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
+ }
+ return err;
+}
+
+static int ca0132_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+
+ /* store the left and right volume */
+ if (ch & 1) {
+ *valp = spec->vnode_lvol[nid - VNODE_START_NID];
+ valp++;
+ }
+ if (ch & 2) {
+ *valp = spec->vnode_rvol[nid - VNODE_START_NID];
+ valp++;
+ }
+ return 0;
+}
+
+static int ca0132_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ hda_nid_t shared_nid = 0;
+ bool effective;
+ int changed = 1;
+
+ /* store the left and right volume */
+ if (ch & 1) {
+ spec->vnode_lvol[nid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+ if (ch & 2) {
+ spec->vnode_rvol[nid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+
+ /* if effective conditions, then update hw immediately. */
+ effective = ca0132_is_vnode_effective(codec, nid, &shared_nid);
+ if (effective) {
+ int dir = get_amp_direction(kcontrol);
+ unsigned long pval;
+
+ CLASS(snd_hda_power, pm)(codec);
+ guard(mutex)(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(shared_nid, ch,
+ 0, dir);
+ changed = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
+ kcontrol->private_value = pval;
+ }
+
+ return changed;
+}
+
+/*
+ * This function is the same as the one above, because using an if statement
+ * inside of the above volume control for the DSP volume would cause too much
+ * lag. This is a lot more smooth.
+ */
+static int ca0132_alt_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ hda_nid_t vnid = 0;
+
+ switch (nid) {
+ case 0x02:
+ vnid = VNID_SPK;
+ break;
+ case 0x07:
+ vnid = VNID_MIC;
+ break;
+ }
+
+ /* store the left and right volume */
+ if (ch & 1) {
+ spec->vnode_lvol[vnid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+ if (ch & 2) {
+ spec->vnode_rvol[vnid - VNODE_START_NID] = *valp;
+ valp++;
+ }
+
+ CLASS(snd_hda_power, pm)(codec);
+ ca0132_alt_dsp_volume_put(codec, vnid);
+ guard(mutex)(&codec->control_mutex);
+ return snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
+}
+
+static int ca0132_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int ch = get_amp_channels(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ unsigned long pval;
+ int err;
+
+ switch (nid) {
+ case VNID_SPK:
+ /* follow shared_out tlv */
+ nid = spec->shared_out_nid;
+ scoped_guard(mutex, &codec->control_mutex) {
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
+ err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
+ kcontrol->private_value = pval;
+ }
+ break;
+ case VNID_MIC:
+ /* follow shared_mic tlv */
+ nid = spec->shared_mic_nid;
+ scoped_guard(mutex, &codec->control_mutex) {
+ pval = kcontrol->private_value;
+ kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
+ err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
+ kcontrol->private_value = pval;
+ }
+ break;
+ default:
+ err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
+ }
+ return err;
+}
+
+/* Add volume slider control for effect level */
+static int ca0132_alt_add_effect_slider(struct hda_codec *codec, hda_nid_t nid,
+ const char *pfx, int dir)
+{
+ char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int type = dir ? HDA_INPUT : HDA_OUTPUT;
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_VOLUME_MONO(namestr, nid, 1, 0, type);
+
+ sprintf(namestr, "FX: %s %s Volume", pfx, dirstr[dir]);
+
+ knew.tlv.c = NULL;
+
+ switch (nid) {
+ case XBASS_XOVER:
+ knew.info = ca0132_alt_xbass_xover_slider_info;
+ knew.get = ca0132_alt_xbass_xover_slider_ctl_get;
+ knew.put = ca0132_alt_xbass_xover_slider_put;
+ break;
+ default:
+ knew.info = ca0132_alt_effect_slider_info;
+ knew.get = ca0132_alt_slider_ctl_get;
+ knew.put = ca0132_alt_effect_slider_put;
+ knew.private_value =
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, type);
+ break;
+ }
+
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Added FX: prefix for the alternative codecs, because otherwise the surround
+ * effect would conflict with the Surround sound volume control. Also seems more
+ * clear as to what the switches do. Left alone for others.
+ */
+static int add_fx_switch(struct hda_codec *codec, hda_nid_t nid,
+ const char *pfx, int dir)
+{
+ struct ca0132_spec *spec = codec->spec;
+ char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int type = dir ? HDA_INPUT : HDA_OUTPUT;
+ struct snd_kcontrol_new knew =
+ CA0132_CODEC_MUTE_MONO(namestr, nid, 1, type);
+ /* If using alt_controls, add FX: prefix. But, don't add FX:
+ * prefix to OutFX or InFX enable controls.
+ */
+ if (ca0132_use_alt_controls(spec) && (nid <= IN_EFFECT_END_NID))
+ sprintf(namestr, "FX: %s %s Switch", pfx, dirstr[dir]);
+ else
+ sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
+
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int add_voicefx(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO(ca0132_voicefx.name,
+ VOICEFX, 1, 0, HDA_INPUT);
+ knew.info = ca0132_voicefx_info;
+ knew.get = ca0132_voicefx_get;
+ knew.put = ca0132_voicefx_put;
+ return snd_hda_ctl_add(codec, VOICEFX, snd_ctl_new1(&knew, codec));
+}
+
+/* Create the EQ Preset control */
+static int add_ca0132_alt_eq_presets(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO(ca0132_alt_eq_enum.name,
+ EQ_PRESET_ENUM, 1, 0, HDA_OUTPUT);
+ knew.info = ca0132_alt_eq_preset_info;
+ knew.get = ca0132_alt_eq_preset_get;
+ knew.put = ca0132_alt_eq_preset_put;
+ return snd_hda_ctl_add(codec, EQ_PRESET_ENUM,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Add enumerated control for the three different settings of the smart volume
+ * output effect. Normal just uses the slider value, and loud and night are
+ * their own things that ignore that value.
+ */
+static int ca0132_alt_add_svm_enum(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("FX: Smart Volume Setting",
+ SMART_VOLUME_ENUM, 1, 0, HDA_OUTPUT);
+ knew.info = ca0132_alt_svm_setting_info;
+ knew.get = ca0132_alt_svm_setting_get;
+ knew.put = ca0132_alt_svm_setting_put;
+ return snd_hda_ctl_add(codec, SMART_VOLUME_ENUM,
+ snd_ctl_new1(&knew, codec));
+
+}
+
+/*
+ * Create an Output Select enumerated control for codecs with surround
+ * out capabilities.
+ */
+static int ca0132_alt_add_output_enum(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("Output Select",
+ OUTPUT_SOURCE_ENUM, 1, 0, HDA_OUTPUT);
+ knew.info = ca0132_alt_output_select_get_info;
+ knew.get = ca0132_alt_output_select_get;
+ knew.put = ca0132_alt_output_select_put;
+ return snd_hda_ctl_add(codec, OUTPUT_SOURCE_ENUM,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Add a control for selecting channel count on speaker output. Setting this
+ * allows the DSP to do bass redirection and channel upmixing on surround
+ * configurations.
+ */
+static int ca0132_alt_add_speaker_channel_cfg_enum(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("Surround Channel Config",
+ SPEAKER_CHANNEL_CFG_ENUM, 1, 0, HDA_OUTPUT);
+ knew.info = ca0132_alt_speaker_channel_cfg_get_info;
+ knew.get = ca0132_alt_speaker_channel_cfg_get;
+ knew.put = ca0132_alt_speaker_channel_cfg_put;
+ return snd_hda_ctl_add(codec, SPEAKER_CHANNEL_CFG_ENUM,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Full range front stereo and rear surround switches. When these are set to
+ * full range, the lower frequencies from these channels are no longer
+ * redirected to the LFE channel.
+ */
+static int ca0132_alt_add_front_full_range_switch(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ CA0132_CODEC_MUTE_MONO("Full-Range Front Speakers",
+ SPEAKER_FULL_RANGE_FRONT, 1, HDA_OUTPUT);
+
+ return snd_hda_ctl_add(codec, SPEAKER_FULL_RANGE_FRONT,
+ snd_ctl_new1(&knew, codec));
+}
+
+static int ca0132_alt_add_rear_full_range_switch(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ CA0132_CODEC_MUTE_MONO("Full-Range Rear Speakers",
+ SPEAKER_FULL_RANGE_REAR, 1, HDA_OUTPUT);
+
+ return snd_hda_ctl_add(codec, SPEAKER_FULL_RANGE_REAR,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Bass redirection redirects audio below the crossover frequency to the LFE
+ * channel on speakers that are set as not being full-range. On configurations
+ * without an LFE channel, it does nothing. Bass redirection seems to be the
+ * replacement for X-Bass on configurations with an LFE channel.
+ */
+static int ca0132_alt_add_bass_redirection_crossover(struct hda_codec *codec)
+{
+ const char *namestr = "Bass Redirection Crossover";
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_VOLUME_MONO(namestr, BASS_REDIRECTION_XOVER, 1, 0,
+ HDA_OUTPUT);
+
+ knew.tlv.c = NULL;
+ knew.info = ca0132_alt_xbass_xover_slider_info;
+ knew.get = ca0132_alt_xbass_xover_slider_ctl_get;
+ knew.put = ca0132_alt_xbass_xover_slider_put;
+
+ return snd_hda_ctl_add(codec, BASS_REDIRECTION_XOVER,
+ snd_ctl_new1(&knew, codec));
+}
+
+static int ca0132_alt_add_bass_redirection_switch(struct hda_codec *codec)
+{
+ const char *namestr = "Bass Redirection";
+ struct snd_kcontrol_new knew =
+ CA0132_CODEC_MUTE_MONO(namestr, BASS_REDIRECTION, 1,
+ HDA_OUTPUT);
+
+ return snd_hda_ctl_add(codec, BASS_REDIRECTION,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Create an Input Source enumerated control for the alternate ca0132 codecs
+ * because the front microphone has no auto-detect, and Line-in has to be set
+ * somehow.
+ */
+static int ca0132_alt_add_input_enum(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("Input Source",
+ INPUT_SOURCE_ENUM, 1, 0, HDA_INPUT);
+ knew.info = ca0132_alt_input_source_info;
+ knew.get = ca0132_alt_input_source_get;
+ knew.put = ca0132_alt_input_source_put;
+ return snd_hda_ctl_add(codec, INPUT_SOURCE_ENUM,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Add mic boost enumerated control. Switches through 0dB to 30dB. This adds
+ * more control than the original mic boost, which is either full 30dB or off.
+ */
+static int ca0132_alt_add_mic_boost_enum(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("Mic Boost Capture Switch",
+ MIC_BOOST_ENUM, 1, 0, HDA_INPUT);
+ knew.info = ca0132_alt_mic_boost_info;
+ knew.get = ca0132_alt_mic_boost_get;
+ knew.put = ca0132_alt_mic_boost_put;
+ return snd_hda_ctl_add(codec, MIC_BOOST_ENUM,
+ snd_ctl_new1(&knew, codec));
+
+}
+
+/*
+ * Add headphone gain enumerated control for the AE-5. This switches between
+ * three modes, low, medium, and high. When non-headphone outputs are selected,
+ * it is automatically set to high. This is the same behavior as Windows.
+ */
+static int ae5_add_headphone_gain_enum(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("AE-5: Headphone Gain",
+ AE5_HEADPHONE_GAIN_ENUM, 1, 0, HDA_OUTPUT);
+ knew.info = ae5_headphone_gain_info;
+ knew.get = ae5_headphone_gain_get;
+ knew.put = ae5_headphone_gain_put;
+ return snd_hda_ctl_add(codec, AE5_HEADPHONE_GAIN_ENUM,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Add sound filter enumerated control for the AE-5. This adds three different
+ * settings: Slow Roll Off, Minimum Phase, and Fast Roll Off. From what I've
+ * read into it, it changes the DAC's interpolation filter.
+ */
+static int ae5_add_sound_filter_enum(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("AE-5: Sound Filter",
+ AE5_SOUND_FILTER_ENUM, 1, 0, HDA_OUTPUT);
+ knew.info = ae5_sound_filter_info;
+ knew.get = ae5_sound_filter_get;
+ knew.put = ae5_sound_filter_put;
+ return snd_hda_ctl_add(codec, AE5_SOUND_FILTER_ENUM,
+ snd_ctl_new1(&knew, codec));
+}
+
+static int zxr_add_headphone_gain_switch(struct hda_codec *codec)
+{
+ struct snd_kcontrol_new knew =
+ CA0132_CODEC_MUTE_MONO("ZxR: 600 Ohm Gain",
+ ZXR_HEADPHONE_GAIN, 1, HDA_OUTPUT);
+
+ return snd_hda_ctl_add(codec, ZXR_HEADPHONE_GAIN,
+ snd_ctl_new1(&knew, codec));
+}
+
+/*
+ * Need to create follower controls for the alternate codecs that have surround
+ * capabilities.
+ */
+static const char * const ca0132_alt_follower_pfxs[] = {
+ "Front", "Surround", "Center", "LFE", NULL,
+};
+
+/*
+ * Also need special channel map, because the default one is incorrect.
+ * I think this has to do with the pin for rear surround being 0x11,
+ * and the center/lfe being 0x10. Usually the pin order is the opposite.
+ */
+static const struct snd_pcm_chmap_elem ca0132_alt_chmaps[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { .channels = 6,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE,
+ SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
+/* Add the correct chmap for streams with 6 channels. */
+static void ca0132_alt_add_chmap_ctls(struct hda_codec *codec)
+{
+ int err = 0;
+ struct hda_pcm *pcm;
+
+ list_for_each_entry(pcm, &codec->pcm_list_head, list) {
+ struct hda_pcm_stream *hinfo =
+ &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK];
+ struct snd_pcm_chmap *chmap;
+ const struct snd_pcm_chmap_elem *elem;
+
+ elem = ca0132_alt_chmaps;
+ if (hinfo->channels_max == 6) {
+ err = snd_pcm_add_chmap_ctls(pcm->pcm,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ elem, hinfo->channels_max, 0, &chmap);
+ if (err < 0)
+ codec_dbg(codec, "snd_pcm_add_chmap_ctls failed!");
+ }
+ }
+}
+
+/*
+ * When changing Node IDs for Mixer Controls below, make sure to update
+ * Node IDs in ca0132_config() as well.
+ */
+static const struct snd_kcontrol_new ca0132_mixer[] = {
+ CA0132_CODEC_VOL("Master Playback Volume", VNID_SPK, HDA_OUTPUT),
+ CA0132_CODEC_MUTE("Master Playback Switch", VNID_SPK, HDA_OUTPUT),
+ CA0132_CODEC_VOL("Capture Volume", VNID_MIC, HDA_INPUT),
+ CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT),
+ HDA_CODEC_VOLUME("Analog-Mic2 Capture Volume", 0x08, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Analog-Mic2 Capture Switch", 0x08, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT),
+ CA0132_CODEC_MUTE_MONO("Mic1-Boost (30dB) Capture Switch",
+ 0x12, 1, HDA_INPUT),
+ CA0132_CODEC_MUTE_MONO("HP/Speaker Playback Switch",
+ VNID_HP_SEL, 1, HDA_OUTPUT),
+ CA0132_CODEC_MUTE_MONO("AMic1/DMic Capture Switch",
+ VNID_AMIC1_SEL, 1, HDA_INPUT),
+ CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch",
+ VNID_HP_ASEL, 1, HDA_OUTPUT),
+ CA0132_CODEC_MUTE_MONO("AMic1/DMic Auto Detect Capture Switch",
+ VNID_AMIC1_ASEL, 1, HDA_INPUT),
+ { } /* end */
+};
+
+/*
+ * Desktop specific control mixer. Removes auto-detect for mic, and adds
+ * surround controls. Also sets both the Front Playback and Capture Volume
+ * controls to alt so they set the DSP's decibel level.
+ */
+static const struct snd_kcontrol_new desktop_mixer[] = {
+ CA0132_ALT_CODEC_VOL("Front Playback Volume", 0x02, HDA_OUTPUT),
+ CA0132_CODEC_MUTE("Front Playback Switch", VNID_SPK, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x04, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x03, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x03, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x03, 2, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x03, 2, 0, HDA_OUTPUT),
+ CA0132_ALT_CODEC_VOL("Capture Volume", 0x07, HDA_INPUT),
+ CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT),
+ HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT),
+ CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch",
+ VNID_HP_ASEL, 1, HDA_OUTPUT),
+ { } /* end */
+};
+
+/*
+ * Same as the Sound Blaster Z, except doesn't use the alt volume for capture
+ * because it doesn't set decibel levels for the DSP for capture.
+ */
+static const struct snd_kcontrol_new r3di_mixer[] = {
+ CA0132_ALT_CODEC_VOL("Front Playback Volume", 0x02, HDA_OUTPUT),
+ CA0132_CODEC_MUTE("Front Playback Switch", VNID_SPK, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x04, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x03, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x03, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x03, 2, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x03, 2, 0, HDA_OUTPUT),
+ CA0132_CODEC_VOL("Capture Volume", VNID_MIC, HDA_INPUT),
+ CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT),
+ HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT),
+ CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch",
+ VNID_HP_ASEL, 1, HDA_OUTPUT),
+ { } /* end */
+};
+
+static int ca0132_build_controls(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int i, num_fx, num_sliders;
+ int err = 0;
+
+ /* Add Mixer controls */
+ for (i = 0; i < spec->num_mixers; i++) {
+ err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
+ if (err < 0)
+ return err;
+ }
+ /* Setup vmaster with surround followers for desktop ca0132 devices */
+ if (ca0132_use_alt_functions(spec)) {
+ snd_hda_set_vmaster_tlv(codec, spec->dacs[0], HDA_OUTPUT,
+ spec->tlv);
+ snd_hda_add_vmaster(codec, "Master Playback Volume",
+ spec->tlv, ca0132_alt_follower_pfxs,
+ "Playback Volume", 0);
+ err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
+ NULL, ca0132_alt_follower_pfxs,
+ "Playback Switch",
+ true, 0, &spec->vmaster_mute.sw_kctl);
+ if (err < 0)
+ return err;
+ }
+
+ /* Add in and out effects controls.
+ * VoiceFX, PE and CrystalVoice are added separately.
+ */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
+ for (i = 0; i < num_fx; i++) {
+ /* Desktop cards break if Echo Cancellation is used. */
+ if (ca0132_use_pci_mmio(spec)) {
+ if (i == (ECHO_CANCELLATION - IN_EFFECT_START_NID +
+ OUT_EFFECTS_COUNT))
+ continue;
+ }
+
+ err = add_fx_switch(codec, ca0132_effects[i].nid,
+ ca0132_effects[i].name,
+ ca0132_effects[i].direct);
+ if (err < 0)
+ return err;
+ }
+ /*
+ * If codec has use_alt_controls set to true, add effect level sliders,
+ * EQ presets, and Smart Volume presets. Also, change names to add FX
+ * prefix, and change PlayEnhancement and CrystalVoice to match.
+ */
+ if (ca0132_use_alt_controls(spec)) {
+ err = ca0132_alt_add_svm_enum(codec);
+ if (err < 0)
+ return err;
+
+ err = add_ca0132_alt_eq_presets(codec);
+ if (err < 0)
+ return err;
+
+ err = add_fx_switch(codec, PLAY_ENHANCEMENT,
+ "Enable OutFX", 0);
+ if (err < 0)
+ return err;
+
+ err = add_fx_switch(codec, CRYSTAL_VOICE,
+ "Enable InFX", 1);
+ if (err < 0)
+ return err;
+
+ num_sliders = OUT_EFFECTS_COUNT - 1;
+ for (i = 0; i < num_sliders; i++) {
+ err = ca0132_alt_add_effect_slider(codec,
+ ca0132_effects[i].nid,
+ ca0132_effects[i].name,
+ ca0132_effects[i].direct);
+ if (err < 0)
+ return err;
+ }
+
+ err = ca0132_alt_add_effect_slider(codec, XBASS_XOVER,
+ "X-Bass Crossover", EFX_DIR_OUT);
+
+ if (err < 0)
+ return err;
+ } else {
+ err = add_fx_switch(codec, PLAY_ENHANCEMENT,
+ "PlayEnhancement", 0);
+ if (err < 0)
+ return err;
+
+ err = add_fx_switch(codec, CRYSTAL_VOICE,
+ "CrystalVoice", 1);
+ if (err < 0)
+ return err;
+ }
+ err = add_voicefx(codec);
+ if (err < 0)
+ return err;
+
+ /*
+ * If the codec uses alt_functions, you need the enumerated controls
+ * to select the new outputs and inputs, plus add the new mic boost
+ * setting control.
+ */
+ if (ca0132_use_alt_functions(spec)) {
+ err = ca0132_alt_add_output_enum(codec);
+ if (err < 0)
+ return err;
+ err = ca0132_alt_add_speaker_channel_cfg_enum(codec);
+ if (err < 0)
+ return err;
+ err = ca0132_alt_add_front_full_range_switch(codec);
+ if (err < 0)
+ return err;
+ err = ca0132_alt_add_rear_full_range_switch(codec);
+ if (err < 0)
+ return err;
+ err = ca0132_alt_add_bass_redirection_crossover(codec);
+ if (err < 0)
+ return err;
+ err = ca0132_alt_add_bass_redirection_switch(codec);
+ if (err < 0)
+ return err;
+ err = ca0132_alt_add_mic_boost_enum(codec);
+ if (err < 0)
+ return err;
+ /*
+ * ZxR only has microphone input, there is no front panel
+ * header on the card, and aux-in is handled by the DBPro board.
+ */
+ if (ca0132_quirk(spec) != QUIRK_ZXR) {
+ err = ca0132_alt_add_input_enum(codec);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_AE5:
+ case QUIRK_AE7:
+ err = ae5_add_headphone_gain_enum(codec);
+ if (err < 0)
+ return err;
+ err = ae5_add_sound_filter_enum(codec);
+ if (err < 0)
+ return err;
+ break;
+ case QUIRK_ZXR:
+ err = zxr_add_headphone_gain_switch(codec);
+ if (err < 0)
+ return err;
+ break;
+ default:
+ break;
+ }
+
+#ifdef ENABLE_TUNING_CONTROLS
+ add_tuning_ctls(codec);
+#endif
+
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ if (spec->dig_out) {
+ err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out,
+ spec->dig_out);
+ if (err < 0)
+ return err;
+ err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
+ if (err < 0)
+ return err;
+ /* spec->multiout.share_spdif = 1; */
+ }
+
+ if (spec->dig_in) {
+ err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
+ if (err < 0)
+ return err;
+ }
+
+ if (ca0132_use_alt_functions(spec))
+ ca0132_alt_add_chmap_ctls(codec);
+
+ return 0;
+}
+
+static int dbpro_build_controls(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int err = 0;
+
+ if (spec->dig_out) {
+ err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out,
+ spec->dig_out);
+ if (err < 0)
+ return err;
+ }
+
+ if (spec->dig_in) {
+ err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * PCM
+ */
+static const struct hda_pcm_stream ca0132_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 6,
+ .ops = {
+ .prepare = ca0132_playback_pcm_prepare,
+ .cleanup = ca0132_playback_pcm_cleanup,
+ .get_delay = ca0132_playback_pcm_delay,
+ },
+};
+
+static const struct hda_pcm_stream ca0132_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .prepare = ca0132_capture_pcm_prepare,
+ .cleanup = ca0132_capture_pcm_cleanup,
+ .get_delay = ca0132_capture_pcm_delay,
+ },
+};
+
+static const struct hda_pcm_stream ca0132_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .open = ca0132_dig_playback_pcm_open,
+ .close = ca0132_dig_playback_pcm_close,
+ .prepare = ca0132_dig_playback_pcm_prepare,
+ .cleanup = ca0132_dig_playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream ca0132_pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
+static int ca0132_build_pcms(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct hda_pcm *info;
+
+ info = snd_hda_codec_pcm_new(codec, "CA0132 Analog");
+ if (!info)
+ return -ENOMEM;
+ if (ca0132_use_alt_functions(spec)) {
+ info->own_chmap = true;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap
+ = ca0132_alt_chmaps;
+ }
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+ spec->multiout.max_channels;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
+
+ /* With the DSP enabled, desktops don't use this ADC. */
+ if (!ca0132_use_alt_functions(spec)) {
+ info = snd_hda_codec_pcm_new(codec, "CA0132 Analog Mic-In2");
+ if (!info)
+ return -ENOMEM;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ ca0132_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1];
+ }
+
+ info = snd_hda_codec_pcm_new(codec, "CA0132 What U Hear");
+ if (!info)
+ return -ENOMEM;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[2];
+
+ if (!spec->dig_out && !spec->dig_in)
+ return 0;
+
+ info = snd_hda_codec_pcm_new(codec, "CA0132 Digital");
+ if (!info)
+ return -ENOMEM;
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
+ if (spec->dig_out) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ ca0132_pcm_digital_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
+ }
+ if (spec->dig_in) {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ ca0132_pcm_digital_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
+ }
+
+ return 0;
+}
+
+static int dbpro_build_pcms(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct hda_pcm *info;
+
+ info = snd_hda_codec_pcm_new(codec, "CA0132 Alt Analog");
+ if (!info)
+ return -ENOMEM;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
+
+
+ if (!spec->dig_out && !spec->dig_in)
+ return 0;
+
+ info = snd_hda_codec_pcm_new(codec, "CA0132 Digital");
+ if (!info)
+ return -ENOMEM;
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
+ if (spec->dig_out) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ ca0132_pcm_digital_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
+ }
+ if (spec->dig_in) {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ ca0132_pcm_digital_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
+ }
+
+ return 0;
+}
+
+static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
+{
+ if (pin) {
+ snd_hda_set_pin_ctl(codec, pin, PIN_HP);
+ if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+ }
+ if (dac && (get_wcaps(codec, dac) & AC_WCAP_OUT_AMP))
+ snd_hda_codec_write(codec, dac, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
+}
+
+static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
+{
+ if (pin) {
+ snd_hda_set_pin_ctl(codec, pin, PIN_VREF80);
+ if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(0));
+ }
+ if (adc && (get_wcaps(codec, adc) & AC_WCAP_IN_AMP)) {
+ snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(0));
+
+ /* init to 0 dB and unmute. */
+ snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0,
+ HDA_AMP_VOLMASK, 0x5a);
+ snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0,
+ HDA_AMP_MUTE, 0);
+ }
+}
+
+static void refresh_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir)
+{
+ unsigned int caps;
+
+ caps = snd_hda_param_read(codec, nid, dir == HDA_OUTPUT ?
+ AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+ snd_hda_override_amp_caps(codec, nid, dir, caps);
+}
+
+/*
+ * Switch between Digital built-in mic and analog mic.
+ */
+static void ca0132_set_dmic(struct hda_codec *codec, int enable)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+ u8 val;
+ unsigned int oldval;
+
+ codec_dbg(codec, "ca0132_set_dmic: enable=%d\n", enable);
+
+ oldval = stop_mic1(codec);
+ ca0132_set_vipsource(codec, 0);
+ if (enable) {
+ /* set DMic input as 2-ch */
+ tmp = FLOAT_TWO;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ val = spec->dmic_ctl;
+ val |= 0x80;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_CTL_SET, val);
+
+ if (!(spec->dmic_ctl & 0x20))
+ chipio_set_control_flag(codec, CONTROL_FLAG_DMIC, 1);
+ } else {
+ /* set AMic input as mono */
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ val = spec->dmic_ctl;
+ /* clear bit7 and bit5 to disable dmic */
+ val &= 0x5f;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_CTL_SET, val);
+
+ if (!(spec->dmic_ctl & 0x20))
+ chipio_set_control_flag(codec, CONTROL_FLAG_DMIC, 0);
+ }
+ ca0132_set_vipsource(codec, 1);
+ resume_mic1(codec, oldval);
+}
+
+/*
+ * Initialization for Digital Mic.
+ */
+static void ca0132_init_dmic(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ u8 val;
+
+ /* Setup Digital Mic here, but don't enable.
+ * Enable based on jack detect.
+ */
+
+ /* MCLK uses MPIO1, set to enable.
+ * Bit 2-0: MPIO select
+ * Bit 3: set to disable
+ * Bit 7-4: reserved
+ */
+ val = 0x01;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_MCLK_SET, val);
+
+ /* Data1 uses MPIO3. Data2 not use
+ * Bit 2-0: Data1 MPIO select
+ * Bit 3: set disable Data1
+ * Bit 6-4: Data2 MPIO select
+ * Bit 7: set disable Data2
+ */
+ val = 0x83;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_PIN_SET, val);
+
+ /* Use Ch-0 and Ch-1. Rate is 48K, mode 1. Disable DMic first.
+ * Bit 3-0: Channel mask
+ * Bit 4: set for 48KHz, clear for 32KHz
+ * Bit 5: mode
+ * Bit 6: set to select Data2, clear for Data1
+ * Bit 7: set to enable DMic, clear for AMic
+ */
+ if (ca0132_quirk(spec) == QUIRK_ALIENWARE_M17XR4)
+ val = 0x33;
+ else
+ val = 0x23;
+ /* keep a copy of dmic ctl val for enable/disable dmic purpuse */
+ spec->dmic_ctl = val;
+ snd_hda_codec_write(codec, spec->input_pins[0], 0,
+ VENDOR_CHIPIO_DMIC_CTL_SET, val);
+}
+
+/*
+ * Initialization for Analog Mic 2
+ */
+static void ca0132_init_analog_mic2(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ guard(mutex)(&spec->chipio_mutex);
+
+ chipio_8051_write_exram_no_mutex(codec, 0x1920, 0x00);
+ chipio_8051_write_exram_no_mutex(codec, 0x192d, 0x00);
+}
+
+static void ca0132_refresh_widget_caps(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int i;
+
+ codec_dbg(codec, "ca0132_refresh_widget_caps.\n");
+ snd_hda_codec_update_widgets(codec);
+
+ for (i = 0; i < spec->multiout.num_dacs; i++)
+ refresh_amp_caps(codec, spec->dacs[i], HDA_OUTPUT);
+
+ for (i = 0; i < spec->num_outputs; i++)
+ refresh_amp_caps(codec, spec->out_pins[i], HDA_OUTPUT);
+
+ for (i = 0; i < spec->num_inputs; i++) {
+ refresh_amp_caps(codec, spec->adcs[i], HDA_INPUT);
+ refresh_amp_caps(codec, spec->input_pins[i], HDA_INPUT);
+ }
+}
+
+
+/* If there is an active channel for some reason, find it and free it. */
+static void ca0132_alt_free_active_dma_channels(struct hda_codec *codec)
+{
+ unsigned int i, tmp;
+ int status;
+
+ /* Read active DSPDMAC channel register. */
+ status = chipio_read(codec, DSPDMAC_CHNLSTART_MODULE_OFFSET, &tmp);
+ if (status >= 0) {
+ /* AND against 0xfff to get the active channel bits. */
+ tmp = tmp & 0xfff;
+
+ /* If there are no active channels, nothing to free. */
+ if (!tmp)
+ return;
+ } else {
+ codec_dbg(codec, "%s: Failed to read active DSP DMA channel register.\n",
+ __func__);
+ return;
+ }
+
+ /*
+ * Check each DSP DMA channel for activity, and if the channel is
+ * active, free it.
+ */
+ for (i = 0; i < DSPDMAC_DMA_CFG_CHANNEL_COUNT; i++) {
+ if (dsp_is_dma_active(codec, i)) {
+ status = dspio_free_dma_chan(codec, i);
+ if (status < 0)
+ codec_dbg(codec, "%s: Failed to free active DSP DMA channel %d.\n",
+ __func__, i);
+ }
+ }
+}
+
+/*
+ * In the case of CT_EXTENSIONS_ENABLE being set to 1, and the DSP being in
+ * use, audio is no longer routed directly to the DAC/ADC from the HDA stream.
+ * Instead, audio is now routed through the DSP's DMA controllers, which
+ * the DSP is tasked with setting up itself. Through debugging, it seems the
+ * cause of most of the no-audio on startup issues were due to improperly
+ * configured DSP DMA channels.
+ *
+ * Normally, the DSP configures these the first time an HDA audio stream is
+ * started post DSP firmware download. That is why creating a 'dummy' stream
+ * worked in fixing the audio in some cases. This works most of the time, but
+ * sometimes if a stream is started/stopped before the DSP can setup the DMA
+ * configuration registers, it ends up in a broken state. Issues can also
+ * arise if streams are started in an unusual order, i.e the audio output dma
+ * channel being sandwiched between the mic1 and mic2 dma channels.
+ *
+ * The solution to this is to make sure that the DSP has no DMA channels
+ * in use post DSP firmware download, and then to manually start each default
+ * DSP stream that uses the DMA channels. These are 0x0c, the audio output
+ * stream, 0x03, analog mic 1, and 0x04, analog mic 2.
+ */
+static void ca0132_alt_start_dsp_audio_streams(struct hda_codec *codec)
+{
+ static const unsigned int dsp_dma_stream_ids[] = { 0x0c, 0x03, 0x04 };
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int i, tmp;
+
+ /*
+ * Check if any of the default streams are active, and if they are,
+ * stop them.
+ */
+ scoped_guard(mutex, &spec->chipio_mutex) {
+ for (i = 0; i < ARRAY_SIZE(dsp_dma_stream_ids); i++) {
+ chipio_get_stream_control(codec, dsp_dma_stream_ids[i], &tmp);
+
+ if (tmp) {
+ chipio_set_stream_control(codec,
+ dsp_dma_stream_ids[i], 0);
+ }
+ }
+ }
+
+ /*
+ * If all DSP streams are inactive, there should be no active DSP DMA
+ * channels. Check and make sure this is the case, and if it isn't,
+ * free any active channels.
+ */
+ ca0132_alt_free_active_dma_channels(codec);
+
+ guard(mutex)(&spec->chipio_mutex);
+
+ /* Make sure stream 0x0c is six channels. */
+ chipio_set_stream_channels(codec, 0x0c, 6);
+
+ for (i = 0; i < ARRAY_SIZE(dsp_dma_stream_ids); i++) {
+ chipio_set_stream_control(codec,
+ dsp_dma_stream_ids[i], 1);
+
+ /* Give the DSP some time to setup the DMA channel. */
+ msleep(75);
+ }
+}
+
+/*
+ * The region of ChipIO memory from 0x190000-0x1903fc is a sort of 'audio
+ * router', where each entry represents a 48khz audio channel, with a format
+ * of an 8-bit destination, an 8-bit source, and an unknown 2-bit number
+ * value. The 2-bit number value is seemingly 0 if inactive, 1 if active,
+ * and 3 if it's using Sample Rate Converter ports.
+ * An example is:
+ * 0x0001f8c0
+ * In this case, f8 is the destination, and c0 is the source. The number value
+ * is 1.
+ * This region of memory is normally managed internally by the 8051, where
+ * the region of exram memory from 0x1477-0x1575 has each byte represent an
+ * entry within the 0x190000 range, and when a range of entries is in use, the
+ * ending value is overwritten with 0xff.
+ * 0x1578 in exram is a table of 0x25 entries, corresponding to the ChipIO
+ * streamID's, where each entry is a starting 0x190000 port offset.
+ * 0x159d in exram is the same as 0x1578, except it contains the ending port
+ * offset for the corresponding streamID.
+ *
+ * On certain cards, such as the SBZ/ZxR/AE7, these are originally setup by
+ * the 8051, then manually overwritten to remap the ports to work with the
+ * new DACs.
+ *
+ * Currently known portID's:
+ * 0x00-0x1f: HDA audio stream input/output ports.
+ * 0x80-0xbf: Sample rate converter input/outputs. Only valid ports seem to
+ * have the lower-nibble set to 0x1, 0x2, and 0x9.
+ * 0xc0-0xdf: DSP DMA input/output ports. Dynamically assigned.
+ * 0xe0-0xff: DAC/ADC audio input/output ports.
+ *
+ * Currently known streamID's:
+ * 0x03: Mic1 ADC to DSP.
+ * 0x04: Mic2 ADC to DSP.
+ * 0x05: HDA node 0x02 audio stream to DSP.
+ * 0x0f: DSP Mic exit to HDA node 0x07.
+ * 0x0c: DSP processed audio to DACs.
+ * 0x14: DAC0, front L/R.
+ *
+ * It is possible to route the HDA audio streams directly to the DAC and
+ * bypass the DSP entirely, with the only downside being that since the DSP
+ * does volume control, the only volume control you'll get is through PCM on
+ * the PC side, in the same way volume is handled for optical out. This may be
+ * useful for debugging.
+ */
+static void chipio_remap_stream(struct hda_codec *codec,
+ const struct chipio_stream_remap_data *remap_data)
+{
+ unsigned int i, stream_offset;
+
+ /* Get the starting port for the stream to be remapped. */
+ chipio_8051_read_exram(codec, 0x1578 + remap_data->stream_id,
+ &stream_offset);
+
+ /*
+ * Check if the stream's port value is 0xff, because the 8051 may not
+ * have gotten around to setting up the stream yet. Wait until it's
+ * setup to remap it's ports.
+ */
+ if (stream_offset == 0xff) {
+ for (i = 0; i < 5; i++) {
+ msleep(25);
+
+ chipio_8051_read_exram(codec, 0x1578 + remap_data->stream_id,
+ &stream_offset);
+
+ if (stream_offset != 0xff)
+ break;
+ }
+ }
+
+ if (stream_offset == 0xff) {
+ codec_info(codec, "%s: Stream 0x%02x ports aren't allocated, remap failed!\n",
+ __func__, remap_data->stream_id);
+ return;
+ }
+
+ /* Offset isn't in bytes, its in 32-bit words, so multiply it by 4. */
+ stream_offset *= 0x04;
+ stream_offset += 0x190000;
+
+ for (i = 0; i < remap_data->count; i++) {
+ chipio_write_no_mutex(codec,
+ stream_offset + remap_data->offset[i],
+ remap_data->value[i]);
+ }
+
+ /* Update stream map configuration. */
+ chipio_write_no_mutex(codec, 0x19042c, 0x00000001);
+}
+
+/*
+ * Default speaker tuning values setup for alternative codecs.
+ */
+static const unsigned int sbz_default_delay_values[] = {
+ /* Non-zero values are floating point 0.000198. */
+ 0x394f9e38, 0x394f9e38, 0x00000000, 0x00000000, 0x00000000, 0x00000000
+};
+
+static const unsigned int zxr_default_delay_values[] = {
+ /* Non-zero values are floating point 0.000220. */
+ 0x00000000, 0x00000000, 0x3966afcd, 0x3966afcd, 0x3966afcd, 0x3966afcd
+};
+
+static const unsigned int ae5_default_delay_values[] = {
+ /* Non-zero values are floating point 0.000100. */
+ 0x00000000, 0x00000000, 0x38d1b717, 0x38d1b717, 0x38d1b717, 0x38d1b717
+};
+
+/*
+ * If we never change these, probably only need them on initialization.
+ */
+static void ca0132_alt_init_speaker_tuning(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int i, tmp, start_req, end_req;
+ const unsigned int *values;
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ values = sbz_default_delay_values;
+ break;
+ case QUIRK_ZXR:
+ values = zxr_default_delay_values;
+ break;
+ case QUIRK_AE5:
+ case QUIRK_AE7:
+ values = ae5_default_delay_values;
+ break;
+ default:
+ values = sbz_default_delay_values;
+ break;
+ }
+
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96, SPEAKER_TUNING_ENABLE_CENTER_EQ, tmp);
+
+ start_req = SPEAKER_TUNING_FRONT_LEFT_VOL_LEVEL;
+ end_req = SPEAKER_TUNING_REAR_RIGHT_VOL_LEVEL;
+ for (i = start_req; i < end_req + 1; i++)
+ dspio_set_uint_param(codec, 0x96, i, tmp);
+
+ start_req = SPEAKER_TUNING_FRONT_LEFT_INVERT;
+ end_req = SPEAKER_TUNING_REAR_RIGHT_INVERT;
+ for (i = start_req; i < end_req + 1; i++)
+ dspio_set_uint_param(codec, 0x96, i, tmp);
+
+
+ for (i = 0; i < 6; i++)
+ dspio_set_uint_param(codec, 0x96,
+ SPEAKER_TUNING_FRONT_LEFT_DELAY + i, values[i]);
+}
+
+/*
+ * Initialize mic for non-chromebook ca0132 implementations.
+ */
+static void ca0132_alt_init_analog_mics(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+
+ /* Mic 1 Setup */
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ if (ca0132_quirk(spec) == QUIRK_R3DI) {
+ chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+ tmp = FLOAT_ONE;
+ } else
+ tmp = FLOAT_THREE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ /* Mic 2 setup (not present on desktop cards) */
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, SR_96_000);
+ if (ca0132_quirk(spec) == QUIRK_R3DI)
+ chipio_set_conn_rate(codec, 0x0F, SR_96_000);
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x80, 0x01, tmp);
+}
+
+/*
+ * Sets the source of stream 0x14 to connpointID 0x48, and the destination
+ * connpointID to 0x91. If this isn't done, the destination is 0x71, and
+ * you get no sound. I'm guessing this has to do with the Sound Blaster Z
+ * having an updated DAC, which changes the destination to that DAC.
+ */
+static void sbz_connect_streams(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ guard(mutex)(&spec->chipio_mutex);
+
+ codec_dbg(codec, "Connect Streams entered, mutex locked and loaded.\n");
+
+ /* This value is 0x43 for 96khz, and 0x83 for 192khz. */
+ chipio_write_no_mutex(codec, 0x18a020, 0x00000043);
+
+ /* Setup stream 0x14 with it's source and destination points */
+ chipio_set_stream_source_dest(codec, 0x14, 0x48, 0x91);
+ chipio_set_conn_rate_no_mutex(codec, 0x48, SR_96_000);
+ chipio_set_conn_rate_no_mutex(codec, 0x91, SR_96_000);
+ chipio_set_stream_channels(codec, 0x14, 2);
+ chipio_set_stream_control(codec, 0x14, 1);
+
+ codec_dbg(codec, "Connect Streams exited, mutex released.\n");
+}
+
+/*
+ * Write data through ChipIO to setup proper stream destinations.
+ * Not sure how it exactly works, but it seems to direct data
+ * to different destinations. Example is f8 to c0, e0 to c0.
+ * All I know is, if you don't set these, you get no sound.
+ */
+static void sbz_chipio_startup_data(struct hda_codec *codec)
+{
+ const struct chipio_stream_remap_data *dsp_out_remap_data;
+ struct ca0132_spec *spec = codec->spec;
+
+ guard(mutex)(&spec->chipio_mutex);
+ codec_dbg(codec, "Startup Data entered, mutex locked and loaded.\n");
+
+ /* Remap DAC0's output ports. */
+ chipio_remap_stream(codec, &stream_remap_data[0]);
+
+ /* Remap DSP audio output stream ports. */
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ dsp_out_remap_data = &stream_remap_data[1];
+ break;
+
+ case QUIRK_ZXR:
+ dsp_out_remap_data = &stream_remap_data[2];
+ break;
+
+ default:
+ dsp_out_remap_data = NULL;
+ break;
+ }
+
+ if (dsp_out_remap_data)
+ chipio_remap_stream(codec, dsp_out_remap_data);
+
+ codec_dbg(codec, "Startup Data exited, mutex released.\n");
+}
+
+static void ca0132_alt_dsp_initial_mic_setup(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+
+ chipio_set_stream_control(codec, 0x03, 0);
+ chipio_set_stream_control(codec, 0x04, 0);
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+
+ tmp = FLOAT_THREE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+
+ chipio_set_stream_control(codec, 0x03, 1);
+ chipio_set_stream_control(codec, 0x04, 1);
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ chipio_write(codec, 0x18b098, 0x0000000c);
+ chipio_write(codec, 0x18b09C, 0x0000000c);
+ break;
+ case QUIRK_AE5:
+ chipio_write(codec, 0x18b098, 0x0000000c);
+ chipio_write(codec, 0x18b09c, 0x0000004c);
+ break;
+ default:
+ break;
+ }
+}
+
+static void ae5_post_dsp_register_set(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ chipio_8051_write_direct(codec, 0x93, 0x10);
+ chipio_8051_write_pll_pmu(codec, 0x44, 0xc2);
+
+ writeb(0xff, spec->mem_base + 0x304);
+ writeb(0xff, spec->mem_base + 0x304);
+ writeb(0xff, spec->mem_base + 0x304);
+ writeb(0xff, spec->mem_base + 0x304);
+ writeb(0x00, spec->mem_base + 0x100);
+ writeb(0xff, spec->mem_base + 0x304);
+ writeb(0x00, spec->mem_base + 0x100);
+ writeb(0xff, spec->mem_base + 0x304);
+ writeb(0x00, spec->mem_base + 0x100);
+ writeb(0xff, spec->mem_base + 0x304);
+ writeb(0x00, spec->mem_base + 0x100);
+ writeb(0xff, spec->mem_base + 0x304);
+
+ ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x3f);
+ ca0113_mmio_command_set(codec, 0x30, 0x2d, 0x3f);
+ ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83);
+}
+
+static void ae5_post_dsp_param_setup(struct hda_codec *codec)
+{
+ /*
+ * Param3 in the 8051's memory is represented by the ascii string 'mch'
+ * which seems to be 'multichannel'. This is also mentioned in the
+ * AE-5's registry values in Windows.
+ */
+ chipio_set_control_param(codec, 3, 0);
+ /*
+ * I believe ASI is 'audio serial interface' and that it's used to
+ * change colors on the external LED strip connected to the AE-5.
+ */
+ chipio_set_control_flag(codec, CONTROL_FLAG_ASI_96KHZ, 1);
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x724, 0x83);
+ chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0);
+
+ chipio_8051_write_exram(codec, 0xfa92, 0x22);
+}
+
+static void ae5_post_dsp_pll_setup(struct hda_codec *codec)
+{
+ chipio_8051_write_pll_pmu(codec, 0x41, 0xc8);
+ chipio_8051_write_pll_pmu(codec, 0x45, 0xcc);
+ chipio_8051_write_pll_pmu(codec, 0x40, 0xcb);
+ chipio_8051_write_pll_pmu(codec, 0x43, 0xc7);
+ chipio_8051_write_pll_pmu(codec, 0x51, 0x8d);
+}
+
+static void ae5_post_dsp_stream_setup(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ guard(mutex)(&spec->chipio_mutex);
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x725, 0x81);
+
+ chipio_set_conn_rate_no_mutex(codec, 0x70, SR_96_000);
+
+ chipio_set_stream_source_dest(codec, 0x5, 0x43, 0x0);
+
+ chipio_set_stream_source_dest(codec, 0x18, 0x9, 0xd0);
+ chipio_set_conn_rate_no_mutex(codec, 0xd0, SR_96_000);
+ chipio_set_stream_channels(codec, 0x18, 6);
+ chipio_set_stream_control(codec, 0x18, 1);
+
+ chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 4);
+
+ chipio_8051_write_pll_pmu_no_mutex(codec, 0x43, 0xc7);
+
+ ca0113_mmio_command_set(codec, 0x48, 0x01, 0x80);
+}
+
+static void ae5_post_dsp_startup_data(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ guard(mutex)(&spec->chipio_mutex);
+
+ chipio_write_no_mutex(codec, 0x189000, 0x0001f101);
+ chipio_write_no_mutex(codec, 0x189004, 0x0001f101);
+ chipio_write_no_mutex(codec, 0x189024, 0x00014004);
+ chipio_write_no_mutex(codec, 0x189028, 0x0002000f);
+
+ ca0113_mmio_command_set(codec, 0x48, 0x0a, 0x05);
+ chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 7);
+ ca0113_mmio_command_set(codec, 0x48, 0x0b, 0x12);
+ ca0113_mmio_command_set(codec, 0x48, 0x04, 0x00);
+ ca0113_mmio_command_set(codec, 0x48, 0x06, 0x48);
+ ca0113_mmio_command_set(codec, 0x48, 0x0a, 0x05);
+ ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83);
+ ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00);
+ ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00);
+ ca0113_mmio_gpio_set(codec, 0, true);
+ ca0113_mmio_gpio_set(codec, 1, true);
+ ca0113_mmio_command_set(codec, 0x48, 0x07, 0x80);
+
+ chipio_write_no_mutex(codec, 0x18b03c, 0x00000012);
+
+ ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00);
+ ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00);
+}
+
+static void ae7_post_dsp_setup_ports(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ guard(mutex)(&spec->chipio_mutex);
+
+ /* Seems to share the same port remapping as the SBZ. */
+ chipio_remap_stream(codec, &stream_remap_data[1]);
+
+ ca0113_mmio_command_set(codec, 0x30, 0x30, 0x00);
+ ca0113_mmio_command_set(codec, 0x48, 0x0d, 0x40);
+ ca0113_mmio_command_set(codec, 0x48, 0x17, 0x00);
+ ca0113_mmio_command_set(codec, 0x48, 0x19, 0x00);
+ ca0113_mmio_command_set(codec, 0x48, 0x11, 0xff);
+ ca0113_mmio_command_set(codec, 0x48, 0x12, 0xff);
+ ca0113_mmio_command_set(codec, 0x48, 0x13, 0xff);
+ ca0113_mmio_command_set(codec, 0x48, 0x14, 0x7f);
+}
+
+static void ae7_post_dsp_asi_stream_setup(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ guard(mutex)(&spec->chipio_mutex);
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x725, 0x81);
+ ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x00);
+
+ chipio_set_conn_rate_no_mutex(codec, 0x70, SR_96_000);
+
+ chipio_set_stream_source_dest(codec, 0x05, 0x43, 0x00);
+ chipio_set_stream_source_dest(codec, 0x18, 0x09, 0xd0);
+
+ chipio_set_conn_rate_no_mutex(codec, 0xd0, SR_96_000);
+ chipio_set_stream_channels(codec, 0x18, 6);
+ chipio_set_stream_control(codec, 0x18, 1);
+
+ chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 4);
+}
+
+static void ae7_post_dsp_pll_setup(struct hda_codec *codec)
+{
+ static const unsigned int addr[] = {
+ 0x41, 0x45, 0x40, 0x43, 0x51
+ };
+ static const unsigned int data[] = {
+ 0xc8, 0xcc, 0xcb, 0xc7, 0x8d
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(addr); i++)
+ chipio_8051_write_pll_pmu_no_mutex(codec, addr[i], data[i]);
+}
+
+static void ae7_post_dsp_asi_setup_ports(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ static const unsigned int target[] = {
+ 0x0b, 0x04, 0x06, 0x0a, 0x0c, 0x11, 0x12, 0x13, 0x14
+ };
+ static const unsigned int data[] = {
+ 0x12, 0x00, 0x48, 0x05, 0x5f, 0xff, 0xff, 0xff, 0x7f
+ };
+ unsigned int i;
+
+ guard(mutex)(&spec->chipio_mutex);
+
+ chipio_8051_write_pll_pmu_no_mutex(codec, 0x43, 0xc7);
+
+ chipio_write_no_mutex(codec, 0x189000, 0x0001f101);
+ chipio_write_no_mutex(codec, 0x189004, 0x0001f101);
+ chipio_write_no_mutex(codec, 0x189024, 0x00014004);
+ chipio_write_no_mutex(codec, 0x189028, 0x0002000f);
+
+ ae7_post_dsp_pll_setup(codec);
+ chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 7);
+
+ for (i = 0; i < ARRAY_SIZE(target); i++)
+ ca0113_mmio_command_set(codec, 0x48, target[i], data[i]);
+
+ ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83);
+ ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00);
+ ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00);
+
+ chipio_set_stream_source_dest(codec, 0x21, 0x64, 0x56);
+ chipio_set_stream_channels(codec, 0x21, 2);
+ chipio_set_conn_rate_no_mutex(codec, 0x56, SR_8_000);
+
+ chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_NODE_ID, 0x09);
+ /*
+ * In the 8051's memory, this param is referred to as 'n2sid', which I
+ * believe is 'node to streamID'. It seems to be a way to assign a
+ * stream to a given HDA node.
+ */
+ chipio_set_control_param_no_mutex(codec, 0x20, 0x21);
+
+ chipio_write_no_mutex(codec, 0x18b038, 0x00000088);
+
+ /*
+ * Now, at this point on Windows, an actual stream is setup and
+ * seemingly sends data to the HDA node 0x09, which is the digital
+ * audio input node. This is left out here, because obviously I don't
+ * know what data is being sent. Interestingly, the AE-5 seems to go
+ * through the motions of getting here and never actually takes this
+ * step, but the AE-7 does.
+ */
+
+ ca0113_mmio_gpio_set(codec, 0, 1);
+ ca0113_mmio_gpio_set(codec, 1, 1);
+
+ ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83);
+ chipio_write_no_mutex(codec, 0x18b03c, 0x00000000);
+ ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00);
+ ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00);
+
+ chipio_set_stream_source_dest(codec, 0x05, 0x43, 0x00);
+ chipio_set_stream_source_dest(codec, 0x18, 0x09, 0xd0);
+
+ chipio_set_conn_rate_no_mutex(codec, 0xd0, SR_96_000);
+ chipio_set_stream_channels(codec, 0x18, 6);
+
+ /*
+ * Runs again, this has been repeated a few times, but I'm just
+ * following what the Windows driver does.
+ */
+ ae7_post_dsp_pll_setup(codec);
+ chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 7);
+}
+
+/*
+ * The Windows driver has commands that seem to setup ASI, which I believe to
+ * be some sort of audio serial interface. My current speculation is that it's
+ * related to communicating with the new DAC.
+ */
+static void ae7_post_dsp_asi_setup(struct hda_codec *codec)
+{
+ chipio_8051_write_direct(codec, 0x93, 0x10);
+
+ chipio_8051_write_pll_pmu(codec, 0x44, 0xc2);
+
+ ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83);
+ ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f);
+
+ chipio_set_control_param(codec, 3, 3);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ASI_96KHZ, 1);
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x724, 0x83);
+ chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0);
+ snd_hda_codec_write(codec, 0x17, 0, 0x794, 0x00);
+
+ chipio_8051_write_exram(codec, 0xfa92, 0x22);
+
+ ae7_post_dsp_pll_setup(codec);
+ ae7_post_dsp_asi_stream_setup(codec);
+
+ chipio_8051_write_pll_pmu(codec, 0x43, 0xc7);
+
+ ae7_post_dsp_asi_setup_ports(codec);
+}
+
+/*
+ * Setup default parameters for DSP
+ */
+static void ca0132_setup_defaults(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+ int num_fx;
+ int idx, i;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return;
+
+ /* out, in effects + voicefx */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1;
+ for (idx = 0; idx < num_fx; idx++) {
+ for (i = 0; i <= ca0132_effects[idx].params; i++) {
+ dspio_set_uint_param(codec, ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[i],
+ ca0132_effects[idx].def_vals[i]);
+ }
+ }
+
+ /*remove DSP headroom*/
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96, 0x3C, tmp);
+
+ /*set speaker EQ bypass attenuation*/
+ dspio_set_uint_param(codec, 0x8f, 0x01, tmp);
+
+ /* set AMic1 and AMic2 as mono mic */
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x00, tmp);
+ dspio_set_uint_param(codec, 0x80, 0x01, tmp);
+
+ /* set AMic1 as CrystalVoice input */
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+
+ /* set WUH source */
+ tmp = FLOAT_TWO;
+ dspio_set_uint_param(codec, 0x31, 0x00, tmp);
+}
+
+/*
+ * Setup default parameters for Recon3D/Recon3Di DSP.
+ */
+
+static void r3d_setup_defaults(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+ int num_fx;
+ int idx, i;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return;
+
+ ca0132_alt_init_analog_mics(codec);
+ ca0132_alt_start_dsp_audio_streams(codec);
+
+ /*remove DSP headroom*/
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96, 0x3C, tmp);
+
+ /* set WUH source */
+ tmp = FLOAT_TWO;
+ dspio_set_uint_param(codec, 0x31, 0x00, tmp);
+ chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+
+ /* Set speaker source? */
+ dspio_set_uint_param(codec, 0x32, 0x00, tmp);
+
+ if (ca0132_quirk(spec) == QUIRK_R3DI)
+ r3di_gpio_dsp_status_set(codec, R3DI_DSP_DOWNLOADED);
+
+ /* Disable mute on Center/LFE. */
+ if (ca0132_quirk(spec) == QUIRK_R3D) {
+ ca0113_mmio_gpio_set(codec, 2, false);
+ ca0113_mmio_gpio_set(codec, 4, true);
+ }
+
+ /* Setup effect defaults */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1;
+ for (idx = 0; idx < num_fx; idx++) {
+ for (i = 0; i <= ca0132_effects[idx].params; i++) {
+ dspio_set_uint_param(codec,
+ ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[i],
+ ca0132_effects[idx].def_vals[i]);
+ }
+ }
+}
+
+/*
+ * Setup default parameters for the Sound Blaster Z DSP. A lot more going on
+ * than the Chromebook setup.
+ */
+static void sbz_setup_defaults(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+ int num_fx;
+ int idx, i;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return;
+
+ ca0132_alt_init_analog_mics(codec);
+ ca0132_alt_start_dsp_audio_streams(codec);
+ sbz_connect_streams(codec);
+ sbz_chipio_startup_data(codec);
+
+ /*
+ * Sets internal input loopback to off, used to have a switch to
+ * enable input loopback, but turned out to be way too buggy.
+ */
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x37, 0x08, tmp);
+ dspio_set_uint_param(codec, 0x37, 0x10, tmp);
+
+ /*remove DSP headroom*/
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96, 0x3C, tmp);
+
+ /* set WUH source */
+ tmp = FLOAT_TWO;
+ dspio_set_uint_param(codec, 0x31, 0x00, tmp);
+ chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+
+ /* Set speaker source? */
+ dspio_set_uint_param(codec, 0x32, 0x00, tmp);
+
+ ca0132_alt_dsp_initial_mic_setup(codec);
+
+ /* out, in effects + voicefx */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1;
+ for (idx = 0; idx < num_fx; idx++) {
+ for (i = 0; i <= ca0132_effects[idx].params; i++) {
+ dspio_set_uint_param(codec,
+ ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[i],
+ ca0132_effects[idx].def_vals[i]);
+ }
+ }
+
+ ca0132_alt_init_speaker_tuning(codec);
+}
+
+/*
+ * Setup default parameters for the Sound BlasterX AE-5 DSP.
+ */
+static void ae5_setup_defaults(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+ int num_fx;
+ int idx, i;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return;
+
+ ca0132_alt_init_analog_mics(codec);
+ ca0132_alt_start_dsp_audio_streams(codec);
+
+ /* New, unknown SCP req's */
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96, 0x29, tmp);
+ dspio_set_uint_param(codec, 0x96, 0x2a, tmp);
+ dspio_set_uint_param(codec, 0x80, 0x0d, tmp);
+ dspio_set_uint_param(codec, 0x80, 0x0e, tmp);
+
+ ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f);
+ ca0113_mmio_gpio_set(codec, 0, false);
+ ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00);
+
+ /* Internal loopback off */
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x37, 0x08, tmp);
+ dspio_set_uint_param(codec, 0x37, 0x10, tmp);
+
+ /*remove DSP headroom*/
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96, 0x3C, tmp);
+
+ /* set WUH source */
+ tmp = FLOAT_TWO;
+ dspio_set_uint_param(codec, 0x31, 0x00, tmp);
+ chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+
+ /* Set speaker source? */
+ dspio_set_uint_param(codec, 0x32, 0x00, tmp);
+
+ ca0132_alt_dsp_initial_mic_setup(codec);
+ ae5_post_dsp_register_set(codec);
+ ae5_post_dsp_param_setup(codec);
+ ae5_post_dsp_pll_setup(codec);
+ ae5_post_dsp_stream_setup(codec);
+ ae5_post_dsp_startup_data(codec);
+
+ /* out, in effects + voicefx */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1;
+ for (idx = 0; idx < num_fx; idx++) {
+ for (i = 0; i <= ca0132_effects[idx].params; i++) {
+ dspio_set_uint_param(codec,
+ ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[i],
+ ca0132_effects[idx].def_vals[i]);
+ }
+ }
+
+ ca0132_alt_init_speaker_tuning(codec);
+}
+
+/*
+ * Setup default parameters for the Sound Blaster AE-7 DSP.
+ */
+static void ae7_setup_defaults(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp;
+ int num_fx;
+ int idx, i;
+
+ if (spec->dsp_state != DSP_DOWNLOADED)
+ return;
+
+ ca0132_alt_init_analog_mics(codec);
+ ca0132_alt_start_dsp_audio_streams(codec);
+ ae7_post_dsp_setup_ports(codec);
+
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96,
+ SPEAKER_TUNING_FRONT_LEFT_INVERT, tmp);
+ dspio_set_uint_param(codec, 0x96,
+ SPEAKER_TUNING_FRONT_RIGHT_INVERT, tmp);
+
+ ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f);
+
+ /* New, unknown SCP req's */
+ dspio_set_uint_param(codec, 0x80, 0x0d, tmp);
+ dspio_set_uint_param(codec, 0x80, 0x0e, tmp);
+
+ ca0113_mmio_gpio_set(codec, 0, false);
+
+ /* Internal loopback off */
+ tmp = FLOAT_ONE;
+ dspio_set_uint_param(codec, 0x37, 0x08, tmp);
+ dspio_set_uint_param(codec, 0x37, 0x10, tmp);
+
+ /*remove DSP headroom*/
+ tmp = FLOAT_ZERO;
+ dspio_set_uint_param(codec, 0x96, 0x3C, tmp);
+
+ /* set WUH source */
+ tmp = FLOAT_TWO;
+ dspio_set_uint_param(codec, 0x31, 0x00, tmp);
+ chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+
+ /* Set speaker source? */
+ dspio_set_uint_param(codec, 0x32, 0x00, tmp);
+ ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00);
+
+ /*
+ * This is the second time we've called this, but this is seemingly
+ * what Windows does.
+ */
+ ca0132_alt_init_analog_mics(codec);
+
+ ae7_post_dsp_asi_setup(codec);
+
+ /*
+ * Not sure why, but these are both set to 1. They're only set to 0
+ * upon shutdown.
+ */
+ ca0113_mmio_gpio_set(codec, 0, true);
+ ca0113_mmio_gpio_set(codec, 1, true);
+
+ /* Volume control related. */
+ ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x04);
+ ca0113_mmio_command_set(codec, 0x48, 0x10, 0x04);
+ ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x80);
+
+ /* out, in effects + voicefx */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1;
+ for (idx = 0; idx < num_fx; idx++) {
+ for (i = 0; i <= ca0132_effects[idx].params; i++) {
+ dspio_set_uint_param(codec,
+ ca0132_effects[idx].mid,
+ ca0132_effects[idx].reqs[i],
+ ca0132_effects[idx].def_vals[i]);
+ }
+ }
+
+ ca0132_alt_init_speaker_tuning(codec);
+}
+
+/*
+ * Initialization of flags in chip
+ */
+static void ca0132_init_flags(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (ca0132_use_alt_functions(spec)) {
+ chipio_set_control_flag(codec, CONTROL_FLAG_DSP_96KHZ, 1);
+ chipio_set_control_flag(codec, CONTROL_FLAG_DAC_96KHZ, 1);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, 1);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_96KHZ, 1);
+ chipio_set_control_flag(codec, CONTROL_FLAG_SRC_RATE_96KHZ, 1);
+ chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0);
+ chipio_set_control_flag(codec, CONTROL_FLAG_SPDIF2OUT, 0);
+ chipio_set_control_flag(codec,
+ CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0);
+ chipio_set_control_flag(codec,
+ CONTROL_FLAG_PORT_A_10KOHM_LOAD, 1);
+ } else {
+ chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0);
+ chipio_set_control_flag(codec,
+ CONTROL_FLAG_PORT_A_COMMON_MODE, 0);
+ chipio_set_control_flag(codec,
+ CONTROL_FLAG_PORT_D_COMMON_MODE, 0);
+ chipio_set_control_flag(codec,
+ CONTROL_FLAG_PORT_A_10KOHM_LOAD, 0);
+ chipio_set_control_flag(codec,
+ CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_HIGH_PASS, 1);
+ }
+}
+
+/*
+ * Initialization of parameters in chip
+ */
+static void ca0132_init_params(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (ca0132_use_alt_functions(spec)) {
+ chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+ chipio_set_conn_rate(codec, 0x0B, SR_48_000);
+ chipio_set_control_param(codec, CONTROL_PARAM_SPDIF1_SOURCE, 0);
+ chipio_set_control_param(codec, 0, 0);
+ chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0);
+ }
+
+ chipio_set_control_param(codec, CONTROL_PARAM_PORTA_160OHM_GAIN, 6);
+ chipio_set_control_param(codec, CONTROL_PARAM_PORTD_160OHM_GAIN, 6);
+}
+
+static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k)
+{
+ chipio_set_control_flag(codec, CONTROL_FLAG_DSP_96KHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_DAC_96KHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_SRC_RATE_96KHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_SRC_CLOCK_196MHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, is96k);
+ chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_96KHZ, is96k);
+
+ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
+ chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+}
+
+static bool ca0132_download_dsp_images(struct hda_codec *codec)
+{
+ bool dsp_loaded = false;
+ struct ca0132_spec *spec = codec->spec;
+ const struct dsp_image_seg *dsp_os_image;
+ const struct firmware *fw_entry = NULL;
+ /*
+ * Alternate firmwares for different variants. The Recon3Di apparently
+ * can use the default firmware, but I'll leave the option in case
+ * it needs it again.
+ */
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ case QUIRK_R3D:
+ case QUIRK_AE5:
+ if (request_firmware(&fw_entry, DESKTOP_EFX_FILE,
+ codec->card->dev) != 0)
+ codec_dbg(codec, "Desktop firmware not found.");
+ else
+ codec_dbg(codec, "Desktop firmware selected.");
+ break;
+ case QUIRK_R3DI:
+ if (request_firmware(&fw_entry, R3DI_EFX_FILE,
+ codec->card->dev) != 0)
+ codec_dbg(codec, "Recon3Di alt firmware not detected.");
+ else
+ codec_dbg(codec, "Recon3Di firmware selected.");
+ break;
+ default:
+ break;
+ }
+ /*
+ * Use default ctefx.bin if no alt firmware is detected, or if none
+ * exists for your particular codec.
+ */
+ if (!fw_entry) {
+ codec_dbg(codec, "Default firmware selected.");
+ if (request_firmware(&fw_entry, EFX_FILE,
+ codec->card->dev) != 0)
+ return false;
+ }
+
+ dsp_os_image = (struct dsp_image_seg *)(fw_entry->data);
+ if (dspload_image(codec, dsp_os_image, 0, 0, true, 0)) {
+ codec_err(codec, "ca0132 DSP load image failed\n");
+ goto exit_download;
+ }
+
+ dsp_loaded = dspload_wait_loaded(codec);
+
+exit_download:
+ release_firmware(fw_entry);
+
+ return dsp_loaded;
+}
+
+static void ca0132_download_dsp(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+#ifndef CONFIG_SND_HDA_CODEC_CA0132_DSP
+ return; /* NOP */
+#endif
+
+ if (spec->dsp_state == DSP_DOWNLOAD_FAILED)
+ return; /* don't retry failures */
+
+ chipio_enable_clocks(codec);
+ if (spec->dsp_state != DSP_DOWNLOADED) {
+ spec->dsp_state = DSP_DOWNLOADING;
+
+ if (!ca0132_download_dsp_images(codec))
+ spec->dsp_state = DSP_DOWNLOAD_FAILED;
+ else
+ spec->dsp_state = DSP_DOWNLOADED;
+ }
+
+ /* For codecs using alt functions, this is already done earlier */
+ if (spec->dsp_state == DSP_DOWNLOADED && !ca0132_use_alt_functions(spec))
+ ca0132_set_dsp_msr(codec, true);
+}
+
+static void ca0132_process_dsp_response(struct hda_codec *codec,
+ struct hda_jack_callback *callback)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ codec_dbg(codec, "ca0132_process_dsp_response\n");
+ CLASS(snd_hda_power_pm, pm)(codec);
+ if (spec->wait_scp) {
+ if (dspio_get_response_data(codec) >= 0)
+ spec->wait_scp = 0;
+ }
+
+ dspio_clear_response_queue(codec);
+}
+
+static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct hda_jack_tbl *tbl;
+
+ /* Delay enabling the HP amp, to let the mic-detection
+ * state machine run.
+ */
+ tbl = snd_hda_jack_tbl_get(codec, cb->nid);
+ if (tbl)
+ tbl->block_report = 1;
+ schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(500));
+}
+
+static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (ca0132_use_alt_functions(spec))
+ ca0132_alt_select_in(codec);
+ else
+ ca0132_select_mic(codec);
+}
+
+static void ca0132_setup_unsol(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ snd_hda_jack_detect_enable_callback(codec, spec->unsol_tag_hp, hp_callback);
+ snd_hda_jack_detect_enable_callback(codec, spec->unsol_tag_amic1,
+ amic_callback);
+ snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_DSP,
+ ca0132_process_dsp_response);
+ /* Front headphone jack detection */
+ if (ca0132_use_alt_functions(spec))
+ snd_hda_jack_detect_enable_callback(codec,
+ spec->unsol_tag_front_hp, hp_callback);
+}
+
+/*
+ * Verbs tables.
+ */
+
+/* Sends before DSP download. */
+static const struct hda_verb ca0132_base_init_verbs[] = {
+ /*enable ct extension*/
+ {0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0x1},
+ {}
+};
+
+/* Send at exit. */
+static const struct hda_verb ca0132_base_exit_verbs[] = {
+ /*set afg to D3*/
+ {0x01, AC_VERB_SET_POWER_STATE, 0x03},
+ /*disable ct extension*/
+ {0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0},
+ {}
+};
+
+/* Other verbs tables. Sends after DSP download. */
+
+static const struct hda_verb ca0132_init_verbs0[] = {
+ /* chip init verbs */
+ {0x15, 0x70D, 0xF0},
+ {0x15, 0x70E, 0xFE},
+ {0x15, 0x707, 0x75},
+ {0x15, 0x707, 0xD3},
+ {0x15, 0x707, 0x09},
+ {0x15, 0x707, 0x53},
+ {0x15, 0x707, 0xD4},
+ {0x15, 0x707, 0xEF},
+ {0x15, 0x707, 0x75},
+ {0x15, 0x707, 0xD3},
+ {0x15, 0x707, 0x09},
+ {0x15, 0x707, 0x02},
+ {0x15, 0x707, 0x37},
+ {0x15, 0x707, 0x78},
+ {0x15, 0x53C, 0xCE},
+ {0x15, 0x575, 0xC9},
+ {0x15, 0x53D, 0xCE},
+ {0x15, 0x5B7, 0xC9},
+ {0x15, 0x70D, 0xE8},
+ {0x15, 0x70E, 0xFE},
+ {0x15, 0x707, 0x02},
+ {0x15, 0x707, 0x68},
+ {0x15, 0x707, 0x62},
+ {0x15, 0x53A, 0xCE},
+ {0x15, 0x546, 0xC9},
+ {0x15, 0x53B, 0xCE},
+ {0x15, 0x5E8, 0xC9},
+ {}
+};
+
+/* Extra init verbs for desktop cards. */
+static const struct hda_verb ca0132_init_verbs1[] = {
+ {0x15, 0x70D, 0x20},
+ {0x15, 0x70E, 0x19},
+ {0x15, 0x707, 0x00},
+ {0x15, 0x539, 0xCE},
+ {0x15, 0x546, 0xC9},
+ {0x15, 0x70D, 0xB7},
+ {0x15, 0x70E, 0x09},
+ {0x15, 0x707, 0x10},
+ {0x15, 0x70D, 0xAF},
+ {0x15, 0x70E, 0x09},
+ {0x15, 0x707, 0x01},
+ {0x15, 0x707, 0x05},
+ {0x15, 0x70D, 0x73},
+ {0x15, 0x70E, 0x09},
+ {0x15, 0x707, 0x14},
+ {0x15, 0x6FF, 0xC4},
+ {}
+};
+
+static void ca0132_init_chip(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int num_fx;
+ int i;
+ unsigned int on;
+
+ mutex_init(&spec->chipio_mutex);
+
+ /*
+ * The Windows driver always does this upon startup, which seems to
+ * clear out any previous configuration. This should help issues where
+ * a boot into Windows prior to a boot into Linux breaks things. Also,
+ * Windows always sends the reset twice.
+ */
+ if (ca0132_use_alt_functions(spec)) {
+ chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0);
+ chipio_write_no_mutex(codec, 0x18b0a4, 0x000000c2);
+
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_CODEC_RESET, 0);
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_CODEC_RESET, 0);
+ }
+
+ spec->cur_out_type = SPEAKER_OUT;
+ if (!ca0132_use_alt_functions(spec))
+ spec->cur_mic_type = DIGITAL_MIC;
+ else
+ spec->cur_mic_type = REAR_MIC;
+
+ spec->cur_mic_boost = 0;
+
+ for (i = 0; i < VNODES_COUNT; i++) {
+ spec->vnode_lvol[i] = 0x5a;
+ spec->vnode_rvol[i] = 0x5a;
+ spec->vnode_lswitch[i] = 0;
+ spec->vnode_rswitch[i] = 0;
+ }
+
+ /*
+ * Default states for effects are in ca0132_effects[].
+ */
+ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
+ for (i = 0; i < num_fx; i++) {
+ on = (unsigned int)ca0132_effects[i].reqs[0];
+ spec->effects_switch[i] = on ? 1 : 0;
+ }
+ /*
+ * Sets defaults for the effect slider controls, only for alternative
+ * ca0132 codecs. Also sets x-bass crossover frequency to 80hz.
+ */
+ if (ca0132_use_alt_controls(spec)) {
+ /* Set speakers to default to full range. */
+ spec->speaker_range_val[0] = 1;
+ spec->speaker_range_val[1] = 1;
+
+ spec->xbass_xover_freq = 8;
+ for (i = 0; i < EFFECT_LEVEL_SLIDERS; i++)
+ spec->fx_ctl_val[i] = effect_slider_defaults[i];
+
+ spec->bass_redirect_xover_freq = 8;
+ }
+
+ spec->voicefx_val = 0;
+ spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID] = 1;
+ spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] = 0;
+
+ /*
+ * The ZxR doesn't have a front panel header, and it's line-in is on
+ * the daughter board. So, there is no input enum control, and we need
+ * to make sure that spec->in_enum_val is set properly.
+ */
+ if (ca0132_quirk(spec) == QUIRK_ZXR)
+ spec->in_enum_val = REAR_MIC;
+
+#ifdef ENABLE_TUNING_CONTROLS
+ ca0132_init_tuning_defaults(codec);
+#endif
+}
+
+/*
+ * Recon3Di exit specific commands.
+ */
+/* prevents popping noise on shutdown */
+static void r3di_gpio_shutdown(struct hda_codec *codec)
+{
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0x00);
+}
+
+/*
+ * Sound Blaster Z exit specific commands.
+ */
+static void sbz_region2_exit(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int i;
+
+ for (i = 0; i < 4; i++)
+ writeb(0x0, spec->mem_base + 0x100);
+ for (i = 0; i < 8; i++)
+ writeb(0xb3, spec->mem_base + 0x304);
+
+ ca0113_mmio_gpio_set(codec, 0, false);
+ ca0113_mmio_gpio_set(codec, 1, false);
+ ca0113_mmio_gpio_set(codec, 4, true);
+ ca0113_mmio_gpio_set(codec, 5, false);
+ ca0113_mmio_gpio_set(codec, 7, false);
+}
+
+static void sbz_set_pin_ctl_default(struct hda_codec *codec)
+{
+ static const hda_nid_t pins[] = {0x0B, 0x0C, 0x0E, 0x12, 0x13};
+ unsigned int i;
+
+ snd_hda_codec_write(codec, 0x11, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40);
+
+ for (i = 0; i < ARRAY_SIZE(pins); i++)
+ snd_hda_codec_write(codec, pins[i], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00);
+}
+
+static void ca0132_clear_unsolicited(struct hda_codec *codec)
+{
+ static const hda_nid_t pins[] = {0x0B, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13};
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(pins); i++) {
+ snd_hda_codec_write(codec, pins[i], 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE, 0x00);
+ }
+}
+
+/* On shutdown, sends commands in sets of three */
+static void sbz_gpio_shutdown_commands(struct hda_codec *codec, int dir,
+ int mask, int data)
+{
+ if (dir >= 0)
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DIRECTION, dir);
+ if (mask >= 0)
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_MASK, mask);
+
+ if (data >= 0)
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, data);
+}
+
+static void zxr_dbpro_power_state_shutdown(struct hda_codec *codec)
+{
+ static const hda_nid_t pins[] = {0x05, 0x0c, 0x09, 0x0e, 0x08, 0x11, 0x01};
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(pins); i++)
+ snd_hda_codec_write(codec, pins[i], 0,
+ AC_VERB_SET_POWER_STATE, 0x03);
+}
+
+static void sbz_exit_chip(struct hda_codec *codec)
+{
+ chipio_set_stream_control(codec, 0x03, 0);
+ chipio_set_stream_control(codec, 0x04, 0);
+
+ /* Mess with GPIO */
+ sbz_gpio_shutdown_commands(codec, 0x07, 0x07, -1);
+ sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x05);
+ sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x01);
+
+ chipio_set_stream_control(codec, 0x14, 0);
+ chipio_set_stream_control(codec, 0x0C, 0);
+
+ chipio_set_conn_rate(codec, 0x41, SR_192_000);
+ chipio_set_conn_rate(codec, 0x91, SR_192_000);
+
+ chipio_write(codec, 0x18a020, 0x00000083);
+
+ sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x03);
+ sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x07);
+ sbz_gpio_shutdown_commands(codec, 0x07, 0x07, 0x06);
+
+ chipio_set_stream_control(codec, 0x0C, 0);
+
+ chipio_set_control_param(codec, 0x0D, 0x24);
+
+ ca0132_clear_unsolicited(codec);
+ sbz_set_pin_ctl_default(codec);
+
+ snd_hda_codec_write(codec, 0x0B, 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+
+ sbz_region2_exit(codec);
+}
+
+static void r3d_exit_chip(struct hda_codec *codec)
+{
+ ca0132_clear_unsolicited(codec);
+ snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00);
+ snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x5b);
+}
+
+static void ae5_exit_chip(struct hda_codec *codec)
+{
+ chipio_set_stream_control(codec, 0x03, 0);
+ chipio_set_stream_control(codec, 0x04, 0);
+
+ ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f);
+ ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83);
+ ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83);
+ ca0113_mmio_command_set(codec, 0x30, 0x30, 0x00);
+ ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x00);
+ ca0113_mmio_command_set(codec, 0x30, 0x2d, 0x00);
+ ca0113_mmio_gpio_set(codec, 0, false);
+ ca0113_mmio_gpio_set(codec, 1, false);
+
+ snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00);
+ snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53);
+
+ chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0);
+
+ chipio_set_stream_control(codec, 0x18, 0);
+ chipio_set_stream_control(codec, 0x0c, 0);
+
+ snd_hda_codec_write(codec, 0x01, 0, 0x724, 0x83);
+}
+
+static void ae7_exit_chip(struct hda_codec *codec)
+{
+ chipio_set_stream_control(codec, 0x18, 0);
+ chipio_set_stream_source_dest(codec, 0x21, 0xc8, 0xc8);
+ chipio_set_stream_channels(codec, 0x21, 0);
+ chipio_set_control_param(codec, CONTROL_PARAM_NODE_ID, 0x09);
+ chipio_set_control_param(codec, 0x20, 0x01);
+
+ chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0);
+
+ chipio_set_stream_control(codec, 0x18, 0);
+ chipio_set_stream_control(codec, 0x0c, 0);
+
+ ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x00);
+ snd_hda_codec_write(codec, 0x15, 0, 0x724, 0x83);
+ ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83);
+ ca0113_mmio_command_set(codec, 0x30, 0x30, 0x00);
+ ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x00);
+ ca0113_mmio_gpio_set(codec, 0, false);
+ ca0113_mmio_gpio_set(codec, 1, false);
+ ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f);
+
+ snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00);
+ snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53);
+}
+
+static void zxr_exit_chip(struct hda_codec *codec)
+{
+ chipio_set_stream_control(codec, 0x03, 0);
+ chipio_set_stream_control(codec, 0x04, 0);
+ chipio_set_stream_control(codec, 0x14, 0);
+ chipio_set_stream_control(codec, 0x0C, 0);
+
+ chipio_set_conn_rate(codec, 0x41, SR_192_000);
+ chipio_set_conn_rate(codec, 0x91, SR_192_000);
+
+ chipio_write(codec, 0x18a020, 0x00000083);
+
+ snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00);
+ snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53);
+
+ ca0132_clear_unsolicited(codec);
+ sbz_set_pin_ctl_default(codec);
+ snd_hda_codec_write(codec, 0x0B, 0, AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+
+ ca0113_mmio_gpio_set(codec, 5, false);
+ ca0113_mmio_gpio_set(codec, 2, false);
+ ca0113_mmio_gpio_set(codec, 3, false);
+ ca0113_mmio_gpio_set(codec, 0, false);
+ ca0113_mmio_gpio_set(codec, 4, true);
+ ca0113_mmio_gpio_set(codec, 0, true);
+ ca0113_mmio_gpio_set(codec, 5, true);
+ ca0113_mmio_gpio_set(codec, 2, false);
+ ca0113_mmio_gpio_set(codec, 3, false);
+}
+
+static void ca0132_exit_chip(struct hda_codec *codec)
+{
+ /* put any chip cleanup stuffs here. */
+
+ if (dspload_is_loaded(codec))
+ dsp_reset(codec);
+}
+
+/*
+ * This fixes a problem that was hard to reproduce. Very rarely, I would
+ * boot up, and there would be no sound, but the DSP indicated it had loaded
+ * properly. I did a few memory dumps to see if anything was different, and
+ * there were a few areas of memory uninitialized with a1a2a3a4. This function
+ * checks if those areas are uninitialized, and if they are, it'll attempt to
+ * reload the card 3 times. Usually it fixes by the second.
+ */
+static void sbz_dsp_startup_check(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int dsp_data_check[4];
+ unsigned int cur_address = 0x390;
+ unsigned int i;
+ unsigned int failure = 0;
+ unsigned int reload = 3;
+
+ if (spec->startup_check_entered)
+ return;
+
+ spec->startup_check_entered = true;
+
+ for (i = 0; i < 4; i++) {
+ chipio_read(codec, cur_address, &dsp_data_check[i]);
+ cur_address += 0x4;
+ }
+ for (i = 0; i < 4; i++) {
+ if (dsp_data_check[i] == 0xa1a2a3a4)
+ failure = 1;
+ }
+
+ codec_dbg(codec, "Startup Check: %d ", failure);
+ if (failure)
+ codec_info(codec, "DSP not initialized properly. Attempting to fix.");
+ /*
+ * While the failure condition is true, and we haven't reached our
+ * three reload limit, continue trying to reload the driver and
+ * fix the issue.
+ */
+ while (failure && (reload != 0)) {
+ codec_info(codec, "Reloading... Tries left: %d", reload);
+ sbz_exit_chip(codec);
+ spec->dsp_state = DSP_DOWNLOAD_INIT;
+ snd_hda_codec_init(codec);
+ failure = 0;
+ for (i = 0; i < 4; i++) {
+ chipio_read(codec, cur_address, &dsp_data_check[i]);
+ cur_address += 0x4;
+ }
+ for (i = 0; i < 4; i++) {
+ if (dsp_data_check[i] == 0xa1a2a3a4)
+ failure = 1;
+ }
+ reload--;
+ }
+
+ if (!failure && reload < 3)
+ codec_info(codec, "DSP fixed.");
+
+ if (!failure)
+ return;
+
+ codec_info(codec, "DSP failed to initialize properly. Either try a full shutdown or a suspend to clear the internal memory.");
+}
+
+/*
+ * This is for the extra volume verbs 0x797 (left) and 0x798 (right). These add
+ * extra precision for decibel values. If you had the dB value in floating point
+ * you would take the value after the decimal point, multiply by 64, and divide
+ * by 2. So for 8.59, it's (59 * 64) / 100. Useful if someone wanted to
+ * implement fixed point or floating point dB volumes. For now, I'll set them
+ * to 0 just incase a value has lingered from a boot into Windows.
+ */
+static void ca0132_alt_vol_setup(struct hda_codec *codec)
+{
+ snd_hda_codec_write(codec, 0x02, 0, 0x797, 0x00);
+ snd_hda_codec_write(codec, 0x02, 0, 0x798, 0x00);
+ snd_hda_codec_write(codec, 0x03, 0, 0x797, 0x00);
+ snd_hda_codec_write(codec, 0x03, 0, 0x798, 0x00);
+ snd_hda_codec_write(codec, 0x04, 0, 0x797, 0x00);
+ snd_hda_codec_write(codec, 0x04, 0, 0x798, 0x00);
+ snd_hda_codec_write(codec, 0x07, 0, 0x797, 0x00);
+ snd_hda_codec_write(codec, 0x07, 0, 0x798, 0x00);
+}
+
+/*
+ * Extra commands that don't really fit anywhere else.
+ */
+static void sbz_pre_dsp_setup(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ writel(0x00820680, spec->mem_base + 0x01C);
+ writel(0x00820680, spec->mem_base + 0x01C);
+
+ chipio_write(codec, 0x18b0a4, 0x000000c2);
+
+ snd_hda_codec_write(codec, 0x11, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x44);
+}
+
+static void r3d_pre_dsp_setup(struct hda_codec *codec)
+{
+ chipio_write(codec, 0x18b0a4, 0x000000c2);
+
+ chipio_8051_write_exram(codec, 0x1c1e, 0x5b);
+
+ snd_hda_codec_write(codec, 0x11, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x44);
+}
+
+static void r3di_pre_dsp_setup(struct hda_codec *codec)
+{
+ chipio_write(codec, 0x18b0a4, 0x000000c2);
+
+ chipio_8051_write_exram(codec, 0x1c1e, 0x5b);
+ chipio_8051_write_exram(codec, 0x1920, 0x00);
+ chipio_8051_write_exram(codec, 0x1921, 0x40);
+
+ snd_hda_codec_write(codec, 0x11, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x04);
+}
+
+/*
+ * The ZxR seems to use alternative DAC's for the surround channels, which
+ * require PLL PMU setup for the clock rate, I'm guessing. Without setting
+ * this up, we get no audio out of the surround jacks.
+ */
+static void zxr_pre_dsp_setup(struct hda_codec *codec)
+{
+ static const unsigned int addr[] = { 0x43, 0x40, 0x41, 0x42, 0x45 };
+ static const unsigned int data[] = { 0x08, 0x0c, 0x0b, 0x07, 0x0d };
+ unsigned int i;
+
+ chipio_write(codec, 0x189000, 0x0001f100);
+ msleep(50);
+ chipio_write(codec, 0x18900c, 0x0001f100);
+ msleep(50);
+
+ /*
+ * This writes a RET instruction at the entry point of the function at
+ * 0xfa92 in exram. This function seems to have something to do with
+ * ASI. Might be some way to prevent the card from reconfiguring the
+ * ASI stuff itself.
+ */
+ chipio_8051_write_exram(codec, 0xfa92, 0x22);
+
+ chipio_8051_write_pll_pmu(codec, 0x51, 0x98);
+
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x725, 0x82);
+ chipio_set_control_param(codec, CONTROL_PARAM_ASI, 3);
+
+ chipio_write(codec, 0x18902c, 0x00000000);
+ msleep(50);
+ chipio_write(codec, 0x18902c, 0x00000003);
+ msleep(50);
+
+ for (i = 0; i < ARRAY_SIZE(addr); i++)
+ chipio_8051_write_pll_pmu(codec, addr[i], data[i]);
+}
+
+/*
+ * These are sent before the DSP is downloaded. Not sure
+ * what they do, or if they're necessary. Could possibly
+ * be removed. Figure they're better to leave in.
+ */
+static const unsigned int ca0113_mmio_init_address_sbz[] = {
+ 0x400, 0x408, 0x40c, 0x01c, 0xc0c, 0xc00, 0xc04, 0xc0c, 0xc0c, 0xc0c,
+ 0xc0c, 0xc08, 0xc08, 0xc08, 0xc08, 0xc08, 0xc04
+};
+
+static const unsigned int ca0113_mmio_init_data_sbz[] = {
+ 0x00000030, 0x00000000, 0x00000003, 0x00000003, 0x00000003,
+ 0x00000003, 0x000000c1, 0x000000f1, 0x00000001, 0x000000c7,
+ 0x000000c1, 0x00000080
+};
+
+static const unsigned int ca0113_mmio_init_data_zxr[] = {
+ 0x00000030, 0x00000000, 0x00000000, 0x00000003, 0x00000003,
+ 0x00000003, 0x00000001, 0x000000f1, 0x00000001, 0x000000c7,
+ 0x000000c1, 0x00000080
+};
+
+static const unsigned int ca0113_mmio_init_address_ae5[] = {
+ 0x400, 0x42c, 0x46c, 0x4ac, 0x4ec, 0x43c, 0x47c, 0x4bc, 0x4fc, 0x408,
+ 0x100, 0x410, 0x40c, 0x100, 0x100, 0x830, 0x86c, 0x800, 0x86c, 0x800,
+ 0x804, 0x20c, 0x01c, 0xc0c, 0xc00, 0xc04, 0xc0c, 0xc0c, 0xc0c, 0xc0c,
+ 0xc08, 0xc08, 0xc08, 0xc08, 0xc08, 0xc04, 0x01c
+};
+
+static const unsigned int ca0113_mmio_init_data_ae5[] = {
+ 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
+ 0x00000600, 0x00000014, 0x00000001, 0x0000060f, 0x0000070f,
+ 0x00000aff, 0x00000000, 0x0000006b, 0x00000001, 0x0000006b,
+ 0x00000057, 0x00800000, 0x00880680, 0x00000080, 0x00000030,
+ 0x00000000, 0x00000000, 0x00000003, 0x00000003, 0x00000003,
+ 0x00000001, 0x000000f1, 0x00000001, 0x000000c7, 0x000000c1,
+ 0x00000080, 0x00880680
+};
+
+static void ca0132_mmio_init_sbz(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int tmp[2], i, count, cur_addr;
+ const unsigned int *addr, *data;
+
+ addr = ca0113_mmio_init_address_sbz;
+ for (i = 0; i < 3; i++)
+ writel(0x00000000, spec->mem_base + addr[i]);
+
+ cur_addr = i;
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_ZXR:
+ tmp[0] = 0x00880480;
+ tmp[1] = 0x00000080;
+ break;
+ case QUIRK_SBZ:
+ tmp[0] = 0x00820680;
+ tmp[1] = 0x00000083;
+ break;
+ case QUIRK_R3D:
+ tmp[0] = 0x00880680;
+ tmp[1] = 0x00000083;
+ break;
+ default:
+ tmp[0] = 0x00000000;
+ tmp[1] = 0x00000000;
+ break;
+ }
+
+ for (i = 0; i < 2; i++)
+ writel(tmp[i], spec->mem_base + addr[cur_addr + i]);
+
+ cur_addr += i;
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_ZXR:
+ count = ARRAY_SIZE(ca0113_mmio_init_data_zxr);
+ data = ca0113_mmio_init_data_zxr;
+ break;
+ default:
+ count = ARRAY_SIZE(ca0113_mmio_init_data_sbz);
+ data = ca0113_mmio_init_data_sbz;
+ break;
+ }
+
+ for (i = 0; i < count; i++)
+ writel(data[i], spec->mem_base + addr[cur_addr + i]);
+}
+
+static void ca0132_mmio_init_ae5(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ const unsigned int *addr, *data;
+ unsigned int i, count;
+
+ addr = ca0113_mmio_init_address_ae5;
+ data = ca0113_mmio_init_data_ae5;
+ count = ARRAY_SIZE(ca0113_mmio_init_data_ae5);
+
+ if (ca0132_quirk(spec) == QUIRK_AE7) {
+ writel(0x00000680, spec->mem_base + 0x1c);
+ writel(0x00880680, spec->mem_base + 0x1c);
+ }
+
+ for (i = 0; i < count; i++) {
+ /*
+ * AE-7 shares all writes with the AE-5, except that it writes
+ * a different value to 0x20c.
+ */
+ if (i == 21 && ca0132_quirk(spec) == QUIRK_AE7) {
+ writel(0x00800001, spec->mem_base + addr[i]);
+ continue;
+ }
+
+ writel(data[i], spec->mem_base + addr[i]);
+ }
+
+ if (ca0132_quirk(spec) == QUIRK_AE5)
+ writel(0x00880680, spec->mem_base + 0x1c);
+}
+
+static void ca0132_mmio_init(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_R3D:
+ case QUIRK_SBZ:
+ case QUIRK_ZXR:
+ ca0132_mmio_init_sbz(codec);
+ break;
+ case QUIRK_AE5:
+ ca0132_mmio_init_ae5(codec);
+ break;
+ default:
+ break;
+ }
+}
+
+static const unsigned int ca0132_ae5_register_set_addresses[] = {
+ 0x304, 0x304, 0x304, 0x304, 0x100, 0x304, 0x100, 0x304, 0x100, 0x304,
+ 0x100, 0x304, 0x86c, 0x800, 0x86c, 0x800, 0x804
+};
+
+static const unsigned char ca0132_ae5_register_set_data[] = {
+ 0x0f, 0x0e, 0x1f, 0x0c, 0x3f, 0x08, 0x7f, 0x00, 0xff, 0x00, 0x6b,
+ 0x01, 0x6b, 0x57
+};
+
+/*
+ * This function writes to some SFR's, does some region2 writes, and then
+ * eventually resets the codec with the 0x7ff verb. Not quite sure why it does
+ * what it does.
+ */
+static void ae5_register_set(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ unsigned int count = ARRAY_SIZE(ca0132_ae5_register_set_addresses);
+ const unsigned int *addr = ca0132_ae5_register_set_addresses;
+ const unsigned char *data = ca0132_ae5_register_set_data;
+ unsigned int i, cur_addr;
+ unsigned char tmp[3];
+
+ if (ca0132_quirk(spec) == QUIRK_AE7)
+ chipio_8051_write_pll_pmu(codec, 0x41, 0xc8);
+
+ chipio_8051_write_direct(codec, 0x93, 0x10);
+ chipio_8051_write_pll_pmu(codec, 0x44, 0xc2);
+
+ if (ca0132_quirk(spec) == QUIRK_AE7) {
+ tmp[0] = 0x03;
+ tmp[1] = 0x03;
+ tmp[2] = 0x07;
+ } else {
+ tmp[0] = 0x0f;
+ tmp[1] = 0x0f;
+ tmp[2] = 0x0f;
+ }
+
+ for (i = cur_addr = 0; i < 3; i++, cur_addr++)
+ writeb(tmp[i], spec->mem_base + addr[cur_addr]);
+
+ /*
+ * First writes are in single bytes, final are in 4 bytes. So, we use
+ * writeb, then writel.
+ */
+ for (i = 0; cur_addr < 12; i++, cur_addr++)
+ writeb(data[i], spec->mem_base + addr[cur_addr]);
+
+ for (; cur_addr < count; i++, cur_addr++)
+ writel(data[i], spec->mem_base + addr[cur_addr]);
+
+ writel(0x00800001, spec->mem_base + 0x20c);
+
+ if (ca0132_quirk(spec) == QUIRK_AE7) {
+ ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83);
+ ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f);
+ } else {
+ ca0113_mmio_command_set(codec, 0x30, 0x2d, 0x3f);
+ }
+
+ chipio_8051_write_direct(codec, 0x90, 0x00);
+ chipio_8051_write_direct(codec, 0x90, 0x10);
+
+ if (ca0132_quirk(spec) == QUIRK_AE5)
+ ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83);
+}
+
+/*
+ * Extra init functions for alternative ca0132 codecs. Done
+ * here so they don't clutter up the main ca0132_init function
+ * anymore than they have to.
+ */
+static void ca0132_alt_init(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ ca0132_alt_vol_setup(codec);
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ codec_dbg(codec, "SBZ alt_init");
+ ca0132_gpio_init(codec);
+ sbz_pre_dsp_setup(codec);
+ snd_hda_sequence_write(codec, spec->chip_init_verbs);
+ snd_hda_sequence_write(codec, spec->desktop_init_verbs);
+ break;
+ case QUIRK_R3DI:
+ codec_dbg(codec, "R3DI alt_init");
+ ca0132_gpio_init(codec);
+ ca0132_gpio_setup(codec);
+ r3di_gpio_dsp_status_set(codec, R3DI_DSP_DOWNLOADING);
+ r3di_pre_dsp_setup(codec);
+ snd_hda_sequence_write(codec, spec->chip_init_verbs);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x6FF, 0xC4);
+ break;
+ case QUIRK_R3D:
+ r3d_pre_dsp_setup(codec);
+ snd_hda_sequence_write(codec, spec->chip_init_verbs);
+ snd_hda_sequence_write(codec, spec->desktop_init_verbs);
+ break;
+ case QUIRK_AE5:
+ ca0132_gpio_init(codec);
+ chipio_8051_write_pll_pmu(codec, 0x49, 0x88);
+ chipio_write(codec, 0x18b030, 0x00000020);
+ snd_hda_sequence_write(codec, spec->chip_init_verbs);
+ snd_hda_sequence_write(codec, spec->desktop_init_verbs);
+ ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f);
+ break;
+ case QUIRK_AE7:
+ ca0132_gpio_init(codec);
+ chipio_8051_write_pll_pmu(codec, 0x49, 0x88);
+ snd_hda_sequence_write(codec, spec->chip_init_verbs);
+ snd_hda_sequence_write(codec, spec->desktop_init_verbs);
+ chipio_write(codec, 0x18b008, 0x000000f8);
+ chipio_write(codec, 0x18b008, 0x000000f0);
+ chipio_write(codec, 0x18b030, 0x00000020);
+ ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f);
+ break;
+ case QUIRK_ZXR:
+ chipio_8051_write_pll_pmu(codec, 0x49, 0x88);
+ snd_hda_sequence_write(codec, spec->chip_init_verbs);
+ snd_hda_sequence_write(codec, spec->desktop_init_verbs);
+ zxr_pre_dsp_setup(codec);
+ break;
+ default:
+ break;
+ }
+}
+
+static int ca0132_init(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+ bool dsp_loaded;
+
+ /*
+ * If the DSP is already downloaded, and init has been entered again,
+ * there's only two reasons for it. One, the codec has awaken from a
+ * suspended state, and in that case dspload_is_loaded will return
+ * false, and the init will be ran again. The other reason it gets
+ * re entered is on startup for some reason it triggers a suspend and
+ * resume state. In this case, it will check if the DSP is downloaded,
+ * and not run the init function again. For codecs using alt_functions,
+ * it will check if the DSP is loaded properly.
+ */
+ if (spec->dsp_state == DSP_DOWNLOADED) {
+ dsp_loaded = dspload_is_loaded(codec);
+ if (!dsp_loaded) {
+ spec->dsp_reload = true;
+ spec->dsp_state = DSP_DOWNLOAD_INIT;
+ } else {
+ if (ca0132_quirk(spec) == QUIRK_SBZ)
+ sbz_dsp_startup_check(codec);
+ return 0;
+ }
+ }
+
+ if (spec->dsp_state != DSP_DOWNLOAD_FAILED)
+ spec->dsp_state = DSP_DOWNLOAD_INIT;
+ spec->curr_chip_addx = INVALID_CHIP_ADDRESS;
+
+ if (ca0132_use_pci_mmio(spec))
+ ca0132_mmio_init(codec);
+
+ CLASS(snd_hda_power_pm, pm)(codec);
+
+ if (ca0132_quirk(spec) == QUIRK_AE5 || ca0132_quirk(spec) == QUIRK_AE7)
+ ae5_register_set(codec);
+
+ ca0132_init_params(codec);
+ ca0132_init_flags(codec);
+
+ snd_hda_sequence_write(codec, spec->base_init_verbs);
+
+ if (ca0132_use_alt_functions(spec))
+ ca0132_alt_init(codec);
+
+ ca0132_download_dsp(codec);
+
+ ca0132_refresh_widget_caps(codec);
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_R3DI:
+ case QUIRK_R3D:
+ r3d_setup_defaults(codec);
+ break;
+ case QUIRK_SBZ:
+ case QUIRK_ZXR:
+ sbz_setup_defaults(codec);
+ break;
+ case QUIRK_AE5:
+ ae5_setup_defaults(codec);
+ break;
+ case QUIRK_AE7:
+ ae7_setup_defaults(codec);
+ break;
+ default:
+ ca0132_setup_defaults(codec);
+ ca0132_init_analog_mic2(codec);
+ ca0132_init_dmic(codec);
+ break;
+ }
+
+ for (i = 0; i < spec->num_outputs; i++)
+ init_output(codec, spec->out_pins[i], spec->dacs[0]);
+
+ init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
+
+ for (i = 0; i < spec->num_inputs; i++)
+ init_input(codec, spec->input_pins[i], spec->adcs[i]);
+
+ init_input(codec, cfg->dig_in_pin, spec->dig_in);
+
+ if (!ca0132_use_alt_functions(spec)) {
+ snd_hda_sequence_write(codec, spec->chip_init_verbs);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_ID_SET, 0x0D);
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_PARAM_EX_VALUE_SET, 0x20);
+ }
+
+ if (ca0132_quirk(spec) == QUIRK_SBZ)
+ ca0132_gpio_setup(codec);
+
+ snd_hda_sequence_write(codec, spec->spec_init_verbs);
+ if (ca0132_use_alt_functions(spec)) {
+ ca0132_alt_select_out(codec);
+ ca0132_alt_select_in(codec);
+ } else {
+ ca0132_select_out(codec);
+ ca0132_select_mic(codec);
+ }
+
+ snd_hda_jack_report_sync(codec);
+
+ /*
+ * Re set the PlayEnhancement switch on a resume event, because the
+ * controls will not be reloaded.
+ */
+ if (spec->dsp_reload) {
+ spec->dsp_reload = false;
+ ca0132_pe_switch_set(codec);
+ }
+
+ return 0;
+}
+
+static int dbpro_init(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int i;
+
+ init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
+ init_input(codec, cfg->dig_in_pin, spec->dig_in);
+
+ for (i = 0; i < spec->num_inputs; i++)
+ init_input(codec, spec->input_pins[i], spec->adcs[i]);
+
+ return 0;
+}
+
+static void ca0132_free(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ cancel_delayed_work_sync(&spec->unsol_hp_work);
+ snd_hda_power_up(codec);
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ sbz_exit_chip(codec);
+ break;
+ case QUIRK_ZXR:
+ zxr_exit_chip(codec);
+ break;
+ case QUIRK_R3D:
+ r3d_exit_chip(codec);
+ break;
+ case QUIRK_AE5:
+ ae5_exit_chip(codec);
+ break;
+ case QUIRK_AE7:
+ ae7_exit_chip(codec);
+ break;
+ case QUIRK_R3DI:
+ r3di_gpio_shutdown(codec);
+ break;
+ default:
+ break;
+ }
+
+ snd_hda_sequence_write(codec, spec->base_exit_verbs);
+ ca0132_exit_chip(codec);
+
+ snd_hda_power_down(codec);
+#ifdef CONFIG_PCI
+ if (spec->mem_base)
+ pci_iounmap(codec->bus->pci, spec->mem_base);
+#endif
+ kfree(spec->spec_init_verbs);
+ kfree(codec->spec);
+}
+
+static void dbpro_free(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ zxr_dbpro_power_state_shutdown(codec);
+
+ kfree(spec->spec_init_verbs);
+ kfree(codec->spec);
+}
+
+static void ca0132_config(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ spec->dacs[0] = 0x2;
+ spec->dacs[1] = 0x3;
+ spec->dacs[2] = 0x4;
+
+ spec->multiout.dac_nids = spec->dacs;
+ spec->multiout.num_dacs = 3;
+
+ if (!ca0132_use_alt_functions(spec))
+ spec->multiout.max_channels = 2;
+ else
+ spec->multiout.max_channels = 6;
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_ALIENWARE:
+ codec_dbg(codec, "%s: QUIRK_ALIENWARE applied.\n", __func__);
+ snd_hda_apply_pincfgs(codec, alienware_pincfgs);
+ break;
+ case QUIRK_SBZ:
+ codec_dbg(codec, "%s: QUIRK_SBZ applied.\n", __func__);
+ snd_hda_apply_pincfgs(codec, sbz_pincfgs);
+ break;
+ case QUIRK_ZXR:
+ codec_dbg(codec, "%s: QUIRK_ZXR applied.\n", __func__);
+ snd_hda_apply_pincfgs(codec, zxr_pincfgs);
+ break;
+ case QUIRK_R3D:
+ codec_dbg(codec, "%s: QUIRK_R3D applied.\n", __func__);
+ snd_hda_apply_pincfgs(codec, r3d_pincfgs);
+ break;
+ case QUIRK_R3DI:
+ codec_dbg(codec, "%s: QUIRK_R3DI applied.\n", __func__);
+ snd_hda_apply_pincfgs(codec, r3di_pincfgs);
+ break;
+ case QUIRK_AE5:
+ codec_dbg(codec, "%s: QUIRK_AE5 applied.\n", __func__);
+ snd_hda_apply_pincfgs(codec, ae5_pincfgs);
+ break;
+ case QUIRK_AE7:
+ codec_dbg(codec, "%s: QUIRK_AE7 applied.\n", __func__);
+ snd_hda_apply_pincfgs(codec, ae7_pincfgs);
+ break;
+ default:
+ break;
+ }
+
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_ALIENWARE:
+ spec->num_outputs = 2;
+ spec->out_pins[0] = 0x0b; /* speaker out */
+ spec->out_pins[1] = 0x0f;
+ spec->shared_out_nid = 0x2;
+ spec->unsol_tag_hp = 0x0f;
+
+ spec->adcs[0] = 0x7; /* digital mic / analog mic1 */
+ spec->adcs[1] = 0x8; /* analog mic2 */
+ spec->adcs[2] = 0xa; /* what u hear */
+
+ spec->num_inputs = 3;
+ spec->input_pins[0] = 0x12;
+ spec->input_pins[1] = 0x11;
+ spec->input_pins[2] = 0x13;
+ spec->shared_mic_nid = 0x7;
+ spec->unsol_tag_amic1 = 0x11;
+ break;
+ case QUIRK_SBZ:
+ case QUIRK_R3D:
+ spec->num_outputs = 2;
+ spec->out_pins[0] = 0x0B; /* Line out */
+ spec->out_pins[1] = 0x0F; /* Rear headphone out */
+ spec->out_pins[2] = 0x10; /* Front Headphone / Center/LFE*/
+ spec->out_pins[3] = 0x11; /* Rear surround */
+ spec->shared_out_nid = 0x2;
+ spec->unsol_tag_hp = spec->out_pins[1];
+ spec->unsol_tag_front_hp = spec->out_pins[2];
+
+ spec->adcs[0] = 0x7; /* Rear Mic / Line-in */
+ spec->adcs[1] = 0x8; /* Front Mic, but only if no DSP */
+ spec->adcs[2] = 0xa; /* what u hear */
+
+ spec->num_inputs = 2;
+ spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */
+ spec->input_pins[1] = 0x13; /* What U Hear */
+ spec->shared_mic_nid = 0x7;
+ spec->unsol_tag_amic1 = spec->input_pins[0];
+
+ /* SPDIF I/O */
+ spec->dig_out = 0x05;
+ spec->multiout.dig_out_nid = spec->dig_out;
+ spec->dig_in = 0x09;
+ break;
+ case QUIRK_ZXR:
+ spec->num_outputs = 2;
+ spec->out_pins[0] = 0x0B; /* Line out */
+ spec->out_pins[1] = 0x0F; /* Rear headphone out */
+ spec->out_pins[2] = 0x10; /* Center/LFE */
+ spec->out_pins[3] = 0x11; /* Rear surround */
+ spec->shared_out_nid = 0x2;
+ spec->unsol_tag_hp = spec->out_pins[1];
+ spec->unsol_tag_front_hp = spec->out_pins[2];
+
+ spec->adcs[0] = 0x7; /* Rear Mic / Line-in */
+ spec->adcs[1] = 0x8; /* Not connected, no front mic */
+ spec->adcs[2] = 0xa; /* what u hear */
+
+ spec->num_inputs = 2;
+ spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */
+ spec->input_pins[1] = 0x13; /* What U Hear */
+ spec->shared_mic_nid = 0x7;
+ spec->unsol_tag_amic1 = spec->input_pins[0];
+ break;
+ case QUIRK_ZXR_DBPRO:
+ spec->adcs[0] = 0x8; /* ZxR DBPro Aux In */
+
+ spec->num_inputs = 1;
+ spec->input_pins[0] = 0x11; /* RCA Line-in */
+
+ spec->dig_out = 0x05;
+ spec->multiout.dig_out_nid = spec->dig_out;
+
+ spec->dig_in = 0x09;
+ break;
+ case QUIRK_AE5:
+ case QUIRK_AE7:
+ spec->num_outputs = 2;
+ spec->out_pins[0] = 0x0B; /* Line out */
+ spec->out_pins[1] = 0x11; /* Rear headphone out */
+ spec->out_pins[2] = 0x10; /* Front Headphone / Center/LFE*/
+ spec->out_pins[3] = 0x0F; /* Rear surround */
+ spec->shared_out_nid = 0x2;
+ spec->unsol_tag_hp = spec->out_pins[1];
+ spec->unsol_tag_front_hp = spec->out_pins[2];
+
+ spec->adcs[0] = 0x7; /* Rear Mic / Line-in */
+ spec->adcs[1] = 0x8; /* Front Mic, but only if no DSP */
+ spec->adcs[2] = 0xa; /* what u hear */
+
+ spec->num_inputs = 2;
+ spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */
+ spec->input_pins[1] = 0x13; /* What U Hear */
+ spec->shared_mic_nid = 0x7;
+ spec->unsol_tag_amic1 = spec->input_pins[0];
+
+ /* SPDIF I/O */
+ spec->dig_out = 0x05;
+ spec->multiout.dig_out_nid = spec->dig_out;
+ break;
+ case QUIRK_R3DI:
+ spec->num_outputs = 2;
+ spec->out_pins[0] = 0x0B; /* Line out */
+ spec->out_pins[1] = 0x0F; /* Rear headphone out */
+ spec->out_pins[2] = 0x10; /* Front Headphone / Center/LFE*/
+ spec->out_pins[3] = 0x11; /* Rear surround */
+ spec->shared_out_nid = 0x2;
+ spec->unsol_tag_hp = spec->out_pins[1];
+ spec->unsol_tag_front_hp = spec->out_pins[2];
+
+ spec->adcs[0] = 0x07; /* Rear Mic / Line-in */
+ spec->adcs[1] = 0x08; /* Front Mic, but only if no DSP */
+ spec->adcs[2] = 0x0a; /* what u hear */
+
+ spec->num_inputs = 2;
+ spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */
+ spec->input_pins[1] = 0x13; /* What U Hear */
+ spec->shared_mic_nid = 0x7;
+ spec->unsol_tag_amic1 = spec->input_pins[0];
+
+ /* SPDIF I/O */
+ spec->dig_out = 0x05;
+ spec->multiout.dig_out_nid = spec->dig_out;
+ break;
+ default:
+ spec->num_outputs = 2;
+ spec->out_pins[0] = 0x0b; /* speaker out */
+ spec->out_pins[1] = 0x10; /* headphone out */
+ spec->shared_out_nid = 0x2;
+ spec->unsol_tag_hp = spec->out_pins[1];
+
+ spec->adcs[0] = 0x7; /* digital mic / analog mic1 */
+ spec->adcs[1] = 0x8; /* analog mic2 */
+ spec->adcs[2] = 0xa; /* what u hear */
+
+ spec->num_inputs = 3;
+ spec->input_pins[0] = 0x12;
+ spec->input_pins[1] = 0x11;
+ spec->input_pins[2] = 0x13;
+ spec->shared_mic_nid = 0x7;
+ spec->unsol_tag_amic1 = spec->input_pins[0];
+
+ /* SPDIF I/O */
+ spec->dig_out = 0x05;
+ spec->multiout.dig_out_nid = spec->dig_out;
+ spec->dig_in = 0x09;
+ break;
+ }
+}
+
+static int ca0132_prepare_verbs(struct hda_codec *codec)
+{
+/* Verbs + terminator (an empty element) */
+#define NUM_SPEC_VERBS 2
+ struct ca0132_spec *spec = codec->spec;
+
+ spec->chip_init_verbs = ca0132_init_verbs0;
+ /*
+ * Since desktop cards use pci_mmio, this can be used to determine
+ * whether or not to use these verbs instead of a separate bool.
+ */
+ if (ca0132_use_pci_mmio(spec))
+ spec->desktop_init_verbs = ca0132_init_verbs1;
+ spec->spec_init_verbs = kcalloc(NUM_SPEC_VERBS,
+ sizeof(struct hda_verb),
+ GFP_KERNEL);
+ if (!spec->spec_init_verbs)
+ return -ENOMEM;
+
+ /* config EAPD */
+ spec->spec_init_verbs[0].nid = 0x0b;
+ spec->spec_init_verbs[0].param = 0x78D;
+ spec->spec_init_verbs[0].verb = 0x00;
+
+ /* Previously commented configuration */
+ /*
+ spec->spec_init_verbs[2].nid = 0x0b;
+ spec->spec_init_verbs[2].param = AC_VERB_SET_EAPD_BTLENABLE;
+ spec->spec_init_verbs[2].verb = 0x02;
+
+ spec->spec_init_verbs[3].nid = 0x10;
+ spec->spec_init_verbs[3].param = 0x78D;
+ spec->spec_init_verbs[3].verb = 0x02;
+
+ spec->spec_init_verbs[4].nid = 0x10;
+ spec->spec_init_verbs[4].param = AC_VERB_SET_EAPD_BTLENABLE;
+ spec->spec_init_verbs[4].verb = 0x02;
+ */
+
+ /* Terminator: spec->spec_init_verbs[NUM_SPEC_VERBS-1] */
+ return 0;
+}
+
+/*
+ * The Sound Blaster ZxR shares the same PCI subsystem ID as some regular
+ * Sound Blaster Z cards. However, they have different HDA codec subsystem
+ * ID's. So, we check for the ZxR's subsystem ID, as well as the DBPro
+ * daughter boards ID.
+ */
+static void sbz_detect_quirk(struct hda_codec *codec)
+{
+ switch (codec->core.subsystem_id) {
+ case 0x11020033:
+ codec->fixup_id = QUIRK_ZXR;
+ break;
+ case 0x1102003f:
+ codec->fixup_id = QUIRK_ZXR_DBPRO;
+ break;
+ default:
+ codec->fixup_id = QUIRK_SBZ;
+ break;
+ }
+}
+
+static void ca0132_codec_remove(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO)
+ return dbpro_free(codec);
+ else
+ return ca0132_free(codec);
+}
+
+static int ca0132_codec_probe(struct hda_codec *codec,
+ const struct hda_device_id *id)
+{
+ struct ca0132_spec *spec;
+ int err;
+
+ codec_dbg(codec, "%s\n", __func__);
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ codec->spec = spec;
+ spec->codec = codec;
+
+ /* Detect codec quirk */
+ snd_hda_pick_fixup(codec, ca0132_quirk_models, ca0132_quirks, NULL);
+ if (ca0132_quirk(spec) == QUIRK_SBZ)
+ sbz_detect_quirk(codec);
+
+ codec->pcm_format_first = 1;
+ codec->no_sticky_stream = 1;
+
+
+ spec->dsp_state = DSP_DOWNLOAD_INIT;
+ spec->num_mixers = 1;
+
+ /* Set which mixers each quirk uses. */
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ spec->mixers[0] = desktop_mixer;
+ snd_hda_codec_set_name(codec, "Sound Blaster Z");
+ break;
+ case QUIRK_ZXR:
+ spec->mixers[0] = desktop_mixer;
+ snd_hda_codec_set_name(codec, "Sound Blaster ZxR");
+ break;
+ case QUIRK_ZXR_DBPRO:
+ break;
+ case QUIRK_R3D:
+ spec->mixers[0] = desktop_mixer;
+ snd_hda_codec_set_name(codec, "Recon3D");
+ break;
+ case QUIRK_R3DI:
+ spec->mixers[0] = r3di_mixer;
+ snd_hda_codec_set_name(codec, "Recon3Di");
+ break;
+ case QUIRK_AE5:
+ spec->mixers[0] = desktop_mixer;
+ snd_hda_codec_set_name(codec, "Sound BlasterX AE-5");
+ break;
+ case QUIRK_AE7:
+ spec->mixers[0] = desktop_mixer;
+ snd_hda_codec_set_name(codec, "Sound Blaster AE-7");
+ break;
+ default:
+ spec->mixers[0] = ca0132_mixer;
+ break;
+ }
+
+ /* Setup whether or not to use alt functions/controls/pci_mmio */
+ switch (ca0132_quirk(spec)) {
+ case QUIRK_SBZ:
+ case QUIRK_R3D:
+ case QUIRK_AE5:
+ case QUIRK_AE7:
+ case QUIRK_ZXR:
+ spec->use_alt_controls = true;
+ spec->use_alt_functions = true;
+ spec->use_pci_mmio = true;
+ break;
+ case QUIRK_R3DI:
+ spec->use_alt_controls = true;
+ spec->use_alt_functions = true;
+ spec->use_pci_mmio = false;
+ break;
+ default:
+ spec->use_alt_controls = false;
+ spec->use_alt_functions = false;
+ spec->use_pci_mmio = false;
+ break;
+ }
+
+#ifdef CONFIG_PCI
+ if (spec->use_pci_mmio) {
+ spec->mem_base = pci_iomap(codec->bus->pci, 2, 0xC20);
+ if (spec->mem_base == NULL) {
+ codec_warn(codec, "pci_iomap failed! Setting quirk to QUIRK_NONE.");
+ codec->fixup_id = QUIRK_NONE;
+ }
+ }
+#endif
+
+ spec->base_init_verbs = ca0132_base_init_verbs;
+ spec->base_exit_verbs = ca0132_base_exit_verbs;
+
+ INIT_DELAYED_WORK(&spec->unsol_hp_work, ca0132_unsol_hp_delayed);
+
+ ca0132_init_chip(codec);
+
+ ca0132_config(codec);
+
+ err = ca0132_prepare_verbs(codec);
+ if (err < 0)
+ goto error;
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+ if (err < 0)
+ goto error;
+
+ ca0132_setup_unsol(codec);
+
+ return 0;
+
+ error:
+ ca0132_codec_remove(codec);
+ return err;
+}
+
+static int ca0132_codec_build_controls(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO)
+ return dbpro_build_controls(codec);
+ else
+ return ca0132_build_controls(codec);
+}
+
+static int ca0132_codec_build_pcms(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO)
+ return dbpro_build_pcms(codec);
+ else
+ return ca0132_build_pcms(codec);
+}
+
+static int ca0132_codec_init(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ if (ca0132_quirk(spec) == QUIRK_ZXR_DBPRO)
+ return dbpro_init(codec);
+ else
+ return ca0132_init(codec);
+}
+
+static int ca0132_codec_suspend(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ cancel_delayed_work_sync(&spec->unsol_hp_work);
+ return 0;
+}
+
+static const struct hda_codec_ops ca0132_codec_ops = {
+ .probe = ca0132_codec_probe,
+ .remove = ca0132_codec_remove,
+ .build_controls = ca0132_codec_build_controls,
+ .build_pcms = ca0132_codec_build_pcms,
+ .init = ca0132_codec_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = ca0132_codec_suspend,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_ca0132[] = {
+ HDA_CODEC_ID(0x11020011, "CA0132"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0132);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Creative Sound Core3D codec");
+
+static struct hda_codec_driver ca0132_driver = {
+ .id = snd_hda_id_ca0132,
+ .ops = &ca0132_codec_ops,
+};
+
+module_hda_codec_driver(ca0132_driver);
diff --git a/sound/hda/codecs/ca0132_regs.h b/sound/hda/codecs/ca0132_regs.h
new file mode 100644
index 000000000000..dc0153df3d5c
--- /dev/null
+++ b/sound/hda/codecs/ca0132_regs.h
@@ -0,0 +1,396 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * HD audio codec driver for Creative CA0132 chip.
+ * CA0132 registers defines.
+ *
+ * Copyright (c) 2011, Creative Technology Ltd.
+ */
+
+#ifndef __CA0132_REGS_H
+#define __CA0132_REGS_H
+
+#define DSP_CHIP_OFFSET 0x100000
+#define DSP_DBGCNTL_MODULE_OFFSET 0xE30
+#define DSP_DBGCNTL_INST_OFFSET \
+ (DSP_CHIP_OFFSET + DSP_DBGCNTL_MODULE_OFFSET)
+
+#define DSP_DBGCNTL_EXEC_LOBIT 0x0
+#define DSP_DBGCNTL_EXEC_HIBIT 0x3
+#define DSP_DBGCNTL_EXEC_MASK 0xF
+
+#define DSP_DBGCNTL_SS_LOBIT 0x4
+#define DSP_DBGCNTL_SS_HIBIT 0x7
+#define DSP_DBGCNTL_SS_MASK 0xF0
+
+#define DSP_DBGCNTL_STATE_LOBIT 0xA
+#define DSP_DBGCNTL_STATE_HIBIT 0xD
+#define DSP_DBGCNTL_STATE_MASK 0x3C00
+
+#define XRAM_CHIP_OFFSET 0x0
+#define XRAM_XRAM_CHANNEL_COUNT 0xE000
+#define XRAM_XRAM_MODULE_OFFSET 0x0
+#define XRAM_XRAM_CHAN_INCR 4
+#define XRAM_XRAM_INST_OFFSET(_chan) \
+ (XRAM_CHIP_OFFSET + XRAM_XRAM_MODULE_OFFSET + \
+ (_chan * XRAM_XRAM_CHAN_INCR))
+
+#define YRAM_CHIP_OFFSET 0x40000
+#define YRAM_YRAM_CHANNEL_COUNT 0x8000
+#define YRAM_YRAM_MODULE_OFFSET 0x0
+#define YRAM_YRAM_CHAN_INCR 4
+#define YRAM_YRAM_INST_OFFSET(_chan) \
+ (YRAM_CHIP_OFFSET + YRAM_YRAM_MODULE_OFFSET + \
+ (_chan * YRAM_YRAM_CHAN_INCR))
+
+#define UC_CHIP_OFFSET 0x80000
+#define UC_UC_CHANNEL_COUNT 0x10000
+#define UC_UC_MODULE_OFFSET 0x0
+#define UC_UC_CHAN_INCR 4
+#define UC_UC_INST_OFFSET(_chan) \
+ (UC_CHIP_OFFSET + UC_UC_MODULE_OFFSET + \
+ (_chan * UC_UC_CHAN_INCR))
+
+#define AXRAM_CHIP_OFFSET 0x3C000
+#define AXRAM_AXRAM_CHANNEL_COUNT 0x1000
+#define AXRAM_AXRAM_MODULE_OFFSET 0x0
+#define AXRAM_AXRAM_CHAN_INCR 4
+#define AXRAM_AXRAM_INST_OFFSET(_chan) \
+ (AXRAM_CHIP_OFFSET + AXRAM_AXRAM_MODULE_OFFSET + \
+ (_chan * AXRAM_AXRAM_CHAN_INCR))
+
+#define AYRAM_CHIP_OFFSET 0x78000
+#define AYRAM_AYRAM_CHANNEL_COUNT 0x1000
+#define AYRAM_AYRAM_MODULE_OFFSET 0x0
+#define AYRAM_AYRAM_CHAN_INCR 4
+#define AYRAM_AYRAM_INST_OFFSET(_chan) \
+ (AYRAM_CHIP_OFFSET + AYRAM_AYRAM_MODULE_OFFSET + \
+ (_chan * AYRAM_AYRAM_CHAN_INCR))
+
+#define DSPDMAC_CHIP_OFFSET 0x110000
+#define DSPDMAC_DMA_CFG_CHANNEL_COUNT 12
+#define DSPDMAC_DMACFG_MODULE_OFFSET 0xF00
+#define DSPDMAC_DMACFG_CHAN_INCR 0x10
+#define DSPDMAC_DMACFG_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_DMACFG_MODULE_OFFSET + \
+ (_chan * DSPDMAC_DMACFG_CHAN_INCR))
+
+#define DSPDMAC_DMACFG_DBADR_LOBIT 0x0
+#define DSPDMAC_DMACFG_DBADR_HIBIT 0x10
+#define DSPDMAC_DMACFG_DBADR_MASK 0x1FFFF
+#define DSPDMAC_DMACFG_LP_LOBIT 0x11
+#define DSPDMAC_DMACFG_LP_HIBIT 0x11
+#define DSPDMAC_DMACFG_LP_MASK 0x20000
+
+#define DSPDMAC_DMACFG_AINCR_LOBIT 0x12
+#define DSPDMAC_DMACFG_AINCR_HIBIT 0x12
+#define DSPDMAC_DMACFG_AINCR_MASK 0x40000
+
+#define DSPDMAC_DMACFG_DWR_LOBIT 0x13
+#define DSPDMAC_DMACFG_DWR_HIBIT 0x13
+#define DSPDMAC_DMACFG_DWR_MASK 0x80000
+
+#define DSPDMAC_DMACFG_AJUMP_LOBIT 0x14
+#define DSPDMAC_DMACFG_AJUMP_HIBIT 0x17
+#define DSPDMAC_DMACFG_AJUMP_MASK 0xF00000
+
+#define DSPDMAC_DMACFG_AMODE_LOBIT 0x18
+#define DSPDMAC_DMACFG_AMODE_HIBIT 0x19
+#define DSPDMAC_DMACFG_AMODE_MASK 0x3000000
+
+#define DSPDMAC_DMACFG_LK_LOBIT 0x1A
+#define DSPDMAC_DMACFG_LK_HIBIT 0x1A
+#define DSPDMAC_DMACFG_LK_MASK 0x4000000
+
+#define DSPDMAC_DMACFG_AICS_LOBIT 0x1B
+#define DSPDMAC_DMACFG_AICS_HIBIT 0x1F
+#define DSPDMAC_DMACFG_AICS_MASK 0xF8000000
+
+#define DSPDMAC_DMACFG_LP_SINGLE 0
+#define DSPDMAC_DMACFG_LP_LOOPING 1
+
+#define DSPDMAC_DMACFG_AINCR_XANDY 0
+#define DSPDMAC_DMACFG_AINCR_XORY 1
+
+#define DSPDMAC_DMACFG_DWR_DMA_RD 0
+#define DSPDMAC_DMACFG_DWR_DMA_WR 1
+
+#define DSPDMAC_DMACFG_AMODE_LINEAR 0
+#define DSPDMAC_DMACFG_AMODE_RSV1 1
+#define DSPDMAC_DMACFG_AMODE_WINTLV 2
+#define DSPDMAC_DMACFG_AMODE_GINTLV 3
+
+#define DSPDMAC_DSP_ADR_OFS_CHANNEL_COUNT 12
+#define DSPDMAC_DSPADROFS_MODULE_OFFSET 0xF04
+#define DSPDMAC_DSPADROFS_CHAN_INCR 0x10
+#define DSPDMAC_DSPADROFS_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADROFS_MODULE_OFFSET + \
+ (_chan * DSPDMAC_DSPADROFS_CHAN_INCR))
+
+#define DSPDMAC_DSPADROFS_COFS_LOBIT 0x0
+#define DSPDMAC_DSPADROFS_COFS_HIBIT 0xF
+#define DSPDMAC_DSPADROFS_COFS_MASK 0xFFFF
+
+#define DSPDMAC_DSPADROFS_BOFS_LOBIT 0x10
+#define DSPDMAC_DSPADROFS_BOFS_HIBIT 0x1F
+#define DSPDMAC_DSPADROFS_BOFS_MASK 0xFFFF0000
+
+#define DSPDMAC_DSP_ADR_WOFS_CHANNEL_COUNT 12
+#define DSPDMAC_DSPADRWOFS_MODULE_OFFSET 0xF04
+#define DSPDMAC_DSPADRWOFS_CHAN_INCR 0x10
+
+#define DSPDMAC_DSPADRWOFS_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADRWOFS_MODULE_OFFSET + \
+ (_chan * DSPDMAC_DSPADRWOFS_CHAN_INCR))
+
+#define DSPDMAC_DSPADRWOFS_WCOFS_LOBIT 0x0
+#define DSPDMAC_DSPADRWOFS_WCOFS_HIBIT 0xA
+#define DSPDMAC_DSPADRWOFS_WCOFS_MASK 0x7FF
+
+#define DSPDMAC_DSPADRWOFS_WCBFR_LOBIT 0xB
+#define DSPDMAC_DSPADRWOFS_WCBFR_HIBIT 0xF
+#define DSPDMAC_DSPADRWOFS_WCBFR_MASK 0xF800
+
+#define DSPDMAC_DSPADRWOFS_WBOFS_LOBIT 0x10
+#define DSPDMAC_DSPADRWOFS_WBOFS_HIBIT 0x1A
+#define DSPDMAC_DSPADRWOFS_WBOFS_MASK 0x7FF0000
+
+#define DSPDMAC_DSPADRWOFS_WBBFR_LOBIT 0x1B
+#define DSPDMAC_DSPADRWOFS_WBBFR_HIBIT 0x1F
+#define DSPDMAC_DSPADRWOFS_WBBFR_MASK 0xF8000000
+
+#define DSPDMAC_DSP_ADR_GOFS_CHANNEL_COUNT 12
+#define DSPDMAC_DSPADRGOFS_MODULE_OFFSET 0xF04
+#define DSPDMAC_DSPADRGOFS_CHAN_INCR 0x10
+#define DSPDMAC_DSPADRGOFS_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADRGOFS_MODULE_OFFSET + \
+ (_chan * DSPDMAC_DSPADRGOFS_CHAN_INCR))
+
+#define DSPDMAC_DSPADRGOFS_GCOFS_LOBIT 0x0
+#define DSPDMAC_DSPADRGOFS_GCOFS_HIBIT 0x9
+#define DSPDMAC_DSPADRGOFS_GCOFS_MASK 0x3FF
+
+#define DSPDMAC_DSPADRGOFS_GCS_LOBIT 0xA
+#define DSPDMAC_DSPADRGOFS_GCS_HIBIT 0xC
+#define DSPDMAC_DSPADRGOFS_GCS_MASK 0x1C00
+
+#define DSPDMAC_DSPADRGOFS_GCBFR_LOBIT 0xD
+#define DSPDMAC_DSPADRGOFS_GCBFR_HIBIT 0xF
+#define DSPDMAC_DSPADRGOFS_GCBFR_MASK 0xE000
+
+#define DSPDMAC_DSPADRGOFS_GBOFS_LOBIT 0x10
+#define DSPDMAC_DSPADRGOFS_GBOFS_HIBIT 0x19
+#define DSPDMAC_DSPADRGOFS_GBOFS_MASK 0x3FF0000
+
+#define DSPDMAC_DSPADRGOFS_GBS_LOBIT 0x1A
+#define DSPDMAC_DSPADRGOFS_GBS_HIBIT 0x1C
+#define DSPDMAC_DSPADRGOFS_GBS_MASK 0x1C000000
+
+#define DSPDMAC_DSPADRGOFS_GBBFR_LOBIT 0x1D
+#define DSPDMAC_DSPADRGOFS_GBBFR_HIBIT 0x1F
+#define DSPDMAC_DSPADRGOFS_GBBFR_MASK 0xE0000000
+
+#define DSPDMAC_XFR_CNT_CHANNEL_COUNT 12
+#define DSPDMAC_XFRCNT_MODULE_OFFSET 0xF08
+#define DSPDMAC_XFRCNT_CHAN_INCR 0x10
+
+#define DSPDMAC_XFRCNT_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_XFRCNT_MODULE_OFFSET + \
+ (_chan * DSPDMAC_XFRCNT_CHAN_INCR))
+
+#define DSPDMAC_XFRCNT_CCNT_LOBIT 0x0
+#define DSPDMAC_XFRCNT_CCNT_HIBIT 0xF
+#define DSPDMAC_XFRCNT_CCNT_MASK 0xFFFF
+
+#define DSPDMAC_XFRCNT_BCNT_LOBIT 0x10
+#define DSPDMAC_XFRCNT_BCNT_HIBIT 0x1F
+#define DSPDMAC_XFRCNT_BCNT_MASK 0xFFFF0000
+
+#define DSPDMAC_IRQ_CNT_CHANNEL_COUNT 12
+#define DSPDMAC_IRQCNT_MODULE_OFFSET 0xF0C
+#define DSPDMAC_IRQCNT_CHAN_INCR 0x10
+#define DSPDMAC_IRQCNT_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_IRQCNT_MODULE_OFFSET + \
+ (_chan * DSPDMAC_IRQCNT_CHAN_INCR))
+
+#define DSPDMAC_IRQCNT_CICNT_LOBIT 0x0
+#define DSPDMAC_IRQCNT_CICNT_HIBIT 0xF
+#define DSPDMAC_IRQCNT_CICNT_MASK 0xFFFF
+
+#define DSPDMAC_IRQCNT_BICNT_LOBIT 0x10
+#define DSPDMAC_IRQCNT_BICNT_HIBIT 0x1F
+#define DSPDMAC_IRQCNT_BICNT_MASK 0xFFFF0000
+
+#define DSPDMAC_AUD_CHSEL_CHANNEL_COUNT 12
+#define DSPDMAC_AUDCHSEL_MODULE_OFFSET 0xFC0
+#define DSPDMAC_AUDCHSEL_CHAN_INCR 0x4
+#define DSPDMAC_AUDCHSEL_INST_OFFSET(_chan) \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_AUDCHSEL_MODULE_OFFSET + \
+ (_chan * DSPDMAC_AUDCHSEL_CHAN_INCR))
+
+#define DSPDMAC_AUDCHSEL_ACS_LOBIT 0x0
+#define DSPDMAC_AUDCHSEL_ACS_HIBIT 0x1F
+#define DSPDMAC_AUDCHSEL_ACS_MASK 0xFFFFFFFF
+
+#define DSPDMAC_CHNLSTART_MODULE_OFFSET 0xFF0
+#define DSPDMAC_CHNLSTART_INST_OFFSET \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLSTART_MODULE_OFFSET)
+
+#define DSPDMAC_CHNLSTART_EN_LOBIT 0x0
+#define DSPDMAC_CHNLSTART_EN_HIBIT 0xB
+#define DSPDMAC_CHNLSTART_EN_MASK 0xFFF
+
+#define DSPDMAC_CHNLSTART_VAI1_LOBIT 0xC
+#define DSPDMAC_CHNLSTART_VAI1_HIBIT 0xF
+#define DSPDMAC_CHNLSTART_VAI1_MASK 0xF000
+
+#define DSPDMAC_CHNLSTART_DIS_LOBIT 0x10
+#define DSPDMAC_CHNLSTART_DIS_HIBIT 0x1B
+#define DSPDMAC_CHNLSTART_DIS_MASK 0xFFF0000
+
+#define DSPDMAC_CHNLSTART_VAI2_LOBIT 0x1C
+#define DSPDMAC_CHNLSTART_VAI2_HIBIT 0x1F
+#define DSPDMAC_CHNLSTART_VAI2_MASK 0xF0000000
+
+#define DSPDMAC_CHNLSTATUS_MODULE_OFFSET 0xFF4
+#define DSPDMAC_CHNLSTATUS_INST_OFFSET \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLSTATUS_MODULE_OFFSET)
+
+#define DSPDMAC_CHNLSTATUS_ISC_LOBIT 0x0
+#define DSPDMAC_CHNLSTATUS_ISC_HIBIT 0xB
+#define DSPDMAC_CHNLSTATUS_ISC_MASK 0xFFF
+
+#define DSPDMAC_CHNLSTATUS_AOO_LOBIT 0xC
+#define DSPDMAC_CHNLSTATUS_AOO_HIBIT 0xC
+#define DSPDMAC_CHNLSTATUS_AOO_MASK 0x1000
+
+#define DSPDMAC_CHNLSTATUS_AOU_LOBIT 0xD
+#define DSPDMAC_CHNLSTATUS_AOU_HIBIT 0xD
+#define DSPDMAC_CHNLSTATUS_AOU_MASK 0x2000
+
+#define DSPDMAC_CHNLSTATUS_AIO_LOBIT 0xE
+#define DSPDMAC_CHNLSTATUS_AIO_HIBIT 0xE
+#define DSPDMAC_CHNLSTATUS_AIO_MASK 0x4000
+
+#define DSPDMAC_CHNLSTATUS_AIU_LOBIT 0xF
+#define DSPDMAC_CHNLSTATUS_AIU_HIBIT 0xF
+#define DSPDMAC_CHNLSTATUS_AIU_MASK 0x8000
+
+#define DSPDMAC_CHNLSTATUS_IEN_LOBIT 0x10
+#define DSPDMAC_CHNLSTATUS_IEN_HIBIT 0x1B
+#define DSPDMAC_CHNLSTATUS_IEN_MASK 0xFFF0000
+
+#define DSPDMAC_CHNLSTATUS_VAI0_LOBIT 0x1C
+#define DSPDMAC_CHNLSTATUS_VAI0_HIBIT 0x1F
+#define DSPDMAC_CHNLSTATUS_VAI0_MASK 0xF0000000
+
+#define DSPDMAC_CHNLPROP_MODULE_OFFSET 0xFF8
+#define DSPDMAC_CHNLPROP_INST_OFFSET \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLPROP_MODULE_OFFSET)
+
+#define DSPDMAC_CHNLPROP_DCON_LOBIT 0x0
+#define DSPDMAC_CHNLPROP_DCON_HIBIT 0xB
+#define DSPDMAC_CHNLPROP_DCON_MASK 0xFFF
+
+#define DSPDMAC_CHNLPROP_FFS_LOBIT 0xC
+#define DSPDMAC_CHNLPROP_FFS_HIBIT 0xC
+#define DSPDMAC_CHNLPROP_FFS_MASK 0x1000
+
+#define DSPDMAC_CHNLPROP_NAJ_LOBIT 0xD
+#define DSPDMAC_CHNLPROP_NAJ_HIBIT 0xD
+#define DSPDMAC_CHNLPROP_NAJ_MASK 0x2000
+
+#define DSPDMAC_CHNLPROP_ENH_LOBIT 0xE
+#define DSPDMAC_CHNLPROP_ENH_HIBIT 0xE
+#define DSPDMAC_CHNLPROP_ENH_MASK 0x4000
+
+#define DSPDMAC_CHNLPROP_MSPCE_LOBIT 0x10
+#define DSPDMAC_CHNLPROP_MSPCE_HIBIT 0x1B
+#define DSPDMAC_CHNLPROP_MSPCE_MASK 0xFFF0000
+
+#define DSPDMAC_CHNLPROP_AC_LOBIT 0x1C
+#define DSPDMAC_CHNLPROP_AC_HIBIT 0x1F
+#define DSPDMAC_CHNLPROP_AC_MASK 0xF0000000
+
+#define DSPDMAC_ACTIVE_MODULE_OFFSET 0xFFC
+#define DSPDMAC_ACTIVE_INST_OFFSET \
+ (DSPDMAC_CHIP_OFFSET + DSPDMAC_ACTIVE_MODULE_OFFSET)
+
+#define DSPDMAC_ACTIVE_AAR_LOBIT 0x0
+#define DSPDMAC_ACTIVE_AAR_HIBIT 0xB
+#define DSPDMAC_ACTIVE_AAR_MASK 0xFFF
+
+#define DSPDMAC_ACTIVE_WFR_LOBIT 0xC
+#define DSPDMAC_ACTIVE_WFR_HIBIT 0x17
+#define DSPDMAC_ACTIVE_WFR_MASK 0xFFF000
+
+#define DSP_AUX_MEM_BASE 0xE000
+#define INVALID_CHIP_ADDRESS (~0U)
+
+#define X_SIZE (XRAM_XRAM_CHANNEL_COUNT * XRAM_XRAM_CHAN_INCR)
+#define Y_SIZE (YRAM_YRAM_CHANNEL_COUNT * YRAM_YRAM_CHAN_INCR)
+#define AX_SIZE (AXRAM_AXRAM_CHANNEL_COUNT * AXRAM_AXRAM_CHAN_INCR)
+#define AY_SIZE (AYRAM_AYRAM_CHANNEL_COUNT * AYRAM_AYRAM_CHAN_INCR)
+#define UC_SIZE (UC_UC_CHANNEL_COUNT * UC_UC_CHAN_INCR)
+
+#define XEXT_SIZE (X_SIZE + AX_SIZE)
+#define YEXT_SIZE (Y_SIZE + AY_SIZE)
+
+#define U64K 0x10000UL
+
+#define X_END (XRAM_CHIP_OFFSET + X_SIZE)
+#define X_EXT (XRAM_CHIP_OFFSET + XEXT_SIZE)
+#define AX_END (XRAM_CHIP_OFFSET + U64K*4)
+
+#define Y_END (YRAM_CHIP_OFFSET + Y_SIZE)
+#define Y_EXT (YRAM_CHIP_OFFSET + YEXT_SIZE)
+#define AY_END (YRAM_CHIP_OFFSET + U64K*4)
+
+#define UC_END (UC_CHIP_OFFSET + UC_SIZE)
+
+#define X_RANGE_MAIN(a, s) \
+ (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < X_END))
+#define X_RANGE_AUX(a, s) \
+ (((a) >= X_END) && ((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < AX_END))
+#define X_RANGE_EXT(a, s) \
+ (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < X_EXT))
+#define X_RANGE_ALL(a, s) \
+ (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < AX_END))
+
+#define Y_RANGE_MAIN(a, s) \
+ (((a) >= YRAM_CHIP_OFFSET) && \
+ ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < Y_END))
+#define Y_RANGE_AUX(a, s) \
+ (((a) >= Y_END) && \
+ ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < AY_END))
+#define Y_RANGE_EXT(a, s) \
+ (((a) >= YRAM_CHIP_OFFSET) && \
+ ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < Y_EXT))
+#define Y_RANGE_ALL(a, s) \
+ (((a) >= YRAM_CHIP_OFFSET) && \
+ ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < AY_END))
+
+#define UC_RANGE(a, s) \
+ (((a) >= UC_CHIP_OFFSET) && \
+ ((a)+((s)-1)*UC_UC_CHAN_INCR < UC_END))
+
+#define X_OFF(a) \
+ (((a) - XRAM_CHIP_OFFSET) / XRAM_XRAM_CHAN_INCR)
+#define AX_OFF(a) \
+ (((a) % (AXRAM_AXRAM_CHANNEL_COUNT * \
+ AXRAM_AXRAM_CHAN_INCR)) / AXRAM_AXRAM_CHAN_INCR)
+
+#define Y_OFF(a) \
+ (((a) - YRAM_CHIP_OFFSET) / YRAM_YRAM_CHAN_INCR)
+#define AY_OFF(a) \
+ (((a) % (AYRAM_AYRAM_CHANNEL_COUNT * \
+ AYRAM_AYRAM_CHAN_INCR)) / AYRAM_AYRAM_CHAN_INCR)
+
+#define UC_OFF(a) (((a) - UC_CHIP_OFFSET) / UC_UC_CHAN_INCR)
+
+#define X_EXT_MAIN_SIZE(a) (XRAM_XRAM_CHANNEL_COUNT - X_OFF(a))
+#define X_EXT_AUX_SIZE(a, s) ((s) - X_EXT_MAIN_SIZE(a))
+
+#define Y_EXT_MAIN_SIZE(a) (YRAM_YRAM_CHANNEL_COUNT - Y_OFF(a))
+#define Y_EXT_AUX_SIZE(a, s) ((s) - Y_EXT_MAIN_SIZE(a))
+
+#endif
diff --git a/sound/hda/codecs/cirrus/Kconfig b/sound/hda/codecs/cirrus/Kconfig
new file mode 100644
index 000000000000..ec6cbcaf64f0
--- /dev/null
+++ b/sound/hda/codecs/cirrus/Kconfig
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menuconfig SND_HDA_CODEC_CIRRUS
+ tristate "Cirrus Logic HD-audio codec support"
+ help
+ Say Y or M here to include Cirrus Logic HD-audio codec support.
+
+ This will enable both CS420x and CS421x HD-audio codec drivers
+ as default, but you can enable/disable each codec driver
+ individually, too (only when CONFIG_EXPERT is set).
+
+if SND_HDA_CODEC_CIRRUS
+
+config SND_HDA_CODEC_CS420X
+ tristate "Build Cirrus Logic CS420x codec support" if EXPERT
+ select SND_HDA_GENERIC
+ default y
+ help
+ Say Y or M here to include Cirrus Logic CS420x codec support
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CS420X=m
+
+config SND_HDA_CODEC_CS421X
+ tristate "Build Cirrus Logic CS421x codec support" if EXPERT
+ select SND_HDA_GENERIC
+ default y
+ help
+ Say Y or M here to include Cirrus Logic CS421x codec support
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CS421X=m
+
+config SND_HDA_CODEC_CS8409
+ tristate "Build Cirrus Logic HDA bridge support"
+ select SND_HDA_GENERIC
+ help
+ Say Y or M here to include Cirrus Logic HDA bridge support
+ such as CS8409.
+
+comment "Set to Y if you want auto-loading the codec driver"
+ depends on SND_HDA=y && SND_HDA_CODEC_CS8409=m
+
+endif
diff --git a/sound/hda/codecs/cirrus/Makefile b/sound/hda/codecs/cirrus/Makefile
new file mode 100644
index 000000000000..dda1873ebcf5
--- /dev/null
+++ b/sound/hda/codecs/cirrus/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+subdir-ccflags-y += -I$(src)/../../common
+
+snd-hda-codec-cs420x-y := cs420x.o
+snd-hda-codec-cs421x-y := cs421x.o
+snd-hda-codec-cs8409-y := cs8409.o cs8409-tables.o
+
+obj-$(CONFIG_SND_HDA_CODEC_CS420X) += snd-hda-codec-cs420x.o
+obj-$(CONFIG_SND_HDA_CODEC_CS421X) += snd-hda-codec-cs421x.o
+obj-$(CONFIG_SND_HDA_CODEC_CS8409) += snd-hda-codec-cs8409.o
diff --git a/sound/hda/codecs/cirrus/cs420x.c b/sound/hda/codecs/cirrus/cs420x.c
new file mode 100644
index 000000000000..13f5f1711fa4
--- /dev/null
+++ b/sound/hda/codecs/cirrus/cs420x.c
@@ -0,0 +1,786 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Cirrus Logic CS420x HD-audio codec
+ *
+ * Copyright (c) 2009 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <linux/pci.h>
+#include <sound/tlv.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "../generic.h"
+
+struct cs_spec {
+ struct hda_gen_spec gen;
+
+ unsigned int gpio_mask;
+ unsigned int gpio_dir;
+ unsigned int gpio_data;
+ unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */
+ unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */
+
+ hda_nid_t vendor_nid;
+
+ /* for MBP SPDIF control */
+ int (*spdif_sw_put)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+};
+
+/* available models with CS420x */
+enum {
+ CS420X_MBP53,
+ CS420X_MBP55,
+ CS420X_IMAC27,
+ CS420X_GPIO_13,
+ CS420X_GPIO_23,
+ CS420X_MBP101,
+ CS420X_MBP81,
+ CS420X_MBA42,
+ CS420X_AUTO,
+ /* aliases */
+ CS420X_IMAC27_122 = CS420X_GPIO_23,
+ CS420X_APPLE = CS420X_GPIO_13,
+};
+
+/* Vendor-specific processing widget */
+#define CS420X_VENDOR_NID 0x11
+#define CS_DIG_OUT1_PIN_NID 0x10
+#define CS_DIG_OUT2_PIN_NID 0x15
+#define CS_DMIC1_PIN_NID 0x0e
+#define CS_DMIC2_PIN_NID 0x12
+
+/* coef indices */
+#define IDX_SPDIF_STAT 0x0000
+#define IDX_SPDIF_CTL 0x0001
+#define IDX_ADC_CFG 0x0002
+/* SZC bitmask, 4 modes below:
+ * 0 = immediate,
+ * 1 = digital immediate, analog zero-cross
+ * 2 = digtail & analog soft-ramp
+ * 3 = digital soft-ramp, analog zero-cross
+ */
+#define CS_COEF_ADC_SZC_MASK (3 << 0)
+#define CS_COEF_ADC_MIC_SZC_MODE (3 << 0) /* SZC setup for mic */
+#define CS_COEF_ADC_LI_SZC_MODE (3 << 0) /* SZC setup for line-in */
+/* PGA mode: 0 = differential, 1 = signle-ended */
+#define CS_COEF_ADC_MIC_PGA_MODE (1 << 5) /* PGA setup for mic */
+#define CS_COEF_ADC_LI_PGA_MODE (1 << 6) /* PGA setup for line-in */
+#define IDX_DAC_CFG 0x0003
+/* SZC bitmask, 4 modes below:
+ * 0 = Immediate
+ * 1 = zero-cross
+ * 2 = soft-ramp
+ * 3 = soft-ramp on zero-cross
+ */
+#define CS_COEF_DAC_HP_SZC_MODE (3 << 0) /* nid 0x02 */
+#define CS_COEF_DAC_LO_SZC_MODE (3 << 2) /* nid 0x03 */
+#define CS_COEF_DAC_SPK_SZC_MODE (3 << 4) /* nid 0x04 */
+
+#define IDX_BEEP_CFG 0x0004
+/* 0x0008 - test reg key */
+/* 0x0009 - 0x0014 -> 12 test regs */
+/* 0x0015 - visibility reg */
+
+/* Cirrus Logic CS4208 */
+#define CS4208_VENDOR_NID 0x24
+
+static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
+{
+ struct cs_spec *spec = codec->spec;
+
+ snd_hda_codec_write(codec, spec->vendor_nid, 0,
+ AC_VERB_SET_COEF_INDEX, idx);
+ return snd_hda_codec_read(codec, spec->vendor_nid, 0,
+ AC_VERB_GET_PROC_COEF, 0);
+}
+
+static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
+ unsigned int coef)
+{
+ struct cs_spec *spec = codec->spec;
+
+ snd_hda_codec_write(codec, spec->vendor_nid, 0,
+ AC_VERB_SET_COEF_INDEX, idx);
+ snd_hda_codec_write(codec, spec->vendor_nid, 0,
+ AC_VERB_SET_PROC_COEF, coef);
+}
+
+/*
+ * auto-mute and auto-mic switching
+ * CS421x auto-output redirecting
+ * HP/SPK/SPDIF
+ */
+
+static void cs_automute(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+
+ snd_hda_gen_update_outputs(codec);
+
+ if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) {
+ if (spec->gen.automute_speaker)
+ spec->gpio_data = spec->gen.hp_jack_present ?
+ spec->gpio_eapd_hp : spec->gpio_eapd_speaker;
+ else
+ spec->gpio_data =
+ spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, spec->gpio_data);
+ }
+}
+
+static bool is_active_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int val;
+
+ val = snd_hda_codec_get_pincfg(codec, nid);
+ return (get_defcfg_connect(val) != AC_JACK_PORT_NONE);
+}
+
+static void init_input_coef(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ unsigned int coef;
+
+ /* CS420x has multiple ADC, CS421x has single ADC */
+ if (spec->vendor_nid == CS420X_VENDOR_NID) {
+ coef = cs_vendor_coef_get(codec, IDX_BEEP_CFG);
+ if (is_active_pin(codec, CS_DMIC2_PIN_NID))
+ coef |= 1 << 4; /* DMIC2 2 chan on, GPIO1 off */
+ if (is_active_pin(codec, CS_DMIC1_PIN_NID))
+ coef |= 1 << 3; /* DMIC1 2 chan on, GPIO0 off
+ * No effect if SPDIF_OUT2 is
+ * selected in IDX_SPDIF_CTL.
+ */
+
+ cs_vendor_coef_set(codec, IDX_BEEP_CFG, coef);
+ }
+}
+
+static const struct hda_verb cs_coef_init_verbs[] = {
+ {0x11, AC_VERB_SET_PROC_STATE, 1},
+ {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG},
+ {0x11, AC_VERB_SET_PROC_COEF,
+ (0x002a /* DAC1/2/3 SZCMode Soft Ramp */
+ | 0x0040 /* Mute DACs on FIFO error */
+ | 0x1000 /* Enable DACs High Pass Filter */
+ | 0x0400 /* Disable Coefficient Auto increment */
+ )},
+ /* ADC1/2 - Digital and Analog Soft Ramp */
+ {0x11, AC_VERB_SET_COEF_INDEX, IDX_ADC_CFG},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x000a},
+ /* Beep */
+ {0x11, AC_VERB_SET_COEF_INDEX, IDX_BEEP_CFG},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x0007}, /* Enable Beep thru DAC1/2/3 */
+
+ {} /* terminator */
+};
+
+static const struct hda_verb cs4208_coef_init_verbs[] = {
+ {0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */
+ {0x24, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */
+ {0x24, AC_VERB_SET_COEF_INDEX, 0x0033},
+ {0x24, AC_VERB_SET_PROC_COEF, 0x0001}, /* A1 ICS */
+ {0x24, AC_VERB_SET_COEF_INDEX, 0x0034},
+ {0x24, AC_VERB_SET_PROC_COEF, 0x1C01}, /* A1 Enable, A Thresh = 300mV */
+ {} /* terminator */
+};
+
+/* Errata: CS4207 rev C0/C1/C2 Silicon
+ *
+ * http://www.cirrus.com/en/pubs/errata/ER880C3.pdf
+ *
+ * 6. At high temperature (TA > +85°C), the digital supply current (IVD)
+ * may be excessive (up to an additional 200 μA), which is most easily
+ * observed while the part is being held in reset (RESET# active low).
+ *
+ * Root Cause: At initial powerup of the device, the logic that drives
+ * the clock and write enable to the S/PDIF SRC RAMs is not properly
+ * initialized.
+ * Certain random patterns will cause a steady leakage current in those
+ * RAM cells. The issue will resolve once the SRCs are used (turned on).
+ *
+ * Workaround: The following verb sequence briefly turns on the S/PDIF SRC
+ * blocks, which will alleviate the issue.
+ */
+
+static const struct hda_verb cs_errata_init_verbs[] = {
+ {0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */
+ {0x11, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */
+
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0008},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x9999},
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0017},
+ {0x11, AC_VERB_SET_PROC_COEF, 0xa412},
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x0009},
+
+ {0x07, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Rx: D0 */
+ {0x08, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Tx: D0 */
+
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0017},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x2412},
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0008},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x0000},
+ {0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x0008},
+ {0x11, AC_VERB_SET_PROC_STATE, 0x00},
+ {} /* terminator */
+};
+
+/* SPDIF setup */
+static void init_digital_coef(struct hda_codec *codec)
+{
+ unsigned int coef;
+
+ coef = 0x0002; /* SRC_MUTE soft-mute on SPDIF (if no lock) */
+ coef |= 0x0008; /* Replace with mute on error */
+ if (is_active_pin(codec, CS_DIG_OUT2_PIN_NID))
+ coef |= 0x4000; /* RX to TX1 or TX2 Loopthru / SPDIF2
+ * SPDIF_OUT2 is shared with GPIO1 and
+ * DMIC_SDA2.
+ */
+ cs_vendor_coef_set(codec, IDX_SPDIF_CTL, coef);
+}
+
+static int cs_init(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+
+ if (spec->vendor_nid == CS420X_VENDOR_NID) {
+ /* init_verb sequence for C0/C1/C2 errata*/
+ snd_hda_sequence_write(codec, cs_errata_init_verbs);
+ snd_hda_sequence_write(codec, cs_coef_init_verbs);
+ } else if (spec->vendor_nid == CS4208_VENDOR_NID) {
+ snd_hda_sequence_write(codec, cs4208_coef_init_verbs);
+ }
+
+ snd_hda_gen_init(codec);
+
+ if (spec->gpio_mask) {
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
+ spec->gpio_mask);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
+ spec->gpio_dir);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_data);
+ }
+
+ if (spec->vendor_nid == CS420X_VENDOR_NID) {
+ init_input_coef(codec);
+ init_digital_coef(codec);
+ }
+
+ return 0;
+}
+
+static int cs_build_controls(struct hda_codec *codec)
+{
+ int err;
+
+ err = snd_hda_gen_build_controls(codec);
+ if (err < 0)
+ return err;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD);
+ return 0;
+}
+
+static int cs_parse_auto_config(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ int err;
+ int i;
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
+ if (err < 0)
+ return err;
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
+ if (err < 0)
+ return err;
+
+ /* keep the ADCs powered up when it's dynamically switchable */
+ if (spec->gen.dyn_adc_switch) {
+ unsigned int done = 0;
+
+ for (i = 0; i < spec->gen.input_mux.num_items; i++) {
+ int idx = spec->gen.dyn_adc_idx[i];
+
+ if (done & (1 << idx))
+ continue;
+ snd_hda_gen_fix_pin_power(codec,
+ spec->gen.adc_nids[idx]);
+ done |= 1 << idx;
+ }
+ }
+
+ return 0;
+}
+
+static const struct hda_model_fixup cs420x_models[] = {
+ { .id = CS420X_MBP53, .name = "mbp53" },
+ { .id = CS420X_MBP55, .name = "mbp55" },
+ { .id = CS420X_IMAC27, .name = "imac27" },
+ { .id = CS420X_IMAC27_122, .name = "imac27_122" },
+ { .id = CS420X_APPLE, .name = "apple" },
+ { .id = CS420X_MBP101, .name = "mbp101" },
+ { .id = CS420X_MBP81, .name = "mbp81" },
+ { .id = CS420X_MBA42, .name = "mba42" },
+ {}
+};
+
+static const struct hda_quirk cs420x_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53),
+ SND_PCI_QUIRK(0x10de, 0x0d94, "MacBookAir 3,1(2)", CS420X_MBP55),
+ SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
+ SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55),
+ /* this conflicts with too many other models */
+ /*SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),*/
+
+ /* codec SSID */
+ SND_PCI_QUIRK(0x106b, 0x0600, "iMac 14,1", CS420X_IMAC27_122),
+ SND_PCI_QUIRK(0x106b, 0x0900, "iMac 12,1", CS420X_IMAC27_122),
+ SND_PCI_QUIRK(0x106b, 0x1c00, "MacBookPro 8,1", CS420X_MBP81),
+ SND_PCI_QUIRK(0x106b, 0x2000, "iMac 12,2", CS420X_IMAC27_122),
+ SND_PCI_QUIRK(0x106b, 0x2800, "MacBookPro 10,1", CS420X_MBP101),
+ SND_PCI_QUIRK(0x106b, 0x5600, "MacBookAir 5,2", CS420X_MBP81),
+ SND_PCI_QUIRK(0x106b, 0x5b00, "MacBookAir 4,2", CS420X_MBA42),
+ SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS420X_APPLE),
+ {} /* terminator */
+};
+
+static const struct hda_pintbl mbp53_pincfgs[] = {
+ { 0x09, 0x012b4050 },
+ { 0x0a, 0x90100141 },
+ { 0x0b, 0x90100140 },
+ { 0x0c, 0x018b3020 },
+ { 0x0d, 0x90a00110 },
+ { 0x0e, 0x400000f0 },
+ { 0x0f, 0x01cbe030 },
+ { 0x10, 0x014be060 },
+ { 0x12, 0x400000f0 },
+ { 0x15, 0x400000f0 },
+ {} /* terminator */
+};
+
+static const struct hda_pintbl mbp55_pincfgs[] = {
+ { 0x09, 0x012b4030 },
+ { 0x0a, 0x90100121 },
+ { 0x0b, 0x90100120 },
+ { 0x0c, 0x400000f0 },
+ { 0x0d, 0x90a00110 },
+ { 0x0e, 0x400000f0 },
+ { 0x0f, 0x400000f0 },
+ { 0x10, 0x014be040 },
+ { 0x12, 0x400000f0 },
+ { 0x15, 0x400000f0 },
+ {} /* terminator */
+};
+
+static const struct hda_pintbl imac27_pincfgs[] = {
+ { 0x09, 0x012b4050 },
+ { 0x0a, 0x90100140 },
+ { 0x0b, 0x90100142 },
+ { 0x0c, 0x018b3020 },
+ { 0x0d, 0x90a00110 },
+ { 0x0e, 0x400000f0 },
+ { 0x0f, 0x01cbe030 },
+ { 0x10, 0x014be060 },
+ { 0x12, 0x01ab9070 },
+ { 0x15, 0x400000f0 },
+ {} /* terminator */
+};
+
+static const struct hda_pintbl mbp101_pincfgs[] = {
+ { 0x0d, 0x40ab90f0 },
+ { 0x0e, 0x90a600f0 },
+ { 0x12, 0x50a600f0 },
+ {} /* terminator */
+};
+
+static const struct hda_pintbl mba42_pincfgs[] = {
+ { 0x09, 0x012b4030 }, /* HP */
+ { 0x0a, 0x400000f0 },
+ { 0x0b, 0x90100120 }, /* speaker */
+ { 0x0c, 0x400000f0 },
+ { 0x0d, 0x90a00110 }, /* mic */
+ { 0x0e, 0x400000f0 },
+ { 0x0f, 0x400000f0 },
+ { 0x10, 0x400000f0 },
+ { 0x12, 0x400000f0 },
+ { 0x15, 0x400000f0 },
+ {} /* terminator */
+};
+
+static const struct hda_pintbl mba6_pincfgs[] = {
+ { 0x10, 0x032120f0 }, /* HP */
+ { 0x11, 0x500000f0 },
+ { 0x12, 0x90100010 }, /* Speaker */
+ { 0x13, 0x500000f0 },
+ { 0x14, 0x500000f0 },
+ { 0x15, 0x770000f0 },
+ { 0x16, 0x770000f0 },
+ { 0x17, 0x430000f0 },
+ { 0x18, 0x43ab9030 }, /* Mic */
+ { 0x19, 0x770000f0 },
+ { 0x1a, 0x770000f0 },
+ { 0x1b, 0x770000f0 },
+ { 0x1c, 0x90a00090 },
+ { 0x1d, 0x500000f0 },
+ { 0x1e, 0x500000f0 },
+ { 0x1f, 0x500000f0 },
+ { 0x20, 0x500000f0 },
+ { 0x21, 0x430000f0 },
+ { 0x22, 0x430000f0 },
+ {} /* terminator */
+};
+
+static void cs420x_fixup_gpio_13(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct cs_spec *spec = codec->spec;
+
+ spec->gpio_eapd_hp = 2; /* GPIO1 = headphones */
+ spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */
+ spec->gpio_mask = spec->gpio_dir =
+ spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
+ }
+}
+
+static void cs420x_fixup_gpio_23(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct cs_spec *spec = codec->spec;
+
+ spec->gpio_eapd_hp = 4; /* GPIO2 = headphones */
+ spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */
+ spec->gpio_mask = spec->gpio_dir =
+ spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
+ }
+}
+
+static const struct hda_fixup cs420x_fixups[] = {
+ [CS420X_MBP53] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = mbp53_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_APPLE,
+ },
+ [CS420X_MBP55] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = mbp55_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_GPIO_13,
+ },
+ [CS420X_IMAC27] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = imac27_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_GPIO_13,
+ },
+ [CS420X_GPIO_13] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs420x_fixup_gpio_13,
+ },
+ [CS420X_GPIO_23] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs420x_fixup_gpio_23,
+ },
+ [CS420X_MBP101] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = mbp101_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_GPIO_13,
+ },
+ [CS420X_MBP81] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* internal mic ADC2: right only, single ended */
+ {0x11, AC_VERB_SET_COEF_INDEX, IDX_ADC_CFG},
+ {0x11, AC_VERB_SET_PROC_COEF, 0x102a},
+ {}
+ },
+ .chained = true,
+ .chain_id = CS420X_GPIO_13,
+ },
+ [CS420X_MBA42] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = mba42_pincfgs,
+ .chained = true,
+ .chain_id = CS420X_GPIO_13,
+ },
+};
+
+static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid)
+{
+ struct cs_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return NULL;
+ codec->spec = spec;
+ spec->vendor_nid = vendor_nid;
+ codec->power_save_node = 1;
+ snd_hda_gen_spec_init(&spec->gen);
+
+ return spec;
+}
+
+static int cs420x_probe(struct hda_codec *codec)
+{
+ int err;
+
+ codec->single_adc_amp = 1;
+
+ snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl,
+ cs420x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = cs_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+}
+
+/*
+ * CS4208 support:
+ * Its layout is no longer compatible with CS4206/CS4207
+ */
+enum {
+ CS4208_MAC_AUTO,
+ CS4208_MBA6,
+ CS4208_MBP11,
+ CS4208_MACMINI,
+ CS4208_GPIO0,
+};
+
+static const struct hda_model_fixup cs4208_models[] = {
+ { .id = CS4208_GPIO0, .name = "gpio0" },
+ { .id = CS4208_MBA6, .name = "mba6" },
+ { .id = CS4208_MBP11, .name = "mbp11" },
+ { .id = CS4208_MACMINI, .name = "macmini" },
+ {}
+};
+
+static const struct hda_quirk cs4208_fixup_tbl[] = {
+ SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS4208_MAC_AUTO),
+ {} /* terminator */
+};
+
+/* codec SSID matching */
+static const struct hda_quirk cs4208_mac_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x106b, 0x5e00, "MacBookPro 11,2", CS4208_MBP11),
+ SND_PCI_QUIRK(0x106b, 0x6c00, "MacMini 7,1", CS4208_MACMINI),
+ SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6),
+ SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6),
+ SND_PCI_QUIRK(0x106b, 0x7800, "MacPro 6,1", CS4208_MACMINI),
+ SND_PCI_QUIRK(0x106b, 0x7b00, "MacBookPro 12,1", CS4208_MBP11),
+ {} /* terminator */
+};
+
+static void cs4208_fixup_gpio0(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct cs_spec *spec = codec->spec;
+
+ spec->gpio_eapd_hp = 0;
+ spec->gpio_eapd_speaker = 1;
+ spec->gpio_mask = spec->gpio_dir =
+ spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
+ }
+}
+
+static const struct hda_fixup cs4208_fixups[];
+
+/* remap the fixup from codec SSID and apply it */
+static void cs4208_fixup_mac(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ codec->fixup_id = HDA_FIXUP_ID_NOT_SET;
+ snd_hda_pick_fixup(codec, NULL, cs4208_mac_fixup_tbl, cs4208_fixups);
+ if (codec->fixup_id == HDA_FIXUP_ID_NOT_SET)
+ codec->fixup_id = CS4208_GPIO0; /* default fixup */
+ snd_hda_apply_fixup(codec, action);
+}
+
+/* MacMini 7,1 has the inverted jack detection */
+static void cs4208_fixup_macmini(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x18, 0x00ab9150 }, /* mic (audio-in) jack: disable detect */
+ { 0x21, 0x004be140 }, /* SPDIF: disable detect */
+ { }
+ };
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* HP pin (0x10) has an inverted detection */
+ codec->inv_jack_detect = 1;
+ /* disable the bogus Mic and SPDIF jack detections */
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ }
+}
+
+static int cs4208_spdif_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs_spec *spec = codec->spec;
+ hda_nid_t pin = spec->gen.autocfg.dig_out_pins[0];
+ int pinctl = ucontrol->value.integer.value[0] ? PIN_OUT : 0;
+
+ snd_hda_set_pin_ctl_cache(codec, pin, pinctl);
+ return spec->spdif_sw_put(kcontrol, ucontrol);
+}
+
+/* hook the SPDIF switch */
+static void cs4208_fixup_spdif_switch(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_BUILD) {
+ struct cs_spec *spec = codec->spec;
+ struct snd_kcontrol *kctl;
+
+ if (!spec->gen.autocfg.dig_out_pins[0])
+ return;
+ kctl = snd_hda_find_mixer_ctl(codec, "IEC958 Playback Switch");
+ if (!kctl)
+ return;
+ spec->spdif_sw_put = kctl->put;
+ kctl->put = cs4208_spdif_sw_put;
+ }
+}
+
+static const struct hda_fixup cs4208_fixups[] = {
+ [CS4208_MBA6] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = mba6_pincfgs,
+ .chained = true,
+ .chain_id = CS4208_GPIO0,
+ },
+ [CS4208_MBP11] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs4208_fixup_spdif_switch,
+ .chained = true,
+ .chain_id = CS4208_GPIO0,
+ },
+ [CS4208_MACMINI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs4208_fixup_macmini,
+ .chained = true,
+ .chain_id = CS4208_GPIO0,
+ },
+ [CS4208_GPIO0] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs4208_fixup_gpio0,
+ },
+ [CS4208_MAC_AUTO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs4208_fixup_mac,
+ },
+};
+
+/* correct the 0dB offset of input pins */
+static void cs4208_fix_amp_caps(struct hda_codec *codec, hda_nid_t adc)
+{
+ unsigned int caps;
+
+ caps = query_amp_caps(codec, adc, HDA_INPUT);
+ caps &= ~(AC_AMPCAP_OFFSET);
+ caps |= 0x02;
+ snd_hda_override_amp_caps(codec, adc, HDA_INPUT, caps);
+}
+
+static int cs4208_probe(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ int err;
+
+ /* exclude NID 0x10 (HP) from output volumes due to different steps */
+ spec->gen.out_vol_mask = 1ULL << 0x10;
+
+ snd_hda_pick_fixup(codec, cs4208_models, cs4208_fixup_tbl,
+ cs4208_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ snd_hda_override_wcaps(codec, 0x18,
+ get_wcaps(codec, 0x18) | AC_WCAP_STEREO);
+ cs4208_fix_amp_caps(codec, 0x18);
+ cs4208_fix_amp_caps(codec, 0x1b);
+ cs4208_fix_amp_caps(codec, 0x1c);
+
+ err = cs_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+}
+
+static int cs_codec_probe(struct hda_codec *codec,
+ const struct hda_device_id *id)
+{
+ struct cs_spec *spec;
+ int err;
+
+ spec = cs_alloc_spec(codec, id->driver_data);
+ if (!spec)
+ return -ENOMEM;
+ spec->gen.automute_hook = cs_automute;
+
+ if (spec->vendor_nid == CS4208_VENDOR_NID)
+ err = cs4208_probe(codec);
+ else
+ err = cs420x_probe(codec);
+ if (err < 0)
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops cs_codec_ops = {
+ .probe = cs_codec_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = cs_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cs_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_cs420x[] = {
+ HDA_CODEC_ID_MODEL(0x10134206, "CS4206", CS420X_VENDOR_NID),
+ HDA_CODEC_ID_MODEL(0x10134207, "CS4207", CS420X_VENDOR_NID),
+ HDA_CODEC_ID_MODEL(0x10134208, "CS4208", CS4208_VENDOR_NID),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cs420x);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cirrus Logic CS420x HD-audio codec");
+
+static struct hda_codec_driver cs420x_driver = {
+ .id = snd_hda_id_cs420x,
+ .ops = &cs_codec_ops,
+};
+
+module_hda_codec_driver(cs420x_driver);
diff --git a/sound/hda/codecs/cirrus/cs421x.c b/sound/hda/codecs/cirrus/cs421x.c
new file mode 100644
index 000000000000..a93e2e0bb391
--- /dev/null
+++ b/sound/hda/codecs/cirrus/cs421x.c
@@ -0,0 +1,590 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Cirrus Logic CS421x HD-audio codec
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <linux/pci.h>
+#include <sound/tlv.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "../generic.h"
+
+struct cs_spec {
+ struct hda_gen_spec gen;
+
+ unsigned int gpio_mask;
+ unsigned int gpio_dir;
+ unsigned int gpio_data;
+ unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */
+ unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */
+
+ /* CS421x */
+ unsigned int spdif_detect:1;
+ unsigned int spdif_present:1;
+ unsigned int sense_b:1;
+ hda_nid_t vendor_nid;
+
+ /* for MBP SPDIF control */
+ int (*spdif_sw_put)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+};
+
+/* CS421x boards */
+enum {
+ CS421X_CDB4210,
+ CS421X_SENSE_B,
+ CS421X_STUMPY,
+};
+
+/* Vendor-specific processing widget */
+#define CS_DIG_OUT1_PIN_NID 0x10
+#define CS_DIG_OUT2_PIN_NID 0x15
+#define CS_DMIC1_PIN_NID 0x0e
+#define CS_DMIC2_PIN_NID 0x12
+
+/* coef indices */
+#define IDX_SPDIF_STAT 0x0000
+#define IDX_SPDIF_CTL 0x0001
+#define IDX_ADC_CFG 0x0002
+/* SZC bitmask, 4 modes below:
+ * 0 = immediate,
+ * 1 = digital immediate, analog zero-cross
+ * 2 = digtail & analog soft-ramp
+ * 3 = digital soft-ramp, analog zero-cross
+ */
+#define CS_COEF_ADC_SZC_MASK (3 << 0)
+#define CS_COEF_ADC_MIC_SZC_MODE (3 << 0) /* SZC setup for mic */
+#define CS_COEF_ADC_LI_SZC_MODE (3 << 0) /* SZC setup for line-in */
+/* PGA mode: 0 = differential, 1 = signle-ended */
+#define CS_COEF_ADC_MIC_PGA_MODE (1 << 5) /* PGA setup for mic */
+#define CS_COEF_ADC_LI_PGA_MODE (1 << 6) /* PGA setup for line-in */
+#define IDX_DAC_CFG 0x0003
+/* SZC bitmask, 4 modes below:
+ * 0 = Immediate
+ * 1 = zero-cross
+ * 2 = soft-ramp
+ * 3 = soft-ramp on zero-cross
+ */
+#define CS_COEF_DAC_HP_SZC_MODE (3 << 0) /* nid 0x02 */
+#define CS_COEF_DAC_LO_SZC_MODE (3 << 2) /* nid 0x03 */
+#define CS_COEF_DAC_SPK_SZC_MODE (3 << 4) /* nid 0x04 */
+
+#define IDX_BEEP_CFG 0x0004
+/* 0x0008 - test reg key */
+/* 0x0009 - 0x0014 -> 12 test regs */
+/* 0x0015 - visibility reg */
+
+/*
+ * Cirrus Logic CS4210
+ *
+ * 1 DAC => HP(sense) / Speakers,
+ * 1 ADC <= LineIn(sense) / MicIn / DMicIn,
+ * 1 SPDIF OUT => SPDIF Transmitter(sense)
+ */
+#define CS4210_DAC_NID 0x02
+#define CS4210_ADC_NID 0x03
+#define CS4210_VENDOR_NID 0x0B
+#define CS421X_DMIC_PIN_NID 0x09 /* Port E */
+#define CS421X_SPDIF_PIN_NID 0x0A /* Port H */
+
+#define CS421X_IDX_DEV_CFG 0x01
+#define CS421X_IDX_ADC_CFG 0x02
+#define CS421X_IDX_DAC_CFG 0x03
+#define CS421X_IDX_SPK_CTL 0x04
+
+/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */
+#define CS4213_VENDOR_NID 0x09
+
+
+static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
+{
+ struct cs_spec *spec = codec->spec;
+
+ snd_hda_codec_write(codec, spec->vendor_nid, 0,
+ AC_VERB_SET_COEF_INDEX, idx);
+ return snd_hda_codec_read(codec, spec->vendor_nid, 0,
+ AC_VERB_GET_PROC_COEF, 0);
+}
+
+static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
+ unsigned int coef)
+{
+ struct cs_spec *spec = codec->spec;
+
+ snd_hda_codec_write(codec, spec->vendor_nid, 0,
+ AC_VERB_SET_COEF_INDEX, idx);
+ snd_hda_codec_write(codec, spec->vendor_nid, 0,
+ AC_VERB_SET_PROC_COEF, coef);
+}
+
+/*
+ * auto-mute and auto-mic switching
+ * CS421x auto-output redirecting
+ * HP/SPK/SPDIF
+ */
+
+static void cs_automute(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+
+ /* mute HPs if spdif jack (SENSE_B) is present */
+ spec->gen.master_mute = !!(spec->spdif_present && spec->sense_b);
+
+ snd_hda_gen_update_outputs(codec);
+
+ if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) {
+ if (spec->gen.automute_speaker)
+ spec->gpio_data = spec->gen.hp_jack_present ?
+ spec->gpio_eapd_hp : spec->gpio_eapd_speaker;
+ else
+ spec->gpio_data =
+ spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
+ snd_hda_codec_write(codec, 0x01, 0,
+ AC_VERB_SET_GPIO_DATA, spec->gpio_data);
+ }
+}
+
+static bool is_active_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int val;
+
+ val = snd_hda_codec_get_pincfg(codec, nid);
+ return (get_defcfg_connect(val) != AC_JACK_PORT_NONE);
+}
+
+static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid)
+{
+ struct cs_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return NULL;
+ codec->spec = spec;
+ spec->vendor_nid = vendor_nid;
+ codec->power_save_node = 1;
+ snd_hda_gen_spec_init(&spec->gen);
+
+ return spec;
+}
+
+/*
+ * Cirrus Logic CS4210
+ *
+ * 1 DAC => HP(sense) / Speakers,
+ * 1 ADC <= LineIn(sense) / MicIn / DMicIn,
+ * 1 SPDIF OUT => SPDIF Transmitter(sense)
+ */
+
+/* CS4210 board names */
+static const struct hda_model_fixup cs421x_models[] = {
+ { .id = CS421X_CDB4210, .name = "cdb4210" },
+ { .id = CS421X_STUMPY, .name = "stumpy" },
+ {}
+};
+
+static const struct hda_quirk cs421x_fixup_tbl[] = {
+ /* Test Intel board + CDB2410 */
+ SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210),
+ {} /* terminator */
+};
+
+/* CS4210 board pinconfigs */
+/* Default CS4210 (CDB4210)*/
+static const struct hda_pintbl cdb4210_pincfgs[] = {
+ { 0x05, 0x0321401f },
+ { 0x06, 0x90170010 },
+ { 0x07, 0x03813031 },
+ { 0x08, 0xb7a70037 },
+ { 0x09, 0xb7a6003e },
+ { 0x0a, 0x034510f0 },
+ {} /* terminator */
+};
+
+/* Stumpy ChromeBox */
+static const struct hda_pintbl stumpy_pincfgs[] = {
+ { 0x05, 0x022120f0 },
+ { 0x06, 0x901700f0 },
+ { 0x07, 0x02a120f0 },
+ { 0x08, 0x77a70037 },
+ { 0x09, 0x77a6003e },
+ { 0x0a, 0x434510f0 },
+ {} /* terminator */
+};
+
+/* Setup GPIO/SENSE for each board (if used) */
+static void cs421x_fixup_sense_b(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct cs_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->sense_b = 1;
+}
+
+static const struct hda_fixup cs421x_fixups[] = {
+ [CS421X_CDB4210] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cdb4210_pincfgs,
+ .chained = true,
+ .chain_id = CS421X_SENSE_B,
+ },
+ [CS421X_SENSE_B] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs421x_fixup_sense_b,
+ },
+ [CS421X_STUMPY] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stumpy_pincfgs,
+ },
+};
+
+static const struct hda_verb cs421x_coef_init_verbs[] = {
+ {0x0B, AC_VERB_SET_PROC_STATE, 1},
+ {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DEV_CFG},
+ /*
+ * Disable Coefficient Index Auto-Increment(DAI)=1,
+ * PDREF=0
+ */
+ {0x0B, AC_VERB_SET_PROC_COEF, 0x0001 },
+
+ {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_ADC_CFG},
+ /* ADC SZCMode = Digital Soft Ramp */
+ {0x0B, AC_VERB_SET_PROC_COEF, 0x0002 },
+
+ {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DAC_CFG},
+ {0x0B, AC_VERB_SET_PROC_COEF,
+ (0x0002 /* DAC SZCMode = Digital Soft Ramp */
+ | 0x0004 /* Mute DAC on FIFO error */
+ | 0x0008 /* Enable DAC High Pass Filter */
+ )},
+ {} /* terminator */
+};
+
+/* Errata: CS4210 rev A1 Silicon
+ *
+ * http://www.cirrus.com/en/pubs/errata/
+ *
+ * Description:
+ * 1. Performance degredation is present in the ADC.
+ * 2. Speaker output is not completely muted upon HP detect.
+ * 3. Noise is present when clipping occurs on the amplified
+ * speaker outputs.
+ *
+ * Workaround:
+ * The following verb sequence written to the registers during
+ * initialization will correct the issues listed above.
+ */
+
+static const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes[] = {
+ {0x0B, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */
+
+ {0x0B, AC_VERB_SET_COEF_INDEX, 0x0006},
+ {0x0B, AC_VERB_SET_PROC_COEF, 0x9999}, /* Test mode: on */
+
+ {0x0B, AC_VERB_SET_COEF_INDEX, 0x000A},
+ {0x0B, AC_VERB_SET_PROC_COEF, 0x14CB}, /* Chop double */
+
+ {0x0B, AC_VERB_SET_COEF_INDEX, 0x0011},
+ {0x0B, AC_VERB_SET_PROC_COEF, 0xA2D0}, /* Increase ADC current */
+
+ {0x0B, AC_VERB_SET_COEF_INDEX, 0x001A},
+ {0x0B, AC_VERB_SET_PROC_COEF, 0x02A9}, /* Mute speaker */
+
+ {0x0B, AC_VERB_SET_COEF_INDEX, 0x001B},
+ {0x0B, AC_VERB_SET_PROC_COEF, 0X1006}, /* Remove noise */
+
+ {} /* terminator */
+};
+
+/* Speaker Amp Gain is controlled by the vendor widget's coef 4 */
+static const DECLARE_TLV_DB_SCALE(cs421x_speaker_boost_db_scale, 900, 300, 0);
+
+static int cs421x_boost_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 3;
+ return 0;
+}
+
+static int cs421x_boost_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] =
+ cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL) & 0x0003;
+ return 0;
+}
+
+static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ unsigned int vol = ucontrol->value.integer.value[0];
+ unsigned int coef =
+ cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL);
+ unsigned int original_coef = coef;
+
+ coef &= ~0x0003;
+ coef |= (vol & 0x0003);
+ if (original_coef != coef) {
+ cs_vendor_coef_set(codec, CS421X_IDX_SPK_CTL, coef);
+ return 1;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new cs421x_speaker_boost_ctl = {
+
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .name = "Speaker Boost Playback Volume",
+ .info = cs421x_boost_vol_info,
+ .get = cs421x_boost_vol_get,
+ .put = cs421x_boost_vol_put,
+ .tlv = { .p = cs421x_speaker_boost_db_scale },
+};
+
+static void cs4210_pinmux_init(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ unsigned int def_conf, coef;
+
+ /* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */
+ coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG);
+
+ if (spec->gpio_mask)
+ coef |= 0x0008; /* B1,B2 are GPIOs */
+ else
+ coef &= ~0x0008;
+
+ if (spec->sense_b)
+ coef |= 0x0010; /* B2 is SENSE_B, not inverted */
+ else
+ coef &= ~0x0010;
+
+ cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef);
+
+ if ((spec->gpio_mask || spec->sense_b) &&
+ is_active_pin(codec, CS421X_DMIC_PIN_NID)) {
+
+ /*
+ * GPIO or SENSE_B forced - disconnect the DMIC pin.
+ */
+ def_conf = snd_hda_codec_get_pincfg(codec, CS421X_DMIC_PIN_NID);
+ def_conf &= ~AC_DEFCFG_PORT_CONN;
+ def_conf |= (AC_JACK_PORT_NONE << AC_DEFCFG_PORT_CONN_SHIFT);
+ snd_hda_codec_set_pincfg(codec, CS421X_DMIC_PIN_NID, def_conf);
+ }
+}
+
+static void cs4210_spdif_automute(struct hda_codec *codec,
+ struct hda_jack_callback *tbl)
+{
+ struct cs_spec *spec = codec->spec;
+ bool spdif_present = false;
+ hda_nid_t spdif_pin = spec->gen.autocfg.dig_out_pins[0];
+
+ /* detect on spdif is specific to CS4210 */
+ if (!spec->spdif_detect ||
+ spec->vendor_nid != CS4210_VENDOR_NID)
+ return;
+
+ spdif_present = snd_hda_jack_detect(codec, spdif_pin);
+ if (spdif_present == spec->spdif_present)
+ return;
+
+ spec->spdif_present = spdif_present;
+ /* SPDIF TX on/off */
+ snd_hda_set_pin_ctl(codec, spdif_pin, spdif_present ? PIN_OUT : 0);
+
+ cs_automute(codec);
+}
+
+static void parse_cs421x_digital(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ int i;
+
+ for (i = 0; i < cfg->dig_outs; i++) {
+ hda_nid_t nid = cfg->dig_out_pins[i];
+
+ if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
+ spec->spdif_detect = 1;
+ snd_hda_jack_detect_enable_callback(codec, nid,
+ cs4210_spdif_automute);
+ }
+ }
+}
+
+static int cs421x_init(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+
+ if (spec->vendor_nid == CS4210_VENDOR_NID) {
+ snd_hda_sequence_write(codec, cs421x_coef_init_verbs);
+ snd_hda_sequence_write(codec, cs421x_coef_init_verbs_A1_silicon_fixes);
+ cs4210_pinmux_init(codec);
+ }
+
+ snd_hda_gen_init(codec);
+
+ if (spec->gpio_mask) {
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
+ spec->gpio_mask);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
+ spec->gpio_dir);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_data);
+ }
+
+ cs4210_spdif_automute(codec, NULL);
+
+ return 0;
+}
+
+static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
+{
+ unsigned int caps;
+
+ /* set the upper-limit for mixer amp to 0dB */
+ caps = query_amp_caps(codec, dac, HDA_OUTPUT);
+ caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT);
+ caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f)
+ << AC_AMPCAP_NUM_STEPS_SHIFT;
+ snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps);
+}
+
+static int cs421x_parse_auto_config(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ hda_nid_t dac = CS4210_DAC_NID;
+ int err;
+
+ fix_volume_caps(codec, dac);
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
+ if (err < 0)
+ return err;
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
+ if (err < 0)
+ return err;
+
+ parse_cs421x_digital(codec);
+
+ if (spec->gen.autocfg.speaker_outs &&
+ spec->vendor_nid == CS4210_VENDOR_NID) {
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL,
+ &cs421x_speaker_boost_ctl))
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/*
+ * Manage PDREF, when transitioning to D3hot
+ * (DAC,ADC) -> D3, PDREF=1, AFG->D3
+ */
+static int cs421x_suspend(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ unsigned int coef;
+
+ snd_hda_shutup_pins(codec);
+
+ snd_hda_codec_write(codec, CS4210_DAC_NID, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ snd_hda_codec_write(codec, CS4210_ADC_NID, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+
+ if (spec->vendor_nid == CS4210_VENDOR_NID) {
+ coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG);
+ coef |= 0x0004; /* PDREF */
+ cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef);
+ }
+
+ return 0;
+}
+
+static int cs421x_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct cs_spec *spec;
+ int err;
+
+ spec = cs_alloc_spec(codec, id->driver_data);
+ if (!spec)
+ return -ENOMEM;
+
+ spec->gen.automute_hook = cs_automute;
+
+ if (spec->vendor_nid == CS4210_VENDOR_NID) {
+ snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl,
+ cs421x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ /*
+ * Update the GPIO/DMIC/SENSE_B pinmux before the configuration
+ * is auto-parsed. If GPIO or SENSE_B is forced, DMIC input
+ * is disabled.
+ */
+ cs4210_pinmux_init(codec);
+ }
+
+ err = cs421x_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops cs421x_codec_ops = {
+ .probe = cs421x_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cs421x_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = cs421x_suspend,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_cs421x[] = {
+ HDA_CODEC_ID_MODEL(0x10134210, "CS4210", CS4210_VENDOR_NID),
+ HDA_CODEC_ID_MODEL(0x10134213, "CS4213", CS4213_VENDOR_NID),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cs421x);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cirrus Logic CS421x HD-audio codec");
+
+static struct hda_codec_driver cs421x_driver = {
+ .id = snd_hda_id_cs421x,
+ .ops = &cs421x_codec_ops,
+};
+
+module_hda_codec_driver(cs421x_driver);
diff --git a/sound/hda/codecs/cirrus/cs8409-tables.c b/sound/hda/codecs/cirrus/cs8409-tables.c
new file mode 100644
index 000000000000..8c703b714a71
--- /dev/null
+++ b/sound/hda/codecs/cirrus/cs8409-tables.c
@@ -0,0 +1,623 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs8409-tables.c -- HD audio codec driver for Cirrus Logic CS8409 HDA bridge chip
+ *
+ * Copyright (C) 2021 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Lucas Tanure <tanureal@opensource.cirrus.com>
+ */
+
+#include "cs8409.h"
+
+/******************************************************************************
+ * CS42L42 Specific Data
+ *
+ ******************************************************************************/
+
+static const DECLARE_TLV_DB_SCALE(cs42l42_dac_db_scale, CS42L42_HP_VOL_REAL_MIN * 100, 100, 1);
+
+static const DECLARE_TLV_DB_SCALE(cs42l42_adc_db_scale, CS42L42_AMIC_VOL_REAL_MIN * 100, 100, 1);
+
+const struct snd_kcontrol_new cs42l42_dac_volume_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .index = 0,
+ .subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG),
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .info = cs42l42_volume_info,
+ .get = cs42l42_volume_get,
+ .put = cs42l42_volume_put,
+ .tlv = { .p = cs42l42_dac_db_scale },
+ .private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_TRANSMITTER_A, 3, CS8409_CODEC0,
+ HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE
+};
+
+const struct snd_kcontrol_new cs42l42_adc_volume_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .index = 0,
+ .subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG),
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .info = cs42l42_volume_info,
+ .get = cs42l42_volume_get,
+ .put = cs42l42_volume_put,
+ .tlv = { .p = cs42l42_adc_db_scale },
+ .private_value = HDA_COMPOSE_AMP_VAL_OFS(CS8409_PIN_ASP1_RECEIVER_A, 1, CS8409_CODEC0,
+ HDA_INPUT, CS42L42_VOL_ADC) | HDA_AMP_VAL_MIN_MUTE
+};
+
+const struct hda_pcm_stream cs42l42_48k_pcm_analog_playback = {
+ .rates = SNDRV_PCM_RATE_48000, /* fixed rate */
+};
+
+const struct hda_pcm_stream cs42l42_48k_pcm_analog_capture = {
+ .rates = SNDRV_PCM_RATE_48000, /* fixed rate */
+};
+
+/******************************************************************************
+ * BULLSEYE / WARLOCK / CYBORG Specific Arrays
+ * CS8409/CS42L42
+ ******************************************************************************/
+
+const struct hda_verb cs8409_cs42l42_init_verbs[] = {
+ { CS8409_PIN_AFG, AC_VERB_SET_GPIO_WAKE_MASK, 0x0018 }, /* WAKE from GPIO 3,4 */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x0002 }, /* Configure GPIO 6,7 */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0080 }, /* I2C mode */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x005b }, /* Set I2C bus speed */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0200 }, /* 100kHz I2C_STO = 2 */
+ {} /* terminator */
+};
+
+static const struct hda_pintbl cs8409_cs42l42_pincfgs[] = {
+ { CS8409_PIN_ASP1_TRANSMITTER_A, 0x042120f0 }, /* ASP-1-TX */
+ { CS8409_PIN_ASP1_RECEIVER_A, 0x04a12050 }, /* ASP-1-RX */
+ { CS8409_PIN_ASP2_TRANSMITTER_A, 0x901000f0 }, /* ASP-2-TX */
+ { CS8409_PIN_DMIC1_IN, 0x90a00090 }, /* DMIC-1 */
+ {} /* terminator */
+};
+
+static const struct hda_pintbl cs8409_cs42l42_pincfgs_no_dmic[] = {
+ { CS8409_PIN_ASP1_TRANSMITTER_A, 0x042120f0 }, /* ASP-1-TX */
+ { CS8409_PIN_ASP1_RECEIVER_A, 0x04a12050 }, /* ASP-1-RX */
+ { CS8409_PIN_ASP2_TRANSMITTER_A, 0x901000f0 }, /* ASP-2-TX */
+ {} /* terminator */
+};
+
+/* Vendor specific HW configuration for CS42L42 */
+static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = {
+ { CS42L42_I2C_TIMEOUT, 0xB0 },
+ { CS42L42_ADC_CTL, 0x00 },
+ { 0x1D02, 0x06 },
+ { CS42L42_ADC_VOLUME, 0x9F },
+ { CS42L42_OSC_SWITCH, 0x01 },
+ { CS42L42_MCLK_CTL, 0x02 },
+ { CS42L42_SRC_CTL, 0x03 },
+ { CS42L42_MCLK_SRC_SEL, 0x00 },
+ { CS42L42_ASP_FRM_CFG, 0x13 },
+ { CS42L42_FSYNC_P_LOWER, 0xFF },
+ { CS42L42_FSYNC_P_UPPER, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x20 },
+ { CS42L42_SPDIF_CLK_CFG, 0x0D },
+ { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x20 },
+ { CS42L42_ASP_RX_DAI0_CH3_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH3_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH3_BIT_LSB, 0x80 },
+ { CS42L42_ASP_RX_DAI0_CH4_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH4_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH4_BIT_LSB, 0xA0 },
+ { CS42L42_ASP_RX_DAI0_EN, 0x0C },
+ { CS42L42_ASP_TX_CH_EN, 0x01 },
+ { CS42L42_ASP_TX_CH_AP_RES, 0x02 },
+ { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_TX_SZ_EN, 0x01 },
+ { CS42L42_PWR_CTL1, 0x0A },
+ { CS42L42_PWR_CTL2, 0x84 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3f },
+ { CS42L42_HP_CTL, 0x0D },
+ { CS42L42_MIC_DET_CTL1, 0xB6 },
+ { CS42L42_TIPSENSE_CTL, 0xC2 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x01 },
+ { CS42L42_HS_SWITCH_CTL, 0xF3 },
+ { CS42L42_PWR_CTL3, 0x20 },
+ { CS42L42_RSENSE_CTL2, 0x00 },
+ { CS42L42_RSENSE_CTL3, 0x00 },
+ { CS42L42_TSENSE_CTL, 0x80 },
+ { CS42L42_HS_BIAS_CTL, 0xC0 },
+ { CS42L42_PWR_CTL1, 0x02, 10000 },
+ { CS42L42_ADC_OVFL_INT_MASK, 0xff },
+ { CS42L42_MIXER_INT_MASK, 0xff },
+ { CS42L42_SRC_INT_MASK, 0xff },
+ { CS42L42_ASP_RX_INT_MASK, 0xff },
+ { CS42L42_ASP_TX_INT_MASK, 0xff },
+ { CS42L42_CODEC_INT_MASK, 0xff },
+ { CS42L42_SRCPL_INT_MASK, 0xff },
+ { CS42L42_VPMON_INT_MASK, 0xff },
+ { CS42L42_PLL_LOCK_INT_MASK, 0xff },
+ { CS42L42_TSRS_PLUG_INT_MASK, 0xff },
+ { CS42L42_DET_INT1_MASK, 0xff },
+ { CS42L42_DET_INT2_MASK, 0xff },
+};
+
+/* Vendor specific hw configuration for CS8409 */
+const struct cs8409_cir_param cs8409_cs42l42_hw_cfg[] = {
+ /* +PLL1/2_EN, +I2C_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0xb008 },
+ /* ASP1/2_EN=0, ASP1_STP=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0002 },
+ /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG3, 0x0a80 },
+ /* ASP1.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL1, 0x0800 },
+ /* ASP1.A: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=32 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL2, 0x0820 },
+ /* ASP2.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL1, 0x0800 },
+ /* ASP2.A: TX.RAP=1, TX.RSZ=24 bits, TX.RCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP2_A_TX_CTRL2, 0x2800 },
+ /* ASP1.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL1, 0x0800 },
+ /* ASP1.A: RX.RAP=0, RX.RSZ=24 bits, RX.RCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL2, 0x0800 },
+ /* ASP1: LCHI = 00h */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL1, 0x8000 },
+ /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL2, 0x28ff },
+ /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL3, 0x0062 },
+ /* ASP2: LCHI=1Fh */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL1, 0x801f },
+ /* ASP2: MC/SC_SRCSEL=PLL1, LCPR=3Fh */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL2, 0x283f },
+ /* ASP2: 5050=1, MCEN=0, FSD=010, SCPOL_IN/OUT=1, SCDIV=1:16 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP2_CLK_CTRL3, 0x805c },
+ /* DMIC1_MO=10b, DMIC1/2_SR=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DMIC_CFG, 0x0023 },
+ /* ASP1/2_BEEP=0 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_BEEP_CFG, 0x0000 },
+ /* ASP1/2_EN=1, ASP1_STP=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0062 },
+ /* -PLL2_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0x9008 },
+ /* TX2.A: pre-scale att.=0 dB */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PRE_SCALE_ATTN2, 0x0000 },
+ /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PAD_CFG_SLW_RATE_CTRL, 0xfc03 },
+ /* test mode on */
+ { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x9999 },
+ /* GPIO hysteresis = 30 us */
+ { CS8409_PIN_VENDOR_WIDGET, 0xc5, 0x0000 },
+ /* test mode off */
+ { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x0000 },
+ {} /* Terminator */
+};
+
+const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[] = {
+ /* EQ_SEL=1, EQ1/2_EN=0 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_CTRL1, 0x4000 },
+ /* +EQ_ACC */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0x4000 },
+ /* +EQ2_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_CTRL1, 0x4010 },
+ /* EQ_DATA_HI=0x0647 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x0647 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=0, EQ_DATA_LO=0x67 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc0c7 },
+ /* EQ_DATA_HI=0x0647 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x0647 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=1, EQ_DATA_LO=0x67 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc1c7 },
+ /* EQ_DATA_HI=0xf370 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xf370 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=2, EQ_DATA_LO=0x71 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc271 },
+ /* EQ_DATA_HI=0x1ef8 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1ef8 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=3, EQ_DATA_LO=0x48 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc348 },
+ /* EQ_DATA_HI=0xc110 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xc110 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=4, EQ_DATA_LO=0x5a */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc45a },
+ /* EQ_DATA_HI=0x1f29 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1f29 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=5, EQ_DATA_LO=0x74 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc574 },
+ /* EQ_DATA_HI=0x1d7a */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1d7a },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=6, EQ_DATA_LO=0x53 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc653 },
+ /* EQ_DATA_HI=0xc38c */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xc38c },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=7, EQ_DATA_LO=0x14 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc714 },
+ /* EQ_DATA_HI=0x1ca3 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0x1ca3 },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=8, EQ_DATA_LO=0xc7 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc8c7 },
+ /* EQ_DATA_HI=0xc38c */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W1, 0xc38c },
+ /* +EQ_WRT, +EQ_ACC, EQ_ADR=9, EQ_DATA_LO=0x14 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0xc914 },
+ /* -EQ_ACC, -EQ_WRT */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PFE_COEF_W2, 0x0000 },
+ {} /* Terminator */
+};
+
+struct sub_codec cs8409_cs42l42_codec = {
+ .addr = CS42L42_I2C_ADDR,
+ .reset_gpio = CS8409_CS42L42_RESET,
+ .irq_mask = CS8409_CS42L42_INT,
+ .init_seq = cs42l42_init_reg_seq,
+ .init_seq_num = ARRAY_SIZE(cs42l42_init_reg_seq),
+ .hp_jack_in = 0,
+ .mic_jack_in = 0,
+ .paged = 1,
+ .suspended = 1,
+ .no_type_dect = 0,
+};
+
+/******************************************************************************
+ * Dolphin Specific Arrays
+ * CS8409/ 2 X CS42L42
+ ******************************************************************************/
+
+const struct hda_verb dolphin_init_verbs[] = {
+ { 0x01, AC_VERB_SET_GPIO_WAKE_MASK, DOLPHIN_WAKE }, /* WAKE from GPIO 0,4 */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_STATE, 0x0001 }, /* Enable VPW processing */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x0002 }, /* Configure GPIO 6,7 */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0080 }, /* I2C mode */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_COEF_INDEX, 0x005b }, /* Set I2C bus speed */
+ { CS8409_PIN_VENDOR_WIDGET, AC_VERB_SET_PROC_COEF, 0x0200 }, /* 100kHz I2C_STO = 2 */
+ {} /* terminator */
+};
+
+static const struct hda_pintbl dolphin_pincfgs[] = {
+ { 0x24, 0x022210f0 }, /* ASP-1-TX-A */
+ { 0x25, 0x010240f0 }, /* ASP-1-TX-B */
+ { 0x34, 0x02a21050 }, /* ASP-1-RX */
+ {} /* terminator */
+};
+
+/* Vendor specific HW configuration for CS42L42 */
+static const struct cs8409_i2c_param dolphin_c0_init_reg_seq[] = {
+ { CS42L42_I2C_TIMEOUT, 0xB0 },
+ { CS42L42_ADC_CTL, 0x00 },
+ { 0x1D02, 0x06 },
+ { CS42L42_ADC_VOLUME, 0x9F },
+ { CS42L42_OSC_SWITCH, 0x01 },
+ { CS42L42_MCLK_CTL, 0x02 },
+ { CS42L42_SRC_CTL, 0x03 },
+ { CS42L42_MCLK_SRC_SEL, 0x00 },
+ { CS42L42_ASP_FRM_CFG, 0x13 },
+ { CS42L42_FSYNC_P_LOWER, 0xFF },
+ { CS42L42_FSYNC_P_UPPER, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x20 },
+ { CS42L42_SPDIF_CLK_CFG, 0x0D },
+ { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x20 },
+ { CS42L42_ASP_RX_DAI0_EN, 0x0C },
+ { CS42L42_ASP_TX_CH_EN, 0x01 },
+ { CS42L42_ASP_TX_CH_AP_RES, 0x02 },
+ { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_TX_SZ_EN, 0x01 },
+ { CS42L42_PWR_CTL1, 0x0A },
+ { CS42L42_PWR_CTL2, 0x84 },
+ { CS42L42_HP_CTL, 0x0D },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3f },
+ { CS42L42_MIC_DET_CTL1, 0xB6 },
+ { CS42L42_TIPSENSE_CTL, 0xC2 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x01 },
+ { CS42L42_HS_SWITCH_CTL, 0xF3 },
+ { CS42L42_PWR_CTL3, 0x20 },
+ { CS42L42_RSENSE_CTL2, 0x00 },
+ { CS42L42_RSENSE_CTL3, 0x00 },
+ { CS42L42_TSENSE_CTL, 0x80 },
+ { CS42L42_HS_BIAS_CTL, 0xC0 },
+ { CS42L42_PWR_CTL1, 0x02, 10000 },
+ { CS42L42_ADC_OVFL_INT_MASK, 0xff },
+ { CS42L42_MIXER_INT_MASK, 0xff },
+ { CS42L42_SRC_INT_MASK, 0xff },
+ { CS42L42_ASP_RX_INT_MASK, 0xff },
+ { CS42L42_ASP_TX_INT_MASK, 0xff },
+ { CS42L42_CODEC_INT_MASK, 0xff },
+ { CS42L42_SRCPL_INT_MASK, 0xff },
+ { CS42L42_VPMON_INT_MASK, 0xff },
+ { CS42L42_PLL_LOCK_INT_MASK, 0xff },
+ { CS42L42_TSRS_PLUG_INT_MASK, 0xff },
+ { CS42L42_DET_INT1_MASK, 0xff },
+ { CS42L42_DET_INT2_MASK, 0xff }
+};
+
+static const struct cs8409_i2c_param dolphin_c1_init_reg_seq[] = {
+ { CS42L42_I2C_TIMEOUT, 0xB0 },
+ { CS42L42_ADC_CTL, 0x00 },
+ { 0x1D02, 0x06 },
+ { CS42L42_ADC_VOLUME, 0x9F },
+ { CS42L42_OSC_SWITCH, 0x01 },
+ { CS42L42_MCLK_CTL, 0x02 },
+ { CS42L42_SRC_CTL, 0x03 },
+ { CS42L42_MCLK_SRC_SEL, 0x00 },
+ { CS42L42_ASP_FRM_CFG, 0x13 },
+ { CS42L42_FSYNC_P_LOWER, 0xFF },
+ { CS42L42_FSYNC_P_UPPER, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x20 },
+ { CS42L42_SPDIF_CLK_CFG, 0x0D },
+ { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x80 },
+ { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x02 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0xA0 },
+ { CS42L42_ASP_RX_DAI0_EN, 0x0C },
+ { CS42L42_ASP_TX_CH_EN, 0x00 },
+ { CS42L42_ASP_TX_CH_AP_RES, 0x02 },
+ { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_TX_SZ_EN, 0x00 },
+ { CS42L42_PWR_CTL1, 0x0E },
+ { CS42L42_PWR_CTL2, 0x84 },
+ { CS42L42_HP_CTL, 0x0D },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3f },
+ { CS42L42_MIC_DET_CTL1, 0xB6 },
+ { CS42L42_TIPSENSE_CTL, 0xC2 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x01 },
+ { CS42L42_HS_SWITCH_CTL, 0xF3 },
+ { CS42L42_PWR_CTL3, 0x20 },
+ { CS42L42_RSENSE_CTL2, 0x00 },
+ { CS42L42_RSENSE_CTL3, 0x00 },
+ { CS42L42_TSENSE_CTL, 0x80 },
+ { CS42L42_HS_BIAS_CTL, 0xC0 },
+ { CS42L42_PWR_CTL1, 0x06, 10000 },
+ { CS42L42_ADC_OVFL_INT_MASK, 0xff },
+ { CS42L42_MIXER_INT_MASK, 0xff },
+ { CS42L42_SRC_INT_MASK, 0xff },
+ { CS42L42_ASP_RX_INT_MASK, 0xff },
+ { CS42L42_ASP_TX_INT_MASK, 0xff },
+ { CS42L42_CODEC_INT_MASK, 0xff },
+ { CS42L42_SRCPL_INT_MASK, 0xff },
+ { CS42L42_VPMON_INT_MASK, 0xff },
+ { CS42L42_PLL_LOCK_INT_MASK, 0xff },
+ { CS42L42_TSRS_PLUG_INT_MASK, 0xff },
+ { CS42L42_DET_INT1_MASK, 0xff },
+ { CS42L42_DET_INT2_MASK, 0xff }
+};
+
+/* Vendor specific hw configuration for CS8409 */
+const struct cs8409_cir_param dolphin_hw_cfg[] = {
+ /* +PLL1/2_EN, +I2C_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0xb008 },
+ /* ASP1_EN=0, ASP1_STP=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0002 },
+ /* ASP1/2_BUS_IDLE=10, +GPIO_I2C */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG3, 0x0a80 },
+ /* ASP1.A: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL1, 0x0800 },
+ /* ASP1.A: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=32 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_TX_CTRL2, 0x0820 },
+ /* ASP1.B: TX.LAP=0, TX.LSZ=24 bits, TX.LCS=128 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_B_TX_CTRL1, 0x0880 },
+ /* ASP1.B: TX.RAP=0, TX.RSZ=24 bits, TX.RCS=160 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_B_TX_CTRL2, 0x08a0 },
+ /* ASP1.A: RX.LAP=0, RX.LSZ=24 bits, RX.LCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL1, 0x0800 },
+ /* ASP1.A: RX.RAP=0, RX.RSZ=24 bits, RX.RCS=0 */
+ { CS8409_PIN_VENDOR_WIDGET, ASP1_A_RX_CTRL2, 0x0800 },
+ /* ASP1: LCHI = 00h */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL1, 0x8000 },
+ /* ASP1: MC/SC_SRCSEL=PLL1, LCPR=FFh */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL2, 0x28ff },
+ /* ASP1: MCEN=0, FSD=011, SCPOL_IN/OUT=0, SCDIV=1:4 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_ASP1_CLK_CTRL3, 0x0062 },
+ /* ASP1/2_BEEP=0 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_BEEP_CFG, 0x0000 },
+ /* ASP1_EN=1, ASP1_STP=1 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG2, 0x0022 },
+ /* -PLL2_EN */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_DEV_CFG1, 0x9008 },
+ /* ASP1_xxx_EN=1, ASP1_MCLK_EN=0 */
+ { CS8409_PIN_VENDOR_WIDGET, CS8409_PAD_CFG_SLW_RATE_CTRL, 0x5400 },
+ /* test mode on */
+ { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x9999 },
+ /* GPIO hysteresis = 30 us */
+ { CS8409_PIN_VENDOR_WIDGET, 0xc5, 0x0000 },
+ /* test mode off */
+ { CS8409_PIN_VENDOR_WIDGET, 0xc0, 0x0000 },
+ {} /* Terminator */
+};
+
+struct sub_codec dolphin_cs42l42_0 = {
+ .addr = DOLPHIN_C0_I2C_ADDR,
+ .reset_gpio = DOLPHIN_C0_RESET,
+ .irq_mask = DOLPHIN_C0_INT,
+ .init_seq = dolphin_c0_init_reg_seq,
+ .init_seq_num = ARRAY_SIZE(dolphin_c0_init_reg_seq),
+ .hp_jack_in = 0,
+ .mic_jack_in = 0,
+ .paged = 1,
+ .suspended = 1,
+ .no_type_dect = 0,
+};
+
+struct sub_codec dolphin_cs42l42_1 = {
+ .addr = DOLPHIN_C1_I2C_ADDR,
+ .reset_gpio = DOLPHIN_C1_RESET,
+ .irq_mask = DOLPHIN_C1_INT,
+ .init_seq = dolphin_c1_init_reg_seq,
+ .init_seq_num = ARRAY_SIZE(dolphin_c1_init_reg_seq),
+ .hp_jack_in = 0,
+ .mic_jack_in = 0,
+ .paged = 1,
+ .suspended = 1,
+ .no_type_dect = 1,
+};
+
+/******************************************************************************
+ * CS8409 Patch Driver Structs
+ * Arrays Used for all projects using CS8409
+ ******************************************************************************/
+
+const struct hda_quirk cs8409_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1028, 0x0A11, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A12, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A23, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A24, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A25, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A29, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A2A, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A2B, "Bullseye", CS8409_BULLSEYE),
+ SND_PCI_QUIRK(0x1028, 0x0A77, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A78, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A79, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7A, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7D, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7E, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A7F, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0A80, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AB0, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB2, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB1, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB3, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB4, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AB5, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ACF, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0AD0, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0AD1, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0AD2, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0AD3, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0AD9, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ADA, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ADB, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ADC, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0ADF, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE0, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE1, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE2, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AE9, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEA, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEB, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEC, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AED, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEE, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AEF, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AF0, "Cyborg", CS8409_CYBORG),
+ SND_PCI_QUIRK(0x1028, 0x0AF4, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0AF5, "Warlock", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0B92, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0B93, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0B94, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0B95, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0B96, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0B97, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0BA5, "Odin", CS8409_ODIN),
+ SND_PCI_QUIRK(0x1028, 0x0BA6, "Odin", CS8409_ODIN),
+ SND_PCI_QUIRK(0x1028, 0x0BA8, "Odin", CS8409_ODIN),
+ SND_PCI_QUIRK(0x1028, 0x0BAA, "Odin", CS8409_ODIN),
+ SND_PCI_QUIRK(0x1028, 0x0BAE, "Odin", CS8409_ODIN),
+ SND_PCI_QUIRK(0x1028, 0x0BB2, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0BB3, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0BB4, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0BB5, "Warlock N3 15 TGL-U Nuvoton EC", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0BB6, "Warlock V3 15 TGL-U Nuvoton EC", CS8409_WARLOCK),
+ SND_PCI_QUIRK(0x1028, 0x0BB8, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0BB9, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0BBA, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0BBB, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0BBC, "Warlock MLK", CS8409_WARLOCK_MLK),
+ SND_PCI_QUIRK(0x1028, 0x0BBD, "Warlock MLK Dual Mic", CS8409_WARLOCK_MLK_DUAL_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0BD4, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0BD5, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0BD6, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0BD7, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0BD8, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C43, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C50, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C51, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C52, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C73, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C75, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C7D, "Dolphin", CS8409_DOLPHIN),
+ SND_PCI_QUIRK(0x1028, 0x0C7F, "Dolphin", CS8409_DOLPHIN),
+ {} /* terminator */
+};
+
+/* Dell Inspiron models with cs8409/cs42l42 */
+const struct hda_model_fixup cs8409_models[] = {
+ { .id = CS8409_BULLSEYE, .name = "bullseye" },
+ { .id = CS8409_WARLOCK, .name = "warlock" },
+ { .id = CS8409_WARLOCK_MLK, .name = "warlock mlk" },
+ { .id = CS8409_WARLOCK_MLK_DUAL_MIC, .name = "warlock mlk dual mic" },
+ { .id = CS8409_CYBORG, .name = "cyborg" },
+ { .id = CS8409_DOLPHIN, .name = "dolphin" },
+ { .id = CS8409_ODIN, .name = "odin" },
+ {}
+};
+
+const struct hda_fixup cs8409_fixups[] = {
+ [CS8409_BULLSEYE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_WARLOCK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_WARLOCK_MLK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_WARLOCK_MLK_DUAL_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_CYBORG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+ [CS8409_FIXUPS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs8409_cs42l42_fixups,
+ },
+ [CS8409_DOLPHIN] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dolphin_pincfgs,
+ .chained = true,
+ .chain_id = CS8409_DOLPHIN_FIXUPS,
+ },
+ [CS8409_DOLPHIN_FIXUPS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = dolphin_fixups,
+ },
+ [CS8409_ODIN] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cs8409_cs42l42_pincfgs_no_dmic,
+ .chained = true,
+ .chain_id = CS8409_FIXUPS,
+ },
+};
diff --git a/sound/hda/codecs/cirrus/cs8409.c b/sound/hda/codecs/cirrus/cs8409.c
new file mode 100644
index 000000000000..2c02d3be89ee
--- /dev/null
+++ b/sound/hda/codecs/cirrus/cs8409.c
@@ -0,0 +1,1475 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HD audio codec driver for Cirrus Logic CS8409 HDA bridge chip
+ *
+ * Copyright (C) 2021 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <linux/mutex.h>
+#include <linux/iopoll.h>
+
+#include "cs8409.h"
+
+/******************************************************************************
+ * CS8409 Specific Functions
+ ******************************************************************************/
+
+static int cs8409_parse_auto_config(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec = codec->spec;
+ int err;
+ int i;
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
+ if (err < 0)
+ return err;
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
+ if (err < 0)
+ return err;
+
+ /* keep the ADCs powered up when it's dynamically switchable */
+ if (spec->gen.dyn_adc_switch) {
+ unsigned int done = 0;
+
+ for (i = 0; i < spec->gen.input_mux.num_items; i++) {
+ int idx = spec->gen.dyn_adc_idx[i];
+
+ if (done & (1 << idx))
+ continue;
+ snd_hda_gen_fix_pin_power(codec, spec->gen.adc_nids[idx]);
+ done |= 1 << idx;
+ }
+ }
+
+ return 0;
+}
+
+static void cs8409_disable_i2c_clock_worker(struct work_struct *work);
+
+static struct cs8409_spec *cs8409_alloc_spec(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return NULL;
+ codec->spec = spec;
+ spec->codec = codec;
+ codec->power_save_node = 1;
+ mutex_init(&spec->i2c_mux);
+ INIT_DELAYED_WORK(&spec->i2c_clk_work, cs8409_disable_i2c_clock_worker);
+ snd_hda_gen_spec_init(&spec->gen);
+
+ return spec;
+}
+
+static inline int cs8409_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
+{
+ snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_COEF_INDEX, idx);
+ return snd_hda_codec_read(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_GET_PROC_COEF, 0);
+}
+
+static inline void cs8409_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
+ unsigned int coef)
+{
+ snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_COEF_INDEX, idx);
+ snd_hda_codec_write(codec, CS8409_PIN_VENDOR_WIDGET, 0, AC_VERB_SET_PROC_COEF, coef);
+}
+
+/*
+ * cs8409_enable_i2c_clock - Disable I2C clocks
+ * @codec: the codec instance
+ * Disable I2C clocks.
+ * This must be called when the i2c mutex is unlocked.
+ */
+static void cs8409_disable_i2c_clock(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ guard(mutex)(&spec->i2c_mux);
+ if (spec->i2c_clck_enabled) {
+ cs8409_vendor_coef_set(spec->codec, 0x0,
+ cs8409_vendor_coef_get(spec->codec, 0x0) & 0xfffffff7);
+ spec->i2c_clck_enabled = 0;
+ }
+}
+
+/*
+ * cs8409_disable_i2c_clock_worker - Worker that disable the I2C Clock after 25ms without use
+ */
+static void cs8409_disable_i2c_clock_worker(struct work_struct *work)
+{
+ struct cs8409_spec *spec = container_of(work, struct cs8409_spec, i2c_clk_work.work);
+
+ cs8409_disable_i2c_clock(spec->codec);
+}
+
+/*
+ * cs8409_enable_i2c_clock - Enable I2C clocks
+ * @codec: the codec instance
+ * Enable I2C clocks.
+ * This must be called when the i2c mutex is locked.
+ */
+static void cs8409_enable_i2c_clock(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ /* Cancel the disable timer, but do not wait for any running disable functions to finish.
+ * If the disable timer runs out before cancel, the delayed work thread will be blocked,
+ * waiting for the mutex to become unlocked. This mutex will be locked for the duration of
+ * any i2c transaction, so the disable function will run to completion immediately
+ * afterwards in the scenario. The next enable call will re-enable the clock, regardless.
+ */
+ cancel_delayed_work(&spec->i2c_clk_work);
+
+ if (!spec->i2c_clck_enabled) {
+ cs8409_vendor_coef_set(codec, 0x0, cs8409_vendor_coef_get(codec, 0x0) | 0x8);
+ spec->i2c_clck_enabled = 1;
+ }
+ queue_delayed_work(system_power_efficient_wq, &spec->i2c_clk_work, msecs_to_jiffies(25));
+}
+
+/**
+ * cs8409_i2c_wait_complete - Wait for I2C transaction
+ * @codec: the codec instance
+ *
+ * Wait for I2C transaction to complete.
+ * Return -ETIMEDOUT if transaction wait times out.
+ */
+static int cs8409_i2c_wait_complete(struct hda_codec *codec)
+{
+ unsigned int retval;
+
+ return read_poll_timeout(cs8409_vendor_coef_get, retval, retval & 0x18,
+ CS42L42_I2C_SLEEP_US, CS42L42_I2C_TIMEOUT_US, false, codec, CS8409_I2C_STS);
+}
+
+/**
+ * cs8409_set_i2c_dev_addr - Set i2c address for transaction
+ * @codec: the codec instance
+ * @addr: I2C Address
+ */
+static void cs8409_set_i2c_dev_addr(struct hda_codec *codec, unsigned int addr)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ if (spec->dev_addr != addr) {
+ cs8409_vendor_coef_set(codec, CS8409_I2C_ADDR, addr);
+ spec->dev_addr = addr;
+ }
+}
+
+/**
+ * cs8409_i2c_set_page - CS8409 I2C set page register.
+ * @scodec: the codec instance
+ * @i2c_reg: Page register
+ *
+ * Returns negative on error.
+ */
+static int cs8409_i2c_set_page(struct sub_codec *scodec, unsigned int i2c_reg)
+{
+ struct hda_codec *codec = scodec->codec;
+
+ if (scodec->paged && (scodec->last_page != (i2c_reg >> 8))) {
+ cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg >> 8);
+ if (cs8409_i2c_wait_complete(codec) < 0)
+ return -EIO;
+ scodec->last_page = i2c_reg >> 8;
+ }
+
+ return 0;
+}
+
+/**
+ * cs8409_i2c_read - CS8409 I2C Read.
+ * @scodec: the codec instance
+ * @addr: Register to read
+ *
+ * Returns negative on error, otherwise returns read value in bits 0-7.
+ */
+static int cs8409_i2c_read(struct sub_codec *scodec, unsigned int addr)
+{
+ struct hda_codec *codec = scodec->codec;
+ struct cs8409_spec *spec = codec->spec;
+ unsigned int i2c_reg_data;
+ unsigned int read_data;
+
+ if (scodec->suspended)
+ return -EPERM;
+
+ guard(mutex)(&spec->i2c_mux);
+ cs8409_enable_i2c_clock(codec);
+ cs8409_set_i2c_dev_addr(codec, scodec->addr);
+
+ if (cs8409_i2c_set_page(scodec, addr))
+ goto error;
+
+ i2c_reg_data = (addr << 8) & 0x0ffff;
+ cs8409_vendor_coef_set(codec, CS8409_I2C_QREAD, i2c_reg_data);
+ if (cs8409_i2c_wait_complete(codec) < 0)
+ goto error;
+
+ /* Register in bits 15-8 and the data in 7-0 */
+ read_data = cs8409_vendor_coef_get(codec, CS8409_I2C_QREAD);
+
+ return read_data & 0x0ff;
+
+error:
+ codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, scodec->addr, addr);
+ return -EIO;
+}
+
+/**
+ * cs8409_i2c_bulk_read - CS8409 I2C Read Sequence.
+ * @scodec: the codec instance
+ * @seq: Register Sequence to read
+ * @count: Number of registeres to read
+ *
+ * Returns negative on error, values are read into value element of cs8409_i2c_param sequence.
+ */
+static int cs8409_i2c_bulk_read(struct sub_codec *scodec, struct cs8409_i2c_param *seq, int count)
+{
+ struct hda_codec *codec = scodec->codec;
+ struct cs8409_spec *spec = codec->spec;
+ unsigned int i2c_reg_data;
+ int i;
+
+ if (scodec->suspended)
+ return -EPERM;
+
+ guard(mutex)(&spec->i2c_mux);
+ cs8409_set_i2c_dev_addr(codec, scodec->addr);
+
+ for (i = 0; i < count; i++) {
+ cs8409_enable_i2c_clock(codec);
+ if (cs8409_i2c_set_page(scodec, seq[i].addr))
+ goto error;
+
+ i2c_reg_data = (seq[i].addr << 8) & 0x0ffff;
+ cs8409_vendor_coef_set(codec, CS8409_I2C_QREAD, i2c_reg_data);
+
+ if (cs8409_i2c_wait_complete(codec) < 0)
+ goto error;
+
+ seq[i].value = cs8409_vendor_coef_get(codec, CS8409_I2C_QREAD) & 0xff;
+ }
+
+ return 0;
+
+error:
+ codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", scodec->addr);
+ return -EIO;
+}
+
+/**
+ * cs8409_i2c_write - CS8409 I2C Write.
+ * @scodec: the codec instance
+ * @addr: Register to write to
+ * @value: Data to write
+ *
+ * Returns negative on error, otherwise returns 0.
+ */
+static int cs8409_i2c_write(struct sub_codec *scodec, unsigned int addr, unsigned int value)
+{
+ struct hda_codec *codec = scodec->codec;
+ struct cs8409_spec *spec = codec->spec;
+ unsigned int i2c_reg_data;
+
+ if (scodec->suspended)
+ return -EPERM;
+
+ guard(mutex)(&spec->i2c_mux);
+
+ cs8409_enable_i2c_clock(codec);
+ cs8409_set_i2c_dev_addr(codec, scodec->addr);
+
+ if (cs8409_i2c_set_page(scodec, addr))
+ goto error;
+
+ i2c_reg_data = ((addr << 8) & 0x0ff00) | (value & 0x0ff);
+ cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg_data);
+
+ if (cs8409_i2c_wait_complete(codec) < 0)
+ goto error;
+
+ return 0;
+
+error:
+ codec_err(codec, "%s() Failed 0x%02x : 0x%04x\n", __func__, scodec->addr, addr);
+ return -EIO;
+}
+
+/**
+ * cs8409_i2c_bulk_write - CS8409 I2C Write Sequence.
+ * @scodec: the codec instance
+ * @seq: Register Sequence to write
+ * @count: Number of registeres to write
+ *
+ * Returns negative on error.
+ */
+static int cs8409_i2c_bulk_write(struct sub_codec *scodec, const struct cs8409_i2c_param *seq,
+ int count)
+{
+ struct hda_codec *codec = scodec->codec;
+ struct cs8409_spec *spec = codec->spec;
+ unsigned int i2c_reg_data;
+ int i;
+
+ if (scodec->suspended)
+ return -EPERM;
+
+ guard(mutex)(&spec->i2c_mux);
+ cs8409_set_i2c_dev_addr(codec, scodec->addr);
+
+ for (i = 0; i < count; i++) {
+ cs8409_enable_i2c_clock(codec);
+ if (cs8409_i2c_set_page(scodec, seq[i].addr))
+ goto error;
+
+ i2c_reg_data = ((seq[i].addr << 8) & 0x0ff00) | (seq[i].value & 0x0ff);
+ cs8409_vendor_coef_set(codec, CS8409_I2C_QWRITE, i2c_reg_data);
+
+ if (cs8409_i2c_wait_complete(codec) < 0)
+ goto error;
+ /* Certain use cases may require a delay
+ * after a write operation before proceeding.
+ */
+ if (seq[i].delay)
+ fsleep(seq[i].delay);
+ }
+
+ return 0;
+
+error:
+ codec_err(codec, "I2C Bulk Write Failed 0x%02x\n", scodec->addr);
+ return -EIO;
+}
+
+static int cs8409_init(struct hda_codec *codec)
+{
+ int ret = snd_hda_gen_init(codec);
+
+ if (!ret)
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
+
+ return ret;
+}
+
+static int cs8409_build_controls(struct hda_codec *codec)
+{
+ int err;
+
+ err = snd_hda_gen_build_controls(codec);
+ if (err < 0)
+ return err;
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD);
+
+ return 0;
+}
+
+/* Enable/Disable Unsolicited Response */
+static void cs8409_enable_ur(struct hda_codec *codec, int flag)
+{
+ struct cs8409_spec *spec = codec->spec;
+ unsigned int ur_gpios = 0;
+ int i;
+
+ for (i = 0; i < spec->num_scodecs; i++)
+ ur_gpios |= spec->scodecs[i]->irq_mask;
+
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK,
+ flag ? ur_gpios : 0);
+
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_UNSOLICITED_ENABLE,
+ flag ? AC_UNSOL_ENABLED : 0);
+}
+
+static void cs8409_fix_caps(struct hda_codec *codec, unsigned int nid)
+{
+ int caps;
+
+ /* CS8409 is simple HDA bridge and intended to be used with a remote
+ * companion codec. Most of input/output PIN(s) have only basic
+ * capabilities. Receive and Transmit NID(s) have only OUTC and INC
+ * capabilities and no presence detect capable (PDC) and call to
+ * snd_hda_gen_build_controls() will mark them as non detectable
+ * phantom jacks. However, a companion codec may be
+ * connected to these pins which supports jack detect
+ * capabilities. We have to override pin capabilities,
+ * otherwise they will not be created as input devices.
+ */
+ caps = snd_hdac_read_parm(&codec->core, nid, AC_PAR_PIN_CAP);
+ if (caps >= 0)
+ snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP,
+ (caps | (AC_PINCAP_IMP_SENSE | AC_PINCAP_PRES_DETECT)));
+
+ snd_hda_override_wcaps(codec, nid, (get_wcaps(codec, nid) | AC_WCAP_UNSOL_CAP));
+}
+
+static int cs8409_spk_sw_gpio_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs8409_spec *spec = codec->spec;
+
+ ucontrol->value.integer.value[0] = !!(spec->gpio_data & spec->speaker_pdn_gpio);
+ return 0;
+}
+
+static int cs8409_spk_sw_gpio_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs8409_spec *spec = codec->spec;
+ unsigned int gpio_data;
+
+ gpio_data = (spec->gpio_data & ~spec->speaker_pdn_gpio) |
+ (ucontrol->value.integer.value[0] ? spec->speaker_pdn_gpio : 0);
+ if (gpio_data == spec->gpio_data)
+ return 0;
+ spec->gpio_data = gpio_data;
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data);
+ return 1;
+}
+
+static const struct snd_kcontrol_new cs8409_spk_sw_ctrl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_ctl_boolean_mono_info,
+ .get = cs8409_spk_sw_gpio_get,
+ .put = cs8409_spk_sw_gpio_put,
+};
+
+/******************************************************************************
+ * CS42L42 Specific Functions
+ ******************************************************************************/
+
+int cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int ofs = get_amp_offset(kctrl);
+ u8 chs = get_amp_channels(kctrl);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->value.integer.step = 1;
+ uinfo->count = chs == 3 ? 2 : 1;
+
+ switch (ofs) {
+ case CS42L42_VOL_DAC:
+ uinfo->value.integer.min = CS42L42_HP_VOL_REAL_MIN;
+ uinfo->value.integer.max = CS42L42_HP_VOL_REAL_MAX;
+ break;
+ case CS42L42_VOL_ADC:
+ uinfo->value.integer.min = CS42L42_AMIC_VOL_REAL_MIN;
+ uinfo->value.integer.max = CS42L42_AMIC_VOL_REAL_MAX;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kctrl);
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42 = spec->scodecs[get_amp_index(kctrl)];
+ int chs = get_amp_channels(kctrl);
+ unsigned int ofs = get_amp_offset(kctrl);
+ long *valp = uctrl->value.integer.value;
+
+ switch (ofs) {
+ case CS42L42_VOL_DAC:
+ if (chs & BIT(0))
+ *valp++ = cs42l42->vol[ofs];
+ if (chs & BIT(1))
+ *valp = cs42l42->vol[ofs+1];
+ break;
+ case CS42L42_VOL_ADC:
+ if (chs & BIT(0))
+ *valp = cs42l42->vol[ofs];
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void cs42l42_mute(struct sub_codec *cs42l42, int vol_type,
+ unsigned int chs, bool mute)
+{
+ if (mute) {
+ if (vol_type == CS42L42_VOL_DAC) {
+ if (chs & BIT(0))
+ cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHA_VOL, 0x3f);
+ if (chs & BIT(1))
+ cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHB_VOL, 0x3f);
+ } else if (vol_type == CS42L42_VOL_ADC) {
+ if (chs & BIT(0))
+ cs8409_i2c_write(cs42l42, CS42L42_ADC_VOLUME, 0x9f);
+ }
+ } else {
+ if (vol_type == CS42L42_VOL_DAC) {
+ if (chs & BIT(0))
+ cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHA_VOL,
+ -(cs42l42->vol[CS42L42_DAC_CH0_VOL_OFFSET])
+ & CS42L42_MIXER_CH_VOL_MASK);
+ if (chs & BIT(1))
+ cs8409_i2c_write(cs42l42, CS42L42_MIXER_CHB_VOL,
+ -(cs42l42->vol[CS42L42_DAC_CH1_VOL_OFFSET])
+ & CS42L42_MIXER_CH_VOL_MASK);
+ } else if (vol_type == CS42L42_VOL_ADC) {
+ if (chs & BIT(0))
+ cs8409_i2c_write(cs42l42, CS42L42_ADC_VOLUME,
+ cs42l42->vol[CS42L42_ADC_VOL_OFFSET]
+ & CS42L42_REG_AMIC_VOL_MASK);
+ }
+ }
+}
+
+int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kctrl);
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42 = spec->scodecs[get_amp_index(kctrl)];
+ int chs = get_amp_channels(kctrl);
+ unsigned int ofs = get_amp_offset(kctrl);
+ long *valp = uctrl->value.integer.value;
+
+ switch (ofs) {
+ case CS42L42_VOL_DAC:
+ if (chs & BIT(0))
+ cs42l42->vol[ofs] = *valp;
+ if (chs & BIT(1)) {
+ valp++;
+ cs42l42->vol[ofs + 1] = *valp;
+ }
+ if (spec->playback_started)
+ cs42l42_mute(cs42l42, CS42L42_VOL_DAC, chs, false);
+ break;
+ case CS42L42_VOL_ADC:
+ if (chs & BIT(0))
+ cs42l42->vol[ofs] = *valp;
+ if (spec->capture_started)
+ cs42l42_mute(cs42l42, CS42L42_VOL_ADC, chs, false);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void cs42l42_playback_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42;
+ int i;
+ bool mute;
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ mute = false;
+ spec->playback_started = 1;
+ break;
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ mute = true;
+ spec->playback_started = 0;
+ break;
+ default:
+ return;
+ }
+
+ for (i = 0; i < spec->num_scodecs; i++) {
+ cs42l42 = spec->scodecs[i];
+ cs42l42_mute(cs42l42, CS42L42_VOL_DAC, 0x3, mute);
+ }
+}
+
+static void cs42l42_capture_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42;
+ int i;
+ bool mute;
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ mute = false;
+ spec->capture_started = 1;
+ break;
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ mute = true;
+ spec->capture_started = 0;
+ break;
+ default:
+ return;
+ }
+
+ for (i = 0; i < spec->num_scodecs; i++) {
+ cs42l42 = spec->scodecs[i];
+ cs42l42_mute(cs42l42, CS42L42_VOL_ADC, 0x3, mute);
+ }
+}
+
+/* Configure CS42L42 slave codec for jack autodetect */
+static void cs42l42_enable_jack_detect(struct sub_codec *cs42l42)
+{
+ cs8409_i2c_write(cs42l42, CS42L42_HSBIAS_SC_AUTOCTL, cs42l42->hsbias_hiz);
+ /* Clear WAKE# */
+ cs8409_i2c_write(cs42l42, CS42L42_WAKE_CTL, 0x00C1);
+ /* Wait ~2.5ms */
+ usleep_range(2500, 3000);
+ /* Set mode WAKE# output follows the combination logic directly */
+ cs8409_i2c_write(cs42l42, CS42L42_WAKE_CTL, 0x00C0);
+ /* Clear interrupts status */
+ cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS);
+ /* Enable interrupt */
+ cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xF3);
+}
+
+/* Enable and run CS42L42 slave codec jack auto detect */
+static void cs42l42_run_jack_detect(struct sub_codec *cs42l42)
+{
+ /* Clear interrupts */
+ cs8409_i2c_read(cs42l42, CS42L42_CODEC_STATUS);
+ cs8409_i2c_read(cs42l42, CS42L42_DET_STATUS1);
+ cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xFF);
+ cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS);
+
+ cs8409_i2c_write(cs42l42, CS42L42_PWR_CTL2, 0x87);
+ cs8409_i2c_write(cs42l42, CS42L42_DAC_CTL2, 0x86);
+ cs8409_i2c_write(cs42l42, CS42L42_MISC_DET_CTL, 0x07);
+ cs8409_i2c_write(cs42l42, CS42L42_CODEC_INT_MASK, 0xFD);
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80);
+ /* Wait ~20ms*/
+ usleep_range(20000, 25000);
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1, 0x77);
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0xc0);
+}
+
+static int cs42l42_manual_hs_det(struct sub_codec *cs42l42)
+{
+ unsigned int hs_det_status;
+ unsigned int hs_det_comp1;
+ unsigned int hs_det_comp2;
+ unsigned int hs_det_sw;
+ unsigned int hs_type;
+
+ /* Set hs detect to manual, active mode */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2,
+ (1 << CS42L42_HSDET_CTRL_SHIFT) |
+ (0 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+
+ /* Configure HS DET comparator reference levels. */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1,
+ (CS42L42_HSDET_COMP1_LVL_VAL << CS42L42_HSDET_COMP1_LVL_SHIFT) |
+ (CS42L42_HSDET_COMP2_LVL_VAL << CS42L42_HSDET_COMP2_LVL_SHIFT));
+
+ /* Open the SW_HSB_HS3 switch and close SW_HSB_HS4 for a Type 1 headset. */
+ cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP1);
+
+ msleep(100);
+
+ hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS);
+
+ hs_det_comp1 = (hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
+ CS42L42_HSDET_COMP1_OUT_SHIFT;
+ hs_det_comp2 = (hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
+ CS42L42_HSDET_COMP2_OUT_SHIFT;
+
+ /* Close the SW_HSB_HS3 switch for a Type 2 headset. */
+ cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, CS42L42_HSDET_SW_COMP2);
+
+ msleep(100);
+
+ hs_det_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS);
+
+ hs_det_comp1 |= ((hs_det_status & CS42L42_HSDET_COMP1_OUT_MASK) >>
+ CS42L42_HSDET_COMP1_OUT_SHIFT) << 1;
+ hs_det_comp2 |= ((hs_det_status & CS42L42_HSDET_COMP2_OUT_MASK) >>
+ CS42L42_HSDET_COMP2_OUT_SHIFT) << 1;
+
+ /* Use Comparator 1 with 1.25V Threshold. */
+ switch (hs_det_comp1) {
+ case CS42L42_HSDET_COMP_TYPE1:
+ hs_type = CS42L42_PLUG_CTIA;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE1;
+ break;
+ case CS42L42_HSDET_COMP_TYPE2:
+ hs_type = CS42L42_PLUG_OMTP;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE2;
+ break;
+ default:
+ /* Fallback to Comparator 2 with 1.75V Threshold. */
+ switch (hs_det_comp2) {
+ case CS42L42_HSDET_COMP_TYPE1:
+ hs_type = CS42L42_PLUG_CTIA;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE1;
+ break;
+ case CS42L42_HSDET_COMP_TYPE2:
+ hs_type = CS42L42_PLUG_OMTP;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE2;
+ break;
+ case CS42L42_HSDET_COMP_TYPE3:
+ hs_type = CS42L42_PLUG_HEADPHONE;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE3;
+ break;
+ default:
+ hs_type = CS42L42_PLUG_INVALID;
+ hs_det_sw = CS42L42_HSDET_SW_TYPE4;
+ break;
+ }
+ }
+
+ /* Set Switches */
+ cs8409_i2c_write(cs42l42, CS42L42_HS_SWITCH_CTL, hs_det_sw);
+
+ /* Set HSDET mode to Manual—Disabled */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2,
+ (0 << CS42L42_HSDET_CTRL_SHIFT) |
+ (0 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (0 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+
+ /* Configure HS DET comparator reference levels. */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL1,
+ (CS42L42_HSDET_COMP1_LVL_DEFAULT << CS42L42_HSDET_COMP1_LVL_SHIFT) |
+ (CS42L42_HSDET_COMP2_LVL_DEFAULT << CS42L42_HSDET_COMP2_LVL_SHIFT));
+
+ return hs_type;
+}
+
+static int cs42l42_handle_tip_sense(struct sub_codec *cs42l42, unsigned int reg_ts_status)
+{
+ int status_changed = 0;
+
+ /* TIP_SENSE INSERT/REMOVE */
+ switch (reg_ts_status) {
+ case CS42L42_TS_PLUG:
+ if (cs42l42->no_type_dect) {
+ status_changed = 1;
+ cs42l42->hp_jack_in = 1;
+ cs42l42->mic_jack_in = 0;
+ } else {
+ cs42l42_run_jack_detect(cs42l42);
+ }
+ break;
+
+ case CS42L42_TS_UNPLUG:
+ status_changed = 1;
+ cs42l42->hp_jack_in = 0;
+ cs42l42->mic_jack_in = 0;
+ break;
+ default:
+ /* jack in transition */
+ break;
+ }
+
+ codec_dbg(cs42l42->codec, "Tip Sense Detection: (%d)\n", reg_ts_status);
+
+ return status_changed;
+}
+
+static int cs42l42_jack_unsol_event(struct sub_codec *cs42l42)
+{
+ int current_plug_status;
+ int status_changed = 0;
+ int reg_cdc_status;
+ int reg_hs_status;
+ int reg_ts_status;
+ int type;
+
+ /* Read jack detect status registers */
+ reg_cdc_status = cs8409_i2c_read(cs42l42, CS42L42_CODEC_STATUS);
+ reg_hs_status = cs8409_i2c_read(cs42l42, CS42L42_HS_DET_STATUS);
+ reg_ts_status = cs8409_i2c_read(cs42l42, CS42L42_TSRS_PLUG_STATUS);
+
+ /* If status values are < 0, read error has occurred. */
+ if (reg_cdc_status < 0 || reg_hs_status < 0 || reg_ts_status < 0)
+ return -EIO;
+
+ current_plug_status = (reg_ts_status & (CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK))
+ >> CS42L42_TS_PLUG_SHIFT;
+
+ /* HSDET_AUTO_DONE */
+ if (reg_cdc_status & CS42L42_HSDET_AUTO_DONE_MASK) {
+
+ /* Disable HSDET_AUTO_DONE */
+ cs8409_i2c_write(cs42l42, CS42L42_CODEC_INT_MASK, 0xFF);
+
+ type = (reg_hs_status & CS42L42_HSDET_TYPE_MASK) >> CS42L42_HSDET_TYPE_SHIFT;
+
+ /* Configure the HSDET mode. */
+ cs8409_i2c_write(cs42l42, CS42L42_HSDET_CTL2, 0x80);
+
+ if (cs42l42->no_type_dect) {
+ status_changed = cs42l42_handle_tip_sense(cs42l42, current_plug_status);
+ } else {
+ if (type == CS42L42_PLUG_INVALID || type == CS42L42_PLUG_HEADPHONE) {
+ codec_dbg(cs42l42->codec,
+ "Auto detect value not valid (%d), running manual det\n",
+ type);
+ type = cs42l42_manual_hs_det(cs42l42);
+ }
+
+ switch (type) {
+ case CS42L42_PLUG_CTIA:
+ case CS42L42_PLUG_OMTP:
+ status_changed = 1;
+ cs42l42->hp_jack_in = 1;
+ cs42l42->mic_jack_in = 1;
+ break;
+ case CS42L42_PLUG_HEADPHONE:
+ status_changed = 1;
+ cs42l42->hp_jack_in = 1;
+ cs42l42->mic_jack_in = 0;
+ break;
+ default:
+ status_changed = 1;
+ cs42l42->hp_jack_in = 0;
+ cs42l42->mic_jack_in = 0;
+ break;
+ }
+ codec_dbg(cs42l42->codec, "Detection done (%d)\n", type);
+ }
+
+ /* Enable the HPOUT ground clamp and configure the HP pull-down */
+ cs8409_i2c_write(cs42l42, CS42L42_DAC_CTL2, 0x02);
+ /* Re-Enable Tip Sense Interrupt */
+ cs8409_i2c_write(cs42l42, CS42L42_TSRS_PLUG_INT_MASK, 0xF3);
+ } else {
+ status_changed = cs42l42_handle_tip_sense(cs42l42, current_plug_status);
+ }
+
+ return status_changed;
+}
+
+static void cs42l42_resume(struct sub_codec *cs42l42)
+{
+ struct hda_codec *codec = cs42l42->codec;
+ struct cs8409_spec *spec = codec->spec;
+ struct cs8409_i2c_param irq_regs[] = {
+ { CS42L42_CODEC_STATUS, 0x00 },
+ { CS42L42_DET_INT_STATUS1, 0x00 },
+ { CS42L42_DET_INT_STATUS2, 0x00 },
+ { CS42L42_TSRS_PLUG_STATUS, 0x00 },
+ };
+ unsigned int fsv;
+
+ /* Bring CS42L42 out of Reset */
+ spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
+ spec->gpio_data |= cs42l42->reset_gpio;
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data);
+ usleep_range(10000, 15000);
+
+ cs42l42->suspended = 0;
+
+ /* Initialize CS42L42 companion codec */
+ cs8409_i2c_bulk_write(cs42l42, cs42l42->init_seq, cs42l42->init_seq_num);
+
+ /* Clear interrupts, by reading interrupt status registers */
+ cs8409_i2c_bulk_read(cs42l42, irq_regs, ARRAY_SIZE(irq_regs));
+
+ fsv = cs8409_i2c_read(cs42l42, CS42L42_HP_CTL);
+ if (cs42l42->full_scale_vol) {
+ // Set the full scale volume bit
+ fsv |= CS42L42_FULL_SCALE_VOL_MASK;
+ cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv);
+ }
+ // Unmute analog channels A and B
+ fsv = (fsv & ~CS42L42_ANA_MUTE_AB);
+ cs8409_i2c_write(cs42l42, CS42L42_HP_CTL, fsv);
+
+ /* we have to explicitly allow unsol event handling even during the
+ * resume phase so that the jack event is processed properly
+ */
+ snd_hda_codec_allow_unsol_events(cs42l42->codec);
+
+ cs42l42_enable_jack_detect(cs42l42);
+}
+
+static void cs42l42_suspend(struct sub_codec *cs42l42)
+{
+ struct hda_codec *codec = cs42l42->codec;
+ struct cs8409_spec *spec = codec->spec;
+ int reg_cdc_status = 0;
+ const struct cs8409_i2c_param cs42l42_pwr_down_seq[] = {
+ { CS42L42_DAC_CTL2, 0x02 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x00 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_HP_CTL, 0x0D },
+ { CS42L42_ASP_RX_DAI0_EN, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x00 },
+ { CS42L42_PWR_CTL1, 0xFE },
+ { CS42L42_PWR_CTL2, 0x8C },
+ { CS42L42_PWR_CTL1, 0xFF },
+ };
+
+ cs8409_i2c_bulk_write(cs42l42, cs42l42_pwr_down_seq, ARRAY_SIZE(cs42l42_pwr_down_seq));
+
+ if (read_poll_timeout(cs8409_i2c_read, reg_cdc_status,
+ (reg_cdc_status & 0x1), CS42L42_PDN_SLEEP_US, CS42L42_PDN_TIMEOUT_US,
+ true, cs42l42, CS42L42_CODEC_STATUS) < 0)
+ codec_warn(codec, "Timeout waiting for PDN_DONE for CS42L42\n");
+
+ /* Power down CS42L42 ASP/EQ/MIX/HP */
+ cs8409_i2c_write(cs42l42, CS42L42_PWR_CTL2, 0x9C);
+ cs42l42->suspended = 1;
+ cs42l42->last_page = 0;
+ cs42l42->hp_jack_in = 0;
+ cs42l42->mic_jack_in = 0;
+
+ /* Put CS42L42 into Reset */
+ spec->gpio_data = snd_hda_codec_read(codec, CS8409_PIN_AFG, 0, AC_VERB_GET_GPIO_DATA, 0);
+ spec->gpio_data &= ~cs42l42->reset_gpio;
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_data);
+}
+
+static void cs8409_remove(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ /* Cancel i2c clock disable timer, and disable clock if left enabled */
+ cancel_delayed_work_sync(&spec->i2c_clk_work);
+ cs8409_disable_i2c_clock(codec);
+
+ snd_hda_gen_remove(codec);
+}
+
+/******************************************************************************
+ * BULLSEYE / WARLOCK / CYBORG Specific Functions
+ * CS8409/CS42L42
+ ******************************************************************************/
+
+/*
+ * In the case of CS8409 we do not have unsolicited events from NID's 0x24
+ * and 0x34 where hs mic and hp are connected. Companion codec CS42L42 will
+ * generate interrupt via gpio 4 to notify jack events. We have to overwrite
+ * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers
+ * and then notify status via generic snd_hda_jack_unsol_event() call.
+ */
+static void cs8409_cs42l42_jack_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0];
+ struct hda_jack_tbl *jk;
+
+ /* jack_unsol_event() will be called every time gpio line changing state.
+ * In this case gpio4 line goes up as a result of reading interrupt status
+ * registers in previous cs8409_jack_unsol_event() call.
+ * We don't need to handle this event, ignoring...
+ */
+ if (res & cs42l42->irq_mask)
+ return;
+
+ if (cs42l42_jack_unsol_event(cs42l42)) {
+ snd_hda_set_pin_ctl(codec, CS8409_CS42L42_SPK_PIN_NID,
+ cs42l42->hp_jack_in ? 0 : PIN_OUT);
+ /* Report jack*/
+ jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_HP_PIN_NID, 0);
+ if (jk)
+ snd_hda_jack_unsol_event(codec, (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
+ AC_UNSOL_RES_TAG);
+ /* Report jack*/
+ jk = snd_hda_jack_tbl_get_mst(codec, CS8409_CS42L42_AMIC_PIN_NID, 0);
+ if (jk)
+ snd_hda_jack_unsol_event(codec, (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
+ AC_UNSOL_RES_TAG);
+ }
+}
+
+static void cs8409_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ if (spec->unsol_event)
+ spec->unsol_event(codec, res);
+ else
+ cs8409_cs42l42_jack_unsol_event(codec, res);
+}
+
+/* Manage PDREF, when transition to D3hot */
+static int cs8409_cs42l42_suspend(struct hda_codec *codec)
+{
+ struct cs8409_spec *spec = codec->spec;
+ int i;
+
+ spec->init_done = 0;
+
+ cs8409_enable_ur(codec, 0);
+
+ for (i = 0; i < spec->num_scodecs; i++)
+ cs42l42_suspend(spec->scodecs[i]);
+
+ /* Cancel i2c clock disable timer, and disable clock if left enabled */
+ cancel_delayed_work_sync(&spec->i2c_clk_work);
+ cs8409_disable_i2c_clock(codec);
+
+ snd_hda_shutup_pins(codec);
+
+ return 0;
+}
+
+/* Vendor specific HW configuration
+ * PLL, ASP, I2C, SPI, GPIOs, DMIC etc...
+ */
+static void cs8409_cs42l42_hw_init(struct hda_codec *codec)
+{
+ const struct cs8409_cir_param *seq = cs8409_cs42l42_hw_cfg;
+ const struct cs8409_cir_param *seq_bullseye = cs8409_cs42l42_bullseye_atn;
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0];
+
+ if (spec->gpio_mask) {
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK,
+ spec->gpio_mask);
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION,
+ spec->gpio_dir);
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_data);
+ }
+
+ for (; seq->nid; seq++)
+ cs8409_vendor_coef_set(codec, seq->cir, seq->coeff);
+
+ if (codec->fixup_id == CS8409_BULLSEYE) {
+ for (; seq_bullseye->nid; seq_bullseye++)
+ cs8409_vendor_coef_set(codec, seq_bullseye->cir, seq_bullseye->coeff);
+ }
+
+ switch (codec->fixup_id) {
+ case CS8409_CYBORG:
+ case CS8409_WARLOCK_MLK_DUAL_MIC:
+ /* DMIC1_MO=00b, DMIC1/2_SR=1 */
+ cs8409_vendor_coef_set(codec, CS8409_DMIC_CFG, 0x0003);
+ break;
+ case CS8409_ODIN:
+ /* ASP1/2_xxx_EN=1, ASP1/2_MCLK_EN=0, DMIC1_SCL_EN=0 */
+ cs8409_vendor_coef_set(codec, CS8409_PAD_CFG_SLW_RATE_CTRL, 0xfc00);
+ break;
+ default:
+ break;
+ }
+
+ cs42l42_resume(cs42l42);
+
+ /* Enable Unsolicited Response */
+ cs8409_enable_ur(codec, 1);
+}
+
+static int cs8409_cs42l42_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
+ unsigned int *res)
+{
+ struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0];
+
+ unsigned int nid = ((cmd >> 20) & 0x07f);
+ unsigned int verb = ((cmd >> 8) & 0x0fff);
+
+ /* CS8409 pins have no AC_PINSENSE_PRESENCE
+ * capabilities. We have to intercept 2 calls for pins 0x24 and 0x34
+ * and return correct pin sense values for read_pin_sense() call from
+ * hda_jack based on CS42L42 jack detect status.
+ */
+ switch (nid) {
+ case CS8409_CS42L42_HP_PIN_NID:
+ if (verb == AC_VERB_GET_PIN_SENSE) {
+ *res = (cs42l42->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0;
+ return 0;
+ }
+ break;
+ case CS8409_CS42L42_AMIC_PIN_NID:
+ if (verb == AC_VERB_GET_PIN_SENSE) {
+ *res = (cs42l42->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0;
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return spec->exec_verb(dev, cmd, flags, res);
+}
+
+void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action)
+{
+ struct cs8409_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_add_verbs(codec, cs8409_cs42l42_init_verbs);
+ /* verb exec op override */
+ spec->exec_verb = codec->core.exec_verb;
+ codec->core.exec_verb = cs8409_cs42l42_exec_verb;
+
+ spec->scodecs[CS8409_CODEC0] = &cs8409_cs42l42_codec;
+ spec->num_scodecs = 1;
+ spec->scodecs[CS8409_CODEC0]->codec = codec;
+
+ spec->gen.suppress_auto_mute = 1;
+ spec->gen.no_primary_hp = 1;
+ spec->gen.suppress_vmaster = 1;
+
+ spec->speaker_pdn_gpio = 0;
+
+ /* GPIO 5 out, 3,4 in */
+ spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio;
+ spec->gpio_data = 0;
+ spec->gpio_mask = 0x03f;
+
+ /* Basic initial sequence for specific hw configuration */
+ snd_hda_sequence_write(codec, cs8409_cs42l42_init_verbs);
+
+ cs8409_fix_caps(codec, CS8409_CS42L42_HP_PIN_NID);
+ cs8409_fix_caps(codec, CS8409_CS42L42_AMIC_PIN_NID);
+
+ spec->scodecs[CS8409_CODEC0]->hsbias_hiz = 0x0020;
+
+ switch (codec->fixup_id) {
+ case CS8409_CYBORG:
+ spec->scodecs[CS8409_CODEC0]->full_scale_vol =
+ CS42L42_FULL_SCALE_VOL_MINUS6DB;
+ spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN;
+ break;
+ case CS8409_ODIN:
+ spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB;
+ spec->speaker_pdn_gpio = CS8409_CYBORG_SPEAKER_PDN;
+ break;
+ case CS8409_WARLOCK_MLK:
+ case CS8409_WARLOCK_MLK_DUAL_MIC:
+ spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_0DB;
+ spec->speaker_pdn_gpio = CS8409_WARLOCK_SPEAKER_PDN;
+ break;
+ default:
+ spec->scodecs[CS8409_CODEC0]->full_scale_vol =
+ CS42L42_FULL_SCALE_VOL_MINUS6DB;
+ spec->speaker_pdn_gpio = CS8409_WARLOCK_SPEAKER_PDN;
+ break;
+ }
+
+ if (spec->speaker_pdn_gpio > 0) {
+ spec->gpio_dir |= spec->speaker_pdn_gpio;
+ spec->gpio_data |= spec->speaker_pdn_gpio;
+ }
+
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ /* Fix Sample Rate to 48kHz */
+ spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback;
+ spec->gen.stream_analog_capture = &cs42l42_48k_pcm_analog_capture;
+ /* add hooks */
+ spec->gen.pcm_playback_hook = cs42l42_playback_pcm_hook;
+ spec->gen.pcm_capture_hook = cs42l42_capture_pcm_hook;
+ if (codec->fixup_id != CS8409_ODIN)
+ /* Set initial DMIC volume to -26 dB */
+ snd_hda_codec_amp_init_stereo(codec, CS8409_CS42L42_DMIC_ADC_PIN_NID,
+ HDA_INPUT, 0, 0xff, 0x19);
+ snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume",
+ &cs42l42_dac_volume_mixer);
+ snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume",
+ &cs42l42_adc_volume_mixer);
+ if (spec->speaker_pdn_gpio > 0)
+ snd_hda_gen_add_kctl(&spec->gen, "Speaker Playback Switch",
+ &cs8409_spk_sw_ctrl);
+ /* Disable Unsolicited Response during boot */
+ cs8409_enable_ur(codec, 0);
+ snd_hda_codec_set_name(codec, "CS8409/CS42L42");
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ cs8409_cs42l42_hw_init(codec);
+ spec->init_done = 1;
+ if (spec->init_done && spec->build_ctrl_done
+ && !spec->scodecs[CS8409_CODEC0]->hp_jack_in)
+ cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]);
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ spec->build_ctrl_done = 1;
+ /* Run jack auto detect first time on boot
+ * after controls have been added, to check if jack has
+ * been already plugged in.
+ * Run immediately after init.
+ */
+ if (spec->init_done && spec->build_ctrl_done
+ && !spec->scodecs[CS8409_CODEC0]->hp_jack_in)
+ cs42l42_run_jack_detect(spec->scodecs[CS8409_CODEC0]);
+ break;
+ default:
+ break;
+ }
+}
+
+/******************************************************************************
+ * Dolphin Specific Functions
+ * CS8409/ 2 X CS42L42
+ ******************************************************************************/
+
+/*
+ * In the case of CS8409 we do not have unsolicited events when
+ * hs mic and hp are connected. Companion codec CS42L42 will
+ * generate interrupt via irq_mask to notify jack events. We have to overwrite
+ * generic snd_hda_jack_unsol_event(), read CS42L42 jack detect status registers
+ * and then notify status via generic snd_hda_jack_unsol_event() call.
+ */
+static void dolphin_jack_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42;
+ struct hda_jack_tbl *jk;
+
+ cs42l42 = spec->scodecs[CS8409_CODEC0];
+ if (!cs42l42->suspended && (~res & cs42l42->irq_mask) &&
+ cs42l42_jack_unsol_event(cs42l42)) {
+ jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_HP_PIN_NID, 0);
+ if (jk)
+ snd_hda_jack_unsol_event(codec,
+ (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
+ AC_UNSOL_RES_TAG);
+
+ jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_AMIC_PIN_NID, 0);
+ if (jk)
+ snd_hda_jack_unsol_event(codec,
+ (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
+ AC_UNSOL_RES_TAG);
+ }
+
+ cs42l42 = spec->scodecs[CS8409_CODEC1];
+ if (!cs42l42->suspended && (~res & cs42l42->irq_mask) &&
+ cs42l42_jack_unsol_event(cs42l42)) {
+ jk = snd_hda_jack_tbl_get_mst(codec, DOLPHIN_LO_PIN_NID, 0);
+ if (jk)
+ snd_hda_jack_unsol_event(codec,
+ (jk->tag << AC_UNSOL_RES_TAG_SHIFT) &
+ AC_UNSOL_RES_TAG);
+ }
+}
+
+/* Vendor specific HW configuration
+ * PLL, ASP, I2C, SPI, GPIOs, DMIC etc...
+ */
+static void dolphin_hw_init(struct hda_codec *codec)
+{
+ const struct cs8409_cir_param *seq = dolphin_hw_cfg;
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42;
+ int i;
+
+ if (spec->gpio_mask) {
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_MASK,
+ spec->gpio_mask);
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DIRECTION,
+ spec->gpio_dir);
+ snd_hda_codec_write(codec, CS8409_PIN_AFG, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_data);
+ }
+
+ for (; seq->nid; seq++)
+ cs8409_vendor_coef_set(codec, seq->cir, seq->coeff);
+
+ for (i = 0; i < spec->num_scodecs; i++) {
+ cs42l42 = spec->scodecs[i];
+ cs42l42_resume(cs42l42);
+ }
+
+ /* Enable Unsolicited Response */
+ cs8409_enable_ur(codec, 1);
+}
+
+static int dolphin_exec_verb(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
+ unsigned int *res)
+{
+ struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+ struct cs8409_spec *spec = codec->spec;
+ struct sub_codec *cs42l42 = spec->scodecs[CS8409_CODEC0];
+
+ unsigned int nid = ((cmd >> 20) & 0x07f);
+ unsigned int verb = ((cmd >> 8) & 0x0fff);
+
+ /* CS8409 pins have no AC_PINSENSE_PRESENCE
+ * capabilities. We have to intercept calls for CS42L42 pins
+ * and return correct pin sense values for read_pin_sense() call from
+ * hda_jack based on CS42L42 jack detect status.
+ */
+ switch (nid) {
+ case DOLPHIN_HP_PIN_NID:
+ case DOLPHIN_LO_PIN_NID:
+ if (nid == DOLPHIN_LO_PIN_NID)
+ cs42l42 = spec->scodecs[CS8409_CODEC1];
+ if (verb == AC_VERB_GET_PIN_SENSE) {
+ *res = (cs42l42->hp_jack_in) ? AC_PINSENSE_PRESENCE : 0;
+ return 0;
+ }
+ break;
+ case DOLPHIN_AMIC_PIN_NID:
+ if (verb == AC_VERB_GET_PIN_SENSE) {
+ *res = (cs42l42->mic_jack_in) ? AC_PINSENSE_PRESENCE : 0;
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return spec->exec_verb(dev, cmd, flags, res);
+}
+
+void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action)
+{
+ struct cs8409_spec *spec = codec->spec;
+ struct snd_kcontrol_new *kctrl;
+ int i;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_add_verbs(codec, dolphin_init_verbs);
+ /* verb exec op override */
+ spec->exec_verb = codec->core.exec_verb;
+ codec->core.exec_verb = dolphin_exec_verb;
+
+ spec->scodecs[CS8409_CODEC0] = &dolphin_cs42l42_0;
+ spec->scodecs[CS8409_CODEC0]->codec = codec;
+ spec->scodecs[CS8409_CODEC1] = &dolphin_cs42l42_1;
+ spec->scodecs[CS8409_CODEC1]->codec = codec;
+ spec->num_scodecs = 2;
+ spec->gen.suppress_vmaster = 1;
+
+ spec->unsol_event = dolphin_jack_unsol_event;
+
+ /* GPIO 1,5 out, 0,4 in */
+ spec->gpio_dir = spec->scodecs[CS8409_CODEC0]->reset_gpio |
+ spec->scodecs[CS8409_CODEC1]->reset_gpio;
+ spec->gpio_data = 0;
+ spec->gpio_mask = 0x03f;
+
+ /* Basic initial sequence for specific hw configuration */
+ snd_hda_sequence_write(codec, dolphin_init_verbs);
+
+ snd_hda_jack_add_kctl(codec, DOLPHIN_LO_PIN_NID, "Line Out", true,
+ SND_JACK_HEADPHONE, NULL);
+
+ snd_hda_jack_add_kctl(codec, DOLPHIN_AMIC_PIN_NID, "Microphone", true,
+ SND_JACK_MICROPHONE, NULL);
+
+ cs8409_fix_caps(codec, DOLPHIN_HP_PIN_NID);
+ cs8409_fix_caps(codec, DOLPHIN_LO_PIN_NID);
+ cs8409_fix_caps(codec, DOLPHIN_AMIC_PIN_NID);
+
+ spec->scodecs[CS8409_CODEC0]->full_scale_vol = CS42L42_FULL_SCALE_VOL_MINUS6DB;
+ spec->scodecs[CS8409_CODEC1]->full_scale_vol = CS42L42_FULL_SCALE_VOL_MINUS6DB;
+
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ /* Fix Sample Rate to 48kHz */
+ spec->gen.stream_analog_playback = &cs42l42_48k_pcm_analog_playback;
+ spec->gen.stream_analog_capture = &cs42l42_48k_pcm_analog_capture;
+ /* add hooks */
+ spec->gen.pcm_playback_hook = cs42l42_playback_pcm_hook;
+ spec->gen.pcm_capture_hook = cs42l42_capture_pcm_hook;
+ snd_hda_gen_add_kctl(&spec->gen, "Headphone Playback Volume",
+ &cs42l42_dac_volume_mixer);
+ snd_hda_gen_add_kctl(&spec->gen, "Mic Capture Volume", &cs42l42_adc_volume_mixer);
+ kctrl = snd_hda_gen_add_kctl(&spec->gen, "Line Out Playback Volume",
+ &cs42l42_dac_volume_mixer);
+ /* Update Line Out kcontrol template */
+ if (kctrl)
+ kctrl->private_value = HDA_COMPOSE_AMP_VAL_OFS(DOLPHIN_HP_PIN_NID, 3, CS8409_CODEC1,
+ HDA_OUTPUT, CS42L42_VOL_DAC) | HDA_AMP_VAL_MIN_MUTE;
+ cs8409_enable_ur(codec, 0);
+ snd_hda_codec_set_name(codec, "CS8409/CS42L42");
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ dolphin_hw_init(codec);
+ spec->init_done = 1;
+ if (spec->init_done && spec->build_ctrl_done) {
+ for (i = 0; i < spec->num_scodecs; i++) {
+ if (!spec->scodecs[i]->hp_jack_in)
+ cs42l42_run_jack_detect(spec->scodecs[i]);
+ }
+ }
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ spec->build_ctrl_done = 1;
+ /* Run jack auto detect first time on boot
+ * after controls have been added, to check if jack has
+ * been already plugged in.
+ * Run immediately after init.
+ */
+ if (spec->init_done && spec->build_ctrl_done) {
+ for (i = 0; i < spec->num_scodecs; i++) {
+ if (!spec->scodecs[i]->hp_jack_in)
+ cs42l42_run_jack_detect(spec->scodecs[i]);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static int cs8409_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ int err;
+
+ if (!cs8409_alloc_spec(codec))
+ return -ENOMEM;
+
+ snd_hda_pick_fixup(codec, cs8409_models, cs8409_fixup_tbl, cs8409_fixups);
+
+ codec_dbg(codec, "Picked ID=%d, VID=%08x, DEV=%08x\n", codec->fixup_id,
+ codec->bus->pci->subsystem_vendor,
+ codec->bus->pci->subsystem_device);
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = cs8409_parse_auto_config(codec);
+ if (err < 0) {
+ cs8409_remove(codec);
+ return err;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ return 0;
+}
+
+static const struct hda_codec_ops cs8409_codec_ops = {
+ .probe = cs8409_probe,
+ .remove = cs8409_remove,
+ .build_controls = cs8409_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cs8409_init,
+ .unsol_event = cs8409_unsol_event,
+ .suspend = cs8409_cs42l42_suspend,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+static const struct hda_device_id snd_hda_id_cs8409[] = {
+ HDA_CODEC_ID(0x10138409, "CS8409"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cs8409);
+
+static struct hda_codec_driver cs8409_driver = {
+ .id = snd_hda_id_cs8409,
+ .ops = &cs8409_codec_ops,
+};
+module_hda_codec_driver(cs8409_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cirrus Logic HDA bridge");
diff --git a/sound/hda/codecs/cirrus/cs8409.h b/sound/hda/codecs/cirrus/cs8409.h
new file mode 100644
index 000000000000..7fe56f4a73bc
--- /dev/null
+++ b/sound/hda/codecs/cirrus/cs8409.h
@@ -0,0 +1,377 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * HD audio codec driver for Cirrus Logic CS8409 HDA bridge chip
+ *
+ * Copyright (C) 2021 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef __CS8409_PATCH_H
+#define __CS8409_PATCH_H
+
+#include <linux/pci.h>
+#include <sound/tlv.h>
+#include <linux/workqueue.h>
+#include <sound/cs42l42.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "../generic.h"
+
+/* CS8409 Specific Definitions */
+
+enum cs8409_pins {
+ CS8409_PIN_ROOT,
+ CS8409_PIN_AFG,
+ CS8409_PIN_ASP1_OUT_A,
+ CS8409_PIN_ASP1_OUT_B,
+ CS8409_PIN_ASP1_OUT_C,
+ CS8409_PIN_ASP1_OUT_D,
+ CS8409_PIN_ASP1_OUT_E,
+ CS8409_PIN_ASP1_OUT_F,
+ CS8409_PIN_ASP1_OUT_G,
+ CS8409_PIN_ASP1_OUT_H,
+ CS8409_PIN_ASP2_OUT_A,
+ CS8409_PIN_ASP2_OUT_B,
+ CS8409_PIN_ASP2_OUT_C,
+ CS8409_PIN_ASP2_OUT_D,
+ CS8409_PIN_ASP2_OUT_E,
+ CS8409_PIN_ASP2_OUT_F,
+ CS8409_PIN_ASP2_OUT_G,
+ CS8409_PIN_ASP2_OUT_H,
+ CS8409_PIN_ASP1_IN_A,
+ CS8409_PIN_ASP1_IN_B,
+ CS8409_PIN_ASP1_IN_C,
+ CS8409_PIN_ASP1_IN_D,
+ CS8409_PIN_ASP1_IN_E,
+ CS8409_PIN_ASP1_IN_F,
+ CS8409_PIN_ASP1_IN_G,
+ CS8409_PIN_ASP1_IN_H,
+ CS8409_PIN_ASP2_IN_A,
+ CS8409_PIN_ASP2_IN_B,
+ CS8409_PIN_ASP2_IN_C,
+ CS8409_PIN_ASP2_IN_D,
+ CS8409_PIN_ASP2_IN_E,
+ CS8409_PIN_ASP2_IN_F,
+ CS8409_PIN_ASP2_IN_G,
+ CS8409_PIN_ASP2_IN_H,
+ CS8409_PIN_DMIC1,
+ CS8409_PIN_DMIC2,
+ CS8409_PIN_ASP1_TRANSMITTER_A,
+ CS8409_PIN_ASP1_TRANSMITTER_B,
+ CS8409_PIN_ASP1_TRANSMITTER_C,
+ CS8409_PIN_ASP1_TRANSMITTER_D,
+ CS8409_PIN_ASP1_TRANSMITTER_E,
+ CS8409_PIN_ASP1_TRANSMITTER_F,
+ CS8409_PIN_ASP1_TRANSMITTER_G,
+ CS8409_PIN_ASP1_TRANSMITTER_H,
+ CS8409_PIN_ASP2_TRANSMITTER_A,
+ CS8409_PIN_ASP2_TRANSMITTER_B,
+ CS8409_PIN_ASP2_TRANSMITTER_C,
+ CS8409_PIN_ASP2_TRANSMITTER_D,
+ CS8409_PIN_ASP2_TRANSMITTER_E,
+ CS8409_PIN_ASP2_TRANSMITTER_F,
+ CS8409_PIN_ASP2_TRANSMITTER_G,
+ CS8409_PIN_ASP2_TRANSMITTER_H,
+ CS8409_PIN_ASP1_RECEIVER_A,
+ CS8409_PIN_ASP1_RECEIVER_B,
+ CS8409_PIN_ASP1_RECEIVER_C,
+ CS8409_PIN_ASP1_RECEIVER_D,
+ CS8409_PIN_ASP1_RECEIVER_E,
+ CS8409_PIN_ASP1_RECEIVER_F,
+ CS8409_PIN_ASP1_RECEIVER_G,
+ CS8409_PIN_ASP1_RECEIVER_H,
+ CS8409_PIN_ASP2_RECEIVER_A,
+ CS8409_PIN_ASP2_RECEIVER_B,
+ CS8409_PIN_ASP2_RECEIVER_C,
+ CS8409_PIN_ASP2_RECEIVER_D,
+ CS8409_PIN_ASP2_RECEIVER_E,
+ CS8409_PIN_ASP2_RECEIVER_F,
+ CS8409_PIN_ASP2_RECEIVER_G,
+ CS8409_PIN_ASP2_RECEIVER_H,
+ CS8409_PIN_DMIC1_IN,
+ CS8409_PIN_DMIC2_IN,
+ CS8409_PIN_BEEP_GEN,
+ CS8409_PIN_VENDOR_WIDGET
+};
+
+enum cs8409_coefficient_index_registers {
+ CS8409_DEV_CFG1,
+ CS8409_DEV_CFG2,
+ CS8409_DEV_CFG3,
+ CS8409_ASP1_CLK_CTRL1,
+ CS8409_ASP1_CLK_CTRL2,
+ CS8409_ASP1_CLK_CTRL3,
+ CS8409_ASP2_CLK_CTRL1,
+ CS8409_ASP2_CLK_CTRL2,
+ CS8409_ASP2_CLK_CTRL3,
+ CS8409_DMIC_CFG,
+ CS8409_BEEP_CFG,
+ ASP1_RX_NULL_INS_RMV,
+ ASP1_Rx_RATE1,
+ ASP1_Rx_RATE2,
+ ASP1_Tx_NULL_INS_RMV,
+ ASP1_Tx_RATE1,
+ ASP1_Tx_RATE2,
+ ASP2_Rx_NULL_INS_RMV,
+ ASP2_Rx_RATE1,
+ ASP2_Rx_RATE2,
+ ASP2_Tx_NULL_INS_RMV,
+ ASP2_Tx_RATE1,
+ ASP2_Tx_RATE2,
+ ASP1_SYNC_CTRL,
+ ASP2_SYNC_CTRL,
+ ASP1_A_TX_CTRL1,
+ ASP1_A_TX_CTRL2,
+ ASP1_B_TX_CTRL1,
+ ASP1_B_TX_CTRL2,
+ ASP1_C_TX_CTRL1,
+ ASP1_C_TX_CTRL2,
+ ASP1_D_TX_CTRL1,
+ ASP1_D_TX_CTRL2,
+ ASP1_E_TX_CTRL1,
+ ASP1_E_TX_CTRL2,
+ ASP1_F_TX_CTRL1,
+ ASP1_F_TX_CTRL2,
+ ASP1_G_TX_CTRL1,
+ ASP1_G_TX_CTRL2,
+ ASP1_H_TX_CTRL1,
+ ASP1_H_TX_CTRL2,
+ ASP2_A_TX_CTRL1,
+ ASP2_A_TX_CTRL2,
+ ASP2_B_TX_CTRL1,
+ ASP2_B_TX_CTRL2,
+ ASP2_C_TX_CTRL1,
+ ASP2_C_TX_CTRL2,
+ ASP2_D_TX_CTRL1,
+ ASP2_D_TX_CTRL2,
+ ASP2_E_TX_CTRL1,
+ ASP2_E_TX_CTRL2,
+ ASP2_F_TX_CTRL1,
+ ASP2_F_TX_CTRL2,
+ ASP2_G_TX_CTRL1,
+ ASP2_G_TX_CTRL2,
+ ASP2_H_TX_CTRL1,
+ ASP2_H_TX_CTRL2,
+ ASP1_A_RX_CTRL1,
+ ASP1_A_RX_CTRL2,
+ ASP1_B_RX_CTRL1,
+ ASP1_B_RX_CTRL2,
+ ASP1_C_RX_CTRL1,
+ ASP1_C_RX_CTRL2,
+ ASP1_D_RX_CTRL1,
+ ASP1_D_RX_CTRL2,
+ ASP1_E_RX_CTRL1,
+ ASP1_E_RX_CTRL2,
+ ASP1_F_RX_CTRL1,
+ ASP1_F_RX_CTRL2,
+ ASP1_G_RX_CTRL1,
+ ASP1_G_RX_CTRL2,
+ ASP1_H_RX_CTRL1,
+ ASP1_H_RX_CTRL2,
+ ASP2_A_RX_CTRL1,
+ ASP2_A_RX_CTRL2,
+ ASP2_B_RX_CTRL1,
+ ASP2_B_RX_CTRL2,
+ ASP2_C_RX_CTRL1,
+ ASP2_C_RX_CTRL2,
+ ASP2_D_RX_CTRL1,
+ ASP2_D_RX_CTRL2,
+ ASP2_E_RX_CTRL1,
+ ASP2_E_RX_CTRL2,
+ ASP2_F_RX_CTRL1,
+ ASP2_F_RX_CTRL2,
+ ASP2_G_RX_CTRL1,
+ ASP2_G_RX_CTRL2,
+ ASP2_H_RX_CTRL1,
+ ASP2_H_RX_CTRL2,
+ CS8409_I2C_ADDR,
+ CS8409_I2C_DATA,
+ CS8409_I2C_CTRL,
+ CS8409_I2C_STS,
+ CS8409_I2C_QWRITE,
+ CS8409_I2C_QREAD,
+ CS8409_SPI_CTRL,
+ CS8409_SPI_TX_DATA,
+ CS8409_SPI_RX_DATA,
+ CS8409_SPI_STS,
+ CS8409_PFE_COEF_W1, /* Parametric filter engine coefficient write 1*/
+ CS8409_PFE_COEF_W2,
+ CS8409_PFE_CTRL1,
+ CS8409_PFE_CTRL2,
+ CS8409_PRE_SCALE_ATTN1,
+ CS8409_PRE_SCALE_ATTN2,
+ CS8409_PFE_COEF_MON1, /* Parametric filter engine coefficient monitor 1*/
+ CS8409_PFE_COEF_MON2,
+ CS8409_ASP1_INTRN_STS,
+ CS8409_ASP2_INTRN_STS,
+ CS8409_ASP1_RX_SCLK_COUNT,
+ CS8409_ASP1_TX_SCLK_COUNT,
+ CS8409_ASP2_RX_SCLK_COUNT,
+ CS8409_ASP2_TX_SCLK_COUNT,
+ CS8409_ASP_UNS_RESP_MASK,
+ CS8409_LOOPBACK_CTRL = 0x80,
+ CS8409_PAD_CFG_SLW_RATE_CTRL = 0x82, /* Pad Config and Slew Rate Control (CIR = 0x0082) */
+};
+
+/* CS42L42 Specific Definitions */
+
+#define CS8409_MAX_CODECS 8
+#define CS42L42_VOLUMES (4U)
+#define CS42L42_HP_VOL_REAL_MIN (-63)
+#define CS42L42_HP_VOL_REAL_MAX (0)
+#define CS42L42_AMIC_VOL_REAL_MIN (-97)
+#define CS42L42_AMIC_VOL_REAL_MAX (12)
+#define CS42L42_REG_AMIC_VOL_MASK (0x00FF)
+#define CS42L42_HSTYPE_MASK (0x03)
+#define CS42L42_I2C_TIMEOUT_US (20000)
+#define CS42L42_I2C_SLEEP_US (2000)
+#define CS42L42_PDN_TIMEOUT_US (250000)
+#define CS42L42_PDN_SLEEP_US (2000)
+#define CS42L42_ANA_MUTE_AB (0x0C)
+#define CS42L42_FULL_SCALE_VOL_MASK (2)
+#define CS42L42_FULL_SCALE_VOL_0DB (0)
+#define CS42L42_FULL_SCALE_VOL_MINUS6DB (1)
+
+/* Dell BULLSEYE / WARLOCK / CYBORG Specific Definitions */
+
+#define CS42L42_I2C_ADDR (0x48 << 1)
+#define CS8409_CS42L42_RESET GENMASK(5, 5) /* CS8409_GPIO5 */
+#define CS8409_CS42L42_INT GENMASK(4, 4) /* CS8409_GPIO4 */
+#define CS8409_CYBORG_SPEAKER_PDN GENMASK(2, 2) /* CS8409_GPIO2 */
+#define CS8409_WARLOCK_SPEAKER_PDN GENMASK(1, 1) /* CS8409_GPIO1 */
+#define CS8409_CS42L42_HP_PIN_NID CS8409_PIN_ASP1_TRANSMITTER_A
+#define CS8409_CS42L42_SPK_PIN_NID CS8409_PIN_ASP2_TRANSMITTER_A
+#define CS8409_CS42L42_AMIC_PIN_NID CS8409_PIN_ASP1_RECEIVER_A
+#define CS8409_CS42L42_DMIC_PIN_NID CS8409_PIN_DMIC1_IN
+#define CS8409_CS42L42_DMIC_ADC_PIN_NID CS8409_PIN_DMIC1
+
+/* Dolphin */
+
+#define DOLPHIN_C0_I2C_ADDR (0x48 << 1)
+#define DOLPHIN_C1_I2C_ADDR (0x49 << 1)
+#define DOLPHIN_HP_PIN_NID CS8409_PIN_ASP1_TRANSMITTER_A
+#define DOLPHIN_LO_PIN_NID CS8409_PIN_ASP1_TRANSMITTER_B
+#define DOLPHIN_AMIC_PIN_NID CS8409_PIN_ASP1_RECEIVER_A
+
+#define DOLPHIN_C0_INT GENMASK(4, 4)
+#define DOLPHIN_C1_INT GENMASK(0, 0)
+#define DOLPHIN_C0_RESET GENMASK(5, 5)
+#define DOLPHIN_C1_RESET GENMASK(1, 1)
+#define DOLPHIN_WAKE (DOLPHIN_C0_INT | DOLPHIN_C1_INT)
+
+enum {
+ CS8409_BULLSEYE,
+ CS8409_WARLOCK,
+ CS8409_WARLOCK_MLK,
+ CS8409_WARLOCK_MLK_DUAL_MIC,
+ CS8409_CYBORG,
+ CS8409_FIXUPS,
+ CS8409_DOLPHIN,
+ CS8409_DOLPHIN_FIXUPS,
+ CS8409_ODIN,
+};
+
+enum {
+ CS8409_CODEC0,
+ CS8409_CODEC1
+};
+
+enum {
+ CS42L42_VOL_ADC,
+ CS42L42_VOL_DAC,
+};
+
+#define CS42L42_ADC_VOL_OFFSET (CS42L42_VOL_ADC)
+#define CS42L42_DAC_CH0_VOL_OFFSET (CS42L42_VOL_DAC)
+#define CS42L42_DAC_CH1_VOL_OFFSET (CS42L42_VOL_DAC + 1)
+
+struct cs8409_i2c_param {
+ unsigned int addr;
+ unsigned int value;
+ unsigned int delay;
+};
+
+struct cs8409_cir_param {
+ unsigned int nid;
+ unsigned int cir;
+ unsigned int coeff;
+};
+
+struct sub_codec {
+ struct hda_codec *codec;
+ unsigned int addr;
+ unsigned int reset_gpio;
+ unsigned int irq_mask;
+ const struct cs8409_i2c_param *init_seq;
+ unsigned int init_seq_num;
+
+ unsigned int hp_jack_in:1;
+ unsigned int mic_jack_in:1;
+ unsigned int suspended:1;
+ unsigned int paged:1;
+ unsigned int last_page;
+ unsigned int hsbias_hiz;
+ unsigned int full_scale_vol:1;
+ unsigned int no_type_dect:1;
+
+ s8 vol[CS42L42_VOLUMES];
+};
+
+struct cs8409_spec {
+ struct hda_gen_spec gen;
+ struct hda_codec *codec;
+
+ struct sub_codec *scodecs[CS8409_MAX_CODECS];
+ unsigned int num_scodecs;
+
+ unsigned int gpio_mask;
+ unsigned int gpio_dir;
+ unsigned int gpio_data;
+
+ int speaker_pdn_gpio;
+
+ struct mutex i2c_mux;
+ unsigned int i2c_clck_enabled;
+ unsigned int dev_addr;
+ struct delayed_work i2c_clk_work;
+
+ unsigned int playback_started:1;
+ unsigned int capture_started:1;
+ unsigned int init_done:1;
+ unsigned int build_ctrl_done:1;
+
+ /* verb exec op override */
+ int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
+ unsigned int *res);
+ /* unsol_event op override */
+ void (*unsol_event)(struct hda_codec *codec, unsigned int res);
+};
+
+extern const struct snd_kcontrol_new cs42l42_dac_volume_mixer;
+extern const struct snd_kcontrol_new cs42l42_adc_volume_mixer;
+
+int cs42l42_volume_info(struct snd_kcontrol *kctrl, struct snd_ctl_elem_info *uinfo);
+int cs42l42_volume_get(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl);
+int cs42l42_volume_put(struct snd_kcontrol *kctrl, struct snd_ctl_elem_value *uctrl);
+
+extern const struct hda_pcm_stream cs42l42_48k_pcm_analog_playback;
+extern const struct hda_pcm_stream cs42l42_48k_pcm_analog_capture;
+extern const struct hda_quirk cs8409_fixup_tbl[];
+extern const struct hda_model_fixup cs8409_models[];
+extern const struct hda_fixup cs8409_fixups[];
+extern const struct hda_verb cs8409_cs42l42_init_verbs[];
+extern const struct cs8409_cir_param cs8409_cs42l42_hw_cfg[];
+extern const struct cs8409_cir_param cs8409_cs42l42_bullseye_atn[];
+extern struct sub_codec cs8409_cs42l42_codec;
+
+extern const struct hda_verb dolphin_init_verbs[];
+extern const struct cs8409_cir_param dolphin_hw_cfg[];
+extern struct sub_codec dolphin_cs42l42_0;
+extern struct sub_codec dolphin_cs42l42_1;
+
+void cs8409_cs42l42_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action);
+void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int action);
+
+#endif
diff --git a/sound/hda/codecs/cm9825.c b/sound/hda/codecs/cm9825.c
new file mode 100644
index 000000000000..5c474ce44348
--- /dev/null
+++ b/sound/hda/codecs/cm9825.c
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * CM9825 HD-audio codec
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "generic.h"
+
+/* CM9825 Offset Definitions */
+
+#define CM9825_VERB_SET_HPF_1 0x781
+#define CM9825_VERB_SET_HPF_2 0x785
+#define CM9825_VERB_SET_PLL 0x7a0
+#define CM9825_VERB_SET_NEG 0x7a1
+#define CM9825_VERB_SET_ADCL 0x7a2
+#define CM9825_VERB_SET_DACL 0x7a3
+#define CM9825_VERB_SET_MBIAS 0x7a4
+#define CM9825_VERB_SET_VNEG 0x7a8
+#define CM9825_VERB_SET_D2S 0x7a9
+#define CM9825_VERB_SET_DACTRL 0x7aa
+#define CM9825_VERB_SET_PDNEG 0x7ac
+#define CM9825_VERB_SET_VDO 0x7ad
+#define CM9825_VERB_SET_CDALR 0x7b0
+#define CM9825_VERB_SET_MTCBA 0x7b1
+#define CM9825_VERB_SET_OTP 0x7b2
+#define CM9825_VERB_SET_OCP 0x7b3
+#define CM9825_VERB_SET_GAD 0x7b4
+#define CM9825_VERB_SET_TMOD 0x7b5
+#define CM9825_VERB_SET_SNR 0x7b6
+
+struct cmi_spec {
+ struct hda_gen_spec gen;
+ const struct hda_verb *chip_d0_verbs;
+ const struct hda_verb *chip_d3_verbs;
+ const struct hda_verb *chip_hp_present_verbs;
+ const struct hda_verb *chip_hp_remove_verbs;
+ struct hda_codec *codec;
+ struct delayed_work unsol_hp_work;
+ int quirk;
+};
+
+static const struct hda_verb cm9825_std_d3_verbs[] = {
+ /* chip sleep verbs */
+ {0x43, CM9825_VERB_SET_D2S, 0x62}, /* depop */
+ {0x43, CM9825_VERB_SET_PLL, 0x01}, /* PLL set */
+ {0x43, CM9825_VERB_SET_NEG, 0xc2}, /* NEG set */
+ {0x43, CM9825_VERB_SET_ADCL, 0x00}, /* ADC */
+ {0x43, CM9825_VERB_SET_DACL, 0x02}, /* DACL */
+ {0x43, CM9825_VERB_SET_VNEG, 0x50}, /* VOL NEG */
+ {0x43, CM9825_VERB_SET_MBIAS, 0x00}, /* MBIAS */
+ {0x43, CM9825_VERB_SET_PDNEG, 0x04}, /* SEL OSC */
+ {0x43, CM9825_VERB_SET_CDALR, 0xf6}, /* Class D */
+ {0x43, CM9825_VERB_SET_OTP, 0xcd}, /* OTP set */
+ {}
+};
+
+static const struct hda_verb cm9825_std_d0_verbs[] = {
+ /* chip init verbs */
+ {0x34, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, /* EAPD set */
+ {0x43, CM9825_VERB_SET_SNR, 0x30}, /* SNR set */
+ {0x43, CM9825_VERB_SET_PLL, 0x00}, /* PLL set */
+ {0x43, CM9825_VERB_SET_ADCL, 0x00}, /* ADC */
+ {0x43, CM9825_VERB_SET_DACL, 0x02}, /* DACL */
+ {0x43, CM9825_VERB_SET_MBIAS, 0x00}, /* MBIAS */
+ {0x43, CM9825_VERB_SET_VNEG, 0x56}, /* VOL NEG */
+ {0x43, CM9825_VERB_SET_D2S, 0x62}, /* depop */
+ {0x43, CM9825_VERB_SET_DACTRL, 0x00}, /* DACTRL set */
+ {0x43, CM9825_VERB_SET_PDNEG, 0x0c}, /* SEL OSC */
+ {0x43, CM9825_VERB_SET_VDO, 0x80}, /* VDO set */
+ {0x43, CM9825_VERB_SET_CDALR, 0xf4}, /* Class D */
+ {0x43, CM9825_VERB_SET_OTP, 0xcd}, /* OTP set */
+ {0x43, CM9825_VERB_SET_MTCBA, 0x61}, /* SR set */
+ {0x43, CM9825_VERB_SET_OCP, 0x33}, /* OTP set */
+ {0x43, CM9825_VERB_SET_GAD, 0x07}, /* ADC -3db */
+ {0x43, CM9825_VERB_SET_TMOD, 0x26}, /* Class D clk */
+ {0x3C, AC_VERB_SET_AMP_GAIN_MUTE |
+ AC_AMP_SET_OUTPUT | AC_AMP_SET_RIGHT, 0x2d}, /* Gain set */
+ {0x3C, AC_VERB_SET_AMP_GAIN_MUTE |
+ AC_AMP_SET_OUTPUT | AC_AMP_SET_LEFT, 0x2d}, /* Gain set */
+ {0x43, CM9825_VERB_SET_HPF_1, 0x40}, /* HPF set */
+ {0x43, CM9825_VERB_SET_HPF_2, 0x40}, /* HPF set */
+ {}
+};
+
+static const struct hda_verb cm9825_hp_present_verbs[] = {
+ {0x42, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00}, /* PIN off */
+ {0x43, CM9825_VERB_SET_ADCL, 0x88}, /* ADC */
+ {0x43, CM9825_VERB_SET_DACL, 0xaa}, /* DACL */
+ {0x43, CM9825_VERB_SET_MBIAS, 0x10}, /* MBIAS */
+ {0x43, CM9825_VERB_SET_D2S, 0xf2}, /* depop */
+ {0x43, CM9825_VERB_SET_DACTRL, 0x00}, /* DACTRL set */
+ {0x43, CM9825_VERB_SET_VDO, 0xc4}, /* VDO set */
+ {}
+};
+
+static const struct hda_verb cm9825_hp_remove_verbs[] = {
+ {0x43, CM9825_VERB_SET_ADCL, 0x00}, /* ADC */
+ {0x43, CM9825_VERB_SET_DACL, 0x56}, /* DACL */
+ {0x43, CM9825_VERB_SET_MBIAS, 0x00}, /* MBIAS */
+ {0x43, CM9825_VERB_SET_D2S, 0x62}, /* depop */
+ {0x43, CM9825_VERB_SET_DACTRL, 0xe0}, /* DACTRL set */
+ {0x43, CM9825_VERB_SET_VDO, 0x80}, /* VDO set */
+ {0x42, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, /* PIN on */
+ {}
+};
+
+static void cm9825_unsol_hp_delayed(struct work_struct *work)
+{
+ struct cmi_spec *spec =
+ container_of(to_delayed_work(work), struct cmi_spec, unsol_hp_work);
+ struct hda_jack_tbl *jack;
+ hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+ bool hp_jack_plugin = false;
+ int err = 0;
+
+ hp_jack_plugin = snd_hda_jack_detect(spec->codec, hp_pin);
+
+ codec_dbg(spec->codec, "hp_jack_plugin %d, hp_pin 0x%X\n",
+ (int)hp_jack_plugin, hp_pin);
+
+ if (!hp_jack_plugin) {
+ err =
+ snd_hda_codec_write(spec->codec, 0x42, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40);
+ if (err)
+ codec_dbg(spec->codec, "codec_write err %d\n", err);
+
+ snd_hda_sequence_write(spec->codec, spec->chip_hp_remove_verbs);
+ } else {
+ snd_hda_sequence_write(spec->codec,
+ spec->chip_hp_present_verbs);
+ }
+
+ jack = snd_hda_jack_tbl_get(spec->codec, hp_pin);
+ if (jack) {
+ jack->block_report = 0;
+ snd_hda_jack_report_sync(spec->codec);
+ }
+}
+
+static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
+{
+ struct cmi_spec *spec = codec->spec;
+ struct hda_jack_tbl *tbl;
+
+ /* Delay enabling the HP amp, to let the mic-detection
+ * state machine run.
+ */
+
+ codec_dbg(spec->codec, "cb->nid 0x%X\n", cb->nid);
+
+ tbl = snd_hda_jack_tbl_get(codec, cb->nid);
+ if (tbl)
+ tbl->block_report = 1;
+ schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(200));
+}
+
+static void cm9825_setup_unsol(struct hda_codec *codec)
+{
+ struct cmi_spec *spec = codec->spec;
+
+ hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+
+ snd_hda_jack_detect_enable_callback(codec, hp_pin, hp_callback);
+}
+
+static int cm9825_init(struct hda_codec *codec)
+{
+ snd_hda_gen_init(codec);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
+
+ return 0;
+}
+
+static void cm9825_remove(struct hda_codec *codec)
+{
+ struct cmi_spec *spec = codec->spec;
+
+ cancel_delayed_work_sync(&spec->unsol_hp_work);
+ snd_hda_gen_remove(codec);
+}
+
+static int cm9825_suspend(struct hda_codec *codec)
+{
+ struct cmi_spec *spec = codec->spec;
+
+ cancel_delayed_work_sync(&spec->unsol_hp_work);
+
+ snd_hda_sequence_write(codec, spec->chip_d3_verbs);
+
+ return 0;
+}
+
+static int cm9825_resume(struct hda_codec *codec)
+{
+ struct cmi_spec *spec = codec->spec;
+ hda_nid_t hp_pin = 0;
+ bool hp_jack_plugin = false;
+ int err;
+
+ err =
+ snd_hda_codec_write(spec->codec, 0x42, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00);
+ if (err)
+ codec_dbg(codec, "codec_write err %d\n", err);
+
+ msleep(150); /* for depop noise */
+
+ snd_hda_codec_init(codec);
+
+ hp_pin = spec->gen.autocfg.hp_pins[0];
+ hp_jack_plugin = snd_hda_jack_detect(spec->codec, hp_pin);
+
+ codec_dbg(spec->codec, "hp_jack_plugin %d, hp_pin 0x%X\n",
+ (int)hp_jack_plugin, hp_pin);
+
+ if (!hp_jack_plugin) {
+ err =
+ snd_hda_codec_write(spec->codec, 0x42, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40);
+
+ if (err)
+ codec_dbg(codec, "codec_write err %d\n", err);
+
+ snd_hda_sequence_write(codec, cm9825_hp_remove_verbs);
+ }
+
+ snd_hda_regmap_sync(codec);
+ hda_call_check_power_status(codec, 0x01);
+
+ return 0;
+}
+
+static int cm9825_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct cmi_spec *spec;
+ struct auto_pin_cfg *cfg;
+ int err;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ INIT_DELAYED_WORK(&spec->unsol_hp_work, cm9825_unsol_hp_delayed);
+ codec->spec = spec;
+ spec->codec = codec;
+ cfg = &spec->gen.autocfg;
+ snd_hda_gen_spec_init(&spec->gen);
+ spec->chip_d0_verbs = cm9825_std_d0_verbs;
+ spec->chip_d3_verbs = cm9825_std_d3_verbs;
+ spec->chip_hp_present_verbs = cm9825_hp_present_verbs;
+ spec->chip_hp_remove_verbs = cm9825_hp_remove_verbs;
+
+ snd_hda_sequence_write(codec, spec->chip_d0_verbs);
+
+ err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
+ if (err < 0)
+ goto error;
+ err = snd_hda_gen_parse_auto_config(codec, cfg);
+ if (err < 0)
+ goto error;
+
+ cm9825_setup_unsol(codec);
+
+ return 0;
+
+ error:
+ cm9825_remove(codec);
+
+ codec_info(codec, "Enter err %d\n", err);
+
+ return err;
+}
+
+static const struct hda_codec_ops cm9825_codec_ops = {
+ .probe = cm9825_probe,
+ .remove = cm9825_remove,
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cm9825_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = cm9825_suspend,
+ .resume = cm9825_resume,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_cm9825[] = {
+ HDA_CODEC_ID(0x13f69825, "CM9825"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cm9825);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("CM9825 HD-audio codec");
+
+static struct hda_codec_driver cm9825_driver = {
+ .id = snd_hda_id_cm9825,
+ .ops = &cm9825_codec_ops,
+};
+
+module_hda_codec_driver(cm9825_driver);
diff --git a/sound/hda/codecs/cmedia.c b/sound/hda/codecs/cmedia.c
new file mode 100644
index 000000000000..15e5a1118a6e
--- /dev/null
+++ b/sound/hda/codecs/cmedia.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Universal codec driver for Intel High Definition Audio Codec
+ *
+ * HD audio codec driver for C-Media CMI9880
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "generic.h"
+
+static int cmedia_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct hda_gen_spec *spec;
+ struct auto_pin_cfg *cfg;
+ bool is_cmi8888 = id->vendor_id == 0x13f68888;
+ int err;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+ cfg = &spec->autocfg;
+ snd_hda_gen_spec_init(spec);
+
+ if (is_cmi8888) {
+ /* mask NID 0x10 from the playback volume selection;
+ * it's a headphone boost volume handled manually below
+ */
+ spec->out_vol_mask = (1ULL << 0x10);
+ }
+
+ err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
+ if (err < 0)
+ goto error;
+ err = snd_hda_gen_parse_auto_config(codec, cfg);
+ if (err < 0)
+ goto error;
+
+ err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
+ if (err < 0)
+ goto error;
+ err = snd_hda_gen_parse_auto_config(codec, cfg);
+ if (err < 0)
+ goto error;
+
+ if (is_cmi8888) {
+ if (get_defcfg_device(snd_hda_codec_get_pincfg(codec, 0x10)) ==
+ AC_JACK_HP_OUT) {
+ static const struct snd_kcontrol_new amp_kctl =
+ HDA_CODEC_VOLUME("Headphone Amp Playback Volume",
+ 0x10, 0, HDA_OUTPUT);
+ if (!snd_hda_gen_add_kctl(spec, NULL, &amp_kctl)) {
+ err = -ENOMEM;
+ goto error;
+ }
+ }
+ }
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops cmedia_codec_ops = {
+ .probe = cmedia_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = snd_hda_gen_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_cmedia[] = {
+ HDA_CODEC_ID(0x13f68888, "CMI8888"),
+ HDA_CODEC_ID(0x13f69880, "CMI9880"),
+ HDA_CODEC_ID(0x434d4980, "CMI9880"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cmedia);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("C-Media HD-audio codec");
+
+static struct hda_codec_driver cmedia_driver = {
+ .id = snd_hda_id_cmedia,
+ .ops = &cmedia_codec_ops,
+};
+
+module_hda_codec_driver(cmedia_driver);
diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c
new file mode 100644
index 000000000000..5fcbc1312c69
--- /dev/null
+++ b/sound/hda/codecs/conexant.c
@@ -0,0 +1,1333 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HD audio codec driver for Conexant HDA audio codec
+ *
+ * Copyright (c) 2006 Pototskiy Akex <alex.pototskiy@gmail.com>
+ * Takashi Iwai <tiwai@suse.de>
+ * Tobin Davis <tdavis@dsl-only.net>
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_beep.h"
+#include "hda_jack.h"
+#include "generic.h"
+
+struct conexant_spec {
+ struct hda_gen_spec gen;
+
+ /* extra EAPD pins */
+ unsigned int num_eapds;
+ hda_nid_t eapds[4];
+ bool dynamic_eapd;
+ hda_nid_t mute_led_eapd;
+
+ unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
+
+ /* OLPC XO specific */
+ bool recording;
+ bool dc_enable;
+ unsigned int dc_input_bias; /* offset into olpc_xo_dc_bias */
+ struct nid_path *dc_mode_path;
+
+ int mute_led_polarity;
+ unsigned int gpio_led;
+ unsigned int gpio_mute_led_mask;
+ unsigned int gpio_mic_led_mask;
+ bool is_cx11880_sn6140;
+};
+
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/* additional beep mixers; private_value will be overwritten */
+static const struct snd_kcontrol_new cxt_beep_mixer[] = {
+ HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
+};
+
+static int set_beep_amp(struct conexant_spec *spec, hda_nid_t nid,
+ int idx, int dir)
+{
+ struct snd_kcontrol_new *knew;
+ unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
+ int i;
+
+ spec->gen.beep_nid = nid;
+ for (i = 0; i < ARRAY_SIZE(cxt_beep_mixer); i++) {
+ knew = snd_hda_gen_add_kctl(&spec->gen, NULL,
+ &cxt_beep_mixer[i]);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = beep_amp;
+ }
+ return 0;
+}
+
+static int cx_auto_parse_beep(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ hda_nid_t nid;
+
+ for_each_hda_codec_node(nid, codec)
+ if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP)
+ return set_beep_amp(spec, nid, 0, HDA_OUTPUT);
+ return 0;
+}
+#else
+#define cx_auto_parse_beep(codec) 0
+#endif
+
+/*
+ * Automatic parser for CX20641 & co
+ */
+
+/* parse EAPDs */
+static void cx_auto_parse_eapd(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ hda_nid_t nid;
+
+ for_each_hda_codec_node(nid, codec) {
+ if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
+ continue;
+ if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD))
+ continue;
+ spec->eapds[spec->num_eapds++] = nid;
+ if (spec->num_eapds >= ARRAY_SIZE(spec->eapds))
+ break;
+ }
+
+ /* NOTE: below is a wild guess; if we have more than two EAPDs,
+ * it's a new chip, where EAPDs are supposed to be associated to
+ * pins, and we can control EAPD per pin.
+ * OTOH, if only one or two EAPDs are found, it's an old chip,
+ * thus it might control over all pins.
+ */
+ if (spec->num_eapds > 2)
+ spec->dynamic_eapd = 1;
+}
+
+static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins,
+ const hda_nid_t *pins, bool on)
+{
+ int i;
+ for (i = 0; i < num_pins; i++) {
+ if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD)
+ snd_hda_codec_write(codec, pins[i], 0,
+ AC_VERB_SET_EAPD_BTLENABLE,
+ on ? 0x02 : 0);
+ }
+}
+
+/* turn on/off EAPD according to Master switch */
+static void cx_auto_vmaster_hook(void *private_data, int enabled)
+{
+ struct hda_codec *codec = private_data;
+ struct conexant_spec *spec = codec->spec;
+
+ cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled);
+}
+
+/* turn on/off EAPD according to Master switch (inversely!) for mute LED */
+static int cx_auto_vmaster_mute_led(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
+ struct conexant_spec *spec = codec->spec;
+
+ snd_hda_codec_write(codec, spec->mute_led_eapd, 0,
+ AC_VERB_SET_EAPD_BTLENABLE,
+ brightness ? 0x02 : 0x00);
+ return 0;
+}
+
+static void cxt_init_gpio_led(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask;
+
+ if (mask) {
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
+ mask);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
+ mask);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_led);
+ }
+}
+
+static void cx_fixup_headset_recog(struct hda_codec *codec)
+{
+ unsigned int mic_present;
+
+ /* fix some headset type recognize fail issue, such as EDIFIER headset */
+ /* set micbias output current comparator threshold from 66% to 55%. */
+ snd_hda_codec_write(codec, 0x1c, 0, 0x320, 0x010);
+ /* set OFF voltage for DFET from -1.2V to -0.8V, set headset micbias register
+ * value adjustment trim from 2.2K ohms to 2.0K ohms.
+ */
+ snd_hda_codec_write(codec, 0x1c, 0, 0x3b0, 0xe10);
+ /* fix reboot headset type recognize fail issue */
+ mic_present = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0x0);
+ if (mic_present & AC_PINSENSE_PRESENCE)
+ /* enable headset mic VREF */
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24);
+ else
+ /* disable headset mic VREF */
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20);
+}
+
+static int cx_init(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ snd_hda_gen_init(codec);
+ if (!spec->dynamic_eapd)
+ cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true);
+
+ cxt_init_gpio_led(codec);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
+
+ if (spec->is_cx11880_sn6140)
+ cx_fixup_headset_recog(codec);
+
+ return 0;
+}
+
+static void cx_auto_shutdown(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+
+ /* Turn the problematic codec into D3 to avoid spurious noises
+ from the internal speaker during (and after) reboot */
+ cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false);
+}
+
+static void cx_remove(struct hda_codec *codec)
+{
+ cx_auto_shutdown(codec);
+ snd_hda_gen_remove(codec);
+}
+
+static void cx_process_headset_plugin(struct hda_codec *codec)
+{
+ unsigned int val;
+ unsigned int count = 0;
+
+ /* Wait headset detect done. */
+ do {
+ val = snd_hda_codec_read(codec, 0x1c, 0, 0xca0, 0x0);
+ if (val & 0x080) {
+ codec_dbg(codec, "headset type detect done!\n");
+ break;
+ }
+ msleep(20);
+ count++;
+ } while (count < 3);
+ val = snd_hda_codec_read(codec, 0x1c, 0, 0xcb0, 0x0);
+ if (val & 0x800) {
+ codec_dbg(codec, "headset plugin, type is CTIA\n");
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24);
+ } else if (val & 0x400) {
+ codec_dbg(codec, "headset plugin, type is OMTP\n");
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24);
+ } else {
+ codec_dbg(codec, "headphone plugin\n");
+ }
+}
+
+static void cx_update_headset_mic_vref(struct hda_codec *codec, struct hda_jack_callback *event)
+{
+ unsigned int mic_present;
+
+ /* In cx11880 and sn6140, the node 16 can only be configured to headphone or disabled,
+ * the node 19 can only be configured to microphone or disabled.
+ * Check hp&mic tag to process headset plugin & plugout.
+ */
+ mic_present = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0x0);
+ if (!(mic_present & AC_PINSENSE_PRESENCE)) /* mic plugout */
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20);
+ else
+ cx_process_headset_plugin(codec);
+}
+
+static int cx_suspend(struct hda_codec *codec)
+{
+ cx_auto_shutdown(codec);
+ return 0;
+}
+
+/*
+ * pin fix-up
+ */
+enum {
+ CXT_PINCFG_LENOVO_X200,
+ CXT_PINCFG_LENOVO_TP410,
+ CXT_PINCFG_LEMOTE_A1004,
+ CXT_PINCFG_LEMOTE_A1205,
+ CXT_PINCFG_COMPAQ_CQ60,
+ CXT_FIXUP_STEREO_DMIC,
+ CXT_PINCFG_LENOVO_NOTEBOOK,
+ CXT_FIXUP_INC_MIC_BOOST,
+ CXT_FIXUP_HEADPHONE_MIC_PIN,
+ CXT_FIXUP_HEADPHONE_MIC,
+ CXT_FIXUP_GPIO1,
+ CXT_FIXUP_ASPIRE_DMIC,
+ CXT_FIXUP_THINKPAD_ACPI,
+ CXT_FIXUP_LENOVO_XPAD_ACPI,
+ CXT_FIXUP_OLPC_XO,
+ CXT_FIXUP_CAP_MIX_AMP,
+ CXT_FIXUP_TOSHIBA_P105,
+ CXT_FIXUP_HP_530,
+ CXT_FIXUP_CAP_MIX_AMP_5047,
+ CXT_FIXUP_MUTE_LED_EAPD,
+ CXT_FIXUP_HP_DOCK,
+ CXT_FIXUP_HP_SPECTRE,
+ CXT_FIXUP_HP_GATE_MIC,
+ CXT_FIXUP_MUTE_LED_GPIO,
+ CXT_FIXUP_HP_ELITEONE_OUT_DIS,
+ CXT_FIXUP_HP_ZBOOK_MUTE_LED,
+ CXT_FIXUP_HEADSET_MIC,
+ CXT_FIXUP_HP_MIC_NO_PRESENCE,
+ CXT_PINCFG_SWS_JS201D,
+ CXT_PINCFG_TOP_SPEAKER,
+ CXT_FIXUP_HP_A_U,
+};
+
+/* for hda_fixup_thinkpad_acpi() */
+#include "helpers/thinkpad.c"
+
+/* for hda_fixup_ideapad_acpi() */
+#include "helpers/ideapad_hotkey_led.c"
+
+static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct conexant_spec *spec = codec->spec;
+ spec->gen.inv_dmic_split = 1;
+}
+
+/* fix widget control pin settings */
+static void cxt_fixup_update_pinctl(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ /* Unset OUT_EN for this Node pin, leaving only HP_EN.
+ * This is the value stored in the codec register after
+ * the correct initialization of the previous windows boot.
+ */
+ snd_hda_set_pin_ctl_cache(codec, 0x1d, AC_PINCTL_HP_EN);
+ }
+}
+
+static void cxt5066_increase_mic_boost(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_override_amp_caps(codec, 0x17, HDA_OUTPUT,
+ (0x3 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x4 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (0 << AC_AMPCAP_MUTE_SHIFT));
+}
+
+static void cxt_update_headset_mode(struct hda_codec *codec)
+{
+ /* The verbs used in this function were tested on a Conexant CX20751/2 codec. */
+ int i;
+ bool mic_mode = false;
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+
+ hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]];
+
+ for (i = 0; i < cfg->num_inputs; i++)
+ if (cfg->inputs[i].pin == mux_pin) {
+ mic_mode = !!cfg->inputs[i].is_headphone_mic;
+ break;
+ }
+
+ if (mic_mode) {
+ snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x7c); /* enable merged mode for analog int-mic */
+ spec->gen.hp_jack_present = false;
+ } else {
+ snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x54); /* disable merged mode for analog int-mic */
+ spec->gen.hp_jack_present = snd_hda_jack_detect(codec, spec->gen.autocfg.hp_pins[0]);
+ }
+
+ snd_hda_gen_update_outputs(codec);
+}
+
+static void cxt_update_headset_mode_hook(struct hda_codec *codec,
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ cxt_update_headset_mode(codec);
+}
+
+static void cxt_fixup_headphone_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct conexant_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->parse_flags |= HDA_PINCFG_HEADPHONE_MIC;
+ snd_hdac_regmap_add_vendor_verb(&codec->core, 0x410);
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ WARN_ON(spec->gen.cap_sync_hook);
+ spec->gen.cap_sync_hook = cxt_update_headset_mode_hook;
+ spec->gen.automute_hook = cxt_update_headset_mode;
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ cxt_update_headset_mode(codec);
+ break;
+ }
+}
+
+static void cxt_fixup_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct conexant_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ break;
+ }
+}
+
+/* OLPC XO 1.5 fixup */
+
+/* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors)
+ * through the microphone jack.
+ * When the user enables this through a mixer switch, both internal and
+ * external microphones are disabled. Gain is fixed at 0dB. In this mode,
+ * we also allow the bias to be configured through a separate mixer
+ * control. */
+
+#define update_mic_pin(codec, nid, val) \
+ snd_hda_codec_write_cache(codec, nid, 0, \
+ AC_VERB_SET_PIN_WIDGET_CONTROL, val)
+
+static const struct hda_input_mux olpc_xo_dc_bias = {
+ .num_items = 3,
+ .items = {
+ { "Off", PIN_IN },
+ { "50%", PIN_VREF50 },
+ { "80%", PIN_VREF80 },
+ },
+};
+
+static void olpc_xo_update_mic_boost(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ int ch, val;
+
+ for (ch = 0; ch < 2; ch++) {
+ val = AC_AMP_SET_OUTPUT |
+ (ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT);
+ if (!spec->dc_enable)
+ val |= snd_hda_codec_amp_read(codec, 0x17, ch, HDA_OUTPUT, 0);
+ snd_hda_codec_write(codec, 0x17, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, val);
+ }
+}
+
+static void olpc_xo_update_mic_pins(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ int cur_input, val;
+ struct nid_path *path;
+
+ cur_input = spec->gen.input_paths[0][spec->gen.cur_mux[0]];
+
+ /* Set up mic pins for port-B, C and F dynamically as the recording
+ * LED is turned on/off by these pin controls
+ */
+ if (!spec->dc_enable) {
+ /* disable DC bias path and pin for port F */
+ update_mic_pin(codec, 0x1e, 0);
+ snd_hda_activate_path(codec, spec->dc_mode_path, false, false);
+
+ /* update port B (ext mic) and C (int mic) */
+ /* OLPC defers mic widget control until when capture is
+ * started because the microphone LED comes on as soon as
+ * these settings are put in place. if we did this before
+ * recording, it would give the false indication that
+ * recording is happening when it is not.
+ */
+ update_mic_pin(codec, 0x1a, spec->recording ?
+ snd_hda_codec_get_pin_target(codec, 0x1a) : 0);
+ update_mic_pin(codec, 0x1b, spec->recording ?
+ snd_hda_codec_get_pin_target(codec, 0x1b) : 0);
+ /* enable normal mic path */
+ path = snd_hda_get_path_from_idx(codec, cur_input);
+ if (path)
+ snd_hda_activate_path(codec, path, true, false);
+ } else {
+ /* disable normal mic path */
+ path = snd_hda_get_path_from_idx(codec, cur_input);
+ if (path)
+ snd_hda_activate_path(codec, path, false, false);
+
+ /* Even though port F is the DC input, the bias is controlled
+ * on port B. We also leave that port as an active input (but
+ * unselected) in DC mode just in case that is necessary to
+ * make the bias setting take effect.
+ */
+ if (spec->recording)
+ val = olpc_xo_dc_bias.items[spec->dc_input_bias].index;
+ else
+ val = 0;
+ update_mic_pin(codec, 0x1a, val);
+ update_mic_pin(codec, 0x1b, 0);
+ /* enable DC bias path and pin */
+ update_mic_pin(codec, 0x1e, spec->recording ? PIN_IN : 0);
+ snd_hda_activate_path(codec, spec->dc_mode_path, true, false);
+ }
+}
+
+/* mic_autoswitch hook */
+static void olpc_xo_automic(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct conexant_spec *spec = codec->spec;
+
+ /* in DC mode, we don't handle automic */
+ if (!spec->dc_enable)
+ snd_hda_gen_mic_autoswitch(codec, jack);
+ olpc_xo_update_mic_pins(codec);
+ if (spec->dc_enable)
+ olpc_xo_update_mic_boost(codec);
+}
+
+/* pcm_capture hook */
+static void olpc_xo_capture_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct conexant_spec *spec = codec->spec;
+
+ /* toggle spec->recording flag and update mic pins accordingly
+ * for turning on/off LED
+ */
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ spec->recording = 1;
+ olpc_xo_update_mic_pins(codec);
+ break;
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ spec->recording = 0;
+ olpc_xo_update_mic_pins(codec);
+ break;
+ }
+}
+
+static int olpc_xo_dc_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ ucontrol->value.integer.value[0] = spec->dc_enable;
+ return 0;
+}
+
+static int olpc_xo_dc_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ int dc_enable = !!ucontrol->value.integer.value[0];
+
+ if (dc_enable == spec->dc_enable)
+ return 0;
+
+ spec->dc_enable = dc_enable;
+ olpc_xo_update_mic_pins(codec);
+ olpc_xo_update_mic_boost(codec);
+ return 1;
+}
+
+static int olpc_xo_dc_bias_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = spec->dc_input_bias;
+ return 0;
+}
+
+static int olpc_xo_dc_bias_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ return snd_hda_input_mux_info(&olpc_xo_dc_bias, uinfo);
+}
+
+static int olpc_xo_dc_bias_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ const struct hda_input_mux *imux = &olpc_xo_dc_bias;
+ unsigned int idx;
+
+ idx = ucontrol->value.enumerated.item[0];
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+ if (spec->dc_input_bias == idx)
+ return 0;
+
+ spec->dc_input_bias = idx;
+ if (spec->dc_enable)
+ olpc_xo_update_mic_pins(codec);
+ return 1;
+}
+
+static const struct snd_kcontrol_new olpc_xo_mixers[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DC Mode Enable Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = olpc_xo_dc_mode_get,
+ .put = olpc_xo_dc_mode_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DC Input Bias Enum",
+ .info = olpc_xo_dc_bias_enum_info,
+ .get = olpc_xo_dc_bias_enum_get,
+ .put = olpc_xo_dc_bias_enum_put,
+ },
+ {}
+};
+
+/* overriding mic boost put callback; update mic boost volume only when
+ * DC mode is disabled
+ */
+static int olpc_xo_mic_boost_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ int ret = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
+ if (ret > 0 && spec->dc_enable)
+ olpc_xo_update_mic_boost(codec);
+ return ret;
+}
+
+static void cxt_fixup_olpc_xo(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct snd_kcontrol_new *kctl;
+ int i;
+
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
+
+ spec->gen.mic_autoswitch_hook = olpc_xo_automic;
+ spec->gen.pcm_capture_hook = olpc_xo_capture_hook;
+ spec->dc_mode_path = snd_hda_add_new_path(codec, 0x1e, 0x14, 0);
+
+ snd_hda_add_new_ctls(codec, olpc_xo_mixers);
+
+ /* OLPC's microphone port is DC coupled for use with external sensors,
+ * therefore we use a 50% mic bias in order to center the input signal
+ * with the DC input range of the codec.
+ */
+ snd_hda_codec_set_pin_target(codec, 0x1a, PIN_VREF50);
+
+ /* override mic boost control */
+ snd_array_for_each(&spec->gen.kctls, i, kctl) {
+ if (!strcmp(kctl->name, "Mic Boost Volume")) {
+ kctl->put = olpc_xo_mic_boost_put;
+ break;
+ }
+ }
+}
+
+static void cxt_fixup_mute_led_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct conexant_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_eapd = 0x1b;
+ spec->dynamic_eapd = true;
+ snd_hda_gen_add_mute_led_cdev(codec, cx_auto_vmaster_mute_led);
+ }
+}
+
+/*
+ * Fix max input level on mixer widget to 0dB
+ * (originally it has 0x2b steps with 0dB offset 0x14)
+ */
+static void cxt_fixup_cap_mix_amp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
+ (0x14 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (1 << AC_AMPCAP_MUTE_SHIFT));
+}
+
+/*
+ * Fix max input level on mixer widget to 0dB
+ * (originally it has 0x1e steps with 0 dB offset 0x17)
+ */
+static void cxt_fixup_cap_mix_amp_5047(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ snd_hda_override_amp_caps(codec, 0x10, HDA_INPUT,
+ (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (1 << AC_AMPCAP_MUTE_SHIFT));
+}
+
+static void cxt_fixup_hp_gate_mic_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ /* the mic pin (0x19) doesn't give an unsolicited event;
+ * probe the mic pin together with the headphone pin (0x16)
+ */
+ if (action == HDA_FIXUP_ACT_PROBE)
+ snd_hda_jack_set_gating_jack(codec, 0x19, 0x16);
+}
+
+/* update LED status via GPIO */
+static void cxt_update_gpio_led(struct hda_codec *codec, unsigned int mask,
+ bool led_on)
+{
+ struct conexant_spec *spec = codec->spec;
+ unsigned int oldval = spec->gpio_led;
+
+ if (spec->mute_led_polarity)
+ led_on = !led_on;
+
+ if (led_on)
+ spec->gpio_led |= mask;
+ else
+ spec->gpio_led &= ~mask;
+ codec_dbg(codec, "mask:%d enabled:%d gpio_led:%d\n",
+ mask, led_on, spec->gpio_led);
+ if (spec->gpio_led != oldval)
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_led);
+}
+
+/* turn on/off mute LED via GPIO per vmaster hook */
+static int cxt_gpio_mute_update(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
+ struct conexant_spec *spec = codec->spec;
+
+ cxt_update_gpio_led(codec, spec->gpio_mute_led_mask, brightness);
+ return 0;
+}
+
+/* turn on/off mic-mute LED via GPIO per capture hook */
+static int cxt_gpio_micmute_update(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
+ struct conexant_spec *spec = codec->spec;
+
+ cxt_update_gpio_led(codec, spec->gpio_mic_led_mask, brightness);
+ return 0;
+}
+
+static void cxt_setup_mute_led(struct hda_codec *codec,
+ unsigned int mute, unsigned int mic_mute)
+{
+ struct conexant_spec *spec = codec->spec;
+
+ spec->gpio_led = 0;
+ spec->mute_led_polarity = 0;
+ if (mute) {
+ snd_hda_gen_add_mute_led_cdev(codec, cxt_gpio_mute_update);
+ spec->gpio_mute_led_mask = mute;
+ }
+ if (mic_mute) {
+ snd_hda_gen_add_micmute_led_cdev(codec, cxt_gpio_micmute_update);
+ spec->gpio_mic_led_mask = mic_mute;
+ }
+}
+
+static void cxt_setup_gpio_unmute(struct hda_codec *codec,
+ unsigned int gpio_mute_mask)
+{
+ if (gpio_mute_mask) {
+ // set gpio data to 0.
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, gpio_mute_mask);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION, gpio_mute_mask);
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_STICKY_MASK, 0);
+ }
+}
+
+static void cxt_fixup_mute_led_gpio(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ cxt_setup_mute_led(codec, 0x01, 0x02);
+}
+
+static void cxt_fixup_hp_zbook_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ cxt_setup_mute_led(codec, 0x10, 0x20);
+}
+
+static void cxt_fixup_hp_a_u(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ // Init vers in BIOS mute the spk/hp by set gpio high to avoid pop noise,
+ // so need to unmute once by clearing the gpio data when runs into the system.
+ if (action == HDA_FIXUP_ACT_INIT)
+ cxt_setup_gpio_unmute(codec, 0x2);
+}
+
+/* ThinkPad X200 & co with cxt5051 */
+static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
+ { 0x16, 0x042140ff }, /* HP (seq# overridden) */
+ { 0x17, 0x21a11000 }, /* dock-mic */
+ { 0x19, 0x2121103f }, /* dock-HP */
+ { 0x1c, 0x21440100 }, /* dock SPDIF out */
+ {}
+};
+
+/* ThinkPad 410/420/510/520, X201 & co with cxt5066 */
+static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = {
+ { 0x19, 0x042110ff }, /* HP (seq# overridden) */
+ { 0x1a, 0x21a190f0 }, /* dock-mic */
+ { 0x1c, 0x212140ff }, /* dock-HP */
+ {}
+};
+
+/* Lemote A1004/A1205 with cxt5066 */
+static const struct hda_pintbl cxt_pincfg_lemote[] = {
+ { 0x1a, 0x90a10020 }, /* Internal mic */
+ { 0x1b, 0x03a11020 }, /* External mic */
+ { 0x1d, 0x400101f0 }, /* Not used */
+ { 0x1e, 0x40a701f0 }, /* Not used */
+ { 0x20, 0x404501f0 }, /* Not used */
+ { 0x22, 0x404401f0 }, /* Not used */
+ { 0x23, 0x40a701f0 }, /* Not used */
+ {}
+};
+
+/* SuoWoSi/South-holding JS201D with sn6140 */
+static const struct hda_pintbl cxt_pincfg_sws_js201d[] = {
+ { 0x16, 0x03211040 }, /* hp out */
+ { 0x17, 0x91170110 }, /* SPK/Class_D */
+ { 0x18, 0x95a70130 }, /* Internal mic */
+ { 0x19, 0x03a11020 }, /* Headset Mic */
+ { 0x1a, 0x40f001f0 }, /* Not used */
+ { 0x21, 0x40f001f0 }, /* Not used */
+ {}
+};
+
+static const struct hda_fixup cxt_fixups[] = {
+ [CXT_PINCFG_LENOVO_X200] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cxt_pincfg_lenovo_x200,
+ },
+ [CXT_PINCFG_LENOVO_TP410] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cxt_pincfg_lenovo_tp410,
+ .chained = true,
+ .chain_id = CXT_FIXUP_THINKPAD_ACPI,
+ },
+ [CXT_PINCFG_LEMOTE_A1004] = {
+ .type = HDA_FIXUP_PINS,
+ .chained = true,
+ .chain_id = CXT_FIXUP_INC_MIC_BOOST,
+ .v.pins = cxt_pincfg_lemote,
+ },
+ [CXT_PINCFG_LEMOTE_A1205] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cxt_pincfg_lemote,
+ },
+ [CXT_PINCFG_COMPAQ_CQ60] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* 0x17 was falsely set up as a mic, it should 0x1d */
+ { 0x17, 0x400001f0 },
+ { 0x1d, 0x97a70120 },
+ { }
+ }
+ },
+ [CXT_FIXUP_STEREO_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_stereo_dmic,
+ },
+ [CXT_PINCFG_LENOVO_NOTEBOOK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x05d71030 },
+ { }
+ },
+ .chain_id = CXT_FIXUP_STEREO_DMIC,
+ },
+ [CXT_FIXUP_INC_MIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt5066_increase_mic_boost,
+ },
+ [CXT_FIXUP_HEADPHONE_MIC_PIN] = {
+ .type = HDA_FIXUP_PINS,
+ .chained = true,
+ .chain_id = CXT_FIXUP_HEADPHONE_MIC,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ }
+ },
+ [CXT_FIXUP_HEADPHONE_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_headphone_mic,
+ },
+ [CXT_FIXUP_GPIO1] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x01 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01 },
+ { 0x01, AC_VERB_SET_GPIO_DATA, 0x01 },
+ { }
+ },
+ },
+ [CXT_FIXUP_ASPIRE_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_stereo_dmic,
+ .chained = true,
+ .chain_id = CXT_FIXUP_GPIO1,
+ },
+ [CXT_FIXUP_THINKPAD_ACPI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = hda_fixup_thinkpad_acpi,
+ },
+ [CXT_FIXUP_LENOVO_XPAD_ACPI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = hda_fixup_ideapad_acpi,
+ .chained = true,
+ .chain_id = CXT_FIXUP_THINKPAD_ACPI,
+ },
+ [CXT_FIXUP_OLPC_XO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_olpc_xo,
+ },
+ [CXT_FIXUP_CAP_MIX_AMP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_cap_mix_amp,
+ },
+ [CXT_FIXUP_TOSHIBA_P105] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x10, 0x961701f0 }, /* speaker/hp */
+ { 0x12, 0x02a1901e }, /* ext mic */
+ { 0x14, 0x95a70110 }, /* int mic */
+ {}
+ },
+ },
+ [CXT_FIXUP_HP_530] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x90a60160 }, /* int mic */
+ {}
+ },
+ .chained = true,
+ .chain_id = CXT_FIXUP_CAP_MIX_AMP,
+ },
+ [CXT_FIXUP_CAP_MIX_AMP_5047] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_cap_mix_amp_5047,
+ },
+ [CXT_FIXUP_MUTE_LED_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_mute_led_eapd,
+ },
+ [CXT_FIXUP_HP_DOCK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x21011020 }, /* line-out */
+ { 0x18, 0x2181103f }, /* line-in */
+ { }
+ },
+ .chained = true,
+ .chain_id = CXT_FIXUP_MUTE_LED_GPIO,
+ },
+ [CXT_FIXUP_HP_SPECTRE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* enable NID 0x1d for the speaker on top */
+ { 0x1d, 0x91170111 },
+ { }
+ }
+ },
+ [CXT_FIXUP_HP_GATE_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_hp_gate_mic_jack,
+ },
+ [CXT_FIXUP_MUTE_LED_GPIO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_mute_led_gpio,
+ },
+ [CXT_FIXUP_HP_ELITEONE_OUT_DIS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_update_pinctl,
+ },
+ [CXT_FIXUP_HP_ZBOOK_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_hp_zbook_mute_led,
+ },
+ [CXT_FIXUP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_headset_mic,
+ },
+ [CXT_FIXUP_HP_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x02a1113c },
+ { }
+ },
+ .chained = true,
+ .chain_id = CXT_FIXUP_HEADSET_MIC,
+ },
+ [CXT_PINCFG_SWS_JS201D] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cxt_pincfg_sws_js201d,
+ },
+ [CXT_PINCFG_TOP_SPEAKER] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1d, 0x82170111 },
+ { }
+ },
+ },
+ [CXT_FIXUP_HP_A_U] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_hp_a_u,
+ },
+};
+
+static const struct hda_quirk cxt5045_fixups[] = {
+ SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT_FIXUP_HP_530),
+ SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT_FIXUP_TOSHIBA_P105),
+ /* HP, Packard Bell, Fujitsu-Siemens & Lenovo laptops have
+ * really bad sound over 0dB on NID 0x17.
+ */
+ SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP),
+ SND_PCI_QUIRK_VENDOR(0x1631, "Packard Bell", CXT_FIXUP_CAP_MIX_AMP),
+ SND_PCI_QUIRK_VENDOR(0x1734, "Fujitsu", CXT_FIXUP_CAP_MIX_AMP),
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT_FIXUP_CAP_MIX_AMP),
+ {}
+};
+
+static const struct hda_model_fixup cxt5045_fixup_models[] = {
+ { .id = CXT_FIXUP_CAP_MIX_AMP, .name = "cap-mix-amp" },
+ { .id = CXT_FIXUP_TOSHIBA_P105, .name = "toshiba-p105" },
+ { .id = CXT_FIXUP_HP_530, .name = "hp-530" },
+ {}
+};
+
+static const struct hda_quirk cxt5047_fixups[] = {
+ /* HP laptops have really bad sound over 0 dB on NID 0x10.
+ */
+ SND_PCI_QUIRK_VENDOR(0x103c, "HP", CXT_FIXUP_CAP_MIX_AMP_5047),
+ {}
+};
+
+static const struct hda_model_fixup cxt5047_fixup_models[] = {
+ { .id = CXT_FIXUP_CAP_MIX_AMP_5047, .name = "cap-mix-amp" },
+ {}
+};
+
+static const struct hda_quirk cxt5051_fixups[] = {
+ SND_PCI_QUIRK(0x103c, 0x360b, "Compaq CQ60", CXT_PINCFG_COMPAQ_CQ60),
+ SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200),
+ {}
+};
+
+static const struct hda_model_fixup cxt5051_fixup_models[] = {
+ { .id = CXT_PINCFG_LENOVO_X200, .name = "lenovo-x200" },
+ {}
+};
+
+static const struct hda_quirk cxt5066_fixups[] = {
+ SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_ASPIRE_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x054f, "Acer Aspire 4830T", CXT_FIXUP_ASPIRE_DMIC),
+ SND_PCI_QUIRK(0x103c, 0x8079, "HP EliteBook 840 G3", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x807C, "HP EliteBook 820 G3", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x80FD, "HP ProBook 640 G2", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x8115, "HP Z1 Gen3", CXT_FIXUP_HP_GATE_MIC),
+ SND_PCI_QUIRK(0x103c, 0x814f, "HP ZBook 15u G3", CXT_FIXUP_MUTE_LED_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE),
+ SND_PCI_QUIRK(0x103c, 0x822e, "HP ProBook 440 G4", CXT_FIXUP_MUTE_LED_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x8231, "HP ProBook 450 G4", CXT_FIXUP_MUTE_LED_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x828c, "HP EliteBook 840 G4", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x8299, "HP 800 G3 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x829a, "HP 800 G3 DM", CXT_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x82b4, "HP ProDesk 600 G3", CXT_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x836e, "HP ProBook 455 G5", CXT_FIXUP_MUTE_LED_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x837f, "HP ProBook 470 G5", CXT_FIXUP_MUTE_LED_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x83b2, "HP EliteBook 840 G5", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x83b3, "HP EliteBook 830 G5", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x83d3, "HP ProBook 640 G4", CXT_FIXUP_HP_DOCK),
+ SND_PCI_QUIRK(0x103c, 0x83e5, "HP EliteOne 1000 G2", CXT_FIXUP_HP_ELITEONE_OUT_DIS),
+ SND_PCI_QUIRK(0x103c, 0x8402, "HP ProBook 645 G4", CXT_FIXUP_MUTE_LED_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x8427, "HP ZBook Studio G5", CXT_FIXUP_HP_ZBOOK_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x844f, "HP ZBook Studio G5", CXT_FIXUP_HP_ZBOOK_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8455, "HP Z2 G4", CXT_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x8456, "HP Z2 G4 SFF", CXT_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x8457, "HP Z2 G4 mini", CXT_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x8458, "HP Z2 G4 mini premium", CXT_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
+ SND_PCI_QUIRK(0x14f1, 0x0252, "MBX-Z60MR100", CXT_FIXUP_HP_A_U),
+ SND_PCI_QUIRK(0x14f1, 0x0265, "SWS JS201D", CXT_PINCFG_SWS_JS201D),
+ SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO),
+ SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x21ce, "Lenovo T420", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x21d2, "Lenovo T420s", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo IdeaPad Z560", CXT_FIXUP_MUTE_LED_EAPD),
+ SND_PCI_QUIRK(0x17aa, 0x3905, "Lenovo G50-30", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x17aa, 0x390b, "Lenovo G50-80", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
+ /* NOTE: we'd need to extend the quirk for 17aa:3977 as the same
+ * PCI SSID is used on multiple Lenovo models
+ */
+ SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo G50-70", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad/Ideapad", CXT_FIXUP_LENOVO_XPAD_ACPI),
+ SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004),
+ SND_PCI_QUIRK(0x1c06, 0x2012, "Lemote A1205", CXT_PINCFG_LEMOTE_A1205),
+ HDA_CODEC_QUIRK(0x2782, 0x12c3, "Sirius Gen1", CXT_PINCFG_TOP_SPEAKER),
+ HDA_CODEC_QUIRK(0x2782, 0x12c5, "Sirius Gen2", CXT_PINCFG_TOP_SPEAKER),
+ {}
+};
+
+static const struct hda_model_fixup cxt5066_fixup_models[] = {
+ { .id = CXT_FIXUP_STEREO_DMIC, .name = "stereo-dmic" },
+ { .id = CXT_FIXUP_GPIO1, .name = "gpio1" },
+ { .id = CXT_FIXUP_HEADPHONE_MIC_PIN, .name = "headphone-mic-pin" },
+ { .id = CXT_PINCFG_LENOVO_TP410, .name = "tp410" },
+ { .id = CXT_FIXUP_THINKPAD_ACPI, .name = "thinkpad" },
+ { .id = CXT_FIXUP_LENOVO_XPAD_ACPI, .name = "thinkpad-ideapad" },
+ { .id = CXT_PINCFG_LEMOTE_A1004, .name = "lemote-a1004" },
+ { .id = CXT_PINCFG_LEMOTE_A1205, .name = "lemote-a1205" },
+ { .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" },
+ { .id = CXT_FIXUP_MUTE_LED_EAPD, .name = "mute-led-eapd" },
+ { .id = CXT_FIXUP_HP_DOCK, .name = "hp-dock" },
+ { .id = CXT_FIXUP_MUTE_LED_GPIO, .name = "mute-led-gpio" },
+ { .id = CXT_FIXUP_HP_ZBOOK_MUTE_LED, .name = "hp-zbook-mute-led" },
+ { .id = CXT_FIXUP_HP_MIC_NO_PRESENCE, .name = "hp-mic-fix" },
+ { .id = CXT_PINCFG_LENOVO_NOTEBOOK, .name = "lenovo-20149" },
+ { .id = CXT_PINCFG_SWS_JS201D, .name = "sws-js201d" },
+ { .id = CXT_PINCFG_TOP_SPEAKER, .name = "sirius-top-speaker" },
+ { .id = CXT_FIXUP_HP_A_U, .name = "HP-U-support" },
+ {}
+};
+
+/* add "fake" mute amp-caps to DACs on cx5051 so that mixer mute switches
+ * can be created (bko#42825)
+ */
+static void add_cx5051_fake_mutes(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ static const hda_nid_t out_nids[] = {
+ 0x10, 0x11, 0
+ };
+ const hda_nid_t *p;
+
+ for (p = out_nids; *p; p++)
+ snd_hda_override_amp_caps(codec, *p, HDA_OUTPUT,
+ AC_AMPCAP_MIN_MUTE |
+ query_amp_caps(codec, *p, HDA_OUTPUT));
+ spec->gen.dac_min_mute = true;
+}
+
+static int cx_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct conexant_spec *spec;
+ int err;
+
+ codec_info(codec, "%s: BIOS auto-probing.\n", codec->core.chip_name);
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ snd_hda_gen_spec_init(&spec->gen);
+ codec->spec = spec;
+
+ /* init cx11880/sn6140 flag and reset headset_present_flag */
+ switch (codec->core.vendor_id) {
+ case 0x14f11f86:
+ case 0x14f11f87:
+ spec->is_cx11880_sn6140 = true;
+ snd_hda_jack_detect_enable_callback(codec, 0x19, cx_update_headset_mic_vref);
+ break;
+ }
+
+ cx_auto_parse_eapd(codec);
+ spec->gen.own_eapd_ctl = 1;
+
+ switch (codec->core.vendor_id) {
+ case 0x14f15045:
+ codec->single_adc_amp = 1;
+ spec->gen.mixer_nid = 0x17;
+ spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
+ snd_hda_pick_fixup(codec, cxt5045_fixup_models,
+ cxt5045_fixups, cxt_fixups);
+ break;
+ case 0x14f15047:
+ codec->pin_amp_workaround = 1;
+ spec->gen.mixer_nid = 0x19;
+ spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
+ snd_hda_pick_fixup(codec, cxt5047_fixup_models,
+ cxt5047_fixups, cxt_fixups);
+ break;
+ case 0x14f15051:
+ add_cx5051_fake_mutes(codec);
+ codec->pin_amp_workaround = 1;
+ snd_hda_pick_fixup(codec, cxt5051_fixup_models,
+ cxt5051_fixups, cxt_fixups);
+ break;
+ case 0x14f15098:
+ codec->pin_amp_workaround = 1;
+ spec->gen.mixer_nid = 0x22;
+ spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
+ snd_hda_pick_fixup(codec, cxt5066_fixup_models,
+ cxt5066_fixups, cxt_fixups);
+ break;
+ case 0x14f150f2:
+ codec->power_save_node = 1;
+ fallthrough;
+ default:
+ codec->pin_amp_workaround = 1;
+ snd_hda_pick_fixup(codec, cxt5066_fixup_models,
+ cxt5066_fixups, cxt_fixups);
+ break;
+ }
+
+ if (!spec->gen.vmaster_mute.hook && spec->dynamic_eapd)
+ spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL,
+ spec->parse_flags);
+ if (err < 0)
+ goto error;
+
+ err = cx_auto_parse_beep(codec);
+ if (err < 0)
+ goto error;
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
+ if (err < 0)
+ goto error;
+
+ /* Some laptops with Conexant chips show stalls in S3 resume,
+ * which falls into the single-cmd mode.
+ * Better to make reset, then.
+ */
+ if (!codec->bus->core.sync_write) {
+ codec_info(codec,
+ "Enable sync_write for stable communication\n");
+ codec->bus->core.sync_write = 1;
+ codec->bus->allow_bus_reset = 1;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ cx_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops cx_codec_ops = {
+ .probe = cx_probe,
+ .remove = cx_remove,
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = cx_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = cx_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ */
+
+static const struct hda_device_id snd_hda_id_conexant[] = {
+ HDA_CODEC_ID(0x14f11f86, "CX11880"),
+ HDA_CODEC_ID(0x14f11f87, "SN6140"),
+ HDA_CODEC_ID(0x14f12008, "CX8200"),
+ HDA_CODEC_ID(0x14f120d0, "CX11970"),
+ HDA_CODEC_ID(0x14f120d1, "SN6180"),
+ HDA_CODEC_ID(0x14f15045, "CX20549 (Venice)"),
+ HDA_CODEC_ID(0x14f15047, "CX20551 (Waikiki)"),
+ HDA_CODEC_ID(0x14f15051, "CX20561 (Hermosa)"),
+ HDA_CODEC_ID(0x14f15066, "CX20582 (Pebble)"),
+ HDA_CODEC_ID(0x14f15067, "CX20583 (Pebble HSF)"),
+ HDA_CODEC_ID(0x14f15068, "CX20584"),
+ HDA_CODEC_ID(0x14f15069, "CX20585"),
+ HDA_CODEC_ID(0x14f1506c, "CX20588"),
+ HDA_CODEC_ID(0x14f1506e, "CX20590"),
+ HDA_CODEC_ID(0x14f15097, "CX20631"),
+ HDA_CODEC_ID(0x14f15098, "CX20632"),
+ HDA_CODEC_ID(0x14f150a1, "CX20641"),
+ HDA_CODEC_ID(0x14f150a2, "CX20642"),
+ HDA_CODEC_ID(0x14f150ab, "CX20651"),
+ HDA_CODEC_ID(0x14f150ac, "CX20652"),
+ HDA_CODEC_ID(0x14f150b8, "CX20664"),
+ HDA_CODEC_ID(0x14f150b9, "CX20665"),
+ HDA_CODEC_ID(0x14f150f1, "CX21722"),
+ HDA_CODEC_ID(0x14f150f2, "CX20722"),
+ HDA_CODEC_ID(0x14f150f3, "CX21724"),
+ HDA_CODEC_ID(0x14f150f4, "CX20724"),
+ HDA_CODEC_ID(0x14f1510f, "CX20751/2"),
+ HDA_CODEC_ID(0x14f15110, "CX20751/2"),
+ HDA_CODEC_ID(0x14f15111, "CX20753/4"),
+ HDA_CODEC_ID(0x14f15113, "CX20755"),
+ HDA_CODEC_ID(0x14f15114, "CX20756"),
+ HDA_CODEC_ID(0x14f15115, "CX20757"),
+ HDA_CODEC_ID(0x14f151d7, "CX20952"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_conexant);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Conexant HD-audio codec");
+
+static struct hda_codec_driver conexant_driver = {
+ .id = snd_hda_id_conexant,
+ .ops = &cx_codec_ops,
+};
+
+module_hda_codec_driver(conexant_driver);
diff --git a/sound/hda/codecs/generic.c b/sound/hda/codecs/generic.c
new file mode 100644
index 000000000000..7bcf9aef8275
--- /dev/null
+++ b/sound/hda/codecs/generic.c
@@ -0,0 +1,6145 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * Generic widget tree parser
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/sort.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/leds.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/tlv.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "hda_beep.h"
+#include "generic.h"
+
+
+/**
+ * snd_hda_gen_spec_init - initialize hda_gen_spec struct
+ * @spec: hda_gen_spec object to initialize
+ *
+ * Initialize the given hda_gen_spec object.
+ */
+int snd_hda_gen_spec_init(struct hda_gen_spec *spec)
+{
+ snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
+ snd_array_init(&spec->paths, sizeof(struct nid_path), 8);
+ snd_array_init(&spec->loopback_list, sizeof(struct hda_amp_list), 8);
+ mutex_init(&spec->pcm_mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_spec_init);
+
+/**
+ * snd_hda_gen_add_kctl - Add a new kctl_new struct from the template
+ * @spec: hda_gen_spec object
+ * @name: name string to override the template, NULL if unchanged
+ * @temp: template for the new kctl
+ *
+ * Add a new kctl (actually snd_kcontrol_new to be instantiated later)
+ * element based on the given snd_kcontrol_new template @temp and the
+ * name string @name to the list in @spec.
+ * Returns the newly created object or NULL as error.
+ */
+struct snd_kcontrol_new *
+snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name,
+ const struct snd_kcontrol_new *temp)
+{
+ struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls);
+ if (!knew)
+ return NULL;
+ *knew = *temp;
+ if (name)
+ knew->name = kstrdup(name, GFP_KERNEL);
+ else if (knew->name)
+ knew->name = kstrdup(knew->name, GFP_KERNEL);
+ if (!knew->name)
+ return NULL;
+ return knew;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_add_kctl);
+
+static void free_kctls(struct hda_gen_spec *spec)
+{
+ if (spec->kctls.list) {
+ struct snd_kcontrol_new *kctl = spec->kctls.list;
+ int i;
+ for (i = 0; i < spec->kctls.used; i++)
+ kfree(kctl[i].name);
+ }
+ snd_array_free(&spec->kctls);
+}
+
+static void snd_hda_gen_spec_free(struct hda_gen_spec *spec)
+{
+ if (!spec)
+ return;
+ free_kctls(spec);
+ snd_array_free(&spec->paths);
+ snd_array_free(&spec->loopback_list);
+#ifdef CONFIG_SND_HDA_GENERIC_LEDS
+ if (spec->led_cdevs[LED_AUDIO_MUTE])
+ led_classdev_unregister(spec->led_cdevs[LED_AUDIO_MUTE]);
+ if (spec->led_cdevs[LED_AUDIO_MICMUTE])
+ led_classdev_unregister(spec->led_cdevs[LED_AUDIO_MICMUTE]);
+#endif
+}
+
+/*
+ * store user hints
+ */
+static void parse_user_hints(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int val;
+
+ val = snd_hda_get_bool_hint(codec, "jack_detect");
+ if (val >= 0)
+ codec->no_jack_detect = !val;
+ val = snd_hda_get_bool_hint(codec, "inv_jack_detect");
+ if (val >= 0)
+ codec->inv_jack_detect = !!val;
+ val = snd_hda_get_bool_hint(codec, "trigger_sense");
+ if (val >= 0)
+ codec->no_trigger_sense = !val;
+ val = snd_hda_get_bool_hint(codec, "inv_eapd");
+ if (val >= 0)
+ codec->inv_eapd = !!val;
+ val = snd_hda_get_bool_hint(codec, "pcm_format_first");
+ if (val >= 0)
+ codec->pcm_format_first = !!val;
+ val = snd_hda_get_bool_hint(codec, "sticky_stream");
+ if (val >= 0)
+ codec->no_sticky_stream = !val;
+ val = snd_hda_get_bool_hint(codec, "spdif_status_reset");
+ if (val >= 0)
+ codec->spdif_status_reset = !!val;
+ val = snd_hda_get_bool_hint(codec, "pin_amp_workaround");
+ if (val >= 0)
+ codec->pin_amp_workaround = !!val;
+ val = snd_hda_get_bool_hint(codec, "single_adc_amp");
+ if (val >= 0)
+ codec->single_adc_amp = !!val;
+ val = snd_hda_get_bool_hint(codec, "power_save_node");
+ if (val >= 0)
+ codec->power_save_node = !!val;
+
+ val = snd_hda_get_bool_hint(codec, "auto_mute");
+ if (val >= 0)
+ spec->suppress_auto_mute = !val;
+ val = snd_hda_get_bool_hint(codec, "auto_mic");
+ if (val >= 0)
+ spec->suppress_auto_mic = !val;
+ val = snd_hda_get_bool_hint(codec, "line_in_auto_switch");
+ if (val >= 0)
+ spec->line_in_auto_switch = !!val;
+ val = snd_hda_get_bool_hint(codec, "auto_mute_via_amp");
+ if (val >= 0)
+ spec->auto_mute_via_amp = !!val;
+ val = snd_hda_get_bool_hint(codec, "need_dac_fix");
+ if (val >= 0)
+ spec->need_dac_fix = !!val;
+ val = snd_hda_get_bool_hint(codec, "primary_hp");
+ if (val >= 0)
+ spec->no_primary_hp = !val;
+ val = snd_hda_get_bool_hint(codec, "multi_io");
+ if (val >= 0)
+ spec->no_multi_io = !val;
+ val = snd_hda_get_bool_hint(codec, "multi_cap_vol");
+ if (val >= 0)
+ spec->multi_cap_vol = !!val;
+ val = snd_hda_get_bool_hint(codec, "inv_dmic_split");
+ if (val >= 0)
+ spec->inv_dmic_split = !!val;
+ val = snd_hda_get_bool_hint(codec, "indep_hp");
+ if (val >= 0)
+ spec->indep_hp = !!val;
+ val = snd_hda_get_bool_hint(codec, "add_stereo_mix_input");
+ if (val >= 0)
+ spec->add_stereo_mix_input = !!val;
+ /* the following two are just for compatibility */
+ val = snd_hda_get_bool_hint(codec, "add_out_jack_modes");
+ if (val >= 0)
+ spec->add_jack_modes = !!val;
+ val = snd_hda_get_bool_hint(codec, "add_in_jack_modes");
+ if (val >= 0)
+ spec->add_jack_modes = !!val;
+ val = snd_hda_get_bool_hint(codec, "add_jack_modes");
+ if (val >= 0)
+ spec->add_jack_modes = !!val;
+ val = snd_hda_get_bool_hint(codec, "power_down_unused");
+ if (val >= 0)
+ spec->power_down_unused = !!val;
+ val = snd_hda_get_bool_hint(codec, "add_hp_mic");
+ if (val >= 0)
+ spec->hp_mic = !!val;
+ val = snd_hda_get_bool_hint(codec, "hp_mic_detect");
+ if (val >= 0)
+ spec->suppress_hp_mic_detect = !val;
+ val = snd_hda_get_bool_hint(codec, "vmaster");
+ if (val >= 0)
+ spec->suppress_vmaster = !val;
+
+ if (!snd_hda_get_int_hint(codec, "mixer_nid", &val))
+ spec->mixer_nid = val;
+}
+
+/*
+ * pin control value accesses
+ */
+
+#define update_pin_ctl(codec, pin, val) \
+ snd_hda_codec_write_cache(codec, pin, 0, \
+ AC_VERB_SET_PIN_WIDGET_CONTROL, val)
+
+/* restore the pinctl based on the cached value */
+static inline void restore_pin_ctl(struct hda_codec *codec, hda_nid_t pin)
+{
+ update_pin_ctl(codec, pin, snd_hda_codec_get_pin_target(codec, pin));
+}
+
+/* set the pinctl target value and write it if requested */
+static void set_pin_target(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int val, bool do_write)
+{
+ if (!pin)
+ return;
+ val = snd_hda_correct_pin_ctl(codec, pin, val);
+ snd_hda_codec_set_pin_target(codec, pin, val);
+ if (do_write)
+ update_pin_ctl(codec, pin, val);
+}
+
+/* set pinctl target values for all given pins */
+static void set_pin_targets(struct hda_codec *codec, int num_pins,
+ hda_nid_t *pins, unsigned int val)
+{
+ int i;
+ for (i = 0; i < num_pins; i++)
+ set_pin_target(codec, pins[i], val, false);
+}
+
+/*
+ * parsing paths
+ */
+
+/* return the position of NID in the list, or -1 if not found */
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+ int i;
+ for (i = 0; i < nums; i++)
+ if (list[i] == nid)
+ return i;
+ return -1;
+}
+
+/* return true if the given NID is contained in the path */
+static bool is_nid_contained(struct nid_path *path, hda_nid_t nid)
+{
+ return find_idx_in_nid_list(nid, path->path, path->depth) >= 0;
+}
+
+static struct nid_path *get_nid_path(struct hda_codec *codec,
+ hda_nid_t from_nid, hda_nid_t to_nid,
+ int anchor_nid)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+ int i;
+
+ snd_array_for_each(&spec->paths, i, path) {
+ if (path->depth <= 0)
+ continue;
+ if ((!from_nid || path->path[0] == from_nid) &&
+ (!to_nid || path->path[path->depth - 1] == to_nid)) {
+ if (!anchor_nid ||
+ (anchor_nid > 0 && is_nid_contained(path, anchor_nid)) ||
+ (anchor_nid < 0 && !is_nid_contained(path, anchor_nid)))
+ return path;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * snd_hda_get_path_idx - get the index number corresponding to the path
+ * instance
+ * @codec: the HDA codec
+ * @path: nid_path object
+ *
+ * The returned index starts from 1, i.e. the actual array index with offset 1,
+ * and zero is handled as an invalid path
+ */
+int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *array = spec->paths.list;
+ ssize_t idx;
+
+ if (!spec->paths.used)
+ return 0;
+ idx = path - array;
+ if (idx < 0 || idx >= spec->paths.used)
+ return 0;
+ return idx + 1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_path_idx);
+
+/**
+ * snd_hda_get_path_from_idx - get the path instance corresponding to the
+ * given index number
+ * @codec: the HDA codec
+ * @idx: the path index
+ */
+struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (idx <= 0 || idx > spec->paths.used)
+ return NULL;
+ return snd_array_elem(&spec->paths, idx - 1);
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_path_from_idx);
+
+/* check whether the given DAC is already found in any existing paths */
+static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct nid_path *path;
+ int i;
+
+ snd_array_for_each(&spec->paths, i, path) {
+ if (path->path[0] == nid)
+ return true;
+ }
+ return false;
+}
+
+/* check whether the given two widgets can be connected */
+static bool is_reachable_path(struct hda_codec *codec,
+ hda_nid_t from_nid, hda_nid_t to_nid)
+{
+ if (!from_nid || !to_nid)
+ return false;
+ return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0;
+}
+
+/* nid, dir and idx */
+#define AMP_VAL_COMPARE_MASK (0xffff | (1U << 18) | (0x0f << 19))
+
+/* check whether the given ctl is already assigned in any path elements */
+static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct nid_path *path;
+ int i;
+
+ val &= AMP_VAL_COMPARE_MASK;
+ snd_array_for_each(&spec->paths, i, path) {
+ if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val)
+ return true;
+ }
+ return false;
+}
+
+/* check whether a control with the given (nid, dir, idx) was assigned */
+static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid,
+ int dir, int idx, int type)
+{
+ unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir);
+ return is_ctl_used(codec, val, type);
+}
+
+static void print_nid_path(struct hda_codec *codec,
+ const char *pfx, struct nid_path *path)
+{
+ char buf[40];
+ char *pos = buf;
+ int i;
+
+ *pos = 0;
+ for (i = 0; i < path->depth; i++)
+ pos += scnprintf(pos, sizeof(buf) - (pos - buf), "%s%02x",
+ pos != buf ? ":" : "",
+ path->path[i]);
+
+ codec_dbg(codec, "%s path: depth=%d '%s'\n", pfx, path->depth, buf);
+}
+
+/* called recursively */
+static bool __parse_nid_path(struct hda_codec *codec,
+ hda_nid_t from_nid, hda_nid_t to_nid,
+ int anchor_nid, struct nid_path *path,
+ int depth)
+{
+ const hda_nid_t *conn;
+ int i, nums;
+
+ if (to_nid == anchor_nid)
+ anchor_nid = 0; /* anchor passed */
+ else if (to_nid == (hda_nid_t)(-anchor_nid))
+ return false; /* hit the exclusive nid */
+
+ nums = snd_hda_get_conn_list(codec, to_nid, &conn);
+ for (i = 0; i < nums; i++) {
+ if (conn[i] != from_nid) {
+ /* special case: when from_nid is 0,
+ * try to find an empty DAC
+ */
+ if (from_nid ||
+ get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT ||
+ is_dac_already_used(codec, conn[i]))
+ continue;
+ }
+ /* anchor is not requested or already passed? */
+ if (anchor_nid <= 0)
+ goto found;
+ }
+ if (depth >= MAX_NID_PATH_DEPTH)
+ return false;
+ for (i = 0; i < nums; i++) {
+ unsigned int type;
+ type = get_wcaps_type(get_wcaps(codec, conn[i]));
+ if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN ||
+ type == AC_WID_PIN)
+ continue;
+ if (__parse_nid_path(codec, from_nid, conn[i],
+ anchor_nid, path, depth + 1))
+ goto found;
+ }
+ return false;
+
+ found:
+ path->path[path->depth] = conn[i];
+ path->idx[path->depth + 1] = i;
+ if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX)
+ path->multi[path->depth + 1] = 1;
+ path->depth++;
+ return true;
+}
+
+/*
+ * snd_hda_parse_nid_path - parse the widget path from the given nid to
+ * the target nid
+ * @codec: the HDA codec
+ * @from_nid: the NID where the path start from
+ * @to_nid: the NID where the path ends at
+ * @anchor_nid: the anchor indication
+ * @path: the path object to store the result
+ *
+ * Returns true if a matching path is found.
+ *
+ * The parsing behavior depends on parameters:
+ * when @from_nid is 0, try to find an empty DAC;
+ * when @anchor_nid is set to a positive value, only paths through the widget
+ * with the given value are evaluated.
+ * when @anchor_nid is set to a negative value, paths through the widget
+ * with the negative of given value are excluded, only other paths are chosen.
+ * when @anchor_nid is zero, no special handling about path selection.
+ */
+static bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
+ hda_nid_t to_nid, int anchor_nid,
+ struct nid_path *path)
+{
+ if (__parse_nid_path(codec, from_nid, to_nid, anchor_nid, path, 1)) {
+ path->path[path->depth] = to_nid;
+ path->depth++;
+ return true;
+ }
+ return false;
+}
+
+/**
+ * snd_hda_add_new_path - parse the path between the given NIDs and
+ * add to the path list
+ * @codec: the HDA codec
+ * @from_nid: the NID where the path start from
+ * @to_nid: the NID where the path ends at
+ * @anchor_nid: the anchor indication, see snd_hda_parse_nid_path()
+ *
+ * If no valid path is found, returns NULL.
+ */
+struct nid_path *
+snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
+ hda_nid_t to_nid, int anchor_nid)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+
+ if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid))
+ return NULL;
+
+ /* check whether the path has been already added */
+ path = get_nid_path(codec, from_nid, to_nid, anchor_nid);
+ if (path)
+ return path;
+
+ path = snd_array_new(&spec->paths);
+ if (!path)
+ return NULL;
+ memset(path, 0, sizeof(*path));
+ if (snd_hda_parse_nid_path(codec, from_nid, to_nid, anchor_nid, path))
+ return path;
+ /* push back */
+ spec->paths.used--;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_add_new_path);
+
+/* clear the given path as invalid so that it won't be picked up later */
+static void invalidate_nid_path(struct hda_codec *codec, int idx)
+{
+ struct nid_path *path = snd_hda_get_path_from_idx(codec, idx);
+ if (!path)
+ return;
+ memset(path, 0, sizeof(*path));
+}
+
+/* return a DAC if paired to the given pin by codec driver */
+static hda_nid_t get_preferred_dac(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const hda_nid_t *list = spec->preferred_dacs;
+
+ if (!list)
+ return 0;
+ for (; *list; list += 2)
+ if (*list == pin)
+ return list[1];
+ return 0;
+}
+
+/* look for an empty DAC slot */
+static hda_nid_t look_for_dac(struct hda_codec *codec, hda_nid_t pin,
+ bool is_digital)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ bool cap_digital;
+ int i;
+
+ for (i = 0; i < spec->num_all_dacs; i++) {
+ hda_nid_t nid = spec->all_dacs[i];
+ if (!nid || is_dac_already_used(codec, nid))
+ continue;
+ cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL);
+ if (is_digital != cap_digital)
+ continue;
+ if (is_reachable_path(codec, nid, pin))
+ return nid;
+ }
+ return 0;
+}
+
+/* replace the channels in the composed amp value with the given number */
+static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs)
+{
+ val &= ~(0x3U << 16);
+ val |= chs << 16;
+ return val;
+}
+
+static bool same_amp_caps(struct hda_codec *codec, hda_nid_t nid1,
+ hda_nid_t nid2, int dir)
+{
+ if (!(get_wcaps(codec, nid1) & (1 << (dir + 1))))
+ return !(get_wcaps(codec, nid2) & (1 << (dir + 1)));
+ return (query_amp_caps(codec, nid1, dir) ==
+ query_amp_caps(codec, nid2, dir));
+}
+
+/* look for a widget suitable for assigning a mute switch in the path */
+static hda_nid_t look_for_out_mute_nid(struct hda_codec *codec,
+ struct nid_path *path)
+{
+ int i;
+
+ for (i = path->depth - 1; i >= 0; i--) {
+ if (nid_has_mute(codec, path->path[i], HDA_OUTPUT))
+ return path->path[i];
+ if (i != path->depth - 1 && i != 0 &&
+ nid_has_mute(codec, path->path[i], HDA_INPUT))
+ return path->path[i];
+ }
+ return 0;
+}
+
+/* look for a widget suitable for assigning a volume ctl in the path */
+static hda_nid_t look_for_out_vol_nid(struct hda_codec *codec,
+ struct nid_path *path)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ for (i = path->depth - 1; i >= 0; i--) {
+ hda_nid_t nid = path->path[i];
+ if ((spec->out_vol_mask >> nid) & 1)
+ continue;
+ if (nid_has_volume(codec, nid, HDA_OUTPUT))
+ return nid;
+ }
+ return 0;
+}
+
+/*
+ * path activation / deactivation
+ */
+
+/* can have the amp-in capability? */
+static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx)
+{
+ hda_nid_t nid = path->path[idx];
+ unsigned int caps = get_wcaps(codec, nid);
+ unsigned int type = get_wcaps_type(caps);
+
+ if (!(caps & AC_WCAP_IN_AMP))
+ return false;
+ if (type == AC_WID_PIN && idx > 0) /* only for input pins */
+ return false;
+ return true;
+}
+
+/* can have the amp-out capability? */
+static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx)
+{
+ hda_nid_t nid = path->path[idx];
+ unsigned int caps = get_wcaps(codec, nid);
+ unsigned int type = get_wcaps_type(caps);
+
+ if (!(caps & AC_WCAP_OUT_AMP))
+ return false;
+ if (type == AC_WID_PIN && !idx) /* only for output pins */
+ return false;
+ return true;
+}
+
+/* check whether the given (nid,dir,idx) is active */
+static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int dir, unsigned int idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int type = get_wcaps_type(get_wcaps(codec, nid));
+ const struct nid_path *path;
+ int i, n;
+
+ if (nid == codec->core.afg)
+ return true;
+
+ snd_array_for_each(&spec->paths, n, path) {
+ if (!path->active)
+ continue;
+ if (codec->power_save_node) {
+ if (!path->stream_enabled)
+ continue;
+ /* ignore unplugged paths except for DAC/ADC */
+ if (!(path->pin_enabled || path->pin_fixed) &&
+ type != AC_WID_AUD_OUT && type != AC_WID_AUD_IN)
+ continue;
+ }
+ for (i = 0; i < path->depth; i++) {
+ if (path->path[i] == nid) {
+ if (dir == HDA_OUTPUT || idx == -1 ||
+ path->idx[i] == idx)
+ return true;
+ break;
+ }
+ }
+ }
+ return false;
+}
+
+/* check whether the NID is referred by any active paths */
+#define is_active_nid_for_any(codec, nid) \
+ is_active_nid(codec, nid, HDA_OUTPUT, -1)
+
+/* get the default amp value for the target state */
+static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid,
+ int dir, unsigned int caps, bool enable)
+{
+ unsigned int val = 0;
+
+ if (caps & AC_AMPCAP_NUM_STEPS) {
+ /* set to 0dB */
+ if (enable)
+ val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
+ }
+ if (caps & (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) {
+ if (!enable)
+ val |= HDA_AMP_MUTE;
+ }
+ return val;
+}
+
+/* is this a stereo widget or a stereo-to-mono mix? */
+static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid, int dir)
+{
+ unsigned int wcaps = get_wcaps(codec, nid);
+ hda_nid_t conn;
+
+ if (wcaps & AC_WCAP_STEREO)
+ return true;
+ if (dir != HDA_INPUT || get_wcaps_type(wcaps) != AC_WID_AUD_MIX)
+ return false;
+ if (snd_hda_get_num_conns(codec, nid) != 1)
+ return false;
+ if (snd_hda_get_connections(codec, nid, &conn, 1) < 0)
+ return false;
+ return !!(get_wcaps(codec, conn) & AC_WCAP_STEREO);
+}
+
+/* initialize the amp value (only at the first time) */
+static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx)
+{
+ unsigned int caps = query_amp_caps(codec, nid, dir);
+ int val = get_amp_val_to_activate(codec, nid, dir, caps, false);
+
+ if (is_stereo_amps(codec, nid, dir))
+ snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val);
+ else
+ snd_hda_codec_amp_init(codec, nid, 0, dir, idx, 0xff, val);
+}
+
+/* update the amp, doing in stereo or mono depending on NID */
+static int update_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx,
+ unsigned int mask, unsigned int val)
+{
+ if (is_stereo_amps(codec, nid, dir))
+ return snd_hda_codec_amp_stereo(codec, nid, dir, idx,
+ mask, val);
+ else
+ return snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
+ mask, val);
+}
+
+/* calculate amp value mask we can modify;
+ * if the given amp is controlled by mixers, don't touch it
+ */
+static unsigned int get_amp_mask_to_modify(struct hda_codec *codec,
+ hda_nid_t nid, int dir, int idx,
+ unsigned int caps)
+{
+ unsigned int mask = 0xff;
+
+ if (caps & (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) {
+ if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_MUTE_CTL))
+ mask &= ~0x80;
+ }
+ if (caps & AC_AMPCAP_NUM_STEPS) {
+ if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) ||
+ is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL))
+ mask &= ~0x7f;
+ }
+ return mask;
+}
+
+static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
+ int idx, int idx_to_check, bool enable)
+{
+ unsigned int caps;
+ unsigned int mask, val;
+
+ caps = query_amp_caps(codec, nid, dir);
+ val = get_amp_val_to_activate(codec, nid, dir, caps, enable);
+ mask = get_amp_mask_to_modify(codec, nid, dir, idx_to_check, caps);
+ if (!mask)
+ return;
+
+ val &= mask;
+ update_amp(codec, nid, dir, idx, mask, val);
+}
+
+static void check_and_activate_amp(struct hda_codec *codec, hda_nid_t nid,
+ int dir, int idx, int idx_to_check,
+ bool enable)
+{
+ /* check whether the given amp is still used by others */
+ if (!enable && is_active_nid(codec, nid, dir, idx_to_check))
+ return;
+ activate_amp(codec, nid, dir, idx, idx_to_check, enable);
+}
+
+static void activate_amp_out(struct hda_codec *codec, struct nid_path *path,
+ int i, bool enable)
+{
+ hda_nid_t nid = path->path[i];
+ init_amp(codec, nid, HDA_OUTPUT, 0);
+ check_and_activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable);
+}
+
+static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
+ int i, bool enable, bool add_aamix)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const hda_nid_t *conn;
+ int n, nums, idx;
+ int type;
+ hda_nid_t nid = path->path[i];
+
+ nums = snd_hda_get_conn_list(codec, nid, &conn);
+ if (nums < 0)
+ return;
+ type = get_wcaps_type(get_wcaps(codec, nid));
+ if (type == AC_WID_PIN ||
+ (type == AC_WID_AUD_IN && codec->single_adc_amp)) {
+ nums = 1;
+ idx = 0;
+ } else
+ idx = path->idx[i];
+
+ for (n = 0; n < nums; n++)
+ init_amp(codec, nid, HDA_INPUT, n);
+
+ /* here is a little bit tricky in comparison with activate_amp_out();
+ * when aa-mixer is available, we need to enable the path as well
+ */
+ for (n = 0; n < nums; n++) {
+ if (n != idx) {
+ if (conn[n] != spec->mixer_merge_nid)
+ continue;
+ /* when aamix is disabled, force to off */
+ if (!add_aamix) {
+ activate_amp(codec, nid, HDA_INPUT, n, n, false);
+ continue;
+ }
+ }
+ check_and_activate_amp(codec, nid, HDA_INPUT, n, idx, enable);
+ }
+}
+
+/* sync power of each widget in the given path */
+static hda_nid_t path_power_update(struct hda_codec *codec,
+ struct nid_path *path,
+ bool allow_powerdown)
+{
+ hda_nid_t nid, changed = 0;
+ int i, state, power;
+
+ for (i = 0; i < path->depth; i++) {
+ nid = path->path[i];
+ if (!(get_wcaps(codec, nid) & AC_WCAP_POWER))
+ continue;
+ if (nid == codec->core.afg)
+ continue;
+ if (!allow_powerdown || is_active_nid_for_any(codec, nid))
+ state = AC_PWRST_D0;
+ else
+ state = AC_PWRST_D3;
+ power = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_POWER_STATE, 0);
+ if (power != (state | (state << 4))) {
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_POWER_STATE, state);
+ changed = nid;
+ /* all known codecs seem to be capable to handl
+ * widgets state even in D3, so far.
+ * if any new codecs need to restore the widget
+ * states after D0 transition, call the function
+ * below.
+ */
+#if 0 /* disabled */
+ if (state == AC_PWRST_D0)
+ snd_hdac_regmap_sync_node(&codec->core, nid);
+#endif
+ }
+ }
+ return changed;
+}
+
+/* do sync with the last power state change */
+static void sync_power_state_change(struct hda_codec *codec, hda_nid_t nid)
+{
+ if (nid) {
+ msleep(10);
+ snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0);
+ }
+}
+
+/**
+ * snd_hda_activate_path - activate or deactivate the given path
+ * @codec: the HDA codec
+ * @path: the path to activate/deactivate
+ * @enable: flag to activate or not
+ * @add_aamix: enable the input from aamix NID
+ *
+ * If @add_aamix is set, enable the input from aa-mix NID as well (if any).
+ */
+void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
+ bool enable, bool add_aamix)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ path->active = enable;
+
+ /* make sure the widget is powered up */
+ if (enable && (spec->power_down_unused || codec->power_save_node))
+ path_power_update(codec, path, codec->power_save_node);
+
+ for (i = path->depth - 1; i >= 0; i--) {
+ hda_nid_t nid = path->path[i];
+
+ if (enable && path->multi[i])
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ path->idx[i]);
+ if (has_amp_in(codec, path, i))
+ activate_amp_in(codec, path, i, enable, add_aamix);
+ if (has_amp_out(codec, path, i))
+ activate_amp_out(codec, path, i, enable);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_activate_path);
+
+/* if the given path is inactive, put widgets into D3 (only if suitable) */
+static void path_power_down_sync(struct hda_codec *codec, struct nid_path *path)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (!(spec->power_down_unused || codec->power_save_node) || path->active)
+ return;
+ sync_power_state_change(codec, path_power_update(codec, path, true));
+}
+
+/* turn on/off EAPD on the given pin */
+static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->own_eapd_ctl ||
+ !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD))
+ return;
+ if (spec->keep_eapd_on && !enable)
+ return;
+ if (codec->inv_eapd)
+ enable = !enable;
+ snd_hda_codec_write_cache(codec, pin, 0,
+ AC_VERB_SET_EAPD_BTLENABLE,
+ enable ? 0x02 : 0x00);
+}
+
+/* re-initialize the path specified by the given path index */
+static void resume_path_from_idx(struct hda_codec *codec, int path_idx)
+{
+ struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx);
+ if (path)
+ snd_hda_activate_path(codec, path, path->active, false);
+}
+
+
+/*
+ * Helper functions for creating mixer ctl elements
+ */
+
+static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+static int hda_gen_bind_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+enum {
+ HDA_CTL_WIDGET_VOL,
+ HDA_CTL_WIDGET_MUTE,
+ HDA_CTL_BIND_MUTE,
+};
+static const struct snd_kcontrol_new control_templates[] = {
+ HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+ /* only the put callback is replaced for handling the special mute */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .subdevice = HDA_SUBDEV_AMP_FLAG,
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_amp_switch_get,
+ .put = hda_gen_mixer_mute_put, /* replaced */
+ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0),
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = hda_gen_bind_mute_get,
+ .put = hda_gen_bind_mute_put, /* replaced */
+ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0),
+ },
+};
+
+/* add dynamic controls from template */
+static struct snd_kcontrol_new *
+add_control(struct hda_gen_spec *spec, int type, const char *name,
+ int cidx, unsigned long val)
+{
+ struct snd_kcontrol_new *knew;
+
+ knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]);
+ if (!knew)
+ return NULL;
+ knew->index = cidx;
+ if (get_amp_nid_(val))
+ knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+ if (knew->access == 0)
+ knew->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ knew->private_value = val;
+ return knew;
+}
+
+static int add_control_with_pfx(struct hda_gen_spec *spec, int type,
+ const char *pfx, const char *dir,
+ const char *sfx, int cidx, unsigned long val)
+{
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int len;
+
+ len = snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
+ if (snd_BUG_ON(len >= sizeof(name)))
+ return -EINVAL;
+ if (!add_control(spec, type, name, cidx, val))
+ return -ENOMEM;
+ return 0;
+}
+
+#define add_pb_vol_ctrl(spec, type, pfx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val)
+#define add_pb_sw_ctrl(spec, type, pfx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val)
+#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val)
+#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val)
+
+static int add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx,
+ unsigned int chs, struct nid_path *path)
+{
+ unsigned int val;
+ if (!path)
+ return 0;
+ val = path->ctls[NID_PATH_VOL_CTL];
+ if (!val)
+ return 0;
+ val = amp_val_replace_channels(val, chs);
+ return __add_pb_vol_ctrl(codec->spec, HDA_CTL_WIDGET_VOL, pfx, cidx, val);
+}
+
+/* return the channel bits suitable for the given path->ctls[] */
+static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path,
+ int type)
+{
+ int chs = 1; /* mono (left only) */
+ if (path) {
+ hda_nid_t nid = get_amp_nid_(path->ctls[type]);
+ if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO))
+ chs = 3; /* stereo */
+ }
+ return chs;
+}
+
+static int add_stereo_vol(struct hda_codec *codec, const char *pfx, int cidx,
+ struct nid_path *path)
+{
+ int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL);
+ return add_vol_ctl(codec, pfx, cidx, chs, path);
+}
+
+/* create a mute-switch for the given mixer widget;
+ * if it has multiple sources (e.g. DAC and loopback), create a bind-mute
+ */
+static int add_sw_ctl(struct hda_codec *codec, const char *pfx, int cidx,
+ unsigned int chs, struct nid_path *path)
+{
+ unsigned int val;
+ int type = HDA_CTL_WIDGET_MUTE;
+
+ if (!path)
+ return 0;
+ val = path->ctls[NID_PATH_MUTE_CTL];
+ if (!val)
+ return 0;
+ val = amp_val_replace_channels(val, chs);
+ if (get_amp_direction_(val) == HDA_INPUT) {
+ hda_nid_t nid = get_amp_nid_(val);
+ int nums = snd_hda_get_num_conns(codec, nid);
+ if (nums > 1) {
+ type = HDA_CTL_BIND_MUTE;
+ val |= nums << 19;
+ }
+ }
+ return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val);
+}
+
+static int add_stereo_sw(struct hda_codec *codec, const char *pfx,
+ int cidx, struct nid_path *path)
+{
+ int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL);
+ return add_sw_ctl(codec, pfx, cidx, chs, path);
+}
+
+/* playback mute control with the software mute bit check */
+static void sync_auto_mute_bits(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (spec->auto_mute_via_amp) {
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ bool enabled = !((spec->mute_bits >> nid) & 1);
+ ucontrol->value.integer.value[0] &= enabled;
+ ucontrol->value.integer.value[1] &= enabled;
+ }
+}
+
+static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ sync_auto_mute_bits(kcontrol, ucontrol);
+ return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+}
+
+/*
+ * Bound mute controls
+ */
+#define AMP_VAL_IDX_SHIFT 19
+#define AMP_VAL_IDX_MASK (0x0f<<19)
+
+static int hda_gen_bind_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned long pval;
+ int err;
+
+ guard(mutex)(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */
+ err = snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
+ kcontrol->private_value = pval;
+ return err;
+}
+
+static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned long pval;
+ int i, indices, err = 0, change = 0;
+
+ sync_auto_mute_bits(kcontrol, ucontrol);
+
+ guard(mutex)(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT;
+ for (i = 0; i < indices; i++) {
+ kcontrol->private_value = (pval & ~AMP_VAL_IDX_MASK) |
+ (i << AMP_VAL_IDX_SHIFT);
+ err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ if (err < 0)
+ break;
+ change |= err;
+ }
+ kcontrol->private_value = pval;
+ return err < 0 ? err : change;
+}
+
+/* any ctl assigned to the path with the given index? */
+static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type)
+{
+ struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx);
+ return path && path->ctls[ctl_type];
+}
+
+static const char * const channel_name[] = {
+ "Front", "Surround", "CLFE", "Side", "Back",
+};
+
+/* give some appropriate ctl name prefix for the given line out channel */
+static const char *get_line_out_pfx(struct hda_codec *codec, int ch,
+ int *index, int ctl_type)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+
+ *index = 0;
+ if (cfg->line_outs == 1 && !spec->multi_ios &&
+ !codec->force_pin_prefix &&
+ !cfg->hp_outs && !cfg->speaker_outs)
+ return spec->vmaster_mute.hook ? "PCM" : "Master";
+
+ /* if there is really a single DAC used in the whole output paths,
+ * use it master (or "PCM" if a vmaster hook is present)
+ */
+ if (spec->multiout.num_dacs == 1 && !spec->mixer_nid &&
+ !codec->force_pin_prefix &&
+ !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0])
+ return spec->vmaster_mute.hook ? "PCM" : "Master";
+
+ /* multi-io channels */
+ if (ch >= cfg->line_outs)
+ goto fixed_name;
+
+ switch (cfg->line_out_type) {
+ case AUTO_PIN_SPEAKER_OUT:
+ /* if the primary channel vol/mute is shared with HP volume,
+ * don't name it as Speaker
+ */
+ if (!ch && cfg->hp_outs &&
+ !path_has_mixer(codec, spec->hp_paths[0], ctl_type))
+ break;
+ if (cfg->line_outs == 1)
+ return "Speaker";
+ if (cfg->line_outs == 2)
+ return ch ? "Bass Speaker" : "Speaker";
+ break;
+ case AUTO_PIN_HP_OUT:
+ /* if the primary channel vol/mute is shared with spk volume,
+ * don't name it as Headphone
+ */
+ if (!ch && cfg->speaker_outs &&
+ !path_has_mixer(codec, spec->speaker_paths[0], ctl_type))
+ break;
+ /* for multi-io case, only the primary out */
+ if (ch && spec->multi_ios)
+ break;
+ *index = ch;
+ return "Headphone";
+ case AUTO_PIN_LINE_OUT:
+ /* This deals with the case where one HP or one Speaker or
+ * one HP + one Speaker need to share the DAC with LO
+ */
+ if (!ch) {
+ bool hp_lo_shared = false, spk_lo_shared = false;
+
+ if (cfg->speaker_outs)
+ spk_lo_shared = !path_has_mixer(codec,
+ spec->speaker_paths[0], ctl_type);
+ if (cfg->hp_outs)
+ hp_lo_shared = !path_has_mixer(codec, spec->hp_paths[0], ctl_type);
+ if (hp_lo_shared && spk_lo_shared)
+ return spec->vmaster_mute.hook ? "PCM" : "Master";
+ if (hp_lo_shared)
+ return "Headphone+LO";
+ if (spk_lo_shared)
+ return "Speaker+LO";
+ }
+ }
+
+ /* for a single channel output, we don't have to name the channel */
+ if (cfg->line_outs == 1 && !spec->multi_ios)
+ return "Line Out";
+
+ fixed_name:
+ if (ch >= ARRAY_SIZE(channel_name)) {
+ snd_BUG();
+ return "PCM";
+ }
+
+ return channel_name[ch];
+}
+
+/*
+ * Parse output paths
+ */
+
+/* badness definition */
+enum {
+ /* No primary DAC is found for the main output */
+ BAD_NO_PRIMARY_DAC = 0x10000,
+ /* No DAC is found for the extra output */
+ BAD_NO_DAC = 0x4000,
+ /* No possible multi-ios */
+ BAD_MULTI_IO = 0x120,
+ /* No individual DAC for extra output */
+ BAD_NO_EXTRA_DAC = 0x102,
+ /* No individual DAC for extra surrounds */
+ BAD_NO_EXTRA_SURR_DAC = 0x101,
+ /* Primary DAC shared with main surrounds */
+ BAD_SHARED_SURROUND = 0x100,
+ /* No independent HP possible */
+ BAD_NO_INDEP_HP = 0x10,
+ /* Primary DAC shared with main CLFE */
+ BAD_SHARED_CLFE = 0x10,
+ /* Primary DAC shared with extra surrounds */
+ BAD_SHARED_EXTRA_SURROUND = 0x10,
+ /* Volume widget is shared */
+ BAD_SHARED_VOL = 0x10,
+};
+
+/* look for widgets in the given path which are appropriate for
+ * volume and mute controls, and assign the values to ctls[].
+ *
+ * When no appropriate widget is found in the path, the badness value
+ * is incremented depending on the situation. The function returns the
+ * total badness for both volume and mute controls.
+ */
+static int assign_out_path_ctls(struct hda_codec *codec, struct nid_path *path)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t nid;
+ unsigned int val;
+ int badness = 0;
+
+ if (!path)
+ return BAD_SHARED_VOL * 2;
+
+ if (path->ctls[NID_PATH_VOL_CTL] ||
+ path->ctls[NID_PATH_MUTE_CTL])
+ return 0; /* already evaluated */
+
+ nid = look_for_out_vol_nid(codec, path);
+ if (nid) {
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ if (spec->dac_min_mute)
+ val |= HDA_AMP_VAL_MIN_MUTE;
+ if (is_ctl_used(codec, val, NID_PATH_VOL_CTL))
+ badness += BAD_SHARED_VOL;
+ else
+ path->ctls[NID_PATH_VOL_CTL] = val;
+ } else
+ badness += BAD_SHARED_VOL;
+ nid = look_for_out_mute_nid(codec, path);
+ if (nid) {
+ unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid));
+ if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT ||
+ nid_has_mute(codec, nid, HDA_OUTPUT))
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ else
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT);
+ if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL))
+ badness += BAD_SHARED_VOL;
+ else
+ path->ctls[NID_PATH_MUTE_CTL] = val;
+ } else
+ badness += BAD_SHARED_VOL;
+ return badness;
+}
+
+const struct badness_table hda_main_out_badness = {
+ .no_primary_dac = BAD_NO_PRIMARY_DAC,
+ .no_dac = BAD_NO_DAC,
+ .shared_primary = BAD_NO_PRIMARY_DAC,
+ .shared_surr = BAD_SHARED_SURROUND,
+ .shared_clfe = BAD_SHARED_CLFE,
+ .shared_surr_main = BAD_SHARED_SURROUND,
+};
+EXPORT_SYMBOL_GPL(hda_main_out_badness);
+
+const struct badness_table hda_extra_out_badness = {
+ .no_primary_dac = BAD_NO_DAC,
+ .no_dac = BAD_NO_DAC,
+ .shared_primary = BAD_NO_EXTRA_DAC,
+ .shared_surr = BAD_SHARED_EXTRA_SURROUND,
+ .shared_clfe = BAD_SHARED_EXTRA_SURROUND,
+ .shared_surr_main = BAD_NO_EXTRA_SURR_DAC,
+};
+EXPORT_SYMBOL_GPL(hda_extra_out_badness);
+
+/* get the DAC of the primary output corresponding to the given array index */
+static hda_nid_t get_primary_out(struct hda_codec *codec, int idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+
+ if (cfg->line_outs > idx)
+ return spec->private_dac_nids[idx];
+ idx -= cfg->line_outs;
+ if (spec->multi_ios > idx)
+ return spec->multi_io[idx].dac;
+ return 0;
+}
+
+/* return the DAC if it's reachable, otherwise zero */
+static inline hda_nid_t try_dac(struct hda_codec *codec,
+ hda_nid_t dac, hda_nid_t pin)
+{
+ return is_reachable_path(codec, dac, pin) ? dac : 0;
+}
+
+/* try to assign DACs to pins and return the resultant badness */
+static int try_assign_dacs(struct hda_codec *codec, int num_outs,
+ const hda_nid_t *pins, hda_nid_t *dacs,
+ int *path_idx,
+ const struct badness_table *bad)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i, j;
+ int badness = 0;
+ hda_nid_t dac;
+
+ if (!num_outs)
+ return 0;
+
+ for (i = 0; i < num_outs; i++) {
+ struct nid_path *path;
+ hda_nid_t pin = pins[i];
+
+ if (!spec->preferred_dacs) {
+ path = snd_hda_get_path_from_idx(codec, path_idx[i]);
+ if (path) {
+ badness += assign_out_path_ctls(codec, path);
+ continue;
+ }
+ }
+
+ dacs[i] = get_preferred_dac(codec, pin);
+ if (dacs[i]) {
+ if (is_dac_already_used(codec, dacs[i]))
+ badness += bad->shared_primary;
+ } else if (spec->preferred_dacs) {
+ badness += BAD_NO_PRIMARY_DAC;
+ }
+
+ if (!dacs[i])
+ dacs[i] = look_for_dac(codec, pin, false);
+ if (!dacs[i] && !i) {
+ /* try to steal the DAC of surrounds for the front */
+ for (j = 1; j < num_outs; j++) {
+ if (is_reachable_path(codec, dacs[j], pin)) {
+ dacs[0] = dacs[j];
+ dacs[j] = 0;
+ invalidate_nid_path(codec, path_idx[j]);
+ path_idx[j] = 0;
+ break;
+ }
+ }
+ }
+ dac = dacs[i];
+ if (!dac) {
+ if (num_outs > 2)
+ dac = try_dac(codec, get_primary_out(codec, i), pin);
+ if (!dac)
+ dac = try_dac(codec, dacs[0], pin);
+ if (!dac)
+ dac = try_dac(codec, get_primary_out(codec, i), pin);
+ if (dac) {
+ if (!i)
+ badness += bad->shared_primary;
+ else if (i == 1)
+ badness += bad->shared_surr;
+ else
+ badness += bad->shared_clfe;
+ } else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) {
+ dac = spec->private_dac_nids[0];
+ badness += bad->shared_surr_main;
+ } else if (!i)
+ badness += bad->no_primary_dac;
+ else
+ badness += bad->no_dac;
+ }
+ if (!dac)
+ continue;
+ path = snd_hda_add_new_path(codec, dac, pin, -spec->mixer_nid);
+ if (!path && !i && spec->mixer_nid) {
+ /* try with aamix */
+ path = snd_hda_add_new_path(codec, dac, pin, 0);
+ }
+ if (!path) {
+ dacs[i] = 0;
+ badness += bad->no_dac;
+ } else {
+ /* print_nid_path(codec, "output", path); */
+ path->active = true;
+ path_idx[i] = snd_hda_get_path_idx(codec, path);
+ badness += assign_out_path_ctls(codec, path);
+ }
+ }
+
+ return badness;
+}
+
+/* return NID if the given pin has only a single connection to a certain DAC */
+static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+ hda_nid_t nid_found = 0;
+
+ for (i = 0; i < spec->num_all_dacs; i++) {
+ hda_nid_t nid = spec->all_dacs[i];
+ if (!nid || is_dac_already_used(codec, nid))
+ continue;
+ if (is_reachable_path(codec, nid, pin)) {
+ if (nid_found)
+ return 0;
+ nid_found = nid;
+ }
+ }
+ return nid_found;
+}
+
+/* check whether the given pin can be a multi-io pin */
+static bool can_be_multiio_pin(struct hda_codec *codec,
+ unsigned int location, hda_nid_t nid)
+{
+ unsigned int defcfg, caps;
+
+ defcfg = snd_hda_codec_get_pincfg(codec, nid);
+ if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX)
+ return false;
+ if (location && get_defcfg_location(defcfg) != location)
+ return false;
+ caps = snd_hda_query_pin_caps(codec, nid);
+ if (!(caps & AC_PINCAP_OUT))
+ return false;
+ return true;
+}
+
+/* count the number of input pins that are capable to be multi-io */
+static int count_multiio_pins(struct hda_codec *codec, hda_nid_t reference_pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
+ unsigned int location = get_defcfg_location(defcfg);
+ int type, i;
+ int num_pins = 0;
+
+ for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type != type)
+ continue;
+ if (can_be_multiio_pin(codec, location,
+ cfg->inputs[i].pin))
+ num_pins++;
+ }
+ }
+ return num_pins;
+}
+
+/*
+ * multi-io helper
+ *
+ * When hardwired is set, try to fill ony hardwired pins, and returns
+ * zero if any pins are filled, non-zero if nothing found.
+ * When hardwired is off, try to fill possible input pins, and returns
+ * the badness value.
+ */
+static int fill_multi_ios(struct hda_codec *codec,
+ hda_nid_t reference_pin,
+ bool hardwired)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int type, i, j, num_pins, old_pins;
+ unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
+ unsigned int location = get_defcfg_location(defcfg);
+ int badness = 0;
+ struct nid_path *path;
+
+ old_pins = spec->multi_ios;
+ if (old_pins >= 2)
+ goto end_fill;
+
+ num_pins = count_multiio_pins(codec, reference_pin);
+ if (num_pins < 2)
+ goto end_fill;
+
+ for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ hda_nid_t dac = 0;
+
+ if (cfg->inputs[i].type != type)
+ continue;
+ if (!can_be_multiio_pin(codec, location, nid))
+ continue;
+ for (j = 0; j < spec->multi_ios; j++) {
+ if (nid == spec->multi_io[j].pin)
+ break;
+ }
+ if (j < spec->multi_ios)
+ continue;
+
+ if (hardwired)
+ dac = get_dac_if_single(codec, nid);
+ else if (!dac)
+ dac = look_for_dac(codec, nid, false);
+ if (!dac) {
+ badness++;
+ continue;
+ }
+ path = snd_hda_add_new_path(codec, dac, nid,
+ -spec->mixer_nid);
+ if (!path) {
+ badness++;
+ continue;
+ }
+ /* print_nid_path(codec, "multiio", path); */
+ spec->multi_io[spec->multi_ios].pin = nid;
+ spec->multi_io[spec->multi_ios].dac = dac;
+ spec->out_paths[cfg->line_outs + spec->multi_ios] =
+ snd_hda_get_path_idx(codec, path);
+ spec->multi_ios++;
+ if (spec->multi_ios >= 2)
+ break;
+ }
+ }
+ end_fill:
+ if (badness)
+ badness = BAD_MULTI_IO;
+ if (old_pins == spec->multi_ios) {
+ if (hardwired)
+ return 1; /* nothing found */
+ else
+ return badness; /* no badness if nothing found */
+ }
+ if (!hardwired && spec->multi_ios < 2) {
+ /* cancel newly assigned paths */
+ spec->paths.used -= spec->multi_ios - old_pins;
+ spec->multi_ios = old_pins;
+ return badness;
+ }
+
+ /* assign volume and mute controls */
+ for (i = old_pins; i < spec->multi_ios; i++) {
+ path = snd_hda_get_path_from_idx(codec, spec->out_paths[cfg->line_outs + i]);
+ badness += assign_out_path_ctls(codec, path);
+ }
+
+ return badness;
+}
+
+/* map DACs for all pins in the list if they are single connections */
+static bool map_singles(struct hda_codec *codec, int outs,
+ const hda_nid_t *pins, hda_nid_t *dacs, int *path_idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+ bool found = false;
+ for (i = 0; i < outs; i++) {
+ struct nid_path *path;
+ hda_nid_t dac;
+ if (dacs[i])
+ continue;
+ dac = get_dac_if_single(codec, pins[i]);
+ if (!dac)
+ continue;
+ path = snd_hda_add_new_path(codec, dac, pins[i],
+ -spec->mixer_nid);
+ if (!path && !i && spec->mixer_nid)
+ path = snd_hda_add_new_path(codec, dac, pins[i], 0);
+ if (path) {
+ dacs[i] = dac;
+ found = true;
+ /* print_nid_path(codec, "output", path); */
+ path->active = true;
+ path_idx[i] = snd_hda_get_path_idx(codec, path);
+ }
+ }
+ return found;
+}
+
+static inline bool has_aamix_out_paths(struct hda_gen_spec *spec)
+{
+ return spec->aamix_out_paths[0] || spec->aamix_out_paths[1] ||
+ spec->aamix_out_paths[2];
+}
+
+/* create a new path including aamix if available, and return its index */
+static int check_aamix_out_path(struct hda_codec *codec, int path_idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+ hda_nid_t path_dac, dac, pin;
+
+ path = snd_hda_get_path_from_idx(codec, path_idx);
+ if (!path || !path->depth ||
+ is_nid_contained(path, spec->mixer_nid))
+ return 0;
+ path_dac = path->path[0];
+ dac = spec->private_dac_nids[0];
+ pin = path->path[path->depth - 1];
+ path = snd_hda_add_new_path(codec, dac, pin, spec->mixer_nid);
+ if (!path) {
+ if (dac != path_dac)
+ dac = path_dac;
+ else if (spec->multiout.hp_out_nid[0])
+ dac = spec->multiout.hp_out_nid[0];
+ else if (spec->multiout.extra_out_nid[0])
+ dac = spec->multiout.extra_out_nid[0];
+ else
+ dac = 0;
+ if (dac)
+ path = snd_hda_add_new_path(codec, dac, pin,
+ spec->mixer_nid);
+ }
+ if (!path)
+ return 0;
+ /* print_nid_path(codec, "output-aamix", path); */
+ path->active = false; /* unused as default */
+ path->pin_fixed = true; /* static route */
+ return snd_hda_get_path_idx(codec, path);
+}
+
+/* check whether the independent HP is available with the current config */
+static bool indep_hp_possible(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct nid_path *path;
+ int i, idx;
+
+ if (cfg->line_out_type == AUTO_PIN_HP_OUT)
+ idx = spec->out_paths[0];
+ else
+ idx = spec->hp_paths[0];
+ path = snd_hda_get_path_from_idx(codec, idx);
+ if (!path)
+ return false;
+
+ /* assume no path conflicts unless aamix is involved */
+ if (!spec->mixer_nid || !is_nid_contained(path, spec->mixer_nid))
+ return true;
+
+ /* check whether output paths contain aamix */
+ for (i = 0; i < cfg->line_outs; i++) {
+ if (spec->out_paths[i] == idx)
+ break;
+ path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]);
+ if (path && is_nid_contained(path, spec->mixer_nid))
+ return false;
+ }
+ for (i = 0; i < cfg->speaker_outs; i++) {
+ path = snd_hda_get_path_from_idx(codec, spec->speaker_paths[i]);
+ if (path && is_nid_contained(path, spec->mixer_nid))
+ return false;
+ }
+
+ return true;
+}
+
+/* fill the empty entries in the dac array for speaker/hp with the
+ * shared dac pointed by the paths
+ */
+static void refill_shared_dacs(struct hda_codec *codec, int num_outs,
+ hda_nid_t *dacs, int *path_idx)
+{
+ struct nid_path *path;
+ int i;
+
+ for (i = 0; i < num_outs; i++) {
+ if (dacs[i])
+ continue;
+ path = snd_hda_get_path_from_idx(codec, path_idx[i]);
+ if (!path)
+ continue;
+ dacs[i] = path->path[0];
+ }
+}
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int fill_and_eval_dacs(struct hda_codec *codec,
+ bool fill_hardwired,
+ bool fill_mio_first)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, err, badness;
+
+ /* set num_dacs once to full for look_for_dac() */
+ spec->multiout.num_dacs = cfg->line_outs;
+ spec->multiout.dac_nids = spec->private_dac_nids;
+ memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
+ memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid));
+ memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid));
+ spec->multi_ios = 0;
+ snd_array_free(&spec->paths);
+
+ /* clear path indices */
+ memset(spec->out_paths, 0, sizeof(spec->out_paths));
+ memset(spec->hp_paths, 0, sizeof(spec->hp_paths));
+ memset(spec->speaker_paths, 0, sizeof(spec->speaker_paths));
+ memset(spec->aamix_out_paths, 0, sizeof(spec->aamix_out_paths));
+ memset(spec->digout_paths, 0, sizeof(spec->digout_paths));
+ memset(spec->input_paths, 0, sizeof(spec->input_paths));
+ memset(spec->loopback_paths, 0, sizeof(spec->loopback_paths));
+ memset(&spec->digin_path, 0, sizeof(spec->digin_path));
+
+ badness = 0;
+
+ /* fill hard-wired DACs first */
+ if (fill_hardwired) {
+ bool mapped;
+ do {
+ mapped = map_singles(codec, cfg->line_outs,
+ cfg->line_out_pins,
+ spec->private_dac_nids,
+ spec->out_paths);
+ mapped |= map_singles(codec, cfg->hp_outs,
+ cfg->hp_pins,
+ spec->multiout.hp_out_nid,
+ spec->hp_paths);
+ mapped |= map_singles(codec, cfg->speaker_outs,
+ cfg->speaker_pins,
+ spec->multiout.extra_out_nid,
+ spec->speaker_paths);
+ if (!spec->no_multi_io &&
+ fill_mio_first && cfg->line_outs == 1 &&
+ cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = fill_multi_ios(codec, cfg->line_out_pins[0], true);
+ if (!err)
+ mapped = true;
+ }
+ } while (mapped);
+ }
+
+ badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins,
+ spec->private_dac_nids, spec->out_paths,
+ spec->main_out_badness);
+
+ if (!spec->no_multi_io && fill_mio_first &&
+ cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ /* try to fill multi-io first */
+ err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
+ if (err < 0)
+ return err;
+ /* we don't count badness at this stage yet */
+ }
+
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+ err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins,
+ spec->multiout.hp_out_nid,
+ spec->hp_paths,
+ spec->extra_out_badness);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = try_assign_dacs(codec, cfg->speaker_outs,
+ cfg->speaker_pins,
+ spec->multiout.extra_out_nid,
+ spec->speaker_paths,
+ spec->extra_out_badness);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
+ if (!spec->no_multi_io &&
+ cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
+
+ if (spec->mixer_nid) {
+ spec->aamix_out_paths[0] =
+ check_aamix_out_path(codec, spec->out_paths[0]);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ spec->aamix_out_paths[1] =
+ check_aamix_out_path(codec, spec->hp_paths[0]);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ spec->aamix_out_paths[2] =
+ check_aamix_out_path(codec, spec->speaker_paths[0]);
+ }
+
+ if (!spec->no_multi_io &&
+ cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+ if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2)
+ spec->multi_ios = 1; /* give badness */
+
+ /* re-count num_dacs and squash invalid entries */
+ spec->multiout.num_dacs = 0;
+ for (i = 0; i < cfg->line_outs; i++) {
+ if (spec->private_dac_nids[i])
+ spec->multiout.num_dacs++;
+ else {
+ memmove(spec->private_dac_nids + i,
+ spec->private_dac_nids + i + 1,
+ sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
+ spec->private_dac_nids[cfg->line_outs - 1] = 0;
+ }
+ }
+
+ spec->ext_channel_count = spec->min_channel_count =
+ spec->multiout.num_dacs * 2;
+
+ if (spec->multi_ios == 2) {
+ for (i = 0; i < 2; i++)
+ spec->private_dac_nids[spec->multiout.num_dacs++] =
+ spec->multi_io[i].dac;
+ } else if (spec->multi_ios) {
+ spec->multi_ios = 0;
+ badness += BAD_MULTI_IO;
+ }
+
+ if (spec->indep_hp && !indep_hp_possible(codec))
+ badness += BAD_NO_INDEP_HP;
+
+ /* re-fill the shared DAC for speaker / headphone */
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ refill_shared_dacs(codec, cfg->hp_outs,
+ spec->multiout.hp_out_nid,
+ spec->hp_paths);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ refill_shared_dacs(codec, cfg->speaker_outs,
+ spec->multiout.extra_out_nid,
+ spec->speaker_paths);
+
+ return badness;
+}
+
+#define DEBUG_BADNESS
+
+#ifdef DEBUG_BADNESS
+#define debug_badness(fmt, ...) \
+ codec_dbg(codec, fmt, ##__VA_ARGS__)
+#else
+#define debug_badness(fmt, ...) \
+ do { if (0) codec_dbg(codec, fmt, ##__VA_ARGS__); } while (0)
+#endif
+
+#ifdef DEBUG_BADNESS
+static inline void print_nid_path_idx(struct hda_codec *codec,
+ const char *pfx, int idx)
+{
+ struct nid_path *path;
+
+ path = snd_hda_get_path_from_idx(codec, idx);
+ if (path)
+ print_nid_path(codec, pfx, path);
+}
+
+static void debug_show_configs(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ static const char * const lo_type[3] = { "LO", "SP", "HP" };
+ int i;
+
+ debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x (type %s)\n",
+ cfg->line_out_pins[0], cfg->line_out_pins[1],
+ cfg->line_out_pins[2], cfg->line_out_pins[3],
+ spec->multiout.dac_nids[0],
+ spec->multiout.dac_nids[1],
+ spec->multiout.dac_nids[2],
+ spec->multiout.dac_nids[3],
+ lo_type[cfg->line_out_type]);
+ for (i = 0; i < cfg->line_outs; i++)
+ print_nid_path_idx(codec, " out", spec->out_paths[i]);
+ if (spec->multi_ios > 0)
+ debug_badness("multi_ios(%d) = %x/%x : %x/%x\n",
+ spec->multi_ios,
+ spec->multi_io[0].pin, spec->multi_io[1].pin,
+ spec->multi_io[0].dac, spec->multi_io[1].dac);
+ for (i = 0; i < spec->multi_ios; i++)
+ print_nid_path_idx(codec, " mio",
+ spec->out_paths[cfg->line_outs + i]);
+ if (cfg->hp_outs)
+ debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
+ cfg->hp_pins[0], cfg->hp_pins[1],
+ cfg->hp_pins[2], cfg->hp_pins[3],
+ spec->multiout.hp_out_nid[0],
+ spec->multiout.hp_out_nid[1],
+ spec->multiout.hp_out_nid[2],
+ spec->multiout.hp_out_nid[3]);
+ for (i = 0; i < cfg->hp_outs; i++)
+ print_nid_path_idx(codec, " hp ", spec->hp_paths[i]);
+ if (cfg->speaker_outs)
+ debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
+ cfg->speaker_pins[0], cfg->speaker_pins[1],
+ cfg->speaker_pins[2], cfg->speaker_pins[3],
+ spec->multiout.extra_out_nid[0],
+ spec->multiout.extra_out_nid[1],
+ spec->multiout.extra_out_nid[2],
+ spec->multiout.extra_out_nid[3]);
+ for (i = 0; i < cfg->speaker_outs; i++)
+ print_nid_path_idx(codec, " spk", spec->speaker_paths[i]);
+ for (i = 0; i < 3; i++)
+ print_nid_path_idx(codec, " mix", spec->aamix_out_paths[i]);
+}
+#else
+#define debug_show_configs(codec, cfg) /* NOP */
+#endif
+
+/* find all available DACs of the codec */
+static void fill_all_dac_nids(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t nid;
+
+ spec->num_all_dacs = 0;
+ memset(spec->all_dacs, 0, sizeof(spec->all_dacs));
+ for_each_hda_codec_node(nid, codec) {
+ if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT)
+ continue;
+ if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) {
+ codec_err(codec, "Too many DACs!\n");
+ break;
+ }
+ spec->all_dacs[spec->num_all_dacs++] = nid;
+ }
+}
+
+static int parse_output_paths(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct auto_pin_cfg *best_cfg __free(kfree) = NULL;
+ unsigned int val;
+ int best_badness = INT_MAX;
+ int badness;
+ bool fill_hardwired = true, fill_mio_first = true;
+ bool best_wired = true, best_mio = true;
+ bool hp_spk_swapped = false;
+
+ best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL);
+ if (!best_cfg)
+ return -ENOMEM;
+ *best_cfg = *cfg;
+
+ for (;;) {
+ badness = fill_and_eval_dacs(codec, fill_hardwired,
+ fill_mio_first);
+ if (badness < 0)
+ return badness;
+ debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n",
+ cfg->line_out_type, fill_hardwired, fill_mio_first,
+ badness);
+ debug_show_configs(codec, cfg);
+ if (badness < best_badness) {
+ best_badness = badness;
+ *best_cfg = *cfg;
+ best_wired = fill_hardwired;
+ best_mio = fill_mio_first;
+ }
+ if (!badness)
+ break;
+ fill_mio_first = !fill_mio_first;
+ if (!fill_mio_first)
+ continue;
+ fill_hardwired = !fill_hardwired;
+ if (!fill_hardwired)
+ continue;
+ if (hp_spk_swapped)
+ break;
+ hp_spk_swapped = true;
+ if (cfg->speaker_outs > 0 &&
+ cfg->line_out_type == AUTO_PIN_HP_OUT) {
+ cfg->hp_outs = cfg->line_outs;
+ memcpy(cfg->hp_pins, cfg->line_out_pins,
+ sizeof(cfg->hp_pins));
+ cfg->line_outs = cfg->speaker_outs;
+ memcpy(cfg->line_out_pins, cfg->speaker_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->speaker_outs = 0;
+ memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
+ cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
+ fill_hardwired = true;
+ continue;
+ }
+ if (cfg->hp_outs > 0 &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ cfg->speaker_outs = cfg->line_outs;
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->line_outs = cfg->hp_outs;
+ memcpy(cfg->line_out_pins, cfg->hp_pins,
+ sizeof(cfg->hp_pins));
+ cfg->hp_outs = 0;
+ memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+ fill_hardwired = true;
+ continue;
+ }
+ break;
+ }
+
+ if (badness) {
+ debug_badness("==> restoring best_cfg\n");
+ *cfg = *best_cfg;
+ fill_and_eval_dacs(codec, best_wired, best_mio);
+ }
+ debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n",
+ cfg->line_out_type, best_wired, best_mio);
+ debug_show_configs(codec, cfg);
+
+ if (cfg->line_out_pins[0]) {
+ struct nid_path *path;
+ path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]);
+ if (path)
+ spec->vmaster_nid = look_for_out_vol_nid(codec, path);
+ if (spec->vmaster_nid) {
+ snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
+ HDA_OUTPUT, spec->vmaster_tlv);
+ if (spec->dac_min_mute)
+ spec->vmaster_tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] |= TLV_DB_SCALE_MUTE;
+ }
+ }
+
+ /* set initial pinctl targets */
+ if (spec->prefer_hp_amp || cfg->line_out_type == AUTO_PIN_HP_OUT)
+ val = PIN_HP;
+ else
+ val = PIN_OUT;
+ set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins, val);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ set_pin_targets(codec, cfg->hp_outs, cfg->hp_pins, PIN_HP);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ val = spec->prefer_hp_amp ? PIN_HP : PIN_OUT;
+ set_pin_targets(codec, cfg->speaker_outs,
+ cfg->speaker_pins, val);
+ }
+
+ /* clear indep_hp flag if not available */
+ if (spec->indep_hp && !indep_hp_possible(codec))
+ spec->indep_hp = 0;
+
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int create_multi_out_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i, err, noutputs;
+
+ noutputs = cfg->line_outs;
+ if (spec->multi_ios > 0 && cfg->line_outs < 3)
+ noutputs += spec->multi_ios;
+
+ for (i = 0; i < noutputs; i++) {
+ const char *name;
+ int index;
+ struct nid_path *path;
+
+ path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]);
+ if (!path)
+ continue;
+
+ name = get_line_out_pfx(codec, i, &index, NID_PATH_VOL_CTL);
+ if (!name || !strcmp(name, "CLFE")) {
+ /* Center/LFE */
+ err = add_vol_ctl(codec, "Center", 0, 1, path);
+ if (err < 0)
+ return err;
+ err = add_vol_ctl(codec, "LFE", 0, 2, path);
+ if (err < 0)
+ return err;
+ } else {
+ err = add_stereo_vol(codec, name, index, path);
+ if (err < 0)
+ return err;
+ }
+
+ name = get_line_out_pfx(codec, i, &index, NID_PATH_MUTE_CTL);
+ if (!name || !strcmp(name, "CLFE")) {
+ err = add_sw_ctl(codec, "Center", 0, 1, path);
+ if (err < 0)
+ return err;
+ err = add_sw_ctl(codec, "LFE", 0, 2, path);
+ if (err < 0)
+ return err;
+ } else {
+ err = add_stereo_sw(codec, name, index, path);
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+static int create_extra_out(struct hda_codec *codec, int path_idx,
+ const char *pfx, int cidx)
+{
+ struct nid_path *path;
+ int err;
+
+ path = snd_hda_get_path_from_idx(codec, path_idx);
+ if (!path)
+ return 0;
+ err = add_stereo_vol(codec, pfx, cidx, path);
+ if (err < 0)
+ return err;
+ err = add_stereo_sw(codec, pfx, cidx, path);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/* add playback controls for speaker and HP outputs */
+static int create_extra_outs(struct hda_codec *codec, int num_pins,
+ const int *paths, const char *pfx)
+{
+ int i;
+
+ for (i = 0; i < num_pins; i++) {
+ const char *name;
+ char tmp[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int err, idx = 0;
+
+ if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker"))
+ name = "Bass Speaker";
+ else if (num_pins >= 3) {
+ snprintf(tmp, sizeof(tmp), "%s %s",
+ pfx, channel_name[i]);
+ name = tmp;
+ } else {
+ name = pfx;
+ idx = i;
+ }
+ err = create_extra_out(codec, paths[i], name, idx);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int create_hp_out_ctls(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return create_extra_outs(codec, spec->autocfg.hp_outs,
+ spec->hp_paths,
+ "Headphone");
+}
+
+static int create_speaker_out_ctls(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return create_extra_outs(codec, spec->autocfg.speaker_outs,
+ spec->speaker_paths,
+ "Speaker");
+}
+
+/*
+ * independent HP controls
+ */
+
+static void call_hp_automute(struct hda_codec *codec,
+ struct hda_jack_callback *jack);
+static int indep_hp_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
+}
+
+static int indep_hp_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = spec->indep_hp_enabled;
+ return 0;
+}
+
+static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
+ int nomix_path_idx, int mix_path_idx,
+ int out_type);
+
+static int indep_hp_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ unsigned int select = ucontrol->value.enumerated.item[0];
+ int ret = 0;
+
+ guard(mutex)(&spec->pcm_mutex);
+ if (spec->active_streams)
+ return -EBUSY;
+
+ if (spec->indep_hp_enabled != select) {
+ hda_nid_t *dacp;
+ if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+ dacp = &spec->private_dac_nids[0];
+ else
+ dacp = &spec->multiout.hp_out_nid[0];
+
+ /* update HP aamix paths in case it conflicts with indep HP */
+ if (spec->have_aamix_ctl) {
+ if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+ update_aamix_paths(codec, spec->aamix_mode,
+ spec->out_paths[0],
+ spec->aamix_out_paths[0],
+ spec->autocfg.line_out_type);
+ else
+ update_aamix_paths(codec, spec->aamix_mode,
+ spec->hp_paths[0],
+ spec->aamix_out_paths[1],
+ AUTO_PIN_HP_OUT);
+ }
+
+ spec->indep_hp_enabled = select;
+ if (spec->indep_hp_enabled)
+ *dacp = 0;
+ else
+ *dacp = spec->alt_dac_nid;
+
+ call_hp_automute(codec, NULL);
+ ret = 1;
+ }
+ return ret;
+}
+
+static const struct snd_kcontrol_new indep_hp_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Independent HP",
+ .info = indep_hp_info,
+ .get = indep_hp_get,
+ .put = indep_hp_put,
+};
+
+
+static int create_indep_hp_ctls(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t dac;
+
+ if (!spec->indep_hp)
+ return 0;
+ if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+ dac = spec->multiout.dac_nids[0];
+ else
+ dac = spec->multiout.hp_out_nid[0];
+ if (!dac) {
+ spec->indep_hp = 0;
+ return 0;
+ }
+
+ spec->indep_hp_enabled = false;
+ spec->alt_dac_nid = dac;
+ if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl))
+ return -ENOMEM;
+ return 0;
+}
+
+/*
+ * channel mode enum control
+ */
+
+static int ch_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ int chs;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = spec->multi_ios + 1;
+ if (uinfo->value.enumerated.item > spec->multi_ios)
+ uinfo->value.enumerated.item = spec->multi_ios;
+ chs = uinfo->value.enumerated.item * 2 + spec->min_channel_count;
+ sprintf(uinfo->value.enumerated.name, "%dch", chs);
+ return 0;
+}
+
+static int ch_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] =
+ (spec->ext_channel_count - spec->min_channel_count) / 2;
+ return 0;
+}
+
+static inline struct nid_path *
+get_multiio_path(struct hda_codec *codec, int idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_get_path_from_idx(codec,
+ spec->out_paths[spec->autocfg.line_outs + idx]);
+}
+
+static void update_automute_all(struct hda_codec *codec);
+
+/* Default value to be passed as aamix argument for snd_hda_activate_path();
+ * used for output paths
+ */
+static bool aamix_default(struct hda_gen_spec *spec)
+{
+ return !spec->have_aamix_ctl || spec->aamix_mode;
+}
+
+static int set_multi_io(struct hda_codec *codec, int idx, bool output)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t nid = spec->multi_io[idx].pin;
+ struct nid_path *path;
+
+ path = get_multiio_path(codec, idx);
+ if (!path)
+ return -EINVAL;
+
+ if (path->active == output)
+ return 0;
+
+ if (output) {
+ set_pin_target(codec, nid, PIN_OUT, true);
+ snd_hda_activate_path(codec, path, true, aamix_default(spec));
+ set_pin_eapd(codec, nid, true);
+ } else {
+ set_pin_eapd(codec, nid, false);
+ snd_hda_activate_path(codec, path, false, aamix_default(spec));
+ set_pin_target(codec, nid, spec->multi_io[idx].ctl_in, true);
+ path_power_down_sync(codec, path);
+ }
+
+ /* update jack retasking in case it modifies any of them */
+ update_automute_all(codec);
+
+ return 0;
+}
+
+static int ch_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ int i, ch;
+
+ ch = ucontrol->value.enumerated.item[0];
+ if (ch < 0 || ch > spec->multi_ios)
+ return -EINVAL;
+ if (ch == (spec->ext_channel_count - spec->min_channel_count) / 2)
+ return 0;
+ spec->ext_channel_count = ch * 2 + spec->min_channel_count;
+ for (i = 0; i < spec->multi_ios; i++)
+ set_multi_io(codec, i, i < ch);
+ spec->multiout.max_channels = max(spec->ext_channel_count,
+ spec->const_channel_count);
+ if (spec->need_dac_fix)
+ spec->multiout.num_dacs = spec->multiout.max_channels / 2;
+ return 1;
+}
+
+static const struct snd_kcontrol_new channel_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Channel Mode",
+ .info = ch_mode_info,
+ .get = ch_mode_get,
+ .put = ch_mode_put,
+};
+
+static int create_multi_channel_mode(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (spec->multi_ios > 0) {
+ if (!snd_hda_gen_add_kctl(spec, NULL, &channel_mode_enum))
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/*
+ * aamix loopback enable/disable switch
+ */
+
+#define loopback_mixing_info indep_hp_info
+
+static int loopback_mixing_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = spec->aamix_mode;
+ return 0;
+}
+
+static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
+ int nomix_path_idx, int mix_path_idx,
+ int out_type)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *nomix_path, *mix_path;
+
+ nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx);
+ mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx);
+ if (!nomix_path || !mix_path)
+ return;
+
+ /* if HP aamix path is driven from a different DAC and the
+ * independent HP mode is ON, can't turn on aamix path
+ */
+ if (out_type == AUTO_PIN_HP_OUT && spec->indep_hp_enabled &&
+ mix_path->path[0] != spec->alt_dac_nid)
+ do_mix = false;
+
+ if (do_mix) {
+ snd_hda_activate_path(codec, nomix_path, false, true);
+ snd_hda_activate_path(codec, mix_path, true, true);
+ path_power_down_sync(codec, nomix_path);
+ } else {
+ snd_hda_activate_path(codec, mix_path, false, false);
+ snd_hda_activate_path(codec, nomix_path, true, false);
+ path_power_down_sync(codec, mix_path);
+ }
+}
+
+/* re-initialize the output paths; only called from loopback_mixing_put() */
+static void update_output_paths(struct hda_codec *codec, int num_outs,
+ const int *paths)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+ int i;
+
+ for (i = 0; i < num_outs; i++) {
+ path = snd_hda_get_path_from_idx(codec, paths[i]);
+ if (path)
+ snd_hda_activate_path(codec, path, path->active,
+ spec->aamix_mode);
+ }
+}
+
+static int loopback_mixing_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int val = ucontrol->value.enumerated.item[0];
+
+ if (val == spec->aamix_mode)
+ return 0;
+ spec->aamix_mode = val;
+ if (has_aamix_out_paths(spec)) {
+ update_aamix_paths(codec, val, spec->out_paths[0],
+ spec->aamix_out_paths[0],
+ cfg->line_out_type);
+ update_aamix_paths(codec, val, spec->hp_paths[0],
+ spec->aamix_out_paths[1],
+ AUTO_PIN_HP_OUT);
+ update_aamix_paths(codec, val, spec->speaker_paths[0],
+ spec->aamix_out_paths[2],
+ AUTO_PIN_SPEAKER_OUT);
+ } else {
+ update_output_paths(codec, cfg->line_outs, spec->out_paths);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ update_output_paths(codec, cfg->hp_outs, spec->hp_paths);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ update_output_paths(codec, cfg->speaker_outs,
+ spec->speaker_paths);
+ }
+ return 1;
+}
+
+static const struct snd_kcontrol_new loopback_mixing_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Loopback Mixing",
+ .info = loopback_mixing_info,
+ .get = loopback_mixing_get,
+ .put = loopback_mixing_put,
+};
+
+static int create_loopback_mixing_ctl(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (!spec->mixer_nid)
+ return 0;
+ if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum))
+ return -ENOMEM;
+ spec->have_aamix_ctl = 1;
+ return 0;
+}
+
+/*
+ * shared headphone/mic handling
+ */
+
+static void call_update_outputs(struct hda_codec *codec);
+
+/* for shared I/O, change the pin-control accordingly */
+static void update_hp_mic(struct hda_codec *codec, int adc_mux, bool force)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ bool as_mic;
+ unsigned int val;
+ hda_nid_t pin;
+
+ pin = spec->hp_mic_pin;
+ as_mic = spec->cur_mux[adc_mux] == spec->hp_mic_mux_idx;
+
+ if (!force) {
+ val = snd_hda_codec_get_pin_target(codec, pin);
+ if (as_mic) {
+ if (val & PIN_IN)
+ return;
+ } else {
+ if (val & PIN_OUT)
+ return;
+ }
+ }
+
+ val = snd_hda_get_default_vref(codec, pin);
+ /* if the HP pin doesn't support VREF and the codec driver gives an
+ * alternative pin, set up the VREF on that pin instead
+ */
+ if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) {
+ const hda_nid_t vref_pin = spec->shared_mic_vref_pin;
+ unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin);
+ if (vref_val != AC_PINCTL_VREF_HIZ)
+ snd_hda_set_pin_ctl_cache(codec, vref_pin,
+ PIN_IN | (as_mic ? vref_val : 0));
+ }
+
+ if (!spec->hp_mic_jack_modes) {
+ if (as_mic)
+ val |= PIN_IN;
+ else
+ val = PIN_HP;
+ set_pin_target(codec, pin, val, true);
+ call_hp_automute(codec, NULL);
+ }
+}
+
+/* create a shared input with the headphone out */
+static int create_hp_mic(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int defcfg;
+ hda_nid_t nid;
+
+ if (!spec->hp_mic) {
+ if (spec->suppress_hp_mic_detect)
+ return 0;
+ /* automatic detection: only if no input or a single internal
+ * input pin is found, try to detect the shared hp/mic
+ */
+ if (cfg->num_inputs > 1)
+ return 0;
+ else if (cfg->num_inputs == 1) {
+ defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin);
+ if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT)
+ return 0;
+ }
+ }
+
+ spec->hp_mic = 0; /* clear once */
+ if (cfg->num_inputs >= AUTO_CFG_MAX_INS)
+ return 0;
+
+ nid = 0;
+ if (cfg->line_out_type == AUTO_PIN_HP_OUT && cfg->line_outs > 0)
+ nid = cfg->line_out_pins[0];
+ else if (cfg->hp_outs > 0)
+ nid = cfg->hp_pins[0];
+ if (!nid)
+ return 0;
+
+ if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN))
+ return 0; /* no input */
+
+ cfg->inputs[cfg->num_inputs].pin = nid;
+ cfg->inputs[cfg->num_inputs].type = AUTO_PIN_MIC;
+ cfg->inputs[cfg->num_inputs].is_headphone_mic = 1;
+ cfg->num_inputs++;
+ spec->hp_mic = 1;
+ spec->hp_mic_pin = nid;
+ /* we can't handle auto-mic together with HP-mic */
+ spec->suppress_auto_mic = 1;
+ codec_dbg(codec, "Enable shared I/O jack on NID 0x%x\n", nid);
+ return 0;
+}
+
+/*
+ * output jack mode
+ */
+
+static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin);
+
+static const char * const out_jack_texts[] = {
+ "Line Out", "Headphone Out",
+};
+
+static int out_jack_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ return snd_hda_enum_helper_info(kcontrol, uinfo, 2, out_jack_texts);
+}
+
+static int out_jack_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ if (snd_hda_codec_get_pin_target(codec, nid) == PIN_HP)
+ ucontrol->value.enumerated.item[0] = 1;
+ else
+ ucontrol->value.enumerated.item[0] = 0;
+ return 0;
+}
+
+static int out_jack_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned int val;
+
+ val = ucontrol->value.enumerated.item[0] ? PIN_HP : PIN_OUT;
+ if (snd_hda_codec_get_pin_target(codec, nid) == val)
+ return 0;
+ snd_hda_set_pin_ctl_cache(codec, nid, val);
+ return 1;
+}
+
+static const struct snd_kcontrol_new out_jack_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = out_jack_mode_info,
+ .get = out_jack_mode_get,
+ .put = out_jack_mode_put,
+};
+
+static bool find_kctl_name(struct hda_codec *codec, const char *name, int idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct snd_kcontrol_new *kctl;
+ int i;
+
+ snd_array_for_each(&spec->kctls, i, kctl) {
+ if (!strcmp(kctl->name, name) && kctl->index == idx)
+ return true;
+ }
+ return false;
+}
+
+static void get_jack_mode_name(struct hda_codec *codec, hda_nid_t pin,
+ char *name, size_t name_len)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int idx = 0;
+
+ snd_hda_get_pin_label(codec, pin, &spec->autocfg, name, name_len, &idx);
+ strlcat(name, " Jack Mode", name_len);
+
+ for (; find_kctl_name(codec, name, idx); idx++)
+ ;
+}
+
+static int get_out_jack_num_items(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->add_jack_modes) {
+ unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
+ if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV))
+ return 2;
+ }
+ return 1;
+}
+
+static int create_out_jack_modes(struct hda_codec *codec, int num_pins,
+ hda_nid_t *pins)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < num_pins; i++) {
+ hda_nid_t pin = pins[i];
+ if (pin == spec->hp_mic_pin)
+ continue;
+ if (get_out_jack_num_items(codec, pin) > 1) {
+ struct snd_kcontrol_new *knew;
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ get_jack_mode_name(codec, pin, name, sizeof(name));
+ knew = snd_hda_gen_add_kctl(spec, name,
+ &out_jack_mode_enum);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = pin;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * input jack mode
+ */
+
+/* from AC_PINCTL_VREF_HIZ to AC_PINCTL_VREF_100 */
+#define NUM_VREFS 6
+
+static const char * const vref_texts[NUM_VREFS] = {
+ "Line In", "Mic 50pc Bias", "Mic 0V Bias",
+ "", "Mic 80pc Bias", "Mic 100pc Bias"
+};
+
+static unsigned int get_vref_caps(struct hda_codec *codec, hda_nid_t pin)
+{
+ unsigned int pincap;
+
+ pincap = snd_hda_query_pin_caps(codec, pin);
+ pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+ /* filter out unusual vrefs */
+ pincap &= ~(AC_PINCAP_VREF_GRD | AC_PINCAP_VREF_100);
+ return pincap;
+}
+
+/* convert from the enum item index to the vref ctl index (0=HIZ, 1=50%...) */
+static int get_vref_idx(unsigned int vref_caps, unsigned int item_idx)
+{
+ unsigned int i, n = 0;
+
+ for (i = 0; i < NUM_VREFS; i++) {
+ if (vref_caps & (1 << i)) {
+ if (n == item_idx)
+ return i;
+ n++;
+ }
+ }
+ return 0;
+}
+
+/* convert back from the vref ctl index to the enum item index */
+static int cvt_from_vref_idx(unsigned int vref_caps, unsigned int idx)
+{
+ unsigned int i, n = 0;
+
+ for (i = 0; i < NUM_VREFS; i++) {
+ if (i == idx)
+ return n;
+ if (vref_caps & (1 << i))
+ n++;
+ }
+ return 0;
+}
+
+static int in_jack_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+
+ snd_hda_enum_helper_info(kcontrol, uinfo, hweight32(vref_caps),
+ vref_texts);
+ /* set the right text */
+ strscpy(uinfo->value.enumerated.name,
+ vref_texts[get_vref_idx(vref_caps, uinfo->value.enumerated.item)]);
+ return 0;
+}
+
+static int in_jack_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ unsigned int idx;
+
+ idx = snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_VREFEN;
+ ucontrol->value.enumerated.item[0] = cvt_from_vref_idx(vref_caps, idx);
+ return 0;
+}
+
+static int in_jack_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ unsigned int val, idx;
+
+ val = snd_hda_codec_get_pin_target(codec, nid);
+ idx = cvt_from_vref_idx(vref_caps, val & AC_PINCTL_VREFEN);
+ if (idx == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ val &= ~AC_PINCTL_VREFEN;
+ val |= get_vref_idx(vref_caps, ucontrol->value.enumerated.item[0]);
+ snd_hda_set_pin_ctl_cache(codec, nid, val);
+ return 1;
+}
+
+static const struct snd_kcontrol_new in_jack_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = in_jack_mode_info,
+ .get = in_jack_mode_get,
+ .put = in_jack_mode_put,
+};
+
+static int get_in_jack_num_items(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int nitems = 0;
+ if (spec->add_jack_modes)
+ nitems = hweight32(get_vref_caps(codec, pin));
+ return nitems ? nitems : 1;
+}
+
+static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct snd_kcontrol_new *knew;
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ unsigned int defcfg;
+
+ if (pin == spec->hp_mic_pin)
+ return 0; /* already done in create_out_jack_mode() */
+
+ /* no jack mode for fixed pins */
+ defcfg = snd_hda_codec_get_pincfg(codec, pin);
+ if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT)
+ return 0;
+
+ /* no multiple vref caps? */
+ if (get_in_jack_num_items(codec, pin) <= 1)
+ return 0;
+
+ get_jack_mode_name(codec, pin, name, sizeof(name));
+ knew = snd_hda_gen_add_kctl(spec, name, &in_jack_mode_enum);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = pin;
+ return 0;
+}
+
+/*
+ * HP/mic shared jack mode
+ */
+static int hp_mic_jack_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ int out_jacks = get_out_jack_num_items(codec, nid);
+ int in_jacks = get_in_jack_num_items(codec, nid);
+ const char *text = NULL;
+ int idx;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = out_jacks + in_jacks;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ idx = uinfo->value.enumerated.item;
+ if (idx < out_jacks) {
+ if (out_jacks > 1)
+ text = out_jack_texts[idx];
+ else
+ text = "Headphone Out";
+ } else {
+ idx -= out_jacks;
+ if (in_jacks > 1) {
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ text = vref_texts[get_vref_idx(vref_caps, idx)];
+ } else
+ text = "Mic In";
+ }
+
+ strscpy(uinfo->value.enumerated.name, text);
+ return 0;
+}
+
+static int get_cur_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t nid)
+{
+ int out_jacks = get_out_jack_num_items(codec, nid);
+ int in_jacks = get_in_jack_num_items(codec, nid);
+ unsigned int val = snd_hda_codec_get_pin_target(codec, nid);
+ int idx = 0;
+
+ if (val & PIN_OUT) {
+ if (out_jacks > 1 && val == PIN_HP)
+ idx = 1;
+ } else if (val & PIN_IN) {
+ idx = out_jacks;
+ if (in_jacks > 1) {
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ val &= AC_PINCTL_VREFEN;
+ idx += cvt_from_vref_idx(vref_caps, val);
+ }
+ }
+ return idx;
+}
+
+static int hp_mic_jack_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ ucontrol->value.enumerated.item[0] =
+ get_cur_hp_mic_jack_mode(codec, nid);
+ return 0;
+}
+
+static int hp_mic_jack_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ int out_jacks = get_out_jack_num_items(codec, nid);
+ int in_jacks = get_in_jack_num_items(codec, nid);
+ unsigned int val, oldval, idx;
+
+ oldval = get_cur_hp_mic_jack_mode(codec, nid);
+ idx = ucontrol->value.enumerated.item[0];
+ if (oldval == idx)
+ return 0;
+
+ if (idx < out_jacks) {
+ if (out_jacks > 1)
+ val = idx ? PIN_HP : PIN_OUT;
+ else
+ val = PIN_HP;
+ } else {
+ idx -= out_jacks;
+ if (in_jacks > 1) {
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ val = snd_hda_codec_get_pin_target(codec, nid);
+ val &= ~(AC_PINCTL_VREFEN | PIN_HP);
+ val |= get_vref_idx(vref_caps, idx) | PIN_IN;
+ } else
+ val = snd_hda_get_default_vref(codec, nid) | PIN_IN;
+ }
+ snd_hda_set_pin_ctl_cache(codec, nid, val);
+ call_hp_automute(codec, NULL);
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new hp_mic_jack_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = hp_mic_jack_mode_info,
+ .get = hp_mic_jack_mode_get,
+ .put = hp_mic_jack_mode_put,
+};
+
+static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct snd_kcontrol_new *knew;
+
+ knew = snd_hda_gen_add_kctl(spec, "Headphone Mic Jack Mode",
+ &hp_mic_jack_mode_enum);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = pin;
+ spec->hp_mic_jack_modes = 1;
+ return 0;
+}
+
+/*
+ * Parse input paths
+ */
+
+/* add the powersave loopback-list entry */
+static int add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx)
+{
+ struct hda_amp_list *list;
+
+ list = snd_array_new(&spec->loopback_list);
+ if (!list)
+ return -ENOMEM;
+ list->nid = mix;
+ list->dir = HDA_INPUT;
+ list->idx = idx;
+ spec->loopback.amplist = spec->loopback_list.list;
+ return 0;
+}
+
+/* return true if either a volume or a mute amp is found for the given
+ * aamix path; the amp has to be either in the mixer node or its direct leaf
+ */
+static bool look_for_mix_leaf_ctls(struct hda_codec *codec, hda_nid_t mix_nid,
+ hda_nid_t pin, unsigned int *mix_val,
+ unsigned int *mute_val)
+{
+ int idx, num_conns;
+ const hda_nid_t *list;
+ hda_nid_t nid;
+
+ idx = snd_hda_get_conn_index(codec, mix_nid, pin, true);
+ if (idx < 0)
+ return false;
+
+ *mix_val = *mute_val = 0;
+ if (nid_has_volume(codec, mix_nid, HDA_INPUT))
+ *mix_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
+ if (nid_has_mute(codec, mix_nid, HDA_INPUT))
+ *mute_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
+ if (*mix_val && *mute_val)
+ return true;
+
+ /* check leaf node */
+ num_conns = snd_hda_get_conn_list(codec, mix_nid, &list);
+ if (num_conns < idx)
+ return false;
+ nid = list[idx];
+ if (!*mix_val && nid_has_volume(codec, nid, HDA_OUTPUT) &&
+ !is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_VOL_CTL))
+ *mix_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ if (!*mute_val && nid_has_mute(codec, nid, HDA_OUTPUT) &&
+ !is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_MUTE_CTL))
+ *mute_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+
+ return *mix_val || *mute_val;
+}
+
+/* create input playback/capture controls for the given pin */
+static int new_analog_input(struct hda_codec *codec, int input_idx,
+ hda_nid_t pin, const char *ctlname, int ctlidx,
+ hda_nid_t mix_nid)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+ unsigned int mix_val, mute_val;
+ int err, idx;
+
+ if (!look_for_mix_leaf_ctls(codec, mix_nid, pin, &mix_val, &mute_val))
+ return 0;
+
+ path = snd_hda_add_new_path(codec, pin, mix_nid, 0);
+ if (!path)
+ return -EINVAL;
+ print_nid_path(codec, "loopback", path);
+ spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path);
+
+ idx = path->idx[path->depth - 1];
+ if (mix_val) {
+ err = __add_pb_vol_ctrl(spec, HDA_CTL_WIDGET_VOL, ctlname, ctlidx, mix_val);
+ if (err < 0)
+ return err;
+ path->ctls[NID_PATH_VOL_CTL] = mix_val;
+ }
+
+ if (mute_val) {
+ err = __add_pb_sw_ctrl(spec, HDA_CTL_WIDGET_MUTE, ctlname, ctlidx, mute_val);
+ if (err < 0)
+ return err;
+ path->ctls[NID_PATH_MUTE_CTL] = mute_val;
+ }
+
+ path->active = true;
+ path->stream_enabled = true; /* no DAC/ADC involved */
+ err = add_loopback_list(spec, mix_nid, idx);
+ if (err < 0)
+ return err;
+
+ if (spec->mixer_nid != spec->mixer_merge_nid &&
+ !spec->loopback_merge_path) {
+ path = snd_hda_add_new_path(codec, spec->mixer_nid,
+ spec->mixer_merge_nid, 0);
+ if (path) {
+ print_nid_path(codec, "loopback-merge", path);
+ path->active = true;
+ path->pin_fixed = true; /* static route */
+ path->stream_enabled = true; /* no DAC/ADC involved */
+ spec->loopback_merge_path =
+ snd_hda_get_path_idx(codec, path);
+ }
+ }
+
+ return 0;
+}
+
+static int is_input_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
+ return (pincap & AC_PINCAP_IN) != 0;
+}
+
+/* Parse the codec tree and retrieve ADCs */
+static int fill_adc_nids(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t nid;
+ hda_nid_t *adc_nids = spec->adc_nids;
+ int max_nums = ARRAY_SIZE(spec->adc_nids);
+ int nums = 0;
+
+ for_each_hda_codec_node(nid, codec) {
+ unsigned int caps = get_wcaps(codec, nid);
+ int type = get_wcaps_type(caps);
+
+ if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL))
+ continue;
+ adc_nids[nums] = nid;
+ if (++nums >= max_nums)
+ break;
+ }
+ spec->num_adc_nids = nums;
+
+ /* copy the detected ADCs to all_adcs[] */
+ spec->num_all_adcs = nums;
+ memcpy(spec->all_adcs, spec->adc_nids, nums * sizeof(hda_nid_t));
+
+ return nums;
+}
+
+/* filter out invalid adc_nids that don't give all active input pins;
+ * if needed, check whether dynamic ADC-switching is available
+ */
+static int check_dyn_adc_switch(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->input_mux;
+ unsigned int ok_bits;
+ int i, n, nums;
+
+ nums = 0;
+ ok_bits = 0;
+ for (n = 0; n < spec->num_adc_nids; n++) {
+ for (i = 0; i < imux->num_items; i++) {
+ if (!spec->input_paths[i][n])
+ break;
+ }
+ if (i >= imux->num_items) {
+ ok_bits |= (1 << n);
+ nums++;
+ }
+ }
+
+ if (!ok_bits) {
+ /* check whether ADC-switch is possible */
+ for (i = 0; i < imux->num_items; i++) {
+ for (n = 0; n < spec->num_adc_nids; n++) {
+ if (spec->input_paths[i][n]) {
+ spec->dyn_adc_idx[i] = n;
+ break;
+ }
+ }
+ }
+
+ codec_dbg(codec, "enabling ADC switching\n");
+ spec->dyn_adc_switch = 1;
+ } else if (nums != spec->num_adc_nids) {
+ /* shrink the invalid adcs and input paths */
+ nums = 0;
+ for (n = 0; n < spec->num_adc_nids; n++) {
+ if (!(ok_bits & (1 << n)))
+ continue;
+ if (n != nums) {
+ spec->adc_nids[nums] = spec->adc_nids[n];
+ for (i = 0; i < imux->num_items; i++) {
+ invalidate_nid_path(codec,
+ spec->input_paths[i][nums]);
+ spec->input_paths[i][nums] =
+ spec->input_paths[i][n];
+ spec->input_paths[i][n] = 0;
+ }
+ }
+ nums++;
+ }
+ spec->num_adc_nids = nums;
+ }
+
+ if (imux->num_items == 1 ||
+ (imux->num_items == 2 && spec->hp_mic)) {
+ codec_dbg(codec, "reducing to a single ADC\n");
+ spec->num_adc_nids = 1; /* reduce to a single ADC */
+ }
+
+ /* single index for individual volumes ctls */
+ if (!spec->dyn_adc_switch && spec->multi_cap_vol)
+ spec->num_adc_nids = 1;
+
+ return 0;
+}
+
+/* parse capture source paths from the given pin and create imux items */
+static int parse_capture_source(struct hda_codec *codec, hda_nid_t pin,
+ int cfg_idx, int num_adcs,
+ const char *label, int anchor)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->input_mux;
+ int imux_idx = imux->num_items;
+ bool imux_added = false;
+ int c;
+
+ for (c = 0; c < num_adcs; c++) {
+ struct nid_path *path;
+ hda_nid_t adc = spec->adc_nids[c];
+
+ if (!is_reachable_path(codec, pin, adc))
+ continue;
+ path = snd_hda_add_new_path(codec, pin, adc, anchor);
+ if (!path)
+ continue;
+ print_nid_path(codec, "input", path);
+ spec->input_paths[imux_idx][c] =
+ snd_hda_get_path_idx(codec, path);
+
+ if (!imux_added) {
+ if (spec->hp_mic_pin == pin)
+ spec->hp_mic_mux_idx = imux->num_items;
+ spec->imux_pins[imux->num_items] = pin;
+ snd_hda_add_imux_item(codec, imux, label, cfg_idx, NULL);
+ imux_added = true;
+ if (spec->dyn_adc_switch)
+ spec->dyn_adc_idx[imux_idx] = c;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * create playback/capture controls for input pins
+ */
+
+/* fill the label for each input at first */
+static int fill_input_pin_labels(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t pin = cfg->inputs[i].pin;
+ const char *label;
+ int j, idx;
+
+ if (!is_input_pin(codec, pin))
+ continue;
+
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ idx = 0;
+ for (j = i - 1; j >= 0; j--) {
+ if (spec->input_labels[j] &&
+ !strcmp(spec->input_labels[j], label)) {
+ idx = spec->input_label_idxs[j] + 1;
+ break;
+ }
+ }
+
+ spec->input_labels[i] = label;
+ spec->input_label_idxs[i] = idx;
+ }
+
+ return 0;
+}
+
+#define CFG_IDX_MIX 99 /* a dummy cfg->input idx for stereo mix */
+
+static int create_input_ctls(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t mixer = spec->mixer_nid;
+ int num_adcs;
+ int i, err;
+ unsigned int val;
+
+ num_adcs = fill_adc_nids(codec);
+ if (num_adcs < 0)
+ return 0;
+
+ err = fill_input_pin_labels(codec);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t pin;
+
+ pin = cfg->inputs[i].pin;
+ if (!is_input_pin(codec, pin))
+ continue;
+
+ val = PIN_IN;
+ if (cfg->inputs[i].type == AUTO_PIN_MIC)
+ val |= snd_hda_get_default_vref(codec, pin);
+ if (pin != spec->hp_mic_pin &&
+ !snd_hda_codec_get_pin_target(codec, pin))
+ set_pin_target(codec, pin, val, false);
+
+ if (mixer) {
+ if (is_reachable_path(codec, pin, mixer)) {
+ err = new_analog_input(codec, i, pin,
+ spec->input_labels[i],
+ spec->input_label_idxs[i],
+ mixer);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ err = parse_capture_source(codec, pin, i, num_adcs,
+ spec->input_labels[i], -mixer);
+ if (err < 0)
+ return err;
+
+ if (spec->add_jack_modes) {
+ err = create_in_jack_mode(codec, pin);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ /* add stereo mix when explicitly enabled via hint */
+ if (mixer && spec->add_stereo_mix_input == HDA_HINT_STEREO_MIX_ENABLE) {
+ err = parse_capture_source(codec, mixer, CFG_IDX_MIX, num_adcs,
+ "Stereo Mix", 0);
+ if (err < 0)
+ return err;
+ else
+ spec->suppress_auto_mic = 1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * input source mux
+ */
+
+/* get the input path specified by the given adc and imux indices */
+static struct nid_path *get_input_path(struct hda_codec *codec, int adc_idx, int imux_idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (imux_idx < 0 || imux_idx >= HDA_MAX_NUM_INPUTS) {
+ snd_BUG();
+ return NULL;
+ }
+ if (spec->dyn_adc_switch)
+ adc_idx = spec->dyn_adc_idx[imux_idx];
+ if (adc_idx < 0 || adc_idx >= AUTO_CFG_MAX_INS) {
+ snd_BUG();
+ return NULL;
+ }
+ return snd_hda_get_path_from_idx(codec, spec->input_paths[imux_idx][adc_idx]);
+}
+
+static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
+ unsigned int idx);
+
+static int mux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_input_mux_info(&spec->input_mux, uinfo);
+}
+
+static int mux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ /* the ctls are created at once with multiple counts */
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+ return 0;
+}
+
+static int mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ return mux_select(codec, adc_idx,
+ ucontrol->value.enumerated.item[0]);
+}
+
+static const struct snd_kcontrol_new cap_src_temp = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input Source",
+ .info = mux_enum_info,
+ .get = mux_enum_get,
+ .put = mux_enum_put,
+};
+
+/*
+ * capture volume and capture switch ctls
+ */
+
+typedef int (*put_call_t)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+/* call the given amp update function for all amps in the imux list at once */
+static int cap_put_caller(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol,
+ put_call_t func, int type)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ const struct hda_input_mux *imux;
+ struct nid_path *path;
+ int i, adc_idx, ret, err = 0;
+
+ imux = &spec->input_mux;
+ adc_idx = kcontrol->id.index;
+ scoped_guard(mutex, &codec->control_mutex) {
+ for (i = 0; i < imux->num_items; i++) {
+ path = get_input_path(codec, adc_idx, i);
+ if (!path || !path->ctls[type])
+ continue;
+ kcontrol->private_value = path->ctls[type];
+ ret = func(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ err = 1;
+ }
+ }
+ if (spec->cap_sync_hook)
+ spec->cap_sync_hook(codec, kcontrol, ucontrol);
+ return err;
+}
+
+/* capture volume ctl callbacks */
+#define cap_vol_info snd_hda_mixer_amp_volume_info
+#define cap_vol_get snd_hda_mixer_amp_volume_get
+#define cap_vol_tlv snd_hda_mixer_amp_tlv
+
+static int cap_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return cap_put_caller(kcontrol, ucontrol,
+ snd_hda_mixer_amp_volume_put,
+ NID_PATH_VOL_CTL);
+}
+
+static const struct snd_kcontrol_new cap_vol_temp = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Volume",
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK),
+ .info = cap_vol_info,
+ .get = cap_vol_get,
+ .put = cap_vol_put,
+ .tlv = { .c = cap_vol_tlv },
+};
+
+/* capture switch ctl callbacks */
+#define cap_sw_info snd_ctl_boolean_stereo_info
+#define cap_sw_get snd_hda_mixer_amp_switch_get
+
+static int cap_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return cap_put_caller(kcontrol, ucontrol,
+ snd_hda_mixer_amp_switch_put,
+ NID_PATH_MUTE_CTL);
+}
+
+static const struct snd_kcontrol_new cap_sw_temp = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Switch",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = cap_sw_info,
+ .get = cap_sw_get,
+ .put = cap_sw_put,
+};
+
+static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path)
+{
+ hda_nid_t nid;
+ int i, depth;
+
+ path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0;
+ for (depth = 0; depth < 3; depth++) {
+ if (depth >= path->depth)
+ return -EINVAL;
+ i = path->depth - depth - 1;
+ nid = path->path[i];
+ if (!path->ctls[NID_PATH_VOL_CTL]) {
+ if (nid_has_volume(codec, nid, HDA_OUTPUT))
+ path->ctls[NID_PATH_VOL_CTL] =
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ else if (nid_has_volume(codec, nid, HDA_INPUT)) {
+ int idx = path->idx[i];
+ if (!depth && codec->single_adc_amp)
+ idx = 0;
+ path->ctls[NID_PATH_VOL_CTL] =
+ HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT);
+ }
+ }
+ if (!path->ctls[NID_PATH_MUTE_CTL]) {
+ if (nid_has_mute(codec, nid, HDA_OUTPUT))
+ path->ctls[NID_PATH_MUTE_CTL] =
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ else if (nid_has_mute(codec, nid, HDA_INPUT)) {
+ int idx = path->idx[i];
+ if (!depth && codec->single_adc_amp)
+ idx = 0;
+ path->ctls[NID_PATH_MUTE_CTL] =
+ HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT);
+ }
+ }
+ }
+ return 0;
+}
+
+static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int val;
+ int i;
+
+ if (!spec->inv_dmic_split)
+ return false;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].pin != nid)
+ continue;
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ return false;
+ val = snd_hda_codec_get_pincfg(codec, nid);
+ return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT;
+ }
+ return false;
+}
+
+/* capture switch put callback for a single control with hook call */
+static int cap_single_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ int ret;
+
+ ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ if (spec->cap_sync_hook)
+ spec->cap_sync_hook(codec, kcontrol, ucontrol);
+
+ return ret;
+}
+
+static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
+ int idx, bool is_switch, unsigned int ctl,
+ bool inv_dmic)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ char tmpname[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int type = is_switch ? HDA_CTL_WIDGET_MUTE : HDA_CTL_WIDGET_VOL;
+ const char *sfx = is_switch ? "Switch" : "Volume";
+ unsigned int chs = inv_dmic ? 1 : 3;
+ struct snd_kcontrol_new *knew;
+
+ if (!ctl)
+ return 0;
+
+ if (label)
+ snprintf(tmpname, sizeof(tmpname),
+ "%s Capture %s", label, sfx);
+ else
+ snprintf(tmpname, sizeof(tmpname),
+ "Capture %s", sfx);
+ knew = add_control(spec, type, tmpname, idx,
+ amp_val_replace_channels(ctl, chs));
+ if (!knew)
+ return -ENOMEM;
+ if (is_switch) {
+ knew->put = cap_single_sw_put;
+ if (spec->mic_mute_led)
+ knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED;
+ }
+ if (!inv_dmic)
+ return 0;
+
+ /* Make independent right kcontrol */
+ if (label)
+ snprintf(tmpname, sizeof(tmpname),
+ "Inverted %s Capture %s", label, sfx);
+ else
+ snprintf(tmpname, sizeof(tmpname),
+ "Inverted Capture %s", sfx);
+ knew = add_control(spec, type, tmpname, idx,
+ amp_val_replace_channels(ctl, 2));
+ if (!knew)
+ return -ENOMEM;
+ if (is_switch) {
+ knew->put = cap_single_sw_put;
+ if (spec->mic_mute_led)
+ knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED;
+ }
+ return 0;
+}
+
+/* create single (and simple) capture volume and switch controls */
+static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx,
+ unsigned int vol_ctl, unsigned int sw_ctl,
+ bool inv_dmic)
+{
+ int err;
+ err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic);
+ if (err < 0)
+ return err;
+ err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/* create bound capture volume and switch controls */
+static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx,
+ unsigned int vol_ctl, unsigned int sw_ctl)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct snd_kcontrol_new *knew;
+
+ if (vol_ctl) {
+ knew = snd_hda_gen_add_kctl(spec, NULL, &cap_vol_temp);
+ if (!knew)
+ return -ENOMEM;
+ knew->index = idx;
+ knew->private_value = vol_ctl;
+ knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+ }
+ if (sw_ctl) {
+ knew = snd_hda_gen_add_kctl(spec, NULL, &cap_sw_temp);
+ if (!knew)
+ return -ENOMEM;
+ knew->index = idx;
+ knew->private_value = sw_ctl;
+ knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+ if (spec->mic_mute_led)
+ knew->access |= SNDRV_CTL_ELEM_ACCESS_MIC_LED;
+ }
+ return 0;
+}
+
+/* return the vol ctl when used first in the imux list */
+static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type)
+{
+ struct nid_path *path;
+ unsigned int ctl;
+ int i;
+
+ path = get_input_path(codec, 0, idx);
+ if (!path)
+ return 0;
+ ctl = path->ctls[type];
+ if (!ctl)
+ return 0;
+ for (i = 0; i < idx - 1; i++) {
+ path = get_input_path(codec, 0, i);
+ if (path && path->ctls[type] == ctl)
+ return 0;
+ }
+ return ctl;
+}
+
+/* create individual capture volume and switch controls per input */
+static int create_multi_cap_vol_ctl(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->input_mux;
+ int i, err, type;
+
+ for (i = 0; i < imux->num_items; i++) {
+ bool inv_dmic;
+ int idx;
+
+ idx = imux->items[i].index;
+ if (idx >= spec->autocfg.num_inputs)
+ continue;
+ inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]);
+
+ for (type = 0; type < 2; type++) {
+ err = add_single_cap_ctl(codec,
+ spec->input_labels[idx],
+ spec->input_label_idxs[idx],
+ type,
+ get_first_cap_ctl(codec, i, type),
+ inv_dmic);
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+static int create_capture_mixers(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->input_mux;
+ int i, n, nums, err;
+
+ if (spec->dyn_adc_switch)
+ nums = 1;
+ else
+ nums = spec->num_adc_nids;
+
+ if (!spec->auto_mic && imux->num_items > 1) {
+ struct snd_kcontrol_new *knew;
+ const char *name;
+ name = nums > 1 ? "Input Source" : "Capture Source";
+ knew = snd_hda_gen_add_kctl(spec, name, &cap_src_temp);
+ if (!knew)
+ return -ENOMEM;
+ knew->count = nums;
+ }
+
+ for (n = 0; n < nums; n++) {
+ bool multi = false;
+ bool multi_cap_vol = spec->multi_cap_vol;
+ bool inv_dmic = false;
+ int vol, sw;
+
+ vol = sw = 0;
+ for (i = 0; i < imux->num_items; i++) {
+ struct nid_path *path;
+ path = get_input_path(codec, n, i);
+ if (!path)
+ continue;
+ parse_capvol_in_path(codec, path);
+ if (!vol)
+ vol = path->ctls[NID_PATH_VOL_CTL];
+ else if (vol != path->ctls[NID_PATH_VOL_CTL]) {
+ multi = true;
+ if (!same_amp_caps(codec, vol,
+ path->ctls[NID_PATH_VOL_CTL], HDA_INPUT))
+ multi_cap_vol = true;
+ }
+ if (!sw)
+ sw = path->ctls[NID_PATH_MUTE_CTL];
+ else if (sw != path->ctls[NID_PATH_MUTE_CTL]) {
+ multi = true;
+ if (!same_amp_caps(codec, sw,
+ path->ctls[NID_PATH_MUTE_CTL], HDA_INPUT))
+ multi_cap_vol = true;
+ }
+ if (is_inv_dmic_pin(codec, spec->imux_pins[i]))
+ inv_dmic = true;
+ }
+
+ if (!multi)
+ err = create_single_cap_vol_ctl(codec, n, vol, sw,
+ inv_dmic);
+ else if (!multi_cap_vol && !inv_dmic)
+ err = create_bind_cap_vol_ctl(codec, n, vol, sw);
+ else
+ err = create_multi_cap_vol_ctl(codec);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * add mic boosts if needed
+ */
+
+/* check whether the given amp is feasible as a boost volume */
+static bool check_boost_vol(struct hda_codec *codec, hda_nid_t nid,
+ int dir, int idx)
+{
+ unsigned int step;
+
+ if (!nid_has_volume(codec, nid, dir) ||
+ is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) ||
+ is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL))
+ return false;
+
+ step = (query_amp_caps(codec, nid, dir) & AC_AMPCAP_STEP_SIZE)
+ >> AC_AMPCAP_STEP_SIZE_SHIFT;
+ if (step < 0x20)
+ return false;
+ return true;
+}
+
+/* look for a boost amp in a widget close to the pin */
+static unsigned int look_for_boost_amp(struct hda_codec *codec,
+ struct nid_path *path)
+{
+ unsigned int val = 0;
+ hda_nid_t nid;
+ int depth;
+
+ for (depth = 0; depth < 3; depth++) {
+ if (depth >= path->depth - 1)
+ break;
+ nid = path->path[depth];
+ if (depth && check_boost_vol(codec, nid, HDA_OUTPUT, 0)) {
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ break;
+ } else if (check_boost_vol(codec, nid, HDA_INPUT,
+ path->idx[depth])) {
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, path->idx[depth],
+ HDA_INPUT);
+ break;
+ }
+ }
+
+ return val;
+}
+
+static int parse_mic_boost(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct hda_input_mux *imux = &spec->input_mux;
+ int i;
+
+ if (!spec->num_adc_nids)
+ return 0;
+
+ for (i = 0; i < imux->num_items; i++) {
+ struct nid_path *path;
+ unsigned int val;
+ int idx;
+ char boost_label[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
+ idx = imux->items[i].index;
+ if (idx >= imux->num_items)
+ continue;
+
+ /* check only line-in and mic pins */
+ if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN)
+ continue;
+
+ path = get_input_path(codec, 0, i);
+ if (!path)
+ continue;
+
+ val = look_for_boost_amp(codec, path);
+ if (!val)
+ continue;
+
+ /* create a boost control */
+ snprintf(boost_label, sizeof(boost_label),
+ "%s Boost Volume", spec->input_labels[idx]);
+ if (!add_control(spec, HDA_CTL_WIDGET_VOL, boost_label,
+ spec->input_label_idxs[idx], val))
+ return -ENOMEM;
+
+ path->ctls[NID_PATH_BOOST_CTL] = val;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_SND_HDA_GENERIC_LEDS
+/*
+ * vmaster mute LED hook helpers
+ */
+
+static int create_mute_led_cdev(struct hda_codec *codec,
+ int (*callback)(struct led_classdev *,
+ enum led_brightness),
+ bool micmute)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct led_classdev *cdev;
+ int idx = micmute ? LED_AUDIO_MICMUTE : LED_AUDIO_MUTE;
+ int err;
+
+ cdev = devm_kzalloc(&codec->core.dev, sizeof(*cdev), GFP_KERNEL);
+ if (!cdev)
+ return -ENOMEM;
+
+ cdev->name = micmute ? "hda::micmute" : "hda::mute";
+ cdev->max_brightness = 1;
+ cdev->default_trigger = micmute ? "audio-micmute" : "audio-mute";
+ cdev->brightness_set_blocking = callback;
+ cdev->flags = LED_CORE_SUSPENDRESUME;
+
+ err = led_classdev_register(&codec->core.dev, cdev);
+ if (err < 0)
+ return err;
+ spec->led_cdevs[idx] = cdev;
+ return 0;
+}
+
+/**
+ * snd_hda_gen_add_mute_led_cdev - Create a LED classdev and enable as vmaster mute LED
+ * @codec: the HDA codec
+ * @callback: the callback for LED classdev brightness_set_blocking
+ */
+int snd_hda_gen_add_mute_led_cdev(struct hda_codec *codec,
+ int (*callback)(struct led_classdev *,
+ enum led_brightness))
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int err;
+
+ if (callback) {
+ err = create_mute_led_cdev(codec, callback, false);
+ if (err) {
+ codec_warn(codec, "failed to create a mute LED cdev\n");
+ return err;
+ }
+ }
+
+ if (spec->vmaster_mute.hook)
+ codec_err(codec, "vmaster hook already present before cdev!\n");
+
+ spec->vmaster_mute_led = 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_add_mute_led_cdev);
+
+/**
+ * snd_hda_gen_add_micmute_led_cdev - Create a LED classdev and enable as mic-mute LED
+ * @codec: the HDA codec
+ * @callback: the callback for LED classdev brightness_set_blocking
+ *
+ * Called from the codec drivers for offering the mic mute LED controls.
+ * This creates a LED classdev and sets up the cap_sync_hook that is called at
+ * each time when the capture mixer switch changes.
+ *
+ * When NULL is passed to @callback, no classdev is created but only the
+ * LED-trigger is set up.
+ *
+ * Returns 0 or a negative error.
+ */
+int snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec,
+ int (*callback)(struct led_classdev *,
+ enum led_brightness))
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int err;
+
+ if (callback) {
+ err = create_mute_led_cdev(codec, callback, true);
+ if (err) {
+ codec_warn(codec, "failed to create a mic-mute LED cdev\n");
+ return err;
+ }
+ }
+
+ spec->mic_mute_led = 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_add_micmute_led_cdev);
+#endif /* CONFIG_SND_HDA_GENERIC_LEDS */
+
+/*
+ * parse digital I/Os and set up NIDs in BIOS auto-parse mode
+ */
+static void parse_digital(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+ int i, nums;
+ hda_nid_t dig_nid, pin;
+
+ /* support multiple SPDIFs; the secondary is set up as a follower */
+ nums = 0;
+ for (i = 0; i < spec->autocfg.dig_outs; i++) {
+ pin = spec->autocfg.dig_out_pins[i];
+ dig_nid = look_for_dac(codec, pin, true);
+ if (!dig_nid)
+ continue;
+ path = snd_hda_add_new_path(codec, dig_nid, pin, 0);
+ if (!path)
+ continue;
+ print_nid_path(codec, "digout", path);
+ path->active = true;
+ path->pin_fixed = true; /* no jack detection */
+ spec->digout_paths[i] = snd_hda_get_path_idx(codec, path);
+ set_pin_target(codec, pin, PIN_OUT, false);
+ if (!nums) {
+ spec->multiout.dig_out_nid = dig_nid;
+ spec->dig_out_type = spec->autocfg.dig_out_type[0];
+ } else {
+ spec->multiout.follower_dig_outs = spec->follower_dig_outs;
+ if (nums >= ARRAY_SIZE(spec->follower_dig_outs) - 1)
+ break;
+ spec->follower_dig_outs[nums - 1] = dig_nid;
+ }
+ nums++;
+ }
+
+ if (spec->autocfg.dig_in_pin) {
+ pin = spec->autocfg.dig_in_pin;
+ for_each_hda_codec_node(dig_nid, codec) {
+ unsigned int wcaps = get_wcaps(codec, dig_nid);
+ if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
+ continue;
+ if (!(wcaps & AC_WCAP_DIGITAL))
+ continue;
+ path = snd_hda_add_new_path(codec, pin, dig_nid, 0);
+ if (path) {
+ print_nid_path(codec, "digin", path);
+ path->active = true;
+ path->pin_fixed = true; /* no jack */
+ spec->dig_in_nid = dig_nid;
+ spec->digin_path = snd_hda_get_path_idx(codec, path);
+ set_pin_target(codec, pin, PIN_IN, false);
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * input MUX handling
+ */
+
+static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur);
+
+/* select the given imux item; either unmute exclusively or select the route */
+static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
+ unsigned int idx)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct hda_input_mux *imux;
+ struct nid_path *old_path, *path;
+
+ imux = &spec->input_mux;
+ if (!imux->num_items)
+ return 0;
+
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+ if (spec->cur_mux[adc_idx] == idx)
+ return 0;
+
+ old_path = get_input_path(codec, adc_idx, spec->cur_mux[adc_idx]);
+ if (!old_path)
+ return 0;
+ if (old_path->active)
+ snd_hda_activate_path(codec, old_path, false, false);
+
+ spec->cur_mux[adc_idx] = idx;
+
+ if (spec->hp_mic)
+ update_hp_mic(codec, adc_idx, false);
+
+ if (spec->dyn_adc_switch)
+ dyn_adc_pcm_resetup(codec, idx);
+
+ path = get_input_path(codec, adc_idx, idx);
+ if (!path)
+ return 0;
+ if (path->active)
+ return 0;
+ snd_hda_activate_path(codec, path, true, false);
+ if (spec->cap_sync_hook)
+ spec->cap_sync_hook(codec, NULL, NULL);
+ path_power_down_sync(codec, old_path);
+ return 1;
+}
+
+/* power up/down widgets in the all paths that match with the given NID
+ * as terminals (either start- or endpoint)
+ *
+ * returns the last changed NID, or zero if unchanged.
+ */
+static hda_nid_t set_path_power(struct hda_codec *codec, hda_nid_t nid,
+ int pin_state, int stream_state)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t last, changed = 0;
+ struct nid_path *path;
+ int n;
+
+ snd_array_for_each(&spec->paths, n, path) {
+ if (!path->depth)
+ continue;
+ if (path->path[0] == nid ||
+ path->path[path->depth - 1] == nid) {
+ bool pin_old = path->pin_enabled;
+ bool stream_old = path->stream_enabled;
+
+ if (pin_state >= 0)
+ path->pin_enabled = pin_state;
+ if (stream_state >= 0)
+ path->stream_enabled = stream_state;
+ if ((!path->pin_fixed && path->pin_enabled != pin_old)
+ || path->stream_enabled != stream_old) {
+ last = path_power_update(codec, path, true);
+ if (last)
+ changed = last;
+ }
+ }
+ }
+ return changed;
+}
+
+/* check the jack status for power control */
+static bool detect_pin_state(struct hda_codec *codec, hda_nid_t pin)
+{
+ if (!is_jack_detectable(codec, pin))
+ return true;
+ return snd_hda_jack_detect_state(codec, pin) != HDA_JACK_NOT_PRESENT;
+}
+
+/* power up/down the paths of the given pin according to the jack state;
+ * power = 0/1 : only power up/down if it matches with the jack state,
+ * < 0 : force power up/down to follow the jack sate
+ *
+ * returns the last changed NID, or zero if unchanged.
+ */
+static hda_nid_t set_pin_power_jack(struct hda_codec *codec, hda_nid_t pin,
+ int power)
+{
+ bool on;
+
+ if (!codec->power_save_node)
+ return 0;
+
+ on = detect_pin_state(codec, pin);
+
+ if (power >= 0 && on != power)
+ return 0;
+ return set_path_power(codec, pin, on, -1);
+}
+
+static void pin_power_callback(struct hda_codec *codec,
+ struct hda_jack_callback *jack,
+ bool on)
+{
+ if (jack && jack->nid)
+ sync_power_state_change(codec,
+ set_pin_power_jack(codec, jack->nid, on));
+}
+
+/* callback only doing power up -- called at first */
+static void pin_power_up_callback(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ pin_power_callback(codec, jack, true);
+}
+
+/* callback only doing power down -- called at last */
+static void pin_power_down_callback(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ pin_power_callback(codec, jack, false);
+}
+
+/* set up the power up/down callbacks */
+static void add_pin_power_ctls(struct hda_codec *codec, int num_pins,
+ const hda_nid_t *pins, bool on)
+{
+ int i;
+ hda_jack_callback_fn cb =
+ on ? pin_power_up_callback : pin_power_down_callback;
+
+ for (i = 0; i < num_pins && pins[i]; i++) {
+ if (is_jack_detectable(codec, pins[i]))
+ snd_hda_jack_detect_enable_callback(codec, pins[i], cb);
+ else
+ set_path_power(codec, pins[i], true, -1);
+ }
+}
+
+/* enabled power callback to each available I/O pin with jack detections;
+ * the digital I/O pins are excluded because of the unreliable detectsion
+ */
+static void add_all_pin_power_ctls(struct hda_codec *codec, bool on)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ if (!codec->power_save_node)
+ return;
+ add_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins, on);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ add_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins, on);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ add_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins, on);
+ for (i = 0; i < cfg->num_inputs; i++)
+ add_pin_power_ctls(codec, 1, &cfg->inputs[i].pin, on);
+}
+
+/* sync path power up/down with the jack states of given pins */
+static void sync_pin_power_ctls(struct hda_codec *codec, int num_pins,
+ const hda_nid_t *pins)
+{
+ int i;
+
+ for (i = 0; i < num_pins && pins[i]; i++)
+ if (is_jack_detectable(codec, pins[i]))
+ set_pin_power_jack(codec, pins[i], -1);
+}
+
+/* sync path power up/down with pins; called at init and resume */
+static void sync_all_pin_power_ctls(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ if (!codec->power_save_node)
+ return;
+ sync_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ sync_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ sync_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins);
+ for (i = 0; i < cfg->num_inputs; i++)
+ sync_pin_power_ctls(codec, 1, &cfg->inputs[i].pin);
+}
+
+/* add fake paths if not present yet */
+static int add_fake_paths(struct hda_codec *codec, hda_nid_t nid,
+ int num_pins, const hda_nid_t *pins)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+ int i;
+
+ for (i = 0; i < num_pins; i++) {
+ if (!pins[i])
+ break;
+ if (get_nid_path(codec, nid, pins[i], 0))
+ continue;
+ path = snd_array_new(&spec->paths);
+ if (!path)
+ return -ENOMEM;
+ memset(path, 0, sizeof(*path));
+ path->depth = 2;
+ path->path[0] = nid;
+ path->path[1] = pins[i];
+ path->active = true;
+ }
+ return 0;
+}
+
+/* create fake paths to all outputs from beep */
+static int add_fake_beep_paths(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t nid = spec->beep_nid;
+ int err;
+
+ if (!codec->power_save_node || !nid)
+ return 0;
+ err = add_fake_paths(codec, nid, cfg->line_outs, cfg->line_out_pins);
+ if (err < 0)
+ return err;
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+ err = add_fake_paths(codec, nid, cfg->hp_outs, cfg->hp_pins);
+ if (err < 0)
+ return err;
+ }
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = add_fake_paths(codec, nid, cfg->speaker_outs,
+ cfg->speaker_pins);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* power up/down beep widget and its output paths */
+static void beep_power_hook(struct hda_beep *beep, bool on)
+{
+ set_path_power(beep->codec, beep->nid, -1, on);
+}
+
+/**
+ * snd_hda_gen_fix_pin_power - Fix the power of the given pin widget to D0
+ * @codec: the HDA codec
+ * @pin: NID of pin to fix
+ */
+int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+
+ path = snd_array_new(&spec->paths);
+ if (!path)
+ return -ENOMEM;
+ memset(path, 0, sizeof(*path));
+ path->depth = 1;
+ path->path[0] = pin;
+ path->active = true;
+ path->pin_fixed = true;
+ path->stream_enabled = true;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_fix_pin_power);
+
+/*
+ * Jack detections for HP auto-mute and mic-switch
+ */
+
+/* check each pin in the given array; returns true if any of them is plugged */
+static bool detect_jacks(struct hda_codec *codec, int num_pins, const hda_nid_t *pins)
+{
+ int i;
+ bool present = false;
+
+ for (i = 0; i < num_pins; i++) {
+ hda_nid_t nid = pins[i];
+ if (!nid)
+ break;
+ /* don't detect pins retasked as inputs */
+ if (snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_IN_EN)
+ continue;
+ if (snd_hda_jack_detect_state(codec, nid) == HDA_JACK_PRESENT)
+ present = true;
+ }
+ return present;
+}
+
+/* standard HP/line-out auto-mute helper */
+static void do_automute(struct hda_codec *codec, int num_pins, const hda_nid_t *pins,
+ int *paths, bool mute)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < num_pins; i++) {
+ hda_nid_t nid = pins[i];
+ unsigned int val, oldval;
+ if (!nid)
+ break;
+
+ oldval = snd_hda_codec_get_pin_target(codec, nid);
+ if (oldval & PIN_IN)
+ continue; /* no mute for inputs */
+
+ if (spec->auto_mute_via_amp) {
+ struct nid_path *path;
+ hda_nid_t mute_nid;
+
+ path = snd_hda_get_path_from_idx(codec, paths[i]);
+ if (!path)
+ continue;
+ mute_nid = get_amp_nid_(path->ctls[NID_PATH_MUTE_CTL]);
+ if (!mute_nid)
+ continue;
+ if (mute)
+ spec->mute_bits |= (1ULL << mute_nid);
+ else
+ spec->mute_bits &= ~(1ULL << mute_nid);
+ continue;
+ } else {
+ /* don't reset VREF value in case it's controlling
+ * the amp (see alc861_fixup_asus_amp_vref_0f())
+ */
+ if (spec->keep_vref_in_automute)
+ val = oldval & ~PIN_HP;
+ else
+ val = 0;
+ if (!mute)
+ val |= oldval;
+ /* here we call update_pin_ctl() so that the pinctl is
+ * changed without changing the pinctl target value;
+ * the original target value will be still referred at
+ * the init / resume again
+ */
+ update_pin_ctl(codec, nid, val);
+ }
+
+ set_pin_eapd(codec, nid, !mute);
+ if (codec->power_save_node) {
+ bool on = !mute;
+ if (on)
+ on = detect_pin_state(codec, nid);
+ set_path_power(codec, nid, on, -1);
+ }
+ }
+}
+
+/**
+ * snd_hda_gen_update_outputs - Toggle outputs muting
+ * @codec: the HDA codec
+ *
+ * Update the mute status of all outputs based on the current jack states.
+ */
+void snd_hda_gen_update_outputs(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int *paths;
+ int on;
+
+ /* Control HP pins/amps depending on master_mute state;
+ * in general, HP pins/amps control should be enabled in all cases,
+ * but currently set only for master_mute, just to be safe
+ */
+ if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+ paths = spec->out_paths;
+ else
+ paths = spec->hp_paths;
+ do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
+ spec->autocfg.hp_pins, paths, spec->master_mute);
+
+ if (!spec->automute_speaker)
+ on = 0;
+ else
+ on = spec->hp_jack_present | spec->line_jack_present;
+ on |= spec->master_mute;
+ spec->speaker_muted = on;
+ if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
+ paths = spec->out_paths;
+ else
+ paths = spec->speaker_paths;
+ do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins),
+ spec->autocfg.speaker_pins, paths, on);
+
+ /* toggle line-out mutes if needed, too */
+ /* if LO is a copy of either HP or Speaker, don't need to handle it */
+ if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] ||
+ spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0])
+ return;
+ if (!spec->automute_lo)
+ on = 0;
+ else
+ on = spec->hp_jack_present;
+ on |= spec->master_mute;
+ spec->line_out_muted = on;
+ paths = spec->out_paths;
+ do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
+ spec->autocfg.line_out_pins, paths, on);
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_update_outputs);
+
+static void call_update_outputs(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->automute_hook)
+ spec->automute_hook(codec);
+ else
+ snd_hda_gen_update_outputs(codec);
+
+ /* sync the whole vmaster followers to reflect the new auto-mute status */
+ if (spec->auto_mute_via_amp && !codec->bus->shutdown)
+ snd_ctl_sync_vmaster(spec->vmaster_mute.sw_kctl, false);
+}
+
+/**
+ * snd_hda_gen_hp_automute - standard HP-automute helper
+ * @codec: the HDA codec
+ * @jack: jack object, NULL for the whole
+ */
+void snd_hda_gen_hp_automute(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t *pins = spec->autocfg.hp_pins;
+ int num_pins = ARRAY_SIZE(spec->autocfg.hp_pins);
+
+ /* No detection for the first HP jack during indep-HP mode */
+ if (spec->indep_hp_enabled) {
+ pins++;
+ num_pins--;
+ }
+
+ spec->hp_jack_present = detect_jacks(codec, num_pins, pins);
+ if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo))
+ return;
+ call_update_outputs(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_hp_automute);
+
+/**
+ * snd_hda_gen_line_automute - standard line-out-automute helper
+ * @codec: the HDA codec
+ * @jack: jack object, NULL for the whole
+ */
+void snd_hda_gen_line_automute(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
+ return;
+ /* check LO jack only when it's different from HP */
+ if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0])
+ return;
+
+ spec->line_jack_present =
+ detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
+ spec->autocfg.line_out_pins);
+ if (!spec->automute_speaker || !spec->detect_lo)
+ return;
+ call_update_outputs(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_line_automute);
+
+/**
+ * snd_hda_gen_mic_autoswitch - standard mic auto-switch helper
+ * @codec: the HDA codec
+ * @jack: jack object, NULL for the whole
+ */
+void snd_hda_gen_mic_autoswitch(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ if (!spec->auto_mic)
+ return;
+
+ for (i = spec->am_num_entries - 1; i > 0; i--) {
+ hda_nid_t pin = spec->am_entry[i].pin;
+ /* don't detect pins retasked as outputs */
+ if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN)
+ continue;
+ if (snd_hda_jack_detect_state(codec, pin) == HDA_JACK_PRESENT) {
+ mux_select(codec, 0, spec->am_entry[i].idx);
+ return;
+ }
+ }
+ mux_select(codec, 0, spec->am_entry[0].idx);
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_mic_autoswitch);
+
+/* call appropriate hooks */
+static void call_hp_automute(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->hp_automute_hook)
+ spec->hp_automute_hook(codec, jack);
+ else
+ snd_hda_gen_hp_automute(codec, jack);
+}
+
+static void call_line_automute(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->line_automute_hook)
+ spec->line_automute_hook(codec, jack);
+ else
+ snd_hda_gen_line_automute(codec, jack);
+}
+
+static void call_mic_autoswitch(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->mic_autoswitch_hook)
+ spec->mic_autoswitch_hook(codec, jack);
+ else
+ snd_hda_gen_mic_autoswitch(codec, jack);
+}
+
+/* update jack retasking */
+static void update_automute_all(struct hda_codec *codec)
+{
+ call_hp_automute(codec, NULL);
+ call_line_automute(codec, NULL);
+ call_mic_autoswitch(codec, NULL);
+}
+
+/*
+ * Auto-Mute mode mixer enum support
+ */
+static int automute_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ static const char * const texts3[] = {
+ "Disabled", "Speaker Only", "Line Out+Speaker"
+ };
+
+ if (spec->automute_speaker_possible && spec->automute_lo_possible)
+ return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3);
+ return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
+}
+
+static int automute_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+ unsigned int val = 0;
+ if (spec->automute_speaker)
+ val++;
+ if (spec->automute_lo)
+ val++;
+
+ ucontrol->value.enumerated.item[0] = val;
+ return 0;
+}
+
+static int automute_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gen_spec *spec = codec->spec;
+
+ switch (ucontrol->value.enumerated.item[0]) {
+ case 0:
+ if (!spec->automute_speaker && !spec->automute_lo)
+ return 0;
+ spec->automute_speaker = 0;
+ spec->automute_lo = 0;
+ break;
+ case 1:
+ if (spec->automute_speaker_possible) {
+ if (!spec->automute_lo && spec->automute_speaker)
+ return 0;
+ spec->automute_speaker = 1;
+ spec->automute_lo = 0;
+ } else if (spec->automute_lo_possible) {
+ if (spec->automute_lo)
+ return 0;
+ spec->automute_lo = 1;
+ } else
+ return -EINVAL;
+ break;
+ case 2:
+ if (!spec->automute_lo_possible || !spec->automute_speaker_possible)
+ return -EINVAL;
+ if (spec->automute_speaker && spec->automute_lo)
+ return 0;
+ spec->automute_speaker = 1;
+ spec->automute_lo = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ call_update_outputs(codec);
+ return 1;
+}
+
+static const struct snd_kcontrol_new automute_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Auto-Mute Mode",
+ .info = automute_mode_info,
+ .get = automute_mode_get,
+ .put = automute_mode_put,
+};
+
+static int add_automute_mode_enum(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (!snd_hda_gen_add_kctl(spec, NULL, &automute_mode_enum))
+ return -ENOMEM;
+ return 0;
+}
+
+/*
+ * Check the availability of HP/line-out auto-mute;
+ * Set up appropriately if really supported
+ */
+static int check_auto_mute_availability(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int present = 0;
+ int i, err;
+
+ if (spec->suppress_auto_mute)
+ return 0;
+
+ if (cfg->hp_pins[0])
+ present++;
+ if (cfg->line_out_pins[0])
+ present++;
+ if (cfg->speaker_pins[0])
+ present++;
+ if (present < 2) /* need two different output types */
+ return 0;
+
+ if (!cfg->speaker_pins[0] &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->speaker_outs = cfg->line_outs;
+ }
+
+ if (!cfg->hp_pins[0] &&
+ cfg->line_out_type == AUTO_PIN_HP_OUT) {
+ memcpy(cfg->hp_pins, cfg->line_out_pins,
+ sizeof(cfg->hp_pins));
+ cfg->hp_outs = cfg->line_outs;
+ }
+
+ for (i = 0; i < cfg->hp_outs; i++) {
+ hda_nid_t nid = cfg->hp_pins[i];
+ if (!is_jack_detectable(codec, nid))
+ continue;
+ codec_dbg(codec, "Enable HP auto-muting on NID 0x%x\n", nid);
+ snd_hda_jack_detect_enable_callback(codec, nid,
+ call_hp_automute);
+ spec->detect_hp = 1;
+ }
+
+ if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) {
+ if (cfg->speaker_outs)
+ for (i = 0; i < cfg->line_outs; i++) {
+ hda_nid_t nid = cfg->line_out_pins[i];
+ if (!is_jack_detectable(codec, nid))
+ continue;
+ codec_dbg(codec, "Enable Line-Out auto-muting on NID 0x%x\n", nid);
+ snd_hda_jack_detect_enable_callback(codec, nid,
+ call_line_automute);
+ spec->detect_lo = 1;
+ }
+ spec->automute_lo_possible = spec->detect_hp;
+ }
+
+ spec->automute_speaker_possible = cfg->speaker_outs &&
+ (spec->detect_hp || spec->detect_lo);
+
+ spec->automute_lo = spec->automute_lo_possible;
+ spec->automute_speaker = spec->automute_speaker_possible;
+
+ if (spec->automute_speaker_possible || spec->automute_lo_possible) {
+ /* create a control for automute mode */
+ err = add_automute_mode_enum(codec);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* check whether all auto-mic pins are valid; setup indices if OK */
+static bool auto_mic_check_imux(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const struct hda_input_mux *imux;
+ int i;
+
+ imux = &spec->input_mux;
+ for (i = 0; i < spec->am_num_entries; i++) {
+ spec->am_entry[i].idx =
+ find_idx_in_nid_list(spec->am_entry[i].pin,
+ spec->imux_pins, imux->num_items);
+ if (spec->am_entry[i].idx < 0)
+ return false; /* no corresponding imux */
+ }
+
+ /* we don't need the jack detection for the first pin */
+ for (i = 1; i < spec->am_num_entries; i++)
+ snd_hda_jack_detect_enable_callback(codec,
+ spec->am_entry[i].pin,
+ call_mic_autoswitch);
+ return true;
+}
+
+static int compare_attr(const void *ap, const void *bp)
+{
+ const struct automic_entry *a = ap;
+ const struct automic_entry *b = bp;
+ return (int)(a->attr - b->attr);
+}
+
+/*
+ * Check the availability of auto-mic switch;
+ * Set up if really supported
+ */
+static int check_auto_mic_availability(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int types;
+ int i, num_pins;
+
+ if (spec->suppress_auto_mic)
+ return 0;
+
+ types = 0;
+ num_pins = 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ unsigned int attr;
+ attr = snd_hda_codec_get_pincfg(codec, nid);
+ attr = snd_hda_get_input_pin_attr(attr);
+ if (types & (1 << attr))
+ return 0; /* already occupied */
+ switch (attr) {
+ case INPUT_PIN_ATTR_INT:
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ return 0; /* invalid type */
+ break;
+ case INPUT_PIN_ATTR_UNUSED:
+ return 0; /* invalid entry */
+ default:
+ if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
+ return 0; /* invalid type */
+ if (!spec->line_in_auto_switch &&
+ cfg->inputs[i].type != AUTO_PIN_MIC)
+ return 0; /* only mic is allowed */
+ if (!is_jack_detectable(codec, nid))
+ return 0; /* no unsol support */
+ break;
+ }
+ if (num_pins >= MAX_AUTO_MIC_PINS)
+ return 0;
+ types |= (1 << attr);
+ spec->am_entry[num_pins].pin = nid;
+ spec->am_entry[num_pins].attr = attr;
+ num_pins++;
+ }
+
+ if (num_pins < 2)
+ return 0;
+
+ spec->am_num_entries = num_pins;
+ /* sort the am_entry in the order of attr so that the pin with a
+ * higher attr will be selected when the jack is plugged.
+ */
+ sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]),
+ compare_attr, NULL);
+
+ if (!auto_mic_check_imux(codec))
+ return 0;
+
+ spec->auto_mic = 1;
+ spec->num_adc_nids = 1;
+ spec->cur_mux[0] = spec->am_entry[0].idx;
+ codec_dbg(codec, "Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n",
+ spec->am_entry[0].pin,
+ spec->am_entry[1].pin,
+ spec->am_entry[2].pin);
+
+ return 0;
+}
+
+/**
+ * snd_hda_gen_path_power_filter - power_filter hook to make inactive widgets
+ * into power down
+ * @codec: the HDA codec
+ * @nid: NID to evalute
+ * @power_state: target power state
+ */
+unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (!spec->power_down_unused && !codec->power_save_node)
+ return power_state;
+ if (power_state != AC_PWRST_D0 || nid == codec->core.afg)
+ return power_state;
+ if (get_wcaps_type(get_wcaps(codec, nid)) >= AC_WID_POWER)
+ return power_state;
+ if (is_active_nid_for_any(codec, nid))
+ return power_state;
+ return AC_PWRST_D3;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_path_power_filter);
+
+/* mute all aamix inputs initially; parse up to the first leaves */
+static void mute_all_mixer_nid(struct hda_codec *codec, hda_nid_t mix)
+{
+ int i, nums;
+ const hda_nid_t *conn;
+ bool has_amp;
+
+ nums = snd_hda_get_conn_list(codec, mix, &conn);
+ has_amp = nid_has_mute(codec, mix, HDA_INPUT);
+ for (i = 0; i < nums; i++) {
+ if (has_amp)
+ update_amp(codec, mix, HDA_INPUT, i,
+ 0xff, HDA_AMP_MUTE);
+ else if (nid_has_volume(codec, conn[i], HDA_OUTPUT))
+ update_amp(codec, conn[i], HDA_OUTPUT, 0,
+ 0xff, HDA_AMP_MUTE);
+ }
+}
+
+/**
+ * snd_hda_gen_stream_pm - Stream power management callback
+ * @codec: the HDA codec
+ * @nid: audio widget
+ * @on: power on/off flag
+ *
+ * Set this in hda_codec_ops.stream_pm. Only valid with power_save_node flag.
+ */
+void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on)
+{
+ if (codec->power_save_node)
+ set_path_power(codec, nid, -1, on);
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_stream_pm);
+
+/* forcibly mute the speaker output without caching; return true if updated */
+static bool force_mute_output_path(struct hda_codec *codec, hda_nid_t nid)
+{
+ if (!nid)
+ return false;
+ if (!nid_has_mute(codec, nid, HDA_OUTPUT))
+ return false; /* no mute, skip */
+ if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
+ snd_hda_codec_amp_read(codec, nid, 1, HDA_OUTPUT, 0) &
+ HDA_AMP_MUTE)
+ return false; /* both channels already muted, skip */
+
+ /* direct amp update without caching */
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AC_AMP_SET_OUTPUT | AC_AMP_SET_LEFT |
+ AC_AMP_SET_RIGHT | HDA_AMP_MUTE);
+ return true;
+}
+
+/**
+ * snd_hda_gen_shutup_speakers - Forcibly mute the speaker outputs
+ * @codec: the HDA codec
+ *
+ * Forcibly mute the speaker outputs, to be called at suspend or shutdown.
+ *
+ * The mute state done by this function isn't cached, hence the original state
+ * will be restored at resume.
+ *
+ * Return true if the mute state has been changed.
+ */
+bool snd_hda_gen_shutup_speakers(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ const int *paths;
+ const struct nid_path *path;
+ int i, p, num_paths;
+ bool updated = false;
+
+ /* if already powered off, do nothing */
+ if (!snd_hdac_is_power_on(&codec->core))
+ return false;
+
+ if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ paths = spec->out_paths;
+ num_paths = spec->autocfg.line_outs;
+ } else {
+ paths = spec->speaker_paths;
+ num_paths = spec->autocfg.speaker_outs;
+ }
+
+ for (i = 0; i < num_paths; i++) {
+ path = snd_hda_get_path_from_idx(codec, paths[i]);
+ if (!path)
+ continue;
+ for (p = 0; p < path->depth; p++)
+ if (force_mute_output_path(codec, path->path[p]))
+ updated = true;
+ }
+
+ return updated;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_shutup_speakers);
+
+/**
+ * snd_hda_gen_parse_auto_config - Parse the given BIOS configuration and
+ * set up the hda_gen_spec
+ * @codec: the HDA codec
+ * @cfg: Parsed pin configuration
+ *
+ * return 1 if successful, 0 if the proper config is not found,
+ * or a negative error code
+ */
+int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int err;
+
+ parse_user_hints(codec);
+
+ if (spec->vmaster_mute_led || spec->mic_mute_led)
+ snd_ctl_led_request();
+
+ if (spec->mixer_nid && !spec->mixer_merge_nid)
+ spec->mixer_merge_nid = spec->mixer_nid;
+
+ if (cfg != &spec->autocfg) {
+ spec->autocfg = *cfg;
+ cfg = &spec->autocfg;
+ }
+
+ if (!spec->main_out_badness)
+ spec->main_out_badness = &hda_main_out_badness;
+ if (!spec->extra_out_badness)
+ spec->extra_out_badness = &hda_extra_out_badness;
+
+ fill_all_dac_nids(codec);
+
+ if (!cfg->line_outs) {
+ if (cfg->dig_outs || cfg->dig_in_pin) {
+ spec->multiout.max_channels = 2;
+ spec->no_analog = 1;
+ goto dig_only;
+ }
+ if (!cfg->num_inputs && !cfg->dig_in_pin)
+ return 0; /* can't find valid BIOS pin config */
+ }
+
+ if (!spec->no_primary_hp &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
+ cfg->line_outs <= cfg->hp_outs) {
+ /* use HP as primary out */
+ cfg->speaker_outs = cfg->line_outs;
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->line_outs = cfg->hp_outs;
+ memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins));
+ cfg->hp_outs = 0;
+ memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+ }
+
+ err = parse_output_paths(codec);
+ if (err < 0)
+ return err;
+ err = create_multi_channel_mode(codec);
+ if (err < 0)
+ return err;
+ err = create_multi_out_ctls(codec, cfg);
+ if (err < 0)
+ return err;
+ err = create_hp_out_ctls(codec);
+ if (err < 0)
+ return err;
+ err = create_speaker_out_ctls(codec);
+ if (err < 0)
+ return err;
+ err = create_indep_hp_ctls(codec);
+ if (err < 0)
+ return err;
+ err = create_loopback_mixing_ctl(codec);
+ if (err < 0)
+ return err;
+ err = create_hp_mic(codec);
+ if (err < 0)
+ return err;
+ err = create_input_ctls(codec);
+ if (err < 0)
+ return err;
+
+ /* add power-down pin callbacks at first */
+ add_all_pin_power_ctls(codec, false);
+
+ spec->const_channel_count = spec->ext_channel_count;
+ /* check the multiple speaker and headphone pins */
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ spec->const_channel_count = max(spec->const_channel_count,
+ cfg->speaker_outs * 2);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ spec->const_channel_count = max(spec->const_channel_count,
+ cfg->hp_outs * 2);
+ spec->multiout.max_channels = max(spec->ext_channel_count,
+ spec->const_channel_count);
+
+ err = check_auto_mute_availability(codec);
+ if (err < 0)
+ return err;
+
+ err = check_dyn_adc_switch(codec);
+ if (err < 0)
+ return err;
+
+ err = check_auto_mic_availability(codec);
+ if (err < 0)
+ return err;
+
+ /* add stereo mix if available and not enabled yet */
+ if (!spec->auto_mic && spec->mixer_nid &&
+ spec->add_stereo_mix_input == HDA_HINT_STEREO_MIX_AUTO &&
+ spec->input_mux.num_items > 1) {
+ err = parse_capture_source(codec, spec->mixer_nid,
+ CFG_IDX_MIX, spec->num_all_adcs,
+ "Stereo Mix", 0);
+ if (err < 0)
+ return err;
+ }
+
+
+ err = create_capture_mixers(codec);
+ if (err < 0)
+ return err;
+
+ err = parse_mic_boost(codec);
+ if (err < 0)
+ return err;
+
+ /* create "Headphone Mic Jack Mode" if no input selection is
+ * available (or user specifies add_jack_modes hint)
+ */
+ if (spec->hp_mic_pin &&
+ (spec->auto_mic || spec->input_mux.num_items == 1 ||
+ spec->add_jack_modes)) {
+ err = create_hp_mic_jack_mode(codec, spec->hp_mic_pin);
+ if (err < 0)
+ return err;
+ }
+
+ if (spec->add_jack_modes) {
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = create_out_jack_modes(codec, cfg->line_outs,
+ cfg->line_out_pins);
+ if (err < 0)
+ return err;
+ }
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+ err = create_out_jack_modes(codec, cfg->hp_outs,
+ cfg->hp_pins);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ /* add power-up pin callbacks at last */
+ add_all_pin_power_ctls(codec, true);
+
+ /* mute all aamix input initially */
+ if (spec->mixer_nid)
+ mute_all_mixer_nid(codec, spec->mixer_nid);
+
+ dig_only:
+ parse_digital(codec);
+
+ if (spec->power_down_unused || codec->power_save_node) {
+ if (!codec->power_filter)
+ codec->power_filter = snd_hda_gen_path_power_filter;
+ }
+
+ if (!spec->no_analog && spec->beep_nid) {
+ err = snd_hda_attach_beep_device(codec, spec->beep_nid);
+ if (err < 0)
+ return err;
+ if (codec->beep && codec->power_save_node) {
+ err = add_fake_beep_paths(codec);
+ if (err < 0)
+ return err;
+ codec->beep->power_hook = beep_power_hook;
+ }
+ }
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_parse_auto_config);
+
+
+/*
+ * Build control elements
+ */
+
+/* follower controls for virtual master */
+static const char * const follower_pfxs[] = {
+ "Front", "Surround", "Center", "LFE", "Side",
+ "Headphone", "Speaker", "Mono", "Line Out",
+ "CLFE", "Bass Speaker", "PCM",
+ "Speaker Front", "Speaker Surround", "Speaker CLFE", "Speaker Side",
+ "Headphone Front", "Headphone Surround", "Headphone CLFE",
+ "Headphone Side", "Headphone+LO", "Speaker+LO",
+ NULL,
+};
+
+/**
+ * snd_hda_gen_build_controls - Build controls from the parsed results
+ * @codec: the HDA codec
+ *
+ * Pass this to build_controls hda_codec_ops.
+ */
+int snd_hda_gen_build_controls(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int err;
+
+ if (spec->kctls.used) {
+ err = snd_hda_add_new_ctls(codec, spec->kctls.list);
+ if (err < 0)
+ return err;
+ }
+
+ if (spec->multiout.dig_out_nid) {
+ err = snd_hda_create_dig_out_ctls(codec,
+ spec->multiout.dig_out_nid,
+ spec->multiout.dig_out_nid,
+ spec->pcm_rec[1]->pcm_type);
+ if (err < 0)
+ return err;
+ if (!spec->no_analog) {
+ err = snd_hda_create_spdif_share_sw(codec,
+ &spec->multiout);
+ if (err < 0)
+ return err;
+ spec->multiout.share_spdif = 1;
+ }
+ }
+ if (spec->dig_in_nid) {
+ err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
+ if (err < 0)
+ return err;
+ }
+
+ /* if we have no master control, let's create it */
+ if (!spec->no_analog && !spec->suppress_vmaster &&
+ !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+ err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+ spec->vmaster_tlv, follower_pfxs,
+ "Playback Volume", 0);
+ if (err < 0)
+ return err;
+ }
+ if (!spec->no_analog && !spec->suppress_vmaster &&
+ !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
+ err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
+ NULL, follower_pfxs,
+ "Playback Switch", true,
+ spec->vmaster_mute_led ?
+ SNDRV_CTL_ELEM_ACCESS_SPK_LED : 0,
+ &spec->vmaster_mute.sw_kctl);
+ if (err < 0)
+ return err;
+ if (spec->vmaster_mute.hook) {
+ snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute);
+ snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+ }
+ }
+
+ free_kctls(spec); /* no longer needed */
+
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_build_controls);
+
+
+/*
+ * PCM definitions
+ */
+
+static void call_pcm_playback_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->pcm_playback_hook)
+ spec->pcm_playback_hook(hinfo, codec, substream, action);
+}
+
+static void call_pcm_capture_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->pcm_capture_hook)
+ spec->pcm_capture_hook(hinfo, codec, substream, action);
+}
+
+/*
+ * Analog playback callbacks
+ */
+static int playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int err;
+
+ guard(mutex)(&spec->pcm_mutex);
+ err = snd_hda_multi_out_analog_open(codec,
+ &spec->multiout, substream,
+ hinfo);
+ if (err < 0)
+ return err;
+
+ spec->active_streams |= 1 << STREAM_MULTI_OUT;
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_OPEN);
+ return 0;
+}
+
+static int playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+ if (!err)
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_PREPARE);
+ return err;
+}
+
+static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+ if (!err)
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_CLEANUP);
+ return err;
+}
+
+static int playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ guard(mutex)(&spec->pcm_mutex);
+ spec->active_streams &= ~(1 << STREAM_MULTI_OUT);
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_CLOSE);
+ return 0;
+}
+
+static int capture_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_OPEN);
+ return 0;
+}
+
+static int capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+ call_pcm_capture_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_PREPARE);
+ return 0;
+}
+
+static int capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+ call_pcm_capture_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_CLEANUP);
+ return 0;
+}
+
+static int capture_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLOSE);
+ return 0;
+}
+
+static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int err = 0;
+
+ guard(mutex)(&spec->pcm_mutex);
+ if (spec->indep_hp && !spec->indep_hp_enabled)
+ err = -EBUSY;
+ else
+ spec->active_streams |= 1 << STREAM_INDEP_HP;
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_OPEN);
+ return err;
+}
+
+static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ guard(mutex)(&spec->pcm_mutex);
+ spec->active_streams &= ~(1 << STREAM_INDEP_HP);
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_CLOSE);
+ return 0;
+}
+
+static int alt_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_PREPARE);
+ return 0;
+}
+
+static int alt_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+ call_pcm_playback_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_CLEANUP);
+ return 0;
+}
+
+/*
+ * Digital out
+ */
+static int dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static int dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
+}
+
+static int dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+#define alt_capture_pcm_open capture_pcm_open
+#define alt_capture_pcm_close capture_pcm_close
+
+static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
+ stream_tag, 0, format);
+ call_pcm_capture_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_PREPARE);
+ return 0;
+}
+
+static int alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ snd_hda_codec_cleanup_stream(codec,
+ spec->adc_nids[substream->number + 1]);
+ call_pcm_capture_hook(hinfo, codec, substream,
+ HDA_GEN_PCM_ACT_CLEANUP);
+ return 0;
+}
+
+/*
+ */
+static const struct hda_pcm_stream pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ /* NID is set in build_pcms */
+ .ops = {
+ .open = playback_pcm_open,
+ .close = playback_pcm_close,
+ .prepare = playback_pcm_prepare,
+ .cleanup = playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in build_pcms */
+ .ops = {
+ .open = capture_pcm_open,
+ .close = capture_pcm_close,
+ .prepare = capture_pcm_prepare,
+ .cleanup = capture_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream pcm_analog_alt_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in build_pcms */
+ .ops = {
+ .open = alt_playback_pcm_open,
+ .close = alt_playback_pcm_close,
+ .prepare = alt_playback_pcm_prepare,
+ .cleanup = alt_playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream pcm_analog_alt_capture = {
+ .substreams = 2, /* can be overridden */
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in build_pcms */
+ .ops = {
+ .open = alt_capture_pcm_open,
+ .close = alt_capture_pcm_close,
+ .prepare = alt_capture_pcm_prepare,
+ .cleanup = alt_capture_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in build_pcms */
+ .ops = {
+ .open = dig_playback_pcm_open,
+ .close = dig_playback_pcm_close,
+ .prepare = dig_playback_pcm_prepare,
+ .cleanup = dig_playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in build_pcms */
+};
+
+/* Used by build_pcms to flag that a PCM has no playback stream */
+static const struct hda_pcm_stream pcm_null_stream = {
+ .substreams = 0,
+ .channels_min = 0,
+ .channels_max = 0,
+};
+
+/*
+ * dynamic changing ADC PCM streams
+ */
+static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]];
+
+ if (spec->cur_adc && spec->cur_adc != new_adc) {
+ /* stream is running, let's swap the current ADC */
+ __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
+ spec->cur_adc = new_adc;
+ snd_hda_codec_setup_stream(codec, new_adc,
+ spec->cur_adc_stream_tag, 0,
+ spec->cur_adc_format);
+ return true;
+ }
+ return false;
+}
+
+/* analog capture with dynamic dual-adc changes */
+static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]];
+ spec->cur_adc_stream_tag = stream_tag;
+ spec->cur_adc_format = format;
+ snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+ call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE);
+ return 0;
+}
+
+static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+ spec->cur_adc = 0;
+ call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLEANUP);
+ return 0;
+}
+
+static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0, /* fill later */
+ .ops = {
+ .prepare = dyn_adc_capture_pcm_prepare,
+ .cleanup = dyn_adc_capture_pcm_cleanup
+ },
+};
+
+static void fill_pcm_stream_name(char *str, size_t len, const char *sfx,
+ const char *chip_name)
+{
+ char *p;
+
+ if (*str)
+ return;
+ strscpy(str, chip_name, len);
+
+ /* drop non-alnum chars after a space */
+ for (p = strchr(str, ' '); p; p = strchr(p + 1, ' ')) {
+ if (!isalnum(p[1])) {
+ *p = 0;
+ break;
+ }
+ }
+ strlcat(str, sfx, len);
+}
+
+/* copy PCM stream info from @default_str, and override non-NULL entries
+ * from @spec_str and @nid
+ */
+static void setup_pcm_stream(struct hda_pcm_stream *str,
+ const struct hda_pcm_stream *default_str,
+ const struct hda_pcm_stream *spec_str,
+ hda_nid_t nid)
+{
+ *str = *default_str;
+ if (nid)
+ str->nid = nid;
+ if (spec_str) {
+ if (spec_str->substreams)
+ str->substreams = spec_str->substreams;
+ if (spec_str->channels_min)
+ str->channels_min = spec_str->channels_min;
+ if (spec_str->channels_max)
+ str->channels_max = spec_str->channels_max;
+ if (spec_str->rates)
+ str->rates = spec_str->rates;
+ if (spec_str->formats)
+ str->formats = spec_str->formats;
+ if (spec_str->maxbps)
+ str->maxbps = spec_str->maxbps;
+ }
+}
+
+/**
+ * snd_hda_gen_build_pcms - build PCM streams based on the parsed results
+ * @codec: the HDA codec
+ *
+ * Pass this to build_pcms hda_codec_ops.
+ */
+int snd_hda_gen_build_pcms(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct hda_pcm *info;
+ bool have_multi_adcs;
+
+ if (spec->no_analog)
+ goto skip_analog;
+
+ fill_pcm_stream_name(spec->stream_name_analog,
+ sizeof(spec->stream_name_analog),
+ " Analog", codec->core.chip_name);
+ info = snd_hda_codec_pcm_new(codec, "%s", spec->stream_name_analog);
+ if (!info)
+ return -ENOMEM;
+ spec->pcm_rec[0] = info;
+
+ if (spec->multiout.num_dacs > 0) {
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK],
+ &pcm_analog_playback,
+ spec->stream_analog_playback,
+ spec->multiout.dac_nids[0]);
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+ spec->multiout.max_channels;
+ if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
+ spec->autocfg.line_outs == 2)
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap =
+ snd_pcm_2_1_chmaps;
+ }
+ if (spec->num_adc_nids) {
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE],
+ (spec->dyn_adc_switch ?
+ &dyn_adc_pcm_analog_capture : &pcm_analog_capture),
+ spec->stream_analog_capture,
+ spec->adc_nids[0]);
+ }
+
+ skip_analog:
+ /* SPDIF for stream index #1 */
+ if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
+ fill_pcm_stream_name(spec->stream_name_digital,
+ sizeof(spec->stream_name_digital),
+ " Digital", codec->core.chip_name);
+ info = snd_hda_codec_pcm_new(codec, "%s",
+ spec->stream_name_digital);
+ if (!info)
+ return -ENOMEM;
+ codec->follower_dig_outs = spec->multiout.follower_dig_outs;
+ spec->pcm_rec[1] = info;
+ if (spec->dig_out_type)
+ info->pcm_type = spec->dig_out_type;
+ else
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
+ if (spec->multiout.dig_out_nid)
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK],
+ &pcm_digital_playback,
+ spec->stream_digital_playback,
+ spec->multiout.dig_out_nid);
+ if (spec->dig_in_nid)
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE],
+ &pcm_digital_capture,
+ spec->stream_digital_capture,
+ spec->dig_in_nid);
+ }
+
+ if (spec->no_analog)
+ return 0;
+
+ /* If the use of more than one ADC is requested for the current
+ * model, configure a second analog capture-only PCM.
+ */
+ have_multi_adcs = (spec->num_adc_nids > 1) &&
+ !spec->dyn_adc_switch && !spec->auto_mic;
+ /* Additional Analaog capture for index #2 */
+ if (spec->alt_dac_nid || have_multi_adcs) {
+ fill_pcm_stream_name(spec->stream_name_alt_analog,
+ sizeof(spec->stream_name_alt_analog),
+ " Alt Analog", codec->core.chip_name);
+ info = snd_hda_codec_pcm_new(codec, "%s",
+ spec->stream_name_alt_analog);
+ if (!info)
+ return -ENOMEM;
+ spec->pcm_rec[2] = info;
+ if (spec->alt_dac_nid)
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK],
+ &pcm_analog_alt_playback,
+ spec->stream_analog_alt_playback,
+ spec->alt_dac_nid);
+ else
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK],
+ &pcm_null_stream, NULL, 0);
+ if (have_multi_adcs) {
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE],
+ &pcm_analog_alt_capture,
+ spec->stream_analog_alt_capture,
+ spec->adc_nids[1]);
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
+ spec->num_adc_nids - 1;
+ } else {
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE],
+ &pcm_null_stream, NULL, 0);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_build_pcms);
+
+
+/*
+ * Standard auto-parser initializations
+ */
+
+/* configure the given path as a proper output */
+static void set_output_and_unmute(struct hda_codec *codec, int path_idx)
+{
+ struct nid_path *path;
+ hda_nid_t pin;
+
+ path = snd_hda_get_path_from_idx(codec, path_idx);
+ if (!path || !path->depth)
+ return;
+ pin = path->path[path->depth - 1];
+ restore_pin_ctl(codec, pin);
+ snd_hda_activate_path(codec, path, path->active,
+ aamix_default(codec->spec));
+ set_pin_eapd(codec, pin, path->active);
+}
+
+/* initialize primary output paths */
+static void init_multi_out(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->autocfg.line_outs; i++)
+ set_output_and_unmute(codec, spec->out_paths[i]);
+}
+
+
+static void __init_extra_out(struct hda_codec *codec, int num_outs, int *paths)
+{
+ int i;
+
+ for (i = 0; i < num_outs; i++)
+ set_output_and_unmute(codec, paths[i]);
+}
+
+/* initialize hp and speaker paths */
+static void init_extra_out(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT)
+ __init_extra_out(codec, spec->autocfg.hp_outs, spec->hp_paths);
+ if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT)
+ __init_extra_out(codec, spec->autocfg.speaker_outs,
+ spec->speaker_paths);
+}
+
+/* initialize multi-io paths */
+static void init_multi_io(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->multi_ios; i++) {
+ hda_nid_t pin = spec->multi_io[i].pin;
+ struct nid_path *path;
+ path = get_multiio_path(codec, i);
+ if (!path)
+ continue;
+ if (!spec->multi_io[i].ctl_in)
+ spec->multi_io[i].ctl_in =
+ snd_hda_codec_get_pin_target(codec, pin);
+ snd_hda_activate_path(codec, path, path->active,
+ aamix_default(spec));
+ }
+}
+
+static void init_aamix_paths(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (!spec->have_aamix_ctl)
+ return;
+ if (!has_aamix_out_paths(spec))
+ return;
+ update_aamix_paths(codec, spec->aamix_mode, spec->out_paths[0],
+ spec->aamix_out_paths[0],
+ spec->autocfg.line_out_type);
+ update_aamix_paths(codec, spec->aamix_mode, spec->hp_paths[0],
+ spec->aamix_out_paths[1],
+ AUTO_PIN_HP_OUT);
+ update_aamix_paths(codec, spec->aamix_mode, spec->speaker_paths[0],
+ spec->aamix_out_paths[2],
+ AUTO_PIN_SPEAKER_OUT);
+}
+
+/* set up input pins and loopback paths */
+static void init_analog_input(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ if (is_input_pin(codec, nid))
+ restore_pin_ctl(codec, nid);
+
+ /* init loopback inputs */
+ if (spec->mixer_nid) {
+ resume_path_from_idx(codec, spec->loopback_paths[i]);
+ resume_path_from_idx(codec, spec->loopback_merge_path);
+ }
+ }
+}
+
+/* initialize ADC paths */
+static void init_input_src(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->input_mux;
+ struct nid_path *path;
+ int i, c, nums;
+
+ if (spec->dyn_adc_switch)
+ nums = 1;
+ else
+ nums = spec->num_adc_nids;
+
+ for (c = 0; c < nums; c++) {
+ for (i = 0; i < imux->num_items; i++) {
+ path = get_input_path(codec, c, i);
+ if (path) {
+ bool active = path->active;
+ if (i == spec->cur_mux[c])
+ active = true;
+ snd_hda_activate_path(codec, path, active, false);
+ }
+ }
+ if (spec->hp_mic)
+ update_hp_mic(codec, c, true);
+ }
+
+ if (spec->cap_sync_hook)
+ spec->cap_sync_hook(codec, NULL, NULL);
+}
+
+/* set right pin controls for digital I/O */
+static void init_digital(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int i;
+ hda_nid_t pin;
+
+ for (i = 0; i < spec->autocfg.dig_outs; i++)
+ set_output_and_unmute(codec, spec->digout_paths[i]);
+ pin = spec->autocfg.dig_in_pin;
+ if (pin) {
+ restore_pin_ctl(codec, pin);
+ resume_path_from_idx(codec, spec->digin_path);
+ }
+}
+
+/* clear unsol-event tags on unused pins; Conexant codecs seem to leave
+ * invalid unsol tags by some reason
+ */
+static void clear_unsol_on_unused_pins(struct hda_codec *codec)
+{
+ const struct hda_pincfg *pin;
+ int i;
+
+ snd_array_for_each(&codec->init_pins, i, pin) {
+ hda_nid_t nid = pin->nid;
+ if (is_jack_detectable(codec, nid) &&
+ !snd_hda_jack_tbl_get(codec, nid))
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE, 0);
+ }
+}
+
+/**
+ * snd_hda_gen_init - initialize the generic spec
+ * @codec: the HDA codec
+ *
+ * This can be put as hda_codec_ops init function.
+ */
+int snd_hda_gen_init(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (spec->init_hook)
+ spec->init_hook(codec);
+
+ if (!spec->skip_verbs)
+ snd_hda_apply_verbs(codec);
+
+ init_multi_out(codec);
+ init_extra_out(codec);
+ init_multi_io(codec);
+ init_aamix_paths(codec);
+ init_analog_input(codec);
+ init_input_src(codec);
+ init_digital(codec);
+
+ clear_unsol_on_unused_pins(codec);
+
+ sync_all_pin_power_ctls(codec);
+
+ /* call init functions of standard auto-mute helpers */
+ update_automute_all(codec);
+
+ snd_hda_regmap_sync(codec);
+
+ if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook)
+ snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+
+ hda_call_check_power_status(codec, 0x01);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_init);
+
+/**
+ * snd_hda_gen_remove - free the generic spec
+ * @codec: the HDA codec
+ *
+ * This can be put as hda_codec_ops remove function.
+ */
+void snd_hda_gen_remove(struct hda_codec *codec)
+{
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_FREE);
+ snd_hda_gen_spec_free(codec->spec);
+ kfree(codec->spec);
+ codec->spec = NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_remove);
+
+/**
+ * snd_hda_gen_check_power_status - check the loopback power save state
+ * @codec: the HDA codec
+ * @nid: NID to inspect
+ *
+ * This can be put as hda_codec_ops check_power_status function.
+ */
+int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_check_power_status);
+
+
+/*
+ * the generic codec support
+ */
+
+static int snd_hda_gen_probe(struct hda_codec *codec,
+ const struct hda_device_id *id)
+{
+ struct hda_gen_spec *spec;
+ int err;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ snd_hda_gen_spec_init(spec);
+ codec->spec = spec;
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0);
+ if (err < 0)
+ goto error;
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg);
+ if (err < 0)
+ goto error;
+
+ return 0;
+
+error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops generic_codec_ops = {
+ .probe = snd_hda_gen_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = snd_hda_gen_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+static const struct hda_device_id snd_hda_id_generic[] = {
+ HDA_CODEC_ID(0x1af40021, "Generic"), /* QEMU */
+ HDA_CODEC_ID(HDA_CODEC_ID_GENERIC, "Generic"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generic);
+
+static struct hda_codec_driver generic_driver = {
+ .id = snd_hda_id_generic,
+ .ops = &generic_codec_ops,
+};
+
+module_hda_codec_driver(generic_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic HD-audio codec parser");
diff --git a/sound/hda/codecs/generic.h b/sound/hda/codecs/generic.h
new file mode 100644
index 000000000000..524591821f8c
--- /dev/null
+++ b/sound/hda/codecs/generic.h
@@ -0,0 +1,357 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Generic BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ */
+
+#ifndef __SOUND_HDA_GENERIC_H
+#define __SOUND_HDA_GENERIC_H
+
+#include <linux/leds.h>
+#include "hda_auto_parser.h"
+
+struct hda_jack_callback;
+
+/* table entry for multi-io paths */
+struct hda_multi_io {
+ hda_nid_t pin; /* multi-io widget pin NID */
+ hda_nid_t dac; /* DAC to be connected */
+ unsigned int ctl_in; /* cached input-pin control value */
+};
+
+/* Widget connection path
+ *
+ * For output, stored in the order of DAC -> ... -> pin,
+ * for input, pin -> ... -> ADC.
+ *
+ * idx[i] contains the source index number to select on of the widget path[i];
+ * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget
+ * multi[] indicates whether it's a selector widget with multi-connectors
+ * (i.e. the connection selection is mandatory)
+ * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
+ */
+
+#define MAX_NID_PATH_DEPTH 10
+
+enum {
+ NID_PATH_VOL_CTL,
+ NID_PATH_MUTE_CTL,
+ NID_PATH_BOOST_CTL,
+ NID_PATH_NUM_CTLS
+};
+
+struct nid_path {
+ int depth;
+ hda_nid_t path[MAX_NID_PATH_DEPTH];
+ unsigned char idx[MAX_NID_PATH_DEPTH];
+ unsigned char multi[MAX_NID_PATH_DEPTH];
+ unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */
+ bool active:1; /* activated by driver */
+ bool pin_enabled:1; /* pins are enabled */
+ bool pin_fixed:1; /* path with fixed pin */
+ bool stream_enabled:1; /* stream is active */
+};
+
+/* mic/line-in auto switching entry */
+
+#define MAX_AUTO_MIC_PINS 3
+
+struct automic_entry {
+ hda_nid_t pin; /* pin */
+ int idx; /* imux index, -1 = invalid */
+ unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */
+};
+
+/* active stream id */
+enum { STREAM_MULTI_OUT, STREAM_INDEP_HP };
+
+/* PCM hook action */
+enum {
+ HDA_GEN_PCM_ACT_OPEN,
+ HDA_GEN_PCM_ACT_PREPARE,
+ HDA_GEN_PCM_ACT_CLEANUP,
+ HDA_GEN_PCM_ACT_CLOSE,
+};
+
+/* DAC assignment badness table */
+struct badness_table {
+ int no_primary_dac; /* no primary DAC */
+ int no_dac; /* no secondary DACs */
+ int shared_primary; /* primary DAC is shared with main output */
+ int shared_surr; /* secondary DAC shared with main or primary */
+ int shared_clfe; /* third DAC shared with main or primary */
+ int shared_surr_main; /* secondary DAC sahred with main/DAC0 */
+};
+
+extern const struct badness_table hda_main_out_badness;
+extern const struct badness_table hda_extra_out_badness;
+
+struct hda_gen_spec {
+ char stream_name_analog[32]; /* analog PCM stream */
+ const struct hda_pcm_stream *stream_analog_playback;
+ const struct hda_pcm_stream *stream_analog_capture;
+
+ char stream_name_alt_analog[32]; /* alternative analog PCM stream */
+ const struct hda_pcm_stream *stream_analog_alt_playback;
+ const struct hda_pcm_stream *stream_analog_alt_capture;
+
+ char stream_name_digital[32]; /* digital PCM stream */
+ const struct hda_pcm_stream *stream_digital_playback;
+ const struct hda_pcm_stream *stream_digital_capture;
+
+ /* PCM */
+ unsigned int active_streams;
+ struct mutex pcm_mutex;
+
+ /* playback */
+ struct hda_multi_out multiout; /* playback set-up
+ * max_channels, dacs must be set
+ * dig_out_nid and hp_nid are optional
+ */
+ hda_nid_t alt_dac_nid;
+ hda_nid_t follower_dig_outs[3]; /* optional - for auto-parsing */
+ int dig_out_type;
+
+ /* capture */
+ unsigned int num_adc_nids;
+ hda_nid_t adc_nids[AUTO_CFG_MAX_INS];
+ hda_nid_t dig_in_nid; /* digital-in NID; optional */
+ hda_nid_t mixer_nid; /* analog-mixer NID */
+ hda_nid_t mixer_merge_nid; /* aamix merge-point NID (optional) */
+ const char *input_labels[HDA_MAX_NUM_INPUTS];
+ int input_label_idxs[HDA_MAX_NUM_INPUTS];
+
+ /* capture setup for dynamic dual-adc switch */
+ hda_nid_t cur_adc;
+ unsigned int cur_adc_stream_tag;
+ unsigned int cur_adc_format;
+
+ /* capture source */
+ struct hda_input_mux input_mux;
+ unsigned int cur_mux[3];
+
+ /* channel model */
+ /* min_channel_count contains the minimum channel count for primary
+ * outputs. When multi_ios is set, the channels can be configured
+ * between min_channel_count and (min_channel_count + multi_ios * 2).
+ *
+ * ext_channel_count contains the current channel count of the primary
+ * out. This varies in the range above.
+ *
+ * Meanwhile, const_channel_count is the channel count for all outputs
+ * including headphone and speakers. It's a constant value, and the
+ * PCM is set up as max(ext_channel_count, const_channel_count).
+ */
+ int min_channel_count; /* min. channel count for primary out */
+ int ext_channel_count; /* current channel count for primary */
+ int const_channel_count; /* channel count for all */
+
+ /* PCM information */
+ struct hda_pcm *pcm_rec[3]; /* used in build_pcms() */
+
+ /* dynamic controls, init_verbs and input_mux */
+ struct auto_pin_cfg autocfg;
+ struct snd_array kctls;
+ hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+ hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
+ unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
+ /* shared hp/mic */
+ hda_nid_t shared_mic_vref_pin;
+ hda_nid_t hp_mic_pin;
+ int hp_mic_mux_idx;
+
+ /* DAC/ADC lists */
+ int num_all_dacs;
+ hda_nid_t all_dacs[16];
+ int num_all_adcs;
+ hda_nid_t all_adcs[AUTO_CFG_MAX_INS];
+
+ /* path list */
+ struct snd_array paths;
+
+ /* path indices */
+ int out_paths[AUTO_CFG_MAX_OUTS];
+ int hp_paths[AUTO_CFG_MAX_OUTS];
+ int speaker_paths[AUTO_CFG_MAX_OUTS];
+ int aamix_out_paths[3];
+ int digout_paths[AUTO_CFG_MAX_OUTS];
+ int input_paths[HDA_MAX_NUM_INPUTS][AUTO_CFG_MAX_INS];
+ int loopback_paths[HDA_MAX_NUM_INPUTS];
+ int loopback_merge_path;
+ int digin_path;
+
+ /* auto-mic stuff */
+ int am_num_entries;
+ struct automic_entry am_entry[MAX_AUTO_MIC_PINS];
+
+ /* for pin sensing */
+ /* current status; set in hda_generic.c */
+ unsigned int hp_jack_present:1;
+ unsigned int line_jack_present:1;
+ unsigned int speaker_muted:1; /* current status of speaker mute */
+ unsigned int line_out_muted:1; /* current status of LO mute */
+
+ /* internal states of automute / autoswitch behavior */
+ unsigned int auto_mic:1;
+ unsigned int automute_speaker:1; /* automute speaker outputs */
+ unsigned int automute_lo:1; /* automute LO outputs */
+
+ /* capabilities detected by parser */
+ unsigned int detect_hp:1; /* Headphone detection enabled */
+ unsigned int detect_lo:1; /* Line-out detection enabled */
+ unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */
+ unsigned int automute_lo_possible:1; /* there are line outs and HP */
+
+ /* additional parameters set by codec drivers */
+ unsigned int master_mute:1; /* master mute over all */
+ unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */
+ unsigned int line_in_auto_switch:1; /* allow line-in auto switch */
+ unsigned int auto_mute_via_amp:1; /* auto-mute via amp instead of pinctl */
+
+ /* parser behavior flags; set before snd_hda_gen_parse_auto_config() */
+ unsigned int suppress_auto_mute:1; /* suppress input jack auto mute */
+ unsigned int suppress_auto_mic:1; /* suppress input jack auto switch */
+
+ /* other parse behavior flags */
+ unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */
+ unsigned int hp_mic:1; /* Allow HP as a mic-in */
+ unsigned int suppress_hp_mic_detect:1; /* Don't detect HP/mic */
+ unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
+ unsigned int no_multi_io:1; /* Don't try multi I/O config */
+ unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
+ unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
+ unsigned int own_eapd_ctl:1; /* set EAPD by own function */
+ unsigned int keep_eapd_on:1; /* don't turn off EAPD automatically */
+ unsigned int vmaster_mute_led:1; /* add SPK-LED flag to vmaster mute switch */
+ unsigned int mic_mute_led:1; /* add MIC-LED flag to capture mute switch */
+ unsigned int indep_hp:1; /* independent HP supported */
+ unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */
+ unsigned int add_stereo_mix_input:2; /* add aamix as a capture src */
+ unsigned int add_jack_modes:1; /* add i/o jack mode enum ctls */
+ unsigned int power_down_unused:1; /* power down unused widgets */
+ unsigned int dac_min_mute:1; /* minimal = mute for DACs */
+ unsigned int suppress_vmaster:1; /* don't create vmaster kctls */
+
+ /* other internal flags */
+ unsigned int no_analog:1; /* digital I/O only */
+ unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */
+ unsigned int indep_hp_enabled:1; /* independent HP enabled */
+ unsigned int have_aamix_ctl:1;
+ unsigned int hp_mic_jack_modes:1;
+ unsigned int skip_verbs:1; /* don't apply verbs at snd_hda_gen_init() */
+
+ /* additional mute flags (only effective with auto_mute_via_amp=1) */
+ u64 mute_bits;
+
+ /* bitmask for skipping volume controls */
+ u64 out_vol_mask;
+
+ /* badness tables for output path evaluations */
+ const struct badness_table *main_out_badness;
+ const struct badness_table *extra_out_badness;
+
+ /* preferred pin/DAC pairs; an array of paired NIDs */
+ const hda_nid_t *preferred_dacs;
+
+ /* loopback mixing mode */
+ bool aamix_mode;
+
+ /* digital beep */
+ hda_nid_t beep_nid;
+
+ /* for virtual master */
+ hda_nid_t vmaster_nid;
+ unsigned int vmaster_tlv[4];
+ struct hda_vmaster_mute_hook vmaster_mute;
+
+ struct hda_loopback_check loopback;
+ struct snd_array loopback_list;
+
+ /* multi-io */
+ int multi_ios;
+ struct hda_multi_io multi_io[4];
+
+ /* hooks */
+ void (*init_hook)(struct hda_codec *codec);
+ void (*automute_hook)(struct hda_codec *codec);
+ void (*cap_sync_hook)(struct hda_codec *codec,
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+ /* PCM hooks */
+ void (*pcm_playback_hook)(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action);
+ void (*pcm_capture_hook)(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action);
+
+ /* automute / autoswitch hooks */
+ void (*hp_automute_hook)(struct hda_codec *codec,
+ struct hda_jack_callback *cb);
+ void (*line_automute_hook)(struct hda_codec *codec,
+ struct hda_jack_callback *cb);
+ void (*mic_autoswitch_hook)(struct hda_codec *codec,
+ struct hda_jack_callback *cb);
+
+ /* leds */
+ struct led_classdev *led_cdevs[NUM_AUDIO_LEDS];
+};
+
+/* values for add_stereo_mix_input flag */
+enum {
+ HDA_HINT_STEREO_MIX_DISABLE, /* No stereo mix input */
+ HDA_HINT_STEREO_MIX_ENABLE, /* Add stereo mix input */
+ HDA_HINT_STEREO_MIX_AUTO, /* Add only if auto-mic is disabled */
+};
+
+int snd_hda_gen_spec_init(struct hda_gen_spec *spec);
+
+int snd_hda_gen_init(struct hda_codec *codec);
+void snd_hda_gen_remove(struct hda_codec *codec);
+
+int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path);
+struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx);
+struct nid_path *
+snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
+ hda_nid_t to_nid, int anchor_nid);
+void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
+ bool enable, bool add_aamix);
+
+struct snd_kcontrol_new *
+snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name,
+ const struct snd_kcontrol_new *temp);
+
+int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg);
+int snd_hda_gen_build_controls(struct hda_codec *codec);
+int snd_hda_gen_build_pcms(struct hda_codec *codec);
+
+/* standard jack event callbacks */
+void snd_hda_gen_hp_automute(struct hda_codec *codec,
+ struct hda_jack_callback *jack);
+void snd_hda_gen_line_automute(struct hda_codec *codec,
+ struct hda_jack_callback *jack);
+void snd_hda_gen_mic_autoswitch(struct hda_codec *codec,
+ struct hda_jack_callback *jack);
+void snd_hda_gen_update_outputs(struct hda_codec *codec);
+
+int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid);
+unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state);
+void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on);
+int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin);
+
+int snd_hda_gen_add_mute_led_cdev(struct hda_codec *codec,
+ int (*callback)(struct led_classdev *,
+ enum led_brightness));
+int snd_hda_gen_add_micmute_led_cdev(struct hda_codec *codec,
+ int (*callback)(struct led_classdev *,
+ enum led_brightness));
+bool snd_hda_gen_shutup_speakers(struct hda_codec *codec);
+
+#endif /* __SOUND_HDA_GENERIC_H */
diff --git a/sound/hda/codecs/hdmi/Kconfig b/sound/hda/codecs/hdmi/Kconfig
new file mode 100644
index 000000000000..6ea3553ba9f8
--- /dev/null
+++ b/sound/hda/codecs/hdmi/Kconfig
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menuconfig SND_HDA_CODEC_HDMI
+ tristate "HD-audio HDMI codec support"
+ help
+ Say Y or M here to include HD-audio HDMI/DislayPort codec support.
+
+ This will enable all HDMI/DP codec drivers as default, but you can
+ enable/disable each codec driver individually, too (only when
+ CONFIG_EXPERT is set).
+
+if SND_HDA_CODEC_HDMI
+
+config SND_HDA_CODEC_HDMI_GENERIC
+ tristate "Generic HDMI/DisplayPort HD-audio codec support" if EXPERT
+ select SND_DYNAMIC_MINORS
+ select SND_PCM_ELD
+ default y
+ help
+ Say Y or M here to include Generic HDMI and DisplayPort HD-audio
+ codec support.
+
+ Note that this option mandatorily enables CONFIG_SND_DYNAMIC_MINORS
+ to assure the multiple streams for DP-MST support.
+
+config SND_HDA_CODEC_HDMI_SIMPLE
+ tristate "Simple HDMI/DisplayPort HD-audio codec support" if EXPERT
+ default y
+ help
+ Say Y or M here to include Simple HDMI and DisplayPort HD-audio
+ codec support for VIA and other codecs.
+
+config SND_HDA_CODEC_HDMI_INTEL
+ tristate "Intel HDMI/DisplayPort HD-audio codec support" if EXPERT
+ select SND_HDA_CODEC_HDMI_GENERIC
+ default y
+ help
+ Say Y or M here to include Intel graphics HDMI and DisplayPort
+ HD-audio codec support.
+
+config SND_HDA_INTEL_HDMI_SILENT_STREAM
+ bool "Enable Silent Stream always for HDMI"
+ depends on SND_HDA_CODEC_HDMI_INTEL
+ help
+ Say Y to enable HD-Audio Keep Alive (KAE) aka Silent Stream
+ for HDMI on hardware that supports the feature.
+
+ When enabled, the HDMI/DisplayPort codec will continue to provide
+ a continuous clock and a valid but silent data stream to
+ any connected external receiver. This allows to avoid gaps
+ at start of playback. Many receivers require multiple seconds
+ to start playing audio after the clock has been stopped.
+ This feature can impact power consumption as resources
+ are kept reserved both at transmitter and receiver.
+
+config SND_HDA_CODEC_HDMI_ATI
+ tristate "AMD/ATI HDMI/DisplayPort HD-audio codec support" if EXPERT
+ select SND_HDA_CODEC_HDMI_GENERIC
+ default y
+ help
+ Say Y or M here to include AMD/ATI graphics HDMI and DisplayPort
+ HD-audio codec support.
+
+config SND_HDA_CODEC_HDMI_NVIDIA
+ tristate "Nvidia HDMI/DisplayPort HD-audio codec support" if EXPERT
+ select SND_HDA_CODEC_HDMI_GENERIC
+ default y
+ help
+ Say Y or M here to include HDMI and DisplayPort HD-audio codec
+ support for the recent Nvidia graphics cards.
+
+config SND_HDA_CODEC_HDMI_NVIDIA_MCP
+ tristate "Legacy Nvidia HDMI/DisplayPort HD-audio codec support" if EXPERT
+ select SND_HDA_CODEC_HDMI_SIMPLE
+ default y
+ help
+ Say Y or M here to include HDMI and DisplayPort HD-audio codec
+ support for the legacy Nvidia graphics like MCP73, MCP67, MCP77/78.
+
+config SND_HDA_CODEC_HDMI_TEGRA
+ tristate "Nvidia Tegra HDMI/DisplayPort HD-audio codec support" if EXPERT
+ select SND_HDA_CODEC_HDMI_GENERIC
+ default y
+ help
+ Say Y or M here to include HDMI and DisplayPort HD-audio codec
+ support for Nvidia Tegra.
+
+endif
diff --git a/sound/hda/codecs/hdmi/Makefile b/sound/hda/codecs/hdmi/Makefile
new file mode 100644
index 000000000000..0e49a9421e3b
--- /dev/null
+++ b/sound/hda/codecs/hdmi/Makefile
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+subdir-ccflags-y += -I$(src)/../../common
+
+snd-hda-codec-hdmi-y := hdmi.o eld.o
+snd-hda-codec-simplehdmi-y := simplehdmi.o
+snd-hda-codec-intelhdmi-y := intelhdmi.o
+snd-hda-codec-atihdmi-y := atihdmi.o
+snd-hda-codec-nvhdmi-y := nvhdmi.o
+snd-hda-codec-nvhdmi-mcp-y := nvhdmi-mcp.o
+snd-hda-codec-tegrahdmi-y := tegrahdmi.o
+
+obj-$(CONFIG_SND_HDA_CODEC_HDMI_GENERIC) += snd-hda-codec-hdmi.o
+obj-$(CONFIG_SND_HDA_CODEC_HDMI_SIMPLE) += snd-hda-codec-simplehdmi.o
+obj-$(CONFIG_SND_HDA_CODEC_HDMI_INTEL) += snd-hda-codec-intelhdmi.o
+obj-$(CONFIG_SND_HDA_CODEC_HDMI_ATI) += snd-hda-codec-atihdmi.o
+obj-$(CONFIG_SND_HDA_CODEC_HDMI_NVIDIA) += snd-hda-codec-nvhdmi.o
+obj-$(CONFIG_SND_HDA_CODEC_HDMI_NVIDIA_MCP) += snd-hda-codec-nvhdmi-mcp.o
+obj-$(CONFIG_SND_HDA_CODEC_HDMI_TEGRA) += snd-hda-codec-tegrahdmi.o
diff --git a/sound/hda/codecs/hdmi/atihdmi.c b/sound/hda/codecs/hdmi/atihdmi.c
new file mode 100644
index 000000000000..44366f75de33
--- /dev/null
+++ b/sound/hda/codecs/hdmi/atihdmi.c
@@ -0,0 +1,615 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ATI/AMD codec support
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/unaligned.h>
+#include <sound/core.h>
+#include <sound/tlv.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hdmi_local.h"
+
+#define is_amdhdmi_rev3_or_later(codec) \
+ ((codec)->core.vendor_id == 0x1002aa01 && \
+ ((codec)->core.revision_id & 0xff00) >= 0x0300)
+#define has_amd_full_remap_support(codec) is_amdhdmi_rev3_or_later(codec)
+
+/* ATI/AMD specific HDA pin verbs, see the AMD HDA Verbs specification */
+#define ATI_VERB_SET_CHANNEL_ALLOCATION 0x771
+#define ATI_VERB_SET_DOWNMIX_INFO 0x772
+#define ATI_VERB_SET_MULTICHANNEL_01 0x777
+#define ATI_VERB_SET_MULTICHANNEL_23 0x778
+#define ATI_VERB_SET_MULTICHANNEL_45 0x779
+#define ATI_VERB_SET_MULTICHANNEL_67 0x77a
+#define ATI_VERB_SET_HBR_CONTROL 0x77c
+#define ATI_VERB_SET_MULTICHANNEL_1 0x785
+#define ATI_VERB_SET_MULTICHANNEL_3 0x786
+#define ATI_VERB_SET_MULTICHANNEL_5 0x787
+#define ATI_VERB_SET_MULTICHANNEL_7 0x788
+#define ATI_VERB_SET_MULTICHANNEL_MODE 0x789
+#define ATI_VERB_GET_CHANNEL_ALLOCATION 0xf71
+#define ATI_VERB_GET_DOWNMIX_INFO 0xf72
+#define ATI_VERB_GET_MULTICHANNEL_01 0xf77
+#define ATI_VERB_GET_MULTICHANNEL_23 0xf78
+#define ATI_VERB_GET_MULTICHANNEL_45 0xf79
+#define ATI_VERB_GET_MULTICHANNEL_67 0xf7a
+#define ATI_VERB_GET_HBR_CONTROL 0xf7c
+#define ATI_VERB_GET_MULTICHANNEL_1 0xf85
+#define ATI_VERB_GET_MULTICHANNEL_3 0xf86
+#define ATI_VERB_GET_MULTICHANNEL_5 0xf87
+#define ATI_VERB_GET_MULTICHANNEL_7 0xf88
+#define ATI_VERB_GET_MULTICHANNEL_MODE 0xf89
+
+/* AMD specific HDA cvt verbs */
+#define ATI_VERB_SET_RAMP_RATE 0x770
+#define ATI_VERB_GET_RAMP_RATE 0xf70
+
+#define ATI_OUT_ENABLE 0x1
+
+#define ATI_MULTICHANNEL_MODE_PAIRED 0
+#define ATI_MULTICHANNEL_MODE_SINGLE 1
+
+#define ATI_HBR_CAPABLE 0x01
+#define ATI_HBR_ENABLE 0x10
+
+/* ATI/AMD specific ELD emulation */
+
+#define ATI_VERB_SET_AUDIO_DESCRIPTOR 0x776
+#define ATI_VERB_SET_SINK_INFO_INDEX 0x780
+#define ATI_VERB_GET_SPEAKER_ALLOCATION 0xf70
+#define ATI_VERB_GET_AUDIO_DESCRIPTOR 0xf76
+#define ATI_VERB_GET_AUDIO_VIDEO_DELAY 0xf7b
+#define ATI_VERB_GET_SINK_INFO_INDEX 0xf80
+#define ATI_VERB_GET_SINK_INFO_DATA 0xf81
+
+#define ATI_SPKALLOC_SPKALLOC 0x007f
+#define ATI_SPKALLOC_TYPE_HDMI 0x0100
+#define ATI_SPKALLOC_TYPE_DISPLAYPORT 0x0200
+
+/* first three bytes are just standard SAD */
+#define ATI_AUDIODESC_CHANNELS 0x00000007
+#define ATI_AUDIODESC_RATES 0x0000ff00
+#define ATI_AUDIODESC_LPCM_STEREO_RATES 0xff000000
+
+/* in standard HDMI VSDB format */
+#define ATI_DELAY_VIDEO_LATENCY 0x000000ff
+#define ATI_DELAY_AUDIO_LATENCY 0x0000ff00
+
+enum ati_sink_info_idx {
+ ATI_INFO_IDX_MANUFACTURER_ID = 0,
+ ATI_INFO_IDX_PRODUCT_ID = 1,
+ ATI_INFO_IDX_SINK_DESC_LEN = 2,
+ ATI_INFO_IDX_PORT_ID_LOW = 3,
+ ATI_INFO_IDX_PORT_ID_HIGH = 4,
+ ATI_INFO_IDX_SINK_DESC_FIRST = 5,
+ ATI_INFO_IDX_SINK_DESC_LAST = 22, /* max len 18 bytes */
+};
+
+static int get_eld_ati(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char *buf, int *eld_size, bool rev3_or_later)
+{
+ int spkalloc, ati_sad, aud_synch;
+ int sink_desc_len = 0;
+ int pos, i;
+
+ /* ATI/AMD does not have ELD, emulate it */
+
+ spkalloc = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SPEAKER_ALLOCATION, 0);
+
+ if (spkalloc <= 0) {
+ codec_info(codec, "HDMI ATI/AMD: no speaker allocation for ELD\n");
+ return -EINVAL;
+ }
+
+ memset(buf, 0, ELD_FIXED_BYTES + ELD_MAX_MNL + ELD_MAX_SAD * 3);
+
+ /* version */
+ buf[0] = ELD_VER_CEA_861D << 3;
+
+ /* speaker allocation from EDID */
+ buf[7] = spkalloc & ATI_SPKALLOC_SPKALLOC;
+
+ /* is DisplayPort? */
+ if (spkalloc & ATI_SPKALLOC_TYPE_DISPLAYPORT)
+ buf[5] |= 0x04;
+
+ pos = ELD_FIXED_BYTES;
+
+ if (rev3_or_later) {
+ int sink_info;
+
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_LOW);
+ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+ put_unaligned_le32(sink_info, buf + 8);
+
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_HIGH);
+ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+ put_unaligned_le32(sink_info, buf + 12);
+
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_MANUFACTURER_ID);
+ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+ put_unaligned_le16(sink_info, buf + 16);
+
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PRODUCT_ID);
+ sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+ put_unaligned_le16(sink_info, buf + 18);
+
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_LEN);
+ sink_desc_len = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+
+ if (sink_desc_len > ELD_MAX_MNL) {
+ codec_info(codec, "HDMI ATI/AMD: Truncating HDMI sink description with length %d\n",
+ sink_desc_len);
+ sink_desc_len = ELD_MAX_MNL;
+ }
+
+ buf[4] |= sink_desc_len;
+
+ for (i = 0; i < sink_desc_len; i++) {
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_FIRST + i);
+ buf[pos++] = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
+ }
+ }
+
+ for (i = AUDIO_CODING_TYPE_LPCM; i <= AUDIO_CODING_TYPE_WMAPRO; i++) {
+ if (i == AUDIO_CODING_TYPE_SACD || i == AUDIO_CODING_TYPE_DST)
+ continue; /* not handled by ATI/AMD */
+
+ snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_AUDIO_DESCRIPTOR, i << 3);
+ ati_sad = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_DESCRIPTOR, 0);
+
+ if (ati_sad <= 0)
+ continue;
+
+ if (ati_sad & ATI_AUDIODESC_RATES) {
+ /* format is supported, copy SAD as-is */
+ buf[pos++] = (ati_sad & 0x0000ff) >> 0;
+ buf[pos++] = (ati_sad & 0x00ff00) >> 8;
+ buf[pos++] = (ati_sad & 0xff0000) >> 16;
+ }
+
+ if (i == AUDIO_CODING_TYPE_LPCM
+ && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES)
+ && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) >> 16 != (ati_sad & ATI_AUDIODESC_RATES)) {
+ /* for PCM there is a separate stereo rate mask */
+ buf[pos++] = ((ati_sad & 0x000000ff) & ~ATI_AUDIODESC_CHANNELS) | 0x1;
+ /* rates from the extra byte */
+ buf[pos++] = (ati_sad & 0xff000000) >> 24;
+ buf[pos++] = (ati_sad & 0x00ff0000) >> 16;
+ }
+ }
+
+ if (pos == ELD_FIXED_BYTES + sink_desc_len) {
+ codec_info(codec, "HDMI ATI/AMD: no audio descriptors for ELD\n");
+ return -EINVAL;
+ }
+
+ /*
+ * HDMI VSDB latency format:
+ * separately for both audio and video:
+ * 0 field not valid or unknown latency
+ * [1..251] msecs = (x-1)*2 (max 500ms with x = 251 = 0xfb)
+ * 255 audio/video not supported
+ *
+ * HDA latency format:
+ * single value indicating video latency relative to audio:
+ * 0 unknown or 0ms
+ * [1..250] msecs = x*2 (max 500ms with x = 250 = 0xfa)
+ * [251..255] reserved
+ */
+ aud_synch = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_VIDEO_DELAY, 0);
+ if ((aud_synch & ATI_DELAY_VIDEO_LATENCY) && (aud_synch & ATI_DELAY_AUDIO_LATENCY)) {
+ int video_latency_hdmi = (aud_synch & ATI_DELAY_VIDEO_LATENCY);
+ int audio_latency_hdmi = (aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8;
+
+ if (video_latency_hdmi <= 0xfb && audio_latency_hdmi <= 0xfb &&
+ video_latency_hdmi > audio_latency_hdmi)
+ buf[6] = video_latency_hdmi - audio_latency_hdmi;
+ /* else unknown/invalid or 0ms or video ahead of audio, so use zero */
+ }
+
+ /* SAD count */
+ buf[5] |= ((pos - ELD_FIXED_BYTES - sink_desc_len) / 3) << 4;
+
+ /* Baseline ELD block length is 4-byte aligned */
+ pos = round_up(pos, 4);
+
+ /* Baseline ELD length (4-byte header is not counted in) */
+ buf[2] = (pos - 4) / 4;
+
+ *eld_size = pos;
+
+ return 0;
+}
+
+static int atihdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, unsigned char *buf, int *eld_size)
+{
+ WARN_ON(dev_id != 0);
+ /* call hda_eld.c ATI/AMD-specific function */
+ return get_eld_ati(codec, nid, buf, eld_size,
+ is_amdhdmi_rev3_or_later(codec));
+}
+
+static void atihdmi_pin_setup_infoframe(struct hda_codec *codec,
+ hda_nid_t pin_nid, int dev_id, int ca,
+ int active_channels, int conn_type)
+{
+ WARN_ON(dev_id != 0);
+ snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca);
+}
+
+static int atihdmi_paired_swap_fc_lfe(int pos)
+{
+ /*
+ * ATI/AMD have automatic FC/LFE swap built-in
+ * when in pairwise mapping mode.
+ */
+
+ switch (pos) {
+ /* see channel_allocations[].speakers[] */
+ case 2: return 3;
+ case 3: return 2;
+ default: return pos;
+ }
+}
+
+static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap,
+ int ca, int chs, unsigned char *map)
+{
+ struct hdac_cea_channel_speaker_allocation *cap;
+ int i, j;
+
+ /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */
+
+ cap = snd_hdac_get_ch_alloc_from_ca(ca);
+ for (i = 0; i < chs; ++i) {
+ int mask = snd_hdac_chmap_to_spk_mask(map[i]);
+ bool ok = false;
+ bool companion_ok = false;
+
+ if (!mask)
+ continue;
+
+ for (j = 0 + i % 2; j < 8; j += 2) {
+ int chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j);
+
+ if (cap->speakers[chan_idx] == mask) {
+ /* channel is in a supported position */
+ ok = true;
+
+ if (i % 2 == 0 && i + 1 < chs) {
+ /* even channel, check the odd companion */
+ int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1);
+ int comp_mask_req = snd_hdac_chmap_to_spk_mask(map[i+1]);
+ int comp_mask_act = cap->speakers[comp_chan_idx];
+
+ if (comp_mask_req == comp_mask_act)
+ companion_ok = true;
+ else
+ return -EINVAL;
+ }
+ break;
+ }
+ }
+
+ if (!ok)
+ return -EINVAL;
+
+ if (companion_ok)
+ i++; /* companion channel already checked */
+ }
+
+ return 0;
+}
+
+static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac,
+ hda_nid_t pin_nid, int hdmi_slot, int stream_channel)
+{
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
+ int verb;
+ int ati_channel_setup = 0;
+
+ if (hdmi_slot > 7)
+ return -EINVAL;
+
+ if (!has_amd_full_remap_support(codec)) {
+ hdmi_slot = atihdmi_paired_swap_fc_lfe(hdmi_slot);
+
+ /* In case this is an odd slot but without stream channel, do not
+ * disable the slot since the corresponding even slot could have a
+ * channel. In case neither have a channel, the slot pair will be
+ * disabled when this function is called for the even slot.
+ */
+ if (hdmi_slot % 2 != 0 && stream_channel == 0xf)
+ return 0;
+
+ hdmi_slot -= hdmi_slot % 2;
+
+ if (stream_channel != 0xf)
+ stream_channel -= stream_channel % 2;
+ }
+
+ verb = ATI_VERB_SET_MULTICHANNEL_01 + hdmi_slot/2 + (hdmi_slot % 2) * 0x00e;
+
+ /* ati_channel_setup format: [7..4] = stream_channel_id, [1] = mute, [0] = enable */
+
+ if (stream_channel != 0xf)
+ ati_channel_setup = (stream_channel << 4) | ATI_OUT_ENABLE;
+
+ return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup);
+}
+
+static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac,
+ hda_nid_t pin_nid, int asp_slot)
+{
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
+ bool was_odd = false;
+ int ati_asp_slot = asp_slot;
+ int verb;
+ int ati_channel_setup;
+
+ if (asp_slot > 7)
+ return -EINVAL;
+
+ if (!has_amd_full_remap_support(codec)) {
+ ati_asp_slot = atihdmi_paired_swap_fc_lfe(asp_slot);
+ if (ati_asp_slot % 2 != 0) {
+ ati_asp_slot -= 1;
+ was_odd = true;
+ }
+ }
+
+ verb = ATI_VERB_GET_MULTICHANNEL_01 + ati_asp_slot/2 + (ati_asp_slot % 2) * 0x00e;
+
+ ati_channel_setup = snd_hda_codec_read(codec, pin_nid, 0, verb, 0);
+
+ if (!(ati_channel_setup & ATI_OUT_ENABLE))
+ return 0xf;
+
+ return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd;
+}
+
+static int atihdmi_paired_chmap_cea_alloc_validate_get_type(
+ struct hdac_chmap *chmap,
+ struct hdac_cea_channel_speaker_allocation *cap,
+ int channels)
+{
+ int c;
+
+ /*
+ * Pre-rev3 ATI/AMD codecs operate in a paired channel mode, so
+ * we need to take that into account (a single channel may take 2
+ * channel slots if we need to carry a silent channel next to it).
+ * On Rev3+ AMD codecs this function is not used.
+ */
+ int chanpairs = 0;
+
+ /* We only produce even-numbered channel count TLVs */
+ if ((channels % 2) != 0)
+ return -1;
+
+ for (c = 0; c < 7; c += 2) {
+ if (cap->speakers[c] || cap->speakers[c+1])
+ chanpairs++;
+ }
+
+ if (chanpairs * 2 != channels)
+ return -1;
+
+ return SNDRV_CTL_TLVT_CHMAP_PAIRED;
+}
+
+static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
+ struct hdac_cea_channel_speaker_allocation *cap,
+ unsigned int *chmap, int channels)
+{
+ /* produce paired maps for pre-rev3 ATI/AMD codecs */
+ int count = 0;
+ int c;
+
+ for (c = 7; c >= 0; c--) {
+ int chan = 7 - atihdmi_paired_swap_fc_lfe(7 - c);
+ int spk = cap->speakers[chan];
+
+ if (!spk) {
+ /* add N/A channel if the companion channel is occupied */
+ if (cap->speakers[chan + (chan % 2 ? -1 : 1)])
+ chmap[count++] = SNDRV_CHMAP_NA;
+
+ continue;
+ }
+
+ chmap[count++] = snd_hdac_spk_to_chmap(spk);
+ }
+
+ WARN_ON(count != channels);
+}
+
+static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
+ int dev_id, bool hbr)
+{
+ int hbr_ctl, hbr_ctl_new;
+
+ WARN_ON(dev_id != 0);
+
+ hbr_ctl = snd_hda_codec_read(codec, pin_nid, 0, ATI_VERB_GET_HBR_CONTROL, 0);
+ if (hbr_ctl >= 0 && (hbr_ctl & ATI_HBR_CAPABLE)) {
+ if (hbr)
+ hbr_ctl_new = hbr_ctl | ATI_HBR_ENABLE;
+ else
+ hbr_ctl_new = hbr_ctl & ~ATI_HBR_ENABLE;
+
+ codec_dbg(codec,
+ "%s: NID=0x%x, %shbr-ctl=0x%x\n",
+ __func__,
+ pin_nid,
+ hbr_ctl == hbr_ctl_new ? "" : "new-",
+ hbr_ctl_new);
+
+ if (hbr_ctl != hbr_ctl_new)
+ snd_hda_codec_write(codec, pin_nid, 0,
+ ATI_VERB_SET_HBR_CONTROL,
+ hbr_ctl_new);
+
+ } else if (hbr)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int atihdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
+ hda_nid_t pin_nid, int dev_id,
+ u32 stream_tag, int format)
+{
+ if (is_amdhdmi_rev3_or_later(codec)) {
+ int ramp_rate = 180; /* default as per AMD spec */
+ /* disable ramp-up/down for non-pcm as per AMD spec */
+ if (format & AC_FMT_TYPE_NON_PCM)
+ ramp_rate = 0;
+
+ snd_hda_codec_write(codec, cvt_nid, 0, ATI_VERB_SET_RAMP_RATE, ramp_rate);
+ }
+
+ return snd_hda_hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
+ stream_tag, format);
+}
+
+
+static int atihdmi_init(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx, err;
+
+ err = snd_hda_hdmi_generic_init(codec);
+
+ if (err)
+ return err;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+
+ /* make sure downmix information in infoframe is zero */
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0, ATI_VERB_SET_DOWNMIX_INFO, 0);
+
+ /* enable channel-wise remap mode if supported */
+ if (has_amd_full_remap_support(codec))
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0,
+ ATI_VERB_SET_MULTICHANNEL_MODE,
+ ATI_MULTICHANNEL_MODE_SINGLE);
+ }
+ codec->auto_runtime_pm = 1;
+
+ return 0;
+}
+
+/* map from pin NID to port; port is 0-based */
+/* for AMD: assume widget NID starting from 3, with step 2 (3, 5, 7, ...) */
+static int atihdmi_pin2port(void *audio_ptr, int pin_nid)
+{
+ return pin_nid / 2 - 1;
+}
+
+/* reverse-map from port to pin NID: see above */
+static int atihdmi_port2pin(struct hda_codec *codec, int port)
+{
+ return port * 2 + 3;
+}
+
+static const struct drm_audio_component_audio_ops atihdmi_audio_ops = {
+ .pin2port = atihdmi_pin2port,
+ .pin_eld_notify = snd_hda_hdmi_acomp_pin_eld_notify,
+ .master_bind = snd_hda_hdmi_acomp_master_bind,
+ .master_unbind = snd_hda_hdmi_acomp_master_unbind,
+};
+
+static int atihdmi_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct hdmi_spec *spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ int err, cvt_idx;
+
+ err = snd_hda_hdmi_generic_probe(codec);
+ if (err)
+ return err;
+
+ spec = codec->spec;
+
+ spec->static_pcm_mapping = true;
+
+ spec->ops.pin_get_eld = atihdmi_pin_get_eld;
+ spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe;
+ spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup;
+ spec->ops.setup_stream = atihdmi_setup_stream;
+
+ spec->chmap.ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel;
+ spec->chmap.ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel;
+
+ if (!has_amd_full_remap_support(codec)) {
+ /* override to ATI/AMD-specific versions with pairwise mapping */
+ spec->chmap.ops.chmap_cea_alloc_validate_get_type =
+ atihdmi_paired_chmap_cea_alloc_validate_get_type;
+ spec->chmap.ops.cea_alloc_to_tlv_chmap =
+ atihdmi_paired_cea_alloc_to_tlv_chmap;
+ spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate;
+ }
+
+ /* ATI/AMD converters do not advertise all of their capabilities */
+ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
+ per_cvt = get_cvt(spec, cvt_idx);
+ per_cvt->channels_max = max(per_cvt->channels_max, 8u);
+ per_cvt->rates |= SUPPORTED_RATES;
+ per_cvt->formats |= SUPPORTED_FORMATS;
+ per_cvt->maxbps = max(per_cvt->maxbps, 24u);
+ }
+
+ spec->chmap.channels_max = max(spec->chmap.channels_max, 8u);
+
+ /* AMD GPUs have neither EPSS nor CLKSTOP bits, hence preventing
+ * the link-down as is. Tell the core to allow it.
+ */
+ codec->link_down_at_suspend = 1;
+
+ snd_hda_hdmi_acomp_init(codec, &atihdmi_audio_ops, atihdmi_port2pin);
+
+ return 0;
+}
+
+static const struct hda_codec_ops atihdmi_codec_ops = {
+ .probe = atihdmi_probe,
+ .remove = snd_hda_hdmi_generic_remove,
+ .init = atihdmi_init,
+ .build_pcms = snd_hda_hdmi_generic_build_pcms,
+ .build_controls = snd_hda_hdmi_generic_build_controls,
+ .unsol_event = snd_hda_hdmi_generic_unsol_event,
+ .suspend = snd_hda_hdmi_generic_suspend,
+ .resume = snd_hda_hdmi_generic_resume,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_atihdmi[] = {
+ HDA_CODEC_ID(0x1002793c, "RS600 HDMI"),
+ HDA_CODEC_ID(0x10027919, "RS600 HDMI"),
+ HDA_CODEC_ID(0x1002791a, "RS690/780 HDMI"),
+ HDA_CODEC_ID(0x1002aa01, "R6xx HDMI"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_atihdmi);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AMD/ATI HDMI HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI");
+
+static struct hda_codec_driver atihdmi_driver = {
+ .id = snd_hda_id_atihdmi,
+ .ops = &atihdmi_codec_ops,
+};
+
+module_hda_codec_driver(atihdmi_driver);
diff --git a/sound/hda/codecs/hdmi/eld.c b/sound/hda/codecs/hdmi/eld.c
new file mode 100644
index 000000000000..1464fd1c675b
--- /dev/null
+++ b/sound/hda/codecs/hdmi/eld.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Generic routines and proc interface for ELD(EDID Like Data) information
+ *
+ * Copyright(c) 2008 Intel Corporation.
+ * Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi>
+ *
+ * Authors:
+ * Wu Fengguang <wfg@linux.intel.com>
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/hda_chmap.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+
+enum cea_edid_versions {
+ CEA_EDID_VER_NONE = 0,
+ CEA_EDID_VER_CEA861 = 1,
+ CEA_EDID_VER_CEA861A = 2,
+ CEA_EDID_VER_CEA861BCD = 3,
+ CEA_EDID_VER_RESERVED = 4,
+};
+
+/*
+ * The following two lists are shared between
+ * - HDMI audio InfoFrame (source to sink)
+ * - CEA E-EDID Extension (sink to source)
+ */
+
+static unsigned int hdmi_get_eld_data(struct hda_codec *codec, hda_nid_t nid,
+ int byte_index)
+{
+ unsigned int val;
+
+ val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_HDMI_ELDD, byte_index);
+#ifdef BE_PARANOID
+ codec_info(codec, "HDMI: ELD data byte %d: 0x%x\n", byte_index, val);
+#endif
+ return val;
+}
+
+int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
+ AC_DIPSIZE_ELD_BUF);
+}
+
+int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char *buf, int *eld_size)
+{
+ int i;
+ int ret = 0;
+ int size;
+
+ /*
+ * ELD size is initialized to zero in caller function. If no errors and
+ * ELD is valid, actual eld_size is assigned.
+ */
+
+ size = snd_hdmi_get_eld_size(codec, nid);
+ if (size == 0) {
+ /* wfg: workaround for ASUS P5E-VM HDMI board */
+ codec_info(codec, "HDMI: ELD buf size is 0, force 128\n");
+ size = 128;
+ }
+ if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) {
+ codec_info(codec, "HDMI: invalid ELD buf size %d\n", size);
+ return -ERANGE;
+ }
+
+ /* set ELD buffer */
+ for (i = 0; i < size; i++) {
+ unsigned int val = hdmi_get_eld_data(codec, nid, i);
+ /*
+ * Graphics driver might be writing to ELD buffer right now.
+ * Just abort. The caller will repoll after a while.
+ */
+ if (!(val & AC_ELDD_ELD_VALID)) {
+ codec_info(codec, "HDMI: invalid ELD data byte %d\n", i);
+ ret = -EINVAL;
+ goto error;
+ }
+ val &= AC_ELDD_ELD_DATA;
+ /*
+ * The first byte cannot be zero. This can happen on some DVI
+ * connections. Some Intel chips may also need some 250ms delay
+ * to return non-zero ELD data, even when the graphics driver
+ * correctly writes ELD content before setting ELD_valid bit.
+ */
+ if (!val && !i) {
+ codec_dbg(codec, "HDMI: 0 ELD data\n");
+ ret = -EINVAL;
+ goto error;
+ }
+ buf[i] = val;
+ }
+
+ *eld_size = size;
+error:
+ return ret;
+}
+
+#ifdef CONFIG_SND_PROC_FS
+void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
+ struct snd_info_buffer *buffer,
+ hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid)
+{
+ snd_iprintf(buffer, "monitor_present\t\t%d\n", eld->monitor_present);
+ snd_iprintf(buffer, "eld_valid\t\t%d\n", eld->eld_valid);
+ snd_iprintf(buffer, "codec_pin_nid\t\t0x%x\n", pin_nid);
+ snd_iprintf(buffer, "codec_dev_id\t\t0x%x\n", dev_id);
+ snd_iprintf(buffer, "codec_cvt_nid\t\t0x%x\n", cvt_nid);
+
+ if (!eld->eld_valid)
+ return;
+
+ snd_print_eld_info(&eld->info, buffer);
+}
+
+void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_parsed_hdmi_eld *e = &eld->info;
+ char line[64];
+ char name[64];
+ char *sname;
+ long long val;
+ unsigned int n;
+
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if (sscanf(line, "%s %llx", name, &val) != 2)
+ continue;
+ /*
+ * We don't allow modification to these fields:
+ * monitor_name manufacture_id product_id
+ * eld_version edid_version
+ */
+ if (!strcmp(name, "monitor_present"))
+ eld->monitor_present = val;
+ else if (!strcmp(name, "eld_valid"))
+ eld->eld_valid = val;
+ else if (!strcmp(name, "connection_type"))
+ e->conn_type = val;
+ else if (!strcmp(name, "port_id"))
+ e->port_id = val;
+ else if (!strcmp(name, "support_hdcp"))
+ e->support_hdcp = val;
+ else if (!strcmp(name, "support_ai"))
+ e->support_ai = val;
+ else if (!strcmp(name, "audio_sync_delay"))
+ e->aud_synch_delay = val;
+ else if (!strcmp(name, "speakers"))
+ e->spk_alloc = val;
+ else if (!strcmp(name, "sad_count"))
+ e->sad_count = val;
+ else if (!strncmp(name, "sad", 3)) {
+ sname = name + 4;
+ n = name[3] - '0';
+ if (name[4] >= '0' && name[4] <= '9') {
+ sname++;
+ n = 10 * n + name[4] - '0';
+ }
+ if (n >= ELD_MAX_SAD)
+ continue;
+ if (!strcmp(sname, "_coding_type"))
+ e->sad[n].format = val;
+ else if (!strcmp(sname, "_channels"))
+ e->sad[n].channels = val;
+ else if (!strcmp(sname, "_rates"))
+ e->sad[n].rates = val;
+ else if (!strcmp(sname, "_bits"))
+ e->sad[n].sample_bits = val;
+ else if (!strcmp(sname, "_max_bitrate"))
+ e->sad[n].max_bitrate = val;
+ else if (!strcmp(sname, "_profile"))
+ e->sad[n].profile = val;
+ if (n >= e->sad_count)
+ e->sad_count = n + 1;
+ }
+ }
+}
+#endif /* CONFIG_SND_PROC_FS */
+
+/* update PCM info based on ELD */
+void snd_hdmi_eld_update_pcm_info(struct snd_parsed_hdmi_eld *e,
+ struct hda_pcm_stream *hinfo)
+{
+ u32 rates;
+ u64 formats;
+ unsigned int maxbps;
+ unsigned int channels_max;
+ int i;
+
+ /* assume basic audio support (the basic audio flag is not in ELD;
+ * however, all audio capable sinks are required to support basic
+ * audio) */
+ rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000;
+ formats = SNDRV_PCM_FMTBIT_S16_LE;
+ maxbps = 16;
+ channels_max = 2;
+ for (i = 0; i < e->sad_count; i++) {
+ struct snd_cea_sad *a = &e->sad[i];
+ rates |= a->rates;
+ if (a->channels > channels_max)
+ channels_max = a->channels;
+ if (a->format == AUDIO_CODING_TYPE_LPCM) {
+ if (a->sample_bits & ELD_PCM_BITS_20) {
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ if (maxbps < 20)
+ maxbps = 20;
+ }
+ if (a->sample_bits & ELD_PCM_BITS_24) {
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ if (maxbps < 24)
+ maxbps = 24;
+ }
+ }
+ }
+
+ /* restrict the parameters by the values the codec provides */
+ hinfo->rates &= rates;
+ hinfo->formats &= formats;
+ hinfo->maxbps = min(hinfo->maxbps, maxbps);
+ hinfo->channels_max = min(hinfo->channels_max, channels_max);
+}
diff --git a/sound/hda/codecs/hdmi/hdmi.c b/sound/hda/codecs/hdmi/hdmi.c
new file mode 100644
index 000000000000..111c9b5335af
--- /dev/null
+++ b/sound/hda/codecs/hdmi/hdmi.c
@@ -0,0 +1,2363 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * hdmi.c - routines for HDMI/DisplayPort codecs
+ *
+ * Copyright(c) 2008-2010 Intel Corporation
+ * Copyright (c) 2006 ATI Technologies Inc.
+ * Copyright (c) 2008 NVIDIA Corp. All rights reserved.
+ * Copyright (c) 2008 Wei Ni <wni@nvidia.com>
+ * Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi>
+ *
+ * Authors:
+ * Wu Fengguang <wfg@linux.intel.com>
+ *
+ * Maintained by:
+ * Wu Fengguang <wfg@linux.intel.com>
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/asoundef.h>
+#include <sound/tlv.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_chmap.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_jack.h"
+#include "hda_controller.h"
+#include "hdmi_local.h"
+
+static bool static_hdmi_pcm;
+module_param(static_hdmi_pcm, bool, 0644);
+MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
+
+static bool enable_acomp = true;
+module_param(enable_acomp, bool, 0444);
+MODULE_PARM_DESC(enable_acomp, "Enable audio component binding (default=yes)");
+
+static bool enable_all_pins;
+module_param(enable_all_pins, bool, 0444);
+MODULE_PARM_DESC(enable_all_pins, "Forcibly enable all pins");
+
+int snd_hda_hdmi_pin_id_to_pin_index(struct hda_codec *codec,
+ hda_nid_t pin_nid, int dev_id)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+ struct hdmi_spec_per_pin *per_pin;
+
+ /*
+ * (dev_id == -1) means it is NON-MST pin
+ * return the first virtual pin on this port
+ */
+ if (dev_id == -1)
+ dev_id = 0;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ per_pin = get_pin(spec, pin_idx);
+ if ((per_pin->pin_nid == pin_nid) &&
+ (per_pin->dev_id == dev_id))
+ return pin_idx;
+ }
+
+ codec_warn(codec, "HDMI: pin NID 0x%x not registered\n", pin_nid);
+ return -EINVAL;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_pin_id_to_pin_index, "SND_HDA_CODEC_HDMI");
+
+static int hinfo_to_pcm_index(struct hda_codec *codec,
+ struct hda_pcm_stream *hinfo)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pcm_idx;
+
+ for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++)
+ if (get_pcm_rec(spec, pcm_idx)->stream == hinfo)
+ return pcm_idx;
+
+ codec_warn(codec, "HDMI: hinfo %p not tied to a PCM\n", hinfo);
+ return -EINVAL;
+}
+
+static int hinfo_to_pin_index(struct hda_codec *codec,
+ struct hda_pcm_stream *hinfo)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin;
+ int pin_idx;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ per_pin = get_pin(spec, pin_idx);
+ if (per_pin->pcm &&
+ per_pin->pcm->pcm->stream == hinfo)
+ return pin_idx;
+ }
+
+ codec_dbg(codec, "HDMI: hinfo %p (pcm %d) not registered\n", hinfo,
+ hinfo_to_pcm_index(codec, hinfo));
+ return -EINVAL;
+}
+
+static struct hdmi_spec_per_pin *pcm_idx_to_pin(struct hdmi_spec *spec,
+ int pcm_idx)
+{
+ int i;
+ struct hdmi_spec_per_pin *per_pin;
+
+ for (i = 0; i < spec->num_pins; i++) {
+ per_pin = get_pin(spec, i);
+ if (per_pin->pcm_idx == pcm_idx)
+ return per_pin;
+ }
+ return NULL;
+}
+
+static int cvt_nid_to_cvt_index(struct hda_codec *codec, hda_nid_t cvt_nid)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int cvt_idx;
+
+ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++)
+ if (get_cvt(spec, cvt_idx)->cvt_nid == cvt_nid)
+ return cvt_idx;
+
+ codec_warn(codec, "HDMI: cvt NID 0x%x not registered\n", cvt_nid);
+ return -EINVAL;
+}
+
+static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin;
+ struct hdmi_eld *eld;
+ int pcm_idx;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+
+ pcm_idx = kcontrol->private_value;
+ guard(mutex)(&spec->pcm_lock);
+ per_pin = pcm_idx_to_pin(spec, pcm_idx);
+ if (!per_pin) {
+ /* no pin is bound to the pcm */
+ uinfo->count = 0;
+ return 0;
+ }
+ eld = &per_pin->sink_eld;
+ uinfo->count = eld->eld_valid ? eld->eld_size : 0;
+ return 0;
+}
+
+static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin;
+ struct hdmi_eld *eld;
+ int pcm_idx;
+
+ pcm_idx = kcontrol->private_value;
+ guard(mutex)(&spec->pcm_lock);
+ per_pin = pcm_idx_to_pin(spec, pcm_idx);
+ if (!per_pin) {
+ /* no pin is bound to the pcm */
+ memset(ucontrol->value.bytes.data, 0,
+ ARRAY_SIZE(ucontrol->value.bytes.data));
+ return 0;
+ }
+
+ eld = &per_pin->sink_eld;
+ if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) ||
+ eld->eld_size > ELD_MAX_SIZE) {
+ snd_BUG();
+ return -EINVAL;
+ }
+
+ memset(ucontrol->value.bytes.data, 0,
+ ARRAY_SIZE(ucontrol->value.bytes.data));
+ if (eld->eld_valid)
+ memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
+ eld->eld_size);
+ return 0;
+}
+
+static const struct snd_kcontrol_new eld_bytes_ctl = {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE |
+ SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK,
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "ELD",
+ .info = hdmi_eld_ctl_info,
+ .get = hdmi_eld_ctl_get,
+};
+
+static int hdmi_create_eld_ctl(struct hda_codec *codec, int pcm_idx,
+ int device)
+{
+ struct snd_kcontrol *kctl;
+ struct hdmi_spec *spec = codec->spec;
+ int err;
+
+ kctl = snd_ctl_new1(&eld_bytes_ctl, codec);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->private_value = pcm_idx;
+ kctl->id.device = device;
+
+ /* no pin nid is associated with the kctl now
+ * tbd: associate pin nid to eld ctl later
+ */
+ err = snd_hda_ctl_add(codec, 0, kctl);
+ if (err < 0)
+ return err;
+
+ get_hdmi_pcm(spec, pcm_idx)->eld_ctl = kctl;
+ return 0;
+}
+
+#ifdef BE_PARANOID
+static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
+ int *packet_index, int *byte_index)
+{
+ int val;
+
+ val = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_HDMI_DIP_INDEX, 0);
+
+ *packet_index = val >> 5;
+ *byte_index = val & 0x1f;
+}
+#endif
+
+static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
+ int packet_index, int byte_index)
+{
+ int val;
+
+ val = (packet_index << 5) | (byte_index & 0x1f);
+
+ snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
+}
+
+static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
+ unsigned char val)
+{
+ snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
+}
+
+static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_out;
+
+ /* Unmute */
+ if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, pin_nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+
+ if (spec->dyn_pin_out)
+ /* Disable pin out until stream is active */
+ pin_out = 0;
+ else
+ /* Enable pin out: some machines with GM965 gets broken output
+ * when the pin is disabled or changed while using with HDMI
+ */
+ pin_out = PIN_OUT;
+
+ snd_hda_codec_write(codec, pin_nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out);
+}
+
+/*
+ * ELD proc files
+ */
+
+#ifdef CONFIG_SND_PROC_FS
+static void print_eld_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdmi_spec_per_pin *per_pin = entry->private_data;
+
+ guard(mutex)(&per_pin->lock);
+ snd_hdmi_print_eld_info(&per_pin->sink_eld, buffer, per_pin->pin_nid,
+ per_pin->dev_id, per_pin->cvt_nid);
+}
+
+static void write_eld_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdmi_spec_per_pin *per_pin = entry->private_data;
+
+ guard(mutex)(&per_pin->lock);
+ snd_hdmi_write_eld_info(&per_pin->sink_eld, buffer);
+}
+
+static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index)
+{
+ char name[32];
+ struct hda_codec *codec = per_pin->codec;
+ struct snd_info_entry *entry;
+ int err;
+
+ snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
+ err = snd_card_proc_new(codec->card, name, &entry);
+ if (err < 0)
+ return err;
+
+ snd_info_set_text_ops(entry, per_pin, print_eld_info);
+ entry->c.text.write = write_eld_info;
+ entry->mode |= 0200;
+ per_pin->proc_entry = entry;
+
+ return 0;
+}
+
+static void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
+{
+ if (!per_pin->codec->bus->shutdown) {
+ snd_info_free_entry(per_pin->proc_entry);
+ per_pin->proc_entry = NULL;
+ }
+}
+#else
+static inline int eld_proc_new(struct hdmi_spec_per_pin *per_pin,
+ int index)
+{
+ return 0;
+}
+static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
+{
+}
+#endif
+
+/*
+ * Audio InfoFrame routines
+ */
+
+/*
+ * Enable Audio InfoFrame Transmission
+ */
+static void hdmi_start_infoframe_trans(struct hda_codec *codec,
+ hda_nid_t pin_nid)
+{
+ hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+ snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+ AC_DIPXMIT_BEST);
+}
+
+/*
+ * Disable Audio InfoFrame Transmission
+ */
+static void hdmi_stop_infoframe_trans(struct hda_codec *codec,
+ hda_nid_t pin_nid)
+{
+ hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+ snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+ AC_DIPXMIT_DISABLE);
+}
+
+static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ int i;
+ int size;
+
+ size = snd_hdmi_get_eld_size(codec, pin_nid);
+ codec_dbg(codec, "HDMI: ELD buf size is %d\n", size);
+
+ for (i = 0; i < 8; i++) {
+ size = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_HDMI_DIP_SIZE, i);
+ codec_dbg(codec, "HDMI: DIP GP[%d] buf size is %d\n", i, size);
+ }
+#endif
+}
+
+static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+#ifdef BE_PARANOID
+ int i, j;
+ int size;
+ int pi, bi;
+ for (i = 0; i < 8; i++) {
+ size = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_HDMI_DIP_SIZE, i);
+ if (size == 0)
+ continue;
+
+ hdmi_set_dip_index(codec, pin_nid, i, 0x0);
+ for (j = 1; j < 1000; j++) {
+ hdmi_write_dip_byte(codec, pin_nid, 0x0);
+ hdmi_get_dip_index(codec, pin_nid, &pi, &bi);
+ if (pi != i)
+ codec_dbg(codec, "dip index %d: %d != %d\n",
+ bi, pi, i);
+ if (bi == 0) /* byte index wrapped around */
+ break;
+ }
+ codec_dbg(codec,
+ "HDMI: DIP GP[%d] buf reported size=%d, written=%d\n",
+ i, size, j);
+ }
+#endif
+}
+
+static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *hdmi_ai)
+{
+ u8 *bytes = (u8 *)hdmi_ai;
+ u8 sum = 0;
+ int i;
+
+ hdmi_ai->checksum = 0;
+
+ for (i = 0; i < sizeof(*hdmi_ai); i++)
+ sum += bytes[i];
+
+ hdmi_ai->checksum = -sum;
+}
+
+static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
+ hda_nid_t pin_nid,
+ u8 *dip, int size)
+{
+ int i;
+
+ hdmi_debug_dip_size(codec, pin_nid);
+ hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
+
+ hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+ for (i = 0; i < size; i++)
+ hdmi_write_dip_byte(codec, pin_nid, dip[i]);
+}
+
+static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
+ u8 *dip, int size)
+{
+ u8 val;
+ int i;
+
+ hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+ if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0)
+ != AC_DIPXMIT_BEST)
+ return false;
+
+ for (i = 0; i < size; i++) {
+ val = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_HDMI_DIP_DATA, 0);
+ if (val != dip[i])
+ return false;
+ }
+
+ return true;
+}
+
+static int hdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, unsigned char *buf, int *eld_size)
+{
+ snd_hda_set_dev_select(codec, nid, dev_id);
+
+ return snd_hdmi_get_eld(codec, nid, buf, eld_size);
+}
+
+static void hdmi_pin_setup_infoframe(struct hda_codec *codec,
+ hda_nid_t pin_nid, int dev_id,
+ int ca, int active_channels,
+ int conn_type)
+{
+ struct hdmi_spec *spec = codec->spec;
+ union audio_infoframe ai;
+
+ memset(&ai, 0, sizeof(ai));
+ if ((conn_type == 0) || /* HDMI */
+ /* Nvidia DisplayPort: Nvidia HW expects same layout as HDMI */
+ (conn_type == 1 && spec->nv_dp_workaround)) {
+ struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi;
+
+ if (conn_type == 0) { /* HDMI */
+ hdmi_ai->type = 0x84;
+ hdmi_ai->ver = 0x01;
+ hdmi_ai->len = 0x0a;
+ } else {/* Nvidia DP */
+ hdmi_ai->type = 0x84;
+ hdmi_ai->ver = 0x1b;
+ hdmi_ai->len = 0x11 << 2;
+ }
+ hdmi_ai->CC02_CT47 = active_channels - 1;
+ hdmi_ai->CA = ca;
+ hdmi_checksum_audio_infoframe(hdmi_ai);
+ } else if (conn_type == 1) { /* DisplayPort */
+ struct dp_audio_infoframe *dp_ai = &ai.dp;
+
+ dp_ai->type = 0x84;
+ dp_ai->len = 0x1b;
+ dp_ai->ver = 0x11 << 2;
+ dp_ai->CC02_CT47 = active_channels - 1;
+ dp_ai->CA = ca;
+ } else {
+ codec_dbg(codec, "HDMI: unknown connection type at pin NID 0x%x\n", pin_nid);
+ return;
+ }
+
+ snd_hda_set_dev_select(codec, pin_nid, dev_id);
+
+ /*
+ * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
+ * sizeof(*dp_ai) to avoid partial match/update problems when
+ * the user switches between HDMI/DP monitors.
+ */
+ if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes,
+ sizeof(ai))) {
+ codec_dbg(codec, "%s: pin NID=0x%x channels=%d ca=0x%02x\n",
+ __func__, pin_nid, active_channels, ca);
+ hdmi_stop_infoframe_trans(codec, pin_nid);
+ hdmi_fill_audio_infoframe(codec, pin_nid,
+ ai.bytes, sizeof(ai));
+ hdmi_start_infoframe_trans(codec, pin_nid);
+ }
+}
+
+void snd_hda_hdmi_setup_audio_infoframe(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ bool non_pcm)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdac_chmap *chmap = &spec->chmap;
+ hda_nid_t pin_nid = per_pin->pin_nid;
+ int dev_id = per_pin->dev_id;
+ int channels = per_pin->channels;
+ int active_channels;
+ struct hdmi_eld *eld;
+ int ca;
+
+ if (!channels)
+ return;
+
+ snd_hda_set_dev_select(codec, pin_nid, dev_id);
+
+ /* some HW (e.g. HSW+) needs reprogramming the amp at each time */
+ if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, pin_nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+
+ eld = &per_pin->sink_eld;
+
+ ca = snd_hdac_channel_allocation(&codec->core,
+ eld->info.spk_alloc, channels,
+ per_pin->chmap_set, non_pcm, per_pin->chmap);
+
+ active_channels = snd_hdac_get_active_channels(ca);
+
+ chmap->ops.set_channel_count(&codec->core, per_pin->cvt_nid,
+ active_channels);
+
+ /*
+ * always configure channel mapping, it may have been changed by the
+ * user in the meantime
+ */
+ snd_hdac_setup_channel_mapping(&spec->chmap,
+ pin_nid, non_pcm, ca, channels,
+ per_pin->chmap, per_pin->chmap_set);
+
+ spec->ops.pin_setup_infoframe(codec, pin_nid, dev_id,
+ ca, active_channels, eld->info.conn_type);
+
+ per_pin->non_pcm = non_pcm;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_setup_audio_infoframe, "SND_HDA_CODEC_HDMI");
+
+/*
+ * Unsolicited events
+ */
+
+static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll);
+
+void snd_hda_hdmi_check_presence_and_report(struct hda_codec *codec,
+ hda_nid_t nid, int dev_id)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx = pin_id_to_pin_index(codec, nid, dev_id);
+
+ if (pin_idx < 0)
+ return;
+ guard(mutex)(&spec->pcm_lock);
+ hdmi_present_sense(get_pin(spec, pin_idx), 1);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_check_presence_and_report,
+ "SND_HDA_CODEC_HDMI");
+
+static void jack_callback(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ /* stop polling when notification is enabled */
+ if (codec_has_acomp(codec))
+ return;
+
+ snd_hda_hdmi_check_presence_and_report(codec, jack->nid, jack->dev_id);
+}
+
+static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res,
+ struct hda_jack_tbl *jack)
+{
+ jack->jack_dirty = 1;
+
+ codec_dbg(codec,
+ "HDMI hot plug event: Codec=%d NID=0x%x Device=%d Inactive=%d Presence_Detect=%d ELD_Valid=%d\n",
+ codec->addr, jack->nid, jack->dev_id, !!(res & AC_UNSOL_RES_IA),
+ !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV));
+
+ snd_hda_hdmi_check_presence_and_report(codec, jack->nid, jack->dev_id);
+}
+
+static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
+{
+ int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
+ int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
+ int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
+ int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
+
+ codec_info(codec,
+ "HDMI CP event: CODEC=%d TAG=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+ codec->addr,
+ tag,
+ subtag,
+ cp_state,
+ cp_ready);
+
+ /* TODO */
+ if (cp_state) {
+ ;
+ }
+ if (cp_ready) {
+ ;
+ }
+}
+
+void snd_hda_hdmi_generic_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
+ int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
+ struct hda_jack_tbl *jack;
+
+ if (codec_has_acomp(codec))
+ return;
+
+ if (codec->dp_mst) {
+ int dev_entry =
+ (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
+
+ jack = snd_hda_jack_tbl_get_from_tag(codec, tag, dev_entry);
+ } else {
+ jack = snd_hda_jack_tbl_get_from_tag(codec, tag, 0);
+ }
+
+ if (!jack) {
+ codec_dbg(codec, "Unexpected HDMI event tag 0x%x\n", tag);
+ return;
+ }
+
+ if (subtag == 0)
+ hdmi_intrinsic_event(codec, res, jack);
+ else
+ hdmi_non_intrinsic_event(codec, res);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_unsol_event, "SND_HDA_CODEC_HDMI");
+
+/*
+ * Callbacks
+ */
+
+/* HBR should be Non-PCM, 8 channels */
+#define is_hbr_format(format) \
+ ((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7)
+
+static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid,
+ int dev_id, bool hbr)
+{
+ int pinctl, new_pinctl;
+
+ if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) {
+ snd_hda_set_dev_select(codec, pin_nid, dev_id);
+ pinctl = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+
+ if (pinctl < 0)
+ return hbr ? -EINVAL : 0;
+
+ new_pinctl = pinctl & ~AC_PINCTL_EPT;
+ if (hbr)
+ new_pinctl |= AC_PINCTL_EPT_HBR;
+ else
+ new_pinctl |= AC_PINCTL_EPT_NATIVE;
+
+ codec_dbg(codec,
+ "hdmi_pin_hbr_setup: NID=0x%x, %spinctl=0x%x\n",
+ pin_nid,
+ pinctl == new_pinctl ? "" : "new-",
+ new_pinctl);
+
+ if (pinctl != new_pinctl)
+ snd_hda_codec_write(codec, pin_nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ new_pinctl);
+ } else if (hbr)
+ return -EINVAL;
+
+ return 0;
+}
+
+int snd_hda_hdmi_setup_stream(struct hda_codec *codec,
+ hda_nid_t cvt_nid,
+ hda_nid_t pin_nid, int dev_id,
+ u32 stream_tag, int format)
+{
+ struct hdmi_spec *spec = codec->spec;
+ unsigned int param;
+ int err;
+
+ err = spec->ops.pin_hbr_setup(codec, pin_nid, dev_id,
+ is_hbr_format(format));
+
+ if (err) {
+ codec_dbg(codec, "hdmi_setup_stream: HBR is not supported\n");
+ return err;
+ }
+
+ if (spec->intel_hsw_fixup) {
+
+ /*
+ * on recent platforms IEC Coding Type is required for HBR
+ * support, read current Digital Converter settings and set
+ * ICT bitfield if needed.
+ */
+ param = snd_hda_codec_read(codec, cvt_nid, 0,
+ AC_VERB_GET_DIGI_CONVERT_1, 0);
+
+ param = (param >> 16) & ~(AC_DIG3_ICT);
+
+ /* on recent platforms ICT mode is required for HBR support */
+ if (is_hbr_format(format))
+ param |= 0x1;
+
+ snd_hda_codec_write(codec, cvt_nid, 0,
+ AC_VERB_SET_DIGI_CONVERT_3, param);
+ }
+
+ snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_setup_stream, "SND_HDA_CODEC_HDMI");
+
+/* Try to find an available converter
+ * If pin_idx is less then zero, just try to find an available converter.
+ * Otherwise, try to find an available converter and get the cvt mux index
+ * of the pin.
+ */
+static int hdmi_choose_cvt(struct hda_codec *codec,
+ int pin_idx, int *cvt_id,
+ bool silent)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin;
+ struct hdmi_spec_per_cvt *per_cvt = NULL;
+ int cvt_idx, mux_idx = 0;
+
+ /* pin_idx < 0 means no pin will be bound to the converter */
+ if (pin_idx < 0)
+ per_pin = NULL;
+ else
+ per_pin = get_pin(spec, pin_idx);
+
+ if (per_pin && per_pin->silent_stream) {
+ cvt_idx = cvt_nid_to_cvt_index(codec, per_pin->cvt_nid);
+ per_cvt = get_cvt(spec, cvt_idx);
+ if (per_cvt->assigned && !silent)
+ return -EBUSY;
+ if (cvt_id)
+ *cvt_id = cvt_idx;
+ return 0;
+ }
+
+ /* Dynamically assign converter to stream */
+ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
+ per_cvt = get_cvt(spec, cvt_idx);
+
+ /* Must not already be assigned */
+ if (per_cvt->assigned || per_cvt->silent_stream)
+ continue;
+ if (per_pin == NULL)
+ break;
+ /* Must be in pin's mux's list of converters */
+ for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++)
+ if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid)
+ break;
+ /* Not in mux list */
+ if (mux_idx == per_pin->num_mux_nids)
+ continue;
+ break;
+ }
+
+ /* No free converters */
+ if (cvt_idx == spec->num_cvts)
+ return -EBUSY;
+
+ if (per_pin != NULL)
+ per_pin->mux_idx = mux_idx;
+
+ if (cvt_id)
+ *cvt_id = cvt_idx;
+
+ return 0;
+}
+
+/* skeleton caller of pin_cvt_fixup ops */
+static void pin_cvt_fixup(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ hda_nid_t cvt_nid)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (spec->ops.pin_cvt_fixup)
+ spec->ops.pin_cvt_fixup(codec, per_pin, cvt_nid);
+}
+
+/* called in hdmi_pcm_open when no pin is assigned to the PCM */
+static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int cvt_idx, pcm_idx;
+ struct hdmi_spec_per_cvt *per_cvt = NULL;
+ int err;
+
+ pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+ if (pcm_idx < 0)
+ return -EINVAL;
+
+ err = hdmi_choose_cvt(codec, -1, &cvt_idx, false);
+ if (err)
+ return err;
+
+ per_cvt = get_cvt(spec, cvt_idx);
+ per_cvt->assigned = true;
+ hinfo->nid = per_cvt->cvt_nid;
+
+ pin_cvt_fixup(codec, NULL, per_cvt->cvt_nid);
+
+ set_bit(pcm_idx, &spec->pcm_in_use);
+ /* todo: setup spdif ctls assign */
+
+ /* Initially set the converter's capabilities */
+ hinfo->channels_min = per_cvt->channels_min;
+ hinfo->channels_max = per_cvt->channels_max;
+ hinfo->rates = per_cvt->rates;
+ hinfo->formats = per_cvt->formats;
+ hinfo->maxbps = per_cvt->maxbps;
+
+ /* Store the updated parameters */
+ runtime->hw.channels_min = hinfo->channels_min;
+ runtime->hw.channels_max = hinfo->channels_max;
+ runtime->hw.formats = hinfo->formats;
+ runtime->hw.rates = hinfo->rates;
+
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+ return 0;
+}
+
+/*
+ * HDA PCM callbacks
+ */
+static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int pin_idx, cvt_idx, pcm_idx;
+ struct hdmi_spec_per_pin *per_pin;
+ struct hdmi_eld *eld;
+ struct hdmi_spec_per_cvt *per_cvt = NULL;
+ int err;
+
+ /* Validate hinfo */
+ pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+ if (pcm_idx < 0)
+ return -EINVAL;
+
+ guard(mutex)(&spec->pcm_lock);
+ pin_idx = hinfo_to_pin_index(codec, hinfo);
+ /* no pin is assigned to the PCM
+ * PA need pcm open successfully when probe
+ */
+ if (pin_idx < 0)
+ return hdmi_pcm_open_no_pin(hinfo, codec, substream);
+
+ err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, false);
+ if (err < 0)
+ return err;
+
+ per_cvt = get_cvt(spec, cvt_idx);
+ /* Claim converter */
+ per_cvt->assigned = true;
+
+ set_bit(pcm_idx, &spec->pcm_in_use);
+ per_pin = get_pin(spec, pin_idx);
+ per_pin->cvt_nid = per_cvt->cvt_nid;
+ hinfo->nid = per_cvt->cvt_nid;
+
+ /* flip stripe flag for the assigned stream if supported */
+ if (get_wcaps(codec, per_cvt->cvt_nid) & AC_WCAP_STRIPE)
+ azx_stream(get_azx_dev(substream))->stripe = 1;
+
+ snd_hda_set_dev_select(codec, per_pin->pin_nid, per_pin->dev_id);
+ snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ per_pin->mux_idx);
+
+ /* configure unused pins to choose other converters */
+ pin_cvt_fixup(codec, per_pin, 0);
+
+ snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid);
+
+ /* Initially set the converter's capabilities */
+ hinfo->channels_min = per_cvt->channels_min;
+ hinfo->channels_max = per_cvt->channels_max;
+ hinfo->rates = per_cvt->rates;
+ hinfo->formats = per_cvt->formats;
+ hinfo->maxbps = per_cvt->maxbps;
+
+ eld = &per_pin->sink_eld;
+ /* Restrict capabilities by ELD if this isn't disabled */
+ if (!static_hdmi_pcm && eld->eld_valid) {
+ snd_hdmi_eld_update_pcm_info(&eld->info, hinfo);
+ if (hinfo->channels_min > hinfo->channels_max ||
+ !hinfo->rates || !hinfo->formats) {
+ per_cvt->assigned = false;
+ hinfo->nid = 0;
+ snd_hda_spdif_ctls_unassign(codec, pcm_idx);
+ return -ENODEV;
+ }
+ }
+
+ /* Store the updated parameters */
+ runtime->hw.channels_min = hinfo->channels_min;
+ runtime->hw.channels_max = hinfo->channels_max;
+ runtime->hw.formats = hinfo->formats;
+ runtime->hw.rates = hinfo->rates;
+
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+ return 0;
+}
+
+/*
+ * HDA/HDMI auto parsing
+ */
+static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ hda_nid_t pin_nid = per_pin->pin_nid;
+ int dev_id = per_pin->dev_id;
+ int conns;
+
+ if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
+ codec_warn(codec,
+ "HDMI: pin NID 0x%x wcaps %#x does not support connection list\n",
+ pin_nid, get_wcaps(codec, pin_nid));
+ return -EINVAL;
+ }
+
+ snd_hda_set_dev_select(codec, pin_nid, dev_id);
+
+ if (spec->intel_hsw_fixup) {
+ conns = spec->num_cvts;
+ memcpy(per_pin->mux_nids, spec->cvt_nids,
+ sizeof(hda_nid_t) * conns);
+ } else {
+ conns = snd_hda_get_raw_connections(codec, pin_nid,
+ per_pin->mux_nids,
+ HDA_MAX_CONNECTIONS);
+ }
+
+ /* all the device entries on the same pin have the same conn list */
+ per_pin->num_mux_nids = conns;
+
+ return 0;
+}
+
+static int hdmi_find_pcm_slot(struct hdmi_spec *spec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ int i;
+
+ for (i = 0; i < spec->pcm_used; i++) {
+ if (!test_bit(i, &spec->pcm_bitmap))
+ return i;
+ }
+ return -EBUSY;
+}
+
+static void hdmi_attach_hda_pcm(struct hdmi_spec *spec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ int idx;
+
+ /* pcm already be attached to the pin */
+ if (per_pin->pcm)
+ return;
+ /* try the previously used slot at first */
+ idx = per_pin->prev_pcm_idx;
+ if (idx >= 0) {
+ if (!test_bit(idx, &spec->pcm_bitmap))
+ goto found;
+ per_pin->prev_pcm_idx = -1; /* no longer valid, clear it */
+ }
+ idx = hdmi_find_pcm_slot(spec, per_pin);
+ if (idx == -EBUSY)
+ return;
+ found:
+ per_pin->pcm_idx = idx;
+ per_pin->pcm = get_hdmi_pcm(spec, idx);
+ set_bit(idx, &spec->pcm_bitmap);
+}
+
+static void hdmi_detach_hda_pcm(struct hdmi_spec *spec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ int idx;
+
+ /* pcm already be detached from the pin */
+ if (!per_pin->pcm)
+ return;
+ idx = per_pin->pcm_idx;
+ per_pin->pcm_idx = -1;
+ per_pin->prev_pcm_idx = idx; /* remember the previous index */
+ per_pin->pcm = NULL;
+ if (idx >= 0 && idx < spec->pcm_used)
+ clear_bit(idx, &spec->pcm_bitmap);
+}
+
+static int hdmi_get_pin_cvt_mux(struct hdmi_spec *spec,
+ struct hdmi_spec_per_pin *per_pin, hda_nid_t cvt_nid)
+{
+ int mux_idx;
+
+ for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++)
+ if (per_pin->mux_nids[mux_idx] == cvt_nid)
+ break;
+ return mux_idx;
+}
+
+static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid);
+
+static void hdmi_pcm_setup_pin(struct hdmi_spec *spec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ struct hda_codec *codec = per_pin->codec;
+ struct hda_pcm *pcm;
+ struct hda_pcm_stream *hinfo;
+ struct snd_pcm_substream *substream;
+ int mux_idx;
+ bool non_pcm;
+
+ if (per_pin->pcm_idx < 0 || per_pin->pcm_idx >= spec->pcm_used)
+ return;
+ pcm = get_pcm_rec(spec, per_pin->pcm_idx);
+ if (!pcm->pcm)
+ return;
+ if (!test_bit(per_pin->pcm_idx, &spec->pcm_in_use))
+ return;
+
+ /* hdmi audio only uses playback and one substream */
+ hinfo = pcm->stream;
+ substream = pcm->pcm->streams[0].substream;
+
+ per_pin->cvt_nid = hinfo->nid;
+
+ mux_idx = hdmi_get_pin_cvt_mux(spec, per_pin, hinfo->nid);
+ if (mux_idx < per_pin->num_mux_nids) {
+ snd_hda_set_dev_select(codec, per_pin->pin_nid,
+ per_pin->dev_id);
+ snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ mux_idx);
+ }
+ snd_hda_spdif_ctls_assign(codec, per_pin->pcm_idx, hinfo->nid);
+
+ non_pcm = check_non_pcm_per_cvt(codec, hinfo->nid);
+ if (substream->runtime)
+ per_pin->channels = substream->runtime->channels;
+ per_pin->setup = true;
+ per_pin->mux_idx = mux_idx;
+
+ snd_hda_hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
+}
+
+static void hdmi_pcm_reset_pin(struct hdmi_spec *spec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ if (per_pin->pcm_idx >= 0 && per_pin->pcm_idx < spec->pcm_used)
+ snd_hda_spdif_ctls_unassign(per_pin->codec, per_pin->pcm_idx);
+
+ per_pin->chmap_set = false;
+ memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
+
+ per_pin->setup = false;
+ per_pin->channels = 0;
+}
+
+static struct snd_jack *pin_idx_to_pcm_jack(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (per_pin->pcm_idx >= 0)
+ return spec->pcm_rec[per_pin->pcm_idx].jack;
+ else
+ return NULL;
+}
+
+/* update per_pin ELD from the given new ELD;
+ * setup info frame and notification accordingly
+ * also notify ELD kctl and report jack status changes
+ */
+static void update_eld(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ struct hdmi_eld *eld,
+ int repoll)
+{
+ struct hdmi_eld *pin_eld = &per_pin->sink_eld;
+ struct hdmi_spec *spec = codec->spec;
+ struct snd_jack *pcm_jack;
+ bool old_eld_valid = pin_eld->eld_valid;
+ bool eld_changed;
+ int pcm_idx;
+
+ if (eld->eld_valid) {
+ if (eld->eld_size <= 0 ||
+ snd_parse_eld(hda_codec_dev(codec), &eld->info,
+ eld->eld_buffer, eld->eld_size) < 0) {
+ eld->eld_valid = false;
+ if (repoll) {
+ schedule_delayed_work(&per_pin->work,
+ msecs_to_jiffies(300));
+ return;
+ }
+ }
+ }
+
+ if (!eld->eld_valid || eld->eld_size <= 0 || eld->info.sad_count <= 0) {
+ eld->eld_valid = false;
+ eld->eld_size = 0;
+ }
+
+ /* for monitor disconnection, save pcm_idx firstly */
+ pcm_idx = per_pin->pcm_idx;
+
+ /*
+ * pcm_idx >=0 before update_eld() means it is in monitor
+ * disconnected event. Jack must be fetched before update_eld().
+ */
+ pcm_jack = pin_idx_to_pcm_jack(codec, per_pin);
+
+ if (!spec->static_pcm_mapping) {
+ if (eld->eld_valid) {
+ hdmi_attach_hda_pcm(spec, per_pin);
+ hdmi_pcm_setup_pin(spec, per_pin);
+ } else {
+ hdmi_pcm_reset_pin(spec, per_pin);
+ hdmi_detach_hda_pcm(spec, per_pin);
+ }
+ }
+
+ /* if pcm_idx == -1, it means this is in monitor connection event
+ * we can get the correct pcm_idx now.
+ */
+ if (pcm_idx == -1)
+ pcm_idx = per_pin->pcm_idx;
+ if (!pcm_jack)
+ pcm_jack = pin_idx_to_pcm_jack(codec, per_pin);
+
+ if (eld->eld_valid)
+ snd_show_eld(hda_codec_dev(codec), &eld->info);
+
+ eld_changed = (pin_eld->eld_valid != eld->eld_valid);
+ eld_changed |= (pin_eld->monitor_present != eld->monitor_present);
+ if (!eld_changed && eld->eld_valid && pin_eld->eld_valid)
+ if (pin_eld->eld_size != eld->eld_size ||
+ memcmp(pin_eld->eld_buffer, eld->eld_buffer,
+ eld->eld_size) != 0)
+ eld_changed = true;
+
+ if (eld_changed) {
+ pin_eld->monitor_present = eld->monitor_present;
+ pin_eld->eld_valid = eld->eld_valid;
+ pin_eld->eld_size = eld->eld_size;
+ if (eld->eld_valid)
+ memcpy(pin_eld->eld_buffer, eld->eld_buffer,
+ eld->eld_size);
+ pin_eld->info = eld->info;
+ }
+
+ /*
+ * Re-setup pin and infoframe. This is needed e.g. when
+ * - sink is first plugged-in
+ * - transcoder can change during stream playback on Haswell
+ * and this can make HW reset converter selection on a pin.
+ */
+ if (eld->eld_valid && !old_eld_valid && per_pin->setup) {
+ pin_cvt_fixup(codec, per_pin, 0);
+ snd_hda_hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
+ }
+
+ if (eld_changed && pcm_idx >= 0)
+ snd_ctl_notify(codec->card,
+ SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO,
+ &get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id);
+
+ if (eld_changed && pcm_jack)
+ snd_jack_report(pcm_jack,
+ (eld->monitor_present && eld->eld_valid) ?
+ SND_JACK_AVOUT : 0);
+}
+
+/* update ELD and jack state via HD-audio verbs */
+static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
+ int repoll)
+{
+ struct hda_codec *codec = per_pin->codec;
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_eld *eld = &spec->temp_eld;
+ struct device *dev = hda_codec_dev(codec);
+ hda_nid_t pin_nid = per_pin->pin_nid;
+ int dev_id = per_pin->dev_id;
+ /*
+ * Always execute a GetPinSense verb here, even when called from
+ * hdmi_intrinsic_event; for some NVIDIA HW, the unsolicited
+ * response's PD bit is not the real PD value, but indicates that
+ * the real PD value changed. An older version of the HD-audio
+ * specification worked this way. Hence, we just ignore the data in
+ * the unsolicited response to avoid custom WARs.
+ */
+ int present;
+
+#ifdef CONFIG_PM
+ if (dev->power.runtime_status == RPM_SUSPENDING)
+ return;
+#endif
+
+ CLASS(snd_hda_power_pm, pm)(codec);
+ if (pm.err < 0 && pm_runtime_suspended(dev))
+ return;
+
+ present = snd_hda_jack_pin_sense(codec, pin_nid, dev_id);
+
+ guard(mutex)(&per_pin->lock);
+ eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
+ if (eld->monitor_present)
+ eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
+ else
+ eld->eld_valid = false;
+
+ codec_dbg(codec,
+ "HDMI status: Codec=%d NID=0x%x Presence_Detect=%d ELD_Valid=%d\n",
+ codec->addr, pin_nid, eld->monitor_present, eld->eld_valid);
+
+ if (eld->eld_valid) {
+ if (spec->ops.pin_get_eld(codec, pin_nid, dev_id,
+ eld->eld_buffer, &eld->eld_size) < 0)
+ eld->eld_valid = false;
+ }
+
+ update_eld(codec, per_pin, eld, repoll);
+}
+
+static void silent_stream_enable(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ int cvt_idx, pin_idx, err;
+
+ /*
+ * Power-up will call hdmi_present_sense, so the PM calls
+ * have to be done without mutex held.
+ */
+
+ CLASS(snd_hda_power_pm, pm)(codec);
+ if (pm.err < 0 && pm.err != -EACCES) {
+ codec_err(codec,
+ "Failed to power up codec for silent stream enable ret=[%d]\n", pm.err);
+ return;
+ }
+
+ guard(mutex)(&per_pin->lock);
+
+ if (per_pin->setup) {
+ codec_dbg(codec, "hdmi: PCM already open, no silent stream\n");
+ return;
+ }
+
+ pin_idx = pin_id_to_pin_index(codec, per_pin->pin_nid, per_pin->dev_id);
+ err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, true);
+ if (err) {
+ codec_err(codec, "hdmi: no free converter to enable silent mode\n");
+ return;
+ }
+
+ per_cvt = get_cvt(spec, cvt_idx);
+ per_cvt->silent_stream = true;
+ per_pin->cvt_nid = per_cvt->cvt_nid;
+ per_pin->silent_stream = true;
+
+ codec_dbg(codec, "hdmi: enabling silent stream pin-NID=0x%x cvt-NID=0x%x\n",
+ per_pin->pin_nid, per_cvt->cvt_nid);
+
+ snd_hda_set_dev_select(codec, per_pin->pin_nid, per_pin->dev_id);
+ snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ per_pin->mux_idx);
+
+ /* configure unused pins to choose other converters */
+ pin_cvt_fixup(codec, per_pin, 0);
+
+ spec->ops.silent_stream(codec, per_pin, true);
+}
+
+static void silent_stream_disable(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ int cvt_idx;
+
+ CLASS(snd_hda_power_pm, pm)(codec);
+ if (pm.err < 0 && pm.err != -EACCES) {
+ codec_err(codec,
+ "Failed to power up codec for silent stream disable ret=[%d]\n",
+ pm.err);
+ return;
+ }
+
+ guard(mutex)(&per_pin->lock);
+ if (!per_pin->silent_stream)
+ return;
+
+ codec_dbg(codec, "HDMI: disable silent stream on pin-NID=0x%x cvt-NID=0x%x\n",
+ per_pin->pin_nid, per_pin->cvt_nid);
+
+ cvt_idx = cvt_nid_to_cvt_index(codec, per_pin->cvt_nid);
+ if (cvt_idx >= 0 && cvt_idx < spec->num_cvts) {
+ per_cvt = get_cvt(spec, cvt_idx);
+ per_cvt->silent_stream = false;
+ }
+
+ spec->ops.silent_stream(codec, per_pin, false);
+
+ per_pin->cvt_nid = 0;
+ per_pin->silent_stream = false;
+}
+
+/* update ELD and jack state via audio component */
+static void sync_eld_via_acomp(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_eld *eld = &spec->temp_eld;
+ bool monitor_prev, monitor_next;
+
+ scoped_guard(mutex, &per_pin->lock) {
+ eld->monitor_present = false;
+ monitor_prev = per_pin->sink_eld.monitor_present;
+ eld->eld_size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid,
+ per_pin->dev_id, &eld->monitor_present,
+ eld->eld_buffer, ELD_MAX_SIZE);
+ eld->eld_valid = (eld->eld_size > 0);
+ update_eld(codec, per_pin, eld, 0);
+ monitor_next = per_pin->sink_eld.monitor_present;
+ }
+
+ if (spec->silent_stream_type) {
+ if (!monitor_prev && monitor_next)
+ silent_stream_enable(codec, per_pin);
+ else if (monitor_prev && !monitor_next)
+ silent_stream_disable(codec, per_pin);
+ }
+}
+
+static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
+{
+ struct hda_codec *codec = per_pin->codec;
+
+ if (!codec_has_acomp(codec))
+ hdmi_present_sense_via_verbs(per_pin, repoll);
+ else
+ sync_eld_via_acomp(codec, per_pin);
+}
+
+static void hdmi_repoll_eld(struct work_struct *work)
+{
+ struct hdmi_spec_per_pin *per_pin =
+ container_of(to_delayed_work(work), struct hdmi_spec_per_pin, work);
+ struct hda_codec *codec = per_pin->codec;
+ struct hdmi_spec *spec = codec->spec;
+ struct hda_jack_tbl *jack;
+
+ jack = snd_hda_jack_tbl_get_mst(codec, per_pin->pin_nid,
+ per_pin->dev_id);
+ if (jack)
+ jack->jack_dirty = 1;
+
+ if (per_pin->repoll_count++ > 6)
+ per_pin->repoll_count = 0;
+
+ guard(mutex)(&spec->pcm_lock);
+ hdmi_present_sense(per_pin, per_pin->repoll_count);
+}
+
+static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+ struct hdmi_spec *spec = codec->spec;
+ unsigned int caps, config;
+ int pin_idx;
+ struct hdmi_spec_per_pin *per_pin;
+ int err;
+ int dev_num, i;
+
+ caps = snd_hda_query_pin_caps(codec, pin_nid);
+ if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
+ return 0;
+
+ /*
+ * For DP MST audio, Configuration Default is the same for
+ * all device entries on the same pin
+ */
+ config = snd_hda_codec_get_pincfg(codec, pin_nid);
+ if (get_defcfg_connect(config) == AC_JACK_PORT_NONE &&
+ !spec->force_connect)
+ return 0;
+
+ /*
+ * To simplify the implementation, malloc all
+ * the virtual pins in the initialization statically
+ */
+ if (spec->intel_hsw_fixup) {
+ /*
+ * On Intel platforms, device entries count returned
+ * by AC_PAR_DEVLIST_LEN is dynamic, and depends on
+ * the type of receiver that is connected. Allocate pin
+ * structures based on worst case.
+ */
+ dev_num = spec->dev_num;
+ } else if (codec->dp_mst) {
+ dev_num = snd_hda_get_num_devices(codec, pin_nid) + 1;
+ /*
+ * spec->dev_num is the maxinum number of device entries
+ * among all the pins
+ */
+ spec->dev_num = (spec->dev_num > dev_num) ?
+ spec->dev_num : dev_num;
+ } else {
+ /*
+ * If the platform doesn't support DP MST,
+ * manually set dev_num to 1. This means
+ * the pin has only one device entry.
+ */
+ dev_num = 1;
+ spec->dev_num = 1;
+ }
+
+ for (i = 0; i < dev_num; i++) {
+ pin_idx = spec->num_pins;
+ per_pin = snd_array_new(&spec->pins);
+
+ if (!per_pin)
+ return -ENOMEM;
+
+ per_pin->pcm = NULL;
+ per_pin->pcm_idx = -1;
+ per_pin->prev_pcm_idx = -1;
+ per_pin->pin_nid = pin_nid;
+ per_pin->pin_nid_idx = spec->num_nids;
+ per_pin->dev_id = i;
+ per_pin->non_pcm = false;
+ snd_hda_set_dev_select(codec, pin_nid, i);
+ err = hdmi_read_pin_conn(codec, pin_idx);
+ if (err < 0)
+ return err;
+ if (!is_jack_detectable(codec, pin_nid))
+ codec_warn(codec, "HDMI: pin NID 0x%x - jack not detectable\n", pin_nid);
+ spec->num_pins++;
+ }
+ spec->num_nids++;
+
+ return 0;
+}
+
+static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ unsigned int chans;
+ int err;
+
+ chans = get_wcaps(codec, cvt_nid);
+ chans = get_wcaps_channels(chans);
+
+ per_cvt = snd_array_new(&spec->cvts);
+ if (!per_cvt)
+ return -ENOMEM;
+
+ per_cvt->cvt_nid = cvt_nid;
+ per_cvt->channels_min = 2;
+ if (chans <= 16) {
+ per_cvt->channels_max = chans;
+ if (chans > spec->chmap.channels_max)
+ spec->chmap.channels_max = chans;
+ }
+
+ err = snd_hda_query_supported_pcm(codec, cvt_nid,
+ &per_cvt->rates,
+ &per_cvt->formats,
+ NULL,
+ &per_cvt->maxbps);
+ if (err < 0)
+ return err;
+
+ if (spec->num_cvts < ARRAY_SIZE(spec->cvt_nids))
+ spec->cvt_nids[spec->num_cvts] = cvt_nid;
+ spec->num_cvts++;
+
+ return 0;
+}
+
+static const struct snd_pci_quirk force_connect_list[] = {
+ SND_PCI_QUIRK(0x103c, 0x83e2, "HP EliteDesk 800 G4", 1),
+ SND_PCI_QUIRK(0x103c, 0x83ef, "HP MP9 G4 Retail System AMS", 1),
+ SND_PCI_QUIRK(0x103c, 0x845a, "HP EliteDesk 800 G4 DM 65W", 1),
+ SND_PCI_QUIRK(0x103c, 0x83f3, "HP ProDesk 400", 1),
+ SND_PCI_QUIRK(0x103c, 0x870f, "HP", 1),
+ SND_PCI_QUIRK(0x103c, 0x871a, "HP", 1),
+ SND_PCI_QUIRK(0x103c, 0x8711, "HP", 1),
+ SND_PCI_QUIRK(0x103c, 0x8715, "HP", 1),
+ SND_PCI_QUIRK(0x1043, 0x86ae, "ASUS", 1), /* Z170 PRO */
+ SND_PCI_QUIRK(0x1043, 0x86c7, "ASUS", 1), /* Z170M PLUS */
+ SND_PCI_QUIRK(0x1462, 0xec94, "MS-7C94", 1),
+ SND_PCI_QUIRK(0x8086, 0x2060, "Intel NUC5CPYB", 1),
+ SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", 1),
+ {}
+};
+
+int snd_hda_hdmi_parse_codec(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ hda_nid_t start_nid;
+ unsigned int caps;
+ int i, nodes;
+ const struct snd_pci_quirk *q;
+
+ nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &start_nid);
+ if (!start_nid || nodes < 0) {
+ codec_warn(codec, "HDMI: failed to get afg sub nodes\n");
+ return -EINVAL;
+ }
+
+ if (enable_all_pins)
+ spec->force_connect = true;
+
+ q = snd_pci_quirk_lookup(codec->bus->pci, force_connect_list);
+
+ if (q && q->value)
+ spec->force_connect = true;
+
+ /*
+ * hdmi_add_pin() assumes total amount of converters to
+ * be known, so first discover all converters
+ */
+ for (i = 0; i < nodes; i++) {
+ hda_nid_t nid = start_nid + i;
+
+ caps = get_wcaps(codec, nid);
+
+ if (!(caps & AC_WCAP_DIGITAL))
+ continue;
+
+ if (get_wcaps_type(caps) == AC_WID_AUD_OUT)
+ hdmi_add_cvt(codec, nid);
+ }
+
+ /* discover audio pins */
+ for (i = 0; i < nodes; i++) {
+ hda_nid_t nid = start_nid + i;
+
+ caps = get_wcaps(codec, nid);
+
+ if (!(caps & AC_WCAP_DIGITAL))
+ continue;
+
+ if (get_wcaps_type(caps) == AC_WID_PIN)
+ hdmi_add_pin(codec, nid);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_parse_codec, "SND_HDA_CODEC_HDMI");
+
+/*
+ */
+static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
+{
+ struct hda_spdif_out *spdif;
+
+ guard(mutex)(&codec->spdif_mutex);
+ spdif = snd_hda_spdif_out_of_nid(codec, cvt_nid);
+ /* Add sanity check to pass klockwork check.
+ * This should never happen.
+ */
+ if (WARN_ON(spdif == NULL))
+ return true;
+ return !!(spdif->status & IEC958_AES0_NONAUDIO);
+}
+
+/*
+ * HDMI callbacks
+ */
+
+int snd_hda_hdmi_generic_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ hda_nid_t cvt_nid = hinfo->nid;
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+ struct hdmi_spec_per_pin *per_pin;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ bool non_pcm;
+ int pinctl, stripe;
+
+ guard(mutex)(&spec->pcm_lock);
+ pin_idx = hinfo_to_pin_index(codec, hinfo);
+ if (pin_idx < 0) {
+ /* when pcm is not bound to a pin skip pin setup and return 0
+ * to make audio playback be ongoing
+ */
+ pin_cvt_fixup(codec, NULL, cvt_nid);
+ snd_hda_codec_setup_stream(codec, cvt_nid,
+ stream_tag, 0, format);
+ return 0;
+ }
+
+ per_pin = get_pin(spec, pin_idx);
+
+ /* Verify pin:cvt selections to avoid silent audio after S3.
+ * After S3, the audio driver restores pin:cvt selections
+ * but this can happen before gfx is ready and such selection
+ * is overlooked by HW. Thus multiple pins can share a same
+ * default convertor and mute control will affect each other,
+ * which can cause a resumed audio playback become silent
+ * after S3.
+ */
+ pin_cvt_fixup(codec, per_pin, 0);
+
+ /* Call sync_audio_rate to set the N/CTS/M manually if necessary */
+ /* Todo: add DP1.2 MST audio support later */
+ if (codec_has_acomp(codec))
+ snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid,
+ per_pin->dev_id, runtime->rate);
+
+ non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
+ scoped_guard(mutex, &per_pin->lock) {
+ per_pin->channels = substream->runtime->channels;
+ per_pin->setup = true;
+
+ if (get_wcaps(codec, cvt_nid) & AC_WCAP_STRIPE) {
+ stripe = snd_hdac_get_stream_stripe_ctl(&codec->bus->core,
+ substream);
+ snd_hda_codec_write(codec, cvt_nid, 0,
+ AC_VERB_SET_STRIPE_CONTROL,
+ stripe);
+ }
+
+ snd_hda_hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
+ }
+ if (spec->dyn_pin_out) {
+ snd_hda_set_dev_select(codec, per_pin->pin_nid,
+ per_pin->dev_id);
+ pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pinctl | PIN_OUT);
+ }
+
+ /* snd_hda_set_dev_select() has been called before */
+ return spec->ops.setup_stream(codec, cvt_nid, per_pin->pin_nid,
+ per_pin->dev_id, stream_tag, format);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_pcm_prepare, "SND_HDA_CODEC_HDMI");
+
+int snd_hda_hdmi_generic_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_pcm_cleanup, "SND_HDA_CODEC_HDMI");
+
+static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int cvt_idx, pin_idx, pcm_idx;
+ struct hdmi_spec_per_cvt *per_cvt;
+ struct hdmi_spec_per_pin *per_pin;
+ int pinctl;
+
+ guard(mutex)(&spec->pcm_lock);
+ if (hinfo->nid) {
+ pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+ if (snd_BUG_ON(pcm_idx < 0))
+ return -EINVAL;
+ cvt_idx = cvt_nid_to_cvt_index(codec, hinfo->nid);
+ if (snd_BUG_ON(cvt_idx < 0))
+ return -EINVAL;
+ per_cvt = get_cvt(spec, cvt_idx);
+ per_cvt->assigned = false;
+ hinfo->nid = 0;
+
+ azx_stream(get_azx_dev(substream))->stripe = 0;
+
+ snd_hda_spdif_ctls_unassign(codec, pcm_idx);
+ clear_bit(pcm_idx, &spec->pcm_in_use);
+ pin_idx = hinfo_to_pin_index(codec, hinfo);
+ /*
+ * In such a case, return 0 to match the behavior in
+ * hdmi_pcm_open()
+ */
+ if (pin_idx < 0)
+ return 0;
+
+ per_pin = get_pin(spec, pin_idx);
+
+ if (spec->dyn_pin_out) {
+ snd_hda_set_dev_select(codec, per_pin->pin_nid,
+ per_pin->dev_id);
+ pinctl = snd_hda_codec_read(codec, per_pin->pin_nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_hda_codec_write(codec, per_pin->pin_nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pinctl & ~PIN_OUT);
+ }
+
+ guard(mutex)(&per_pin->lock);
+ per_pin->chmap_set = false;
+ memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
+
+ per_pin->setup = false;
+ per_pin->channels = 0;
+ }
+
+ return 0;
+}
+
+static const struct hda_pcm_ops generic_ops = {
+ .open = hdmi_pcm_open,
+ .close = hdmi_pcm_close,
+ .prepare = snd_hda_hdmi_generic_pcm_prepare,
+ .cleanup = snd_hda_hdmi_generic_pcm_cleanup,
+};
+
+static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx)
+{
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
+
+ if (!per_pin)
+ return 0;
+
+ return per_pin->sink_eld.info.spk_alloc;
+}
+
+static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
+ unsigned char *chmap)
+{
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
+
+ /* chmap is already set to 0 in caller */
+ if (!per_pin)
+ return;
+
+ memcpy(chmap, per_pin->chmap, ARRAY_SIZE(per_pin->chmap));
+}
+
+static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
+ unsigned char *chmap, int prepared)
+{
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
+
+ if (!per_pin)
+ return;
+ guard(mutex)(&per_pin->lock);
+ per_pin->chmap_set = true;
+ memcpy(per_pin->chmap, chmap, ARRAY_SIZE(per_pin->chmap));
+ if (prepared)
+ snd_hda_hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
+}
+
+static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)
+{
+ struct hda_codec *codec = hdac_to_hda_codec(hdac);
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
+
+ return per_pin ? true:false;
+}
+
+int snd_hda_hdmi_generic_build_pcms(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int idx, pcm_num;
+
+ /* limit the PCM devices to the codec converters or available PINs */
+ pcm_num = min(spec->num_cvts, spec->num_pins);
+ codec_dbg(codec, "hdmi: pcm_num set to %d\n", pcm_num);
+
+ for (idx = 0; idx < pcm_num; idx++) {
+ struct hdmi_spec_per_cvt *per_cvt;
+ struct hda_pcm *info;
+ struct hda_pcm_stream *pstr;
+
+ info = snd_hda_codec_pcm_new(codec, "HDMI %d", idx);
+ if (!info)
+ return -ENOMEM;
+
+ spec->pcm_rec[idx].pcm = info;
+ spec->pcm_used++;
+ info->pcm_type = HDA_PCM_TYPE_HDMI;
+ info->own_chmap = true;
+
+ pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
+ pstr->substreams = 1;
+ pstr->ops = generic_ops;
+
+ per_cvt = get_cvt(spec, 0);
+ pstr->channels_min = per_cvt->channels_min;
+ pstr->channels_max = per_cvt->channels_max;
+
+ /* pcm number is less than pcm_rec array size */
+ if (spec->pcm_used >= ARRAY_SIZE(spec->pcm_rec))
+ break;
+ /* other pstr fields are set in open */
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_build_pcms, "SND_HDA_CODEC_HDMI");
+
+static void free_hdmi_jack_priv(struct snd_jack *jack)
+{
+ struct hdmi_pcm *pcm = jack->private_data;
+
+ pcm->jack = NULL;
+}
+
+static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx)
+{
+ char hdmi_str[32] = "HDMI/DP";
+ struct hdmi_spec *spec = codec->spec;
+ struct snd_jack *jack;
+ int pcmdev = get_pcm_rec(spec, pcm_idx)->device;
+ int err;
+
+ if (pcmdev > 0)
+ sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
+
+ err = snd_jack_new(codec->card, hdmi_str, SND_JACK_AVOUT, &jack,
+ true, false);
+ if (err < 0)
+ return err;
+
+ spec->pcm_rec[pcm_idx].jack = jack;
+ jack->private_data = &spec->pcm_rec[pcm_idx];
+ jack->private_free = free_hdmi_jack_priv;
+ return 0;
+}
+
+int snd_hda_hdmi_generic_build_controls(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int dev, err;
+ int pin_idx, pcm_idx;
+
+ for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) {
+ if (!get_pcm_rec(spec, pcm_idx)->pcm) {
+ /* no PCM: mark this for skipping permanently */
+ set_bit(pcm_idx, &spec->pcm_bitmap);
+ continue;
+ }
+
+ err = generic_hdmi_build_jack(codec, pcm_idx);
+ if (err < 0)
+ return err;
+
+ /* create the spdif for each pcm
+ * pin will be bound when monitor is connected
+ */
+ err = snd_hda_create_dig_out_ctls(codec,
+ 0, spec->cvt_nids[0],
+ HDA_PCM_TYPE_HDMI);
+ if (err < 0)
+ return err;
+ snd_hda_spdif_ctls_unassign(codec, pcm_idx);
+
+ dev = get_pcm_rec(spec, pcm_idx)->device;
+ if (dev != SNDRV_PCM_INVALID_DEVICE) {
+ /* add control for ELD Bytes */
+ err = hdmi_create_eld_ctl(codec, pcm_idx, dev);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ struct hdmi_eld *pin_eld = &per_pin->sink_eld;
+
+ if (spec->static_pcm_mapping) {
+ hdmi_attach_hda_pcm(spec, per_pin);
+ hdmi_pcm_setup_pin(spec, per_pin);
+ }
+
+ pin_eld->eld_valid = false;
+ hdmi_present_sense(per_pin, 0);
+ }
+
+ /* add channel maps */
+ for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) {
+ struct hda_pcm *pcm;
+
+ pcm = get_pcm_rec(spec, pcm_idx);
+ if (!pcm || !pcm->pcm)
+ break;
+ err = snd_hdac_add_chmap_ctls(pcm->pcm, pcm_idx, &spec->chmap);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_build_controls, "SND_HDA_CODEC_HDMI");
+
+int snd_hda_hdmi_generic_init_per_pins(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+
+ per_pin->codec = codec;
+ mutex_init(&per_pin->lock);
+ INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld);
+ eld_proc_new(per_pin, pin_idx);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_init_per_pins, "SND_HDA_CODEC_HDMI");
+
+int snd_hda_hdmi_generic_init(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+
+ guard(mutex)(&spec->bind_lock);
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ hda_nid_t pin_nid = per_pin->pin_nid;
+ int dev_id = per_pin->dev_id;
+
+ snd_hda_set_dev_select(codec, pin_nid, dev_id);
+ hdmi_init_pin(codec, pin_nid);
+ if (codec_has_acomp(codec))
+ continue;
+ snd_hda_jack_detect_enable_callback_mst(codec, pin_nid, dev_id,
+ jack_callback);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_init, "SND_HDA_CODEC_HDMI");
+
+static void hdmi_array_init(struct hdmi_spec *spec, int nums)
+{
+ snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), nums);
+ snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), nums);
+}
+
+static void hdmi_array_free(struct hdmi_spec *spec)
+{
+ snd_array_free(&spec->pins);
+ snd_array_free(&spec->cvts);
+}
+
+void snd_hda_hdmi_generic_spec_free(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (spec) {
+ hdmi_array_free(spec);
+ kfree(spec);
+ codec->spec = NULL;
+ }
+ codec->dp_mst = false;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_spec_free, "SND_HDA_CODEC_HDMI");
+
+void snd_hda_hdmi_generic_remove(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx, pcm_idx;
+
+ if (spec->acomp_registered) {
+ snd_hdac_acomp_exit(&codec->bus->core);
+ } else if (codec_has_acomp(codec)) {
+ snd_hdac_acomp_register_notifier(&codec->bus->core, NULL);
+ }
+ codec->relaxed_resume = 0;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ cancel_delayed_work_sync(&per_pin->work);
+ eld_proc_free(per_pin);
+ }
+
+ for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) {
+ if (spec->pcm_rec[pcm_idx].jack == NULL)
+ continue;
+ snd_device_free(codec->card, spec->pcm_rec[pcm_idx].jack);
+ }
+
+ snd_hda_hdmi_generic_spec_free(codec);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_remove, "SND_HDA_CODEC_HDMI");
+
+int snd_hda_hdmi_generic_suspend(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ cancel_delayed_work_sync(&per_pin->work);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_suspend, "SND_HDA_CODEC_HDMI");
+
+int snd_hda_hdmi_generic_resume(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx;
+
+ snd_hda_codec_init(codec);
+ snd_hda_regmap_sync(codec);
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ hdmi_present_sense(per_pin, 1);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_resume, "SND_HDA_CODEC_HDMI");
+
+static const struct hdmi_ops generic_standard_hdmi_ops = {
+ .pin_get_eld = hdmi_pin_get_eld,
+ .pin_setup_infoframe = hdmi_pin_setup_infoframe,
+ .pin_hbr_setup = hdmi_pin_hbr_setup,
+ .setup_stream = snd_hda_hdmi_setup_stream,
+};
+
+/* allocate codec->spec and assign/initialize generic parser ops */
+int snd_hda_hdmi_generic_alloc(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+
+ spec->codec = codec;
+ spec->ops = generic_standard_hdmi_ops;
+ spec->dev_num = 1; /* initialize to 1 */
+ mutex_init(&spec->pcm_lock);
+ mutex_init(&spec->bind_lock);
+ snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
+
+ spec->chmap.ops.get_chmap = hdmi_get_chmap;
+ spec->chmap.ops.set_chmap = hdmi_set_chmap;
+ spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached;
+ spec->chmap.ops.get_spk_alloc = hdmi_get_spk_alloc;
+
+ codec->spec = spec;
+ hdmi_array_init(spec, 4);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_alloc, "SND_HDA_CODEC_HDMI");
+
+/* generic HDMI parser */
+int snd_hda_hdmi_generic_probe(struct hda_codec *codec)
+{
+ int err;
+
+ err = snd_hda_hdmi_generic_alloc(codec);
+ if (err < 0)
+ return err;
+
+ err = snd_hda_hdmi_parse_codec(codec);
+ if (err < 0) {
+ snd_hda_hdmi_generic_spec_free(codec);
+ return err;
+ }
+
+ snd_hda_hdmi_generic_init_per_pins(codec);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_generic_probe, "SND_HDA_CODEC_HDMI");
+
+/*
+ * generic audio component binding
+ */
+
+/* turn on / off the unsol event jack detection dynamically */
+static void reprogram_jack_detect(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, bool use_acomp)
+{
+ struct hda_jack_tbl *tbl;
+
+ tbl = snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
+ if (tbl) {
+ /* clear unsol even if component notifier is used, or re-enable
+ * if notifier is cleared
+ */
+ unsigned int val = use_acomp ? 0 : (AC_USRSP_EN | tbl->tag);
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE, val);
+ }
+}
+
+/* set up / clear component notifier dynamically */
+static void generic_acomp_notifier_set(struct drm_audio_component *acomp,
+ bool use_acomp)
+{
+ struct hdmi_spec *spec;
+ int i;
+
+ spec = container_of(acomp->audio_ops, struct hdmi_spec, drm_audio_ops);
+ guard(mutex)(&spec->bind_lock);
+ spec->use_acomp_notifier = use_acomp;
+ spec->codec->relaxed_resume = use_acomp;
+ spec->codec->bus->keep_power = 0;
+ /* reprogram each jack detection logic depending on the notifier */
+ for (i = 0; i < spec->num_pins; i++)
+ reprogram_jack_detect(spec->codec,
+ get_pin(spec, i)->pin_nid,
+ get_pin(spec, i)->dev_id,
+ use_acomp);
+}
+
+/* enable / disable the notifier via master bind / unbind */
+int snd_hda_hdmi_acomp_master_bind(struct device *dev,
+ struct drm_audio_component *acomp)
+{
+ generic_acomp_notifier_set(acomp, true);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_master_bind, "SND_HDA_CODEC_HDMI");
+
+void snd_hda_hdmi_acomp_master_unbind(struct device *dev,
+ struct drm_audio_component *acomp)
+{
+ generic_acomp_notifier_set(acomp, false);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_master_unbind, "SND_HDA_CODEC_HDMI");
+
+/* check whether both HD-audio and DRM PCI devices belong to the same bus */
+static int match_bound_vga(struct device *dev, int subtype, void *data)
+{
+ struct hdac_bus *bus = data;
+ struct pci_dev *pci, *master;
+
+ if (!dev_is_pci(dev) || !dev_is_pci(bus->dev))
+ return 0;
+ master = to_pci_dev(bus->dev);
+ pci = to_pci_dev(dev);
+ return master->bus == pci->bus;
+}
+
+/* audio component notifier for AMD/Nvidia HDMI codecs */
+void snd_hda_hdmi_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id)
+{
+ struct hda_codec *codec = audio_ptr;
+ struct hdmi_spec *spec = codec->spec;
+ hda_nid_t pin_nid = spec->port2pin(codec, port);
+
+ if (!pin_nid)
+ return;
+ if (get_wcaps_type(get_wcaps(codec, pin_nid)) != AC_WID_PIN)
+ return;
+ /* skip notification during system suspend (but not in runtime PM);
+ * the state will be updated at resume
+ */
+ if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND)
+ return;
+
+ snd_hda_hdmi_check_presence_and_report(codec, pin_nid, dev_id);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_pin_eld_notify, "SND_HDA_CODEC_HDMI");
+
+/* set up the private drm_audio_ops from the template */
+void snd_hda_hdmi_setup_drm_audio_ops(struct hda_codec *codec,
+ const struct drm_audio_component_audio_ops *ops)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ spec->drm_audio_ops.audio_ptr = codec;
+ /* intel_audio_codec_enable() or intel_audio_codec_disable()
+ * will call pin_eld_notify with using audio_ptr pointer
+ * We need make sure audio_ptr is really setup
+ */
+ wmb();
+ spec->drm_audio_ops.pin2port = ops->pin2port;
+ spec->drm_audio_ops.pin_eld_notify = ops->pin_eld_notify;
+ spec->drm_audio_ops.master_bind = ops->master_bind;
+ spec->drm_audio_ops.master_unbind = ops->master_unbind;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_setup_drm_audio_ops, "SND_HDA_CODEC_HDMI");
+
+/* initialize the generic HDMI audio component */
+void snd_hda_hdmi_acomp_init(struct hda_codec *codec,
+ const struct drm_audio_component_audio_ops *ops,
+ int (*port2pin)(struct hda_codec *, int))
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (!enable_acomp) {
+ codec_info(codec, "audio component disabled by module option\n");
+ return;
+ }
+
+ spec->port2pin = port2pin;
+ snd_hda_hdmi_setup_drm_audio_ops(codec, ops);
+ if (!snd_hdac_acomp_init(&codec->bus->core, &spec->drm_audio_ops,
+ match_bound_vga, 0)) {
+ spec->acomp_registered = true;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_acomp_init, "SND_HDA_CODEC_HDMI");
+
+/*
+ */
+
+enum {
+ MODEL_GENERIC,
+ MODEL_GF,
+};
+
+static int generichdmi_probe(struct hda_codec *codec,
+ const struct hda_device_id *id)
+{
+ int err;
+
+ err = snd_hda_hdmi_generic_probe(codec);
+ if (err < 0)
+ return err;
+ /*
+ * Glenfly GPUs have two codecs, stream switches from one codec to
+ * another, need to do actual clean-ups in codec_cleanup_stream
+ */
+ if (id->driver_data == MODEL_GF)
+ codec->no_sticky_stream = 1;
+
+ return 0;
+}
+
+static const struct hda_codec_ops generichdmi_codec_ops = {
+ .probe = generichdmi_probe,
+ .remove = snd_hda_hdmi_generic_remove,
+ .init = snd_hda_hdmi_generic_init,
+ .build_pcms = snd_hda_hdmi_generic_build_pcms,
+ .build_controls = snd_hda_hdmi_generic_build_controls,
+ .unsol_event = snd_hda_hdmi_generic_unsol_event,
+ .suspend = snd_hda_hdmi_generic_suspend,
+ .resume = snd_hda_hdmi_generic_resume,
+};
+
+/*
+ */
+static const struct hda_device_id snd_hda_id_generichdmi[] = {
+ HDA_CODEC_ID_MODEL(0x00147a47, "Loongson HDMI", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10951390, "SiI1390 HDMI", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10951392, "SiI1392 HDMI", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x11069f84, "VX11 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x11069f85, "VX11 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x17e80047, "Chrontel HDMI", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x1d179f86, "ZX-100S HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f87, "ZX-100S HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f88, "KX-5000 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f89, "KX-5000 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f8a, "KX-6000 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f8b, "KX-6000 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f8c, "KX-6000G HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f8d, "KX-6000G HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f8e, "KX-7000 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f8f, "KX-7000 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x1d179f90, "KX-7000 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x67663d82, "Arise 82 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x67663d83, "Arise 83 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x67663d84, "Arise 84 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x67663d85, "Arise 85 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x67663d86, "Arise 86 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x67663d87, "Arise 87 HDMI/DP", MODEL_GF),
+ HDA_CODEC_ID_MODEL(0x80862801, "Bearlake HDMI", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x80862802, "Cantiga HDMI", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x80862803, "Eaglelake HDMI", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x80862880, "CedarTrail HDMI", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x808629fb, "Crestline HDMI", MODEL_GENERIC),
+ /* special ID for generic HDMI */
+ HDA_CODEC_ID_MODEL(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", MODEL_GENERIC),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generichdmi);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic HDMI HD-audio codec");
+
+static struct hda_codec_driver generichdmi_driver = {
+ .id = snd_hda_id_generichdmi,
+ .ops = &generichdmi_codec_ops,
+};
+
+module_hda_codec_driver(generichdmi_driver);
diff --git a/sound/hda/codecs/hdmi/hdmi_local.h b/sound/hda/codecs/hdmi/hdmi_local.h
new file mode 100644
index 000000000000..548241ad3fa9
--- /dev/null
+++ b/sound/hda/codecs/hdmi/hdmi_local.h
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HD-audio HDMI codec driver
+ */
+
+#ifndef __HDA_HDMI_LOCAL_H
+#define __HDA_HDMI_LOCAL_H
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_chmap.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+
+struct hdmi_spec_per_cvt {
+ hda_nid_t cvt_nid;
+ bool assigned; /* the stream has been assigned */
+ bool silent_stream; /* silent stream activated */
+ unsigned int channels_min;
+ unsigned int channels_max;
+ u32 rates;
+ u64 formats;
+ unsigned int maxbps;
+};
+
+/* max. connections to a widget */
+#define HDA_MAX_CONNECTIONS 32
+
+struct hdmi_spec_per_pin {
+ hda_nid_t pin_nid;
+ int dev_id;
+ /* pin idx, different device entries on the same pin use the same idx */
+ int pin_nid_idx;
+ int num_mux_nids;
+ hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
+ int mux_idx;
+ hda_nid_t cvt_nid;
+
+ struct hda_codec *codec;
+ struct hdmi_eld sink_eld;
+ struct mutex lock;
+ struct delayed_work work;
+ struct hdmi_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/
+ int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */
+ int prev_pcm_idx; /* previously assigned pcm index */
+ int repoll_count;
+ bool setup; /* the stream has been set up by prepare callback */
+ bool silent_stream;
+ int channels; /* current number of channels */
+ bool non_pcm;
+ bool chmap_set; /* channel-map override by ALSA API? */
+ unsigned char chmap[8]; /* ALSA API channel-map */
+#ifdef CONFIG_SND_PROC_FS
+ struct snd_info_entry *proc_entry;
+#endif
+};
+
+/* operations used by generic code that can be overridden by codec drivers */
+struct hdmi_ops {
+ int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid,
+ int dev_id, unsigned char *buf, int *eld_size);
+
+ void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid,
+ int dev_id,
+ int ca, int active_channels, int conn_type);
+
+ /* enable/disable HBR (HD passthrough) */
+ int (*pin_hbr_setup)(struct hda_codec *codec, hda_nid_t pin_nid,
+ int dev_id, bool hbr);
+
+ int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid,
+ hda_nid_t pin_nid, int dev_id, u32 stream_tag,
+ int format);
+
+ void (*pin_cvt_fixup)(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ hda_nid_t cvt_nid);
+
+ void (*silent_stream)(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ bool enable);
+};
+
+struct hdmi_pcm {
+ struct hda_pcm *pcm;
+ struct snd_jack *jack;
+ struct snd_kcontrol *eld_ctl;
+};
+
+enum {
+ SILENT_STREAM_OFF = 0,
+ SILENT_STREAM_KAE, /* use standard HDA Keep-Alive */
+ SILENT_STREAM_I915, /* Intel i915 extension */
+};
+
+struct hdmi_spec {
+ struct hda_codec *codec;
+ int num_cvts;
+ struct snd_array cvts; /* struct hdmi_spec_per_cvt */
+ hda_nid_t cvt_nids[4]; /* only for haswell fix */
+
+ /*
+ * num_pins is the number of virtual pins
+ * for example, there are 3 pins, and each pin
+ * has 4 device entries, then the num_pins is 12
+ */
+ int num_pins;
+ /*
+ * num_nids is the number of real pins
+ * In the above example, num_nids is 3
+ */
+ int num_nids;
+ /*
+ * dev_num is the number of device entries
+ * on each pin.
+ * In the above example, dev_num is 4
+ */
+ int dev_num;
+ struct snd_array pins; /* struct hdmi_spec_per_pin */
+ struct hdmi_pcm pcm_rec[8];
+ struct mutex pcm_lock;
+ struct mutex bind_lock; /* for audio component binding */
+ /* pcm_bitmap means which pcms have been assigned to pins*/
+ unsigned long pcm_bitmap;
+ int pcm_used; /* counter of pcm_rec[] */
+ /* bitmap shows whether the pcm is opened in user space
+ * bit 0 means the first playback PCM (PCM3);
+ * bit 1 means the second playback PCM, and so on.
+ */
+ unsigned long pcm_in_use;
+
+ struct hdmi_eld temp_eld;
+ struct hdmi_ops ops;
+
+ bool dyn_pin_out;
+ bool static_pcm_mapping;
+ /* hdmi interrupt trigger control flag for Nvidia codec */
+ bool hdmi_intr_trig_ctrl;
+ bool nv_dp_workaround; /* workaround DP audio infoframe for Nvidia */
+
+ bool intel_hsw_fixup; /* apply Intel platform-specific fixups */
+ /*
+ * Non-generic VIA/NVIDIA specific
+ */
+ struct hda_multi_out multiout;
+ struct hda_pcm_stream pcm_playback;
+
+ bool use_acomp_notifier; /* use eld_notify callback for hotplug */
+ bool acomp_registered; /* audio component registered in this driver */
+ bool force_connect; /* force connectivity */
+ struct drm_audio_component_audio_ops drm_audio_ops;
+ int (*port2pin)(struct hda_codec *codec, int port); /* reverse port/pin mapping */
+
+ struct hdac_chmap chmap;
+ hda_nid_t vendor_nid;
+ const int *port_map;
+ int port_num;
+ int silent_stream_type;
+
+ const struct snd_pcm_hw_constraint_list *hw_constraints_channels;
+};
+
+#ifdef CONFIG_SND_HDA_COMPONENT
+static inline bool codec_has_acomp(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ return spec->use_acomp_notifier;
+}
+#else
+#define codec_has_acomp(codec) false
+#endif
+
+struct hdmi_audio_infoframe {
+ u8 type; /* 0x84 */
+ u8 ver; /* 0x01 */
+ u8 len; /* 0x0a */
+
+ u8 checksum;
+
+ u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */
+ u8 SS01_SF24;
+ u8 CXT04;
+ u8 CA;
+ u8 LFEPBL01_LSV36_DM_INH7;
+};
+
+struct dp_audio_infoframe {
+ u8 type; /* 0x84 */
+ u8 len; /* 0x1b */
+ u8 ver; /* 0x11 << 2 */
+
+ u8 CC02_CT47; /* match with HDMI infoframe from this on */
+ u8 SS01_SF24;
+ u8 CXT04;
+ u8 CA;
+ u8 LFEPBL01_LSV36_DM_INH7;
+};
+
+union audio_infoframe {
+ struct hdmi_audio_infoframe hdmi;
+ struct dp_audio_infoframe dp;
+ DECLARE_FLEX_ARRAY(u8, bytes);
+};
+
+#ifdef LIMITED_RATE_FMT_SUPPORT
+/* support only the safe format and rate */
+#define SUPPORTED_RATES SNDRV_PCM_RATE_48000
+#define SUPPORTED_MAXBPS 16
+#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+#else
+/* support all rates and formats */
+#define SUPPORTED_RATES \
+ (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+#define SUPPORTED_MAXBPS 24
+#define SUPPORTED_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+#endif
+
+/*
+ * HDMI routines
+ */
+
+#define get_pin(spec, idx) \
+ ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx))
+#define get_cvt(spec, idx) \
+ ((struct hdmi_spec_per_cvt *)snd_array_elem(&spec->cvts, idx))
+/* obtain hdmi_pcm object assigned to idx */
+#define get_hdmi_pcm(spec, idx) (&(spec)->pcm_rec[idx])
+/* obtain hda_pcm object assigned to idx */
+#define get_pcm_rec(spec, idx) (get_hdmi_pcm(spec, idx)->pcm)
+
+/* Generic HDMI codec support */
+int snd_hda_hdmi_generic_alloc(struct hda_codec *codec);
+int snd_hda_hdmi_parse_codec(struct hda_codec *codec);
+int snd_hda_hdmi_generic_probe(struct hda_codec *codec);
+void snd_hda_hdmi_generic_remove(struct hda_codec *codec);
+
+int snd_hda_hdmi_generic_build_pcms(struct hda_codec *codec);
+int snd_hda_hdmi_generic_build_controls(struct hda_codec *codec);
+int snd_hda_hdmi_generic_init(struct hda_codec *codec);
+int snd_hda_hdmi_generic_suspend(struct hda_codec *codec);
+int snd_hda_hdmi_generic_resume(struct hda_codec *codec);
+void snd_hda_hdmi_generic_unsol_event(struct hda_codec *codec, unsigned int res);
+
+int snd_hda_hdmi_pin_id_to_pin_index(struct hda_codec *codec,
+ hda_nid_t pin_nid, int dev_id);
+#define pin_id_to_pin_index(codec, pin, dev) \
+ snd_hda_hdmi_pin_id_to_pin_index(codec, pin, dev)
+int snd_hda_hdmi_generic_init_per_pins(struct hda_codec *codec);
+void snd_hda_hdmi_generic_spec_free(struct hda_codec *codec);
+int snd_hda_hdmi_setup_stream(struct hda_codec *codec,
+ hda_nid_t cvt_nid,
+ hda_nid_t pin_nid, int dev_id,
+ u32 stream_tag, int format);
+
+int snd_hda_hdmi_generic_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream);
+int snd_hda_hdmi_generic_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream);
+
+void snd_hda_hdmi_check_presence_and_report(struct hda_codec *codec,
+ hda_nid_t nid, int dev_id);
+void snd_hda_hdmi_setup_audio_infoframe(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ bool non_pcm);
+
+/* Audio component support */
+void snd_hda_hdmi_setup_drm_audio_ops(struct hda_codec *codec,
+ const struct drm_audio_component_audio_ops *ops);
+void snd_hda_hdmi_acomp_init(struct hda_codec *codec,
+ const struct drm_audio_component_audio_ops *ops,
+ int (*port2pin)(struct hda_codec *, int));
+void snd_hda_hdmi_acomp_pin_eld_notify(void *audio_ptr, int port, int dev_id);
+int snd_hda_hdmi_acomp_master_bind(struct device *dev,
+ struct drm_audio_component *acomp);
+void snd_hda_hdmi_acomp_master_unbind(struct device *dev,
+ struct drm_audio_component *acomp);
+
+/* Simple / legacy HDMI codec support */
+int snd_hda_hdmi_simple_probe(struct hda_codec *codec,
+ hda_nid_t cvt_nid, hda_nid_t pin_nid);
+void snd_hda_hdmi_simple_remove(struct hda_codec *codec);
+
+int snd_hda_hdmi_simple_build_pcms(struct hda_codec *codec);
+int snd_hda_hdmi_simple_build_controls(struct hda_codec *codec);
+int snd_hda_hdmi_simple_init(struct hda_codec *codec);
+void snd_hda_hdmi_simple_unsol_event(struct hda_codec *codec,
+ unsigned int res);
+int snd_hda_hdmi_simple_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream);
+
+#endif /* __HDA_HDMI_LOCAL_H */
diff --git a/sound/hda/codecs/hdmi/intelhdmi.c b/sound/hda/codecs/hdmi/intelhdmi.c
new file mode 100644
index 000000000000..9460c8db39a9
--- /dev/null
+++ b/sound/hda/codecs/hdmi/intelhdmi.c
@@ -0,0 +1,812 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Intel HDMI codec support
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hdmi_local.h"
+
+static bool enable_silent_stream =
+IS_ENABLED(CONFIG_SND_HDA_INTEL_HDMI_SILENT_STREAM);
+module_param(enable_silent_stream, bool, 0644);
+MODULE_PARM_DESC(enable_silent_stream, "Enable Silent Stream for HDMI devices");
+
+enum {
+ MODEL_HSW,
+ MODEL_GLK,
+ MODEL_ICL,
+ MODEL_TGL,
+ MODEL_ADLP,
+ MODEL_BYT,
+ MODEL_CPT,
+};
+
+#define INTEL_GET_VENDOR_VERB 0xf81
+#define INTEL_SET_VENDOR_VERB 0x781
+#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */
+#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */
+
+static void intel_haswell_enable_all_pins(struct hda_codec *codec,
+ bool update_tree)
+{
+ unsigned int vendor_param;
+ struct hdmi_spec *spec = codec->spec;
+
+ vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0,
+ INTEL_GET_VENDOR_VERB, 0);
+ if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS)
+ return;
+
+ vendor_param |= INTEL_EN_ALL_PIN_CVTS;
+ vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0,
+ INTEL_SET_VENDOR_VERB, vendor_param);
+ if (vendor_param == -1)
+ return;
+
+ if (update_tree)
+ snd_hda_codec_update_widgets(codec);
+}
+
+static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec)
+{
+ unsigned int vendor_param;
+ struct hdmi_spec *spec = codec->spec;
+
+ vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0,
+ INTEL_GET_VENDOR_VERB, 0);
+ if (vendor_param == -1 || vendor_param & INTEL_EN_DP12)
+ return;
+
+ /* enable DP1.2 mode */
+ vendor_param |= INTEL_EN_DP12;
+ snd_hdac_regmap_add_vendor_verb(&codec->core, INTEL_SET_VENDOR_VERB);
+ snd_hda_codec_write_cache(codec, spec->vendor_nid, 0,
+ INTEL_SET_VENDOR_VERB, vendor_param);
+}
+
+/* Haswell needs to re-issue the vendor-specific verbs before turning to D0.
+ * Otherwise you may get severe h/w communication errors.
+ */
+static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+ unsigned int power_state)
+{
+ /* check codec->spec: it can be called before the probe gets called */
+ if (codec->spec) {
+ if (power_state == AC_PWRST_D0) {
+ intel_haswell_enable_all_pins(codec, false);
+ intel_haswell_fixup_enable_dp12(codec);
+ }
+ }
+
+ snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state);
+ snd_hda_codec_set_power_to_all(codec, fg, power_state);
+}
+
+/* There is a fixed mapping between audio pin node and display port.
+ * on SNB, IVY, HSW, BSW, SKL, BXT, KBL:
+ * Pin Widget 5 - PORT B (port = 1 in i915 driver)
+ * Pin Widget 6 - PORT C (port = 2 in i915 driver)
+ * Pin Widget 7 - PORT D (port = 3 in i915 driver)
+ *
+ * on VLV, ILK:
+ * Pin Widget 4 - PORT B (port = 1 in i915 driver)
+ * Pin Widget 5 - PORT C (port = 2 in i915 driver)
+ * Pin Widget 6 - PORT D (port = 3 in i915 driver)
+ */
+static int intel_base_nid(struct hda_codec *codec)
+{
+ switch (codec->core.vendor_id) {
+ case 0x80860054: /* ILK */
+ case 0x80862804: /* ILK */
+ case 0x80862882: /* VLV */
+ return 4;
+ default:
+ return 5;
+ }
+}
+
+static int intel_pin2port(void *audio_ptr, int pin_nid)
+{
+ struct hda_codec *codec = audio_ptr;
+ struct hdmi_spec *spec = codec->spec;
+ int base_nid, i;
+
+ if (!spec->port_num) {
+ base_nid = intel_base_nid(codec);
+ if (WARN_ON(pin_nid < base_nid || pin_nid >= base_nid + 3))
+ return -1;
+ return pin_nid - base_nid + 1;
+ }
+
+ /*
+ * looking for the pin number in the mapping table and return
+ * the index which indicate the port number
+ */
+ for (i = 0; i < spec->port_num; i++) {
+ if (pin_nid == spec->port_map[i])
+ return i;
+ }
+
+ codec_info(codec, "Can't find the HDMI/DP port for pin NID 0x%x\n", pin_nid);
+ return -1;
+}
+
+static int intel_port2pin(struct hda_codec *codec, int port)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (!spec->port_num) {
+ /* we assume only from port-B to port-D */
+ if (port < 1 || port > 3)
+ return 0;
+ return port + intel_base_nid(codec) - 1;
+ }
+
+ if (port < 0 || port >= spec->port_num)
+ return 0;
+ return spec->port_map[port];
+}
+
+static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe)
+{
+ struct hda_codec *codec = audio_ptr;
+ int pin_nid;
+ int dev_id = pipe;
+
+ pin_nid = intel_port2pin(codec, port);
+ if (!pin_nid)
+ return;
+ /* skip notification during system suspend (but not in runtime PM);
+ * the state will be updated at resume
+ */
+ if (codec->core.dev.power.power_state.event == PM_EVENT_SUSPEND)
+ return;
+
+ snd_hdac_i915_set_bclk(&codec->bus->core);
+ snd_hda_hdmi_check_presence_and_report(codec, pin_nid, dev_id);
+}
+
+static const struct drm_audio_component_audio_ops intel_audio_ops = {
+ .pin2port = intel_pin2port,
+ .pin_eld_notify = intel_pin_eld_notify,
+};
+
+/* register i915 component pin_eld_notify callback */
+static void register_i915_notifier(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ spec->use_acomp_notifier = true;
+ spec->port2pin = intel_port2pin;
+ snd_hda_hdmi_setup_drm_audio_ops(codec, &intel_audio_ops);
+ snd_hdac_acomp_register_notifier(&codec->bus->core,
+ &spec->drm_audio_ops);
+ /* no need for forcible resume for jack check thanks to notifier */
+ codec->relaxed_resume = 1;
+}
+
+#define I915_SILENT_RATE 48000
+#define I915_SILENT_CHANNELS 2
+#define I915_SILENT_FORMAT_BITS 16
+#define I915_SILENT_FMT_MASK 0xf
+
+static void silent_stream_enable_i915(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ unsigned int format;
+
+ snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid,
+ per_pin->dev_id, I915_SILENT_RATE);
+
+ /* trigger silent stream generation in hw */
+ format = snd_hdac_stream_format(I915_SILENT_CHANNELS, I915_SILENT_FORMAT_BITS,
+ I915_SILENT_RATE);
+ snd_hda_codec_setup_stream(codec, per_pin->cvt_nid,
+ I915_SILENT_FMT_MASK, I915_SILENT_FMT_MASK, format);
+ usleep_range(100, 200);
+ snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, I915_SILENT_FMT_MASK, 0, format);
+
+ per_pin->channels = I915_SILENT_CHANNELS;
+ snd_hda_hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
+}
+
+static void silent_stream_set_kae(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ bool enable)
+{
+ unsigned int param;
+
+ codec_dbg(codec, "HDMI: KAE %d cvt-NID=0x%x\n", enable, per_pin->cvt_nid);
+
+ param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0);
+ param = (param >> 16) & 0xff;
+
+ if (enable)
+ param |= AC_DIG3_KAE;
+ else
+ param &= ~AC_DIG3_KAE;
+
+ snd_hda_codec_write(codec, per_pin->cvt_nid, 0, AC_VERB_SET_DIGI_CONVERT_3, param);
+}
+
+static void i915_set_silent_stream(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ bool enable)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ switch (spec->silent_stream_type) {
+ case SILENT_STREAM_KAE:
+ if (enable) {
+ silent_stream_enable_i915(codec, per_pin);
+ silent_stream_set_kae(codec, per_pin, true);
+ } else {
+ silent_stream_set_kae(codec, per_pin, false);
+ }
+ break;
+ case SILENT_STREAM_I915:
+ if (enable) {
+ silent_stream_enable_i915(codec, per_pin);
+ snd_hda_power_up_pm(codec);
+ } else {
+ /* release ref taken in silent_stream_enable() */
+ snd_hda_power_down_pm(codec);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void haswell_verify_D0(struct hda_codec *codec,
+ hda_nid_t cvt_nid, hda_nid_t nid)
+{
+ int pwr;
+
+ /* For Haswell, the converter 1/2 may keep in D3 state after bootup,
+ * thus pins could only choose converter 0 for use. Make sure the
+ * converters are in correct power state
+ */
+ if (!snd_hda_check_power_state(codec, cvt_nid, AC_PWRST_D0))
+ snd_hda_codec_write(codec, cvt_nid, 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+
+ if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0)) {
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D0);
+ msleep(40);
+ pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0);
+ pwr = (pwr & AC_PWRST_ACTUAL) >> AC_PWRST_ACTUAL_SHIFT;
+ codec_dbg(codec, "Haswell HDMI audio: Power for NID 0x%x is now D%d\n", nid, pwr);
+ }
+}
+
+/* Assure the pin select the right convetor */
+static void intel_verify_pin_cvt_connect(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ hda_nid_t pin_nid = per_pin->pin_nid;
+ int mux_idx, curr;
+
+ mux_idx = per_pin->mux_idx;
+ curr = snd_hda_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_CONNECT_SEL, 0);
+ if (curr != mux_idx)
+ snd_hda_codec_write_cache(codec, pin_nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ mux_idx);
+}
+
+/* get the mux index for the converter of the pins
+ * converter's mux index is the same for all pins on Intel platform
+ */
+static int intel_cvt_id_to_mux_idx(struct hdmi_spec *spec,
+ hda_nid_t cvt_nid)
+{
+ int i;
+
+ for (i = 0; i < spec->num_cvts; i++)
+ if (spec->cvt_nids[i] == cvt_nid)
+ return i;
+ return -EINVAL;
+}
+
+/* Intel HDMI workaround to fix audio routing issue:
+ * For some Intel display codecs, pins share the same connection list.
+ * So a conveter can be selected by multiple pins and playback on any of these
+ * pins will generate sound on the external display, because audio flows from
+ * the same converter to the display pipeline. Also muting one pin may make
+ * other pins have no sound output.
+ * So this function assures that an assigned converter for a pin is not selected
+ * by any other pins.
+ */
+static void intel_not_share_assigned_cvt(struct hda_codec *codec,
+ hda_nid_t pin_nid,
+ int dev_id, int mux_idx)
+{
+ struct hdmi_spec *spec = codec->spec;
+ hda_nid_t nid;
+ int cvt_idx, curr;
+ struct hdmi_spec_per_cvt *per_cvt;
+ struct hdmi_spec_per_pin *per_pin;
+ int pin_idx;
+
+ /* configure the pins connections */
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ int dev_id_saved;
+ int dev_num;
+
+ per_pin = get_pin(spec, pin_idx);
+ /*
+ * pin not connected to monitor
+ * no need to operate on it
+ */
+ if (!per_pin->pcm)
+ continue;
+
+ if ((per_pin->pin_nid == pin_nid) &&
+ (per_pin->dev_id == dev_id))
+ continue;
+
+ /*
+ * if per_pin->dev_id >= dev_num,
+ * snd_hda_get_dev_select() will fail,
+ * and the following operation is unpredictable.
+ * So skip this situation.
+ */
+ dev_num = snd_hda_get_num_devices(codec, per_pin->pin_nid) + 1;
+ if (per_pin->dev_id >= dev_num)
+ continue;
+
+ nid = per_pin->pin_nid;
+
+ /*
+ * Calling this function should not impact
+ * on the device entry selection
+ * So let's save the dev id for each pin,
+ * and restore it when return
+ */
+ dev_id_saved = snd_hda_get_dev_select(codec, nid);
+ snd_hda_set_dev_select(codec, nid, per_pin->dev_id);
+ curr = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONNECT_SEL, 0);
+ if (curr != mux_idx) {
+ snd_hda_set_dev_select(codec, nid, dev_id_saved);
+ continue;
+ }
+
+
+ /* choose an unassigned converter. The conveters in the
+ * connection list are in the same order as in the codec.
+ */
+ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
+ per_cvt = get_cvt(spec, cvt_idx);
+ if (!per_cvt->assigned) {
+ codec_dbg(codec,
+ "choose cvt %d for pin NID 0x%x\n",
+ cvt_idx, nid);
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ cvt_idx);
+ break;
+ }
+ }
+ snd_hda_set_dev_select(codec, nid, dev_id_saved);
+ }
+}
+
+/* A wrapper of intel_not_share_asigned_cvt() */
+static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
+ hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid)
+{
+ int mux_idx;
+ struct hdmi_spec *spec = codec->spec;
+
+ /* On Intel platform, the mapping of converter nid to
+ * mux index of the pins are always the same.
+ * The pin nid may be 0, this means all pins will not
+ * share the converter.
+ */
+ mux_idx = intel_cvt_id_to_mux_idx(spec, cvt_nid);
+ if (mux_idx >= 0)
+ intel_not_share_assigned_cvt(codec, pin_nid, dev_id, mux_idx);
+}
+
+/* setup_stream ops override for HSW+ */
+static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
+ hda_nid_t pin_nid, int dev_id, u32 stream_tag,
+ int format)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx = pin_id_to_pin_index(codec, pin_nid, dev_id);
+ struct hdmi_spec_per_pin *per_pin;
+ int res;
+
+ if (pin_idx < 0)
+ per_pin = NULL;
+ else
+ per_pin = get_pin(spec, pin_idx);
+
+ haswell_verify_D0(codec, cvt_nid, pin_nid);
+
+ if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) {
+ silent_stream_set_kae(codec, per_pin, false);
+ /* wait for pending transfers in codec to clear */
+ usleep_range(100, 200);
+ }
+
+ res = snd_hda_hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id,
+ stream_tag, format);
+
+ if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) {
+ usleep_range(100, 200);
+ silent_stream_set_kae(codec, per_pin, true);
+ }
+
+ return res;
+}
+
+/* pin_cvt_fixup ops override for HSW+ and VLV+ */
+static void i915_pin_cvt_fixup(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin,
+ hda_nid_t cvt_nid)
+{
+ if (per_pin) {
+ haswell_verify_D0(codec, per_pin->cvt_nid, per_pin->pin_nid);
+ snd_hda_set_dev_select(codec, per_pin->pin_nid,
+ per_pin->dev_id);
+ intel_verify_pin_cvt_connect(codec, per_pin);
+ intel_not_share_assigned_cvt(codec, per_pin->pin_nid,
+ per_pin->dev_id, per_pin->mux_idx);
+ } else {
+ intel_not_share_assigned_cvt_nid(codec, 0, 0, cvt_nid);
+ }
+}
+
+static int i915_hdmi_suspend(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ bool silent_streams = false;
+ int pin_idx, res;
+
+ res = snd_hda_hdmi_generic_suspend(codec);
+ if (spec->silent_stream_type != SILENT_STREAM_KAE)
+ return res;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+
+ if (per_pin->silent_stream) {
+ silent_streams = true;
+ break;
+ }
+ }
+
+ if (silent_streams) {
+ /*
+ * stream-id should remain programmed when codec goes
+ * to runtime suspend
+ */
+ codec->no_stream_clean_at_suspend = 1;
+
+ /*
+ * the system might go to S3, in which case keep-alive
+ * must be reprogrammed upon resume
+ */
+ codec->forced_resume = 1;
+
+ codec_dbg(codec, "HDMI: KAE active at suspend\n");
+ } else {
+ codec->no_stream_clean_at_suspend = 0;
+ codec->forced_resume = 0;
+ }
+
+ return res;
+}
+
+static int i915_hdmi_resume(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pin_idx, res;
+
+ res = snd_hda_hdmi_generic_resume(codec);
+ if (spec->silent_stream_type != SILENT_STREAM_KAE)
+ return res;
+
+ /* KAE not programmed at suspend, nothing to do here */
+ if (!codec->no_stream_clean_at_suspend)
+ return res;
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+
+ /*
+ * If system was in suspend with monitor connected,
+ * the codec setting may have been lost. Re-enable
+ * keep-alive.
+ */
+ if (per_pin->silent_stream) {
+ unsigned int param;
+
+ param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0,
+ AC_VERB_GET_CONV, 0);
+ if (!param) {
+ codec_dbg(codec, "HDMI: KAE: restore stream id\n");
+ silent_stream_enable_i915(codec, per_pin);
+ }
+
+ param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0,
+ AC_VERB_GET_DIGI_CONVERT_1, 0);
+ if (!(param & (AC_DIG3_KAE << 16))) {
+ codec_dbg(codec, "HDMI: KAE: restore DIG3_KAE\n");
+ silent_stream_set_kae(codec, per_pin, true);
+ }
+ }
+ }
+
+ return res;
+}
+
+/* precondition and allocation for Intel codecs */
+static int alloc_intel_hdmi(struct hda_codec *codec)
+{
+ /* requires i915 binding */
+ if (!codec->bus->core.audio_component) {
+ codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
+ /* set probe_id here to prevent generic fallback binding */
+ codec->probe_id = HDA_CODEC_ID_SKIP_PROBE;
+ return -ENODEV;
+ }
+
+ return snd_hda_hdmi_generic_alloc(codec);
+}
+
+/* parse and post-process for Intel codecs */
+static int parse_intel_hdmi(struct hda_codec *codec)
+{
+ int err, retries = 3;
+
+ do {
+ err = snd_hda_hdmi_parse_codec(codec);
+ } while (err < 0 && retries--);
+
+ if (err < 0)
+ return err;
+
+ snd_hda_hdmi_generic_init_per_pins(codec);
+ register_i915_notifier(codec);
+ return 0;
+}
+
+/* Intel Haswell and onwards; audio component with eld notifier */
+static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid,
+ const int *port_map, int port_num, int dev_num,
+ bool send_silent_stream)
+{
+ struct hdmi_spec *spec;
+
+ spec = codec->spec;
+ codec->dp_mst = true;
+ spec->vendor_nid = vendor_nid;
+ spec->port_map = port_map;
+ spec->port_num = port_num;
+ spec->intel_hsw_fixup = true;
+ spec->dev_num = dev_num;
+
+ intel_haswell_enable_all_pins(codec, true);
+ intel_haswell_fixup_enable_dp12(codec);
+
+ codec->display_power_control = 1;
+
+ codec->depop_delay = 0;
+ codec->auto_runtime_pm = 1;
+
+ spec->ops.setup_stream = i915_hsw_setup_stream;
+ spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
+ spec->ops.silent_stream = i915_set_silent_stream;
+
+ /*
+ * Enable silent stream feature, if it is enabled via
+ * module param or Kconfig option
+ */
+ if (send_silent_stream)
+ spec->silent_stream_type = SILENT_STREAM_I915;
+
+ return parse_intel_hdmi(codec);
+}
+
+static int probe_i915_hsw_hdmi(struct hda_codec *codec)
+{
+ return intel_hsw_common_init(codec, 0x08, NULL, 0, 3,
+ enable_silent_stream);
+}
+
+static int probe_i915_glk_hdmi(struct hda_codec *codec)
+{
+ /*
+ * Silent stream calls audio component .get_power() from
+ * .pin_eld_notify(). On GLK this will deadlock in i915 due
+ * to the audio vs. CDCLK workaround.
+ */
+ return intel_hsw_common_init(codec, 0x0b, NULL, 0, 3, false);
+}
+
+static int probe_i915_icl_hdmi(struct hda_codec *codec)
+{
+ /*
+ * pin to port mapping table where the value indicate the pin number and
+ * the index indicate the port number.
+ */
+ static const int map[] = {0x0, 0x4, 0x6, 0x8, 0xa, 0xb};
+
+ return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 3,
+ enable_silent_stream);
+}
+
+static int probe_i915_tgl_hdmi(struct hda_codec *codec)
+{
+ /*
+ * pin to port mapping table where the value indicate the pin number and
+ * the index indicate the port number.
+ */
+ static const int map[] = {0x4, 0x6, 0x8, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+
+ return intel_hsw_common_init(codec, 0x02, map, ARRAY_SIZE(map), 4,
+ enable_silent_stream);
+}
+
+static int probe_i915_adlp_hdmi(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int res;
+
+ res = probe_i915_tgl_hdmi(codec);
+ if (!res) {
+ spec = codec->spec;
+
+ if (spec->silent_stream_type)
+ spec->silent_stream_type = SILENT_STREAM_KAE;
+ }
+
+ return res;
+}
+
+/* Intel Baytrail and Braswell; with eld notifier */
+static int probe_i915_byt_hdmi(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+
+ spec = codec->spec;
+
+ /* For Valleyview/Cherryview, only the display codec is in the display
+ * power well and can use link_power ops to request/release the power.
+ */
+ codec->display_power_control = 1;
+
+ codec->depop_delay = 0;
+ codec->auto_runtime_pm = 1;
+
+ spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
+
+ return parse_intel_hdmi(codec);
+}
+
+/* Intel IronLake, SandyBridge and IvyBridge; with eld notifier */
+static int probe_i915_cpt_hdmi(struct hda_codec *codec)
+{
+ return parse_intel_hdmi(codec);
+}
+
+/*
+ * common driver probe
+ */
+static int intelhdmi_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ int err;
+
+ err = alloc_intel_hdmi(codec);
+ if (err < 0)
+ return err;
+
+ switch (id->driver_data) {
+ case MODEL_HSW:
+ err = probe_i915_hsw_hdmi(codec);
+ break;
+ case MODEL_GLK:
+ err = probe_i915_glk_hdmi(codec);
+ break;
+ case MODEL_ICL:
+ err = probe_i915_icl_hdmi(codec);
+ break;
+ case MODEL_TGL:
+ err = probe_i915_tgl_hdmi(codec);
+ break;
+ case MODEL_ADLP:
+ err = probe_i915_adlp_hdmi(codec);
+ break;
+ case MODEL_BYT:
+ err = probe_i915_byt_hdmi(codec);
+ break;
+ case MODEL_CPT:
+ err = probe_i915_cpt_hdmi(codec);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ if (err < 0) {
+ snd_hda_hdmi_generic_spec_free(codec);
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct hda_codec_ops intelhdmi_codec_ops = {
+ .probe = intelhdmi_probe,
+ .remove = snd_hda_hdmi_generic_remove,
+ .init = snd_hda_hdmi_generic_init,
+ .build_pcms = snd_hda_hdmi_generic_build_pcms,
+ .build_controls = snd_hda_hdmi_generic_build_controls,
+ .unsol_event = snd_hda_hdmi_generic_unsol_event,
+ .suspend = i915_hdmi_suspend,
+ .resume = i915_hdmi_resume,
+ .set_power_state = haswell_set_power_state,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_intelhdmi[] = {
+ HDA_CODEC_ID_MODEL(0x80860054, "IbexPeak HDMI", MODEL_CPT),
+ HDA_CODEC_ID_MODEL(0x80862800, "Geminilake HDMI", MODEL_GLK),
+ HDA_CODEC_ID_MODEL(0x80862804, "IbexPeak HDMI", MODEL_CPT),
+ HDA_CODEC_ID_MODEL(0x80862805, "CougarPoint HDMI", MODEL_CPT),
+ HDA_CODEC_ID_MODEL(0x80862806, "PantherPoint HDMI", MODEL_CPT),
+ HDA_CODEC_ID_MODEL(0x80862807, "Haswell HDMI", MODEL_HSW),
+ HDA_CODEC_ID_MODEL(0x80862808, "Broadwell HDMI", MODEL_HSW),
+ HDA_CODEC_ID_MODEL(0x80862809, "Skylake HDMI", MODEL_HSW),
+ HDA_CODEC_ID_MODEL(0x8086280a, "Broxton HDMI", MODEL_HSW),
+ HDA_CODEC_ID_MODEL(0x8086280b, "Kabylake HDMI", MODEL_HSW),
+ HDA_CODEC_ID_MODEL(0x8086280c, "Cannonlake HDMI", MODEL_GLK),
+ HDA_CODEC_ID_MODEL(0x8086280d, "Geminilake HDMI", MODEL_GLK),
+ HDA_CODEC_ID_MODEL(0x8086280f, "Icelake HDMI", MODEL_ICL),
+ HDA_CODEC_ID_MODEL(0x80862812, "Tigerlake HDMI", MODEL_TGL),
+ HDA_CODEC_ID_MODEL(0x80862814, "DG1 HDMI", MODEL_TGL),
+ HDA_CODEC_ID_MODEL(0x80862815, "Alderlake HDMI", MODEL_TGL),
+ HDA_CODEC_ID_MODEL(0x80862816, "Rocketlake HDMI", MODEL_TGL),
+ HDA_CODEC_ID_MODEL(0x80862818, "Raptorlake HDMI", MODEL_TGL),
+ HDA_CODEC_ID_MODEL(0x80862819, "DG2 HDMI", MODEL_TGL),
+ HDA_CODEC_ID_MODEL(0x8086281a, "Jasperlake HDMI", MODEL_ICL),
+ HDA_CODEC_ID_MODEL(0x8086281b, "Elkhartlake HDMI", MODEL_ICL),
+ HDA_CODEC_ID_MODEL(0x8086281c, "Alderlake-P HDMI", MODEL_ADLP),
+ HDA_CODEC_ID_MODEL(0x8086281d, "Meteor Lake HDMI", MODEL_ADLP),
+ HDA_CODEC_ID_MODEL(0x8086281e, "Battlemage HDMI", MODEL_ADLP),
+ HDA_CODEC_ID_MODEL(0x8086281f, "Raptor Lake P HDMI", MODEL_ADLP),
+ HDA_CODEC_ID_MODEL(0x80862820, "Lunar Lake HDMI", MODEL_ADLP),
+ HDA_CODEC_ID_MODEL(0x80862822, "Panther Lake HDMI", MODEL_ADLP),
+ HDA_CODEC_ID_MODEL(0x80862823, "Wildcat Lake HDMI", MODEL_ADLP),
+ HDA_CODEC_ID_MODEL(0x80862824, "Nova Lake HDMI", MODEL_ADLP),
+ HDA_CODEC_ID_MODEL(0x80862882, "Valleyview2 HDMI", MODEL_BYT),
+ HDA_CODEC_ID_MODEL(0x80862883, "Braswell HDMI", MODEL_BYT),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_intelhdmi);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Intel HDMI HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI");
+
+static struct hda_codec_driver intelhdmi_driver = {
+ .id = snd_hda_id_intelhdmi,
+ .ops = &intelhdmi_codec_ops,
+};
+
+module_hda_codec_driver(intelhdmi_driver);
diff --git a/sound/hda/codecs/hdmi/nvhdmi-mcp.c b/sound/hda/codecs/hdmi/nvhdmi-mcp.c
new file mode 100644
index 000000000000..1c5fdfe872f2
--- /dev/null
+++ b/sound/hda/codecs/hdmi/nvhdmi-mcp.c
@@ -0,0 +1,383 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Legacy Nvidia HDMI codec support
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hdmi_local.h"
+
+enum { MODEL_2CH, MODEL_8CH };
+
+#define Nv_VERB_SET_Channel_Allocation 0xF79
+#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A
+#define Nv_VERB_SET_Audio_Protection_On 0xF98
+#define Nv_VERB_SET_Audio_Protection_Off 0xF99
+
+#define nvhdmi_master_con_nid_7x 0x04
+#define nvhdmi_master_pin_nid_7x 0x05
+
+static const hda_nid_t nvhdmi_con_nids_7x[4] = {
+ /*front, rear, clfe, rear_surr */
+ 0x6, 0x8, 0xa, 0xc,
+};
+
+static const struct hda_verb nvhdmi_basic_init_7x_2ch[] = {
+ /* set audio protect on */
+ { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
+ /* enable digital output on pin widget */
+ { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ {} /* terminator */
+};
+
+static const struct hda_verb nvhdmi_basic_init_7x_8ch[] = {
+ /* set audio protect on */
+ { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
+ /* enable digital output on pin widget */
+ { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ {} /* terminator */
+};
+
+static int nvhdmi_mcp_init(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (spec->multiout.max_channels == 2)
+ snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_2ch);
+ else
+ snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_8ch);
+ return 0;
+}
+
+static void nvhdmi_8ch_7x_set_info_frame_parameters(struct hda_codec *codec,
+ int channels)
+{
+ unsigned int chanmask;
+ int chan = channels ? (channels - 1) : 1;
+
+ switch (channels) {
+ default:
+ case 0:
+ case 2:
+ chanmask = 0x00;
+ break;
+ case 4:
+ chanmask = 0x08;
+ break;
+ case 6:
+ chanmask = 0x0b;
+ break;
+ case 8:
+ chanmask = 0x13;
+ break;
+ }
+
+ /* Set the audio infoframe channel allocation and checksum fields. The
+ * channel count is computed implicitly by the hardware.
+ */
+ snd_hda_codec_write(codec, 0x1, 0,
+ Nv_VERB_SET_Channel_Allocation, chanmask);
+
+ snd_hda_codec_write(codec, 0x1, 0,
+ Nv_VERB_SET_Info_Frame_Checksum,
+ (0x71 - chan - chanmask));
+}
+
+static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int i;
+
+ snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
+ 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+ for (i = 0; i < 4; i++) {
+ /* set the stream id */
+ snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
+ AC_VERB_SET_CHANNEL_STREAMID, 0);
+ /* set the stream format */
+ snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
+ AC_VERB_SET_STREAM_FORMAT, 0);
+ }
+
+ /* The audio hardware sends a channel count of 0x7 (8ch) when all the
+ * streams are disabled.
+ */
+ nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8);
+
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ int chs;
+ unsigned int dataDCC2, channel_id;
+ int i;
+ struct hdmi_spec *spec = codec->spec;
+ struct hda_spdif_out *spdif;
+ struct hdmi_spec_per_cvt *per_cvt;
+
+ guard(mutex)(&codec->spdif_mutex);
+ per_cvt = get_cvt(spec, 0);
+ spdif = snd_hda_spdif_out_of_nid(codec, per_cvt->cvt_nid);
+
+ chs = substream->runtime->channels;
+
+ dataDCC2 = 0x2;
+
+ /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
+ if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE))
+ snd_hda_codec_write(codec,
+ nvhdmi_master_con_nid_7x,
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_1,
+ spdif->ctls & ~AC_DIG1_ENABLE & 0xff);
+
+ /* set the stream id */
+ snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
+ AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
+
+ /* set the stream format */
+ snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
+ AC_VERB_SET_STREAM_FORMAT, format);
+
+ /* turn on again (if needed) */
+ /* enable and set the channel status audio/data flag */
+ if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) {
+ snd_hda_codec_write(codec,
+ nvhdmi_master_con_nid_7x,
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_1,
+ spdif->ctls & 0xff);
+ snd_hda_codec_write(codec,
+ nvhdmi_master_con_nid_7x,
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (chs == 2)
+ channel_id = 0;
+ else
+ channel_id = i * 2;
+
+ /* turn off SPDIF once;
+ *otherwise the IEC958 bits won't be updated
+ */
+ if (codec->spdif_status_reset &&
+ (spdif->ctls & AC_DIG1_ENABLE))
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_1,
+ spdif->ctls & ~AC_DIG1_ENABLE & 0xff);
+ /* set the stream id */
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_CHANNEL_STREAMID,
+ (stream_tag << 4) | channel_id);
+ /* set the stream format */
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_STREAM_FORMAT,
+ format);
+ /* turn on again (if needed) */
+ /* enable and set the channel status audio/data flag */
+ if (codec->spdif_status_reset &&
+ (spdif->ctls & AC_DIG1_ENABLE)) {
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_1,
+ spdif->ctls & 0xff);
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
+ }
+ }
+
+ nvhdmi_8ch_7x_set_info_frame_parameters(codec, chs);
+
+ return 0;
+}
+
+static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ .nid = nvhdmi_master_con_nid_7x,
+ .rates = SUPPORTED_RATES,
+ .maxbps = SUPPORTED_MAXBPS,
+ .formats = SUPPORTED_FORMATS,
+ .ops = {
+ .open = snd_hda_hdmi_simple_pcm_open,
+ .close = nvhdmi_8ch_7x_pcm_close,
+ .prepare = nvhdmi_8ch_7x_pcm_prepare
+ },
+};
+
+static int nvhdmi_mcp_build_pcms(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_hdmi_simple_build_pcms(codec);
+ if (!err && spec->multiout.max_channels == 8) {
+ struct hda_pcm *info = get_pcm_rec(spec, 0);
+
+ info->own_chmap = true;
+ }
+ return err;
+}
+
+static int nvhdmi_mcp_build_controls(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hda_pcm *info;
+ struct snd_pcm_chmap *chmap;
+ int err;
+
+ err = snd_hda_hdmi_simple_build_controls(codec);
+ if (err < 0)
+ return err;
+
+ if (spec->multiout.max_channels != 8)
+ return 0;
+
+ /* add channel maps */
+ info = get_pcm_rec(spec, 0);
+ err = snd_pcm_add_chmap_ctls(info->pcm,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps, 8, 0, &chmap);
+ if (err < 0)
+ return err;
+ switch (codec->preset->vendor_id) {
+ case 0x10de0002:
+ case 0x10de0003:
+ case 0x10de0005:
+ case 0x10de0006:
+ chmap->channel_mask = (1U << 2) | (1U << 8);
+ break;
+ case 0x10de0007:
+ chmap->channel_mask = (1U << 2) | (1U << 6) | (1U << 8);
+ }
+ return 0;
+}
+
+static const unsigned int channels_2_6_8[] = {
+ 2, 6, 8
+};
+
+static const unsigned int channels_2_8[] = {
+ 2, 8
+};
+
+static const struct snd_pcm_hw_constraint_list hw_constraints_2_6_8_channels = {
+ .count = ARRAY_SIZE(channels_2_6_8),
+ .list = channels_2_6_8,
+ .mask = 0,
+};
+
+static const struct snd_pcm_hw_constraint_list hw_constraints_2_8_channels = {
+ .count = ARRAY_SIZE(channels_2_8),
+ .list = channels_2_8,
+ .mask = 0,
+};
+
+static int nvhdmi_mcp_probe(struct hda_codec *codec,
+ const struct hda_device_id *id)
+{
+ struct hdmi_spec *spec;
+ int err;
+
+ err = snd_hda_hdmi_simple_probe(codec, nvhdmi_master_con_nid_7x,
+ nvhdmi_master_pin_nid_7x);
+ if (err < 0)
+ return err;
+
+ /* override the PCM rates, etc, as the codec doesn't give full list */
+ spec = codec->spec;
+ spec->pcm_playback.rates = SUPPORTED_RATES;
+ spec->pcm_playback.maxbps = SUPPORTED_MAXBPS;
+ spec->pcm_playback.formats = SUPPORTED_FORMATS;
+ spec->nv_dp_workaround = true;
+
+ if (id->driver_data == MODEL_2CH)
+ return 0;
+
+ spec->multiout.max_channels = 8;
+ spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x;
+
+ switch (codec->preset->vendor_id) {
+ case 0x10de0002:
+ case 0x10de0003:
+ case 0x10de0005:
+ case 0x10de0006:
+ spec->hw_constraints_channels = &hw_constraints_2_8_channels;
+ break;
+ case 0x10de0007:
+ spec->hw_constraints_channels = &hw_constraints_2_6_8_channels;
+ break;
+ default:
+ break;
+ }
+
+ /* Initialize the audio infoframe channel mask and checksum to something
+ * valid
+ */
+ nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8);
+
+ return 0;
+}
+
+static const struct hda_codec_ops nvhdmi_mcp_codec_ops = {
+ .probe = nvhdmi_mcp_probe,
+ .remove = snd_hda_hdmi_simple_remove,
+ .build_pcms = nvhdmi_mcp_build_pcms,
+ .build_controls = nvhdmi_mcp_build_controls,
+ .init = nvhdmi_mcp_init,
+ .unsol_event = snd_hda_hdmi_simple_unsol_event,
+};
+
+static const struct hda_device_id snd_hda_id_nvhdmi_mcp[] = {
+ HDA_CODEC_ID_MODEL(0x10de0001, "MCP73 HDMI", MODEL_2CH),
+ HDA_CODEC_ID_MODEL(0x10de0002, "MCP77/78 HDMI", MODEL_8CH),
+ HDA_CODEC_ID_MODEL(0x10de0003, "MCP77/78 HDMI", MODEL_8CH),
+ HDA_CODEC_ID_MODEL(0x10de0004, "GPU 04 HDMI", MODEL_8CH),
+ HDA_CODEC_ID_MODEL(0x10de0005, "MCP77/78 HDMI", MODEL_8CH),
+ HDA_CODEC_ID_MODEL(0x10de0006, "MCP77/78 HDMI", MODEL_8CH),
+ HDA_CODEC_ID_MODEL(0x10de0007, "MCP79/7A HDMI", MODEL_8CH),
+ HDA_CODEC_ID_MODEL(0x10de0067, "MCP67 HDMI", MODEL_2CH),
+ HDA_CODEC_ID_MODEL(0x10de8001, "MCP73 HDMI", MODEL_2CH),
+ HDA_CODEC_ID_MODEL(0x10de8067, "MCP67/68 HDMI", MODEL_2CH),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_nvhdmi_mcp);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Legacy Nvidia HDMI HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI");
+
+static struct hda_codec_driver nvhdmi_mcp_driver = {
+ .id = snd_hda_id_nvhdmi_mcp,
+ .ops = &nvhdmi_mcp_codec_ops,
+};
+
+module_hda_codec_driver(nvhdmi_mcp_driver);
diff --git a/sound/hda/codecs/hdmi/nvhdmi.c b/sound/hda/codecs/hdmi/nvhdmi.c
new file mode 100644
index 000000000000..94671ad24b5e
--- /dev/null
+++ b/sound/hda/codecs/hdmi/nvhdmi.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Nvidia HDMI codec support
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/tlv.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hdmi_local.h"
+
+enum {
+ MODEL_GENERIC,
+ MODEL_LEGACY,
+};
+
+/*
+ * NVIDIA codecs ignore ASP mapping for 2ch - confirmed on:
+ * - 0x10de0015
+ * - 0x10de0040
+ */
+static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
+ struct hdac_cea_channel_speaker_allocation *cap, int channels)
+{
+ if (cap->ca_index == 0x00 && channels == 2)
+ return SNDRV_CTL_TLVT_CHMAP_FIXED;
+
+ /* If the speaker allocation matches the channel count, it is OK. */
+ if (cap->channels != channels)
+ return -1;
+
+ /* all channels are remappable freely */
+ return SNDRV_CTL_TLVT_CHMAP_VAR;
+}
+
+static int nvhdmi_chmap_validate(struct hdac_chmap *chmap,
+ int ca, int chs, unsigned char *map)
+{
+ if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR))
+ return -EINVAL;
+
+ return 0;
+}
+
+/* map from pin NID to port; port is 0-based */
+/* for Nvidia: assume widget NID starting from 4, with step 1 (4, 5, 6, ...) */
+static int nvhdmi_pin2port(void *audio_ptr, int pin_nid)
+{
+ return pin_nid - 4;
+}
+
+/* reverse-map from port to pin NID: see above */
+static int nvhdmi_port2pin(struct hda_codec *codec, int port)
+{
+ return port + 4;
+}
+
+static const struct drm_audio_component_audio_ops nvhdmi_audio_ops = {
+ .pin2port = nvhdmi_pin2port,
+ .pin_eld_notify = snd_hda_hdmi_acomp_pin_eld_notify,
+ .master_bind = snd_hda_hdmi_acomp_master_bind,
+ .master_unbind = snd_hda_hdmi_acomp_master_unbind,
+};
+
+static int probe_generic(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int err;
+
+ err = snd_hda_hdmi_generic_alloc(codec);
+ if (err < 0)
+ return err;
+ codec->dp_mst = true;
+
+ spec = codec->spec;
+
+ err = snd_hda_hdmi_parse_codec(codec);
+ if (err < 0) {
+ snd_hda_hdmi_generic_spec_free(codec);
+ return err;
+ }
+
+ snd_hda_hdmi_generic_init_per_pins(codec);
+
+ spec->dyn_pin_out = true;
+
+ spec->chmap.ops.chmap_cea_alloc_validate_get_type =
+ nvhdmi_chmap_cea_alloc_validate_get_type;
+ spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
+ spec->nv_dp_workaround = true;
+
+ codec->link_down_at_suspend = 1;
+
+ snd_hda_hdmi_acomp_init(codec, &nvhdmi_audio_ops, nvhdmi_port2pin);
+
+ return 0;
+}
+
+static int probe_legacy(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int err;
+
+ err = snd_hda_hdmi_generic_probe(codec);
+ if (err)
+ return err;
+
+ spec = codec->spec;
+ spec->dyn_pin_out = true;
+
+ spec->chmap.ops.chmap_cea_alloc_validate_get_type =
+ nvhdmi_chmap_cea_alloc_validate_get_type;
+ spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
+ spec->nv_dp_workaround = true;
+
+ codec->link_down_at_suspend = 1;
+
+ return 0;
+}
+
+static int nvhdmi_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ if (id->driver_data == MODEL_LEGACY)
+ return probe_legacy(codec);
+ else
+ return probe_generic(codec);
+}
+
+static const struct hda_codec_ops nvhdmi_codec_ops = {
+ .probe = nvhdmi_probe,
+ .remove = snd_hda_hdmi_generic_remove,
+ .init = snd_hda_hdmi_generic_init,
+ .build_pcms = snd_hda_hdmi_generic_build_pcms,
+ .build_controls = snd_hda_hdmi_generic_build_controls,
+ .unsol_event = snd_hda_hdmi_generic_unsol_event,
+ .suspend = snd_hda_hdmi_generic_suspend,
+ .resume = snd_hda_hdmi_generic_resume,
+};
+
+static const struct hda_device_id snd_hda_id_nvhdmi[] = {
+ HDA_CODEC_ID_MODEL(0x10de0008, "GPU 08 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0009, "GPU 09 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de000a, "GPU 0a HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de000b, "GPU 0b HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de000c, "MCP89 HDMI", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de000d, "GPU 0d HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0010, "GPU 10 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0011, "GPU 11 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0012, "GPU 12 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0013, "GPU 13 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0014, "GPU 14 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0015, "GPU 15 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0016, "GPU 16 HDMI/DP", MODEL_LEGACY),
+ /* 17 is known to be absent */
+ HDA_CODEC_ID_MODEL(0x10de0018, "GPU 18 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0019, "GPU 19 HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de001a, "GPU 1a HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de001b, "GPU 1b HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de001c, "GPU 1c HDMI/DP", MODEL_LEGACY),
+ HDA_CODEC_ID_MODEL(0x10de0040, "GPU 40 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0041, "GPU 41 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0042, "GPU 42 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0043, "GPU 43 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0044, "GPU 44 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0045, "GPU 45 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0050, "GPU 50 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0051, "GPU 51 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0052, "GPU 52 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0060, "GPU 60 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0061, "GPU 61 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0062, "GPU 62 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0070, "GPU 70 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0071, "GPU 71 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0072, "GPU 72 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0073, "GPU 73 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0074, "GPU 74 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0076, "GPU 76 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de007b, "GPU 7b HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de007c, "GPU 7c HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de007d, "GPU 7d HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de007e, "GPU 7e HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0080, "GPU 80 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0081, "GPU 81 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0082, "GPU 82 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0083, "GPU 83 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0084, "GPU 84 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0090, "GPU 90 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0091, "GPU 91 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0092, "GPU 92 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0093, "GPU 93 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0094, "GPU 94 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0095, "GPU 95 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0097, "GPU 97 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0098, "GPU 98 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de0099, "GPU 99 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de009a, "GPU 9a HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de009b, "GPU 9b HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de009c, "GPU 9c HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de009d, "GPU 9d HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de009e, "GPU 9e HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de009f, "GPU 9f HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00a0, "GPU a0 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00a1, "GPU a1 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00a3, "GPU a3 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00a4, "GPU a4 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00a5, "GPU a5 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00a6, "GPU a6 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00a7, "GPU a7 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00a8, "GPU a8 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00a9, "GPU a9 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00aa, "GPU aa HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00ab, "GPU ab HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00ad, "GPU ad HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00ae, "GPU ae HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00af, "GPU af HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00b0, "GPU b0 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00b1, "GPU b1 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00c0, "GPU c0 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00c1, "GPU c1 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00c3, "GPU c3 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00c4, "GPU c4 HDMI/DP", MODEL_GENERIC),
+ HDA_CODEC_ID_MODEL(0x10de00c5, "GPU c5 HDMI/DP", MODEL_GENERIC),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_nvhdmi);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Nvidia HDMI HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI");
+
+static struct hda_codec_driver nvhdmi_driver = {
+ .id = snd_hda_id_nvhdmi,
+ .ops = &nvhdmi_codec_ops,
+};
+
+module_hda_codec_driver(nvhdmi_driver);
diff --git a/sound/hda/codecs/hdmi/simplehdmi.c b/sound/hda/codecs/hdmi/simplehdmi.c
new file mode 100644
index 000000000000..193c8dc882af
--- /dev/null
+++ b/sound/hda/codecs/hdmi/simplehdmi.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Non-generic simple HDMI codec support
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include "hdmi_local.h"
+#include "hda_jack.h"
+
+int snd_hda_hdmi_simple_build_pcms(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hda_pcm *info;
+ unsigned int chans;
+ struct hda_pcm_stream *pstr;
+ struct hdmi_spec_per_cvt *per_cvt;
+
+ per_cvt = get_cvt(spec, 0);
+ chans = get_wcaps(codec, per_cvt->cvt_nid);
+ chans = get_wcaps_channels(chans);
+
+ info = snd_hda_codec_pcm_new(codec, "HDMI 0");
+ if (!info)
+ return -ENOMEM;
+ spec->pcm_rec[0].pcm = info;
+ info->pcm_type = HDA_PCM_TYPE_HDMI;
+ pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
+ *pstr = spec->pcm_playback;
+ pstr->nid = per_cvt->cvt_nid;
+ if (pstr->channels_max <= 2 && chans && chans <= 16)
+ pstr->channels_max = chans;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_build_pcms, "SND_HDA_CODEC_HDMI");
+
+/* unsolicited event for jack sensing */
+void snd_hda_hdmi_simple_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ snd_hda_jack_set_dirty_all(codec);
+ snd_hda_jack_report_sync(codec);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_unsol_event, "SND_HDA_CODEC_HDMI");
+
+static void free_hdmi_jack_priv(struct snd_jack *jack)
+{
+ struct hdmi_pcm *pcm = jack->private_data;
+
+ pcm->jack = NULL;
+}
+
+static int simple_hdmi_build_jack(struct hda_codec *codec)
+{
+ char hdmi_str[32] = "HDMI/DP";
+ struct hdmi_spec *spec = codec->spec;
+ struct snd_jack *jack;
+ struct hdmi_pcm *pcmp = get_hdmi_pcm(spec, 0);
+ int pcmdev = pcmp->pcm->device;
+ int err;
+
+ if (pcmdev > 0)
+ sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
+
+ err = snd_jack_new(codec->card, hdmi_str, SND_JACK_AVOUT, &jack,
+ true, false);
+ if (err < 0)
+ return err;
+
+ pcmp->jack = jack;
+ jack->private_data = pcmp;
+ jack->private_free = free_hdmi_jack_priv;
+ return 0;
+}
+
+int snd_hda_hdmi_simple_build_controls(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ int err;
+
+ per_cvt = get_cvt(spec, 0);
+ err = snd_hda_create_dig_out_ctls(codec, per_cvt->cvt_nid,
+ per_cvt->cvt_nid,
+ HDA_PCM_TYPE_HDMI);
+ if (err < 0)
+ return err;
+ return simple_hdmi_build_jack(codec);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_build_controls, "SND_HDA_CODEC_HDMI");
+
+int snd_hda_hdmi_simple_init(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, 0);
+ hda_nid_t pin = per_pin->pin_nid;
+
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ /* some codecs require to unmute the pin */
+ if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+ snd_hda_jack_detect_enable(codec, pin, per_pin->dev_id);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_init, "SND_HDA_CODEC_HDMI");
+
+void snd_hda_hdmi_simple_remove(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ snd_array_free(&spec->pins);
+ snd_array_free(&spec->cvts);
+ kfree(spec);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_remove, "SND_HDA_CODEC_HDMI");
+
+int snd_hda_hdmi_simple_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ if (spec->hw_constraints_channels) {
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ spec->hw_constraints_channels);
+ } else {
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+ }
+
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_pcm_open, "SND_HDA_CODEC_HDMI");
+
+static int simple_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static const struct hda_pcm_stream simple_pcm_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .open = snd_hda_hdmi_simple_pcm_open,
+ .close = simple_playback_pcm_close,
+ .prepare = simple_playback_pcm_prepare
+ },
+};
+
+int snd_hda_hdmi_simple_probe(struct hda_codec *codec,
+ hda_nid_t cvt_nid, hda_nid_t pin_nid)
+{
+ struct hdmi_spec *spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ struct hdmi_spec_per_pin *per_pin;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+
+ spec->codec = codec;
+ codec->spec = spec;
+ snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), 1);
+ snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), 1);
+
+ spec->multiout.num_dacs = 0; /* no analog */
+ spec->multiout.max_channels = 2;
+ spec->multiout.dig_out_nid = cvt_nid;
+ spec->num_cvts = 1;
+ spec->num_pins = 1;
+ per_pin = snd_array_new(&spec->pins);
+ per_cvt = snd_array_new(&spec->cvts);
+ if (!per_pin || !per_cvt) {
+ snd_hda_hdmi_simple_remove(codec);
+ return -ENOMEM;
+ }
+ per_cvt->cvt_nid = cvt_nid;
+ per_pin->pin_nid = pin_nid;
+ spec->pcm_playback = simple_pcm_playback;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(snd_hda_hdmi_simple_probe, "SND_HDA_CODEC_HDMI");
+
+/*
+ * driver entries
+ */
+
+enum { MODEL_VIA };
+
+/* VIA HDMI Implementation */
+#define VIAHDMI_CVT_NID 0x02 /* audio converter1 */
+#define VIAHDMI_PIN_NID 0x03 /* HDMI output pin1 */
+
+static int simplehdmi_probe(struct hda_codec *codec,
+ const struct hda_device_id *id)
+{
+ switch (id->driver_data) {
+ case MODEL_VIA:
+ return snd_hda_hdmi_simple_probe(codec, VIAHDMI_CVT_NID,
+ VIAHDMI_PIN_NID);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct hda_codec_ops simplehdmi_codec_ops = {
+ .probe = simplehdmi_probe,
+ .remove = snd_hda_hdmi_simple_remove,
+ .build_controls = snd_hda_hdmi_simple_build_controls,
+ .build_pcms = snd_hda_hdmi_simple_build_pcms,
+ .init = snd_hda_hdmi_simple_init,
+ .unsol_event = snd_hda_hdmi_simple_unsol_event,
+};
+
+static const struct hda_device_id snd_hda_id_simplehdmi[] = {
+ HDA_CODEC_ID_MODEL(0x11069f80, "VX900 HDMI/DP", MODEL_VIA),
+ HDA_CODEC_ID_MODEL(0x11069f81, "VX900 HDMI/DP", MODEL_VIA),
+ {} /* terminator */
+};
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Simple HDMI HD-audio codec support");
+
+static struct hda_codec_driver simplehdmi_driver = {
+ .id = snd_hda_id_simplehdmi,
+ .ops = &simplehdmi_codec_ops,
+};
+
+module_hda_codec_driver(simplehdmi_driver);
diff --git a/sound/hda/codecs/hdmi/tegrahdmi.c b/sound/hda/codecs/hdmi/tegrahdmi.c
new file mode 100644
index 000000000000..5f6fe31aa202
--- /dev/null
+++ b/sound/hda/codecs/hdmi/tegrahdmi.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Nvidia Tegra HDMI codec support
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/tlv.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hdmi_local.h"
+
+enum {
+ MODEL_TEGRA,
+ MODEL_TEGRA234,
+};
+
+/*
+ * The HDA codec on NVIDIA Tegra contains two scratch registers that are
+ * accessed using vendor-defined verbs. These registers can be used for
+ * interoperability between the HDA and HDMI drivers.
+ */
+
+/* Audio Function Group node */
+#define NVIDIA_AFG_NID 0x01
+
+/*
+ * The SCRATCH0 register is used to notify the HDMI codec of changes in audio
+ * format. On Tegra, bit 31 is used as a trigger that causes an interrupt to
+ * be raised in the HDMI codec. The remainder of the bits is arbitrary. This
+ * implementation stores the HDA format (see AC_FMT_*) in bits [15:0] and an
+ * additional bit (at position 30) to signal the validity of the format.
+ *
+ * | 31 | 30 | 29 16 | 15 0 |
+ * +---------+-------+--------+--------+
+ * | TRIGGER | VALID | UNUSED | FORMAT |
+ * +-----------------------------------|
+ *
+ * Note that for the trigger bit to take effect it needs to change value
+ * (i.e. it needs to be toggled). The trigger bit is not applicable from
+ * TEGRA234 chip onwards, as new verb id 0xf80 will be used for interrupt
+ * trigger to hdmi.
+ */
+#define NVIDIA_SET_HOST_INTR 0xf80
+#define NVIDIA_GET_SCRATCH0 0xfa6
+#define NVIDIA_SET_SCRATCH0_BYTE0 0xfa7
+#define NVIDIA_SET_SCRATCH0_BYTE1 0xfa8
+#define NVIDIA_SET_SCRATCH0_BYTE2 0xfa9
+#define NVIDIA_SET_SCRATCH0_BYTE3 0xfaa
+#define NVIDIA_SCRATCH_TRIGGER (1 << 7)
+#define NVIDIA_SCRATCH_VALID (1 << 6)
+
+#define NVIDIA_GET_SCRATCH1 0xfab
+#define NVIDIA_SET_SCRATCH1_BYTE0 0xfac
+#define NVIDIA_SET_SCRATCH1_BYTE1 0xfad
+#define NVIDIA_SET_SCRATCH1_BYTE2 0xfae
+#define NVIDIA_SET_SCRATCH1_BYTE3 0xfaf
+
+/*
+ * The format parameter is the HDA audio format (see AC_FMT_*). If set to 0,
+ * the format is invalidated so that the HDMI codec can be disabled.
+ */
+static void tegra_hdmi_set_format(struct hda_codec *codec,
+ hda_nid_t cvt_nid,
+ unsigned int format)
+{
+ unsigned int value;
+ unsigned int nid = NVIDIA_AFG_NID;
+ struct hdmi_spec *spec = codec->spec;
+
+ /*
+ * Tegra HDA codec design from TEGRA234 chip onwards support DP MST.
+ * This resulted in moving scratch registers from audio function
+ * group to converter widget context. So CVT NID should be used for
+ * scratch register read/write for DP MST supported Tegra HDA codec.
+ */
+ if (codec->dp_mst)
+ nid = cvt_nid;
+
+ /* bits [31:30] contain the trigger and valid bits */
+ value = snd_hda_codec_read(codec, nid, 0,
+ NVIDIA_GET_SCRATCH0, 0);
+ value = (value >> 24) & 0xff;
+
+ /* bits [15:0] are used to store the HDA format */
+ snd_hda_codec_write(codec, nid, 0,
+ NVIDIA_SET_SCRATCH0_BYTE0,
+ (format >> 0) & 0xff);
+ snd_hda_codec_write(codec, nid, 0,
+ NVIDIA_SET_SCRATCH0_BYTE1,
+ (format >> 8) & 0xff);
+
+ /* bits [16:24] are unused */
+ snd_hda_codec_write(codec, nid, 0,
+ NVIDIA_SET_SCRATCH0_BYTE2, 0);
+
+ /*
+ * Bit 30 signals that the data is valid and hence that HDMI audio can
+ * be enabled.
+ */
+ if (format == 0)
+ value &= ~NVIDIA_SCRATCH_VALID;
+ else
+ value |= NVIDIA_SCRATCH_VALID;
+
+ if (spec->hdmi_intr_trig_ctrl) {
+ /*
+ * For Tegra HDA Codec design from TEGRA234 onwards, the
+ * Interrupt to hdmi driver is triggered by writing
+ * non-zero values to verb 0xF80 instead of 31st bit of
+ * scratch register.
+ */
+ snd_hda_codec_write(codec, nid, 0,
+ NVIDIA_SET_SCRATCH0_BYTE3, value);
+ snd_hda_codec_write(codec, nid, 0,
+ NVIDIA_SET_HOST_INTR, 0x1);
+ } else {
+ /*
+ * Whenever the 31st trigger bit is toggled, an interrupt is raised
+ * in the HDMI codec. The HDMI driver will use that as trigger
+ * to update its configuration.
+ */
+ value ^= NVIDIA_SCRATCH_TRIGGER;
+
+ snd_hda_codec_write(codec, nid, 0,
+ NVIDIA_SET_SCRATCH0_BYTE3, value);
+ }
+}
+
+static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ int err;
+
+ err = snd_hda_hdmi_generic_pcm_prepare(hinfo, codec, stream_tag,
+ format, substream);
+ if (err < 0)
+ return err;
+
+ /* notify the HDMI codec of the format change */
+ tegra_hdmi_set_format(codec, hinfo->nid, format);
+
+ return 0;
+}
+
+static int tegra_hdmi_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ /* invalidate the format in the HDMI codec */
+ tegra_hdmi_set_format(codec, hinfo->nid, 0);
+
+ return snd_hda_hdmi_generic_pcm_cleanup(hinfo, codec, substream);
+}
+
+static struct hda_pcm *hda_find_pcm_by_type(struct hda_codec *codec, int type)
+{
+ struct hdmi_spec *spec = codec->spec;
+ unsigned int i;
+
+ for (i = 0; i < spec->num_pins; i++) {
+ struct hda_pcm *pcm = get_pcm_rec(spec, i);
+
+ if (pcm->pcm_type == type)
+ return pcm;
+ }
+
+ return NULL;
+}
+
+static int tegra_hdmi_build_pcms(struct hda_codec *codec)
+{
+ struct hda_pcm_stream *stream;
+ struct hda_pcm *pcm;
+ int err;
+
+ err = snd_hda_hdmi_generic_build_pcms(codec);
+ if (err < 0)
+ return err;
+
+ pcm = hda_find_pcm_by_type(codec, HDA_PCM_TYPE_HDMI);
+ if (!pcm)
+ return -ENODEV;
+
+ /*
+ * Override ->prepare() and ->cleanup() operations to notify the HDMI
+ * codec about format changes.
+ */
+ stream = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK];
+ stream->ops.prepare = tegra_hdmi_pcm_prepare;
+ stream->ops.cleanup = tegra_hdmi_pcm_cleanup;
+
+ return 0;
+}
+
+/*
+ * NVIDIA codecs ignore ASP mapping for 2ch - confirmed on:
+ * - 0x10de0015
+ * - 0x10de0040
+ */
+static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
+ struct hdac_cea_channel_speaker_allocation *cap, int channels)
+{
+ if (cap->ca_index == 0x00 && channels == 2)
+ return SNDRV_CTL_TLVT_CHMAP_FIXED;
+
+ /* If the speaker allocation matches the channel count, it is OK. */
+ if (cap->channels != channels)
+ return -1;
+
+ /* all channels are remappable freely */
+ return SNDRV_CTL_TLVT_CHMAP_VAR;
+}
+
+static int nvhdmi_chmap_validate(struct hdac_chmap *chmap,
+ int ca, int chs, unsigned char *map)
+{
+ if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int tegra_hdmi_init(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int i, err;
+
+ err = snd_hda_hdmi_parse_codec(codec);
+ if (err < 0) {
+ snd_hda_hdmi_generic_spec_free(codec);
+ return err;
+ }
+
+ for (i = 0; i < spec->num_cvts; i++)
+ snd_hda_codec_write(codec, spec->cvt_nids[i], 0,
+ AC_VERB_SET_DIGI_CONVERT_1,
+ AC_DIG1_ENABLE);
+
+ snd_hda_hdmi_generic_init_per_pins(codec);
+
+ codec->depop_delay = 10;
+ spec->chmap.ops.chmap_cea_alloc_validate_get_type =
+ nvhdmi_chmap_cea_alloc_validate_get_type;
+ spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
+
+ spec->chmap.ops.chmap_cea_alloc_validate_get_type =
+ nvhdmi_chmap_cea_alloc_validate_get_type;
+ spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
+ spec->nv_dp_workaround = true;
+
+ return 0;
+}
+
+static int tegrahdmi_probe(struct hda_codec *codec,
+ const struct hda_device_id *id)
+{
+ struct hdmi_spec *spec;
+ int err;
+
+ err = snd_hda_hdmi_generic_alloc(codec);
+ if (err < 0)
+ return err;
+
+ if (id->driver_data == MODEL_TEGRA234) {
+ codec->dp_mst = true;
+ spec = codec->spec;
+ spec->dyn_pin_out = true;
+ spec->hdmi_intr_trig_ctrl = true;
+ }
+
+ return tegra_hdmi_init(codec);
+}
+
+static const struct hda_codec_ops tegrahdmi_codec_ops = {
+ .probe = tegrahdmi_probe,
+ .remove = snd_hda_hdmi_generic_remove,
+ .init = snd_hda_hdmi_generic_init,
+ .build_pcms = tegra_hdmi_build_pcms,
+ .build_controls = snd_hda_hdmi_generic_build_controls,
+ .unsol_event = snd_hda_hdmi_generic_unsol_event,
+ .suspend = snd_hda_hdmi_generic_suspend,
+ .resume = snd_hda_hdmi_generic_resume,
+};
+
+static const struct hda_device_id snd_hda_id_tegrahdmi[] = {
+ HDA_CODEC_ID_MODEL(0x10de0020, "Tegra30 HDMI", MODEL_TEGRA),
+ HDA_CODEC_ID_MODEL(0x10de0022, "Tegra114 HDMI", MODEL_TEGRA),
+ HDA_CODEC_ID_MODEL(0x10de0028, "Tegra124 HDMI", MODEL_TEGRA),
+ HDA_CODEC_ID_MODEL(0x10de0029, "Tegra210 HDMI/DP", MODEL_TEGRA),
+ HDA_CODEC_ID_MODEL(0x10de002d, "Tegra186 HDMI/DP0", MODEL_TEGRA),
+ HDA_CODEC_ID_MODEL(0x10de002e, "Tegra186 HDMI/DP1", MODEL_TEGRA),
+ HDA_CODEC_ID_MODEL(0x10de002f, "Tegra194 HDMI/DP2", MODEL_TEGRA),
+ HDA_CODEC_ID_MODEL(0x10de0030, "Tegra194 HDMI/DP3", MODEL_TEGRA),
+ HDA_CODEC_ID_MODEL(0x10de0031, "Tegra234 HDMI/DP", MODEL_TEGRA234),
+ HDA_CODEC_ID_MODEL(0x10de0033, "SoC 33 HDMI/DP", MODEL_TEGRA234),
+ HDA_CODEC_ID_MODEL(0x10de0034, "Tegra264 HDMI/DP", MODEL_TEGRA234),
+ HDA_CODEC_ID_MODEL(0x10de0035, "SoC 35 HDMI/DP", MODEL_TEGRA234),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_tegrahdmi);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Nvidia Tegra HDMI HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI");
+
+static struct hda_codec_driver tegrahdmi_driver = {
+ .id = snd_hda_id_tegrahdmi,
+ .ops = &tegrahdmi_codec_ops,
+};
+
+module_hda_codec_driver(tegrahdmi_driver);
diff --git a/sound/hda/codecs/helpers/hp_x360.c b/sound/hda/codecs/helpers/hp_x360.c
new file mode 100644
index 000000000000..969542c57358
--- /dev/null
+++ b/sound/hda/codecs/helpers/hp_x360.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Fixes for HP X360 laptops with top B&O speakers
+ * to be included from codec driver
+ */
+
+static void alc295_fixup_hp_top_speakers(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x17, 0x90170110 },
+ { }
+ };
+ static const struct coef_fw alc295_hp_speakers_coefs[] = {
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0000), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003f), WRITE_COEF(0x28, 0x1000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0004), WRITE_COEF(0x28, 0x0600), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x0006), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0xc0c0), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0008), WRITE_COEF(0x28, 0xb000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x002e), WRITE_COEF(0x28, 0x0800), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x00c1), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x0320), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0039), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003b), WRITE_COEF(0x28, 0xffff), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003c), WRITE_COEF(0x28, 0xffd0), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003a), WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0080), WRITE_COEF(0x28, 0x0880), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003a), WRITE_COEF(0x28, 0x0dfe), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0018), WRITE_COEF(0x28, 0x0219), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x005d), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x9142), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c0), WRITE_COEF(0x28, 0x01ce), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c1), WRITE_COEF(0x28, 0xed0c), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c2), WRITE_COEF(0x28, 0x1c00), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c3), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c4), WRITE_COEF(0x28, 0x0200), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c5), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c6), WRITE_COEF(0x28, 0x0399), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c7), WRITE_COEF(0x28, 0x2330), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c8), WRITE_COEF(0x28, 0x1e5d), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00c9), WRITE_COEF(0x28, 0x6eff), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00ca), WRITE_COEF(0x28, 0x01c0), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cb), WRITE_COEF(0x28, 0xed0c), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cc), WRITE_COEF(0x28, 0x1c00), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cd), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00ce), WRITE_COEF(0x28, 0x0200), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00cf), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d0), WRITE_COEF(0x28, 0x0399), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d1), WRITE_COEF(0x28, 0x2330), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d2), WRITE_COEF(0x28, 0x1e5d), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x00d3), WRITE_COEF(0x28, 0x6eff), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0062), WRITE_COEF(0x28, 0x8000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0063), WRITE_COEF(0x28, 0x5f5f), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0064), WRITE_COEF(0x28, 0x1000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0065), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0066), WRITE_COEF(0x28, 0x4004), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0067), WRITE_COEF(0x28, 0x0802), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0068), WRITE_COEF(0x28, 0x890f), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0069), WRITE_COEF(0x28, 0xe021), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0070), WRITE_COEF(0x28, 0x8012), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0071), WRITE_COEF(0x28, 0x3450), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0072), WRITE_COEF(0x28, 0x0123), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0073), WRITE_COEF(0x28, 0x4543), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0074), WRITE_COEF(0x28, 0x2100), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0075), WRITE_COEF(0x28, 0x4321), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0076), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0050), WRITE_COEF(0x28, 0x8200), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003a), WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0051), WRITE_COEF(0x28, 0x0707), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0052), WRITE_COEF(0x28, 0x4090), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x0090), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x721f), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0012), WRITE_COEF(0x28, 0xebeb), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x009e), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0060), WRITE_COEF(0x28, 0x2213), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006a), WRITE_COEF(0x28, 0x0006), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x006c), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x003f), WRITE_COEF(0x28, 0x3000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0004), WRITE_COEF(0x28, 0x0500), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0040), WRITE_COEF(0x28, 0x800c), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0046), WRITE_COEF(0x28, 0xc22e), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x004b), WRITE_COEF(0x28, 0x0000), WRITE_COEF(0x29, 0xb024),
+ WRITE_COEF(0x24, 0x0012), WRITE_COEF(0x26, 0x0050), WRITE_COEF(0x28, 0x82ec), WRITE_COEF(0x29, 0xb024),
+ };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ alc295_fixup_disable_dac3(codec, fix, action);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ alc_process_coef_fw(codec, alc295_hp_speakers_coefs);
+ break;
+ }
+}
diff --git a/sound/hda/codecs/helpers/ideapad_hotkey_led.c b/sound/hda/codecs/helpers/ideapad_hotkey_led.c
new file mode 100644
index 000000000000..c10d97964d49
--- /dev/null
+++ b/sound/hda/codecs/helpers/ideapad_hotkey_led.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Ideapad helper functions for Lenovo Ideapad LED control,
+ * It should be included from codec driver.
+ */
+
+#if IS_ENABLED(CONFIG_IDEAPAD_LAPTOP)
+
+#include <linux/acpi.h>
+#include <linux/leds.h>
+
+static bool is_ideapad(struct hda_codec *codec)
+{
+ return (codec->core.subsystem_id >> 16 == 0x17aa) &&
+ (acpi_dev_found("LHK2019") || acpi_dev_found("VPC2004"));
+}
+
+static void hda_fixup_ideapad_acpi(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ if (!is_ideapad(codec))
+ return;
+ snd_hda_gen_add_mute_led_cdev(codec, NULL);
+ snd_hda_gen_add_micmute_led_cdev(codec, NULL);
+ }
+}
+
+#else /* CONFIG_IDEAPAD_LAPTOP */
+
+static void hda_fixup_ideapad_acpi(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+}
+
+#endif /* CONFIG_IDEAPAD_LAPTOP */
diff --git a/sound/hda/codecs/helpers/ideapad_s740.c b/sound/hda/codecs/helpers/ideapad_s740.c
new file mode 100644
index 000000000000..564b9086e52d
--- /dev/null
+++ b/sound/hda/codecs/helpers/ideapad_s740.c
@@ -0,0 +1,492 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Fixes for Lenovo Ideapad S740, to be included from codec driver */
+
+static const struct hda_verb alc285_ideapad_s740_coefs[] = {
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x10 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0320 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0041 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0041 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x007f },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x007f },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x003c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0011 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x003c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0011 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x000c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001a },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x000c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001a },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x000f },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0042 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x000f },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0042 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0040 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0040 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0003 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0009 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0003 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0009 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x004c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x004c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001d },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x004e },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001d },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x004e },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001b },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001b },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0019 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0025 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0019 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0025 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0018 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0037 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0018 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0037 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001a },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0040 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001a },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0040 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0016 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0076 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0016 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0076 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0017 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0017 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0007 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0086 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0007 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0086 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0042 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0042 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x007f },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x007f },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x003c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0011 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x003c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0011 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x000c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x002a },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x000c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x002a },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x000f },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0046 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x000f },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0046 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0044 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0044 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0003 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0009 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0003 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0009 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x004c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x004c },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001b },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001b },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0019 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0025 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0019 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0025 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0018 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0037 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0018 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0037 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001a },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0040 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x001a },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0040 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0016 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0076 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0016 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0076 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0017 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0017 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0010 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0015 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0007 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0086 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0007 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0086 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0001 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x29 },
+{ 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0002 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+{ 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+{}
+};
+
+static void alc285_fixup_ideapad_s740_coef(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_add_verbs(codec, alc285_ideapad_s740_coefs);
+ break;
+ }
+}
diff --git a/sound/hda/codecs/helpers/thinkpad.c b/sound/hda/codecs/helpers/thinkpad.c
new file mode 100644
index 000000000000..de4d8deed102
--- /dev/null
+++ b/sound/hda/codecs/helpers/thinkpad.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Helper functions for Thinkpad LED control;
+ * to be included from codec driver
+ */
+
+#if IS_ENABLED(CONFIG_THINKPAD_ACPI)
+
+#include <linux/acpi.h>
+#include <linux/leds.h>
+
+static bool is_thinkpad(struct hda_codec *codec)
+{
+ return (codec->core.subsystem_id >> 16 == 0x17aa) &&
+ (acpi_dev_found("LEN0068") || acpi_dev_found("LEN0268") ||
+ acpi_dev_found("IBM0068"));
+}
+
+static void hda_fixup_thinkpad_acpi(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ if (!is_thinkpad(codec))
+ return;
+ snd_hda_gen_add_mute_led_cdev(codec, NULL);
+ snd_hda_gen_add_micmute_led_cdev(codec, NULL);
+ }
+}
+
+#else /* CONFIG_THINKPAD_ACPI */
+
+static void hda_fixup_thinkpad_acpi(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+}
+
+#endif /* CONFIG_THINKPAD_ACPI */
diff --git a/sound/hda/codecs/realtek/Kconfig b/sound/hda/codecs/realtek/Kconfig
new file mode 100644
index 000000000000..cdc6d9509a01
--- /dev/null
+++ b/sound/hda/codecs/realtek/Kconfig
@@ -0,0 +1,104 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menuconfig SND_HDA_CODEC_REALTEK
+ tristate "Realtek HD-audio codec support"
+ help
+ Say Y or M here to include Realtek HD-audio codec support.
+
+ This will enable all Realtek HD-audio codec drivers as default,
+ but you can enable/disable each codec driver individually, too
+ (only when CONFIG_EXPERT is set).
+
+if SND_HDA_CODEC_REALTEK
+
+config SND_HDA_CODEC_REALTEK_LIB
+ tristate
+ select SND_HDA_GENERIC
+ select SND_HDA_GENERIC_LEDS
+ select SND_HDA_SCODEC_COMPONENT
+
+config SND_HDA_CODEC_ALC260
+ tristate "Build Realtek ALC260 HD-audio codec support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC260 HD-audio codec support
+
+config SND_HDA_CODEC_ALC262
+ tristate "Build Realtek ALC262 HD-audio codec support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC262 HD-audio codec support
+
+config SND_HDA_CODEC_ALC268
+ tristate "Build Realtek ALC268 HD-audio codec support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC268 and compatible HD-audio
+ codec support
+
+config SND_HDA_CODEC_ALC269
+ tristate "Build Realtek ALC269 HD-audio codecs support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC269 and compatible HD-audio
+ codec support
+
+config SND_HDA_CODEC_ALC662
+ tristate "Build Realtek ALC662 HD-audio codecs support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC662 and compatible HD-audio
+ codec support
+
+config SND_HDA_CODEC_ALC680
+ tristate "Build Realtek ALC680 HD-audio codecs support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC680 HD-audio codec support
+
+config SND_HDA_CODEC_ALC861
+ tristate "Build Realtek ALC861 HD-audio codecs support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC861 HD-audio codec support
+
+config SND_HDA_CODEC_ALC861VD
+ tristate "Build Realtek ALC861-VD HD-audio codecs support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC861-VD HD-audio codec support
+
+config SND_HDA_CODEC_ALC880
+ tristate "Build Realtek ALC880 HD-audio codecs support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC880 HD-audio codec support
+
+config SND_HDA_CODEC_ALC882
+ tristate "Build Realtek ALC882 HD-audio codecs support" if EXPERT
+ depends on INPUT
+ select SND_HDA_CODEC_REALTEK_LIB
+ default y
+ help
+ Say Y or M here to include Realtek ALC882 and compatible HD-audio
+ codec support
+
+endif
diff --git a/sound/hda/codecs/realtek/Makefile b/sound/hda/codecs/realtek/Makefile
new file mode 100644
index 000000000000..c6ee4e526a40
--- /dev/null
+++ b/sound/hda/codecs/realtek/Makefile
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0
+subdir-ccflags-y += -I$(src)/../../common
+
+snd-hda-codec-realtek-lib-y := realtek.o
+snd-hda-codec-alc260-y := alc260.o
+snd-hda-codec-alc262-y := alc262.o
+snd-hda-codec-alc268-y := alc268.o
+snd-hda-codec-alc269-y := alc269.o
+snd-hda-codec-alc662-y := alc662.o
+snd-hda-codec-alc680-y := alc680.o
+snd-hda-codec-alc861-y := alc861.o
+snd-hda-codec-alc861vd-y := alc861vd.o
+snd-hda-codec-alc880-y := alc880.o
+snd-hda-codec-alc882-y := alc882.o
+
+obj-$(CONFIG_SND_HDA_CODEC_REALTEK_LIB) += snd-hda-codec-realtek-lib.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC260) += snd-hda-codec-alc260.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC262) += snd-hda-codec-alc262.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC268) += snd-hda-codec-alc268.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC269) += snd-hda-codec-alc269.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC662) += snd-hda-codec-alc662.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC680) += snd-hda-codec-alc680.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC861) += snd-hda-codec-alc861.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC861VD) += snd-hda-codec-alc861vd.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC880) += snd-hda-codec-alc880.o
+obj-$(CONFIG_SND_HDA_CODEC_ALC882) += snd-hda-codec-alc882.o
diff --git a/sound/hda/codecs/realtek/alc260.c b/sound/hda/codecs/realtek/alc260.c
new file mode 100644
index 000000000000..8bd47079dccb
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc260.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek ALC260 codec
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+static int alc260_parse_auto_config(struct hda_codec *codec)
+{
+ static const hda_nid_t alc260_ignore[] = { 0x17, 0 };
+ static const hda_nid_t alc260_ssids[] = { 0x10, 0x15, 0x0f, 0 };
+ return alc_parse_auto_config(codec, alc260_ignore, alc260_ssids);
+}
+
+/*
+ * Pin config fixes
+ */
+enum {
+ ALC260_FIXUP_HP_DC5750,
+ ALC260_FIXUP_HP_PIN_0F,
+ ALC260_FIXUP_COEF,
+ ALC260_FIXUP_GPIO1,
+ ALC260_FIXUP_GPIO1_TOGGLE,
+ ALC260_FIXUP_REPLACER,
+ ALC260_FIXUP_HP_B1900,
+ ALC260_FIXUP_KN1,
+ ALC260_FIXUP_FSC_S7020,
+ ALC260_FIXUP_FSC_S7020_JWSE,
+ ALC260_FIXUP_VAIO_PINS,
+};
+
+static void alc260_gpio1_automute(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ alc_update_gpio_data(codec, 0x01, spec->gen.hp_jack_present);
+}
+
+static void alc260_fixup_gpio1_toggle(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ /* although the machine has only one output pin, we need to
+ * toggle GPIO1 according to the jack state
+ */
+ spec->gen.automute_hook = alc260_gpio1_automute;
+ spec->gen.detect_hp = 1;
+ spec->gen.automute_speaker = 1;
+ spec->gen.autocfg.hp_pins[0] = 0x0f; /* copy it for automute */
+ snd_hda_jack_detect_enable_callback(codec, 0x0f,
+ snd_hda_gen_hp_automute);
+ alc_setup_gpio(codec, 0x01);
+ }
+}
+
+static void alc260_fixup_kn1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x0f, 0x02214000 }, /* HP/speaker */
+ { 0x12, 0x90a60160 }, /* int mic */
+ { 0x13, 0x02a19000 }, /* ext mic */
+ { 0x18, 0x01446000 }, /* SPDIF out */
+ /* disable bogus I/O pins */
+ { 0x10, 0x411111f0 },
+ { 0x11, 0x411111f0 },
+ { 0x14, 0x411111f0 },
+ { 0x15, 0x411111f0 },
+ { 0x16, 0x411111f0 },
+ { 0x17, 0x411111f0 },
+ { 0x19, 0x411111f0 },
+ { }
+ };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ spec->init_amp = ALC_INIT_NONE;
+ break;
+ }
+}
+
+static void alc260_fixup_fsc_s7020(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->init_amp = ALC_INIT_NONE;
+}
+
+static void alc260_fixup_fsc_s7020_jwse(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.add_jack_modes = 1;
+ spec->gen.hp_mic = 1;
+ }
+}
+
+static const struct hda_fixup alc260_fixups[] = {
+ [ALC260_FIXUP_HP_DC5750] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x11, 0x90130110 }, /* speaker */
+ { }
+ }
+ },
+ [ALC260_FIXUP_HP_PIN_0F] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x0f, 0x01214000 }, /* HP */
+ { }
+ }
+ },
+ [ALC260_FIXUP_COEF] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x1a, AC_VERB_SET_PROC_COEF, 0x3040 },
+ { }
+ },
+ },
+ [ALC260_FIXUP_GPIO1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_gpio1,
+ },
+ [ALC260_FIXUP_GPIO1_TOGGLE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_gpio1_toggle,
+ .chained = true,
+ .chain_id = ALC260_FIXUP_HP_PIN_0F,
+ },
+ [ALC260_FIXUP_REPLACER] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x1a, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x1a, AC_VERB_SET_PROC_COEF, 0x3050 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC260_FIXUP_GPIO1_TOGGLE,
+ },
+ [ALC260_FIXUP_HP_B1900] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_gpio1_toggle,
+ .chained = true,
+ .chain_id = ALC260_FIXUP_COEF,
+ },
+ [ALC260_FIXUP_KN1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_kn1,
+ },
+ [ALC260_FIXUP_FSC_S7020] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_fsc_s7020,
+ },
+ [ALC260_FIXUP_FSC_S7020_JWSE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_fsc_s7020_jwse,
+ .chained = true,
+ .chain_id = ALC260_FIXUP_FSC_S7020,
+ },
+ [ALC260_FIXUP_VAIO_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* Pin configs are missing completely on some VAIOs */
+ { 0x0f, 0x01211020 },
+ { 0x10, 0x0001003f },
+ { 0x11, 0x411111f0 },
+ { 0x12, 0x01a15930 },
+ { 0x13, 0x411111f0 },
+ { 0x14, 0x411111f0 },
+ { 0x15, 0x411111f0 },
+ { 0x16, 0x411111f0 },
+ { 0x17, 0x411111f0 },
+ { 0x18, 0x411111f0 },
+ { 0x19, 0x411111f0 },
+ { }
+ }
+ },
+};
+
+static const struct hda_quirk alc260_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_FIXUP_GPIO1),
+ SND_PCI_QUIRK(0x1025, 0x007f, "Acer Aspire 9500", ALC260_FIXUP_COEF),
+ SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1),
+ SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750),
+ SND_PCI_QUIRK(0x103c, 0x30ba, "HP Presario B1900", ALC260_FIXUP_HP_B1900),
+ SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_FIXUP_VAIO_PINS),
+ SND_PCI_QUIRK(0x104d, 0x81e2, "Sony VAIO TX", ALC260_FIXUP_HP_PIN_0F),
+ SND_PCI_QUIRK(0x10cf, 0x1326, "FSC LifeBook S7020", ALC260_FIXUP_FSC_S7020),
+ SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FIXUP_GPIO1),
+ SND_PCI_QUIRK(0x152d, 0x0729, "Quanta KN1", ALC260_FIXUP_KN1),
+ SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_FIXUP_REPLACER),
+ SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_FIXUP_COEF),
+ {}
+};
+
+static const struct hda_model_fixup alc260_fixup_models[] = {
+ {.id = ALC260_FIXUP_GPIO1, .name = "gpio1"},
+ {.id = ALC260_FIXUP_COEF, .name = "coef"},
+ {.id = ALC260_FIXUP_FSC_S7020, .name = "fujitsu"},
+ {.id = ALC260_FIXUP_FSC_S7020_JWSE, .name = "fujitsu-jwse"},
+ {}
+};
+
+/*
+ */
+static int alc260_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct alc_spec *spec;
+ int err;
+
+ err = alc_alloc_spec(codec, 0x07);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
+ /* as quite a few machines require HP amp for speaker outputs,
+ * it's easier to enable it unconditionally; even if it's unneeded,
+ * it's almost harmless.
+ */
+ spec->gen.prefer_hp_amp = 1;
+ spec->gen.beep_nid = 0x01;
+
+ spec->shutup = alc_eapd_shutup;
+
+ alc_pre_init(codec);
+
+ snd_hda_pick_fixup(codec, alc260_fixup_models, alc260_fixup_tbl,
+ alc260_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ /* automatic parse from the BIOS config */
+ err = alc260_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ if (!spec->gen.no_analog) {
+ err = set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
+ if (err < 0)
+ goto error;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops alc260_codec_ops = {
+ .probe = alc260_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc260[] = {
+ HDA_CODEC_ID(0x10ec0260, "ALC260"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc260);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC260 HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+
+static struct hda_codec_driver alc260_driver = {
+ .id = snd_hda_id_alc260,
+ .ops = &alc260_codec_ops,
+};
+
+module_hda_codec_driver(alc260_driver);
diff --git a/sound/hda/codecs/realtek/alc262.c b/sound/hda/codecs/realtek/alc262.c
new file mode 100644
index 000000000000..3ec06cf5d2a6
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc262.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek ALC262 codec
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+static int alc262_parse_auto_config(struct hda_codec *codec)
+{
+ static const hda_nid_t alc262_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc262_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ return alc_parse_auto_config(codec, alc262_ignore, alc262_ssids);
+}
+
+/*
+ * Pin config fixes
+ */
+enum {
+ ALC262_FIXUP_FSC_H270,
+ ALC262_FIXUP_FSC_S7110,
+ ALC262_FIXUP_HP_Z200,
+ ALC262_FIXUP_TYAN,
+ ALC262_FIXUP_LENOVO_3000,
+ ALC262_FIXUP_BENQ,
+ ALC262_FIXUP_BENQ_T31,
+ ALC262_FIXUP_INV_DMIC,
+ ALC262_FIXUP_INTEL_BAYLEYBAY,
+};
+
+static const struct hda_fixup alc262_fixups[] = {
+ [ALC262_FIXUP_FSC_H270] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0221142f }, /* front HP */
+ { 0x1b, 0x0121141f }, /* rear HP */
+ { }
+ }
+ },
+ [ALC262_FIXUP_FSC_S7110] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x90170110 }, /* speaker */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC262_FIXUP_BENQ,
+ },
+ [ALC262_FIXUP_HP_Z200] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x99130120 }, /* internal speaker */
+ { }
+ }
+ },
+ [ALC262_FIXUP_TYAN] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x1993e1f0 }, /* int AUX */
+ { }
+ }
+ },
+ [ALC262_FIXUP_LENOVO_3000] = {
+ .type = HDA_FIXUP_PINCTLS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, PIN_VREF50 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC262_FIXUP_BENQ,
+ },
+ [ALC262_FIXUP_BENQ] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 },
+ {}
+ }
+ },
+ [ALC262_FIXUP_BENQ_T31] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 },
+ {}
+ }
+ },
+ [ALC262_FIXUP_INV_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic,
+ },
+ [ALC262_FIXUP_INTEL_BAYLEYBAY] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_no_depop_delay,
+ },
+};
+
+static const struct hda_quirk alc262_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", ALC262_FIXUP_HP_Z200),
+ SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu Lifebook S7110", ALC262_FIXUP_FSC_S7110),
+ SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ),
+ SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_FIXUP_TYAN),
+ SND_PCI_QUIRK(0x1734, 0x1141, "FSC ESPRIMO U9210", ALC262_FIXUP_FSC_H270),
+ SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", ALC262_FIXUP_FSC_H270),
+ SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000", ALC262_FIXUP_LENOVO_3000),
+ SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_FIXUP_BENQ),
+ SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_FIXUP_BENQ_T31),
+ SND_PCI_QUIRK(0x8086, 0x7270, "BayleyBay", ALC262_FIXUP_INTEL_BAYLEYBAY),
+ {}
+};
+
+static const struct hda_model_fixup alc262_fixup_models[] = {
+ {.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {.id = ALC262_FIXUP_FSC_H270, .name = "fsc-h270"},
+ {.id = ALC262_FIXUP_FSC_S7110, .name = "fsc-s7110"},
+ {.id = ALC262_FIXUP_HP_Z200, .name = "hp-z200"},
+ {.id = ALC262_FIXUP_TYAN, .name = "tyan"},
+ {.id = ALC262_FIXUP_LENOVO_3000, .name = "lenovo-3000"},
+ {.id = ALC262_FIXUP_BENQ, .name = "benq"},
+ {.id = ALC262_FIXUP_BENQ_T31, .name = "benq-t31"},
+ {.id = ALC262_FIXUP_INTEL_BAYLEYBAY, .name = "bayleybay"},
+ {}
+};
+
+/*
+ */
+static int alc262_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct alc_spec *spec;
+ int err;
+
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
+ spec->gen.shared_mic_vref_pin = 0x18;
+
+ spec->shutup = alc_eapd_shutup;
+
+#if 0
+ /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is
+ * under-run
+ */
+ alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x80);
+#endif
+ alc_fix_pll_init(codec, 0x20, 0x0a, 10);
+
+ alc_pre_init(codec);
+
+ snd_hda_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl,
+ alc262_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ alc_auto_parse_customize_define(codec);
+
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
+
+ /* automatic parse from the BIOS config */
+ err = alc262_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ if (!spec->gen.no_analog && spec->gen.beep_nid) {
+ err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ if (err < 0)
+ goto error;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops alc262_codec_ops = {
+ .probe = alc262_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc262[] = {
+ HDA_CODEC_ID(0x10ec0262, "ALC262"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc262);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC262 HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+
+static struct hda_codec_driver alc262_driver = {
+ .id = snd_hda_id_alc262,
+ .ops = &alc262_codec_ops,
+};
+
+module_hda_codec_driver(alc262_driver);
diff --git a/sound/hda/codecs/realtek/alc268.c b/sound/hda/codecs/realtek/alc268.c
new file mode 100644
index 000000000000..4b565fb7bd1c
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc268.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+/* bind Beep switches of both NID 0x0f and 0x10 */
+static int alc268_beep_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned long pval;
+ int err;
+
+ guard(mutex)(&codec->control_mutex);
+ pval = kcontrol->private_value;
+ kcontrol->private_value = (pval & ~0xff) | 0x0f;
+ err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ if (err >= 0) {
+ kcontrol->private_value = (pval & ~0xff) | 0x10;
+ err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ }
+ kcontrol->private_value = pval;
+ return err;
+}
+
+static const struct snd_kcontrol_new alc268_beep_mixer[] = {
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Beep Playback Switch",
+ .subdevice = HDA_SUBDEV_AMP_FLAG,
+ .info = snd_hda_mixer_amp_switch_info,
+ .get = snd_hda_mixer_amp_switch_get,
+ .put = alc268_beep_switch_put,
+ .private_value = HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT)
+ },
+};
+
+/* set PCBEEP vol = 0, mute connections */
+static const struct hda_verb alc268_beep_init_verbs[] = {
+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ { }
+};
+
+enum {
+ ALC268_FIXUP_INV_DMIC,
+ ALC268_FIXUP_HP_EAPD,
+ ALC268_FIXUP_SPDIF,
+};
+
+static const struct hda_fixup alc268_fixups[] = {
+ [ALC268_FIXUP_INV_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic,
+ },
+ [ALC268_FIXUP_HP_EAPD] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x15, AC_VERB_SET_EAPD_BTLENABLE, 0},
+ {}
+ }
+ },
+ [ALC268_FIXUP_SPDIF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x014b1180 }, /* enable SPDIF out */
+ {}
+ }
+ },
+};
+
+static const struct hda_model_fixup alc268_fixup_models[] = {
+ {.id = ALC268_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {.id = ALC268_FIXUP_HP_EAPD, .name = "hp-eapd"},
+ {.id = ALC268_FIXUP_SPDIF, .name = "spdif"},
+ {}
+};
+
+static const struct hda_quirk alc268_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x0139, "Acer TravelMate 6293", ALC268_FIXUP_SPDIF),
+ SND_PCI_QUIRK(0x1025, 0x015b, "Acer AOA 150 (ZG5)", ALC268_FIXUP_INV_DMIC),
+ /* below is codec SSID since multiple Toshiba laptops have the
+ * same PCI SSID 1179:ff00
+ */
+ SND_PCI_QUIRK(0x1179, 0xff06, "Toshiba P200", ALC268_FIXUP_HP_EAPD),
+ {}
+};
+
+/*
+ * BIOS auto configuration
+ */
+static int alc268_parse_auto_config(struct hda_codec *codec)
+{
+ static const hda_nid_t alc268_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ return alc_parse_auto_config(codec, NULL, alc268_ssids);
+}
+
+/*
+ */
+static int alc268_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct alc_spec *spec;
+ int i, err;
+
+ /* ALC268 has no aa-loopback mixer */
+ err = alc_alloc_spec(codec, 0);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
+
+ spec->shutup = alc_eapd_shutup;
+
+ alc_pre_init(codec);
+
+ snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ /* automatic parse from the BIOS config */
+ err = alc268_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ if (err > 0 && !spec->gen.no_analog &&
+ spec->gen.autocfg.speaker_pins[0] != 0x1d) {
+ for (i = 0; i < ARRAY_SIZE(alc268_beep_mixer); i++) {
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL,
+ &alc268_beep_mixer[i])) {
+ err = -ENOMEM;
+ goto error;
+ }
+ }
+ snd_hda_add_verbs(codec, alc268_beep_init_verbs);
+ if (!query_amp_caps(codec, 0x1d, HDA_INPUT))
+ /* override the amp caps for beep generator */
+ snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT,
+ (0x0c << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (0 << AC_AMPCAP_MUTE_SHIFT));
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops alc268_codec_ops = {
+ .probe = alc268_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc268[] = {
+ HDA_CODEC_ID(0x10ec0267, "ALC267"),
+ HDA_CODEC_ID(0x10ec0268, "ALC268"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc268);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC267/268 HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+
+static struct hda_codec_driver alc268_driver = {
+ .id = snd_hda_id_alc268,
+ .ops = &alc268_codec_ops,
+};
+
+module_hda_codec_driver(alc268_driver);
diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c
new file mode 100644
index 000000000000..d90a6c01f63b
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc269.c
@@ -0,0 +1,8311 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek ALC269 and compatible codecs
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+/* keep halting ALC5505 DSP, for power saving */
+#define HALT_REALTEK_ALC5505
+
+static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = {
+ .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
+};
+
+static const struct hda_pcm_stream alc269_44k_pcm_analog_capture = {
+ .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
+};
+
+/* different alc269-variants */
+enum {
+ ALC269_TYPE_ALC269VA,
+ ALC269_TYPE_ALC269VB,
+ ALC269_TYPE_ALC269VC,
+ ALC269_TYPE_ALC269VD,
+ ALC269_TYPE_ALC280,
+ ALC269_TYPE_ALC282,
+ ALC269_TYPE_ALC283,
+ ALC269_TYPE_ALC284,
+ ALC269_TYPE_ALC293,
+ ALC269_TYPE_ALC286,
+ ALC269_TYPE_ALC298,
+ ALC269_TYPE_ALC255,
+ ALC269_TYPE_ALC256,
+ ALC269_TYPE_ALC257,
+ ALC269_TYPE_ALC215,
+ ALC269_TYPE_ALC225,
+ ALC269_TYPE_ALC245,
+ ALC269_TYPE_ALC287,
+ ALC269_TYPE_ALC294,
+ ALC269_TYPE_ALC300,
+ ALC269_TYPE_ALC623,
+ ALC269_TYPE_ALC700,
+};
+
+/*
+ * BIOS auto configuration
+ */
+static int alc269_parse_auto_config(struct hda_codec *codec)
+{
+ static const hda_nid_t alc269_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc269_ssids[] = { 0, 0x1b, 0x14, 0x21 };
+ static const hda_nid_t alc269va_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ struct alc_spec *spec = codec->spec;
+ const hda_nid_t *ssids;
+
+ switch (spec->codec_variant) {
+ case ALC269_TYPE_ALC269VA:
+ case ALC269_TYPE_ALC269VC:
+ case ALC269_TYPE_ALC280:
+ case ALC269_TYPE_ALC284:
+ case ALC269_TYPE_ALC293:
+ ssids = alc269va_ssids;
+ break;
+ case ALC269_TYPE_ALC269VB:
+ case ALC269_TYPE_ALC269VD:
+ case ALC269_TYPE_ALC282:
+ case ALC269_TYPE_ALC283:
+ case ALC269_TYPE_ALC286:
+ case ALC269_TYPE_ALC298:
+ case ALC269_TYPE_ALC255:
+ case ALC269_TYPE_ALC256:
+ case ALC269_TYPE_ALC257:
+ case ALC269_TYPE_ALC215:
+ case ALC269_TYPE_ALC225:
+ case ALC269_TYPE_ALC245:
+ case ALC269_TYPE_ALC287:
+ case ALC269_TYPE_ALC294:
+ case ALC269_TYPE_ALC300:
+ case ALC269_TYPE_ALC623:
+ case ALC269_TYPE_ALC700:
+ ssids = alc269_ssids;
+ break;
+ default:
+ ssids = alc269_ssids;
+ break;
+ }
+
+ return alc_parse_auto_config(codec, alc269_ignore, ssids);
+}
+
+static const struct hda_jack_keymap alc_headset_btn_keymap[] = {
+ { SND_JACK_BTN_0, KEY_PLAYPAUSE },
+ { SND_JACK_BTN_1, KEY_VOICECOMMAND },
+ { SND_JACK_BTN_2, KEY_VOLUMEUP },
+ { SND_JACK_BTN_3, KEY_VOLUMEDOWN },
+ {}
+};
+
+static void alc_headset_btn_callback(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ int report = 0;
+
+ if (jack->unsol_res & (7 << 13))
+ report |= SND_JACK_BTN_0;
+
+ if (jack->unsol_res & (1 << 16 | 3 << 8))
+ report |= SND_JACK_BTN_1;
+
+ /* Volume up key */
+ if (jack->unsol_res & (7 << 23))
+ report |= SND_JACK_BTN_2;
+
+ /* Volume down key */
+ if (jack->unsol_res & (7 << 10))
+ report |= SND_JACK_BTN_3;
+
+ snd_hda_jack_set_button_state(codec, jack->nid, report);
+}
+
+static void alc_disable_headset_jack_key(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (!spec->has_hs_key)
+ return;
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0215:
+ case 0x10ec0225:
+ case 0x10ec0285:
+ case 0x10ec0287:
+ case 0x10ec0295:
+ case 0x10ec0289:
+ case 0x10ec0299:
+ alc_write_coef_idx(codec, 0x48, 0x0);
+ alc_update_coef_idx(codec, 0x49, 0x0045, 0x0);
+ alc_update_coef_idx(codec, 0x44, 0x0045 << 8, 0x0);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x10ec0257:
+ case 0x19e58326:
+ alc_write_coef_idx(codec, 0x48, 0x0);
+ alc_update_coef_idx(codec, 0x49, 0x0045, 0x0);
+ break;
+ }
+}
+
+static void alc_enable_headset_jack_key(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (!spec->has_hs_key)
+ return;
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0215:
+ case 0x10ec0225:
+ case 0x10ec0285:
+ case 0x10ec0287:
+ case 0x10ec0295:
+ case 0x10ec0289:
+ case 0x10ec0299:
+ alc_write_coef_idx(codec, 0x48, 0xd011);
+ alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045);
+ alc_update_coef_idx(codec, 0x44, 0x007f << 8, 0x0045 << 8);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x10ec0257:
+ case 0x19e58326:
+ alc_write_coef_idx(codec, 0x48, 0xd011);
+ alc_update_coef_idx(codec, 0x49, 0x007f, 0x0045);
+ break;
+ }
+}
+
+static void alc_fixup_headset_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->has_hs_key = 1;
+ snd_hda_jack_detect_enable_callback(codec, 0x55,
+ alc_headset_btn_callback);
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ hp_pin = alc_get_hp_pin(spec);
+ if (!hp_pin || snd_hda_jack_bind_keymap(codec, 0x55,
+ alc_headset_btn_keymap,
+ hp_pin))
+ snd_hda_jack_add_kctl(codec, 0x55, "Headset Jack",
+ false, SND_JACK_HEADSET,
+ alc_headset_btn_keymap);
+
+ alc_enable_headset_jack_key(codec);
+ break;
+ }
+}
+
+static void alc269vb_toggle_power_output(struct hda_codec *codec, int power_up)
+{
+ alc_update_coef_idx(codec, 0x04, 1 << 11, power_up ? (1 << 11) : 0);
+}
+
+static void alc269_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->codec_variant == ALC269_TYPE_ALC269VB)
+ alc269vb_toggle_power_output(codec, 0);
+ if (spec->codec_variant == ALC269_TYPE_ALC269VB &&
+ (alc_get_coef0(codec) & 0x00ff) == 0x018) {
+ msleep(150);
+ }
+ alc_shutup_pins(codec);
+}
+
+static const struct coef_fw alc282_coefs[] = {
+ WRITE_COEF(0x03, 0x0002), /* Power Down Control */
+ UPDATE_COEF(0x05, 0xff3f, 0x0700), /* FIFO and filter clock */
+ WRITE_COEF(0x07, 0x0200), /* DMIC control */
+ UPDATE_COEF(0x06, 0x00f0, 0), /* Analog clock */
+ UPDATE_COEF(0x08, 0xfffc, 0x0c2c), /* JD */
+ WRITE_COEF(0x0a, 0xcccc), /* JD offset1 */
+ WRITE_COEF(0x0b, 0xcccc), /* JD offset2 */
+ WRITE_COEF(0x0e, 0x6e00), /* LDO1/2/3, DAC/ADC */
+ UPDATE_COEF(0x0f, 0xf800, 0x1000), /* JD */
+ UPDATE_COEF(0x10, 0xfc00, 0x0c00), /* Capless */
+ WRITE_COEF(0x6f, 0x0), /* Class D test 4 */
+ UPDATE_COEF(0x0c, 0xfe00, 0), /* IO power down directly */
+ WRITE_COEF(0x34, 0xa0c0), /* ANC */
+ UPDATE_COEF(0x16, 0x0008, 0), /* AGC MUX */
+ UPDATE_COEF(0x1d, 0x00e0, 0), /* DAC simple content protection */
+ UPDATE_COEF(0x1f, 0x00e0, 0), /* ADC simple content protection */
+ WRITE_COEF(0x21, 0x8804), /* DAC ADC Zero Detection */
+ WRITE_COEF(0x63, 0x2902), /* PLL */
+ WRITE_COEF(0x68, 0xa080), /* capless control 2 */
+ WRITE_COEF(0x69, 0x3400), /* capless control 3 */
+ WRITE_COEF(0x6a, 0x2f3e), /* capless control 4 */
+ WRITE_COEF(0x6b, 0x0), /* capless control 5 */
+ UPDATE_COEF(0x6d, 0x0fff, 0x0900), /* class D test 2 */
+ WRITE_COEF(0x6e, 0x110a), /* class D test 3 */
+ UPDATE_COEF(0x70, 0x00f8, 0x00d8), /* class D test 5 */
+ WRITE_COEF(0x71, 0x0014), /* class D test 6 */
+ WRITE_COEF(0x72, 0xc2ba), /* classD OCP */
+ UPDATE_COEF(0x77, 0x0f80, 0), /* classD pure DC test */
+ WRITE_COEF(0x6c, 0xfc06), /* Class D amp control */
+ {}
+};
+
+static void alc282_restore_default_value(struct hda_codec *codec)
+{
+ alc_process_coef_fw(codec, alc282_coefs);
+}
+
+static void alc282_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp_pin_sense;
+ int coef78;
+
+ alc282_restore_default_value(codec);
+
+ if (!hp_pin)
+ return;
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+ coef78 = alc_read_coef_idx(codec, 0x78);
+
+ /* Index 0x78 Direct Drive HP AMP LPM Control 1 */
+ /* Headphone capless set to high power mode */
+ alc_write_coef_idx(codec, 0x78, 0x9004);
+
+ if (hp_pin_sense)
+ msleep(2);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ if (hp_pin_sense)
+ msleep(85);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+
+ if (hp_pin_sense)
+ msleep(100);
+
+ /* Headphone capless set to normal mode */
+ alc_write_coef_idx(codec, 0x78, coef78);
+}
+
+static void alc282_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp_pin_sense;
+ int coef78;
+
+ if (!hp_pin) {
+ alc269_shutup(codec);
+ return;
+ }
+
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+ coef78 = alc_read_coef_idx(codec, 0x78);
+ alc_write_coef_idx(codec, 0x78, 0x9004);
+
+ if (hp_pin_sense)
+ msleep(2);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ if (hp_pin_sense)
+ msleep(85);
+
+ if (!spec->no_shutup_pins)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+ if (hp_pin_sense)
+ msleep(100);
+
+ alc_auto_setup_eapd(codec, false);
+ alc_shutup_pins(codec);
+ alc_write_coef_idx(codec, 0x78, coef78);
+}
+
+static const struct coef_fw alc283_coefs[] = {
+ WRITE_COEF(0x03, 0x0002), /* Power Down Control */
+ UPDATE_COEF(0x05, 0xff3f, 0x0700), /* FIFO and filter clock */
+ WRITE_COEF(0x07, 0x0200), /* DMIC control */
+ UPDATE_COEF(0x06, 0x00f0, 0), /* Analog clock */
+ UPDATE_COEF(0x08, 0xfffc, 0x0c2c), /* JD */
+ WRITE_COEF(0x0a, 0xcccc), /* JD offset1 */
+ WRITE_COEF(0x0b, 0xcccc), /* JD offset2 */
+ WRITE_COEF(0x0e, 0x6fc0), /* LDO1/2/3, DAC/ADC */
+ UPDATE_COEF(0x0f, 0xf800, 0x1000), /* JD */
+ UPDATE_COEF(0x10, 0xfc00, 0x0c00), /* Capless */
+ WRITE_COEF(0x3a, 0x0), /* Class D test 4 */
+ UPDATE_COEF(0x0c, 0xfe00, 0x0), /* IO power down directly */
+ WRITE_COEF(0x22, 0xa0c0), /* ANC */
+ UPDATE_COEFEX(0x53, 0x01, 0x000f, 0x0008), /* AGC MUX */
+ UPDATE_COEF(0x1d, 0x00e0, 0), /* DAC simple content protection */
+ UPDATE_COEF(0x1f, 0x00e0, 0), /* ADC simple content protection */
+ WRITE_COEF(0x21, 0x8804), /* DAC ADC Zero Detection */
+ WRITE_COEF(0x2e, 0x2902), /* PLL */
+ WRITE_COEF(0x33, 0xa080), /* capless control 2 */
+ WRITE_COEF(0x34, 0x3400), /* capless control 3 */
+ WRITE_COEF(0x35, 0x2f3e), /* capless control 4 */
+ WRITE_COEF(0x36, 0x0), /* capless control 5 */
+ UPDATE_COEF(0x38, 0x0fff, 0x0900), /* class D test 2 */
+ WRITE_COEF(0x39, 0x110a), /* class D test 3 */
+ UPDATE_COEF(0x3b, 0x00f8, 0x00d8), /* class D test 5 */
+ WRITE_COEF(0x3c, 0x0014), /* class D test 6 */
+ WRITE_COEF(0x3d, 0xc2ba), /* classD OCP */
+ UPDATE_COEF(0x42, 0x0f80, 0x0), /* classD pure DC test */
+ WRITE_COEF(0x49, 0x0), /* test mode */
+ UPDATE_COEF(0x40, 0xf800, 0x9800), /* Class D DC enable */
+ UPDATE_COEF(0x42, 0xf000, 0x2000), /* DC offset */
+ WRITE_COEF(0x37, 0xfc06), /* Class D amp control */
+ UPDATE_COEF(0x1b, 0x8000, 0), /* HP JD control */
+ {}
+};
+
+static void alc283_restore_default_value(struct hda_codec *codec)
+{
+ alc_process_coef_fw(codec, alc283_coefs);
+}
+
+static void alc283_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp_pin_sense;
+
+ alc283_restore_default_value(codec);
+
+ if (!hp_pin)
+ return;
+
+ msleep(30);
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+
+ /* Index 0x43 Direct Drive HP AMP LPM Control 1 */
+ /* Headphone capless set to high power mode */
+ alc_write_coef_idx(codec, 0x43, 0x9004);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ if (hp_pin_sense)
+ msleep(85);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+
+ if (hp_pin_sense)
+ msleep(85);
+ /* Index 0x46 Combo jack auto switch control 2 */
+ /* 3k pull low control for Headset jack. */
+ alc_update_coef_idx(codec, 0x46, 3 << 12, 0);
+ /* Headphone capless set to normal mode */
+ alc_write_coef_idx(codec, 0x43, 0x9614);
+}
+
+static void alc283_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp_pin_sense;
+
+ if (!hp_pin) {
+ alc269_shutup(codec);
+ return;
+ }
+
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+
+ alc_write_coef_idx(codec, 0x43, 0x9004);
+
+ /*depop hp during suspend*/
+ alc_write_coef_idx(codec, 0x06, 0x2100);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ if (hp_pin_sense)
+ msleep(100);
+
+ if (!spec->no_shutup_pins)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+ alc_update_coef_idx(codec, 0x46, 0, 3 << 12);
+
+ if (hp_pin_sense)
+ msleep(100);
+ alc_auto_setup_eapd(codec, false);
+ alc_shutup_pins(codec);
+ alc_write_coef_idx(codec, 0x43, 0x9614);
+}
+
+static void alc256_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp_pin_sense;
+
+ if (spec->ultra_low_power) {
+ alc_update_coef_idx(codec, 0x03, 1<<1, 1<<1);
+ alc_update_coef_idx(codec, 0x08, 3<<2, 3<<2);
+ alc_update_coef_idx(codec, 0x08, 7<<4, 0);
+ alc_update_coef_idx(codec, 0x3b, 1<<15, 0);
+ alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6);
+ msleep(30);
+ }
+
+ if (!hp_pin)
+ hp_pin = 0x21;
+
+ msleep(30);
+
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+
+ if (hp_pin_sense) {
+ msleep(2);
+ alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+
+ msleep(75);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+
+ msleep(75);
+ alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */
+ }
+ alc_update_coef_idx(codec, 0x46, 3 << 12, 0);
+ alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 1 << 15); /* Clear bit */
+ alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 0 << 15);
+ /*
+ * Expose headphone mic (or possibly Line In on some machines) instead
+ * of PC Beep on 1Ah, and disable 1Ah loopback for all outputs. See
+ * Documentation/sound/hd-audio/realtek-pc-beep.rst for details of
+ * this register.
+ */
+ alc_write_coef_idx(codec, 0x36, 0x5757);
+}
+
+static void alc256_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp_pin_sense;
+
+ if (!hp_pin)
+ hp_pin = 0x21;
+
+ alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */
+
+ /* 3k pull low control for Headset jack. */
+ /* NOTE: call this before clearing the pin, otherwise codec stalls */
+ /* If disable 3k pulldown control for alc257, the Mic detection will not work correctly
+ * when booting with headset plugged. So skip setting it for the codec alc257
+ */
+ if (spec->en_3kpull_low)
+ alc_update_coef_idx(codec, 0x46, 0, 3 << 12);
+
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+
+ if (hp_pin_sense) {
+ msleep(2);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ msleep(75);
+
+ if (!spec->no_shutup_pins)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+ msleep(75);
+ }
+
+ alc_auto_setup_eapd(codec, false);
+ alc_shutup_pins(codec);
+ if (spec->ultra_low_power) {
+ msleep(50);
+ alc_update_coef_idx(codec, 0x03, 1<<1, 0);
+ alc_update_coef_idx(codec, 0x08, 7<<4, 7<<4);
+ alc_update_coef_idx(codec, 0x08, 3<<2, 0);
+ alc_update_coef_idx(codec, 0x3b, 1<<15, 1<<15);
+ alc_update_coef_idx(codec, 0x0e, 7<<6, 0);
+ msleep(30);
+ }
+}
+
+static void alc285_hp_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ int i, val;
+ int coef38, coef0d, coef36;
+
+ alc_write_coefex_idx(codec, 0x58, 0x00, 0x1888); /* write default value */
+ alc_update_coef_idx(codec, 0x4a, 1<<15, 1<<15); /* Reset HP JD */
+ coef38 = alc_read_coef_idx(codec, 0x38); /* Amp control */
+ coef0d = alc_read_coef_idx(codec, 0x0d); /* Digital Misc control */
+ coef36 = alc_read_coef_idx(codec, 0x36); /* Passthrough Control */
+ alc_update_coef_idx(codec, 0x38, 1<<4, 0x0);
+ alc_update_coef_idx(codec, 0x0d, 0x110, 0x0);
+
+ alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000);
+
+ if (hp_pin)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ msleep(130);
+ alc_update_coef_idx(codec, 0x36, 1<<14, 1<<14);
+ alc_update_coef_idx(codec, 0x36, 1<<13, 0x0);
+
+ if (hp_pin)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+ msleep(10);
+ alc_write_coef_idx(codec, 0x67, 0x0); /* Set HP depop to manual mode */
+ alc_write_coefex_idx(codec, 0x58, 0x00, 0x7880);
+ alc_write_coefex_idx(codec, 0x58, 0x0f, 0xf049);
+ alc_update_coefex_idx(codec, 0x58, 0x03, 0x00f0, 0x00c0);
+
+ alc_write_coefex_idx(codec, 0x58, 0x00, 0xf888); /* HP depop procedure start */
+ val = alc_read_coefex_idx(codec, 0x58, 0x00);
+ for (i = 0; i < 20 && val & 0x8000; i++) {
+ msleep(50);
+ val = alc_read_coefex_idx(codec, 0x58, 0x00);
+ } /* Wait for depop procedure finish */
+
+ alc_write_coefex_idx(codec, 0x58, 0x00, val); /* write back the result */
+ alc_update_coef_idx(codec, 0x38, 1<<4, coef38);
+ alc_update_coef_idx(codec, 0x0d, 0x110, coef0d);
+ alc_update_coef_idx(codec, 0x36, 3<<13, coef36);
+
+ msleep(50);
+ alc_update_coef_idx(codec, 0x4a, 1<<15, 0);
+}
+
+static void alc225_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp1_pin_sense, hp2_pin_sense;
+
+ if (spec->ultra_low_power) {
+ alc_update_coef_idx(codec, 0x08, 0x0f << 2, 3<<2);
+ alc_update_coef_idx(codec, 0x0e, 7<<6, 7<<6);
+ alc_update_coef_idx(codec, 0x33, 1<<11, 0);
+ msleep(30);
+ }
+
+ if (spec->codec_variant != ALC269_TYPE_ALC287 &&
+ spec->codec_variant != ALC269_TYPE_ALC245)
+ /* required only at boot or S3 and S4 resume time */
+ if (!spec->done_hp_init ||
+ is_s3_resume(codec) ||
+ is_s4_resume(codec)) {
+ alc285_hp_init(codec);
+ spec->done_hp_init = true;
+ }
+
+ if (!hp_pin)
+ hp_pin = 0x21;
+ msleep(30);
+
+ hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+ hp2_pin_sense = snd_hda_jack_detect(codec, 0x16);
+
+ if (hp1_pin_sense || hp2_pin_sense) {
+ msleep(2);
+ alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x1); /* Low power */
+
+ if (hp1_pin_sense)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ if (hp2_pin_sense)
+ snd_hda_codec_write(codec, 0x16, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ msleep(75);
+
+ if (hp1_pin_sense)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ if (hp2_pin_sense)
+ snd_hda_codec_write(codec, 0x16, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+
+ msleep(75);
+ alc_update_coef_idx(codec, 0x4a, 3 << 10, 0);
+ alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */
+ }
+}
+
+static void alc225_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp1_pin_sense, hp2_pin_sense;
+
+ if (!hp_pin)
+ hp_pin = 0x21;
+
+ hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+ hp2_pin_sense = snd_hda_jack_detect(codec, 0x16);
+
+ if (hp1_pin_sense || hp2_pin_sense) {
+ alc_disable_headset_jack_key(codec);
+ /* 3k pull low control for Headset jack. */
+ alc_update_coef_idx(codec, 0x4a, 0, 3 << 10);
+ msleep(2);
+
+ if (hp1_pin_sense)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ if (hp2_pin_sense)
+ snd_hda_codec_write(codec, 0x16, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ msleep(75);
+
+ if (hp1_pin_sense)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+ if (hp2_pin_sense)
+ snd_hda_codec_write(codec, 0x16, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+ msleep(75);
+ alc_update_coef_idx(codec, 0x4a, 3 << 10, 0);
+ alc_enable_headset_jack_key(codec);
+ }
+ alc_auto_setup_eapd(codec, false);
+ alc_shutup_pins(codec);
+ if (spec->ultra_low_power) {
+ msleep(50);
+ alc_update_coef_idx(codec, 0x08, 0x0f << 2, 0x0c << 2);
+ alc_update_coef_idx(codec, 0x0e, 7<<6, 0);
+ alc_update_coef_idx(codec, 0x33, 1<<11, 1<<11);
+ alc_update_coef_idx(codec, 0x4a, 3<<4, 2<<4);
+ msleep(30);
+ }
+}
+
+static void alc222_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp1_pin_sense, hp2_pin_sense;
+
+ if (!hp_pin)
+ return;
+
+ msleep(30);
+
+ hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+ hp2_pin_sense = snd_hda_jack_detect(codec, 0x14);
+
+ if (hp1_pin_sense || hp2_pin_sense) {
+ msleep(2);
+
+ if (hp1_pin_sense)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ if (hp2_pin_sense)
+ snd_hda_codec_write(codec, 0x14, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ msleep(75);
+
+ if (hp1_pin_sense)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ if (hp2_pin_sense)
+ snd_hda_codec_write(codec, 0x14, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+
+ msleep(75);
+ }
+}
+
+static void alc222_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp1_pin_sense, hp2_pin_sense;
+
+ if (!hp_pin)
+ hp_pin = 0x21;
+
+ hp1_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+ hp2_pin_sense = snd_hda_jack_detect(codec, 0x14);
+
+ if (hp1_pin_sense || hp2_pin_sense) {
+ msleep(2);
+
+ if (hp1_pin_sense)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ if (hp2_pin_sense)
+ snd_hda_codec_write(codec, 0x14, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ msleep(75);
+
+ if (hp1_pin_sense)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+ if (hp2_pin_sense)
+ snd_hda_codec_write(codec, 0x14, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+ msleep(75);
+ }
+ alc_auto_setup_eapd(codec, false);
+ alc_shutup_pins(codec);
+}
+
+static void alc_default_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp_pin_sense;
+
+ if (!hp_pin)
+ return;
+
+ msleep(30);
+
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+
+ if (hp_pin_sense) {
+ msleep(2);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+
+ msleep(75);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ msleep(75);
+ }
+}
+
+static void alc_default_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ bool hp_pin_sense;
+
+ if (!hp_pin) {
+ alc269_shutup(codec);
+ return;
+ }
+
+ hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+
+ if (hp_pin_sense) {
+ msleep(2);
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ msleep(75);
+
+ if (!spec->no_shutup_pins)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+ msleep(75);
+ }
+ alc_auto_setup_eapd(codec, false);
+ alc_shutup_pins(codec);
+}
+
+static void alc294_hp_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+ int i, val;
+
+ if (!hp_pin)
+ return;
+
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ msleep(100);
+
+ if (!spec->no_shutup_pins)
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+ alc_update_coef_idx(codec, 0x6f, 0x000f, 0);/* Set HP depop to manual mode */
+ alc_update_coefex_idx(codec, 0x58, 0x00, 0x8000, 0x8000); /* HP depop procedure start */
+
+ /* Wait for depop procedure finish */
+ val = alc_read_coefex_idx(codec, 0x58, 0x01);
+ for (i = 0; i < 20 && val & 0x0080; i++) {
+ msleep(50);
+ val = alc_read_coefex_idx(codec, 0x58, 0x01);
+ }
+ /* Set HP depop to auto mode */
+ alc_update_coef_idx(codec, 0x6f, 0x000f, 0x000b);
+ msleep(50);
+}
+
+static void alc294_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ /* required only at boot or S4 resume time */
+ if (!spec->done_hp_init || is_s4_resume(codec)) {
+ alc294_hp_init(codec);
+ spec->done_hp_init = true;
+ }
+ alc_default_init(codec);
+}
+
+static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg,
+ unsigned int val)
+{
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1);
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val & 0xffff); /* LSB */
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val >> 16); /* MSB */
+}
+
+static int alc5505_coef_get(struct hda_codec *codec, unsigned int index_reg)
+{
+ unsigned int val;
+
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1);
+ val = snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0)
+ & 0xffff;
+ val |= snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0)
+ << 16;
+ return val;
+}
+
+static void alc5505_dsp_halt(struct hda_codec *codec)
+{
+ unsigned int val;
+
+ alc5505_coef_set(codec, 0x3000, 0x000c); /* DSP CPU stop */
+ alc5505_coef_set(codec, 0x880c, 0x0008); /* DDR enter self refresh */
+ alc5505_coef_set(codec, 0x61c0, 0x11110080); /* Clock control for PLL and CPU */
+ alc5505_coef_set(codec, 0x6230, 0xfc0d4011); /* Disable Input OP */
+ alc5505_coef_set(codec, 0x61b4, 0x040a2b03); /* Stop PLL2 */
+ alc5505_coef_set(codec, 0x61b0, 0x00005b17); /* Stop PLL1 */
+ alc5505_coef_set(codec, 0x61b8, 0x04133303); /* Stop PLL3 */
+ val = alc5505_coef_get(codec, 0x6220);
+ alc5505_coef_set(codec, 0x6220, (val | 0x3000)); /* switch Ringbuffer clock to DBUS clock */
+}
+
+static void alc5505_dsp_back_from_halt(struct hda_codec *codec)
+{
+ alc5505_coef_set(codec, 0x61b8, 0x04133302);
+ alc5505_coef_set(codec, 0x61b0, 0x00005b16);
+ alc5505_coef_set(codec, 0x61b4, 0x040a2b02);
+ alc5505_coef_set(codec, 0x6230, 0xf80d4011);
+ alc5505_coef_set(codec, 0x6220, 0x2002010f);
+ alc5505_coef_set(codec, 0x880c, 0x00000004);
+}
+
+static void alc5505_dsp_init(struct hda_codec *codec)
+{
+ unsigned int val;
+
+ alc5505_dsp_halt(codec);
+ alc5505_dsp_back_from_halt(codec);
+ alc5505_coef_set(codec, 0x61b0, 0x5b14); /* PLL1 control */
+ alc5505_coef_set(codec, 0x61b0, 0x5b16);
+ alc5505_coef_set(codec, 0x61b4, 0x04132b00); /* PLL2 control */
+ alc5505_coef_set(codec, 0x61b4, 0x04132b02);
+ alc5505_coef_set(codec, 0x61b8, 0x041f3300); /* PLL3 control*/
+ alc5505_coef_set(codec, 0x61b8, 0x041f3302);
+ snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_CODEC_RESET, 0); /* Function reset */
+ alc5505_coef_set(codec, 0x61b8, 0x041b3302);
+ alc5505_coef_set(codec, 0x61b8, 0x04173302);
+ alc5505_coef_set(codec, 0x61b8, 0x04163302);
+ alc5505_coef_set(codec, 0x8800, 0x348b328b); /* DRAM control */
+ alc5505_coef_set(codec, 0x8808, 0x00020022); /* DRAM control */
+ alc5505_coef_set(codec, 0x8818, 0x00000400); /* DRAM control */
+
+ val = alc5505_coef_get(codec, 0x6200) >> 16; /* Read revision ID */
+ if (val <= 3)
+ alc5505_coef_set(codec, 0x6220, 0x2002010f); /* I/O PAD Configuration */
+ else
+ alc5505_coef_set(codec, 0x6220, 0x6002018f);
+
+ alc5505_coef_set(codec, 0x61ac, 0x055525f0); /**/
+ alc5505_coef_set(codec, 0x61c0, 0x12230080); /* Clock control */
+ alc5505_coef_set(codec, 0x61b4, 0x040e2b02); /* PLL2 control */
+ alc5505_coef_set(codec, 0x61bc, 0x010234f8); /* OSC Control */
+ alc5505_coef_set(codec, 0x880c, 0x00000004); /* DRAM Function control */
+ alc5505_coef_set(codec, 0x880c, 0x00000003);
+ alc5505_coef_set(codec, 0x880c, 0x00000010);
+
+#ifdef HALT_REALTEK_ALC5505
+ alc5505_dsp_halt(codec);
+#endif
+}
+
+#ifdef HALT_REALTEK_ALC5505
+#define alc5505_dsp_suspend(codec) do { } while (0) /* NOP */
+#define alc5505_dsp_resume(codec) do { } while (0) /* NOP */
+#else
+#define alc5505_dsp_suspend(codec) alc5505_dsp_halt(codec)
+#define alc5505_dsp_resume(codec) alc5505_dsp_back_from_halt(codec)
+#endif
+
+static int alc269_suspend(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->has_alc5505_dsp)
+ alc5505_dsp_suspend(codec);
+
+ return alc_suspend(codec);
+}
+
+static int alc269_resume(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->codec_variant == ALC269_TYPE_ALC269VB)
+ alc269vb_toggle_power_output(codec, 0);
+ if (spec->codec_variant == ALC269_TYPE_ALC269VB &&
+ (alc_get_coef0(codec) & 0x00ff) == 0x018) {
+ msleep(150);
+ }
+
+ snd_hda_codec_init(codec);
+
+ if (spec->codec_variant == ALC269_TYPE_ALC269VB)
+ alc269vb_toggle_power_output(codec, 1);
+ if (spec->codec_variant == ALC269_TYPE_ALC269VB &&
+ (alc_get_coef0(codec) & 0x00ff) == 0x017) {
+ msleep(200);
+ }
+
+ snd_hda_regmap_sync(codec);
+ hda_call_check_power_status(codec, 0x01);
+
+ /* on some machine, the BIOS will clear the codec gpio data when enter
+ * suspend, and won't restore the data after resume, so we restore it
+ * in the driver.
+ */
+ if (spec->gpio_data)
+ alc_write_gpio_data(codec);
+
+ if (spec->has_alc5505_dsp)
+ alc5505_dsp_resume(codec);
+
+ return 0;
+}
+
+static void alc269_fixup_pincfg_no_hp_to_lineout(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
+}
+
+static void alc269_fixup_pincfg_U7x7_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ unsigned int cfg_headphone = snd_hda_codec_get_pincfg(codec, 0x21);
+ unsigned int cfg_headset_mic = snd_hda_codec_get_pincfg(codec, 0x19);
+
+ if (cfg_headphone && cfg_headset_mic == 0x411111f0)
+ snd_hda_codec_set_pincfg(codec, 0x19,
+ (cfg_headphone & ~AC_DEFCFG_DEVICE) |
+ (AC_JACK_MIC_IN << AC_DEFCFG_DEVICE_SHIFT));
+}
+
+static void alc269_fixup_hweq(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc_update_coef_idx(codec, 0x1e, 0, 0x80);
+}
+
+static void alc271_fixup_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const struct hda_verb verbs[] = {
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x0d},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x4000},
+ {}
+ };
+ unsigned int cfg;
+
+ if (strcmp(codec->core.chip_name, "ALC271X") &&
+ strcmp(codec->core.chip_name, "ALC269VB"))
+ return;
+ cfg = snd_hda_codec_get_pincfg(codec, 0x12);
+ if (get_defcfg_connect(cfg) == AC_JACK_PORT_FIXED)
+ snd_hda_sequence_write(codec, verbs);
+}
+
+/* Fix the speaker amp after resume, etc */
+static void alc269vb_fixup_aspire_e1_coef(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc_update_coef_idx(codec, 0x0d, 0x6000, 0x6000);
+}
+
+static void alc269_fixup_pcm_44k(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
+
+ /* Due to a hardware problem on Lenovo Ideadpad, we need to
+ * fix the sample rate of analog I/O to 44.1kHz
+ */
+ spec->gen.stream_analog_playback = &alc269_44k_pcm_analog_playback;
+ spec->gen.stream_analog_capture = &alc269_44k_pcm_analog_capture;
+}
+
+static void alc269_fixup_stereo_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /* The digital-mic unit sends PDM (differential signal) instead of
+ * the standard PCM, thus you can't record a valid mono stream as is.
+ * Below is a workaround specific to ALC269 to control the dmic
+ * signal source as mono.
+ */
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc_update_coef_idx(codec, 0x07, 0, 0x80);
+}
+
+static void alc269_quanta_automute(struct hda_codec *codec)
+{
+ snd_hda_gen_update_outputs(codec);
+
+ alc_write_coef_idx(codec, 0x0c, 0x680);
+ alc_write_coef_idx(codec, 0x0c, 0x480);
+}
+
+static void alc269_fixup_quanta_mute(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
+ spec->gen.automute_hook = alc269_quanta_automute;
+}
+
+static void alc269_x101_hp_automute_hook(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ int vref;
+ msleep(200);
+ snd_hda_gen_hp_automute(codec, jack);
+
+ vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0;
+ msleep(100);
+ snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ vref);
+ msleep(500);
+ snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ vref);
+}
+
+/*
+ * Magic sequence to make Huawei Matebook X right speaker working (bko#197801)
+ */
+struct hda_alc298_mbxinit {
+ unsigned char value_0x23;
+ unsigned char value_0x25;
+};
+
+static void alc298_huawei_mbx_stereo_seq(struct hda_codec *codec,
+ const struct hda_alc298_mbxinit *initval,
+ bool first)
+{
+ snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x0);
+ alc_write_coef_idx(codec, 0x26, 0xb000);
+
+ if (first)
+ snd_hda_codec_write(codec, 0x21, 0, AC_VERB_GET_PIN_SENSE, 0x0);
+
+ snd_hda_codec_write(codec, 0x6, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x80);
+ alc_write_coef_idx(codec, 0x26, 0xf000);
+ alc_write_coef_idx(codec, 0x23, initval->value_0x23);
+
+ if (initval->value_0x23 != 0x1e)
+ alc_write_coef_idx(codec, 0x25, initval->value_0x25);
+
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x26);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb010);
+}
+
+static void alc298_fixup_huawei_mbx_stereo(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ /* Initialization magic */
+ static const struct hda_alc298_mbxinit dac_init[] = {
+ {0x0c, 0x00}, {0x0d, 0x00}, {0x0e, 0x00}, {0x0f, 0x00},
+ {0x10, 0x00}, {0x1a, 0x40}, {0x1b, 0x82}, {0x1c, 0x00},
+ {0x1d, 0x00}, {0x1e, 0x00}, {0x1f, 0x00},
+ {0x20, 0xc2}, {0x21, 0xc8}, {0x22, 0x26}, {0x23, 0x24},
+ {0x27, 0xff}, {0x28, 0xff}, {0x29, 0xff}, {0x2a, 0x8f},
+ {0x2b, 0x02}, {0x2c, 0x48}, {0x2d, 0x34}, {0x2e, 0x00},
+ {0x2f, 0x00},
+ {0x30, 0x00}, {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00},
+ {0x34, 0x00}, {0x35, 0x01}, {0x36, 0x93}, {0x37, 0x0c},
+ {0x38, 0x00}, {0x39, 0x00}, {0x3a, 0xf8}, {0x38, 0x80},
+ {}
+ };
+ const struct hda_alc298_mbxinit *seq;
+
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
+
+ /* Start */
+ snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x00);
+ snd_hda_codec_write(codec, 0x06, 0, AC_VERB_SET_DIGI_CONVERT_3, 0x80);
+ alc_write_coef_idx(codec, 0x26, 0xf000);
+ alc_write_coef_idx(codec, 0x22, 0x31);
+ alc_write_coef_idx(codec, 0x23, 0x0b);
+ alc_write_coef_idx(codec, 0x25, 0x00);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x26);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, 0xb010);
+
+ for (seq = dac_init; seq->value_0x23; seq++)
+ alc298_huawei_mbx_stereo_seq(codec, seq, seq == dac_init);
+}
+
+static void alc269_fixup_x101_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ spec->gen.hp_automute_hook = alc269_x101_hp_automute_hook;
+ }
+}
+
+static void alc_update_vref_led(struct hda_codec *codec, hda_nid_t pin,
+ bool polarity, bool on)
+{
+ unsigned int pinval;
+
+ if (!pin)
+ return;
+ if (polarity)
+ on = !on;
+ pinval = snd_hda_codec_get_pin_target(codec, pin);
+ pinval &= ~AC_PINCTL_VREFEN;
+ pinval |= on ? AC_PINCTL_VREF_80 : AC_PINCTL_VREF_HIZ;
+ /* temporarily power up/down for setting VREF */
+ CLASS(snd_hda_power_pm, pm)(codec);
+ snd_hda_set_pin_ctl_cache(codec, pin, pinval);
+}
+
+/* update mute-LED according to the speaker mute state via mic VREF pin */
+static int vref_mute_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
+ struct alc_spec *spec = codec->spec;
+
+ alc_update_vref_led(codec, spec->mute_led_nid,
+ spec->mute_led_polarity, brightness);
+ return 0;
+}
+
+/* Make sure the led works even in runtime suspend */
+static unsigned int led_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (power_state != AC_PWRST_D3 || nid == 0 ||
+ (nid != spec->mute_led_nid && nid != spec->cap_mute_led_nid))
+ return power_state;
+
+ /* Set pin ctl again, it might have just been set to 0 */
+ snd_hda_set_pin_ctl(codec, nid,
+ snd_hda_codec_get_pin_target(codec, nid));
+
+ return snd_hda_gen_path_power_filter(codec, nid, power_state);
+}
+
+static void alc269_fixup_hp_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ const struct dmi_device *dev = NULL;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
+ int pol, pin;
+ if (sscanf(dev->name, "HP_Mute_LED_%d_%x", &pol, &pin) != 2)
+ continue;
+ if (pin < 0x0a || pin >= 0x10)
+ break;
+ spec->mute_led_polarity = pol;
+ spec->mute_led_nid = pin - 0x0a + 0x18;
+ snd_hda_gen_add_mute_led_cdev(codec, vref_mute_led_set);
+ codec->power_filter = led_power_filter;
+ codec_dbg(codec,
+ "Detected mute LED for %x:%d\n", spec->mute_led_nid,
+ spec->mute_led_polarity);
+ break;
+ }
+}
+
+static void alc269_fixup_hp_mute_led_micx(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action, hda_nid_t pin)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 0;
+ spec->mute_led_nid = pin;
+ snd_hda_gen_add_mute_led_cdev(codec, vref_mute_led_set);
+ codec->power_filter = led_power_filter;
+ }
+}
+
+static void alc269_fixup_hp_mute_led_mic1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x18);
+}
+
+static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x19);
+}
+
+static void alc269_fixup_hp_mute_led_mic3(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1b);
+}
+
+static void alc236_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_hp_gpio_led(codec, action, 0x02, 0x01);
+}
+
+static void alc269_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10);
+}
+
+static void alc285_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_hp_gpio_led(codec, action, 0x04, 0x01);
+}
+
+static void alc286_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_hp_gpio_led(codec, action, 0x02, 0x20);
+}
+
+static void alc287_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_hp_gpio_led(codec, action, 0x10, 0);
+}
+
+static void alc245_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->micmute_led_polarity = 1;
+ alc_fixup_hp_gpio_led(codec, action, 0, 0x04);
+}
+
+/* turn on/off mic-mute LED per capture hook via VREF change */
+static int vref_micmute_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
+ struct alc_spec *spec = codec->spec;
+
+ alc_update_vref_led(codec, spec->cap_mute_led_nid,
+ spec->micmute_led_polarity, brightness);
+ return 0;
+}
+
+static void alc269_fixup_hp_gpio_mic1_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ alc_fixup_hp_gpio_led(codec, action, 0x08, 0);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* Like hp_gpio_mic1_led, but also needs GPIO4 low to
+ * enable headphone amp
+ */
+ spec->gpio_mask |= 0x10;
+ spec->gpio_dir |= 0x10;
+ spec->cap_mute_led_nid = 0x18;
+ snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set);
+ codec->power_filter = led_power_filter;
+ }
+}
+
+static void alc280_fixup_hp_gpio4(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ alc_fixup_hp_gpio_led(codec, action, 0x08, 0);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->cap_mute_led_nid = 0x18;
+ snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set);
+ codec->power_filter = led_power_filter;
+ }
+}
+
+/* HP Spectre x360 14 model needs a unique workaround for enabling the amp;
+ * it needs to toggle the GPIO0 once on and off at each time (bko#210633)
+ */
+static void alc245_fixup_hp_x360_amp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gpio_mask |= 0x01;
+ spec->gpio_dir |= 0x01;
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ /* need to toggle GPIO to enable the amp */
+ alc_update_gpio_data(codec, 0x01, true);
+ msleep(100);
+ alc_update_gpio_data(codec, 0x01, false);
+ break;
+ }
+}
+
+/* toggle GPIO2 at each time stream is started; we use PREPARE state instead */
+static void alc274_hp_envy_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ alc_update_gpio_data(codec, 0x04, true);
+ break;
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ alc_update_gpio_data(codec, 0x04, false);
+ break;
+ }
+}
+
+static void alc274_fixup_hp_envy_gpio(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ spec->gpio_mask |= 0x04;
+ spec->gpio_dir |= 0x04;
+ spec->gen.pcm_playback_hook = alc274_hp_envy_pcm_hook;
+ }
+}
+
+static void alc_update_coef_led(struct hda_codec *codec,
+ struct alc_coef_led *led,
+ bool polarity, bool on)
+{
+ if (polarity)
+ on = !on;
+ /* temporarily power up/down for setting COEF bit */
+ alc_update_coef_idx(codec, led->idx, led->mask,
+ on ? led->on : led->off);
+}
+
+/* update mute-LED according to the speaker mute state via COEF bit */
+static int coef_mute_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
+ struct alc_spec *spec = codec->spec;
+
+ alc_update_coef_led(codec, &spec->mute_led_coef,
+ spec->mute_led_polarity, brightness);
+ return 0;
+}
+
+static void alc285_fixup_hp_mute_led_coefbit(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 0;
+ spec->mute_led_coef.idx = 0x0b;
+ spec->mute_led_coef.mask = 1 << 3;
+ spec->mute_led_coef.on = 1 << 3;
+ spec->mute_led_coef.off = 0;
+ snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
+ }
+}
+
+static void alc236_fixup_hp_mute_led_coefbit(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 0;
+ spec->mute_led_coef.idx = 0x34;
+ spec->mute_led_coef.mask = 1 << 5;
+ spec->mute_led_coef.on = 0;
+ spec->mute_led_coef.off = 1 << 5;
+ snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
+ }
+}
+
+static void alc236_fixup_hp_mute_led_coefbit2(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 0;
+ spec->mute_led_coef.idx = 0x07;
+ spec->mute_led_coef.mask = 1;
+ spec->mute_led_coef.on = 1;
+ spec->mute_led_coef.off = 0;
+ snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
+ }
+}
+
+static void alc245_fixup_hp_mute_led_coefbit(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 0;
+ spec->mute_led_coef.idx = 0x0b;
+ spec->mute_led_coef.mask = 3 << 2;
+ spec->mute_led_coef.on = 2 << 2;
+ spec->mute_led_coef.off = 1 << 2;
+ snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
+ }
+}
+
+static void alc245_fixup_hp_mute_led_v1_coefbit(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 0;
+ spec->mute_led_coef.idx = 0x0b;
+ spec->mute_led_coef.mask = 3 << 2;
+ spec->mute_led_coef.on = 1 << 3;
+ spec->mute_led_coef.off = 0;
+ snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
+ }
+}
+
+/* turn on/off mic-mute LED per capture hook by coef bit */
+static int coef_micmute_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
+ struct alc_spec *spec = codec->spec;
+
+ alc_update_coef_led(codec, &spec->mic_led_coef,
+ spec->micmute_led_polarity, brightness);
+ return 0;
+}
+
+static void alc285_fixup_hp_coef_micmute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mic_led_coef.idx = 0x19;
+ spec->mic_led_coef.mask = 1 << 13;
+ spec->mic_led_coef.on = 1 << 13;
+ spec->mic_led_coef.off = 0;
+ snd_hda_gen_add_micmute_led_cdev(codec, coef_micmute_led_set);
+ }
+}
+
+static void alc285_fixup_hp_gpio_micmute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->micmute_led_polarity = 1;
+ alc_fixup_hp_gpio_led(codec, action, 0, 0x04);
+}
+
+static void alc236_fixup_hp_coef_micmute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mic_led_coef.idx = 0x35;
+ spec->mic_led_coef.mask = 3 << 2;
+ spec->mic_led_coef.on = 2 << 2;
+ spec->mic_led_coef.off = 1 << 2;
+ snd_hda_gen_add_micmute_led_cdev(codec, coef_micmute_led_set);
+ }
+}
+
+static void alc295_fixup_hp_mute_led_coefbit11(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 0;
+ spec->mute_led_coef.idx = 0xb;
+ spec->mute_led_coef.mask = 3 << 3;
+ spec->mute_led_coef.on = 1 << 3;
+ spec->mute_led_coef.off = 1 << 4;
+ snd_hda_gen_add_mute_led_cdev(codec, coef_mute_led_set);
+ }
+}
+
+static void alc285_fixup_hp_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc285_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc285_fixup_hp_coef_micmute_led(codec, fix, action);
+}
+
+static void alc285_fixup_hp_spectre_x360_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc285_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc285_fixup_hp_gpio_micmute_led(codec, fix, action);
+}
+
+static void alc236_fixup_hp_mute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc236_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc236_fixup_hp_coef_micmute_led(codec, fix, action);
+}
+
+static void alc236_fixup_hp_micmute_led_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->cap_mute_led_nid = 0x1a;
+ snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set);
+ codec->power_filter = led_power_filter;
+ }
+}
+
+static void alc236_fixup_hp_mute_led_micmute_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc236_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc236_fixup_hp_micmute_led_vref(codec, fix, action);
+}
+
+static inline void alc298_samsung_write_coef_pack(struct hda_codec *codec,
+ const unsigned short coefs[2])
+{
+ alc_write_coef_idx(codec, 0x23, coefs[0]);
+ alc_write_coef_idx(codec, 0x25, coefs[1]);
+ alc_write_coef_idx(codec, 0x26, 0xb011);
+}
+
+struct alc298_samsung_amp_desc {
+ unsigned char nid;
+ unsigned short init_seq[2][2];
+};
+
+static void alc298_fixup_samsung_amp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ int i, j;
+ static const unsigned short init_seq[][2] = {
+ { 0x19, 0x00 }, { 0x20, 0xc0 }, { 0x22, 0x44 }, { 0x23, 0x08 },
+ { 0x24, 0x85 }, { 0x25, 0x41 }, { 0x35, 0x40 }, { 0x36, 0x01 },
+ { 0x38, 0x81 }, { 0x3a, 0x03 }, { 0x3b, 0x81 }, { 0x40, 0x3e },
+ { 0x41, 0x07 }, { 0x400, 0x1 }
+ };
+ static const struct alc298_samsung_amp_desc amps[] = {
+ { 0x3a, { { 0x18, 0x1 }, { 0x26, 0x0 } } },
+ { 0x39, { { 0x18, 0x2 }, { 0x26, 0x1 } } }
+ };
+
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(amps); i++) {
+ alc_write_coef_idx(codec, 0x22, amps[i].nid);
+
+ for (j = 0; j < ARRAY_SIZE(amps[i].init_seq); j++)
+ alc298_samsung_write_coef_pack(codec, amps[i].init_seq[j]);
+
+ for (j = 0; j < ARRAY_SIZE(init_seq); j++)
+ alc298_samsung_write_coef_pack(codec, init_seq[j]);
+ }
+}
+
+struct alc298_samsung_v2_amp_desc {
+ unsigned short nid;
+ int init_seq_size;
+ unsigned short init_seq[18][2];
+};
+
+static const struct alc298_samsung_v2_amp_desc
+alc298_samsung_v2_amp_desc_tbl[] = {
+ { 0x38, 18, {
+ { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 },
+ { 0x201b, 0x0001 }, { 0x201d, 0x0001 }, { 0x201f, 0x00fe },
+ { 0x2021, 0x0000 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 },
+ { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e },
+ { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x2399, 0x0003 },
+ { 0x23a4, 0x00b5 }, { 0x23a5, 0x0001 }, { 0x23ba, 0x0094 }
+ }},
+ { 0x39, 18, {
+ { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 },
+ { 0x201b, 0x0002 }, { 0x201d, 0x0002 }, { 0x201f, 0x00fd },
+ { 0x2021, 0x0001 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 },
+ { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e },
+ { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x2399, 0x0003 },
+ { 0x23a4, 0x00b5 }, { 0x23a5, 0x0001 }, { 0x23ba, 0x0094 }
+ }},
+ { 0x3c, 15, {
+ { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 },
+ { 0x201b, 0x0001 }, { 0x201d, 0x0001 }, { 0x201f, 0x00fe },
+ { 0x2021, 0x0000 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 },
+ { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e },
+ { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x23ba, 0x008d }
+ }},
+ { 0x3d, 15, {
+ { 0x23e1, 0x0000 }, { 0x2012, 0x006f }, { 0x2014, 0x0000 },
+ { 0x201b, 0x0002 }, { 0x201d, 0x0002 }, { 0x201f, 0x00fd },
+ { 0x2021, 0x0001 }, { 0x2022, 0x0010 }, { 0x203d, 0x0005 },
+ { 0x203f, 0x0003 }, { 0x2050, 0x002c }, { 0x2076, 0x000e },
+ { 0x207c, 0x004a }, { 0x2081, 0x0003 }, { 0x23ba, 0x008d }
+ }}
+};
+
+static void alc298_samsung_v2_enable_amps(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ static const unsigned short enable_seq[][2] = {
+ { 0x203a, 0x0081 }, { 0x23ff, 0x0001 },
+ };
+ int i, j;
+
+ for (i = 0; i < spec->num_speaker_amps; i++) {
+ alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid);
+ for (j = 0; j < ARRAY_SIZE(enable_seq); j++)
+ alc298_samsung_write_coef_pack(codec, enable_seq[j]);
+ codec_dbg(codec, "alc298_samsung_v2: Enabled speaker amp 0x%02x\n",
+ alc298_samsung_v2_amp_desc_tbl[i].nid);
+ }
+}
+
+static void alc298_samsung_v2_disable_amps(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ static const unsigned short disable_seq[][2] = {
+ { 0x23ff, 0x0000 }, { 0x203a, 0x0080 },
+ };
+ int i, j;
+
+ for (i = 0; i < spec->num_speaker_amps; i++) {
+ alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid);
+ for (j = 0; j < ARRAY_SIZE(disable_seq); j++)
+ alc298_samsung_write_coef_pack(codec, disable_seq[j]);
+ codec_dbg(codec, "alc298_samsung_v2: Disabled speaker amp 0x%02x\n",
+ alc298_samsung_v2_amp_desc_tbl[i].nid);
+ }
+}
+
+static void alc298_samsung_v2_playback_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ /* Dynamically enable/disable speaker amps before and after playback */
+ if (action == HDA_GEN_PCM_ACT_OPEN)
+ alc298_samsung_v2_enable_amps(codec);
+ if (action == HDA_GEN_PCM_ACT_CLOSE)
+ alc298_samsung_v2_disable_amps(codec);
+}
+
+static void alc298_samsung_v2_init_amps(struct hda_codec *codec,
+ int num_speaker_amps)
+{
+ struct alc_spec *spec = codec->spec;
+ int i, j;
+
+ /* Set spec's num_speaker_amps before doing anything else */
+ spec->num_speaker_amps = num_speaker_amps;
+
+ /* Disable speaker amps before init to prevent any physical damage */
+ alc298_samsung_v2_disable_amps(codec);
+
+ /* Initialize the speaker amps */
+ for (i = 0; i < spec->num_speaker_amps; i++) {
+ alc_write_coef_idx(codec, 0x22, alc298_samsung_v2_amp_desc_tbl[i].nid);
+ for (j = 0; j < alc298_samsung_v2_amp_desc_tbl[i].init_seq_size; j++) {
+ alc298_samsung_write_coef_pack(codec,
+ alc298_samsung_v2_amp_desc_tbl[i].init_seq[j]);
+ }
+ alc_write_coef_idx(codec, 0x89, 0x0);
+ codec_dbg(codec, "alc298_samsung_v2: Initialized speaker amp 0x%02x\n",
+ alc298_samsung_v2_amp_desc_tbl[i].nid);
+ }
+
+ /* register hook to enable speaker amps only when they are needed */
+ spec->gen.pcm_playback_hook = alc298_samsung_v2_playback_hook;
+}
+
+static void alc298_fixup_samsung_amp_v2_2_amps(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PROBE)
+ alc298_samsung_v2_init_amps(codec, 2);
+}
+
+static void alc298_fixup_samsung_amp_v2_4_amps(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PROBE)
+ alc298_samsung_v2_init_amps(codec, 4);
+}
+
+static void gpio2_mic_hotkey_event(struct hda_codec *codec,
+ struct hda_jack_callback *event)
+{
+ struct alc_spec *spec = codec->spec;
+
+ /* GPIO2 just toggles on a keypress/keyrelease cycle. Therefore
+ send both key on and key off event for every interrupt. */
+ input_report_key(spec->kb_dev, spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX], 1);
+ input_sync(spec->kb_dev);
+ input_report_key(spec->kb_dev, spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX], 0);
+ input_sync(spec->kb_dev);
+}
+
+static int alc_register_micmute_input_device(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ spec->kb_dev = input_allocate_device();
+ if (!spec->kb_dev) {
+ codec_err(codec, "Out of memory (input_allocate_device)\n");
+ return -ENOMEM;
+ }
+
+ spec->alc_mute_keycode_map[ALC_KEY_MICMUTE_INDEX] = KEY_MICMUTE;
+
+ spec->kb_dev->name = "Microphone Mute Button";
+ spec->kb_dev->evbit[0] = BIT_MASK(EV_KEY);
+ spec->kb_dev->keycodesize = sizeof(spec->alc_mute_keycode_map[0]);
+ spec->kb_dev->keycodemax = ARRAY_SIZE(spec->alc_mute_keycode_map);
+ spec->kb_dev->keycode = spec->alc_mute_keycode_map;
+ for (i = 0; i < ARRAY_SIZE(spec->alc_mute_keycode_map); i++)
+ set_bit(spec->alc_mute_keycode_map[i], spec->kb_dev->keybit);
+
+ if (input_register_device(spec->kb_dev)) {
+ codec_err(codec, "input_register_device failed\n");
+ input_free_device(spec->kb_dev);
+ spec->kb_dev = NULL;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/* GPIO1 = set according to SKU external amp
+ * GPIO2 = mic mute hotkey
+ * GPIO3 = mute LED
+ * GPIO4 = mic mute LED
+ */
+static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ alc_fixup_hp_gpio_led(codec, action, 0x08, 0x10);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->init_amp = ALC_INIT_DEFAULT;
+ if (alc_register_micmute_input_device(codec) != 0)
+ return;
+
+ spec->gpio_mask |= 0x06;
+ spec->gpio_dir |= 0x02;
+ spec->gpio_data |= 0x02;
+ snd_hda_codec_write_cache(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x04);
+ snd_hda_jack_detect_enable_callback(codec, codec->core.afg,
+ gpio2_mic_hotkey_event);
+ return;
+ }
+
+ if (!spec->kb_dev)
+ return;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_FREE:
+ input_unregister_device(spec->kb_dev);
+ spec->kb_dev = NULL;
+ }
+}
+
+/* Line2 = mic mute hotkey
+ * GPIO2 = mic mute LED
+ */
+static void alc233_fixup_lenovo_line2_mic_hotkey(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ alc_fixup_hp_gpio_led(codec, action, 0, 0x04);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->init_amp = ALC_INIT_DEFAULT;
+ if (alc_register_micmute_input_device(codec) != 0)
+ return;
+
+ snd_hda_jack_detect_enable_callback(codec, 0x1b,
+ gpio2_mic_hotkey_event);
+ return;
+ }
+
+ if (!spec->kb_dev)
+ return;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_FREE:
+ input_unregister_device(spec->kb_dev);
+ spec->kb_dev = NULL;
+ }
+}
+
+static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ alc269_fixup_hp_mute_led_micx(codec, fix, action, 0x1a);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->cap_mute_led_nid = 0x18;
+ snd_hda_gen_add_micmute_led_cdev(codec, vref_micmute_led_set);
+ }
+}
+
+static void alc233_fixup_lenovo_low_en_micmute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->micmute_led_polarity = 1;
+ alc233_fixup_lenovo_line2_mic_hotkey(codec, fix, action);
+}
+
+static void alc255_set_default_jack_type(struct hda_codec *codec)
+{
+ /* Set to iphone type */
+ static const struct coef_fw alc255fw[] = {
+ WRITE_COEF(0x1b, 0x880b),
+ WRITE_COEF(0x45, 0xd089),
+ WRITE_COEF(0x1b, 0x080b),
+ WRITE_COEF(0x46, 0x0004),
+ WRITE_COEF(0x1b, 0x0c0b),
+ {}
+ };
+ static const struct coef_fw alc256fw[] = {
+ WRITE_COEF(0x1b, 0x884b),
+ WRITE_COEF(0x45, 0xd089),
+ WRITE_COEF(0x1b, 0x084b),
+ WRITE_COEF(0x46, 0x0004),
+ WRITE_COEF(0x1b, 0x0c4b),
+ {}
+ };
+ switch (codec->core.vendor_id) {
+ case 0x10ec0255:
+ alc_process_coef_fw(codec, alc255fw);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ alc_process_coef_fw(codec, alc256fw);
+ break;
+ }
+ msleep(30);
+}
+
+static void alc_fixup_headset_mode_alc255(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ alc255_set_default_jack_type(codec);
+ }
+ alc_fixup_headset_mode(codec, fix, action);
+}
+
+static void alc_fixup_headset_mode_alc255_no_hp_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ alc255_set_default_jack_type(codec);
+ }
+ else
+ alc_fixup_headset_mode(codec, fix, action);
+}
+
+static void alc288_update_headset_jack_cb(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct alc_spec *spec = codec->spec;
+
+ alc_update_headset_jack_cb(codec, jack);
+ /* Headset Mic enable or disable, only for Dell Dino */
+ alc_update_gpio_data(codec, 0x40, spec->gen.hp_jack_present);
+}
+
+static void alc_fixup_headset_mode_dell_alc288(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_headset_mode(codec, fix, action);
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ /* toggled via hp_automute_hook */
+ spec->gpio_mask |= 0x40;
+ spec->gpio_dir |= 0x40;
+ spec->gen.hp_automute_hook = alc288_update_headset_jack_cb;
+ }
+}
+
+static void alc_fixup_no_shutup(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->no_shutup_pins = 1;
+ }
+}
+
+/* fixup for Thinkpad docks: add dock pins, avoid HP parser fixup */
+static void alc_fixup_tpt440_dock(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x16, 0x21211010 }, /* dock headphone */
+ { 0x19, 0x21a11010 }, /* dock mic */
+ { }
+ };
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
+ codec->power_save_node = 0; /* avoid click noises */
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ }
+}
+
+static void alc_fixup_tpt470_dock(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x17, 0x21211010 }, /* dock headphone */
+ { 0x19, 0x21a11010 }, /* dock mic */
+ { }
+ };
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ } else if (action == HDA_FIXUP_ACT_INIT) {
+ /* Enable DOCK device */
+ snd_hda_codec_write(codec, 0x17, 0,
+ AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0);
+ /* Enable DOCK device */
+ snd_hda_codec_write(codec, 0x19, 0,
+ AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0);
+ }
+}
+
+static void alc_fixup_tpt470_dacs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /* Assure the speaker pin to be coupled with DAC NID 0x03; otherwise
+ * the speaker output becomes too low by some reason on Thinkpads with
+ * ALC298 codec
+ */
+ static const hda_nid_t preferred_pairs[] = {
+ 0x14, 0x03, 0x17, 0x02, 0x21, 0x02,
+ 0
+ };
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->gen.preferred_dacs = preferred_pairs;
+}
+
+static void alc295_fixup_asus_dacs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const hda_nid_t preferred_pairs[] = {
+ 0x17, 0x02, 0x21, 0x03, 0
+ };
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->gen.preferred_dacs = preferred_pairs;
+}
+
+static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ int mic_pin = alc_find_ext_mic_pin(codec);
+ int hp_pin = alc_get_hp_pin(spec);
+
+ if (snd_BUG_ON(!mic_pin || !hp_pin))
+ return;
+ snd_hda_jack_set_gating_jack(codec, mic_pin, hp_pin);
+ }
+}
+
+static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ int i;
+
+ /* The mic boosts on level 2 and 3 are too noisy
+ on the internal mic input.
+ Therefore limit the boost to 0 or 1. */
+
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ unsigned int defcfg;
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ continue;
+ defcfg = snd_hda_codec_get_pincfg(codec, nid);
+ if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT)
+ continue;
+
+ snd_hda_override_amp_caps(codec, nid, HDA_INPUT,
+ (0x00 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x01 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x2f << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (0 << AC_AMPCAP_MUTE_SHIFT));
+ }
+}
+
+static void alc283_hp_automute_hook(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ int vref;
+
+ msleep(200);
+ snd_hda_gen_hp_automute(codec, jack);
+
+ vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0;
+
+ msleep(600);
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ vref);
+}
+
+static void alc283_fixup_chromebook(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_override_wcaps(codec, 0x03, 0);
+ /* Disable AA-loopback as it causes white noise */
+ spec->gen.mixer_nid = 0;
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ /* MIC2-VREF control */
+ /* Set to manual mode */
+ alc_update_coef_idx(codec, 0x06, 0x000c, 0);
+ /* Enable Line1 input control by verb */
+ alc_update_coef_idx(codec, 0x1a, 0, 1 << 4);
+ break;
+ }
+}
+
+static void alc283_fixup_sense_combo_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gen.hp_automute_hook = alc283_hp_automute_hook;
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ /* MIC2-VREF control */
+ /* Set to manual mode */
+ alc_update_coef_idx(codec, 0x06, 0x000c, 0);
+ break;
+ }
+}
+
+/* mute tablet speaker pin (0x14) via dock plugging in addition */
+static void asus_tx300_automute(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ snd_hda_gen_update_outputs(codec);
+ if (snd_hda_jack_detect(codec, 0x1b))
+ spec->gen.mute_bits |= (1ULL << 0x14);
+}
+
+static void alc282_fixup_asus_tx300(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_pintbl dock_pins[] = {
+ { 0x1b, 0x21114000 }, /* dock speaker pin */
+ {}
+ };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->init_amp = ALC_INIT_DEFAULT;
+ /* TX300 needs to set up GPIO2 for the speaker amp */
+ alc_setup_gpio(codec, 0x04);
+ snd_hda_apply_pincfgs(codec, dock_pins);
+ spec->gen.auto_mute_via_amp = 1;
+ spec->gen.automute_hook = asus_tx300_automute;
+ snd_hda_jack_detect_enable_callback(codec, 0x1b,
+ snd_hda_gen_hp_automute);
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ spec->init_amp = ALC_INIT_DEFAULT;
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ /* this is a bit tricky; give more sane names for the main
+ * (tablet) speaker and the dock speaker, respectively
+ */
+ rename_ctl(codec, "Speaker Playback Switch",
+ "Dock Speaker Playback Switch");
+ rename_ctl(codec, "Bass Speaker Playback Switch",
+ "Speaker Playback Switch");
+ break;
+ }
+}
+
+static void alc290_fixup_mono_speakers(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* DAC node 0x03 is giving mono output. We therefore want to
+ make sure 0x14 (front speaker) and 0x15 (headphones) use the
+ stereo DAC, while leaving 0x17 (bass speaker) for node 0x03. */
+ static const hda_nid_t conn1[] = { 0x0c };
+ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1);
+ snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn1), conn1);
+ }
+}
+
+static void alc298_fixup_speaker_volume(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* The speaker is routed to the Node 0x06 by a mistake, as a result
+ we can't adjust the speaker's volume since this node does not has
+ Amp-out capability. we change the speaker's route to:
+ Node 0x02 (Audio Output) -> Node 0x0c (Audio Mixer) -> Node 0x17 (
+ Pin Complex), since Node 0x02 has Amp-out caps, we can adjust
+ speaker's volume now. */
+
+ static const hda_nid_t conn1[] = { 0x0c };
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn1), conn1);
+ }
+}
+
+/* disable DAC3 (0x06) selection on NID 0x17 as it has no volume amp control */
+static void alc295_fixup_disable_dac3(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ static const hda_nid_t conn[] = { 0x02, 0x03 };
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ }
+}
+
+/* force NID 0x17 (Bass Speaker) to DAC1 to share it with the main speaker */
+static void alc285_fixup_speaker2_to_dac1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ static const hda_nid_t conn[] = { 0x02 };
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ }
+}
+
+/* disable DAC3 (0x06) selection on NID 0x15 - share Speaker/Bass Speaker DAC 0x03 */
+static void alc294_fixup_bass_speaker_15(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ static const hda_nid_t conn[] = { 0x02, 0x03 };
+ snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn), conn);
+ snd_hda_gen_add_micmute_led_cdev(codec, NULL);
+ }
+}
+
+/* Hook to update amp GPIO4 for automute */
+static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct alc_spec *spec = codec->spec;
+
+ snd_hda_gen_hp_automute(codec, jack);
+ /* mute_led_polarity is set to 0, so we pass inverted value here */
+ alc_update_gpio_led(codec, 0x10, spec->mute_led_polarity,
+ !spec->gen.hp_jack_present);
+}
+
+/* Manage GPIOs for HP EliteBook Folio 9480m.
+ *
+ * GPIO4 is the headphone amplifier power control
+ * GPIO3 is the audio output mute indicator LED
+ */
+
+static void alc280_fixup_hp_9480m(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ alc_fixup_hp_gpio_led(codec, action, 0x08, 0);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* amp at GPIO4; toggled via alc280_hp_gpio4_automute_hook() */
+ spec->gpio_mask |= 0x10;
+ spec->gpio_dir |= 0x10;
+ spec->gen.hp_automute_hook = alc280_hp_gpio4_automute_hook;
+ }
+}
+
+static void alc275_fixup_gpio4_off(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gpio_mask |= 0x04;
+ spec->gpio_dir |= 0x04;
+ /* set data bit low */
+ }
+}
+
+/* Quirk for Thinkpad X1 7th and 8th Gen
+ * The following fixed routing needed
+ * DAC1 (NID 0x02) -> Speaker (NID 0x14); some eq applied secretly
+ * DAC2 (NID 0x03) -> Bass (NID 0x17) & Headphone (NID 0x21); sharing a DAC
+ * DAC3 (NID 0x06) -> Unused, due to the lack of volume amp
+ */
+static void alc285_fixup_thinkpad_x1_gen7(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const hda_nid_t conn[] = { 0x02, 0x03 }; /* exclude 0x06 */
+ static const hda_nid_t preferred_pairs[] = {
+ 0x14, 0x02, 0x17, 0x03, 0x21, 0x03, 0
+ };
+ struct alc_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ spec->gen.preferred_dacs = preferred_pairs;
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ /* The generic parser creates somewhat unintuitive volume ctls
+ * with the fixed routing above, and the shared DAC2 may be
+ * confusing for PA.
+ * Rename those to unique names so that PA doesn't touch them
+ * and use only Master volume.
+ */
+ rename_ctl(codec, "Front Playback Volume", "DAC1 Playback Volume");
+ rename_ctl(codec, "Bass Speaker Playback Volume", "DAC2 Playback Volume");
+ break;
+ }
+}
+
+static void alc225_fixup_s3_pop_noise(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ codec->power_save_node = 1;
+}
+
+/* Forcibly assign NID 0x03 to HP/LO while NID 0x02 to SPK for EQ */
+static void alc274_fixup_bind_dacs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const hda_nid_t preferred_pairs[] = {
+ 0x21, 0x03, 0x1b, 0x03, 0x16, 0x02,
+ 0
+ };
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ spec->gen.preferred_dacs = preferred_pairs;
+ spec->gen.auto_mute_via_amp = 1;
+ codec->power_save_node = 0;
+}
+
+/* avoid DAC 0x06 for speaker switch 0x17; it has no volume control */
+static void alc274_fixup_hp_aio_bind_dacs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const hda_nid_t conn[] = { 0x02, 0x03 }; /* exclude 0x06 */
+ /* The speaker is routed to the Node 0x06 by a mistake, thus the
+ * speaker's volume can't be adjusted since the node doesn't have
+ * Amp-out capability. Assure the speaker and lineout pin to be
+ * coupled with DAC NID 0x02.
+ */
+ static const hda_nid_t preferred_pairs[] = {
+ 0x16, 0x02, 0x17, 0x02, 0x21, 0x03, 0
+ };
+ struct alc_spec *spec = codec->spec;
+
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ spec->gen.preferred_dacs = preferred_pairs;
+}
+
+/* avoid DAC 0x06 for bass speaker 0x17; it has no volume control */
+static void alc289_fixup_asus_ga401(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const hda_nid_t preferred_pairs[] = {
+ 0x14, 0x02, 0x17, 0x02, 0x21, 0x03, 0
+ };
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->gen.preferred_dacs = preferred_pairs;
+}
+
+/* The DAC of NID 0x3 will introduce click/pop noise on headphones, so invalidate it */
+static void alc285_fixup_invalidate_dacs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_override_wcaps(codec, 0x03, 0);
+}
+
+static void alc_combo_jack_hp_jd_restart(struct hda_codec *codec)
+{
+ switch (codec->core.vendor_id) {
+ case 0x10ec0274:
+ case 0x10ec0294:
+ case 0x10ec0225:
+ case 0x10ec0295:
+ case 0x10ec0299:
+ alc_update_coef_idx(codec, 0x4a, 0x8000, 1 << 15); /* Reset HP JD */
+ alc_update_coef_idx(codec, 0x4a, 0x8000, 0 << 15);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0235:
+ case 0x10ec0236:
+ case 0x10ec0255:
+ case 0x10ec0256:
+ case 0x10ec0257:
+ case 0x19e58326:
+ alc_update_coef_idx(codec, 0x1b, 0x8000, 1 << 15); /* Reset HP JD */
+ alc_update_coef_idx(codec, 0x1b, 0x8000, 0 << 15);
+ break;
+ }
+}
+
+static void alc295_fixup_chromebook(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->ultra_low_power = true;
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ alc_combo_jack_hp_jd_restart(codec);
+ break;
+ }
+}
+
+static void alc256_fixup_chromebook(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ if (codec->core.subsystem_id == 0x10280d76)
+ spec->gen.suppress_auto_mute = 0;
+ else
+ spec->gen.suppress_auto_mute = 1;
+ spec->gen.suppress_auto_mic = 1;
+ spec->en_3kpull_low = false;
+ break;
+ }
+}
+
+static void alc_fixup_disable_mic_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ);
+}
+
+
+static void alc294_gx502_toggle_output(struct hda_codec *codec,
+ struct hda_jack_callback *cb)
+{
+ /* The Windows driver sets the codec up in a very different way where
+ * it appears to leave 0x10 = 0x8a20 set. For Linux we need to toggle it
+ */
+ if (snd_hda_jack_detect_state(codec, 0x21) == HDA_JACK_PRESENT)
+ alc_write_coef_idx(codec, 0x10, 0x8a20);
+ else
+ alc_write_coef_idx(codec, 0x10, 0x0a20);
+}
+
+static void alc294_fixup_gx502_hp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /* Pin 0x21: headphones/headset mic */
+ if (!is_jack_detectable(codec, 0x21))
+ return;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_jack_detect_enable_callback(codec, 0x21,
+ alc294_gx502_toggle_output);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ /* Make sure to start in a correct state, i.e. if
+ * headphones have been plugged in before powering up the system
+ */
+ alc294_gx502_toggle_output(codec, NULL);
+ break;
+ }
+}
+
+static void alc294_gu502_toggle_output(struct hda_codec *codec,
+ struct hda_jack_callback *cb)
+{
+ /* Windows sets 0x10 to 0x8420 for Node 0x20 which is
+ * responsible from changes between speakers and headphones
+ */
+ if (snd_hda_jack_detect_state(codec, 0x21) == HDA_JACK_PRESENT)
+ alc_write_coef_idx(codec, 0x10, 0x8420);
+ else
+ alc_write_coef_idx(codec, 0x10, 0x0a20);
+}
+
+static void alc294_fixup_gu502_hp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (!is_jack_detectable(codec, 0x21))
+ return;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_jack_detect_enable_callback(codec, 0x21,
+ alc294_gu502_toggle_output);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ alc294_gu502_toggle_output(codec, NULL);
+ break;
+ }
+}
+
+static void alc285_fixup_hp_gpio_amp_init(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
+
+ msleep(100);
+ alc_write_coef_idx(codec, 0x65, 0x0);
+}
+
+static void alc274_fixup_hp_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ switch (action) {
+ case HDA_FIXUP_ACT_INIT:
+ alc_combo_jack_hp_jd_restart(codec);
+ break;
+ }
+}
+
+static void alc_fixup_no_int_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /* Mic RING SLEEVE swap for combo jack */
+ alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12);
+ spec->no_internal_mic_pin = true;
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ alc_combo_jack_hp_jd_restart(codec);
+ break;
+ }
+}
+
+/* GPIO1 = amplifier on/off
+ * GPIO3 = mic mute LED
+ */
+static void alc285_fixup_hp_spectre_x360_eb1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const hda_nid_t conn[] = { 0x02 };
+
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x14, 0x90170110 }, /* front/high speakers */
+ { 0x17, 0x90170130 }, /* back/bass speakers */
+ { }
+ };
+
+ //enable micmute led
+ alc_fixup_hp_gpio_led(codec, action, 0x00, 0x04);
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->micmute_led_polarity = 1;
+ /* needed for amp of back speakers */
+ spec->gpio_mask |= 0x01;
+ spec->gpio_dir |= 0x01;
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ /* share DAC to have unified volume control */
+ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn), conn);
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ /* need to toggle GPIO to enable the amp of back speakers */
+ alc_update_gpio_data(codec, 0x01, true);
+ msleep(100);
+ alc_update_gpio_data(codec, 0x01, false);
+ break;
+ }
+}
+
+/* GPIO1 = amplifier on/off */
+static void alc285_fixup_hp_spectre_x360_df1(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const hda_nid_t conn[] = { 0x02 };
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x14, 0x90170110 }, /* front/high speakers */
+ { 0x17, 0x90170130 }, /* back/bass speakers */
+ { }
+ };
+
+ // enable mute led
+ alc285_fixup_hp_mute_led_coefbit(codec, fix, action);
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /* needed for amp of back speakers */
+ spec->gpio_mask |= 0x01;
+ spec->gpio_dir |= 0x01;
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ /* share DAC to have unified volume control */
+ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn), conn);
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ /* need to toggle GPIO to enable the amp of back speakers */
+ alc_update_gpio_data(codec, 0x01, true);
+ msleep(100);
+ alc_update_gpio_data(codec, 0x01, false);
+ break;
+ }
+}
+
+static void alc285_fixup_hp_spectre_x360(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const hda_nid_t conn[] = { 0x02 };
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x14, 0x90170110 }, /* rear speaker */
+ { }
+ };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ /* force front speaker to DAC1 */
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ break;
+ }
+}
+
+static void alc285_fixup_hp_envy_x360(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ static const struct coef_fw coefs[] = {
+ WRITE_COEF(0x08, 0x6a0c), WRITE_COEF(0x0d, 0xa023),
+ WRITE_COEF(0x10, 0x0320), WRITE_COEF(0x1a, 0x8c03),
+ WRITE_COEF(0x25, 0x1800), WRITE_COEF(0x26, 0x003a),
+ WRITE_COEF(0x28, 0x1dfe), WRITE_COEF(0x29, 0xb014),
+ WRITE_COEF(0x2b, 0x1dfe), WRITE_COEF(0x37, 0xfe15),
+ WRITE_COEF(0x38, 0x7909), WRITE_COEF(0x45, 0xd489),
+ WRITE_COEF(0x46, 0x00f4), WRITE_COEF(0x4a, 0x21e0),
+ WRITE_COEF(0x66, 0x03f0), WRITE_COEF(0x67, 0x1000),
+ WRITE_COEF(0x6e, 0x1005), { }
+ };
+
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x12, 0xb7a60130 }, /* Internal microphone*/
+ { 0x14, 0x90170150 }, /* B&O soundbar speakers */
+ { 0x17, 0x90170153 }, /* Side speakers */
+ { 0x19, 0x03a11040 }, /* Headset microphone */
+ { }
+ };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_apply_pincfgs(codec, pincfgs);
+
+ /* Fixes volume control problem for side speakers */
+ alc295_fixup_disable_dac3(codec, fix, action);
+
+ /* Fixes no sound from headset speaker */
+ snd_hda_codec_amp_stereo(codec, 0x21, HDA_OUTPUT, 0, -1, 0);
+
+ /* Auto-enable headset mic when plugged */
+ snd_hda_jack_set_gating_jack(codec, 0x19, 0x21);
+
+ /* Headset mic volume enhancement */
+ snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREF50);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ alc_process_coef_fw(codec, coefs);
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ rename_ctl(codec, "Bass Speaker Playback Volume",
+ "B&O-Tuned Playback Volume");
+ rename_ctl(codec, "Front Playback Switch",
+ "B&O Soundbar Playback Switch");
+ rename_ctl(codec, "Bass Speaker Playback Switch",
+ "Side Speaker Playback Switch");
+ break;
+ }
+}
+
+static void alc285_fixup_hp_beep(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ codec->beep_just_power_on = true;
+ } else if (action == HDA_FIXUP_ACT_INIT) {
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+ /*
+ * Just enable loopback to internal speaker and headphone jack.
+ * Disable amplification to get about the same beep volume as
+ * was on pure BIOS setup before loading the driver.
+ */
+ alc_update_coef_idx(codec, 0x36, 0x7070, BIT(13));
+
+ snd_hda_enable_beep_device(codec, 1);
+
+#if !IS_ENABLED(CONFIG_INPUT_PCSPKR)
+ dev_warn_once(hda_codec_dev(codec),
+ "enable CONFIG_INPUT_PCSPKR to get PC beeps\n");
+#endif
+#endif
+ }
+}
+
+/* for hda_fixup_thinkpad_acpi() */
+#include "../helpers/thinkpad.c"
+
+static void alc_fixup_thinkpad_acpi(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_no_shutup(codec, fix, action); /* reduce click noise */
+ hda_fixup_thinkpad_acpi(codec, fix, action);
+}
+
+/* for hda_fixup_ideapad_acpi() */
+#include "../helpers/ideapad_hotkey_led.c"
+
+static void alc_fixup_ideapad_acpi(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ hda_fixup_ideapad_acpi(codec, fix, action);
+}
+
+/* Fixup for Lenovo Legion 15IMHg05 speaker output on headset removal. */
+static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gen.suppress_auto_mute = 1;
+ break;
+ }
+}
+
+static void comp_acpi_device_notify(acpi_handle handle, u32 event, void *data)
+{
+ struct hda_codec *cdc = data;
+ struct alc_spec *spec = cdc->spec;
+
+ codec_info(cdc, "ACPI Notification %d\n", event);
+
+ hda_component_acpi_device_notify(&spec->comps, handle, event, data);
+}
+
+static int comp_bind(struct device *dev)
+{
+ struct hda_codec *cdc = dev_to_hda_codec(dev);
+ struct alc_spec *spec = cdc->spec;
+ int ret;
+
+ ret = hda_component_manager_bind(cdc, &spec->comps);
+ if (ret)
+ return ret;
+
+ return hda_component_manager_bind_acpi_notifications(cdc,
+ &spec->comps,
+ comp_acpi_device_notify, cdc);
+}
+
+static void comp_unbind(struct device *dev)
+{
+ struct hda_codec *cdc = dev_to_hda_codec(dev);
+ struct alc_spec *spec = cdc->spec;
+
+ hda_component_manager_unbind_acpi_notifications(cdc, &spec->comps, comp_acpi_device_notify);
+ hda_component_manager_unbind(cdc, &spec->comps);
+}
+
+static const struct component_master_ops comp_master_ops = {
+ .bind = comp_bind,
+ .unbind = comp_unbind,
+};
+
+static void comp_generic_playback_hook(struct hda_pcm_stream *hinfo, struct hda_codec *cdc,
+ struct snd_pcm_substream *sub, int action)
+{
+ struct alc_spec *spec = cdc->spec;
+
+ hda_component_manager_playback_hook(&spec->comps, action);
+}
+
+static void comp_generic_fixup(struct hda_codec *cdc, int action, const char *bus,
+ const char *hid, const char *match_str, int count)
+{
+ struct alc_spec *spec = cdc->spec;
+ int ret;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ ret = hda_component_manager_init(cdc, &spec->comps, count, bus, hid,
+ match_str, &comp_master_ops);
+ if (ret)
+ return;
+
+ spec->gen.pcm_playback_hook = comp_generic_playback_hook;
+ break;
+ case HDA_FIXUP_ACT_FREE:
+ hda_component_manager_free(&spec->comps, &comp_master_ops);
+ break;
+ }
+}
+
+static void find_cirrus_companion_amps(struct hda_codec *cdc)
+{
+ struct device *dev = hda_codec_dev(cdc);
+ struct acpi_device *adev;
+ struct fwnode_handle *fwnode __free(fwnode_handle) = NULL;
+ const char *bus = NULL;
+ static const struct {
+ const char *hid;
+ const char *name;
+ } acpi_ids[] = {{ "CSC3554", "cs35l54-hda" },
+ { "CSC3556", "cs35l56-hda" },
+ { "CSC3557", "cs35l57-hda" }};
+ char *match;
+ int i, count = 0, count_devindex = 0;
+
+ for (i = 0; i < ARRAY_SIZE(acpi_ids); ++i) {
+ adev = acpi_dev_get_first_match_dev(acpi_ids[i].hid, NULL, -1);
+ if (adev)
+ break;
+ }
+ if (!adev) {
+ codec_dbg(cdc, "Did not find ACPI entry for a Cirrus Amp\n");
+ return;
+ }
+
+ count = i2c_acpi_client_count(adev);
+ if (count > 0) {
+ bus = "i2c";
+ } else {
+ count = acpi_spi_count_resources(adev);
+ if (count > 0)
+ bus = "spi";
+ }
+
+ fwnode = fwnode_handle_get(acpi_fwnode_handle(adev));
+ acpi_dev_put(adev);
+
+ if (!bus) {
+ codec_err(cdc, "Did not find any buses for %s\n", acpi_ids[i].hid);
+ return;
+ }
+
+ if (!fwnode) {
+ codec_err(cdc, "Could not get fwnode for %s\n", acpi_ids[i].hid);
+ return;
+ }
+
+ /*
+ * When available the cirrus,dev-index property is an accurate
+ * count of the amps in a system and is used in preference to
+ * the count of bus devices that can contain additional address
+ * alias entries.
+ */
+ count_devindex = fwnode_property_count_u32(fwnode, "cirrus,dev-index");
+ if (count_devindex > 0)
+ count = count_devindex;
+
+ match = devm_kasprintf(dev, GFP_KERNEL, "-%%s:00-%s.%%d", acpi_ids[i].name);
+ if (!match)
+ return;
+ codec_info(cdc, "Found %d %s on %s (%s)\n", count, acpi_ids[i].hid, bus, match);
+ comp_generic_fixup(cdc, HDA_FIXUP_ACT_PRE_PROBE, bus, acpi_ids[i].hid, match, count);
+}
+
+static void cs35l41_fixup_i2c_two(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 2);
+}
+
+static void cs35l41_fixup_i2c_four(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(cdc, action, "i2c", "CSC3551", "-%s:00-cs35l41-hda.%d", 4);
+}
+
+static void cs35l41_fixup_spi_two(struct hda_codec *codec, const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 2);
+}
+
+static void cs35l41_fixup_spi_one(struct hda_codec *codec, const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 1);
+}
+
+static void cs35l41_fixup_spi_four(struct hda_codec *codec, const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(codec, action, "spi", "CSC3551", "-%s:00-cs35l41-hda.%d", 4);
+}
+
+static void alc287_fixup_legion_16achg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix,
+ int action)
+{
+ comp_generic_fixup(cdc, action, "i2c", "CLSA0100", "-%s:00-cs35l41-hda.%d", 2);
+}
+
+static void alc287_fixup_legion_16ithg6_speakers(struct hda_codec *cdc, const struct hda_fixup *fix,
+ int action)
+{
+ comp_generic_fixup(cdc, action, "i2c", "CLSA0101", "-%s:00-cs35l41-hda.%d", 2);
+}
+
+static void alc285_fixup_asus_ga403u(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
+{
+ /*
+ * The same SSID has been re-used in different hardware, they have
+ * different codecs and the newer GA403U has a ALC285.
+ */
+ if (cdc->core.vendor_id != 0x10ec0285)
+ alc_fixup_inv_dmic(cdc, fix, action);
+}
+
+static void tas2781_fixup_tias_i2c(struct hda_codec *cdc,
+ const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(cdc, action, "i2c", "TIAS2781", "-%s:00", 1);
+}
+
+static void tas2781_fixup_spi(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(cdc, action, "spi", "TXNW2781", "-%s:00-tas2781-hda.%d", 2);
+}
+
+static void tas2781_fixup_txnw_i2c(struct hda_codec *cdc,
+ const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(cdc, action, "i2c", "TXNW2781", "-%s:00-tas2781-hda.%d", 1);
+}
+
+static void yoga7_14arb7_fixup_i2c(struct hda_codec *cdc,
+ const struct hda_fixup *fix, int action)
+{
+ comp_generic_fixup(cdc, action, "i2c", "INT8866", "-%s:00", 1);
+}
+
+static void alc256_fixup_acer_sfg16_micmute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_hp_gpio_led(codec, action, 0, 0x04);
+}
+
+
+/* for alc295_fixup_hp_top_speakers */
+#include "../helpers/hp_x360.c"
+
+/* for alc285_fixup_ideapad_s740_coef() */
+#include "../helpers/ideapad_s740.c"
+
+static const struct coef_fw alc256_fixup_set_coef_defaults_coefs[] = {
+ WRITE_COEF(0x10, 0x0020), WRITE_COEF(0x24, 0x0000),
+ WRITE_COEF(0x26, 0x0000), WRITE_COEF(0x29, 0x3000),
+ WRITE_COEF(0x37, 0xfe05), WRITE_COEF(0x45, 0x5089),
+ {}
+};
+
+static void alc256_fixup_set_coef_defaults(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ /*
+ * A certain other OS sets these coeffs to different values. On at least
+ * one TongFang barebone these settings might survive even a cold
+ * reboot. So to restore a clean slate the values are explicitly reset
+ * to default here. Without this, the external microphone is always in a
+ * plugged-in state, while the internal microphone is always in an
+ * unplugged state, breaking the ability to use the internal microphone.
+ */
+ alc_process_coef_fw(codec, alc256_fixup_set_coef_defaults_coefs);
+}
+
+static const struct coef_fw alc233_fixup_no_audio_jack_coefs[] = {
+ WRITE_COEF(0x1a, 0x9003), WRITE_COEF(0x1b, 0x0e2b), WRITE_COEF(0x37, 0xfe06),
+ WRITE_COEF(0x38, 0x4981), WRITE_COEF(0x45, 0xd489), WRITE_COEF(0x46, 0x0074),
+ WRITE_COEF(0x49, 0x0149),
+ {}
+};
+
+static void alc233_fixup_no_audio_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ /*
+ * The audio jack input and output is not detected on the ASRock NUC Box
+ * 1100 series when cold booting without this fix. Warm rebooting from a
+ * certain other OS makes the audio functional, as COEF settings are
+ * preserved in this case. This fix sets these altered COEF values as
+ * the default.
+ */
+ alc_process_coef_fw(codec, alc233_fixup_no_audio_jack_coefs);
+}
+
+static void alc256_fixup_mic_no_presence_and_resume(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ /*
+ * The Clevo NJ51CU comes either with the ALC293 or the ALC256 codec,
+ * but uses the 0x8686 subproduct id in both cases. The ALC256 codec
+ * needs an additional quirk for sound working after suspend and resume.
+ */
+ if (codec->core.vendor_id == 0x10ec0256) {
+ alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+ snd_hda_codec_set_pincfg(codec, 0x19, 0x04a11120);
+ } else {
+ snd_hda_codec_set_pincfg(codec, 0x1a, 0x04a1113c);
+ }
+}
+
+static void alc256_decrease_headphone_amp_val(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ u32 caps;
+ u8 nsteps, offs;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ caps = query_amp_caps(codec, 0x3, HDA_OUTPUT);
+ nsteps = ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) - 10;
+ offs = ((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT) - 10;
+ caps &= ~AC_AMPCAP_NUM_STEPS & ~AC_AMPCAP_OFFSET;
+ caps |= (nsteps << AC_AMPCAP_NUM_STEPS_SHIFT) | (offs << AC_AMPCAP_OFFSET_SHIFT);
+
+ if (snd_hda_override_amp_caps(codec, 0x3, HDA_OUTPUT, caps))
+ codec_warn(codec, "failed to override amp caps for NID 0x3\n");
+}
+
+static void alc_fixup_dell4_mic_no_presence_quiet(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->gen.input_mux;
+ int i;
+
+ alc269_fixup_limit_int_mic_boost(codec, fix, action);
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /**
+ * Set the vref of pin 0x19 (Headset Mic) and pin 0x1b (Headphone Mic)
+ * to Hi-Z to avoid pop noises at startup and when plugging and
+ * unplugging headphones.
+ */
+ snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ);
+ snd_hda_codec_set_pin_target(codec, 0x1b, PIN_VREFHIZ);
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ /**
+ * Make the internal mic (0x12) the default input source to
+ * prevent pop noises on cold boot.
+ */
+ for (i = 0; i < imux->num_items; i++) {
+ if (spec->gen.imux_pins[i] == 0x12) {
+ spec->gen.cur_mux[0] = i;
+ break;
+ }
+ }
+ break;
+ }
+}
+
+static void alc287_fixup_yoga9_14iap7_bass_spk_pin(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /*
+ * The Pin Complex 0x17 for the bass speakers is wrongly reported as
+ * unconnected.
+ */
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x17, 0x90170121 },
+ { }
+ };
+ /*
+ * Avoid DAC 0x06 and 0x08, as they have no volume controls.
+ * DAC 0x02 and 0x03 would be fine.
+ */
+ static const hda_nid_t conn[] = { 0x02, 0x03 };
+ /*
+ * Prefer both speakerbar (0x14) and bass speakers (0x17) connected to DAC 0x02.
+ * Headphones (0x21) are connected to DAC 0x03.
+ */
+ static const hda_nid_t preferred_pairs[] = {
+ 0x14, 0x02,
+ 0x17, 0x02,
+ 0x21, 0x03,
+ 0
+ };
+ struct alc_spec *spec = codec->spec;
+
+ /* Support Audio mute LED and Mic mute LED on keyboard */
+ hda_fixup_ideapad_acpi(codec, fix, action);
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ spec->gen.preferred_dacs = preferred_pairs;
+ break;
+ }
+}
+
+static void alc295_fixup_dell_inspiron_top_speakers(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x14, 0x90170151 },
+ { 0x17, 0x90170150 },
+ { }
+ };
+ static const hda_nid_t conn[] = { 0x02, 0x03 };
+ static const hda_nid_t preferred_pairs[] = {
+ 0x14, 0x02,
+ 0x17, 0x03,
+ 0x21, 0x02,
+ 0
+ };
+ struct alc_spec *spec = codec->spec;
+
+ alc_fixup_no_shutup(codec, fix, action);
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ spec->gen.preferred_dacs = preferred_pairs;
+ break;
+ }
+}
+
+/* Forcibly assign NID 0x03 to HP while NID 0x02 to SPK */
+static void alc287_fixup_bind_dacs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const hda_nid_t conn[] = { 0x02, 0x03 }; /* exclude 0x06 */
+ static const hda_nid_t preferred_pairs[] = {
+ 0x17, 0x02, 0x21, 0x03, 0
+ };
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ spec->gen.preferred_dacs = preferred_pairs;
+ spec->gen.auto_mute_via_amp = 1;
+ if (spec->gen.autocfg.speaker_pins[0] != 0x14) {
+ snd_hda_codec_write_cache(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ 0x0); /* Make sure 0x14 was disable */
+ }
+}
+
+/* Fix none verb table of Headset Mic pin */
+static void alc2xx_fixup_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x19, 0x03a1103c },
+ { }
+ };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12);
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ break;
+ }
+}
+
+static void alc245_fixup_hp_spectre_x360_eu0xxx(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /*
+ * The Pin Complex 0x14 for the treble speakers is wrongly reported as
+ * unconnected.
+ * The Pin Complex 0x17 for the bass speakers has the lowest association
+ * and sequence values so shift it up a bit to squeeze 0x14 in.
+ */
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x14, 0x90170110 }, // top/treble
+ { 0x17, 0x90170111 }, // bottom/bass
+ { }
+ };
+
+ /*
+ * Force DAC 0x02 for the bass speakers 0x17.
+ */
+ static const hda_nid_t conn[] = { 0x02 };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ break;
+ }
+
+ cs35l41_fixup_i2c_two(codec, fix, action);
+ alc245_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc245_fixup_hp_gpio_led(codec, fix, action);
+}
+
+/* some changes for Spectre x360 16, 2024 model */
+static void alc245_fixup_hp_spectre_x360_16_aa0xxx(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /*
+ * The Pin Complex 0x14 for the treble speakers is wrongly reported as
+ * unconnected.
+ * The Pin Complex 0x17 for the bass speakers has the lowest association
+ * and sequence values so shift it up a bit to squeeze 0x14 in.
+ */
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x14, 0x90170110 }, // top/treble
+ { 0x17, 0x90170111 }, // bottom/bass
+ { }
+ };
+
+ /*
+ * Force DAC 0x02 for the bass speakers 0x17.
+ */
+ static const hda_nid_t conn[] = { 0x02 };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /* needed for amp of back speakers */
+ spec->gpio_mask |= 0x01;
+ spec->gpio_dir |= 0x01;
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ /* need to toggle GPIO to enable the amp of back speakers */
+ alc_update_gpio_data(codec, 0x01, true);
+ msleep(100);
+ alc_update_gpio_data(codec, 0x01, false);
+ break;
+ }
+
+ cs35l41_fixup_i2c_two(codec, fix, action);
+ alc245_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc245_fixup_hp_gpio_led(codec, fix, action);
+}
+
+static void alc245_fixup_hp_zbook_firefly_g12a(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const hda_nid_t conn[] = { 0x02 };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gen.auto_mute_via_amp = 1;
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ break;
+ }
+
+ cs35l41_fixup_i2c_two(codec, fix, action);
+ alc245_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc285_fixup_hp_coef_micmute_led(codec, fix, action);
+}
+
+/*
+ * ALC287 PCM hooks
+ */
+static void alc287_alc1318_playback_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ switch (action) {
+ case HDA_GEN_PCM_ACT_OPEN:
+ alc_write_coefex_idx(codec, 0x5a, 0x00, 0x954f); /* write gpio3 to high */
+ break;
+ case HDA_GEN_PCM_ACT_CLOSE:
+ alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */
+ break;
+ }
+}
+
+static void alc287_s4_power_gpio3_default(struct hda_codec *codec)
+{
+ if (is_s4_suspend(codec)) {
+ alc_write_coefex_idx(codec, 0x5a, 0x00, 0x554f); /* write gpio3 as default value */
+ }
+}
+
+static void alc287_fixup_lenovo_thinkpad_with_alc1318(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const struct coef_fw coefs[] = {
+ WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC300),
+ WRITE_COEF(0x28, 0x0001), WRITE_COEF(0x29, 0xb023),
+ WRITE_COEF(0x24, 0x0013), WRITE_COEF(0x25, 0x0000), WRITE_COEF(0x26, 0xC301),
+ WRITE_COEF(0x28, 0x0001), WRITE_COEF(0x29, 0xb023),
+ };
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ alc_update_coef_idx(codec, 0x10, 1<<11, 1<<11);
+ alc_process_coef_fw(codec, coefs);
+ spec->power_hook = alc287_s4_power_gpio3_default;
+ spec->gen.pcm_playback_hook = alc287_alc1318_playback_pcm_hook;
+}
+/* GPIO2: mute led GPIO3: micmute led */
+static void alc245_tas2781_spi_hp_fixup_muteled(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const hda_nid_t conn[] = { 0x02 };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gen.auto_mute_via_amp = 1;
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ break;
+ }
+
+ tas2781_fixup_spi(codec, fix, action);
+ alc_fixup_hp_gpio_led(codec, action, 0x04, 0x0);
+ alc285_fixup_hp_coef_micmute_led(codec, fix, action);
+}
+/* JD2: mute led GPIO3: micmute led */
+static void alc245_tas2781_i2c_hp_fixup_muteled(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const hda_nid_t conn[] = { 0x02 };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gen.auto_mute_via_amp = 1;
+ snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+ break;
+ }
+
+ tas2781_fixup_txnw_i2c(codec, fix, action);
+ alc245_fixup_hp_mute_led_coefbit(codec, fix, action);
+ alc285_fixup_hp_coef_micmute_led(codec, fix, action);
+}
+/*
+ * Clear COEF 0x0d (PCBEEP passthrough) bit 0x40 where BIOS sets it wrongly
+ * at PM resume
+ */
+static void alc283_fixup_dell_hp_resume(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc_write_coef_idx(codec, 0xd, 0x2800);
+}
+
+/* Swap DAC assignments for HP and speaker */
+static void alc288_fixup_surface_swap_dacs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static hda_nid_t preferred_pairs[] = {
+ 0x21, 0x03, 0x14, 0x02, 0
+ };
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ spec->gen.preferred_dacs = preferred_pairs;
+}
+
+enum {
+ ALC269_FIXUP_GPIO2,
+ ALC269_FIXUP_SONY_VAIO,
+ ALC275_FIXUP_SONY_VAIO_GPIO2,
+ ALC269_FIXUP_DELL_M101Z,
+ ALC269_FIXUP_SKU_IGNORE,
+ ALC269_FIXUP_ASUS_G73JW,
+ ALC269_FIXUP_ASUS_N7601ZM_PINS,
+ ALC269_FIXUP_ASUS_N7601ZM,
+ ALC269_FIXUP_LENOVO_EAPD,
+ ALC275_FIXUP_SONY_HWEQ,
+ ALC275_FIXUP_SONY_DISABLE_AAMIX,
+ ALC271_FIXUP_DMIC,
+ ALC269_FIXUP_PCM_44K,
+ ALC269_FIXUP_STEREO_DMIC,
+ ALC269_FIXUP_HEADSET_MIC,
+ ALC269_FIXUP_QUANTA_MUTE,
+ ALC269_FIXUP_LIFEBOOK,
+ ALC269_FIXUP_LIFEBOOK_EXTMIC,
+ ALC269_FIXUP_LIFEBOOK_HP_PIN,
+ ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT,
+ ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC,
+ ALC269_FIXUP_AMIC,
+ ALC269_FIXUP_DMIC,
+ ALC269VB_FIXUP_AMIC,
+ ALC269VB_FIXUP_DMIC,
+ ALC269_FIXUP_HP_MUTE_LED,
+ ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC269_FIXUP_HP_MUTE_LED_MIC2,
+ ALC269_FIXUP_HP_MUTE_LED_MIC3,
+ ALC269_FIXUP_HP_GPIO_LED,
+ ALC269_FIXUP_HP_GPIO_MIC1_LED,
+ ALC269_FIXUP_HP_LINE1_MIC1_LED,
+ ALC269_FIXUP_INV_DMIC,
+ ALC269_FIXUP_LENOVO_DOCK,
+ ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST,
+ ALC269_FIXUP_NO_SHUTUP,
+ ALC286_FIXUP_SONY_MIC_NO_PRESENCE,
+ ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT,
+ ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST,
+ ALC269_FIXUP_DELL2_MIC_NO_PRESENCE,
+ ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
+ ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
+ ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET,
+ ALC269_FIXUP_HEADSET_MODE,
+ ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC,
+ ALC269_FIXUP_ASPIRE_HEADSET_MIC,
+ ALC269_FIXUP_ASUS_X101_FUNC,
+ ALC269_FIXUP_ASUS_X101_VERB,
+ ALC269_FIXUP_ASUS_X101,
+ ALC271_FIXUP_AMIC_MIC2,
+ ALC271_FIXUP_HP_GATE_MIC_JACK,
+ ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572,
+ ALC269_FIXUP_ACER_AC700,
+ ALC269_FIXUP_LIMIT_INT_MIC_BOOST,
+ ALC269VB_FIXUP_ASUS_ZENBOOK,
+ ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A,
+ ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE,
+ ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED,
+ ALC269VB_FIXUP_ORDISSIMO_EVE2,
+ ALC283_FIXUP_CHROME_BOOK,
+ ALC283_FIXUP_SENSE_COMBO_JACK,
+ ALC282_FIXUP_ASUS_TX300,
+ ALC283_FIXUP_INT_MIC,
+ ALC290_FIXUP_MONO_SPEAKERS,
+ ALC290_FIXUP_MONO_SPEAKERS_HSJACK,
+ ALC290_FIXUP_SUBWOOFER,
+ ALC290_FIXUP_SUBWOOFER_HSJACK,
+ ALC295_FIXUP_HP_MUTE_LED_COEFBIT11,
+ ALC269_FIXUP_THINKPAD_ACPI,
+ ALC269_FIXUP_LENOVO_XPAD_ACPI,
+ ALC269_FIXUP_DMIC_THINKPAD_ACPI,
+ ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13,
+ ALC269VC_FIXUP_INFINIX_Y4_MAX,
+ ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO,
+ ALC255_FIXUP_ACER_MIC_NO_PRESENCE,
+ ALC255_FIXUP_ASUS_MIC_NO_PRESENCE,
+ ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST,
+ ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
+ ALC255_FIXUP_HEADSET_MODE,
+ ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC,
+ ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC292_FIXUP_TPT440_DOCK,
+ ALC292_FIXUP_TPT440,
+ ALC283_FIXUP_HEADSET_MIC,
+ ALC255_FIXUP_MIC_MUTE_LED,
+ ALC282_FIXUP_ASPIRE_V5_PINS,
+ ALC269VB_FIXUP_ASPIRE_E1_COEF,
+ ALC280_FIXUP_HP_GPIO4,
+ ALC286_FIXUP_HP_GPIO_LED,
+ ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY,
+ ALC280_FIXUP_HP_DOCK_PINS,
+ ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED,
+ ALC280_FIXUP_HP_9480M,
+ ALC245_FIXUP_HP_X360_AMP,
+ ALC285_FIXUP_HP_SPECTRE_X360_EB1,
+ ALC285_FIXUP_HP_SPECTRE_X360_DF1,
+ ALC285_FIXUP_HP_ENVY_X360,
+ ALC288_FIXUP_DELL_HEADSET_MODE,
+ ALC288_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC288_FIXUP_DELL_XPS_13,
+ ALC288_FIXUP_DISABLE_AAMIX,
+ ALC292_FIXUP_DELL_E7X_AAMIX,
+ ALC292_FIXUP_DELL_E7X,
+ ALC292_FIXUP_DISABLE_AAMIX,
+ ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK,
+ ALC298_FIXUP_ALIENWARE_MIC_NO_PRESENCE,
+ ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE,
+ ALC275_FIXUP_DELL_XPS,
+ ALC293_FIXUP_LENOVO_SPK_NOISE,
+ ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY,
+ ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED,
+ ALC255_FIXUP_DELL_SPK_NOISE,
+ ALC225_FIXUP_DISABLE_MIC_VREF,
+ ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC295_FIXUP_DISABLE_DAC3,
+ ALC285_FIXUP_SPEAKER2_TO_DAC1,
+ ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1,
+ ALC285_FIXUP_ASUS_HEADSET_MIC,
+ ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS,
+ ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1,
+ ALC285_FIXUP_ASUS_I2C_HEADSET_MIC,
+ ALC280_FIXUP_HP_HEADSET_MIC,
+ ALC221_FIXUP_HP_FRONT_MIC,
+ ALC292_FIXUP_TPT460,
+ ALC298_FIXUP_SPK_VOLUME,
+ ALC298_FIXUP_LENOVO_SPK_VOLUME,
+ ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER,
+ ALC269_FIXUP_ATIV_BOOK_8,
+ ALC221_FIXUP_HP_288PRO_MIC_NO_PRESENCE,
+ ALC221_FIXUP_HP_MIC_NO_PRESENCE,
+ ALC256_FIXUP_ASUS_HEADSET_MODE,
+ ALC256_FIXUP_ASUS_MIC,
+ ALC256_FIXUP_ASUS_AIO_GPIO2,
+ ALC233_FIXUP_ASUS_MIC_NO_PRESENCE,
+ ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE,
+ ALC233_FIXUP_LENOVO_MULTI_CODECS,
+ ALC233_FIXUP_ACER_HEADSET_MIC,
+ ALC294_FIXUP_LENOVO_MIC_LOCATION,
+ ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE,
+ ALC225_FIXUP_S3_POP_NOISE,
+ ALC700_FIXUP_INTEL_REFERENCE,
+ ALC274_FIXUP_DELL_BIND_DACS,
+ ALC274_FIXUP_DELL_AIO_LINEOUT_VERB,
+ ALC298_FIXUP_TPT470_DOCK_FIX,
+ ALC298_FIXUP_TPT470_DOCK,
+ ALC255_FIXUP_DUMMY_LINEOUT_VERB,
+ ALC255_FIXUP_DELL_HEADSET_MIC,
+ ALC256_FIXUP_HUAWEI_MACH_WX9_PINS,
+ ALC298_FIXUP_HUAWEI_MBX_STEREO,
+ ALC295_FIXUP_HP_X360,
+ ALC221_FIXUP_HP_HEADSET_MIC,
+ ALC285_FIXUP_LENOVO_HEADPHONE_NOISE,
+ ALC295_FIXUP_HP_AUTO_MUTE,
+ ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE,
+ ALC294_FIXUP_ASUS_MIC,
+ ALC294_FIXUP_ASUS_HEADSET_MIC,
+ ALC294_FIXUP_ASUS_I2C_HEADSET_MIC,
+ ALC294_FIXUP_ASUS_SPK,
+ ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE,
+ ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE,
+ ALC255_FIXUP_ACER_HEADSET_MIC,
+ ALC295_FIXUP_CHROME_BOOK,
+ ALC225_FIXUP_HEADSET_JACK,
+ ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE,
+ ALC225_FIXUP_WYSE_AUTO_MUTE,
+ ALC225_FIXUP_WYSE_DISABLE_MIC_VREF,
+ ALC286_FIXUP_ACER_AIO_HEADSET_MIC,
+ ALC256_FIXUP_ASUS_HEADSET_MIC,
+ ALC256_FIXUP_ASUS_MIC_NO_PRESENCE,
+ ALC255_FIXUP_PREDATOR_SUBWOOFER,
+ ALC299_FIXUP_PREDATOR_SPK,
+ ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE,
+ ALC289_FIXUP_DELL_SPK1,
+ ALC289_FIXUP_DELL_SPK2,
+ ALC289_FIXUP_DUAL_SPK,
+ ALC289_FIXUP_RTK_AMP_DUAL_SPK,
+ ALC294_FIXUP_SPK2_TO_DAC1,
+ ALC294_FIXUP_ASUS_DUAL_SPK,
+ ALC285_FIXUP_THINKPAD_X1_GEN7,
+ ALC285_FIXUP_THINKPAD_HEADSET_JACK,
+ ALC294_FIXUP_ASUS_ALLY,
+ ALC294_FIXUP_ASUS_ALLY_PINS,
+ ALC294_FIXUP_ASUS_ALLY_VERBS,
+ ALC294_FIXUP_ASUS_ALLY_SPEAKER,
+ ALC294_FIXUP_ASUS_HPE,
+ ALC294_FIXUP_ASUS_COEF_1B,
+ ALC294_FIXUP_ASUS_GX502_HP,
+ ALC294_FIXUP_ASUS_GX502_PINS,
+ ALC294_FIXUP_ASUS_GX502_VERBS,
+ ALC294_FIXUP_ASUS_GU502_HP,
+ ALC294_FIXUP_ASUS_GU502_PINS,
+ ALC294_FIXUP_ASUS_GU502_VERBS,
+ ALC294_FIXUP_ASUS_G513_PINS,
+ ALC285_FIXUP_ASUS_G533Z_PINS,
+ ALC285_FIXUP_HP_GPIO_LED,
+ ALC285_FIXUP_HP_MUTE_LED,
+ ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED,
+ ALC285_FIXUP_HP_BEEP_MICMUTE_LED,
+ ALC236_FIXUP_HP_MUTE_LED_COEFBIT2,
+ ALC236_FIXUP_HP_GPIO_LED,
+ ALC236_FIXUP_HP_MUTE_LED,
+ ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF,
+ ALC236_FIXUP_LENOVO_INV_DMIC,
+ ALC298_FIXUP_SAMSUNG_AMP,
+ ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS,
+ ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS,
+ ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET,
+ ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET,
+ ALC295_FIXUP_ASUS_MIC_NO_PRESENCE,
+ ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS,
+ ALC269VC_FIXUP_ACER_HEADSET_MIC,
+ ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE,
+ ALC289_FIXUP_ASUS_GA401,
+ ALC289_FIXUP_ASUS_GA502,
+ ALC256_FIXUP_ACER_MIC_NO_PRESENCE,
+ ALC285_FIXUP_HP_GPIO_AMP_INIT,
+ ALC269_FIXUP_CZC_B20,
+ ALC269_FIXUP_CZC_TMI,
+ ALC269_FIXUP_CZC_L101,
+ ALC269_FIXUP_LEMOTE_A1802,
+ ALC269_FIXUP_LEMOTE_A190X,
+ ALC256_FIXUP_INTEL_NUC8_RUGGED,
+ ALC233_FIXUP_INTEL_NUC8_DMIC,
+ ALC233_FIXUP_INTEL_NUC8_BOOST,
+ ALC256_FIXUP_INTEL_NUC10,
+ ALC255_FIXUP_XIAOMI_HEADSET_MIC,
+ ALC274_FIXUP_HP_MIC,
+ ALC274_FIXUP_HP_HEADSET_MIC,
+ ALC274_FIXUP_HP_ENVY_GPIO,
+ ALC274_FIXUP_ASUS_ZEN_AIO_27,
+ ALC256_FIXUP_ASUS_HPE,
+ ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK,
+ ALC287_FIXUP_HP_GPIO_LED,
+ ALC256_FIXUP_HP_HEADSET_MIC,
+ ALC245_FIXUP_HP_GPIO_LED,
+ ALC236_FIXUP_DELL_AIO_HEADSET_MIC,
+ ALC282_FIXUP_ACER_DISABLE_LINEOUT,
+ ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST,
+ ALC256_FIXUP_ACER_HEADSET_MIC,
+ ALC285_FIXUP_IDEAPAD_S740_COEF,
+ ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST,
+ ALC295_FIXUP_ASUS_DACS,
+ ALC295_FIXUP_HP_OMEN,
+ ALC285_FIXUP_HP_SPECTRE_X360,
+ ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP,
+ ALC623_FIXUP_LENOVO_THINKSTATION_P340,
+ ALC255_FIXUP_ACER_HEADPHONE_AND_MIC,
+ ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST,
+ ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS,
+ ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE,
+ ALC287_FIXUP_YOGA7_14ITL_SPEAKERS,
+ ALC298_FIXUP_LENOVO_C940_DUET7,
+ ALC287_FIXUP_13S_GEN2_SPEAKERS,
+ ALC256_FIXUP_SET_COEF_DEFAULTS,
+ ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE,
+ ALC233_FIXUP_NO_AUDIO_JACK,
+ ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME,
+ ALC285_FIXUP_LEGION_Y9000X_SPEAKERS,
+ ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE,
+ ALC287_FIXUP_LEGION_16ACHG6,
+ ALC287_FIXUP_CS35L41_I2C_2,
+ ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED,
+ ALC287_FIXUP_CS35L41_I2C_4,
+ ALC245_FIXUP_CS35L41_SPI_1,
+ ALC245_FIXUP_CS35L41_SPI_2,
+ ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED,
+ ALC245_FIXUP_CS35L41_SPI_4,
+ ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED,
+ ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED,
+ ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE,
+ ALC287_FIXUP_LEGION_16ITHG6,
+ ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK,
+ ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN,
+ ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN,
+ ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS,
+ ALC236_FIXUP_DELL_DUAL_CODECS,
+ ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI,
+ ALC287_FIXUP_TAS2781_I2C,
+ ALC295_FIXUP_DELL_TAS2781_I2C,
+ ALC245_FIXUP_TAS2781_SPI_2,
+ ALC287_FIXUP_TXNW2781_I2C,
+ ALC287_FIXUP_YOGA7_14ARB7_I2C,
+ ALC245_FIXUP_HP_MUTE_LED_COEFBIT,
+ ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT,
+ ALC245_FIXUP_HP_X360_MUTE_LEDS,
+ ALC287_FIXUP_THINKPAD_I2S_SPK,
+ ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD,
+ ALC2XX_FIXUP_HEADSET_MIC,
+ ALC289_FIXUP_DELL_CS35L41_SPI_2,
+ ALC294_FIXUP_CS35L41_I2C_2,
+ ALC256_FIXUP_ACER_SFG16_MICMUTE_LED,
+ ALC256_FIXUP_HEADPHONE_AMP_VOL,
+ ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX,
+ ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX,
+ ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A,
+ ALC285_FIXUP_ASUS_GA403U,
+ ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC,
+ ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1,
+ ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC,
+ ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1,
+ ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318,
+ ALC256_FIXUP_CHROME_BOOK,
+ ALC245_FIXUP_CLEVO_NOISY_MIC,
+ ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE,
+ ALC233_FIXUP_MEDION_MTL_SPK,
+ ALC294_FIXUP_BASS_SPEAKER_15,
+ ALC283_FIXUP_DELL_HP_RESUME,
+ ALC294_FIXUP_ASUS_CS35L41_SPI_2,
+ ALC274_FIXUP_HP_AIO_BIND_DACS,
+ ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2,
+ ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC,
+ ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1,
+ ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC,
+ ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK,
+ ALC256_FIXUP_VAIO_RPL_MIC_NO_PRESENCE,
+ ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED,
+ ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED,
+ ALC288_FIXUP_SURFACE_SWAP_DACS,
+ ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO,
+};
+
+/* A special fixup for Lenovo C940 and Yoga Duet 7;
+ * both have the very same PCI SSID, and we need to apply different fixups
+ * depending on the codec ID
+ */
+static void alc298_fixup_lenovo_c940_duet7(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ int id;
+
+ if (codec->core.vendor_id == 0x10ec0298)
+ id = ALC298_FIXUP_LENOVO_SPK_VOLUME; /* C940 */
+ else
+ id = ALC287_FIXUP_YOGA7_14ITL_SPEAKERS; /* Duet 7 */
+ __snd_hda_apply_fixup(codec, id, action, 0);
+}
+
+static const struct hda_fixup alc269_fixups[] = {
+ [ALC269_FIXUP_GPIO2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_gpio2,
+ },
+ [ALC269_FIXUP_SONY_VAIO] = {
+ .type = HDA_FIXUP_PINCTLS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x19, PIN_VREFGRD},
+ {}
+ }
+ },
+ [ALC275_FIXUP_SONY_VAIO_GPIO2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc275_fixup_gpio4_off,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_SONY_VAIO
+ },
+ [ALC269_FIXUP_DELL_M101Z] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Enables internal speaker */
+ {0x20, AC_VERB_SET_COEF_INDEX, 13},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x4040},
+ {}
+ }
+ },
+ [ALC269_FIXUP_SKU_IGNORE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_sku_ignore,
+ },
+ [ALC269_FIXUP_ASUS_G73JW] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x99130111 }, /* subwoofer */
+ { }
+ }
+ },
+ [ALC269_FIXUP_ASUS_N7601ZM_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03A11050 },
+ { 0x1a, 0x03A11C30 },
+ { 0x21, 0x03211420 },
+ { }
+ }
+ },
+ [ALC269_FIXUP_ASUS_N7601ZM] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x62},
+ {0x20, AC_VERB_SET_PROC_COEF, 0xa007},
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x10},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x8420},
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x0f},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x7774},
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_ASUS_N7601ZM_PINS,
+ },
+ [ALC269_FIXUP_LENOVO_EAPD] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0},
+ {}
+ }
+ },
+ [ALC275_FIXUP_SONY_HWEQ] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hweq,
+ .chained = true,
+ .chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2
+ },
+ [ALC275_FIXUP_SONY_DISABLE_AAMIX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_SONY_VAIO
+ },
+ [ALC271_FIXUP_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc271_fixup_dmic,
+ },
+ [ALC269_FIXUP_PCM_44K] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_pcm_44k,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_QUANTA_MUTE
+ },
+ [ALC269_FIXUP_STEREO_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_stereo_dmic,
+ },
+ [ALC269_FIXUP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mic,
+ },
+ [ALC269_FIXUP_QUANTA_MUTE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_quanta_mute,
+ },
+ [ALC269_FIXUP_LIFEBOOK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x2101103f }, /* dock line-out */
+ { 0x1b, 0x23a11040 }, /* dock mic-in */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_QUANTA_MUTE
+ },
+ [ALC269_FIXUP_LIFEBOOK_EXTMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1903c }, /* headset mic, with jack detect */
+ { }
+ },
+ },
+ [ALC269_FIXUP_LIFEBOOK_HP_PIN] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x21, 0x0221102f }, /* HP out */
+ { }
+ },
+ },
+ [ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_pincfg_no_hp_to_lineout,
+ },
+ [ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_pincfg_U7x7_headset_mic,
+ },
+ [ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x90170151 }, /* use as internal speaker (LFE) */
+ { 0x1b, 0x90170152 }, /* use as internal speaker (back) */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
+ },
+ [ALC269VC_FIXUP_INFINIX_Y4_MAX] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x90170150 }, /* use as internal speaker */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
+ },
+ [ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x03a19020 }, /* headset mic */
+ { 0x1b, 0x90170150 }, /* speaker */
+ { }
+ },
+ },
+ [ALC269_FIXUP_AMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0121401f }, /* HP out */
+ { 0x18, 0x01a19c20 }, /* mic */
+ { 0x19, 0x99a3092f }, /* int-mic */
+ { }
+ },
+ },
+ [ALC269_FIXUP_DMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x99a3092f }, /* int-mic */
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0121401f }, /* HP out */
+ { 0x18, 0x01a19c20 }, /* mic */
+ { }
+ },
+ },
+ [ALC269VB_FIXUP_AMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x18, 0x01a19c20 }, /* mic */
+ { 0x19, 0x99a3092f }, /* int-mic */
+ { 0x21, 0x0121401f }, /* HP out */
+ { }
+ },
+ },
+ [ALC269VB_FIXUP_DMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x99a3092f }, /* int-mic */
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x18, 0x01a19c20 }, /* mic */
+ { 0x21, 0x0121401f }, /* HP out */
+ { }
+ },
+ },
+ [ALC269_FIXUP_HP_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_mute_led,
+ },
+ [ALC269_FIXUP_HP_MUTE_LED_MIC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_mute_led_mic1,
+ },
+ [ALC269_FIXUP_HP_MUTE_LED_MIC2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_mute_led_mic2,
+ },
+ [ALC269_FIXUP_HP_MUTE_LED_MIC3] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_mute_led_mic3,
+ .chained = true,
+ .chain_id = ALC295_FIXUP_HP_AUTO_MUTE
+ },
+ [ALC269_FIXUP_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_gpio_led,
+ },
+ [ALC269_FIXUP_HP_GPIO_MIC1_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_gpio_mic1_led,
+ },
+ [ALC269_FIXUP_HP_LINE1_MIC1_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_hp_line1_mic1_led,
+ },
+ [ALC269_FIXUP_INV_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic,
+ },
+ [ALC269_FIXUP_NO_SHUTUP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_no_shutup,
+ },
+ [ALC269_FIXUP_LENOVO_DOCK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x23a11040 }, /* dock mic */
+ { 0x1b, 0x2121103f }, /* dock headphone */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT
+ },
+ [ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_LENOVO_DOCK,
+ },
+ [ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_pincfg_no_hp_to_lineout,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
+ [ALC269_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC269_FIXUP_DELL2_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x21014020 }, /* dock line out */
+ { 0x19, 0x21a19030 }, /* dock mic */
+ { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC269_FIXUP_DELL3_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC269_FIXUP_DELL4_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1b, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC269_FIXUP_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_MIC_MUTE_LED
+ },
+ [ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_no_hp_mic,
+ },
+ [ALC269_FIXUP_ASPIRE_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* headset mic w/o jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE,
+ },
+ [ALC286_FIXUP_SONY_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC256_FIXUP_HUAWEI_MACH_WX9_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x12, 0x90a60130},
+ {0x13, 0x40000000},
+ {0x14, 0x90170110},
+ {0x18, 0x411111f0},
+ {0x19, 0x04a11040},
+ {0x1a, 0x411111f0},
+ {0x1b, 0x90170112},
+ {0x1d, 0x40759a05},
+ {0x1e, 0x411111f0},
+ {0x21, 0x04211020},
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_MIC_MUTE_LED
+ },
+ [ALC298_FIXUP_HUAWEI_MBX_STEREO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc298_fixup_huawei_mbx_stereo,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_MIC_MUTE_LED
+ },
+ [ALC269_FIXUP_ASUS_X101_FUNC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_x101_headset_mic,
+ },
+ [ALC269_FIXUP_ASUS_X101_VERB] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x08},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x0310},
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_ASUS_X101_FUNC
+ },
+ [ALC269_FIXUP_ASUS_X101] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x04a1182c }, /* Headset mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_ASUS_X101_VERB
+ },
+ [ALC271_FIXUP_AMIC_MIC2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x19, 0x01a19c20 }, /* mic */
+ { 0x1b, 0x99a7012f }, /* int-mic */
+ { 0x21, 0x0121401f }, /* HP out */
+ { }
+ },
+ },
+ [ALC271_FIXUP_HP_GATE_MIC_JACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc271_hp_gate_mic_jack,
+ .chained = true,
+ .chain_id = ALC271_FIXUP_AMIC_MIC2,
+ },
+ [ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC271_FIXUP_HP_GATE_MIC_JACK,
+ },
+ [ALC269_FIXUP_ACER_AC700] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x99a3092f }, /* int-mic */
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x18, 0x03a11c20 }, /* mic */
+ { 0x1e, 0x0346101e }, /* SPDIF1 */
+ { 0x21, 0x0321101f }, /* HP out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC271_FIXUP_DMIC,
+ },
+ [ALC269_FIXUP_LIMIT_INT_MIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
+ [ALC269VB_FIXUP_ASUS_ZENBOOK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC269VB_FIXUP_DMIC,
+ },
+ [ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* class-D output amp +5dB */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x12 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2800 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC269VB_FIXUP_ASUS_ZENBOOK,
+ },
+ [ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a110f0 }, /* use as headset mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ },
+ [ALC269VB_FIXUP_ORDISSIMO_EVE2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x99a3092f }, /* int-mic */
+ { 0x18, 0x03a11d20 }, /* mic */
+ { 0x19, 0x411111f0 }, /* Unused bogus pin */
+ { }
+ },
+ },
+ [ALC283_FIXUP_CHROME_BOOK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc283_fixup_chromebook,
+ },
+ [ALC283_FIXUP_SENSE_COMBO_JACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc283_fixup_sense_combo_jack,
+ .chained = true,
+ .chain_id = ALC283_FIXUP_CHROME_BOOK,
+ },
+ [ALC282_FIXUP_ASUS_TX300] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc282_fixup_asus_tx300,
+ },
+ [ALC283_FIXUP_INT_MIC] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x1a},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x0011},
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
+ },
+ [ALC290_FIXUP_SUBWOOFER_HSJACK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x90170112 }, /* subwoofer */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC290_FIXUP_MONO_SPEAKERS_HSJACK,
+ },
+ [ALC290_FIXUP_SUBWOOFER] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x90170112 }, /* subwoofer */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC290_FIXUP_MONO_SPEAKERS,
+ },
+ [ALC290_FIXUP_MONO_SPEAKERS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc290_fixup_mono_speakers,
+ },
+ [ALC290_FIXUP_MONO_SPEAKERS_HSJACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc290_fixup_mono_speakers,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
+ },
+ [ALC269_FIXUP_THINKPAD_ACPI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_thinkpad_acpi,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_SKU_IGNORE,
+ },
+ [ALC269_FIXUP_LENOVO_XPAD_ACPI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_ideapad_acpi,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
+ [ALC269_FIXUP_DMIC_THINKPAD_ACPI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
+ [ALC255_FIXUP_ACER_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_HEADSET_MODE
+ },
+ [ALC255_FIXUP_ASUS_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_HEADSET_MODE
+ },
+ [ALC255_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_HEADSET_MODE
+ },
+ [ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC255_FIXUP_DELL2_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC255_FIXUP_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_alc255,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_MIC_MUTE_LED
+ },
+ [ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_alc255_no_hp_mic,
+ },
+ [ALC293_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC292_FIXUP_TPT440_DOCK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_tpt440_dock,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
+ },
+ [ALC292_FIXUP_TPT440] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC292_FIXUP_TPT440_DOCK,
+ },
+ [ALC283_FIXUP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x04a110f0 },
+ { },
+ },
+ },
+ [ALC255_FIXUP_MIC_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_micmute_led,
+ },
+ [ALC282_FIXUP_ASPIRE_V5_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x90a60130 },
+ { 0x14, 0x90170110 },
+ { 0x17, 0x40000008 },
+ { 0x18, 0x411111f0 },
+ { 0x19, 0x01a1913c },
+ { 0x1a, 0x411111f0 },
+ { 0x1b, 0x411111f0 },
+ { 0x1d, 0x40f89b2d },
+ { 0x1e, 0x411111f0 },
+ { 0x21, 0x0321101f },
+ { },
+ },
+ },
+ [ALC269VB_FIXUP_ASPIRE_E1_COEF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269vb_fixup_aspire_e1_coef,
+ },
+ [ALC280_FIXUP_HP_GPIO4] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc280_fixup_hp_gpio4,
+ },
+ [ALC286_FIXUP_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc286_fixup_hp_gpio_led,
+ },
+ [ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc280_fixup_hp_gpio2_mic_hotkey,
+ },
+ [ALC280_FIXUP_HP_DOCK_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x21011020 }, /* line-out */
+ { 0x1a, 0x01a1903c }, /* headset mic */
+ { 0x18, 0x2181103f }, /* line-in */
+ { },
+ },
+ .chained = true,
+ .chain_id = ALC280_FIXUP_HP_GPIO4
+ },
+ [ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x21011020 }, /* line-out */
+ { 0x18, 0x2181103f }, /* line-in */
+ { },
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HP_GPIO_MIC1_LED
+ },
+ [ALC280_FIXUP_HP_9480M] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc280_fixup_hp_9480m,
+ },
+ [ALC245_FIXUP_HP_X360_AMP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_x360_amp,
+ .chained = true,
+ .chain_id = ALC245_FIXUP_HP_GPIO_LED
+ },
+ [ALC288_FIXUP_DELL_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_dell_alc288,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_MIC_MUTE_LED
+ },
+ [ALC288_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC288_FIXUP_DELL_HEADSET_MODE
+ },
+ [ALC288_FIXUP_DISABLE_AAMIX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC288_FIXUP_DELL_XPS_13] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_dell_xps13,
+ .chained = true,
+ .chain_id = ALC288_FIXUP_DISABLE_AAMIX
+ },
+ [ALC292_FIXUP_DISABLE_AAMIX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE
+ },
+ [ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC293_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC292_FIXUP_DELL_E7X_AAMIX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_dell_xps13,
+ .chained = true,
+ .chain_id = ALC292_FIXUP_DISABLE_AAMIX
+ },
+ [ALC292_FIXUP_DELL_E7X] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_micmute_led,
+ /* micmute fixup must be applied at last */
+ .chained_before = true,
+ .chain_id = ALC292_FIXUP_DELL_E7X_AAMIX,
+ },
+ [ALC298_FIXUP_ALIENWARE_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* headset mic w/o jack detect */
+ { }
+ },
+ .chained_before = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE,
+ },
+ [ALC298_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC275_FIXUP_DELL_XPS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Enables internal speaker */
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x1f},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x00c0},
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x30},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x00b1},
+ {}
+ }
+ },
+ [ALC293_FIXUP_LENOVO_SPK_NOISE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+ },
+ [ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc233_fixup_lenovo_line2_mic_hotkey,
+ },
+ [ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc233_fixup_lenovo_low_en_micmute_led,
+ },
+ [ALC233_FIXUP_INTEL_NUC8_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic,
+ .chained = true,
+ .chain_id = ALC233_FIXUP_INTEL_NUC8_BOOST,
+ },
+ [ALC233_FIXUP_INTEL_NUC8_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost
+ },
+ [ALC255_FIXUP_DELL_SPK_NOISE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC225_FIXUP_DISABLE_MIC_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_mic_vref,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC225_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Disable pass-through path for FRONT 14h */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x36 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x57d7 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC225_FIXUP_DISABLE_MIC_VREF
+ },
+ [ALC280_FIXUP_HP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC,
+ },
+ [ALC221_FIXUP_HP_FRONT_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x02a19020 }, /* Front Mic */
+ { }
+ },
+ },
+ [ALC292_FIXUP_TPT460] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_tpt440_dock,
+ .chained = true,
+ .chain_id = ALC293_FIXUP_LENOVO_SPK_NOISE,
+ },
+ [ALC298_FIXUP_SPK_VOLUME] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc298_fixup_speaker_volume,
+ .chained = true,
+ .chain_id = ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE,
+ },
+ [ALC298_FIXUP_LENOVO_SPK_VOLUME] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc298_fixup_speaker_volume,
+ },
+ [ALC295_FIXUP_DISABLE_DAC3] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc295_fixup_disable_dac3,
+ },
+ [ALC285_FIXUP_SPEAKER2_TO_DAC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+ },
+ [ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC245_FIXUP_CS35L41_SPI_2
+ },
+ [ALC285_FIXUP_ASUS_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 },
+ { 0x1b, 0x03a11c30 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC285_FIXUP_ASUS_SPEAKER2_TO_DAC1
+ },
+ [ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x90170120 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC285_FIXUP_ASUS_HEADSET_MIC
+ },
+ [ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC287_FIXUP_CS35L41_I2C_2
+ },
+ [ALC285_FIXUP_ASUS_I2C_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 },
+ { 0x1b, 0x03a11c30 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC285_FIXUP_ASUS_I2C_SPEAKER2_TO_DAC1
+ },
+ [ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x90170151 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC269_FIXUP_ATIV_BOOK_8] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_auto_mute_via_amp,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_NO_SHUTUP
+ },
+ [ALC221_FIXUP_HP_288PRO_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01813030 }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC221_FIXUP_HP_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC256_FIXUP_ASUS_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode,
+ },
+ [ALC256_FIXUP_ASUS_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x13, 0x90a60160 }, /* use as internal mic */
+ { 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE
+ },
+ [ALC256_FIXUP_ASUS_AIO_GPIO2] = {
+ .type = HDA_FIXUP_FUNC,
+ /* Set up GPIO2 for the speaker amp */
+ .v.func = alc_fixup_gpio4,
+ },
+ [ALC233_FIXUP_ASUS_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Enables internal speaker */
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x40},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x8800},
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC233_FIXUP_ASUS_MIC_NO_PRESENCE
+ },
+ [ALC233_FIXUP_LENOVO_MULTI_CODECS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc233_alc662_fixup_lenovo_dual_codecs,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_GPIO2
+ },
+ [ALC233_FIXUP_ACER_HEADSET_MIC] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC233_FIXUP_ASUS_MIC_NO_PRESENCE
+ },
+ [ALC294_FIXUP_LENOVO_MIC_LOCATION] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* Change the mic location from front to right, otherwise there are
+ two front mics with the same name, pulseaudio can't handle them.
+ This is just a temporary workaround, after applying this fixup,
+ there will be one "Front Mic" and one "Mic" in this machine.
+ */
+ { 0x1a, 0x04a19040 },
+ { }
+ },
+ },
+ [ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x0101102f }, /* Rear Headset HP */
+ { 0x19, 0x02a1913c }, /* use as Front headset mic, without its own jack detect */
+ { 0x1a, 0x01a19030 }, /* Rear Headset MIC */
+ { 0x1b, 0x02011020 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC225_FIXUP_S3_POP_NOISE
+ },
+ [ALC225_FIXUP_S3_POP_NOISE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc225_fixup_s3_pop_noise,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC700_FIXUP_INTEL_REFERENCE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Enables internal speaker */
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x45},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x5289},
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x4A},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x001b},
+ {0x58, AC_VERB_SET_COEF_INDEX, 0x00},
+ {0x58, AC_VERB_SET_PROC_COEF, 0x3888},
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x6f},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x2c0b},
+ {}
+ }
+ },
+ [ALC274_FIXUP_DELL_BIND_DACS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc274_fixup_bind_dacs,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC274_FIXUP_DELL_AIO_LINEOUT_VERB] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x0401102f },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC274_FIXUP_DELL_BIND_DACS
+ },
+ [ALC298_FIXUP_TPT470_DOCK_FIX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_tpt470_dock,
+ .chained = true,
+ .chain_id = ALC293_FIXUP_LENOVO_SPK_NOISE
+ },
+ [ALC298_FIXUP_TPT470_DOCK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_tpt470_dacs,
+ .chained = true,
+ .chain_id = ALC298_FIXUP_TPT470_DOCK_FIX
+ },
+ [ALC255_FIXUP_DUMMY_LINEOUT_VERB] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x0201101f },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC255_FIXUP_DELL_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC295_FIXUP_HP_X360] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc295_fixup_hp_top_speakers,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HP_MUTE_LED_MIC3
+ },
+ [ALC221_FIXUP_HP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x0181313f},
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC285_FIXUP_LENOVO_HEADPHONE_NOISE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_invalidate_dacs,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+ },
+ [ALC295_FIXUP_HP_AUTO_MUTE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_auto_mute_via_amp,
+ },
+ [ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC294_FIXUP_ASUS_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x13, 0x90a60160 }, /* use as internal mic */
+ { 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC294_FIXUP_ASUS_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1103c }, /* use as headset mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC294_FIXUP_ASUS_I2C_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a19020 }, /* use as headset mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC287_FIXUP_CS35L41_I2C_2
+ },
+ [ALC294_FIXUP_ASUS_SPK] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Set EAPD high */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x40 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x8800 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x7774 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC
+ },
+ [ALC295_FIXUP_CHROME_BOOK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc295_fixup_chromebook,
+ .chained = true,
+ .chain_id = ALC225_FIXUP_HEADSET_JACK
+ },
+ [ALC225_FIXUP_HEADSET_JACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_jack,
+ },
+ [ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Disable PCBEEP-IN passthrough */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x36 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x57d7 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC285_FIXUP_LENOVO_HEADPHONE_NOISE
+ },
+ [ALC255_FIXUP_ACER_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11130 },
+ { 0x1a, 0x90a60140 }, /* use as internal mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x01011020 }, /* Rear Line out */
+ { 0x19, 0x01a1913c }, /* use as Front headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC225_FIXUP_WYSE_AUTO_MUTE
+ },
+ [ALC225_FIXUP_WYSE_AUTO_MUTE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_auto_mute_via_amp,
+ .chained = true,
+ .chain_id = ALC225_FIXUP_WYSE_DISABLE_MIC_VREF
+ },
+ [ALC225_FIXUP_WYSE_DISABLE_MIC_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_mic_vref,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC286_FIXUP_ACER_AIO_HEADSET_MIC] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x4f },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x5029 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE
+ },
+ [ALC256_FIXUP_ASUS_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11020 }, /* headset mic with jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE
+ },
+ [ALC256_FIXUP_ASUS_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE
+ },
+ [ALC255_FIXUP_PREDATOR_SUBWOOFER] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x90170151 }, /* use as internal speaker (LFE) */
+ { 0x1b, 0x90170152 } /* use as internal speaker (back) */
+ }
+ },
+ [ALC299_FIXUP_PREDATOR_SPK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x21, 0x90170150 }, /* use as headset mic, without its own jack detect */
+ { }
+ }
+ },
+ [ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_i2c_two,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_PREDATOR_SUBWOOFER
+ },
+ [ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x04a11040 },
+ { 0x21, 0x04211020 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE
+ },
+ [ALC289_FIXUP_DELL_SPK1] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x90170140 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE
+ },
+ [ALC289_FIXUP_DELL_SPK2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x90170130 }, /* bass spk */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE
+ },
+ [ALC289_FIXUP_DUAL_SPK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC289_FIXUP_DELL_SPK2
+ },
+ [ALC289_FIXUP_RTK_AMP_DUAL_SPK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC289_FIXUP_DELL_SPK1
+ },
+ [ALC294_FIXUP_SPK2_TO_DAC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC
+ },
+ [ALC294_FIXUP_ASUS_DUAL_SPK] = {
+ .type = HDA_FIXUP_FUNC,
+ /* The GPIO must be pulled to initialize the AMP */
+ .v.func = alc_fixup_gpio4,
+ .chained = true,
+ .chain_id = ALC294_FIXUP_SPK2_TO_DAC1
+ },
+ [ALC294_FIXUP_ASUS_ALLY] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_i2c_two,
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_ALLY_PINS
+ },
+ [ALC294_FIXUP_ASUS_ALLY_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 },
+ { 0x1a, 0x03a11c30 },
+ { 0x21, 0x03211420 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_ALLY_VERBS
+ },
+ [ALC294_FIXUP_ASUS_ALLY_VERBS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x46 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0004 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x47 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xa47a },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x49 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0049},
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x4a },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x201b },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x6b },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x4278},
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_ALLY_SPEAKER
+ },
+ [ALC294_FIXUP_ASUS_ALLY_SPEAKER] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ },
+ [ALC285_FIXUP_THINKPAD_X1_GEN7] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_thinkpad_x1_gen7,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+ },
+ [ALC285_FIXUP_THINKPAD_HEADSET_JACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_jack,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_THINKPAD_X1_GEN7
+ },
+ [ALC294_FIXUP_ASUS_HPE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Set EAPD high */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x7774 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC
+ },
+ [ALC294_FIXUP_ASUS_GX502_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 }, /* front HP mic */
+ { 0x1a, 0x01a11830 }, /* rear external mic */
+ { 0x21, 0x03211020 }, /* front HP out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_GX502_VERBS
+ },
+ [ALC294_FIXUP_ASUS_GX502_VERBS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* set 0x15 to HP-OUT ctrl */
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+ /* unmute the 0x15 amp */
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_GX502_HP
+ },
+ [ALC294_FIXUP_ASUS_GX502_HP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc294_fixup_gx502_hp,
+ },
+ [ALC295_FIXUP_DELL_TAS2781_I2C] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = tas2781_fixup_tias_i2c,
+ .chained = true,
+ .chain_id = ALC289_FIXUP_DUAL_SPK
+ },
+ [ALC294_FIXUP_ASUS_GU502_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a11050 }, /* rear HP mic */
+ { 0x1a, 0x01a11830 }, /* rear external mic */
+ { 0x21, 0x012110f0 }, /* rear HP out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_GU502_VERBS
+ },
+ [ALC294_FIXUP_ASUS_GU502_VERBS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* set 0x15 to HP-OUT ctrl */
+ { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+ /* unmute the 0x15 amp */
+ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 },
+ /* set 0x1b to HP-OUT */
+ { 0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_GU502_HP
+ },
+ [ALC294_FIXUP_ASUS_GU502_HP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc294_fixup_gu502_hp,
+ },
+ [ALC294_FIXUP_ASUS_G513_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 }, /* front HP mic */
+ { 0x1a, 0x03a11c30 }, /* rear external mic */
+ { 0x21, 0x03211420 }, /* front HP out */
+ { }
+ },
+ },
+ [ALC285_FIXUP_ASUS_G533Z_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x90170152 }, /* Speaker Surround Playback Switch */
+ { 0x19, 0x03a19020 }, /* Mic Boost Volume */
+ { 0x1a, 0x03a11c30 }, /* Mic Boost Volume */
+ { 0x1e, 0x90170151 }, /* Rear jack, IN OUT EAPD Detect */
+ { 0x21, 0x03211420 },
+ { }
+ },
+ },
+ [ALC294_FIXUP_ASUS_COEF_1B] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Set bit 10 to correct noisy output after reboot from
+ * Windows 10 (due to pop noise reduction?)
+ */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x1b },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x4e4b },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC289_FIXUP_ASUS_GA401,
+ },
+ [ALC285_FIXUP_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_gpio_led,
+ },
+ [ALC285_FIXUP_HP_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_mute_led,
+ },
+ [ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_spectre_x360_mute_led,
+ },
+ [ALC285_FIXUP_HP_BEEP_MICMUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_beep,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_MUTE_LED,
+ },
+ [ALC236_FIXUP_HP_MUTE_LED_COEFBIT2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc236_fixup_hp_mute_led_coefbit2,
+ },
+ [ALC236_FIXUP_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc236_fixup_hp_gpio_led,
+ },
+ [ALC236_FIXUP_HP_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc236_fixup_hp_mute_led,
+ },
+ [ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc236_fixup_hp_mute_led_micmute_vref,
+ },
+ [ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc236_fixup_hp_mute_led_coefbit2,
+ .chained = true,
+ .chain_id = ALC236_FIXUP_HP_GPIO_LED,
+ },
+ [ALC236_FIXUP_LENOVO_INV_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic,
+ .chained = true,
+ .chain_id = ALC283_FIXUP_INT_MIC,
+ },
+ [ALC295_FIXUP_HP_MUTE_LED_COEFBIT11] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc295_fixup_hp_mute_led_coefbit11,
+ },
+ [ALC298_FIXUP_SAMSUNG_AMP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc298_fixup_samsung_amp,
+ .chained = true,
+ .chain_id = ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET
+ },
+ [ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc298_fixup_samsung_amp_v2_2_amps
+ },
+ [ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc298_fixup_samsung_amp_v2_4_amps
+ },
+ [ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc5 },
+ { }
+ },
+ },
+ [ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x08},
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2fcf},
+ { }
+ },
+ },
+ [ALC295_FIXUP_ASUS_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x90100120 }, /* use as internal speaker */
+ { 0x18, 0x02a111f0 }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01011020 }, /* use as line out */
+ { },
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC269VC_FIXUP_ACER_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x02a11030 }, /* use as headset mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a11130 }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC289_FIXUP_ASUS_GA401] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc289_fixup_asus_ga401,
+ .chained = true,
+ .chain_id = ALC289_FIXUP_ASUS_GA502,
+ },
+ [ALC289_FIXUP_ASUS_GA502] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11020 }, /* headset mic with jack detect */
+ { }
+ },
+ },
+ [ALC256_FIXUP_ACER_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x02a11120 }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE
+ },
+ [ALC285_FIXUP_HP_GPIO_AMP_INIT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_gpio_amp_init,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_GPIO_LED
+ },
+ [ALC269_FIXUP_CZC_B20] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x411111f0 },
+ { 0x14, 0x90170110 }, /* speaker */
+ { 0x15, 0x032f1020 }, /* HP out */
+ { 0x17, 0x411111f0 },
+ { 0x18, 0x03ab1040 }, /* mic */
+ { 0x19, 0xb7a7013f },
+ { 0x1a, 0x0181305f },
+ { 0x1b, 0x411111f0 },
+ { 0x1d, 0x411111f0 },
+ { 0x1e, 0x411111f0 },
+ { }
+ },
+ .chain_id = ALC269_FIXUP_DMIC,
+ },
+ [ALC269_FIXUP_CZC_TMI] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x4000c000 },
+ { 0x14, 0x90170110 }, /* speaker */
+ { 0x15, 0x0421401f }, /* HP out */
+ { 0x17, 0x411111f0 },
+ { 0x18, 0x04a19020 }, /* mic */
+ { 0x19, 0x411111f0 },
+ { 0x1a, 0x411111f0 },
+ { 0x1b, 0x411111f0 },
+ { 0x1d, 0x40448505 },
+ { 0x1e, 0x411111f0 },
+ { 0x20, 0x8000ffff },
+ { }
+ },
+ .chain_id = ALC269_FIXUP_DMIC,
+ },
+ [ALC269_FIXUP_CZC_L101] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x40000000 },
+ { 0x14, 0x01014010 }, /* speaker */
+ { 0x15, 0x411111f0 }, /* HP out */
+ { 0x16, 0x411111f0 },
+ { 0x18, 0x01a19020 }, /* mic */
+ { 0x19, 0x02a19021 },
+ { 0x1a, 0x0181302f },
+ { 0x1b, 0x0221401f },
+ { 0x1c, 0x411111f0 },
+ { 0x1d, 0x4044c601 },
+ { 0x1e, 0x411111f0 },
+ { }
+ },
+ .chain_id = ALC269_FIXUP_DMIC,
+ },
+ [ALC269_FIXUP_LEMOTE_A1802] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0x40000000 },
+ { 0x14, 0x90170110 }, /* speaker */
+ { 0x17, 0x411111f0 },
+ { 0x18, 0x03a19040 }, /* mic1 */
+ { 0x19, 0x90a70130 }, /* mic2 */
+ { 0x1a, 0x411111f0 },
+ { 0x1b, 0x411111f0 },
+ { 0x1d, 0x40489d2d },
+ { 0x1e, 0x411111f0 },
+ { 0x20, 0x0003ffff },
+ { 0x21, 0x03214020 },
+ { }
+ },
+ .chain_id = ALC269_FIXUP_DMIC,
+ },
+ [ALC269_FIXUP_LEMOTE_A190X] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0121401f }, /* HP out */
+ { 0x18, 0x01a19c20 }, /* rear mic */
+ { 0x19, 0x99a3092f }, /* front mic */
+ { 0x1b, 0x0201401f }, /* front lineout */
+ { }
+ },
+ .chain_id = ALC269_FIXUP_DMIC,
+ },
+ [ALC256_FIXUP_INTEL_NUC8_RUGGED] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC256_FIXUP_INTEL_NUC10] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC255_FIXUP_XIAOMI_HEADSET_MIC] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC289_FIXUP_ASUS_GA502
+ },
+ [ALC274_FIXUP_HP_MIC] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 },
+ { }
+ },
+ },
+ [ALC274_FIXUP_HP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc274_fixup_hp_headset_mic,
+ .chained = true,
+ .chain_id = ALC274_FIXUP_HP_MIC
+ },
+ [ALC274_FIXUP_HP_ENVY_GPIO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc274_fixup_hp_envy_gpio,
+ },
+ [ALC274_FIXUP_ASUS_ZEN_AIO_27] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x10 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xc420 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x40 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x8800 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x49 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0249 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x4a },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x202b },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x62 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xa007 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x6b },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x5060 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC2XX_FIXUP_HEADSET_MIC,
+ },
+ [ALC256_FIXUP_ASUS_HPE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Set EAPD high */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x7778 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC
+ },
+ [ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_jack,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+ },
+ [ALC287_FIXUP_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_hp_gpio_led,
+ },
+ [ALC256_FIXUP_HP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc274_fixup_hp_headset_mic,
+ },
+ [ALC236_FIXUP_DELL_AIO_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_no_int_mic,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC282_FIXUP_ACER_DISABLE_LINEOUT] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x411111f0 },
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { },
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_ACER_MIC_NO_PRESENCE,
+ },
+ [ALC256_FIXUP_ACER_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x02a1113c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x90a1092f }, /* use as internal mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC285_FIXUP_IDEAPAD_S740_COEF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_ideapad_s740_coef,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
+ [ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_MUTE_LED,
+ },
+ [ALC295_FIXUP_ASUS_DACS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc295_fixup_asus_dacs,
+ },
+ [ALC295_FIXUP_HP_OMEN] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x12, 0xb7a60130 },
+ { 0x13, 0x40000000 },
+ { 0x14, 0x411111f0 },
+ { 0x16, 0x411111f0 },
+ { 0x17, 0x90170110 },
+ { 0x18, 0x411111f0 },
+ { 0x19, 0x02a11030 },
+ { 0x1a, 0x411111f0 },
+ { 0x1b, 0x04a19030 },
+ { 0x1d, 0x40600001 },
+ { 0x1e, 0x411111f0 },
+ { 0x21, 0x03211020 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HP_LINE1_MIC1_LED,
+ },
+ [ALC285_FIXUP_HP_SPECTRE_X360] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_spectre_x360,
+ },
+ [ALC285_FIXUP_HP_SPECTRE_X360_EB1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_spectre_x360_eb1
+ },
+ [ALC285_FIXUP_HP_SPECTRE_X360_DF1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_spectre_x360_df1
+ },
+ [ALC285_FIXUP_HP_ENVY_X360] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_hp_envy_x360,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_GPIO_AMP_INIT,
+ },
+ [ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_ideapad_s740_coef,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK,
+ },
+ [ALC623_FIXUP_LENOVO_THINKSTATION_P340] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_no_shutup,
+ .chained = true,
+ .chain_id = ALC283_FIXUP_HEADSET_MIC,
+ },
+ [ALC255_FIXUP_ACER_HEADPHONE_AND_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x21, 0x03211030 }, /* Change the Headphone location to Left */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_XIAOMI_HEADSET_MIC
+ },
+ [ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF,
+ },
+ [ALC285_FIXUP_LEGION_Y9000X_SPEAKERS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_ideapad_s740_coef,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE,
+ },
+ [ALC285_FIXUP_LEGION_Y9000X_AUTOMUTE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_legion_15imhg05_speakers,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
+ [ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS] = {
+ .type = HDA_FIXUP_VERBS,
+ //.v.verbs = legion_15imhg05_coefs,
+ .v.verbs = (const struct hda_verb[]) {
+ // set left speaker Legion 7i.
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x41 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xc },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x1a },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ // set right speaker Legion 7i.
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x42 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xc },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2a },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE,
+ },
+ [ALC287_FIXUP_LEGION_15IMHG05_AUTOMUTE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_legion_15imhg05_speakers,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE,
+ },
+ [ALC287_FIXUP_YOGA7_14ITL_SPEAKERS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ // set left speaker Yoga 7i.
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x41 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xc },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x1a },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ // set right speaker Yoga 7i.
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x46 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xc },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2a },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE,
+ },
+ [ALC298_FIXUP_LENOVO_C940_DUET7] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc298_fixup_lenovo_c940_duet7,
+ },
+ [ALC287_FIXUP_13S_GEN2_SPEAKERS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x41 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x42 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE,
+ },
+ [ALC256_FIXUP_SET_COEF_DEFAULTS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc256_fixup_set_coef_defaults,
+ },
+ [ALC245_FIXUP_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_gpio_led,
+ },
+ [ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11120 }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC,
+ },
+ [ALC233_FIXUP_NO_AUDIO_JACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc233_fixup_no_audio_jack,
+ },
+ [ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc256_fixup_mic_no_presence_and_resume,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC287_FIXUP_LEGION_16ACHG6] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_legion_16achg6_speakers,
+ },
+ [ALC287_FIXUP_CS35L41_I2C_2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_i2c_two,
+ },
+ [ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_i2c_two,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_MUTE_LED,
+ },
+ [ALC287_FIXUP_CS35L41_I2C_4] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_i2c_four,
+ },
+ [ALC245_FIXUP_CS35L41_SPI_2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_spi_two,
+ },
+ [ALC245_FIXUP_CS35L41_SPI_1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_spi_one,
+ },
+ [ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_spi_two,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_GPIO_LED,
+ },
+ [ALC245_FIXUP_CS35L41_SPI_4] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_spi_four,
+ },
+ [ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_spi_four,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_GPIO_LED,
+ },
+ [ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x19 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x8e11 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_MUTE_LED,
+ },
+ [ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_dell4_mic_no_presence_quiet,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
+ },
+ [ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x02a1112c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
+ },
+ [ALC287_FIXUP_LEGION_16ITHG6] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_legion_16ithg6_speakers,
+ },
+ [ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ // enable left speaker
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x41 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xc },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x1a },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xf },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x42 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x10 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x40 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ // enable right speaker
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x24 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x46 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xc },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2a },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xf },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x46 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x10 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x44 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x2 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xb020 },
+
+ { },
+ },
+ },
+ [ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_yoga9_14iap7_bass_spk_pin,
+ .chained = true,
+ .chain_id = ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK,
+ },
+ [ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_yoga9_14iap7_bass_spk_pin,
+ .chained = true,
+ .chain_id = ALC287_FIXUP_CS35L41_I2C_2,
+ },
+ [ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc295_fixup_dell_inspiron_top_speakers,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
+ },
+ [ALC236_FIXUP_DELL_DUAL_CODECS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.func = alc1220_fixup_gb_dual_codecs,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ },
+ [ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_i2c_two,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK,
+ },
+ [ALC287_FIXUP_TAS2781_I2C] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = tas2781_fixup_tias_i2c,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK,
+ },
+ [ALC245_FIXUP_TAS2781_SPI_2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = tas2781_fixup_spi,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_GPIO_LED,
+ },
+ [ALC287_FIXUP_TXNW2781_I2C] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = tas2781_fixup_txnw_i2c,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK,
+ },
+ [ALC287_FIXUP_YOGA7_14ARB7_I2C] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = yoga7_14arb7_fixup_i2c,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK,
+ },
+ [ALC245_FIXUP_HP_MUTE_LED_COEFBIT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_mute_led_coefbit,
+ },
+ [ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_mute_led_v1_coefbit,
+ },
+ [ALC245_FIXUP_HP_X360_MUTE_LEDS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_mute_led_coefbit,
+ .chained = true,
+ .chain_id = ALC245_FIXUP_HP_GPIO_LED
+ },
+ [ALC287_FIXUP_THINKPAD_I2S_SPK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_bind_dacs,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK,
+ },
+ [ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_bind_dacs,
+ .chained = true,
+ .chain_id = ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI,
+ },
+ [ALC2XX_FIXUP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc2xx_fixup_headset_mic,
+ },
+ [ALC289_FIXUP_DELL_CS35L41_SPI_2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_spi_two,
+ .chained = true,
+ .chain_id = ALC289_FIXUP_DUAL_SPK
+ },
+ [ALC294_FIXUP_CS35L41_I2C_2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_i2c_two,
+ },
+ [ALC256_FIXUP_ACER_SFG16_MICMUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc256_fixup_acer_sfg16_micmute_led,
+ },
+ [ALC256_FIXUP_HEADPHONE_AMP_VOL] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc256_decrease_headphone_amp_val,
+ },
+ [ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_spectre_x360_eu0xxx,
+ },
+ [ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_spectre_x360_16_aa0xxx,
+ },
+ [ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_fixup_hp_zbook_firefly_g12a,
+ },
+ [ALC285_FIXUP_ASUS_GA403U] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_asus_ga403u,
+ },
+ [ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 },
+ { 0x1b, 0x03a11c30 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1
+ },
+ [ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC,
+ },
+ [ALC285_FIXUP_ASUS_GU605_SPI_2_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 },
+ { 0x1b, 0x03a11c30 },
+ { }
+ },
+ },
+ [ALC285_FIXUP_ASUS_GA403U_I2C_SPEAKER2_TO_DAC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ .chained = true,
+ .chain_id = ALC285_FIXUP_ASUS_GA403U,
+ },
+ [ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc287_fixup_lenovo_thinkpad_with_alc1318,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+ },
+ [ALC256_FIXUP_CHROME_BOOK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc256_fixup_chromebook,
+ .chained = true,
+ .chain_id = ALC225_FIXUP_HEADSET_JACK
+ },
+ [ALC245_FIXUP_CLEVO_NOISY_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE,
+ },
+ [ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a1113c }, /* use as headset mic, without its own jack detect */
+ { 0x1b, 0x20a11040 }, /* dock mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
+ },
+ [ALC233_FIXUP_MEDION_MTL_SPK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x90170110 },
+ { }
+ },
+ },
+ [ALC294_FIXUP_BASS_SPEAKER_15] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc294_fixup_bass_speaker_15,
+ },
+ [ALC283_FIXUP_DELL_HP_RESUME] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc283_fixup_dell_hp_resume,
+ },
+ [ALC294_FIXUP_ASUS_CS35L41_SPI_2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs35l41_fixup_spi_two,
+ .chained = true,
+ .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC,
+ },
+ [ALC274_FIXUP_HP_AIO_BIND_DACS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc274_fixup_hp_aio_bind_dacs,
+ },
+ [ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 },
+ { 0x1b, 0x03a11c30 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1
+ },
+ [ALC285_FIXUP_ASUS_GA605K_I2C_SPEAKER2_TO_DAC1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc285_fixup_speaker2_to_dac1,
+ },
+ [ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc269_fixup_limit_int_mic_boost,
+ .chained = true,
+ .chain_id = ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE,
+ },
+ [ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x90170151 }, /* Internal Speaker LFE */
+ { 0x1e, 0x90170150 }, /* Internal Speaker */
+ { }
+ },
+ },
+ [ALC256_FIXUP_VAIO_RPL_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a1113c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x22a190a0 }, /* dock mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
+ },
+ [ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_tas2781_spi_hp_fixup_muteled,
+ },
+ [ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc245_tas2781_i2c_hp_fixup_muteled,
+ },
+ [ALC288_FIXUP_SURFACE_SWAP_DACS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc288_fixup_surface_swap_dacs,
+ },
+};
+
+static const struct hda_quirk alc269_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x0283, "Acer TravelMate 8371", ALC269_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700),
+ SND_PCI_QUIRK(0x1025, 0x072d, "Acer Aspire V5-571G", ALC269_FIXUP_ASPIRE_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK),
+ SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK),
+ SND_PCI_QUIRK(0x1025, 0x0762, "Acer Aspire E1-472", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572),
+ SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572),
+ SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS),
+ SND_PCI_QUIRK(0x1025, 0x080d, "Acer Aspire V5-122P", ALC269_FIXUP_ASPIRE_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x0840, "Acer Aspire E1", ALC269VB_FIXUP_ASPIRE_E1_COEF),
+ SND_PCI_QUIRK(0x1025, 0x100c, "Acer Aspire E5-574G", ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1025, 0x101c, "Acer Veriton N2510G", ALC269_FIXUP_LIFEBOOK),
+ SND_PCI_QUIRK(0x1025, 0x102b, "Acer Aspire C24-860", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x1065, "Acer Aspire C20-820", ALC269VC_FIXUP_ACER_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x106d, "Acer Cloudbook 14", ALC283_FIXUP_CHROME_BOOK),
+ SND_PCI_QUIRK(0x1025, 0x1094, "Acer Aspire E5-575T", ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1025, 0x1099, "Acer Aspire E5-523G", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x110e, "Acer Aspire ES1-432", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x1166, "Acer Veriton N4640G", ALC269_FIXUP_LIFEBOOK),
+ SND_PCI_QUIRK(0x1025, 0x1167, "Acer Veriton N6640G", ALC269_FIXUP_LIFEBOOK),
+ SND_PCI_QUIRK(0x1025, 0x1177, "Acer Predator G9-593", ALC255_FIXUP_PREDATOR_SUBWOOFER),
+ SND_PCI_QUIRK(0x1025, 0x1178, "Acer Predator G9-593", ALC255_FIXUP_PREDATOR_SUBWOOFER),
+ SND_PCI_QUIRK(0x1025, 0x1246, "Acer Predator Helios 500", ALC299_FIXUP_PREDATOR_SPK),
+ SND_PCI_QUIRK(0x1025, 0x1247, "Acer vCopperbox", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS),
+ SND_PCI_QUIRK(0x1025, 0x1248, "Acer Veriton N4660G", ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x1269, "Acer SWIFT SF314-54", ALC256_FIXUP_ACER_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x126a, "Acer Swift SF114-32", ALC256_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x128f, "Acer Veriton Z6860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x1290, "Acer Veriton Z4860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x1291, "Acer Veriton Z4660G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x129c, "Acer SWIFT SF314-55", ALC256_FIXUP_ACER_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x129d, "Acer SWIFT SF313-51", ALC256_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x1300, "Acer SWIFT SF314-56", ALC256_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x1308, "Acer Aspire Z24-890", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x132a, "Acer TravelMate B114-21", ALC233_FIXUP_ACER_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x1330, "Acer TravelMate X514-51T", ALC255_FIXUP_ACER_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x1360, "Acer Aspire A115", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x141f, "Acer Spin SP513-54N", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x142b, "Acer Swift SF314-42", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x1430, "Acer TravelMate B311R-31", ALC256_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x1466, "Acer Aspire A515-56", ALC255_FIXUP_ACER_HEADPHONE_AND_MIC),
+ SND_PCI_QUIRK(0x1025, 0x1534, "Acer Predator PH315-54", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED),
+ SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1025, 0x182c, "Acer Helios ZPD", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1025, 0x1844, "Acer Helios ZPS", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
+ SND_PCI_QUIRK(0x1028, 0x053c, "Dell Latitude E5430", ALC292_FIXUP_DELL_E7X),
+ SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS),
+ SND_PCI_QUIRK(0x1028, 0x05bd, "Dell Latitude E6440", ALC292_FIXUP_DELL_E7X),
+ SND_PCI_QUIRK(0x1028, 0x05be, "Dell Latitude E6540", ALC292_FIXUP_DELL_E7X),
+ SND_PCI_QUIRK(0x1028, 0x05ca, "Dell Latitude E7240", ALC292_FIXUP_DELL_E7X),
+ SND_PCI_QUIRK(0x1028, 0x05cb, "Dell Latitude E7440", ALC292_FIXUP_DELL_E7X),
+ SND_PCI_QUIRK(0x1028, 0x05da, "Dell Vostro 5460", ALC290_FIXUP_SUBWOOFER),
+ SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0604, "Dell Venue 11 Pro 7130", ALC283_FIXUP_DELL_HP_RESUME),
+ SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
+ SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
+ SND_PCI_QUIRK(0x1028, 0x062c, "Dell Latitude E5550", ALC292_FIXUP_DELL_E7X),
+ SND_PCI_QUIRK(0x1028, 0x062e, "Dell Latitude E7450", ALC292_FIXUP_DELL_E7X),
+ SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK),
+ SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0665, "Dell XPS 13", ALC288_FIXUP_DELL_XPS_13),
+ SND_PCI_QUIRK(0x1028, 0x0669, "Dell Optiplex 9020m", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x069a, "Dell Vostro 5480", ALC290_FIXUP_SUBWOOFER_HSJACK),
+ SND_PCI_QUIRK(0x1028, 0x06c7, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x06d9, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x06da, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x06db, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x06dd, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x06de, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x06df, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x06e0, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
+ SND_PCI_QUIRK(0x1028, 0x0706, "Dell Inspiron 7559", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER),
+ SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE),
+ SND_PCI_QUIRK(0x1028, 0x0738, "Dell Precision 5820", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1028, 0x075c, "Dell XPS 27 7760", ALC298_FIXUP_SPK_VOLUME),
+ SND_PCI_QUIRK(0x1028, 0x075d, "Dell AIO", ALC298_FIXUP_SPK_VOLUME),
+ SND_PCI_QUIRK(0x1028, 0x0798, "Dell Inspiron 17 7000 Gaming", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER),
+ SND_PCI_QUIRK(0x1028, 0x07b0, "Dell Precision 7520", ALC295_FIXUP_DISABLE_DAC3),
+ SND_PCI_QUIRK(0x1028, 0x080c, "Dell WYSE", ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x084b, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB),
+ SND_PCI_QUIRK(0x1028, 0x084e, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB),
+ SND_PCI_QUIRK(0x1028, 0x0871, "Dell Precision 3630", ALC255_FIXUP_DELL_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0872, "Dell Precision 3630", ALC255_FIXUP_DELL_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0873, "Dell Precision 3930", ALC255_FIXUP_DUMMY_LINEOUT_VERB),
+ SND_PCI_QUIRK(0x1028, 0x0879, "Dell Latitude 5420 Rugged", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x08ad, "Dell WYSE AIO", ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x08ae, "Dell WYSE NB", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0935, "Dell", ALC274_FIXUP_DELL_AIO_LINEOUT_VERB),
+ SND_PCI_QUIRK(0x1028, 0x097d, "Dell Precision", ALC289_FIXUP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x097e, "Dell Precision", ALC289_FIXUP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x098d, "Dell Precision", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x09bf, "Dell Precision", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0a2e, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0a30, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0a38, "Dell Latitude 7520", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET),
+ SND_PCI_QUIRK(0x1028, 0x0a58, "Dell", ALC255_FIXUP_DELL_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1028, 0x0a61, "Dell XPS 15 9510", ALC289_FIXUP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0a62, "Dell Precision 5560", ALC289_FIXUP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0a9d, "Dell Latitude 5430", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0a9e, "Dell Latitude 5430", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0b19, "Dell XPS 15 9520", ALC289_FIXUP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0b1a, "Dell Precision 5570", ALC289_FIXUP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0b27, "Dell", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0b28, "Dell", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0b37, "Dell Inspiron 16 Plus 7620 2-in-1", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS),
+ SND_PCI_QUIRK(0x1028, 0x0b71, "Dell Inspiron 16 Plus 7620", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS),
+ SND_PCI_QUIRK(0x1028, 0x0beb, "Dell XPS 15 9530 (2023)", ALC289_FIXUP_DELL_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0c03, "Dell Precision 5340", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0c0b, "Dell Oasis 14 RPL-P", ALC289_FIXUP_RTK_AMP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0c0d, "Dell Oasis", ALC289_FIXUP_RTK_AMP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0c0e, "Dell Oasis 16", ALC289_FIXUP_RTK_AMP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0c19, "Dell Precision 3340", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0c1a, "Dell Precision 3340", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0c1b, "Dell Precision 3440", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0c1c, "Dell Precision 3540", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0c1d, "Dell Precision 3440", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0c1e, "Dell Precision 3540", ALC236_FIXUP_DELL_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1028, 0x0c28, "Dell Inspiron 16 Plus 7630", ALC295_FIXUP_DELL_INSPIRON_TOP_SPEAKERS),
+ SND_PCI_QUIRK(0x1028, 0x0c4d, "Dell", ALC287_FIXUP_CS35L41_I2C_4),
+ SND_PCI_QUIRK(0x1028, 0x0c94, "Dell Polaris 3 metal", ALC295_FIXUP_DELL_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1028, 0x0c96, "Dell Polaris 2in1", ALC295_FIXUP_DELL_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1028, 0x0cbd, "Dell Oasis 13 CS MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cbe, "Dell Oasis 13 2-IN-1 MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cbf, "Dell Oasis 13 Low Weight MTU-L", ALC289_FIXUP_DELL_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc0, "Dell Oasis 13", ALC289_FIXUP_RTK_AMP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x0cc1, "Dell Oasis 14 MTL-H/U", ALC289_FIXUP_DELL_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc2, "Dell Oasis 14 2-in-1 MTL-H/U", ALC289_FIXUP_DELL_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc3, "Dell Oasis 14 Low Weight MTL-U", ALC289_FIXUP_DELL_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc4, "Dell Oasis 16 MTL-H/U", ALC289_FIXUP_DELL_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1028, 0x0cc5, "Dell Oasis 14", ALC289_FIXUP_RTK_AMP_DUAL_SPK),
+ SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
+ SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x21f9, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2210, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2214, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x221b, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x221c, "HP EliteBook 755 G2", ALC280_FIXUP_HP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x103c, 0x2221, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2225, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2236, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2237, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2238, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2239, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x224b, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2253, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2254, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2255, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2256, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2257, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2259, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x225a, "HP", ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x225f, "HP", ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY),
+ SND_PCI_QUIRK(0x103c, 0x2260, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2263, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2264, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2265, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2268, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x226a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x226b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x226e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2271, "HP", ALC286_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x2272, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2272, "HP", ALC280_FIXUP_HP_DOCK_PINS),
+ SND_PCI_QUIRK(0x103c, 0x2273, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2273, "HP", ALC280_FIXUP_HP_DOCK_PINS),
+ SND_PCI_QUIRK(0x103c, 0x2278, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x227f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2282, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x228b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x228e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x229e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22b2, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22b7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22bf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22c4, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22c5, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22c7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22c8, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22cf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22db, "HP", ALC280_FIXUP_HP_9480M),
+ SND_PCI_QUIRK(0x103c, 0x22dc, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x22fb, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x2334, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2335, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2336, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2337, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2b5e, "HP 288 Pro G2 MT", ALC221_FIXUP_HP_288PRO_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x802e, "HP Z240 SFF", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x802f, "HP Z240", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x8077, "HP", ALC256_FIXUP_HP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x103c, 0x8158, "HP", ALC256_FIXUP_HP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x103c, 0x820d, "HP Pavilion 15", ALC295_FIXUP_HP_X360),
+ SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC),
+ SND_PCI_QUIRK(0x103c, 0x827e, "HP x360", ALC295_FIXUP_HP_X360),
+ SND_PCI_QUIRK(0x103c, 0x827f, "HP x360", ALC269_FIXUP_HP_MUTE_LED_MIC3),
+ SND_PCI_QUIRK(0x103c, 0x82bf, "HP G3 mini", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x82c0, "HP G3 mini premium", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x83b9, "HP Spectre x360", ALC269_FIXUP_HP_MUTE_LED_MIC3),
+ SND_PCI_QUIRK(0x103c, 0x841c, "HP Pavilion 15-CK0xx", ALC269_FIXUP_HP_MUTE_LED_MIC3),
+ SND_PCI_QUIRK(0x103c, 0x8497, "HP Envy x360", ALC269_FIXUP_HP_MUTE_LED_MIC3),
+ SND_PCI_QUIRK(0x103c, 0x84a6, "HP 250 G7 Notebook PC", ALC269_FIXUP_HP_LINE1_MIC1_LED),
+ SND_PCI_QUIRK(0x103c, 0x84ae, "HP 15-db0403ng", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x84da, "HP OMEN dc0019-ur", ALC295_FIXUP_HP_OMEN),
+ SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3),
+ SND_PCI_QUIRK(0x103c, 0x8519, "HP Spectre x360 15-df0xxx", ALC285_FIXUP_HP_SPECTRE_X360),
+ SND_PCI_QUIRK(0x103c, 0x8537, "HP ProBook 440 G6", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8548, "HP EliteBook x360 830 G6", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x854a, "HP EliteBook 830 G6", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11),
+ SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360),
+ SND_PCI_QUIRK(0x103c, 0x8603, "HP Omen 17-cb0xxx", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x860c, "HP ZBook 17 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x861f, "HP Elite Dragonfly G1", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x86c1, "HP Laptop 15-da3001TU", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x86e7, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1),
+ SND_PCI_QUIRK(0x103c, 0x863e, "HP Spectre x360 15-df1xxx", ALC285_FIXUP_HP_SPECTRE_X360_DF1),
+ SND_PCI_QUIRK(0x103c, 0x86e8, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1),
+ SND_PCI_QUIRK(0x103c, 0x86f9, "HP Spectre x360 13-aw0xxx", ALC285_FIXUP_HP_SPECTRE_X360_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8716, "HP Elite Dragonfly G2 Notebook PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x8720, "HP EliteBook x360 1040 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x8724, "HP EliteBook 850 G7", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8728, "HP EliteBook 840 G7", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8729, "HP", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8730, "HP ProBook 445 G7", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8735, "HP ProBook 435 G7", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8736, "HP", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x8760, "HP EliteBook 8{4,5}5 G7", ALC285_FIXUP_HP_BEEP_MICMUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x876e, "HP ENVY x360 Convertible 13-ay0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS),
+ SND_PCI_QUIRK(0x103c, 0x877a, "HP", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x877d, "HP", ALC236_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8780, "HP ZBook Fury 17 G7 Mobile Workstation",
+ ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x8783, "HP ZBook Fury 15 G7 Mobile Workstation",
+ ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x8786, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8787, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8788, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x87b7, "HP Laptop 14-fq0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87cc, "HP Pavilion 15-eg0xxx", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87d3, "HP Laptop 15-gw0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x87df, "HP ProBook 430 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87e5, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87e7, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87f1, "HP ProBook 630 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87f2, "HP ProBook 640 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87f4, "HP", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87f5, "HP", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x87f6, "HP Spectre x360 14", ALC245_FIXUP_HP_X360_AMP),
+ SND_PCI_QUIRK(0x103c, 0x87f7, "HP Spectre x360 14", ALC245_FIXUP_HP_X360_AMP),
+ SND_PCI_QUIRK(0x103c, 0x87fd, "HP Laptop 14-dq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x87fe, "HP Laptop 15s-fq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x8805, "HP ProBook 650 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x880d, "HP EliteBook 830 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8811, "HP Spectre x360 15-eb1xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1),
+ SND_PCI_QUIRK(0x103c, 0x8812, "HP Spectre x360 15-eb1xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1),
+ SND_PCI_QUIRK(0x103c, 0x881d, "HP 250 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x881e, "HP Laptop 15s-du3xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x8846, "HP EliteBook 850 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8847, "HP EliteBook x360 830 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x884b, "HP EliteBook 840 Aero G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x884c, "HP EliteBook 840 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8862, "HP ProBook 445 G8 Notebook PC", ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x103c, 0x8863, "HP ProBook 445 G8 Notebook PC", ALC236_FIXUP_HP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x103c, 0x886d, "HP ZBook Fury 17.3 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x8870, "HP ZBook Fury 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x8873, "HP ZBook Studio 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
+ SND_PCI_QUIRK(0x103c, 0x887a, "HP Laptop 15s-eq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x887c, "HP Laptop 14s-fq1xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x888a, "HP ENVY x360 Convertible 15-eu0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS),
+ SND_PCI_QUIRK(0x103c, 0x888d, "HP ZBook Power 15.6 inch G8 Mobile Workstation PC", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8895, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8896, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8898, "HP EliteBook 845 G8 Notebook PC", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x103c, 0x88d0, "HP Pavilion 15-eh1xxx (mainboard 88D0)", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x88dd, "HP Pavilion 15z-ec200", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8902, "HP OMEN 16", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x890e, "HP 255 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x8919, "HP Pavilion Aero Laptop 13-be0xxx", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x896d, "HP ZBook Firefly 16 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x896e, "HP EliteBook x360 830 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8971, "HP EliteBook 830 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8972, "HP EliteBook 840 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8973, "HP EliteBook 860 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8974, "HP EliteBook 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8975, "HP EliteBook x360 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x897d, "HP mt440 Mobile Thin Client U74", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8981, "HP Elite Dragonfly G3", ALC245_FIXUP_CS35L41_SPI_4),
+ SND_PCI_QUIRK(0x103c, 0x898a, "HP Pavilion 15-eg100", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x898e, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x898f, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8991, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8992, "HP EliteBook 845 G9", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8994, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8995, "HP EliteBook 855 G9", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x89a0, "HP Laptop 15-dw4xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x89a4, "HP ProBook 440 G9", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89a6, "HP ProBook 450 G9", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89aa, "HP EliteBook 630 G9", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89ac, "HP EliteBook 640 G9", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89ae, "HP EliteBook 650 G9", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89c0, "HP ZBook Power 15.6 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89c3, "Zbook Studio G9", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x89da, "HP Spectre x360 14t-ea100", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX),
+ SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8a20, "HP Laptop 15s-fq5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x8a25, "HP Victus 16-d1xxx (MB 8A25)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8a26, "HP Victus 16-d1xxx (MB 8A26)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8a28, "HP Envy 13", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a29, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a2a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a2b, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a2c, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a2d, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a2e, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a30, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a31, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a4f, "HP Victus 15-fa0xxx (MB 8A4F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4),
+ SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8a75, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8a76, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8a77, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8a78, "HP Dev One", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x103c, 0x8aa0, "HP ProBook 440 G9 (MB 8A9E)", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8aa3, "HP ProBook 450 G9 (MB 8AA1)", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8aa8, "HP EliteBook 640 G9 (MB 8AA6)", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8aab, "HP EliteBook 650 G9 (MB 8AA9)", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ab9, "HP EliteBook 840 G8 (MB 8AB8)", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8abb, "HP ZBook Firefly 14 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ad1, "HP EliteBook 840 14 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ad2, "HP EliteBook 860 16 inch G9 Notebook PC", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ad8, "HP 800 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b0f, "HP Elite mt645 G7 Mobile Thin Client U81", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8b2f, "HP 255 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x8b3a, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8b3f, "HP mt440 Mobile Thin Client U91", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b42, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b43, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b44, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b45, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b46, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b47, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b59, "HP Elite mt645 G7 Mobile Thin Client U89", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8b5d, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8b5e, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8b5f, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8b63, "HP Elite Dragonfly 13.5 inch G4", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b65, "HP ProBook 455 15.6 inch G10 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8b66, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8b70, "HP EliteBook 835 G10", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b72, "HP EliteBook 845 G10", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b74, "HP EliteBook 845W G10", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b77, "HP ElieBook 865 G10", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8b7a, "HP", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b7d, "HP", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b87, "HP", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b8a, "HP", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b8b, "HP", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b8d, "HP", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b8f, "HP", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b92, "HP", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8b96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8b97, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8bb3, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8bbe, "HP Victus 16-r0xxx (MB 8BBE)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8bc8, "HP Victus 15-fa1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8bcd, "HP Omen 16-xd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8bd4, "HP Victus 16-s0xxx (MB 8BD4)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8bd6, "HP Pavilion Aero Laptop 13z-be200", ALC287_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8bdd, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8bde, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8bdf, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8be0, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8be1, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8be2, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8be3, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8be5, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8be6, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8be7, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8be8, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8be9, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8bf0, "HP", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c15, "HP Spectre x360 2-in-1 Laptop 14-eu0xxx", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX),
+ SND_PCI_QUIRK(0x103c, 0x8c16, "HP Spectre x360 2-in-1 Laptop 16-aa0xxx", ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX),
+ SND_PCI_QUIRK(0x103c, 0x8c17, "HP Spectre 16", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c21, "HP Pavilion Plus Laptop 14-ey0XXX", ALC245_FIXUP_HP_X360_MUTE_LEDS),
+ SND_PCI_QUIRK(0x103c, 0x8c2d, "HP Victus 15-fa1xxx (MB 8C2D)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8c30, "HP Victus 15-fb1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c47, "HP EliteBook 840 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c48, "HP EliteBook 860 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c49, "HP Elite x360 830 2-in-1 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c4d, "HP Omen", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c4e, "HP Omen", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c4f, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c50, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c51, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c52, "HP EliteBook 1040 G11", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c53, "HP Elite x360 1040 2-in-1 G11", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c66, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c67, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c68, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c6a, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8c70, "HP EliteBook 835 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c71, "HP EliteBook 845 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c72, "HP EliteBook 865 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c7b, "HP ProBook 445 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c7c, "HP ProBook 445 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c7d, "HP ProBook 465 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c7e, "HP ProBook 465 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c7f, "HP EliteBook 645 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c80, "HP EliteBook 645 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c81, "HP EliteBook 665 G11", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c89, "HP ProBook 460 G11", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c8a, "HP EliteBook 630", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c8c, "HP EliteBook 660", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c8d, "HP ProBook 440 G11", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c8e, "HP ProBook 460 G11", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c90, "HP EliteBook 640", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c91, "HP EliteBook 660", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8c96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c97, "HP ZBook", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8c99, "HP Victus 16-r1xxx (MB 8C99)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8c9c, "HP Victus 16-s1xxx (MB 8C9C)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8ca1, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ca2, "HP ZBook Power", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ca4, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ca7, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8caf, "HP Elite mt645 G8 Mobile Thin Client", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8cbd, "HP Pavilion Aero Laptop 13-bg0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS),
+ SND_PCI_QUIRK(0x103c, 0x8cdd, "HP Spectre", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX),
+ SND_PCI_QUIRK(0x103c, 0x8cde, "HP OmniBook Ultra Flip Laptop 14t", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX),
+ SND_PCI_QUIRK(0x103c, 0x8cdf, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ce0, "HP SnowWhite", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d01, "HP ZBook Power 14 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d07, "HP Victus 15-fb2xxx (MB 8D07)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8d18, "HP EliteStudio 8 AIO", ALC274_FIXUP_HP_AIO_BIND_DACS),
+ SND_PCI_QUIRK(0x103c, 0x8d84, "HP EliteBook X G1i", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d85, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d86, "HP Elite X360 14 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d8c, "HP EliteBook 13 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d8d, "HP Elite X360 13 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d8e, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d8f, "HP EliteBook 14 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d90, "HP EliteBook 16 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d91, "HP ZBook Firefly 14 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d92, "HP ZBook Firefly 16 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8d9b, "HP 17 Turbine OmniBook 7 UMA", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8d9c, "HP 17 Turbine OmniBook 7 DIS", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8d9d, "HP 17 Turbine OmniBook X UMA", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8d9e, "HP 17 Turbine OmniBook X DIS", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8d9f, "HP 14 Cadet (x360)", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8da0, "HP 16 Clipper OmniBook 7(X360)", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8da1, "HP 16 Clipper OmniBook X", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8da7, "HP 14 Enstrom OmniBook X", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8da8, "HP 16 Piston OmniBook X", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8dd4, "HP EliteStudio 8 AIO", ALC274_FIXUP_HP_AIO_BIND_DACS),
+ SND_PCI_QUIRK(0x103c, 0x8de8, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2),
+ SND_PCI_QUIRK(0x103c, 0x8de9, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2),
+ SND_PCI_QUIRK(0x103c, 0x8dec, "HP EliteBook 640 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ded, "HP EliteBook 640 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8dee, "HP EliteBook 660 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8def, "HP EliteBook 660 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8df0, "HP EliteBook 630 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8df1, "HP EliteBook 630 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8dfb, "HP EliteBook 6 G1a 14", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8dfc, "HP EliteBook 645 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8dfd, "HP EliteBook 6 G1a 16", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
+ SND_PCI_QUIRK(0x103c, 0x8dfe, "HP EliteBook 665 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8e11, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e12, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e13, "HP Trekker", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e14, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A),
+ SND_PCI_QUIRK(0x103c, 0x8e15, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A),
+ SND_PCI_QUIRK(0x103c, 0x8e16, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A),
+ SND_PCI_QUIRK(0x103c, 0x8e17, "HP ZBook Firefly 14 G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A),
+ SND_PCI_QUIRK(0x103c, 0x8e18, "HP ZBook Firefly 14 G12A", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A),
+ SND_PCI_QUIRK(0x103c, 0x8e19, "HP ZBook Firefly 14 G12A", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A),
+ SND_PCI_QUIRK(0x103c, 0x8e1a, "HP ZBook Firefly 14 G12A", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A),
+ SND_PCI_QUIRK(0x103c, 0x8e1b, "HP EliteBook G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A),
+ SND_PCI_QUIRK(0x103c, 0x8e1c, "HP EliteBook G12", ALC245_FIXUP_HP_ZBOOK_FIREFLY_G12A),
+ SND_PCI_QUIRK(0x103c, 0x8e1d, "HP ZBook X Gli 16 G12", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8e2c, "HP EliteBook 16 G12", ALC285_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8e36, "HP 14 Enstrom OmniBook X", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e37, "HP 16 Piston OmniBook X", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e3a, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e3b, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e60, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8eb6, "HP Abe A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x8eb7, "HP Abe A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x8eb8, "HP Abe A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x8ec1, "HP 200 G2i", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x8ec4, "HP Bantie I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x8ec5, "HP Bantie I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x8ece, "HP Abe I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x8ecf, "HP Abe I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x8ed2, "HP Abe I6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x8ed5, "HP EliteBook 8 Flip G2i 13", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ed6, "HP EliteBook 8 G2i 13", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ed7, "HP EliteBook 8 G2i 14", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ed8, "HP EliteBook 8 G2i 16", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ed9, "HP ZBook Firefly 14W", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8eda, "HP ZBook Firefly 16W", ALC245_FIXUP_HP_TAS2781_SPI_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8ee4, "HP Bantie A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x8ee5, "HP Bantie A6U", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
+ SND_PCI_QUIRK(0x103c, 0x8f0c, "HP ZBook X G2i 16W", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8f0e, "HP ZBook X G2i 16W", ALC236_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8f40, "HP ZBook 8 G2a 14", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8f41, "HP ZBook 8 G2a 16", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8f42, "HP ZBook 8 G2a 14W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x8f62, "HP ZBook 8 G2a 16W", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED),
+ SND_PCI_QUIRK(0x1043, 0x1032, "ASUS VivoBook X513EA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1034, "ASUS GU605C", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1),
+ SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
+ SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
+ SND_PCI_QUIRK(0x1043, 0x1054, "ASUS G614FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1043, 0x106f, "ASUS VivoBook X515UA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1074, "ASUS G614PH/PM/PP", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x10a1, "ASUS UX391UA", ALC294_FIXUP_ASUS_SPK),
+ SND_PCI_QUIRK(0x1043, 0x10a4, "ASUS TP3407SA", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x10c0, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
+ SND_PCI_QUIRK(0x1043, 0x10d0, "ASUS X540LA/X540LJ", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x10d3, "ASUS K6500ZC", ALC294_FIXUP_ASUS_SPK),
+ SND_PCI_QUIRK(0x1043, 0x1154, "ASUS TP3607SH", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1043, 0x1194, "ASUS UM3406KA", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x11c0, "ASUS X556UR", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1204, "ASUS Strix G615JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x1214, "ASUS Strix G615LH_LM_LP", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x125e, "ASUS Q524UQK", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1271, "ASUS X430UN", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1290, "ASUS X441SA", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1294, "ASUS B3405CVA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x12a0, "ASUS X441UV", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x12a3, "Asus N7691ZM", ALC269_FIXUP_ASUS_N7601ZM),
+ SND_PCI_QUIRK(0x1043, 0x12af, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x12b4, "ASUS B3405CCA / P3405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x12e0, "ASUS X541SA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x12f0, "ASUS X541UV", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1313, "Asus K42JZ", ALC269VB_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1314, "ASUS GA605K", ALC285_FIXUP_ASUS_GA605K_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1384, "ASUS RC73XA", ALC287_FIXUP_TXNW2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x1394, "ASUS RC73YA", ALC287_FIXUP_TXNW2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x13b0, "ASUS Z550SA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK),
+ SND_PCI_QUIRK(0x1043, 0x1433, "ASUS GX650PY/PZ/PV/PU/PYV/PZV/PIV/PVV", ALC285_FIXUP_ASUS_I2C_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1454, "ASUS PM3406CKA", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1460, "Asus VivoBook 15", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1463, "Asus GA402X/GA402N", ALC285_FIXUP_ASUS_I2C_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1473, "ASUS GU604VI/VC/VE/VG/VJ/VQ/VU/VV/VY/VZ", ALC285_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1483, "ASUS GU603VQ/VU/VV/VJ/VI", ALC285_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1493, "ASUS GV601VV/VU/VJ/VQ/VI", ALC285_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G614JY/JZ/JG", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS G513PI/PU/PV", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x14f2, "ASUS VivoBook X515JA", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1503, "ASUS G733PY/PZ/PZV/PYV", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A),
+ SND_PCI_QUIRK(0x1043, 0x1533, "ASUS GV302XA/XJ/XQ/XU/XV/XI", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1573, "ASUS GZ301VV/VQ/VU/VJ/VA/VC/VE/VVC/VQC/VUC/VJC/VEC/VCC", ALC285_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1652, "ASUS ROG Zephyrus Do 15 SE", ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK),
+ SND_PCI_QUIRK(0x1043, 0x1662, "ASUS GV301QH", ALC294_FIXUP_ASUS_DUAL_SPK),
+ SND_PCI_QUIRK(0x1043, 0x1663, "ASUS GU603ZI/ZJ/ZQ/ZU/ZV", ALC285_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1683, "ASUS UM3402YAR", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS UX3402VA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x16b2, "ASUS GU603", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x16d3, "ASUS UX5304VA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS UX7602VI/BZ", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1740, "ASUS UX430UA", ALC295_FIXUP_ASUS_DACS),
+ SND_PCI_QUIRK(0x1043, 0x17d1, "ASUS UX431FL", ALC294_FIXUP_ASUS_DUAL_SPK),
+ SND_PCI_QUIRK(0x1043, 0x17f3, "ROG Ally NR2301L/X", ALC294_FIXUP_ASUS_ALLY),
+ SND_PCI_QUIRK(0x1043, 0x1863, "ASUS UX6404VI/VV", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1881, "ASUS Zephyrus S/M", ALC294_FIXUP_ASUS_GX502_PINS),
+ SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS UM3504DA", ALC294_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x18f1, "Asus FX505DT", ALC256_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x194e, "ASUS UX563FD", ALC294_FIXUP_ASUS_HPE),
+ SND_PCI_QUIRK(0x1043, 0x1970, "ASUS UX550VE", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x1982, "ASUS B1400CEPE", ALC256_FIXUP_ASUS_HPE),
+ SND_PCI_QUIRK(0x1043, 0x19ce, "ASUS B9450FA", ALC294_FIXUP_ASUS_HPE),
+ SND_PCI_QUIRK(0x1043, 0x19e1, "ASUS UX581LV", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
+ SND_PCI_QUIRK(0x1043, 0x1a63, "ASUS UX3405MA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1a83, "ASUS UM5302LA", ALC294_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1a8e, "ASUS G712LWS", ALC294_FIXUP_LENOVO_MIC_LOCATION),
+ SND_PCI_QUIRK(0x1043, 0x1a8f, "ASUS UX582ZS", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1b11, "ASUS UX431DA", ALC294_FIXUP_ASUS_COEF_1B),
+ SND_PCI_QUIRK(0x1043, 0x1b13, "ASUS U41SV/GA403U", ALC285_FIXUP_ASUS_GA403U_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1b93, "ASUS G614JVR/JIR", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1bbd, "ASUS Z550MA", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1c03, "ASUS UM3406HA", ALC294_FIXUP_ASUS_I2C_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1c23, "Asus X55U", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1043, 0x1c33, "ASUS UX5304MA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1c43, "ASUS UX8406MA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1c62, "ASUS GU603", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x1c63, "ASUS GU605M", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1),
+ SND_PCI_QUIRK(0x1043, 0x1c80, "ASUS VivoBook TP401", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1c92, "ASUS ROG Strix G15", ALC285_FIXUP_ASUS_G533Z_PINS),
+ SND_PCI_QUIRK(0x1043, 0x1c9f, "ASUS G614JU/JV/JI", ALC285_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1caf, "ASUS G634JY/JZ/JI/JG", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS),
+ SND_PCI_QUIRK(0x1043, 0x1ccd, "ASUS X555UB", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1ccf, "ASUS G814JU/JV/JI", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1cdf, "ASUS G814JY/JZ/JG", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1cef, "ASUS G834JY/JZ/JI/JG", ALC285_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1d1f, "ASUS G713PI/PU/PV/PVN", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1d42, "ASUS Zephyrus G14 2022", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x1d4e, "ASUS TM420", ALC256_FIXUP_ASUS_HPE),
+ SND_PCI_QUIRK(0x1043, 0x1da2, "ASUS UP6502ZA/ZD", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1df3, "ASUS UM5606WA", ALC294_FIXUP_BASS_SPEAKER_15),
+ SND_PCI_QUIRK(0x1043, 0x1264, "ASUS UM5606KA", ALC294_FIXUP_BASS_SPEAKER_15),
+ SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1e10, "ASUS VivoBook X507UAR", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502),
+ SND_PCI_QUIRK(0x1043, 0x1e12, "ASUS UM3402", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1e1f, "ASUS Vivobook 15 X1504VAP", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1e51, "ASUS Zephyrus M15", ALC294_FIXUP_ASUS_GU502_PINS),
+ SND_PCI_QUIRK(0x1043, 0x1e5e, "ASUS ROG Strix G513", ALC294_FIXUP_ASUS_G513_PINS),
+ SND_PCI_QUIRK(0x1043, 0x1e63, "ASUS H7606W", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1),
+ SND_PCI_QUIRK(0x1043, 0x1e83, "ASUS GA605W", ALC285_FIXUP_ASUS_GU605_SPI_SPEAKER2_TO_DAC1),
+ SND_PCI_QUIRK(0x1043, 0x1e8e, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x1e93, "ASUS ExpertBook B9403CVAR", ALC294_FIXUP_ASUS_HPE),
+ SND_PCI_QUIRK(0x1043, 0x1eb3, "ASUS Ally RCLA72", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x1ed3, "ASUS HN7306W", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1ee2, "ASUS UM6702RA/RC", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1c52, "ASUS Zephyrus G15 2022", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x1f12, "ASUS UM5302", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x1f1f, "ASUS H7604JI/JV/J3D", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1f62, "ASUS UX7602ZM", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1f63, "ASUS P5405CSA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x1f92, "ASUS ROG Flow X16", ALC289_FIXUP_ASUS_GA401),
+ SND_PCI_QUIRK(0x1043, 0x1fb3, "ASUS ROG Flow Z13 GZ302EA", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x3011, "ASUS B5605CVA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x3030, "ASUS ZN270IE", ALC256_FIXUP_ASUS_AIO_GPIO2),
+ SND_PCI_QUIRK(0x1043, 0x3061, "ASUS B3405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x3071, "ASUS B5405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x30c1, "ASUS B3605CCA / P3605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x30d1, "ASUS B5405CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x30e1, "ASUS B5605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x31d0, "ASUS Zen AIO 27 Z272SD_A272SD", ALC274_FIXUP_ASUS_ZEN_AIO_27),
+ SND_PCI_QUIRK(0x1043, 0x31e1, "ASUS B5605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x31f1, "ASUS B3605CCA", ALC294_FIXUP_ASUS_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x3391, "ASUS PM3606CKA", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x3a20, "ASUS G614JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS),
+ SND_PCI_QUIRK(0x1043, 0x3a30, "ASUS G814JVR/JIR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS),
+ SND_PCI_QUIRK(0x1043, 0x3a40, "ASUS G814JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS),
+ SND_PCI_QUIRK(0x1043, 0x3a50, "ASUS G834JYR/JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS),
+ SND_PCI_QUIRK(0x1043, 0x3a60, "ASUS G634JYR/JZR", ALC285_FIXUP_ASUS_SPI_REAR_SPEAKERS),
+ SND_PCI_QUIRK(0x1043, 0x3d78, "ASUS GA603KH", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x3d88, "ASUS GA603KM", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x3e00, "ASUS G814FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x3e20, "ASUS G814PH/PM/PP", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x1043, 0x3e30, "ASUS TP3607SA", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x3ee0, "ASUS Strix G815_JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x3ef0, "ASUS Strix G635LR_LW_LX", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x3f00, "ASUS Strix G815LH_LM_LP", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x3f10, "ASUS Strix G835LR_LW_LX", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x3f30, "ASUS Strix G815LR_LW", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x3fd0, "ASUS B3605CVA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x3ff0, "ASUS B5405CVA", ALC245_FIXUP_CS35L41_SPI_2),
+ SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1043, 0x8516, "ASUS X101CH", ALC269_FIXUP_ASUS_X101),
+ SND_PCI_QUIRK(0x1043, 0x88f4, "ASUS NUC14LNS", ALC245_FIXUP_CS35L41_SPI_1),
+ SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2),
+ SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
+ SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
+ SND_PCI_QUIRK(0x104d, 0x9099, "Sony VAIO S13", ALC275_FIXUP_SONY_DISABLE_AAMIX),
+ SND_PCI_QUIRK(0x104d, 0x90b5, "Sony VAIO Pro 11", ALC286_FIXUP_SONY_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x104d, 0x90b6, "Sony VAIO Pro 13", ALC286_FIXUP_SONY_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK),
+ SND_PCI_QUIRK(0x10cf, 0x159f, "Lifebook E780", ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT),
+ SND_PCI_QUIRK(0x10cf, 0x15dc, "Lifebook T731", ALC269_FIXUP_LIFEBOOK_HP_PIN),
+ SND_PCI_QUIRK(0x10cf, 0x1629, "Lifebook U7x7", ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC),
+ SND_PCI_QUIRK(0x10cf, 0x1757, "Lifebook E752", ALC269_FIXUP_LIFEBOOK_HP_PIN),
+ SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC),
+ SND_PCI_QUIRK(0x10ec, 0x10f2, "Intel Reference board", ALC700_FIXUP_INTEL_REFERENCE),
+ SND_PCI_QUIRK(0x10ec, 0x118c, "Medion EE4254 MD62100", ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE),
+ SND_PCI_QUIRK(0x10ec, 0x119e, "Positivo SU C1400", ALC269_FIXUP_ASPIRE_HEADSET_MIC),
+ SND_PCI_QUIRK(0x10ec, 0x11bc, "VAIO VJFE-IL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x10ec, 0x1230, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
+ SND_PCI_QUIRK(0x10ec, 0x124c, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
+ SND_PCI_QUIRK(0x10ec, 0x1252, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
+ SND_PCI_QUIRK(0x10ec, 0x1254, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
+ SND_PCI_QUIRK(0x10ec, 0x12cc, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
+ SND_PCI_QUIRK(0x10ec, 0x12f6, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
+ SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_ASPIRE_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1414, 0x9c20, "Microsoft Surface Pro 2/3", ALC288_FIXUP_SURFACE_SWAP_DACS),
+ SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x144d, 0xc169, "Samsung Notebook 9 Pen (NP930SBE-K01US)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc176, "Samsung Notebook 9 Pro (NP930MBE-K04US)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc189, "Samsung Galaxy Flex Book (NT950QCG-X716)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc18a, "Samsung Galaxy Book Ion (NP930XCJ-K01US)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc1a3, "Samsung Galaxy Book Pro (NP935XDB-KC1SE)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc1a4, "Samsung Galaxy Book Pro 360 (NT935QBD)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc1a6, "Samsung Galaxy Book Pro 360 (NP930QBD)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc740, "Samsung Ativ book 8 (NP870Z5G)", ALC269_FIXUP_ATIV_BOOK_8),
+ SND_PCI_QUIRK(0x144d, 0xc812, "Samsung Notebook Pen S (NT950SBE-X58)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc830, "Samsung Galaxy Book Ion (NT950XCJ-X716A)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc832, "Samsung Galaxy Book Flex Alpha (NP730QCJ)", ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET),
+ SND_PCI_QUIRK(0x144d, 0xca03, "Samsung Galaxy Book2 Pro 360 (NP930QED)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xca06, "Samsung Galaxy Book3 360 (NP730QFG)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET),
+ SND_PCI_QUIRK(0x144d, 0xc868, "Samsung Galaxy Book2 Pro (NP930XED)", ALC298_FIXUP_SAMSUNG_AMP),
+ SND_PCI_QUIRK(0x144d, 0xc870, "Samsung Galaxy Book2 Pro (NP950XED)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS),
+ SND_PCI_QUIRK(0x144d, 0xc872, "Samsung Galaxy Book2 Pro (NP950XEE)", ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS),
+ SND_PCI_QUIRK(0x144d, 0xc886, "Samsung Galaxy Book3 Pro (NP964XFG)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS),
+ SND_PCI_QUIRK(0x144d, 0xc1ca, "Samsung Galaxy Book3 Pro 360 (NP960QFG)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS),
+ SND_PCI_QUIRK(0x144d, 0xc1cc, "Samsung Galaxy Book3 Ultra (NT960XFH)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS),
+ SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1462, 0xb171, "Cubi N 8GL (MS-B171)", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x152d, 0x1082, "Quanta NL3", ALC269_FIXUP_LIFEBOOK),
+ SND_PCI_QUIRK(0x152d, 0x1262, "Huawei NBLB-WAX9N", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1558, 0x0353, "Clevo V35[05]SN[CDE]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x1323, "Clevo N130ZU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x1325, "Clevo N15[01][CW]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x1401, "Clevo L140[CZ]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x1403, "Clevo N140CU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x1404, "Clevo N150CU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x14a1, "Clevo L141MU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x2624, "Clevo L240TU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x28c1, "Clevo V370VND", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1558, 0x35a1, "Clevo V3[56]0EN[CDE]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x35b1, "Clevo V3[57]0WN[MNP]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x4018, "Clevo NV40M[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x4019, "Clevo NV40MZ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x4020, "Clevo NV40MB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x4041, "Clevo NV4[15]PZ", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x40a1, "Clevo NL40GU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x40c1, "Clevo NL40[CZ]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x40d1, "Clevo NL41DU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x5015, "Clevo NH5[58]H[HJK]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x5017, "Clevo NH7[79]H[HJK]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50a3, "Clevo NJ51GU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50b3, "Clevo NK50S[BEZ]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50b6, "Clevo NK50S5", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50b8, "Clevo NK50SZ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50d5, "Clevo NP50D5", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50e1, "Clevo NH5[58]HPQ", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50e2, "Clevo NH7[79]HPQ", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50f0, "Clevo NH50A[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50f2, "Clevo NH50E[PR]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50f3, "Clevo NH58DPQ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50f5, "Clevo NH55EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x50f6, "Clevo NH55DPQ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x5101, "Clevo S510WU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x5157, "Clevo W517GU1", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x51a1, "Clevo NS50MU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x51b1, "Clevo NS50AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x51b3, "Clevo NS70AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x5630, "Clevo NP50RNJS", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x5700, "Clevo X560WN[RST]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x70a1, "Clevo NB70T[HJK]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x70b3, "Clevo NK70SB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x70f2, "Clevo NH79EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x70f3, "Clevo NH77DPQ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x70f4, "Clevo NH77EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x70f6, "Clevo NH77DPQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x7716, "Clevo NS50PU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x7717, "Clevo NS70PU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x7718, "Clevo L140PU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x7724, "Clevo L140AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8228, "Clevo NR40BU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8520, "Clevo NH50D[CD]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8521, "Clevo NH77D[CD]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8535, "Clevo NH50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8536, "Clevo NH79D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8550, "Clevo NH[57][0-9][ER][ACDH]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8551, "Clevo NH[57][0-9][ER][ACDH]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8560, "Clevo NH[57][0-9][ER][ACDH]Q", ALC269_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1558, 0x8561, "Clevo NH[57][0-9][ER][ACDH]Q", ALC269_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1558, 0x8562, "Clevo NH[57][0-9]RZ[Q]", ALC269_FIXUP_DMIC),
+ SND_PCI_QUIRK(0x1558, 0x8668, "Clevo NP50B[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x866d, "Clevo NP5[05]PN[HJK]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x867c, "Clevo NP7[01]PNP", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x867d, "Clevo NP7[01]PN[HJK]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8680, "Clevo NJ50LU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8686, "Clevo NH50[CZ]U", ALC256_FIXUP_MIC_NO_PRESENCE_AND_RESUME),
+ SND_PCI_QUIRK(0x1558, 0x8a20, "Clevo NH55DCQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8a51, "Clevo NH70RCQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x8d50, "Clevo NH55RCQ-M", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x951d, "Clevo N950T[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x9600, "Clevo N960K[PR]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x961d, "Clevo N960S[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0x971d, "Clevo N970T[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xa500, "Clevo NL5[03]RU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xa554, "VAIO VJFH52", ALC269_FIXUP_VAIO_VJFH52_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xa559, "VAIO RPL", ALC256_FIXUP_VAIO_RPL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xa600, "Clevo NL50NU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xa650, "Clevo NP[567]0SN[CD]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xa671, "Clevo NP70SN[CDE]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xa741, "Clevo V54x_6x_TNE", ALC245_FIXUP_CLEVO_NOISY_MIC),
+ SND_PCI_QUIRK(0x1558, 0xa743, "Clevo V54x_6x_TU", ALC245_FIXUP_CLEVO_NOISY_MIC),
+ SND_PCI_QUIRK(0x1558, 0xa763, "Clevo V54x_6x_TU", ALC245_FIXUP_CLEVO_NOISY_MIC),
+ SND_PCI_QUIRK(0x1558, 0xb018, "Clevo NP50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xb019, "Clevo NH77D[BE]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xb022, "Clevo NH77D[DC][QW]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xc018, "Clevo NP50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xc019, "Clevo NH77D[BE]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1558, 0xc022, "Clevo NH77[DC][QW]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS),
+ SND_PCI_QUIRK(0x17aa, 0x1048, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340),
+ SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
+ SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE),
+ SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE),
+ SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE),
+ SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE),
+ SND_PCI_QUIRK(0x17aa, 0x21f3, "Thinkpad T430", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x21f6, "Thinkpad T530", ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad T440s", ALC292_FIXUP_TPT440),
+ SND_PCI_QUIRK(0x17aa, 0x220e, "Thinkpad T440p", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2210, "Thinkpad T540p", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2211, "Thinkpad W541", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x2218, "Thinkpad X1 Carbon 2nd", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x222d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x222e, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2231, "Thinkpad T560", ALC292_FIXUP_TPT460),
+ SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC292_FIXUP_TPT460),
+ SND_PCI_QUIRK(0x17aa, 0x2234, "Thinkpad ICE-1", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x2245, "Thinkpad T470", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2246, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2247, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2249, "Thinkpad", ALC292_FIXUP_TPT460),
+ SND_PCI_QUIRK(0x17aa, 0x224b, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x224c, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x224d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x2292, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK),
+ SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK),
+ SND_PCI_QUIRK(0x17aa, 0x22c1, "Thinkpad P1 Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK),
+ SND_PCI_QUIRK(0x17aa, 0x22c2, "Thinkpad X1 Extreme Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK),
+ SND_PCI_QUIRK(0x17aa, 0x22f1, "Thinkpad", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x22f2, "Thinkpad", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x22f3, "Thinkpad", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x2316, "Thinkpad P1 Gen 6", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x2317, "Thinkpad P1 Gen 6", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x2318, "Thinkpad Z13 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x2319, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x231a, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x231e, "Thinkpad", ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318),
+ SND_PCI_QUIRK(0x17aa, 0x231f, "Thinkpad", ALC287_FIXUP_LENOVO_THKPAD_WH_ALC1318),
+ SND_PCI_QUIRK(0x17aa, 0x2326, "Hera2", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
+ SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
+ SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
+ SND_PCI_QUIRK(0x17aa, 0x3111, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
+ SND_PCI_QUIRK(0x17aa, 0x312a, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
+ SND_PCI_QUIRK(0x17aa, 0x312f, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
+ SND_PCI_QUIRK(0x17aa, 0x313c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
+ SND_PCI_QUIRK(0x17aa, 0x3151, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x3176, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x3178, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x31af, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340),
+ SND_PCI_QUIRK(0x17aa, 0x334b, "Lenovo ThinkCentre M70 Gen5", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x3384, "ThinkCentre M90a PRO", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED),
+ SND_PCI_QUIRK(0x17aa, 0x3386, "ThinkCentre M90a Gen6", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED),
+ SND_PCI_QUIRK(0x17aa, 0x3387, "ThinkCentre M70a Gen6", ALC233_FIXUP_LENOVO_L2MH_LOW_ENLED),
+ SND_PCI_QUIRK(0x17aa, 0x3801, "Lenovo Yoga9 14IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
+ HDA_CODEC_QUIRK(0x17aa, 0x3802, "DuetITL 2021", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga Pro 9 14IRP8", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3813, "Legion 7i 15IMHG05", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940 / Yoga Duet 7", ALC298_FIXUP_LENOVO_C940_DUET7),
+ SND_PCI_QUIRK(0x17aa, 0x3819, "Lenovo 13s Gen2 ITL", ALC287_FIXUP_13S_GEN2_SPEAKERS),
+ HDA_CODEC_QUIRK(0x17aa, 0x3820, "IdeaPad 330-17IKB 81DM", ALC269_FIXUP_ASPIRE_HEADSET_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x3820, "Yoga Duet 7 13ITL6", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3824, "Legion Y9000X 2020", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3827, "Ideapad S740", ALC285_FIXUP_IDEAPAD_S740_COEF),
+ SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x383d, "Legion Y9000X 2019", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3843, "Yoga 9i", ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP),
+ SND_PCI_QUIRK(0x17aa, 0x3847, "Legion 7 16ACHG6", ALC287_FIXUP_LEGION_16ACHG6),
+ SND_PCI_QUIRK(0x17aa, 0x384a, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3852, "Lenovo Yoga 7 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3853, "Lenovo Yoga 7 15ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x3855, "Legion 7 16ITHG6", ALC287_FIXUP_LEGION_16ITHG6),
+ SND_PCI_QUIRK(0x17aa, 0x3865, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x3866, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
+ HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_SPEAKER2_TO_DAC1),
+ HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */
+ SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3877, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x3878, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x387d, "Yoga S780-16 pro Quad AAC", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x387e, "Yoga S780-16 pro Quad YC", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x387f, "Yoga S780-16 pro dual LX", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3880, "Yoga S780-16 pro dual YC", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3881, "YB9 dual power mode2 YC", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3882, "Lenovo Yoga Pro 7 14APH8", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x3884, "Y780 YG DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3886, "Y780 VECO DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3891, "Lenovo Yoga Pro 7 14AHP9", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x38a5, "Y580P AMD dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38a7, "Y780P AMD YG dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38a8, "Y780P AMD VECO dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38a9, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x38ab, "Thinkbook 16P", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x38b4, "Legion Slim 7 16IRH8", ALC287_FIXUP_CS35L41_I2C_2),
+ HDA_CODEC_QUIRK(0x17aa, 0x391c, "Lenovo Yoga 7 2-in-1 14AKP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x38b5, "Legion Slim 7 16IRH8", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x38b6, "Legion Slim 7 16APH8", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x38b7, "Legion Slim 7 16APH8", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x17aa, 0x38b8, "Yoga S780-14.5 proX AMD YC Dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38b9, "Yoga S780-14.5 proX AMD LX Dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38ba, "Yoga S780-14.5 Air AMD quad YC", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38bb, "Yoga S780-14.5 Air AMD quad AAC", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38be, "Yoga S980-14.5 proX YC Dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38bf, "Yoga S980-14.5 proX LX Dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38c3, "Y980 DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38c7, "Thinkbook 13x Gen 4", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x38c8, "Thinkbook 13x Gen 4", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x38cb, "Y790 YG DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38cd, "Y790 VECO DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38d2, "Lenovo Yoga 9 14IMH9", ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x38d3, "Yoga S990-16 Pro IMH YC Dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38d4, "Yoga S990-16 Pro IMH VECO Dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38d5, "Yoga S990-16 Pro IMH YC Quad", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38d6, "Yoga S990-16 Pro IMH VECO Quad", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38d7, "Lenovo Yoga 9 14IMH9", ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x38df, "Yoga Y990 Intel YC Dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38e0, "Yoga Y990 Intel VECO Dual", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38f8, "Yoga Book 9i", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38df, "Y990 YG DUAL", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x38f9, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x38fa, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x38fd, "ThinkBook plus Gen5 Hybrid", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
+ SND_PCI_QUIRK(0x17aa, 0x390d, "Lenovo Yoga Pro 7 14ASP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC),
+ SND_PCI_QUIRK(0x17aa, 0x391f, "Yoga S990-16 pro Quad YC Quad", ALC287_FIXUP_TXNW2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_TXNW2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3929, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x392b, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
+ SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
+ SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
+ SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x501e, "Thinkpad L440", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x5034, "Thinkpad T450", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x5036, "Thinkpad T450s", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x503c, "Thinkpad L450", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x504a, "ThinkPad X260", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x504b, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE),
+ SND_PCI_QUIRK(0x17aa, 0x5050, "Thinkpad T560p", ALC292_FIXUP_TPT460),
+ SND_PCI_QUIRK(0x17aa, 0x5051, "Thinkpad L460", ALC292_FIXUP_TPT460),
+ SND_PCI_QUIRK(0x17aa, 0x5053, "Thinkpad T460", ALC292_FIXUP_TPT460),
+ SND_PCI_QUIRK(0x17aa, 0x505d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x505f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x5062, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x508b, "Thinkpad X12 Gen 1", ALC287_FIXUP_LEGION_15IMHG05_SPEAKERS),
+ SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x511e, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
+ SND_PCI_QUIRK(0x17aa, 0x9e56, "Lenovo ZhaoYang CF4620Z", ALC286_FIXUP_SONY_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1849, 0x0269, "Positivo Master C6400", ALC269VB_FIXUP_ASUS_ZENBOOK),
+ SND_PCI_QUIRK(0x1849, 0x1233, "ASRock NUC Box 1100", ALC233_FIXUP_NO_AUDIO_JACK),
+ SND_PCI_QUIRK(0x1849, 0xa233, "Positivo Master C6300", ALC269_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1854, 0x0440, "LG CQ6", ALC256_FIXUP_HEADPHONE_AMP_VOL),
+ SND_PCI_QUIRK(0x1854, 0x0441, "LG CQ6 AIO", ALC256_FIXUP_HEADPHONE_AMP_VOL),
+ SND_PCI_QUIRK(0x1854, 0x0488, "LG gram 16 (16Z90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS),
+ SND_PCI_QUIRK(0x1854, 0x0489, "LG gram 16 (16Z90R-A)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS),
+ SND_PCI_QUIRK(0x1854, 0x048a, "LG gram 17 (17ZD90R)", ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS),
+ SND_PCI_QUIRK(0x19e5, 0x3204, "Huawei MACH-WX9", ALC256_FIXUP_HUAWEI_MACH_WX9_PINS),
+ SND_PCI_QUIRK(0x19e5, 0x320f, "Huawei WRT-WX9 ", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x19e5, 0x3212, "Huawei KLV-WX9 ", ALC256_FIXUP_ACER_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1b35, 0x1235, "CZC B20", ALC269_FIXUP_CZC_B20),
+ SND_PCI_QUIRK(0x1b35, 0x1236, "CZC TMI", ALC269_FIXUP_CZC_TMI),
+ SND_PCI_QUIRK(0x1b35, 0x1237, "CZC L101", ALC269_FIXUP_CZC_L101),
+ SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
+ SND_PCI_QUIRK(0x1c06, 0x2013, "Lemote A1802", ALC269_FIXUP_LEMOTE_A1802),
+ SND_PCI_QUIRK(0x1c06, 0x2015, "Lemote A190X", ALC269_FIXUP_LEMOTE_A190X),
+ SND_PCI_QUIRK(0x1c6c, 0x122a, "Positivo N14AP7", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1c6c, 0x1251, "Positivo N14KP6-TG", ALC288_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1d05, 0x1132, "TongFang PHxTxX1", ALC256_FIXUP_SET_COEF_DEFAULTS),
+ SND_PCI_QUIRK(0x1d05, 0x1096, "TongFang GMxMRxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x1100, "TongFang GKxNRxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x1111, "TongFang GMxZGxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x1119, "TongFang GMxZGxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x1129, "TongFang GMxZGxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x1147, "TongFang GMxTGxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x115c, "TongFang GMxTGxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x121b, "TongFang GMxAGxx", ALC269_FIXUP_NO_SHUTUP),
+ SND_PCI_QUIRK(0x1d05, 0x1387, "TongFang GMxIXxx", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1d05, 0x1409, "TongFang GMxIXxx", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1d05, 0x300f, "TongFang X6AR5xxY", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1d05, 0x3019, "TongFang X6FR5xxY", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS),
+ SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1d72, 0x1945, "Redmi G", ALC256_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1d72, 0x1947, "RedmiBook Air", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1ee7, 0x2078, "HONOR BRB-X M1010", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1f66, 0x0105, "Ayaneo Portable Game Player", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x2014, 0x800a, "Positivo ARN50", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13),
+ SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO),
+ SND_PCI_QUIRK(0x2782, 0x1407, "Positivo P15X", ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC),
+ SND_PCI_QUIRK(0x2782, 0x1409, "Positivo K116J", ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC),
+ SND_PCI_QUIRK(0x2782, 0x1701, "Infinix Y4 Max", ALC269VC_FIXUP_INFINIX_Y4_MAX),
+ SND_PCI_QUIRK(0x2782, 0x1705, "MEDION E15433", ALC269VC_FIXUP_INFINIX_Y4_MAX),
+ SND_PCI_QUIRK(0x2782, 0x1707, "Vaio VJFE-ADL", ALC298_FIXUP_SPK_VOLUME),
+ SND_PCI_QUIRK(0x2782, 0x4900, "MEDION E15443", ALC233_FIXUP_MEDION_MTL_SPK),
+ SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC),
+ SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED),
+ SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10),
+ SND_PCI_QUIRK(0x8086, 0x3038, "Intel NUC 13", ALC295_FIXUP_CHROME_BOOK),
+ SND_PCI_QUIRK(0xf111, 0x0001, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0xf111, 0x0006, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0xf111, 0x0009, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0xf111, 0x000b, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0xf111, 0x000c, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE),
+
+#if 0
+ /* Below is a quirk table taken from the old code.
+ * Basically the device should work as is without the fixup table.
+ * If BIOS doesn't give a proper info, enable the corresponding
+ * fixup entry.
+ */
+ SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A",
+ ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x11b3, "ASUS K52DR", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x11e3, "ASUS U33Jc", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80Jt", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82JV", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x12d3, "ASUS N61Jv", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1593, "ASUS N51Vn", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_FIXUP_DMIC),
+ SND_PCI_QUIRK(0x17aa, 0x3be9, "Quanta Wistron", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_AMIC),
+ SND_PCI_QUIRK(0x17ff, 0x059a, "Quanta EL3", ALC269_FIXUP_DMIC),
+ SND_PCI_QUIRK(0x17ff, 0x059b, "Quanta JR1", ALC269_FIXUP_DMIC),
+#endif
+ {}
+};
+
+static const struct hda_quirk alc269_fixup_vendor_tbl[] = {
+ SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC),
+ SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
+ SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo XPAD", ALC269_FIXUP_LENOVO_XPAD_ACPI),
+ SND_PCI_QUIRK_VENDOR(0x19e5, "Huawei Matebook", ALC255_FIXUP_MIC_MUTE_LED),
+ {}
+};
+
+static const struct hda_model_fixup alc269_fixup_models[] = {
+ {.id = ALC269_FIXUP_AMIC, .name = "laptop-amic"},
+ {.id = ALC269_FIXUP_DMIC, .name = "laptop-dmic"},
+ {.id = ALC269_FIXUP_STEREO_DMIC, .name = "alc269-dmic"},
+ {.id = ALC271_FIXUP_DMIC, .name = "alc271-dmic"},
+ {.id = ALC269_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {.id = ALC269_FIXUP_HEADSET_MIC, .name = "headset-mic"},
+ {.id = ALC269_FIXUP_HEADSET_MODE, .name = "headset-mode"},
+ {.id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, .name = "headset-mode-no-hp-mic"},
+ {.id = ALC269_FIXUP_LENOVO_DOCK, .name = "lenovo-dock"},
+ {.id = ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST, .name = "lenovo-dock-limit-boost"},
+ {.id = ALC269_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"},
+ {.id = ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, .name = "hp-dock-gpio-mic1-led"},
+ {.id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "dell-headset-multi"},
+ {.id = ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "dell-headset-dock"},
+ {.id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, .name = "dell-headset3"},
+ {.id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, .name = "dell-headset4"},
+ {.id = ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET, .name = "dell-headset4-quiet"},
+ {.id = ALC283_FIXUP_CHROME_BOOK, .name = "alc283-dac-wcaps"},
+ {.id = ALC283_FIXUP_SENSE_COMBO_JACK, .name = "alc283-sense-combo"},
+ {.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"},
+ {.id = ALC292_FIXUP_TPT440, .name = "tpt440"},
+ {.id = ALC292_FIXUP_TPT460, .name = "tpt460"},
+ {.id = ALC298_FIXUP_TPT470_DOCK_FIX, .name = "tpt470-dock-fix"},
+ {.id = ALC298_FIXUP_TPT470_DOCK, .name = "tpt470-dock"},
+ {.id = ALC233_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"},
+ {.id = ALC700_FIXUP_INTEL_REFERENCE, .name = "alc700-ref"},
+ {.id = ALC269_FIXUP_SONY_VAIO, .name = "vaio"},
+ {.id = ALC269_FIXUP_DELL_M101Z, .name = "dell-m101z"},
+ {.id = ALC269_FIXUP_ASUS_G73JW, .name = "asus-g73jw"},
+ {.id = ALC269_FIXUP_LENOVO_EAPD, .name = "lenovo-eapd"},
+ {.id = ALC275_FIXUP_SONY_HWEQ, .name = "sony-hweq"},
+ {.id = ALC269_FIXUP_PCM_44K, .name = "pcm44k"},
+ {.id = ALC269_FIXUP_LIFEBOOK, .name = "lifebook"},
+ {.id = ALC269_FIXUP_LIFEBOOK_EXTMIC, .name = "lifebook-extmic"},
+ {.id = ALC269_FIXUP_LIFEBOOK_HP_PIN, .name = "lifebook-hp-pin"},
+ {.id = ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC, .name = "lifebook-u7x7"},
+ {.id = ALC269VB_FIXUP_AMIC, .name = "alc269vb-amic"},
+ {.id = ALC269VB_FIXUP_DMIC, .name = "alc269vb-dmic"},
+ {.id = ALC269_FIXUP_HP_MUTE_LED_MIC1, .name = "hp-mute-led-mic1"},
+ {.id = ALC269_FIXUP_HP_MUTE_LED_MIC2, .name = "hp-mute-led-mic2"},
+ {.id = ALC269_FIXUP_HP_MUTE_LED_MIC3, .name = "hp-mute-led-mic3"},
+ {.id = ALC269_FIXUP_HP_GPIO_MIC1_LED, .name = "hp-gpio-mic1"},
+ {.id = ALC269_FIXUP_HP_LINE1_MIC1_LED, .name = "hp-line1-mic1"},
+ {.id = ALC269_FIXUP_NO_SHUTUP, .name = "noshutup"},
+ {.id = ALC286_FIXUP_SONY_MIC_NO_PRESENCE, .name = "sony-nomic"},
+ {.id = ALC269_FIXUP_ASPIRE_HEADSET_MIC, .name = "aspire-headset-mic"},
+ {.id = ALC269_FIXUP_ASUS_X101, .name = "asus-x101"},
+ {.id = ALC271_FIXUP_HP_GATE_MIC_JACK, .name = "acer-ao7xx"},
+ {.id = ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572, .name = "acer-aspire-e1"},
+ {.id = ALC269_FIXUP_ACER_AC700, .name = "acer-ac700"},
+ {.id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST, .name = "limit-mic-boost"},
+ {.id = ALC269VB_FIXUP_ASUS_ZENBOOK, .name = "asus-zenbook"},
+ {.id = ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A, .name = "asus-zenbook-ux31a"},
+ {.id = ALC269VB_FIXUP_ORDISSIMO_EVE2, .name = "ordissimo"},
+ {.id = ALC282_FIXUP_ASUS_TX300, .name = "asus-tx300"},
+ {.id = ALC283_FIXUP_INT_MIC, .name = "alc283-int-mic"},
+ {.id = ALC290_FIXUP_MONO_SPEAKERS_HSJACK, .name = "mono-speakers"},
+ {.id = ALC290_FIXUP_SUBWOOFER_HSJACK, .name = "alc290-subwoofer"},
+ {.id = ALC269_FIXUP_THINKPAD_ACPI, .name = "thinkpad"},
+ {.id = ALC269_FIXUP_LENOVO_XPAD_ACPI, .name = "lenovo-xpad-led"},
+ {.id = ALC269_FIXUP_DMIC_THINKPAD_ACPI, .name = "dmic-thinkpad"},
+ {.id = ALC255_FIXUP_ACER_MIC_NO_PRESENCE, .name = "alc255-acer"},
+ {.id = ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, .name = "alc255-asus"},
+ {.id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc255-dell1"},
+ {.id = ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, .name = "alc255-dell2"},
+ {.id = ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc293-dell1"},
+ {.id = ALC283_FIXUP_HEADSET_MIC, .name = "alc283-headset"},
+ {.id = ALC255_FIXUP_MIC_MUTE_LED, .name = "alc255-dell-mute"},
+ {.id = ALC282_FIXUP_ASPIRE_V5_PINS, .name = "aspire-v5"},
+ {.id = ALC269VB_FIXUP_ASPIRE_E1_COEF, .name = "aspire-e1-coef"},
+ {.id = ALC280_FIXUP_HP_GPIO4, .name = "hp-gpio4"},
+ {.id = ALC286_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"},
+ {.id = ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY, .name = "hp-gpio2-hotkey"},
+ {.id = ALC280_FIXUP_HP_DOCK_PINS, .name = "hp-dock-pins"},
+ {.id = ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, .name = "hp-dock-gpio-mic"},
+ {.id = ALC280_FIXUP_HP_9480M, .name = "hp-9480m"},
+ {.id = ALC288_FIXUP_DELL_HEADSET_MODE, .name = "alc288-dell-headset"},
+ {.id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc288-dell1"},
+ {.id = ALC288_FIXUP_DELL_XPS_13, .name = "alc288-dell-xps13"},
+ {.id = ALC292_FIXUP_DELL_E7X, .name = "dell-e7x"},
+ {.id = ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK, .name = "alc293-dell"},
+ {.id = ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc298-dell1"},
+ {.id = ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE, .name = "alc298-dell-aio"},
+ {.id = ALC275_FIXUP_DELL_XPS, .name = "alc275-dell-xps"},
+ {.id = ALC293_FIXUP_LENOVO_SPK_NOISE, .name = "lenovo-spk-noise"},
+ {.id = ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY, .name = "lenovo-hotkey"},
+ {.id = ALC255_FIXUP_DELL_SPK_NOISE, .name = "dell-spk-noise"},
+ {.id = ALC225_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "alc225-dell1"},
+ {.id = ALC295_FIXUP_DISABLE_DAC3, .name = "alc295-disable-dac3"},
+ {.id = ALC285_FIXUP_SPEAKER2_TO_DAC1, .name = "alc285-speaker2-to-dac1"},
+ {.id = ALC280_FIXUP_HP_HEADSET_MIC, .name = "alc280-hp-headset"},
+ {.id = ALC221_FIXUP_HP_FRONT_MIC, .name = "alc221-hp-mic"},
+ {.id = ALC298_FIXUP_SPK_VOLUME, .name = "alc298-spk-volume"},
+ {.id = ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER, .name = "dell-inspiron-7559"},
+ {.id = ALC269_FIXUP_ATIV_BOOK_8, .name = "ativ-book"},
+ {.id = ALC221_FIXUP_HP_MIC_NO_PRESENCE, .name = "alc221-hp-mic"},
+ {.id = ALC256_FIXUP_ASUS_HEADSET_MODE, .name = "alc256-asus-headset"},
+ {.id = ALC256_FIXUP_ASUS_MIC, .name = "alc256-asus-mic"},
+ {.id = ALC256_FIXUP_ASUS_AIO_GPIO2, .name = "alc256-asus-aio"},
+ {.id = ALC233_FIXUP_ASUS_MIC_NO_PRESENCE, .name = "alc233-asus"},
+ {.id = ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE, .name = "alc233-eapd"},
+ {.id = ALC294_FIXUP_LENOVO_MIC_LOCATION, .name = "alc294-lenovo-mic"},
+ {.id = ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE, .name = "alc225-wyse"},
+ {.id = ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, .name = "alc274-dell-aio"},
+ {.id = ALC255_FIXUP_DUMMY_LINEOUT_VERB, .name = "alc255-dummy-lineout"},
+ {.id = ALC255_FIXUP_DELL_HEADSET_MIC, .name = "alc255-dell-headset"},
+ {.id = ALC295_FIXUP_HP_X360, .name = "alc295-hp-x360"},
+ {.id = ALC225_FIXUP_HEADSET_JACK, .name = "alc-headset-jack"},
+ {.id = ALC295_FIXUP_CHROME_BOOK, .name = "alc-chrome-book"},
+ {.id = ALC256_FIXUP_CHROME_BOOK, .name = "alc-2024y-chromebook"},
+ {.id = ALC299_FIXUP_PREDATOR_SPK, .name = "predator-spk"},
+ {.id = ALC298_FIXUP_HUAWEI_MBX_STEREO, .name = "huawei-mbx-stereo"},
+ {.id = ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE, .name = "alc256-medion-headset"},
+ {.id = ALC298_FIXUP_SAMSUNG_AMP, .name = "alc298-samsung-amp"},
+ {.id = ALC298_FIXUP_SAMSUNG_AMP_V2_2_AMPS, .name = "alc298-samsung-amp-v2-2-amps"},
+ {.id = ALC298_FIXUP_SAMSUNG_AMP_V2_4_AMPS, .name = "alc298-samsung-amp-v2-4-amps"},
+ {.id = ALC256_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, .name = "alc256-samsung-headphone"},
+ {.id = ALC255_FIXUP_XIAOMI_HEADSET_MIC, .name = "alc255-xiaomi-headset"},
+ {.id = ALC274_FIXUP_HP_MIC, .name = "alc274-hp-mic-detect"},
+ {.id = ALC245_FIXUP_HP_X360_AMP, .name = "alc245-hp-x360-amp"},
+ {.id = ALC295_FIXUP_HP_OMEN, .name = "alc295-hp-omen"},
+ {.id = ALC285_FIXUP_HP_SPECTRE_X360, .name = "alc285-hp-spectre-x360"},
+ {.id = ALC285_FIXUP_HP_SPECTRE_X360_EB1, .name = "alc285-hp-spectre-x360-eb1"},
+ {.id = ALC285_FIXUP_HP_SPECTRE_X360_DF1, .name = "alc285-hp-spectre-x360-df1"},
+ {.id = ALC285_FIXUP_HP_ENVY_X360, .name = "alc285-hp-envy-x360"},
+ {.id = ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP, .name = "alc287-ideapad-bass-spk-amp"},
+ {.id = ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN, .name = "alc287-yoga9-bass-spk-pin"},
+ {.id = ALC623_FIXUP_LENOVO_THINKSTATION_P340, .name = "alc623-lenovo-thinkstation-p340"},
+ {.id = ALC255_FIXUP_ACER_HEADPHONE_AND_MIC, .name = "alc255-acer-headphone-and-mic"},
+ {.id = ALC285_FIXUP_HP_GPIO_AMP_INIT, .name = "alc285-hp-amp-init"},
+ {.id = ALC236_FIXUP_LENOVO_INV_DMIC, .name = "alc236-fixup-lenovo-inv-mic"},
+ {.id = ALC2XX_FIXUP_HEADSET_MIC, .name = "alc2xx-fixup-headset-mic"},
+ {}
+};
+#define ALC225_STANDARD_PINS \
+ {0x21, 0x04211020}
+
+#define ALC256_STANDARD_PINS \
+ {0x12, 0x90a60140}, \
+ {0x14, 0x90170110}, \
+ {0x21, 0x02211020}
+
+#define ALC282_STANDARD_PINS \
+ {0x14, 0x90170110}
+
+#define ALC290_STANDARD_PINS \
+ {0x12, 0x99a30130}
+
+#define ALC292_STANDARD_PINS \
+ {0x14, 0x90170110}, \
+ {0x15, 0x0221401f}
+
+#define ALC295_STANDARD_PINS \
+ {0x12, 0xb7a60130}, \
+ {0x14, 0x90170110}, \
+ {0x21, 0x04211020}
+
+#define ALC298_STANDARD_PINS \
+ {0x12, 0x90a60130}, \
+ {0x21, 0x03211020}
+
+static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
+ SND_HDA_PIN_QUIRK(0x10ec0221, 0x103c, "HP Workstation", ALC221_FIXUP_HP_HEADSET_MIC,
+ {0x14, 0x01014020},
+ {0x17, 0x90170110},
+ {0x18, 0x02a11030},
+ {0x19, 0x0181303F},
+ {0x21, 0x0221102f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1025, "Acer", ALC255_FIXUP_ACER_MIC_NO_PRESENCE,
+ {0x12, 0x90a601c0},
+ {0x14, 0x90171120},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1043, "ASUS", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x14, 0x90170110},
+ {0x1b, 0x90a70130},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1043, "ASUS", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x1a, 0x90a70130},
+ {0x1b, 0x90170110},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60130},
+ {0x14, 0x901701a0}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60130},
+ {0x14, 0x901701b0}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60150},
+ {0x14, 0x901701a0}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60150},
+ {0x14, 0x901701b0}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60130},
+ {0x1b, 0x90170110}),
+ SND_HDA_PIN_QUIRK(0x10ec0233, 0x8086, "Intel NUC Skull Canyon", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x1b, 0x01111010},
+ {0x1e, 0x01451130},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY,
+ {0x12, 0x90a60140},
+ {0x14, 0x90170110},
+ {0x19, 0x02a11030},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC294_FIXUP_LENOVO_MIC_LOCATION,
+ {0x14, 0x90170110},
+ {0x19, 0x02a11030},
+ {0x1a, 0x02a11040},
+ {0x1b, 0x01014020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC294_FIXUP_LENOVO_MIC_LOCATION,
+ {0x14, 0x90170110},
+ {0x19, 0x02a11030},
+ {0x1a, 0x02a11040},
+ {0x1b, 0x01011020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC294_FIXUP_LENOVO_MIC_LOCATION,
+ {0x14, 0x90170110},
+ {0x19, 0x02a11020},
+ {0x1a, 0x02a11030},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC,
+ {0x21, 0x02211010}),
+ SND_HDA_PIN_QUIRK(0x10ec0236, 0x103c, "HP", ALC256_FIXUP_HP_HEADSET_MIC,
+ {0x14, 0x90170110},
+ {0x19, 0x02a11020},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
+ {0x14, 0x90170110},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170130},
+ {0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60140},
+ {0x14, 0x90170110},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60160},
+ {0x14, 0x90170120},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170110},
+ {0x1b, 0x02011020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170110},
+ {0x1b, 0x01011020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170130},
+ {0x1b, 0x01014020},
+ {0x21, 0x0221103f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170130},
+ {0x1b, 0x01011020},
+ {0x21, 0x0221103f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170130},
+ {0x1b, 0x02011020},
+ {0x21, 0x0221103f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170150},
+ {0x1b, 0x02011020},
+ {0x21, 0x0221105f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170110},
+ {0x1b, 0x01014020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60160},
+ {0x14, 0x90170120},
+ {0x17, 0x90170140},
+ {0x21, 0x0321102f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60160},
+ {0x14, 0x90170130},
+ {0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60160},
+ {0x14, 0x90170140},
+ {0x21, 0x02211050}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60170},
+ {0x14, 0x90170120},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60170},
+ {0x14, 0x90170130},
+ {0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60170},
+ {0x14, 0x90171130},
+ {0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60170},
+ {0x14, 0x90170140},
+ {0x21, 0x02211050}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell Inspiron 5548", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60180},
+ {0x14, 0x90170130},
+ {0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell Inspiron 5565", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60180},
+ {0x14, 0x90170120},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x1b, 0x01011020},
+ {0x21, 0x02211010}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC,
+ {0x14, 0x90170110},
+ {0x1b, 0x90a70130},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC,
+ {0x14, 0x90170110},
+ {0x1b, 0x90a70130},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x12, 0x90a60130},
+ {0x14, 0x90170110},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x12, 0x90a60130},
+ {0x14, 0x90170110},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x1a, 0x90a70130},
+ {0x1b, 0x90170110},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x103c, "HP", ALC256_FIXUP_HP_HEADSET_MIC,
+ {0x14, 0x90170110},
+ {0x19, 0x02a11020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0274, 0x103c, "HP", ALC274_FIXUP_HP_HEADSET_MIC,
+ {0x17, 0x90170110},
+ {0x19, 0x03a11030},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4,
+ {0x12, 0x90a60130},
+ {0x14, 0x90170110},
+ {0x15, 0x0421101f},
+ {0x1a, 0x04a11020}),
+ SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED,
+ {0x12, 0x90a60140},
+ {0x14, 0x90170110},
+ {0x15, 0x0421101f},
+ {0x18, 0x02811030},
+ {0x1a, 0x04a1103f},
+ {0x1b, 0x02011020}),
+ SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP 15 Touchsmart", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC282_STANDARD_PINS,
+ {0x12, 0x99a30130},
+ {0x19, 0x03a11020},
+ {0x21, 0x0321101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC282_STANDARD_PINS,
+ {0x12, 0x99a30130},
+ {0x19, 0x03a11020},
+ {0x21, 0x03211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC282_STANDARD_PINS,
+ {0x12, 0x99a30130},
+ {0x19, 0x03a11030},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC282_STANDARD_PINS,
+ {0x12, 0x99a30130},
+ {0x19, 0x04a11020},
+ {0x21, 0x0421101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0282, 0x103c, "HP", ALC269_FIXUP_HP_LINE1_MIC1_LED,
+ ALC282_STANDARD_PINS,
+ {0x12, 0x90a60140},
+ {0x19, 0x04a11030},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0282, 0x1025, "Acer", ALC282_FIXUP_ACER_DISABLE_LINEOUT,
+ ALC282_STANDARD_PINS,
+ {0x12, 0x90a609c0},
+ {0x18, 0x03a11830},
+ {0x19, 0x04a19831},
+ {0x1a, 0x0481303f},
+ {0x1b, 0x04211020},
+ {0x21, 0x0321101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0282, 0x1025, "Acer", ALC282_FIXUP_ACER_DISABLE_LINEOUT,
+ ALC282_STANDARD_PINS,
+ {0x12, 0x90a60940},
+ {0x18, 0x03a11830},
+ {0x19, 0x04a19831},
+ {0x1a, 0x0481303f},
+ {0x1b, 0x04211020},
+ {0x21, 0x0321101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC282_STANDARD_PINS,
+ {0x12, 0x90a60130},
+ {0x21, 0x0321101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60160},
+ {0x14, 0x90170120},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC282_STANDARD_PINS,
+ {0x12, 0x90a60130},
+ {0x19, 0x03a11020},
+ {0x21, 0x0321101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE,
+ {0x12, 0x90a60130},
+ {0x14, 0x90170110},
+ {0x19, 0x04a11040},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE,
+ {0x14, 0x90170110},
+ {0x19, 0x04a11040},
+ {0x1d, 0x40600001},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK,
+ {0x14, 0x90170110},
+ {0x19, 0x04a11040},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC285_FIXUP_THINKPAD_HEADSET_JACK,
+ {0x14, 0x90170110},
+ {0x17, 0x90170111},
+ {0x19, 0x03a11030},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC287_FIXUP_THINKPAD_I2S_SPK,
+ {0x17, 0x90170110},
+ {0x19, 0x03a11030},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC287_FIXUP_THINKPAD_I2S_SPK,
+ {0x17, 0x90170110}, /* 0x231f with RTK I2S AMP */
+ {0x19, 0x04a11040},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0286, 0x1025, "Acer", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE,
+ {0x12, 0x90a60130},
+ {0x17, 0x90170110},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0288, 0x1028, "Dell", ALC288_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60120},
+ {0x14, 0x90170110},
+ {0x21, 0x0321101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC290_STANDARD_PINS,
+ {0x15, 0x04211040},
+ {0x18, 0x90170112},
+ {0x1a, 0x04a11020}),
+ SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC290_STANDARD_PINS,
+ {0x15, 0x04211040},
+ {0x18, 0x90170110},
+ {0x1a, 0x04a11020}),
+ SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC290_STANDARD_PINS,
+ {0x15, 0x0421101f},
+ {0x1a, 0x04a11020}),
+ SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC290_STANDARD_PINS,
+ {0x15, 0x04211020},
+ {0x1a, 0x04a11040}),
+ SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC290_STANDARD_PINS,
+ {0x14, 0x90170110},
+ {0x15, 0x04211020},
+ {0x1a, 0x04a11040}),
+ SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC290_STANDARD_PINS,
+ {0x14, 0x90170110},
+ {0x15, 0x04211020},
+ {0x1a, 0x04a11020}),
+ SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
+ ALC290_STANDARD_PINS,
+ {0x14, 0x90170110},
+ {0x15, 0x0421101f},
+ {0x1a, 0x04a11020}),
+ SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE,
+ ALC292_STANDARD_PINS,
+ {0x12, 0x90a60140},
+ {0x16, 0x01014020},
+ {0x19, 0x01a19030}),
+ SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE,
+ ALC292_STANDARD_PINS,
+ {0x12, 0x90a60140},
+ {0x16, 0x01014020},
+ {0x18, 0x02a19031},
+ {0x19, 0x01a1903e}),
+ SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
+ ALC292_STANDARD_PINS,
+ {0x12, 0x90a60140}),
+ SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC292_STANDARD_PINS,
+ {0x13, 0x90a60140},
+ {0x16, 0x21014020},
+ {0x19, 0x21a19030}),
+ SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC292_STANDARD_PINS,
+ {0x13, 0x90a60140}),
+ SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_HPE,
+ {0x17, 0x90170110},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_MIC,
+ {0x14, 0x90170110},
+ {0x1b, 0x90a70130},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK,
+ {0x12, 0x90a60130},
+ {0x17, 0x90170110},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK,
+ {0x12, 0x90a60130},
+ {0x17, 0x90170110},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK,
+ {0x12, 0x90a60130},
+ {0x17, 0x90170110},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x12, 0x90a60120},
+ {0x17, 0x90170110},
+ {0x21, 0x04211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x12, 0x90a60130},
+ {0x17, 0x90170110},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x12, 0x90a60130},
+ {0x17, 0x90170110},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC298_STANDARD_PINS,
+ {0x17, 0x90170110}),
+ SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC298_STANDARD_PINS,
+ {0x17, 0x90170140}),
+ SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC298_STANDARD_PINS,
+ {0x17, 0x90170150}),
+ SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_SPK_VOLUME,
+ {0x12, 0xb7a60140},
+ {0x13, 0xb7a60150},
+ {0x17, 0x90170110},
+ {0x1a, 0x03011020},
+ {0x21, 0x03211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_ALIENWARE_MIC_NO_PRESENCE,
+ {0x12, 0xb7a60140},
+ {0x17, 0x90170110},
+ {0x1a, 0x03a11030},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0299, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60130},
+ {0x17, 0x90170110}),
+ SND_HDA_PIN_QUIRK(0x10ec0623, 0x17aa, "Lenovo", ALC283_FIXUP_HEADSET_MIC,
+ {0x14, 0x01014010},
+ {0x17, 0x90170120},
+ {0x18, 0x02a11030},
+ {0x19, 0x02a1103f},
+ {0x21, 0x0221101f}),
+ {}
+};
+
+/* This is the fallback pin_fixup_tbl for alc269 family, to make the tbl match
+ * more machines, don't need to match all valid pins, just need to match
+ * all the pins defined in the tbl. Just because of this reason, it is possible
+ * that a single machine matches multiple tbls, so there is one limitation:
+ * at most one tbl is allowed to define for the same vendor and same codec
+ */
+static const struct snd_hda_pin_quirk alc269_fallback_pin_fixup_tbl[] = {
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1025, "Acer", ALC2XX_FIXUP_HEADSET_MIC,
+ {0x19, 0x40000000}),
+ SND_HDA_PIN_QUIRK(0x10ec0289, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE,
+ {0x19, 0x40000000},
+ {0x1b, 0x40000000}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE_QUIET,
+ {0x19, 0x40000000},
+ {0x1b, 0x40000000}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x19, 0x40000000},
+ {0x1a, 0x40000000}),
+ SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_LIMIT_INT_MIC_BOOST,
+ {0x19, 0x40000000},
+ {0x1a, 0x40000000}),
+ SND_HDA_PIN_QUIRK(0x10ec0274, 0x1028, "Dell", ALC269_FIXUP_DELL1_LIMIT_INT_MIC_BOOST,
+ {0x19, 0x40000000},
+ {0x1a, 0x40000000}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC2XX_FIXUP_HEADSET_MIC,
+ {0x19, 0x40000000}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1558, "Clevo", ALC2XX_FIXUP_HEADSET_MIC,
+ {0x19, 0x40000000}),
+ {}
+};
+
+static void alc269_fill_coef(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int val;
+
+ if (spec->codec_variant != ALC269_TYPE_ALC269VB)
+ return;
+
+ if ((alc_get_coef0(codec) & 0x00ff) < 0x015) {
+ alc_write_coef_idx(codec, 0xf, 0x960b);
+ alc_write_coef_idx(codec, 0xe, 0x8817);
+ }
+
+ if ((alc_get_coef0(codec) & 0x00ff) == 0x016) {
+ alc_write_coef_idx(codec, 0xf, 0x960b);
+ alc_write_coef_idx(codec, 0xe, 0x8814);
+ }
+
+ if ((alc_get_coef0(codec) & 0x00ff) == 0x017) {
+ /* Power up output pin */
+ alc_update_coef_idx(codec, 0x04, 0, 1<<11);
+ }
+
+ if ((alc_get_coef0(codec) & 0x00ff) == 0x018) {
+ val = alc_read_coef_idx(codec, 0xd);
+ if (val != -1 && (val & 0x0c00) >> 10 != 0x1) {
+ /* Capless ramp up clock control */
+ alc_write_coef_idx(codec, 0xd, val | (1<<10));
+ }
+ val = alc_read_coef_idx(codec, 0x17);
+ if (val != -1 && (val & 0x01c0) >> 6 != 0x4) {
+ /* Class D power on reset */
+ alc_write_coef_idx(codec, 0x17, val | (1<<7));
+ }
+ }
+
+ /* HP */
+ alc_update_coef_idx(codec, 0x4, 0, 1<<11);
+}
+
+static void alc269_remove(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec)
+ hda_component_manager_free(&spec->comps, &comp_master_ops);
+
+ snd_hda_gen_remove(codec);
+}
+
+/*
+ */
+static int alc269_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct alc_spec *spec;
+ int err;
+
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
+ spec->gen.shared_mic_vref_pin = 0x18;
+ codec->power_save_node = 0;
+ spec->en_3kpull_low = true;
+
+ spec->shutup = alc_default_shutup;
+ spec->init_hook = alc_default_init;
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0269:
+ spec->codec_variant = ALC269_TYPE_ALC269VA;
+ switch (alc_get_coef0(codec) & 0x00f0) {
+ case 0x0010:
+ if (codec->bus->pci &&
+ codec->bus->pci->subsystem_vendor == 0x1025 &&
+ spec->cdefine.platform_type == 1)
+ err = alc_codec_rename(codec, "ALC271X");
+ spec->codec_variant = ALC269_TYPE_ALC269VB;
+ break;
+ case 0x0020:
+ if (codec->bus->pci &&
+ codec->bus->pci->subsystem_vendor == 0x17aa &&
+ codec->bus->pci->subsystem_device == 0x21f3)
+ err = alc_codec_rename(codec, "ALC3202");
+ spec->codec_variant = ALC269_TYPE_ALC269VC;
+ break;
+ case 0x0030:
+ spec->codec_variant = ALC269_TYPE_ALC269VD;
+ break;
+ default:
+ alc_fix_pll_init(codec, 0x20, 0x04, 15);
+ }
+ if (err < 0)
+ goto error;
+ spec->shutup = alc269_shutup;
+ spec->init_hook = alc269_fill_coef;
+ alc269_fill_coef(codec);
+ break;
+
+ case 0x10ec0280:
+ case 0x10ec0290:
+ spec->codec_variant = ALC269_TYPE_ALC280;
+ break;
+ case 0x10ec0282:
+ spec->codec_variant = ALC269_TYPE_ALC282;
+ spec->shutup = alc282_shutup;
+ spec->init_hook = alc282_init;
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ spec->codec_variant = ALC269_TYPE_ALC283;
+ spec->shutup = alc283_shutup;
+ spec->init_hook = alc283_init;
+ break;
+ case 0x10ec0284:
+ case 0x10ec0292:
+ spec->codec_variant = ALC269_TYPE_ALC284;
+ break;
+ case 0x10ec0293:
+ spec->codec_variant = ALC269_TYPE_ALC293;
+ break;
+ case 0x10ec0286:
+ case 0x10ec0288:
+ spec->codec_variant = ALC269_TYPE_ALC286;
+ break;
+ case 0x10ec0298:
+ spec->codec_variant = ALC269_TYPE_ALC298;
+ break;
+ case 0x10ec0235:
+ case 0x10ec0255:
+ spec->codec_variant = ALC269_TYPE_ALC255;
+ spec->shutup = alc256_shutup;
+ spec->init_hook = alc256_init;
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ spec->codec_variant = ALC269_TYPE_ALC256;
+ spec->shutup = alc256_shutup;
+ spec->init_hook = alc256_init;
+ spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */
+ if (codec->core.vendor_id == 0x10ec0236 &&
+ codec->bus->pci->vendor != PCI_VENDOR_ID_AMD)
+ spec->en_3kpull_low = false;
+ break;
+ case 0x10ec0257:
+ spec->codec_variant = ALC269_TYPE_ALC257;
+ spec->shutup = alc256_shutup;
+ spec->init_hook = alc256_init;
+ spec->gen.mixer_nid = 0;
+ spec->en_3kpull_low = false;
+ break;
+ case 0x10ec0215:
+ case 0x10ec0245:
+ case 0x10ec0285:
+ case 0x10ec0289:
+ if (alc_get_coef0(codec) & 0x0010)
+ spec->codec_variant = ALC269_TYPE_ALC245;
+ else
+ spec->codec_variant = ALC269_TYPE_ALC215;
+ spec->shutup = alc225_shutup;
+ spec->init_hook = alc225_init;
+ spec->gen.mixer_nid = 0;
+ break;
+ case 0x10ec0225:
+ case 0x10ec0295:
+ case 0x10ec0299:
+ spec->codec_variant = ALC269_TYPE_ALC225;
+ spec->shutup = alc225_shutup;
+ spec->init_hook = alc225_init;
+ spec->gen.mixer_nid = 0; /* no loopback on ALC225, ALC295 and ALC299 */
+ break;
+ case 0x10ec0287:
+ spec->codec_variant = ALC269_TYPE_ALC287;
+ spec->shutup = alc225_shutup;
+ spec->init_hook = alc225_init;
+ spec->gen.mixer_nid = 0; /* no loopback on ALC287 */
+ break;
+ case 0x10ec0234:
+ case 0x10ec0274:
+ case 0x10ec0294:
+ spec->codec_variant = ALC269_TYPE_ALC294;
+ spec->gen.mixer_nid = 0; /* ALC2x4 does not have any loopback mixer path */
+ alc_update_coef_idx(codec, 0x6b, 0x0018, (1<<4) | (1<<3)); /* UAJ MIC Vref control by verb */
+ spec->init_hook = alc294_init;
+ break;
+ case 0x10ec0300:
+ spec->codec_variant = ALC269_TYPE_ALC300;
+ spec->gen.mixer_nid = 0; /* no loopback on ALC300 */
+ break;
+ case 0x10ec0222:
+ case 0x10ec0623:
+ spec->codec_variant = ALC269_TYPE_ALC623;
+ spec->shutup = alc222_shutup;
+ spec->init_hook = alc222_init;
+ break;
+ case 0x10ec0700:
+ case 0x10ec0701:
+ case 0x10ec0703:
+ case 0x10ec0711:
+ spec->codec_variant = ALC269_TYPE_ALC700;
+ spec->gen.mixer_nid = 0; /* ALC700 does not have any loopback mixer path */
+ alc_update_coef_idx(codec, 0x4a, 1 << 15, 0); /* Combo jack auto trigger control */
+ spec->init_hook = alc294_init;
+ break;
+
+ }
+
+ if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) {
+ spec->has_alc5505_dsp = 1;
+ spec->init_hook = alc5505_dsp_init;
+ }
+
+ alc_pre_init(codec);
+
+ snd_hda_pick_fixup(codec, alc269_fixup_models,
+ alc269_fixup_tbl, alc269_fixups);
+ /* FIXME: both TX300 and ROG Strix G17 have the same SSID, and
+ * the quirk breaks the latter (bko#214101).
+ * Clear the wrong entry.
+ */
+ if (codec->fixup_id == ALC282_FIXUP_ASUS_TX300 &&
+ codec->core.vendor_id == 0x10ec0294) {
+ codec_dbg(codec, "Clear wrong fixup for ASUS ROG Strix G17\n");
+ codec->fixup_id = HDA_FIXUP_ID_NOT_SET;
+ }
+
+ snd_hda_pick_pin_fixup(codec, alc269_pin_fixup_tbl, alc269_fixups, true);
+ snd_hda_pick_pin_fixup(codec, alc269_fallback_pin_fixup_tbl, alc269_fixups, false);
+ snd_hda_pick_fixup(codec, NULL, alc269_fixup_vendor_tbl,
+ alc269_fixups);
+
+ /*
+ * Check whether ACPI describes companion amplifiers that require
+ * component binding
+ */
+ find_cirrus_companion_amps(codec);
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ alc_auto_parse_customize_define(codec);
+
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
+
+ /* automatic parse from the BIOS config */
+ err = alc269_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ if (!spec->gen.no_analog && spec->gen.beep_nid && spec->gen.mixer_nid) {
+ err = set_beep_amp(spec, spec->gen.mixer_nid, 0x04, HDA_INPUT);
+ if (err < 0)
+ goto error;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ alc269_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops alc269_codec_ops = {
+ .probe = alc269_probe,
+ .remove = alc269_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = alc269_suspend,
+ .resume = alc269_resume,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc269[] = {
+ HDA_CODEC_ID(0x10ec0215, "ALC215"),
+ HDA_CODEC_ID(0x10ec0221, "ALC221"),
+ HDA_CODEC_ID(0x10ec0222, "ALC222"),
+ HDA_CODEC_ID(0x10ec0225, "ALC225"),
+ HDA_CODEC_ID(0x10ec0230, "ALC236"),
+ HDA_CODEC_ID(0x10ec0231, "ALC231"),
+ HDA_CODEC_ID(0x10ec0233, "ALC233"),
+ HDA_CODEC_ID(0x10ec0234, "ALC234"),
+ HDA_CODEC_ID(0x10ec0235, "ALC233"),
+ HDA_CODEC_ID(0x10ec0236, "ALC236"),
+ HDA_CODEC_ID(0x10ec0245, "ALC245"),
+ HDA_CODEC_ID(0x10ec0255, "ALC255"),
+ HDA_CODEC_ID(0x10ec0256, "ALC256"),
+ HDA_CODEC_ID(0x10ec0257, "ALC257"),
+ HDA_CODEC_ID(0x10ec0269, "ALC269"),
+ HDA_CODEC_ID(0x10ec0270, "ALC270"),
+ HDA_CODEC_ID(0x10ec0274, "ALC274"),
+ HDA_CODEC_ID(0x10ec0275, "ALC275"),
+ HDA_CODEC_ID(0x10ec0276, "ALC276"),
+ HDA_CODEC_ID(0x10ec0280, "ALC280"),
+ HDA_CODEC_ID(0x10ec0282, "ALC282"),
+ HDA_CODEC_ID(0x10ec0283, "ALC283"),
+ HDA_CODEC_ID(0x10ec0284, "ALC284"),
+ HDA_CODEC_ID(0x10ec0285, "ALC285"),
+ HDA_CODEC_ID(0x10ec0286, "ALC286"),
+ HDA_CODEC_ID(0x10ec0287, "ALC287"),
+ HDA_CODEC_ID(0x10ec0288, "ALC288"),
+ HDA_CODEC_ID(0x10ec0289, "ALC289"),
+ HDA_CODEC_ID(0x10ec0290, "ALC290"),
+ HDA_CODEC_ID(0x10ec0292, "ALC292"),
+ HDA_CODEC_ID(0x10ec0293, "ALC293"),
+ HDA_CODEC_ID(0x10ec0294, "ALC294"),
+ HDA_CODEC_ID(0x10ec0295, "ALC295"),
+ HDA_CODEC_ID(0x10ec0298, "ALC298"),
+ HDA_CODEC_ID(0x10ec0299, "ALC299"),
+ HDA_CODEC_ID(0x10ec0300, "ALC300"),
+ HDA_CODEC_ID(0x10ec0623, "ALC623"),
+ HDA_CODEC_ID(0x10ec0700, "ALC700"),
+ HDA_CODEC_ID(0x10ec0701, "ALC701"),
+ HDA_CODEC_ID(0x10ec0703, "ALC703"),
+ HDA_CODEC_ID(0x10ec0711, "ALC711"),
+ HDA_CODEC_ID(0x19e58326, "HW8326"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc269);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC269 and compatible HD-audio codecs");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+MODULE_IMPORT_NS("SND_HDA_SCODEC_COMPONENT");
+
+static struct hda_codec_driver alc269_driver = {
+ .id = snd_hda_id_alc269,
+ .ops = &alc269_codec_ops,
+};
+
+module_hda_codec_driver(alc269_driver);
diff --git a/sound/hda/codecs/realtek/alc662.c b/sound/hda/codecs/realtek/alc662.c
new file mode 100644
index 000000000000..5073165d1f3c
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc662.c
@@ -0,0 +1,1116 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek ALC662 and compatible codecs
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+/*
+ * ALC662 support
+ *
+ * ALC662 is almost identical with ALC880 but has cleaner and more flexible
+ * configuration. Each pin widget can choose any input DACs and a mixer.
+ * Each ADC is connected from a mixer of all inputs. This makes possible
+ * 6-channel independent captures.
+ *
+ * In addition, an independent DAC for the multi-playback (not used in this
+ * driver yet).
+ */
+
+/*
+ * BIOS auto configuration
+ */
+
+static int alc662_parse_auto_config(struct hda_codec *codec)
+{
+ static const hda_nid_t alc662_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc663_ssids[] = { 0x15, 0x1b, 0x14, 0x21 };
+ static const hda_nid_t alc662_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ const hda_nid_t *ssids;
+
+ if (codec->core.vendor_id == 0x10ec0272 || codec->core.vendor_id == 0x10ec0663 ||
+ codec->core.vendor_id == 0x10ec0665 || codec->core.vendor_id == 0x10ec0670 ||
+ codec->core.vendor_id == 0x10ec0671)
+ ssids = alc663_ssids;
+ else
+ ssids = alc662_ssids;
+ return alc_parse_auto_config(codec, alc662_ignore, ssids);
+}
+
+static void alc272_fixup_mario(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ if (snd_hda_override_amp_caps(codec, 0x2, HDA_OUTPUT,
+ (0x3b << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x3b << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x03 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (0 << AC_AMPCAP_MUTE_SHIFT)))
+ codec_warn(codec, "failed to override amp caps for NID 0x2\n");
+}
+
+/* avoid D3 for keeping GPIO up */
+static unsigned int gpio_led_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
+{
+ struct alc_spec *spec = codec->spec;
+ if (nid == codec->core.afg && power_state == AC_PWRST_D3 && spec->gpio_data)
+ return AC_PWRST_D0;
+ return power_state;
+}
+
+static void alc662_fixup_led_gpio1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ alc_fixup_hp_gpio_led(codec, action, 0x01, 0);
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_polarity = 1;
+ codec->power_filter = gpio_led_power_filter;
+ }
+}
+
+static void alc662_usi_automute_hook(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ int vref;
+ msleep(200);
+ snd_hda_gen_hp_automute(codec, jack);
+
+ vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0;
+ msleep(100);
+ snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ vref);
+}
+
+static void alc662_fixup_usi_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ spec->gen.hp_automute_hook = alc662_usi_automute_hook;
+ }
+}
+
+static void alc662_aspire_ethos_mute_speakers(struct hda_codec *codec,
+ struct hda_jack_callback *cb)
+{
+ /* surround speakers at 0x1b already get muted automatically when
+ * headphones are plugged in, but we have to mute/unmute the remaining
+ * channels manually:
+ * 0x15 - front left/front right
+ * 0x18 - front center/ LFE
+ */
+ if (snd_hda_jack_detect_state(codec, 0x1b) == HDA_JACK_PRESENT) {
+ snd_hda_set_pin_ctl_cache(codec, 0x15, 0);
+ snd_hda_set_pin_ctl_cache(codec, 0x18, 0);
+ } else {
+ snd_hda_set_pin_ctl_cache(codec, 0x15, PIN_OUT);
+ snd_hda_set_pin_ctl_cache(codec, 0x18, PIN_OUT);
+ }
+}
+
+static void alc662_fixup_aspire_ethos_hp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /* Pin 0x1b: shared headphones jack and surround speakers */
+ if (!is_jack_detectable(codec, 0x1b))
+ return;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_jack_detect_enable_callback(codec, 0x1b,
+ alc662_aspire_ethos_mute_speakers);
+ /* subwoofer needs an extra GPIO setting to become audible */
+ alc_setup_gpio(codec, 0x02);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ /* Make sure to start in a correct state, i.e. if
+ * headphones have been plugged in before powering up the system
+ */
+ alc662_aspire_ethos_mute_speakers(codec, NULL);
+ break;
+ }
+}
+
+static void alc671_fixup_hp_headset_mic2(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x19, 0x02a11040 }, /* use as headset mic, with its own jack detect */
+ { 0x1b, 0x0181304f },
+ { }
+ };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->gen.mixer_nid = 0;
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ alc_write_coef_idx(codec, 0x19, 0xa054);
+ break;
+ }
+}
+
+static void alc897_hp_automute_hook(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ int vref;
+
+ snd_hda_gen_hp_automute(codec, jack);
+ vref = spec->gen.hp_jack_present ? (PIN_HP | AC_PINCTL_VREF_100) : PIN_HP;
+ snd_hda_set_pin_ctl(codec, 0x1b, vref);
+}
+
+static void alc897_fixup_lenovo_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.hp_automute_hook = alc897_hp_automute_hook;
+ spec->no_shutup_pins = 1;
+ }
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ snd_hda_set_pin_ctl_cache(codec, 0x1a, PIN_IN | AC_PINCTL_VREF_100);
+ }
+}
+
+static void alc897_fixup_lenovo_headset_mode(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ spec->gen.hp_automute_hook = alc897_hp_automute_hook;
+ }
+}
+
+static const struct coef_fw alc668_coefs[] = {
+ WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0),
+ WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80),
+ WRITE_COEF(0x08, 0x0031), WRITE_COEF(0x0a, 0x0060), WRITE_COEF(0x0b, 0x0),
+ WRITE_COEF(0x0c, 0x7cf7), WRITE_COEF(0x0d, 0x1080), WRITE_COEF(0x0e, 0x7f7f),
+ WRITE_COEF(0x0f, 0xcccc), WRITE_COEF(0x10, 0xddcc), WRITE_COEF(0x11, 0x0001),
+ WRITE_COEF(0x13, 0x0), WRITE_COEF(0x14, 0x2aa0), WRITE_COEF(0x17, 0xa940),
+ WRITE_COEF(0x19, 0x0), WRITE_COEF(0x1a, 0x0), WRITE_COEF(0x1b, 0x0),
+ WRITE_COEF(0x1c, 0x0), WRITE_COEF(0x1d, 0x0), WRITE_COEF(0x1e, 0x7418),
+ WRITE_COEF(0x1f, 0x0804), WRITE_COEF(0x20, 0x4200), WRITE_COEF(0x21, 0x0468),
+ WRITE_COEF(0x22, 0x8ccc), WRITE_COEF(0x23, 0x0250), WRITE_COEF(0x24, 0x7418),
+ WRITE_COEF(0x27, 0x0), WRITE_COEF(0x28, 0x8ccc), WRITE_COEF(0x2a, 0xff00),
+ WRITE_COEF(0x2b, 0x8000), WRITE_COEF(0xa7, 0xff00), WRITE_COEF(0xa8, 0x8000),
+ WRITE_COEF(0xaa, 0x2e17), WRITE_COEF(0xab, 0xa0c0), WRITE_COEF(0xac, 0x0),
+ WRITE_COEF(0xad, 0x0), WRITE_COEF(0xae, 0x2ac6), WRITE_COEF(0xaf, 0xa480),
+ WRITE_COEF(0xb0, 0x0), WRITE_COEF(0xb1, 0x0), WRITE_COEF(0xb2, 0x0),
+ WRITE_COEF(0xb3, 0x0), WRITE_COEF(0xb4, 0x0), WRITE_COEF(0xb5, 0x1040),
+ WRITE_COEF(0xb6, 0xd697), WRITE_COEF(0xb7, 0x902b), WRITE_COEF(0xb8, 0xd697),
+ WRITE_COEF(0xb9, 0x902b), WRITE_COEF(0xba, 0xb8ba), WRITE_COEF(0xbb, 0xaaab),
+ WRITE_COEF(0xbc, 0xaaaf), WRITE_COEF(0xbd, 0x6aaa), WRITE_COEF(0xbe, 0x1c02),
+ WRITE_COEF(0xc0, 0x00ff), WRITE_COEF(0xc1, 0x0fa6),
+ {}
+};
+
+static void alc668_restore_default_value(struct hda_codec *codec)
+{
+ alc_process_coef_fw(codec, alc668_coefs);
+}
+
+static void alc_fixup_headset_mode_alc662(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ spec->gen.hp_mic = 1; /* Mic-in is same pin as headphone */
+
+ /* Disable boost for mic-in permanently. (This code is only called
+ from quirks that guarantee that the headphone is at NID 0x1b.) */
+ snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000);
+ snd_hda_override_wcaps(codec, 0x1b, get_wcaps(codec, 0x1b) & ~AC_WCAP_IN_AMP);
+ } else
+ alc_fixup_headset_mode(codec, fix, action);
+}
+
+static void alc_fixup_headset_mode_alc668(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ alc_write_coef_idx(codec, 0xc4, 0x8000);
+ alc_update_coef_idx(codec, 0xc2, ~0xfe, 0);
+ snd_hda_set_pin_ctl_cache(codec, 0x18, 0);
+ }
+ alc_fixup_headset_mode(codec, fix, action);
+}
+
+enum {
+ ALC662_FIXUP_ASPIRE,
+ ALC662_FIXUP_LED_GPIO1,
+ ALC662_FIXUP_IDEAPAD,
+ ALC272_FIXUP_MARIO,
+ ALC662_FIXUP_CZC_ET26,
+ ALC662_FIXUP_CZC_P10T,
+ ALC662_FIXUP_SKU_IGNORE,
+ ALC662_FIXUP_HP_RP5800,
+ ALC662_FIXUP_ASUS_MODE1,
+ ALC662_FIXUP_ASUS_MODE2,
+ ALC662_FIXUP_ASUS_MODE3,
+ ALC662_FIXUP_ASUS_MODE4,
+ ALC662_FIXUP_ASUS_MODE5,
+ ALC662_FIXUP_ASUS_MODE6,
+ ALC662_FIXUP_ASUS_MODE7,
+ ALC662_FIXUP_ASUS_MODE8,
+ ALC662_FIXUP_NO_JACK_DETECT,
+ ALC662_FIXUP_ZOTAC_Z68,
+ ALC662_FIXUP_INV_DMIC,
+ ALC662_FIXUP_DELL_MIC_NO_PRESENCE,
+ ALC668_FIXUP_DELL_MIC_NO_PRESENCE,
+ ALC662_FIXUP_HEADSET_MODE,
+ ALC668_FIXUP_HEADSET_MODE,
+ ALC662_FIXUP_BASS_MODE4_CHMAP,
+ ALC662_FIXUP_BASS_16,
+ ALC662_FIXUP_BASS_1A,
+ ALC662_FIXUP_BASS_CHMAP,
+ ALC668_FIXUP_AUTO_MUTE,
+ ALC668_FIXUP_DELL_DISABLE_AAMIX,
+ ALC668_FIXUP_DELL_XPS13,
+ ALC662_FIXUP_ASUS_Nx50,
+ ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE,
+ ALC668_FIXUP_ASUS_Nx51,
+ ALC668_FIXUP_MIC_COEF,
+ ALC668_FIXUP_ASUS_G751,
+ ALC891_FIXUP_HEADSET_MODE,
+ ALC891_FIXUP_DELL_MIC_NO_PRESENCE,
+ ALC662_FIXUP_ACER_VERITON,
+ ALC892_FIXUP_ASROCK_MOBO,
+ ALC662_FIXUP_USI_FUNC,
+ ALC662_FIXUP_USI_HEADSET_MODE,
+ ALC662_FIXUP_LENOVO_MULTI_CODECS,
+ ALC669_FIXUP_ACER_ASPIRE_ETHOS,
+ ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET,
+ ALC671_FIXUP_HP_HEADSET_MIC2,
+ ALC662_FIXUP_ACER_X2660G_HEADSET_MODE,
+ ALC662_FIXUP_ACER_NITRO_HEADSET_MODE,
+ ALC668_FIXUP_ASUS_NO_HEADSET_MIC,
+ ALC668_FIXUP_HEADSET_MIC,
+ ALC668_FIXUP_MIC_DET_COEF,
+ ALC897_FIXUP_LENOVO_HEADSET_MIC,
+ ALC897_FIXUP_HEADSET_MIC_PIN,
+ ALC897_FIXUP_HP_HSMIC_VERB,
+ ALC897_FIXUP_LENOVO_HEADSET_MODE,
+ ALC897_FIXUP_HEADSET_MIC_PIN2,
+ ALC897_FIXUP_UNIS_H3C_X500S,
+ ALC897_FIXUP_HEADSET_MIC_PIN3,
+};
+
+static const struct hda_fixup alc662_fixups[] = {
+ [ALC662_FIXUP_ASPIRE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x99130112 }, /* subwoofer */
+ { }
+ }
+ },
+ [ALC662_FIXUP_LED_GPIO1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc662_fixup_led_gpio1,
+ },
+ [ALC662_FIXUP_IDEAPAD] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x99130112 }, /* subwoofer */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_LED_GPIO1,
+ },
+ [ALC272_FIXUP_MARIO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc272_fixup_mario,
+ },
+ [ALC662_FIXUP_CZC_ET26] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x12, 0x403cc000},
+ {0x14, 0x90170110}, /* speaker */
+ {0x15, 0x411111f0},
+ {0x16, 0x411111f0},
+ {0x18, 0x01a19030}, /* mic */
+ {0x19, 0x90a7013f}, /* int-mic */
+ {0x1a, 0x01014020},
+ {0x1b, 0x0121401f},
+ {0x1c, 0x411111f0},
+ {0x1d, 0x411111f0},
+ {0x1e, 0x40478e35},
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_CZC_P10T] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0},
+ {}
+ }
+ },
+ [ALC662_FIXUP_SKU_IGNORE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_sku_ignore,
+ },
+ [ALC662_FIXUP_HP_RP5800] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x0221201f }, /* HP out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE1] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x18, 0x01a19c20 }, /* mic */
+ { 0x19, 0x99a3092f }, /* int-mic */
+ { 0x21, 0x0121401f }, /* HP out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x18, 0x01a19820 }, /* mic */
+ { 0x19, 0x99a3092f }, /* int-mic */
+ { 0x1b, 0x0121401f }, /* HP out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE3] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0121441f }, /* HP */
+ { 0x18, 0x01a19840 }, /* mic */
+ { 0x19, 0x99a3094f }, /* int-mic */
+ { 0x21, 0x01211420 }, /* HP2 */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE4] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x16, 0x99130111 }, /* speaker */
+ { 0x18, 0x01a19840 }, /* mic */
+ { 0x19, 0x99a3094f }, /* int-mic */
+ { 0x21, 0x0121441f }, /* HP */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE5] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0121441f }, /* HP */
+ { 0x16, 0x99130111 }, /* speaker */
+ { 0x18, 0x01a19840 }, /* mic */
+ { 0x19, 0x99a3094f }, /* int-mic */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE6] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x01211420 }, /* HP2 */
+ { 0x18, 0x01a19840 }, /* mic */
+ { 0x19, 0x99a3094f }, /* int-mic */
+ { 0x1b, 0x0121441f }, /* HP */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE7] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x17, 0x99130111 }, /* speaker */
+ { 0x18, 0x01a19840 }, /* mic */
+ { 0x19, 0x99a3094f }, /* int-mic */
+ { 0x1b, 0x01214020 }, /* HP */
+ { 0x21, 0x0121401f }, /* HP */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_ASUS_MODE8] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x12, 0x99a30970 }, /* int-mic */
+ { 0x15, 0x01214020 }, /* HP */
+ { 0x17, 0x99130111 }, /* speaker */
+ { 0x18, 0x01a19840 }, /* mic */
+ { 0x21, 0x0121401f }, /* HP */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_SKU_IGNORE
+ },
+ [ALC662_FIXUP_NO_JACK_DETECT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_no_jack_detect,
+ },
+ [ALC662_FIXUP_ZOTAC_Z68] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x02214020 }, /* Front HP */
+ { }
+ }
+ },
+ [ALC662_FIXUP_INV_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic,
+ },
+ [ALC668_FIXUP_DELL_XPS13] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_dell_xps13,
+ .chained = true,
+ .chain_id = ALC668_FIXUP_DELL_DISABLE_AAMIX
+ },
+ [ALC668_FIXUP_DELL_DISABLE_AAMIX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE
+ },
+ [ALC668_FIXUP_AUTO_MUTE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_auto_mute_via_amp,
+ .chained = true,
+ .chain_id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE
+ },
+ [ALC662_FIXUP_DELL_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a1113c }, /* use as headset mic, without its own jack detect */
+ /* headphone mic by setting pin control of 0x1b (headphone out) to in + vref_50 */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_HEADSET_MODE
+ },
+ [ALC662_FIXUP_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_alc662,
+ },
+ [ALC668_FIXUP_DELL_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+ { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC668_FIXUP_HEADSET_MODE
+ },
+ [ALC668_FIXUP_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_alc668,
+ },
+ [ALC662_FIXUP_BASS_MODE4_CHMAP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_bass_chmap,
+ .chained = true,
+ .chain_id = ALC662_FIXUP_ASUS_MODE4
+ },
+ [ALC662_FIXUP_BASS_16] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x16, 0x80106111}, /* bass speaker */
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_BASS_CHMAP,
+ },
+ [ALC662_FIXUP_BASS_1A] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x1a, 0x80106111}, /* bass speaker */
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_BASS_CHMAP,
+ },
+ [ALC662_FIXUP_BASS_CHMAP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_bass_chmap,
+ },
+ [ALC662_FIXUP_ASUS_Nx50] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_auto_mute_via_amp,
+ .chained = true,
+ .chain_id = ALC662_FIXUP_BASS_1A
+ },
+ [ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_alc668,
+ .chain_id = ALC662_FIXUP_BASS_CHMAP
+ },
+ [ALC668_FIXUP_ASUS_Nx51] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+ { 0x1a, 0x90170151 }, /* bass speaker */
+ { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE,
+ },
+ [ALC668_FIXUP_MIC_COEF] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0xc3 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x4000 },
+ {}
+ },
+ },
+ [ALC668_FIXUP_ASUS_G751] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x0421101f }, /* HP */
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC668_FIXUP_MIC_COEF
+ },
+ [ALC891_FIXUP_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode,
+ },
+ [ALC891_FIXUP_DELL_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+ { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC891_FIXUP_HEADSET_MODE
+ },
+ [ALC662_FIXUP_ACER_VERITON] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x50170120 }, /* no internal speaker */
+ { }
+ }
+ },
+ [ALC892_FIXUP_ASROCK_MOBO] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x40f000f0 }, /* disabled */
+ { 0x16, 0x40f000f0 }, /* disabled */
+ { }
+ }
+ },
+ [ALC662_FIXUP_USI_FUNC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc662_fixup_usi_headset_mic,
+ },
+ [ALC662_FIXUP_USI_HEADSET_MODE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x02a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x18, 0x01a1903d },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_USI_FUNC
+ },
+ [ALC662_FIXUP_LENOVO_MULTI_CODECS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc233_alc662_fixup_lenovo_dual_codecs,
+ },
+ [ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc662_fixup_aspire_ethos_hp,
+ },
+ [ALC669_FIXUP_ACER_ASPIRE_ETHOS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x92130110 }, /* front speakers */
+ { 0x18, 0x99130111 }, /* center/subwoofer */
+ { 0x1b, 0x11130012 }, /* surround plus jack for HP */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC669_FIXUP_ACER_ASPIRE_ETHOS_HEADSET
+ },
+ [ALC671_FIXUP_HP_HEADSET_MIC2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc671_fixup_hp_headset_mic2,
+ },
+ [ALC662_FIXUP_ACER_X2660G_HEADSET_MODE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x02a1113c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_USI_FUNC
+ },
+ [ALC662_FIXUP_ACER_NITRO_HEADSET_MODE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x01a11140 }, /* use as headset mic, without its own jack detect */
+ { 0x1b, 0x0221144f },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_USI_FUNC
+ },
+ [ALC668_FIXUP_ASUS_NO_HEADSET_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x04a1112c },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC668_FIXUP_HEADSET_MIC
+ },
+ [ALC668_FIXUP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mic,
+ .chained = true,
+ .chain_id = ALC668_FIXUP_MIC_DET_COEF
+ },
+ [ALC668_FIXUP_MIC_DET_COEF] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x15 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0d60 },
+ {}
+ },
+ },
+ [ALC897_FIXUP_LENOVO_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc897_fixup_lenovo_headset_mic,
+ },
+ [ALC897_FIXUP_HEADSET_MIC_PIN] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x03a11050 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC897_FIXUP_LENOVO_HEADSET_MIC
+ },
+ [ALC897_FIXUP_HP_HSMIC_VERB] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ },
+ [ALC897_FIXUP_LENOVO_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc897_fixup_lenovo_headset_mode,
+ },
+ [ALC897_FIXUP_HEADSET_MIC_PIN2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x01a11140 }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC897_FIXUP_LENOVO_HEADSET_MODE
+ },
+ [ALC897_FIXUP_UNIS_H3C_X500S] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x14, AC_VERB_SET_EAPD_BTLENABLE, 0 },
+ {}
+ },
+ },
+ [ALC897_FIXUP_HEADSET_MIC_PIN3] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a11050 }, /* use as headset mic */
+ { }
+ },
+ },
+};
+
+static const struct hda_quirk alc662_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1019, 0x9859, "JP-IK LEAP W502", ALC897_FIXUP_HEADSET_MIC_PIN3),
+ SND_PCI_QUIRK(0x1025, 0x022f, "Acer Aspire One", ALC662_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x0241, "Packard Bell DOTS", ALC662_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE),
+ SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE),
+ SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x034a, "Gateway LT27", ALC662_FIXUP_INV_DMIC),
+ SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
+ SND_PCI_QUIRK(0x1025, 0x0566, "Acer Aspire Ethos 8951G", ALC669_FIXUP_ACER_ASPIRE_ETHOS),
+ SND_PCI_QUIRK(0x1025, 0x123c, "Acer Nitro N50-600", ALC662_FIXUP_ACER_NITRO_HEADSET_MODE),
+ SND_PCI_QUIRK(0x1025, 0x124e, "Acer 2660G", ALC662_FIXUP_ACER_X2660G_HEADSET_MODE),
+ SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x05fe, "Dell XPS 15", ALC668_FIXUP_DELL_XPS13),
+ SND_PCI_QUIRK(0x1028, 0x060a, "Dell XPS 13", ALC668_FIXUP_DELL_XPS13),
+ SND_PCI_QUIRK(0x1028, 0x060d, "Dell M3800", ALC668_FIXUP_DELL_XPS13),
+ SND_PCI_QUIRK(0x1028, 0x0625, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0626, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0696, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0698, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
+ SND_PCI_QUIRK(0x103c, 0x870c, "HP", ALC897_FIXUP_HP_HSMIC_VERB),
+ SND_PCI_QUIRK(0x103c, 0x8719, "HP", ALC897_FIXUP_HP_HSMIC_VERB),
+ SND_PCI_QUIRK(0x103c, 0x872b, "HP", ALC897_FIXUP_HP_HSMIC_VERB),
+ SND_PCI_QUIRK(0x103c, 0x873e, "HP", ALC671_FIXUP_HP_HEADSET_MIC2),
+ SND_PCI_QUIRK(0x103c, 0x8768, "HP Slim Desktop S01", ALC671_FIXUP_HP_HEADSET_MIC2),
+ SND_PCI_QUIRK(0x103c, 0x877e, "HP 288 Pro G6", ALC671_FIXUP_HP_HEADSET_MIC2),
+ SND_PCI_QUIRK(0x103c, 0x885f, "HP 288 Pro G8", ALC671_FIXUP_HP_HEADSET_MIC2),
+ SND_PCI_QUIRK(0x1043, 0x1080, "Asus UX501VW", ALC668_FIXUP_HEADSET_MODE),
+ SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_ASUS_Nx50),
+ SND_PCI_QUIRK(0x1043, 0x129d, "Asus N750", ALC662_FIXUP_ASUS_Nx50),
+ SND_PCI_QUIRK(0x1043, 0x12ff, "ASUS G751", ALC668_FIXUP_ASUS_G751),
+ SND_PCI_QUIRK(0x1043, 0x13df, "Asus N550JX", ALC662_FIXUP_BASS_1A),
+ SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP),
+ SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16),
+ SND_PCI_QUIRK(0x1043, 0x177d, "ASUS N551", ALC668_FIXUP_ASUS_Nx51),
+ SND_PCI_QUIRK(0x1043, 0x17bd, "ASUS N751", ALC668_FIXUP_ASUS_Nx51),
+ SND_PCI_QUIRK(0x1043, 0x185d, "ASUS G551JW", ALC668_FIXUP_ASUS_NO_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71SL", ALC662_FIXUP_ASUS_MODE8),
+ SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16),
+ SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_MODE4_CHMAP),
+ SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),
+ SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD),
+ SND_PCI_QUIRK(0x14cd, 0x5003, "USI", ALC662_FIXUP_USI_HEADSET_MODE),
+ SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC662_FIXUP_LENOVO_MULTI_CODECS),
+ SND_PCI_QUIRK(0x17aa, 0x1057, "Lenovo P360", ALC897_FIXUP_HEADSET_MIC_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x1064, "Lenovo P3 Tower", ALC897_FIXUP_HEADSET_MIC_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x32ca, "Lenovo ThinkCentre M80", ALC897_FIXUP_HEADSET_MIC_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x32cb, "Lenovo ThinkCentre M70", ALC897_FIXUP_HEADSET_MIC_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x32cf, "Lenovo ThinkCentre M950", ALC897_FIXUP_HEADSET_MIC_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x32f7, "Lenovo ThinkCentre M90", ALC897_FIXUP_HEADSET_MIC_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x3321, "Lenovo ThinkCentre M70 Gen4", ALC897_FIXUP_HEADSET_MIC_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x331b, "Lenovo ThinkCentre M90 Gen4", ALC897_FIXUP_HEADSET_MIC_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x3364, "Lenovo ThinkCentre M90 Gen5", ALC897_FIXUP_HEADSET_MIC_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x3742, "Lenovo TianYi510Pro-14IOB", ALC897_FIXUP_HEADSET_MIC_PIN2),
+ SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD),
+ SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD),
+ SND_PCI_QUIRK(0x1849, 0x5892, "ASRock B150M", ALC892_FIXUP_ASROCK_MOBO),
+ SND_PCI_QUIRK(0x19da, 0xa130, "Zotac Z68", ALC662_FIXUP_ZOTAC_Z68),
+ SND_PCI_QUIRK(0x1b0a, 0x01b8, "ACER Veriton", ALC662_FIXUP_ACER_VERITON),
+ SND_PCI_QUIRK(0x1b35, 0x1234, "CZC ET26", ALC662_FIXUP_CZC_ET26),
+ SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T),
+ SND_PCI_QUIRK(0x1c6c, 0x1239, "Compaq N14JP6-V2", ALC897_FIXUP_HP_HSMIC_VERB),
+
+#if 0
+ /* Below is a quirk table taken from the old code.
+ * Basically the device should work as is without the fixup table.
+ * If BIOS doesn't give a proper info, enable the corresponding
+ * fixup entry.
+ */
+ SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x1173, "ASUS K73Jn", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1303, "ASUS G60J", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1333, "ASUS G60Jx", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x13e3, "ASUS N71JA", ALC662_FIXUP_ASUS_MODE7),
+ SND_PCI_QUIRK(0x1043, 0x1463, "ASUS N71", ALC662_FIXUP_ASUS_MODE7),
+ SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G72", ALC662_FIXUP_ASUS_MODE8),
+ SND_PCI_QUIRK(0x1043, 0x1563, "ASUS N90", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x15d3, "ASUS N50SF F50SF", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS K40C K50C", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1733, "ASUS N81De", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC662_FIXUP_ASUS_MODE6),
+ SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC662_FIXUP_ASUS_MODE6),
+ SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1793, "ASUS F50GX", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC662_FIXUP_ASUS_MODE5),
+ SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC662_FIXUP_ASUS_MODE6),
+ SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1853, "ASUS F50Z", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x18c3, "ASUS VX5", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x1943, "ASUS Vx3V", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC662_FIXUP_ASUS_MODE3),
+ SND_PCI_QUIRK(0x1043, 0x1983, "ASUS N5051A", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC662_FIXUP_ASUS_MODE1),
+ SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC662_FIXUP_ASUS_MODE4),
+#endif
+ {}
+};
+
+static const struct hda_model_fixup alc662_fixup_models[] = {
+ {.id = ALC662_FIXUP_ASPIRE, .name = "aspire"},
+ {.id = ALC662_FIXUP_IDEAPAD, .name = "ideapad"},
+ {.id = ALC272_FIXUP_MARIO, .name = "mario"},
+ {.id = ALC662_FIXUP_HP_RP5800, .name = "hp-rp5800"},
+ {.id = ALC662_FIXUP_ASUS_MODE1, .name = "asus-mode1"},
+ {.id = ALC662_FIXUP_ASUS_MODE2, .name = "asus-mode2"},
+ {.id = ALC662_FIXUP_ASUS_MODE3, .name = "asus-mode3"},
+ {.id = ALC662_FIXUP_ASUS_MODE4, .name = "asus-mode4"},
+ {.id = ALC662_FIXUP_ASUS_MODE5, .name = "asus-mode5"},
+ {.id = ALC662_FIXUP_ASUS_MODE6, .name = "asus-mode6"},
+ {.id = ALC662_FIXUP_ASUS_MODE7, .name = "asus-mode7"},
+ {.id = ALC662_FIXUP_ASUS_MODE8, .name = "asus-mode8"},
+ {.id = ALC662_FIXUP_ZOTAC_Z68, .name = "zotac-z68"},
+ {.id = ALC662_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {.id = ALC662_FIXUP_DELL_MIC_NO_PRESENCE, .name = "alc662-headset-multi"},
+ {.id = ALC668_FIXUP_DELL_MIC_NO_PRESENCE, .name = "dell-headset-multi"},
+ {.id = ALC662_FIXUP_HEADSET_MODE, .name = "alc662-headset"},
+ {.id = ALC668_FIXUP_HEADSET_MODE, .name = "alc668-headset"},
+ {.id = ALC662_FIXUP_BASS_16, .name = "bass16"},
+ {.id = ALC662_FIXUP_BASS_1A, .name = "bass1a"},
+ {.id = ALC668_FIXUP_AUTO_MUTE, .name = "automute"},
+ {.id = ALC668_FIXUP_DELL_XPS13, .name = "dell-xps13"},
+ {.id = ALC662_FIXUP_ASUS_Nx50, .name = "asus-nx50"},
+ {.id = ALC668_FIXUP_ASUS_Nx51, .name = "asus-nx51"},
+ {.id = ALC668_FIXUP_ASUS_G751, .name = "asus-g751"},
+ {.id = ALC891_FIXUP_HEADSET_MODE, .name = "alc891-headset"},
+ {.id = ALC891_FIXUP_DELL_MIC_NO_PRESENCE, .name = "alc891-headset-multi"},
+ {.id = ALC662_FIXUP_ACER_VERITON, .name = "acer-veriton"},
+ {.id = ALC892_FIXUP_ASROCK_MOBO, .name = "asrock-mobo"},
+ {.id = ALC662_FIXUP_USI_HEADSET_MODE, .name = "usi-headset"},
+ {.id = ALC662_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"},
+ {.id = ALC669_FIXUP_ACER_ASPIRE_ETHOS, .name = "aspire-ethos"},
+ {.id = ALC897_FIXUP_UNIS_H3C_X500S, .name = "unis-h3c-x500s"},
+ {}
+};
+
+static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = {
+ SND_HDA_PIN_QUIRK(0x10ec0867, 0x1028, "Dell", ALC891_FIXUP_DELL_MIC_NO_PRESENCE,
+ {0x17, 0x02211010},
+ {0x18, 0x01a19030},
+ {0x1a, 0x01813040},
+ {0x21, 0x01014020}),
+ SND_HDA_PIN_QUIRK(0x10ec0867, 0x1028, "Dell", ALC891_FIXUP_DELL_MIC_NO_PRESENCE,
+ {0x16, 0x01813030},
+ {0x17, 0x02211010},
+ {0x18, 0x01a19040},
+ {0x21, 0x01014020}),
+ SND_HDA_PIN_QUIRK(0x10ec0662, 0x1028, "Dell", ALC662_FIXUP_DELL_MIC_NO_PRESENCE,
+ {0x14, 0x01014010},
+ {0x18, 0x01a19020},
+ {0x1a, 0x0181302f},
+ {0x1b, 0x0221401f}),
+ SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE,
+ {0x12, 0x99a30130},
+ {0x14, 0x90170110},
+ {0x15, 0x0321101f},
+ {0x16, 0x03011020}),
+ SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE,
+ {0x12, 0x99a30140},
+ {0x14, 0x90170110},
+ {0x15, 0x0321101f},
+ {0x16, 0x03011020}),
+ SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE,
+ {0x12, 0x99a30150},
+ {0x14, 0x90170110},
+ {0x15, 0x0321101f},
+ {0x16, 0x03011020}),
+ SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell", ALC668_FIXUP_AUTO_MUTE,
+ {0x14, 0x90170110},
+ {0x15, 0x0321101f},
+ {0x16, 0x03011020}),
+ SND_HDA_PIN_QUIRK(0x10ec0668, 0x1028, "Dell XPS 15", ALC668_FIXUP_AUTO_MUTE,
+ {0x12, 0x90a60130},
+ {0x14, 0x90170110},
+ {0x15, 0x0321101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2,
+ {0x14, 0x01014010},
+ {0x17, 0x90170150},
+ {0x19, 0x02a11060},
+ {0x1b, 0x01813030},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2,
+ {0x14, 0x01014010},
+ {0x18, 0x01a19040},
+ {0x1b, 0x01813030},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0671, 0x103c, "HP cPC", ALC671_FIXUP_HP_HEADSET_MIC2,
+ {0x14, 0x01014020},
+ {0x17, 0x90170110},
+ {0x18, 0x01a19050},
+ {0x1b, 0x01813040},
+ {0x21, 0x02211030}),
+ {}
+};
+
+/*
+ */
+static int alc662_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct alc_spec *spec;
+ int err;
+
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
+
+ spec->shutup = alc_eapd_shutup;
+
+ /* handle multiple HPs as is */
+ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
+
+ alc_fix_pll_init(codec, 0x20, 0x04, 15);
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0668:
+ spec->init_hook = alc668_restore_default_value;
+ break;
+ }
+
+ alc_pre_init(codec);
+
+ snd_hda_pick_fixup(codec, alc662_fixup_models,
+ alc662_fixup_tbl, alc662_fixups);
+ snd_hda_pick_pin_fixup(codec, alc662_pin_fixup_tbl, alc662_fixups, true);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ alc_auto_parse_customize_define(codec);
+
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
+
+ if ((alc_get_coef0(codec) & (1 << 14)) &&
+ codec->bus->pci && codec->bus->pci->subsystem_vendor == 0x1025 &&
+ spec->cdefine.platform_type == 1) {
+ err = alc_codec_rename(codec, "ALC272X");
+ if (err < 0)
+ goto error;
+ }
+
+ /* automatic parse from the BIOS config */
+ err = alc662_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ if (!spec->gen.no_analog && spec->gen.beep_nid) {
+ switch (codec->core.vendor_id) {
+ case 0x10ec0662:
+ err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ break;
+ case 0x10ec0272:
+ case 0x10ec0663:
+ case 0x10ec0665:
+ case 0x10ec0668:
+ err = set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
+ break;
+ case 0x10ec0273:
+ err = set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT);
+ break;
+ }
+ if (err < 0)
+ goto error;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops alc662_codec_ops = {
+ .probe = alc662_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc662[] = {
+ HDA_CODEC_ID(0x10ec0272, "ALC272"),
+ HDA_CODEC_ID_REV(0x10ec0662, 0x100101, "ALC662 rev1"),
+ HDA_CODEC_ID_REV(0x10ec0662, 0x100300, "ALC662 rev3"),
+ HDA_CODEC_ID(0x10ec0663, "ALC663"),
+ HDA_CODEC_ID(0x10ec0665, "ALC665"),
+ HDA_CODEC_ID(0x10ec0667, "ALC667"),
+ HDA_CODEC_ID(0x10ec0668, "ALC668"),
+ HDA_CODEC_ID(0x10ec0670, "ALC670"),
+ HDA_CODEC_ID(0x10ec0671, "ALC671"),
+ HDA_CODEC_ID(0x10ec0867, "ALC891"),
+ HDA_CODEC_ID(0x10ec0892, "ALC892"),
+ HDA_CODEC_ID(0x10ec0897, "ALC897"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc662);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC662 and compatible HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+
+static struct hda_codec_driver alc662_driver = {
+ .id = snd_hda_id_alc662,
+ .ops = &alc662_codec_ops,
+};
+
+module_hda_codec_driver(alc662_driver);
diff --git a/sound/hda/codecs/realtek/alc680.c b/sound/hda/codecs/realtek/alc680.c
new file mode 100644
index 000000000000..8aab1026243c
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc680.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek ALC680 codec
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+static int alc680_parse_auto_config(struct hda_codec *codec)
+{
+ return alc_parse_auto_config(codec, NULL, NULL);
+}
+
+/*
+ */
+static int alc680_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ int err;
+
+ /* ALC680 has no aa-loopback mixer */
+ err = alc_alloc_spec(codec, 0);
+ if (err < 0)
+ return err;
+
+ /* automatic parse from the BIOS config */
+ err = alc680_parse_auto_config(codec);
+ if (err < 0) {
+ snd_hda_gen_remove(codec);
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct hda_codec_ops alc680_codec_ops = {
+ .probe = alc680_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc680[] = {
+ HDA_CODEC_ID(0x10ec0680, "ALC680"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc680);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC680 HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+
+static struct hda_codec_driver alc680_driver = {
+ .id = snd_hda_id_alc680,
+ .ops = &alc680_codec_ops,
+};
+
+module_hda_codec_driver(alc680_driver);
diff --git a/sound/hda/codecs/realtek/alc861.c b/sound/hda/codecs/realtek/alc861.c
new file mode 100644
index 000000000000..270037c6504a
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc861.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek ALC861 codec
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+static int alc861_parse_auto_config(struct hda_codec *codec)
+{
+ static const hda_nid_t alc861_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc861_ssids[] = { 0x0e, 0x0f, 0x0b, 0 };
+ return alc_parse_auto_config(codec, alc861_ignore, alc861_ssids);
+}
+
+/* Pin config fixes */
+enum {
+ ALC861_FIXUP_FSC_AMILO_PI1505,
+ ALC861_FIXUP_AMP_VREF_0F,
+ ALC861_FIXUP_NO_JACK_DETECT,
+ ALC861_FIXUP_ASUS_A6RP,
+ ALC660_FIXUP_ASUS_W7J,
+};
+
+/* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */
+static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int val;
+
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
+ val = snd_hda_codec_get_pin_target(codec, 0x0f);
+ if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN)))
+ val |= AC_PINCTL_IN_EN;
+ val |= AC_PINCTL_VREF_50;
+ snd_hda_set_pin_ctl(codec, 0x0f, val);
+ spec->gen.keep_vref_in_automute = 1;
+}
+
+static const struct hda_fixup alc861_fixups[] = {
+ [ALC861_FIXUP_FSC_AMILO_PI1505] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x0b, 0x0221101f }, /* HP */
+ { 0x0f, 0x90170310 }, /* speaker */
+ { }
+ }
+ },
+ [ALC861_FIXUP_AMP_VREF_0F] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc861_fixup_asus_amp_vref_0f,
+ },
+ [ALC861_FIXUP_NO_JACK_DETECT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_no_jack_detect,
+ },
+ [ALC861_FIXUP_ASUS_A6RP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc861_fixup_asus_amp_vref_0f,
+ .chained = true,
+ .chain_id = ALC861_FIXUP_NO_JACK_DETECT,
+ },
+ [ALC660_FIXUP_ASUS_W7J] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* ASUS W7J needs a magic pin setup on unused NID 0x10
+ * for enabling outputs
+ */
+ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ { }
+ },
+ }
+};
+
+static const struct hda_quirk alc861_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1043, 0x1253, "ASUS W7J", ALC660_FIXUP_ASUS_W7J),
+ SND_PCI_QUIRK(0x1043, 0x1263, "ASUS Z35HL", ALC660_FIXUP_ASUS_W7J),
+ SND_PCI_QUIRK(0x1043, 0x1393, "ASUS A6Rp", ALC861_FIXUP_ASUS_A6RP),
+ SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", ALC861_FIXUP_AMP_VREF_0F),
+ SND_PCI_QUIRK(0x1462, 0x7254, "HP DX2200", ALC861_FIXUP_NO_JACK_DETECT),
+ SND_PCI_QUIRK_VENDOR(0x1584, "Haier/Uniwill", ALC861_FIXUP_AMP_VREF_0F),
+ SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", ALC861_FIXUP_FSC_AMILO_PI1505),
+ {}
+};
+
+/*
+ */
+static int alc861_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct alc_spec *spec;
+ int err;
+
+ err = alc_alloc_spec(codec, 0x15);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x23;
+
+ spec->power_hook = alc_power_eapd;
+
+ alc_pre_init(codec);
+
+ snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ /* automatic parse from the BIOS config */
+ err = alc861_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ if (!spec->gen.no_analog) {
+ err = set_beep_amp(spec, 0x23, 0, HDA_OUTPUT);
+ if (err < 0)
+ goto error;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops alc861_codec_ops = {
+ .probe = alc861_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc861[] = {
+ HDA_CODEC_ID_REV(0x10ec0861, 0x100340, "ALC660"),
+ HDA_CODEC_ID(0x10ec0861, "ALC861"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc861);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC861 HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+
+static struct hda_codec_driver alc861_driver = {
+ .id = snd_hda_id_alc861,
+ .ops = &alc861_codec_ops,
+};
+
+module_hda_codec_driver(alc861_driver);
diff --git a/sound/hda/codecs/realtek/alc861vd.c b/sound/hda/codecs/realtek/alc861vd.c
new file mode 100644
index 000000000000..44264e0d6e56
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc861vd.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek ALC861-VD codec
+// Based on ALC882
+// In addition, an independent DAC
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+static int alc861vd_parse_auto_config(struct hda_codec *codec)
+{
+ static const hda_nid_t alc861vd_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc861vd_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ return alc_parse_auto_config(codec, alc861vd_ignore, alc861vd_ssids);
+}
+
+enum {
+ ALC660VD_FIX_ASUS_GPIO1,
+ ALC861VD_FIX_DALLAS,
+};
+
+/* exclude VREF80 */
+static void alc861vd_fixup_dallas(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ snd_hda_override_pin_caps(codec, 0x18, 0x00000734);
+ snd_hda_override_pin_caps(codec, 0x19, 0x0000073c);
+ }
+}
+
+/* reset GPIO1 */
+static void alc660vd_fixup_asus_gpio1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->gpio_mask |= 0x02;
+ alc_fixup_gpio(codec, action, 0x01);
+}
+
+static const struct hda_fixup alc861vd_fixups[] = {
+ [ALC660VD_FIX_ASUS_GPIO1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc660vd_fixup_asus_gpio1,
+ },
+ [ALC861VD_FIX_DALLAS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc861vd_fixup_dallas,
+ },
+};
+
+static const struct hda_quirk alc861vd_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_FIX_DALLAS),
+ SND_PCI_QUIRK(0x1043, 0x1339, "ASUS A7-K", ALC660VD_FIX_ASUS_GPIO1),
+ SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_FIX_DALLAS),
+ {}
+};
+
+/*
+ */
+static int alc861vd_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct alc_spec *spec;
+ int err;
+
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x23;
+
+ spec->shutup = alc_eapd_shutup;
+
+ alc_pre_init(codec);
+
+ snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ /* automatic parse from the BIOS config */
+ err = alc861vd_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ if (!spec->gen.no_analog) {
+ err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ if (err < 0)
+ goto error;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops alc861vd_codec_ops = {
+ .probe = alc861vd_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc861vd[] = {
+ HDA_CODEC_ID(0x10ec0660, "ALC660-VD"),
+ HDA_CODEC_ID(0x10ec0862, "ALC861-VD"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc861vd);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC861-VD HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+
+static struct hda_codec_driver alc861vd_driver = {
+ .id = snd_hda_id_alc861vd,
+ .ops = &alc861vd_codec_ops,
+};
+
+module_hda_codec_driver(alc861vd_driver);
diff --git a/sound/hda/codecs/realtek/alc880.c b/sound/hda/codecs/realtek/alc880.c
new file mode 100644
index 000000000000..bf1bdf11ec2d
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc880.c
@@ -0,0 +1,509 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek ALC880 codec
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+static void alc880_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ /* For some reason, the res given from ALC880 is broken.
+ Here we adjust it properly. */
+ snd_hda_jack_unsol_event(codec, res >> 2);
+}
+
+static int alc880_parse_auto_config(struct hda_codec *codec)
+{
+ static const hda_nid_t alc880_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc880_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ return alc_parse_auto_config(codec, alc880_ignore, alc880_ssids);
+}
+
+/*
+ * ALC880 fix-ups
+ */
+enum {
+ ALC880_FIXUP_GPIO1,
+ ALC880_FIXUP_GPIO2,
+ ALC880_FIXUP_MEDION_RIM,
+ ALC880_FIXUP_LG,
+ ALC880_FIXUP_LG_LW25,
+ ALC880_FIXUP_W810,
+ ALC880_FIXUP_EAPD_COEF,
+ ALC880_FIXUP_TCL_S700,
+ ALC880_FIXUP_VOL_KNOB,
+ ALC880_FIXUP_FUJITSU,
+ ALC880_FIXUP_F1734,
+ ALC880_FIXUP_UNIWILL,
+ ALC880_FIXUP_UNIWILL_DIG,
+ ALC880_FIXUP_Z71V,
+ ALC880_FIXUP_ASUS_W5A,
+ ALC880_FIXUP_3ST_BASE,
+ ALC880_FIXUP_3ST,
+ ALC880_FIXUP_3ST_DIG,
+ ALC880_FIXUP_5ST_BASE,
+ ALC880_FIXUP_5ST,
+ ALC880_FIXUP_5ST_DIG,
+ ALC880_FIXUP_6ST_BASE,
+ ALC880_FIXUP_6ST,
+ ALC880_FIXUP_6ST_DIG,
+ ALC880_FIXUP_6ST_AUTOMUTE,
+};
+
+/* enable the volume-knob widget support on NID 0x21 */
+static void alc880_fixup_vol_knob(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PROBE)
+ snd_hda_jack_detect_enable_callback(codec, 0x21,
+ alc_update_knob_master);
+}
+
+static const struct hda_fixup alc880_fixups[] = {
+ [ALC880_FIXUP_GPIO1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_gpio1,
+ },
+ [ALC880_FIXUP_GPIO2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_gpio2,
+ },
+ [ALC880_FIXUP_MEDION_RIM] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_GPIO2,
+ },
+ [ALC880_FIXUP_LG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* disable bogus unused pins */
+ { 0x16, 0x411111f0 },
+ { 0x18, 0x411111f0 },
+ { 0x1a, 0x411111f0 },
+ { }
+ }
+ },
+ [ALC880_FIXUP_LG_LW25] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1a, 0x0181344f }, /* line-in */
+ { 0x1b, 0x0321403f }, /* headphone */
+ { }
+ }
+ },
+ [ALC880_FIXUP_W810] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* disable bogus unused pins */
+ { 0x17, 0x411111f0 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_GPIO2,
+ },
+ [ALC880_FIXUP_EAPD_COEF] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* change to EAPD mode */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 },
+ {}
+ },
+ },
+ [ALC880_FIXUP_TCL_S700] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* change to EAPD mode */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_GPIO2,
+ },
+ [ALC880_FIXUP_VOL_KNOB] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc880_fixup_vol_knob,
+ },
+ [ALC880_FIXUP_FUJITSU] = {
+ /* override all pins as BIOS on old Amilo is broken */
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x0121401f }, /* HP */
+ { 0x15, 0x99030120 }, /* speaker */
+ { 0x16, 0x99030130 }, /* bass speaker */
+ { 0x17, 0x411111f0 }, /* N/A */
+ { 0x18, 0x411111f0 }, /* N/A */
+ { 0x19, 0x01a19950 }, /* mic-in */
+ { 0x1a, 0x411111f0 }, /* N/A */
+ { 0x1b, 0x411111f0 }, /* N/A */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ { 0x1e, 0x01454140 }, /* SPDIF out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_VOL_KNOB,
+ },
+ [ALC880_FIXUP_F1734] = {
+ /* almost compatible with FUJITSU, but no bass and SPDIF */
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x0121401f }, /* HP */
+ { 0x15, 0x99030120 }, /* speaker */
+ { 0x16, 0x411111f0 }, /* N/A */
+ { 0x17, 0x411111f0 }, /* N/A */
+ { 0x18, 0x411111f0 }, /* N/A */
+ { 0x19, 0x01a19950 }, /* mic-in */
+ { 0x1a, 0x411111f0 }, /* N/A */
+ { 0x1b, 0x411111f0 }, /* N/A */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_VOL_KNOB,
+ },
+ [ALC880_FIXUP_UNIWILL] = {
+ /* need to fix HP and speaker pins to be parsed correctly */
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x0121411f }, /* HP */
+ { 0x15, 0x99030120 }, /* speaker */
+ { 0x16, 0x99030130 }, /* bass speaker */
+ { }
+ },
+ },
+ [ALC880_FIXUP_UNIWILL_DIG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* disable bogus unused pins */
+ { 0x17, 0x411111f0 },
+ { 0x19, 0x411111f0 },
+ { 0x1b, 0x411111f0 },
+ { 0x1f, 0x411111f0 },
+ { }
+ }
+ },
+ [ALC880_FIXUP_Z71V] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* set up the whole pins as BIOS is utterly broken */
+ { 0x14, 0x99030120 }, /* speaker */
+ { 0x15, 0x0121411f }, /* HP */
+ { 0x16, 0x411111f0 }, /* N/A */
+ { 0x17, 0x411111f0 }, /* N/A */
+ { 0x18, 0x01a19950 }, /* mic-in */
+ { 0x19, 0x411111f0 }, /* N/A */
+ { 0x1a, 0x01813031 }, /* line-in */
+ { 0x1b, 0x411111f0 }, /* N/A */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ { 0x1e, 0x0144111e }, /* SPDIF */
+ { }
+ }
+ },
+ [ALC880_FIXUP_ASUS_W5A] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* set up the whole pins as BIOS is utterly broken */
+ { 0x14, 0x0121411f }, /* HP */
+ { 0x15, 0x411111f0 }, /* N/A */
+ { 0x16, 0x411111f0 }, /* N/A */
+ { 0x17, 0x411111f0 }, /* N/A */
+ { 0x18, 0x90a60160 }, /* mic */
+ { 0x19, 0x411111f0 }, /* N/A */
+ { 0x1a, 0x411111f0 }, /* N/A */
+ { 0x1b, 0x411111f0 }, /* N/A */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ { 0x1e, 0xb743111e }, /* SPDIF out */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_GPIO1,
+ },
+ [ALC880_FIXUP_3ST_BASE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x01014010 }, /* line-out */
+ { 0x15, 0x411111f0 }, /* N/A */
+ { 0x16, 0x411111f0 }, /* N/A */
+ { 0x17, 0x411111f0 }, /* N/A */
+ { 0x18, 0x01a19c30 }, /* mic-in */
+ { 0x19, 0x0121411f }, /* HP */
+ { 0x1a, 0x01813031 }, /* line-in */
+ { 0x1b, 0x02a19c40 }, /* front-mic */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ /* 0x1e is filled in below */
+ { 0x1f, 0x411111f0 }, /* N/A */
+ { }
+ }
+ },
+ [ALC880_FIXUP_3ST] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_3ST_BASE,
+ },
+ [ALC880_FIXUP_3ST_DIG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x0144111e }, /* SPDIF */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_3ST_BASE,
+ },
+ [ALC880_FIXUP_5ST_BASE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x01014010 }, /* front */
+ { 0x15, 0x411111f0 }, /* N/A */
+ { 0x16, 0x01011411 }, /* CLFE */
+ { 0x17, 0x01016412 }, /* surr */
+ { 0x18, 0x01a19c30 }, /* mic-in */
+ { 0x19, 0x0121411f }, /* HP */
+ { 0x1a, 0x01813031 }, /* line-in */
+ { 0x1b, 0x02a19c40 }, /* front-mic */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ /* 0x1e is filled in below */
+ { 0x1f, 0x411111f0 }, /* N/A */
+ { }
+ }
+ },
+ [ALC880_FIXUP_5ST] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_5ST_BASE,
+ },
+ [ALC880_FIXUP_5ST_DIG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x0144111e }, /* SPDIF */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_5ST_BASE,
+ },
+ [ALC880_FIXUP_6ST_BASE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x01014010 }, /* front */
+ { 0x15, 0x01016412 }, /* surr */
+ { 0x16, 0x01011411 }, /* CLFE */
+ { 0x17, 0x01012414 }, /* side */
+ { 0x18, 0x01a19c30 }, /* mic-in */
+ { 0x19, 0x02a19c40 }, /* front-mic */
+ { 0x1a, 0x01813031 }, /* line-in */
+ { 0x1b, 0x0121411f }, /* HP */
+ { 0x1c, 0x411111f0 }, /* N/A */
+ { 0x1d, 0x411111f0 }, /* N/A */
+ /* 0x1e is filled in below */
+ { 0x1f, 0x411111f0 }, /* N/A */
+ { }
+ }
+ },
+ [ALC880_FIXUP_6ST] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x411111f0 }, /* N/A */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_6ST_BASE,
+ },
+ [ALC880_FIXUP_6ST_DIG] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1e, 0x0144111e }, /* SPDIF */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC880_FIXUP_6ST_BASE,
+ },
+ [ALC880_FIXUP_6ST_AUTOMUTE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x0121401f }, /* HP with jack detect */
+ { }
+ },
+ .chained_before = true,
+ .chain_id = ALC880_FIXUP_6ST_BASE,
+ },
+};
+
+static const struct hda_quirk alc880_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_FIXUP_W810),
+ SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS W5A", ALC880_FIXUP_ASUS_W5A),
+ SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_FIXUP_Z71V),
+ SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_FIXUP_GPIO1),
+ SND_PCI_QUIRK(0x147b, 0x1045, "ABit AA8XE", ALC880_FIXUP_6ST_AUTOMUTE),
+ SND_PCI_QUIRK(0x1558, 0x5401, "Clevo GPIO2", ALC880_FIXUP_GPIO2),
+ SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", ALC880_FIXUP_EAPD_COEF),
+ SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_FIXUP_UNIWILL_DIG),
+ SND_PCI_QUIRK(0x1584, 0x9054, "Uniwill", ALC880_FIXUP_F1734),
+ SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_FIXUP_UNIWILL),
+ SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB),
+ SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810),
+ SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM),
+ SND_PCI_QUIRK(0x1631, 0xe011, "PB 13201056", ALC880_FIXUP_6ST_AUTOMUTE),
+ SND_PCI_QUIRK(0x1734, 0x107c, "FSC Amilo M1437", ALC880_FIXUP_FUJITSU),
+ SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU),
+ SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_FIXUP_F1734),
+ SND_PCI_QUIRK(0x1734, 0x10b0, "FSC Amilo Pi1556", ALC880_FIXUP_FUJITSU),
+ SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_FIXUP_LG),
+ SND_PCI_QUIRK(0x1854, 0x005f, "LG P1 Express", ALC880_FIXUP_LG),
+ SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_FIXUP_LG),
+ SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_FIXUP_LG_LW25),
+ SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_FIXUP_TCL_S700),
+
+ /* Below is the copied entries from alc880_quirks.c.
+ * It's not quite sure whether BIOS sets the correct pin-config table
+ * on these machines, thus they are kept to be compatible with
+ * the old static quirks. Once when it's confirmed to work without
+ * these overrides, it'd be better to remove.
+ */
+ SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_FIXUP_6ST),
+ SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_FIXUP_3ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_FIXUP_3ST_DIG),
+ SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_FIXUP_3ST),
+ SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_FIXUP_3ST),
+ SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_FIXUP_3ST),
+ SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_FIXUP_5ST),
+ SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_FIXUP_5ST),
+ SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_FIXUP_5ST),
+ SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_FIXUP_6ST_DIG), /* broken BIOS */
+ SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_FIXUP_6ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_FIXUP_3ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_FIXUP_3ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_FIXUP_3ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_FIXUP_5ST_DIG),
+ /* default Intel */
+ SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_FIXUP_3ST),
+ SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_FIXUP_5ST_DIG),
+ SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_FIXUP_6ST_DIG),
+ {}
+};
+
+static const struct hda_model_fixup alc880_fixup_models[] = {
+ {.id = ALC880_FIXUP_3ST, .name = "3stack"},
+ {.id = ALC880_FIXUP_3ST_DIG, .name = "3stack-digout"},
+ {.id = ALC880_FIXUP_5ST, .name = "5stack"},
+ {.id = ALC880_FIXUP_5ST_DIG, .name = "5stack-digout"},
+ {.id = ALC880_FIXUP_6ST, .name = "6stack"},
+ {.id = ALC880_FIXUP_6ST_DIG, .name = "6stack-digout"},
+ {.id = ALC880_FIXUP_6ST_AUTOMUTE, .name = "6stack-automute"},
+ {}
+};
+
+
+/*
+ * OK, here we have finally the probe for ALC880
+ */
+static int alc880_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct alc_spec *spec;
+ int err;
+
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
+ spec->gen.need_dac_fix = 1;
+ spec->gen.beep_nid = 0x01;
+
+ alc_pre_init(codec);
+
+ snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl,
+ alc880_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ /* automatic parse from the BIOS config */
+ err = alc880_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ if (!spec->gen.no_analog) {
+ err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ if (err < 0)
+ goto error;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops alc880_codec_ops = {
+ .probe = alc880_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = alc880_unsol_event,
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc880[] = {
+ HDA_CODEC_ID(0x10ec0880, "ALC880"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc880);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC880 HD-audio codec");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+
+static struct hda_codec_driver alc880_driver = {
+ .id = snd_hda_id_alc880,
+ .ops = &alc880_codec_ops,
+};
+
+module_hda_codec_driver(alc880_driver);
diff --git a/sound/hda/codecs/realtek/alc882.c b/sound/hda/codecs/realtek/alc882.c
new file mode 100644
index 000000000000..529fecd5baa0
--- /dev/null
+++ b/sound/hda/codecs/realtek/alc882.c
@@ -0,0 +1,861 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek ALC882/883/885/888/889 codec support
+//
+// ALC882 is almost identical with ALC880 but has cleaner and more flexible
+// configuration. Each pin widget can choose any input DACs and a mixer.
+// Each ADC is connected from a mixer of all inputs. This makes possible
+// 6-channel independent captures.
+//
+// In addition, an independent DAC for the multi-playback (not used in this
+// driver yet).
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+/*
+ * Pin config fixes
+ */
+enum {
+ ALC882_FIXUP_ABIT_AW9D_MAX,
+ ALC882_FIXUP_LENOVO_Y530,
+ ALC882_FIXUP_PB_M5210,
+ ALC882_FIXUP_ACER_ASPIRE_7736,
+ ALC882_FIXUP_ASUS_W90V,
+ ALC889_FIXUP_CD,
+ ALC889_FIXUP_FRONT_HP_NO_PRESENCE,
+ ALC889_FIXUP_VAIO_TT,
+ ALC888_FIXUP_EEE1601,
+ ALC886_FIXUP_EAPD,
+ ALC882_FIXUP_EAPD,
+ ALC883_FIXUP_EAPD,
+ ALC883_FIXUP_ACER_EAPD,
+ ALC882_FIXUP_GPIO1,
+ ALC882_FIXUP_GPIO2,
+ ALC882_FIXUP_GPIO3,
+ ALC889_FIXUP_COEF,
+ ALC882_FIXUP_ASUS_W2JC,
+ ALC882_FIXUP_ACER_ASPIRE_4930G,
+ ALC882_FIXUP_ACER_ASPIRE_8930G,
+ ALC882_FIXUP_ASPIRE_8930G_VERBS,
+ ALC885_FIXUP_MACPRO_GPIO,
+ ALC889_FIXUP_DAC_ROUTE,
+ ALC889_FIXUP_MBP_VREF,
+ ALC889_FIXUP_IMAC91_VREF,
+ ALC889_FIXUP_MBA11_VREF,
+ ALC889_FIXUP_MBA21_VREF,
+ ALC889_FIXUP_MP11_VREF,
+ ALC889_FIXUP_MP41_VREF,
+ ALC882_FIXUP_INV_DMIC,
+ ALC882_FIXUP_NO_PRIMARY_HP,
+ ALC887_FIXUP_ASUS_BASS,
+ ALC887_FIXUP_BASS_CHMAP,
+ ALC1220_FIXUP_GB_DUAL_CODECS,
+ ALC1220_FIXUP_GB_X570,
+ ALC1220_FIXUP_CLEVO_P950,
+ ALC1220_FIXUP_CLEVO_PB51ED,
+ ALC1220_FIXUP_CLEVO_PB51ED_PINS,
+ ALC887_FIXUP_ASUS_AUDIO,
+ ALC887_FIXUP_ASUS_HMIC,
+ ALCS1200A_FIXUP_MIC_VREF,
+ ALC888VD_FIXUP_MIC_100VREF,
+};
+
+static void alc889_fixup_coef(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
+ alc_update_coef_idx(codec, 7, 0, 0x2030);
+}
+
+/* set up GPIO at initialization */
+static void alc885_fixup_macpro_gpio(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ spec->gpio_write_delay = true;
+ alc_fixup_gpio3(codec, fix, action);
+}
+
+/* Fix the connection of some pins for ALC889:
+ * At least, Acer Aspire 5935 shows the connections to DAC3/4 don't
+ * work correctly (bko#42740)
+ */
+static void alc889_fixup_dac_route(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* fake the connections during parsing the tree */
+ static const hda_nid_t conn1[] = { 0x0c, 0x0d };
+ static const hda_nid_t conn2[] = { 0x0e, 0x0f };
+ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1);
+ snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn1), conn1);
+ snd_hda_override_conn_list(codec, 0x18, ARRAY_SIZE(conn2), conn2);
+ snd_hda_override_conn_list(codec, 0x1a, ARRAY_SIZE(conn2), conn2);
+ } else if (action == HDA_FIXUP_ACT_PROBE) {
+ /* restore the connections */
+ static const hda_nid_t conn[] = { 0x0c, 0x0d, 0x0e, 0x0f, 0x26 };
+ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn), conn);
+ snd_hda_override_conn_list(codec, 0x15, ARRAY_SIZE(conn), conn);
+ snd_hda_override_conn_list(codec, 0x18, ARRAY_SIZE(conn), conn);
+ snd_hda_override_conn_list(codec, 0x1a, ARRAY_SIZE(conn), conn);
+ }
+}
+
+/* Set VREF on HP pin */
+static void alc889_fixup_mbp_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const hda_nid_t nids[] = { 0x14, 0x15, 0x19 };
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ if (action != HDA_FIXUP_ACT_INIT)
+ return;
+ for (i = 0; i < ARRAY_SIZE(nids); i++) {
+ unsigned int val = snd_hda_codec_get_pincfg(codec, nids[i]);
+ if (get_defcfg_device(val) != AC_JACK_HP_OUT)
+ continue;
+ val = snd_hda_codec_get_pin_target(codec, nids[i]);
+ val |= AC_PINCTL_VREF_80;
+ snd_hda_set_pin_ctl(codec, nids[i], val);
+ spec->gen.keep_vref_in_automute = 1;
+ break;
+ }
+}
+
+static void alc889_fixup_mac_pins(struct hda_codec *codec,
+ const hda_nid_t *nids, int num_nids)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < num_nids; i++) {
+ unsigned int val;
+ val = snd_hda_codec_get_pin_target(codec, nids[i]);
+ val |= AC_PINCTL_VREF_50;
+ snd_hda_set_pin_ctl(codec, nids[i], val);
+ }
+ spec->gen.keep_vref_in_automute = 1;
+}
+
+/* Set VREF on speaker pins on imac91 */
+static void alc889_fixup_imac91_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const hda_nid_t nids[] = { 0x18, 0x1a };
+
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
+}
+
+/* Set VREF on speaker pins on mba11 */
+static void alc889_fixup_mba11_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const hda_nid_t nids[] = { 0x18 };
+
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
+}
+
+/* Set VREF on speaker pins on mba21 */
+static void alc889_fixup_mba21_vref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const hda_nid_t nids[] = { 0x18, 0x19 };
+
+ if (action == HDA_FIXUP_ACT_INIT)
+ alc889_fixup_mac_pins(codec, nids, ARRAY_SIZE(nids));
+}
+
+/* Don't take HP output as primary
+ * Strangely, the speaker output doesn't work on Vaio Z and some Vaio
+ * all-in-one desktop PCs (for example VGC-LN51JGB) through DAC 0x05
+ */
+static void alc882_fixup_no_primary_hp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.no_primary_hp = 1;
+ spec->gen.no_multi_io = 1;
+ }
+}
+
+static void alc1220_fixup_gb_x570(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ static const hda_nid_t conn1[] = { 0x0c };
+ static const struct coef_fw gb_x570_coefs[] = {
+ WRITE_COEF(0x07, 0x03c0),
+ WRITE_COEF(0x1a, 0x01c1),
+ WRITE_COEF(0x1b, 0x0202),
+ WRITE_COEF(0x43, 0x3005),
+ {}
+ };
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1);
+ snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ alc_process_coef_fw(codec, gb_x570_coefs);
+ break;
+ }
+}
+
+static void alc1220_fixup_clevo_p950(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ static const hda_nid_t conn1[] = { 0x0c };
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ alc_update_coef_idx(codec, 0x7, 0, 0x3c3);
+ /* We therefore want to make sure 0x14 (front headphone) and
+ * 0x1b (speakers) use the stereo DAC 0x02
+ */
+ snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn1), conn1);
+ snd_hda_override_conn_list(codec, 0x1b, ARRAY_SIZE(conn1), conn1);
+}
+
+static void alc1220_fixup_clevo_pb51ed(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ alc1220_fixup_clevo_p950(codec, fix, action);
+ alc_fixup_headset_mode_no_hp_mic(codec, fix, action);
+}
+
+static void alc887_asus_hp_automute_hook(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int vref;
+
+ snd_hda_gen_hp_automute(codec, jack);
+
+ if (spec->gen.hp_jack_present)
+ vref = AC_PINCTL_VREF_80;
+ else
+ vref = AC_PINCTL_VREF_HIZ;
+ snd_hda_set_pin_ctl(codec, 0x19, PIN_HP | vref);
+}
+
+static void alc887_fixup_asus_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action != HDA_FIXUP_ACT_PROBE)
+ return;
+ snd_hda_set_pin_ctl_cache(codec, 0x1b, PIN_HP);
+ spec->gen.hp_automute_hook = alc887_asus_hp_automute_hook;
+}
+
+static const struct hda_fixup alc882_fixups[] = {
+ [ALC882_FIXUP_ABIT_AW9D_MAX] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x01080104 }, /* side */
+ { 0x16, 0x01011012 }, /* rear */
+ { 0x17, 0x01016011 }, /* clfe */
+ { }
+ }
+ },
+ [ALC882_FIXUP_LENOVO_Y530] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x99130112 }, /* rear int speakers */
+ { 0x16, 0x99130111 }, /* subwoofer */
+ { }
+ }
+ },
+ [ALC882_FIXUP_PB_M5210] = {
+ .type = HDA_FIXUP_PINCTLS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, PIN_VREF50 },
+ {}
+ }
+ },
+ [ALC882_FIXUP_ACER_ASPIRE_7736] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_sku_ignore,
+ },
+ [ALC882_FIXUP_ASUS_W90V] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x99130110 }, /* fix sequence for CLFE */
+ { }
+ }
+ },
+ [ALC889_FIXUP_CD] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1c, 0x993301f0 }, /* CD */
+ { }
+ }
+ },
+ [ALC889_FIXUP_FRONT_HP_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x02214120 }, /* Front HP jack is flaky, disable jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC889_FIXUP_CD,
+ },
+ [ALC889_FIXUP_VAIO_TT] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x17, 0x90170111 }, /* hidden surround speaker */
+ { }
+ }
+ },
+ [ALC888_FIXUP_EEE1601] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0838 },
+ { }
+ }
+ },
+ [ALC886_FIXUP_EAPD] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* change to EAPD mode */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0068 },
+ { }
+ }
+ },
+ [ALC882_FIXUP_EAPD] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* change to EAPD mode */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 },
+ { }
+ }
+ },
+ [ALC883_FIXUP_EAPD] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* change to EAPD mode */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 },
+ { }
+ }
+ },
+ [ALC883_FIXUP_ACER_EAPD] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* eanable EAPD on Acer laptops */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 },
+ { }
+ }
+ },
+ [ALC882_FIXUP_GPIO1] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_gpio1,
+ },
+ [ALC882_FIXUP_GPIO2] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_gpio2,
+ },
+ [ALC882_FIXUP_GPIO3] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_gpio3,
+ },
+ [ALC882_FIXUP_ASUS_W2JC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_gpio1,
+ .chained = true,
+ .chain_id = ALC882_FIXUP_EAPD,
+ },
+ [ALC889_FIXUP_COEF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_coef,
+ },
+ [ALC882_FIXUP_ACER_ASPIRE_4930G] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x99130111 }, /* CLFE speaker */
+ { 0x17, 0x99130112 }, /* surround speaker */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC882_FIXUP_GPIO1,
+ },
+ [ALC882_FIXUP_ACER_ASPIRE_8930G] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x16, 0x99130111 }, /* CLFE speaker */
+ { 0x1b, 0x99130112 }, /* surround speaker */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC882_FIXUP_ASPIRE_8930G_VERBS,
+ },
+ [ALC882_FIXUP_ASPIRE_8930G_VERBS] = {
+ /* additional init verbs for Acer Aspire 8930G */
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Enable all DACs */
+ /* DAC DISABLE/MUTE 1? */
+ /* setting bits 1-5 disables DAC nids 0x02-0x06
+ * apparently. Init=0x38 */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x03 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+ /* DAC DISABLE/MUTE 2? */
+ /* some bit here disables the other DACs.
+ * Init=0x4900 */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x08 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0000 },
+ /* DMIC fix
+ * This laptop has a stereo digital microphone.
+ * The mics are only 1cm apart which makes the stereo
+ * useless. However, either the mic or the ALC889
+ * makes the signal become a difference/sum signal
+ * instead of standard stereo, which is annoying.
+ * So instead we flip this bit which makes the
+ * codec replicate the sum signal to both channels,
+ * turning it into a normal mono mic.
+ */
+ /* DMIC_CONTROL? Init value = 0x0001 */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0003 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC882_FIXUP_GPIO1,
+ },
+ [ALC885_FIXUP_MACPRO_GPIO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc885_fixup_macpro_gpio,
+ },
+ [ALC889_FIXUP_DAC_ROUTE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_dac_route,
+ },
+ [ALC889_FIXUP_MBP_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mbp_vref,
+ .chained = true,
+ .chain_id = ALC882_FIXUP_GPIO1,
+ },
+ [ALC889_FIXUP_IMAC91_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_imac91_vref,
+ .chained = true,
+ .chain_id = ALC882_FIXUP_GPIO1,
+ },
+ [ALC889_FIXUP_MBA11_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mba11_vref,
+ .chained = true,
+ .chain_id = ALC889_FIXUP_MBP_VREF,
+ },
+ [ALC889_FIXUP_MBA21_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mba21_vref,
+ .chained = true,
+ .chain_id = ALC889_FIXUP_MBP_VREF,
+ },
+ [ALC889_FIXUP_MP11_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mba11_vref,
+ .chained = true,
+ .chain_id = ALC885_FIXUP_MACPRO_GPIO,
+ },
+ [ALC889_FIXUP_MP41_VREF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc889_fixup_mbp_vref,
+ .chained = true,
+ .chain_id = ALC885_FIXUP_MACPRO_GPIO,
+ },
+ [ALC882_FIXUP_INV_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic,
+ },
+ [ALC882_FIXUP_NO_PRIMARY_HP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc882_fixup_no_primary_hp,
+ },
+ [ALC887_FIXUP_ASUS_BASS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x16, 0x99130130}, /* bass speaker */
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC887_FIXUP_BASS_CHMAP,
+ },
+ [ALC887_FIXUP_BASS_CHMAP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_bass_chmap,
+ },
+ [ALC1220_FIXUP_GB_DUAL_CODECS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc1220_fixup_gb_dual_codecs,
+ },
+ [ALC1220_FIXUP_GB_X570] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc1220_fixup_gb_x570,
+ },
+ [ALC1220_FIXUP_CLEVO_P950] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc1220_fixup_clevo_p950,
+ },
+ [ALC1220_FIXUP_CLEVO_PB51ED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc1220_fixup_clevo_pb51ed,
+ },
+ [ALC1220_FIXUP_CLEVO_PB51ED_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC1220_FIXUP_CLEVO_PB51ED,
+ },
+ [ALC887_FIXUP_ASUS_AUDIO] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x02a14150 }, /* use as headset mic, without its own jack detect */
+ { 0x19, 0x22219420 },
+ {}
+ },
+ },
+ [ALC887_FIXUP_ASUS_HMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc887_fixup_asus_jack,
+ .chained = true,
+ .chain_id = ALC887_FIXUP_ASUS_AUDIO,
+ },
+ [ALCS1200A_FIXUP_MIC_VREF] = {
+ .type = HDA_FIXUP_PINCTLS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, PIN_VREF50 }, /* rear mic */
+ { 0x19, PIN_VREF50 }, /* front mic */
+ {}
+ }
+ },
+ [ALC888VD_FIXUP_MIC_100VREF] = {
+ .type = HDA_FIXUP_PINCTLS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, PIN_VREF100 }, /* headset mic */
+ {}
+ }
+ },
+};
+
+static const struct hda_quirk alc882_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x0107, "Acer Aspire", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_FIXUP_ACER_EAPD),
+ SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G",
+ ALC882_FIXUP_ACER_ASPIRE_8930G),
+ SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G",
+ ALC882_FIXUP_ACER_ASPIRE_8930G),
+ SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", ALC882_FIXUP_PB_M5210),
+ SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x021e, "Acer Aspire 5739G",
+ ALC882_FIXUP_ACER_ASPIRE_4930G),
+ SND_PCI_QUIRK(0x1025, 0x0259, "Acer Aspire 5935", ALC889_FIXUP_DAC_ROUTE),
+ SND_PCI_QUIRK(0x1025, 0x026b, "Acer Aspire 8940G", ALC882_FIXUP_ACER_ASPIRE_8930G),
+ SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", ALC882_FIXUP_ACER_ASPIRE_7736),
+ SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x1043, 0x1873, "ASUS W90V", ALC882_FIXUP_ASUS_W90V),
+ SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC),
+ SND_PCI_QUIRK(0x1043, 0x2390, "Asus D700SA", ALC887_FIXUP_ASUS_HMIC),
+ SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601),
+ SND_PCI_QUIRK(0x1043, 0x84bc, "ASUS ET2700", ALC887_FIXUP_ASUS_BASS),
+ SND_PCI_QUIRK(0x1043, 0x8691, "ASUS ROG Ranger VIII", ALC882_FIXUP_GPIO3),
+ SND_PCI_QUIRK(0x1043, 0x8797, "ASUS TUF B550M-PLUS", ALCS1200A_FIXUP_MIC_VREF),
+ SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP),
+ SND_PCI_QUIRK(0x104d, 0x9044, "Sony VAIO AiO", ALC882_FIXUP_NO_PRIMARY_HP),
+ SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT),
+ SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP),
+ SND_PCI_QUIRK(0x104d, 0x9060, "Sony Vaio VPCL14M1R", ALC882_FIXUP_NO_PRIMARY_HP),
+
+ /* All Apple entries are in codec SSIDs */
+ SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC889_FIXUP_MP11_VREF),
+ SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_FIXUP_MACPRO_GPIO),
+ SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_FIXUP_MACPRO_GPIO),
+ SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC889_FIXUP_MBA11_VREF),
+ SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC889_FIXUP_MBA21_VREF),
+ SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC889_FIXUP_MBP_VREF),
+ SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_FIXUP_MACPRO_GPIO),
+ SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC889_FIXUP_IMAC91_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC889_FIXUP_IMAC91_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC889_FIXUP_IMAC91_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4200, "Mac Pro 4,1/5,1", ALC889_FIXUP_MP41_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4300, "iMac 9,1", ALC889_FIXUP_IMAC91_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC889_FIXUP_IMAC91_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC889_FIXUP_IMAC91_VREF),
+ SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC889_FIXUP_MBA11_VREF),
+
+ SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC882_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x10ec, 0x12d8, "iBase Elo Touch", ALC888VD_FIXUP_MIC_100VREF),
+ SND_PCI_QUIRK(0x13fe, 0x1009, "Advantech MIT-W101", ALC886_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_GB_X570),
+ SND_PCI_QUIRK(0x1458, 0xa0ce, "Gigabyte X570 Aorus Xtreme", ALC1220_FIXUP_GB_X570),
+ SND_PCI_QUIRK(0x1458, 0xa0d5, "Gigabyte X570S Aorus Master", ALC1220_FIXUP_GB_X570),
+ SND_PCI_QUIRK(0x1462, 0x11f7, "MSI-GE63", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1462, 0x1228, "MSI-GP63", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1462, 0x1229, "MSI-GP73", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1462, 0x1275, "MSI-GL63", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1462, 0x1276, "MSI-GL73", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1462, 0x1293, "MSI-GP65", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD),
+ SND_PCI_QUIRK(0x1462, 0xcc34, "MSI Godlike X570", ALC1220_FIXUP_GB_DUAL_CODECS),
+ SND_PCI_QUIRK(0x1462, 0xda57, "MSI Z270-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS),
+ SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3),
+ SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX),
+ SND_PCI_QUIRK(0x1558, 0x3702, "Clevo X370SN[VW]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x50d3, "Clevo PC50[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x5802, "Clevo X58[05]WN[RST]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x65d1, "Clevo PB51[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x65d2, "Clevo PB51R[CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x65e1, "Clevo PB51[ED][DF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x65e5, "Clevo PC50D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x65f1, "Clevo PC50HS", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x65f5, "Clevo PD50PN[NRT]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x66a2, "Clevo PE60RNE", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x66a6, "Clevo PE60SN[CDE]-[GS]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x67d1, "Clevo PB71[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x67e1, "Clevo PB71[DE][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x67e5, "Clevo PC70D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x67f1, "Clevo PC70H[PRS]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x67f5, "Clevo PD70PN[NRT]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x70d1, "Clevo PC70[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x7714, "Clevo X170SM", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK(0x1558, 0x7715, "Clevo X170KM-G", ALC1220_FIXUP_CLEVO_PB51ED),
+ SND_PCI_QUIRK(0x1558, 0x9501, "Clevo P950HR", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x9506, "Clevo P955HQ", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x950a, "Clevo P955H[PR]", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x95e1, "Clevo P95xER", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x95e2, "Clevo P950ER", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x95e3, "Clevo P955[ER]T", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x95e4, "Clevo P955ER", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x95e5, "Clevo P955EE6", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x95e6, "Clevo P950R[CDF]", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x96e1, "Clevo P960[ER][CDFN]-K", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x97e1, "Clevo P970[ER][CDFN]", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0x97e2, "Clevo P970RC-M", ALC1220_FIXUP_CLEVO_P950),
+ SND_PCI_QUIRK(0x1558, 0xd502, "Clevo PD50SNE", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+ SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD),
+ SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530),
+ SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_FIXUP_COEF),
+ {}
+};
+
+static const struct hda_model_fixup alc882_fixup_models[] = {
+ {.id = ALC882_FIXUP_ABIT_AW9D_MAX, .name = "abit-aw9d"},
+ {.id = ALC882_FIXUP_LENOVO_Y530, .name = "lenovo-y530"},
+ {.id = ALC882_FIXUP_ACER_ASPIRE_7736, .name = "acer-aspire-7736"},
+ {.id = ALC882_FIXUP_ASUS_W90V, .name = "asus-w90v"},
+ {.id = ALC889_FIXUP_CD, .name = "cd"},
+ {.id = ALC889_FIXUP_FRONT_HP_NO_PRESENCE, .name = "no-front-hp"},
+ {.id = ALC889_FIXUP_VAIO_TT, .name = "vaio-tt"},
+ {.id = ALC888_FIXUP_EEE1601, .name = "eee1601"},
+ {.id = ALC882_FIXUP_EAPD, .name = "alc882-eapd"},
+ {.id = ALC883_FIXUP_EAPD, .name = "alc883-eapd"},
+ {.id = ALC882_FIXUP_GPIO1, .name = "gpio1"},
+ {.id = ALC882_FIXUP_GPIO2, .name = "gpio2"},
+ {.id = ALC882_FIXUP_GPIO3, .name = "gpio3"},
+ {.id = ALC889_FIXUP_COEF, .name = "alc889-coef"},
+ {.id = ALC882_FIXUP_ASUS_W2JC, .name = "asus-w2jc"},
+ {.id = ALC882_FIXUP_ACER_ASPIRE_4930G, .name = "acer-aspire-4930g"},
+ {.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"},
+ {.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"},
+ {.id = ALC885_FIXUP_MACPRO_GPIO, .name = "macpro-gpio"},
+ {.id = ALC889_FIXUP_DAC_ROUTE, .name = "dac-route"},
+ {.id = ALC889_FIXUP_MBP_VREF, .name = "mbp-vref"},
+ {.id = ALC889_FIXUP_IMAC91_VREF, .name = "imac91-vref"},
+ {.id = ALC889_FIXUP_MBA11_VREF, .name = "mba11-vref"},
+ {.id = ALC889_FIXUP_MBA21_VREF, .name = "mba21-vref"},
+ {.id = ALC889_FIXUP_MP11_VREF, .name = "mp11-vref"},
+ {.id = ALC889_FIXUP_MP41_VREF, .name = "mp41-vref"},
+ {.id = ALC882_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {.id = ALC882_FIXUP_NO_PRIMARY_HP, .name = "no-primary-hp"},
+ {.id = ALC887_FIXUP_ASUS_BASS, .name = "asus-bass"},
+ {.id = ALC1220_FIXUP_GB_DUAL_CODECS, .name = "dual-codecs"},
+ {.id = ALC1220_FIXUP_GB_X570, .name = "gb-x570"},
+ {.id = ALC1220_FIXUP_CLEVO_P950, .name = "clevo-p950"},
+ {}
+};
+
+static const struct snd_hda_pin_quirk alc882_pin_fixup_tbl[] = {
+ SND_HDA_PIN_QUIRK(0x10ec1220, 0x1043, "ASUS", ALC1220_FIXUP_CLEVO_P950,
+ {0x14, 0x01014010},
+ {0x15, 0x01011012},
+ {0x16, 0x01016011},
+ {0x18, 0x01a19040},
+ {0x19, 0x02a19050},
+ {0x1a, 0x0181304f},
+ {0x1b, 0x0221401f},
+ {0x1e, 0x01456130}),
+ SND_HDA_PIN_QUIRK(0x10ec1220, 0x1462, "MS-7C35", ALC1220_FIXUP_CLEVO_P950,
+ {0x14, 0x01015010},
+ {0x15, 0x01011012},
+ {0x16, 0x01011011},
+ {0x18, 0x01a11040},
+ {0x19, 0x02a19050},
+ {0x1a, 0x0181104f},
+ {0x1b, 0x0221401f},
+ {0x1e, 0x01451130}),
+ {}
+};
+
+/*
+ * BIOS auto configuration
+ */
+/* almost identical with ALC880 parser... */
+static int alc882_parse_auto_config(struct hda_codec *codec)
+{
+ static const hda_nid_t alc882_ignore[] = { 0x1d, 0 };
+ static const hda_nid_t alc882_ssids[] = { 0x15, 0x1b, 0x14, 0 };
+ return alc_parse_auto_config(codec, alc882_ignore, alc882_ssids);
+}
+
+/*
+ */
+static int alc882_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct alc_spec *spec;
+ int err;
+
+ err = alc_alloc_spec(codec, 0x0b);
+ if (err < 0)
+ return err;
+
+ spec = codec->spec;
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0882:
+ case 0x10ec0885:
+ case 0x10ec0900:
+ case 0x10ec0b00:
+ case 0x10ec1220:
+ break;
+ default:
+ /* ALC883 and variants */
+ alc_fix_pll_init(codec, 0x20, 0x0a, 10);
+ break;
+ }
+
+ alc_pre_init(codec);
+
+ snd_hda_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl,
+ alc882_fixups);
+ snd_hda_pick_pin_fixup(codec, alc882_pin_fixup_tbl, alc882_fixups, true);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ alc_auto_parse_customize_define(codec);
+
+ if (has_cdefine_beep(codec))
+ spec->gen.beep_nid = 0x01;
+
+ /* automatic parse from the BIOS config */
+ err = alc882_parse_auto_config(codec);
+ if (err < 0)
+ goto error;
+
+ if (!spec->gen.no_analog && spec->gen.beep_nid) {
+ err = set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ if (err < 0)
+ goto error;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ snd_hda_gen_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops alc882_codec_ops = {
+ .probe = alc882_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = alc_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = alc_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .resume = alc_resume,
+ .suspend = alc_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_alc882[] = {
+ HDA_CODEC_ID_REV(0x10ec0662, 0x100002, "ALC662 rev2"),
+ HDA_CODEC_ID(0x10ec0882, "ALC882"),
+ HDA_CODEC_ID(0x10ec0883, "ALC883"),
+ HDA_CODEC_ID_REV(0x10ec0885, 0x100101, "ALC889A"),
+ HDA_CODEC_ID_REV(0x10ec0885, 0x100103, "ALC889A"),
+ HDA_CODEC_ID(0x10ec0885, "ALC885"),
+ HDA_CODEC_ID(0x10ec0887, "ALC887"),
+ HDA_CODEC_ID_REV(0x10ec0888, 0x100101, "ALC1200"),
+ HDA_CODEC_ID(0x10ec0888, "ALC888"),
+ HDA_CODEC_ID(0x10ec0889, "ALC889"),
+ HDA_CODEC_ID(0x10ec0899, "ALC898"),
+ HDA_CODEC_ID(0x10ec0900, "ALC1150"),
+ HDA_CODEC_ID(0x10ec0b00, "ALCS1200A"),
+ HDA_CODEC_ID(0x10ec1168, "ALC1220"),
+ HDA_CODEC_ID(0x10ec1220, "ALC1220"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_alc882);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek ALC882 and compatible HD-audio codecs");
+MODULE_IMPORT_NS("SND_HDA_CODEC_REALTEK");
+
+static struct hda_codec_driver alc882_driver = {
+ .id = snd_hda_id_alc882,
+ .ops = &alc882_codec_ops,
+};
+
+module_hda_codec_driver(alc882_driver);
diff --git a/sound/hda/codecs/realtek/realtek.c b/sound/hda/codecs/realtek/realtek.c
new file mode 100644
index 000000000000..ca377a5adadb
--- /dev/null
+++ b/sound/hda/codecs/realtek/realtek.c
@@ -0,0 +1,2271 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek HD-audio codec support code
+//
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include "realtek.h"
+
+static int __alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx)
+{
+ unsigned int val;
+
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx);
+ val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0);
+ return val;
+}
+
+int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx)
+{
+ guard(coef_mutex)(codec);
+ return __alc_read_coefex_idx(codec, nid, coef_idx);
+}
+EXPORT_SYMBOL_NS_GPL(alc_read_coefex_idx, "SND_HDA_CODEC_REALTEK");
+
+static void __alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int coef_val)
+{
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, coef_idx);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PROC_COEF, coef_val);
+}
+
+void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int coef_val)
+{
+ guard(coef_mutex)(codec);
+ __alc_write_coefex_idx(codec, nid, coef_idx, coef_val);
+}
+EXPORT_SYMBOL_NS_GPL(alc_write_coefex_idx, "SND_HDA_CODEC_REALTEK");
+
+static void __alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int mask,
+ unsigned int bits_set)
+{
+ unsigned int val = __alc_read_coefex_idx(codec, nid, coef_idx);
+
+ if (val != -1)
+ __alc_write_coefex_idx(codec, nid, coef_idx,
+ (val & ~mask) | bits_set);
+}
+
+void alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int mask,
+ unsigned int bits_set)
+{
+ guard(coef_mutex)(codec);
+ __alc_update_coefex_idx(codec, nid, coef_idx, mask, bits_set);
+}
+EXPORT_SYMBOL_NS_GPL(alc_update_coefex_idx, "SND_HDA_CODEC_REALTEK");
+
+/* a special bypass for COEF 0; read the cached value at the second time */
+unsigned int alc_get_coef0(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (!spec->coef0)
+ spec->coef0 = alc_read_coef_idx(codec, 0);
+ return spec->coef0;
+}
+EXPORT_SYMBOL_NS_GPL(alc_get_coef0, "SND_HDA_CODEC_REALTEK");
+
+void alc_process_coef_fw(struct hda_codec *codec, const struct coef_fw *fw)
+{
+ guard(coef_mutex)(codec);
+ for (; fw->nid; fw++) {
+ if (fw->mask == (unsigned short)-1)
+ __alc_write_coefex_idx(codec, fw->nid, fw->idx, fw->val);
+ else
+ __alc_update_coefex_idx(codec, fw->nid, fw->idx,
+ fw->mask, fw->val);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_process_coef_fw, "SND_HDA_CODEC_REALTEK");
+
+/*
+ * GPIO setup tables, used in initialization
+ */
+
+/* Enable GPIO mask and set output */
+void alc_setup_gpio(struct hda_codec *codec, unsigned int mask)
+{
+ struct alc_spec *spec = codec->spec;
+
+ spec->gpio_mask |= mask;
+ spec->gpio_dir |= mask;
+ spec->gpio_data |= mask;
+}
+EXPORT_SYMBOL_NS_GPL(alc_setup_gpio, "SND_HDA_CODEC_REALTEK");
+
+void alc_write_gpio_data(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_data);
+}
+EXPORT_SYMBOL_NS_GPL(alc_write_gpio_data, "SND_HDA_CODEC_REALTEK");
+
+void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask,
+ bool on)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int oldval = spec->gpio_data;
+
+ if (on)
+ spec->gpio_data |= mask;
+ else
+ spec->gpio_data &= ~mask;
+ if (oldval != spec->gpio_data)
+ alc_write_gpio_data(codec);
+}
+EXPORT_SYMBOL_NS_GPL(alc_update_gpio_data, "SND_HDA_CODEC_REALTEK");
+
+void alc_write_gpio(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (!spec->gpio_mask)
+ return;
+
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_MASK, spec->gpio_mask);
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_DIRECTION, spec->gpio_dir);
+ if (spec->gpio_write_delay)
+ msleep(1);
+ alc_write_gpio_data(codec);
+}
+EXPORT_SYMBOL_NS_GPL(alc_write_gpio, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_gpio(struct hda_codec *codec, int action, unsigned int mask)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ alc_setup_gpio(codec, mask);
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_gpio1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_gpio(codec, action, 0x01);
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio1, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_gpio2(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_gpio(codec, action, 0x02);
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio2, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_gpio3(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_gpio(codec, action, 0x03);
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio3, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_gpio4(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_gpio(codec, action, 0x04);
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_gpio4, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_micmute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ snd_hda_gen_add_micmute_led_cdev(codec, NULL);
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_micmute_led, "SND_HDA_CODEC_REALTEK");
+
+/*
+ * Fix hardware PLL issue
+ * On some codecs, the analog PLL gating control must be off while
+ * the default value is 1.
+ */
+void alc_fix_pll(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->pll_nid)
+ alc_update_coefex_idx(codec, spec->pll_nid, spec->pll_coef_idx,
+ 1 << spec->pll_coef_bit, 0);
+}
+EXPORT_SYMBOL_NS_GPL(alc_fix_pll, "SND_HDA_CODEC_REALTEK");
+
+void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int coef_bit)
+{
+ struct alc_spec *spec = codec->spec;
+ spec->pll_nid = nid;
+ spec->pll_coef_idx = coef_idx;
+ spec->pll_coef_bit = coef_bit;
+ alc_fix_pll(codec);
+}
+EXPORT_SYMBOL_NS_GPL(alc_fix_pll_init, "SND_HDA_CODEC_REALTEK");
+
+/* update the master volume per volume-knob's unsol event */
+void alc_update_knob_master(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ unsigned int val;
+ struct snd_kcontrol *kctl;
+ struct snd_ctl_elem_value *uctl __free(kfree) = NULL;
+
+ kctl = snd_hda_find_mixer_ctl(codec, "Master Playback Volume");
+ if (!kctl)
+ return;
+ uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
+ if (!uctl)
+ return;
+ val = snd_hda_codec_read(codec, jack->nid, 0,
+ AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
+ val &= HDA_AMP_VOLMASK;
+ uctl->value.integer.value[0] = val;
+ uctl->value.integer.value[1] = val;
+ kctl->put(kctl, uctl);
+}
+EXPORT_SYMBOL_NS_GPL(alc_update_knob_master, "SND_HDA_CODEC_REALTEK");
+
+/* Change EAPD to verb control */
+void alc_fill_eapd_coef(struct hda_codec *codec)
+{
+ int coef;
+
+ coef = alc_get_coef0(codec);
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0262:
+ alc_update_coef_idx(codec, 0x7, 0, 1<<5);
+ break;
+ case 0x10ec0267:
+ case 0x10ec0268:
+ alc_update_coef_idx(codec, 0x7, 0, 1<<13);
+ break;
+ case 0x10ec0269:
+ if ((coef & 0x00f0) == 0x0010)
+ alc_update_coef_idx(codec, 0xd, 0, 1<<14);
+ if ((coef & 0x00f0) == 0x0020)
+ alc_update_coef_idx(codec, 0x4, 1<<15, 0);
+ if ((coef & 0x00f0) == 0x0030)
+ alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+ break;
+ case 0x10ec0280:
+ case 0x10ec0284:
+ case 0x10ec0290:
+ case 0x10ec0292:
+ alc_update_coef_idx(codec, 0x4, 1<<15, 0);
+ break;
+ case 0x10ec0225:
+ case 0x10ec0295:
+ case 0x10ec0299:
+ alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000);
+ fallthrough;
+ case 0x10ec0215:
+ case 0x10ec0236:
+ case 0x10ec0245:
+ case 0x10ec0256:
+ case 0x10ec0257:
+ case 0x10ec0285:
+ case 0x10ec0289:
+ alc_update_coef_idx(codec, 0x36, 1<<13, 0);
+ fallthrough;
+ case 0x10ec0230:
+ case 0x10ec0233:
+ case 0x10ec0235:
+ case 0x10ec0255:
+ case 0x19e58326:
+ case 0x10ec0282:
+ case 0x10ec0283:
+ case 0x10ec0286:
+ case 0x10ec0288:
+ case 0x10ec0298:
+ case 0x10ec0300:
+ alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+ break;
+ case 0x10ec0275:
+ alc_update_coef_idx(codec, 0xe, 0, 1<<0);
+ break;
+ case 0x10ec0287:
+ alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+ alc_write_coef_idx(codec, 0x8, 0x4ab7);
+ break;
+ case 0x10ec0293:
+ alc_update_coef_idx(codec, 0xa, 1<<13, 0);
+ break;
+ case 0x10ec0234:
+ case 0x10ec0274:
+ alc_write_coef_idx(codec, 0x6e, 0x0c25);
+ fallthrough;
+ case 0x10ec0294:
+ case 0x10ec0700:
+ case 0x10ec0701:
+ case 0x10ec0703:
+ case 0x10ec0711:
+ alc_update_coef_idx(codec, 0x10, 1<<15, 0);
+ break;
+ case 0x10ec0662:
+ if ((coef & 0x00f0) == 0x0030)
+ alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */
+ break;
+ case 0x10ec0272:
+ case 0x10ec0273:
+ case 0x10ec0663:
+ case 0x10ec0665:
+ case 0x10ec0670:
+ case 0x10ec0671:
+ case 0x10ec0672:
+ alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */
+ break;
+ case 0x10ec0222:
+ case 0x10ec0623:
+ alc_update_coef_idx(codec, 0x19, 1<<13, 0);
+ break;
+ case 0x10ec0668:
+ alc_update_coef_idx(codec, 0x7, 3<<13, 0);
+ break;
+ case 0x10ec0867:
+ alc_update_coef_idx(codec, 0x4, 1<<10, 0);
+ break;
+ case 0x10ec0888:
+ if ((coef & 0x00f0) == 0x0020 || (coef & 0x00f0) == 0x0030)
+ alc_update_coef_idx(codec, 0x7, 1<<5, 0);
+ break;
+ case 0x10ec0892:
+ case 0x10ec0897:
+ alc_update_coef_idx(codec, 0x7, 1<<5, 0);
+ break;
+ case 0x10ec0899:
+ case 0x10ec0900:
+ case 0x10ec0b00:
+ case 0x10ec1168:
+ case 0x10ec1220:
+ alc_update_coef_idx(codec, 0x7, 1<<1, 0);
+ break;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_fill_eapd_coef, "SND_HDA_CODEC_REALTEK");
+
+/* turn on/off EAPD control (only if available) */
+static void set_eapd(struct hda_codec *codec, hda_nid_t nid, int on)
+{
+ if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
+ return;
+ if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
+ on ? 2 : 0);
+}
+
+/* turn on/off EAPD controls of the codec */
+void alc_auto_setup_eapd(struct hda_codec *codec, bool on)
+{
+ /* We currently only handle front, HP */
+ static const hda_nid_t pins[] = {
+ 0x0f, 0x10, 0x14, 0x15, 0x17, 0
+ };
+ const hda_nid_t *p;
+ for (p = pins; *p; p++)
+ set_eapd(codec, *p, on);
+}
+EXPORT_SYMBOL_NS_GPL(alc_auto_setup_eapd, "SND_HDA_CODEC_REALTEK");
+
+/* Returns the nid of the external mic input pin, or 0 if it cannot be found. */
+int alc_find_ext_mic_pin(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ hda_nid_t nid;
+ unsigned int defcfg;
+ int i;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ continue;
+ nid = cfg->inputs[i].pin;
+ defcfg = snd_hda_codec_get_pincfg(codec, nid);
+ if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT)
+ continue;
+ return nid;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(alc_find_ext_mic_pin, "SND_HDA_CODEC_REALTEK");
+
+void alc_headset_mic_no_shutup(struct hda_codec *codec)
+{
+ const struct hda_pincfg *pin;
+ int mic_pin = alc_find_ext_mic_pin(codec);
+ int i;
+
+ /* don't shut up pins when unloading the driver; otherwise it breaks
+ * the default pin setup at the next load of the driver
+ */
+ if (codec->bus->shutdown)
+ return;
+
+ snd_array_for_each(&codec->init_pins, i, pin) {
+ /* use read here for syncing after issuing each verb */
+ if (pin->nid != mic_pin)
+ snd_hda_codec_read(codec, pin->nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+ }
+
+ codec->pins_shutup = 1;
+}
+EXPORT_SYMBOL_NS_GPL(alc_headset_mic_no_shutup, "SND_HDA_CODEC_REALTEK");
+
+void alc_shutup_pins(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->no_shutup_pins)
+ return;
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x10ec0257:
+ case 0x19e58326:
+ case 0x10ec0283:
+ case 0x10ec0285:
+ case 0x10ec0286:
+ case 0x10ec0287:
+ case 0x10ec0288:
+ case 0x10ec0295:
+ case 0x10ec0298:
+ alc_headset_mic_no_shutup(codec);
+ break;
+ default:
+ snd_hda_shutup_pins(codec);
+ break;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_shutup_pins, "SND_HDA_CODEC_REALTEK");
+
+/* generic shutup callback;
+ * just turning off EAPD and a little pause for avoiding pop-noise
+ */
+void alc_eapd_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ alc_auto_setup_eapd(codec, false);
+ if (!spec->no_depop_delay)
+ msleep(200);
+ alc_shutup_pins(codec);
+}
+EXPORT_SYMBOL_NS_GPL(alc_eapd_shutup, "SND_HDA_CODEC_REALTEK");
+
+/* additional initialization for ALC888 variants */
+static void alc888_coef_init(struct hda_codec *codec)
+{
+ switch (alc_get_coef0(codec) & 0x00f0) {
+ /* alc888-VA */
+ case 0x00:
+ /* alc888-VB */
+ case 0x10:
+ alc_update_coef_idx(codec, 7, 0, 0x2030); /* Turn EAPD to High */
+ break;
+ }
+}
+
+/* generic EAPD initialization */
+void alc_auto_init_amp(struct hda_codec *codec, int type)
+{
+ alc_auto_setup_eapd(codec, true);
+ alc_write_gpio(codec);
+ switch (type) {
+ case ALC_INIT_DEFAULT:
+ switch (codec->core.vendor_id) {
+ case 0x10ec0260:
+ alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x2010);
+ break;
+ case 0x10ec0880:
+ case 0x10ec0882:
+ case 0x10ec0883:
+ case 0x10ec0885:
+ alc_update_coef_idx(codec, 7, 0, 0x2030);
+ break;
+ case 0x10ec0888:
+ alc888_coef_init(codec);
+ break;
+ }
+ break;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_auto_init_amp, "SND_HDA_CODEC_REALTEK");
+
+/* get a primary headphone pin if available */
+hda_nid_t alc_get_hp_pin(struct alc_spec *spec)
+{
+ if (spec->gen.autocfg.hp_pins[0])
+ return spec->gen.autocfg.hp_pins[0];
+ if (spec->gen.autocfg.line_out_type == AC_JACK_HP_OUT)
+ return spec->gen.autocfg.line_out_pins[0];
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(alc_get_hp_pin, "SND_HDA_CODEC_REALTEK");
+
+/*
+ * Realtek SSID verification
+ */
+
+/* Could be any non-zero and even value. When used as fixup, tells
+ * the driver to ignore any present sku defines.
+ */
+#define ALC_FIXUP_SKU_IGNORE (2)
+
+void alc_fixup_sku_ignore(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->cdefine.fixup = 1;
+ spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_sku_ignore, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_no_depop_delay(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ spec->no_depop_delay = 1;
+ codec->depop_delay = 0;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_no_depop_delay, "SND_HDA_CODEC_REALTEK");
+
+int alc_auto_parse_customize_define(struct hda_codec *codec)
+{
+ unsigned int ass, tmp, i;
+ unsigned nid = 0;
+ struct alc_spec *spec = codec->spec;
+
+ spec->cdefine.enable_pcbeep = 1; /* assume always enabled */
+
+ if (spec->cdefine.fixup) {
+ ass = spec->cdefine.sku_cfg;
+ if (ass == ALC_FIXUP_SKU_IGNORE)
+ return -1;
+ goto do_sku;
+ }
+
+ if (!codec->bus->pci)
+ return -1;
+ ass = codec->core.subsystem_id & 0xffff;
+ if (ass != codec->bus->pci->subsystem_device && (ass & 1))
+ goto do_sku;
+
+ nid = 0x1d;
+ if (codec->core.vendor_id == 0x10ec0260)
+ nid = 0x17;
+ ass = snd_hda_codec_get_pincfg(codec, nid);
+
+ if (!(ass & 1)) {
+ codec_info(codec, "%s: SKU not ready 0x%08x\n",
+ codec->core.chip_name, ass);
+ return -1;
+ }
+
+ /* check sum */
+ tmp = 0;
+ for (i = 1; i < 16; i++) {
+ if ((ass >> i) & 1)
+ tmp++;
+ }
+ if (((ass >> 16) & 0xf) != tmp)
+ return -1;
+
+ spec->cdefine.port_connectivity = ass >> 30;
+ spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20;
+ spec->cdefine.check_sum = (ass >> 16) & 0xf;
+ spec->cdefine.customization = ass >> 8;
+do_sku:
+ spec->cdefine.sku_cfg = ass;
+ spec->cdefine.external_amp = (ass & 0x38) >> 3;
+ spec->cdefine.platform_type = (ass & 0x4) >> 2;
+ spec->cdefine.swap = (ass & 0x2) >> 1;
+ spec->cdefine.override = ass & 0x1;
+
+ codec_dbg(codec, "SKU: Nid=0x%x sku_cfg=0x%08x\n",
+ nid, spec->cdefine.sku_cfg);
+ codec_dbg(codec, "SKU: port_connectivity=0x%x\n",
+ spec->cdefine.port_connectivity);
+ codec_dbg(codec, "SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep);
+ codec_dbg(codec, "SKU: check_sum=0x%08x\n", spec->cdefine.check_sum);
+ codec_dbg(codec, "SKU: customization=0x%08x\n", spec->cdefine.customization);
+ codec_dbg(codec, "SKU: external_amp=0x%x\n", spec->cdefine.external_amp);
+ codec_dbg(codec, "SKU: platform_type=0x%x\n", spec->cdefine.platform_type);
+ codec_dbg(codec, "SKU: swap=0x%x\n", spec->cdefine.swap);
+ codec_dbg(codec, "SKU: override=0x%x\n", spec->cdefine.override);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(alc_auto_parse_customize_define, "SND_HDA_CODEC_REALTEK");
+
+/* return the position of NID in the list, or -1 if not found */
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+ int i;
+ for (i = 0; i < nums; i++)
+ if (list[i] == nid)
+ return i;
+ return -1;
+}
+/* return true if the given NID is found in the list */
+static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+ return find_idx_in_nid_list(nid, list, nums) >= 0;
+}
+
+/* check subsystem ID and set up device-specific initialization;
+ * return 1 if initialized, 0 if invalid SSID
+ */
+/* 32-bit subsystem ID for BIOS loading in HD Audio codec.
+ * 31 ~ 16 : Manufacture ID
+ * 15 ~ 8 : SKU ID
+ * 7 ~ 0 : Assembly ID
+ * port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36
+ */
+int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports)
+{
+ unsigned int ass, tmp, i;
+ unsigned nid;
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->cdefine.fixup) {
+ ass = spec->cdefine.sku_cfg;
+ if (ass == ALC_FIXUP_SKU_IGNORE)
+ return 0;
+ goto do_sku;
+ }
+
+ ass = codec->core.subsystem_id & 0xffff;
+ if (codec->bus->pci &&
+ ass != codec->bus->pci->subsystem_device && (ass & 1))
+ goto do_sku;
+
+ /* invalid SSID, check the special NID pin defcfg instead */
+ /*
+ * 31~30 : port connectivity
+ * 29~21 : reserve
+ * 20 : PCBEEP input
+ * 19~16 : Check sum (15:1)
+ * 15~1 : Custom
+ * 0 : override
+ */
+ nid = 0x1d;
+ if (codec->core.vendor_id == 0x10ec0260)
+ nid = 0x17;
+ ass = snd_hda_codec_get_pincfg(codec, nid);
+ codec_dbg(codec,
+ "realtek: No valid SSID, checking pincfg 0x%08x for NID 0x%x\n",
+ ass, nid);
+ if (!(ass & 1))
+ return 0;
+ if ((ass >> 30) != 1) /* no physical connection */
+ return 0;
+
+ /* check sum */
+ tmp = 0;
+ for (i = 1; i < 16; i++) {
+ if ((ass >> i) & 1)
+ tmp++;
+ }
+ if (((ass >> 16) & 0xf) != tmp)
+ return 0;
+do_sku:
+ codec_dbg(codec, "realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n",
+ ass & 0xffff, codec->core.vendor_id);
+ /*
+ * 0 : override
+ * 1 : Swap Jack
+ * 2 : 0 --> Desktop, 1 --> Laptop
+ * 3~5 : External Amplifier control
+ * 7~6 : Reserved
+ */
+ tmp = (ass & 0x38) >> 3; /* external Amp control */
+ if (spec->init_amp == ALC_INIT_UNDEFINED) {
+ switch (tmp) {
+ case 1:
+ alc_setup_gpio(codec, 0x01);
+ break;
+ case 3:
+ alc_setup_gpio(codec, 0x02);
+ break;
+ case 7:
+ alc_setup_gpio(codec, 0x04);
+ break;
+ case 5:
+ default:
+ spec->init_amp = ALC_INIT_DEFAULT;
+ break;
+ }
+ }
+
+ /* is laptop or Desktop and enable the function "Mute internal speaker
+ * when the external headphone out jack is plugged"
+ */
+ if (!(ass & 0x8000))
+ return 1;
+ /*
+ * 10~8 : Jack location
+ * 12~11: Headphone out -> 00: PortA, 01: PortE, 02: PortD, 03: Resvered
+ * 14~13: Resvered
+ * 15 : 1 --> enable the function "Mute internal speaker
+ * when the external headphone out jack is plugged"
+ */
+ if (!alc_get_hp_pin(spec)) {
+ hda_nid_t nid;
+ tmp = (ass >> 11) & 0x3; /* HP to chassis */
+ nid = ports[tmp];
+ if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins,
+ spec->gen.autocfg.line_outs))
+ return 1;
+ spec->gen.autocfg.hp_pins[0] = nid;
+ }
+ return 1;
+}
+EXPORT_SYMBOL_NS_GPL(alc_subsystem_id, "SND_HDA_CODEC_REALTEK");
+
+/* Check the validity of ALC subsystem-id
+ * ports contains an array of 4 pin NIDs for port-A, E, D and I */
+void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports)
+{
+ if (!alc_subsystem_id(codec, ports)) {
+ struct alc_spec *spec = codec->spec;
+ if (spec->init_amp == ALC_INIT_UNDEFINED) {
+ codec_dbg(codec,
+ "realtek: Enable default setup for auto mode as fallback\n");
+ spec->init_amp = ALC_INIT_DEFAULT;
+ }
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_ssid_check, "SND_HDA_CODEC_REALTEK");
+
+/* inverted digital-mic */
+void alc_fixup_inv_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ spec->gen.inv_dmic_split = 1;
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_inv_dmic, "SND_HDA_CODEC_REALTEK");
+
+int alc_build_controls(struct hda_codec *codec)
+{
+ int err;
+
+ err = snd_hda_gen_build_controls(codec);
+ if (err < 0)
+ return err;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(alc_build_controls, "SND_HDA_CODEC_REALTEK");
+
+int alc_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ /* hibernation resume needs the full chip initialization */
+ if (is_s4_resume(codec))
+ alc_pre_init(codec);
+
+ if (spec->init_hook)
+ spec->init_hook(codec);
+
+ spec->gen.skip_verbs = 1; /* applied in below */
+ snd_hda_gen_init(codec);
+ alc_fix_pll(codec);
+ alc_auto_init_amp(codec, spec->init_amp);
+ snd_hda_apply_verbs(codec); /* apply verbs here after own init */
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(alc_init, "SND_HDA_CODEC_REALTEK");
+
+void alc_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (!snd_hda_get_bool_hint(codec, "shutup"))
+ return; /* disabled explicitly by hints */
+
+ if (spec && spec->shutup)
+ spec->shutup(codec);
+ else
+ alc_shutup_pins(codec);
+}
+EXPORT_SYMBOL_NS_GPL(alc_shutup, "SND_HDA_CODEC_REALTEK");
+
+void alc_power_eapd(struct hda_codec *codec)
+{
+ alc_auto_setup_eapd(codec, false);
+}
+EXPORT_SYMBOL_NS_GPL(alc_power_eapd, "SND_HDA_CODEC_REALTEK");
+
+int alc_suspend(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ alc_shutup(codec);
+ if (spec && spec->power_hook)
+ spec->power_hook(codec);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(alc_suspend, "SND_HDA_CODEC_REALTEK");
+
+int alc_resume(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (!spec->no_depop_delay)
+ msleep(150); /* to avoid pop noise */
+ snd_hda_codec_init(codec);
+ snd_hda_regmap_sync(codec);
+ hda_call_check_power_status(codec, 0x01);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(alc_resume, "SND_HDA_CODEC_REALTEK");
+
+/*
+ * Rename codecs appropriately from COEF value or subvendor id
+ */
+struct alc_codec_rename_table {
+ unsigned int vendor_id;
+ unsigned short coef_mask;
+ unsigned short coef_bits;
+ const char *name;
+};
+
+struct alc_codec_rename_pci_table {
+ unsigned int codec_vendor_id;
+ unsigned short pci_subvendor;
+ unsigned short pci_subdevice;
+ const char *name;
+};
+
+static const struct alc_codec_rename_table rename_tbl[] = {
+ { 0x10ec0221, 0xf00f, 0x1003, "ALC231" },
+ { 0x10ec0269, 0xfff0, 0x3010, "ALC277" },
+ { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" },
+ { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" },
+ { 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" },
+ { 0x10ec0269, 0xffff, 0xa023, "ALC259" },
+ { 0x10ec0269, 0xffff, 0x6023, "ALC281X" },
+ { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" },
+ { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" },
+ { 0x10ec0662, 0xffff, 0x4020, "ALC656" },
+ { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" },
+ { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" },
+ { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" },
+ { 0x10ec0899, 0x2000, 0x2000, "ALC899" },
+ { 0x10ec0892, 0xffff, 0x8020, "ALC661" },
+ { 0x10ec0892, 0xffff, 0x8011, "ALC661" },
+ { 0x10ec0892, 0xffff, 0x4011, "ALC656" },
+ { } /* terminator */
+};
+
+static const struct alc_codec_rename_pci_table rename_pci_tbl[] = {
+ { 0x10ec0280, 0x1028, 0, "ALC3220" },
+ { 0x10ec0282, 0x1028, 0, "ALC3221" },
+ { 0x10ec0283, 0x1028, 0, "ALC3223" },
+ { 0x10ec0288, 0x1028, 0, "ALC3263" },
+ { 0x10ec0292, 0x1028, 0, "ALC3226" },
+ { 0x10ec0293, 0x1028, 0, "ALC3235" },
+ { 0x10ec0255, 0x1028, 0, "ALC3234" },
+ { 0x10ec0668, 0x1028, 0, "ALC3661" },
+ { 0x10ec0275, 0x1028, 0, "ALC3260" },
+ { 0x10ec0899, 0x1028, 0, "ALC3861" },
+ { 0x10ec0298, 0x1028, 0, "ALC3266" },
+ { 0x10ec0236, 0x1028, 0, "ALC3204" },
+ { 0x10ec0256, 0x1028, 0, "ALC3246" },
+ { 0x10ec0225, 0x1028, 0, "ALC3253" },
+ { 0x10ec0295, 0x1028, 0, "ALC3254" },
+ { 0x10ec0299, 0x1028, 0, "ALC3271" },
+ { 0x10ec0670, 0x1025, 0, "ALC669X" },
+ { 0x10ec0676, 0x1025, 0, "ALC679X" },
+ { 0x10ec0282, 0x1043, 0, "ALC3229" },
+ { 0x10ec0233, 0x1043, 0, "ALC3236" },
+ { 0x10ec0280, 0x103c, 0, "ALC3228" },
+ { 0x10ec0282, 0x103c, 0, "ALC3227" },
+ { 0x10ec0286, 0x103c, 0, "ALC3242" },
+ { 0x10ec0290, 0x103c, 0, "ALC3241" },
+ { 0x10ec0668, 0x103c, 0, "ALC3662" },
+ { 0x10ec0283, 0x17aa, 0, "ALC3239" },
+ { 0x10ec0292, 0x17aa, 0, "ALC3232" },
+ { 0x10ec0257, 0x12f0, 0, "ALC3328" },
+ { } /* terminator */
+};
+
+static int alc_codec_rename_from_preset(struct hda_codec *codec)
+{
+ const struct alc_codec_rename_table *p;
+ const struct alc_codec_rename_pci_table *q;
+
+ for (p = rename_tbl; p->vendor_id; p++) {
+ if (p->vendor_id != codec->core.vendor_id)
+ continue;
+ if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits)
+ return alc_codec_rename(codec, p->name);
+ }
+
+ if (!codec->bus->pci)
+ return 0;
+ for (q = rename_pci_tbl; q->codec_vendor_id; q++) {
+ if (q->codec_vendor_id != codec->core.vendor_id)
+ continue;
+ if (q->pci_subvendor != codec->bus->pci->subsystem_vendor)
+ continue;
+ if (!q->pci_subdevice ||
+ q->pci_subdevice == codec->bus->pci->subsystem_device)
+ return alc_codec_rename(codec, q->name);
+ }
+
+ return 0;
+}
+
+/*
+ * Digital-beep handlers
+ */
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+
+/* additional beep mixers; private_value will be overwritten */
+static const struct snd_kcontrol_new alc_beep_mixer[] = {
+ HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT),
+ HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT),
+};
+
+/* set up and create beep controls */
+int alc_set_beep_amp(struct alc_spec *spec, hda_nid_t nid, int idx, int dir)
+{
+ struct snd_kcontrol_new *knew;
+ unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(alc_beep_mixer); i++) {
+ knew = snd_hda_gen_add_kctl(&spec->gen, NULL,
+ &alc_beep_mixer[i]);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = beep_amp;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(alc_set_beep_amp, "SND_HDA_CODEC_REALTEK");
+
+static const struct snd_pci_quirk beep_allow_list[] = {
+ SND_PCI_QUIRK(0x1043, 0x103c, "ASUS", 1),
+ SND_PCI_QUIRK(0x1043, 0x115d, "ASUS", 1),
+ SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1),
+ SND_PCI_QUIRK(0x1043, 0x8376, "EeePC", 1),
+ SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1),
+ SND_PCI_QUIRK(0x1043, 0x831a, "EeePC", 1),
+ SND_PCI_QUIRK(0x1043, 0x834a, "EeePC", 1),
+ SND_PCI_QUIRK(0x1458, 0xa002, "GA-MA790X", 1),
+ SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1),
+ /* denylist -- no beep available */
+ SND_PCI_QUIRK(0x17aa, 0x309e, "Lenovo ThinkCentre M73", 0),
+ SND_PCI_QUIRK(0x17aa, 0x30a3, "Lenovo ThinkCentre M93", 0),
+ {}
+};
+
+int alc_has_cdefine_beep(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ const struct snd_pci_quirk *q;
+ q = snd_pci_quirk_lookup(codec->bus->pci, beep_allow_list);
+ if (q)
+ return q->value;
+ return spec->cdefine.enable_pcbeep;
+}
+EXPORT_SYMBOL_NS_GPL(alc_has_cdefine_beep, "SND_HDA_CODEC_REALTEK");
+
+#endif /* CONFIG_SND_HDA_INPUT_BEEP */
+
+/* parse the BIOS configuration and set up the alc_spec */
+/* return 1 if successful, 0 if the proper config is not found,
+ * or a negative error code
+ */
+int alc_parse_auto_config(struct hda_codec *codec,
+ const hda_nid_t *ignore_nids,
+ const hda_nid_t *ssid_nids)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ int err;
+
+ err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids,
+ spec->parse_flags);
+ if (err < 0)
+ return err;
+
+ if (ssid_nids)
+ alc_ssid_check(codec, ssid_nids);
+
+ err = snd_hda_gen_parse_auto_config(codec, cfg);
+ if (err < 0)
+ return err;
+
+ return 1;
+}
+EXPORT_SYMBOL_NS_GPL(alc_parse_auto_config, "SND_HDA_CODEC_REALTEK");
+
+/* common preparation job for alc_spec */
+int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
+{
+ struct alc_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ int err;
+
+ if (!spec)
+ return -ENOMEM;
+ codec->spec = spec;
+ snd_hda_gen_spec_init(&spec->gen);
+ spec->gen.mixer_nid = mixer_nid;
+ spec->gen.own_eapd_ctl = 1;
+ codec->single_adc_amp = 1;
+ /* FIXME: do we need this for all Realtek codec models? */
+ codec->spdif_status_reset = 1;
+ codec->forced_resume = 1;
+ mutex_init(&spec->coef_mutex);
+
+ err = alc_codec_rename_from_preset(codec);
+ if (err < 0) {
+ kfree(spec);
+ return err;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(alc_alloc_spec, "SND_HDA_CODEC_REALTEK");
+
+/* For dual-codec configuration, we need to disable some features to avoid
+ * conflicts of kctls and PCM streams
+ */
+void alc_fixup_dual_codecs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ /* disable vmaster */
+ spec->gen.suppress_vmaster = 1;
+ /* auto-mute and auto-mic switch don't work with multiple codecs */
+ spec->gen.suppress_auto_mute = 1;
+ spec->gen.suppress_auto_mic = 1;
+ /* disable aamix as well */
+ spec->gen.mixer_nid = 0;
+ /* add location prefix to avoid conflicts */
+ codec->force_pin_prefix = 1;
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_dual_codecs, "SND_HDA_CODEC_REALTEK");
+
+static const struct snd_pcm_chmap_elem asus_pcm_2_1_chmaps[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_NA, SNDRV_CHMAP_LFE } }, /* LFE only on right */
+ { }
+};
+
+/* override the 2.1 chmap */
+void alc_fixup_bass_chmap(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_BUILD) {
+ struct alc_spec *spec = codec->spec;
+ spec->gen.pcm_rec[0]->stream[0].chmap = asus_pcm_2_1_chmaps;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_bass_chmap, "SND_HDA_CODEC_REALTEK");
+
+/* exported as it's used by multiple codecs */
+void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ alc_fixup_dual_codecs(codec, fix, action);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /* override card longname to provide a unique UCM profile */
+ strscpy(codec->card->longname, "HDAudio-Gigabyte-ALC1220DualCodecs");
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ /* rename Capture controls depending on the codec */
+ rename_ctl(codec, "Capture Volume",
+ codec->addr == 0 ?
+ "Rear-Panel Capture Volume" :
+ "Front-Panel Capture Volume");
+ rename_ctl(codec, "Capture Switch",
+ codec->addr == 0 ?
+ "Rear-Panel Capture Switch" :
+ "Front-Panel Capture Switch");
+ break;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc1220_fixup_gb_dual_codecs, "SND_HDA_CODEC_REALTEK");
+
+void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ alc_fixup_dual_codecs(codec, fix, action);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /* override card longname to provide a unique UCM profile */
+ strscpy(codec->card->longname, "HDAudio-Lenovo-DualCodecs");
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ /* rename Capture controls depending on the codec */
+ rename_ctl(codec, "Capture Volume",
+ codec->addr == 0 ?
+ "Rear-Panel Capture Volume" :
+ "Front-Panel Capture Volume");
+ rename_ctl(codec, "Capture Switch",
+ codec->addr == 0 ?
+ "Rear-Panel Capture Switch" :
+ "Front-Panel Capture Switch");
+ break;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc233_alc662_fixup_lenovo_dual_codecs, "SND_HDA_CODEC_REALTEK");
+
+static void alc_shutup_dell_xps13(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int hp_pin = alc_get_hp_pin(spec);
+
+ /* Prevent pop noises when headphones are plugged in */
+ snd_hda_codec_write(codec, hp_pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ msleep(20);
+}
+
+void alc_fixup_dell_xps13(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->gen.input_mux;
+ int i;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /* mic pin 0x19 must be initialized with Vref Hi-Z, otherwise
+ * it causes a click noise at start up
+ */
+ snd_hda_codec_set_pin_target(codec, 0x19, PIN_VREFHIZ);
+ spec->shutup = alc_shutup_dell_xps13;
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ /* Make the internal mic the default input source. */
+ for (i = 0; i < imux->num_items; i++) {
+ if (spec->gen.imux_pins[i] == 0x12) {
+ spec->gen.cur_mux[0] = i;
+ break;
+ }
+ }
+ break;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_dell_xps13, "SND_HDA_CODEC_REALTEK");
+
+/*
+ * headset handling
+ */
+
+static void alc_hp_mute_disable(struct hda_codec *codec, unsigned int delay)
+{
+ if (delay <= 0)
+ delay = 75;
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ msleep(delay);
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+ msleep(delay);
+}
+
+static void alc_hp_enable_unmute(struct hda_codec *codec, unsigned int delay)
+{
+ if (delay <= 0)
+ delay = 75;
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ msleep(delay);
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ msleep(delay);
+}
+
+static const struct coef_fw alc225_pre_hsmode[] = {
+ UPDATE_COEF(0x4a, 1<<8, 0),
+ UPDATE_COEFEX(0x57, 0x05, 1<<14, 0),
+ UPDATE_COEF(0x63, 3<<14, 3<<14),
+ UPDATE_COEF(0x4a, 3<<4, 2<<4),
+ UPDATE_COEF(0x4a, 3<<10, 3<<10),
+ UPDATE_COEF(0x45, 0x3f<<10, 0x34<<10),
+ UPDATE_COEF(0x4a, 3<<10, 0),
+ {}
+};
+
+static void alc_headset_mode_unplugged(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ static const struct coef_fw coef0255[] = {
+ WRITE_COEF(0x1b, 0x0c0b), /* LDO and MISC control */
+ WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */
+ UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/
+ WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */
+ WRITE_COEFEX(0x57, 0x03, 0x8aa6), /* Direct Drive HP Amp control */
+ {}
+ };
+ static const struct coef_fw coef0256[] = {
+ WRITE_COEF(0x1b, 0x0c4b), /* LDO and MISC control */
+ WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */
+ WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */
+ WRITE_COEFEX(0x57, 0x03, 0x09a3), /* Direct Drive HP Amp control */
+ UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/
+ {}
+ };
+ static const struct coef_fw coef0233[] = {
+ WRITE_COEF(0x1b, 0x0c0b),
+ WRITE_COEF(0x45, 0xc429),
+ UPDATE_COEF(0x35, 0x4000, 0),
+ WRITE_COEF(0x06, 0x2104),
+ WRITE_COEF(0x1a, 0x0001),
+ WRITE_COEF(0x26, 0x0004),
+ WRITE_COEF(0x32, 0x42a3),
+ {}
+ };
+ static const struct coef_fw coef0288[] = {
+ UPDATE_COEF(0x4f, 0xfcc0, 0xc400),
+ UPDATE_COEF(0x50, 0x2000, 0x2000),
+ UPDATE_COEF(0x56, 0x0006, 0x0006),
+ UPDATE_COEF(0x66, 0x0008, 0),
+ UPDATE_COEF(0x67, 0x2000, 0),
+ {}
+ };
+ static const struct coef_fw coef0298[] = {
+ UPDATE_COEF(0x19, 0x1300, 0x0300),
+ {}
+ };
+ static const struct coef_fw coef0292[] = {
+ WRITE_COEF(0x76, 0x000e),
+ WRITE_COEF(0x6c, 0x2400),
+ WRITE_COEF(0x18, 0x7308),
+ WRITE_COEF(0x6b, 0xc429),
+ {}
+ };
+ static const struct coef_fw coef0293[] = {
+ UPDATE_COEF(0x10, 7<<8, 6<<8), /* SET Line1 JD to 0 */
+ UPDATE_COEFEX(0x57, 0x05, 1<<15|1<<13, 0x0), /* SET charge pump by verb */
+ UPDATE_COEFEX(0x57, 0x03, 1<<10, 1<<10), /* SET EN_OSW to 1 */
+ UPDATE_COEF(0x1a, 1<<3, 1<<3), /* Combo JD gating with LINE1-VREFO */
+ WRITE_COEF(0x45, 0xc429), /* Set to TRS type */
+ UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */
+ {}
+ };
+ static const struct coef_fw coef0668[] = {
+ WRITE_COEF(0x15, 0x0d40),
+ WRITE_COEF(0xb7, 0x802b),
+ {}
+ };
+ static const struct coef_fw coef0225[] = {
+ UPDATE_COEF(0x63, 3<<14, 0),
+ {}
+ };
+ static const struct coef_fw coef0274[] = {
+ UPDATE_COEF(0x4a, 0x0100, 0),
+ UPDATE_COEFEX(0x57, 0x05, 0x4000, 0),
+ UPDATE_COEF(0x6b, 0xf000, 0x5000),
+ UPDATE_COEF(0x4a, 0x0010, 0),
+ UPDATE_COEF(0x4a, 0x0c00, 0x0c00),
+ WRITE_COEF(0x45, 0x5289),
+ UPDATE_COEF(0x4a, 0x0c00, 0),
+ {}
+ };
+
+ if (spec->no_internal_mic_pin) {
+ alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12);
+ return;
+ }
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0255:
+ alc_process_coef_fw(codec, coef0255);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ alc_hp_mute_disable(codec, 75);
+ alc_process_coef_fw(codec, coef0256);
+ break;
+ case 0x10ec0234:
+ case 0x10ec0274:
+ case 0x10ec0294:
+ alc_process_coef_fw(codec, coef0274);
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ alc_process_coef_fw(codec, coef0233);
+ break;
+ case 0x10ec0286:
+ case 0x10ec0288:
+ alc_process_coef_fw(codec, coef0288);
+ break;
+ case 0x10ec0298:
+ alc_process_coef_fw(codec, coef0298);
+ alc_process_coef_fw(codec, coef0288);
+ break;
+ case 0x10ec0292:
+ alc_process_coef_fw(codec, coef0292);
+ break;
+ case 0x10ec0293:
+ alc_process_coef_fw(codec, coef0293);
+ break;
+ case 0x10ec0668:
+ alc_process_coef_fw(codec, coef0668);
+ break;
+ case 0x10ec0215:
+ case 0x10ec0225:
+ case 0x10ec0285:
+ case 0x10ec0295:
+ case 0x10ec0289:
+ case 0x10ec0299:
+ alc_hp_mute_disable(codec, 75);
+ alc_process_coef_fw(codec, alc225_pre_hsmode);
+ alc_process_coef_fw(codec, coef0225);
+ break;
+ case 0x10ec0867:
+ alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0);
+ break;
+ }
+ codec_dbg(codec, "Headset jack set to unplugged mode.\n");
+}
+
+
+static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
+ hda_nid_t mic_pin)
+{
+ static const struct coef_fw coef0255[] = {
+ WRITE_COEFEX(0x57, 0x03, 0x8aa6),
+ WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */
+ {}
+ };
+ static const struct coef_fw coef0256[] = {
+ UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14), /* Direct Drive HP Amp control(Set to verb control)*/
+ WRITE_COEFEX(0x57, 0x03, 0x09a3),
+ WRITE_COEF(0x06, 0x6100), /* Set MIC2 Vref gate to normal */
+ {}
+ };
+ static const struct coef_fw coef0233[] = {
+ UPDATE_COEF(0x35, 0, 1<<14),
+ WRITE_COEF(0x06, 0x2100),
+ WRITE_COEF(0x1a, 0x0021),
+ WRITE_COEF(0x26, 0x008c),
+ {}
+ };
+ static const struct coef_fw coef0288[] = {
+ UPDATE_COEF(0x4f, 0x00c0, 0),
+ UPDATE_COEF(0x50, 0x2000, 0),
+ UPDATE_COEF(0x56, 0x0006, 0),
+ UPDATE_COEF(0x4f, 0xfcc0, 0xc400),
+ UPDATE_COEF(0x66, 0x0008, 0x0008),
+ UPDATE_COEF(0x67, 0x2000, 0x2000),
+ {}
+ };
+ static const struct coef_fw coef0292[] = {
+ WRITE_COEF(0x19, 0xa208),
+ WRITE_COEF(0x2e, 0xacf0),
+ {}
+ };
+ static const struct coef_fw coef0293[] = {
+ UPDATE_COEFEX(0x57, 0x05, 0, 1<<15|1<<13), /* SET charge pump by verb */
+ UPDATE_COEFEX(0x57, 0x03, 1<<10, 0), /* SET EN_OSW to 0 */
+ UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */
+ {}
+ };
+ static const struct coef_fw coef0688[] = {
+ WRITE_COEF(0xb7, 0x802b),
+ WRITE_COEF(0xb5, 0x1040),
+ UPDATE_COEF(0xc3, 0, 1<<12),
+ {}
+ };
+ static const struct coef_fw coef0225[] = {
+ UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14),
+ UPDATE_COEF(0x4a, 3<<4, 2<<4),
+ UPDATE_COEF(0x63, 3<<14, 0),
+ {}
+ };
+ static const struct coef_fw coef0274[] = {
+ UPDATE_COEFEX(0x57, 0x05, 0x4000, 0x4000),
+ UPDATE_COEF(0x4a, 0x0010, 0),
+ UPDATE_COEF(0x6b, 0xf000, 0),
+ {}
+ };
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0255:
+ alc_write_coef_idx(codec, 0x45, 0xc489);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0255);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ alc_write_coef_idx(codec, 0x45, 0xc489);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0256);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0234:
+ case 0x10ec0274:
+ case 0x10ec0294:
+ alc_write_coef_idx(codec, 0x45, 0x4689);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0274);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ alc_write_coef_idx(codec, 0x45, 0xc429);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0233);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0286:
+ case 0x10ec0288:
+ case 0x10ec0298:
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0288);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0292:
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0292);
+ break;
+ case 0x10ec0293:
+ /* Set to TRS mode */
+ alc_write_coef_idx(codec, 0x45, 0xc429);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0293);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0867:
+ alc_update_coefex_idx(codec, 0x57, 0x5, 0, 1<<14);
+ fallthrough;
+ case 0x10ec0221:
+ case 0x10ec0662:
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0668:
+ alc_write_coef_idx(codec, 0x11, 0x0001);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0688);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ case 0x10ec0215:
+ case 0x10ec0225:
+ case 0x10ec0285:
+ case 0x10ec0295:
+ case 0x10ec0289:
+ case 0x10ec0299:
+ alc_process_coef_fw(codec, alc225_pre_hsmode);
+ alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x31<<10);
+ snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
+ alc_process_coef_fw(codec, coef0225);
+ snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
+ break;
+ }
+ codec_dbg(codec, "Headset jack set to mic-in mode.\n");
+}
+
+static void alc_headset_mode_default(struct hda_codec *codec)
+{
+ static const struct coef_fw coef0225[] = {
+ UPDATE_COEF(0x45, 0x3f<<10, 0x30<<10),
+ UPDATE_COEF(0x45, 0x3f<<10, 0x31<<10),
+ UPDATE_COEF(0x49, 3<<8, 0<<8),
+ UPDATE_COEF(0x4a, 3<<4, 3<<4),
+ UPDATE_COEF(0x63, 3<<14, 0),
+ UPDATE_COEF(0x67, 0xf000, 0x3000),
+ {}
+ };
+ static const struct coef_fw coef0255[] = {
+ WRITE_COEF(0x45, 0xc089),
+ WRITE_COEF(0x45, 0xc489),
+ WRITE_COEFEX(0x57, 0x03, 0x8ea6),
+ WRITE_COEF(0x49, 0x0049),
+ {}
+ };
+ static const struct coef_fw coef0256[] = {
+ WRITE_COEF(0x45, 0xc489),
+ WRITE_COEFEX(0x57, 0x03, 0x0da3),
+ WRITE_COEF(0x49, 0x0049),
+ UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/
+ WRITE_COEF(0x06, 0x6100),
+ {}
+ };
+ static const struct coef_fw coef0233[] = {
+ WRITE_COEF(0x06, 0x2100),
+ WRITE_COEF(0x32, 0x4ea3),
+ {}
+ };
+ static const struct coef_fw coef0288[] = {
+ UPDATE_COEF(0x4f, 0xfcc0, 0xc400), /* Set to TRS type */
+ UPDATE_COEF(0x50, 0x2000, 0x2000),
+ UPDATE_COEF(0x56, 0x0006, 0x0006),
+ UPDATE_COEF(0x66, 0x0008, 0),
+ UPDATE_COEF(0x67, 0x2000, 0),
+ {}
+ };
+ static const struct coef_fw coef0292[] = {
+ WRITE_COEF(0x76, 0x000e),
+ WRITE_COEF(0x6c, 0x2400),
+ WRITE_COEF(0x6b, 0xc429),
+ WRITE_COEF(0x18, 0x7308),
+ {}
+ };
+ static const struct coef_fw coef0293[] = {
+ UPDATE_COEF(0x4a, 0x000f, 0x000e), /* Combo Jack auto detect */
+ WRITE_COEF(0x45, 0xC429), /* Set to TRS type */
+ UPDATE_COEF(0x1a, 1<<3, 0), /* Combo JD gating without LINE1-VREFO */
+ {}
+ };
+ static const struct coef_fw coef0688[] = {
+ WRITE_COEF(0x11, 0x0041),
+ WRITE_COEF(0x15, 0x0d40),
+ WRITE_COEF(0xb7, 0x802b),
+ {}
+ };
+ static const struct coef_fw coef0274[] = {
+ WRITE_COEF(0x45, 0x4289),
+ UPDATE_COEF(0x4a, 0x0010, 0x0010),
+ UPDATE_COEF(0x6b, 0x0f00, 0),
+ UPDATE_COEF(0x49, 0x0300, 0x0300),
+ {}
+ };
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0215:
+ case 0x10ec0225:
+ case 0x10ec0285:
+ case 0x10ec0295:
+ case 0x10ec0289:
+ case 0x10ec0299:
+ alc_process_coef_fw(codec, alc225_pre_hsmode);
+ alc_process_coef_fw(codec, coef0225);
+ alc_hp_enable_unmute(codec, 75);
+ break;
+ case 0x10ec0255:
+ alc_process_coef_fw(codec, coef0255);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ alc_write_coef_idx(codec, 0x1b, 0x0e4b);
+ alc_write_coef_idx(codec, 0x45, 0xc089);
+ msleep(50);
+ alc_process_coef_fw(codec, coef0256);
+ alc_hp_enable_unmute(codec, 75);
+ break;
+ case 0x10ec0234:
+ case 0x10ec0274:
+ case 0x10ec0294:
+ alc_process_coef_fw(codec, coef0274);
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ alc_process_coef_fw(codec, coef0233);
+ break;
+ case 0x10ec0286:
+ case 0x10ec0288:
+ case 0x10ec0298:
+ alc_process_coef_fw(codec, coef0288);
+ break;
+ case 0x10ec0292:
+ alc_process_coef_fw(codec, coef0292);
+ break;
+ case 0x10ec0293:
+ alc_process_coef_fw(codec, coef0293);
+ break;
+ case 0x10ec0668:
+ alc_process_coef_fw(codec, coef0688);
+ break;
+ case 0x10ec0867:
+ alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0);
+ break;
+ }
+ codec_dbg(codec, "Headset jack set to headphone (default) mode.\n");
+}
+
+/* Iphone type */
+static void alc_headset_mode_ctia(struct hda_codec *codec)
+{
+ int val;
+
+ static const struct coef_fw coef0255[] = {
+ WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */
+ WRITE_COEF(0x1b, 0x0c2b),
+ WRITE_COEFEX(0x57, 0x03, 0x8ea6),
+ {}
+ };
+ static const struct coef_fw coef0256[] = {
+ WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */
+ WRITE_COEF(0x1b, 0x0e6b),
+ {}
+ };
+ static const struct coef_fw coef0233[] = {
+ WRITE_COEF(0x45, 0xd429),
+ WRITE_COEF(0x1b, 0x0c2b),
+ WRITE_COEF(0x32, 0x4ea3),
+ {}
+ };
+ static const struct coef_fw coef0288[] = {
+ UPDATE_COEF(0x50, 0x2000, 0x2000),
+ UPDATE_COEF(0x56, 0x0006, 0x0006),
+ UPDATE_COEF(0x66, 0x0008, 0),
+ UPDATE_COEF(0x67, 0x2000, 0),
+ {}
+ };
+ static const struct coef_fw coef0292[] = {
+ WRITE_COEF(0x6b, 0xd429),
+ WRITE_COEF(0x76, 0x0008),
+ WRITE_COEF(0x18, 0x7388),
+ {}
+ };
+ static const struct coef_fw coef0293[] = {
+ WRITE_COEF(0x45, 0xd429), /* Set to ctia type */
+ UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */
+ {}
+ };
+ static const struct coef_fw coef0688[] = {
+ WRITE_COEF(0x11, 0x0001),
+ WRITE_COEF(0x15, 0x0d60),
+ WRITE_COEF(0xc3, 0x0000),
+ {}
+ };
+ static const struct coef_fw coef0225_1[] = {
+ UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10),
+ UPDATE_COEF(0x63, 3<<14, 2<<14),
+ {}
+ };
+ static const struct coef_fw coef0225_2[] = {
+ UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10),
+ UPDATE_COEF(0x63, 3<<14, 1<<14),
+ {}
+ };
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0255:
+ alc_process_coef_fw(codec, coef0255);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ alc_process_coef_fw(codec, coef0256);
+ alc_hp_enable_unmute(codec, 75);
+ break;
+ case 0x10ec0234:
+ case 0x10ec0274:
+ case 0x10ec0294:
+ alc_write_coef_idx(codec, 0x45, 0xd689);
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ alc_process_coef_fw(codec, coef0233);
+ break;
+ case 0x10ec0298:
+ val = alc_read_coef_idx(codec, 0x50);
+ if (val & (1 << 12)) {
+ alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0020);
+ alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400);
+ msleep(300);
+ } else {
+ alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010);
+ alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400);
+ msleep(300);
+ }
+ break;
+ case 0x10ec0286:
+ case 0x10ec0288:
+ alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400);
+ msleep(300);
+ alc_process_coef_fw(codec, coef0288);
+ break;
+ case 0x10ec0292:
+ alc_process_coef_fw(codec, coef0292);
+ break;
+ case 0x10ec0293:
+ alc_process_coef_fw(codec, coef0293);
+ break;
+ case 0x10ec0668:
+ alc_process_coef_fw(codec, coef0688);
+ break;
+ case 0x10ec0215:
+ case 0x10ec0225:
+ case 0x10ec0285:
+ case 0x10ec0295:
+ case 0x10ec0289:
+ case 0x10ec0299:
+ val = alc_read_coef_idx(codec, 0x45);
+ if (val & (1 << 9))
+ alc_process_coef_fw(codec, coef0225_2);
+ else
+ alc_process_coef_fw(codec, coef0225_1);
+ alc_hp_enable_unmute(codec, 75);
+ break;
+ case 0x10ec0867:
+ alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0);
+ break;
+ }
+ codec_dbg(codec, "Headset jack set to iPhone-style headset mode.\n");
+}
+
+/* Nokia type */
+static void alc_headset_mode_omtp(struct hda_codec *codec)
+{
+ static const struct coef_fw coef0255[] = {
+ WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */
+ WRITE_COEF(0x1b, 0x0c2b),
+ WRITE_COEFEX(0x57, 0x03, 0x8ea6),
+ {}
+ };
+ static const struct coef_fw coef0256[] = {
+ WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */
+ WRITE_COEF(0x1b, 0x0e6b),
+ {}
+ };
+ static const struct coef_fw coef0233[] = {
+ WRITE_COEF(0x45, 0xe429),
+ WRITE_COEF(0x1b, 0x0c2b),
+ WRITE_COEF(0x32, 0x4ea3),
+ {}
+ };
+ static const struct coef_fw coef0288[] = {
+ UPDATE_COEF(0x50, 0x2000, 0x2000),
+ UPDATE_COEF(0x56, 0x0006, 0x0006),
+ UPDATE_COEF(0x66, 0x0008, 0),
+ UPDATE_COEF(0x67, 0x2000, 0),
+ {}
+ };
+ static const struct coef_fw coef0292[] = {
+ WRITE_COEF(0x6b, 0xe429),
+ WRITE_COEF(0x76, 0x0008),
+ WRITE_COEF(0x18, 0x7388),
+ {}
+ };
+ static const struct coef_fw coef0293[] = {
+ WRITE_COEF(0x45, 0xe429), /* Set to omtp type */
+ UPDATE_COEF(0x10, 7<<8, 7<<8), /* SET Line1 JD to 1 */
+ {}
+ };
+ static const struct coef_fw coef0688[] = {
+ WRITE_COEF(0x11, 0x0001),
+ WRITE_COEF(0x15, 0x0d50),
+ WRITE_COEF(0xc3, 0x0000),
+ {}
+ };
+ static const struct coef_fw coef0225[] = {
+ UPDATE_COEF(0x45, 0x3f<<10, 0x39<<10),
+ UPDATE_COEF(0x63, 3<<14, 2<<14),
+ {}
+ };
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0255:
+ alc_process_coef_fw(codec, coef0255);
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ alc_process_coef_fw(codec, coef0256);
+ alc_hp_enable_unmute(codec, 75);
+ break;
+ case 0x10ec0234:
+ case 0x10ec0274:
+ case 0x10ec0294:
+ alc_write_coef_idx(codec, 0x45, 0xe689);
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ alc_process_coef_fw(codec, coef0233);
+ break;
+ case 0x10ec0298:
+ alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010);/* Headset output enable */
+ alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xe400);
+ msleep(300);
+ break;
+ case 0x10ec0286:
+ case 0x10ec0288:
+ alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xe400);
+ msleep(300);
+ alc_process_coef_fw(codec, coef0288);
+ break;
+ case 0x10ec0292:
+ alc_process_coef_fw(codec, coef0292);
+ break;
+ case 0x10ec0293:
+ alc_process_coef_fw(codec, coef0293);
+ break;
+ case 0x10ec0668:
+ alc_process_coef_fw(codec, coef0688);
+ break;
+ case 0x10ec0215:
+ case 0x10ec0225:
+ case 0x10ec0285:
+ case 0x10ec0295:
+ case 0x10ec0289:
+ case 0x10ec0299:
+ alc_process_coef_fw(codec, coef0225);
+ alc_hp_enable_unmute(codec, 75);
+ break;
+ }
+ codec_dbg(codec, "Headset jack set to Nokia-style headset mode.\n");
+}
+
+static void alc_determine_headset_type(struct hda_codec *codec)
+{
+ int val;
+ bool is_ctia = false;
+ struct alc_spec *spec = codec->spec;
+ static const struct coef_fw coef0255[] = {
+ WRITE_COEF(0x45, 0xd089), /* combo jack auto switch control(Check type)*/
+ WRITE_COEF(0x49, 0x0149), /* combo jack auto switch control(Vref
+ conteol) */
+ {}
+ };
+ static const struct coef_fw coef0288[] = {
+ UPDATE_COEF(0x4f, 0xfcc0, 0xd400), /* Check Type */
+ {}
+ };
+ static const struct coef_fw coef0298[] = {
+ UPDATE_COEF(0x50, 0x2000, 0x2000),
+ UPDATE_COEF(0x56, 0x0006, 0x0006),
+ UPDATE_COEF(0x66, 0x0008, 0),
+ UPDATE_COEF(0x67, 0x2000, 0),
+ UPDATE_COEF(0x19, 0x1300, 0x1300),
+ {}
+ };
+ static const struct coef_fw coef0293[] = {
+ UPDATE_COEF(0x4a, 0x000f, 0x0008), /* Combo Jack auto detect */
+ WRITE_COEF(0x45, 0xD429), /* Set to ctia type */
+ {}
+ };
+ static const struct coef_fw coef0688[] = {
+ WRITE_COEF(0x11, 0x0001),
+ WRITE_COEF(0xb7, 0x802b),
+ WRITE_COEF(0x15, 0x0d60),
+ WRITE_COEF(0xc3, 0x0c00),
+ {}
+ };
+ static const struct coef_fw coef0274[] = {
+ UPDATE_COEF(0x4a, 0x0010, 0),
+ UPDATE_COEF(0x4a, 0x8000, 0),
+ WRITE_COEF(0x45, 0xd289),
+ UPDATE_COEF(0x49, 0x0300, 0x0300),
+ {}
+ };
+
+ if (spec->no_internal_mic_pin) {
+ alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12);
+ return;
+ }
+
+ switch (codec->core.vendor_id) {
+ case 0x10ec0255:
+ alc_process_coef_fw(codec, coef0255);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x0070) == 0x0070;
+ break;
+ case 0x10ec0230:
+ case 0x10ec0236:
+ case 0x10ec0256:
+ case 0x19e58326:
+ alc_write_coef_idx(codec, 0x1b, 0x0e4b);
+ alc_write_coef_idx(codec, 0x06, 0x6104);
+ alc_write_coefex_idx(codec, 0x57, 0x3, 0x09a3);
+
+ alc_process_coef_fw(codec, coef0255);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x0070) == 0x0070;
+ if (!is_ctia) {
+ alc_write_coef_idx(codec, 0x45, 0xe089);
+ msleep(100);
+ val = alc_read_coef_idx(codec, 0x46);
+ if ((val & 0x0070) == 0x0070)
+ is_ctia = false;
+ else
+ is_ctia = true;
+ }
+ alc_write_coefex_idx(codec, 0x57, 0x3, 0x0da3);
+ alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0);
+ break;
+ case 0x10ec0234:
+ case 0x10ec0274:
+ case 0x10ec0294:
+ alc_process_coef_fw(codec, coef0274);
+ msleep(850);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x00f0) == 0x00f0;
+ break;
+ case 0x10ec0233:
+ case 0x10ec0283:
+ alc_write_coef_idx(codec, 0x45, 0xd029);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x0070) == 0x0070;
+ break;
+ case 0x10ec0298:
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ msleep(100);
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+ msleep(200);
+
+ val = alc_read_coef_idx(codec, 0x50);
+ if (val & (1 << 12)) {
+ alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0020);
+ alc_process_coef_fw(codec, coef0288);
+ msleep(350);
+ val = alc_read_coef_idx(codec, 0x50);
+ is_ctia = (val & 0x0070) == 0x0070;
+ } else {
+ alc_update_coef_idx(codec, 0x8e, 0x0070, 0x0010);
+ alc_process_coef_fw(codec, coef0288);
+ msleep(350);
+ val = alc_read_coef_idx(codec, 0x50);
+ is_ctia = (val & 0x0070) == 0x0070;
+ }
+ alc_process_coef_fw(codec, coef0298);
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+ msleep(75);
+ snd_hda_codec_write(codec, 0x21, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ break;
+ case 0x10ec0286:
+ case 0x10ec0288:
+ alc_process_coef_fw(codec, coef0288);
+ msleep(350);
+ val = alc_read_coef_idx(codec, 0x50);
+ is_ctia = (val & 0x0070) == 0x0070;
+ break;
+ case 0x10ec0292:
+ alc_write_coef_idx(codec, 0x6b, 0xd429);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0x6c);
+ is_ctia = (val & 0x001c) == 0x001c;
+ break;
+ case 0x10ec0293:
+ alc_process_coef_fw(codec, coef0293);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x0070) == 0x0070;
+ break;
+ case 0x10ec0668:
+ alc_process_coef_fw(codec, coef0688);
+ msleep(300);
+ val = alc_read_coef_idx(codec, 0xbe);
+ is_ctia = (val & 0x1c02) == 0x1c02;
+ break;
+ case 0x10ec0215:
+ case 0x10ec0225:
+ case 0x10ec0285:
+ case 0x10ec0295:
+ case 0x10ec0289:
+ case 0x10ec0299:
+ alc_process_coef_fw(codec, alc225_pre_hsmode);
+ alc_update_coef_idx(codec, 0x67, 0xf000, 0x1000);
+ val = alc_read_coef_idx(codec, 0x45);
+ if (val & (1 << 9)) {
+ alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x34<<10);
+ alc_update_coef_idx(codec, 0x49, 3<<8, 2<<8);
+ msleep(800);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x00f0) == 0x00f0;
+ } else {
+ alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x34<<10);
+ alc_update_coef_idx(codec, 0x49, 3<<8, 1<<8);
+ msleep(800);
+ val = alc_read_coef_idx(codec, 0x46);
+ is_ctia = (val & 0x00f0) == 0x00f0;
+ }
+ if (!is_ctia) {
+ alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x38<<10);
+ alc_update_coef_idx(codec, 0x49, 3<<8, 1<<8);
+ msleep(100);
+ val = alc_read_coef_idx(codec, 0x46);
+ if ((val & 0x00f0) == 0x00f0)
+ is_ctia = false;
+ else
+ is_ctia = true;
+ }
+ alc_update_coef_idx(codec, 0x4a, 7<<6, 7<<6);
+ alc_update_coef_idx(codec, 0x4a, 3<<4, 3<<4);
+ alc_update_coef_idx(codec, 0x67, 0xf000, 0x3000);
+ break;
+ case 0x10ec0867:
+ is_ctia = true;
+ break;
+ }
+
+ codec_dbg(codec, "Headset jack detected iPhone-style headset: %s\n",
+ str_yes_no(is_ctia));
+ spec->current_headset_type = is_ctia ? ALC_HEADSET_TYPE_CTIA : ALC_HEADSET_TYPE_OMTP;
+}
+
+static void alc_update_headset_mode(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]];
+ hda_nid_t hp_pin = alc_get_hp_pin(spec);
+
+ int new_headset_mode;
+
+ if (!snd_hda_jack_detect(codec, hp_pin))
+ new_headset_mode = ALC_HEADSET_MODE_UNPLUGGED;
+ else if (mux_pin == spec->headset_mic_pin)
+ new_headset_mode = ALC_HEADSET_MODE_HEADSET;
+ else if (mux_pin == spec->headphone_mic_pin)
+ new_headset_mode = ALC_HEADSET_MODE_MIC;
+ else
+ new_headset_mode = ALC_HEADSET_MODE_HEADPHONE;
+
+ if (new_headset_mode == spec->current_headset_mode) {
+ snd_hda_gen_update_outputs(codec);
+ return;
+ }
+
+ switch (new_headset_mode) {
+ case ALC_HEADSET_MODE_UNPLUGGED:
+ alc_headset_mode_unplugged(codec);
+ spec->current_headset_mode = ALC_HEADSET_MODE_UNKNOWN;
+ spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN;
+ spec->gen.hp_jack_present = false;
+ break;
+ case ALC_HEADSET_MODE_HEADSET:
+ if (spec->current_headset_type == ALC_HEADSET_TYPE_UNKNOWN)
+ alc_determine_headset_type(codec);
+ if (spec->current_headset_type == ALC_HEADSET_TYPE_CTIA)
+ alc_headset_mode_ctia(codec);
+ else if (spec->current_headset_type == ALC_HEADSET_TYPE_OMTP)
+ alc_headset_mode_omtp(codec);
+ spec->gen.hp_jack_present = true;
+ break;
+ case ALC_HEADSET_MODE_MIC:
+ alc_headset_mode_mic_in(codec, hp_pin, spec->headphone_mic_pin);
+ spec->gen.hp_jack_present = false;
+ break;
+ case ALC_HEADSET_MODE_HEADPHONE:
+ alc_headset_mode_default(codec);
+ spec->gen.hp_jack_present = true;
+ break;
+ }
+ if (new_headset_mode != ALC_HEADSET_MODE_MIC) {
+ snd_hda_set_pin_ctl_cache(codec, hp_pin,
+ AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+ if (spec->headphone_mic_pin && spec->headphone_mic_pin != hp_pin)
+ snd_hda_set_pin_ctl_cache(codec, spec->headphone_mic_pin,
+ PIN_VREFHIZ);
+ }
+ spec->current_headset_mode = new_headset_mode;
+
+ snd_hda_gen_update_outputs(codec);
+}
+
+static void alc_update_headset_mode_hook(struct hda_codec *codec,
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ alc_update_headset_mode(codec);
+}
+
+void alc_update_headset_jack_cb(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ snd_hda_gen_hp_automute(codec, jack);
+ alc_update_headset_mode(codec);
+}
+EXPORT_SYMBOL_NS_GPL(alc_update_headset_jack_cb, "SND_HDA_CODEC_REALTEK");
+
+static void alc_probe_headset_mode(struct hda_codec *codec)
+{
+ int i;
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+
+ /* Find mic pins */
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].is_headset_mic && !spec->headset_mic_pin)
+ spec->headset_mic_pin = cfg->inputs[i].pin;
+ if (cfg->inputs[i].is_headphone_mic && !spec->headphone_mic_pin)
+ spec->headphone_mic_pin = cfg->inputs[i].pin;
+ }
+
+ WARN_ON(spec->gen.cap_sync_hook);
+ spec->gen.cap_sync_hook = alc_update_headset_mode_hook;
+ spec->gen.automute_hook = alc_update_headset_mode;
+ spec->gen.hp_automute_hook = alc_update_headset_jack_cb;
+}
+
+void alc_fixup_headset_mode(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC | HDA_PINCFG_HEADPHONE_MIC;
+ break;
+ case HDA_FIXUP_ACT_PROBE:
+ alc_probe_headset_mode(codec);
+ break;
+ case HDA_FIXUP_ACT_INIT:
+ if (is_s3_resume(codec) || is_s4_resume(codec)) {
+ spec->current_headset_mode = ALC_HEADSET_MODE_UNKNOWN;
+ spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN;
+ }
+ alc_update_headset_mode(codec);
+ break;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_headset_mode, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+ }
+ else
+ alc_fixup_headset_mode(codec, fix, action);
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_headset_mode_no_hp_mic, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->parse_flags |= HDA_PINCFG_HEADSET_MIC;
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_headset_mic, "SND_HDA_CODEC_REALTEK");
+
+/* update LED status via GPIO */
+void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask,
+ int polarity, bool enabled)
+{
+ if (polarity)
+ enabled = !enabled;
+ alc_update_gpio_data(codec, mask, !enabled); /* muted -> LED on */
+}
+EXPORT_SYMBOL_NS_GPL(alc_update_gpio_led, "SND_HDA_CODEC_REALTEK");
+
+/* turn on/off mic-mute LED via GPIO per capture hook */
+static int micmute_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
+ struct alc_spec *spec = codec->spec;
+
+ alc_update_gpio_led(codec, spec->gpio_mic_led_mask,
+ spec->micmute_led_polarity, !brightness);
+ return 0;
+}
+
+/* turn on/off mute LED via GPIO per vmaster hook */
+static int gpio_mute_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
+ struct alc_spec *spec = codec->spec;
+
+ alc_update_gpio_led(codec, spec->gpio_mute_led_mask,
+ spec->mute_led_polarity, !brightness);
+ return 0;
+}
+
+/* setup mute and mic-mute GPIO bits, add hooks appropriately */
+void alc_fixup_hp_gpio_led(struct hda_codec *codec,
+ int action,
+ unsigned int mute_mask,
+ unsigned int micmute_mask)
+{
+ struct alc_spec *spec = codec->spec;
+
+ alc_fixup_gpio(codec, action, mute_mask | micmute_mask);
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ if (mute_mask) {
+ spec->gpio_mute_led_mask = mute_mask;
+ snd_hda_gen_add_mute_led_cdev(codec, gpio_mute_led_set);
+ }
+ if (micmute_mask) {
+ spec->gpio_mic_led_mask = micmute_mask;
+ snd_hda_gen_add_micmute_led_cdev(codec, micmute_led_set);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_hp_gpio_led, "SND_HDA_CODEC_REALTEK");
+
+/* suppress the jack-detection */
+void alc_fixup_no_jack_detect(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ codec->no_jack_detect = 1;
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_no_jack_detect, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_disable_aamix(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ /* Disable AA-loopback as it causes white noise */
+ spec->gen.mixer_nid = 0;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_disable_aamix, "SND_HDA_CODEC_REALTEK");
+
+void alc_fixup_auto_mute_via_amp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->gen.auto_mute_via_amp = 1;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(alc_fixup_auto_mute_via_amp, "SND_HDA_CODEC_REALTEK");
+
+MODULE_IMPORT_NS("SND_HDA_SCODEC_COMPONENT");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek HD-audio codec helper");
diff --git a/sound/hda/codecs/realtek/realtek.h b/sound/hda/codecs/realtek/realtek.h
new file mode 100644
index 000000000000..b2a919904c4c
--- /dev/null
+++ b/sound/hda/codecs/realtek/realtek.h
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Realtek HD-audio codec support code
+//
+
+#ifndef __HDA_REALTEK_H
+#define __HDA_REALTEK_H
+
+#include <linux/acpi.h>
+#include <linux/cleanup.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/leds.h>
+#include <linux/ctype.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_beep.h"
+#include "hda_jack.h"
+#include "../generic.h"
+#include "../side-codecs/hda_component.h"
+
+/* extra amp-initialization sequence types */
+enum {
+ ALC_INIT_UNDEFINED,
+ ALC_INIT_NONE,
+ ALC_INIT_DEFAULT,
+};
+
+enum {
+ ALC_HEADSET_MODE_UNKNOWN,
+ ALC_HEADSET_MODE_UNPLUGGED,
+ ALC_HEADSET_MODE_HEADSET,
+ ALC_HEADSET_MODE_MIC,
+ ALC_HEADSET_MODE_HEADPHONE,
+};
+
+enum {
+ ALC_HEADSET_TYPE_UNKNOWN,
+ ALC_HEADSET_TYPE_CTIA,
+ ALC_HEADSET_TYPE_OMTP,
+};
+
+enum {
+ ALC_KEY_MICMUTE_INDEX,
+};
+
+struct alc_customize_define {
+ unsigned int sku_cfg;
+ unsigned char port_connectivity;
+ unsigned char check_sum;
+ unsigned char customization;
+ unsigned char external_amp;
+ unsigned int enable_pcbeep:1;
+ unsigned int platform_type:1;
+ unsigned int swap:1;
+ unsigned int override:1;
+ unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */
+};
+
+struct alc_coef_led {
+ unsigned int idx;
+ unsigned int mask;
+ unsigned int on;
+ unsigned int off;
+};
+
+struct alc_spec {
+ struct hda_gen_spec gen; /* must be at head */
+
+ /* codec parameterization */
+ struct alc_customize_define cdefine;
+ unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
+
+ /* GPIO bits */
+ unsigned int gpio_mask;
+ unsigned int gpio_dir;
+ unsigned int gpio_data;
+ bool gpio_write_delay; /* add a delay before writing gpio_data */
+
+ /* mute LED for HP laptops, see vref_mute_led_set() */
+ int mute_led_polarity;
+ int micmute_led_polarity;
+ hda_nid_t mute_led_nid;
+ hda_nid_t cap_mute_led_nid;
+
+ unsigned int gpio_mute_led_mask;
+ unsigned int gpio_mic_led_mask;
+ struct alc_coef_led mute_led_coef;
+ struct alc_coef_led mic_led_coef;
+ struct mutex coef_mutex;
+
+ hda_nid_t headset_mic_pin;
+ hda_nid_t headphone_mic_pin;
+ int current_headset_mode;
+ int current_headset_type;
+
+ /* hooks */
+ void (*init_hook)(struct hda_codec *codec);
+ void (*power_hook)(struct hda_codec *codec);
+ void (*shutup)(struct hda_codec *codec);
+
+ int init_amp;
+ int codec_variant; /* flag for other variants */
+ unsigned int has_alc5505_dsp:1;
+ unsigned int no_depop_delay:1;
+ unsigned int done_hp_init:1;
+ unsigned int no_shutup_pins:1;
+ unsigned int ultra_low_power:1;
+ unsigned int has_hs_key:1;
+ unsigned int no_internal_mic_pin:1;
+ unsigned int en_3kpull_low:1;
+ int num_speaker_amps;
+
+ /* for PLL fix */
+ hda_nid_t pll_nid;
+ unsigned int pll_coef_idx, pll_coef_bit;
+ unsigned int coef0;
+ struct input_dev *kb_dev;
+ u8 alc_mute_keycode_map[1];
+
+ /* component binding */
+ struct hda_component_parent comps;
+};
+
+int alc_read_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx);
+void alc_write_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int coef_val);
+void alc_update_coefex_idx(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int mask,
+ unsigned int bits_set);
+#define alc_read_coef_idx(codec, coef_idx) \
+ alc_read_coefex_idx(codec, 0x20, coef_idx)
+#define alc_write_coef_idx(codec, coef_idx, coef_val) \
+ alc_write_coefex_idx(codec, 0x20, coef_idx, coef_val)
+#define alc_update_coef_idx(codec, coef_idx, mask, bits_set) \
+ alc_update_coefex_idx(codec, 0x20, coef_idx, mask, bits_set)
+
+unsigned int alc_get_coef0(struct hda_codec *codec);
+
+/* coef writes/updates batch */
+struct coef_fw {
+ unsigned char nid;
+ unsigned char idx;
+ unsigned short mask;
+ unsigned short val;
+};
+
+#define UPDATE_COEFEX(_nid, _idx, _mask, _val) \
+ { .nid = (_nid), .idx = (_idx), .mask = (_mask), .val = (_val) }
+#define WRITE_COEFEX(_nid, _idx, _val) UPDATE_COEFEX(_nid, _idx, -1, _val)
+#define WRITE_COEF(_idx, _val) WRITE_COEFEX(0x20, _idx, _val)
+#define UPDATE_COEF(_idx, _mask, _val) UPDATE_COEFEX(0x20, _idx, _mask, _val)
+
+void alc_process_coef_fw(struct hda_codec *codec, const struct coef_fw *fw);
+
+/*
+ * GPIO helpers
+ */
+void alc_setup_gpio(struct hda_codec *codec, unsigned int mask);
+void alc_write_gpio_data(struct hda_codec *codec);
+void alc_update_gpio_data(struct hda_codec *codec, unsigned int mask,
+ bool on);
+void alc_write_gpio(struct hda_codec *codec);
+
+/* common GPIO fixups */
+void alc_fixup_gpio(struct hda_codec *codec, int action, unsigned int mask);
+void alc_fixup_gpio1(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_gpio2(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_gpio3(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_gpio4(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_micmute_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+
+/*
+ * Common init code, callbacks and helpers
+ */
+void alc_fix_pll(struct hda_codec *codec);
+void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int coef_idx, unsigned int coef_bit);
+void alc_fill_eapd_coef(struct hda_codec *codec);
+void alc_auto_setup_eapd(struct hda_codec *codec, bool on);
+
+int alc_find_ext_mic_pin(struct hda_codec *codec);
+void alc_headset_mic_no_shutup(struct hda_codec *codec);
+void alc_shutup_pins(struct hda_codec *codec);
+void alc_eapd_shutup(struct hda_codec *codec);
+void alc_auto_init_amp(struct hda_codec *codec, int type);
+hda_nid_t alc_get_hp_pin(struct alc_spec *spec);
+int alc_auto_parse_customize_define(struct hda_codec *codec);
+int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports);
+void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports);
+int alc_build_controls(struct hda_codec *codec);
+void alc_update_knob_master(struct hda_codec *codec,
+ struct hda_jack_callback *jack);
+
+static inline void alc_pre_init(struct hda_codec *codec)
+{
+ alc_fill_eapd_coef(codec);
+}
+
+#define is_s3_resume(codec) \
+ ((codec)->core.dev.power.power_state.event == PM_EVENT_RESUME)
+#define is_s4_resume(codec) \
+ ((codec)->core.dev.power.power_state.event == PM_EVENT_RESTORE)
+#define is_s4_suspend(codec) \
+ ((codec)->core.dev.power.power_state.event == PM_EVENT_FREEZE)
+
+int alc_init(struct hda_codec *codec);
+void alc_shutup(struct hda_codec *codec);
+void alc_power_eapd(struct hda_codec *codec);
+int alc_suspend(struct hda_codec *codec);
+int alc_resume(struct hda_codec *codec);
+
+int alc_parse_auto_config(struct hda_codec *codec,
+ const hda_nid_t *ignore_nids,
+ const hda_nid_t *ssid_nids);
+int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid);
+
+#define alc_codec_rename(codec, name) snd_hda_codec_set_name(codec, name)
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+int alc_set_beep_amp(struct alc_spec *spec, hda_nid_t nid, int idx, int dir);
+int alc_has_cdefine_beep(struct hda_codec *codec);
+#define set_beep_amp alc_set_beep_amp
+#define has_cdefine_beep alc_has_cdefine_beep
+#else
+#define set_beep_amp(spec, nid, idx, dir) 0
+#define has_cdefine_beep(codec) 0
+#endif
+
+static inline void rename_ctl(struct hda_codec *codec, const char *oldname,
+ const char *newname)
+{
+ struct snd_kcontrol *kctl;
+
+ kctl = snd_hda_find_mixer_ctl(codec, oldname);
+ if (kctl)
+ snd_ctl_rename(codec->card, kctl, newname);
+}
+
+/* Common fixups */
+void alc_fixup_sku_ignore(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_no_depop_delay(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_inv_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_dual_codecs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_bass_chmap(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_headset_mode(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_update_headset_jack_cb(struct hda_codec *codec,
+ struct hda_jack_callback *jack);
+void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask,
+ int polarity, bool enabled);
+void alc_fixup_hp_gpio_led(struct hda_codec *codec,
+ int action,
+ unsigned int mute_mask,
+ unsigned int micmute_mask);
+void alc_fixup_no_jack_detect(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_disable_aamix(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+void alc_fixup_auto_mute_via_amp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+
+/* device-specific, but used by multiple codec drivers */
+void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action);
+void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action);
+void alc_fixup_dell_xps13(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action);
+
+/*
+ * COEF access helper functions
+ */
+static inline void coef_mutex_lock(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ snd_hda_power_up_pm(codec);
+ mutex_lock(&spec->coef_mutex);
+}
+
+static inline void coef_mutex_unlock(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ mutex_unlock(&spec->coef_mutex);
+ snd_hda_power_down_pm(codec);
+}
+
+DEFINE_GUARD(coef_mutex, struct hda_codec *, coef_mutex_lock(_T), coef_mutex_unlock(_T))
+
+#endif /* __HDA_REALTEK_H */
diff --git a/sound/hda/codecs/senarytech.c b/sound/hda/codecs/senarytech.c
new file mode 100644
index 000000000000..63cda57cf786
--- /dev/null
+++ b/sound/hda/codecs/senarytech.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HD audio codec driver for Senary HDA audio codec
+ *
+ * Initially based on conexant.c
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_beep.h"
+#include "hda_jack.h"
+#include "generic.h"
+
+/* GPIO node ID */
+#define SENARY_GPIO_NODE 0x01
+
+struct senary_spec {
+ struct hda_gen_spec gen;
+
+ /* extra EAPD pins */
+ unsigned int num_eapds;
+ hda_nid_t eapds[4];
+ hda_nid_t mute_led_eapd;
+
+ unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
+
+ int mute_led_polarity;
+ unsigned int gpio_led;
+ unsigned int gpio_mute_led_mask;
+ unsigned int gpio_mic_led_mask;
+};
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/* additional beep mixers; private_value will be overwritten */
+static const struct snd_kcontrol_new senary_beep_mixer[] = {
+ HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
+};
+
+static int set_beep_amp(struct senary_spec *spec, hda_nid_t nid,
+ int idx, int dir)
+{
+ struct snd_kcontrol_new *knew;
+ unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
+ int i;
+
+ spec->gen.beep_nid = nid;
+ for (i = 0; i < ARRAY_SIZE(senary_beep_mixer); i++) {
+ knew = snd_hda_gen_add_kctl(&spec->gen, NULL,
+ &senary_beep_mixer[i]);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = beep_amp;
+ }
+ return 0;
+}
+
+static int senary_auto_parse_beep(struct hda_codec *codec)
+{
+ struct senary_spec *spec = codec->spec;
+ hda_nid_t nid;
+
+ for_each_hda_codec_node(nid, codec)
+ if ((get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) &&
+ (get_wcaps(codec, nid) & (AC_WCAP_OUT_AMP | AC_WCAP_AMP_OVRD)))
+ return set_beep_amp(spec, nid, 0, HDA_OUTPUT);
+ return 0;
+}
+#else
+#define senary_auto_parse_beep(codec) 0
+#endif
+
+/* parse EAPDs */
+static void senary_auto_parse_eapd(struct hda_codec *codec)
+{
+ struct senary_spec *spec = codec->spec;
+ hda_nid_t nid;
+
+ for_each_hda_codec_node(nid, codec) {
+ if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
+ continue;
+ if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD))
+ continue;
+ spec->eapds[spec->num_eapds++] = nid;
+ if (spec->num_eapds >= ARRAY_SIZE(spec->eapds))
+ break;
+ }
+}
+
+static void senary_auto_turn_eapd(struct hda_codec *codec, int num_pins,
+ const hda_nid_t *pins, bool on)
+{
+ int i;
+
+ for (i = 0; i < num_pins; i++) {
+ if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD)
+ snd_hda_codec_write(codec, pins[i], 0,
+ AC_VERB_SET_EAPD_BTLENABLE,
+ on ? 0x02 : 0);
+ }
+}
+
+/* turn on/off EAPD according to Master switch */
+static void senary_auto_vmaster_hook(void *private_data, int enabled)
+{
+ struct hda_codec *codec = private_data;
+ struct senary_spec *spec = codec->spec;
+
+ senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled);
+}
+
+static void senary_init_gpio_led(struct hda_codec *codec)
+{
+ struct senary_spec *spec = codec->spec;
+ unsigned int mask = spec->gpio_mute_led_mask | spec->gpio_mic_led_mask;
+
+ if (mask) {
+ snd_hda_codec_write(codec, SENARY_GPIO_NODE, 0, AC_VERB_SET_GPIO_MASK,
+ mask);
+ snd_hda_codec_write(codec, SENARY_GPIO_NODE, 0, AC_VERB_SET_GPIO_DIRECTION,
+ mask);
+ snd_hda_codec_write(codec, SENARY_GPIO_NODE, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_led);
+ }
+}
+
+static int senary_init(struct hda_codec *codec)
+{
+ snd_hda_gen_init(codec);
+ senary_init_gpio_led(codec);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
+
+ return 0;
+}
+
+static void senary_shutdown(struct hda_codec *codec)
+{
+ struct senary_spec *spec = codec->spec;
+
+ /* Turn the problematic codec into D3 to avoid spurious noises
+ * from the internal speaker during (and after) reboot
+ */
+ senary_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false);
+}
+
+static void senary_remove(struct hda_codec *codec)
+{
+ senary_shutdown(codec);
+ snd_hda_gen_remove(codec);
+}
+
+static int senary_suspend(struct hda_codec *codec)
+{
+ senary_shutdown(codec);
+ return 0;
+}
+
+static int senary_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct senary_spec *spec;
+ int err;
+
+ codec_info(codec, "%s: BIOS auto-probing.\n", codec->core.chip_name);
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ snd_hda_gen_spec_init(&spec->gen);
+ codec->spec = spec;
+
+ senary_auto_parse_eapd(codec);
+ spec->gen.own_eapd_ctl = 1;
+
+ if (!spec->gen.vmaster_mute.hook)
+ spec->gen.vmaster_mute.hook = senary_auto_vmaster_hook;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL,
+ spec->parse_flags);
+ if (err < 0)
+ goto error;
+
+ err = senary_auto_parse_beep(codec);
+ if (err < 0)
+ goto error;
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
+ if (err < 0)
+ goto error;
+
+ /* Some laptops with Senary chips show stalls in S3 resume,
+ * which falls into the single-cmd mode.
+ * Better to make reset, then.
+ */
+ if (!codec->bus->core.sync_write) {
+ codec_info(codec,
+ "Enable sync_write for stable communication\n");
+ codec->bus->core.sync_write = 1;
+ codec->bus->allow_bus_reset = 1;
+ }
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+
+ error:
+ senary_remove(codec);
+ return err;
+}
+
+static const struct hda_codec_ops senary_codec_ops = {
+ .probe = senary_probe,
+ .remove = senary_remove,
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = senary_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = senary_suspend,
+ .check_power_status = snd_hda_gen_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ */
+
+static const struct hda_device_id snd_hda_id_senary[] = {
+ HDA_CODEC_ID(0x1fa86186, "SN6186"),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_senary);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Senarytech HD-audio codec");
+
+static struct hda_codec_driver senary_driver = {
+ .id = snd_hda_id_senary,
+ .ops = &senary_codec_ops,
+};
+
+module_hda_codec_driver(senary_driver);
diff --git a/sound/pci/hda/patch_si3054.c b/sound/hda/codecs/si3054.c
index f419ee8d75f0..87cf9da9f3bf 100644
--- a/sound/pci/hda/patch_si3054.c
+++ b/sound/hda/codecs/si3054.c
@@ -1,32 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Universal Interface for Intel High Definition Audio Codec
*
- * HD audio interface patch for Silicon Labs 3054/5 modem codec
+ * HD audio codec driver for Silicon Labs 3054/5 modem codec
*
* Copyright (c) 2005 Sasha Khapyorsky <sashak@alsa-project.org>
* Takashi Iwai <tiwai@suse.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <sound/core.h>
-#include "hda_codec.h"
+#include <sound/hda_codec.h>
#include "hda_local.h"
/* si3054 verbs */
@@ -82,7 +69,6 @@
struct si3054_spec {
unsigned international;
- struct hda_pcm pcm;
};
@@ -130,7 +116,7 @@ static int si3054_switch_put(struct snd_kcontrol *kcontrol,
}
-static struct snd_kcontrol_new si3054_modem_mixer[] = {
+static const struct snd_kcontrol_new si3054_modem_mixer[] = {
SI3054_KCONTROL("Off-hook Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_OH),
SI3054_KCONTROL("Caller ID Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_CID),
{}
@@ -169,8 +155,8 @@ static int si3054_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
- static unsigned int rates[] = { 8000, 9600, 16000 };
- static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+ static const unsigned int rates[] = { 8000, 9600, 16000 };
+ static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
@@ -181,7 +167,7 @@ static int si3054_pcm_open(struct hda_pcm_stream *hinfo,
}
-static struct hda_pcm_stream si3054_pcm = {
+static const struct hda_pcm_stream si3054_pcm = {
.substreams = 1,
.channels_min = 1,
.channels_max = 1,
@@ -198,14 +184,15 @@ static struct hda_pcm_stream si3054_pcm = {
static int si3054_build_pcms(struct hda_codec *codec)
{
- struct si3054_spec *spec = codec->spec;
- struct hda_pcm *info = &spec->pcm;
- si3054_pcm.nid = codec->mfg;
- codec->num_pcms = 1;
- codec->pcm_info = info;
- info->name = "Si3054 Modem";
+ struct hda_pcm *info;
+
+ info = snd_hda_codec_pcm_new(codec, "Si3054 Modem");
+ if (!info)
+ return -ENOMEM;
info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm;
info->stream[SNDRV_PCM_STREAM_CAPTURE] = si3054_pcm;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = codec->core.mfg;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = codec->core.mfg;
info->pcm_type = HDA_PCM_TYPE_MODEM;
return 0;
}
@@ -221,8 +208,12 @@ static int si3054_init(struct hda_codec *codec)
unsigned wait_count;
u16 val;
+ if (snd_hdac_regmap_add_vendor_verb(&codec->core,
+ SI3054_VERB_WRITE_NODE))
+ return -ENOMEM;
+
snd_hda_codec_write(codec, AC_NODE_ROOT, 0, AC_VERB_SET_CODEC_RESET, 0);
- snd_hda_codec_write(codec, codec->mfg, 0, AC_VERB_SET_STREAM_FORMAT, 0);
+ snd_hda_codec_write(codec, codec->core.mfg, 0, AC_VERB_SET_STREAM_FORMAT, 0);
SET_REG(codec, SI3054_LINE_RATE, 9600);
SET_REG(codec, SI3054_LINE_LEVEL, SI3054_DTAG_MASK|SI3054_ATAG_MASK);
SET_REG(codec, SI3054_EXTENDED_MID, 0);
@@ -234,7 +225,7 @@ static int si3054_init(struct hda_codec *codec)
} while ((val & SI3054_MEI_READY) != SI3054_MEI_READY && wait_count--);
if((val&SI3054_MEI_READY) != SI3054_MEI_READY) {
- snd_printk(KERN_ERR "si3054: cannot initialize. EXT MID = %04x\n", val);
+ codec_err(codec, "si3054: cannot initialize. EXT MID = %04x\n", val);
/* let's pray that this is no fatal error */
/* return -EACCES; */
}
@@ -245,7 +236,8 @@ static int si3054_init(struct hda_codec *codec)
SET_REG(codec, SI3054_LINE_CFG1,0x200);
if((GET_REG(codec,SI3054_LINE_STATUS) & (1<<6)) == 0) {
- snd_printd("Link Frame Detect(FDT) is not ready (line status: %04x)\n",
+ codec_dbg(codec,
+ "Link Frame Detect(FDT) is not ready (line status: %04x)\n",
GET_REG(codec,SI3054_LINE_STATUS));
}
@@ -254,82 +246,58 @@ static int si3054_init(struct hda_codec *codec)
return 0;
}
-static void si3054_free(struct hda_codec *codec)
+static void si3054_remove(struct hda_codec *codec)
{
kfree(codec->spec);
}
-
/*
*/
-static struct hda_codec_ops si3054_patch_ops = {
- .build_controls = si3054_build_controls,
- .build_pcms = si3054_build_pcms,
- .init = si3054_init,
- .free = si3054_free,
-};
-
-static int patch_si3054(struct hda_codec *codec)
+static int si3054_probe(struct hda_codec *codec, const struct hda_device_id *id)
{
- struct si3054_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
+ codec->spec = kzalloc(sizeof(struct si3054_spec), GFP_KERNEL);
+ if (!codec->spec)
return -ENOMEM;
- codec->spec = spec;
- codec->patch_ops = si3054_patch_ops;
return 0;
}
+static const struct hda_codec_ops si3054_codec_ops = {
+ .probe = si3054_probe,
+ .remove = si3054_remove,
+ .build_controls = si3054_build_controls,
+ .build_pcms = si3054_build_pcms,
+ .init = si3054_init,
+};
+
/*
- * patch entries
+ * driver entries
*/
-static struct hda_codec_preset snd_hda_preset_si3054[] = {
- { .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x11c13055, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x11c13155, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x10573155, .name = "Si3054", .patch = patch_si3054 },
+static const struct hda_device_id snd_hda_id_si3054[] = {
+ HDA_CODEC_ID(0x163c3055, "Si3054"),
+ HDA_CODEC_ID(0x163c3155, "Si3054"),
+ HDA_CODEC_ID(0x11c13026, "Si3054"),
+ HDA_CODEC_ID(0x11c13055, "Si3054"),
+ HDA_CODEC_ID(0x11c13155, "Si3054"),
+ HDA_CODEC_ID(0x10573055, "Si3054"),
+ HDA_CODEC_ID(0x10573057, "Si3054"),
+ HDA_CODEC_ID(0x10573155, "Si3054"),
/* VIA HDA on Clevo m540 */
- { .id = 0x11063288, .name = "Si3054", .patch = patch_si3054 },
+ HDA_CODEC_ID(0x11063288, "Si3054"),
/* Asus A8J Modem (SM56) */
- { .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 },
+ HDA_CODEC_ID(0x15433155, "Si3054"),
/* LG LW20 modem */
- { .id = 0x18540018, .name = "Si3054", .patch = patch_si3054 },
+ HDA_CODEC_ID(0x18540018, "Si3054"),
{}
};
-
-MODULE_ALIAS("snd-hda-codec-id:163c3055");
-MODULE_ALIAS("snd-hda-codec-id:163c3155");
-MODULE_ALIAS("snd-hda-codec-id:11c13026");
-MODULE_ALIAS("snd-hda-codec-id:11c13055");
-MODULE_ALIAS("snd-hda-codec-id:11c13155");
-MODULE_ALIAS("snd-hda-codec-id:10573055");
-MODULE_ALIAS("snd-hda-codec-id:10573057");
-MODULE_ALIAS("snd-hda-codec-id:10573155");
-MODULE_ALIAS("snd-hda-codec-id:11063288");
-MODULE_ALIAS("snd-hda-codec-id:15433155");
-MODULE_ALIAS("snd-hda-codec-id:18540018");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_si3054);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Si3054 HD-audio modem codec");
-static struct hda_codec_preset_list si3054_list = {
- .preset = snd_hda_preset_si3054,
- .owner = THIS_MODULE,
+static struct hda_codec_driver si3054_driver = {
+ .id = snd_hda_id_si3054,
+ .ops = &si3054_codec_ops,
};
-static int __init patch_si3054_init(void)
-{
- return snd_hda_add_codec_preset(&si3054_list);
-}
-
-static void __exit patch_si3054_exit(void)
-{
- snd_hda_delete_codec_preset(&si3054_list);
-}
-
-module_init(patch_si3054_init)
-module_exit(patch_si3054_exit)
+module_hda_codec_driver(si3054_driver);
diff --git a/sound/hda/codecs/side-codecs/Kconfig b/sound/hda/codecs/side-codecs/Kconfig
new file mode 100644
index 000000000000..f674e9a9c7d7
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/Kconfig
@@ -0,0 +1,143 @@
+config SND_HDA_CIRRUS_SCODEC
+ tristate
+
+config SND_HDA_CIRRUS_SCODEC_KUNIT_TEST
+ tristate "KUnit test for Cirrus side-codec library" if !KUNIT_ALL_TESTS
+ depends on SND_HDA_CIRRUS_SCODEC && GPIOLIB && KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds KUnit tests for the cirrus side-codec library.
+ For more information on KUnit and unit tests in general,
+ please refer to the KUnit documentation in
+ Documentation/dev-tools/kunit/.
+ If in doubt, say "N".
+
+config SND_HDA_SCODEC_CS35L41
+ tristate
+ select SND_HDA_GENERIC
+ select REGMAP_IRQ
+ select FW_CS_DSP
+
+config SND_HDA_SCODEC_COMPONENT
+ tristate
+
+config SND_HDA_SCODEC_CS35L41_I2C
+ tristate "Build CS35L41 HD-audio side codec support for I2C Bus"
+ depends on I2C
+ depends on ACPI
+ depends on EFI
+ depends on SND_SOC
+ select SND_SOC_CS35L41_LIB
+ select SND_HDA_SCODEC_CS35L41
+ select SND_SOC_CS_AMP_LIB
+ help
+ Say Y or M here to include CS35L41 I2C HD-audio side codec support
+ in snd-hda-intel driver, such as ALC287.
+
+comment "Set to Y if you want auto-loading the side codec driver"
+ depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_I2C=m
+
+config SND_HDA_SCODEC_CS35L41_SPI
+ tristate "Build CS35L41 HD-audio codec support for SPI Bus"
+ depends on SPI_MASTER
+ depends on ACPI
+ depends on EFI
+ depends on SND_SOC
+ select SND_SOC_CS35L41_LIB
+ select SND_HDA_SCODEC_CS35L41
+ select SND_SOC_CS_AMP_LIB
+ help
+ Say Y or M here to include CS35L41 SPI HD-audio side codec support
+ in snd-hda-intel driver, such as ALC287.
+
+comment "Set to Y if you want auto-loading the side codec driver"
+ depends on SND_HDA=y && SND_HDA_SCODEC_CS35L41_SPI=m
+
+config SND_HDA_SCODEC_CS35L56
+ tristate
+
+config SND_HDA_SCODEC_CS35L56_I2C
+ tristate "Build CS35L56 HD-audio side codec support for I2C Bus"
+ depends on I2C
+ depends on ACPI
+ depends on SND_SOC
+ select FW_CS_DSP
+ imply SERIAL_MULTI_INSTANTIATE
+ select SND_HDA_GENERIC
+ select SND_SOC_CS35L56_SHARED
+ select SND_HDA_SCODEC_CS35L56
+ select SND_HDA_CIRRUS_SCODEC
+ select SND_SOC_CS_AMP_LIB
+ help
+ Say Y or M here to include CS35L56 amplifier support with
+ I2C control.
+
+config SND_HDA_SCODEC_CS35L56_SPI
+ tristate "Build CS35L56 HD-audio side codec support for SPI Bus"
+ depends on SPI_MASTER
+ depends on ACPI
+ depends on SND_SOC
+ select FW_CS_DSP
+ imply SERIAL_MULTI_INSTANTIATE
+ select SND_HDA_GENERIC
+ select SND_SOC_CS35L56_SHARED
+ select SND_HDA_SCODEC_CS35L56
+ select SND_HDA_CIRRUS_SCODEC
+ select SND_SOC_CS_AMP_LIB
+ help
+ Say Y or M here to include CS35L56 amplifier support with
+ SPI control.
+
+menu "CS35L56 driver options"
+ depends on SND_HDA_SCODEC_CS35L56
+
+config SND_HDA_SCODEC_CS35L56_CAL_DEBUGFS
+ bool "CS35L56 create debugfs for factory calibration"
+ default N
+ depends on DEBUG_FS
+ select SND_SOC_CS35L56_CAL_DEBUGFS_COMMON
+ help
+ Create debugfs entries used during factory-line manufacture
+ for factory calibration.
+
+ If unsure select "N".
+endmenu
+
+config SND_HDA_SCODEC_TAS2781
+ tristate
+ select SND_HDA_GENERIC
+
+config SND_HDA_SCODEC_TAS2781_I2C
+ tristate "Build TAS2781 HD-audio side codec support for I2C Bus"
+ depends on I2C
+ depends on ACPI
+ depends on EFI
+ depends on SND_SOC
+ select SND_HDA_SCODEC_TAS2781
+ select SND_SOC_TAS2781_COMLIB_I2C
+ select SND_SOC_TAS2781_FMWLIB
+ select CRC32
+ help
+ Say Y or M here to include TAS2781 I2C HD-audio side codec support
+ in snd-hda-intel driver, such as ALC287.
+
+comment "Set to Y if you want auto-loading the side codec driver"
+ depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_I2C=m
+
+config SND_HDA_SCODEC_TAS2781_SPI
+ tristate "Build TAS2781 HD-audio side codec support for SPI Bus"
+ depends on SPI_MASTER
+ depends on ACPI
+ depends on EFI
+ depends on SND_SOC
+ select SND_HDA_SCODEC_TAS2781
+ select SND_SOC_TAS2781_COMLIB
+ select SND_SOC_TAS2781_FMWLIB
+ select CRC8
+ select CRC32
+ help
+ Say Y or M here to include TAS2781 SPI HD-audio side codec support
+ in snd-hda-intel driver, such as ALC287.
+
+comment "Set to Y if you want auto-loading the side codec driver"
+ depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_SPI=m
diff --git a/sound/hda/codecs/side-codecs/Makefile b/sound/hda/codecs/side-codecs/Makefile
new file mode 100644
index 000000000000..245e84f6a121
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/Makefile
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0
+subdir-ccflags-y += -I$(src)/../../common
+
+snd-hda-cirrus-scodec-y := cirrus_scodec.o
+snd-hda-cirrus-scodec-test-y := cirrus_scodec_test.o
+snd-hda-scodec-cs35l41-y := cs35l41_hda.o cs35l41_hda_property.o
+snd-hda-scodec-cs35l41-i2c-y := cs35l41_hda_i2c.o
+snd-hda-scodec-cs35l41-spi-y := cs35l41_hda_spi.o
+snd-hda-scodec-cs35l56-y := cs35l56_hda.o
+snd-hda-scodec-cs35l56-i2c-y := cs35l56_hda_i2c.o
+snd-hda-scodec-cs35l56-spi-y := cs35l56_hda_spi.o
+snd-hda-scodec-component-y := hda_component.o
+snd-hda-scodec-tas2781-y := tas2781_hda.o
+snd-hda-scodec-tas2781-i2c-y := tas2781_hda_i2c.o
+snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o
+
+obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC) += snd-hda-cirrus-scodec.o
+obj-$(CONFIG_SND_HDA_CIRRUS_SCODEC_KUNIT_TEST) += snd-hda-cirrus-scodec-test.o
+obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o
+obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o
+obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o
+obj-$(CONFIG_SND_HDA_SCODEC_CS35L56) += snd-hda-scodec-cs35l56.o
+obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_I2C) += snd-hda-scodec-cs35l56-i2c.o
+obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o
+obj-$(CONFIG_SND_HDA_SCODEC_COMPONENT) += snd-hda-scodec-component.o
+obj-$(CONFIG_SND_HDA_SCODEC_TAS2781) += snd-hda-scodec-tas2781.o
+obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o
+obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_SPI) += snd-hda-scodec-tas2781-spi.o
diff --git a/sound/hda/codecs/side-codecs/cirrus_scodec.c b/sound/hda/codecs/side-codecs/cirrus_scodec.c
new file mode 100644
index 000000000000..3c670207ba30
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cirrus_scodec.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Common code for Cirrus side-codecs.
+//
+// Copyright (C) 2021, 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/dev_printk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+
+#include "cirrus_scodec.h"
+
+int cirrus_scodec_get_speaker_id(struct device *dev, int amp_index,
+ int num_amps, int fixed_gpio_id)
+{
+ struct gpio_desc *speaker_id_desc;
+ int speaker_id = -ENOENT;
+
+ if (fixed_gpio_id >= 0) {
+ dev_dbg(dev, "Found Fixed Speaker ID GPIO (index = %d)\n", fixed_gpio_id);
+ speaker_id_desc = gpiod_get_index(dev, NULL, fixed_gpio_id, GPIOD_IN);
+ if (IS_ERR(speaker_id_desc)) {
+ speaker_id = PTR_ERR(speaker_id_desc);
+ return speaker_id;
+ }
+ speaker_id = gpiod_get_value_cansleep(speaker_id_desc);
+ gpiod_put(speaker_id_desc);
+ } else {
+ int base_index;
+ int gpios_per_amp;
+ int count;
+ int tmp;
+ int i;
+
+ count = gpiod_count(dev, "spk-id");
+ if (count > 0) {
+ speaker_id = 0;
+ gpios_per_amp = count / num_amps;
+ base_index = gpios_per_amp * amp_index;
+
+ if (count % num_amps)
+ return -EINVAL;
+
+ dev_dbg(dev, "Found %d Speaker ID GPIOs per Amp\n", gpios_per_amp);
+
+ for (i = 0; i < gpios_per_amp; i++) {
+ speaker_id_desc = gpiod_get_index(dev, "spk-id", i + base_index,
+ GPIOD_IN);
+ if (IS_ERR(speaker_id_desc)) {
+ speaker_id = PTR_ERR(speaker_id_desc);
+ break;
+ }
+ tmp = gpiod_get_value_cansleep(speaker_id_desc);
+ gpiod_put(speaker_id_desc);
+ if (tmp < 0) {
+ speaker_id = tmp;
+ break;
+ }
+ speaker_id |= tmp << i;
+ }
+ }
+ }
+
+ dev_dbg(dev, "Speaker ID = %d\n", speaker_id);
+
+ return speaker_id;
+}
+EXPORT_SYMBOL_NS_GPL(cirrus_scodec_get_speaker_id, "SND_HDA_CIRRUS_SCODEC");
+
+MODULE_DESCRIPTION("HDA Cirrus side-codec library");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/hda/codecs/side-codecs/cirrus_scodec.h b/sound/hda/codecs/side-codecs/cirrus_scodec.h
new file mode 100644
index 000000000000..ba2041d8ef24
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cirrus_scodec.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2023 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef CIRRUS_SCODEC_H
+#define CIRRUS_SCODEC_H
+
+int cirrus_scodec_get_speaker_id(struct device *dev, int amp_index,
+ int num_amps, int fixed_gpio_id);
+
+#endif /* CIRRUS_SCODEC_H */
diff --git a/sound/hda/codecs/side-codecs/cirrus_scodec_test.c b/sound/hda/codecs/side-codecs/cirrus_scodec_test.c
new file mode 100644
index 000000000000..3cca750857b6
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cirrus_scodec_test.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// KUnit test for the Cirrus side-codec library.
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <kunit/platform_device.h>
+#include <kunit/resource.h>
+#include <kunit/test.h>
+#include <linux/device.h>
+#include <linux/device/faux.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "cirrus_scodec.h"
+
+KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,
+ struct faux_device *)
+KUNIT_DEFINE_ACTION_WRAPPER(device_remove_software_node_wrapper,
+ device_remove_software_node,
+ struct device *)
+
+struct cirrus_scodec_test_gpio {
+ unsigned int pin_state;
+ struct gpio_chip chip;
+};
+
+struct cirrus_scodec_test_priv {
+ struct faux_device *amp_dev;
+ struct platform_device *gpio_pdev;
+ struct cirrus_scodec_test_gpio *gpio_priv;
+};
+
+static int cirrus_scodec_test_gpio_get_direction(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ return GPIO_LINE_DIRECTION_IN;
+}
+
+static int cirrus_scodec_test_gpio_direction_in(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ return 0;
+}
+
+static int cirrus_scodec_test_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct cirrus_scodec_test_gpio *gpio_priv = gpiochip_get_data(chip);
+
+ return !!(gpio_priv->pin_state & BIT(offset));
+}
+
+static int cirrus_scodec_test_gpio_direction_out(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ return -EOPNOTSUPP;
+}
+
+static int cirrus_scodec_test_gpio_set(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ return -EOPNOTSUPP;
+}
+
+static int cirrus_scodec_test_gpio_set_config(struct gpio_chip *gc,
+ unsigned int offset,
+ unsigned long config)
+{
+ switch (pinconf_to_config_param(config)) {
+ case PIN_CONFIG_LEVEL:
+ case PIN_CONFIG_OUTPUT_ENABLE:
+ return -EOPNOTSUPP;
+ default:
+ return 0;
+ }
+}
+
+static const struct gpio_chip cirrus_scodec_test_gpio_chip = {
+ .label = "cirrus_scodec_test_gpio",
+ .owner = THIS_MODULE,
+ .request = gpiochip_generic_request,
+ .free = gpiochip_generic_free,
+ .get_direction = cirrus_scodec_test_gpio_get_direction,
+ .direction_input = cirrus_scodec_test_gpio_direction_in,
+ .get = cirrus_scodec_test_gpio_get,
+ .direction_output = cirrus_scodec_test_gpio_direction_out,
+ .set = cirrus_scodec_test_gpio_set,
+ .set_config = cirrus_scodec_test_gpio_set_config,
+ .base = -1,
+ .ngpio = 32,
+};
+
+static int cirrus_scodec_test_gpio_probe(struct platform_device *pdev)
+{
+ struct cirrus_scodec_test_gpio *gpio_priv;
+ int ret;
+
+ gpio_priv = devm_kzalloc(&pdev->dev, sizeof(*gpio_priv), GFP_KERNEL);
+ if (!gpio_priv)
+ return -ENOMEM;
+
+ /* GPIO core modifies our struct gpio_chip so use a copy */
+ gpio_priv->chip = cirrus_scodec_test_gpio_chip;
+ ret = devm_gpiochip_add_data(&pdev->dev, &gpio_priv->chip, gpio_priv);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to add gpiochip\n");
+
+ dev_set_drvdata(&pdev->dev, gpio_priv);
+
+ return 0;
+}
+
+static struct platform_driver cirrus_scodec_test_gpio_driver = {
+ .driver.name = "cirrus_scodec_test_gpio_drv",
+ .driver.owner = THIS_MODULE,
+ .probe = cirrus_scodec_test_gpio_probe,
+};
+
+/* software_node referencing the gpio driver */
+static const struct software_node cirrus_scodec_test_gpio_swnode = {
+ .name = "cirrus_scodec_test_gpio",
+};
+
+static void cirrus_scodec_test_create_gpio(struct kunit *test)
+{
+ struct cirrus_scodec_test_priv *priv = test->priv;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ kunit_platform_driver_register(test, &cirrus_scodec_test_gpio_driver));
+
+ priv->gpio_pdev = kunit_platform_device_alloc(test,
+ cirrus_scodec_test_gpio_driver.driver.name,
+ PLATFORM_DEVID_NONE);
+ KUNIT_ASSERT_NOT_NULL(test, priv->gpio_pdev);
+
+ KUNIT_ASSERT_EQ(test, 0, device_add_software_node(&priv->gpio_pdev->dev,
+ &cirrus_scodec_test_gpio_swnode));
+ KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test,
+ device_remove_software_node_wrapper,
+ &priv->gpio_pdev->dev));
+
+ KUNIT_ASSERT_EQ(test, 0, kunit_platform_device_add(test, priv->gpio_pdev));
+
+ priv->gpio_priv = dev_get_drvdata(&priv->gpio_pdev->dev);
+ KUNIT_ASSERT_NOT_NULL(test, priv->gpio_priv);
+}
+
+static void cirrus_scodec_test_set_gpio_ref_arg(struct software_node_ref_args *arg,
+ int gpio_num)
+{
+ struct software_node_ref_args template =
+ SOFTWARE_NODE_REFERENCE(&cirrus_scodec_test_gpio_swnode, gpio_num, 0);
+
+ *arg = template;
+}
+
+static int cirrus_scodec_test_set_spkid_swnode(struct kunit *test,
+ struct device *dev,
+ struct software_node_ref_args *args,
+ int num_args)
+{
+ const struct property_entry props_template[] = {
+ PROPERTY_ENTRY_REF_ARRAY_LEN("spk-id-gpios", args, num_args),
+ { }
+ };
+ struct property_entry *props;
+ struct software_node *node;
+
+ node = kunit_kzalloc(test, sizeof(*node), GFP_KERNEL);
+ if (!node)
+ return -ENOMEM;
+
+ props = kunit_kzalloc(test, sizeof(props_template), GFP_KERNEL);
+ if (!props)
+ return -ENOMEM;
+
+ memcpy(props, props_template, sizeof(props_template));
+ node->properties = props;
+
+ return device_add_software_node(dev, node);
+}
+
+struct cirrus_scodec_test_spkid_param {
+ int num_amps;
+ int gpios_per_amp;
+ int num_amps_sharing;
+};
+
+static void cirrus_scodec_test_spkid_parse(struct kunit *test)
+{
+ struct cirrus_scodec_test_priv *priv = test->priv;
+ const struct cirrus_scodec_test_spkid_param *param = test->param_value;
+ int num_spk_id_refs = param->num_amps * param->gpios_per_amp;
+ struct software_node_ref_args *refs;
+ struct device *dev = &priv->amp_dev->dev;
+ unsigned int v;
+ int i, ret;
+
+ refs = kunit_kcalloc(test, num_spk_id_refs, sizeof(*refs), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, refs);
+
+ for (i = 0, v = 0; i < num_spk_id_refs; ) {
+ cirrus_scodec_test_set_gpio_ref_arg(&refs[i++], v++);
+
+ /*
+ * If amps are sharing GPIOs repeat the last set of
+ * GPIOs until we've done that number of amps.
+ * We have done all GPIOs for an amp when i is a multiple
+ * of gpios_per_amp.
+ * We have done all amps sharing the same GPIOs when i is
+ * a multiple of (gpios_per_amp * num_amps_sharing).
+ */
+ if (!(i % param->gpios_per_amp) &&
+ (i % (param->gpios_per_amp * param->num_amps_sharing)))
+ v -= param->gpios_per_amp;
+ }
+
+ ret = cirrus_scodec_test_set_spkid_swnode(test, dev, refs, num_spk_id_refs);
+ KUNIT_EXPECT_EQ_MSG(test, ret, 0, "Failed to add swnode\n");
+
+ for (i = 0; i < param->num_amps; ++i) {
+ for (v = 0; v < (1 << param->gpios_per_amp); ++v) {
+ /* Set only the GPIO bits used by this amp */
+ priv->gpio_priv->pin_state =
+ v << (param->gpios_per_amp * (i / param->num_amps_sharing));
+
+ ret = cirrus_scodec_get_speaker_id(dev, i, param->num_amps, -1);
+ KUNIT_EXPECT_EQ_MSG(test, ret, v,
+ "get_speaker_id failed amp:%d pin_state:%#x\n",
+ i, priv->gpio_priv->pin_state);
+ }
+ }
+}
+
+static void cirrus_scodec_test_no_spkid(struct kunit *test)
+{
+ struct cirrus_scodec_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+ int ret;
+
+ ret = cirrus_scodec_get_speaker_id(dev, 0, 4, -1);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+}
+
+static int cirrus_scodec_test_case_init(struct kunit *test)
+{
+ struct cirrus_scodec_test_priv *priv;
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ test->priv = priv;
+
+ /* Create dummy GPIO */
+ cirrus_scodec_test_create_gpio(test);
+
+ /* Create dummy amp driver dev */
+ priv->amp_dev = faux_device_create("cirrus_scodec_test_amp_drv", NULL, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev);
+ KUNIT_ASSERT_EQ(test, 0, kunit_add_action_or_reset(test,
+ faux_device_destroy_wrapper,
+ priv->amp_dev));
+
+ return 0;
+}
+
+static const struct cirrus_scodec_test_spkid_param cirrus_scodec_test_spkid_param_cases[] = {
+ { .num_amps = 2, .gpios_per_amp = 1, .num_amps_sharing = 1 },
+ { .num_amps = 2, .gpios_per_amp = 2, .num_amps_sharing = 1 },
+ { .num_amps = 2, .gpios_per_amp = 3, .num_amps_sharing = 1 },
+ { .num_amps = 2, .gpios_per_amp = 4, .num_amps_sharing = 1 },
+ { .num_amps = 3, .gpios_per_amp = 1, .num_amps_sharing = 1 },
+ { .num_amps = 3, .gpios_per_amp = 2, .num_amps_sharing = 1 },
+ { .num_amps = 3, .gpios_per_amp = 3, .num_amps_sharing = 1 },
+ { .num_amps = 3, .gpios_per_amp = 4, .num_amps_sharing = 1 },
+ { .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 1 },
+ { .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 1 },
+ { .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 1 },
+ { .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 1 },
+
+ /* Same GPIO shared by all amps */
+ { .num_amps = 2, .gpios_per_amp = 1, .num_amps_sharing = 2 },
+ { .num_amps = 2, .gpios_per_amp = 2, .num_amps_sharing = 2 },
+ { .num_amps = 2, .gpios_per_amp = 3, .num_amps_sharing = 2 },
+ { .num_amps = 2, .gpios_per_amp = 4, .num_amps_sharing = 2 },
+ { .num_amps = 3, .gpios_per_amp = 1, .num_amps_sharing = 3 },
+ { .num_amps = 3, .gpios_per_amp = 2, .num_amps_sharing = 3 },
+ { .num_amps = 3, .gpios_per_amp = 3, .num_amps_sharing = 3 },
+ { .num_amps = 3, .gpios_per_amp = 4, .num_amps_sharing = 3 },
+ { .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 4 },
+ { .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 4 },
+ { .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 4 },
+ { .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 4 },
+
+ /* Two sets of shared GPIOs */
+ { .num_amps = 4, .gpios_per_amp = 1, .num_amps_sharing = 2 },
+ { .num_amps = 4, .gpios_per_amp = 2, .num_amps_sharing = 2 },
+ { .num_amps = 4, .gpios_per_amp = 3, .num_amps_sharing = 2 },
+ { .num_amps = 4, .gpios_per_amp = 4, .num_amps_sharing = 2 },
+};
+
+static void cirrus_scodec_test_spkid_param_desc(const struct cirrus_scodec_test_spkid_param *param,
+ char *desc)
+{
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "amps:%d gpios_per_amp:%d num_amps_sharing:%d",
+ param->num_amps, param->gpios_per_amp, param->num_amps_sharing);
+}
+
+KUNIT_ARRAY_PARAM(cirrus_scodec_test_spkid, cirrus_scodec_test_spkid_param_cases,
+ cirrus_scodec_test_spkid_param_desc);
+
+static struct kunit_case cirrus_scodec_test_cases[] = {
+ KUNIT_CASE_PARAM(cirrus_scodec_test_spkid_parse, cirrus_scodec_test_spkid_gen_params),
+ KUNIT_CASE(cirrus_scodec_test_no_spkid),
+ { } /* terminator */
+};
+
+static struct kunit_suite cirrus_scodec_test_suite = {
+ .name = "snd-hda-scodec-cs35l56-test",
+ .init = cirrus_scodec_test_case_init,
+ .test_cases = cirrus_scodec_test_cases,
+};
+
+kunit_test_suite(cirrus_scodec_test_suite);
+
+MODULE_IMPORT_NS("SND_HDA_CIRRUS_SCODEC");
+MODULE_DESCRIPTION("KUnit test for the Cirrus side-codec library");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c
new file mode 100644
index 000000000000..c0f2a3ff77a1
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c
@@ -0,0 +1,2098 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS35l41 ALSA HDA audio driver
+//
+// Copyright 2021 Cirrus Logic, Inc.
+//
+// Author: Lucas Tanure <tanureal@opensource.cirrus.com>
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <sound/hda_codec.h>
+#include <sound/soc.h>
+#include <linux/pm_runtime.h>
+#include <linux/spi/spi.h>
+#include <linux/vmalloc.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "../generic.h"
+#include "hda_component.h"
+#include "cs35l41_hda.h"
+#include "cs35l41_hda_property.h"
+
+#define CS35L41_PART "cs35l41"
+
+#define HALO_STATE_DSP_CTL_NAME "HALO_STATE"
+#define HALO_STATE_DSP_CTL_TYPE 5
+#define HALO_STATE_DSP_CTL_ALG 262308
+#define CAL_R_DSP_CTL_NAME "CAL_R"
+#define CAL_STATUS_DSP_CTL_NAME "CAL_STATUS"
+#define CAL_CHECKSUM_DSP_CTL_NAME "CAL_CHECKSUM"
+#define CAL_AMBIENT_DSP_CTL_NAME "CAL_AMBIENT"
+#define CAL_DSP_CTL_TYPE 5
+#define CAL_DSP_CTL_ALG 205
+#define CS35L41_UUID "50d90cdc-3de4-4f18-b528-c7fe3b71f40d"
+#define CS35L41_DSM_GET_MUTE 5
+#define CS35L41_NOTIFY_EVENT 0x91
+#define CS35L41_TUNING_SIG 0x109A4A35
+
+enum cs35l41_tuning_param_types {
+ TUNING_PARAM_GAIN,
+};
+
+struct cs35l41_tuning_param_hdr {
+ __le32 tuning_index;
+ __le32 type;
+ __le32 size;
+} __packed;
+
+struct cs35l41_tuning_param {
+ struct cs35l41_tuning_param_hdr hdr;
+ union {
+ __le32 gain;
+ };
+} __packed;
+
+struct cs35l41_tuning_params {
+ __le32 signature;
+ __le32 version;
+ __le32 size;
+ __le32 num_entries;
+ u8 data[];
+} __packed;
+
+/* Firmware calibration controls */
+static const struct cirrus_amp_cal_controls cs35l41_calibration_controls = {
+ .alg_id = CAL_DSP_CTL_ALG,
+ .mem_region = CAL_DSP_CTL_TYPE,
+ .ambient = CAL_AMBIENT_DSP_CTL_NAME,
+ .calr = CAL_R_DSP_CTL_NAME,
+ .status = CAL_STATUS_DSP_CTL_NAME,
+ .checksum = CAL_CHECKSUM_DSP_CTL_NAME,
+};
+
+enum cs35l41_hda_fw_id {
+ CS35L41_HDA_FW_SPK_PROT,
+ CS35L41_HDA_FW_SPK_CALI,
+ CS35L41_HDA_FW_SPK_DIAG,
+ CS35L41_HDA_FW_MISC,
+ CS35L41_HDA_NUM_FW
+};
+
+static const char * const cs35l41_hda_fw_ids[CS35L41_HDA_NUM_FW] = {
+ [CS35L41_HDA_FW_SPK_PROT] = "spk-prot",
+ [CS35L41_HDA_FW_SPK_CALI] = "spk-cali",
+ [CS35L41_HDA_FW_SPK_DIAG] = "spk-diag",
+ [CS35L41_HDA_FW_MISC] = "misc",
+};
+
+static bool firmware_autostart = 1;
+module_param(firmware_autostart, bool, 0444);
+MODULE_PARM_DESC(firmware_autostart, "Allow automatic firmware download on boot"
+ "(0=Disable, 1=Enable) (default=1); ");
+
+static const char channel_name[3] = { 'L', 'R', 'C' };
+
+static const struct reg_sequence cs35l41_hda_config[] = {
+ { CS35L41_PLL_CLK_CTRL, 0x00000430 }, // 3072000Hz, BCLK Input, PLL_REFCLK_EN = 1
+ { CS35L41_DSP_CLK_CTRL, 0x00000003 }, // DSP CLK EN
+ { CS35L41_GLOBAL_CLK_CTRL, 0x00000003 }, // GLOBAL_FS = 48 kHz
+ { CS35L41_SP_RATE_CTRL, 0x00000021 }, // ASP_BCLK_FREQ = 3.072 MHz
+ { CS35L41_SP_FORMAT, 0x20200200 }, // 32 bits RX/TX slots, I2S, clk consumer
+ { CS35L41_SP_TX_WL, 0x00000018 }, // 24 cycles/slot
+ { CS35L41_SP_RX_WL, 0x00000018 }, // 24 cycles/slot
+ { CS35L41_ASP_TX1_SRC, 0x00000018 }, // ASPTX1 SRC = VMON
+ { CS35L41_ASP_TX2_SRC, 0x00000019 }, // ASPTX2 SRC = IMON
+ { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON
+ { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON
+};
+
+static const struct reg_sequence cs35l41_hda_config_no_dsp[] = {
+ { CS35L41_SP_HIZ_CTRL, 0x00000002 }, // Hi-Z unused
+ { CS35L41_DAC_PCM1_SRC, 0x00000008 }, // DACPCM1_SRC = ASPRX1
+ { CS35L41_ASP_TX3_SRC, 0x00000000 }, // ASPTX3 SRC = ZERO FILL
+ { CS35L41_ASP_TX4_SRC, 0x00000000 }, // ASPTX4 SRC = ZERO FILL
+ { CS35L41_DSP1_RX5_SRC, 0x00000020 }, // DSP1RX5 SRC = ERRVOL
+ { CS35L41_DSP1_RX6_SRC, 0x00000021 }, // DSP1RX6 SRC = CLASSH_TGT
+};
+
+static const struct reg_sequence cs35l41_hda_config_dsp[] = {
+ { CS35L41_SP_HIZ_CTRL, 0x00000003 }, // Hi-Z unused/disabled
+ { CS35L41_DAC_PCM1_SRC, 0x00000032 }, // DACPCM1_SRC = DSP1TX1
+ { CS35L41_ASP_TX3_SRC, 0x00000028 }, // ASPTX3 SRC = VPMON
+ { CS35L41_ASP_TX4_SRC, 0x00000029 }, // ASPTX4 SRC = VBSTMON
+ { CS35L41_DSP1_RX6_SRC, 0x00000029 }, // DSP1RX6 SRC = VBSTMON
+};
+
+static const struct reg_sequence cs35l41_hda_unmute[] = {
+ { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB
+ { CS35L41_AMP_GAIN_CTRL, 0x00000084 }, // AMP_GAIN_PCM 4.5 dB
+};
+
+static const struct reg_sequence cs35l41_hda_mute[] = {
+ { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, // AMP_GAIN_PCM 0.5 dB
+ { CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM Mute
+};
+
+static const struct cs_dsp_client_ops client_ops = {
+ /* cs_dsp requires the client to provide this even if it is empty */
+};
+
+static int cs35l41_request_tuning_param_file(struct cs35l41_hda *cs35l41, char *tuning_filename,
+ const struct firmware **firmware, char **filename,
+ const char *ssid)
+{
+ int ret = 0;
+
+ /* Filename is the same as the tuning file with "cfg" suffix */
+ *filename = kasprintf(GFP_KERNEL, "%scfg", tuning_filename);
+ if (*filename == NULL)
+ return -ENOMEM;
+
+ ret = firmware_request_nowarn(firmware, *filename, cs35l41->dev);
+ if (ret != 0) {
+ dev_dbg(cs35l41->dev, "Failed to request '%s'\n", *filename);
+ kfree(*filename);
+ *filename = NULL;
+ }
+
+ return ret;
+}
+
+static int cs35l41_request_firmware_file(struct cs35l41_hda *cs35l41,
+ const struct firmware **firmware, char **filename,
+ const char *ssid, const char *amp_name,
+ int spkid, const char *filetype)
+{
+ const char * const dsp_name = cs35l41->cs_dsp.name;
+ char *s, c;
+ int ret = 0;
+
+ if (spkid > -1 && ssid && amp_name)
+ *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-spkid%d-%s.%s", CS35L41_PART,
+ dsp_name, cs35l41_hda_fw_ids[cs35l41->firmware_type],
+ ssid, spkid, amp_name, filetype);
+ else if (spkid > -1 && ssid)
+ *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-spkid%d.%s", CS35L41_PART,
+ dsp_name, cs35l41_hda_fw_ids[cs35l41->firmware_type],
+ ssid, spkid, filetype);
+ else if (ssid && amp_name)
+ *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-%s.%s", CS35L41_PART,
+ dsp_name, cs35l41_hda_fw_ids[cs35l41->firmware_type],
+ ssid, amp_name, filetype);
+ else if (ssid)
+ *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s.%s", CS35L41_PART,
+ dsp_name, cs35l41_hda_fw_ids[cs35l41->firmware_type],
+ ssid, filetype);
+ else
+ *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s.%s", CS35L41_PART,
+ dsp_name, cs35l41_hda_fw_ids[cs35l41->firmware_type],
+ filetype);
+
+ if (*filename == NULL)
+ return -ENOMEM;
+
+ /*
+ * Make sure that filename is lower-case and any non alpha-numeric
+ * characters except full stop and '/' are replaced with hyphens.
+ */
+ s = *filename;
+ while (*s) {
+ c = *s;
+ if (isalnum(c))
+ *s = tolower(c);
+ else if (c != '.' && c != '/')
+ *s = '-';
+ s++;
+ }
+
+ ret = firmware_request_nowarn(firmware, *filename, cs35l41->dev);
+ if (ret != 0) {
+ dev_dbg(cs35l41->dev, "Failed to request '%s'\n", *filename);
+ kfree(*filename);
+ *filename = NULL;
+ }
+
+ return ret;
+}
+
+static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41,
+ const struct firmware **wmfw_firmware,
+ char **wmfw_filename,
+ const struct firmware **coeff_firmware,
+ char **coeff_filename)
+{
+ int ret;
+
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ cs35l41->acpi_subsystem_id, cs35l41->amp_name,
+ cs35l41->speaker_id, "wmfw");
+ if (!ret) {
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ cs35l41->acpi_subsystem_id, cs35l41->amp_name,
+ cs35l41->speaker_id, "bin");
+ if (ret)
+ goto coeff_err;
+
+ return 0;
+ }
+
+ /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ cs35l41->acpi_subsystem_id,
+ cs35l41->amp_name, -1, "wmfw");
+ if (!ret) {
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ cs35l41->acpi_subsystem_id, cs35l41->amp_name,
+ cs35l41->speaker_id, "bin");
+ if (ret)
+ goto coeff_err;
+
+ return 0;
+ }
+
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN>.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ cs35l41->acpi_subsystem_id,
+ NULL, cs35l41->speaker_id, "wmfw");
+ if (!ret) {
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ cs35l41->acpi_subsystem_id,
+ cs35l41->amp_name, cs35l41->speaker_id, "bin");
+ if (ret)
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware,
+ coeff_filename,
+ cs35l41->acpi_subsystem_id, NULL,
+ cs35l41->speaker_id, "bin");
+ if (ret)
+ goto coeff_err;
+
+ return 0;
+ }
+
+ /* try cirrus/part-dspN-fwtype-sub.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ cs35l41->acpi_subsystem_id,
+ NULL, -1, "wmfw");
+ if (!ret) {
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ cs35l41->acpi_subsystem_id, cs35l41->amp_name,
+ cs35l41->speaker_id, "bin");
+ if (ret)
+ /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware,
+ coeff_filename,
+ cs35l41->acpi_subsystem_id, NULL,
+ cs35l41->speaker_id, "bin");
+ if (ret)
+ goto coeff_err;
+ }
+
+ return ret;
+coeff_err:
+ release_firmware(*wmfw_firmware);
+ kfree(*wmfw_filename);
+ return ret;
+}
+
+static int cs35l41_fallback_firmware_file(struct cs35l41_hda *cs35l41,
+ const struct firmware **wmfw_firmware,
+ char **wmfw_filename,
+ const struct firmware **coeff_firmware,
+ char **coeff_filename)
+{
+ int ret;
+
+ /* Handle fallback */
+ dev_warn(cs35l41->dev, "Falling back to default firmware.\n");
+
+ /* fallback try cirrus/part-dspN-fwtype.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ NULL, NULL, -1, "wmfw");
+ if (ret)
+ goto err;
+
+ /* fallback try cirrus/part-dspN-fwtype.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ NULL, NULL, -1, "bin");
+ if (ret) {
+ release_firmware(*wmfw_firmware);
+ kfree(*wmfw_filename);
+ goto err;
+ }
+ return 0;
+
+err:
+ dev_warn(cs35l41->dev, "Unable to find firmware and tuning\n");
+ return ret;
+}
+
+static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41,
+ const struct firmware **wmfw_firmware,
+ char **wmfw_filename,
+ const struct firmware **coeff_firmware,
+ char **coeff_filename)
+{
+ int ret;
+
+ if (cs35l41->speaker_id > -1) {
+ ret = cs35l41_request_firmware_files_spkid(cs35l41, wmfw_firmware, wmfw_filename,
+ coeff_firmware, coeff_filename);
+ goto out;
+ }
+
+ /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ cs35l41->acpi_subsystem_id,
+ cs35l41->amp_name, -1, "wmfw");
+ if (!ret) {
+ /* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ cs35l41->acpi_subsystem_id, cs35l41->amp_name,
+ -1, "bin");
+ if (ret)
+ goto coeff_err;
+
+ goto out;
+ }
+
+ /* try cirrus/part-dspN-fwtype-sub.wmfw */
+ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ cs35l41->acpi_subsystem_id,
+ NULL, -1, "wmfw");
+ if (!ret) {
+ /* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ cs35l41->acpi_subsystem_id,
+ cs35l41->amp_name, -1, "bin");
+ if (ret)
+ /* try cirrus/part-dspN-fwtype-sub.bin */
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ cs35l41->acpi_subsystem_id, NULL, -1,
+ "bin");
+ if (ret)
+ goto coeff_err;
+ }
+
+out:
+ if (ret)
+ /* if all attempts at finding firmware fail, try fallback */
+ goto fallback;
+
+ return 0;
+
+coeff_err:
+ release_firmware(*wmfw_firmware);
+ kfree(*wmfw_filename);
+fallback:
+ return cs35l41_fallback_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
+ coeff_firmware, coeff_filename);
+}
+
+
+static void cs35l41_hda_apply_calibration(struct cs35l41_hda *cs35l41)
+{
+ int ret;
+
+ if (!cs35l41->cal_data_valid)
+ return;
+
+ ret = cs_amp_write_cal_coeffs(&cs35l41->cs_dsp, &cs35l41_calibration_controls,
+ &cs35l41->cal_data);
+ if (ret < 0)
+ dev_warn(cs35l41->dev, "Failed to apply calibration: %d\n", ret);
+ else
+ dev_info(cs35l41->dev, "Calibration applied: R0=%d\n", cs35l41->cal_data.calR);
+}
+
+static int cs35l41_read_silicon_uid(struct cs35l41_hda *cs35l41, u64 *uid)
+{
+ u32 tmp;
+ int ret;
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_DIE_STS2, &tmp);
+ if (ret) {
+ dev_err(cs35l41->dev, "Cannot obtain CS35L41_DIE_STS2: %d\n", ret);
+ return ret;
+ }
+
+ *uid = tmp;
+ *uid <<= 32;
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_DIE_STS1, &tmp);
+ if (ret) {
+ dev_err(cs35l41->dev, "Cannot obtain CS35L41_DIE_STS1: %d\n", ret);
+ return ret;
+ }
+
+ *uid |= tmp;
+
+ dev_dbg(cs35l41->dev, "UniqueID = %#llx\n", *uid);
+
+ return 0;
+}
+
+static int cs35l41_get_calibration(struct cs35l41_hda *cs35l41)
+{
+ u64 silicon_uid;
+ int ret;
+
+ ret = cs35l41_read_silicon_uid(cs35l41, &silicon_uid);
+ if (ret < 0)
+ return ret;
+
+ ret = cs_amp_get_efi_calibration_data(cs35l41->dev, silicon_uid,
+ cs35l41->index,
+ &cs35l41->cal_data);
+
+ /* Only return an error status if probe should be aborted */
+ if ((ret == -ENOENT) || (ret == -EOVERFLOW))
+ return 0;
+
+ if (ret < 0)
+ return ret;
+
+ cs35l41->cal_data_valid = true;
+
+ return 0;
+}
+
+
+static void cs35l41_set_default_tuning_params(struct cs35l41_hda *cs35l41)
+{
+ cs35l41->tuning_gain = DEFAULT_AMP_GAIN_PCM;
+}
+
+static int cs35l41_read_tuning_params(struct cs35l41_hda *cs35l41, const struct firmware *firmware)
+{
+ struct cs35l41_tuning_params *params;
+ unsigned int offset = 0;
+ unsigned int end;
+ int i;
+
+ params = (void *)&firmware->data[0];
+
+ if (le32_to_cpu(params->size) != firmware->size) {
+ dev_err(cs35l41->dev, "Wrong Size for Tuning Param file. Expected %d got %zu\n",
+ le32_to_cpu(params->size), firmware->size);
+ return -EINVAL;
+ }
+
+ if (le32_to_cpu(params->version) != 1) {
+ dev_err(cs35l41->dev, "Unsupported Tuning Param Version: %d\n",
+ le32_to_cpu(params->version));
+ return -EINVAL;
+ }
+
+ if (le32_to_cpu(params->signature) != CS35L41_TUNING_SIG) {
+ dev_err(cs35l41->dev,
+ "Mismatched Signature for Tuning Param file. Expected %#x got %#x\n",
+ CS35L41_TUNING_SIG, le32_to_cpu(params->signature));
+ return -EINVAL;
+ }
+
+ end = firmware->size - sizeof(struct cs35l41_tuning_params);
+
+ for (i = 0; i < le32_to_cpu(params->num_entries); i++) {
+ struct cs35l41_tuning_param *param;
+
+ if ((offset >= end) || ((offset + sizeof(struct cs35l41_tuning_param_hdr)) >= end))
+ return -EFAULT;
+
+ param = (void *)&params->data[offset];
+ offset += le32_to_cpu(param->hdr.size);
+
+ if (offset > end)
+ return -EFAULT;
+
+ switch (le32_to_cpu(param->hdr.type)) {
+ case TUNING_PARAM_GAIN:
+ cs35l41->tuning_gain = le32_to_cpu(param->gain);
+ dev_dbg(cs35l41->dev, "Applying Gain: %d\n", cs35l41->tuning_gain);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int cs35l41_load_tuning_params(struct cs35l41_hda *cs35l41, char *tuning_filename)
+{
+ const struct firmware *tuning_param_file = NULL;
+ char *tuning_param_filename = NULL;
+ int ret;
+
+ ret = cs35l41_request_tuning_param_file(cs35l41, tuning_filename, &tuning_param_file,
+ &tuning_param_filename, cs35l41->acpi_subsystem_id);
+ if (ret) {
+ dev_dbg(cs35l41->dev, "Missing Tuning Param for file: %s: %d\n", tuning_filename,
+ ret);
+ return 0;
+ }
+
+ ret = cs35l41_read_tuning_params(cs35l41, tuning_param_file);
+ if (ret) {
+ dev_err(cs35l41->dev, "Error reading Tuning Params from file: %s: %d\n",
+ tuning_param_filename, ret);
+ /* Reset to default Tuning Parameters */
+ cs35l41_set_default_tuning_params(cs35l41);
+ }
+
+ release_firmware(tuning_param_file);
+ kfree(tuning_param_filename);
+
+ return ret;
+}
+
+static int cs35l41_init_dsp(struct cs35l41_hda *cs35l41)
+{
+ const struct firmware *coeff_firmware = NULL;
+ const struct firmware *wmfw_firmware = NULL;
+ struct cs_dsp *dsp = &cs35l41->cs_dsp;
+ char *coeff_filename = NULL;
+ char *wmfw_filename = NULL;
+ int ret;
+
+ if (!cs35l41->halo_initialized) {
+ cs35l41_configure_cs_dsp(cs35l41->dev, cs35l41->regmap, dsp);
+ dsp->client_ops = &client_ops;
+
+ ret = cs_dsp_halo_init(&cs35l41->cs_dsp);
+ if (ret)
+ return ret;
+ cs35l41->halo_initialized = true;
+ }
+
+ cs35l41_set_default_tuning_params(cs35l41);
+
+ ret = cs35l41_request_firmware_files(cs35l41, &wmfw_firmware, &wmfw_filename,
+ &coeff_firmware, &coeff_filename);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(cs35l41->dev, "Loading WMFW Firmware: %s\n", wmfw_filename);
+ if (coeff_filename) {
+ dev_dbg(cs35l41->dev, "Loading Coefficient File: %s\n", coeff_filename);
+ ret = cs35l41_load_tuning_params(cs35l41, coeff_filename);
+ if (ret)
+ dev_warn(cs35l41->dev, "Unable to load Tuning Parameters: %d\n", ret);
+ } else {
+ dev_warn(cs35l41->dev, "No Coefficient File available.\n");
+ }
+
+ ret = cs_dsp_power_up(dsp, wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename,
+ cs35l41_hda_fw_ids[cs35l41->firmware_type]);
+ if (ret)
+ goto err;
+
+ cs35l41_hda_apply_calibration(cs35l41);
+
+err:
+ if (ret)
+ cs35l41_set_default_tuning_params(cs35l41);
+ release_firmware(wmfw_firmware);
+ release_firmware(coeff_firmware);
+ kfree(wmfw_filename);
+ kfree(coeff_filename);
+
+ return ret;
+}
+
+static void cs35l41_shutdown_dsp(struct cs35l41_hda *cs35l41)
+{
+ struct cs_dsp *dsp = &cs35l41->cs_dsp;
+
+ cs35l41_set_default_tuning_params(cs35l41);
+ cs_dsp_stop(dsp);
+ cs_dsp_power_down(dsp);
+ dev_dbg(cs35l41->dev, "Unloaded Firmware\n");
+}
+
+static void cs35l41_remove_dsp(struct cs35l41_hda *cs35l41)
+{
+ struct cs_dsp *dsp = &cs35l41->cs_dsp;
+
+ cancel_work_sync(&cs35l41->fw_load_work);
+
+ guard(mutex)(&cs35l41->fw_mutex);
+ cs35l41_shutdown_dsp(cs35l41);
+ cs_dsp_remove(dsp);
+ cs35l41->halo_initialized = false;
+}
+
+/* Protection release cycle to get the speaker out of Safe-Mode */
+static void cs35l41_error_release(struct device *dev, struct regmap *regmap, unsigned int mask)
+{
+ regmap_write(regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
+ regmap_set_bits(regmap, CS35L41_PROTECT_REL_ERR_IGN, mask);
+ regmap_clear_bits(regmap, CS35L41_PROTECT_REL_ERR_IGN, mask);
+}
+
+/* Clear all errors to release safe mode. Global Enable must be cleared first. */
+static void cs35l41_irq_release(struct cs35l41_hda *cs35l41)
+{
+ cs35l41_error_release(cs35l41->dev, cs35l41->regmap, cs35l41->irq_errors);
+ cs35l41->irq_errors = 0;
+}
+
+static void cs35l41_update_mixer(struct cs35l41_hda *cs35l41)
+{
+ struct regmap *reg = cs35l41->regmap;
+ unsigned int asp_en = 0;
+ unsigned int dsp1rx2_src = 0;
+
+ regmap_multi_reg_write(reg, cs35l41_hda_config, ARRAY_SIZE(cs35l41_hda_config));
+
+ if (cs35l41->cs_dsp.running) {
+ asp_en |= CS35L41_ASP_TX1_EN_MASK; // ASP_TX1_EN = 1
+ regmap_multi_reg_write(reg, cs35l41_hda_config_dsp,
+ ARRAY_SIZE(cs35l41_hda_config_dsp));
+ if (cs35l41->hw_cfg.bst_type == CS35L41_INT_BOOST)
+ regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VPMON);
+ else
+ regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VBSTMON);
+ } else {
+ regmap_multi_reg_write(reg, cs35l41_hda_config_no_dsp,
+ ARRAY_SIZE(cs35l41_hda_config_no_dsp));
+ }
+
+ if (cs35l41->hw_cfg.spk_pos == CS35L41_CENTER) {
+ asp_en |= CS35L41_ASP_RX2_EN_MASK; // ASP_RX2_EN = 1
+ dsp1rx2_src = 0x00000009; // DSP1RX2 SRC = ASPRX2
+ } else {
+ dsp1rx2_src = 0x00000008; // DSP1RX2 SRC = ASPRX1
+ }
+
+ asp_en |= CS35L41_ASP_RX1_EN_MASK; // ASP_RX1_EN = 1
+
+ regmap_write(reg, CS35L41_SP_ENABLES, asp_en);
+ regmap_write(reg, CS35L41_DSP1_RX1_SRC, 0x00000008); // DSP1RX1 SRC = ASPRX1
+ regmap_write(reg, CS35L41_DSP1_RX2_SRC, dsp1rx2_src);
+}
+
+static void cs35l41_hda_play_start(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct regmap *reg = cs35l41->regmap;
+
+ dev_dbg(dev, "Play (Start)\n");
+
+ if (cs35l41->playback_started) {
+ dev_dbg(dev, "Playback already started.");
+ return;
+ }
+
+ cs35l41->playback_started = true;
+
+ cs35l41_update_mixer(cs35l41);
+
+ if (cs35l41->cs_dsp.running) {
+ regmap_update_bits(reg, CS35L41_PWR_CTRL2,
+ CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK,
+ 1 << CS35L41_VMON_EN_SHIFT | 1 << CS35L41_IMON_EN_SHIFT);
+ cs35l41_set_cspl_mbox_cmd(cs35l41->dev, reg, CSPL_MBOX_CMD_RESUME);
+ }
+ regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_AMP_EN_MASK, 1 << CS35L41_AMP_EN_SHIFT);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
+ regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00008001);
+
+}
+
+static void cs35l41_mute(struct device *dev, bool mute)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct regmap *reg = cs35l41->regmap;
+ unsigned int amp_gain;
+
+ dev_dbg(dev, "Mute(%d:%d) Playback Started: %d\n", mute, cs35l41->mute_override,
+ cs35l41->playback_started);
+
+ if (cs35l41->playback_started) {
+ if (mute || cs35l41->mute_override) {
+ dev_dbg(dev, "Muting\n");
+ regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mute));
+ } else {
+ dev_dbg(dev, "Unmuting\n");
+ if (cs35l41->cs_dsp.running) {
+ dev_dbg(dev, "Using Tuned Gain: %d\n", cs35l41->tuning_gain);
+ amp_gain = (cs35l41->tuning_gain << CS35L41_AMP_GAIN_PCM_SHIFT) |
+ (DEFAULT_AMP_GAIN_PDM << CS35L41_AMP_GAIN_PDM_SHIFT);
+
+ /* AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB */
+ regmap_write(reg, CS35L41_AMP_DIG_VOL_CTRL, 0x00008000);
+ regmap_write(reg, CS35L41_AMP_GAIN_CTRL, amp_gain);
+ } else {
+ regmap_multi_reg_write(reg, cs35l41_hda_unmute,
+ ARRAY_SIZE(cs35l41_hda_unmute));
+ }
+ }
+ }
+}
+
+static void cs35l41_hda_play_done(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct regmap *reg = cs35l41->regmap;
+
+ dev_dbg(dev, "Play (Complete)\n");
+
+ cs35l41_global_enable(dev, reg, cs35l41->hw_cfg.bst_type, 1,
+ &cs35l41->cs_dsp);
+ cs35l41_mute(dev, false);
+}
+
+static void cs35l41_hda_pause_start(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct regmap *reg = cs35l41->regmap;
+
+ dev_dbg(dev, "Pause (Start)\n");
+
+ cs35l41_mute(dev, true);
+ cs35l41_global_enable(dev, reg, cs35l41->hw_cfg.bst_type, 0,
+ &cs35l41->cs_dsp);
+}
+
+static void cs35l41_hda_pause_done(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct regmap *reg = cs35l41->regmap;
+
+ dev_dbg(dev, "Pause (Complete)\n");
+
+ regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
+ regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00000001);
+ if (cs35l41->cs_dsp.running) {
+ cs35l41_set_cspl_mbox_cmd(dev, reg, CSPL_MBOX_CMD_PAUSE);
+ regmap_update_bits(reg, CS35L41_PWR_CTRL2,
+ CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK,
+ 0 << CS35L41_VMON_EN_SHIFT | 0 << CS35L41_IMON_EN_SHIFT);
+ }
+ cs35l41_irq_release(cs35l41);
+ cs35l41->playback_started = false;
+}
+
+static void cs35l41_hda_pre_playback_hook(struct device *dev, int action)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ scoped_guard(mutex, &cs35l41->fw_mutex) {
+ cs35l41_hda_pause_start(dev);
+ }
+ break;
+ default:
+ break;
+ }
+}
+static void cs35l41_hda_playback_hook(struct device *dev, int action)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_OPEN:
+ /*
+ * All amps must be resumed before we can start playing back.
+ * This ensures, for external boost, that all amps are in AMP_SAFE mode.
+ * Do this in HDA_GEN_PCM_ACT_OPEN, since this is run prior to any of the
+ * other actions.
+ */
+ pm_runtime_get_sync(dev);
+ break;
+ case HDA_GEN_PCM_ACT_PREPARE:
+ scoped_guard(mutex, &cs35l41->fw_mutex) {
+ cs35l41_hda_play_start(dev);
+ }
+ break;
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ scoped_guard(mutex, &cs35l41->fw_mutex) {
+ cs35l41_hda_pause_done(dev);
+ }
+ break;
+ case HDA_GEN_PCM_ACT_CLOSE:
+ scoped_guard(mutex, &cs35l41->fw_mutex) {
+ if (!cs35l41->cs_dsp.running && cs35l41->request_fw_load &&
+ !cs35l41->fw_request_ongoing) {
+ dev_info(dev, "Requesting Firmware Load after HDA_GEN_PCM_ACT_CLOSE\n");
+ cs35l41->fw_request_ongoing = true;
+ schedule_work(&cs35l41->fw_load_work);
+ }
+ }
+
+ /*
+ * Playback must be finished for all amps before we start runtime suspend.
+ * This ensures no amps are playing back when we start putting them to sleep.
+ */
+ pm_runtime_put_autosuspend(dev);
+ break;
+ default:
+ break;
+ }
+}
+
+static void cs35l41_hda_post_playback_hook(struct device *dev, int action)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ scoped_guard(mutex, &cs35l41->fw_mutex) {
+ cs35l41_hda_play_done(dev);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static int cs35l41_hda_channel_map(struct cs35l41_hda *cs35l41)
+{
+ unsigned int tx_num = 0;
+ unsigned int *tx_slot = NULL;
+ unsigned int rx_num;
+ unsigned int *rx_slot;
+ unsigned int mono = 0;
+
+ if (!cs35l41->amp_name) {
+ if (cs35l41->hw_cfg.spk_pos >= ARRAY_SIZE(channel_name))
+ return -EINVAL;
+
+ cs35l41->amp_name = devm_kasprintf(cs35l41->dev, GFP_KERNEL, "%c%d",
+ channel_name[cs35l41->hw_cfg.spk_pos],
+ cs35l41->channel_index);
+ if (!cs35l41->amp_name)
+ return -ENOMEM;
+ }
+
+ rx_num = 1;
+ if (cs35l41->hw_cfg.spk_pos == CS35L41_CENTER)
+ rx_slot = &mono;
+ else
+ rx_slot = &cs35l41->hw_cfg.spk_pos;
+
+ return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_num, tx_slot, rx_num,
+ rx_slot);
+}
+
+static int cs35l41_verify_id(struct cs35l41_hda *cs35l41, unsigned int *regid, unsigned int *reg_revid)
+{
+ unsigned int mtl_revid, chipid;
+ int ret;
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_DEVID, regid);
+ if (ret) {
+ dev_err_probe(cs35l41->dev, ret, "Get Device ID failed\n");
+ return ret;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_REVID, reg_revid);
+ if (ret) {
+ dev_err_probe(cs35l41->dev, ret, "Get Revision ID failed\n");
+ return ret;
+ }
+
+ mtl_revid = *reg_revid & CS35L41_MTLREVID_MASK;
+
+ chipid = (mtl_revid % 2) ? CS35L41R_CHIP_ID : CS35L41_CHIP_ID;
+ if (*regid != chipid) {
+ dev_err(cs35l41->dev, "CS35L41 Device ID (%X). Expected ID %X\n", *regid, chipid);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int cs35l41_ready_for_reset(struct cs35l41_hda *cs35l41)
+{
+ guard(mutex)(&cs35l41->fw_mutex);
+ if (cs35l41->cs_dsp.running) {
+ cs35l41->cs_dsp.running = false;
+ cs35l41->cs_dsp.booted = false;
+ }
+ regcache_mark_dirty(cs35l41->regmap);
+
+ return 0;
+}
+
+static int cs35l41_system_suspend_prep(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l41->dev, "System Suspend Prepare\n");
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
+ dev_err_once(cs35l41->dev, "System Suspend not supported\n");
+ return 0; /* don't block the whole system suspend */
+ }
+
+ guard(mutex)(&cs35l41->fw_mutex);
+ if (cs35l41->playback_started)
+ cs35l41_hda_pause_start(dev);
+
+ return 0;
+}
+
+static int cs35l41_system_suspend(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(cs35l41->dev, "System Suspend\n");
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
+ dev_err_once(cs35l41->dev, "System Suspend not supported\n");
+ return 0; /* don't block the whole system suspend */
+ }
+
+ scoped_guard(mutex, &cs35l41->fw_mutex) {
+ if (cs35l41->playback_started)
+ cs35l41_hda_pause_done(dev);
+ }
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret) {
+ dev_err(dev, "System Suspend Failed, unable to runtime suspend: %d\n", ret);
+ return ret;
+ }
+
+ /* Shutdown DSP before system suspend */
+ ret = cs35l41_ready_for_reset(cs35l41);
+ if (ret)
+ dev_err(dev, "System Suspend Failed, not ready for Reset: %d\n", ret);
+
+ if (cs35l41->reset_gpio) {
+ dev_info(cs35l41->dev, "Asserting Reset\n");
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+ usleep_range(2000, 2100);
+ }
+
+ dev_dbg(cs35l41->dev, "System Suspended\n");
+
+ return ret;
+}
+
+static int cs35l41_wait_boot_done(struct cs35l41_hda *cs35l41)
+{
+ unsigned int int_status;
+ int ret;
+
+ ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS4, int_status,
+ int_status & CS35L41_OTP_BOOT_DONE, 1000, 100000);
+ if (ret) {
+ dev_err(cs35l41->dev, "Failed waiting for OTP_BOOT_DONE\n");
+ return ret;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS3, &int_status);
+ if (ret || (int_status & CS35L41_OTP_BOOT_ERR)) {
+ dev_err(cs35l41->dev, "OTP Boot status %x error\n",
+ int_status & CS35L41_OTP_BOOT_ERR);
+ if (!ret)
+ ret = -EIO;
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs35l41_system_resume(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(cs35l41->dev, "System Resume\n");
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
+ dev_err_once(cs35l41->dev, "System Resume not supported\n");
+ return 0; /* don't block the whole system resume */
+ }
+
+ if (cs35l41->reset_gpio) {
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+ usleep_range(2000, 2100);
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 1);
+ }
+
+ usleep_range(2000, 2100);
+
+ regcache_cache_only(cs35l41->regmap, false);
+
+ regmap_write(cs35l41->regmap, CS35L41_SFT_RESET, CS35L41_SOFTWARE_RESET);
+ usleep_range(2000, 2100);
+
+ ret = cs35l41_wait_boot_done(cs35l41);
+ if (ret)
+ return ret;
+
+ regcache_cache_only(cs35l41->regmap, true);
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret) {
+ dev_err(dev, "System Resume Failed: Unable to runtime resume: %d\n", ret);
+ return ret;
+ }
+
+ guard(mutex)(&cs35l41->fw_mutex);
+
+ if (cs35l41->request_fw_load && !cs35l41->fw_request_ongoing) {
+ cs35l41->fw_request_ongoing = true;
+ schedule_work(&cs35l41->fw_load_work);
+ }
+
+ return ret;
+}
+
+static int cs35l41_runtime_idle(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH)
+ return -EBUSY; /* suspend not supported yet on this model */
+ return 0;
+}
+
+static int cs35l41_runtime_suspend(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(cs35l41->dev, "Runtime Suspend\n");
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
+ dev_dbg(cs35l41->dev, "Runtime Suspend not supported\n");
+ return 0;
+ }
+
+ guard(mutex)(&cs35l41->fw_mutex);
+
+ if (cs35l41->cs_dsp.running) {
+ ret = cs35l41_enter_hibernate(cs35l41->dev, cs35l41->regmap,
+ cs35l41->hw_cfg.bst_type);
+ if (ret)
+ return ret;
+ } else {
+ cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
+ }
+
+ regcache_cache_only(cs35l41->regmap, true);
+ regcache_mark_dirty(cs35l41->regmap);
+
+ return 0;
+}
+
+static int cs35l41_runtime_resume(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ unsigned int regid, reg_revid;
+ int ret;
+
+ dev_dbg(cs35l41->dev, "Runtime Resume\n");
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
+ dev_dbg(cs35l41->dev, "Runtime Resume not supported\n");
+ return 0;
+ }
+
+ guard(mutex)(&cs35l41->fw_mutex);
+
+ regcache_cache_only(cs35l41->regmap, false);
+
+ if (cs35l41->cs_dsp.running) {
+ ret = cs35l41_exit_hibernate(cs35l41->dev, cs35l41->regmap);
+ if (ret) {
+ dev_warn(cs35l41->dev, "Unable to exit Hibernate.");
+ return ret;
+ }
+ }
+
+ ret = cs35l41_verify_id(cs35l41, &regid, &reg_revid);
+ if (ret)
+ return ret;
+
+ /* Test key needs to be unlocked to allow the OTP settings to re-apply */
+ cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap);
+ ret = regcache_sync(cs35l41->regmap);
+ cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
+ if (ret) {
+ dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret);
+ return ret;
+ }
+
+ if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
+ cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, &cs35l41->hw_cfg);
+
+ dev_dbg(cs35l41->dev, "CS35L41 Resumed (%x), Revision: %02X\n", regid, reg_revid);
+
+ return 0;
+}
+
+static int cs35l41_hda_read_ctl(struct cs_dsp *dsp, const char *name, int type,
+ unsigned int alg, void *buf, size_t len)
+{
+ guard(mutex)(&dsp->pwr_lock);
+ return cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(dsp, name, type, alg), 0, buf, len);
+}
+
+static int cs35l41_smart_amp(struct cs35l41_hda *cs35l41)
+{
+ unsigned int fw_status;
+ __be32 halo_sts;
+ int ret;
+
+ if (cs35l41->bypass_fw) {
+ dev_warn(cs35l41->dev, "Bypassing Firmware.\n");
+ return 0;
+ }
+
+ ret = cs35l41_init_dsp(cs35l41);
+ if (ret) {
+ dev_warn(cs35l41->dev, "Cannot Initialize Firmware. Error: %d\n", ret);
+ goto clean_dsp;
+ }
+
+ ret = cs35l41_write_fs_errata(cs35l41->dev, cs35l41->regmap);
+ if (ret) {
+ dev_err(cs35l41->dev, "Cannot Write FS Errata: %d\n", ret);
+ goto clean_dsp;
+ }
+
+ ret = cs_dsp_run(&cs35l41->cs_dsp);
+ if (ret) {
+ dev_err(cs35l41->dev, "Fail to start dsp: %d\n", ret);
+ goto clean_dsp;
+ }
+
+ ret = read_poll_timeout(cs35l41_hda_read_ctl, ret,
+ be32_to_cpu(halo_sts) == HALO_STATE_CODE_RUN,
+ 1000, 15000, false, &cs35l41->cs_dsp, HALO_STATE_DSP_CTL_NAME,
+ HALO_STATE_DSP_CTL_TYPE, HALO_STATE_DSP_CTL_ALG,
+ &halo_sts, sizeof(halo_sts));
+
+ if (ret) {
+ dev_err(cs35l41->dev, "Timeout waiting for HALO Core to start. State: %u\n",
+ halo_sts);
+ goto clean_dsp;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_DSP_MBOX_2, &fw_status);
+ if (ret < 0) {
+ dev_err(cs35l41->dev,
+ "Failed to read firmware status: %d\n", ret);
+ goto clean_dsp;
+ }
+
+ switch (fw_status) {
+ case CSPL_MBOX_STS_RUNNING:
+ case CSPL_MBOX_STS_PAUSED:
+ break;
+ default:
+ dev_err(cs35l41->dev, "Firmware status is invalid: %u\n",
+ fw_status);
+ ret = -EINVAL;
+ goto clean_dsp;
+ }
+
+ ret = cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap, CSPL_MBOX_CMD_PAUSE);
+ if (ret) {
+ dev_err(cs35l41->dev, "Error waiting for DSP to pause: %u\n", ret);
+ goto clean_dsp;
+ }
+
+ dev_info(cs35l41->dev, "Firmware Loaded - Type: %s, Gain: %d\n",
+ cs35l41_hda_fw_ids[cs35l41->firmware_type], cs35l41->tuning_gain);
+
+ return 0;
+
+clean_dsp:
+ cs35l41_shutdown_dsp(cs35l41);
+ return ret;
+}
+
+static void cs35l41_load_firmware(struct cs35l41_hda *cs35l41, bool load)
+{
+ if (cs35l41->cs_dsp.running && !load) {
+ dev_dbg(cs35l41->dev, "Unloading Firmware\n");
+ cs35l41_shutdown_dsp(cs35l41);
+ } else if (!cs35l41->cs_dsp.running && load) {
+ dev_dbg(cs35l41->dev, "Loading Firmware\n");
+ cs35l41_smart_amp(cs35l41);
+ } else {
+ dev_dbg(cs35l41->dev, "Unable to Load firmware.\n");
+ }
+}
+
+static int cs35l41_fw_load_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = cs35l41->request_fw_load;
+ return 0;
+}
+
+static int cs35l41_mute_override_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = cs35l41->mute_override;
+ return 0;
+}
+
+static void cs35l41_fw_load_work(struct work_struct *work)
+{
+ struct cs35l41_hda *cs35l41 = container_of(work, struct cs35l41_hda, fw_load_work);
+
+ pm_runtime_get_sync(cs35l41->dev);
+
+ scoped_guard(mutex, &cs35l41->fw_mutex) {
+ /* Recheck if playback is ongoing, mutex will block playback during firmware loading */
+ if (cs35l41->playback_started)
+ dev_err(cs35l41->dev, "Cannot Load/Unload firmware during Playback. Retrying...\n");
+ else
+ cs35l41_load_firmware(cs35l41, cs35l41->request_fw_load);
+
+ cs35l41->fw_request_ongoing = false;
+ }
+
+ pm_runtime_put_autosuspend(cs35l41->dev);
+}
+
+static int cs35l41_fw_load_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
+
+ if (cs35l41->request_fw_load == ucontrol->value.integer.value[0])
+ return 0;
+
+ if (cs35l41->fw_request_ongoing) {
+ dev_dbg(cs35l41->dev, "Existing request not complete\n");
+ return -EBUSY;
+ }
+
+ /* Check if playback is ongoing when initial request is made */
+ if (cs35l41->playback_started) {
+ dev_err(cs35l41->dev, "Cannot Load/Unload firmware during Playback\n");
+ return -EBUSY;
+ }
+
+ cs35l41->fw_request_ongoing = true;
+ cs35l41->request_fw_load = ucontrol->value.integer.value[0];
+ schedule_work(&cs35l41->fw_load_work);
+
+ return 1;
+}
+
+static int cs35l41_fw_type_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = cs35l41->firmware_type;
+
+ return 0;
+}
+
+static int cs35l41_fw_type_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
+
+ if (ucontrol->value.enumerated.item[0] < CS35L41_HDA_NUM_FW) {
+ if (cs35l41->firmware_type != ucontrol->value.enumerated.item[0]) {
+ cs35l41->firmware_type = ucontrol->value.enumerated.item[0];
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int cs35l41_fw_type_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(cs35l41_hda_fw_ids), cs35l41_hda_fw_ids);
+}
+
+static int cs35l41_create_controls(struct cs35l41_hda *cs35l41)
+{
+ char fw_type_ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ char fw_load_ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ char mute_override_ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ struct snd_kcontrol_new fw_type_ctl = {
+ .name = fw_type_ctl_name,
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = cs35l41_fw_type_ctl_info,
+ .get = cs35l41_fw_type_ctl_get,
+ .put = cs35l41_fw_type_ctl_put,
+ };
+ struct snd_kcontrol_new fw_load_ctl = {
+ .name = fw_load_ctl_name,
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = snd_ctl_boolean_mono_info,
+ .get = cs35l41_fw_load_ctl_get,
+ .put = cs35l41_fw_load_ctl_put,
+ };
+ struct snd_kcontrol_new mute_override_ctl = {
+ .name = mute_override_ctl_name,
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = snd_ctl_boolean_mono_info,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .get = cs35l41_mute_override_ctl_get,
+ };
+ int ret;
+
+ scnprintf(fw_type_ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s DSP1 Firmware Type",
+ cs35l41->amp_name);
+ scnprintf(fw_load_ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s DSP1 Firmware Load",
+ cs35l41->amp_name);
+ scnprintf(mute_override_ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s Forced Mute Status",
+ cs35l41->amp_name);
+
+ ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&fw_type_ctl, cs35l41));
+ if (ret) {
+ dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", fw_type_ctl.name, ret);
+ return ret;
+ }
+
+ dev_dbg(cs35l41->dev, "Added Control %s\n", fw_type_ctl.name);
+
+ ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&fw_load_ctl, cs35l41));
+ if (ret) {
+ dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", fw_load_ctl.name, ret);
+ return ret;
+ }
+
+ dev_dbg(cs35l41->dev, "Added Control %s\n", fw_load_ctl.name);
+
+ ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&mute_override_ctl, cs35l41));
+ if (ret) {
+ dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", mute_override_ctl.name,
+ ret);
+ return ret;
+ }
+
+ dev_dbg(cs35l41->dev, "Added Control %s\n", mute_override_ctl.name);
+
+ return 0;
+}
+
+static bool cs35l41_dsm_supported(acpi_handle handle, unsigned int commands)
+{
+ guid_t guid;
+
+ guid_parse(CS35L41_UUID, &guid);
+
+ return acpi_check_dsm(handle, &guid, 0, BIT(commands));
+}
+
+static int cs35l41_get_acpi_mute_state(struct cs35l41_hda *cs35l41, acpi_handle handle)
+{
+ guid_t guid;
+ union acpi_object *ret;
+ int mute = -ENODEV;
+
+ guid_parse(CS35L41_UUID, &guid);
+
+ if (cs35l41_dsm_supported(handle, CS35L41_DSM_GET_MUTE)) {
+ ret = acpi_evaluate_dsm(handle, &guid, 0, CS35L41_DSM_GET_MUTE, NULL);
+ if (!ret)
+ return -EINVAL;
+ mute = *ret->buffer.pointer;
+ dev_dbg(cs35l41->dev, "CS35L41_DSM_GET_MUTE: %d\n", mute);
+ }
+
+ dev_dbg(cs35l41->dev, "%s: %d\n", __func__, mute);
+
+ return mute;
+}
+
+static void cs35l41_acpi_device_notify(acpi_handle handle, u32 event, struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ int mute;
+
+ if (event != CS35L41_NOTIFY_EVENT)
+ return;
+
+ mute = cs35l41_get_acpi_mute_state(cs35l41, handle);
+ if (mute < 0) {
+ dev_warn(cs35l41->dev, "Unable to retrieve mute state: %d\n", mute);
+ return;
+ }
+
+ dev_dbg(cs35l41->dev, "Requesting mute value: %d\n", mute);
+ cs35l41->mute_override = (mute > 0);
+ cs35l41_mute(cs35l41->dev, cs35l41->mute_override);
+}
+
+static int cs35l41_hda_bind(struct device *dev, struct device *master, void *master_data)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
+ unsigned int sleep_flags;
+ int ret = 0;
+
+ comp = hda_component_from_index(parent, cs35l41->index);
+ if (!comp)
+ return -EINVAL;
+
+ if (comp->dev)
+ return -EBUSY;
+
+ pm_runtime_get_sync(dev);
+
+ mutex_lock(&cs35l41->fw_mutex);
+
+ comp->dev = dev;
+ cs35l41->codec = parent->codec;
+ if (!cs35l41->acpi_subsystem_id)
+ cs35l41->acpi_subsystem_id = kasprintf(GFP_KERNEL, "%.8x",
+ cs35l41->codec->core.subsystem_id);
+
+ strscpy(comp->name, dev_name(dev), sizeof(comp->name));
+
+ cs35l41->firmware_type = CS35L41_HDA_FW_SPK_PROT;
+
+ if (firmware_autostart) {
+ dev_dbg(cs35l41->dev, "Firmware Autostart.\n");
+ cs35l41->request_fw_load = true;
+ if (cs35l41_smart_amp(cs35l41) < 0)
+ dev_warn(cs35l41->dev, "Cannot Run Firmware, reverting to dsp bypass...\n");
+ } else {
+ dev_dbg(cs35l41->dev, "Firmware Autostart is disabled.\n");
+ }
+
+ ret = cs35l41_create_controls(cs35l41);
+
+ comp->playback_hook = cs35l41_hda_playback_hook;
+ comp->pre_playback_hook = cs35l41_hda_pre_playback_hook;
+ comp->post_playback_hook = cs35l41_hda_post_playback_hook;
+ comp->acpi_notify = cs35l41_acpi_device_notify;
+ comp->adev = cs35l41->dacpi;
+
+ comp->acpi_notifications_supported = cs35l41_dsm_supported(acpi_device_handle(comp->adev),
+ CS35L41_DSM_GET_MUTE);
+
+ cs35l41->mute_override = cs35l41_get_acpi_mute_state(cs35l41,
+ acpi_device_handle(cs35l41->dacpi)) > 0;
+
+ mutex_unlock(&cs35l41->fw_mutex);
+
+ sleep_flags = lock_system_sleep();
+ if (!device_link_add(&cs35l41->codec->core.dev, cs35l41->dev, DL_FLAG_STATELESS))
+ dev_warn(dev, "Unable to create device link\n");
+ unlock_system_sleep(sleep_flags);
+
+ pm_runtime_put_autosuspend(dev);
+
+ dev_info(cs35l41->dev,
+ "CS35L41 Bound - SSID: %s, BST: %d, VSPK: %d, CH: %c, FW EN: %d, SPKID: %d\n",
+ cs35l41->acpi_subsystem_id, cs35l41->hw_cfg.bst_type,
+ cs35l41->hw_cfg.gpio1.func == CS35l41_VSPK_SWITCH,
+ channel_name[cs35l41->hw_cfg.spk_pos],
+ cs35l41->cs_dsp.running, cs35l41->speaker_id);
+
+ return ret;
+}
+
+static void cs35l41_hda_unbind(struct device *dev, struct device *master, void *master_data)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
+ unsigned int sleep_flags;
+
+ comp = hda_component_from_index(parent, cs35l41->index);
+ if (!comp)
+ return;
+
+ if (comp->dev == dev) {
+ sleep_flags = lock_system_sleep();
+ device_link_remove(&cs35l41->codec->core.dev, cs35l41->dev);
+ unlock_system_sleep(sleep_flags);
+ memset(comp, 0, sizeof(*comp));
+ }
+}
+
+static const struct component_ops cs35l41_hda_comp_ops = {
+ .bind = cs35l41_hda_bind,
+ .unbind = cs35l41_hda_unbind,
+};
+
+static irqreturn_t cs35l41_bst_short_err(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "LBST Error\n");
+ set_bit(CS35L41_BST_SHORT_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l41_bst_dcm_uvp_err(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "DCM VBST Under Voltage Error\n");
+ set_bit(CS35L41_BST_UVP_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l41_bst_ovp_err(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "VBST Over Voltage error\n");
+ set_bit(CS35L41_BST_OVP_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l41_temp_err(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "Over temperature error\n");
+ set_bit(CS35L41_TEMP_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l41_temp_warn(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "Over temperature warning\n");
+ set_bit(CS35L41_TEMP_WARN_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l41_amp_short(int irq, void *data)
+{
+ struct cs35l41_hda *cs35l41 = data;
+
+ dev_crit_ratelimited(cs35l41->dev, "Amp short error\n");
+ set_bit(CS35L41_AMP_SHORT_ERR_RLS_SHIFT, &cs35l41->irq_errors);
+
+ return IRQ_HANDLED;
+}
+
+static const struct cs35l41_irq cs35l41_irqs[] = {
+ CS35L41_IRQ(BST_OVP_ERR, "Boost Overvoltage Error", cs35l41_bst_ovp_err),
+ CS35L41_IRQ(BST_DCM_UVP_ERR, "Boost Undervoltage Error", cs35l41_bst_dcm_uvp_err),
+ CS35L41_IRQ(BST_SHORT_ERR, "Boost Inductor Short Error", cs35l41_bst_short_err),
+ CS35L41_IRQ(TEMP_WARN, "Temperature Warning", cs35l41_temp_warn),
+ CS35L41_IRQ(TEMP_ERR, "Temperature Error", cs35l41_temp_err),
+ CS35L41_IRQ(AMP_SHORT_ERR, "Amp Short", cs35l41_amp_short),
+};
+
+static const struct regmap_irq cs35l41_reg_irqs[] = {
+ CS35L41_REG_IRQ(IRQ1_STATUS1, BST_OVP_ERR),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, BST_DCM_UVP_ERR),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, BST_SHORT_ERR),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, TEMP_WARN),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, TEMP_ERR),
+ CS35L41_REG_IRQ(IRQ1_STATUS1, AMP_SHORT_ERR),
+};
+
+static const struct regmap_irq_chip cs35l41_regmap_irq_chip = {
+ .name = "cs35l41 IRQ1 Controller",
+ .status_base = CS35L41_IRQ1_STATUS1,
+ .mask_base = CS35L41_IRQ1_MASK1,
+ .ack_base = CS35L41_IRQ1_STATUS1,
+ .num_regs = 4,
+ .irqs = cs35l41_reg_irqs,
+ .num_irqs = ARRAY_SIZE(cs35l41_reg_irqs),
+ .runtime_pm = true,
+};
+
+static void cs35l41_configure_interrupt(struct cs35l41_hda *cs35l41, int irq_pol)
+{
+ int irq;
+ int ret;
+ int i;
+
+ if (!cs35l41->irq) {
+ dev_warn(cs35l41->dev, "No Interrupt Found");
+ goto err;
+ }
+
+ ret = devm_regmap_add_irq_chip(cs35l41->dev, cs35l41->regmap, cs35l41->irq,
+ IRQF_ONESHOT | IRQF_SHARED | irq_pol,
+ 0, &cs35l41_regmap_irq_chip, &cs35l41->irq_data);
+ if (ret) {
+ dev_dbg(cs35l41->dev, "Unable to add IRQ Chip: %d.", ret);
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs35l41_irqs); i++) {
+ irq = regmap_irq_get_virq(cs35l41->irq_data, cs35l41_irqs[i].irq);
+ if (irq < 0) {
+ ret = irq;
+ dev_dbg(cs35l41->dev, "Unable to map IRQ %s: %d.", cs35l41_irqs[i].name,
+ ret);
+ goto err;
+ }
+
+ ret = devm_request_threaded_irq(cs35l41->dev, irq, NULL,
+ cs35l41_irqs[i].handler,
+ IRQF_ONESHOT | IRQF_SHARED | irq_pol,
+ cs35l41_irqs[i].name, cs35l41);
+ if (ret) {
+ dev_dbg(cs35l41->dev, "Unable to allocate IRQ %s:: %d.",
+ cs35l41_irqs[i].name, ret);
+ goto err;
+ }
+ }
+ return;
+err:
+ dev_warn(cs35l41->dev,
+ "IRQ Config Failed. Amp errors may not be recoverable without reboot.");
+}
+
+static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41)
+{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+ bool using_irq = false;
+ int irq_pol;
+ int ret;
+
+ if (!cs35l41->hw_cfg.valid)
+ return -EINVAL;
+
+ ret = cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, hw_cfg);
+ if (ret)
+ return ret;
+
+ if (hw_cfg->gpio1.valid) {
+ switch (hw_cfg->gpio1.func) {
+ case CS35L41_NOT_USED:
+ break;
+ case CS35l41_VSPK_SWITCH:
+ hw_cfg->gpio1.func = CS35L41_GPIO1_GPIO;
+ hw_cfg->gpio1.out_en = true;
+ break;
+ case CS35l41_SYNC:
+ hw_cfg->gpio1.func = CS35L41_GPIO1_MDSYNC;
+ break;
+ default:
+ dev_err(cs35l41->dev, "Invalid function %d for GPIO1\n",
+ hw_cfg->gpio1.func);
+ return -EINVAL;
+ }
+ }
+
+ if (hw_cfg->gpio2.valid) {
+ switch (hw_cfg->gpio2.func) {
+ case CS35L41_NOT_USED:
+ break;
+ case CS35L41_INTERRUPT:
+ using_irq = true;
+ hw_cfg->gpio2.func = CS35L41_GPIO2_INT_OPEN_DRAIN;
+ break;
+ default:
+ dev_err(cs35l41->dev, "Invalid GPIO2 function %d\n", hw_cfg->gpio2.func);
+ return -EINVAL;
+ }
+ }
+
+ irq_pol = cs35l41_gpio_config(cs35l41->regmap, hw_cfg);
+
+ if (using_irq)
+ cs35l41_configure_interrupt(cs35l41, irq_pol);
+
+ return cs35l41_hda_channel_map(cs35l41);
+}
+
+int cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int fixed_gpio_id)
+{
+ struct gpio_desc *speaker_id_desc;
+ int speaker_id = -ENODEV;
+
+ if (fixed_gpio_id >= 0) {
+ dev_dbg(dev, "Found Fixed Speaker ID GPIO (index = %d)\n", fixed_gpio_id);
+ speaker_id_desc = gpiod_get_index(dev, NULL, fixed_gpio_id, GPIOD_IN);
+ if (IS_ERR(speaker_id_desc)) {
+ speaker_id = PTR_ERR(speaker_id_desc);
+ return speaker_id;
+ }
+ speaker_id = gpiod_get_value_cansleep(speaker_id_desc);
+ gpiod_put(speaker_id_desc);
+ dev_dbg(dev, "Speaker ID = %d\n", speaker_id);
+ } else {
+ int base_index;
+ int gpios_per_amp;
+ int count;
+ int tmp;
+ int i;
+
+ count = gpiod_count(dev, "spk-id");
+ if (count > 0) {
+ speaker_id = 0;
+ gpios_per_amp = count / num_amps;
+ base_index = gpios_per_amp * amp_index;
+
+ if (count % num_amps)
+ return -EINVAL;
+
+ dev_dbg(dev, "Found %d Speaker ID GPIOs per Amp\n", gpios_per_amp);
+
+ for (i = 0; i < gpios_per_amp; i++) {
+ speaker_id_desc = gpiod_get_index(dev, "spk-id", i + base_index,
+ GPIOD_IN);
+ if (IS_ERR(speaker_id_desc)) {
+ speaker_id = PTR_ERR(speaker_id_desc);
+ break;
+ }
+ tmp = gpiod_get_value_cansleep(speaker_id_desc);
+ gpiod_put(speaker_id_desc);
+ if (tmp < 0) {
+ speaker_id = tmp;
+ break;
+ }
+ speaker_id |= tmp << i;
+ }
+ dev_dbg(dev, "Speaker ID = %d\n", speaker_id);
+ }
+ }
+ return speaker_id;
+}
+
+int cs35l41_hda_parse_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id)
+{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+ u32 values[HDA_MAX_COMPONENTS];
+ char *property;
+ size_t nval;
+ int i, ret;
+
+ property = "cirrus,dev-index";
+ ret = device_property_count_u32(physdev, property);
+ if (ret <= 0)
+ goto err;
+
+ if (ret > ARRAY_SIZE(values)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ nval = ret;
+
+ ret = device_property_read_u32_array(physdev, property, values, nval);
+ if (ret)
+ goto err;
+
+ cs35l41->index = -1;
+ for (i = 0; i < nval; i++) {
+ if (values[i] == id) {
+ cs35l41->index = i;
+ break;
+ }
+ }
+ if (cs35l41->index == -1) {
+ dev_err(cs35l41->dev, "No index found in %s\n", property);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /* To use the same release code for all laptop variants we can't use devm_ version of
+ * gpiod_get here, as CLSA010* don't have a fully functional bios with an _DSD node
+ */
+ cs35l41->reset_gpio = fwnode_gpiod_get_index(acpi_fwnode_handle(cs35l41->dacpi), "reset",
+ cs35l41->index, GPIOD_OUT_LOW,
+ "cs35l41-reset");
+
+ property = "cirrus,speaker-position";
+ ret = device_property_read_u32_array(physdev, property, values, nval);
+ if (ret)
+ goto err;
+ hw_cfg->spk_pos = values[cs35l41->index];
+
+ cs35l41->channel_index = 0;
+ for (i = 0; i < cs35l41->index; i++)
+ if (values[i] == hw_cfg->spk_pos)
+ cs35l41->channel_index++;
+
+ property = "cirrus,gpio1-func";
+ ret = device_property_read_u32_array(physdev, property, values, nval);
+ if (ret)
+ goto err;
+ hw_cfg->gpio1.func = values[cs35l41->index];
+ hw_cfg->gpio1.valid = true;
+
+ property = "cirrus,gpio2-func";
+ ret = device_property_read_u32_array(physdev, property, values, nval);
+ if (ret)
+ goto err;
+ hw_cfg->gpio2.func = values[cs35l41->index];
+ hw_cfg->gpio2.valid = true;
+
+ property = "cirrus,boost-peak-milliamp";
+ ret = device_property_read_u32_array(physdev, property, values, nval);
+ if (ret == 0)
+ hw_cfg->bst_ipk = values[cs35l41->index];
+ else
+ hw_cfg->bst_ipk = -1;
+
+ property = "cirrus,boost-ind-nanohenry";
+ ret = device_property_read_u32_array(physdev, property, values, nval);
+ if (ret == 0)
+ hw_cfg->bst_ind = values[cs35l41->index];
+ else
+ hw_cfg->bst_ind = -1;
+
+ property = "cirrus,boost-cap-microfarad";
+ ret = device_property_read_u32_array(physdev, property, values, nval);
+ if (ret == 0)
+ hw_cfg->bst_cap = values[cs35l41->index];
+ else
+ hw_cfg->bst_cap = -1;
+
+ cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, cs35l41->index, nval, -1);
+
+ if (hw_cfg->bst_ind > 0 || hw_cfg->bst_cap > 0 || hw_cfg->bst_ipk > 0)
+ hw_cfg->bst_type = CS35L41_INT_BOOST;
+ else
+ hw_cfg->bst_type = CS35L41_EXT_BOOST;
+
+ hw_cfg->valid = true;
+
+ return 0;
+err:
+ dev_err(cs35l41->dev, "Failed property %s: %d\n", property, ret);
+ hw_cfg->valid = false;
+ hw_cfg->gpio1.valid = false;
+ hw_cfg->gpio2.valid = false;
+ acpi_dev_put(cs35l41->dacpi);
+
+ return ret;
+}
+
+static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, int id)
+{
+ struct acpi_device *adev;
+ struct device *physdev;
+ struct spi_device *spi;
+ const char *sub;
+ int ret;
+
+ adev = acpi_dev_get_first_match_dev(hid, NULL, -1);
+ if (!adev) {
+ dev_err(cs35l41->dev, "Failed to find an ACPI device for %s\n", hid);
+ return -ENODEV;
+ }
+
+ cs35l41->dacpi = adev;
+ physdev = get_device(acpi_get_first_physical_node(adev));
+
+ sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev));
+ if (IS_ERR(sub))
+ sub = NULL;
+ cs35l41->acpi_subsystem_id = sub;
+
+ ret = cs35l41_add_dsd_properties(cs35l41, physdev, id, hid);
+ if (!ret) {
+ dev_info(cs35l41->dev, "Using extra _DSD properties, bypassing _DSD in ACPI\n");
+ goto out;
+ }
+
+ ret = cs35l41_hda_parse_acpi(cs35l41, physdev, id);
+ if (ret) {
+ put_device(physdev);
+ return ret;
+ }
+out:
+ put_device(physdev);
+
+ cs35l41->bypass_fw = false;
+ if (cs35l41->control_bus == SPI) {
+ spi = to_spi_device(cs35l41->dev);
+ if (spi->max_speed_hz < CS35L41_MAX_ACCEPTABLE_SPI_SPEED_HZ) {
+ dev_warn(cs35l41->dev,
+ "SPI speed is too slow to support firmware download: %d Hz.\n",
+ spi->max_speed_hz);
+ cs35l41->bypass_fw = true;
+ }
+ }
+
+ return 0;
+}
+
+int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,
+ struct regmap *regmap, enum control_bus control_bus)
+{
+ unsigned int regid, reg_revid;
+ struct cs35l41_hda *cs35l41;
+ int ret;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l41_irqs) != ARRAY_SIZE(cs35l41_reg_irqs));
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l41_irqs) != CS35L41_NUM_IRQ);
+
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ cs35l41 = devm_kzalloc(dev, sizeof(*cs35l41), GFP_KERNEL);
+ if (!cs35l41)
+ return -ENOMEM;
+
+ cs35l41->dev = dev;
+ cs35l41->irq = irq;
+ cs35l41->regmap = regmap;
+ cs35l41->control_bus = control_bus;
+ dev_set_drvdata(dev, cs35l41);
+
+ ret = cs35l41_hda_read_acpi(cs35l41, device_name, id);
+ if (ret)
+ return dev_err_probe(cs35l41->dev, ret, "Platform not supported\n");
+
+ if (IS_ERR(cs35l41->reset_gpio)) {
+ ret = PTR_ERR(cs35l41->reset_gpio);
+ cs35l41->reset_gpio = NULL;
+ if (ret == -EBUSY) {
+ dev_info(cs35l41->dev, "Reset line busy, assuming shared reset\n");
+ } else {
+ dev_err_probe(cs35l41->dev, ret, "Failed to get reset GPIO\n");
+ goto err;
+ }
+ }
+ if (cs35l41->reset_gpio) {
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+ usleep_range(2000, 2100);
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 1);
+ }
+
+ usleep_range(2000, 2100);
+ regmap_write(cs35l41->regmap, CS35L41_SFT_RESET, CS35L41_SOFTWARE_RESET);
+ usleep_range(2000, 2100);
+
+ ret = cs35l41_wait_boot_done(cs35l41);
+ if (ret)
+ goto err;
+
+ ret = cs35l41_verify_id(cs35l41, &regid, &reg_revid);
+ if (ret)
+ goto err;
+
+ ret = cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap);
+ if (ret)
+ goto err;
+
+ ret = cs35l41_register_errata_patch(cs35l41->dev, cs35l41->regmap, reg_revid);
+ if (ret)
+ goto err;
+
+ ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap);
+ if (ret) {
+ dev_err_probe(cs35l41->dev, ret, "OTP Unpack failed\n");
+ goto err;
+ }
+
+ ret = cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
+ if (ret)
+ goto err;
+
+ ret = cs35l41_get_calibration(cs35l41);
+ if (ret && ret != -ENOENT)
+ goto err;
+
+ cs35l41_mute(cs35l41->dev, true);
+
+ INIT_WORK(&cs35l41->fw_load_work, cs35l41_fw_load_work);
+ mutex_init(&cs35l41->fw_mutex);
+
+ pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000);
+ pm_runtime_use_autosuspend(cs35l41->dev);
+ pm_runtime_set_active(cs35l41->dev);
+ pm_runtime_get_noresume(cs35l41->dev);
+ pm_runtime_enable(cs35l41->dev);
+
+ ret = cs35l41_hda_apply_properties(cs35l41);
+ if (ret)
+ goto err_pm;
+
+ pm_runtime_put_autosuspend(cs35l41->dev);
+
+ ret = component_add(cs35l41->dev, &cs35l41_hda_comp_ops);
+ if (ret) {
+ dev_err_probe(cs35l41->dev, ret, "Register component failed\n");
+ goto err_pm;
+ }
+
+ dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n", regid, reg_revid);
+
+ return 0;
+
+err_pm:
+ pm_runtime_dont_use_autosuspend(cs35l41->dev);
+ pm_runtime_disable(cs35l41->dev);
+ pm_runtime_put_noidle(cs35l41->dev);
+
+err:
+ if (cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type))
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+ gpiod_put(cs35l41->reset_gpio);
+ gpiod_put(cs35l41->cs_gpio);
+ acpi_dev_put(cs35l41->dacpi);
+ kfree(cs35l41->acpi_subsystem_id);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l41_hda_probe, "SND_HDA_SCODEC_CS35L41");
+
+void cs35l41_hda_remove(struct device *dev)
+{
+ struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
+
+ component_del(cs35l41->dev, &cs35l41_hda_comp_ops);
+
+ pm_runtime_get_sync(cs35l41->dev);
+ pm_runtime_dont_use_autosuspend(cs35l41->dev);
+ pm_runtime_disable(cs35l41->dev);
+
+ if (cs35l41->halo_initialized)
+ cs35l41_remove_dsp(cs35l41);
+
+ acpi_dev_put(cs35l41->dacpi);
+
+ pm_runtime_put_noidle(cs35l41->dev);
+
+ if (cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type))
+ gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
+ gpiod_put(cs35l41->reset_gpio);
+ gpiod_put(cs35l41->cs_gpio);
+ kfree(cs35l41->acpi_subsystem_id);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l41_hda_remove, "SND_HDA_SCODEC_CS35L41");
+
+const struct dev_pm_ops cs35l41_hda_pm_ops = {
+ RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume,
+ cs35l41_runtime_idle)
+ .prepare = cs35l41_system_suspend_prep,
+ SYSTEM_SLEEP_PM_OPS(cs35l41_system_suspend, cs35l41_system_resume)
+};
+EXPORT_SYMBOL_NS_GPL(cs35l41_hda_pm_ops, "SND_HDA_SCODEC_CS35L41");
+
+MODULE_DESCRIPTION("CS35L41 HDA Driver");
+MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
+MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("FW_CS_DSP");
+MODULE_FIRMWARE("cirrus/cs35l41-*.wmfw");
+MODULE_FIRMWARE("cirrus/cs35l41-*.bin");
diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.h b/sound/hda/codecs/side-codecs/cs35l41_hda.h
new file mode 100644
index 000000000000..7d003c598e93
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l41_hda.h
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * CS35L41 ALSA HDA audio driver
+ *
+ * Copyright 2021 Cirrus Logic, Inc.
+ *
+ * Author: Lucas Tanure <tanureal@opensource.cirrus.com>
+ */
+
+#ifndef __CS35L41_HDA_H__
+#define __CS35L41_HDA_H__
+
+#include <linux/acpi.h>
+#include <linux/efi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/device.h>
+#include <sound/cs35l41.h>
+#include <sound/cs-amp-lib.h>
+
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/firmware/cirrus/wmfw.h>
+
+#define CS35L41_MAX_ACCEPTABLE_SPI_SPEED_HZ 1000000
+#define DEFAULT_AMP_GAIN_PCM 17 /* 17.5dB Gain */
+#define DEFAULT_AMP_GAIN_PDM 19 /* 19.5dB Gain */
+
+struct cs35l41_amp_cal_data {
+ u32 calTarget[2];
+ u32 calTime[2];
+ s8 calAmbient;
+ u8 calStatus;
+ u16 calR;
+} __packed;
+
+struct cs35l41_amp_efi_data {
+ u32 size;
+ u32 count;
+ struct cs35l41_amp_cal_data data[];
+} __packed;
+
+enum cs35l41_hda_spk_pos {
+ CS35L41_LEFT,
+ CS35L41_RIGHT,
+ CS35L41_CENTER,
+};
+
+enum cs35l41_hda_gpio_function {
+ CS35L41_NOT_USED,
+ CS35l41_VSPK_SWITCH,
+ CS35L41_INTERRUPT,
+ CS35l41_SYNC,
+};
+
+enum control_bus {
+ I2C,
+ SPI
+};
+
+struct cs35l41_hda {
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *cs_gpio;
+ struct cs35l41_hw_cfg hw_cfg;
+ struct hda_codec *codec;
+
+ int irq;
+ int index;
+ int channel_index;
+ unsigned volatile long irq_errors;
+ const char *amp_name;
+ const char *acpi_subsystem_id;
+ int firmware_type;
+ int speaker_id;
+ struct mutex fw_mutex;
+ struct work_struct fw_load_work;
+
+ struct regmap_irq_chip_data *irq_data;
+ bool firmware_running;
+ bool request_fw_load;
+ bool fw_request_ongoing;
+ bool halo_initialized;
+ bool playback_started;
+ struct cs_dsp cs_dsp;
+ struct acpi_device *dacpi;
+ bool mute_override;
+ enum control_bus control_bus;
+ bool bypass_fw;
+ unsigned int tuning_gain;
+ struct cirrus_amp_cal_data cal_data;
+ bool cal_data_valid;
+
+};
+
+enum halo_state {
+ HALO_STATE_CODE_INIT_DOWNLOAD = 0,
+ HALO_STATE_CODE_START,
+ HALO_STATE_CODE_RUN
+};
+
+extern const struct dev_pm_ops cs35l41_hda_pm_ops;
+
+int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,
+ struct regmap *regmap, enum control_bus control_bus);
+void cs35l41_hda_remove(struct device *dev);
+int cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int fixed_gpio_id);
+int cs35l41_hda_parse_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id);
+
+#endif /*__CS35L41_HDA_H__*/
diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda_i2c.c b/sound/hda/codecs/side-codecs/cs35l41_hda_i2c.c
new file mode 100644
index 000000000000..e77495413c21
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l41_hda_i2c.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS35l41 HDA I2C driver
+//
+// Copyright 2021 Cirrus Logic, Inc.
+//
+// Author: Lucas Tanure <tanureal@opensource.cirrus.com>
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+
+#include "cs35l41_hda.h"
+
+static int cs35l41_hda_i2c_probe(struct i2c_client *clt)
+{
+ const char *device_name;
+
+ /*
+ * Compare against the device name so it works for SPI, normal ACPI
+ * and for ACPI by serial-multi-instantiate matching cases.
+ */
+ if (strstr(dev_name(&clt->dev), "CLSA0100"))
+ device_name = "CLSA0100";
+ else if (strstr(dev_name(&clt->dev), "CLSA0101"))
+ device_name = "CLSA0101";
+ else if (strstr(dev_name(&clt->dev), "CSC3551"))
+ device_name = "CSC3551";
+ else
+ return -ENODEV;
+
+ return cs35l41_hda_probe(&clt->dev, device_name, clt->addr, clt->irq,
+ devm_regmap_init_i2c(clt, &cs35l41_regmap_i2c), I2C);
+}
+
+static void cs35l41_hda_i2c_remove(struct i2c_client *clt)
+{
+ cs35l41_hda_remove(&clt->dev);
+}
+
+static const struct i2c_device_id cs35l41_hda_i2c_id[] = {
+ { "cs35l41-hda" },
+ {}
+};
+
+static const struct acpi_device_id cs35l41_acpi_hda_match[] = {
+ {"CLSA0100", 0 },
+ {"CLSA0101", 0 },
+ {"CSC3551", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_hda_match);
+
+static struct i2c_driver cs35l41_i2c_driver = {
+ .driver = {
+ .name = "cs35l41-hda",
+ .acpi_match_table = cs35l41_acpi_hda_match,
+ .pm = &cs35l41_hda_pm_ops,
+ },
+ .id_table = cs35l41_hda_i2c_id,
+ .probe = cs35l41_hda_i2c_probe,
+ .remove = cs35l41_hda_i2c_remove,
+};
+module_i2c_driver(cs35l41_i2c_driver);
+
+MODULE_DESCRIPTION("HDA CS35L41 driver");
+MODULE_IMPORT_NS("SND_HDA_SCODEC_CS35L41");
+MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda_property.c b/sound/hda/codecs/side-codecs/cs35l41_hda_property.c
new file mode 100644
index 000000000000..16d5ea77192f
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l41_hda_property.c
@@ -0,0 +1,582 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS35L41 ALSA HDA Property driver
+//
+// Copyright 2023 Cirrus Logic, Inc.
+//
+// Author: Stefan Binding <sbinding@opensource.cirrus.com>
+
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/string.h>
+#include "cs35l41_hda_property.h"
+#include <linux/spi/spi.h>
+
+#define MAX_AMPS 4
+
+struct cs35l41_config {
+ const char *ssid;
+ int num_amps;
+ enum {
+ INTERNAL,
+ EXTERNAL
+ } boost_type;
+ u8 channel[MAX_AMPS];
+ int reset_gpio_index; /* -1 if no reset gpio */
+ int spkid_gpio_index; /* -1 if no spkid gpio */
+ int cs_gpio_index; /* -1 if no cs gpio, or cs-gpios already exists, max num amps == 2 */
+ int boost_ind_nanohenry; /* Required if boost_type == Internal */
+ int boost_peak_milliamp; /* Required if boost_type == Internal */
+ int boost_cap_microfarad; /* Required if boost_type == Internal */
+};
+
+static const struct cs35l41_config cs35l41_config_table[] = {
+ { "10251826", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "1025182C", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "10251844", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "10280B27", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10280B28", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10280BEB", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 0, 0, 0 },
+ { "10280C4D", 4, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT }, 0, 1, -1, 1000, 4500, 24 },
+/*
+ * Device 103C89C6 does have _DSD, however it is setup to use the wrong boost type.
+ * We can override the _DSD to correct the boost type here.
+ * Since this laptop has valid ACPI, we do not need to handle cs-gpios, since that already exists
+ * in the ACPI. The Reset GPIO is also valid, so we can use the Reset defined in _DSD.
+ */
+ { "103C89C6", 2, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, -1, -1, -1, 1000, 4500, 24 },
+ { "103C8A28", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A29", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A2A", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A2B", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A2C", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A2D", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A2E", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A30", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A31", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8A6E", 4, EXTERNAL, { CS35L41_LEFT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_RIGHT }, 0, -1, -1, 0, 0, 0 },
+ { "103C8BB3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BB4", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BDF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE0", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE1", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE2", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE5", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE6", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE7", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE8", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8BE9", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8B3A", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8C15", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 },
+ { "103C8C16", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 },
+ { "103C8C17", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4000, 24 },
+ { "103C8C4D", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8C4E", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8C4F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8C50", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8C51", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8CDD", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4100, 24 },
+ { "103C8CDE", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 3900, 24 },
+ { "104312AF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431433", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431463", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431473", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
+ { "10431483", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
+ { "10431493", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "104314D3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "104314E3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431503", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431533", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431573", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431663", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 1000, 4500, 24 },
+ { "10431683", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "104316A3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "104316D3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "104316F3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "104317F3", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431863", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "104318D3", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "10431A83", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431B93", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431C9F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431CAF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431CCF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431CDF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431CEF", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10431D1F", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431DA2", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "10431E02", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "10431E12", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "10431EE2", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "10431F12", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 1000, 4500, 24 },
+ { "10431F1F", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, -1, 0, 0, 0, 0 },
+ { "10431F62", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 0, 0, 0 },
+ { "10433A20", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10433A30", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10433A40", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10433A50", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "10433A60", 2, INTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 1, 2, 0, 1000, 4500, 24 },
+ { "17AA3865", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "17AA3866", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "17AA386E", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
+ { "17AA386F", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "17AA3877", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "17AA3878", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 },
+ { "17AA38A9", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
+ { "17AA38AB", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
+ { "17AA38B4", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "17AA38B5", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "17AA38B6", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "17AA38B7", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 1, -1, 0, 0, 0 },
+ { "17AA38C7", 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 },
+ { "17AA38C8", 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 },
+ { "17AA38F9", 2, EXTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
+ { "17AA38FA", 2, EXTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, 0, 0 }, 0, 2, -1, 0, 0, 0 },
+ { "17AA3929", 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 },
+ { "17AA392B", 4, INTERNAL, { CS35L41_RIGHT, CS35L41_LEFT, CS35L41_RIGHT, CS35L41_LEFT }, 0, 2, -1, 1000, 4500, 24 },
+ {}
+};
+
+static int cs35l41_add_gpios(struct cs35l41_hda *cs35l41, struct device *physdev, int reset_gpio,
+ int spkid_gpio, int cs_gpio_index, int num_amps)
+{
+ struct acpi_gpio_mapping *gpio_mapping = NULL;
+ struct acpi_gpio_params *reset_gpio_params = NULL;
+ struct acpi_gpio_params *spkid_gpio_params = NULL;
+ struct acpi_gpio_params *cs_gpio_params = NULL;
+ unsigned int num_entries = 0;
+ unsigned int reset_index, spkid_index, csgpio_index;
+ int i;
+
+ /*
+ * GPIO Mapping only needs to be done once, since it would be available for subsequent amps
+ */
+ if (cs35l41->dacpi->driver_gpios)
+ return 0;
+
+ if (reset_gpio >= 0) {
+ reset_index = num_entries;
+ num_entries++;
+ }
+
+ if (spkid_gpio >= 0) {
+ spkid_index = num_entries;
+ num_entries++;
+ }
+
+ if ((cs_gpio_index >= 0) && (num_amps == 2)) {
+ csgpio_index = num_entries;
+ num_entries++;
+ }
+
+ if (!num_entries)
+ return 0;
+
+ /* must include termination entry */
+ num_entries++;
+
+ gpio_mapping = devm_kcalloc(physdev, num_entries, sizeof(struct acpi_gpio_mapping),
+ GFP_KERNEL);
+
+ if (!gpio_mapping)
+ goto err;
+
+ if (reset_gpio >= 0) {
+ gpio_mapping[reset_index].name = "reset-gpios";
+ reset_gpio_params = devm_kcalloc(physdev, num_amps, sizeof(struct acpi_gpio_params),
+ GFP_KERNEL);
+ if (!reset_gpio_params)
+ goto err;
+
+ for (i = 0; i < num_amps; i++)
+ reset_gpio_params[i].crs_entry_index = reset_gpio;
+
+ gpio_mapping[reset_index].data = reset_gpio_params;
+ gpio_mapping[reset_index].size = num_amps;
+ }
+
+ if (spkid_gpio >= 0) {
+ gpio_mapping[spkid_index].name = "spk-id-gpios";
+ spkid_gpio_params = devm_kcalloc(physdev, num_amps, sizeof(struct acpi_gpio_params),
+ GFP_KERNEL);
+ if (!spkid_gpio_params)
+ goto err;
+
+ for (i = 0; i < num_amps; i++)
+ spkid_gpio_params[i].crs_entry_index = spkid_gpio;
+
+ gpio_mapping[spkid_index].data = spkid_gpio_params;
+ gpio_mapping[spkid_index].size = num_amps;
+ }
+
+ if ((cs_gpio_index >= 0) && (num_amps == 2)) {
+ gpio_mapping[csgpio_index].name = "cs-gpios";
+ /* only one GPIO CS is supported without using _DSD, obtained using index 0 */
+ cs_gpio_params = devm_kzalloc(physdev, sizeof(struct acpi_gpio_params), GFP_KERNEL);
+ if (!cs_gpio_params)
+ goto err;
+
+ cs_gpio_params->crs_entry_index = cs_gpio_index;
+
+ gpio_mapping[csgpio_index].data = cs_gpio_params;
+ gpio_mapping[csgpio_index].size = 1;
+ }
+
+ return devm_acpi_dev_add_driver_gpios(physdev, gpio_mapping);
+err:
+ devm_kfree(physdev, gpio_mapping);
+ devm_kfree(physdev, reset_gpio_params);
+ devm_kfree(physdev, spkid_gpio_params);
+ devm_kfree(physdev, cs_gpio_params);
+ return -ENOMEM;
+}
+
+static int generic_dsd_config(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid)
+{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+ const struct cs35l41_config *cfg;
+ struct gpio_desc *cs_gpiod;
+ struct spi_device *spi;
+ bool dsd_found;
+ int ret;
+ int i;
+
+ for (cfg = cs35l41_config_table; cfg->ssid; cfg++) {
+ if (!strcasecmp(cfg->ssid, cs35l41->acpi_subsystem_id))
+ break;
+ }
+
+ if (!cfg->ssid)
+ return -ENOENT;
+
+ if (!cs35l41->dacpi || cs35l41->dacpi != ACPI_COMPANION(physdev)) {
+ dev_err(cs35l41->dev, "ACPI Device does not match, cannot override _DSD.\n");
+ return -ENODEV;
+ }
+
+ dev_info(cs35l41->dev, "Adding DSD properties for %s\n", cs35l41->acpi_subsystem_id);
+
+ dsd_found = acpi_dev_has_props(cs35l41->dacpi);
+
+ if (!dsd_found) {
+ ret = cs35l41_add_gpios(cs35l41, physdev, cfg->reset_gpio_index,
+ cfg->spkid_gpio_index, cfg->cs_gpio_index,
+ cfg->num_amps);
+ if (ret) {
+ dev_err(cs35l41->dev, "Error adding GPIO mapping: %d\n", ret);
+ return ret;
+ }
+ } else if (cfg->reset_gpio_index >= 0 || cfg->spkid_gpio_index >= 0) {
+ dev_warn(cs35l41->dev, "Cannot add Reset/Speaker ID/SPI CS GPIO Mapping, "
+ "_DSD already exists.\n");
+ }
+
+ if (cs35l41->control_bus == SPI) {
+ cs35l41->index = id;
+
+ /*
+ * Manually set the Chip Select for the second amp <cs_gpio_index> in the node.
+ * This is only supported for systems with 2 amps, since we cannot expand the
+ * default number of chip selects without using cs-gpios
+ * The CS GPIO must be set high prior to communicating with the first amp (which
+ * uses a native chip select), to ensure the second amp does not clash with the
+ * first.
+ */
+ if (IS_ENABLED(CONFIG_SPI) && cfg->cs_gpio_index >= 0) {
+ spi = to_spi_device(cs35l41->dev);
+
+ if (cfg->num_amps != 2) {
+ dev_warn(cs35l41->dev,
+ "Cannot update SPI CS, Number of Amps (%d) != 2\n",
+ cfg->num_amps);
+ } else if (dsd_found) {
+ dev_warn(cs35l41->dev,
+ "Cannot update SPI CS, _DSD already exists.\n");
+ } else {
+ /*
+ * This is obtained using driver_gpios, since only one GPIO for CS
+ * exists, this can be obtained using index 0.
+ */
+ cs_gpiod = gpiod_get_index(physdev, "cs", 0, GPIOD_OUT_LOW);
+ if (IS_ERR(cs_gpiod)) {
+ dev_err(cs35l41->dev,
+ "Unable to get Chip Select GPIO descriptor\n");
+ return PTR_ERR(cs_gpiod);
+ }
+ if (id == 1) {
+ spi_set_csgpiod(spi, 0, cs_gpiod);
+ cs35l41->cs_gpio = cs_gpiod;
+ } else {
+ gpiod_set_value_cansleep(cs_gpiod, true);
+ gpiod_put(cs_gpiod);
+ }
+ spi_setup(spi);
+ }
+ }
+ } else {
+ if (cfg->num_amps > 2)
+ /*
+ * i2c addresses for 3/4 amps are used in order: 0x40, 0x41, 0x42, 0x43,
+ * subtracting 0x40 would give zero-based index
+ */
+ cs35l41->index = id - 0x40;
+ else
+ /* i2c addr 0x40 for first amp (always), 0x41/0x42 for 2nd amp */
+ cs35l41->index = id == 0x40 ? 0 : 1;
+ }
+
+ cs35l41->reset_gpio = fwnode_gpiod_get_index(acpi_fwnode_handle(cs35l41->dacpi), "reset",
+ cs35l41->index, GPIOD_OUT_LOW,
+ "cs35l41-reset");
+ cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, cs35l41->index, cfg->num_amps, -1);
+
+ hw_cfg->spk_pos = cfg->channel[cs35l41->index];
+
+ cs35l41->channel_index = 0;
+ for (i = 0; i < cs35l41->index; i++)
+ if (cfg->channel[i] == hw_cfg->spk_pos)
+ cs35l41->channel_index++;
+
+ if (cfg->boost_type == INTERNAL) {
+ hw_cfg->bst_type = CS35L41_INT_BOOST;
+ hw_cfg->bst_ind = cfg->boost_ind_nanohenry;
+ hw_cfg->bst_ipk = cfg->boost_peak_milliamp;
+ hw_cfg->bst_cap = cfg->boost_cap_microfarad;
+ hw_cfg->gpio1.func = CS35L41_NOT_USED;
+ hw_cfg->gpio1.valid = true;
+ } else {
+ hw_cfg->bst_type = CS35L41_EXT_BOOST;
+ hw_cfg->bst_ind = -1;
+ hw_cfg->bst_ipk = -1;
+ hw_cfg->bst_cap = -1;
+ hw_cfg->gpio1.func = CS35l41_VSPK_SWITCH;
+ hw_cfg->gpio1.valid = true;
+ }
+
+ hw_cfg->gpio2.func = CS35L41_INTERRUPT;
+ hw_cfg->gpio2.valid = true;
+ hw_cfg->valid = true;
+
+ return 0;
+}
+
+/*
+ * Systems 103C8C66, 103C8C67, 103C8C68, 103C8C6A use a dual speaker id system - each speaker has
+ * its own speaker id.
+ */
+static int hp_i2c_int_2amp_dual_spkid(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid)
+{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+
+ /* If _DSD exists for this laptop, we cannot support it through here */
+ if (acpi_dev_has_props(cs35l41->dacpi))
+ return -ENOENT;
+
+ /* check I2C address to assign the index */
+ cs35l41->index = id == 0x40 ? 0 : 1;
+ cs35l41->channel_index = 0;
+ cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH);
+ if (cs35l41->index == 0)
+ cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 1);
+ else
+ cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2);
+ hw_cfg->spk_pos = cs35l41->index;
+ hw_cfg->gpio2.func = CS35L41_INTERRUPT;
+ hw_cfg->gpio2.valid = true;
+ hw_cfg->valid = true;
+
+ hw_cfg->bst_type = CS35L41_INT_BOOST;
+ hw_cfg->bst_ind = 1000;
+ hw_cfg->bst_ipk = 4100;
+ hw_cfg->bst_cap = 24;
+ hw_cfg->gpio1.func = CS35L41_NOT_USED;
+ hw_cfg->gpio1.valid = true;
+
+ return 0;
+}
+
+/*
+ * Device CLSA010(0/1) doesn't have _DSD so a gpiod_get by the label reset won't work.
+ * And devices created by serial-multi-instantiate don't have their device struct
+ * pointing to the correct fwnode, so acpi_dev must be used here.
+ * And devm functions expect that the device requesting the resource has the correct
+ * fwnode.
+ */
+static int lenovo_legion_no_acpi(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid)
+{
+ struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
+
+ /* check I2C address to assign the index */
+ cs35l41->index = id == 0x40 ? 0 : 1;
+ cs35l41->channel_index = 0;
+ cs35l41->reset_gpio = gpiod_get_index(physdev, NULL, 0, GPIOD_OUT_HIGH);
+ cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, 0, 0, 2);
+ hw_cfg->spk_pos = cs35l41->index;
+ hw_cfg->gpio2.func = CS35L41_INTERRUPT;
+ hw_cfg->gpio2.valid = true;
+ hw_cfg->valid = true;
+
+ if (strcmp(hid, "CLSA0100") == 0) {
+ hw_cfg->bst_type = CS35L41_EXT_BOOST_NO_VSPK_SWITCH;
+ } else if (strcmp(hid, "CLSA0101") == 0) {
+ hw_cfg->bst_type = CS35L41_EXT_BOOST;
+ hw_cfg->gpio1.func = CS35l41_VSPK_SWITCH;
+ hw_cfg->gpio1.valid = true;
+ }
+
+ return 0;
+}
+
+static int missing_speaker_id_gpio2(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid)
+{
+ int ret;
+
+ ret = cs35l41_add_gpios(cs35l41, physdev, -1, 2, -1, 2);
+ if (ret) {
+ dev_err(cs35l41->dev, "Error adding GPIO mapping: %d\n", ret);
+ return ret;
+ }
+
+ return cs35l41_hda_parse_acpi(cs35l41, physdev, id);
+}
+
+struct cs35l41_prop_model {
+ const char *hid;
+ const char *ssid;
+ int (*add_prop)(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid);
+};
+
+static const struct cs35l41_prop_model cs35l41_prop_model_table[] = {
+ { "CLSA0100", NULL, lenovo_legion_no_acpi },
+ { "CLSA0101", NULL, lenovo_legion_no_acpi },
+ { "CSC3551", "10251826", generic_dsd_config },
+ { "CSC3551", "1025182C", generic_dsd_config },
+ { "CSC3551", "10251844", generic_dsd_config },
+ { "CSC3551", "10280B27", generic_dsd_config },
+ { "CSC3551", "10280B28", generic_dsd_config },
+ { "CSC3551", "10280BEB", generic_dsd_config },
+ { "CSC3551", "10280C4D", generic_dsd_config },
+ { "CSC3551", "103C89C6", generic_dsd_config },
+ { "CSC3551", "103C8A28", generic_dsd_config },
+ { "CSC3551", "103C8A29", generic_dsd_config },
+ { "CSC3551", "103C8A2A", generic_dsd_config },
+ { "CSC3551", "103C8A2B", generic_dsd_config },
+ { "CSC3551", "103C8A2C", generic_dsd_config },
+ { "CSC3551", "103C8A2D", generic_dsd_config },
+ { "CSC3551", "103C8A2E", generic_dsd_config },
+ { "CSC3551", "103C8A30", generic_dsd_config },
+ { "CSC3551", "103C8A31", generic_dsd_config },
+ { "CSC3551", "103C8A6E", generic_dsd_config },
+ { "CSC3551", "103C8BB3", generic_dsd_config },
+ { "CSC3551", "103C8BB4", generic_dsd_config },
+ { "CSC3551", "103C8BDD", generic_dsd_config },
+ { "CSC3551", "103C8BDE", generic_dsd_config },
+ { "CSC3551", "103C8BDF", generic_dsd_config },
+ { "CSC3551", "103C8BE0", generic_dsd_config },
+ { "CSC3551", "103C8BE1", generic_dsd_config },
+ { "CSC3551", "103C8BE2", generic_dsd_config },
+ { "CSC3551", "103C8BE3", generic_dsd_config },
+ { "CSC3551", "103C8BE5", generic_dsd_config },
+ { "CSC3551", "103C8BE6", generic_dsd_config },
+ { "CSC3551", "103C8BE7", generic_dsd_config },
+ { "CSC3551", "103C8BE8", generic_dsd_config },
+ { "CSC3551", "103C8BE9", generic_dsd_config },
+ { "CSC3551", "103C8B3A", generic_dsd_config },
+ { "CSC3551", "103C8C15", generic_dsd_config },
+ { "CSC3551", "103C8C16", generic_dsd_config },
+ { "CSC3551", "103C8C17", generic_dsd_config },
+ { "CSC3551", "103C8C4D", generic_dsd_config },
+ { "CSC3551", "103C8C4E", generic_dsd_config },
+ { "CSC3551", "103C8C4F", generic_dsd_config },
+ { "CSC3551", "103C8C50", generic_dsd_config },
+ { "CSC3551", "103C8C51", generic_dsd_config },
+ { "CSC3551", "103C8C66", hp_i2c_int_2amp_dual_spkid },
+ { "CSC3551", "103C8C67", hp_i2c_int_2amp_dual_spkid },
+ { "CSC3551", "103C8C68", hp_i2c_int_2amp_dual_spkid },
+ { "CSC3551", "103C8C6A", hp_i2c_int_2amp_dual_spkid },
+ { "CSC3551", "103C8CDD", generic_dsd_config },
+ { "CSC3551", "103C8CDE", generic_dsd_config },
+ { "CSC3551", "104312AF", generic_dsd_config },
+ { "CSC3551", "10431433", generic_dsd_config },
+ { "CSC3551", "10431463", generic_dsd_config },
+ { "CSC3551", "10431473", generic_dsd_config },
+ { "CSC3551", "10431483", generic_dsd_config },
+ { "CSC3551", "10431493", generic_dsd_config },
+ { "CSC3551", "104314D3", generic_dsd_config },
+ { "CSC3551", "104314E3", generic_dsd_config },
+ { "CSC3551", "10431503", generic_dsd_config },
+ { "CSC3551", "10431533", generic_dsd_config },
+ { "CSC3551", "10431573", generic_dsd_config },
+ { "CSC3551", "10431663", generic_dsd_config },
+ { "CSC3551", "10431683", generic_dsd_config },
+ { "CSC3551", "104316A3", generic_dsd_config },
+ { "CSC3551", "104316D3", generic_dsd_config },
+ { "CSC3551", "104316F3", generic_dsd_config },
+ { "CSC3551", "104317F3", generic_dsd_config },
+ { "CSC3551", "10431863", generic_dsd_config },
+ { "CSC3551", "104318D3", generic_dsd_config },
+ { "CSC3551", "10431A63", missing_speaker_id_gpio2 },
+ { "CSC3551", "10431A83", generic_dsd_config },
+ { "CSC3551", "10431B93", generic_dsd_config },
+ { "CSC3551", "10431C9F", generic_dsd_config },
+ { "CSC3551", "10431CAF", generic_dsd_config },
+ { "CSC3551", "10431CCF", generic_dsd_config },
+ { "CSC3551", "10431CDF", generic_dsd_config },
+ { "CSC3551", "10431CEF", generic_dsd_config },
+ { "CSC3551", "10431D1F", generic_dsd_config },
+ { "CSC3551", "10431DA2", generic_dsd_config },
+ { "CSC3551", "10431E02", generic_dsd_config },
+ { "CSC3551", "10431E12", generic_dsd_config },
+ { "CSC3551", "10431EE2", generic_dsd_config },
+ { "CSC3551", "10431F12", generic_dsd_config },
+ { "CSC3551", "10431F1F", generic_dsd_config },
+ { "CSC3551", "10431F62", generic_dsd_config },
+ { "CSC3551", "10433A20", generic_dsd_config },
+ { "CSC3551", "10433A30", generic_dsd_config },
+ { "CSC3551", "10433A40", generic_dsd_config },
+ { "CSC3551", "10433A50", generic_dsd_config },
+ { "CSC3551", "10433A60", generic_dsd_config },
+ { "CSC3551", "17AA3865", generic_dsd_config },
+ { "CSC3551", "17AA3866", generic_dsd_config },
+ { "CSC3551", "17AA386E", generic_dsd_config },
+ { "CSC3551", "17AA386F", generic_dsd_config },
+ { "CSC3551", "17AA3877", generic_dsd_config },
+ { "CSC3551", "17AA3878", generic_dsd_config },
+ { "CSC3551", "17AA38A9", generic_dsd_config },
+ { "CSC3551", "17AA38AB", generic_dsd_config },
+ { "CSC3551", "17AA38B4", generic_dsd_config },
+ { "CSC3551", "17AA38B5", generic_dsd_config },
+ { "CSC3551", "17AA38B6", generic_dsd_config },
+ { "CSC3551", "17AA38B7", generic_dsd_config },
+ { "CSC3551", "17AA38C7", generic_dsd_config },
+ { "CSC3551", "17AA38C8", generic_dsd_config },
+ { "CSC3551", "17AA38F9", generic_dsd_config },
+ { "CSC3551", "17AA38FA", generic_dsd_config },
+ { "CSC3551", "17AA3929", generic_dsd_config },
+ { "CSC3551", "17AA392B", generic_dsd_config },
+ {}
+};
+
+int cs35l41_add_dsd_properties(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid)
+{
+ const struct cs35l41_prop_model *model;
+
+ for (model = cs35l41_prop_model_table; model->hid; model++) {
+ if (!strcmp(model->hid, hid) &&
+ (!model->ssid ||
+ (cs35l41->acpi_subsystem_id &&
+ !strcasecmp(model->ssid, cs35l41->acpi_subsystem_id))))
+ return model->add_prop(cs35l41, physdev, id, hid);
+ }
+
+ return -ENOENT;
+}
diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda_property.h b/sound/hda/codecs/side-codecs/cs35l41_hda_property.h
new file mode 100644
index 000000000000..fd834042e2fd
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l41_hda_property.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * CS35L41 ALSA HDA Property driver
+ *
+ * Copyright 2023 Cirrus Logic, Inc.
+ *
+ * Author: Stefan Binding <sbinding@opensource.cirrus.com>
+ */
+
+#ifndef CS35L41_HDA_PROP_H
+#define CS35L41_HDA_PROP_H
+
+#include <linux/device.h>
+#include "cs35l41_hda.h"
+
+int cs35l41_add_dsd_properties(struct cs35l41_hda *cs35l41, struct device *physdev, int id,
+ const char *hid);
+#endif /* CS35L41_HDA_PROP_H */
diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda_spi.c b/sound/hda/codecs/side-codecs/cs35l41_hda_spi.c
new file mode 100644
index 000000000000..2acbaf8467a0
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l41_hda_spi.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS35l41 HDA SPI driver
+//
+// Copyright 2021 Cirrus Logic, Inc.
+//
+// Author: Lucas Tanure <tanureal@opensource.cirrus.com>
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include "cs35l41_hda.h"
+
+static int cs35l41_hda_spi_probe(struct spi_device *spi)
+{
+ const char *device_name;
+
+ /*
+ * Compare against the device name so it works for SPI, normal ACPI
+ * and for ACPI by serial-multi-instantiate matching cases.
+ */
+ if (strstr(dev_name(&spi->dev), "CSC3551"))
+ device_name = "CSC3551";
+ else
+ return -ENODEV;
+
+ return cs35l41_hda_probe(&spi->dev, device_name, spi_get_chipselect(spi, 0), spi->irq,
+ devm_regmap_init_spi(spi, &cs35l41_regmap_spi), SPI);
+}
+
+static void cs35l41_hda_spi_remove(struct spi_device *spi)
+{
+ cs35l41_hda_remove(&spi->dev);
+}
+
+static const struct spi_device_id cs35l41_hda_spi_id[] = {
+ { "cs35l41-hda", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, cs35l41_hda_spi_id);
+
+static const struct acpi_device_id cs35l41_acpi_hda_match[] = {
+ { "CSC3551", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, cs35l41_acpi_hda_match);
+
+static struct spi_driver cs35l41_spi_driver = {
+ .driver = {
+ .name = "cs35l41-hda",
+ .acpi_match_table = cs35l41_acpi_hda_match,
+ .pm = &cs35l41_hda_pm_ops,
+ },
+ .id_table = cs35l41_hda_spi_id,
+ .probe = cs35l41_hda_spi_probe,
+ .remove = cs35l41_hda_spi_remove,
+};
+module_spi_driver(cs35l41_spi_driver);
+
+MODULE_DESCRIPTION("HDA CS35L41 driver");
+MODULE_IMPORT_NS("SND_HDA_SCODEC_CS35L41");
+MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.c b/sound/hda/codecs/side-codecs/cs35l56_hda.c
new file mode 100644
index 000000000000..f7ba92e11957
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l56_hda.c
@@ -0,0 +1,1286 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// HDA audio driver for Cirrus Logic CS35L56 smart amp
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+//
+
+#include <linux/acpi.h>
+#include <linux/debugfs.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/cs-amp-lib.h>
+#include <sound/hda_codec.h>
+#include <sound/tlv.h>
+#include "cirrus_scodec.h"
+#include "cs35l56_hda.h"
+#include "hda_component.h"
+#include "../generic.h"
+
+ /*
+ * The cs35l56_hda_dai_config[] reg sequence configures the device as
+ * ASP1_BCLK_FREQ = 3.072 MHz
+ * ASP1_RX_WIDTH = 32 cycles per slot, ASP1_TX_WIDTH = 32 cycles per slot, ASP1_FMT = I2S
+ * ASP1_DOUT_HIZ_CONTROL = Hi-Z during unused timeslots
+ * ASP1_RX_WL = 24 bits per sample
+ * ASP1_TX_WL = 24 bits per sample
+ * ASP1_RXn_EN 1..3 and ASP1_TXn_EN 1..4 disabled
+ *
+ * Override any Windows-specific mixer settings applied by the firmware.
+ */
+static const struct reg_sequence cs35l56_hda_dai_config[] = {
+ { CS35L56_ASP1_CONTROL1, 0x00000021 },
+ { CS35L56_ASP1_CONTROL2, 0x20200200 },
+ { CS35L56_ASP1_CONTROL3, 0x00000003 },
+ { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 },
+ { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 },
+ { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 },
+ { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 },
+ { CS35L56_ASP1_ENABLES1, 0x00000000 },
+ { CS35L56_ASP1TX1_INPUT, 0x00000018 },
+ { CS35L56_ASP1TX2_INPUT, 0x00000019 },
+ { CS35L56_ASP1TX3_INPUT, 0x00000020 },
+ { CS35L56_ASP1TX4_INPUT, 0x00000028 },
+
+};
+
+static void cs35l56_hda_wait_dsp_ready(struct cs35l56_hda *cs35l56)
+{
+ /* Wait for patching to complete */
+ flush_work(&cs35l56->dsp_work);
+}
+
+static void cs35l56_hda_play(struct cs35l56_hda *cs35l56)
+{
+ unsigned int val;
+ int ret;
+
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
+ pm_runtime_get_sync(cs35l56->base.dev);
+ ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PLAY);
+ if (ret == 0) {
+ /* Wait for firmware to enter PS0 power state */
+ ret = regmap_read_poll_timeout(cs35l56->base.regmap,
+ cs35l56->base.fw_reg->transducer_actual_ps,
+ val, (val == CS35L56_PS0),
+ CS35L56_PS0_POLL_US,
+ CS35L56_PS0_TIMEOUT_US);
+ if (ret)
+ dev_warn(cs35l56->base.dev, "PS0 wait failed: %d\n", ret);
+ }
+ regmap_set_bits(cs35l56->base.regmap, CS35L56_ASP1_ENABLES1,
+ BIT(CS35L56_ASP_RX1_EN_SHIFT) | BIT(CS35L56_ASP_RX2_EN_SHIFT) |
+ cs35l56->asp_tx_mask);
+ cs35l56->playing = true;
+}
+
+static void cs35l56_hda_pause(struct cs35l56_hda *cs35l56)
+{
+ cs35l56->playing = false;
+ cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PAUSE);
+ regmap_clear_bits(cs35l56->base.regmap, CS35L56_ASP1_ENABLES1,
+ BIT(CS35L56_ASP_RX1_EN_SHIFT) | BIT(CS35L56_ASP_RX2_EN_SHIFT) |
+ BIT(CS35L56_ASP_TX1_EN_SHIFT) | BIT(CS35L56_ASP_TX2_EN_SHIFT) |
+ BIT(CS35L56_ASP_TX3_EN_SHIFT) | BIT(CS35L56_ASP_TX4_EN_SHIFT));
+
+ pm_runtime_put_autosuspend(cs35l56->base.dev);
+}
+
+static void cs35l56_hda_playback_hook(struct device *dev, int action)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ dev_dbg(cs35l56->base.dev, "%s()%d: action: %d\n", __func__, __LINE__, action);
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_PREPARE:
+ if (cs35l56->playing)
+ break;
+
+ /* If we're suspended: flag that resume should start playback */
+ if (cs35l56->suspended) {
+ cs35l56->playing = true;
+ break;
+ }
+
+ cs35l56_hda_play(cs35l56);
+ break;
+ case HDA_GEN_PCM_ACT_CLEANUP:
+ if (!cs35l56->playing)
+ break;
+
+ cs35l56_hda_pause(cs35l56);
+ break;
+ default:
+ break;
+ }
+}
+
+static int cs35l56_hda_runtime_suspend(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ if (cs35l56->cs_dsp.booted)
+ cs_dsp_stop(&cs35l56->cs_dsp);
+
+ return cs35l56_runtime_suspend_common(&cs35l56->base);
+}
+
+static int cs35l56_hda_runtime_resume(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = cs35l56_runtime_resume_common(&cs35l56->base, false);
+ if (ret < 0)
+ return ret;
+
+ if (cs35l56->cs_dsp.booted) {
+ ret = cs_dsp_run(&cs35l56->cs_dsp);
+ if (ret) {
+ dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret);
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE);
+ regmap_write(cs35l56->base.regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
+ CS35L56_MBOX_CMD_HIBERNATE_NOW);
+
+ regcache_cache_only(cs35l56->base.regmap, true);
+
+ return ret;
+}
+
+static int cs35l56_hda_mixer_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = CS35L56_NUM_INPUT_SRC;
+ if (uinfo->value.enumerated.item >= CS35L56_NUM_INPUT_SRC)
+ uinfo->value.enumerated.item = CS35L56_NUM_INPUT_SRC - 1;
+ strscpy(uinfo->value.enumerated.name, cs35l56_tx_input_texts[uinfo->value.enumerated.item],
+ sizeof(uinfo->value.enumerated.name));
+
+ return 0;
+}
+
+static int cs35l56_hda_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol);
+ unsigned int reg_val;
+ int i;
+
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
+ regmap_read(cs35l56->base.regmap, kcontrol->private_value, &reg_val);
+ reg_val &= CS35L56_ASP_TXn_SRC_MASK;
+
+ for (i = 0; i < CS35L56_NUM_INPUT_SRC; ++i) {
+ if (cs35l56_tx_input_values[i] == reg_val) {
+ ucontrol->value.enumerated.item[0] = i;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int cs35l56_hda_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol);
+ unsigned int item = ucontrol->value.enumerated.item[0];
+ bool changed;
+
+ if (item >= CS35L56_NUM_INPUT_SRC)
+ return -EINVAL;
+
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
+ regmap_update_bits_check(cs35l56->base.regmap, kcontrol->private_value,
+ CS35L56_INPUT_MASK, cs35l56_tx_input_values[item],
+ &changed);
+
+ return changed;
+}
+
+static int cs35l56_hda_posture_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = CS35L56_MAIN_POSTURE_MIN;
+ uinfo->value.integer.max = CS35L56_MAIN_POSTURE_MAX;
+ return 0;
+}
+
+static int cs35l56_hda_posture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol);
+ unsigned int pos;
+ int ret;
+
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
+ ret = regmap_read(cs35l56->base.regmap,
+ cs35l56->base.fw_reg->posture_number, &pos);
+ if (ret)
+ return ret;
+
+ ucontrol->value.integer.value[0] = pos;
+
+ return 0;
+}
+
+static int cs35l56_hda_posture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol);
+ unsigned long pos = ucontrol->value.integer.value[0];
+ bool changed;
+ int ret;
+
+ if ((pos < CS35L56_MAIN_POSTURE_MIN) ||
+ (pos > CS35L56_MAIN_POSTURE_MAX))
+ return -EINVAL;
+
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
+ ret = regmap_update_bits_check(cs35l56->base.regmap, cs35l56->base.fw_reg->posture_number,
+ CS35L56_MAIN_POSTURE_MASK, pos, &changed);
+ if (ret)
+ return ret;
+
+ return changed;
+}
+
+static const struct {
+ const char *name;
+ unsigned int reg;
+} cs35l56_hda_mixer_controls[] = {
+ { "ASP1 TX1 Source", CS35L56_ASP1TX1_INPUT },
+ { "ASP1 TX2 Source", CS35L56_ASP1TX2_INPUT },
+ { "ASP1 TX3 Source", CS35L56_ASP1TX3_INPUT },
+ { "ASP1 TX4 Source", CS35L56_ASP1TX4_INPUT },
+};
+
+static const DECLARE_TLV_DB_SCALE(cs35l56_hda_vol_tlv, -10000, 25, 0);
+
+static int cs35l56_hda_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.step = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = CS35L56_MAIN_RENDER_USER_VOLUME_MAX -
+ CS35L56_MAIN_RENDER_USER_VOLUME_MIN;
+
+ return 0;
+}
+
+static int cs35l56_hda_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol);
+ unsigned int raw_vol;
+ int vol;
+ int ret;
+
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
+ ret = regmap_read(cs35l56->base.regmap, cs35l56->base.fw_reg->user_volume, &raw_vol);
+
+ if (ret)
+ return ret;
+
+ vol = (s16)(raw_vol & 0xFFFF);
+ vol >>= CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT;
+
+ if (vol & BIT(CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT))
+ vol |= ~((int)(BIT(CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT) - 1));
+
+ ucontrol->value.integer.value[0] = vol - CS35L56_MAIN_RENDER_USER_VOLUME_MIN;
+
+ return 0;
+}
+
+static int cs35l56_hda_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct cs35l56_hda *cs35l56 = snd_kcontrol_chip(kcontrol);
+ long vol = ucontrol->value.integer.value[0];
+ unsigned int raw_vol;
+ bool changed;
+ int ret;
+
+ if ((vol < 0) || (vol > (CS35L56_MAIN_RENDER_USER_VOLUME_MAX -
+ CS35L56_MAIN_RENDER_USER_VOLUME_MIN)))
+ return -EINVAL;
+
+ raw_vol = (vol + CS35L56_MAIN_RENDER_USER_VOLUME_MIN) <<
+ CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT;
+
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
+ ret = regmap_update_bits_check(cs35l56->base.regmap, cs35l56->base.fw_reg->user_volume,
+ CS35L56_MAIN_RENDER_USER_VOLUME_MASK, raw_vol, &changed);
+ if (ret)
+ return ret;
+
+ return changed;
+}
+
+static void cs35l56_hda_create_controls(struct cs35l56_hda *cs35l56)
+{
+ struct snd_kcontrol_new ctl_template = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = cs35l56_hda_posture_info,
+ .get = cs35l56_hda_posture_get,
+ .put = cs35l56_hda_posture_put,
+ };
+ char name[64];
+ int i;
+
+ snprintf(name, sizeof(name), "%s Posture Number", cs35l56->amp_name);
+ ctl_template.name = name;
+ cs35l56->posture_ctl = snd_ctl_new1(&ctl_template, cs35l56);
+ if (snd_ctl_add(cs35l56->codec->card, cs35l56->posture_ctl))
+ dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n", ctl_template.name);
+
+ /* Mixer controls */
+ ctl_template.info = cs35l56_hda_mixer_info;
+ ctl_template.get = cs35l56_hda_mixer_get;
+ ctl_template.put = cs35l56_hda_mixer_put;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l56->mixer_ctl) != ARRAY_SIZE(cs35l56_hda_mixer_controls));
+
+ for (i = 0; i < ARRAY_SIZE(cs35l56_hda_mixer_controls); ++i) {
+ snprintf(name, sizeof(name), "%s %s", cs35l56->amp_name,
+ cs35l56_hda_mixer_controls[i].name);
+ ctl_template.private_value = cs35l56_hda_mixer_controls[i].reg;
+ cs35l56->mixer_ctl[i] = snd_ctl_new1(&ctl_template, cs35l56);
+ if (snd_ctl_add(cs35l56->codec->card, cs35l56->mixer_ctl[i])) {
+ dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n",
+ ctl_template.name);
+ }
+ }
+
+ ctl_template.info = cs35l56_hda_vol_info;
+ ctl_template.get = cs35l56_hda_vol_get;
+ ctl_template.put = cs35l56_hda_vol_put;
+ ctl_template.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ);
+ ctl_template.tlv.p = cs35l56_hda_vol_tlv;
+ snprintf(name, sizeof(name), "%s Speaker Playback Volume", cs35l56->amp_name);
+ ctl_template.name = name;
+ cs35l56->volume_ctl = snd_ctl_new1(&ctl_template, cs35l56);
+ if (snd_ctl_add(cs35l56->codec->card, cs35l56->volume_ctl))
+ dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n", ctl_template.name);
+}
+
+static void cs35l56_hda_remove_controls(struct cs35l56_hda *cs35l56)
+{
+ int i;
+
+ for (i = ARRAY_SIZE(cs35l56->mixer_ctl) - 1; i >= 0; i--)
+ snd_ctl_remove(cs35l56->codec->card, cs35l56->mixer_ctl[i]);
+
+ snd_ctl_remove(cs35l56->codec->card, cs35l56->posture_ctl);
+ snd_ctl_remove(cs35l56->codec->card, cs35l56->volume_ctl);
+}
+
+static const struct cs_dsp_client_ops cs35l56_hda_client_ops = {
+ /* cs_dsp requires the client to provide this even if it is empty */
+};
+
+static int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56,
+ const struct firmware **firmware, char **filename,
+ const char *base_name, const char *system_name,
+ const char *amp_name,
+ const char *filetype)
+{
+ char *s, c;
+ int ret = 0;
+
+ if (system_name && amp_name)
+ *filename = kasprintf(GFP_KERNEL, "%s-%s-%s.%s", base_name,
+ system_name, amp_name, filetype);
+ else if (system_name)
+ *filename = kasprintf(GFP_KERNEL, "%s-%s.%s", base_name,
+ system_name, filetype);
+ else
+ *filename = kasprintf(GFP_KERNEL, "%s.%s", base_name, filetype);
+
+ if (!*filename)
+ return -ENOMEM;
+
+ /*
+ * Make sure that filename is lower-case and any non alpha-numeric
+ * characters except full stop and forward slash are replaced with
+ * hyphens.
+ */
+ s = *filename;
+ while (*s) {
+ c = *s;
+ if (isalnum(c))
+ *s = tolower(c);
+ else if (c != '.' && c != '/')
+ *s = '-';
+ s++;
+ }
+
+ ret = firmware_request_nowarn(firmware, *filename, cs35l56->base.dev);
+ if (ret) {
+ dev_dbg(cs35l56->base.dev, "Failed to request '%s'\n", *filename);
+ kfree(*filename);
+ *filename = NULL;
+ return ret;
+ }
+
+ dev_dbg(cs35l56->base.dev, "Found '%s'\n", *filename);
+
+ return 0;
+}
+
+static void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56,
+ unsigned int preloaded_fw_ver,
+ const struct firmware **wmfw_firmware,
+ char **wmfw_filename,
+ const struct firmware **coeff_firmware,
+ char **coeff_filename)
+{
+ const char *system_name = cs35l56->system_name;
+ const char *amp_name = cs35l56->amp_name;
+ char base_name[37];
+ int ret;
+
+ if (preloaded_fw_ver) {
+ snprintf(base_name, sizeof(base_name),
+ "cirrus/cs35l%02x-%02x%s-%06x-dsp1-misc",
+ cs35l56->base.type,
+ cs35l56->base.rev,
+ cs35l56->base.secured ? "-s" : "",
+ preloaded_fw_ver & 0xffffff);
+ } else {
+ snprintf(base_name, sizeof(base_name),
+ "cirrus/cs35l%02x-%02x%s-dsp1-misc",
+ cs35l56->base.type,
+ cs35l56->base.rev,
+ cs35l56->base.secured ? "-s" : "");
+ }
+
+ if (system_name && amp_name) {
+ if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
+ base_name, system_name, amp_name, "wmfw")) {
+ cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
+ base_name, system_name, amp_name, "bin");
+ return;
+ }
+ }
+
+ if (system_name) {
+ if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
+ base_name, system_name, NULL, "wmfw")) {
+ if (amp_name)
+ cs35l56_hda_request_firmware_file(cs35l56,
+ coeff_firmware, coeff_filename,
+ base_name, system_name,
+ amp_name, "bin");
+ if (!*coeff_firmware)
+ cs35l56_hda_request_firmware_file(cs35l56,
+ coeff_firmware, coeff_filename,
+ base_name, system_name,
+ NULL, "bin");
+ return;
+ }
+
+ /*
+ * Check for system-specific bin files without wmfw before
+ * falling back to generic firmware
+ */
+ if (amp_name)
+ cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
+ base_name, system_name, amp_name, "bin");
+ if (!*coeff_firmware)
+ cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
+ base_name, system_name, NULL, "bin");
+
+ if (*coeff_firmware)
+ return;
+ }
+
+ ret = cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
+ base_name, NULL, NULL, "wmfw");
+ if (!ret) {
+ cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
+ base_name, NULL, NULL, "bin");
+ return;
+ }
+
+ if (!*coeff_firmware)
+ cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
+ base_name, NULL, NULL, "bin");
+}
+
+static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmware,
+ char *wmfw_filename,
+ const struct firmware *coeff_firmware,
+ char *coeff_filename)
+{
+ release_firmware(wmfw_firmware);
+ kfree(wmfw_filename);
+
+ release_firmware(coeff_firmware);
+ kfree(coeff_filename);
+}
+
+static int cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56)
+{
+ int ret;
+
+ if (!cs35l56->base.cal_data_valid || cs35l56->base.secured)
+ return -EACCES;
+
+ ret = cs_amp_write_cal_coeffs(&cs35l56->cs_dsp,
+ &cs35l56_calibration_controls,
+ &cs35l56->base.cal_data);
+ if (ret < 0) {
+ dev_warn(cs35l56->base.dev, "Failed to write calibration: %d\n", ret);
+ return ret;
+ }
+
+ dev_info(cs35l56->base.dev, "Calibration applied\n");
+
+ return 0;
+}
+
+static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
+{
+ const struct firmware *coeff_firmware = NULL;
+ const struct firmware *wmfw_firmware = NULL;
+ char *coeff_filename = NULL;
+ char *wmfw_filename = NULL;
+ unsigned int preloaded_fw_ver;
+ bool firmware_missing;
+ int ret;
+
+ /*
+ * Prepare for a new DSP power-up. If the DSP has had firmware
+ * downloaded previously then it needs to be powered down so that it
+ * can be updated.
+ */
+ if (cs35l56->base.fw_patched)
+ cs_dsp_power_down(&cs35l56->cs_dsp);
+
+ cs35l56->base.fw_patched = false;
+
+ ret = pm_runtime_resume_and_get(cs35l56->base.dev);
+ if (ret < 0) {
+ dev_err(cs35l56->base.dev, "Failed to resume and get %d\n", ret);
+ return;
+ }
+
+ /*
+ * The firmware can only be upgraded if it is currently running
+ * from the built-in ROM. If not, the wmfw/bin must be for the
+ * version of firmware that is running on the chip.
+ */
+ ret = cs35l56_read_prot_status(&cs35l56->base, &firmware_missing, &preloaded_fw_ver);
+ if (ret)
+ goto err_pm_put;
+
+ if (firmware_missing)
+ preloaded_fw_ver = 0;
+
+ cs35l56_hda_request_firmware_files(cs35l56, preloaded_fw_ver,
+ &wmfw_firmware, &wmfw_filename,
+ &coeff_firmware, &coeff_filename);
+
+ /*
+ * If the BIOS didn't patch the firmware a bin file is mandatory to
+ * enable the ASP·
+ */
+ if (!coeff_firmware && firmware_missing) {
+ dev_err(cs35l56->base.dev, ".bin file required but not found\n");
+ goto err_fw_release;
+ }
+
+ mutex_lock(&cs35l56->base.irq_lock);
+
+ /*
+ * If the firmware hasn't been patched it must be shutdown before
+ * doing a full patch and reset afterwards. If it is already
+ * running a patched version the firmware files only contain
+ * tunings and we can use the lower cost reinit sequence instead.
+ */
+ if (firmware_missing && (wmfw_firmware || coeff_firmware)) {
+ ret = cs35l56_firmware_shutdown(&cs35l56->base);
+ if (ret)
+ goto err;
+ }
+
+ ret = cs_dsp_power_up(&cs35l56->cs_dsp, wmfw_firmware, wmfw_filename,
+ coeff_firmware, coeff_filename, "misc");
+ if (ret) {
+ dev_dbg(cs35l56->base.dev, "%s: cs_dsp_power_up ret %d\n", __func__, ret);
+ goto err;
+ }
+
+ if (wmfw_filename)
+ dev_dbg(cs35l56->base.dev, "Loaded WMFW Firmware: %s\n", wmfw_filename);
+
+ if (coeff_filename)
+ dev_dbg(cs35l56->base.dev, "Loaded Coefficients: %s\n", coeff_filename);
+
+ /* If we downloaded firmware, reset the device and wait for it to boot */
+ if (firmware_missing && (wmfw_firmware || coeff_firmware)) {
+ cs35l56_system_reset(&cs35l56->base, false);
+ regcache_mark_dirty(cs35l56->base.regmap);
+ ret = cs35l56_wait_for_firmware_boot(&cs35l56->base);
+ if (ret)
+ goto err_powered_up;
+
+ regcache_cache_only(cs35l56->base.regmap, false);
+ }
+
+ /* Disable auto-hibernate so that runtime_pm has control */
+ ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
+ if (ret)
+ goto err_powered_up;
+
+ regcache_sync(cs35l56->base.regmap);
+
+ regmap_clear_bits(cs35l56->base.regmap,
+ cs35l56->base.fw_reg->prot_sts,
+ CS35L56_FIRMWARE_MISSING);
+ cs35l56->base.fw_patched = true;
+
+ ret = cs_dsp_run(&cs35l56->cs_dsp);
+ if (ret)
+ dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret);
+
+ /* Don't need to check return code, it's not fatal if this fails */
+ cs35l56_hda_apply_calibration(cs35l56);
+
+ ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
+ if (ret)
+ cs_dsp_stop(&cs35l56->cs_dsp);
+
+ cs35l56_log_tuning(&cs35l56->base, &cs35l56->cs_dsp);
+
+err_powered_up:
+ if (!cs35l56->base.fw_patched)
+ cs_dsp_power_down(&cs35l56->cs_dsp);
+err:
+ mutex_unlock(&cs35l56->base.irq_lock);
+err_fw_release:
+ cs35l56_hda_release_firmware_files(wmfw_firmware, wmfw_filename,
+ coeff_firmware, coeff_filename);
+err_pm_put:
+ pm_runtime_put(cs35l56->base.dev);
+}
+
+static void cs35l56_hda_dsp_work(struct work_struct *work)
+{
+ struct cs35l56_hda *cs35l56 = container_of(work, struct cs35l56_hda, dsp_work);
+
+ cs35l56_hda_fw_load(cs35l56);
+}
+
+static ssize_t cs35l56_hda_debugfs_calibrate_write(struct file *file,
+ const char __user *from,
+ size_t count, loff_t *ppos)
+{
+ struct cs35l56_base *cs35l56_base = file->private_data;
+ ssize_t ret;
+
+ ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_calibrate_debugfs_write(cs35l56_base, from, count, ppos);
+ pm_runtime_autosuspend(cs35l56_base->dev);
+
+ return ret;
+}
+
+static ssize_t cs35l56_hda_debugfs_cal_temperature_write(struct file *file,
+ const char __user *from,
+ size_t count, loff_t *ppos)
+{
+ struct cs35l56_base *cs35l56_base = file->private_data;
+ ssize_t ret;
+
+ ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_cal_ambient_debugfs_write(cs35l56_base, from, count, ppos);
+ pm_runtime_autosuspend(cs35l56_base->dev);
+
+ return ret;
+}
+
+static ssize_t cs35l56_hda_debugfs_cal_data_read(struct file *file,
+ char __user *to,
+ size_t count, loff_t *ppos)
+{
+ struct cs35l56_base *cs35l56_base = file->private_data;
+ ssize_t ret;
+
+ ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_cal_data_debugfs_read(cs35l56_base, to, count, ppos);
+ pm_runtime_autosuspend(cs35l56_base->dev);
+
+ return ret;
+}
+
+static ssize_t cs35l56_hda_debugfs_cal_data_write(struct file *file,
+ const char __user *from,
+ size_t count, loff_t *ppos)
+{
+ struct cs35l56_base *cs35l56_base = file->private_data;
+ struct cs35l56_hda *cs35l56 = cs35l56_hda_from_base(cs35l56_base);
+ ssize_t ret;
+
+ ret = cs35l56_cal_data_debugfs_write(cs35l56_base, from, count, ppos);
+ if (ret == -ENODATA)
+ return count; /* Ignore writes of empty cal blobs */
+
+ if (ret < 0)
+ return ret;
+
+ ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_hda_apply_calibration(cs35l56);
+ if (ret == 0)
+ cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_AUDIO_REINIT);
+ else
+ count = -EIO;
+
+ pm_runtime_autosuspend(cs35l56_base->dev);
+
+ return count;
+}
+
+static const struct cs35l56_cal_debugfs_fops cs35l56_hda_cal_debugfs_fops = {
+ .calibrate = {
+ .write = cs35l56_hda_debugfs_calibrate_write,
+ },
+ .cal_temperature = {
+ .write = cs35l56_hda_debugfs_cal_temperature_write,
+ },
+ .cal_data = {
+ .read = cs35l56_hda_debugfs_cal_data_read,
+ .write = cs35l56_hda_debugfs_cal_data_write,
+ },
+};
+
+static int cs35l56_hda_bind(struct device *dev, struct device *master, void *master_data)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
+
+ comp = hda_component_from_index(parent, cs35l56->index);
+ if (!comp)
+ return -EINVAL;
+
+ if (comp->dev)
+ return -EBUSY;
+
+ comp->dev = dev;
+ cs35l56->codec = parent->codec;
+ strscpy(comp->name, dev_name(dev), sizeof(comp->name));
+ comp->playback_hook = cs35l56_hda_playback_hook;
+
+ queue_work(system_long_wq, &cs35l56->dsp_work);
+
+ cs35l56_hda_create_controls(cs35l56);
+
+#if IS_ENABLED(CONFIG_SND_DEBUG)
+ cs35l56->debugfs_root = debugfs_create_dir(dev_name(cs35l56->base.dev), sound_debugfs_root);
+ cs_dsp_init_debugfs(&cs35l56->cs_dsp, cs35l56->debugfs_root);
+#endif
+
+ if (IS_ENABLED(CONFIG_SND_HDA_SCODEC_CS35L56_CAL_DEBUGFS))
+ cs35l56_create_cal_debugfs(&cs35l56->base, &cs35l56_hda_cal_debugfs_fops);
+
+ dev_dbg(cs35l56->base.dev, "Bound\n");
+
+ return 0;
+}
+
+static void cs35l56_hda_unbind(struct device *dev, struct device *master, void *master_data)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
+
+ cancel_work_sync(&cs35l56->dsp_work);
+
+ cs35l56_remove_cal_debugfs(&cs35l56->base);
+ cs35l56_hda_remove_controls(cs35l56);
+
+#if IS_ENABLED(CONFIG_SND_DEBUG)
+ cs_dsp_cleanup_debugfs(&cs35l56->cs_dsp);
+ debugfs_remove_recursive(cs35l56->debugfs_root);
+#endif
+
+ if (cs35l56->base.fw_patched)
+ cs_dsp_power_down(&cs35l56->cs_dsp);
+
+ comp = hda_component_from_index(parent, cs35l56->index);
+ if (comp && (comp->dev == dev))
+ memset(comp, 0, sizeof(*comp));
+
+ cs35l56->codec = NULL;
+
+ dev_dbg(cs35l56->base.dev, "Unbound\n");
+}
+
+static const struct component_ops cs35l56_hda_comp_ops = {
+ .bind = cs35l56_hda_bind,
+ .unbind = cs35l56_hda_unbind,
+};
+
+static int cs35l56_hda_system_suspend(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ cs35l56_hda_wait_dsp_ready(cs35l56);
+
+ if (cs35l56->playing)
+ cs35l56_hda_pause(cs35l56);
+
+ cs35l56->suspended = true;
+
+ /*
+ * The interrupt line is normally shared, but after we start suspending
+ * we can't check if our device is the source of an interrupt, and can't
+ * clear it. Prevent this race by temporarily disabling the parent irq
+ * until we reach _no_irq.
+ */
+ if (cs35l56->base.irq)
+ disable_irq(cs35l56->base.irq);
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int cs35l56_hda_system_suspend_late(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ /*
+ * RESET is usually shared by all amps so it must not be asserted until
+ * all driver instances have done their suspend() stage.
+ */
+ if (cs35l56->base.reset_gpio) {
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+ cs35l56_wait_min_reset_pulse();
+ }
+
+ return 0;
+}
+
+static int cs35l56_hda_system_suspend_no_irq(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ /* Handlers are now disabled so the parent IRQ can safely be re-enabled. */
+ if (cs35l56->base.irq)
+ enable_irq(cs35l56->base.irq);
+
+ return 0;
+}
+
+static int cs35l56_hda_system_resume_no_irq(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ /*
+ * WAKE interrupts unmask if the CS35L56 hibernates, which can cause
+ * spurious interrupts, and the interrupt line is normally shared.
+ * We can't check if our device is the source of an interrupt, and can't
+ * clear it, until it has fully resumed. Prevent this race by temporarily
+ * disabling the parent irq until we complete resume().
+ */
+ if (cs35l56->base.irq)
+ disable_irq(cs35l56->base.irq);
+
+ return 0;
+}
+
+static int cs35l56_hda_system_resume_early(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ /* Ensure a spec-compliant RESET pulse. */
+ if (cs35l56->base.reset_gpio) {
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+ cs35l56_wait_min_reset_pulse();
+
+ /* Release shared RESET before drivers start resume(). */
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1);
+ cs35l56_wait_control_port_ready();
+ }
+
+ return 0;
+}
+
+static int cs35l56_hda_system_resume(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+ int ret;
+
+ /* Undo pm_runtime_force_suspend() before re-enabling the irq */
+ ret = pm_runtime_force_resume(dev);
+ if (cs35l56->base.irq)
+ enable_irq(cs35l56->base.irq);
+
+ if (ret)
+ return ret;
+
+ cs35l56->suspended = false;
+
+ if (!cs35l56->codec)
+ return 0;
+
+ ret = cs35l56_is_fw_reload_needed(&cs35l56->base);
+ dev_dbg(cs35l56->base.dev, "fw_reload_needed: %d\n", ret);
+ if (ret > 0)
+ queue_work(system_long_wq, &cs35l56->dsp_work);
+
+ if (cs35l56->playing)
+ cs35l56_hda_play(cs35l56);
+
+ return 0;
+}
+
+static int cs35l56_hda_fixup_yoga9(struct cs35l56_hda *cs35l56, int *bus_addr)
+{
+ /* The cirrus,dev-index property has the wrong values */
+ switch (*bus_addr) {
+ case 0x30:
+ cs35l56->index = 1;
+ return 0;
+ case 0x31:
+ cs35l56->index = 0;
+ return 0;
+ default:
+ /* There is a pseudo-address for broadcast to both amps - ignore it */
+ dev_dbg(cs35l56->base.dev, "Ignoring I2C address %#x\n", *bus_addr);
+ return 0;
+ }
+}
+
+static const struct {
+ const char *sub;
+ int (*fixup_fn)(struct cs35l56_hda *cs35l56, int *bus_addr);
+} cs35l56_hda_fixups[] = {
+ {
+ .sub = "17AA390B", /* Lenovo Yoga Book 9i GenX */
+ .fixup_fn = cs35l56_hda_fixup_yoga9,
+ },
+};
+
+static int cs35l56_hda_apply_platform_fixups(struct cs35l56_hda *cs35l56, const char *sub,
+ int *bus_addr)
+{
+ int i;
+
+ if (IS_ERR(sub))
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l56_hda_fixups); i++) {
+ if (strcasecmp(cs35l56_hda_fixups[i].sub, sub) == 0) {
+ dev_dbg(cs35l56->base.dev, "Applying fixup for %s\n",
+ cs35l56_hda_fixups[i].sub);
+ return (cs35l56_hda_fixups[i].fixup_fn)(cs35l56, bus_addr);
+ }
+ }
+
+ return 0;
+}
+
+static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id)
+{
+ u32 values[HDA_MAX_COMPONENTS];
+ char hid_string[8];
+ struct acpi_device *adev;
+ const char *property, *sub;
+ size_t nval;
+ int i, ret;
+
+ /*
+ * ACPI_COMPANION isn't available when this driver was instantiated by
+ * the serial-multi-instantiate driver, so lookup the node by HID
+ */
+ if (!ACPI_COMPANION(cs35l56->base.dev)) {
+ snprintf(hid_string, sizeof(hid_string), "CSC%04X", hid);
+ adev = acpi_dev_get_first_match_dev(hid_string, NULL, -1);
+ if (!adev) {
+ dev_err(cs35l56->base.dev, "Failed to find an ACPI device for %s\n",
+ dev_name(cs35l56->base.dev));
+ return -ENODEV;
+ }
+ ACPI_COMPANION_SET(cs35l56->base.dev, adev);
+ }
+
+ /* Initialize things that could be overwritten by a fixup */
+ cs35l56->index = -1;
+
+ sub = acpi_get_subsystem_id(ACPI_HANDLE(cs35l56->base.dev));
+ ret = cs35l56_hda_apply_platform_fixups(cs35l56, sub, &id);
+ if (ret)
+ return ret;
+
+ if (cs35l56->index == -1) {
+ property = "cirrus,dev-index";
+ ret = device_property_count_u32(cs35l56->base.dev, property);
+ if (ret <= 0)
+ goto err;
+
+ if (ret > ARRAY_SIZE(values)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ nval = ret;
+
+ ret = device_property_read_u32_array(cs35l56->base.dev, property, values, nval);
+ if (ret)
+ goto err;
+
+ for (i = 0; i < nval; i++) {
+ if (values[i] == id) {
+ cs35l56->index = i;
+ break;
+ }
+ }
+
+ /*
+ * It's not an error for the ID to be missing: for I2C there can be
+ * an alias address that is not a real device. So reject silently.
+ */
+ if (cs35l56->index == -1) {
+ dev_dbg(cs35l56->base.dev, "No index found in %s\n", property);
+ ret = -ENODEV;
+ goto err;
+ }
+ }
+
+ if (IS_ERR(sub)) {
+ dev_info(cs35l56->base.dev,
+ "Read ACPI _SUB failed(%ld): fallback to generic firmware\n",
+ PTR_ERR(sub));
+ } else {
+ ret = cirrus_scodec_get_speaker_id(cs35l56->base.dev, cs35l56->index, nval, -1);
+ if (ret == -ENOENT) {
+ cs35l56->system_name = sub;
+ } else if (ret >= 0) {
+ cs35l56->system_name = kasprintf(GFP_KERNEL, "%s-spkid%d", sub, ret);
+ kfree(sub);
+ if (!cs35l56->system_name)
+ return -ENOMEM;
+ } else {
+ return ret;
+ }
+ }
+
+ cs35l56->base.reset_gpio = devm_gpiod_get_index_optional(cs35l56->base.dev,
+ "reset",
+ cs35l56->index,
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l56->base.reset_gpio)) {
+ ret = PTR_ERR(cs35l56->base.reset_gpio);
+
+ /*
+ * If RESET is shared the first amp to probe will grab the reset
+ * line and reset all the amps
+ */
+ if (ret != -EBUSY)
+ return dev_err_probe(cs35l56->base.dev, ret, "Failed to get reset GPIO\n");
+
+ dev_info(cs35l56->base.dev, "Reset GPIO busy, assume shared reset\n");
+ cs35l56->base.reset_gpio = NULL;
+ }
+
+ return 0;
+
+err:
+ if (ret != -ENODEV)
+ dev_err(cs35l56->base.dev, "Failed property %s: %d\n", property, ret);
+
+ return ret;
+}
+
+int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id)
+{
+ int ret;
+
+ mutex_init(&cs35l56->base.irq_lock);
+ dev_set_drvdata(cs35l56->base.dev, cs35l56);
+
+ INIT_WORK(&cs35l56->dsp_work, cs35l56_hda_dsp_work);
+
+ ret = cs35l56_hda_read_acpi(cs35l56, hid, id);
+ if (ret)
+ goto err;
+
+ cs35l56->amp_name = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, "AMP%d",
+ cs35l56->index + 1);
+ if (!cs35l56->amp_name) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ cs35l56->base.type = hid & 0xff;
+ cs35l56->base.cal_index = cs35l56->index;
+
+ cs35l56_init_cs_dsp(&cs35l56->base, &cs35l56->cs_dsp);
+ cs35l56->cs_dsp.client_ops = &cs35l56_hda_client_ops;
+
+ if (cs35l56->base.reset_gpio) {
+ dev_dbg(cs35l56->base.dev, "Hard reset\n");
+
+ /*
+ * The GPIOD_OUT_LOW to *_gpiod_get_*() will be ignored if the
+ * ACPI defines a different default state. So explicitly set low.
+ */
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+ cs35l56_wait_min_reset_pulse();
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1);
+ }
+
+ ret = cs35l56_hw_init(&cs35l56->base);
+ if (ret < 0)
+ goto err;
+
+ /* Reset the device and wait for it to boot */
+ cs35l56_system_reset(&cs35l56->base, false);
+ ret = cs35l56_wait_for_firmware_boot(&cs35l56->base);
+ if (ret)
+ goto err;
+
+ regcache_cache_only(cs35l56->base.regmap, false);
+
+ ret = cs35l56_set_patch(&cs35l56->base);
+ if (ret)
+ goto err;
+
+ regcache_mark_dirty(cs35l56->base.regmap);
+ regcache_sync(cs35l56->base.regmap);
+
+ /* Disable auto-hibernate so that runtime_pm has control */
+ ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
+ if (ret)
+ goto err;
+
+ ret = cs35l56_get_calibration(&cs35l56->base);
+ if (ret)
+ goto err;
+
+ ret = cs_dsp_halo_init(&cs35l56->cs_dsp);
+ if (ret) {
+ dev_err_probe(cs35l56->base.dev, ret, "cs_dsp_halo_init failed\n");
+ goto err;
+ }
+
+ dev_info(cs35l56->base.dev, "DSP system name: '%s', amp name: '%s'\n",
+ cs35l56->system_name, cs35l56->amp_name);
+
+ regmap_multi_reg_write(cs35l56->base.regmap, cs35l56_hda_dai_config,
+ ARRAY_SIZE(cs35l56_hda_dai_config));
+
+ /*
+ * By default only enable one ASP1TXn, where n=amplifier index,
+ * This prevents multiple amps trying to drive the same slot.
+ */
+ cs35l56->asp_tx_mask = BIT(cs35l56->index);
+
+ pm_runtime_set_autosuspend_delay(cs35l56->base.dev, 3000);
+ pm_runtime_use_autosuspend(cs35l56->base.dev);
+ pm_runtime_set_active(cs35l56->base.dev);
+ pm_runtime_mark_last_busy(cs35l56->base.dev);
+ pm_runtime_enable(cs35l56->base.dev);
+
+ cs35l56->base.init_done = true;
+
+ ret = component_add(cs35l56->base.dev, &cs35l56_hda_comp_ops);
+ if (ret) {
+ dev_err(cs35l56->base.dev, "Register component failed: %d\n", ret);
+ goto pm_err;
+ }
+
+ return 0;
+
+pm_err:
+ pm_runtime_disable(cs35l56->base.dev);
+ cs_dsp_remove(&cs35l56->cs_dsp);
+err:
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_hda_common_probe, "SND_HDA_SCODEC_CS35L56");
+
+void cs35l56_hda_remove(struct device *dev)
+{
+ struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
+
+ component_del(cs35l56->base.dev, &cs35l56_hda_comp_ops);
+
+ pm_runtime_dont_use_autosuspend(cs35l56->base.dev);
+ pm_runtime_get_sync(cs35l56->base.dev);
+ pm_runtime_disable(cs35l56->base.dev);
+
+ cs_dsp_remove(&cs35l56->cs_dsp);
+
+ kfree(cs35l56->system_name);
+ pm_runtime_put_noidle(cs35l56->base.dev);
+
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_hda_remove, "SND_HDA_SCODEC_CS35L56");
+
+const struct dev_pm_ops cs35l56_hda_pm_ops = {
+ RUNTIME_PM_OPS(cs35l56_hda_runtime_suspend, cs35l56_hda_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend, cs35l56_hda_system_resume)
+ LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend_late,
+ cs35l56_hda_system_resume_early)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend_no_irq,
+ cs35l56_hda_system_resume_no_irq)
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_hda_pm_ops, "SND_HDA_SCODEC_CS35L56");
+
+MODULE_DESCRIPTION("CS35L56 HDA Driver");
+MODULE_IMPORT_NS("FW_CS_DSP");
+MODULE_IMPORT_NS("SND_HDA_CIRRUS_SCODEC");
+MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED");
+MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("cirrus/cs35l54-*.wmfw");
+MODULE_FIRMWARE("cirrus/cs35l54-*.bin");
+MODULE_FIRMWARE("cirrus/cs35l56-*.wmfw");
+MODULE_FIRMWARE("cirrus/cs35l56-*.bin");
diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.h b/sound/hda/codecs/side-codecs/cs35l56_hda.h
new file mode 100644
index 000000000000..cb4b5e7356a3
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l56_hda.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * HDA audio driver for Cirrus Logic CS35L56 smart amp
+ *
+ * Copyright (C) 2023 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef __CS35L56_HDA_H__
+#define __CS35L56_HDA_H__
+
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/firmware/cirrus/wmfw.h>
+#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
+#include <sound/cs35l56.h>
+
+struct dentry;
+
+struct cs35l56_hda {
+ struct cs35l56_base base;
+ struct hda_codec *codec;
+ struct work_struct dsp_work;
+
+ int index;
+ const char *system_name;
+ const char *amp_name;
+
+ struct cs_dsp cs_dsp;
+ bool playing;
+ bool suspended;
+ u8 asp_tx_mask;
+
+ struct snd_kcontrol *posture_ctl;
+ struct snd_kcontrol *volume_ctl;
+ struct snd_kcontrol *mixer_ctl[4];
+
+#if IS_ENABLED(CONFIG_SND_DEBUG)
+ struct dentry *debugfs_root;
+#endif
+};
+
+static inline struct cs35l56_hda *cs35l56_hda_from_base(struct cs35l56_base *cs35l56_base)
+{
+ return container_of(cs35l56_base, struct cs35l56_hda, base);
+}
+
+extern const struct dev_pm_ops cs35l56_hda_pm_ops;
+
+int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id);
+void cs35l56_hda_remove(struct device *dev);
+
+#endif /*__CS35L56_HDA_H__*/
diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda_i2c.c b/sound/hda/codecs/side-codecs/cs35l56_hda_i2c.c
new file mode 100644
index 000000000000..1072f17385ac
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l56_hda_i2c.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// CS35L56 HDA audio driver I2C binding
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "cs35l56_hda.h"
+
+static int cs35l56_hda_i2c_probe(struct i2c_client *clt)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(clt);
+ struct cs35l56_hda *cs35l56;
+ int ret;
+
+ cs35l56 = devm_kzalloc(&clt->dev, sizeof(*cs35l56), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ cs35l56->base.dev = &clt->dev;
+
+#ifdef CS35L56_WAKE_HOLD_TIME_US
+ cs35l56->base.can_hibernate = true;
+#endif
+
+ cs35l56->base.regmap = devm_regmap_init_i2c(clt, &cs35l56_regmap_i2c);
+ if (IS_ERR(cs35l56->base.regmap)) {
+ ret = PTR_ERR(cs35l56->base.regmap);
+ dev_err(cs35l56->base.dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = cs35l56_hda_common_probe(cs35l56, id->driver_data, clt->addr);
+ if (ret)
+ return ret;
+ ret = cs35l56_irq_request(&cs35l56->base, clt->irq);
+ if (ret < 0)
+ cs35l56_hda_remove(cs35l56->base.dev);
+
+ return ret;
+}
+
+static void cs35l56_hda_i2c_remove(struct i2c_client *clt)
+{
+ cs35l56_hda_remove(&clt->dev);
+}
+
+static const struct i2c_device_id cs35l56_hda_i2c_id[] = {
+ { "cs35l54-hda", 0x3554 },
+ { "cs35l56-hda", 0x3556 },
+ { "cs35l57-hda", 0x3557 },
+ {}
+};
+
+static const struct acpi_device_id cs35l56_acpi_hda_match[] = {
+ { "CSC3554", 0 },
+ { "CSC3556", 0 },
+ { "CSC3557", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, cs35l56_acpi_hda_match);
+
+static struct i2c_driver cs35l56_hda_i2c_driver = {
+ .driver = {
+ .name = "cs35l56-hda",
+ .acpi_match_table = cs35l56_acpi_hda_match,
+ .pm = &cs35l56_hda_pm_ops,
+ },
+ .id_table = cs35l56_hda_i2c_id,
+ .probe = cs35l56_hda_i2c_probe,
+ .remove = cs35l56_hda_i2c_remove,
+};
+module_i2c_driver(cs35l56_hda_i2c_driver);
+
+MODULE_DESCRIPTION("HDA CS35L56 I2C driver");
+MODULE_IMPORT_NS("SND_HDA_SCODEC_CS35L56");
+MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda_spi.c b/sound/hda/codecs/side-codecs/cs35l56_hda_spi.c
new file mode 100644
index 000000000000..f802c83c57b4
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/cs35l56_hda_spi.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// CS35L56 HDA audio driver SPI binding
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "cs35l56_hda.h"
+
+static int cs35l56_hda_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct cs35l56_hda *cs35l56;
+ int ret;
+
+ cs35l56 = devm_kzalloc(&spi->dev, sizeof(*cs35l56), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ cs35l56->base.dev = &spi->dev;
+ ret = cs35l56_init_config_for_spi(&cs35l56->base, spi);
+ if (ret)
+ return ret;
+
+#ifdef CS35L56_WAKE_HOLD_TIME_US
+ cs35l56->base.can_hibernate = true;
+#endif
+
+ cs35l56->base.regmap = devm_regmap_init_spi(spi, &cs35l56_regmap_spi);
+ if (IS_ERR(cs35l56->base.regmap)) {
+ ret = PTR_ERR(cs35l56->base.regmap);
+ dev_err(cs35l56->base.dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = cs35l56_hda_common_probe(cs35l56, id->driver_data, spi_get_chipselect(spi, 0));
+ if (ret)
+ return ret;
+ ret = cs35l56_irq_request(&cs35l56->base, spi->irq);
+ if (ret < 0)
+ cs35l56_hda_remove(cs35l56->base.dev);
+
+ return ret;
+}
+
+static void cs35l56_hda_spi_remove(struct spi_device *spi)
+{
+ cs35l56_hda_remove(&spi->dev);
+}
+
+static const struct spi_device_id cs35l56_hda_spi_id[] = {
+ { "cs35l54-hda", 0x3554 },
+ { "cs35l56-hda", 0x3556 },
+ { "cs35l57-hda", 0x3557 },
+ {}
+};
+
+static const struct acpi_device_id cs35l56_acpi_hda_match[] = {
+ { "CSC3554", 0 },
+ { "CSC3556", 0 },
+ { "CSC3557", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, cs35l56_acpi_hda_match);
+
+static struct spi_driver cs35l56_hda_spi_driver = {
+ .driver = {
+ .name = "cs35l56-hda",
+ .acpi_match_table = cs35l56_acpi_hda_match,
+ .pm = &cs35l56_hda_pm_ops,
+ },
+ .id_table = cs35l56_hda_spi_id,
+ .probe = cs35l56_hda_spi_probe,
+ .remove = cs35l56_hda_spi_remove,
+};
+module_spi_driver(cs35l56_hda_spi_driver);
+
+MODULE_DESCRIPTION("HDA CS35L56 SPI driver");
+MODULE_IMPORT_NS("SND_HDA_SCODEC_CS35L56");
+MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/hda/codecs/side-codecs/hda_component.c b/sound/hda/codecs/side-codecs/hda_component.c
new file mode 100644
index 000000000000..8a2a200600a7
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/hda_component.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HD audio Component Binding Interface
+ *
+ * Copyright (C) 2021, 2023 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#include <linux/acpi.h>
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/hda_codec.h>
+#include "hda_component.h"
+#include "hda_local.h"
+
+#ifdef CONFIG_ACPI
+void hda_component_acpi_device_notify(struct hda_component_parent *parent,
+ acpi_handle handle, u32 event, void *data)
+{
+ struct hda_component *comp;
+ int i;
+
+ guard(mutex)(&parent->mutex);
+ for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
+ comp = hda_component_from_index(parent, i);
+ if (comp->dev && comp->acpi_notify)
+ comp->acpi_notify(acpi_device_handle(comp->adev), event, comp->dev);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_acpi_device_notify, "SND_HDA_SCODEC_COMPONENT");
+
+int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc,
+ struct hda_component_parent *parent,
+ acpi_notify_handler handler, void *data)
+{
+ bool support_notifications = false;
+ struct acpi_device *adev;
+ struct hda_component *comp;
+ int ret;
+ int i;
+
+ adev = parent->comps[0].adev;
+ if (!acpi_device_handle(adev))
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
+ comp = hda_component_from_index(parent, i);
+ support_notifications = support_notifications ||
+ comp->acpi_notifications_supported;
+ }
+
+ if (support_notifications) {
+ ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
+ handler, data);
+ if (ret < 0) {
+ codec_warn(cdc, "Failed to install notify handler: %d\n", ret);
+ return 0;
+ }
+
+ codec_dbg(cdc, "Notify handler installed\n");
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind_acpi_notifications, "SND_HDA_SCODEC_COMPONENT");
+
+void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc,
+ struct hda_component_parent *parent,
+ acpi_notify_handler handler)
+{
+ struct acpi_device *adev;
+ int ret;
+
+ adev = parent->comps[0].adev;
+ if (!acpi_device_handle(adev))
+ return;
+
+ ret = acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, handler);
+ if (ret < 0)
+ codec_warn(cdc, "Failed to uninstall notify handler: %d\n", ret);
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_manager_unbind_acpi_notifications, "SND_HDA_SCODEC_COMPONENT");
+#endif /* ifdef CONFIG_ACPI */
+
+void hda_component_manager_playback_hook(struct hda_component_parent *parent, int action)
+{
+ struct hda_component *comp;
+ int i;
+
+ guard(mutex)(&parent->mutex);
+ for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
+ comp = hda_component_from_index(parent, i);
+ if (comp->dev && comp->pre_playback_hook)
+ comp->pre_playback_hook(comp->dev, action);
+ }
+ for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
+ comp = hda_component_from_index(parent, i);
+ if (comp->dev && comp->playback_hook)
+ comp->playback_hook(comp->dev, action);
+ }
+ for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
+ comp = hda_component_from_index(parent, i);
+ if (comp->dev && comp->post_playback_hook)
+ comp->post_playback_hook(comp->dev, action);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_manager_playback_hook, "SND_HDA_SCODEC_COMPONENT");
+
+struct hda_scodec_match {
+ const char *bus;
+ const char *hid;
+ const char *match_str;
+ int index;
+};
+
+/* match the device name in a slightly relaxed manner */
+static int hda_comp_match_dev_name(struct device *dev, void *data)
+{
+ struct hda_scodec_match *p = data;
+ const char *d = dev_name(dev);
+ int n = strlen(p->bus);
+ char tmp[32];
+
+ /* check the bus name */
+ if (strncmp(d, p->bus, n))
+ return 0;
+ /* skip the bus number */
+ if (isdigit(d[n]))
+ n++;
+ /* the rest must be exact matching */
+ snprintf(tmp, sizeof(tmp), p->match_str, p->hid, p->index);
+ return !strcmp(d + n, tmp);
+}
+
+int hda_component_manager_bind(struct hda_codec *cdc,
+ struct hda_component_parent *parent)
+{
+ /* Init shared and component specific data */
+ memset(parent->comps, 0, sizeof(parent->comps));
+
+ guard(mutex)(&parent->mutex);
+ return component_bind_all(hda_codec_dev(cdc), parent);
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind, "SND_HDA_SCODEC_COMPONENT");
+
+int hda_component_manager_init(struct hda_codec *cdc,
+ struct hda_component_parent *parent, int count,
+ const char *bus, const char *hid,
+ const char *match_str,
+ const struct component_master_ops *ops)
+{
+ struct device *dev = hda_codec_dev(cdc);
+ struct component_match *match = NULL;
+ struct hda_scodec_match *sm;
+ int ret, i;
+
+ if (parent->codec) {
+ codec_err(cdc, "Component binding already created (SSID: %x)\n",
+ cdc->core.subsystem_id);
+ return -EINVAL;
+ }
+ parent->codec = cdc;
+
+ mutex_init(&parent->mutex);
+
+ for (i = 0; i < count; i++) {
+ sm = devm_kmalloc(dev, sizeof(*sm), GFP_KERNEL);
+ if (!sm)
+ return -ENOMEM;
+
+ sm->bus = bus;
+ sm->hid = hid;
+ sm->match_str = match_str;
+ sm->index = i;
+ component_match_add(dev, &match, hda_comp_match_dev_name, sm);
+ if (IS_ERR(match)) {
+ codec_err(cdc, "Fail to add component %ld\n", PTR_ERR(match));
+ return PTR_ERR(match);
+ }
+ }
+
+ ret = component_master_add_with_match(dev, ops, match);
+ if (ret)
+ codec_err(cdc, "Fail to register component aggregator %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_manager_init, "SND_HDA_SCODEC_COMPONENT");
+
+void hda_component_manager_free(struct hda_component_parent *parent,
+ const struct component_master_ops *ops)
+{
+ struct device *dev;
+
+ if (!parent->codec)
+ return;
+
+ dev = hda_codec_dev(parent->codec);
+
+ component_master_del(dev, ops);
+
+ parent->codec = NULL;
+}
+EXPORT_SYMBOL_NS_GPL(hda_component_manager_free, "SND_HDA_SCODEC_COMPONENT");
+
+MODULE_DESCRIPTION("HD Audio component binding library");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/hda/codecs/side-codecs/hda_component.h b/sound/hda/codecs/side-codecs/hda_component.h
new file mode 100644
index 000000000000..075137a73bae
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/hda_component.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * HD audio Component Binding Interface
+ *
+ * Copyright (C) 2021 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef __HDA_COMPONENT_H__
+#define __HDA_COMPONENT_H__
+
+#include <linux/acpi.h>
+#include <linux/component.h>
+#include <linux/mutex.h>
+#include <sound/hda_codec.h>
+
+#define HDA_MAX_COMPONENTS 4
+#define HDA_MAX_NAME_SIZE 50
+
+struct hda_component {
+ struct device *dev;
+ char name[HDA_MAX_NAME_SIZE];
+ struct acpi_device *adev;
+ bool acpi_notifications_supported;
+ void (*acpi_notify)(acpi_handle handle, u32 event, struct device *dev);
+ void (*pre_playback_hook)(struct device *dev, int action);
+ void (*playback_hook)(struct device *dev, int action);
+ void (*post_playback_hook)(struct device *dev, int action);
+};
+
+struct hda_component_parent {
+ struct mutex mutex;
+ struct hda_codec *codec;
+ struct hda_component comps[HDA_MAX_COMPONENTS];
+};
+
+#ifdef CONFIG_ACPI
+void hda_component_acpi_device_notify(struct hda_component_parent *parent,
+ acpi_handle handle, u32 event, void *data);
+int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc,
+ struct hda_component_parent *parent,
+ acpi_notify_handler handler, void *data);
+void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc,
+ struct hda_component_parent *parent,
+ acpi_notify_handler handler);
+#else
+static inline void hda_component_acpi_device_notify(struct hda_component_parent *parent,
+ acpi_handle handle,
+ u32 event,
+ void *data)
+{
+}
+
+static inline int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc,
+ struct hda_component_parent *parent,
+ acpi_notify_handler handler,
+ void *data)
+
+{
+ return 0;
+}
+
+static inline void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc,
+ struct hda_component_parent *parent,
+ acpi_notify_handler handler)
+{
+}
+#endif /* ifdef CONFIG_ACPI */
+
+void hda_component_manager_playback_hook(struct hda_component_parent *parent, int action);
+
+int hda_component_manager_init(struct hda_codec *cdc,
+ struct hda_component_parent *parent, int count,
+ const char *bus, const char *hid,
+ const char *match_str,
+ const struct component_master_ops *ops);
+
+void hda_component_manager_free(struct hda_component_parent *parent,
+ const struct component_master_ops *ops);
+
+int hda_component_manager_bind(struct hda_codec *cdc, struct hda_component_parent *parent);
+
+static inline struct hda_component *hda_component_from_index(struct hda_component_parent *parent,
+ int index)
+{
+ if (!parent)
+ return NULL;
+
+ if (index < 0 || index >= ARRAY_SIZE(parent->comps))
+ return NULL;
+
+ return &parent->comps[index];
+}
+
+static inline void hda_component_manager_unbind(struct hda_codec *cdc,
+ struct hda_component_parent *parent)
+{
+ guard(mutex)(&parent->mutex);
+ component_unbind_all(hda_codec_dev(cdc), parent);
+}
+
+#endif /* ifndef __HDA_COMPONENT_H__ */
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda.c b/sound/hda/codecs/side-codecs/tas2781_hda.c
new file mode 100644
index 000000000000..96e6d82dc69e
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/tas2781_hda.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TAS2781 HDA Shared Lib for I2C&SPI driver
+//
+// Copyright 2025 Texas Instruments, Inc.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+
+#include <linux/component.h>
+#include <linux/crc8.h>
+#include <linux/crc32.h>
+#include <linux/efi.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include <sound/tas2781.h>
+
+#include "tas2781_hda.h"
+
+#define CALIBRATION_DATA_AREA_NUM 2
+
+const efi_guid_t tasdev_fct_efi_guid[] = {
+ /* DELL */
+ EFI_GUID(0xcc92382d, 0x6337, 0x41cb, 0xa8, 0x8b, 0x8e, 0xce, 0x74,
+ 0x91, 0xea, 0x9f),
+ /* HP */
+ EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a,
+ 0xa3, 0x5d, 0xb3),
+ /* LENOVO & OTHERS */
+ EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc, 0x09, 0x43, 0xa3, 0xf4,
+ 0x31, 0x0a, 0x92),
+};
+EXPORT_SYMBOL_NS_GPL(tasdev_fct_efi_guid, "SND_HDA_SCODEC_TAS2781");
+
+/*
+ * The order of calibrated-data writing function is a bit different from the
+ * order in UEFI. Here is the conversion to match the order of calibrated-data
+ * writing function.
+ */
+static void cali_cnv(unsigned char *data, unsigned int base, int offset)
+{
+ struct cali_reg reg_data;
+
+ memcpy(&reg_data, &data[base], sizeof(reg_data));
+ /* the data order has to be swapped between r0_low_reg and inv0_reg */
+ swap(reg_data.r0_low_reg, reg_data.invr0_reg);
+
+ cpu_to_be32_array((__force __be32 *)(data + offset + 1),
+ (u32 *)&reg_data, TASDEV_CALIB_N);
+}
+
+static void tas2781_apply_calib(struct tasdevice_priv *p)
+{
+ struct calidata *cali_data = &p->cali_data;
+ struct cali_reg *r = &cali_data->cali_reg_array;
+ unsigned char *data = cali_data->data;
+ unsigned int *tmp_val = (unsigned int *)data;
+ unsigned int cali_reg[TASDEV_CALIB_N] = {
+ TASDEVICE_REG(0, 0x17, 0x74),
+ TASDEVICE_REG(0, 0x18, 0x0c),
+ TASDEVICE_REG(0, 0x18, 0x14),
+ TASDEVICE_REG(0, 0x13, 0x70),
+ TASDEVICE_REG(0, 0x18, 0x7c),
+ };
+ unsigned int crc, oft, node_num;
+ unsigned char *buf;
+ int i, j, k, l;
+
+ if (tmp_val[0] == 2781) {
+ /*
+ * New features were added in calibrated Data V3:
+ * 1. Added calibration registers address define in
+ * a node, marked as Device id == 0x80.
+ * New features were added in calibrated Data V2:
+ * 1. Added some the fields to store the link_id and
+ * uniqie_id for multi-link solutions
+ * 2. Support flexible number of devices instead of
+ * fixed one in V1.
+ * Layout of calibrated data V2 in UEFI(total 256 bytes):
+ * ChipID (2781, 4 bytes)
+ * Data-Group-Sum (4 bytes)
+ * TimeStamp of Calibration (4 bytes)
+ * for (i = 0; i < Data-Group-Sum; i++) {
+ * if (Data type != 0x80) (4 bytes)
+ * Calibrated Data of Device #i (20 bytes)
+ * else
+ * Calibration registers address (5*4 = 20 bytes)
+ * # V2: No reg addr in data grp section.
+ * # V3: Normally the last grp is the reg addr.
+ * }
+ * CRC (4 bytes)
+ * Reserved (the rest)
+ */
+ crc = crc32(~0, data, (3 + tmp_val[1] * 6) * 4) ^ ~0;
+
+ if (crc != tmp_val[3 + tmp_val[1] * 6]) {
+ cali_data->total_sz = 0;
+ dev_err(p->dev, "%s: CRC error\n", __func__);
+ return;
+ }
+ node_num = tmp_val[1];
+
+ for (j = 0, k = 0; j < node_num; j++) {
+ oft = j * 6 + 3;
+ if (tmp_val[oft] == TASDEV_UEFI_CALI_REG_ADDR_FLG) {
+ for (i = 0; i < TASDEV_CALIB_N; i++) {
+ buf = &data[(oft + i + 1) * 4];
+ cali_reg[i] = TASDEVICE_REG(buf[1],
+ buf[2], buf[3]);
+ }
+ } else {
+ l = j * (cali_data->cali_dat_sz_per_dev + 1);
+ if (k >= p->ndev || l > oft * 4) {
+ dev_err(p->dev, "%s: dev sum error\n",
+ __func__);
+ cali_data->total_sz = 0;
+ return;
+ }
+
+ data[l] = k;
+ oft++;
+ cali_cnv(data, 4 * oft, l);
+ k++;
+ }
+ }
+ } else {
+ /*
+ * Calibration data is in V1 format.
+ * struct cali_data {
+ * char cali_data[20];
+ * }
+ *
+ * struct {
+ * struct cali_data cali_data[4];
+ * int TimeStamp of Calibration (4 bytes)
+ * int CRC (4 bytes)
+ * } ueft;
+ */
+ crc = crc32(~0, data, 84) ^ ~0;
+ if (crc != tmp_val[21]) {
+ cali_data->total_sz = 0;
+ dev_err(p->dev, "%s: V1 CRC error\n", __func__);
+ return;
+ }
+
+ for (j = p->ndev - 1; j >= 0; j--) {
+ l = j * (cali_data->cali_dat_sz_per_dev + 1);
+ cali_cnv(data, cali_data->cali_dat_sz_per_dev * j, l);
+ data[l] = j;
+ }
+ }
+
+ if (p->dspbin_typ == TASDEV_BASIC) {
+ r->r0_reg = cali_reg[0];
+ r->invr0_reg = cali_reg[1];
+ r->r0_low_reg = cali_reg[2];
+ r->pow_reg = cali_reg[3];
+ r->tlimit_reg = cali_reg[4];
+ }
+
+ p->is_user_space_calidata = true;
+ cali_data->total_sz = p->ndev * (cali_data->cali_dat_sz_per_dev + 1);
+}
+
+/*
+ * Update the calibration data, including speaker impedance, f0, etc,
+ * into algo. Calibrate data is done by manufacturer in the factory.
+ * The data is used by Algo for calculating the speaker temperature,
+ * speaker membrane excursion and f0 in real time during playback.
+ * Calibration data format in EFI is V2, since 2024.
+ */
+int tas2781_save_calibration(struct tas2781_hda *hda)
+{
+ /*
+ * GUID was used for data access in BIOS, it was provided by board
+ * manufactory.
+ */
+ efi_guid_t efi_guid = tasdev_fct_efi_guid[LENOVO];
+ /*
+ * Some devices save the calibrated data into L"CALI_DATA",
+ * and others into L"SmartAmpCalibrationData".
+ */
+ static efi_char16_t *efi_name[CALIBRATION_DATA_AREA_NUM] = {
+ L"CALI_DATA",
+ L"SmartAmpCalibrationData",
+ };
+ struct tasdevice_priv *p = hda->priv;
+ struct calidata *cali_data = &p->cali_data;
+ unsigned long total_sz = 0;
+ unsigned int attr, size;
+ unsigned char *data;
+ efi_status_t status;
+ int i;
+
+ if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) {
+ dev_err(p->dev, "%s: NO EFI FOUND!\n", __func__);
+ return -EINVAL;
+ }
+
+ if (hda->catlog_id < LENOVO)
+ efi_guid = tasdev_fct_efi_guid[hda->catlog_id];
+
+ cali_data->cali_dat_sz_per_dev = 20;
+ size = p->ndev * (cali_data->cali_dat_sz_per_dev + 1);
+ for (i = 0; i < CALIBRATION_DATA_AREA_NUM; i++) {
+ /* Get real size of UEFI variable */
+ status = efi.get_variable(efi_name[i], &efi_guid, &attr,
+ &total_sz, NULL);
+ cali_data->total_sz = total_sz > size ? total_sz : size;
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ /* Allocate data buffer of data_size bytes */
+ data = cali_data->data = devm_kzalloc(p->dev,
+ cali_data->total_sz, GFP_KERNEL);
+ if (!data) {
+ status = -ENOMEM;
+ continue;
+ }
+ /* Get variable contents into buffer */
+ status = efi.get_variable(efi_name[i], &efi_guid,
+ &attr, &cali_data->total_sz, data);
+ }
+ /* Check whether get the calibrated data */
+ if (status == EFI_SUCCESS)
+ break;
+ }
+
+ if (status != EFI_SUCCESS) {
+ cali_data->total_sz = 0;
+ return status;
+ }
+
+ tas2781_apply_calib(p);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(tas2781_save_calibration, "SND_HDA_SCODEC_TAS2781");
+
+void tas2781_hda_remove(struct device *dev,
+ const struct component_ops *ops)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+ component_del(tas_hda->dev, ops);
+
+ pm_runtime_get_sync(tas_hda->dev);
+ pm_runtime_disable(tas_hda->dev);
+
+ pm_runtime_put_noidle(tas_hda->dev);
+
+ tasdevice_remove(tas_hda->priv);
+}
+EXPORT_SYMBOL_NS_GPL(tas2781_hda_remove, "SND_HDA_SCODEC_TAS2781");
+
+int tasdevice_info_profile(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = tas_priv->rcabin.ncfgs - 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_info_profile, "SND_HDA_SCODEC_TAS2781");
+
+int tasdevice_info_programs(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = tas_priv->fmw->nr_programs - 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_info_programs, "SND_HDA_SCODEC_TAS2781");
+
+int tasdevice_info_config(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_fw *tas_fw = tas_priv->fmw;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = tas_fw->nr_configurations - 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_info_config, "SND_HDA_SCODEC_TAS2781");
+
+int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id;
+
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", __func__,
+ kcontrol->id.name, tas_priv->rcabin.profile_cfg_id);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_get_profile_id, "SND_HDA_SCODEC_TAS2781");
+
+int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ int profile_id = ucontrol->value.integer.value[0];
+ int max = tas_priv->rcabin.ncfgs - 1;
+ int val, ret = 0;
+
+ val = clamp(profile_id, 0, max);
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", __func__,
+ kcontrol->id.name, tas_priv->rcabin.profile_cfg_id, val);
+
+ if (tas_priv->rcabin.profile_cfg_id != val) {
+ tas_priv->rcabin.profile_cfg_id = val;
+ ret = 1;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_set_profile_id, "SND_HDA_SCODEC_TAS2781");
+
+int tasdevice_program_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = tas_priv->cur_prog;
+
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", __func__,
+ kcontrol->id.name, tas_priv->cur_prog);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_program_get, "SND_HDA_SCODEC_TAS2781");
+
+int tasdevice_program_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_fw *tas_fw = tas_priv->fmw;
+ int nr_program = ucontrol->value.integer.value[0];
+ int max = tas_fw->nr_programs - 1;
+ int val, ret = 0;
+
+ val = clamp(nr_program, 0, max);
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", __func__,
+ kcontrol->id.name, tas_priv->cur_prog, val);
+
+ if (tas_priv->cur_prog != val) {
+ tas_priv->cur_prog = val;
+ ret = 1;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_program_put, "SND_HDA_SCODEC_TAS2781");
+
+int tasdevice_config_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = tas_priv->cur_conf;
+
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n", __func__,
+ kcontrol->id.name, tas_priv->cur_conf);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_config_get, "SND_HDA_SCODEC_TAS2781");
+
+int tasdevice_config_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_fw *tas_fw = tas_priv->fmw;
+ int nr_config = ucontrol->value.integer.value[0];
+ int max = tas_fw->nr_configurations - 1;
+ int val, ret = 0;
+
+ val = clamp(nr_config, 0, max);
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n", __func__,
+ kcontrol->id.name, tas_priv->cur_conf, val);
+
+ if (tas_priv->cur_conf != val) {
+ tas_priv->cur_conf = val;
+ ret = 1;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(tasdevice_config_put, "SND_HDA_SCODEC_TAS2781");
+
+MODULE_DESCRIPTION("TAS2781 HDA Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda.h b/sound/hda/codecs/side-codecs/tas2781_hda.h
new file mode 100644
index 000000000000..66188909a0bb
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/tas2781_hda.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * HDA audio driver for Texas Instruments TAS2781 smart amp
+ *
+ * Copyright (C) 2025 Texas Instruments, Inc.
+ */
+#ifndef __TAS2781_HDA_H__
+#define __TAS2781_HDA_H__
+
+#include <sound/asound.h>
+
+/* Flag of calibration registers address. */
+#define TASDEV_UEFI_CALI_REG_ADDR_FLG BIT(7)
+
+#define TASDEV_CALIB_N 5
+
+/*
+ * No standard control callbacks for SNDRV_CTL_ELEM_IFACE_CARD
+ * Define two controls, one is Volume control callbacks, the other is
+ * flag setting control callbacks.
+ */
+
+/* Volume control callbacks for tas2781 */
+#define ACARD_SINGLE_RANGE_EXT_TLV(xname, xreg, xshift, xmin, xmax, xinvert, \
+ xhandler_get, xhandler_put, tlv_array) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = (xname), \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = snd_soc_info_volsw, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = (unsigned long)&(struct soc_mixer_control) { \
+ .reg = xreg, .rreg = xreg, \
+ .shift = xshift, .rshift = xshift,\
+ .min = xmin, .max = xmax, .invert = xinvert, \
+ } \
+}
+
+/* Flag control callbacks for tas2781 */
+#define ACARD_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD, \
+ .name = xname, \
+ .info = snd_ctl_boolean_mono_info, \
+ .get = xhandler_get, \
+ .put = xhandler_put, \
+ .private_value = xdata, \
+}
+
+enum device_catlog_id {
+ DELL = 0,
+ HP,
+ LENOVO,
+ OTHERS
+};
+
+struct tas2781_hda {
+ struct device *dev;
+ struct tasdevice_priv *priv;
+ struct snd_kcontrol *dsp_prog_ctl;
+ struct snd_kcontrol *dsp_conf_ctl;
+ struct snd_kcontrol *prof_ctl;
+ enum device_catlog_id catlog_id;
+ void *hda_priv;
+};
+
+extern const efi_guid_t tasdev_fct_efi_guid[];
+
+int tas2781_save_calibration(struct tas2781_hda *p);
+void tas2781_hda_remove(struct device *dev,
+ const struct component_ops *ops);
+int tasdevice_info_profile(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uctl);
+int tasdevice_info_programs(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uctl);
+int tasdevice_info_config(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *uctl);
+int tasdevice_set_profile_id(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl);
+int tasdevice_get_profile_id(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl);
+int tasdevice_program_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl);
+int tasdevice_program_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl);
+int tasdevice_config_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl);
+int tasdevice_config_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl);
+
+#endif
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
new file mode 100644
index 000000000000..c8619995b1d7
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
@@ -0,0 +1,834 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TAS2781 HDA I2C driver
+//
+// Copyright 2023 - 2025 Texas Instruments, Inc.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+// Current maintainer: Baojun Xu <baojun.xu@ti.com>
+
+#include <linux/unaligned.h>
+#include <linux/acpi.h>
+#include <linux/crc8.h>
+#include <linux/crc32.h>
+#include <linux/efi.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pci_ids.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/hda_codec.h>
+#include <sound/soc.h>
+#include <sound/tas2781.h>
+#include <sound/tas2781-comlib-i2c.h>
+#include <sound/tlv.h>
+#include <sound/tas2770-tlv.h>
+#include <sound/tas2781-tlv.h>
+#include <sound/tas5825-tlv.h>
+
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_component.h"
+#include "hda_jack.h"
+#include "../generic.h"
+#include "tas2781_hda.h"
+
+#define TAS2563_CAL_VAR_NAME_MAX 16
+#define TAS2563_CAL_ARRAY_SIZE 80
+#define TAS2563_CAL_DATA_SIZE 4
+#define TAS2563_MAX_CHANNELS 4
+#define TAS2563_CAL_CH_SIZE 20
+
+#define TAS2563_CAL_R0_LOW TASDEVICE_REG(0, 0x0f, 0x48)
+#define TAS2563_CAL_POWER TASDEVICE_REG(0, 0x0d, 0x3c)
+#define TAS2563_CAL_INVR0 TASDEVICE_REG(0, 0x0f, 0x40)
+#define TAS2563_CAL_TLIM TASDEVICE_REG(0, 0x10, 0x14)
+#define TAS2563_CAL_R0 TASDEVICE_REG(0, 0x0f, 0x34)
+
+enum device_chip_id {
+ HDA_TAS2563,
+ HDA_TAS2770,
+ HDA_TAS2781,
+ HDA_TAS5825,
+ HDA_OTHERS
+};
+
+struct tas2781_hda_i2c_priv {
+ struct snd_kcontrol *snd_ctls[2];
+ int (*save_calibration)(struct tas2781_hda *h);
+
+ int hda_chip_id;
+};
+
+static int tas2781_get_i2c_res(struct acpi_resource *ares, void *data)
+{
+ struct tasdevice_priv *tas_priv = data;
+ struct acpi_resource_i2c_serialbus *sb;
+
+ if (i2c_acpi_get_i2c_resource(ares, &sb)) {
+ if (tas_priv->ndev < TASDEVICE_MAX_CHANNELS &&
+ sb->slave_address != tas_priv->global_addr) {
+ tas_priv->tasdevice[tas_priv->ndev].dev_addr =
+ (unsigned int)sb->slave_address;
+ tas_priv->ndev++;
+ }
+ }
+ return 1;
+}
+
+static const struct acpi_gpio_params speakerid_gpios = { 0, 0, false };
+
+static const struct acpi_gpio_mapping tas2781_speaker_id_gpios[] = {
+ { "speakerid-gpios", &speakerid_gpios, 1 },
+ { }
+};
+
+static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid)
+{
+ struct gpio_desc *speaker_id;
+ struct acpi_device *adev;
+ struct device *physdev;
+ LIST_HEAD(resources);
+ const char *sub;
+ uint32_t subid;
+ int ret;
+
+ adev = acpi_dev_get_first_match_dev(hid, NULL, -1);
+ if (!adev) {
+ dev_err(p->dev,
+ "Failed to find an ACPI device for %s\n", hid);
+ return -ENODEV;
+ }
+
+ physdev = get_device(acpi_get_first_physical_node(adev));
+ ret = acpi_dev_get_resources(adev, &resources, tas2781_get_i2c_res, p);
+ if (ret < 0) {
+ dev_err(p->dev, "Failed to get ACPI resource.\n");
+ goto err;
+ }
+ sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev));
+ if (IS_ERR(sub)) {
+ /* No subsys id in older tas2563 projects. */
+ if (!strncmp(hid, "INT8866", sizeof("INT8866")))
+ goto end_2563;
+ dev_err(p->dev, "Failed to get SUBSYS ID.\n");
+ ret = PTR_ERR(sub);
+ goto err;
+ }
+ /* Speaker id was needed for ASUS projects. */
+ ret = kstrtou32(sub, 16, &subid);
+ if (!ret && upper_16_bits(subid) == PCI_VENDOR_ID_ASUSTEK) {
+ ret = acpi_dev_add_driver_gpios(adev, tas2781_speaker_id_gpios);
+ if (ret < 0) {
+ dev_err(p->dev, "Failed to add driver gpio %d.\n",
+ ret);
+ p->speaker_id = -1;
+ goto end_2563;
+ }
+
+ speaker_id = fwnode_gpiod_get_index(acpi_fwnode_handle(adev),
+ "speakerid", 0, GPIOD_IN, NULL);
+ if (!IS_ERR(speaker_id)) {
+ p->speaker_id = gpiod_get_value_cansleep(speaker_id);
+ dev_dbg(p->dev, "Got speaker id gpio from ACPI: %d.\n",
+ p->speaker_id);
+ gpiod_put(speaker_id);
+ } else {
+ p->speaker_id = -1;
+ ret = PTR_ERR(speaker_id);
+ dev_err(p->dev, "Get speaker id gpio failed %d.\n",
+ ret);
+ }
+
+ acpi_dev_remove_driver_gpios(adev);
+ } else {
+ p->speaker_id = -1;
+ }
+
+end_2563:
+ acpi_dev_free_resource_list(&resources);
+ strscpy(p->dev_name, hid, sizeof(p->dev_name));
+ put_device(physdev);
+ acpi_dev_put(adev);
+
+ return 0;
+
+err:
+ dev_err(p->dev, "read acpi error, ret: %d\n", ret);
+ put_device(physdev);
+ acpi_dev_put(adev);
+
+ return ret;
+}
+
+static void tas2781_hda_playback_hook(struct device *dev, int action)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+ dev_dbg(tas_hda->dev, "%s: action = %d\n", __func__, action);
+ switch (action) {
+ case HDA_GEN_PCM_ACT_OPEN:
+ pm_runtime_get_sync(dev);
+ scoped_guard(mutex, &tas_hda->priv->codec_lock) {
+ tasdevice_tuning_switch(tas_hda->priv, 0);
+ tas_hda->priv->playback_started = true;
+ }
+ break;
+ case HDA_GEN_PCM_ACT_CLOSE:
+ scoped_guard(mutex, &tas_hda->priv->codec_lock) {
+ tasdevice_tuning_switch(tas_hda->priv, 1);
+ tas_hda->priv->playback_started = false;
+ }
+
+ pm_runtime_put_autosuspend(dev);
+ break;
+ default:
+ break;
+ }
+}
+
+static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int ret;
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ ret = tasdevice_amp_getvol(tas_priv, ucontrol, mc);
+
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: %ld\n",
+ __func__, kcontrol->id.name, ucontrol->value.integer.value[0]);
+
+ return ret;
+}
+
+static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: -> %ld\n",
+ __func__, kcontrol->id.name, ucontrol->value.integer.value[0]);
+
+ /* The check of the given value is in tasdevice_amp_putvol. */
+ return tasdevice_amp_putvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status;
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d\n",
+ __func__, kcontrol->id.name, tas_priv->force_fwload_status);
+
+ return 0;
+}
+
+static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ bool change, val = (bool)ucontrol->value.integer.value[0];
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ dev_dbg(tas_priv->dev, "%s: kcontrol %s: %d -> %d\n",
+ __func__, kcontrol->id.name,
+ tas_priv->force_fwload_status, val);
+
+ if (tas_priv->force_fwload_status == val)
+ change = false;
+ else {
+ change = true;
+ tas_priv->force_fwload_status = val;
+ }
+
+ return change;
+}
+
+static const struct snd_kcontrol_new tas2770_snd_controls[] = {
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Volume", TAS2770_AMP_LEVEL,
+ 0, 0, 20, 0, tas2781_amp_getvol,
+ tas2781_amp_putvol, tas2770_amp_tlv),
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS2770_DVC_LEVEL,
+ 0, 0, 200, 1, tas2781_amp_getvol,
+ tas2781_amp_putvol, tas2770_dvc_tlv),
+};
+
+static const struct snd_kcontrol_new tas2781_snd_controls[] = {
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Volume", TAS2781_AMP_LEVEL,
+ 1, 0, 20, 0, tas2781_amp_getvol,
+ tas2781_amp_putvol, tas2781_amp_tlv),
+ ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0,
+ tas2781_force_fwload_get, tas2781_force_fwload_put),
+};
+
+static const struct snd_kcontrol_new tas5825_snd_controls[] = {
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Analog Volume", TAS5825_AMP_LEVEL,
+ 0, 0, 31, 1, tas2781_amp_getvol,
+ tas2781_amp_putvol, tas5825_amp_tlv),
+ ACARD_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS5825_DVC_LEVEL,
+ 0, 0, 254, 1, tas2781_amp_getvol,
+ tas2781_amp_putvol, tas5825_dvc_tlv),
+ ACARD_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0,
+ tas2781_force_fwload_get, tas2781_force_fwload_put),
+};
+
+static const struct snd_kcontrol_new tasdevice_prof_ctrl = {
+ .name = "Speaker Profile Id",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_profile,
+ .get = tasdevice_get_profile_id,
+ .put = tasdevice_set_profile_id,
+};
+
+static const struct snd_kcontrol_new tasdevice_dsp_prog_ctrl = {
+ .name = "Speaker Program Id",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_programs,
+ .get = tasdevice_program_get,
+ .put = tasdevice_program_put,
+};
+
+static const struct snd_kcontrol_new tasdevice_dsp_conf_ctrl = {
+ .name = "Speaker Config Id",
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_config,
+ .get = tasdevice_config_get,
+ .put = tasdevice_config_put,
+};
+
+static int tas2563_save_calibration(struct tas2781_hda *h)
+{
+ efi_guid_t efi_guid = tasdev_fct_efi_guid[LENOVO];
+ char *vars[TASDEV_CALIB_N] = {
+ "R0_%d", "R0_Low_%d", "InvR0_%d", "Power_%d", "TLim_%d"
+ };
+ efi_char16_t efi_name[TAS2563_CAL_VAR_NAME_MAX];
+ unsigned long max_size = TAS2563_CAL_DATA_SIZE;
+ unsigned char var8[TAS2563_CAL_VAR_NAME_MAX];
+ struct tasdevice_priv *p = h->priv;
+ struct calidata *cd = &p->cali_data;
+ struct cali_reg *r = &cd->cali_reg_array;
+ unsigned int offset = 0;
+ unsigned char *data;
+ __be32 bedata;
+ efi_status_t status;
+ unsigned int attr;
+ int ret, i, j, k;
+
+ if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) {
+ dev_err(p->dev, "%s: NO EFI FOUND!\n", __func__);
+ return -EINVAL;
+ }
+
+ cd->cali_dat_sz_per_dev = TAS2563_CAL_DATA_SIZE * TASDEV_CALIB_N;
+
+ /* extra byte for each device is the device number */
+ cd->total_sz = (cd->cali_dat_sz_per_dev + 1) * p->ndev;
+ data = cd->data = devm_kzalloc(p->dev, cd->total_sz,
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ for (i = 0; i < p->ndev; ++i) {
+ data[offset] = i;
+ offset++;
+ for (j = 0; j < TASDEV_CALIB_N; ++j) {
+ /* EFI name for calibration started with 1, not 0 */
+ ret = snprintf(var8, sizeof(var8), vars[j], i + 1);
+ if (ret < 0 || ret >= sizeof(var8) - 1) {
+ dev_err(p->dev, "%s: Read %s failed\n",
+ __func__, var8);
+ return -EINVAL;
+ }
+ /*
+ * Our variable names are ASCII by construction, but
+ * EFI names are wide chars. Convert and zero-pad.
+ */
+ memset(efi_name, 0, sizeof(efi_name));
+ for (k = 0; k < sizeof(var8) && var8[k]; k++)
+ efi_name[k] = var8[k];
+ status = efi.get_variable(efi_name,
+ &efi_guid, &attr, &max_size,
+ &data[offset]);
+ if (status != EFI_SUCCESS ||
+ max_size != TAS2563_CAL_DATA_SIZE) {
+ dev_warn(p->dev,
+ "Dev %d: Caldat[%d] read failed %ld\n",
+ i, j, status);
+ return -EINVAL;
+ }
+ bedata = cpu_to_be32(*(uint32_t *)&data[offset]);
+ memcpy(&data[offset], &bedata, sizeof(bedata));
+ offset += TAS2563_CAL_DATA_SIZE;
+ }
+ }
+
+ if (cd->total_sz != offset) {
+ dev_err(p->dev, "%s: tot_size(%lu) and offset(%u) mismatch\n",
+ __func__, cd->total_sz, offset);
+ return -EINVAL;
+ }
+
+ r->r0_reg = TAS2563_CAL_R0;
+ r->invr0_reg = TAS2563_CAL_INVR0;
+ r->r0_low_reg = TAS2563_CAL_R0_LOW;
+ r->pow_reg = TAS2563_CAL_POWER;
+ r->tlimit_reg = TAS2563_CAL_TLIM;
+
+ /*
+ * TAS2781_FMWLIB supports two solutions of calibrated data. One is
+ * from the driver itself: driver reads the calibrated files directly
+ * during probe; The other from user space: during init of audio hal,
+ * the audio hal will pass the calibrated data via kcontrol interface.
+ * Driver will store this data in "struct calidata" for use. For hda
+ * device, calibrated data are usunally saved into UEFI. So Hda side
+ * codec driver use the mixture of these two solutions, driver reads
+ * the data from UEFI, then store this data in "struct calidata" for
+ * use.
+ */
+ p->is_user_space_calidata = true;
+
+ return 0;
+}
+
+static void tas2781_hda_remove_controls(struct tas2781_hda *tas_hda)
+{
+ struct tas2781_hda_i2c_priv *hda_priv = tas_hda->hda_priv;
+ struct hda_codec *codec = tas_hda->priv->codec;
+
+ snd_ctl_remove(codec->card, tas_hda->dsp_prog_ctl);
+ snd_ctl_remove(codec->card, tas_hda->dsp_conf_ctl);
+
+ for (int i = ARRAY_SIZE(hda_priv->snd_ctls) - 1; i >= 0; i--)
+ snd_ctl_remove(codec->card, hda_priv->snd_ctls[i]);
+
+ snd_ctl_remove(codec->card, tas_hda->prof_ctl);
+}
+
+static void tasdev_add_kcontrols(struct tasdevice_priv *tas_priv,
+ struct snd_kcontrol **ctls, struct hda_codec *codec,
+ const struct snd_kcontrol_new *tas_snd_ctrls, int num_ctls)
+{
+ int i, ret;
+
+ for (i = 0; i < num_ctls; i++) {
+ ctls[i] = snd_ctl_new1(
+ &tas_snd_ctrls[i], tas_priv);
+ ret = snd_ctl_add(codec->card, ctls[i]);
+ if (ret) {
+ dev_err(tas_priv->dev,
+ "Failed to add KControl %s = %d\n",
+ tas_snd_ctrls[i].name, ret);
+ break;
+ }
+ }
+}
+
+static void tasdevice_dspfw_init(void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev);
+ struct tas2781_hda_i2c_priv *hda_priv = tas_hda->hda_priv;
+ struct hda_codec *codec = tas_priv->codec;
+ int ret;
+
+ tasdevice_dsp_remove(tas_priv);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+ if (tas_priv->speaker_id >= 0) {
+ snprintf(tas_priv->coef_binaryname,
+ sizeof(tas_priv->coef_binaryname),
+ "TAS2XXX%04X%d.bin",
+ lower_16_bits(codec->core.subsystem_id),
+ tas_priv->speaker_id);
+ } else {
+ snprintf(tas_priv->coef_binaryname,
+ sizeof(tas_priv->coef_binaryname),
+ "TAS2XXX%04X.bin",
+ lower_16_bits(codec->core.subsystem_id));
+ }
+ ret = tasdevice_dsp_parser(tas_priv);
+ if (ret) {
+ dev_err(tas_priv->dev, "dspfw load %s error\n",
+ tas_priv->coef_binaryname);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ return;
+ }
+ tasdev_add_kcontrols(tas_priv, &tas_hda->dsp_prog_ctl, codec,
+ &tasdevice_dsp_prog_ctrl, 1);
+ tasdev_add_kcontrols(tas_priv, &tas_hda->dsp_conf_ctl, codec,
+ &tasdevice_dsp_conf_ctrl, 1);
+
+ tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+ tasdevice_prmg_load(tas_priv, 0);
+ if (tas_priv->fmw->nr_programs > 0)
+ tas_priv->cur_prog = 0;
+ if (tas_priv->fmw->nr_configurations > 0)
+ tas_priv->cur_conf = 0;
+
+ /* Init common setting for different audio profiles */
+ if (tas_priv->rcabin.init_profile_id >= 0)
+ tasdevice_select_cfg_blk(tas_priv,
+ tas_priv->rcabin.init_profile_id,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP);
+
+ /* If calibrated data occurs error, dsp will still works with default
+ * calibrated data inside algo.
+ */
+ hda_priv->save_calibration(tas_hda);
+}
+
+static void tasdev_fw_ready(const struct firmware *fmw, void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev);
+ struct tas2781_hda_i2c_priv *hda_priv = tas_hda->hda_priv;
+ struct hda_codec *codec = tas_priv->codec;
+ int ret;
+
+ pm_runtime_get_sync(tas_priv->dev);
+ mutex_lock(&tas_priv->codec_lock);
+
+ ret = tasdevice_rca_parser(tas_priv, fmw);
+ if (ret)
+ goto out;
+
+ tas_priv->fw_state = TASDEVICE_RCA_FW_OK;
+ tasdev_add_kcontrols(tas_priv, &tas_hda->prof_ctl, codec,
+ &tasdevice_prof_ctrl, 1);
+
+ switch (hda_priv->hda_chip_id) {
+ case HDA_TAS2770:
+ tasdev_add_kcontrols(tas_priv, hda_priv->snd_ctls, codec,
+ &tas2770_snd_controls[0],
+ ARRAY_SIZE(tas2770_snd_controls));
+ break;
+ case HDA_TAS2781:
+ tasdev_add_kcontrols(tas_priv, hda_priv->snd_ctls, codec,
+ &tas2781_snd_controls[0],
+ ARRAY_SIZE(tas2781_snd_controls));
+ tasdevice_dspfw_init(context);
+ break;
+ case HDA_TAS5825:
+ tasdev_add_kcontrols(tas_priv, hda_priv->snd_ctls, codec,
+ &tas5825_snd_controls[0],
+ ARRAY_SIZE(tas5825_snd_controls));
+ tasdevice_dspfw_init(context);
+ break;
+ case HDA_TAS2563:
+ tasdevice_dspfw_init(context);
+ break;
+ default:
+ break;
+ }
+
+out:
+ mutex_unlock(&tas_hda->priv->codec_lock);
+ release_firmware(fmw);
+ pm_runtime_put_autosuspend(tas_hda->dev);
+}
+
+static int tas2781_hda_bind(struct device *dev, struct device *master,
+ void *master_data)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
+ struct hda_codec *codec;
+ unsigned int subid;
+ int ret;
+
+ comp = hda_component_from_index(parent, tas_hda->priv->index);
+ if (!comp)
+ return -EINVAL;
+
+ if (comp->dev)
+ return -EBUSY;
+
+ codec = parent->codec;
+ subid = codec->core.subsystem_id >> 16;
+
+ switch (subid) {
+ case 0x1028:
+ tas_hda->catlog_id = DELL;
+ break;
+ default:
+ tas_hda->catlog_id = LENOVO;
+ break;
+ }
+
+ pm_runtime_get_sync(dev);
+
+ comp->dev = dev;
+
+ strscpy(comp->name, dev_name(dev), sizeof(comp->name));
+
+ ret = tascodec_init(tas_hda->priv, codec, THIS_MODULE, tasdev_fw_ready);
+ if (!ret)
+ comp->playback_hook = tas2781_hda_playback_hook;
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static void tas2781_hda_unbind(struct device *dev,
+ struct device *master, void *master_data)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
+
+ comp = hda_component_from_index(parent, tas_hda->priv->index);
+ if (comp && (comp->dev == dev)) {
+ comp->dev = NULL;
+ memset(comp->name, 0, sizeof(comp->name));
+ comp->playback_hook = NULL;
+ }
+
+ tas2781_hda_remove_controls(tas_hda);
+
+ tasdevice_config_info_remove(tas_hda->priv);
+ tasdevice_dsp_remove(tas_hda->priv);
+
+ tas_hda->priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+}
+
+static const struct component_ops tas2781_hda_comp_ops = {
+ .bind = tas2781_hda_bind,
+ .unbind = tas2781_hda_unbind,
+};
+
+static int tas2781_hda_i2c_probe(struct i2c_client *clt)
+{
+ struct tas2781_hda_i2c_priv *hda_priv;
+ struct tas2781_hda *tas_hda;
+ const char *device_name;
+ int ret;
+
+ tas_hda = devm_kzalloc(&clt->dev, sizeof(*tas_hda), GFP_KERNEL);
+ if (!tas_hda)
+ return -ENOMEM;
+
+ hda_priv = devm_kzalloc(&clt->dev, sizeof(*hda_priv), GFP_KERNEL);
+ if (!hda_priv)
+ return -ENOMEM;
+
+ tas_hda->hda_priv = hda_priv;
+
+ dev_set_drvdata(&clt->dev, tas_hda);
+ tas_hda->dev = &clt->dev;
+
+ tas_hda->priv = tasdevice_kzalloc(clt);
+ if (!tas_hda->priv)
+ return -ENOMEM;
+
+ if (strstr(dev_name(&clt->dev), "TIAS2781")) {
+ /*
+ * TAS2781, integrated on-chip DSP with
+ * global I2C address supported.
+ */
+ device_name = "TIAS2781";
+ hda_priv->hda_chip_id = HDA_TAS2781;
+ hda_priv->save_calibration = tas2781_save_calibration;
+ tas_hda->priv->global_addr = TAS2781_GLOBAL_ADDR;
+ } else if (strstarts(dev_name(&clt->dev), "i2c-TXNW2770")) {
+ /*
+ * TAS2770, has no on-chip DSP, so no calibration data
+ * required; has no global I2C address supported.
+ */
+ device_name = "TXNW2770";
+ hda_priv->hda_chip_id = HDA_TAS2770;
+ } else if (strstarts(dev_name(&clt->dev),
+ "i2c-TXNW2781:00-tas2781-hda.0")) {
+ device_name = "TXNW2781";
+ hda_priv->hda_chip_id = HDA_TAS2781;
+ hda_priv->save_calibration = tas2781_save_calibration;
+ tas_hda->priv->global_addr = TAS2781_GLOBAL_ADDR;
+ } else if (strstr(dev_name(&clt->dev), "INT8866")) {
+ /*
+ * TAS2563, integrated on-chip DSP with
+ * global I2C address supported.
+ */
+ device_name = "INT8866";
+ hda_priv->hda_chip_id = HDA_TAS2563;
+ hda_priv->save_calibration = tas2563_save_calibration;
+ tas_hda->priv->global_addr = TAS2563_GLOBAL_ADDR;
+ } else if (strstarts(dev_name(&clt->dev), "i2c-TXNW5825")) {
+ /*
+ * TAS5825, integrated on-chip DSP without
+ * global I2C address and calibration supported.
+ */
+ device_name = "TXNW5825";
+ hda_priv->hda_chip_id = HDA_TAS5825;
+ tas_hda->priv->chip_id = TAS5825;
+ } else {
+ return -ENODEV;
+ }
+
+ tas_hda->priv->irq = clt->irq;
+ ret = tas2781_read_acpi(tas_hda->priv, device_name);
+ if (ret)
+ return dev_err_probe(tas_hda->dev, ret,
+ "Platform not supported\n");
+
+ ret = tasdevice_init(tas_hda->priv);
+ if (ret)
+ goto err;
+
+ pm_runtime_set_autosuspend_delay(tas_hda->dev, 3000);
+ pm_runtime_use_autosuspend(tas_hda->dev);
+ pm_runtime_mark_last_busy(tas_hda->dev);
+ pm_runtime_set_active(tas_hda->dev);
+ pm_runtime_enable(tas_hda->dev);
+
+ tasdevice_reset(tas_hda->priv);
+
+ ret = component_add(tas_hda->dev, &tas2781_hda_comp_ops);
+ if (ret) {
+ dev_err(tas_hda->dev, "Register component failed: %d\n", ret);
+ pm_runtime_disable(tas_hda->dev);
+ }
+
+err:
+ if (ret)
+ tas2781_hda_remove(&clt->dev, &tas2781_hda_comp_ops);
+ return ret;
+}
+
+static void tas2781_hda_i2c_remove(struct i2c_client *clt)
+{
+ tas2781_hda_remove(&clt->dev, &tas2781_hda_comp_ops);
+}
+
+static int tas2781_runtime_suspend(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+ dev_dbg(tas_hda->dev, "Runtime Suspend\n");
+
+ guard(mutex)(&tas_hda->priv->codec_lock);
+
+ /* The driver powers up the amplifiers at module load time.
+ * Stop the playback if it's unused.
+ */
+ if (tas_hda->priv->playback_started) {
+ tasdevice_tuning_switch(tas_hda->priv, 1);
+ tas_hda->priv->playback_started = false;
+ }
+
+ return 0;
+}
+
+static int tas2781_runtime_resume(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+ dev_dbg(tas_hda->dev, "Runtime Resume\n");
+
+ guard(mutex)(&tas_hda->priv->codec_lock);
+
+ tasdevice_prmg_load(tas_hda->priv, tas_hda->priv->cur_prog);
+
+ return 0;
+}
+
+static int tas2781_system_suspend(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+
+ dev_dbg(tas_hda->priv->dev, "System Suspend\n");
+
+ guard(mutex)(&tas_hda->priv->codec_lock);
+
+ /* Shutdown chip before system suspend */
+ if (tas_hda->priv->playback_started)
+ tasdevice_tuning_switch(tas_hda->priv, 1);
+
+ /*
+ * Reset GPIO may be shared, so cannot reset here.
+ * However beyond this point, amps may be powered down.
+ */
+ return 0;
+}
+
+static int tas2781_system_resume(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ int i;
+
+ dev_dbg(tas_hda->priv->dev, "System Resume\n");
+
+ guard(mutex)(&tas_hda->priv->codec_lock);
+
+ for (i = 0; i < tas_hda->priv->ndev; i++) {
+ tas_hda->priv->tasdevice[i].cur_book = -1;
+ tas_hda->priv->tasdevice[i].cur_prog = -1;
+ tas_hda->priv->tasdevice[i].cur_conf = -1;
+ }
+ tasdevice_reset(tas_hda->priv);
+ tasdevice_prmg_load(tas_hda->priv, tas_hda->priv->cur_prog);
+
+ /* Init common setting for different audio profiles */
+ if (tas_hda->priv->rcabin.init_profile_id >= 0)
+ tasdevice_select_cfg_blk(tas_hda->priv,
+ tas_hda->priv->rcabin.init_profile_id,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP);
+
+ if (tas_hda->priv->playback_started)
+ tasdevice_tuning_switch(tas_hda->priv, 0);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tas2781_hda_pm_ops = {
+ RUNTIME_PM_OPS(tas2781_runtime_suspend, tas2781_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(tas2781_system_suspend, tas2781_system_resume)
+};
+
+static const struct i2c_device_id tas2781_hda_i2c_id[] = {
+ { "tas2781-hda" },
+ {}
+};
+
+static const struct acpi_device_id tas2781_acpi_hda_match[] = {
+ {"INT8866", 0 },
+ {"TIAS2781", 0 },
+ {"TXNW2770", 0 },
+ {"TXNW2781", 0 },
+ {"TXNW5825", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, tas2781_acpi_hda_match);
+
+static struct i2c_driver tas2781_hda_i2c_driver = {
+ .driver = {
+ .name = "tas2781-hda",
+ .acpi_match_table = tas2781_acpi_hda_match,
+ .pm = &tas2781_hda_pm_ops,
+ },
+ .id_table = tas2781_hda_i2c_id,
+ .probe = tas2781_hda_i2c_probe,
+ .remove = tas2781_hda_i2c_remove,
+};
+module_i2c_driver(tas2781_hda_i2c_driver);
+
+MODULE_DESCRIPTION("TAS2781 HDA Driver");
+MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_TAS2781_FMWLIB");
+MODULE_IMPORT_NS("SND_HDA_SCODEC_TAS2781");
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
new file mode 100644
index 000000000000..b9a55672bf15
--- /dev/null
+++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
@@ -0,0 +1,956 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TAS2781 HDA SPI driver
+//
+// Copyright 2024 - 2025 Texas Instruments, Inc.
+//
+// Author: Baojun Xu <baojun.xu@ti.com>
+
+#include <linux/acpi.h>
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/crc8.h>
+#include <linux/crc32.h>
+#include <linux/efi.h>
+#include <linux/firmware.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <sound/hda_codec.h>
+#include <sound/soc.h>
+#include <sound/tas2781.h>
+#include <sound/tlv.h>
+#include <sound/tas2781-tlv.h>
+
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_component.h"
+#include "hda_jack.h"
+#include "../generic.h"
+#include "tas2781_hda.h"
+
+#define TASDEVICE_RANGE_MAX_SIZE (256 * 128)
+#define TASDEVICE_WIN_LEN 128
+#define TAS2781_SPI_MAX_FREQ (4 * HZ_PER_MHZ)
+/* Flag of calibration registers address. */
+#define TASDEVICE_CALIBRATION_REG_ADDRESS BIT(7)
+#define TASDEV_UEFI_CALI_REG_ADDR_FLG BIT(7)
+
+/* System Reset Check Register */
+#define TAS2781_REG_CLK_CONFIG TASDEVICE_REG(0x0, 0x0, 0x5c)
+#define TAS2781_REG_CLK_CONFIG_RESET 0x19
+
+struct tas2781_hda_spi_priv {
+ struct snd_kcontrol *snd_ctls[3];
+};
+
+static const struct regmap_range_cfg tasdevice_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = TASDEVICE_RANGE_MAX_SIZE,
+ .selector_reg = TASDEVICE_PAGE_SELECT,
+ .selector_mask = GENMASK(7, 0),
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = TASDEVICE_WIN_LEN,
+ },
+};
+
+static const struct regmap_config tasdevice_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .zero_flag_mask = true,
+ .read_flag_mask = 0x01,
+ .reg_shift = -1,
+ .cache_type = REGCACHE_NONE,
+ .ranges = tasdevice_ranges,
+ .num_ranges = ARRAY_SIZE(tasdevice_ranges),
+ .max_register = TASDEVICE_RANGE_MAX_SIZE,
+};
+
+static int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv,
+ unsigned short chn, unsigned int reg, unsigned int *val)
+{
+ int ret;
+
+ /*
+ * In our TAS2781 SPI mode, if read from other book (not book 0),
+ * or read from page number larger than 1 in book 0, one more byte
+ * read is needed, and first byte is a dummy byte, need to be ignored.
+ */
+ if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) {
+ unsigned char data[2];
+
+ ret = tasdevice_dev_bulk_read(tas_priv, chn, reg,
+ data, sizeof(data));
+ *val = data[1];
+ } else {
+ ret = tasdevice_dev_read(tas_priv, chn, reg, val);
+ }
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int tasdevice_spi_dev_bulk_read(struct tasdevice_priv *tas_priv,
+ unsigned short chn, unsigned int reg, unsigned char *data,
+ unsigned int len)
+{
+ int ret;
+
+ /*
+ * In our TAS2781 SPI mode, if read from other book (not book 0),
+ * or read from page number larger than 1 in book 0, one more byte
+ * read is needed, and first byte is a dummy byte, need to be ignored.
+ */
+ if ((TASDEVICE_BOOK_ID(reg) > 0) || (TASDEVICE_PAGE_ID(reg) > 1)) {
+ unsigned char buf[TASDEVICE_WIN_LEN + 1];
+
+ ret = tasdevice_dev_bulk_read(tas_priv, chn, reg,
+ buf, len + 1);
+ memcpy(data, buf + 1, len);
+ } else {
+ ret = tasdevice_dev_bulk_read(tas_priv, chn, reg, data, len);
+ }
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int tasdevice_spi_dev_update_bits(struct tasdevice_priv *tas_priv,
+ unsigned short chn, unsigned int reg, unsigned int mask,
+ unsigned int value)
+{
+ int ret, val;
+
+ /*
+ * In our TAS2781 SPI mode, read/write was masked in last bit of
+ * address, it cause regmap_update_bits() not work as expected.
+ */
+ ret = tasdevice_dev_read(tas_priv, chn, reg, &val);
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = tasdevice_dev_write(tas_priv, chn, TASDEVICE_PAGE_REG(reg),
+ (val & ~mask) | (mask & value));
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int tasdevice_spi_change_chn_book(struct tasdevice_priv *p,
+ unsigned short chn, int book)
+{
+ int ret = 0;
+
+ if (chn == p->index) {
+ struct tasdevice *tasdev = &p->tasdevice[chn];
+ struct regmap *map = p->regmap;
+
+ if (tasdev->cur_book != book) {
+ ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book);
+ if (ret < 0)
+ dev_err(p->dev, "%s, E=%d\n", __func__, ret);
+ else
+ tasdev->cur_book = book;
+ }
+ } else {
+ ret = -EXDEV;
+ dev_dbg(p->dev, "Not error, %s ignore channel(%d)\n",
+ __func__, chn);
+ }
+
+ return ret;
+}
+
+static void tas2781_spi_reset(struct tasdevice_priv *tas_dev)
+{
+ int ret;
+
+ if (tas_dev->reset) {
+ gpiod_set_value_cansleep(tas_dev->reset, 0);
+ fsleep(800);
+ gpiod_set_value_cansleep(tas_dev->reset, 1);
+ } else {
+ ret = tasdevice_dev_write(tas_dev, tas_dev->index,
+ TASDEVICE_REG_SWRESET, TASDEVICE_REG_SWRESET_RESET);
+ if (ret < 0) {
+ dev_err(tas_dev->dev, "dev sw-reset fail, %d\n", ret);
+ return;
+ }
+ fsleep(1000);
+ }
+}
+
+static int tascodec_spi_init(struct tasdevice_priv *tas_priv,
+ void *codec, struct module *module,
+ void (*cont)(const struct firmware *fw, void *context))
+{
+ int ret;
+
+ /*
+ * Codec Lock Hold to ensure that codec_probe and firmware parsing and
+ * loading do not simultaneously execute.
+ */
+ guard(mutex)(&tas_priv->codec_lock);
+
+ scnprintf(tas_priv->rca_binaryname,
+ sizeof(tas_priv->rca_binaryname), "%sRCA%d.bin",
+ tas_priv->dev_name, tas_priv->ndev);
+ crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL);
+ tas_priv->codec = codec;
+ ret = request_firmware_nowait(module, FW_ACTION_UEVENT,
+ tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv,
+ cont);
+ if (ret)
+ dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n",
+ ret);
+
+ return ret;
+}
+
+static void tasdevice_spi_init(struct tasdevice_priv *tas_priv)
+{
+ tas_priv->tasdevice[tas_priv->index].cur_book = -1;
+ tas_priv->tasdevice[tas_priv->index].cur_conf = -1;
+ tas_priv->tasdevice[tas_priv->index].cur_prog = -1;
+
+ tas_priv->isspi = true;
+
+ tas_priv->update_bits = tasdevice_spi_dev_update_bits;
+ tas_priv->change_chn_book = tasdevice_spi_change_chn_book;
+ tas_priv->dev_read = tasdevice_spi_dev_read;
+ tas_priv->dev_bulk_read = tasdevice_spi_dev_bulk_read;
+
+ mutex_init(&tas_priv->codec_lock);
+}
+
+static int tasdevice_spi_amp_putvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ unsigned char mask;
+ int max = mc->max;
+ int val, ret;
+
+ mask = rounddown_pow_of_two(max);
+ mask <<= mc->shift;
+ val = clamp(invert ? max - ucontrol->value.integer.value[0] :
+ ucontrol->value.integer.value[0], 0, max);
+
+ ret = tasdevice_spi_dev_update_bits(tas_priv, tas_priv->index,
+ mc->reg, mask, (unsigned int)(val << mc->shift));
+ if (ret)
+ dev_err(tas_priv->dev, "set AMP vol error in dev %d\n",
+ tas_priv->index);
+
+ return ret;
+}
+
+static int tasdevice_spi_amp_getvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ unsigned char mask = 0;
+ int max = mc->max;
+ int ret, val;
+
+ ret = tasdevice_spi_dev_read(tas_priv, tas_priv->index, mc->reg, &val);
+ if (ret) {
+ dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__);
+ return ret;
+ }
+
+ mask = rounddown_pow_of_two(max);
+ mask <<= mc->shift;
+ val = (val & mask) >> mc->shift;
+ val = clamp(invert ? max - val : val, 0, max);
+ ucontrol->value.integer.value[0] = val;
+
+ return ret;
+}
+
+static int tasdevice_spi_digital_putvol(struct tasdevice_priv *p,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ int max = mc->max;
+ int val, ret;
+
+ val = clamp(invert ? max - ucontrol->value.integer.value[0] :
+ ucontrol->value.integer.value[0], 0, max);
+ ret = tasdevice_dev_write(p, p->index, mc->reg, (unsigned int)val);
+ if (ret)
+ dev_err(p->dev, "set digital vol err in dev %d\n", p->index);
+
+ return ret;
+}
+
+static int tasdevice_spi_digital_getvol(struct tasdevice_priv *p,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ int max = mc->max;
+ int ret, val;
+
+ ret = tasdevice_spi_dev_read(p, p->index, mc->reg, &val);
+ if (ret) {
+ dev_err(p->dev, "%s, get digital vol err\n", __func__);
+ return ret;
+ }
+
+ val = clamp(invert ? max - val : val, 0, max);
+ ucontrol->value.integer.value[0] = val;
+
+ return ret;
+}
+
+static int tas2781_read_acpi(struct tas2781_hda *tas_hda,
+ const char *hid, int id)
+{
+ struct tasdevice_priv *p = tas_hda->priv;
+ struct acpi_device *adev;
+ struct device *physdev;
+ u32 values[HDA_MAX_COMPONENTS];
+ const char *property;
+ size_t nval;
+ int ret, i;
+
+ adev = acpi_dev_get_first_match_dev(hid, NULL, -1);
+ if (!adev) {
+ dev_err(p->dev, "Failed to find ACPI device: %s\n", hid);
+ return -ENODEV;
+ }
+
+ strscpy(p->dev_name, hid, sizeof(p->dev_name));
+ physdev = get_device(acpi_get_first_physical_node(adev));
+ acpi_dev_put(adev);
+
+ property = "ti,dev-index";
+ ret = device_property_count_u32(physdev, property);
+ if (ret <= 0 || ret > ARRAY_SIZE(values)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ p->ndev = nval = ret;
+
+ ret = device_property_read_u32_array(physdev, property, values, nval);
+ if (ret)
+ goto err;
+
+ p->index = U8_MAX;
+ for (i = 0; i < nval; i++) {
+ if (values[i] == id) {
+ p->index = i;
+ break;
+ }
+ }
+ if (p->index == U8_MAX) {
+ dev_dbg(p->dev, "No index found in %s\n", property);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ if (p->index == 0) {
+ /* All of amps share same RESET pin. */
+ p->reset = devm_gpiod_get_index_optional(physdev, "reset",
+ p->index, GPIOD_OUT_LOW);
+ if (IS_ERR(p->reset)) {
+ ret = PTR_ERR(p->reset);
+ dev_err_probe(p->dev, ret, "Failed on reset GPIO\n");
+ goto err;
+ }
+ }
+ put_device(physdev);
+
+ return 0;
+err:
+ dev_err(p->dev, "read acpi error, ret: %d\n", ret);
+ put_device(physdev);
+ acpi_dev_put(adev);
+
+ return ret;
+}
+
+static void tas2781_hda_playback_hook(struct device *dev, int action)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct tasdevice_priv *tas_priv = tas_hda->priv;
+
+ if (action == HDA_GEN_PCM_ACT_OPEN) {
+ pm_runtime_get_sync(dev);
+ guard(mutex)(&tas_priv->codec_lock);
+ if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK)
+ tasdevice_tuning_switch(tas_hda->priv, 0);
+ } else if (action == HDA_GEN_PCM_ACT_CLOSE) {
+ guard(mutex)(&tas_priv->codec_lock);
+ if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK)
+ tasdevice_tuning_switch(tas_priv, 1);
+ pm_runtime_put_autosuspend(dev);
+ }
+}
+
+/*
+ * tas2781_digital_getvol - get the volum control
+ * @kcontrol: control pointer
+ * @ucontrol: User data
+ *
+ * Customer Kcontrol for tas2781 is primarily for regmap booking, paging
+ * depends on internal regmap mechanism.
+ * tas2781 contains book and page two-level register map, especially
+ * book switching will set the register BXXP00R7F, after switching to the
+ * correct book, then leverage the mechanism for paging to access the
+ * register.
+ *
+ * Return 0 if succeeded.
+ */
+static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ guard(mutex)(&tas_priv->codec_lock);
+ return tasdevice_spi_digital_getvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ guard(mutex)(&tas_priv->codec_lock);
+ return tasdevice_spi_amp_getvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ guard(mutex)(&tas_priv->codec_lock);
+ return tasdevice_spi_digital_putvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ guard(mutex)(&tas_priv->codec_lock);
+ return tasdevice_spi_amp_putvol(tas_priv, ucontrol, mc);
+}
+
+static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status;
+ dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__,
+ str_on_off(tas_priv->force_fwload_status));
+
+ return 0;
+}
+
+static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tasdevice_priv *tas_priv = snd_kcontrol_chip(kcontrol);
+ bool change, val = (bool)ucontrol->value.integer.value[0];
+
+ if (tas_priv->force_fwload_status == val) {
+ change = false;
+ } else {
+ change = true;
+ tas_priv->force_fwload_status = val;
+ }
+ dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__,
+ str_on_off(tas_priv->force_fwload_status));
+
+ return change;
+}
+
+static struct snd_kcontrol_new tas2781_snd_ctls[] = {
+ ACARD_SINGLE_RANGE_EXT_TLV(NULL, TAS2781_AMP_LEVEL, 1, 0, 20, 0,
+ tas2781_amp_getvol, tas2781_amp_putvol,
+ tas2781_amp_tlv),
+ ACARD_SINGLE_RANGE_EXT_TLV(NULL, TAS2781_DVC_LVL, 0, 0, 200, 1,
+ tas2781_digital_getvol, tas2781_digital_putvol,
+ tas2781_dvc_tlv),
+ ACARD_SINGLE_BOOL_EXT(NULL, 0, tas2781_force_fwload_get,
+ tas2781_force_fwload_put),
+};
+
+static struct snd_kcontrol_new tas2781_prof_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_profile,
+ .get = tasdevice_get_profile_id,
+ .put = tasdevice_set_profile_id,
+};
+
+static struct snd_kcontrol_new tas2781_dsp_ctls[] = {
+ /* Speaker Program */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_programs,
+ .get = tasdevice_program_get,
+ .put = tasdevice_program_put,
+ },
+ /* Speaker Config */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+ .info = tasdevice_info_config,
+ .get = tasdevice_config_get,
+ .put = tasdevice_config_put,
+ },
+};
+
+static void tas2781_hda_remove_controls(struct tas2781_hda *tas_hda)
+{
+ struct hda_codec *codec = tas_hda->priv->codec;
+ struct tas2781_hda_spi_priv *h_priv = tas_hda->hda_priv;
+
+ snd_ctl_remove(codec->card, tas_hda->dsp_prog_ctl);
+
+ snd_ctl_remove(codec->card, tas_hda->dsp_conf_ctl);
+
+ for (int i = ARRAY_SIZE(h_priv->snd_ctls) - 1; i >= 0; i--)
+ snd_ctl_remove(codec->card, h_priv->snd_ctls[i]);
+
+ snd_ctl_remove(codec->card, tas_hda->prof_ctl);
+}
+
+static int tas2781_hda_spi_prf_ctl(struct tas2781_hda *h)
+{
+ struct tasdevice_priv *p = h->priv;
+ struct hda_codec *c = p->codec;
+ char name[64];
+ int rc;
+
+ snprintf(name, sizeof(name), "Speaker-%d Profile Id", p->index);
+ tas2781_prof_ctl.name = name;
+ h->prof_ctl = snd_ctl_new1(&tas2781_prof_ctl, p);
+ rc = snd_ctl_add(c->card, h->prof_ctl);
+ if (rc)
+ dev_err(p->dev, "Failed to add KControl: %s, rc = %d\n",
+ tas2781_prof_ctl.name, rc);
+ return rc;
+}
+
+static int tas2781_hda_spi_snd_ctls(struct tas2781_hda *h)
+{
+ struct tas2781_hda_spi_priv *h_priv = h->hda_priv;
+ struct tasdevice_priv *p = h->priv;
+ struct hda_codec *c = p->codec;
+ char name[64];
+ int i = 0;
+ int rc;
+
+ snprintf(name, sizeof(name), "Speaker-%d Analog Volume", p->index);
+ tas2781_snd_ctls[i].name = name;
+ h_priv->snd_ctls[i] = snd_ctl_new1(&tas2781_snd_ctls[i], p);
+ rc = snd_ctl_add(c->card, h_priv->snd_ctls[i]);
+ if (rc) {
+ dev_err(p->dev, "Failed to add KControl: %s, rc = %d\n",
+ tas2781_snd_ctls[i].name, rc);
+ return rc;
+ }
+ i++;
+ snprintf(name, sizeof(name), "Speaker-%d Digital Volume", p->index);
+ tas2781_snd_ctls[i].name = name;
+ h_priv->snd_ctls[i] = snd_ctl_new1(&tas2781_snd_ctls[i], p);
+ rc = snd_ctl_add(c->card, h_priv->snd_ctls[i]);
+ if (rc) {
+ dev_err(p->dev, "Failed to add KControl: %s, rc = %d\n",
+ tas2781_snd_ctls[i].name, rc);
+ return rc;
+ }
+ i++;
+ snprintf(name, sizeof(name), "Froce Speaker-%d FW Load", p->index);
+ tas2781_snd_ctls[i].name = name;
+ h_priv->snd_ctls[i] = snd_ctl_new1(&tas2781_snd_ctls[i], p);
+ rc = snd_ctl_add(c->card, h_priv->snd_ctls[i]);
+ if (rc) {
+ dev_err(p->dev, "Failed to add KControl: %s, rc = %d\n",
+ tas2781_snd_ctls[i].name, rc);
+ }
+ return rc;
+}
+
+static int tas2781_hda_spi_dsp_ctls(struct tas2781_hda *h)
+{
+ struct tasdevice_priv *p = h->priv;
+ struct hda_codec *c = p->codec;
+ char name[64];
+ int i = 0;
+ int rc;
+
+ snprintf(name, sizeof(name), "Speaker-%d Program Id", p->index);
+ tas2781_dsp_ctls[i].name = name;
+ h->dsp_prog_ctl = snd_ctl_new1(&tas2781_dsp_ctls[i], p);
+ rc = snd_ctl_add(c->card, h->dsp_prog_ctl);
+ if (rc) {
+ dev_err(p->dev, "Failed to add KControl: %s, rc = %d\n",
+ tas2781_dsp_ctls[i].name, rc);
+ return rc;
+ }
+ i++;
+ snprintf(name, sizeof(name), "Speaker-%d Config Id", p->index);
+ tas2781_dsp_ctls[i].name = name;
+ h->dsp_conf_ctl = snd_ctl_new1(&tas2781_dsp_ctls[i], p);
+ rc = snd_ctl_add(c->card, h->dsp_conf_ctl);
+ if (rc) {
+ dev_err(p->dev, "Failed to add KControl: %s, rc = %d\n",
+ tas2781_dsp_ctls[i].name, rc);
+ }
+
+ return rc;
+}
+
+static void tasdev_fw_ready(const struct firmware *fmw, void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+ struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev);
+ struct hda_codec *codec = tas_priv->codec;
+ int ret, val;
+
+ pm_runtime_get_sync(tas_priv->dev);
+ guard(mutex)(&tas_priv->codec_lock);
+
+ ret = tasdevice_rca_parser(tas_priv, fmw);
+ if (ret)
+ goto out;
+
+ /* Add control one time only. */
+ ret = tas2781_hda_spi_prf_ctl(tas_hda);
+ if (ret)
+ goto out;
+
+ ret = tas2781_hda_spi_snd_ctls(tas_hda);
+ if (ret)
+ goto out;
+
+ tasdevice_dsp_remove(tas_priv);
+
+ tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+ scnprintf(tas_priv->coef_binaryname, 64, "TAS2XXX%04X-%01d.bin",
+ lower_16_bits(codec->core.subsystem_id), tas_priv->index);
+ ret = tasdevice_dsp_parser(tas_priv);
+ if (ret) {
+ dev_err(tas_priv->dev, "dspfw load %s error\n",
+ tas_priv->coef_binaryname);
+ tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL;
+ goto out;
+ }
+
+ ret = tas2781_hda_spi_dsp_ctls(tas_hda);
+ if (ret)
+ goto out;
+ /* Perform AMP reset before firmware download. */
+ tas2781_spi_reset(tas_priv);
+ tas_priv->rcabin.profile_cfg_id = 0;
+
+ tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+ ret = tasdevice_spi_dev_read(tas_priv, tas_priv->index,
+ TAS2781_REG_CLK_CONFIG, &val);
+ if (ret < 0)
+ goto out;
+
+ if (val == TAS2781_REG_CLK_CONFIG_RESET) {
+ ret = tasdevice_prmg_load(tas_priv, 0);
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "FW download failed = %d\n",
+ ret);
+ goto out;
+ }
+ tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+ }
+ if (tas_priv->fmw->nr_programs > 0)
+ tas_priv->tasdevice[tas_priv->index].cur_prog = 0;
+ if (tas_priv->fmw->nr_configurations > 0)
+ tas_priv->tasdevice[tas_priv->index].cur_conf = 0;
+
+ /*
+ * If calibrated data occurs error, dsp will still works with default
+ * calibrated data inside algo.
+ */
+ tas2781_save_calibration(tas_hda);
+out:
+ release_firmware(fmw);
+ pm_runtime_put_autosuspend(tas_hda->priv->dev);
+}
+
+static int tas2781_hda_bind(struct device *dev, struct device *master,
+ void *master_data)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct hda_component_parent *parent = master_data;
+ struct hda_component *comp;
+ struct hda_codec *codec;
+ int ret;
+
+ comp = hda_component_from_index(parent, tas_hda->priv->index);
+ if (!comp)
+ return -EINVAL;
+
+ if (comp->dev)
+ return -EBUSY;
+
+ codec = parent->codec;
+
+ pm_runtime_get_sync(dev);
+
+ comp->dev = dev;
+
+ strscpy(comp->name, dev_name(dev), sizeof(comp->name));
+
+ ret = tascodec_spi_init(tas_hda->priv, codec, THIS_MODULE,
+ tasdev_fw_ready);
+ if (!ret)
+ comp->playback_hook = tas2781_hda_playback_hook;
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static void tas2781_hda_unbind(struct device *dev, struct device *master,
+ void *master_data)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct hda_component_parent *parent = master_data;
+ struct tasdevice_priv *tas_priv = tas_hda->priv;
+ struct hda_component *comp;
+
+ comp = hda_component_from_index(parent, tas_priv->index);
+ if (comp && (comp->dev == dev)) {
+ comp->dev = NULL;
+ memset(comp->name, 0, sizeof(comp->name));
+ comp->playback_hook = NULL;
+ }
+
+ tas2781_hda_remove_controls(tas_hda);
+
+ tasdevice_config_info_remove(tas_priv);
+ tasdevice_dsp_remove(tas_priv);
+
+ tas_hda->priv->fw_state = TASDEVICE_DSP_FW_PENDING;
+}
+
+static const struct component_ops tas2781_hda_comp_ops = {
+ .bind = tas2781_hda_bind,
+ .unbind = tas2781_hda_unbind,
+};
+
+static int tas2781_hda_spi_probe(struct spi_device *spi)
+{
+ struct tas2781_hda_spi_priv *hda_priv;
+ struct tasdevice_priv *tas_priv;
+ struct tas2781_hda *tas_hda;
+ const char *device_name;
+ int ret = 0;
+
+ tas_hda = devm_kzalloc(&spi->dev, sizeof(*tas_hda), GFP_KERNEL);
+ if (!tas_hda)
+ return -ENOMEM;
+
+ hda_priv = devm_kzalloc(&spi->dev, sizeof(*hda_priv), GFP_KERNEL);
+ if (!hda_priv)
+ return -ENOMEM;
+
+ tas_hda->hda_priv = hda_priv;
+ spi->max_speed_hz = TAS2781_SPI_MAX_FREQ;
+
+ tas_priv = devm_kzalloc(&spi->dev, sizeof(*tas_priv), GFP_KERNEL);
+ if (!tas_priv)
+ return -ENOMEM;
+ tas_priv->dev = &spi->dev;
+ tas_hda->priv = tas_priv;
+ tas_priv->regmap = devm_regmap_init_spi(spi, &tasdevice_regmap);
+ if (IS_ERR(tas_priv->regmap)) {
+ ret = PTR_ERR(tas_priv->regmap);
+ dev_err(tas_priv->dev, "Failed to allocate regmap: %d\n",
+ ret);
+ return ret;
+ }
+ if (strstr(dev_name(&spi->dev), "TXNW2781")) {
+ device_name = "TXNW2781";
+ } else {
+ dev_err(tas_priv->dev, "Unmatched spi dev %s\n",
+ dev_name(&spi->dev));
+ return -ENODEV;
+ }
+
+ tas_priv->irq = spi->irq;
+ dev_set_drvdata(&spi->dev, tas_hda);
+ ret = tas2781_read_acpi(tas_hda, device_name,
+ spi_get_chipselect(spi, 0));
+ if (ret)
+ return dev_err_probe(tas_priv->dev, ret,
+ "Platform not supported\n");
+
+ tasdevice_spi_init(tas_priv);
+
+ pm_runtime_set_autosuspend_delay(tas_priv->dev, 3000);
+ pm_runtime_use_autosuspend(tas_priv->dev);
+ pm_runtime_set_active(tas_priv->dev);
+ pm_runtime_get_noresume(tas_priv->dev);
+ pm_runtime_enable(tas_priv->dev);
+
+ pm_runtime_put_autosuspend(tas_priv->dev);
+
+ ret = component_add(tas_priv->dev, &tas2781_hda_comp_ops);
+ if (ret) {
+ dev_err(tas_priv->dev, "Register component fail: %d\n", ret);
+ pm_runtime_disable(tas_priv->dev);
+ tas2781_hda_remove(&spi->dev, &tas2781_hda_comp_ops);
+ }
+
+ return ret;
+}
+
+static void tas2781_hda_spi_remove(struct spi_device *spi)
+{
+ tas2781_hda_remove(&spi->dev, &tas2781_hda_comp_ops);
+}
+
+static int tas2781_runtime_suspend(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct tasdevice_priv *tas_priv = tas_hda->priv;
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK
+ && tas_priv->playback_started)
+ tasdevice_tuning_switch(tas_priv, 1);
+
+ tas_priv->tasdevice[tas_priv->index].cur_book = -1;
+ tas_priv->tasdevice[tas_priv->index].cur_conf = -1;
+
+ return 0;
+}
+
+static int tas2781_runtime_resume(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct tasdevice_priv *tas_priv = tas_hda->priv;
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK
+ && tas_priv->playback_started)
+ tasdevice_tuning_switch(tas_priv, 0);
+
+ return 0;
+}
+
+static int tas2781_system_suspend(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct tasdevice_priv *tas_priv = tas_hda->priv;
+ int ret;
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ return ret;
+
+ /* Shutdown chip before system suspend */
+ if (tas_priv->fw_state == TASDEVICE_DSP_FW_ALL_OK
+ && tas_priv->playback_started)
+ tasdevice_tuning_switch(tas_priv, 1);
+
+ return 0;
+}
+
+static int tas2781_system_resume(struct device *dev)
+{
+ struct tas2781_hda *tas_hda = dev_get_drvdata(dev);
+ struct tasdevice_priv *tas_priv = tas_hda->priv;
+ int ret, val;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&tas_priv->codec_lock);
+ ret = tas_priv->dev_read(tas_priv, tas_priv->index,
+ TAS2781_REG_CLK_CONFIG, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val == TAS2781_REG_CLK_CONFIG_RESET) {
+ tas_priv->tasdevice[tas_priv->index].cur_book = -1;
+ tas_priv->tasdevice[tas_priv->index].cur_conf = -1;
+ tas_priv->tasdevice[tas_priv->index].cur_prog = -1;
+
+ ret = tasdevice_prmg_load(tas_priv, 0);
+ if (ret < 0) {
+ dev_err(tas_priv->dev,
+ "FW download failed = %d\n", ret);
+ return ret;
+ }
+ tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+
+ if (tas_priv->playback_started)
+ tasdevice_tuning_switch(tas_priv, 0);
+ }
+
+ return ret;
+}
+
+static const struct dev_pm_ops tas2781_hda_pm_ops = {
+ RUNTIME_PM_OPS(tas2781_runtime_suspend, tas2781_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(tas2781_system_suspend, tas2781_system_resume)
+};
+
+static const struct spi_device_id tas2781_hda_spi_id[] = {
+ { "tas2781-hda", },
+ {}
+};
+
+static const struct acpi_device_id tas2781_acpi_hda_match[] = {
+ {"TXNW2781", },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, tas2781_acpi_hda_match);
+
+static struct spi_driver tas2781_hda_spi_driver = {
+ .driver = {
+ .name = "tas2781-hda",
+ .acpi_match_table = tas2781_acpi_hda_match,
+ .pm = &tas2781_hda_pm_ops,
+ },
+ .id_table = tas2781_hda_spi_id,
+ .probe = tas2781_hda_spi_probe,
+ .remove = tas2781_hda_spi_remove,
+};
+module_spi_driver(tas2781_hda_spi_driver);
+
+MODULE_DESCRIPTION("TAS2781 HDA SPI Driver");
+MODULE_AUTHOR("Baojun, Xu, <baojun.xug@ti.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_TAS2781_FMWLIB");
+MODULE_IMPORT_NS("SND_HDA_SCODEC_TAS2781");
diff --git a/sound/hda/codecs/sigmatel.c b/sound/hda/codecs/sigmatel.c
new file mode 100644
index 000000000000..ecbee408d771
--- /dev/null
+++ b/sound/hda/codecs/sigmatel.c
@@ -0,0 +1,5169 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * HD audio codec driver for SigmaTel STAC92xx
+ *
+ * Copyright (c) 2005 Embedded Alley Solutions, Inc.
+ * Matt Porter <mporter@embeddedalley.com>
+ *
+ * Based on cmedia.c and realtek.c
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_beep.h"
+#include "hda_jack.h"
+#include "generic.h"
+
+enum {
+ STAC_REF,
+ STAC_9200_OQO,
+ STAC_9200_DELL_D21,
+ STAC_9200_DELL_D22,
+ STAC_9200_DELL_D23,
+ STAC_9200_DELL_M21,
+ STAC_9200_DELL_M22,
+ STAC_9200_DELL_M23,
+ STAC_9200_DELL_M24,
+ STAC_9200_DELL_M25,
+ STAC_9200_DELL_M26,
+ STAC_9200_DELL_M27,
+ STAC_9200_M4,
+ STAC_9200_M4_2,
+ STAC_9200_PANASONIC,
+ STAC_9200_EAPD_INIT,
+ STAC_9200_MODELS
+};
+
+enum {
+ STAC_9205_REF,
+ STAC_9205_DELL_M42,
+ STAC_9205_DELL_M43,
+ STAC_9205_DELL_M44,
+ STAC_9205_EAPD,
+ STAC_9205_MODELS
+};
+
+enum {
+ STAC_92HD73XX_NO_JD, /* no jack-detection */
+ STAC_92HD73XX_REF,
+ STAC_92HD73XX_INTEL,
+ STAC_DELL_M6_AMIC,
+ STAC_DELL_M6_DMIC,
+ STAC_DELL_M6_BOTH,
+ STAC_DELL_EQ,
+ STAC_ALIENWARE_M17X,
+ STAC_ELO_VUPOINT_15MX,
+ STAC_92HD89XX_HP_FRONT_JACK,
+ STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK,
+ STAC_92HD73XX_ASUS_MOBO,
+ STAC_92HD73XX_MODELS
+};
+
+enum {
+ STAC_92HD83XXX_REF,
+ STAC_92HD83XXX_PWR_REF,
+ STAC_DELL_S14,
+ STAC_DELL_VOSTRO_3500,
+ STAC_92HD83XXX_HP_cNB11_INTQUAD,
+ STAC_HP_DV7_4000,
+ STAC_HP_ZEPHYR,
+ STAC_92HD83XXX_HP_LED,
+ STAC_92HD83XXX_HP_INV_LED,
+ STAC_92HD83XXX_HP_MIC_LED,
+ STAC_HP_LED_GPIO10,
+ STAC_92HD83XXX_HEADSET_JACK,
+ STAC_92HD83XXX_HP,
+ STAC_HP_ENVY_BASS,
+ STAC_HP_BNB13_EQ,
+ STAC_HP_ENVY_TS_BASS,
+ STAC_HP_ENVY_TS_DAC_BIND,
+ STAC_92HD83XXX_GPIO10_EAPD,
+ STAC_92HD83XXX_MODELS
+};
+
+enum {
+ STAC_92HD71BXX_REF,
+ STAC_DELL_M4_1,
+ STAC_DELL_M4_2,
+ STAC_DELL_M4_3,
+ STAC_HP_M4,
+ STAC_HP_DV4,
+ STAC_HP_DV5,
+ STAC_HP_HDX,
+ STAC_92HD71BXX_HP,
+ STAC_92HD71BXX_NO_DMIC,
+ STAC_92HD71BXX_NO_SMUX,
+ STAC_92HD71BXX_MODELS
+};
+
+enum {
+ STAC_92HD95_HP_LED,
+ STAC_92HD95_HP_BASS,
+ STAC_92HD95_MODELS
+};
+
+enum {
+ STAC_925x_REF,
+ STAC_M1,
+ STAC_M1_2,
+ STAC_M2,
+ STAC_M2_2,
+ STAC_M3,
+ STAC_M5,
+ STAC_M6,
+ STAC_925x_MODELS
+};
+
+enum {
+ STAC_D945_REF,
+ STAC_D945GTP3,
+ STAC_D945GTP5,
+ STAC_INTEL_MAC_V1,
+ STAC_INTEL_MAC_V2,
+ STAC_INTEL_MAC_V3,
+ STAC_INTEL_MAC_V4,
+ STAC_INTEL_MAC_V5,
+ STAC_INTEL_MAC_AUTO,
+ STAC_ECS_202,
+ STAC_922X_DELL_D81,
+ STAC_922X_DELL_D82,
+ STAC_922X_DELL_M81,
+ STAC_922X_DELL_M82,
+ STAC_922X_INTEL_MAC_GPIO,
+ STAC_922X_MODELS
+};
+
+enum {
+ STAC_D965_REF_NO_JD, /* no jack-detection */
+ STAC_D965_REF,
+ STAC_D965_3ST,
+ STAC_D965_5ST,
+ STAC_D965_5ST_NO_FP,
+ STAC_D965_VERBS,
+ STAC_DELL_3ST,
+ STAC_DELL_BIOS,
+ STAC_NEMO_DEFAULT,
+ STAC_DELL_BIOS_AMIC,
+ STAC_DELL_BIOS_SPDIF,
+ STAC_927X_DELL_DMIC,
+ STAC_927X_VOLKNOB,
+ STAC_927X_MODELS
+};
+
+enum {
+ STAC_9872_VAIO,
+ STAC_9872_MODELS
+};
+
+struct sigmatel_spec {
+ struct hda_gen_spec gen;
+
+ unsigned int eapd_switch: 1;
+ unsigned int linear_tone_beep:1;
+ unsigned int headset_jack:1; /* 4-pin headset jack (hp + mono mic) */
+ unsigned int volknob_init:1; /* special volume-knob initialization */
+ unsigned int powerdown_adcs:1;
+ unsigned int have_spdif_mux:1;
+
+ /* gpio lines */
+ unsigned int eapd_mask;
+ unsigned int gpio_mask;
+ unsigned int gpio_dir;
+ unsigned int gpio_data;
+ unsigned int gpio_mute;
+ unsigned int gpio_led;
+ unsigned int gpio_led_polarity;
+ unsigned int vref_mute_led_nid; /* pin NID for mute-LED vref control */
+ unsigned int vref_led;
+ int default_polarity;
+
+ unsigned int mic_mute_led_gpio; /* capture mute LED GPIO */
+ unsigned int mic_enabled; /* current mic mute state (bitmask) */
+
+ /* stream */
+ unsigned int stream_delay;
+
+ /* analog loopback */
+ const struct snd_kcontrol_new *aloopback_ctl;
+ unsigned int aloopback;
+ unsigned char aloopback_mask;
+ unsigned char aloopback_shift;
+
+ /* power management */
+ unsigned int power_map_bits;
+ unsigned int num_pwrs;
+ const hda_nid_t *pwr_nids;
+ unsigned int active_adcs;
+
+ /* beep widgets */
+ hda_nid_t anabeep_nid;
+ bool beep_power_on;
+
+ /* SPDIF-out mux */
+ const char * const *spdif_labels;
+ struct hda_input_mux spdif_mux;
+ unsigned int cur_smux[2];
+};
+
+#define AC_VERB_IDT_SET_POWER_MAP 0x7ec
+#define AC_VERB_IDT_GET_POWER_MAP 0xfec
+
+static const hda_nid_t stac92hd73xx_pwr_nids[8] = {
+ 0x0a, 0x0b, 0x0c, 0xd, 0x0e,
+ 0x0f, 0x10, 0x11
+};
+
+static const hda_nid_t stac92hd83xxx_pwr_nids[7] = {
+ 0x0a, 0x0b, 0x0c, 0xd, 0x0e,
+ 0x0f, 0x10
+};
+
+static const hda_nid_t stac92hd71bxx_pwr_nids[3] = {
+ 0x0a, 0x0d, 0x0f
+};
+
+
+/*
+ * PCM hooks
+ */
+static void stac_playback_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ if (action == HDA_GEN_PCM_ACT_OPEN && spec->stream_delay)
+ msleep(spec->stream_delay);
+}
+
+static void stac_capture_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i, idx = 0;
+
+ if (!spec->powerdown_adcs)
+ return;
+
+ for (i = 0; i < spec->gen.num_all_adcs; i++) {
+ if (spec->gen.all_adcs[i] == hinfo->nid) {
+ idx = i;
+ break;
+ }
+ }
+
+ switch (action) {
+ case HDA_GEN_PCM_ACT_OPEN:
+ msleep(40);
+ snd_hda_codec_write(codec, hinfo->nid, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ spec->active_adcs |= (1 << idx);
+ break;
+ case HDA_GEN_PCM_ACT_CLOSE:
+ snd_hda_codec_write(codec, hinfo->nid, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ spec->active_adcs &= ~(1 << idx);
+ break;
+ }
+}
+
+/*
+ * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a
+ * funky external mute control using GPIO pins.
+ */
+
+static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
+ unsigned int dir_mask, unsigned int data)
+{
+ unsigned int gpiostate, gpiomask, gpiodir;
+ hda_nid_t fg = codec->core.afg;
+
+ codec_dbg(codec, "%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data);
+
+ gpiostate = snd_hda_codec_read(codec, fg, 0,
+ AC_VERB_GET_GPIO_DATA, 0);
+ gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask);
+
+ gpiomask = snd_hda_codec_read(codec, fg, 0,
+ AC_VERB_GET_GPIO_MASK, 0);
+ gpiomask |= mask;
+
+ gpiodir = snd_hda_codec_read(codec, fg, 0,
+ AC_VERB_GET_GPIO_DIRECTION, 0);
+ gpiodir |= dir_mask;
+
+ /* Configure GPIOx as CMOS */
+ snd_hda_codec_write(codec, fg, 0, 0x7e7, 0);
+
+ snd_hda_codec_write(codec, fg, 0,
+ AC_VERB_SET_GPIO_MASK, gpiomask);
+ snd_hda_codec_read(codec, fg, 0,
+ AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */
+
+ msleep(1);
+
+ snd_hda_codec_read(codec, fg, 0,
+ AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
+}
+
+/* hook for controlling mic-mute LED GPIO */
+static int stac_capture_led_update(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (brightness)
+ spec->gpio_data |= spec->mic_mute_led_gpio;
+ else
+ spec->gpio_data &= ~spec->mic_mute_led_gpio;
+ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
+ return 0;
+}
+
+static int stac_vrefout_set(struct hda_codec *codec,
+ hda_nid_t nid, unsigned int new_vref)
+{
+ int error, pinctl;
+
+ codec_dbg(codec, "%s, nid %x ctl %x\n", __func__, nid, new_vref);
+ pinctl = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+
+ if (pinctl < 0)
+ return pinctl;
+
+ pinctl &= 0xff;
+ pinctl &= ~AC_PINCTL_VREFEN;
+ pinctl |= (new_vref & AC_PINCTL_VREFEN);
+
+ error = snd_hda_set_pin_ctl_cache(codec, nid, pinctl);
+ if (error < 0)
+ return error;
+
+ return 1;
+}
+
+/* prevent codec AFG to D3 state when vref-out pin is used for mute LED */
+/* this hook is set in stac_setup_gpio() */
+static unsigned int stac_vref_led_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
+{
+ if (nid == codec->core.afg && power_state == AC_PWRST_D3)
+ return AC_PWRST_D1;
+ return snd_hda_gen_path_power_filter(codec, nid, power_state);
+}
+
+/* update mute-LED accoring to the master switch */
+static void stac_update_led_status(struct hda_codec *codec, bool muted)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (!spec->gpio_led)
+ return;
+
+ /* LED state is inverted on these systems */
+ if (spec->gpio_led_polarity)
+ muted = !muted;
+
+ if (!spec->vref_mute_led_nid) {
+ if (muted)
+ spec->gpio_data |= spec->gpio_led;
+ else
+ spec->gpio_data &= ~spec->gpio_led;
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data);
+ } else {
+ spec->vref_led = muted ? AC_PINCTL_VREF_50 : AC_PINCTL_VREF_GRD;
+ stac_vrefout_set(codec, spec->vref_mute_led_nid,
+ spec->vref_led);
+ }
+}
+
+/* vmaster hook to update mute LED */
+static int stac_vmaster_hook(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent);
+
+ stac_update_led_status(codec, brightness);
+ return 0;
+}
+
+/* automute hook to handle GPIO mute and EAPD updates */
+static void stac_update_outputs(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (spec->gpio_mute)
+ spec->gen.master_mute =
+ !(snd_hda_codec_read(codec, codec->core.afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute);
+
+ snd_hda_gen_update_outputs(codec);
+
+ if (spec->eapd_mask && spec->eapd_switch) {
+ unsigned int val = spec->gpio_data;
+ if (spec->gen.speaker_muted)
+ val &= ~spec->eapd_mask;
+ else
+ val |= spec->eapd_mask;
+ if (spec->gpio_data != val) {
+ spec->gpio_data = val;
+ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir,
+ val);
+ }
+ }
+}
+
+static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
+ bool enable, bool do_write)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int idx, val;
+
+ for (idx = 0; idx < spec->num_pwrs; idx++) {
+ if (spec->pwr_nids[idx] == nid)
+ break;
+ }
+ if (idx >= spec->num_pwrs)
+ return;
+
+ idx = 1 << idx;
+
+ val = spec->power_map_bits;
+ if (enable)
+ val &= ~idx;
+ else
+ val |= idx;
+
+ /* power down unused output ports */
+ if (val != spec->power_map_bits) {
+ spec->power_map_bits = val;
+ if (do_write)
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_IDT_SET_POWER_MAP, val);
+ }
+}
+
+/* update power bit per jack plug/unplug */
+static void jack_update_power(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i;
+
+ if (!spec->num_pwrs)
+ return;
+
+ if (jack && jack->nid) {
+ stac_toggle_power_map(codec, jack->nid,
+ snd_hda_jack_detect(codec, jack->nid),
+ true);
+ return;
+ }
+
+ /* update all jacks */
+ for (i = 0; i < spec->num_pwrs; i++) {
+ hda_nid_t nid = spec->pwr_nids[i];
+ if (!snd_hda_jack_tbl_get(codec, nid))
+ continue;
+ stac_toggle_power_map(codec, nid,
+ snd_hda_jack_detect(codec, nid),
+ false);
+ }
+
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_IDT_SET_POWER_MAP,
+ spec->power_map_bits);
+}
+
+static void stac_vref_event(struct hda_codec *codec,
+ struct hda_jack_callback *event)
+{
+ unsigned int data;
+
+ data = snd_hda_codec_read(codec, codec->core.afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0);
+ /* toggle VREF state based on GPIOx status */
+ snd_hda_codec_write(codec, codec->core.afg, 0, 0x7e0,
+ !!(data & (1 << event->private_data)));
+}
+
+/* initialize the power map and enable the power event to jacks that
+ * haven't been assigned to automute
+ */
+static void stac_init_power_map(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->num_pwrs; i++) {
+ hda_nid_t nid = spec->pwr_nids[i];
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ def_conf = get_defcfg_connect(def_conf);
+ if (def_conf == AC_JACK_PORT_COMPLEX &&
+ spec->vref_mute_led_nid != nid &&
+ is_jack_detectable(codec, nid)) {
+ snd_hda_jack_detect_enable_callback(codec, nid,
+ jack_update_power);
+ } else {
+ if (def_conf == AC_JACK_PORT_NONE)
+ stac_toggle_power_map(codec, nid, false, false);
+ else
+ stac_toggle_power_map(codec, nid, true, false);
+ }
+ }
+}
+
+/*
+ */
+
+static inline bool get_int_hint(struct hda_codec *codec, const char *key,
+ int *valp)
+{
+ return !snd_hda_get_int_hint(codec, key, valp);
+}
+
+/* override some hints from the hwdep entry */
+static void stac_store_hints(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int val;
+
+ if (get_int_hint(codec, "gpio_mask", &spec->gpio_mask)) {
+ spec->eapd_mask = spec->gpio_dir = spec->gpio_data =
+ spec->gpio_mask;
+ }
+ if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir))
+ spec->gpio_dir &= spec->gpio_mask;
+ if (get_int_hint(codec, "gpio_data", &spec->gpio_data))
+ spec->gpio_data &= spec->gpio_mask;
+ if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask))
+ spec->eapd_mask &= spec->gpio_mask;
+ if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute))
+ spec->gpio_mute &= spec->gpio_mask;
+ val = snd_hda_get_bool_hint(codec, "eapd_switch");
+ if (val >= 0)
+ spec->eapd_switch = val;
+}
+
+/*
+ * loopback controls
+ */
+
+#define stac_aloopback_info snd_ctl_boolean_mono_info
+
+static int stac_aloopback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ struct sigmatel_spec *spec = codec->spec;
+
+ ucontrol->value.integer.value[0] = !!(spec->aloopback &
+ (spec->aloopback_mask << idx));
+ return 0;
+}
+
+static int stac_aloopback_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ unsigned int dac_mode;
+ unsigned int val, idx_val;
+
+ idx_val = spec->aloopback_mask << idx;
+ if (ucontrol->value.integer.value[0])
+ val = spec->aloopback | idx_val;
+ else
+ val = spec->aloopback & ~idx_val;
+ if (spec->aloopback == val)
+ return 0;
+
+ spec->aloopback = val;
+
+ /* Only return the bits defined by the shift value of the
+ * first two bytes of the mask
+ */
+ dac_mode = snd_hda_codec_read(codec, codec->core.afg, 0,
+ kcontrol->private_value & 0xFFFF, 0x0);
+ dac_mode >>= spec->aloopback_shift;
+
+ if (spec->aloopback & idx_val) {
+ snd_hda_power_up(codec);
+ dac_mode |= idx_val;
+ } else {
+ snd_hda_power_down(codec);
+ dac_mode &= ~idx_val;
+ }
+
+ snd_hda_codec_write_cache(codec, codec->core.afg, 0,
+ kcontrol->private_value >> 16, dac_mode);
+
+ return 1;
+}
+
+#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = "Analog Loopback", \
+ .count = cnt, \
+ .info = stac_aloopback_info, \
+ .get = stac_aloopback_get, \
+ .put = stac_aloopback_put, \
+ .private_value = verb_read | (verb_write << 16), \
+ }
+
+/*
+ * Mute LED handling on HP laptops
+ */
+
+/* check whether it's a HP laptop with a docking port */
+static bool hp_bnb2011_with_dock(struct hda_codec *codec)
+{
+ if (codec->core.vendor_id != 0x111d7605 &&
+ codec->core.vendor_id != 0x111d76d1)
+ return false;
+
+ switch (codec->core.subsystem_id) {
+ case 0x103c1618:
+ case 0x103c1619:
+ case 0x103c161a:
+ case 0x103c161b:
+ case 0x103c161c:
+ case 0x103c161d:
+ case 0x103c161e:
+ case 0x103c161f:
+
+ case 0x103c162a:
+ case 0x103c162b:
+
+ case 0x103c1630:
+ case 0x103c1631:
+
+ case 0x103c1633:
+ case 0x103c1634:
+ case 0x103c1635:
+
+ case 0x103c3587:
+ case 0x103c3588:
+ case 0x103c3589:
+ case 0x103c358a:
+
+ case 0x103c3667:
+ case 0x103c3668:
+ case 0x103c3669:
+
+ return true;
+ }
+ return false;
+}
+
+static bool hp_blike_system(u32 subsystem_id)
+{
+ switch (subsystem_id) {
+ case 0x103c1473: /* HP ProBook 6550b */
+ case 0x103c1520:
+ case 0x103c1521:
+ case 0x103c1523:
+ case 0x103c1524:
+ case 0x103c1525:
+ case 0x103c1722:
+ case 0x103c1723:
+ case 0x103c1724:
+ case 0x103c1725:
+ case 0x103c1726:
+ case 0x103c1727:
+ case 0x103c1728:
+ case 0x103c1729:
+ case 0x103c172a:
+ case 0x103c172b:
+ case 0x103c307e:
+ case 0x103c307f:
+ case 0x103c3080:
+ case 0x103c3081:
+ case 0x103c7007:
+ case 0x103c7008:
+ return true;
+ }
+ return false;
+}
+
+static void set_hp_led_gpio(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int gpio;
+
+ if (spec->gpio_led)
+ return;
+
+ gpio = snd_hda_param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP);
+ gpio &= AC_GPIO_IO_COUNT;
+ if (gpio > 3)
+ spec->gpio_led = 0x08; /* GPIO 3 */
+ else
+ spec->gpio_led = 0x01; /* GPIO 0 */
+}
+
+/*
+ * This method searches for the mute LED GPIO configuration
+ * provided as OEM string in SMBIOS. The format of that string
+ * is HP_Mute_LED_P_G or HP_Mute_LED_P
+ * where P can be 0 or 1 and defines mute LED GPIO control state (low/high)
+ * that corresponds to the NOT muted state of the master volume
+ * and G is the index of the GPIO to use as the mute LED control (0..9)
+ * If _G portion is missing it is assigned based on the codec ID
+ *
+ * So, HP B-series like systems may have HP_Mute_LED_0 (current models)
+ * or HP_Mute_LED_0_3 (future models) OEM SMBIOS strings
+ *
+ *
+ * The dv-series laptops don't seem to have the HP_Mute_LED* strings in
+ * SMBIOS - at least the ones I have seen do not have them - which include
+ * my own system (HP Pavilion dv6-1110ax) and my cousin's
+ * HP Pavilion dv9500t CTO.
+ * Need more information on whether it is true across the entire series.
+ * -- kunal
+ */
+static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ const struct dmi_device *dev = NULL;
+
+ if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
+ get_int_hint(codec, "gpio_led_polarity",
+ &spec->gpio_led_polarity);
+ return 1;
+ }
+
+ while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
+ if (sscanf(dev->name, "HP_Mute_LED_%u_%x",
+ &spec->gpio_led_polarity,
+ &spec->gpio_led) == 2) {
+ unsigned int max_gpio;
+ max_gpio = snd_hda_param_read(codec, codec->core.afg,
+ AC_PAR_GPIO_CAP);
+ max_gpio &= AC_GPIO_IO_COUNT;
+ if (spec->gpio_led < max_gpio)
+ spec->gpio_led = 1 << spec->gpio_led;
+ else
+ spec->vref_mute_led_nid = spec->gpio_led;
+ return 1;
+ }
+ if (sscanf(dev->name, "HP_Mute_LED_%u",
+ &spec->gpio_led_polarity) == 1) {
+ set_hp_led_gpio(codec);
+ return 1;
+ }
+ /* BIOS bug: unfilled OEM string */
+ if (strstr(dev->name, "HP_Mute_LED_P_G")) {
+ set_hp_led_gpio(codec);
+ if (default_polarity >= 0)
+ spec->gpio_led_polarity = default_polarity;
+ else
+ spec->gpio_led_polarity = 1;
+ return 1;
+ }
+ }
+
+ /*
+ * Fallback case - if we don't find the DMI strings,
+ * we statically set the GPIO - if not a B-series system
+ * and default polarity is provided
+ */
+ if (!hp_blike_system(codec->core.subsystem_id) &&
+ (default_polarity == 0 || default_polarity == 1)) {
+ set_hp_led_gpio(codec);
+ spec->gpio_led_polarity = default_polarity;
+ return 1;
+ }
+ return 0;
+}
+
+/* check whether a built-in speaker is included in parsed pins */
+static bool has_builtin_speaker(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ const hda_nid_t *nid_pin;
+ int nids, i;
+
+ if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ nid_pin = spec->gen.autocfg.line_out_pins;
+ nids = spec->gen.autocfg.line_outs;
+ } else {
+ nid_pin = spec->gen.autocfg.speaker_pins;
+ nids = spec->gen.autocfg.speaker_outs;
+ }
+
+ for (i = 0; i < nids; i++) {
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid_pin[i]);
+ if (snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT)
+ return true;
+ }
+ return false;
+}
+
+/*
+ * PC beep controls
+ */
+
+/* create PC beep volume controls */
+static int stac_auto_create_beep_ctls(struct hda_codec *codec,
+ hda_nid_t nid)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT);
+ struct snd_kcontrol_new *knew;
+ static const struct snd_kcontrol_new abeep_mute_ctl =
+ HDA_CODEC_MUTE(NULL, 0, 0, 0);
+ static const struct snd_kcontrol_new dbeep_mute_ctl =
+ HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0);
+ static const struct snd_kcontrol_new beep_vol_ctl =
+ HDA_CODEC_VOLUME(NULL, 0, 0, 0);
+
+ /* check for mute support for the amp */
+ if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) {
+ const struct snd_kcontrol_new *temp;
+ if (spec->anabeep_nid == nid)
+ temp = &abeep_mute_ctl;
+ else
+ temp = &dbeep_mute_ctl;
+ knew = snd_hda_gen_add_kctl(&spec->gen,
+ "Beep Playback Switch", temp);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value =
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT);
+ }
+
+ /* check to see if there is volume support for the amp */
+ if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
+ knew = snd_hda_gen_add_kctl(&spec->gen,
+ "Beep Playback Volume",
+ &beep_vol_ctl);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value =
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+#define stac_dig_beep_switch_info snd_ctl_boolean_mono_info
+
+static int stac_dig_beep_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = codec->beep->enabled;
+ return 0;
+}
+
+static int stac_dig_beep_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]);
+}
+
+static const struct snd_kcontrol_new stac_dig_beep_ctrl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Beep Playback Switch",
+ .info = stac_dig_beep_switch_info,
+ .get = stac_dig_beep_switch_get,
+ .put = stac_dig_beep_switch_put,
+};
+
+static int stac_beep_switch_ctl(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &stac_dig_beep_ctrl))
+ return -ENOMEM;
+ return 0;
+}
+#endif
+
+/*
+ * SPDIF-out mux controls
+ */
+
+static int stac_smux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ return snd_hda_input_mux_info(&spec->spdif_mux, uinfo);
+}
+
+static int stac_smux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ ucontrol->value.enumerated.item[0] = spec->cur_smux[smux_idx];
+ return 0;
+}
+
+static int stac_smux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ return snd_hda_input_mux_put(codec, &spec->spdif_mux, ucontrol,
+ spec->gen.autocfg.dig_out_pins[smux_idx],
+ &spec->cur_smux[smux_idx]);
+}
+
+static const struct snd_kcontrol_new stac_smux_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Playback Source",
+ /* count set later */
+ .info = stac_smux_enum_info,
+ .get = stac_smux_enum_get,
+ .put = stac_smux_enum_put,
+};
+
+static const char * const stac_spdif_labels[] = {
+ "Digital Playback", "Analog Mux 1", "Analog Mux 2", NULL
+};
+
+static int stac_create_spdif_mux_ctls(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ const char * const *labels = spec->spdif_labels;
+ struct snd_kcontrol_new *kctl;
+ int i, num_cons;
+
+ if (cfg->dig_outs < 1)
+ return 0;
+
+ num_cons = snd_hda_get_num_conns(codec, cfg->dig_out_pins[0]);
+ if (num_cons <= 1)
+ return 0;
+
+ if (!labels)
+ labels = stac_spdif_labels;
+ for (i = 0; i < num_cons; i++) {
+ if (snd_BUG_ON(!labels[i]))
+ return -EINVAL;
+ snd_hda_add_imux_item(codec, &spec->spdif_mux, labels[i], i, NULL);
+ }
+
+ kctl = snd_hda_gen_add_kctl(&spec->gen, NULL, &stac_smux_mixer);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->count = cfg->dig_outs;
+
+ return 0;
+}
+
+static const struct hda_verb stac9200_eapd_init[] = {
+ /* set dac0mux for dac converter */
+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
+ {}
+};
+
+static const struct hda_verb dell_eq_core_init[] = {
+ /* set master volume to max value without distortion
+ * and direct control */
+ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec},
+ {}
+};
+
+static const struct hda_verb stac92hd73xx_core_init[] = {
+ /* set master volume and direct control */
+ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ {}
+};
+
+static const struct hda_verb stac92hd83xxx_core_init[] = {
+ /* power state controls amps */
+ { 0x01, AC_VERB_SET_EAPD, 1 << 2},
+ {}
+};
+
+static const struct hda_verb stac92hd83xxx_hp_zephyr_init[] = {
+ { 0x22, 0x785, 0x43 },
+ { 0x22, 0x782, 0xe0 },
+ { 0x22, 0x795, 0x00 },
+ {}
+};
+
+static const struct hda_verb stac92hd71bxx_core_init[] = {
+ /* set master volume and direct control */
+ { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ {}
+};
+
+static const hda_nid_t stac92hd71bxx_unmute_nids[] = {
+ /* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */
+ 0x0f, 0x0a, 0x0d, 0
+};
+
+static const struct hda_verb stac925x_core_init[] = {
+ /* set dac0mux for dac converter */
+ { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* mute the master volume */
+ { 0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ {}
+};
+
+static const struct hda_verb stac922x_core_init[] = {
+ /* set master volume and direct control */
+ { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ {}
+};
+
+static const struct hda_verb d965_core_init[] = {
+ /* unmute node 0x1b */
+ { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* select node 0x03 as DAC */
+ { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {}
+};
+
+static const struct hda_verb dell_3st_core_init[] = {
+ /* don't set delta bit */
+ {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f},
+ /* unmute node 0x1b */
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* select node 0x03 as DAC */
+ {0x0b, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {}
+};
+
+static const struct hda_verb stac927x_core_init[] = {
+ /* set master volume and direct control */
+ { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ /* enable analog pc beep path */
+ { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
+ {}
+};
+
+static const struct hda_verb stac927x_volknob_core_init[] = {
+ /* don't set delta bit */
+ {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f},
+ /* enable analog pc beep path */
+ {0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
+ {}
+};
+
+static const struct hda_verb stac9205_core_init[] = {
+ /* set master volume and direct control */
+ { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ /* enable analog pc beep path */
+ { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
+ {}
+};
+
+static const struct snd_kcontrol_new stac92hd73xx_6ch_loopback =
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3);
+
+static const struct snd_kcontrol_new stac92hd73xx_8ch_loopback =
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4);
+
+static const struct snd_kcontrol_new stac92hd73xx_10ch_loopback =
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5);
+
+static const struct snd_kcontrol_new stac92hd71bxx_loopback =
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2);
+
+static const struct snd_kcontrol_new stac9205_loopback =
+ STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1);
+
+static const struct snd_kcontrol_new stac927x_loopback =
+ STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1);
+
+static const struct hda_pintbl ref9200_pin_configs[] = {
+ { 0x08, 0x01c47010 },
+ { 0x09, 0x01447010 },
+ { 0x0d, 0x0221401f },
+ { 0x0e, 0x01114010 },
+ { 0x0f, 0x02a19020 },
+ { 0x10, 0x01a19021 },
+ { 0x11, 0x90100140 },
+ { 0x12, 0x01813122 },
+ {}
+};
+
+static const struct hda_pintbl gateway9200_m4_pin_configs[] = {
+ { 0x08, 0x400000fe },
+ { 0x09, 0x404500f4 },
+ { 0x0d, 0x400100f0 },
+ { 0x0e, 0x90110010 },
+ { 0x0f, 0x400100f1 },
+ { 0x10, 0x02a1902e },
+ { 0x11, 0x500000f2 },
+ { 0x12, 0x500000f3 },
+ {}
+};
+
+static const struct hda_pintbl gateway9200_m4_2_pin_configs[] = {
+ { 0x08, 0x400000fe },
+ { 0x09, 0x404500f4 },
+ { 0x0d, 0x400100f0 },
+ { 0x0e, 0x90110010 },
+ { 0x0f, 0x400100f1 },
+ { 0x10, 0x02a1902e },
+ { 0x11, 0x500000f2 },
+ { 0x12, 0x500000f3 },
+ {}
+};
+
+/*
+ STAC 9200 pin configs for
+ 102801A8
+ 102801DE
+ 102801E8
+*/
+static const struct hda_pintbl dell9200_d21_pin_configs[] = {
+ { 0x08, 0x400001f0 },
+ { 0x09, 0x400001f1 },
+ { 0x0d, 0x02214030 },
+ { 0x0e, 0x01014010 },
+ { 0x0f, 0x02a19020 },
+ { 0x10, 0x01a19021 },
+ { 0x11, 0x90100140 },
+ { 0x12, 0x01813122 },
+ {}
+};
+
+/*
+ STAC 9200 pin configs for
+ 102801C0
+ 102801C1
+*/
+static const struct hda_pintbl dell9200_d22_pin_configs[] = {
+ { 0x08, 0x400001f0 },
+ { 0x09, 0x400001f1 },
+ { 0x0d, 0x0221401f },
+ { 0x0e, 0x01014010 },
+ { 0x0f, 0x01813020 },
+ { 0x10, 0x02a19021 },
+ { 0x11, 0x90100140 },
+ { 0x12, 0x400001f2 },
+ {}
+};
+
+/*
+ STAC 9200 pin configs for
+ 102801C4 (Dell Dimension E310)
+ 102801C5
+ 102801C7
+ 102801D9
+ 102801DA
+ 102801E3
+*/
+static const struct hda_pintbl dell9200_d23_pin_configs[] = {
+ { 0x08, 0x400001f0 },
+ { 0x09, 0x400001f1 },
+ { 0x0d, 0x0221401f },
+ { 0x0e, 0x01014010 },
+ { 0x0f, 0x01813020 },
+ { 0x10, 0x01a19021 },
+ { 0x11, 0x90100140 },
+ { 0x12, 0x400001f2 },
+ {}
+};
+
+
+/*
+ STAC 9200-32 pin configs for
+ 102801B5 (Dell Inspiron 630m)
+ 102801D8 (Dell Inspiron 640m)
+*/
+static const struct hda_pintbl dell9200_m21_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x03441340 },
+ { 0x0d, 0x0321121f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x408003fb },
+ { 0x10, 0x03a11020 },
+ { 0x11, 0x401003fc },
+ { 0x12, 0x403003fd },
+ {}
+};
+
+/*
+ STAC 9200-32 pin configs for
+ 102801C2 (Dell Latitude D620)
+ 102801C8
+ 102801CC (Dell Latitude D820)
+ 102801D4
+ 102801D6
+*/
+static const struct hda_pintbl dell9200_m22_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x0144131f },
+ { 0x0d, 0x0321121f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x90a70321 },
+ { 0x10, 0x03a11020 },
+ { 0x11, 0x401003fb },
+ { 0x12, 0x40f000fc },
+ {}
+};
+
+/*
+ STAC 9200-32 pin configs for
+ 102801CE (Dell XPS M1710)
+ 102801CF (Dell Precision M90)
+*/
+static const struct hda_pintbl dell9200_m23_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x01441340 },
+ { 0x0d, 0x0421421f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x408003fb },
+ { 0x10, 0x04a1102e },
+ { 0x11, 0x90170311 },
+ { 0x12, 0x403003fc },
+ {}
+};
+
+/*
+ STAC 9200-32 pin configs for
+ 102801C9
+ 102801CA
+ 102801CB (Dell Latitude 120L)
+ 102801D3
+*/
+static const struct hda_pintbl dell9200_m24_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x404003fb },
+ { 0x0d, 0x0321121f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x408003fc },
+ { 0x10, 0x03a11020 },
+ { 0x11, 0x401003fd },
+ { 0x12, 0x403003fe },
+ {}
+};
+
+/*
+ STAC 9200-32 pin configs for
+ 102801BD (Dell Inspiron E1505n)
+ 102801EE
+ 102801EF
+*/
+static const struct hda_pintbl dell9200_m25_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x01441340 },
+ { 0x0d, 0x0421121f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x408003fb },
+ { 0x10, 0x04a11020 },
+ { 0x11, 0x401003fc },
+ { 0x12, 0x403003fd },
+ {}
+};
+
+/*
+ STAC 9200-32 pin configs for
+ 102801F5 (Dell Inspiron 1501)
+ 102801F6
+*/
+static const struct hda_pintbl dell9200_m26_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x404003fb },
+ { 0x0d, 0x0421121f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x408003fc },
+ { 0x10, 0x04a11020 },
+ { 0x11, 0x401003fd },
+ { 0x12, 0x403003fe },
+ {}
+};
+
+/*
+ STAC 9200-32
+ 102801CD (Dell Inspiron E1705/9400)
+*/
+static const struct hda_pintbl dell9200_m27_pin_configs[] = {
+ { 0x08, 0x40c003fa },
+ { 0x09, 0x01441340 },
+ { 0x0d, 0x0421121f },
+ { 0x0e, 0x90170310 },
+ { 0x0f, 0x90170310 },
+ { 0x10, 0x04a11020 },
+ { 0x11, 0x90170310 },
+ { 0x12, 0x40f003fc },
+ {}
+};
+
+static const struct hda_pintbl oqo9200_pin_configs[] = {
+ { 0x08, 0x40c000f0 },
+ { 0x09, 0x404000f1 },
+ { 0x0d, 0x0221121f },
+ { 0x0e, 0x02211210 },
+ { 0x0f, 0x90170111 },
+ { 0x10, 0x90a70120 },
+ { 0x11, 0x400000f2 },
+ { 0x12, 0x400000f3 },
+ {}
+};
+
+/*
+ * STAC 92HD700
+ * 18881000 Amigaone X1000
+ */
+static const struct hda_pintbl nemo_pin_configs[] = {
+ { 0x0a, 0x02214020 }, /* Front panel HP socket */
+ { 0x0b, 0x02a19080 }, /* Front Mic */
+ { 0x0c, 0x0181304e }, /* Line in */
+ { 0x0d, 0x01014010 }, /* Line out */
+ { 0x0e, 0x01a19040 }, /* Rear Mic */
+ { 0x0f, 0x01011012 }, /* Rear speakers */
+ { 0x10, 0x01016011 }, /* Center speaker */
+ { 0x11, 0x01012014 }, /* Side speakers (7.1) */
+ { 0x12, 0x103301f0 }, /* Motherboard CD line in connector */
+ { 0x13, 0x411111f0 }, /* Unused */
+ { 0x14, 0x411111f0 }, /* Unused */
+ { 0x21, 0x01442170 }, /* S/PDIF line out */
+ { 0x22, 0x411111f0 }, /* Unused */
+ { 0x23, 0x411111f0 }, /* Unused */
+ {}
+};
+
+static void stac9200_fixup_panasonic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gpio_mask = spec->gpio_dir = 0x09;
+ spec->gpio_data = 0x00;
+ /* CF-74 has no headphone detection, and the driver should *NOT*
+ * do detection and HP/speaker toggle because the hardware does it.
+ */
+ spec->gen.suppress_auto_mute = 1;
+ }
+}
+
+
+static const struct hda_fixup stac9200_fixups[] = {
+ [STAC_REF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = ref9200_pin_configs,
+ },
+ [STAC_9200_OQO] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = oqo9200_pin_configs,
+ .chained = true,
+ .chain_id = STAC_9200_EAPD_INIT,
+ },
+ [STAC_9200_DELL_D21] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_d21_pin_configs,
+ },
+ [STAC_9200_DELL_D22] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_d22_pin_configs,
+ },
+ [STAC_9200_DELL_D23] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_d23_pin_configs,
+ },
+ [STAC_9200_DELL_M21] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m21_pin_configs,
+ },
+ [STAC_9200_DELL_M22] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m22_pin_configs,
+ },
+ [STAC_9200_DELL_M23] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m23_pin_configs,
+ },
+ [STAC_9200_DELL_M24] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m24_pin_configs,
+ },
+ [STAC_9200_DELL_M25] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m25_pin_configs,
+ },
+ [STAC_9200_DELL_M26] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m26_pin_configs,
+ },
+ [STAC_9200_DELL_M27] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell9200_m27_pin_configs,
+ },
+ [STAC_9200_M4] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = gateway9200_m4_pin_configs,
+ .chained = true,
+ .chain_id = STAC_9200_EAPD_INIT,
+ },
+ [STAC_9200_M4_2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = gateway9200_m4_2_pin_configs,
+ .chained = true,
+ .chain_id = STAC_9200_EAPD_INIT,
+ },
+ [STAC_9200_PANASONIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac9200_fixup_panasonic,
+ },
+ [STAC_9200_EAPD_INIT] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
+ {}
+ },
+ },
+};
+
+static const struct hda_model_fixup stac9200_models[] = {
+ { .id = STAC_REF, .name = "ref" },
+ { .id = STAC_9200_OQO, .name = "oqo" },
+ { .id = STAC_9200_DELL_D21, .name = "dell-d21" },
+ { .id = STAC_9200_DELL_D22, .name = "dell-d22" },
+ { .id = STAC_9200_DELL_D23, .name = "dell-d23" },
+ { .id = STAC_9200_DELL_M21, .name = "dell-m21" },
+ { .id = STAC_9200_DELL_M22, .name = "dell-m22" },
+ { .id = STAC_9200_DELL_M23, .name = "dell-m23" },
+ { .id = STAC_9200_DELL_M24, .name = "dell-m24" },
+ { .id = STAC_9200_DELL_M25, .name = "dell-m25" },
+ { .id = STAC_9200_DELL_M26, .name = "dell-m26" },
+ { .id = STAC_9200_DELL_M27, .name = "dell-m27" },
+ { .id = STAC_9200_M4, .name = "gateway-m4" },
+ { .id = STAC_9200_M4_2, .name = "gateway-m4-2" },
+ { .id = STAC_9200_PANASONIC, .name = "panasonic" },
+ {}
+};
+
+static const struct hda_quirk stac9200_fixup_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+ "DFI LanParty", STAC_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+ "DFI LanParty", STAC_REF),
+ /* Dell laptops have BIOS problem */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a8,
+ "unknown Dell", STAC_9200_DELL_D21),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01b5,
+ "Dell Inspiron 630m", STAC_9200_DELL_M21),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01bd,
+ "Dell Inspiron E1505n", STAC_9200_DELL_M25),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c0,
+ "unknown Dell", STAC_9200_DELL_D22),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c1,
+ "unknown Dell", STAC_9200_DELL_D22),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c2,
+ "Dell Latitude D620", STAC_9200_DELL_M22),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c5,
+ "unknown Dell", STAC_9200_DELL_D23),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c7,
+ "unknown Dell", STAC_9200_DELL_D23),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c8,
+ "unknown Dell", STAC_9200_DELL_M22),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c9,
+ "unknown Dell", STAC_9200_DELL_M24),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ca,
+ "unknown Dell", STAC_9200_DELL_M24),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cb,
+ "Dell Latitude 120L", STAC_9200_DELL_M24),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cc,
+ "Dell Latitude D820", STAC_9200_DELL_M22),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cd,
+ "Dell Inspiron E1705/9400", STAC_9200_DELL_M27),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ce,
+ "Dell XPS M1710", STAC_9200_DELL_M23),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cf,
+ "Dell Precision M90", STAC_9200_DELL_M23),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d3,
+ "unknown Dell", STAC_9200_DELL_M22),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d4,
+ "unknown Dell", STAC_9200_DELL_M22),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d6,
+ "unknown Dell", STAC_9200_DELL_M22),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d8,
+ "Dell Inspiron 640m", STAC_9200_DELL_M21),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d9,
+ "unknown Dell", STAC_9200_DELL_D23),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01da,
+ "unknown Dell", STAC_9200_DELL_D23),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01de,
+ "unknown Dell", STAC_9200_DELL_D21),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01e3,
+ "unknown Dell", STAC_9200_DELL_D23),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01e8,
+ "unknown Dell", STAC_9200_DELL_D21),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ee,
+ "unknown Dell", STAC_9200_DELL_M25),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ef,
+ "unknown Dell", STAC_9200_DELL_M25),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f5,
+ "Dell Inspiron 1501", STAC_9200_DELL_M26),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f6,
+ "unknown Dell", STAC_9200_DELL_M26),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0201,
+ "Dell Latitude D430", STAC_9200_DELL_M22),
+ /* Panasonic */
+ SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_9200_PANASONIC),
+ /* Gateway machines needs EAPD to be set on resume */
+ SND_PCI_QUIRK(0x107b, 0x0205, "Gateway S-7110M", STAC_9200_M4),
+ SND_PCI_QUIRK(0x107b, 0x0317, "Gateway MT3423, MX341*", STAC_9200_M4_2),
+ SND_PCI_QUIRK(0x107b, 0x0318, "Gateway ML3019, MT3707", STAC_9200_M4_2),
+ /* OQO Mobile */
+ SND_PCI_QUIRK(0x1106, 0x3288, "OQO Model 2", STAC_9200_OQO),
+ {} /* terminator */
+};
+
+static const struct hda_pintbl ref925x_pin_configs[] = {
+ { 0x07, 0x40c003f0 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x01813022 },
+ { 0x0b, 0x02a19021 },
+ { 0x0c, 0x90a70320 },
+ { 0x0d, 0x02214210 },
+ { 0x10, 0x01019020 },
+ { 0x11, 0x9033032e },
+ {}
+};
+
+static const struct hda_pintbl stac925xM1_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x9033032e },
+ {}
+};
+
+static const struct hda_pintbl stac925xM1_2_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x9033032e },
+ {}
+};
+
+static const struct hda_pintbl stac925xM2_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x9033032e },
+ {}
+};
+
+static const struct hda_pintbl stac925xM2_2_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x9033032e },
+ {}
+};
+
+static const struct hda_pintbl stac925xM3_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x503303f3 },
+ {}
+};
+
+static const struct hda_pintbl stac925xM5_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x9033032e },
+ {}
+};
+
+static const struct hda_pintbl stac925xM6_pin_configs[] = {
+ { 0x07, 0x40c003f4 },
+ { 0x08, 0x424503f2 },
+ { 0x0a, 0x400000f3 },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x40a000f0 },
+ { 0x0d, 0x90100210 },
+ { 0x10, 0x400003f1 },
+ { 0x11, 0x90330320 },
+ {}
+};
+
+static const struct hda_fixup stac925x_fixups[] = {
+ [STAC_REF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = ref925x_pin_configs,
+ },
+ [STAC_M1] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM1_pin_configs,
+ },
+ [STAC_M1_2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM1_2_pin_configs,
+ },
+ [STAC_M2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM2_pin_configs,
+ },
+ [STAC_M2_2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM2_2_pin_configs,
+ },
+ [STAC_M3] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM3_pin_configs,
+ },
+ [STAC_M5] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM5_pin_configs,
+ },
+ [STAC_M6] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac925xM6_pin_configs,
+ },
+};
+
+static const struct hda_model_fixup stac925x_models[] = {
+ { .id = STAC_REF, .name = "ref" },
+ { .id = STAC_M1, .name = "m1" },
+ { .id = STAC_M1_2, .name = "m1-2" },
+ { .id = STAC_M2, .name = "m2" },
+ { .id = STAC_M2_2, .name = "m2-2" },
+ { .id = STAC_M3, .name = "m3" },
+ { .id = STAC_M5, .name = "m5" },
+ { .id = STAC_M6, .name = "m6" },
+ {}
+};
+
+static const struct hda_quirk stac925x_fixup_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF),
+ SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF),
+
+ /* Default table for unknown ID */
+ SND_PCI_QUIRK(0x1002, 0x437b, "Gateway mobile", STAC_M2_2),
+
+ /* gateway machines are checked via codec ssid */
+ SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_M2),
+ SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_M5),
+ SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_M1),
+ SND_PCI_QUIRK(0x107b, 0x0681, "Gateway NX860", STAC_M2),
+ SND_PCI_QUIRK(0x107b, 0x0367, "Gateway MX6453", STAC_M1_2),
+ /* Not sure about the brand name for those */
+ SND_PCI_QUIRK(0x107b, 0x0281, "Gateway mobile", STAC_M1),
+ SND_PCI_QUIRK(0x107b, 0x0507, "Gateway mobile", STAC_M3),
+ SND_PCI_QUIRK(0x107b, 0x0281, "Gateway mobile", STAC_M6),
+ SND_PCI_QUIRK(0x107b, 0x0685, "Gateway mobile", STAC_M2_2),
+ {} /* terminator */
+};
+
+static const struct hda_pintbl ref92hd73xx_pin_configs[] = {
+ // Port A-H
+ { 0x0a, 0x02214030 },
+ { 0x0b, 0x02a19040 },
+ { 0x0c, 0x01a19020 },
+ { 0x0d, 0x02214030 },
+ { 0x0e, 0x0181302e },
+ { 0x0f, 0x01014010 },
+ { 0x10, 0x01014020 },
+ { 0x11, 0x01014030 },
+ // CD in
+ { 0x12, 0x02319040 },
+ // Digial Mic ins
+ { 0x13, 0x90a000f0 },
+ { 0x14, 0x90a000f0 },
+ // Digital outs
+ { 0x22, 0x01452050 },
+ { 0x23, 0x01452050 },
+ {}
+};
+
+static const struct hda_pintbl dell_m6_pin_configs[] = {
+ { 0x0a, 0x0321101f },
+ { 0x0b, 0x4f00000f },
+ { 0x0c, 0x4f0000f0 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x03a11020 },
+ { 0x0f, 0x0321101f },
+ { 0x10, 0x4f0000f0 },
+ { 0x11, 0x4f0000f0 },
+ { 0x12, 0x4f0000f0 },
+ { 0x13, 0x90a60160 },
+ { 0x14, 0x4f0000f0 },
+ { 0x22, 0x4f0000f0 },
+ { 0x23, 0x4f0000f0 },
+ {}
+};
+
+static const struct hda_pintbl alienware_m17x_pin_configs[] = {
+ { 0x0a, 0x0321101f },
+ { 0x0b, 0x0321101f },
+ { 0x0c, 0x03a11020 },
+ { 0x0d, 0x03014020 },
+ { 0x0e, 0x90170110 },
+ { 0x0f, 0x4f0000f0 },
+ { 0x10, 0x4f0000f0 },
+ { 0x11, 0x4f0000f0 },
+ { 0x12, 0x4f0000f0 },
+ { 0x13, 0x90a60160 },
+ { 0x14, 0x4f0000f0 },
+ { 0x22, 0x4f0000f0 },
+ { 0x23, 0x904601b0 },
+ {}
+};
+
+static const struct hda_pintbl intel_dg45id_pin_configs[] = {
+ // Analog outputs
+ { 0x0a, 0x02214230 },
+ { 0x0b, 0x02A19240 },
+ { 0x0c, 0x01013214 },
+ { 0x0d, 0x01014210 },
+ { 0x0e, 0x01A19250 },
+ { 0x0f, 0x01011212 },
+ { 0x10, 0x01016211 },
+ // Digital output
+ { 0x22, 0x01451380 },
+ { 0x23, 0x40f000f0 },
+ {}
+};
+
+static const struct hda_pintbl stac92hd89xx_hp_front_jack_pin_configs[] = {
+ { 0x0a, 0x02214030 },
+ { 0x0b, 0x02A19010 },
+ {}
+};
+
+static const struct hda_pintbl stac92hd89xx_hp_z1_g2_right_mic_jack_pin_configs[] = {
+ { 0x0e, 0x400000f0 },
+ {}
+};
+
+static void stac92hd73xx_fixup_ref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_apply_pincfgs(codec, ref92hd73xx_pin_configs);
+ spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0;
+}
+
+static void stac92hd73xx_fixup_dell(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ snd_hda_apply_pincfgs(codec, dell_m6_pin_configs);
+ spec->eapd_switch = 0;
+}
+
+static void stac92hd73xx_fixup_dell_eq(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ stac92hd73xx_fixup_dell(codec);
+ snd_hda_add_verbs(codec, dell_eq_core_init);
+ spec->volknob_init = 1;
+}
+
+/* Analog Mics */
+static void stac92hd73xx_fixup_dell_m6_amic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ stac92hd73xx_fixup_dell(codec);
+ snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
+}
+
+/* Digital Mics */
+static void stac92hd73xx_fixup_dell_m6_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ stac92hd73xx_fixup_dell(codec);
+ snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
+}
+
+/* Both */
+static void stac92hd73xx_fixup_dell_m6_both(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ stac92hd73xx_fixup_dell(codec);
+ snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
+ snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
+}
+
+static void stac92hd73xx_fixup_alienware_m17x(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_apply_pincfgs(codec, alienware_m17x_pin_configs);
+ spec->eapd_switch = 0;
+}
+
+static void stac92hd73xx_fixup_no_jd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ codec->no_jack_detect = 1;
+}
+
+
+static void stac92hd73xx_disable_automute(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ spec->gen.suppress_auto_mute = 1;
+}
+
+static const struct hda_fixup stac92hd73xx_fixups[] = {
+ [STAC_92HD73XX_REF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_ref,
+ },
+ [STAC_DELL_M6_AMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_dell_m6_amic,
+ },
+ [STAC_DELL_M6_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_dell_m6_dmic,
+ },
+ [STAC_DELL_M6_BOTH] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_dell_m6_both,
+ },
+ [STAC_DELL_EQ] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_dell_eq,
+ },
+ [STAC_ALIENWARE_M17X] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_alienware_m17x,
+ },
+ [STAC_ELO_VUPOINT_15MX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_disable_automute,
+ },
+ [STAC_92HD73XX_INTEL] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = intel_dg45id_pin_configs,
+ },
+ [STAC_92HD73XX_NO_JD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd73xx_fixup_no_jd,
+ },
+ [STAC_92HD89XX_HP_FRONT_JACK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac92hd89xx_hp_front_jack_pin_configs,
+ },
+ [STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac92hd89xx_hp_z1_g2_right_mic_jack_pin_configs,
+ },
+ [STAC_92HD73XX_ASUS_MOBO] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* enable 5.1 and SPDIF out */
+ { 0x0c, 0x01014411 },
+ { 0x0d, 0x01014410 },
+ { 0x0e, 0x01014412 },
+ { 0x22, 0x014b1180 },
+ { }
+ }
+ },
+};
+
+static const struct hda_model_fixup stac92hd73xx_models[] = {
+ { .id = STAC_92HD73XX_NO_JD, .name = "no-jd" },
+ { .id = STAC_92HD73XX_REF, .name = "ref" },
+ { .id = STAC_92HD73XX_INTEL, .name = "intel" },
+ { .id = STAC_DELL_M6_AMIC, .name = "dell-m6-amic" },
+ { .id = STAC_DELL_M6_DMIC, .name = "dell-m6-dmic" },
+ { .id = STAC_DELL_M6_BOTH, .name = "dell-m6" },
+ { .id = STAC_DELL_EQ, .name = "dell-eq" },
+ { .id = STAC_ALIENWARE_M17X, .name = "alienware" },
+ { .id = STAC_ELO_VUPOINT_15MX, .name = "elo-vupoint-15mx" },
+ { .id = STAC_92HD73XX_ASUS_MOBO, .name = "asus-mobo" },
+ {}
+};
+
+static const struct hda_quirk stac92hd73xx_fixup_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+ "DFI LanParty", STAC_92HD73XX_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+ "DFI LanParty", STAC_92HD73XX_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5001,
+ "Intel DP45SG", STAC_92HD73XX_INTEL),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5002,
+ "Intel DG45ID", STAC_92HD73XX_INTEL),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5003,
+ "Intel DG45FC", STAC_92HD73XX_INTEL),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0254,
+ "Dell Studio 1535", STAC_DELL_M6_DMIC),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0255,
+ "unknown Dell", STAC_DELL_M6_DMIC),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0256,
+ "unknown Dell", STAC_DELL_M6_BOTH),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0257,
+ "unknown Dell", STAC_DELL_M6_BOTH),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025e,
+ "unknown Dell", STAC_DELL_M6_AMIC),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025f,
+ "unknown Dell", STAC_DELL_M6_AMIC),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0271,
+ "unknown Dell", STAC_DELL_M6_DMIC),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0272,
+ "unknown Dell", STAC_DELL_M6_DMIC),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x029f,
+ "Dell Studio 1537", STAC_DELL_M6_DMIC),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a0,
+ "Dell Studio 17", STAC_DELL_M6_DMIC),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02be,
+ "Dell Studio 1555", STAC_DELL_M6_DMIC),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02bd,
+ "Dell Studio 1557", STAC_DELL_M6_DMIC),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02fe,
+ "Dell Studio XPS 1645", STAC_DELL_M6_DMIC),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0413,
+ "Dell Studio 1558", STAC_DELL_M6_DMIC),
+ /* codec SSID matching */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a1,
+ "Alienware M17x", STAC_ALIENWARE_M17X),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x043a,
+ "Alienware M17x", STAC_ALIENWARE_M17X),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0490,
+ "Alienware M17x R3", STAC_DELL_EQ),
+ SND_PCI_QUIRK(0x1059, 0x1011,
+ "ELO VuPoint 15MX", STAC_ELO_VUPOINT_15MX),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1927,
+ "HP Z1 G2", STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2b17,
+ "unknown HP", STAC_92HD89XX_HP_FRONT_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_ASUSTEK, 0x83f8, "ASUS AT4NM10",
+ STAC_92HD73XX_ASUS_MOBO),
+ {} /* terminator */
+};
+
+static const struct hda_pintbl ref92hd83xxx_pin_configs[] = {
+ { 0x0a, 0x02214030 },
+ { 0x0b, 0x02211010 },
+ { 0x0c, 0x02a19020 },
+ { 0x0d, 0x02170130 },
+ { 0x0e, 0x01014050 },
+ { 0x0f, 0x01819040 },
+ { 0x10, 0x01014020 },
+ { 0x11, 0x90a3014e },
+ { 0x1f, 0x01451160 },
+ { 0x20, 0x98560170 },
+ {}
+};
+
+static const struct hda_pintbl dell_s14_pin_configs[] = {
+ { 0x0a, 0x0221403f },
+ { 0x0b, 0x0221101f },
+ { 0x0c, 0x02a19020 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x40f000f0 },
+ { 0x0f, 0x40f000f0 },
+ { 0x10, 0x40f000f0 },
+ { 0x11, 0x90a60160 },
+ { 0x1f, 0x40f000f0 },
+ { 0x20, 0x40f000f0 },
+ {}
+};
+
+static const struct hda_pintbl dell_vostro_3500_pin_configs[] = {
+ { 0x0a, 0x02a11020 },
+ { 0x0b, 0x0221101f },
+ { 0x0c, 0x400000f0 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x400000f1 },
+ { 0x0f, 0x400000f2 },
+ { 0x10, 0x400000f3 },
+ { 0x11, 0x90a60160 },
+ { 0x1f, 0x400000f4 },
+ { 0x20, 0x400000f5 },
+ {}
+};
+
+static const struct hda_pintbl hp_dv7_4000_pin_configs[] = {
+ { 0x0a, 0x03a12050 },
+ { 0x0b, 0x0321201f },
+ { 0x0c, 0x40f000f0 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x40f000f0 },
+ { 0x0f, 0x40f000f0 },
+ { 0x10, 0x90170110 },
+ { 0x11, 0xd5a30140 },
+ { 0x1f, 0x40f000f0 },
+ { 0x20, 0x40f000f0 },
+ {}
+};
+
+static const struct hda_pintbl hp_zephyr_pin_configs[] = {
+ { 0x0a, 0x01813050 },
+ { 0x0b, 0x0421201f },
+ { 0x0c, 0x04a1205e },
+ { 0x0d, 0x96130310 },
+ { 0x0e, 0x96130310 },
+ { 0x0f, 0x0101401f },
+ { 0x10, 0x1111611f },
+ { 0x11, 0xd5a30130 },
+ {}
+};
+
+static const struct hda_pintbl hp_cNB11_intquad_pin_configs[] = {
+ { 0x0a, 0x40f000f0 },
+ { 0x0b, 0x0221101f },
+ { 0x0c, 0x02a11020 },
+ { 0x0d, 0x92170110 },
+ { 0x0e, 0x40f000f0 },
+ { 0x0f, 0x92170110 },
+ { 0x10, 0x40f000f0 },
+ { 0x11, 0xd5a30130 },
+ { 0x1f, 0x40f000f0 },
+ { 0x20, 0x40f000f0 },
+ {}
+};
+
+static void stac92hd83xxx_fixup_hp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ if (hp_bnb2011_with_dock(codec)) {
+ snd_hda_codec_set_pincfg(codec, 0xa, 0x2101201f);
+ snd_hda_codec_set_pincfg(codec, 0xf, 0x2181205e);
+ }
+
+ if (find_mute_led_cfg(codec, spec->default_polarity))
+ codec_dbg(codec, "mute LED gpio %d polarity %d\n",
+ spec->gpio_led,
+ spec->gpio_led_polarity);
+
+ /* allow auto-switching of dock line-in */
+ spec->gen.line_in_auto_switch = true;
+}
+
+static void stac92hd83xxx_fixup_hp_zephyr(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_apply_pincfgs(codec, hp_zephyr_pin_configs);
+ snd_hda_add_verbs(codec, stac92hd83xxx_hp_zephyr_init);
+}
+
+static void stac92hd83xxx_fixup_hp_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->default_polarity = 0;
+}
+
+static void stac92hd83xxx_fixup_hp_inv_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->default_polarity = 1;
+}
+
+static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mic_mute_led_gpio = 0x08; /* GPIO3 */
+ /* resetting controller clears GPIO, so we need to keep on */
+ codec->core.power_caps &= ~AC_PWRST_CLKSTOP;
+ }
+}
+
+static void stac92hd83xxx_fixup_hp_led_gpio10(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gpio_led = 0x10; /* GPIO4 */
+ spec->default_polarity = 0;
+ }
+}
+
+static void stac92hd83xxx_fixup_headset_jack(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->headset_jack = 1;
+}
+
+static void stac92hd83xxx_fixup_gpio10_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir =
+ spec->gpio_data = 0x10;
+ spec->eapd_switch = 0;
+}
+
+static void hp_envy_ts_fixup_dac_bind(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ static const hda_nid_t preferred_pairs[] = {
+ 0xd, 0x13,
+ 0
+ };
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ spec->gen.preferred_dacs = preferred_pairs;
+}
+
+static const struct hda_verb hp_bnb13_eq_verbs[] = {
+ /* 44.1KHz base */
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0x68 },
+ { 0x22, 0x7A8, 0x17 },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0x68 },
+ { 0x22, 0x7AB, 0x17 },
+ { 0x22, 0x7AC, 0x00 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x83 },
+ { 0x22, 0x7A7, 0x2F },
+ { 0x22, 0x7A8, 0xD1 },
+ { 0x22, 0x7A9, 0x83 },
+ { 0x22, 0x7AA, 0x2F },
+ { 0x22, 0x7AB, 0xD1 },
+ { 0x22, 0x7AC, 0x01 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0x68 },
+ { 0x22, 0x7A8, 0x17 },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0x68 },
+ { 0x22, 0x7AB, 0x17 },
+ { 0x22, 0x7AC, 0x02 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x7C },
+ { 0x22, 0x7A7, 0xC6 },
+ { 0x22, 0x7A8, 0x0C },
+ { 0x22, 0x7A9, 0x7C },
+ { 0x22, 0x7AA, 0xC6 },
+ { 0x22, 0x7AB, 0x0C },
+ { 0x22, 0x7AC, 0x03 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xC3 },
+ { 0x22, 0x7A7, 0x25 },
+ { 0x22, 0x7A8, 0xAF },
+ { 0x22, 0x7A9, 0xC3 },
+ { 0x22, 0x7AA, 0x25 },
+ { 0x22, 0x7AB, 0xAF },
+ { 0x22, 0x7AC, 0x04 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0x85 },
+ { 0x22, 0x7A8, 0x73 },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0x85 },
+ { 0x22, 0x7AB, 0x73 },
+ { 0x22, 0x7AC, 0x05 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x85 },
+ { 0x22, 0x7A7, 0x39 },
+ { 0x22, 0x7A8, 0xC7 },
+ { 0x22, 0x7A9, 0x85 },
+ { 0x22, 0x7AA, 0x39 },
+ { 0x22, 0x7AB, 0xC7 },
+ { 0x22, 0x7AC, 0x06 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3C },
+ { 0x22, 0x7A7, 0x90 },
+ { 0x22, 0x7A8, 0xB0 },
+ { 0x22, 0x7A9, 0x3C },
+ { 0x22, 0x7AA, 0x90 },
+ { 0x22, 0x7AB, 0xB0 },
+ { 0x22, 0x7AC, 0x07 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x7A },
+ { 0x22, 0x7A7, 0xC6 },
+ { 0x22, 0x7A8, 0x39 },
+ { 0x22, 0x7A9, 0x7A },
+ { 0x22, 0x7AA, 0xC6 },
+ { 0x22, 0x7AB, 0x39 },
+ { 0x22, 0x7AC, 0x08 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xC4 },
+ { 0x22, 0x7A7, 0xE9 },
+ { 0x22, 0x7A8, 0xDC },
+ { 0x22, 0x7A9, 0xC4 },
+ { 0x22, 0x7AA, 0xE9 },
+ { 0x22, 0x7AB, 0xDC },
+ { 0x22, 0x7AC, 0x09 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3D },
+ { 0x22, 0x7A7, 0xE1 },
+ { 0x22, 0x7A8, 0x0D },
+ { 0x22, 0x7A9, 0x3D },
+ { 0x22, 0x7AA, 0xE1 },
+ { 0x22, 0x7AB, 0x0D },
+ { 0x22, 0x7AC, 0x0A },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x89 },
+ { 0x22, 0x7A7, 0xB6 },
+ { 0x22, 0x7A8, 0xEB },
+ { 0x22, 0x7A9, 0x89 },
+ { 0x22, 0x7AA, 0xB6 },
+ { 0x22, 0x7AB, 0xEB },
+ { 0x22, 0x7AC, 0x0B },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x39 },
+ { 0x22, 0x7A7, 0x9D },
+ { 0x22, 0x7A8, 0xFE },
+ { 0x22, 0x7A9, 0x39 },
+ { 0x22, 0x7AA, 0x9D },
+ { 0x22, 0x7AB, 0xFE },
+ { 0x22, 0x7AC, 0x0C },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x76 },
+ { 0x22, 0x7A7, 0x49 },
+ { 0x22, 0x7A8, 0x15 },
+ { 0x22, 0x7A9, 0x76 },
+ { 0x22, 0x7AA, 0x49 },
+ { 0x22, 0x7AB, 0x15 },
+ { 0x22, 0x7AC, 0x0D },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xC8 },
+ { 0x22, 0x7A7, 0x80 },
+ { 0x22, 0x7A8, 0xF5 },
+ { 0x22, 0x7A9, 0xC8 },
+ { 0x22, 0x7AA, 0x80 },
+ { 0x22, 0x7AB, 0xF5 },
+ { 0x22, 0x7AC, 0x0E },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x40 },
+ { 0x22, 0x7A7, 0x00 },
+ { 0x22, 0x7A8, 0x00 },
+ { 0x22, 0x7A9, 0x40 },
+ { 0x22, 0x7AA, 0x00 },
+ { 0x22, 0x7AB, 0x00 },
+ { 0x22, 0x7AC, 0x0F },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x90 },
+ { 0x22, 0x7A7, 0x68 },
+ { 0x22, 0x7A8, 0xF1 },
+ { 0x22, 0x7A9, 0x90 },
+ { 0x22, 0x7AA, 0x68 },
+ { 0x22, 0x7AB, 0xF1 },
+ { 0x22, 0x7AC, 0x10 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x34 },
+ { 0x22, 0x7A7, 0x47 },
+ { 0x22, 0x7A8, 0x6C },
+ { 0x22, 0x7A9, 0x34 },
+ { 0x22, 0x7AA, 0x47 },
+ { 0x22, 0x7AB, 0x6C },
+ { 0x22, 0x7AC, 0x11 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x6F },
+ { 0x22, 0x7A7, 0x97 },
+ { 0x22, 0x7A8, 0x0F },
+ { 0x22, 0x7A9, 0x6F },
+ { 0x22, 0x7AA, 0x97 },
+ { 0x22, 0x7AB, 0x0F },
+ { 0x22, 0x7AC, 0x12 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xCB },
+ { 0x22, 0x7A7, 0xB8 },
+ { 0x22, 0x7A8, 0x94 },
+ { 0x22, 0x7A9, 0xCB },
+ { 0x22, 0x7AA, 0xB8 },
+ { 0x22, 0x7AB, 0x94 },
+ { 0x22, 0x7AC, 0x13 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x40 },
+ { 0x22, 0x7A7, 0x00 },
+ { 0x22, 0x7A8, 0x00 },
+ { 0x22, 0x7A9, 0x40 },
+ { 0x22, 0x7AA, 0x00 },
+ { 0x22, 0x7AB, 0x00 },
+ { 0x22, 0x7AC, 0x14 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x95 },
+ { 0x22, 0x7A7, 0x76 },
+ { 0x22, 0x7A8, 0x5B },
+ { 0x22, 0x7A9, 0x95 },
+ { 0x22, 0x7AA, 0x76 },
+ { 0x22, 0x7AB, 0x5B },
+ { 0x22, 0x7AC, 0x15 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x31 },
+ { 0x22, 0x7A7, 0xAC },
+ { 0x22, 0x7A8, 0x31 },
+ { 0x22, 0x7A9, 0x31 },
+ { 0x22, 0x7AA, 0xAC },
+ { 0x22, 0x7AB, 0x31 },
+ { 0x22, 0x7AC, 0x16 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x6A },
+ { 0x22, 0x7A7, 0x89 },
+ { 0x22, 0x7A8, 0xA5 },
+ { 0x22, 0x7A9, 0x6A },
+ { 0x22, 0x7AA, 0x89 },
+ { 0x22, 0x7AB, 0xA5 },
+ { 0x22, 0x7AC, 0x17 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xCE },
+ { 0x22, 0x7A7, 0x53 },
+ { 0x22, 0x7A8, 0xCF },
+ { 0x22, 0x7A9, 0xCE },
+ { 0x22, 0x7AA, 0x53 },
+ { 0x22, 0x7AB, 0xCF },
+ { 0x22, 0x7AC, 0x18 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x40 },
+ { 0x22, 0x7A7, 0x00 },
+ { 0x22, 0x7A8, 0x00 },
+ { 0x22, 0x7A9, 0x40 },
+ { 0x22, 0x7AA, 0x00 },
+ { 0x22, 0x7AB, 0x00 },
+ { 0x22, 0x7AC, 0x19 },
+ { 0x22, 0x7AD, 0x80 },
+ /* 48KHz base */
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0x88 },
+ { 0x22, 0x7A8, 0xDC },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0x88 },
+ { 0x22, 0x7AB, 0xDC },
+ { 0x22, 0x7AC, 0x1A },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x82 },
+ { 0x22, 0x7A7, 0xEE },
+ { 0x22, 0x7A8, 0x46 },
+ { 0x22, 0x7A9, 0x82 },
+ { 0x22, 0x7AA, 0xEE },
+ { 0x22, 0x7AB, 0x46 },
+ { 0x22, 0x7AC, 0x1B },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0x88 },
+ { 0x22, 0x7A8, 0xDC },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0x88 },
+ { 0x22, 0x7AB, 0xDC },
+ { 0x22, 0x7AC, 0x1C },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x7D },
+ { 0x22, 0x7A7, 0x09 },
+ { 0x22, 0x7A8, 0x28 },
+ { 0x22, 0x7A9, 0x7D },
+ { 0x22, 0x7AA, 0x09 },
+ { 0x22, 0x7AB, 0x28 },
+ { 0x22, 0x7AC, 0x1D },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xC2 },
+ { 0x22, 0x7A7, 0xE5 },
+ { 0x22, 0x7A8, 0xB4 },
+ { 0x22, 0x7A9, 0xC2 },
+ { 0x22, 0x7AA, 0xE5 },
+ { 0x22, 0x7AB, 0xB4 },
+ { 0x22, 0x7AC, 0x1E },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0xA3 },
+ { 0x22, 0x7A8, 0x1F },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0xA3 },
+ { 0x22, 0x7AB, 0x1F },
+ { 0x22, 0x7AC, 0x1F },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x84 },
+ { 0x22, 0x7A7, 0xCA },
+ { 0x22, 0x7A8, 0xF1 },
+ { 0x22, 0x7A9, 0x84 },
+ { 0x22, 0x7AA, 0xCA },
+ { 0x22, 0x7AB, 0xF1 },
+ { 0x22, 0x7AC, 0x20 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3C },
+ { 0x22, 0x7A7, 0xD5 },
+ { 0x22, 0x7A8, 0x9C },
+ { 0x22, 0x7A9, 0x3C },
+ { 0x22, 0x7AA, 0xD5 },
+ { 0x22, 0x7AB, 0x9C },
+ { 0x22, 0x7AC, 0x21 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x7B },
+ { 0x22, 0x7A7, 0x35 },
+ { 0x22, 0x7A8, 0x0F },
+ { 0x22, 0x7A9, 0x7B },
+ { 0x22, 0x7AA, 0x35 },
+ { 0x22, 0x7AB, 0x0F },
+ { 0x22, 0x7AC, 0x22 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xC4 },
+ { 0x22, 0x7A7, 0x87 },
+ { 0x22, 0x7A8, 0x45 },
+ { 0x22, 0x7A9, 0xC4 },
+ { 0x22, 0x7AA, 0x87 },
+ { 0x22, 0x7AB, 0x45 },
+ { 0x22, 0x7AC, 0x23 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3E },
+ { 0x22, 0x7A7, 0x0A },
+ { 0x22, 0x7A8, 0x78 },
+ { 0x22, 0x7A9, 0x3E },
+ { 0x22, 0x7AA, 0x0A },
+ { 0x22, 0x7AB, 0x78 },
+ { 0x22, 0x7AC, 0x24 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x88 },
+ { 0x22, 0x7A7, 0xE2 },
+ { 0x22, 0x7A8, 0x05 },
+ { 0x22, 0x7A9, 0x88 },
+ { 0x22, 0x7AA, 0xE2 },
+ { 0x22, 0x7AB, 0x05 },
+ { 0x22, 0x7AC, 0x25 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x3A },
+ { 0x22, 0x7A7, 0x1A },
+ { 0x22, 0x7A8, 0xA3 },
+ { 0x22, 0x7A9, 0x3A },
+ { 0x22, 0x7AA, 0x1A },
+ { 0x22, 0x7AB, 0xA3 },
+ { 0x22, 0x7AC, 0x26 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x77 },
+ { 0x22, 0x7A7, 0x1D },
+ { 0x22, 0x7A8, 0xFB },
+ { 0x22, 0x7A9, 0x77 },
+ { 0x22, 0x7AA, 0x1D },
+ { 0x22, 0x7AB, 0xFB },
+ { 0x22, 0x7AC, 0x27 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xC7 },
+ { 0x22, 0x7A7, 0xDA },
+ { 0x22, 0x7A8, 0xE5 },
+ { 0x22, 0x7A9, 0xC7 },
+ { 0x22, 0x7AA, 0xDA },
+ { 0x22, 0x7AB, 0xE5 },
+ { 0x22, 0x7AC, 0x28 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x40 },
+ { 0x22, 0x7A7, 0x00 },
+ { 0x22, 0x7A8, 0x00 },
+ { 0x22, 0x7A9, 0x40 },
+ { 0x22, 0x7AA, 0x00 },
+ { 0x22, 0x7AB, 0x00 },
+ { 0x22, 0x7AC, 0x29 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x8E },
+ { 0x22, 0x7A7, 0xD7 },
+ { 0x22, 0x7A8, 0x22 },
+ { 0x22, 0x7A9, 0x8E },
+ { 0x22, 0x7AA, 0xD7 },
+ { 0x22, 0x7AB, 0x22 },
+ { 0x22, 0x7AC, 0x2A },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x35 },
+ { 0x22, 0x7A7, 0x26 },
+ { 0x22, 0x7A8, 0xC6 },
+ { 0x22, 0x7A9, 0x35 },
+ { 0x22, 0x7AA, 0x26 },
+ { 0x22, 0x7AB, 0xC6 },
+ { 0x22, 0x7AC, 0x2B },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x71 },
+ { 0x22, 0x7A7, 0x28 },
+ { 0x22, 0x7A8, 0xDE },
+ { 0x22, 0x7A9, 0x71 },
+ { 0x22, 0x7AA, 0x28 },
+ { 0x22, 0x7AB, 0xDE },
+ { 0x22, 0x7AC, 0x2C },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xCA },
+ { 0x22, 0x7A7, 0xD9 },
+ { 0x22, 0x7A8, 0x3A },
+ { 0x22, 0x7A9, 0xCA },
+ { 0x22, 0x7AA, 0xD9 },
+ { 0x22, 0x7AB, 0x3A },
+ { 0x22, 0x7AC, 0x2D },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x40 },
+ { 0x22, 0x7A7, 0x00 },
+ { 0x22, 0x7A8, 0x00 },
+ { 0x22, 0x7A9, 0x40 },
+ { 0x22, 0x7AA, 0x00 },
+ { 0x22, 0x7AB, 0x00 },
+ { 0x22, 0x7AC, 0x2E },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x93 },
+ { 0x22, 0x7A7, 0x5E },
+ { 0x22, 0x7A8, 0xD8 },
+ { 0x22, 0x7A9, 0x93 },
+ { 0x22, 0x7AA, 0x5E },
+ { 0x22, 0x7AB, 0xD8 },
+ { 0x22, 0x7AC, 0x2F },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x32 },
+ { 0x22, 0x7A7, 0xB7 },
+ { 0x22, 0x7A8, 0xB1 },
+ { 0x22, 0x7A9, 0x32 },
+ { 0x22, 0x7AA, 0xB7 },
+ { 0x22, 0x7AB, 0xB1 },
+ { 0x22, 0x7AC, 0x30 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x6C },
+ { 0x22, 0x7A7, 0xA1 },
+ { 0x22, 0x7A8, 0x28 },
+ { 0x22, 0x7A9, 0x6C },
+ { 0x22, 0x7AA, 0xA1 },
+ { 0x22, 0x7AB, 0x28 },
+ { 0x22, 0x7AC, 0x31 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0xCD },
+ { 0x22, 0x7A7, 0x48 },
+ { 0x22, 0x7A8, 0x4F },
+ { 0x22, 0x7A9, 0xCD },
+ { 0x22, 0x7AA, 0x48 },
+ { 0x22, 0x7AB, 0x4F },
+ { 0x22, 0x7AC, 0x32 },
+ { 0x22, 0x7AD, 0x80 },
+ { 0x22, 0x7A6, 0x40 },
+ { 0x22, 0x7A7, 0x00 },
+ { 0x22, 0x7A8, 0x00 },
+ { 0x22, 0x7A9, 0x40 },
+ { 0x22, 0x7AA, 0x00 },
+ { 0x22, 0x7AB, 0x00 },
+ { 0x22, 0x7AC, 0x33 },
+ { 0x22, 0x7AD, 0x80 },
+ /* common */
+ { 0x22, 0x782, 0xC1 },
+ { 0x22, 0x771, 0x2C },
+ { 0x22, 0x772, 0x2C },
+ { 0x22, 0x788, 0x04 },
+ { 0x01, 0x7B0, 0x08 },
+ {}
+};
+
+static const struct hda_fixup stac92hd83xxx_fixups[] = {
+ [STAC_92HD83XXX_REF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = ref92hd83xxx_pin_configs,
+ },
+ [STAC_92HD83XXX_PWR_REF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = ref92hd83xxx_pin_configs,
+ },
+ [STAC_DELL_S14] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_s14_pin_configs,
+ },
+ [STAC_DELL_VOSTRO_3500] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_vostro_3500_pin_configs,
+ },
+ [STAC_92HD83XXX_HP_cNB11_INTQUAD] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = hp_cNB11_intquad_pin_configs,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_92HD83XXX_HP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_hp,
+ },
+ [STAC_HP_DV7_4000] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = hp_dv7_4000_pin_configs,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_HP_ZEPHYR] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_hp_zephyr,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_92HD83XXX_HP_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_hp_led,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_92HD83XXX_HP_INV_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_hp_inv_led,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_92HD83XXX_HP_MIC_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_hp_mic_led,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_HP_LED_GPIO10] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_hp_led_gpio10,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP,
+ },
+ [STAC_92HD83XXX_HEADSET_JACK] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_headset_jack,
+ },
+ [STAC_HP_ENVY_BASS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x0f, 0x90170111 },
+ {}
+ },
+ },
+ [STAC_HP_BNB13_EQ] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = hp_bnb13_eq_verbs,
+ .chained = true,
+ .chain_id = STAC_92HD83XXX_HP_MIC_LED,
+ },
+ [STAC_HP_ENVY_TS_BASS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x10, 0x92170111 },
+ {}
+ },
+ },
+ [STAC_HP_ENVY_TS_DAC_BIND] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = hp_envy_ts_fixup_dac_bind,
+ .chained = true,
+ .chain_id = STAC_HP_ENVY_TS_BASS,
+ },
+ [STAC_92HD83XXX_GPIO10_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd83xxx_fixup_gpio10_eapd,
+ },
+};
+
+static const struct hda_model_fixup stac92hd83xxx_models[] = {
+ { .id = STAC_92HD83XXX_REF, .name = "ref" },
+ { .id = STAC_92HD83XXX_PWR_REF, .name = "mic-ref" },
+ { .id = STAC_DELL_S14, .name = "dell-s14" },
+ { .id = STAC_DELL_VOSTRO_3500, .name = "dell-vostro-3500" },
+ { .id = STAC_92HD83XXX_HP_cNB11_INTQUAD, .name = "hp_cNB11_intquad" },
+ { .id = STAC_HP_DV7_4000, .name = "hp-dv7-4000" },
+ { .id = STAC_HP_ZEPHYR, .name = "hp-zephyr" },
+ { .id = STAC_92HD83XXX_HP_LED, .name = "hp-led" },
+ { .id = STAC_92HD83XXX_HP_INV_LED, .name = "hp-inv-led" },
+ { .id = STAC_92HD83XXX_HP_MIC_LED, .name = "hp-mic-led" },
+ { .id = STAC_92HD83XXX_HEADSET_JACK, .name = "headset-jack" },
+ { .id = STAC_HP_ENVY_BASS, .name = "hp-envy-bass" },
+ { .id = STAC_HP_BNB13_EQ, .name = "hp-bnb13-eq" },
+ { .id = STAC_HP_ENVY_TS_BASS, .name = "hp-envy-ts-bass" },
+ {}
+};
+
+static const struct hda_quirk stac92hd83xxx_fixup_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+ "DFI LanParty", STAC_92HD83XXX_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+ "DFI LanParty", STAC_92HD83XXX_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba,
+ "unknown Dell", STAC_DELL_S14),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0532,
+ "Dell Latitude E6230", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0533,
+ "Dell Latitude E6330", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0534,
+ "Dell Latitude E6430", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0535,
+ "Dell Latitude E6530", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x053c,
+ "Dell Latitude E5430", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x053d,
+ "Dell Latitude E5530", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0549,
+ "Dell Latitude E5430", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x057d,
+ "Dell Latitude E6430s", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0584,
+ "Dell Latitude E6430U", STAC_92HD83XXX_HEADSET_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x1028,
+ "Dell Vostro 3500", STAC_DELL_VOSTRO_3500),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1656,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1657,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1658,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1659,
+ "HP Pavilion dv7", STAC_HP_DV7_4000),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165A,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165B,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1888,
+ "HP Envy Spectre", STAC_HP_ENVY_BASS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1899,
+ "HP Folio 13", STAC_HP_LED_GPIO10),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x18df,
+ "HP Folio", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x18F8,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1909,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x190A,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x190e,
+ "HP ENVY TS", STAC_HP_ENVY_TS_BASS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1967,
+ "HP ENVY TS", STAC_HP_ENVY_TS_DAC_BIND),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1940,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1941,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1942,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1943,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1944,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1945,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1946,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1948,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1949,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194A,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194B,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194C,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194E,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x194F,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1950,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1951,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x195A,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x195B,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x195C,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1991,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2103,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2104,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2105,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2106,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2107,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2108,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2109,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x210A,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x210B,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211C,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211D,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211E,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x211F,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2120,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2121,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2122,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2123,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x213E,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x213F,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2140,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B2,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B3,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B5,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x21B6,
+ "HP bNB13", STAC_HP_BNB13_EQ),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x1900,
+ "HP", STAC_92HD83XXX_HP_MIC_LED),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x2000,
+ "HP", STAC_92HD83XXX_HP_MIC_LED),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x2100,
+ "HP", STAC_92HD83XXX_HP_MIC_LED),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355B,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355C,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355D,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355E,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355F,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3560,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358B,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358C,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358D,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3591,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3592,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3593,
+ "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3561,
+ "HP", STAC_HP_ZEPHYR),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3660,
+ "HP Mini", STAC_92HD83XXX_HP_LED),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x144E,
+ "HP Pavilion dv5", STAC_92HD83XXX_HP_INV_LED),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x148a,
+ "HP Mini", STAC_92HD83XXX_HP_LED),
+ SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_HP, "HP", STAC_92HD83XXX_HP),
+ /* match both for 0xfa91 and 0xfa93 */
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_TOSHIBA, 0xfffd, 0xfa91,
+ "Toshiba Satellite S50D", STAC_92HD83XXX_GPIO10_EAPD),
+ {} /* terminator */
+};
+
+/* HP dv7 bass switch - GPIO5 */
+#define stac_hp_bass_gpio_info snd_ctl_boolean_mono_info
+static int stac_hp_bass_gpio_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ ucontrol->value.integer.value[0] = !!(spec->gpio_data & 0x20);
+ return 0;
+}
+
+static int stac_hp_bass_gpio_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int gpio_data;
+
+ gpio_data = (spec->gpio_data & ~0x20) |
+ (ucontrol->value.integer.value[0] ? 0x20 : 0);
+ if (gpio_data == spec->gpio_data)
+ return 0;
+ spec->gpio_data = gpio_data;
+ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
+ return 1;
+}
+
+static const struct snd_kcontrol_new stac_hp_bass_sw_ctrl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = stac_hp_bass_gpio_info,
+ .get = stac_hp_bass_gpio_get,
+ .put = stac_hp_bass_gpio_put,
+};
+
+static int stac_add_hp_bass_switch(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (!snd_hda_gen_add_kctl(&spec->gen, "Bass Speaker Playback Switch",
+ &stac_hp_bass_sw_ctrl))
+ return -ENOMEM;
+
+ spec->gpio_mask |= 0x20;
+ spec->gpio_dir |= 0x20;
+ spec->gpio_data |= 0x20;
+ return 0;
+}
+
+static const struct hda_pintbl ref92hd71bxx_pin_configs[] = {
+ { 0x0a, 0x02214030 },
+ { 0x0b, 0x02a19040 },
+ { 0x0c, 0x01a19020 },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x0181302e },
+ { 0x0f, 0x01014010 },
+ { 0x14, 0x01019020 },
+ { 0x18, 0x90a000f0 },
+ { 0x19, 0x90a000f0 },
+ { 0x1e, 0x01452050 },
+ { 0x1f, 0x01452050 },
+ {}
+};
+
+static const struct hda_pintbl dell_m4_1_pin_configs[] = {
+ { 0x0a, 0x0421101f },
+ { 0x0b, 0x04a11221 },
+ { 0x0c, 0x40f000f0 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x23a1902e },
+ { 0x0f, 0x23014250 },
+ { 0x14, 0x40f000f0 },
+ { 0x18, 0x90a000f0 },
+ { 0x19, 0x40f000f0 },
+ { 0x1e, 0x4f0000f0 },
+ { 0x1f, 0x4f0000f0 },
+ {}
+};
+
+static const struct hda_pintbl dell_m4_2_pin_configs[] = {
+ { 0x0a, 0x0421101f },
+ { 0x0b, 0x04a11221 },
+ { 0x0c, 0x90a70330 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x23a1902e },
+ { 0x0f, 0x23014250 },
+ { 0x14, 0x40f000f0 },
+ { 0x18, 0x40f000f0 },
+ { 0x19, 0x40f000f0 },
+ { 0x1e, 0x044413b0 },
+ { 0x1f, 0x044413b0 },
+ {}
+};
+
+static const struct hda_pintbl dell_m4_3_pin_configs[] = {
+ { 0x0a, 0x0421101f },
+ { 0x0b, 0x04a11221 },
+ { 0x0c, 0x90a70330 },
+ { 0x0d, 0x90170110 },
+ { 0x0e, 0x40f000f0 },
+ { 0x0f, 0x40f000f0 },
+ { 0x14, 0x40f000f0 },
+ { 0x18, 0x90a000f0 },
+ { 0x19, 0x40f000f0 },
+ { 0x1e, 0x044413b0 },
+ { 0x1f, 0x044413b0 },
+ {}
+};
+
+static void stac92hd71bxx_fixup_ref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_apply_pincfgs(codec, ref92hd71bxx_pin_configs);
+ spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0;
+}
+
+static void stac92hd71bxx_fixup_hp_m4(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct hda_jack_callback *jack;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ /* Enable VREF power saving on GPIO1 detect */
+ snd_hda_codec_write_cache(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02);
+ jack = snd_hda_jack_detect_enable_callback(codec, codec->core.afg,
+ stac_vref_event);
+ if (!IS_ERR(jack))
+ jack->private_data = 0x02;
+
+ spec->gpio_mask |= 0x02;
+
+ /* enable internal microphone */
+ snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040);
+}
+
+static void stac92hd71bxx_fixup_hp_dv4(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ spec->gpio_led = 0x01;
+}
+
+static void stac92hd71bxx_fixup_hp_dv5(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ unsigned int cap;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010);
+ break;
+
+ case HDA_FIXUP_ACT_PROBE:
+ /* enable bass on HP dv7 */
+ cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP);
+ cap &= AC_GPIO_IO_COUNT;
+ if (cap >= 6)
+ stac_add_hp_bass_switch(codec);
+ break;
+ }
+}
+
+static void stac92hd71bxx_fixup_hp_hdx(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ spec->gpio_led = 0x08;
+}
+
+static bool is_hp_output(struct hda_codec *codec, hda_nid_t pin)
+{
+ unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, pin);
+
+ /* count line-out, too, as BIOS sets often so */
+ return get_defcfg_connect(pin_cfg) != AC_JACK_PORT_NONE &&
+ (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT ||
+ get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT);
+}
+
+static void fixup_hp_headphone(struct hda_codec *codec, hda_nid_t pin)
+{
+ unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, pin);
+
+ /* It was changed in the BIOS to just satisfy MS DTM.
+ * Lets turn it back into follower HP
+ */
+ pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE)) |
+ (AC_JACK_HP_OUT << AC_DEFCFG_DEVICE_SHIFT);
+ pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC | AC_DEFCFG_SEQUENCE))) |
+ 0x1f;
+ snd_hda_codec_set_pincfg(codec, pin, pin_cfg);
+}
+
+static void stac92hd71bxx_fixup_hp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ /* when both output A and F are assigned, these are supposedly
+ * dock and built-in headphones; fix both pin configs
+ */
+ if (is_hp_output(codec, 0x0a) && is_hp_output(codec, 0x0f)) {
+ fixup_hp_headphone(codec, 0x0a);
+ fixup_hp_headphone(codec, 0x0f);
+ }
+
+ if (find_mute_led_cfg(codec, 1))
+ codec_dbg(codec, "mute LED gpio %d polarity %d\n",
+ spec->gpio_led,
+ spec->gpio_led_polarity);
+
+}
+
+static const struct hda_fixup stac92hd71bxx_fixups[] = {
+ [STAC_92HD71BXX_REF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd71bxx_fixup_ref,
+ },
+ [STAC_DELL_M4_1] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_m4_1_pin_configs,
+ },
+ [STAC_DELL_M4_2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_m4_2_pin_configs,
+ },
+ [STAC_DELL_M4_3] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_m4_3_pin_configs,
+ },
+ [STAC_HP_M4] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd71bxx_fixup_hp_m4,
+ .chained = true,
+ .chain_id = STAC_92HD71BXX_HP,
+ },
+ [STAC_HP_DV4] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd71bxx_fixup_hp_dv4,
+ .chained = true,
+ .chain_id = STAC_HP_DV5,
+ },
+ [STAC_HP_DV5] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd71bxx_fixup_hp_dv5,
+ .chained = true,
+ .chain_id = STAC_92HD71BXX_HP,
+ },
+ [STAC_HP_HDX] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd71bxx_fixup_hp_hdx,
+ .chained = true,
+ .chain_id = STAC_92HD71BXX_HP,
+ },
+ [STAC_92HD71BXX_HP] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd71bxx_fixup_hp,
+ },
+};
+
+static const struct hda_model_fixup stac92hd71bxx_models[] = {
+ { .id = STAC_92HD71BXX_REF, .name = "ref" },
+ { .id = STAC_DELL_M4_1, .name = "dell-m4-1" },
+ { .id = STAC_DELL_M4_2, .name = "dell-m4-2" },
+ { .id = STAC_DELL_M4_3, .name = "dell-m4-3" },
+ { .id = STAC_HP_M4, .name = "hp-m4" },
+ { .id = STAC_HP_DV4, .name = "hp-dv4" },
+ { .id = STAC_HP_DV5, .name = "hp-dv5" },
+ { .id = STAC_HP_HDX, .name = "hp-hdx" },
+ { .id = STAC_HP_DV4, .name = "hp-dv4-1222nr" },
+ {}
+};
+
+static const struct hda_quirk stac92hd71bxx_fixup_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+ "DFI LanParty", STAC_92HD71BXX_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+ "DFI LanParty", STAC_92HD71BXX_REF),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x1720,
+ "HP", STAC_HP_DV5),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080,
+ "HP", STAC_HP_DV5),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0,
+ "HP dv4-7", STAC_HP_DV4),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3600,
+ "HP dv4-7", STAC_HP_DV5),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3610,
+ "HP HDX", STAC_HP_HDX), /* HDX18 */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a,
+ "HP mini 1000", STAC_HP_M4),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361b,
+ "HP HDX", STAC_HP_HDX), /* HDX16 */
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3620,
+ "HP dv6", STAC_HP_DV5),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3061,
+ "HP dv6", STAC_HP_DV5), /* HP dv6-1110ax */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x363e,
+ "HP DV6", STAC_HP_DV5),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010,
+ "HP", STAC_HP_DV5),
+ SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_HP, "HP", STAC_92HD71BXX_HP),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0250,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024f,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024d,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0251,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0277,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0263,
+ "unknown Dell", STAC_DELL_M4_2),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0265,
+ "unknown Dell", STAC_DELL_M4_2),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0262,
+ "unknown Dell", STAC_DELL_M4_2),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0264,
+ "unknown Dell", STAC_DELL_M4_2),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02aa,
+ "unknown Dell", STAC_DELL_M4_3),
+ {} /* terminator */
+};
+
+static const struct hda_pintbl ref922x_pin_configs[] = {
+ { 0x0a, 0x01014010 },
+ { 0x0b, 0x01016011 },
+ { 0x0c, 0x01012012 },
+ { 0x0d, 0x0221401f },
+ { 0x0e, 0x01813122 },
+ { 0x0f, 0x01011014 },
+ { 0x10, 0x01441030 },
+ { 0x11, 0x01c41030 },
+ { 0x15, 0x40000100 },
+ { 0x1b, 0x40000100 },
+ {}
+};
+
+/*
+ STAC 922X pin configs for
+ 102801A7
+ 102801AB
+ 102801A9
+ 102801D1
+ 102801D2
+*/
+static const struct hda_pintbl dell_922x_d81_pin_configs[] = {
+ { 0x0a, 0x02214030 },
+ { 0x0b, 0x01a19021 },
+ { 0x0c, 0x01111012 },
+ { 0x0d, 0x01114010 },
+ { 0x0e, 0x02a19020 },
+ { 0x0f, 0x01117011 },
+ { 0x10, 0x400001f0 },
+ { 0x11, 0x400001f1 },
+ { 0x15, 0x01813122 },
+ { 0x1b, 0x400001f2 },
+ {}
+};
+
+/*
+ STAC 922X pin configs for
+ 102801AC
+ 102801D0
+*/
+static const struct hda_pintbl dell_922x_d82_pin_configs[] = {
+ { 0x0a, 0x02214030 },
+ { 0x0b, 0x01a19021 },
+ { 0x0c, 0x01111012 },
+ { 0x0d, 0x01114010 },
+ { 0x0e, 0x02a19020 },
+ { 0x0f, 0x01117011 },
+ { 0x10, 0x01451140 },
+ { 0x11, 0x400001f0 },
+ { 0x15, 0x01813122 },
+ { 0x1b, 0x400001f1 },
+ {}
+};
+
+/*
+ STAC 922X pin configs for
+ 102801BF
+*/
+static const struct hda_pintbl dell_922x_m81_pin_configs[] = {
+ { 0x0a, 0x0321101f },
+ { 0x0b, 0x01112024 },
+ { 0x0c, 0x01111222 },
+ { 0x0d, 0x91174220 },
+ { 0x0e, 0x03a11050 },
+ { 0x0f, 0x01116221 },
+ { 0x10, 0x90a70330 },
+ { 0x11, 0x01452340 },
+ { 0x15, 0x40C003f1 },
+ { 0x1b, 0x405003f0 },
+ {}
+};
+
+/*
+ STAC 9221 A1 pin configs for
+ 102801D7 (Dell XPS M1210)
+*/
+static const struct hda_pintbl dell_922x_m82_pin_configs[] = {
+ { 0x0a, 0x02211211 },
+ { 0x0b, 0x408103ff },
+ { 0x0c, 0x02a1123e },
+ { 0x0d, 0x90100310 },
+ { 0x0e, 0x408003f1 },
+ { 0x0f, 0x0221121f },
+ { 0x10, 0x03451340 },
+ { 0x11, 0x40c003f2 },
+ { 0x15, 0x508003f3 },
+ { 0x1b, 0x405003f4 },
+ {}
+};
+
+static const struct hda_pintbl d945gtp3_pin_configs[] = {
+ { 0x0a, 0x0221401f },
+ { 0x0b, 0x01a19022 },
+ { 0x0c, 0x01813021 },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x40000100 },
+ { 0x0f, 0x40000100 },
+ { 0x10, 0x40000100 },
+ { 0x11, 0x40000100 },
+ { 0x15, 0x02a19120 },
+ { 0x1b, 0x40000100 },
+ {}
+};
+
+static const struct hda_pintbl d945gtp5_pin_configs[] = {
+ { 0x0a, 0x0221401f },
+ { 0x0b, 0x01011012 },
+ { 0x0c, 0x01813024 },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x01a19021 },
+ { 0x0f, 0x01016011 },
+ { 0x10, 0x01452130 },
+ { 0x11, 0x40000100 },
+ { 0x15, 0x02a19320 },
+ { 0x1b, 0x40000100 },
+ {}
+};
+
+static const struct hda_pintbl intel_mac_v1_pin_configs[] = {
+ { 0x0a, 0x0121e21f },
+ { 0x0b, 0x400000ff },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x400000fd },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0181e020 },
+ { 0x10, 0x1145e030 },
+ { 0x11, 0x11c5e240 },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
+};
+
+static const struct hda_pintbl intel_mac_v2_pin_configs[] = {
+ { 0x0a, 0x0121e21f },
+ { 0x0b, 0x90a7012e },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x400000fd },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0181e020 },
+ { 0x10, 0x1145e230 },
+ { 0x11, 0x500000fa },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
+};
+
+static const struct hda_pintbl intel_mac_v3_pin_configs[] = {
+ { 0x0a, 0x0121e21f },
+ { 0x0b, 0x90a7012e },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x400000fd },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0181e020 },
+ { 0x10, 0x1145e230 },
+ { 0x11, 0x11c5e240 },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
+};
+
+static const struct hda_pintbl intel_mac_v4_pin_configs[] = {
+ { 0x0a, 0x0321e21f },
+ { 0x0b, 0x03a1e02e },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x9017e11f },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0381e020 },
+ { 0x10, 0x1345e230 },
+ { 0x11, 0x13c5e240 },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
+};
+
+static const struct hda_pintbl intel_mac_v5_pin_configs[] = {
+ { 0x0a, 0x0321e21f },
+ { 0x0b, 0x03a1e02e },
+ { 0x0c, 0x9017e110 },
+ { 0x0d, 0x9017e11f },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x0381e020 },
+ { 0x10, 0x1345e230 },
+ { 0x11, 0x13c5e240 },
+ { 0x15, 0x400000fc },
+ { 0x1b, 0x400000fb },
+ {}
+};
+
+static const struct hda_pintbl ecs202_pin_configs[] = {
+ { 0x0a, 0x0221401f },
+ { 0x0b, 0x02a19020 },
+ { 0x0c, 0x01a19020 },
+ { 0x0d, 0x01114010 },
+ { 0x0e, 0x408000f0 },
+ { 0x0f, 0x01813022 },
+ { 0x10, 0x074510a0 },
+ { 0x11, 0x40c400f1 },
+ { 0x15, 0x9037012e },
+ { 0x1b, 0x40e000f2 },
+ {}
+};
+
+/* codec SSIDs for Intel Mac sharing the same PCI SSID 8384:7680 */
+static const struct hda_quirk stac922x_intel_mac_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x0000, 0x0100, "Mac Mini", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x0800, "Mac", STAC_INTEL_MAC_V1),
+ SND_PCI_QUIRK(0x106b, 0x0600, "Mac", STAC_INTEL_MAC_V2),
+ SND_PCI_QUIRK(0x106b, 0x0700, "Mac", STAC_INTEL_MAC_V2),
+ SND_PCI_QUIRK(0x106b, 0x0e00, "Mac", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x0f00, "Mac", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x1600, "Mac", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x1700, "Mac", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x0200, "Mac", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x1e00, "Mac", STAC_INTEL_MAC_V3),
+ SND_PCI_QUIRK(0x106b, 0x1a00, "Mac", STAC_INTEL_MAC_V4),
+ SND_PCI_QUIRK(0x106b, 0x0a00, "Mac", STAC_INTEL_MAC_V5),
+ SND_PCI_QUIRK(0x106b, 0x2200, "Mac", STAC_INTEL_MAC_V5),
+ {}
+};
+
+static const struct hda_fixup stac922x_fixups[];
+
+/* remap the fixup from codec SSID and apply it */
+static void stac922x_fixup_intel_mac_auto(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ codec->fixup_id = HDA_FIXUP_ID_NOT_SET;
+ snd_hda_pick_fixup(codec, NULL, stac922x_intel_mac_fixup_tbl,
+ stac922x_fixups);
+ if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET)
+ snd_hda_apply_fixup(codec, action);
+}
+
+static void stac922x_fixup_intel_mac_gpio(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gpio_mask = spec->gpio_dir = 0x03;
+ spec->gpio_data = 0x03;
+ }
+}
+
+static const struct hda_fixup stac922x_fixups[] = {
+ [STAC_D945_REF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = ref922x_pin_configs,
+ },
+ [STAC_D945GTP3] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = d945gtp3_pin_configs,
+ },
+ [STAC_D945GTP5] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = d945gtp5_pin_configs,
+ },
+ [STAC_INTEL_MAC_AUTO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac922x_fixup_intel_mac_auto,
+ },
+ [STAC_INTEL_MAC_V1] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = intel_mac_v1_pin_configs,
+ .chained = true,
+ .chain_id = STAC_922X_INTEL_MAC_GPIO,
+ },
+ [STAC_INTEL_MAC_V2] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = intel_mac_v2_pin_configs,
+ .chained = true,
+ .chain_id = STAC_922X_INTEL_MAC_GPIO,
+ },
+ [STAC_INTEL_MAC_V3] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = intel_mac_v3_pin_configs,
+ .chained = true,
+ .chain_id = STAC_922X_INTEL_MAC_GPIO,
+ },
+ [STAC_INTEL_MAC_V4] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = intel_mac_v4_pin_configs,
+ .chained = true,
+ .chain_id = STAC_922X_INTEL_MAC_GPIO,
+ },
+ [STAC_INTEL_MAC_V5] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = intel_mac_v5_pin_configs,
+ .chained = true,
+ .chain_id = STAC_922X_INTEL_MAC_GPIO,
+ },
+ [STAC_922X_INTEL_MAC_GPIO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac922x_fixup_intel_mac_gpio,
+ },
+ [STAC_ECS_202] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = ecs202_pin_configs,
+ },
+ [STAC_922X_DELL_D81] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_922x_d81_pin_configs,
+ },
+ [STAC_922X_DELL_D82] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_922x_d82_pin_configs,
+ },
+ [STAC_922X_DELL_M81] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_922x_m81_pin_configs,
+ },
+ [STAC_922X_DELL_M82] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_922x_m82_pin_configs,
+ },
+};
+
+static const struct hda_model_fixup stac922x_models[] = {
+ { .id = STAC_D945_REF, .name = "ref" },
+ { .id = STAC_D945GTP5, .name = "5stack" },
+ { .id = STAC_D945GTP3, .name = "3stack" },
+ { .id = STAC_INTEL_MAC_V1, .name = "intel-mac-v1" },
+ { .id = STAC_INTEL_MAC_V2, .name = "intel-mac-v2" },
+ { .id = STAC_INTEL_MAC_V3, .name = "intel-mac-v3" },
+ { .id = STAC_INTEL_MAC_V4, .name = "intel-mac-v4" },
+ { .id = STAC_INTEL_MAC_V5, .name = "intel-mac-v5" },
+ { .id = STAC_INTEL_MAC_AUTO, .name = "intel-mac-auto" },
+ { .id = STAC_ECS_202, .name = "ecs202" },
+ { .id = STAC_922X_DELL_D81, .name = "dell-d81" },
+ { .id = STAC_922X_DELL_D82, .name = "dell-d82" },
+ { .id = STAC_922X_DELL_M81, .name = "dell-m81" },
+ { .id = STAC_922X_DELL_M82, .name = "dell-m82" },
+ /* for backward compatibility */
+ { .id = STAC_INTEL_MAC_V3, .name = "macmini" },
+ { .id = STAC_INTEL_MAC_V5, .name = "macbook" },
+ { .id = STAC_INTEL_MAC_V3, .name = "macbook-pro-v1" },
+ { .id = STAC_INTEL_MAC_V3, .name = "macbook-pro" },
+ { .id = STAC_INTEL_MAC_V2, .name = "imac-intel" },
+ { .id = STAC_INTEL_MAC_V3, .name = "imac-intel-20" },
+ {}
+};
+
+static const struct hda_quirk stac922x_fixup_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+ "DFI LanParty", STAC_D945_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+ "DFI LanParty", STAC_D945_REF),
+ /* Intel 945G based systems */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0101,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0202,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0606,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0601,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0111,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1115,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1116,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1117,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1118,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1119,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x8826,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5049,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5055,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5048,
+ "Intel D945G", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0110,
+ "Intel D945G", STAC_D945GTP3),
+ /* Intel D945G 5-stack systems */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0404,
+ "Intel D945G", STAC_D945GTP5),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0303,
+ "Intel D945G", STAC_D945GTP5),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0013,
+ "Intel D945G", STAC_D945GTP5),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0417,
+ "Intel D945G", STAC_D945GTP5),
+ /* Intel 945P based systems */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0b0b,
+ "Intel D945P", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0112,
+ "Intel D945P", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0d0d,
+ "Intel D945P", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0909,
+ "Intel D945P", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0505,
+ "Intel D945P", STAC_D945GTP3),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0707,
+ "Intel D945P", STAC_D945GTP5),
+ /* other intel */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0204,
+ "Intel D945", STAC_D945_REF),
+ /* other systems */
+
+ /* Apple Intel Mac (Mac Mini, MacBook, MacBook Pro...) */
+ SND_PCI_QUIRK(0x8384, 0x7680, "Mac", STAC_INTEL_MAC_AUTO),
+
+ /* Dell systems */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a7,
+ "unknown Dell", STAC_922X_DELL_D81),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a9,
+ "unknown Dell", STAC_922X_DELL_D81),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ab,
+ "unknown Dell", STAC_922X_DELL_D81),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ac,
+ "unknown Dell", STAC_922X_DELL_D82),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01bf,
+ "unknown Dell", STAC_922X_DELL_M81),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d0,
+ "unknown Dell", STAC_922X_DELL_D82),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d1,
+ "unknown Dell", STAC_922X_DELL_D81),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d2,
+ "unknown Dell", STAC_922X_DELL_D81),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d7,
+ "Dell XPS M1210", STAC_922X_DELL_M82),
+ /* ECS/PC Chips boards */
+ SND_PCI_QUIRK_MASK(0x1019, 0xf000, 0x2000,
+ "ECS/PC chips", STAC_ECS_202),
+ {} /* terminator */
+};
+
+static const struct hda_pintbl ref927x_pin_configs[] = {
+ { 0x0a, 0x02214020 },
+ { 0x0b, 0x02a19080 },
+ { 0x0c, 0x0181304e },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x01a19040 },
+ { 0x0f, 0x01011012 },
+ { 0x10, 0x01016011 },
+ { 0x11, 0x0101201f },
+ { 0x12, 0x183301f0 },
+ { 0x13, 0x18a001f0 },
+ { 0x14, 0x18a001f0 },
+ { 0x21, 0x01442070 },
+ { 0x22, 0x01c42190 },
+ { 0x23, 0x40000100 },
+ {}
+};
+
+static const struct hda_pintbl d965_3st_pin_configs[] = {
+ { 0x0a, 0x0221401f },
+ { 0x0b, 0x02a19120 },
+ { 0x0c, 0x40000100 },
+ { 0x0d, 0x01014011 },
+ { 0x0e, 0x01a19021 },
+ { 0x0f, 0x01813024 },
+ { 0x10, 0x40000100 },
+ { 0x11, 0x40000100 },
+ { 0x12, 0x40000100 },
+ { 0x13, 0x40000100 },
+ { 0x14, 0x40000100 },
+ { 0x21, 0x40000100 },
+ { 0x22, 0x40000100 },
+ { 0x23, 0x40000100 },
+ {}
+};
+
+static const struct hda_pintbl d965_5st_pin_configs[] = {
+ { 0x0a, 0x02214020 },
+ { 0x0b, 0x02a19080 },
+ { 0x0c, 0x0181304e },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x01a19040 },
+ { 0x0f, 0x01011012 },
+ { 0x10, 0x01016011 },
+ { 0x11, 0x40000100 },
+ { 0x12, 0x40000100 },
+ { 0x13, 0x40000100 },
+ { 0x14, 0x40000100 },
+ { 0x21, 0x01442070 },
+ { 0x22, 0x40000100 },
+ { 0x23, 0x40000100 },
+ {}
+};
+
+static const struct hda_pintbl d965_5st_no_fp_pin_configs[] = {
+ { 0x0a, 0x40000100 },
+ { 0x0b, 0x40000100 },
+ { 0x0c, 0x0181304e },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x01a19040 },
+ { 0x0f, 0x01011012 },
+ { 0x10, 0x01016011 },
+ { 0x11, 0x40000100 },
+ { 0x12, 0x40000100 },
+ { 0x13, 0x40000100 },
+ { 0x14, 0x40000100 },
+ { 0x21, 0x01442070 },
+ { 0x22, 0x40000100 },
+ { 0x23, 0x40000100 },
+ {}
+};
+
+static const struct hda_pintbl dell_3st_pin_configs[] = {
+ { 0x0a, 0x02211230 },
+ { 0x0b, 0x02a11220 },
+ { 0x0c, 0x01a19040 },
+ { 0x0d, 0x01114210 },
+ { 0x0e, 0x01111212 },
+ { 0x0f, 0x01116211 },
+ { 0x10, 0x01813050 },
+ { 0x11, 0x01112214 },
+ { 0x12, 0x403003fa },
+ { 0x13, 0x90a60040 },
+ { 0x14, 0x90a60040 },
+ { 0x21, 0x404003fb },
+ { 0x22, 0x40c003fc },
+ { 0x23, 0x40000100 },
+ {}
+};
+
+static void stac927x_fixup_ref_no_jd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /* no jack detecion for ref-no-jd model */
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ codec->no_jack_detect = 1;
+}
+
+static void stac927x_fixup_ref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ snd_hda_apply_pincfgs(codec, ref927x_pin_configs);
+ spec->eapd_mask = spec->gpio_mask = 0;
+ spec->gpio_dir = spec->gpio_data = 0;
+ }
+}
+
+static void stac927x_fixup_dell_dmic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ if (codec->core.subsystem_id != 0x1028022f) {
+ /* GPIO2 High = Enable EAPD */
+ spec->eapd_mask = spec->gpio_mask = 0x04;
+ spec->gpio_dir = spec->gpio_data = 0x04;
+ }
+
+ snd_hda_add_verbs(codec, dell_3st_core_init);
+ spec->volknob_init = 1;
+}
+
+static void stac927x_fixup_volknob(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ snd_hda_add_verbs(codec, stac927x_volknob_core_init);
+ spec->volknob_init = 1;
+ }
+}
+
+static const struct hda_fixup stac927x_fixups[] = {
+ [STAC_D965_REF_NO_JD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac927x_fixup_ref_no_jd,
+ .chained = true,
+ .chain_id = STAC_D965_REF,
+ },
+ [STAC_D965_REF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac927x_fixup_ref,
+ },
+ [STAC_D965_3ST] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = d965_3st_pin_configs,
+ .chained = true,
+ .chain_id = STAC_D965_VERBS,
+ },
+ [STAC_D965_5ST] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = d965_5st_pin_configs,
+ .chained = true,
+ .chain_id = STAC_D965_VERBS,
+ },
+ [STAC_D965_VERBS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = d965_core_init,
+ },
+ [STAC_D965_5ST_NO_FP] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = d965_5st_no_fp_pin_configs,
+ },
+ [STAC_NEMO_DEFAULT] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = nemo_pin_configs,
+ },
+ [STAC_DELL_3ST] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_3st_pin_configs,
+ .chained = true,
+ .chain_id = STAC_927X_DELL_DMIC,
+ },
+ [STAC_DELL_BIOS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* correct the front output jack as a hp out */
+ { 0x0f, 0x0221101f },
+ /* correct the front input jack as a mic */
+ { 0x0e, 0x02a79130 },
+ {}
+ },
+ .chained = true,
+ .chain_id = STAC_927X_DELL_DMIC,
+ },
+ [STAC_DELL_BIOS_AMIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* configure the analog microphone on some laptops */
+ { 0x0c, 0x90a79130 },
+ {}
+ },
+ .chained = true,
+ .chain_id = STAC_DELL_BIOS,
+ },
+ [STAC_DELL_BIOS_SPDIF] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* correct the device field to SPDIF out */
+ { 0x21, 0x01442070 },
+ {}
+ },
+ .chained = true,
+ .chain_id = STAC_DELL_BIOS,
+ },
+ [STAC_927X_DELL_DMIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac927x_fixup_dell_dmic,
+ },
+ [STAC_927X_VOLKNOB] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac927x_fixup_volknob,
+ },
+};
+
+static const struct hda_model_fixup stac927x_models[] = {
+ { .id = STAC_D965_REF_NO_JD, .name = "ref-no-jd" },
+ { .id = STAC_D965_REF, .name = "ref" },
+ { .id = STAC_D965_3ST, .name = "3stack" },
+ { .id = STAC_D965_5ST, .name = "5stack" },
+ { .id = STAC_D965_5ST_NO_FP, .name = "5stack-no-fp" },
+ { .id = STAC_DELL_3ST, .name = "dell-3stack" },
+ { .id = STAC_DELL_BIOS, .name = "dell-bios" },
+ { .id = STAC_NEMO_DEFAULT, .name = "nemo-default" },
+ { .id = STAC_DELL_BIOS_AMIC, .name = "dell-bios-amic" },
+ { .id = STAC_927X_VOLKNOB, .name = "volknob" },
+ {}
+};
+
+static const struct hda_quirk stac927x_fixup_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+ "DFI LanParty", STAC_D965_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+ "DFI LanParty", STAC_D965_REF),
+ /* Intel 946 based systems */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x3d01, "Intel D946", STAC_D965_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xa301, "Intel D946", STAC_D965_3ST),
+ /* 965 based 3 stack systems */
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2100,
+ "Intel D965", STAC_D965_3ST),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2000,
+ "Intel D965", STAC_D965_3ST),
+ /* Dell 3 stack systems */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01dd, "Dell Dimension E520", STAC_DELL_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ed, "Dell ", STAC_DELL_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f4, "Dell ", STAC_DELL_3ST),
+ /* Dell 3 stack systems with verb table in BIOS */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_BIOS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0227, "Dell Vostro 1400 ", STAC_DELL_BIOS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS_SPDIF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell Inspiron 1525", STAC_DELL_BIOS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0242, "Dell ", STAC_DELL_BIOS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0243, "Dell ", STAC_DELL_BIOS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS_SPDIF),
+ /* 965 based 5 stack systems */
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2300,
+ "Intel D965", STAC_D965_5ST),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2500,
+ "Intel D965", STAC_D965_5ST),
+ /* Nemo */
+ SND_PCI_QUIRK(0x1888, 0x1000, "AmigaOne X1000", STAC_NEMO_DEFAULT),
+ /* volume-knob fixes */
+ SND_PCI_QUIRK_VENDOR(0x10cf, "FSC", STAC_927X_VOLKNOB),
+ {} /* terminator */
+};
+
+static const struct hda_pintbl ref9205_pin_configs[] = {
+ { 0x0a, 0x40000100 },
+ { 0x0b, 0x40000100 },
+ { 0x0c, 0x01016011 },
+ { 0x0d, 0x01014010 },
+ { 0x0e, 0x01813122 },
+ { 0x0f, 0x01a19021 },
+ { 0x14, 0x01019020 },
+ { 0x16, 0x40000100 },
+ { 0x17, 0x90a000f0 },
+ { 0x18, 0x90a000f0 },
+ { 0x21, 0x01441030 },
+ { 0x22, 0x01c41030 },
+ {}
+};
+
+/*
+ STAC 9205 pin configs for
+ 102801F1
+ 102801F2
+ 102801FC
+ 102801FD
+ 10280204
+ 1028021F
+ 10280228 (Dell Vostro 1500)
+ 10280229 (Dell Vostro 1700)
+*/
+static const struct hda_pintbl dell_9205_m42_pin_configs[] = {
+ { 0x0a, 0x0321101F },
+ { 0x0b, 0x03A11020 },
+ { 0x0c, 0x400003FA },
+ { 0x0d, 0x90170310 },
+ { 0x0e, 0x400003FB },
+ { 0x0f, 0x400003FC },
+ { 0x14, 0x400003FD },
+ { 0x16, 0x40F000F9 },
+ { 0x17, 0x90A60330 },
+ { 0x18, 0x400003FF },
+ { 0x21, 0x0144131F },
+ { 0x22, 0x40C003FE },
+ {}
+};
+
+/*
+ STAC 9205 pin configs for
+ 102801F9
+ 102801FA
+ 102801FE
+ 102801FF (Dell Precision M4300)
+ 10280206
+ 10280200
+ 10280201
+*/
+static const struct hda_pintbl dell_9205_m43_pin_configs[] = {
+ { 0x0a, 0x0321101f },
+ { 0x0b, 0x03a11020 },
+ { 0x0c, 0x90a70330 },
+ { 0x0d, 0x90170310 },
+ { 0x0e, 0x400000fe },
+ { 0x0f, 0x400000ff },
+ { 0x14, 0x400000fd },
+ { 0x16, 0x40f000f9 },
+ { 0x17, 0x400000fa },
+ { 0x18, 0x400000fc },
+ { 0x21, 0x0144131f },
+ { 0x22, 0x40c003f8 },
+ /* Enable SPDIF in/out */
+ { 0x1f, 0x01441030 },
+ { 0x20, 0x1c410030 },
+ {}
+};
+
+static const struct hda_pintbl dell_9205_m44_pin_configs[] = {
+ { 0x0a, 0x0421101f },
+ { 0x0b, 0x04a11020 },
+ { 0x0c, 0x400003fa },
+ { 0x0d, 0x90170310 },
+ { 0x0e, 0x400003fb },
+ { 0x0f, 0x400003fc },
+ { 0x14, 0x400003fd },
+ { 0x16, 0x400003f9 },
+ { 0x17, 0x90a60330 },
+ { 0x18, 0x400003ff },
+ { 0x21, 0x01441340 },
+ { 0x22, 0x40c003fe },
+ {}
+};
+
+static void stac9205_fixup_ref(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ snd_hda_apply_pincfgs(codec, ref9205_pin_configs);
+ /* SPDIF-In enabled */
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0;
+ }
+}
+
+static void stac9205_fixup_dell_m43(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct hda_jack_callback *jack;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ snd_hda_apply_pincfgs(codec, dell_9205_m43_pin_configs);
+
+ /* Enable unsol response for GPIO4/Dock HP connection */
+ snd_hda_codec_write_cache(codec, codec->core.afg, 0,
+ AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
+ jack = snd_hda_jack_detect_enable_callback(codec, codec->core.afg,
+ stac_vref_event);
+ if (!IS_ERR(jack))
+ jack->private_data = 0x01;
+
+ spec->gpio_dir = 0x0b;
+ spec->eapd_mask = 0x01;
+ spec->gpio_mask = 0x1b;
+ spec->gpio_mute = 0x10;
+ /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute,
+ * GPIO3 Low = DRM
+ */
+ spec->gpio_data = 0x01;
+ }
+}
+
+static void stac9205_fixup_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ spec->eapd_switch = 0;
+}
+
+static const struct hda_fixup stac9205_fixups[] = {
+ [STAC_9205_REF] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac9205_fixup_ref,
+ },
+ [STAC_9205_DELL_M42] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_9205_m42_pin_configs,
+ },
+ [STAC_9205_DELL_M43] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac9205_fixup_dell_m43,
+ },
+ [STAC_9205_DELL_M44] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = dell_9205_m44_pin_configs,
+ },
+ [STAC_9205_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac9205_fixup_eapd,
+ },
+ {}
+};
+
+static const struct hda_model_fixup stac9205_models[] = {
+ { .id = STAC_9205_REF, .name = "ref" },
+ { .id = STAC_9205_DELL_M42, .name = "dell-m42" },
+ { .id = STAC_9205_DELL_M43, .name = "dell-m43" },
+ { .id = STAC_9205_DELL_M44, .name = "dell-m44" },
+ { .id = STAC_9205_EAPD, .name = "eapd" },
+ {}
+};
+
+static const struct hda_quirk stac9205_fixup_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+ "DFI LanParty", STAC_9205_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xfb30,
+ "SigmaTel", STAC_9205_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+ "DFI LanParty", STAC_9205_REF),
+ /* Dell */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1,
+ "unknown Dell", STAC_9205_DELL_M42),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2,
+ "unknown Dell", STAC_9205_DELL_M42),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f8,
+ "Dell Precision", STAC_9205_DELL_M43),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f9,
+ "Dell Precision", STAC_9205_DELL_M43),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fa,
+ "Dell Precision", STAC_9205_DELL_M43),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc,
+ "unknown Dell", STAC_9205_DELL_M42),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd,
+ "unknown Dell", STAC_9205_DELL_M42),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fe,
+ "Dell Precision", STAC_9205_DELL_M43),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ff,
+ "Dell Precision M4300", STAC_9205_DELL_M43),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0204,
+ "unknown Dell", STAC_9205_DELL_M42),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206,
+ "Dell Precision", STAC_9205_DELL_M43),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021b,
+ "Dell Precision", STAC_9205_DELL_M43),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021c,
+ "Dell Precision", STAC_9205_DELL_M43),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f,
+ "Dell Inspiron", STAC_9205_DELL_M44),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228,
+ "Dell Vostro 1500", STAC_9205_DELL_M42),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0229,
+ "Dell Vostro 1700", STAC_9205_DELL_M42),
+ /* Gateway */
+ SND_PCI_QUIRK(0x107b, 0x0560, "Gateway T6834c", STAC_9205_EAPD),
+ SND_PCI_QUIRK(0x107b, 0x0565, "Gateway T1616", STAC_9205_EAPD),
+ {} /* terminator */
+};
+
+static void stac92hd95_fixup_hp_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ if (find_mute_led_cfg(codec, spec->default_polarity))
+ codec_dbg(codec, "mute LED gpio %d polarity %d\n",
+ spec->gpio_led,
+ spec->gpio_led_polarity);
+}
+
+static const struct hda_fixup stac92hd95_fixups[] = {
+ [STAC_92HD95_HP_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = stac92hd95_fixup_hp_led,
+ },
+ [STAC_92HD95_HP_BASS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x1a, 0x795, 0x00}, /* HPF to 100Hz */
+ {}
+ },
+ .chained = true,
+ .chain_id = STAC_92HD95_HP_LED,
+ },
+};
+
+static const struct hda_quirk stac92hd95_fixup_tbl[] = {
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1911, "HP Spectre 13", STAC_92HD95_HP_BASS),
+ {} /* terminator */
+};
+
+static const struct hda_model_fixup stac92hd95_models[] = {
+ { .id = STAC_92HD95_HP_LED, .name = "hp-led" },
+ { .id = STAC_92HD95_HP_BASS, .name = "hp-bass" },
+ {}
+};
+
+
+static int stac_parse_auto_config(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int err;
+ int flags = 0;
+
+ if (spec->headset_jack)
+ flags |= HDA_PINCFG_HEADSET_MIC;
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, flags);
+ if (err < 0)
+ return err;
+
+ /* add hooks */
+ spec->gen.pcm_playback_hook = stac_playback_pcm_hook;
+ spec->gen.pcm_capture_hook = stac_capture_pcm_hook;
+
+ spec->gen.automute_hook = stac_update_outputs;
+
+ if (spec->gpio_led)
+ snd_hda_gen_add_mute_led_cdev(codec, stac_vmaster_hook);
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
+ if (err < 0)
+ return err;
+
+ if (spec->vref_mute_led_nid) {
+ err = snd_hda_gen_fix_pin_power(codec, spec->vref_mute_led_nid);
+ if (err < 0)
+ return err;
+ }
+
+ /* setup analog beep controls */
+ if (spec->anabeep_nid > 0) {
+ err = stac_auto_create_beep_ctls(codec,
+ spec->anabeep_nid);
+ if (err < 0)
+ return err;
+ }
+
+ /* setup digital beep controls and input device */
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+ if (spec->gen.beep_nid) {
+ hda_nid_t nid = spec->gen.beep_nid;
+ unsigned int caps;
+
+ err = stac_auto_create_beep_ctls(codec, nid);
+ if (err < 0)
+ return err;
+ if (codec->beep) {
+ /* IDT/STAC codecs have linear beep tone parameter */
+ codec->beep->linear_tone = spec->linear_tone_beep;
+ /* keep power up while beep is enabled */
+ codec->beep->keep_power_at_enable = 1;
+ /* if no beep switch is available, make its own one */
+ caps = query_amp_caps(codec, nid, HDA_OUTPUT);
+ if (!(caps & AC_AMPCAP_MUTE)) {
+ err = stac_beep_switch_ctl(codec);
+ if (err < 0)
+ return err;
+ }
+ }
+ }
+#endif
+
+ if (spec->aloopback_ctl &&
+ snd_hda_get_bool_hint(codec, "loopback") == 1) {
+ unsigned int wr_verb =
+ spec->aloopback_ctl->private_value >> 16;
+ if (snd_hdac_regmap_add_vendor_verb(&codec->core, wr_verb))
+ return -ENOMEM;
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, spec->aloopback_ctl))
+ return -ENOMEM;
+ }
+
+ if (spec->have_spdif_mux) {
+ err = stac_create_spdif_mux_ctls(codec);
+ if (err < 0)
+ return err;
+ }
+
+ stac_init_power_map(codec);
+
+ return 0;
+}
+
+static int stac_init(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i;
+
+ /* override some hints */
+ stac_store_hints(codec);
+
+ /* set up GPIO */
+ /* turn on EAPD statically when spec->eapd_switch isn't set.
+ * otherwise, unsol event will turn it on/off dynamically
+ */
+ if (!spec->eapd_switch)
+ spec->gpio_data |= spec->eapd_mask;
+ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
+
+ snd_hda_gen_init(codec);
+
+ /* sync the power-map */
+ if (spec->num_pwrs)
+ snd_hda_codec_write(codec, codec->core.afg, 0,
+ AC_VERB_IDT_SET_POWER_MAP,
+ spec->power_map_bits);
+
+ /* power down inactive ADCs */
+ if (spec->powerdown_adcs) {
+ for (i = 0; i < spec->gen.num_all_adcs; i++) {
+ if (spec->active_adcs & (1 << i))
+ continue;
+ snd_hda_codec_write(codec, spec->gen.all_adcs[i], 0,
+ AC_VERB_SET_POWER_STATE,
+ AC_PWRST_D3);
+ }
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_SND_PROC_FS
+static void stac92hd_proc_hook(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ if (nid == codec->core.afg)
+ snd_iprintf(buffer, "Power-Map: 0x%02x\n",
+ snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_IDT_GET_POWER_MAP, 0));
+}
+
+static void analog_loop_proc_hook(struct snd_info_buffer *buffer,
+ struct hda_codec *codec,
+ unsigned int verb)
+{
+ snd_iprintf(buffer, "Analog Loopback: 0x%02x\n",
+ snd_hda_codec_read(codec, codec->core.afg, 0, verb, 0));
+}
+
+/* stac92hd71bxx, stac92hd73xx */
+static void stac92hd7x_proc_hook(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ stac92hd_proc_hook(buffer, codec, nid);
+ if (nid == codec->core.afg)
+ analog_loop_proc_hook(buffer, codec, 0xfa0);
+}
+
+static void stac9205_proc_hook(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ if (nid == codec->core.afg)
+ analog_loop_proc_hook(buffer, codec, 0xfe0);
+}
+
+static void stac927x_proc_hook(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ if (nid == codec->core.afg)
+ analog_loop_proc_hook(buffer, codec, 0xfeb);
+}
+#else
+#define stac92hd_proc_hook NULL
+#define stac92hd7x_proc_hook NULL
+#define stac9205_proc_hook NULL
+#define stac927x_proc_hook NULL
+#endif
+
+static int stac_suspend(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ snd_hda_shutup_pins(codec);
+
+ if (spec->eapd_mask)
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data &
+ ~spec->eapd_mask);
+
+ return 0;
+}
+
+static int alloc_stac_spec(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ snd_hda_gen_spec_init(&spec->gen);
+ codec->spec = spec;
+ codec->no_trigger_sense = 1; /* seems common with STAC/IDT codecs */
+ spec->gen.dac_min_mute = true;
+ return 0;
+}
+
+static int probe_stac9200(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec;
+ int err;
+
+ spec = codec->spec;
+ spec->linear_tone_beep = 1;
+ spec->gen.own_eapd_ctl = 1;
+
+ codec->power_filter = snd_hda_codec_eapd_power_filter;
+
+ snd_hda_add_verbs(codec, stac9200_eapd_init);
+
+ snd_hda_pick_fixup(codec, stac9200_models, stac9200_fixup_tbl,
+ stac9200_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = stac_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+}
+
+static int probe_stac925x(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec;
+ int err;
+
+ spec = codec->spec;
+ spec->linear_tone_beep = 1;
+ spec->gen.own_eapd_ctl = 1;
+
+ snd_hda_add_verbs(codec, stac925x_core_init);
+
+ snd_hda_pick_fixup(codec, stac925x_models, stac925x_fixup_tbl,
+ stac925x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = stac_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+}
+
+static int probe_stac92hd73xx(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec;
+ int err;
+ int num_dacs;
+
+ spec = codec->spec;
+ /* enable power_save_node only for new 92HD89xx chips, as it causes
+ * click noises on old 92HD73xx chips.
+ */
+ if ((codec->core.vendor_id & 0xfffffff0) != 0x111d7670)
+ codec->power_save_node = 1;
+ spec->linear_tone_beep = 0;
+ spec->gen.mixer_nid = 0x1d;
+ spec->have_spdif_mux = 1;
+
+ num_dacs = snd_hda_get_num_conns(codec, 0x0a) - 1;
+ if (num_dacs < 3 || num_dacs > 5) {
+ codec_warn(codec,
+ "Could not determine number of channels defaulting to DAC count\n");
+ num_dacs = 5;
+ }
+
+ switch (num_dacs) {
+ case 0x3: /* 6 Channel */
+ spec->aloopback_ctl = &stac92hd73xx_6ch_loopback;
+ break;
+ case 0x4: /* 8 Channel */
+ spec->aloopback_ctl = &stac92hd73xx_8ch_loopback;
+ break;
+ case 0x5: /* 10 Channel */
+ spec->aloopback_ctl = &stac92hd73xx_10ch_loopback;
+ break;
+ }
+
+ spec->aloopback_mask = 0x01;
+ spec->aloopback_shift = 8;
+
+ spec->gen.beep_nid = 0x1c; /* digital beep */
+
+ /* GPIO0 High = Enable EAPD */
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
+ spec->gpio_data = 0x01;
+
+ spec->eapd_switch = 1;
+
+ spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
+ spec->pwr_nids = stac92hd73xx_pwr_nids;
+
+ spec->gen.own_eapd_ctl = 1;
+ spec->gen.power_down_unused = 1;
+
+ snd_hda_pick_fixup(codec, stac92hd73xx_models, stac92hd73xx_fixup_tbl,
+ stac92hd73xx_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ if (!spec->volknob_init)
+ snd_hda_add_verbs(codec, stac92hd73xx_core_init);
+
+ err = stac_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+
+ /* Don't GPIO-mute speakers if there are no internal speakers, because
+ * the GPIO might be necessary for Headphone
+ */
+ if (spec->eapd_switch && !has_builtin_speaker(codec))
+ spec->eapd_switch = 0;
+
+ codec->proc_widget_hook = stac92hd7x_proc_hook;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+}
+
+static void stac_setup_gpio(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ spec->gpio_mask |= spec->eapd_mask;
+ if (spec->gpio_led) {
+ if (!spec->vref_mute_led_nid) {
+ spec->gpio_mask |= spec->gpio_led;
+ spec->gpio_dir |= spec->gpio_led;
+ spec->gpio_data |= spec->gpio_led;
+ } else {
+ codec->power_filter = stac_vref_led_power_filter;
+ }
+ }
+
+ if (spec->mic_mute_led_gpio) {
+ spec->gpio_mask |= spec->mic_mute_led_gpio;
+ spec->gpio_dir |= spec->mic_mute_led_gpio;
+ spec->mic_enabled = 0;
+ spec->gpio_data |= spec->mic_mute_led_gpio;
+ snd_hda_gen_add_micmute_led_cdev(codec, stac_capture_led_update);
+ }
+}
+
+static int probe_stac92hd83xxx(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec;
+ int err;
+
+ /* longer delay needed for D3 */
+ codec->core.power_caps &= ~AC_PWRST_EPSS;
+
+ spec = codec->spec;
+ codec->power_save_node = 1;
+ spec->linear_tone_beep = 0;
+ spec->gen.own_eapd_ctl = 1;
+ spec->gen.power_down_unused = 1;
+ spec->gen.mixer_nid = 0x1b;
+
+ spec->gen.beep_nid = 0x21; /* digital beep */
+ spec->pwr_nids = stac92hd83xxx_pwr_nids;
+ spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
+ spec->default_polarity = -1; /* no default cfg */
+
+ snd_hda_add_verbs(codec, stac92hd83xxx_core_init);
+
+ snd_hda_pick_fixup(codec, stac92hd83xxx_models, stac92hd83xxx_fixup_tbl,
+ stac92hd83xxx_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ stac_setup_gpio(codec);
+
+ err = stac_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+
+ codec->proc_widget_hook = stac92hd_proc_hook;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+}
+
+static const hda_nid_t stac92hd95_pwr_nids[] = {
+ 0x0a, 0x0b, 0x0c, 0x0d
+};
+
+static int probe_stac92hd95(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec;
+ int err;
+
+ /* longer delay needed for D3 */
+ codec->core.power_caps &= ~AC_PWRST_EPSS;
+
+ spec = codec->spec;
+ codec->power_save_node = 1;
+ spec->linear_tone_beep = 0;
+ spec->gen.own_eapd_ctl = 1;
+ spec->gen.power_down_unused = 1;
+
+ spec->gen.beep_nid = 0x19; /* digital beep */
+ spec->pwr_nids = stac92hd95_pwr_nids;
+ spec->num_pwrs = ARRAY_SIZE(stac92hd95_pwr_nids);
+ spec->default_polarity = 0;
+
+ snd_hda_pick_fixup(codec, stac92hd95_models, stac92hd95_fixup_tbl,
+ stac92hd95_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ stac_setup_gpio(codec);
+
+ err = stac_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+
+ codec->proc_widget_hook = stac92hd_proc_hook;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+}
+
+static int probe_stac92hd71bxx(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec;
+ const hda_nid_t *unmute_nids = stac92hd71bxx_unmute_nids;
+ int err;
+
+ spec = codec->spec;
+ /* disabled power_save_node since it causes noises on a Dell machine */
+ /* codec->power_save_node = 1; */
+ spec->linear_tone_beep = 0;
+ spec->gen.own_eapd_ctl = 1;
+ spec->gen.power_down_unused = 1;
+ spec->gen.mixer_nid = 0x17;
+ spec->have_spdif_mux = 1;
+
+ /* GPIO0 = EAPD */
+ spec->gpio_mask = 0x01;
+ spec->gpio_dir = 0x01;
+ spec->gpio_data = 0x01;
+
+ switch (codec->core.vendor_id) {
+ case 0x111d76b6: /* 4 Port without Analog Mixer */
+ case 0x111d76b7:
+ unmute_nids++;
+ break;
+ case 0x111d7608: /* 5 Port with Analog Mixer */
+ if ((codec->core.revision_id & 0xf) == 0 ||
+ (codec->core.revision_id & 0xf) == 1)
+ spec->stream_delay = 40; /* 40 milliseconds */
+
+ /* disable VSW */
+ unmute_nids++;
+ snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
+ snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
+ break;
+ case 0x111d7603: /* 6 Port with Analog Mixer */
+ if ((codec->core.revision_id & 0xf) == 1)
+ spec->stream_delay = 40; /* 40 milliseconds */
+
+ break;
+ }
+
+ if (get_wcaps_type(get_wcaps(codec, 0x28)) == AC_WID_VOL_KNB)
+ snd_hda_add_verbs(codec, stac92hd71bxx_core_init);
+
+ if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) {
+ const hda_nid_t *p;
+ for (p = unmute_nids; *p; p++)
+ snd_hda_codec_amp_init_stereo(codec, *p, HDA_INPUT, 0,
+ 0xff, 0x00);
+ }
+
+ spec->aloopback_ctl = &stac92hd71bxx_loopback;
+ spec->aloopback_mask = 0x50;
+ spec->aloopback_shift = 0;
+
+ spec->powerdown_adcs = 1;
+ spec->gen.beep_nid = 0x26; /* digital beep */
+ spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
+ spec->pwr_nids = stac92hd71bxx_pwr_nids;
+
+ snd_hda_pick_fixup(codec, stac92hd71bxx_models, stac92hd71bxx_fixup_tbl,
+ stac92hd71bxx_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ stac_setup_gpio(codec);
+
+ err = stac_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+
+ codec->proc_widget_hook = stac92hd7x_proc_hook;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+}
+
+static int probe_stac922x(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec;
+ int err;
+
+ spec = codec->spec;
+ spec->linear_tone_beep = 1;
+ spec->gen.own_eapd_ctl = 1;
+
+ snd_hda_add_verbs(codec, stac922x_core_init);
+
+ /* Fix Mux capture level; max to 2 */
+ snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT,
+ (0 << AC_AMPCAP_OFFSET_SHIFT) |
+ (2 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (0 << AC_AMPCAP_MUTE_SHIFT));
+
+ snd_hda_pick_fixup(codec, stac922x_models, stac922x_fixup_tbl,
+ stac922x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = stac_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+}
+
+static const char * const stac927x_spdif_labels[] = {
+ "Digital Playback", "ADAT", "Analog Mux 1",
+ "Analog Mux 2", "Analog Mux 3", NULL
+};
+
+static int probe_stac927x(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec;
+ int err;
+
+ spec = codec->spec;
+ spec->linear_tone_beep = 1;
+ spec->gen.own_eapd_ctl = 1;
+ spec->have_spdif_mux = 1;
+ spec->spdif_labels = stac927x_spdif_labels;
+
+ spec->gen.beep_nid = 0x23; /* digital beep */
+
+ /* GPIO0 High = Enable EAPD */
+ spec->eapd_mask = spec->gpio_mask = 0x01;
+ spec->gpio_dir = spec->gpio_data = 0x01;
+
+ spec->aloopback_ctl = &stac927x_loopback;
+ spec->aloopback_mask = 0x40;
+ spec->aloopback_shift = 0;
+ spec->eapd_switch = 1;
+
+ snd_hda_pick_fixup(codec, stac927x_models, stac927x_fixup_tbl,
+ stac927x_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ if (!spec->volknob_init)
+ snd_hda_add_verbs(codec, stac927x_core_init);
+
+ err = stac_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+
+ codec->proc_widget_hook = stac927x_proc_hook;
+
+ /*
+ * !!FIXME!!
+ * The STAC927x seem to require fairly long delays for certain
+ * command sequences. With too short delays (even if the answer
+ * is set to RIRB properly), it results in the silence output
+ * on some hardwares like Dell.
+ *
+ * The below flag enables the longer delay (see get_response
+ * in hda_intel.c).
+ */
+ codec->bus->core.needs_damn_long_delay = 1;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+}
+
+static int probe_stac9205(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec;
+ int err;
+
+ spec = codec->spec;
+ spec->linear_tone_beep = 1;
+ spec->gen.own_eapd_ctl = 1;
+ spec->have_spdif_mux = 1;
+
+ spec->gen.beep_nid = 0x23; /* digital beep */
+
+ snd_hda_add_verbs(codec, stac9205_core_init);
+ spec->aloopback_ctl = &stac9205_loopback;
+
+ spec->aloopback_mask = 0x40;
+ spec->aloopback_shift = 0;
+
+ /* GPIO0 High = EAPD */
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
+ spec->gpio_data = 0x01;
+
+ /* Turn on/off EAPD per HP plugging */
+ spec->eapd_switch = 1;
+
+ snd_hda_pick_fixup(codec, stac9205_models, stac9205_fixup_tbl,
+ stac9205_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = stac_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+
+ codec->proc_widget_hook = stac9205_proc_hook;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+}
+
+/*
+ * STAC9872 hack
+ */
+
+static const struct hda_verb stac9872_core_init[] = {
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */
+ {}
+};
+
+static const struct hda_pintbl stac9872_vaio_pin_configs[] = {
+ { 0x0a, 0x03211020 },
+ { 0x0b, 0x411111f0 },
+ { 0x0c, 0x411111f0 },
+ { 0x0d, 0x03a15030 },
+ { 0x0e, 0x411111f0 },
+ { 0x0f, 0x90170110 },
+ { 0x11, 0x411111f0 },
+ { 0x13, 0x411111f0 },
+ { 0x14, 0x90a7013e },
+ {}
+};
+
+static const struct hda_model_fixup stac9872_models[] = {
+ { .id = STAC_9872_VAIO, .name = "vaio" },
+ {}
+};
+
+static const struct hda_fixup stac9872_fixups[] = {
+ [STAC_9872_VAIO] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = stac9872_vaio_pin_configs,
+ },
+};
+
+static const struct hda_quirk stac9872_fixup_tbl[] = {
+ SND_PCI_QUIRK_MASK(0x104d, 0xfff0, 0x81e0,
+ "Sony VAIO F/S", STAC_9872_VAIO),
+ {} /* terminator */
+};
+
+static int probe_stac9872(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec;
+ int err;
+
+ spec = codec->spec;
+ spec->linear_tone_beep = 1;
+ spec->gen.own_eapd_ctl = 1;
+
+ snd_hda_add_verbs(codec, stac9872_core_init);
+
+ snd_hda_pick_fixup(codec, stac9872_models, stac9872_fixup_tbl,
+ stac9872_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ err = stac_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
+ return 0;
+}
+
+/*
+ * common driver probe
+ */
+
+enum {
+ MODEL_STAC9200,
+ MODEL_STAC9205,
+ MODEL_STAC922X,
+ MODEL_STAC925X,
+ MODEL_STAC927X,
+ MODEL_STAC9872,
+ MODEL_STAC92HD71BXX,
+ MODEL_STAC92HD73XX,
+ MODEL_STAC92HD83XXX,
+ MODEL_STAC92HD95,
+};
+
+static int stac_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ int err;
+
+ err = alloc_stac_spec(codec);
+ if (err < 0)
+ return err;
+
+ switch (id->driver_data) {
+ case MODEL_STAC9200:
+ err = probe_stac9200(codec);
+ break;
+ case MODEL_STAC9205:
+ err = probe_stac9205(codec);
+ break;
+ case MODEL_STAC922X:
+ err = probe_stac922x(codec);
+ break;
+ case MODEL_STAC925X:
+ err = probe_stac925x(codec);
+ break;
+ case MODEL_STAC927X:
+ err = probe_stac927x(codec);
+ break;
+ case MODEL_STAC9872:
+ err = probe_stac9872(codec);
+ break;
+ case MODEL_STAC92HD71BXX:
+ err = probe_stac92hd71bxx(codec);
+ break;
+ case MODEL_STAC92HD73XX:
+ err = probe_stac92hd73xx(codec);
+ break;
+ case MODEL_STAC92HD83XXX:
+ err = probe_stac92hd83xxx(codec);
+ break;
+ case MODEL_STAC92HD95:
+ err = probe_stac92hd95(codec);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ if (err < 0) {
+ snd_hda_gen_remove(codec);
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct hda_codec_ops stac_codec_ops = {
+ .probe = stac_probe,
+ .remove = snd_hda_gen_remove,
+ .build_controls = snd_hda_gen_build_controls,
+ .build_pcms = snd_hda_gen_build_pcms,
+ .init = stac_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = stac_suspend,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_sigmatel[] = {
+ HDA_CODEC_ID_MODEL(0x83847690, "STAC9200", MODEL_STAC9200),
+ HDA_CODEC_ID_MODEL(0x83847882, "STAC9220 A1", MODEL_STAC922X),
+ HDA_CODEC_ID_MODEL(0x83847680, "STAC9221 A1", MODEL_STAC922X),
+ HDA_CODEC_ID_MODEL(0x83847880, "STAC9220 A2", MODEL_STAC922X),
+ HDA_CODEC_ID_MODEL(0x83847681, "STAC9220D/9223D A2", MODEL_STAC922X),
+ HDA_CODEC_ID_MODEL(0x83847682, "STAC9221 A2", MODEL_STAC922X),
+ HDA_CODEC_ID_MODEL(0x83847683, "STAC9221D A2", MODEL_STAC922X),
+ HDA_CODEC_ID_MODEL(0x83847618, "STAC9227", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847619, "STAC9227", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847638, "STAC92HD700", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847616, "STAC9228", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847617, "STAC9228", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847614, "STAC9229", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847615, "STAC9229", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847620, "STAC9274", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847621, "STAC9274D", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847622, "STAC9273X", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847623, "STAC9273D", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847624, "STAC9272X", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847625, "STAC9272D", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847626, "STAC9271X", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847627, "STAC9271D", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847628, "STAC9274X5NH", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847629, "STAC9274D5NH", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847632, "STAC9202", MODEL_STAC925X),
+ HDA_CODEC_ID_MODEL(0x83847633, "STAC9202D", MODEL_STAC925X),
+ HDA_CODEC_ID_MODEL(0x83847634, "STAC9250", MODEL_STAC925X),
+ HDA_CODEC_ID_MODEL(0x83847635, "STAC9250D", MODEL_STAC925X),
+ HDA_CODEC_ID_MODEL(0x83847636, "STAC9251", MODEL_STAC925X),
+ HDA_CODEC_ID_MODEL(0x83847637, "STAC9250D", MODEL_STAC925X),
+ HDA_CODEC_ID_MODEL(0x83847645, "92HD206X", MODEL_STAC927X),
+ HDA_CODEC_ID_MODEL(0x83847646, "92HD206D", MODEL_STAC927X),
+ /* The following does not take into account .id=0x83847661 when subsys =
+ * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
+ * currently not fully supported.
+ */
+ HDA_CODEC_ID_MODEL(0x83847661, "CXD9872RD/K", MODEL_STAC9872),
+ HDA_CODEC_ID_MODEL(0x83847662, "STAC9872AK", MODEL_STAC9872),
+ HDA_CODEC_ID_MODEL(0x83847664, "CXD9872AKD", MODEL_STAC9872),
+ HDA_CODEC_ID_MODEL(0x83847698, "STAC9205", MODEL_STAC9205),
+ HDA_CODEC_ID_MODEL(0x838476a0, "STAC9205", MODEL_STAC9205),
+ HDA_CODEC_ID_MODEL(0x838476a1, "STAC9205D", MODEL_STAC9205),
+ HDA_CODEC_ID_MODEL(0x838476a2, "STAC9204", MODEL_STAC9205),
+ HDA_CODEC_ID_MODEL(0x838476a3, "STAC9204D", MODEL_STAC9205),
+ HDA_CODEC_ID_MODEL(0x838476a4, "STAC9255", MODEL_STAC9205),
+ HDA_CODEC_ID_MODEL(0x838476a5, "STAC9255D", MODEL_STAC9205),
+ HDA_CODEC_ID_MODEL(0x838476a6, "STAC9254", MODEL_STAC9205),
+ HDA_CODEC_ID_MODEL(0x838476a7, "STAC9254D", MODEL_STAC9205),
+ HDA_CODEC_ID_MODEL(0x111d7603, "92HD75B3X5", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d7604, "92HD83C1X5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76d4, "92HD83C1C5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d7605, "92HD81B1X5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76d5, "92HD81B1C5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76d1, "92HD87B1/3", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76d9, "92HD87B2/4", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d7666, "92HD88B3", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d7667, "92HD88B1", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d7668, "92HD88B2", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d7669, "92HD88B4", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d7608, "92HD75B2X5", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d7674, "92HD73D1X5", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d7675, "92HD73C1X5", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d7676, "92HD73E1X5", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d7695, "92HD95", MODEL_STAC92HD95),
+ HDA_CODEC_ID_MODEL(0x111d76b0, "92HD71B8X", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d76b1, "92HD71B8X", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d76b2, "92HD71B7X", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d76b3, "92HD71B7X", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d76b4, "92HD71B6X", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d76b5, "92HD71B6X", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d76b6, "92HD71B5X", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d76b7, "92HD71B5X", MODEL_STAC92HD71BXX),
+ HDA_CODEC_ID_MODEL(0x111d76c0, "92HD89C3", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76c1, "92HD89C2", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76c2, "92HD89C1", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76c3, "92HD89B3", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76c4, "92HD89B2", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76c5, "92HD89B1", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76c6, "92HD89E3", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76c7, "92HD89E2", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76c8, "92HD89E1", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76c9, "92HD89D3", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76ca, "92HD89D2", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76cb, "92HD89D1", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76cc, "92HD89F3", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76cd, "92HD89F2", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76ce, "92HD89F1", MODEL_STAC92HD73XX),
+ HDA_CODEC_ID_MODEL(0x111d76df, "92HD93BXX", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76e0, "92HD91BXX", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76e3, "92HD98BXX", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76e5, "92HD99BXX", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76e7, "92HD90BXX", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76e8, "92HD66B1X5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76e9, "92HD66B2X5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76ea, "92HD66B3X5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76eb, "92HD66C1X5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76ec, "92HD66C2X5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76ed, "92HD66C3X5", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76ee, "92HD66B1X3", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76ef, "92HD66B2X3", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76f0, "92HD66B3X3", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76f1, "92HD66C1X3", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76f2, "92HD66C2X3", MODEL_STAC92HD83XXX),
+ HDA_CODEC_ID_MODEL(0x111d76f3, "92HD66C3/65", MODEL_STAC92HD83XXX),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_sigmatel);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec");
+
+static struct hda_codec_driver sigmatel_driver = {
+ .id = snd_hda_id_sigmatel,
+ .ops = &stac_codec_ops,
+};
+
+module_hda_codec_driver(sigmatel_driver);
diff --git a/sound/hda/codecs/via.c b/sound/hda/codecs/via.c
new file mode 100644
index 000000000000..6becea9bb810
--- /dev/null
+++ b/sound/hda/codecs/via.c
@@ -0,0 +1,1174 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * HD audio codec driver for VIA VT17xx/VT18xx/VT20xx codec
+ *
+ * (C) 2006-2009 VIA Technology, Inc.
+ * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
+ */
+
+/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
+/* */
+/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
+/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
+/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
+/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
+/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
+/* 2007-09-17 Lydia Wang Add VT1708B codec support */
+/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
+/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
+/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
+/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
+/* 2008-04-09 Lydia Wang Add Independent HP feature */
+/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
+/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
+/* 2009-02-16 Logan Li Add support for VT1718S */
+/* 2009-03-13 Logan Li Add support for VT1716S */
+/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
+/* 2009-07-08 Lydia Wang Add support for VT2002P */
+/* 2009-07-21 Lydia Wang Add support for VT1812 */
+/* 2009-09-19 Lydia Wang Add support for VT1818S */
+/* */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/asoundef.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+#include "generic.h"
+
+/* Pin Widget NID */
+#define VT1708_HP_PIN_NID 0x20
+#define VT1708_CD_PIN_NID 0x24
+
+enum VIA_HDA_CODEC {
+ UNKNOWN = -1,
+ VT1708,
+ VT1709,
+ VT1709_10CH,
+ VT1709_6CH,
+ VT1708B,
+ VT1708B_8CH,
+ VT1708B_4CH,
+ VT1708S,
+ VT1708BCE,
+ VT1702,
+ VT1718S,
+ VT1716S,
+ VT2002P,
+ VT1812,
+ VT1802,
+ VT1705CF,
+ VT1808,
+ VT3476,
+ CODEC_TYPES,
+};
+
+#define VT2002P_COMPATIBLE(spec) \
+ ((spec)->codec_type == VT2002P ||\
+ (spec)->codec_type == VT1812 ||\
+ (spec)->codec_type == VT1802)
+
+struct via_spec {
+ struct hda_gen_spec gen;
+
+ /* HP mode source */
+ unsigned int dmic_enabled;
+ enum VIA_HDA_CODEC codec_type;
+
+ /* analog low-power control */
+ bool alc_mode;
+
+ /* work to check hp jack state */
+ int hp_work_active;
+ int vt1708_jack_detect;
+};
+
+static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
+static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action);
+
+static struct via_spec *via_new_spec(struct hda_codec *codec)
+{
+ struct via_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return NULL;
+
+ codec->spec = spec;
+ snd_hda_gen_spec_init(&spec->gen);
+ spec->codec_type = get_codec_type(codec);
+ /* VT1708BCE & VT1708S are almost same */
+ if (spec->codec_type == VT1708BCE)
+ spec->codec_type = VT1708S;
+ spec->gen.indep_hp = 1;
+ spec->gen.keep_eapd_on = 1;
+ spec->gen.dac_min_mute = 1;
+ spec->gen.pcm_playback_hook = via_playback_pcm_hook;
+ spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
+ codec->power_save_node = 1;
+ spec->gen.power_down_unused = 1;
+ return spec;
+}
+
+static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
+{
+ u32 vendor_id = codec->core.vendor_id;
+ u16 ven_id = vendor_id >> 16;
+ u16 dev_id = vendor_id & 0xffff;
+ enum VIA_HDA_CODEC codec_type;
+
+ /* get codec type */
+ if (ven_id != 0x1106)
+ codec_type = UNKNOWN;
+ else if (dev_id >= 0x1708 && dev_id <= 0x170b)
+ codec_type = VT1708;
+ else if (dev_id >= 0xe710 && dev_id <= 0xe713)
+ codec_type = VT1709_10CH;
+ else if (dev_id >= 0xe714 && dev_id <= 0xe717)
+ codec_type = VT1709_6CH;
+ else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
+ codec_type = VT1708B_8CH;
+ if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
+ codec_type = VT1708BCE;
+ } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
+ codec_type = VT1708B_4CH;
+ else if ((dev_id & 0xfff) == 0x397
+ && (dev_id >> 12) < 8)
+ codec_type = VT1708S;
+ else if ((dev_id & 0xfff) == 0x398
+ && (dev_id >> 12) < 8)
+ codec_type = VT1702;
+ else if ((dev_id & 0xfff) == 0x428
+ && (dev_id >> 12) < 8)
+ codec_type = VT1718S;
+ else if (dev_id == 0x0433 || dev_id == 0xa721)
+ codec_type = VT1716S;
+ else if (dev_id == 0x0441 || dev_id == 0x4441)
+ codec_type = VT1718S;
+ else if (dev_id == 0x0438 || dev_id == 0x4438)
+ codec_type = VT2002P;
+ else if (dev_id == 0x0448)
+ codec_type = VT1812;
+ else if (dev_id == 0x0440)
+ codec_type = VT1708S;
+ else if ((dev_id & 0xfff) == 0x446)
+ codec_type = VT1802;
+ else if (dev_id == 0x4760)
+ codec_type = VT1705CF;
+ else if (dev_id == 0x4761 || dev_id == 0x4762)
+ codec_type = VT1808;
+ else
+ codec_type = UNKNOWN;
+ return codec_type;
+};
+
+static void analog_low_current_mode(struct hda_codec *codec);
+static bool is_aa_path_mute(struct hda_codec *codec);
+
+#define hp_detect_with_aa(codec) \
+ (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
+ !is_aa_path_mute(codec))
+
+static void vt1708_stop_hp_work(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
+ return;
+ if (spec->hp_work_active) {
+ snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
+ codec->jackpoll_interval = 0;
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+ spec->hp_work_active = false;
+ }
+}
+
+static void vt1708_update_hp_work(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
+ return;
+ if (spec->vt1708_jack_detect) {
+ if (!spec->hp_work_active) {
+ codec->jackpoll_interval = msecs_to_jiffies(100);
+ snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0);
+ schedule_delayed_work(&codec->jackpoll_work, 0);
+ spec->hp_work_active = true;
+ }
+ } else if (!hp_detect_with_aa(codec))
+ vt1708_stop_hp_work(codec);
+}
+
+static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
+}
+
+static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->gen.power_down_unused;
+ return 0;
+}
+
+static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ bool val = !!ucontrol->value.enumerated.item[0];
+
+ if (val == spec->gen.power_down_unused)
+ return 0;
+ /* codec->power_save_node = val; */ /* widget PM seems yet broken */
+ spec->gen.power_down_unused = val;
+ analog_low_current_mode(codec);
+ return 1;
+}
+
+static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Dynamic Power-Control",
+ .info = via_pin_power_ctl_info,
+ .get = via_pin_power_ctl_get,
+ .put = via_pin_power_ctl_put,
+};
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/* additional beep mixers; the actual parameters are overwritten at build */
+static const struct snd_kcontrol_new via_beep_mixer[] = {
+ HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
+};
+
+static int set_beep_amp(struct via_spec *spec, hda_nid_t nid,
+ int idx, int dir)
+{
+ struct snd_kcontrol_new *knew;
+ unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
+ int i;
+
+ spec->gen.beep_nid = nid;
+ for (i = 0; i < ARRAY_SIZE(via_beep_mixer); i++) {
+ knew = snd_hda_gen_add_kctl(&spec->gen, NULL,
+ &via_beep_mixer[i]);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = beep_amp;
+ }
+ return 0;
+}
+
+static int auto_parse_beep(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ hda_nid_t nid;
+
+ for_each_hda_codec_node(nid, codec)
+ if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP)
+ return set_beep_amp(spec, nid, 0, HDA_OUTPUT);
+ return 0;
+}
+#else
+#define auto_parse_beep(codec) 0
+#endif
+
+/* check AA path's mute status */
+static bool is_aa_path_mute(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ const struct hda_amp_list *p;
+ int ch, v;
+
+ p = spec->gen.loopback.amplist;
+ if (!p)
+ return true;
+ for (; p->nid; p++) {
+ for (ch = 0; ch < 2; ch++) {
+ v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
+ p->idx);
+ if (!(v & HDA_AMP_MUTE) && v > 0)
+ return false;
+ }
+ }
+ return true;
+}
+
+/* enter/exit analog low-current mode */
+static void __analog_low_current_mode(struct hda_codec *codec, bool force)
+{
+ struct via_spec *spec = codec->spec;
+ bool enable;
+ unsigned int verb, parm;
+
+ if (!codec->power_save_node)
+ enable = false;
+ else
+ enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
+ if (enable == spec->alc_mode && !force)
+ return;
+ spec->alc_mode = enable;
+
+ /* decide low current mode's verb & parameter */
+ switch (spec->codec_type) {
+ case VT1708B_8CH:
+ case VT1708B_4CH:
+ verb = 0xf70;
+ parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
+ break;
+ case VT1708S:
+ case VT1718S:
+ case VT1716S:
+ verb = 0xf73;
+ parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
+ break;
+ case VT1702:
+ verb = 0xf73;
+ parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
+ break;
+ case VT2002P:
+ case VT1812:
+ case VT1802:
+ verb = 0xf93;
+ parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
+ break;
+ case VT1705CF:
+ case VT1808:
+ verb = 0xf82;
+ parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
+ break;
+ default:
+ return; /* other codecs are not supported */
+ }
+ /* send verb */
+ snd_hda_codec_write(codec, codec->core.afg, 0, verb, parm);
+}
+
+static void analog_low_current_mode(struct hda_codec *codec)
+{
+ return __analog_low_current_mode(codec, false);
+}
+
+static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream,
+ int action)
+{
+ analog_low_current_mode(codec);
+ vt1708_update_hp_work(codec);
+}
+
+static void via_remove(struct hda_codec *codec)
+{
+ vt1708_stop_hp_work(codec);
+ snd_hda_gen_remove(codec);
+}
+
+static int via_suspend(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ vt1708_stop_hp_work(codec);
+
+ /* Fix pop noise on headphones */
+ if (spec->codec_type == VT1802)
+ snd_hda_shutup_pins(codec);
+
+ return 0;
+}
+
+static int via_resume(struct hda_codec *codec)
+{
+ /* some delay here to make jack detection working (bko#98921) */
+ msleep(10);
+ snd_hda_codec_init(codec);
+ snd_hda_regmap_sync(codec);
+ return 0;
+}
+
+static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct via_spec *spec = codec->spec;
+ analog_low_current_mode(codec);
+ vt1708_update_hp_work(codec);
+ return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
+}
+
+/*
+ */
+
+static const struct hda_verb vt1708_init_verbs[] = {
+ /* power down jack detect function */
+ {0x1, 0xf81, 0x1},
+ { }
+};
+static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int def_conf;
+ unsigned char seqassoc;
+
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ seqassoc = (unsigned char) get_defcfg_association(def_conf);
+ seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
+ if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
+ && (seqassoc == 0xf0 || seqassoc == 0xff)) {
+ def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
+ snd_hda_codec_set_pincfg(codec, nid, def_conf);
+ }
+}
+
+static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+
+ if (spec->codec_type != VT1708)
+ return 0;
+ ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
+ return 0;
+}
+
+static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ int val;
+
+ if (spec->codec_type != VT1708)
+ return 0;
+ val = !!ucontrol->value.integer.value[0];
+ if (spec->vt1708_jack_detect == val)
+ return 0;
+ spec->vt1708_jack_detect = val;
+ vt1708_update_hp_work(codec);
+ return 1;
+}
+
+static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Jack Detect",
+ .count = 1,
+ .info = snd_ctl_boolean_mono_info,
+ .get = vt1708_jack_detect_get,
+ .put = vt1708_jack_detect_put,
+};
+
+static const struct badness_table via_main_out_badness = {
+ .no_primary_dac = 0x10000,
+ .no_dac = 0x4000,
+ .shared_primary = 0x10000,
+ .shared_surr = 0x20,
+ .shared_clfe = 0x20,
+ .shared_surr_main = 0x20,
+};
+static const struct badness_table via_extra_out_badness = {
+ .no_primary_dac = 0x4000,
+ .no_dac = 0x4000,
+ .shared_primary = 0x12,
+ .shared_surr = 0x20,
+ .shared_clfe = 0x20,
+ .shared_surr_main = 0x10,
+};
+
+static int via_parse_auto_config(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+
+ spec->gen.main_out_badness = &via_main_out_badness;
+ spec->gen.extra_out_badness = &via_extra_out_badness;
+
+ err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
+ if (err < 0)
+ return err;
+
+ err = auto_parse_beep(codec);
+ if (err < 0)
+ return err;
+
+ err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
+ if (err < 0)
+ return err;
+
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &via_pin_power_ctl_enum))
+ return -ENOMEM;
+
+ /* disable widget PM at start for compatibility */
+ codec->power_save_node = 0;
+ spec->gen.power_down_unused = 0;
+ return 0;
+}
+
+static int via_init(struct hda_codec *codec)
+{
+ /* init power states */
+ __analog_low_current_mode(codec, true);
+
+ snd_hda_gen_init(codec);
+
+ vt1708_update_hp_work(codec);
+
+ return 0;
+}
+
+static int via_build_controls(struct hda_codec *codec)
+{
+ /* In order not to create "Phantom Jack" controls,
+ temporary enable jackpoll */
+ int err;
+ int old_interval = codec->jackpoll_interval;
+ if (old_interval)
+ codec->jackpoll_interval = msecs_to_jiffies(100);
+ err = snd_hda_gen_build_controls(codec);
+ if (old_interval)
+ codec->jackpoll_interval = old_interval;
+ return err;
+}
+
+static int via_build_pcms(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int i, err;
+
+ err = snd_hda_gen_build_pcms(codec);
+ if (err < 0 || codec->core.vendor_id != 0x11061708)
+ return err;
+
+ /* We got noisy outputs on the right channel on VT1708 when
+ * 24bit samples are used. Until any workaround is found,
+ * disable the 24bit format, so far.
+ */
+ for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) {
+ struct hda_pcm *info = spec->gen.pcm_rec[i];
+ if (!info)
+ continue;
+ if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
+ info->pcm_type != HDA_PCM_TYPE_AUDIO)
+ continue;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats =
+ SNDRV_PCM_FMTBIT_S16_LE;
+ }
+
+ return 0;
+}
+
+static int probe_vt1708(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+
+ spec->gen.mixer_nid = 0x17;
+
+ /* set jackpoll_interval while parsing the codec */
+ codec->jackpoll_interval = msecs_to_jiffies(100);
+ spec->vt1708_jack_detect = 1;
+
+ /* don't support the input jack switching due to lack of unsol event */
+ /* (it may work with polling, though, but it needs testing) */
+ spec->gen.suppress_auto_mic = 1;
+ /* Some machines show the broken speaker mute */
+ spec->gen.auto_mute_via_amp = 1;
+
+ /* Add HP and CD pin config connect bit re-config action */
+ vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
+ vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
+
+ err = snd_hda_add_verbs(codec, vt1708_init_verbs);
+ if (err < 0)
+ return err;
+
+ /* automatic parse from the BIOS config */
+ err = via_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+
+ /* add jack detect on/off control */
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1708_jack_detect_ctl))
+ return -ENOMEM;
+
+ /* clear jackpoll_interval again; it's set dynamically */
+ codec->jackpoll_interval = 0;
+
+ return 0;
+}
+
+static int probe_vt1709(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+
+ spec->gen.mixer_nid = 0x18;
+
+ return via_parse_auto_config(codec);
+}
+
+static int probe_vt1708S(struct hda_codec *codec);
+static int probe_vt1708B(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+
+ if (get_codec_type(codec) == VT1708BCE)
+ return probe_vt1708S(codec);
+
+ spec->gen.mixer_nid = 0x16;
+
+ /* automatic parse from the BIOS config */
+ return via_parse_auto_config(codec);
+}
+
+/* Support for VT1708S */
+static const struct hda_verb vt1708S_init_verbs[] = {
+ /* Enable Mic Boost Volume backdoor */
+ {0x1, 0xf98, 0x1},
+ /* don't bybass mixer */
+ {0x1, 0xf88, 0xc0},
+ { }
+};
+
+static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
+ int offset, int num_steps, int step_size)
+{
+ snd_hda_override_wcaps(codec, pin,
+ get_wcaps(codec, pin) | AC_WCAP_IN_AMP);
+ snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
+ (offset << AC_AMPCAP_OFFSET_SHIFT) |
+ (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (0 << AC_AMPCAP_MUTE_SHIFT));
+}
+
+static int probe_vt1708S(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+
+ spec->gen.mixer_nid = 0x16;
+ override_mic_boost(codec, 0x1a, 0, 3, 40);
+ override_mic_boost(codec, 0x1e, 0, 3, 40);
+
+ /* correct names for VT1708BCE */
+ if (get_codec_type(codec) == VT1708BCE)
+ snd_hda_codec_set_name(codec, "VT1708BCE");
+ /* correct names for VT1705 */
+ if (codec->core.vendor_id == 0x11064397)
+ snd_hda_codec_set_name(codec, "VT1705");
+
+ err = snd_hda_add_verbs(codec, vt1708S_init_verbs);
+ if (err < 0)
+ return err;
+
+ return via_parse_auto_config(codec);
+}
+
+/* Support for VT1702 */
+
+static const struct hda_verb vt1702_init_verbs[] = {
+ /* mixer enable */
+ {0x1, 0xF88, 0x3},
+ /* GPIO 0~2 */
+ {0x1, 0xF82, 0x3F},
+ { }
+};
+
+static int probe_vt1702(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+
+ spec->gen.mixer_nid = 0x1a;
+
+ /* limit AA path volume to 0 dB */
+ snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
+ (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (1 << AC_AMPCAP_MUTE_SHIFT));
+
+ err = snd_hda_add_verbs(codec, vt1702_init_verbs);
+ if (err < 0)
+ return err;
+
+ /* automatic parse from the BIOS config */
+ return via_parse_auto_config(codec);
+}
+
+/* Support for VT1718S */
+
+static const struct hda_verb vt1718S_init_verbs[] = {
+ /* Enable MW0 adjust Gain 5 */
+ {0x1, 0xfb2, 0x10},
+ /* Enable Boost Volume backdoor */
+ {0x1, 0xf88, 0x8},
+
+ { }
+};
+
+/* Add a connection to the primary DAC from AA-mixer for some codecs
+ * This isn't listed from the raw info, but the chip has a secret connection.
+ */
+static int add_secret_dac_path(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int i, nums;
+ hda_nid_t conn[8];
+ hda_nid_t nid;
+
+ if (!spec->gen.mixer_nid)
+ return 0;
+ nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
+ ARRAY_SIZE(conn) - 1);
+ if (nums < 0)
+ return nums;
+
+ for (i = 0; i < nums; i++) {
+ if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
+ return 0;
+ }
+
+ /* find the primary DAC and add to the connection list */
+ for_each_hda_codec_node(nid, codec) {
+ unsigned int caps = get_wcaps(codec, nid);
+ if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
+ !(caps & AC_WCAP_DIGITAL)) {
+ conn[nums++] = nid;
+ return snd_hda_override_conn_list(codec,
+ spec->gen.mixer_nid,
+ nums, conn);
+ }
+ }
+ return 0;
+}
+
+
+static int probe_vt1718S(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+
+ spec->gen.mixer_nid = 0x21;
+ override_mic_boost(codec, 0x2b, 0, 3, 40);
+ override_mic_boost(codec, 0x29, 0, 3, 40);
+ add_secret_dac_path(codec);
+
+ err = snd_hda_add_verbs(codec, vt1718S_init_verbs);
+ if (err < 0)
+ return err;
+
+ /* automatic parse from the BIOS config */
+ return via_parse_auto_config(codec);
+}
+
+/* Support for VT1716S */
+
+static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ int index = 0;
+
+ index = snd_hda_codec_read(codec, 0x26, 0,
+ AC_VERB_GET_CONNECT_SEL, 0);
+ if (index != -1)
+ *ucontrol->value.integer.value = index;
+
+ return 0;
+}
+
+static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ int index = *ucontrol->value.integer.value;
+
+ snd_hda_codec_write(codec, 0x26, 0,
+ AC_VERB_SET_CONNECT_SEL, index);
+ spec->dmic_enabled = index;
+ return 1;
+}
+
+static const struct snd_kcontrol_new vt1716s_dmic_mixer_vol =
+ HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT);
+static const struct snd_kcontrol_new vt1716s_dmic_mixer_sw = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Mic Capture Switch",
+ .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
+ .count = 1,
+ .info = vt1716s_dmic_info,
+ .get = vt1716s_dmic_get,
+ .put = vt1716s_dmic_put,
+};
+
+
+/* mono-out mixer elements */
+static const struct snd_kcontrol_new vt1716S_mono_out_mixer =
+ HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT);
+
+static const struct hda_verb vt1716S_init_verbs[] = {
+ /* Enable Boost Volume backdoor */
+ {0x1, 0xf8a, 0x80},
+ /* don't bybass mixer */
+ {0x1, 0xf88, 0xc0},
+ /* Enable mono output */
+ {0x1, 0xf90, 0x08},
+ { }
+};
+
+static int probe_vt1716S(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+
+ spec->gen.mixer_nid = 0x16;
+ override_mic_boost(codec, 0x1a, 0, 3, 40);
+ override_mic_boost(codec, 0x1e, 0, 3, 40);
+
+ err = snd_hda_add_verbs(codec, vt1716S_init_verbs);
+ if (err < 0)
+ return err;
+
+ /* automatic parse from the BIOS config */
+ err = via_parse_auto_config(codec);
+ if (err < 0)
+ return err;
+
+ if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_vol) ||
+ !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_sw) ||
+ !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716S_mono_out_mixer))
+ return -ENOMEM;
+
+ return 0;
+}
+
+/* for vt2002P */
+
+static const struct hda_verb vt2002P_init_verbs[] = {
+ /* Class-D speaker related verbs */
+ {0x1, 0xfe0, 0x4},
+ {0x1, 0xfe9, 0x80},
+ {0x1, 0xfe2, 0x22},
+ /* Enable Boost Volume backdoor */
+ {0x1, 0xfb9, 0x24},
+ /* Enable AOW0 to MW9 */
+ {0x1, 0xfb8, 0x88},
+ { }
+};
+
+static const struct hda_verb vt1802_init_verbs[] = {
+ /* Enable Boost Volume backdoor */
+ {0x1, 0xfb9, 0x24},
+ /* Enable AOW0 to MW9 */
+ {0x1, 0xfb8, 0x88},
+ { }
+};
+
+/*
+ * pin fix-up
+ */
+enum {
+ VIA_FIXUP_INTMIC_BOOST,
+ VIA_FIXUP_ASUS_G75,
+ VIA_FIXUP_POWER_SAVE,
+};
+
+static void via_fixup_intmic_boost(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ override_mic_boost(codec, 0x30, 0, 2, 40);
+}
+
+static void via_fixup_power_save(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ codec->power_save_node = 0;
+}
+
+static const struct hda_fixup via_fixups[] = {
+ [VIA_FIXUP_INTMIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = via_fixup_intmic_boost,
+ },
+ [VIA_FIXUP_ASUS_G75] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* set 0x24 and 0x33 as speakers */
+ { 0x24, 0x991301f0 },
+ { 0x33, 0x991301f1 }, /* subwoofer */
+ { }
+ }
+ },
+ [VIA_FIXUP_POWER_SAVE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = via_fixup_power_save,
+ },
+};
+
+static const struct hda_quirk vt2002p_fixups[] = {
+ SND_PCI_QUIRK(0x1043, 0x13f7, "Asus B23E", VIA_FIXUP_POWER_SAVE),
+ SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
+ SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
+ SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", VIA_FIXUP_POWER_SAVE),
+ {}
+};
+
+/* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
+ * Replace this with mixer NID 0x1c
+ */
+static void fix_vt1802_connections(struct hda_codec *codec)
+{
+ static const hda_nid_t conn_24[] = { 0x14, 0x1c };
+ static const hda_nid_t conn_33[] = { 0x1c };
+
+ snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
+ snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
+}
+
+/* Support for vt2002P */
+static int probe_vt2002P(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+
+ spec->gen.mixer_nid = 0x21;
+ override_mic_boost(codec, 0x2b, 0, 3, 40);
+ override_mic_boost(codec, 0x29, 0, 3, 40);
+ if (spec->codec_type == VT1802)
+ fix_vt1802_connections(codec);
+ add_secret_dac_path(codec);
+
+ snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
+ if (spec->codec_type == VT1802)
+ err = snd_hda_add_verbs(codec, vt1802_init_verbs);
+ else
+ err = snd_hda_add_verbs(codec, vt2002P_init_verbs);
+ if (err < 0)
+ return err;
+
+ /* automatic parse from the BIOS config */
+ return via_parse_auto_config(codec);
+}
+
+/* for vt1812 */
+
+static const struct hda_verb vt1812_init_verbs[] = {
+ /* Enable Boost Volume backdoor */
+ {0x1, 0xfb9, 0x24},
+ /* Enable AOW0 to MW9 */
+ {0x1, 0xfb8, 0xa8},
+ { }
+};
+
+static int probe_vt1812(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+
+ spec->gen.mixer_nid = 0x21;
+ override_mic_boost(codec, 0x2b, 0, 3, 40);
+ override_mic_boost(codec, 0x29, 0, 3, 40);
+ add_secret_dac_path(codec);
+
+ err = snd_hda_add_verbs(codec, vt1812_init_verbs);
+ if (err < 0)
+ return err;
+
+ /* automatic parse from the BIOS config */
+ return via_parse_auto_config(codec);
+}
+
+/* Support for vt3476 */
+
+static const struct hda_verb vt3476_init_verbs[] = {
+ /* Enable DMic 8/16/32K */
+ {0x1, 0xF7B, 0x30},
+ /* Enable Boost Volume backdoor */
+ {0x1, 0xFB9, 0x20},
+ /* Enable AOW-MW9 path */
+ {0x1, 0xFB8, 0x10},
+ { }
+};
+
+static int probe_vt3476(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+
+ spec->gen.mixer_nid = 0x3f;
+ add_secret_dac_path(codec);
+
+ err = snd_hda_add_verbs(codec, vt3476_init_verbs);
+ if (err < 0)
+ return err;
+
+ /* automatic parse from the BIOS config */
+ return via_parse_auto_config(codec);
+
+}
+
+/*
+ * common driver probe
+ */
+static int via_probe(struct hda_codec *codec, const struct hda_device_id *id)
+{
+ struct via_spec *spec;
+ int err;
+
+ /* create a codec specific record */
+ spec = via_new_spec(codec);
+ if (!spec)
+ return -ENOMEM;
+
+ switch (id->driver_data) {
+ case VT1708:
+ err = probe_vt1708(codec);
+ break;
+ case VT1709:
+ err = probe_vt1709(codec);
+ break;
+ case VT1708B:
+ err = probe_vt1708B(codec);
+ break;
+ case VT1708S:
+ err = probe_vt1708S(codec);
+ break;
+ case VT1702:
+ err = probe_vt1702(codec);
+ break;
+ case VT1718S:
+ err = probe_vt1718S(codec);
+ break;
+ case VT1716S:
+ err = probe_vt1716S(codec);
+ break;
+ case VT2002P:
+ err = probe_vt2002P(codec);
+ break;
+ case VT1812:
+ err = probe_vt1812(codec);
+ break;
+ case VT3476:
+ err = probe_vt3476(codec);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ if (err < 0) {
+ via_remove(codec);
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct hda_codec_ops via_codec_ops = {
+ .probe = via_probe,
+ .remove = via_remove,
+ .build_controls = via_build_controls,
+ .build_pcms = via_build_pcms,
+ .init = via_init,
+ .unsol_event = snd_hda_jack_unsol_event,
+ .suspend = via_suspend,
+ .resume = via_resume,
+ .check_power_status = via_check_power_status,
+ .stream_pm = snd_hda_gen_stream_pm,
+};
+
+/*
+ * driver entries
+ */
+static const struct hda_device_id snd_hda_id_via[] = {
+ HDA_CODEC_ID_MODEL(0x11061708, "VT1708", VT1708),
+ HDA_CODEC_ID_MODEL(0x11061709, "VT1708", VT1708),
+ HDA_CODEC_ID_MODEL(0x1106170a, "VT1708", VT1708),
+ HDA_CODEC_ID_MODEL(0x1106170b, "VT1708", VT1708),
+ HDA_CODEC_ID_MODEL(0x1106e710, "VT1709 10-Ch", VT1709),
+ HDA_CODEC_ID_MODEL(0x1106e711, "VT1709 10-Ch", VT1709),
+ HDA_CODEC_ID_MODEL(0x1106e712, "VT1709 10-Ch", VT1709),
+ HDA_CODEC_ID_MODEL(0x1106e713, "VT1709 10-Ch", VT1709),
+ HDA_CODEC_ID_MODEL(0x1106e714, "VT1709 6-Ch", VT1709),
+ HDA_CODEC_ID_MODEL(0x1106e715, "VT1709 6-Ch", VT1709),
+ HDA_CODEC_ID_MODEL(0x1106e716, "VT1709 6-Ch", VT1709),
+ HDA_CODEC_ID_MODEL(0x1106e717, "VT1709 6-Ch", VT1709),
+ HDA_CODEC_ID_MODEL(0x1106e720, "VT1708B 8-Ch", VT1708B),
+ HDA_CODEC_ID_MODEL(0x1106e721, "VT1708B 8-Ch", VT1708B),
+ HDA_CODEC_ID_MODEL(0x1106e722, "VT1708B 8-Ch", VT1708B),
+ HDA_CODEC_ID_MODEL(0x1106e723, "VT1708B 8-Ch", VT1708B),
+ HDA_CODEC_ID_MODEL(0x1106e724, "VT1708B 4-Ch", VT1708B),
+ HDA_CODEC_ID_MODEL(0x1106e725, "VT1708B 4-Ch", VT1708B),
+ HDA_CODEC_ID_MODEL(0x1106e726, "VT1708B 4-Ch", VT1708B),
+ HDA_CODEC_ID_MODEL(0x1106e727, "VT1708B 4-Ch", VT1708B),
+ HDA_CODEC_ID_MODEL(0x11060397, "VT1708S", VT1708S),
+ HDA_CODEC_ID_MODEL(0x11061397, "VT1708S", VT1708S),
+ HDA_CODEC_ID_MODEL(0x11062397, "VT1708S", VT1708S),
+ HDA_CODEC_ID_MODEL(0x11063397, "VT1708S", VT1708S),
+ HDA_CODEC_ID_MODEL(0x11064397, "VT1705", VT1708S),
+ HDA_CODEC_ID_MODEL(0x11065397, "VT1708S", VT1708S),
+ HDA_CODEC_ID_MODEL(0x11066397, "VT1708S", VT1708S),
+ HDA_CODEC_ID_MODEL(0x11067397, "VT1708S", VT1708S),
+ HDA_CODEC_ID_MODEL(0x11060398, "VT1702", VT1702),
+ HDA_CODEC_ID_MODEL(0x11061398, "VT1702", VT1702),
+ HDA_CODEC_ID_MODEL(0x11062398, "VT1702", VT1702),
+ HDA_CODEC_ID_MODEL(0x11063398, "VT1702", VT1702),
+ HDA_CODEC_ID_MODEL(0x11064398, "VT1702", VT1702),
+ HDA_CODEC_ID_MODEL(0x11065398, "VT1702", VT1702),
+ HDA_CODEC_ID_MODEL(0x11066398, "VT1702", VT1702),
+ HDA_CODEC_ID_MODEL(0x11067398, "VT1702", VT1702),
+ HDA_CODEC_ID_MODEL(0x11060428, "VT1718S", VT1718S),
+ HDA_CODEC_ID_MODEL(0x11064428, "VT1718S", VT1718S),
+ HDA_CODEC_ID_MODEL(0x11060441, "VT2020", VT1718S),
+ HDA_CODEC_ID_MODEL(0x11064441, "VT1828S", VT1718S),
+ HDA_CODEC_ID_MODEL(0x11060433, "VT1716S", VT1716S),
+ HDA_CODEC_ID_MODEL(0x1106a721, "VT1716S", VT1716S),
+ HDA_CODEC_ID_MODEL(0x11060438, "VT2002P", VT2002P),
+ HDA_CODEC_ID_MODEL(0x11064438, "VT2002P", VT2002P),
+ HDA_CODEC_ID_MODEL(0x11060448, "VT1812", VT1812),
+ HDA_CODEC_ID_MODEL(0x11060440, "VT1818S", VT1708S),
+ HDA_CODEC_ID_MODEL(0x11060446, "VT1802", VT2002P),
+ HDA_CODEC_ID_MODEL(0x11068446, "VT1802", VT2002P),
+ HDA_CODEC_ID_MODEL(0x11064760, "VT1705CF", VT3476),
+ HDA_CODEC_ID_MODEL(0x11064761, "VT1708SCE", VT3476),
+ HDA_CODEC_ID_MODEL(0x11064762, "VT1808", VT3476),
+ {} /* terminator */
+};
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via);
+
+static struct hda_codec_driver via_driver = {
+ .id = snd_hda_id_via,
+ .ops = &via_codec_ops,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("VIA HD-audio codec");
+
+module_hda_codec_driver(via_driver);
diff --git a/sound/hda/common/Kconfig b/sound/hda/common/Kconfig
new file mode 100644
index 000000000000..f38e1947fb3e
--- /dev/null
+++ b/sound/hda/common/Kconfig
@@ -0,0 +1,97 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config SND_HDA
+ tristate
+ select SND_PCM
+ select SND_VMASTER
+ select SND_JACK
+ select SND_HDA_CORE
+
+if SND_HDA
+
+config SND_HDA_HWDEP
+ bool "Build hwdep interface for HD-audio driver"
+ select SND_HWDEP
+ help
+ Say Y here to build a hwdep interface for HD-audio driver.
+ This interface can be used for out-of-band communication
+ with codecs for debugging purposes.
+
+config SND_HDA_RECONFIG
+ bool "Allow dynamic codec reconfiguration"
+ help
+ Say Y here to enable the HD-audio codec re-configuration feature.
+ It allows user to clear the whole codec configuration, change the
+ codec setup, add extra verbs, and re-configure the codec dynamically.
+
+ Note that this item alone doesn't provide the sysfs interface, but
+ enables the feature just for the patch loader below.
+ If you need the traditional sysfs entries for the manual interaction,
+ turn on CONFIG_SND_HDA_HWDEP as well.
+
+config SND_HDA_INPUT_BEEP
+ bool "Support digital beep via input layer"
+ depends on INPUT=y || INPUT=SND_HDA
+ help
+ Say Y here to build a digital beep interface for HD-audio
+ driver. This interface is used to generate digital beeps.
+
+config SND_HDA_INPUT_BEEP_MODE
+ int "Digital beep registration mode (0=off, 1=on)"
+ depends on SND_HDA_INPUT_BEEP=y
+ default "1"
+ range 0 1
+ help
+ Set 0 to disable the digital beep interface for HD-audio by default.
+ Set 1 to always enable the digital beep interface for HD-audio by
+ default.
+
+config SND_HDA_PATCH_LOADER
+ bool "Support initialization patch loading for HD-audio"
+ select FW_LOADER
+ select SND_HDA_RECONFIG
+ help
+ Say Y here to allow the HD-audio driver to load a pseudo
+ firmware file ("patch") for overriding the BIOS setup at
+ start up. The "patch" file can be specified via patch module
+ option, such as patch=hda-init.
+
+config SND_HDA_POWER_SAVE_DEFAULT
+ int "Default time-out for HD-audio power-save mode"
+ depends on PM
+ default 0
+ help
+ The default time-out value in seconds for HD-audio automatic
+ power-save mode. 0 means to disable the power-save mode.
+
+config SND_HDA_CTL_DEV_ID
+ bool "Use the device identifier field for controls"
+ depends on SND_HDA_INTEL
+ help
+ Say Y to use the device identifier field for (mixer)
+ controls (old behaviour until this option is available).
+
+ When enabled, the multiple HDA codecs may set the device
+ field in control (mixer) element identifiers. The use
+ of this field is not recommended and defined for mixer controls.
+
+ The old behaviour (Y) is obsolete and will be removed. Consider
+ to not enable this option.
+
+config SND_HDA_PREALLOC_SIZE
+ int "Pre-allocated buffer size for HD-audio driver"
+ range 0 32768
+ default 0 if SND_DMA_SGBUF
+ default 64 if !SND_DMA_SGBUF
+ help
+ Specifies the default pre-allocated buffer-size in kB for the
+ HD-audio driver. A larger buffer (e.g. 2048) is preferred
+ for systems using PulseAudio. The default 64 is chosen just
+ for compatibility reasons.
+ On x86 systems, the default is zero as S/G allocation works
+ and no preallocation is needed in most cases.
+
+ Note that the pre-allocation size can be changed dynamically
+ via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too.
+
+endif
diff --git a/sound/hda/common/Makefile b/sound/hda/common/Makefile
new file mode 100644
index 000000000000..3344fa0efe75
--- /dev/null
+++ b/sound/hda/common/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+snd-hda-codec-y := bind.o codec.o jack.o auto_parser.o sysfs.o
+snd-hda-codec-y += controller.o
+snd-hda-codec-$(CONFIG_SND_PROC_FS) += proc.o
+
+snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hwdep.o
+snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += beep.o
+
+# for trace-points
+CFLAGS_controller.o := -I$(src)
+
+# common driver
+obj-$(CONFIG_SND_HDA) := snd-hda-codec.o
diff --git a/sound/hda/common/auto_parser.c b/sound/hda/common/auto_parser.c
new file mode 100644
index 000000000000..8923813ce424
--- /dev/null
+++ b/sound/hda/common/auto_parser.c
@@ -0,0 +1,1104 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/sort.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+
+/*
+ * Helper for automatic pin configuration
+ */
+
+static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list)
+{
+ for (; *list; list++)
+ if (*list == nid)
+ return 1;
+ return 0;
+}
+
+/* a pair of input pin and its sequence */
+struct auto_out_pin {
+ hda_nid_t pin;
+ short seq;
+};
+
+static int compare_seq(const void *ap, const void *bp)
+{
+ const struct auto_out_pin *a = ap;
+ const struct auto_out_pin *b = bp;
+ return (int)(a->seq - b->seq);
+}
+
+/*
+ * Sort an associated group of pins according to their sequence numbers.
+ * then store it to a pin array.
+ */
+static void sort_pins_by_sequence(hda_nid_t *pins, struct auto_out_pin *list,
+ int num_pins)
+{
+ int i;
+ sort(list, num_pins, sizeof(list[0]), compare_seq, NULL);
+ for (i = 0; i < num_pins; i++)
+ pins[i] = list[i].pin;
+}
+
+
+/* add the found input-pin to the cfg->inputs[] table */
+static void add_auto_cfg_input_pin(struct hda_codec *codec, struct auto_pin_cfg *cfg,
+ hda_nid_t nid, int type)
+{
+ if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
+ cfg->inputs[cfg->num_inputs].pin = nid;
+ cfg->inputs[cfg->num_inputs].type = type;
+ cfg->inputs[cfg->num_inputs].has_boost_on_pin =
+ nid_has_volume(codec, nid, HDA_INPUT);
+ cfg->num_inputs++;
+ }
+}
+
+static int compare_input_type(const void *ap, const void *bp)
+{
+ const struct auto_pin_cfg_item *a = ap;
+ const struct auto_pin_cfg_item *b = bp;
+ if (a->type != b->type)
+ return (int)(a->type - b->type);
+
+ /* If has both hs_mic and hp_mic, pick the hs_mic ahead of hp_mic. */
+ if (a->is_headset_mic && b->is_headphone_mic)
+ return -1; /* don't swap */
+ else if (a->is_headphone_mic && b->is_headset_mic)
+ return 1; /* swap */
+
+ /* In case one has boost and the other one has not,
+ pick the one with boost first. */
+ if (a->has_boost_on_pin != b->has_boost_on_pin)
+ return (int)(b->has_boost_on_pin - a->has_boost_on_pin);
+
+ /* Keep the original order */
+ return a->order - b->order;
+}
+
+/* Reorder the surround channels
+ * ALSA sequence is front/surr/clfe/side
+ * HDA sequence is:
+ * 4-ch: front/surr => OK as it is
+ * 6-ch: front/clfe/surr
+ * 8-ch: front/clfe/rear/side|fc
+ */
+static void reorder_outputs(unsigned int nums, hda_nid_t *pins)
+{
+ switch (nums) {
+ case 3:
+ case 4:
+ swap(pins[1], pins[2]);
+ break;
+ }
+}
+
+/* check whether the given pin has a proper pin I/O capability bit */
+static bool check_pincap_validity(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int dev)
+{
+ unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
+
+ /* some old hardware don't return the proper pincaps */
+ if (!pincap)
+ return true;
+
+ switch (dev) {
+ case AC_JACK_LINE_OUT:
+ case AC_JACK_SPEAKER:
+ case AC_JACK_HP_OUT:
+ case AC_JACK_SPDIF_OUT:
+ case AC_JACK_DIG_OTHER_OUT:
+ return !!(pincap & AC_PINCAP_OUT);
+ default:
+ return !!(pincap & AC_PINCAP_IN);
+ }
+}
+
+static bool can_be_headset_mic(struct hda_codec *codec,
+ struct auto_pin_cfg_item *item,
+ int seq_number)
+{
+ int attr;
+ unsigned int def_conf;
+ if (item->type != AUTO_PIN_MIC)
+ return false;
+
+ if (item->is_headset_mic || item->is_headphone_mic)
+ return false; /* Already assigned */
+
+ def_conf = snd_hda_codec_get_pincfg(codec, item->pin);
+ attr = snd_hda_get_input_pin_attr(def_conf);
+ if (attr <= INPUT_PIN_ATTR_DOCK)
+ return false;
+
+ if (seq_number >= 0) {
+ int seq = get_defcfg_sequence(def_conf);
+ if (seq != seq_number)
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Parse all pin widgets and store the useful pin nids to cfg
+ *
+ * The number of line-outs or any primary output is stored in line_outs,
+ * and the corresponding output pins are assigned to line_out_pins[],
+ * in the order of front, rear, CLFE, side, ...
+ *
+ * If more extra outputs (speaker and headphone) are found, the pins are
+ * assisnged to hp_pins[] and speaker_pins[], respectively. If no line-out jack
+ * is detected, one of speaker of HP pins is assigned as the primary
+ * output, i.e. to line_out_pins[0]. So, line_outs is always positive
+ * if any analog output exists.
+ *
+ * The analog input pins are assigned to inputs array.
+ * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
+ * respectively.
+ */
+int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg,
+ const hda_nid_t *ignore_nids,
+ unsigned int cond_flags)
+{
+ hda_nid_t nid;
+ short seq, assoc_line_out;
+ struct auto_out_pin line_out[ARRAY_SIZE(cfg->line_out_pins)];
+ struct auto_out_pin speaker_out[ARRAY_SIZE(cfg->speaker_pins)];
+ struct auto_out_pin hp_out[ARRAY_SIZE(cfg->hp_pins)];
+ int i;
+
+ if (!snd_hda_get_int_hint(codec, "parser_flags", &i))
+ cond_flags = i;
+
+ memset(cfg, 0, sizeof(*cfg));
+
+ memset(line_out, 0, sizeof(line_out));
+ memset(speaker_out, 0, sizeof(speaker_out));
+ memset(hp_out, 0, sizeof(hp_out));
+ assoc_line_out = 0;
+
+ for_each_hda_codec_node(nid, codec) {
+ unsigned int wid_caps = get_wcaps(codec, nid);
+ unsigned int wid_type = get_wcaps_type(wid_caps);
+ unsigned int def_conf;
+ short assoc, loc, conn, dev;
+
+ /* read all default configuration for pin complex */
+ if (wid_type != AC_WID_PIN)
+ continue;
+ /* ignore the given nids (e.g. pc-beep returns error) */
+ if (ignore_nids && is_in_nid_list(nid, ignore_nids))
+ continue;
+
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ conn = get_defcfg_connect(def_conf);
+ if (conn == AC_JACK_PORT_NONE)
+ continue;
+ loc = get_defcfg_location(def_conf);
+ dev = get_defcfg_device(def_conf);
+
+ /* workaround for buggy BIOS setups */
+ if (dev == AC_JACK_LINE_OUT) {
+ if (conn == AC_JACK_PORT_FIXED ||
+ conn == AC_JACK_PORT_BOTH)
+ dev = AC_JACK_SPEAKER;
+ }
+
+ if (!check_pincap_validity(codec, nid, dev))
+ continue;
+
+ switch (dev) {
+ case AC_JACK_LINE_OUT:
+ seq = get_defcfg_sequence(def_conf);
+ assoc = get_defcfg_association(def_conf);
+
+ if (!(wid_caps & AC_WCAP_STEREO))
+ if (!cfg->mono_out_pin)
+ cfg->mono_out_pin = nid;
+ if (!assoc)
+ continue;
+ if (!assoc_line_out)
+ assoc_line_out = assoc;
+ else if (assoc_line_out != assoc) {
+ codec_info(codec,
+ "ignore pin 0x%x with mismatching assoc# 0x%x vs 0x%x\n",
+ nid, assoc, assoc_line_out);
+ continue;
+ }
+ if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins)) {
+ codec_info(codec,
+ "ignore pin 0x%x, too many assigned pins\n",
+ nid);
+ continue;
+ }
+ line_out[cfg->line_outs].pin = nid;
+ line_out[cfg->line_outs].seq = seq;
+ cfg->line_outs++;
+ break;
+ case AC_JACK_SPEAKER:
+ seq = get_defcfg_sequence(def_conf);
+ assoc = get_defcfg_association(def_conf);
+ if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins)) {
+ codec_info(codec,
+ "ignore pin 0x%x, too many assigned pins\n",
+ nid);
+ continue;
+ }
+ speaker_out[cfg->speaker_outs].pin = nid;
+ speaker_out[cfg->speaker_outs].seq = (assoc << 4) | seq;
+ cfg->speaker_outs++;
+ break;
+ case AC_JACK_HP_OUT:
+ seq = get_defcfg_sequence(def_conf);
+ assoc = get_defcfg_association(def_conf);
+ if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins)) {
+ codec_info(codec,
+ "ignore pin 0x%x, too many assigned pins\n",
+ nid);
+ continue;
+ }
+ hp_out[cfg->hp_outs].pin = nid;
+ hp_out[cfg->hp_outs].seq = (assoc << 4) | seq;
+ cfg->hp_outs++;
+ break;
+ case AC_JACK_MIC_IN:
+ add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_MIC);
+ break;
+ case AC_JACK_LINE_IN:
+ add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_LINE_IN);
+ break;
+ case AC_JACK_CD:
+ add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_CD);
+ break;
+ case AC_JACK_AUX:
+ add_auto_cfg_input_pin(codec, cfg, nid, AUTO_PIN_AUX);
+ break;
+ case AC_JACK_SPDIF_OUT:
+ case AC_JACK_DIG_OTHER_OUT:
+ if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins)) {
+ codec_info(codec,
+ "ignore pin 0x%x, too many assigned pins\n",
+ nid);
+ continue;
+ }
+ cfg->dig_out_pins[cfg->dig_outs] = nid;
+ cfg->dig_out_type[cfg->dig_outs] =
+ (loc == AC_JACK_LOC_HDMI) ?
+ HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
+ cfg->dig_outs++;
+ break;
+ case AC_JACK_SPDIF_IN:
+ case AC_JACK_DIG_OTHER_IN:
+ cfg->dig_in_pin = nid;
+ if (loc == AC_JACK_LOC_HDMI)
+ cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
+ else
+ cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
+ break;
+ }
+ }
+
+ /* Find a pin that could be a headset or headphone mic */
+ if (cond_flags & HDA_PINCFG_HEADSET_MIC || cond_flags & HDA_PINCFG_HEADPHONE_MIC) {
+ bool hsmic = !!(cond_flags & HDA_PINCFG_HEADSET_MIC);
+ bool hpmic = !!(cond_flags & HDA_PINCFG_HEADPHONE_MIC);
+ for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++)
+ if (hsmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xc)) {
+ cfg->inputs[i].is_headset_mic = 1;
+ hsmic = false;
+ } else if (hpmic && can_be_headset_mic(codec, &cfg->inputs[i], 0xd)) {
+ cfg->inputs[i].is_headphone_mic = 1;
+ hpmic = false;
+ }
+
+ /* If we didn't find our sequence number mark, fall back to any sequence number */
+ for (i = 0; (hsmic || hpmic) && (i < cfg->num_inputs); i++) {
+ if (!can_be_headset_mic(codec, &cfg->inputs[i], -1))
+ continue;
+ if (hsmic) {
+ cfg->inputs[i].is_headset_mic = 1;
+ hsmic = false;
+ } else if (hpmic) {
+ cfg->inputs[i].is_headphone_mic = 1;
+ hpmic = false;
+ }
+ }
+
+ if (hsmic)
+ codec_dbg(codec, "Told to look for a headset mic, but didn't find any.\n");
+ if (hpmic)
+ codec_dbg(codec, "Told to look for a headphone mic, but didn't find any.\n");
+ }
+
+ /* FIX-UP:
+ * If no line-out is defined but multiple HPs are found,
+ * some of them might be the real line-outs.
+ */
+ if (!cfg->line_outs && cfg->hp_outs > 1 &&
+ !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) {
+ i = 0;
+ while (i < cfg->hp_outs) {
+ /* The real HPs should have the sequence 0x0f */
+ if ((hp_out[i].seq & 0x0f) == 0x0f) {
+ i++;
+ continue;
+ }
+ /* Move it to the line-out table */
+ line_out[cfg->line_outs++] = hp_out[i];
+ cfg->hp_outs--;
+ memmove(hp_out + i, hp_out + i + 1,
+ sizeof(hp_out[0]) * (cfg->hp_outs - i));
+ }
+ memset(hp_out + cfg->hp_outs, 0,
+ sizeof(hp_out[0]) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
+ if (!cfg->hp_outs)
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+
+ }
+
+ /* sort by sequence */
+ sort_pins_by_sequence(cfg->line_out_pins, line_out, cfg->line_outs);
+ sort_pins_by_sequence(cfg->speaker_pins, speaker_out,
+ cfg->speaker_outs);
+ sort_pins_by_sequence(cfg->hp_pins, hp_out, cfg->hp_outs);
+
+ /*
+ * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
+ * as a primary output
+ */
+ if (!cfg->line_outs &&
+ !(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) {
+ if (cfg->speaker_outs) {
+ cfg->line_outs = cfg->speaker_outs;
+ memcpy(cfg->line_out_pins, cfg->speaker_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->speaker_outs = 0;
+ memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
+ cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
+ } else if (cfg->hp_outs) {
+ cfg->line_outs = cfg->hp_outs;
+ memcpy(cfg->line_out_pins, cfg->hp_pins,
+ sizeof(cfg->hp_pins));
+ cfg->hp_outs = 0;
+ memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+ }
+ }
+
+ reorder_outputs(cfg->line_outs, cfg->line_out_pins);
+ reorder_outputs(cfg->hp_outs, cfg->hp_pins);
+ reorder_outputs(cfg->speaker_outs, cfg->speaker_pins);
+
+ /* sort inputs in the order of AUTO_PIN_* type */
+ for (i = 0; i < cfg->num_inputs; i++)
+ cfg->inputs[i].order = i;
+ sort(cfg->inputs, cfg->num_inputs, sizeof(cfg->inputs[0]),
+ compare_input_type, NULL);
+
+ /*
+ * debug prints of the parsed results
+ */
+ codec_info(codec, "autoconfig for %s: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n",
+ codec->core.chip_name, cfg->line_outs, cfg->line_out_pins[0],
+ cfg->line_out_pins[1], cfg->line_out_pins[2],
+ cfg->line_out_pins[3], cfg->line_out_pins[4],
+ cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" :
+ (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ?
+ "speaker" : "line"));
+ codec_info(codec, " speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+ cfg->speaker_outs, cfg->speaker_pins[0],
+ cfg->speaker_pins[1], cfg->speaker_pins[2],
+ cfg->speaker_pins[3], cfg->speaker_pins[4]);
+ codec_info(codec, " hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+ cfg->hp_outs, cfg->hp_pins[0],
+ cfg->hp_pins[1], cfg->hp_pins[2],
+ cfg->hp_pins[3], cfg->hp_pins[4]);
+ codec_info(codec, " mono: mono_out=0x%x\n", cfg->mono_out_pin);
+ if (cfg->dig_outs)
+ codec_info(codec, " dig-out=0x%x/0x%x\n",
+ cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
+ codec_info(codec, " inputs:\n");
+ for (i = 0; i < cfg->num_inputs; i++) {
+ codec_info(codec, " %s=0x%x\n",
+ hda_get_autocfg_input_label(codec, cfg, i),
+ cfg->inputs[i].pin);
+ }
+ if (cfg->dig_in_pin)
+ codec_info(codec, " dig-in=0x%x\n", cfg->dig_in_pin);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_parse_pin_defcfg);
+
+/**
+ * snd_hda_get_input_pin_attr - Get the input pin attribute from pin config
+ * @def_conf: pin configuration value
+ *
+ * Guess the input pin attribute (INPUT_PIN_ATTR_XXX) from the given
+ * default pin configuration value.
+ */
+int snd_hda_get_input_pin_attr(unsigned int def_conf)
+{
+ unsigned int loc = get_defcfg_location(def_conf);
+ unsigned int conn = get_defcfg_connect(def_conf);
+ if (conn == AC_JACK_PORT_NONE)
+ return INPUT_PIN_ATTR_UNUSED;
+ /* Windows may claim the internal mic to be BOTH, too */
+ if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH)
+ return INPUT_PIN_ATTR_INT;
+ if ((loc & 0x30) == AC_JACK_LOC_INTERNAL)
+ return INPUT_PIN_ATTR_INT;
+ if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
+ return INPUT_PIN_ATTR_DOCK;
+ if (loc == AC_JACK_LOC_REAR)
+ return INPUT_PIN_ATTR_REAR;
+ if (loc == AC_JACK_LOC_FRONT)
+ return INPUT_PIN_ATTR_FRONT;
+ return INPUT_PIN_ATTR_NORMAL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_input_pin_attr);
+
+/**
+ * hda_get_input_pin_label - Give a label for the given input pin
+ * @codec: the HDA codec
+ * @item: ping config item to refer
+ * @pin: the pin NID
+ * @check_location: flag to add the jack location prefix
+ *
+ * When @check_location is true, the function checks the pin location
+ * for mic and line-in pins, and set an appropriate prefix like "Front",
+ * "Rear", "Internal".
+ */
+static const char *hda_get_input_pin_label(struct hda_codec *codec,
+ const struct auto_pin_cfg_item *item,
+ hda_nid_t pin, bool check_location)
+{
+ unsigned int def_conf;
+ static const char * const mic_names[] = {
+ "Internal Mic", "Dock Mic", "Mic", "Rear Mic", "Front Mic"
+ };
+ int attr;
+
+ def_conf = snd_hda_codec_get_pincfg(codec, pin);
+
+ switch (get_defcfg_device(def_conf)) {
+ case AC_JACK_MIC_IN:
+ if (item && item->is_headset_mic)
+ return "Headset Mic";
+ if (item && item->is_headphone_mic)
+ return "Headphone Mic";
+ if (!check_location)
+ return "Mic";
+ attr = snd_hda_get_input_pin_attr(def_conf);
+ if (!attr)
+ return "None";
+ return mic_names[attr - 1];
+ case AC_JACK_LINE_IN:
+ if (!check_location)
+ return "Line";
+ attr = snd_hda_get_input_pin_attr(def_conf);
+ if (!attr)
+ return "None";
+ if (attr == INPUT_PIN_ATTR_DOCK)
+ return "Dock Line";
+ return "Line";
+ case AC_JACK_AUX:
+ return "Aux";
+ case AC_JACK_CD:
+ return "CD";
+ case AC_JACK_SPDIF_IN:
+ return "SPDIF In";
+ case AC_JACK_DIG_OTHER_IN:
+ return "Digital In";
+ case AC_JACK_HP_OUT:
+ return "Headphone Mic";
+ default:
+ return "Misc";
+ }
+}
+
+/* Check whether the location prefix needs to be added to the label.
+ * If all mic-jacks are in the same location (e.g. rear panel), we don't
+ * have to put "Front" prefix to each label. In such a case, returns false.
+ */
+static int check_mic_location_need(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input)
+{
+ unsigned int defc;
+ int i, attr, attr2;
+
+ defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
+ attr = snd_hda_get_input_pin_attr(defc);
+ /* for internal or docking mics, we need locations */
+ if (attr <= INPUT_PIN_ATTR_NORMAL)
+ return 1;
+
+ attr = 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
+ attr2 = snd_hda_get_input_pin_attr(defc);
+ if (attr2 >= INPUT_PIN_ATTR_NORMAL) {
+ if (attr && attr != attr2)
+ return 1; /* different locations found */
+ attr = attr2;
+ }
+ }
+ return 0;
+}
+
+/**
+ * hda_get_autocfg_input_label - Get a label for the given input
+ * @codec: the HDA codec
+ * @cfg: the parsed pin configuration
+ * @input: the input index number
+ *
+ * Get a label for the given input pin defined by the autocfg item.
+ * Unlike hda_get_input_pin_label(), this function checks all inputs
+ * defined in autocfg and avoids the redundant mic/line prefix as much as
+ * possible.
+ */
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input)
+{
+ int type = cfg->inputs[input].type;
+ int has_multiple_pins = 0;
+
+ if ((input > 0 && cfg->inputs[input - 1].type == type) ||
+ (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
+ has_multiple_pins = 1;
+ if (has_multiple_pins && type == AUTO_PIN_MIC)
+ has_multiple_pins &= check_mic_location_need(codec, cfg, input);
+ has_multiple_pins |= codec->force_pin_prefix;
+ return hda_get_input_pin_label(codec, &cfg->inputs[input],
+ cfg->inputs[input].pin,
+ has_multiple_pins);
+}
+EXPORT_SYMBOL_GPL(hda_get_autocfg_input_label);
+
+/* return the position of NID in the list, or -1 if not found */
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+ int i;
+ for (i = 0; i < nums; i++)
+ if (list[i] == nid)
+ return i;
+ return -1;
+}
+
+/* get a unique suffix or an index number */
+static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins,
+ int num_pins, int *indexp)
+{
+ static const char * const channel_sfx[] = {
+ " Front", " Surround", " CLFE", " Side"
+ };
+ int i;
+
+ i = find_idx_in_nid_list(nid, pins, num_pins);
+ if (i < 0)
+ return NULL;
+ if (num_pins == 1)
+ return "";
+ if (num_pins > ARRAY_SIZE(channel_sfx)) {
+ if (indexp)
+ *indexp = i;
+ return "";
+ }
+ return channel_sfx[i];
+}
+
+static const char *check_output_pfx(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ int attr = snd_hda_get_input_pin_attr(def_conf);
+
+ /* check the location */
+ switch (attr) {
+ case INPUT_PIN_ATTR_DOCK:
+ return "Dock ";
+ case INPUT_PIN_ATTR_FRONT:
+ return "Front ";
+ }
+ return "";
+}
+
+static int get_hp_label_index(struct hda_codec *codec, hda_nid_t nid,
+ const hda_nid_t *pins, int num_pins)
+{
+ int i, j, idx = 0;
+
+ const char *pfx = check_output_pfx(codec, nid);
+
+ i = find_idx_in_nid_list(nid, pins, num_pins);
+ if (i < 0)
+ return -1;
+ for (j = 0; j < i; j++)
+ if (pfx == check_output_pfx(codec, pins[j]))
+ idx++;
+
+ return idx;
+}
+
+static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid,
+ const struct auto_pin_cfg *cfg,
+ const char *name, char *label, int maxlen,
+ int *indexp)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ int attr = snd_hda_get_input_pin_attr(def_conf);
+ const char *pfx, *sfx = "";
+
+ /* handle as a speaker if it's a fixed line-out */
+ if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT)
+ name = "Speaker";
+ pfx = check_output_pfx(codec, nid);
+
+ if (cfg) {
+ /* try to give a unique suffix if needed */
+ sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs,
+ indexp);
+ if (!sfx)
+ sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs,
+ indexp);
+ if (!sfx) {
+ /* don't add channel suffix for Headphone controls */
+ int idx = get_hp_label_index(codec, nid, cfg->hp_pins,
+ cfg->hp_outs);
+ if (idx >= 0 && indexp)
+ *indexp = idx;
+ sfx = "";
+ }
+ }
+ snprintf(label, maxlen, "%s%s%s", pfx, name, sfx);
+ return 1;
+}
+
+#define is_hdmi_cfg(conf) \
+ (get_defcfg_location(conf) == AC_JACK_LOC_HDMI)
+
+/**
+ * snd_hda_get_pin_label - Get a label for the given I/O pin
+ * @codec: the HDA codec
+ * @nid: pin NID
+ * @cfg: the parsed pin configuration
+ * @label: the string buffer to store
+ * @maxlen: the max length of string buffer (including termination)
+ * @indexp: the pointer to return the index number (for multiple ctls)
+ *
+ * Get a label for the given pin. This function works for both input and
+ * output pins. When @cfg is given as non-NULL, the function tries to get
+ * an optimized label using hda_get_autocfg_input_label().
+ *
+ * This function tries to give a unique label string for the pin as much as
+ * possible. For example, when the multiple line-outs are present, it adds
+ * the channel suffix like "Front", "Surround", etc (only when @cfg is given).
+ * If no unique name with a suffix is available and @indexp is non-NULL, the
+ * index number is stored in the pointer.
+ */
+int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
+ const struct auto_pin_cfg *cfg,
+ char *label, int maxlen, int *indexp)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ const char *name = NULL;
+ int i;
+ bool hdmi;
+
+ if (indexp)
+ *indexp = 0;
+ if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+ return 0;
+
+ switch (get_defcfg_device(def_conf)) {
+ case AC_JACK_LINE_OUT:
+ return fill_audio_out_name(codec, nid, cfg, "Line Out",
+ label, maxlen, indexp);
+ case AC_JACK_SPEAKER:
+ return fill_audio_out_name(codec, nid, cfg, "Speaker",
+ label, maxlen, indexp);
+ case AC_JACK_HP_OUT:
+ return fill_audio_out_name(codec, nid, cfg, "Headphone",
+ label, maxlen, indexp);
+ case AC_JACK_SPDIF_OUT:
+ case AC_JACK_DIG_OTHER_OUT:
+ hdmi = is_hdmi_cfg(def_conf);
+ name = hdmi ? "HDMI" : "SPDIF";
+ if (cfg && indexp)
+ for (i = 0; i < cfg->dig_outs; i++) {
+ hda_nid_t pin = cfg->dig_out_pins[i];
+ unsigned int c;
+ if (pin == nid)
+ break;
+ c = snd_hda_codec_get_pincfg(codec, pin);
+ if (hdmi == is_hdmi_cfg(c))
+ (*indexp)++;
+ }
+ break;
+ default:
+ if (cfg) {
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].pin != nid)
+ continue;
+ name = hda_get_autocfg_input_label(codec, cfg, i);
+ if (name)
+ break;
+ }
+ }
+ if (!name)
+ name = hda_get_input_pin_label(codec, NULL, nid, true);
+ break;
+ }
+ if (!name)
+ return 0;
+ strscpy(label, name, maxlen);
+ return 1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_pin_label);
+
+/**
+ * snd_hda_add_verbs - Add verbs to the init list
+ * @codec: the HDA codec
+ * @list: zero-terminated verb list to add
+ *
+ * Append the given verb list to the execution list. The verbs will be
+ * performed at init and resume time via snd_hda_apply_verbs().
+ */
+int snd_hda_add_verbs(struct hda_codec *codec,
+ const struct hda_verb *list)
+{
+ const struct hda_verb **v;
+ v = snd_array_new(&codec->verbs);
+ if (!v)
+ return -ENOMEM;
+ *v = list;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_add_verbs);
+
+/**
+ * snd_hda_apply_verbs - Execute the init verb lists
+ * @codec: the HDA codec
+ */
+void snd_hda_apply_verbs(struct hda_codec *codec)
+{
+ const struct hda_verb **v;
+ int i;
+
+ snd_array_for_each(&codec->verbs, i, v)
+ snd_hda_sequence_write(codec, *v);
+}
+EXPORT_SYMBOL_GPL(snd_hda_apply_verbs);
+
+/**
+ * snd_hda_apply_pincfgs - Set each pin config in the given list
+ * @codec: the HDA codec
+ * @cfg: NULL-terminated pin config table
+ */
+void snd_hda_apply_pincfgs(struct hda_codec *codec,
+ const struct hda_pintbl *cfg)
+{
+ for (; cfg->nid; cfg++)
+ snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
+}
+EXPORT_SYMBOL_GPL(snd_hda_apply_pincfgs);
+
+static void set_pin_targets(struct hda_codec *codec,
+ const struct hda_pintbl *cfg)
+{
+ for (; cfg->nid; cfg++)
+ snd_hda_set_pin_ctl_cache(codec, cfg->nid, cfg->val);
+}
+
+void __snd_hda_apply_fixup(struct hda_codec *codec, int id, int action, int depth)
+{
+ const char *modelname = codec->fixup_name;
+
+ while (id >= 0) {
+ const struct hda_fixup *fix = codec->fixup_list + id;
+
+ if (++depth > 10)
+ break;
+ if (fix->chained_before)
+ __snd_hda_apply_fixup(codec, fix->chain_id, action, depth + 1);
+
+ switch (fix->type) {
+ case HDA_FIXUP_PINS:
+ if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins)
+ break;
+ codec_dbg(codec, "%s: Apply pincfg for %s\n",
+ codec->core.chip_name, modelname);
+ snd_hda_apply_pincfgs(codec, fix->v.pins);
+ break;
+ case HDA_FIXUP_VERBS:
+ if (action != HDA_FIXUP_ACT_PROBE || !fix->v.verbs)
+ break;
+ codec_dbg(codec, "%s: Apply fix-verbs for %s\n",
+ codec->core.chip_name, modelname);
+ snd_hda_add_verbs(codec, fix->v.verbs);
+ break;
+ case HDA_FIXUP_FUNC:
+ if (!fix->v.func)
+ break;
+ codec_dbg(codec, "%s: Apply fix-func for %s\n",
+ codec->core.chip_name, modelname);
+ fix->v.func(codec, fix, action);
+ break;
+ case HDA_FIXUP_PINCTLS:
+ if (action != HDA_FIXUP_ACT_PROBE || !fix->v.pins)
+ break;
+ codec_dbg(codec, "%s: Apply pinctl for %s\n",
+ codec->core.chip_name, modelname);
+ set_pin_targets(codec, fix->v.pins);
+ break;
+ default:
+ codec_err(codec, "%s: Invalid fixup type %d\n",
+ codec->core.chip_name, fix->type);
+ break;
+ }
+ if (!fix->chained || fix->chained_before)
+ break;
+ id = fix->chain_id;
+ }
+}
+EXPORT_SYMBOL_GPL(__snd_hda_apply_fixup);
+
+/**
+ * snd_hda_apply_fixup - Apply the fixup chain with the given action
+ * @codec: the HDA codec
+ * @action: fixup action (HDA_FIXUP_ACT_XXX)
+ */
+void snd_hda_apply_fixup(struct hda_codec *codec, int action)
+{
+ if (codec->fixup_list)
+ __snd_hda_apply_fixup(codec, codec->fixup_id, action, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hda_apply_fixup);
+
+#define IGNORE_SEQ_ASSOC (~(AC_DEFCFG_SEQUENCE | AC_DEFCFG_DEF_ASSOC))
+
+static bool pin_config_match(struct hda_codec *codec,
+ const struct hda_pintbl *pins,
+ bool match_all_pins)
+{
+ const struct hda_pincfg *pin;
+ int i;
+
+ snd_array_for_each(&codec->init_pins, i, pin) {
+ hda_nid_t nid = pin->nid;
+ u32 cfg = pin->cfg;
+ const struct hda_pintbl *t_pins;
+ int found;
+
+ t_pins = pins;
+ found = 0;
+ for (; t_pins->nid; t_pins++) {
+ if (t_pins->nid == nid) {
+ found = 1;
+ if ((t_pins->val & IGNORE_SEQ_ASSOC) == (cfg & IGNORE_SEQ_ASSOC))
+ break;
+ else if ((cfg & 0xf0000000) == 0x40000000 && (t_pins->val & 0xf0000000) == 0x40000000)
+ break;
+ else
+ return false;
+ }
+ }
+ if (match_all_pins &&
+ !found && (cfg & 0xf0000000) != 0x40000000)
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * snd_hda_pick_pin_fixup - Pick up a fixup matching with the pin quirk list
+ * @codec: the HDA codec
+ * @pin_quirk: zero-terminated pin quirk list
+ * @fixlist: the fixup list
+ * @match_all_pins: all valid pins must match with the table entries
+ */
+void snd_hda_pick_pin_fixup(struct hda_codec *codec,
+ const struct snd_hda_pin_quirk *pin_quirk,
+ const struct hda_fixup *fixlist,
+ bool match_all_pins)
+{
+ const struct snd_hda_pin_quirk *pq;
+ const char *name = NULL;
+
+ if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET)
+ return;
+
+ for (pq = pin_quirk; pq->subvendor; pq++) {
+ if ((codec->core.subsystem_id & 0xffff0000) != (pq->subvendor << 16))
+ continue;
+ if (codec->core.vendor_id != pq->codec)
+ continue;
+ if (pin_config_match(codec, pq->pins, match_all_pins)) {
+ codec->fixup_id = pq->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ codec->fixup_name = pq->name;
+ name = pq->name;
+#endif
+ codec_info(codec, "%s: picked fixup %s (pin match)\n",
+ codec->core.chip_name, name ? name : "");
+ codec->fixup_list = fixlist;
+ return;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_pick_pin_fixup);
+
+/* check whether the given quirk entry matches with vendor/device pair */
+static bool hda_quirk_match(u16 vendor, u16 device, const struct hda_quirk *q)
+{
+ if (q->subvendor != vendor)
+ return false;
+ return !q->subdevice ||
+ (device & q->subdevice_mask) == q->subdevice;
+}
+
+/* look through the quirk list and return the matching entry */
+static const struct hda_quirk *
+hda_quirk_lookup_id(u16 vendor, u16 device, const struct hda_quirk *list)
+{
+ const struct hda_quirk *q;
+
+ for (q = list; q->subvendor || q->subdevice; q++) {
+ if (hda_quirk_match(vendor, device, q))
+ return q;
+ }
+ return NULL;
+}
+
+/**
+ * snd_hda_pick_fixup - Pick up a fixup matching with PCI/codec SSID or model string
+ * @codec: the HDA codec
+ * @models: NULL-terminated model string list
+ * @quirk: zero-terminated PCI/codec SSID quirk list
+ * @fixlist: the fixup list
+ *
+ * Pick up a fixup entry matching with the given model string or SSID.
+ * If a fixup was already set beforehand, the function doesn't do anything.
+ * When a special model string "nofixup" is given, also no fixup is applied.
+ *
+ * The function tries to find the matching model name at first, if given.
+ * If the model string contains the SSID alias, try to look up with the given
+ * alias ID.
+ * If nothing matched, try to look up the PCI SSID.
+ * If still nothing matched, try to look up the codec SSID.
+ */
+void snd_hda_pick_fixup(struct hda_codec *codec,
+ const struct hda_model_fixup *models,
+ const struct hda_quirk *quirk,
+ const struct hda_fixup *fixlist)
+{
+ const struct hda_quirk *q;
+ int id = HDA_FIXUP_ID_NOT_SET;
+ const char *name = NULL;
+ const char *type = NULL;
+ unsigned int vendor, device;
+ u16 pci_vendor, pci_device;
+ u16 codec_vendor, codec_device;
+
+ if (codec->fixup_id != HDA_FIXUP_ID_NOT_SET)
+ return;
+
+ /* when model=nofixup is given, don't pick up any fixups */
+ if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
+ id = HDA_FIXUP_ID_NO_FIXUP;
+ fixlist = NULL;
+ codec_info(codec, "%s: picked no fixup (nofixup specified)\n",
+ codec->core.chip_name);
+ goto found;
+ }
+
+ /* match with the model name string */
+ if (codec->modelname && models) {
+ while (models->name) {
+ if (!strcmp(codec->modelname, models->name)) {
+ id = models->id;
+ name = models->name;
+ codec_info(codec, "%s: picked fixup %s (model specified)\n",
+ codec->core.chip_name, name);
+ goto found;
+ }
+ models++;
+ }
+ }
+
+ if (!quirk)
+ return;
+
+ if (codec->bus->pci) {
+ pci_vendor = codec->bus->pci->subsystem_vendor;
+ pci_device = codec->bus->pci->subsystem_device;
+ }
+
+ codec_vendor = codec->core.subsystem_id >> 16;
+ codec_device = codec->core.subsystem_id & 0xffff;
+
+ /* match with the SSID alias given by the model string "XXXX:YYYY" */
+ if (codec->modelname &&
+ sscanf(codec->modelname, "%04x:%04x", &vendor, &device) == 2) {
+ q = hda_quirk_lookup_id(vendor, device, quirk);
+ if (q) {
+ type = "alias SSID";
+ goto found_device;
+ }
+ }
+
+ /* match primarily with the PCI SSID */
+ for (q = quirk; q->subvendor || q->subdevice; q++) {
+ /* if the entry is specific to codec SSID, check with it */
+ if (!codec->bus->pci || q->match_codec_ssid) {
+ if (hda_quirk_match(codec_vendor, codec_device, q)) {
+ type = "codec SSID";
+ goto found_device;
+ }
+ } else {
+ if (hda_quirk_match(pci_vendor, pci_device, q)) {
+ type = "PCI SSID";
+ goto found_device;
+ }
+ }
+ }
+
+ /* match with the codec SSID */
+ q = hda_quirk_lookup_id(codec_vendor, codec_device, quirk);
+ if (q) {
+ type = "codec SSID";
+ goto found_device;
+ }
+
+ return; /* no matching */
+
+ found_device:
+ id = q->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ name = q->name;
+#endif
+ codec_info(codec, "%s: picked fixup %s for %s %04x:%04x\n",
+ codec->core.chip_name, name ? name : "",
+ type, q->subvendor, q->subdevice);
+ found:
+ codec->fixup_id = id;
+ codec->fixup_list = fixlist;
+ codec->fixup_name = name;
+}
+EXPORT_SYMBOL_GPL(snd_hda_pick_fixup);
diff --git a/sound/hda/common/beep.c b/sound/hda/common/beep.c
new file mode 100644
index 000000000000..13a7d92e8d8d
--- /dev/null
+++ b/sound/hda/common/beep.c
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Digital Beep Input Interface for HD-audio codec
+ *
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
+ * Copyright (c) 2008 Embedded Alley Solutions Inc
+ */
+
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include "hda_beep.h"
+#include "hda_local.h"
+
+enum {
+ DIGBEEP_HZ_STEP = 46875, /* 46.875 Hz */
+ DIGBEEP_HZ_MIN = 93750, /* 93.750 Hz */
+ DIGBEEP_HZ_MAX = 12000000, /* 12 KHz */
+};
+
+/* generate or stop tone */
+static void generate_tone(struct hda_beep *beep, int tone)
+{
+ struct hda_codec *codec = beep->codec;
+
+ if (tone && !beep->playing) {
+ snd_hda_power_up(codec);
+ if (beep->power_hook)
+ beep->power_hook(beep, true);
+ beep->playing = 1;
+ }
+ if (!codec->beep_just_power_on)
+ snd_hda_codec_write(codec, beep->nid, 0,
+ AC_VERB_SET_BEEP_CONTROL, tone);
+ if (!tone && beep->playing) {
+ beep->playing = 0;
+ if (beep->power_hook)
+ beep->power_hook(beep, false);
+ snd_hda_power_down(codec);
+ }
+}
+
+static void snd_hda_generate_beep(struct work_struct *work)
+{
+ struct hda_beep *beep =
+ container_of(work, struct hda_beep, beep_work);
+
+ if (beep->enabled)
+ generate_tone(beep, beep->tone);
+}
+
+/* (non-standard) Linear beep tone calculation for IDT/STAC codecs
+ *
+ * The tone frequency of beep generator on IDT/STAC codecs is
+ * defined from the 8bit tone parameter, in Hz,
+ * freq = 48000 * (257 - tone) / 1024
+ * that is from 12kHz to 93.75Hz in steps of 46.875 Hz
+ */
+static int beep_linear_tone(struct hda_beep *beep, int hz)
+{
+ if (hz <= 0)
+ return 0;
+ hz *= 1000; /* fixed point */
+ hz = hz - DIGBEEP_HZ_MIN
+ + DIGBEEP_HZ_STEP / 2; /* round to nearest step */
+ if (hz < 0)
+ hz = 0; /* turn off PC beep*/
+ else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
+ hz = 1; /* max frequency */
+ else {
+ hz /= DIGBEEP_HZ_STEP;
+ hz = 255 - hz;
+ }
+ return hz;
+}
+
+/* HD-audio standard beep tone parameter calculation
+ *
+ * The tone frequency in Hz is calculated as
+ * freq = 48000 / (tone * 4)
+ * from 47Hz to 12kHz
+ */
+static int beep_standard_tone(struct hda_beep *beep, int hz)
+{
+ if (hz <= 0)
+ return 0; /* disabled */
+ hz = 12000 / hz;
+ if (hz > 0xff)
+ return 0xff;
+ if (hz <= 0)
+ return 1;
+ return hz;
+}
+
+static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
+ unsigned int code, int hz)
+{
+ struct hda_beep *beep = input_get_drvdata(dev);
+
+ switch (code) {
+ case SND_BELL:
+ if (hz)
+ hz = 1000;
+ fallthrough;
+ case SND_TONE:
+ if (beep->linear_tone)
+ beep->tone = beep_linear_tone(beep, hz);
+ else
+ beep->tone = beep_standard_tone(beep, hz);
+ break;
+ default:
+ return -1;
+ }
+
+ /* schedule beep event */
+ schedule_work(&beep->beep_work);
+ return 0;
+}
+
+static void turn_on_beep(struct hda_beep *beep)
+{
+ if (beep->keep_power_at_enable)
+ snd_hda_power_up_pm(beep->codec);
+}
+
+static void turn_off_beep(struct hda_beep *beep)
+{
+ cancel_work_sync(&beep->beep_work);
+ if (beep->playing) {
+ /* turn off beep */
+ generate_tone(beep, 0);
+ }
+ if (beep->keep_power_at_enable)
+ snd_hda_power_down_pm(beep->codec);
+}
+
+/**
+ * snd_hda_enable_beep_device - Turn on/off beep sound
+ * @codec: the HDA codec
+ * @enable: flag to turn on/off
+ */
+int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
+{
+ struct hda_beep *beep = codec->beep;
+ if (!beep)
+ return 0;
+ enable = !!enable;
+ if (beep->enabled != enable) {
+ beep->enabled = enable;
+ if (enable)
+ turn_on_beep(beep);
+ else
+ turn_off_beep(beep);
+ return 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_enable_beep_device);
+
+static int beep_dev_register(struct snd_device *device)
+{
+ struct hda_beep *beep = device->device_data;
+ int err;
+
+ err = input_register_device(beep->dev);
+ if (!err)
+ beep->registered = true;
+ return err;
+}
+
+static int beep_dev_disconnect(struct snd_device *device)
+{
+ struct hda_beep *beep = device->device_data;
+
+ if (beep->registered)
+ input_unregister_device(beep->dev);
+ else
+ input_free_device(beep->dev);
+ if (beep->enabled)
+ turn_off_beep(beep);
+ return 0;
+}
+
+static int beep_dev_free(struct snd_device *device)
+{
+ struct hda_beep *beep = device->device_data;
+
+ beep->codec->beep = NULL;
+ kfree(beep);
+ return 0;
+}
+
+/**
+ * snd_hda_attach_beep_device - Attach a beep input device
+ * @codec: the HDA codec
+ * @nid: beep NID
+ *
+ * Attach a beep object to the given widget. If beep hint is turned off
+ * explicitly or beep_mode of the codec is turned off, this doesn't nothing.
+ *
+ * Currently, only one beep device is allowed to each codec.
+ */
+int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
+{
+ static const struct snd_device_ops ops = {
+ .dev_register = beep_dev_register,
+ .dev_disconnect = beep_dev_disconnect,
+ .dev_free = beep_dev_free,
+ };
+ struct input_dev *input_dev;
+ struct hda_beep *beep;
+ int err;
+
+ if (!codec->beep_just_power_on) {
+ if (!snd_hda_get_bool_hint(codec, "beep"))
+ return 0; /* disabled explicitly by hints */
+ if (codec->beep_mode == HDA_BEEP_MODE_OFF)
+ return 0; /* disabled by module option */
+ }
+
+ beep = kzalloc(sizeof(*beep), GFP_KERNEL);
+ if (beep == NULL)
+ return -ENOMEM;
+ snprintf(beep->phys, sizeof(beep->phys),
+ "card%d/codec#%d/beep0", codec->card->number, codec->addr);
+ /* enable linear scale */
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_DIGI_CONVERT_2, 0x01);
+
+ beep->nid = nid;
+ beep->codec = codec;
+ codec->beep = beep;
+
+ INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ err = -ENOMEM;
+ goto err_free;
+ }
+
+ /* setup digital beep device */
+ input_dev->name = "HDA Digital PCBeep";
+ input_dev->phys = beep->phys;
+ input_dev->id.bustype = BUS_PCI;
+ input_dev->dev.parent = &codec->card->card_dev;
+
+ input_dev->id.vendor = codec->core.vendor_id >> 16;
+ input_dev->id.product = codec->core.vendor_id & 0xffff;
+ input_dev->id.version = 0x01;
+
+ input_dev->evbit[0] = BIT_MASK(EV_SND);
+ input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+ input_dev->event = snd_hda_beep_event;
+ input_set_drvdata(input_dev, beep);
+
+ beep->dev = input_dev;
+
+ err = snd_device_new(codec->card, SNDRV_DEV_JACK, beep, &ops);
+ if (err < 0)
+ goto err_input;
+
+ return 0;
+
+ err_input:
+ input_free_device(beep->dev);
+ err_free:
+ kfree(beep);
+ codec->beep = NULL;
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hda_attach_beep_device);
+
+/**
+ * snd_hda_detach_beep_device - Detach the beep device
+ * @codec: the HDA codec
+ */
+void snd_hda_detach_beep_device(struct hda_codec *codec)
+{
+ if (!codec->bus->shutdown && codec->beep)
+ snd_device_free(codec->card, codec->beep);
+}
+EXPORT_SYMBOL_GPL(snd_hda_detach_beep_device);
+
+static bool ctl_has_mute(struct snd_kcontrol *kcontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ return query_amp_caps(codec, get_amp_nid(kcontrol),
+ get_amp_direction(kcontrol)) & AC_AMPCAP_MUTE;
+}
+
+/* get/put callbacks for beep mute mixer switches */
+
+/**
+ * snd_hda_mixer_amp_switch_get_beep - Get callback for beep controls
+ * @kcontrol: ctl element
+ * @ucontrol: pointer to get/store the data
+ */
+int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_beep *beep = codec->beep;
+ int chs = get_amp_channels(kcontrol);
+
+ if (beep && (!beep->enabled || !ctl_has_mute(kcontrol))) {
+ if (chs & 1)
+ ucontrol->value.integer.value[0] = beep->enabled;
+ if (chs & 2)
+ ucontrol->value.integer.value[1] = beep->enabled;
+ return 0;
+ }
+ return snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get_beep);
+
+/**
+ * snd_hda_mixer_amp_switch_put_beep - Put callback for beep controls
+ * @kcontrol: ctl element
+ * @ucontrol: pointer to get/store the data
+ */
+int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_beep *beep = codec->beep;
+ if (beep) {
+ u8 chs = get_amp_channels(kcontrol);
+ int enable = 0;
+ long *valp = ucontrol->value.integer.value;
+ if (chs & 1) {
+ enable |= *valp;
+ valp++;
+ }
+ if (chs & 2)
+ enable |= *valp;
+ snd_hda_enable_beep_device(codec, enable);
+ }
+ if (!ctl_has_mute(kcontrol))
+ return 0;
+ return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put_beep);
diff --git a/sound/hda/common/bind.c b/sound/hda/common/bind.c
new file mode 100644
index 000000000000..bb1090b65699
--- /dev/null
+++ b/sound/hda/common/bind.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HD-audio codec driver binding
+ * Copyright (c) Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/pm.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_jack.h"
+
+/*
+ * find a matching codec id
+ */
+static int hda_codec_match(struct hdac_device *dev, const struct hdac_driver *drv)
+{
+ struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+ const struct hda_codec_driver *driver =
+ container_of(drv, struct hda_codec_driver, core);
+ const struct hda_device_id *list;
+ /* check probe_id instead of vendor_id if set */
+ u32 id = codec->probe_id ? codec->probe_id : codec->core.vendor_id;
+ u32 rev_id = codec->core.revision_id;
+
+ for (list = driver->id; list->vendor_id; list++) {
+ if (list->vendor_id == id &&
+ (!list->rev_id || list->rev_id == rev_id)) {
+ codec->preset = list;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* process an unsolicited event */
+static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev)
+{
+ struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+
+ /* ignore unsol events during shutdown */
+ if (codec->card->shutdown || codec->bus->shutdown)
+ return;
+
+ /* ignore unsol events during system suspend/resume */
+ if (codec->core.dev.power.power_state.event != PM_EVENT_ON)
+ return;
+
+ if (driver->ops->unsol_event)
+ driver->ops->unsol_event(codec, ev);
+}
+
+/**
+ * snd_hda_codec_set_name - set the codec name
+ * @codec: the HDA codec
+ * @name: name string to set
+ */
+int snd_hda_codec_set_name(struct hda_codec *codec, const char *name)
+{
+ int err;
+
+ if (!name)
+ return 0;
+ err = snd_hdac_device_set_chip_name(&codec->core, name);
+ if (err < 0)
+ return err;
+
+ /* update the mixer name */
+ if (!*codec->card->mixername ||
+ codec->bus->mixer_assigned >= codec->core.addr) {
+ snprintf(codec->card->mixername,
+ sizeof(codec->card->mixername), "%s %s",
+ codec->core.vendor_name, codec->core.chip_name);
+ codec->bus->mixer_assigned = codec->core.addr;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_name);
+
+static int hda_codec_driver_probe(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+ struct module *owner = dev->driver->owner;
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+ int err;
+
+ if (codec->bus->core.ext_ops) {
+ if (WARN_ON(!codec->bus->core.ext_ops->hdev_attach))
+ return -EINVAL;
+ return codec->bus->core.ext_ops->hdev_attach(&codec->core);
+ }
+
+ if (WARN_ON(!codec->preset))
+ return -EINVAL;
+
+ err = snd_hda_codec_set_name(codec, codec->preset->name);
+ if (err < 0)
+ goto error;
+ err = snd_hdac_regmap_init(&codec->core);
+ if (err < 0)
+ goto error;
+
+ if (!try_module_get(owner)) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ if (WARN_ON(!(driver->ops && driver->ops->probe))) {
+ err = -EINVAL;
+ goto error_module_put;
+ }
+
+ err = driver->ops->probe(codec, codec->preset);
+ if (err < 0)
+ goto error_module_put;
+ err = snd_hda_codec_build_pcms(codec);
+ if (err < 0)
+ goto error_module;
+ err = snd_hda_codec_build_controls(codec);
+ if (err < 0)
+ goto error_module;
+ /* only register after the bus probe finished; otherwise it's racy */
+ if (!codec->bus->bus_probing && codec->card->registered) {
+ err = snd_card_register(codec->card);
+ if (err < 0)
+ goto error_module;
+ snd_hda_codec_register(codec);
+ }
+
+ codec->core.lazy_cache = true;
+ return 0;
+
+ error_module:
+ if (driver->ops->remove)
+ driver->ops->remove(codec);
+ error_module_put:
+ module_put(owner);
+
+ error:
+ snd_hda_codec_cleanup_for_unbind(codec);
+ codec->preset = NULL;
+ return err;
+}
+
+static int hda_codec_driver_remove(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+
+ if (codec->bus->core.ext_ops) {
+ if (WARN_ON(!codec->bus->core.ext_ops->hdev_detach))
+ return -EINVAL;
+ return codec->bus->core.ext_ops->hdev_detach(&codec->core);
+ }
+
+ snd_hda_codec_disconnect_pcms(codec);
+ snd_hda_jack_tbl_disconnect(codec);
+ if (!refcount_dec_and_test(&codec->pcm_ref))
+ wait_event(codec->remove_sleep, !refcount_read(&codec->pcm_ref));
+ snd_power_sync_ref(codec->bus->card);
+
+ if (driver->ops->remove)
+ driver->ops->remove(codec);
+ snd_hda_codec_cleanup_for_unbind(codec);
+ codec->preset = NULL;
+ module_put(dev->driver->owner);
+ return 0;
+}
+
+static void hda_codec_driver_shutdown(struct device *dev)
+{
+ snd_hda_codec_shutdown(dev_to_hda_codec(dev));
+}
+
+int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
+ struct module *owner)
+{
+ drv->core.driver.name = name;
+ drv->core.driver.owner = owner;
+ drv->core.driver.bus = &snd_hda_bus_type;
+ drv->core.driver.probe = hda_codec_driver_probe;
+ drv->core.driver.remove = hda_codec_driver_remove;
+ drv->core.driver.shutdown = hda_codec_driver_shutdown;
+ drv->core.driver.pm = pm_ptr(&hda_codec_driver_pm);
+ drv->core.type = HDA_DEV_LEGACY;
+ drv->core.match = hda_codec_match;
+ drv->core.unsol_event = hda_codec_unsol_event;
+ return driver_register(&drv->core.driver);
+}
+EXPORT_SYMBOL_GPL(__hda_codec_driver_register);
+
+void hda_codec_driver_unregister(struct hda_codec_driver *drv)
+{
+ driver_unregister(&drv->core.driver);
+}
+EXPORT_SYMBOL_GPL(hda_codec_driver_unregister);
+
+static inline bool codec_probed(struct hda_codec *codec)
+{
+ return device_attach(hda_codec_dev(codec)) > 0 && codec->preset;
+}
+
+/* try to auto-load codec module */
+static void request_codec_module(struct hda_codec *codec)
+{
+#ifdef MODULE
+ char modalias[32];
+ const char *mod = NULL;
+
+ switch (codec->probe_id) {
+ case HDA_CODEC_ID_GENERIC_HDMI:
+#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI)
+ mod = "snd-hda-codec-hdmi";
+#endif
+ break;
+ case HDA_CODEC_ID_GENERIC:
+#if IS_MODULE(CONFIG_SND_HDA_GENERIC)
+ mod = "snd-hda-codec-generic";
+#endif
+ break;
+ default:
+ snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias));
+ mod = modalias;
+ break;
+ }
+
+ if (mod)
+ request_module(mod);
+#endif /* MODULE */
+}
+
+/* try to auto-load and bind the codec module */
+static void codec_bind_module(struct hda_codec *codec)
+{
+#ifdef MODULE
+ request_codec_module(codec);
+ if (codec_probed(codec))
+ return;
+#endif
+}
+
+#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI)
+/* if all audio out widgets are digital, let's assume the codec as a HDMI/DP */
+static bool is_likely_hdmi_codec(struct hda_codec *codec)
+{
+ hda_nid_t nid;
+
+ /*
+ * For ASoC users, if snd_hda_hdmi_codec module is denylisted and any
+ * event causes i915 enumeration to fail, ->wcaps remains uninitialized.
+ */
+ if (!codec->wcaps)
+ return true;
+
+ for_each_hda_codec_node(nid, codec) {
+ unsigned int wcaps = get_wcaps(codec, nid);
+ switch (get_wcaps_type(wcaps)) {
+ case AC_WID_AUD_IN:
+ return false; /* HDMI parser supports only HDMI out */
+ case AC_WID_AUD_OUT:
+ if (!(wcaps & AC_WCAP_DIGITAL))
+ return false;
+ break;
+ }
+ }
+ return true;
+}
+#else
+/* no HDMI codec parser support */
+#define is_likely_hdmi_codec(codec) false
+#endif /* CONFIG_SND_HDA_CODEC_HDMI */
+
+static int codec_bind_generic(struct hda_codec *codec)
+{
+ if (codec->probe_id)
+ return -ENODEV;
+
+ if (is_likely_hdmi_codec(codec)) {
+ codec->probe_id = HDA_CODEC_ID_GENERIC_HDMI;
+ request_codec_module(codec);
+ if (codec_probed(codec))
+ return 0;
+ }
+
+ codec->probe_id = HDA_CODEC_ID_GENERIC;
+ request_codec_module(codec);
+ if (codec_probed(codec))
+ return 0;
+ return -ENODEV;
+}
+
+#if IS_ENABLED(CONFIG_SND_HDA_GENERIC)
+#define is_generic_config(codec) \
+ (codec->modelname && !strcmp(codec->modelname, "generic"))
+#else
+#define is_generic_config(codec) 0
+#endif
+
+/**
+ * snd_hda_codec_configure - (Re-)configure the HD-audio codec
+ * @codec: the HDA codec
+ *
+ * Start parsing of the given codec tree and (re-)initialize the whole
+ * codec driver binding.
+ *
+ * Returns 0 if successful or a negative error code.
+ */
+int snd_hda_codec_configure(struct hda_codec *codec)
+{
+ int err;
+
+ if (codec->configured)
+ return 0;
+
+ if (is_generic_config(codec))
+ codec->probe_id = HDA_CODEC_ID_GENERIC;
+ else
+ codec->probe_id = 0;
+
+ if (!device_is_registered(&codec->core.dev)) {
+ err = snd_hdac_device_register(&codec->core);
+ if (err < 0)
+ return err;
+ }
+
+ if (!codec->preset)
+ codec_bind_module(codec);
+ if (!codec->preset) {
+ err = codec_bind_generic(codec);
+ if (err < 0) {
+ codec_dbg(codec, "Unable to bind the codec\n");
+ return err;
+ }
+ }
+
+ codec->configured = 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_configure);
diff --git a/sound/hda/common/codec.c b/sound/hda/common/codec.c
new file mode 100644
index 000000000000..c6d44168c7f9
--- /dev/null
+++ b/sound/hda/common/codec.c
@@ -0,0 +1,4048 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/minmax.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include <sound/asoundef.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include "hda_local.h"
+#include "hda_beep.h"
+#include "hda_jack.h"
+#include <sound/hda_hwdep.h>
+#include <sound/hda_component.h>
+
+#define codec_in_pm(codec) snd_hdac_is_in_pm(&codec->core)
+#define hda_codec_is_power_on(codec) snd_hdac_is_power_on(&codec->core)
+#define codec_has_epss(codec) \
+ ((codec)->core.power_caps & AC_PWRST_EPSS)
+#define codec_has_clkstop(codec) \
+ ((codec)->core.power_caps & AC_PWRST_CLKSTOP)
+
+static int call_exec_verb(struct hda_bus *bus, struct hda_codec *codec,
+ unsigned int cmd, unsigned int flags,
+ unsigned int *res)
+{
+ int err;
+
+ CLASS(snd_hda_power_pm, pm)(codec);
+ guard(mutex)(&bus->core.cmd_mutex);
+ if (flags & HDA_RW_NO_RESPONSE_FALLBACK)
+ bus->no_response_fallback = 1;
+ err = snd_hdac_bus_exec_verb_unlocked(&bus->core, codec->core.addr,
+ cmd, res);
+ bus->no_response_fallback = 0;
+ return err;
+}
+
+/*
+ * Send and receive a verb - passed to exec_verb override for hdac_device
+ */
+static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd,
+ unsigned int flags, unsigned int *res)
+{
+ struct hda_codec *codec = container_of(dev, struct hda_codec, core);
+ struct hda_bus *bus = codec->bus;
+ int err;
+
+ if (cmd == ~0)
+ return -1;
+
+ again:
+ err = call_exec_verb(bus, codec, cmd, flags, res);
+ if (!codec_in_pm(codec) && res && err == -EAGAIN) {
+ if (bus->response_reset) {
+ codec_dbg(codec,
+ "resetting BUS due to fatal communication error\n");
+ snd_hda_bus_reset(bus);
+ }
+ goto again;
+ }
+ /* clear reset-flag when the communication gets recovered */
+ if (!err || codec_in_pm(codec))
+ bus->response_reset = 0;
+ return err;
+}
+
+/**
+ * snd_hda_sequence_write - sequence writes
+ * @codec: the HDA codec
+ * @seq: VERB array to send
+ *
+ * Send the commands sequentially from the given array.
+ * The array must be terminated with NID=0.
+ */
+void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq)
+{
+ for (; seq->nid; seq++)
+ snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param);
+}
+EXPORT_SYMBOL_GPL(snd_hda_sequence_write);
+
+/* connection list element */
+struct hda_conn_list {
+ struct list_head list;
+ int len;
+ hda_nid_t nid;
+ hda_nid_t conns[] __counted_by(len);
+};
+
+/* look up the cached results */
+static struct hda_conn_list *
+lookup_conn_list(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_conn_list *p;
+ list_for_each_entry(p, &codec->conn_list, list) {
+ if (p->nid == nid)
+ return p;
+ }
+ return NULL;
+}
+
+static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
+ const hda_nid_t *list)
+{
+ struct hda_conn_list *p;
+
+ p = kmalloc(struct_size(p, conns, len), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ p->len = len;
+ p->nid = nid;
+ memcpy(p->conns, list, len * sizeof(hda_nid_t));
+ list_add(&p->list, &codec->conn_list);
+ return 0;
+}
+
+static void remove_conn_list(struct hda_codec *codec)
+{
+ while (!list_empty(&codec->conn_list)) {
+ struct hda_conn_list *p;
+ p = list_first_entry(&codec->conn_list, typeof(*p), list);
+ list_del(&p->list);
+ kfree(p);
+ }
+}
+
+/* read the connection and add to the cache */
+static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
+{
+ hda_nid_t list[32];
+ hda_nid_t *result = list;
+ int len;
+
+ len = snd_hda_get_raw_connections(codec, nid, list, ARRAY_SIZE(list));
+ if (len == -ENOSPC) {
+ len = snd_hda_get_num_raw_conns(codec, nid);
+ result = kmalloc_array(len, sizeof(hda_nid_t), GFP_KERNEL);
+ if (!result)
+ return -ENOMEM;
+ len = snd_hda_get_raw_connections(codec, nid, result, len);
+ }
+ if (len >= 0)
+ len = snd_hda_override_conn_list(codec, nid, len, result);
+ if (result != list)
+ kfree(result);
+ return len;
+}
+
+/**
+ * snd_hda_get_conn_list - get connection list
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @listp: the pointer to store NID list
+ *
+ * Parses the connection list of the given widget and stores the pointer
+ * to the list of NIDs.
+ *
+ * Returns the number of connections, or a negative error code.
+ *
+ * Note that the returned pointer isn't protected against the list
+ * modification. If snd_hda_override_conn_list() might be called
+ * concurrently, protect with a mutex appropriately.
+ */
+int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
+ const hda_nid_t **listp)
+{
+ bool added = false;
+
+ for (;;) {
+ int err;
+ const struct hda_conn_list *p;
+
+ /* if the connection-list is already cached, read it */
+ p = lookup_conn_list(codec, nid);
+ if (p) {
+ if (listp)
+ *listp = p->conns;
+ return p->len;
+ }
+ if (snd_BUG_ON(added))
+ return -EINVAL;
+
+ err = read_and_add_raw_conns(codec, nid);
+ if (err < 0)
+ return err;
+ added = true;
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_conn_list);
+
+/**
+ * snd_hda_get_connections - copy connection list
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @conn_list: connection list array; when NULL, checks only the size
+ * @max_conns: max. number of connections to store
+ *
+ * Parses the connection list of the given widget and stores the list
+ * of NIDs.
+ *
+ * Returns the number of connections, or a negative error code.
+ */
+int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
+ hda_nid_t *conn_list, int max_conns)
+{
+ const hda_nid_t *list;
+ int len = snd_hda_get_conn_list(codec, nid, &list);
+
+ if (len > 0 && conn_list) {
+ if (len > max_conns) {
+ codec_err(codec, "Too many connections %d for NID 0x%x\n",
+ len, nid);
+ return -EINVAL;
+ }
+ memcpy(conn_list, list, len * sizeof(hda_nid_t));
+ }
+
+ return len;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_connections);
+
+/**
+ * snd_hda_override_conn_list - add/modify the connection-list to cache
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @len: number of connection list entries
+ * @list: the list of connection entries
+ *
+ * Add or modify the given connection-list to the cache. If the corresponding
+ * cache already exists, invalidate it and append a new one.
+ *
+ * Returns zero or a negative error code.
+ */
+int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
+ const hda_nid_t *list)
+{
+ struct hda_conn_list *p;
+
+ p = lookup_conn_list(codec, nid);
+ if (p) {
+ list_del(&p->list);
+ kfree(p);
+ }
+
+ return add_conn_list(codec, nid, len, list);
+}
+EXPORT_SYMBOL_GPL(snd_hda_override_conn_list);
+
+/**
+ * snd_hda_get_conn_index - get the connection index of the given NID
+ * @codec: the HDA codec
+ * @mux: NID containing the list
+ * @nid: NID to select
+ * @recursive: 1 when searching NID recursively, otherwise 0
+ *
+ * Parses the connection list of the widget @mux and checks whether the
+ * widget @nid is present. If it is, return the connection index.
+ * Otherwise it returns -1.
+ */
+int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
+ hda_nid_t nid, int recursive)
+{
+ const hda_nid_t *conn;
+ int i, nums;
+
+ nums = snd_hda_get_conn_list(codec, mux, &conn);
+ for (i = 0; i < nums; i++)
+ if (conn[i] == nid)
+ return i;
+ if (!recursive)
+ return -1;
+ if (recursive > 10) {
+ codec_dbg(codec, "too deep connection for 0x%x\n", nid);
+ return -1;
+ }
+ recursive++;
+ for (i = 0; i < nums; i++) {
+ unsigned int type = get_wcaps_type(get_wcaps(codec, conn[i]));
+ if (type == AC_WID_PIN || type == AC_WID_AUD_OUT)
+ continue;
+ if (snd_hda_get_conn_index(codec, conn[i], nid, recursive) >= 0)
+ return i;
+ }
+ return -1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_conn_index);
+
+/**
+ * snd_hda_get_num_devices - get DEVLIST_LEN parameter of the given widget
+ * @codec: the HDA codec
+ * @nid: NID of the pin to parse
+ *
+ * Get the device entry number on the given widget. This is a feature of
+ * DP MST audio. Each pin can have several device entries in it.
+ */
+unsigned int snd_hda_get_num_devices(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int wcaps = get_wcaps(codec, nid);
+ int parm;
+
+ if (!codec->dp_mst || !(wcaps & AC_WCAP_DIGITAL) ||
+ get_wcaps_type(wcaps) != AC_WID_PIN)
+ return 0;
+
+ parm = snd_hdac_read_parm_uncached(&codec->core, nid, AC_PAR_DEVLIST_LEN);
+ if (parm == -1)
+ parm = 0;
+ return parm & AC_DEV_LIST_LEN_MASK;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_num_devices);
+
+/**
+ * snd_hda_get_devices - copy device list without cache
+ * @codec: the HDA codec
+ * @nid: NID of the pin to parse
+ * @dev_list: device list array
+ * @max_devices: max. number of devices to store
+ *
+ * Copy the device list. This info is dynamic and so not cached.
+ * Currently called only from hda_proc.c, so not exported.
+ */
+unsigned int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
+ u8 *dev_list, unsigned int max_devices)
+{
+ unsigned int parm, i, dev_len, devices;
+
+ parm = snd_hda_get_num_devices(codec, nid);
+ if (!parm) /* not multi-stream capable */
+ return 0;
+
+ dev_len = min(parm + 1, max_devices);
+
+ devices = 0;
+ while (devices < dev_len) {
+ if (snd_hdac_read(&codec->core, nid,
+ AC_VERB_GET_DEVICE_LIST, devices, &parm))
+ break; /* error */
+
+ for (i = 0; i < 8; i++) {
+ dev_list[devices] = (u8)parm;
+ parm >>= 4;
+ devices++;
+ if (devices >= dev_len)
+ break;
+ }
+ }
+ return devices;
+}
+
+/**
+ * snd_hda_get_dev_select - get device entry select on the pin
+ * @codec: the HDA codec
+ * @nid: NID of the pin to get device entry select
+ *
+ * Get the devcie entry select on the pin. Return the device entry
+ * id selected on the pin. Return 0 means the first device entry
+ * is selected or MST is not supported.
+ */
+int snd_hda_get_dev_select(struct hda_codec *codec, hda_nid_t nid)
+{
+ /* not support dp_mst will always return 0, using first dev_entry */
+ if (!codec->dp_mst)
+ return 0;
+
+ return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DEVICE_SEL, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_dev_select);
+
+/**
+ * snd_hda_set_dev_select - set device entry select on the pin
+ * @codec: the HDA codec
+ * @nid: NID of the pin to set device entry select
+ * @dev_id: device entry id to be set
+ *
+ * Set the device entry select on the pin nid.
+ */
+int snd_hda_set_dev_select(struct hda_codec *codec, hda_nid_t nid, int dev_id)
+{
+ int ret, num_devices;
+
+ /* not support dp_mst will always return 0, using first dev_entry */
+ if (!codec->dp_mst)
+ return 0;
+
+ /* AC_PAR_DEVLIST_LEN is 0 based. */
+ num_devices = snd_hda_get_num_devices(codec, nid) + 1;
+ /* If Device List Length is 0 (num_device = 1),
+ * the pin is not multi stream capable.
+ * Do nothing in this case.
+ */
+ if (num_devices == 1)
+ return 0;
+
+ /* Behavior of setting index being equal to or greater than
+ * Device List Length is not predictable
+ */
+ if (num_devices <= dev_id)
+ return -EINVAL;
+
+ ret = snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_DEVICE_SEL, dev_id);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_set_dev_select);
+
+/*
+ * read widget caps for each widget and store in cache
+ */
+static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node)
+{
+ int i;
+ hda_nid_t nid;
+
+ codec->wcaps = kmalloc_array(codec->core.num_nodes, 4, GFP_KERNEL);
+ if (!codec->wcaps)
+ return -ENOMEM;
+ nid = codec->core.start_nid;
+ for (i = 0; i < codec->core.num_nodes; i++, nid++)
+ codec->wcaps[i] = snd_hdac_read_parm_uncached(&codec->core,
+ nid, AC_PAR_AUDIO_WIDGET_CAP);
+ return 0;
+}
+
+/* read all pin default configurations and save codec->init_pins */
+static int read_pin_defaults(struct hda_codec *codec)
+{
+ hda_nid_t nid;
+
+ for_each_hda_codec_node(nid, codec) {
+ struct hda_pincfg *pin;
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int wid_type = get_wcaps_type(wcaps);
+ if (wid_type != AC_WID_PIN)
+ continue;
+ pin = snd_array_new(&codec->init_pins);
+ if (!pin)
+ return -ENOMEM;
+ pin->nid = nid;
+ pin->cfg = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONFIG_DEFAULT, 0);
+ /*
+ * all device entries are the same widget control so far
+ * fixme: if any codec is different, need fix here
+ */
+ pin->ctrl = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL,
+ 0);
+ }
+ return 0;
+}
+
+/* look up the given pin config list and return the item matching with NID */
+static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec,
+ struct snd_array *array,
+ hda_nid_t nid)
+{
+ struct hda_pincfg *pin;
+ int i;
+
+ snd_array_for_each(array, i, pin) {
+ if (pin->nid == nid)
+ return pin;
+ }
+ return NULL;
+}
+
+/* set the current pin config value for the given NID.
+ * the value is cached, and read via snd_hda_codec_get_pincfg()
+ */
+int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
+ hda_nid_t nid, unsigned int cfg)
+{
+ struct hda_pincfg *pin;
+
+ pin = look_up_pincfg(codec, list, nid);
+ if (!pin) {
+ pin = snd_array_new(list);
+ if (!pin)
+ return -ENOMEM;
+ pin->nid = nid;
+ }
+ pin->cfg = cfg;
+ return 0;
+}
+
+/**
+ * snd_hda_codec_set_pincfg - Override a pin default configuration
+ * @codec: the HDA codec
+ * @nid: NID to set the pin config
+ * @cfg: the pin default config value
+ *
+ * Override a pin default configuration value in the cache.
+ * This value can be read by snd_hda_codec_get_pincfg() in a higher
+ * priority than the real hardware value.
+ */
+int snd_hda_codec_set_pincfg(struct hda_codec *codec,
+ hda_nid_t nid, unsigned int cfg)
+{
+ return snd_hda_add_pincfg(codec, &codec->driver_pins, nid, cfg);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_pincfg);
+
+/**
+ * snd_hda_codec_get_pincfg - Obtain a pin-default configuration
+ * @codec: the HDA codec
+ * @nid: NID to get the pin config
+ *
+ * Get the current pin config value of the given pin NID.
+ * If the pincfg value is cached or overridden via sysfs or driver,
+ * returns the cached value.
+ */
+unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_pincfg *pin;
+
+#ifdef CONFIG_SND_HDA_RECONFIG
+ {
+ unsigned int cfg = 0;
+ scoped_guard(mutex, &codec->user_mutex) {
+ pin = look_up_pincfg(codec, &codec->user_pins, nid);
+ if (pin)
+ cfg = pin->cfg;
+ }
+ if (cfg)
+ return cfg;
+ }
+#endif
+ pin = look_up_pincfg(codec, &codec->driver_pins, nid);
+ if (pin)
+ return pin->cfg;
+ pin = look_up_pincfg(codec, &codec->init_pins, nid);
+ if (pin)
+ return pin->cfg;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_get_pincfg);
+
+/**
+ * snd_hda_codec_set_pin_target - remember the current pinctl target value
+ * @codec: the HDA codec
+ * @nid: pin NID
+ * @val: assigned pinctl value
+ *
+ * This function stores the given value to a pinctl target value in the
+ * pincfg table. This isn't always as same as the actually written value
+ * but can be referred at any time via snd_hda_codec_get_pin_target().
+ */
+int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int val)
+{
+ struct hda_pincfg *pin;
+
+ pin = look_up_pincfg(codec, &codec->init_pins, nid);
+ if (!pin)
+ return -EINVAL;
+ pin->target = val;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_pin_target);
+
+/**
+ * snd_hda_codec_get_pin_target - return the current pinctl target value
+ * @codec: the HDA codec
+ * @nid: pin NID
+ */
+int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_pincfg *pin;
+
+ pin = look_up_pincfg(codec, &codec->init_pins, nid);
+ if (!pin)
+ return 0;
+ return pin->target;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_get_pin_target);
+
+/**
+ * snd_hda_shutup_pins - Shut up all pins
+ * @codec: the HDA codec
+ *
+ * Clear all pin controls to shup up before suspend for avoiding click noise.
+ * The controls aren't cached so that they can be resumed properly.
+ */
+void snd_hda_shutup_pins(struct hda_codec *codec)
+{
+ const struct hda_pincfg *pin;
+ int i;
+
+ /* don't shut up pins when unloading the driver; otherwise it breaks
+ * the default pin setup at the next load of the driver
+ */
+ if (codec->bus->shutdown)
+ return;
+ snd_array_for_each(&codec->init_pins, i, pin) {
+ /* use read here for syncing after issuing each verb */
+ snd_hda_codec_read(codec, pin->nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+ }
+ codec->pins_shutup = 1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_shutup_pins);
+
+/* Restore the pin controls cleared previously via snd_hda_shutup_pins() */
+static void restore_shutup_pins(struct hda_codec *codec)
+{
+ const struct hda_pincfg *pin;
+ int i;
+
+ if (!codec->pins_shutup)
+ return;
+ if (codec->bus->shutdown)
+ return;
+ snd_array_for_each(&codec->init_pins, i, pin) {
+ snd_hda_codec_write(codec, pin->nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pin->ctrl);
+ }
+ codec->pins_shutup = 0;
+}
+
+static void hda_jackpoll_work(struct work_struct *work)
+{
+ struct hda_codec *codec =
+ container_of(work, struct hda_codec, jackpoll_work.work);
+
+ if (!codec->jackpoll_interval)
+ return;
+
+ /* the power-up/down sequence triggers the runtime resume */
+ CLASS(snd_hda_power, pm)(codec);
+ /* update jacks manually if polling is required, too */
+ snd_hda_jack_set_dirty_all(codec);
+ snd_hda_jack_poll_all(codec);
+ schedule_delayed_work(&codec->jackpoll_work, codec->jackpoll_interval);
+}
+
+/* release all pincfg lists */
+static void free_init_pincfgs(struct hda_codec *codec)
+{
+ snd_array_free(&codec->driver_pins);
+#ifdef CONFIG_SND_HDA_RECONFIG
+ snd_array_free(&codec->user_pins);
+#endif
+ snd_array_free(&codec->init_pins);
+}
+
+/*
+ * audio-converter setup caches
+ */
+struct hda_cvt_setup {
+ hda_nid_t nid;
+ u8 stream_tag;
+ u8 channel_id;
+ u16 format_id;
+ unsigned char active; /* cvt is currently used */
+ unsigned char dirty; /* setups should be cleared */
+};
+
+/* get or create a cache entry for the given audio converter NID */
+static struct hda_cvt_setup *
+get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_cvt_setup *p;
+ int i;
+
+ snd_array_for_each(&codec->cvt_setups, i, p) {
+ if (p->nid == nid)
+ return p;
+ }
+ p = snd_array_new(&codec->cvt_setups);
+ if (p)
+ p->nid = nid;
+ return p;
+}
+
+/*
+ * PCM device
+ */
+void snd_hda_codec_pcm_put(struct hda_pcm *pcm)
+{
+ if (refcount_dec_and_test(&pcm->codec->pcm_ref))
+ wake_up(&pcm->codec->remove_sleep);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_put);
+
+struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
+ const char *fmt, ...)
+{
+ struct hda_pcm *pcm;
+ va_list args;
+
+ pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
+ if (!pcm)
+ return NULL;
+
+ pcm->codec = codec;
+ va_start(args, fmt);
+ pcm->name = kvasprintf(GFP_KERNEL, fmt, args);
+ va_end(args);
+ if (!pcm->name) {
+ kfree(pcm);
+ return NULL;
+ }
+
+ list_add_tail(&pcm->list, &codec->pcm_list_head);
+ refcount_inc(&codec->pcm_ref);
+ return pcm;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new);
+
+/*
+ * codec destructor
+ */
+void snd_hda_codec_disconnect_pcms(struct hda_codec *codec)
+{
+ struct hda_pcm *pcm;
+
+ list_for_each_entry(pcm, &codec->pcm_list_head, list) {
+ if (pcm->disconnected)
+ continue;
+ if (pcm->pcm)
+ snd_device_disconnect(codec->card, pcm->pcm);
+ snd_hda_codec_pcm_put(pcm);
+ pcm->disconnected = 1;
+ }
+}
+
+static void codec_release_pcms(struct hda_codec *codec)
+{
+ struct hda_pcm *pcm, *n;
+
+ list_for_each_entry_safe(pcm, n, &codec->pcm_list_head, list) {
+ list_del(&pcm->list);
+ if (pcm->pcm)
+ snd_device_free(pcm->codec->card, pcm->pcm);
+ clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits);
+ kfree(pcm->name);
+ kfree(pcm);
+ }
+}
+
+/**
+ * snd_hda_codec_cleanup_for_unbind - Prepare codec for removal
+ * @codec: codec device to cleanup
+ */
+void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
+{
+ if (codec->core.registered) {
+ /* pm_runtime_put() is called in snd_hdac_device_exit() */
+ pm_runtime_get_noresume(hda_codec_dev(codec));
+ pm_runtime_disable(hda_codec_dev(codec));
+ codec->core.registered = 0;
+ }
+
+ snd_hda_codec_disconnect_pcms(codec);
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+ if (!codec->in_freeing)
+ snd_hda_ctls_clear(codec);
+ codec_release_pcms(codec);
+ snd_hda_detach_beep_device(codec);
+ snd_hda_jack_tbl_clear(codec);
+ codec->proc_widget_hook = NULL;
+ codec->spec = NULL;
+
+ /* free only driver_pins so that init_pins + user_pins are restored */
+ snd_array_free(&codec->driver_pins);
+ snd_array_free(&codec->cvt_setups);
+ snd_array_free(&codec->spdif_out);
+ snd_array_free(&codec->verbs);
+ codec->follower_dig_outs = NULL;
+ codec->spdif_status_reset = 0;
+ snd_array_free(&codec->mixers);
+ snd_array_free(&codec->nids);
+ remove_conn_list(codec);
+ snd_hdac_regmap_exit(&codec->core);
+ codec->configured = 0;
+ refcount_set(&codec->pcm_ref, 1); /* reset refcount */
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup_for_unbind);
+
+static unsigned int hda_set_power_state(struct hda_codec *codec,
+ unsigned int power_state);
+
+/* enable/disable display power per codec */
+void snd_hda_codec_display_power(struct hda_codec *codec, bool enable)
+{
+ if (codec->display_power_control)
+ snd_hdac_display_power(&codec->bus->core, codec->addr, enable);
+}
+
+/**
+ * snd_hda_codec_register - Finalize codec initialization
+ * @codec: codec device to register
+ *
+ * Also called from hda_bind.c
+ */
+void snd_hda_codec_register(struct hda_codec *codec)
+{
+ if (codec->core.registered)
+ return;
+ if (device_is_registered(hda_codec_dev(codec))) {
+ snd_hda_codec_display_power(codec, true);
+ pm_runtime_enable(hda_codec_dev(codec));
+ /* it was powered up in snd_hda_codec_new(), now all done */
+ snd_hda_power_down(codec);
+ codec->core.registered = 1;
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_register);
+
+static int snd_hda_codec_dev_register(struct snd_device *device)
+{
+ snd_hda_codec_register(device->device_data);
+ return 0;
+}
+
+/**
+ * snd_hda_codec_unregister - Unregister specified codec device
+ * @codec: codec device to unregister
+ */
+void snd_hda_codec_unregister(struct hda_codec *codec)
+{
+ codec->in_freeing = 1;
+ /*
+ * snd_hda_codec_device_new() is used by legacy HDA and ASoC driver.
+ * We can't unregister ASoC device since it will be unregistered in
+ * snd_hdac_ext_bus_device_remove().
+ */
+ if (codec->core.type == HDA_DEV_LEGACY)
+ snd_hdac_device_unregister(&codec->core);
+ snd_hda_codec_display_power(codec, false);
+
+ /*
+ * In the case of ASoC HD-audio bus, the device refcount is released in
+ * snd_hdac_ext_bus_device_remove() explicitly.
+ */
+ if (codec->core.type == HDA_DEV_LEGACY)
+ put_device(hda_codec_dev(codec));
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_unregister);
+
+static int snd_hda_codec_dev_free(struct snd_device *device)
+{
+ snd_hda_codec_unregister(device->device_data);
+ return 0;
+}
+
+static void snd_hda_codec_dev_release(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+
+ free_init_pincfgs(codec);
+ snd_hdac_device_exit(&codec->core);
+ snd_hda_sysfs_clear(codec);
+ kfree(codec->modelname);
+ kfree(codec->wcaps);
+ kfree(codec);
+}
+
+#define DEV_NAME_LEN 31
+
+/**
+ * snd_hda_codec_device_init - allocate HDA codec device
+ * @bus: codec's parent bus
+ * @codec_addr: the codec address on the parent bus
+ * @fmt: format string for the device's name
+ *
+ * Returns newly allocated codec device or ERR_PTR() on failure.
+ */
+struct hda_codec *
+snd_hda_codec_device_init(struct hda_bus *bus, unsigned int codec_addr,
+ const char *fmt, ...)
+{
+ va_list vargs;
+ char name[DEV_NAME_LEN];
+ struct hda_codec *codec;
+ int err;
+
+ if (snd_BUG_ON(!bus))
+ return ERR_PTR(-EINVAL);
+ if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS))
+ return ERR_PTR(-EINVAL);
+
+ codec = kzalloc(sizeof(*codec), GFP_KERNEL);
+ if (!codec)
+ return ERR_PTR(-ENOMEM);
+
+ va_start(vargs, fmt);
+ vsprintf(name, fmt, vargs);
+ va_end(vargs);
+
+ err = snd_hdac_device_init(&codec->core, &bus->core, name, codec_addr);
+ if (err < 0) {
+ kfree(codec);
+ return ERR_PTR(err);
+ }
+
+ codec->bus = bus;
+ codec->depop_delay = -1;
+ codec->fixup_id = HDA_FIXUP_ID_NOT_SET;
+ codec->core.dev.release = snd_hda_codec_dev_release;
+ codec->core.type = HDA_DEV_LEGACY;
+
+ mutex_init(&codec->spdif_mutex);
+ mutex_init(&codec->control_mutex);
+ snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
+ snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32);
+ snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
+ snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
+ snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
+ snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
+ snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16);
+ snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8);
+ INIT_LIST_HEAD(&codec->conn_list);
+ INIT_LIST_HEAD(&codec->pcm_list_head);
+ INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
+ refcount_set(&codec->pcm_ref, 1);
+ init_waitqueue_head(&codec->remove_sleep);
+
+ return codec;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_device_init);
+
+/**
+ * snd_hda_codec_new - create a HDA codec
+ * @bus: the bus to assign
+ * @card: card for this codec
+ * @codec_addr: the codec address
+ * @codecp: the pointer to store the generated codec
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
+ unsigned int codec_addr, struct hda_codec **codecp)
+{
+ struct hda_codec *codec;
+ int ret;
+
+ codec = snd_hda_codec_device_init(bus, codec_addr, "hdaudioC%dD%d",
+ card->number, codec_addr);
+ if (IS_ERR(codec))
+ return PTR_ERR(codec);
+ *codecp = codec;
+
+ ret = snd_hda_codec_device_new(bus, card, codec_addr, *codecp, true);
+ if (ret)
+ put_device(hda_codec_dev(*codecp));
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_new);
+
+int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card,
+ unsigned int codec_addr, struct hda_codec *codec,
+ bool snddev_managed)
+{
+ char component[31];
+ hda_nid_t fg;
+ int err;
+ static const struct snd_device_ops dev_ops = {
+ .dev_register = snd_hda_codec_dev_register,
+ .dev_free = snd_hda_codec_dev_free,
+ };
+
+ dev_dbg(card->dev, "%s: entry\n", __func__);
+
+ if (snd_BUG_ON(!bus))
+ return -EINVAL;
+ if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS))
+ return -EINVAL;
+
+ codec->core.exec_verb = codec_exec_verb;
+ codec->card = card;
+ codec->addr = codec_addr;
+
+ codec->power_jiffies = jiffies;
+
+ snd_hda_sysfs_init(codec);
+
+ if (codec->bus->modelname) {
+ codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
+ if (!codec->modelname)
+ return -ENOMEM;
+ }
+
+ fg = codec->core.afg ? codec->core.afg : codec->core.mfg;
+ err = read_widget_caps(codec, fg);
+ if (err < 0)
+ return err;
+ err = read_pin_defaults(codec);
+ if (err < 0)
+ return err;
+
+ /* power-up all before initialization */
+ hda_set_power_state(codec, AC_PWRST_D0);
+ codec->core.dev.power.power_state = PMSG_ON;
+
+ snd_hda_codec_proc_new(codec);
+
+ snd_hda_create_hwdep(codec);
+
+ sprintf(component, "HDA:%08x,%08x,%08x", codec->core.vendor_id,
+ codec->core.subsystem_id, codec->core.revision_id);
+ snd_component_add(card, component);
+
+ if (snddev_managed) {
+ /* ASoC features component management instead */
+ err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops);
+ if (err < 0)
+ return err;
+ }
+
+#ifdef CONFIG_PM
+ /* PM runtime needs to be enabled later after binding codec */
+ if (codec->core.dev.power.runtime_auto)
+ pm_runtime_forbid(&codec->core.dev);
+ else
+ /* Keep the usage_count consistent across subsequent probing */
+ pm_runtime_get_noresume(&codec->core.dev);
+#endif
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_device_new);
+
+/**
+ * snd_hda_codec_update_widgets - Refresh widget caps and pin defaults
+ * @codec: the HDA codec
+ *
+ * Forcibly refresh the all widget caps and the init pin configurations of
+ * the given codec.
+ */
+int snd_hda_codec_update_widgets(struct hda_codec *codec)
+{
+ hda_nid_t fg;
+ int err;
+
+ err = snd_hdac_refresh_widgets(&codec->core);
+ if (err < 0)
+ return err;
+
+ /* Assume the function group node does not change,
+ * only the widget nodes may change.
+ */
+ kfree(codec->wcaps);
+ fg = codec->core.afg ? codec->core.afg : codec->core.mfg;
+ err = read_widget_caps(codec, fg);
+ if (err < 0)
+ return err;
+
+ snd_array_free(&codec->init_pins);
+ err = read_pin_defaults(codec);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_update_widgets);
+
+/* update the stream-id if changed */
+static void update_pcm_stream_id(struct hda_codec *codec,
+ struct hda_cvt_setup *p, hda_nid_t nid,
+ u32 stream_tag, int channel_id)
+{
+ unsigned int oldval, newval;
+
+ if (p->stream_tag != stream_tag || p->channel_id != channel_id) {
+ oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+ newval = (stream_tag << 4) | channel_id;
+ if (oldval != newval)
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_CHANNEL_STREAMID,
+ newval);
+ p->stream_tag = stream_tag;
+ p->channel_id = channel_id;
+ }
+}
+
+/* update the format-id if changed */
+static void update_pcm_format(struct hda_codec *codec, struct hda_cvt_setup *p,
+ hda_nid_t nid, int format)
+{
+ unsigned int oldval;
+
+ if (p->format_id != format) {
+ oldval = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_STREAM_FORMAT, 0);
+ if (oldval != format) {
+ msleep(1);
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_STREAM_FORMAT,
+ format);
+ }
+ p->format_id = format;
+ }
+}
+
+/**
+ * snd_hda_codec_setup_stream - set up the codec for streaming
+ * @codec: the CODEC to set up
+ * @nid: the NID to set up
+ * @stream_tag: stream tag to pass, it's between 0x1 and 0xf.
+ * @channel_id: channel id to pass, zero based.
+ * @format: stream format.
+ */
+void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
+ u32 stream_tag,
+ int channel_id, int format)
+{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+ struct hda_codec *c;
+ struct hda_cvt_setup *p;
+ int type;
+ int i;
+
+ if (!nid)
+ return;
+
+ codec_dbg(codec,
+ "hda_codec_setup_stream: NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
+ nid, stream_tag, channel_id, format);
+ p = get_hda_cvt_setup(codec, nid);
+ if (!p)
+ return;
+
+ if (driver->ops->stream_pm)
+ driver->ops->stream_pm(codec, nid, true);
+ if (codec->pcm_format_first)
+ update_pcm_format(codec, p, nid, format);
+ update_pcm_stream_id(codec, p, nid, stream_tag, channel_id);
+ if (!codec->pcm_format_first)
+ update_pcm_format(codec, p, nid, format);
+
+ p->active = 1;
+ p->dirty = 0;
+
+ /* make other inactive cvts with the same stream-tag dirty */
+ type = get_wcaps_type(get_wcaps(codec, nid));
+ list_for_each_codec(c, codec->bus) {
+ snd_array_for_each(&c->cvt_setups, i, p) {
+ if (!p->active && p->stream_tag == stream_tag &&
+ get_wcaps_type(get_wcaps(c, p->nid)) == type)
+ p->dirty = 1;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_setup_stream);
+
+static void really_cleanup_stream(struct hda_codec *codec,
+ struct hda_cvt_setup *q);
+
+/**
+ * __snd_hda_codec_cleanup_stream - clean up the codec for closing
+ * @codec: the CODEC to clean up
+ * @nid: the NID to clean up
+ * @do_now: really clean up the stream instead of clearing the active flag
+ */
+void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
+ int do_now)
+{
+ struct hda_cvt_setup *p;
+
+ if (!nid)
+ return;
+
+ if (codec->no_sticky_stream)
+ do_now = 1;
+
+ codec_dbg(codec, "hda_codec_cleanup_stream: NID=0x%x\n", nid);
+ p = get_hda_cvt_setup(codec, nid);
+ if (p) {
+ /* here we just clear the active flag when do_now isn't set;
+ * actual clean-ups will be done later in
+ * purify_inactive_streams() called from snd_hda_codec_prpapre()
+ */
+ if (do_now)
+ really_cleanup_stream(codec, p);
+ else
+ p->active = 0;
+ }
+}
+EXPORT_SYMBOL_GPL(__snd_hda_codec_cleanup_stream);
+
+static void really_cleanup_stream(struct hda_codec *codec,
+ struct hda_cvt_setup *q)
+{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+ hda_nid_t nid = q->nid;
+
+ if (q->stream_tag || q->channel_id)
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+ if (q->format_id)
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0
+);
+ memset(q, 0, sizeof(*q));
+ q->nid = nid;
+ if (driver->ops->stream_pm)
+ driver->ops->stream_pm(codec, nid, false);
+}
+
+/* clean up the all conflicting obsolete streams */
+static void purify_inactive_streams(struct hda_codec *codec)
+{
+ struct hda_codec *c;
+ struct hda_cvt_setup *p;
+ int i;
+
+ list_for_each_codec(c, codec->bus) {
+ snd_array_for_each(&c->cvt_setups, i, p) {
+ if (p->dirty)
+ really_cleanup_stream(c, p);
+ }
+ }
+}
+
+/* clean up all streams; called from suspend */
+static void hda_cleanup_all_streams(struct hda_codec *codec)
+{
+ struct hda_cvt_setup *p;
+ int i;
+
+ snd_array_for_each(&codec->cvt_setups, i, p) {
+ if (p->stream_tag)
+ really_cleanup_stream(codec, p);
+ }
+}
+
+/*
+ * amp access functions
+ */
+
+/**
+ * query_amp_caps - query AMP capabilities
+ * @codec: the HD-auio codec
+ * @nid: the NID to query
+ * @direction: either #HDA_INPUT or #HDA_OUTPUT
+ *
+ * Query AMP capabilities for the given widget and direction.
+ * Returns the obtained capability bits.
+ *
+ * When cap bits have been already read, this doesn't read again but
+ * returns the cached value.
+ */
+u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
+{
+ if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
+ nid = codec->core.afg;
+ return snd_hda_param_read(codec, nid,
+ direction == HDA_OUTPUT ?
+ AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+}
+EXPORT_SYMBOL_GPL(query_amp_caps);
+
+/**
+ * snd_hda_check_amp_caps - query AMP capabilities
+ * @codec: the HD-audio codec
+ * @nid: the NID to query
+ * @dir: either #HDA_INPUT or #HDA_OUTPUT
+ * @bits: bit mask to check the result
+ *
+ * Check whether the widget has the given amp capability for the direction.
+ */
+bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
+ int dir, unsigned int bits)
+{
+ if (!nid)
+ return false;
+ if (get_wcaps(codec, nid) & (1 << (dir + 1)))
+ if (query_amp_caps(codec, nid, dir) & bits)
+ return true;
+ return false;
+}
+EXPORT_SYMBOL_GPL(snd_hda_check_amp_caps);
+
+/**
+ * snd_hda_override_amp_caps - Override the AMP capabilities
+ * @codec: the CODEC to clean up
+ * @nid: the NID to clean up
+ * @dir: either #HDA_INPUT or #HDA_OUTPUT
+ * @caps: the capability bits to set
+ *
+ * Override the cached AMP caps bits value by the given one.
+ * This function is useful if the driver needs to adjust the AMP ranges,
+ * e.g. limit to 0dB, etc.
+ *
+ * Returns zero if successful or a negative error code.
+ */
+int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
+ unsigned int caps)
+{
+ unsigned int parm;
+
+ snd_hda_override_wcaps(codec, nid,
+ get_wcaps(codec, nid) | AC_WCAP_AMP_OVRD);
+ parm = dir == HDA_OUTPUT ? AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP;
+ return snd_hdac_override_parm(&codec->core, nid, parm, caps);
+}
+EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps);
+
+static unsigned int encode_amp(struct hda_codec *codec, hda_nid_t nid,
+ int ch, int dir, int idx)
+{
+ unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx);
+
+ /* enable fake mute if no h/w mute but min=mute */
+ if ((query_amp_caps(codec, nid, dir) &
+ (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)) == AC_AMPCAP_MIN_MUTE)
+ cmd |= AC_AMP_FAKE_MUTE;
+ return cmd;
+}
+
+/**
+ * snd_hda_codec_amp_update - update the AMP mono value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel to update (0 or 1)
+ * @dir: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the AMP values for the given channel, direction and index.
+ */
+int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid,
+ int ch, int dir, int idx, int mask, int val)
+{
+ unsigned int cmd = encode_amp(codec, nid, ch, dir, idx);
+
+ return snd_hdac_regmap_update_raw(&codec->core, cmd, mask, val);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update);
+
+/**
+ * snd_hda_codec_amp_stereo - update the AMP stereo values
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the AMP values like snd_hda_codec_amp_update(), but for a
+ * stereo widget with the same mask and value.
+ */
+int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
+ int direction, int idx, int mask, int val)
+{
+ int ch, ret = 0;
+
+ if (snd_BUG_ON(mask & ~0xff))
+ mask &= 0xff;
+ for (ch = 0; ch < 2; ch++)
+ ret |= snd_hda_codec_amp_update(codec, nid, ch, direction,
+ idx, mask, val);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_amp_stereo);
+
+/**
+ * snd_hda_codec_amp_init - initialize the AMP value
+ * @codec: the HDA codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @dir: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Works like snd_hda_codec_amp_update() but it writes the value only at
+ * the first access. If the amp was already initialized / updated beforehand,
+ * this does nothing.
+ */
+int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
+ int dir, int idx, int mask, int val)
+{
+ unsigned int cmd = encode_amp(codec, nid, ch, dir, idx);
+
+ if (!codec->core.regmap)
+ return -EINVAL;
+ return snd_hdac_regmap_update_raw_once(&codec->core, cmd, mask, val);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init);
+
+/**
+ * snd_hda_codec_amp_init_stereo - initialize the stereo AMP value
+ * @codec: the HDA codec
+ * @nid: NID to read the AMP value
+ * @dir: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Call snd_hda_codec_amp_init() for both stereo channels.
+ */
+int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
+ int dir, int idx, int mask, int val)
+{
+ int ch, ret = 0;
+
+ if (snd_BUG_ON(mask & ~0xff))
+ mask &= 0xff;
+ for (ch = 0; ch < 2; ch++)
+ ret |= snd_hda_codec_amp_init(codec, nid, ch, dir,
+ idx, mask, val);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init_stereo);
+
+static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
+ unsigned int ofs)
+{
+ u32 caps = query_amp_caps(codec, nid, dir);
+ /* get num steps */
+ caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+ if (ofs < caps)
+ caps -= ofs;
+ return caps;
+}
+
+/**
+ * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer
+ * @kcontrol: referred ctl element
+ * @uinfo: pointer to get/store the data
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
+int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ u16 nid = get_amp_nid(kcontrol);
+ u8 chs = get_amp_channels(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ unsigned int ofs = get_amp_offset(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = get_amp_max_value(codec, nid, dir, ofs);
+ if (!uinfo->value.integer.max) {
+ codec_warn(codec,
+ "num_steps = 0 for NID=0x%x (ctl = %s)\n",
+ nid, kcontrol->id.name);
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_info);
+
+
+static inline unsigned int
+read_amp_value(struct hda_codec *codec, hda_nid_t nid,
+ int ch, int dir, int idx, unsigned int ofs)
+{
+ unsigned int val;
+ val = snd_hda_codec_amp_read(codec, nid, ch, dir, idx);
+ val &= HDA_AMP_VOLMASK;
+ if (val >= ofs)
+ val -= ofs;
+ else
+ val = 0;
+ return val;
+}
+
+static inline int
+update_amp_value(struct hda_codec *codec, hda_nid_t nid,
+ int ch, int dir, int idx, unsigned int ofs,
+ unsigned int val)
+{
+ unsigned int maxval;
+
+ if (val > 0)
+ val += ofs;
+ /* ofs = 0: raw max value */
+ maxval = get_amp_max_value(codec, nid, dir, 0);
+ if (val > maxval)
+ return -EINVAL;
+ return snd_hda_codec_amp_update(codec, nid, ch, dir, idx,
+ HDA_AMP_VOLMASK, val);
+}
+
+/**
+ * snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume
+ * @kcontrol: ctl element
+ * @ucontrol: pointer to get/store the data
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
+int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int chs = get_amp_channels(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ int idx = get_amp_index(kcontrol);
+ unsigned int ofs = get_amp_offset(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+
+ if (chs & 1)
+ *valp++ = read_amp_value(codec, nid, 0, dir, idx, ofs);
+ if (chs & 2)
+ *valp = read_amp_value(codec, nid, 1, dir, idx, ofs);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_get);
+
+/**
+ * snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume
+ * @kcontrol: ctl element
+ * @ucontrol: pointer to get/store the data
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
+int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int chs = get_amp_channels(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ int idx = get_amp_index(kcontrol);
+ unsigned int ofs = get_amp_offset(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int change = 0;
+ int err;
+
+ if (chs & 1) {
+ err = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp);
+ if (err < 0)
+ return err;
+ change |= err;
+ valp++;
+ }
+ if (chs & 2) {
+ err = update_amp_value(codec, nid, 1, dir, idx, ofs, *valp);
+ if (err < 0)
+ return err;
+ change |= err;
+ }
+ return change;
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_put);
+
+/* inquiry the amp caps and convert to TLV */
+static void get_ctl_amp_tlv(struct snd_kcontrol *kcontrol, unsigned int *tlv)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ unsigned int ofs = get_amp_offset(kcontrol);
+ bool min_mute = get_amp_min_mute(kcontrol);
+ u32 caps, val1, val2;
+
+ caps = query_amp_caps(codec, nid, dir);
+ val2 = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
+ val2 = (val2 + 1) * 25;
+ val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
+ val1 += ofs;
+ val1 = ((int)val1) * ((int)val2);
+ if (min_mute || (caps & AC_AMPCAP_MIN_MUTE))
+ val2 |= TLV_DB_SCALE_MUTE;
+ tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_SCALE;
+ tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int);
+ tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = val1;
+ tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = val2;
+}
+
+/**
+ * snd_hda_mixer_amp_tlv - TLV callback for a standard AMP mixer volume
+ * @kcontrol: ctl element
+ * @op_flag: operation flag
+ * @size: byte size of input TLV
+ * @_tlv: TLV data
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
+int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *_tlv)
+{
+ unsigned int tlv[4];
+
+ if (size < 4 * sizeof(unsigned int))
+ return -ENOMEM;
+ get_ctl_amp_tlv(kcontrol, tlv);
+ if (copy_to_user(_tlv, tlv, sizeof(tlv)))
+ return -EFAULT;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_tlv);
+
+/**
+ * snd_hda_set_vmaster_tlv - Set TLV for a virtual master control
+ * @codec: HD-audio codec
+ * @nid: NID of a reference widget
+ * @dir: #HDA_INPUT or #HDA_OUTPUT
+ * @tlv: TLV data to be stored, at least 4 elements
+ *
+ * Set (static) TLV data for a virtual master volume using the AMP caps
+ * obtained from the reference NID.
+ * The volume range is recalculated as if the max volume is 0dB.
+ */
+void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
+ unsigned int *tlv)
+{
+ u32 caps;
+ int nums, step;
+
+ caps = query_amp_caps(codec, nid, dir);
+ nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+ step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
+ step = (step + 1) * 25;
+ tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_SCALE;
+ tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int);
+ tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = -nums * step;
+ tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = step;
+}
+EXPORT_SYMBOL_GPL(snd_hda_set_vmaster_tlv);
+
+/* find a mixer control element with the given name */
+static struct snd_kcontrol *
+find_mixer_ctl(struct hda_codec *codec, const char *name, int dev, int idx)
+{
+ struct snd_ctl_elem_id id;
+ memset(&id, 0, sizeof(id));
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ id.device = dev;
+ id.index = idx;
+ if (snd_BUG_ON(strlen(name) >= sizeof(id.name)))
+ return NULL;
+ strscpy(id.name, name);
+ return snd_ctl_find_id(codec->card, &id);
+}
+
+/**
+ * snd_hda_find_mixer_ctl - Find a mixer control element with the given name
+ * @codec: HD-audio codec
+ * @name: ctl id name string
+ *
+ * Get the control element with the given id string and IFACE_MIXER.
+ */
+struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
+ const char *name)
+{
+ return find_mixer_ctl(codec, name, 0, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hda_find_mixer_ctl);
+
+static int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name,
+ int start_idx)
+{
+ int i, idx;
+ /* 16 ctlrs should be large enough */
+ for (i = 0, idx = start_idx; i < 16; i++, idx++) {
+ if (!find_mixer_ctl(codec, name, 0, idx))
+ return idx;
+ }
+ return -EBUSY;
+}
+
+/**
+ * snd_hda_ctl_add - Add a control element and assign to the codec
+ * @codec: HD-audio codec
+ * @nid: corresponding NID (optional)
+ * @kctl: the control element to assign
+ *
+ * Add the given control element to an array inside the codec instance.
+ * All control elements belonging to a codec are supposed to be added
+ * by this function so that a proper clean-up works at the free or
+ * reconfiguration time.
+ *
+ * If non-zero @nid is passed, the NID is assigned to the control element.
+ * The assignment is shown in the codec proc file.
+ *
+ * snd_hda_ctl_add() checks the control subdev id field whether
+ * #HDA_SUBDEV_NID_FLAG bit is set. If set (and @nid is zero), the lower
+ * bits value is taken as the NID to assign. The #HDA_NID_ITEM_AMP bit
+ * specifies if kctl->private_value is a HDA amplifier value.
+ */
+int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
+ struct snd_kcontrol *kctl)
+{
+ int err;
+ unsigned short flags = 0;
+ struct hda_nid_item *item;
+
+ if (kctl->id.subdevice & HDA_SUBDEV_AMP_FLAG) {
+ flags |= HDA_NID_ITEM_AMP;
+ if (nid == 0)
+ nid = get_amp_nid_(kctl->private_value);
+ }
+ if ((kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) != 0 && nid == 0)
+ nid = kctl->id.subdevice & 0xffff;
+ if (kctl->id.subdevice & (HDA_SUBDEV_NID_FLAG|HDA_SUBDEV_AMP_FLAG))
+ kctl->id.subdevice = 0;
+ err = snd_ctl_add(codec->card, kctl);
+ if (err < 0)
+ return err;
+ item = snd_array_new(&codec->mixers);
+ if (!item)
+ return -ENOMEM;
+ item->kctl = kctl;
+ item->nid = nid;
+ item->flags = flags;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_ctl_add);
+
+/**
+ * snd_hda_ctls_clear - Clear all controls assigned to the given codec
+ * @codec: HD-audio codec
+ */
+void snd_hda_ctls_clear(struct hda_codec *codec)
+{
+ int i;
+ struct hda_nid_item *items = codec->mixers.list;
+
+ for (i = 0; i < codec->mixers.used; i++)
+ snd_ctl_remove(codec->card, items[i].kctl);
+ snd_array_free(&codec->mixers);
+ snd_array_free(&codec->nids);
+}
+
+/**
+ * snd_hda_lock_devices - pseudo device locking
+ * @bus: the BUS
+ *
+ * toggle card->shutdown to allow/disallow the device access (as a hack)
+ */
+int snd_hda_lock_devices(struct hda_bus *bus)
+{
+ struct snd_card *card = bus->card;
+ struct hda_codec *codec;
+
+ guard(spinlock)(&card->files_lock);
+ if (card->shutdown)
+ return -EINVAL;
+ card->shutdown = 1;
+ if (!list_empty(&card->ctl_files))
+ goto err_clear;
+
+ list_for_each_codec(codec, bus) {
+ struct hda_pcm *cpcm;
+ list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
+ if (!cpcm->pcm)
+ continue;
+ if (cpcm->pcm->streams[0].substream_opened ||
+ cpcm->pcm->streams[1].substream_opened)
+ goto err_clear;
+ }
+ }
+ return 0;
+
+ err_clear:
+ card->shutdown = 0;
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_lock_devices);
+
+/**
+ * snd_hda_unlock_devices - pseudo device unlocking
+ * @bus: the BUS
+ */
+void snd_hda_unlock_devices(struct hda_bus *bus)
+{
+ struct snd_card *card = bus->card;
+
+ guard(spinlock)(&card->files_lock);
+ card->shutdown = 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_unlock_devices);
+
+/**
+ * snd_hda_codec_reset - Clear all objects assigned to the codec
+ * @codec: HD-audio codec
+ *
+ * This frees the all PCM and control elements assigned to the codec, and
+ * clears the caches and restores the pin default configurations.
+ *
+ * When a device is being used, it returns -EBSY. If successfully freed,
+ * returns zero.
+ */
+int snd_hda_codec_reset(struct hda_codec *codec)
+{
+ struct hda_bus *bus = codec->bus;
+
+ if (snd_hda_lock_devices(bus) < 0)
+ return -EBUSY;
+
+ /* OK, let it free */
+ device_release_driver(hda_codec_dev(codec));
+
+ /* allow device access again */
+ snd_hda_unlock_devices(bus);
+ return 0;
+}
+
+typedef int (*map_follower_func_t)(struct hda_codec *, void *, struct snd_kcontrol *);
+
+/* apply the function to all matching follower ctls in the mixer list */
+static int map_followers(struct hda_codec *codec, const char * const *followers,
+ const char *suffix, map_follower_func_t func, void *data)
+{
+ struct hda_nid_item *items;
+ const char * const *s;
+ int i, err;
+
+ items = codec->mixers.list;
+ for (i = 0; i < codec->mixers.used; i++) {
+ struct snd_kcontrol *sctl = items[i].kctl;
+ if (!sctl || sctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER)
+ continue;
+ for (s = followers; *s; s++) {
+ char tmpname[sizeof(sctl->id.name)];
+ const char *name = *s;
+ if (suffix) {
+ snprintf(tmpname, sizeof(tmpname), "%s %s",
+ name, suffix);
+ name = tmpname;
+ }
+ if (!strcmp(sctl->id.name, name)) {
+ err = func(codec, data, sctl);
+ if (err)
+ return err;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int check_follower_present(struct hda_codec *codec,
+ void *data, struct snd_kcontrol *sctl)
+{
+ return 1;
+}
+
+/* call kctl->put with the given value(s) */
+static int put_kctl_with_value(struct snd_kcontrol *kctl, int val)
+{
+ struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL;
+
+ ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL);
+ if (!ucontrol)
+ return -ENOMEM;
+ ucontrol->value.integer.value[0] = val;
+ ucontrol->value.integer.value[1] = val;
+ kctl->put(kctl, ucontrol);
+ return 0;
+}
+
+struct follower_init_arg {
+ struct hda_codec *codec;
+ int step;
+};
+
+/* initialize the follower volume with 0dB via snd_ctl_apply_vmaster_followers() */
+static int init_follower_0dB(struct snd_kcontrol *follower,
+ struct snd_kcontrol *kctl,
+ void *_arg)
+{
+ struct follower_init_arg *arg = _arg;
+ int _tlv[4];
+ const int *tlv = NULL;
+ int step;
+ int val;
+
+ if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
+ if (kctl->tlv.c != snd_hda_mixer_amp_tlv) {
+ codec_err(arg->codec,
+ "Unexpected TLV callback for follower %s:%d\n",
+ kctl->id.name, kctl->id.index);
+ return 0; /* ignore */
+ }
+ get_ctl_amp_tlv(kctl, _tlv);
+ tlv = _tlv;
+ } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ)
+ tlv = kctl->tlv.p;
+
+ if (!tlv || tlv[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE)
+ return 0;
+
+ step = tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP];
+ step &= ~TLV_DB_SCALE_MUTE;
+ if (!step)
+ return 0;
+ if (arg->step && arg->step != step) {
+ codec_err(arg->codec,
+ "Mismatching dB step for vmaster follower (%d!=%d)\n",
+ arg->step, step);
+ return 0;
+ }
+
+ arg->step = step;
+ val = -tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] / step;
+ if (val > 0) {
+ put_kctl_with_value(follower, val);
+ return val;
+ }
+
+ return 0;
+}
+
+/* unmute the follower via snd_ctl_apply_vmaster_followers() */
+static int init_follower_unmute(struct snd_kcontrol *follower,
+ struct snd_kcontrol *kctl,
+ void *_arg)
+{
+ return put_kctl_with_value(follower, 1);
+}
+
+static int add_follower(struct hda_codec *codec,
+ void *data, struct snd_kcontrol *follower)
+{
+ return snd_ctl_add_follower(data, follower);
+}
+
+/**
+ * __snd_hda_add_vmaster - create a virtual master control and add followers
+ * @codec: HD-audio codec
+ * @name: vmaster control name
+ * @tlv: TLV data (optional)
+ * @followers: follower control names (optional)
+ * @suffix: suffix string to each follower name (optional)
+ * @init_follower_vol: initialize followers to unmute/0dB
+ * @access: kcontrol access rights
+ * @ctl_ret: store the vmaster kcontrol in return
+ *
+ * Create a virtual master control with the given name. The TLV data
+ * must be either NULL or a valid data.
+ *
+ * @followers is a NULL-terminated array of strings, each of which is a
+ * follower control name. All controls with these names are assigned to
+ * the new virtual master control.
+ *
+ * This function returns zero if successful or a negative error code.
+ */
+int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
+ unsigned int *tlv, const char * const *followers,
+ const char *suffix, bool init_follower_vol,
+ unsigned int access, struct snd_kcontrol **ctl_ret)
+{
+ struct snd_kcontrol *kctl;
+ int err;
+
+ if (ctl_ret)
+ *ctl_ret = NULL;
+
+ err = map_followers(codec, followers, suffix, check_follower_present, NULL);
+ if (err != 1) {
+ codec_dbg(codec, "No follower found for %s\n", name);
+ return 0;
+ }
+ kctl = snd_ctl_make_virtual_master(name, tlv);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->vd[0].access |= access;
+ err = snd_hda_ctl_add(codec, 0, kctl);
+ if (err < 0)
+ return err;
+
+ err = map_followers(codec, followers, suffix, add_follower, kctl);
+ if (err < 0)
+ return err;
+
+ /* init with master mute & zero volume */
+ put_kctl_with_value(kctl, 0);
+ if (init_follower_vol) {
+ struct follower_init_arg arg = {
+ .codec = codec,
+ .step = 0,
+ };
+ snd_ctl_apply_vmaster_followers(kctl,
+ tlv ? init_follower_0dB : init_follower_unmute,
+ &arg);
+ }
+
+ if (ctl_ret)
+ *ctl_ret = kctl;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__snd_hda_add_vmaster);
+
+/* meta hook to call each driver's vmaster hook */
+static void vmaster_hook(void *private_data, int enabled)
+{
+ struct hda_vmaster_mute_hook *hook = private_data;
+
+ hook->hook(hook->codec, enabled);
+}
+
+/**
+ * snd_hda_add_vmaster_hook - Add a vmaster hw specific hook
+ * @codec: the HDA codec
+ * @hook: the vmaster hook object
+ *
+ * Add a hw specific hook (like EAPD) with the given vmaster switch kctl.
+ */
+int snd_hda_add_vmaster_hook(struct hda_codec *codec,
+ struct hda_vmaster_mute_hook *hook)
+{
+ if (!hook->hook || !hook->sw_kctl)
+ return 0;
+ hook->codec = codec;
+ snd_ctl_add_vmaster_hook(hook->sw_kctl, vmaster_hook, hook);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_add_vmaster_hook);
+
+/**
+ * snd_hda_sync_vmaster_hook - Sync vmaster hook
+ * @hook: the vmaster hook
+ *
+ * Call the hook with the current value for synchronization.
+ * Should be called in init callback.
+ */
+void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook)
+{
+ if (!hook->hook || !hook->codec)
+ return;
+ /* don't call vmaster hook in the destructor since it might have
+ * been already destroyed
+ */
+ if (hook->codec->bus->shutdown)
+ return;
+ snd_ctl_sync_vmaster_hook(hook->sw_kctl);
+}
+EXPORT_SYMBOL_GPL(snd_hda_sync_vmaster_hook);
+
+
+/**
+ * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch
+ * @kcontrol: referred ctl element
+ * @uinfo: pointer to get/store the data
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
+int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int chs = get_amp_channels(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = chs == 3 ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_info);
+
+/**
+ * snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch
+ * @kcontrol: ctl element
+ * @ucontrol: pointer to get/store the data
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
+int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int chs = get_amp_channels(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ int idx = get_amp_index(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+
+ if (chs & 1)
+ *valp++ = (snd_hda_codec_amp_read(codec, nid, 0, dir, idx) &
+ HDA_AMP_MUTE) ? 0 : 1;
+ if (chs & 2)
+ *valp = (snd_hda_codec_amp_read(codec, nid, 1, dir, idx) &
+ HDA_AMP_MUTE) ? 0 : 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get);
+
+/**
+ * snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch
+ * @kcontrol: ctl element
+ * @ucontrol: pointer to get/store the data
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
+int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = get_amp_nid(kcontrol);
+ int chs = get_amp_channels(kcontrol);
+ int dir = get_amp_direction(kcontrol);
+ int idx = get_amp_index(kcontrol);
+ long *valp = ucontrol->value.integer.value;
+ int change = 0;
+
+ if (chs & 1) {
+ if (*valp < 0 || *valp > 1)
+ return -EINVAL;
+ change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
+ HDA_AMP_MUTE,
+ *valp ? 0 : HDA_AMP_MUTE);
+ valp++;
+ }
+ if (chs & 2) {
+ if (*valp < 0 || *valp > 1)
+ return -EINVAL;
+ change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
+ HDA_AMP_MUTE,
+ *valp ? 0 : HDA_AMP_MUTE);
+ }
+ hda_call_check_power_status(codec, nid);
+ return change;
+}
+EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put);
+
+/*
+ * SPDIF out controls
+ */
+
+static int snd_hda_spdif_mask_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_hda_spdif_cmask_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
+ IEC958_AES0_NONAUDIO |
+ IEC958_AES0_CON_EMPHASIS_5015 |
+ IEC958_AES0_CON_NOT_COPYRIGHT;
+ ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY |
+ IEC958_AES1_CON_ORIGINAL;
+ return 0;
+}
+
+static int snd_hda_spdif_pmask_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
+ IEC958_AES0_NONAUDIO |
+ IEC958_AES0_PRO_EMPHASIS_5015;
+ return 0;
+}
+
+static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ int idx = kcontrol->private_value;
+ struct hda_spdif_out *spdif;
+
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return -EINVAL;
+ guard(mutex)(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ ucontrol->value.iec958.status[0] = spdif->status & 0xff;
+ ucontrol->value.iec958.status[1] = (spdif->status >> 8) & 0xff;
+ ucontrol->value.iec958.status[2] = (spdif->status >> 16) & 0xff;
+ ucontrol->value.iec958.status[3] = (spdif->status >> 24) & 0xff;
+
+ return 0;
+}
+
+/* convert from SPDIF status bits to HDA SPDIF bits
+ * bit 0 (DigEn) is always set zero (to be filled later)
+ */
+static unsigned short convert_from_spdif_status(unsigned int sbits)
+{
+ unsigned short val = 0;
+
+ if (sbits & IEC958_AES0_PROFESSIONAL)
+ val |= AC_DIG1_PROFESSIONAL;
+ if (sbits & IEC958_AES0_NONAUDIO)
+ val |= AC_DIG1_NONAUDIO;
+ if (sbits & IEC958_AES0_PROFESSIONAL) {
+ if ((sbits & IEC958_AES0_PRO_EMPHASIS) ==
+ IEC958_AES0_PRO_EMPHASIS_5015)
+ val |= AC_DIG1_EMPHASIS;
+ } else {
+ if ((sbits & IEC958_AES0_CON_EMPHASIS) ==
+ IEC958_AES0_CON_EMPHASIS_5015)
+ val |= AC_DIG1_EMPHASIS;
+ if (!(sbits & IEC958_AES0_CON_NOT_COPYRIGHT))
+ val |= AC_DIG1_COPYRIGHT;
+ if (sbits & (IEC958_AES1_CON_ORIGINAL << 8))
+ val |= AC_DIG1_LEVEL;
+ val |= sbits & (IEC958_AES1_CON_CATEGORY << 8);
+ }
+ return val;
+}
+
+/* convert to SPDIF status bits from HDA SPDIF bits
+ */
+static unsigned int convert_to_spdif_status(unsigned short val)
+{
+ unsigned int sbits = 0;
+
+ if (val & AC_DIG1_NONAUDIO)
+ sbits |= IEC958_AES0_NONAUDIO;
+ if (val & AC_DIG1_PROFESSIONAL)
+ sbits |= IEC958_AES0_PROFESSIONAL;
+ if (sbits & IEC958_AES0_PROFESSIONAL) {
+ if (val & AC_DIG1_EMPHASIS)
+ sbits |= IEC958_AES0_PRO_EMPHASIS_5015;
+ } else {
+ if (val & AC_DIG1_EMPHASIS)
+ sbits |= IEC958_AES0_CON_EMPHASIS_5015;
+ if (!(val & AC_DIG1_COPYRIGHT))
+ sbits |= IEC958_AES0_CON_NOT_COPYRIGHT;
+ if (val & AC_DIG1_LEVEL)
+ sbits |= (IEC958_AES1_CON_ORIGINAL << 8);
+ sbits |= val & (0x7f << 8);
+ }
+ return sbits;
+}
+
+/* set digital convert verbs both for the given NID and its followers */
+static void set_dig_out(struct hda_codec *codec, hda_nid_t nid,
+ int mask, int val)
+{
+ const hda_nid_t *d;
+
+ snd_hdac_regmap_update(&codec->core, nid, AC_VERB_SET_DIGI_CONVERT_1,
+ mask, val);
+ d = codec->follower_dig_outs;
+ if (!d)
+ return;
+ for (; *d; d++)
+ snd_hdac_regmap_update(&codec->core, *d,
+ AC_VERB_SET_DIGI_CONVERT_1, mask, val);
+}
+
+static inline void set_dig_out_convert(struct hda_codec *codec, hda_nid_t nid,
+ int dig1, int dig2)
+{
+ unsigned int mask = 0;
+ unsigned int val = 0;
+
+ if (dig1 != -1) {
+ mask |= 0xff;
+ val = dig1;
+ }
+ if (dig2 != -1) {
+ mask |= 0xff00;
+ val |= dig2 << 8;
+ }
+ set_dig_out(codec, nid, mask, val);
+}
+
+static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ int idx = kcontrol->private_value;
+ struct hda_spdif_out *spdif;
+ hda_nid_t nid;
+ unsigned short val;
+ int change;
+
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return -EINVAL;
+ guard(mutex)(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ nid = spdif->nid;
+ spdif->status = ucontrol->value.iec958.status[0] |
+ ((unsigned int)ucontrol->value.iec958.status[1] << 8) |
+ ((unsigned int)ucontrol->value.iec958.status[2] << 16) |
+ ((unsigned int)ucontrol->value.iec958.status[3] << 24);
+ val = convert_from_spdif_status(spdif->status);
+ val |= spdif->ctls & 1;
+ change = spdif->ctls != val;
+ spdif->ctls = val;
+ if (change && nid != (u16)-1)
+ set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff);
+ return change;
+}
+
+#define snd_hda_spdif_out_switch_info snd_ctl_boolean_mono_info
+
+static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ int idx = kcontrol->private_value;
+ struct hda_spdif_out *spdif;
+
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return -EINVAL;
+ guard(mutex)(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ ucontrol->value.integer.value[0] = spdif->ctls & AC_DIG1_ENABLE;
+ return 0;
+}
+
+static inline void set_spdif_ctls(struct hda_codec *codec, hda_nid_t nid,
+ int dig1, int dig2)
+{
+ set_dig_out_convert(codec, nid, dig1, dig2);
+ /* unmute amp switch (if any) */
+ if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
+ (dig1 & AC_DIG1_ENABLE))
+ snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, 0);
+}
+
+static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ int idx = kcontrol->private_value;
+ struct hda_spdif_out *spdif;
+ hda_nid_t nid;
+ unsigned short val;
+ int change;
+
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return -EINVAL;
+ guard(mutex)(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ nid = spdif->nid;
+ val = spdif->ctls & ~AC_DIG1_ENABLE;
+ if (ucontrol->value.integer.value[0])
+ val |= AC_DIG1_ENABLE;
+ change = spdif->ctls != val;
+ spdif->ctls = val;
+ if (change && nid != (u16)-1)
+ set_spdif_ctls(codec, nid, val & 0xff, -1);
+ return change;
+}
+
+static const struct snd_kcontrol_new dig_mixes[] = {
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
+ .info = snd_hda_spdif_mask_info,
+ .get = snd_hda_spdif_cmask_get,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),
+ .info = snd_hda_spdif_mask_info,
+ .get = snd_hda_spdif_pmask_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+ .info = snd_hda_spdif_mask_info,
+ .get = snd_hda_spdif_default_get,
+ .put = snd_hda_spdif_default_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
+ .info = snd_hda_spdif_out_switch_info,
+ .get = snd_hda_spdif_out_switch_get,
+ .put = snd_hda_spdif_out_switch_put,
+ },
+ { } /* end */
+};
+
+/**
+ * snd_hda_create_dig_out_ctls - create Output SPDIF-related controls
+ * @codec: the HDA codec
+ * @associated_nid: NID that new ctls associated with
+ * @cvt_nid: converter NID
+ * @type: HDA_PCM_TYPE_*
+ * Creates controls related with the digital output.
+ * Called from each codec driver supporting the digital out.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
+ hda_nid_t associated_nid,
+ hda_nid_t cvt_nid,
+ int type)
+{
+ int err;
+ struct snd_kcontrol *kctl;
+ const struct snd_kcontrol_new *dig_mix;
+ int idx = 0;
+ int val = 0;
+ const int spdif_index = 16;
+ struct hda_spdif_out *spdif;
+ struct hda_bus *bus = codec->bus;
+
+ if (bus->primary_dig_out_type == HDA_PCM_TYPE_HDMI &&
+ type == HDA_PCM_TYPE_SPDIF) {
+ idx = spdif_index;
+ } else if (bus->primary_dig_out_type == HDA_PCM_TYPE_SPDIF &&
+ type == HDA_PCM_TYPE_HDMI) {
+ /* suppose a single SPDIF device */
+ for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
+ struct snd_ctl_elem_id id;
+
+ kctl = find_mixer_ctl(codec, dig_mix->name, 0, 0);
+ if (!kctl)
+ break;
+ id = kctl->id;
+ id.index = spdif_index;
+ err = snd_ctl_rename_id(codec->card, &kctl->id, &id);
+ if (err < 0)
+ return err;
+ }
+ bus->primary_dig_out_type = HDA_PCM_TYPE_HDMI;
+ }
+ if (!bus->primary_dig_out_type)
+ bus->primary_dig_out_type = type;
+
+ idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch", idx);
+ if (idx < 0) {
+ codec_err(codec, "too many IEC958 outputs\n");
+ return -EBUSY;
+ }
+ spdif = snd_array_new(&codec->spdif_out);
+ if (!spdif)
+ return -ENOMEM;
+ for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
+ kctl = snd_ctl_new1(dig_mix, codec);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->id.index = idx;
+ kctl->private_value = codec->spdif_out.used - 1;
+ err = snd_hda_ctl_add(codec, associated_nid, kctl);
+ if (err < 0)
+ return err;
+ }
+ spdif->nid = cvt_nid;
+ snd_hdac_regmap_read(&codec->core, cvt_nid,
+ AC_VERB_GET_DIGI_CONVERT_1, &val);
+ spdif->ctls = val;
+ spdif->status = convert_to_spdif_status(spdif->ctls);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_create_dig_out_ctls);
+
+/**
+ * snd_hda_spdif_out_of_nid - get the hda_spdif_out entry from the given NID
+ * @codec: the HDA codec
+ * @nid: widget NID
+ *
+ * call within spdif_mutex lock
+ */
+struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
+ hda_nid_t nid)
+{
+ struct hda_spdif_out *spdif;
+ int i;
+
+ snd_array_for_each(&codec->spdif_out, i, spdif) {
+ if (spdif->nid == nid)
+ return spdif;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_spdif_out_of_nid);
+
+/**
+ * snd_hda_spdif_ctls_unassign - Unassign the given SPDIF ctl
+ * @codec: the HDA codec
+ * @idx: the SPDIF ctl index
+ *
+ * Unassign the widget from the given SPDIF control.
+ */
+void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx)
+{
+ struct hda_spdif_out *spdif;
+
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return;
+ guard(mutex)(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ spdif->nid = (u16)-1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_unassign);
+
+/**
+ * snd_hda_spdif_ctls_assign - Assign the SPDIF controls to the given NID
+ * @codec: the HDA codec
+ * @idx: the SPDIF ctl idx
+ * @nid: widget NID
+ *
+ * Assign the widget to the SPDIF control with the given index.
+ */
+void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid)
+{
+ struct hda_spdif_out *spdif;
+ unsigned short val;
+
+ if (WARN_ON(codec->spdif_out.used <= idx))
+ return;
+ guard(mutex)(&codec->spdif_mutex);
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ if (spdif->nid != nid) {
+ spdif->nid = nid;
+ val = spdif->ctls;
+ set_spdif_ctls(codec, nid, val & 0xff, (val >> 8) & 0xff);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_assign);
+
+/*
+ * SPDIF sharing with analog output
+ */
+static int spdif_share_sw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = mout->share_spdif;
+ return 0;
+}
+
+static int spdif_share_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
+ mout->share_spdif = !!ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static const struct snd_kcontrol_new spdif_share_sw = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Default PCM Playback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = spdif_share_sw_get,
+ .put = spdif_share_sw_put,
+};
+
+/**
+ * snd_hda_create_spdif_share_sw - create Default PCM switch
+ * @codec: the HDA codec
+ * @mout: multi-out instance
+ */
+int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
+ struct hda_multi_out *mout)
+{
+ struct snd_kcontrol *kctl;
+
+ if (!mout->dig_out_nid)
+ return 0;
+
+ kctl = snd_ctl_new1(&spdif_share_sw, mout);
+ if (!kctl)
+ return -ENOMEM;
+ /* ATTENTION: here mout is passed as private_data, instead of codec */
+ return snd_hda_ctl_add(codec, mout->dig_out_nid, kctl);
+}
+EXPORT_SYMBOL_GPL(snd_hda_create_spdif_share_sw);
+
+/*
+ * SPDIF input
+ */
+
+#define snd_hda_spdif_in_switch_info snd_hda_spdif_out_switch_info
+
+static int snd_hda_spdif_in_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = codec->spdif_in_enable;
+ return 0;
+}
+
+static int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned int val = !!ucontrol->value.integer.value[0];
+ int change;
+
+ guard(mutex)(&codec->spdif_mutex);
+ change = codec->spdif_in_enable != val;
+ if (change) {
+ codec->spdif_in_enable = val;
+ snd_hdac_regmap_write(&codec->core, nid,
+ AC_VERB_SET_DIGI_CONVERT_1, val);
+ }
+ return change;
+}
+
+static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned int val;
+ unsigned int sbits;
+
+ snd_hdac_regmap_read(&codec->core, nid,
+ AC_VERB_GET_DIGI_CONVERT_1, &val);
+ sbits = convert_to_spdif_status(val);
+ ucontrol->value.iec958.status[0] = sbits;
+ ucontrol->value.iec958.status[1] = sbits >> 8;
+ ucontrol->value.iec958.status[2] = sbits >> 16;
+ ucontrol->value.iec958.status[3] = sbits >> 24;
+ return 0;
+}
+
+static const struct snd_kcontrol_new dig_in_ctls[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH),
+ .info = snd_hda_spdif_in_switch_info,
+ .get = snd_hda_spdif_in_switch_get,
+ .put = snd_hda_spdif_in_switch_put,
+ },
+ {
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+ .info = snd_hda_spdif_mask_info,
+ .get = snd_hda_spdif_in_status_get,
+ },
+ { } /* end */
+};
+
+/**
+ * snd_hda_create_spdif_in_ctls - create Input SPDIF-related controls
+ * @codec: the HDA codec
+ * @nid: audio in widget NID
+ *
+ * Creates controls related with the SPDIF input.
+ * Called from each codec driver supporting the SPDIF in.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
+{
+ int err;
+ struct snd_kcontrol *kctl;
+ const struct snd_kcontrol_new *dig_mix;
+ int idx;
+
+ idx = find_empty_mixer_ctl_idx(codec, "IEC958 Capture Switch", 0);
+ if (idx < 0) {
+ codec_err(codec, "too many IEC958 inputs\n");
+ return -EBUSY;
+ }
+ for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
+ kctl = snd_ctl_new1(dig_mix, codec);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->private_value = nid;
+ err = snd_hda_ctl_add(codec, nid, kctl);
+ if (err < 0)
+ return err;
+ }
+ codec->spdif_in_enable =
+ snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_DIGI_CONVERT_1, 0) &
+ AC_DIG1_ENABLE;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_create_spdif_in_ctls);
+
+/**
+ * snd_hda_codec_set_power_to_all - Set the power state to all widgets
+ * @codec: the HDA codec
+ * @fg: function group (not used now)
+ * @power_state: the power state to set (AC_PWRST_*)
+ *
+ * Set the given power state to all widgets that have the power control.
+ * If the codec has power_filter set, it evaluates the power state and
+ * filter out if it's unchanged as D3.
+ */
+void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
+ unsigned int power_state)
+{
+ hda_nid_t nid;
+
+ for_each_hda_codec_node(nid, codec) {
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int state = power_state;
+ if (!(wcaps & AC_WCAP_POWER))
+ continue;
+ if (codec->power_filter) {
+ state = codec->power_filter(codec, nid, power_state);
+ if (state != power_state && power_state == AC_PWRST_D3)
+ continue;
+ }
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
+ state);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_to_all);
+
+/**
+ * snd_hda_codec_eapd_power_filter - A power filter callback for EAPD
+ * @codec: the HDA codec
+ * @nid: widget NID
+ * @power_state: power state to evalue
+ *
+ * Don't power down the widget if it controls eapd and EAPD_BTLENABLE is set.
+ * This can be used a codec power_filter callback.
+ */
+unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
+{
+ if (nid == codec->core.afg || nid == codec->core.mfg)
+ return power_state;
+ if (power_state == AC_PWRST_D3 &&
+ get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN &&
+ (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) {
+ int eapd = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_EAPD_BTLENABLE, 0);
+ if (eapd & 0x02)
+ return AC_PWRST_D0;
+ }
+ return power_state;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_eapd_power_filter);
+
+/*
+ * set power state of the codec, and return the power state
+ */
+static unsigned int hda_set_power_state(struct hda_codec *codec,
+ unsigned int power_state)
+{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+ hda_nid_t fg = codec->core.afg ? codec->core.afg : codec->core.mfg;
+ int count;
+ unsigned int state;
+ int flags = 0;
+
+ /* this delay seems necessary to avoid click noise at power-down */
+ if (power_state == AC_PWRST_D3) {
+ if (codec->depop_delay < 0)
+ msleep(codec_has_epss(codec) ? 10 : 100);
+ else if (codec->depop_delay > 0)
+ msleep(codec->depop_delay);
+ flags = HDA_RW_NO_RESPONSE_FALLBACK;
+ }
+
+ /* repeat power states setting at most 10 times*/
+ for (count = 0; count < 10; count++) {
+ /* might be called before binding to driver, too */
+ if (driver && driver->ops && driver->ops->set_power_state)
+ driver->ops->set_power_state(codec, fg, power_state);
+ else {
+ state = power_state;
+ if (codec->power_filter)
+ state = codec->power_filter(codec, fg, state);
+ if (state == power_state || power_state != AC_PWRST_D3)
+ snd_hda_codec_read(codec, fg, flags,
+ AC_VERB_SET_POWER_STATE,
+ state);
+ snd_hda_codec_set_power_to_all(codec, fg, power_state);
+ }
+ state = snd_hda_sync_power_state(codec, fg, power_state);
+ if (!(state & AC_PWRST_ERROR))
+ break;
+ }
+
+ return state;
+}
+
+/* sync power states of all widgets;
+ * this is called at the end of codec parsing
+ */
+static void sync_power_up_states(struct hda_codec *codec)
+{
+ hda_nid_t nid;
+
+ /* don't care if no filter is used */
+ if (!codec->power_filter)
+ return;
+
+ for_each_hda_codec_node(nid, codec) {
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int target;
+ if (!(wcaps & AC_WCAP_POWER))
+ continue;
+ target = codec->power_filter(codec, nid, AC_PWRST_D0);
+ if (target == AC_PWRST_D0)
+ continue;
+ if (!snd_hda_check_power_state(codec, nid, target))
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_POWER_STATE, target);
+ }
+}
+
+#ifdef CONFIG_SND_HDA_RECONFIG
+/* execute additional init verbs */
+static void hda_exec_init_verbs(struct hda_codec *codec)
+{
+ if (codec->init_verbs.list)
+ snd_hda_sequence_write(codec, codec->init_verbs.list);
+}
+#else
+static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
+#endif
+
+/* update the power on/off account with the current jiffies */
+static void update_power_acct(struct hda_codec *codec, bool on)
+{
+ unsigned long delta = jiffies - codec->power_jiffies;
+
+ if (on)
+ codec->power_on_acct += delta;
+ else
+ codec->power_off_acct += delta;
+ codec->power_jiffies += delta;
+}
+
+void snd_hda_update_power_acct(struct hda_codec *codec)
+{
+ update_power_acct(codec, hda_codec_is_power_on(codec));
+}
+
+/*
+ * call suspend and power-down; used both from PM and power-save
+ * this function returns the power state in the end
+ */
+static unsigned int hda_call_codec_suspend(struct hda_codec *codec)
+{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+ unsigned int state;
+
+ snd_hdac_enter_pm(&codec->core);
+ if (driver->ops->suspend)
+ driver->ops->suspend(codec);
+ if (!codec->no_stream_clean_at_suspend)
+ hda_cleanup_all_streams(codec);
+ state = hda_set_power_state(codec, AC_PWRST_D3);
+ update_power_acct(codec, true);
+ snd_hdac_leave_pm(&codec->core);
+ return state;
+}
+
+/*
+ * kick up codec; used both from PM and power-save
+ */
+static void hda_call_codec_resume(struct hda_codec *codec)
+{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+
+ snd_hdac_enter_pm(&codec->core);
+ if (codec->core.regmap)
+ regcache_mark_dirty(codec->core.regmap);
+
+ codec->power_jiffies = jiffies;
+
+ hda_set_power_state(codec, AC_PWRST_D0);
+ restore_shutup_pins(codec);
+ hda_exec_init_verbs(codec);
+ snd_hda_jack_set_dirty_all(codec);
+ if (driver->ops->resume)
+ driver->ops->resume(codec);
+ else {
+ snd_hda_codec_init(codec);
+ snd_hda_regmap_sync(codec);
+ }
+
+ snd_hda_jack_report_sync(codec);
+ codec->core.dev.power.power_state = PMSG_ON;
+ snd_hdac_leave_pm(&codec->core);
+ if (codec->jackpoll_interval)
+ schedule_delayed_work(&codec->jackpoll_work,
+ codec->jackpoll_interval);
+}
+
+static int hda_codec_runtime_suspend(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+ unsigned int state;
+
+ /* Nothing to do if card registration fails and the component driver never probes */
+ if (!codec->card)
+ return 0;
+
+ state = hda_call_codec_suspend(codec);
+ if (codec->link_down_at_suspend ||
+ (codec_has_clkstop(codec) && codec_has_epss(codec) &&
+ (state & AC_PWRST_CLK_STOP_OK)))
+ snd_hdac_codec_link_down(&codec->core);
+ snd_hda_codec_display_power(codec, false);
+
+ return 0;
+}
+
+static int hda_codec_runtime_resume(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+
+ /* Nothing to do if card registration fails and the component driver never probes */
+ if (!codec->card)
+ return 0;
+
+ snd_hda_codec_display_power(codec, true);
+ snd_hdac_codec_link_up(&codec->core);
+ hda_call_codec_resume(codec);
+ pm_runtime_mark_last_busy(dev);
+ return 0;
+}
+
+static int hda_codec_runtime_idle(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+
+ if (codec->jackpoll_interval && !codec->bus->jackpoll_in_suspend)
+ return -EBUSY;
+ return 0;
+}
+
+static int hda_codec_pm_prepare(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+ dev->power.power_state = PMSG_SUSPEND;
+ return pm_runtime_suspended(dev);
+}
+
+static void hda_codec_pm_complete(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+
+ /* If no other pm-functions are called between prepare() and complete() */
+ if (dev->power.power_state.event == PM_EVENT_SUSPEND)
+ dev->power.power_state = PMSG_RESUME;
+
+ if (pm_runtime_suspended(dev) && (codec->jackpoll_interval ||
+ hda_codec_need_resume(codec) || codec->forced_resume))
+ pm_request_resume(dev);
+}
+
+static int hda_codec_pm_suspend(struct device *dev)
+{
+ dev->power.power_state = PMSG_SUSPEND;
+ return pm_runtime_force_suspend(dev);
+}
+
+static int hda_codec_pm_resume(struct device *dev)
+{
+ dev->power.power_state = PMSG_RESUME;
+ return pm_runtime_force_resume(dev);
+}
+
+static int hda_codec_pm_freeze(struct device *dev)
+{
+ struct hda_codec *codec = dev_to_hda_codec(dev);
+
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+ dev->power.power_state = PMSG_FREEZE;
+ return pm_runtime_force_suspend(dev);
+}
+
+static int hda_codec_pm_thaw(struct device *dev)
+{
+ dev->power.power_state = PMSG_THAW;
+ return pm_runtime_force_resume(dev);
+}
+
+static int hda_codec_pm_restore(struct device *dev)
+{
+ dev->power.power_state = PMSG_RESTORE;
+ return pm_runtime_force_resume(dev);
+}
+
+/* referred in hda_bind.c */
+const struct dev_pm_ops hda_codec_driver_pm = {
+ .prepare = pm_sleep_ptr(hda_codec_pm_prepare),
+ .complete = pm_sleep_ptr(hda_codec_pm_complete),
+ .suspend = pm_sleep_ptr(hda_codec_pm_suspend),
+ .resume = pm_sleep_ptr(hda_codec_pm_resume),
+ .freeze = pm_sleep_ptr(hda_codec_pm_freeze),
+ .thaw = pm_sleep_ptr(hda_codec_pm_thaw),
+ .poweroff = pm_sleep_ptr(hda_codec_pm_suspend),
+ .restore = pm_sleep_ptr(hda_codec_pm_restore),
+ RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume,
+ hda_codec_runtime_idle)
+};
+
+/* suspend the codec at shutdown; called from driver's shutdown callback */
+void snd_hda_codec_shutdown(struct hda_codec *codec)
+{
+ struct hda_pcm *cpcm;
+
+ /* Skip the shutdown if codec is not registered */
+ if (!codec->core.registered)
+ return;
+
+ codec->jackpoll_interval = 0; /* don't poll any longer */
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+ list_for_each_entry(cpcm, &codec->pcm_list_head, list)
+ snd_pcm_suspend_all(cpcm->pcm);
+
+ pm_runtime_force_suspend(hda_codec_dev(codec));
+ pm_runtime_disable(hda_codec_dev(codec));
+}
+
+/*
+ * add standard channel maps if not specified
+ */
+static int add_std_chmaps(struct hda_codec *codec)
+{
+ struct hda_pcm *pcm;
+ int str, err;
+
+ list_for_each_entry(pcm, &codec->pcm_list_head, list) {
+ for (str = 0; str < 2; str++) {
+ struct hda_pcm_stream *hinfo = &pcm->stream[str];
+ struct snd_pcm_chmap *chmap;
+ const struct snd_pcm_chmap_elem *elem;
+
+ if (!pcm->pcm || pcm->own_chmap || !hinfo->substreams)
+ continue;
+ elem = hinfo->chmap ? hinfo->chmap : snd_pcm_std_chmaps;
+ err = snd_pcm_add_chmap_ctls(pcm->pcm, str, elem,
+ hinfo->channels_max,
+ 0, &chmap);
+ if (err < 0)
+ return err;
+ chmap->channel_mask = SND_PCM_CHMAP_MASK_2468;
+ }
+ }
+ return 0;
+}
+
+/* default channel maps for 2.1 speakers;
+ * since HD-audio supports only stereo, odd number channels are omitted
+ */
+const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+ { .channels = 4,
+ .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR,
+ SNDRV_CHMAP_LFE, SNDRV_CHMAP_LFE } },
+ { }
+};
+EXPORT_SYMBOL_GPL(snd_pcm_2_1_chmaps);
+
+int snd_hda_codec_build_controls(struct hda_codec *codec)
+{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+ int err;
+
+ hda_exec_init_verbs(codec);
+ /* continue to initialize... */
+ err = snd_hda_codec_init(codec);
+ if (err < 0)
+ return err;
+
+ if (driver->ops->build_controls) {
+ err = driver->ops->build_controls(codec);
+ if (err < 0)
+ return err;
+ }
+
+ /* we create chmaps here instead of build_pcms */
+ err = add_std_chmaps(codec);
+ if (err < 0)
+ return err;
+
+ snd_hda_jack_report_sync(codec); /* call at the last init point */
+ if (codec->jackpoll_interval)
+ schedule_delayed_work(&codec->jackpoll_work,
+ codec->jackpoll_interval);
+
+ sync_power_up_states(codec);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_build_controls);
+
+/*
+ * PCM stuff
+ */
+static int hda_pcm_default_open_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+static int hda_pcm_default_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+ return 0;
+}
+
+static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+ return 0;
+}
+
+static int set_pcm_default_values(struct hda_codec *codec,
+ struct hda_pcm_stream *info)
+{
+ int err;
+
+ /* query support PCM information from the given NID */
+ if (info->nid && (!info->rates || !info->formats)) {
+ err = snd_hda_query_supported_pcm(codec, info->nid,
+ info->rates ? NULL : &info->rates,
+ info->formats ? NULL : &info->formats,
+ info->subformats ? NULL : &info->subformats,
+ info->maxbps ? NULL : &info->maxbps);
+ if (err < 0)
+ return err;
+ }
+ if (info->ops.open == NULL)
+ info->ops.open = hda_pcm_default_open_close;
+ if (info->ops.close == NULL)
+ info->ops.close = hda_pcm_default_open_close;
+ if (info->ops.prepare == NULL) {
+ if (snd_BUG_ON(!info->nid))
+ return -EINVAL;
+ info->ops.prepare = hda_pcm_default_prepare;
+ }
+ if (info->ops.cleanup == NULL) {
+ if (snd_BUG_ON(!info->nid))
+ return -EINVAL;
+ info->ops.cleanup = hda_pcm_default_cleanup;
+ }
+ return 0;
+}
+
+/*
+ * codec prepare/cleanup entries
+ */
+/**
+ * snd_hda_codec_prepare - Prepare a stream
+ * @codec: the HDA codec
+ * @hinfo: PCM information
+ * @stream: stream tag to assign
+ * @format: format id to assign
+ * @substream: PCM substream to assign
+ *
+ * Calls the prepare callback set by the codec with the given arguments.
+ * Clean up the inactive streams when successful.
+ */
+int snd_hda_codec_prepare(struct hda_codec *codec,
+ struct hda_pcm_stream *hinfo,
+ unsigned int stream,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ int ret;
+
+ guard(mutex)(&codec->bus->prepare_mutex);
+ if (hinfo->ops.prepare)
+ ret = hinfo->ops.prepare(hinfo, codec, stream, format,
+ substream);
+ else
+ ret = -ENODEV;
+ if (ret >= 0)
+ purify_inactive_streams(codec);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_prepare);
+
+/**
+ * snd_hda_codec_cleanup - Clean up stream resources
+ * @codec: the HDA codec
+ * @hinfo: PCM information
+ * @substream: PCM substream
+ *
+ * Calls the cleanup callback set by the codec with the given arguments.
+ */
+void snd_hda_codec_cleanup(struct hda_codec *codec,
+ struct hda_pcm_stream *hinfo,
+ struct snd_pcm_substream *substream)
+{
+ guard(mutex)(&codec->bus->prepare_mutex);
+ if (hinfo->ops.cleanup)
+ hinfo->ops.cleanup(hinfo, codec, substream);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup);
+
+/* global */
+const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
+ "Audio", "SPDIF", "HDMI", "Modem"
+};
+
+/*
+ * get the empty PCM device number to assign
+ */
+static int get_empty_pcm_device(struct hda_bus *bus, unsigned int type)
+{
+ /* audio device indices; not linear to keep compatibility */
+ /* assigned to static slots up to dev#10; if more needed, assign
+ * the later slot dynamically (when CONFIG_SND_DYNAMIC_MINORS=y)
+ */
+ static const int audio_idx[HDA_PCM_NTYPES][5] = {
+ [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 },
+ [HDA_PCM_TYPE_SPDIF] = { 1, -1 },
+ [HDA_PCM_TYPE_HDMI] = { 3, 7, 8, 9, -1 },
+ [HDA_PCM_TYPE_MODEM] = { 6, -1 },
+ };
+ int i;
+
+ if (type >= HDA_PCM_NTYPES) {
+ dev_err(bus->card->dev, "Invalid PCM type %d\n", type);
+ return -EINVAL;
+ }
+
+ for (i = 0; audio_idx[type][i] >= 0; i++) {
+#ifndef CONFIG_SND_DYNAMIC_MINORS
+ if (audio_idx[type][i] >= 8)
+ break;
+#endif
+ if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits))
+ return audio_idx[type][i];
+ }
+
+#ifdef CONFIG_SND_DYNAMIC_MINORS
+ /* non-fixed slots starting from 10 */
+ for (i = 10; i < 32; i++) {
+ if (!test_and_set_bit(i, bus->pcm_dev_bits))
+ return i;
+ }
+#endif
+
+ dev_warn(bus->card->dev, "Too many %s devices\n",
+ snd_hda_pcm_type_name[type]);
+#ifndef CONFIG_SND_DYNAMIC_MINORS
+ dev_warn(bus->card->dev,
+ "Consider building the kernel with CONFIG_SND_DYNAMIC_MINORS=y\n");
+#endif
+ return -EAGAIN;
+}
+
+/* call build_pcms ops of the given codec and set up the default parameters */
+int snd_hda_codec_parse_pcms(struct hda_codec *codec)
+{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+ struct hda_pcm *cpcm;
+ int err;
+
+ if (!list_empty(&codec->pcm_list_head))
+ return 0; /* already parsed */
+
+ if (!driver->ops->build_pcms)
+ return 0;
+
+ err = driver->ops->build_pcms(codec);
+ if (err < 0) {
+ codec_err(codec, "cannot build PCMs for #%d (error %d)\n",
+ codec->core.addr, err);
+ return err;
+ }
+
+ list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
+ int stream;
+
+ for_each_pcm_streams(stream) {
+ struct hda_pcm_stream *info = &cpcm->stream[stream];
+
+ if (!info->substreams)
+ continue;
+ err = set_pcm_default_values(codec, info);
+ if (err < 0) {
+ codec_warn(codec,
+ "fail to setup default for PCM %s\n",
+ cpcm->name);
+ return err;
+ }
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_parse_pcms);
+
+/* assign all PCMs of the given codec */
+int snd_hda_codec_build_pcms(struct hda_codec *codec)
+{
+ struct hda_bus *bus = codec->bus;
+ struct hda_pcm *cpcm;
+ int dev, err;
+
+ err = snd_hda_codec_parse_pcms(codec);
+ if (err < 0)
+ return err;
+
+ /* attach a new PCM streams */
+ list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
+ if (cpcm->pcm)
+ continue; /* already attached */
+ if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
+ continue; /* no substreams assigned */
+
+ dev = get_empty_pcm_device(bus, cpcm->pcm_type);
+ if (dev < 0) {
+ cpcm->device = SNDRV_PCM_INVALID_DEVICE;
+ continue; /* no fatal error */
+ }
+ cpcm->device = dev;
+ err = snd_hda_attach_pcm_stream(bus, codec, cpcm);
+ if (err < 0) {
+ codec_err(codec,
+ "cannot attach PCM stream %d for codec #%d\n",
+ dev, codec->core.addr);
+ continue; /* no fatal error */
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * snd_hda_add_new_ctls - create controls from the array
+ * @codec: the HDA codec
+ * @knew: the array of struct snd_kcontrol_new
+ *
+ * This helper function creates and add new controls in the given array.
+ * The array must be terminated with an empty entry as terminator.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_add_new_ctls(struct hda_codec *codec,
+ const struct snd_kcontrol_new *knew)
+{
+ int err;
+
+ for (; knew->name; knew++) {
+ struct snd_kcontrol *kctl;
+ int addr = 0, idx = 0;
+ if (knew->iface == (__force snd_ctl_elem_iface_t)-1)
+ continue; /* skip this codec private value */
+ for (;;) {
+ kctl = snd_ctl_new1(knew, codec);
+ if (!kctl)
+ return -ENOMEM;
+ /* Do not use the id.device field for MIXER elements.
+ * This field is for real device numbers (like PCM) but codecs
+ * are hidden components from the user space view (unrelated
+ * to the mixer element identification).
+ */
+ if (addr > 0 && codec->ctl_dev_id)
+ kctl->id.device = addr;
+ if (idx > 0)
+ kctl->id.index = idx;
+ err = snd_hda_ctl_add(codec, 0, kctl);
+ if (!err)
+ break;
+ /* try first with another device index corresponding to
+ * the codec addr; if it still fails (or it's the
+ * primary codec), then try another control index
+ */
+ if (!addr && codec->core.addr) {
+ addr = codec->core.addr;
+ if (!codec->ctl_dev_id)
+ idx += 10 * addr;
+ } else if (!idx && !knew->index) {
+ idx = find_empty_mixer_ctl_idx(codec,
+ knew->name, 0);
+ if (idx <= 0)
+ return err;
+ } else
+ return err;
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_add_new_ctls);
+
+/**
+ * snd_hda_codec_set_power_save - Configure codec's runtime PM
+ * @codec: codec device to configure
+ * @delay: autosuspend delay
+ */
+void snd_hda_codec_set_power_save(struct hda_codec *codec, int delay)
+{
+ struct device *dev = hda_codec_dev(codec);
+
+ if (delay == 0 && codec->auto_runtime_pm)
+ delay = 3000;
+
+ if (delay > 0) {
+ pm_runtime_set_autosuspend_delay(dev, delay);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_allow(dev);
+ if (!pm_runtime_suspended(dev))
+ pm_runtime_mark_last_busy(dev);
+ } else {
+ pm_runtime_dont_use_autosuspend(dev);
+ pm_runtime_forbid(dev);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_save);
+
+/**
+ * snd_hda_set_power_save - reprogram autosuspend for the given delay
+ * @bus: HD-audio bus
+ * @delay: autosuspend delay in msec, 0 = off
+ *
+ * Synchronize the runtime PM autosuspend state from the power_save option.
+ */
+void snd_hda_set_power_save(struct hda_bus *bus, int delay)
+{
+ struct hda_codec *c;
+
+ list_for_each_codec(c, bus)
+ snd_hda_codec_set_power_save(c, delay);
+}
+EXPORT_SYMBOL_GPL(snd_hda_set_power_save);
+
+/**
+ * snd_hda_check_amp_list_power - Check the amp list and update the power
+ * @codec: HD-audio codec
+ * @check: the object containing an AMP list and the status
+ * @nid: NID to check / update
+ *
+ * Check whether the given NID is in the amp list. If it's in the list,
+ * check the current AMP status, and update the power-status according
+ * to the mute status.
+ *
+ * This function is supposed to be set or called from the check_power_status
+ * patch ops.
+ */
+int snd_hda_check_amp_list_power(struct hda_codec *codec,
+ struct hda_loopback_check *check,
+ hda_nid_t nid)
+{
+ const struct hda_amp_list *p;
+ int ch, v;
+
+ if (!check->amplist)
+ return 0;
+ for (p = check->amplist; p->nid; p++) {
+ if (p->nid == nid)
+ break;
+ }
+ if (!p->nid)
+ return 0; /* nothing changed */
+
+ for (p = check->amplist; p->nid; p++) {
+ for (ch = 0; ch < 2; ch++) {
+ v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
+ p->idx);
+ if (!(v & HDA_AMP_MUTE) && v > 0) {
+ if (!check->power_on) {
+ check->power_on = 1;
+ snd_hda_power_up_pm(codec);
+ }
+ return 1;
+ }
+ }
+ }
+ if (check->power_on) {
+ check->power_on = 0;
+ snd_hda_power_down_pm(codec);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_check_amp_list_power);
+
+/*
+ * input MUX helper
+ */
+
+/**
+ * snd_hda_input_mux_info - Info callback helper for the input-mux enum
+ * @imux: imux helper object
+ * @uinfo: pointer to get/store the data
+ */
+int snd_hda_input_mux_info(const struct hda_input_mux *imux,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int index;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = imux->num_items;
+ if (!imux->num_items)
+ return 0;
+ index = uinfo->value.enumerated.item;
+ if (index >= imux->num_items)
+ index = imux->num_items - 1;
+ strscpy(uinfo->value.enumerated.name, imux->items[index].label);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_input_mux_info);
+
+/**
+ * snd_hda_input_mux_put - Put callback helper for the input-mux enum
+ * @codec: the HDA codec
+ * @imux: imux helper object
+ * @ucontrol: pointer to get/store the data
+ * @nid: input mux NID
+ * @cur_val: pointer to get/store the current imux value
+ */
+int snd_hda_input_mux_put(struct hda_codec *codec,
+ const struct hda_input_mux *imux,
+ struct snd_ctl_elem_value *ucontrol,
+ hda_nid_t nid,
+ unsigned int *cur_val)
+{
+ unsigned int idx;
+
+ if (!imux->num_items)
+ return 0;
+ idx = ucontrol->value.enumerated.item[0];
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+ if (*cur_val == idx)
+ return 0;
+ snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
+ imux->items[idx].index);
+ *cur_val = idx;
+ return 1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_input_mux_put);
+
+
+/**
+ * snd_hda_enum_helper_info - Helper for simple enum ctls
+ * @kcontrol: ctl element
+ * @uinfo: pointer to get/store the data
+ * @num_items: number of enum items
+ * @texts: enum item string array
+ *
+ * process kcontrol info callback of a simple string enum array
+ * when @num_items is 0 or @texts is NULL, assume a boolean enum array
+ */
+int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo,
+ int num_items, const char * const *texts)
+{
+ static const char * const texts_default[] = {
+ "Disabled", "Enabled"
+ };
+
+ if (!texts || !num_items) {
+ num_items = 2;
+ texts = texts_default;
+ }
+
+ return snd_ctl_enum_info(uinfo, 1, num_items, texts);
+}
+EXPORT_SYMBOL_GPL(snd_hda_enum_helper_info);
+
+/*
+ * Multi-channel / digital-out PCM helper functions
+ */
+
+/* setup SPDIF output stream */
+static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int stream_tag, unsigned int format)
+{
+ struct hda_spdif_out *spdif;
+ unsigned int curr_fmt;
+ bool reset;
+
+ spdif = snd_hda_spdif_out_of_nid(codec, nid);
+ /* Add sanity check to pass klockwork check.
+ * This should never happen.
+ */
+ if (WARN_ON(spdif == NULL))
+ return;
+
+ curr_fmt = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_STREAM_FORMAT, 0);
+ reset = codec->spdif_status_reset &&
+ (spdif->ctls & AC_DIG1_ENABLE) &&
+ curr_fmt != format;
+
+ /* turn off SPDIF if needed; otherwise the IEC958 bits won't be
+ updated */
+ if (reset)
+ set_dig_out_convert(codec, nid,
+ spdif->ctls & ~AC_DIG1_ENABLE & 0xff,
+ -1);
+ snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
+ if (codec->follower_dig_outs) {
+ const hda_nid_t *d;
+ for (d = codec->follower_dig_outs; *d; d++)
+ snd_hda_codec_setup_stream(codec, *d, stream_tag, 0,
+ format);
+ }
+ /* turn on again (if needed) */
+ if (reset)
+ set_dig_out_convert(codec, nid,
+ spdif->ctls & 0xff, -1);
+}
+
+static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
+{
+ snd_hda_codec_cleanup_stream(codec, nid);
+ if (codec->follower_dig_outs) {
+ const hda_nid_t *d;
+ for (d = codec->follower_dig_outs; *d; d++)
+ snd_hda_codec_cleanup_stream(codec, *d);
+ }
+}
+
+/**
+ * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
+ */
+int snd_hda_multi_out_dig_open(struct hda_codec *codec,
+ struct hda_multi_out *mout)
+{
+ guard(mutex)(&codec->spdif_mutex);
+ if (mout->dig_out_used == HDA_DIG_ANALOG_DUP)
+ /* already opened as analog dup; reset it once */
+ cleanup_dig_out_stream(codec, mout->dig_out_nid);
+ mout->dig_out_used = HDA_DIG_EXCLUSIVE;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_open);
+
+/**
+ * snd_hda_multi_out_dig_prepare - prepare the digital out stream
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
+ * @stream_tag: stream tag to assign
+ * @format: format id to assign
+ * @substream: PCM substream to assign
+ */
+int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
+ struct hda_multi_out *mout,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ guard(mutex)(&codec->spdif_mutex);
+ setup_dig_out_stream(codec, mout->dig_out_nid, stream_tag, format);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_prepare);
+
+/**
+ * snd_hda_multi_out_dig_cleanup - clean-up the digital out stream
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
+ */
+int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
+ struct hda_multi_out *mout)
+{
+ guard(mutex)(&codec->spdif_mutex);
+ cleanup_dig_out_stream(codec, mout->dig_out_nid);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_cleanup);
+
+/**
+ * snd_hda_multi_out_dig_close - release the digital out stream
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
+ */
+int snd_hda_multi_out_dig_close(struct hda_codec *codec,
+ struct hda_multi_out *mout)
+{
+ guard(mutex)(&codec->spdif_mutex);
+ mout->dig_out_used = 0;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_close);
+
+/**
+ * snd_hda_multi_out_analog_open - open analog outputs
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
+ * @substream: PCM substream to assign
+ * @hinfo: PCM information to assign
+ *
+ * Open analog outputs and set up the hw-constraints.
+ * If the digital outputs can be opened as follower, open the digital
+ * outputs, too.
+ */
+int snd_hda_multi_out_analog_open(struct hda_codec *codec,
+ struct hda_multi_out *mout,
+ struct snd_pcm_substream *substream,
+ struct hda_pcm_stream *hinfo)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ runtime->hw.channels_max = mout->max_channels;
+ if (mout->dig_out_nid) {
+ if (!mout->analog_rates) {
+ mout->analog_rates = hinfo->rates;
+ mout->analog_formats = hinfo->formats;
+ mout->analog_maxbps = hinfo->maxbps;
+ } else {
+ runtime->hw.rates = mout->analog_rates;
+ runtime->hw.formats = mout->analog_formats;
+ hinfo->maxbps = mout->analog_maxbps;
+ }
+ if (!mout->spdif_rates) {
+ snd_hda_query_supported_pcm(codec, mout->dig_out_nid,
+ &mout->spdif_rates,
+ &mout->spdif_formats,
+ NULL,
+ &mout->spdif_maxbps);
+ }
+ guard(mutex)(&codec->spdif_mutex);
+ if (mout->share_spdif) {
+ if ((runtime->hw.rates & mout->spdif_rates) &&
+ (runtime->hw.formats & mout->spdif_formats)) {
+ runtime->hw.rates &= mout->spdif_rates;
+ runtime->hw.formats &= mout->spdif_formats;
+ if (mout->spdif_maxbps < hinfo->maxbps)
+ hinfo->maxbps = mout->spdif_maxbps;
+ } else {
+ mout->share_spdif = 0;
+ /* FIXME: need notify? */
+ }
+ }
+ }
+ return snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+}
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_open);
+
+/**
+ * snd_hda_multi_out_analog_prepare - Preapre the analog outputs.
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
+ * @stream_tag: stream tag to assign
+ * @format: format id to assign
+ * @substream: PCM substream to assign
+ *
+ * Set up the i/o for analog out.
+ * When the digital out is available, copy the front out to digital out, too.
+ */
+int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
+ struct hda_multi_out *mout,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ const hda_nid_t *nids = mout->dac_nids;
+ int chs = substream->runtime->channels;
+ struct hda_spdif_out *spdif;
+ int i;
+
+ scoped_guard(mutex, &codec->spdif_mutex) {
+ spdif = snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid);
+ if (mout->dig_out_nid && mout->share_spdif &&
+ mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
+ if (chs == 2 && spdif != NULL &&
+ snd_hda_is_supported_format(codec, mout->dig_out_nid,
+ format) &&
+ !(spdif->status & IEC958_AES0_NONAUDIO)) {
+ mout->dig_out_used = HDA_DIG_ANALOG_DUP;
+ setup_dig_out_stream(codec, mout->dig_out_nid,
+ stream_tag, format);
+ } else {
+ mout->dig_out_used = 0;
+ cleanup_dig_out_stream(codec, mout->dig_out_nid);
+ }
+ }
+ }
+
+ /* front */
+ snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
+ 0, format);
+ if (!mout->no_share_stream &&
+ mout->hp_nid && mout->hp_nid != nids[HDA_FRONT])
+ /* headphone out will just decode front left/right (stereo) */
+ snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
+ 0, format);
+ /* extra outputs copied from front */
+ for (i = 0; i < ARRAY_SIZE(mout->hp_out_nid); i++)
+ if (!mout->no_share_stream && mout->hp_out_nid[i])
+ snd_hda_codec_setup_stream(codec,
+ mout->hp_out_nid[i],
+ stream_tag, 0, format);
+
+ /* surrounds */
+ for (i = 1; i < mout->num_dacs; i++) {
+ if (chs >= (i + 1) * 2) /* independent out */
+ snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
+ i * 2, format);
+ else if (!mout->no_share_stream) /* copy front */
+ snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
+ 0, format);
+ }
+
+ /* extra surrounds */
+ for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) {
+ int ch = 0;
+ if (!mout->extra_out_nid[i])
+ break;
+ if (chs >= (i + 1) * 2)
+ ch = i * 2;
+ else if (!mout->no_share_stream)
+ break;
+ snd_hda_codec_setup_stream(codec, mout->extra_out_nid[i],
+ stream_tag, ch, format);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_prepare);
+
+/**
+ * snd_hda_multi_out_analog_cleanup - clean up the setting for analog out
+ * @codec: the HDA codec
+ * @mout: hda_multi_out object
+ */
+int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
+ struct hda_multi_out *mout)
+{
+ const hda_nid_t *nids = mout->dac_nids;
+ int i;
+
+ for (i = 0; i < mout->num_dacs; i++)
+ snd_hda_codec_cleanup_stream(codec, nids[i]);
+ if (mout->hp_nid)
+ snd_hda_codec_cleanup_stream(codec, mout->hp_nid);
+ for (i = 0; i < ARRAY_SIZE(mout->hp_out_nid); i++)
+ if (mout->hp_out_nid[i])
+ snd_hda_codec_cleanup_stream(codec,
+ mout->hp_out_nid[i]);
+ for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
+ if (mout->extra_out_nid[i])
+ snd_hda_codec_cleanup_stream(codec,
+ mout->extra_out_nid[i]);
+ guard(mutex)(&codec->spdif_mutex);
+ if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
+ cleanup_dig_out_stream(codec, mout->dig_out_nid);
+ mout->dig_out_used = 0;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_cleanup);
+
+/**
+ * snd_hda_get_default_vref - Get the default (mic) VREF pin bits
+ * @codec: the HDA codec
+ * @pin: referred pin NID
+ *
+ * Guess the suitable VREF pin bits to be set as the pin-control value.
+ * Note: the function doesn't set the AC_PINCTL_IN_EN bit.
+ */
+unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin)
+{
+ unsigned int pincap;
+ unsigned int oldval;
+ oldval = snd_hda_codec_read(codec, pin, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ pincap = snd_hda_query_pin_caps(codec, pin);
+ pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+ /* Exception: if the default pin setup is vref50, we give it priority */
+ if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50)
+ return AC_PINCTL_VREF_80;
+ else if (pincap & AC_PINCAP_VREF_50)
+ return AC_PINCTL_VREF_50;
+ else if (pincap & AC_PINCAP_VREF_100)
+ return AC_PINCTL_VREF_100;
+ else if (pincap & AC_PINCAP_VREF_GRD)
+ return AC_PINCTL_VREF_GRD;
+ return AC_PINCTL_VREF_HIZ;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_default_vref);
+
+/**
+ * snd_hda_correct_pin_ctl - correct the pin ctl value for matching with the pin cap
+ * @codec: the HDA codec
+ * @pin: referred pin NID
+ * @val: pin ctl value to audit
+ */
+unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
+ hda_nid_t pin, unsigned int val)
+{
+ static const unsigned int cap_lists[][2] = {
+ { AC_PINCTL_VREF_100, AC_PINCAP_VREF_100 },
+ { AC_PINCTL_VREF_80, AC_PINCAP_VREF_80 },
+ { AC_PINCTL_VREF_50, AC_PINCAP_VREF_50 },
+ { AC_PINCTL_VREF_GRD, AC_PINCAP_VREF_GRD },
+ };
+ unsigned int cap;
+
+ if (!val)
+ return 0;
+ cap = snd_hda_query_pin_caps(codec, pin);
+ if (!cap)
+ return val; /* don't know what to do... */
+
+ if (val & AC_PINCTL_OUT_EN) {
+ if (!(cap & AC_PINCAP_OUT))
+ val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+ else if ((val & AC_PINCTL_HP_EN) && !(cap & AC_PINCAP_HP_DRV))
+ val &= ~AC_PINCTL_HP_EN;
+ }
+
+ if (val & AC_PINCTL_IN_EN) {
+ if (!(cap & AC_PINCAP_IN))
+ val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN);
+ else {
+ unsigned int vcap, vref;
+ int i;
+ vcap = (cap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+ vref = val & AC_PINCTL_VREFEN;
+ for (i = 0; i < ARRAY_SIZE(cap_lists); i++) {
+ if (vref == cap_lists[i][0] &&
+ !(vcap & cap_lists[i][1])) {
+ if (i == ARRAY_SIZE(cap_lists) - 1)
+ vref = AC_PINCTL_VREF_HIZ;
+ else
+ vref = cap_lists[i + 1][0];
+ }
+ }
+ val &= ~AC_PINCTL_VREFEN;
+ val |= vref;
+ }
+ }
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(snd_hda_correct_pin_ctl);
+
+/**
+ * _snd_hda_set_pin_ctl - Helper to set pin ctl value
+ * @codec: the HDA codec
+ * @pin: referred pin NID
+ * @val: pin control value to set
+ * @cached: access over codec pinctl cache or direct write
+ *
+ * This function is a helper to set a pin ctl value more safely.
+ * It corrects the pin ctl value via snd_hda_correct_pin_ctl(), stores the
+ * value in pin target array via snd_hda_codec_set_pin_target(), then
+ * actually writes the value via either snd_hda_codec_write_cache() or
+ * snd_hda_codec_write() depending on @cached flag.
+ */
+int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int val, bool cached)
+{
+ val = snd_hda_correct_pin_ctl(codec, pin, val);
+ snd_hda_codec_set_pin_target(codec, pin, val);
+ if (cached)
+ return snd_hda_codec_write_cache(codec, pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+ else
+ return snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+}
+EXPORT_SYMBOL_GPL(_snd_hda_set_pin_ctl);
+
+/**
+ * snd_hda_add_imux_item - Add an item to input_mux
+ * @codec: the HDA codec
+ * @imux: imux helper object
+ * @label: the name of imux item to assign
+ * @index: index number of imux item to assign
+ * @type_idx: pointer to store the resultant label index
+ *
+ * When the same label is used already in the existing items, the number
+ * suffix is appended to the label. This label index number is stored
+ * to type_idx when non-NULL pointer is given.
+ */
+int snd_hda_add_imux_item(struct hda_codec *codec,
+ struct hda_input_mux *imux, const char *label,
+ int index, int *type_idx)
+{
+ int i, label_idx = 0;
+ if (imux->num_items >= HDA_MAX_NUM_INPUTS) {
+ codec_err(codec, "hda_codec: Too many imux items!\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < imux->num_items; i++) {
+ if (!strncmp(label, imux->items[i].label, strlen(label)))
+ label_idx++;
+ }
+ if (type_idx)
+ *type_idx = label_idx;
+ if (label_idx > 0)
+ snprintf(imux->items[imux->num_items].label,
+ sizeof(imux->items[imux->num_items].label),
+ "%s %d", label, label_idx);
+ else
+ strscpy(imux->items[imux->num_items].label, label,
+ sizeof(imux->items[imux->num_items].label));
+ imux->items[imux->num_items].index = index;
+ imux->num_items++;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_add_imux_item);
+
+/**
+ * snd_hda_bus_reset_codecs - Reset the bus
+ * @bus: HD-audio bus
+ */
+void snd_hda_bus_reset_codecs(struct hda_bus *bus)
+{
+ struct hda_codec *codec;
+
+ list_for_each_codec(codec, bus) {
+ /* FIXME: maybe a better way needed for forced reset */
+ if (current_work() != &codec->jackpoll_work.work)
+ cancel_delayed_work_sync(&codec->jackpoll_work);
+ if (hda_codec_is_power_on(codec)) {
+ hda_call_codec_suspend(codec);
+ hda_call_codec_resume(codec);
+ }
+ }
+}
+
+/**
+ * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
+ * @pcm: PCM caps bits
+ * @buf: the string buffer to write
+ * @buflen: the max buffer length
+ *
+ * used by hda_proc.c and hda_eld.c
+ */
+void snd_print_pcm_bits(int pcm, char *buf, int buflen)
+{
+ static const unsigned int bits[] = { 8, 16, 20, 24, 32 };
+ int i, j;
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(bits); i++)
+ if (pcm & (AC_SUPPCM_BITS_8 << i))
+ j += scnprintf(buf + j, buflen - j, " %d", bits[i]);
+
+ buf[j] = '\0'; /* necessary when j == 0 */
+}
+EXPORT_SYMBOL_GPL(snd_print_pcm_bits);
+
+MODULE_DESCRIPTION("HDA codec core");
+MODULE_LICENSE("GPL");
diff --git a/sound/hda/common/controller.c b/sound/hda/common/controller.c
new file mode 100644
index 000000000000..b1cfd9bd4dcb
--- /dev/null
+++ b/sound/hda/common/controller.c
@@ -0,0 +1,1322 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Implementation of primary alsa driver code base for Intel HD Audio.
+ *
+ * Copyright(c) 2004 Intel Corporation
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ * PeiSen Hou <pshou@realtek.com.tw>
+ */
+
+#include <linux/clocksource.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#ifdef CONFIG_X86
+/* for art-tsc conversion */
+#include <asm/tsc.h>
+#endif
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include "hda_controller.h"
+#include "hda_local.h"
+
+#define CREATE_TRACE_POINTS
+#include "controller_trace.h"
+
+/* DSP lock helpers */
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+#define guard_dsp_lock(dev) guard(snd_hdac_dsp_lock)(azx_stream(dev))
+#else
+#define guard_dsp_lock(dev) do {} while (0)
+#endif
+#define dsp_is_locked(dev) snd_hdac_stream_is_locked(azx_stream(dev))
+
+/* assign a stream for the PCM */
+static inline struct azx_dev *
+azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream)
+{
+ struct hdac_stream *s;
+
+ s = snd_hdac_stream_assign(azx_bus(chip), substream);
+ if (!s)
+ return NULL;
+ return stream_to_azx_dev(s);
+}
+
+/* release the assigned stream */
+static inline void azx_release_device(struct azx_dev *azx_dev)
+{
+ snd_hdac_stream_release(azx_stream(azx_dev));
+}
+
+static inline struct hda_pcm_stream *
+to_hda_pcm_stream(struct snd_pcm_substream *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ return &apcm->info->stream[substream->stream];
+}
+
+static u64 azx_adjust_codec_delay(struct snd_pcm_substream *substream,
+ u64 nsec)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
+ u64 codec_frames, codec_nsecs;
+
+ if (!hinfo->ops.get_delay)
+ return nsec;
+
+ codec_frames = hinfo->ops.get_delay(hinfo, apcm->codec, substream);
+ codec_nsecs = div_u64(codec_frames * 1000000000LL,
+ substream->runtime->rate);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return nsec + codec_nsecs;
+
+ return (nsec > codec_nsecs) ? nsec - codec_nsecs : 0;
+}
+
+/*
+ * PCM ops
+ */
+
+static int azx_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
+ struct azx *chip = apcm->chip;
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+
+ trace_azx_pcm_close(chip, azx_dev);
+ scoped_guard(mutex, &chip->open_mutex) {
+ azx_release_device(azx_dev);
+ if (hinfo->ops.close)
+ hinfo->ops.close(hinfo, apcm->codec, substream);
+ snd_hda_power_down(apcm->codec);
+ }
+ snd_hda_codec_pcm_put(apcm->info);
+ return 0;
+}
+
+static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx *chip = apcm->chip;
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ struct hdac_stream *hdas = azx_stream(azx_dev);
+
+ trace_azx_pcm_hw_params(chip, azx_dev);
+ guard_dsp_lock(azx_dev);
+ if (dsp_is_locked(azx_dev))
+ return -EBUSY;
+
+ /* Set up BDLEs here, return -ENOMEM if too many BDLEs are required */
+ hdas->bufsize = params_buffer_bytes(hw_params);
+ hdas->period_bytes = params_period_bytes(hw_params);
+ hdas->format_val = 0;
+ hdas->no_period_wakeup =
+ (hw_params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) &&
+ (hw_params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP);
+ if (snd_hdac_stream_setup_periods(hdas) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
+
+ /* reset BDL address */
+ guard_dsp_lock(azx_dev);
+ if (!dsp_is_locked(azx_dev))
+ snd_hdac_stream_cleanup(azx_stream(azx_dev));
+
+ snd_hda_codec_cleanup(apcm->codec, hinfo, substream);
+
+ azx_stream(azx_dev)->prepared = 0;
+ return 0;
+}
+
+static int azx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx *chip = apcm->chip;
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int format_val, stream_tag, bits;
+ int err;
+ struct hda_spdif_out *spdif =
+ snd_hda_spdif_out_of_nid(apcm->codec, hinfo->nid);
+ unsigned short ctls = spdif ? spdif->ctls : 0;
+
+ trace_azx_pcm_prepare(chip, azx_dev);
+ guard_dsp_lock(azx_dev);
+ if (dsp_is_locked(azx_dev))
+ return -EBUSY;
+
+ snd_hdac_stream_reset(azx_stream(azx_dev));
+ bits = snd_hdac_stream_format_bits(runtime->format, SNDRV_PCM_SUBFORMAT_STD, hinfo->maxbps);
+
+ format_val = snd_hdac_spdif_stream_format(runtime->channels, bits, runtime->rate, ctls);
+ if (!format_val) {
+ dev_err(chip->card->dev,
+ "invalid format_val, rate=%d, ch=%d, format=%d\n",
+ runtime->rate, runtime->channels, runtime->format);
+ return -EINVAL;
+ }
+
+ err = snd_hdac_stream_set_params(azx_stream(azx_dev), format_val);
+ if (err < 0)
+ return err;
+
+ snd_hdac_stream_setup(azx_stream(azx_dev), false);
+
+ stream_tag = azx_dev->core.stream_tag;
+ /* CA-IBG chips need the playback stream starting from 1 */
+ if ((chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) &&
+ stream_tag > chip->capture_streams)
+ stream_tag -= chip->capture_streams;
+ err = snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag,
+ azx_dev->core.format_val, substream);
+ if (err < 0)
+ return err;
+
+ azx_stream(azx_dev)->prepared = 1;
+ return 0;
+}
+
+static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx *chip = apcm->chip;
+ struct hdac_bus *bus = azx_bus(chip);
+ struct azx_dev *azx_dev;
+ struct snd_pcm_substream *s;
+ struct hdac_stream *hstr;
+ bool start;
+ int sbits = 0;
+ int sync_reg;
+
+ azx_dev = get_azx_dev(substream);
+ trace_azx_pcm_trigger(chip, azx_dev, cmd);
+
+ hstr = azx_stream(azx_dev);
+ if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC)
+ sync_reg = AZX_REG_OLD_SSYNC;
+ else
+ sync_reg = AZX_REG_SSYNC;
+
+ if (dsp_is_locked(azx_dev) || !hstr->prepared)
+ return -EPIPE;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ start = true;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ start = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ azx_dev = get_azx_dev(s);
+ sbits |= 1 << azx_dev->core.index;
+ snd_pcm_trigger_done(s, substream);
+ }
+
+ scoped_guard(spinlock, &bus->reg_lock) {
+ /* first, set SYNC bits of corresponding streams */
+ snd_hdac_stream_sync_trigger(hstr, true, sbits, sync_reg);
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ azx_dev = get_azx_dev(s);
+ if (start) {
+ azx_dev->insufficient = 1;
+ snd_hdac_stream_start(azx_stream(azx_dev));
+ } else {
+ snd_hdac_stream_stop(azx_stream(azx_dev));
+ }
+ }
+ }
+
+ snd_hdac_stream_sync(hstr, start, sbits);
+
+ guard(spinlock)(&bus->reg_lock);
+ /* reset SYNC bits */
+ snd_hdac_stream_sync_trigger(hstr, false, sbits, sync_reg);
+ snd_hdac_stream_timecounter_init(hstr, sbits, start);
+ return 0;
+}
+
+unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev)
+{
+ return snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev));
+}
+EXPORT_SYMBOL_GPL(azx_get_pos_lpib);
+
+unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev)
+{
+ return snd_hdac_stream_get_pos_posbuf(azx_stream(azx_dev));
+}
+EXPORT_SYMBOL_GPL(azx_get_pos_posbuf);
+
+unsigned int azx_get_position(struct azx *chip,
+ struct azx_dev *azx_dev)
+{
+ struct snd_pcm_substream *substream = azx_dev->core.substream;
+ unsigned int pos;
+ int stream = substream->stream;
+ int delay = 0;
+
+ if (chip->get_position[stream])
+ pos = chip->get_position[stream](chip, azx_dev);
+ else /* use the position buffer as default */
+ pos = azx_get_pos_posbuf(chip, azx_dev);
+
+ if (pos >= azx_dev->core.bufsize)
+ pos = 0;
+
+ if (substream->runtime) {
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
+
+ if (chip->get_delay[stream])
+ delay += chip->get_delay[stream](chip, azx_dev, pos);
+ if (hinfo->ops.get_delay)
+ delay += hinfo->ops.get_delay(hinfo, apcm->codec,
+ substream);
+ substream->runtime->delay = delay;
+ }
+
+ trace_azx_get_position(chip, azx_dev, pos, delay);
+ return pos;
+}
+EXPORT_SYMBOL_GPL(azx_get_position);
+
+static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx *chip = apcm->chip;
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ return bytes_to_frames(substream->runtime,
+ azx_get_position(chip, azx_dev));
+}
+
+/*
+ * azx_scale64: Scale base by mult/div while not overflowing sanely
+ *
+ * Derived from scale64_check_overflow in kernel/time/timekeeping.c
+ *
+ * The tmestamps for a 48Khz stream can overflow after (2^64/10^9)/48K which
+ * is about 384307 ie ~4.5 days.
+ *
+ * This scales the calculation so that overflow will happen but after 2^64 /
+ * 48000 secs, which is pretty large!
+ *
+ * In caln below:
+ * base may overflow, but since there isn’t any additional division
+ * performed on base it’s OK
+ * rem can’t overflow because both are 32-bit values
+ */
+
+#ifdef CONFIG_X86
+static u64 azx_scale64(u64 base, u32 num, u32 den)
+{
+ u64 rem;
+
+ rem = do_div(base, den);
+
+ base *= num;
+ rem *= num;
+
+ do_div(rem, den);
+
+ return base + rem;
+}
+
+static int azx_get_sync_time(ktime_t *device,
+ struct system_counterval_t *system, void *ctx)
+{
+ struct snd_pcm_substream *substream = ctx;
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx *chip = apcm->chip;
+ struct snd_pcm_runtime *runtime;
+ u64 ll_counter, ll_counter_l, ll_counter_h;
+ u64 tsc_counter, tsc_counter_l, tsc_counter_h;
+ u32 wallclk_ctr, wallclk_cycles;
+ bool direction;
+ u32 dma_select;
+ u32 timeout;
+ u32 retry_count = 0;
+
+ runtime = substream->runtime;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ direction = 1;
+ else
+ direction = 0;
+
+ /* 0th stream tag is not used, so DMA ch 0 is for 1st stream tag */
+ do {
+ timeout = 100;
+ dma_select = (direction << GTSCC_CDMAS_DMA_DIR_SHIFT) |
+ (azx_dev->core.stream_tag - 1);
+ snd_hdac_chip_writel(azx_bus(chip), GTSCC, dma_select);
+
+ /* Enable the capture */
+ snd_hdac_chip_updatel(azx_bus(chip), GTSCC, 0, GTSCC_TSCCI_MASK);
+
+ while (timeout) {
+ if (snd_hdac_chip_readl(azx_bus(chip), GTSCC) &
+ GTSCC_TSCCD_MASK)
+ break;
+
+ timeout--;
+ }
+
+ if (!timeout) {
+ dev_err(chip->card->dev, "GTSCC capture Timedout!\n");
+ return -EIO;
+ }
+
+ /* Read wall clock counter */
+ wallclk_ctr = snd_hdac_chip_readl(azx_bus(chip), WALFCC);
+
+ /* Read TSC counter */
+ tsc_counter_l = snd_hdac_chip_readl(azx_bus(chip), TSCCL);
+ tsc_counter_h = snd_hdac_chip_readl(azx_bus(chip), TSCCU);
+
+ /* Read Link counter */
+ ll_counter_l = snd_hdac_chip_readl(azx_bus(chip), LLPCL);
+ ll_counter_h = snd_hdac_chip_readl(azx_bus(chip), LLPCU);
+
+ /* Ack: registers read done */
+ snd_hdac_chip_writel(azx_bus(chip), GTSCC, GTSCC_TSCCD_SHIFT);
+
+ tsc_counter = (tsc_counter_h << TSCCU_CCU_SHIFT) |
+ tsc_counter_l;
+
+ ll_counter = (ll_counter_h << LLPC_CCU_SHIFT) | ll_counter_l;
+ wallclk_cycles = wallclk_ctr & WALFCC_CIF_MASK;
+
+ /*
+ * An error occurs near frame "rollover". The clocks in
+ * frame value indicates whether this error may have
+ * occurred. Here we use the value of 10 i.e.,
+ * HDA_MAX_CYCLE_OFFSET
+ */
+ if (wallclk_cycles < HDA_MAX_CYCLE_VALUE - HDA_MAX_CYCLE_OFFSET
+ && wallclk_cycles > HDA_MAX_CYCLE_OFFSET)
+ break;
+
+ /*
+ * Sleep before we read again, else we may again get
+ * value near to MAX_CYCLE. Try to sleep for different
+ * amount of time so we dont hit the same number again
+ */
+ udelay(retry_count++);
+
+ } while (retry_count != HDA_MAX_CYCLE_READ_RETRY);
+
+ if (retry_count == HDA_MAX_CYCLE_READ_RETRY) {
+ dev_err_ratelimited(chip->card->dev,
+ "Error in WALFCC cycle count\n");
+ return -EIO;
+ }
+
+ *device = ns_to_ktime(azx_scale64(ll_counter,
+ NSEC_PER_SEC, runtime->rate));
+ *device = ktime_add_ns(*device, (wallclk_cycles * NSEC_PER_SEC) /
+ ((HDA_MAX_CYCLE_VALUE + 1) * runtime->rate));
+
+ system->cycles = tsc_counter;
+ system->cs_id = CSID_X86_ART;
+
+ return 0;
+}
+
+#else
+static int azx_get_sync_time(ktime_t *device,
+ struct system_counterval_t *system, void *ctx)
+{
+ return -ENXIO;
+}
+#endif
+
+static int azx_get_crosststamp(struct snd_pcm_substream *substream,
+ struct system_device_crosststamp *xtstamp)
+{
+ return get_device_system_crosststamp(azx_get_sync_time,
+ substream, NULL, xtstamp);
+}
+
+static inline bool is_link_time_supported(struct snd_pcm_runtime *runtime,
+ struct snd_pcm_audio_tstamp_config *ts)
+{
+ if (runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME)
+ if (ts->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED)
+ return true;
+
+ return false;
+}
+
+static int azx_get_time_info(struct snd_pcm_substream *substream,
+ struct timespec64 *system_ts, struct timespec64 *audio_ts,
+ struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
+ struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
+{
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct system_device_crosststamp xtstamp;
+ int ret;
+ u64 nsec;
+
+ if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) &&
+ (audio_tstamp_config->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK)) {
+
+ snd_pcm_gettime(substream->runtime, system_ts);
+
+ nsec = timecounter_read(&azx_dev->core.tc);
+ if (audio_tstamp_config->report_delay)
+ nsec = azx_adjust_codec_delay(substream, nsec);
+
+ *audio_ts = ns_to_timespec64(nsec);
+
+ audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK;
+ audio_tstamp_report->accuracy_report = 1; /* rest of structure is valid */
+ audio_tstamp_report->accuracy = 42; /* 24 MHz WallClock == 42ns resolution */
+
+ } else if (is_link_time_supported(runtime, audio_tstamp_config)) {
+
+ ret = azx_get_crosststamp(substream, &xtstamp);
+ if (ret)
+ return ret;
+
+ switch (runtime->tstamp_type) {
+ case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC:
+ return -EINVAL;
+
+ case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW:
+ *system_ts = ktime_to_timespec64(xtstamp.sys_monoraw);
+ break;
+
+ default:
+ *system_ts = ktime_to_timespec64(xtstamp.sys_realtime);
+ break;
+
+ }
+
+ *audio_ts = ktime_to_timespec64(xtstamp.device);
+
+ audio_tstamp_report->actual_type =
+ SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED;
+ audio_tstamp_report->accuracy_report = 1;
+ /* 24 MHz WallClock == 42ns resolution */
+ audio_tstamp_report->accuracy = 42;
+
+ } else {
+ audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
+ }
+
+ return 0;
+}
+
+static const struct snd_pcm_hardware azx_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ /* No full-resume yet implemented */
+ /* SNDRV_PCM_INFO_RESUME |*/
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */
+ SNDRV_PCM_INFO_HAS_LINK_ATIME |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = AZX_MAX_BUF_SIZE,
+ .period_bytes_min = 128,
+ .period_bytes_max = AZX_MAX_BUF_SIZE / 2,
+ .periods_min = 2,
+ .periods_max = AZX_MAX_FRAG,
+ .fifo_size = 0,
+};
+
+static int azx_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream);
+ struct azx *chip = apcm->chip;
+ struct azx_dev *azx_dev;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+ int buff_step;
+
+ snd_hda_codec_pcm_get(apcm->info);
+ mutex_lock(&chip->open_mutex);
+ azx_dev = azx_assign_device(chip, substream);
+ trace_azx_pcm_open(chip, azx_dev);
+ if (azx_dev == NULL) {
+ err = -EBUSY;
+ goto unlock;
+ }
+ runtime->private_data = azx_dev;
+
+ runtime->hw = azx_pcm_hw;
+ if (chip->gts_present)
+ runtime->hw.info |= SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME;
+ runtime->hw.channels_min = hinfo->channels_min;
+ runtime->hw.channels_max = hinfo->channels_max;
+ runtime->hw.formats = hinfo->formats;
+ runtime->hw.rates = hinfo->rates;
+ snd_pcm_limit_hw_rates(runtime);
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+ /* avoid wrap-around with wall-clock */
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME,
+ 20,
+ 178000000);
+
+ if (chip->align_buffer_size)
+ /* constrain buffer sizes to be multiple of 128
+ bytes. This is more efficient in terms of memory
+ access but isn't required by the HDA spec and
+ prevents users from specifying exact period/buffer
+ sizes. For example for 44.1kHz, a period size set
+ to 20ms will be rounded to 19.59ms. */
+ buff_step = 128;
+ else
+ /* Don't enforce steps on buffer sizes, still need to
+ be multiple of 4 bytes (HDA spec). Tested on Intel
+ HDA controllers, may not work on all devices where
+ option needs to be disabled */
+ buff_step = 4;
+
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ buff_step);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ buff_step);
+ snd_hda_power_up(apcm->codec);
+ if (hinfo->ops.open)
+ err = hinfo->ops.open(hinfo, apcm->codec, substream);
+ else
+ err = -ENODEV;
+ if (err < 0) {
+ azx_release_device(azx_dev);
+ goto powerdown;
+ }
+ snd_pcm_limit_hw_rates(runtime);
+ /* sanity check */
+ if (snd_BUG_ON(!runtime->hw.channels_min) ||
+ snd_BUG_ON(!runtime->hw.channels_max) ||
+ snd_BUG_ON(!runtime->hw.formats) ||
+ snd_BUG_ON(!runtime->hw.rates)) {
+ azx_release_device(azx_dev);
+ if (hinfo->ops.close)
+ hinfo->ops.close(hinfo, apcm->codec, substream);
+ err = -EINVAL;
+ goto powerdown;
+ }
+
+ /* disable LINK_ATIME timestamps for capture streams
+ until we figure out how to handle digital inputs */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; /* legacy */
+ runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME;
+ }
+
+ snd_pcm_set_sync(substream);
+ mutex_unlock(&chip->open_mutex);
+ return 0;
+
+ powerdown:
+ snd_hda_power_down(apcm->codec);
+ unlock:
+ mutex_unlock(&chip->open_mutex);
+ snd_hda_codec_pcm_put(apcm->info);
+ return err;
+}
+
+static const struct snd_pcm_ops azx_pcm_ops = {
+ .open = azx_pcm_open,
+ .close = azx_pcm_close,
+ .hw_params = azx_pcm_hw_params,
+ .hw_free = azx_pcm_hw_free,
+ .prepare = azx_pcm_prepare,
+ .trigger = azx_pcm_trigger,
+ .pointer = azx_pcm_pointer,
+ .get_time_info = azx_get_time_info,
+};
+
+static void azx_pcm_free(struct snd_pcm *pcm)
+{
+ struct azx_pcm *apcm = pcm->private_data;
+ if (apcm) {
+ list_del(&apcm->list);
+ apcm->info->pcm = NULL;
+ kfree(apcm);
+ }
+}
+
+#define MAX_PREALLOC_SIZE (32 * 1024 * 1024)
+
+int snd_hda_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec,
+ struct hda_pcm *cpcm)
+{
+ struct hdac_bus *bus = &_bus->core;
+ struct azx *chip = bus_to_azx(bus);
+ struct snd_pcm *pcm;
+ struct azx_pcm *apcm;
+ int pcm_dev = cpcm->device;
+ unsigned int size;
+ int s, err;
+ int type = SNDRV_DMA_TYPE_DEV_SG;
+
+ list_for_each_entry(apcm, &chip->pcm_list, list) {
+ if (apcm->pcm->device == pcm_dev) {
+ dev_err(chip->card->dev, "PCM %d already exists\n",
+ pcm_dev);
+ return -EBUSY;
+ }
+ }
+ err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
+ cpcm->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams,
+ cpcm->stream[SNDRV_PCM_STREAM_CAPTURE].substreams,
+ &pcm);
+ if (err < 0)
+ return err;
+ strscpy(pcm->name, cpcm->name, sizeof(pcm->name));
+ apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
+ if (apcm == NULL) {
+ snd_device_free(chip->card, pcm);
+ return -ENOMEM;
+ }
+ apcm->chip = chip;
+ apcm->pcm = pcm;
+ apcm->codec = codec;
+ apcm->info = cpcm;
+ pcm->private_data = apcm;
+ pcm->private_free = azx_pcm_free;
+ if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM)
+ pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
+ list_add_tail(&apcm->list, &chip->pcm_list);
+ cpcm->pcm = pcm;
+ for (s = 0; s < 2; s++) {
+ if (cpcm->stream[s].substreams)
+ snd_pcm_set_ops(pcm, s, &azx_pcm_ops);
+ }
+ /* buffer pre-allocation */
+ size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024;
+ if (size > MAX_PREALLOC_SIZE)
+ size = MAX_PREALLOC_SIZE;
+ if (chip->uc_buffer)
+ type = SNDRV_DMA_TYPE_DEV_WC_SG;
+ snd_pcm_set_managed_buffer_all(pcm, type, chip->card->dev,
+ size, MAX_PREALLOC_SIZE);
+ return 0;
+}
+
+static unsigned int azx_command_addr(u32 cmd)
+{
+ unsigned int addr = cmd >> 28;
+
+ if (addr >= AZX_MAX_CODECS) {
+ snd_BUG();
+ addr = 0;
+ }
+
+ return addr;
+}
+
+/* receive a response */
+static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
+ unsigned int *res)
+{
+ struct azx *chip = bus_to_azx(bus);
+ struct hda_bus *hbus = &chip->bus;
+ int err;
+
+ again:
+ err = snd_hdac_bus_get_response(bus, addr, res);
+ if (!err)
+ return 0;
+
+ if (hbus->no_response_fallback)
+ return -EIO;
+
+ if (!bus->polling_mode) {
+ dev_warn(chip->card->dev,
+ "azx_get_response timeout, switching to polling mode: last cmd=0x%08x\n",
+ bus->last_cmd[addr]);
+ bus->polling_mode = 1;
+ goto again;
+ }
+
+ if (chip->msi) {
+ dev_warn(chip->card->dev,
+ "No response from codec, disabling MSI: last cmd=0x%08x\n",
+ bus->last_cmd[addr]);
+ if (chip->ops->disable_msi_reset_irq &&
+ chip->ops->disable_msi_reset_irq(chip) < 0)
+ return -EIO;
+ goto again;
+ }
+
+ if (chip->probing) {
+ /* If this critical timeout happens during the codec probing
+ * phase, this is likely an access to a non-existing codec
+ * slot. Better to return an error and reset the system.
+ */
+ return -EIO;
+ }
+
+ /* no fallback mechanism? */
+ if (!chip->fallback_to_single_cmd)
+ return -EIO;
+
+ /* a fatal communication error; need either to reset or to fallback
+ * to the single_cmd mode
+ */
+ if (hbus->allow_bus_reset && !hbus->response_reset && !hbus->in_reset) {
+ hbus->response_reset = 1;
+ dev_err(chip->card->dev,
+ "No response from codec, resetting bus: last cmd=0x%08x\n",
+ bus->last_cmd[addr]);
+ return -EAGAIN; /* give a chance to retry */
+ }
+
+ dev_err(chip->card->dev,
+ "azx_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n",
+ bus->last_cmd[addr]);
+ chip->single_cmd = 1;
+ hbus->response_reset = 0;
+ snd_hdac_bus_stop_cmd_io(bus);
+ return -EIO;
+}
+
+/*
+ * Use the single immediate command instead of CORB/RIRB for simplicity
+ *
+ * Note: according to Intel, this is not preferred use. The command was
+ * intended for the BIOS only, and may get confused with unsolicited
+ * responses. So, we shouldn't use it for normal operation from the
+ * driver.
+ * I left the codes, however, for debugging/testing purposes.
+ */
+
+/* receive a response */
+static int azx_single_wait_for_response(struct azx *chip, unsigned int addr)
+{
+ int timeout = 50;
+
+ while (timeout--) {
+ /* check IRV busy bit */
+ if (azx_readw(chip, IRS) & AZX_IRS_VALID) {
+ /* reuse rirb.res as the response return value */
+ azx_bus(chip)->rirb.res[addr] = azx_readl(chip, IR);
+ return 0;
+ }
+ udelay(1);
+ }
+ if (printk_ratelimit())
+ dev_dbg(chip->card->dev, "get_response timeout: IRS=0x%x\n",
+ azx_readw(chip, IRS));
+ azx_bus(chip)->rirb.res[addr] = -1;
+ return -EIO;
+}
+
+/* send a command */
+static int azx_single_send_cmd(struct hdac_bus *bus, u32 val)
+{
+ struct azx *chip = bus_to_azx(bus);
+ unsigned int addr = azx_command_addr(val);
+ int timeout = 50;
+
+ bus->last_cmd[azx_command_addr(val)] = val;
+ while (timeout--) {
+ /* check ICB busy bit */
+ if (!((azx_readw(chip, IRS) & AZX_IRS_BUSY))) {
+ /* Clear IRV valid bit */
+ azx_writew(chip, IRS, azx_readw(chip, IRS) |
+ AZX_IRS_VALID);
+ azx_writel(chip, IC, val);
+ azx_writew(chip, IRS, azx_readw(chip, IRS) |
+ AZX_IRS_BUSY);
+ return azx_single_wait_for_response(chip, addr);
+ }
+ udelay(1);
+ }
+ if (printk_ratelimit())
+ dev_dbg(chip->card->dev,
+ "send_cmd timeout: IRS=0x%x, val=0x%x\n",
+ azx_readw(chip, IRS), val);
+ return -EIO;
+}
+
+/* receive a response */
+static int azx_single_get_response(struct hdac_bus *bus, unsigned int addr,
+ unsigned int *res)
+{
+ if (res)
+ *res = bus->rirb.res[addr];
+ return 0;
+}
+
+/*
+ * The below are the main callbacks from hda_codec.
+ *
+ * They are just the skeleton to call sub-callbacks according to the
+ * current setting of chip->single_cmd.
+ */
+
+/* send a command */
+static int azx_send_cmd(struct hdac_bus *bus, unsigned int val)
+{
+ struct azx *chip = bus_to_azx(bus);
+
+ if (chip->disabled)
+ return 0;
+ if (chip->single_cmd || bus->use_pio_for_commands)
+ return azx_single_send_cmd(bus, val);
+ else
+ return snd_hdac_bus_send_cmd(bus, val);
+}
+
+/* get a response */
+static int azx_get_response(struct hdac_bus *bus, unsigned int addr,
+ unsigned int *res)
+{
+ struct azx *chip = bus_to_azx(bus);
+
+ if (chip->disabled)
+ return 0;
+ if (chip->single_cmd || bus->use_pio_for_commands)
+ return azx_single_get_response(bus, addr, res);
+ else
+ return azx_rirb_get_response(bus, addr, res);
+}
+
+static const struct hdac_bus_ops bus_core_ops = {
+ .command = azx_send_cmd,
+ .get_response = azx_get_response,
+};
+
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+/*
+ * DSP loading code (e.g. for CA0132)
+ */
+
+/* use the first stream for loading DSP */
+static struct azx_dev *
+azx_get_dsp_loader_dev(struct azx *chip)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ struct hdac_stream *s;
+
+ list_for_each_entry(s, &bus->stream_list, list)
+ if (s->index == chip->playback_index_offset)
+ return stream_to_azx_dev(s);
+
+ return NULL;
+}
+
+int snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
+ unsigned int byte_size,
+ struct snd_dma_buffer *bufp)
+{
+ struct hdac_bus *bus = &codec->bus->core;
+ struct azx *chip = bus_to_azx(bus);
+ struct azx_dev *azx_dev;
+ struct hdac_stream *hstr;
+ bool saved = false;
+ int err;
+
+ azx_dev = azx_get_dsp_loader_dev(chip);
+ hstr = azx_stream(azx_dev);
+ scoped_guard(spinlock_irq, &bus->reg_lock) {
+ if (hstr->opened) {
+ chip->saved_azx_dev = *azx_dev;
+ saved = true;
+ }
+ }
+
+ err = snd_hdac_dsp_prepare(hstr, format, byte_size, bufp);
+ if (err < 0) {
+ guard(spinlock_irq)(&bus->reg_lock);
+ if (saved)
+ *azx_dev = chip->saved_azx_dev;
+ return err;
+ }
+
+ hstr->prepared = 0;
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_prepare);
+
+void snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start)
+{
+ struct hdac_bus *bus = &codec->bus->core;
+ struct azx *chip = bus_to_azx(bus);
+ struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
+
+ snd_hdac_dsp_trigger(azx_stream(azx_dev), start);
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_trigger);
+
+void snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
+ struct snd_dma_buffer *dmab)
+{
+ struct hdac_bus *bus = &codec->bus->core;
+ struct azx *chip = bus_to_azx(bus);
+ struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
+ struct hdac_stream *hstr = azx_stream(azx_dev);
+
+ if (!dmab->area || !hstr->locked)
+ return;
+
+ snd_hdac_dsp_cleanup(hstr, dmab);
+ guard(spinlock_irq)(&bus->reg_lock);
+ if (hstr->opened)
+ *azx_dev = chip->saved_azx_dev;
+ hstr->locked = false;
+}
+EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_cleanup);
+#endif /* CONFIG_SND_HDA_DSP_LOADER */
+
+/*
+ * reset and start the controller registers
+ */
+void azx_init_chip(struct azx *chip, bool full_reset)
+{
+ if (snd_hdac_bus_init_chip(azx_bus(chip), full_reset)) {
+ /* correct RINTCNT for CXT */
+ if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND)
+ azx_writew(chip, RINTCNT, 0xc0);
+ }
+}
+EXPORT_SYMBOL_GPL(azx_init_chip);
+
+void azx_stop_all_streams(struct azx *chip)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+
+ snd_hdac_stop_streams(bus);
+}
+EXPORT_SYMBOL_GPL(azx_stop_all_streams);
+
+void azx_stop_chip(struct azx *chip)
+{
+ snd_hdac_bus_stop_chip(azx_bus(chip));
+}
+EXPORT_SYMBOL_GPL(azx_stop_chip);
+
+/*
+ * interrupt handler
+ */
+static void stream_update(struct hdac_bus *bus, struct hdac_stream *s)
+{
+ struct azx *chip = bus_to_azx(bus);
+ struct azx_dev *azx_dev = stream_to_azx_dev(s);
+
+ /* check whether this IRQ is really acceptable */
+ if (!chip->ops->position_check ||
+ chip->ops->position_check(chip, azx_dev)) {
+ spin_unlock(&bus->reg_lock);
+ snd_pcm_period_elapsed(azx_stream(azx_dev)->substream);
+ spin_lock(&bus->reg_lock);
+ }
+}
+
+irqreturn_t azx_interrupt(int irq, void *dev_id)
+{
+ struct azx *chip = dev_id;
+ struct hdac_bus *bus = azx_bus(chip);
+ u32 status;
+ bool active, handled = false;
+ int repeat = 0; /* count for avoiding endless loop */
+
+ if (azx_has_pm_runtime(chip))
+ if (!pm_runtime_active(chip->card->dev))
+ return IRQ_NONE;
+
+ guard(spinlock)(&bus->reg_lock);
+
+ if (chip->disabled)
+ return IRQ_NONE;
+
+ do {
+ status = azx_readl(chip, INTSTS);
+ if (status == 0 || status == 0xffffffff)
+ break;
+
+ handled = true;
+ active = false;
+ if (snd_hdac_bus_handle_stream_irq(bus, status, stream_update))
+ active = true;
+
+ status = azx_readb(chip, RIRBSTS);
+ if (status & RIRB_INT_MASK) {
+ /*
+ * Clearing the interrupt status here ensures that no
+ * interrupt gets masked after the RIRB wp is read in
+ * snd_hdac_bus_update_rirb. This avoids a possible
+ * race condition where codec response in RIRB may
+ * remain unserviced by IRQ, eventually falling back
+ * to polling mode in azx_rirb_get_response.
+ */
+ azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
+ active = true;
+ if (status & RIRB_INT_RESPONSE) {
+ if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND)
+ udelay(80);
+ snd_hdac_bus_update_rirb(bus);
+ }
+ }
+ } while (active && ++repeat < 10);
+
+ return IRQ_RETVAL(handled);
+}
+EXPORT_SYMBOL_GPL(azx_interrupt);
+
+/*
+ * Codec initerface
+ */
+
+/*
+ * Probe the given codec address
+ */
+static int probe_codec(struct azx *chip, int addr)
+{
+ unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
+ (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
+ struct hdac_bus *bus = azx_bus(chip);
+ int err;
+ unsigned int res = -1;
+
+ scoped_guard(mutex, &bus->cmd_mutex) {
+ chip->probing = 1;
+ azx_send_cmd(bus, cmd);
+ err = azx_get_response(bus, addr, &res);
+ chip->probing = 0;
+ }
+ if (err < 0 || res == -1)
+ return -EIO;
+ dev_dbg(chip->card->dev, "codec #%d probed OK\n", addr);
+ return 0;
+}
+
+void snd_hda_bus_reset(struct hda_bus *bus)
+{
+ struct azx *chip = bus_to_azx(&bus->core);
+
+ bus->in_reset = 1;
+ azx_stop_chip(chip);
+ azx_init_chip(chip, true);
+ if (bus->core.chip_init)
+ snd_hda_bus_reset_codecs(bus);
+ bus->in_reset = 0;
+}
+
+/* HD-audio bus initialization */
+int azx_bus_init(struct azx *chip, const char *model)
+{
+ struct hda_bus *bus = &chip->bus;
+ int err;
+
+ err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops);
+ if (err < 0)
+ return err;
+
+ bus->card = chip->card;
+ mutex_init(&bus->prepare_mutex);
+ bus->pci = chip->pci;
+ bus->modelname = model;
+ bus->mixer_assigned = -1;
+ bus->core.snoop = azx_snoop(chip);
+ if (chip->get_position[0] != azx_get_pos_lpib ||
+ chip->get_position[1] != azx_get_pos_lpib)
+ bus->core.use_posbuf = true;
+ bus->core.bdl_pos_adj = chip->bdl_pos_adj;
+ if (chip->driver_caps & AZX_DCAPS_CORBRP_SELF_CLEAR)
+ bus->core.corbrp_self_clear = true;
+
+ if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY)
+ bus->core.align_bdle_4k = true;
+
+ if (chip->driver_caps & AZX_DCAPS_PIO_COMMANDS)
+ bus->core.use_pio_for_commands = true;
+
+ /* enable sync_write flag for stable communication as default */
+ bus->core.sync_write = 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(azx_bus_init);
+
+/* Probe codecs */
+int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ int c, codecs, err;
+
+ codecs = 0;
+ if (!max_slots)
+ max_slots = AZX_DEFAULT_CODECS;
+
+ /* First try to probe all given codec slots */
+ for (c = 0; c < max_slots; c++) {
+ if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) {
+ if (probe_codec(chip, c) < 0) {
+ /* Some BIOSen give you wrong codec addresses
+ * that don't exist
+ */
+ dev_warn(chip->card->dev,
+ "Codec #%d probe error; disabling it...\n", c);
+ bus->codec_mask &= ~(1 << c);
+ /* no codecs */
+ if (bus->codec_mask == 0)
+ break;
+ /* More badly, accessing to a non-existing
+ * codec often screws up the controller chip,
+ * and disturbs the further communications.
+ * Thus if an error occurs during probing,
+ * better to reset the controller chip to
+ * get back to the sanity state.
+ */
+ azx_stop_chip(chip);
+ azx_init_chip(chip, true);
+ }
+ }
+ }
+
+ /* Then create codec instances */
+ for (c = 0; c < max_slots; c++) {
+ if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) {
+ struct hda_codec *codec;
+ err = snd_hda_codec_new(&chip->bus, chip->card, c, &codec);
+ if (err < 0)
+ continue;
+ codec->jackpoll_interval = chip->jackpoll_interval;
+ codec->beep_mode = chip->beep_mode;
+ codec->ctl_dev_id = chip->ctl_dev_id;
+ codecs++;
+ }
+ }
+ if (!codecs) {
+ dev_err(chip->card->dev, "no codecs initialized\n");
+ return -ENXIO;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(azx_probe_codecs);
+
+/* configure each codec instance */
+int azx_codec_configure(struct azx *chip)
+{
+ struct hda_codec *codec, *next;
+ int success = 0;
+
+ list_for_each_codec(codec, &chip->bus) {
+ if (!snd_hda_codec_configure(codec))
+ success++;
+ }
+
+ if (success) {
+ /* unregister failed codecs if any codec has been probed */
+ list_for_each_codec_safe(codec, next, &chip->bus) {
+ if (!codec->configured) {
+ codec_err(codec, "Unable to configure, disabling\n");
+ snd_hdac_device_unregister(&codec->core);
+ }
+ }
+ }
+
+ return success ? 0 : -ENODEV;
+}
+EXPORT_SYMBOL_GPL(azx_codec_configure);
+
+static int stream_direction(struct azx *chip, unsigned char index)
+{
+ if (index >= chip->capture_index_offset &&
+ index < chip->capture_index_offset + chip->capture_streams)
+ return SNDRV_PCM_STREAM_CAPTURE;
+ return SNDRV_PCM_STREAM_PLAYBACK;
+}
+
+/* initialize SD streams */
+int azx_init_streams(struct azx *chip)
+{
+ int i;
+ int stream_tags[2] = { 0, 0 };
+
+ /* initialize each stream (aka device)
+ * assign the starting bdl address to each stream (device)
+ * and initialize
+ */
+ for (i = 0; i < chip->num_streams; i++) {
+ struct azx_dev *azx_dev = kzalloc(sizeof(*azx_dev), GFP_KERNEL);
+ int dir, tag;
+
+ if (!azx_dev)
+ return -ENOMEM;
+
+ dir = stream_direction(chip, i);
+ /* stream tag must be unique throughout
+ * the stream direction group,
+ * valid values 1...15
+ * use separate stream tag if the flag
+ * AZX_DCAPS_SEPARATE_STREAM_TAG is used
+ */
+ if (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG)
+ tag = ++stream_tags[dir];
+ else
+ tag = i + 1;
+ snd_hdac_stream_init(azx_bus(chip), azx_stream(azx_dev),
+ i, dir, tag);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(azx_init_streams);
+
+void azx_free_streams(struct azx *chip)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ struct hdac_stream *s;
+
+ while (!list_empty(&bus->stream_list)) {
+ s = list_first_entry(&bus->stream_list, struct hdac_stream, list);
+ list_del(&s->list);
+ kfree(stream_to_azx_dev(s));
+ }
+}
+EXPORT_SYMBOL_GPL(azx_free_streams);
diff --git a/sound/hda/common/controller_trace.h b/sound/hda/common/controller_trace.h
new file mode 100644
index 000000000000..7f5841f8919e
--- /dev/null
+++ b/sound/hda/common/controller_trace.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hda_controller
+#define TRACE_INCLUDE_FILE controller_trace
+
+#if !defined(_TRACE_HDA_CONTROLLER_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_HDA_CONTROLLER_H
+
+#include <linux/tracepoint.h>
+
+struct azx;
+struct azx_dev;
+
+TRACE_EVENT(azx_pcm_trigger,
+
+ TP_PROTO(struct azx *chip, struct azx_dev *dev, int cmd),
+
+ TP_ARGS(chip, dev, cmd),
+
+ TP_STRUCT__entry(
+ __field( int, card )
+ __field( int, idx )
+ __field( int, cmd )
+ ),
+
+ TP_fast_assign(
+ __entry->card = (chip)->card->number;
+ __entry->idx = (dev)->core.index;
+ __entry->cmd = cmd;
+ ),
+
+ TP_printk("[%d:%d] cmd=%d", __entry->card, __entry->idx, __entry->cmd)
+);
+
+TRACE_EVENT(azx_get_position,
+
+ TP_PROTO(struct azx *chip, struct azx_dev *dev, unsigned int pos, unsigned int delay),
+
+ TP_ARGS(chip, dev, pos, delay),
+
+ TP_STRUCT__entry(
+ __field( int, card )
+ __field( int, idx )
+ __field( unsigned int, pos )
+ __field( unsigned int, delay )
+ ),
+
+ TP_fast_assign(
+ __entry->card = (chip)->card->number;
+ __entry->idx = (dev)->core.index;
+ __entry->pos = pos;
+ __entry->delay = delay;
+ ),
+
+ TP_printk("[%d:%d] pos=%u, delay=%u", __entry->card, __entry->idx, __entry->pos, __entry->delay)
+);
+
+DECLARE_EVENT_CLASS(azx_pcm,
+ TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+
+ TP_ARGS(chip, azx_dev),
+
+ TP_STRUCT__entry(
+ __field( unsigned char, stream_tag )
+ ),
+
+ TP_fast_assign(
+ __entry->stream_tag = (azx_dev)->core.stream_tag;
+ ),
+
+ TP_printk("stream_tag: %d", __entry->stream_tag)
+);
+
+DEFINE_EVENT(azx_pcm, azx_pcm_open,
+ TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+ TP_ARGS(chip, azx_dev)
+);
+
+DEFINE_EVENT(azx_pcm, azx_pcm_close,
+ TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+ TP_ARGS(chip, azx_dev)
+);
+
+DEFINE_EVENT(azx_pcm, azx_pcm_hw_params,
+ TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+ TP_ARGS(chip, azx_dev)
+);
+
+DEFINE_EVENT(azx_pcm, azx_pcm_prepare,
+ TP_PROTO(struct azx *chip, struct azx_dev *azx_dev),
+ TP_ARGS(chip, azx_dev)
+);
+
+#endif /* _TRACE_HDA_CONTROLLER_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
diff --git a/sound/hda/common/hda_auto_parser.h b/sound/hda/common/hda_auto_parser.h
new file mode 100644
index 000000000000..87af3d8c02f7
--- /dev/null
+++ b/sound/hda/common/hda_auto_parser.h
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ */
+
+#ifndef __SOUND_HDA_AUTO_PARSER_H
+#define __SOUND_HDA_AUTO_PARSER_H
+
+#include "hda_local.h"
+
+/*
+ * Helper for automatic pin configuration
+ */
+
+enum {
+ AUTO_PIN_MIC,
+ AUTO_PIN_LINE_IN,
+ AUTO_PIN_CD,
+ AUTO_PIN_AUX,
+ AUTO_PIN_LAST
+};
+
+enum {
+ AUTO_PIN_LINE_OUT,
+ AUTO_PIN_SPEAKER_OUT,
+ AUTO_PIN_HP_OUT
+};
+
+#define AUTO_CFG_MAX_OUTS HDA_MAX_OUTS
+#define AUTO_CFG_MAX_INS 18
+
+struct auto_pin_cfg_item {
+ hda_nid_t pin;
+ int type;
+ unsigned int is_headset_mic:1;
+ unsigned int is_headphone_mic:1; /* Mic-only in headphone jack */
+ unsigned int has_boost_on_pin:1;
+ int order;
+};
+
+struct auto_pin_cfg;
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input);
+int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
+ const struct auto_pin_cfg *cfg,
+ char *label, int maxlen, int *indexp);
+
+enum {
+ INPUT_PIN_ATTR_UNUSED, /* pin not connected */
+ INPUT_PIN_ATTR_INT, /* internal mic/line-in */
+ INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */
+ INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */
+ INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */
+ INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */
+ INPUT_PIN_ATTR_LAST = INPUT_PIN_ATTR_FRONT,
+};
+
+int snd_hda_get_input_pin_attr(unsigned int def_conf);
+
+struct auto_pin_cfg {
+ int line_outs;
+ /* sorted in the order of Front/Surr/CLFE/Side */
+ hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS];
+ int speaker_outs;
+ hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS];
+ int hp_outs;
+ int line_out_type; /* AUTO_PIN_XXX_OUT */
+ hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
+ int num_inputs;
+ struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
+ int dig_outs;
+ hda_nid_t dig_out_pins[2];
+ hda_nid_t dig_in_pin;
+ hda_nid_t mono_out_pin;
+ int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */
+ int dig_in_type; /* HDA_PCM_TYPE_XXX */
+};
+
+/* bit-flags for snd_hda_parse_pin_def_config() behavior */
+#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */
+#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */
+#define HDA_PINCFG_HEADSET_MIC (1 << 2) /* Try to find headset mic; mark seq number as 0xc to trigger */
+#define HDA_PINCFG_HEADPHONE_MIC (1 << 3) /* Try to find headphone mic; mark seq number as 0xd to trigger */
+
+int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg,
+ const hda_nid_t *ignore_nids,
+ unsigned int cond_flags);
+
+/* older function */
+#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \
+ snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0)
+
+static inline int auto_cfg_hp_outs(const struct auto_pin_cfg *cfg)
+{
+ return (cfg->line_out_type == AUTO_PIN_HP_OUT) ?
+ cfg->line_outs : cfg->hp_outs;
+}
+static inline const hda_nid_t *auto_cfg_hp_pins(const struct auto_pin_cfg *cfg)
+{
+ return (cfg->line_out_type == AUTO_PIN_HP_OUT) ?
+ cfg->line_out_pins : cfg->hp_pins;
+}
+static inline int auto_cfg_speaker_outs(const struct auto_pin_cfg *cfg)
+{
+ return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ?
+ cfg->line_outs : cfg->speaker_outs;
+}
+static inline const hda_nid_t *auto_cfg_speaker_pins(const struct auto_pin_cfg *cfg)
+{
+ return (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) ?
+ cfg->line_out_pins : cfg->speaker_pins;
+}
+
+#endif /* __SOUND_HDA_AUTO_PARSER_H */
diff --git a/sound/hda/common/hda_beep.h b/sound/hda/common/hda_beep.h
new file mode 100644
index 000000000000..923ea862446a
--- /dev/null
+++ b/sound/hda/common/hda_beep.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Digital Beep Input Interface for HD-audio codec
+ *
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
+ * Copyright (c) 2008 Embedded Alley Solutions Inc
+ */
+
+#ifndef __SOUND_HDA_BEEP_H
+#define __SOUND_HDA_BEEP_H
+
+#include <sound/hda_codec.h>
+
+#define HDA_BEEP_MODE_OFF 0
+#define HDA_BEEP_MODE_ON 1
+
+/* beep information */
+struct hda_beep {
+ struct input_dev *dev;
+ struct hda_codec *codec;
+ char phys[32];
+ int tone;
+ hda_nid_t nid;
+ unsigned int registered:1;
+ unsigned int enabled:1;
+ unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */
+ unsigned int playing:1;
+ unsigned int keep_power_at_enable:1; /* set by driver */
+ struct work_struct beep_work; /* scheduled task for beep event */
+ void (*power_hook)(struct hda_beep *beep, bool on);
+};
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+int snd_hda_enable_beep_device(struct hda_codec *codec, int enable);
+int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
+void snd_hda_detach_beep_device(struct hda_codec *codec);
+#else
+static inline int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
+{
+ return 0;
+}
+static inline void snd_hda_detach_beep_device(struct hda_codec *codec)
+{
+}
+#endif
+#endif
diff --git a/sound/hda/common/hda_controller.h b/sound/hda/common/hda_controller.h
new file mode 100644
index 000000000000..c2d0109866e6
--- /dev/null
+++ b/sound/hda/common/hda_controller.h
@@ -0,0 +1,215 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Common functionality for the alsa driver code base for HD Audio.
+ */
+
+#ifndef __SOUND_HDA_CONTROLLER_H
+#define __SOUND_HDA_CONTROLLER_H
+
+#include <linux/timecounter.h>
+#include <linux/interrupt.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_register.h>
+
+#define AZX_MAX_CODECS HDA_MAX_CODECS
+#define AZX_DEFAULT_CODECS 4
+
+/* driver quirks (capabilities) */
+/* bits 0-7 are used for indicating driver type */
+#define AZX_DCAPS_NO_TCSEL (1 << 8) /* No Intel TCSEL bit */
+#define AZX_DCAPS_NO_MSI (1 << 9) /* No MSI support */
+#define AZX_DCAPS_SNOOP_MASK (3 << 10) /* snoop type mask */
+#define AZX_DCAPS_SNOOP_OFF (1 << 12) /* snoop default off */
+#ifdef CONFIG_SND_HDA_I915
+#define AZX_DCAPS_I915_COMPONENT (1 << 13) /* bind with i915 gfx */
+#else
+#define AZX_DCAPS_I915_COMPONENT 0 /* NOP */
+#endif
+/* 14 unused */
+#define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */
+#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */
+#define AZX_DCAPS_AMD_WORKAROUND (1 << 17) /* AMD-specific workaround */
+#define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */
+/* 19 unused */
+#define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */
+#define AZX_DCAPS_NO_ALIGN_BUFSIZE (1 << 21) /* no buffer size alignment */
+/* 22 unused */
+#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */
+/* 24 unused */
+#define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */
+#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */
+#define AZX_DCAPS_RETRY_PROBE (1 << 27) /* retry probe if no codec is configured */
+#define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */
+#define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */
+#define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */
+#define AZX_DCAPS_PIO_COMMANDS (1 << 31) /* Use PIO instead of CORB for commands */
+
+enum {
+ AZX_SNOOP_TYPE_NONE,
+ AZX_SNOOP_TYPE_SCH,
+ AZX_SNOOP_TYPE_ATI,
+ AZX_SNOOP_TYPE_NVIDIA,
+};
+
+struct azx_dev {
+ struct hdac_stream core;
+
+ unsigned int irq_pending:1;
+ /*
+ * For VIA:
+ * A flag to ensure DMA position is 0
+ * when link position is not greater than FIFO size
+ */
+ unsigned int insufficient:1;
+};
+
+#define azx_stream(dev) (&(dev)->core)
+#define stream_to_azx_dev(s) container_of(s, struct azx_dev, core)
+
+struct azx;
+
+/* Functions to read/write to hda registers. */
+struct hda_controller_ops {
+ /* Disable msi if supported, PCI only */
+ int (*disable_msi_reset_irq)(struct azx *);
+ /* Check if current position is acceptable */
+ int (*position_check)(struct azx *chip, struct azx_dev *azx_dev);
+ /* enable/disable the link power */
+ int (*link_power)(struct azx *chip, bool enable);
+};
+
+struct azx_pcm {
+ struct azx *chip;
+ struct snd_pcm *pcm;
+ struct hda_codec *codec;
+ struct hda_pcm *info;
+ struct list_head list;
+};
+
+typedef unsigned int (*azx_get_pos_callback_t)(struct azx *, struct azx_dev *);
+typedef int (*azx_get_delay_callback_t)(struct azx *, struct azx_dev *, unsigned int pos);
+
+struct azx {
+ struct hda_bus bus;
+
+ struct snd_card *card;
+ struct pci_dev *pci;
+ int dev_index;
+
+ /* chip type specific */
+ int driver_type;
+ unsigned int driver_caps;
+ int playback_streams;
+ int playback_index_offset;
+ int capture_streams;
+ int capture_index_offset;
+ int num_streams;
+ int jackpoll_interval; /* jack poll interval in jiffies */
+
+ /* Register interaction. */
+ const struct hda_controller_ops *ops;
+
+ /* position adjustment callbacks */
+ azx_get_pos_callback_t get_position[2];
+ azx_get_delay_callback_t get_delay[2];
+
+ /* locks */
+ struct mutex open_mutex; /* Prevents concurrent open/close operations */
+
+ /* PCM */
+ struct list_head pcm_list; /* azx_pcm list */
+
+ /* HD codec */
+ int codec_probe_mask; /* copied from probe_mask option */
+ unsigned int beep_mode;
+ bool ctl_dev_id;
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+ const struct firmware *fw;
+#endif
+
+ /* flags */
+ int bdl_pos_adj;
+ unsigned int running:1;
+ unsigned int fallback_to_single_cmd:1;
+ unsigned int single_cmd:1;
+ unsigned int msi:1;
+ unsigned int probing:1; /* codec probing phase */
+ unsigned int snoop:1;
+ unsigned int uc_buffer:1; /* non-cached pages for stream buffers */
+ unsigned int align_buffer_size:1;
+ unsigned int disabled:1; /* disabled by vga_switcheroo */
+ unsigned int pm_prepared:1;
+
+ /* GTS present */
+ unsigned int gts_present:1;
+
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+ struct azx_dev saved_azx_dev;
+#endif
+};
+
+#define azx_bus(chip) (&(chip)->bus.core)
+#define bus_to_azx(_bus) container_of(_bus, struct azx, bus.core)
+
+static inline bool azx_snoop(struct azx *chip)
+{
+ return !IS_ENABLED(CONFIG_X86) || chip->snoop;
+}
+
+/*
+ * macros for easy use
+ */
+
+#define azx_writel(chip, reg, value) \
+ snd_hdac_chip_writel(azx_bus(chip), reg, value)
+#define azx_readl(chip, reg) \
+ snd_hdac_chip_readl(azx_bus(chip), reg)
+#define azx_writew(chip, reg, value) \
+ snd_hdac_chip_writew(azx_bus(chip), reg, value)
+#define azx_readw(chip, reg) \
+ snd_hdac_chip_readw(azx_bus(chip), reg)
+#define azx_writeb(chip, reg, value) \
+ snd_hdac_chip_writeb(azx_bus(chip), reg, value)
+#define azx_readb(chip, reg) \
+ snd_hdac_chip_readb(azx_bus(chip), reg)
+
+#define azx_has_pm_runtime(chip) \
+ ((chip)->driver_caps & AZX_DCAPS_PM_RUNTIME)
+
+/* PCM setup */
+static inline struct azx_dev *get_azx_dev(struct snd_pcm_substream *substream)
+{
+ return substream->runtime->private_data;
+}
+unsigned int azx_get_position(struct azx *chip, struct azx_dev *azx_dev);
+unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev);
+unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev);
+
+/* Stream control. */
+void azx_stop_all_streams(struct azx *chip);
+
+/* Allocation functions. */
+#define azx_alloc_stream_pages(chip) \
+ snd_hdac_bus_alloc_stream_pages(azx_bus(chip))
+#define azx_free_stream_pages(chip) \
+ snd_hdac_bus_free_stream_pages(azx_bus(chip))
+
+/* Low level azx interface */
+void azx_init_chip(struct azx *chip, bool full_reset);
+void azx_stop_chip(struct azx *chip);
+#define azx_enter_link_reset(chip) \
+ snd_hdac_bus_enter_link_reset(azx_bus(chip))
+irqreturn_t azx_interrupt(int irq, void *dev_id);
+
+/* Codec interface */
+int azx_bus_init(struct azx *chip, const char *model);
+int azx_probe_codecs(struct azx *chip, unsigned int max_slots);
+int azx_codec_configure(struct azx *chip);
+int azx_init_streams(struct azx *chip);
+void azx_free_streams(struct azx *chip);
+
+#endif /* __SOUND_HDA_CONTROLLER_H */
diff --git a/sound/hda/common/hda_jack.h b/sound/hda/common/hda_jack.h
new file mode 100644
index 000000000000..ff7d289c034b
--- /dev/null
+++ b/sound/hda/common/hda_jack.h
@@ -0,0 +1,195 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Jack-detection handling for HD-audio
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#ifndef __SOUND_HDA_JACK_H
+#define __SOUND_HDA_JACK_H
+
+#include <linux/err.h>
+#include <sound/jack.h>
+
+struct auto_pin_cfg;
+struct hda_jack_tbl;
+struct hda_jack_callback;
+
+typedef void (*hda_jack_callback_fn) (struct hda_codec *, struct hda_jack_callback *);
+
+struct hda_jack_callback {
+ hda_nid_t nid;
+ int dev_id;
+ hda_jack_callback_fn func;
+ unsigned int private_data; /* arbitrary data */
+ unsigned int unsol_res; /* unsolicited event bits */
+ struct hda_jack_tbl *jack; /* associated jack entry */
+ struct hda_jack_callback *next;
+};
+
+struct hda_jack_tbl {
+ hda_nid_t nid;
+ int dev_id;
+ unsigned char tag; /* unsol event tag */
+ struct hda_jack_callback *callback;
+ /* jack-detection stuff */
+ unsigned int pin_sense; /* cached pin-sense value */
+ unsigned int jack_detect:1; /* capable of jack-detection? */
+ unsigned int jack_dirty:1; /* needs to update? */
+ unsigned int phantom_jack:1; /* a fixed, always present port? */
+ unsigned int block_report:1; /* in a transitional state - do not report to userspace */
+ hda_nid_t gating_jack; /* valid when gating jack plugged */
+ hda_nid_t gated_jack; /* gated is dependent on this jack */
+ hda_nid_t key_report_jack; /* key reports to this jack */
+ int type;
+ int button_state;
+ struct snd_jack *jack;
+};
+
+struct hda_jack_keymap {
+ enum snd_jack_types type;
+ int key;
+};
+
+struct hda_jack_tbl *
+snd_hda_jack_tbl_get_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id);
+
+/**
+ * snd_hda_jack_tbl_get - query the jack-table entry for the given NID
+ * @codec: the HDA codec
+ * @nid: pin NID to refer to
+ */
+static inline struct hda_jack_tbl *
+snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_jack_tbl_get_mst(codec, nid, 0);
+}
+
+struct hda_jack_tbl *
+snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec,
+ unsigned char tag, int dev_id);
+
+void snd_hda_jack_tbl_disconnect(struct hda_codec *codec);
+void snd_hda_jack_tbl_clear(struct hda_codec *codec);
+
+void snd_hda_jack_set_dirty_all(struct hda_codec *codec);
+
+int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id);
+
+struct hda_jack_callback *
+snd_hda_jack_detect_enable_callback_mst(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, hda_jack_callback_fn func);
+
+/**
+ * snd_hda_jack_detect_enable - enable the jack-detection
+ * @codec: the HDA codec
+ * @nid: pin NID to enable
+ * @func: callback function to register
+ *
+ * In the case of error, the return value will be a pointer embedded with
+ * errno. Check and handle the return value appropriately with standard
+ * macros such as @IS_ERR() and @PTR_ERR().
+ */
+static inline struct hda_jack_callback *
+snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
+ hda_jack_callback_fn cb)
+{
+ return snd_hda_jack_detect_enable_callback_mst(codec, nid, 0, cb);
+}
+
+int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
+ hda_nid_t gating_nid);
+
+int snd_hda_jack_bind_keymap(struct hda_codec *codec, hda_nid_t key_nid,
+ const struct hda_jack_keymap *keymap,
+ hda_nid_t jack_nid);
+
+void snd_hda_jack_set_button_state(struct hda_codec *codec, hda_nid_t jack_nid,
+ int button_state);
+
+u32 snd_hda_jack_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id);
+
+/* the jack state returned from snd_hda_jack_detect_state() */
+enum {
+ HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT, HDA_JACK_PHANTOM,
+};
+
+int snd_hda_jack_detect_state_mst(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id);
+
+/**
+ * snd_hda_jack_detect_state - query pin Presence Detect status
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ *
+ * Query and return the pin's Presence Detect status, as either
+ * HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM.
+ */
+static inline int
+snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_jack_detect_state_mst(codec, nid, 0);
+}
+
+/**
+ * snd_hda_jack_detect_mst - Detect the jack
+ * @codec: the HDA codec
+ * @nid: pin NID to check jack detection
+ * @dev_id: pin device entry id
+ */
+static inline bool
+snd_hda_jack_detect_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id)
+{
+ return snd_hda_jack_detect_state_mst(codec, nid, dev_id) !=
+ HDA_JACK_NOT_PRESENT;
+}
+
+/**
+ * snd_hda_jack_detect - Detect the jack
+ * @codec: the HDA codec
+ * @nid: pin NID to check jack detection
+ */
+static inline bool
+snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_jack_detect_mst(codec, nid, 0);
+}
+
+bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid);
+
+int snd_hda_jack_add_kctl_mst(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, const char *name, bool phantom_jack,
+ int type, const struct hda_jack_keymap *keymap);
+
+/**
+ * snd_hda_jack_add_kctl - Add a kctl for the given pin
+ * @codec: the HDA codec
+ * @nid: pin NID to assign
+ * @name: string name for the jack
+ * @phantom_jack: flag to deal as a phantom jack
+ * @type: jack type bits to be reported, 0 for guessing from pincfg
+ * @keymap: optional jack / key mapping
+ *
+ * This assigns a jack-detection kctl to the given pin. The kcontrol
+ * will have the given name and index.
+ */
+static inline int
+snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
+ const char *name, bool phantom_jack,
+ int type, const struct hda_jack_keymap *keymap)
+{
+ return snd_hda_jack_add_kctl_mst(codec, nid, 0,
+ name, phantom_jack, type, keymap);
+}
+
+int snd_hda_jack_add_kctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg);
+
+void snd_hda_jack_report_sync(struct hda_codec *codec);
+
+void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res);
+
+void snd_hda_jack_poll_all(struct hda_codec *codec);
+
+#endif /* __SOUND_HDA_JACK_H */
diff --git a/sound/pci/hda/hda_local.h b/sound/hda/common/hda_local.h
index 46bbefe2e4a9..a7e53277a0fe 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/hda/common/hda_local.h
@@ -1,28 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Universal Interface for Intel High Definition Audio Codec
*
* Local helper functions
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 59
- * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __SOUND_HDA_LOCAL_H
#define __SOUND_HDA_LOCAL_H
+#include <sound/pcm_drm_eld.h>
+
/* We abuse kcontrol_new.subdev field to pass the NID corresponding to
* the given new control. If id.subdev has a bit flag HDA_SUBDEV_NID_FLAG,
* snd_hda_ctl_add() takes the lower-bit subdev value as a valid NID.
@@ -89,7 +78,7 @@
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
.subdevice = HDA_SUBDEV_AMP_FLAG, \
.info = snd_hda_mixer_amp_switch_info, \
- .get = snd_hda_mixer_amp_switch_get, \
+ .get = snd_hda_mixer_amp_switch_get_beep, \
.put = snd_hda_mixer_amp_switch_put_beep, \
.private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
#else
@@ -113,7 +102,7 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
- unsigned int size, unsigned int __user *tlv);
+ unsigned int size, unsigned int __user *_tlv);
int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
@@ -121,104 +110,70 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
#ifdef CONFIG_SND_HDA_INPUT_BEEP
+int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
#endif
/* lowlevel accessor with caching; use carefully */
-int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
- int direction, int index);
-int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
- int direction, int idx, int mask, int val);
+#define snd_hda_codec_amp_read(codec, nid, ch, dir, idx) \
+ snd_hdac_regmap_get_amp(&(codec)->core, nid, ch, dir, idx)
+int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid,
+ int ch, int dir, int idx, int mask, int val);
int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
- int dir, int idx, int mask, int val);
-#ifdef SND_HDA_NEEDS_RESUME
-void snd_hda_codec_resume_amp(struct hda_codec *codec);
-#endif
-
+ int direction, int idx, int mask, int val);
+int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
+ int direction, int idx, int mask, int val);
+int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
+ int dir, int idx, int mask, int val);
void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int *tlv);
struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
const char *name);
-int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
- unsigned int *tlv, const char **slaves);
+int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
+ unsigned int *tlv, const char * const *followers,
+ const char *suffix, bool init_follower_vol,
+ unsigned int access, struct snd_kcontrol **ctl_ret);
+#define snd_hda_add_vmaster(codec, name, tlv, followers, suffix, access) \
+ __snd_hda_add_vmaster(codec, name, tlv, followers, suffix, true, access, NULL)
int snd_hda_codec_reset(struct hda_codec *codec);
+void snd_hda_codec_disconnect_pcms(struct hda_codec *codec);
+
+#define snd_hda_regmap_sync(codec) snd_hdac_regmap_sync(&(codec)->core)
+
+struct hda_vmaster_mute_hook {
+ /* below two fields must be filled by the caller of
+ * snd_hda_add_vmaster_hook() beforehand
+ */
+ struct snd_kcontrol *sw_kctl;
+ void (*hook)(void *, int);
+ /* below are initialized automatically */
+ struct hda_codec *codec;
+};
+
+int snd_hda_add_vmaster_hook(struct hda_codec *codec,
+ struct hda_vmaster_mute_hook *hook);
+void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook);
/* amp value bits */
#define HDA_AMP_MUTE 0x80
#define HDA_AMP_UNMUTE 0x00
#define HDA_AMP_VOLMASK 0x7f
-/* mono switch binding multiple inputs */
-#define HDA_BIND_MUTE_MONO(xname, nid, channel, indices, direction) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
- .info = snd_hda_mixer_amp_switch_info, \
- .get = snd_hda_mixer_bind_switch_get, \
- .put = snd_hda_mixer_bind_switch_put, \
- .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, indices, direction) }
-
-/* stereo switch binding multiple inputs */
-#define HDA_BIND_MUTE(xname,nid,indices,dir) \
- HDA_BIND_MUTE_MONO(xname,nid,3,indices,dir)
-
-int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-
-/* more generic bound controls */
-struct hda_ctl_ops {
- snd_kcontrol_info_t *info;
- snd_kcontrol_get_t *get;
- snd_kcontrol_put_t *put;
- snd_kcontrol_tlv_rw_t *tlv;
-};
-
-extern struct hda_ctl_ops snd_hda_bind_vol; /* for bind-volume with TLV */
-extern struct hda_ctl_ops snd_hda_bind_sw; /* for bind-switch */
-
-struct hda_bind_ctls {
- struct hda_ctl_ops *ops;
- unsigned long values[];
-};
-
-int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo);
-int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
- unsigned int size, unsigned int __user *tlv);
-
-#define HDA_BIND_VOL(xname, bindrec) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\
- SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,\
- .info = snd_hda_mixer_bind_ctls_info,\
- .get = snd_hda_mixer_bind_ctls_get,\
- .put = snd_hda_mixer_bind_ctls_put,\
- .tlv = { .c = snd_hda_mixer_bind_tlv },\
- .private_value = (long) (bindrec) }
-#define HDA_BIND_SW(xname, bindrec) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\
- .name = xname, \
- .info = snd_hda_mixer_bind_ctls_info,\
- .get = snd_hda_mixer_bind_ctls_get,\
- .put = snd_hda_mixer_bind_ctls_put,\
- .private_value = (long) (bindrec) }
-
/*
* SPDIF I/O
*/
-int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid);
+int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
+ hda_nid_t associated_nid,
+ hda_nid_t cvt_nid, int type);
+#define snd_hda_create_spdif_out_ctls(codec, anid, cnid) \
+ snd_hda_create_dig_out_ctls(codec, anid, cnid, HDA_PCM_TYPE_SPDIF)
int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
/*
* input MUX helper
*/
-#define HDA_MAX_NUM_INPUTS 16
+#define HDA_MAX_NUM_INPUTS 36
struct hda_input_mux_item {
char label[32];
unsigned int index;
@@ -234,29 +189,9 @@ int snd_hda_input_mux_put(struct hda_codec *codec,
const struct hda_input_mux *imux,
struct snd_ctl_elem_value *ucontrol, hda_nid_t nid,
unsigned int *cur_val);
-
-/*
- * Channel mode helper
- */
-struct hda_channel_mode {
- int channels;
- const struct hda_verb *sequence;
-};
-
-int snd_hda_ch_mode_info(struct hda_codec *codec,
- struct snd_ctl_elem_info *uinfo,
- const struct hda_channel_mode *chmode,
- int num_chmodes);
-int snd_hda_ch_mode_get(struct hda_codec *codec,
- struct snd_ctl_elem_value *ucontrol,
- const struct hda_channel_mode *chmode,
- int num_chmodes,
- int max_channels);
-int snd_hda_ch_mode_put(struct hda_codec *codec,
- struct snd_ctl_elem_value *ucontrol,
- const struct hda_channel_mode *chmode,
- int num_chmodes,
- int *max_channelsp);
+int snd_hda_add_imux_item(struct hda_codec *codec,
+ struct hda_input_mux *imux, const char *label,
+ int index, int *type_idx);
/*
* Multi-channel / digital-out PCM helper
@@ -265,13 +200,16 @@ int snd_hda_ch_mode_put(struct hda_codec *codec,
enum { HDA_FRONT, HDA_REAR, HDA_CLFE, HDA_SIDE }; /* index for dac_nidx */
enum { HDA_DIG_NONE, HDA_DIG_EXCLUSIVE, HDA_DIG_ANALOG_DUP }; /* dig_out_used */
+#define HDA_MAX_OUTS 5
+
struct hda_multi_out {
int num_dacs; /* # of DACs, must be more than 1 */
- hda_nid_t *dac_nids; /* DAC list */
+ const hda_nid_t *dac_nids; /* DAC list */
hda_nid_t hp_nid; /* optional DAC for HP, 0 when not exists */
- hda_nid_t extra_out_nid[3]; /* optional DACs, 0 when not exists */
+ hda_nid_t hp_out_nid[HDA_MAX_OUTS]; /* DACs for multiple HPs */
+ hda_nid_t extra_out_nid[HDA_MAX_OUTS]; /* other (e.g. speaker) DACs */
hda_nid_t dig_out_nid; /* digital out audio widget */
- hda_nid_t *slave_dig_outs;
+ const hda_nid_t *follower_dig_outs;
int max_channels; /* currently supported analog channels */
int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */
int no_share_stream; /* don't share a stream with multiple pins */
@@ -311,125 +249,144 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
struct hda_multi_out *mout);
/*
- * generic codec parser
- */
-#ifdef CONFIG_SND_HDA_GENERIC
-int snd_hda_parse_generic_codec(struct hda_codec *codec);
-#else
-static inline int snd_hda_parse_generic_codec(struct hda_codec *codec)
-{
- return -ENODEV;
-}
-#endif
-
-/*
* generic proc interface
*/
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
int snd_hda_codec_proc_new(struct hda_codec *codec);
#else
static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; }
#endif
-#define SND_PRINT_RATES_ADVISED_BUFSIZE 80
-void snd_print_pcm_rates(int pcm, char *buf, int buflen);
-
#define SND_PRINT_BITS_ADVISED_BUFSIZE 16
void snd_print_pcm_bits(int pcm, char *buf, int buflen);
/*
* Misc
*/
-int snd_hda_check_board_config(struct hda_codec *codec, int num_configs,
- const char **modelnames,
- const struct snd_pci_quirk *pci_list);
-int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
- int num_configs, const char **models,
- const struct snd_pci_quirk *tbl);
int snd_hda_add_new_ctls(struct hda_codec *codec,
- struct snd_kcontrol_new *knew);
+ const struct snd_kcontrol_new *knew);
/*
- * unsolicited event handler
+ * Fix-up pin default configurations and add default verbs
*/
-#define HDA_UNSOL_QUEUE_SIZE 64
+struct hda_pintbl {
+ hda_nid_t nid;
+ u32 val;
+};
-struct hda_bus_unsolicited {
- /* ring buffer */
- u32 queue[HDA_UNSOL_QUEUE_SIZE * 2];
- unsigned int rp, wp;
+struct hda_model_fixup {
+ const int id;
+ const char *name;
+};
- /* workqueue */
- struct work_struct work;
- struct hda_bus *bus;
+struct hda_fixup {
+ int type;
+ bool chained:1; /* call the chained fixup(s) after this */
+ bool chained_before:1; /* call the chained fixup(s) before this */
+ int chain_id;
+ union {
+ const struct hda_pintbl *pins;
+ const struct hda_verb *verbs;
+ void (*func)(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action);
+ } v;
};
/*
- * Helper for automatic pin configuration
+ * extended form of snd_pci_quirk:
+ * for PCI SSID matching, use SND_PCI_QUIRK() like before;
+ * for codec SSID matching, use the new HDA_CODEC_QUIRK() instead
*/
-
-enum {
- AUTO_PIN_MIC,
- AUTO_PIN_LINE_IN,
- AUTO_PIN_CD,
- AUTO_PIN_AUX,
- AUTO_PIN_LAST
+struct hda_quirk {
+ unsigned short subvendor; /* PCI subvendor ID */
+ unsigned short subdevice; /* PCI subdevice ID */
+ unsigned short subdevice_mask; /* bitmask to match */
+ bool match_codec_ssid; /* match only with codec SSID */
+ int value; /* value */
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ const char *name; /* name of the device (optional) */
+#endif
};
-enum {
- AUTO_PIN_LINE_OUT,
- AUTO_PIN_SPEAKER_OUT,
- AUTO_PIN_HP_OUT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+#define HDA_CODEC_QUIRK(vend, dev, xname, val) \
+ { _SND_PCI_QUIRK_ID(vend, dev), .value = (val), .name = (xname),\
+ .match_codec_ssid = true }
+#else
+#define HDA_CODEC_QUIRK(vend, dev, xname, val) \
+ { _SND_PCI_QUIRK_ID(vend, dev), .value = (val), \
+ .match_codec_ssid = true }
+#endif
+
+struct snd_hda_pin_quirk {
+ unsigned int codec; /* Codec vendor/device ID */
+ unsigned short subvendor; /* PCI subvendor ID */
+ const struct hda_pintbl *pins; /* list of matching pins */
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ const char *name;
+#endif
+ int value; /* quirk value */
};
-#define AUTO_CFG_MAX_OUTS 5
-#define AUTO_CFG_MAX_INS 8
+#ifdef CONFIG_SND_DEBUG_VERBOSE
-struct auto_pin_cfg_item {
- hda_nid_t pin;
- int type;
-};
+#define SND_HDA_PIN_QUIRK(_codec, _subvendor, _name, _value, _pins...) \
+ { .codec = _codec,\
+ .subvendor = _subvendor,\
+ .name = _name,\
+ .value = _value,\
+ .pins = (const struct hda_pintbl[]) { _pins, {0, 0}} \
+ }
+#else
+
+#define SND_HDA_PIN_QUIRK(_codec, _subvendor, _name, _value, _pins...) \
+ { .codec = _codec,\
+ .subvendor = _subvendor,\
+ .value = _value,\
+ .pins = (const struct hda_pintbl[]) { _pins, {0, 0}} \
+ }
+
+#endif
-struct auto_pin_cfg;
-const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
- int check_location);
-const char *hda_get_autocfg_input_label(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg,
- int input);
-int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
- int index, int *type_index_ret);
+#define HDA_FIXUP_ID_NOT_SET -1
+#define HDA_FIXUP_ID_NO_FIXUP -2
+/* fixup types */
enum {
- INPUT_PIN_ATTR_UNUSED, /* pin not connected */
- INPUT_PIN_ATTR_INT, /* internal mic/line-in */
- INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */
- INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */
- INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */
- INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */
+ HDA_FIXUP_INVALID,
+ HDA_FIXUP_PINS,
+ HDA_FIXUP_VERBS,
+ HDA_FIXUP_FUNC,
+ HDA_FIXUP_PINCTLS,
};
-int snd_hda_get_input_pin_attr(unsigned int def_conf);
-
-struct auto_pin_cfg {
- int line_outs;
- /* sorted in the order of Front/Surr/CLFE/Side */
- hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS];
- int speaker_outs;
- hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS];
- int hp_outs;
- int line_out_type; /* AUTO_PIN_XXX_OUT */
- hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
- int num_inputs;
- struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
- int dig_outs;
- hda_nid_t dig_out_pins[2];
- hda_nid_t dig_in_pin;
- hda_nid_t mono_out_pin;
- int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */
- int dig_in_type; /* HDA_PCM_TYPE_XXX */
+/* fixup action definitions */
+enum {
+ HDA_FIXUP_ACT_PRE_PROBE,
+ HDA_FIXUP_ACT_PROBE,
+ HDA_FIXUP_ACT_INIT,
+ HDA_FIXUP_ACT_BUILD,
+ HDA_FIXUP_ACT_FREE,
};
+int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list);
+void snd_hda_apply_verbs(struct hda_codec *codec);
+void snd_hda_apply_pincfgs(struct hda_codec *codec,
+ const struct hda_pintbl *cfg);
+void snd_hda_apply_fixup(struct hda_codec *codec, int action);
+void __snd_hda_apply_fixup(struct hda_codec *codec, int id, int action, int depth);
+void snd_hda_pick_fixup(struct hda_codec *codec,
+ const struct hda_model_fixup *models,
+ const struct hda_quirk *quirk,
+ const struct hda_fixup *fixlist);
+void snd_hda_pick_pin_fixup(struct hda_codec *codec,
+ const struct snd_hda_pin_quirk *pin_quirk,
+ const struct hda_fixup *fixlist,
+ bool match_all_pins);
+
+/* helper macros to retrieve pin default-config values */
#define get_defcfg_connect(cfg) \
((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT)
#define get_defcfg_association(cfg) \
@@ -440,10 +397,8 @@ struct auto_pin_cfg {
(cfg & AC_DEFCFG_SEQUENCE)
#define get_defcfg_device(cfg) \
((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT)
-
-int snd_hda_parse_pin_def_config(struct hda_codec *codec,
- struct auto_pin_cfg *cfg,
- hda_nid_t *ignore_nids);
+#define get_defcfg_misc(cfg) \
+ ((cfg & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT)
/* amp values */
#define AMP_IN_MUTE(idx) (0x7080 | ((idx)<<8))
@@ -462,19 +417,82 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
#define PIN_HP (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN)
#define PIN_HP_AMP (AC_PINCTL_HP_EN)
+unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin);
+unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
+ hda_nid_t pin, unsigned int val);
+int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int val, bool cached);
+
+/**
+ * _snd_hda_set_pin_ctl - Set a pin-control value safely
+ * @codec: the codec instance
+ * @pin: the pin NID to set the control
+ * @val: the pin-control value (AC_PINCTL_* bits)
+ *
+ * This function sets the pin-control value to the given pin, but
+ * filters out the invalid pin-control bits when the pin has no such
+ * capabilities. For example, when PIN_HP is passed but the pin has no
+ * HP-drive capability, the HP bit is omitted.
+ *
+ * The function doesn't check the input VREF capability bits, though.
+ * Use snd_hda_get_default_vref() to guess the right value.
+ * Also, this function is only for analog pins, not for HDMI pins.
+ */
+static inline int
+snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val)
+{
+ return _snd_hda_set_pin_ctl(codec, pin, val, false);
+}
+
+/**
+ * snd_hda_set_pin_ctl_cache - Set a pin-control value safely
+ * @codec: the codec instance
+ * @pin: the pin NID to set the control
+ * @val: the pin-control value (AC_PINCTL_* bits)
+ *
+ * Just like snd_hda_set_pin_ctl() but write to cache as well.
+ */
+static inline int
+snd_hda_set_pin_ctl_cache(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int val)
+{
+ return _snd_hda_set_pin_ctl(codec, pin, val, true);
+}
+
+int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid);
+int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int val);
+
+#define for_each_hda_codec_node(nid, codec) \
+ for ((nid) = (codec)->core.start_nid; (nid) < (codec)->core.end_nid; (nid)++)
+
+/* Set the codec power_state flag to indicate to allow unsol event handling;
+ * see hda_codec_unsol_event() in hda_bind.c. Calling this might confuse the
+ * state tracking, so use with care.
+ */
+static inline void snd_hda_codec_allow_unsol_events(struct hda_codec *codec)
+{
+ codec->core.dev.power.power_state = PMSG_ON;
+}
+
/*
* get widget capabilities
*/
static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid)
{
- if (nid < codec->start_nid ||
- nid >= codec->start_nid + codec->num_nodes)
+ if (nid < codec->core.start_nid ||
+ nid >= codec->core.start_nid + codec->core.num_nodes)
return 0;
- return codec->wcaps[nid - codec->start_nid];
+ return codec->wcaps[nid - codec->core.start_nid];
}
/* get the widget type from widget capability bits */
-#define get_wcaps_type(wcaps) (((wcaps) & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT)
+static inline int get_wcaps_type(unsigned int wcaps)
+{
+ if (!wcaps)
+ return -1; /* invalid type */
+ return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+}
static inline unsigned int get_wcaps_channels(u32 wcaps)
{
@@ -486,12 +504,60 @@ static inline unsigned int get_wcaps_channels(u32 wcaps)
return chans;
}
+static inline void snd_hda_override_wcaps(struct hda_codec *codec,
+ hda_nid_t nid, u32 val)
+{
+ if (nid >= codec->core.start_nid &&
+ nid < codec->core.start_nid + codec->core.num_nodes)
+ codec->wcaps[nid - codec->core.start_nid] = val;
+}
+
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps);
-u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid);
-u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid);
-int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
+/**
+ * snd_hda_query_pin_caps - Query PIN capabilities
+ * @codec: the HD-auio codec
+ * @nid: the NID to query
+ *
+ * Query PIN capabilities for the given widget.
+ * Returns the obtained capability bits.
+ *
+ * When cap bits have been already read, this doesn't read again but
+ * returns the cached value.
+ */
+static inline u32
+snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
+{
+ return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+
+}
+
+/**
+ * snd_hda_override_pin_caps - Override the pin capabilities
+ * @codec: the CODEC
+ * @nid: the NID to override
+ * @caps: the capability bits to set
+ *
+ * Override the cached PIN capabilitiy bits value by the given one.
+ *
+ * Returns zero if successful or a negative error code.
+ */
+static inline int
+snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int caps)
+{
+ return snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP, caps);
+}
+
+bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
+ int dir, unsigned int bits);
+
+#define nid_has_mute(codec, nid, dir) \
+ snd_hda_check_amp_caps(codec, nid, dir, (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE))
+#define nid_has_volume(codec, nid, dir) \
+ snd_hda_check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS)
+
/* flags for hda_nid_item */
#define HDA_NID_ITEM_AMP (1<<0)
@@ -505,8 +571,6 @@ struct hda_nid_item {
int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
struct snd_kcontrol *kctl);
-int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl,
- unsigned int index, hda_nid_t nid);
void snd_hda_ctls_clear(struct hda_codec *codec);
/*
@@ -518,27 +582,15 @@ int snd_hda_create_hwdep(struct hda_codec *codec);
static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; }
#endif
-#if defined(CONFIG_SND_HDA_POWER_SAVE) && defined(CONFIG_SND_HDA_HWDEP)
-int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec);
-#else
-static inline int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec)
-{
- return 0;
-}
-#endif
+void snd_hda_sysfs_init(struct hda_codec *codec);
+void snd_hda_sysfs_clear(struct hda_codec *codec);
-#ifdef CONFIG_SND_HDA_RECONFIG
-int snd_hda_hwdep_add_sysfs(struct hda_codec *codec);
-#else
-static inline int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
-{
- return 0;
-}
-#endif
+extern const struct attribute_group *snd_hda_dev_attr_groups[];
#ifdef CONFIG_SND_HDA_RECONFIG
const char *snd_hda_get_hint(struct hda_codec *codec, const char *key);
int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key);
+int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp);
#else
static inline
const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
@@ -551,13 +603,18 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
{
return -ENOENT;
}
+
+static inline
+int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
+{
+ return -ENOENT;
+}
#endif
/*
* power-management
*/
-#ifdef CONFIG_SND_HDA_POWER_SAVE
void snd_hda_schedule_power_save(struct hda_codec *codec);
struct hda_amp_list {
@@ -567,14 +624,42 @@ struct hda_amp_list {
};
struct hda_loopback_check {
- struct hda_amp_list *amplist;
+ const struct hda_amp_list *amplist;
int power_on;
};
int snd_hda_check_amp_list_power(struct hda_codec *codec,
struct hda_loopback_check *check,
hda_nid_t nid);
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
+
+/* check whether the actual power state matches with the target state */
+static inline bool
+snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int target_state)
+{
+ return snd_hdac_check_power_state(&codec->core, nid, target_state);
+}
+
+static inline unsigned int snd_hda_sync_power_state(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int target_state)
+{
+ return snd_hdac_sync_power_state(&codec->core, nid, target_state);
+}
+unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state);
+
+void snd_hda_codec_shutdown(struct hda_codec *codec);
+
+static inline int snd_hda_codec_init(struct hda_codec *codec)
+{
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
+
+ if (driver->ops->init)
+ return driver->ops->init(codec);
+ return 0;
+}
/*
* AMP control callbacks
@@ -583,77 +668,58 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
#define get_amp_nid_(pv) ((pv) & 0xffff)
#define get_amp_nid(kc) get_amp_nid_((kc)->private_value)
#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3)
-#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
-#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
+#define get_amp_direction_(pv) (((pv) >> 18) & 0x1)
+#define get_amp_direction(kc) get_amp_direction_((kc)->private_value)
+#define get_amp_index_(pv) (((pv) >> 19) & 0xf)
+#define get_amp_index(kc) get_amp_index_((kc)->private_value)
#define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f)
#define get_amp_min_mute(kc) (((kc)->private_value >> 29) & 0x1)
/*
- * CEA Short Audio Descriptor data
+ * enum control helper
*/
-struct cea_sad {
- int channels;
- int format; /* (format == 0) indicates invalid SAD */
- int rates;
- int sample_bits; /* for LPCM */
- int max_bitrate; /* for AC3...ATRAC */
- int profile; /* for WMAPRO */
-};
+int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo,
+ int num_items, const char * const *texts);
+#define snd_hda_enum_bool_helper_info(kcontrol, uinfo) \
+ snd_hda_enum_helper_info(kcontrol, uinfo, 0, NULL)
-#define ELD_FIXED_BYTES 20
-#define ELD_MAX_MNL 16
-#define ELD_MAX_SAD 16
-
-/*
- * ELD: EDID Like Data
- */
struct hdmi_eld {
bool monitor_present;
bool eld_valid;
int eld_size;
- int baseline_len;
- int eld_ver;
- int cea_edid_ver;
- char monitor_name[ELD_MAX_MNL + 1];
- int manufacture_id;
- int product_id;
- u64 port_id;
- int support_hdcp;
- int support_ai;
- int conn_type;
- int aud_synch_delay;
- int spk_alloc;
- int sad_count;
- struct cea_sad sad[ELD_MAX_SAD];
-#ifdef CONFIG_PROC_FS
- struct snd_info_entry *proc_entry;
-#endif
+ char eld_buffer[ELD_MAX_SIZE];
+ struct snd_parsed_hdmi_eld info;
};
int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid);
-int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t);
-void snd_hdmi_show_eld(struct hdmi_eld *eld);
-void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm,
- struct hda_pcm_stream *codec_pars);
-
-#ifdef CONFIG_PROC_FS
-int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
- int index);
-void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld);
-#else
-static inline int snd_hda_eld_proc_new(struct hda_codec *codec,
- struct hdmi_eld *eld,
- int index)
-{
- return 0;
-}
-static inline void snd_hda_eld_proc_free(struct hda_codec *codec,
- struct hdmi_eld *eld)
-{
-}
+int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char *buf, int *eld_size);
+void snd_hdmi_eld_update_pcm_info(struct snd_parsed_hdmi_eld *e,
+ struct hda_pcm_stream *hinfo);
+
+#ifdef CONFIG_SND_PROC_FS
+void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
+ struct snd_info_buffer *buffer,
+ hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid);
+void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
+ struct snd_info_buffer *buffer);
#endif
#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen);
+void snd_hda_codec_display_power(struct hda_codec *codec, bool enable);
+
+/*
+ */
+#define codec_err(codec, fmt, args...) \
+ dev_err(hda_codec_dev(codec), fmt, ##args)
+#define codec_warn(codec, fmt, args...) \
+ dev_warn(hda_codec_dev(codec), fmt, ##args)
+#define codec_info(codec, fmt, args...) \
+ dev_info(hda_codec_dev(codec), fmt, ##args)
+#define codec_dbg(codec, fmt, args...) \
+ dev_dbg(hda_codec_dev(codec), fmt, ##args)
+
#endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/hda/common/hwdep.c b/sound/hda/common/hwdep.c
new file mode 100644
index 000000000000..9325e5c3cbe6
--- /dev/null
+++ b/sound/hda/common/hwdep.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HWDEP Interface for HD-audio codec
+ *
+ * Copyright (c) 2007 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/nospec.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include <sound/hda_hwdep.h>
+#include <sound/minors.h>
+
+/*
+ * write/read an out-of-bound verb
+ */
+static int verb_write_ioctl(struct hda_codec *codec,
+ struct hda_verb_ioctl __user *arg)
+{
+ u32 verb, res;
+
+ if (get_user(verb, &arg->verb))
+ return -EFAULT;
+ res = snd_hda_codec_read(codec, verb >> 24, 0,
+ (verb >> 8) & 0xffff, verb & 0xff);
+ if (put_user(res, &arg->res))
+ return -EFAULT;
+ return 0;
+}
+
+static int get_wcap_ioctl(struct hda_codec *codec,
+ struct hda_verb_ioctl __user *arg)
+{
+ u32 verb, res;
+
+ if (get_user(verb, &arg->verb))
+ return -EFAULT;
+ /* open-code get_wcaps(verb>>24) with nospec */
+ verb >>= 24;
+ if (verb < codec->core.start_nid ||
+ verb >= codec->core.start_nid + codec->core.num_nodes) {
+ res = 0;
+ } else {
+ verb -= codec->core.start_nid;
+ verb = array_index_nospec(verb, codec->core.num_nodes);
+ res = codec->wcaps[verb];
+ }
+ if (put_user(res, &arg->res))
+ return -EFAULT;
+ return 0;
+}
+
+
+/*
+ */
+static int hda_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct hda_codec *codec = hw->private_data;
+ void __user *argp = (void __user *)arg;
+
+ switch (cmd) {
+ case HDA_IOCTL_PVERSION:
+ return put_user(HDA_HWDEP_VERSION, (int __user *)argp);
+ case HDA_IOCTL_VERB_WRITE:
+ return verb_write_ioctl(codec, argp);
+ case HDA_IOCTL_GET_WCAP:
+ return get_wcap_ioctl(codec, argp);
+ }
+ return -ENOIOCTLCMD;
+}
+
+#ifdef CONFIG_COMPAT
+static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hda_hwdep_ioctl(hw, file, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
+{
+ if (!capable(CAP_SYS_RAWIO))
+ return -EACCES;
+ return 0;
+}
+
+int snd_hda_create_hwdep(struct hda_codec *codec)
+{
+ char hwname[16];
+ struct snd_hwdep *hwdep;
+ int err;
+
+ sprintf(hwname, "HDA Codec %d", codec->addr);
+ err = snd_hwdep_new(codec->card, hwname, codec->addr, &hwdep);
+ if (err < 0)
+ return err;
+ codec->hwdep = hwdep;
+ sprintf(hwdep->name, "HDA Codec %d", codec->addr);
+ hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
+ hwdep->private_data = codec;
+ hwdep->exclusive = 1;
+
+ hwdep->ops.open = hda_hwdep_open;
+ hwdep->ops.ioctl = hda_hwdep_ioctl;
+#ifdef CONFIG_COMPAT
+ hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
+#endif
+
+ /* for sysfs */
+ hwdep->dev->groups = snd_hda_dev_attr_groups;
+ dev_set_drvdata(hwdep->dev, codec);
+
+ return 0;
+}
diff --git a/sound/hda/common/jack.c b/sound/hda/common/jack.c
new file mode 100644
index 000000000000..7d7786df60ea
--- /dev/null
+++ b/sound/hda/common/jack.c
@@ -0,0 +1,770 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Jack-detection handling for HD-audio
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/jack.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+#include "hda_jack.h"
+
+/**
+ * is_jack_detectable - Check whether the given pin is jack-detectable
+ * @codec: the HDA codec
+ * @nid: pin NID
+ *
+ * Check whether the given pin is capable to report the jack detection.
+ * The jack detection might not work by various reasons, e.g. the jack
+ * detection is prohibited in the codec level, the pin config has
+ * AC_DEFCFG_MISC_NO_PRESENCE bit, no unsol support, etc.
+ */
+bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid)
+{
+ if (codec->no_jack_detect)
+ return false;
+ if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT))
+ return false;
+ if (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
+ AC_DEFCFG_MISC_NO_PRESENCE)
+ return false;
+ if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) &&
+ !codec->jackpoll_interval)
+ return false;
+ return true;
+}
+EXPORT_SYMBOL_GPL(is_jack_detectable);
+
+/* execute pin sense measurement */
+static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id)
+{
+ u32 pincap;
+ u32 val;
+
+ if (!codec->no_trigger_sense) {
+ pincap = snd_hda_query_pin_caps(codec, nid);
+ if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
+ snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_SET_PIN_SENSE, 0);
+ }
+ val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_SENSE, dev_id);
+ if (codec->inv_jack_detect)
+ val ^= AC_PINSENSE_PRESENCE;
+ return val;
+}
+
+/**
+ * snd_hda_jack_tbl_get_mst - query the jack-table entry for the given NID
+ * @codec: the HDA codec
+ * @nid: pin NID to refer to
+ * @dev_id: pin device entry id
+ */
+struct hda_jack_tbl *
+snd_hda_jack_tbl_get_mst(struct hda_codec *codec, hda_nid_t nid, int dev_id)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i;
+
+ if (!nid || !jack)
+ return NULL;
+ for (i = 0; i < codec->jacktbl.used; i++, jack++)
+ if (jack->nid == nid && jack->dev_id == dev_id)
+ return jack;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_mst);
+
+/**
+ * snd_hda_jack_tbl_get_from_tag - query the jack-table entry for the given tag
+ * @codec: the HDA codec
+ * @tag: tag value to refer to
+ * @dev_id: pin device entry id
+ */
+struct hda_jack_tbl *
+snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec,
+ unsigned char tag, int dev_id)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i;
+
+ if (!tag || !jack)
+ return NULL;
+ for (i = 0; i < codec->jacktbl.used; i++, jack++)
+ if (jack->tag == tag && jack->dev_id == dev_id)
+ return jack;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_from_tag);
+
+static struct hda_jack_tbl *
+any_jack_tbl_get_from_nid(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i;
+
+ if (!nid || !jack)
+ return NULL;
+ for (i = 0; i < codec->jacktbl.used; i++, jack++)
+ if (jack->nid == nid)
+ return jack;
+ return NULL;
+}
+
+/**
+ * snd_hda_jack_tbl_new - create a jack-table entry for the given NID
+ * @codec: the HDA codec
+ * @nid: pin NID to assign
+ * @dev_id: pin device entry id
+ */
+static struct hda_jack_tbl *
+snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid, int dev_id)
+{
+ struct hda_jack_tbl *jack =
+ snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
+ struct hda_jack_tbl *existing_nid_jack =
+ any_jack_tbl_get_from_nid(codec, nid);
+
+ WARN_ON(dev_id != 0 && !codec->dp_mst);
+
+ if (jack)
+ return jack;
+ jack = snd_array_new(&codec->jacktbl);
+ if (!jack)
+ return NULL;
+ jack->nid = nid;
+ jack->dev_id = dev_id;
+ jack->jack_dirty = 1;
+ if (existing_nid_jack) {
+ jack->tag = existing_nid_jack->tag;
+
+ /*
+ * Copy jack_detect from existing_nid_jack to avoid
+ * snd_hda_jack_detect_enable_callback_mst() making multiple
+ * SET_UNSOLICITED_ENABLE calls on the same pin.
+ */
+ jack->jack_detect = existing_nid_jack->jack_detect;
+ } else {
+ jack->tag = codec->jacktbl.used;
+ }
+
+ return jack;
+}
+
+void snd_hda_jack_tbl_disconnect(struct hda_codec *codec)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i;
+
+ for (i = 0; i < codec->jacktbl.used; i++, jack++) {
+ if (!codec->bus->shutdown && jack->jack)
+ snd_device_disconnect(codec->card, jack->jack);
+ }
+}
+
+void snd_hda_jack_tbl_clear(struct hda_codec *codec)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i;
+
+ for (i = 0; i < codec->jacktbl.used; i++, jack++) {
+ struct hda_jack_callback *cb, *next;
+
+ /* free jack instances manually when clearing/reconfiguring */
+ if (!codec->bus->shutdown && jack->jack)
+ snd_device_free(codec->card, jack->jack);
+
+ for (cb = jack->callback; cb; cb = next) {
+ next = cb->next;
+ kfree(cb);
+ }
+ }
+ snd_array_free(&codec->jacktbl);
+}
+
+#define get_jack_plug_state(sense) !!(sense & AC_PINSENSE_PRESENCE)
+
+/* update the cached value and notification flag if needed */
+static void jack_detect_update(struct hda_codec *codec,
+ struct hda_jack_tbl *jack)
+{
+ if (!jack->jack_dirty)
+ return;
+
+ if (jack->phantom_jack)
+ jack->pin_sense = AC_PINSENSE_PRESENCE;
+ else
+ jack->pin_sense = read_pin_sense(codec, jack->nid,
+ jack->dev_id);
+
+ /* A gating jack indicates the jack is invalid if gating is unplugged */
+ if (jack->gating_jack &&
+ !snd_hda_jack_detect_mst(codec, jack->gating_jack, jack->dev_id))
+ jack->pin_sense &= ~AC_PINSENSE_PRESENCE;
+
+ jack->jack_dirty = 0;
+
+ /* If a jack is gated by this one update it. */
+ if (jack->gated_jack) {
+ struct hda_jack_tbl *gated =
+ snd_hda_jack_tbl_get_mst(codec, jack->gated_jack,
+ jack->dev_id);
+ if (gated) {
+ gated->jack_dirty = 1;
+ jack_detect_update(codec, gated);
+ }
+ }
+}
+
+/**
+ * snd_hda_jack_set_dirty_all - Mark all the cached as dirty
+ * @codec: the HDA codec
+ *
+ * This function sets the dirty flag to all entries of jack table.
+ * It's called from the resume path in hda_codec.c.
+ */
+void snd_hda_jack_set_dirty_all(struct hda_codec *codec)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i;
+
+ for (i = 0; i < codec->jacktbl.used; i++, jack++)
+ if (jack->nid)
+ jack->jack_dirty = 1;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_set_dirty_all);
+
+/**
+ * snd_hda_jack_pin_sense - execute pin sense measurement
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ * @dev_id: pin device entry id
+ *
+ * Execute necessary pin sense measurement and return its Presence Detect,
+ * Impedance, ELD Valid etc. status bits.
+ */
+u32 snd_hda_jack_pin_sense(struct hda_codec *codec, hda_nid_t nid, int dev_id)
+{
+ struct hda_jack_tbl *jack =
+ snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
+ if (jack) {
+ jack_detect_update(codec, jack);
+ return jack->pin_sense;
+ }
+ return read_pin_sense(codec, nid, dev_id);
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_pin_sense);
+
+/**
+ * snd_hda_jack_detect_state_mst - query pin Presence Detect status
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ * @dev_id: pin device entry id
+ *
+ * Query and return the pin's Presence Detect status, as either
+ * HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM.
+ */
+int snd_hda_jack_detect_state_mst(struct hda_codec *codec,
+ hda_nid_t nid, int dev_id)
+{
+ struct hda_jack_tbl *jack =
+ snd_hda_jack_tbl_get_mst(codec, nid, dev_id);
+ if (jack && jack->phantom_jack)
+ return HDA_JACK_PHANTOM;
+ else if (snd_hda_jack_pin_sense(codec, nid, dev_id) &
+ AC_PINSENSE_PRESENCE)
+ return HDA_JACK_PRESENT;
+ else
+ return HDA_JACK_NOT_PRESENT;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_detect_state_mst);
+
+static struct hda_jack_callback *
+find_callback_from_list(struct hda_jack_tbl *jack,
+ hda_jack_callback_fn func)
+{
+ struct hda_jack_callback *cb;
+
+ if (!func)
+ return NULL;
+
+ for (cb = jack->callback; cb; cb = cb->next) {
+ if (cb->func == func)
+ return cb;
+ }
+
+ return NULL;
+}
+
+/**
+ * snd_hda_jack_detect_enable_callback_mst - enable the jack-detection
+ * @codec: the HDA codec
+ * @nid: pin NID to enable
+ * @func: callback function to register
+ * @dev_id: pin device entry id
+ *
+ * In the case of error, the return value will be a pointer embedded with
+ * errno. Check and handle the return value appropriately with standard
+ * macros such as @IS_ERR() and @PTR_ERR().
+ */
+struct hda_jack_callback *
+snd_hda_jack_detect_enable_callback_mst(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, hda_jack_callback_fn func)
+{
+ struct hda_jack_tbl *jack;
+ struct hda_jack_callback *callback = NULL;
+ int err;
+
+ jack = snd_hda_jack_tbl_new(codec, nid, dev_id);
+ if (!jack)
+ return ERR_PTR(-ENOMEM);
+
+ callback = find_callback_from_list(jack, func);
+
+ if (func && !callback) {
+ callback = kzalloc(sizeof(*callback), GFP_KERNEL);
+ if (!callback)
+ return ERR_PTR(-ENOMEM);
+ callback->func = func;
+ callback->nid = jack->nid;
+ callback->dev_id = jack->dev_id;
+ callback->next = jack->callback;
+ jack->callback = callback;
+ }
+
+ if (jack->jack_detect)
+ return callback; /* already registered */
+ jack->jack_detect = 1;
+ if (codec->jackpoll_interval > 0)
+ return callback; /* No unsol if we're polling instead */
+ err = snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | jack->tag);
+ if (err < 0)
+ return ERR_PTR(err);
+ return callback;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable_callback_mst);
+
+/**
+ * snd_hda_jack_detect_enable - Enable the jack detection on the given pin
+ * @codec: the HDA codec
+ * @nid: pin NID to enable jack detection
+ * @dev_id: pin device entry id
+ *
+ * Enable the jack detection with the default callback. Returns zero if
+ * successful or a negative error code.
+ */
+int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id)
+{
+ return PTR_ERR_OR_ZERO(snd_hda_jack_detect_enable_callback_mst(codec,
+ nid,
+ dev_id,
+ NULL));
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable);
+
+/**
+ * snd_hda_jack_set_gating_jack - Set gating jack.
+ * @codec: the HDA codec
+ * @gated_nid: gated pin NID
+ * @gating_nid: gating pin NID
+ *
+ * Indicates the gated jack is only valid when the gating jack is plugged.
+ */
+int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
+ hda_nid_t gating_nid)
+{
+ struct hda_jack_tbl *gated = snd_hda_jack_tbl_new(codec, gated_nid, 0);
+ struct hda_jack_tbl *gating =
+ snd_hda_jack_tbl_new(codec, gating_nid, 0);
+
+ WARN_ON(codec->dp_mst);
+
+ if (!gated || !gating)
+ return -EINVAL;
+
+ gated->gating_jack = gating_nid;
+ gating->gated_jack = gated_nid;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_set_gating_jack);
+
+/**
+ * snd_hda_jack_bind_keymap - bind keys generated from one NID to another jack.
+ * @codec: the HDA codec
+ * @key_nid: key event is generated by this pin NID
+ * @keymap: map of key type and key code
+ * @jack_nid: key reports to the jack of this pin NID
+ *
+ * This function is used in the case of key is generated from one NID while is
+ * reported to the jack of another NID.
+ */
+int snd_hda_jack_bind_keymap(struct hda_codec *codec, hda_nid_t key_nid,
+ const struct hda_jack_keymap *keymap,
+ hda_nid_t jack_nid)
+{
+ const struct hda_jack_keymap *map;
+ struct hda_jack_tbl *key_gen = snd_hda_jack_tbl_get(codec, key_nid);
+ struct hda_jack_tbl *report_to = snd_hda_jack_tbl_get(codec, jack_nid);
+
+ WARN_ON(codec->dp_mst);
+
+ if (!key_gen || !report_to || !report_to->jack)
+ return -EINVAL;
+
+ key_gen->key_report_jack = jack_nid;
+
+ if (keymap)
+ for (map = keymap; map->type; map++)
+ snd_jack_set_key(report_to->jack, map->type, map->key);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_bind_keymap);
+
+/**
+ * snd_hda_jack_set_button_state - report button event to the hda_jack_tbl button_state.
+ * @codec: the HDA codec
+ * @jack_nid: the button event reports to the jack_tbl of this NID
+ * @button_state: the button event captured by codec
+ *
+ * Codec driver calls this function to report the button event.
+ */
+void snd_hda_jack_set_button_state(struct hda_codec *codec, hda_nid_t jack_nid,
+ int button_state)
+{
+ struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, jack_nid);
+
+ if (!jack)
+ return;
+
+ if (jack->key_report_jack) {
+ struct hda_jack_tbl *report_to =
+ snd_hda_jack_tbl_get(codec, jack->key_report_jack);
+
+ if (report_to) {
+ report_to->button_state = button_state;
+ return;
+ }
+ }
+
+ jack->button_state = button_state;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_set_button_state);
+
+/**
+ * snd_hda_jack_report_sync - sync the states of all jacks and report if changed
+ * @codec: the HDA codec
+ */
+void snd_hda_jack_report_sync(struct hda_codec *codec)
+{
+ struct hda_jack_tbl *jack;
+ int i, state;
+
+ /* update all jacks at first */
+ jack = codec->jacktbl.list;
+ for (i = 0; i < codec->jacktbl.used; i++, jack++)
+ if (jack->nid)
+ jack_detect_update(codec, jack);
+
+ /* report the updated jacks; it's done after updating all jacks
+ * to make sure that all gating jacks properly have been set
+ */
+ jack = codec->jacktbl.list;
+ for (i = 0; i < codec->jacktbl.used; i++, jack++)
+ if (jack->nid) {
+ if (!jack->jack || jack->block_report)
+ continue;
+ state = jack->button_state;
+ if (get_jack_plug_state(jack->pin_sense))
+ state |= jack->type;
+ snd_jack_report(jack->jack, state);
+ if (jack->button_state) {
+ snd_jack_report(jack->jack,
+ state & ~jack->button_state);
+ jack->button_state = 0; /* button released */
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_report_sync);
+
+/* guess the jack type from the pin-config */
+static int get_input_jack_type(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ switch (get_defcfg_device(def_conf)) {
+ case AC_JACK_LINE_OUT:
+ case AC_JACK_SPEAKER:
+ return SND_JACK_LINEOUT;
+ case AC_JACK_HP_OUT:
+ return SND_JACK_HEADPHONE;
+ case AC_JACK_SPDIF_OUT:
+ case AC_JACK_DIG_OTHER_OUT:
+ return SND_JACK_AVOUT;
+ case AC_JACK_MIC_IN:
+ return SND_JACK_MICROPHONE;
+ default:
+ return SND_JACK_LINEIN;
+ }
+}
+
+static void hda_free_jack_priv(struct snd_jack *jack)
+{
+ struct hda_jack_tbl *jacks = jack->private_data;
+ jacks->nid = 0;
+ jacks->jack = NULL;
+}
+
+/**
+ * snd_hda_jack_add_kctl_mst - Add a kctl for the given pin
+ * @codec: the HDA codec
+ * @nid: pin NID to assign
+ * @dev_id : pin device entry id
+ * @name: string name for the jack
+ * @phantom_jack: flag to deal as a phantom jack
+ * @type: jack type bits to be reported, 0 for guessing from pincfg
+ * @keymap: optional jack / key mapping
+ *
+ * This assigns a jack-detection kctl to the given pin. The kcontrol
+ * will have the given name and index.
+ */
+int snd_hda_jack_add_kctl_mst(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, const char *name, bool phantom_jack,
+ int type, const struct hda_jack_keymap *keymap)
+{
+ struct hda_jack_tbl *jack;
+ const struct hda_jack_keymap *map;
+ int err, state, buttons;
+
+ jack = snd_hda_jack_tbl_new(codec, nid, dev_id);
+ if (!jack)
+ return 0;
+ if (jack->jack)
+ return 0; /* already created */
+
+ if (!type)
+ type = get_input_jack_type(codec, nid);
+
+ buttons = 0;
+ if (keymap) {
+ for (map = keymap; map->type; map++)
+ buttons |= map->type;
+ }
+
+ err = snd_jack_new(codec->card, name, type | buttons,
+ &jack->jack, true, phantom_jack);
+ if (err < 0)
+ return err;
+
+ jack->phantom_jack = !!phantom_jack;
+ jack->type = type;
+ jack->button_state = 0;
+ jack->jack->private_data = jack;
+ jack->jack->private_free = hda_free_jack_priv;
+ if (keymap) {
+ for (map = keymap; map->type; map++)
+ snd_jack_set_key(jack->jack, map->type, map->key);
+ }
+
+ state = snd_hda_jack_detect_mst(codec, nid, dev_id);
+ snd_jack_report(jack->jack, state ? jack->type : 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctl_mst);
+
+static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
+ const struct auto_pin_cfg *cfg,
+ const char *base_name)
+{
+ unsigned int def_conf, conn;
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+ int err;
+ bool phantom_jack;
+
+ WARN_ON(codec->dp_mst);
+
+ if (!nid)
+ return 0;
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ conn = get_defcfg_connect(def_conf);
+ if (conn == AC_JACK_PORT_NONE)
+ return 0;
+ phantom_jack = (conn != AC_JACK_PORT_COMPLEX) ||
+ !is_jack_detectable(codec, nid);
+
+ if (base_name)
+ strscpy(name, base_name, sizeof(name));
+ else
+ snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), NULL);
+ if (phantom_jack)
+ /* Example final name: "Internal Mic Phantom Jack" */
+ strncat(name, " Phantom", sizeof(name) - strlen(name) - 1);
+ err = snd_hda_jack_add_kctl(codec, nid, name, phantom_jack, 0, NULL);
+ if (err < 0)
+ return err;
+
+ if (!phantom_jack)
+ return snd_hda_jack_detect_enable(codec, nid, 0);
+ return 0;
+}
+
+/**
+ * snd_hda_jack_add_kctls - Add kctls for all pins included in the given pincfg
+ * @codec: the HDA codec
+ * @cfg: pin config table to parse
+ */
+int snd_hda_jack_add_kctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ const hda_nid_t *p;
+ int i, err;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ /* If we have headphone mics; make sure they get the right name
+ before grabbed by output pins */
+ if (cfg->inputs[i].is_headphone_mic) {
+ if (auto_cfg_hp_outs(cfg) == 1)
+ err = add_jack_kctl(codec, auto_cfg_hp_pins(cfg)[0],
+ cfg, "Headphone Mic");
+ else
+ err = add_jack_kctl(codec, cfg->inputs[i].pin,
+ cfg, "Headphone Mic");
+ } else
+ err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg,
+ NULL);
+ if (err < 0)
+ return err;
+ }
+
+ for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) {
+ err = add_jack_kctl(codec, *p, cfg, NULL);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) {
+ if (*p == *cfg->line_out_pins) /* might be duplicated */
+ break;
+ err = add_jack_kctl(codec, *p, cfg, NULL);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) {
+ if (*p == *cfg->line_out_pins) /* might be duplicated */
+ break;
+ err = add_jack_kctl(codec, *p, cfg, NULL);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) {
+ err = add_jack_kctl(codec, *p, cfg, NULL);
+ if (err < 0)
+ return err;
+ }
+ err = add_jack_kctl(codec, cfg->dig_in_pin, cfg, NULL);
+ if (err < 0)
+ return err;
+ err = add_jack_kctl(codec, cfg->mono_out_pin, cfg, NULL);
+ if (err < 0)
+ return err;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_add_kctls);
+
+static void call_jack_callback(struct hda_codec *codec, unsigned int res,
+ struct hda_jack_tbl *jack)
+{
+ struct hda_jack_callback *cb;
+
+ for (cb = jack->callback; cb; cb = cb->next) {
+ cb->jack = jack;
+ cb->unsol_res = res;
+ cb->func(codec, cb);
+ }
+ if (jack->gated_jack) {
+ struct hda_jack_tbl *gated =
+ snd_hda_jack_tbl_get_mst(codec, jack->gated_jack,
+ jack->dev_id);
+ if (gated) {
+ for (cb = gated->callback; cb; cb = cb->next) {
+ cb->jack = gated;
+ cb->unsol_res = res;
+ cb->func(codec, cb);
+ }
+ }
+ }
+}
+
+/**
+ * snd_hda_jack_unsol_event - Handle an unsolicited event
+ * @codec: the HDA codec
+ * @res: the unsolicited event data
+ */
+void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ struct hda_jack_tbl *event;
+ int tag = (res & AC_UNSOL_RES_TAG) >> AC_UNSOL_RES_TAG_SHIFT;
+
+ if (codec->dp_mst) {
+ int dev_entry =
+ (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
+
+ event = snd_hda_jack_tbl_get_from_tag(codec, tag, dev_entry);
+ } else {
+ event = snd_hda_jack_tbl_get_from_tag(codec, tag, 0);
+ }
+ if (!event)
+ return;
+
+ if (event->key_report_jack) {
+ struct hda_jack_tbl *report_to =
+ snd_hda_jack_tbl_get_mst(codec, event->key_report_jack,
+ event->dev_id);
+ if (report_to)
+ report_to->jack_dirty = 1;
+ } else
+ event->jack_dirty = 1;
+
+ call_jack_callback(codec, res, event);
+ snd_hda_jack_report_sync(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_unsol_event);
+
+/**
+ * snd_hda_jack_poll_all - Poll all jacks
+ * @codec: the HDA codec
+ *
+ * Poll all detectable jacks with dirty flag, update the status, call
+ * callbacks and call snd_hda_jack_report_sync() if any changes are found.
+ */
+void snd_hda_jack_poll_all(struct hda_codec *codec)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i, changes = 0;
+
+ for (i = 0; i < codec->jacktbl.used; i++, jack++) {
+ unsigned int old_sense;
+ if (!jack->nid || !jack->jack_dirty || jack->phantom_jack)
+ continue;
+ old_sense = get_jack_plug_state(jack->pin_sense);
+ jack_detect_update(codec, jack);
+ if (old_sense == get_jack_plug_state(jack->pin_sense))
+ continue;
+ changes = 1;
+ call_jack_callback(codec, 0, jack);
+ }
+ if (changes)
+ snd_hda_jack_report_sync(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hda_jack_poll_all);
+
diff --git a/sound/pci/hda/hda_proc.c b/sound/hda/common/proc.c
index f025200f2a62..5f3f61519ba6 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/hda/common/proc.c
@@ -1,49 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Universal Interface for Intel High Definition Audio Codec
*
* Generic proc interface
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
+#include <linux/slab.h>
#include <sound/core.h>
-#include "hda_codec.h"
+#include <linux/module.h>
+#include <sound/hda_codec.h>
#include "hda_local.h"
-static char *bits_names(unsigned int bits, char *names[], int size)
-{
- int i, n;
- static char buf[128];
-
- for (i = 0, n = 0; i < size; i++) {
- if (bits & (1U<<i) && names[i])
- n += snprintf(buf + n, sizeof(buf) - n, " %s",
- names[i]);
- }
- buf[n] = '\0';
+static int dump_coef = -1;
+module_param(dump_coef, int, 0644);
+MODULE_PARM_DESC(dump_coef, "Dump processing coefficients in codec proc file (-1=auto, 0=disable, 1=enable)");
- return buf;
-}
+/* always use noncached version */
+#define param_read(codec, nid, parm) \
+ snd_hdac_read_parm_uncached(&(codec)->core, nid, parm)
static const char *get_wid_type_name(unsigned int wid_value)
{
- static char *names[16] = {
+ static const char * const names[16] = {
[AC_WID_AUD_OUT] = "Audio Output",
[AC_WID_AUD_IN] = "Audio Input",
[AC_WID_AUD_MIX] = "Audio Mixer",
@@ -54,6 +35,8 @@ static const char *get_wid_type_name(unsigned int wid_value)
[AC_WID_BEEP] = "Beep Generator Widget",
[AC_WID_VENDOR] = "Vendor Defined Widget",
};
+ if (wid_value == -1)
+ return "UNKNOWN Widget";
wid_value &= 0xf;
if (names[wid_value])
return names[wid_value];
@@ -91,10 +74,10 @@ static void print_nid_array(struct snd_info_buffer *buffer,
static void print_nid_pcms(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
- int pcm, type;
+ int type;
struct hda_pcm *cpcm;
- for (pcm = 0; pcm < codec->num_pcms; pcm++) {
- cpcm = &codec->pcm_info[pcm];
+
+ list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
for (type = 0; type < 2; type++) {
if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL)
continue;
@@ -111,9 +94,8 @@ static void print_amp_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid, int dir)
{
unsigned int caps;
- caps = snd_hda_param_read(codec, nid,
- dir == HDA_OUTPUT ?
- AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+ caps = param_read(codec, nid, dir == HDA_OUTPUT ?
+ AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
if (caps == -1 || caps == 0) {
snd_iprintf(buffer, "N/A\n");
return;
@@ -126,38 +108,70 @@ static void print_amp_caps(struct snd_info_buffer *buffer,
(caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT);
}
+/* is this a stereo widget or a stereo-to-mono mix? */
+static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid,
+ int dir, unsigned int wcaps, int indices)
+{
+ hda_nid_t conn;
+
+ if (wcaps & AC_WCAP_STEREO)
+ return true;
+ /* check for a stereo-to-mono mix; it must be:
+ * only a single connection, only for input, and only a mixer widget
+ */
+ if (indices != 1 || dir != HDA_INPUT ||
+ get_wcaps_type(wcaps) != AC_WID_AUD_MIX)
+ return false;
+
+ if (snd_hda_get_raw_connections(codec, nid, &conn, 1) < 0)
+ return false;
+ /* the connection source is a stereo? */
+ wcaps = snd_hda_param_read(codec, conn, AC_PAR_AUDIO_WIDGET_CAP);
+ return !!(wcaps & AC_WCAP_STEREO);
+}
+
static void print_amp_vals(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid,
- int dir, int stereo, int indices)
+ int dir, unsigned int wcaps, int indices)
{
unsigned int val;
+ bool stereo;
int i;
+ stereo = is_stereo_amps(codec, nid, dir, wcaps, indices);
+
dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
for (i = 0; i < indices; i++) {
snd_iprintf(buffer, " [");
+ val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_AMP_GAIN_MUTE,
+ AC_AMP_GET_LEFT | dir | i);
+ snd_iprintf(buffer, "0x%02x", val);
if (stereo) {
val = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_LEFT | dir | i);
- snd_iprintf(buffer, "0x%02x ", val);
+ AC_AMP_GET_RIGHT | dir | i);
+ snd_iprintf(buffer, " 0x%02x", val);
}
- val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_RIGHT | dir | i);
- snd_iprintf(buffer, "0x%02x]", val);
+ snd_iprintf(buffer, "]");
}
snd_iprintf(buffer, "\n");
}
static void print_pcm_rates(struct snd_info_buffer *buffer, unsigned int pcm)
{
- char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
+ static const unsigned int rates[] = {
+ 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
+ 96000, 176400, 192000, 384000
+ };
+ int i;
pcm &= AC_SUPPCM_RATES;
snd_iprintf(buffer, " rates [0x%x]:", pcm);
- snd_print_pcm_rates(pcm, buf, sizeof(buf));
- snd_iprintf(buffer, "%s\n", buf);
+ for (i = 0; i < ARRAY_SIZE(rates); i++)
+ if (pcm & (1 << i))
+ snd_iprintf(buffer, " %d", rates[i]);
+ snd_iprintf(buffer, "\n");
}
static void print_pcm_bits(struct snd_info_buffer *buffer, unsigned int pcm)
@@ -185,8 +199,8 @@ static void print_pcm_formats(struct snd_info_buffer *buffer,
static void print_pcm_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
- unsigned int pcm = snd_hda_param_read(codec, nid, AC_PAR_PCM);
- unsigned int stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
+ unsigned int pcm = param_read(codec, nid, AC_PAR_PCM);
+ unsigned int stream = param_read(codec, nid, AC_PAR_STREAM);
if (pcm == -1 || stream == -1) {
snd_iprintf(buffer, "N/A\n");
return;
@@ -198,7 +212,7 @@ static void print_pcm_caps(struct snd_info_buffer *buffer,
static const char *get_jack_connection(u32 cfg)
{
- static char *names[16] = {
+ static const char * const names[16] = {
"Unknown", "1/8", "1/4", "ATAPI",
"RCA", "Optical","Digital", "Analog",
"DIN", "XLR", "RJ11", "Comb",
@@ -213,7 +227,7 @@ static const char *get_jack_connection(u32 cfg)
static const char *get_jack_color(u32 cfg)
{
- static char *names[16] = {
+ static const char * const names[16] = {
"Unknown", "Black", "Grey", "Blue",
"Green", "Red", "Orange", "Yellow",
"Purple", "Pink", NULL, NULL,
@@ -226,14 +240,77 @@ static const char *get_jack_color(u32 cfg)
return "UNKNOWN";
}
+/*
+ * Parse the pin default config value and returns the string of the
+ * jack location, e.g. "Rear", "Front", etc.
+ */
+static const char *get_jack_location(u32 cfg)
+{
+ static const char * const bases[7] = {
+ "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
+ };
+ static const unsigned char specials_idx[] = {
+ 0x07, 0x08,
+ 0x17, 0x18, 0x19,
+ 0x37, 0x38
+ };
+ static const char * const specials[] = {
+ "Rear Panel", "Drive Bar",
+ "Riser", "HDMI", "ATAPI",
+ "Mobile-In", "Mobile-Out"
+ };
+ int i;
+
+ cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
+ if ((cfg & 0x0f) < 7)
+ return bases[cfg & 0x0f];
+ for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
+ if (cfg == specials_idx[i])
+ return specials[i];
+ }
+ return "UNKNOWN";
+}
+
+/*
+ * Parse the pin default config value and returns the string of the
+ * jack connectivity, i.e. external or internal connection.
+ */
+static const char *get_jack_connectivity(u32 cfg)
+{
+ static const char * const jack_locations[4] = {
+ "Ext", "Int", "Sep", "Oth"
+ };
+
+ return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3];
+}
+
+/*
+ * Parse the pin default config value and returns the string of the
+ * jack type, i.e. the purpose of the jack, such as Line-Out or CD.
+ */
+static const char *get_jack_type(u32 cfg)
+{
+ static const char * const jack_types[16] = {
+ "Line Out", "Speaker", "HP Out", "CD",
+ "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
+ "Line In", "Aux", "Mic", "Telephony",
+ "SPDIF In", "Digital In", "Reserved", "Other"
+ };
+
+ return jack_types[(cfg & AC_DEFCFG_DEVICE)
+ >> AC_DEFCFG_DEVICE_SHIFT];
+}
+
static void print_pin_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid,
int *supports_vref)
{
- static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
+ static const char * const jack_conns[4] = {
+ "Jack", "N/A", "Fixed", "Both"
+ };
unsigned int caps, val;
- caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+ caps = param_read(codec, nid, AC_PAR_PIN_CAP);
snd_iprintf(buffer, " Pincap 0x%08x:", caps);
if (caps & AC_PINCAP_IN)
snd_iprintf(buffer, " IN");
@@ -249,7 +326,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
snd_iprintf(buffer, " Balanced");
if (caps & AC_PINCAP_HDMI) {
/* Realtek uses this bit as a different meaning */
- if ((codec->vendor_id >> 16) == 0x10ec)
+ if ((codec->core.vendor_id >> 16) == 0x10ec)
snd_iprintf(buffer, " R/L");
else {
if (caps & AC_PINCAP_HBR)
@@ -297,9 +374,9 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
- snd_hda_get_jack_type(caps),
- snd_hda_get_jack_connectivity(caps),
- snd_hda_get_jack_location(caps));
+ get_jack_type(caps),
+ get_jack_connectivity(caps),
+ get_jack_location(caps));
snd_iprintf(buffer, " Conn = %s, Color = %s\n",
get_jack_connection(caps),
get_jack_color(caps));
@@ -361,8 +438,7 @@ static void print_pin_ctls(struct snd_info_buffer *buffer,
static void print_vol_knob(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
- unsigned int cap = snd_hda_param_read(codec, nid,
- AC_PAR_VOL_KNB_CAP);
+ unsigned int cap = param_read(codec, nid, AC_PAR_VOL_KNB_CAP);
snd_iprintf(buffer, " Volume-Knob: delta=%d, steps=%d, ",
(cap >> 7) & 1, cap & 0x7f);
cap = snd_hda_codec_read(codec, nid, 0,
@@ -394,6 +470,9 @@ static void print_digital_conv(struct snd_info_buffer *buffer,
{
unsigned int digi1 = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_DIGI_CONVERT_1, 0);
+ unsigned char digi2 = digi1 >> 8;
+ unsigned char digi3 = digi1 >> 16;
+
snd_iprintf(buffer, " Digital:");
if (digi1 & AC_DIG1_ENABLE)
snd_iprintf(buffer, " Enabled");
@@ -404,24 +483,28 @@ static void print_digital_conv(struct snd_info_buffer *buffer,
if (digi1 & AC_DIG1_EMPHASIS)
snd_iprintf(buffer, " Preemphasis");
if (digi1 & AC_DIG1_COPYRIGHT)
- snd_iprintf(buffer, " Copyright");
+ snd_iprintf(buffer, " Non-Copyright");
if (digi1 & AC_DIG1_NONAUDIO)
snd_iprintf(buffer, " Non-Audio");
if (digi1 & AC_DIG1_PROFESSIONAL)
snd_iprintf(buffer, " Pro");
if (digi1 & AC_DIG1_LEVEL)
snd_iprintf(buffer, " GenLevel");
+ if (digi3 & AC_DIG3_KAE)
+ snd_iprintf(buffer, " KAE");
snd_iprintf(buffer, "\n");
snd_iprintf(buffer, " Digital category: 0x%x\n",
- (digi1 >> 8) & AC_DIG2_CC);
+ digi2 & AC_DIG2_CC);
+ snd_iprintf(buffer, " IEC Coding Type: 0x%x\n",
+ digi3 & AC_DIG3_ICT);
}
static const char *get_pwr_state(u32 state)
{
- static const char *buf[4] = {
- "D0", "D1", "D2", "D3"
+ static const char * const buf[] = {
+ "D0", "D1", "D2", "D3", "D3cold"
};
- if (state < 4)
+ if (state < ARRAY_SIZE(buf))
return buf[state];
return "UNKNOWN";
}
@@ -429,7 +512,7 @@ static const char *get_pwr_state(u32 state)
static void print_power_state(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
- static char *names[] = {
+ static const char * const names[] = {
[ilog2(AC_PWRST_D0SUP)] = "D0",
[ilog2(AC_PWRST_D1SUP)] = "D1",
[ilog2(AC_PWRST_D2SUP)] = "D2",
@@ -440,17 +523,31 @@ static void print_power_state(struct snd_info_buffer *buffer,
[ilog2(AC_PWRST_EPSS)] = "EPSS",
};
- int sup = snd_hda_param_read(codec, nid, AC_PAR_POWER_STATE);
+ int sup = param_read(codec, nid, AC_PAR_POWER_STATE);
int pwr = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_POWER_STATE, 0);
- if (sup)
- snd_iprintf(buffer, " Power states: %s\n",
- bits_names(sup, names, ARRAY_SIZE(names)));
+ if (sup != -1) {
+ int i;
- snd_iprintf(buffer, " Power: setting=%s, actual=%s\n",
+ snd_iprintf(buffer, " Power states: ");
+ for (i = 0; i < ARRAY_SIZE(names); i++) {
+ if (sup & (1U << i))
+ snd_iprintf(buffer, " %s", names[i]);
+ }
+ snd_iprintf(buffer, "\n");
+ }
+
+ snd_iprintf(buffer, " Power: setting=%s, actual=%s",
get_pwr_state(pwr & AC_PWRST_SETTING),
get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
AC_PWRST_ACTUAL_SHIFT));
+ if (pwr & AC_PWRST_ERROR)
+ snd_iprintf(buffer, ", Error");
+ if (pwr & AC_PWRST_CLK_STOP_OK)
+ snd_iprintf(buffer, ", Clock-stop-OK");
+ if (pwr & AC_PWRST_SETTING_RESET)
+ snd_iprintf(buffer, ", Setting-reset");
+ snd_iprintf(buffer, "\n");
}
static void print_unsol_cap(struct snd_info_buffer *buffer,
@@ -464,14 +561,38 @@ static void print_unsol_cap(struct snd_info_buffer *buffer,
(unsol & AC_UNSOL_ENABLED) ? 1 : 0);
}
+static inline bool can_dump_coef(struct hda_codec *codec)
+{
+ switch (dump_coef) {
+ case 0: return false;
+ case 1: return true;
+ default: return codec->dump_coef;
+ }
+}
+
static void print_proc_caps(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
- unsigned int proc_caps = snd_hda_param_read(codec, nid,
- AC_PAR_PROC_CAP);
+ unsigned int i, ncoeff, oldindex;
+ unsigned int proc_caps = param_read(codec, nid, AC_PAR_PROC_CAP);
+ ncoeff = (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT;
snd_iprintf(buffer, " Processing caps: benign=%d, ncoeff=%d\n",
- proc_caps & AC_PCAP_BENIGN,
- (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT);
+ proc_caps & AC_PCAP_BENIGN, ncoeff);
+
+ if (!can_dump_coef(codec))
+ return;
+
+ /* Note: This is racy - another process could run in parallel and change
+ the coef index too. */
+ oldindex = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_COEF_INDEX, 0);
+ for (i = 0; i < ncoeff; i++) {
+ unsigned int val;
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, i);
+ val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF,
+ 0);
+ snd_iprintf(buffer, " Coeff 0x%02x: 0x%04x\n", i, val);
+ }
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_COEF_INDEX, oldindex);
}
static void print_conn_list(struct snd_info_buffer *buffer,
@@ -480,6 +601,8 @@ static void print_conn_list(struct snd_info_buffer *buffer,
int conn_len)
{
int c, curr = -1;
+ const hda_nid_t *list;
+ int cache_len;
if (conn_len > 1 &&
wid_type != AC_WID_AUD_MIX &&
@@ -497,13 +620,26 @@ static void print_conn_list(struct snd_info_buffer *buffer,
}
snd_iprintf(buffer, "\n");
}
+
+ /* Get Cache connections info */
+ cache_len = snd_hda_get_conn_list(codec, nid, &list);
+ if (cache_len >= 0 && (cache_len != conn_len ||
+ memcmp(list, conn, conn_len) != 0)) {
+ snd_iprintf(buffer, " In-driver Connection: %d\n", cache_len);
+ if (cache_len > 0) {
+ snd_iprintf(buffer, " ");
+ for (c = 0; c < cache_len; c++)
+ snd_iprintf(buffer, " 0x%02x", list[c]);
+ snd_iprintf(buffer, "\n");
+ }
+ }
}
static void print_gpio(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
unsigned int gpio =
- snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP);
+ param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP);
unsigned int enable, direction, wake, unsol, sticky, data;
int i, max;
snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, "
@@ -543,13 +679,74 @@ static void print_gpio(struct snd_info_buffer *buffer,
print_nid_array(buffer, codec, nid, &codec->nids);
}
-static void print_codec_info(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
+static void print_dpmst_connections(struct snd_info_buffer *buffer, struct hda_codec *codec,
+ hda_nid_t nid, int dev_num)
{
- struct hda_codec *codec = entry->private_data;
- hda_nid_t nid;
- int i, nodes;
+ int c, conn_len, curr, dev_id_saved;
+ hda_nid_t *conn;
+
+ conn_len = snd_hda_get_num_raw_conns(codec, nid);
+ if (conn_len <= 0)
+ return;
+
+ conn = kmalloc_array(conn_len, sizeof(hda_nid_t), GFP_KERNEL);
+ if (!conn)
+ return;
+
+ dev_id_saved = snd_hda_get_dev_select(codec, nid);
+
+ snd_hda_set_dev_select(codec, nid, dev_num);
+ curr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0);
+ if (snd_hda_get_raw_connections(codec, nid, conn, conn_len) < 0)
+ goto out;
+
+ for (c = 0; c < conn_len; c++) {
+ snd_iprintf(buffer, " 0x%02x", conn[c]);
+ if (c == curr)
+ snd_iprintf(buffer, "*");
+ }
+
+out:
+ kfree(conn);
+ snd_hda_set_dev_select(codec, nid, dev_id_saved);
+}
+static void print_device_list(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ int i, curr = -1;
+ u8 dev_list[AC_MAX_DEV_LIST_LEN];
+ unsigned int devlist_len;
+
+ devlist_len = snd_hda_get_devices(codec, nid, dev_list,
+ AC_MAX_DEV_LIST_LEN);
+ snd_iprintf(buffer, " Devices: %u\n", devlist_len);
+ if (devlist_len == 0)
+ return;
+
+ curr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DEVICE_SEL, 0);
+
+ for (i = 0; i < devlist_len; i++) {
+ if (i == curr)
+ snd_iprintf(buffer, " *");
+ else
+ snd_iprintf(buffer, " ");
+
+ snd_iprintf(buffer,
+ "Dev %02d: PD = %d, ELDV = %d, IA = %d, Connections [", i,
+ !!(dev_list[i] & AC_DE_PD),
+ !!(dev_list[i] & AC_DE_ELDV),
+ !!(dev_list[i] & AC_DE_IA));
+
+ print_dpmst_connections(buffer, codec, nid, i);
+
+ snd_iprintf(buffer, " ]\n");
+ }
+}
+
+static void print_codec_core_info(struct hdac_device *codec,
+ struct snd_info_buffer *buffer)
+{
snd_iprintf(buffer, "Codec: ");
if (codec->vendor_name && codec->chip_name)
snd_iprintf(buffer, "%s %s\n",
@@ -571,34 +768,44 @@ static void print_codec_info(struct snd_info_entry *entry,
snd_iprintf(buffer, "Modem Function Group: 0x%x\n", codec->mfg);
else
snd_iprintf(buffer, "No Modem Function Group found\n");
+}
- if (! codec->afg)
+static void print_codec_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hda_codec *codec = entry->private_data;
+ hda_nid_t nid, fg;
+ int i, nodes;
+
+ print_codec_core_info(&codec->core, buffer);
+ fg = codec->core.afg;
+ if (!fg)
return;
- snd_hda_power_up(codec);
+ CLASS(snd_hda_power, pm)(codec);
snd_iprintf(buffer, "Default PCM:\n");
- print_pcm_caps(buffer, codec, codec->afg);
+ print_pcm_caps(buffer, codec, fg);
snd_iprintf(buffer, "Default Amp-In caps: ");
- print_amp_caps(buffer, codec, codec->afg, HDA_INPUT);
+ print_amp_caps(buffer, codec, fg, HDA_INPUT);
snd_iprintf(buffer, "Default Amp-Out caps: ");
- print_amp_caps(buffer, codec, codec->afg, HDA_OUTPUT);
+ print_amp_caps(buffer, codec, fg, HDA_OUTPUT);
+ snd_iprintf(buffer, "State of AFG node 0x%02x:\n", fg);
+ print_power_state(buffer, codec, fg);
- nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
+ nodes = snd_hda_get_sub_nodes(codec, fg, &nid);
if (! nid || nodes < 0) {
snd_iprintf(buffer, "Invalid AFG subtree\n");
- snd_hda_power_down(codec);
return;
}
- print_gpio(buffer, codec, codec->afg);
+ print_gpio(buffer, codec, fg);
if (codec->proc_widget_hook)
- codec->proc_widget_hook(buffer, codec, codec->afg);
+ codec->proc_widget_hook(buffer, codec, fg);
for (i = 0; i < nodes; i++, nid++) {
unsigned int wid_caps =
- snd_hda_param_read(codec, nid,
- AC_PAR_AUDIO_WIDGET_CAP);
+ param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
unsigned int wid_type = get_wcaps_type(wid_caps);
- hda_nid_t conn[HDA_MAX_CONNECTIONS];
+ hda_nid_t *conn = NULL;
int conn_len = 0;
snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
@@ -635,17 +842,32 @@ static void print_codec_info(struct snd_info_entry *entry,
if (wid_type == AC_WID_VOL_KNB)
wid_caps |= AC_WCAP_CONN_LIST;
- if (wid_caps & AC_WCAP_CONN_LIST)
- conn_len = snd_hda_get_connections(codec, nid, conn,
- HDA_MAX_CONNECTIONS);
+ if (wid_caps & AC_WCAP_CONN_LIST) {
+ conn_len = snd_hda_get_num_raw_conns(codec, nid);
+ if (conn_len > 0) {
+ conn = kmalloc_array(conn_len,
+ sizeof(hda_nid_t),
+ GFP_KERNEL);
+ if (!conn)
+ return;
+ if (snd_hda_get_raw_connections(codec, nid, conn,
+ conn_len) < 0)
+ conn_len = 0;
+ }
+ }
if (wid_caps & AC_WCAP_IN_AMP) {
snd_iprintf(buffer, " Amp-In caps: ");
print_amp_caps(buffer, codec, nid, HDA_INPUT);
snd_iprintf(buffer, " Amp-In vals: ");
- print_amp_vals(buffer, codec, nid, HDA_INPUT,
- wid_caps & AC_WCAP_STEREO,
- wid_type == AC_WID_PIN ? 1 : conn_len);
+ if (wid_type == AC_WID_PIN ||
+ (codec->single_adc_amp &&
+ wid_type == AC_WID_AUD_IN))
+ print_amp_vals(buffer, codec, nid, HDA_INPUT,
+ wid_caps, 1);
+ else
+ print_amp_vals(buffer, codec, nid, HDA_INPUT,
+ wid_caps, conn_len);
}
if (wid_caps & AC_WCAP_OUT_AMP) {
snd_iprintf(buffer, " Amp-Out caps: ");
@@ -654,11 +876,10 @@ static void print_codec_info(struct snd_info_entry *entry,
if (wid_type == AC_WID_PIN &&
codec->pin_amp_workaround)
print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
- wid_caps & AC_WCAP_STEREO,
- conn_len);
+ wid_caps, conn_len);
else
print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
- wid_caps & AC_WCAP_STEREO, 1);
+ wid_caps, 1);
}
switch (wid_type) {
@@ -694,6 +915,9 @@ static void print_codec_info(struct snd_info_entry *entry,
(wid_caps & AC_WCAP_DELAY) >>
AC_WCAP_DELAY_SHIFT);
+ if (wid_type == AC_WID_PIN && codec->dp_mst)
+ print_device_list(buffer, codec, nid);
+
if (wid_caps & AC_WCAP_CONN_LIST)
print_conn_list(buffer, codec, nid, wid_type,
conn, conn_len);
@@ -703,8 +927,9 @@ static void print_codec_info(struct snd_info_entry *entry,
if (codec->proc_widget_hook)
codec->proc_widget_hook(buffer, codec, nid);
+
+ kfree(conn);
}
- snd_hda_power_down(codec);
}
/*
@@ -713,15 +938,8 @@ static void print_codec_info(struct snd_info_entry *entry,
int snd_hda_codec_proc_new(struct hda_codec *codec)
{
char name[32];
- struct snd_info_entry *entry;
- int err;
-
- snprintf(name, sizeof(name), "codec#%d", codec->addr);
- err = snd_card_proc_new(codec->bus->card, name, &entry);
- if (err < 0)
- return err;
- snd_info_set_text_ops(entry, codec, print_codec_info);
- return 0;
+ snprintf(name, sizeof(name), "codec#%d", codec->core.addr);
+ return snd_card_ro_proc_new(codec->card, name, codec, print_codec_info);
}
diff --git a/sound/hda/common/sysfs.c b/sound/hda/common/sysfs.c
new file mode 100644
index 000000000000..f8c8483fd5e5
--- /dev/null
+++ b/sound/hda/common/sysfs.c
@@ -0,0 +1,764 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * sysfs interface for HD-audio codec
+ *
+ * Copyright (c) 2014 Takashi Iwai <tiwai@suse.de>
+ *
+ * split from hda_hwdep.c
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/mutex.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include <sound/hda_codec.h>
+#include "hda_local.h"
+#include <sound/hda_hwdep.h>
+#include <sound/minors.h>
+
+/* hint string pair */
+struct hda_hint {
+ const char *key;
+ const char *val; /* contained in the same alloc as key */
+};
+
+static ssize_t power_on_acct_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ snd_hda_update_power_acct(codec);
+ return sysfs_emit(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct));
+}
+
+static ssize_t power_off_acct_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ snd_hda_update_power_acct(codec);
+ return sysfs_emit(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct));
+}
+
+static DEVICE_ATTR_RO(power_on_acct);
+static DEVICE_ATTR_RO(power_off_acct);
+
+#define CODEC_INFO_SHOW(type, field) \
+static ssize_t type##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct hda_codec *codec = dev_get_drvdata(dev); \
+ return sysfs_emit(buf, "0x%x\n", codec->field); \
+}
+
+#define CODEC_INFO_STR_SHOW(type, field) \
+static ssize_t type##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct hda_codec *codec = dev_get_drvdata(dev); \
+ return sysfs_emit(buf, "%s\n", \
+ codec->field ? codec->field : ""); \
+}
+
+CODEC_INFO_SHOW(vendor_id, core.vendor_id);
+CODEC_INFO_SHOW(subsystem_id, core.subsystem_id);
+CODEC_INFO_SHOW(revision_id, core.revision_id);
+CODEC_INFO_SHOW(afg, core.afg);
+CODEC_INFO_SHOW(mfg, core.mfg);
+CODEC_INFO_STR_SHOW(vendor_name, core.vendor_name);
+CODEC_INFO_STR_SHOW(chip_name, core.chip_name);
+CODEC_INFO_STR_SHOW(modelname, modelname);
+
+static ssize_t pin_configs_show(struct hda_codec *codec,
+ struct snd_array *list,
+ char *buf)
+{
+ const struct hda_pincfg *pin;
+ int i, len = 0;
+
+ guard(mutex)(&codec->user_mutex);
+ snd_array_for_each(list, i, pin) {
+ len += sysfs_emit_at(buf, len, "0x%02x 0x%08x\n",
+ pin->nid, pin->cfg);
+ }
+ return len;
+}
+
+static ssize_t init_pin_configs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ return pin_configs_show(codec, &codec->init_pins, buf);
+}
+
+static ssize_t driver_pin_configs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ return pin_configs_show(codec, &codec->driver_pins, buf);
+}
+
+#ifdef CONFIG_SND_HDA_RECONFIG
+
+/*
+ * sysfs interface
+ */
+
+static int clear_codec(struct hda_codec *codec)
+{
+ int err;
+
+ err = snd_hda_codec_reset(codec);
+ if (err < 0) {
+ codec_err(codec, "The codec is being used, can't free.\n");
+ return err;
+ }
+ snd_hda_sysfs_clear(codec);
+ return 0;
+}
+
+static int reconfig_codec(struct hda_codec *codec)
+{
+ int err;
+
+ CLASS(snd_hda_power, pm)(codec);
+ codec_info(codec, "hda-codec: reconfiguring\n");
+ err = snd_hda_codec_reset(codec);
+ if (err < 0) {
+ codec_err(codec,
+ "The codec is being used, can't reconfigure.\n");
+ return err;
+ }
+ err = device_reprobe(hda_codec_dev(codec));
+ if (err < 0)
+ return err;
+ return snd_card_register(codec->card);
+}
+
+/*
+ * allocate a string at most len chars, and remove the trailing EOL
+ */
+static char *kstrndup_noeol(const char *src, size_t len)
+{
+ char *s = kstrndup(src, len, GFP_KERNEL);
+ char *p;
+ if (!s)
+ return NULL;
+ p = strchr(s, '\n');
+ if (p)
+ *p = 0;
+ return s;
+}
+
+#define CODEC_INFO_STORE(type, field) \
+static ssize_t type##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct hda_codec *codec = dev_get_drvdata(dev); \
+ unsigned long val; \
+ int err = kstrtoul(buf, 0, &val); \
+ if (err < 0) \
+ return err; \
+ codec->field = val; \
+ return count; \
+}
+
+#define CODEC_INFO_STR_STORE(type, field) \
+static ssize_t type##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct hda_codec *codec = dev_get_drvdata(dev); \
+ char *s = kstrndup_noeol(buf, 64); \
+ if (!s) \
+ return -ENOMEM; \
+ kfree(codec->field); \
+ codec->field = s; \
+ return count; \
+}
+
+CODEC_INFO_STORE(vendor_id, core.vendor_id);
+CODEC_INFO_STORE(subsystem_id, core.subsystem_id);
+CODEC_INFO_STORE(revision_id, core.revision_id);
+CODEC_INFO_STR_STORE(vendor_name, core.vendor_name);
+CODEC_INFO_STR_STORE(chip_name, core.chip_name);
+CODEC_INFO_STR_STORE(modelname, modelname);
+
+#define CODEC_ACTION_STORE(type) \
+static ssize_t type##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct hda_codec *codec = dev_get_drvdata(dev); \
+ int err = 0; \
+ if (*buf) \
+ err = type##_codec(codec); \
+ return err < 0 ? err : count; \
+}
+
+CODEC_ACTION_STORE(reconfig);
+CODEC_ACTION_STORE(clear);
+
+static ssize_t init_verbs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ const struct hda_verb *v;
+ int i, len = 0;
+
+ guard(mutex)(&codec->user_mutex);
+ snd_array_for_each(&codec->init_verbs, i, v) {
+ len += sysfs_emit_at(buf, len, "0x%02x 0x%03x 0x%04x\n",
+ v->nid, v->verb, v->param);
+ }
+ return len;
+}
+
+static int parse_init_verbs(struct hda_codec *codec, const char *buf)
+{
+ struct hda_verb *v;
+ int nid, verb, param;
+
+ if (sscanf(buf, "%i %i %i", &nid, &verb, &param) != 3)
+ return -EINVAL;
+ if (!nid || !verb)
+ return -EINVAL;
+ guard(mutex)(&codec->user_mutex);
+ v = snd_array_new(&codec->init_verbs);
+ if (!v)
+ return -ENOMEM;
+ v->nid = nid;
+ v->verb = verb;
+ v->param = param;
+ return 0;
+}
+
+static ssize_t init_verbs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ int err = parse_init_verbs(codec, buf);
+ if (err < 0)
+ return err;
+ return count;
+}
+
+static ssize_t hints_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ const struct hda_hint *hint;
+ int i, len = 0;
+
+ guard(mutex)(&codec->user_mutex);
+ snd_array_for_each(&codec->hints, i, hint) {
+ len += sysfs_emit_at(buf, len, "%s = %s\n",
+ hint->key, hint->val);
+ }
+ return len;
+}
+
+static struct hda_hint *get_hint(struct hda_codec *codec, const char *key)
+{
+ struct hda_hint *hint;
+ int i;
+
+ snd_array_for_each(&codec->hints, i, hint) {
+ if (!strcmp(hint->key, key))
+ return hint;
+ }
+ return NULL;
+}
+
+static void remove_trail_spaces(char *str)
+{
+ char *p;
+ if (!*str)
+ return;
+ p = str + strlen(str) - 1;
+ for (; isspace(*p); p--) {
+ *p = 0;
+ if (p == str)
+ return;
+ }
+}
+
+#define MAX_HINTS 1024
+
+static int parse_hints(struct hda_codec *codec, const char *buf)
+{
+ char *key __free(kfree) = NULL;
+ char *val;
+ struct hda_hint *hint;
+
+ buf = skip_spaces(buf);
+ if (!*buf || *buf == '#' || *buf == '\n')
+ return 0;
+ if (*buf == '=')
+ return -EINVAL;
+ key = kstrndup_noeol(buf, 1024);
+ if (!key)
+ return -ENOMEM;
+ /* extract key and val */
+ val = strchr(key, '=');
+ if (!val)
+ return -EINVAL;
+ *val++ = 0;
+ val = skip_spaces(val);
+ remove_trail_spaces(key);
+ remove_trail_spaces(val);
+ guard(mutex)(&codec->user_mutex);
+ hint = get_hint(codec, key);
+ if (hint) {
+ /* replace */
+ kfree(hint->key);
+ goto replace;
+ }
+ /* allocate a new hint entry */
+ if (codec->hints.used >= MAX_HINTS)
+ return -ENOMEM;
+ hint = snd_array_new(&codec->hints);
+ if (!hint)
+ return -ENOMEM;
+ replace:
+ hint->key = no_free_ptr(key);
+ hint->val = val;
+ return 0;
+}
+
+static ssize_t hints_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ int err = parse_hints(codec, buf);
+ if (err < 0)
+ return err;
+ return count;
+}
+
+static ssize_t user_pin_configs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ return pin_configs_show(codec, &codec->user_pins, buf);
+}
+
+static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
+{
+ int nid, cfg;
+
+ if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
+ return -EINVAL;
+ if (!nid)
+ return -EINVAL;
+ guard(mutex)(&codec->user_mutex);
+ return snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
+}
+
+static ssize_t user_pin_configs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct hda_codec *codec = dev_get_drvdata(dev);
+ int err = parse_user_pin_configs(codec, buf);
+ if (err < 0)
+ return err;
+ return count;
+}
+
+/* sysfs attributes exposed only when CONFIG_SND_HDA_RECONFIG=y */
+static DEVICE_ATTR_RW(init_verbs);
+static DEVICE_ATTR_RW(hints);
+static DEVICE_ATTR_RW(user_pin_configs);
+static DEVICE_ATTR_WO(reconfig);
+static DEVICE_ATTR_WO(clear);
+
+/**
+ * snd_hda_get_hint - Look for hint string
+ * @codec: the HDA codec
+ * @key: the hint key string
+ *
+ * Look for a hint key/value pair matching with the given key string
+ * and returns the value string. If nothing found, returns NULL.
+ */
+const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
+{
+ struct hda_hint *hint = get_hint(codec, key);
+ return hint ? hint->val : NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_hint);
+
+/**
+ * snd_hda_get_bool_hint - Get a boolean hint value
+ * @codec: the HDA codec
+ * @key: the hint key string
+ *
+ * Look for a hint key/value pair matching with the given key string
+ * and returns a boolean value parsed from the value. If no matching
+ * key is found, return a negative value.
+ */
+int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
+{
+ const char *p;
+
+ guard(mutex)(&codec->user_mutex);
+ p = snd_hda_get_hint(codec, key);
+ if (!p || !*p)
+ return -ENOENT;
+ switch (toupper(*p)) {
+ case 'T': /* true */
+ case 'Y': /* yes */
+ case '1':
+ return 1;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_bool_hint);
+
+/**
+ * snd_hda_get_int_hint - Get an integer hint value
+ * @codec: the HDA codec
+ * @key: the hint key string
+ * @valp: pointer to store a value
+ *
+ * Look for a hint key/value pair matching with the given key string
+ * and stores the integer value to @valp. If no matching key is found,
+ * return a negative error code. Otherwise it returns zero.
+ */
+int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
+{
+ const char *p;
+ unsigned long val;
+
+ guard(mutex)(&codec->user_mutex);
+ p = snd_hda_get_hint(codec, key);
+ if (!p)
+ return -ENOENT;
+ else if (kstrtoul(p, 0, &val))
+ return -EINVAL;
+ else {
+ *valp = val;
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_int_hint);
+#endif /* CONFIG_SND_HDA_RECONFIG */
+
+/*
+ * common sysfs attributes
+ */
+#ifdef CONFIG_SND_HDA_RECONFIG
+#define RECONFIG_DEVICE_ATTR(name) DEVICE_ATTR_RW(name)
+#else
+#define RECONFIG_DEVICE_ATTR(name) DEVICE_ATTR_RO(name)
+#endif
+static RECONFIG_DEVICE_ATTR(vendor_id);
+static RECONFIG_DEVICE_ATTR(subsystem_id);
+static RECONFIG_DEVICE_ATTR(revision_id);
+static DEVICE_ATTR_RO(afg);
+static DEVICE_ATTR_RO(mfg);
+static RECONFIG_DEVICE_ATTR(vendor_name);
+static RECONFIG_DEVICE_ATTR(chip_name);
+static RECONFIG_DEVICE_ATTR(modelname);
+static DEVICE_ATTR_RO(init_pin_configs);
+static DEVICE_ATTR_RO(driver_pin_configs);
+
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+
+/* parser mode */
+enum {
+ LINE_MODE_NONE,
+ LINE_MODE_CODEC,
+ LINE_MODE_MODEL,
+ LINE_MODE_PINCFG,
+ LINE_MODE_VERB,
+ LINE_MODE_HINT,
+ LINE_MODE_VENDOR_ID,
+ LINE_MODE_SUBSYSTEM_ID,
+ LINE_MODE_REVISION_ID,
+ LINE_MODE_CHIP_NAME,
+ NUM_LINE_MODES,
+};
+
+static inline int strmatch(const char *a, const char *b)
+{
+ return strncasecmp(a, b, strlen(b)) == 0;
+}
+
+/* parse the contents after the line "[codec]"
+ * accept only the line with three numbers, and assign the current codec
+ */
+static void parse_codec_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ int vendorid, subid, caddr;
+ struct hda_codec *codec;
+
+ *codecp = NULL;
+ if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) {
+ list_for_each_codec(codec, bus) {
+ if ((vendorid <= 0 || codec->core.vendor_id == vendorid) &&
+ (subid <= 0 || codec->core.subsystem_id == subid) &&
+ codec->core.addr == caddr) {
+ *codecp = codec;
+ break;
+ }
+ }
+ }
+}
+
+/* parse the contents after the other command tags, [pincfg], [verb],
+ * [vendor_id], [subsystem_id], [revision_id], [chip_name], [hint] and [model]
+ * just pass to the sysfs helper (only when any codec was specified)
+ */
+static void parse_pincfg_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ parse_user_pin_configs(*codecp, buf);
+}
+
+static void parse_verb_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ parse_init_verbs(*codecp, buf);
+}
+
+static void parse_hint_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ parse_hints(*codecp, buf);
+}
+
+static void parse_model_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ kfree((*codecp)->modelname);
+ (*codecp)->modelname = kstrdup(buf, GFP_KERNEL);
+}
+
+static void parse_chip_name_mode(char *buf, struct hda_bus *bus,
+ struct hda_codec **codecp)
+{
+ snd_hda_codec_set_name(*codecp, buf);
+}
+
+#define DEFINE_PARSE_ID_MODE(name) \
+static void parse_##name##_mode(char *buf, struct hda_bus *bus, \
+ struct hda_codec **codecp) \
+{ \
+ unsigned long val; \
+ if (!kstrtoul(buf, 0, &val)) \
+ (*codecp)->core.name = val; \
+}
+
+DEFINE_PARSE_ID_MODE(vendor_id);
+DEFINE_PARSE_ID_MODE(subsystem_id);
+DEFINE_PARSE_ID_MODE(revision_id);
+
+
+struct hda_patch_item {
+ const char *tag;
+ const char *alias;
+ void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc);
+};
+
+static const struct hda_patch_item patch_items[NUM_LINE_MODES] = {
+ [LINE_MODE_CODEC] = {
+ .tag = "[codec]",
+ .parser = parse_codec_mode,
+ },
+ [LINE_MODE_MODEL] = {
+ .tag = "[model]",
+ .parser = parse_model_mode,
+ },
+ [LINE_MODE_VERB] = {
+ .tag = "[verb]",
+ .alias = "[init_verbs]",
+ .parser = parse_verb_mode,
+ },
+ [LINE_MODE_PINCFG] = {
+ .tag = "[pincfg]",
+ .alias = "[user_pin_configs]",
+ .parser = parse_pincfg_mode,
+ },
+ [LINE_MODE_HINT] = {
+ .tag = "[hint]",
+ .alias = "[hints]",
+ .parser = parse_hint_mode
+ },
+ [LINE_MODE_VENDOR_ID] = {
+ .tag = "[vendor_id]",
+ .parser = parse_vendor_id_mode,
+ },
+ [LINE_MODE_SUBSYSTEM_ID] = {
+ .tag = "[subsystem_id]",
+ .parser = parse_subsystem_id_mode,
+ },
+ [LINE_MODE_REVISION_ID] = {
+ .tag = "[revision_id]",
+ .parser = parse_revision_id_mode,
+ },
+ [LINE_MODE_CHIP_NAME] = {
+ .tag = "[chip_name]",
+ .parser = parse_chip_name_mode,
+ },
+};
+
+/* check the line starting with '[' -- change the parser mode accordingly */
+static int parse_line_mode(char *buf, struct hda_bus *bus)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(patch_items); i++) {
+ if (!patch_items[i].tag)
+ continue;
+ if (strmatch(buf, patch_items[i].tag))
+ return i;
+ if (patch_items[i].alias && strmatch(buf, patch_items[i].alias))
+ return i;
+ }
+ return LINE_MODE_NONE;
+}
+
+/* copy one line from the buffer in fw, and update the fields in fw
+ * return zero if it reaches to the end of the buffer, or non-zero
+ * if successfully copied a line
+ *
+ * the spaces at the beginning and the end of the line are stripped
+ */
+static int get_line_from_fw(char *buf, int size, size_t *fw_size_p,
+ const void **fw_data_p)
+{
+ int len;
+ size_t fw_size = *fw_size_p;
+ const char *p = *fw_data_p;
+
+ while (isspace(*p) && fw_size) {
+ p++;
+ fw_size--;
+ }
+ if (!fw_size)
+ return 0;
+
+ for (len = 0; len < fw_size; len++) {
+ if (!*p)
+ break;
+ if (*p == '\n') {
+ p++;
+ len++;
+ break;
+ }
+ if (len < size)
+ *buf++ = *p++;
+ }
+ *buf = 0;
+ *fw_size_p = fw_size - len;
+ *fw_data_p = p;
+ remove_trail_spaces(buf);
+ return 1;
+}
+
+/**
+ * snd_hda_load_patch - load a "patch" firmware file and parse it
+ * @bus: HD-audio bus
+ * @fw_size: the firmware byte size
+ * @fw_buf: the firmware data
+ */
+int snd_hda_load_patch(struct hda_bus *bus, size_t fw_size, const void *fw_buf)
+{
+ char buf[128];
+ struct hda_codec *codec;
+ int line_mode;
+
+ line_mode = LINE_MODE_NONE;
+ codec = NULL;
+ while (get_line_from_fw(buf, sizeof(buf) - 1, &fw_size, &fw_buf)) {
+ if (!*buf || *buf == '#' || *buf == '\n')
+ continue;
+ if (*buf == '[')
+ line_mode = parse_line_mode(buf, bus);
+ else if (patch_items[line_mode].parser &&
+ (codec || line_mode <= LINE_MODE_CODEC))
+ patch_items[line_mode].parser(buf, bus, &codec);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_load_patch);
+#endif /* CONFIG_SND_HDA_PATCH_LOADER */
+
+/*
+ * sysfs entries
+ */
+static struct attribute *hda_dev_attrs[] = {
+ &dev_attr_vendor_id.attr,
+ &dev_attr_subsystem_id.attr,
+ &dev_attr_revision_id.attr,
+ &dev_attr_afg.attr,
+ &dev_attr_mfg.attr,
+ &dev_attr_vendor_name.attr,
+ &dev_attr_chip_name.attr,
+ &dev_attr_modelname.attr,
+ &dev_attr_init_pin_configs.attr,
+ &dev_attr_driver_pin_configs.attr,
+ &dev_attr_power_on_acct.attr,
+ &dev_attr_power_off_acct.attr,
+#ifdef CONFIG_SND_HDA_RECONFIG
+ &dev_attr_init_verbs.attr,
+ &dev_attr_hints.attr,
+ &dev_attr_user_pin_configs.attr,
+ &dev_attr_reconfig.attr,
+ &dev_attr_clear.attr,
+#endif
+ NULL
+};
+
+static const struct attribute_group hda_dev_attr_group = {
+ .attrs = hda_dev_attrs,
+};
+
+const struct attribute_group *snd_hda_dev_attr_groups[] = {
+ &hda_dev_attr_group,
+ NULL
+};
+
+void snd_hda_sysfs_init(struct hda_codec *codec)
+{
+ mutex_init(&codec->user_mutex);
+#ifdef CONFIG_SND_HDA_RECONFIG
+ snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
+ snd_array_init(&codec->hints, sizeof(struct hda_hint), 32);
+ snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16);
+#endif
+}
+
+void snd_hda_sysfs_clear(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_HDA_RECONFIG
+ struct hda_hint *hint;
+ int i;
+
+ /* clear init verbs */
+ snd_array_free(&codec->init_verbs);
+ /* clear hints */
+ snd_array_for_each(&codec->hints, i, hint) {
+ kfree(hint->key); /* we don't need to free hint->val */
+ }
+ snd_array_free(&codec->hints);
+ snd_array_free(&codec->user_pins);
+#endif
+}
diff --git a/sound/hda/controllers/Kconfig b/sound/hda/controllers/Kconfig
new file mode 100644
index 000000000000..34721f50b055
--- /dev/null
+++ b/sound/hda/controllers/Kconfig
@@ -0,0 +1,42 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SND_HDA_INTEL
+ tristate "HD Audio PCI"
+ depends on SND_PCI
+ select SND_HDA
+ select SND_INTEL_DSP_CONFIG
+ help
+ Say Y here to include support for Intel "High Definition
+ Audio" (Azalia) and its compatible devices.
+
+ This option enables the HD-audio controller. Don't forget
+ to choose the appropriate HD-audio codec options.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-hda-intel.
+
+config SND_HDA_TEGRA
+ tristate "NVIDIA Tegra HD Audio"
+ depends on ARCH_TEGRA
+ select SND_HDA
+ select SND_HDA_ALIGNED_MMIO
+ help
+ Say Y here to support the HDA controller present in NVIDIA
+ Tegra SoCs
+
+ This options enables support for the HD Audio controller
+ present in some NVIDIA Tegra SoCs, used to communicate audio
+ to the HDMI output.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-hda-tegra.
+
+config SND_HDA_ACPI
+ tristate "HD Audio ACPI"
+ depends on ACPI
+ select SND_HDA
+ help
+ Say Y here to include support for Azalia-compatible HDA controllers
+ which are advertised via ACPI objects.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-hda-acpi.
diff --git a/sound/hda/controllers/Makefile b/sound/hda/controllers/Makefile
new file mode 100644
index 000000000000..a4bcd055e9ae
--- /dev/null
+++ b/sound/hda/controllers/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+snd-hda-intel-y := intel.o
+snd-hda-tegra-y := tegra.o
+snd-hda-acpi-y := acpi.o
+
+subdir-ccflags-y += -I$(src)/../common
+
+# for trace-points
+CFLAGS_intel.o := -I$(src)
+
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
+obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o
+obj-$(CONFIG_SND_HDA_ACPI) += snd-hda-acpi.o
diff --git a/sound/hda/controllers/acpi.c b/sound/hda/controllers/acpi.c
new file mode 100644
index 000000000000..505cc97e0ee9
--- /dev/null
+++ b/sound/hda/controllers/acpi.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ALSA driver for ACPI-based HDA Controllers.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+
+#include <sound/hda_codec.h>
+
+#include "hda_controller.h"
+
+struct hda_acpi {
+ struct azx azx;
+ struct snd_card *card;
+ struct platform_device *pdev;
+ void __iomem *regs;
+ struct work_struct probe_work;
+ const struct hda_data *data;
+};
+
+/**
+ * struct hda_data - Optional device-specific data
+ * @short_name: Used for the ALSA card name; defaults to KBUILD_MODNAME
+ * @long_name: Used for longer description; defaults to short_name
+ * @flags: Passed to &azx->driver_caps
+ *
+ * A pointer to a record of this type may be stored in the
+ * &acpi_device_id->driver_data field of an ACPI match table entry in order to
+ * customize the naming and behavior of a particular device. All fields are
+ * optional and sensible defaults will be selected in their absence.
+ */
+struct hda_data {
+ const char *short_name;
+ const char *long_name;
+ unsigned long flags;
+};
+
+static int hda_acpi_dev_disconnect(struct snd_device *device)
+{
+ struct azx *chip = device->device_data;
+
+ chip->bus.shutdown = 1;
+ return 0;
+}
+
+static int hda_acpi_dev_free(struct snd_device *device)
+{
+ struct azx *azx = device->device_data;
+ struct hda_acpi *hda = container_of(azx, struct hda_acpi, azx);
+
+ cancel_work_sync(&hda->probe_work);
+ if (azx_bus(azx)->chip_init) {
+ azx_stop_all_streams(azx);
+ azx_stop_chip(azx);
+ }
+
+ azx_free_stream_pages(azx);
+ azx_free_streams(azx);
+ snd_hdac_bus_exit(azx_bus(azx));
+
+ return 0;
+}
+
+static int hda_acpi_init(struct hda_acpi *hda)
+{
+ struct hdac_bus *bus = azx_bus(&hda->azx);
+ struct snd_card *card = hda->azx.card;
+ struct device *dev = &hda->pdev->dev;
+ struct azx *azx = &hda->azx;
+ struct resource *res;
+ unsigned short gcap;
+ const char *sname, *lname;
+ int err, irq;
+
+ /* The base address for the HDA registers and the interrupt are wrapped
+ * in an ACPI _CRS object which can be parsed by platform_get_irq() and
+ * devm_platform_get_and_ioremap_resource()
+ */
+
+ irq = platform_get_irq(hda->pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ hda->regs = devm_platform_get_and_ioremap_resource(hda->pdev, 0, &res);
+ if (IS_ERR(hda->regs))
+ return PTR_ERR(hda->regs);
+
+ bus->remap_addr = hda->regs;
+ bus->addr = res->start;
+
+ err = devm_request_irq(dev, irq, azx_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, azx);
+ if (err) {
+ dev_err(dev, "unable to request IRQ %d, disabling device\n",
+ irq);
+ return err;
+ }
+ bus->irq = irq;
+ bus->dma_stop_delay = 100;
+ card->sync_irq = bus->irq;
+
+ gcap = azx_readw(azx, GCAP);
+ dev_dbg(dev, "chipset global capabilities = 0x%x\n", gcap);
+
+ azx->align_buffer_size = 1;
+
+ azx->capture_streams = (gcap >> 8) & 0x0f;
+ azx->playback_streams = (gcap >> 12) & 0x0f;
+
+ azx->capture_index_offset = 0;
+ azx->playback_index_offset = azx->capture_streams;
+ azx->num_streams = azx->playback_streams + azx->capture_streams;
+
+ err = azx_init_streams(azx);
+ if (err < 0) {
+ dev_err(dev, "failed to initialize streams: %d\n", err);
+ return err;
+ }
+
+ err = azx_alloc_stream_pages(azx);
+ if (err < 0) {
+ dev_err(dev, "failed to allocate stream pages: %d\n", err);
+ return err;
+ }
+
+ azx_init_chip(azx, 1);
+
+ if (!bus->codec_mask) {
+ dev_err(dev, "no codecs found!\n");
+ return -ENODEV;
+ }
+
+ strscpy(card->driver, "hda-acpi");
+
+ sname = hda->data->short_name ? hda->data->short_name : KBUILD_MODNAME;
+
+ if (strlen(sname) > sizeof(card->shortname))
+ dev_info(dev, "truncating shortname for card %s\n", sname);
+ strscpy(card->shortname, sname);
+
+ lname = hda->data->long_name ? hda->data->long_name : sname;
+
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx irq %i", lname, bus->addr, bus->irq);
+
+ return 0;
+}
+
+static void hda_acpi_probe_work(struct work_struct *work)
+{
+ struct hda_acpi *hda = container_of(work, struct hda_acpi, probe_work);
+ struct azx *chip = &hda->azx;
+ int err;
+
+ err = hda_acpi_init(hda);
+ if (err < 0)
+ return;
+
+ err = azx_probe_codecs(chip, 8);
+ if (err < 0)
+ return;
+
+ err = azx_codec_configure(chip);
+ if (err < 0)
+ return;
+
+ err = snd_card_register(chip->card);
+ if (err < 0)
+ return;
+
+ chip->running = 1;
+}
+
+static int hda_acpi_create(struct hda_acpi *hda)
+{
+ static const struct snd_device_ops ops = {
+ .dev_disconnect = hda_acpi_dev_disconnect,
+ .dev_free = hda_acpi_dev_free,
+ };
+ static const struct hda_controller_ops null_ops;
+ struct azx *azx = &hda->azx;
+ int err;
+
+ mutex_init(&azx->open_mutex);
+ azx->card = hda->card;
+ INIT_LIST_HEAD(&azx->pcm_list);
+
+ azx->ops = &null_ops;
+ azx->driver_caps = hda->data->flags;
+ azx->driver_type = hda->data->flags & 0xff;
+ azx->codec_probe_mask = -1;
+
+ err = azx_bus_init(azx, NULL);
+ if (err < 0)
+ return err;
+
+ err = snd_device_new(hda->card, SNDRV_DEV_LOWLEVEL, &hda->azx, &ops);
+ if (err < 0) {
+ dev_err(&hda->pdev->dev, "Error creating device\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int hda_acpi_probe(struct platform_device *pdev)
+{
+ struct hda_acpi *hda;
+ int err;
+
+ hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL);
+ if (!hda)
+ return -ENOMEM;
+
+ hda->pdev = pdev;
+ hda->data = acpi_device_get_match_data(&pdev->dev);
+
+ /* Fall back to defaults if the table didn't have a *struct hda_data */
+ if (!hda->data)
+ hda->data = devm_kzalloc(&pdev->dev, sizeof(*hda->data),
+ GFP_KERNEL);
+ if (!hda->data)
+ return -ENOMEM;
+
+ err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, 0, &hda->card);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Error creating card!\n");
+ return err;
+ }
+
+ INIT_WORK(&hda->probe_work, hda_acpi_probe_work);
+
+ err = hda_acpi_create(hda);
+ if (err < 0)
+ goto out_free;
+ hda->card->private_data = &hda->azx;
+
+ dev_set_drvdata(&pdev->dev, hda->card);
+
+ schedule_work(&hda->probe_work);
+
+ return 0;
+
+out_free:
+ snd_card_free(hda->card);
+ return err;
+}
+
+static void hda_acpi_remove(struct platform_device *pdev)
+{
+ snd_card_free(dev_get_drvdata(&pdev->dev));
+}
+
+static void hda_acpi_shutdown(struct platform_device *pdev)
+{
+ struct snd_card *card = dev_get_drvdata(&pdev->dev);
+ struct azx *chip;
+
+ if (!card)
+ return;
+ chip = card->private_data;
+ if (chip && chip->running)
+ azx_stop_chip(chip);
+}
+
+static int hda_acpi_suspend(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ int rc;
+
+ rc = pm_runtime_force_suspend(dev);
+ if (rc < 0)
+ return rc;
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+ return 0;
+}
+
+static int hda_acpi_resume(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ int rc;
+
+ rc = pm_runtime_force_resume(dev);
+ if (rc < 0)
+ return rc;
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+ return 0;
+}
+
+static const struct dev_pm_ops hda_acpi_pm = {
+ SYSTEM_SLEEP_PM_OPS(hda_acpi_suspend, hda_acpi_resume)
+};
+
+static const struct hda_data nvidia_hda_data = {
+ .short_name = "NVIDIA",
+ .long_name = "NVIDIA HDA Controller",
+ .flags = AZX_DCAPS_CORBRP_SELF_CLEAR,
+};
+
+static const struct acpi_device_id hda_acpi_match[] = {
+ { .id = "NVDA2014", .driver_data = (uintptr_t) &nvidia_hda_data },
+ { .id = "NVDA2015", .driver_data = (uintptr_t) &nvidia_hda_data },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, hda_acpi_match);
+
+static struct platform_driver hda_acpi_platform_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .pm = &hda_acpi_pm,
+ .acpi_match_table = hda_acpi_match,
+ },
+ .probe = hda_acpi_probe,
+ .remove = hda_acpi_remove,
+ .shutdown = hda_acpi_shutdown,
+};
+module_platform_driver(hda_acpi_platform_driver);
+
+MODULE_DESCRIPTION("Driver for ACPI-based HDA Controllers");
+MODULE_LICENSE("GPL");
diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c
new file mode 100644
index 000000000000..1e8e3d61291a
--- /dev/null
+++ b/sound/hda/controllers/intel.c
@@ -0,0 +1,2834 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * hda_intel.c - Implementation of primary alsa driver code base
+ * for Intel HD Audio.
+ *
+ * Copyright(c) 2004 Intel Corporation
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ * PeiSen Hou <pshou@realtek.com.tw>
+ *
+ * CONTACTS:
+ *
+ * Matt Jared matt.jared@intel.com
+ * Andy Kopp andy.kopp@intel.com
+ * Dan Kogan dan.d.kogan@intel.com
+ *
+ * CHANGES:
+ *
+ * 2004.12.01 Major rewrite by tiwai, merged the work of pshou
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <linux/clocksource.h>
+#include <linux/time.h>
+#include <linux/completion.h>
+#include <linux/acpi.h>
+#include <linux/pgtable.h>
+#include <linux/dmi.h>
+
+#ifdef CONFIG_X86
+/* for snoop control */
+#include <asm/set_memory.h>
+#include <asm/cpufeature.h>
+#endif
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
+#include <sound/intel-dsp-config.h>
+#include <linux/vgaarb.h>
+#include <linux/vga_switcheroo.h>
+#include <linux/apple-gmux.h>
+#include <linux/firmware.h>
+#include <sound/hda_codec.h>
+#include "intel.h"
+
+#define CREATE_TRACE_POINTS
+#include "intel_trace.h"
+
+/* position fix mode */
+enum {
+ POS_FIX_AUTO,
+ POS_FIX_LPIB,
+ POS_FIX_POSBUF,
+ POS_FIX_VIACOMBO,
+ POS_FIX_COMBO,
+ POS_FIX_SKL,
+ POS_FIX_FIFO,
+};
+
+/* Defines for ATI HD Audio support in SB450 south bridge */
+#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR 0x42
+#define ATI_SB450_HDAUDIO_ENABLE_SNOOP 0x02
+
+/* Defines for Nvidia HDA support */
+#define NVIDIA_HDA_TRANSREG_ADDR 0x4e
+#define NVIDIA_HDA_ENABLE_COHBITS 0x0f
+#define NVIDIA_HDA_ISTRM_COH 0x4d
+#define NVIDIA_HDA_OSTRM_COH 0x4c
+#define NVIDIA_HDA_ENABLE_COHBIT 0x01
+
+/* Defines for Intel SCH HDA snoop control */
+#define INTEL_HDA_CGCTL 0x48
+#define INTEL_HDA_CGCTL_MISCBDCGE (0x1 << 6)
+#define INTEL_SCH_HDA_DEVC 0x78
+#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11)
+
+/* max number of SDs */
+/* ICH, ATI and VIA have 4 playback and 4 capture */
+#define ICH6_NUM_CAPTURE 4
+#define ICH6_NUM_PLAYBACK 4
+
+/* ULI has 6 playback and 5 capture */
+#define ULI_NUM_CAPTURE 5
+#define ULI_NUM_PLAYBACK 6
+
+/* ATI HDMI may have up to 8 playbacks and 0 capture */
+#define ATIHDMI_NUM_CAPTURE 0
+#define ATIHDMI_NUM_PLAYBACK 8
+
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static char *model[SNDRV_CARDS];
+static int position_fix[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
+static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
+static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
+static int probe_only[SNDRV_CARDS];
+static int jackpoll_ms[SNDRV_CARDS];
+static int single_cmd = -1;
+static int enable_msi = -1;
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+static char *patch[SNDRV_CARDS];
+#endif
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
+ CONFIG_SND_HDA_INPUT_BEEP_MODE};
+#endif
+static bool dmic_detect = 1;
+static bool ctl_dev_id = IS_ENABLED(CONFIG_SND_HDA_CTL_DEV_ID) ? 1 : 0;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Intel HD audio interface.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Intel HD audio interface.");
+module_param_array(model, charp, NULL, 0444);
+MODULE_PARM_DESC(model, "Use the given board model.");
+module_param_array(position_fix, int, NULL, 0444);
+MODULE_PARM_DESC(position_fix, "DMA pointer read method."
+ "(-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO, 5 = SKL+, 6 = FIFO).");
+module_param_array(bdl_pos_adj, int, NULL, 0644);
+MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
+module_param_array(probe_mask, int, NULL, 0444);
+MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
+module_param_array(probe_only, int, NULL, 0444);
+MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization.");
+module_param_array(jackpoll_ms, int, NULL, 0444);
+MODULE_PARM_DESC(jackpoll_ms, "Ms between polling for jack events (default = 0, using unsol events only)");
+module_param(single_cmd, bint, 0444);
+MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
+ "(for debugging only).");
+module_param(enable_msi, bint, 0444);
+MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+module_param_array(patch, charp, NULL, 0444);
+MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface.");
+#endif
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+module_param_array(beep_mode, bool, NULL, 0444);
+MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode "
+ "(0=off, 1=on) (default=1).");
+#endif
+module_param(dmic_detect, bool, 0444);
+MODULE_PARM_DESC(dmic_detect, "Allow DSP driver selection (bypass this driver) "
+ "(0=off, 1=on) (default=1); "
+ "deprecated, use snd-intel-dspcfg.dsp_driver option instead");
+module_param(ctl_dev_id, bool, 0444);
+MODULE_PARM_DESC(ctl_dev_id, "Use control device identifier (based on codec address).");
+
+#ifdef CONFIG_PM
+static int param_set_xint(const char *val, const struct kernel_param *kp);
+static const struct kernel_param_ops param_ops_xint = {
+ .set = param_set_xint,
+ .get = param_get_int,
+};
+#define param_check_xint param_check_int
+
+static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
+module_param(power_save, xint, 0644);
+MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
+ "(in second, 0 = disable).");
+
+static int pm_blacklist = -1;
+module_param(pm_blacklist, bint, 0644);
+MODULE_PARM_DESC(pm_blacklist, "Enable power-management denylist");
+
+/* reset the HD-audio controller in power save mode.
+ * this may give more power-saving, but will take longer time to
+ * wake up.
+ */
+static bool power_save_controller = 1;
+module_param(power_save_controller, bool, 0644);
+MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode.");
+#else /* CONFIG_PM */
+#define power_save 0
+#define pm_blacklist 0
+#define power_save_controller false
+#endif /* CONFIG_PM */
+
+static int align_buffer_size = -1;
+module_param(align_buffer_size, bint, 0644);
+MODULE_PARM_DESC(align_buffer_size,
+ "Force buffer and period sizes to be multiple of 128 bytes.");
+
+#ifdef CONFIG_X86
+static int hda_snoop = -1;
+module_param_named(snoop, hda_snoop, bint, 0444);
+MODULE_PARM_DESC(snoop, "Enable/disable snooping");
+#else
+#define hda_snoop true
+#endif
+
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Intel HDA driver");
+
+#if defined(CONFIG_PM) && defined(CONFIG_VGA_SWITCHEROO)
+#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI)
+#define SUPPORT_VGA_SWITCHEROO
+#endif
+#endif
+
+
+/*
+ */
+
+/* driver types */
+enum {
+ AZX_DRIVER_ICH,
+ AZX_DRIVER_PCH,
+ AZX_DRIVER_SCH,
+ AZX_DRIVER_SKL,
+ AZX_DRIVER_HDMI,
+ AZX_DRIVER_ATI,
+ AZX_DRIVER_ATIHDMI,
+ AZX_DRIVER_ATIHDMI_NS,
+ AZX_DRIVER_GFHDMI,
+ AZX_DRIVER_VIA,
+ AZX_DRIVER_SIS,
+ AZX_DRIVER_ULI,
+ AZX_DRIVER_NVIDIA,
+ AZX_DRIVER_TERA,
+ AZX_DRIVER_CTX,
+ AZX_DRIVER_CTHDA,
+ AZX_DRIVER_CMEDIA,
+ AZX_DRIVER_ZHAOXIN,
+ AZX_DRIVER_ZHAOXINHDMI,
+ AZX_DRIVER_LOONGSON,
+ AZX_DRIVER_GENERIC,
+ AZX_NUM_DRIVERS, /* keep this as last entry */
+};
+
+#define azx_get_snoop_type(chip) \
+ (((chip)->driver_caps & AZX_DCAPS_SNOOP_MASK) >> 10)
+#define AZX_DCAPS_SNOOP_TYPE(type) ((AZX_SNOOP_TYPE_ ## type) << 10)
+
+/* quirks for old Intel chipsets */
+#define AZX_DCAPS_INTEL_ICH \
+ (AZX_DCAPS_OLD_SSYNC | AZX_DCAPS_NO_ALIGN_BUFSIZE)
+
+/* quirks for Intel PCH */
+#define AZX_DCAPS_INTEL_PCH_BASE \
+ (AZX_DCAPS_NO_ALIGN_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY |\
+ AZX_DCAPS_SNOOP_TYPE(SCH))
+
+/* PCH up to IVB; no runtime PM; bind with i915 gfx */
+#define AZX_DCAPS_INTEL_PCH_NOPM \
+ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_I915_COMPONENT)
+
+/* PCH for HSW/BDW; with runtime PM */
+/* no i915 binding for this as HSW/BDW has another controller for HDMI */
+#define AZX_DCAPS_INTEL_PCH \
+ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME)
+
+/* HSW HDMI */
+#define AZX_DCAPS_INTEL_HASWELL \
+ (/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_COUNT_LPIB_DELAY |\
+ AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_COMPONENT |\
+ AZX_DCAPS_SNOOP_TYPE(SCH))
+
+/* Broadwell HDMI can't use position buffer reliably, force to use LPIB */
+#define AZX_DCAPS_INTEL_BROADWELL \
+ (/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_POSFIX_LPIB |\
+ AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_COMPONENT |\
+ AZX_DCAPS_SNOOP_TYPE(SCH))
+
+#define AZX_DCAPS_INTEL_BAYTRAIL \
+ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_I915_COMPONENT)
+
+#define AZX_DCAPS_INTEL_BRASWELL \
+ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\
+ AZX_DCAPS_I915_COMPONENT)
+
+#define AZX_DCAPS_INTEL_SKYLAKE \
+ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\
+ AZX_DCAPS_SEPARATE_STREAM_TAG | AZX_DCAPS_I915_COMPONENT)
+
+#define AZX_DCAPS_INTEL_BROXTON AZX_DCAPS_INTEL_SKYLAKE
+
+#define AZX_DCAPS_INTEL_LNL \
+ (AZX_DCAPS_INTEL_SKYLAKE | AZX_DCAPS_PIO_COMMANDS)
+
+/* quirks for ATI SB / AMD Hudson */
+#define AZX_DCAPS_PRESET_ATI_SB \
+ (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_POSFIX_LPIB |\
+ AZX_DCAPS_SNOOP_TYPE(ATI))
+
+/* quirks for ATI/AMD HDMI */
+#define AZX_DCAPS_PRESET_ATI_HDMI \
+ (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_POSFIX_LPIB|\
+ AZX_DCAPS_NO_MSI64)
+
+/* quirks for ATI HDMI with snoop off */
+#define AZX_DCAPS_PRESET_ATI_HDMI_NS \
+ (AZX_DCAPS_PRESET_ATI_HDMI | AZX_DCAPS_SNOOP_OFF)
+
+/* quirks for AMD SB */
+#define AZX_DCAPS_PRESET_AMD_SB \
+ (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_AMD_WORKAROUND |\
+ AZX_DCAPS_SNOOP_TYPE(ATI) | AZX_DCAPS_PM_RUNTIME |\
+ AZX_DCAPS_RETRY_PROBE)
+
+/* quirks for Nvidia */
+#define AZX_DCAPS_PRESET_NVIDIA \
+ (AZX_DCAPS_NO_MSI | AZX_DCAPS_CORBRP_SELF_CLEAR |\
+ AZX_DCAPS_SNOOP_TYPE(NVIDIA))
+
+#define AZX_DCAPS_PRESET_CTHDA \
+ (AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB |\
+ AZX_DCAPS_NO_64BIT |\
+ AZX_DCAPS_4K_BDLE_BOUNDARY | AZX_DCAPS_SNOOP_OFF)
+
+/*
+ * vga_switcheroo support
+ */
+#ifdef SUPPORT_VGA_SWITCHEROO
+#define use_vga_switcheroo(chip) ((chip)->use_vga_switcheroo)
+#define needs_eld_notify_link(chip) ((chip)->bus.keep_power)
+#else
+#define use_vga_switcheroo(chip) 0
+#define needs_eld_notify_link(chip) false
+#endif
+
+static const char * const driver_short_names[] = {
+ [AZX_DRIVER_ICH] = "HDA Intel",
+ [AZX_DRIVER_PCH] = "HDA Intel PCH",
+ [AZX_DRIVER_SCH] = "HDA Intel MID",
+ [AZX_DRIVER_SKL] = "HDA Intel PCH", /* kept old name for compatibility */
+ [AZX_DRIVER_HDMI] = "HDA Intel HDMI",
+ [AZX_DRIVER_ATI] = "HDA ATI SB",
+ [AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI",
+ [AZX_DRIVER_ATIHDMI_NS] = "HDA ATI HDMI",
+ [AZX_DRIVER_GFHDMI] = "HDA GF HDMI",
+ [AZX_DRIVER_VIA] = "HDA VIA VT82xx",
+ [AZX_DRIVER_SIS] = "HDA SIS966",
+ [AZX_DRIVER_ULI] = "HDA ULI M5461",
+ [AZX_DRIVER_NVIDIA] = "HDA NVidia",
+ [AZX_DRIVER_TERA] = "HDA Teradici",
+ [AZX_DRIVER_CTX] = "HDA Creative",
+ [AZX_DRIVER_CTHDA] = "HDA Creative",
+ [AZX_DRIVER_CMEDIA] = "HDA C-Media",
+ [AZX_DRIVER_ZHAOXIN] = "HDA Zhaoxin",
+ [AZX_DRIVER_ZHAOXINHDMI] = "HDA Zhaoxin HDMI",
+ [AZX_DRIVER_LOONGSON] = "HDA Loongson",
+ [AZX_DRIVER_GENERIC] = "HD-Audio Generic",
+};
+
+static int azx_acquire_irq(struct azx *chip, int do_disconnect);
+static void set_default_power_save(struct azx *chip);
+
+/*
+ * initialize the PCI registers
+ */
+/* update bits in a PCI register byte */
+static void update_pci_byte(struct pci_dev *pci, unsigned int reg,
+ unsigned char mask, unsigned char val)
+{
+ unsigned char data;
+
+ pci_read_config_byte(pci, reg, &data);
+ data &= ~mask;
+ data |= (val & mask);
+ pci_write_config_byte(pci, reg, data);
+}
+
+static void azx_init_pci(struct azx *chip)
+{
+ int snoop_type = azx_get_snoop_type(chip);
+
+ /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44)
+ * TCSEL == Traffic Class Select Register, which sets PCI express QOS
+ * Ensuring these bits are 0 clears playback static on some HD Audio
+ * codecs.
+ * The PCI register TCSEL is defined in the Intel manuals.
+ */
+ if (!(chip->driver_caps & AZX_DCAPS_NO_TCSEL)) {
+ dev_dbg(chip->card->dev, "Clearing TCSEL\n");
+ update_pci_byte(chip->pci, AZX_PCIREG_TCSEL, 0x07, 0);
+ }
+
+ /* For ATI SB450/600/700/800/900 and AMD Hudson azalia HD audio,
+ * we need to enable snoop.
+ */
+ if (snoop_type == AZX_SNOOP_TYPE_ATI) {
+ dev_dbg(chip->card->dev, "Setting ATI snoop: %d\n",
+ azx_snoop(chip));
+ update_pci_byte(chip->pci,
+ ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, 0x07,
+ azx_snoop(chip) ? ATI_SB450_HDAUDIO_ENABLE_SNOOP : 0);
+ }
+
+ /* For NVIDIA HDA, enable snoop */
+ if (snoop_type == AZX_SNOOP_TYPE_NVIDIA) {
+ dev_dbg(chip->card->dev, "Setting Nvidia snoop: %d\n",
+ azx_snoop(chip));
+ update_pci_byte(chip->pci,
+ NVIDIA_HDA_TRANSREG_ADDR,
+ 0x0f, NVIDIA_HDA_ENABLE_COHBITS);
+ update_pci_byte(chip->pci,
+ NVIDIA_HDA_ISTRM_COH,
+ 0x01, NVIDIA_HDA_ENABLE_COHBIT);
+ update_pci_byte(chip->pci,
+ NVIDIA_HDA_OSTRM_COH,
+ 0x01, NVIDIA_HDA_ENABLE_COHBIT);
+ }
+
+ /* Enable SCH/PCH snoop if needed */
+ if (snoop_type == AZX_SNOOP_TYPE_SCH) {
+ unsigned short snoop;
+ pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop);
+ if ((!azx_snoop(chip) && !(snoop & INTEL_SCH_HDA_DEVC_NOSNOOP)) ||
+ (azx_snoop(chip) && (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP))) {
+ snoop &= ~INTEL_SCH_HDA_DEVC_NOSNOOP;
+ if (!azx_snoop(chip))
+ snoop |= INTEL_SCH_HDA_DEVC_NOSNOOP;
+ pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC, snoop);
+ pci_read_config_word(chip->pci,
+ INTEL_SCH_HDA_DEVC, &snoop);
+ }
+ dev_dbg(chip->card->dev, "SCH snoop: %s\n",
+ (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) ?
+ "Disabled" : "Enabled");
+ }
+}
+
+/*
+ * In BXT-P A0, HD-Audio DMA requests is later than expected,
+ * and makes an audio stream sensitive to system latencies when
+ * 24/32 bits are playing.
+ * Adjusting threshold of DMA fifo to force the DMA request
+ * sooner to improve latency tolerance at the expense of power.
+ */
+static void bxt_reduce_dma_latency(struct azx *chip)
+{
+ u32 val;
+
+ val = azx_readl(chip, VS_EM4L);
+ val &= (0x3 << 20);
+ azx_writel(chip, VS_EM4L, val);
+}
+
+/*
+ * ML_LCAP bits:
+ * bit 0: 6 MHz Supported
+ * bit 1: 12 MHz Supported
+ * bit 2: 24 MHz Supported
+ * bit 3: 48 MHz Supported
+ * bit 4: 96 MHz Supported
+ * bit 5: 192 MHz Supported
+ */
+static int intel_get_lctl_scf(struct azx *chip)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ static const int preferred_bits[] = { 2, 3, 1, 4, 5 };
+ u32 val, t;
+ int i;
+
+ val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCAP);
+
+ for (i = 0; i < ARRAY_SIZE(preferred_bits); i++) {
+ t = preferred_bits[i];
+ if (val & (1 << t))
+ return t;
+ }
+
+ dev_warn(chip->card->dev, "set audio clock frequency to 6MHz");
+ return 0;
+}
+
+static int intel_ml_lctl_set_power(struct azx *chip, int state)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ u32 val;
+ int timeout;
+
+ /*
+ * Changes to LCTL.SCF are only needed for the first multi-link dealing
+ * with external codecs
+ */
+ val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
+ val &= ~AZX_ML_LCTL_SPA;
+ val |= state << AZX_ML_LCTL_SPA_SHIFT;
+ writel(val, bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
+ /* wait for CPA */
+ timeout = 50;
+ while (timeout) {
+ if (((readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL)) &
+ AZX_ML_LCTL_CPA) == (state << AZX_ML_LCTL_CPA_SHIFT))
+ return 0;
+ timeout--;
+ udelay(10);
+ }
+
+ return -1;
+}
+
+static void intel_init_lctl(struct azx *chip)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ u32 val;
+ int ret;
+
+ /* 0. check lctl register value is correct or not */
+ val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
+ /* only perform additional configurations if the SCF is initially based on 6MHz */
+ if ((val & AZX_ML_LCTL_SCF) != 0)
+ return;
+
+ /*
+ * Before operating on SPA, CPA must match SPA.
+ * Any deviation may result in undefined behavior.
+ */
+ if (((val & AZX_ML_LCTL_SPA) >> AZX_ML_LCTL_SPA_SHIFT) !=
+ ((val & AZX_ML_LCTL_CPA) >> AZX_ML_LCTL_CPA_SHIFT))
+ return;
+
+ /* 1. turn link down: set SPA to 0 and wait CPA to 0 */
+ ret = intel_ml_lctl_set_power(chip, 0);
+ udelay(100);
+ if (ret)
+ goto set_spa;
+
+ /* 2. update SCF to select an audio clock different from 6MHz */
+ val &= ~AZX_ML_LCTL_SCF;
+ val |= intel_get_lctl_scf(chip);
+ writel(val, bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
+
+set_spa:
+ /* 4. turn link up: set SPA to 1 and wait CPA to 1 */
+ intel_ml_lctl_set_power(chip, 1);
+ udelay(100);
+}
+
+static void hda_intel_init_chip(struct azx *chip, bool full_reset)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ struct pci_dev *pci = chip->pci;
+ u32 val;
+
+ snd_hdac_set_codec_wakeup(bus, true);
+ if (chip->driver_type == AZX_DRIVER_SKL) {
+ pci_read_config_dword(pci, INTEL_HDA_CGCTL, &val);
+ val = val & ~INTEL_HDA_CGCTL_MISCBDCGE;
+ pci_write_config_dword(pci, INTEL_HDA_CGCTL, val);
+ }
+ azx_init_chip(chip, full_reset);
+ if (chip->driver_type == AZX_DRIVER_SKL) {
+ pci_read_config_dword(pci, INTEL_HDA_CGCTL, &val);
+ val = val | INTEL_HDA_CGCTL_MISCBDCGE;
+ pci_write_config_dword(pci, INTEL_HDA_CGCTL, val);
+ }
+
+ snd_hdac_set_codec_wakeup(bus, false);
+
+ /* reduce dma latency to avoid noise */
+ if (HDA_CONTROLLER_IS_APL(pci))
+ bxt_reduce_dma_latency(chip);
+
+ if (bus->mlcap != NULL)
+ intel_init_lctl(chip);
+}
+
+/* calculate runtime delay from LPIB */
+static int azx_get_delay_from_lpib(struct azx *chip, struct azx_dev *azx_dev,
+ unsigned int pos)
+{
+ struct snd_pcm_substream *substream = azx_dev->core.substream;
+ int stream = substream->stream;
+ unsigned int lpib_pos = azx_get_pos_lpib(chip, azx_dev);
+ int delay;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ delay = pos - lpib_pos;
+ else
+ delay = lpib_pos - pos;
+ if (delay < 0) {
+ if (delay >= azx_dev->core.delay_negative_threshold)
+ delay = 0;
+ else
+ delay += azx_dev->core.bufsize;
+ }
+
+ if (delay >= azx_dev->core.period_bytes) {
+ dev_info(chip->card->dev,
+ "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n",
+ delay, azx_dev->core.period_bytes);
+ delay = 0;
+ chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY;
+ chip->get_delay[stream] = NULL;
+ }
+
+ return bytes_to_frames(substream->runtime, delay);
+}
+
+static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev);
+
+/* called from IRQ */
+static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev)
+{
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ int ok;
+
+ ok = azx_position_ok(chip, azx_dev);
+ if (ok == 1) {
+ azx_dev->irq_pending = 0;
+ return ok;
+ } else if (ok == 0) {
+ /* bogus IRQ, process it later */
+ azx_dev->irq_pending = 1;
+ schedule_work(&hda->irq_pending_work);
+ }
+ return 0;
+}
+
+#define display_power(chip, enable) \
+ snd_hdac_display_power(azx_bus(chip), HDA_CODEC_IDX_CONTROLLER, enable)
+
+/*
+ * Check whether the current DMA position is acceptable for updating
+ * periods. Returns non-zero if it's OK.
+ *
+ * Many HD-audio controllers appear pretty inaccurate about
+ * the update-IRQ timing. The IRQ is issued before actually the
+ * data is processed. So, we need to process it afterwords in a
+ * workqueue.
+ *
+ * Returns 1 if OK to proceed, 0 for delay handling, -1 for skipping update
+ */
+static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
+{
+ struct snd_pcm_substream *substream = azx_dev->core.substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int stream = substream->stream;
+ u32 wallclk;
+ unsigned int pos;
+ snd_pcm_uframes_t hwptr, target;
+
+ /*
+ * The value of the WALLCLK register is always 0
+ * on the Loongson controller, so we return directly.
+ */
+ if (chip->driver_type == AZX_DRIVER_LOONGSON)
+ return 1;
+
+ wallclk = azx_readl(chip, WALLCLK) - azx_dev->core.start_wallclk;
+ if (wallclk < (azx_dev->core.period_wallclk * 2) / 3)
+ return -1; /* bogus (too early) interrupt */
+
+ if (chip->get_position[stream])
+ pos = chip->get_position[stream](chip, azx_dev);
+ else { /* use the position buffer as default */
+ pos = azx_get_pos_posbuf(chip, azx_dev);
+ if (!pos || pos == (u32)-1) {
+ dev_info(chip->card->dev,
+ "Invalid position buffer, using LPIB read method instead.\n");
+ chip->get_position[stream] = azx_get_pos_lpib;
+ if (chip->get_position[0] == azx_get_pos_lpib &&
+ chip->get_position[1] == azx_get_pos_lpib)
+ azx_bus(chip)->use_posbuf = false;
+ pos = azx_get_pos_lpib(chip, azx_dev);
+ chip->get_delay[stream] = NULL;
+ } else {
+ chip->get_position[stream] = azx_get_pos_posbuf;
+ if (chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)
+ chip->get_delay[stream] = azx_get_delay_from_lpib;
+ }
+ }
+
+ if (pos >= azx_dev->core.bufsize)
+ pos = 0;
+
+ if (WARN_ONCE(!azx_dev->core.period_bytes,
+ "hda-intel: zero azx_dev->period_bytes"))
+ return -1; /* this shouldn't happen! */
+ if (wallclk < (azx_dev->core.period_wallclk * 5) / 4 &&
+ pos % azx_dev->core.period_bytes > azx_dev->core.period_bytes / 2)
+ /* NG - it's below the first next period boundary */
+ return chip->bdl_pos_adj ? 0 : -1;
+ azx_dev->core.start_wallclk += wallclk;
+
+ if (azx_dev->core.no_period_wakeup)
+ return 1; /* OK, no need to check period boundary */
+
+ if (runtime->hw_ptr_base != runtime->hw_ptr_interrupt)
+ return 1; /* OK, already in hwptr updating process */
+
+ /* check whether the period gets really elapsed */
+ pos = bytes_to_frames(runtime, pos);
+ hwptr = runtime->hw_ptr_base + pos;
+ if (hwptr < runtime->status->hw_ptr)
+ hwptr += runtime->buffer_size;
+ target = runtime->hw_ptr_interrupt + runtime->period_size;
+ if (hwptr < target) {
+ /* too early wakeup, process it later */
+ return chip->bdl_pos_adj ? 0 : -1;
+ }
+
+ return 1; /* OK, it's fine */
+}
+
+/*
+ * The work for pending PCM period updates.
+ */
+static void azx_irq_pending_work(struct work_struct *work)
+{
+ struct hda_intel *hda = container_of(work, struct hda_intel, irq_pending_work);
+ struct azx *chip = &hda->chip;
+ struct hdac_bus *bus = azx_bus(chip);
+ struct hdac_stream *s;
+ int pending, ok;
+
+ if (!hda->irq_pending_warned) {
+ dev_info(chip->card->dev,
+ "IRQ timing workaround is activated for card #%d. Suggest a bigger bdl_pos_adj.\n",
+ chip->card->number);
+ hda->irq_pending_warned = 1;
+ }
+
+ for (;;) {
+ pending = 0;
+ spin_lock_irq(&bus->reg_lock);
+ list_for_each_entry(s, &bus->stream_list, list) {
+ struct azx_dev *azx_dev = stream_to_azx_dev(s);
+ if (!azx_dev->irq_pending ||
+ !s->substream ||
+ !s->running)
+ continue;
+ ok = azx_position_ok(chip, azx_dev);
+ if (ok > 0) {
+ azx_dev->irq_pending = 0;
+ spin_unlock(&bus->reg_lock);
+ snd_pcm_period_elapsed(s->substream);
+ spin_lock(&bus->reg_lock);
+ } else if (ok < 0) {
+ pending = 0; /* too early */
+ } else
+ pending++;
+ }
+ spin_unlock_irq(&bus->reg_lock);
+ if (!pending)
+ return;
+ msleep(1);
+ }
+}
+
+/* clear irq_pending flags and assure no on-going workq */
+static void azx_clear_irq_pending(struct azx *chip)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ struct hdac_stream *s;
+
+ guard(spinlock_irq)(&bus->reg_lock);
+ list_for_each_entry(s, &bus->stream_list, list) {
+ struct azx_dev *azx_dev = stream_to_azx_dev(s);
+ azx_dev->irq_pending = 0;
+ }
+}
+
+static int azx_acquire_irq(struct azx *chip, int do_disconnect)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ int ret;
+
+ if (!chip->msi || pci_alloc_irq_vectors(chip->pci, 1, 1, PCI_IRQ_MSI) < 0) {
+ ret = pci_alloc_irq_vectors(chip->pci, 1, 1, PCI_IRQ_INTX);
+ if (ret < 0)
+ return ret;
+ chip->msi = 0;
+ }
+
+ if (request_irq(chip->pci->irq, azx_interrupt,
+ chip->msi ? 0 : IRQF_SHARED,
+ chip->card->irq_descr, chip)) {
+ dev_err(chip->card->dev,
+ "unable to grab IRQ %d, disabling device\n",
+ chip->pci->irq);
+ if (do_disconnect)
+ snd_card_disconnect(chip->card);
+ return -1;
+ }
+ bus->irq = chip->pci->irq;
+ chip->card->sync_irq = bus->irq;
+ return 0;
+}
+
+/* get the current DMA position with correction on VIA chips */
+static unsigned int azx_via_get_position(struct azx *chip,
+ struct azx_dev *azx_dev)
+{
+ unsigned int link_pos, mini_pos, bound_pos;
+ unsigned int mod_link_pos, mod_dma_pos, mod_mini_pos;
+ unsigned int fifo_size;
+
+ link_pos = snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev));
+ if (azx_dev->core.substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* Playback, no problem using link position */
+ return link_pos;
+ }
+
+ /* Capture */
+ /* For new chipset,
+ * use mod to get the DMA position just like old chipset
+ */
+ mod_dma_pos = le32_to_cpu(*azx_dev->core.posbuf);
+ mod_dma_pos %= azx_dev->core.period_bytes;
+
+ fifo_size = azx_stream(azx_dev)->fifo_size;
+
+ if (azx_dev->insufficient) {
+ /* Link position never gather than FIFO size */
+ if (link_pos <= fifo_size)
+ return 0;
+
+ azx_dev->insufficient = 0;
+ }
+
+ if (link_pos <= fifo_size)
+ mini_pos = azx_dev->core.bufsize + link_pos - fifo_size;
+ else
+ mini_pos = link_pos - fifo_size;
+
+ /* Find nearest previous boudary */
+ mod_mini_pos = mini_pos % azx_dev->core.period_bytes;
+ mod_link_pos = link_pos % azx_dev->core.period_bytes;
+ if (mod_link_pos >= fifo_size)
+ bound_pos = link_pos - mod_link_pos;
+ else if (mod_dma_pos >= mod_mini_pos)
+ bound_pos = mini_pos - mod_mini_pos;
+ else {
+ bound_pos = mini_pos - mod_mini_pos + azx_dev->core.period_bytes;
+ if (bound_pos >= azx_dev->core.bufsize)
+ bound_pos = 0;
+ }
+
+ /* Calculate real DMA position we want */
+ return bound_pos + mod_dma_pos;
+}
+
+#define AMD_FIFO_SIZE 32
+
+/* get the current DMA position with FIFO size correction */
+static unsigned int azx_get_pos_fifo(struct azx *chip, struct azx_dev *azx_dev)
+{
+ struct snd_pcm_substream *substream = azx_dev->core.substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int pos, delay;
+
+ pos = snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev));
+ if (!runtime)
+ return pos;
+
+ runtime->delay = AMD_FIFO_SIZE;
+ delay = frames_to_bytes(runtime, AMD_FIFO_SIZE);
+ if (azx_dev->insufficient) {
+ if (pos < delay) {
+ delay = pos;
+ runtime->delay = bytes_to_frames(runtime, pos);
+ } else {
+ azx_dev->insufficient = 0;
+ }
+ }
+
+ /* correct the DMA position for capture stream */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (pos < delay)
+ pos += azx_dev->core.bufsize;
+ pos -= delay;
+ }
+
+ return pos;
+}
+
+static int azx_get_delay_from_fifo(struct azx *chip, struct azx_dev *azx_dev,
+ unsigned int pos)
+{
+ struct snd_pcm_substream *substream = azx_dev->core.substream;
+
+ /* just read back the calculated value in the above */
+ return substream->runtime->delay;
+}
+
+static void __azx_shutdown_chip(struct azx *chip, bool skip_link_reset)
+{
+ azx_stop_chip(chip);
+ if (!skip_link_reset)
+ azx_enter_link_reset(chip);
+ azx_clear_irq_pending(chip);
+ display_power(chip, false);
+}
+
+static DEFINE_MUTEX(card_list_lock);
+static LIST_HEAD(card_list);
+
+static void azx_shutdown_chip(struct azx *chip)
+{
+ __azx_shutdown_chip(chip, false);
+}
+
+static void azx_add_card_list(struct azx *chip)
+{
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+
+ guard(mutex)(&card_list_lock);
+ list_add(&hda->list, &card_list);
+}
+
+static void azx_del_card_list(struct azx *chip)
+{
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+
+ guard(mutex)(&card_list_lock);
+ list_del_init(&hda->list);
+}
+
+/* trigger power-save check at writing parameter */
+static int __maybe_unused param_set_xint(const char *val, const struct kernel_param *kp)
+{
+ struct hda_intel *hda;
+ struct azx *chip;
+ int prev = power_save;
+ int ret = param_set_int(val, kp);
+
+ if (ret || prev == power_save)
+ return ret;
+
+ if (pm_blacklist > 0)
+ return 0;
+
+ guard(mutex)(&card_list_lock);
+ list_for_each_entry(hda, &card_list, list) {
+ chip = &hda->chip;
+ if (!hda->probe_continued || chip->disabled ||
+ hda->runtime_pm_disabled)
+ continue;
+ snd_hda_set_power_save(&chip->bus, power_save * 1000);
+ }
+ return 0;
+}
+
+/*
+ * power management
+ */
+static bool azx_is_pm_ready(struct snd_card *card)
+{
+ struct azx *chip;
+ struct hda_intel *hda;
+
+ if (!card)
+ return false;
+ chip = card->private_data;
+ hda = container_of(chip, struct hda_intel, chip);
+ if (chip->disabled || hda->init_failed || !chip->running)
+ return false;
+ return true;
+}
+
+static void __azx_runtime_resume(struct azx *chip)
+{
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ struct hdac_bus *bus = azx_bus(chip);
+ struct hda_codec *codec;
+ int status;
+
+ display_power(chip, true);
+ if (hda->need_i915_power)
+ snd_hdac_i915_set_bclk(bus);
+
+ /* Read STATESTS before controller reset */
+ status = azx_readw(chip, STATESTS);
+
+ azx_init_pci(chip);
+ hda_intel_init_chip(chip, true);
+
+ /* Avoid codec resume if runtime resume is for system suspend */
+ if (!chip->pm_prepared) {
+ list_for_each_codec(codec, &chip->bus) {
+ if (codec->relaxed_resume)
+ continue;
+
+ if (codec->forced_resume || (status & (1 << codec->addr)))
+ pm_request_resume(hda_codec_dev(codec));
+ }
+ }
+
+ /* power down again for link-controlled chips */
+ if (!hda->need_i915_power)
+ display_power(chip, false);
+}
+
+static int azx_prepare(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip;
+
+ if (!azx_is_pm_ready(card))
+ return 0;
+
+ chip = card->private_data;
+ chip->pm_prepared = 1;
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+ flush_work(&azx_bus(chip)->unsol_work);
+
+ /* HDA controller always requires different WAKEEN for runtime suspend
+ * and system suspend, so don't use direct-complete here.
+ */
+ return 0;
+}
+
+static void azx_complete(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip;
+
+ if (!azx_is_pm_ready(card))
+ return;
+
+ chip = card->private_data;
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ chip->pm_prepared = 0;
+}
+
+static int azx_suspend(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip;
+
+ if (!azx_is_pm_ready(card))
+ return 0;
+
+ chip = card->private_data;
+ azx_shutdown_chip(chip);
+
+ trace_azx_suspend(chip);
+ return 0;
+}
+
+static int azx_resume(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip;
+
+ if (!azx_is_pm_ready(card))
+ return 0;
+
+ chip = card->private_data;
+
+ __azx_runtime_resume(chip);
+
+ trace_azx_resume(chip);
+ return 0;
+}
+
+/* put codec down to D3 at hibernation for Intel SKL+;
+ * otherwise BIOS may still access the codec and screw up the driver
+ */
+static int azx_freeze_noirq(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip = card->private_data;
+ struct pci_dev *pci = to_pci_dev(dev);
+
+ if (!azx_is_pm_ready(card))
+ return 0;
+ if (chip->driver_type == AZX_DRIVER_SKL)
+ pci_set_power_state(pci, PCI_D3hot);
+
+ return 0;
+}
+
+static int azx_thaw_noirq(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip = card->private_data;
+ struct pci_dev *pci = to_pci_dev(dev);
+
+ if (!azx_is_pm_ready(card))
+ return 0;
+ if (chip->driver_type == AZX_DRIVER_SKL)
+ pci_set_power_state(pci, PCI_D0);
+
+ return 0;
+}
+
+static int azx_runtime_suspend(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip;
+
+ if (!azx_is_pm_ready(card))
+ return 0;
+ chip = card->private_data;
+
+ /* enable controller wake up event */
+ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) | STATESTS_INT_MASK);
+
+ azx_shutdown_chip(chip);
+ trace_azx_runtime_suspend(chip);
+ return 0;
+}
+
+static int azx_runtime_resume(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip;
+
+ if (!azx_is_pm_ready(card))
+ return 0;
+ chip = card->private_data;
+ __azx_runtime_resume(chip);
+
+ /* disable controller Wake Up event*/
+ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & ~STATESTS_INT_MASK);
+
+ trace_azx_runtime_resume(chip);
+ return 0;
+}
+
+static int azx_runtime_idle(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip;
+ struct hda_intel *hda;
+
+ if (!card)
+ return 0;
+
+ chip = card->private_data;
+ hda = container_of(chip, struct hda_intel, chip);
+ if (chip->disabled || hda->init_failed)
+ return 0;
+
+ if (!power_save_controller || !azx_has_pm_runtime(chip) ||
+ azx_bus(chip)->codec_powered || !chip->running)
+ return -EBUSY;
+
+ /* ELD notification gets broken when HD-audio bus is off */
+ if (needs_eld_notify_link(chip))
+ return -EBUSY;
+
+ return 0;
+}
+
+static const struct dev_pm_ops azx_pm = {
+ SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume)
+ .prepare = pm_sleep_ptr(azx_prepare),
+ .complete = pm_sleep_ptr(azx_complete),
+ .freeze_noirq = pm_sleep_ptr(azx_freeze_noirq),
+ .thaw_noirq = pm_sleep_ptr(azx_thaw_noirq),
+ RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, azx_runtime_idle)
+};
+
+
+static int azx_probe_continue(struct azx *chip);
+
+#ifdef SUPPORT_VGA_SWITCHEROO
+static struct pci_dev *get_bound_vga(struct pci_dev *pci);
+
+static void azx_vs_set_state(struct pci_dev *pci,
+ enum vga_switcheroo_state state)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct azx *chip = card->private_data;
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ struct hda_codec *codec;
+ bool disabled;
+
+ wait_for_completion(&hda->probe_wait);
+ if (hda->init_failed)
+ return;
+
+ disabled = (state == VGA_SWITCHEROO_OFF);
+ if (chip->disabled == disabled)
+ return;
+
+ if (!hda->probe_continued) {
+ chip->disabled = disabled;
+ if (!disabled) {
+ dev_info(chip->card->dev,
+ "Start delayed initialization\n");
+ if (azx_probe_continue(chip) < 0)
+ dev_err(chip->card->dev, "initialization error\n");
+ }
+ } else {
+ dev_info(chip->card->dev, "%s via vga_switcheroo\n",
+ disabled ? "Disabling" : "Enabling");
+ if (disabled) {
+ list_for_each_codec(codec, &chip->bus) {
+ pm_runtime_suspend(hda_codec_dev(codec));
+ pm_runtime_disable(hda_codec_dev(codec));
+ }
+ pm_runtime_suspend(card->dev);
+ pm_runtime_disable(card->dev);
+ /* when we get suspended by vga_switcheroo we end up in D3cold,
+ * however we have no ACPI handle, so pci/acpi can't put us there,
+ * put ourselves there */
+ pci->current_state = PCI_D3cold;
+ chip->disabled = true;
+ if (snd_hda_lock_devices(&chip->bus))
+ dev_warn(chip->card->dev,
+ "Cannot lock devices!\n");
+ } else {
+ snd_hda_unlock_devices(&chip->bus);
+ chip->disabled = false;
+ pm_runtime_enable(card->dev);
+ list_for_each_codec(codec, &chip->bus) {
+ pm_runtime_enable(hda_codec_dev(codec));
+ pm_runtime_resume(hda_codec_dev(codec));
+ }
+ }
+ }
+}
+
+static bool azx_vs_can_switch(struct pci_dev *pci)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct azx *chip = card->private_data;
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+
+ wait_for_completion(&hda->probe_wait);
+ if (hda->init_failed)
+ return false;
+ if (chip->disabled || !hda->probe_continued)
+ return true;
+ if (snd_hda_lock_devices(&chip->bus))
+ return false;
+ snd_hda_unlock_devices(&chip->bus);
+ return true;
+}
+
+/*
+ * The discrete GPU cannot power down unless the HDA controller runtime
+ * suspends, so activate runtime PM on codecs even if power_save == 0.
+ */
+static void setup_vga_switcheroo_runtime_pm(struct azx *chip)
+{
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ struct hda_codec *codec;
+
+ if (hda->use_vga_switcheroo && !needs_eld_notify_link(chip)) {
+ list_for_each_codec(codec, &chip->bus)
+ codec->auto_runtime_pm = 1;
+ /* reset the power save setup */
+ if (chip->running)
+ set_default_power_save(chip);
+ }
+}
+
+static void azx_vs_gpu_bound(struct pci_dev *pci,
+ enum vga_switcheroo_client_id client_id)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct azx *chip = card->private_data;
+
+ if (client_id == VGA_SWITCHEROO_DIS)
+ chip->bus.keep_power = 0;
+ setup_vga_switcheroo_runtime_pm(chip);
+}
+
+static void init_vga_switcheroo(struct azx *chip)
+{
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ struct pci_dev *p = get_bound_vga(chip->pci);
+ struct pci_dev *parent;
+ if (p) {
+ dev_info(chip->card->dev,
+ "Handle vga_switcheroo audio client\n");
+ hda->use_vga_switcheroo = 1;
+
+ /* cleared in either gpu_bound op or codec probe, or when its
+ * upstream port has _PR3 (i.e. dGPU).
+ */
+ parent = pci_upstream_bridge(p);
+ chip->bus.keep_power = parent ? !pci_pr3_present(parent) : 1;
+ chip->driver_caps |= AZX_DCAPS_PM_RUNTIME;
+ pci_dev_put(p);
+ }
+}
+
+static const struct vga_switcheroo_client_ops azx_vs_ops = {
+ .set_gpu_state = azx_vs_set_state,
+ .can_switch = azx_vs_can_switch,
+ .gpu_bound = azx_vs_gpu_bound,
+};
+
+static int register_vga_switcheroo(struct azx *chip)
+{
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ struct pci_dev *p;
+ int err;
+
+ if (!hda->use_vga_switcheroo)
+ return 0;
+
+ p = get_bound_vga(chip->pci);
+ err = vga_switcheroo_register_audio_client(chip->pci, &azx_vs_ops, p);
+ pci_dev_put(p);
+
+ if (err < 0)
+ return err;
+ hda->vga_switcheroo_registered = 1;
+
+ return 0;
+}
+#else
+#define init_vga_switcheroo(chip) /* NOP */
+#define register_vga_switcheroo(chip) 0
+#define check_hdmi_disabled(pci) false
+#define setup_vga_switcheroo_runtime_pm(chip) /* NOP */
+#endif /* SUPPORT_VGA_SWITCHER */
+
+/*
+ * destructor
+ */
+static void azx_free(struct azx *chip)
+{
+ struct pci_dev *pci = chip->pci;
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ struct hdac_bus *bus = azx_bus(chip);
+
+ if (hda->freed)
+ return;
+
+ if (azx_has_pm_runtime(chip) && chip->running) {
+ pm_runtime_get_noresume(&pci->dev);
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_dont_use_autosuspend(&pci->dev);
+ }
+
+ chip->running = 0;
+
+ azx_del_card_list(chip);
+
+ hda->init_failed = 1; /* to be sure */
+ complete_all(&hda->probe_wait);
+
+ if (use_vga_switcheroo(hda)) {
+ if (chip->disabled && hda->probe_continued)
+ snd_hda_unlock_devices(&chip->bus);
+ if (hda->vga_switcheroo_registered) {
+ vga_switcheroo_unregister_client(chip->pci);
+
+ /* Some GPUs don't have sound, and azx_first_init fails,
+ * leaving the device probed but non-functional. As long
+ * as it's probed, the PCI subsystem keeps its runtime
+ * PM status as active. Force it to suspended (as we
+ * actually stop the chip) to allow GPU to suspend via
+ * vga_switcheroo, and print a warning.
+ */
+ dev_warn(&pci->dev, "GPU sound probed, but not operational: please add a quirk to driver_denylist\n");
+ pm_runtime_disable(&pci->dev);
+ pm_runtime_set_suspended(&pci->dev);
+ pm_runtime_enable(&pci->dev);
+ }
+ }
+
+ if (bus->chip_init) {
+ azx_clear_irq_pending(chip);
+ azx_stop_all_streams(chip);
+ azx_stop_chip(chip);
+ }
+
+ if (bus->irq >= 0)
+ free_irq(bus->irq, (void*)chip);
+
+ azx_free_stream_pages(chip);
+ azx_free_streams(chip);
+ snd_hdac_bus_exit(bus);
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+ release_firmware(chip->fw);
+#endif
+ display_power(chip, false);
+
+ if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT)
+ snd_hdac_i915_exit(bus);
+
+ hda->freed = 1;
+}
+
+static int azx_dev_disconnect(struct snd_device *device)
+{
+ struct azx *chip = device->device_data;
+ struct hdac_bus *bus = azx_bus(chip);
+
+ chip->bus.shutdown = 1;
+ cancel_work_sync(&bus->unsol_work);
+
+ return 0;
+}
+
+static int azx_dev_free(struct snd_device *device)
+{
+ azx_free(device->device_data);
+ return 0;
+}
+
+#ifdef SUPPORT_VGA_SWITCHEROO
+#ifdef CONFIG_ACPI
+/* ATPX is in the integrated GPU's namespace */
+static bool atpx_present(void)
+{
+ struct pci_dev *pdev = NULL;
+ acpi_handle dhandle, atpx_handle;
+ acpi_status status;
+
+ while ((pdev = pci_get_base_class(PCI_BASE_CLASS_DISPLAY, pdev))) {
+ if ((pdev->class != PCI_CLASS_DISPLAY_VGA << 8) &&
+ (pdev->class != PCI_CLASS_DISPLAY_OTHER << 8))
+ continue;
+
+ dhandle = ACPI_HANDLE(&pdev->dev);
+ if (dhandle) {
+ status = acpi_get_handle(dhandle, "ATPX", &atpx_handle);
+ if (ACPI_SUCCESS(status)) {
+ pci_dev_put(pdev);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+#else
+static bool atpx_present(void)
+{
+ return false;
+}
+#endif
+
+/*
+ * Check of disabled HDMI controller by vga_switcheroo
+ */
+static struct pci_dev *get_bound_vga(struct pci_dev *pci)
+{
+ struct pci_dev *p;
+
+ /* check only discrete GPU */
+ switch (pci->vendor) {
+ case PCI_VENDOR_ID_ATI:
+ case PCI_VENDOR_ID_AMD:
+ if (pci->devfn == 1) {
+ p = pci_get_domain_bus_and_slot(pci_domain_nr(pci->bus),
+ pci->bus->number, 0);
+ if (p) {
+ /* ATPX is in the integrated GPU's ACPI namespace
+ * rather than the dGPU's namespace. However,
+ * the dGPU is the one who is involved in
+ * vgaswitcheroo.
+ */
+ if (pci_is_display(p) &&
+ (atpx_present() || apple_gmux_detect(NULL, NULL)))
+ return p;
+ pci_dev_put(p);
+ }
+ }
+ break;
+ case PCI_VENDOR_ID_NVIDIA:
+ if (pci->devfn == 1) {
+ p = pci_get_domain_bus_and_slot(pci_domain_nr(pci->bus),
+ pci->bus->number, 0);
+ if (p) {
+ if (pci_is_display(p))
+ return p;
+ pci_dev_put(p);
+ }
+ }
+ break;
+ }
+ return NULL;
+}
+
+static bool check_hdmi_disabled(struct pci_dev *pci)
+{
+ bool vga_inactive = false;
+ struct pci_dev *p = get_bound_vga(pci);
+
+ if (p) {
+ if (vga_switcheroo_get_client_state(p) == VGA_SWITCHEROO_OFF)
+ vga_inactive = true;
+ pci_dev_put(p);
+ }
+ return vga_inactive;
+}
+#endif /* SUPPORT_VGA_SWITCHEROO */
+
+/*
+ * allow/deny-listing for position_fix
+ */
+static const struct snd_pci_quirk position_fix_list[] = {
+ SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS M2V", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x104d, 0x9069, "Sony VPCS11V9E", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x10de, 0xcb89, "Macbook Pro 7,1", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x1297, 0x3166, "Shuttle", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x1458, 0xa022, "ga-ma770-ud3", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x1565, 0x8218, "Biostar Microtech", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x1849, 0x0888, "775Dual-VSTA", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x8086, 0x2503, "DG965OT AAD63733-203", POS_FIX_LPIB),
+ {}
+};
+
+static int check_position_fix(struct azx *chip, int fix)
+{
+ const struct snd_pci_quirk *q;
+
+ switch (fix) {
+ case POS_FIX_AUTO:
+ case POS_FIX_LPIB:
+ case POS_FIX_POSBUF:
+ case POS_FIX_VIACOMBO:
+ case POS_FIX_COMBO:
+ case POS_FIX_SKL:
+ case POS_FIX_FIFO:
+ return fix;
+ }
+
+ q = snd_pci_quirk_lookup(chip->pci, position_fix_list);
+ if (q) {
+ dev_info(chip->card->dev,
+ "position_fix set to %d for device %04x:%04x\n",
+ q->value, q->subvendor, q->subdevice);
+ return q->value;
+ }
+
+ /* Check VIA/ATI HD Audio Controller exist */
+ if (chip->driver_type == AZX_DRIVER_VIA) {
+ dev_dbg(chip->card->dev, "Using VIACOMBO position fix\n");
+ return POS_FIX_VIACOMBO;
+ }
+ if (chip->driver_caps & AZX_DCAPS_AMD_WORKAROUND) {
+ dev_dbg(chip->card->dev, "Using FIFO position fix\n");
+ return POS_FIX_FIFO;
+ }
+ if (chip->driver_caps & AZX_DCAPS_POSFIX_LPIB) {
+ dev_dbg(chip->card->dev, "Using LPIB position fix\n");
+ return POS_FIX_LPIB;
+ }
+ if (chip->driver_type == AZX_DRIVER_SKL) {
+ dev_dbg(chip->card->dev, "Using SKL position fix\n");
+ return POS_FIX_SKL;
+ }
+ return POS_FIX_AUTO;
+}
+
+static void assign_position_fix(struct azx *chip, int fix)
+{
+ static const azx_get_pos_callback_t callbacks[] = {
+ [POS_FIX_AUTO] = NULL,
+ [POS_FIX_LPIB] = azx_get_pos_lpib,
+ [POS_FIX_POSBUF] = azx_get_pos_posbuf,
+ [POS_FIX_VIACOMBO] = azx_via_get_position,
+ [POS_FIX_COMBO] = azx_get_pos_lpib,
+ [POS_FIX_SKL] = azx_get_pos_posbuf,
+ [POS_FIX_FIFO] = azx_get_pos_fifo,
+ };
+
+ chip->get_position[0] = chip->get_position[1] = callbacks[fix];
+
+ /* combo mode uses LPIB only for playback */
+ if (fix == POS_FIX_COMBO)
+ chip->get_position[1] = NULL;
+
+ if ((fix == POS_FIX_POSBUF || fix == POS_FIX_SKL) &&
+ (chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)) {
+ chip->get_delay[0] = chip->get_delay[1] =
+ azx_get_delay_from_lpib;
+ }
+
+ if (fix == POS_FIX_FIFO)
+ chip->get_delay[0] = chip->get_delay[1] =
+ azx_get_delay_from_fifo;
+}
+
+/*
+ * deny-lists for probe_mask
+ */
+static const struct snd_pci_quirk probe_mask_list[] = {
+ /* Thinkpad often breaks the controller communication when accessing
+ * to the non-working (or non-existing) modem codec slot.
+ */
+ SND_PCI_QUIRK(0x1014, 0x05b7, "Thinkpad Z60", 0x01),
+ SND_PCI_QUIRK(0x17aa, 0x2010, "Thinkpad X/T/R60", 0x01),
+ SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X/T/R61", 0x01),
+ /* broken BIOS */
+ SND_PCI_QUIRK(0x1028, 0x20ac, "Dell Studio Desktop", 0x01),
+ /* including bogus ALC268 in slot#2 that conflicts with ALC888 */
+ SND_PCI_QUIRK(0x17c0, 0x4085, "Medion MD96630", 0x01),
+ /* forced codec slots */
+ SND_PCI_QUIRK(0x1043, 0x1262, "ASUS W5Fm", 0x103),
+ SND_PCI_QUIRK(0x1046, 0x1262, "ASUS W5F", 0x103),
+ SND_PCI_QUIRK(0x1558, 0x0351, "Schenker Dock 15", 0x105),
+ /* WinFast VP200 H (Teradici) user reported broken communication */
+ SND_PCI_QUIRK(0x3a21, 0x040d, "WinFast VP200 H", 0x101),
+ {}
+};
+
+#define AZX_FORCE_CODEC_MASK 0x100
+
+static void check_probe_mask(struct azx *chip, int dev)
+{
+ const struct snd_pci_quirk *q;
+
+ chip->codec_probe_mask = probe_mask[dev];
+ if (chip->codec_probe_mask == -1) {
+ q = snd_pci_quirk_lookup(chip->pci, probe_mask_list);
+ if (q) {
+ dev_info(chip->card->dev,
+ "probe_mask set to 0x%x for device %04x:%04x\n",
+ q->value, q->subvendor, q->subdevice);
+ chip->codec_probe_mask = q->value;
+ }
+ }
+
+ /* check forced option */
+ if (chip->codec_probe_mask != -1 &&
+ (chip->codec_probe_mask & AZX_FORCE_CODEC_MASK)) {
+ azx_bus(chip)->codec_mask = chip->codec_probe_mask & 0xff;
+ dev_info(chip->card->dev, "codec_mask forced to 0x%x\n",
+ (int)azx_bus(chip)->codec_mask);
+ }
+}
+
+/*
+ * allow/deny-list for enable_msi
+ */
+static const struct snd_pci_quirk msi_deny_list[] = {
+ SND_PCI_QUIRK(0x103c, 0x2191, "HP", 0), /* AMD Hudson */
+ SND_PCI_QUIRK(0x103c, 0x2192, "HP", 0), /* AMD Hudson */
+ SND_PCI_QUIRK(0x103c, 0x21f7, "HP", 0), /* AMD Hudson */
+ SND_PCI_QUIRK(0x103c, 0x21fa, "HP", 0), /* AMD Hudson */
+ SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */
+ SND_PCI_QUIRK(0x1043, 0x81f6, "ASUS", 0), /* nvidia */
+ SND_PCI_QUIRK(0x1043, 0x822d, "ASUS", 0), /* Athlon64 X2 + nvidia MCP55 */
+ SND_PCI_QUIRK(0x1179, 0xfb44, "Toshiba Satellite C870", 0), /* AMD Hudson */
+ SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */
+ SND_PCI_QUIRK(0xa0a0, 0x0575, "Aopen MZ915-M", 0), /* ICH6 */
+ {}
+};
+
+static void check_msi(struct azx *chip)
+{
+ const struct snd_pci_quirk *q;
+
+ if (enable_msi >= 0) {
+ chip->msi = !!enable_msi;
+ return;
+ }
+ chip->msi = 1; /* enable MSI as default */
+ q = snd_pci_quirk_lookup(chip->pci, msi_deny_list);
+ if (q) {
+ dev_info(chip->card->dev,
+ "msi for device %04x:%04x set to %d\n",
+ q->subvendor, q->subdevice, q->value);
+ chip->msi = q->value;
+ return;
+ }
+
+ /* NVidia chipsets seem to cause troubles with MSI */
+ if (chip->driver_caps & AZX_DCAPS_NO_MSI) {
+ dev_info(chip->card->dev, "Disabling MSI\n");
+ chip->msi = 0;
+ }
+}
+
+/* check the snoop mode availability */
+static void azx_check_snoop_available(struct azx *chip)
+{
+ int snoop = hda_snoop;
+
+ if (snoop >= 0) {
+ dev_info(chip->card->dev, "Force to %s mode by module option\n",
+ snoop ? "snoop" : "non-snoop");
+ chip->snoop = snoop;
+ chip->uc_buffer = !snoop;
+ return;
+ }
+
+ snoop = true;
+ if (azx_get_snoop_type(chip) == AZX_SNOOP_TYPE_NONE &&
+ chip->driver_type == AZX_DRIVER_VIA) {
+ /* force to non-snoop mode for a new VIA controller
+ * when BIOS is set
+ */
+ u8 val;
+ pci_read_config_byte(chip->pci, 0x42, &val);
+ if (!(val & 0x80) && (chip->pci->revision == 0x30 ||
+ chip->pci->revision == 0x20))
+ snoop = false;
+ }
+
+ if (chip->driver_caps & AZX_DCAPS_SNOOP_OFF)
+ snoop = false;
+
+ chip->snoop = snoop;
+ if (!snoop) {
+ dev_info(chip->card->dev, "Force to non-snoop mode\n");
+ /* C-Media requires non-cached pages only for CORB/RIRB */
+ if (chip->driver_type != AZX_DRIVER_CMEDIA)
+ chip->uc_buffer = true;
+ }
+}
+
+static void azx_probe_work(struct work_struct *work)
+{
+ struct hda_intel *hda = container_of(work, struct hda_intel, probe_work.work);
+ azx_probe_continue(&hda->chip);
+}
+
+static int default_bdl_pos_adj(struct azx *chip)
+{
+ /* some exceptions: Atoms seem problematic with value 1 */
+ if (chip->pci->vendor == PCI_VENDOR_ID_INTEL) {
+ switch (chip->pci->device) {
+ case PCI_DEVICE_ID_INTEL_HDA_BYT:
+ case PCI_DEVICE_ID_INTEL_HDA_BSW:
+ return 32;
+ case PCI_DEVICE_ID_INTEL_HDA_APL:
+ return 64;
+ }
+ }
+
+ switch (chip->driver_type) {
+ /*
+ * increase the bdl size for Glenfly Gpus for hardware
+ * limitation on hdac interrupt interval
+ */
+ case AZX_DRIVER_GFHDMI:
+ return 128;
+ case AZX_DRIVER_ICH:
+ case AZX_DRIVER_PCH:
+ return 1;
+ case AZX_DRIVER_ZHAOXINHDMI:
+ return 128;
+ default:
+ return 32;
+ }
+}
+
+/*
+ * constructor
+ */
+static const struct hda_controller_ops pci_hda_ops;
+
+static int azx_create(struct snd_card *card, struct pci_dev *pci,
+ int dev, unsigned int driver_caps,
+ struct azx **rchip)
+{
+ static const struct snd_device_ops ops = {
+ .dev_disconnect = azx_dev_disconnect,
+ .dev_free = azx_dev_free,
+ };
+ struct hda_intel *hda;
+ struct azx *chip;
+ int err;
+
+ *rchip = NULL;
+
+ err = pcim_enable_device(pci);
+ if (err < 0)
+ return err;
+
+ hda = devm_kzalloc(&pci->dev, sizeof(*hda), GFP_KERNEL);
+ if (!hda)
+ return -ENOMEM;
+
+ chip = &hda->chip;
+ mutex_init(&chip->open_mutex);
+ chip->card = card;
+ chip->pci = pci;
+ chip->ops = &pci_hda_ops;
+ chip->driver_caps = driver_caps;
+ chip->driver_type = driver_caps & 0xff;
+ check_msi(chip);
+ chip->dev_index = dev;
+ if (jackpoll_ms[dev] >= 50 && jackpoll_ms[dev] <= 60000)
+ chip->jackpoll_interval = msecs_to_jiffies(jackpoll_ms[dev]);
+ INIT_LIST_HEAD(&chip->pcm_list);
+ INIT_WORK(&hda->irq_pending_work, azx_irq_pending_work);
+ INIT_LIST_HEAD(&hda->list);
+ init_vga_switcheroo(chip);
+ init_completion(&hda->probe_wait);
+
+ assign_position_fix(chip, check_position_fix(chip, position_fix[dev]));
+
+ if (single_cmd < 0) /* allow fallback to single_cmd at errors */
+ chip->fallback_to_single_cmd = 1;
+ else /* explicitly set to single_cmd or not */
+ chip->single_cmd = single_cmd;
+
+ azx_check_snoop_available(chip);
+
+ if (bdl_pos_adj[dev] < 0)
+ chip->bdl_pos_adj = default_bdl_pos_adj(chip);
+ else
+ chip->bdl_pos_adj = bdl_pos_adj[dev];
+
+ err = azx_bus_init(chip, model[dev]);
+ if (err < 0)
+ return err;
+
+ /* use the non-cached pages in non-snoop mode */
+ if (!azx_snoop(chip))
+ azx_bus(chip)->dma_type = SNDRV_DMA_TYPE_DEV_WC;
+
+ if (chip->driver_type == AZX_DRIVER_NVIDIA) {
+ dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n");
+ chip->bus.core.needs_damn_long_delay = 1;
+ }
+
+ check_probe_mask(chip, dev);
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ dev_err(card->dev, "Error creating device [card]!\n");
+ azx_free(chip);
+ return err;
+ }
+
+ /* continue probing in work context as may trigger request module */
+ INIT_DELAYED_WORK(&hda->probe_work, azx_probe_work);
+
+ *rchip = chip;
+
+ return 0;
+}
+
+static int azx_first_init(struct azx *chip)
+{
+ int dev = chip->dev_index;
+ struct pci_dev *pci = chip->pci;
+ struct snd_card *card = chip->card;
+ struct hdac_bus *bus = azx_bus(chip);
+ int err;
+ unsigned short gcap;
+ unsigned int dma_bits = 64;
+
+#if BITS_PER_LONG != 64
+ /* Fix up base address on ULI M5461 */
+ if (chip->driver_type == AZX_DRIVER_ULI) {
+ u16 tmp3;
+ pci_read_config_word(pci, 0x40, &tmp3);
+ pci_write_config_word(pci, 0x40, tmp3 | 0x10);
+ pci_write_config_dword(pci, PCI_BASE_ADDRESS_1, 0);
+ }
+#endif
+ /*
+ * Fix response write request not synced to memory when handle
+ * hdac interrupt on Glenfly Gpus
+ */
+ if (chip->driver_type == AZX_DRIVER_GFHDMI)
+ bus->polling_mode = 1;
+
+ if (chip->driver_type == AZX_DRIVER_LOONGSON) {
+ bus->polling_mode = 1;
+ bus->not_use_interrupts = 1;
+ bus->access_sdnctl_in_dword = 1;
+ if (!chip->jackpoll_interval)
+ chip->jackpoll_interval = msecs_to_jiffies(1500);
+ }
+
+ if (chip->driver_type == AZX_DRIVER_ZHAOXINHDMI)
+ bus->polling_mode = 1;
+
+ bus->remap_addr = pcim_iomap_region(pci, 0, "ICH HD audio");
+ if (IS_ERR(bus->remap_addr))
+ return PTR_ERR(bus->remap_addr);
+
+ bus->addr = pci_resource_start(pci, 0);
+
+ if (chip->driver_type == AZX_DRIVER_SKL)
+ snd_hdac_bus_parse_capabilities(bus);
+
+ /*
+ * Some Intel CPUs has always running timer (ART) feature and
+ * controller may have Global time sync reporting capability, so
+ * check both of these before declaring synchronized time reporting
+ * capability SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME
+ */
+ chip->gts_present = false;
+
+#ifdef CONFIG_X86
+ if (bus->ppcap && boot_cpu_has(X86_FEATURE_ART))
+ chip->gts_present = true;
+#endif
+
+ if (chip->msi && chip->driver_caps & AZX_DCAPS_NO_MSI64) {
+ dev_dbg(card->dev, "Disabling 64bit MSI\n");
+ pci->no_64bit_msi = true;
+ }
+
+ pci_set_master(pci);
+
+ gcap = azx_readw(chip, GCAP);
+ dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
+
+ /* AMD devices support 40 or 48bit DMA, take the safe one */
+ if (chip->pci->vendor == PCI_VENDOR_ID_AMD)
+ dma_bits = 40;
+
+ /* disable SB600 64bit support for safety */
+ if (chip->pci->vendor == PCI_VENDOR_ID_ATI) {
+ struct pci_dev *p_smbus;
+ dma_bits = 40;
+ p_smbus = pci_get_device(PCI_VENDOR_ID_ATI,
+ PCI_DEVICE_ID_ATI_SBX00_SMBUS,
+ NULL);
+ if (p_smbus) {
+ if (p_smbus->revision < 0x30)
+ gcap &= ~AZX_GCAP_64OK;
+ pci_dev_put(p_smbus);
+ }
+ }
+
+ /* NVidia hardware normally only supports up to 40 bits of DMA */
+ if (chip->pci->vendor == PCI_VENDOR_ID_NVIDIA)
+ dma_bits = 40;
+
+ /* disable 64bit DMA address on some devices */
+ if (chip->driver_caps & AZX_DCAPS_NO_64BIT) {
+ dev_dbg(card->dev, "Disabling 64bit DMA\n");
+ gcap &= ~AZX_GCAP_64OK;
+ }
+
+ /* disable buffer size rounding to 128-byte multiples if supported */
+ if (align_buffer_size >= 0)
+ chip->align_buffer_size = !!align_buffer_size;
+ else {
+ if (chip->driver_caps & AZX_DCAPS_NO_ALIGN_BUFSIZE)
+ chip->align_buffer_size = 0;
+ else
+ chip->align_buffer_size = 1;
+ }
+
+ /* allow 64bit DMA address if supported by H/W */
+ if (!(gcap & AZX_GCAP_64OK))
+ dma_bits = 32;
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(dma_bits)))
+ dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32));
+ dma_set_max_seg_size(&pci->dev, UINT_MAX);
+
+ /* read number of streams from GCAP register instead of using
+ * hardcoded value
+ */
+ chip->capture_streams = (gcap >> 8) & 0x0f;
+ chip->playback_streams = (gcap >> 12) & 0x0f;
+ if (!chip->playback_streams && !chip->capture_streams) {
+ /* gcap didn't give any info, switching to old method */
+
+ switch (chip->driver_type) {
+ case AZX_DRIVER_ULI:
+ chip->playback_streams = ULI_NUM_PLAYBACK;
+ chip->capture_streams = ULI_NUM_CAPTURE;
+ break;
+ case AZX_DRIVER_ATIHDMI:
+ case AZX_DRIVER_ATIHDMI_NS:
+ chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
+ chip->capture_streams = ATIHDMI_NUM_CAPTURE;
+ break;
+ case AZX_DRIVER_GFHDMI:
+ case AZX_DRIVER_ZHAOXINHDMI:
+ case AZX_DRIVER_GENERIC:
+ default:
+ chip->playback_streams = ICH6_NUM_PLAYBACK;
+ chip->capture_streams = ICH6_NUM_CAPTURE;
+ break;
+ }
+ }
+ chip->capture_index_offset = 0;
+ chip->playback_index_offset = chip->capture_streams;
+ chip->num_streams = chip->playback_streams + chip->capture_streams;
+
+ /* sanity check for the SDxCTL.STRM field overflow */
+ if (chip->num_streams > 15 &&
+ (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG) == 0) {
+ dev_warn(chip->card->dev, "number of I/O streams is %d, "
+ "forcing separate stream tags", chip->num_streams);
+ chip->driver_caps |= AZX_DCAPS_SEPARATE_STREAM_TAG;
+ }
+
+ /* initialize streams */
+ err = azx_init_streams(chip);
+ if (err < 0)
+ return err;
+
+ err = azx_alloc_stream_pages(chip);
+ if (err < 0)
+ return err;
+
+ /* initialize chip */
+ azx_init_pci(chip);
+
+ snd_hdac_i915_set_bclk(bus);
+
+ hda_intel_init_chip(chip, (probe_only[dev] & 2) == 0);
+
+ /* codec detection */
+ if (!azx_bus(chip)->codec_mask) {
+ dev_err(card->dev, "no codecs found!\n");
+ /* keep running the rest for the runtime PM */
+ }
+
+ if (azx_acquire_irq(chip, 0) < 0)
+ return -EBUSY;
+
+ strscpy(card->driver, "HDA-Intel");
+ strscpy(card->shortname, driver_short_names[chip->driver_type],
+ sizeof(card->shortname));
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx irq %i",
+ card->shortname, bus->addr, bus->irq);
+
+ return 0;
+}
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+/* callback from request_firmware_nowait() */
+static void azx_firmware_cb(const struct firmware *fw, void *context)
+{
+ struct snd_card *card = context;
+ struct azx *chip = card->private_data;
+
+ if (fw)
+ chip->fw = fw;
+ else
+ dev_err(card->dev, "Cannot load firmware, continue without patching\n");
+ if (!chip->disabled) {
+ /* continue probing */
+ azx_probe_continue(chip);
+ }
+}
+#endif
+
+static int disable_msi_reset_irq(struct azx *chip)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ int err;
+
+ free_irq(bus->irq, chip);
+ bus->irq = -1;
+ chip->card->sync_irq = -1;
+ pci_free_irq_vectors(chip->pci);
+ chip->msi = 0;
+ err = azx_acquire_irq(chip, 1);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/* Denylist for skipping the whole probe:
+ * some HD-audio PCI entries are exposed without any codecs, and such devices
+ * should be ignored from the beginning.
+ */
+static const struct pci_device_id driver_denylist[] = {
+ { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1043, 0x874f) }, /* ASUS ROG Zenith II / Strix */
+ { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1462, 0xcb59) }, /* MSI TRX40 Creator */
+ { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1462, 0xcb60) }, /* MSI TRX40 */
+ { PCI_DEVICE_SUB(0x1022, 0x15e3, 0x1462, 0xee59) }, /* MSI X870E Tomahawk WiFi */
+ {}
+};
+
+static struct pci_device_id driver_denylist_ideapad_z570[] = {
+ { PCI_DEVICE_SUB(0x10de, 0x0bea, 0x0000, 0x0000) }, /* NVIDIA GF108 HDA */
+ {}
+};
+
+/* DMI-based denylist, to be used when:
+ * - PCI subsystem IDs are zero, impossible to distinguish from valid sound cards.
+ * - Different modifications of the same laptop use different GPU models.
+ */
+static const struct dmi_system_id driver_denylist_dmi[] = {
+ {
+ /* No HDA in NVIDIA DGPU. BIOS disables it, but quirk_nvidia_hda() reenables. */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Ideapad Z570"),
+ },
+ .driver_data = &driver_denylist_ideapad_z570,
+ },
+ {}
+};
+
+static const struct hda_controller_ops pci_hda_ops = {
+ .disable_msi_reset_irq = disable_msi_reset_irq,
+ .position_check = azx_position_check,
+};
+
+static DECLARE_BITMAP(probed_devs, SNDRV_CARDS);
+
+static int azx_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ const struct dmi_system_id *dmi;
+ struct snd_card *card;
+ struct hda_intel *hda;
+ struct azx *chip;
+ bool schedule_probe;
+ int dev;
+ int err;
+
+ if (pci_match_id(driver_denylist, pci)) {
+ dev_info(&pci->dev, "Skipping the device on the denylist\n");
+ return -ENODEV;
+ }
+
+ dmi = dmi_first_match(driver_denylist_dmi);
+ if (dmi && pci_match_id(dmi->driver_data, pci)) {
+ dev_info(&pci->dev, "Skipping the device on the DMI denylist\n");
+ return -ENODEV;
+ }
+
+ dev = find_first_zero_bit(probed_devs, SNDRV_CARDS);
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ set_bit(dev, probed_devs);
+ return -ENOENT;
+ }
+
+ /*
+ * stop probe if another Intel's DSP driver should be activated
+ */
+ if (dmic_detect) {
+ err = snd_intel_dsp_driver_probe(pci);
+ if (err != SND_INTEL_DSP_DRIVER_ANY && err != SND_INTEL_DSP_DRIVER_LEGACY) {
+ dev_dbg(&pci->dev, "HDAudio driver not selected, aborting probe\n");
+ return -ENODEV;
+ }
+ } else {
+ dev_warn(&pci->dev, "dmic_detect option is deprecated, pass snd-intel-dspcfg.dsp_driver=1 option instead\n");
+ }
+
+ err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
+ if (err < 0) {
+ dev_err(&pci->dev, "Error creating card!\n");
+ return err;
+ }
+
+ err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
+ if (err < 0)
+ goto out_free;
+ card->private_data = chip;
+ hda = container_of(chip, struct hda_intel, chip);
+
+ pci_set_drvdata(pci, card);
+
+#ifdef CONFIG_SND_HDA_I915
+ /* bind with i915 if needed */
+ if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT) {
+ err = snd_hdac_i915_init(azx_bus(chip));
+ if (err < 0) {
+ if (err == -EPROBE_DEFER)
+ goto out_free;
+
+ /* if the controller is bound only with HDMI/DP
+ * (for HSW and BDW), we need to abort the probe;
+ * for other chips, still continue probing as other
+ * codecs can be on the same link.
+ */
+ if (HDA_CONTROLLER_IN_GPU(pci)) {
+ dev_err_probe(card->dev, err,
+ "HSW/BDW HD-audio HDMI/DP requires binding with gfx driver\n");
+
+ goto out_free;
+ } else {
+ /* don't bother any longer */
+ chip->driver_caps &= ~AZX_DCAPS_I915_COMPONENT;
+ }
+ }
+
+ /* HSW/BDW controllers need this power */
+ if (HDA_CONTROLLER_IN_GPU(pci))
+ hda->need_i915_power = true;
+ }
+#else
+ if (HDA_CONTROLLER_IN_GPU(pci))
+ dev_err(card->dev, "Haswell/Broadwell HDMI/DP must build in CONFIG_SND_HDA_I915\n");
+#endif
+
+ err = register_vga_switcheroo(chip);
+ if (err < 0) {
+ dev_err(card->dev, "Error registering vga_switcheroo client\n");
+ goto out_free;
+ }
+
+ if (check_hdmi_disabled(pci)) {
+ dev_info(card->dev, "VGA controller is disabled\n");
+ dev_info(card->dev, "Delaying initialization\n");
+ chip->disabled = true;
+ }
+
+ schedule_probe = !chip->disabled;
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+ if (patch[dev] && *patch[dev]) {
+ dev_info(card->dev, "Applying patch firmware '%s'\n",
+ patch[dev]);
+ err = request_firmware_nowait(THIS_MODULE, true, patch[dev],
+ &pci->dev, GFP_KERNEL, card,
+ azx_firmware_cb);
+ if (err < 0)
+ goto out_free;
+ schedule_probe = false; /* continued in azx_firmware_cb() */
+ }
+#endif /* CONFIG_SND_HDA_PATCH_LOADER */
+
+ if (schedule_probe)
+ schedule_delayed_work(&hda->probe_work, 0);
+
+ set_bit(dev, probed_devs);
+ if (chip->disabled)
+ complete_all(&hda->probe_wait);
+ return 0;
+
+out_free:
+ pci_set_drvdata(pci, NULL);
+ snd_card_free(card);
+ return err;
+}
+
+/* On some boards setting power_save to a non 0 value leads to clicking /
+ * popping sounds when ever we enter/leave powersaving mode. Ideally we would
+ * figure out how to avoid these sounds, but that is not always feasible.
+ * So we keep a list of devices where we disable powersaving as its known
+ * to causes problems on these devices.
+ */
+static const struct snd_pci_quirk power_save_denylist[] = {
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ SND_PCI_QUIRK(0x1849, 0xc892, "Asrock B85M-ITX", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ SND_PCI_QUIRK(0x1849, 0x0397, "Asrock N68C-S UCC", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ SND_PCI_QUIRK(0x1849, 0x7662, "Asrock H81M-HDS", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ SND_PCI_QUIRK(0x1043, 0x8733, "Asus Prime X370-Pro", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ SND_PCI_QUIRK(0x1028, 0x0497, "Dell Precision T3600", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ /* Note the P55A-UD3 and Z87-D3HP share the subsys id for the HDA dev */
+ SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P55A-UD3 / Z87-D3HP", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
+ SND_PCI_QUIRK(0x8086, 0x2040, "Intel DZ77BH-55K", 0),
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=199607 */
+ SND_PCI_QUIRK(0x8086, 0x2057, "Intel NUC5i7RYB", 0),
+ /* https://bugs.launchpad.net/bugs/1821663 */
+ SND_PCI_QUIRK(0x8086, 0x2064, "Intel SDP 8086:2064", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1520902 */
+ SND_PCI_QUIRK(0x8086, 0x2068, "Intel NUC7i3BNB", 0),
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=198611 */
+ SND_PCI_QUIRK(0x17aa, 0x2227, "Lenovo X1 Carbon 3rd Gen", 0),
+ SND_PCI_QUIRK(0x17aa, 0x316e, "Lenovo ThinkCentre M70q", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1689623 */
+ SND_PCI_QUIRK(0x17aa, 0x367b, "Lenovo IdeaCentre B550", 0),
+ /* https://bugzilla.redhat.com/show_bug.cgi?id=1572975 */
+ SND_PCI_QUIRK(0x17aa, 0x36a7, "Lenovo C50 All in one", 0),
+ /* https://bugs.launchpad.net/bugs/1821663 */
+ SND_PCI_QUIRK(0x1631, 0xe017, "Packard Bell NEC IMEDIA 5204", 0),
+ /* KONTRON SinglePC may cause a stall at runtime resume */
+ SND_PCI_QUIRK(0x1734, 0x1232, "KONTRON SinglePC", 0),
+ /* Dell ALC3271 */
+ SND_PCI_QUIRK(0x1028, 0x0962, "Dell ALC3271", 0),
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=220210 */
+ SND_PCI_QUIRK(0x17aa, 0x5079, "Lenovo Thinkpad E15", 0),
+ {}
+};
+
+static void set_default_power_save(struct azx *chip)
+{
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ int val = power_save;
+
+ if (pm_blacklist < 0) {
+ const struct snd_pci_quirk *q;
+
+ q = snd_pci_quirk_lookup(chip->pci, power_save_denylist);
+ if (q && val) {
+ dev_info(chip->card->dev, "device %04x:%04x is on the power_save denylist, forcing power_save to 0\n",
+ q->subvendor, q->subdevice);
+ val = 0;
+ hda->runtime_pm_disabled = 1;
+ }
+ } else if (pm_blacklist > 0) {
+ dev_info(chip->card->dev, "Forcing power_save to 0 via option\n");
+ val = 0;
+ }
+ snd_hda_set_power_save(&chip->bus, val * 1000);
+}
+
+/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */
+static const unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = {
+ [AZX_DRIVER_NVIDIA] = 8,
+ [AZX_DRIVER_TERA] = 1,
+};
+
+static int azx_probe_continue(struct azx *chip)
+{
+ struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
+ struct hdac_bus *bus = azx_bus(chip);
+ struct pci_dev *pci = chip->pci;
+ int dev = chip->dev_index;
+ int err;
+
+ if (chip->disabled || hda->init_failed)
+ return -EIO;
+ if (hda->probe_retry)
+ goto probe_retry;
+
+ to_hda_bus(bus)->bus_probing = 1;
+ hda->probe_continued = 1;
+
+ /* Request display power well for the HDA controller or codec. For
+ * Haswell/Broadwell, both the display HDA controller and codec need
+ * this power. For other platforms, like Baytrail/Braswell, only the
+ * display codec needs the power and it can be released after probe.
+ */
+ display_power(chip, true);
+
+ err = azx_first_init(chip);
+ if (err < 0)
+ goto out_free;
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+ chip->beep_mode = beep_mode[dev];
+#endif
+
+ chip->ctl_dev_id = ctl_dev_id;
+
+ /* create codec instances */
+ if (bus->codec_mask) {
+ err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]);
+ if (err < 0)
+ goto out_free;
+ }
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+ if (chip->fw) {
+ err = snd_hda_load_patch(&chip->bus, chip->fw->size,
+ chip->fw->data);
+ if (err < 0)
+ goto out_free;
+ }
+#endif
+
+ probe_retry:
+ if (bus->codec_mask && !(probe_only[dev] & 1)) {
+ err = azx_codec_configure(chip);
+ if (err) {
+ if ((chip->driver_caps & AZX_DCAPS_RETRY_PROBE) &&
+ ++hda->probe_retry < 60) {
+ schedule_delayed_work(&hda->probe_work,
+ msecs_to_jiffies(1000));
+ return 0; /* keep things up */
+ }
+ dev_err(chip->card->dev, "Cannot probe codecs, giving up\n");
+ goto out_free;
+ }
+ }
+
+ err = snd_card_register(chip->card);
+ if (err < 0)
+ goto out_free;
+
+ setup_vga_switcheroo_runtime_pm(chip);
+
+ chip->running = 1;
+ azx_add_card_list(chip);
+
+ set_default_power_save(chip);
+
+ if (azx_has_pm_runtime(chip)) {
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+ pm_runtime_put_autosuspend(&pci->dev);
+ }
+
+out_free:
+ if (err < 0) {
+ pci_set_drvdata(pci, NULL);
+ snd_card_free(chip->card);
+ return err;
+ }
+
+ if (!hda->need_i915_power)
+ display_power(chip, false);
+ complete_all(&hda->probe_wait);
+ to_hda_bus(bus)->bus_probing = 0;
+ hda->probe_retry = 0;
+ return 0;
+}
+
+static void azx_remove(struct pci_dev *pci)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct azx *chip;
+ struct hda_intel *hda;
+
+ if (card) {
+ /* cancel the pending probing work */
+ chip = card->private_data;
+ hda = container_of(chip, struct hda_intel, chip);
+ /* FIXME: below is an ugly workaround.
+ * Both device_release_driver() and driver_probe_device()
+ * take *both* the device's and its parent's lock before
+ * calling the remove() and probe() callbacks. The codec
+ * probe takes the locks of both the codec itself and its
+ * parent, i.e. the PCI controller dev. Meanwhile, when
+ * the PCI controller is unbound, it takes its lock, too
+ * ==> ouch, a deadlock!
+ * As a workaround, we unlock temporarily here the controller
+ * device during cancel_work_sync() call.
+ */
+ device_unlock(&pci->dev);
+ cancel_delayed_work_sync(&hda->probe_work);
+ device_lock(&pci->dev);
+
+ clear_bit(chip->dev_index, probed_devs);
+ pci_set_drvdata(pci, NULL);
+ snd_card_free(card);
+ }
+}
+
+static void azx_shutdown(struct pci_dev *pci)
+{
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct azx *chip;
+
+ if (!card)
+ return;
+ chip = card->private_data;
+ if (chip && chip->running)
+ __azx_shutdown_chip(chip, true);
+}
+
+/* PCI IDs */
+static const struct pci_device_id azx_ids[] = {
+ /* CPT */
+ { PCI_DEVICE_DATA(INTEL, HDA_CPT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM) },
+ /* PBG */
+ { PCI_DEVICE_DATA(INTEL, HDA_PBG, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM) },
+ /* Panther Point */
+ { PCI_DEVICE_DATA(INTEL, HDA_PPT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM) },
+ /* Lynx Point */
+ { PCI_DEVICE_DATA(INTEL, HDA_LPT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) },
+ /* 9 Series */
+ { PCI_DEVICE_DATA(INTEL, HDA_9_SERIES, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) },
+ /* Wellsburg */
+ { PCI_DEVICE_DATA(INTEL, HDA_WBG_0, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) },
+ { PCI_DEVICE_DATA(INTEL, HDA_WBG_1, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) },
+ /* Lewisburg */
+ { PCI_DEVICE_DATA(INTEL, HDA_LBG_0, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_LBG_1, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Lynx Point-LP */
+ { PCI_DEVICE_DATA(INTEL, HDA_LPT_LP_0, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) },
+ /* Lynx Point-LP */
+ { PCI_DEVICE_DATA(INTEL, HDA_LPT_LP_1, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) },
+ /* Wildcat Point-LP */
+ { PCI_DEVICE_DATA(INTEL, HDA_WPT_LP, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) },
+ /* Skylake (Sunrise Point) */
+ { PCI_DEVICE_DATA(INTEL, HDA_SKL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Skylake-LP (Sunrise Point-LP) */
+ { PCI_DEVICE_DATA(INTEL, HDA_SKL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Kabylake */
+ { PCI_DEVICE_DATA(INTEL, HDA_KBL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Kabylake-LP */
+ { PCI_DEVICE_DATA(INTEL, HDA_KBL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Kabylake-H */
+ { PCI_DEVICE_DATA(INTEL, HDA_KBL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Coffelake */
+ { PCI_DEVICE_DATA(INTEL, HDA_CNL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Cannonlake */
+ { PCI_DEVICE_DATA(INTEL, HDA_CNL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* CometLake-LP */
+ { PCI_DEVICE_DATA(INTEL, HDA_CML_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* CometLake-H */
+ { PCI_DEVICE_DATA(INTEL, HDA_CML_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RKL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* CometLake-S */
+ { PCI_DEVICE_DATA(INTEL, HDA_CML_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* CometLake-R */
+ { PCI_DEVICE_DATA(INTEL, HDA_CML_R, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Icelake */
+ { PCI_DEVICE_DATA(INTEL, HDA_ICL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Icelake-H */
+ { PCI_DEVICE_DATA(INTEL, HDA_ICL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Jasperlake */
+ { PCI_DEVICE_DATA(INTEL, HDA_ICL_N, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_JSL_N, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Tigerlake */
+ { PCI_DEVICE_DATA(INTEL, HDA_TGL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Tigerlake-H */
+ { PCI_DEVICE_DATA(INTEL, HDA_TGL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* DG1 */
+ { PCI_DEVICE_DATA(INTEL, HDA_DG1, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* DG2 */
+ { PCI_DEVICE_DATA(INTEL, HDA_DG2_0, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_DG2_1, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_DG2_2, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Alderlake-S */
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Alderlake-P */
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_P, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_PS, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_PX, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Alderlake-M */
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_M, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Alderlake-N */
+ { PCI_DEVICE_DATA(INTEL, HDA_ADL_N, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Elkhart Lake */
+ { PCI_DEVICE_DATA(INTEL, HDA_EHL_0, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_EHL_3, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Raptor Lake */
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_0, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_1, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_M, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_RPL_PX, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ { PCI_DEVICE_DATA(INTEL, HDA_MTL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Battlemage */
+ { PCI_DEVICE_DATA(INTEL, HDA_BMG, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Lunarlake-P */
+ { PCI_DEVICE_DATA(INTEL, HDA_LNL_P, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
+ /* Arrow Lake-S */
+ { PCI_DEVICE_DATA(INTEL, HDA_ARL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Arrow Lake */
+ { PCI_DEVICE_DATA(INTEL, HDA_ARL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
+ /* Panther Lake */
+ { PCI_DEVICE_DATA(INTEL, HDA_PTL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
+ /* Panther Lake-H */
+ { PCI_DEVICE_DATA(INTEL, HDA_PTL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
+ /* Wildcat Lake */
+ { PCI_DEVICE_DATA(INTEL, HDA_WCL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
+ /* Nova Lake */
+ { PCI_DEVICE_DATA(INTEL, HDA_NVL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
+ /* Apollolake (Broxton-P) */
+ { PCI_DEVICE_DATA(INTEL, HDA_APL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) },
+ /* Gemini-Lake */
+ { PCI_DEVICE_DATA(INTEL, HDA_GML, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) },
+ /* Haswell */
+ { PCI_DEVICE_DATA(INTEL, HDA_HSW_0, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) },
+ { PCI_DEVICE_DATA(INTEL, HDA_HSW_2, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) },
+ { PCI_DEVICE_DATA(INTEL, HDA_HSW_3, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) },
+ /* Broadwell */
+ { PCI_DEVICE_DATA(INTEL, HDA_BDW, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_BROADWELL) },
+ /* 5 Series/3400 */
+ { PCI_DEVICE_DATA(INTEL, HDA_5_3400_SERIES_0, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM) },
+ { PCI_DEVICE_DATA(INTEL, HDA_5_3400_SERIES_1, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM) },
+ /* Poulsbo */
+ { PCI_DEVICE_DATA(INTEL, HDA_POULSBO, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_BASE |
+ AZX_DCAPS_POSFIX_LPIB) },
+ /* Oaktrail */
+ { PCI_DEVICE_DATA(INTEL, HDA_OAKTRAIL, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_BASE) },
+ /* BayTrail */
+ { PCI_DEVICE_DATA(INTEL, HDA_BYT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BAYTRAIL) },
+ /* Braswell */
+ { PCI_DEVICE_DATA(INTEL, HDA_BSW, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BRASWELL) },
+ /* ICH6 */
+ { PCI_DEVICE_DATA(INTEL, HDA_ICH6, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
+ /* ICH7 */
+ { PCI_DEVICE_DATA(INTEL, HDA_ICH7, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
+ /* ESB2 */
+ { PCI_DEVICE_DATA(INTEL, HDA_ESB2, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
+ /* ICH8 */
+ { PCI_DEVICE_DATA(INTEL, HDA_ICH8, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
+ /* ICH9 */
+ { PCI_DEVICE_DATA(INTEL, HDA_ICH9_0, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
+ /* ICH9 */
+ { PCI_DEVICE_DATA(INTEL, HDA_ICH9_1, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
+ /* ICH10 */
+ { PCI_DEVICE_DATA(INTEL, HDA_ICH10_0, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
+ /* ICH10 */
+ { PCI_DEVICE_DATA(INTEL, HDA_ICH10_1, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
+ /* Generic Intel */
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID),
+ .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+ .class_mask = 0xffffff,
+ .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_NO_ALIGN_BUFSIZE },
+ /* ATI SB 450/600/700/800/900 */
+ { PCI_VDEVICE(ATI, 0x437b),
+ .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB },
+ { PCI_VDEVICE(ATI, 0x4383),
+ .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB },
+ /* AMD Hudson */
+ { PCI_VDEVICE(AMD, 0x780d),
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB },
+ /* AMD, X370 & co */
+ { PCI_VDEVICE(AMD, 0x1457),
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB },
+ /* AMD, X570 & co */
+ { PCI_VDEVICE(AMD, 0x1487),
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB },
+ /* AMD Stoney */
+ { PCI_VDEVICE(AMD, 0x157a),
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB |
+ AZX_DCAPS_PM_RUNTIME },
+ /* AMD Raven */
+ { PCI_VDEVICE(AMD, 0x15e3),
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB },
+ /* ATI HDMI */
+ { PCI_VDEVICE(ATI, 0x0002),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0x1308),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_VDEVICE(ATI, 0x157a),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_VDEVICE(ATI, 0x15b3),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_VDEVICE(ATI, 0x793b),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0x7919),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0x960f),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0x970f),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0x9840),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_VDEVICE(ATI, 0xaa00),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0xaa08),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0xaa10),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0xaa18),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0xaa20),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0xaa28),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0xaa30),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0xaa38),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0xaa40),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0xaa48),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0xaa50),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0xaa58),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0xaa60),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0xaa68),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0xaa80),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0xaa88),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0xaa90),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0xaa98),
+ .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_VDEVICE(ATI, 0x9902),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_VDEVICE(ATI, 0xaaa0),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_VDEVICE(ATI, 0xaaa8),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_VDEVICE(ATI, 0xaab0),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_VDEVICE(ATI, 0xaac0),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xaac8),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xaad8),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xaae0),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xaae8),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xaaf0),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xaaf8),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xab00),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xab08),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xab10),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xab18),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xab20),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xab28),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xab30),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xab38),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ { PCI_VDEVICE(ATI, 0xab40),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+ AZX_DCAPS_PM_RUNTIME },
+ /* GLENFLY */
+ { PCI_DEVICE(PCI_VENDOR_ID_GLENFLY, PCI_ANY_ID),
+ .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+ .class_mask = 0xffffff,
+ .driver_data = AZX_DRIVER_GFHDMI | AZX_DCAPS_POSFIX_LPIB |
+ AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT },
+ /* VIA VT8251/VT8237A */
+ { PCI_VDEVICE(VIA, 0x3288), .driver_data = AZX_DRIVER_VIA },
+ /* VIA GFX VT7122/VX900 */
+ { PCI_VDEVICE(VIA, 0x9170), .driver_data = AZX_DRIVER_GENERIC },
+ /* VIA GFX VT6122/VX11 */
+ { PCI_VDEVICE(VIA, 0x9140), .driver_data = AZX_DRIVER_GENERIC },
+ /* SIS966 */
+ { PCI_VDEVICE(SI, 0x7502), .driver_data = AZX_DRIVER_SIS },
+ /* ULI M5461 */
+ { PCI_VDEVICE(AL, 0x5461), .driver_data = AZX_DRIVER_ULI },
+ /* NVIDIA MCP */
+ { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),
+ .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+ .class_mask = 0xffffff,
+ .driver_data = AZX_DRIVER_NVIDIA | AZX_DCAPS_PRESET_NVIDIA },
+ /* Teradici */
+ { PCI_DEVICE(0x6549, 0x1200),
+ .driver_data = AZX_DRIVER_TERA | AZX_DCAPS_NO_64BIT },
+ { PCI_DEVICE(0x6549, 0x2200),
+ .driver_data = AZX_DRIVER_TERA | AZX_DCAPS_NO_64BIT },
+ /* Creative X-Fi (CA0110-IBG) */
+ /* CTHDA chips */
+ { PCI_VDEVICE(CREATIVE, 0x0010),
+ .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA },
+ { PCI_VDEVICE(CREATIVE, 0x0012),
+ .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA },
+#if !IS_ENABLED(CONFIG_SND_CTXFI)
+ /* the following entry conflicts with snd-ctxfi driver,
+ * as ctxfi driver mutates from HD-audio to native mode with
+ * a special command sequence.
+ */
+ { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID),
+ .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+ .class_mask = 0xffffff,
+ .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
+ AZX_DCAPS_NO_64BIT | AZX_DCAPS_POSFIX_LPIB },
+#else
+ /* this entry seems still valid -- i.e. without emu20kx chip */
+ { PCI_VDEVICE(CREATIVE, 0x0009),
+ .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
+ AZX_DCAPS_NO_64BIT | AZX_DCAPS_POSFIX_LPIB },
+#endif
+ /* CM8888 */
+ { PCI_VDEVICE(CMEDIA, 0x5011),
+ .driver_data = AZX_DRIVER_CMEDIA |
+ AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_SNOOP_OFF },
+ /* Vortex86MX */
+ { PCI_VDEVICE(RDC, 0x3010), .driver_data = AZX_DRIVER_GENERIC },
+ /* VMware HDAudio */
+ { PCI_VDEVICE(VMWARE, 0x1977), .driver_data = AZX_DRIVER_GENERIC },
+ /* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
+ .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+ .class_mask = 0xffffff,
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_ANY_ID),
+ .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+ .class_mask = 0xffffff,
+ .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI },
+ /* Zhaoxin */
+ { PCI_VDEVICE(ZHAOXIN, 0x3288), .driver_data = AZX_DRIVER_ZHAOXIN },
+ { PCI_VDEVICE(ZHAOXIN, 0x9141),
+ .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB |
+ AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT },
+ { PCI_VDEVICE(ZHAOXIN, 0x9142),
+ .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB |
+ AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT },
+ { PCI_VDEVICE(ZHAOXIN, 0x9144),
+ .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB |
+ AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT },
+ { PCI_VDEVICE(ZHAOXIN, 0x9145),
+ .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB |
+ AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT },
+ { PCI_VDEVICE(ZHAOXIN, 0x9146),
+ .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB |
+ AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT },
+ /* Loongson HDAudio*/
+ { PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDA),
+ .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL },
+ { PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDMI),
+ .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, azx_ids);
+
+/* pci_driver definition */
+static struct pci_driver azx_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = azx_ids,
+ .probe = azx_probe,
+ .remove = azx_remove,
+ .shutdown = azx_shutdown,
+ .driver = {
+ .pm = pm_ptr(&azx_pm),
+ },
+};
+
+module_pci_driver(azx_driver);
diff --git a/sound/hda/controllers/intel.h b/sound/hda/controllers/intel.h
new file mode 100644
index 000000000000..2d1725f86ef1
--- /dev/null
+++ b/sound/hda/controllers/intel.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ */
+#ifndef __SOUND_HDA_INTEL_H
+#define __SOUND_HDA_INTEL_H
+
+#include "hda_controller.h"
+
+struct hda_intel {
+ struct azx chip;
+
+ /* for pending irqs */
+ struct work_struct irq_pending_work;
+
+ /* sync probing */
+ struct completion probe_wait;
+ struct delayed_work probe_work;
+
+ /* card list (for power_save trigger) */
+ struct list_head list;
+
+ /* extra flags */
+ unsigned int irq_pending_warned:1;
+ unsigned int probe_continued:1;
+ unsigned int runtime_pm_disabled:1;
+
+ /* vga_switcheroo setup */
+ unsigned int use_vga_switcheroo:1;
+ unsigned int vga_switcheroo_registered:1;
+ unsigned int init_failed:1; /* delayed init failed */
+ unsigned int freed:1; /* resources already released */
+
+ bool need_i915_power:1; /* the hda controller needs i915 power */
+
+ int probe_retry; /* being probe-retry */
+};
+
+#endif
diff --git a/sound/hda/controllers/intel_trace.h b/sound/hda/controllers/intel_trace.h
new file mode 100644
index 000000000000..fb10ab9e7e55
--- /dev/null
+++ b/sound/hda/controllers/intel_trace.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hda_intel
+#define TRACE_INCLUDE_FILE intel_trace
+
+#if !defined(_TRACE_HDA_INTEL_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_HDA_INTEL_H
+
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(hda_pm,
+ TP_PROTO(struct azx *chip),
+
+ TP_ARGS(chip),
+
+ TP_STRUCT__entry(
+ __field(int, dev_index)
+ ),
+
+ TP_fast_assign(
+ __entry->dev_index = (chip)->dev_index;
+ ),
+
+ TP_printk("card index: %d", __entry->dev_index)
+);
+
+DEFINE_EVENT(hda_pm, azx_suspend,
+ TP_PROTO(struct azx *chip),
+ TP_ARGS(chip)
+);
+
+DEFINE_EVENT(hda_pm, azx_resume,
+ TP_PROTO(struct azx *chip),
+ TP_ARGS(chip)
+);
+
+DEFINE_EVENT(hda_pm, azx_runtime_suspend,
+ TP_PROTO(struct azx *chip),
+ TP_ARGS(chip)
+);
+
+DEFINE_EVENT(hda_pm, azx_runtime_resume,
+ TP_PROTO(struct azx *chip),
+ TP_ARGS(chip)
+);
+
+#endif /* _TRACE_HDA_INTEL_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
diff --git a/sound/hda/controllers/tegra.c b/sound/hda/controllers/tegra.c
new file mode 100644
index 000000000000..6ab338f37db5
--- /dev/null
+++ b/sound/hda/controllers/tegra.c
@@ -0,0 +1,652 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Implementation of primary ALSA driver code base for NVIDIA Tegra HDA.
+ */
+
+#include <linux/clk.h>
+#include <linux/clocksource.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/pm_runtime.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+
+#include <sound/hda_codec.h>
+#include "hda_controller.h"
+
+/* Defines for Nvidia Tegra HDA support */
+#define HDA_BAR0 0x8000
+
+#define HDA_CFG_CMD 0x1004
+#define HDA_CFG_BAR0 0x1010
+
+#define HDA_ENABLE_IO_SPACE (1 << 0)
+#define HDA_ENABLE_MEM_SPACE (1 << 1)
+#define HDA_ENABLE_BUS_MASTER (1 << 2)
+#define HDA_ENABLE_SERR (1 << 8)
+#define HDA_DISABLE_INTR (1 << 10)
+#define HDA_BAR0_INIT_PROGRAM 0xFFFFFFFF
+#define HDA_BAR0_FINAL_PROGRAM (1 << 14)
+
+/* IPFS */
+#define HDA_IPFS_CONFIG 0x180
+#define HDA_IPFS_EN_FPCI 0x1
+
+#define HDA_IPFS_FPCI_BAR0 0x80
+#define HDA_FPCI_BAR0_START 0x40
+
+#define HDA_IPFS_INTR_MASK 0x188
+#define HDA_IPFS_EN_INTR (1 << 16)
+
+/* FPCI */
+#define FPCI_DBG_CFG_2 0x10F4
+#define FPCI_GCAP_NSDO_SHIFT 18
+#define FPCI_GCAP_NSDO_MASK (0x3 << FPCI_GCAP_NSDO_SHIFT)
+
+/* max number of SDs */
+#define NUM_CAPTURE_SD 1
+#define NUM_PLAYBACK_SD 1
+
+/*
+ * Tegra194 does not reflect correct number of SDO lines. Below macro
+ * is used to update the GCAP register to workaround the issue.
+ */
+#define TEGRA194_NUM_SDO_LINES 4
+
+struct hda_tegra_soc {
+ bool has_hda2codec_2x_reset;
+ bool has_hda2hdmi;
+ bool has_hda2codec_2x;
+ bool input_stream;
+ bool always_on;
+ bool requires_init;
+};
+
+struct hda_tegra {
+ struct azx chip;
+ struct device *dev;
+ struct reset_control_bulk_data resets[3];
+ struct clk_bulk_data clocks[3];
+ unsigned int nresets;
+ unsigned int nclocks;
+ void __iomem *regs;
+ struct work_struct probe_work;
+ const struct hda_tegra_soc *soc;
+};
+
+#ifdef CONFIG_PM
+static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
+module_param(power_save, bint, 0644);
+MODULE_PARM_DESC(power_save,
+ "Automatic power-saving timeout (in seconds, 0 = disable).");
+#else
+#define power_save 0
+#endif
+
+static const struct hda_controller_ops hda_tegra_ops; /* nothing special */
+
+static void hda_tegra_init(struct hda_tegra *hda)
+{
+ u32 v;
+
+ /* Enable PCI access */
+ v = readl(hda->regs + HDA_IPFS_CONFIG);
+ v |= HDA_IPFS_EN_FPCI;
+ writel(v, hda->regs + HDA_IPFS_CONFIG);
+
+ /* Enable MEM/IO space and bus master */
+ v = readl(hda->regs + HDA_CFG_CMD);
+ v &= ~HDA_DISABLE_INTR;
+ v |= HDA_ENABLE_MEM_SPACE | HDA_ENABLE_IO_SPACE |
+ HDA_ENABLE_BUS_MASTER | HDA_ENABLE_SERR;
+ writel(v, hda->regs + HDA_CFG_CMD);
+
+ writel(HDA_BAR0_INIT_PROGRAM, hda->regs + HDA_CFG_BAR0);
+ writel(HDA_BAR0_FINAL_PROGRAM, hda->regs + HDA_CFG_BAR0);
+ writel(HDA_FPCI_BAR0_START, hda->regs + HDA_IPFS_FPCI_BAR0);
+
+ v = readl(hda->regs + HDA_IPFS_INTR_MASK);
+ v |= HDA_IPFS_EN_INTR;
+ writel(v, hda->regs + HDA_IPFS_INTR_MASK);
+}
+
+/*
+ * power management
+ */
+static int hda_tegra_suspend(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ int rc;
+
+ rc = pm_runtime_force_suspend(dev);
+ if (rc < 0)
+ return rc;
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+ return 0;
+}
+
+static int hda_tegra_resume(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ int rc;
+
+ rc = pm_runtime_force_resume(dev);
+ if (rc < 0)
+ return rc;
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+ return 0;
+}
+
+static int hda_tegra_runtime_suspend(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip = card->private_data;
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+
+ if (chip && chip->running) {
+ /* enable controller wake up event */
+ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) |
+ STATESTS_INT_MASK);
+
+ azx_stop_chip(chip);
+ azx_enter_link_reset(chip);
+ }
+ clk_bulk_disable_unprepare(hda->nclocks, hda->clocks);
+
+ return 0;
+}
+
+static int hda_tegra_runtime_resume(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip = card->private_data;
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+ int rc;
+
+ if (!chip->running) {
+ rc = reset_control_bulk_assert(hda->nresets, hda->resets);
+ if (rc)
+ return rc;
+ }
+
+ rc = clk_bulk_prepare_enable(hda->nclocks, hda->clocks);
+ if (rc != 0)
+ return rc;
+ if (chip->running) {
+ if (hda->soc->requires_init)
+ hda_tegra_init(hda);
+
+ azx_init_chip(chip, 1);
+ /* disable controller wake up event*/
+ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) &
+ ~STATESTS_INT_MASK);
+ } else {
+ usleep_range(10, 100);
+
+ rc = reset_control_bulk_deassert(hda->nresets, hda->resets);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops hda_tegra_pm = {
+ SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume)
+ RUNTIME_PM_OPS(hda_tegra_runtime_suspend, hda_tegra_runtime_resume, NULL)
+};
+
+static int hda_tegra_dev_disconnect(struct snd_device *device)
+{
+ struct azx *chip = device->device_data;
+
+ chip->bus.shutdown = 1;
+ return 0;
+}
+
+/*
+ * destructor
+ */
+static int hda_tegra_dev_free(struct snd_device *device)
+{
+ struct azx *chip = device->device_data;
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+
+ cancel_work_sync(&hda->probe_work);
+ if (azx_bus(chip)->chip_init) {
+ azx_stop_all_streams(chip);
+ azx_stop_chip(chip);
+ }
+
+ azx_free_stream_pages(chip);
+ azx_free_streams(chip);
+ snd_hdac_bus_exit(azx_bus(chip));
+
+ return 0;
+}
+
+static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev)
+{
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+ struct hdac_bus *bus = azx_bus(chip);
+ struct resource *res;
+
+ hda->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(hda->regs))
+ return PTR_ERR(hda->regs);
+
+ bus->remap_addr = hda->regs + HDA_BAR0;
+ bus->addr = res->start + HDA_BAR0;
+
+ if (hda->soc->requires_init)
+ hda_tegra_init(hda);
+
+ return 0;
+}
+
+static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
+{
+ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
+ struct hdac_bus *bus = azx_bus(chip);
+ struct snd_card *card = chip->card;
+ int err;
+ unsigned short gcap;
+ int irq_id = platform_get_irq(pdev, 0);
+ const char *sname, *drv_name = "tegra-hda";
+ struct device_node *np = pdev->dev.of_node;
+
+ if (irq_id < 0)
+ return irq_id;
+
+ err = hda_tegra_init_chip(chip, pdev);
+ if (err)
+ return err;
+
+ err = devm_request_irq(chip->card->dev, irq_id, azx_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "unable to request IRQ %d, disabling device\n",
+ irq_id);
+ return err;
+ }
+ bus->irq = irq_id;
+ bus->dma_stop_delay = 100;
+ card->sync_irq = bus->irq;
+
+ /*
+ * Tegra194 has 4 SDO lines and the STRIPE can be used to
+ * indicate how many of the SDO lines the stream should be
+ * striped. But GCAP register does not reflect the true
+ * capability of HW. Below workaround helps to fix this.
+ *
+ * GCAP_NSDO is bits 19:18 in T_AZA_DBG_CFG_2,
+ * 0 for 1 SDO, 1 for 2 SDO, 2 for 4 SDO lines.
+ */
+ if (of_device_is_compatible(np, "nvidia,tegra194-hda")) {
+ u32 val;
+
+ dev_info(card->dev, "Override SDO lines to %u\n",
+ TEGRA194_NUM_SDO_LINES);
+
+ val = readl(hda->regs + FPCI_DBG_CFG_2) & ~FPCI_GCAP_NSDO_MASK;
+ val |= (TEGRA194_NUM_SDO_LINES >> 1) << FPCI_GCAP_NSDO_SHIFT;
+ writel(val, hda->regs + FPCI_DBG_CFG_2);
+ }
+
+ gcap = azx_readw(chip, GCAP);
+ dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
+
+ chip->align_buffer_size = 1;
+
+ /* read number of streams from GCAP register instead of using
+ * hardcoded value
+ */
+ chip->capture_streams = (gcap >> 8) & 0x0f;
+
+ /* The GCAP register on Tegra234 implies no Input Streams(ISS) support,
+ * but the HW output stream descriptor programming should start with
+ * offset 0x20*4 from base stream descriptor address. This will be a
+ * problem while calculating the offset for output stream descriptor
+ * which will be considering input stream also. So here output stream
+ * starts with offset 0 which is wrong as HW register for output stream
+ * offset starts with 4.
+ */
+ if (!hda->soc->input_stream)
+ chip->capture_streams = 4;
+
+ chip->playback_streams = (gcap >> 12) & 0x0f;
+ if (!chip->playback_streams && !chip->capture_streams) {
+ /* gcap didn't give any info, switching to old method */
+ chip->playback_streams = NUM_PLAYBACK_SD;
+ chip->capture_streams = NUM_CAPTURE_SD;
+ }
+ chip->capture_index_offset = 0;
+ chip->playback_index_offset = chip->capture_streams;
+ chip->num_streams = chip->playback_streams + chip->capture_streams;
+
+ /* initialize streams */
+ err = azx_init_streams(chip);
+ if (err < 0) {
+ dev_err(card->dev, "failed to initialize streams: %d\n", err);
+ return err;
+ }
+
+ err = azx_alloc_stream_pages(chip);
+ if (err < 0) {
+ dev_err(card->dev, "failed to allocate stream pages: %d\n",
+ err);
+ return err;
+ }
+
+ /* initialize chip */
+ azx_init_chip(chip, 1);
+
+ /*
+ * Playback (for 44.1K/48K, 2-channel, 16-bps) fails with
+ * 4 SDO lines due to legacy design limitation. Following
+ * is, from HD Audio Specification (Revision 1.0a), used to
+ * control striping of the stream across multiple SDO lines
+ * for sample rates <= 48K.
+ *
+ * { ((num_channels * bits_per_sample) / number of SDOs) >= 8 }
+ *
+ * Due to legacy design issue it is recommended that above
+ * ratio must be greater than 8. Since number of SDO lines is
+ * in powers of 2, next available ratio is 16 which can be
+ * used as a limiting factor here.
+ */
+ if (of_device_is_compatible(np, "nvidia,tegra30-hda"))
+ chip->bus.core.sdo_limit = 16;
+
+ /* codec detection */
+ if (!bus->codec_mask) {
+ dev_err(card->dev, "no codecs found!\n");
+ return -ENODEV;
+ }
+
+ /* driver name */
+ strscpy(card->driver, drv_name);
+ /* shortname for card */
+ sname = of_get_property(np, "nvidia,model", NULL);
+ if (!sname)
+ sname = drv_name;
+ if (strlen(sname) > sizeof(card->shortname))
+ dev_info(card->dev, "truncating shortname for card\n");
+ strscpy(card->shortname, sname);
+
+ /* longname for card */
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx irq %i",
+ card->shortname, bus->addr, bus->irq);
+
+ return 0;
+}
+
+/*
+ * constructor
+ */
+
+static void hda_tegra_probe_work(struct work_struct *work);
+
+static int hda_tegra_create(struct snd_card *card,
+ unsigned int driver_caps,
+ struct hda_tegra *hda)
+{
+ static const struct snd_device_ops ops = {
+ .dev_disconnect = hda_tegra_dev_disconnect,
+ .dev_free = hda_tegra_dev_free,
+ };
+ struct azx *chip;
+ int err;
+
+ chip = &hda->chip;
+
+ mutex_init(&chip->open_mutex);
+ chip->card = card;
+ chip->ops = &hda_tegra_ops;
+ chip->driver_caps = driver_caps;
+ chip->driver_type = driver_caps & 0xff;
+ chip->dev_index = 0;
+ INIT_LIST_HEAD(&chip->pcm_list);
+
+ chip->codec_probe_mask = -1;
+
+ chip->single_cmd = false;
+ chip->snoop = true;
+
+ INIT_WORK(&hda->probe_work, hda_tegra_probe_work);
+
+ err = azx_bus_init(chip, NULL);
+ if (err < 0)
+ return err;
+
+ chip->bus.core.sync_write = 0;
+ chip->bus.core.needs_damn_long_delay = 1;
+ chip->bus.core.aligned_mmio = 1;
+
+ /*
+ * HDA power domain and clocks are always on for Tegra264 and
+ * the jack detection logic would work always, so no need of
+ * jack polling mechanism running.
+ */
+ if (!hda->soc->always_on) {
+ chip->jackpoll_interval = msecs_to_jiffies(5000);
+ chip->bus.jackpoll_in_suspend = 1;
+ }
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ dev_err(card->dev, "Error creating device\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct hda_tegra_soc tegra30_data = {
+ .has_hda2codec_2x_reset = true,
+ .has_hda2hdmi = true,
+ .has_hda2codec_2x = true,
+ .input_stream = true,
+ .always_on = false,
+ .requires_init = true,
+};
+
+static const struct hda_tegra_soc tegra194_data = {
+ .has_hda2codec_2x_reset = false,
+ .has_hda2hdmi = true,
+ .has_hda2codec_2x = true,
+ .input_stream = true,
+ .always_on = false,
+ .requires_init = true,
+};
+
+static const struct hda_tegra_soc tegra234_data = {
+ .has_hda2codec_2x_reset = true,
+ .has_hda2hdmi = false,
+ .has_hda2codec_2x = true,
+ .input_stream = false,
+ .always_on = false,
+ .requires_init = true,
+};
+
+static const struct hda_tegra_soc tegra264_data = {
+ .has_hda2codec_2x_reset = true,
+ .has_hda2hdmi = false,
+ .has_hda2codec_2x = false,
+ .input_stream = false,
+ .always_on = true,
+ .requires_init = false,
+};
+
+static const struct of_device_id hda_tegra_match[] = {
+ { .compatible = "nvidia,tegra30-hda", .data = &tegra30_data },
+ { .compatible = "nvidia,tegra194-hda", .data = &tegra194_data },
+ { .compatible = "nvidia,tegra234-hda", .data = &tegra234_data },
+ { .compatible = "nvidia,tegra264-hda", .data = &tegra264_data },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hda_tegra_match);
+
+static int hda_tegra_probe(struct platform_device *pdev)
+{
+ const unsigned int driver_flags = AZX_DCAPS_CORBRP_SELF_CLEAR |
+ AZX_DCAPS_PM_RUNTIME |
+ AZX_DCAPS_4K_BDLE_BOUNDARY;
+ struct snd_card *card;
+ struct azx *chip;
+ struct hda_tegra *hda;
+ int err;
+
+ hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL);
+ if (!hda)
+ return -ENOMEM;
+ hda->dev = &pdev->dev;
+ chip = &hda->chip;
+
+ hda->soc = of_device_get_match_data(&pdev->dev);
+
+ err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, 0, &card);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Error creating card!\n");
+ return err;
+ }
+
+ hda->resets[hda->nresets++].id = "hda";
+
+ /*
+ * "hda2hdmi" is not applicable for Tegra234. This is because the
+ * codec is separate IP and not under display SOR partition now.
+ */
+ if (hda->soc->has_hda2hdmi)
+ hda->resets[hda->nresets++].id = "hda2hdmi";
+
+ /*
+ * "hda2codec_2x" reset is not present on Tegra194. Though DT would
+ * be updated to reflect this, but to have backward compatibility
+ * below is necessary.
+ */
+ if (hda->soc->has_hda2codec_2x_reset)
+ hda->resets[hda->nresets++].id = "hda2codec_2x";
+
+ err = devm_reset_control_bulk_get_exclusive(&pdev->dev, hda->nresets,
+ hda->resets);
+ if (err)
+ goto out_free;
+
+ hda->clocks[hda->nclocks++].id = "hda";
+ if (hda->soc->has_hda2hdmi)
+ hda->clocks[hda->nclocks++].id = "hda2hdmi";
+
+ if (hda->soc->has_hda2codec_2x)
+ hda->clocks[hda->nclocks++].id = "hda2codec_2x";
+
+ err = devm_clk_bulk_get(&pdev->dev, hda->nclocks, hda->clocks);
+ if (err < 0)
+ goto out_free;
+
+ err = hda_tegra_create(card, driver_flags, hda);
+ if (err < 0)
+ goto out_free;
+ card->private_data = chip;
+
+ dev_set_drvdata(&pdev->dev, card);
+
+ pm_runtime_enable(hda->dev);
+ if (!azx_has_pm_runtime(chip))
+ pm_runtime_forbid(hda->dev);
+
+ schedule_work(&hda->probe_work);
+
+ return 0;
+
+out_free:
+ snd_card_free(card);
+ return err;
+}
+
+static void hda_tegra_probe_work(struct work_struct *work)
+{
+ struct hda_tegra *hda = container_of(work, struct hda_tegra, probe_work);
+ struct azx *chip = &hda->chip;
+ struct platform_device *pdev = to_platform_device(hda->dev);
+ int err;
+
+ pm_runtime_get_sync(hda->dev);
+ err = hda_tegra_first_init(chip, pdev);
+ if (err < 0)
+ goto out_free;
+
+ /* create codec instances */
+ err = azx_probe_codecs(chip, 8);
+ if (err < 0)
+ goto out_free;
+
+ err = azx_codec_configure(chip);
+ if (err < 0)
+ goto out_free;
+
+ err = snd_card_register(chip->card);
+ if (err < 0)
+ goto out_free;
+
+ chip->running = 1;
+ snd_hda_set_power_save(&chip->bus, power_save * 1000);
+
+ out_free:
+ pm_runtime_put(hda->dev);
+ return; /* no error return from async probe */
+}
+
+static void hda_tegra_remove(struct platform_device *pdev)
+{
+ snd_card_free(dev_get_drvdata(&pdev->dev));
+ pm_runtime_disable(&pdev->dev);
+}
+
+static void hda_tegra_shutdown(struct platform_device *pdev)
+{
+ struct snd_card *card = dev_get_drvdata(&pdev->dev);
+ struct azx *chip;
+
+ if (!card)
+ return;
+ chip = card->private_data;
+ if (chip && chip->running)
+ azx_stop_chip(chip);
+}
+
+static struct platform_driver tegra_platform_hda = {
+ .driver = {
+ .name = "tegra-hda",
+ .pm = pm_ptr(&hda_tegra_pm),
+ .of_match_table = hda_tegra_match,
+ },
+ .probe = hda_tegra_probe,
+ .remove = hda_tegra_remove,
+ .shutdown = hda_tegra_shutdown,
+};
+module_platform_driver(tegra_platform_hda);
+
+MODULE_DESCRIPTION("Tegra HDA bus driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/hda/core/Kconfig b/sound/hda/core/Kconfig
new file mode 100644
index 000000000000..bfdcf6384c52
--- /dev/null
+++ b/sound/hda/core/Kconfig
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SND_HDA_CORE
+ tristate
+ select REGMAP
+
+config SND_HDA_DSP_LOADER
+ bool
+
+config SND_HDA_ALIGNED_MMIO
+ bool
+
+config SND_HDA_COMPONENT
+ bool
+
+config SND_HDA_I915
+ bool
+ select SND_HDA_COMPONENT
+
+config SND_HDA_EXT_CORE
+ tristate
+ select SND_HDA_CORE
+
+config SND_INTEL_NHLT
+ bool
+ # this config should be selected only for Intel ACPI platforms.
+ # A fallback is provided so that the code compiles in all cases.
+
+config SND_INTEL_DSP_CONFIG
+ tristate
+ select ACPI_NHLT if ACPI
+ select SND_INTEL_NHLT if ACPI
+ select SND_INTEL_SOUNDWIRE_ACPI if ACPI
+ # this config should be selected only for Intel DSP platforms.
+ # A fallback is provided so that the code compiles in all cases.
+
+config SND_INTEL_SOUNDWIRE_ACPI
+ tristate
+
+config SND_INTEL_BYT_PREFER_SOF
+ bool "Prefer SOF driver over SST on BY/CHT platforms"
+ depends on SND_SST_ATOM_HIFI2_PLATFORM_ACPI && SND_SOC_SOF_BAYTRAIL
+ default n
+ help
+ The kernel has 2 drivers for the Low Power Engine audio-block on
+ Bay- and Cherry-Trail SoCs. The old SST driver and the new SOF
+ driver. If both drivers are enabled then the kernel will default
+ to using the old SST driver, unless told otherwise through the
+ snd_intel_dspcfg.dsp_driver module-parameter.
+
+ Set this option to Y to make the kernel default to the new SOF
+ driver instead.
diff --git a/sound/hda/core/Makefile b/sound/hda/core/Makefile
new file mode 100644
index 000000000000..89cb46143050
--- /dev/null
+++ b/sound/hda/core/Makefile
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0
+snd-hda-core-y := hda_bus_type.o bus.o device.o sysfs.o \
+ regmap.o controller.o stream.o array.o hdmi_chmap.o
+
+snd-hda-core-y += trace.o
+CFLAGS_trace.o := -I$(src)
+
+# for sync with i915 gfx driver
+snd-hda-core-$(CONFIG_SND_HDA_COMPONENT) += component.o
+snd-hda-core-$(CONFIG_SND_HDA_I915) += i915.o
+
+obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o
+
+#extended hda
+obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/
+
+snd-intel-dspcfg-y := intel-dsp-config.o
+snd-intel-dspcfg-$(CONFIG_SND_INTEL_NHLT) += intel-nhlt.o
+obj-$(CONFIG_SND_INTEL_DSP_CONFIG) += snd-intel-dspcfg.o
+
+snd-intel-sdw-acpi-y := intel-sdw-acpi.o
+obj-$(CONFIG_SND_INTEL_SOUNDWIRE_ACPI) += snd-intel-sdw-acpi.o
diff --git a/sound/hda/core/array.c b/sound/hda/core/array.c
new file mode 100644
index 000000000000..a204dcee0034
--- /dev/null
+++ b/sound/hda/core/array.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * generic arrays
+ */
+
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+
+/**
+ * snd_array_new - get a new element from the given array
+ * @array: the array object
+ *
+ * Get a new element from the given array. If it exceeds the
+ * pre-allocated array size, re-allocate the array.
+ *
+ * Returns NULL if allocation failed.
+ */
+void *snd_array_new(struct snd_array *array)
+{
+ if (snd_BUG_ON(!array->elem_size))
+ return NULL;
+ if (array->used >= array->alloced) {
+ int num = array->alloced + array->alloc_align;
+ int oldsize = array->alloced * array->elem_size;
+ int size = (num + 1) * array->elem_size;
+ void *nlist;
+ if (snd_BUG_ON(num >= 4096))
+ return NULL;
+ nlist = krealloc(array->list, size, GFP_KERNEL);
+ if (!nlist)
+ return NULL;
+ memset(nlist + oldsize, 0, size - oldsize);
+ array->list = nlist;
+ array->alloced = num;
+ }
+ return snd_array_elem(array, array->used++);
+}
+EXPORT_SYMBOL_GPL(snd_array_new);
+
+/**
+ * snd_array_free - free the given array elements
+ * @array: the array object
+ */
+void snd_array_free(struct snd_array *array)
+{
+ kfree(array->list);
+ array->used = 0;
+ array->alloced = 0;
+ array->list = NULL;
+}
+EXPORT_SYMBOL_GPL(snd_array_free);
diff --git a/sound/hda/core/bus.c b/sound/hda/core/bus.c
new file mode 100644
index 000000000000..9b196c915f37
--- /dev/null
+++ b/sound/hda/core/bus.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HD-audio core bus driver
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <sound/hdaudio.h>
+#include "local.h"
+#include "trace.h"
+
+static void snd_hdac_bus_process_unsol_events(struct work_struct *work);
+
+static const struct hdac_bus_ops default_ops = {
+ .command = snd_hdac_bus_send_cmd,
+ .get_response = snd_hdac_bus_get_response,
+ .link_power = snd_hdac_bus_link_power,
+};
+
+/**
+ * snd_hdac_bus_init - initialize a HD-audio bas bus
+ * @bus: the pointer to bus object
+ * @dev: device pointer
+ * @ops: bus verb operators
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
+ const struct hdac_bus_ops *ops)
+{
+ memset(bus, 0, sizeof(*bus));
+ bus->dev = dev;
+ if (ops)
+ bus->ops = ops;
+ else
+ bus->ops = &default_ops;
+ bus->dma_type = SNDRV_DMA_TYPE_DEV;
+ INIT_LIST_HEAD(&bus->stream_list);
+ INIT_LIST_HEAD(&bus->codec_list);
+ INIT_WORK(&bus->unsol_work, snd_hdac_bus_process_unsol_events);
+ spin_lock_init(&bus->reg_lock);
+ mutex_init(&bus->cmd_mutex);
+ mutex_init(&bus->lock);
+ INIT_LIST_HEAD(&bus->hlink_list);
+ init_waitqueue_head(&bus->rirb_wq);
+ bus->irq = -1;
+
+ /*
+ * Default value of '8' is as per the HD audio specification (Rev 1.0a).
+ * Following relation is used to derive STRIPE control value.
+ * For sample rate <= 48K:
+ * { ((num_channels * bits_per_sample) / number of SDOs) >= 8 }
+ * For sample rate > 48K:
+ * { ((num_channels * bits_per_sample * rate/48000) /
+ * number of SDOs) >= 8 }
+ */
+ bus->sdo_limit = 8;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_init);
+
+/**
+ * snd_hdac_bus_exit - clean up a HD-audio bas bus
+ * @bus: the pointer to bus object
+ */
+void snd_hdac_bus_exit(struct hdac_bus *bus)
+{
+ WARN_ON(!list_empty(&bus->stream_list));
+ WARN_ON(!list_empty(&bus->codec_list));
+ cancel_work_sync(&bus->unsol_work);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_exit);
+
+/**
+ * snd_hdac_bus_exec_verb - execute a HD-audio verb on the given bus
+ * @bus: bus object
+ * @addr: the HDAC device address
+ * @cmd: HD-audio encoded verb
+ * @res: pointer to store the response, NULL if performing asynchronously
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
+ unsigned int cmd, unsigned int *res)
+{
+ guard(mutex)(&bus->cmd_mutex);
+ return snd_hdac_bus_exec_verb_unlocked(bus, addr, cmd, res);
+}
+
+/**
+ * snd_hdac_bus_exec_verb_unlocked - unlocked version
+ * @bus: bus object
+ * @addr: the HDAC device address
+ * @cmd: HD-audio encoded verb
+ * @res: pointer to store the response, NULL if performing asynchronously
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr,
+ unsigned int cmd, unsigned int *res)
+{
+ unsigned int tmp;
+ int err;
+
+ if (cmd == ~0)
+ return -EINVAL;
+
+ if (res)
+ *res = -1;
+ else if (bus->sync_write)
+ res = &tmp;
+ for (;;) {
+ trace_hda_send_cmd(bus, cmd);
+ err = bus->ops->command(bus, cmd);
+ if (err != -EAGAIN)
+ break;
+ /* process pending verbs */
+ err = bus->ops->get_response(bus, addr, &tmp);
+ if (err)
+ break;
+ }
+ if (!err && res) {
+ err = bus->ops->get_response(bus, addr, res);
+ trace_hda_get_response(bus, addr, *res);
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb_unlocked);
+
+/**
+ * snd_hdac_bus_queue_event - add an unsolicited event to queue
+ * @bus: the BUS
+ * @res: unsolicited event (lower 32bit of RIRB entry)
+ * @res_ex: codec addr and flags (upper 32bit or RIRB entry)
+ *
+ * Adds the given event to the queue. The events are processed in
+ * the workqueue asynchronously. Call this function in the interrupt
+ * hanlder when RIRB receives an unsolicited event.
+ */
+void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex)
+{
+ unsigned int wp;
+
+ if (!bus)
+ return;
+
+ trace_hda_unsol_event(bus, res, res_ex);
+ wp = (bus->unsol_wp + 1) % HDA_UNSOL_QUEUE_SIZE;
+ bus->unsol_wp = wp;
+
+ wp <<= 1;
+ bus->unsol_queue[wp] = res;
+ bus->unsol_queue[wp + 1] = res_ex;
+
+ schedule_work(&bus->unsol_work);
+}
+
+/*
+ * process queued unsolicited events
+ */
+static void snd_hdac_bus_process_unsol_events(struct work_struct *work)
+{
+ struct hdac_bus *bus = container_of(work, struct hdac_bus, unsol_work);
+ struct hdac_device *codec;
+ struct hdac_driver *drv;
+ unsigned int rp, caddr, res;
+
+ spin_lock_irq(&bus->reg_lock);
+ while (bus->unsol_rp != bus->unsol_wp) {
+ rp = (bus->unsol_rp + 1) % HDA_UNSOL_QUEUE_SIZE;
+ bus->unsol_rp = rp;
+ rp <<= 1;
+ res = bus->unsol_queue[rp];
+ caddr = bus->unsol_queue[rp + 1];
+ if (!(caddr & (1 << 4))) /* no unsolicited event? */
+ continue;
+ codec = bus->caddr_tbl[caddr & 0x0f];
+ if (!codec || !codec->registered)
+ continue;
+ spin_unlock_irq(&bus->reg_lock);
+ drv = drv_to_hdac_driver(codec->dev.driver);
+ if (drv->unsol_event)
+ drv->unsol_event(codec, res);
+ spin_lock_irq(&bus->reg_lock);
+ }
+ spin_unlock_irq(&bus->reg_lock);
+}
+
+/**
+ * snd_hdac_bus_add_device - Add a codec to bus
+ * @bus: HDA core bus
+ * @codec: HDA core device to add
+ *
+ * Adds the given codec to the list in the bus. The caddr_tbl array
+ * and codec_powered bits are updated, as well.
+ * Returns zero if success, or a negative error code.
+ */
+int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
+{
+ if (bus->caddr_tbl[codec->addr]) {
+ dev_err(bus->dev, "address 0x%x is already occupied\n",
+ codec->addr);
+ return -EBUSY;
+ }
+
+ list_add_tail(&codec->list, &bus->codec_list);
+ bus->caddr_tbl[codec->addr] = codec;
+ set_bit(codec->addr, &bus->codec_powered);
+ bus->num_codecs++;
+ return 0;
+}
+
+/**
+ * snd_hdac_bus_remove_device - Remove a codec from bus
+ * @bus: HDA core bus
+ * @codec: HDA core device to remove
+ */
+void snd_hdac_bus_remove_device(struct hdac_bus *bus,
+ struct hdac_device *codec)
+{
+ WARN_ON(bus != codec->bus);
+ if (list_empty(&codec->list))
+ return;
+ list_del_init(&codec->list);
+ bus->caddr_tbl[codec->addr] = NULL;
+ clear_bit(codec->addr, &bus->codec_powered);
+ bus->num_codecs--;
+ flush_work(&bus->unsol_work);
+}
+
+#ifdef CONFIG_SND_HDA_ALIGNED_MMIO
+/* Helpers for aligned read/write of mmio space, for Tegra */
+unsigned int snd_hdac_aligned_read(void __iomem *addr, unsigned int mask)
+{
+ void __iomem *aligned_addr =
+ (void __iomem *)((unsigned long)(addr) & ~0x3);
+ unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+ unsigned int v;
+
+ v = readl(aligned_addr);
+ return (v >> shift) & mask;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_aligned_read);
+
+void snd_hdac_aligned_write(unsigned int val, void __iomem *addr,
+ unsigned int mask)
+{
+ void __iomem *aligned_addr =
+ (void __iomem *)((unsigned long)(addr) & ~0x3);
+ unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
+ unsigned int v;
+
+ v = readl(aligned_addr);
+ v &= ~(mask << shift);
+ v |= val << shift;
+ writel(v, aligned_addr);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_aligned_write);
+#endif /* CONFIG_SND_HDA_ALIGNED_MMIO */
+
+void snd_hdac_codec_link_up(struct hdac_device *codec)
+{
+ struct hdac_bus *bus = codec->bus;
+
+ if (bus->ops->link_power)
+ bus->ops->link_power(codec, true);
+ else
+ snd_hdac_bus_link_power(codec, true);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_link_up);
+
+void snd_hdac_codec_link_down(struct hdac_device *codec)
+{
+ struct hdac_bus *bus = codec->bus;
+
+ if (bus->ops->link_power)
+ bus->ops->link_power(codec, false);
+ else
+ snd_hdac_bus_link_power(codec, false);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_link_down);
diff --git a/sound/hda/core/component.c b/sound/hda/core/component.c
new file mode 100644
index 000000000000..04755903880e
--- /dev/null
+++ b/sound/hda/core/component.c
@@ -0,0 +1,351 @@
+// SPDX-License-Identifier: GPL-2.0
+// hdac_component.c - routines for sync between HD-A core and DRM driver
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/component.h>
+#include <linux/string_choices.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_component.h>
+#include <sound/hda_register.h>
+
+static void hdac_acomp_release(struct device *dev, void *res)
+{
+}
+
+static struct drm_audio_component *hdac_get_acomp(struct device *dev)
+{
+ return devres_find(dev, hdac_acomp_release, NULL, NULL);
+}
+
+/**
+ * snd_hdac_set_codec_wakeup - Enable / disable HDMI/DP codec wakeup
+ * @bus: HDA core bus
+ * @enable: enable or disable the wakeup
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function should be called during the chip reset, also called at
+ * resume for updating STATESTS register read.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable)
+{
+ struct drm_audio_component *acomp = bus->audio_component;
+
+ if (!acomp || !acomp->ops)
+ return -ENODEV;
+
+ if (!acomp->ops->codec_wake_override)
+ return 0;
+
+ dev_dbg(bus->dev, "%s codec wakeup\n", str_enable_disable(enable));
+
+ acomp->ops->codec_wake_override(acomp->dev, enable);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_set_codec_wakeup);
+
+/**
+ * snd_hdac_display_power - Power up / down the power refcount
+ * @bus: HDA core bus
+ * @idx: HDA codec address, pass HDA_CODEC_IDX_CONTROLLER for controller
+ * @enable: power up or down
+ *
+ * This function is used by either HD-audio controller or codec driver that
+ * needs the interaction with graphics driver.
+ *
+ * This function updates the power status, and calls the get_power() and
+ * put_power() ops accordingly, toggling the codec wakeup, too.
+ */
+void snd_hdac_display_power(struct hdac_bus *bus, unsigned int idx, bool enable)
+{
+ struct drm_audio_component *acomp = bus->audio_component;
+
+ dev_dbg(bus->dev, "display power %s\n", str_enable_disable(enable));
+
+ guard(mutex)(&bus->lock);
+ if (enable)
+ set_bit(idx, &bus->display_power_status);
+ else
+ clear_bit(idx, &bus->display_power_status);
+
+ if (!acomp || !acomp->ops)
+ return;
+
+ if (bus->display_power_status) {
+ if (!bus->display_power_active) {
+ unsigned long cookie = -1;
+
+ if (acomp->ops->get_power)
+ cookie = acomp->ops->get_power(acomp->dev);
+
+ snd_hdac_set_codec_wakeup(bus, true);
+ snd_hdac_set_codec_wakeup(bus, false);
+ bus->display_power_active = cookie;
+ }
+ } else {
+ if (bus->display_power_active) {
+ unsigned long cookie = bus->display_power_active;
+
+ if (acomp->ops->put_power)
+ acomp->ops->put_power(acomp->dev, cookie);
+
+ bus->display_power_active = 0;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_display_power);
+
+/**
+ * snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate
+ * @codec: HDA codec
+ * @nid: the pin widget NID
+ * @dev_id: device identifier
+ * @rate: the sample rate to set
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function sets N/CTS value based on the given sample rate.
+ * Returns zero for success, or a negative error code.
+ */
+int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid,
+ int dev_id, int rate)
+{
+ struct hdac_bus *bus = codec->bus;
+ struct drm_audio_component *acomp = bus->audio_component;
+ int port, pipe;
+
+ if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
+ return -ENODEV;
+ port = nid;
+ if (acomp->audio_ops && acomp->audio_ops->pin2port) {
+ port = acomp->audio_ops->pin2port(codec, nid);
+ if (port < 0)
+ return -EINVAL;
+ }
+ pipe = dev_id;
+ return acomp->ops->sync_audio_rate(acomp->dev, port, pipe, rate);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
+
+/**
+ * snd_hdac_acomp_get_eld - Get the audio state and ELD via component
+ * @codec: HDA codec
+ * @nid: the pin widget NID
+ * @dev_id: device identifier
+ * @audio_enabled: the pointer to store the current audio state
+ * @buffer: the buffer pointer to store ELD bytes
+ * @max_bytes: the max bytes to be stored on @buffer
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function queries the current state of the audio on the given
+ * digital port and fetches the ELD bytes onto the given buffer.
+ * It returns the number of bytes for the total ELD data, zero for
+ * invalid ELD, or a negative error code.
+ *
+ * The return size is the total bytes required for the whole ELD bytes,
+ * thus it may be over @max_bytes. If it's over @max_bytes, it implies
+ * that only a part of ELD bytes have been fetched.
+ */
+int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, int dev_id,
+ bool *audio_enabled, char *buffer, int max_bytes)
+{
+ struct hdac_bus *bus = codec->bus;
+ struct drm_audio_component *acomp = bus->audio_component;
+ int port, pipe;
+
+ if (!acomp || !acomp->ops || !acomp->ops->get_eld)
+ return -ENODEV;
+
+ port = nid;
+ if (acomp->audio_ops && acomp->audio_ops->pin2port) {
+ port = acomp->audio_ops->pin2port(codec, nid);
+ if (port < 0)
+ return -EINVAL;
+ }
+ pipe = dev_id;
+ return acomp->ops->get_eld(acomp->dev, port, pipe, audio_enabled,
+ buffer, max_bytes);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld);
+
+static int hdac_component_master_bind(struct device *dev)
+{
+ struct drm_audio_component *acomp = hdac_get_acomp(dev);
+ int ret;
+
+ if (WARN_ON(!acomp))
+ return -EINVAL;
+
+ ret = component_bind_all(dev, acomp);
+ if (ret < 0)
+ return ret;
+
+ if (WARN_ON(!(acomp->dev && acomp->ops))) {
+ ret = -EINVAL;
+ goto out_unbind;
+ }
+
+ /* pin the module to avoid dynamic unbinding, but only if given */
+ if (!try_module_get(acomp->ops->owner)) {
+ ret = -ENODEV;
+ goto out_unbind;
+ }
+
+ if (acomp->audio_ops && acomp->audio_ops->master_bind) {
+ ret = acomp->audio_ops->master_bind(dev, acomp);
+ if (ret < 0)
+ goto module_put;
+ }
+
+ complete_all(&acomp->master_bind_complete);
+ return 0;
+
+ module_put:
+ module_put(acomp->ops->owner);
+out_unbind:
+ component_unbind_all(dev, acomp);
+ complete_all(&acomp->master_bind_complete);
+
+ return ret;
+}
+
+static void hdac_component_master_unbind(struct device *dev)
+{
+ struct drm_audio_component *acomp = hdac_get_acomp(dev);
+
+ if (acomp->audio_ops && acomp->audio_ops->master_unbind)
+ acomp->audio_ops->master_unbind(dev, acomp);
+ module_put(acomp->ops->owner);
+ component_unbind_all(dev, acomp);
+ WARN_ON(acomp->ops || acomp->dev);
+}
+
+static const struct component_master_ops hdac_component_master_ops = {
+ .bind = hdac_component_master_bind,
+ .unbind = hdac_component_master_unbind,
+};
+
+/**
+ * snd_hdac_acomp_register_notifier - Register audio component ops
+ * @bus: HDA core bus
+ * @aops: audio component ops
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function sets the given ops to be called by the graphics driver.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_acomp_register_notifier(struct hdac_bus *bus,
+ const struct drm_audio_component_audio_ops *aops)
+{
+ if (!bus->audio_component)
+ return -ENODEV;
+
+ bus->audio_component->audio_ops = aops;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_acomp_register_notifier);
+
+/**
+ * snd_hdac_acomp_init - Initialize audio component
+ * @bus: HDA core bus
+ * @aops: audio component ops
+ * @match_master: match function for finding components
+ * @extra_size: Extra bytes to allocate
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function initializes and sets up the audio component to communicate
+ * with graphics driver.
+ *
+ * Unlike snd_hdac_i915_init(), this function doesn't synchronize with the
+ * binding with the DRM component. Each caller needs to sync via master_bind
+ * audio_ops.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_acomp_init(struct hdac_bus *bus,
+ const struct drm_audio_component_audio_ops *aops,
+ int (*match_master)(struct device *, int, void *),
+ size_t extra_size)
+{
+ struct component_match *match = NULL;
+ struct device *dev = bus->dev;
+ struct drm_audio_component *acomp;
+ int ret;
+
+ if (WARN_ON(hdac_get_acomp(dev)))
+ return -EBUSY;
+
+ acomp = devres_alloc(hdac_acomp_release, sizeof(*acomp) + extra_size,
+ GFP_KERNEL);
+ if (!acomp)
+ return -ENOMEM;
+ acomp->audio_ops = aops;
+ init_completion(&acomp->master_bind_complete);
+ bus->audio_component = acomp;
+ devres_add(dev, acomp);
+
+ component_match_add_typed(dev, &match, match_master, bus);
+ ret = component_master_add_with_match(dev, &hdac_component_master_ops,
+ match);
+ if (ret < 0)
+ goto out_err;
+
+ return 0;
+
+out_err:
+ bus->audio_component = NULL;
+ devres_destroy(dev, hdac_acomp_release, NULL, NULL);
+ dev_info(dev, "failed to add audio component master (%d)\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_acomp_init);
+
+/**
+ * snd_hdac_acomp_exit - Finalize audio component
+ * @bus: HDA core bus
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with graphics driver.
+ *
+ * This function releases the audio component that has been used.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_acomp_exit(struct hdac_bus *bus)
+{
+ struct device *dev = bus->dev;
+ struct drm_audio_component *acomp = bus->audio_component;
+
+ if (!acomp)
+ return 0;
+
+ if (WARN_ON(bus->display_power_active) && acomp->ops)
+ acomp->ops->put_power(acomp->dev, bus->display_power_active);
+
+ bus->display_power_active = 0;
+ bus->display_power_status = 0;
+
+ component_master_del(dev, &hdac_component_master_ops);
+
+ bus->audio_component = NULL;
+ devres_destroy(dev, hdac_acomp_release, NULL, NULL);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_acomp_exit);
diff --git a/sound/hda/core/controller.c b/sound/hda/core/controller.c
new file mode 100644
index 000000000000..a7c00ad80117
--- /dev/null
+++ b/sound/hda/core/controller.c
@@ -0,0 +1,766 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HD-audio controller helpers
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_register.h>
+#include "local.h"
+
+/* clear CORB read pointer properly */
+static void azx_clear_corbrp(struct hdac_bus *bus)
+{
+ int timeout;
+
+ for (timeout = 1000; timeout > 0; timeout--) {
+ if (snd_hdac_chip_readw(bus, CORBRP) & AZX_CORBRP_RST)
+ break;
+ udelay(1);
+ }
+ if (timeout <= 0)
+ dev_err(bus->dev, "CORB reset timeout#1, CORBRP = %d\n",
+ snd_hdac_chip_readw(bus, CORBRP));
+
+ snd_hdac_chip_writew(bus, CORBRP, 0);
+ for (timeout = 1000; timeout > 0; timeout--) {
+ if (snd_hdac_chip_readw(bus, CORBRP) == 0)
+ break;
+ udelay(1);
+ }
+ if (timeout <= 0)
+ dev_err(bus->dev, "CORB reset timeout#2, CORBRP = %d\n",
+ snd_hdac_chip_readw(bus, CORBRP));
+}
+
+/**
+ * snd_hdac_bus_init_cmd_io - set up CORB/RIRB buffers
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
+{
+ WARN_ON_ONCE(!bus->rb.area);
+
+ guard(spinlock_irq)(&bus->reg_lock);
+ /* CORB set up */
+ bus->corb.addr = bus->rb.addr;
+ bus->corb.buf = (__le32 *)bus->rb.area;
+ snd_hdac_chip_writel(bus, CORBLBASE, (u32)bus->corb.addr);
+ snd_hdac_chip_writel(bus, CORBUBASE, upper_32_bits(bus->corb.addr));
+
+ /* set the corb size to 256 entries (ULI requires explicitly) */
+ snd_hdac_chip_writeb(bus, CORBSIZE, 0x02);
+ /* set the corb write pointer to 0 */
+ snd_hdac_chip_writew(bus, CORBWP, 0);
+
+ /* reset the corb hw read pointer */
+ snd_hdac_chip_writew(bus, CORBRP, AZX_CORBRP_RST);
+ if (!bus->corbrp_self_clear)
+ azx_clear_corbrp(bus);
+
+ /* enable corb dma */
+ if (!bus->use_pio_for_commands)
+ snd_hdac_chip_writeb(bus, CORBCTL, AZX_CORBCTL_RUN);
+
+ /* RIRB set up */
+ bus->rirb.addr = bus->rb.addr + 2048;
+ bus->rirb.buf = (__le32 *)(bus->rb.area + 2048);
+ bus->rirb.wp = bus->rirb.rp = 0;
+ memset(bus->rirb.cmds, 0, sizeof(bus->rirb.cmds));
+ snd_hdac_chip_writel(bus, RIRBLBASE, (u32)bus->rirb.addr);
+ snd_hdac_chip_writel(bus, RIRBUBASE, upper_32_bits(bus->rirb.addr));
+
+ /* set the rirb size to 256 entries (ULI requires explicitly) */
+ snd_hdac_chip_writeb(bus, RIRBSIZE, 0x02);
+ /* reset the rirb hw write pointer */
+ snd_hdac_chip_writew(bus, RIRBWP, AZX_RIRBWP_RST);
+ /* set N=1, get RIRB response interrupt for new entry */
+ snd_hdac_chip_writew(bus, RINTCNT, 1);
+ /* enable rirb dma and response irq */
+ if (bus->not_use_interrupts)
+ snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN);
+ else
+ snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
+ /* Accept unsolicited responses */
+ snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_init_cmd_io);
+
+/* wait for cmd dmas till they are stopped */
+static void hdac_wait_for_cmd_dmas(struct hdac_bus *bus)
+{
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while ((snd_hdac_chip_readb(bus, RIRBCTL) & AZX_RBCTL_DMA_EN)
+ && time_before(jiffies, timeout))
+ udelay(10);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while ((snd_hdac_chip_readb(bus, CORBCTL) & AZX_CORBCTL_RUN)
+ && time_before(jiffies, timeout))
+ udelay(10);
+}
+
+/**
+ * snd_hdac_bus_stop_cmd_io - clean up CORB/RIRB buffers
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_bus_stop_cmd_io(struct hdac_bus *bus)
+{
+ scoped_guard(spinlock_irq, &bus->reg_lock) {
+ /* disable ringbuffer DMAs */
+ snd_hdac_chip_writeb(bus, RIRBCTL, 0);
+ snd_hdac_chip_writeb(bus, CORBCTL, 0);
+ }
+
+ hdac_wait_for_cmd_dmas(bus);
+
+ guard(spinlock_irq)(&bus->reg_lock);
+ /* disable unsolicited responses */
+ snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_cmd_io);
+
+static unsigned int azx_command_addr(u32 cmd)
+{
+ unsigned int addr = cmd >> 28;
+
+ if (snd_BUG_ON(addr >= HDA_MAX_CODECS))
+ addr = 0;
+ return addr;
+}
+
+/* receive an Immediate Response with PIO */
+static int snd_hdac_bus_wait_for_pio_response(struct hdac_bus *bus,
+ unsigned int addr)
+{
+ int timeout = 50;
+
+ while (timeout--) {
+ /* check IRV bit */
+ if (snd_hdac_chip_readw(bus, IRS) & AZX_IRS_VALID) {
+ /* reuse rirb.res as the response return value */
+ bus->rirb.res[addr] = snd_hdac_chip_readl(bus, IR);
+ return 0;
+ }
+ udelay(1);
+ }
+
+ dev_dbg_ratelimited(bus->dev, "get_response_pio timeout: IRS=%#x\n",
+ snd_hdac_chip_readw(bus, IRS));
+
+ bus->rirb.res[addr] = -1;
+
+ return -EIO;
+}
+
+/**
+ * snd_hdac_bus_send_cmd_pio - send a command verb via Immediate Command
+ * @bus: HD-audio core bus
+ * @val: encoded verb value to send
+ *
+ * Returns zero for success or a negative error code.
+ */
+static int snd_hdac_bus_send_cmd_pio(struct hdac_bus *bus, unsigned int val)
+{
+ unsigned int addr = azx_command_addr(val);
+ int timeout = 50;
+
+ guard(spinlock_irq)(&bus->reg_lock);
+
+ while (timeout--) {
+ /* check ICB bit */
+ if (!((snd_hdac_chip_readw(bus, IRS) & AZX_IRS_BUSY))) {
+ /* Clear IRV bit */
+ snd_hdac_chip_updatew(bus, IRS, AZX_IRS_VALID, AZX_IRS_VALID);
+ snd_hdac_chip_writel(bus, IC, val);
+ /* Set ICB bit */
+ snd_hdac_chip_updatew(bus, IRS, AZX_IRS_BUSY, AZX_IRS_BUSY);
+
+ return snd_hdac_bus_wait_for_pio_response(bus, addr);
+ }
+ udelay(1);
+ }
+
+ dev_dbg_ratelimited(bus->dev, "send_cmd_pio timeout: IRS=%#x, val=%#x\n",
+ snd_hdac_chip_readw(bus, IRS), val);
+
+ return -EIO;
+}
+
+/**
+ * snd_hdac_bus_get_response_pio - receive a response via Immediate Response
+ * @bus: HD-audio core bus
+ * @addr: codec address
+ * @res: pointer to store the value, NULL when not needed
+ *
+ * Returns zero if a value is read, or a negative error code.
+ */
+static int snd_hdac_bus_get_response_pio(struct hdac_bus *bus,
+ unsigned int addr, unsigned int *res)
+{
+ if (res)
+ *res = bus->rirb.res[addr];
+
+ return 0;
+}
+
+/**
+ * snd_hdac_bus_send_cmd_corb - send a command verb via CORB
+ * @bus: HD-audio core bus
+ * @val: encoded verb value to send
+ *
+ * Returns zero for success or a negative error code.
+ */
+static int snd_hdac_bus_send_cmd_corb(struct hdac_bus *bus, unsigned int val)
+{
+ unsigned int addr = azx_command_addr(val);
+ unsigned int wp, rp;
+
+ guard(spinlock_irq)(&bus->reg_lock);
+
+ bus->last_cmd[azx_command_addr(val)] = val;
+
+ /* add command to corb */
+ wp = snd_hdac_chip_readw(bus, CORBWP);
+ if (wp == 0xffff) {
+ /* something wrong, controller likely turned to D3 */
+ return -EIO;
+ }
+ wp++;
+ wp %= AZX_MAX_CORB_ENTRIES;
+
+ rp = snd_hdac_chip_readw(bus, CORBRP);
+ if (wp == rp) {
+ /* oops, it's full */
+ return -EAGAIN;
+ }
+
+ bus->rirb.cmds[addr]++;
+ bus->corb.buf[wp] = cpu_to_le32(val);
+ snd_hdac_chip_writew(bus, CORBWP, wp);
+
+ return 0;
+}
+
+#define AZX_RIRB_EX_UNSOL_EV (1<<4)
+
+/**
+ * snd_hdac_bus_update_rirb - retrieve RIRB entries
+ * @bus: HD-audio core bus
+ *
+ * Usually called from interrupt handler.
+ * The caller needs bus->reg_lock spinlock before calling this.
+ */
+void snd_hdac_bus_update_rirb(struct hdac_bus *bus)
+{
+ unsigned int rp, wp;
+ unsigned int addr;
+ u32 res, res_ex;
+
+ wp = snd_hdac_chip_readw(bus, RIRBWP);
+ if (wp == 0xffff) {
+ /* something wrong, controller likely turned to D3 */
+ return;
+ }
+
+ if (wp == bus->rirb.wp)
+ return;
+ bus->rirb.wp = wp;
+
+ while (bus->rirb.rp != wp) {
+ bus->rirb.rp++;
+ bus->rirb.rp %= AZX_MAX_RIRB_ENTRIES;
+
+ rp = bus->rirb.rp << 1; /* an RIRB entry is 8-bytes */
+ res_ex = le32_to_cpu(bus->rirb.buf[rp + 1]);
+ res = le32_to_cpu(bus->rirb.buf[rp]);
+ addr = res_ex & 0xf;
+ if (addr >= HDA_MAX_CODECS) {
+ dev_err(bus->dev,
+ "spurious response %#x:%#x, rp = %d, wp = %d",
+ res, res_ex, bus->rirb.rp, wp);
+ snd_BUG();
+ } else if (res_ex & AZX_RIRB_EX_UNSOL_EV)
+ snd_hdac_bus_queue_event(bus, res, res_ex);
+ else if (bus->rirb.cmds[addr]) {
+ bus->rirb.res[addr] = res;
+ bus->rirb.cmds[addr]--;
+ if (!bus->rirb.cmds[addr] &&
+ waitqueue_active(&bus->rirb_wq))
+ wake_up(&bus->rirb_wq);
+ } else {
+ dev_err_ratelimited(bus->dev,
+ "spurious response %#x:%#x, last cmd=%#08x\n",
+ res, res_ex, bus->last_cmd[addr]);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_update_rirb);
+
+/**
+ * snd_hdac_bus_get_response_rirb - receive a response via RIRB
+ * @bus: HD-audio core bus
+ * @addr: codec address
+ * @res: pointer to store the value, NULL when not needed
+ *
+ * Returns zero if a value is read, or a negative error code.
+ */
+static int snd_hdac_bus_get_response_rirb(struct hdac_bus *bus,
+ unsigned int addr, unsigned int *res)
+{
+ unsigned long timeout;
+ unsigned long loopcounter;
+ wait_queue_entry_t wait;
+ bool warned = false;
+
+ init_wait_entry(&wait, 0);
+ timeout = jiffies + msecs_to_jiffies(1000);
+
+ for (loopcounter = 0;; loopcounter++) {
+ scoped_guard(spinlock_irq, &bus->reg_lock) {
+ if (!bus->polling_mode)
+ prepare_to_wait(&bus->rirb_wq, &wait,
+ TASK_UNINTERRUPTIBLE);
+ if (bus->polling_mode)
+ snd_hdac_bus_update_rirb(bus);
+ if (!bus->rirb.cmds[addr]) {
+ if (res)
+ *res = bus->rirb.res[addr]; /* the last value */
+ if (!bus->polling_mode)
+ finish_wait(&bus->rirb_wq, &wait);
+ return 0;
+ }
+ }
+ if (time_after(jiffies, timeout))
+ break;
+#define LOOP_COUNT_MAX 3000
+ if (!bus->polling_mode) {
+ schedule_timeout(msecs_to_jiffies(2));
+ } else if (bus->needs_damn_long_delay ||
+ loopcounter > LOOP_COUNT_MAX) {
+ if (loopcounter > LOOP_COUNT_MAX && !warned) {
+ dev_dbg_ratelimited(bus->dev,
+ "too slow response, last cmd=%#08x\n",
+ bus->last_cmd[addr]);
+ warned = true;
+ }
+ msleep(2); /* temporary workaround */
+ } else {
+ udelay(10);
+ cond_resched();
+ }
+ }
+
+ if (!bus->polling_mode)
+ finish_wait(&bus->rirb_wq, &wait);
+
+ return -EIO;
+}
+
+/**
+ * snd_hdac_bus_send_cmd - send a command verb via CORB or PIO
+ * @bus: HD-audio core bus
+ * @val: encoded verb value to send
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val)
+{
+ if (bus->use_pio_for_commands)
+ return snd_hdac_bus_send_cmd_pio(bus, val);
+
+ return snd_hdac_bus_send_cmd_corb(bus, val);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_send_cmd);
+
+/**
+ * snd_hdac_bus_get_response - receive a response via RIRB or PIO
+ * @bus: HD-audio core bus
+ * @addr: codec address
+ * @res: pointer to store the value, NULL when not needed
+ *
+ * Returns zero if a value is read, or a negative error code.
+ */
+int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
+ unsigned int *res)
+{
+ if (bus->use_pio_for_commands)
+ return snd_hdac_bus_get_response_pio(bus, addr, res);
+
+ return snd_hdac_bus_get_response_rirb(bus, addr, res);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response);
+
+#define HDAC_MAX_CAPS 10
+/**
+ * snd_hdac_bus_parse_capabilities - parse capability structure
+ * @bus: the pointer to bus object
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus)
+{
+ unsigned int cur_cap;
+ unsigned int offset;
+ unsigned int counter = 0;
+
+ offset = snd_hdac_chip_readw(bus, LLCH);
+
+ /* Lets walk the linked capabilities list */
+ do {
+ cur_cap = _snd_hdac_chip_readl(bus, offset);
+
+ dev_dbg(bus->dev, "Capability version: 0x%x\n",
+ (cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF);
+
+ dev_dbg(bus->dev, "HDA capability ID: 0x%x\n",
+ (cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF);
+
+ if (cur_cap == -1) {
+ dev_dbg(bus->dev, "Invalid capability reg read\n");
+ break;
+ }
+
+ switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) {
+ case AZX_ML_CAP_ID:
+ dev_dbg(bus->dev, "Found ML capability\n");
+ bus->mlcap = bus->remap_addr + offset;
+ break;
+
+ case AZX_GTS_CAP_ID:
+ dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset);
+ bus->gtscap = bus->remap_addr + offset;
+ break;
+
+ case AZX_PP_CAP_ID:
+ /* PP capability found, the Audio DSP is present */
+ dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset);
+ bus->ppcap = bus->remap_addr + offset;
+ break;
+
+ case AZX_SPB_CAP_ID:
+ /* SPIB capability found, handler function */
+ dev_dbg(bus->dev, "Found SPB capability\n");
+ bus->spbcap = bus->remap_addr + offset;
+ break;
+
+ case AZX_DRSM_CAP_ID:
+ /* DMA resume capability found, handler function */
+ dev_dbg(bus->dev, "Found DRSM capability\n");
+ bus->drsmcap = bus->remap_addr + offset;
+ break;
+
+ default:
+ dev_err(bus->dev, "Unknown capability %d\n", cur_cap);
+ cur_cap = 0;
+ break;
+ }
+
+ counter++;
+
+ if (counter > HDAC_MAX_CAPS) {
+ dev_err(bus->dev, "We exceeded HDAC capabilities!!!\n");
+ break;
+ }
+
+ /* read the offset of next capability */
+ offset = cur_cap & AZX_CAP_HDR_NXT_PTR_MASK;
+
+ } while (offset);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_parse_capabilities);
+
+/*
+ * Lowlevel interface
+ */
+
+/**
+ * snd_hdac_bus_enter_link_reset - enter link reset
+ * @bus: HD-audio core bus
+ *
+ * Enter to the link reset state.
+ */
+void snd_hdac_bus_enter_link_reset(struct hdac_bus *bus)
+{
+ unsigned long timeout;
+
+ /* reset controller */
+ snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_RESET, 0);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while ((snd_hdac_chip_readb(bus, GCTL) & AZX_GCTL_RESET) &&
+ time_before(jiffies, timeout))
+ usleep_range(500, 1000);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_enter_link_reset);
+
+/**
+ * snd_hdac_bus_exit_link_reset - exit link reset
+ * @bus: HD-audio core bus
+ *
+ * Exit from the link reset state.
+ */
+void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus)
+{
+ unsigned long timeout;
+
+ snd_hdac_chip_updateb(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+ while (!snd_hdac_chip_readb(bus, GCTL) && time_before(jiffies, timeout))
+ usleep_range(500, 1000);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_exit_link_reset);
+
+/* reset codec link */
+int snd_hdac_bus_reset_link(struct hdac_bus *bus, bool full_reset)
+{
+ if (!full_reset)
+ goto skip_reset;
+
+ /* clear STATESTS if not in reset */
+ if (snd_hdac_chip_readb(bus, GCTL) & AZX_GCTL_RESET)
+ snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK);
+
+ /* reset controller */
+ snd_hdac_bus_enter_link_reset(bus);
+
+ /* delay for >= 100us for codec PLL to settle per spec
+ * Rev 0.9 section 5.5.1
+ */
+ usleep_range(500, 1000);
+
+ /* Bring controller out of reset */
+ snd_hdac_bus_exit_link_reset(bus);
+
+ /* Brent Chartrand said to wait >= 540us for codecs to initialize */
+ usleep_range(1000, 1200);
+
+ skip_reset:
+ /* check to see if controller is ready */
+ if (!snd_hdac_chip_readb(bus, GCTL)) {
+ dev_dbg(bus->dev, "controller not ready!\n");
+ return -EBUSY;
+ }
+
+ /* detect codecs */
+ if (!bus->codec_mask) {
+ bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS);
+ dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_reset_link);
+
+/* enable interrupts */
+static void azx_int_enable(struct hdac_bus *bus)
+{
+ /* enable controller CIE and GIE */
+ snd_hdac_chip_updatel(bus, INTCTL,
+ AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN,
+ AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN);
+}
+
+/* disable interrupts */
+static void azx_int_disable(struct hdac_bus *bus)
+{
+ struct hdac_stream *azx_dev;
+
+ /* disable interrupts in stream descriptor */
+ list_for_each_entry(azx_dev, &bus->stream_list, list)
+ snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0);
+
+ /* disable SIE for all streams & disable controller CIE and GIE */
+ snd_hdac_chip_writel(bus, INTCTL, 0);
+}
+
+/* clear interrupts */
+static void azx_int_clear(struct hdac_bus *bus)
+{
+ struct hdac_stream *azx_dev;
+
+ /* clear stream status */
+ list_for_each_entry(azx_dev, &bus->stream_list, list)
+ snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
+
+ /* clear STATESTS */
+ snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK);
+
+ /* clear rirb status */
+ snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
+
+ /* clear int status */
+ snd_hdac_chip_writel(bus, INTSTS, AZX_INT_CTRL_EN | AZX_INT_ALL_STREAM);
+}
+
+/**
+ * snd_hdac_bus_init_chip - reset and start the controller registers
+ * @bus: HD-audio core bus
+ * @full_reset: Do full reset
+ */
+bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset)
+{
+ if (bus->chip_init)
+ return false;
+
+ /* reset controller */
+ snd_hdac_bus_reset_link(bus, full_reset);
+
+ /* clear interrupts */
+ azx_int_clear(bus);
+
+ /* initialize the codec command I/O */
+ snd_hdac_bus_init_cmd_io(bus);
+
+ /* enable interrupts after CORB/RIRB buffers are initialized above */
+ azx_int_enable(bus);
+
+ /* program the position buffer */
+ if (bus->use_posbuf && bus->posbuf.addr) {
+ snd_hdac_chip_writel(bus, DPLBASE, (u32)bus->posbuf.addr);
+ snd_hdac_chip_writel(bus, DPUBASE, upper_32_bits(bus->posbuf.addr));
+ }
+
+ bus->chip_init = true;
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_init_chip);
+
+/**
+ * snd_hdac_bus_stop_chip - disable the whole IRQ and I/Os
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_bus_stop_chip(struct hdac_bus *bus)
+{
+ if (!bus->chip_init)
+ return;
+
+ /* disable interrupts */
+ azx_int_disable(bus);
+ azx_int_clear(bus);
+
+ /* disable CORB/RIRB */
+ snd_hdac_bus_stop_cmd_io(bus);
+
+ /* disable position buffer */
+ if (bus->posbuf.addr) {
+ snd_hdac_chip_writel(bus, DPLBASE, 0);
+ snd_hdac_chip_writel(bus, DPUBASE, 0);
+ }
+
+ bus->chip_init = false;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_chip);
+
+/**
+ * snd_hdac_bus_handle_stream_irq - interrupt handler for streams
+ * @bus: HD-audio core bus
+ * @status: INTSTS register value
+ * @ack: callback to be called for woken streams
+ *
+ * Returns the bits of handled streams, or zero if no stream is handled.
+ */
+int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
+ void (*ack)(struct hdac_bus *,
+ struct hdac_stream *))
+{
+ struct hdac_stream *azx_dev;
+ u8 sd_status;
+ int handled = 0;
+
+ list_for_each_entry(azx_dev, &bus->stream_list, list) {
+ if (status & azx_dev->sd_int_sta_mask) {
+ sd_status = snd_hdac_stream_readb(azx_dev, SD_STS);
+ snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
+ handled |= 1 << azx_dev->index;
+ if ((!azx_dev->substream && !azx_dev->cstream) ||
+ !azx_dev->running || !(sd_status & SD_INT_COMPLETE))
+ continue;
+ if (ack)
+ ack(bus, azx_dev);
+ }
+ }
+ return handled;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_handle_stream_irq);
+
+/**
+ * snd_hdac_bus_alloc_stream_pages - allocate BDL and other buffers
+ * @bus: HD-audio core bus
+ *
+ * Call this after assigning the all streams.
+ * Returns zero for success, or a negative error code.
+ */
+int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus)
+{
+ struct hdac_stream *s;
+ int num_streams = 0;
+ int dma_type = bus->dma_type ? bus->dma_type : SNDRV_DMA_TYPE_DEV;
+ int err;
+
+ list_for_each_entry(s, &bus->stream_list, list) {
+ /* allocate memory for the BDL for each stream */
+ err = snd_dma_alloc_pages(dma_type, bus->dev,
+ BDL_SIZE, &s->bdl);
+ num_streams++;
+ if (err < 0)
+ return -ENOMEM;
+ }
+
+ if (WARN_ON(!num_streams))
+ return -EINVAL;
+ /* allocate memory for the position buffer */
+ err = snd_dma_alloc_pages(dma_type, bus->dev,
+ num_streams * 8, &bus->posbuf);
+ if (err < 0)
+ return -ENOMEM;
+ list_for_each_entry(s, &bus->stream_list, list)
+ s->posbuf = (__le32 *)(bus->posbuf.area + s->index * 8);
+
+ /* single page (at least 4096 bytes) must suffice for both ringbuffes */
+ return snd_dma_alloc_pages(dma_type, bus->dev, PAGE_SIZE, &bus->rb);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_alloc_stream_pages);
+
+/**
+ * snd_hdac_bus_free_stream_pages - release BDL and other buffers
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus)
+{
+ struct hdac_stream *s;
+
+ list_for_each_entry(s, &bus->stream_list, list) {
+ if (s->bdl.area)
+ snd_dma_free_pages(&s->bdl);
+ }
+
+ if (bus->rb.area)
+ snd_dma_free_pages(&bus->rb);
+ if (bus->posbuf.area)
+ snd_dma_free_pages(&bus->posbuf);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_free_stream_pages);
+
+/**
+ * snd_hdac_bus_link_power - power up/down codec link
+ * @codec: HD-audio device
+ * @enable: whether to power-up the link
+ */
+void snd_hdac_bus_link_power(struct hdac_device *codec, bool enable)
+{
+ if (enable)
+ set_bit(codec->addr, &codec->bus->codec_powered);
+ else
+ clear_bit(codec->addr, &codec->bus->codec_powered);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_link_power);
diff --git a/sound/hda/core/device.c b/sound/hda/core/device.c
new file mode 100644
index 000000000000..160c8d0453b0
--- /dev/null
+++ b/sound/hda/core/device.c
@@ -0,0 +1,1167 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HD-audio codec core device
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/pm_runtime.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_regmap.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "local.h"
+
+static void setup_fg_nodes(struct hdac_device *codec);
+static int get_codec_vendor_name(struct hdac_device *codec);
+
+static void default_release(struct device *dev)
+{
+ snd_hdac_device_exit(dev_to_hdac_dev(dev));
+}
+
+/**
+ * snd_hdac_device_init - initialize the HD-audio codec base device
+ * @codec: device to initialize
+ * @bus: but to attach
+ * @name: device name string
+ * @addr: codec address
+ *
+ * Returns zero for success or a negative error code.
+ *
+ * This function increments the runtime PM counter and marks it active.
+ * The caller needs to turn it off appropriately later.
+ *
+ * The caller needs to set the device's release op properly by itself.
+ */
+int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus,
+ const char *name, unsigned int addr)
+{
+ struct device *dev;
+ hda_nid_t fg;
+ int err;
+
+ dev = &codec->dev;
+ device_initialize(dev);
+ dev->parent = bus->dev;
+ dev->bus = &snd_hda_bus_type;
+ dev->release = default_release;
+ dev->groups = hdac_dev_attr_groups;
+ dev_set_name(dev, "%s", name);
+ device_enable_async_suspend(dev);
+
+ codec->bus = bus;
+ codec->addr = addr;
+ codec->type = HDA_DEV_CORE;
+ mutex_init(&codec->widget_lock);
+ mutex_init(&codec->regmap_lock);
+ pm_runtime_set_active(&codec->dev);
+ pm_runtime_get_noresume(&codec->dev);
+ atomic_set(&codec->in_pm, 0);
+
+ err = snd_hdac_bus_add_device(bus, codec);
+ if (err < 0)
+ goto error;
+
+ /* fill parameters */
+ codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
+ AC_PAR_VENDOR_ID);
+ if (codec->vendor_id == -1) {
+ /* read again, hopefully the access method was corrected
+ * in the last read...
+ */
+ codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
+ AC_PAR_VENDOR_ID);
+ }
+
+ codec->subsystem_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
+ AC_PAR_SUBSYSTEM_ID);
+ codec->revision_id = snd_hdac_read_parm(codec, AC_NODE_ROOT,
+ AC_PAR_REV_ID);
+
+ setup_fg_nodes(codec);
+ if (!codec->afg && !codec->mfg) {
+ dev_err(dev, "no AFG or MFG node found\n");
+ err = -ENODEV;
+ goto error;
+ }
+
+ fg = codec->afg ? codec->afg : codec->mfg;
+
+ err = snd_hdac_refresh_widgets(codec);
+ if (err < 0)
+ goto error;
+
+ codec->power_caps = snd_hdac_read_parm(codec, fg, AC_PAR_POWER_STATE);
+ /* reread ssid if not set by parameter */
+ if (codec->subsystem_id == -1 || codec->subsystem_id == 0)
+ snd_hdac_read(codec, fg, AC_VERB_GET_SUBSYSTEM_ID, 0,
+ &codec->subsystem_id);
+
+ err = get_codec_vendor_name(codec);
+ if (err < 0)
+ goto error;
+
+ codec->chip_name = kasprintf(GFP_KERNEL, "ID %x",
+ codec->vendor_id & 0xffff);
+ if (!codec->chip_name) {
+ err = -ENOMEM;
+ goto error;
+ }
+
+ return 0;
+
+ error:
+ put_device(&codec->dev);
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_init);
+
+/**
+ * snd_hdac_device_exit - clean up the HD-audio codec base device
+ * @codec: device to clean up
+ */
+void snd_hdac_device_exit(struct hdac_device *codec)
+{
+ pm_runtime_put_noidle(&codec->dev);
+ /* keep balance of runtime PM child_count in parent device */
+ pm_runtime_set_suspended(&codec->dev);
+ snd_hdac_bus_remove_device(codec->bus, codec);
+ kfree(codec->vendor_name);
+ kfree(codec->chip_name);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_exit);
+
+/**
+ * snd_hdac_device_register - register the hd-audio codec base device
+ * @codec: the device to register
+ */
+int snd_hdac_device_register(struct hdac_device *codec)
+{
+ int err;
+
+ err = device_add(&codec->dev);
+ if (err < 0)
+ return err;
+ scoped_guard(mutex, &codec->widget_lock) {
+ err = hda_widget_sysfs_init(codec);
+ }
+ if (err < 0) {
+ device_del(&codec->dev);
+ return err;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_register);
+
+/**
+ * snd_hdac_device_unregister - unregister the hd-audio codec base device
+ * @codec: the device to unregister
+ */
+void snd_hdac_device_unregister(struct hdac_device *codec)
+{
+ if (device_is_registered(&codec->dev)) {
+ scoped_guard(mutex, &codec->widget_lock) {
+ hda_widget_sysfs_exit(codec);
+ }
+ device_del(&codec->dev);
+ snd_hdac_bus_remove_device(codec->bus, codec);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_unregister);
+
+/**
+ * snd_hdac_device_set_chip_name - set/update the codec name
+ * @codec: the HDAC device
+ * @name: name string to set
+ *
+ * Returns 0 if the name is set or updated, or a negative error code.
+ */
+int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name)
+{
+ char *newname;
+
+ if (!name)
+ return 0;
+ newname = kstrdup(name, GFP_KERNEL);
+ if (!newname)
+ return -ENOMEM;
+ kfree(codec->chip_name);
+ codec->chip_name = newname;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_set_chip_name);
+
+/**
+ * snd_hdac_codec_modalias - give the module alias name
+ * @codec: HDAC device
+ * @buf: string buffer to store
+ * @size: string buffer size
+ *
+ * Returns the size of string, like snprintf(), or a negative error code.
+ */
+int snd_hdac_codec_modalias(const struct hdac_device *codec, char *buf, size_t size)
+{
+ return scnprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n",
+ codec->vendor_id, codec->revision_id, codec->type);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias);
+
+/**
+ * snd_hdac_make_cmd - compose a 32bit command word to be sent to the
+ * HD-audio controller
+ * @codec: the codec object
+ * @nid: NID to encode
+ * @verb: verb to encode
+ * @parm: parameter to encode
+ *
+ * Return an encoded command verb or -1 for error.
+ */
+static unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
+ unsigned int verb, unsigned int parm)
+{
+ u32 val, addr;
+
+ addr = codec->addr;
+ if ((addr & ~0xf) || (nid & ~0x7f) ||
+ (verb & ~0xfff) || (parm & ~0xffff)) {
+ dev_err(&codec->dev, "out of range cmd %x:%x:%x:%x\n",
+ addr, nid, verb, parm);
+ return -1;
+ }
+
+ val = addr << 28;
+ val |= (u32)nid << 20;
+ val |= verb << 8;
+ val |= parm;
+ return val;
+}
+
+/**
+ * snd_hdac_exec_verb - execute an encoded verb
+ * @codec: the codec object
+ * @cmd: encoded verb to execute
+ * @flags: optional flags, pass zero for default
+ * @res: the pointer to store the result, NULL if running async
+ *
+ * Returns zero if successful, or a negative error code.
+ *
+ * This calls the exec_verb op when set in hdac_codec. If not,
+ * call the default snd_hdac_bus_exec_verb().
+ */
+int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
+ unsigned int flags, unsigned int *res)
+{
+ if (codec->exec_verb)
+ return codec->exec_verb(codec, cmd, flags, res);
+ return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res);
+}
+
+
+/**
+ * snd_hdac_read - execute a verb
+ * @codec: the codec object
+ * @nid: NID to execute a verb
+ * @verb: verb to execute
+ * @parm: parameter for a verb
+ * @res: the pointer to store the result, NULL if running async
+ *
+ * Returns zero if successful, or a negative error code.
+ */
+int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid,
+ unsigned int verb, unsigned int parm, unsigned int *res)
+{
+ unsigned int cmd = snd_hdac_make_cmd(codec, nid, verb, parm);
+
+ return snd_hdac_exec_verb(codec, cmd, 0, res);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_read);
+
+/**
+ * _snd_hdac_read_parm - read a parmeter
+ * @codec: the codec object
+ * @nid: NID to read a parameter
+ * @parm: parameter to read
+ * @res: pointer to store the read value
+ *
+ * This function returns zero or an error unlike snd_hdac_read_parm().
+ */
+int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm,
+ unsigned int *res)
+{
+ unsigned int cmd;
+
+ cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm;
+ return snd_hdac_regmap_read_raw(codec, cmd, res);
+}
+EXPORT_SYMBOL_GPL(_snd_hdac_read_parm);
+
+/**
+ * snd_hdac_read_parm_uncached - read a codec parameter without caching
+ * @codec: the codec object
+ * @nid: NID to read a parameter
+ * @parm: parameter to read
+ *
+ * Returns -1 for error. If you need to distinguish the error more
+ * strictly, use snd_hdac_read() directly.
+ */
+int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid,
+ int parm)
+{
+ unsigned int cmd, val;
+
+ cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm;
+ if (snd_hdac_regmap_read_raw_uncached(codec, cmd, &val) < 0)
+ return -1;
+ return val;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_read_parm_uncached);
+
+/**
+ * snd_hdac_override_parm - override read-only parameters
+ * @codec: the codec object
+ * @nid: NID for the parameter
+ * @parm: the parameter to change
+ * @val: the parameter value to overwrite
+ */
+int snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid,
+ unsigned int parm, unsigned int val)
+{
+ unsigned int verb = (AC_VERB_PARAMETERS << 8) | (nid << 20) | parm;
+ int err;
+
+ if (!codec->regmap)
+ return -EINVAL;
+
+ codec->caps_overwriting = true;
+ err = snd_hdac_regmap_write_raw(codec, verb, val);
+ codec->caps_overwriting = false;
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_override_parm);
+
+/**
+ * snd_hdac_get_sub_nodes - get start NID and number of subtree nodes
+ * @codec: the codec object
+ * @nid: NID to inspect
+ * @start_id: the pointer to store the starting NID
+ *
+ * Returns the number of subtree nodes or zero if not found.
+ * This function reads parameters always without caching.
+ */
+int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid,
+ hda_nid_t *start_id)
+{
+ unsigned int parm;
+
+ parm = snd_hdac_read_parm_uncached(codec, nid, AC_PAR_NODE_COUNT);
+ if (parm == -1) {
+ *start_id = 0;
+ return 0;
+ }
+ *start_id = (parm >> 16) & 0x7fff;
+ return (int)(parm & 0x7fff);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_sub_nodes);
+
+/*
+ * look for an AFG and MFG nodes
+ */
+static void setup_fg_nodes(struct hdac_device *codec)
+{
+ int i, total_nodes, function_id;
+ hda_nid_t nid;
+
+ total_nodes = snd_hdac_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
+ for (i = 0; i < total_nodes; i++, nid++) {
+ function_id = snd_hdac_read_parm(codec, nid,
+ AC_PAR_FUNCTION_TYPE);
+ switch (function_id & 0xff) {
+ case AC_GRP_AUDIO_FUNCTION:
+ codec->afg = nid;
+ codec->afg_function_id = function_id & 0xff;
+ codec->afg_unsol = (function_id >> 8) & 1;
+ break;
+ case AC_GRP_MODEM_FUNCTION:
+ codec->mfg = nid;
+ codec->mfg_function_id = function_id & 0xff;
+ codec->mfg_unsol = (function_id >> 8) & 1;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/**
+ * snd_hdac_refresh_widgets - Reset the widget start/end nodes
+ * @codec: the codec object
+ */
+int snd_hdac_refresh_widgets(struct hdac_device *codec)
+{
+ hda_nid_t start_nid;
+ int nums, err = 0;
+
+ /*
+ * Serialize against multiple threads trying to update the sysfs
+ * widgets array.
+ */
+ guard(mutex)(&codec->widget_lock);
+ nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid);
+ if (!start_nid || nums <= 0 || nums >= 0xff) {
+ dev_err(&codec->dev, "cannot read sub nodes for FG 0x%02x\n",
+ codec->afg);
+ return -EINVAL;
+ }
+
+ err = hda_widget_sysfs_reinit(codec, start_nid, nums);
+ if (err < 0)
+ return err;
+
+ codec->num_nodes = nums;
+ codec->start_nid = start_nid;
+ codec->end_nid = start_nid + nums;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets);
+
+/* return CONNLIST_LEN parameter of the given widget */
+static unsigned int get_num_conns(struct hdac_device *codec, hda_nid_t nid)
+{
+ unsigned int wcaps = snd_hdac_get_wcaps(codec, nid);
+ unsigned int parm;
+
+ if (!(wcaps & AC_WCAP_CONN_LIST) &&
+ snd_hdac_get_wcaps_type(wcaps) != AC_WID_VOL_KNB)
+ return 0;
+
+ parm = snd_hdac_read_parm(codec, nid, AC_PAR_CONNLIST_LEN);
+ if (parm == -1)
+ parm = 0;
+ return parm;
+}
+
+/**
+ * snd_hdac_get_connections - get a widget connection list
+ * @codec: the codec object
+ * @nid: NID
+ * @conn_list: the array to store the results, can be NULL
+ * @max_conns: the max size of the given array
+ *
+ * Returns the number of connected widgets, zero for no connection, or a
+ * negative error code. When the number of elements don't fit with the
+ * given array size, it returns -ENOSPC.
+ *
+ * When @conn_list is NULL, it just checks the number of connections.
+ */
+int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid,
+ hda_nid_t *conn_list, int max_conns)
+{
+ unsigned int parm;
+ int i, conn_len, conns, err;
+ unsigned int shift, num_elems, mask;
+ hda_nid_t prev_nid;
+ int null_count = 0;
+
+ parm = get_num_conns(codec, nid);
+ if (!parm)
+ return 0;
+
+ if (parm & AC_CLIST_LONG) {
+ /* long form */
+ shift = 16;
+ num_elems = 2;
+ } else {
+ /* short form */
+ shift = 8;
+ num_elems = 4;
+ }
+ conn_len = parm & AC_CLIST_LENGTH;
+ mask = (1 << (shift-1)) - 1;
+
+ if (!conn_len)
+ return 0; /* no connection */
+
+ if (conn_len == 1) {
+ /* single connection */
+ err = snd_hdac_read(codec, nid, AC_VERB_GET_CONNECT_LIST, 0,
+ &parm);
+ if (err < 0)
+ return err;
+ if (conn_list)
+ conn_list[0] = parm & mask;
+ return 1;
+ }
+
+ /* multi connection */
+ conns = 0;
+ prev_nid = 0;
+ for (i = 0; i < conn_len; i++) {
+ int range_val;
+ hda_nid_t val, n;
+
+ if (i % num_elems == 0) {
+ err = snd_hdac_read(codec, nid,
+ AC_VERB_GET_CONNECT_LIST, i,
+ &parm);
+ if (err < 0)
+ return -EIO;
+ }
+ range_val = !!(parm & (1 << (shift-1))); /* ranges */
+ val = parm & mask;
+ if (val == 0 && null_count++) { /* no second chance */
+ dev_dbg(&codec->dev,
+ "invalid CONNECT_LIST verb %x[%i]:%x\n",
+ nid, i, parm);
+ return 0;
+ }
+ parm >>= shift;
+ if (range_val) {
+ /* ranges between the previous and this one */
+ if (!prev_nid || prev_nid >= val) {
+ dev_warn(&codec->dev,
+ "invalid dep_range_val %x:%x\n",
+ prev_nid, val);
+ continue;
+ }
+ for (n = prev_nid + 1; n <= val; n++) {
+ if (conn_list) {
+ if (conns >= max_conns)
+ return -ENOSPC;
+ conn_list[conns] = n;
+ }
+ conns++;
+ }
+ } else {
+ if (conn_list) {
+ if (conns >= max_conns)
+ return -ENOSPC;
+ conn_list[conns] = val;
+ }
+ conns++;
+ }
+ prev_nid = val;
+ }
+ return conns;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_connections);
+
+#ifdef CONFIG_PM
+/**
+ * snd_hdac_power_up - power up the codec
+ * @codec: the codec object
+ *
+ * This function calls the runtime PM helper to power up the given codec.
+ * Unlike snd_hdac_power_up_pm(), you should call this only for the code
+ * path that isn't included in PM path. Otherwise it gets stuck.
+ *
+ * Returns zero if successful, or a negative error code.
+ */
+int snd_hdac_power_up(struct hdac_device *codec)
+{
+ return pm_runtime_get_sync(&codec->dev);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_power_up);
+
+/**
+ * snd_hdac_power_down - power down the codec
+ * @codec: the codec object
+ *
+ * Returns zero if successful, or a negative error code.
+ */
+int snd_hdac_power_down(struct hdac_device *codec)
+{
+ struct device *dev = &codec->dev;
+
+ return pm_runtime_put_autosuspend(dev);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_power_down);
+
+/**
+ * snd_hdac_power_up_pm - power up the codec
+ * @codec: the codec object
+ *
+ * This function can be called in a recursive code path like init code
+ * which may be called by PM suspend/resume again. OTOH, if a power-up
+ * call must wake up the sleeper (e.g. in a kctl callback), use
+ * snd_hdac_power_up() instead.
+ *
+ * Returns zero if successful, or a negative error code.
+ */
+int snd_hdac_power_up_pm(struct hdac_device *codec)
+{
+ if (!atomic_inc_not_zero(&codec->in_pm))
+ return snd_hdac_power_up(codec);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm);
+
+/* like snd_hdac_power_up_pm(), but only increment the pm count when
+ * already powered up. Returns -1 if not powered up, 1 if incremented
+ * or 0 if unchanged. Only used in hdac_regmap.c
+ */
+int snd_hdac_keep_power_up(struct hdac_device *codec)
+{
+ if (!atomic_inc_not_zero(&codec->in_pm)) {
+ int ret = pm_runtime_get_if_active(&codec->dev);
+ if (!ret)
+ return -1;
+ if (ret < 0)
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * snd_hdac_power_down_pm - power down the codec
+ * @codec: the codec object
+ *
+ * Like snd_hdac_power_up_pm(), this function is used in a recursive
+ * code path like init code which may be called by PM suspend/resume again.
+ *
+ * Returns zero if successful, or a negative error code.
+ */
+int snd_hdac_power_down_pm(struct hdac_device *codec)
+{
+ if (atomic_dec_if_positive(&codec->in_pm) < 0)
+ return snd_hdac_power_down(codec);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm);
+#endif
+
+/* codec vendor labels */
+struct hda_vendor_id {
+ unsigned int id;
+ const char *name;
+};
+
+static const struct hda_vendor_id hda_vendor_ids[] = {
+ { 0x0014, "Loongson" },
+ { 0x1002, "ATI" },
+ { 0x1013, "Cirrus Logic" },
+ { 0x1057, "Motorola" },
+ { 0x1095, "Silicon Image" },
+ { 0x10de, "Nvidia" },
+ { 0x10ec, "Realtek" },
+ { 0x1102, "Creative" },
+ { 0x1106, "VIA" },
+ { 0x111d, "IDT" },
+ { 0x11c1, "LSI" },
+ { 0x11d4, "Analog Devices" },
+ { 0x13f6, "C-Media" },
+ { 0x14f1, "Conexant" },
+ { 0x17e8, "Chrontel" },
+ { 0x1854, "LG" },
+ { 0x19e5, "Huawei" },
+ { 0x1aec, "Wolfson Microelectronics" },
+ { 0x1af4, "QEMU" },
+ { 0x1fa8, "Senarytech" },
+ { 0x434d, "C-Media" },
+ { 0x8086, "Intel" },
+ { 0x8384, "SigmaTel" },
+ {} /* terminator */
+};
+
+/* store the codec vendor name */
+static int get_codec_vendor_name(struct hdac_device *codec)
+{
+ const struct hda_vendor_id *c;
+ u16 vendor_id = codec->vendor_id >> 16;
+
+ for (c = hda_vendor_ids; c->id; c++) {
+ if (c->id == vendor_id) {
+ codec->vendor_name = kstrdup(c->name, GFP_KERNEL);
+ return codec->vendor_name ? 0 : -ENOMEM;
+ }
+ }
+
+ codec->vendor_name = kasprintf(GFP_KERNEL, "Generic %04x", vendor_id);
+ return codec->vendor_name ? 0 : -ENOMEM;
+}
+
+/*
+ * stream formats
+ */
+struct hda_rate_tbl {
+ unsigned int hz;
+ unsigned int alsa_bits;
+ unsigned int hda_fmt;
+};
+
+/* rate = base * mult / div */
+#define HDA_RATE(base, mult, div) \
+ (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \
+ (((div) - 1) << AC_FMT_DIV_SHIFT))
+
+static const struct hda_rate_tbl rate_bits[] = {
+ /* rate in Hz, ALSA rate bitmask, HDA format value */
+
+ /* autodetected value used in snd_hda_query_supported_pcm */
+ { 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) },
+ { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) },
+ { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) },
+ { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) },
+ { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) },
+ { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) },
+ { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) },
+ { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) },
+ { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) },
+ { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) },
+ { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) },
+#define AC_PAR_PCM_RATE_BITS 11
+ /* up to bits 10, 384kHZ isn't supported properly */
+
+ /* not autodetected value */
+ { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) },
+
+ { 0 } /* terminator */
+};
+
+static snd_pcm_format_t snd_hdac_format_normalize(snd_pcm_format_t format)
+{
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S20_LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ return SNDRV_PCM_FORMAT_S32_LE;
+
+ case SNDRV_PCM_FORMAT_U20_LE:
+ case SNDRV_PCM_FORMAT_U24_LE:
+ return SNDRV_PCM_FORMAT_U32_LE;
+
+ case SNDRV_PCM_FORMAT_S20_BE:
+ case SNDRV_PCM_FORMAT_S24_BE:
+ return SNDRV_PCM_FORMAT_S32_BE;
+
+ case SNDRV_PCM_FORMAT_U20_BE:
+ case SNDRV_PCM_FORMAT_U24_BE:
+ return SNDRV_PCM_FORMAT_U32_BE;
+
+ default:
+ return format;
+ }
+}
+
+/**
+ * snd_hdac_stream_format_bits - obtain bits per sample value.
+ * @format: the PCM format.
+ * @subformat: the PCM subformat.
+ * @maxbits: the maximum bits per sample.
+ *
+ * Return: The number of bits per sample.
+ */
+unsigned int snd_hdac_stream_format_bits(snd_pcm_format_t format, snd_pcm_subformat_t subformat,
+ unsigned int maxbits)
+{
+ struct snd_pcm_hw_params params;
+ unsigned int bits;
+
+ memset(&params, 0, sizeof(params));
+
+ params_set_format(&params, snd_hdac_format_normalize(format));
+ snd_mask_set(hw_param_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT),
+ (__force unsigned int)subformat);
+
+ bits = snd_pcm_hw_params_bits(&params);
+ if (maxbits)
+ return min(bits, maxbits);
+ return bits;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_format_bits);
+
+/**
+ * snd_hdac_stream_format - convert format parameters to SDxFMT value.
+ * @channels: the number of channels.
+ * @bits: bits per sample.
+ * @rate: the sample rate.
+ *
+ * Return: The format bitset or zero if invalid.
+ */
+unsigned int snd_hdac_stream_format(unsigned int channels, unsigned int bits, unsigned int rate)
+{
+ unsigned int val = 0;
+ int i;
+
+ for (i = 0; rate_bits[i].hz; i++) {
+ if (rate_bits[i].hz == rate) {
+ val = rate_bits[i].hda_fmt;
+ break;
+ }
+ }
+
+ if (!rate_bits[i].hz)
+ return 0;
+
+ if (channels == 0 || channels > 16)
+ return 0;
+ val |= channels - 1;
+
+ switch (bits) {
+ case 8:
+ val |= AC_FMT_BITS_8;
+ break;
+ case 16:
+ val |= AC_FMT_BITS_16;
+ break;
+ case 20:
+ val |= AC_FMT_BITS_20;
+ break;
+ case 24:
+ val |= AC_FMT_BITS_24;
+ break;
+ case 32:
+ val |= AC_FMT_BITS_32;
+ break;
+ default:
+ return 0;
+ }
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_format);
+
+/**
+ * snd_hdac_spdif_stream_format - convert format parameters to SDxFMT value.
+ * @channels: the number of channels.
+ * @bits: bits per sample.
+ * @rate: the sample rate.
+ * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant).
+ *
+ * Return: The format bitset or zero if invalid.
+ */
+unsigned int snd_hdac_spdif_stream_format(unsigned int channels, unsigned int bits,
+ unsigned int rate, unsigned short spdif_ctls)
+{
+ unsigned int val = snd_hdac_stream_format(channels, bits, rate);
+
+ if (val && spdif_ctls & AC_DIG1_NONAUDIO)
+ val |= AC_FMT_TYPE_NON_PCM;
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_spdif_stream_format);
+
+static unsigned int query_pcm_param(struct hdac_device *codec, hda_nid_t nid)
+{
+ unsigned int val = 0;
+
+ if (nid != codec->afg &&
+ (snd_hdac_get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD))
+ val = snd_hdac_read_parm(codec, nid, AC_PAR_PCM);
+ if (!val || val == -1)
+ val = snd_hdac_read_parm(codec, codec->afg, AC_PAR_PCM);
+ if (!val || val == -1)
+ return 0;
+ return val;
+}
+
+static unsigned int query_stream_param(struct hdac_device *codec, hda_nid_t nid)
+{
+ unsigned int streams = snd_hdac_read_parm(codec, nid, AC_PAR_STREAM);
+
+ if (!streams || streams == -1)
+ streams = snd_hdac_read_parm(codec, codec->afg, AC_PAR_STREAM);
+ if (!streams || streams == -1)
+ return 0;
+ return streams;
+}
+
+/**
+ * snd_hdac_query_supported_pcm - query the supported PCM rates and formats
+ * @codec: the codec object
+ * @nid: NID to query
+ * @ratesp: the pointer to store the detected rate bitflags
+ * @formatsp: the pointer to store the detected formats
+ * @subformatsp: the pointer to store the detected subformats for S32_LE format
+ * @bpsp: the pointer to store the detected format widths
+ *
+ * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp,
+ * @subformatsp or @bpsp argument is ignored.
+ *
+ * Returns 0 if successful, otherwise a negative error code.
+ */
+int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
+ u32 *ratesp, u64 *formatsp, u32 *subformatsp,
+ unsigned int *bpsp)
+{
+ unsigned int i, val, wcaps;
+
+ wcaps = snd_hdac_get_wcaps(codec, nid);
+ val = query_pcm_param(codec, nid);
+
+ if (ratesp) {
+ u32 rates = 0;
+ for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) {
+ if (val & (1 << i))
+ rates |= rate_bits[i].alsa_bits;
+ }
+ if (rates == 0) {
+ dev_err(&codec->dev,
+ "rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)\n",
+ nid, val,
+ (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0);
+ return -EIO;
+ }
+ *ratesp = rates;
+ }
+
+ if (formatsp || subformatsp || bpsp) {
+ unsigned int streams, bps;
+ u32 subformats = 0;
+ u64 formats = 0;
+
+ streams = query_stream_param(codec, nid);
+ if (!streams)
+ return -EIO;
+
+ bps = 0;
+ if (streams & AC_SUPFMT_PCM) {
+ if (val & AC_SUPPCM_BITS_8) {
+ formats |= SNDRV_PCM_FMTBIT_U8;
+ bps = 8;
+ }
+ if (val & AC_SUPPCM_BITS_16) {
+ formats |= SNDRV_PCM_FMTBIT_S16_LE;
+ bps = 16;
+ }
+ if (val & AC_SUPPCM_BITS_20) {
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_20;
+ bps = 20;
+ }
+ if (val & AC_SUPPCM_BITS_24) {
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_24;
+ bps = 24;
+ }
+ if (val & AC_SUPPCM_BITS_32) {
+ if (wcaps & AC_WCAP_DIGITAL) {
+ formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
+ } else {
+ formats |= SNDRV_PCM_FMTBIT_S32_LE;
+ subformats |= SNDRV_PCM_SUBFMTBIT_MSBITS_MAX;
+ bps = 32;
+ }
+ }
+ }
+#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */
+ if (streams & AC_SUPFMT_FLOAT32) {
+ formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
+ if (!bps)
+ bps = 32;
+ }
+#endif
+ if (streams == AC_SUPFMT_AC3) {
+ /* should be exclusive */
+ /* temporary hack: we have still no proper support
+ * for the direct AC3 stream...
+ */
+ formats |= SNDRV_PCM_FMTBIT_U8;
+ bps = 8;
+ }
+ if (formats == 0) {
+ dev_err(&codec->dev,
+ "formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)\n",
+ nid, val,
+ (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0,
+ streams);
+ return -EIO;
+ }
+ if (formatsp)
+ *formatsp = formats;
+ if (subformatsp)
+ *subformatsp = subformats;
+ if (bpsp)
+ *bpsp = bps;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_query_supported_pcm);
+
+/**
+ * snd_hdac_is_supported_format - Check the validity of the format
+ * @codec: the codec object
+ * @nid: NID to check
+ * @format: the HD-audio format value to check
+ *
+ * Check whether the given node supports the format value.
+ *
+ * Returns true if supported, false if not.
+ */
+bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid,
+ unsigned int format)
+{
+ int i;
+ unsigned int val = 0, rate, stream;
+
+ val = query_pcm_param(codec, nid);
+ if (!val)
+ return false;
+
+ rate = format & 0xff00;
+ for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++)
+ if (rate_bits[i].hda_fmt == rate) {
+ if (val & (1 << i))
+ break;
+ return false;
+ }
+ if (i >= AC_PAR_PCM_RATE_BITS)
+ return false;
+
+ stream = query_stream_param(codec, nid);
+ if (!stream)
+ return false;
+
+ if (stream & AC_SUPFMT_PCM) {
+ switch (format & 0xf0) {
+ case 0x00:
+ if (!(val & AC_SUPPCM_BITS_8))
+ return false;
+ break;
+ case 0x10:
+ if (!(val & AC_SUPPCM_BITS_16))
+ return false;
+ break;
+ case 0x20:
+ if (!(val & AC_SUPPCM_BITS_20))
+ return false;
+ break;
+ case 0x30:
+ if (!(val & AC_SUPPCM_BITS_24))
+ return false;
+ break;
+ case 0x40:
+ if (!(val & AC_SUPPCM_BITS_32))
+ return false;
+ break;
+ default:
+ return false;
+ }
+ } else {
+ /* FIXME: check for float32 and AC3? */
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_is_supported_format);
+
+static unsigned int codec_read(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
+ unsigned int res;
+
+ if (snd_hdac_exec_verb(hdac, cmd, flags, &res))
+ return -1;
+
+ return res;
+}
+
+static int codec_write(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
+
+ return snd_hdac_exec_verb(hdac, cmd, flags, NULL);
+}
+
+/**
+ * snd_hdac_codec_read - send a command and get the response
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @flags: optional bit flags
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command and read the corresponding response.
+ *
+ * Returns the obtained response value, or -1 for an error.
+ */
+int snd_hdac_codec_read(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ return codec_read(hdac, nid, flags, verb, parm);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_read);
+
+/**
+ * snd_hdac_codec_write - send a single command without waiting for response
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @flags: optional bit flags
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command without waiting for response.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_codec_write(struct hdac_device *hdac, hda_nid_t nid,
+ int flags, unsigned int verb, unsigned int parm)
+{
+ return codec_write(hdac, nid, flags, verb, parm);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_write);
+
+/**
+ * snd_hdac_check_power_state - check whether the actual power state matches
+ * with the target state
+ *
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @target_state: target state to check for
+ *
+ * Return true if state matches, false if not
+ */
+bool snd_hdac_check_power_state(struct hdac_device *hdac,
+ hda_nid_t nid, unsigned int target_state)
+{
+ unsigned int state = codec_read(hdac, nid, 0,
+ AC_VERB_GET_POWER_STATE, 0);
+
+ if (state & AC_PWRST_ERROR)
+ return true;
+ state = (state >> 4) & 0x0f;
+ return (state == target_state);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_check_power_state);
+/**
+ * snd_hdac_sync_power_state - wait until actual power state matches
+ * with the target state
+ *
+ * @codec: the HDAC device
+ * @nid: NID to send the command
+ * @power_state: target power state to wait for
+ *
+ * Return power state or PS_ERROR if codec rejects GET verb.
+ */
+unsigned int snd_hdac_sync_power_state(struct hdac_device *codec,
+ hda_nid_t nid, unsigned int power_state)
+{
+ unsigned long end_time = jiffies + msecs_to_jiffies(500);
+ unsigned int state, actual_state, count;
+
+ for (count = 0; count < 500; count++) {
+ state = snd_hdac_codec_read(codec, nid, 0,
+ AC_VERB_GET_POWER_STATE, 0);
+ if (state & AC_PWRST_ERROR) {
+ msleep(20);
+ break;
+ }
+ actual_state = (state >> 4) & 0x0f;
+ if (actual_state == power_state)
+ break;
+ if (time_after_eq(jiffies, end_time))
+ break;
+ /* wait until the codec reachs to the target state */
+ msleep(1);
+ }
+ return state;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_sync_power_state);
diff --git a/sound/hda/core/ext/Makefile b/sound/hda/core/ext/Makefile
new file mode 100644
index 000000000000..85190a7eb5de
--- /dev/null
+++ b/sound/hda/core/ext/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-hda-ext-core-y := bus.o controller.o stream.o
+
+obj-$(CONFIG_SND_HDA_EXT_CORE) += snd-hda-ext-core.o
diff --git a/sound/hda/core/ext/bus.c b/sound/hda/core/ext/bus.c
new file mode 100644
index 000000000000..6004ea1c373e
--- /dev/null
+++ b/sound/hda/core/ext/bus.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * hdac-ext-bus.c - HD-audio extended core bus functions.
+ *
+ * Copyright (C) 2014-2015 Intel Corp
+ * Author: Jeeja KP <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <sound/hdaudio_ext.h>
+
+MODULE_DESCRIPTION("HDA extended core");
+MODULE_LICENSE("GPL v2");
+
+/**
+ * snd_hdac_ext_bus_init - initialize a HD-audio extended bus
+ * @bus: the pointer to HDAC bus object
+ * @dev: device pointer
+ * @ops: bus verb operators
+ * @ext_ops: operators used for ASoC HDA codec drivers
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
+ const struct hdac_bus_ops *ops,
+ const struct hdac_ext_bus_ops *ext_ops)
+{
+ int ret;
+
+ ret = snd_hdac_bus_init(bus, dev, ops);
+ if (ret < 0)
+ return ret;
+
+ bus->ext_ops = ext_ops;
+ /* FIXME:
+ * Currently only one bus is supported, if there is device with more
+ * buses, bus->idx should be greater than 0, but there needs to be a
+ * reliable way to always assign same number.
+ */
+ bus->idx = 0;
+ bus->cmd_dma_state = true;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init);
+
+/**
+ * snd_hdac_ext_bus_exit - clean up a HD-audio extended bus
+ * @bus: the pointer to HDAC bus object
+ */
+void snd_hdac_ext_bus_exit(struct hdac_bus *bus)
+{
+ snd_hdac_bus_exit(bus);
+ WARN_ON(!list_empty(&bus->hlink_list));
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_exit);
+
+/**
+ * snd_hdac_ext_bus_device_remove - remove HD-audio extended codec base devices
+ *
+ * @bus: the pointer to HDAC bus object
+ */
+void snd_hdac_ext_bus_device_remove(struct hdac_bus *bus)
+{
+ struct hdac_device *codec, *__codec;
+ /*
+ * we need to remove all the codec devices objects created in the
+ * snd_hdac_ext_bus_device_init
+ */
+ list_for_each_entry_safe(codec, __codec, &bus->codec_list, list) {
+ snd_hdac_device_unregister(codec);
+ put_device(&codec->dev);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_remove);
+#define dev_to_hdac(dev) (container_of((dev), \
+ struct hdac_device, dev))
+
+static inline struct hdac_driver *get_hdrv(struct device *dev)
+{
+ struct hdac_driver *hdrv = drv_to_hdac_driver(dev->driver);
+ return hdrv;
+}
+
+static inline struct hdac_device *get_hdev(struct device *dev)
+{
+ struct hdac_device *hdev = dev_to_hdac_dev(dev);
+ return hdev;
+}
+
+static int hda_ext_drv_probe(struct device *dev)
+{
+ return (get_hdrv(dev))->probe(get_hdev(dev));
+}
+
+static int hdac_ext_drv_remove(struct device *dev)
+{
+ return (get_hdrv(dev))->remove(get_hdev(dev));
+}
+
+static void hdac_ext_drv_shutdown(struct device *dev)
+{
+ return (get_hdrv(dev))->shutdown(get_hdev(dev));
+}
+
+/**
+ * snd_hda_ext_driver_register - register a driver for ext hda devices
+ *
+ * @drv: ext hda driver structure
+ */
+int snd_hda_ext_driver_register(struct hdac_driver *drv)
+{
+ drv->type = HDA_DEV_ASOC;
+ drv->driver.bus = &snd_hda_bus_type;
+ /* we use default match */
+
+ if (drv->probe)
+ drv->driver.probe = hda_ext_drv_probe;
+ if (drv->remove)
+ drv->driver.remove = hdac_ext_drv_remove;
+ if (drv->shutdown)
+ drv->driver.shutdown = hdac_ext_drv_shutdown;
+
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(snd_hda_ext_driver_register);
+
+/**
+ * snd_hda_ext_driver_unregister - unregister a driver for ext hda devices
+ *
+ * @drv: ext hda driver structure
+ */
+void snd_hda_ext_driver_unregister(struct hdac_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(snd_hda_ext_driver_unregister);
diff --git a/sound/hda/core/ext/controller.c b/sound/hda/core/ext/controller.c
new file mode 100644
index 000000000000..9eea3ea2dae0
--- /dev/null
+++ b/sound/hda/core/ext/controller.c
@@ -0,0 +1,410 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * hdac-ext-controller.c - HD-audio extended controller functions.
+ *
+ * Copyright (C) 2014-2015 Intel Corp
+ * Author: Jeeja KP <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/hda_register.h>
+#include <sound/hdaudio_ext.h>
+
+/*
+ * processing pipe helpers - these helpers are useful for dealing with HDA
+ * new capability of processing pipelines
+ */
+
+/**
+ * snd_hdac_ext_bus_ppcap_enable - enable/disable processing pipe capability
+ * @bus: the pointer to HDAC bus object
+ * @enable: flag to turn on/off the capability
+ */
+void snd_hdac_ext_bus_ppcap_enable(struct hdac_bus *bus, bool enable)
+{
+
+ if (!bus->ppcap) {
+ dev_err(bus->dev, "Address of PP capability is NULL");
+ return;
+ }
+
+ if (enable)
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
+ AZX_PPCTL_GPROCEN, AZX_PPCTL_GPROCEN);
+ else
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
+ AZX_PPCTL_GPROCEN, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable);
+
+/**
+ * snd_hdac_ext_bus_ppcap_int_enable - ppcap interrupt enable/disable
+ * @bus: the pointer to HDAC bus object
+ * @enable: flag to enable/disable interrupt
+ */
+void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_bus *bus, bool enable)
+{
+
+ if (!bus->ppcap) {
+ dev_err(bus->dev, "Address of PP capability is NULL\n");
+ return;
+ }
+
+ if (enable)
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
+ AZX_PPCTL_PIE, AZX_PPCTL_PIE);
+ else
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
+ AZX_PPCTL_PIE, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable);
+
+/*
+ * Multilink helpers - these helpers are useful for dealing with HDA
+ * new multilink capability
+ */
+
+/**
+ * snd_hdac_ext_bus_get_ml_capabilities - get multilink capability
+ * @bus: the pointer to HDAC bus object
+ *
+ * This will parse all links and read the mlink capabilities and add them
+ * in hlink_list of extended hdac bus
+ * Note: this will be freed on bus exit by driver
+ */
+int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus)
+{
+ int idx;
+ u32 link_count;
+ struct hdac_ext_link *hlink;
+ u32 leptr;
+
+ link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
+
+ dev_dbg(bus->dev, "In %s Link count: %d\n", __func__, link_count);
+
+ for (idx = 0; idx < link_count; idx++) {
+ hlink = kzalloc(sizeof(*hlink), GFP_KERNEL);
+ if (!hlink)
+ return -ENOMEM;
+ hlink->index = idx;
+ hlink->bus = bus;
+ hlink->ml_addr = bus->mlcap + AZX_ML_BASE +
+ (AZX_ML_INTERVAL * idx);
+ hlink->lcaps = readl(hlink->ml_addr + AZX_REG_ML_LCAP);
+ hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID);
+ hlink->slcount = FIELD_GET(AZX_ML_HDA_LCAP_SLCOUNT, hlink->lcaps) + 1;
+
+ if (hdac_ext_link_alt(hlink)) {
+ leptr = readl(hlink->ml_addr + AZX_REG_ML_LEPTR);
+ hlink->id = FIELD_GET(AZX_REG_ML_LEPTR_ID, leptr);
+ }
+
+ /* since link in On, update the ref */
+ hlink->ref_count = 1;
+
+ list_add_tail(&hlink->list, &bus->hlink_list);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_ml_capabilities);
+
+/**
+ * snd_hdac_ext_link_free_all- free hdac extended link objects
+ *
+ * @bus: the pointer to HDAC bus object
+ */
+
+void snd_hdac_ext_link_free_all(struct hdac_bus *bus)
+{
+ struct hdac_ext_link *hlink;
+
+ while (!list_empty(&bus->hlink_list)) {
+ hlink = list_first_entry(&bus->hlink_list, struct hdac_ext_link, list);
+ list_del(&hlink->list);
+ kfree(hlink);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_link_free_all);
+
+struct hdac_ext_link *snd_hdac_ext_bus_get_hlink_by_id(struct hdac_bus *bus, u32 id)
+{
+ struct hdac_ext_link *hlink;
+
+ list_for_each_entry(hlink, &bus->hlink_list, list)
+ if (hdac_ext_link_alt(hlink) && hlink->id == id)
+ return hlink;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_hlink_by_id);
+
+/**
+ * snd_hdac_ext_bus_get_hlink_by_addr - get hlink at specified address
+ * @bus: hlink's parent bus device
+ * @addr: codec device address
+ *
+ * Returns hlink object or NULL if matching hlink is not found.
+ */
+struct hdac_ext_link *snd_hdac_ext_bus_get_hlink_by_addr(struct hdac_bus *bus, int addr)
+{
+ struct hdac_ext_link *hlink;
+
+ list_for_each_entry(hlink, &bus->hlink_list, list)
+ if (hlink->lsdiid & (0x1 << addr))
+ return hlink;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_hlink_by_addr);
+
+/**
+ * snd_hdac_ext_bus_get_hlink_by_name - get hlink based on codec name
+ * @bus: the pointer to HDAC bus object
+ * @codec_name: codec name
+ */
+struct hdac_ext_link *snd_hdac_ext_bus_get_hlink_by_name(struct hdac_bus *bus,
+ const char *codec_name)
+{
+ int bus_idx, addr;
+
+ if (sscanf(codec_name, "ehdaudio%dD%d", &bus_idx, &addr) != 2)
+ return NULL;
+ if (bus->idx != bus_idx)
+ return NULL;
+ if (addr < 0 || addr > 31)
+ return NULL;
+
+ return snd_hdac_ext_bus_get_hlink_by_addr(bus, addr);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_hlink_by_name);
+
+static int check_hdac_link_power_active(struct hdac_ext_link *hlink, bool enable)
+{
+ int timeout;
+ u32 val;
+ int mask = (1 << AZX_ML_LCTL_CPA_SHIFT);
+
+ udelay(3);
+ timeout = 150;
+
+ do {
+ val = readl(hlink->ml_addr + AZX_REG_ML_LCTL);
+ if (enable) {
+ if (((val & mask) >> AZX_ML_LCTL_CPA_SHIFT))
+ return 0;
+ } else {
+ if (!((val & mask) >> AZX_ML_LCTL_CPA_SHIFT))
+ return 0;
+ }
+ udelay(3);
+ } while (--timeout);
+
+ return -EIO;
+}
+
+/**
+ * snd_hdac_ext_bus_link_power_up -power up hda link
+ * @hlink: HD-audio extended link
+ */
+int snd_hdac_ext_bus_link_power_up(struct hdac_ext_link *hlink)
+{
+ snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL,
+ AZX_ML_LCTL_SPA, AZX_ML_LCTL_SPA);
+
+ return check_hdac_link_power_active(hlink, true);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up);
+
+/**
+ * snd_hdac_ext_bus_link_power_down -power down hda link
+ * @hlink: HD-audio extended link
+ */
+int snd_hdac_ext_bus_link_power_down(struct hdac_ext_link *hlink)
+{
+ snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, AZX_ML_LCTL_SPA, 0);
+
+ return check_hdac_link_power_active(hlink, false);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down);
+
+/**
+ * snd_hdac_ext_bus_link_power_up_all -power up all hda link
+ * @bus: the pointer to HDAC bus object
+ */
+int snd_hdac_ext_bus_link_power_up_all(struct hdac_bus *bus)
+{
+ struct hdac_ext_link *hlink = NULL;
+ int ret;
+
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
+ ret = snd_hdac_ext_bus_link_power_up(hlink);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_up_all);
+
+/**
+ * snd_hdac_ext_bus_link_power_down_all -power down all hda link
+ * @bus: the pointer to HDAC bus object
+ */
+int snd_hdac_ext_bus_link_power_down_all(struct hdac_bus *bus)
+{
+ struct hdac_ext_link *hlink = NULL;
+ int ret;
+
+ list_for_each_entry(hlink, &bus->hlink_list, list) {
+ ret = snd_hdac_ext_bus_link_power_down(hlink);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all);
+
+/**
+ * snd_hdac_ext_bus_link_set_stream_id - maps stream id to link output
+ * @link: HD-audio ext link to set up
+ * @stream: stream id
+ */
+void snd_hdac_ext_bus_link_set_stream_id(struct hdac_ext_link *link,
+ int stream)
+{
+ snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 1 << stream);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_set_stream_id);
+
+/**
+ * snd_hdac_ext_bus_link_clear_stream_id - maps stream id to link output
+ * @link: HD-audio ext link to set up
+ * @stream: stream id
+ */
+void snd_hdac_ext_bus_link_clear_stream_id(struct hdac_ext_link *link,
+ int stream)
+{
+ snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_clear_stream_id);
+
+int snd_hdac_ext_bus_link_get(struct hdac_bus *bus,
+ struct hdac_ext_link *hlink)
+{
+ unsigned long codec_mask;
+ int ret = 0;
+
+ guard(mutex)(&bus->lock);
+
+ /*
+ * if we move from 0 to 1, count will be 1 so power up this link
+ * as well, also check the dma status and trigger that
+ */
+ if (++hlink->ref_count == 1) {
+ if (!bus->cmd_dma_state) {
+ snd_hdac_bus_init_cmd_io(bus);
+ bus->cmd_dma_state = true;
+ }
+
+ ret = snd_hdac_ext_bus_link_power_up(hlink);
+
+ /*
+ * clear the register to invalidate all the output streams
+ */
+ snd_hdac_updatew(hlink->ml_addr, AZX_REG_ML_LOSIDV,
+ AZX_ML_LOSIDV_STREAM_MASK, 0);
+ /*
+ * wait for 521usec for codec to report status
+ * HDA spec section 4.3 - Codec Discovery
+ */
+ udelay(521);
+ codec_mask = snd_hdac_chip_readw(bus, STATESTS);
+ dev_dbg(bus->dev, "codec_mask = 0x%lx\n", codec_mask);
+ snd_hdac_chip_writew(bus, STATESTS, codec_mask);
+ if (!bus->codec_mask)
+ bus->codec_mask = codec_mask;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_get);
+
+int snd_hdac_ext_bus_link_put(struct hdac_bus *bus,
+ struct hdac_ext_link *hlink)
+{
+ int ret = 0;
+ struct hdac_ext_link *hlink_tmp;
+ bool link_up = false;
+
+ guard(mutex)(&bus->lock);
+
+ /*
+ * if we move from 1 to 0, count will be 0
+ * so power down this link as well
+ */
+ if (--hlink->ref_count == 0) {
+ ret = snd_hdac_ext_bus_link_power_down(hlink);
+
+ /*
+ * now check if all links are off, if so turn off
+ * cmd dma as well
+ */
+ list_for_each_entry(hlink_tmp, &bus->hlink_list, list) {
+ if (hlink_tmp->ref_count) {
+ link_up = true;
+ break;
+ }
+ }
+
+ if (!link_up) {
+ snd_hdac_bus_stop_cmd_io(bus);
+ bus->cmd_dma_state = false;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_put);
+
+static void hdac_ext_codec_link_up(struct hdac_device *codec)
+{
+ const char *devname = dev_name(&codec->dev);
+ struct hdac_ext_link *hlink =
+ snd_hdac_ext_bus_get_hlink_by_name(codec->bus, devname);
+
+ if (hlink)
+ snd_hdac_ext_bus_link_get(codec->bus, hlink);
+}
+
+static void hdac_ext_codec_link_down(struct hdac_device *codec)
+{
+ const char *devname = dev_name(&codec->dev);
+ struct hdac_ext_link *hlink =
+ snd_hdac_ext_bus_get_hlink_by_name(codec->bus, devname);
+
+ if (hlink)
+ snd_hdac_ext_bus_link_put(codec->bus, hlink);
+}
+
+void snd_hdac_ext_bus_link_power(struct hdac_device *codec, bool enable)
+{
+ struct hdac_bus *bus = codec->bus;
+ bool oldstate = test_bit(codec->addr, &bus->codec_powered);
+
+ if (enable == oldstate)
+ return;
+
+ snd_hdac_bus_link_power(codec, enable);
+
+ if (enable)
+ hdac_ext_codec_link_up(codec);
+ else
+ hdac_ext_codec_link_down(codec);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power);
diff --git a/sound/hda/core/ext/stream.c b/sound/hda/core/ext/stream.c
new file mode 100644
index 000000000000..b4759198e51d
--- /dev/null
+++ b/sound/hda/core/ext/stream.c
@@ -0,0 +1,449 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * hdac-ext-stream.c - HD-audio extended stream operations.
+ *
+ * Copyright (C) 2015 Intel Corp
+ * Author: Jeeja KP <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/hda_register.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/compress_driver.h>
+
+/**
+ * snd_hdac_ext_host_stream_setup - Setup a HOST stream.
+ * @hext_stream: HDAudio stream to set up.
+ * @code_loading: Whether the stream is for PCM or code-loading.
+ *
+ * Return: Zero on success or negative error code.
+ */
+int snd_hdac_ext_host_stream_setup(struct hdac_ext_stream *hext_stream, bool code_loading)
+{
+ return hext_stream->host_setup(hdac_stream(hext_stream), code_loading);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_host_stream_setup);
+
+/**
+ * snd_hdac_apl_host_stream_setup - Setup a HOST stream following procedure
+ * recommended for ApolloLake devices.
+ * @hstream: HDAudio stream to set up.
+ * @code_loading: Whether the stream is for PCM or code-loading.
+ *
+ * Return: Zero on success or negative error code.
+ */
+static int snd_hdac_apl_host_stream_setup(struct hdac_stream *hstream, bool code_loading)
+{
+ struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
+ int ret;
+
+ snd_hdac_ext_stream_decouple(hstream->bus, hext_stream, false);
+ ret = snd_hdac_stream_setup(hstream, code_loading);
+ snd_hdac_ext_stream_decouple(hstream->bus, hext_stream, true);
+
+ return ret;
+}
+
+/**
+ * snd_hdac_ext_stream_init - initialize each stream (aka device)
+ * @bus: HD-audio core bus
+ * @hext_stream: HD-audio ext core stream object to initialize
+ * @idx: stream index number
+ * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE)
+ * @tag: the tag id to assign
+ *
+ * initialize the stream, if ppcap is enabled then init those and then
+ * invoke hdac stream initialization routine
+ */
+static void snd_hdac_ext_stream_init(struct hdac_bus *bus,
+ struct hdac_ext_stream *hext_stream,
+ int idx, int direction, int tag)
+{
+ if (bus->ppcap) {
+ hext_stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE +
+ AZX_PPHC_INTERVAL * idx;
+
+ hext_stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE +
+ AZX_PPLC_MULTI * bus->num_streams +
+ AZX_PPLC_INTERVAL * idx;
+ }
+
+ hext_stream->decoupled = false;
+ snd_hdac_stream_init(bus, &hext_stream->hstream, idx, direction, tag);
+}
+
+/**
+ * snd_hdac_ext_stream_init_all - create and initialize the stream objects
+ * for an extended hda bus
+ * @bus: HD-audio core bus
+ * @start_idx: start index for streams
+ * @num_stream: number of streams to initialize
+ * @dir: direction of streams
+ */
+int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx,
+ int num_stream, int dir)
+{
+ struct pci_dev *pci = to_pci_dev(bus->dev);
+ int (*setup_op)(struct hdac_stream *, bool);
+ int stream_tag = 0;
+ int i, tag, idx = start_idx;
+
+ if (pci->device == PCI_DEVICE_ID_INTEL_HDA_APL)
+ setup_op = snd_hdac_apl_host_stream_setup;
+ else
+ setup_op = snd_hdac_stream_setup;
+
+ for (i = 0; i < num_stream; i++) {
+ struct hdac_ext_stream *hext_stream =
+ kzalloc(sizeof(*hext_stream), GFP_KERNEL);
+ if (!hext_stream)
+ return -ENOMEM;
+ tag = ++stream_tag;
+ snd_hdac_ext_stream_init(bus, hext_stream, idx, dir, tag);
+ idx++;
+ hext_stream->host_setup = setup_op;
+ }
+
+ return 0;
+
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all);
+
+/**
+ * snd_hdac_ext_stream_free_all - free hdac extended stream objects
+ *
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_ext_stream_free_all(struct hdac_bus *bus)
+{
+ struct hdac_stream *s, *_s;
+ struct hdac_ext_stream *hext_stream;
+
+ list_for_each_entry_safe(s, _s, &bus->stream_list, list) {
+ hext_stream = stream_to_hdac_ext_stream(s);
+ snd_hdac_ext_stream_decouple(bus, hext_stream, false);
+ list_del(&s->list);
+ kfree(hext_stream);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_free_all);
+
+void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus,
+ struct hdac_ext_stream *hext_stream,
+ bool decouple)
+{
+ struct hdac_stream *hstream = &hext_stream->hstream;
+ u32 val;
+ int mask = AZX_PPCTL_PROCEN(hstream->index);
+
+ val = readw(bus->ppcap + AZX_REG_PP_PPCTL) & mask;
+
+ if (decouple && !val)
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, mask);
+ else if (!decouple && val)
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0);
+
+ hext_stream->decoupled = decouple;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple_locked);
+
+/**
+ * snd_hdac_ext_stream_decouple - decouple the hdac stream
+ * @bus: HD-audio core bus
+ * @hext_stream: HD-audio ext core stream object to initialize
+ * @decouple: flag to decouple
+ */
+void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
+ struct hdac_ext_stream *hext_stream, bool decouple)
+{
+ guard(spinlock_irq)(&bus->reg_lock);
+ snd_hdac_ext_stream_decouple_locked(bus, hext_stream, decouple);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple);
+
+/**
+ * snd_hdac_ext_stream_start - start a stream
+ * @hext_stream: HD-audio ext core stream to start
+ */
+void snd_hdac_ext_stream_start(struct hdac_ext_stream *hext_stream)
+{
+ snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
+ AZX_PPLCCTL_RUN, AZX_PPLCCTL_RUN);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_start);
+
+/**
+ * snd_hdac_ext_stream_clear - stop a stream DMA
+ * @hext_stream: HD-audio ext core stream to stop
+ */
+void snd_hdac_ext_stream_clear(struct hdac_ext_stream *hext_stream)
+{
+ snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_clear);
+
+/**
+ * snd_hdac_ext_stream_reset - reset a stream
+ * @hext_stream: HD-audio ext core stream to reset
+ */
+void snd_hdac_ext_stream_reset(struct hdac_ext_stream *hext_stream)
+{
+ unsigned char val;
+ int timeout;
+
+ snd_hdac_ext_stream_clear(hext_stream);
+
+ snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
+ AZX_PPLCCTL_STRST, AZX_PPLCCTL_STRST);
+ udelay(3);
+ timeout = 50;
+ do {
+ val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) &
+ AZX_PPLCCTL_STRST;
+ if (val)
+ break;
+ udelay(3);
+ } while (--timeout);
+ val &= ~AZX_PPLCCTL_STRST;
+ writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL);
+ udelay(3);
+
+ timeout = 50;
+ /* waiting for hardware to report that the stream is out of reset */
+ do {
+ val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST;
+ if (!val)
+ break;
+ udelay(3);
+ } while (--timeout);
+
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_reset);
+
+/**
+ * snd_hdac_ext_stream_setup - set up the SD for streaming
+ * @hext_stream: HD-audio ext core stream to set up
+ * @fmt: stream format
+ */
+int snd_hdac_ext_stream_setup(struct hdac_ext_stream *hext_stream, int fmt)
+{
+ struct hdac_stream *hstream = &hext_stream->hstream;
+ unsigned int val;
+
+ /* make sure the run bit is zero for SD */
+ snd_hdac_ext_stream_clear(hext_stream);
+ /* program the stream_tag */
+ val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL);
+ val = (val & ~AZX_PPLCCTL_STRM_MASK) |
+ (hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT);
+ writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL);
+
+ /* program the stream format */
+ writew(fmt, hext_stream->pplc_addr + AZX_REG_PPLCFMT);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_setup);
+
+static struct hdac_ext_stream *
+hdac_ext_link_dma_stream_assign(struct hdac_bus *bus,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_ext_stream *res = NULL;
+ struct hdac_stream *hstream = NULL;
+
+ if (!bus->ppcap) {
+ dev_err(bus->dev, "stream type not supported\n");
+ return NULL;
+ }
+
+ guard(spinlock_irq)(&bus->reg_lock);
+ list_for_each_entry(hstream, &bus->stream_list, list) {
+ struct hdac_ext_stream *hext_stream = container_of(hstream,
+ struct hdac_ext_stream,
+ hstream);
+ if (hstream->direction != substream->stream)
+ continue;
+
+ /* check if link stream is available */
+ if (!hext_stream->link_locked) {
+ res = hext_stream;
+ break;
+ }
+
+ }
+ if (res) {
+ snd_hdac_ext_stream_decouple_locked(bus, res, true);
+ res->link_locked = 1;
+ res->link_substream = substream;
+ }
+ return res;
+}
+
+static struct hdac_ext_stream *
+hdac_ext_host_dma_stream_assign(struct hdac_bus *bus,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_ext_stream *res = NULL;
+ struct hdac_stream *hstream = NULL;
+
+ if (!bus->ppcap) {
+ dev_err(bus->dev, "stream type not supported\n");
+ return NULL;
+ }
+
+ guard(spinlock_irq)(&bus->reg_lock);
+ list_for_each_entry(hstream, &bus->stream_list, list) {
+ struct hdac_ext_stream *hext_stream = container_of(hstream,
+ struct hdac_ext_stream,
+ hstream);
+ if (hstream->direction != substream->stream)
+ continue;
+
+ if (!hstream->opened) {
+ res = hext_stream;
+ break;
+ }
+ }
+ if (res) {
+ snd_hdac_ext_stream_decouple_locked(bus, res, true);
+ res->hstream.opened = 1;
+ res->hstream.running = 0;
+ res->hstream.substream = substream;
+ }
+
+ return res;
+}
+
+/**
+ * snd_hdac_ext_stream_assign - assign a stream for the PCM
+ * @bus: HD-audio core bus
+ * @substream: PCM substream to assign
+ * @type: type of stream (coupled, host or link stream)
+ *
+ * This assigns the stream based on the type (coupled/host/link), for the
+ * given PCM substream, assigns it and returns the stream object
+ *
+ * coupled: Looks for an unused stream
+ * host: Looks for an unused decoupled host stream
+ * link: Looks for an unused decoupled link stream
+ *
+ * If no stream is free, returns NULL. The function tries to keep using
+ * the same stream object when it's used beforehand. when a stream is
+ * decoupled, it becomes a host stream and link stream.
+ */
+struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus,
+ struct snd_pcm_substream *substream,
+ int type)
+{
+ struct hdac_ext_stream *hext_stream = NULL;
+ struct hdac_stream *hstream = NULL;
+
+ switch (type) {
+ case HDAC_EXT_STREAM_TYPE_COUPLED:
+ hstream = snd_hdac_stream_assign(bus, substream);
+ if (hstream)
+ hext_stream = container_of(hstream,
+ struct hdac_ext_stream,
+ hstream);
+ return hext_stream;
+
+ case HDAC_EXT_STREAM_TYPE_HOST:
+ return hdac_ext_host_dma_stream_assign(bus, substream);
+
+ case HDAC_EXT_STREAM_TYPE_LINK:
+ return hdac_ext_link_dma_stream_assign(bus, substream);
+
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign);
+
+/**
+ * snd_hdac_ext_stream_release - release the assigned stream
+ * @hext_stream: HD-audio ext core stream to release
+ * @type: type of stream (coupled, host or link stream)
+ *
+ * Release the stream that has been assigned by snd_hdac_ext_stream_assign().
+ */
+void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type)
+{
+ struct hdac_bus *bus = hext_stream->hstream.bus;
+
+ switch (type) {
+ case HDAC_EXT_STREAM_TYPE_COUPLED:
+ snd_hdac_stream_release(&hext_stream->hstream);
+ break;
+
+ case HDAC_EXT_STREAM_TYPE_HOST:
+ scoped_guard(spinlock_irq, &bus->reg_lock) {
+ /* couple link only if not in use */
+ if (!hext_stream->link_locked)
+ snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false);
+ snd_hdac_stream_release_locked(&hext_stream->hstream);
+ }
+ break;
+
+ case HDAC_EXT_STREAM_TYPE_LINK:
+ scoped_guard(spinlock_irq, &bus->reg_lock) {
+ /* couple host only if not in use */
+ if (!hext_stream->hstream.opened)
+ snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false);
+ hext_stream->link_locked = 0;
+ hext_stream->link_substream = NULL;
+ }
+ break;
+
+ default:
+ dev_dbg(bus->dev, "Invalid type %d\n", type);
+ }
+
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release);
+
+/**
+ * snd_hdac_ext_cstream_assign - assign a host stream for compress
+ * @bus: HD-audio core bus
+ * @cstream: Compress stream to assign
+ *
+ * Assign an unused host stream for the given compress stream.
+ * If no stream is free, NULL is returned. Stream is decoupled
+ * before assignment.
+ */
+struct hdac_ext_stream *snd_hdac_ext_cstream_assign(struct hdac_bus *bus,
+ struct snd_compr_stream *cstream)
+{
+ struct hdac_ext_stream *res = NULL;
+ struct hdac_stream *hstream;
+
+ guard(spinlock_irq)(&bus->reg_lock);
+ list_for_each_entry(hstream, &bus->stream_list, list) {
+ struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
+
+ if (hstream->direction != cstream->direction)
+ continue;
+
+ if (!hstream->opened) {
+ res = hext_stream;
+ break;
+ }
+ }
+
+ if (res) {
+ snd_hdac_ext_stream_decouple_locked(bus, res, true);
+ res->hstream.opened = 1;
+ res->hstream.running = 0;
+ res->hstream.cstream = cstream;
+ }
+
+ return res;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_cstream_assign);
diff --git a/sound/hda/core/hda_bus_type.c b/sound/hda/core/hda_bus_type.c
new file mode 100644
index 000000000000..eb72a7af2e56
--- /dev/null
+++ b/sound/hda/core/hda_bus_type.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HD-audio bus
+ */
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/export.h>
+#include <sound/hdaudio.h>
+
+MODULE_DESCRIPTION("HD-audio bus");
+MODULE_LICENSE("GPL");
+
+/**
+ * hdac_get_device_id - gets the hdac device id entry
+ * @hdev: HD-audio core device
+ * @drv: HD-audio codec driver
+ *
+ * Compares the hdac device vendor_id and revision_id to the hdac_device
+ * driver id_table and returns the matching device id entry.
+ */
+const struct hda_device_id *
+hdac_get_device_id(struct hdac_device *hdev, const struct hdac_driver *drv)
+{
+ if (drv->id_table) {
+ const struct hda_device_id *id = drv->id_table;
+
+ while (id->vendor_id) {
+ if (hdev->vendor_id == id->vendor_id &&
+ (!id->rev_id || id->rev_id == hdev->revision_id))
+ return id;
+ id++;
+ }
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(hdac_get_device_id);
+
+static int hdac_codec_match(struct hdac_device *dev, const struct hdac_driver *drv)
+{
+ if (hdac_get_device_id(dev, drv))
+ return 1;
+ else
+ return 0;
+}
+
+static int hda_bus_match(struct device *dev, const struct device_driver *drv)
+{
+ struct hdac_device *hdev = dev_to_hdac_dev(dev);
+ const struct hdac_driver *hdrv = drv_to_hdac_driver(drv);
+
+ if (hdev->type != hdrv->type)
+ return 0;
+
+ /*
+ * if driver provided a match function use that otherwise we will
+ * use hdac_codec_match function
+ */
+ if (hdrv->match)
+ return hdrv->match(hdev, hdrv);
+ else
+ return hdac_codec_match(hdev, hdrv);
+ return 1;
+}
+
+static int hda_uevent(const struct device *dev, struct kobj_uevent_env *env)
+{
+ char modalias[32];
+
+ snd_hdac_codec_modalias(dev_to_hdac_dev(dev), modalias,
+ sizeof(modalias));
+ if (add_uevent_var(env, "MODALIAS=%s", modalias))
+ return -ENOMEM;
+ return 0;
+}
+
+const struct bus_type snd_hda_bus_type = {
+ .name = "hdaudio",
+ .match = hda_bus_match,
+ .uevent = hda_uevent,
+};
+EXPORT_SYMBOL_GPL(snd_hda_bus_type);
+
+static int __init hda_bus_init(void)
+{
+ return bus_register(&snd_hda_bus_type);
+}
+
+static void __exit hda_bus_exit(void)
+{
+ bus_unregister(&snd_hda_bus_type);
+}
+
+subsys_initcall(hda_bus_init);
+module_exit(hda_bus_exit);
diff --git a/sound/hda/core/hdmi_chmap.c b/sound/hda/core/hdmi_chmap.c
new file mode 100644
index 000000000000..7b276047f85a
--- /dev/null
+++ b/sound/hda/core/hdmi_chmap.c
@@ -0,0 +1,868 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HDMI Channel map support helpers
+ */
+
+#include <linux/module.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <sound/hda_chmap.h>
+
+/*
+ * CEA speaker placement:
+ *
+ * FLH FCH FRH
+ * FLW FL FLC FC FRC FR FRW
+ *
+ * LFE
+ * TC
+ *
+ * RL RLC RC RRC RR
+ *
+ * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
+ * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
+ */
+enum cea_speaker_placement {
+ FL = (1 << 0), /* Front Left */
+ FC = (1 << 1), /* Front Center */
+ FR = (1 << 2), /* Front Right */
+ FLC = (1 << 3), /* Front Left Center */
+ FRC = (1 << 4), /* Front Right Center */
+ RL = (1 << 5), /* Rear Left */
+ RC = (1 << 6), /* Rear Center */
+ RR = (1 << 7), /* Rear Right */
+ RLC = (1 << 8), /* Rear Left Center */
+ RRC = (1 << 9), /* Rear Right Center */
+ LFE = (1 << 10), /* Low Frequency Effect */
+ FLW = (1 << 11), /* Front Left Wide */
+ FRW = (1 << 12), /* Front Right Wide */
+ FLH = (1 << 13), /* Front Left High */
+ FCH = (1 << 14), /* Front Center High */
+ FRH = (1 << 15), /* Front Right High */
+ TC = (1 << 16), /* Top Center */
+};
+
+static const char * const cea_speaker_allocation_names[] = {
+ /* 0 */ "FL/FR",
+ /* 1 */ "LFE",
+ /* 2 */ "FC",
+ /* 3 */ "RL/RR",
+ /* 4 */ "RC",
+ /* 5 */ "FLC/FRC",
+ /* 6 */ "RLC/RRC",
+ /* 7 */ "FLW/FRW",
+ /* 8 */ "FLH/FRH",
+ /* 9 */ "TC",
+ /* 10 */ "FCH",
+};
+
+/*
+ * ELD SA bits in the CEA Speaker Allocation data block
+ */
+static const int eld_speaker_allocation_bits[] = {
+ [0] = FL | FR,
+ [1] = LFE,
+ [2] = FC,
+ [3] = RL | RR,
+ [4] = RC,
+ [5] = FLC | FRC,
+ [6] = RLC | RRC,
+ /* the following are not defined in ELD yet */
+ [7] = FLW | FRW,
+ [8] = FLH | FRH,
+ [9] = TC,
+ [10] = FCH,
+};
+
+/*
+ * ALSA sequence is:
+ *
+ * surround40 surround41 surround50 surround51 surround71
+ * ch0 front left = = = =
+ * ch1 front right = = = =
+ * ch2 rear left = = = =
+ * ch3 rear right = = = =
+ * ch4 LFE center center center
+ * ch5 LFE LFE
+ * ch6 side left
+ * ch7 side right
+ *
+ * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
+ */
+static int hdmi_channel_mapping[0x32][8] = {
+ /* stereo */
+ [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
+ /* 2.1 */
+ [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
+ /* Dolby Surround */
+ [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
+ /* surround40 */
+ [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
+ /* 4ch */
+ [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
+ /* surround41 */
+ [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
+ /* surround50 */
+ [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
+ /* surround51 */
+ [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
+ /* 7.1 */
+ [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
+};
+
+/*
+ * This is an ordered list!
+ *
+ * The preceding ones have better chances to be selected by
+ * hdmi_channel_allocation().
+ */
+static struct hdac_cea_channel_speaker_allocation channel_allocations[] = {
+/* channel: 7 6 5 4 3 2 1 0 */
+{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } },
+ /* 2.1 */
+{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } },
+ /* Dolby Surround */
+{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } },
+ /* surround40 */
+{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } },
+ /* surround41 */
+{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } },
+ /* surround50 */
+{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } },
+ /* surround51 */
+{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } },
+ /* 6.1 */
+{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } },
+ /* surround71 */
+{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } },
+
+{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } },
+{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } },
+{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } },
+{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } },
+{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } },
+{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } },
+{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } },
+{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } },
+{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } },
+{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } },
+{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } },
+{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } },
+{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } },
+{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } },
+};
+
+static int hdmi_pin_set_slot_channel(struct hdac_device *codec,
+ hda_nid_t pin_nid, int asp_slot, int channel)
+{
+ return snd_hdac_codec_write(codec, pin_nid, 0,
+ AC_VERB_SET_HDMI_CHAN_SLOT,
+ (channel << 4) | asp_slot);
+}
+
+static int hdmi_pin_get_slot_channel(struct hdac_device *codec,
+ hda_nid_t pin_nid, int asp_slot)
+{
+ return (snd_hdac_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_HDMI_CHAN_SLOT,
+ asp_slot) & 0xf0) >> 4;
+}
+
+static int hdmi_get_channel_count(struct hdac_device *codec, hda_nid_t cvt_nid)
+{
+ return 1 + snd_hdac_codec_read(codec, cvt_nid, 0,
+ AC_VERB_GET_CVT_CHAN_COUNT, 0);
+}
+
+static void hdmi_set_channel_count(struct hdac_device *codec,
+ hda_nid_t cvt_nid, int chs)
+{
+ if (chs != hdmi_get_channel_count(codec, cvt_nid))
+ snd_hdac_codec_write(codec, cvt_nid, 0,
+ AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
+}
+
+/*
+ * Channel mapping routines
+ */
+
+/*
+ * Compute derived values in channel_allocations[].
+ */
+static void init_channel_allocations(void)
+{
+ int i, j;
+ struct hdac_cea_channel_speaker_allocation *p;
+
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ p = channel_allocations + i;
+ p->channels = 0;
+ p->spk_mask = 0;
+ for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
+ if (p->speakers[j]) {
+ p->channels++;
+ p->spk_mask |= p->speakers[j];
+ }
+ }
+}
+
+static int get_channel_allocation_order(int ca)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if (channel_allocations[i].ca_index == ca)
+ break;
+ }
+ return i;
+}
+
+void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
+ if (spk_alloc & (1 << i))
+ j += scnprintf(buf + j, buflen - j, " %s",
+ cea_speaker_allocation_names[i]);
+ }
+ buf[j] = '\0'; /* necessary when j == 0 */
+}
+EXPORT_SYMBOL_GPL(snd_hdac_print_channel_allocation);
+
+/*
+ * The transformation takes two steps:
+ *
+ * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
+ * spk_mask => (channel_allocations[]) => ai->CA
+ *
+ * TODO: it could select the wrong CA from multiple candidates.
+*/
+static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec,
+ int spk_alloc, int channels)
+{
+ int i;
+ int ca = 0;
+ int spk_mask = 0;
+ char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+
+ /*
+ * CA defaults to 0 for basic stereo audio
+ */
+ if (channels <= 2)
+ return 0;
+
+ /*
+ * expand ELD's speaker allocation mask
+ *
+ * ELD tells the speaker mask in a compact(paired) form,
+ * expand ELD's notions to match the ones used by Audio InfoFrame.
+ */
+ for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
+ if (spk_alloc & (1 << i))
+ spk_mask |= eld_speaker_allocation_bits[i];
+ }
+
+ /* search for the first working match in the CA table */
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if (channels == channel_allocations[i].channels &&
+ (spk_mask & channel_allocations[i].spk_mask) ==
+ channel_allocations[i].spk_mask) {
+ ca = channel_allocations[i].ca_index;
+ break;
+ }
+ }
+
+ if (!ca) {
+ /*
+ * if there was no match, select the regular ALSA channel
+ * allocation with the matching number of channels
+ */
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if (channels == channel_allocations[i].channels) {
+ ca = channel_allocations[i].ca_index;
+ break;
+ }
+ }
+ }
+
+ snd_hdac_print_channel_allocation(spk_alloc, buf, sizeof(buf));
+ dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n",
+ ca, channels, buf);
+
+ return ca;
+}
+
+static void hdmi_debug_channel_mapping(struct hdac_chmap *chmap,
+ hda_nid_t pin_nid)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ int i;
+ int channel;
+
+ for (i = 0; i < 8; i++) {
+ channel = chmap->ops.pin_get_slot_channel(
+ chmap->hdac, pin_nid, i);
+ dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n",
+ channel, i);
+ }
+#endif
+}
+
+static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap,
+ hda_nid_t pin_nid,
+ bool non_pcm,
+ int ca)
+{
+ struct hdac_cea_channel_speaker_allocation *ch_alloc;
+ int i;
+ int err;
+ int order;
+ int non_pcm_mapping[8];
+
+ order = get_channel_allocation_order(ca);
+ ch_alloc = &channel_allocations[order];
+
+ if (hdmi_channel_mapping[ca][1] == 0) {
+ int hdmi_slot = 0;
+ /* fill actual channel mappings in ALSA channel (i) order */
+ for (i = 0; i < ch_alloc->channels; i++) {
+ while (!WARN_ON(hdmi_slot >= 8) &&
+ !ch_alloc->speakers[7 - hdmi_slot])
+ hdmi_slot++; /* skip zero slots */
+
+ hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++;
+ }
+ /* fill the rest of the slots with ALSA channel 0xf */
+ for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++)
+ if (!ch_alloc->speakers[7 - hdmi_slot])
+ hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot;
+ }
+
+ if (non_pcm) {
+ for (i = 0; i < ch_alloc->channels; i++)
+ non_pcm_mapping[i] = (i << 4) | i;
+ for (; i < 8; i++)
+ non_pcm_mapping[i] = (0xf << 4) | i;
+ }
+
+ for (i = 0; i < 8; i++) {
+ int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i];
+ int hdmi_slot = slotsetup & 0x0f;
+ int channel = (slotsetup & 0xf0) >> 4;
+
+ err = chmap->ops.pin_set_slot_channel(chmap->hdac,
+ pin_nid, hdmi_slot, channel);
+ if (err) {
+ dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n");
+ break;
+ }
+ }
+}
+
+struct channel_map_table {
+ unsigned char map; /* ALSA API channel map position */
+ int spk_mask; /* speaker position bit mask */
+};
+
+static struct channel_map_table map_tables[] = {
+ { SNDRV_CHMAP_FL, FL },
+ { SNDRV_CHMAP_FR, FR },
+ { SNDRV_CHMAP_RL, RL },
+ { SNDRV_CHMAP_RR, RR },
+ { SNDRV_CHMAP_LFE, LFE },
+ { SNDRV_CHMAP_FC, FC },
+ { SNDRV_CHMAP_RLC, RLC },
+ { SNDRV_CHMAP_RRC, RRC },
+ { SNDRV_CHMAP_RC, RC },
+ { SNDRV_CHMAP_FLC, FLC },
+ { SNDRV_CHMAP_FRC, FRC },
+ { SNDRV_CHMAP_TFL, FLH },
+ { SNDRV_CHMAP_TFR, FRH },
+ { SNDRV_CHMAP_FLW, FLW },
+ { SNDRV_CHMAP_FRW, FRW },
+ { SNDRV_CHMAP_TC, TC },
+ { SNDRV_CHMAP_TFC, FCH },
+ {} /* terminator */
+};
+
+/* from ALSA API channel position to speaker bit mask */
+int snd_hdac_chmap_to_spk_mask(unsigned char c)
+{
+ struct channel_map_table *t = map_tables;
+
+ for (; t->map; t++) {
+ if (t->map == c)
+ return t->spk_mask;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_chmap_to_spk_mask);
+
+/* from ALSA API channel position to CEA slot */
+static int to_cea_slot(int ordered_ca, unsigned char pos)
+{
+ int mask = snd_hdac_chmap_to_spk_mask(pos);
+ int i;
+
+ /* Add sanity check to pass klockwork check.
+ * This should never happen.
+ */
+ if (ordered_ca >= ARRAY_SIZE(channel_allocations))
+ return -1;
+
+ if (mask) {
+ for (i = 0; i < 8; i++) {
+ if (channel_allocations[ordered_ca].speakers[7 - i] == mask)
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/* from speaker bit mask to ALSA API channel position */
+int snd_hdac_spk_to_chmap(int spk)
+{
+ struct channel_map_table *t = map_tables;
+
+ for (; t->map; t++) {
+ if (t->spk_mask == spk)
+ return t->map;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_spk_to_chmap);
+
+/* from CEA slot to ALSA API channel position */
+static int from_cea_slot(int ordered_ca, unsigned char slot)
+{
+ int mask;
+
+ /* Add sanity check to pass klockwork check.
+ * This should never happen.
+ */
+ if (slot >= 8)
+ return 0;
+
+ mask = channel_allocations[ordered_ca].speakers[7 - slot];
+
+ return snd_hdac_spk_to_chmap(mask);
+}
+
+/* get the CA index corresponding to the given ALSA API channel map */
+static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
+{
+ int i, spks = 0, spk_mask = 0;
+
+ for (i = 0; i < chs; i++) {
+ int mask = snd_hdac_chmap_to_spk_mask(map[i]);
+
+ if (mask) {
+ spk_mask |= mask;
+ spks++;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if ((chs == channel_allocations[i].channels ||
+ spks == channel_allocations[i].channels) &&
+ (spk_mask & channel_allocations[i].spk_mask) ==
+ channel_allocations[i].spk_mask)
+ return channel_allocations[i].ca_index;
+ }
+ return -1;
+}
+
+/* set up the channel slots for the given ALSA API channel map */
+static int hdmi_manual_setup_channel_mapping(struct hdac_chmap *chmap,
+ hda_nid_t pin_nid,
+ int chs, unsigned char *map,
+ int ca)
+{
+ int ordered_ca = get_channel_allocation_order(ca);
+ int alsa_pos, hdmi_slot;
+ int assignments[8] = {[0 ... 7] = 0xf};
+
+ for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) {
+
+ hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]);
+
+ if (hdmi_slot < 0)
+ continue; /* unassigned channel */
+
+ assignments[hdmi_slot] = alsa_pos;
+ }
+
+ for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) {
+ int err;
+
+ err = chmap->ops.pin_set_slot_channel(chmap->hdac,
+ pin_nid, hdmi_slot, assignments[hdmi_slot]);
+ if (err)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* store ALSA API channel map from the current default map */
+static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
+{
+ int i;
+ int ordered_ca = get_channel_allocation_order(ca);
+
+ for (i = 0; i < 8; i++) {
+ if (ordered_ca < ARRAY_SIZE(channel_allocations) &&
+ i < channel_allocations[ordered_ca].channels)
+ map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);
+ else
+ map[i] = 0;
+ }
+}
+
+void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap,
+ hda_nid_t pin_nid, bool non_pcm, int ca,
+ int channels, unsigned char *map,
+ bool chmap_set)
+{
+ if (!non_pcm && chmap_set) {
+ hdmi_manual_setup_channel_mapping(chmap, pin_nid,
+ channels, map, ca);
+ } else {
+ hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca);
+ hdmi_setup_fake_chmap(map, ca);
+ }
+
+ hdmi_debug_channel_mapping(chmap, pin_nid);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_setup_channel_mapping);
+
+int snd_hdac_get_active_channels(int ca)
+{
+ int ordered_ca = get_channel_allocation_order(ca);
+
+ /* Add sanity check to pass klockwork check.
+ * This should never happen.
+ */
+ if (ordered_ca >= ARRAY_SIZE(channel_allocations))
+ ordered_ca = 0;
+
+ return channel_allocations[ordered_ca].channels;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_active_channels);
+
+struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca)
+{
+ return &channel_allocations[get_channel_allocation_order(ca)];
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_ch_alloc_from_ca);
+
+int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc,
+ int channels, bool chmap_set, bool non_pcm, unsigned char *map)
+{
+ int ca;
+
+ if (!non_pcm && chmap_set)
+ ca = hdmi_manual_channel_allocation(channels, map);
+ else
+ ca = hdmi_channel_allocation_spk_alloc_blk(hdac,
+ spk_alloc, channels);
+
+ if (ca < 0)
+ ca = 0;
+
+ return ca;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_channel_allocation);
+
+/*
+ * ALSA API channel-map control callbacks
+ */
+static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hdac_chmap *chmap = info->private_data;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chmap->channels_max;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SNDRV_CHMAP_LAST;
+ return 0;
+}
+
+static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
+ struct hdac_cea_channel_speaker_allocation *cap, int channels)
+{
+ /* If the speaker allocation matches the channel count, it is OK.*/
+ if (cap->channels != channels)
+ return -1;
+
+ /* all channels are remappable freely */
+ return SNDRV_CTL_TLVT_CHMAP_VAR;
+}
+
+static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
+ struct hdac_cea_channel_speaker_allocation *cap,
+ unsigned int *chmap, int channels)
+{
+ int count = 0;
+ int c;
+
+ for (c = 7; c >= 0; c--) {
+ int spk = cap->speakers[c];
+
+ if (!spk)
+ continue;
+
+ chmap[count++] = snd_hdac_spk_to_chmap(spk);
+ }
+
+ WARN_ON(count != channels);
+}
+
+static int spk_mask_from_spk_alloc(int spk_alloc)
+{
+ int i;
+ int spk_mask = eld_speaker_allocation_bits[0];
+
+ for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
+ if (spk_alloc & (1 << i))
+ spk_mask |= eld_speaker_allocation_bits[i];
+ }
+
+ return spk_mask;
+}
+
+static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hdac_chmap *chmap = info->private_data;
+ int pcm_idx = kcontrol->private_value;
+ unsigned int __user *dst;
+ int chs, count = 0;
+ unsigned long max_chs;
+ int type;
+ int spk_alloc, spk_mask;
+
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
+ return -EFAULT;
+ size -= 8;
+ dst = tlv + 2;
+
+ spk_alloc = chmap->ops.get_spk_alloc(chmap->hdac, pcm_idx);
+ spk_mask = spk_mask_from_spk_alloc(spk_alloc);
+
+ max_chs = hweight_long(spk_mask);
+
+ for (chs = 2; chs <= max_chs; chs++) {
+ int i;
+ struct hdac_cea_channel_speaker_allocation *cap;
+
+ cap = channel_allocations;
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
+ int chs_bytes = chs * 4;
+ unsigned int tlv_chmap[8];
+
+ if (cap->channels != chs)
+ continue;
+
+ if (!(cap->spk_mask == (spk_mask & cap->spk_mask)))
+ continue;
+
+ type = chmap->ops.chmap_cea_alloc_validate_get_type(
+ chmap, cap, chs);
+ if (type < 0)
+ return -ENODEV;
+ if (size < 8)
+ return -ENOMEM;
+
+ if (put_user(type, dst) ||
+ put_user(chs_bytes, dst + 1))
+ return -EFAULT;
+
+ dst += 2;
+ size -= 8;
+ count += 8;
+
+ if (size < chs_bytes)
+ return -ENOMEM;
+
+ size -= chs_bytes;
+ count += chs_bytes;
+ chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap,
+ tlv_chmap, chs);
+
+ if (copy_to_user(dst, tlv_chmap, chs_bytes))
+ return -EFAULT;
+ dst += chs;
+ }
+ }
+
+ if (put_user(count, tlv + 1))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hdac_chmap *chmap = info->private_data;
+ int pcm_idx = kcontrol->private_value;
+ unsigned char pcm_chmap[8];
+ int i;
+
+ memset(pcm_chmap, 0, sizeof(pcm_chmap));
+ chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap);
+
+ for (i = 0; i < ARRAY_SIZE(pcm_chmap); i++)
+ ucontrol->value.integer.value[i] = pcm_chmap[i];
+
+ return 0;
+}
+
+/* a simple sanity check for input values to chmap kcontrol */
+static int chmap_value_check(struct hdac_chmap *hchmap,
+ const struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+
+ for (i = 0; i < hchmap->channels_max; i++) {
+ if (ucontrol->value.integer.value[i] < 0 ||
+ ucontrol->value.integer.value[i] > SNDRV_CHMAP_LAST)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hdac_chmap *hchmap = info->private_data;
+ int pcm_idx = kcontrol->private_value;
+ unsigned int ctl_idx;
+ struct snd_pcm_substream *substream;
+ unsigned char chmap[8], per_pin_chmap[8];
+ int i, err, ca, prepared = 0;
+
+ err = chmap_value_check(hchmap, ucontrol);
+ if (err < 0)
+ return err;
+
+ /* No monitor is connected in dyn_pcm_assign.
+ * It's invalid to setup the chmap
+ */
+ if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx))
+ return 0;
+
+ ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ substream = snd_pcm_chmap_substream(info, ctl_idx);
+ if (!substream || !substream->runtime)
+ return 0; /* just for avoiding error from alsactl restore */
+ switch (substream->runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_SETUP:
+ break;
+ case SNDRV_PCM_STATE_PREPARED:
+ prepared = 1;
+ break;
+ default:
+ return -EBUSY;
+ }
+ memset(chmap, 0, sizeof(chmap));
+ for (i = 0; i < ARRAY_SIZE(chmap); i++)
+ chmap[i] = ucontrol->value.integer.value[i];
+
+ hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap);
+ if (!memcmp(chmap, per_pin_chmap, sizeof(chmap)))
+ return 0;
+ ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
+ if (ca < 0)
+ return -EINVAL;
+ if (hchmap->ops.chmap_validate) {
+ err = hchmap->ops.chmap_validate(hchmap, ca,
+ ARRAY_SIZE(chmap), chmap);
+ if (err)
+ return err;
+ }
+
+ hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared);
+
+ return 0;
+}
+
+static const struct hdac_chmap_ops chmap_ops = {
+ .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type,
+ .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap,
+ .pin_get_slot_channel = hdmi_pin_get_slot_channel,
+ .pin_set_slot_channel = hdmi_pin_set_slot_channel,
+ .set_channel_count = hdmi_set_channel_count,
+};
+
+void snd_hdac_register_chmap_ops(struct hdac_device *hdac,
+ struct hdac_chmap *chmap)
+{
+ chmap->ops = chmap_ops;
+ chmap->hdac = hdac;
+ init_channel_allocations();
+}
+EXPORT_SYMBOL_GPL(snd_hdac_register_chmap_ops);
+
+int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx,
+ struct hdac_chmap *hchmap)
+{
+ struct snd_pcm_chmap *chmap;
+ struct snd_kcontrol *kctl;
+ int err, i;
+
+ err = snd_pcm_add_chmap_ctls(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, 0, pcm_idx, &chmap);
+ if (err < 0)
+ return err;
+ /* override handlers */
+ chmap->private_data = hchmap;
+ kctl = chmap->kctl;
+ for (i = 0; i < kctl->count; i++)
+ kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+ kctl->info = hdmi_chmap_ctl_info;
+ kctl->get = hdmi_chmap_ctl_get;
+ kctl->put = hdmi_chmap_ctl_put;
+ kctl->tlv.c = hdmi_chmap_ctl_tlv;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_add_chmap_ctls);
diff --git a/sound/hda/core/i915.c b/sound/hda/core/i915.c
new file mode 100644
index 000000000000..44438c799f95
--- /dev/null
+++ b/sound/hda/core/i915.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * hdac_i915.c - routines for sync between HD-A core and i915 display driver
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_register.h>
+#include <video/nomodeset.h>
+
+static int gpu_bind = -1;
+module_param(gpu_bind, int, 0644);
+MODULE_PARM_DESC(gpu_bind, "Whether to bind sound component to GPU "
+ "(1=always, 0=never, -1=on nomodeset(default))");
+
+/**
+ * snd_hdac_i915_set_bclk - Reprogram BCLK for HSW/BDW
+ * @bus: HDA core bus
+ *
+ * Intel HSW/BDW display HDA controller is in GPU. Both its power and link BCLK
+ * depends on GPU. Two Extended Mode registers EM4 (M value) and EM5 (N Value)
+ * are used to convert CDClk (Core Display Clock) to 24MHz BCLK:
+ * BCLK = CDCLK * M / N
+ * The values will be lost when the display power well is disabled and need to
+ * be restored to avoid abnormal playback speed.
+ *
+ * Call this function at initializing and changing power well, as well as
+ * at ELD notifier for the hotplug.
+ */
+void snd_hdac_i915_set_bclk(struct hdac_bus *bus)
+{
+ struct drm_audio_component *acomp = bus->audio_component;
+ struct pci_dev *pci = to_pci_dev(bus->dev);
+ int cdclk_freq;
+ unsigned int bclk_m, bclk_n;
+
+ if (!acomp || !acomp->ops || !acomp->ops->get_cdclk_freq)
+ return; /* only for i915 binding */
+ if (!HDA_CONTROLLER_IS_HSW(pci))
+ return; /* only HSW/BDW */
+
+ cdclk_freq = acomp->ops->get_cdclk_freq(acomp->dev);
+ switch (cdclk_freq) {
+ case 337500:
+ bclk_m = 16;
+ bclk_n = 225;
+ break;
+
+ case 450000:
+ default: /* default CDCLK 450MHz */
+ bclk_m = 4;
+ bclk_n = 75;
+ break;
+
+ case 540000:
+ bclk_m = 4;
+ bclk_n = 90;
+ break;
+
+ case 675000:
+ bclk_m = 8;
+ bclk_n = 225;
+ break;
+ }
+
+ snd_hdac_chip_writew(bus, HSW_EM4, bclk_m);
+ snd_hdac_chip_writew(bus, HSW_EM5, bclk_n);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_i915_set_bclk);
+
+/* returns true if the devices can be connected for audio */
+static bool connectivity_check(struct pci_dev *i915, struct pci_dev *hdac)
+{
+ struct pci_bus *bus_a = i915->bus, *bus_b = hdac->bus;
+
+ /* directly connected on the same bus */
+ if (bus_a == bus_b)
+ return true;
+
+ bus_a = bus_a->parent;
+ bus_b = bus_b->parent;
+
+ /* connected via parent bus (may be NULL!) */
+ if (bus_a == bus_b)
+ return true;
+
+ if (!bus_a || !bus_b)
+ return false;
+
+ /*
+ * on i915 discrete GPUs with embedded HDA audio, the two
+ * devices are connected via 2nd level PCI bridge
+ */
+ bus_a = bus_a->parent;
+ bus_b = bus_b->parent;
+ if (bus_a && bus_a == bus_b)
+ return true;
+
+ return false;
+}
+
+static int i915_component_master_match(struct device *dev, int subcomponent,
+ void *data)
+{
+ struct pci_dev *hdac_pci, *i915_pci;
+ struct hdac_bus *bus = data;
+
+ if (!dev_is_pci(dev))
+ return 0;
+
+ hdac_pci = to_pci_dev(bus->dev);
+ i915_pci = to_pci_dev(dev);
+
+ if ((!strcmp(dev->driver->name, "i915") ||
+ !strcmp(dev->driver->name, "xe")) &&
+ subcomponent == I915_COMPONENT_AUDIO &&
+ connectivity_check(i915_pci, hdac_pci))
+ return 1;
+
+ return 0;
+}
+
+/* check whether Intel graphics is present and reachable */
+static int i915_gfx_present(struct pci_dev *hdac_pci)
+{
+ /* List of known platforms with no i915 support. */
+ static const struct pci_device_id denylist[] = {
+ /* CNL */
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a40), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a41), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a42), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a44), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a49), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a4a), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a4c), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a50), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a51), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a52), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a54), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a59), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a5a), 0x030000, 0xff0000 },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x5a5c), 0x030000, 0xff0000 },
+ /* LKF */
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9840), 0x030000, 0xff0000 },
+ {}
+ };
+ struct pci_dev *display_dev = NULL;
+
+ if (!gpu_bind || (gpu_bind < 0 && video_firmware_drivers_only()))
+ return false;
+
+ for_each_pci_dev(display_dev) {
+ if (display_dev->vendor != PCI_VENDOR_ID_INTEL ||
+ !pci_is_display(display_dev))
+ continue;
+
+ if (pci_match_id(denylist, display_dev))
+ continue;
+
+ if (connectivity_check(display_dev, hdac_pci)) {
+ pci_dev_put(display_dev);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * snd_hdac_i915_init - Initialize i915 audio component
+ * @bus: HDA core bus
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function initializes and sets up the audio component to communicate
+ * with i915 graphics driver.
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_i915_init(struct hdac_bus *bus)
+{
+ struct drm_audio_component *acomp;
+ int err;
+
+ if (!i915_gfx_present(to_pci_dev(bus->dev)))
+ return -ENODEV;
+
+ err = snd_hdac_acomp_init(bus, NULL,
+ i915_component_master_match,
+ sizeof(struct i915_audio_component) - sizeof(*acomp));
+ if (err < 0)
+ return err;
+ acomp = bus->audio_component;
+ if (!acomp)
+ return -ENODEV;
+ if (!acomp->ops) {
+ snd_hdac_acomp_exit(bus);
+ return dev_err_probe(bus->dev, -EPROBE_DEFER,
+ "couldn't bind with audio component\n");
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_i915_init);
diff --git a/sound/hda/core/intel-dsp-config.c b/sound/hda/core/intel-dsp-config.c
new file mode 100644
index 000000000000..c401c0658421
--- /dev/null
+++ b/sound/hda/core/intel-dsp-config.c
@@ -0,0 +1,851 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Jaroslav Kysela <perex@perex.cz>
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_intel.h>
+#include <sound/core.h>
+#include <sound/intel-dsp-config.h>
+#include <sound/intel-nhlt.h>
+#include <sound/soc-acpi.h>
+
+#include <acpi/nhlt.h>
+
+static int dsp_driver;
+
+module_param(dsp_driver, int, 0444);
+MODULE_PARM_DESC(dsp_driver, "Force the DSP driver for Intel DSP (0=auto, 1=legacy, 2=SST, 3=SOF, 4=AVS)");
+
+#define FLAG_SST BIT(0)
+#define FLAG_SOF BIT(1)
+#define FLAG_SST_ONLY_IF_DMIC BIT(15)
+#define FLAG_SOF_ONLY_IF_DMIC BIT(16)
+#define FLAG_SOF_ONLY_IF_SOUNDWIRE BIT(17)
+
+#define FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE (FLAG_SOF_ONLY_IF_DMIC | \
+ FLAG_SOF_ONLY_IF_SOUNDWIRE)
+
+struct config_entry {
+ u32 flags;
+ u16 device;
+ u8 acpi_hid[ACPI_ID_LEN];
+ const struct dmi_system_id *dmi_table;
+ const struct snd_soc_acpi_codecs *codec_hid;
+};
+
+static const struct snd_soc_acpi_codecs __maybe_unused essx_83x6 = {
+ .num_codecs = 3,
+ .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
+};
+
+/*
+ * configuration table
+ * - the order of similar PCI ID entries is important!
+ * - the first successful match will win
+ */
+static const struct config_entry config_table[] = {
+/* Merrifield */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_MERRIFIELD)
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_SST_TNG,
+ },
+#endif
+/*
+ * Skylake, Kabylake, Apollolake
+ * the legacy HDAudio driver is used except on Up Squared (SOF) and
+ * Chromebooks (SST), as well as devices based on the ES8336 codec
+ */
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_AVS)
+ {
+ .flags = FLAG_SST,
+ .device = PCI_DEVICE_ID_INTEL_HDA_SKL_LP,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
+ .device = PCI_DEVICE_ID_INTEL_HDA_SKL_LP,
+ },
+ {
+ .flags = FLAG_SST,
+ .device = PCI_DEVICE_ID_INTEL_HDA_KBL_LP,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC,
+ .device = PCI_DEVICE_ID_INTEL_HDA_KBL_LP,
+ },
+ {
+ .flags = FLAG_SST,
+ .device = PCI_DEVICE_ID_INTEL_HDA_APL,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SST,
+ .device = PCI_DEVICE_ID_INTEL_HDA_RPL_M,
+ },
+ {
+ .flags = FLAG_SST,
+ .device = PCI_DEVICE_ID_INTEL_HDA_FCL,
+ },
+#else /* AVS disabled; force to legacy as SOF doesn't work for SKL or KBL */
+ {
+ .device = PCI_DEVICE_ID_INTEL_HDA_SKL_LP,
+ },
+ {
+ .device = PCI_DEVICE_ID_INTEL_HDA_KBL_LP,
+ },
+#endif
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_APL,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Up Squared",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
+ DMI_MATCH(DMI_BOARD_NAME, "UP-APL01"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_APL,
+ .codec_hid = &essx_83x6,
+ },
+#endif
+
+/*
+ * Geminilake uses legacy HDAudio driver except for Google
+ * Chromebooks and devices based on the ES8336 codec
+ */
+/* Geminilake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_GML,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_GML,
+ .codec_hid = &essx_83x6,
+ },
+#endif
+
+/*
+ * CoffeeLake, CannonLake, CometLake, IceLake, TigerLake, AlderLake,
+ * RaptorLake, MeteorLake use legacy HDAudio driver except for Google
+ * Chromebooks and when DMICs are present. Two cases are required since
+ * Coreboot does not expose NHLT tables.
+ *
+ * When the Chromebook quirk is not present, it's based on information
+ * that no such device exists. When the quirk is present, it could be
+ * either based on product information or a placeholder.
+ */
+
+/* Cannonlake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_CNL_LP,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {
+ .ident = "UP-WHL",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_CNL_LP,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_CNL_LP,
+ },
+#endif
+
+/* Coffelake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_CNL_H,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_CNL_H,
+ },
+#endif
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE)
+/* Cometlake-LP */
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_CML_LP,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6")
+ },
+ },
+ {
+ /* early version of SKU 09C6 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983")
+ },
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_CML_LP,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_CML_LP,
+ },
+/* Cometlake-H */
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_CML_H,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"),
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"),
+ },
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_CML_H,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_CML_H,
+ },
+#endif
+
+/* Icelake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ICL_LP,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ICL_LP,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ICL_LP,
+ },
+#endif
+
+/* Jasper Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_JSL_N,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {
+ .ident = "Google firmware",
+ .matches = {
+ DMI_MATCH(DMI_BIOS_VERSION, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_JSL_N,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+ .device = PCI_DEVICE_ID_INTEL_HDA_JSL_N,
+ },
+#endif
+
+/* Tigerlake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE)
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_TGL_LP,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {
+ .ident = "UPX-TGL",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_TGL_LP,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_TGL_LP,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_TGL_H,
+ },
+#endif
+
+/* Elkhart Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE)
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+ .device = PCI_DEVICE_ID_INTEL_HDA_EHL_0,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC,
+ .device = PCI_DEVICE_ID_INTEL_HDA_EHL_3,
+ },
+#endif
+
+/* Alder Lake / Raptor Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_ALDERLAKE)
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_S,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_RPL_S,
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_P,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_P,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_P,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_PX,
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_PS,
+ .codec_hid = &essx_83x6,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_PS,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_M,
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_N,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ADL_N,
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_0,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_0,
+ },
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_1,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_RPL_P_1,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_RPL_M,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_RPL_PX,
+ },
+#endif
+
+/* Meteor Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_METEORLAKE)
+ /* Meteorlake-P */
+ {
+ .flags = FLAG_SOF,
+ .device = PCI_DEVICE_ID_INTEL_HDA_MTL,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .ident = "Google Chromebooks",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ }
+ },
+ {}
+ }
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_MTL,
+ },
+ /* ArrowLake-S */
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ARL_S,
+ },
+ /* ArrowLake */
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_ARL,
+ },
+#endif
+
+/* Lunar Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_LUNARLAKE)
+ /* Lunarlake-P */
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_LNL_P,
+ },
+#endif
+
+ /* Panther Lake, Wildcat Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_PANTHERLAKE)
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_PTL,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_PTL_H,
+ },
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_WCL,
+ },
+
+#endif
+
+ /* Nova Lake */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_NOVALAKE)
+ {
+ .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+ .device = PCI_DEVICE_ID_INTEL_HDA_NVL_S,
+ },
+#endif
+
+};
+
+static const struct config_entry *snd_intel_dsp_find_config
+ (struct pci_dev *pci, const struct config_entry *table, u32 len)
+{
+ u16 device;
+
+ device = pci->device;
+ for (; len > 0; len--, table++) {
+ if (table->device != device)
+ continue;
+ if (table->dmi_table && !dmi_check_system(table->dmi_table))
+ continue;
+ if (table->codec_hid) {
+ int i;
+
+ for (i = 0; i < table->codec_hid->num_codecs; i++) {
+ struct nhlt_acpi_table *nhlt;
+ bool ssp_found = false;
+
+ if (!acpi_dev_present(table->codec_hid->codecs[i], NULL, -1))
+ continue;
+
+ nhlt = intel_nhlt_init(&pci->dev);
+ if (!nhlt) {
+ dev_warn(&pci->dev, "%s: NHLT table not found, skipped HID %s\n",
+ __func__, table->codec_hid->codecs[i]);
+ continue;
+ }
+
+ if (intel_nhlt_has_endpoint_type(nhlt, NHLT_LINK_SSP) &&
+ intel_nhlt_ssp_endpoint_mask(nhlt, NHLT_DEVICE_I2S))
+ ssp_found = true;
+
+ intel_nhlt_free(nhlt);
+
+ if (ssp_found)
+ break;
+
+ dev_warn(&pci->dev, "%s: no valid SSP found for HID %s, skipped\n",
+ __func__, table->codec_hid->codecs[i]);
+ }
+ if (i == table->codec_hid->num_codecs)
+ continue;
+ }
+ return table;
+ }
+ return NULL;
+}
+
+static int snd_intel_dsp_check_dmic(struct pci_dev *pci)
+{
+ int ret = 0;
+
+ acpi_nhlt_get_gbl_table();
+
+ if (acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_PDM, -1, -1, -1))
+ ret = 1;
+
+ acpi_nhlt_put_gbl_table();
+
+ return ret;
+}
+
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
+static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
+{
+ struct sdw_intel_acpi_info info;
+ acpi_handle handle;
+ int ret;
+
+ handle = ACPI_HANDLE(&pci->dev);
+ if (!handle)
+ return -ENODEV;
+
+ ret = sdw_intel_acpi_scan(handle, &info);
+ if (ret < 0)
+ return ret;
+
+ return info.link_mask;
+}
+#else
+static int snd_intel_dsp_check_soundwire(struct pci_dev *pci)
+{
+ return 0;
+}
+#endif
+
+int snd_intel_dsp_driver_probe(struct pci_dev *pci)
+{
+ const struct config_entry *cfg;
+
+ /* Intel vendor only */
+ if (pci->vendor != PCI_VENDOR_ID_INTEL)
+ return SND_INTEL_DSP_DRIVER_ANY;
+
+ /*
+ * Legacy devices don't have a PCI-based DSP and use HDaudio
+ * for HDMI/DP support, ignore kernel parameter
+ */
+ switch (pci->device) {
+ case PCI_DEVICE_ID_INTEL_HDA_BDW:
+ case PCI_DEVICE_ID_INTEL_HDA_HSW_0:
+ case PCI_DEVICE_ID_INTEL_HDA_HSW_2:
+ case PCI_DEVICE_ID_INTEL_HDA_HSW_3:
+ case PCI_DEVICE_ID_INTEL_HDA_BYT:
+ case PCI_DEVICE_ID_INTEL_HDA_BSW:
+ return SND_INTEL_DSP_DRIVER_ANY;
+ }
+
+ if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST)
+ return dsp_driver;
+
+ /*
+ * detect DSP by checking class/subclass/prog-id information
+ * class=04 subclass 03 prog-if 00: no DSP, use legacy driver
+ * class=04 subclass 01 prog-if 00: DSP is present
+ * (and may be required e.g. for DMIC or SSP support)
+ * class=04 subclass 03 prog-if 80: use DSP or legacy mode
+ */
+ if (pci->class == 0x040300)
+ return SND_INTEL_DSP_DRIVER_LEGACY;
+ if (pci->class != 0x040100 && pci->class != 0x040380) {
+ dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, selecting HDAudio legacy driver\n", pci->class);
+ return SND_INTEL_DSP_DRIVER_LEGACY;
+ }
+
+ dev_dbg(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class);
+
+ /* find the configuration for the specific device */
+ cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table));
+ if (!cfg)
+ return SND_INTEL_DSP_DRIVER_ANY;
+
+ if (cfg->flags & FLAG_SOF) {
+ if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE &&
+ snd_intel_dsp_check_soundwire(pci) > 0) {
+ dev_info_once(&pci->dev, "SoundWire enabled on CannonLake+ platform, using SOF driver\n");
+ return SND_INTEL_DSP_DRIVER_SOF;
+ }
+ if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC &&
+ snd_intel_dsp_check_dmic(pci)) {
+ dev_info_once(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n");
+ return SND_INTEL_DSP_DRIVER_SOF;
+ }
+ if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE))
+ return SND_INTEL_DSP_DRIVER_SOF;
+ }
+
+
+ if (cfg->flags & FLAG_SST) {
+ if (cfg->flags & FLAG_SST_ONLY_IF_DMIC) {
+ if (snd_intel_dsp_check_dmic(pci)) {
+ dev_info_once(&pci->dev, "Digital mics found on Skylake+ platform, using SST driver\n");
+ return SND_INTEL_DSP_DRIVER_SST;
+ }
+ } else {
+ return SND_INTEL_DSP_DRIVER_SST;
+ }
+ }
+
+ return SND_INTEL_DSP_DRIVER_LEGACY;
+}
+EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe);
+
+/* Should we default to SOF or SST for BYT/CHT ? */
+#if IS_ENABLED(CONFIG_SND_INTEL_BYT_PREFER_SOF) || \
+ !IS_ENABLED(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI)
+#define FLAG_SST_OR_SOF_BYT FLAG_SOF
+#else
+#define FLAG_SST_OR_SOF_BYT FLAG_SST
+#endif
+
+/*
+ * configuration table
+ * - the order of similar ACPI ID entries is important!
+ * - the first successful match will win
+ */
+static const struct config_entry acpi_config_table[] = {
+#if IS_ENABLED(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI) || \
+ IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL)
+/* BayTrail */
+ {
+ .flags = FLAG_SST_OR_SOF_BYT,
+ .acpi_hid = "LPE0F28",
+ },
+ {
+ .flags = FLAG_SST_OR_SOF_BYT,
+ .acpi_hid = "80860F28",
+ },
+/* CherryTrail */
+ {
+ .flags = FLAG_SST_OR_SOF_BYT,
+ .acpi_hid = "808622A8",
+ },
+#endif
+/* Broadwell */
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CATPT)
+ {
+ .flags = FLAG_SST,
+ .acpi_hid = "INT3438"
+ },
+#endif
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL)
+ {
+ .flags = FLAG_SOF,
+ .acpi_hid = "INT3438"
+ },
+#endif
+/* Haswell - not supported by SOF but added for consistency */
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CATPT)
+ {
+ .flags = FLAG_SST,
+ .acpi_hid = "INT33C8"
+ },
+#endif
+};
+
+static const struct config_entry *snd_intel_acpi_dsp_find_config(const u8 acpi_hid[ACPI_ID_LEN],
+ const struct config_entry *table,
+ u32 len)
+{
+ for (; len > 0; len--, table++) {
+ if (memcmp(table->acpi_hid, acpi_hid, ACPI_ID_LEN))
+ continue;
+ if (table->dmi_table && !dmi_check_system(table->dmi_table))
+ continue;
+ return table;
+ }
+ return NULL;
+}
+
+int snd_intel_acpi_dsp_driver_probe(struct device *dev, const u8 acpi_hid[ACPI_ID_LEN])
+{
+ const struct config_entry *cfg;
+
+ if (dsp_driver > SND_INTEL_DSP_DRIVER_LEGACY && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST)
+ return dsp_driver;
+
+ if (dsp_driver == SND_INTEL_DSP_DRIVER_LEGACY) {
+ dev_warn(dev, "dsp_driver parameter %d not supported, using automatic detection\n",
+ SND_INTEL_DSP_DRIVER_LEGACY);
+ }
+
+ /* find the configuration for the specific device */
+ cfg = snd_intel_acpi_dsp_find_config(acpi_hid, acpi_config_table,
+ ARRAY_SIZE(acpi_config_table));
+ if (!cfg)
+ return SND_INTEL_DSP_DRIVER_ANY;
+
+ if (cfg->flags & FLAG_SST)
+ return SND_INTEL_DSP_DRIVER_SST;
+
+ if (cfg->flags & FLAG_SOF)
+ return SND_INTEL_DSP_DRIVER_SOF;
+
+ return SND_INTEL_DSP_DRIVER_SST;
+}
+EXPORT_SYMBOL_GPL(snd_intel_acpi_dsp_driver_probe);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel DSP config driver");
+MODULE_IMPORT_NS("SND_INTEL_SOUNDWIRE_ACPI");
diff --git a/sound/hda/core/intel-nhlt.c b/sound/hda/core/intel-nhlt.c
new file mode 100644
index 000000000000..6d72a871bda0
--- /dev/null
+++ b/sound/hda/core/intel-nhlt.c
@@ -0,0 +1,388 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2015-2019 Intel Corporation
+
+#include <linux/acpi.h>
+#include <sound/intel-nhlt.h>
+
+struct nhlt_acpi_table *intel_nhlt_init(struct device *dev)
+{
+ struct nhlt_acpi_table *nhlt;
+ acpi_status status;
+
+ status = acpi_get_table(ACPI_SIG_NHLT, 0,
+ (struct acpi_table_header **)&nhlt);
+ if (ACPI_FAILURE(status)) {
+ dev_warn(dev, "NHLT table not found\n");
+ return NULL;
+ }
+
+ return nhlt;
+}
+EXPORT_SYMBOL_GPL(intel_nhlt_init);
+
+void intel_nhlt_free(struct nhlt_acpi_table *nhlt)
+{
+ acpi_put_table((struct acpi_table_header *)nhlt);
+}
+EXPORT_SYMBOL_GPL(intel_nhlt_free);
+
+int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt)
+{
+ struct nhlt_endpoint *epnt;
+ struct nhlt_dmic_array_config *cfg;
+ struct nhlt_vendor_dmic_array_config *cfg_vendor;
+ struct nhlt_fmt *fmt_configs;
+ unsigned int dmic_geo = 0;
+ u16 max_ch = 0;
+ u8 i, j;
+
+ if (!nhlt)
+ return 0;
+
+ if (nhlt->header.length <= sizeof(struct acpi_table_header)) {
+ dev_warn(dev, "Invalid DMIC description table\n");
+ return 0;
+ }
+
+ for (j = 0, epnt = nhlt->desc; j < nhlt->endpoint_count; j++,
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length)) {
+
+ if (epnt->linktype != NHLT_LINK_DMIC)
+ continue;
+
+ cfg = (struct nhlt_dmic_array_config *)(epnt->config.caps);
+ fmt_configs = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size);
+
+ /* find max number of channels based on format_configuration */
+ if (fmt_configs->fmt_count) {
+ struct nhlt_fmt_cfg *fmt_cfg = fmt_configs->fmt_config;
+
+ dev_dbg(dev, "found %d format definitions\n",
+ fmt_configs->fmt_count);
+
+ for (i = 0; i < fmt_configs->fmt_count; i++) {
+ struct wav_fmt_ext *fmt_ext;
+
+ fmt_ext = &fmt_cfg->fmt_ext;
+
+ if (fmt_ext->fmt.channels > max_ch)
+ max_ch = fmt_ext->fmt.channels;
+
+ /* Move to the next nhlt_fmt_cfg */
+ fmt_cfg = (struct nhlt_fmt_cfg *)(fmt_cfg->config.caps +
+ fmt_cfg->config.size);
+ }
+ dev_dbg(dev, "max channels found %d\n", max_ch);
+ } else {
+ dev_dbg(dev, "No format information found\n");
+ }
+
+ if (cfg->device_config.config_type != NHLT_CONFIG_TYPE_MIC_ARRAY) {
+ dmic_geo = max_ch;
+ } else {
+ switch (cfg->array_type) {
+ case NHLT_MIC_ARRAY_2CH_SMALL:
+ case NHLT_MIC_ARRAY_2CH_BIG:
+ dmic_geo = MIC_ARRAY_2CH;
+ break;
+
+ case NHLT_MIC_ARRAY_4CH_1ST_GEOM:
+ case NHLT_MIC_ARRAY_4CH_L_SHAPED:
+ case NHLT_MIC_ARRAY_4CH_2ND_GEOM:
+ dmic_geo = MIC_ARRAY_4CH;
+ break;
+ case NHLT_MIC_ARRAY_VENDOR_DEFINED:
+ cfg_vendor = (struct nhlt_vendor_dmic_array_config *)cfg;
+ dmic_geo = cfg_vendor->nb_mics;
+ break;
+ default:
+ dev_warn(dev, "%s: undefined DMIC array_type 0x%0x\n",
+ __func__, cfg->array_type);
+ }
+
+ if (dmic_geo > 0) {
+ dev_dbg(dev, "Array with %d dmics\n", dmic_geo);
+ }
+ if (max_ch > dmic_geo) {
+ dev_dbg(dev, "max channels %d exceed dmic number %d\n",
+ max_ch, dmic_geo);
+ }
+ }
+ }
+
+ dev_dbg(dev, "dmic number %d max_ch %d\n", dmic_geo, max_ch);
+
+ return dmic_geo;
+}
+EXPORT_SYMBOL_GPL(intel_nhlt_get_dmic_geo);
+
+bool intel_nhlt_has_endpoint_type(struct nhlt_acpi_table *nhlt, u8 link_type)
+{
+ struct nhlt_endpoint *epnt;
+ int i;
+
+ if (!nhlt)
+ return false;
+
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
+ for (i = 0; i < nhlt->endpoint_count; i++) {
+ if (epnt->linktype == link_type)
+ return true;
+
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+ return false;
+}
+EXPORT_SYMBOL(intel_nhlt_has_endpoint_type);
+
+int intel_nhlt_ssp_endpoint_mask(struct nhlt_acpi_table *nhlt, u8 device_type)
+{
+ struct nhlt_endpoint *epnt;
+ int ssp_mask = 0;
+ int i;
+
+ if (!nhlt || (device_type != NHLT_DEVICE_BT && device_type != NHLT_DEVICE_I2S))
+ return 0;
+
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
+ for (i = 0; i < nhlt->endpoint_count; i++) {
+ if (epnt->linktype == NHLT_LINK_SSP && epnt->device_type == device_type) {
+ /* for SSP the virtual bus id is the SSP port */
+ ssp_mask |= BIT(epnt->virtual_bus_id);
+ }
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+
+ return ssp_mask;
+}
+EXPORT_SYMBOL(intel_nhlt_ssp_endpoint_mask);
+
+#define SSP_BLOB_V1_0_SIZE 84
+#define SSP_BLOB_V1_0_MDIVC_OFFSET 19 /* offset in u32 */
+
+#define SSP_BLOB_V1_5_SIZE 96
+#define SSP_BLOB_V1_5_MDIVC_OFFSET 21 /* offset in u32 */
+#define SSP_BLOB_VER_1_5 0xEE000105
+
+#define SSP_BLOB_V2_0_SIZE 88
+#define SSP_BLOB_V2_0_MDIVC_OFFSET 20 /* offset in u32 */
+#define SSP_BLOB_VER_2_0 0xEE000200
+
+int intel_nhlt_ssp_mclk_mask(struct nhlt_acpi_table *nhlt, int ssp_num)
+{
+ struct nhlt_endpoint *epnt;
+ struct nhlt_fmt *fmt;
+ struct nhlt_fmt_cfg *cfg;
+ int mclk_mask = 0;
+ int i, j;
+
+ if (!nhlt)
+ return 0;
+
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
+ for (i = 0; i < nhlt->endpoint_count; i++) {
+
+ /* we only care about endpoints connected to an audio codec over SSP */
+ if (epnt->linktype == NHLT_LINK_SSP &&
+ epnt->device_type == NHLT_DEVICE_I2S &&
+ epnt->virtual_bus_id == ssp_num) {
+
+ fmt = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size);
+ cfg = fmt->fmt_config;
+
+ /*
+ * In theory all formats should use the same MCLK but it doesn't hurt to
+ * double-check that the configuration is consistent
+ */
+ for (j = 0; j < fmt->fmt_count; j++) {
+ u32 *blob;
+ int mdivc_offset;
+ int size;
+
+ /* first check we have enough data to read the blob type */
+ if (cfg->config.size < 8)
+ return -EINVAL;
+
+ blob = (u32 *)cfg->config.caps;
+
+ if (blob[1] == SSP_BLOB_VER_2_0) {
+ mdivc_offset = SSP_BLOB_V2_0_MDIVC_OFFSET;
+ size = SSP_BLOB_V2_0_SIZE;
+ } else if (blob[1] == SSP_BLOB_VER_1_5) {
+ mdivc_offset = SSP_BLOB_V1_5_MDIVC_OFFSET;
+ size = SSP_BLOB_V1_5_SIZE;
+ } else {
+ mdivc_offset = SSP_BLOB_V1_0_MDIVC_OFFSET;
+ size = SSP_BLOB_V1_0_SIZE;
+ }
+
+ /* make sure we have enough data for the fixed part of the blob */
+ if (cfg->config.size < size)
+ return -EINVAL;
+
+ mclk_mask |= blob[mdivc_offset] & GENMASK(1, 0);
+
+ cfg = (struct nhlt_fmt_cfg *)(cfg->config.caps + cfg->config.size);
+ }
+ }
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+
+ /* make sure only one MCLK is used */
+ if (hweight_long(mclk_mask) != 1)
+ return -EINVAL;
+
+ return mclk_mask;
+}
+EXPORT_SYMBOL(intel_nhlt_ssp_mclk_mask);
+
+static struct nhlt_specific_cfg *
+nhlt_get_specific_cfg(struct device *dev, struct nhlt_fmt *fmt, u8 num_ch,
+ u32 rate, u8 vbps, u8 bps, bool ignore_vbps)
+{
+ struct nhlt_fmt_cfg *cfg = fmt->fmt_config;
+ struct wav_fmt *wfmt;
+ u16 _bps, _vbps;
+ int i;
+
+ dev_dbg(dev, "Endpoint format count=%d\n", fmt->fmt_count);
+
+ for (i = 0; i < fmt->fmt_count; i++) {
+ wfmt = &cfg->fmt_ext.fmt;
+ _bps = wfmt->bits_per_sample;
+ _vbps = cfg->fmt_ext.sample.valid_bits_per_sample;
+
+ dev_dbg(dev, "Endpoint format: ch=%d fmt=%d/%d rate=%d\n",
+ wfmt->channels, _vbps, _bps, wfmt->samples_per_sec);
+
+ /*
+ * When looking for exact match of configuration ignore the vbps
+ * from NHLT table when ignore_vbps is true
+ */
+ if (wfmt->channels == num_ch && wfmt->samples_per_sec == rate &&
+ (ignore_vbps || vbps == _vbps) && bps == _bps)
+ return &cfg->config;
+
+ cfg = (struct nhlt_fmt_cfg *)(cfg->config.caps + cfg->config.size);
+ }
+
+ return NULL;
+}
+
+static bool nhlt_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt,
+ u32 bus_id, u8 link_type, u8 dir, u8 dev_type)
+{
+ dev_dbg(dev, "Endpoint: vbus_id=%d link_type=%d dir=%d dev_type = %d\n",
+ epnt->virtual_bus_id, epnt->linktype,
+ epnt->direction, epnt->device_type);
+
+ if ((epnt->virtual_bus_id != bus_id) ||
+ (epnt->linktype != link_type) ||
+ (epnt->direction != dir))
+ return false;
+
+ /* link of type DMIC bypasses device_type check */
+ return epnt->linktype == NHLT_LINK_DMIC ||
+ epnt->device_type == dev_type;
+}
+
+struct nhlt_specific_cfg *
+intel_nhlt_get_endpoint_blob(struct device *dev, struct nhlt_acpi_table *nhlt,
+ u32 bus_id, u8 link_type, u8 vbps, u8 bps,
+ u8 num_ch, u32 rate, u8 dir, u8 dev_type)
+{
+ struct nhlt_specific_cfg *cfg;
+ struct nhlt_endpoint *epnt;
+ bool ignore_vbps = false;
+ struct nhlt_fmt *fmt;
+ int i;
+
+ if (!nhlt)
+ return NULL;
+
+ dev_dbg(dev, "Looking for configuration:\n");
+ dev_dbg(dev, " vbus_id=%d link_type=%d dir=%d, dev_type=%d\n",
+ bus_id, link_type, dir, dev_type);
+ if (link_type == NHLT_LINK_DMIC && bps == 32 && (vbps == 24 || vbps == 32)) {
+ /*
+ * The DMIC hardware supports only one type of 32 bits sample
+ * size, which is 24 bit sampling on the MSB side and bits[1:0]
+ * are used for indicating the channel number.
+ * It has been observed that some NHLT tables have the vbps
+ * specified as 32 while some uses 24.
+ * The format these variations describe are identical, the
+ * hardware is configured and behaves the same way.
+ * Note: when the samples assumed to be vbps=32 then the 'noise'
+ * introduced by the lower two bits (channel number) have no
+ * real life implication on audio quality.
+ */
+ dev_dbg(dev,
+ " ch=%d fmt=%d rate=%d (vbps is ignored for DMIC 32bit format)\n",
+ num_ch, bps, rate);
+ ignore_vbps = true;
+ } else {
+ dev_dbg(dev, " ch=%d fmt=%d/%d rate=%d\n", num_ch, vbps, bps, rate);
+ }
+ dev_dbg(dev, "Endpoint count=%d\n", nhlt->endpoint_count);
+
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
+
+ for (i = 0; i < nhlt->endpoint_count; i++) {
+ if (nhlt_check_ep_match(dev, epnt, bus_id, link_type, dir, dev_type)) {
+ fmt = (struct nhlt_fmt *)(epnt->config.caps + epnt->config.size);
+
+ cfg = nhlt_get_specific_cfg(dev, fmt, num_ch, rate,
+ vbps, bps, ignore_vbps);
+ if (cfg)
+ return cfg;
+ }
+
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(intel_nhlt_get_endpoint_blob);
+
+int intel_nhlt_ssp_device_type(struct device *dev, struct nhlt_acpi_table *nhlt,
+ u8 virtual_bus_id)
+{
+ struct nhlt_endpoint *epnt;
+ int i;
+
+ if (!nhlt) {
+ dev_err(dev, "%s: NHLT table is missing (query for SSP%d)\n",
+ __func__, virtual_bus_id);
+ return -EINVAL;
+ }
+
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
+ for (i = 0; i < nhlt->endpoint_count; i++) {
+ /* for SSP link the virtual bus id is the SSP port number */
+ if (epnt->linktype == NHLT_LINK_SSP &&
+ epnt->virtual_bus_id == virtual_bus_id) {
+ dev_dbg(dev, "SSP%d: dev_type=%d\n", virtual_bus_id,
+ epnt->device_type);
+ return epnt->device_type;
+ }
+
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+
+ dev_err(dev, "%s: No match for SSP%d in NHLT table\n", __func__,
+ virtual_bus_id);
+
+ dev_dbg(dev, "Available endpoints:\n");
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
+ for (i = 0; i < nhlt->endpoint_count; i++) {
+ dev_dbg(dev,
+ "%d: link_type: %d, vbus_id: %d, dir: %d, dev_type: %d\n",
+ i, epnt->linktype, epnt->virtual_bus_id,
+ epnt->direction, epnt->device_type);
+
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(intel_nhlt_ssp_device_type);
diff --git a/sound/hda/core/intel-sdw-acpi.c b/sound/hda/core/intel-sdw-acpi.c
new file mode 100644
index 000000000000..d3511135f7d3
--- /dev/null
+++ b/sound/hda/core/intel-sdw-acpi.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2015-2021 Intel Corporation.
+
+/*
+ * SDW Intel ACPI scan helpers
+ */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/soundwire/sdw_intel.h>
+#include <linux/string.h>
+
+#define SDW_LINK_TYPE 4 /* from Intel ACPI documentation */
+
+static int ctrl_link_mask;
+module_param_named(sdw_link_mask, ctrl_link_mask, int, 0444);
+MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)");
+
+static ulong ctrl_addr = 0x40000000;
+module_param_named(sdw_ctrl_addr, ctrl_addr, ulong, 0444);
+MODULE_PARM_DESC(sdw_ctrl_addr, "Intel SoundWire Controller _ADR");
+
+static bool is_link_enabled(struct fwnode_handle *fw_node, u8 idx)
+{
+ struct fwnode_handle *link;
+ char name[32];
+ u32 quirk_mask = 0;
+
+ /* Find master handle */
+ snprintf(name, sizeof(name),
+ "mipi-sdw-link-%hhu-subproperties", idx);
+
+ link = fwnode_get_named_child_node(fw_node, name);
+ if (!link)
+ return false;
+
+ fwnode_property_read_u32(link,
+ "intel-quirk-mask",
+ &quirk_mask);
+
+ fwnode_handle_put(link);
+
+ if (quirk_mask & SDW_INTEL_QUIRK_MASK_BUS_DISABLE)
+ return false;
+
+ return true;
+}
+
+static int
+sdw_intel_scan_controller(struct sdw_intel_acpi_info *info)
+{
+ struct acpi_device *adev = acpi_fetch_acpi_dev(info->handle);
+ struct fwnode_handle *fwnode;
+ unsigned long list;
+ unsigned int i;
+ u32 count;
+ u32 tmp;
+ int ret;
+
+ if (!adev)
+ return -EINVAL;
+
+ fwnode = acpi_fwnode_handle(adev);
+
+ /*
+ * Found controller, find links supported
+ *
+ * In theory we could check the number of links supported in
+ * hardware, but in that step we cannot assume SoundWire IP is
+ * powered.
+ *
+ * In addition, if the BIOS doesn't even provide this
+ * 'master-count' property then all the inits based on link
+ * masks will fail as well.
+ *
+ * We will check the hardware capabilities in the startup() step
+ */
+ ret = fwnode_property_read_u32(fwnode, "mipi-sdw-manager-list", &tmp);
+ if (ret) {
+ ret = fwnode_property_read_u32(fwnode, "mipi-sdw-master-count", &count);
+ if (ret) {
+ dev_err(&adev->dev,
+ "Failed to read mipi-sdw-master-count: %d\n",
+ ret);
+ return ret;
+ }
+ list = GENMASK(count - 1, 0);
+ } else {
+ list = tmp;
+ count = hweight32(list);
+ }
+
+ /* Check count is within bounds */
+ if (count > SDW_INTEL_MAX_LINKS) {
+ dev_err(&adev->dev, "Link count %d exceeds max %d\n",
+ count, SDW_INTEL_MAX_LINKS);
+ return -EINVAL;
+ }
+
+ if (!count) {
+ dev_warn(&adev->dev, "No SoundWire links detected\n");
+ return -EINVAL;
+ }
+ dev_dbg(&adev->dev, "ACPI reports %d SDW Link devices\n", count);
+
+ info->count = count;
+ info->link_mask = 0;
+
+ for_each_set_bit(i, &list, SDW_INTEL_MAX_LINKS) {
+ if (ctrl_link_mask && !(ctrl_link_mask & BIT(i))) {
+ dev_dbg(&adev->dev,
+ "Link %d masked, will not be enabled\n", i);
+ continue;
+ }
+
+ if (!is_link_enabled(fwnode, i)) {
+ dev_dbg(&adev->dev,
+ "Link %d not selected in firmware\n", i);
+ continue;
+ }
+
+ info->link_mask |= BIT(i);
+ }
+
+ return 0;
+}
+
+static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
+ void *cdata, void **return_value)
+{
+ struct sdw_intel_acpi_info *info = cdata;
+ u64 adr;
+ int ret;
+
+ ret = acpi_get_local_u64_address(handle, &adr);
+ if (ret < 0)
+ return AE_OK; /* keep going */
+
+ if (!acpi_fetch_acpi_dev(handle)) {
+ pr_err("%s: Couldn't find ACPI handle\n", __func__);
+ return AE_NOT_FOUND;
+ }
+
+ /*
+ * On some Intel platforms, multiple children of the HDAS
+ * device can be found, but only one of them is the SoundWire
+ * controller. The SNDW device is always exposed with
+ * Name(_ADR, 0x40000000), with bits 31..28 representing the
+ * SoundWire link so filter accordingly
+ */
+ if (FIELD_GET(GENMASK(31, 28), adr) != SDW_LINK_TYPE)
+ return AE_OK; /* keep going */
+
+ if (adr != ctrl_addr)
+ return AE_OK; /* keep going */
+
+ /* found the correct SoundWire controller */
+ info->handle = handle;
+
+ /* device found, stop namespace walk */
+ return AE_CTRL_TERMINATE;
+}
+
+/**
+ * sdw_intel_acpi_scan() - SoundWire Intel init routine
+ * @parent_handle: ACPI parent handle
+ * @info: description of what firmware/DSDT tables expose
+ *
+ * This scans the namespace and queries firmware to figure out which
+ * links to enable. A follow-up use of sdw_intel_probe() and
+ * sdw_intel_startup() is required for creation of devices and bus
+ * startup
+ */
+int sdw_intel_acpi_scan(acpi_handle parent_handle,
+ struct sdw_intel_acpi_info *info)
+{
+ acpi_status status;
+
+ info->handle = NULL;
+ /*
+ * In the HDAS ACPI scope, 'SNDW' may be either the child of
+ * 'HDAS' or the grandchild of 'HDAS'. So let's go through
+ * the ACPI from 'HDAS' at max depth of 2 to find the 'SNDW'
+ * device.
+ */
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE,
+ parent_handle, 2,
+ sdw_intel_acpi_cb,
+ NULL, info, NULL);
+ if (ACPI_FAILURE(status) || info->handle == NULL)
+ return -ENODEV;
+
+ return sdw_intel_scan_controller(info);
+}
+EXPORT_SYMBOL_NS(sdw_intel_acpi_scan, "SND_INTEL_SOUNDWIRE_ACPI");
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("Intel Soundwire ACPI helpers");
diff --git a/sound/hda/core/local.h b/sound/hda/core/local.h
new file mode 100644
index 000000000000..5f03b203c416
--- /dev/null
+++ b/sound/hda/core/local.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Local helper macros and functions for HD-audio core drivers
+ */
+
+#ifndef __HDAC_LOCAL_H
+#define __HDAC_LOCAL_H
+
+extern const struct attribute_group *hdac_dev_attr_groups[];
+int hda_widget_sysfs_init(struct hdac_device *codec);
+int hda_widget_sysfs_reinit(struct hdac_device *codec, hda_nid_t start_nid,
+ int num_nodes);
+void hda_widget_sysfs_exit(struct hdac_device *codec);
+
+int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec);
+void snd_hdac_bus_remove_device(struct hdac_bus *bus,
+ struct hdac_device *codec);
+void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex);
+int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr,
+ unsigned int cmd, unsigned int *res);
+
+int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd,
+ unsigned int flags, unsigned int *res);
+
+#endif /* __HDAC_LOCAL_H */
diff --git a/sound/hda/core/regmap.c b/sound/hda/core/regmap.c
new file mode 100644
index 000000000000..e7b866fc52c1
--- /dev/null
+++ b/sound/hda/core/regmap.c
@@ -0,0 +1,588 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Regmap support for HD-audio verbs
+ *
+ * A virtual register is translated to one or more hda verbs for write,
+ * vice versa for read.
+ *
+ * A few limitations:
+ * - Provided for not all verbs but only subset standard non-volatile verbs.
+ * - For reading, only AC_VERB_GET_* variants can be used.
+ * - For writing, mapped to the *corresponding* AC_VERB_SET_* variants,
+ * so can't handle asymmetric verbs for read and write
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/export.h>
+#include <linux/pm.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_regmap.h>
+#include "local.h"
+
+static int codec_pm_lock(struct hdac_device *codec)
+{
+ return snd_hdac_keep_power_up(codec);
+}
+
+static void codec_pm_unlock(struct hdac_device *codec, int lock)
+{
+ if (lock == 1)
+ snd_hdac_power_down_pm(codec);
+}
+
+#define get_verb(reg) (((reg) >> 8) & 0xfff)
+
+static bool hda_volatile_reg(struct device *dev, unsigned int reg)
+{
+ struct hdac_device *codec = dev_to_hdac_dev(dev);
+ unsigned int verb = get_verb(reg);
+
+ switch (verb) {
+ case AC_VERB_GET_PROC_COEF:
+ return !codec->cache_coef;
+ case AC_VERB_GET_COEF_INDEX:
+ case AC_VERB_GET_PROC_STATE:
+ case AC_VERB_GET_POWER_STATE:
+ case AC_VERB_GET_PIN_SENSE:
+ case AC_VERB_GET_HDMI_DIP_SIZE:
+ case AC_VERB_GET_HDMI_ELDD:
+ case AC_VERB_GET_HDMI_DIP_INDEX:
+ case AC_VERB_GET_HDMI_DIP_DATA:
+ case AC_VERB_GET_HDMI_DIP_XMIT:
+ case AC_VERB_GET_HDMI_CP_CTRL:
+ case AC_VERB_GET_HDMI_CHAN_SLOT:
+ case AC_VERB_GET_DEVICE_SEL:
+ case AC_VERB_GET_DEVICE_LIST: /* read-only volatile */
+ return true;
+ }
+
+ return false;
+}
+
+static bool hda_writeable_reg(struct device *dev, unsigned int reg)
+{
+ struct hdac_device *codec = dev_to_hdac_dev(dev);
+ unsigned int verb = get_verb(reg);
+ const unsigned int *v;
+ int i;
+
+ snd_array_for_each(&codec->vendor_verbs, i, v) {
+ if (verb == *v)
+ return true;
+ }
+
+ if (codec->caps_overwriting)
+ return true;
+
+ switch (verb & 0xf00) {
+ case AC_VERB_GET_STREAM_FORMAT:
+ case AC_VERB_GET_AMP_GAIN_MUTE:
+ return true;
+ case AC_VERB_GET_PROC_COEF:
+ return codec->cache_coef;
+ case 0xf00:
+ break;
+ default:
+ return false;
+ }
+
+ switch (verb) {
+ case AC_VERB_GET_CONNECT_SEL:
+ case AC_VERB_GET_SDI_SELECT:
+ case AC_VERB_GET_PIN_WIDGET_CONTROL:
+ case AC_VERB_GET_UNSOLICITED_RESPONSE: /* only as SET_UNSOLICITED_ENABLE */
+ case AC_VERB_GET_BEEP_CONTROL:
+ case AC_VERB_GET_EAPD_BTLENABLE:
+ case AC_VERB_GET_DIGI_CONVERT_1:
+ case AC_VERB_GET_DIGI_CONVERT_2: /* only for beep control */
+ case AC_VERB_GET_VOLUME_KNOB_CONTROL:
+ case AC_VERB_GET_GPIO_MASK:
+ case AC_VERB_GET_GPIO_DIRECTION:
+ case AC_VERB_GET_GPIO_DATA: /* not for volatile read */
+ case AC_VERB_GET_GPIO_WAKE_MASK:
+ case AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK:
+ case AC_VERB_GET_GPIO_STICKY_MASK:
+ return true;
+ }
+
+ return false;
+}
+
+static bool hda_readable_reg(struct device *dev, unsigned int reg)
+{
+ struct hdac_device *codec = dev_to_hdac_dev(dev);
+ unsigned int verb = get_verb(reg);
+
+ if (codec->caps_overwriting)
+ return true;
+
+ switch (verb) {
+ case AC_VERB_PARAMETERS:
+ case AC_VERB_GET_CONNECT_LIST:
+ case AC_VERB_GET_SUBSYSTEM_ID:
+ return true;
+ /* below are basically writable, but disabled for reducing unnecessary
+ * writes at sync
+ */
+ case AC_VERB_GET_CONFIG_DEFAULT: /* usually just read */
+ case AC_VERB_GET_CONV: /* managed in PCM code */
+ case AC_VERB_GET_CVT_CHAN_COUNT: /* managed in HDMI CA code */
+ return true;
+ }
+
+ return hda_writeable_reg(dev, reg);
+}
+
+/*
+ * Stereo amp pseudo register:
+ * for making easier to handle the stereo volume control, we provide a
+ * fake register to deal both left and right channels by a single
+ * (pseudo) register access. A verb consisting of SET_AMP_GAIN with
+ * *both* SET_LEFT and SET_RIGHT bits takes a 16bit value, the lower 8bit
+ * for the left and the upper 8bit for the right channel.
+ */
+static bool is_stereo_amp_verb(unsigned int reg)
+{
+ if (((reg >> 8) & 0x700) != AC_VERB_SET_AMP_GAIN_MUTE)
+ return false;
+ return (reg & (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT)) ==
+ (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT);
+}
+
+/* read a pseudo stereo amp register (16bit left+right) */
+static int hda_reg_read_stereo_amp(struct hdac_device *codec,
+ unsigned int reg, unsigned int *val)
+{
+ unsigned int left, right;
+ int err;
+
+ reg &= ~(AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT);
+ err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_LEFT, 0, &left);
+ if (err < 0)
+ return err;
+ err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_RIGHT, 0, &right);
+ if (err < 0)
+ return err;
+ *val = left | (right << 8);
+ return 0;
+}
+
+/* write a pseudo stereo amp register (16bit left+right) */
+static int hda_reg_write_stereo_amp(struct hdac_device *codec,
+ unsigned int reg, unsigned int val)
+{
+ int err;
+ unsigned int verb, left, right;
+
+ verb = AC_VERB_SET_AMP_GAIN_MUTE << 8;
+ if (reg & AC_AMP_GET_OUTPUT)
+ verb |= AC_AMP_SET_OUTPUT;
+ else
+ verb |= AC_AMP_SET_INPUT | ((reg & 0xf) << 8);
+ reg = (reg & ~0xfffff) | verb;
+
+ left = val & 0xff;
+ right = (val >> 8) & 0xff;
+ if (left == right) {
+ reg |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT;
+ return snd_hdac_exec_verb(codec, reg | left, 0, NULL);
+ }
+
+ err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_LEFT | left, 0, NULL);
+ if (err < 0)
+ return err;
+ err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_RIGHT | right, 0, NULL);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/* read a pseudo coef register (16bit) */
+static int hda_reg_read_coef(struct hdac_device *codec, unsigned int reg,
+ unsigned int *val)
+{
+ unsigned int verb;
+ int err;
+
+ if (!codec->cache_coef)
+ return -EINVAL;
+ /* LSB 8bit = coef index */
+ verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8);
+ err = snd_hdac_exec_verb(codec, verb, 0, NULL);
+ if (err < 0)
+ return err;
+ verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8);
+ return snd_hdac_exec_verb(codec, verb, 0, val);
+}
+
+/* write a pseudo coef register (16bit) */
+static int hda_reg_write_coef(struct hdac_device *codec, unsigned int reg,
+ unsigned int val)
+{
+ unsigned int verb;
+ int err;
+
+ if (!codec->cache_coef)
+ return -EINVAL;
+ /* LSB 8bit = coef index */
+ verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8);
+ err = snd_hdac_exec_verb(codec, verb, 0, NULL);
+ if (err < 0)
+ return err;
+ verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8) |
+ (val & 0xffff);
+ return snd_hdac_exec_verb(codec, verb, 0, NULL);
+}
+
+static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct hdac_device *codec = context;
+ int verb = get_verb(reg);
+ int err;
+ int pm_lock = 0;
+
+ if (verb != AC_VERB_GET_POWER_STATE) {
+ pm_lock = codec_pm_lock(codec);
+ if (pm_lock < 0)
+ return -EAGAIN;
+ }
+ reg |= (codec->addr << 28);
+ if (is_stereo_amp_verb(reg)) {
+ err = hda_reg_read_stereo_amp(codec, reg, val);
+ goto out;
+ }
+ if (verb == AC_VERB_GET_PROC_COEF) {
+ err = hda_reg_read_coef(codec, reg, val);
+ goto out;
+ }
+ if ((verb & 0x700) == AC_VERB_SET_AMP_GAIN_MUTE)
+ reg &= ~AC_AMP_FAKE_MUTE;
+
+ err = snd_hdac_exec_verb(codec, reg, 0, val);
+ if (err < 0)
+ goto out;
+ /* special handling for asymmetric reads */
+ if (verb == AC_VERB_GET_POWER_STATE) {
+ if (*val & AC_PWRST_ERROR)
+ *val = -1;
+ else /* take only the actual state */
+ *val = (*val >> 4) & 0x0f;
+ }
+ out:
+ codec_pm_unlock(codec, pm_lock);
+ return err;
+}
+
+static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct hdac_device *codec = context;
+ unsigned int verb;
+ int i, bytes, err;
+ int pm_lock = 0;
+
+ if (codec->caps_overwriting)
+ return 0;
+
+ reg &= ~0x00080000U; /* drop GET bit */
+ reg |= (codec->addr << 28);
+ verb = get_verb(reg);
+
+ if (verb != AC_VERB_SET_POWER_STATE) {
+ pm_lock = codec_pm_lock(codec);
+ if (pm_lock < 0)
+ return codec->lazy_cache ? 0 : -EAGAIN;
+ }
+
+ if (is_stereo_amp_verb(reg)) {
+ err = hda_reg_write_stereo_amp(codec, reg, val);
+ goto out;
+ }
+
+ if (verb == AC_VERB_SET_PROC_COEF) {
+ err = hda_reg_write_coef(codec, reg, val);
+ goto out;
+ }
+
+ switch (verb & 0xf00) {
+ case AC_VERB_SET_AMP_GAIN_MUTE:
+ if ((reg & AC_AMP_FAKE_MUTE) && (val & AC_AMP_MUTE))
+ val = 0;
+ verb = AC_VERB_SET_AMP_GAIN_MUTE;
+ if (reg & AC_AMP_GET_LEFT)
+ verb |= AC_AMP_SET_LEFT >> 8;
+ else
+ verb |= AC_AMP_SET_RIGHT >> 8;
+ if (reg & AC_AMP_GET_OUTPUT) {
+ verb |= AC_AMP_SET_OUTPUT >> 8;
+ } else {
+ verb |= AC_AMP_SET_INPUT >> 8;
+ verb |= reg & 0xf;
+ }
+ break;
+ }
+
+ switch (verb) {
+ case AC_VERB_SET_DIGI_CONVERT_1:
+ bytes = 2;
+ break;
+ case AC_VERB_SET_CONFIG_DEFAULT_BYTES_0:
+ bytes = 4;
+ break;
+ default:
+ bytes = 1;
+ break;
+ }
+
+ for (i = 0; i < bytes; i++) {
+ reg &= ~0xfffff;
+ reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff);
+ err = snd_hdac_exec_verb(codec, reg, 0, NULL);
+ if (err < 0)
+ goto out;
+ }
+
+ out:
+ codec_pm_unlock(codec, pm_lock);
+ return err;
+}
+
+static const struct regmap_config hda_regmap_cfg = {
+ .name = "hdaudio",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .max_register = 0xfffffff,
+ .writeable_reg = hda_writeable_reg,
+ .readable_reg = hda_readable_reg,
+ .volatile_reg = hda_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_read = hda_reg_read,
+ .reg_write = hda_reg_write,
+ .use_single_read = true,
+ .use_single_write = true,
+ .disable_locking = true,
+};
+
+/**
+ * snd_hdac_regmap_init - Initialize regmap for HDA register accesses
+ * @codec: the codec object
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_regmap_init(struct hdac_device *codec)
+{
+ struct regmap *regmap;
+
+ regmap = regmap_init(&codec->dev, NULL, codec, &hda_regmap_cfg);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+ codec->regmap = regmap;
+ snd_array_init(&codec->vendor_verbs, sizeof(unsigned int), 8);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_init);
+
+/**
+ * snd_hdac_regmap_exit - Release the regmap from HDA codec
+ * @codec: the codec object
+ */
+void snd_hdac_regmap_exit(struct hdac_device *codec)
+{
+ if (codec->regmap) {
+ regmap_exit(codec->regmap);
+ codec->regmap = NULL;
+ snd_array_free(&codec->vendor_verbs);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_exit);
+
+/**
+ * snd_hdac_regmap_add_vendor_verb - add a vendor-specific verb to regmap
+ * @codec: the codec object
+ * @verb: verb to allow accessing via regmap
+ *
+ * Returns zero for success or a negative error code.
+ */
+int snd_hdac_regmap_add_vendor_verb(struct hdac_device *codec,
+ unsigned int verb)
+{
+ unsigned int *p = snd_array_new(&codec->vendor_verbs);
+
+ if (!p)
+ return -ENOMEM;
+ *p = verb | 0x800; /* set GET bit */
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_add_vendor_verb);
+
+/*
+ * helper functions
+ */
+
+/* write a pseudo-register value (w/o power sequence) */
+static int reg_raw_write(struct hdac_device *codec, unsigned int reg,
+ unsigned int val)
+{
+ guard(mutex)(&codec->regmap_lock);
+ if (!codec->regmap)
+ return hda_reg_write(codec, reg, val);
+ else
+ return regmap_write(codec->regmap, reg, val);
+}
+
+/* a helper macro to call @func_call; retry with power-up if failed */
+#define CALL_RAW_FUNC(codec, func_call) \
+ ({ \
+ int _err = func_call; \
+ if (_err == -EAGAIN) { \
+ _err = snd_hdac_power_up_pm(codec); \
+ if (_err >= 0) \
+ _err = func_call; \
+ snd_hdac_power_down_pm(codec); \
+ } \
+ _err;})
+
+/**
+ * snd_hdac_regmap_write_raw - write a pseudo register with power mgmt
+ * @codec: the codec object
+ * @reg: pseudo register
+ * @val: value to write
+ *
+ * Returns zero if successful or a negative error code.
+ */
+int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
+ unsigned int val)
+{
+ return CALL_RAW_FUNC(codec, reg_raw_write(codec, reg, val));
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw);
+
+static int reg_raw_read(struct hdac_device *codec, unsigned int reg,
+ unsigned int *val, bool uncached)
+{
+ guard(mutex)(&codec->regmap_lock);
+ if (uncached || !codec->regmap)
+ return hda_reg_read(codec, reg, val);
+ else
+ return regmap_read(codec->regmap, reg, val);
+}
+
+static int __snd_hdac_regmap_read_raw(struct hdac_device *codec,
+ unsigned int reg, unsigned int *val,
+ bool uncached)
+{
+ return CALL_RAW_FUNC(codec, reg_raw_read(codec, reg, val, uncached));
+}
+
+/**
+ * snd_hdac_regmap_read_raw - read a pseudo register with power mgmt
+ * @codec: the codec object
+ * @reg: pseudo register
+ * @val: pointer to store the read value
+ *
+ * Returns zero if successful or a negative error code.
+ */
+int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg,
+ unsigned int *val)
+{
+ return __snd_hdac_regmap_read_raw(codec, reg, val, false);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_read_raw);
+
+/* Works like snd_hdac_regmap_read_raw(), but this doesn't read from the
+ * cache but always via hda verbs.
+ */
+int snd_hdac_regmap_read_raw_uncached(struct hdac_device *codec,
+ unsigned int reg, unsigned int *val)
+{
+ return __snd_hdac_regmap_read_raw(codec, reg, val, true);
+}
+
+static int reg_raw_update(struct hdac_device *codec, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
+ unsigned int orig;
+ bool change;
+ int err;
+
+ guard(mutex)(&codec->regmap_lock);
+ if (codec->regmap) {
+ err = regmap_update_bits_check(codec->regmap, reg, mask, val,
+ &change);
+ if (!err)
+ err = change ? 1 : 0;
+ } else {
+ err = hda_reg_read(codec, reg, &orig);
+ if (!err) {
+ val &= mask;
+ val |= orig & ~mask;
+ if (val != orig) {
+ err = hda_reg_write(codec, reg, val);
+ if (!err)
+ err = 1;
+ }
+ }
+ }
+ return err;
+}
+
+/**
+ * snd_hdac_regmap_update_raw - update a pseudo register with power mgmt
+ * @codec: the codec object
+ * @reg: pseudo register
+ * @mask: bit mask to update
+ * @val: value to update
+ *
+ * Returns zero if successful or a negative error code.
+ */
+int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
+ return CALL_RAW_FUNC(codec, reg_raw_update(codec, reg, mask, val));
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw);
+
+static int reg_raw_update_once(struct hdac_device *codec, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
+ if (!codec->regmap)
+ return reg_raw_update(codec, reg, mask, val);
+
+ guard(mutex)(&codec->regmap_lock);
+ /* Discard any updates to already initialised registers. */
+ if (!regcache_reg_cached(codec->regmap, reg))
+ return regmap_update_bits(codec->regmap, reg, mask, val);
+ return 0;
+}
+
+/**
+ * snd_hdac_regmap_update_raw_once - initialize the register value only once
+ * @codec: the codec object
+ * @reg: pseudo register
+ * @mask: bit mask to update
+ * @val: value to update
+ *
+ * Performs the update of the register bits only once when the register
+ * hasn't been initialized yet. Used in HD-audio legacy driver.
+ * Returns zero if successful or a negative error code
+ */
+int snd_hdac_regmap_update_raw_once(struct hdac_device *codec, unsigned int reg,
+ unsigned int mask, unsigned int val)
+{
+ return CALL_RAW_FUNC(codec, reg_raw_update_once(codec, reg, mask, val));
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw_once);
+
+/**
+ * snd_hdac_regmap_sync - sync out the cached values for PM resume
+ * @codec: the codec object
+ */
+void snd_hdac_regmap_sync(struct hdac_device *codec)
+{
+ guard(mutex)(&codec->regmap_lock);
+ if (codec->regmap)
+ regcache_sync(codec->regmap);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_regmap_sync);
diff --git a/sound/hda/core/stream.c b/sound/hda/core/stream.c
new file mode 100644
index 000000000000..579ec544ef4a
--- /dev/null
+++ b/sound/hda/core/stream.c
@@ -0,0 +1,1004 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HD-audio stream operations
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/clocksource.h>
+#include <sound/compress_driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_register.h>
+#include "trace.h"
+
+/*
+ * the hdac_stream library is intended to be used with the following
+ * transitions. The states are not formally defined in the code but loosely
+ * inspired by boolean variables. Note that the 'prepared' field is not used
+ * in this library but by the callers during the hw_params/prepare transitions
+ *
+ * |
+ * stream_init() |
+ * v
+ * +--+-------+
+ * | unused |
+ * +--+----+--+
+ * | ^
+ * stream_assign() | | stream_release()
+ * v |
+ * +--+----+--+
+ * | opened |
+ * +--+----+--+
+ * | ^
+ * stream_reset() | |
+ * stream_setup() | | stream_cleanup()
+ * v |
+ * +--+----+--+
+ * | prepared |
+ * +--+----+--+
+ * | ^
+ * stream_start() | | stream_stop()
+ * v |
+ * +--+----+--+
+ * | running |
+ * +----------+
+ */
+
+/**
+ * snd_hdac_get_stream_stripe_ctl - get stripe control value
+ * @bus: HD-audio core bus
+ * @substream: PCM substream
+ */
+int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int channels = runtime->channels,
+ rate = runtime->rate,
+ bits_per_sample = runtime->sample_bits,
+ max_sdo_lines, value, sdo_line;
+
+ /* T_AZA_GCAP_NSDO is 1:2 bitfields in GCAP */
+ max_sdo_lines = snd_hdac_chip_readl(bus, GCAP) & AZX_GCAP_NSDO;
+
+ /* following is from HD audio spec */
+ for (sdo_line = max_sdo_lines; sdo_line > 0; sdo_line >>= 1) {
+ if (rate > 48000)
+ value = (channels * bits_per_sample *
+ (rate / 48000)) / sdo_line;
+ else
+ value = (channels * bits_per_sample) / sdo_line;
+
+ if (value >= bus->sdo_limit)
+ break;
+ }
+
+ /* stripe value: 0 for 1SDO, 1 for 2SDO, 2 for 4SDO lines */
+ return sdo_line >> 1;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_stream_stripe_ctl);
+
+/**
+ * snd_hdac_stream_init - initialize each stream (aka device)
+ * @bus: HD-audio core bus
+ * @azx_dev: HD-audio core stream object to initialize
+ * @idx: stream index number
+ * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE)
+ * @tag: the tag id to assign
+ *
+ * Assign the starting bdl address to each stream (device) and initialize.
+ */
+void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev,
+ int idx, int direction, int tag)
+{
+ azx_dev->bus = bus;
+ /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
+ azx_dev->sd_addr = bus->remap_addr + (0x20 * idx + 0x80);
+ /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
+ azx_dev->sd_int_sta_mask = 1 << idx;
+ azx_dev->index = idx;
+ azx_dev->direction = direction;
+ azx_dev->stream_tag = tag;
+ snd_hdac_dsp_lock_init(azx_dev);
+ list_add_tail(&azx_dev->list, &bus->stream_list);
+
+ if (bus->spbcap) {
+ azx_dev->spib_addr = bus->spbcap + AZX_SPB_BASE +
+ AZX_SPB_INTERVAL * idx +
+ AZX_SPB_SPIB;
+
+ azx_dev->fifo_addr = bus->spbcap + AZX_SPB_BASE +
+ AZX_SPB_INTERVAL * idx +
+ AZX_SPB_MAXFIFO;
+ }
+
+ if (bus->drsmcap)
+ azx_dev->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE +
+ AZX_DRSM_INTERVAL * idx;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_init);
+
+/**
+ * snd_hdac_stream_start - start a stream
+ * @azx_dev: HD-audio core stream to start
+ *
+ * Start a stream, set start_wallclk and set the running flag.
+ */
+void snd_hdac_stream_start(struct hdac_stream *azx_dev)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ int stripe_ctl;
+
+ trace_snd_hdac_stream_start(bus, azx_dev);
+
+ azx_dev->start_wallclk = snd_hdac_chip_readl(bus, WALLCLK);
+
+ /* enable SIE */
+ snd_hdac_chip_updatel(bus, INTCTL,
+ 1 << azx_dev->index,
+ 1 << azx_dev->index);
+ /* set stripe control */
+ if (azx_dev->stripe) {
+ if (azx_dev->substream)
+ stripe_ctl = snd_hdac_get_stream_stripe_ctl(bus, azx_dev->substream);
+ else
+ stripe_ctl = 0;
+ snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK,
+ stripe_ctl);
+ }
+ /* set DMA start and interrupt mask */
+ if (bus->access_sdnctl_in_dword)
+ snd_hdac_stream_updatel(azx_dev, SD_CTL,
+ 0, SD_CTL_DMA_START | SD_INT_MASK);
+ else
+ snd_hdac_stream_updateb(azx_dev, SD_CTL,
+ 0, SD_CTL_DMA_START | SD_INT_MASK);
+ azx_dev->running = true;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_start);
+
+/**
+ * snd_hdac_stream_clear - helper to clear stream registers and stop DMA transfers
+ * @azx_dev: HD-audio core stream to stop
+ */
+static void snd_hdac_stream_clear(struct hdac_stream *azx_dev)
+{
+ snd_hdac_stream_updateb(azx_dev, SD_CTL,
+ SD_CTL_DMA_START | SD_INT_MASK, 0);
+ snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
+ if (azx_dev->stripe)
+ snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0);
+ azx_dev->running = false;
+}
+
+/**
+ * snd_hdac_stream_stop - stop a stream
+ * @azx_dev: HD-audio core stream to stop
+ *
+ * Stop a stream DMA and disable stream interrupt
+ */
+void snd_hdac_stream_stop(struct hdac_stream *azx_dev)
+{
+ trace_snd_hdac_stream_stop(azx_dev->bus, azx_dev);
+
+ snd_hdac_stream_clear(azx_dev);
+ /* disable SIE */
+ snd_hdac_chip_updatel(azx_dev->bus, INTCTL, 1 << azx_dev->index, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_stop);
+
+/**
+ * snd_hdac_stop_streams - stop all streams
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_stop_streams(struct hdac_bus *bus)
+{
+ struct hdac_stream *stream;
+
+ list_for_each_entry(stream, &bus->stream_list, list)
+ snd_hdac_stream_stop(stream);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stop_streams);
+
+/**
+ * snd_hdac_stop_streams_and_chip - stop all streams and chip if running
+ * @bus: HD-audio core bus
+ */
+void snd_hdac_stop_streams_and_chip(struct hdac_bus *bus)
+{
+
+ if (bus->chip_init) {
+ snd_hdac_stop_streams(bus);
+ snd_hdac_bus_stop_chip(bus);
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stop_streams_and_chip);
+
+/**
+ * snd_hdac_stream_reset - reset a stream
+ * @azx_dev: HD-audio core stream to reset
+ */
+void snd_hdac_stream_reset(struct hdac_stream *azx_dev)
+{
+ unsigned char val;
+ int dma_run_state;
+
+ snd_hdac_stream_clear(azx_dev);
+
+ dma_run_state = snd_hdac_stream_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START;
+
+ snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET);
+
+ /* wait for hardware to report that the stream entered reset */
+ snd_hdac_stream_readb_poll(azx_dev, SD_CTL, val, (val & SD_CTL_STREAM_RESET), 3, 300);
+
+ if (azx_dev->bus->dma_stop_delay && dma_run_state)
+ udelay(azx_dev->bus->dma_stop_delay);
+
+ snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_STREAM_RESET, 0);
+
+ /* wait for hardware to report that the stream is out of reset */
+ snd_hdac_stream_readb_poll(azx_dev, SD_CTL, val, !(val & SD_CTL_STREAM_RESET), 3, 300);
+
+ /* reset first position - may not be synced with hw at this time */
+ if (azx_dev->posbuf)
+ *azx_dev->posbuf = 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_reset);
+
+/**
+ * snd_hdac_stream_setup - set up the SD for streaming
+ * @azx_dev: HD-audio core stream to set up
+ * @code_loading: Whether the stream is for PCM or code-loading.
+ */
+int snd_hdac_stream_setup(struct hdac_stream *azx_dev, bool code_loading)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ struct snd_pcm_runtime *runtime;
+ unsigned int val;
+ u16 reg;
+ int ret;
+
+ if (azx_dev->substream)
+ runtime = azx_dev->substream->runtime;
+ else
+ runtime = NULL;
+ /* make sure the run bit is zero for SD */
+ snd_hdac_stream_clear(azx_dev);
+ /* program the stream_tag */
+ val = snd_hdac_stream_readl(azx_dev, SD_CTL);
+ val = (val & ~SD_CTL_STREAM_TAG_MASK) |
+ (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT);
+ if (!bus->snoop)
+ val |= SD_CTL_TRAFFIC_PRIO;
+ snd_hdac_stream_writel(azx_dev, SD_CTL, val);
+
+ /* program the length of samples in cyclic buffer */
+ snd_hdac_stream_writel(azx_dev, SD_CBL, azx_dev->bufsize);
+
+ /* program the stream format */
+ /* this value needs to be the same as the one programmed */
+ snd_hdac_stream_writew(azx_dev, SD_FORMAT, azx_dev->format_val);
+
+ /* program the stream LVI (last valid index) of the BDL */
+ snd_hdac_stream_writew(azx_dev, SD_LVI, azx_dev->frags - 1);
+
+ /* program the BDL address */
+ /* lower BDL address */
+ snd_hdac_stream_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr);
+ /* upper BDL address */
+ snd_hdac_stream_writel(azx_dev, SD_BDLPU,
+ upper_32_bits(azx_dev->bdl.addr));
+
+ /* enable the position buffer */
+ if (bus->use_posbuf && bus->posbuf.addr) {
+ if (!(snd_hdac_chip_readl(bus, DPLBASE) & AZX_DPLBASE_ENABLE))
+ snd_hdac_chip_writel(bus, DPLBASE,
+ (u32)bus->posbuf.addr | AZX_DPLBASE_ENABLE);
+ }
+
+ /* set the interrupt enable bits in the descriptor control register */
+ snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_INT_MASK);
+
+ if (!code_loading) {
+ /* Once SDxFMT is set, the controller programs SDxFIFOS to non-zero value. */
+ ret = snd_hdac_stream_readw_poll(azx_dev, SD_FIFOSIZE, reg,
+ reg & AZX_SD_FIFOSIZE_MASK, 3, 300);
+ if (ret)
+ dev_dbg(bus->dev, "polling SD_FIFOSIZE 0x%04x failed: %d\n",
+ AZX_REG_SD_FIFOSIZE, ret);
+ azx_dev->fifo_size = reg;
+ }
+
+ /* when LPIB delay correction gives a small negative value,
+ * we ignore it; currently set the threshold statically to
+ * 64 frames
+ */
+ if (runtime && runtime->period_size > 64)
+ azx_dev->delay_negative_threshold =
+ -frames_to_bytes(runtime, 64);
+ else
+ azx_dev->delay_negative_threshold = 0;
+
+ /* wallclk has 24Mhz clock source */
+ if (runtime)
+ azx_dev->period_wallclk = (((runtime->period_size * 24000) /
+ runtime->rate) * 1000);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_setup);
+
+/**
+ * snd_hdac_stream_cleanup - cleanup a stream
+ * @azx_dev: HD-audio core stream to clean up
+ */
+void snd_hdac_stream_cleanup(struct hdac_stream *azx_dev)
+{
+ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
+ snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
+ snd_hdac_stream_writel(azx_dev, SD_CTL, 0);
+ azx_dev->bufsize = 0;
+ azx_dev->period_bytes = 0;
+ azx_dev->format_val = 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_cleanup);
+
+/**
+ * snd_hdac_stream_assign - assign a stream for the PCM
+ * @bus: HD-audio core bus
+ * @substream: PCM substream to assign
+ *
+ * Look for an unused stream for the given PCM substream, assign it
+ * and return the stream object. If no stream is free, returns NULL.
+ * The function tries to keep using the same stream object when it's used
+ * beforehand. Also, when bus->reverse_assign flag is set, the last free
+ * or matching entry is returned. This is needed for some strange codecs.
+ */
+struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_stream *azx_dev;
+ struct hdac_stream *res = NULL;
+
+ /* make a non-zero unique key for the substream */
+ int key = (substream->number << 2) | (substream->stream + 1);
+
+ if (substream->pcm)
+ key |= (substream->pcm->device << 16);
+
+ guard(spinlock_irq)(&bus->reg_lock);
+ list_for_each_entry(azx_dev, &bus->stream_list, list) {
+ if (azx_dev->direction != substream->stream)
+ continue;
+ if (azx_dev->opened)
+ continue;
+ if (azx_dev->assigned_key == key) {
+ res = azx_dev;
+ break;
+ }
+ if (!res || bus->reverse_assign)
+ res = azx_dev;
+ }
+ if (res) {
+ res->opened = 1;
+ res->running = 0;
+ res->assigned_key = key;
+ res->substream = substream;
+ }
+ return res;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_assign);
+
+/**
+ * snd_hdac_stream_release_locked - release the assigned stream
+ * @azx_dev: HD-audio core stream to release
+ *
+ * Release the stream that has been assigned by snd_hdac_stream_assign().
+ * The bus->reg_lock needs to be taken at a higher level
+ */
+void snd_hdac_stream_release_locked(struct hdac_stream *azx_dev)
+{
+ azx_dev->opened = 0;
+ azx_dev->running = 0;
+ azx_dev->substream = NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_release_locked);
+
+/**
+ * snd_hdac_stream_release - release the assigned stream
+ * @azx_dev: HD-audio core stream to release
+ *
+ * Release the stream that has been assigned by snd_hdac_stream_assign().
+ */
+void snd_hdac_stream_release(struct hdac_stream *azx_dev)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+
+ guard(spinlock_irq)(&bus->reg_lock);
+ snd_hdac_stream_release_locked(azx_dev);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_release);
+
+/**
+ * snd_hdac_get_stream - return hdac_stream based on stream_tag and
+ * direction
+ *
+ * @bus: HD-audio core bus
+ * @dir: direction for the stream to be found
+ * @stream_tag: stream tag for stream to be found
+ */
+struct hdac_stream *snd_hdac_get_stream(struct hdac_bus *bus,
+ int dir, int stream_tag)
+{
+ struct hdac_stream *s;
+
+ list_for_each_entry(s, &bus->stream_list, list) {
+ if (s->direction == dir && s->stream_tag == stream_tag)
+ return s;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_stream);
+
+/*
+ * set up a BDL entry
+ */
+static int setup_bdle(struct hdac_bus *bus,
+ struct snd_dma_buffer *dmab,
+ struct hdac_stream *azx_dev, __le32 **bdlp,
+ int ofs, int size, int with_ioc)
+{
+ __le32 *bdl = *bdlp;
+
+ while (size > 0) {
+ dma_addr_t addr;
+ int chunk;
+
+ if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
+ return -EINVAL;
+
+ addr = snd_sgbuf_get_addr(dmab, ofs);
+ /* program the address field of the BDL entry */
+ bdl[0] = cpu_to_le32((u32)addr);
+ bdl[1] = cpu_to_le32(upper_32_bits(addr));
+ /* program the size field of the BDL entry */
+ chunk = snd_sgbuf_get_chunk_size(dmab, ofs, size);
+ /* one BDLE cannot cross 4K boundary on CTHDA chips */
+ if (bus->align_bdle_4k) {
+ u32 remain = 0x1000 - (ofs & 0xfff);
+
+ if (chunk > remain)
+ chunk = remain;
+ }
+ bdl[2] = cpu_to_le32(chunk);
+ /* program the IOC to enable interrupt
+ * only when the whole fragment is processed
+ */
+ size -= chunk;
+ bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01);
+ bdl += 4;
+ azx_dev->frags++;
+ ofs += chunk;
+ }
+ *bdlp = bdl;
+ return ofs;
+}
+
+/**
+ * snd_hdac_stream_setup_bdle - set up BDL entries
+ * @azx_dev: HD-audio core stream to set up
+ * @dmab: allocated DMA buffer
+ * @runtime: substream runtime, optional
+ *
+ * Set up the buffer descriptor table of the given stream based on the
+ * period and buffer sizes of the assigned PCM substream.
+ */
+static int snd_hdac_stream_setup_bdle(struct hdac_stream *azx_dev, struct snd_dma_buffer *dmab,
+ struct snd_pcm_runtime *runtime)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ int i, ofs, periods, period_bytes;
+ int pos_adj, pos_align;
+ __le32 *bdl;
+
+ /* reset BDL address */
+ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
+ snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
+
+ period_bytes = azx_dev->period_bytes;
+ periods = azx_dev->bufsize / period_bytes;
+
+ /* program the initial BDL entries */
+ bdl = (__le32 *)azx_dev->bdl.area;
+ ofs = 0;
+ azx_dev->frags = 0;
+
+ pos_adj = bus->bdl_pos_adj;
+ if (runtime && !azx_dev->no_period_wakeup && pos_adj > 0) {
+ pos_align = pos_adj;
+ pos_adj = DIV_ROUND_UP(pos_adj * runtime->rate, 48000);
+ if (!pos_adj)
+ pos_adj = pos_align;
+ else
+ pos_adj = roundup(pos_adj, pos_align);
+ pos_adj = frames_to_bytes(runtime, pos_adj);
+ if (pos_adj >= period_bytes) {
+ dev_warn(bus->dev, "Too big adjustment %d\n",
+ pos_adj);
+ pos_adj = 0;
+ } else {
+ ofs = setup_bdle(bus, dmab, azx_dev,
+ &bdl, ofs, pos_adj, true);
+ if (ofs < 0)
+ goto error;
+ }
+ } else
+ pos_adj = 0;
+
+ for (i = 0; i < periods; i++) {
+ if (i == periods - 1 && pos_adj)
+ ofs = setup_bdle(bus, dmab, azx_dev,
+ &bdl, ofs, period_bytes - pos_adj, 0);
+ else
+ ofs = setup_bdle(bus, dmab, azx_dev,
+ &bdl, ofs, period_bytes,
+ !azx_dev->no_period_wakeup);
+ if (ofs < 0)
+ goto error;
+ }
+ return 0;
+
+ error:
+ dev_dbg(bus->dev, "Too many BDL entries: buffer=%d, period=%d\n",
+ azx_dev->bufsize, period_bytes);
+ return -EINVAL;
+}
+
+/**
+ * snd_hdac_stream_setup_periods - set up BDL entries
+ * @azx_dev: HD-audio core stream to set up
+ *
+ * Set up the buffer descriptor table of the given stream based on the
+ * period and buffer sizes of the assigned PCM substream.
+ */
+int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
+{
+ struct snd_pcm_substream *substream = azx_dev->substream;
+ struct snd_compr_stream *cstream = azx_dev->cstream;
+ struct snd_pcm_runtime *runtime = NULL;
+ struct snd_dma_buffer *dmab;
+
+ if (substream) {
+ runtime = substream->runtime;
+ dmab = snd_pcm_get_dma_buf(substream);
+ } else if (cstream) {
+ dmab = snd_pcm_get_dma_buf(cstream);
+ } else {
+ WARN(1, "No substream or cstream assigned\n");
+ return -EINVAL;
+ }
+
+ return snd_hdac_stream_setup_bdle(azx_dev, dmab, runtime);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods);
+
+/**
+ * snd_hdac_stream_set_params - set stream parameters
+ * @azx_dev: HD-audio core stream for which parameters are to be set
+ * @format_val: format value parameter
+ *
+ * Setup the HD-audio core stream parameters from substream of the stream
+ * and passed format value
+ */
+int snd_hdac_stream_set_params(struct hdac_stream *azx_dev,
+ unsigned int format_val)
+{
+ struct snd_pcm_substream *substream = azx_dev->substream;
+ struct snd_compr_stream *cstream = azx_dev->cstream;
+ unsigned int bufsize, period_bytes;
+ unsigned int no_period_wakeup;
+ int err;
+
+ if (substream) {
+ bufsize = snd_pcm_lib_buffer_bytes(substream);
+ period_bytes = snd_pcm_lib_period_bytes(substream);
+ no_period_wakeup = substream->runtime->no_period_wakeup;
+ } else if (cstream) {
+ bufsize = cstream->runtime->buffer_size;
+ period_bytes = cstream->runtime->fragment_size;
+ no_period_wakeup = 0;
+ } else {
+ return -EINVAL;
+ }
+
+ if (bufsize != azx_dev->bufsize ||
+ period_bytes != azx_dev->period_bytes ||
+ format_val != azx_dev->format_val ||
+ no_period_wakeup != azx_dev->no_period_wakeup) {
+ azx_dev->bufsize = bufsize;
+ azx_dev->period_bytes = period_bytes;
+ azx_dev->format_val = format_val;
+ azx_dev->no_period_wakeup = no_period_wakeup;
+ err = snd_hdac_stream_setup_periods(azx_dev);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_set_params);
+
+static u64 azx_cc_read(struct cyclecounter *cc)
+{
+ struct hdac_stream *azx_dev = container_of(cc, struct hdac_stream, cc);
+
+ return snd_hdac_chip_readl(azx_dev->bus, WALLCLK);
+}
+
+static void azx_timecounter_init(struct hdac_stream *azx_dev,
+ bool force, u64 last)
+{
+ struct timecounter *tc = &azx_dev->tc;
+ struct cyclecounter *cc = &azx_dev->cc;
+ u64 nsec;
+
+ cc->read = azx_cc_read;
+ cc->mask = CLOCKSOURCE_MASK(32);
+
+ /*
+ * Calculate the optimal mult/shift values. The counter wraps
+ * around after ~178.9 seconds.
+ */
+ clocks_calc_mult_shift(&cc->mult, &cc->shift, 24000000,
+ NSEC_PER_SEC, 178);
+
+ nsec = 0; /* audio time is elapsed time since trigger */
+ timecounter_init(tc, cc, nsec);
+ if (force) {
+ /*
+ * force timecounter to use predefined value,
+ * used for synchronized starts
+ */
+ tc->cycle_last = last;
+ }
+}
+
+/**
+ * snd_hdac_stream_timecounter_init - initialize time counter
+ * @azx_dev: HD-audio core stream (master stream)
+ * @streams: bit flags of streams to set up
+ * @start: true for PCM trigger start, false for other cases
+ *
+ * Initializes the time counter of streams marked by the bit flags (each
+ * bit corresponds to the stream index).
+ * The trigger timestamp of PCM substream assigned to the given stream is
+ * updated accordingly, too.
+ */
+void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev,
+ unsigned int streams, bool start)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ struct snd_pcm_runtime *runtime = azx_dev->substream->runtime;
+ struct hdac_stream *s;
+ bool inited = false;
+ u64 cycle_last = 0;
+
+ if (!start)
+ goto skip;
+
+ list_for_each_entry(s, &bus->stream_list, list) {
+ if ((streams & (1 << s->index))) {
+ azx_timecounter_init(s, inited, cycle_last);
+ if (!inited) {
+ inited = true;
+ cycle_last = s->tc.cycle_last;
+ }
+ }
+ }
+
+skip:
+ snd_pcm_gettime(runtime, &runtime->trigger_tstamp);
+ runtime->trigger_tstamp_latched = true;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_timecounter_init);
+
+/**
+ * snd_hdac_stream_sync_trigger - turn on/off stream sync register
+ * @azx_dev: HD-audio core stream (master stream)
+ * @set: true = set, false = clear
+ * @streams: bit flags of streams to sync
+ * @reg: the stream sync register address
+ */
+void snd_hdac_stream_sync_trigger(struct hdac_stream *azx_dev, bool set,
+ unsigned int streams, unsigned int reg)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ unsigned int val;
+
+ if (!reg)
+ reg = AZX_REG_SSYNC;
+ val = _snd_hdac_chip_readl(bus, reg);
+ if (set)
+ val |= streams;
+ else
+ val &= ~streams;
+ _snd_hdac_chip_writel(bus, reg, val);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_sync_trigger);
+
+/**
+ * snd_hdac_stream_sync - sync with start/stop trigger operation
+ * @azx_dev: HD-audio core stream (master stream)
+ * @start: true = start, false = stop
+ * @streams: bit flags of streams to sync
+ *
+ * For @start = true, wait until all FIFOs get ready.
+ * For @start = false, wait until all RUN bits are cleared.
+ */
+void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start,
+ unsigned int streams)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ int nwait, timeout;
+ struct hdac_stream *s;
+
+ for (timeout = 5000; timeout; timeout--) {
+ nwait = 0;
+ list_for_each_entry(s, &bus->stream_list, list) {
+ if (!(streams & (1 << s->index)))
+ continue;
+
+ if (start) {
+ /* check FIFO gets ready */
+ if (!(snd_hdac_stream_readb(s, SD_STS) &
+ SD_STS_FIFO_READY))
+ nwait++;
+ } else {
+ /* check RUN bit is cleared */
+ if (snd_hdac_stream_readb(s, SD_CTL) &
+ SD_CTL_DMA_START) {
+ nwait++;
+ /*
+ * Perform stream reset if DMA RUN
+ * bit not cleared within given timeout
+ */
+ if (timeout == 1)
+ snd_hdac_stream_reset(s);
+ }
+ }
+ }
+ if (!nwait)
+ break;
+ cpu_relax();
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_sync);
+
+/**
+ * snd_hdac_stream_spbcap_enable - enable SPIB for a stream
+ * @bus: HD-audio core bus
+ * @enable: flag to enable/disable SPIB
+ * @index: stream index for which SPIB need to be enabled
+ */
+void snd_hdac_stream_spbcap_enable(struct hdac_bus *bus,
+ bool enable, int index)
+{
+ u32 mask = 0;
+
+ if (!bus->spbcap) {
+ dev_err(bus->dev, "Address of SPB capability is NULL\n");
+ return;
+ }
+
+ mask |= (1 << index);
+
+ if (enable)
+ snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, mask);
+ else
+ snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_spbcap_enable);
+
+/**
+ * snd_hdac_stream_set_spib - sets the spib value of a stream
+ * @bus: HD-audio core bus
+ * @azx_dev: hdac_stream
+ * @value: spib value to set
+ */
+int snd_hdac_stream_set_spib(struct hdac_bus *bus,
+ struct hdac_stream *azx_dev, u32 value)
+{
+ if (!bus->spbcap) {
+ dev_err(bus->dev, "Address of SPB capability is NULL\n");
+ return -EINVAL;
+ }
+
+ writel(value, azx_dev->spib_addr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_set_spib);
+
+/**
+ * snd_hdac_stream_drsm_enable - enable DMA resume for a stream
+ * @bus: HD-audio core bus
+ * @enable: flag to enable/disable DRSM
+ * @index: stream index for which DRSM need to be enabled
+ */
+void snd_hdac_stream_drsm_enable(struct hdac_bus *bus,
+ bool enable, int index)
+{
+ u32 mask = 0;
+
+ if (!bus->drsmcap) {
+ dev_err(bus->dev, "Address of DRSM capability is NULL\n");
+ return;
+ }
+
+ mask |= (1 << index);
+
+ if (enable)
+ snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, mask);
+ else
+ snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, 0);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_drsm_enable);
+
+/*
+ * snd_hdac_stream_wait_drsm - wait for HW to clear RSM for a stream
+ * @azx_dev: HD-audio core stream to await RSM for
+ *
+ * Returns 0 on success and -ETIMEDOUT upon a timeout.
+ */
+int snd_hdac_stream_wait_drsm(struct hdac_stream *azx_dev)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ u32 mask, reg;
+ int ret;
+
+ mask = 1 << azx_dev->index;
+
+ ret = read_poll_timeout(snd_hdac_reg_readl, reg, !(reg & mask), 250, 2000, false, bus,
+ bus->drsmcap + AZX_REG_DRSM_CTL);
+ if (ret)
+ dev_dbg(bus->dev, "polling RSM 0x%08x failed: %d\n", mask, ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_wait_drsm);
+
+/**
+ * snd_hdac_stream_set_dpibr - sets the dpibr value of a stream
+ * @bus: HD-audio core bus
+ * @azx_dev: hdac_stream
+ * @value: dpib value to set
+ */
+int snd_hdac_stream_set_dpibr(struct hdac_bus *bus,
+ struct hdac_stream *azx_dev, u32 value)
+{
+ if (!bus->drsmcap) {
+ dev_err(bus->dev, "Address of DRSM capability is NULL\n");
+ return -EINVAL;
+ }
+
+ writel(value, azx_dev->dpibr_addr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_set_dpibr);
+
+/**
+ * snd_hdac_stream_set_lpib - sets the lpib value of a stream
+ * @azx_dev: hdac_stream
+ * @value: lpib value to set
+ */
+int snd_hdac_stream_set_lpib(struct hdac_stream *azx_dev, u32 value)
+{
+ snd_hdac_stream_writel(azx_dev, SD_LPIB, value);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_stream_set_lpib);
+
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+/**
+ * snd_hdac_dsp_prepare - prepare for DSP loading
+ * @azx_dev: HD-audio core stream used for DSP loading
+ * @format: HD-audio stream format
+ * @byte_size: data chunk byte size
+ * @bufp: allocated buffer
+ *
+ * Allocate the buffer for the given size and set up the given stream for
+ * DSP loading. Returns the stream tag (>= 0), or a negative error code.
+ */
+int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format,
+ unsigned int byte_size, struct snd_dma_buffer *bufp)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+ int err;
+
+ guard(snd_hdac_dsp_lock)(azx_dev);
+ scoped_guard(spinlock_irq, &bus->reg_lock) {
+ if (azx_dev->running || azx_dev->locked)
+ return -EBUSY;
+ azx_dev->locked = true;
+ }
+
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev,
+ byte_size, bufp);
+ if (err < 0)
+ goto err_alloc;
+
+ azx_dev->substream = NULL;
+ azx_dev->bufsize = byte_size;
+ /* It is recommended to transfer the firmware in two or more chunks. */
+ azx_dev->period_bytes = byte_size / 2;
+ azx_dev->format_val = format;
+ azx_dev->no_period_wakeup = 1;
+
+ snd_hdac_stream_reset(azx_dev);
+
+ err = snd_hdac_stream_setup_bdle(azx_dev, bufp, NULL);
+ if (err < 0)
+ goto error;
+
+ snd_hdac_stream_setup(azx_dev, true);
+ return azx_dev->stream_tag;
+
+ error:
+ snd_dma_free_pages(bufp);
+ err_alloc:
+ scoped_guard(spinlock_irq, &bus->reg_lock) {
+ azx_dev->locked = false;
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_dsp_prepare);
+
+/**
+ * snd_hdac_dsp_trigger - start / stop DSP loading
+ * @azx_dev: HD-audio core stream used for DSP loading
+ * @start: trigger start or stop
+ */
+void snd_hdac_dsp_trigger(struct hdac_stream *azx_dev, bool start)
+{
+ if (start)
+ snd_hdac_stream_start(azx_dev);
+ else
+ snd_hdac_stream_stop(azx_dev);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_dsp_trigger);
+
+/**
+ * snd_hdac_dsp_cleanup - clean up the stream from DSP loading to normal
+ * @azx_dev: HD-audio core stream used for DSP loading
+ * @dmab: buffer used by DSP loading
+ */
+void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev,
+ struct snd_dma_buffer *dmab)
+{
+ struct hdac_bus *bus = azx_dev->bus;
+
+ if (!dmab->area || !azx_dev->locked)
+ return;
+
+ guard(snd_hdac_dsp_lock)(azx_dev);
+ /* reset BDL address */
+ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
+ snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
+ snd_hdac_stream_writel(azx_dev, SD_CTL, 0);
+ azx_dev->bufsize = 0;
+ azx_dev->period_bytes = 0;
+ azx_dev->format_val = 0;
+
+ snd_dma_free_pages(dmab);
+ dmab->area = NULL;
+
+ guard(spinlock_irq)(&bus->reg_lock);
+ azx_dev->locked = false;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_dsp_cleanup);
+#endif /* CONFIG_SND_HDA_DSP_LOADER */
diff --git a/sound/hda/core/sysfs.c b/sound/hda/core/sysfs.c
new file mode 100644
index 000000000000..bffe52859dba
--- /dev/null
+++ b/sound/hda/core/sysfs.c
@@ -0,0 +1,469 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * sysfs support for HD-audio core device
+ */
+
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/hdaudio.h>
+#include "local.h"
+
+struct hdac_widget_tree {
+ struct kobject *root;
+ struct kobject *afg;
+ struct kobject **nodes;
+};
+
+#define CODEC_ATTR(type) \
+static ssize_t type##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct hdac_device *codec = dev_to_hdac_dev(dev); \
+ return sysfs_emit(buf, "0x%x\n", codec->type); \
+} \
+static DEVICE_ATTR_RO(type)
+
+#define CODEC_ATTR_STR(type) \
+static ssize_t type##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct hdac_device *codec = dev_to_hdac_dev(dev); \
+ return sysfs_emit(buf, "%s\n", \
+ codec->type ? codec->type : ""); \
+} \
+static DEVICE_ATTR_RO(type)
+
+CODEC_ATTR(type);
+CODEC_ATTR(vendor_id);
+CODEC_ATTR(subsystem_id);
+CODEC_ATTR(revision_id);
+CODEC_ATTR(afg);
+CODEC_ATTR(mfg);
+CODEC_ATTR_STR(vendor_name);
+CODEC_ATTR_STR(chip_name);
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return snd_hdac_codec_modalias(dev_to_hdac_dev(dev), buf, 256);
+}
+static DEVICE_ATTR_RO(modalias);
+
+static struct attribute *hdac_dev_attrs[] = {
+ &dev_attr_type.attr,
+ &dev_attr_vendor_id.attr,
+ &dev_attr_subsystem_id.attr,
+ &dev_attr_revision_id.attr,
+ &dev_attr_afg.attr,
+ &dev_attr_mfg.attr,
+ &dev_attr_vendor_name.attr,
+ &dev_attr_chip_name.attr,
+ &dev_attr_modalias.attr,
+ NULL
+};
+
+static const struct attribute_group hdac_dev_attr_group = {
+ .attrs = hdac_dev_attrs,
+};
+
+const struct attribute_group *hdac_dev_attr_groups[] = {
+ &hdac_dev_attr_group,
+ NULL
+};
+
+/*
+ * Widget tree sysfs
+ *
+ * This is a tree showing the attributes of each widget. It appears like
+ * /sys/bus/hdaudioC0D0/widgets/04/caps
+ */
+
+struct widget_attribute;
+
+struct widget_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf);
+ ssize_t (*store)(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr,
+ const char *buf, size_t count);
+};
+
+static int get_codec_nid(struct kobject *kobj, struct hdac_device **codecp)
+{
+ struct device *dev = kobj_to_dev(kobj->parent->parent);
+ int nid;
+ ssize_t ret;
+
+ ret = kstrtoint(kobj->name, 16, &nid);
+ if (ret < 0)
+ return ret;
+ *codecp = dev_to_hdac_dev(dev);
+ return nid;
+}
+
+static ssize_t widget_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct widget_attribute *wid_attr =
+ container_of(attr, struct widget_attribute, attr);
+ struct hdac_device *codec;
+ int nid;
+
+ if (!wid_attr->show)
+ return -EIO;
+ nid = get_codec_nid(kobj, &codec);
+ if (nid < 0)
+ return nid;
+ return wid_attr->show(codec, nid, wid_attr, buf);
+}
+
+static ssize_t widget_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct widget_attribute *wid_attr =
+ container_of(attr, struct widget_attribute, attr);
+ struct hdac_device *codec;
+ int nid;
+
+ if (!wid_attr->store)
+ return -EIO;
+ nid = get_codec_nid(kobj, &codec);
+ if (nid < 0)
+ return nid;
+ return wid_attr->store(codec, nid, wid_attr, buf, count);
+}
+
+static const struct sysfs_ops widget_sysfs_ops = {
+ .show = widget_attr_show,
+ .store = widget_attr_store,
+};
+
+static void widget_release(struct kobject *kobj)
+{
+ kfree(kobj);
+}
+
+static const struct kobj_type widget_ktype = {
+ .release = widget_release,
+ .sysfs_ops = &widget_sysfs_ops,
+};
+
+#define WIDGET_ATTR_RO(_name) \
+ struct widget_attribute wid_attr_##_name = __ATTR_RO(_name)
+#define WIDGET_ATTR_RW(_name) \
+ struct widget_attribute wid_attr_##_name = __ATTR_RW(_name)
+
+static ssize_t caps_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "0x%08x\n", snd_hdac_get_wcaps(codec, nid));
+}
+
+static ssize_t pin_caps_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ if (snd_hdac_get_wcaps_type(snd_hdac_get_wcaps(codec, nid)) != AC_WID_PIN)
+ return 0;
+ return sysfs_emit(buf, "0x%08x\n",
+ snd_hdac_read_parm(codec, nid, AC_PAR_PIN_CAP));
+}
+
+static ssize_t pin_cfg_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ unsigned int val;
+
+ if (snd_hdac_get_wcaps_type(snd_hdac_get_wcaps(codec, nid)) != AC_WID_PIN)
+ return 0;
+ if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val))
+ return 0;
+ return sysfs_emit(buf, "0x%08x\n", val);
+}
+
+static bool has_pcm_cap(struct hdac_device *codec, hda_nid_t nid)
+{
+ if (nid == codec->afg || nid == codec->mfg)
+ return true;
+ switch (snd_hdac_get_wcaps_type(snd_hdac_get_wcaps(codec, nid))) {
+ case AC_WID_AUD_OUT:
+ case AC_WID_AUD_IN:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static ssize_t pcm_caps_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ if (!has_pcm_cap(codec, nid))
+ return 0;
+ return sysfs_emit(buf, "0x%08x\n",
+ snd_hdac_read_parm(codec, nid, AC_PAR_PCM));
+}
+
+static ssize_t pcm_formats_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ if (!has_pcm_cap(codec, nid))
+ return 0;
+ return sysfs_emit(buf, "0x%08x\n",
+ snd_hdac_read_parm(codec, nid, AC_PAR_STREAM));
+}
+
+static ssize_t amp_in_caps_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ if (nid != codec->afg && !(snd_hdac_get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
+ return 0;
+ return sysfs_emit(buf, "0x%08x\n",
+ snd_hdac_read_parm(codec, nid, AC_PAR_AMP_IN_CAP));
+}
+
+static ssize_t amp_out_caps_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ if (nid != codec->afg && !(snd_hdac_get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
+ return 0;
+ return sysfs_emit(buf, "0x%08x\n",
+ snd_hdac_read_parm(codec, nid, AC_PAR_AMP_OUT_CAP));
+}
+
+static ssize_t power_caps_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ if (nid != codec->afg && !(snd_hdac_get_wcaps(codec, nid) & AC_WCAP_POWER))
+ return 0;
+ return sysfs_emit(buf, "0x%08x\n",
+ snd_hdac_read_parm(codec, nid, AC_PAR_POWER_STATE));
+}
+
+static ssize_t gpio_caps_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ return sysfs_emit(buf, "0x%08x\n",
+ snd_hdac_read_parm(codec, nid, AC_PAR_GPIO_CAP));
+}
+
+static ssize_t connections_show(struct hdac_device *codec, hda_nid_t nid,
+ struct widget_attribute *attr, char *buf)
+{
+ hda_nid_t list[32];
+ int i, nconns;
+ ssize_t ret = 0;
+
+ nconns = snd_hdac_get_connections(codec, nid, list, ARRAY_SIZE(list));
+ if (nconns <= 0)
+ return nconns;
+ for (i = 0; i < nconns; i++)
+ ret += sysfs_emit_at(buf, ret, "%s0x%02x", i ? " " : "", list[i]);
+ ret += sysfs_emit_at(buf, ret, "\n");
+ return ret;
+}
+
+static WIDGET_ATTR_RO(caps);
+static WIDGET_ATTR_RO(pin_caps);
+static WIDGET_ATTR_RO(pin_cfg);
+static WIDGET_ATTR_RO(pcm_caps);
+static WIDGET_ATTR_RO(pcm_formats);
+static WIDGET_ATTR_RO(amp_in_caps);
+static WIDGET_ATTR_RO(amp_out_caps);
+static WIDGET_ATTR_RO(power_caps);
+static WIDGET_ATTR_RO(gpio_caps);
+static WIDGET_ATTR_RO(connections);
+
+static struct attribute *widget_node_attrs[] = {
+ &wid_attr_caps.attr,
+ &wid_attr_pin_caps.attr,
+ &wid_attr_pin_cfg.attr,
+ &wid_attr_pcm_caps.attr,
+ &wid_attr_pcm_formats.attr,
+ &wid_attr_amp_in_caps.attr,
+ &wid_attr_amp_out_caps.attr,
+ &wid_attr_power_caps.attr,
+ &wid_attr_connections.attr,
+ NULL,
+};
+
+static struct attribute *widget_afg_attrs[] = {
+ &wid_attr_pcm_caps.attr,
+ &wid_attr_pcm_formats.attr,
+ &wid_attr_amp_in_caps.attr,
+ &wid_attr_amp_out_caps.attr,
+ &wid_attr_power_caps.attr,
+ &wid_attr_gpio_caps.attr,
+ NULL,
+};
+
+static const struct attribute_group widget_node_group = {
+ .attrs = widget_node_attrs,
+};
+
+static const struct attribute_group widget_afg_group = {
+ .attrs = widget_afg_attrs,
+};
+
+static void free_widget_node(struct kobject *kobj,
+ const struct attribute_group *group)
+{
+ if (kobj) {
+ sysfs_remove_group(kobj, group);
+ kobject_put(kobj);
+ }
+}
+
+static void widget_tree_free(struct hdac_device *codec)
+{
+ struct hdac_widget_tree *tree = codec->widgets;
+ struct kobject **p;
+
+ if (!tree)
+ return;
+ free_widget_node(tree->afg, &widget_afg_group);
+ if (tree->nodes) {
+ for (p = tree->nodes; *p; p++)
+ free_widget_node(*p, &widget_node_group);
+ kfree(tree->nodes);
+ }
+ kobject_put(tree->root);
+ kfree(tree);
+ codec->widgets = NULL;
+}
+
+static int add_widget_node(struct kobject *parent, hda_nid_t nid,
+ const struct attribute_group *group,
+ struct kobject **res)
+{
+ struct kobject *kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
+ int err;
+
+ if (!kobj)
+ return -ENOMEM;
+ kobject_init(kobj, &widget_ktype);
+ err = kobject_add(kobj, parent, "%02x", nid);
+ if (err < 0) {
+ kobject_put(kobj);
+ return err;
+ }
+ err = sysfs_create_group(kobj, group);
+ if (err < 0) {
+ kobject_put(kobj);
+ return err;
+ }
+
+ *res = kobj;
+ return 0;
+}
+
+static int widget_tree_create(struct hdac_device *codec)
+{
+ struct hdac_widget_tree *tree;
+ int i, err;
+ hda_nid_t nid;
+
+ tree = codec->widgets = kzalloc(sizeof(*tree), GFP_KERNEL);
+ if (!tree)
+ return -ENOMEM;
+
+ tree->root = kobject_create_and_add("widgets", &codec->dev.kobj);
+ if (!tree->root)
+ return -ENOMEM;
+
+ tree->nodes = kcalloc(codec->num_nodes + 1, sizeof(*tree->nodes),
+ GFP_KERNEL);
+ if (!tree->nodes)
+ return -ENOMEM;
+
+ for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) {
+ err = add_widget_node(tree->root, nid, &widget_node_group,
+ &tree->nodes[i]);
+ if (err < 0)
+ return err;
+ }
+
+ if (codec->afg) {
+ err = add_widget_node(tree->root, codec->afg,
+ &widget_afg_group, &tree->afg);
+ if (err < 0)
+ return err;
+ }
+
+ kobject_uevent(tree->root, KOBJ_CHANGE);
+ return 0;
+}
+
+/* call with codec->widget_lock held */
+int hda_widget_sysfs_init(struct hdac_device *codec)
+{
+ int err;
+
+ if (codec->widgets)
+ return 0; /* already created */
+
+ err = widget_tree_create(codec);
+ if (err < 0) {
+ widget_tree_free(codec);
+ return err;
+ }
+
+ return 0;
+}
+
+/* call with codec->widget_lock held */
+void hda_widget_sysfs_exit(struct hdac_device *codec)
+{
+ widget_tree_free(codec);
+}
+
+/* call with codec->widget_lock held */
+int hda_widget_sysfs_reinit(struct hdac_device *codec,
+ hda_nid_t start_nid, int num_nodes)
+{
+ struct hdac_widget_tree *tree;
+ hda_nid_t end_nid = start_nid + num_nodes;
+ hda_nid_t nid;
+ int i;
+
+ if (!codec->widgets)
+ return 0;
+
+ tree = kmemdup(codec->widgets, sizeof(*tree), GFP_KERNEL);
+ if (!tree)
+ return -ENOMEM;
+
+ tree->nodes = kcalloc(num_nodes + 1, sizeof(*tree->nodes), GFP_KERNEL);
+ if (!tree->nodes) {
+ kfree(tree);
+ return -ENOMEM;
+ }
+
+ /* prune non-existing nodes */
+ for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) {
+ if (nid < start_nid || nid >= end_nid)
+ free_widget_node(codec->widgets->nodes[i],
+ &widget_node_group);
+ }
+
+ /* add new nodes */
+ for (i = 0, nid = start_nid; i < num_nodes; i++, nid++) {
+ if (nid < codec->start_nid || nid >= codec->end_nid)
+ add_widget_node(tree->root, nid, &widget_node_group,
+ &tree->nodes[i]);
+ else
+ tree->nodes[i] =
+ codec->widgets->nodes[nid - codec->start_nid];
+ }
+
+ /* replace with the new tree */
+ kfree(codec->widgets->nodes);
+ kfree(codec->widgets);
+ codec->widgets = tree;
+
+ kobject_uevent(tree->root, KOBJ_CHANGE);
+ return 0;
+}
diff --git a/sound/hda/core/trace.c b/sound/hda/core/trace.c
new file mode 100644
index 000000000000..ca2d6bd94518
--- /dev/null
+++ b/sound/hda/core/trace.c
@@ -0,0 +1,6 @@
+/*
+ * tracepoint definitions for HD-audio core drivers
+ */
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/sound/hda/core/trace.h b/sound/hda/core/trace.h
new file mode 100644
index 000000000000..280c42f3eb75
--- /dev/null
+++ b/sound/hda/core/trace.h
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hda
+
+#if !defined(__HDAC_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define __HDAC_TRACE_H
+
+#include <linux/tracepoint.h>
+#include <linux/device.h>
+#include <sound/hdaudio.h>
+
+#ifndef HDAC_MSG_MAX
+#define HDAC_MSG_MAX 500
+#endif
+
+struct hdac_bus;
+struct hdac_codec;
+
+TRACE_EVENT(hda_send_cmd,
+ TP_PROTO(struct hdac_bus *bus, unsigned int cmd),
+ TP_ARGS(bus, cmd),
+ TP_STRUCT__entry(
+ __string(name, dev_name((bus)->dev))
+ __field(u32, cmd)
+ ),
+ TP_fast_assign(
+ __assign_str(name);
+ __entry->cmd = cmd;
+ ),
+ TP_printk("[%s:%d] val=0x%08x", __get_str(name), __entry->cmd >> 28, __entry->cmd)
+);
+
+TRACE_EVENT(hda_get_response,
+ TP_PROTO(struct hdac_bus *bus, unsigned int addr, unsigned int res),
+ TP_ARGS(bus, addr, res),
+ TP_STRUCT__entry(
+ __string(name, dev_name((bus)->dev))
+ __field(u32, addr)
+ __field(u32, res)
+ ),
+ TP_fast_assign(
+ __assign_str(name);
+ __entry->addr = addr;
+ __entry->res = res;
+ ),
+ TP_printk("[%s:%d] val=0x%08x", __get_str(name), __entry->addr, __entry->res)
+);
+
+TRACE_EVENT(hda_unsol_event,
+ TP_PROTO(struct hdac_bus *bus, u32 res, u32 res_ex),
+ TP_ARGS(bus, res, res_ex),
+ TP_STRUCT__entry(
+ __string(name, dev_name((bus)->dev))
+ __field(u32, res)
+ __field(u32, res_ex)
+ ),
+ TP_fast_assign(
+ __assign_str(name);
+ __entry->res = res;
+ __entry->res_ex = res_ex;
+ ),
+ TP_printk("[%s:%d] res=0x%08x, res_ex=0x%08x", __get_str(name),
+ __entry->res_ex & 0x0f, __entry->res, __entry->res_ex)
+);
+
+DECLARE_EVENT_CLASS(hdac_stream,
+ TP_PROTO(struct hdac_bus *bus, struct hdac_stream *azx_dev),
+
+ TP_ARGS(bus, azx_dev),
+
+ TP_STRUCT__entry(
+ __field(unsigned char, stream_tag)
+ ),
+
+ TP_fast_assign(
+ __entry->stream_tag = (azx_dev)->stream_tag;
+ ),
+
+ TP_printk("stream_tag: %d", __entry->stream_tag)
+);
+
+DEFINE_EVENT(hdac_stream, snd_hdac_stream_start,
+ TP_PROTO(struct hdac_bus *bus, struct hdac_stream *azx_dev),
+ TP_ARGS(bus, azx_dev)
+);
+
+DEFINE_EVENT(hdac_stream, snd_hdac_stream_stop,
+ TP_PROTO(struct hdac_bus *bus, struct hdac_stream *azx_dev),
+ TP_ARGS(bus, azx_dev)
+);
+
+#endif /* __HDAC_TRACE_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+#include <trace/define_trace.h>
diff --git a/sound/i2c/Makefile b/sound/i2c/Makefile
index 36879bf88700..c827f9f70a33 100644
--- a/sound/i2c/Makefile
+++ b/sound/i2c/Makefile
@@ -1,11 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-i2c-objs := i2c.o
-snd-cs8427-objs := cs8427.o
-snd-tea6330t-objs := tea6330t.o
+snd-i2c-y := i2c.o
+snd-cs8427-y := cs8427.o
+snd-tea6330t-y := tea6330t.o
obj-$(CONFIG_SND) += other/
diff --git a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c
index 04ae8704cdcd..46f081268348 100644
--- a/sound/i2c/cs8427.c
+++ b/sound/i2c/cs8427.c
@@ -1,30 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for control of the CS8427 via i2c bus
* IEC958 (S/PDIF) receiver & transmitter by Cirrus Logic
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/bitrev.h>
-#include <asm/unaligned.h>
+#include <linux/module.h>
+#include <linux/unaligned.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -64,9 +50,11 @@ int snd_cs8427_reg_write(struct snd_i2c_device *device, unsigned char reg,
buf[0] = reg & 0x7f;
buf[1] = val;
- if ((err = snd_i2c_sendbytes(device, buf, 2)) != 2) {
- snd_printk(KERN_ERR "unable to send bytes 0x%02x:0x%02x "
- "to CS8427 (%i)\n", buf[0], buf[1], err);
+ err = snd_i2c_sendbytes(device, buf, 2);
+ if (err != 2) {
+ dev_err(device->bus->card->dev,
+ "unable to send bytes 0x%02x:0x%02x to CS8427 (%i)\n",
+ buf[0], buf[1], err);
return err < 0 ? err : -EIO;
}
return 0;
@@ -79,14 +67,16 @@ static int snd_cs8427_reg_read(struct snd_i2c_device *device, unsigned char reg)
int err;
unsigned char buf;
- if ((err = snd_i2c_sendbytes(device, &reg, 1)) != 1) {
- snd_printk(KERN_ERR "unable to send register 0x%x byte "
- "to CS8427\n", reg);
+ err = snd_i2c_sendbytes(device, &reg, 1);
+ if (err != 1) {
+ dev_err(device->bus->card->dev,
+ "unable to send register 0x%x byte to CS8427\n", reg);
return err < 0 ? err : -EIO;
}
- if ((err = snd_i2c_readbytes(device, &buf, 1)) != 1) {
- snd_printk(KERN_ERR "unable to read register 0x%x byte "
- "from CS8427\n", reg);
+ err = snd_i2c_readbytes(device, &buf, 1);
+ if (err != 1) {
+ dev_err(device->bus->card->dev,
+ "unable to read register 0x%x byte from CS8427\n", reg);
return err < 0 ? err : -EIO;
}
return buf;
@@ -117,12 +107,13 @@ static int snd_cs8427_send_corudata(struct snd_i2c_device *device,
struct cs8427 *chip = device->private_data;
char *hw_data = udata ?
chip->playback.hw_udata : chip->playback.hw_status;
- char data[32];
+ unsigned char data[32];
int err, idx;
if (!memcmp(hw_data, ndata, count))
return 0;
- if ((err = snd_cs8427_select_corudata(device, udata)) < 0)
+ err = snd_cs8427_select_corudata(device, udata);
+ if (err < 0)
return err;
memcpy(hw_data, ndata, count);
if (udata) {
@@ -149,10 +140,8 @@ static void snd_cs8427_free(struct snd_i2c_device *device)
kfree(device->private_data);
}
-int snd_cs8427_create(struct snd_i2c_bus *bus,
- unsigned char addr,
- unsigned int reset_timeout,
- struct snd_i2c_device **r_cs8427)
+int snd_cs8427_init(struct snd_i2c_bus *bus,
+ struct snd_i2c_device *device)
{
static unsigned char initvals1[] = {
CS8427_REG_CONTROL1 | CS8427_REG_AUTOINC,
@@ -199,36 +188,26 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
Inhibit E->F transfers. */
CS8427_UD | CS8427_EFTUI | CS8427_DETUI,
};
+ struct cs8427 *chip = device->private_data;
int err;
- struct cs8427 *chip;
- struct snd_i2c_device *device;
unsigned char buf[24];
- if ((err = snd_i2c_device_create(bus, "CS8427",
- CS8427_ADDR | (addr & 7),
- &device)) < 0)
- return err;
- chip = device->private_data = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- snd_i2c_device_free(device);
- return -ENOMEM;
- }
- device->private_free = snd_cs8427_free;
-
snd_i2c_lock(bus);
err = snd_cs8427_reg_read(device, CS8427_REG_ID_AND_VER);
if (err != CS8427_VER8427A) {
/* give second chance */
- snd_printk(KERN_WARNING "invalid CS8427 signature 0x%x: "
- "let me try again...\n", err);
+ dev_warn(device->bus->card->dev,
+ "invalid CS8427 signature 0x%x: let me try again...\n",
+ err);
err = snd_cs8427_reg_read(device, CS8427_REG_ID_AND_VER);
}
if (err != CS8427_VER8427A) {
snd_i2c_unlock(bus);
- snd_printk(KERN_ERR "unable to find CS8427 signature "
- "(expected 0x%x, read 0x%x),\n",
- CS8427_VER8427A, err);
- snd_printk(KERN_ERR " initialization is not completed\n");
+ dev_err(device->bus->card->dev,
+ "unable to find CS8427 signature (expected 0x%x, read 0x%x),\n",
+ CS8427_VER8427A, err);
+ dev_err(device->bus->card->dev,
+ " initialization is not completed\n");
return -EFAULT;
}
/* turn off run bit while making changes to configuration */
@@ -237,7 +216,8 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
goto __fail;
/* send initial values */
memcpy(chip->regmap + (initvals1[0] & 0x7f), initvals1 + 1, 6);
- if ((err = snd_i2c_sendbytes(device, initvals1, 7)) != 7) {
+ err = snd_i2c_sendbytes(device, initvals1, 7);
+ if (err != 7) {
err = err < 0 ? err : -EIO;
goto __fail;
}
@@ -245,11 +225,13 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
memset(buf, 0, 7);
/* from address 9 to 15 */
buf[0] = 9; /* register */
- if ((err = snd_i2c_sendbytes(device, buf, 7)) != 7)
+ err = snd_i2c_sendbytes(device, buf, 7);
+ if (err != 7)
goto __fail;
/* send transfer initialization sequence */
memcpy(chip->regmap + (initvals2[0] & 0x7f), initvals2 + 1, 3);
- if ((err = snd_i2c_sendbytes(device, initvals2, 4)) != 4) {
+ err = snd_i2c_sendbytes(device, initvals2, 4);
+ if (err != 4) {
err = err < 0 ? err : -EIO;
goto __fail;
}
@@ -263,10 +245,44 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
snd_i2c_unlock(bus);
/* turn on run bit and rock'n'roll */
+ snd_cs8427_reset(device);
+
+ return 0;
+
+__fail:
+ snd_i2c_unlock(bus);
+
+ return err;
+}
+EXPORT_SYMBOL(snd_cs8427_init);
+
+int snd_cs8427_create(struct snd_i2c_bus *bus,
+ unsigned char addr,
+ unsigned int reset_timeout,
+ struct snd_i2c_device **r_cs8427)
+{
+ int err;
+ struct cs8427 *chip;
+ struct snd_i2c_device *device;
+
+ err = snd_i2c_device_create(bus, "CS8427", CS8427_ADDR | (addr & 7),
+ &device);
+ if (err < 0)
+ return err;
+ chip = device->private_data = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL) {
+ snd_i2c_device_free(device);
+ return -ENOMEM;
+ }
+ device->private_free = snd_cs8427_free;
+
if (reset_timeout < 1)
reset_timeout = 1;
chip->reset_timeout = reset_timeout;
- snd_cs8427_reset(device);
+
+ err = snd_cs8427_init(bus, device);
+ if (err)
+ goto __fail;
#if 0 // it's nice for read tests
{
@@ -276,7 +292,7 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
snd_i2c_sendbytes(device, buf, 1);
snd_i2c_readbytes(device, buf, 127);
for (xx = 0; xx < 127; xx++)
- printk(KERN_DEBUG "reg[0x%x] = 0x%x\n", xx+1, buf[xx]);
+ dev_dbg(device->bus->card->dev, "reg[0x%x] = 0x%x\n", xx+1, buf[xx]);
}
#endif
@@ -285,7 +301,6 @@ int snd_cs8427_create(struct snd_i2c_bus *bus,
return 0;
__fail:
- snd_i2c_unlock(bus);
snd_i2c_device_free(device);
return err < 0 ? err : -EIO;
}
@@ -378,16 +393,17 @@ static int snd_cs8427_qsubcode_get(struct snd_kcontrol *kcontrol,
int err;
snd_i2c_lock(device->bus);
- if ((err = snd_i2c_sendbytes(device, &reg, 1)) != 1) {
- snd_printk(KERN_ERR "unable to send register 0x%x byte "
- "to CS8427\n", reg);
+ err = snd_i2c_sendbytes(device, &reg, 1);
+ if (err != 1) {
+ dev_err(device->bus->card->dev,
+ "unable to send register 0x%x byte to CS8427\n", reg);
snd_i2c_unlock(device->bus);
return err < 0 ? err : -EIO;
}
err = snd_i2c_readbytes(device, ucontrol->value.bytes.data, 10);
if (err != 10) {
- snd_printk(KERN_ERR "unable to read Q-subcode bytes "
- "from CS8427\n");
+ dev_err(device->bus->card->dev,
+ "unable to read Q-subcode bytes from CS8427\n");
snd_i2c_unlock(device->bus);
return err < 0 ? err : -EIO;
}
@@ -454,7 +470,7 @@ static int snd_cs8427_spdif_mask_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_cs8427_iec958_controls[] = {
+static const struct snd_kcontrol_new snd_cs8427_iec958_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.info = snd_cs8427_in_status_info,
@@ -548,10 +564,13 @@ int snd_cs8427_iec958_active(struct snd_i2c_device *cs8427, int active)
if (snd_BUG_ON(!cs8427))
return -ENXIO;
chip = cs8427->private_data;
- if (active)
+ if (active) {
memcpy(chip->playback.pcm_status,
chip->playback.def_status, 24);
- chip->playback.pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ chip->playback.pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ } else {
+ chip->playback.pcm_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ }
snd_ctl_notify(cs8427->bus->card,
SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO,
&chip->playback.pcm_ctl->id);
@@ -601,15 +620,3 @@ int snd_cs8427_iec958_pcm(struct snd_i2c_device *cs8427, unsigned int rate)
}
EXPORT_SYMBOL(snd_cs8427_iec958_pcm);
-
-static int __init alsa_cs8427_module_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_cs8427_module_exit(void)
-{
-}
-
-module_init(alsa_cs8427_module_init)
-module_exit(alsa_cs8427_module_exit)
diff --git a/sound/i2c/i2c.c b/sound/i2c/i2c.c
index eb7c7d05a7c1..847e3b6ca601 100644
--- a/sound/i2c/i2c.c
+++ b/sound/i2c/i2c.c
@@ -1,27 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Generic i2c interface for ALSA
*
* (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
* Modified for the ALSA driver by Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <sound/core.h>
@@ -38,7 +25,7 @@ static int snd_i2c_bit_readbytes(struct snd_i2c_device *device,
static int snd_i2c_bit_probeaddr(struct snd_i2c_bus *bus,
unsigned short addr);
-static struct snd_i2c_ops snd_i2c_bit_ops = {
+static const struct snd_i2c_ops snd_i2c_bit_ops = {
.sendbytes = snd_i2c_bit_sendbytes,
.readbytes = snd_i2c_bit_readbytes,
.probeaddr = snd_i2c_bit_probeaddr,
@@ -80,7 +67,7 @@ int snd_i2c_bus_create(struct snd_card *card, const char *name,
{
struct snd_i2c_bus *bus;
int err;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_i2c_bus_dev_free,
};
@@ -97,7 +84,7 @@ int snd_i2c_bus_create(struct snd_card *card, const char *name,
list_add_tail(&bus->buses, &master->buses);
bus->master = master;
}
- strlcpy(bus->name, name, sizeof(bus->name));
+ strscpy(bus->name, name, sizeof(bus->name));
err = snd_device_new(card, SNDRV_DEV_BUS, bus, &ops);
if (err < 0) {
snd_i2c_bus_free(bus);
@@ -121,7 +108,7 @@ int snd_i2c_device_create(struct snd_i2c_bus *bus, const char *name,
if (device == NULL)
return -ENOMEM;
device->addr = addr;
- strlcpy(device->name, name, sizeof(device->name));
+ strscpy(device->name, name, sizeof(device->name));
list_add_tail(&device->list, &bus->devices);
device->bus = bus;
*rdevice = device;
@@ -337,16 +324,3 @@ static int snd_i2c_bit_probeaddr(struct snd_i2c_bus *bus, unsigned short addr)
snd_i2c_bit_stop(bus);
return err;
}
-
-
-static int __init alsa_i2c_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_i2c_exit(void)
-{
-}
-
-module_init(alsa_i2c_init)
-module_exit(alsa_i2c_exit)
diff --git a/sound/i2c/other/Makefile b/sound/i2c/other/Makefile
index 2dad40f3f622..0a2c0d147ab8 100644
--- a/sound/i2c/other/Makefile
+++ b/sound/i2c/other/Makefile
@@ -1,17 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ak4114-objs := ak4114.o
-snd-ak4117-objs := ak4117.o
-snd-ak4113-objs := ak4113.o
-snd-ak4xxx-adda-objs := ak4xxx-adda.o
-snd-pt2258-objs := pt2258.o
-snd-tea575x-tuner-objs := tea575x-tuner.o
+snd-ak4114-y := ak4114.o
+snd-ak4117-y := ak4117.o
+snd-ak4113-y := ak4113.o
+snd-ak4xxx-adda-y := ak4xxx-adda.o
+snd-pt2258-y := pt2258.o
# Module Dependency
obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o
obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4113.o snd-ak4xxx-adda.o snd-pt2258.o
-obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o
diff --git a/sound/i2c/other/ak4113.c b/sound/i2c/other/ak4113.c
index 971a84a4fa77..70b3f7e17f9e 100644
--- a/sound/i2c/other/ak4113.c
+++ b/sound/i2c/other/ak4113.c
@@ -1,28 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for control of the AK4113 via I2C/4-wire serial interface
* IEC958 (S/PDIF) receiver by Asahi Kasei
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Copyright (c) by Pavel Hofman <pavel.hofman@ivitera.com>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -55,10 +41,8 @@ static inline unsigned char reg_read(struct ak4113 *ak4113, unsigned char reg)
static void snd_ak4113_free(struct ak4113 *chip)
{
- chip->init = 1; /* don't schedule new work */
- mb();
- cancel_delayed_work(&chip->work);
- flush_scheduled_work();
+ atomic_inc(&chip->wq_processing); /* don't schedule new work */
+ cancel_delayed_work_sync(&chip->work);
kfree(chip);
}
@@ -74,9 +58,9 @@ int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
void *private_data, struct ak4113 **r_ak4113)
{
struct ak4113 *chip;
- int err = 0;
+ int err;
unsigned char reg;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_ak4113_dev_free,
};
@@ -89,6 +73,8 @@ int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
chip->write = write;
chip->private_data = private_data;
INIT_DELAYED_WORK(&chip->work, ak4113_stats);
+ atomic_set(&chip->wq_processing, 0);
+ mutex_init(&chip->reinit_mutex);
for (reg = 0; reg < AK4113_WRITABLE_REGS ; reg++)
chip->regmap[reg] = pgm[reg];
@@ -98,7 +84,7 @@ int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
AK4113_CINT | AK4113_STC);
chip->rcs1 = reg_read(chip, AK4113_REG_RCS1);
chip->rcs2 = reg_read(chip, AK4113_REG_RCS2);
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ err = snd_device_new(card, SNDRV_DEV_CODEC, chip, &ops);
if (err < 0)
goto __fail;
@@ -108,7 +94,7 @@ int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
__fail:
snd_ak4113_free(chip);
- return err < 0 ? err : -EIO;
+ return err;
}
EXPORT_SYMBOL_GPL(snd_ak4113_create);
@@ -139,13 +125,13 @@ static void ak4113_init_regs(struct ak4113 *chip)
void snd_ak4113_reinit(struct ak4113 *chip)
{
- chip->init = 1;
- mb();
- flush_scheduled_work();
- ak4113_init_regs(chip);
+ if (atomic_inc_return(&chip->wq_processing) == 1)
+ cancel_delayed_work_sync(&chip->work);
+ scoped_guard(mutex, &chip->reinit_mutex) {
+ ak4113_init_regs(chip);
+ }
/* bring up statistics / event queing */
- chip->init = 0;
- if (chip->kctls[0])
+ if (atomic_dec_and_test(&chip->wq_processing))
schedule_delayed_work(&chip->work, HZ / 10);
}
EXPORT_SYMBOL_GPL(snd_ak4113_reinit);
@@ -198,13 +184,11 @@ static int snd_ak4113_in_error_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
- long *ptr;
- spin_lock_irq(&chip->lock);
- ptr = (long *)(((char *)chip) + kcontrol->private_value);
- ucontrol->value.integer.value[0] = *ptr;
- *ptr = 0;
- spin_unlock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
+ ucontrol->value.integer.value[0] =
+ chip->errors[kcontrol->private_value];
+ chip->errors[kcontrol->private_value] = 0;
return 0;
}
@@ -250,14 +234,13 @@ static int snd_ak4113_rx_put(struct snd_kcontrol *kcontrol,
int change;
u8 old_val;
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
old_val = chip->regmap[AK4113_REG_IO1];
change = ucontrol->value.integer.value[0] != AK4113_IPS(old_val);
if (change)
reg_write(chip, AK4113_REG_IO1,
(old_val & (~AK4113_IPS(0xff))) |
(AK4113_IPS(ucontrol->value.integer.value[0])));
- spin_unlock_irq(&chip->lock);
return change;
}
@@ -364,7 +347,7 @@ static int snd_ak4113_spdif_qget(struct snd_kcontrol *kcontrol,
}
/* Don't forget to change AK4113_CONTROLS define!!! */
-static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
+static const struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "IEC958 Parity Errors",
@@ -372,7 +355,7 @@ static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4113_in_error_info,
.get = snd_ak4113_in_error_get,
- .private_value = offsetof(struct ak4113, parity_errors),
+ .private_value = AK4113_PARITY_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -381,7 +364,7 @@ static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4113_in_error_info,
.get = snd_ak4113_in_error_get,
- .private_value = offsetof(struct ak4113, v_bit_errors),
+ .private_value = AK4113_V_BIT_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -390,7 +373,7 @@ static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4113_in_error_info,
.get = snd_ak4113_in_error_get,
- .private_value = offsetof(struct ak4113, ccrc_errors),
+ .private_value = AK4113_CCRC_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -399,7 +382,7 @@ static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4113_in_error_info,
.get = snd_ak4113_in_error_get,
- .private_value = offsetof(struct ak4113, qcrc_errors),
+ .private_value = AK4113_QCRC_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -426,7 +409,7 @@ static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = "IEC958 Preample Capture Default",
+ .name = "IEC958 Preamble Capture Default",
.access = SNDRV_CTL_ELEM_ACCESS_READ |
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4113_spdif_pinfo,
@@ -492,9 +475,8 @@ static void snd_ak4113_proc_regs_read(struct snd_info_entry *entry,
static void snd_ak4113_proc_init(struct ak4113 *ak4113)
{
- struct snd_info_entry *entry;
- if (!snd_card_proc_new(ak4113->card, "ak4113", &entry))
- snd_info_set_text_ops(entry, ak4113, snd_ak4113_proc_regs_read);
+ snd_card_ro_proc_new(ak4113->card, "ak4113", ak4113,
+ snd_ak4113_proc_regs_read);
}
int snd_ak4113_build(struct ak4113 *ak4113,
@@ -548,27 +530,27 @@ int snd_ak4113_check_rate_and_errors(struct ak4113 *ak4113, unsigned int flags)
goto __rate;
rcs0 = reg_read(ak4113, AK4113_REG_RCS0);
rcs2 = reg_read(ak4113, AK4113_REG_RCS2);
- spin_lock_irqsave(&ak4113->lock, _flags);
- if (rcs0 & AK4113_PAR)
- ak4113->parity_errors++;
- if (rcs0 & AK4113_V)
- ak4113->v_bit_errors++;
- if (rcs2 & AK4113_CCRC)
- ak4113->ccrc_errors++;
- if (rcs2 & AK4113_QCRC)
- ak4113->qcrc_errors++;
- c0 = (ak4113->rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC |
- AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK)) ^
- (rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC |
- AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK));
- c1 = (ak4113->rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
- AK4113_DAT | 0xf0)) ^
- (rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
- AK4113_DAT | 0xf0));
- ak4113->rcs0 = rcs0 & ~(AK4113_QINT | AK4113_CINT | AK4113_STC);
- ak4113->rcs1 = rcs1;
- ak4113->rcs2 = rcs2;
- spin_unlock_irqrestore(&ak4113->lock, _flags);
+ scoped_guard(spinlock_irqsave, &ak4113->lock) {
+ if (rcs0 & AK4113_PAR)
+ ak4113->errors[AK4113_PARITY_ERRORS]++;
+ if (rcs0 & AK4113_V)
+ ak4113->errors[AK4113_V_BIT_ERRORS]++;
+ if (rcs2 & AK4113_CCRC)
+ ak4113->errors[AK4113_CCRC_ERRORS]++;
+ if (rcs2 & AK4113_QCRC)
+ ak4113->errors[AK4113_QCRC_ERRORS]++;
+ c0 = (ak4113->rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC |
+ AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK)) ^
+ (rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC |
+ AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK));
+ c1 = (ak4113->rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
+ AK4113_DAT | 0xf0)) ^
+ (rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
+ AK4113_DAT | 0xf0));
+ ak4113->rcs0 = rcs0 & ~(AK4113_QINT | AK4113_CINT | AK4113_STC);
+ ak4113->rcs1 = rcs1;
+ ak4113->rcs2 = rcs2;
+ }
if (rcs0 & AK4113_PAR)
snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
@@ -615,8 +597,6 @@ __rate:
(runtime->rate != res)) {
snd_pcm_stream_lock_irqsave(ak4113->substream, _flags);
if (snd_pcm_running(ak4113->substream)) {
- /*printk(KERN_DEBUG "rate changed (%i <- %i)\n",
- * runtime->rate, res); */
snd_pcm_stop(ak4113->substream,
SNDRV_PCM_STATE_DRAINING);
wake_up(&runtime->sleep);
@@ -632,8 +612,25 @@ static void ak4113_stats(struct work_struct *work)
{
struct ak4113 *chip = container_of(work, struct ak4113, work.work);
- if (!chip->init)
+ if (atomic_inc_return(&chip->wq_processing) == 1)
snd_ak4113_check_rate_and_errors(chip, chip->check_flags);
- schedule_delayed_work(&chip->work, HZ / 10);
+ if (atomic_dec_and_test(&chip->wq_processing))
+ schedule_delayed_work(&chip->work, HZ / 10);
+}
+
+#ifdef CONFIG_PM
+void snd_ak4113_suspend(struct ak4113 *chip)
+{
+ atomic_inc(&chip->wq_processing); /* don't schedule new work */
+ cancel_delayed_work_sync(&chip->work);
+}
+EXPORT_SYMBOL(snd_ak4113_suspend);
+
+void snd_ak4113_resume(struct ak4113 *chip)
+{
+ atomic_dec(&chip->wq_processing);
+ snd_ak4113_reinit(chip);
}
+EXPORT_SYMBOL(snd_ak4113_resume);
+#endif
diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c
index 0341451f814c..0e3a272c1490 100644
--- a/sound/i2c/other/ak4114.c
+++ b/sound/i2c/other/ak4114.c
@@ -1,27 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for control of the AK4114 via I2C and 4-wire serial interface
* IEC958 (S/PDIF) receiver by Asahi Kasei
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -52,23 +38,10 @@ static inline unsigned char reg_read(struct ak4114 *ak4114, unsigned char reg)
return ak4114->read(ak4114->private_data, reg);
}
-#if 0
-static void reg_dump(struct ak4114 *ak4114)
-{
- int i;
-
- printk(KERN_DEBUG "AK4114 REG DUMP:\n");
- for (i = 0; i < 0x20; i++)
- printk(KERN_DEBUG "reg[%02x] = %02x (%02x)\n", i, reg_read(ak4114, i), i < sizeof(ak4114->regmap) ? ak4114->regmap[i] : 0);
-}
-#endif
-
static void snd_ak4114_free(struct ak4114 *chip)
{
- chip->init = 1; /* don't schedule new work */
- mb();
- cancel_delayed_work(&chip->work);
- flush_scheduled_work();
+ atomic_inc(&chip->wq_processing); /* don't schedule new work */
+ cancel_delayed_work_sync(&chip->work);
kfree(chip);
}
@@ -81,13 +54,13 @@ static int snd_ak4114_dev_free(struct snd_device *device)
int snd_ak4114_create(struct snd_card *card,
ak4114_read_t *read, ak4114_write_t *write,
- const unsigned char pgm[7], const unsigned char txcsb[5],
+ const unsigned char pgm[6], const unsigned char txcsb[5],
void *private_data, struct ak4114 **r_ak4114)
{
struct ak4114 *chip;
int err = 0;
unsigned char reg;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_ak4114_dev_free,
};
@@ -100,8 +73,10 @@ int snd_ak4114_create(struct snd_card *card,
chip->write = write;
chip->private_data = private_data;
INIT_DELAYED_WORK(&chip->work, ak4114_stats);
+ atomic_set(&chip->wq_processing, 0);
+ mutex_init(&chip->reinit_mutex);
- for (reg = 0; reg < 7; reg++)
+ for (reg = 0; reg < 6; reg++)
chip->regmap[reg] = pgm[reg];
for (reg = 0; reg < 5; reg++)
chip->txcsb[reg] = txcsb[reg];
@@ -111,7 +86,8 @@ int snd_ak4114_create(struct snd_card *card,
chip->rcs0 = reg_read(chip, AK4114_REG_RCS0) & ~(AK4114_QINT | AK4114_CINT);
chip->rcs1 = reg_read(chip, AK4114_REG_RCS1);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0)
+ err = snd_device_new(card, SNDRV_DEV_CODEC, chip, &ops);
+ if (err < 0)
goto __fail;
if (r_ak4114)
@@ -120,8 +96,9 @@ int snd_ak4114_create(struct snd_card *card,
__fail:
snd_ak4114_free(chip);
- return err < 0 ? err : -EIO;
+ return err;
}
+EXPORT_SYMBOL(snd_ak4114_create);
void snd_ak4114_reg_write(struct ak4114 *chip, unsigned char reg, unsigned char mask, unsigned char val)
{
@@ -131,6 +108,7 @@ void snd_ak4114_reg_write(struct ak4114 *chip, unsigned char reg, unsigned char
reg_write(chip, reg,
(chip->txcsb[reg-AK4114_REG_TXCSB0] & ~mask) | val);
}
+EXPORT_SYMBOL(snd_ak4114_reg_write);
static void ak4114_init_regs(struct ak4114 *chip)
{
@@ -142,7 +120,7 @@ static void ak4114_init_regs(struct ak4114 *chip)
/* release reset, but leave powerdown */
reg_write(chip, AK4114_REG_PWRDN, (old | AK4114_RST) & ~AK4114_PWN);
udelay(200);
- for (reg = 1; reg < 7; reg++)
+ for (reg = 1; reg < 6; reg++)
reg_write(chip, reg, chip->regmap[reg]);
for (reg = 0; reg < 5; reg++)
reg_write(chip, reg + AK4114_REG_TXCSB0, chip->txcsb[reg]);
@@ -152,15 +130,16 @@ static void ak4114_init_regs(struct ak4114 *chip)
void snd_ak4114_reinit(struct ak4114 *chip)
{
- chip->init = 1;
- mb();
- flush_scheduled_work();
- ak4114_init_regs(chip);
+ if (atomic_inc_return(&chip->wq_processing) == 1)
+ cancel_delayed_work_sync(&chip->work);
+ scoped_guard(mutex, &chip->reinit_mutex) {
+ ak4114_init_regs(chip);
+ }
/* bring up statistics / event queing */
- chip->init = 0;
- if (chip->kctls[0])
+ if (atomic_dec_and_test(&chip->wq_processing))
schedule_delayed_work(&chip->work, HZ / 10);
}
+EXPORT_SYMBOL(snd_ak4114_reinit);
static unsigned int external_rate(unsigned char rcs1)
{
@@ -190,13 +169,11 @@ static int snd_ak4114_in_error_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct ak4114 *chip = snd_kcontrol_chip(kcontrol);
- long *ptr;
- spin_lock_irq(&chip->lock);
- ptr = (long *)(((char *)chip) + kcontrol->private_value);
- ucontrol->value.integer.value[0] = *ptr;
- *ptr = 0;
- spin_unlock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
+ ucontrol->value.integer.value[0] =
+ chip->errors[kcontrol->private_value];
+ chip->errors[kcontrol->private_value] = 0;
return 0;
}
@@ -330,14 +307,14 @@ static int snd_ak4114_spdif_qget(struct snd_kcontrol *kcontrol,
}
/* Don't forget to change AK4114_CONTROLS define!!! */
-static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = {
+static const struct snd_kcontrol_new snd_ak4114_iec958_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "IEC958 Parity Errors",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4114_in_error_info,
.get = snd_ak4114_in_error_get,
- .private_value = offsetof(struct ak4114, parity_errors),
+ .private_value = AK4114_PARITY_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -345,7 +322,7 @@ static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = {
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4114_in_error_info,
.get = snd_ak4114_in_error_get,
- .private_value = offsetof(struct ak4114, v_bit_errors),
+ .private_value = AK4114_V_BIT_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -353,7 +330,7 @@ static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = {
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4114_in_error_info,
.get = snd_ak4114_in_error_get,
- .private_value = offsetof(struct ak4114, ccrc_errors),
+ .private_value = AK4114_CCRC_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -361,7 +338,7 @@ static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = {
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4114_in_error_info,
.get = snd_ak4114_in_error_get,
- .private_value = offsetof(struct ak4114, qcrc_errors),
+ .private_value = AK4114_QCRC_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -401,7 +378,7 @@ static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = "IEC958 Preample Capture Default",
+ .name = "IEC958 Preamble Capture Default",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4114_spdif_pinfo,
.get = snd_ak4114_spdif_pget,
@@ -462,9 +439,8 @@ static void snd_ak4114_proc_regs_read(struct snd_info_entry *entry,
static void snd_ak4114_proc_init(struct ak4114 *ak4114)
{
- struct snd_info_entry *entry;
- if (!snd_card_proc_new(ak4114->card, "ak4114", &entry))
- snd_info_set_text_ops(entry, ak4114, snd_ak4114_proc_regs_read);
+ snd_card_ro_proc_new(ak4114->card, "ak4114", ak4114,
+ snd_ak4114_proc_regs_read);
}
int snd_ak4114_build(struct ak4114 *ak4114,
@@ -505,6 +481,7 @@ int snd_ak4114_build(struct ak4114 *ak4114,
schedule_delayed_work(&ak4114->work, HZ / 10);
return 0;
}
+EXPORT_SYMBOL(snd_ak4114_build);
/* notify kcontrols if any parameters are changed */
static void ak4114_notify(struct ak4114 *ak4114,
@@ -560,6 +537,7 @@ int snd_ak4114_external_rate(struct ak4114 *ak4114)
rcs1 = reg_read(ak4114, AK4114_REG_RCS1);
return external_rate(rcs1);
}
+EXPORT_SYMBOL(snd_ak4114_external_rate);
int snd_ak4114_check_rate_and_errors(struct ak4114 *ak4114, unsigned int flags)
{
@@ -573,21 +551,21 @@ int snd_ak4114_check_rate_and_errors(struct ak4114 *ak4114, unsigned int flags)
if (flags & AK4114_CHECK_NO_STAT)
goto __rate;
rcs0 = reg_read(ak4114, AK4114_REG_RCS0);
- spin_lock_irqsave(&ak4114->lock, _flags);
- if (rcs0 & AK4114_PAR)
- ak4114->parity_errors++;
- if (rcs1 & AK4114_V)
- ak4114->v_bit_errors++;
- if (rcs1 & AK4114_CCRC)
- ak4114->ccrc_errors++;
- if (rcs1 & AK4114_QCRC)
- ak4114->qcrc_errors++;
- c0 = (ak4114->rcs0 & (AK4114_QINT | AK4114_CINT | AK4114_PEM | AK4114_AUDION | AK4114_AUTO | AK4114_UNLCK)) ^
- (rcs0 & (AK4114_QINT | AK4114_CINT | AK4114_PEM | AK4114_AUDION | AK4114_AUTO | AK4114_UNLCK));
- c1 = (ak4114->rcs1 & 0xf0) ^ (rcs1 & 0xf0);
- ak4114->rcs0 = rcs0 & ~(AK4114_QINT | AK4114_CINT);
- ak4114->rcs1 = rcs1;
- spin_unlock_irqrestore(&ak4114->lock, _flags);
+ scoped_guard(spinlock_irqsave, &ak4114->lock) {
+ if (rcs0 & AK4114_PAR)
+ ak4114->errors[AK4114_PARITY_ERRORS]++;
+ if (rcs1 & AK4114_V)
+ ak4114->errors[AK4114_V_BIT_ERRORS]++;
+ if (rcs1 & AK4114_CCRC)
+ ak4114->errors[AK4114_CCRC_ERRORS]++;
+ if (rcs1 & AK4114_QCRC)
+ ak4114->errors[AK4114_QCRC_ERRORS]++;
+ c0 = (ak4114->rcs0 & (AK4114_QINT | AK4114_CINT | AK4114_PEM | AK4114_AUDION | AK4114_AUTO | AK4114_UNLCK)) ^
+ (rcs0 & (AK4114_QINT | AK4114_CINT | AK4114_PEM | AK4114_AUDION | AK4114_AUTO | AK4114_UNLCK));
+ c1 = (ak4114->rcs1 & 0xf0) ^ (rcs1 & 0xf0);
+ ak4114->rcs0 = rcs0 & ~(AK4114_QINT | AK4114_CINT);
+ ak4114->rcs1 = rcs1;
+ }
ak4114_notify(ak4114, rcs0, rcs1, c0, c1);
if (ak4114->change_callback && (c0 | c1) != 0)
@@ -599,7 +577,6 @@ int snd_ak4114_check_rate_and_errors(struct ak4114 *ak4114, unsigned int flags)
if (!(flags & AK4114_CHECK_NO_RATE) && runtime && runtime->rate != res) {
snd_pcm_stream_lock_irqsave(ak4114->capture_substream, _flags);
if (snd_pcm_running(ak4114->capture_substream)) {
- // printk(KERN_DEBUG "rate changed (%i <- %i)\n", runtime->rate, res);
snd_pcm_stop(ak4114->capture_substream, SNDRV_PCM_STATE_DRAINING);
res = 1;
}
@@ -607,20 +584,30 @@ int snd_ak4114_check_rate_and_errors(struct ak4114 *ak4114, unsigned int flags)
}
return res;
}
+EXPORT_SYMBOL(snd_ak4114_check_rate_and_errors);
static void ak4114_stats(struct work_struct *work)
{
struct ak4114 *chip = container_of(work, struct ak4114, work.work);
- if (!chip->init)
+ if (atomic_inc_return(&chip->wq_processing) == 1)
snd_ak4114_check_rate_and_errors(chip, chip->check_flags);
+ if (atomic_dec_and_test(&chip->wq_processing))
+ schedule_delayed_work(&chip->work, HZ / 10);
+}
- schedule_delayed_work(&chip->work, HZ / 10);
+#ifdef CONFIG_PM
+void snd_ak4114_suspend(struct ak4114 *chip)
+{
+ atomic_inc(&chip->wq_processing); /* don't schedule new work */
+ cancel_delayed_work_sync(&chip->work);
}
+EXPORT_SYMBOL(snd_ak4114_suspend);
-EXPORT_SYMBOL(snd_ak4114_create);
-EXPORT_SYMBOL(snd_ak4114_reg_write);
-EXPORT_SYMBOL(snd_ak4114_reinit);
-EXPORT_SYMBOL(snd_ak4114_build);
-EXPORT_SYMBOL(snd_ak4114_external_rate);
-EXPORT_SYMBOL(snd_ak4114_check_rate_and_errors);
+void snd_ak4114_resume(struct ak4114 *chip)
+{
+ atomic_dec(&chip->wq_processing);
+ snd_ak4114_reinit(chip);
+}
+EXPORT_SYMBOL(snd_ak4114_resume);
+#endif
diff --git a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c
index 2cad2d612518..d2ec20f885f0 100644
--- a/sound/i2c/other/ak4117.c
+++ b/sound/i2c/other/ak4117.c
@@ -1,27 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for control of the AK4117 via 4-wire serial interface
* IEC958 (S/PDIF) receiver by Asahi Kasei
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -34,7 +20,7 @@ MODULE_LICENSE("GPL");
#define AK4117_ADDR 0x00 /* fixed address */
-static void snd_ak4117_timer(unsigned long data);
+static void snd_ak4117_timer(struct timer_list *t);
static void reg_write(struct ak4117 *ak4117, unsigned char reg, unsigned char val)
{
@@ -48,20 +34,9 @@ static inline unsigned char reg_read(struct ak4117 *ak4117, unsigned char reg)
return ak4117->read(ak4117->private_data, reg);
}
-#if 0
-static void reg_dump(struct ak4117 *ak4117)
-{
- int i;
-
- printk(KERN_DEBUG "AK4117 REG DUMP:\n");
- for (i = 0; i < 0x1b; i++)
- printk(KERN_DEBUG "reg[%02x] = %02x (%02x)\n", i, reg_read(ak4117, i), i < sizeof(ak4117->regmap) ? ak4117->regmap[i] : 0);
-}
-#endif
-
static void snd_ak4117_free(struct ak4117 *chip)
{
- del_timer(&chip->timer);
+ timer_shutdown_sync(&chip->timer);
kfree(chip);
}
@@ -78,7 +53,7 @@ int snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t
struct ak4117 *chip;
int err = 0;
unsigned char reg;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_ak4117_dev_free,
};
@@ -90,9 +65,7 @@ int snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t
chip->read = read;
chip->write = write;
chip->private_data = private_data;
- init_timer(&chip->timer);
- chip->timer.data = (unsigned long)chip;
- chip->timer.function = snd_ak4117_timer;
+ timer_setup(&chip->timer, snd_ak4117_timer, 0);
for (reg = 0; reg < 5; reg++)
chip->regmap[reg] = pgm[reg];
@@ -102,7 +75,8 @@ int snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t
chip->rcs1 = reg_read(chip, AK4117_REG_RCS1);
chip->rcs2 = reg_read(chip, AK4117_REG_RCS2);
- if ((err = snd_device_new(card, SNDRV_DEV_CODEC, chip, &ops)) < 0)
+ err = snd_device_new(card, SNDRV_DEV_CODEC, chip, &ops);
+ if (err < 0)
goto __fail;
if (r_ak4117)
@@ -111,7 +85,7 @@ int snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t
__fail:
snd_ak4117_free(chip);
- return err < 0 ? err : -EIO;
+ return err;
}
void snd_ak4117_reg_write(struct ak4117 *chip, unsigned char reg, unsigned char mask, unsigned char val)
@@ -125,7 +99,7 @@ void snd_ak4117_reinit(struct ak4117 *chip)
{
unsigned char old = chip->regmap[AK4117_REG_PWRDN], reg;
- del_timer(&chip->timer);
+ timer_delete(&chip->timer);
chip->init = 1;
/* bring the chip to reset state and powerdown state */
reg_write(chip, AK4117_REG_PWRDN, 0);
@@ -138,8 +112,7 @@ void snd_ak4117_reinit(struct ak4117 *chip)
/* release powerdown, everything is initialized now */
reg_write(chip, AK4117_REG_PWRDN, old | AK4117_RST | AK4117_PWN);
chip->init = 0;
- chip->timer.expires = 1 + jiffies;
- add_timer(&chip->timer);
+ mod_timer(&chip->timer, 1 + jiffies);
}
static unsigned int external_rate(unsigned char rcs1)
@@ -170,13 +143,11 @@ static int snd_ak4117_in_error_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct ak4117 *chip = snd_kcontrol_chip(kcontrol);
- long *ptr;
- spin_lock_irq(&chip->lock);
- ptr = (long *)(((char *)chip) + kcontrol->private_value);
- ucontrol->value.integer.value[0] = *ptr;
- *ptr = 0;
- spin_unlock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
+ ucontrol->value.integer.value[0] =
+ chip->errors[kcontrol->private_value];
+ chip->errors[kcontrol->private_value] = 0;
return 0;
}
@@ -220,12 +191,11 @@ static int snd_ak4117_rx_put(struct snd_kcontrol *kcontrol,
int change;
u8 old_val;
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
old_val = chip->regmap[AK4117_REG_IO];
change = !!ucontrol->value.integer.value[0] != ((old_val & AK4117_IPS) ? 1 : 0);
if (change)
reg_write(chip, AK4117_REG_IO, (old_val & ~AK4117_IPS) | (ucontrol->value.integer.value[0] ? AK4117_IPS : 0));
- spin_unlock_irq(&chip->lock);
return change;
}
@@ -323,14 +293,14 @@ static int snd_ak4117_spdif_qget(struct snd_kcontrol *kcontrol,
}
/* Don't forget to change AK4117_CONTROLS define!!! */
-static struct snd_kcontrol_new snd_ak4117_iec958_controls[] = {
+static const struct snd_kcontrol_new snd_ak4117_iec958_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "IEC958 Parity Errors",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4117_in_error_info,
.get = snd_ak4117_in_error_get,
- .private_value = offsetof(struct ak4117, parity_errors),
+ .private_value = AK4117_PARITY_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -338,7 +308,7 @@ static struct snd_kcontrol_new snd_ak4117_iec958_controls[] = {
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4117_in_error_info,
.get = snd_ak4117_in_error_get,
- .private_value = offsetof(struct ak4117, v_bit_errors),
+ .private_value = AK4117_V_BIT_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -346,7 +316,7 @@ static struct snd_kcontrol_new snd_ak4117_iec958_controls[] = {
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4117_in_error_info,
.get = snd_ak4117_in_error_get,
- .private_value = offsetof(struct ak4117, ccrc_errors),
+ .private_value = AK4117_CCRC_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -354,7 +324,7 @@ static struct snd_kcontrol_new snd_ak4117_iec958_controls[] = {
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4117_in_error_info,
.get = snd_ak4117_in_error_get,
- .private_value = offsetof(struct ak4117, qcrc_errors),
+ .private_value = AK4117_QCRC_ERRORS,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -379,7 +349,7 @@ static struct snd_kcontrol_new snd_ak4117_iec958_controls[] = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = "IEC958 Preample Capture Default",
+ .name = "IEC958 Preamble Capture Default",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4117_spdif_pinfo,
.get = snd_ak4117_spdif_pget,
@@ -469,24 +439,23 @@ int snd_ak4117_check_rate_and_errors(struct ak4117 *ak4117, unsigned int flags)
goto __rate;
rcs0 = reg_read(ak4117, AK4117_REG_RCS0);
rcs2 = reg_read(ak4117, AK4117_REG_RCS2);
- // printk(KERN_DEBUG "AK IRQ: rcs0 = 0x%x, rcs1 = 0x%x, rcs2 = 0x%x\n", rcs0, rcs1, rcs2);
- spin_lock_irqsave(&ak4117->lock, _flags);
- if (rcs0 & AK4117_PAR)
- ak4117->parity_errors++;
- if (rcs0 & AK4117_V)
- ak4117->v_bit_errors++;
- if (rcs2 & AK4117_CCRC)
- ak4117->ccrc_errors++;
- if (rcs2 & AK4117_QCRC)
- ak4117->qcrc_errors++;
- c0 = (ak4117->rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK)) ^
- (rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK));
- c1 = (ak4117->rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f)) ^
- (rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f));
- ak4117->rcs0 = rcs0 & ~(AK4117_QINT | AK4117_CINT | AK4117_STC);
- ak4117->rcs1 = rcs1;
- ak4117->rcs2 = rcs2;
- spin_unlock_irqrestore(&ak4117->lock, _flags);
+ scoped_guard(spinlock_irqsave, &ak4117->lock) {
+ if (rcs0 & AK4117_PAR)
+ ak4117->errors[AK4117_PARITY_ERRORS]++;
+ if (rcs0 & AK4117_V)
+ ak4117->errors[AK4117_V_BIT_ERRORS]++;
+ if (rcs2 & AK4117_CCRC)
+ ak4117->errors[AK4117_CCRC_ERRORS]++;
+ if (rcs2 & AK4117_QCRC)
+ ak4117->errors[AK4117_QCRC_ERRORS]++;
+ c0 = (ak4117->rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK)) ^
+ (rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK));
+ c1 = (ak4117->rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f)) ^
+ (rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f));
+ ak4117->rcs0 = rcs0 & ~(AK4117_QINT | AK4117_CINT | AK4117_STC);
+ ak4117->rcs1 = rcs1;
+ ak4117->rcs2 = rcs2;
+ }
if (rcs0 & AK4117_PAR)
snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[0]->id);
@@ -522,7 +491,6 @@ int snd_ak4117_check_rate_and_errors(struct ak4117 *ak4117, unsigned int flags)
if (!(flags & AK4117_CHECK_NO_RATE) && runtime && runtime->rate != res) {
snd_pcm_stream_lock_irqsave(ak4117->substream, _flags);
if (snd_pcm_running(ak4117->substream)) {
- // printk(KERN_DEBUG "rate changed (%i <- %i)\n", runtime->rate, res);
snd_pcm_stop(ak4117->substream, SNDRV_PCM_STATE_DRAINING);
wake_up(&runtime->sleep);
res = 1;
@@ -532,15 +500,14 @@ int snd_ak4117_check_rate_and_errors(struct ak4117 *ak4117, unsigned int flags)
return res;
}
-static void snd_ak4117_timer(unsigned long data)
+static void snd_ak4117_timer(struct timer_list *t)
{
- struct ak4117 *chip = (struct ak4117 *)data;
+ struct ak4117 *chip = timer_container_of(chip, t, timer);
if (chip->init)
return;
snd_ak4117_check_rate_and_errors(chip, 0);
- chip->timer.expires = 1 + jiffies;
- add_timer(&chip->timer);
+ mod_timer(&chip->timer, 1 + jiffies);
}
EXPORT_SYMBOL(snd_ak4117_create);
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index 57ccba88700d..b24c80410d45 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -1,30 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for AK4524 / AK4528 / AK4529 / AK4355 / AK4358 / AK4381
* AD and DA converters
*
* Copyright (c) 2000-2004 Jaroslav Kysela <perex@perex.cz>,
* Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/tlv.h>
@@ -404,8 +391,6 @@ static int put_ak_reg(struct snd_kcontrol *kcontrol, int addr,
nval = mask - nval;
if (AK_GET_NEEDSMSB(kcontrol->private_value))
nval |= 0x80;
- /* printk(KERN_DEBUG "DEBUG - AK writing reg: chip %x addr %x,
- nval %x\n", chip, addr, nval); */
snd_akm4xxx_write(ak, chip, addr, nval);
return 1;
}
@@ -464,17 +449,10 @@ static int snd_akm4xxx_stereo_volume_put(struct snd_kcontrol *kcontrol,
static int snd_akm4xxx_deemphasis_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = {
+ static const char * const texts[4] = {
"44.1kHz", "Off", "48kHz", "32kHz",
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item >= 4)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
}
static int snd_akm4xxx_deemphasis_get(struct snd_kcontrol *kcontrol,
@@ -569,22 +547,13 @@ static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol,
{
struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
int mixer_ch = AK_GET_SHIFT(kcontrol->private_value);
- const char **input_names;
- int num_names, idx;
+ unsigned int num_names;
num_names = ak4xxx_capture_num_inputs(ak, mixer_ch);
if (!num_names)
return -EINVAL;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = num_names;
- idx = uinfo->value.enumerated.item;
- if (idx >= num_names)
- return -EINVAL;
- input_names = ak->adc_info[mixer_ch].input_names;
- strncpy(uinfo->value.enumerated.name, input_names[idx],
- sizeof(uinfo->value.enumerated.name));
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, num_names,
+ ak->adc_info[mixer_ch].input_names);
}
static int ak4xxx_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -804,11 +773,12 @@ static int build_adc_controls(struct snd_akm4xxx *ak)
return err;
memset(&knew, 0, sizeof(knew));
- knew.name = ak->adc_info[mixer_ch].selector_name;
- if (!knew.name) {
+ if (!ak->adc_info ||
+ !ak->adc_info[mixer_ch].selector_name) {
knew.name = "Capture Channel";
knew.index = mixer_ch + ak->idx_offset * 2;
- }
+ } else
+ knew.name = ak->adc_info[mixer_ch].selector_name;
knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
knew.info = ak4xxx_capture_source_info;
@@ -874,7 +844,6 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
return 0;
}
-#ifdef CONFIG_PROC_FS
static void proc_regs_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -891,17 +860,8 @@ static void proc_regs_read(struct snd_info_entry *entry,
static int proc_init(struct snd_akm4xxx *ak)
{
- struct snd_info_entry *entry;
- int err;
- err = snd_card_proc_new(ak->card, ak->name, &entry);
- if (err < 0)
- return err;
- snd_info_set_text_ops(entry, ak, proc_regs_read);
- return 0;
+ return snd_card_ro_proc_new(ak->card, ak->name, ak, proc_regs_read);
}
-#else /* !CONFIG_PROC_FS */
-static int proc_init(struct snd_akm4xxx *ak) { return 0; }
-#endif
int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
{
@@ -930,15 +890,3 @@ int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
return 0;
}
EXPORT_SYMBOL(snd_akm4xxx_build_controls);
-
-static int __init alsa_akm4xxx_module_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_akm4xxx_module_exit(void)
-{
-}
-
-module_init(alsa_akm4xxx_module_init)
-module_exit(alsa_akm4xxx_module_exit)
diff --git a/sound/i2c/other/pt2258.c b/sound/i2c/other/pt2258.c
index 797d3a6687eb..0fbac827124b 100644
--- a/sound/i2c/other/pt2258.c
+++ b/sound/i2c/other/pt2258.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA Driver for the PT2258 volume controller.
*
* Copyright (c) 2006 Jochen Voss <voss@seehuhn.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <sound/core.h>
@@ -24,6 +10,7 @@
#include <sound/tlv.h>
#include <sound/i2c.h>
#include <sound/pt2258.h>
+#include <linux/module.h>
MODULE_AUTHOR("Jochen Voss <voss@seehuhn.de>");
MODULE_DESCRIPTION("PT2258 volume controller (Princeton Technology Corp.)");
@@ -76,7 +63,7 @@ int snd_pt2258_reset(struct snd_pt2258 *pt)
__error:
snd_i2c_unlock(pt->i2c_bus);
- snd_printk(KERN_ERR "PT2258 reset failed\n");
+ dev_err(pt->card->dev, "PT2258 reset failed\n");
return -EIO;
}
@@ -93,7 +80,7 @@ static int pt2258_stereo_volume_info(struct snd_kcontrol *kcontrol,
static int pt2258_stereo_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_pt2258 *pt = kcontrol->private_data;
+ struct snd_pt2258 *pt = snd_kcontrol_chip(kcontrol);
int base = kcontrol->private_value;
/* chip does not support register reads */
@@ -105,7 +92,7 @@ static int pt2258_stereo_volume_get(struct snd_kcontrol *kcontrol,
static int pt2258_stereo_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_pt2258 *pt = kcontrol->private_data;
+ struct snd_pt2258 *pt = snd_kcontrol_chip(kcontrol);
int base = kcontrol->private_value;
unsigned char bytes[2];
int val0, val1;
@@ -137,7 +124,7 @@ static int pt2258_stereo_volume_put(struct snd_kcontrol *kcontrol,
__error:
snd_i2c_unlock(pt->i2c_bus);
- snd_printk(KERN_ERR "PT2258 access failed\n");
+ dev_err(pt->card->dev, "PT2258 access failed\n");
return -EIO;
}
@@ -146,7 +133,7 @@ static int pt2258_stereo_volume_put(struct snd_kcontrol *kcontrol,
static int pt2258_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_pt2258 *pt = kcontrol->private_data;
+ struct snd_pt2258 *pt = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = !pt->mute;
return 0;
@@ -155,7 +142,7 @@ static int pt2258_switch_get(struct snd_kcontrol *kcontrol,
static int pt2258_switch_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_pt2258 *pt = kcontrol->private_data;
+ struct snd_pt2258 *pt = snd_kcontrol_chip(kcontrol);
unsigned char bytes[2];
int val;
@@ -174,7 +161,7 @@ static int pt2258_switch_put(struct snd_kcontrol *kcontrol,
__error:
snd_i2c_unlock(pt->i2c_bus);
- snd_printk(KERN_ERR "PT2258 access failed 2\n");
+ dev_err(pt->card->dev, "PT2258 access failed 2\n");
return -EIO;
}
diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c
deleted file mode 100644
index ee538f1ae846..000000000000
--- a/sound/i2c/other/tea575x-tuner.c
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips
- *
- * Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <asm/io.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/version.h>
-#include <sound/core.h>
-#include <sound/tea575x-tuner.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
-MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips");
-MODULE_LICENSE("GPL");
-
-static int radio_nr = -1;
-module_param(radio_nr, int, 0);
-
-#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
-#define FREQ_LO (87 * 16000)
-#define FREQ_HI (108 * 16000)
-
-/*
- * definitions
- */
-
-#define TEA575X_BIT_SEARCH (1<<24) /* 1 = search action, 0 = tuned */
-#define TEA575X_BIT_UPDOWN (1<<23) /* 0 = search down, 1 = search up */
-#define TEA575X_BIT_MONO (1<<22) /* 0 = stereo, 1 = mono */
-#define TEA575X_BIT_BAND_MASK (3<<20)
-#define TEA575X_BIT_BAND_FM (0<<20)
-#define TEA575X_BIT_BAND_MW (1<<20)
-#define TEA575X_BIT_BAND_LW (1<<21)
-#define TEA575X_BIT_BAND_SW (1<<22)
-#define TEA575X_BIT_PORT_0 (1<<19) /* user bit */
-#define TEA575X_BIT_PORT_1 (1<<18) /* user bit */
-#define TEA575X_BIT_SEARCH_MASK (3<<16) /* search level */
-#define TEA575X_BIT_SEARCH_5_28 (0<<16) /* FM >5uV, AM >28uV */
-#define TEA575X_BIT_SEARCH_10_40 (1<<16) /* FM >10uV, AM > 40uV */
-#define TEA575X_BIT_SEARCH_30_63 (2<<16) /* FM >30uV, AM > 63uV */
-#define TEA575X_BIT_SEARCH_150_1000 (3<<16) /* FM > 150uV, AM > 1000uV */
-#define TEA575X_BIT_DUMMY (1<<15) /* buffer */
-#define TEA575X_BIT_FREQ_MASK 0x7fff
-
-static struct v4l2_queryctrl radio_qctrl[] = {
- {
- .id = V4L2_CID_AUDIO_MUTE,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .default_value = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- }
-};
-
-/*
- * lowlevel part
- */
-
-static void snd_tea575x_set_freq(struct snd_tea575x *tea)
-{
- unsigned long freq;
-
- freq = tea->freq / 16; /* to kHz */
- if (freq > 108000)
- freq = 108000;
- if (freq < 87000)
- freq = 87000;
- /* crystal fixup */
- if (tea->tea5759)
- freq -= tea->freq_fixup;
- else
- freq += tea->freq_fixup;
- /* freq /= 12.5 */
- freq *= 10;
- freq /= 125;
-
- tea->val &= ~TEA575X_BIT_FREQ_MASK;
- tea->val |= freq & TEA575X_BIT_FREQ_MASK;
- tea->ops->write(tea, tea->val);
-}
-
-/*
- * Linux Video interface
- */
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- struct snd_tea575x *tea = video_drvdata(file);
-
- strcpy(v->card, tea->tea5759 ? "TEA5759" : "TEA5757");
- strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver));
- strlcpy(v->card, "Maestro Radio", sizeof(v->card));
- sprintf(v->bus_info, "PCI");
- v->version = RADIO_VERSION;
- v->capabilities = V4L2_CAP_TUNER;
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- if (v->index > 0)
- return -EINVAL;
-
- strcpy(v->name, "FM");
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = FREQ_LO;
- v->rangehigh = FREQ_HI;
- v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
- v->capability = V4L2_TUNER_CAP_LOW;
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xffff;
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- if (v->index > 0)
- return -EINVAL;
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct snd_tea575x *tea = video_drvdata(file);
-
- f->type = V4L2_TUNER_RADIO;
- f->frequency = tea->freq;
- return 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct snd_tea575x *tea = video_drvdata(file);
-
- if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
- return -EINVAL;
-
- tea->freq = f->frequency;
-
- snd_tea575x_set_freq(tea);
-
- return 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- if (a->index > 1)
- return -EINVAL;
-
- strcpy(a->name, "Radio");
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- if (a->index != 0)
- return -EINVAL;
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
- if (qc->id && qc->id == radio_qctrl[i].id) {
- memcpy(qc, &(radio_qctrl[i]),
- sizeof(*qc));
- return 0;
- }
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct snd_tea575x *tea = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (tea->ops->mute) {
- ctrl->value = tea->mute;
- return 0;
- }
- }
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct snd_tea575x *tea = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (tea->ops->mute) {
- tea->ops->mute(tea, ctrl->value);
- tea->mute = ctrl->value;
- return 0;
- }
- }
- return -EINVAL;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- if (i != 0)
- return -EINVAL;
- return 0;
-}
-
-static int snd_tea575x_exclusive_open(struct file *file)
-{
- struct snd_tea575x *tea = video_drvdata(file);
-
- return test_and_set_bit(0, &tea->in_use) ? -EBUSY : 0;
-}
-
-static int snd_tea575x_exclusive_release(struct file *file)
-{
- struct snd_tea575x *tea = video_drvdata(file);
-
- clear_bit(0, &tea->in_use);
- return 0;
-}
-
-static const struct v4l2_file_operations tea575x_fops = {
- .owner = THIS_MODULE,
- .open = snd_tea575x_exclusive_open,
- .release = snd_tea575x_exclusive_release,
- .ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
-};
-
-static struct video_device tea575x_radio = {
- .name = "tea575x-tuner",
- .fops = &tea575x_fops,
- .ioctl_ops = &tea575x_ioctl_ops,
- .release = video_device_release,
-};
-
-/*
- * initialize all the tea575x chips
- */
-void snd_tea575x_init(struct snd_tea575x *tea)
-{
- int retval;
- unsigned int val;
- struct video_device *tea575x_radio_inst;
-
- val = tea->ops->read(tea);
- if (val == 0x1ffffff || val == 0) {
- snd_printk(KERN_ERR
- "tea575x-tuner: Cannot find TEA575x chip\n");
- return;
- }
-
- tea->in_use = 0;
- tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
- tea->freq = 90500 * 16; /* 90.5Mhz default */
-
- tea575x_radio_inst = video_device_alloc();
- if (tea575x_radio_inst == NULL) {
- printk(KERN_ERR "tea575x-tuner: not enough memory\n");
- return;
- }
-
- memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
-
- strcpy(tea575x_radio.name, tea->tea5759 ?
- "TEA5759 radio" : "TEA5757 radio");
-
- video_set_drvdata(tea575x_radio_inst, tea);
-
- retval = video_register_device(tea575x_radio_inst,
- VFL_TYPE_RADIO, radio_nr);
- if (retval) {
- printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
- kfree(tea575x_radio_inst);
- return;
- }
-
- snd_tea575x_set_freq(tea);
-
- /* mute on init */
- if (tea->ops->mute) {
- tea->ops->mute(tea, 1);
- tea->mute = 1;
- }
- tea->vd = tea575x_radio_inst;
-}
-
-void snd_tea575x_exit(struct snd_tea575x *tea)
-{
- if (tea->vd) {
- video_unregister_device(tea->vd);
- tea->vd = NULL;
- }
-}
-
-static int __init alsa_tea575x_module_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_tea575x_module_exit(void)
-{
-}
-
-module_init(alsa_tea575x_module_init)
-module_exit(alsa_tea575x_module_exit)
-
-EXPORT_SYMBOL(snd_tea575x_init);
-EXPORT_SYMBOL(snd_tea575x_exit);
diff --git a/sound/i2c/tea6330t.c b/sound/i2c/tea6330t.c
index 0e3a9f2c5297..676d58054944 100644
--- a/sound/i2c/tea6330t.c
+++ b/sound/i2c/tea6330t.c
@@ -1,27 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for control of the TEA6330T circuit via i2c bus
* Sound fader control circuit for car radios by Philips Semiconductors
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/tea6330t.h>
@@ -70,9 +56,6 @@ int snd_tea6330t_detect(struct snd_i2c_bus *bus, int equalizer)
static void snd_tea6330t_set(struct tea6330t *tea,
unsigned char addr, unsigned char value)
{
-#if 0
- printk(KERN_DEBUG "set - 0x%x/0x%x\n", addr, value);
-#endif
snd_i2c_write(tea->bus, TEA6330T_ADDR, addr, value, 1);
}
#endif
@@ -129,7 +112,8 @@ static int snd_tea6330t_put_master_volume(struct snd_kcontrol *kcontrol,
bytes[count++] = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT] = tea->mright;
}
if (count > 0) {
- if ((err = snd_i2c_sendbytes(tea->device, bytes, count)) < 0)
+ err = snd_i2c_sendbytes(tea->device, bytes, count);
+ if (err < 0)
change = err;
}
snd_i2c_unlock(tea->bus);
@@ -174,7 +158,8 @@ static int snd_tea6330t_put_master_switch(struct snd_kcontrol *kcontrol,
bytes[0] = TEA6330T_SADDR_VOLUME_LEFT;
bytes[1] = tea->regs[TEA6330T_SADDR_VOLUME_LEFT];
bytes[2] = tea->regs[TEA6330T_SADDR_VOLUME_RIGHT];
- if ((err = snd_i2c_sendbytes(tea->device, bytes, 3)) < 0)
+ err = snd_i2c_sendbytes(tea->device, bytes, 3);
+ if (err < 0)
change = err;
snd_i2c_unlock(tea->bus);
return change;
@@ -221,7 +206,8 @@ static int snd_tea6330t_put_bass(struct snd_kcontrol *kcontrol,
change = tea->regs[TEA6330T_SADDR_BASS] != val1;
bytes[0] = TEA6330T_SADDR_BASS;
bytes[1] = tea->regs[TEA6330T_SADDR_BASS] = val1;
- if ((err = snd_i2c_sendbytes(tea->device, bytes, 2)) < 0)
+ err = snd_i2c_sendbytes(tea->device, bytes, 2);
+ if (err < 0)
change = err;
snd_i2c_unlock(tea->bus);
return change;
@@ -268,13 +254,14 @@ static int snd_tea6330t_put_treble(struct snd_kcontrol *kcontrol,
change = tea->regs[TEA6330T_SADDR_TREBLE] != val1;
bytes[0] = TEA6330T_SADDR_TREBLE;
bytes[1] = tea->regs[TEA6330T_SADDR_TREBLE] = val1;
- if ((err = snd_i2c_sendbytes(tea->device, bytes, 2)) < 0)
+ err = snd_i2c_sendbytes(tea->device, bytes, 2);
+ if (err < 0)
change = err;
snd_i2c_unlock(tea->bus);
return change;
}
-static struct snd_kcontrol_new snd_tea6330t_controls[] = {
+static const struct snd_kcontrol_new snd_tea6330t_controls[] = {
TEA6330T_MASTER_SWITCH("Master Playback Switch", 0),
TEA6330T_MASTER_VOLUME("Master Playback Volume", 0),
TEA6330T_BASS("Tone Control - Bass", 0),
@@ -292,16 +279,17 @@ int snd_tea6330t_update_mixer(struct snd_card *card,
{
struct snd_i2c_device *device;
struct tea6330t *tea;
- struct snd_kcontrol_new *knew;
+ const struct snd_kcontrol_new *knew;
unsigned int idx;
- int err = -ENOMEM;
+ int err;
u8 default_treble, default_bass;
unsigned char bytes[7];
tea = kzalloc(sizeof(*tea), GFP_KERNEL);
if (tea == NULL)
return -ENOMEM;
- if ((err = snd_i2c_device_create(bus, "TEA6330T", TEA6330T_ADDR, &device)) < 0) {
+ err = snd_i2c_device_create(bus, "TEA6330T", TEA6330T_ADDR, &device);
+ if (err < 0) {
kfree(tea);
return err;
}
@@ -341,18 +329,21 @@ int snd_tea6330t_update_mixer(struct snd_card *card,
bytes[0] = TEA6330T_SADDR_VOLUME_LEFT;
for (idx = 0; idx < 6; idx++)
bytes[idx+1] = tea->regs[idx];
- if ((err = snd_i2c_sendbytes(device, bytes, 7)) < 0)
+ err = snd_i2c_sendbytes(device, bytes, 7);
+ if (err < 0)
goto __error;
strcat(card->mixername, ",TEA6330T");
- if ((err = snd_component_add(card, "TEA6330T")) < 0)
+ err = snd_component_add(card, "TEA6330T");
+ if (err < 0)
goto __error;
for (idx = 0; idx < ARRAY_SIZE(snd_tea6330t_controls); idx++) {
knew = &snd_tea6330t_controls[idx];
if (tea->treble == 0 && !strcmp(knew->name, "Tone Control - Treble"))
continue;
- if ((err = snd_ctl_add(card, snd_ctl_new1(knew, tea))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(knew, tea));
+ if (err < 0)
goto __error;
}
@@ -367,19 +358,3 @@ int snd_tea6330t_update_mixer(struct snd_card *card,
EXPORT_SYMBOL(snd_tea6330t_detect);
EXPORT_SYMBOL(snd_tea6330t_update_mixer);
-
-/*
- * INIT part
- */
-
-static int __init alsa_tea6330t_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_tea6330t_exit(void)
-{
-}
-
-module_init(alsa_tea6330t_init)
-module_exit(alsa_tea6330t_exit)
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index 52064cfa91f3..f8159179e38d 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -1,25 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-only
# ALSA ISA drivers
config SND_WSS_LIB
- tristate
- select SND_PCM
+ tristate
+ select SND_PCM
+ select SND_TIMER
config SND_SB_COMMON
- tristate
+ tristate
config SND_SB8_DSP
- tristate
- select SND_PCM
- select SND_SB_COMMON
+ tristate
+ select SND_PCM
+ select SND_SB_COMMON
config SND_SB16_DSP
- tristate
- select SND_PCM
- select SND_SB_COMMON
+ tristate
+ select SND_PCM
+ select SND_SB_COMMON
menuconfig SND_ISA
bool "ISA sound devices"
- depends on ISA && ISA_DMA_API
+ depends on ISA || COMPILE_TEST
+ depends on ISA_DMA_API
+ depends on HAS_IOPORT
default y
help
Support for sound devices connected via the ISA bus.
@@ -42,6 +46,7 @@ config SND_AD1816A
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_PCM
+ select SND_TIMER
help
Say Y here to include support for Analog Devices SoundPort
AD1816A or compatible sound chips.
@@ -117,6 +122,18 @@ config SND_AZT2320
To compile this driver as a module, choose M here: the module
will be called snd-azt2320.
+config SND_CMI8328
+ tristate "C-Media CMI8328"
+ select SND_WSS_LIB
+ select SND_OPL3_LIB
+ select SND_MPU401_UART
+ help
+ Say Y here to include support for soundcards based on the
+ C-Media CMI8328 chip.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-cmi8328.
+
config SND_CMI8330
tristate "C-Media CMI8330"
select SND_WSS_LIB
@@ -179,7 +196,7 @@ config SND_ES18XX
config SND_SC6000
tristate "Gallant SC-6000/6600/7000 and Audio Excel DSP 16"
- depends on HAS_IOPORT
+ depends on HAS_IOPORT_MAP
select SND_WSS_LIB
select SND_OPL3_LIB
select SND_MPU401_UART
@@ -197,6 +214,7 @@ config SND_GUSCLASSIC
tristate "Gravis UltraSound Classic"
select SND_RAWMIDI
select SND_PCM
+ select SND_TIMER
help
Say Y here to include support for Gravis UltraSound Classic
soundcards.
@@ -209,6 +227,7 @@ config SND_GUSEXTREME
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_PCM
+ select SND_TIMER
help
Say Y here to include support for Gravis UltraSound Extreme
soundcards.
@@ -361,6 +380,7 @@ config SND_SBAWE
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_SB16_DSP
+ select SND_SEQ_DEVICE if SND_SEQUENCER != n
help
Say Y here to include support for Sound Blaster AWE soundcards
(including the Plug and Play version).
@@ -368,6 +388,13 @@ config SND_SBAWE
To compile this driver as a module, choose M here: the module
will be called snd-sbawe.
+# select SEQ stuff to min(SND_SEQUENCER,SND_XXX)
+config SND_SBAWE_SEQ
+ def_tristate SND_SEQUENCER && SND_SBAWE
+ select SND_SEQ_MIDI_EMUL
+ select SND_SEQ_VIRMIDI
+ select SND_SYNTH_EMUX
+
config SND_SB16_CSP
bool "Sound Blaster 16/AWE CSP support"
depends on (SND_SB16 || SND_SBAWE) && (BROKEN || !PPC)
@@ -413,7 +440,7 @@ config SND_WAVEFRONT
config SND_MSND_PINNACLE
tristate "Turtle Beach MultiSound Pinnacle/Fiji driver"
- depends on X86 && EXPERIMENTAL
+ depends on X86
select FW_LOADER
select SND_MPU401_UART
select SND_PCM
@@ -426,7 +453,7 @@ config SND_MSND_PINNACLE
config SND_MSND_CLASSIC
tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey"
- depends on X86 && EXPERIMENTAL
+ depends on X86
select FW_LOADER
select SND_MPU401_UART
select SND_PCM
@@ -434,7 +461,7 @@ config SND_MSND_CLASSIC
Say M here if you have a Turtle Beach MultiSound Classic, Tahiti or
Monterey (not for the Pinnacle or Fiji).
- See <file:Documentation/sound/oss/MultiSound> for important information
+ See <file:Documentation/sound/cards/multisound.sh> for important information
about this driver. Note that it has been discontinued, but the
Voyetra Turtle Beach knowledge base entry for it is still available
at <http://www.turtlebeach.com/site/kb_ftp/790.asp>.
diff --git a/sound/isa/Makefile b/sound/isa/Makefile
index 8d781e419e2e..2135d68a15ac 100644
--- a/sound/isa/Makefile
+++ b/sound/isa/Makefile
@@ -1,21 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-adlib-objs := adlib.o
-snd-als100-objs := als100.o
-snd-azt2320-objs := azt2320.o
-snd-cmi8330-objs := cmi8330.o
-snd-es18xx-objs := es18xx.o
-snd-opl3sa2-objs := opl3sa2.o
-snd-sc6000-objs := sc6000.o
-snd-sscape-objs := sscape.o
+snd-adlib-y := adlib.o
+snd-als100-y := als100.o
+snd-azt2320-y := azt2320.o
+snd-cmi8328-y := cmi8328.o
+snd-cmi8330-y := cmi8330.o
+snd-es18xx-y := es18xx.o
+snd-opl3sa2-y := opl3sa2.o
+snd-sc6000-y := sc6000.o
+snd-sscape-y := sscape.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ADLIB) += snd-adlib.o
obj-$(CONFIG_SND_ALS100) += snd-als100.o
obj-$(CONFIG_SND_AZT2320) += snd-azt2320.o
+obj-$(CONFIG_SND_CMI8328) += snd-cmi8328.o
obj-$(CONFIG_SND_CMI8330) += snd-cmi8330.o
obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o
obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o
diff --git a/sound/isa/ad1816a/Makefile b/sound/isa/ad1816a/Makefile
index 487ab23860e3..573325228534 100644
--- a/sound/isa/ad1816a/Makefile
+++ b/sound/isa/ad1816a/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ad1816a-objs := ad1816a.o ad1816a_lib.o
+snd-ad1816a-y := ad1816a.o ad1816a_lib.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AD1816A) += snd-ad1816a.o
diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c
index 3cb75bc97699..8e84d4091f1e 100644
--- a/sound/isa/ad1816a/ad1816a.c
+++ b/sound/isa/ad1816a/ad1816a.c
@@ -1,50 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
card-ad1816a.c - driver for ADI SoundPort AD1816A based soundcards.
Copyright (C) 2000 by Massimo Piccioni <dafastidio@libero.it>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/pnp.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/ad1816a.h>
#include <sound/mpu401.h>
#include <sound/opl3.h>
-#define PFX "ad1816a: "
-
MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
MODULE_DESCRIPTION("AD1816A, AD1815");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Highscreen,Sound-Boostar 16 3D},"
- "{Analog Devices,AD1815},"
- "{Analog Devices,AD1816A},"
- "{TerraTec,Base 64},"
- "{TerraTec,AudioSystem EWS64S},"
- "{Aztech/Newcom SC-16 3D},"
- "{Shark Predator ISA}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 1-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
@@ -63,15 +42,10 @@ MODULE_PARM_DESC(enable, "Enable ad1816a based soundcard.");
module_param_array(clockfreq, int, NULL, 0444);
MODULE_PARM_DESC(clockfreq, "Clock frequency for ad1816a driver (default = 0).");
-struct snd_card_ad1816a {
- struct pnp_dev *dev;
- struct pnp_dev *devmpu;
-};
-
-static struct pnp_card_device_id snd_ad1816a_pnpids[] = {
+static const struct pnp_card_device_id snd_ad1816a_pnpids[] = {
/* Analog Devices AD1815 */
{ .id = "ADS7150", .devs = { { .id = "ADS7150" }, { .id = "ADS7151" } } },
- /* Analog Device AD1816? */
+ /* Analog Devices AD1816? */
{ .id = "ADS7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
/* Analog Devices AD1816A - added by Kenneth Platz <kxp@atl.hp.com> */
{ .id = "ADS7181", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
@@ -99,28 +73,19 @@ MODULE_DEVICE_TABLE(pnp_card, snd_ad1816a_pnpids);
#define DRIVER_NAME "snd-card-ad1816a"
-static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acard,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_card_ad1816a_pnp(int dev, struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
struct pnp_dev *pdev;
int err;
- acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
- if (acard->dev == NULL)
+ pdev = pnp_request_card_device(card, id->devs[0].id, NULL);
+ if (pdev == NULL)
return -EBUSY;
- acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL);
- if (acard->devmpu == NULL) {
- mpu_port[dev] = -1;
- snd_printk(KERN_WARNING PFX "MPU401 device busy, skipping.\n");
- }
-
- pdev = acard->dev;
-
err = pnp_activate_dev(pdev);
if (err < 0) {
- printk(KERN_ERR PFX "AUDIO PnP configure failure\n");
+ dev_err(&pdev->dev, "AUDIO PnP configure failure\n");
return -EBUSY;
}
@@ -130,16 +95,17 @@ static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acar
dma2[dev] = pnp_dma(pdev, 1);
irq[dev] = pnp_irq(pdev, 0);
- if (acard->devmpu == NULL)
+ pdev = pnp_request_card_device(card, id->devs[1].id, NULL);
+ if (pdev == NULL) {
+ mpu_port[dev] = -1;
+ pr_warn("MPU401 device busy, skipping.\n");
return 0;
-
- pdev = acard->devmpu;
+ }
err = pnp_activate_dev(pdev);
if (err < 0) {
- printk(KERN_ERR PFX "MPU401 PnP configure failure\n");
+ dev_err(&pdev->dev, "MPU401 PnP configure failure\n");
mpu_port[dev] = -1;
- acard->devmpu = NULL;
} else {
mpu_port[dev] = pnp_port_start(pdev, 0);
mpu_irq[dev] = pnp_irq(pdev, 0);
@@ -148,93 +114,84 @@ static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acar
return 0;
}
-static int __devinit snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
int error;
struct snd_card *card;
- struct snd_card_ad1816a *acard;
struct snd_ad1816a *chip;
struct snd_opl3 *opl3;
- struct snd_timer *timer;
- error = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_ad1816a), &card);
+ error = snd_devm_card_new(&pcard->card->dev,
+ index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_ad1816a), &card);
if (error < 0)
return error;
- acard = card->private_data;
+ chip = card->private_data;
- if ((error = snd_card_ad1816a_pnp(dev, acard, pcard, pid))) {
- snd_card_free(card);
+ error = snd_card_ad1816a_pnp(dev, pcard, pid);
+ if (error)
return error;
- }
- snd_card_set_dev(card, &pcard->card->dev);
-
- if ((error = snd_ad1816a_create(card, port[dev],
- irq[dev],
- dma1[dev],
- dma2[dev],
- &chip)) < 0) {
- snd_card_free(card);
+
+ error = snd_ad1816a_create(card, port[dev],
+ irq[dev],
+ dma1[dev],
+ dma2[dev],
+ chip);
+ if (error)
return error;
- }
if (clockfreq[dev] >= 5000 && clockfreq[dev] <= 100000)
chip->clock_freq = clockfreq[dev];
- strcpy(card->driver, "AD1816A");
- strcpy(card->shortname, "ADI SoundPort AD1816A");
+ strscpy(card->driver, "AD1816A");
+ strscpy(card->shortname, "ADI SoundPort AD1816A");
sprintf(card->longname, "%s, SS at 0x%lx, irq %d, dma %d&%d",
card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
- if ((error = snd_ad1816a_pcm(chip, 0, NULL)) < 0) {
- snd_card_free(card);
+ error = snd_ad1816a_pcm(chip, 0);
+ if (error < 0)
return error;
- }
- if ((error = snd_ad1816a_mixer(chip)) < 0) {
- snd_card_free(card);
+ error = snd_ad1816a_mixer(chip);
+ if (error < 0)
return error;
- }
- error = snd_ad1816a_timer(chip, 0, &timer);
- if (error < 0) {
- snd_card_free(card);
+ error = snd_ad1816a_timer(chip, 0);
+ if (error < 0)
return error;
- }
if (mpu_port[dev] > 0) {
if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- mpu_port[dev], 0, mpu_irq[dev], IRQF_DISABLED,
+ mpu_port[dev], 0, mpu_irq[dev],
NULL) < 0)
- printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n", mpu_port[dev]);
+ dev_err(card->dev, "no MPU-401 device at 0x%lx.\n",
+ mpu_port[dev]);
}
if (fm_port[dev] > 0) {
if (snd_opl3_create(card,
fm_port[dev], fm_port[dev] + 2,
OPL3_HW_AUTO, 0, &opl3) < 0) {
- printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx.\n", fm_port[dev], fm_port[dev] + 2);
+ dev_err(card->dev, "no OPL device at 0x%lx-0x%lx.\n",
+ fm_port[dev], fm_port[dev] + 2);
} else {
error = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
- if (error < 0) {
- snd_card_free(card);
+ if (error < 0)
return error;
- }
}
}
- if ((error = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ error = snd_card_register(card);
+ if (error < 0)
return error;
- }
pnp_set_card_drvdata(pcard, card);
return 0;
}
-static unsigned int __devinitdata ad1816a_devices;
+static unsigned int ad1816a_devices;
-static int __devinit snd_ad1816a_pnp_detect(struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_ad1816a_pnp_detect(struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
static int dev;
int res;
@@ -252,19 +209,36 @@ static int __devinit snd_ad1816a_pnp_detect(struct pnp_card_link *card,
return -ENODEV;
}
-static void __devexit snd_ad1816a_pnp_remove(struct pnp_card_link * pcard)
+#ifdef CONFIG_PM
+static int snd_ad1816a_pnp_suspend(struct pnp_card_link *pcard,
+ pm_message_t state)
{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
+ struct snd_card *card = pnp_get_card_drvdata(pcard);
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ snd_ad1816a_suspend(card->private_data);
+ return 0;
+}
+
+static int snd_ad1816a_pnp_resume(struct pnp_card_link *pcard)
+{
+ struct snd_card *card = pnp_get_card_drvdata(pcard);
+
+ snd_ad1816a_resume(card->private_data);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
}
+#endif
static struct pnp_card_driver ad1816a_pnpc_driver = {
.flags = PNP_DRIVER_RES_DISABLE,
.name = "ad1816a",
.id_table = snd_ad1816a_pnpids,
.probe = snd_ad1816a_pnp_detect,
- .remove = __devexit_p(snd_ad1816a_pnp_remove),
- /* FIXME: suspend/resume */
+#ifdef CONFIG_PM
+ .suspend = snd_ad1816a_pnp_suspend,
+ .resume = snd_ad1816a_pnp_resume,
+#endif
};
static int __init alsa_card_ad1816a_init(void)
@@ -278,7 +252,7 @@ static int __init alsa_card_ad1816a_init(void)
if (!ad1816a_devices) {
pnp_unregister_card_driver(&ad1816a_pnpc_driver);
#ifdef MODULE
- printk(KERN_ERR "no AD1816A based soundcards found.\n");
+ pr_err("no AD1816A based soundcards found.\n");
#endif /* MODULE */
return -ENODEV;
}
diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c
index 05aef8b97e96..50f62304de61 100644
--- a/sound/isa/ad1816a/ad1816a_lib.c
+++ b/sound/isa/ad1816a/ad1816a_lib.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
ad1816a.c - lowlevel code for Analog Devices AD1816A chip.
Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/delay.h>
@@ -22,11 +10,11 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/ioport.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/tlv.h>
#include <sound/ad1816a.h>
-#include <asm/io.h>
#include <asm/dma.h>
static inline int snd_ad1816a_busy_wait(struct snd_ad1816a *chip)
@@ -37,7 +25,7 @@ static inline int snd_ad1816a_busy_wait(struct snd_ad1816a *chip)
if (inb(AD1816A_REG(AD1816A_CHIP_STATUS)) & AD1816A_READY)
return 0;
- snd_printk(KERN_WARNING "chip busy.\n");
+ dev_warn(chip->card->dev, "chip busy.\n");
return -EBUSY;
}
@@ -85,7 +73,8 @@ static void snd_ad1816a_write_mask(struct snd_ad1816a *chip, unsigned char reg,
static unsigned char snd_ad1816a_get_format(struct snd_ad1816a *chip,
- unsigned int format, int channels)
+ snd_pcm_format_t format,
+ int channels)
{
unsigned char retval = AD1816A_FMT_LINEAR_8;
@@ -107,14 +96,10 @@ static unsigned char snd_ad1816a_get_format(struct snd_ad1816a *chip,
static int snd_ad1816a_open(struct snd_ad1816a *chip, unsigned int mode)
{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
- if (chip->mode & mode) {
- spin_unlock_irqrestore(&chip->lock, flags);
+ if (chip->mode & mode)
return -EAGAIN;
- }
switch ((mode &= AD1816A_MODE_OPEN)) {
case AD1816A_MODE_PLAYBACK:
@@ -137,15 +122,12 @@ static int snd_ad1816a_open(struct snd_ad1816a *chip, unsigned int mode)
}
chip->mode |= mode;
- spin_unlock_irqrestore(&chip->lock, flags);
return 0;
}
static void snd_ad1816a_close(struct snd_ad1816a *chip, unsigned int mode)
{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
switch ((mode &= AD1816A_MODE_OPEN)) {
case AD1816A_MODE_PLAYBACK:
@@ -166,10 +148,9 @@ static void snd_ad1816a_close(struct snd_ad1816a *chip, unsigned int mode)
snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
AD1816A_TIMER_IRQ_ENABLE, 0x0000);
}
- if (!((chip->mode &= ~mode) & AD1816A_MODE_OPEN))
+ chip->mode &= ~mode;
+ if (!(chip->mode & AD1816A_MODE_OPEN))
chip->mode = 0;
-
- spin_unlock_irqrestore(&chip->lock, flags);
}
@@ -181,22 +162,22 @@ static int snd_ad1816a_trigger(struct snd_ad1816a *chip, unsigned char what,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_STOP:
- spin_lock(&chip->lock);
- cmd = (cmd == SNDRV_PCM_TRIGGER_START) ? 0xff: 0x00;
- /* if (what & AD1816A_PLAYBACK_ENABLE) */
- /* That is not valid, because playback and capture enable
- * are the same bit pattern, just to different addresses
- */
- if (! iscapture)
- snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,
- AD1816A_PLAYBACK_ENABLE, cmd);
- else
- snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG,
- AD1816A_CAPTURE_ENABLE, cmd);
- spin_unlock(&chip->lock);
+ scoped_guard(spinlock, &chip->lock) {
+ cmd = (cmd == SNDRV_PCM_TRIGGER_START) ? 0xff: 0x00;
+ /* if (what & AD1816A_PLAYBACK_ENABLE) */
+ /* That is not valid, because playback and capture enable
+ * are the same bit pattern, just to different addresses
+ */
+ if (!iscapture)
+ snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,
+ AD1816A_PLAYBACK_ENABLE, cmd);
+ else
+ snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG,
+ AD1816A_CAPTURE_ENABLE, cmd);
+ }
break;
default:
- snd_printk(KERN_WARNING "invalid trigger mode 0x%x.\n", what);
+ dev_warn(chip->card->dev, "invalid trigger mode 0x%x.\n", what);
error = -EINVAL;
}
@@ -217,25 +198,13 @@ static int snd_ad1816a_capture_trigger(struct snd_pcm_substream *substream, int
SNDRV_PCM_STREAM_CAPTURE, cmd, 1);
}
-static int snd_ad1816a_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_ad1816a_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_ad1816a_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_ad1816a *chip = snd_pcm_substream_chip(substream);
- unsigned long flags;
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int size, rate;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
chip->p_dma_size = size = snd_pcm_lib_buffer_bytes(substream);
snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,
@@ -255,19 +224,16 @@ static int snd_ad1816a_playback_prepare(struct snd_pcm_substream *substream)
snd_ad1816a_write(chip, AD1816A_PLAYBACK_BASE_COUNT,
snd_pcm_lib_period_bytes(substream) / 4 - 1);
-
- spin_unlock_irqrestore(&chip->lock, flags);
return 0;
}
static int snd_ad1816a_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_ad1816a *chip = snd_pcm_substream_chip(substream);
- unsigned long flags;
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int size, rate;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
chip->c_dma_size = size = snd_pcm_lib_buffer_bytes(substream);
snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG,
@@ -287,8 +253,6 @@ static int snd_ad1816a_capture_prepare(struct snd_pcm_substream *substream)
snd_ad1816a_write(chip, AD1816A_CAPTURE_BASE_COUNT,
snd_pcm_lib_period_bytes(substream) / 4 - 1);
-
- spin_unlock_irqrestore(&chip->lock, flags);
return 0;
}
@@ -319,9 +283,9 @@ static irqreturn_t snd_ad1816a_interrupt(int irq, void *dev_id)
struct snd_ad1816a *chip = dev_id;
unsigned char status;
- spin_lock(&chip->lock);
- status = snd_ad1816a_in(chip, AD1816A_INTERRUPT_STATUS);
- spin_unlock(&chip->lock);
+ scoped_guard(spinlock, &chip->lock) {
+ status = snd_ad1816a_in(chip, AD1816A_INTERRUPT_STATUS);
+ }
if ((status & AD1816A_PLAYBACK_IRQ_PENDING) && chip->playback_substream)
snd_pcm_period_elapsed(chip->playback_substream);
@@ -332,14 +296,14 @@ static irqreturn_t snd_ad1816a_interrupt(int irq, void *dev_id)
if ((status & AD1816A_TIMER_IRQ_PENDING) && chip->timer)
snd_timer_interrupt(chip->timer, chip->timer->sticks);
- spin_lock(&chip->lock);
- snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00);
- spin_unlock(&chip->lock);
+ scoped_guard(spinlock, &chip->lock) {
+ snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00);
+ }
return IRQ_HANDLED;
}
-static struct snd_pcm_hardware snd_ad1816a_playback = {
+static const struct snd_pcm_hardware snd_ad1816a_playback = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
@@ -358,7 +322,7 @@ static struct snd_pcm_hardware snd_ad1816a_playback = {
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_ad1816a_capture = {
+static const struct snd_pcm_hardware snd_ad1816a_capture = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
@@ -402,9 +366,9 @@ static unsigned long snd_ad1816a_timer_resolution(struct snd_timer *timer)
static int snd_ad1816a_timer_start(struct snd_timer *timer)
{
unsigned short bits;
- unsigned long flags;
struct snd_ad1816a *chip = snd_timer_chip(timer);
- spin_lock_irqsave(&chip->lock, flags);
+
+ guard(spinlock_irqsave)(&chip->lock);
bits = snd_ad1816a_read(chip, AD1816A_INTERRUPT_ENABLE);
if (!(bits & AD1816A_TIMER_ENABLE)) {
@@ -414,24 +378,20 @@ static int snd_ad1816a_timer_start(struct snd_timer *timer)
snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
AD1816A_TIMER_ENABLE, 0xffff);
}
- spin_unlock_irqrestore(&chip->lock, flags);
return 0;
}
static int snd_ad1816a_timer_stop(struct snd_timer *timer)
{
- unsigned long flags;
struct snd_ad1816a *chip = snd_timer_chip(timer);
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
snd_ad1816a_write_mask(chip, AD1816A_INTERRUPT_ENABLE,
AD1816A_TIMER_ENABLE, 0x0000);
-
- spin_unlock_irqrestore(&chip->lock, flags);
return 0;
}
-static struct snd_timer_hardware snd_ad1816a_timer_table = {
+static const struct snd_timer_hardware snd_ad1816a_timer_table = {
.flags = SNDRV_TIMER_HW_AUTO,
.resolution = 10000,
.ticks = 65535,
@@ -448,7 +408,8 @@ static int snd_ad1816a_playback_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int error;
- if ((error = snd_ad1816a_open(chip, AD1816A_MODE_PLAYBACK)) < 0)
+ error = snd_ad1816a_open(chip, AD1816A_MODE_PLAYBACK);
+ if (error < 0)
return error;
runtime->hw = snd_ad1816a_playback;
snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
@@ -463,7 +424,8 @@ static int snd_ad1816a_capture_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int error;
- if ((error = snd_ad1816a_open(chip, AD1816A_MODE_CAPTURE)) < 0)
+ error = snd_ad1816a_open(chip, AD1816A_MODE_CAPTURE);
+ if (error < 0)
return error;
runtime->hw = snd_ad1816a_capture;
snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
@@ -491,11 +453,9 @@ static int snd_ad1816a_capture_close(struct snd_pcm_substream *substream)
}
-static void __devinit snd_ad1816a_init(struct snd_ad1816a *chip)
+static void snd_ad1816a_init(struct snd_ad1816a *chip)
{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
snd_ad1816a_out(chip, AD1816A_INTERRUPT_STATUS, 0x00);
snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG,
@@ -507,15 +467,32 @@ static void __devinit snd_ad1816a_init(struct snd_ad1816a *chip)
AD1816A_CAPTURE_NOT_EQUAL | AD1816A_WSS_ENABLE, 0xffff);
snd_ad1816a_write(chip, AD1816A_DSP_CONFIG, 0x0000);
snd_ad1816a_write(chip, AD1816A_POWERDOWN_CTRL, 0x0000);
+}
+
+#ifdef CONFIG_PM
+void snd_ad1816a_suspend(struct snd_ad1816a *chip)
+{
+ int reg;
- spin_unlock_irqrestore(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
+ for (reg = 0; reg < 48; reg++)
+ chip->image[reg] = snd_ad1816a_read(chip, reg);
}
-static int __devinit snd_ad1816a_probe(struct snd_ad1816a *chip)
+void snd_ad1816a_resume(struct snd_ad1816a *chip)
{
- unsigned long flags;
+ int reg;
- spin_lock_irqsave(&chip->lock, flags);
+ snd_ad1816a_init(chip);
+ guard(spinlock_irqsave)(&chip->lock);
+ for (reg = 0; reg < 48; reg++)
+ snd_ad1816a_write(chip, reg, chip->image[reg]);
+}
+#endif
+
+static int snd_ad1816a_probe(struct snd_ad1816a *chip)
+{
+ guard(spinlock_irqsave)(&chip->lock);
switch (chip->version = snd_ad1816a_read(chip, AD1816A_VERSION_ID)) {
case 0:
@@ -530,86 +507,51 @@ static int __devinit snd_ad1816a_probe(struct snd_ad1816a *chip)
default:
chip->hardware = AD1816A_HW_AUTO;
}
-
- spin_unlock_irqrestore(&chip->lock, flags);
- return 0;
-}
-
-static int snd_ad1816a_free(struct snd_ad1816a *chip)
-{
- release_and_free_resource(chip->res_port);
- if (chip->irq >= 0)
- free_irq(chip->irq, (void *) chip);
- if (chip->dma1 >= 0) {
- snd_dma_disable(chip->dma1);
- free_dma(chip->dma1);
- }
- if (chip->dma2 >= 0) {
- snd_dma_disable(chip->dma2);
- free_dma(chip->dma2);
- }
- kfree(chip);
return 0;
}
-static int snd_ad1816a_dev_free(struct snd_device *device)
-{
- struct snd_ad1816a *chip = device->device_data;
- return snd_ad1816a_free(chip);
-}
-
-static const char __devinit *snd_ad1816a_chip_id(struct snd_ad1816a *chip)
+static const char *snd_ad1816a_chip_id(struct snd_ad1816a *chip)
{
switch (chip->hardware) {
case AD1816A_HW_AD1816A: return "AD1816A";
case AD1816A_HW_AD1815: return "AD1815";
case AD1816A_HW_AD18MAX10: return "AD18max10";
default:
- snd_printk(KERN_WARNING "Unknown chip version %d:%d.\n",
- chip->version, chip->hardware);
+ dev_warn(chip->card->dev, "Unknown chip version %d:%d.\n",
+ chip->version, chip->hardware);
return "AD1816A - unknown";
}
}
-int __devinit snd_ad1816a_create(struct snd_card *card,
- unsigned long port, int irq, int dma1, int dma2,
- struct snd_ad1816a **rchip)
+int snd_ad1816a_create(struct snd_card *card,
+ unsigned long port, int irq, int dma1, int dma2,
+ struct snd_ad1816a *chip)
{
- static struct snd_device_ops ops = {
- .dev_free = snd_ad1816a_dev_free,
- };
int error;
- struct snd_ad1816a *chip;
-
- *rchip = NULL;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL)
- return -ENOMEM;
chip->irq = -1;
chip->dma1 = -1;
chip->dma2 = -1;
- if ((chip->res_port = request_region(port, 16, "AD1816A")) == NULL) {
- snd_printk(KERN_ERR "ad1816a: can't grab port 0x%lx\n", port);
- snd_ad1816a_free(chip);
+ chip->res_port = devm_request_region(card->dev, port, 16, "AD1816A");
+ if (!chip->res_port) {
+ dev_err(card->dev, "ad1816a: can't grab port 0x%lx\n", port);
return -EBUSY;
}
- if (request_irq(irq, snd_ad1816a_interrupt, IRQF_DISABLED, "AD1816A", (void *) chip)) {
- snd_printk(KERN_ERR "ad1816a: can't grab IRQ %d\n", irq);
- snd_ad1816a_free(chip);
+ if (devm_request_irq(card->dev, irq, snd_ad1816a_interrupt, 0,
+ "AD1816A", (void *) chip)) {
+ dev_err(card->dev, "ad1816a: can't grab IRQ %d\n", irq);
return -EBUSY;
}
chip->irq = irq;
- if (request_dma(dma1, "AD1816A - 1")) {
- snd_printk(KERN_ERR "ad1816a: can't grab DMA1 %d\n", dma1);
- snd_ad1816a_free(chip);
+ card->sync_irq = chip->irq;
+ if (snd_devm_request_dma(card->dev, dma1, "AD1816A - 1")) {
+ dev_err(card->dev, "ad1816a: can't grab DMA1 %d\n", dma1);
return -EBUSY;
}
chip->dma1 = dma1;
- if (request_dma(dma2, "AD1816A - 2")) {
- snd_printk(KERN_ERR "ad1816a: can't grab DMA2 %d\n", dma2);
- snd_ad1816a_free(chip);
+ if (snd_devm_request_dma(card->dev, dma2, "AD1816A - 2")) {
+ dev_err(card->dev, "ad1816a: can't grab DMA2 %d\n", dma2);
return -EBUSY;
}
chip->dma2 = dma2;
@@ -618,51 +560,38 @@ int __devinit snd_ad1816a_create(struct snd_card *card,
chip->port = port;
spin_lock_init(&chip->lock);
- if ((error = snd_ad1816a_probe(chip))) {
- snd_ad1816a_free(chip);
+ error = snd_ad1816a_probe(chip);
+ if (error)
return error;
- }
snd_ad1816a_init(chip);
- /* Register device */
- if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_ad1816a_free(chip);
- return error;
- }
-
- *rchip = chip;
return 0;
}
-static struct snd_pcm_ops snd_ad1816a_playback_ops = {
+static const struct snd_pcm_ops snd_ad1816a_playback_ops = {
.open = snd_ad1816a_playback_open,
.close = snd_ad1816a_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ad1816a_hw_params,
- .hw_free = snd_ad1816a_hw_free,
.prepare = snd_ad1816a_playback_prepare,
.trigger = snd_ad1816a_playback_trigger,
.pointer = snd_ad1816a_playback_pointer,
};
-static struct snd_pcm_ops snd_ad1816a_capture_ops = {
+static const struct snd_pcm_ops snd_ad1816a_capture_ops = {
.open = snd_ad1816a_capture_open,
.close = snd_ad1816a_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ad1816a_hw_params,
- .hw_free = snd_ad1816a_hw_free,
.prepare = snd_ad1816a_capture_prepare,
.trigger = snd_ad1816a_capture_trigger,
.pointer = snd_ad1816a_capture_pointer,
};
-int __devinit snd_ad1816a_pcm(struct snd_ad1816a *chip, int device, struct snd_pcm **rpcm)
+int snd_ad1816a_pcm(struct snd_ad1816a *chip, int device)
{
int error;
struct snd_pcm *pcm;
- if ((error = snd_pcm_new(chip->card, "AD1816A", device, 1, 1, &pcm)))
+ error = snd_pcm_new(chip->card, "AD1816A", device, 1, 1, &pcm);
+ if (error)
return error;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1816a_playback_ops);
@@ -671,20 +600,17 @@ int __devinit snd_ad1816a_pcm(struct snd_ad1816a *chip, int device, struct snd_p
pcm->private_data = chip;
pcm->info_flags = (chip->dma1 == chip->dma2 ) ? SNDRV_PCM_INFO_JOINT_DUPLEX : 0;
- strcpy(pcm->name, snd_ad1816a_chip_id(chip));
+ strscpy(pcm->name, snd_ad1816a_chip_id(chip));
snd_ad1816a_init(chip);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
- 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, chip->card->dev,
+ 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
chip->pcm = pcm;
- if (rpcm)
- *rpcm = pcm;
return 0;
}
-int __devinit snd_ad1816a_timer(struct snd_ad1816a *chip, int device, struct snd_timer **rtimer)
+int snd_ad1816a_timer(struct snd_ad1816a *chip, int device)
{
struct snd_timer *timer;
struct snd_timer_id tid;
@@ -695,14 +621,13 @@ int __devinit snd_ad1816a_timer(struct snd_ad1816a *chip, int device, struct snd
tid.card = chip->card->number;
tid.device = device;
tid.subdevice = 0;
- if ((error = snd_timer_new(chip->card, "AD1816A", &tid, &timer)) < 0)
+ error = snd_timer_new(chip->card, "AD1816A", &tid, &timer);
+ if (error < 0)
return error;
- strcpy(timer->name, snd_ad1816a_chip_id(chip));
+ strscpy(timer->name, snd_ad1816a_chip_id(chip));
timer->private_data = chip;
chip->timer = timer;
timer->hw = snd_ad1816a_timer_table;
- if (rtimer)
- *rtimer = timer;
return 0;
}
@@ -712,29 +637,21 @@ int __devinit snd_ad1816a_timer(struct snd_ad1816a *chip, int device, struct snd
static int snd_ad1816a_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[8] = {
+ static const char * const texts[8] = {
"Line", "Mix", "CD", "Synth", "Video",
"Mic", "Phone",
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 2;
- uinfo->value.enumerated.items = 7;
- if (uinfo->value.enumerated.item > 6)
- uinfo->value.enumerated.item = 6;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 2, 7, texts);
}
static int snd_ad1816a_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ad1816a *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
unsigned short val;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
val = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL);
- spin_unlock_irqrestore(&chip->lock, flags);
ucontrol->value.enumerated.item[0] = (val >> 12) & 7;
ucontrol->value.enumerated.item[1] = (val >> 4) & 7;
return 0;
@@ -743,7 +660,6 @@ static int snd_ad1816a_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
static int snd_ad1816a_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ad1816a *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
unsigned short val;
int change;
@@ -752,10 +668,9 @@ static int snd_ad1816a_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
return -EINVAL;
val = (ucontrol->value.enumerated.item[0] << 12) |
(ucontrol->value.enumerated.item[1] << 4);
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
change = snd_ad1816a_read(chip, AD1816A_ADC_SOURCE_SEL) != val;
snd_ad1816a_write(chip, AD1816A_ADC_SOURCE_SEL, val);
- spin_unlock_irqrestore(&chip->lock, flags);
return change;
}
@@ -785,15 +700,13 @@ static int snd_ad1816a_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl
static int snd_ad1816a_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ad1816a *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
ucontrol->value.integer.value[0] = (snd_ad1816a_read(chip, reg) >> shift) & mask;
- spin_unlock_irqrestore(&chip->lock, flags);
if (invert)
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
return 0;
@@ -802,7 +715,6 @@ static int snd_ad1816a_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_ad1816a_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ad1816a *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
@@ -814,12 +726,11 @@ static int snd_ad1816a_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
if (invert)
val = mask - val;
val <<= shift;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
old_val = snd_ad1816a_read(chip, reg);
val = (old_val & ~(mask << shift)) | val;
change = val != old_val;
snd_ad1816a_write(chip, reg, val);
- spin_unlock_irqrestore(&chip->lock, flags);
return change;
}
@@ -850,7 +761,6 @@ static int snd_ad1816a_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl
static int snd_ad1816a_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ad1816a *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift_left = (kcontrol->private_value >> 8) & 0x0f;
int shift_right = (kcontrol->private_value >> 12) & 0x0f;
@@ -858,11 +768,10 @@ static int snd_ad1816a_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
int invert = (kcontrol->private_value >> 24) & 0xff;
unsigned short val;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
val = snd_ad1816a_read(chip, reg);
ucontrol->value.integer.value[0] = (val >> shift_left) & mask;
ucontrol->value.integer.value[1] = (val >> shift_right) & mask;
- spin_unlock_irqrestore(&chip->lock, flags);
if (invert) {
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
@@ -873,7 +782,6 @@ static int snd_ad1816a_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_ad1816a_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ad1816a *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift_left = (kcontrol->private_value >> 8) & 0x0f;
int shift_right = (kcontrol->private_value >> 12) & 0x0f;
@@ -890,12 +798,11 @@ static int snd_ad1816a_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
}
val1 <<= shift_left;
val2 <<= shift_right;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
old_val = snd_ad1816a_read(chip, reg);
val1 = (old_val & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
change = val1 != old_val;
snd_ad1816a_write(chip, reg, val1);
- spin_unlock_irqrestore(&chip->lock, flags);
return change;
}
@@ -905,7 +812,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
-static struct snd_kcontrol_new snd_ad1816a_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ad1816a_controls[] = {
AD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1),
AD1816A_DOUBLE_TLV("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1,
db_scale_5bit),
@@ -951,7 +858,7 @@ AD1816A_SINGLE("3D Control - Switch", AD1816A_3D_PHAT_CTRL, 15, 1, 1),
AD1816A_SINGLE("3D Control - Level", AD1816A_3D_PHAT_CTRL, 0, 15, 0),
};
-int __devinit snd_ad1816a_mixer(struct snd_ad1816a *chip)
+int snd_ad1816a_mixer(struct snd_ad1816a *chip)
{
struct snd_card *card;
unsigned int idx;
@@ -962,10 +869,11 @@ int __devinit snd_ad1816a_mixer(struct snd_ad1816a *chip)
card = chip->card;
- strcpy(card->mixername, snd_ad1816a_chip_id(chip));
+ strscpy(card->mixername, snd_ad1816a_chip_id(chip));
for (idx = 0; idx < ARRAY_SIZE(snd_ad1816a_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1816a_controls[idx], chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1816a_controls[idx], chip));
+ if (err < 0)
return err;
}
return 0;
diff --git a/sound/isa/ad1848/Makefile b/sound/isa/ad1848/Makefile
index 3d6dea3ff927..5fdfc1c9f059 100644
--- a/sound/isa/ad1848/Makefile
+++ b/sound/isa/ad1848/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ad1848-objs := ad1848.o
+snd-ad1848-y := ad1848.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AD1848) += snd-ad1848.o
diff --git a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c
index 4beeb6f98e0e..401d8df28d87 100644
--- a/sound/isa/ad1848/ad1848.c
+++ b/sound/isa/ad1848/ad1848.c
@@ -1,24 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Generic driver for AD1848/AD1847/CS4248 chips (0.1 Alpha)
* Copyright (c) by Tugrul Galatali <galatalt@stuy.edu>,
* Jaroslav Kysela <perex@perex.cz>
* Based on card-4232.c by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -26,7 +11,7 @@
#include <linux/isa.h>
#include <linux/time.h>
#include <linux/wait.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/wss.h>
#include <sound/initval.h>
@@ -37,17 +22,14 @@
MODULE_DESCRIPTION(CRD_NAME);
MODULE_AUTHOR("Tugrul Galatali <galatalt@stuy.edu>, Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Analog Devices,AD1848},"
- "{Analog Devices,AD1847},"
- "{Crystal Semiconductors,CS4248}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */
-static int thinkpad[SNDRV_CARDS]; /* Thinkpad special case */
+static bool thinkpad[SNDRV_CARDS]; /* Thinkpad special case */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");
@@ -55,16 +37,16 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for " CRD_NAME " driver.");
module_param_array(thinkpad, bool, NULL, 0444);
MODULE_PARM_DESC(thinkpad, "Enable only for the onboard CS4248 of IBM Thinkpad 360/750/755 series.");
-static int __devinit snd_ad1848_match(struct device *dev, unsigned int n)
+static int snd_ad1848_match(struct device *dev, unsigned int n)
{
if (!enable[n])
return 0;
@@ -84,14 +66,13 @@ static int __devinit snd_ad1848_match(struct device *dev, unsigned int n)
return 1;
}
-static int __devinit snd_ad1848_probe(struct device *dev, unsigned int n)
+static int snd_ad1848_probe(struct device *dev, unsigned int n)
{
struct snd_card *card;
struct snd_wss *chip;
- struct snd_pcm *pcm;
int error;
- error = snd_card_create(index[n], id[n], THIS_MODULE, 0, &card);
+ error = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE, 0, &card);
if (error < 0)
return error;
@@ -99,44 +80,36 @@ static int __devinit snd_ad1848_probe(struct device *dev, unsigned int n)
thinkpad[n] ? WSS_HW_THINKPAD : WSS_HW_DETECT,
0, &chip);
if (error < 0)
- goto out;
+ return error;
card->private_data = chip;
- error = snd_wss_pcm(chip, 0, &pcm);
+ error = snd_wss_pcm(chip, 0);
if (error < 0)
- goto out;
+ return error;
error = snd_wss_mixer(chip);
if (error < 0)
- goto out;
-
- strcpy(card->driver, "AD1848");
- strcpy(card->shortname, pcm->name);
+ return error;
- sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
- pcm->name, chip->port, irq[n], dma1[n]);
- if (thinkpad[n])
- strcat(card->longname, " [Thinkpad]");
+ strscpy(card->driver, "AD1848", sizeof(card->driver));
+ strscpy(card->shortname, chip->pcm->name, sizeof(card->shortname));
- snd_card_set_dev(card, dev);
+ if (!thinkpad[n])
+ scnprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, irq %d, dma %d",
+ chip->pcm->name, chip->port, irq[n], dma1[n]);
+ else
+ scnprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, irq %d, dma %d [Thinkpad]",
+ chip->pcm->name, chip->port, irq[n], dma1[n]);
error = snd_card_register(card);
if (error < 0)
- goto out;
+ return error;
dev_set_drvdata(dev, card);
return 0;
-
-out: snd_card_free(card);
- return error;
-}
-
-static int __devexit snd_ad1848_remove(struct device *dev, unsigned int n)
-{
- snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
- return 0;
}
#ifdef CONFIG_PM
@@ -164,7 +137,6 @@ static int snd_ad1848_resume(struct device *dev, unsigned int n)
static struct isa_driver snd_ad1848_driver = {
.match = snd_ad1848_match,
.probe = snd_ad1848_probe,
- .remove = __devexit_p(snd_ad1848_remove),
#ifdef CONFIG_PM
.suspend = snd_ad1848_suspend,
.resume = snd_ad1848_resume,
@@ -174,15 +146,4 @@ static struct isa_driver snd_ad1848_driver = {
}
};
-static int __init alsa_card_ad1848_init(void)
-{
- return isa_register_driver(&snd_ad1848_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_ad1848_exit(void)
-{
- isa_unregister_driver(&snd_ad1848_driver);
-}
-
-module_init(alsa_card_ad1848_init);
-module_exit(alsa_card_ad1848_exit);
+module_isa_driver(snd_ad1848_driver, SNDRV_CARDS);
diff --git a/sound/isa/adlib.c b/sound/isa/adlib.c
index 7465ae036e0b..03fb2bce9255 100644
--- a/sound/isa/adlib.c
+++ b/sound/isa/adlib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AdLib FM card driver.
*/
@@ -18,7 +19,7 @@ MODULE_LICENSE("GPL");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
module_param_array(index, int, NULL, 0444);
@@ -27,10 +28,10 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-static int __devinit snd_adlib_match(struct device *dev, unsigned int n)
+static int snd_adlib_match(struct device *dev, unsigned int n)
{
if (!enable[n])
return 0;
@@ -42,88 +43,57 @@ static int __devinit snd_adlib_match(struct device *dev, unsigned int n)
return 1;
}
-static void snd_adlib_free(struct snd_card *card)
-{
- release_and_free_resource(card->private_data);
-}
-
-static int __devinit snd_adlib_probe(struct device *dev, unsigned int n)
+static int snd_adlib_probe(struct device *dev, unsigned int n)
{
struct snd_card *card;
struct snd_opl3 *opl3;
int error;
- error = snd_card_create(index[n], id[n], THIS_MODULE, 0, &card);
+ error = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE, 0, &card);
if (error < 0) {
dev_err(dev, "could not create card\n");
return error;
}
- card->private_data = request_region(port[n], 4, CRD_NAME);
+ card->private_data = devm_request_region(dev, port[n], 4, CRD_NAME);
if (!card->private_data) {
dev_err(dev, "could not grab ports\n");
- error = -EBUSY;
- goto out;
+ return -EBUSY;
}
- card->private_free = snd_adlib_free;
- strcpy(card->driver, DEV_NAME);
- strcpy(card->shortname, CRD_NAME);
+ strscpy(card->driver, DEV_NAME);
+ strscpy(card->shortname, CRD_NAME);
sprintf(card->longname, CRD_NAME " at %#lx", port[n]);
error = snd_opl3_create(card, port[n], port[n] + 2, OPL3_HW_AUTO, 1, &opl3);
if (error < 0) {
dev_err(dev, "could not create OPL\n");
- goto out;
+ return error;
}
error = snd_opl3_hwdep_new(opl3, 0, 0, NULL);
if (error < 0) {
dev_err(dev, "could not create FM\n");
- goto out;
+ return error;
}
- snd_card_set_dev(card, dev);
-
error = snd_card_register(card);
if (error < 0) {
dev_err(dev, "could not register card\n");
- goto out;
+ return error;
}
dev_set_drvdata(dev, card);
return 0;
-
-out: snd_card_free(card);
- return error;
-}
-
-static int __devexit snd_adlib_remove(struct device *dev, unsigned int n)
-{
- snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
- return 0;
}
static struct isa_driver snd_adlib_driver = {
.match = snd_adlib_match,
.probe = snd_adlib_probe,
- .remove = __devexit_p(snd_adlib_remove),
.driver = {
.name = DEV_NAME
}
};
-static int __init alsa_card_adlib_init(void)
-{
- return isa_register_driver(&snd_adlib_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_adlib_exit(void)
-{
- isa_unregister_driver(&snd_adlib_driver);
-}
-
-module_init(alsa_card_adlib_init);
-module_exit(alsa_card_adlib_exit);
+module_isa_driver(snd_adlib_driver, SNDRV_CARDS);
diff --git a/sound/isa/als100.c b/sound/isa/als100.c
index 20becc89f6f6..cfc241bd252e 100644
--- a/sound/isa/als100.c
+++ b/sound/isa/als100.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
card-als100.c - driver for Avance Logic ALS100 based soundcards.
@@ -7,54 +8,28 @@
Thanks to Pierfrancesco 'qM2' Passerini.
Generalised for soundcards based on DT-0196 and ALS-007 chips
- by Jonathan Woithe <jwoithe@physics.adelaide.edu.au>: June 2002.
+ by Jonathan Woithe <jwoithe@just42.net>: June 2002.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/time.h>
#include <linux/pnp.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/mpu401.h>
#include <sound/opl3.h>
#include <sound/sb.h>
-#define PFX "als100: "
-
MODULE_DESCRIPTION("Avance Logic ALS007/ALS1X0");
-MODULE_SUPPORTED_DEVICE("{{Diamond Technologies DT-019X},"
- "{Avance Logic ALS-007}}"
- "{{Avance Logic,ALS100 - PRO16PNP},"
- "{Avance Logic,ALS110},"
- "{Avance Logic,ALS120},"
- "{Avance Logic,ALS200},"
- "{3D Melody,MF1000},"
- "{Digimate,3D Sound},"
- "{Avance Logic,ALS120},"
- "{RTL,RTL3000}}");
-
MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
MODULE_LICENSE("GPL");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
@@ -79,7 +54,7 @@ struct snd_card_als100 {
struct snd_sb *chip;
};
-static struct pnp_card_device_id snd_als100_pnpids[] = {
+static const struct pnp_card_device_id snd_als100_pnpids[] = {
/* DT197A30 */
{ .id = "RWB1688",
.devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } },
@@ -117,9 +92,9 @@ static struct pnp_card_device_id snd_als100_pnpids[] = {
MODULE_DEVICE_TABLE(pnp_card, snd_als100_pnpids);
-static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
struct pnp_dev *pdev;
int err;
@@ -135,7 +110,7 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
+ dev_err(&pdev->dev, "AUDIO pnp configure failure\n");
return err;
}
port[dev] = pnp_port_start(pdev, 0);
@@ -158,7 +133,7 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
__mpu_error:
if (pdev) {
pnp_release_card_device(pdev);
- snd_printk(KERN_ERR PFX "MPU401 pnp configure failure, skipping\n");
+ dev_err(&pdev->dev, "MPU401 pnp configure failure, skipping\n");
}
acard->devmpu = NULL;
mpu_port[dev] = -1;
@@ -174,7 +149,7 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
__fm_error:
if (pdev) {
pnp_release_card_device(pdev);
- snd_printk(KERN_ERR PFX "OPL3 pnp configure failure, skipping\n");
+ dev_err(&pdev->dev, "OPL3 pnp configure failure, skipping\n");
}
acard->devopl = NULL;
fm_port[dev] = -1;
@@ -183,9 +158,9 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
return 0;
}
-static int __devinit snd_card_als100_probe(int dev,
- struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_card_als100_probe(int dev,
+ struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
int error;
struct snd_sb *chip;
@@ -193,17 +168,16 @@ static int __devinit snd_card_als100_probe(int dev,
struct snd_card_als100 *acard;
struct snd_opl3 *opl3;
- error = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_als100), &card);
+ error = snd_devm_card_new(&pcard->card->dev,
+ index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_als100), &card);
if (error < 0)
return error;
acard = card->private_data;
- if ((error = snd_card_als100_pnp(dev, acard, pcard, pid))) {
- snd_card_free(card);
+ error = snd_card_als100_pnp(dev, acard, pcard, pid);
+ if (error)
return error;
- }
- snd_card_set_dev(card, &pcard->card->dev);
if (pid->driver_data == SB_HW_DT019X)
dma16[dev] = -1;
@@ -213,35 +187,32 @@ static int __devinit snd_card_als100_probe(int dev,
dma8[dev], dma16[dev],
pid->driver_data,
&chip);
- if (error < 0) {
- snd_card_free(card);
+ if (error < 0)
return error;
- }
acard->chip = chip;
if (pid->driver_data == SB_HW_DT019X) {
- strcpy(card->driver, "DT-019X");
- strcpy(card->shortname, "Diamond Tech. DT-019X");
- sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d",
- card->shortname, chip->name, chip->port,
- irq[dev], dma8[dev]);
+ strscpy(card->driver, "DT-019X");
+ strscpy(card->shortname, "Diamond Tech. DT-019X");
+ snprintf(card->longname, sizeof(card->longname),
+ "Diamond Tech. DT-019X, %s at 0x%lx, irq %d, dma %d",
+ chip->name, chip->port, irq[dev], dma8[dev]);
} else {
- strcpy(card->driver, "ALS100");
- strcpy(card->shortname, "Avance Logic ALS100");
- sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d",
- card->shortname, chip->name, chip->port,
- irq[dev], dma8[dev], dma16[dev]);
+ strscpy(card->driver, "ALS100");
+ strscpy(card->shortname, "Avance Logic ALS100");
+ snprintf(card->longname, sizeof(card->longname),
+ "Avance Logic ALS100, %s at 0x%lx, irq %d, dma %d&%d",
+ chip->name, chip->port, irq[dev], dma8[dev],
+ dma16[dev]);
}
- if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) {
- snd_card_free(card);
+ error = snd_sb16dsp_pcm(chip, 0);
+ if (error < 0)
return error;
- }
- if ((error = snd_sbmixer_new(chip)) < 0) {
- snd_card_free(card);
+ error = snd_sbmixer_new(chip);
+ if (error < 0)
return error;
- }
if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
int mpu_type = MPU401_HW_ALS100;
@@ -256,41 +227,37 @@ static int __devinit snd_card_als100_probe(int dev,
mpu_type,
mpu_port[dev], 0,
mpu_irq[dev],
- mpu_irq[dev] >= 0 ? IRQF_DISABLED : 0,
NULL) < 0)
- snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
+ dev_err(card->dev, "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
}
if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
if (snd_opl3_create(card,
fm_port[dev], fm_port[dev] + 2,
OPL3_HW_AUTO, 0, &opl3) < 0) {
- snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
- fm_port[dev], fm_port[dev] + 2);
+ dev_err(card->dev, "no OPL device at 0x%lx-0x%lx\n",
+ fm_port[dev], fm_port[dev] + 2);
} else {
- if ((error = snd_opl3_timer_new(opl3, 0, 1)) < 0) {
- snd_card_free(card);
+ error = snd_opl3_timer_new(opl3, 0, 1);
+ if (error < 0)
return error;
- }
- if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
- snd_card_free(card);
+ error = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (error < 0)
return error;
- }
}
}
- if ((error = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ error = snd_card_register(card);
+ if (error < 0)
return error;
- }
pnp_set_card_drvdata(pcard, card);
return 0;
}
-static unsigned int __devinitdata als100_devices;
+static unsigned int als100_devices;
-static int __devinit snd_als100_pnp_detect(struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_als100_pnp_detect(struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
static int dev;
int res;
@@ -308,12 +275,6 @@ static int __devinit snd_als100_pnp_detect(struct pnp_card_link *card,
return -ENODEV;
}
-static void __devexit snd_als100_pnp_remove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_als100_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
{
@@ -322,7 +283,6 @@ static int snd_als100_pnp_suspend(struct pnp_card_link *pcard, pm_message_t stat
struct snd_sb *chip = acard->chip;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_sbmixer_suspend(chip);
return 0;
}
@@ -345,7 +305,6 @@ static struct pnp_card_driver als100_pnpc_driver = {
.name = "als100",
.id_table = snd_als100_pnpids,
.probe = snd_als100_pnp_detect,
- .remove = __devexit_p(snd_als100_pnp_remove),
#ifdef CONFIG_PM
.suspend = snd_als100_pnp_suspend,
.resume = snd_als100_pnp_resume,
@@ -363,7 +322,7 @@ static int __init alsa_card_als100_init(void)
if (!als100_devices) {
pnp_unregister_card_driver(&als100_pnpc_driver);
#ifdef MODULE
- snd_printk(KERN_ERR "no Avance Logic based soundcards found\n");
+ pr_err("no Avance Logic based soundcards found\n");
#endif
return -ENODEV;
}
diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c
index aac8dc15c2fe..588b9f0831d3 100644
--- a/sound/isa/azt2320.c
+++ b/sound/isa/azt2320.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
card-azt2320.c - driver for Aztech Systems AZT2320 based soundcards.
Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
@@ -29,33 +17,26 @@
activation method (full-duplex audio!).
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/pnp.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/wss.h>
#include <sound/mpu401.h>
#include <sound/opl3.h>
-#define PFX "azt2320: "
-
MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
MODULE_DESCRIPTION("Aztech Systems AZT2320");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Aztech Systems,PRO16V},"
- "{Aztech Systems,AZT2320},"
- "{Aztech Systems,AZT3300},"
- "{Aztech Systems,AZT2320},"
- "{Aztech Systems,AZT3000}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
@@ -79,7 +60,7 @@ struct snd_card_azt2320 {
struct snd_wss *chip;
};
-static struct pnp_card_device_id snd_azt2320_pnpids[] = {
+static const struct pnp_card_device_id snd_azt2320_pnpids[] = {
/* PRO16V */
{ .id = "AZT1008", .devs = { { "AZT1008" }, { "AZT2001" }, } },
/* Aztech Sound Galaxy 16 */
@@ -99,9 +80,9 @@ MODULE_DEVICE_TABLE(pnp_card, snd_azt2320_pnpids);
#define DRIVER_NAME "snd-card-azt2320"
-static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acard,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acard,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
struct pnp_dev *pdev;
int err;
@@ -116,7 +97,7 @@ static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acar
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
+ dev_err(&pdev->dev, "AUDIO pnp configure failure\n");
return err;
}
port[dev] = pnp_port_start(pdev, 0);
@@ -137,7 +118,7 @@ static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acar
__mpu_error:
if (pdev) {
pnp_release_card_device(pdev);
- snd_printk(KERN_ERR PFX "MPU401 pnp configure failure, skipping\n");
+ dev_err(&pdev->dev, "MPU401 pnp configure failure, skipping\n");
}
acard->devmpu = NULL;
mpu_port[dev] = -1;
@@ -147,7 +128,7 @@ static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acar
}
/* same of snd_sbdsp_command by Jaroslav Kysela */
-static int __devinit snd_card_azt2320_command(unsigned long port, unsigned char val)
+static int snd_card_azt2320_command(unsigned long port, unsigned char val)
{
int i;
unsigned long limit;
@@ -161,22 +142,24 @@ static int __devinit snd_card_azt2320_command(unsigned long port, unsigned char
return -EBUSY;
}
-static int __devinit snd_card_azt2320_enable_wss(unsigned long port)
+static int snd_card_azt2320_enable_wss(unsigned long port)
{
int error;
- if ((error = snd_card_azt2320_command(port, 0x09)))
+ error = snd_card_azt2320_command(port, 0x09);
+ if (error)
return error;
- if ((error = snd_card_azt2320_command(port, 0x00)))
+ error = snd_card_azt2320_command(port, 0x00);
+ if (error)
return error;
mdelay(5);
return 0;
}
-static int __devinit snd_card_azt2320_probe(int dev,
- struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_card_azt2320_probe(int dev,
+ struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
int error;
struct snd_card *card;
@@ -184,91 +167,77 @@ static int __devinit snd_card_azt2320_probe(int dev,
struct snd_wss *chip;
struct snd_opl3 *opl3;
- error = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_azt2320), &card);
+ error = snd_devm_card_new(&pcard->card->dev,
+ index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_azt2320), &card);
if (error < 0)
return error;
acard = card->private_data;
- if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) {
- snd_card_free(card);
+ error = snd_card_azt2320_pnp(dev, acard, pcard, pid);
+ if (error)
return error;
- }
- snd_card_set_dev(card, &pcard->card->dev);
- if ((error = snd_card_azt2320_enable_wss(port[dev]))) {
- snd_card_free(card);
+ error = snd_card_azt2320_enable_wss(port[dev]);
+ if (error)
return error;
- }
error = snd_wss_create(card, wss_port[dev], -1,
irq[dev],
dma1[dev], dma2[dev],
WSS_HW_DETECT, 0, &chip);
- if (error < 0) {
- snd_card_free(card);
+ if (error < 0)
return error;
- }
- strcpy(card->driver, "AZT2320");
- strcpy(card->shortname, "Aztech AZT2320");
+ strscpy(card->driver, "AZT2320");
+ strscpy(card->shortname, "Aztech AZT2320");
sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i",
card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
- error = snd_wss_pcm(chip, 0, NULL);
- if (error < 0) {
- snd_card_free(card);
+ error = snd_wss_pcm(chip, 0);
+ if (error < 0)
return error;
- }
error = snd_wss_mixer(chip);
- if (error < 0) {
- snd_card_free(card);
+ if (error < 0)
return error;
- }
- error = snd_wss_timer(chip, 0, NULL);
- if (error < 0) {
- snd_card_free(card);
+ error = snd_wss_timer(chip, 0);
+ if (error < 0)
return error;
- }
if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320,
mpu_port[dev], 0,
- mpu_irq[dev], IRQF_DISABLED,
- NULL) < 0)
- snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
+ mpu_irq[dev], NULL) < 0)
+ dev_err(card->dev, "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
}
if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
if (snd_opl3_create(card,
fm_port[dev], fm_port[dev] + 2,
OPL3_HW_AUTO, 0, &opl3) < 0) {
- snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
- fm_port[dev], fm_port[dev] + 2);
+ dev_err(card->dev, "no OPL device at 0x%lx-0x%lx\n",
+ fm_port[dev], fm_port[dev] + 2);
} else {
- if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) {
- snd_card_free(card);
+ error = snd_opl3_timer_new(opl3, 1, 2);
+ if (error < 0)
return error;
- }
- if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
- snd_card_free(card);
+ error = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (error < 0)
return error;
- }
}
}
- if ((error = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ error = snd_card_register(card);
+ if (error < 0)
return error;
- }
pnp_set_card_drvdata(pcard, card);
return 0;
}
-static unsigned int __devinitdata azt2320_devices;
+static unsigned int azt2320_devices;
-static int __devinit snd_azt2320_pnp_detect(struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_azt2320_pnp_detect(struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
static int dev;
int res;
@@ -286,12 +255,6 @@ static int __devinit snd_azt2320_pnp_detect(struct pnp_card_link *card,
return -ENODEV;
}
-static void __devexit snd_azt2320_pnp_remove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_azt2320_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
{
@@ -321,7 +284,6 @@ static struct pnp_card_driver azt2320_pnpc_driver = {
.name = "azt2320",
.id_table = snd_azt2320_pnpids,
.probe = snd_azt2320_pnp_detect,
- .remove = __devexit_p(snd_azt2320_pnp_remove),
#ifdef CONFIG_PM
.suspend = snd_azt2320_pnp_suspend,
.resume = snd_azt2320_pnp_resume,
@@ -339,7 +301,7 @@ static int __init alsa_card_azt2320_init(void)
if (!azt2320_devices) {
pnp_unregister_card_driver(&azt2320_pnpc_driver);
#ifdef MODULE
- snd_printk(KERN_ERR "no AZT2320 based soundcards found\n");
+ pr_err("no AZT2320 based soundcards found\n");
#endif
return -ENODEV;
}
diff --git a/sound/isa/cmi8328.c b/sound/isa/cmi8328.c
new file mode 100644
index 000000000000..4e6d823af103
--- /dev/null
+++ b/sound/isa/cmi8328.c
@@ -0,0 +1,460 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for C-Media CMI8328-based soundcards, such as AudioExcel AV500
+ * Copyright (c) 2012 Ondrej Zary
+ *
+ * AudioExcel AV500 card consists of:
+ * - CMI8328 - main chip (SB Pro emulation, gameport, OPL3, MPU401, CD-ROM)
+ * - CS4231A - WSS codec
+ * - Dream SAM9233+GMS950400+RAM+ROM: Wavetable MIDI, connected to MPU401
+ */
+
+#include <linux/init.h>
+#include <linux/isa.h>
+#include <linux/module.h>
+#include <linux/gameport.h>
+#include <asm/dma.h>
+#include <sound/core.h>
+#include <sound/wss.h>
+#include <sound/opl3.h>
+#include <sound/mpu401.h>
+#define SNDRV_LEGACY_FIND_FREE_IOPORT
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Ondrej Zary <linux@rainbow-software.org>");
+MODULE_DESCRIPTION("C-Media CMI8328");
+MODULE_LICENSE("GPL");
+
+#if IS_ENABLED(CONFIG_GAMEPORT)
+#define SUPPORT_JOYSTICK 1
+#endif
+
+/* I/O port is configured by jumpers on the card to one of these */
+static const int cmi8328_ports[] = { 0x530, 0xe80, 0xf40, 0x604 };
+#define CMI8328_MAX ARRAY_SIZE(cmi8328_ports)
+
+static int index[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = -1};
+static char *id[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = NULL};
+static long port[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_PORT};
+static int irq[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_IRQ};
+static int dma1[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_DMA};
+static int dma2[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_DMA};
+static long mpuport[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_PORT};
+static int mpuirq[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = SNDRV_AUTO_IRQ};
+#ifdef SUPPORT_JOYSTICK
+static bool gameport[CMI8328_MAX] = {[0 ... (CMI8328_MAX-1)] = true};
+#endif
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for CMI8328 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for CMI8328 soundcard.");
+
+module_param_hw_array(port, long, ioport, NULL, 0444);
+MODULE_PARM_DESC(port, "Port # for CMI8328 driver.");
+module_param_hw_array(irq, int, irq, NULL, 0444);
+MODULE_PARM_DESC(irq, "IRQ # for CMI8328 driver.");
+module_param_hw_array(dma1, int, dma, NULL, 0444);
+MODULE_PARM_DESC(dma1, "DMA1 for CMI8328 driver.");
+module_param_hw_array(dma2, int, dma, NULL, 0444);
+MODULE_PARM_DESC(dma2, "DMA2 for CMI8328 driver.");
+
+module_param_hw_array(mpuport, long, ioport, NULL, 0444);
+MODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8328 driver.");
+module_param_hw_array(mpuirq, int, irq, NULL, 0444);
+MODULE_PARM_DESC(mpuirq, "IRQ # for CMI8328 MPU-401 port.");
+#ifdef SUPPORT_JOYSTICK
+module_param_array(gameport, bool, NULL, 0444);
+MODULE_PARM_DESC(gameport, "Enable gameport.");
+#endif
+
+struct snd_cmi8328 {
+ u16 port;
+ u8 cfg[3];
+ u8 wss_cfg;
+ struct snd_card *card;
+ struct snd_wss *wss;
+#ifdef SUPPORT_JOYSTICK
+ struct gameport *gameport;
+#endif
+};
+
+/* CMI8328 configuration registers */
+#define CFG1 0x61
+#define CFG1_SB_DISABLE (1 << 0)
+#define CFG1_GAMEPORT (1 << 1)
+/*
+ * bit 0: SB: 0=enabled, 1=disabled
+ * bit 1: gameport: 0=disabled, 1=enabled
+ * bits 2-4: SB IRQ: 001=3, 010=5, 011=7, 100=9, 101=10, 110=11
+ * bits 5-6: SB DMA: 00=disabled (when SB disabled), 01=DMA0, 10=DMA1, 11=DMA3
+ * bit 7: SB port: 0=0x220, 1=0x240
+ */
+#define CFG2 0x62
+#define CFG2_MPU_ENABLE (1 << 2)
+/*
+ * bits 0-1: CD-ROM mode: 00=disabled, 01=Panasonic, 10=Sony/Mitsumi/Wearnes,
+ 11=IDE
+ * bit 2: MPU401: 0=disabled, 1=enabled
+ * bits 3-4: MPU401 IRQ: 00=3, 01=5, 10=7, 11=9,
+ * bits 5-7: MPU401 port: 000=0x300, 001=0x310, 010=0x320, 011=0x330, 100=0x332,
+ 101=0x334, 110=0x336
+ */
+#define CFG3 0x63
+/*
+ * bits 0-2: CD-ROM IRQ: 000=disabled, 001=3, 010=5, 011=7, 100=9, 101=10,
+ 110=11
+ * bits 3-4: CD-ROM DMA: 00=disabled, 01=DMA0, 10=DMA1, 11=DMA3
+ * bits 5-7: CD-ROM port: 000=0x300, 001=0x310, 010=0x320, 011=0x330, 100=0x340,
+ 101=0x350, 110=0x360, 111=0x370
+ */
+
+static u8 snd_cmi8328_cfg_read(u16 port, u8 reg)
+{
+ outb(0x43, port + 3);
+ outb(0x21, port + 3);
+ outb(reg, port + 3);
+ return inb(port);
+}
+
+static void snd_cmi8328_cfg_write(u16 port, u8 reg, u8 val)
+{
+ outb(0x43, port + 3);
+ outb(0x21, port + 3);
+ outb(reg, port + 3);
+ outb(val, port + 3); /* yes, value goes to the same port as index */
+}
+
+#ifdef CONFIG_PM
+static void snd_cmi8328_cfg_save(u16 port, u8 cfg[])
+{
+ cfg[0] = snd_cmi8328_cfg_read(port, CFG1);
+ cfg[1] = snd_cmi8328_cfg_read(port, CFG2);
+ cfg[2] = snd_cmi8328_cfg_read(port, CFG3);
+}
+
+static void snd_cmi8328_cfg_restore(u16 port, u8 cfg[])
+{
+ snd_cmi8328_cfg_write(port, CFG1, cfg[0]);
+ snd_cmi8328_cfg_write(port, CFG2, cfg[1]);
+ snd_cmi8328_cfg_write(port, CFG3, cfg[2]);
+}
+#endif /* CONFIG_PM */
+
+static int snd_cmi8328_mixer(struct snd_wss *chip)
+{
+ struct snd_card *card;
+ struct snd_ctl_elem_id id1, id2;
+ int err;
+
+ card = chip->card;
+
+ memset(&id1, 0, sizeof(id1));
+ memset(&id2, 0, sizeof(id2));
+ id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ /* rename AUX0 switch to CD */
+ strscpy(id1.name, "Aux Playback Switch");
+ strscpy(id2.name, "CD Playback Switch");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ dev_err(card->dev, "error renaming control\n");
+ return err;
+ }
+ /* rename AUX0 volume to CD */
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "CD Playback Volume");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ dev_err(card->dev, "error renaming control\n");
+ return err;
+ }
+ /* rename AUX1 switch to Synth */
+ strscpy(id1.name, "Aux Playback Switch");
+ id1.index = 1;
+ strscpy(id2.name, "Synth Playback Switch");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ dev_err(card->dev, "error renaming control\n");
+ return err;
+ }
+ /* rename AUX1 volume to Synth */
+ strscpy(id1.name, "Aux Playback Volume");
+ id1.index = 1;
+ strscpy(id2.name, "Synth Playback Volume");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ dev_err(card->dev, "error renaming control\n");
+ return err;
+ }
+
+ return 0;
+}
+
+/* find index of an item in "-1"-ended array */
+static int array_find(const int array[], int item)
+{
+ int i;
+
+ for (i = 0; array[i] != -1; i++)
+ if (array[i] == item)
+ return i;
+
+ return -1;
+}
+/* the same for long */
+static int array_find_l(const long array[], long item)
+{
+ int i;
+
+ for (i = 0; array[i] != -1; i++)
+ if (array[i] == item)
+ return i;
+
+ return -1;
+}
+
+static int snd_cmi8328_probe(struct device *pdev, unsigned int ndev)
+{
+ struct snd_card *card;
+ struct snd_opl3 *opl3;
+ struct snd_cmi8328 *cmi;
+#ifdef SUPPORT_JOYSTICK
+ struct resource *res;
+#endif
+ int err, pos;
+ static const long mpu_ports[] = { 0x330, 0x300, 0x310, 0x320, 0x332, 0x334,
+ 0x336, -1 };
+ static const u8 mpu_port_bits[] = { 3, 0, 1, 2, 4, 5, 6 };
+ static const int mpu_irqs[] = { 9, 7, 5, 3, -1 };
+ static const u8 mpu_irq_bits[] = { 3, 2, 1, 0 };
+ static const int irqs[] = { 9, 10, 11, 7, -1 };
+ static const u8 irq_bits[] = { 2, 3, 4, 1 };
+ static const int dma1s[] = { 3, 1, 0, -1 };
+ static const u8 dma_bits[] = { 3, 2, 1 };
+ static const int dma2s[][2] = { {1, -1}, {0, -1}, {-1, -1}, {0, -1} };
+ u16 port = cmi8328_ports[ndev];
+ u8 val;
+
+ /* 0xff is invalid configuration (but settable - hope it isn't set) */
+ if (snd_cmi8328_cfg_read(port, CFG1) == 0xff)
+ return -ENODEV;
+ /* the SB disable bit must NEVER EVER be cleared or the WSS dies */
+ snd_cmi8328_cfg_write(port, CFG1, CFG1_SB_DISABLE);
+ if (snd_cmi8328_cfg_read(port, CFG1) != CFG1_SB_DISABLE)
+ return -ENODEV;
+ /* disable everything first */
+ snd_cmi8328_cfg_write(port, CFG2, 0); /* disable CDROM and MPU401 */
+ snd_cmi8328_cfg_write(port, CFG3, 0); /* disable CDROM IRQ and DMA */
+
+ if (irq[ndev] == SNDRV_AUTO_IRQ) {
+ irq[ndev] = snd_legacy_find_free_irq(irqs);
+ if (irq[ndev] < 0) {
+ dev_err(pdev, "unable to find a free IRQ\n");
+ return -EBUSY;
+ }
+ }
+ if (dma1[ndev] == SNDRV_AUTO_DMA) {
+ dma1[ndev] = snd_legacy_find_free_dma(dma1s);
+ if (dma1[ndev] < 0) {
+ dev_err(pdev, "unable to find a free DMA1\n");
+ return -EBUSY;
+ }
+ }
+ if (dma2[ndev] == SNDRV_AUTO_DMA) {
+ dma2[ndev] = snd_legacy_find_free_dma(dma2s[dma1[ndev] % 4]);
+ if (dma2[ndev] < 0) {
+ dev_warn(pdev, "unable to find a free DMA2, full-duplex will not work\n");
+ dma2[ndev] = -1;
+ }
+ }
+ /* configure WSS IRQ... */
+ pos = array_find(irqs, irq[ndev]);
+ if (pos < 0) {
+ dev_err(pdev, "invalid IRQ %d\n", irq[ndev]);
+ return -EINVAL;
+ }
+ val = irq_bits[pos] << 3;
+ /* ...and DMA... */
+ pos = array_find(dma1s, dma1[ndev]);
+ if (pos < 0) {
+ dev_err(pdev, "invalid DMA1 %d\n", dma1[ndev]);
+ return -EINVAL;
+ }
+ val |= dma_bits[pos];
+ /* ...and DMA2 */
+ if (dma2[ndev] >= 0 && dma1[ndev] != dma2[ndev]) {
+ pos = array_find(dma2s[dma1[ndev]], dma2[ndev]);
+ if (pos < 0) {
+ dev_err(pdev, "invalid DMA2 %d\n", dma2[ndev]);
+ return -EINVAL;
+ }
+ val |= 0x04; /* enable separate capture DMA */
+ }
+ outb(val, port);
+
+ err = snd_devm_card_new(pdev, index[ndev], id[ndev], THIS_MODULE,
+ sizeof(struct snd_cmi8328), &card);
+ if (err < 0)
+ return err;
+ cmi = card->private_data;
+ cmi->card = card;
+ cmi->port = port;
+ cmi->wss_cfg = val;
+
+ err = snd_wss_create(card, port + 4, -1, irq[ndev], dma1[ndev],
+ dma2[ndev], WSS_HW_DETECT, 0, &cmi->wss);
+ if (err < 0)
+ return err;
+
+ err = snd_wss_pcm(cmi->wss, 0);
+ if (err < 0)
+ return err;
+
+ err = snd_wss_mixer(cmi->wss);
+ if (err < 0)
+ return err;
+ err = snd_cmi8328_mixer(cmi->wss);
+ if (err < 0)
+ return err;
+
+ if (snd_wss_timer(cmi->wss, 0) < 0)
+ dev_warn(pdev, "error initializing WSS timer\n");
+
+ if (mpuport[ndev] == SNDRV_AUTO_PORT) {
+ mpuport[ndev] = snd_legacy_find_free_ioport(mpu_ports, 2);
+ if (mpuport[ndev] < 0)
+ dev_err(pdev, "unable to find a free MPU401 port\n");
+ }
+ if (mpuirq[ndev] == SNDRV_AUTO_IRQ) {
+ mpuirq[ndev] = snd_legacy_find_free_irq(mpu_irqs);
+ if (mpuirq[ndev] < 0)
+ dev_err(pdev, "unable to find a free MPU401 IRQ\n");
+ }
+ /* enable and configure MPU401 */
+ if (mpuport[ndev] > 0 && mpuirq[ndev] > 0) {
+ val = CFG2_MPU_ENABLE;
+ pos = array_find_l(mpu_ports, mpuport[ndev]);
+ if (pos < 0)
+ dev_warn(pdev, "invalid MPU401 port 0x%lx\n",
+ mpuport[ndev]);
+ else {
+ val |= mpu_port_bits[pos] << 5;
+ pos = array_find(mpu_irqs, mpuirq[ndev]);
+ if (pos < 0)
+ dev_warn(pdev, "invalid MPU401 IRQ %d\n",
+ mpuirq[ndev]);
+ else {
+ val |= mpu_irq_bits[pos] << 3;
+ snd_cmi8328_cfg_write(port, CFG2, val);
+ if (snd_mpu401_uart_new(card, 0,
+ MPU401_HW_MPU401, mpuport[ndev],
+ 0, mpuirq[ndev], NULL) < 0)
+ dev_err(pdev, "error initializing MPU401\n");
+ }
+ }
+ }
+ /* OPL3 is hardwired to 0x388 and cannot be disabled */
+ if (snd_opl3_create(card, 0x388, 0x38a, OPL3_HW_AUTO, 0, &opl3) < 0)
+ dev_err(pdev, "error initializing OPL3\n");
+ else
+ if (snd_opl3_hwdep_new(opl3, 0, 1, NULL) < 0)
+ dev_warn(pdev, "error initializing OPL3 hwdep\n");
+
+ strscpy(card->driver, "CMI8328");
+ strscpy(card->shortname, "C-Media CMI8328");
+ sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d,%d",
+ card->shortname, cmi->wss->port, irq[ndev], dma1[ndev],
+ (dma2[ndev] >= 0) ? dma2[ndev] : dma1[ndev]);
+
+ dev_set_drvdata(pdev, card);
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
+#ifdef SUPPORT_JOYSTICK
+ if (!gameport[ndev])
+ return 0;
+ /* gameport is hardwired to 0x200 */
+ res = devm_request_region(pdev, 0x200, 8, "CMI8328 gameport");
+ if (!res)
+ dev_warn(pdev, "unable to allocate gameport I/O port\n");
+ else {
+ struct gameport *gp = cmi->gameport = gameport_allocate_port();
+ if (cmi->gameport) {
+ gameport_set_name(gp, "CMI8328 Gameport");
+ gameport_set_phys(gp, "%s/gameport0", dev_name(pdev));
+ gameport_set_dev_parent(gp, pdev);
+ gp->io = 0x200;
+ /* Enable gameport */
+ snd_cmi8328_cfg_write(port, CFG1,
+ CFG1_SB_DISABLE | CFG1_GAMEPORT);
+ gameport_register_port(gp);
+ }
+ }
+#endif
+ return 0;
+}
+
+static void snd_cmi8328_remove(struct device *pdev, unsigned int dev)
+{
+ struct snd_card *card = dev_get_drvdata(pdev);
+ struct snd_cmi8328 *cmi = card->private_data;
+
+#ifdef SUPPORT_JOYSTICK
+ if (cmi->gameport)
+ gameport_unregister_port(cmi->gameport);
+#endif
+ /* disable everything */
+ snd_cmi8328_cfg_write(cmi->port, CFG1, CFG1_SB_DISABLE);
+ snd_cmi8328_cfg_write(cmi->port, CFG2, 0);
+ snd_cmi8328_cfg_write(cmi->port, CFG3, 0);
+}
+
+#ifdef CONFIG_PM
+static int snd_cmi8328_suspend(struct device *pdev, unsigned int n,
+ pm_message_t state)
+{
+ struct snd_card *card = dev_get_drvdata(pdev);
+ struct snd_cmi8328 *cmi;
+
+ if (!card) /* ignore absent devices */
+ return 0;
+ cmi = card->private_data;
+ snd_cmi8328_cfg_save(cmi->port, cmi->cfg);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ cmi->wss->suspend(cmi->wss);
+
+ return 0;
+}
+
+static int snd_cmi8328_resume(struct device *pdev, unsigned int n)
+{
+ struct snd_card *card = dev_get_drvdata(pdev);
+ struct snd_cmi8328 *cmi;
+
+ if (!card) /* ignore absent devices */
+ return 0;
+ cmi = card->private_data;
+ snd_cmi8328_cfg_restore(cmi->port, cmi->cfg);
+ outb(cmi->wss_cfg, cmi->port);
+ cmi->wss->resume(cmi->wss);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+ return 0;
+}
+#endif
+
+static struct isa_driver snd_cmi8328_driver = {
+ .probe = snd_cmi8328_probe,
+ .remove = snd_cmi8328_remove,
+#ifdef CONFIG_PM
+ .suspend = snd_cmi8328_suspend,
+ .resume = snd_cmi8328_resume,
+#endif
+ .driver = {
+ .name = "cmi8328"
+ },
+};
+
+module_isa_driver(snd_cmi8328_driver, CMI8328_MAX);
diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c
index fe79a169acb5..3d1f19321b9e 100644
--- a/sound/isa/cmi8330.c
+++ b/sound/isa/cmi8330.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for C-Media's CMI8330 and CMI8329 soundcards.
* Copyright (c) by George Talusan <gstalusan@uwaterloo.ca>
* http://www.undergrad.math.uwaterloo.ca/~gstalusa
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -47,7 +33,7 @@
#include <linux/err.h>
#include <linux/isa.h>
#include <linux/pnp.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/wss.h>
#include <sound/opl3.h>
@@ -65,13 +51,12 @@
MODULE_AUTHOR("George Talusan <gstalusan@uwaterloo.ca>");
MODULE_DESCRIPTION("C-Media CMI8330/CMI8329");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8330,isapnp:{CMI0001,@@@0001,@X@0001}}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
#endif
static long sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
static int sbirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
@@ -95,27 +80,27 @@ module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
#endif
-module_param_array(sbport, long, NULL, 0444);
+module_param_hw_array(sbport, long, ioport, NULL, 0444);
MODULE_PARM_DESC(sbport, "Port # for CMI8330/CMI8329 SB driver.");
-module_param_array(sbirq, int, NULL, 0444);
+module_param_hw_array(sbirq, int, irq, NULL, 0444);
MODULE_PARM_DESC(sbirq, "IRQ # for CMI8330/CMI8329 SB driver.");
-module_param_array(sbdma8, int, NULL, 0444);
+module_param_hw_array(sbdma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(sbdma8, "DMA8 for CMI8330/CMI8329 SB driver.");
-module_param_array(sbdma16, int, NULL, 0444);
+module_param_hw_array(sbdma16, int, dma, NULL, 0444);
MODULE_PARM_DESC(sbdma16, "DMA16 for CMI8330/CMI8329 SB driver.");
-module_param_array(wssport, long, NULL, 0444);
+module_param_hw_array(wssport, long, ioport, NULL, 0444);
MODULE_PARM_DESC(wssport, "Port # for CMI8330/CMI8329 WSS driver.");
-module_param_array(wssirq, int, NULL, 0444);
+module_param_hw_array(wssirq, int, irq, NULL, 0444);
MODULE_PARM_DESC(wssirq, "IRQ # for CMI8330/CMI8329 WSS driver.");
-module_param_array(wssdma, int, NULL, 0444);
+module_param_hw_array(wssdma, int, dma, NULL, 0444);
MODULE_PARM_DESC(wssdma, "DMA for CMI8330/CMI8329 WSS driver.");
-module_param_array(fmport, long, NULL, 0444);
+module_param_hw_array(fmport, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fmport, "FM port # for CMI8330/CMI8329 driver.");
-module_param_array(mpuport, long, NULL, 0444);
+module_param_hw_array(mpuport, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8330/CMI8329 driver.");
-module_param_array(mpuirq, int, NULL, 0444);
+module_param_hw_array(mpuirq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpuirq, "IRQ # for CMI8330/CMI8329 MPU-401 port.");
#ifdef CONFIG_PNP
static int isa_registered;
@@ -134,7 +119,7 @@ static int pnp_registered;
#define CMI8330_LINGAIN 25
#define CMI8330_CDINGAIN 26
-static unsigned char snd_cmi8330_image[((CMI8330_CDINGAIN)-16) + 1] =
+static const unsigned char snd_cmi8330_image[((CMI8330_CDINGAIN)-16) + 1] =
{
0x40, /* 16 - recording mux (SB-mixer-enabled) */
#ifdef ENABLE_SB_MIXER
@@ -182,7 +167,7 @@ struct snd_cmi8330 {
#ifdef CONFIG_PNP
-static struct pnp_card_device_id snd_cmi8330_pnpids[] = {
+static const struct pnp_card_device_id snd_cmi8330_pnpids[] = {
{ .id = "CMI0001", .devs = { { "@X@0001" }, { "@@@0001" }, { "@H@0001" }, { "A@@0001" } } },
{ .id = "CMI0001", .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } } },
{ .id = "" }
@@ -193,7 +178,7 @@ MODULE_DEVICE_TABLE(pnp_card, snd_cmi8330_pnpids);
#endif
-static struct snd_kcontrol_new snd_cmi8330_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_cmi8330_controls[] = {
WSS_DOUBLE("Master Playback Volume", 0,
CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0),
WSS_SINGLE("Loud Playback Switch", 0,
@@ -249,7 +234,7 @@ WSS_SINGLE(SNDRV_CTL_NAME_IEC958("Input ", PLAYBACK, SWITCH), 0,
};
#ifdef ENABLE_SB_MIXER
-static struct sbmix_elem cmi8330_sb_mixers[] __devinitdata = {
+static const struct sbmix_elem cmi8330_sb_mixers[] = {
SB_DOUBLE("SB Master Playback Volume", SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31),
SB_DOUBLE("Tone Control - Bass", SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15),
SB_DOUBLE("Tone Control - Treble", SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15),
@@ -267,7 +252,7 @@ SB_DOUBLE("SB Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6
SB_SINGLE("SB Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1),
};
-static unsigned char cmi8330_sb_init_values[][2] __devinitdata = {
+static const unsigned char cmi8330_sb_init_values[][2] = {
{ SB_DSP4_MASTER_DEV + 0, 0 },
{ SB_DSP4_MASTER_DEV + 1, 0 },
{ SB_DSP4_PCM_DEV + 0, 0 },
@@ -281,37 +266,37 @@ static unsigned char cmi8330_sb_init_values[][2] __devinitdata = {
};
-static int __devinit cmi8330_add_sb_mixers(struct snd_sb *chip)
+static int cmi8330_add_sb_mixers(struct snd_sb *chip)
{
int idx, err;
- unsigned long flags;
- spin_lock_irqsave(&chip->mixer_lock, flags);
- snd_sbmixer_write(chip, 0x00, 0x00); /* mixer reset */
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->mixer_lock) {
+ snd_sbmixer_write(chip, 0x00, 0x00); /* mixer reset */
+ }
/* mute and zero volume channels */
for (idx = 0; idx < ARRAY_SIZE(cmi8330_sb_init_values); idx++) {
- spin_lock_irqsave(&chip->mixer_lock, flags);
- snd_sbmixer_write(chip, cmi8330_sb_init_values[idx][0],
- cmi8330_sb_init_values[idx][1]);
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->mixer_lock) {
+ snd_sbmixer_write(chip, cmi8330_sb_init_values[idx][0],
+ cmi8330_sb_init_values[idx][1]);
+ }
}
for (idx = 0; idx < ARRAY_SIZE(cmi8330_sb_mixers); idx++) {
- if ((err = snd_sbmixer_add_ctl_elem(chip, &cmi8330_sb_mixers[idx])) < 0)
+ err = snd_sbmixer_add_ctl_elem(chip, &cmi8330_sb_mixers[idx]);
+ if (err < 0)
return err;
}
return 0;
}
#endif
-static int __devinit snd_cmi8330_mixer(struct snd_card *card, struct snd_cmi8330 *acard)
+static int snd_cmi8330_mixer(struct snd_card *card, struct snd_cmi8330 *acard)
{
unsigned int idx;
int err;
- strcpy(card->mixername, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D");
+ strscpy(card->mixername, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D");
for (idx = 0; idx < ARRAY_SIZE(snd_cmi8330_controls); idx++) {
err = snd_ctl_add(card,
@@ -322,16 +307,17 @@ static int __devinit snd_cmi8330_mixer(struct snd_card *card, struct snd_cmi8330
}
#ifdef ENABLE_SB_MIXER
- if ((err = cmi8330_add_sb_mixers(acard->sb)) < 0)
+ err = cmi8330_add_sb_mixers(acard->sb);
+ if (err < 0)
return err;
#endif
return 0;
}
#ifdef CONFIG_PNP
-static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
struct pnp_dev *pdev;
int err;
@@ -355,7 +341,7 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "AD1848 PnP configure failure\n");
+ dev_err(&pdev->dev, "AD1848 PnP configure failure\n");
return -EBUSY;
}
wssport[dev] = pnp_port_start(pdev, 0);
@@ -369,7 +355,7 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "SB16 PnP configure failure\n");
+ dev_err(&pdev->dev, "SB16 PnP configure failure\n");
return -EBUSY;
}
sbport[dev] = pnp_port_start(pdev, 0);
@@ -389,7 +375,7 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
err = pnp_activate_dev(pdev);
if (err < 0)
- snd_printk(KERN_ERR "MPU-401 PnP configure failure: will be disabled\n");
+ dev_err(&pdev->dev, "MPU-401 PnP configure failure: will be disabled\n");
else {
mpuport[dev] = pnp_port_start(pdev, 0);
mpuirq[dev] = pnp_irq(pdev, 0);
@@ -437,19 +423,20 @@ static int snd_cmi8330_capture_open(struct snd_pcm_substream *substream)
return chip->streams[SNDRV_PCM_STREAM_CAPTURE].open(substream);
}
-static int __devinit snd_cmi8330_pcm(struct snd_card *card, struct snd_cmi8330 *chip)
+static int snd_cmi8330_pcm(struct snd_card *card, struct snd_cmi8330 *chip)
{
struct snd_pcm *pcm;
const struct snd_pcm_ops *ops;
int err;
- static snd_pcm_open_callback_t cmi_open_callbacks[2] = {
+ static const snd_pcm_open_callback_t cmi_open_callbacks[2] = {
snd_cmi8330_playback_open,
snd_cmi8330_capture_open
};
- if ((err = snd_pcm_new(card, (chip->type == CMI8329) ? "CMI8329" : "CMI8330", 0, 1, 1, &pcm)) < 0)
+ err = snd_pcm_new(card, (chip->type == CMI8329) ? "CMI8329" : "CMI8330", 0, 1, 1, &pcm);
+ if (err < 0)
return err;
- strcpy(pcm->name, (chip->type == CMI8329) ? "CMI8329" : "CMI8330");
+ strscpy(pcm->name, (chip->type == CMI8329) ? "CMI8329" : "CMI8330");
pcm->private_data = chip;
/* SB16 */
@@ -469,9 +456,8 @@ static int __devinit snd_cmi8330_pcm(struct snd_card *card, struct snd_cmi8330 *
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK].ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &chip->streams[SNDRV_PCM_STREAM_CAPTURE].ops);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
- 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, 64*1024, 128*1024);
chip->pcm = pcm;
return 0;
@@ -484,7 +470,6 @@ static int snd_cmi8330_suspend(struct snd_card *card)
struct snd_cmi8330 *acard = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(acard->pcm);
acard->wss->suspend(acard->wss);
snd_sbmixer_suspend(acard->sb);
return 0;
@@ -512,18 +497,17 @@ static int snd_cmi8330_resume(struct snd_card *card)
#define is_isapnp_selected(dev) 0
#endif
-#define PFX "cmi8330: "
-
-static int snd_cmi8330_card_new(int dev, struct snd_card **cardp)
+static int snd_cmi8330_card_new(struct device *pdev, int dev,
+ struct snd_card **cardp)
{
struct snd_card *card;
struct snd_cmi8330 *acard;
int err;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_cmi8330), &card);
+ err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_cmi8330), &card);
if (err < 0) {
- snd_printk(KERN_ERR PFX "could not get a new card\n");
+ dev_err(pdev, "could not get a new card\n");
return err;
}
acard = card->private_data;
@@ -532,7 +516,7 @@ static int snd_cmi8330_card_new(int dev, struct snd_card **cardp)
return 0;
}
-static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
+static int snd_cmi8330_probe(struct snd_card *card, int dev)
{
struct snd_cmi8330 *acard;
int i, err;
@@ -544,26 +528,27 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
wssdma[dev], -1,
WSS_HW_DETECT, 0, &acard->wss);
if (err < 0) {
- snd_printk(KERN_ERR PFX "AD1848 device busy??\n");
+ dev_err(card->dev, "AD1848 device busy??\n");
return err;
}
if (acard->wss->hardware != WSS_HW_CMI8330) {
- snd_printk(KERN_ERR PFX "AD1848 not found during probe\n");
+ dev_err(card->dev, "AD1848 not found during probe\n");
return -ENODEV;
}
- if ((err = snd_sbdsp_create(card, sbport[dev],
- sbirq[dev],
- snd_sb16dsp_interrupt,
- sbdma8[dev],
- sbdma16[dev],
- SB_HW_AUTO, &acard->sb)) < 0) {
- snd_printk(KERN_ERR PFX "SB16 device busy??\n");
+ err = snd_sbdsp_create(card, sbport[dev],
+ sbirq[dev],
+ snd_sb16dsp_interrupt,
+ sbdma8[dev],
+ sbdma16[dev],
+ SB_HW_AUTO, &acard->sb);
+ if (err < 0) {
+ dev_err(card->dev, "SB16 device busy??\n");
return err;
}
if (acard->sb->hardware != SB_HW_16) {
- snd_printk(KERN_ERR PFX "SB16 not found during probe\n");
- return err;
+ dev_err(card->dev, "SB16 not found during probe\n");
+ return -ENODEV;
}
snd_wss_out(acard->wss, CS4231_MISC_INFO, 0x40); /* switch on MODE2 */
@@ -571,22 +556,24 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
snd_wss_out(acard->wss, i,
snd_cmi8330_image[i - CMI8330_RMUX3D]);
- if ((err = snd_cmi8330_mixer(card, acard)) < 0) {
- snd_printk(KERN_ERR PFX "failed to create mixers\n");
+ err = snd_cmi8330_mixer(card, acard);
+ if (err < 0) {
+ dev_err(card->dev, "failed to create mixers\n");
return err;
}
- if ((err = snd_cmi8330_pcm(card, acard)) < 0) {
- snd_printk(KERN_ERR PFX "failed to create pcms\n");
+ err = snd_cmi8330_pcm(card, acard);
+ if (err < 0) {
+ dev_err(card->dev, "failed to create pcms\n");
return err;
}
if (fmport[dev] != SNDRV_AUTO_PORT) {
if (snd_opl3_create(card,
fmport[dev], fmport[dev] + 2,
OPL3_HW_AUTO, 0, &opl3) < 0) {
- snd_printk(KERN_ERR PFX
- "no OPL device at 0x%lx-0x%lx ?\n",
- fmport[dev], fmport[dev] + 2);
+ dev_err(card->dev,
+ "no OPL device at 0x%lx-0x%lx ?\n",
+ fmport[dev], fmport[dev] + 2);
} else {
err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
if (err < 0)
@@ -597,13 +584,13 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
if (mpuport[dev] != SNDRV_AUTO_PORT) {
if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
mpuport[dev], 0, mpuirq[dev],
- IRQF_DISABLED, NULL) < 0)
- printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n",
+ NULL) < 0)
+ dev_err(card->dev, "no MPU-401 device at 0x%lx.\n",
mpuport[dev]);
}
- strcpy(card->driver, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D");
- strcpy(card->shortname, (acard->type == CMI8329) ? "C-Media CMI8329" : "C-Media CMI8330/C3D");
+ strscpy(card->driver, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D");
+ strscpy(card->shortname, (acard->type == CMI8329) ? "C-Media CMI8329" : "C-Media CMI8330/C3D");
sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
card->shortname,
acard->wss->port,
@@ -613,48 +600,38 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
return snd_card_register(card);
}
-static int __devinit snd_cmi8330_isa_match(struct device *pdev,
- unsigned int dev)
+static int snd_cmi8330_isa_match(struct device *pdev,
+ unsigned int dev)
{
if (!enable[dev] || is_isapnp_selected(dev))
return 0;
if (wssport[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR PFX "specify wssport\n");
+ dev_err(pdev, "specify wssport\n");
return 0;
}
if (sbport[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR PFX "specify sbport\n");
+ dev_err(pdev, "specify sbport\n");
return 0;
}
return 1;
}
-static int __devinit snd_cmi8330_isa_probe(struct device *pdev,
- unsigned int dev)
+static int snd_cmi8330_isa_probe(struct device *pdev,
+ unsigned int dev)
{
struct snd_card *card;
int err;
- err = snd_cmi8330_card_new(dev, &card);
+ err = snd_cmi8330_card_new(pdev, dev, &card);
if (err < 0)
return err;
- snd_card_set_dev(card, pdev);
- if ((err = snd_cmi8330_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_cmi8330_probe(card, dev);
+ if (err < 0)
return err;
- }
dev_set_drvdata(pdev, card);
return 0;
}
-static int __devexit snd_cmi8330_isa_remove(struct device *devptr,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
- return 0;
-}
-
#ifdef CONFIG_PM
static int snd_cmi8330_isa_suspend(struct device *dev, unsigned int n,
pm_message_t state)
@@ -673,7 +650,6 @@ static int snd_cmi8330_isa_resume(struct device *dev, unsigned int n)
static struct isa_driver snd_cmi8330_driver = {
.match = snd_cmi8330_isa_match,
.probe = snd_cmi8330_isa_probe,
- .remove = __devexit_p(snd_cmi8330_isa_remove),
#ifdef CONFIG_PM
.suspend = snd_cmi8330_isa_suspend,
.resume = snd_cmi8330_isa_resume,
@@ -685,8 +661,8 @@ static struct isa_driver snd_cmi8330_driver = {
#ifdef CONFIG_PNP
-static int __devinit snd_cmi8330_pnp_detect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_cmi8330_pnp_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
static int dev;
struct snd_card *card;
@@ -699,30 +675,22 @@ static int __devinit snd_cmi8330_pnp_detect(struct pnp_card_link *pcard,
if (dev >= SNDRV_CARDS)
return -ENODEV;
- res = snd_cmi8330_card_new(dev, &card);
+ res = snd_cmi8330_card_new(&pcard->card->dev, dev, &card);
if (res < 0)
return res;
- if ((res = snd_cmi8330_pnp(dev, card->private_data, pcard, pid)) < 0) {
- snd_printk(KERN_ERR PFX "PnP detection failed\n");
- snd_card_free(card);
+ res = snd_cmi8330_pnp(dev, card->private_data, pcard, pid);
+ if (res < 0) {
+ dev_err(card->dev, "PnP detection failed\n");
return res;
}
- snd_card_set_dev(card, &pcard->card->dev);
- if ((res = snd_cmi8330_probe(card, dev)) < 0) {
- snd_card_free(card);
+ res = snd_cmi8330_probe(card, dev);
+ if (res < 0)
return res;
- }
pnp_set_card_drvdata(pcard, card);
dev++;
return 0;
}
-static void __devexit snd_cmi8330_pnp_remove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_cmi8330_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
{
@@ -740,7 +708,6 @@ static struct pnp_card_driver cmi8330_pnpc_driver = {
.name = "cmi8330",
.id_table = snd_cmi8330_pnpids,
.probe = snd_cmi8330_pnp_detect,
- .remove = __devexit_p(snd_cmi8330_pnp_remove),
#ifdef CONFIG_PM
.suspend = snd_cmi8330_pnp_suspend,
.resume = snd_cmi8330_pnp_resume,
diff --git a/sound/isa/cs423x/Makefile b/sound/isa/cs423x/Makefile
index 6d397e8d54ac..013a777d23fa 100644
--- a/sound/isa/cs423x/Makefile
+++ b/sound/isa/cs423x/Makefile
@@ -1,10 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-cs4231-objs := cs4231.o
-snd-cs4236-objs := cs4236.o cs4236_lib.o
+snd-cs4231-y := cs4231.o
+snd-cs4236-y := cs4236.o cs4236_lib.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_CS4231) += snd-cs4231.o
diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c
index cb9153e75b82..c87be4be6df1 100644
--- a/sound/isa/cs423x/cs4231.c
+++ b/sound/isa/cs423x/cs4231.c
@@ -1,23 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Generic driver for CS4231 chips
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Originally the CS4232/CS4232A driver, modified for use on CS4231 by
* Tugrul Galatali <galatalt@stuy.edu>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -25,7 +11,7 @@
#include <linux/isa.h>
#include <linux/time.h>
#include <linux/wait.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/wss.h>
#include <sound/mpu401.h>
@@ -37,11 +23,10 @@
MODULE_DESCRIPTION(CRD_NAME);
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Crystal Semiconductors,CS4231}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */
@@ -55,20 +40,20 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for " CRD_NAME " driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for " CRD_NAME " driver.");
-static int __devinit snd_cs4231_match(struct device *dev, unsigned int n)
+static int snd_cs4231_match(struct device *dev, unsigned int n)
{
if (!enable[n])
return 0;
@@ -88,72 +73,62 @@ static int __devinit snd_cs4231_match(struct device *dev, unsigned int n)
return 1;
}
-static int __devinit snd_cs4231_probe(struct device *dev, unsigned int n)
+static int snd_cs4231_probe(struct device *dev, unsigned int n)
{
struct snd_card *card;
struct snd_wss *chip;
- struct snd_pcm *pcm;
int error;
- error = snd_card_create(index[n], id[n], THIS_MODULE, 0, &card);
+ error = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE, 0, &card);
if (error < 0)
return error;
error = snd_wss_create(card, port[n], -1, irq[n], dma1[n], dma2[n],
WSS_HW_DETECT, 0, &chip);
if (error < 0)
- goto out;
+ return error;
card->private_data = chip;
- error = snd_wss_pcm(chip, 0, &pcm);
+ error = snd_wss_pcm(chip, 0);
if (error < 0)
- goto out;
+ return error;
- strcpy(card->driver, "CS4231");
- strcpy(card->shortname, pcm->name);
+ strscpy(card->driver, "CS4231", sizeof(card->driver));
+ strscpy(card->shortname, chip->pcm->name, sizeof(card->shortname));
- sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
- pcm->name, chip->port, irq[n], dma1[n]);
- if (dma2[n] >= 0)
- sprintf(card->longname + strlen(card->longname), "&%d", dma2[n]);
+ if (dma2[n] < 0)
+ scnprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, irq %d, dma %d",
+ chip->pcm->name, chip->port, irq[n], dma1[n]);
+ else
+ scnprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, irq %d, dma %d&%d",
+ chip->pcm->name, chip->port, irq[n], dma1[n], dma2[n]);
error = snd_wss_mixer(chip);
if (error < 0)
- goto out;
+ return error;
- error = snd_wss_timer(chip, 0, NULL);
+ error = snd_wss_timer(chip, 0);
if (error < 0)
- goto out;
+ return error;
if (mpu_port[n] > 0 && mpu_port[n] != SNDRV_AUTO_PORT) {
if (mpu_irq[n] == SNDRV_AUTO_IRQ)
mpu_irq[n] = -1;
if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232,
mpu_port[n], 0, mpu_irq[n],
- mpu_irq[n] >= 0 ? IRQF_DISABLED : 0,
NULL) < 0)
dev_warn(dev, "MPU401 not detected\n");
}
- snd_card_set_dev(card, dev);
-
error = snd_card_register(card);
if (error < 0)
- goto out;
+ return error;
dev_set_drvdata(dev, card);
return 0;
-
-out: snd_card_free(card);
- return error;
-}
-
-static int __devexit snd_cs4231_remove(struct device *dev, unsigned int n)
-{
- snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
- return 0;
}
#ifdef CONFIG_PM
@@ -181,7 +156,6 @@ static int snd_cs4231_resume(struct device *dev, unsigned int n)
static struct isa_driver snd_cs4231_driver = {
.match = snd_cs4231_match,
.probe = snd_cs4231_probe,
- .remove = __devexit_p(snd_cs4231_remove),
#ifdef CONFIG_PM
.suspend = snd_cs4231_suspend,
.resume = snd_cs4231_resume,
@@ -191,15 +165,4 @@ static struct isa_driver snd_cs4231_driver = {
}
};
-static int __init alsa_card_cs4231_init(void)
-{
- return isa_register_driver(&snd_cs4231_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_cs4231_exit(void)
-{
- isa_unregister_driver(&snd_cs4231_driver);
-}
-
-module_init(alsa_card_cs4231_init);
-module_exit(alsa_card_cs4231_exit);
+module_isa_driver(snd_cs4231_driver, SNDRV_CARDS);
diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c
index 999dc1e0fdbd..e36cc147651a 100644
--- a/sound/isa/cs423x/cs4236.c
+++ b/sound/isa/cs423x/cs4236.c
@@ -1,29 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for generic CS4232/CS4235/CS4236/CS4236B/CS4237B/CS4238B/CS4239 chips
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
#include <linux/pnp.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/wss.h>
#include <sound/mpu401.h>
@@ -33,40 +18,6 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cirrus Logic CS4232-9");
-MODULE_SUPPORTED_DEVICE("{{Turtle Beach,TBS-2000},"
- "{Turtle Beach,Tropez Plus},"
- "{SIC CrystalWave 32},"
- "{Hewlett Packard,Omnibook 5500},"
- "{TerraTec,Maestro 32/96},"
- "{Philips,PCA70PS}},"
- "{{Crystal Semiconductors,CS4235},"
- "{Crystal Semiconductors,CS4236},"
- "{Crystal Semiconductors,CS4237},"
- "{Crystal Semiconductors,CS4238},"
- "{Crystal Semiconductors,CS4239},"
- "{Acer,AW37},"
- "{Acer,AW35/Pro},"
- "{Crystal,3D},"
- "{Crystal Computer,TidalWave128},"
- "{Dell,Optiplex GX1},"
- "{Dell,Workstation 400 sound},"
- "{EliteGroup,P5TX-LA sound},"
- "{Gallant,SC-70P},"
- "{Gateway,E1000 Onboard CS4236B},"
- "{Genius,Sound Maker 3DJ},"
- "{Hewlett Packard,HP6330 sound},"
- "{IBM,PC 300PL sound},"
- "{IBM,Aptiva 2137 E24},"
- "{IBM,IntelliStation M Pro},"
- "{Intel,Marlin Spike Mobo CS4235},"
- "{Intel PR440FX Onboard},"
- "{Guillemot,MaxiSound 16 PnP},"
- "{NewClear,3D},"
- "{TerraTec,AudioSystem EWS64L/XL},"
- "{Typhoon Soundsystem,CS4236B},"
- "{Turtle Beach,Malibu},"
- "{Unknown,Digital PC 5000 Onboard}}");
-
MODULE_ALIAS("snd_cs4232");
#define IDENT "CS4232+"
@@ -74,9 +25,9 @@ MODULE_ALIAS("snd_cs4232");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
#endif
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static long cport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
@@ -98,23 +49,23 @@ MODULE_PARM_DESC(enable, "Enable " IDENT " soundcard.");
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard.");
#endif
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " IDENT " driver.");
-module_param_array(cport, long, NULL, 0444);
+module_param_hw_array(cport, long, ioport, NULL, 0444);
MODULE_PARM_DESC(cport, "Control port # for " IDENT " driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " IDENT " driver.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for " IDENT " driver.");
-module_param_array(sb_port, long, NULL, 0444);
+module_param_hw_array(sb_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(sb_port, "SB port # for " IDENT " driver (optional).");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " IDENT " driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " IDENT " driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for " IDENT " driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for " IDENT " driver.");
#ifdef CONFIG_PNP
@@ -125,7 +76,6 @@ static int pnp_registered;
struct snd_card_cs4236 {
struct snd_wss *chip;
- struct resource *res_sb_port;
#ifdef CONFIG_PNP
struct pnp_dev *wss;
struct pnp_dev *ctrl;
@@ -149,7 +99,7 @@ static const struct pnp_device_id snd_cs423x_pnpbiosids[] = {
MODULE_DEVICE_TABLE(pnp, snd_cs423x_pnpbiosids);
#define CS423X_ISAPNP_DRIVER "cs4232_isapnp"
-static struct pnp_card_device_id snd_cs423x_pnpids[] = {
+static const struct pnp_card_device_id snd_cs423x_pnpids[] = {
/* Philips PCA70PS */
{ .id = "CSC0d32", .devs = { { "CSC0000" }, { "CSC0010" }, { "PNPb006" } } },
/* TerraTec Maestro 32/96 (CS4232) */
@@ -251,10 +201,10 @@ static struct pnp_card_device_id snd_cs423x_pnpids[] = {
MODULE_DEVICE_TABLE(pnp_card, snd_cs423x_pnpids);
/* WSS initialization */
-static int __devinit snd_cs423x_pnp_init_wss(int dev, struct pnp_dev *pdev)
+static int snd_cs423x_pnp_init_wss(int dev, struct pnp_dev *pdev)
{
if (pnp_activate_dev(pdev) < 0) {
- printk(KERN_ERR IDENT " WSS PnP configure failed for WSS (out of resources?)\n");
+ dev_err(&pdev->dev, IDENT " WSS PnP configure failed for WSS (out of resources?)\n");
return -EBUSY;
}
port[dev] = pnp_port_start(pdev, 0);
@@ -264,48 +214,51 @@ static int __devinit snd_cs423x_pnp_init_wss(int dev, struct pnp_dev *pdev)
irq[dev] = pnp_irq(pdev, 0);
dma1[dev] = pnp_dma(pdev, 0);
dma2[dev] = pnp_dma(pdev, 1) == 4 ? -1 : (int)pnp_dma(pdev, 1);
- snd_printdd("isapnp WSS: wss port=0x%lx, fm port=0x%lx, sb port=0x%lx\n",
- port[dev], fm_port[dev], sb_port[dev]);
- snd_printdd("isapnp WSS: irq=%i, dma1=%i, dma2=%i\n",
- irq[dev], dma1[dev], dma2[dev]);
+ dev_dbg(&pdev->dev,
+ "isapnp WSS: wss port=0x%lx, fm port=0x%lx, sb port=0x%lx\n",
+ port[dev], fm_port[dev], sb_port[dev]);
+ dev_dbg(&pdev->dev,
+ "isapnp WSS: irq=%i, dma1=%i, dma2=%i\n",
+ irq[dev], dma1[dev], dma2[dev]);
return 0;
}
/* CTRL initialization */
-static int __devinit snd_cs423x_pnp_init_ctrl(int dev, struct pnp_dev *pdev)
+static int snd_cs423x_pnp_init_ctrl(int dev, struct pnp_dev *pdev)
{
if (pnp_activate_dev(pdev) < 0) {
- printk(KERN_ERR IDENT " CTRL PnP configure failed for WSS (out of resources?)\n");
+ dev_err(&pdev->dev, IDENT " CTRL PnP configure failed for WSS (out of resources?)\n");
return -EBUSY;
}
cport[dev] = pnp_port_start(pdev, 0);
- snd_printdd("isapnp CTRL: control port=0x%lx\n", cport[dev]);
+ dev_dbg(&pdev->dev, "isapnp CTRL: control port=0x%lx\n", cport[dev]);
return 0;
}
/* MPU initialization */
-static int __devinit snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev)
+static int snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev)
{
if (pnp_activate_dev(pdev) < 0) {
- printk(KERN_ERR IDENT " MPU401 PnP configure failed for WSS (out of resources?)\n");
+ dev_err(&pdev->dev, IDENT " MPU401 PnP configure failed for WSS (out of resources?)\n");
mpu_port[dev] = SNDRV_AUTO_PORT;
mpu_irq[dev] = SNDRV_AUTO_IRQ;
} else {
mpu_port[dev] = pnp_port_start(pdev, 0);
if (mpu_irq[dev] >= 0 &&
- pnp_irq_valid(pdev, 0) && pnp_irq(pdev, 0) >= 0) {
+ pnp_irq_valid(pdev, 0) &&
+ pnp_irq(pdev, 0) != (resource_size_t)-1) {
mpu_irq[dev] = pnp_irq(pdev, 0);
} else {
mpu_irq[dev] = -1; /* disable interrupt */
}
}
- snd_printdd("isapnp MPU: port=0x%lx, irq=%i\n", mpu_port[dev], mpu_irq[dev]);
+ dev_dbg(&pdev->dev, "isapnp MPU: port=0x%lx, irq=%i\n", mpu_port[dev], mpu_irq[dev]);
return 0;
}
-static int __devinit snd_card_cs423x_pnp(int dev, struct snd_card_cs4236 *acard,
- struct pnp_dev *pdev,
- struct pnp_dev *cdev)
+static int snd_card_cs423x_pnp(int dev, struct snd_card_cs4236 *acard,
+ struct pnp_dev *pdev,
+ struct pnp_dev *cdev)
{
acard->wss = pdev;
if (snd_cs423x_pnp_init_wss(dev, acard->wss) < 0)
@@ -317,9 +270,9 @@ static int __devinit snd_card_cs423x_pnp(int dev, struct snd_card_cs4236 *acard,
return 0;
}
-static int __devinit snd_card_cs423x_pnpc(int dev, struct snd_card_cs4236 *acard,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_card_cs423x_pnpc(int dev, struct snd_card_cs4236 *acard,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
acard->wss = pnp_request_card_device(card, id->devs[0].id, NULL);
if (acard->wss == NULL)
@@ -357,41 +310,36 @@ static int __devinit snd_card_cs423x_pnpc(int dev, struct snd_card_cs4236 *acard
#define is_isapnp_selected(dev) 0
#endif
-static void snd_card_cs4236_free(struct snd_card *card)
-{
- struct snd_card_cs4236 *acard = card->private_data;
-
- release_and_free_resource(acard->res_sb_port);
-}
-
-static int snd_cs423x_card_new(int dev, struct snd_card **cardp)
+static int snd_cs423x_card_new(struct device *pdev, int dev,
+ struct snd_card **cardp)
{
struct snd_card *card;
int err;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_cs4236), &card);
+ err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_cs4236), &card);
if (err < 0)
return err;
- card->private_free = snd_card_cs4236_free;
*cardp = card;
return 0;
}
-static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
+static int snd_cs423x_probe(struct snd_card *card, int dev)
{
struct snd_card_cs4236 *acard;
- struct snd_pcm *pcm;
struct snd_wss *chip;
struct snd_opl3 *opl3;
int err;
acard = card->private_data;
- if (sb_port[dev] > 0 && sb_port[dev] != SNDRV_AUTO_PORT)
- if ((acard->res_sb_port = request_region(sb_port[dev], 16, IDENT " SB")) == NULL) {
- printk(KERN_ERR IDENT ": unable to register SB port at 0x%lx\n", sb_port[dev]);
+ if (sb_port[dev] > 0 && sb_port[dev] != SNDRV_AUTO_PORT) {
+ if (!devm_request_region(card->dev, sb_port[dev], 16,
+ IDENT " SB")) {
+ dev_err(card->dev, IDENT ": unable to register SB port at 0x%lx\n",
+ sb_port[dev]);
return -EBUSY;
}
+ }
err = snd_cs4236_create(card, port[dev], cport[dev],
irq[dev],
@@ -403,7 +351,7 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
acard->chip = chip;
if (chip->hardware & WSS_HW_CS4236B_MASK) {
- err = snd_cs4236_pcm(chip, 0, &pcm);
+ err = snd_cs4236_pcm(chip, 0);
if (err < 0)
return err;
@@ -411,7 +359,7 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
if (err < 0)
return err;
} else {
- err = snd_wss_pcm(chip, 0, &pcm);
+ err = snd_wss_pcm(chip, 0);
if (err < 0)
return err;
@@ -419,17 +367,19 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
if (err < 0)
return err;
}
- strcpy(card->driver, pcm->name);
- strcpy(card->shortname, pcm->name);
- sprintf(card->longname, "%s at 0x%lx, irq %i, dma %i",
- pcm->name,
- chip->port,
- irq[dev],
- dma1[dev]);
- if (dma2[dev] >= 0)
- sprintf(card->longname + strlen(card->longname), "&%d", dma2[dev]);
-
- err = snd_wss_timer(chip, 0, NULL);
+ strscpy(card->driver, chip->pcm->name, sizeof(card->driver));
+ strscpy(card->shortname, chip->pcm->name, sizeof(card->shortname));
+ if (dma2[dev] < 0)
+ scnprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, irq %i, dma %i",
+ chip->pcm->name, chip->port, irq[dev], dma1[dev]);
+ else
+ scnprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, irq %i, dma %i&%d",
+ chip->pcm->name, chip->port, irq[dev], dma1[dev],
+ dma2[dev]);
+
+ err = snd_wss_timer(chip, 0);
if (err < 0)
return err;
@@ -437,9 +387,10 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
if (snd_opl3_create(card,
fm_port[dev], fm_port[dev] + 2,
OPL3_HW_OPL3_CS, 0, &opl3) < 0) {
- printk(KERN_WARNING IDENT ": OPL3 not detected\n");
+ dev_warn(card->dev, IDENT ": OPL3 not detected\n");
} else {
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0)
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
return err;
}
}
@@ -449,16 +400,15 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
mpu_irq[dev] = -1;
if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232,
mpu_port[dev], 0,
- mpu_irq[dev],
- mpu_irq[dev] >= 0 ? IRQF_DISABLED : 0, NULL) < 0)
- printk(KERN_WARNING IDENT ": MPU401 not detected\n");
+ mpu_irq[dev], NULL) < 0)
+ dev_warn(card->dev, IDENT ": MPU401 not detected\n");
}
return snd_card_register(card);
}
-static int __devinit snd_cs423x_isa_match(struct device *pdev,
- unsigned int dev)
+static int snd_cs423x_isa_match(struct device *pdev,
+ unsigned int dev)
{
if (!enable[dev] || is_isapnp_selected(dev))
return 0;
@@ -482,33 +432,22 @@ static int __devinit snd_cs423x_isa_match(struct device *pdev,
return 1;
}
-static int __devinit snd_cs423x_isa_probe(struct device *pdev,
- unsigned int dev)
+static int snd_cs423x_isa_probe(struct device *pdev,
+ unsigned int dev)
{
struct snd_card *card;
int err;
- err = snd_cs423x_card_new(dev, &card);
+ err = snd_cs423x_card_new(pdev, dev, &card);
if (err < 0)
return err;
- snd_card_set_dev(card, pdev);
- if ((err = snd_cs423x_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_cs423x_probe(card, dev);
+ if (err < 0)
return err;
- }
-
dev_set_drvdata(pdev, card);
return 0;
}
-static int __devexit snd_cs423x_isa_remove(struct device *pdev,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(pdev));
- dev_set_drvdata(pdev, NULL);
- return 0;
-}
-
#ifdef CONFIG_PM
static int snd_cs423x_suspend(struct snd_card *card)
{
@@ -541,7 +480,6 @@ static int snd_cs423x_isa_resume(struct device *dev, unsigned int n)
static struct isa_driver cs423x_isa_driver = {
.match = snd_cs423x_isa_match,
.probe = snd_cs423x_isa_probe,
- .remove = __devexit_p(snd_cs423x_isa_remove),
#ifdef CONFIG_PM
.suspend = snd_cs423x_isa_suspend,
.resume = snd_cs423x_isa_resume,
@@ -553,13 +491,13 @@ static struct isa_driver cs423x_isa_driver = {
#ifdef CONFIG_PNP
-static int __devinit snd_cs423x_pnpbios_detect(struct pnp_dev *pdev,
- const struct pnp_device_id *id)
+static int snd_cs423x_pnpbios_detect(struct pnp_dev *pdev,
+ const struct pnp_device_id *id)
{
static int dev;
int err;
struct snd_card *card;
- struct pnp_dev *cdev;
+ struct pnp_dev *cdev, *iter;
char cid[PNP_ID_LEN];
if (pnp_device_is_isapnp(pdev))
@@ -572,38 +510,31 @@ static int __devinit snd_cs423x_pnpbios_detect(struct pnp_dev *pdev,
return -ENODEV;
/* prepare second id */
- strcpy(cid, pdev->id[0].id);
+ strscpy(cid, pdev->id[0].id);
cid[5] = '1';
cdev = NULL;
- list_for_each_entry(cdev, &(pdev->protocol->devices), protocol_list) {
- if (!strcmp(cdev->id[0].id, cid))
+ list_for_each_entry(iter, &(pdev->protocol->devices), protocol_list) {
+ if (!strcmp(iter->id[0].id, cid)) {
+ cdev = iter;
break;
+ }
}
- err = snd_cs423x_card_new(dev, &card);
+ err = snd_cs423x_card_new(&pdev->dev, dev, &card);
if (err < 0)
return err;
err = snd_card_cs423x_pnp(dev, card->private_data, pdev, cdev);
if (err < 0) {
- printk(KERN_ERR "PnP BIOS detection failed for " IDENT "\n");
- snd_card_free(card);
+ dev_err(card->dev, "PnP BIOS detection failed for " IDENT "\n");
return err;
}
- snd_card_set_dev(card, &pdev->dev);
- if ((err = snd_cs423x_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_cs423x_probe(card, dev);
+ if (err < 0)
return err;
- }
pnp_set_drvdata(pdev, card);
dev++;
return 0;
}
-static void __devexit snd_cs423x_pnp_remove(struct pnp_dev *pdev)
-{
- snd_card_free(pnp_get_drvdata(pdev));
- pnp_set_drvdata(pdev, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_cs423x_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
{
@@ -620,15 +551,14 @@ static struct pnp_driver cs423x_pnp_driver = {
.name = "cs423x-pnpbios",
.id_table = snd_cs423x_pnpbiosids,
.probe = snd_cs423x_pnpbios_detect,
- .remove = __devexit_p(snd_cs423x_pnp_remove),
#ifdef CONFIG_PM
.suspend = snd_cs423x_pnp_suspend,
.resume = snd_cs423x_pnp_resume,
#endif
};
-static int __devinit snd_cs423x_pnpc_detect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_cs423x_pnpc_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
static int dev;
struct snd_card *card;
@@ -641,31 +571,23 @@ static int __devinit snd_cs423x_pnpc_detect(struct pnp_card_link *pcard,
if (dev >= SNDRV_CARDS)
return -ENODEV;
- res = snd_cs423x_card_new(dev, &card);
+ res = snd_cs423x_card_new(&pcard->card->dev, dev, &card);
if (res < 0)
return res;
- if ((res = snd_card_cs423x_pnpc(dev, card->private_data, pcard, pid)) < 0) {
- printk(KERN_ERR "isapnp detection failed and probing for " IDENT
+ res = snd_card_cs423x_pnpc(dev, card->private_data, pcard, pid);
+ if (res < 0) {
+ dev_err(card->dev, "isapnp detection failed and probing for " IDENT
" is not supported\n");
- snd_card_free(card);
return res;
}
- snd_card_set_dev(card, &pcard->card->dev);
- if ((res = snd_cs423x_probe(card, dev)) < 0) {
- snd_card_free(card);
+ res = snd_cs423x_probe(card, dev);
+ if (res < 0)
return res;
- }
pnp_set_card_drvdata(pcard, card);
dev++;
return 0;
}
-static void __devexit snd_cs423x_pnpc_remove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_cs423x_pnpc_suspend(struct pnp_card_link *pcard, pm_message_t state)
{
@@ -683,7 +605,6 @@ static struct pnp_card_driver cs423x_pnpc_driver = {
.name = CS423X_ISAPNP_DRIVER,
.id_table = snd_cs423x_pnpids,
.probe = snd_cs423x_pnpc_detect,
- .remove = __devexit_p(snd_cs423x_pnpc_remove),
#ifdef CONFIG_PM
.suspend = snd_cs423x_pnpc_suspend,
.resume = snd_cs423x_pnpc_resume,
diff --git a/sound/isa/cs423x/cs4236_lib.c b/sound/isa/cs423x/cs4236_lib.c
index c5adca300632..e2c29e831020 100644
--- a/sound/isa/cs423x/cs4236_lib.c
+++ b/sound/isa/cs423x/cs4236_lib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of CS4235/4236B/4237B/4238B/4239 chips
@@ -7,21 +8,6 @@
*
* Bugs:
* -----
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -53,7 +39,7 @@
* D7: consumer serial port enable (CS4237B,CS4238B)
* D6: channels status block reset (CS4237B,CS4238B)
* D5: user bit in sub-frame of digital audio data (CS4237B,CS4238B)
- * D4: validity bit bit in sub-frame of digital audio data (CS4237B,CS4238B)
+ * D4: validity bit in sub-frame of digital audio data (CS4237B,CS4238B)
*
* C5 lower channel status (digital serial data description) (CS4237B,CS4238B)
* D7-D6: first two bits of category code
@@ -79,7 +65,7 @@
*
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/time.h>
@@ -94,7 +80,7 @@
*
*/
-static unsigned char snd_cs4236_ext_map[18] = {
+static const unsigned char snd_cs4236_ext_map[18] = {
/* CS4236_LEFT_LINE */ 0xff,
/* CS4236_RIGHT_LINE */ 0xff,
/* CS4236_LEFT_MIC */ 0xdf,
@@ -138,7 +124,7 @@ static unsigned char snd_cs4236_ctrl_in(struct snd_wss *chip, unsigned char reg)
#define CLOCKS 8
-static struct snd_ratnum clocks[CLOCKS] = {
+static const struct snd_ratnum clocks[CLOCKS] = {
{ .num = 16934400, .den_min = 353, .den_max = 353, .den_step = 1 },
{ .num = 16934400, .den_min = 529, .den_max = 529, .den_step = 1 },
{ .num = 16934400, .den_min = 617, .den_max = 617, .den_step = 1 },
@@ -149,7 +135,7 @@ static struct snd_ratnum clocks[CLOCKS] = {
{ .num = 16934400/16, .den_min = 21, .den_max = 192, .den_step = 1 }
};
-static struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
+static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
.nrats = CLOCKS,
.rats = clocks,
};
@@ -183,10 +169,9 @@ static void snd_cs4236_playback_format(struct snd_wss *chip,
struct snd_pcm_hw_params *params,
unsigned char pdfr)
{
- unsigned long flags;
unsigned char rate = divisor_to_rate_register(params->rate_den);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
/* set fast playback format change and clean playback FIFO */
snd_wss_out(chip, CS4231_ALT_FEATURE_1,
chip->image[CS4231_ALT_FEATURE_1] | 0x10);
@@ -194,17 +179,15 @@ static void snd_cs4236_playback_format(struct snd_wss *chip,
snd_wss_out(chip, CS4231_ALT_FEATURE_1,
chip->image[CS4231_ALT_FEATURE_1] & ~0x10);
snd_cs4236_ext_out(chip, CS4236_DAC_RATE, rate);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static void snd_cs4236_capture_format(struct snd_wss *chip,
struct snd_pcm_hw_params *params,
unsigned char cdfr)
{
- unsigned long flags;
unsigned char rate = divisor_to_rate_register(params->rate_den);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
/* set fast capture format change and clean capture FIFO */
snd_wss_out(chip, CS4231_ALT_FEATURE_1,
chip->image[CS4231_ALT_FEATURE_1] | 0x20);
@@ -212,7 +195,6 @@ static void snd_cs4236_capture_format(struct snd_wss *chip,
snd_wss_out(chip, CS4231_ALT_FEATURE_1,
chip->image[CS4231_ALT_FEATURE_1] & ~0x20);
snd_cs4236_ext_out(chip, CS4236_ADC_RATE, rate);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
#ifdef CONFIG_PM
@@ -220,48 +202,45 @@ static void snd_cs4236_capture_format(struct snd_wss *chip,
static void snd_cs4236_suspend(struct snd_wss *chip)
{
int reg;
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
for (reg = 0; reg < 32; reg++)
chip->image[reg] = snd_wss_in(chip, reg);
for (reg = 0; reg < 18; reg++)
chip->eimage[reg] = snd_cs4236_ext_in(chip, CS4236_I23VAL(reg));
for (reg = 2; reg < 9; reg++)
chip->cimage[reg] = snd_cs4236_ctrl_in(chip, reg);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static void snd_cs4236_resume(struct snd_wss *chip)
{
int reg;
- unsigned long flags;
snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- for (reg = 0; reg < 32; reg++) {
- switch (reg) {
- case CS4236_EXT_REG:
- case CS4231_VERSION:
- case 27: /* why? CS4235 - master left */
- case 29: /* why? CS4235 - master right */
- break;
- default:
- snd_wss_out(chip, reg, chip->image[reg]);
- break;
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ for (reg = 0; reg < 32; reg++) {
+ switch (reg) {
+ case CS4236_EXT_REG:
+ case CS4231_VERSION:
+ case 27: /* why? CS4235 - master left */
+ case 29: /* why? CS4235 - master right */
+ break;
+ default:
+ snd_wss_out(chip, reg, chip->image[reg]);
+ break;
+ }
}
- }
- for (reg = 0; reg < 18; reg++)
- snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), chip->eimage[reg]);
- for (reg = 2; reg < 9; reg++) {
- switch (reg) {
- case 7:
- break;
- default:
- snd_cs4236_ctrl_out(chip, reg, chip->cimage[reg]);
+ for (reg = 0; reg < 18; reg++)
+ snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), chip->eimage[reg]);
+ for (reg = 2; reg < 9; reg++) {
+ switch (reg) {
+ case 7:
+ break;
+ default:
+ snd_cs4236_ctrl_out(chip, reg, chip->cimage[reg]);
+ }
}
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_wss_mce_down(chip);
}
@@ -293,8 +272,8 @@ int snd_cs4236_create(struct snd_card *card,
return err;
if ((chip->hardware & WSS_HW_CS4236B_MASK) == 0) {
- snd_printd("chip is not CS4236+, hardware=0x%x\n",
- chip->hardware);
+ dev_dbg(card->dev, "chip is not CS4236+, hardware=0x%x\n",
+ chip->hardware);
*rchip = chip;
return 0;
}
@@ -302,27 +281,25 @@ int snd_cs4236_create(struct snd_card *card,
{
int idx;
for (idx = 0; idx < 8; idx++)
- snd_printk(KERN_DEBUG "CD%i = 0x%x\n",
- idx, inb(chip->cport + idx));
+ dev_dbg(card->dev, "CD%i = 0x%x\n",
+ idx, inb(chip->cport + idx));
for (idx = 0; idx < 9; idx++)
- snd_printk(KERN_DEBUG "C%i = 0x%x\n",
- idx, snd_cs4236_ctrl_in(chip, idx));
+ dev_dbg(card->dev, "C%i = 0x%x\n",
+ idx, snd_cs4236_ctrl_in(chip, idx));
}
#endif
if (cport < 0x100 || cport == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR "please, specify control port "
- "for CS4236+ chips\n");
- snd_device_free(card, chip);
+ dev_err(card->dev, "please, specify control port for CS4236+ chips\n");
return -ENODEV;
}
ver1 = snd_cs4236_ctrl_in(chip, 1);
ver2 = snd_cs4236_ext_in(chip, CS4236_VERSION);
- snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n",
- cport, ver1, ver2);
+ dev_dbg(card->dev, "CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n",
+ cport, ver1, ver2);
if (ver1 != ver2) {
- snd_printk(KERN_ERR "CS4236+ chip detected, but "
- "control port 0x%lx is not valid\n", cport);
- snd_device_free(card, chip);
+ dev_err(card->dev,
+ "CS4236+ chip detected, but control port 0x%lx is not valid\n",
+ cport);
return -ENODEV;
}
snd_cs4236_ctrl_out(chip, 0, 0x00);
@@ -376,17 +353,14 @@ int snd_cs4236_create(struct snd_card *card,
return 0;
}
-int snd_cs4236_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm)
+int snd_cs4236_pcm(struct snd_wss *chip, int device)
{
- struct snd_pcm *pcm;
int err;
- err = snd_wss_pcm(chip, device, &pcm);
+ err = snd_wss_pcm(chip, device);
if (err < 0)
return err;
- pcm->info_flags &= ~SNDRV_PCM_INFO_JOINT_DUPLEX;
- if (rpcm)
- *rpcm = pcm;
+ chip->pcm->info_flags &= ~SNDRV_PCM_INFO_JOINT_DUPLEX;
return 0;
}
@@ -422,15 +396,13 @@ static int snd_cs4236_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_cs4236_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(reg)] >> shift) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert)
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
return 0;
@@ -439,7 +411,6 @@ static int snd_cs4236_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int snd_cs4236_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
@@ -451,11 +422,10 @@ static int snd_cs4236_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
if (invert)
val = mask - val;
val <<= shift;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
val = (chip->eimage[CS4236_REG(reg)] & ~(mask << shift)) | val;
change = val != chip->eimage[CS4236_REG(reg)];
snd_cs4236_ext_out(chip, reg, val);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -468,15 +438,13 @@ static int snd_cs4236_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int snd_cs4236_get_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = (chip->cimage[reg] >> shift) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert)
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
return 0;
@@ -485,7 +453,6 @@ static int snd_cs4236_get_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_cs4236_put_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
@@ -497,11 +464,10 @@ static int snd_cs4236_put_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_
if (invert)
val = mask - val;
val <<= shift;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
val = (chip->cimage[reg] & ~(mask << shift)) | val;
change = val != chip->cimage[reg];
snd_cs4236_ctrl_out(chip, reg, val);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -535,7 +501,6 @@ static int snd_cs4236_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_cs4236_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -543,10 +508,9 @@ static int snd_cs4236_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
int mask = (kcontrol->private_value >> 24) & 0xff;
int invert = (kcontrol->private_value >> 22) & 1;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = (chip->eimage[CS4236_REG(left_reg)] >> shift_left) & mask;
ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert) {
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
@@ -557,7 +521,6 @@ static int snd_cs4236_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -575,7 +538,7 @@ static int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
}
val1 <<= shift_left;
val2 <<= shift_right;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (left_reg != right_reg) {
val1 = (chip->eimage[CS4236_REG(left_reg)] & ~(mask << shift_left)) | val1;
val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2;
@@ -587,7 +550,6 @@ static int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
change = val1 != chip->eimage[CS4236_REG(left_reg)];
snd_cs4236_ext_out(chip, left_reg, val1);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -611,7 +573,6 @@ static int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int snd_cs4236_get_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -619,10 +580,9 @@ static int snd_cs4236_get_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_
int mask = (kcontrol->private_value >> 24) & 0xff;
int invert = (kcontrol->private_value >> 22) & 1;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
ucontrol->value.integer.value[1] = (chip->eimage[CS4236_REG(right_reg)] >> shift_right) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert) {
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
@@ -633,7 +593,6 @@ static int snd_cs4236_get_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_cs4236_put_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -651,13 +610,12 @@ static int snd_cs4236_put_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_
}
val1 <<= shift_left;
val2 <<= shift_right;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2;
change = val1 != chip->image[left_reg] || val2 != chip->eimage[CS4236_REG(right_reg)];
snd_wss_out(chip, left_reg, val1);
snd_cs4236_ext_out(chip, right_reg, val2);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -677,31 +635,27 @@ static inline int snd_cs4236_mixer_master_digital_invert_volume(int vol)
static int snd_cs4236_get_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & 0x7f);
ucontrol->value.integer.value[1] = snd_cs4236_mixer_master_digital_invert_volume(chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & 0x7f);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
static int snd_cs4236_put_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned short val1, val2;
val1 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[0] & 0x7f);
val2 = snd_cs4236_mixer_master_digital_invert_volume(ucontrol->value.integer.value[1] & 0x7f);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
val1 = (chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] & ~0x7f) | val1;
val2 = (chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)] & ~0x7f) | val2;
change = val1 != chip->eimage[CS4236_REG(CS4236_LEFT_MASTER)] || val2 != chip->eimage[CS4236_REG(CS4236_RIGHT_MASTER)];
snd_cs4236_ext_out(chip, CS4236_LEFT_MASTER, val1);
snd_cs4236_ext_out(chip, CS4236_RIGHT_MASTER, val2);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -738,31 +692,27 @@ static inline int snd_cs4235_mixer_output_accu_set_volume(int vol)
static int snd_cs4235_get_output_accu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_LEFT_MASTER]);
ucontrol->value.integer.value[1] = snd_cs4235_mixer_output_accu_get_volume(chip->image[CS4235_RIGHT_MASTER]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
static int snd_cs4235_put_output_accu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned short val1, val2;
val1 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[0]);
val2 = snd_cs4235_mixer_output_accu_set_volume(ucontrol->value.integer.value[1]);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
val1 = (chip->image[CS4235_LEFT_MASTER] & ~(3 << 5)) | val1;
val2 = (chip->image[CS4235_RIGHT_MASTER] & ~(3 << 5)) | val2;
change = val1 != chip->image[CS4235_LEFT_MASTER] || val2 != chip->image[CS4235_RIGHT_MASTER];
snd_wss_out(chip, CS4235_LEFT_MASTER, val1);
snd_wss_out(chip, CS4235_RIGHT_MASTER, val2);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -775,7 +725,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_2bit, -1800, 600, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
-static struct snd_kcontrol_new snd_cs4236_controls[] = {
+static const struct snd_kcontrol_new snd_cs4236_controls[] = {
CS4236_DOUBLE("Master Digital Playback Switch", 0,
CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1),
@@ -870,7 +820,7 @@ CS4236_DOUBLE1_TLV("Loopback Digital Playback Volume", 0,
static const DECLARE_TLV_DB_SCALE(db_scale_5bit_6db_max, -5600, 200, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_2bit_16db_max, -2400, 800, 0);
-static struct snd_kcontrol_new snd_cs4235_controls[] = {
+static const struct snd_kcontrol_new snd_cs4235_controls[] = {
WSS_DOUBLE("Master Playback Switch", 0,
CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1),
@@ -948,62 +898,58 @@ WSS_DOUBLE("Analog Loopback Switch", 0,
static int snd_cs4236_get_iec958_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = chip->image[CS4231_ALT_FEATURE_1] & 0x02 ? 1 : 0;
#if 0
- printk(KERN_DEBUG "get valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, "
- "C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
- snd_wss_in(chip, CS4231_ALT_FEATURE_1),
- snd_cs4236_ctrl_in(chip, 3),
- snd_cs4236_ctrl_in(chip, 4),
- snd_cs4236_ctrl_in(chip, 5),
- snd_cs4236_ctrl_in(chip, 6),
- snd_cs4236_ctrl_in(chip, 8));
+ dev_dbg(chip->card->dev,
+ "get valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
+ snd_wss_in(chip, CS4231_ALT_FEATURE_1),
+ snd_cs4236_ctrl_in(chip, 3),
+ snd_cs4236_ctrl_in(chip, 4),
+ snd_cs4236_ctrl_in(chip, 5),
+ snd_cs4236_ctrl_in(chip, 6),
+ snd_cs4236_ctrl_in(chip, 8));
#endif
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
static int snd_cs4236_put_iec958_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned short enable, val;
enable = ucontrol->value.integer.value[0] & 1;
- mutex_lock(&chip->mce_mutex);
+ guard(mutex)(&chip->mce_mutex);
snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- val = (chip->image[CS4231_ALT_FEATURE_1] & ~0x0e) | (0<<2) | (enable << 1);
- change = val != chip->image[CS4231_ALT_FEATURE_1];
- snd_wss_out(chip, CS4231_ALT_FEATURE_1, val);
- val = snd_cs4236_ctrl_in(chip, 4) | 0xc0;
- snd_cs4236_ctrl_out(chip, 4, val);
- udelay(100);
- val &= ~0x40;
- snd_cs4236_ctrl_out(chip, 4, val);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ val = (chip->image[CS4231_ALT_FEATURE_1] & ~0x0e) | (0<<2) | (enable << 1);
+ change = val != chip->image[CS4231_ALT_FEATURE_1];
+ snd_wss_out(chip, CS4231_ALT_FEATURE_1, val);
+ val = snd_cs4236_ctrl_in(chip, 4) | 0xc0;
+ snd_cs4236_ctrl_out(chip, 4, val);
+ udelay(100);
+ val &= ~0x40;
+ snd_cs4236_ctrl_out(chip, 4, val);
+ }
snd_wss_mce_down(chip);
- mutex_unlock(&chip->mce_mutex);
#if 0
- printk(KERN_DEBUG "set valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, "
- "C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
- snd_wss_in(chip, CS4231_ALT_FEATURE_1),
- snd_cs4236_ctrl_in(chip, 3),
- snd_cs4236_ctrl_in(chip, 4),
- snd_cs4236_ctrl_in(chip, 5),
- snd_cs4236_ctrl_in(chip, 6),
- snd_cs4236_ctrl_in(chip, 8));
+ dev_dbg(chip->card->dev,
+ "set valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n",
+ snd_wss_in(chip, CS4231_ALT_FEATURE_1),
+ snd_cs4236_ctrl_in(chip, 3),
+ snd_cs4236_ctrl_in(chip, 4),
+ snd_cs4236_ctrl_in(chip, 5),
+ snd_cs4236_ctrl_in(chip, 6),
+ snd_cs4236_ctrl_in(chip, 8));
#endif
return change;
}
-static struct snd_kcontrol_new snd_cs4236_iec958_controls[] = {
+static const struct snd_kcontrol_new snd_cs4236_iec958_controls[] = {
CS4236_IEC958_ENABLE("IEC958 Output Enable", 0),
CS4236_SINGLEC("IEC958 Output Validity", 0, 4, 4, 1, 0),
CS4236_SINGLEC("IEC958 Output User", 0, 4, 5, 1, 0),
@@ -1012,12 +958,12 @@ CS4236_SINGLEC("IEC958 Output Channel Status Low", 0, 5, 1, 127, 0),
CS4236_SINGLEC("IEC958 Output Channel Status High", 0, 6, 0, 255, 0)
};
-static struct snd_kcontrol_new snd_cs4236_3d_controls_cs4235[] = {
+static const struct snd_kcontrol_new snd_cs4236_3d_controls_cs4235[] = {
CS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0),
CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1)
};
-static struct snd_kcontrol_new snd_cs4236_3d_controls_cs4237[] = {
+static const struct snd_kcontrol_new snd_cs4236_3d_controls_cs4237[] = {
CS4236_SINGLEC("3D Control - Switch", 0, 3, 7, 1, 0),
CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1),
CS4236_SINGLEC("3D Control - Center", 0, 2, 0, 15, 1),
@@ -1025,7 +971,7 @@ CS4236_SINGLEC("3D Control - Mono", 0, 3, 6, 1, 0),
CS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0)
};
-static struct snd_kcontrol_new snd_cs4236_3d_controls_cs4238[] = {
+static const struct snd_kcontrol_new snd_cs4236_3d_controls_cs4238[] = {
CS4236_SINGLEC("3D Control - Switch", 0, 3, 4, 1, 0),
CS4236_SINGLEC("3D Control - Space", 0, 2, 4, 15, 1),
CS4236_SINGLEC("3D Control - Volume", 0, 2, 0, 15, 1),
@@ -1037,22 +983,24 @@ int snd_cs4236_mixer(struct snd_wss *chip)
struct snd_card *card;
unsigned int idx, count;
int err;
- struct snd_kcontrol_new *kcontrol;
+ const struct snd_kcontrol_new *kcontrol;
if (snd_BUG_ON(!chip || !chip->card))
return -EINVAL;
card = chip->card;
- strcpy(card->mixername, snd_wss_chip_id(chip));
+ strscpy(card->mixername, snd_wss_chip_id(chip));
if (chip->hardware == WSS_HW_CS4235 ||
chip->hardware == WSS_HW_CS4239) {
for (idx = 0; idx < ARRAY_SIZE(snd_cs4235_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4235_controls[idx], chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4235_controls[idx], chip));
+ if (err < 0)
return err;
}
} else {
for (idx = 0; idx < ARRAY_SIZE(snd_cs4236_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_controls[idx], chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_controls[idx], chip));
+ if (err < 0)
return err;
}
}
@@ -1075,13 +1023,15 @@ int snd_cs4236_mixer(struct snd_wss *chip)
kcontrol = NULL;
}
for (idx = 0; idx < count; idx++, kcontrol++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(kcontrol, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(kcontrol, chip));
+ if (err < 0)
return err;
}
if (chip->hardware == WSS_HW_CS4237B ||
chip->hardware == WSS_HW_CS4238B) {
for (idx = 0; idx < ARRAY_SIZE(snd_cs4236_iec958_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_iec958_controls[idx], chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_iec958_controls[idx], chip));
+ if (err < 0)
return err;
}
}
diff --git a/sound/isa/es1688/Makefile b/sound/isa/es1688/Makefile
index aee1e4ddb22a..7d6c44a8eaad 100644
--- a/sound/isa/es1688/Makefile
+++ b/sound/isa/es1688/Makefile
@@ -1,10 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-es1688-lib-objs := es1688_lib.o
-snd-es1688-objs := es1688.o
+snd-es1688-lib-y := es1688_lib.o
+snd-es1688-y := es1688.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ES1688) += snd-es1688.o snd-es1688-lib.o
diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c
index 0cde8131a575..6a95dfb7600a 100644
--- a/sound/isa/es1688/es1688.c
+++ b/sound/isa/es1688/es1688.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for generic ESS AudioDrive ESx688 soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -25,7 +10,7 @@
#include <linux/isapnp.h>
#include <linux/time.h>
#include <linux/wait.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/es1688.h>
@@ -41,19 +26,14 @@
MODULE_DESCRIPTION(CRD_NAME);
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ESS,ES688 PnP AudioDrive,pnp:ESS0100},"
- "{ESS,ES1688 PnP AudioDrive,pnp:ESS0102},"
- "{ESS,ES688 AudioDrive,pnp:ESS6881},"
- "{ESS,ES1688 AudioDrive,pnp:ESS1681}}");
-
MODULE_ALIAS("snd_es968");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
+static bool isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
#endif
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */
static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* Usually 0x388 */
static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
@@ -71,17 +51,17 @@ module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
#endif
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
-module_param_array(irq, int, NULL, 0444);
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for ES1688 driver.");
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
-module_param_array(dma8, int, NULL, 0444);
+module_param_hw_array(dma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma8, "8-bit DMA # for " CRD_NAME " driver.");
#ifdef CONFIG_PNP
@@ -90,18 +70,18 @@ MODULE_PARM_DESC(dma8, "8-bit DMA # for " CRD_NAME " driver.");
#define is_isapnp_selected(dev) 0
#endif
-static int __devinit snd_es1688_match(struct device *dev, unsigned int n)
+static int snd_es1688_match(struct device *dev, unsigned int n)
{
return enable[n] && !is_isapnp_selected(n);
}
-static int __devinit snd_es1688_legacy_create(struct snd_card *card,
- struct device *dev, unsigned int n)
+static int snd_es1688_legacy_create(struct snd_card *card,
+ struct device *dev, unsigned int n)
{
struct snd_es1688 *chip = card->private_data;
- static long possible_ports[] = {0x220, 0x240, 0x260};
- static int possible_irqs[] = {5, 9, 10, 7, -1};
- static int possible_dmas[] = {1, 3, 0, -1};
+ static const long possible_ports[] = {0x220, 0x240, 0x260};
+ static const int possible_irqs[] = {5, 9, 10, 7, -1};
+ static const int possible_dmas[] = {1, 3, 0, -1};
int i, error;
@@ -134,14 +114,13 @@ static int __devinit snd_es1688_legacy_create(struct snd_card *card,
return error;
}
-static int __devinit snd_es1688_probe(struct snd_card *card, unsigned int n)
+static int snd_es1688_probe(struct snd_card *card, unsigned int n)
{
struct snd_es1688 *chip = card->private_data;
struct snd_opl3 *opl3;
- struct snd_pcm *pcm;
int error;
- error = snd_es1688_pcm(card, chip, 0, &pcm);
+ error = snd_es1688_pcm(card, chip, 0);
if (error < 0)
return error;
@@ -149,11 +128,11 @@ static int __devinit snd_es1688_probe(struct snd_card *card, unsigned int n)
if (error < 0)
return error;
- strlcpy(card->driver, "ES1688", sizeof(card->driver));
- strlcpy(card->shortname, pcm->name, sizeof(card->shortname));
- snprintf(card->longname, sizeof(card->longname),
- "%s at 0x%lx, irq %i, dma %i", pcm->name, chip->port,
- chip->irq, chip->dma8);
+ strscpy(card->driver, "ES1688", sizeof(card->driver));
+ strscpy(card->shortname, chip->pcm->name, sizeof(card->shortname));
+ scnprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx, irq %i, dma %i", chip->pcm->name, chip->port,
+ chip->irq, chip->dma8);
if (fm_port[n] == SNDRV_AUTO_PORT)
fm_port[n] = port[n]; /* share the same port */
@@ -174,7 +153,7 @@ static int __devinit snd_es1688_probe(struct snd_card *card, unsigned int n)
chip->mpu_port > 0) {
error = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688,
chip->mpu_port, 0,
- mpu_irq[n], IRQF_DISABLED, NULL);
+ mpu_irq[n], NULL);
if (error < 0)
return error;
}
@@ -182,45 +161,32 @@ static int __devinit snd_es1688_probe(struct snd_card *card, unsigned int n)
return snd_card_register(card);
}
-static int __devinit snd_es1688_isa_probe(struct device *dev, unsigned int n)
+static int snd_es1688_isa_probe(struct device *dev, unsigned int n)
{
struct snd_card *card;
int error;
- error = snd_card_create(index[n], id[n], THIS_MODULE,
- sizeof(struct snd_es1688), &card);
+ error = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE,
+ sizeof(struct snd_es1688), &card);
if (error < 0)
return error;
error = snd_es1688_legacy_create(card, dev, n);
if (error < 0)
- goto out;
-
- snd_card_set_dev(card, dev);
+ return error;
error = snd_es1688_probe(card, n);
if (error < 0)
- goto out;
+ return error;
dev_set_drvdata(dev, card);
return 0;
-out:
- snd_card_free(card);
- return error;
-}
-
-static int __devexit snd_es1688_isa_remove(struct device *dev, unsigned int n)
-{
- snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
- return 0;
}
static struct isa_driver snd_es1688_driver = {
.match = snd_es1688_match,
.probe = snd_es1688_isa_probe,
- .remove = __devexit_p(snd_es1688_isa_remove),
#if 0 /* FIXME */
.suspend = snd_es1688_suspend,
.resume = snd_es1688_resume,
@@ -233,9 +199,9 @@ static struct isa_driver snd_es1688_driver = {
static int snd_es968_pnp_is_probed;
#ifdef CONFIG_PNP
-static int __devinit snd_card_es968_pnp(struct snd_card *card, unsigned int n,
- struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_card_es968_pnp(struct snd_card *card, unsigned int n,
+ struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
struct snd_es1688 *chip = card->private_data;
struct pnp_dev *pdev;
@@ -247,7 +213,7 @@ static int __devinit snd_card_es968_pnp(struct snd_card *card, unsigned int n,
error = pnp_activate_dev(pdev);
if (error < 0) {
- snd_printk(KERN_ERR "ES968 pnp configure failure\n");
+ dev_err(card->dev, "ES968 pnp configure failure\n");
return error;
}
port[n] = pnp_port_start(pdev, 0);
@@ -258,13 +224,12 @@ static int __devinit snd_card_es968_pnp(struct snd_card *card, unsigned int n,
mpu_irq[n], dma8[n], ES1688_HW_AUTO);
}
-static int __devinit snd_es968_pnp_detect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_es968_pnp_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
struct snd_card *card;
static unsigned int dev;
int error;
- struct snd_es1688 *chip;
if (snd_es968_pnp_is_probed)
return -EBUSY;
@@ -275,18 +240,15 @@ static int __devinit snd_es968_pnp_detect(struct pnp_card_link *pcard,
if (dev == SNDRV_CARDS)
return -ENODEV;
- error = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_es1688), &card);
+ error = snd_devm_card_new(&pcard->card->dev,
+ index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_es1688), &card);
if (error < 0)
return error;
- chip = card->private_data;
error = snd_card_es968_pnp(card, dev, pcard, pid);
- if (error < 0) {
- snd_card_free(card);
+ if (error < 0)
return error;
- }
- snd_card_set_dev(card, &pcard->card->dev);
error = snd_es1688_probe(card, dev);
if (error < 0)
return error;
@@ -295,10 +257,8 @@ static int __devinit snd_es968_pnp_detect(struct pnp_card_link *pcard,
return 0;
}
-static void __devexit snd_es968_pnp_remove(struct pnp_card_link * pcard)
+static void snd_es968_pnp_remove(struct pnp_card_link *pcard)
{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
snd_es968_pnp_is_probed = 0;
}
@@ -307,10 +267,8 @@ static int snd_es968_pnp_suspend(struct pnp_card_link *pcard,
pm_message_t state)
{
struct snd_card *card = pnp_get_card_drvdata(pcard);
- struct snd_es1688 *chip = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
return 0;
}
@@ -325,7 +283,7 @@ static int snd_es968_pnp_resume(struct pnp_card_link *pcard)
}
#endif
-static struct pnp_card_device_id snd_es968_pnpids[] = {
+static const struct pnp_card_device_id snd_es968_pnpids[] = {
{ .id = "ESS0968", .devs = { { "@@@0968" }, } },
{ .id = "ESS0968", .devs = { { "ESS0968" }, } },
{ .id = "", } /* end */
@@ -338,7 +296,7 @@ static struct pnp_card_driver es968_pnpc_driver = {
.name = DEV_NAME " PnP",
.id_table = snd_es968_pnpids,
.probe = snd_es968_pnp_detect,
- .remove = __devexit_p(snd_es968_pnp_remove),
+ .remove = snd_es968_pnp_remove,
#ifdef CONFIG_PM
.suspend = snd_es968_pnp_suspend,
.resume = snd_es968_pnp_resume,
diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c
index 07676200496a..59987dbc9ae9 100644
--- a/sound/isa/es1688/es1688_lib.c
+++ b/sound/isa/es1688/es1688_lib.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of ESS ES1688/688/488 chip
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -24,11 +9,12 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/es1688.h>
#include <sound/initval.h>
-#include <asm/io.h>
#include <asm/dma.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -44,9 +30,7 @@ static int snd_es1688_dsp_command(struct snd_es1688 *chip, unsigned char val)
outb(val, ES1688P(chip, COMMAND));
return 1;
}
-#ifdef CONFIG_SND_DEBUG
- printk(KERN_DEBUG "snd_es1688_dsp_command: timeout (0x%x)\n", val);
-#endif
+ dev_dbg(chip->card->dev, "%s: timeout (0x%x)\n", __func__, val);
return 0;
}
@@ -57,7 +41,8 @@ static int snd_es1688_dsp_get_byte(struct snd_es1688 *chip)
for (i = 1000; i; i--)
if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80)
return inb(ES1688P(chip, READ));
- snd_printd("es1688 get byte failed: 0x%lx = 0x%x!!!\n", ES1688P(chip, DATA_AVAIL), inb(ES1688P(chip, DATA_AVAIL)));
+ dev_dbg(chip->card->dev, "es1688 get byte failed: 0x%lx = 0x%x!!!\n",
+ ES1688P(chip, DATA_AVAIL), inb(ES1688P(chip, DATA_AVAIL)));
return -ENODEV;
}
@@ -109,7 +94,8 @@ int snd_es1688_reset(struct snd_es1688 *chip)
udelay(30);
for (i = 0; i < 1000 && !(inb(ES1688P(chip, DATA_AVAIL)) & 0x80); i++);
if (inb(ES1688P(chip, READ)) != 0xaa) {
- snd_printd("ess_reset at 0x%lx: failed!!!\n", chip->port);
+ dev_dbg(chip->card->dev, "ess_reset at 0x%lx: failed!!!\n",
+ chip->port);
return -ENODEV;
}
snd_es1688_dsp_command(chip, 0xc6); /* enable extended mode */
@@ -119,85 +105,82 @@ EXPORT_SYMBOL(snd_es1688_reset);
static int snd_es1688_probe(struct snd_es1688 *chip)
{
- unsigned long flags;
- unsigned short major, minor, hw;
+ unsigned short major, minor;
int i;
/*
* initialization sequence
*/
- spin_lock_irqsave(&chip->reg_lock, flags); /* Some ESS1688 cards need this */
- inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
- inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
- inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
- inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */
- inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
- inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */
- inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
- inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
- inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */
- inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
- inb(ES1688P(chip, ENABLE0)); /* ENABLE0 */
-
- if (snd_es1688_reset(chip) < 0) {
- snd_printdd("ESS: [0x%lx] reset failed... 0x%x\n", chip->port, inb(ES1688P(chip, READ)));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return -ENODEV;
- }
- snd_es1688_dsp_command(chip, 0xe7); /* return identification */
-
- for (i = 1000, major = minor = 0; i; i--) {
- if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) {
- if (major == 0) {
- major = inb(ES1688P(chip, READ));
- } else {
- minor = inb(ES1688P(chip, READ));
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) { /* Some ESS1688 cards need this */
+ inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
+ inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
+ inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
+ inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */
+ inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
+ inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */
+ inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
+ inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
+ inb(ES1688P(chip, ENABLE2)); /* ENABLE2 */
+ inb(ES1688P(chip, ENABLE1)); /* ENABLE1 */
+ inb(ES1688P(chip, ENABLE0)); /* ENABLE0 */
+
+ if (snd_es1688_reset(chip) < 0) {
+ dev_dbg(chip->card->dev, "ESS: [0x%lx] reset failed... 0x%x\n",
+ chip->port, inb(ES1688P(chip, READ)));
+ return -ENODEV;
+ }
+ snd_es1688_dsp_command(chip, 0xe7); /* return identification */
+
+ for (i = 1000, major = minor = 0; i; i--) {
+ if (inb(ES1688P(chip, DATA_AVAIL)) & 0x80) {
+ if (major == 0)
+ major = inb(ES1688P(chip, READ));
+ else
+ minor = inb(ES1688P(chip, READ));
}
}
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
- snd_printdd("ESS: [0x%lx] found.. major = 0x%x, minor = 0x%x\n", chip->port, major, minor);
+ dev_dbg(chip->card->dev,
+ "ESS: [0x%lx] found.. major = 0x%x, minor = 0x%x\n",
+ chip->port, major, minor);
chip->version = (major << 8) | minor;
if (!chip->version)
return -ENODEV; /* probably SB */
- hw = ES1688_HW_AUTO;
switch (chip->version & 0xfff0) {
case 0x4880:
- snd_printk(KERN_ERR "[0x%lx] ESS: AudioDrive ES488 detected, "
- "but driver is in another place\n", chip->port);
+ dev_err(chip->card->dev,
+ "[0x%lx] ESS: AudioDrive ES488 detected, but driver is in another place\n",
+ chip->port);
return -ENODEV;
case 0x6880:
- hw = (chip->version & 0x0f) >= 8 ? ES1688_HW_1688 : ES1688_HW_688;
break;
default:
- snd_printk(KERN_ERR "[0x%lx] ESS: unknown AudioDrive chip "
- "with version 0x%x (Jazz16 soundcard?)\n",
- chip->port, chip->version);
+ dev_err(chip->card->dev,
+ "[0x%lx] ESS: unknown AudioDrive chip with version 0x%x (Jazz16 soundcard?)\n",
+ chip->port, chip->version);
return -ENODEV;
}
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */
- snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */
+ snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */
+ }
/* enable joystick, but disable OPL3 */
- spin_lock_irqsave(&chip->mixer_lock, flags);
- snd_es1688_mixer_write(chip, 0x40, 0x01);
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->mixer_lock) {
+ snd_es1688_mixer_write(chip, 0x40, 0x01);
+ }
return 0;
}
static int snd_es1688_init(struct snd_es1688 * chip, int enable)
{
- static int irqs[16] = {-1, -1, 0, -1, -1, 1, -1, 2, -1, 0, 3, -1, -1, -1, -1, -1};
- unsigned long flags;
+ static const int irqs[16] = {-1, -1, 0, -1, -1, 1, -1, 2, -1, 0, 3, -1, -1, -1, -1, -1};
int cfg, irq_bits, dma, dma_bits, tmp, tmp1;
/* ok.. setup MPU-401 port and joystick and OPL3 */
@@ -226,38 +209,36 @@ static int snd_es1688_init(struct snd_es1688 * chip, int enable)
}
}
}
-#if 0
- snd_printk(KERN_DEBUG "mpu cfg = 0x%x\n", cfg);
-#endif
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_mixer_write(chip, 0x40, cfg);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_mixer_write(chip, 0x40, cfg);
+ }
/* --- */
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_read(chip, 0xb1);
- snd_es1688_read(chip, 0xb2);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_read(chip, 0xb1);
+ snd_es1688_read(chip, 0xb2);
+ }
if (enable) {
cfg = 0xf0; /* enable only DMA counter interrupt */
irq_bits = irqs[chip->irq & 0x0f];
if (irq_bits < 0) {
- snd_printk(KERN_ERR "[0x%lx] ESS: bad IRQ %d "
- "for ES1688 chip!!\n",
- chip->port, chip->irq);
+ dev_err(chip->card->dev,
+ "[0x%lx] ESS: bad IRQ %d for ES1688 chip!!\n",
+ chip->port, chip->irq);
#if 0
irq_bits = 0;
cfg = 0x10;
#endif
return -EINVAL;
}
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_write(chip, 0xb1, cfg | (irq_bits << 2));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_write(chip, 0xb1, cfg | (irq_bits << 2));
+ }
cfg = 0xf0; /* extended mode DMA enable */
dma = chip->dma8;
if (dma > 3 || dma == 2) {
- snd_printk(KERN_ERR "[0x%lx] ESS: bad DMA channel %d "
- "for ES1688 chip!!\n", chip->port, dma);
+ dev_err(chip->card->dev,
+ "[0x%lx] ESS: bad DMA channel %d for ES1688 chip!!\n",
+ chip->port, dma);
#if 0
dma_bits = 0;
cfg = 0x00; /* disable all DMA */
@@ -268,20 +249,20 @@ static int snd_es1688_init(struct snd_es1688 * chip, int enable)
if (dma != 3)
dma_bits++;
}
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_write(chip, 0xb2, cfg | (dma_bits << 2));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_write(chip, 0xb2, cfg | (dma_bits << 2));
+ }
} else {
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */
- snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_write(chip, 0xb1, 0x10); /* disable IRQ */
+ snd_es1688_write(chip, 0xb2, 0x00); /* disable DMA */
+ }
+ }
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_read(chip, 0xb1);
+ snd_es1688_read(chip, 0xb2);
+ snd_es1688_reset(chip);
}
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_read(chip, 0xb1);
- snd_es1688_read(chip, 0xb2);
- snd_es1688_reset(chip);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -289,7 +270,7 @@ static int snd_es1688_init(struct snd_es1688 * chip, int enable)
*/
-static struct snd_ratnum clocks[2] = {
+static const struct snd_ratnum clocks[2] = {
{
.num = 795444,
.den_min = 1,
@@ -304,7 +285,7 @@ static struct snd_ratnum clocks[2] = {
}
};
-static struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
+static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
.nrats = 2,
.rats = clocks,
};
@@ -325,12 +306,6 @@ static void snd_es1688_set_rate(struct snd_es1688 *chip, struct snd_pcm_substrea
snd_es1688_write(chip, 0xa2, divider);
}
-static int snd_es1688_ioctl(struct snd_pcm_substream *substream,
- unsigned int cmd, void *arg)
-{
- return snd_pcm_lib_ioctl(substream, cmd, arg);
-}
-
static int snd_es1688_trigger(struct snd_es1688 *chip, int cmd, unsigned char value)
{
int val;
@@ -340,85 +315,70 @@ static int snd_es1688_trigger(struct snd_es1688 *chip, int cmd, unsigned char va
} else if (cmd != SNDRV_PCM_TRIGGER_START) {
return -EINVAL;
}
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
chip->trigger_value = value;
val = snd_es1688_read(chip, 0xb8);
- if ((val < 0) || (val & 0x0f) == value) {
- spin_unlock(&chip->reg_lock);
+ if ((val < 0) || (val & 0x0f) == value)
return -EINVAL; /* something is wrong */
- }
#if 0
- printk(KERN_DEBUG "trigger: val = 0x%x, value = 0x%x\n", val, value);
- printk(KERN_DEBUG "trigger: pointer = 0x%x\n",
- snd_dma_pointer(chip->dma8, chip->dma_size));
+ dev_dbg(chip->card->dev, "trigger: val = 0x%x, value = 0x%x\n", val, value);
+ dev_dbg(chip->card->dev, "trigger: pointer = 0x%x\n",
+ snd_dma_pointer(chip->dma8, chip->dma_size));
#endif
snd_es1688_write(chip, 0xb8, (val & 0xf0) | value);
- spin_unlock(&chip->reg_lock);
return 0;
}
-static int snd_es1688_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_es1688_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_es1688_playback_prepare(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_es1688 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
chip->dma_size = size;
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_reset(chip);
- snd_es1688_set_rate(chip, substream);
- snd_es1688_write(chip, 0xb8, 4); /* auto init DMA mode */
- snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels));
- snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */
- if (runtime->channels == 1) {
- if (snd_pcm_format_width(runtime->format) == 8) {
- /* 8. bit mono */
- snd_es1688_write(chip, 0xb6, 0x80);
- snd_es1688_write(chip, 0xb7, 0x51);
- snd_es1688_write(chip, 0xb7, 0xd0);
- } else {
- /* 16. bit mono */
- snd_es1688_write(chip, 0xb6, 0x00);
- snd_es1688_write(chip, 0xb7, 0x71);
- snd_es1688_write(chip, 0xb7, 0xf4);
- }
- } else {
- if (snd_pcm_format_width(runtime->format) == 8) {
- /* 8. bit stereo */
- snd_es1688_write(chip, 0xb6, 0x80);
- snd_es1688_write(chip, 0xb7, 0x51);
- snd_es1688_write(chip, 0xb7, 0x98);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_reset(chip);
+ snd_es1688_set_rate(chip, substream);
+ snd_es1688_write(chip, 0xb8, 4); /* auto init DMA mode */
+ snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels));
+ snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */
+ if (runtime->channels == 1) {
+ if (snd_pcm_format_width(runtime->format) == 8) {
+ /* 8. bit mono */
+ snd_es1688_write(chip, 0xb6, 0x80);
+ snd_es1688_write(chip, 0xb7, 0x51);
+ snd_es1688_write(chip, 0xb7, 0xd0);
+ } else {
+ /* 16. bit mono */
+ snd_es1688_write(chip, 0xb6, 0x00);
+ snd_es1688_write(chip, 0xb7, 0x71);
+ snd_es1688_write(chip, 0xb7, 0xf4);
+ }
} else {
- /* 16. bit stereo */
- snd_es1688_write(chip, 0xb6, 0x00);
- snd_es1688_write(chip, 0xb7, 0x71);
- snd_es1688_write(chip, 0xb7, 0xbc);
+ if (snd_pcm_format_width(runtime->format) == 8) {
+ /* 8. bit stereo */
+ snd_es1688_write(chip, 0xb6, 0x80);
+ snd_es1688_write(chip, 0xb7, 0x51);
+ snd_es1688_write(chip, 0xb7, 0x98);
+ } else {
+ /* 16. bit stereo */
+ snd_es1688_write(chip, 0xb6, 0x00);
+ snd_es1688_write(chip, 0xb7, 0x71);
+ snd_es1688_write(chip, 0xb7, 0xbc);
+ }
}
+ snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50);
+ snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50);
+ snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKON);
}
- snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50);
- snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50);
- snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKON);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
/* --- */
count = -count;
snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_write(chip, 0xa4, (unsigned char) count);
- snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_write(chip, 0xa4, (unsigned char) count);
+ snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8));
+ }
return 0;
}
@@ -431,51 +391,50 @@ static int snd_es1688_playback_trigger(struct snd_pcm_substream *substream,
static int snd_es1688_capture_prepare(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_es1688 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
chip->dma_size = size;
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_reset(chip);
- snd_es1688_set_rate(chip, substream);
- snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKOFF);
- snd_es1688_write(chip, 0xb8, 0x0e); /* auto init DMA mode */
- snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels));
- snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */
- if (runtime->channels == 1) {
- if (snd_pcm_format_width(runtime->format) == 8) {
- /* 8. bit mono */
- snd_es1688_write(chip, 0xb7, 0x51);
- snd_es1688_write(chip, 0xb7, 0xd0);
- } else {
- /* 16. bit mono */
- snd_es1688_write(chip, 0xb7, 0x71);
- snd_es1688_write(chip, 0xb7, 0xf4);
- }
- } else {
- if (snd_pcm_format_width(runtime->format) == 8) {
- /* 8. bit stereo */
- snd_es1688_write(chip, 0xb7, 0x51);
- snd_es1688_write(chip, 0xb7, 0x98);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_reset(chip);
+ snd_es1688_set_rate(chip, substream);
+ snd_es1688_dsp_command(chip, ES1688_DSP_CMD_SPKOFF);
+ snd_es1688_write(chip, 0xb8, 0x0e); /* auto init DMA mode */
+ snd_es1688_write(chip, 0xa8, (snd_es1688_read(chip, 0xa8) & ~0x03) | (3 - runtime->channels));
+ snd_es1688_write(chip, 0xb9, 2); /* demand mode (4 bytes/request) */
+ if (runtime->channels == 1) {
+ if (snd_pcm_format_width(runtime->format) == 8) {
+ /* 8. bit mono */
+ snd_es1688_write(chip, 0xb7, 0x51);
+ snd_es1688_write(chip, 0xb7, 0xd0);
+ } else {
+ /* 16. bit mono */
+ snd_es1688_write(chip, 0xb7, 0x71);
+ snd_es1688_write(chip, 0xb7, 0xf4);
+ }
} else {
- /* 16. bit stereo */
- snd_es1688_write(chip, 0xb7, 0x71);
- snd_es1688_write(chip, 0xb7, 0xbc);
+ if (snd_pcm_format_width(runtime->format) == 8) {
+ /* 8. bit stereo */
+ snd_es1688_write(chip, 0xb7, 0x51);
+ snd_es1688_write(chip, 0xb7, 0x98);
+ } else {
+ /* 16. bit stereo */
+ snd_es1688_write(chip, 0xb7, 0x71);
+ snd_es1688_write(chip, 0xb7, 0xbc);
+ }
}
+ snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50);
+ snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50);
}
- snd_es1688_write(chip, 0xb1, (snd_es1688_read(chip, 0xb1) & 0x0f) | 0x50);
- snd_es1688_write(chip, 0xb2, (snd_es1688_read(chip, 0xb2) & 0x0f) | 0x50);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
/* --- */
count = -count;
snd_dma_program(chip->dma8, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_es1688_write(chip, 0xa4, (unsigned char) count);
- snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_es1688_write(chip, 0xa4, (unsigned char) count);
+ snd_es1688_write(chip, 0xa5, (unsigned char) (count >> 8));
+ }
return 0;
}
@@ -525,7 +484,7 @@ static snd_pcm_uframes_t snd_es1688_capture_pointer(struct snd_pcm_substream *su
*/
-static struct snd_pcm_hardware snd_es1688_playback =
+static const struct snd_pcm_hardware snd_es1688_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -543,7 +502,7 @@ static struct snd_pcm_hardware snd_es1688_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_es1688_capture =
+static const struct snd_pcm_hardware snd_es1688_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -611,10 +570,9 @@ static int snd_es1688_capture_close(struct snd_pcm_substream *substream)
static int snd_es1688_free(struct snd_es1688 *chip)
{
- if (chip->res_port) {
+ if (chip->hardware != ES1688_HW_UNDEF)
snd_es1688_init(chip, 0);
- release_and_free_resource(chip->res_port);
- }
+ release_and_free_resource(chip->res_port);
if (chip->irq >= 0)
free_irq(chip->irq, (void *) chip);
if (chip->dma8 >= 0) {
@@ -646,7 +604,7 @@ int snd_es1688_create(struct snd_card *card,
int dma8,
unsigned short hardware)
{
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_es1688_dev_free,
};
@@ -654,21 +612,31 @@ int snd_es1688_create(struct snd_card *card,
if (chip == NULL)
return -ENOMEM;
+ chip->card = card;
chip->irq = -1;
chip->dma8 = -1;
+ chip->hardware = ES1688_HW_UNDEF;
- if ((chip->res_port = request_region(port + 4, 12, "ES1688")) == NULL) {
- snd_printk(KERN_ERR "es1688: can't grab port 0x%lx\n", port + 4);
- return -EBUSY;
+ chip->res_port = request_region(port + 4, 12, "ES1688");
+ if (chip->res_port == NULL) {
+ dev_err(card->dev, "es1688: can't grab port 0x%lx\n", port + 4);
+ err = -EBUSY;
+ goto exit;
}
- if (request_irq(irq, snd_es1688_interrupt, IRQF_DISABLED, "ES1688", (void *) chip)) {
- snd_printk(KERN_ERR "es1688: can't grab IRQ %d\n", irq);
- return -EBUSY;
+
+ err = request_irq(irq, snd_es1688_interrupt, 0, "ES1688", (void *) chip);
+ if (err < 0) {
+ dev_err(card->dev, "es1688: can't grab IRQ %d\n", irq);
+ goto exit;
}
+
chip->irq = irq;
- if (request_dma(dma8, "ES1688")) {
- snd_printk(KERN_ERR "es1688: can't grab DMA8 %d\n", dma8);
- return -EBUSY;
+ card->sync_irq = chip->irq;
+ err = request_dma(dma8, "ES1688");
+
+ if (err < 0) {
+ dev_err(card->dev, "es1688: can't grab DMA8 %d\n", dma8);
+ goto exit;
}
chip->dma8 = dma8;
@@ -684,40 +652,37 @@ int snd_es1688_create(struct snd_card *card,
err = snd_es1688_probe(chip);
if (err < 0)
- return err;
+ goto exit;
err = snd_es1688_init(chip, 1);
if (err < 0)
- return err;
+ goto exit;
/* Register device */
- return snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+exit:
+ if (err)
+ snd_es1688_free(chip);
+ return err;
}
-static struct snd_pcm_ops snd_es1688_playback_ops = {
+static const struct snd_pcm_ops snd_es1688_playback_ops = {
.open = snd_es1688_playback_open,
.close = snd_es1688_playback_close,
- .ioctl = snd_es1688_ioctl,
- .hw_params = snd_es1688_hw_params,
- .hw_free = snd_es1688_hw_free,
.prepare = snd_es1688_playback_prepare,
.trigger = snd_es1688_playback_trigger,
.pointer = snd_es1688_playback_pointer,
};
-static struct snd_pcm_ops snd_es1688_capture_ops = {
+static const struct snd_pcm_ops snd_es1688_capture_ops = {
.open = snd_es1688_capture_open,
.close = snd_es1688_capture_close,
- .ioctl = snd_es1688_ioctl,
- .hw_params = snd_es1688_hw_params,
- .hw_free = snd_es1688_hw_free,
.prepare = snd_es1688_capture_prepare,
.trigger = snd_es1688_capture_trigger,
.pointer = snd_es1688_capture_pointer,
};
-int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip,
- int device, struct snd_pcm **rpcm)
+int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, int device)
{
struct snd_pcm *pcm;
int err;
@@ -731,15 +696,11 @@ int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip,
pcm->private_data = chip;
pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
- sprintf(pcm->name, snd_es1688_chip_id(chip));
+ strscpy(pcm->name, snd_es1688_chip_id(chip));
chip->pcm = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
- 64*1024, 64*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev,
+ 64*1024, 64*1024);
return 0;
}
@@ -749,18 +710,12 @@ int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip,
static int snd_es1688_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[9] = {
+ static const char * const texts[8] = {
"Mic", "Mic Master", "CD", "AOUT",
"Mic1", "Mix", "Line", "Master"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 8;
- if (uinfo->value.enumerated.item > 7)
- uinfo->value.enumerated.item = 7;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 8, texts);
}
static int snd_es1688_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -773,19 +728,17 @@ static int snd_es1688_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
static int snd_es1688_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
unsigned char oval, nval;
int change;
if (ucontrol->value.enumerated.item[0] > 8)
return -EINVAL;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
oval = snd_es1688_mixer_read(chip, ES1688_REC_DEV);
nval = (ucontrol->value.enumerated.item[0] & 7) | (oval & ~15);
change = nval != oval;
if (change)
snd_es1688_mixer_write(chip, ES1688_REC_DEV, nval);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -809,15 +762,13 @@ static int snd_es1688_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_es1688_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = (snd_es1688_mixer_read(chip, reg) >> shift) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert)
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
return 0;
@@ -826,7 +777,6 @@ static int snd_es1688_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int snd_es1688_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
@@ -838,13 +788,12 @@ static int snd_es1688_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
if (invert)
nval = mask - nval;
nval <<= shift;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
oval = snd_es1688_mixer_read(chip, reg);
nval = (oval & ~(mask << shift)) | nval;
change = nval != oval;
if (change)
snd_es1688_mixer_write(chip, reg, nval);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -868,7 +817,6 @@ static int snd_es1688_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_es1688_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -877,7 +825,7 @@ static int snd_es1688_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
int invert = (kcontrol->private_value >> 22) & 1;
unsigned char left, right;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (left_reg < 0xa0)
left = snd_es1688_mixer_read(chip, left_reg);
else
@@ -889,7 +837,6 @@ static int snd_es1688_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
right = snd_es1688_read(chip, right_reg);
} else
right = left;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
ucontrol->value.integer.value[0] = (left >> shift_left) & mask;
ucontrol->value.integer.value[1] = (right >> shift_right) & mask;
if (invert) {
@@ -902,7 +849,6 @@ static int snd_es1688_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int snd_es1688_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_es1688 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -920,7 +866,7 @@ static int snd_es1688_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
}
val1 <<= shift_left;
val2 <<= shift_right;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (left_reg != right_reg) {
if (left_reg < 0xa0)
oval1 = snd_es1688_mixer_read(chip, left_reg);
@@ -958,11 +904,10 @@ static int snd_es1688_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
}
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
-static struct snd_kcontrol_new snd_es1688_controls[] = {
+static const struct snd_kcontrol_new snd_es1688_controls[] = {
ES1688_DOUBLE("Master Playback Volume", 0, ES1688_MASTER_DEV, ES1688_MASTER_DEV, 4, 0, 15, 0),
ES1688_DOUBLE("PCM Playback Volume", 0, ES1688_PCM_DEV, ES1688_PCM_DEV, 4, 0, 15, 0),
ES1688_DOUBLE("Line Playback Volume", 0, ES1688_LINE_DEV, ES1688_LINE_DEV, 4, 0, 15, 0),
@@ -984,7 +929,7 @@ ES1688_SINGLE("Capture Switch", 0, ES1688_REC_DEV, 4, 1, 1),
#define ES1688_INIT_TABLE_SIZE (sizeof(snd_es1688_init_table)/2)
-static unsigned char snd_es1688_init_table[][2] = {
+static const unsigned char snd_es1688_init_table[][2] = {
{ ES1688_MASTER_DEV, 0 },
{ ES1688_PCM_DEV, 0 },
{ ES1688_LINE_DEV, 0 },
@@ -1006,10 +951,11 @@ int snd_es1688_mixer(struct snd_card *card, struct snd_es1688 *chip)
if (snd_BUG_ON(!chip || !card))
return -EINVAL;
- strcpy(card->mixername, snd_es1688_chip_id(chip));
+ strscpy(card->mixername, snd_es1688_chip_id(chip));
for (idx = 0; idx < ARRAY_SIZE(snd_es1688_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es1688_controls[idx], chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_es1688_controls[idx], chip));
+ if (err < 0)
return err;
}
for (idx = 0; idx < ES1688_INIT_TABLE_SIZE; idx++) {
@@ -1027,19 +973,3 @@ EXPORT_SYMBOL(snd_es1688_mixer_write);
EXPORT_SYMBOL(snd_es1688_create);
EXPORT_SYMBOL(snd_es1688_pcm);
EXPORT_SYMBOL(snd_es1688_mixer);
-
-/*
- * INIT part
- */
-
-static int __init alsa_es1688_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_es1688_exit(void)
-{
-}
-
-module_init(alsa_es1688_init)
-module_exit(alsa_es1688_exit)
diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c
index fb4d6b34bbca..1da7b400a17b 100644
--- a/sound/isa/es18xx.c
+++ b/sound/isa/es18xx.c
@@ -1,23 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for generic ESS AudioDrive ES18xx soundcards
* Copyright (c) by Christian Fischbach <fishbach@pool.informatik.rwth-aachen.de>
* Copyright (c) by Abramo Bagnara <abramo@alsa-project.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/* GENERAL NOTES:
*
@@ -82,10 +67,10 @@
#include <linux/isa.h>
#include <linux/pnp.h>
#include <linux/isapnp.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/delay.h>
+#include <linux/io.h>
-#include <asm/io.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -97,14 +82,10 @@
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>
-#define PFX "es18xx: "
-
struct snd_es18xx {
+ struct snd_card *card;
unsigned long port; /* port of ESS chip */
unsigned long ctrl_port; /* Control port of ESS chip */
- struct resource *res_port;
- struct resource *res_mpu_port;
- struct resource *res_ctrl_port;
int irq; /* IRQ number of ESS chip */
int dma1; /* DMA1 */
int dma2; /* DMA2 */
@@ -156,6 +137,7 @@ struct snd_es18xx {
#define ES18XX_I2S 0x0200 /* I2S mixer control */
#define ES18XX_MUTEREC 0x0400 /* Record source can be muted */
#define ES18XX_CONTROL 0x0800 /* Has control ports */
+#define ES18XX_GPO_2BIT 0x1000 /* GPO0,1 controlled by PM port */
/* Power Management */
#define ES18XX_PM 0x07
@@ -182,7 +164,7 @@ static int snd_es18xx_dsp_command(struct snd_es18xx *chip, unsigned char val)
outb(val, chip->port + 0x0C);
return 0;
}
- snd_printk(KERN_ERR "dsp_command: timeout (0x%x)\n", val);
+ dev_err(chip->card->dev, "dsp_command: timeout (0x%x)\n", val);
return -EINVAL;
}
@@ -193,8 +175,8 @@ static int snd_es18xx_dsp_get_byte(struct snd_es18xx *chip)
for(i = MILLISECOND/10; i; i--)
if (inb(chip->port + 0x0C) & 0x40)
return inb(chip->port + 0x0A);
- snd_printk(KERN_ERR "dsp_get_byte failed: 0x%lx = 0x%x!!!\n",
- chip->port + 0x0A, inb(chip->port + 0x0A));
+ dev_err(chip->card->dev, "dsp_get_byte failed: 0x%lx = 0x%x!!!\n",
+ chip->port + 0x0A, inb(chip->port + 0x0A));
return -ENODEV;
}
@@ -203,40 +185,35 @@ static int snd_es18xx_dsp_get_byte(struct snd_es18xx *chip)
static int snd_es18xx_write(struct snd_es18xx *chip,
unsigned char reg, unsigned char data)
{
- unsigned long flags;
int ret;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ret = snd_es18xx_dsp_command(chip, reg);
if (ret < 0)
- goto end;
+ return ret;
ret = snd_es18xx_dsp_command(chip, data);
- end:
- spin_unlock_irqrestore(&chip->reg_lock, flags);
#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Reg %02x set to %02x\n", reg, data);
+ dev_dbg(chip->card->dev, "Reg %02x set to %02x\n", reg, data);
#endif
return ret;
}
static int snd_es18xx_read(struct snd_es18xx *chip, unsigned char reg)
{
- unsigned long flags;
int ret, data;
- spin_lock_irqsave(&chip->reg_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->reg_lock);
ret = snd_es18xx_dsp_command(chip, 0xC0);
if (ret < 0)
- goto end;
+ return ret;
ret = snd_es18xx_dsp_command(chip, reg);
if (ret < 0)
- goto end;
+ return ret;
data = snd_es18xx_dsp_get_byte(chip);
ret = data;
#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Reg %02x now is %02x (%d)\n", reg, data, ret);
+ dev_dbg(chip->card->dev, "Reg %02x now is %02x (%d)\n", reg, data, ret);
#endif
- end:
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return ret;
}
@@ -246,62 +223,55 @@ static int snd_es18xx_bits(struct snd_es18xx *chip, unsigned char reg,
{
int ret;
unsigned char old, new, oval;
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->reg_lock);
ret = snd_es18xx_dsp_command(chip, 0xC0);
if (ret < 0)
- goto end;
+ return ret;
ret = snd_es18xx_dsp_command(chip, reg);
if (ret < 0)
- goto end;
+ return ret;
ret = snd_es18xx_dsp_get_byte(chip);
- if (ret < 0) {
- goto end;
- }
+ if (ret < 0)
+ return ret;
old = ret;
oval = old & mask;
if (val != oval) {
ret = snd_es18xx_dsp_command(chip, reg);
if (ret < 0)
- goto end;
+ return ret;
new = (old & ~mask) | (val & mask);
ret = snd_es18xx_dsp_command(chip, new);
if (ret < 0)
- goto end;
+ return ret;
#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Reg %02x was %02x, set to %02x (%d)\n",
- reg, old, new, ret);
+ dev_dbg(chip->card->dev, "Reg %02x was %02x, set to %02x (%d)\n",
+ reg, old, new, ret);
#endif
}
- ret = oval;
- end:
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return ret;
+ return oval;
}
static inline void snd_es18xx_mixer_write(struct snd_es18xx *chip,
unsigned char reg, unsigned char data)
{
- unsigned long flags;
- spin_lock_irqsave(&chip->mixer_lock, flags);
+ guard(spinlock_irqsave)(&chip->mixer_lock);
outb(reg, chip->port + 0x04);
outb(data, chip->port + 0x05);
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Mixer reg %02x set to %02x\n", reg, data);
+ dev_dbg(chip->card->dev, "Mixer reg %02x set to %02x\n", reg, data);
#endif
}
static inline int snd_es18xx_mixer_read(struct snd_es18xx *chip, unsigned char reg)
{
- unsigned long flags;
int data;
- spin_lock_irqsave(&chip->mixer_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->mixer_lock);
outb(reg, chip->port + 0x04);
data = inb(chip->port + 0x05);
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Mixer reg %02x now is %02x\n", reg, data);
+ dev_dbg(chip->card->dev, "Mixer reg %02x now is %02x\n", reg, data);
#endif
return data;
}
@@ -311,8 +281,8 @@ static inline int snd_es18xx_mixer_bits(struct snd_es18xx *chip, unsigned char r
unsigned char mask, unsigned char val)
{
unsigned char old, new, oval;
- unsigned long flags;
- spin_lock_irqsave(&chip->mixer_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->mixer_lock);
outb(reg, chip->port + 0x04);
old = inb(chip->port + 0x05);
oval = old & mask;
@@ -320,11 +290,10 @@ static inline int snd_es18xx_mixer_bits(struct snd_es18xx *chip, unsigned char r
new = (old & ~mask) | (val & mask);
outb(new, chip->port + 0x05);
#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Mixer reg %02x was %02x, set to %02x\n",
- reg, old, new);
+ dev_dbg(chip->card->dev, "Mixer reg %02x was %02x, set to %02x\n",
+ reg, old, new);
#endif
}
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
return oval;
}
@@ -332,23 +301,22 @@ static inline int snd_es18xx_mixer_writable(struct snd_es18xx *chip, unsigned ch
unsigned char mask)
{
int old, expected, new;
- unsigned long flags;
- spin_lock_irqsave(&chip->mixer_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->mixer_lock);
outb(reg, chip->port + 0x04);
old = inb(chip->port + 0x05);
expected = old ^ mask;
outb(expected, chip->port + 0x05);
new = inb(chip->port + 0x05);
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Mixer reg %02x was %02x, set to %02x, now is %02x\n",
- reg, old, expected, new);
+ dev_dbg(chip->card->dev, "Mixer reg %02x was %02x, set to %02x, now is %02x\n",
+ reg, old, expected, new);
#endif
return expected == new;
}
-static int __devinit snd_es18xx_reset(struct snd_es18xx *chip)
+static int snd_es18xx_reset(struct snd_es18xx *chip)
{
int i;
outb(0x03, chip->port + 0x06);
@@ -368,7 +336,7 @@ static int snd_es18xx_reset_fifo(struct snd_es18xx *chip)
return 0;
}
-static struct snd_ratnum new_clocks[2] = {
+static const struct snd_ratnum new_clocks[2] = {
{
.num = 793800,
.den_min = 1,
@@ -383,12 +351,12 @@ static struct snd_ratnum new_clocks[2] = {
}
};
-static struct snd_pcm_hw_constraint_ratnums new_hw_constraints_clocks = {
+static const struct snd_pcm_hw_constraint_ratnums new_hw_constraints_clocks = {
.nrats = 2,
.rats = new_clocks,
};
-static struct snd_ratnum old_clocks[2] = {
+static const struct snd_ratnum old_clocks[2] = {
{
.num = 795444,
.den_min = 1,
@@ -403,7 +371,7 @@ static struct snd_ratnum old_clocks[2] = {
}
};
-static struct snd_pcm_hw_constraint_ratnums old_hw_constraints_clocks = {
+static const struct snd_pcm_hw_constraint_ratnums old_hw_constraints_clocks = {
.nrats = 2,
.rats = old_clocks,
};
@@ -448,7 +416,7 @@ static int snd_es18xx_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
- int shift, err;
+ int shift;
shift = 0;
if (params_channels(hw_params) == 2)
@@ -467,16 +435,9 @@ static int snd_es18xx_playback_hw_params(struct snd_pcm_substream *substream,
} else {
chip->dma1_shift = shift;
}
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
- return err;
return 0;
}
-static int snd_es18xx_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_es18xx_playback1_prepare(struct snd_es18xx *chip,
struct snd_pcm_substream *substream)
{
@@ -520,7 +481,7 @@ static int snd_es18xx_playback1_trigger(struct snd_es18xx *chip,
snd_es18xx_mixer_write(chip, 0x78, 0x93);
#ifdef AVOID_POPS
/* Avoid pops */
- udelay(100000);
+ mdelay(100);
if (chip->caps & ES18XX_PCM2)
/* Restore Audio 2 volume */
snd_es18xx_mixer_write(chip, 0x7C, chip->audio2_vol);
@@ -537,7 +498,7 @@ static int snd_es18xx_playback1_trigger(struct snd_es18xx *chip,
/* Stop DMA */
snd_es18xx_mixer_write(chip, 0x78, 0x00);
#ifdef AVOID_POPS
- udelay(25000);
+ mdelay(25);
if (chip->caps & ES18XX_PCM2)
/* Set Audio 2 volume to 0 */
snd_es18xx_mixer_write(chip, 0x7C, 0);
@@ -557,7 +518,7 @@ static int snd_es18xx_capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
- int shift, err;
+ int shift;
shift = 0;
if ((chip->caps & ES18XX_DUPLEX_MONO) &&
@@ -571,8 +532,6 @@ static int snd_es18xx_capture_hw_params(struct snd_pcm_substream *substream,
if (snd_pcm_format_width(params_format(hw_params)) == 16)
shift++;
chip->dma1_shift = shift;
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
- return err;
return 0;
}
@@ -596,7 +555,7 @@ static int snd_es18xx_capture_prepare(struct snd_pcm_substream *substream)
snd_es18xx_write(chip, 0xA5, count >> 8);
#ifdef AVOID_POPS
- udelay(100000);
+ mdelay(100);
#endif
/* Set format */
@@ -691,7 +650,7 @@ static int snd_es18xx_playback2_trigger(struct snd_es18xx *chip,
snd_es18xx_write(chip, 0xB8, 0x05);
#ifdef AVOID_POPS
/* Avoid pops */
- udelay(100000);
+ mdelay(100);
/* Enable Audio 1 */
snd_es18xx_dsp_command(chip, 0xD1);
#endif
@@ -705,7 +664,7 @@ static int snd_es18xx_playback2_trigger(struct snd_es18xx *chip,
snd_es18xx_write(chip, 0xB8, 0x00);
#ifdef AVOID_POPS
/* Avoid pops */
- udelay(25000);
+ mdelay(25);
/* Disable Audio 1 */
snd_es18xx_dsp_command(chip, 0xD3);
#endif
@@ -837,7 +796,7 @@ static snd_pcm_uframes_t snd_es18xx_capture_pointer(struct snd_pcm_substream *su
return pos >> chip->dma1_shift;
}
-static struct snd_pcm_hardware snd_es18xx_playback =
+static const struct snd_pcm_hardware snd_es18xx_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
@@ -857,7 +816,7 @@ static struct snd_pcm_hardware snd_es18xx_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_es18xx_capture =
+static const struct snd_pcm_hardware snd_es18xx_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
@@ -929,7 +888,6 @@ static int snd_es18xx_playback_close(struct snd_pcm_substream *substream)
else
chip->playback_b_substream = NULL;
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -938,7 +896,6 @@ static int snd_es18xx_capture_close(struct snd_pcm_substream *substream)
struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
chip->capture_a_substream = NULL;
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -964,49 +921,33 @@ static int snd_es18xx_capture_close(struct snd_pcm_substream *substream)
static int snd_es18xx_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts5Source[5] = {
+ static const char * const texts5Source[5] = {
"Mic", "CD", "Line", "Master", "Mix"
};
- static char *texts8Source[8] = {
+ static const char * const texts8Source[8] = {
"Mic", "Mic Master", "CD", "AOUT",
"Mic1", "Mix", "Line", "Master"
};
struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
switch (chip->version) {
case 0x1868:
case 0x1878:
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name,
- texts5Source[uinfo->value.enumerated.item]);
- break;
+ return snd_ctl_enum_info(uinfo, 1, 4, texts5Source);
case 0x1887:
case 0x1888:
- uinfo->value.enumerated.items = 5;
- if (uinfo->value.enumerated.item > 4)
- uinfo->value.enumerated.item = 4;
- strcpy(uinfo->value.enumerated.name, texts5Source[uinfo->value.enumerated.item]);
- break;
- case 0x1869: /* DS somewhat contradictory for 1869: could be be 5 or 8 */
+ return snd_ctl_enum_info(uinfo, 1, 5, texts5Source);
+ case 0x1869: /* DS somewhat contradictory for 1869: could be 5 or 8 */
case 0x1879:
- uinfo->value.enumerated.items = 8;
- if (uinfo->value.enumerated.item > 7)
- uinfo->value.enumerated.item = 7;
- strcpy(uinfo->value.enumerated.name, texts8Source[uinfo->value.enumerated.item]);
- break;
+ return snd_ctl_enum_info(uinfo, 1, 8, texts8Source);
default:
return -EINVAL;
}
- return 0;
}
static int snd_es18xx_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- static unsigned char invMap4Source[8] = {0, 0, 1, 1, 0, 0, 2, 3};
+ static const unsigned char invMap4Source[8] = {0, 0, 1, 1, 0, 0, 2, 3};
struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
int muxSource = snd_es18xx_mixer_read(chip, 0x1c) & 0x07;
if (!(chip->version == 0x1869 || chip->version == 0x1879)) {
@@ -1023,7 +964,7 @@ static int snd_es18xx_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
static int snd_es18xx_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- static unsigned char map4Source[4] = {0, 2, 6, 7};
+ static const unsigned char map4Source[4] = {0, 2, 6, 7};
struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
unsigned char val = ucontrol->value.enumerated.item[0];
unsigned char retVal = 0;
@@ -1039,6 +980,7 @@ static int snd_es18xx_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
val = 3;
} else
retVal = snd_es18xx_mixer_bits(chip, 0x7a, 0x08, 0x00) != 0x00;
+ fallthrough;
/* 4 source chips */
case 0x1868:
case 0x1878:
@@ -1136,11 +1078,14 @@ static int snd_es18xx_reg_read(struct snd_es18xx *chip, unsigned char reg)
return snd_es18xx_read(chip, reg);
}
-#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, invert) \
+#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, flags) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
.info = snd_es18xx_info_single, \
.get = snd_es18xx_get_single, .put = snd_es18xx_put_single, \
- .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
+ .private_value = reg | (shift << 8) | (mask << 16) | (flags << 24) }
+
+#define ES18XX_FL_INVERT (1 << 0)
+#define ES18XX_FL_PMPORT (1 << 1)
static int snd_es18xx_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
@@ -1159,10 +1104,14 @@ static int snd_es18xx_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
- int invert = (kcontrol->private_value >> 24) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & ES18XX_FL_INVERT;
+ int pm_port = (kcontrol->private_value >> 24) & ES18XX_FL_PMPORT;
int val;
-
- val = snd_es18xx_reg_read(chip, reg);
+
+ if (pm_port)
+ val = inb(chip->port + ES18XX_PM);
+ else
+ val = snd_es18xx_reg_read(chip, reg);
ucontrol->value.integer.value[0] = (val >> shift) & mask;
if (invert)
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
@@ -1175,7 +1124,8 @@ static int snd_es18xx_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
- int invert = (kcontrol->private_value >> 24) & 0xff;
+ int invert = (kcontrol->private_value >> 24) & ES18XX_FL_INVERT;
+ int pm_port = (kcontrol->private_value >> 24) & ES18XX_FL_PMPORT;
unsigned char val;
val = (ucontrol->value.integer.value[0] & mask);
@@ -1183,6 +1133,15 @@ static int snd_es18xx_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
val = mask - val;
mask <<= shift;
val <<= shift;
+ if (pm_port) {
+ unsigned char cur = inb(chip->port + ES18XX_PM);
+
+ if ((cur & mask) == val)
+ return 0;
+ outb((cur & ~mask) | val, chip->port + ES18XX_PM);
+ return 1;
+ }
+
return snd_es18xx_reg_bits(chip, reg, mask, val) != val;
}
@@ -1269,7 +1228,7 @@ static int snd_es18xx_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
* The controls that are universal to all chipsets are fully initialized
* here.
*/
-static struct snd_kcontrol_new snd_es18xx_base_controls[] = {
+static const struct snd_kcontrol_new snd_es18xx_base_controls[] = {
ES18XX_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0),
ES18XX_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
ES18XX_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0),
@@ -1288,7 +1247,7 @@ ES18XX_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0),
}
};
-static struct snd_kcontrol_new snd_es18xx_recmix_controls[] = {
+static const struct snd_kcontrol_new snd_es18xx_recmix_controls[] = {
ES18XX_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0),
ES18XX_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0),
ES18XX_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0),
@@ -1300,35 +1259,35 @@ ES18XX_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0)
/*
* The chipset specific mixer controls
*/
-static struct snd_kcontrol_new snd_es18xx_opt_speaker =
+static const struct snd_kcontrol_new snd_es18xx_opt_speaker =
ES18XX_SINGLE("Beep Playback Volume", 0, 0x3c, 0, 7, 0);
-static struct snd_kcontrol_new snd_es18xx_opt_1869[] = {
-ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
+static const struct snd_kcontrol_new snd_es18xx_opt_1869[] = {
+ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, ES18XX_FL_INVERT),
ES18XX_SINGLE("Video Playback Switch", 0, 0x7f, 0, 1, 0),
ES18XX_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
ES18XX_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0)
};
-static struct snd_kcontrol_new snd_es18xx_opt_1878 =
+static const struct snd_kcontrol_new snd_es18xx_opt_1878 =
ES18XX_DOUBLE("Video Playback Volume", 0, 0x68, 0x68, 4, 0, 15, 0);
-static struct snd_kcontrol_new snd_es18xx_opt_1879[] = {
+static const struct snd_kcontrol_new snd_es18xx_opt_1879[] = {
ES18XX_SINGLE("Video Playback Switch", 0, 0x71, 6, 1, 0),
ES18XX_DOUBLE("Video Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
ES18XX_DOUBLE("Video Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0)
};
-static struct snd_kcontrol_new snd_es18xx_pcm1_controls[] = {
+static const struct snd_kcontrol_new snd_es18xx_pcm1_controls[] = {
ES18XX_DOUBLE("PCM Playback Volume", 0, 0x14, 0x14, 4, 0, 15, 0),
};
-static struct snd_kcontrol_new snd_es18xx_pcm2_controls[] = {
+static const struct snd_kcontrol_new snd_es18xx_pcm2_controls[] = {
ES18XX_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0),
ES18XX_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0)
};
-static struct snd_kcontrol_new snd_es18xx_spatializer_controls[] = {
+static const struct snd_kcontrol_new snd_es18xx_spatializer_controls[] = {
ES18XX_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1339,13 +1298,13 @@ ES18XX_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),
}
};
-static struct snd_kcontrol_new snd_es18xx_micpre1_control =
+static const struct snd_kcontrol_new snd_es18xx_micpre1_control =
ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0xa9, 2, 1, 0);
-static struct snd_kcontrol_new snd_es18xx_micpre2_control =
+static const struct snd_kcontrol_new snd_es18xx_micpre2_control =
ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0);
-static struct snd_kcontrol_new snd_es18xx_hw_volume_controls[] = {
+static const struct snd_kcontrol_new snd_es18xx_hw_volume_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Hardware Master Playback Volume",
@@ -1363,30 +1322,32 @@ static struct snd_kcontrol_new snd_es18xx_hw_volume_controls[] = {
ES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0),
};
-static int __devinit snd_es18xx_config_read(struct snd_es18xx *chip, unsigned char reg)
-{
- int data;
+static const struct snd_kcontrol_new snd_es18xx_opt_gpo_2bit[] = {
+ES18XX_SINGLE("GPO0 Switch", 0, ES18XX_PM, 0, 1, ES18XX_FL_PMPORT),
+ES18XX_SINGLE("GPO1 Switch", 0, ES18XX_PM, 1, 1, ES18XX_FL_PMPORT),
+};
+static int snd_es18xx_config_read(struct snd_es18xx *chip, unsigned char reg)
+{
outb(reg, chip->ctrl_port);
- data = inb(chip->ctrl_port + 1);
- return data;
+ return inb(chip->ctrl_port + 1);
}
-static void __devinit snd_es18xx_config_write(struct snd_es18xx *chip,
- unsigned char reg, unsigned char data)
+static void snd_es18xx_config_write(struct snd_es18xx *chip,
+ unsigned char reg, unsigned char data)
{
/* No need for spinlocks, this function is used only in
otherwise protected init code */
outb(reg, chip->ctrl_port);
outb(data, chip->ctrl_port + 1);
#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Config reg %02x set to %02x\n", reg, data);
+ dev_dbg(chip->card->dev, "Config reg %02x set to %02x\n", reg, data);
#endif
}
-static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip,
- unsigned long mpu_port,
- unsigned long fm_port)
+static int snd_es18xx_initialize(struct snd_es18xx *chip,
+ unsigned long mpu_port,
+ unsigned long fm_port)
{
int mask = 0;
@@ -1449,7 +1410,7 @@ static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip,
irqmask = 3;
break;
default:
- snd_printk(KERN_ERR "invalid irq %d\n", chip->irq);
+ dev_err(chip->card->dev, "invalid irq %d\n", chip->irq);
return -ENODEV;
}
switch (chip->dma1) {
@@ -1463,7 +1424,7 @@ static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip,
dma1mask = 3;
break;
default:
- snd_printk(KERN_ERR "invalid dma1 %d\n", chip->dma1);
+ dev_err(chip->card->dev, "invalid dma1 %d\n", chip->dma1);
return -ENODEV;
}
switch (chip->dma2) {
@@ -1480,7 +1441,7 @@ static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip,
dma2mask = 3;
break;
default:
- snd_printk(KERN_ERR "invalid dma2 %d\n", chip->dma2);
+ dev_err(chip->card->dev, "invalid dma2 %d\n", chip->dma2);
return -ENODEV;
}
@@ -1549,13 +1510,13 @@ static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip,
return 0;
}
-static int __devinit snd_es18xx_identify(struct snd_es18xx *chip)
+static int snd_es18xx_identify(struct snd_card *card, struct snd_es18xx *chip)
{
int hi,lo;
/* reset */
if (snd_es18xx_reset(chip) < 0) {
- snd_printk(KERN_ERR "reset at 0x%lx failed!!!\n", chip->port);
+ dev_err(card->dev, "reset at 0x%lx failed!!!\n", chip->port);
return -ENODEV;
}
@@ -1591,8 +1552,9 @@ static int __devinit snd_es18xx_identify(struct snd_es18xx *chip)
udelay(10);
chip->ctrl_port += inb(chip->port + 0x05);
- if ((chip->res_ctrl_port = request_region(chip->ctrl_port, 8, "ES18xx - CTRL")) == NULL) {
- snd_printk(KERN_ERR PFX "unable go grab port 0x%lx\n", chip->ctrl_port);
+ if (!devm_request_region(card->dev, chip->ctrl_port, 8,
+ "ES18xx - CTRL")) {
+ dev_err(card->dev, "unable go grab port 0x%lx\n", chip->ctrl_port);
return -EBUSY;
}
@@ -1618,21 +1580,22 @@ static int __devinit snd_es18xx_identify(struct snd_es18xx *chip)
return 0;
}
-static int __devinit snd_es18xx_probe(struct snd_es18xx *chip,
- unsigned long mpu_port,
- unsigned long fm_port)
+static int snd_es18xx_probe(struct snd_card *card,
+ struct snd_es18xx *chip,
+ unsigned long mpu_port,
+ unsigned long fm_port)
{
- if (snd_es18xx_identify(chip) < 0) {
- snd_printk(KERN_ERR PFX "[0x%lx] ESS chip not found\n", chip->port);
+ if (snd_es18xx_identify(card, chip) < 0) {
+ dev_err(card->dev, "[0x%lx] ESS chip not found\n", chip->port);
return -ENODEV;
}
switch (chip->version) {
case 0x1868:
- chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL;
+ chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL | ES18XX_GPO_2BIT;
break;
case 0x1869:
- chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV;
+ chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV | ES18XX_GPO_2BIT;
break;
case 0x1878:
chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_I2S | ES18XX_CONTROL;
@@ -1642,15 +1605,15 @@ static int __devinit snd_es18xx_probe(struct snd_es18xx *chip,
break;
case 0x1887:
case 0x1888:
- chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME;
+ chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_GPO_2BIT;
break;
default:
- snd_printk(KERN_ERR "[0x%lx] unsupported chip ES%x\n",
- chip->port, chip->version);
+ dev_err(card->dev, "[0x%lx] unsupported chip ES%x\n",
+ chip->port, chip->version);
return -ENODEV;
}
- snd_printd("[0x%lx] ESS%x chip found\n", chip->port, chip->version);
+ dev_dbg(card->dev, "[0x%lx] ESS%x chip found\n", chip->port, chip->version);
if (chip->dma1 == chip->dma2)
chip->caps &= ~(ES18XX_PCM2 | ES18XX_DUPLEX_SAME);
@@ -1658,38 +1621,31 @@ static int __devinit snd_es18xx_probe(struct snd_es18xx *chip,
return snd_es18xx_initialize(chip, mpu_port, fm_port);
}
-static struct snd_pcm_ops snd_es18xx_playback_ops = {
+static const struct snd_pcm_ops snd_es18xx_playback_ops = {
.open = snd_es18xx_playback_open,
.close = snd_es18xx_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_es18xx_playback_hw_params,
- .hw_free = snd_es18xx_pcm_hw_free,
.prepare = snd_es18xx_playback_prepare,
.trigger = snd_es18xx_playback_trigger,
.pointer = snd_es18xx_playback_pointer,
};
-static struct snd_pcm_ops snd_es18xx_capture_ops = {
+static const struct snd_pcm_ops snd_es18xx_capture_ops = {
.open = snd_es18xx_capture_open,
.close = snd_es18xx_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_es18xx_capture_hw_params,
- .hw_free = snd_es18xx_pcm_hw_free,
.prepare = snd_es18xx_capture_prepare,
.trigger = snd_es18xx_capture_trigger,
.pointer = snd_es18xx_capture_pointer,
};
-static int __devinit snd_es18xx_pcm(struct snd_card *card, int device,
- struct snd_pcm **rpcm)
+static int snd_es18xx_pcm(struct snd_card *card, int device)
{
struct snd_es18xx *chip = card->private_data;
struct snd_pcm *pcm;
char str[16];
int err;
- if (rpcm)
- *rpcm = NULL;
sprintf(str, "ES%x", chip->version);
if (chip->caps & ES18XX_PCM2)
err = snd_pcm_new(card, str, device, 2, 1, &pcm);
@@ -1711,13 +1667,9 @@ static int __devinit snd_es18xx_pcm(struct snd_card *card, int device,
sprintf(pcm->name, "ESS AudioDrive ES%x", chip->version);
chip->pcm = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
- 64*1024,
- chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev,
+ 64*1024,
+ chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
return 0;
}
@@ -1729,8 +1681,6 @@ static int snd_es18xx_suspend(struct snd_card *card, pm_message_t state)
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
-
/* power down */
chip->pm_reg = (unsigned char)snd_es18xx_read(chip, ES18XX_PM);
chip->pm_reg |= (ES18XX_PM_FM | ES18XX_PM_SUS);
@@ -1752,43 +1702,15 @@ static int snd_es18xx_resume(struct snd_card *card)
}
#endif /* CONFIG_PM */
-static int snd_es18xx_free(struct snd_card *card)
+static int snd_es18xx_new_device(struct snd_card *card,
+ unsigned long port,
+ unsigned long mpu_port,
+ unsigned long fm_port,
+ int irq, int dma1, int dma2)
{
struct snd_es18xx *chip = card->private_data;
- release_and_free_resource(chip->res_port);
- release_and_free_resource(chip->res_ctrl_port);
- release_and_free_resource(chip->res_mpu_port);
- if (chip->irq >= 0)
- free_irq(chip->irq, (void *) card);
- if (chip->dma1 >= 0) {
- disable_dma(chip->dma1);
- free_dma(chip->dma1);
- }
- if (chip->dma2 >= 0 && chip->dma1 != chip->dma2) {
- disable_dma(chip->dma2);
- free_dma(chip->dma2);
- }
- return 0;
-}
-
-static int snd_es18xx_dev_free(struct snd_device *device)
-{
- return snd_es18xx_free(device->card);
-}
-
-static int __devinit snd_es18xx_new_device(struct snd_card *card,
- unsigned long port,
- unsigned long mpu_port,
- unsigned long fm_port,
- int irq, int dma1, int dma2)
-{
- struct snd_es18xx *chip = card->private_data;
- static struct snd_device_ops ops = {
- .dev_free = snd_es18xx_dev_free,
- };
- int err;
-
+ chip->card = card;
spin_lock_init(&chip->reg_lock);
spin_lock_init(&chip->mixer_lock);
chip->port = port;
@@ -1798,54 +1720,44 @@ static int __devinit snd_es18xx_new_device(struct snd_card *card,
chip->audio2_vol = 0x00;
chip->active = 0;
- chip->res_port = request_region(port, 16, "ES18xx");
- if (chip->res_port == NULL) {
- snd_es18xx_free(card);
- snd_printk(KERN_ERR PFX "unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1);
+ if (!devm_request_region(card->dev, port, 16, "ES18xx")) {
+ dev_err(card->dev, "unable to grab ports 0x%lx-0x%lx\n", port, port + 16 - 1);
return -EBUSY;
}
- if (request_irq(irq, snd_es18xx_interrupt, IRQF_DISABLED, "ES18xx",
- (void *) card)) {
- snd_es18xx_free(card);
- snd_printk(KERN_ERR PFX "unable to grap IRQ %d\n", irq);
+ if (devm_request_irq(card->dev, irq, snd_es18xx_interrupt, 0, "ES18xx",
+ (void *) card)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", irq);
return -EBUSY;
}
chip->irq = irq;
+ card->sync_irq = chip->irq;
- if (request_dma(dma1, "ES18xx DMA 1")) {
- snd_es18xx_free(card);
- snd_printk(KERN_ERR PFX "unable to grap DMA1 %d\n", dma1);
+ if (snd_devm_request_dma(card->dev, dma1, "ES18xx DMA 1")) {
+ dev_err(card->dev, "unable to grab DMA1 %d\n", dma1);
return -EBUSY;
}
chip->dma1 = dma1;
- if (dma2 != dma1 && request_dma(dma2, "ES18xx DMA 2")) {
- snd_es18xx_free(card);
- snd_printk(KERN_ERR PFX "unable to grap DMA2 %d\n", dma2);
+ if (dma2 != dma1 &&
+ snd_devm_request_dma(card->dev, dma2, "ES18xx DMA 2")) {
+ dev_err(card->dev, "unable to grab DMA2 %d\n", dma2);
return -EBUSY;
}
chip->dma2 = dma2;
- if (snd_es18xx_probe(chip, mpu_port, fm_port) < 0) {
- snd_es18xx_free(card);
+ if (snd_es18xx_probe(card, chip, mpu_port, fm_port) < 0)
return -ENODEV;
- }
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0) {
- snd_es18xx_free(card);
- return err;
- }
return 0;
}
-static int __devinit snd_es18xx_mixer(struct snd_card *card)
+static int snd_es18xx_mixer(struct snd_card *card)
{
struct snd_es18xx *chip = card->private_data;
int err;
unsigned int idx;
- strcpy(card->mixername, chip->pcm->name);
+ strscpy(card->mixername, chip->pcm->name);
for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_base_controls); idx++) {
struct snd_kcontrol *kctl;
@@ -1862,41 +1774,48 @@ static int __devinit snd_es18xx_mixer(struct snd_card *card)
break;
}
}
- if ((err = snd_ctl_add(card, kctl)) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
}
if (chip->caps & ES18XX_PCM2) {
for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_pcm2_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm2_controls[idx], chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm2_controls[idx], chip));
+ if (err < 0)
return err;
}
} else {
for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_pcm1_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm1_controls[idx], chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm1_controls[idx], chip));
+ if (err < 0)
return err;
}
}
if (chip->caps & ES18XX_RECMIX) {
for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_recmix_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_recmix_controls[idx], chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_recmix_controls[idx], chip));
+ if (err < 0)
return err;
}
}
switch (chip->version) {
default:
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre1_control, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre1_control, chip));
+ if (err < 0)
return err;
break;
case 0x1869:
case 0x1879:
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre2_control, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre2_control, chip));
+ if (err < 0)
return err;
break;
}
if (chip->caps & ES18XX_SPATIALIZER) {
for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_spatializer_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_spatializer_controls[idx], chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_spatializer_controls[idx], chip));
+ if (err < 0)
return err;
}
}
@@ -1909,7 +1828,8 @@ static int __devinit snd_es18xx_mixer(struct snd_card *card)
else
chip->hw_switch = kctl;
kctl->private_free = snd_es18xx_hwv_free;
- if ((err = snd_ctl_add(card, kctl)) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
}
@@ -1944,29 +1864,30 @@ static int __devinit snd_es18xx_mixer(struct snd_card *card)
return err;
}
}
+ if (chip->caps & ES18XX_GPO_2BIT) {
+ for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_opt_gpo_2bit); idx++) {
+ err = snd_ctl_add(card,
+ snd_ctl_new1(&snd_es18xx_opt_gpo_2bit[idx],
+ chip));
+ if (err < 0)
+ return err;
+ }
+ }
return 0;
}
/* Card level */
-MODULE_AUTHOR("Christian Fischbach <fishbach@pool.informatik.rwth-aachen.de>, Abramo Bagnara <abramo@alsa-project.org>");
+MODULE_AUTHOR("Christian Fischbach <fishbach@pool.informatik.rwth-aachen.de>, Abramo Bagnara <abramo@alsa-project.org>");
MODULE_DESCRIPTION("ESS ES18xx AudioDrive");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ESS,ES1868 PnP AudioDrive},"
- "{ESS,ES1869 PnP AudioDrive},"
- "{ESS,ES1878 PnP AudioDrive},"
- "{ESS,ES1879 PnP AudioDrive},"
- "{ESS,ES1887 PnP AudioDrive},"
- "{ESS,ES1888 PnP AudioDrive},"
- "{ESS,ES1887 AudioDrive},"
- "{ESS,ES1888 AudioDrive}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
+static bool isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
#endif
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260,0x280 */
#ifndef CONFIG_PNP
@@ -1989,17 +1910,17 @@ MODULE_PARM_DESC(enable, "Enable ES18xx soundcard.");
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
#endif
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for ES18xx driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for ES18xx driver.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for ES18xx driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for ES18xx driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA 1 # for ES18xx driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA 2 # for ES18xx driver.");
#ifdef CONFIG_PNP
@@ -2007,7 +1928,7 @@ static int isa_registered;
static int pnp_registered;
static int pnpc_registered;
-static struct pnp_device_id snd_audiodrive_pnpbiosids[] = {
+static const struct pnp_device_id snd_audiodrive_pnpbiosids[] = {
{ .id = "ESS1869" },
{ .id = "ESS1879" },
{ .id = "" } /* end */
@@ -2016,10 +1937,10 @@ static struct pnp_device_id snd_audiodrive_pnpbiosids[] = {
MODULE_DEVICE_TABLE(pnp, snd_audiodrive_pnpbiosids);
/* PnP main device initialization */
-static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev)
+static int snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev)
{
if (pnp_activate_dev(pdev) < 0) {
- snd_printk(KERN_ERR PFX "PnP configure failure (out of resources?)\n");
+ dev_err(&pdev->dev, "PnP configure failure (out of resources?)\n");
return -EBUSY;
}
/* ok. hack using Vendor-Defined Card-Level registers */
@@ -2038,13 +1959,17 @@ static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev)
dma1[dev] = pnp_dma(pdev, 0);
dma2[dev] = pnp_dma(pdev, 1);
irq[dev] = pnp_irq(pdev, 0);
- snd_printdd("PnP ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n", port[dev], fm_port[dev], mpu_port[dev]);
- snd_printdd("PnP ES18xx: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]);
+ dev_dbg(&pdev->dev,
+ "PnP ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n",
+ port[dev], fm_port[dev], mpu_port[dev]);
+ dev_dbg(&pdev->dev,
+ "PnP ES18xx: dma1=%i, dma2=%i, irq=%i\n",
+ dma1[dev], dma2[dev], irq[dev]);
return 0;
}
-static int __devinit snd_audiodrive_pnp(int dev, struct snd_es18xx *chip,
- struct pnp_dev *pdev)
+static int snd_audiodrive_pnp(int dev, struct snd_es18xx *chip,
+ struct pnp_dev *pdev)
{
chip->dev = pdev;
if (snd_audiodrive_pnp_init_main(dev, chip->dev) < 0)
@@ -2052,7 +1977,7 @@ static int __devinit snd_audiodrive_pnp(int dev, struct snd_es18xx *chip,
return 0;
}
-static struct pnp_card_device_id snd_audiodrive_pnpids[] = {
+static const struct pnp_card_device_id snd_audiodrive_pnpids[] = {
/* ESS 1868 (integrated on Compaq dual P-Pro motherboard and Genius 18PnP 3D) */
{ .id = "ESS1868", .devs = { { "ESS1868" }, { "ESS0000" } } },
/* ESS 1868 (integrated on Maxisound Cards) */
@@ -2073,9 +1998,9 @@ static struct pnp_card_device_id snd_audiodrive_pnpids[] = {
MODULE_DEVICE_TABLE(pnp_card, snd_audiodrive_pnpids);
-static int __devinit snd_audiodrive_pnpc(int dev, struct snd_es18xx *chip,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_audiodrive_pnpc(int dev, struct snd_es18xx *chip,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
chip->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
if (chip->dev == NULL)
@@ -2087,11 +2012,12 @@ static int __devinit snd_audiodrive_pnpc(int dev, struct snd_es18xx *chip,
/* Control port initialization */
if (pnp_activate_dev(chip->devc) < 0) {
- snd_printk(KERN_ERR PFX "PnP control configure failure (out of resources?)\n");
+ dev_err(chip->card->dev,
+ "PnP control configure failure (out of resources?)\n");
return -EAGAIN;
}
- snd_printdd("pnp: port=0x%llx\n",
- (unsigned long long)pnp_port_start(chip->devc, 0));
+ dev_dbg(chip->card->dev, "pnp: port=0x%llx\n",
+ (unsigned long long)pnp_port_start(chip->devc, 0));
if (snd_audiodrive_pnp_init_main(dev, chip->dev) < 0)
return -EBUSY;
@@ -2105,13 +2031,14 @@ static int __devinit snd_audiodrive_pnpc(int dev, struct snd_es18xx *chip,
#define is_isapnp_selected(dev) 0
#endif
-static int snd_es18xx_card_new(int dev, struct snd_card **cardp)
+static int snd_es18xx_card_new(struct device *pdev, int dev,
+ struct snd_card **cardp)
{
- return snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_es18xx), cardp);
+ return snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_es18xx), cardp);
}
-static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev)
+static int snd_audiodrive_probe(struct snd_card *card, int dev)
{
struct snd_es18xx *chip = card->private_data;
struct snd_opl3 *opl3;
@@ -2137,7 +2064,7 @@ static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev)
chip->port,
irq[dev], dma1[dev]);
- err = snd_es18xx_pcm(card, 0, NULL);
+ err = snd_es18xx_pcm(card, 0);
if (err < 0)
return err;
@@ -2148,9 +2075,9 @@ static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev)
if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
if (snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2,
OPL3_HW_OPL3, 0, &opl3) < 0) {
- snd_printk(KERN_WARNING PFX
- "opl3 not detected at 0x%lx\n",
- fm_port[dev]);
+ dev_warn(card->dev,
+ "opl3 not detected at 0x%lx\n",
+ fm_port[dev]);
} else {
err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
if (err < 0)
@@ -2160,8 +2087,8 @@ static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev)
if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX,
- mpu_port[dev], 0,
- irq[dev], 0, &chip->rmidi);
+ mpu_port[dev], MPU401_INFO_IRQ_HOOK,
+ -1, &chip->rmidi);
if (err < 0)
return err;
}
@@ -2169,49 +2096,50 @@ static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev)
return snd_card_register(card);
}
-static int __devinit snd_es18xx_isa_match(struct device *pdev, unsigned int dev)
+static int snd_es18xx_isa_match(struct device *pdev, unsigned int dev)
{
return enable[dev] && !is_isapnp_selected(dev);
}
-static int __devinit snd_es18xx_isa_probe1(int dev, struct device *devptr)
+static int snd_es18xx_isa_probe1(int dev, struct device *devptr)
{
struct snd_card *card;
int err;
- err = snd_es18xx_card_new(dev, &card);
+ err = snd_es18xx_card_new(devptr, dev, &card);
if (err < 0)
return err;
- snd_card_set_dev(card, devptr);
- if ((err = snd_audiodrive_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_audiodrive_probe(card, dev);
+ if (err < 0)
return err;
- }
dev_set_drvdata(devptr, card);
return 0;
}
-static int __devinit snd_es18xx_isa_probe(struct device *pdev, unsigned int dev)
+static int snd_es18xx_isa_probe(struct device *pdev, unsigned int dev)
{
int err;
- static int possible_irqs[] = {5, 9, 10, 7, 11, 12, -1};
- static int possible_dmas[] = {1, 0, 3, 5, -1};
+ static const int possible_irqs[] = {5, 9, 10, 7, 11, 12, -1};
+ static const int possible_dmas[] = {1, 0, 3, 5, -1};
if (irq[dev] == SNDRV_AUTO_IRQ) {
- if ((irq[dev] = snd_legacy_find_free_irq(possible_irqs)) < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
+ irq[dev] = snd_legacy_find_free_irq(possible_irqs);
+ if (irq[dev] < 0) {
+ dev_err(pdev, "unable to find a free IRQ\n");
return -EBUSY;
}
}
if (dma1[dev] == SNDRV_AUTO_DMA) {
- if ((dma1[dev] = snd_legacy_find_free_dma(possible_dmas)) < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free DMA1\n");
+ dma1[dev] = snd_legacy_find_free_dma(possible_dmas);
+ if (dma1[dev] < 0) {
+ dev_err(pdev, "unable to find a free DMA1\n");
return -EBUSY;
}
}
if (dma2[dev] == SNDRV_AUTO_DMA) {
- if ((dma2[dev] = snd_legacy_find_free_dma(possible_dmas)) < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free DMA2\n");
+ dma2[dev] = snd_legacy_find_free_dma(possible_dmas);
+ if (dma2[dev] < 0) {
+ dev_err(pdev, "unable to find a free DMA2\n");
return -EBUSY;
}
}
@@ -2219,7 +2147,7 @@ static int __devinit snd_es18xx_isa_probe(struct device *pdev, unsigned int dev)
if (port[dev] != SNDRV_AUTO_PORT) {
return snd_es18xx_isa_probe1(dev, pdev);
} else {
- static unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280};
+ static const unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280};
int i;
for (i = 0; i < ARRAY_SIZE(possible_ports); i++) {
port[dev] = possible_ports[i];
@@ -2231,14 +2159,6 @@ static int __devinit snd_es18xx_isa_probe(struct device *pdev, unsigned int dev)
}
}
-static int __devexit snd_es18xx_isa_remove(struct device *devptr,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
- return 0;
-}
-
#ifdef CONFIG_PM
static int snd_es18xx_isa_suspend(struct device *dev, unsigned int n,
pm_message_t state)
@@ -2257,7 +2177,6 @@ static int snd_es18xx_isa_resume(struct device *dev, unsigned int n)
static struct isa_driver snd_es18xx_isa_driver = {
.match = snd_es18xx_isa_match,
.probe = snd_es18xx_isa_probe,
- .remove = __devexit_p(snd_es18xx_isa_remove),
#ifdef CONFIG_PM
.suspend = snd_es18xx_isa_suspend,
.resume = snd_es18xx_isa_resume,
@@ -2269,8 +2188,8 @@ static struct isa_driver snd_es18xx_isa_driver = {
#ifdef CONFIG_PNP
-static int __devinit snd_audiodrive_pnp_detect(struct pnp_dev *pdev,
- const struct pnp_device_id *id)
+static int snd_audiodrive_pnp_detect(struct pnp_dev *pdev,
+ const struct pnp_device_id *id)
{
static int dev;
int err;
@@ -2285,29 +2204,20 @@ static int __devinit snd_audiodrive_pnp_detect(struct pnp_dev *pdev,
if (dev >= SNDRV_CARDS)
return -ENODEV;
- err = snd_es18xx_card_new(dev, &card);
+ err = snd_es18xx_card_new(&pdev->dev, dev, &card);
if (err < 0)
return err;
- if ((err = snd_audiodrive_pnp(dev, card->private_data, pdev)) < 0) {
- snd_card_free(card);
+ err = snd_audiodrive_pnp(dev, card->private_data, pdev);
+ if (err < 0)
return err;
- }
- snd_card_set_dev(card, &pdev->dev);
- if ((err = snd_audiodrive_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_audiodrive_probe(card, dev);
+ if (err < 0)
return err;
- }
pnp_set_drvdata(pdev, card);
dev++;
return 0;
}
-static void __devexit snd_audiodrive_pnp_remove(struct pnp_dev * pdev)
-{
- snd_card_free(pnp_get_drvdata(pdev));
- pnp_set_drvdata(pdev, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_audiodrive_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
{
@@ -2323,15 +2233,14 @@ static struct pnp_driver es18xx_pnp_driver = {
.name = "es18xx-pnpbios",
.id_table = snd_audiodrive_pnpbiosids,
.probe = snd_audiodrive_pnp_detect,
- .remove = __devexit_p(snd_audiodrive_pnp_remove),
#ifdef CONFIG_PM
.suspend = snd_audiodrive_pnp_suspend,
.resume = snd_audiodrive_pnp_resume,
#endif
};
-static int __devinit snd_audiodrive_pnpc_detect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_audiodrive_pnpc_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
static int dev;
struct snd_card *card;
@@ -2344,31 +2253,22 @@ static int __devinit snd_audiodrive_pnpc_detect(struct pnp_card_link *pcard,
if (dev >= SNDRV_CARDS)
return -ENODEV;
- res = snd_es18xx_card_new(dev, &card);
+ res = snd_es18xx_card_new(&pcard->card->dev, dev, &card);
if (res < 0)
return res;
- if ((res = snd_audiodrive_pnpc(dev, card->private_data, pcard, pid)) < 0) {
- snd_card_free(card);
+ res = snd_audiodrive_pnpc(dev, card->private_data, pcard, pid);
+ if (res < 0)
return res;
- }
- snd_card_set_dev(card, &pcard->card->dev);
- if ((res = snd_audiodrive_probe(card, dev)) < 0) {
- snd_card_free(card);
+ res = snd_audiodrive_probe(card, dev);
+ if (res < 0)
return res;
- }
pnp_set_card_drvdata(pcard, card);
dev++;
return 0;
}
-static void __devexit snd_audiodrive_pnpc_remove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_audiodrive_pnpc_suspend(struct pnp_card_link *pcard, pm_message_t state)
{
@@ -2387,7 +2287,6 @@ static struct pnp_card_driver es18xx_pnpc_driver = {
.name = "es18xx",
.id_table = snd_audiodrive_pnpids,
.probe = snd_audiodrive_pnpc_detect,
- .remove = __devexit_p(snd_audiodrive_pnpc_remove),
#ifdef CONFIG_PM
.suspend = snd_audiodrive_pnpc_suspend,
.resume = snd_audiodrive_pnpc_resume,
diff --git a/sound/isa/galaxy/Makefile b/sound/isa/galaxy/Makefile
index e307066d4315..2dbd519860a6 100644
--- a/sound/isa/galaxy/Makefile
+++ b/sound/isa/galaxy/Makefile
@@ -1,10 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
#
-snd-azt1605-objs := azt1605.o
-snd-azt2316-objs := azt2316.o
+snd-azt1605-y := azt1605.o
+snd-azt2316-y := azt2316.o
obj-$(CONFIG_SND_AZT1605) += snd-azt1605.o
obj-$(CONFIG_SND_AZT2316) += snd-azt2316.o
diff --git a/sound/isa/galaxy/azt1605.c b/sound/isa/galaxy/azt1605.c
index 9a97643cb713..545fb13fbea6 100644
--- a/sound/isa/galaxy/azt1605.c
+++ b/sound/isa/galaxy/azt1605.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Aztech AZT1605 Driver
* Copyright (C) 2007,2010 Rene Herman
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
*/
#define AZT1605
diff --git a/sound/isa/galaxy/azt2316.c b/sound/isa/galaxy/azt2316.c
index 189441141df6..76251e845f85 100644
--- a/sound/isa/galaxy/azt2316.c
+++ b/sound/isa/galaxy/azt2316.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Aztech AZT2316 Driver
* Copyright (C) 2007,2010 Rene Herman
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
*/
#define AZT2316
diff --git a/sound/isa/galaxy/galaxy.c b/sound/isa/galaxy/galaxy.c
index ee54df082b9c..b0f1562d0fc0 100644
--- a/sound/isa/galaxy/galaxy.c
+++ b/sound/isa/galaxy/galaxy.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Aztech AZT1605/AZT2316 Driver
* Copyright (C) 2007,2010 Rene Herman
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
*/
#include <linux/kernel.h>
@@ -35,7 +22,7 @@ MODULE_LICENSE("GPL");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");
@@ -53,21 +40,21 @@ static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-module_param_array(wss_port, long, NULL, 0444);
+module_param_hw_array(wss_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(wss_port, "WSS port # for " CRD_NAME " driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for " CRD_NAME " driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "Playback DMA # for " CRD_NAME " driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "Capture DMA # for " CRD_NAME " driver.");
/*
@@ -84,7 +71,7 @@ MODULE_PARM_DESC(dma2, "Capture DMA # for " CRD_NAME " driver.");
#define DSP_COMMAND_GET_VERSION 0xe1
-static int __devinit dsp_get_byte(void __iomem *port, u8 *val)
+static int dsp_get_byte(void __iomem *port, u8 *val)
{
int loops = 1000;
@@ -97,7 +84,7 @@ static int __devinit dsp_get_byte(void __iomem *port, u8 *val)
return 0;
}
-static int __devinit dsp_reset(void __iomem *port)
+static int dsp_reset(void __iomem *port)
{
u8 val;
@@ -111,7 +98,7 @@ static int __devinit dsp_reset(void __iomem *port)
return 0;
}
-static int __devinit dsp_command(void __iomem *port, u8 cmd)
+static int dsp_command(void __iomem *port, u8 cmd)
{
int loops = 1000;
@@ -124,7 +111,7 @@ static int __devinit dsp_command(void __iomem *port, u8 cmd)
return 0;
}
-static int __devinit dsp_get_version(void __iomem *port, u8 *major, u8 *minor)
+static int dsp_get_version(void __iomem *port, u8 *major, u8 *minor)
{
int err;
@@ -161,7 +148,7 @@ static int __devinit dsp_get_version(void __iomem *port, u8 *major, u8 *minor)
#define WSS_SIGNATURE 4
-static int __devinit wss_detect(void __iomem *wss_port)
+static int wss_detect(void __iomem *wss_port)
{
if ((ioread8(wss_port + WSS_PORT_SIGNATURE) & 0x3f) != WSS_SIGNATURE)
return -ENODEV;
@@ -204,7 +191,7 @@ struct snd_galaxy {
static u32 config[SNDRV_CARDS];
static u8 wss_config[SNDRV_CARDS];
-static int __devinit snd_galaxy_match(struct device *dev, unsigned int n)
+static int snd_galaxy_match(struct device *dev, unsigned int n)
{
if (!enable[n])
return 0;
@@ -260,6 +247,7 @@ static int __devinit snd_galaxy_match(struct device *dev, unsigned int n)
break;
case 2:
irq[n] = 9;
+ fallthrough;
case 9:
wss_config[n] |= WSS_CONFIG_IRQ_9;
break;
@@ -304,6 +292,7 @@ static int __devinit snd_galaxy_match(struct device *dev, unsigned int n)
case 1:
if (dma1[n] == 0)
break;
+ fallthrough;
default:
dev_err(dev, "invalid capture DMA %d\n", dma2[n]);
return 0;
@@ -333,6 +322,7 @@ mpu:
break;
case 2:
mpu_irq[n] = 9;
+ fallthrough;
case 9:
config[n] |= GALAXY_CONFIG_MPUIRQ_2;
break;
@@ -379,7 +369,7 @@ fm:
return 1;
}
-static int __devinit galaxy_init(struct snd_galaxy *galaxy, u8 *type)
+static int galaxy_init(struct snd_galaxy *galaxy, u8 *type)
{
u8 major;
u8 minor;
@@ -411,7 +401,7 @@ static int __devinit galaxy_init(struct snd_galaxy *galaxy, u8 *type)
return 0;
}
-static int __devinit galaxy_set_mode(struct snd_galaxy *galaxy, u8 mode)
+static int galaxy_set_mode(struct snd_galaxy *galaxy, u8 mode)
{
int err;
@@ -449,7 +439,7 @@ static void galaxy_set_config(struct snd_galaxy *galaxy, u32 config)
msleep(10);
}
-static void __devinit galaxy_config(struct snd_galaxy *galaxy, u32 config)
+static void galaxy_config(struct snd_galaxy *galaxy, u32 config)
{
int i;
@@ -461,7 +451,7 @@ static void __devinit galaxy_config(struct snd_galaxy *galaxy, u32 config)
galaxy_set_config(galaxy, config);
}
-static int __devinit galaxy_wss_config(struct snd_galaxy *galaxy, u8 wss_config)
+static int galaxy_wss_config(struct snd_galaxy *galaxy, u8 wss_config)
{
int err;
@@ -482,23 +472,13 @@ static void snd_galaxy_free(struct snd_card *card)
{
struct snd_galaxy *galaxy = card->private_data;
- if (galaxy->wss_port) {
+ if (galaxy->wss_port)
wss_set_config(galaxy->wss_port, 0);
- ioport_unmap(galaxy->wss_port);
- release_and_free_resource(galaxy->res_wss_port);
- }
- if (galaxy->config_port) {
+ if (galaxy->config_port)
galaxy_set_config(galaxy, galaxy->config);
- ioport_unmap(galaxy->config_port);
- release_and_free_resource(galaxy->res_config_port);
- }
- if (galaxy->port) {
- ioport_unmap(galaxy->port);
- release_and_free_resource(galaxy->res_port);
- }
}
-static int __devinit snd_galaxy_probe(struct device *dev, unsigned int n)
+static int __snd_galaxy_probe(struct device *dev, unsigned int n)
{
struct snd_galaxy *galaxy;
struct snd_wss *chip;
@@ -506,62 +486,64 @@ static int __devinit snd_galaxy_probe(struct device *dev, unsigned int n)
u8 type;
int err;
- err = snd_card_create(index[n], id[n], THIS_MODULE, sizeof *galaxy,
- &card);
+ err = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE,
+ sizeof(*galaxy), &card);
if (err < 0)
return err;
- snd_card_set_dev(card, dev);
-
card->private_free = snd_galaxy_free;
galaxy = card->private_data;
- galaxy->res_port = request_region(port[n], 16, DRV_NAME);
+ galaxy->res_port = devm_request_region(dev, port[n], 16, DRV_NAME);
if (!galaxy->res_port) {
dev_err(dev, "could not grab ports %#lx-%#lx\n", port[n],
port[n] + 15);
- err = -EBUSY;
- goto error;
+ return -EBUSY;
}
- galaxy->port = ioport_map(port[n], 16);
+ galaxy->port = devm_ioport_map(dev, port[n], 16);
+ if (!galaxy->port)
+ return -ENOMEM;
err = galaxy_init(galaxy, &type);
if (err < 0) {
dev_err(dev, "did not find a Sound Galaxy at %#lx\n", port[n]);
- goto error;
+ return err;
}
dev_info(dev, "Sound Galaxy (type %d) found at %#lx\n", type, port[n]);
- galaxy->res_config_port = request_region(port[n] + GALAXY_PORT_CONFIG,
- 16, DRV_NAME);
+ galaxy->res_config_port =
+ devm_request_region(dev, port[n] + GALAXY_PORT_CONFIG, 16,
+ DRV_NAME);
if (!galaxy->res_config_port) {
dev_err(dev, "could not grab ports %#lx-%#lx\n",
port[n] + GALAXY_PORT_CONFIG,
port[n] + GALAXY_PORT_CONFIG + 15);
- err = -EBUSY;
- goto error;
+ return -EBUSY;
}
- galaxy->config_port = ioport_map(port[n] + GALAXY_PORT_CONFIG, 16);
-
+ galaxy->config_port =
+ devm_ioport_map(dev, port[n] + GALAXY_PORT_CONFIG, 16);
+ if (!galaxy->config_port)
+ return -ENOMEM;
galaxy_config(galaxy, config[n]);
- galaxy->res_wss_port = request_region(wss_port[n], 4, DRV_NAME);
+ galaxy->res_wss_port = devm_request_region(dev, wss_port[n], 4, DRV_NAME);
if (!galaxy->res_wss_port) {
dev_err(dev, "could not grab ports %#lx-%#lx\n", wss_port[n],
wss_port[n] + 3);
- err = -EBUSY;
- goto error;
+ return -EBUSY;
}
- galaxy->wss_port = ioport_map(wss_port[n], 4);
+ galaxy->wss_port = devm_ioport_map(dev, wss_port[n], 4);
+ if (!galaxy->wss_port)
+ return -ENOMEM;
err = galaxy_wss_config(galaxy, wss_config[n]);
if (err < 0) {
dev_err(dev, "could not configure WSS\n");
- goto error;
+ return err;
}
- strcpy(card->driver, DRV_NAME);
- strcpy(card->shortname, DRV_NAME);
+ strscpy(card->driver, DRV_NAME);
+ strscpy(card->shortname, DRV_NAME);
sprintf(card->longname, "%s at %#lx/%#lx, irq %d, dma %d/%d",
card->shortname, port[n], wss_port[n], irq[n], dma1[n],
dma2[n]);
@@ -569,26 +551,25 @@ static int __devinit snd_galaxy_probe(struct device *dev, unsigned int n)
err = snd_wss_create(card, wss_port[n] + 4, -1, irq[n], dma1[n],
dma2[n], WSS_HW_DETECT, 0, &chip);
if (err < 0)
- goto error;
+ return err;
- err = snd_wss_pcm(chip, 0, NULL);
+ err = snd_wss_pcm(chip, 0);
if (err < 0)
- goto error;
+ return err;
err = snd_wss_mixer(chip);
if (err < 0)
- goto error;
+ return err;
- err = snd_wss_timer(chip, 0, NULL);
+ err = snd_wss_timer(chip, 0);
if (err < 0)
- goto error;
+ return err;
if (mpu_port[n] >= 0) {
err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- mpu_port[n], 0, mpu_irq[n],
- IRQF_DISABLED, NULL);
+ mpu_port[n], 0, mpu_irq[n], NULL);
if (err < 0)
- goto error;
+ return err;
}
if (fm_port[n] >= 0) {
@@ -598,55 +579,37 @@ static int __devinit snd_galaxy_probe(struct device *dev, unsigned int n)
OPL3_HW_AUTO, 0, &opl3);
if (err < 0) {
dev_err(dev, "no OPL device at %#lx\n", fm_port[n]);
- goto error;
+ return err;
}
err = snd_opl3_timer_new(opl3, 1, 2);
if (err < 0)
- goto error;
+ return err;
err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
if (err < 0)
- goto error;
+ return err;
}
err = snd_card_register(card);
if (err < 0)
- goto error;
+ return err;
dev_set_drvdata(dev, card);
return 0;
-
-error:
- snd_card_free(card);
- return err;
}
-static int __devexit snd_galaxy_remove(struct device *dev, unsigned int n)
+static int snd_galaxy_probe(struct device *dev, unsigned int n)
{
- snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
- return 0;
+ return snd_card_free_on_error(dev, __snd_galaxy_probe(dev, n));
}
static struct isa_driver snd_galaxy_driver = {
.match = snd_galaxy_match,
.probe = snd_galaxy_probe,
- .remove = __devexit_p(snd_galaxy_remove),
.driver = {
.name = DEV_NAME
}
};
-static int __init alsa_card_galaxy_init(void)
-{
- return isa_register_driver(&snd_galaxy_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_galaxy_exit(void)
-{
- isa_unregister_driver(&snd_galaxy_driver);
-}
-
-module_init(alsa_card_galaxy_init);
-module_exit(alsa_card_galaxy_exit);
+module_isa_driver(snd_galaxy_driver, SNDRV_CARDS);
diff --git a/sound/isa/gus/Makefile b/sound/isa/gus/Makefile
index 6cd4ee03754a..4924c1904fa4 100644
--- a/sound/isa/gus/Makefile
+++ b/sound/isa/gus/Makefile
@@ -1,20 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-gus-lib-objs := gus_main.o \
+snd-gus-lib-y := gus_main.o \
gus_io.o gus_irq.o gus_timer.o \
gus_mem.o gus_mem_proc.o gus_dram.o gus_dma.o gus_volume.o \
gus_pcm.o gus_mixer.o \
gus_uart.o \
gus_reset.o
-snd-gusclassic-objs := gusclassic.o
-snd-gusextreme-objs := gusextreme.o
-snd-gusmax-objs := gusmax.o
-snd-interwave-objs := interwave.o
-snd-interwave-stb-objs := interwave-stb.o
+snd-gusclassic-y := gusclassic.o
+snd-gusextreme-y := gusextreme.o
+snd-gusmax-y := gusmax.o
+snd-interwave-y := interwave.o
+snd-interwave-stb-y := interwave-stb.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_GUSCLASSIC) += snd-gusclassic.o snd-gus-lib.o
diff --git a/sound/isa/gus/gus_dma.c b/sound/isa/gus/gus_dma.c
index 36c27c832360..ffc69e26227e 100644
--- a/sound/isa/gus/gus_dma.c
+++ b/sound/isa/gus/gus_dma.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for GF1 DMA control
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <asm/dma.h>
@@ -26,12 +11,9 @@
static void snd_gf1_dma_ack(struct snd_gus_card * gus)
{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, 0x00);
snd_gf1_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
static void snd_gf1_dma_program(struct snd_gus_card * gus,
@@ -40,20 +22,22 @@ static void snd_gf1_dma_program(struct snd_gus_card * gus,
unsigned int count,
unsigned int cmd)
{
- unsigned long flags;
unsigned int address;
unsigned char dma_cmd;
unsigned int address_high;
- snd_printdd("dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n",
- addr, buf_addr, count);
+ dev_dbg(gus->card->dev,
+ "dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n",
+ addr, buf_addr, count);
if (gus->gf1.dma1 > 3) {
if (gus->gf1.enh_mode) {
address = addr >> 1;
} else {
if (addr & 0x1f) {
- snd_printd("snd_gf1_dma_transfer: unaligned address (0x%x)?\n", addr);
+ dev_dbg(gus->card->dev,
+ "%s: unaligned address (0x%x)?\n",
+ __func__, addr);
return;
}
address = (addr & 0x000c0000) | ((addr & 0x0003ffff) >> 1);
@@ -78,10 +62,11 @@ static void snd_gf1_dma_program(struct snd_gus_card * gus,
snd_gf1_dma_ack(gus);
snd_dma_program(gus->gf1.dma1, buf_addr, count, dma_cmd & SNDRV_GF1_DMA_READ ? DMA_MODE_READ : DMA_MODE_WRITE);
#if 0
- snd_printk(KERN_DEBUG "address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n",
- address << 1, count, dma_cmd);
+ dev_dbg(gus->card->dev,
+ "address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n",
+ address << 1, count, dma_cmd);
#endif
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
if (gus->gf1.enh_mode) {
address_high = ((address >> 16) & 0x000000f0) | (address & 0x0000000f);
snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));
@@ -89,7 +74,6 @@ static void snd_gf1_dma_program(struct snd_gus_card * gus,
} else
snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));
snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, dma_cmd);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
static struct snd_gf1_dma_block *snd_gf1_dma_next_block(struct snd_gus_card * gus)
@@ -131,39 +115,37 @@ static void snd_gf1_dma_interrupt(struct snd_gus_card * gus)
snd_gf1_dma_ack(gus);
if (gus->gf1.dma_ack)
gus->gf1.dma_ack(gus, gus->gf1.dma_private_data);
- spin_lock(&gus->dma_lock);
- if (gus->gf1.dma_data_pcm == NULL &&
- gus->gf1.dma_data_synth == NULL) {
- gus->gf1.dma_ack = NULL;
- gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER;
- spin_unlock(&gus->dma_lock);
- return;
+ scoped_guard(spinlock, &gus->dma_lock) {
+ if (gus->gf1.dma_data_pcm == NULL &&
+ gus->gf1.dma_data_synth == NULL) {
+ gus->gf1.dma_ack = NULL;
+ gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER;
+ return;
+ }
+ block = snd_gf1_dma_next_block(gus);
}
- block = snd_gf1_dma_next_block(gus);
- spin_unlock(&gus->dma_lock);
+ if (!block)
+ return;
snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);
kfree(block);
#if 0
- snd_printd(KERN_DEBUG "program dma (IRQ) - "
- "addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",
- block->addr, block->buf_addr, block->count, block->cmd);
+ dev_dbg(gus->card->dev,
+ "program dma (IRQ) - addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",
+ block->addr, block->buf_addr, block->count, block->cmd);
#endif
}
int snd_gf1_dma_init(struct snd_gus_card * gus)
{
- mutex_lock(&gus->dma_mutex);
+ guard(mutex)(&gus->dma_mutex);
gus->gf1.dma_shared++;
- if (gus->gf1.dma_shared > 1) {
- mutex_unlock(&gus->dma_mutex);
+ if (gus->gf1.dma_shared > 1)
return 0;
- }
gus->gf1.interrupt_handler_dma_write = snd_gf1_dma_interrupt;
gus->gf1.dma_data_pcm =
gus->gf1.dma_data_pcm_last =
gus->gf1.dma_data_synth =
gus->gf1.dma_data_synth_last = NULL;
- mutex_unlock(&gus->dma_mutex);
return 0;
}
@@ -171,7 +153,7 @@ int snd_gf1_dma_done(struct snd_gus_card * gus)
{
struct snd_gf1_dma_block *block;
- mutex_lock(&gus->dma_mutex);
+ guard(mutex)(&gus->dma_mutex);
gus->gf1.dma_shared--;
if (!gus->gf1.dma_shared) {
snd_dma_disable(gus->gf1.dma1);
@@ -188,7 +170,6 @@ int snd_gf1_dma_done(struct snd_gus_card * gus)
gus->gf1.dma_data_pcm_last =
gus->gf1.dma_data_synth_last = NULL;
}
- mutex_unlock(&gus->dma_mutex);
return 0;
}
@@ -197,54 +178,58 @@ int snd_gf1_dma_transfer_block(struct snd_gus_card * gus,
int atomic,
int synth)
{
- unsigned long flags;
struct snd_gf1_dma_block *block;
+ struct snd_gf1_dma_block *free_block = NULL;
block = kmalloc(sizeof(*block), atomic ? GFP_ATOMIC : GFP_KERNEL);
- if (block == NULL) {
- snd_printk(KERN_ERR "gf1: DMA transfer failure; not enough memory\n");
+ if (!block)
return -ENOMEM;
- }
+
*block = *__block;
block->next = NULL;
- snd_printdd("addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",
- block->addr, (long) block->buffer, block->count,
- block->cmd);
-
- snd_printdd("gus->gf1.dma_data_pcm_last = 0x%lx\n",
- (long)gus->gf1.dma_data_pcm_last);
- snd_printdd("gus->gf1.dma_data_pcm = 0x%lx\n",
- (long)gus->gf1.dma_data_pcm);
-
- spin_lock_irqsave(&gus->dma_lock, flags);
- if (synth) {
- if (gus->gf1.dma_data_synth_last) {
- gus->gf1.dma_data_synth_last->next = block;
- gus->gf1.dma_data_synth_last = block;
+ dev_dbg(gus->card->dev,
+ "addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",
+ block->addr, (long) block->buffer, block->count,
+ block->cmd);
+
+ dev_dbg(gus->card->dev,
+ "gus->gf1.dma_data_pcm_last = 0x%lx\n",
+ (long)gus->gf1.dma_data_pcm_last);
+ dev_dbg(gus->card->dev,
+ "gus->gf1.dma_data_pcm = 0x%lx\n",
+ (long)gus->gf1.dma_data_pcm);
+
+ scoped_guard(spinlock_irqsave, &gus->dma_lock) {
+ if (synth) {
+ if (gus->gf1.dma_data_synth_last) {
+ gus->gf1.dma_data_synth_last->next = block;
+ gus->gf1.dma_data_synth_last = block;
+ } else {
+ gus->gf1.dma_data_synth =
+ gus->gf1.dma_data_synth_last = block;
+ }
} else {
- gus->gf1.dma_data_synth =
- gus->gf1.dma_data_synth_last = block;
+ if (gus->gf1.dma_data_pcm_last) {
+ gus->gf1.dma_data_pcm_last->next = block;
+ gus->gf1.dma_data_pcm_last = block;
+ } else {
+ gus->gf1.dma_data_pcm =
+ gus->gf1.dma_data_pcm_last = block;
+ }
}
- } else {
- if (gus->gf1.dma_data_pcm_last) {
- gus->gf1.dma_data_pcm_last->next = block;
- gus->gf1.dma_data_pcm_last = block;
- } else {
- gus->gf1.dma_data_pcm =
- gus->gf1.dma_data_pcm_last = block;
+ if (!(gus->gf1.dma_flags & SNDRV_GF1_DMA_TRIGGER)) {
+ gus->gf1.dma_flags |= SNDRV_GF1_DMA_TRIGGER;
+ free_block = snd_gf1_dma_next_block(gus);
}
}
- if (!(gus->gf1.dma_flags & SNDRV_GF1_DMA_TRIGGER)) {
- gus->gf1.dma_flags |= SNDRV_GF1_DMA_TRIGGER;
- block = snd_gf1_dma_next_block(gus);
- spin_unlock_irqrestore(&gus->dma_lock, flags);
- if (block == NULL)
- return 0;
- snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);
- kfree(block);
- return 0;
+
+ if (free_block) {
+ snd_gf1_dma_program(gus, free_block->addr, free_block->buf_addr,
+ free_block->count,
+ (unsigned short)free_block->cmd);
+ kfree(free_block);
}
- spin_unlock_irqrestore(&gus->dma_lock, flags);
+
return 0;
}
diff --git a/sound/isa/gus/gus_dram.c b/sound/isa/gus/gus_dram.c
index fd2e2e2ed4e7..50fe738ee3ea 100644
--- a/sound/isa/gus/gus_dram.c
+++ b/sound/isa/gus/gus_dram.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* DRAM access routines
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/time.h>
@@ -28,7 +13,6 @@
static int snd_gus_dram_poke(struct snd_gus_card *gus, char __user *_buffer,
unsigned int address, unsigned int size)
{
- unsigned long flags;
unsigned int size1, size2;
char buffer[256], *pbuffer;
@@ -37,11 +21,10 @@ static int snd_gus_dram_poke(struct snd_gus_card *gus, char __user *_buffer,
if (copy_from_user(buffer, _buffer, size1))
return -EFAULT;
if (gus->interwave) {
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
snd_gf1_dram_addr(gus, address);
outsb(GUSP(gus, DRAM), buffer, size1);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
address += size1;
} else {
pbuffer = buffer;
@@ -66,19 +49,17 @@ static int snd_gus_dram_peek(struct snd_gus_card *gus, char __user *_buffer,
unsigned int address, unsigned int size,
int rom)
{
- unsigned long flags;
unsigned int size1, size2;
char buffer[256], *pbuffer;
while (size > 0) {
size1 = size > sizeof(buffer) ? sizeof(buffer) : size;
if (gus->interwave) {
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, rom ? 0x03 : 0x01);
snd_gf1_dram_addr(gus, address);
insb(GUSP(gus, DRAM), buffer, size1);
snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
address += size1;
} else {
pbuffer = buffer;
diff --git a/sound/isa/gus/gus_instr.c b/sound/isa/gus/gus_instr.c
deleted file mode 100644
index 4dc9caf8ddcf..000000000000
--- a/sound/isa/gus/gus_instr.c
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Routines for Gravis UltraSound soundcards - Synthesizer
- * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <linux/time.h>
-#include <sound/core.h>
-#include <sound/gus.h>
-
-/*
- *
- */
-
-int snd_gus_iwffff_put_sample(void *private_data, struct iwffff_wave *wave,
- char __user *data, long len, int atomic)
-{
- struct snd_gus_card *gus = private_data;
- struct snd_gf1_mem_block *block;
- int err;
-
- if (wave->format & IWFFFF_WAVE_ROM)
- return 0; /* it's probably ok - verify the address? */
- if (wave->format & IWFFFF_WAVE_STEREO)
- return -EINVAL; /* not supported */
- block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
- SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF,
- NULL, wave->size,
- wave->format & IWFFFF_WAVE_16BIT, 1,
- wave->share_id);
- if (block == NULL)
- return -ENOMEM;
- err = snd_gus_dram_write(gus, data,
- block->ptr, wave->size);
- if (err < 0) {
- snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0);
- snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block);
- snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1);
- return err;
- }
- wave->address.memory = block->ptr;
- return 0;
-}
-
-int snd_gus_iwffff_get_sample(void *private_data, struct iwffff_wave *wave,
- char __user *data, long len, int atomic)
-{
- struct snd_gus_card *gus = private_data;
-
- return snd_gus_dram_read(gus, data, wave->address.memory, wave->size,
- wave->format & IWFFFF_WAVE_ROM ? 1 : 0);
-}
-
-int snd_gus_iwffff_remove_sample(void *private_data, struct iwffff_wave *wave,
- int atomic)
-{
- struct snd_gus_card *gus = private_data;
-
- if (wave->format & IWFFFF_WAVE_ROM)
- return 0; /* it's probably ok - verify the address? */
- return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory);
-}
-
-/*
- *
- */
-
-int snd_gus_gf1_put_sample(void *private_data, struct gf1_wave *wave,
- char __user *data, long len, int atomic)
-{
- struct snd_gus_card *gus = private_data;
- struct snd_gf1_mem_block *block;
- int err;
-
- if (wave->format & GF1_WAVE_STEREO)
- return -EINVAL; /* not supported */
- block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
- SNDRV_GF1_MEM_OWNER_WAVE_GF1,
- NULL, wave->size,
- wave->format & GF1_WAVE_16BIT, 1,
- wave->share_id);
- if (block == NULL)
- return -ENOMEM;
- err = snd_gus_dram_write(gus, data,
- block->ptr, wave->size);
- if (err < 0) {
- snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0);
- snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block);
- snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1);
- return err;
- }
- wave->address.memory = block->ptr;
- return 0;
-}
-
-int snd_gus_gf1_get_sample(void *private_data, struct gf1_wave *wave,
- char __user *data, long len, int atomic)
-{
- struct snd_gus_card *gus = private_data;
-
- return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, 0);
-}
-
-int snd_gus_gf1_remove_sample(void *private_data, struct gf1_wave *wave,
- int atomic)
-{
- struct snd_gus_card *gus = private_data;
-
- return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory);
-}
-
-/*
- *
- */
-
-int snd_gus_simple_put_sample(void *private_data, struct simple_instrument *instr,
- char __user *data, long len, int atomic)
-{
- struct snd_gus_card *gus = private_data;
- struct snd_gf1_mem_block *block;
- int err;
-
- if (instr->format & SIMPLE_WAVE_STEREO)
- return -EINVAL; /* not supported */
- block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
- SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE,
- NULL, instr->size,
- instr->format & SIMPLE_WAVE_16BIT, 1,
- instr->share_id);
- if (block == NULL)
- return -ENOMEM;
- err = snd_gus_dram_write(gus, data, block->ptr, instr->size);
- if (err < 0) {
- snd_gf1_mem_lock(&gus->gf1.mem_alloc, 0);
- snd_gf1_mem_xfree(&gus->gf1.mem_alloc, block);
- snd_gf1_mem_lock(&gus->gf1.mem_alloc, 1);
- return err;
- }
- instr->address.memory = block->ptr;
- return 0;
-}
-
-int snd_gus_simple_get_sample(void *private_data, struct simple_instrument *instr,
- char __user *data, long len, int atomic)
-{
- struct snd_gus_card *gus = private_data;
-
- return snd_gus_dram_read(gus, data, instr->address.memory, instr->size, 0);
-}
-
-int snd_gus_simple_remove_sample(void *private_data, struct simple_instrument *instr,
- int atomic)
-{
- struct snd_gus_card *gus = private_data;
-
- return snd_gf1_mem_free(&gus->gf1.mem_alloc, instr->address.memory);
-}
diff --git a/sound/isa/gus/gus_io.c b/sound/isa/gus/gus_io.c
index ca79878d8d8c..3e6f35084f26 100644
--- a/sound/isa/gus/gus_io.c
+++ b/sound/isa/gus/gus_io.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* I/O routines for GF1/InterWave synthesizer chips
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
@@ -192,99 +177,37 @@ unsigned int snd_gf1_read_addr(struct snd_gus_card * gus,
void snd_gf1_i_ctrl_stop(struct snd_gus_card * gus, unsigned char reg)
{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
__snd_gf1_ctrl_stop(gus, reg);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
void snd_gf1_i_write8(struct snd_gus_card * gus,
unsigned char reg,
unsigned char data)
{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
__snd_gf1_write8(gus, reg, data);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
unsigned char snd_gf1_i_look8(struct snd_gus_card * gus, unsigned char reg)
{
- unsigned long flags;
- unsigned char res;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
- res = __snd_gf1_look8(gus, reg);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
- return res;
+ guard(spinlock_irqsave)(&gus->reg_lock);
+ return __snd_gf1_look8(gus, reg);
}
void snd_gf1_i_write16(struct snd_gus_card * gus,
unsigned char reg,
unsigned int data)
{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
__snd_gf1_write16(gus, reg, data);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
unsigned short snd_gf1_i_look16(struct snd_gus_card * gus, unsigned char reg)
{
- unsigned long flags;
- unsigned short res;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
- res = __snd_gf1_look16(gus, reg);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
- return res;
-}
-
-#if 0
-
-void snd_gf1_i_adlib_write(struct snd_gus_card * gus,
- unsigned char reg,
- unsigned char data)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
- __snd_gf1_adlib_write(gus, reg, data);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
-}
-
-void snd_gf1_i_write_addr(struct snd_gus_card * gus, unsigned char reg,
- unsigned int addr, short w_16bit)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
- __snd_gf1_write_addr(gus, reg, addr, w_16bit);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
-}
-
-#endif /* 0 */
-
-#ifdef CONFIG_SND_DEBUG
-static unsigned int snd_gf1_i_read_addr(struct snd_gus_card * gus,
- unsigned char reg, short w_16bit)
-{
- unsigned int res;
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
- res = __snd_gf1_read_addr(gus, reg, w_16bit);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
- return res;
+ guard(spinlock_irqsave)(&gus->reg_lock);
+ return __snd_gf1_look16(gus, reg);
}
-#endif
-
-/*
-
- */
void snd_gf1_dram_addr(struct snd_gus_card * gus, unsigned int addr)
{
@@ -300,9 +223,7 @@ void snd_gf1_dram_addr(struct snd_gus_card * gus, unsigned int addr)
void snd_gf1_poke(struct snd_gus_card * gus, unsigned int addr, unsigned char data)
{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
mb();
outw((unsigned short) addr, gus->gf1.reg_data16);
@@ -312,15 +233,11 @@ void snd_gf1_poke(struct snd_gus_card * gus, unsigned int addr, unsigned char da
outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
mb();
outb(data, gus->gf1.reg_dram);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
unsigned char snd_gf1_peek(struct snd_gus_card * gus, unsigned int addr)
{
- unsigned long flags;
- unsigned char res;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
mb();
outw((unsigned short) addr, gus->gf1.reg_data16);
@@ -329,22 +246,16 @@ unsigned char snd_gf1_peek(struct snd_gus_card * gus, unsigned int addr)
mb();
outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
mb();
- res = inb(gus->gf1.reg_dram);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
- return res;
+ return inb(gus->gf1.reg_dram);
}
#if 0
void snd_gf1_pokew(struct snd_gus_card * gus, unsigned int addr, unsigned short data)
{
- unsigned long flags;
-
-#ifdef CONFIG_SND_DEBUG
if (!gus->interwave)
- snd_printk(KERN_DEBUG "snd_gf1_pokew - GF1!!!\n");
-#endif
- spin_lock_irqsave(&gus->reg_lock, flags);
+ dev_dbg(gus->card->dev, "%s - GF1!!!\n", __func__);
+ guard(spinlock_irqsave)(&gus->reg_lock);
outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
mb();
outw((unsigned short) addr, gus->gf1.reg_data16);
@@ -356,19 +267,13 @@ void snd_gf1_pokew(struct snd_gus_card * gus, unsigned int addr, unsigned short
outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel);
mb();
outw(data, gus->gf1.reg_data16);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
unsigned short snd_gf1_peekw(struct snd_gus_card * gus, unsigned int addr)
{
- unsigned long flags;
- unsigned short res;
-
-#ifdef CONFIG_SND_DEBUG
if (!gus->interwave)
- snd_printk(KERN_DEBUG "snd_gf1_peekw - GF1!!!\n");
-#endif
- spin_lock_irqsave(&gus->reg_lock, flags);
+ dev_dbg(gus->card->dev, "%s - GF1!!!\n", __func__);
+ guard(spinlock_irqsave)(&gus->reg_lock);
outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
mb();
outw((unsigned short) addr, gus->gf1.reg_data16);
@@ -379,25 +284,20 @@ unsigned short snd_gf1_peekw(struct snd_gus_card * gus, unsigned int addr)
mb();
outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel);
mb();
- res = inw(gus->gf1.reg_data16);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
- return res;
+ return inw(gus->gf1.reg_data16);
}
void snd_gf1_dram_setmem(struct snd_gus_card * gus, unsigned int addr,
unsigned short value, unsigned int count)
{
unsigned long port;
- unsigned long flags;
-#ifdef CONFIG_SND_DEBUG
if (!gus->interwave)
- snd_printk(KERN_DEBUG "snd_gf1_dram_setmem - GF1!!!\n");
-#endif
+ dev_dbg(gus->card->dev, "%s - GF1!!!\n", __func__);
addr &= ~1;
count >>= 1;
port = GUSP(gus, GF1DATALOW);
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
mb();
outw((unsigned short) addr, gus->gf1.reg_data16);
@@ -409,7 +309,6 @@ void snd_gf1_dram_setmem(struct snd_gus_card * gus, unsigned int addr,
outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel);
while (count--)
outw(value, port);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
#endif /* 0 */
@@ -418,7 +317,7 @@ void snd_gf1_select_active_voices(struct snd_gus_card * gus)
{
unsigned short voices;
- static unsigned short voices_tbl[32 - 14 + 1] =
+ static const unsigned short voices_tbl[32 - 14 + 1] =
{
44100, 41160, 38587, 36317, 34300, 32494, 30870, 29400, 28063, 26843,
25725, 24696, 23746, 22866, 22050, 21289, 20580, 19916, 19293
@@ -439,102 +338,3 @@ void snd_gf1_select_active_voices(struct snd_gus_card * gus)
udelay(100);
}
}
-
-#ifdef CONFIG_SND_DEBUG
-
-void snd_gf1_print_voice_registers(struct snd_gus_card * gus)
-{
- unsigned char mode;
- int voice, ctrl;
-
- voice = gus->gf1.active_voice;
- printk(KERN_INFO " -%i- GF1 voice ctrl, ramp ctrl = 0x%x, 0x%x\n", voice, ctrl = snd_gf1_i_read8(gus, 0), snd_gf1_i_read8(gus, 0x0d));
- printk(KERN_INFO " -%i- GF1 frequency = 0x%x\n", voice, snd_gf1_i_read16(gus, 1));
- printk(KERN_INFO " -%i- GF1 loop start, end = 0x%x (0x%x), 0x%x (0x%x)\n", voice, snd_gf1_i_read_addr(gus, 2, ctrl & 4), snd_gf1_i_read_addr(gus, 2, (ctrl & 4) ^ 4), snd_gf1_i_read_addr(gus, 4, ctrl & 4), snd_gf1_i_read_addr(gus, 4, (ctrl & 4) ^ 4));
- printk(KERN_INFO " -%i- GF1 ramp start, end, rate = 0x%x, 0x%x, 0x%x\n", voice, snd_gf1_i_read8(gus, 7), snd_gf1_i_read8(gus, 8), snd_gf1_i_read8(gus, 6));
- printk(KERN_INFO" -%i- GF1 volume = 0x%x\n", voice, snd_gf1_i_read16(gus, 9));
- printk(KERN_INFO " -%i- GF1 position = 0x%x (0x%x)\n", voice, snd_gf1_i_read_addr(gus, 0x0a, ctrl & 4), snd_gf1_i_read_addr(gus, 0x0a, (ctrl & 4) ^ 4));
- if (gus->interwave && snd_gf1_i_read8(gus, 0x19) & 0x01) { /* enhanced mode */
- mode = snd_gf1_i_read8(gus, 0x15);
- printk(KERN_INFO " -%i- GFA1 mode = 0x%x\n", voice, mode);
- if (mode & 0x01) { /* Effect processor */
- printk(KERN_INFO " -%i- GFA1 effect address = 0x%x\n", voice, snd_gf1_i_read_addr(gus, 0x11, ctrl & 4));
- printk(KERN_INFO " -%i- GFA1 effect volume = 0x%x\n", voice, snd_gf1_i_read16(gus, 0x16));
- printk(KERN_INFO " -%i- GFA1 effect volume final = 0x%x\n", voice, snd_gf1_i_read16(gus, 0x1d));
- printk(KERN_INFO " -%i- GFA1 effect acumulator = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x14));
- }
- if (mode & 0x20) {
- printk(KERN_INFO " -%i- GFA1 left offset = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x13), snd_gf1_i_read16(gus, 0x13) >> 4);
- printk(KERN_INFO " -%i- GFA1 left offset final = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x1c), snd_gf1_i_read16(gus, 0x1c) >> 4);
- printk(KERN_INFO " -%i- GFA1 right offset = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x0c), snd_gf1_i_read16(gus, 0x0c) >> 4);
- printk(KERN_INFO " -%i- GFA1 right offset final = 0x%x (%i)\n", voice, snd_gf1_i_read16(gus, 0x1b), snd_gf1_i_read16(gus, 0x1b) >> 4);
- } else
- printk(KERN_INFO " -%i- GF1 pan = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c));
- } else
- printk(KERN_INFO " -%i- GF1 pan = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c));
-}
-
-#if 0
-
-void snd_gf1_print_global_registers(struct snd_gus_card * gus)
-{
- unsigned char global_mode = 0x00;
-
- printk(KERN_INFO " -G- GF1 active voices = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_ACTIVE_VOICES));
- if (gus->interwave) {
- global_mode = snd_gf1_i_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE);
- printk(KERN_INFO " -G- GF1 global mode = 0x%x\n", global_mode);
- }
- if (global_mode & 0x02) /* LFO enabled? */
- printk(KERN_INFO " -G- GF1 LFO base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_LFO_BASE));
- printk(KERN_INFO " -G- GF1 voices IRQ read = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_VOICES_IRQ_READ));
- printk(KERN_INFO " -G- GF1 DRAM DMA control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL));
- printk(KERN_INFO " -G- GF1 DRAM DMA high/low = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH), snd_gf1_i_read16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW));
- printk(KERN_INFO " -G- GF1 DRAM IO high/low = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_IO_HIGH), snd_gf1_i_read16(gus, SNDRV_GF1_GW_DRAM_IO_LOW));
- if (!gus->interwave)
- printk(KERN_INFO " -G- GF1 record DMA control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL));
- printk(KERN_INFO " -G- GF1 DRAM IO 16 = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_DRAM_IO16));
- if (gus->gf1.enh_mode) {
- printk(KERN_INFO " -G- GFA1 memory config = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG));
- printk(KERN_INFO " -G- GFA1 memory control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_MEMORY_CONTROL));
- printk(KERN_INFO " -G- GFA1 FIFO record base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR));
- printk(KERN_INFO " -G- GFA1 FIFO playback base = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR));
- printk(KERN_INFO " -G- GFA1 interleave control = 0x%x\n", snd_gf1_i_look16(gus, SNDRV_GF1_GW_INTERLEAVE));
- }
-}
-
-void snd_gf1_print_setup_registers(struct snd_gus_card * gus)
-{
- printk(KERN_INFO " -S- mix control = 0x%x\n", inb(GUSP(gus, MIXCNTRLREG)));
- printk(KERN_INFO " -S- IRQ status = 0x%x\n", inb(GUSP(gus, IRQSTAT)));
- printk(KERN_INFO " -S- timer control = 0x%x\n", inb(GUSP(gus, TIMERCNTRL)));
- printk(KERN_INFO " -S- timer data = 0x%x\n", inb(GUSP(gus, TIMERDATA)));
- printk(KERN_INFO " -S- status read = 0x%x\n", inb(GUSP(gus, REGCNTRLS)));
- printk(KERN_INFO " -S- Sound Blaster control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL));
- printk(KERN_INFO " -S- AdLib timer 1/2 = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1), snd_gf1_i_look8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2));
- printk(KERN_INFO " -S- reset = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET));
- if (gus->interwave) {
- printk(KERN_INFO " -S- compatibility = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_COMPATIBILITY));
- printk(KERN_INFO " -S- decode control = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_DECODE_CONTROL));
- printk(KERN_INFO " -S- version number = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER));
- printk(KERN_INFO " -S- MPU-401 emul. control A/B = 0x%x/0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A), snd_gf1_i_look8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B));
- printk(KERN_INFO " -S- emulation IRQ = 0x%x\n", snd_gf1_i_look8(gus, SNDRV_GF1_GB_EMULATION_IRQ));
- }
-}
-
-void snd_gf1_peek_print_block(struct snd_gus_card * gus, unsigned int addr, int count, int w_16bit)
-{
- if (!w_16bit) {
- while (count-- > 0)
- printk(count > 0 ? "%02x:" : "%02x", snd_gf1_peek(gus, addr++));
- } else {
- while (count-- > 0) {
- printk(count > 0 ? "%04x:" : "%04x", snd_gf1_peek(gus, addr) | (snd_gf1_peek(gus, addr + 1) << 8));
- addr += 2;
- }
- }
-}
-
-#endif /* 0 */
-
-#endif
diff --git a/sound/isa/gus/gus_irq.c b/sound/isa/gus/gus_irq.c
index 2055aff71b50..0e1054402c91 100644
--- a/sound/isa/gus/gus_irq.c
+++ b/sound/isa/gus/gus_irq.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routine for IRQ handling from GF1/InterWave chip
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <sound/core.h>
@@ -41,7 +26,6 @@ __again:
if (status == 0)
return IRQ_RETVAL(handled);
handled = 1;
- /* snd_printk(KERN_DEBUG "IRQ: status = 0x%x\n", status); */
if (status & 0x02) {
STAT_ADD(gus->gf1.interrupt_stat_midi_in);
if (gus->gf1.interrupt_handler_midi_in)
@@ -65,9 +49,9 @@ __again:
continue; /* multi request */
already |= _current_; /* mark request */
#if 0
- printk(KERN_DEBUG "voice = %i, voice_status = 0x%x, "
- "voice_verify = %i\n",
- voice, voice_status, inb(GUSP(gus, GF1PAGE)));
+ dev_dbg(gus->card->dev,
+ "voice = %i, voice_status = 0x%x, voice_verify = %i\n",
+ voice, voice_status, inb(GUSP(gus, GF1PAGE)));
#endif
pvoice = &gus->gf1.voices[voice];
if (pvoice->use) {
@@ -140,10 +124,7 @@ static void snd_gus_irq_info_read(struct snd_info_entry *entry,
void snd_gus_irq_profile_init(struct snd_gus_card *gus)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(gus->card, "gusirq", &entry))
- snd_info_set_text_ops(entry, gus, snd_gus_irq_info_read);
+ snd_card_ro_proc_new(gus->card, "gusirq", gus, snd_gus_irq_info_read);
}
#endif
diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c
index 12eb98f2f931..5f50a39c6f16 100644
--- a/sound/isa/gus/gus_main.c
+++ b/sound/isa/gus/gus_main.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for Gravis UltraSound soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -24,6 +9,7 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/ioport.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/gus.h>
#include <sound/control.h>
@@ -36,18 +22,6 @@ MODULE_LICENSE("GPL");
static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches);
-int snd_gus_use_inc(struct snd_gus_card * gus)
-{
- if (!try_module_get(gus->card->module))
- return 0;
- return 1;
-}
-
-void snd_gus_use_dec(struct snd_gus_card * gus)
-{
- module_put(gus->card->module);
-}
-
static int snd_gus_joystick_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -68,20 +42,18 @@ static int snd_gus_joystick_get(struct snd_kcontrol *kcontrol, struct snd_ctl_el
static int snd_gus_joystick_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned char nval;
nval = ucontrol->value.integer.value[0] & 31;
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
change = gus->joystick_dac != nval;
gus->joystick_dac = nval;
snd_gf1_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
return change;
}
-static struct snd_kcontrol_new snd_gus_joystick_control = {
+static const struct snd_kcontrol_new snd_gus_joystick_control = {
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "Joystick Speed",
.info = snd_gus_joystick_info,
@@ -139,7 +111,7 @@ int snd_gus_create(struct snd_card *card,
{
struct snd_gus_card *gus;
int err;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_gus_dev_free,
};
@@ -170,31 +142,34 @@ int snd_gus_create(struct snd_card *card,
gus->gf1.reg_timerctrl = GUSP(gus, TIMERCNTRL);
gus->gf1.reg_timerdata = GUSP(gus, TIMERDATA);
/* allocate resources */
- if ((gus->gf1.res_port1 = request_region(port, 16, "GUS GF1 (Adlib/SB)")) == NULL) {
- snd_printk(KERN_ERR "gus: can't grab SB port 0x%lx\n", port);
+ gus->gf1.res_port1 = request_region(port, 16, "GUS GF1 (Adlib/SB)");
+ if (!gus->gf1.res_port1) {
+ dev_err(card->dev, "gus: can't grab SB port 0x%lx\n", port);
snd_gus_free(gus);
return -EBUSY;
}
- if ((gus->gf1.res_port2 = request_region(port + 0x100, 12, "GUS GF1 (Synth)")) == NULL) {
- snd_printk(KERN_ERR "gus: can't grab synth port 0x%lx\n", port + 0x100);
+ gus->gf1.res_port2 = request_region(port + 0x100, 12, "GUS GF1 (Synth)");
+ if (!gus->gf1.res_port2) {
+ dev_err(card->dev, "gus: can't grab synth port 0x%lx\n", port + 0x100);
snd_gus_free(gus);
return -EBUSY;
}
- if (irq >= 0 && request_irq(irq, snd_gus_interrupt, IRQF_DISABLED, "GUS GF1", (void *) gus)) {
- snd_printk(KERN_ERR "gus: can't grab irq %d\n", irq);
+ if (irq >= 0 && request_irq(irq, snd_gus_interrupt, 0, "GUS GF1", (void *) gus)) {
+ dev_err(card->dev, "gus: can't grab irq %d\n", irq);
snd_gus_free(gus);
return -EBUSY;
}
gus->gf1.irq = irq;
+ card->sync_irq = irq;
if (request_dma(dma1, "GUS - 1")) {
- snd_printk(KERN_ERR "gus: can't grab DMA1 %d\n", dma1);
+ dev_err(card->dev, "gus: can't grab DMA1 %d\n", dma1);
snd_gus_free(gus);
return -EBUSY;
}
gus->gf1.dma1 = dma1;
if (dma2 >= 0 && dma1 != dma2) {
if (request_dma(dma2, "GUS - 2")) {
- snd_printk(KERN_ERR "gus: can't grab DMA2 %d\n", dma2);
+ dev_err(card->dev, "gus: can't grab DMA2 %d\n", dma2);
snd_gus_free(gus);
return -EBUSY;
}
@@ -219,7 +194,8 @@ int snd_gus_create(struct snd_card *card,
gus->gf1.pcm_channels = pcm_channels;
gus->gf1.volume_ramp = 25;
gus->gf1.smooth_pan = 1;
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, gus, &ops)) < 0) {
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, gus, &ops);
+ if (err < 0) {
snd_gus_free(gus);
return err;
}
@@ -239,7 +215,9 @@ static int snd_gus_detect_memory(struct snd_gus_card * gus)
snd_gf1_poke(gus, 0L, 0xaa);
snd_gf1_poke(gus, 1L, 0x55);
if (snd_gf1_peek(gus, 0L) != 0xaa || snd_gf1_peek(gus, 1L) != 0x55) {
- snd_printk(KERN_ERR "plain GF1 card at 0x%lx without onboard DRAM?\n", gus->gf1.port);
+ dev_err(gus->card->dev,
+ "plain GF1 card at 0x%lx without onboard DRAM?\n",
+ gus->gf1.port);
return -ENOMEM;
}
for (idx = 1, d = 0xab; idx < 4; idx++, d++) {
@@ -269,11 +247,10 @@ static int snd_gus_detect_memory(struct snd_gus_card * gus)
static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches)
{
struct snd_card *card;
- unsigned long flags;
int irq, dma1, dma2;
- static unsigned char irqs[16] =
+ static const unsigned char irqs[16] =
{0, 0, 1, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7};
- static unsigned char dmas[8] =
+ static const unsigned char dmas[8] =
{6, 1, 0, 2, 0, 3, 4, 5};
if (snd_BUG_ON(!gus))
@@ -297,14 +274,14 @@ static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches)
dma1 |= gus->equal_dma ? 0x40 : (dma2 << 3);
if ((dma1 & 7) == 0 || (dma2 & 7) == 0) {
- snd_printk(KERN_ERR "Error! DMA isn't defined.\n");
+ dev_err(gus->card->dev, "Error! DMA isn't defined.\n");
return -EINVAL;
}
irq = gus->gf1.irq;
irq = abs(irq);
irq = irqs[irq & 0x0f];
if (irq == 0) {
- snd_printk(KERN_ERR "Error! IRQ isn't defined.\n");
+ dev_err(gus->card->dev, "Error! IRQ isn't defined.\n");
return -EINVAL;
}
irq |= 0x40;
@@ -312,34 +289,34 @@ static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches)
card->mixer.mix_ctrl_reg |= 0x10;
#endif
- spin_lock_irqsave(&gus->reg_lock, flags);
- outb(5, GUSP(gus, REGCNTRLS));
- outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
- outb(0x00, GUSP(gus, IRQDMACNTRLREG));
- outb(0, GUSP(gus, REGCNTRLS));
- spin_unlock_irqrestore(&gus->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &gus->reg_lock) {
+ outb(5, GUSP(gus, REGCNTRLS));
+ outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+ outb(0x00, GUSP(gus, IRQDMACNTRLREG));
+ outb(0, GUSP(gus, REGCNTRLS));
+ }
udelay(100);
- spin_lock_irqsave(&gus->reg_lock, flags);
- outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
- outb(dma1, GUSP(gus, IRQDMACNTRLREG));
- if (latches) {
- outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
- outb(irq, GUSP(gus, IRQDMACNTRLREG));
+ scoped_guard(spinlock_irqsave, &gus->reg_lock) {
+ outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+ outb(dma1, GUSP(gus, IRQDMACNTRLREG));
+ if (latches) {
+ outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+ outb(irq, GUSP(gus, IRQDMACNTRLREG));
+ }
}
- spin_unlock_irqrestore(&gus->reg_lock, flags);
udelay(100);
- spin_lock_irqsave(&gus->reg_lock, flags);
- outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
- outb(dma1, GUSP(gus, IRQDMACNTRLREG));
- if (latches) {
- outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
- outb(irq, GUSP(gus, IRQDMACNTRLREG));
+ scoped_guard(spinlock_irqsave, &gus->reg_lock) {
+ outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+ outb(dma1, GUSP(gus, IRQDMACNTRLREG));
+ if (latches) {
+ outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+ outb(irq, GUSP(gus, IRQDMACNTRLREG));
+ }
}
- spin_unlock_irqrestore(&gus->reg_lock, flags);
snd_gf1_delay(gus);
@@ -347,29 +324,28 @@ static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches)
gus->mix_cntrl_reg |= 0x08; /* enable latches */
else
gus->mix_cntrl_reg &= ~0x08; /* disable latches */
- spin_lock_irqsave(&gus->reg_lock, flags);
- outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
- outb(0, GUSP(gus, GF1PAGE));
- spin_unlock_irqrestore(&gus->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &gus->reg_lock) {
+ outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+ outb(0, GUSP(gus, GF1PAGE));
+ }
return 0;
}
static int snd_gus_check_version(struct snd_gus_card * gus)
{
- unsigned long flags;
unsigned char val, rev;
struct snd_card *card;
card = gus->card;
- spin_lock_irqsave(&gus->reg_lock, flags);
- outb(0x20, GUSP(gus, REGCNTRLS));
- val = inb(GUSP(gus, REGCNTRLS));
- rev = inb(GUSP(gus, BOARDVERSION));
- spin_unlock_irqrestore(&gus->reg_lock, flags);
- snd_printdd("GF1 [0x%lx] init - val = 0x%x, rev = 0x%x\n", gus->gf1.port, val, rev);
- strcpy(card->driver, "GUS");
- strcpy(card->longname, "Gravis UltraSound Classic (2.4)");
+ scoped_guard(spinlock_irqsave, &gus->reg_lock) {
+ outb(0x20, GUSP(gus, REGCNTRLS));
+ val = inb(GUSP(gus, REGCNTRLS));
+ rev = inb(GUSP(gus, BOARDVERSION));
+ }
+ dev_dbg(card->dev, "GF1 [0x%lx] init - val = 0x%x, rev = 0x%x\n", gus->gf1.port, val, rev);
+ strscpy(card->driver, "GUS");
+ strscpy(card->longname, "Gravis UltraSound Classic (2.4)");
if ((val != 255 && (val & 0x06)) || (rev >= 5 && rev != 255)) {
if (rev >= 5 && rev <= 9) {
gus->ics_flag = 1;
@@ -380,24 +356,27 @@ static int snd_gus_check_version(struct snd_gus_card * gus)
}
if (rev >= 10 && rev != 255) {
if (rev >= 10 && rev <= 11) {
- strcpy(card->driver, "GUS MAX");
- strcpy(card->longname, "Gravis UltraSound MAX");
+ strscpy(card->driver, "GUS MAX");
+ strscpy(card->longname, "Gravis UltraSound MAX");
gus->max_flag = 1;
} else if (rev == 0x30) {
- strcpy(card->driver, "GUS ACE");
- strcpy(card->longname, "Gravis UltraSound Ace");
+ strscpy(card->driver, "GUS ACE");
+ strscpy(card->longname, "Gravis UltraSound Ace");
gus->ace_flag = 1;
} else if (rev == 0x50) {
- strcpy(card->driver, "GUS Extreme");
- strcpy(card->longname, "Gravis UltraSound Extreme");
+ strscpy(card->driver, "GUS Extreme");
+ strscpy(card->longname, "Gravis UltraSound Extreme");
gus->ess_flag = 1;
} else {
- snd_printk(KERN_ERR "unknown GF1 revision number at 0x%lx - 0x%x (0x%x)\n", gus->gf1.port, rev, val);
- snd_printk(KERN_ERR " please - report to <perex@perex.cz>\n");
+ dev_err(card->dev,
+ "unknown GF1 revision number at 0x%lx - 0x%x (0x%x)\n",
+ gus->gf1.port, rev, val);
+ dev_err(card->dev,
+ " please - report to <perex@perex.cz>\n");
}
}
}
- strcpy(card->shortname, card->longname);
+ strscpy(card->shortname, card->longname, sizeof(card->shortname));
gus->uart_enable = 1; /* standard GUSes doesn't have midi uart trouble */
snd_gus_init_control(gus);
return 0;
@@ -408,14 +387,17 @@ int snd_gus_initialize(struct snd_gus_card *gus)
int err;
if (!gus->interwave) {
- if ((err = snd_gus_check_version(gus)) < 0) {
- snd_printk(KERN_ERR "version check failed\n");
+ err = snd_gus_check_version(gus);
+ if (err < 0) {
+ dev_err(gus->card->dev, "version check failed\n");
return err;
}
- if ((err = snd_gus_detect_memory(gus)) < 0)
+ err = snd_gus_detect_memory(gus);
+ if (err < 0)
return err;
}
- if ((err = snd_gus_init_dma_irq(gus, 1)) < 0)
+ err = snd_gus_init_dma_irq(gus, 1);
+ if (err < 0)
return err;
snd_gf1_start(gus);
gus->initialized = 1;
@@ -445,8 +427,6 @@ EXPORT_SYMBOL(snd_gf1_new_mixer);
/* gus_pcm.c */
EXPORT_SYMBOL(snd_gf1_pcm_new);
/* gus.c */
-EXPORT_SYMBOL(snd_gus_use_inc);
-EXPORT_SYMBOL(snd_gus_use_dec);
EXPORT_SYMBOL(snd_gus_create);
EXPORT_SYMBOL(snd_gus_initialize);
/* gus_irq.c */
@@ -463,20 +443,3 @@ EXPORT_SYMBOL(snd_gf1_translate_freq);
EXPORT_SYMBOL(snd_gf1_mem_alloc);
EXPORT_SYMBOL(snd_gf1_mem_xfree);
EXPORT_SYMBOL(snd_gf1_mem_free);
-EXPORT_SYMBOL(snd_gf1_mem_lock);
-
-/*
- * INIT part
- */
-
-static int __init alsa_gus_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_gus_exit(void)
-{
-}
-
-module_init(alsa_gus_init)
-module_exit(alsa_gus_exit)
diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c
index af888a022fc0..8d95d8d5abdf 100644
--- a/sound/isa/gus/gus_mem.c
+++ b/sound/isa/gus/gus_mem.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* GUS's memory allocation routines / bottom layer
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
@@ -30,17 +15,9 @@ static void snd_gf1_mem_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer);
#endif
-void snd_gf1_mem_lock(struct snd_gf1_mem * alloc, int xup)
-{
- if (!xup) {
- mutex_lock(&alloc->memory_mutex);
- } else {
- mutex_unlock(&alloc->memory_mutex);
- }
-}
-
-static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc,
- struct snd_gf1_mem_block * block)
+static struct snd_gf1_mem_block *
+snd_gf1_mem_xalloc(struct snd_gf1_mem *alloc, struct snd_gf1_mem_block *block,
+ const char *name)
{
struct snd_gf1_mem_block *pblock, *nblock;
@@ -48,6 +25,12 @@ static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc,
if (nblock == NULL)
return NULL;
*nblock = *block;
+ nblock->name = kstrdup(name, GFP_KERNEL);
+ if (!nblock->name) {
+ kfree(nblock);
+ return NULL;
+ }
+
pblock = alloc->first;
while (pblock) {
if (pblock->ptr > nblock->ptr) {
@@ -58,8 +41,7 @@ static struct snd_gf1_mem_block *snd_gf1_mem_xalloc(struct snd_gf1_mem * alloc,
alloc->first = nblock;
else
nblock->prev->next = nblock;
- mutex_unlock(&alloc->memory_mutex);
- return NULL;
+ return nblock;
}
pblock = pblock->next;
}
@@ -79,7 +61,6 @@ int snd_gf1_mem_xfree(struct snd_gf1_mem * alloc, struct snd_gf1_mem_block * blo
{
if (block->share) { /* ok.. shared block */
block->share--;
- mutex_unlock(&alloc->memory_mutex);
return 0;
}
if (alloc->first == block) {
@@ -191,46 +172,37 @@ struct snd_gf1_mem_block *snd_gf1_mem_alloc(struct snd_gf1_mem * alloc, int owne
{
struct snd_gf1_mem_block block, *nblock;
- snd_gf1_mem_lock(alloc, 0);
+ guard(mutex)(&alloc->memory_mutex);
if (share_id != NULL) {
nblock = snd_gf1_mem_share(alloc, share_id);
if (nblock != NULL) {
if (size != (int)nblock->size) {
/* TODO: remove in the future */
- snd_printk(KERN_ERR "snd_gf1_mem_alloc - share: sizes differ\n");
+ pr_err("%s - share: sizes differ\n", __func__);
goto __std;
}
nblock->share++;
- snd_gf1_mem_lock(alloc, 1);
return NULL;
}
}
__std:
- if (snd_gf1_mem_find(alloc, &block, size, w_16, align) < 0) {
- snd_gf1_mem_lock(alloc, 1);
+ if (snd_gf1_mem_find(alloc, &block, size, w_16, align) < 0)
return NULL;
- }
if (share_id != NULL)
memcpy(&block.share_id, share_id, sizeof(block.share_id));
block.owner = owner;
- block.name = kstrdup(name, GFP_KERNEL);
- nblock = snd_gf1_mem_xalloc(alloc, &block);
- snd_gf1_mem_lock(alloc, 1);
+ nblock = snd_gf1_mem_xalloc(alloc, &block, name);
return nblock;
}
int snd_gf1_mem_free(struct snd_gf1_mem * alloc, unsigned int address)
{
- int result;
struct snd_gf1_mem_block *block;
- snd_gf1_mem_lock(alloc, 0);
- if ((block = snd_gf1_mem_look(alloc, address)) != NULL) {
- result = snd_gf1_mem_xfree(alloc, block);
- snd_gf1_mem_lock(alloc, 1);
- return result;
- }
- snd_gf1_mem_lock(alloc, 1);
+ guard(mutex)(&alloc->memory_mutex);
+ block = snd_gf1_mem_look(alloc, address);
+ if (block)
+ return snd_gf1_mem_xfree(alloc, block);
return -EINVAL;
}
@@ -238,9 +210,6 @@ int snd_gf1_mem_init(struct snd_gus_card * gus)
{
struct snd_gf1_mem *alloc;
struct snd_gf1_mem_block block;
-#ifdef CONFIG_SND_DEBUG
- struct snd_info_entry *entry;
-#endif
alloc = &gus->gf1.mem_alloc;
mutex_init(&alloc->memory_mutex);
@@ -253,18 +222,15 @@ int snd_gf1_mem_init(struct snd_gus_card * gus)
if (gus->gf1.enh_mode) {
block.ptr = 0;
block.size = 1024;
- block.name = kstrdup("InterWave LFOs", GFP_KERNEL);
- if (snd_gf1_mem_xalloc(alloc, &block) == NULL)
+ if (!snd_gf1_mem_xalloc(alloc, &block, "InterWave LFOs"))
return -ENOMEM;
}
block.ptr = gus->gf1.default_voice_address;
block.size = 4;
- block.name = kstrdup("Voice default (NULL's)", GFP_KERNEL);
- if (snd_gf1_mem_xalloc(alloc, &block) == NULL)
+ if (!snd_gf1_mem_xalloc(alloc, &block, "Voice default (NULL's)"))
return -ENOMEM;
#ifdef CONFIG_SND_DEBUG
- if (! snd_card_proc_new(gus->card, "gusmem", &entry))
- snd_info_set_text_ops(entry, gus, snd_gf1_mem_info_read);
+ snd_card_ro_proc_new(gus->card, "gusmem", gus, snd_gf1_mem_info_read);
#endif
return 0;
}
@@ -296,7 +262,7 @@ static void snd_gf1_mem_info_read(struct snd_info_entry *entry,
gus = entry->private_data;
alloc = &gus->gf1.mem_alloc;
- mutex_lock(&alloc->memory_mutex);
+ guard(mutex)(&alloc->memory_mutex);
snd_iprintf(buffer, "8-bit banks : \n ");
for (i = 0; i < 4; i++)
snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_8[i].address, alloc->banks_8[i].size >> 10, i + 1 < 4 ? "," : "");
@@ -310,7 +276,7 @@ static void snd_gf1_mem_info_read(struct snd_info_entry *entry,
used = 0;
for (block = alloc->first, i = 0; block; block = block->next, i++) {
used += block->size;
- snd_iprintf(buffer, "Block %i at 0x%lx onboard 0x%x size %i (0x%x):\n", i, (long) block, block->ptr, block->size, block->size);
+ snd_iprintf(buffer, "Block %i onboard 0x%x size %i (0x%x):\n", i, block->ptr, block->size, block->size);
if (block->share ||
block->share_id[0] || block->share_id[1] ||
block->share_id[2] || block->share_id[3])
@@ -340,7 +306,6 @@ static void snd_gf1_mem_info_read(struct snd_info_entry *entry,
}
snd_iprintf(buffer, " Total: memory = %i, used = %i, free = %i\n",
total, used, total - used);
- mutex_unlock(&alloc->memory_mutex);
#if 0
ultra_iprintf(buffer, " Verify: free = %i, max 8-bit block = %i, max 16-bit block = %i\n",
ultra_memory_free_size(card, &card->gf1.mem_alloc),
diff --git a/sound/isa/gus/gus_mem_proc.c b/sound/isa/gus/gus_mem_proc.c
index 2ccb3fadd7be..b5e1d1649500 100644
--- a/sound/isa/gus/gus_mem_proc.c
+++ b/sound/isa/gus/gus_mem_proc.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* GUS's memory access via proc filesystem
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/slab.h>
@@ -52,7 +37,7 @@ static void snd_gf1_mem_proc_free(struct snd_info_entry *entry)
kfree(priv);
}
-static struct snd_info_entry_ops snd_gf1_mem_proc_ops = {
+static const struct snd_info_entry_ops snd_gf1_mem_proc_ops = {
.read = snd_gf1_mem_proc_dump,
};
diff --git a/sound/isa/gus/gus_mixer.c b/sound/isa/gus/gus_mixer.c
index 0dd43414016e..9bfdb4e45a5d 100644
--- a/sound/isa/gus/gus_mixer.c
+++ b/sound/isa/gus/gus_mixer.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of ICS 2101 chip and "mixer" in GF1 chip
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/time.h>
@@ -52,7 +37,6 @@ static int snd_gf1_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
static int snd_gf1_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int shift = kcontrol->private_value & 0xff;
int invert = (kcontrol->private_value >> 8) & 1;
int change;
@@ -62,13 +46,12 @@ static int snd_gf1_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
if (invert)
nval ^= 1;
nval <<= shift;
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
oval = gus->mix_cntrl_reg;
nval = (oval & ~(1 << shift)) | nval;
change = nval != oval;
outb(gus->mix_cntrl_reg = nval, GUSP(gus, MIXCNTRLREG));
outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE));
- spin_unlock_irqrestore(&gus->reg_lock, flags);
return change;
}
@@ -90,14 +73,12 @@ static int snd_ics_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
static int snd_ics_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int addr = kcontrol->private_value & 0xff;
unsigned char left, right;
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
left = gus->gf1.ics_regs[addr][0];
right = gus->gf1.ics_regs[addr][1];
- spin_unlock_irqrestore(&gus->reg_lock, flags);
ucontrol->value.integer.value[0] = left & 127;
ucontrol->value.integer.value[1] = right & 127;
return 0;
@@ -106,25 +87,21 @@ static int snd_ics_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
static int snd_ics_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int addr = kcontrol->private_value & 0xff;
int change;
- unsigned char val1, val2, oval1, oval2, tmp;
+ unsigned char val1, val2, oval1, oval2;
val1 = ucontrol->value.integer.value[0] & 127;
val2 = ucontrol->value.integer.value[1] & 127;
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
oval1 = gus->gf1.ics_regs[addr][0];
oval2 = gus->gf1.ics_regs[addr][1];
change = val1 != oval1 || val2 != oval2;
gus->gf1.ics_regs[addr][0] = val1;
gus->gf1.ics_regs[addr][1] = val2;
if (gus->ics_flag && gus->ics_flipped &&
- (addr == SNDRV_ICS_GF1_DEV || addr == SNDRV_ICS_MASTER_DEV)) {
- tmp = val1;
- val1 = val2;
- val2 = tmp;
- }
+ (addr == SNDRV_ICS_GF1_DEV || addr == SNDRV_ICS_MASTER_DEV))
+ swap(val1, val2);
addr <<= 3;
outb(addr | 0, GUSP(gus, MIXCNTRLPORT));
outb(1, GUSP(gus, MIXDATAPORT));
@@ -134,17 +111,16 @@ static int snd_ics_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
outb(2, GUSP(gus, MIXDATAPORT));
outb(addr | 3, GUSP(gus, MIXCNTRLPORT));
outb((unsigned char) val2, GUSP(gus, MIXDATAPORT));
- spin_unlock_irqrestore(&gus->reg_lock, flags);
return change;
}
-static struct snd_kcontrol_new snd_gf1_controls[] = {
+static const struct snd_kcontrol_new snd_gf1_controls[] = {
GF1_SINGLE("Master Playback Switch", 0, 1, 1),
GF1_SINGLE("Line Switch", 0, 0, 1),
GF1_SINGLE("Mic Switch", 0, 2, 0)
};
-static struct snd_kcontrol_new snd_ics_controls[] = {
+static const struct snd_kcontrol_new snd_ics_controls[] = {
GF1_SINGLE("Master Playback Switch", 0, 1, 1),
ICS_DOUBLE("Master Playback Volume", 0, SNDRV_ICS_MASTER_DEV),
ICS_DOUBLE("Synth Playback Volume", 0, SNDRV_ICS_GF1_DEV),
@@ -170,7 +146,7 @@ int snd_gf1_new_mixer(struct snd_gus_card * gus)
if (gus->ics_flag)
snd_component_add(card, "ICS2101");
if (card->mixername[0] == '\0') {
- strcpy(card->mixername, gus->ics_flag ? "GF1,ICS2101" : "GF1");
+ strscpy(card->mixername, gus->ics_flag ? "GF1,ICS2101" : "GF1");
} else {
if (gus->ics_flag)
strcat(card->mixername, ",ICS2101");
@@ -180,12 +156,14 @@ int snd_gf1_new_mixer(struct snd_gus_card * gus)
if (!gus->ics_flag) {
max = gus->ess_flag ? 1 : ARRAY_SIZE(snd_gf1_controls);
for (idx = 0; idx < max; idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_gf1_controls[idx], gus))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_gf1_controls[idx], gus));
+ if (err < 0)
return err;
}
} else {
for (idx = 0; idx < ARRAY_SIZE(snd_ics_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ics_controls[idx], gus))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_ics_controls[idx], gus));
+ if (err < 0)
return err;
}
}
diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c
index 2dcf45bf7293..9249cbff30f3 100644
--- a/sound/isa/gus/gus_pcm.c
+++ b/sound/isa/gus/gus_pcm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of GF1 chip (PCM things)
@@ -7,26 +8,12 @@
*
* This code emulates autoinit DMA transfer for playback, recording by GF1
* chip doesn't support autoinit DMA.
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <asm/dma.h>
#include <linux/slab.h>
+#include <linux/sched/signal.h>
+
#include <sound/core.h>
#include <sound/control.h>
#include <sound/gus.h>
@@ -59,8 +46,6 @@ struct gus_pcm_private {
int final_volume;
};
-static int snd_gf1_pcm_use_dma = 1;
-
static void snd_gf1_pcm_block_change_ack(struct snd_gus_card * gus, void *private_data)
{
struct gus_pcm_private *pcmp = private_data;
@@ -82,10 +67,6 @@ static int snd_gf1_pcm_block_change(struct snd_pcm_substream *substream,
count += offset & 31;
offset &= ~31;
- /*
- snd_printk(KERN_DEBUG "block change - offset = 0x%x, count = 0x%x\n",
- offset, count);
- */
memset(&block, 0, sizeof(block));
block.cmd = SNDRV_GF1_DMA_IRQ;
if (snd_pcm_format_unsigned(runtime->format))
@@ -108,7 +89,6 @@ static void snd_gf1_pcm_trigger_up(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
struct snd_gus_card * gus = pcmp->gus;
- unsigned long flags;
unsigned char voice_ctrl, ramp_ctrl;
unsigned short rate;
unsigned int curr, begin, end;
@@ -116,14 +96,12 @@ static void snd_gf1_pcm_trigger_up(struct snd_pcm_substream *substream)
unsigned char pan;
unsigned int voice;
- spin_lock_irqsave(&pcmp->lock, flags);
- if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) {
- spin_unlock_irqrestore(&pcmp->lock, flags);
- return;
+ scoped_guard(spinlock_irqsave, &pcmp->lock) {
+ if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)
+ return;
+ pcmp->flags |= SNDRV_GF1_PCM_PFLG_ACTIVE;
+ pcmp->final_volume = 0;
}
- pcmp->flags |= SNDRV_GF1_PCM_PFLG_ACTIVE;
- pcmp->final_volume = 0;
- spin_unlock_irqrestore(&pcmp->lock, flags);
rate = snd_gf1_translate_freq(gus, runtime->rate << 4);
/* enable WAVE IRQ */
voice_ctrl = snd_pcm_format_width(runtime->format) == 16 ? 0x24 : 0x20;
@@ -138,14 +116,9 @@ static void snd_gf1_pcm_trigger_up(struct snd_pcm_substream *substream)
curr = begin + (pcmp->bpos * pcmp->block_size) / runtime->channels;
end = curr + (pcmp->block_size / runtime->channels);
end -= snd_pcm_format_width(runtime->format) == 16 ? 2 : 1;
- /*
- snd_printk(KERN_DEBUG "init: curr=0x%x, begin=0x%x, end=0x%x, "
- "ctrl=0x%x, ramp=0x%x, rate=0x%x\n",
- curr, begin, end, voice_ctrl, ramp_ctrl, rate);
- */
pan = runtime->channels == 2 ? (!voice ? 1 : 14) : 8;
vol = !voice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, pan);
snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, rate);
@@ -161,9 +134,9 @@ static void snd_gf1_pcm_trigger_up(struct snd_pcm_substream *substream)
snd_gf1_delay(gus);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
}
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
- spin_lock_irqsave(&gus->reg_lock, flags);
+
+ guard(spinlock_irqsave)(&gus->reg_lock);
for (voice = 0; voice < pcmp->voices; voice++) {
snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
if (gus->gf1.enh_mode)
@@ -180,7 +153,6 @@ static void snd_gf1_pcm_trigger_up(struct snd_pcm_substream *substream)
voice_ctrl &= ~0x20; /* disable IRQ for next voice */
}
}
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
static void snd_gf1_pcm_interrupt_wave(struct snd_gus_card * gus,
@@ -193,65 +165,65 @@ static void snd_gf1_pcm_interrupt_wave(struct snd_gus_card * gus,
unsigned int end, step;
if (!pvoice->private_data) {
- snd_printd("snd_gf1_pcm: unknown wave irq?\n");
+ dev_dbg(gus->card->dev, "%s: unknown wave irq?\n", __func__);
snd_gf1_smart_stop_voice(gus, pvoice->number);
return;
}
pcmp = pvoice->private_data;
if (pcmp == NULL) {
- snd_printd("snd_gf1_pcm: unknown wave irq?\n");
+ dev_dbg(gus->card->dev, "%s: unknown wave irq?\n", __func__);
snd_gf1_smart_stop_voice(gus, pvoice->number);
return;
}
gus = pcmp->gus;
runtime = pcmp->substream->runtime;
- spin_lock(&gus->reg_lock);
- snd_gf1_select_voice(gus, pvoice->number);
- voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & ~0x8b;
- ramp_ctrl = (snd_gf1_read8(gus, SNDRV_GF1_VB_VOLUME_CONTROL) & ~0xa4) | 0x03;
+ scoped_guard(spinlock, &gus->reg_lock) {
+ snd_gf1_select_voice(gus, pvoice->number);
+ voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & ~0x8b;
+ ramp_ctrl = (snd_gf1_read8(gus, SNDRV_GF1_VB_VOLUME_CONTROL) & ~0xa4) | 0x03;
#if 0
- snd_gf1_select_voice(gus, pvoice->number);
- printk(KERN_DEBUG "position = 0x%x\n",
- (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
- snd_gf1_select_voice(gus, pcmp->pvoices[1]->number);
- printk(KERN_DEBUG "position = 0x%x\n",
- (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
- snd_gf1_select_voice(gus, pvoice->number);
+ snd_gf1_select_voice(gus, pvoice->number);
+ dev_dbg(gus->card->dev, "position = 0x%x\n",
+ (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
+ snd_gf1_select_voice(gus, pcmp->pvoices[1]->number);
+ dev_dbg(gus->card->dev, "position = 0x%x\n",
+ (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
+ snd_gf1_select_voice(gus, pvoice->number);
#endif
- pcmp->bpos++;
- pcmp->bpos %= pcmp->blocks;
- if (pcmp->bpos + 1 >= pcmp->blocks) { /* last block? */
- voice_ctrl |= 0x08; /* enable loop */
- } else {
- ramp_ctrl |= 0x04; /* enable rollover */
- }
- end = pcmp->memory + (((pcmp->bpos + 1) * pcmp->block_size) / runtime->channels);
- end -= voice_ctrl & 4 ? 2 : 1;
- step = pcmp->dma_size / runtime->channels;
- voice_ctrl |= 0x20;
- if (!pcmp->final_volume) {
- ramp_ctrl |= 0x20;
- ramp_ctrl &= ~0x03;
- }
- for (idx = 0; idx < pcmp->voices; idx++, end += step) {
- snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number);
- snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4);
- snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
- snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
- voice_ctrl &= ~0x20;
- }
- if (!gus->gf1.enh_mode) {
- snd_gf1_delay(gus);
+ pcmp->bpos++;
+ pcmp->bpos %= pcmp->blocks;
+ if (pcmp->bpos + 1 >= pcmp->blocks) { /* last block? */
+ voice_ctrl |= 0x08; /* enable loop */
+ } else {
+ ramp_ctrl |= 0x04; /* enable rollover */
+ }
+ end = pcmp->memory + (((pcmp->bpos + 1) * pcmp->block_size) / runtime->channels);
+ end -= voice_ctrl & 4 ? 2 : 1;
+ step = pcmp->dma_size / runtime->channels;
voice_ctrl |= 0x20;
- for (idx = 0; idx < pcmp->voices; idx++) {
+ if (!pcmp->final_volume) {
+ ramp_ctrl |= 0x20;
+ ramp_ctrl &= ~0x03;
+ }
+ for (idx = 0; idx < pcmp->voices; idx++, end += step) {
snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number);
+ snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4);
snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
voice_ctrl &= ~0x20;
}
+ if (!gus->gf1.enh_mode) {
+ snd_gf1_delay(gus);
+ voice_ctrl |= 0x20;
+ for (idx = 0; idx < pcmp->voices; idx++) {
+ snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number);
+ snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
+ snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
+ voice_ctrl &= ~0x20;
+ }
+ }
}
- spin_unlock(&gus->reg_lock);
snd_pcm_period_elapsed(pcmp->substream);
#if 0
@@ -276,10 +248,10 @@ static void snd_gf1_pcm_interrupt_volume(struct snd_gus_card * gus,
struct gus_pcm_private *pcmp = pvoice->private_data;
/* stop ramp, but leave rollover bit untouched */
- spin_lock(&gus->reg_lock);
- snd_gf1_select_voice(gus, pvoice->number);
- snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
- spin_unlock(&gus->reg_lock);
+ scoped_guard(spinlock, &gus->reg_lock) {
+ snd_gf1_select_voice(gus, pvoice->number);
+ snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+ }
if (pcmp == NULL)
return;
/* are we active? */
@@ -290,11 +262,10 @@ static void snd_gf1_pcm_interrupt_volume(struct snd_gus_card * gus,
if (pcmp->substream == NULL)
return;
vol = !cvoice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
- spin_lock(&gus->reg_lock);
+ guard(spinlock)(&gus->reg_lock);
snd_gf1_select_voice(gus, pvoice->number);
snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol);
pcmp->final_volume = 1;
- spin_unlock(&gus->reg_lock);
}
static void snd_gf1_pcm_volume_change(struct snd_gus_card * gus)
@@ -306,20 +277,14 @@ static int snd_gf1_pcm_poke_block(struct snd_gus_card *gus, unsigned char *buf,
int w16, int invert)
{
unsigned int len;
- unsigned long flags;
- /*
- printk(KERN_DEBUG
- "poke block; buf = 0x%x, pos = %i, count = %i, port = 0x%x\n",
- (int)buf, pos, count, gus->gf1.port);
- */
while (count > 0) {
len = count;
if (len > 512) /* limit, to allow IRQ */
len = 512;
count -= len;
if (gus->interwave) {
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01 | (invert ? 0x08 : 0x00));
snd_gf1_dram_addr(gus, pos);
if (w16) {
@@ -328,7 +293,6 @@ static int snd_gf1_pcm_poke_block(struct snd_gus_card *gus, unsigned char *buf,
} else {
outsb(GUSP(gus, DRAM), buf, len);
}
- spin_unlock_irqrestore(&gus->reg_lock, flags);
buf += 512;
pos += 512;
} else {
@@ -353,66 +317,67 @@ static int snd_gf1_pcm_poke_block(struct snd_gus_card *gus, unsigned char *buf,
return 0;
}
-static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream,
- int voice,
- snd_pcm_uframes_t pos,
- void __user *src,
- snd_pcm_uframes_t count)
+static int get_bpos(struct gus_pcm_private *pcmp, int voice, unsigned int pos,
+ unsigned int len)
{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct gus_pcm_private *pcmp = runtime->private_data;
- unsigned int bpos, len;
-
- bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2));
- len = samples_to_bytes(runtime, count);
+ unsigned int bpos = pos + (voice * (pcmp->dma_size / 2));
if (snd_BUG_ON(bpos > pcmp->dma_size))
return -EIO;
if (snd_BUG_ON(bpos + len > pcmp->dma_size))
return -EIO;
- if (copy_from_user(runtime->dma_area + bpos, src, len))
- return -EFAULT;
- if (snd_gf1_pcm_use_dma && len > 32) {
- return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len);
- } else {
- struct snd_gus_card *gus = pcmp->gus;
- int err, w16, invert;
+ return bpos;
+}
- w16 = (snd_pcm_format_width(runtime->format) == 16);
- invert = snd_pcm_format_unsigned(runtime->format);
- if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0)
- return err;
- }
- return 0;
+static int playback_copy_ack(struct snd_pcm_substream *substream,
+ unsigned int bpos, unsigned int len)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct gus_pcm_private *pcmp = runtime->private_data;
+ struct snd_gus_card *gus = pcmp->gus;
+ int w16, invert;
+
+ if (len > 32)
+ return snd_gf1_pcm_block_change(substream, bpos,
+ pcmp->memory + bpos, len);
+
+ w16 = (snd_pcm_format_width(runtime->format) == 16);
+ invert = snd_pcm_format_unsigned(runtime->format);
+ return snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos,
+ pcmp->memory + bpos, len, w16, invert);
+}
+
+static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream,
+ int voice, unsigned long pos,
+ struct iov_iter *src, unsigned long count)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct gus_pcm_private *pcmp = runtime->private_data;
+ unsigned int len = count;
+ int bpos;
+
+ bpos = get_bpos(pcmp, voice, pos, len);
+ if (bpos < 0)
+ return bpos;
+ if (copy_from_iter(runtime->dma_area + bpos, len, src) != len)
+ return -EFAULT;
+ return playback_copy_ack(substream, bpos, len);
}
static int snd_gf1_pcm_playback_silence(struct snd_pcm_substream *substream,
- int voice,
- snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
+ int voice, unsigned long pos,
+ unsigned long count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
- unsigned int bpos, len;
+ unsigned int len = count;
+ int bpos;
- bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2));
- len = samples_to_bytes(runtime, count);
- if (snd_BUG_ON(bpos > pcmp->dma_size))
- return -EIO;
- if (snd_BUG_ON(bpos + len > pcmp->dma_size))
- return -EIO;
- snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos, count);
- if (snd_gf1_pcm_use_dma && len > 32) {
- return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len);
- } else {
- struct snd_gus_card *gus = pcmp->gus;
- int err, w16, invert;
-
- w16 = (snd_pcm_format_width(runtime->format) == 16);
- invert = snd_pcm_format_unsigned(runtime->format);
- if ((err = snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, pcmp->memory + bpos, len, w16, invert)) < 0)
- return err;
- }
- return 0;
+ bpos = get_bpos(pcmp, voice, pos, len);
+ if (bpos < 0)
+ return bpos;
+ snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos,
+ bytes_to_samples(runtime, count));
+ return playback_copy_ack(substream, bpos, len);
}
static int snd_gf1_pcm_playback_hw_params(struct snd_pcm_substream *substream,
@@ -421,27 +386,26 @@ static int snd_gf1_pcm_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
- int err;
-
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
- return err;
- if (err > 0) { /* change */
+
+ if (runtime->buffer_changed) {
struct snd_gf1_mem_block *block;
if (pcmp->memory > 0) {
snd_gf1_mem_free(&gus->gf1.mem_alloc, pcmp->memory);
pcmp->memory = 0;
}
- if ((block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
- SNDRV_GF1_MEM_OWNER_DRIVER,
- "GF1 PCM",
- runtime->dma_bytes, 1, 32,
- NULL)) == NULL)
+ block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
+ SNDRV_GF1_MEM_OWNER_DRIVER,
+ "GF1 PCM",
+ runtime->dma_bytes, 1, 32,
+ NULL);
+ if (!block)
return -ENOMEM;
pcmp->memory = block->ptr;
}
pcmp->voices = params_channels(hw_params);
if (pcmp->pvoices[0] == NULL) {
- if ((pcmp->pvoices[0] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL)
+ pcmp->pvoices[0] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0);
+ if (!pcmp->pvoices[0])
return -ENOMEM;
pcmp->pvoices[0]->handler_wave = snd_gf1_pcm_interrupt_wave;
pcmp->pvoices[0]->handler_volume = snd_gf1_pcm_interrupt_volume;
@@ -449,7 +413,8 @@ static int snd_gf1_pcm_playback_hw_params(struct snd_pcm_substream *substream,
pcmp->pvoices[0]->private_data = pcmp;
}
if (pcmp->voices > 1 && pcmp->pvoices[1] == NULL) {
- if ((pcmp->pvoices[1] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0)) == NULL)
+ pcmp->pvoices[1] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0);
+ if (!pcmp->pvoices[1])
return -ENOMEM;
pcmp->pvoices[1]->handler_wave = snd_gf1_pcm_interrupt_wave;
pcmp->pvoices[1]->handler_volume = snd_gf1_pcm_interrupt_volume;
@@ -469,7 +434,6 @@ static int snd_gf1_pcm_playback_hw_free(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
- snd_pcm_lib_free_pages(substream);
if (pcmp->pvoices[0]) {
snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[0]);
pcmp->pvoices[0] = NULL;
@@ -508,9 +472,9 @@ static int snd_gf1_pcm_playback_trigger(struct snd_pcm_substream *substream,
if (cmd == SNDRV_PCM_TRIGGER_START) {
snd_gf1_pcm_trigger_up(substream);
} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
- spin_lock(&pcmp->lock);
- pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE;
- spin_unlock(&pcmp->lock);
+ scoped_guard(spinlock, &pcmp->lock) {
+ pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE;
+ }
voice = pcmp->pvoices[0]->number;
snd_gf1_stop_voices(gus, voice, voice);
if (pcmp->pvoices[1]) {
@@ -532,7 +496,7 @@ static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(struct snd_pcm_substream *
unsigned char voice_ctrl;
pos = 0;
- spin_lock(&gus->reg_lock);
+ guard(spinlock)(&gus->reg_lock);
if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) {
snd_gf1_select_voice(gus, pcmp->pvoices[0]->number);
voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
@@ -541,18 +505,17 @@ static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(struct snd_pcm_substream *
pos <<= 1;
pos = bytes_to_frames(runtime, pos);
}
- spin_unlock(&gus->reg_lock);
return pos;
}
-static struct snd_ratnum clock = {
+static const struct snd_ratnum clock = {
.num = 9878400/16,
.den_min = 2,
.den_max = 257,
.den_step = 1,
};
-static struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
+static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
.nrats = 1,
.rats = &clock,
};
@@ -572,12 +535,7 @@ static int snd_gf1_pcm_capture_hw_params(struct snd_pcm_substream *substream,
gus->gf1.pcm_rcntrl_reg |= 4;
if (snd_pcm_format_unsigned(params_format(hw_params)))
gus->gf1.pcm_rcntrl_reg |= 0x80;
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_gf1_pcm_capture_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static int snd_gf1_pcm_capture_prepare(struct snd_pcm_substream *substream)
@@ -606,10 +564,9 @@ static int snd_gf1_pcm_capture_trigger(struct snd_pcm_substream *substream,
return -EINVAL;
}
- spin_lock(&gus->reg_lock);
+ guard(spinlock)(&gus->reg_lock);
snd_gf1_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, val);
snd_gf1_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL);
- spin_unlock(&gus->reg_lock);
return 0;
}
@@ -633,7 +590,7 @@ static void snd_gf1_pcm_interrupt_dma_read(struct snd_gus_card * gus)
}
}
-static struct snd_pcm_hardware snd_gf1_pcm_playback =
+static const struct snd_pcm_hardware snd_gf1_pcm_playback =
{
.info = SNDRV_PCM_INFO_NONINTERLEAVED,
.formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
@@ -651,7 +608,7 @@ static struct snd_pcm_hardware snd_gf1_pcm_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_gf1_pcm_capture =
+static const struct snd_pcm_hardware snd_gf1_pcm_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -693,10 +650,12 @@ static int snd_gf1_pcm_playback_open(struct snd_pcm_substream *substream)
runtime->private_free = snd_gf1_pcm_playback_free;
#if 0
- printk(KERN_DEBUG "playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n",
- (long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer);
+ dev_dbg(gus->card->dev,
+ "playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n",
+ (long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer);
#endif
- if ((err = snd_gf1_dma_init(gus)) < 0)
+ err = snd_gf1_dma_init(gus);
+ if (err < 0)
return err;
pcmp->flags = SNDRV_GF1_PCM_PFLG_NONE;
pcmp->substream = substream;
@@ -714,7 +673,7 @@ static int snd_gf1_pcm_playback_close(struct snd_pcm_substream *substream)
struct gus_pcm_private *pcmp = runtime->private_data;
if (!wait_event_timeout(pcmp->sleep, (atomic_read(&pcmp->dma_count) <= 0), 2*HZ))
- snd_printk(KERN_ERR "gf1 pcm - serious DMA problem\n");
+ dev_err(gus->card->dev, "gf1 pcm - serious DMA problem\n");
snd_gf1_dma_done(gus);
return 0;
@@ -756,19 +715,16 @@ static int snd_gf1_pcm_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl
static int snd_gf1_pcm_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- spin_lock_irqsave(&gus->pcm_volume_level_lock, flags);
+ guard(spinlock_irqsave)(&gus->pcm_volume_level_lock);
ucontrol->value.integer.value[0] = gus->gf1.pcm_volume_level_left1;
ucontrol->value.integer.value[1] = gus->gf1.pcm_volume_level_right1;
- spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags);
return 0;
}
static int snd_gf1_pcm_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned int idx;
unsigned short val1, val2, vol;
@@ -777,37 +733,36 @@ static int snd_gf1_pcm_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
val1 = ucontrol->value.integer.value[0] & 127;
val2 = ucontrol->value.integer.value[1] & 127;
- spin_lock_irqsave(&gus->pcm_volume_level_lock, flags);
- change = val1 != gus->gf1.pcm_volume_level_left1 ||
- val2 != gus->gf1.pcm_volume_level_right1;
- gus->gf1.pcm_volume_level_left1 = val1;
- gus->gf1.pcm_volume_level_right1 = val2;
- gus->gf1.pcm_volume_level_left = snd_gf1_lvol_to_gvol_raw(val1 << 9) << 4;
- gus->gf1.pcm_volume_level_right = snd_gf1_lvol_to_gvol_raw(val2 << 9) << 4;
- spin_unlock_irqrestore(&gus->pcm_volume_level_lock, flags);
+ scoped_guard(spinlock_irqsave, &gus->pcm_volume_level_lock) {
+ change = val1 != gus->gf1.pcm_volume_level_left1 ||
+ val2 != gus->gf1.pcm_volume_level_right1;
+ gus->gf1.pcm_volume_level_left1 = val1;
+ gus->gf1.pcm_volume_level_right1 = val2;
+ gus->gf1.pcm_volume_level_left = snd_gf1_lvol_to_gvol_raw(val1 << 9) << 4;
+ gus->gf1.pcm_volume_level_right = snd_gf1_lvol_to_gvol_raw(val2 << 9) << 4;
+ }
/* are we active? */
- spin_lock_irqsave(&gus->voice_alloc, flags);
- for (idx = 0; idx < 32; idx++) {
- pvoice = &gus->gf1.voices[idx];
- if (!pvoice->pcm)
- continue;
- pcmp = pvoice->private_data;
- if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE))
- continue;
- /* load real volume - better precision */
- spin_lock(&gus->reg_lock);
- snd_gf1_select_voice(gus, pvoice->number);
- snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
- vol = pvoice == pcmp->pvoices[0] ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
- snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol);
- pcmp->final_volume = 1;
- spin_unlock(&gus->reg_lock);
+ scoped_guard(spinlock_irqsave, &gus->voice_alloc) {
+ for (idx = 0; idx < 32; idx++) {
+ pvoice = &gus->gf1.voices[idx];
+ if (!pvoice->pcm)
+ continue;
+ pcmp = pvoice->private_data;
+ if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE))
+ continue;
+ /* load real volume - better precision */
+ guard(spinlock)(&gus->reg_lock);
+ snd_gf1_select_voice(gus, pvoice->number);
+ snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
+ vol = pvoice == pcmp->pvoices[0] ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
+ snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol);
+ pcmp->final_volume = 1;
+ }
}
- spin_unlock_irqrestore(&gus->voice_alloc, flags);
return change;
}
-static struct snd_kcontrol_new snd_gf1_pcm_volume_control =
+static const struct snd_kcontrol_new snd_gf1_pcm_volume_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
@@ -816,7 +771,7 @@ static struct snd_kcontrol_new snd_gf1_pcm_volume_control =
.put = snd_gf1_pcm_volume_put
};
-static struct snd_kcontrol_new snd_gf1_pcm_volume_control1 =
+static const struct snd_kcontrol_new snd_gf1_pcm_volume_control1 =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "GPCM Playback Volume",
@@ -825,31 +780,28 @@ static struct snd_kcontrol_new snd_gf1_pcm_volume_control1 =
.put = snd_gf1_pcm_volume_put
};
-static struct snd_pcm_ops snd_gf1_pcm_playback_ops = {
+static const struct snd_pcm_ops snd_gf1_pcm_playback_ops = {
.open = snd_gf1_pcm_playback_open,
.close = snd_gf1_pcm_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_gf1_pcm_playback_hw_params,
.hw_free = snd_gf1_pcm_playback_hw_free,
.prepare = snd_gf1_pcm_playback_prepare,
.trigger = snd_gf1_pcm_playback_trigger,
.pointer = snd_gf1_pcm_playback_pointer,
.copy = snd_gf1_pcm_playback_copy,
- .silence = snd_gf1_pcm_playback_silence,
+ .fill_silence = snd_gf1_pcm_playback_silence,
};
-static struct snd_pcm_ops snd_gf1_pcm_capture_ops = {
+static const struct snd_pcm_ops snd_gf1_pcm_capture_ops = {
.open = snd_gf1_pcm_capture_open,
.close = snd_gf1_pcm_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_gf1_pcm_capture_hw_params,
- .hw_free = snd_gf1_pcm_capture_hw_free,
.prepare = snd_gf1_pcm_capture_prepare,
.trigger = snd_gf1_pcm_capture_trigger,
.pointer = snd_gf1_pcm_capture_pointer,
};
-int snd_gf1_pcm_new(struct snd_gus_card * gus, int pcm_dev, int control_index, struct snd_pcm ** rpcm)
+int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index)
{
struct snd_card *card;
struct snd_kcontrol *kctl;
@@ -857,8 +809,6 @@ int snd_gf1_pcm_new(struct snd_gus_card * gus, int pcm_dev, int control_index, s
struct snd_pcm_substream *substream;
int capture, err;
- if (rpcm)
- *rpcm = NULL;
card = gus->card;
capture = !gus->interwave && !gus->ess_flag && !gus->ace_flag ? 1 : 0;
err = snd_pcm_new(card,
@@ -874,9 +824,9 @@ int snd_gf1_pcm_new(struct snd_gus_card * gus, int pcm_dev, int control_index, s
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_gf1_pcm_playback_ops);
for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
- snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
- 64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024);
+ snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
+ card->dev,
+ 64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024);
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
@@ -884,11 +834,11 @@ int snd_gf1_pcm_new(struct snd_gus_card * gus, int pcm_dev, int control_index, s
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_gf1_pcm_capture_ops);
if (gus->gf1.dma2 == gus->gf1.dma1)
pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
- snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
- SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(),
- 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024);
+ snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+ SNDRV_DMA_TYPE_DEV, card->dev,
+ 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024);
}
- strcpy(pcm->name, pcm->id);
+ strscpy(pcm->name, pcm->id);
if (gus->interwave) {
sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A');
}
@@ -899,12 +849,11 @@ int snd_gf1_pcm_new(struct snd_gus_card * gus, int pcm_dev, int control_index, s
kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control1, gus);
else
kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control, gus);
- if ((err = snd_ctl_add(card, kctl)) < 0)
- return err;
kctl->id.index = control_index;
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
- if (rpcm)
- *rpcm = pcm;
return 0;
}
diff --git a/sound/isa/gus/gus_reset.c b/sound/isa/gus/gus_reset.c
index 3d1fed0c2620..a7a3e764bb77 100644
--- a/sound/isa/gus/gus_reset.c
+++ b/sound/isa/gus/gus_reset.c
@@ -1,21 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
@@ -24,11 +9,6 @@
#include <sound/core.h>
#include <sound/gus.h>
-extern void snd_gf1_timers_init(struct snd_gus_card * gus);
-extern void snd_gf1_timers_done(struct snd_gus_card * gus);
-extern int snd_gf1_synth_init(struct snd_gus_card * gus);
-extern void snd_gf1_synth_done(struct snd_gus_card * gus);
-
/*
* ok.. default interrupt handlers...
*/
@@ -100,26 +80,20 @@ void snd_gf1_set_default_handlers(struct snd_gus_card * gus, unsigned int what)
static void snd_gf1_clear_regs(struct snd_gus_card * gus)
{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
inb(GUSP(gus, IRQSTAT));
snd_gf1_write8(gus, 0x41, 0); /* DRAM DMA Control Register */
snd_gf1_write8(gus, 0x45, 0); /* Timer Control */
snd_gf1_write8(gus, 0x49, 0); /* Sampling Control Register */
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
static void snd_gf1_look_regs(struct snd_gus_card * gus)
{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_look8(gus, 0x41); /* DRAM DMA Control Register */
snd_gf1_look8(gus, 0x49); /* Sampling Control Register */
inb(GUSP(gus, IRQSTAT));
snd_gf1_read8(gus, 0x0f); /* IRQ Source Register */
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
/*
@@ -128,42 +102,35 @@ static void snd_gf1_look_regs(struct snd_gus_card * gus)
void snd_gf1_smart_stop_voice(struct snd_gus_card * gus, unsigned short voice)
{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_select_voice(gus, voice);
#if 0
- printk(KERN_DEBUG " -%i- smart stop voice - volume = 0x%x\n", voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME));
+ dev_dbg(gus->card->dev,
+ " -%i- smart stop voice - volume = 0x%x\n",
+ voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME));
#endif
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
void snd_gf1_stop_voice(struct snd_gus_card * gus, unsigned short voice)
{
- unsigned long flags;
-
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_select_voice(gus, voice);
#if 0
- printk(KERN_DEBUG " -%i- stop voice - volume = 0x%x\n", voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME));
+ dev_dbg(gus->card->dev,
+ " -%i- stop voice - volume = 0x%x\n",
+ voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME));
#endif
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
if (gus->gf1.enh_mode)
snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, 0);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
-#if 0
- snd_gf1_lfo_shutdown(gus, voice, ULTRA_LFO_VIBRATO);
- snd_gf1_lfo_shutdown(gus, voice, ULTRA_LFO_TREMOLO);
-#endif
}
static void snd_gf1_clear_voices(struct snd_gus_card * gus, unsigned short v_min,
unsigned short v_max)
{
- unsigned long flags;
unsigned int daddr;
unsigned short i, w_16;
@@ -173,7 +140,7 @@ static void snd_gf1_clear_voices(struct snd_gus_card * gus, unsigned short v_min
if (gus->gf1.syn_voices)
gus->gf1.syn_voices[i].flags = ~VFLG_DYNAMIC;
#endif
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_select_voice(gus, i);
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); /* Voice Control Register = voice stop */
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); /* Volume Ramp Control Register = ramp off */
@@ -194,23 +161,17 @@ static void snd_gf1_clear_voices(struct snd_gus_card * gus, unsigned short v_min
snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, 0);
snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, 0);
}
- spin_unlock_irqrestore(&gus->reg_lock, flags);
-#if 0
- snd_gf1_lfo_shutdown(gus, i, ULTRA_LFO_VIBRATO);
- snd_gf1_lfo_shutdown(gus, i, ULTRA_LFO_TREMOLO);
-#endif
}
}
void snd_gf1_stop_voices(struct snd_gus_card * gus, unsigned short v_min, unsigned short v_max)
{
- unsigned long flags;
short i, ramp_ok;
unsigned short ramp_end;
if (!in_interrupt()) { /* this can't be done in interrupt */
for (i = v_min, ramp_ok = 0; i <= v_max; i++) {
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_select_voice(gus, i);
ramp_end = snd_gf1_read16(gus, 9) >> 8;
if (ramp_end > SNDRV_GF1_MIN_OFFSET) {
@@ -224,7 +185,6 @@ void snd_gf1_stop_voices(struct snd_gus_card * gus, unsigned short v_min, unsign
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, 0x40);
}
}
- spin_unlock_irqrestore(&gus->reg_lock, flags);
}
msleep_interruptible(50);
}
@@ -257,21 +217,17 @@ static void snd_gf1_alloc_voice_use(struct snd_gus_card * gus,
struct snd_gus_voice *snd_gf1_alloc_voice(struct snd_gus_card * gus, int type, int client, int port)
{
struct snd_gus_voice *pvoice;
- unsigned long flags;
int idx;
- spin_lock_irqsave(&gus->voice_alloc, flags);
+ guard(spinlock_irqsave)(&gus->voice_alloc);
if (type == SNDRV_GF1_VOICE_TYPE_PCM) {
- if (gus->gf1.pcm_alloc_voices >= gus->gf1.pcm_channels) {
- spin_unlock_irqrestore(&gus->voice_alloc, flags);
+ if (gus->gf1.pcm_alloc_voices >= gus->gf1.pcm_channels)
return NULL;
- }
}
for (idx = 0; idx < 32; idx++) {
pvoice = &gus->gf1.voices[idx];
if (!pvoice->use) {
snd_gf1_alloc_voice_use(gus, pvoice, type, client, port);
- spin_unlock_irqrestore(&gus->voice_alloc, flags);
return pvoice;
}
}
@@ -280,34 +236,29 @@ struct snd_gus_voice *snd_gf1_alloc_voice(struct snd_gus_card * gus, int type, i
if (pvoice->midi && !pvoice->client) {
snd_gf1_clear_voices(gus, pvoice->number, pvoice->number);
snd_gf1_alloc_voice_use(gus, pvoice, type, client, port);
- spin_unlock_irqrestore(&gus->voice_alloc, flags);
return pvoice;
}
}
- spin_unlock_irqrestore(&gus->voice_alloc, flags);
return NULL;
}
void snd_gf1_free_voice(struct snd_gus_card * gus, struct snd_gus_voice *voice)
{
- unsigned long flags;
void (*private_free)(struct snd_gus_voice *voice);
- void *private_data;
if (voice == NULL || !voice->use)
return;
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | voice->number);
snd_gf1_clear_voices(gus, voice->number, voice->number);
- spin_lock_irqsave(&gus->voice_alloc, flags);
- private_free = voice->private_free;
- private_data = voice->private_data;
- voice->private_free = NULL;
- voice->private_data = NULL;
- if (voice->pcm)
- gus->gf1.pcm_alloc_voices--;
- voice->use = voice->pcm = 0;
- voice->sample_ops = NULL;
- spin_unlock_irqrestore(&gus->voice_alloc, flags);
+ scoped_guard(spinlock_irqsave, &gus->voice_alloc) {
+ private_free = voice->private_free;
+ voice->private_free = NULL;
+ voice->private_data = NULL;
+ if (voice->pcm)
+ gus->gf1.pcm_alloc_voices--;
+ voice->use = voice->pcm = 0;
+ voice->sample_ops = NULL;
+ }
if (private_free)
private_free(voice);
}
@@ -318,7 +269,6 @@ void snd_gf1_free_voice(struct snd_gus_card * gus, struct snd_gus_voice *voice)
int snd_gf1_start(struct snd_gus_card * gus)
{
- unsigned long flags;
unsigned int i;
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */
@@ -350,9 +300,7 @@ int snd_gf1_start(struct snd_gus_card * gus)
} else {
gus->gf1.sw_lfo = 1;
}
-#if 0
- snd_gf1_lfo_init(gus);
-#endif
+
if (gus->gf1.memory > 0)
for (i = 0; i < 4; i++)
snd_gf1_poke(gus, gus->gf1.default_voice_address + i, 0);
@@ -369,10 +317,10 @@ int snd_gf1_start(struct snd_gus_card * gus)
}
while ((snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ) & 0xc0) != 0xc0);
- spin_lock_irqsave(&gus->reg_lock, flags);
- outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE));
- outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
- spin_unlock_irqrestore(&gus->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &gus->reg_lock) {
+ outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE));
+ outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
+ }
snd_gf1_timers_init(gus);
snd_gf1_look_regs(gus);
@@ -406,8 +354,6 @@ int snd_gf1_stop(struct snd_gus_card * gus)
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* disable IRQ & DAC */
snd_gf1_timers_done(gus);
snd_gf1_mem_done(gus);
-#if 0
- snd_gf1_lfo_done(gus);
-#endif
+
return 0;
}
diff --git a/sound/isa/gus/gus_tables.h b/sound/isa/gus/gus_tables.h
index 42a4ca0d622b..ea0f007fa200 100644
--- a/sound/isa/gus/gus_tables.h
+++ b/sound/isa/gus/gus_tables.h
@@ -1,21 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define SNDRV_GF1_SCALE_TABLE_SIZE 128
diff --git a/sound/isa/gus/gus_timer.c b/sound/isa/gus/gus_timer.c
index c53727147a1a..e3a8847e02cf 100644
--- a/sound/isa/gus/gus_timer.c
+++ b/sound/isa/gus/gus_timer.c
@@ -1,24 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for Gravis UltraSound soundcards - Timers
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
* GUS have similar timers as AdLib (OPL2/OPL3 chips).
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/time.h>
@@ -31,33 +16,29 @@
static int snd_gf1_timer1_start(struct snd_timer * timer)
{
- unsigned long flags;
unsigned char tmp;
unsigned int ticks;
struct snd_gus_card *gus;
gus = snd_timer_chip(timer);
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
ticks = timer->sticks;
tmp = (gus->gf1.timer_enabled |= 4);
snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1, 256 - ticks); /* timer 1 count */
snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 1 IRQ */
snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */
- spin_unlock_irqrestore(&gus->reg_lock, flags);
return 0;
}
static int snd_gf1_timer1_stop(struct snd_timer * timer)
{
- unsigned long flags;
unsigned char tmp;
struct snd_gus_card *gus;
gus = snd_timer_chip(timer);
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
tmp = (gus->gf1.timer_enabled &= ~4);
snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* disable timer #1 */
- spin_unlock_irqrestore(&gus->reg_lock, flags);
return 0;
}
@@ -67,33 +48,29 @@ static int snd_gf1_timer1_stop(struct snd_timer * timer)
static int snd_gf1_timer2_start(struct snd_timer * timer)
{
- unsigned long flags;
unsigned char tmp;
unsigned int ticks;
struct snd_gus_card *gus;
gus = snd_timer_chip(timer);
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
ticks = timer->sticks;
tmp = (gus->gf1.timer_enabled |= 8);
snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2, 256 - ticks); /* timer 2 count */
snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 2 IRQ */
snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */
- spin_unlock_irqrestore(&gus->reg_lock, flags);
return 0;
}
static int snd_gf1_timer2_stop(struct snd_timer * timer)
{
- unsigned long flags;
unsigned char tmp;
struct snd_gus_card *gus;
gus = snd_timer_chip(timer);
- spin_lock_irqsave(&gus->reg_lock, flags);
+ guard(spinlock_irqsave)(&gus->reg_lock);
tmp = (gus->gf1.timer_enabled &= ~8);
snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* disable timer #1 */
- spin_unlock_irqrestore(&gus->reg_lock, flags);
return 0;
}
@@ -123,7 +100,7 @@ static void snd_gf1_interrupt_timer2(struct snd_gus_card * gus)
*/
-static struct snd_timer_hardware snd_gf1_timer1 =
+static const struct snd_timer_hardware snd_gf1_timer1 =
{
.flags = SNDRV_TIMER_HW_STOP,
.resolution = 80000,
@@ -132,7 +109,7 @@ static struct snd_timer_hardware snd_gf1_timer1 =
.stop = snd_gf1_timer1_stop,
};
-static struct snd_timer_hardware snd_gf1_timer2 =
+static const struct snd_timer_hardware snd_gf1_timer2 =
{
.flags = SNDRV_TIMER_HW_STOP,
.resolution = 320000,
@@ -171,7 +148,7 @@ void snd_gf1_timers_init(struct snd_gus_card * gus)
tid.subdevice = 0;
if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) {
- strcpy(timer->name, "GF1 timer #1");
+ strscpy(timer->name, "GF1 timer #1");
timer->private_data = gus;
timer->private_free = snd_gf1_timer1_free;
timer->hw = snd_gf1_timer1;
@@ -181,7 +158,7 @@ void snd_gf1_timers_init(struct snd_gus_card * gus)
tid.device++;
if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) {
- strcpy(timer->name, "GF1 timer #2");
+ strscpy(timer->name, "GF1 timer #2");
timer->private_data = gus;
timer->private_free = snd_gf1_timer2_free;
timer->hw = snd_gf1_timer2;
diff --git a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c
index 21cc42e4c4be..770d8f3e4cff 100644
--- a/sound/isa/gus/gus_uart.c
+++ b/sound/isa/gus/gus_uart.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for the GF1 MIDI interface - like UART 6850
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
@@ -28,7 +13,8 @@
static void snd_gf1_interrupt_midi_in(struct snd_gus_card * gus)
{
int count;
- unsigned char stat, data, byte;
+ unsigned char stat, byte;
+ __always_unused unsigned char data;
unsigned long flags;
count = 10;
@@ -63,13 +49,12 @@ static void snd_gf1_interrupt_midi_in(struct snd_gus_card * gus)
static void snd_gf1_interrupt_midi_out(struct snd_gus_card * gus)
{
char byte;
- unsigned long flags;
/* try unlock output */
if (snd_gf1_uart_stat(gus) & 0x01)
snd_gf1_interrupt_midi_in(gus);
- spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+ guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (snd_gf1_uart_stat(gus) & 0x02) { /* Tx FIFO free? */
if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) { /* no other bytes or error */
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */
@@ -77,7 +62,6 @@ static void snd_gf1_interrupt_midi_out(struct snd_gus_card * gus)
snd_gf1_uart_put(gus, byte);
}
}
- spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
}
static void snd_gf1_uart_reset(struct snd_gus_card * gus, int close)
@@ -91,31 +75,30 @@ static void snd_gf1_uart_reset(struct snd_gus_card * gus, int close)
static int snd_gf1_uart_output_open(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_gus_card *gus;
gus = substream->rmidi->private_data;
- spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+ guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (!(gus->gf1.uart_cmd & 0x80)) { /* input active? */
snd_gf1_uart_reset(gus, 0);
}
gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out;
gus->midi_substream_output = substream;
- spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
#if 0
- snd_printk(KERN_DEBUG "write init - cmd = 0x%x, stat = 0x%x\n", gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
+ dev_dbg(gus->card->dev,
+ "write init - cmd = 0x%x, stat = 0x%x\n",
+ gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
#endif
return 0;
}
static int snd_gf1_uart_input_open(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_gus_card *gus;
int i;
gus = substream->rmidi->private_data;
- spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+ guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) {
snd_gf1_uart_reset(gus, 0);
}
@@ -125,60 +108,53 @@ static int snd_gf1_uart_input_open(struct snd_rawmidi_substream *substream)
for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++)
snd_gf1_uart_get(gus); /* clean Rx */
if (i >= 1000)
- snd_printk(KERN_ERR "gus midi uart init read - cleanup error\n");
+ dev_err(gus->card->dev, "gus midi uart init read - cleanup error\n");
}
- spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
#if 0
- snd_printk(KERN_DEBUG
- "read init - enable = %i, cmd = 0x%x, stat = 0x%x\n",
- gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
- snd_printk(KERN_DEBUG
- "[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x "
- "(page = 0x%x)\n",
- gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100),
- inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102));
+ dev_dbg(gus->card->dev,
+ "read init - enable = %i, cmd = 0x%x, stat = 0x%x\n",
+ gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
+ dev_dbg(gus->card->dev,
+ "[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n",
+ gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100),
+ inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102));
#endif
return 0;
}
static int snd_gf1_uart_output_close(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_gus_card *gus;
gus = substream->rmidi->private_data;
- spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+ guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in)
snd_gf1_uart_reset(gus, 1);
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT);
gus->midi_substream_output = NULL;
- spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
return 0;
}
static int snd_gf1_uart_input_close(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_gus_card *gus;
gus = substream->rmidi->private_data;
- spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+ guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out)
snd_gf1_uart_reset(gus, 1);
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN);
gus->midi_substream_input = NULL;
- spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
return 0;
}
static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
struct snd_gus_card *gus;
- unsigned long flags;
gus = substream->rmidi->private_data;
- spin_lock_irqsave(&gus->uart_cmd_lock, flags);
+ guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (up) {
if ((gus->gf1.uart_cmd & 0x80) == 0)
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */
@@ -186,7 +162,6 @@ static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream,
if (gus->gf1.uart_cmd & 0x80)
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */
}
- spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
}
static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
@@ -227,36 +202,33 @@ static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream,
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
}
-static struct snd_rawmidi_ops snd_gf1_uart_output =
+static const struct snd_rawmidi_ops snd_gf1_uart_output =
{
.open = snd_gf1_uart_output_open,
.close = snd_gf1_uart_output_close,
.trigger = snd_gf1_uart_output_trigger,
};
-static struct snd_rawmidi_ops snd_gf1_uart_input =
+static const struct snd_rawmidi_ops snd_gf1_uart_input =
{
.open = snd_gf1_uart_input_open,
.close = snd_gf1_uart_input_close,
.trigger = snd_gf1_uart_input_trigger,
};
-int snd_gf1_rawmidi_new(struct snd_gus_card * gus, int device, struct snd_rawmidi ** rrawmidi)
+int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device)
{
struct snd_rawmidi *rmidi;
int err;
- if (rrawmidi)
- *rrawmidi = NULL;
- if ((err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi)) < 0)
+ err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi);
+ if (err < 0)
return err;
- strcpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1");
+ strscpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1");
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
rmidi->private_data = gus;
gus->midi_uart = rmidi;
- if (rrawmidi)
- *rrawmidi = rmidi;
return err;
}
diff --git a/sound/isa/gus/gus_volume.c b/sound/isa/gus/gus_volume.c
index c3c028a4a46b..e729621756cf 100644
--- a/sound/isa/gus/gus_volume.c
+++ b/sound/isa/gus/gus_volume.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/time.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/gus.h>
#define __GUS_TABLES_ALLOC__
@@ -76,7 +62,7 @@ unsigned int snd_gf1_calc_ramp_rate(struct snd_gus_card * gus,
unsigned short end,
unsigned int us)
{
- static unsigned char vol_rates[19] =
+ static const unsigned char vol_rates[19] =
{
23, 24, 26, 28, 29, 31, 32, 34,
36, 37, 39, 40, 42, 44, 45, 47,
@@ -118,7 +104,8 @@ unsigned short snd_gf1_translate_freq(struct snd_gus_card * gus, unsigned int fr
freq16 = 50;
if (freq16 & 0xf8000000) {
freq16 = ~0xf8000000;
- snd_printk(KERN_ERR "snd_gf1_translate_freq: overflow - freq = 0x%x\n", freq16);
+ dev_err(gus->card->dev, "%s: overflow - freq = 0x%x\n",
+ __func__, freq16);
}
return ((freq16 << 9) + (gus->gf1.playback_freq >> 1)) / gus->gf1.playback_freq;
}
@@ -127,7 +114,7 @@ unsigned short snd_gf1_translate_freq(struct snd_gus_card * gus, unsigned int fr
short snd_gf1_compute_vibrato(short cents, unsigned short fc_register)
{
- static short vibrato_table[] =
+ static const short vibrato_table[] =
{
0, 0, 32, 592, 61, 1175, 93, 1808,
124, 2433, 152, 3007, 182, 3632, 213, 4290,
@@ -135,7 +122,8 @@ short snd_gf1_compute_vibrato(short cents, unsigned short fc_register)
};
long depth;
- short *vi1, *vi2, pcents, v1;
+ const short *vi1, *vi2;
+ short pcents, v1;
pcents = cents < 0 ? -cents : cents;
for (vi1 = vibrato_table, vi2 = vi1 + 2; pcents > *vi2; vi1 = vi2, vi2 += 2);
@@ -159,7 +147,7 @@ short snd_gf1_compute_vibrato(short cents, unsigned short fc_register)
unsigned short snd_gf1_compute_pitchbend(unsigned short pitchbend, unsigned short sens)
{
- static long log_table[] = {1024, 1085, 1149, 1218, 1290, 1367, 1448, 1534, 1625, 1722, 1825, 1933};
+ static const long log_table[] = {1024, 1085, 1149, 1218, 1290, 1367, 1448, 1534, 1625, 1722, 1825, 1933};
int wheel, sensitivity;
unsigned int mantissa, f1, f2;
unsigned short semitones, f1_index, f2_index, f1_power, f2_power;
@@ -202,14 +190,14 @@ unsigned short snd_gf1_compute_freq(unsigned int freq,
fc = (freq << 10) / rate;
if (fc > 97391L) {
fc = 97391;
- snd_printk(KERN_ERR "patch: (1) fc frequency overflow - %u\n", fc);
+ pr_err("patch: (1) fc frequency overflow - %u\n", fc);
}
fc = (fc * 44100UL) / mix_rate;
while (scale--)
fc <<= 1;
if (fc > 65535L) {
fc = 65535;
- snd_printk(KERN_ERR "patch: (2) fc frequency overflow - %u\n", fc);
+ pr_err("patch: (2) fc frequency overflow - %u\n", fc);
}
return (unsigned short) fc;
}
diff --git a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c
index 086b8f0e0f94..101202acefb3 100644
--- a/sound/isa/gus/gusclassic.c
+++ b/sound/isa/gus/gusclassic.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Gravis UltraSound Classic soundcard
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -24,7 +9,7 @@
#include <linux/isa.h>
#include <linux/delay.h>
#include <linux/time.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/gus.h>
@@ -38,11 +23,10 @@
MODULE_DESCRIPTION(CRD_NAME);
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Classic}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x230,0x240,0x250,0x260 */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 3,5,9,11,12,15 */
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */
@@ -58,13 +42,13 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for " CRD_NAME " driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for " CRD_NAME " driver.");
module_param_array(joystick_dac, int, NULL, 0444);
MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for " CRD_NAME " driver.");
@@ -73,17 +57,18 @@ MODULE_PARM_DESC(channels, "GF1 channels for " CRD_NAME " driver.");
module_param_array(pcm_channels, int, NULL, 0444);
MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for " CRD_NAME " driver.");
-static int __devinit snd_gusclassic_match(struct device *dev, unsigned int n)
+static int snd_gusclassic_match(struct device *dev, unsigned int n)
{
return enable[n];
}
-static int __devinit snd_gusclassic_create(struct snd_card *card,
- struct device *dev, unsigned int n, struct snd_gus_card **rgus)
+static int snd_gusclassic_create(struct snd_card *card,
+ struct device *dev, unsigned int n,
+ struct snd_gus_card **rgus)
{
- static long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260};
- static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, 4, -1};
- static int possible_dmas[] = {5, 6, 7, 1, 3, -1};
+ static const long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260};
+ static const int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, 4, -1};
+ static const int possible_dmas[] = {5, 6, 7, 1, 3, -1};
int i, error;
@@ -123,32 +108,34 @@ static int __devinit snd_gusclassic_create(struct snd_card *card,
return error;
}
-static int __devinit snd_gusclassic_detect(struct snd_gus_card *gus)
+static int snd_gusclassic_detect(struct snd_gus_card *gus)
{
unsigned char d;
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */
- if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) {
- snd_printdd("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 0) {
+ dev_dbg(gus->card->dev, "[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
udelay(160);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */
udelay(160);
- if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) {
- snd_printdd("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 1) {
+ dev_dbg(gus->card->dev, "[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
return 0;
}
-static int __devinit snd_gusclassic_probe(struct device *dev, unsigned int n)
+static int snd_gusclassic_probe(struct device *dev, unsigned int n)
{
struct snd_card *card;
struct snd_gus_card *gus;
int error;
- error = snd_card_create(index[n], id[n], THIS_MODULE, 0, &card);
+ error = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE, 0, &card);
if (error < 0)
return error;
@@ -157,37 +144,37 @@ static int __devinit snd_gusclassic_probe(struct device *dev, unsigned int n)
error = snd_gusclassic_create(card, dev, n, &gus);
if (error < 0)
- goto out;
+ return error;
error = snd_gusclassic_detect(gus);
if (error < 0)
- goto out;
+ return error;
gus->joystick_dac = joystick_dac[n];
error = snd_gus_initialize(gus);
if (error < 0)
- goto out;
+ return error;
error = -ENODEV;
if (gus->max_flag || gus->ess_flag) {
dev_err(dev, "GUS Classic or ACE soundcard was "
"not detected at 0x%lx\n", gus->gf1.port);
- goto out;
+ return error;
}
error = snd_gf1_new_mixer(gus);
if (error < 0)
- goto out;
+ return error;
- error = snd_gf1_pcm_new(gus, 0, 0, NULL);
+ error = snd_gf1_pcm_new(gus, 0, 0);
if (error < 0)
- goto out;
+ return error;
if (!gus->ace_flag) {
- error = snd_gf1_rawmidi_new(gus, 0, NULL);
+ error = snd_gf1_rawmidi_new(gus, 0);
if (error < 0)
- goto out;
+ return error;
}
sprintf(card->longname + strlen(card->longname),
@@ -198,48 +185,23 @@ static int __devinit snd_gusclassic_probe(struct device *dev, unsigned int n)
sprintf(card->longname + strlen(card->longname),
"&%d", gus->gf1.dma2);
- snd_card_set_dev(card, dev);
-
error = snd_card_register(card);
if (error < 0)
- goto out;
+ return error;
dev_set_drvdata(dev, card);
return 0;
-
-out: snd_card_free(card);
- return error;
-}
-
-static int __devexit snd_gusclassic_remove(struct device *dev, unsigned int n)
-{
- snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
- return 0;
}
static struct isa_driver snd_gusclassic_driver = {
.match = snd_gusclassic_match,
.probe = snd_gusclassic_probe,
- .remove = __devexit_p(snd_gusclassic_remove),
#if 0 /* FIXME */
.suspend = snd_gusclassic_suspend,
- .remove = snd_gusclassic_remove,
#endif
.driver = {
.name = DEV_NAME
}
};
-static int __init alsa_card_gusclassic_init(void)
-{
- return isa_register_driver(&snd_gusclassic_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_gusclassic_exit(void)
-{
- isa_unregister_driver(&snd_gusclassic_driver);
-}
-
-module_init(alsa_card_gusclassic_init);
-module_exit(alsa_card_gusclassic_exit);
+module_isa_driver(snd_gusclassic_driver, SNDRV_CARDS);
diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c
index 008e8e5bfa37..ed921b89b00a 100644
--- a/sound/isa/gus/gusextreme.c
+++ b/sound/isa/gus/gusextreme.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Gravis UltraSound Extreme soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -24,7 +9,7 @@
#include <linux/isa.h>
#include <linux/delay.h>
#include <linux/time.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/gus.h>
@@ -42,11 +27,10 @@
MODULE_DESCRIPTION(CRD_NAME);
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Extreme}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */
static long gf1_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x210,0x220,0x230,0x240,0x250,0x260,0x270 */
static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x300,0x310,0x320 */
@@ -66,21 +50,21 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-module_param_array(gf1_port, long, NULL, 0444);
+module_param_hw_array(gf1_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(gf1_port, "GF1 port # for " CRD_NAME " driver (optional).");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
-module_param_array(gf1_irq, int, NULL, 0444);
+module_param_hw_array(gf1_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(gf1_irq, "GF1 IRQ # for " CRD_NAME " driver.");
-module_param_array(dma8, int, NULL, 0444);
+module_param_hw_array(dma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma8, "8-bit DMA # for " CRD_NAME " driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "GF1 DMA # for " CRD_NAME " driver.");
module_param_array(joystick_dac, int, NULL, 0444);
MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for " CRD_NAME " driver.");
@@ -89,17 +73,18 @@ MODULE_PARM_DESC(channels, "GF1 channels for " CRD_NAME " driver.");
module_param_array(pcm_channels, int, NULL, 0444);
MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for " CRD_NAME " driver.");
-static int __devinit snd_gusextreme_match(struct device *dev, unsigned int n)
+static int snd_gusextreme_match(struct device *dev, unsigned int n)
{
return enable[n];
}
-static int __devinit snd_gusextreme_es1688_create(struct snd_card *card,
- struct snd_es1688 *chip, struct device *dev, unsigned int n)
+static int snd_gusextreme_es1688_create(struct snd_card *card,
+ struct snd_es1688 *chip,
+ struct device *dev, unsigned int n)
{
- static long possible_ports[] = {0x220, 0x240, 0x260};
- static int possible_irqs[] = {5, 9, 10, 7, -1};
- static int possible_dmas[] = {1, 3, 0, -1};
+ static const long possible_ports[] = {0x220, 0x240, 0x260};
+ static const int possible_irqs[] = {5, 9, 10, 7, -1};
+ static const int possible_dmas[] = {1, 3, 0, -1};
int i, error;
@@ -132,11 +117,12 @@ static int __devinit snd_gusextreme_es1688_create(struct snd_card *card,
return error;
}
-static int __devinit snd_gusextreme_gus_card_create(struct snd_card *card,
- struct device *dev, unsigned int n, struct snd_gus_card **rgus)
+static int snd_gusextreme_gus_card_create(struct snd_card *card,
+ struct device *dev, unsigned int n,
+ struct snd_gus_card **rgus)
{
- static int possible_irqs[] = {11, 12, 15, 9, 5, 7, 3, -1};
- static int possible_dmas[] = {5, 6, 7, 3, 1, -1};
+ static const int possible_irqs[] = {11, 12, 15, 9, 5, 7, 3, -1};
+ static const int possible_dmas[] = {5, 6, 7, 3, 1, -1};
if (gf1_irq[n] == SNDRV_AUTO_IRQ) {
gf1_irq[n] = snd_legacy_find_free_irq(possible_irqs);
@@ -156,10 +142,9 @@ static int __devinit snd_gusextreme_gus_card_create(struct snd_card *card,
0, channels[n], pcm_channels[n], 0, rgus);
}
-static int __devinit snd_gusextreme_detect(struct snd_gus_card *gus,
- struct snd_es1688 *es1688)
+static int snd_gusextreme_detect(struct snd_gus_card *gus,
+ struct snd_es1688 *es1688)
{
- unsigned long flags;
unsigned char d;
/*
@@ -176,37 +161,39 @@ static int __devinit snd_gusextreme_detect(struct snd_gus_card *gus,
* 0x260 = 2,2,1
*/
- spin_lock_irqsave(&es1688->mixer_lock, flags);
- snd_es1688_mixer_write(es1688, 0x40, 0x0b); /* don't change!!! */
- spin_unlock_irqrestore(&es1688->mixer_lock, flags);
+ scoped_guard(spinlock_irqsave, &es1688->mixer_lock) {
+ snd_es1688_mixer_write(es1688, 0x40, 0x0b); /* don't change!!! */
+ }
- spin_lock_irqsave(&es1688->reg_lock, flags);
- outb(gus->gf1.port & 0x040 ? 2 : 0, ES1688P(es1688, INIT1));
- outb(0, 0x201);
- outb(gus->gf1.port & 0x020 ? 2 : 0, ES1688P(es1688, INIT1));
- outb(0, 0x201);
- outb(gus->gf1.port & 0x010 ? 3 : 1, ES1688P(es1688, INIT1));
- spin_unlock_irqrestore(&es1688->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &es1688->reg_lock) {
+ outb(gus->gf1.port & 0x040 ? 2 : 0, ES1688P(es1688, INIT1));
+ outb(0, 0x201);
+ outb(gus->gf1.port & 0x020 ? 2 : 0, ES1688P(es1688, INIT1));
+ outb(0, 0x201);
+ outb(gus->gf1.port & 0x010 ? 3 : 1, ES1688P(es1688, INIT1));
+ }
udelay(100);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */
- if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) {
- snd_printdd("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 0) {
+ dev_dbg(gus->card->dev, "[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
return -EIO;
}
udelay(160);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */
udelay(160);
- if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) {
- snd_printdd("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 1) {
+ dev_dbg(gus->card->dev, "[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
return -EIO;
}
return 0;
}
-static int __devinit snd_gusextreme_mixer(struct snd_card *card)
+static int snd_gusextreme_mixer(struct snd_card *card)
{
struct snd_ctl_elem_id id1, id2;
int error;
@@ -216,15 +203,15 @@ static int __devinit snd_gusextreme_mixer(struct snd_card *card)
id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
/* reassign AUX to SYNTHESIZER */
- strcpy(id1.name, "Aux Playback Volume");
- strcpy(id2.name, "Synth Playback Volume");
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "Synth Playback Volume");
error = snd_ctl_rename_id(card, &id1, &id2);
if (error < 0)
return error;
/* reassign Master Playback Switch to Synth Playback Switch */
- strcpy(id1.name, "Master Playback Switch");
- strcpy(id2.name, "Synth Playback Switch");
+ strscpy(id1.name, "Master Playback Switch");
+ strscpy(id2.name, "Synth Playback Switch");
error = snd_ctl_rename_id(card, &id1, &id2);
if (error < 0)
return error;
@@ -232,7 +219,7 @@ static int __devinit snd_gusextreme_mixer(struct snd_card *card)
return 0;
}
-static int __devinit snd_gusextreme_probe(struct device *dev, unsigned int n)
+static int snd_gusextreme_probe(struct device *dev, unsigned int n)
{
struct snd_card *card;
struct snd_gus_card *gus;
@@ -240,8 +227,8 @@ static int __devinit snd_gusextreme_probe(struct device *dev, unsigned int n)
struct snd_opl3 *opl3;
int error;
- error = snd_card_create(index[n], id[n], THIS_MODULE,
- sizeof(struct snd_es1688), &card);
+ error = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE,
+ sizeof(struct snd_es1688), &card);
if (error < 0)
return error;
@@ -255,56 +242,56 @@ static int __devinit snd_gusextreme_probe(struct device *dev, unsigned int n)
error = snd_gusextreme_es1688_create(card, es1688, dev, n);
if (error < 0)
- goto out;
+ return error;
if (gf1_port[n] < 0)
gf1_port[n] = es1688->port + 0x20;
error = snd_gusextreme_gus_card_create(card, dev, n, &gus);
if (error < 0)
- goto out;
+ return error;
error = snd_gusextreme_detect(gus, es1688);
if (error < 0)
- goto out;
+ return error;
gus->joystick_dac = joystick_dac[n];
error = snd_gus_initialize(gus);
if (error < 0)
- goto out;
+ return error;
error = -ENODEV;
if (!gus->ess_flag) {
dev_err(dev, "GUS Extreme soundcard was not "
"detected at 0x%lx\n", gus->gf1.port);
- goto out;
+ return error;
}
gus->codec_flag = 1;
- error = snd_es1688_pcm(card, es1688, 0, NULL);
+ error = snd_es1688_pcm(card, es1688, 0);
if (error < 0)
- goto out;
+ return error;
error = snd_es1688_mixer(card, es1688);
if (error < 0)
- goto out;
+ return error;
snd_component_add(card, "ES1688");
if (pcm_channels[n] > 0) {
- error = snd_gf1_pcm_new(gus, 1, 1, NULL);
+ error = snd_gf1_pcm_new(gus, 1, 1);
if (error < 0)
- goto out;
+ return error;
}
error = snd_gf1_new_mixer(gus);
if (error < 0)
- goto out;
+ return error;
error = snd_gusextreme_mixer(card);
if (error < 0)
- goto out;
+ return error;
if (snd_opl3_create(card, es1688->port, es1688->port + 2,
OPL3_HW_OPL3, 0, &opl3) < 0)
@@ -312,45 +299,31 @@ static int __devinit snd_gusextreme_probe(struct device *dev, unsigned int n)
else {
error = snd_opl3_hwdep_new(opl3, 0, 2, NULL);
if (error < 0)
- goto out;
+ return error;
}
if (es1688->mpu_port >= 0x300) {
error = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688,
- es1688->mpu_port, 0,
- mpu_irq[n], IRQF_DISABLED, NULL);
+ es1688->mpu_port, 0, mpu_irq[n], NULL);
if (error < 0)
- goto out;
+ return error;
}
sprintf(card->longname, "Gravis UltraSound Extreme at 0x%lx, "
"irq %i&%i, dma %i&%i", es1688->port,
gus->gf1.irq, es1688->irq, gus->gf1.dma1, es1688->dma8);
- snd_card_set_dev(card, dev);
-
error = snd_card_register(card);
if (error < 0)
- goto out;
+ return error;
dev_set_drvdata(dev, card);
return 0;
-
-out: snd_card_free(card);
- return error;
-}
-
-static int __devexit snd_gusextreme_remove(struct device *dev, unsigned int n)
-{
- snd_card_free(dev_get_drvdata(dev));
- dev_set_drvdata(dev, NULL);
- return 0;
}
static struct isa_driver snd_gusextreme_driver = {
.match = snd_gusextreme_match,
.probe = snd_gusextreme_probe,
- .remove = __devexit_p(snd_gusextreme_remove),
#if 0 /* FIXME */
.suspend = snd_gusextreme_suspend,
.resume = snd_gusextreme_resume,
@@ -360,15 +333,4 @@ static struct isa_driver snd_gusextreme_driver = {
}
};
-static int __init alsa_card_gusextreme_init(void)
-{
- return isa_register_driver(&snd_gusextreme_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_gusextreme_exit(void)
-{
- isa_unregister_driver(&snd_gusextreme_driver);
-}
-
-module_init(alsa_card_gusextreme_init);
-module_exit(alsa_card_gusextreme_exit);
+module_isa_driver(snd_gusextreme_driver, SNDRV_CARDS);
diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c
index 3e4a58b72913..b572411c4422 100644
--- a/sound/isa/gus/gusmax.c
+++ b/sound/isa/gus/gusmax.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Gravis UltraSound MAX soundcard
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -24,7 +9,7 @@
#include <linux/isa.h>
#include <linux/delay.h>
#include <linux/time.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/gus.h>
@@ -36,11 +21,10 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Gravis UltraSound MAX");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound MAX}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x230,0x240,0x250,0x260 */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */
@@ -56,13 +40,13 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for GUS MAX soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable GUS MAX soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for GUS MAX driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for GUS MAX driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for GUS MAX driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for GUS MAX driver.");
module_param_array(joystick_dac, int, NULL, 0444);
MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS MAX driver.");
@@ -80,22 +64,22 @@ struct snd_gusmax {
unsigned short pcm_status_reg;
};
-#define PFX "gusmax: "
-
-static int __devinit snd_gusmax_detect(struct snd_gus_card * gus)
+static int snd_gusmax_detect(struct snd_gus_card *gus)
{
unsigned char d;
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */
- if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) {
- snd_printdd("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 0) {
+ dev_dbg(gus->card->dev, "[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
udelay(160);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */
udelay(160);
- if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) {
- snd_printdd("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 1) {
+ dev_dbg(gus->card->dev, "[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
@@ -124,8 +108,8 @@ static irqreturn_t snd_gusmax_interrupt(int irq, void *dev_id)
return IRQ_RETVAL(handled);
}
-static void __devinit snd_gusmax_init(int dev, struct snd_card *card,
- struct snd_gus_card * gus)
+static void snd_gusmax_init(int dev, struct snd_card *card,
+ struct snd_gus_card *gus)
{
gus->equal_irq = 1;
gus->codec_flag = 1;
@@ -140,7 +124,7 @@ static void __devinit snd_gusmax_init(int dev, struct snd_card *card,
outb(gus->max_cntrl_val, GUSP(gus, MAXCNTRLPORT));
}
-static int __devinit snd_gusmax_mixer(struct snd_wss *chip)
+static int snd_gusmax_mixer(struct snd_wss *chip)
{
struct snd_card *card = chip->card;
struct snd_ctl_elem_id id1, id2;
@@ -150,22 +134,26 @@ static int __devinit snd_gusmax_mixer(struct snd_wss *chip)
memset(&id2, 0, sizeof(id2));
id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
/* reassign AUXA to SYNTHESIZER */
- strcpy(id1.name, "Aux Playback Switch");
- strcpy(id2.name, "Synth Playback Switch");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ strscpy(id1.name, "Aux Playback Switch");
+ strscpy(id2.name, "Synth Playback Switch");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
- strcpy(id1.name, "Aux Playback Volume");
- strcpy(id2.name, "Synth Playback Volume");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "Synth Playback Volume");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
/* reassign AUXB to CD */
- strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
- strcpy(id2.name, "CD Playback Switch");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ strscpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+ strscpy(id2.name, "CD Playback Switch");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
- strcpy(id1.name, "Aux Playback Volume");
- strcpy(id2.name, "CD Playback Volume");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "CD Playback Volume");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
#if 0
/* reassign Mono Input to MIC */
@@ -189,62 +177,51 @@ static int __devinit snd_gusmax_mixer(struct snd_wss *chip)
return 0;
}
-static void snd_gusmax_free(struct snd_card *card)
-{
- struct snd_gusmax *maxcard = card->private_data;
-
- if (maxcard == NULL)
- return;
- if (maxcard->irq >= 0)
- free_irq(maxcard->irq, (void *)maxcard);
-}
-
-static int __devinit snd_gusmax_match(struct device *pdev, unsigned int dev)
+static int snd_gusmax_match(struct device *pdev, unsigned int dev)
{
return enable[dev];
}
-static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev)
+static int snd_gusmax_probe(struct device *pdev, unsigned int dev)
{
- static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1};
- static int possible_dmas[] = {5, 6, 7, 1, 3, -1};
+ static const int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1};
+ static const int possible_dmas[] = {5, 6, 7, 1, 3, -1};
int xirq, xdma1, xdma2, err;
struct snd_card *card;
struct snd_gus_card *gus = NULL;
struct snd_wss *wss;
struct snd_gusmax *maxcard;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_gusmax), &card);
+ err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_gusmax), &card);
if (err < 0)
return err;
- card->private_free = snd_gusmax_free;
maxcard = card->private_data;
maxcard->card = card;
maxcard->irq = -1;
xirq = irq[dev];
if (xirq == SNDRV_AUTO_IRQ) {
- if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
- err = -EBUSY;
- goto _err;
+ xirq = snd_legacy_find_free_irq(possible_irqs);
+ if (xirq < 0) {
+ dev_err(pdev, "unable to find a free IRQ\n");
+ return -EBUSY;
}
}
xdma1 = dma1[dev];
if (xdma1 == SNDRV_AUTO_DMA) {
- if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free DMA1\n");
- err = -EBUSY;
- goto _err;
+ xdma1 = snd_legacy_find_free_dma(possible_dmas);
+ if (xdma1 < 0) {
+ dev_err(pdev, "unable to find a free DMA1\n");
+ return -EBUSY;
}
}
xdma2 = dma2[dev];
if (xdma2 == SNDRV_AUTO_DMA) {
- if ((xdma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free DMA2\n");
- err = -EBUSY;
- goto _err;
+ xdma2 = snd_legacy_find_free_dma(possible_dmas);
+ if (xdma2 < 0) {
+ dev_err(pdev, "unable to find a free DMA2\n");
+ return -EBUSY;
}
}
@@ -256,7 +233,7 @@ static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev)
pcm_channels[dev],
0, &gus);
} else {
- static unsigned long possible_ports[] = {
+ static const unsigned long possible_ports[] = {
0x220, 0x230, 0x240, 0x250, 0x260
};
int i;
@@ -274,30 +251,32 @@ static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev)
}
}
if (err < 0)
- goto _err;
+ return err;
- if ((err = snd_gusmax_detect(gus)) < 0)
- goto _err;
+ err = snd_gusmax_detect(gus);
+ if (err < 0)
+ return err;
maxcard->gus_status_reg = gus->gf1.reg_irqstat;
maxcard->pcm_status_reg = gus->gf1.port + 0x10c + 2;
snd_gusmax_init(dev, card, gus);
- if ((err = snd_gus_initialize(gus)) < 0)
- goto _err;
+ err = snd_gus_initialize(gus);
+ if (err < 0)
+ return err;
if (!gus->max_flag) {
- snd_printk(KERN_ERR PFX "GUS MAX soundcard was not detected at 0x%lx\n", gus->gf1.port);
- err = -ENODEV;
- goto _err;
+ dev_err(pdev, "GUS MAX soundcard was not detected at 0x%lx\n", gus->gf1.port);
+ return -ENODEV;
}
- if (request_irq(xirq, snd_gusmax_interrupt, IRQF_DISABLED, "GUS MAX", (void *)maxcard)) {
- snd_printk(KERN_ERR PFX "unable to grab IRQ %d\n", xirq);
- err = -EBUSY;
- goto _err;
+ if (devm_request_irq(card->dev, xirq, snd_gusmax_interrupt, 0,
+ "GUS MAX", (void *)maxcard)) {
+ dev_err(pdev, "unable to grab IRQ %d\n", xirq);
+ return -EBUSY;
}
maxcard->irq = xirq;
-
+ card->sync_irq = maxcard->irq;
+
err = snd_wss_create(card,
gus->gf1.port + 0x10c, -1, xirq,
xdma2 < 0 ? xdma1 : xdma2, xdma1,
@@ -307,58 +286,46 @@ static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev)
WSS_HWSHARE_DMA2,
&wss);
if (err < 0)
- goto _err;
+ return err;
- err = snd_wss_pcm(wss, 0, NULL);
+ err = snd_wss_pcm(wss, 0);
if (err < 0)
- goto _err;
+ return err;
err = snd_wss_mixer(wss);
if (err < 0)
- goto _err;
+ return err;
- err = snd_wss_timer(wss, 2, NULL);
+ err = snd_wss_timer(wss, 2);
if (err < 0)
- goto _err;
+ return err;
if (pcm_channels[dev] > 0) {
- if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0)
- goto _err;
+ err = snd_gf1_pcm_new(gus, 1, 1);
+ if (err < 0)
+ return err;
}
err = snd_gusmax_mixer(wss);
if (err < 0)
- goto _err;
+ return err;
- err = snd_gf1_rawmidi_new(gus, 0, NULL);
+ err = snd_gf1_rawmidi_new(gus, 0);
if (err < 0)
- goto _err;
+ return err;
sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %i, dma %i", gus->gf1.port, xirq, xdma1);
if (xdma2 >= 0)
sprintf(card->longname + strlen(card->longname), "&%i", xdma2);
- snd_card_set_dev(card, pdev);
-
err = snd_card_register(card);
if (err < 0)
- goto _err;
+ return err;
maxcard->gus = gus;
maxcard->wss = wss;
dev_set_drvdata(pdev, card);
return 0;
-
- _err:
- snd_card_free(card);
- return err;
-}
-
-static int __devexit snd_gusmax_remove(struct device *devptr, unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
- return 0;
}
#define DEV_NAME "gusmax"
@@ -366,22 +333,10 @@ static int __devexit snd_gusmax_remove(struct device *devptr, unsigned int dev)
static struct isa_driver snd_gusmax_driver = {
.match = snd_gusmax_match,
.probe = snd_gusmax_probe,
- .remove = __devexit_p(snd_gusmax_remove),
/* FIXME: suspend/resume */
.driver = {
.name = DEV_NAME
},
};
-static int __init alsa_card_gusmax_init(void)
-{
- return isa_register_driver(&snd_gusmax_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_gusmax_exit(void)
-{
- isa_unregister_driver(&snd_gusmax_driver);
-}
-
-module_init(alsa_card_gusmax_init)
-module_exit(alsa_card_gusmax_exit)
+module_isa_driver(snd_gusmax_driver, SNDRV_CARDS);
diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c
index c7b80e4730fc..18adcd35e117 100644
--- a/sound/isa/gus/interwave.c
+++ b/sound/isa/gus/interwave.c
@@ -1,25 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for AMD InterWave soundcard
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
* 1999/07/22 Erik Inge Bolso <knan@mo.himolde.no>
* * mixer group handlers
- *
*/
#include <linux/init.h>
@@ -27,7 +12,7 @@
#include <linux/isa.h>
#include <linux/delay.h>
#include <linux/pnp.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/gus.h>
@@ -43,21 +28,15 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
#ifndef SNDRV_STB
MODULE_DESCRIPTION("AMD InterWave");
-MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Plug & Play},"
- "{STB,SoundRage32},"
- "{MED,MED3210},"
- "{Dynasonix,Dynasonix Pro},"
- "{Panasonic,PCA761AW}}");
#else
MODULE_DESCRIPTION("AMD InterWave STB with TEA6330T");
-MODULE_SUPPORTED_DEVICE("{{AMD,InterWave STB with TEA6330T}}");
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
#endif
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x210,0x220,0x230,0x240,0x250,0x260 */
#ifdef SNDRV_STB
@@ -73,11 +52,9 @@ static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
static int effect[SNDRV_CARDS];
#ifdef SNDRV_STB
-#define PFX "interwave-stb: "
#define INTERWAVE_DRIVER "snd_interwave_stb"
#define INTERWAVE_PNP_DRIVER "interwave-stb"
#else
-#define PFX "interwave: "
#define INTERWAVE_DRIVER "snd_interwave"
#define INTERWAVE_PNP_DRIVER "interwave"
#endif
@@ -92,17 +69,17 @@ MODULE_PARM_DESC(enable, "Enable InterWave soundcard.");
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard.");
#endif
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for InterWave driver.");
#ifdef SNDRV_STB
-module_param_array(port_tc, long, NULL, 0444);
+module_param_hw_array(port_tc, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port_tc, "Tone control (TEA6330T - i2c bus) port # for InterWave driver.");
#endif
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for InterWave driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for InterWave driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for InterWave driver.");
module_param_array(joystick_dac, int, NULL, 0444);
MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for InterWave driver.");
@@ -136,7 +113,7 @@ struct snd_interwave {
static int isa_registered;
static int pnp_registered;
-static struct pnp_card_device_id snd_interwave_pnpids[] = {
+static const struct pnp_card_device_id snd_interwave_pnpids[] = {
#ifndef SNDRV_STB
/* Gravis UltraSound Plug & Play */
{ .id = "GRV0001", .devs = { { .id = "GRV0000" } } },
@@ -169,7 +146,7 @@ static void snd_interwave_i2c_setlines(struct snd_i2c_bus *bus, int ctrl, int da
unsigned long port = bus->private_value;
#if 0
- printk(KERN_DEBUG "i2c_setlines - 0x%lx <- %i,%i\n", port, ctrl, data);
+ dev_dbg(bus->card->dev, "i2c_setlines - 0x%lx <- %i,%i\n", port, ctrl, data);
#endif
outb((data << 1) | ctrl, port);
udelay(10);
@@ -182,7 +159,7 @@ static int snd_interwave_i2c_getclockline(struct snd_i2c_bus *bus)
res = inb(port) & 1;
#if 0
- printk(KERN_DEBUG "i2c_getclockline - 0x%lx -> %i\n", port, res);
+ dev_dbg(bus->card->dev, "i2c_getclockline - 0x%lx -> %i\n", port, res);
#endif
return res;
}
@@ -196,7 +173,7 @@ static int snd_interwave_i2c_getdataline(struct snd_i2c_bus *bus, int ack)
udelay(10);
res = (inb(port) & 2) >> 1;
#if 0
- printk(KERN_DEBUG "i2c_getdataline - 0x%lx -> %i\n", port, res);
+ dev_dbg(bus->card->dev, "i2c_getdataline - 0x%lx -> %i\n", port, res);
#endif
return res;
}
@@ -207,9 +184,9 @@ static struct snd_i2c_bit_ops snd_interwave_i2c_bit_ops = {
.getdata = snd_interwave_i2c_getdataline,
};
-static int __devinit snd_interwave_detect_stb(struct snd_interwave *iwcard,
- struct snd_gus_card * gus, int dev,
- struct snd_i2c_bus **rbus)
+static int snd_interwave_detect_stb(struct snd_interwave *iwcard,
+ struct snd_gus_card *gus, int dev,
+ struct snd_i2c_bus **rbus)
{
unsigned long port;
struct snd_i2c_bus *bus;
@@ -225,66 +202,75 @@ static int __devinit snd_interwave_detect_stb(struct snd_interwave *iwcard,
port = 0x360;
}
while (port <= 0x380) {
- if ((iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)")) != NULL)
+ iwcard->i2c_res = devm_request_region(card->dev, port, 1,
+ "InterWave (I2C bus)");
+ if (iwcard->i2c_res)
break;
port += 0x10;
}
} else {
- iwcard->i2c_res = request_region(port, 1, "InterWave (I2C bus)");
+ iwcard->i2c_res = devm_request_region(card->dev, port, 1,
+ "InterWave (I2C bus)");
}
if (iwcard->i2c_res == NULL) {
- snd_printk(KERN_ERR "interwave: can't grab i2c bus port\n");
+ dev_err(card->dev, "interwave: can't grab i2c bus port\n");
return -ENODEV;
}
sprintf(name, "InterWave-%i", card->number);
- if ((err = snd_i2c_bus_create(card, name, NULL, &bus)) < 0)
+ err = snd_i2c_bus_create(card, name, NULL, &bus);
+ if (err < 0)
return err;
bus->private_value = port;
bus->hw_ops.bit = &snd_interwave_i2c_bit_ops;
- if ((err = snd_tea6330t_detect(bus, 0)) < 0)
+ err = snd_tea6330t_detect(bus, 0);
+ if (err < 0)
return err;
*rbus = bus;
return 0;
}
#endif
-static int __devinit snd_interwave_detect(struct snd_interwave *iwcard,
- struct snd_gus_card * gus,
- int dev
+static int snd_interwave_detect(struct snd_interwave *iwcard,
+ struct snd_gus_card *gus,
+ int dev
#ifdef SNDRV_STB
- , struct snd_i2c_bus **rbus
+ , struct snd_i2c_bus **rbus
#endif
)
{
- unsigned long flags;
unsigned char rev1, rev2;
int d;
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */
- if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) {
- snd_printdd("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 0) {
+ dev_dbg(gus->card->dev, "[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
udelay(160);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */
udelay(160);
- if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) {
- snd_printdd("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
+ d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
+ if ((d & 0x07) != 1) {
+ dev_dbg(gus->card->dev, "[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
- spin_lock_irqsave(&gus->reg_lock, flags);
- rev1 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER);
- snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, ~rev1);
- rev2 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER);
- snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, rev1);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
- snd_printdd("[0x%lx] InterWave check - rev1=0x%x, rev2=0x%x\n", gus->gf1.port, rev1, rev2);
+ scoped_guard(spinlock_irqsave, &gus->reg_lock) {
+ rev1 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, ~rev1);
+ rev2 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, rev1);
+ }
+ dev_dbg(gus->card->dev,
+ "[0x%lx] InterWave check - rev1=0x%x, rev2=0x%x\n",
+ gus->gf1.port, rev1, rev2);
if ((rev1 & 0xf0) == (rev2 & 0xf0) &&
(rev1 & 0x0f) != (rev2 & 0x0f)) {
- snd_printdd("[0x%lx] InterWave check - passed\n", gus->gf1.port);
+ dev_dbg(gus->card->dev,
+ "[0x%lx] InterWave check - passed\n", gus->gf1.port);
gus->interwave = 1;
- strcpy(gus->card->shortname, "AMD InterWave");
+ strscpy(gus->card->shortname, "AMD InterWave");
gus->revision = rev1 >> 4;
#ifndef SNDRV_STB
return 0; /* ok.. We have an InterWave board */
@@ -292,7 +278,7 @@ static int __devinit snd_interwave_detect(struct snd_interwave *iwcard,
return snd_interwave_detect_stb(iwcard, gus, dev, rbus);
#endif
}
- snd_printdd("[0x%lx] InterWave check - failed\n", gus->gf1.port);
+ dev_dbg(gus->card->dev, "[0x%lx] InterWave check - failed\n", gus->gf1.port);
return -ENODEV;
}
@@ -318,7 +304,7 @@ static irqreturn_t snd_interwave_interrupt(int irq, void *dev_id)
return IRQ_RETVAL(handled);
}
-static void __devinit snd_interwave_reset(struct snd_gus_card * gus)
+static void snd_interwave_reset(struct snd_gus_card *gus)
{
snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x00);
udelay(160);
@@ -326,7 +312,7 @@ static void __devinit snd_interwave_reset(struct snd_gus_card * gus)
udelay(160);
}
-static void __devinit snd_interwave_bank_sizes(struct snd_gus_card * gus, int *sizes)
+static void snd_interwave_bank_sizes(struct snd_gus_card *gus, int *sizes)
{
unsigned int idx;
unsigned int local;
@@ -341,7 +327,7 @@ static void __devinit snd_interwave_bank_sizes(struct snd_gus_card * gus, int *s
snd_gf1_poke(gus, local, d);
snd_gf1_poke(gus, local + 1, d + 1);
#if 0
- printk(KERN_DEBUG "d = 0x%x, local = 0x%x, "
+ dev_dbg(gus->card->dev, "d = 0x%x, local = 0x%x, "
"local + 1 = 0x%x, idx << 22 = 0x%x\n",
d,
snd_gf1_peek(gus, local),
@@ -356,7 +342,7 @@ static void __devinit snd_interwave_bank_sizes(struct snd_gus_card * gus, int *s
}
}
#if 0
- printk(KERN_DEBUG "sizes: %i %i %i %i\n",
+ dev_dbg(gus->card->dev, "sizes: %i %i %i %i\n",
sizes[0], sizes[1], sizes[2], sizes[3]);
#endif
}
@@ -377,9 +363,9 @@ struct rom_hdr {
/* 511 */ unsigned char csum;
};
-static void __devinit snd_interwave_detect_memory(struct snd_gus_card * gus)
+static void snd_interwave_detect_memory(struct snd_gus_card *gus)
{
- static unsigned int lmc[13] =
+ static const unsigned int lmc[13] =
{
0x00000001, 0x00000101, 0x01010101, 0x00000401,
0x04040401, 0x00040101, 0x04040101, 0x00000004,
@@ -411,12 +397,12 @@ static void __devinit snd_interwave_detect_memory(struct snd_gus_card * gus)
lmct = (psizes[3] << 24) | (psizes[2] << 16) |
(psizes[1] << 8) | psizes[0];
#if 0
- printk(KERN_DEBUG "lmct = 0x%08x\n", lmct);
+ dev_dbg(gus->card->dev, "lmct = 0x%08x\n", lmct);
#endif
for (i = 0; i < ARRAY_SIZE(lmc); i++)
if (lmct == lmc[i]) {
#if 0
- printk(KERN_DEBUG "found !!! %i\n", i);
+ dev_dbg(gus->card->dev, "found !!! %i\n", i);
#endif
snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | i);
snd_interwave_bank_sizes(gus, psizes);
@@ -442,19 +428,11 @@ static void __devinit snd_interwave_detect_memory(struct snd_gus_card * gus)
for (bank_pos = 0; bank_pos < 16L * 1024L * 1024L; bank_pos += 4L * 1024L * 1024L) {
for (i = 0; i < 8; ++i)
iwave[i] = snd_gf1_peek(gus, bank_pos + i);
-#ifdef CONFIG_SND_DEBUG_ROM
- printk(KERN_DEBUG "ROM at 0x%06x = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", bank_pos,
- iwave[0], iwave[1], iwave[2], iwave[3],
- iwave[4], iwave[5], iwave[6], iwave[7]);
-#endif
if (strncmp(iwave, "INTRWAVE", 8))
continue; /* first check */
csum = 0;
for (i = 0; i < sizeof(struct rom_hdr); i++)
csum += snd_gf1_peek(gus, bank_pos + i);
-#ifdef CONFIG_SND_DEBUG_ROM
- printk(KERN_DEBUG "ROM checksum = 0x%x (computed)\n", csum);
-#endif
if (csum != 0)
continue; /* not valid rom */
gus->gf1.rom_banks++;
@@ -476,20 +454,18 @@ static void __devinit snd_interwave_detect_memory(struct snd_gus_card * gus)
snd_interwave_reset(gus);
}
-static void __devinit snd_interwave_init(int dev, struct snd_gus_card * gus)
+static void snd_interwave_init(int dev, struct snd_gus_card *gus)
{
- unsigned long flags;
-
/* ok.. some InterWave specific initialization */
- spin_lock_irqsave(&gus->reg_lock, flags);
- snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0x00);
- snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f);
- snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49);
- snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11);
- snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00);
- snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30);
- snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00);
- spin_unlock_irqrestore(&gus->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &gus->reg_lock) {
+ snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0x00);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30);
+ snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00);
+ }
gus->equal_irq = 1;
gus->codec_flag = 1;
gus->interwave = 1;
@@ -498,7 +474,7 @@ static void __devinit snd_interwave_init(int dev, struct snd_gus_card * gus)
}
-static struct snd_kcontrol_new snd_interwave_controls[] = {
+static const struct snd_kcontrol_new snd_interwave_controls[] = {
WSS_DOUBLE("Master Playback Switch", 0,
CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 7, 7, 1, 1),
WSS_DOUBLE("Master Playback Volume", 0,
@@ -509,7 +485,7 @@ WSS_DOUBLE("Mic Playback Volume", 0,
CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 0, 0, 31, 1)
};
-static int __devinit snd_interwave_mixer(struct snd_wss *chip)
+static int snd_interwave_mixer(struct snd_wss *chip)
{
struct snd_card *card = chip->card;
struct snd_ctl_elem_id id1, id2;
@@ -521,47 +497,55 @@ static int __devinit snd_interwave_mixer(struct snd_wss *chip)
id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
#if 0
/* remove mono microphone controls */
- strcpy(id1.name, "Mic Playback Switch");
- if ((err = snd_ctl_remove_id(card, &id1)) < 0)
+ strscpy(id1.name, "Mic Playback Switch");
+ err = snd_ctl_remove_id(card, &id1);
+ if (err < 0)
return err;
- strcpy(id1.name, "Mic Playback Volume");
- if ((err = snd_ctl_remove_id(card, &id1)) < 0)
+ strscpy(id1.name, "Mic Playback Volume");
+ err = snd_ctl_remove_id(card, &id1);
+ if (err < 0)
return err;
#endif
/* add new master and mic controls */
- for (idx = 0; idx < ARRAY_SIZE(snd_interwave_controls); idx++)
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_interwave_controls[idx], chip))) < 0)
+ for (idx = 0; idx < ARRAY_SIZE(snd_interwave_controls); idx++) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_interwave_controls[idx], chip));
+ if (err < 0)
return err;
+ }
snd_wss_out(chip, CS4231_LINE_LEFT_OUTPUT, 0x9f);
snd_wss_out(chip, CS4231_LINE_RIGHT_OUTPUT, 0x9f);
snd_wss_out(chip, CS4231_LEFT_MIC_INPUT, 0x9f);
snd_wss_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f);
/* reassign AUXA to SYNTHESIZER */
- strcpy(id1.name, "Aux Playback Switch");
- strcpy(id2.name, "Synth Playback Switch");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ strscpy(id1.name, "Aux Playback Switch");
+ strscpy(id2.name, "Synth Playback Switch");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
- strcpy(id1.name, "Aux Playback Volume");
- strcpy(id2.name, "Synth Playback Volume");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "Synth Playback Volume");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
/* reassign AUXB to CD */
- strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
- strcpy(id2.name, "CD Playback Switch");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ strscpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+ strscpy(id2.name, "CD Playback Switch");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
- strcpy(id1.name, "Aux Playback Volume");
- strcpy(id2.name, "CD Playback Volume");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "CD Playback Volume");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
return 0;
}
#ifdef CONFIG_PNP
-static int __devinit snd_interwave_pnp(int dev, struct snd_interwave *iwcard,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_interwave_pnp(int dev, struct snd_interwave *iwcard,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
struct pnp_dev *pdev;
int err;
@@ -580,12 +564,12 @@ static int __devinit snd_interwave_pnp(int dev, struct snd_interwave *iwcard,
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "InterWave PnP configure failure (out of resources?)\n");
+ dev_err(&pdev->dev, "InterWave PnP configure failure (out of resources?)\n");
return err;
}
if (pnp_port_start(pdev, 0) + 0x100 != pnp_port_start(pdev, 1) ||
pnp_port_start(pdev, 0) + 0x10c != pnp_port_start(pdev, 2)) {
- snd_printk(KERN_ERR "PnP configure failure (wrong ports)\n");
+ dev_err(&pdev->dev, "PnP configure failure (wrong ports)\n");
return -ENOENT;
}
port[dev] = pnp_port_start(pdev, 0);
@@ -593,68 +577,65 @@ static int __devinit snd_interwave_pnp(int dev, struct snd_interwave *iwcard,
if (dma2[dev] >= 0)
dma2[dev] = pnp_dma(pdev, 1);
irq[dev] = pnp_irq(pdev, 0);
- snd_printdd("isapnp IW: sb port=0x%llx, gf1 port=0x%llx, codec port=0x%llx\n",
- (unsigned long long)pnp_port_start(pdev, 0),
- (unsigned long long)pnp_port_start(pdev, 1),
- (unsigned long long)pnp_port_start(pdev, 2));
- snd_printdd("isapnp IW: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]);
+ dev_dbg(&pdev->dev,
+ "isapnp IW: sb port=0x%llx, gf1 port=0x%llx, codec port=0x%llx\n",
+ (unsigned long long)pnp_port_start(pdev, 0),
+ (unsigned long long)pnp_port_start(pdev, 1),
+ (unsigned long long)pnp_port_start(pdev, 2));
+ dev_dbg(&pdev->dev,
+ "isapnp IW: dma1=%i, dma2=%i, irq=%i\n",
+ dma1[dev], dma2[dev], irq[dev]);
#ifdef SNDRV_STB
/* Tone Control initialization */
pdev = iwcard->devtc;
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "InterWave ToneControl PnP configure failure (out of resources?)\n");
+ dev_err(&pdev->dev,
+ "InterWave ToneControl PnP configure failure (out of resources?)\n");
return err;
}
port_tc[dev] = pnp_port_start(pdev, 0);
- snd_printdd("isapnp IW: tone control port=0x%lx\n", port_tc[dev]);
+ dev_dbg(&pdev->dev, "isapnp IW: tone control port=0x%lx\n", port_tc[dev]);
#endif
return 0;
}
#endif /* CONFIG_PNP */
-static void snd_interwave_free(struct snd_card *card)
-{
- struct snd_interwave *iwcard = card->private_data;
-
- if (iwcard == NULL)
- return;
-#ifdef SNDRV_STB
- release_and_free_resource(iwcard->i2c_res);
-#endif
- if (iwcard->irq >= 0)
- free_irq(iwcard->irq, (void *)iwcard);
-}
-
-static int snd_interwave_card_new(int dev, struct snd_card **cardp)
+static int snd_interwave_card_new(struct device *pdev, int dev,
+ struct snd_card **cardp)
{
struct snd_card *card;
struct snd_interwave *iwcard;
int err;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_interwave), &card);
+ err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_interwave), &card);
if (err < 0)
return err;
iwcard = card->private_data;
iwcard->card = card;
iwcard->irq = -1;
- card->private_free = snd_interwave_free;
*cardp = card;
return 0;
}
-static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
+static int snd_interwave_probe_gus(struct snd_card *card, int dev,
+ struct snd_gus_card **gusp)
+{
+ return snd_gus_create(card, port[dev], -irq[dev], dma1[dev], dma2[dev],
+ 0, 32, pcm_channels[dev], effect[dev], gusp);
+}
+
+static int snd_interwave_probe(struct snd_card *card, int dev,
+ struct snd_gus_card *gus)
{
int xirq, xdma1, xdma2;
struct snd_interwave *iwcard = card->private_data;
struct snd_wss *wss;
- struct snd_gus_card *gus;
#ifdef SNDRV_STB
struct snd_i2c_bus *i2c_bus;
#endif
- struct snd_pcm *pcm;
char *str;
int err;
@@ -662,18 +643,12 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
xdma1 = dma1[dev];
xdma2 = dma2[dev];
- if ((err = snd_gus_create(card,
- port[dev],
- -xirq, xdma1, xdma2,
- 0, 32,
- pcm_channels[dev], effect[dev], &gus)) < 0)
- return err;
-
- if ((err = snd_interwave_detect(iwcard, gus, dev
+ err = snd_interwave_detect(iwcard, gus, dev
#ifdef SNDRV_STB
- , &i2c_bus
+ , &i2c_bus
#endif
- )) < 0)
+ );
+ if (err < 0)
return err;
iwcard->gus_status_reg = gus->gf1.reg_irqstat;
@@ -681,15 +656,17 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
snd_interwave_init(dev, gus);
snd_interwave_detect_memory(gus);
- if ((err = snd_gus_initialize(gus)) < 0)
+ err = snd_gus_initialize(gus);
+ if (err < 0)
return err;
- if (request_irq(xirq, snd_interwave_interrupt, IRQF_DISABLED,
- "InterWave", iwcard)) {
- snd_printk(KERN_ERR PFX "unable to grab IRQ %d\n", xirq);
+ if (devm_request_irq(card->dev, xirq, snd_interwave_interrupt, 0,
+ "InterWave", iwcard)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", xirq);
return -EBUSY;
}
iwcard->irq = xirq;
+ card->sync_irq = iwcard->irq;
err = snd_wss_create(card,
gus->gf1.port + 0x10c, -1, xirq,
@@ -702,14 +679,15 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
if (err < 0)
return err;
- err = snd_wss_pcm(wss, 0, &pcm);
+ err = snd_wss_pcm(wss, 0);
if (err < 0)
return err;
- sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A');
- strcat(pcm->name, " (codec)");
+ sprintf(wss->pcm->name + strlen(wss->pcm->name), " rev %c",
+ gus->revision + 'A');
+ strcat(wss->pcm->name, " (codec)");
- err = snd_wss_timer(wss, 2, NULL);
+ err = snd_wss_timer(wss, 2);
if (err < 0)
return err;
@@ -718,7 +696,7 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
return err;
if (pcm_channels[dev] > 0) {
- err = snd_gf1_pcm_new(gus, 1, 1, NULL);
+ err = snd_gf1_pcm_new(gus, 1, 1);
if (err < 0)
return err;
}
@@ -732,22 +710,26 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
memset(&id1, 0, sizeof(id1));
memset(&id2, 0, sizeof(id2));
id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id1.name, "Master Playback Switch");
- strcpy(id2.name, id1.name);
+ strscpy(id1.name, "Master Playback Switch");
+ strscpy(id2.name, id1.name);
id2.index = 1;
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
- strcpy(id1.name, "Master Playback Volume");
- strcpy(id2.name, id1.name);
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
+ strscpy(id1.name, "Master Playback Volume");
+ strscpy(id2.name, id1.name);
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0)
return err;
- if ((err = snd_tea6330t_update_mixer(card, i2c_bus, 0, 1)) < 0)
+ err = snd_tea6330t_update_mixer(card, i2c_bus, 0, 1);
+ if (err < 0)
return err;
}
#endif
gus->uart_enable = midi[dev];
- if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0)
+ err = snd_gf1_rawmidi_new(gus, 0);
+ if (err < 0)
return err;
#ifndef SNDRV_STB
@@ -757,8 +739,8 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
#else
str = "InterWave STB";
#endif
- strcpy(card->driver, str);
- strcpy(card->shortname, str);
+ strscpy(card->driver, str);
+ strscpy(card->shortname, str);
sprintf(card->longname, "%s at 0x%lx, irq %i, dma %d",
str,
gus->gf1.port,
@@ -776,26 +758,8 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
return 0;
}
-static int __devinit snd_interwave_isa_probe1(int dev, struct device *devptr)
-{
- struct snd_card *card;
- int err;
-
- err = snd_interwave_card_new(dev, &card);
- if (err < 0)
- return err;
-
- snd_card_set_dev(card, devptr);
- if ((err = snd_interwave_probe(card, dev)) < 0) {
- snd_card_free(card);
- return err;
- }
- dev_set_drvdata(devptr, card);
- return 0;
-}
-
-static int __devinit snd_interwave_isa_match(struct device *pdev,
- unsigned int dev)
+static int snd_interwave_isa_match(struct device *pdev,
+ unsigned int dev)
{
if (!enable[dev])
return 0;
@@ -806,58 +770,67 @@ static int __devinit snd_interwave_isa_match(struct device *pdev,
return 1;
}
-static int __devinit snd_interwave_isa_probe(struct device *pdev,
- unsigned int dev)
+static int snd_interwave_isa_probe(struct device *pdev,
+ unsigned int dev)
{
+ struct snd_card *card;
+ struct snd_gus_card *gus;
int err;
- static int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1};
- static int possible_dmas[] = {0, 1, 3, 5, 6, 7, -1};
+ static const int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1};
+ static const int possible_dmas[] = {0, 1, 3, 5, 6, 7, -1};
if (irq[dev] == SNDRV_AUTO_IRQ) {
- if ((irq[dev] = snd_legacy_find_free_irq(possible_irqs)) < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
+ irq[dev] = snd_legacy_find_free_irq(possible_irqs);
+ if (irq[dev] < 0) {
+ dev_err(pdev, "unable to find a free IRQ\n");
return -EBUSY;
}
}
if (dma1[dev] == SNDRV_AUTO_DMA) {
- if ((dma1[dev] = snd_legacy_find_free_dma(possible_dmas)) < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free DMA1\n");
+ dma1[dev] = snd_legacy_find_free_dma(possible_dmas);
+ if (dma1[dev] < 0) {
+ dev_err(pdev, "unable to find a free DMA1\n");
return -EBUSY;
}
}
if (dma2[dev] == SNDRV_AUTO_DMA) {
- if ((dma2[dev] = snd_legacy_find_free_dma(possible_dmas)) < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free DMA2\n");
+ dma2[dev] = snd_legacy_find_free_dma(possible_dmas);
+ if (dma2[dev] < 0) {
+ dev_err(pdev, "unable to find a free DMA2\n");
return -EBUSY;
}
}
+ err = snd_interwave_card_new(pdev, dev, &card);
+ if (err < 0)
+ return err;
+
if (port[dev] != SNDRV_AUTO_PORT)
- return snd_interwave_isa_probe1(dev, pdev);
+ err = snd_interwave_probe_gus(card, dev, &gus);
else {
- static long possible_ports[] = {0x210, 0x220, 0x230, 0x240, 0x250, 0x260};
+ static const long possible_ports[] = {0x210, 0x220, 0x230, 0x240, 0x250, 0x260};
int i;
for (i = 0; i < ARRAY_SIZE(possible_ports); i++) {
port[dev] = possible_ports[i];
- err = snd_interwave_isa_probe1(dev, pdev);
+ err = snd_interwave_probe_gus(card, dev, &gus);
if (! err)
return 0;
}
- return err;
}
-}
+ if (err < 0)
+ return err;
-static int __devexit snd_interwave_isa_remove(struct device *devptr, unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
+ err = snd_interwave_probe(card, dev, gus);
+ if (err < 0)
+ return err;
+
+ dev_set_drvdata(pdev, card);
return 0;
}
static struct isa_driver snd_interwave_driver = {
.match = snd_interwave_isa_match,
.probe = snd_interwave_isa_probe,
- .remove = __devexit_p(snd_interwave_isa_remove),
/* FIXME: suspend,resume */
.driver = {
.name = INTERWAVE_DRIVER
@@ -865,11 +838,12 @@ static struct isa_driver snd_interwave_driver = {
};
#ifdef CONFIG_PNP
-static int __devinit snd_interwave_pnp_detect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_interwave_pnp_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
static int dev;
struct snd_card *card;
+ struct snd_gus_card *gus;
int res;
for ( ; dev < SNDRV_CARDS; dev++) {
@@ -879,36 +853,29 @@ static int __devinit snd_interwave_pnp_detect(struct pnp_card_link *pcard,
if (dev >= SNDRV_CARDS)
return -ENODEV;
- res = snd_interwave_card_new(dev, &card);
+ res = snd_interwave_card_new(&pcard->card->dev, dev, &card);
if (res < 0)
return res;
- if ((res = snd_interwave_pnp(dev, card->private_data, pcard, pid)) < 0) {
- snd_card_free(card);
+ res = snd_interwave_pnp(dev, card->private_data, pcard, pid);
+ if (res < 0)
return res;
- }
- snd_card_set_dev(card, &pcard->card->dev);
- if ((res = snd_interwave_probe(card, dev)) < 0) {
- snd_card_free(card);
+ res = snd_interwave_probe_gus(card, dev, &gus);
+ if (res < 0)
+ return res;
+ res = snd_interwave_probe(card, dev, gus);
+ if (res < 0)
return res;
- }
pnp_set_card_drvdata(pcard, card);
dev++;
return 0;
}
-static void __devexit snd_interwave_pnp_remove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
-}
-
static struct pnp_card_driver interwave_pnpc_driver = {
.flags = PNP_DRIVER_RES_DISABLE,
.name = INTERWAVE_PNP_DRIVER,
.id_table = snd_interwave_pnpids,
.probe = snd_interwave_pnp_detect,
- .remove = __devexit_p(snd_interwave_pnp_remove),
/* FIXME: suspend,resume */
};
diff --git a/sound/isa/msnd/Makefile b/sound/isa/msnd/Makefile
index 2171c0aa2f62..d56412aae857 100644
--- a/sound/isa/msnd/Makefile
+++ b/sound/isa/msnd/Makefile
@@ -1,7 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
-snd-msnd-lib-objs := msnd.o msnd_midi.o msnd_pinnacle_mixer.o
-snd-msnd-pinnacle-objs := msnd_pinnacle.o
-snd-msnd-classic-objs := msnd_classic.o
+snd-msnd-lib-y := msnd.o msnd_pinnacle_mixer.o
+snd-msnd-pinnacle-y := msnd_pinnacle.o
+snd-msnd-classic-y := msnd_classic.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_MSND_PINNACLE) += snd-msnd-pinnacle.o snd-msnd-lib.o
diff --git a/sound/isa/msnd/msnd.c b/sound/isa/msnd/msnd.c
index 3a1526ae1729..5e350234d572 100644
--- a/sound/isa/msnd/msnd.c
+++ b/sound/isa/msnd/msnd.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*********************************************************************
*
* 2002/06/30 Karsten Wiese:
@@ -19,28 +20,16 @@
*
* Copyright (C) 1998 Andrew Veliath
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
********************************************************************/
#include <linux/kernel.h>
+#include <linux/sched/signal.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
@@ -52,7 +41,7 @@
#define LOGNAME "msnd"
-void snd_msnd_init_queue(void *base, int start, int size)
+void snd_msnd_init_queue(void __iomem *base, int start, int size)
{
writew(PCTODSP_BASED(start), base + JQS_wStart);
writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize);
@@ -87,17 +76,13 @@ static int snd_msnd_wait_HC0(struct snd_msnd *dev)
int snd_msnd_send_dsp_cmd(struct snd_msnd *dev, u8 cmd)
{
- unsigned long flags;
-
- spin_lock_irqsave(&dev->lock, flags);
+ guard(spinlock_irqsave)(&dev->lock);
if (snd_msnd_wait_HC0(dev) == 0) {
outb(cmd, dev->io + HP_CVR);
- spin_unlock_irqrestore(&dev->lock, flags);
return 0;
}
- spin_unlock_irqrestore(&dev->lock, flags);
- snd_printd(KERN_ERR LOGNAME ": Send DSP command timeout\n");
+ dev_dbg(dev->card->dev, LOGNAME ": Send DSP command timeout\n");
return -EIO;
}
@@ -115,7 +100,7 @@ int snd_msnd_send_word(struct snd_msnd *dev, unsigned char high,
return 0;
}
- snd_printd(KERN_ERR LOGNAME ": Send host word timeout\n");
+ dev_dbg(dev->card->dev, LOGNAME ": Send host word timeout\n");
return -EIO;
}
@@ -126,7 +111,7 @@ int snd_msnd_upload_host(struct snd_msnd *dev, const u8 *bin, int len)
int i;
if (len % 3 != 0) {
- snd_printk(KERN_ERR LOGNAME
+ dev_err(dev->card->dev, LOGNAME
": Upload host data not multiple of 3!\n");
return -EINVAL;
}
@@ -144,14 +129,12 @@ EXPORT_SYMBOL(snd_msnd_upload_host);
int snd_msnd_enable_irq(struct snd_msnd *dev)
{
- unsigned long flags;
-
if (dev->irq_ref++)
return 0;
- snd_printdd(LOGNAME ": Enabling IRQ\n");
+ dev_dbg(dev->card->dev, LOGNAME ": Enabling IRQ\n");
- spin_lock_irqsave(&dev->lock, flags);
+ guard(spinlock_irqsave)(&dev->lock);
if (snd_msnd_wait_TXDE(dev) == 0) {
outb(inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR);
if (dev->type == msndClassic)
@@ -162,12 +145,10 @@ int snd_msnd_enable_irq(struct snd_msnd *dev)
enable_irq(dev->irq);
snd_msnd_init_queue(dev->DSPQ, dev->dspq_data_buff,
dev->dspq_buff_size);
- spin_unlock_irqrestore(&dev->lock, flags);
return 0;
}
- spin_unlock_irqrestore(&dev->lock, flags);
- snd_printd(KERN_ERR LOGNAME ": Enable IRQ failed\n");
+ dev_dbg(dev->card->dev, LOGNAME ": Enable IRQ failed\n");
return -EIO;
}
@@ -175,29 +156,25 @@ EXPORT_SYMBOL(snd_msnd_enable_irq);
int snd_msnd_disable_irq(struct snd_msnd *dev)
{
- unsigned long flags;
-
if (--dev->irq_ref > 0)
return 0;
if (dev->irq_ref < 0)
- snd_printd(KERN_WARNING LOGNAME ": IRQ ref count is %d\n",
- dev->irq_ref);
+ dev_dbg(dev->card->dev, LOGNAME ": IRQ ref count is %d\n",
+ dev->irq_ref);
- snd_printdd(LOGNAME ": Disabling IRQ\n");
+ dev_dbg(dev->card->dev, LOGNAME ": Disabling IRQ\n");
- spin_lock_irqsave(&dev->lock, flags);
+ guard(spinlock_irqsave)(&dev->lock);
if (snd_msnd_wait_TXDE(dev) == 0) {
outb(inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR);
if (dev->type == msndClassic)
outb(HPIRQ_NONE, dev->io + HP_IRQM);
disable_irq(dev->irq);
- spin_unlock_irqrestore(&dev->lock, flags);
return 0;
}
- spin_unlock_irqrestore(&dev->lock, flags);
- snd_printd(KERN_ERR LOGNAME ": Disable IRQ failed\n");
+ dev_dbg(dev->card->dev, LOGNAME ": Disable IRQ failed\n");
return -EIO;
}
@@ -231,8 +208,8 @@ void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file)
snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP);
snd_msnd_disable_irq(chip);
if (file) {
- snd_printd(KERN_INFO LOGNAME
- ": Stopping read for %p\n", file);
+ dev_dbg(chip->card->dev, LOGNAME
+ ": Stopping read for %p\n", file);
chip->mode &= ~FMODE_READ;
}
clear_bit(F_AUDIO_READ_INUSE, &chip->flags);
@@ -244,8 +221,8 @@ void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file)
}
snd_msnd_disable_irq(chip);
if (file) {
- snd_printd(KERN_INFO
- LOGNAME ": Stopping write for %p\n", file);
+ dev_dbg(chip->card->dev,
+ LOGNAME ": Stopping write for %p\n", file);
chip->mode &= ~FMODE_WRITE;
}
clear_bit(F_AUDIO_WRITE_INUSE, &chip->flags);
@@ -268,7 +245,7 @@ int snd_msnd_DARQ(struct snd_msnd *chip, int bank)
udelay(1);
if (chip->capturePeriods == 2) {
- void *pDAQ = chip->mappedbase + DARQ_DATA_BUFF +
+ void __iomem *pDAQ = chip->mappedbase + DARQ_DATA_BUFF +
bank * DAQDS__size + DAQDS_wStart;
unsigned short offset = 0x3000 + chip->capturePeriodBytes;
@@ -307,7 +284,7 @@ int snd_msnd_DAPQ(struct snd_msnd *chip, int start)
{
u16 DAPQ_tail;
int protect = start, nbanks = 0;
- void *DAQD;
+ void __iomem *DAQD;
static int play_banks_submitted;
/* unsigned long flags;
spin_lock_irqsave(&chip->lock, flags); not necessary */
@@ -340,12 +317,6 @@ int snd_msnd_DAPQ(struct snd_msnd *chip, int start)
++nbanks;
/* Then advance the tail */
- /*
- if (protect)
- snd_printd(KERN_INFO "B %X %lX\n",
- bank_num, xtime.tv_usec);
- */
-
DAPQ_tail = (++bank_num % 3) * PCTODSP_OFFSET(DAQDS__size);
writew(DAPQ_tail, chip->DAPQ + JQS_wTail);
/* Tell the DSP to play the bank */
@@ -354,10 +325,6 @@ int snd_msnd_DAPQ(struct snd_msnd *chip, int start)
if (2 == bank_num)
break;
}
- /*
- if (protect)
- snd_printd(KERN_INFO "%lX\n", xtime.tv_usec);
- */
/* spin_unlock_irqrestore(&chip->lock, flags); not necessary */
return nbanks;
}
@@ -368,7 +335,7 @@ static void snd_msnd_play_reset_queue(struct snd_msnd *chip,
unsigned int pcm_count)
{
int n;
- void *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF;
+ void __iomem *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF;
chip->last_playbank = -1;
chip->playLimit = pcm_count * (pcm_periods - 1);
@@ -396,8 +363,7 @@ static void snd_msnd_capture_reset_queue(struct snd_msnd *chip,
unsigned int pcm_count)
{
int n;
- void *pDAQ;
- /* unsigned long flags; */
+ void __iomem *pDAQ;
/* snd_msnd_init_queue(chip->DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE); */
@@ -409,15 +375,15 @@ static void snd_msnd_capture_reset_queue(struct snd_msnd *chip,
chip->DARQ + JQS_wTail);
#if 0 /* Critical section: bank 1 access. this is how the OSS driver does it:*/
- spin_lock_irqsave(&chip->lock, flags);
- outb(HPBLKSEL_1, chip->io + HP_BLKS);
- memset_io(chip->mappedbase, 0, DAR_BUFF_SIZE * 3);
- outb(HPBLKSEL_0, chip->io + HP_BLKS);
- spin_unlock_irqrestore(&chip->lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->lock) {
+ outb(HPBLKSEL_1, chip->io + HP_BLKS);
+ memset_io(chip->mappedbase, 0, DAR_BUFF_SIZE * 3);
+ outb(HPBLKSEL_0, chip->io + HP_BLKS);
+ }
#endif
chip->capturePeriodBytes = pcm_count;
- snd_printdd("snd_msnd_capture_reset_queue() %i\n", pcm_count);
+ dev_dbg(chip->card->dev, "%s() %i\n", __func__, pcm_count);
pDAQ = chip->mappedbase + DARQ_DATA_BUFF;
@@ -435,8 +401,8 @@ static void snd_msnd_capture_reset_queue(struct snd_msnd *chip,
}
}
-static struct snd_pcm_hardware snd_msnd_playback = {
- .info = SNDRV_PCM_INFO_MMAP |
+static const struct snd_pcm_hardware snd_msnd_playback = {
+ .info = SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BATCH,
@@ -454,8 +420,8 @@ static struct snd_pcm_hardware snd_msnd_playback = {
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_msnd_capture = {
- .info = SNDRV_PCM_INFO_MMAP |
+static const struct snd_pcm_hardware snd_msnd_capture = {
+ .info = SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BATCH,
@@ -483,7 +449,8 @@ static int snd_msnd_playback_open(struct snd_pcm_substream *substream)
clear_bit(F_WRITING, &chip->flags);
snd_msnd_enable_irq(chip);
- runtime->dma_area = chip->mappedbase;
+ runtime->dma_area = (__force void *)chip->mappedbase;
+ runtime->dma_addr = chip->base;
runtime->dma_bytes = 0x3000;
chip->playback_substream = substream;
@@ -506,7 +473,7 @@ static int snd_msnd_playback_hw_params(struct snd_pcm_substream *substream,
{
int i;
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
- void *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF;
+ void __iomem *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF;
chip->play_sample_size = snd_pcm_format_width(params_format(params));
chip->play_channels = params_channels(params);
@@ -543,21 +510,21 @@ static int snd_msnd_playback_trigger(struct snd_pcm_substream *substream,
int result = 0;
if (cmd == SNDRV_PCM_TRIGGER_START) {
- snd_printdd("snd_msnd_playback_trigger(START)\n");
+ dev_dbg(chip->card->dev, "%s(START)\n", __func__);
chip->banksPlayed = 0;
set_bit(F_WRITING, &chip->flags);
snd_msnd_DAPQ(chip, 1);
} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
- snd_printdd("snd_msnd_playback_trigger(STop)\n");
+ dev_dbg(chip->card->dev, "%s(STOP)\n", __func__);
/* interrupt diagnostic, comment this out later */
clear_bit(F_WRITING, &chip->flags);
snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP);
} else {
- snd_printd(KERN_ERR "snd_msnd_playback_trigger(?????)\n");
+ dev_dbg(chip->card->dev, "%s(?????)\n", __func__);
result = -EINVAL;
}
- snd_printdd("snd_msnd_playback_trigger() ENDE\n");
+ dev_dbg(chip->card->dev, "%s() ENDE\n", __func__);
return result;
}
@@ -570,14 +537,14 @@ snd_msnd_playback_pointer(struct snd_pcm_substream *substream)
}
-static struct snd_pcm_ops snd_msnd_playback_ops = {
+static const struct snd_pcm_ops snd_msnd_playback_ops = {
.open = snd_msnd_playback_open,
.close = snd_msnd_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_msnd_playback_hw_params,
.prepare = snd_msnd_playback_prepare,
.trigger = snd_msnd_playback_trigger,
.pointer = snd_msnd_playback_pointer,
+ .mmap = snd_pcm_lib_mmap_iomem,
};
static int snd_msnd_capture_open(struct snd_pcm_substream *substream)
@@ -587,7 +554,8 @@ static int snd_msnd_capture_open(struct snd_pcm_substream *substream)
set_bit(F_AUDIO_READ_INUSE, &chip->flags);
snd_msnd_enable_irq(chip);
- runtime->dma_area = chip->mappedbase + 0x3000;
+ runtime->dma_area = (__force void *)chip->mappedbase + 0x3000;
+ runtime->dma_addr = chip->base + 0x3000;
runtime->dma_bytes = 0x3000;
memset(runtime->dma_area, 0, runtime->dma_bytes);
chip->capture_substream = substream;
@@ -652,7 +620,7 @@ static int snd_msnd_capture_hw_params(struct snd_pcm_substream *substream,
{
int i;
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
- void *pDAQ = chip->mappedbase + DARQ_DATA_BUFF;
+ void __iomem *pDAQ = chip->mappedbase + DARQ_DATA_BUFF;
chip->capture_sample_size = snd_pcm_format_width(params_format(params));
chip->capture_channels = params_channels(params);
@@ -667,19 +635,18 @@ static int snd_msnd_capture_hw_params(struct snd_pcm_substream *substream,
}
-static struct snd_pcm_ops snd_msnd_capture_ops = {
+static const struct snd_pcm_ops snd_msnd_capture_ops = {
.open = snd_msnd_capture_open,
.close = snd_msnd_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_msnd_capture_hw_params,
.prepare = snd_msnd_capture_prepare,
.trigger = snd_msnd_capture_trigger,
.pointer = snd_msnd_capture_pointer,
+ .mmap = snd_pcm_lib_mmap_iomem,
};
-int snd_msnd_pcm(struct snd_card *card, int device,
- struct snd_pcm **rpcm)
+int snd_msnd_pcm(struct snd_card *card, int device)
{
struct snd_msnd *chip = card->private_data;
struct snd_pcm *pcm;
@@ -693,11 +660,8 @@ int snd_msnd_pcm(struct snd_card *card, int device,
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_msnd_capture_ops);
pcm->private_data = chip;
- strcpy(pcm->name, "Hurricane");
-
+ strscpy(pcm->name, "Hurricane");
- if (rpcm)
- *rpcm = pcm;
return 0;
}
EXPORT_SYMBOL(snd_msnd_pcm);
diff --git a/sound/isa/msnd/msnd.h b/sound/isa/msnd/msnd.h
index 3773e242b58e..3d7810ed9186 100644
--- a/sound/isa/msnd/msnd.h
+++ b/sound/isa/msnd/msnd.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*********************************************************************
*
* msnd.h
@@ -10,20 +11,6 @@
* Copyright (C) 1998 Andrew Veliath
* Copyright (C) 1993 Turtle Beach Systems, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
********************************************************************/
#ifndef __MSND_H
#define __MSND_H
@@ -229,7 +216,6 @@ struct snd_msnd {
int captureLimit;
int capturePeriods;
struct snd_card *card;
- void *msndmidi_mpu;
struct snd_rawmidi *rmidi;
/* Hardware resources */
@@ -249,7 +235,7 @@ struct snd_msnd {
/* State variables */
enum { msndClassic, msndPinnacle } type;
- mode_t mode;
+ fmode_t mode;
unsigned long flags;
#define F_RESETTING 0
#define F_HAVEDIGITAL 1
@@ -283,7 +269,7 @@ struct snd_msnd {
};
-void snd_msnd_init_queue(void *base, int start, int size);
+void snd_msnd_init_queue(void __iomem *base, int start, int size);
int snd_msnd_send_dsp_cmd(struct snd_msnd *chip, u8 cmd);
int snd_msnd_send_word(struct snd_msnd *chip,
@@ -297,12 +283,9 @@ int snd_msnd_disable_irq(struct snd_msnd *chip);
void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file);
int snd_msnd_DAPQ(struct snd_msnd *chip, int start);
int snd_msnd_DARQ(struct snd_msnd *chip, int start);
-int snd_msnd_pcm(struct snd_card *card, int device, struct snd_pcm **rpcm);
-
-int snd_msndmidi_new(struct snd_card *card, int device);
-void snd_msndmidi_input_read(void *mpu);
+int snd_msnd_pcm(struct snd_card *card, int device);
void snd_msndmix_setup(struct snd_msnd *chip);
-int __devinit snd_msndmix_new(struct snd_card *card);
+int snd_msndmix_new(struct snd_card *card);
int snd_msndmix_force_recsrc(struct snd_msnd *chip, int recsrc);
#endif /* __MSND_H */
diff --git a/sound/isa/msnd/msnd_classic.h b/sound/isa/msnd/msnd_classic.h
index f18d5fa5baf4..74d2b9af4683 100644
--- a/sound/isa/msnd/msnd_classic.h
+++ b/sound/isa/msnd/msnd_classic.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*********************************************************************
*
* msnd_classic.h
@@ -10,20 +11,6 @@
* Copyright (C) 1998 Andrew Veliath
* Copyright (C) 1993 Turtle Beach Systems, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
********************************************************************/
#ifndef __MSND_CLASSIC_H
#define __MSND_CLASSIC_H
diff --git a/sound/isa/msnd/msnd_midi.c b/sound/isa/msnd/msnd_midi.c
deleted file mode 100644
index 787495674235..000000000000
--- a/sound/isa/msnd/msnd_midi.c
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- * Copyright (c) 2009 by Krzysztof Helt
- * Routines for control of MPU-401 in UART mode
- *
- * MPU-401 supports UART mode which is not capable generate transmit
- * interrupts thus output is done via polling. Also, if irq < 0, then
- * input is done also via polling. Do not expect good performance.
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/errno.h>
-#include <sound/core.h>
-#include <sound/rawmidi.h>
-
-#include "msnd.h"
-
-#define MSNDMIDI_MODE_BIT_INPUT 0
-#define MSNDMIDI_MODE_BIT_OUTPUT 1
-#define MSNDMIDI_MODE_BIT_INPUT_TRIGGER 2
-#define MSNDMIDI_MODE_BIT_OUTPUT_TRIGGER 3
-
-struct snd_msndmidi {
- struct snd_msnd *dev;
-
- unsigned long mode; /* MSNDMIDI_MODE_XXXX */
-
- struct snd_rawmidi_substream *substream_input;
-
- spinlock_t input_lock;
-};
-
-/*
- * input/output open/close - protected by open_mutex in rawmidi.c
- */
-static int snd_msndmidi_input_open(struct snd_rawmidi_substream *substream)
-{
- struct snd_msndmidi *mpu;
-
- snd_printdd("snd_msndmidi_input_open()\n");
-
- mpu = substream->rmidi->private_data;
-
- mpu->substream_input = substream;
-
- snd_msnd_enable_irq(mpu->dev);
-
- snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_START);
- set_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode);
- return 0;
-}
-
-static int snd_msndmidi_input_close(struct snd_rawmidi_substream *substream)
-{
- struct snd_msndmidi *mpu;
-
- mpu = substream->rmidi->private_data;
- snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_STOP);
- clear_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode);
- mpu->substream_input = NULL;
- snd_msnd_disable_irq(mpu->dev);
- return 0;
-}
-
-static void snd_msndmidi_input_drop(struct snd_msndmidi *mpu)
-{
- u16 tail;
-
- tail = readw(mpu->dev->MIDQ + JQS_wTail);
- writew(tail, mpu->dev->MIDQ + JQS_wHead);
-}
-
-/*
- * trigger input
- */
-static void snd_msndmidi_input_trigger(struct snd_rawmidi_substream *substream,
- int up)
-{
- unsigned long flags;
- struct snd_msndmidi *mpu;
-
- snd_printdd("snd_msndmidi_input_trigger(, %i)\n", up);
-
- mpu = substream->rmidi->private_data;
- spin_lock_irqsave(&mpu->input_lock, flags);
- if (up) {
- if (!test_and_set_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER,
- &mpu->mode))
- snd_msndmidi_input_drop(mpu);
- } else {
- clear_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, &mpu->mode);
- }
- spin_unlock_irqrestore(&mpu->input_lock, flags);
- if (up)
- snd_msndmidi_input_read(mpu);
-}
-
-void snd_msndmidi_input_read(void *mpuv)
-{
- unsigned long flags;
- struct snd_msndmidi *mpu = mpuv;
- void *pwMIDQData = mpu->dev->mappedbase + MIDQ_DATA_BUFF;
-
- spin_lock_irqsave(&mpu->input_lock, flags);
- while (readw(mpu->dev->MIDQ + JQS_wTail) !=
- readw(mpu->dev->MIDQ + JQS_wHead)) {
- u16 wTmp, val;
- val = readw(pwMIDQData + 2 * readw(mpu->dev->MIDQ + JQS_wHead));
-
- if (test_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER,
- &mpu->mode))
- snd_rawmidi_receive(mpu->substream_input,
- (unsigned char *)&val, 1);
-
- wTmp = readw(mpu->dev->MIDQ + JQS_wHead) + 1;
- if (wTmp > readw(mpu->dev->MIDQ + JQS_wSize))
- writew(0, mpu->dev->MIDQ + JQS_wHead);
- else
- writew(wTmp, mpu->dev->MIDQ + JQS_wHead);
- }
- spin_unlock_irqrestore(&mpu->input_lock, flags);
-}
-EXPORT_SYMBOL(snd_msndmidi_input_read);
-
-static struct snd_rawmidi_ops snd_msndmidi_input = {
- .open = snd_msndmidi_input_open,
- .close = snd_msndmidi_input_close,
- .trigger = snd_msndmidi_input_trigger,
-};
-
-static void snd_msndmidi_free(struct snd_rawmidi *rmidi)
-{
- struct snd_msndmidi *mpu = rmidi->private_data;
- kfree(mpu);
-}
-
-int snd_msndmidi_new(struct snd_card *card, int device)
-{
- struct snd_msnd *chip = card->private_data;
- struct snd_msndmidi *mpu;
- struct snd_rawmidi *rmidi;
- int err;
-
- err = snd_rawmidi_new(card, "MSND-MIDI", device, 1, 1, &rmidi);
- if (err < 0)
- return err;
- mpu = kzalloc(sizeof(*mpu), GFP_KERNEL);
- if (mpu == NULL) {
- snd_device_free(card, rmidi);
- return -ENOMEM;
- }
- mpu->dev = chip;
- chip->msndmidi_mpu = mpu;
- rmidi->private_data = mpu;
- rmidi->private_free = snd_msndmidi_free;
- spin_lock_init(&mpu->input_lock);
- strcpy(rmidi->name, "MSND MIDI");
- snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
- &snd_msndmidi_input);
- rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
- return 0;
-}
diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c
index 91d6023a63e5..c4eec391cd29 100644
--- a/sound/isa/msnd/msnd_pinnacle.c
+++ b/sound/isa/msnd/msnd_pinnacle.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*********************************************************************
*
* Linux multisound pinnacle/fiji driver for ALSA.
@@ -10,7 +11,6 @@
* support in alsa, i left all the MSND_CLASSIC tokens in this file.
* but for now this untested & undone.
*
- *
* ripped from linux kernel 2.4.18 by Karsten Wiese.
*
* the following is a copy of the 2.4.18 OSS FREE file-heading comment:
@@ -30,20 +30,6 @@
*
* Copyright (C) 1998 Andrew Veliath
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
********************************************************************/
#include <linux/kernel.h>
@@ -73,17 +59,19 @@
#ifdef MSND_CLASSIC
# include "msnd_classic.h"
# define LOGNAME "msnd_classic"
+# define DEV_NAME "msnd-classic"
#else
# include "msnd_pinnacle.h"
# define LOGNAME "snd_msnd_pinnacle"
+# define DEV_NAME "msnd-pinnacle"
#endif
-static void __devinit set_default_audio_parameters(struct snd_msnd *chip)
+static void set_default_audio_parameters(struct snd_msnd *chip)
{
- chip->play_sample_size = DEFSAMPLESIZE;
+ chip->play_sample_size = snd_pcm_format_width(DEFSAMPLESIZE);
chip->play_sample_rate = DEFSAMPLERATE;
chip->play_channels = DEFCHANNELS;
- chip->capture_sample_size = DEFSAMPLESIZE;
+ chip->capture_sample_size = snd_pcm_format_width(DEFSAMPLESIZE);
chip->capture_sample_rate = DEFSAMPLERATE;
chip->capture_channels = DEFCHANNELS;
}
@@ -93,11 +81,12 @@ static void snd_msnd_eval_dsp_msg(struct snd_msnd *chip, u16 wMessage)
switch (HIBYTE(wMessage)) {
case HIMT_PLAY_DONE: {
if (chip->banksPlayed < 3)
- snd_printdd("%08X: HIMT_PLAY_DONE: %i\n",
+ dev_dbg(chip->card->dev, "%08X: HIMT_PLAY_DONE: %i\n",
(unsigned)jiffies, LOBYTE(wMessage));
if (chip->last_playbank == LOBYTE(wMessage)) {
- snd_printdd("chip.last_playbank == LOBYTE(wMessage)\n");
+ dev_dbg(chip->card->dev,
+ "chip.last_playbank == LOBYTE(wMessage)\n");
break;
}
chip->banksPlayed++;
@@ -133,33 +122,29 @@ static void snd_msnd_eval_dsp_msg(struct snd_msnd *chip, u16 wMessage)
case HIDSP_PLAY_UNDER:
#endif
case HIDSP_INT_PLAY_UNDER:
- snd_printd(KERN_WARNING LOGNAME ": Play underflow %i\n",
+ dev_dbg(chip->card->dev,
+ LOGNAME ": Play underflow %i\n",
chip->banksPlayed);
if (chip->banksPlayed > 2)
clear_bit(F_WRITING, &chip->flags);
break;
case HIDSP_INT_RECORD_OVER:
- snd_printd(KERN_WARNING LOGNAME ": Record overflow\n");
+ dev_dbg(chip->card->dev, LOGNAME ": Record overflow\n");
clear_bit(F_READING, &chip->flags);
break;
default:
- snd_printd(KERN_WARNING LOGNAME
- ": DSP message %d 0x%02x\n",
- LOBYTE(wMessage), LOBYTE(wMessage));
+ dev_dbg(chip->card->dev, LOGNAME
+ ": DSP message %d 0x%02x\n",
+ LOBYTE(wMessage), LOBYTE(wMessage));
break;
}
break;
- case HIMT_MIDI_IN_UCHAR:
- if (chip->msndmidi_mpu)
- snd_msndmidi_input_read(chip->msndmidi_mpu);
- break;
-
default:
- snd_printd(KERN_WARNING LOGNAME ": HIMT message %d 0x%02x\n",
- HIBYTE(wMessage), HIBYTE(wMessage));
+ dev_dbg(chip->card->dev, LOGNAME ": HIMT message %d 0x%02x\n",
+ HIBYTE(wMessage), HIBYTE(wMessage));
break;
}
}
@@ -167,32 +152,34 @@ static void snd_msnd_eval_dsp_msg(struct snd_msnd *chip, u16 wMessage)
static irqreturn_t snd_msnd_interrupt(int irq, void *dev_id)
{
struct snd_msnd *chip = dev_id;
- void *pwDSPQData = chip->mappedbase + DSPQ_DATA_BUFF;
+ void __iomem *pwDSPQData = chip->mappedbase + DSPQ_DATA_BUFF;
+ u16 head, tail, size;
/* Send ack to DSP */
/* inb(chip->io + HP_RXL); */
/* Evaluate queued DSP messages */
- while (readw(chip->DSPQ + JQS_wTail) != readw(chip->DSPQ + JQS_wHead)) {
- u16 wTmp;
-
- snd_msnd_eval_dsp_msg(chip,
- readw(pwDSPQData + 2 * readw(chip->DSPQ + JQS_wHead)));
-
- wTmp = readw(chip->DSPQ + JQS_wHead) + 1;
- if (wTmp > readw(chip->DSPQ + JQS_wSize))
- writew(0, chip->DSPQ + JQS_wHead);
- else
- writew(wTmp, chip->DSPQ + JQS_wHead);
+ head = readw(chip->DSPQ + JQS_wHead);
+ tail = readw(chip->DSPQ + JQS_wTail);
+ size = readw(chip->DSPQ + JQS_wSize);
+ if (head > size || tail > size)
+ goto out;
+ while (head != tail) {
+ snd_msnd_eval_dsp_msg(chip, readw(pwDSPQData + 2 * head));
+ if (++head > size)
+ head = 0;
+ writew(head, chip->DSPQ + JQS_wHead);
}
+ out:
/* Send ack to DSP */
inb(chip->io + HP_RXL);
return IRQ_HANDLED;
}
-static int snd_msnd_reset_dsp(long io, unsigned char *info)
+static int snd_msnd_reset_dsp(struct snd_msnd *chip, unsigned char *info)
{
+ long io = chip->io;
int timeout = 100;
outb(HPDSPRESET_ON, io + HP_DSPR);
@@ -208,12 +195,12 @@ static int snd_msnd_reset_dsp(long io, unsigned char *info)
return 0;
msleep(1);
}
- snd_printk(KERN_ERR LOGNAME ": Cannot reset DSP\n");
+ dev_err(chip->card->dev, LOGNAME ": Cannot reset DSP\n");
return -EIO;
}
-static int __devinit snd_msnd_probe(struct snd_card *card)
+static int snd_msnd_probe(struct snd_card *card)
{
struct snd_msnd *chip = card->private_data;
unsigned char info;
@@ -224,19 +211,19 @@ static int __devinit snd_msnd_probe(struct snd_card *card)
#endif
if (!request_region(chip->io, DSP_NUMIO, "probing")) {
- snd_printk(KERN_ERR LOGNAME ": I/O port conflict\n");
+ dev_err(card->dev, LOGNAME ": I/O port conflict\n");
return -ENODEV;
}
- if (snd_msnd_reset_dsp(chip->io, &info) < 0) {
+ if (snd_msnd_reset_dsp(chip, &info) < 0) {
release_region(chip->io, DSP_NUMIO);
return -ENODEV;
}
#ifdef MSND_CLASSIC
- strcpy(card->shortname, "Classic/Tahiti/Monterey");
- strcpy(card->longname, "Turtle Beach Multisound");
- printk(KERN_INFO LOGNAME ": %s, "
+ strscpy(card->shortname, "Classic/Tahiti/Monterey");
+ strscpy(card->longname, "Turtle Beach Multisound");
+ dev_info(card->dev, LOGNAME ": %s, "
"I/O 0x%lx-0x%lx, IRQ %d, memory mapped to 0x%lX-0x%lX\n",
card->shortname,
chip->io, chip->io + DSP_NUMIO - 1,
@@ -264,39 +251,39 @@ static int __devinit snd_msnd_probe(struct snd_card *card)
switch (info & 0x7) {
case 0x0:
rev = "I";
- strcpy(card->shortname, pin);
+ strscpy(card->shortname, pin);
break;
case 0x1:
rev = "F";
- strcpy(card->shortname, pin);
+ strscpy(card->shortname, pin);
break;
case 0x2:
rev = "G";
- strcpy(card->shortname, pin);
+ strscpy(card->shortname, pin);
break;
case 0x3:
rev = "H";
- strcpy(card->shortname, pin);
+ strscpy(card->shortname, pin);
break;
case 0x4:
rev = "E";
- strcpy(card->shortname, fiji);
+ strscpy(card->shortname, fiji);
break;
case 0x5:
rev = "C";
- strcpy(card->shortname, fiji);
+ strscpy(card->shortname, fiji);
break;
case 0x6:
rev = "D";
- strcpy(card->shortname, fiji);
+ strscpy(card->shortname, fiji);
break;
case 0x7:
rev = "A-B (Fiji) or A-E (Pinnacle)";
- strcpy(card->shortname, pinfiji);
+ strscpy(card->shortname, pinfiji);
break;
}
- strcpy(card->longname, "Turtle Beach Multisound Pinnacle");
- printk(KERN_INFO LOGNAME ": %s revision %s, Xilinx version %s, "
+ strscpy(card->longname, "Turtle Beach Multisound Pinnacle");
+ dev_info(card->dev, LOGNAME ": %s revision %s, Xilinx version %s, "
"I/O 0x%lx-0x%lx, IRQ %d, memory mapped to 0x%lX-0x%lX\n",
card->shortname,
rev, xv,
@@ -313,7 +300,6 @@ static int snd_msnd_init_sma(struct snd_msnd *chip)
{
static int initted;
u16 mastVolLeft, mastVolRight;
- unsigned long flags;
#ifdef MSND_CLASSIC
outb(chip->memid, chip->io + HP_MEMM);
@@ -330,11 +316,11 @@ static int snd_msnd_init_sma(struct snd_msnd *chip)
memset_io(chip->mappedbase, 0, 0x8000);
/* Critical section: bank 1 access */
- spin_lock_irqsave(&chip->lock, flags);
- outb(HPBLKSEL_1, chip->io + HP_BLKS);
- memset_io(chip->mappedbase, 0, 0x8000);
- outb(HPBLKSEL_0, chip->io + HP_BLKS);
- spin_unlock_irqrestore(&chip->lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->lock) {
+ outb(HPBLKSEL_1, chip->io + HP_BLKS);
+ memset_io(chip->mappedbase, 0, 0x8000);
+ outb(HPBLKSEL_0, chip->io + HP_BLKS);
+ }
/* Digital audio play queue */
chip->DAPQ = chip->mappedbase + DAPQ_OFFSET;
@@ -388,22 +374,22 @@ static int upload_dsp_code(struct snd_card *card)
err = request_firmware(&init_fw, INITCODEFILE, card->dev);
if (err < 0) {
- printk(KERN_ERR LOGNAME ": Error loading " INITCODEFILE);
+ dev_err(card->dev, LOGNAME ": Error loading " INITCODEFILE);
goto cleanup1;
}
err = request_firmware(&perm_fw, PERMCODEFILE, card->dev);
if (err < 0) {
- printk(KERN_ERR LOGNAME ": Error loading " PERMCODEFILE);
+ dev_err(card->dev, LOGNAME ": Error loading " PERMCODEFILE);
goto cleanup;
}
memcpy_toio(chip->mappedbase, perm_fw->data, perm_fw->size);
if (snd_msnd_upload_host(chip, init_fw->data, init_fw->size) < 0) {
- printk(KERN_WARNING LOGNAME ": Error uploading to DSP\n");
+ dev_warn(card->dev, LOGNAME ": Error uploading to DSP\n");
err = -ENODEV;
goto cleanup;
}
- printk(KERN_INFO LOGNAME ": DSP firmware uploaded\n");
+ dev_info(card->dev, LOGNAME ": DSP firmware uploaded\n");
err = 0;
cleanup:
@@ -436,17 +422,17 @@ static int snd_msnd_initialize(struct snd_card *card)
#endif
err = snd_msnd_init_sma(chip);
if (err < 0) {
- printk(KERN_WARNING LOGNAME ": Cannot initialize SMA\n");
+ dev_warn(card->dev, LOGNAME ": Cannot initialize SMA\n");
return err;
}
- err = snd_msnd_reset_dsp(chip->io, NULL);
+ err = snd_msnd_reset_dsp(chip, NULL);
if (err < 0)
return err;
err = upload_dsp_code(card);
if (err < 0) {
- printk(KERN_WARNING LOGNAME ": Cannot upload DSP code\n");
+ dev_warn(card->dev, LOGNAME ": Cannot upload DSP code\n");
return err;
}
@@ -455,7 +441,7 @@ static int snd_msnd_initialize(struct snd_card *card)
while (readw(chip->mappedbase)) {
msleep(1);
if (!timeout--) {
- snd_printd(KERN_ERR LOGNAME ": DSP reset timeout\n");
+ dev_err(card->dev, LOGNAME ": DSP reset timeout\n");
return -EIO;
}
}
@@ -477,17 +463,12 @@ static int snd_msnd_dsp_full_reset(struct snd_card *card)
rv = snd_msnd_initialize(card);
if (rv)
- printk(KERN_WARNING LOGNAME ": DSP reset failed\n");
+ dev_warn(card->dev, LOGNAME ": DSP reset failed\n");
snd_msndmix_force_recsrc(chip, 0);
clear_bit(F_RESETTING, &chip->flags);
return rv;
}
-static int snd_msnd_dev_free(struct snd_device *device)
-{
- snd_printdd("snd_msnd_chip_free()\n");
- return 0;
-}
static int snd_msnd_send_dsp_cmd_chk(struct snd_msnd *chip, u8 cmd)
{
@@ -497,9 +478,9 @@ static int snd_msnd_send_dsp_cmd_chk(struct snd_msnd *chip, u8 cmd)
return snd_msnd_send_dsp_cmd(chip, cmd);
}
-static int __devinit snd_msnd_calibrate_adc(struct snd_msnd *chip, u16 srate)
+static int snd_msnd_calibrate_adc(struct snd_msnd *chip, u16 srate)
{
- snd_printdd("snd_msnd_calibrate_adc(%i)\n", srate);
+ dev_dbg(chip->card->dev, "snd_msnd_calibrate_adc(%i)\n", srate);
writew(srate, chip->SMA + SMA_wCalFreqAtoD);
if (chip->calibrate_signal == 0)
writew(readw(chip->SMA + SMA_wCurrHostStatusFlags)
@@ -512,7 +493,7 @@ static int __devinit snd_msnd_calibrate_adc(struct snd_msnd *chip, u16 srate)
schedule_timeout_interruptible(msecs_to_jiffies(333));
return 0;
}
- printk(KERN_WARNING LOGNAME ": ADC calibration failed\n");
+ dev_warn(chip->card->dev, LOGNAME ": ADC calibration failed\n");
return -EIO;
}
@@ -535,61 +516,51 @@ static void snd_msnd_mpu401_close(struct snd_mpu401 *mpu)
static long mpu_io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
-static int __devinit snd_msnd_attach(struct snd_card *card)
+static int snd_msnd_attach(struct snd_card *card)
{
struct snd_msnd *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_msnd_dev_free,
- };
- err = request_irq(chip->irq, snd_msnd_interrupt, 0, card->shortname,
- chip);
+ err = devm_request_irq(card->dev, chip->irq, snd_msnd_interrupt, 0,
+ card->shortname, chip);
if (err < 0) {
- printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", chip->irq);
+ dev_err(card->dev, LOGNAME ": Couldn't grab IRQ %d\n", chip->irq);
return err;
}
- if (request_region(chip->io, DSP_NUMIO, card->shortname) == NULL) {
- free_irq(chip->irq, chip);
+ card->sync_irq = chip->irq;
+ if (!devm_request_region(card->dev, chip->io, DSP_NUMIO,
+ card->shortname))
return -EBUSY;
- }
- if (!request_mem_region(chip->base, BUFFSIZE, card->shortname)) {
- printk(KERN_ERR LOGNAME
+ if (!devm_request_mem_region(card->dev, chip->base, BUFFSIZE,
+ card->shortname)) {
+ dev_err(card->dev, LOGNAME
": unable to grab memory region 0x%lx-0x%lx\n",
chip->base, chip->base + BUFFSIZE - 1);
- release_region(chip->io, DSP_NUMIO);
- free_irq(chip->irq, chip);
return -EBUSY;
}
- chip->mappedbase = ioremap_nocache(chip->base, 0x8000);
+ chip->mappedbase = devm_ioremap(card->dev, chip->base, 0x8000);
if (!chip->mappedbase) {
- printk(KERN_ERR LOGNAME
+ dev_err(card->dev, LOGNAME
": unable to map memory region 0x%lx-0x%lx\n",
chip->base, chip->base + BUFFSIZE - 1);
- err = -EIO;
- goto err_release_region;
+ return -EIO;
}
err = snd_msnd_dsp_full_reset(card);
if (err < 0)
- goto err_release_region;
-
- /* Register device */
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0)
- goto err_release_region;
+ return err;
- err = snd_msnd_pcm(card, 0, NULL);
+ err = snd_msnd_pcm(card, 0);
if (err < 0) {
- printk(KERN_ERR LOGNAME ": error creating new PCM device\n");
- goto err_release_region;
+ dev_err(card->dev, LOGNAME ": error creating new PCM device\n");
+ return err;
}
err = snd_msndmix_new(card);
if (err < 0) {
- printk(KERN_ERR LOGNAME ": error creating new Mixer device\n");
- goto err_release_region;
+ dev_err(card->dev, LOGNAME ": error creating new Mixer device\n");
+ return err;
}
@@ -600,12 +571,12 @@ static int __devinit snd_msnd_attach(struct snd_card *card)
mpu_io[0],
MPU401_MODE_INPUT |
MPU401_MODE_OUTPUT,
- mpu_irq[0], IRQF_DISABLED,
+ mpu_irq[0],
&chip->rmidi);
if (err < 0) {
- printk(KERN_ERR LOGNAME
+ dev_err(card->dev, LOGNAME
": error creating new Midi device\n");
- goto err_release_region;
+ return err;
}
mpu = chip->rmidi->private_data;
@@ -620,132 +591,114 @@ static int __devinit snd_msnd_attach(struct snd_card *card)
err = snd_card_register(card);
if (err < 0)
- goto err_release_region;
+ return err;
return 0;
-
-err_release_region:
- if (chip->mappedbase)
- iounmap(chip->mappedbase);
- release_mem_region(chip->base, BUFFSIZE);
- release_region(chip->io, DSP_NUMIO);
- free_irq(chip->irq, chip);
- return err;
}
-static void __devexit snd_msnd_unload(struct snd_card *card)
-{
- struct snd_msnd *chip = card->private_data;
-
- iounmap(chip->mappedbase);
- release_mem_region(chip->base, BUFFSIZE);
- release_region(chip->io, DSP_NUMIO);
- free_irq(chip->irq, chip);
- snd_card_free(card);
-}
-
#ifndef MSND_CLASSIC
/* Pinnacle/Fiji Logical Device Configuration */
-static int __devinit snd_msnd_write_cfg(int cfg, int reg, int value)
+static int snd_msnd_write_cfg(struct snd_msnd *chip, int cfg, int reg, int value)
{
outb(reg, cfg);
outb(value, cfg + 1);
if (value != inb(cfg + 1)) {
- printk(KERN_ERR LOGNAME ": snd_msnd_write_cfg: I/O error\n");
+ dev_err(chip->card->dev, LOGNAME ": %s: I/O error\n", __func__);
return -EIO;
}
return 0;
}
-static int __devinit snd_msnd_write_cfg_io0(int cfg, int num, u16 io)
+static int snd_msnd_write_cfg_io0(struct snd_msnd *chip, int cfg, int num, u16 io)
{
- if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_LOGDEVICE, num))
return -EIO;
- if (snd_msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io)))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_IO0_BASEHI, HIBYTE(io)))
return -EIO;
- if (snd_msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io)))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_IO0_BASELO, LOBYTE(io)))
return -EIO;
return 0;
}
-static int __devinit snd_msnd_write_cfg_io1(int cfg, int num, u16 io)
+static int snd_msnd_write_cfg_io1(struct snd_msnd *chip, int cfg, int num, u16 io)
{
- if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_LOGDEVICE, num))
return -EIO;
- if (snd_msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io)))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_IO1_BASEHI, HIBYTE(io)))
return -EIO;
- if (snd_msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io)))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_IO1_BASELO, LOBYTE(io)))
return -EIO;
return 0;
}
-static int __devinit snd_msnd_write_cfg_irq(int cfg, int num, u16 irq)
+static int snd_msnd_write_cfg_irq(struct snd_msnd *chip, int cfg, int num, u16 irq)
{
- if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_LOGDEVICE, num))
return -EIO;
- if (snd_msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq)))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_IRQ_NUMBER, LOBYTE(irq)))
return -EIO;
- if (snd_msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE))
return -EIO;
return 0;
}
-static int __devinit snd_msnd_write_cfg_mem(int cfg, int num, int mem)
+static int snd_msnd_write_cfg_mem(struct snd_msnd *chip, int cfg, int num, int mem)
{
u16 wmem;
mem >>= 8;
wmem = (u16)(mem & 0xfff);
- if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_LOGDEVICE, num))
return -EIO;
- if (snd_msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem)))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_MEMBASEHI, HIBYTE(wmem)))
return -EIO;
- if (snd_msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem)))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_MEMBASELO, LOBYTE(wmem)))
return -EIO;
- if (wmem && snd_msnd_write_cfg(cfg, IREG_MEMCONTROL,
+ if (wmem && snd_msnd_write_cfg(chip, cfg, IREG_MEMCONTROL,
MEMTYPE_HIADDR | MEMTYPE_16BIT))
return -EIO;
return 0;
}
-static int __devinit snd_msnd_activate_logical(int cfg, int num)
+static int snd_msnd_activate_logical(struct snd_msnd *chip, int cfg, int num)
{
- if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_LOGDEVICE, num))
return -EIO;
- if (snd_msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_ACTIVATE, LD_ACTIVATE))
return -EIO;
return 0;
}
-static int __devinit snd_msnd_write_cfg_logical(int cfg, int num, u16 io0,
- u16 io1, u16 irq, int mem)
+static int snd_msnd_write_cfg_logical(struct snd_msnd *chip,
+ int cfg, int num, u16 io0,
+ u16 io1, u16 irq, int mem)
{
- if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
+ if (snd_msnd_write_cfg(chip, cfg, IREG_LOGDEVICE, num))
return -EIO;
- if (snd_msnd_write_cfg_io0(cfg, num, io0))
+ if (snd_msnd_write_cfg_io0(chip, cfg, num, io0))
return -EIO;
- if (snd_msnd_write_cfg_io1(cfg, num, io1))
+ if (snd_msnd_write_cfg_io1(chip, cfg, num, io1))
return -EIO;
- if (snd_msnd_write_cfg_irq(cfg, num, irq))
+ if (snd_msnd_write_cfg_irq(chip, cfg, num, irq))
return -EIO;
- if (snd_msnd_write_cfg_mem(cfg, num, mem))
+ if (snd_msnd_write_cfg_mem(chip, cfg, num, mem))
return -EIO;
- if (snd_msnd_activate_logical(cfg, num))
+ if (snd_msnd_activate_logical(chip, cfg, num))
return -EIO;
return 0;
}
-static int __devinit snd_msnd_pinnacle_cfg_reset(int cfg)
+static int snd_msnd_pinnacle_cfg_reset(struct snd_msnd *chip, int cfg)
{
int i;
/* Reset devices if told to */
- printk(KERN_INFO LOGNAME ": Resetting all devices\n");
+ dev_info(chip->card->dev, LOGNAME ": Resetting all devices\n");
for (i = 0; i < 4; ++i)
- if (snd_msnd_write_cfg_logical(cfg, i, 0, 0, 0, 0))
+ if (snd_msnd_write_cfg_logical(chip, cfg, i, 0, 0, 0, 0))
return -EIO;
return 0;
@@ -755,9 +708,9 @@ static int __devinit snd_msnd_pinnacle_cfg_reset(int cfg)
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-module_param_array(index, int, NULL, S_IRUGO);
+module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for msnd_pinnacle soundcard.");
-module_param_array(id, charp, NULL, S_IRUGO);
+module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for msnd_pinnacle soundcard.");
static long io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
@@ -785,7 +738,7 @@ static int write_ndelay[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 1 };
static int calibrate_signal;
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard.");
#define has_isapnp(x) isapnp[x]
@@ -799,32 +752,32 @@ MODULE_LICENSE("GPL");
MODULE_FIRMWARE(INITCODEFILE);
MODULE_FIRMWARE(PERMCODEFILE);
-module_param_array(io, long, NULL, S_IRUGO);
+module_param_hw_array(io, long, ioport, NULL, 0444);
MODULE_PARM_DESC(io, "IO port #");
-module_param_array(irq, int, NULL, S_IRUGO);
-module_param_array(mem, long, NULL, S_IRUGO);
-module_param_array(write_ndelay, int, NULL, S_IRUGO);
-module_param(calibrate_signal, int, S_IRUGO);
+module_param_hw_array(irq, int, irq, NULL, 0444);
+module_param_hw_array(mem, long, iomem, NULL, 0444);
+module_param_array(write_ndelay, int, NULL, 0444);
+module_param(calibrate_signal, int, 0444);
#ifndef MSND_CLASSIC
-module_param_array(digital, int, NULL, S_IRUGO);
-module_param_array(cfg, long, NULL, S_IRUGO);
-module_param_array(reset, int, 0, S_IRUGO);
-module_param_array(mpu_io, long, NULL, S_IRUGO);
-module_param_array(mpu_irq, int, NULL, S_IRUGO);
-module_param_array(ide_io0, long, NULL, S_IRUGO);
-module_param_array(ide_io1, long, NULL, S_IRUGO);
-module_param_array(ide_irq, int, NULL, S_IRUGO);
-module_param_array(joystick_io, long, NULL, S_IRUGO);
+module_param_array(digital, int, NULL, 0444);
+module_param_hw_array(cfg, long, ioport, NULL, 0444);
+module_param_array(reset, int, NULL, 0444);
+module_param_hw_array(mpu_io, long, ioport, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
+module_param_hw_array(ide_io0, long, ioport, NULL, 0444);
+module_param_hw_array(ide_io1, long, ioport, NULL, 0444);
+module_param_hw_array(ide_irq, int, irq, NULL, 0444);
+module_param_hw_array(joystick_io, long, ioport, NULL, 0444);
#endif
-static int __devinit snd_msnd_isa_match(struct device *pdev, unsigned int i)
+static int snd_msnd_isa_match(struct device *pdev, unsigned int i)
{
if (io[i] == SNDRV_AUTO_PORT)
return 0;
if (irq[i] == SNDRV_AUTO_PORT || mem[i] == SNDRV_AUTO_PORT) {
- printk(KERN_WARNING LOGNAME ": io, irq and mem must be set\n");
+ dev_warn(pdev, LOGNAME ": io, irq and mem must be set\n");
return 0;
}
@@ -837,14 +790,14 @@ static int __devinit snd_msnd_isa_match(struct device *pdev, unsigned int i)
io[i] == 0x220 ||
io[i] == 0x210 ||
io[i] == 0x3e0)) {
- printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set "
+ dev_err(pdev, LOGNAME ": \"io\" - DSP I/O base must be set "
" to 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x290, "
"or 0x3E0\n");
return 0;
}
#else
if (io[i] < 0x100 || io[i] > 0x3e0 || (io[i] % 0x10) != 0) {
- printk(KERN_ERR LOGNAME
+ dev_err(pdev, LOGNAME
": \"io\" - DSP I/O base must within the range 0x100 "
"to 0x3E0 and must be evenly divisible by 0x10\n");
return 0;
@@ -857,7 +810,7 @@ static int __devinit snd_msnd_isa_match(struct device *pdev, unsigned int i)
irq[i] == 10 ||
irq[i] == 11 ||
irq[i] == 12)) {
- printk(KERN_ERR LOGNAME
+ dev_err(pdev, LOGNAME
": \"irq\" - must be set to 5, 7, 9, 10, 11 or 12\n");
return 0;
}
@@ -868,7 +821,7 @@ static int __devinit snd_msnd_isa_match(struct device *pdev, unsigned int i)
mem[i] == 0xd8000 ||
mem[i] == 0xe0000 ||
mem[i] == 0xe8000)) {
- printk(KERN_ERR LOGNAME ": \"mem\" - must be set to "
+ dev_err(pdev, LOGNAME ": \"mem\" - must be set to "
"0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or "
"0xe8000\n");
return 0;
@@ -876,9 +829,9 @@ static int __devinit snd_msnd_isa_match(struct device *pdev, unsigned int i)
#ifndef MSND_CLASSIC
if (cfg[i] == SNDRV_AUTO_PORT) {
- printk(KERN_INFO LOGNAME ": Assuming PnP mode\n");
+ dev_info(pdev, LOGNAME ": Assuming PnP mode\n");
} else if (cfg[i] != 0x250 && cfg[i] != 0x260 && cfg[i] != 0x270) {
- printk(KERN_INFO LOGNAME
+ dev_info(pdev, LOGNAME
": Config port must be 0x250, 0x260 or 0x270 "
"(or unspecified for PnP mode)\n");
return 0;
@@ -888,7 +841,7 @@ static int __devinit snd_msnd_isa_match(struct device *pdev, unsigned int i)
return 1;
}
-static int __devinit snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
+static int snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
{
int err;
struct snd_card *card;
@@ -899,16 +852,15 @@ static int __devinit snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
|| cfg[idx] == SNDRV_AUTO_PORT
#endif
) {
- printk(KERN_INFO LOGNAME ": Assuming PnP mode\n");
+ dev_info(pdev, LOGNAME ": Assuming PnP mode\n");
return -ENODEV;
}
- err = snd_card_create(index[idx], id[idx], THIS_MODULE,
- sizeof(struct snd_msnd), &card);
+ err = snd_devm_card_new(pdev, index[idx], id[idx], THIS_MODULE,
+ sizeof(struct snd_msnd), &card);
if (err < 0)
return err;
- snd_card_set_dev(card, pdev);
chip = card->private_data;
chip->card = card;
@@ -943,73 +895,70 @@ static int __devinit snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
chip->memid = HPMEM_E800; break;
}
#else
- printk(KERN_INFO LOGNAME ": Non-PnP mode: configuring at port 0x%lx\n",
- cfg[idx]);
+ dev_info(pdev, LOGNAME ": Non-PnP mode: configuring at port 0x%lx\n",
+ cfg[idx]);
- if (!request_region(cfg[idx], 2, "Pinnacle/Fiji Config")) {
- printk(KERN_ERR LOGNAME ": Config port 0x%lx conflict\n",
- cfg[idx]);
- snd_card_free(card);
+ if (!devm_request_region(card->dev, cfg[idx], 2,
+ "Pinnacle/Fiji Config")) {
+ dev_err(pdev, LOGNAME ": Config port 0x%lx conflict\n",
+ cfg[idx]);
return -EIO;
}
if (reset[idx])
- if (snd_msnd_pinnacle_cfg_reset(cfg[idx])) {
- err = -EIO;
- goto cfg_error;
- }
+ if (snd_msnd_pinnacle_cfg_reset(chip, cfg[idx]))
+ return -EIO;
/* DSP */
- err = snd_msnd_write_cfg_logical(cfg[idx], 0,
+ err = snd_msnd_write_cfg_logical(chip, cfg[idx], 0,
io[idx], 0,
irq[idx], mem[idx]);
if (err)
- goto cfg_error;
+ return err;
/* The following are Pinnacle specific */
/* MPU */
if (mpu_io[idx] != SNDRV_AUTO_PORT
&& mpu_irq[idx] != SNDRV_AUTO_IRQ) {
- printk(KERN_INFO LOGNAME
+ dev_info(pdev, LOGNAME
": Configuring MPU to I/O 0x%lx IRQ %d\n",
mpu_io[idx], mpu_irq[idx]);
- err = snd_msnd_write_cfg_logical(cfg[idx], 1,
+ err = snd_msnd_write_cfg_logical(chip, cfg[idx], 1,
mpu_io[idx], 0,
mpu_irq[idx], 0);
if (err)
- goto cfg_error;
+ return err;
}
/* IDE */
if (ide_io0[idx] != SNDRV_AUTO_PORT
&& ide_io1[idx] != SNDRV_AUTO_PORT
&& ide_irq[idx] != SNDRV_AUTO_IRQ) {
- printk(KERN_INFO LOGNAME
+ dev_info(pdev, LOGNAME
": Configuring IDE to I/O 0x%lx, 0x%lx IRQ %d\n",
ide_io0[idx], ide_io1[idx], ide_irq[idx]);
- err = snd_msnd_write_cfg_logical(cfg[idx], 2,
+ err = snd_msnd_write_cfg_logical(chip, cfg[idx], 2,
ide_io0[idx], ide_io1[idx],
ide_irq[idx], 0);
if (err)
- goto cfg_error;
+ return err;
}
/* Joystick */
if (joystick_io[idx] != SNDRV_AUTO_PORT) {
- printk(KERN_INFO LOGNAME
+ dev_info(pdev, LOGNAME
": Configuring joystick to I/O 0x%lx\n",
joystick_io[idx]);
- err = snd_msnd_write_cfg_logical(cfg[idx], 3,
+ err = snd_msnd_write_cfg_logical(chip, cfg[idx], 3,
joystick_io[idx], 0,
0, 0);
if (err)
- goto cfg_error;
+ return err;
}
- release_region(cfg[idx], 2);
#endif /* MSND_CLASSIC */
@@ -1038,42 +987,23 @@ static int __devinit snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
spin_lock_init(&chip->lock);
err = snd_msnd_probe(card);
if (err < 0) {
- printk(KERN_ERR LOGNAME ": Probe failed\n");
- snd_card_free(card);
+ dev_err(pdev, LOGNAME ": Probe failed\n");
return err;
}
err = snd_msnd_attach(card);
if (err < 0) {
- printk(KERN_ERR LOGNAME ": Attach failed\n");
- snd_card_free(card);
+ dev_err(pdev, LOGNAME ": Attach failed\n");
return err;
}
dev_set_drvdata(pdev, card);
return 0;
-
-#ifndef MSND_CLASSIC
-cfg_error:
- release_region(cfg[idx], 2);
- snd_card_free(card);
- return err;
-#endif
-}
-
-static int __devexit snd_msnd_isa_remove(struct device *pdev, unsigned int dev)
-{
- snd_msnd_unload(dev_get_drvdata(pdev));
- dev_set_drvdata(pdev, NULL);
- return 0;
}
-#define DEV_NAME "msnd-pinnacle"
-
static struct isa_driver snd_msnd_driver = {
.match = snd_msnd_isa_match,
.probe = snd_msnd_isa_probe,
- .remove = __devexit_p(snd_msnd_isa_remove),
/* FIXME: suspend, resume */
.driver = {
.name = DEV_NAME
@@ -1081,8 +1011,8 @@ static struct isa_driver snd_msnd_driver = {
};
#ifdef CONFIG_PNP
-static int __devinit snd_msnd_pnp_detect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_msnd_pnp_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
static int idx;
struct pnp_dev *pnp_dev;
@@ -1110,12 +1040,12 @@ static int __devinit snd_msnd_pnp_detect(struct pnp_card_link *pcard,
return -ENODEV;
if (!pnp_is_active(pnp_dev) && pnp_activate_dev(pnp_dev) < 0) {
- printk(KERN_INFO "msnd_pinnacle: device is inactive\n");
+ dev_info(&pcard->card->dev, "msnd_pinnacle: device is inactive\n");
return -EBUSY;
}
if (!pnp_is_active(mpu_dev) && pnp_activate_dev(mpu_dev) < 0) {
- printk(KERN_INFO "msnd_pinnacle: MPU device is inactive\n");
+ dev_info(&pcard->card->dev, "msnd_pinnacle: MPU device is inactive\n");
return -EBUSY;
}
@@ -1123,14 +1053,14 @@ static int __devinit snd_msnd_pnp_detect(struct pnp_card_link *pcard,
* Create a new ALSA sound card entry, in anticipation
* of detecting our hardware ...
*/
- ret = snd_card_create(index[idx], id[idx], THIS_MODULE,
- sizeof(struct snd_msnd), &card);
+ ret = snd_devm_card_new(&pcard->card->dev,
+ index[idx], id[idx], THIS_MODULE,
+ sizeof(struct snd_msnd), &card);
if (ret < 0)
return ret;
chip = card->private_data;
chip->card = card;
- snd_card_set_dev(card, &pcard->card->dev);
/*
* Read the correct parameters off the ISA PnP bus ...
@@ -1166,35 +1096,25 @@ static int __devinit snd_msnd_pnp_detect(struct pnp_card_link *pcard,
spin_lock_init(&chip->lock);
ret = snd_msnd_probe(card);
if (ret < 0) {
- printk(KERN_ERR LOGNAME ": Probe failed\n");
- goto _release_card;
+ dev_err(&pcard->card->dev, LOGNAME ": Probe failed\n");
+ return ret;
}
ret = snd_msnd_attach(card);
if (ret < 0) {
- printk(KERN_ERR LOGNAME ": Attach failed\n");
- goto _release_card;
+ dev_err(&pcard->card->dev, LOGNAME ": Attach failed\n");
+ return ret;
}
pnp_set_card_drvdata(pcard, card);
++idx;
return 0;
-
-_release_card:
- snd_card_free(card);
- return ret;
-}
-
-static void __devexit snd_msnd_pnp_remove(struct pnp_card_link *pcard)
-{
- snd_msnd_unload(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
}
static int isa_registered;
static int pnp_registered;
-static struct pnp_card_device_id msnd_pnpids[] = {
+static const struct pnp_card_device_id msnd_pnpids[] = {
/* Pinnacle PnP */
{ .id = "BVJ0440", .devs = { { "TBS0000" }, { "TBS0001" } } },
{ .id = "" } /* end */
@@ -1207,7 +1127,6 @@ static struct pnp_card_driver msnd_pnpc_driver = {
.name = "msnd_pinnacle",
.id_table = msnd_pnpids,
.probe = snd_msnd_pnp_detect,
- .remove = __devexit_p(snd_msnd_pnp_remove),
};
#endif /* CONFIG_PNP */
diff --git a/sound/isa/msnd/msnd_pinnacle.h b/sound/isa/msnd/msnd_pinnacle.h
index 48318d1ee340..c928d8492ef2 100644
--- a/sound/isa/msnd/msnd_pinnacle.h
+++ b/sound/isa/msnd/msnd_pinnacle.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*********************************************************************
*
* msnd_pinnacle.h
@@ -10,20 +11,6 @@
* Copyright (C) 1998 Andrew Veliath
* Copyright (C) 1993 Turtle Beach Systems, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
********************************************************************/
#ifndef __MSND_PINNACLE_H
#define __MSND_PINNACLE_H
diff --git a/sound/isa/msnd/msnd_pinnacle_mixer.c b/sound/isa/msnd/msnd_pinnacle_mixer.c
index 494058a1a502..ec354483b9f8 100644
--- a/sound/isa/msnd/msnd_pinnacle_mixer.c
+++ b/sound/isa/msnd/msnd_pinnacle_mixer.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/***************************************************************************
msnd_pinnacle_mixer.c - description
-------------------
@@ -8,14 +9,11 @@
/***************************************************************************
* *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
* *
***************************************************************************/
#include <linux/io.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -54,20 +52,13 @@
static int snd_msndmix_info_mux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {
+ static const char * const texts[3] = {
"Analog", "MASS", "SPDIF",
};
struct snd_msnd *chip = snd_kcontrol_chip(kcontrol);
unsigned items = test_bit(F_HAVEDIGITAL, &chip->flags) ? 3 : 2;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = items;
- if (uinfo->value.enumerated.item >= items)
- uinfo->value.enumerated.item = items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, items, texts);
}
static int snd_msndmix_get_mux(struct snd_kcontrol *kcontrol,
@@ -145,14 +136,12 @@ static int snd_msndmix_volume_get(struct snd_kcontrol *kcontrol,
{
struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
int addr = kcontrol->private_value;
- unsigned long flags;
- spin_lock_irqsave(&msnd->mixer_lock, flags);
+ guard(spinlock_irqsave)(&msnd->mixer_lock);
ucontrol->value.integer.value[0] = msnd->left_levels[addr] * 100;
ucontrol->value.integer.value[0] /= 0xFFFF;
ucontrol->value.integer.value[1] = msnd->right_levels[addr] * 100;
ucontrol->value.integer.value[1] /= 0xFFFF;
- spin_unlock_irqrestore(&msnd->mixer_lock, flags);
return 0;
}
@@ -228,11 +217,9 @@ static int snd_msndmix_set(struct snd_msnd *dev, int d, int left, int right)
case MSND_MIXER_VOLUME: /* master volume */
writew(wLeft, dev->SMA + SMA_wCurrMastVolLeft);
writew(wRight, dev->SMA + SMA_wCurrMastVolRight);
- /* fall through */
-
+ fallthrough;
case MSND_MIXER_AUX: /* aux pot control */
/* scaled by master volume */
- /* fall through */
/* digital controls */
case MSND_MIXER_SYNTH: /* synth vol (dsp mix) */
@@ -264,15 +251,13 @@ static int snd_msndmix_volume_put(struct snd_kcontrol *kcontrol,
struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
int change, addr = kcontrol->private_value;
int left, right;
- unsigned long flags;
left = ucontrol->value.integer.value[0] % 101;
right = ucontrol->value.integer.value[1] % 101;
- spin_lock_irqsave(&msnd->mixer_lock, flags);
+ guard(spinlock_irqsave)(&msnd->mixer_lock);
change = msnd->left_levels[addr] != left
|| msnd->right_levels[addr] != right;
snd_msndmix_set(msnd, addr, left, right);
- spin_unlock_irqrestore(&msnd->mixer_lock, flags);
return change;
}
@@ -284,7 +269,7 @@ static int snd_msndmix_volume_put(struct snd_kcontrol *kcontrol,
.private_value = addr }
-static struct snd_kcontrol_new snd_msnd_controls[] = {
+static const struct snd_kcontrol_new snd_msnd_controls[] = {
DUMMY_VOLUME("Master Volume", 0, MSND_MIXER_VOLUME),
DUMMY_VOLUME("PCM Volume", 0, MSND_MIXER_PCM),
DUMMY_VOLUME("Aux Volume", 0, MSND_MIXER_AUX),
@@ -301,7 +286,7 @@ DUMMY_VOLUME("Monitor", 0, MSND_MIXER_IMIX),
};
-int __devinit snd_msndmix_new(struct snd_card *card)
+int snd_msndmix_new(struct snd_card *card)
{
struct snd_msnd *chip = card->private_data;
unsigned int idx;
@@ -310,13 +295,14 @@ int __devinit snd_msndmix_new(struct snd_card *card)
if (snd_BUG_ON(!chip))
return -EINVAL;
spin_lock_init(&chip->mixer_lock);
- strcpy(card->mixername, "MSND Pinnacle Mixer");
+ strscpy(card->mixername, "MSND Pinnacle Mixer");
- for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++)
+ for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++) {
err = snd_ctl_add(card,
snd_ctl_new1(snd_msnd_controls + idx, chip));
if (err < 0)
return err;
+ }
return 0;
}
diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c
index 265abcce9dba..8c1767697b62 100644
--- a/sound/isa/opl3sa2.c
+++ b/sound/isa/opl3sa2.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Yamaha OPL3-SA[2,3] soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -25,7 +10,8 @@
#include <linux/interrupt.h>
#include <linux/pm.h>
#include <linux/pnp.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/wss.h>
#include <sound/mpu401.h>
@@ -33,22 +19,15 @@
#include <sound/initval.h>
#include <sound/tlv.h>
-#include <asm/io.h>
-
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Yamaha OPL3SA2+");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Yamaha,YMF719E-S},"
- "{Genius,Sound Maker 3DX},"
- "{Yamaha,OPL3SA3},"
- "{Intel,AL440LX sound},"
- "{NeoMagic,MagicWave 3DX}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
#endif
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0xf86,0x370,0x100 */
static long sb_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */
@@ -70,21 +49,21 @@ MODULE_PARM_DESC(enable, "Enable OPL3-SA soundcard.");
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
#endif
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for OPL3-SA driver.");
-module_param_array(sb_port, long, NULL, 0444);
+module_param_hw_array(sb_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(sb_port, "SB port # for OPL3-SA driver.");
-module_param_array(wss_port, long, NULL, 0444);
+module_param_hw_array(wss_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(wss_port, "WSS port # for OPL3-SA driver.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for OPL3-SA driver.");
-module_param_array(midi_port, long, NULL, 0444);
+module_param_hw_array(midi_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(midi_port, "MIDI port # for OPL3-SA driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for OPL3-SA driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for OPL3-SA driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for OPL3-SA driver.");
module_param_array(opl3sa3_ymode, int, NULL, 0444);
MODULE_PARM_DESC(opl3sa3_ymode, "Speaker size selection for 3D Enhancement mode: Desktop/Large Notebook/Small Notebook/HiFi.");
@@ -129,6 +108,7 @@ struct snd_opl3sa2 {
int irq;
int single_dma;
spinlock_t reg_lock;
+ struct snd_card *card;
struct snd_hwdep *synth;
struct snd_rawmidi *rmidi;
struct snd_wss *wss;
@@ -142,7 +122,7 @@ struct snd_opl3sa2 {
#ifdef CONFIG_PNP
-static struct pnp_device_id snd_opl3sa2_pnpbiosids[] = {
+static const struct pnp_device_id snd_opl3sa2_pnpbiosids[] = {
{ .id = "YMH0021" },
{ .id = "NMX2210" }, /* Gateway Solo 2500 */
{ .id = "" } /* end */
@@ -150,7 +130,7 @@ static struct pnp_device_id snd_opl3sa2_pnpbiosids[] = {
MODULE_DEVICE_TABLE(pnp, snd_opl3sa2_pnpbiosids);
-static struct pnp_card_device_id snd_opl3sa2_pnpids[] = {
+static const struct pnp_card_device_id snd_opl3sa2_pnpids[] = {
/* Yamaha YMF719E-S (Genius Sound Maker 3DX) */
{ .id = "YMH0020", .devs = { { "YMH0021" } } },
/* Yamaha OPL3-SA3 (integrated on Intel's Pentium II AL440LX motherboard) */
@@ -178,12 +158,12 @@ static unsigned char __snd_opl3sa2_read(struct snd_opl3sa2 *chip, unsigned char
unsigned char result;
#if 0
outb(0x1d, port); /* password */
- printk(KERN_DEBUG "read [0x%lx] = 0x%x\n", port, inb(port));
+ dev_dbg(chip->card->dev, "read [0x%lx] = 0x%x\n", port, inb(port));
#endif
outb(reg, chip->port); /* register */
result = inb(chip->port + 1);
#if 0
- printk(KERN_DEBUG "read [0x%lx] = 0x%x [0x%x]\n",
+ dev_dbg(chip->card->dev, "read [0x%lx] = 0x%x [0x%x]\n",
port, result, inb(port));
#endif
return result;
@@ -192,13 +172,8 @@ static unsigned char __snd_opl3sa2_read(struct snd_opl3sa2 *chip, unsigned char
/* read control port (with spinlock) */
static unsigned char snd_opl3sa2_read(struct snd_opl3sa2 *chip, unsigned char reg)
{
- unsigned long flags;
- unsigned char result;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
- result = __snd_opl3sa2_read(chip, reg);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return result;
+ guard(spinlock_irqsave)(&chip->reg_lock);
+ return __snd_opl3sa2_read(chip, reg);
}
/* write control port (w/o spinlock) */
@@ -215,13 +190,11 @@ static void __snd_opl3sa2_write(struct snd_opl3sa2 *chip, unsigned char reg, uns
/* write control port (with spinlock) */
static void snd_opl3sa2_write(struct snd_opl3sa2 *chip, unsigned char reg, unsigned char value)
{
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
__snd_opl3sa2_write(chip, reg, value);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
-static int __devinit snd_opl3sa2_detect(struct snd_card *card)
+static int snd_opl3sa2_detect(struct snd_card *card)
{
struct snd_opl3sa2 *chip = card->private_data;
unsigned long port;
@@ -229,18 +202,16 @@ static int __devinit snd_opl3sa2_detect(struct snd_card *card)
char str[2];
port = chip->port;
- if ((chip->res_port = request_region(port, 2, "OPL3-SA control")) == NULL) {
- snd_printk(KERN_ERR PFX "can't grab port 0x%lx\n", port);
+ chip->res_port = devm_request_region(card->dev, port, 2,
+ "OPL3-SA control");
+ if (!chip->res_port) {
+ dev_err(card->dev, "can't grab port 0x%lx\n", port);
return -EBUSY;
}
- /*
- snd_printk(KERN_DEBUG "REG 0A = 0x%x\n",
- snd_opl3sa2_read(chip, 0x0a));
- */
chip->version = 0;
tmp = snd_opl3sa2_read(chip, OPL3SA2_MISC);
if (tmp == 0xff) {
- snd_printd("OPL3-SA [0x%lx] detect = 0x%x\n", port, tmp);
+ dev_dbg(card->dev, "OPL3-SA [0x%lx] detect = 0x%x\n", port, tmp);
return -ENODEV;
}
switch (tmp & 0x07) {
@@ -260,15 +231,17 @@ static int __devinit snd_opl3sa2_detect(struct snd_card *card)
str[1] = 0;
strcat(card->shortname, str);
snd_opl3sa2_write(chip, OPL3SA2_MISC, tmp ^ 7);
- if ((tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MISC)) != tmp) {
- snd_printd("OPL3-SA [0x%lx] detect (1) = 0x%x (0x%x)\n", port, tmp, tmp1);
+ tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MISC);
+ if (tmp1 != tmp) {
+ dev_dbg(card->dev, "OPL3-SA [0x%lx] detect (1) = 0x%x (0x%x)\n", port, tmp, tmp1);
return -ENODEV;
}
- /* try if the MIC register is accesible */
+ /* try if the MIC register is accessible */
tmp = snd_opl3sa2_read(chip, OPL3SA2_MIC);
snd_opl3sa2_write(chip, OPL3SA2_MIC, 0x8a);
- if (((tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MIC)) & 0x9f) != 0x8a) {
- snd_printd("OPL3-SA [0x%lx] detect (2) = 0x%x (0x%x)\n", port, tmp, tmp1);
+ tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MIC);
+ if ((tmp1 & 0x9f) != 0x8a) {
+ dev_dbg(card->dev, "OPL3-SA [0x%lx] detect (2) = 0x%x (0x%x)\n", port, tmp, tmp1);
return -ENODEV;
}
snd_opl3sa2_write(chip, OPL3SA2_MIC, 0x9f);
@@ -356,15 +329,13 @@ static irqreturn_t snd_opl3sa2_interrupt(int irq, void *dev_id)
static int snd_opl3sa2_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = (chip->ctlregs[reg] >> shift) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert)
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
return 0;
@@ -373,7 +344,6 @@ static int snd_opl3sa2_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_opl3sa2_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
@@ -385,12 +355,11 @@ static int snd_opl3sa2_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
if (invert)
val = mask - val;
val <<= shift;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
oval = chip->ctlregs[reg];
val = (oval & ~(mask << shift)) | val;
change = val != oval;
__snd_opl3sa2_write(chip, reg, val);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -411,7 +380,6 @@ static int snd_opl3sa2_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_opl3sa2_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -419,10 +387,9 @@ static int snd_opl3sa2_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
int mask = (kcontrol->private_value >> 24) & 0xff;
int invert = (kcontrol->private_value >> 22) & 1;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = (chip->ctlregs[left_reg] >> shift_left) & mask;
ucontrol->value.integer.value[1] = (chip->ctlregs[right_reg] >> shift_right) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert) {
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
@@ -433,7 +400,6 @@ static int snd_opl3sa2_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_opl3sa2_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -451,7 +417,7 @@ static int snd_opl3sa2_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
}
val1 <<= shift_left;
val2 <<= shift_right;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (left_reg != right_reg) {
oval1 = chip->ctlregs[left_reg];
oval2 = chip->ctlregs[right_reg];
@@ -466,14 +432,13 @@ static int snd_opl3sa2_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
change = val1 != oval1;
__snd_opl3sa2_write(chip, left_reg, val1);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
static const DECLARE_TLV_DB_SCALE(db_scale_master, -3000, 200, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
-static struct snd_kcontrol_new snd_opl3sa2_controls[] = {
+static const struct snd_kcontrol_new snd_opl3sa2_controls[] = {
OPL3SA2_DOUBLE("Master Playback Switch", 0, 0x07, 0x08, 7, 7, 1, 1),
OPL3SA2_DOUBLE_TLV("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1,
db_scale_master),
@@ -483,7 +448,7 @@ OPL3SA2_SINGLE_TLV("Mic Playback Volume", 0, 0x09, 0, 31, 1,
OPL3SA2_SINGLE("ZV Port Switch", 0, 0x02, 0, 1, 0),
};
-static struct snd_kcontrol_new snd_opl3sa2_tone_controls[] = {
+static const struct snd_kcontrol_new snd_opl3sa2_tone_controls[] = {
OPL3SA2_DOUBLE("3D Control - Wide", 0, 0x14, 0x14, 4, 0, 7, 0),
OPL3SA2_DOUBLE("Tone Control - Bass", 0, 0x15, 0x15, 4, 0, 7, 0),
OPL3SA2_DOUBLE("Tone Control - Treble", 0, 0x16, 0x16, 4, 0, 7, 0)
@@ -496,7 +461,7 @@ static void snd_opl3sa2_master_free(struct snd_kcontrol *kcontrol)
chip->master_volume = NULL;
}
-static int __devinit snd_opl3sa2_mixer(struct snd_card *card)
+static int snd_opl3sa2_mixer(struct snd_card *card)
{
struct snd_opl3sa2 *chip = card->private_data;
struct snd_ctl_elem_id id1, id2;
@@ -508,34 +473,40 @@ static int __devinit snd_opl3sa2_mixer(struct snd_card *card)
memset(&id2, 0, sizeof(id2));
id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
/* reassign AUX0 to CD */
- strcpy(id1.name, "Aux Playback Switch");
- strcpy(id2.name, "CD Playback Switch");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) {
- snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n");
+ strscpy(id1.name, "Aux Playback Switch");
+ strscpy(id2.name, "CD Playback Switch");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ dev_err(card->dev, "Cannot rename opl3sa2 control\n");
return err;
}
- strcpy(id1.name, "Aux Playback Volume");
- strcpy(id2.name, "CD Playback Volume");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) {
- snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n");
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "CD Playback Volume");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ dev_err(card->dev, "Cannot rename opl3sa2 control\n");
return err;
}
/* reassign AUX1 to FM */
- strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
- strcpy(id2.name, "FM Playback Switch");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) {
- snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n");
+ strscpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+ strscpy(id2.name, "FM Playback Switch");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ dev_err(card->dev, "Cannot rename opl3sa2 control\n");
return err;
}
- strcpy(id1.name, "Aux Playback Volume");
- strcpy(id2.name, "FM Playback Volume");
- if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) {
- snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n");
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "FM Playback Volume");
+ err = snd_ctl_rename_id(card, &id1, &id2);
+ if (err < 0) {
+ dev_err(card->dev, "Cannot rename opl3sa2 control\n");
return err;
}
/* add OPL3SA2 controls */
for (idx = 0; idx < ARRAY_SIZE(snd_opl3sa2_controls); idx++) {
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_opl3sa2_controls[idx], chip))) < 0)
+ kctl = snd_ctl_new1(&snd_opl3sa2_controls[idx], chip);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
switch (idx) {
case 0: chip->master_switch = kctl; kctl->private_free = snd_opl3sa2_master_free; break;
@@ -543,9 +514,11 @@ static int __devinit snd_opl3sa2_mixer(struct snd_card *card)
}
}
if (chip->version > 2) {
- for (idx = 0; idx < ARRAY_SIZE(snd_opl3sa2_tone_controls); idx++)
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opl3sa2_tone_controls[idx], chip))) < 0)
+ for (idx = 0; idx < ARRAY_SIZE(snd_opl3sa2_tone_controls); idx++) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_opl3sa2_tone_controls[idx], chip));
+ if (err < 0)
return err;
+ }
}
return 0;
}
@@ -596,11 +569,11 @@ static int snd_opl3sa2_resume(struct snd_card *card)
#endif /* CONFIG_PM */
#ifdef CONFIG_PNP
-static int __devinit snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip,
- struct pnp_dev *pdev)
+static int snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip,
+ struct pnp_dev *pdev)
{
if (pnp_activate_dev(pdev) < 0) {
- snd_printk(KERN_ERR "PnP configure failure (out of resources?)\n");
+ dev_err(chip->card->dev, "PnP configure failure (out of resources?)\n");
return -EBUSY;
}
sb_port[dev] = pnp_port_start(pdev, 0);
@@ -611,43 +584,35 @@ static int __devinit snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip,
dma1[dev] = pnp_dma(pdev, 0);
dma2[dev] = pnp_dma(pdev, 1);
irq[dev] = pnp_irq(pdev, 0);
- snd_printdd("%sPnP OPL3-SA: sb port=0x%lx, wss port=0x%lx, fm port=0x%lx, midi port=0x%lx\n",
+ dev_dbg(chip->card->dev, "%sPnP OPL3-SA: sb port=0x%lx, wss port=0x%lx, fm port=0x%lx, midi port=0x%lx\n",
pnp_device_is_pnpbios(pdev) ? "BIOS" : "ISA", sb_port[dev], wss_port[dev], fm_port[dev], midi_port[dev]);
- snd_printdd("%sPnP OPL3-SA: control port=0x%lx, dma1=%i, dma2=%i, irq=%i\n",
+ dev_dbg(chip->card->dev, "%sPnP OPL3-SA: control port=0x%lx, dma1=%i, dma2=%i, irq=%i\n",
pnp_device_is_pnpbios(pdev) ? "BIOS" : "ISA", port[dev], dma1[dev], dma2[dev], irq[dev]);
return 0;
}
#endif /* CONFIG_PNP */
-static void snd_opl3sa2_free(struct snd_card *card)
-{
- struct snd_opl3sa2 *chip = card->private_data;
- if (chip->irq >= 0)
- free_irq(chip->irq, card);
- release_and_free_resource(chip->res_port);
-}
-
-static int snd_opl3sa2_card_new(int dev, struct snd_card **cardp)
+static int snd_opl3sa2_card_new(struct device *pdev, int dev,
+ struct snd_card **cardp)
{
struct snd_card *card;
struct snd_opl3sa2 *chip;
int err;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_opl3sa2), &card);
+ err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_opl3sa2), &card);
if (err < 0)
return err;
- strcpy(card->driver, "OPL3SA2");
- strcpy(card->shortname, "Yamaha OPL3-SA");
+ strscpy(card->driver, "OPL3SA2");
+ strscpy(card->shortname, "Yamaha OPL3-SA");
chip = card->private_data;
spin_lock_init(&chip->reg_lock);
chip->irq = -1;
- card->private_free = snd_opl3sa2_free;
*cardp = card;
return 0;
}
-static int __devinit snd_opl3sa2_probe(struct snd_card *card, int dev)
+static int snd_opl3sa2_probe(struct snd_card *card, int dev)
{
int xirq, xdma1, xdma2;
struct snd_opl3sa2 *chip;
@@ -657,6 +622,7 @@ static int __devinit snd_opl3sa2_probe(struct snd_card *card, int dev)
/* initialise this card from supplied (or default) parameter*/
chip = card->private_data;
+ chip->card = card;
chip->ymode = opl3sa3_ymode[dev] & 0x03 ;
chip->port = port[dev];
xirq = irq[dev];
@@ -667,23 +633,24 @@ static int __devinit snd_opl3sa2_probe(struct snd_card *card, int dev)
err = snd_opl3sa2_detect(card);
if (err < 0)
return err;
- err = request_irq(xirq, snd_opl3sa2_interrupt, IRQF_DISABLED,
- "OPL3-SA2", card);
+ err = devm_request_irq(card->dev, xirq, snd_opl3sa2_interrupt, 0,
+ "OPL3-SA2", card);
if (err) {
- snd_printk(KERN_ERR PFX "can't grab IRQ %d\n", xirq);
+ dev_err(card->dev, "can't grab IRQ %d\n", xirq);
return -ENODEV;
}
chip->irq = xirq;
+ card->sync_irq = chip->irq;
err = snd_wss_create(card,
wss_port[dev] + 4, -1,
xirq, xdma1, xdma2,
WSS_HW_OPL3SA2, WSS_HWSHARE_IRQ, &wss);
if (err < 0) {
- snd_printd("Oops, WSS not detected at 0x%lx\n", wss_port[dev] + 4);
+ dev_dbg(card->dev, "Oops, WSS not detected at 0x%lx\n", wss_port[dev] + 4);
return err;
}
chip->wss = wss;
- err = snd_wss_pcm(wss, 0, NULL);
+ err = snd_wss_pcm(wss, 0);
if (err < 0)
return err;
err = snd_wss_mixer(wss);
@@ -692,23 +659,28 @@ static int __devinit snd_opl3sa2_probe(struct snd_card *card, int dev)
err = snd_opl3sa2_mixer(card);
if (err < 0)
return err;
- err = snd_wss_timer(wss, 0, NULL);
+ err = snd_wss_timer(wss, 0);
if (err < 0)
return err;
if (fm_port[dev] >= 0x340 && fm_port[dev] < 0x400) {
- if ((err = snd_opl3_create(card, fm_port[dev],
- fm_port[dev] + 2,
- OPL3_HW_OPL3, 0, &opl3)) < 0)
+ err = snd_opl3_create(card, fm_port[dev],
+ fm_port[dev] + 2,
+ OPL3_HW_OPL3, 0, &opl3);
+ if (err < 0)
return err;
- if ((err = snd_opl3_timer_new(opl3, 1, 2)) < 0)
+ err = snd_opl3_timer_new(opl3, 1, 2);
+ if (err < 0)
return err;
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, &chip->synth)) < 0)
+ err = snd_opl3_hwdep_new(opl3, 0, 1, &chip->synth);
+ if (err < 0)
return err;
}
if (midi_port[dev] >= 0x300 && midi_port[dev] < 0x340) {
- if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_OPL3SA2,
- midi_port[dev], 0,
- xirq, 0, &chip->rmidi)) < 0)
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_OPL3SA2,
+ midi_port[dev],
+ MPU401_INFO_IRQ_HOOK, -1,
+ &chip->rmidi);
+ if (err < 0)
return err;
}
sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
@@ -720,8 +692,8 @@ static int __devinit snd_opl3sa2_probe(struct snd_card *card, int dev)
}
#ifdef CONFIG_PNP
-static int __devinit snd_opl3sa2_pnp_detect(struct pnp_dev *pdev,
- const struct pnp_device_id *id)
+static int snd_opl3sa2_pnp_detect(struct pnp_dev *pdev,
+ const struct pnp_device_id *id)
{
static int dev;
int err;
@@ -736,29 +708,20 @@ static int __devinit snd_opl3sa2_pnp_detect(struct pnp_dev *pdev,
if (dev >= SNDRV_CARDS)
return -ENODEV;
- err = snd_opl3sa2_card_new(dev, &card);
+ err = snd_opl3sa2_card_new(&pdev->dev, dev, &card);
if (err < 0)
return err;
- if ((err = snd_opl3sa2_pnp(dev, card->private_data, pdev)) < 0) {
- snd_card_free(card);
+ err = snd_opl3sa2_pnp(dev, card->private_data, pdev);
+ if (err < 0)
return err;
- }
- snd_card_set_dev(card, &pdev->dev);
- if ((err = snd_opl3sa2_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_opl3sa2_probe(card, dev);
+ if (err < 0)
return err;
- }
pnp_set_drvdata(pdev, card);
dev++;
return 0;
}
-static void __devexit snd_opl3sa2_pnp_remove(struct pnp_dev * pdev)
-{
- snd_card_free(pnp_get_drvdata(pdev));
- pnp_set_drvdata(pdev, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_opl3sa2_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
{
@@ -774,15 +737,14 @@ static struct pnp_driver opl3sa2_pnp_driver = {
.name = "snd-opl3sa2-pnpbios",
.id_table = snd_opl3sa2_pnpbiosids,
.probe = snd_opl3sa2_pnp_detect,
- .remove = __devexit_p(snd_opl3sa2_pnp_remove),
#ifdef CONFIG_PM
.suspend = snd_opl3sa2_pnp_suspend,
.resume = snd_opl3sa2_pnp_resume,
#endif
};
-static int __devinit snd_opl3sa2_pnp_cdetect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *id)
+static int snd_opl3sa2_pnp_cdetect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *id)
{
static int dev;
struct pnp_dev *pdev;
@@ -791,7 +753,7 @@ static int __devinit snd_opl3sa2_pnp_cdetect(struct pnp_card_link *pcard,
pdev = pnp_request_card_device(pcard, id->devs[0].id, NULL);
if (pdev == NULL) {
- snd_printk(KERN_ERR PFX "can't get pnp device from id '%s'\n",
+ dev_err(&pcard->card->dev, "can't get pnp device from id '%s'\n",
id->devs[0].id);
return -EBUSY;
}
@@ -802,29 +764,20 @@ static int __devinit snd_opl3sa2_pnp_cdetect(struct pnp_card_link *pcard,
if (dev >= SNDRV_CARDS)
return -ENODEV;
- err = snd_opl3sa2_card_new(dev, &card);
+ err = snd_opl3sa2_card_new(&pdev->dev, dev, &card);
if (err < 0)
return err;
- if ((err = snd_opl3sa2_pnp(dev, card->private_data, pdev)) < 0) {
- snd_card_free(card);
+ err = snd_opl3sa2_pnp(dev, card->private_data, pdev);
+ if (err < 0)
return err;
- }
- snd_card_set_dev(card, &pdev->dev);
- if ((err = snd_opl3sa2_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_opl3sa2_probe(card, dev);
+ if (err < 0)
return err;
- }
pnp_set_card_drvdata(pcard, card);
dev++;
return 0;
}
-static void __devexit snd_opl3sa2_pnp_cremove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_opl3sa2_pnp_csuspend(struct pnp_card_link *pcard, pm_message_t state)
{
@@ -841,7 +794,6 @@ static struct pnp_card_driver opl3sa2_pnpc_driver = {
.name = "snd-opl3sa2-cpnp",
.id_table = snd_opl3sa2_pnpids,
.probe = snd_opl3sa2_pnp_cdetect,
- .remove = __devexit_p(snd_opl3sa2_pnp_cremove),
#ifdef CONFIG_PM
.suspend = snd_opl3sa2_pnp_csuspend,
.resume = snd_opl3sa2_pnp_cresume,
@@ -849,8 +801,8 @@ static struct pnp_card_driver opl3sa2_pnpc_driver = {
};
#endif /* CONFIG_PNP */
-static int __devinit snd_opl3sa2_isa_match(struct device *pdev,
- unsigned int dev)
+static int snd_opl3sa2_isa_match(struct device *pdev,
+ unsigned int dev)
{
if (!enable[dev])
return 0;
@@ -859,50 +811,40 @@ static int __devinit snd_opl3sa2_isa_match(struct device *pdev,
return 0;
#endif
if (port[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR PFX "specify port\n");
+ dev_err(pdev, "specify port\n");
return 0;
}
if (wss_port[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR PFX "specify wss_port\n");
+ dev_err(pdev, "specify wss_port\n");
return 0;
}
if (fm_port[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR PFX "specify fm_port\n");
+ dev_err(pdev, "specify fm_port\n");
return 0;
}
if (midi_port[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR PFX "specify midi_port\n");
+ dev_err(pdev, "specify midi_port\n");
return 0;
}
return 1;
}
-static int __devinit snd_opl3sa2_isa_probe(struct device *pdev,
- unsigned int dev)
+static int snd_opl3sa2_isa_probe(struct device *pdev,
+ unsigned int dev)
{
struct snd_card *card;
int err;
- err = snd_opl3sa2_card_new(dev, &card);
+ err = snd_opl3sa2_card_new(pdev, dev, &card);
if (err < 0)
return err;
- snd_card_set_dev(card, pdev);
- if ((err = snd_opl3sa2_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_opl3sa2_probe(card, dev);
+ if (err < 0)
return err;
- }
dev_set_drvdata(pdev, card);
return 0;
}
-static int __devexit snd_opl3sa2_isa_remove(struct device *devptr,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
- return 0;
-}
-
#ifdef CONFIG_PM
static int snd_opl3sa2_isa_suspend(struct device *dev, unsigned int n,
pm_message_t state)
@@ -921,7 +863,6 @@ static int snd_opl3sa2_isa_resume(struct device *dev, unsigned int n)
static struct isa_driver snd_opl3sa2_isa_driver = {
.match = snd_opl3sa2_isa_match,
.probe = snd_opl3sa2_isa_probe,
- .remove = __devexit_p(snd_opl3sa2_isa_remove),
#ifdef CONFIG_PM
.suspend = snd_opl3sa2_isa_suspend,
.resume = snd_opl3sa2_isa_resume,
diff --git a/sound/isa/opti9xx/Makefile b/sound/isa/opti9xx/Makefile
index b4d894db257a..44a2fb220456 100644
--- a/sound/isa/opti9xx/Makefile
+++ b/sound/isa/opti9xx/Makefile
@@ -1,12 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-opti92x-ad1848-objs := opti92x-ad1848.o
-snd-opti92x-cs4231-objs := opti92x-cs4231.o
-snd-opti93x-objs := opti93x.o
-snd-miro-objs := miro.o
+snd-opti92x-ad1848-y := opti92x-ad1848.o
+snd-opti92x-cs4231-y := opti92x-cs4231.o
+snd-opti93x-y := opti93x.o
+snd-miro-y := miro.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opti92x-ad1848.o
diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c
index 8c24102d0d93..c320af3e9a05 100644
--- a/sound/isa/opti9xx/miro.c
+++ b/sound/isa/opti9xx/miro.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA soundcard driver for Miro miroSOUND PCM1 pro
* miroSOUND PCM12
@@ -6,20 +7,6 @@
* Copyright (C) 2004-2005 Martin Langer <martin-langer@gmx.de>
*
* Based on OSS ACI and ALSA OPTi9xx drivers
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
@@ -28,8 +15,8 @@
#include <linux/pnp.h>
#include <linux/delay.h>
#include <linux/ioport.h>
-#include <linux/moduleparam.h>
-#include <asm/io.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/wss.h>
@@ -37,6 +24,7 @@
#include <sound/opl4.h>
#include <sound/control.h>
#include <sound/info.h>
+#define SNDRV_LEGACY_FIND_FREE_IOPORT
#define SNDRV_LEGACY_FIND_FREE_IRQ
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>
@@ -45,9 +33,6 @@
MODULE_AUTHOR("Martin Langer <martin-langer@gmx.de>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Miro miroSOUND PCM1 pro, PCM12, PCM20 Radio");
-MODULE_SUPPORTED_DEVICE("{{Miro,miroSOUND PCM1 pro}, "
- "{Miro,miroSOUND PCM12}, "
- "{Miro,miroSOUND PCM20 Radio}}");
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
@@ -61,26 +46,26 @@ static int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */
static int wss;
static int ide;
#ifdef CONFIG_PNP
-static int isapnp = 1; /* Enable ISA PnP detection */
+static bool isapnp = 1; /* Enable ISA PnP detection */
#endif
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for miro soundcard.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for miro soundcard.");
-module_param(port, long, 0444);
+module_param_hw(port, long, ioport, 0444);
MODULE_PARM_DESC(port, "WSS port # for miro driver.");
-module_param(mpu_port, long, 0444);
+module_param_hw(mpu_port, long, ioport, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for miro driver.");
-module_param(fm_port, long, 0444);
+module_param_hw(fm_port, long, ioport, 0444);
MODULE_PARM_DESC(fm_port, "FM Port # for miro driver.");
-module_param(irq, int, 0444);
+module_param_hw(irq, int, irq, 0444);
MODULE_PARM_DESC(irq, "WSS irq # for miro driver.");
-module_param(mpu_irq, int, 0444);
+module_param_hw(mpu_irq, int, irq, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for miro driver.");
-module_param(dma1, int, 0444);
+module_param_hw(dma1, int, dma, 0444);
MODULE_PARM_DESC(dma1, "1st dma # for miro driver.");
-module_param(dma2, int, 0444);
+module_param_hw(dma2, int, dma, 0444);
MODULE_PARM_DESC(dma2, "2nd dma # for miro driver.");
module_param(wss, int, 0444);
MODULE_PARM_DESC(wss, "wss mode");
@@ -126,12 +111,13 @@ struct snd_miro {
long mpu_port;
int mpu_irq;
+ struct snd_card *card;
struct snd_miro_aci *aci;
};
static struct snd_miro_aci aci_device;
-static char * snd_opti9xx_names[] = {
+static const char * const snd_opti9xx_names[] = {
"unknown",
"82C928", "82C929",
"82C924", "82C925",
@@ -142,7 +128,7 @@ static int snd_miro_pnp_is_probed;
#ifdef CONFIG_PNP
-static struct pnp_card_device_id snd_miro_pnpids[] = {
+static const struct pnp_card_device_id snd_miro_pnpids[] = {
/* PCM20 and PCM12 in PnP mode */
{ .id = "MIR0924",
.devs = { { "MIR0000" }, { "MIR0002" }, { "MIR0005" } }, },
@@ -166,8 +152,9 @@ static int aci_busy_wait(struct snd_miro_aci *aci)
byte = inb(aci->aci_port + ACI_REG_BUSY);
if ((byte & 1) == 0) {
if (timeout >= ACI_MINTIME)
- snd_printd("aci ready in round %ld.\n",
- timeout-ACI_MINTIME);
+ dev_dbg(aci->card->dev,
+ "aci ready in round %ld.\n",
+ timeout-ACI_MINTIME);
return byte;
}
if (timeout >= ACI_MINTIME) {
@@ -175,10 +162,13 @@ static int aci_busy_wait(struct snd_miro_aci *aci)
switch (timeout-ACI_MINTIME) {
case 0 ... 9:
out /= 10;
+ fallthrough;
case 10 ... 19:
out /= 10;
+ fallthrough;
case 20 ... 30:
out /= 10;
+ fallthrough;
default:
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(out);
@@ -186,7 +176,7 @@ static int aci_busy_wait(struct snd_miro_aci *aci)
}
}
}
- snd_printk(KERN_ERR "aci_busy_wait() time out\n");
+ dev_err(aci->card->dev, "%s() time out\n", __func__);
return -EBUSY;
}
@@ -196,7 +186,7 @@ static inline int aci_write(struct snd_miro_aci *aci, unsigned char byte)
outb(byte, aci->aci_port + ACI_REG_COMMAND);
return 0;
} else {
- snd_printk(KERN_ERR "aci busy, aci_write(0x%x) stopped.\n", byte);
+ dev_err(aci->card->dev, "aci busy, %s(0x%x) stopped.\n", __func__, byte);
return -EBUSY;
}
}
@@ -209,7 +199,7 @@ static inline int aci_read(struct snd_miro_aci *aci)
byte = inb(aci->aci_port + ACI_REG_STATUS);
return byte;
} else {
- snd_printk(KERN_ERR "aci busy, aci_read() stopped.\n");
+ dev_err(aci->card->dev, "aci busy, %s() stopped.\n", __func__);
return -EBUSY;
}
}
@@ -272,8 +262,8 @@ static int snd_miro_get_capture(struct snd_kcontrol *kcontrol,
value = aci_getvalue(miro->aci, ACI_S_GENERAL);
if (value < 0) {
- snd_printk(KERN_ERR "snd_miro_get_capture() failed: %d\n",
- value);
+ dev_err(miro->card->dev, "%s() failed: %d\n", __func__,
+ value);
return value;
}
@@ -292,8 +282,8 @@ static int snd_miro_put_capture(struct snd_kcontrol *kcontrol,
error = aci_setvalue(miro->aci, ACI_SET_SOLOMODE, value);
if (error < 0) {
- snd_printk(KERN_ERR "snd_miro_put_capture() failed: %d\n",
- error);
+ dev_err(miro->card->dev, "%s() failed: %d\n", __func__,
+ error);
return error;
}
@@ -334,8 +324,8 @@ static int snd_miro_get_preamp(struct snd_kcontrol *kcontrol,
value = aci_getvalue(miro->aci, ACI_GET_PREAMP);
if (value < 0) {
- snd_printk(KERN_ERR "snd_miro_get_preamp() failed: %d\n",
- value);
+ dev_err(miro->card->dev, "%s() failed: %d\n", __func__,
+ value);
return value;
}
@@ -354,8 +344,8 @@ static int snd_miro_put_preamp(struct snd_kcontrol *kcontrol,
error = aci_setvalue(miro->aci, ACI_SET_PREAMP, value);
if (error < 0) {
- snd_printk(KERN_ERR "snd_miro_put_preamp() failed: %d\n",
- error);
+ dev_err(miro->card->dev, "%s() failed: %d\n", __func__,
+ error);
return error;
}
@@ -386,7 +376,8 @@ static int snd_miro_put_amp(struct snd_kcontrol *kcontrol,
error = aci_setvalue(miro->aci, ACI_SET_POWERAMP, value);
if (error < 0) {
- snd_printk(KERN_ERR "snd_miro_put_amp() to %d failed: %d\n", value, error);
+ dev_err(miro->card->dev, "%s() to %d failed: %d\n", __func__,
+ value, error);
return error;
}
@@ -442,13 +433,15 @@ static int snd_miro_get_double(struct snd_kcontrol *kcontrol,
right_val = aci_getvalue(miro->aci, right_reg);
if (right_val < 0) {
- snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", right_reg, right_val);
+ dev_err(miro->card->dev, "aci_getvalue(%d) failed: %d\n",
+ right_reg, right_val);
return right_val;
}
left_val = aci_getvalue(miro->aci, left_reg);
if (left_val < 0) {
- snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", left_reg, left_val);
+ dev_err(miro->card->dev, "aci_getvalue(%d) failed: %d\n",
+ left_reg, left_val);
return left_val;
}
@@ -501,13 +494,15 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
left_old = aci_getvalue(aci, getreg_left);
if (left_old < 0) {
- snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_left, left_old);
+ dev_err(miro->card->dev, "aci_getvalue(%d) failed: %d\n",
+ getreg_left, left_old);
return left_old;
}
right_old = aci_getvalue(aci, getreg_right);
if (right_old < 0) {
- snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_right, right_old);
+ dev_err(miro->card->dev, "aci_getvalue(%d) failed: %d\n",
+ getreg_right, right_old);
return right_old;
}
@@ -527,15 +522,15 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
if (left >= 0) {
error = aci_setvalue(aci, setreg_left, left);
if (error < 0) {
- snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
- left, error);
+ dev_err(miro->card->dev, "aci_setvalue(%d) failed: %d\n",
+ left, error);
return error;
}
} else {
error = aci_setvalue(aci, setreg_left, 0x80 - left);
if (error < 0) {
- snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
- 0x80 - left, error);
+ dev_err(miro->card->dev, "aci_setvalue(%d) failed: %d\n",
+ 0x80 - left, error);
return error;
}
}
@@ -543,15 +538,15 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
if (right >= 0) {
error = aci_setvalue(aci, setreg_right, right);
if (error < 0) {
- snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
- right, error);
+ dev_err(miro->card->dev, "aci_setvalue(%d) failed: %d\n",
+ right, error);
return error;
}
} else {
error = aci_setvalue(aci, setreg_right, 0x80 - right);
if (error < 0) {
- snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
- 0x80 - right, error);
+ dev_err(miro->card->dev, "aci_setvalue(%d) failed: %d\n",
+ 0x80 - right, error);
return error;
}
}
@@ -569,14 +564,14 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
error = aci_setvalue(aci, setreg_left, 0x20 - left);
if (error < 0) {
- snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
- 0x20 - left, error);
+ dev_err(miro->card->dev, "aci_setvalue(%d) failed: %d\n",
+ 0x20 - left, error);
return error;
}
error = aci_setvalue(aci, setreg_right, 0x20 - right);
if (error < 0) {
- snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
- 0x20 - right, error);
+ dev_err(miro->card->dev, "aci_setvalue(%d) failed: %d\n",
+ 0x20 - right, error);
return error;
}
}
@@ -586,7 +581,7 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_miro_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_miro_controls[] = {
MIRO_DOUBLE("Master Playback Volume", 0, ACI_GET_MASTER, ACI_SET_MASTER),
MIRO_DOUBLE("Mic Playback Volume", 1, ACI_GET_MIC, ACI_SET_MIC),
MIRO_DOUBLE("Line Playback Volume", 1, ACI_GET_LINE, ACI_SET_LINE),
@@ -598,7 +593,7 @@ MIRO_DOUBLE("Aux Playback Volume", 2, ACI_GET_LINE2, ACI_SET_LINE2),
/* Equalizer with seven bands (only PCM20)
from -12dB up to +12dB on each band */
-static struct snd_kcontrol_new snd_miro_eq_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_miro_eq_controls[] = {
MIRO_DOUBLE("Tone Control - 28 Hz", 0, ACI_GET_EQ1, ACI_SET_EQ1),
MIRO_DOUBLE("Tone Control - 160 Hz", 0, ACI_GET_EQ2, ACI_SET_EQ2),
MIRO_DOUBLE("Tone Control - 400 Hz", 0, ACI_GET_EQ3, ACI_SET_EQ3),
@@ -608,15 +603,15 @@ MIRO_DOUBLE("Tone Control - 6.3 kHz", 0, ACI_GET_EQ6, ACI_SET_EQ6),
MIRO_DOUBLE("Tone Control - 16 kHz", 0, ACI_GET_EQ7, ACI_SET_EQ7),
};
-static struct snd_kcontrol_new snd_miro_radio_control[] __devinitdata = {
+static const struct snd_kcontrol_new snd_miro_radio_control[] = {
MIRO_DOUBLE("Radio Playback Volume", 0, ACI_GET_LINE1, ACI_SET_LINE1),
};
-static struct snd_kcontrol_new snd_miro_line_control[] __devinitdata = {
+static const struct snd_kcontrol_new snd_miro_line_control[] = {
MIRO_DOUBLE("Line Playback Volume", 2, ACI_GET_LINE1, ACI_SET_LINE1),
};
-static struct snd_kcontrol_new snd_miro_preamp_control[] __devinitdata = {
+static const struct snd_kcontrol_new snd_miro_preamp_control[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mic Boost",
@@ -626,7 +621,7 @@ static struct snd_kcontrol_new snd_miro_preamp_control[] __devinitdata = {
.put = snd_miro_put_preamp,
}};
-static struct snd_kcontrol_new snd_miro_amp_control[] __devinitdata = {
+static const struct snd_kcontrol_new snd_miro_amp_control[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line Boost",
@@ -636,7 +631,7 @@ static struct snd_kcontrol_new snd_miro_amp_control[] __devinitdata = {
.put = snd_miro_put_amp,
}};
-static struct snd_kcontrol_new snd_miro_capture_control[] __devinitdata = {
+static const struct snd_kcontrol_new snd_miro_capture_control[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Capture Switch",
@@ -646,7 +641,7 @@ static struct snd_kcontrol_new snd_miro_capture_control[] __devinitdata = {
.put = snd_miro_put_capture,
}};
-static unsigned char aci_init_values[][2] __devinitdata = {
+static const unsigned char aci_init_values[][2] = {
{ ACI_SET_MUTE, 0x00 },
{ ACI_SET_POWERAMP, 0x00 },
{ ACI_SET_PREAMP, 0x00 },
@@ -669,7 +664,7 @@ static unsigned char aci_init_values[][2] __devinitdata = {
{ ACI_SET_MASTER + 1, 0x20 },
};
-static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
+static int snd_set_aci_init_values(struct snd_miro *miro)
{
int idx, error;
struct snd_miro_aci *aci = miro->aci;
@@ -679,7 +674,7 @@ static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
if ((aci->aci_product == 'A') && wss) {
error = aci_setvalue(aci, ACI_SET_WSS, wss);
if (error < 0) {
- snd_printk(KERN_ERR "enabling WSS mode failed\n");
+ dev_err(miro->card->dev, "enabling WSS mode failed\n");
return error;
}
}
@@ -689,7 +684,7 @@ static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
if (ide) {
error = aci_setvalue(aci, ACI_SET_IDE, ide);
if (error < 0) {
- snd_printk(KERN_ERR "enabling IDE port failed\n");
+ dev_err(miro->card->dev, "enabling IDE port failed\n");
return error;
}
}
@@ -700,8 +695,8 @@ static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
error = aci_setvalue(aci, aci_init_values[idx][0],
aci_init_values[idx][1]);
if (error < 0) {
- snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
- aci_init_values[idx][0], error);
+ dev_err(miro->card->dev, "aci_setvalue(%d) failed: %d\n",
+ aci_init_values[idx][0], error);
return error;
}
}
@@ -712,8 +707,8 @@ static int __devinit snd_set_aci_init_values(struct snd_miro *miro)
return 0;
}
-static int __devinit snd_miro_mixer(struct snd_card *card,
- struct snd_miro *miro)
+static int snd_miro_mixer(struct snd_card *card,
+ struct snd_miro *miro)
{
unsigned int idx;
int err;
@@ -723,10 +718,10 @@ static int __devinit snd_miro_mixer(struct snd_card *card,
switch (miro->hardware) {
case OPTi9XX_HW_82C924:
- strcpy(card->mixername, "ACI & OPTi924");
+ strscpy(card->mixername, "ACI & OPTi924");
break;
case OPTi9XX_HW_82C929:
- strcpy(card->mixername, "ACI & OPTi929");
+ strscpy(card->mixername, "ACI & OPTi929");
break;
default:
snd_BUG();
@@ -734,35 +729,43 @@ static int __devinit snd_miro_mixer(struct snd_card *card,
}
for (idx = 0; idx < ARRAY_SIZE(snd_miro_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_controls[idx], miro))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_controls[idx], miro));
+ if (err < 0)
return err;
}
if ((miro->aci->aci_product == 'A') ||
(miro->aci->aci_product == 'B')) {
/* PCM1/PCM12 with power-amp and Line 2 */
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_line_control[0], miro))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_line_control[0], miro));
+ if (err < 0)
return err;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_amp_control[0], miro))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_amp_control[0], miro));
+ if (err < 0)
return err;
}
if ((miro->aci->aci_product == 'B') ||
(miro->aci->aci_product == 'C')) {
/* PCM12/PCM20 with mic-preamp */
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_preamp_control[0], miro))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_preamp_control[0], miro));
+ if (err < 0)
return err;
- if (miro->aci->aci_version >= 176)
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_capture_control[0], miro))) < 0)
+ if (miro->aci->aci_version >= 176) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_capture_control[0], miro));
+ if (err < 0)
return err;
+ }
}
if (miro->aci->aci_product == 'C') {
/* PCM20 with radio and 7 band equalizer */
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_radio_control[0], miro))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_radio_control[0], miro));
+ if (err < 0)
return err;
for (idx = 0; idx < ARRAY_SIZE(snd_miro_eq_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_eq_controls[idx], miro))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_eq_controls[idx], miro));
+ if (err < 0)
return err;
}
}
@@ -770,27 +773,13 @@ static int __devinit snd_miro_mixer(struct snd_card *card,
return 0;
}
-static long snd_legacy_find_free_ioport(long *port_table, long size)
-{
- while (*port_table != -1) {
- struct resource *res;
- if ((res = request_region(*port_table, size,
- "ALSA test")) != NULL) {
- release_and_free_resource(res);
- return *port_table;
- }
- port_table++;
- }
- return -1;
-}
-
-static int __devinit snd_miro_init(struct snd_miro *chip,
- unsigned short hardware)
+static int snd_miro_init(struct snd_miro *chip,
+ unsigned short hardware)
{
- static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
+ static const int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
chip->hardware = hardware;
- strcpy(chip->name, snd_opti9xx_names[hardware]);
+ strscpy(chip->name, snd_opti9xx_names[hardware]);
chip->mc_base_size = opti9xx_mc_size[hardware];
@@ -823,7 +812,7 @@ static int __devinit snd_miro_init(struct snd_miro *chip,
break;
default:
- snd_printk(KERN_ERR "sorry, no support for %d\n", hardware);
+ dev_err(chip->card->dev, "sorry, no support for %d\n", hardware);
return -ENODEV;
}
@@ -833,10 +822,9 @@ static int __devinit snd_miro_init(struct snd_miro *chip,
static unsigned char snd_miro_read(struct snd_miro *chip,
unsigned char reg)
{
- unsigned long flags;
unsigned char retval = 0xff;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
outb(chip->password, chip->mc_base + chip->pwd_reg);
switch (chip->hardware) {
@@ -847,25 +835,23 @@ static unsigned char snd_miro_read(struct snd_miro *chip,
retval = inb(chip->mc_base + 9);
break;
}
+ fallthrough;
case OPTi9XX_HW_82C929:
retval = inb(chip->mc_base + reg);
break;
default:
- snd_printk(KERN_ERR "sorry, no support for %d\n", chip->hardware);
+ dev_err(chip->card->dev, "sorry, no support for %d\n", chip->hardware);
}
- spin_unlock_irqrestore(&chip->lock, flags);
return retval;
}
static void snd_miro_write(struct snd_miro *chip, unsigned char reg,
unsigned char value)
{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
outb(chip->password, chip->mc_base + chip->pwd_reg);
switch (chip->hardware) {
@@ -876,22 +862,24 @@ static void snd_miro_write(struct snd_miro *chip, unsigned char reg,
outb(value, chip->mc_base + 9);
break;
}
+ fallthrough;
case OPTi9XX_HW_82C929:
outb(value, chip->mc_base + reg);
break;
default:
- snd_printk(KERN_ERR "sorry, no support for %d\n", chip->hardware);
+ dev_err(chip->card->dev, "sorry, no support for %d\n", chip->hardware);
}
-
- spin_unlock_irqrestore(&chip->lock, flags);
}
+static inline void snd_miro_write_mask(struct snd_miro *chip,
+ unsigned char reg, unsigned char value, unsigned char mask)
+{
+ unsigned char oldval = snd_miro_read(chip, reg);
-#define snd_miro_write_mask(chip, reg, value, mask) \
- snd_miro_write(chip, reg, \
- (snd_miro_read(chip, reg) & ~(mask)) | ((value) & (mask)))
+ snd_miro_write(chip, reg, (oldval & ~mask) | (value & mask));
+}
/*
* Proc Interface
@@ -1002,27 +990,23 @@ static void snd_miro_proc_read(struct snd_info_entry * entry,
snd_iprintf(buffer, " preamp : 0x%x\n", aci->aci_preamp);
}
-static void __devinit snd_miro_proc_init(struct snd_card *card,
- struct snd_miro *miro)
+static void snd_miro_proc_init(struct snd_card *card,
+ struct snd_miro *miro)
{
- struct snd_info_entry *entry;
-
- if (!snd_card_proc_new(card, "miro", &entry))
- snd_info_set_text_ops(entry, miro, snd_miro_proc_read);
+ snd_card_ro_proc_new(card, "miro", miro, snd_miro_proc_read);
}
/*
* Init
*/
-static int __devinit snd_miro_configure(struct snd_miro *chip)
+static int snd_miro_configure(struct snd_miro *chip)
{
unsigned char wss_base_bits;
unsigned char irq_bits;
unsigned char dma_bits;
unsigned char mpu_port_bits = 0;
unsigned char mpu_irq_bits;
- unsigned long flags;
snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */
@@ -1038,7 +1022,7 @@ static int __devinit snd_miro_configure(struct snd_miro *chip)
snd_miro_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);
break;
default:
- snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
+ dev_err(chip->card->dev, "chip %d not supported\n", chip->hardware);
return -EINVAL;
}
@@ -1061,7 +1045,7 @@ static int __devinit snd_miro_configure(struct snd_miro *chip)
wss_base_bits = 0x02;
break;
default:
- snd_printk(KERN_ERR "WSS port 0x%lx not valid\n", chip->wss_base);
+ dev_err(chip->card->dev, "WSS port 0x%lx not valid\n", chip->wss_base);
goto __skip_base;
}
snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
@@ -1084,7 +1068,7 @@ __skip_base:
irq_bits = 0x04;
break;
default:
- snd_printk(KERN_ERR "WSS irq # %d not valid\n", chip->irq);
+ dev_err(chip->card->dev, "WSS irq # %d not valid\n", chip->irq);
goto __skip_resources;
}
@@ -1099,12 +1083,12 @@ __skip_base:
dma_bits = 0x03;
break;
default:
- snd_printk(KERN_ERR "WSS dma1 # %d not valid\n", chip->dma1);
+ dev_err(chip->card->dev, "WSS dma1 # %d not valid\n", chip->dma1);
goto __skip_resources;
}
if (chip->dma1 == chip->dma2) {
- snd_printk(KERN_ERR "don't want to share dmas\n");
+ dev_err(chip->card->dev, "don't want to share dmas\n");
return -EBUSY;
}
@@ -1113,14 +1097,14 @@ __skip_base:
case 1:
break;
default:
- snd_printk(KERN_ERR "WSS dma2 # %d not valid\n", chip->dma2);
+ dev_err(chip->card->dev, "WSS dma2 # %d not valid\n", chip->dma2);
goto __skip_resources;
}
dma_bits |= 0x04;
- spin_lock_irqsave(&chip->lock, flags);
- outb(irq_bits << 3 | dma_bits, chip->wss_base);
- spin_unlock_irqrestore(&chip->lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->lock) {
+ outb(irq_bits << 3 | dma_bits, chip->wss_base);
+ }
__skip_resources:
if (chip->hardware > OPTi9XX_HW_82C928) {
@@ -1141,8 +1125,8 @@ __skip_resources:
mpu_port_bits = 0x00;
break;
default:
- snd_printk(KERN_ERR "MPU-401 port 0x%lx not valid\n",
- chip->mpu_port);
+ dev_err(chip->card->dev, "MPU-401 port 0x%lx not valid\n",
+ chip->mpu_port);
goto __skip_mpu;
}
@@ -1160,8 +1144,8 @@ __skip_resources:
mpu_irq_bits = 0x01;
break;
default:
- snd_printk(KERN_ERR "MPU-401 irq # %d not valid\n",
- chip->mpu_irq);
+ dev_err(chip->card->dev, "MPU-401 irq # %d not valid\n",
+ chip->mpu_irq);
goto __skip_mpu;
}
@@ -1175,12 +1159,13 @@ __skip_mpu:
return 0;
}
-static int __devinit snd_miro_opti_check(struct snd_miro *chip)
+static int snd_miro_opti_check(struct snd_card *card, struct snd_miro *chip)
{
unsigned char value;
- chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size,
- "OPTi9xx MC");
+ chip->res_mc_base =
+ devm_request_region(card->dev, chip->mc_base,
+ chip->mc_base_size, "OPTi9xx MC");
if (chip->res_mc_base == NULL)
return -ENOMEM;
@@ -1189,23 +1174,24 @@ static int __devinit snd_miro_opti_check(struct snd_miro *chip)
if (value == snd_miro_read(chip, OPTi9XX_MC_REG(1)))
return 0;
- release_and_free_resource(chip->res_mc_base);
+ devm_release_resource(card->dev, chip->res_mc_base);
chip->res_mc_base = NULL;
return -ENODEV;
}
-static int __devinit snd_card_miro_detect(struct snd_card *card,
- struct snd_miro *chip)
+static int snd_card_miro_detect(struct snd_card *card,
+ struct snd_miro *chip)
{
int i, err;
for (i = OPTi9XX_HW_82C929; i <= OPTi9XX_HW_82C924; i++) {
- if ((err = snd_miro_init(chip, i)) < 0)
+ err = snd_miro_init(chip, i);
+ if (err < 0)
return err;
- err = snd_miro_opti_check(chip);
+ err = snd_miro_opti_check(card, chip);
if (err == 0)
return 1;
}
@@ -1213,8 +1199,8 @@ static int __devinit snd_card_miro_detect(struct snd_card *card,
return -ENODEV;
}
-static int __devinit snd_card_miro_aci_detect(struct snd_card *card,
- struct snd_miro *miro)
+static int snd_card_miro_aci_detect(struct snd_card *card,
+ struct snd_miro *miro)
{
unsigned char regval;
int i;
@@ -1222,6 +1208,7 @@ static int __devinit snd_card_miro_aci_detect(struct snd_card *card,
miro->aci = aci;
+ aci->card = card;
mutex_init(&aci->aci_mutex);
/* get ACI port from OPTi9xx MC 4 */
@@ -1229,78 +1216,67 @@ static int __devinit snd_card_miro_aci_detect(struct snd_card *card,
regval=inb(miro->mc_base + 4);
aci->aci_port = (regval & 0x10) ? 0x344 : 0x354;
- miro->res_aci_port = request_region(aci->aci_port, 3, "miro aci");
+ miro->res_aci_port =
+ devm_request_region(card->dev, aci->aci_port, 3, "miro aci");
if (miro->res_aci_port == NULL) {
- snd_printk(KERN_ERR "aci i/o area 0x%lx-0x%lx already used.\n",
- aci->aci_port, aci->aci_port+2);
+ dev_err(card->dev, "aci i/o area 0x%lx-0x%lx already used.\n",
+ aci->aci_port, aci->aci_port+2);
return -ENOMEM;
}
/* force ACI into a known state */
for (i = 0; i < 3; i++)
if (snd_aci_cmd(aci, ACI_ERROR_OP, -1, -1) < 0) {
- snd_printk(KERN_ERR "can't force aci into known state.\n");
+ dev_err(card->dev, "can't force aci into known state.\n");
return -ENXIO;
}
aci->aci_vendor = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1);
aci->aci_product = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1);
if (aci->aci_vendor < 0 || aci->aci_product < 0) {
- snd_printk(KERN_ERR "can't read aci id on 0x%lx.\n",
- aci->aci_port);
+ dev_err(card->dev, "can't read aci id on 0x%lx.\n",
+ aci->aci_port);
return -ENXIO;
}
aci->aci_version = snd_aci_cmd(aci, ACI_READ_VERSION, -1, -1);
if (aci->aci_version < 0) {
- snd_printk(KERN_ERR "can't read aci version on 0x%lx.\n",
- aci->aci_port);
+ dev_err(card->dev, "can't read aci version on 0x%lx.\n",
+ aci->aci_port);
return -ENXIO;
}
if (snd_aci_cmd(aci, ACI_INIT, -1, -1) < 0 ||
snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0 ||
snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0) {
- snd_printk(KERN_ERR "can't initialize aci.\n");
+ dev_err(card->dev, "can't initialize aci.\n");
return -ENXIO;
}
return 0;
}
-static void snd_card_miro_free(struct snd_card *card)
-{
- struct snd_miro *miro = card->private_data;
-
- release_and_free_resource(miro->res_aci_port);
- if (miro->aci)
- miro->aci->aci_port = 0;
- release_and_free_resource(miro->res_mc_base);
-}
-
-static int __devinit snd_miro_probe(struct snd_card *card)
+static int snd_miro_probe(struct snd_card *card)
{
int error;
struct snd_miro *miro = card->private_data;
struct snd_wss *codec;
- struct snd_timer *timer;
- struct snd_pcm *pcm;
struct snd_rawmidi *rmidi;
if (!miro->res_mc_base) {
- miro->res_mc_base = request_region(miro->mc_base,
- miro->mc_base_size,
- "miro (OPTi9xx MC)");
+ miro->res_mc_base = devm_request_region(card->dev,
+ miro->mc_base,
+ miro->mc_base_size,
+ "miro (OPTi9xx MC)");
if (miro->res_mc_base == NULL) {
- snd_printk(KERN_ERR "request for OPTI9xx MC failed\n");
+ dev_err(card->dev, "request for OPTI9xx MC failed\n");
return -ENOMEM;
}
}
error = snd_card_miro_aci_detect(card, miro);
if (error < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to detect aci chip\n");
+ dev_err(card->dev, "unable to detect aci chip\n");
return -ENODEV;
}
@@ -1324,7 +1300,7 @@ static int __devinit snd_miro_probe(struct snd_card *card)
if (error < 0)
return error;
- error = snd_wss_pcm(codec, 0, &pcm);
+ error = snd_wss_pcm(codec, 0);
if (error < 0)
return error;
@@ -1332,11 +1308,11 @@ static int __devinit snd_miro_probe(struct snd_card *card)
if (error < 0)
return error;
- error = snd_wss_timer(codec, 0, &timer);
+ error = snd_wss_timer(codec, 0);
if (error < 0)
return error;
- miro->pcm = pcm;
+ miro->pcm = codec->pcm;
error = snd_miro_mixer(card, miro);
if (error < 0)
@@ -1360,28 +1336,28 @@ static int __devinit snd_miro_probe(struct snd_card *card)
default:
sprintf(card->shortname,
"unknown miro");
- snd_printk(KERN_INFO "unknown miro aci id\n");
+ dev_info(card->dev, "unknown miro aci id\n");
break;
}
} else {
- snd_printk(KERN_INFO "found unsupported aci card\n");
+ dev_info(card->dev, "found unsupported aci card\n");
sprintf(card->shortname, "unknown Cardinal Technologies");
}
- strcpy(card->driver, "miro");
- sprintf(card->longname, "%s: OPTi%s, %s at 0x%lx, irq %d, dma %d&%d",
- card->shortname, miro->name, pcm->name, miro->wss_base + 4,
- miro->irq, miro->dma1, miro->dma2);
+ strscpy(card->driver, "miro");
+ scnprintf(card->longname, sizeof(card->longname),
+ "%s: OPTi%s, %s at 0x%lx, irq %d, dma %d&%d",
+ card->shortname, miro->name, codec->pcm->name,
+ miro->wss_base + 4, miro->irq, miro->dma1, miro->dma2);
if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT)
rmidi = NULL;
else {
error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- mpu_port, 0, miro->mpu_irq, IRQF_DISABLED,
- &rmidi);
+ mpu_port, 0, miro->mpu_irq, &rmidi);
if (error < 0)
- snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
- mpu_port);
+ dev_warn(card->dev, "no MPU-401 device at 0x%lx?\n",
+ mpu_port);
}
if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
@@ -1390,8 +1366,8 @@ static int __devinit snd_miro_probe(struct snd_card *card)
if (snd_opl4_create(card, fm_port, fm_port - 8,
2, &opl3, &opl4) < 0)
- snd_printk(KERN_WARNING "no OPL4 device at 0x%lx\n",
- fm_port);
+ dev_warn(card->dev, "no OPL4 device at 0x%lx\n",
+ fm_port);
}
error = snd_set_aci_init_values(miro);
@@ -1401,7 +1377,7 @@ static int __devinit snd_miro_probe(struct snd_card *card)
return snd_card_register(card);
}
-static int __devinit snd_miro_isa_match(struct device *devptr, unsigned int n)
+static int snd_miro_isa_match(struct device *devptr, unsigned int n)
{
#ifdef CONFIG_PNP
if (snd_miro_pnp_is_probed)
@@ -1412,40 +1388,37 @@ static int __devinit snd_miro_isa_match(struct device *devptr, unsigned int n)
return 1;
}
-static int __devinit snd_miro_isa_probe(struct device *devptr, unsigned int n)
+static int snd_miro_isa_probe(struct device *devptr, unsigned int n)
{
- static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
- static long possible_mpu_ports[] = {0x330, 0x300, 0x310, 0x320, -1};
- static int possible_irqs[] = {11, 9, 10, 7, -1};
- static int possible_mpu_irqs[] = {10, 5, 9, 7, -1};
- static int possible_dma1s[] = {3, 1, 0, -1};
- static int possible_dma2s[][2] = { {1, -1}, {0, -1}, {-1, -1},
+ static const long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
+ static const long possible_mpu_ports[] = {0x330, 0x300, 0x310, 0x320, -1};
+ static const int possible_irqs[] = {11, 9, 10, 7, -1};
+ static const int possible_mpu_irqs[] = {10, 5, 9, 7, -1};
+ static const int possible_dma1s[] = {3, 1, 0, -1};
+ static const int possible_dma2s[][2] = { {1, -1}, {0, -1}, {-1, -1},
{0, -1} };
int error;
struct snd_miro *miro;
struct snd_card *card;
- error = snd_card_create(index, id, THIS_MODULE,
- sizeof(struct snd_miro), &card);
+ error = snd_devm_card_new(devptr, index, id, THIS_MODULE,
+ sizeof(struct snd_miro), &card);
if (error < 0)
return error;
- card->private_free = snd_card_miro_free;
miro = card->private_data;
error = snd_card_miro_detect(card, miro);
if (error < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to detect OPTi9xx chip\n");
+ dev_err(card->dev, "unable to detect OPTi9xx chip\n");
return -ENODEV;
}
if (port == SNDRV_AUTO_PORT) {
port = snd_legacy_find_free_ioport(possible_ports, 4);
if (port < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free WSS port\n");
+ dev_err(card->dev, "unable to find a free WSS port\n");
return -EBUSY;
}
}
@@ -1453,9 +1426,8 @@ static int __devinit snd_miro_isa_probe(struct device *devptr, unsigned int n)
if (mpu_port == SNDRV_AUTO_PORT) {
mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2);
if (mpu_port < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR
- "unable to find a free MPU401 port\n");
+ dev_err(card->dev,
+ "unable to find a free MPU401 port\n");
return -EBUSY;
}
}
@@ -1463,63 +1435,46 @@ static int __devinit snd_miro_isa_probe(struct device *devptr, unsigned int n)
if (irq == SNDRV_AUTO_IRQ) {
irq = snd_legacy_find_free_irq(possible_irqs);
if (irq < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free IRQ\n");
+ dev_err(card->dev, "unable to find a free IRQ\n");
return -EBUSY;
}
}
if (mpu_irq == SNDRV_AUTO_IRQ) {
mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs);
if (mpu_irq < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR
- "unable to find a free MPU401 IRQ\n");
+ dev_err(card->dev,
+ "unable to find a free MPU401 IRQ\n");
return -EBUSY;
}
}
if (dma1 == SNDRV_AUTO_DMA) {
dma1 = snd_legacy_find_free_dma(possible_dma1s);
if (dma1 < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free DMA1\n");
+ dev_err(card->dev, "unable to find a free DMA1\n");
return -EBUSY;
}
}
if (dma2 == SNDRV_AUTO_DMA) {
dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4]);
if (dma2 < 0) {
- snd_card_free(card);
- snd_printk(KERN_ERR "unable to find a free DMA2\n");
+ dev_err(card->dev, "unable to find a free DMA2\n");
return -EBUSY;
}
}
- snd_card_set_dev(card, devptr);
-
error = snd_miro_probe(card);
- if (error < 0) {
- snd_card_free(card);
+ if (error < 0)
return error;
- }
dev_set_drvdata(devptr, card);
return 0;
}
-static int __devexit snd_miro_isa_remove(struct device *devptr,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
- return 0;
-}
-
#define DEV_NAME "miro"
static struct isa_driver snd_miro_driver = {
.match = snd_miro_isa_match,
.probe = snd_miro_isa_probe,
- .remove = __devexit_p(snd_miro_isa_remove),
/* FIXME: suspend/resume */
.driver = {
.name = DEV_NAME
@@ -1528,9 +1483,9 @@ static struct isa_driver snd_miro_driver = {
#ifdef CONFIG_PNP
-static int __devinit snd_card_miro_pnp(struct snd_miro *chip,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *pid)
+static int snd_card_miro_pnp(struct snd_miro *chip,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *pid)
{
struct pnp_dev *pdev;
int err;
@@ -1551,14 +1506,14 @@ static int __devinit snd_card_miro_pnp(struct snd_miro *chip,
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
+ dev_err(chip->card->dev, "AUDIO pnp configure failure: %d\n", err);
return err;
}
err = pnp_activate_dev(devmc);
if (err < 0) {
- snd_printk(KERN_ERR "MC pnp configure failure: %d\n",
- err);
+ dev_err(chip->card->dev, "MC pnp configure failure: %d\n",
+ err);
return err;
}
@@ -1579,7 +1534,7 @@ static int __devinit snd_card_miro_pnp(struct snd_miro *chip,
if (mpu_port > 0) {
err = pnp_activate_dev(devmpu);
if (err < 0) {
- snd_printk(KERN_ERR "MPU401 pnp configure failure\n");
+ dev_err(chip->card->dev, "MPU401 pnp configure failure\n");
mpu_port = -1;
return err;
}
@@ -1589,8 +1544,8 @@ static int __devinit snd_card_miro_pnp(struct snd_miro *chip,
return 0;
}
-static int __devinit snd_miro_pnp_probe(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_miro_pnp_probe(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
struct snd_card *card;
int err;
@@ -1600,49 +1555,39 @@ static int __devinit snd_miro_pnp_probe(struct pnp_card_link *pcard,
return -EBUSY;
if (!isapnp)
return -ENODEV;
- err = snd_card_create(index, id, THIS_MODULE,
+ err = snd_devm_card_new(&pcard->card->dev, index, id, THIS_MODULE,
sizeof(struct snd_miro), &card);
if (err < 0)
return err;
- card->private_free = snd_card_miro_free;
miro = card->private_data;
+ miro->card = card;
err = snd_card_miro_pnp(miro, pcard, pid);
- if (err) {
- snd_card_free(card);
+ if (err)
return err;
- }
/* only miroSOUND PCM20 and PCM12 == OPTi924 */
err = snd_miro_init(miro, OPTi9XX_HW_82C924);
- if (err) {
- snd_card_free(card);
+ if (err)
return err;
- }
- err = snd_miro_opti_check(miro);
+ err = snd_miro_opti_check(card, miro);
if (err) {
- snd_printk(KERN_ERR "OPTI chip not found\n");
- snd_card_free(card);
+ dev_err(card->dev, "OPTI chip not found\n");
return err;
}
- snd_card_set_dev(card, &pcard->card->dev);
err = snd_miro_probe(card);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
pnp_set_card_drvdata(pcard, card);
snd_miro_pnp_is_probed = 1;
return 0;
}
-static void __devexit snd_miro_pnp_remove(struct pnp_card_link * pcard)
+static void snd_miro_pnp_remove(struct pnp_card_link *pcard)
{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
snd_miro_pnp_is_probed = 0;
}
@@ -1651,7 +1596,7 @@ static struct pnp_card_driver miro_pnpc_driver = {
.name = "miro",
.id_table = snd_miro_pnpids,
.probe = snd_miro_pnp_probe,
- .remove = __devexit_p(snd_miro_pnp_remove),
+ .remove = snd_miro_pnp_remove,
};
#endif
diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c
index c35dc68930dc..abaa3ed3ab5c 100644
--- a/sound/isa/opti9xx/opti92x-ad1848.c
+++ b/sound/isa/opti9xx/opti92x-ad1848.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards.
Copyright (C) 1998-2000 by Massimo Piccioni <dafastidio@libero.it>
@@ -7,19 +8,6 @@
Thanks to Maria Grazia Pollarini, Salvatore Vassallo.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
@@ -28,8 +16,8 @@
#include <linux/isa.h>
#include <linux/delay.h>
#include <linux/pnp.h>
-#include <linux/moduleparam.h>
-#include <asm/io.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/tlv.h>
@@ -39,6 +27,7 @@
#ifndef OPTi93X
#include <sound/opl4.h>
#endif
+#define SNDRV_LEGACY_FIND_FREE_IOPORT
#define SNDRV_LEGACY_FIND_FREE_IRQ
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>
@@ -47,25 +36,19 @@ MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
MODULE_LICENSE("GPL");
#ifdef OPTi93X
MODULE_DESCRIPTION("OPTi93X");
-MODULE_SUPPORTED_DEVICE("{{OPTi,82C931/3}}");
#else /* OPTi93X */
#ifdef CS4231
MODULE_DESCRIPTION("OPTi92X - CS4231");
-MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (CS4231)},"
- "{OPTi,82C925 (CS4231)}}");
#else /* CS4231 */
MODULE_DESCRIPTION("OPTi92X - AD1848");
-MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (AD1848)},"
- "{OPTi,82C925 (AD1848)},"
- "{OAK,Mozart}}");
#endif /* CS4231 */
#endif /* OPTi93X */
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
-//static int enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */
+//static bool enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */
#ifdef CONFIG_PNP
-static int isapnp = 1; /* Enable ISA PnP detection */
+static bool isapnp = true; /* Enable ISA PnP detection */
#endif
static long port = SNDRV_DEFAULT_PORT1; /* 0x530,0xe80,0xf40,0x604 */
static long mpu_port = SNDRV_DEFAULT_PORT1; /* 0x300,0x310,0x320,0x330 */
@@ -87,20 +70,20 @@ MODULE_PARM_DESC(id, "ID string for opti9xx based soundcard.");
module_param(isapnp, bool, 0444);
MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");
#endif
-module_param(port, long, 0444);
+module_param_hw(port, long, ioport, 0444);
MODULE_PARM_DESC(port, "WSS port # for opti9xx driver.");
-module_param(mpu_port, long, 0444);
+module_param_hw(mpu_port, long, ioport, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for opti9xx driver.");
-module_param(fm_port, long, 0444);
+module_param_hw(fm_port, long, ioport, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for opti9xx driver.");
-module_param(irq, int, 0444);
+module_param_hw(irq, int, irq, 0444);
MODULE_PARM_DESC(irq, "WSS irq # for opti9xx driver.");
-module_param(mpu_irq, int, 0444);
+module_param_hw(mpu_irq, int, irq, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for opti9xx driver.");
-module_param(dma1, int, 0444);
+module_param_hw(dma1, int, dma, 0444);
MODULE_PARM_DESC(dma1, "1st dma # for opti9xx driver.");
#if defined(CS4231) || defined(OPTi93X)
-module_param(dma2, int, 0444);
+module_param_hw(dma2, int, dma, 0444);
MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver.");
#endif /* CS4231 || OPTi93X */
@@ -126,6 +109,7 @@ MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver.");
#endif /* OPTi93X */
struct snd_opti9xx {
+ struct snd_card *card;
unsigned short hardware;
unsigned char password;
char name[7];
@@ -135,10 +119,9 @@ struct snd_opti9xx {
unsigned long mc_base_size;
#ifdef OPTi93X
unsigned long mc_indir_index;
- unsigned long mc_indir_size;
struct resource *res_mc_indir;
- struct snd_wss *codec;
#endif /* OPTi93X */
+ struct snd_wss *codec;
unsigned long pwd_reg;
spinlock_t lock;
@@ -151,7 +134,7 @@ static int snd_opti9xx_pnp_is_probed;
#ifdef CONFIG_PNP
-static struct pnp_card_device_id snd_opti9xx_pnpids[] = {
+static const struct pnp_card_device_id snd_opti9xx_pnpids[] = {
#ifndef OPTi93X
/* OPTi 82C924 */
{ .id = "OPT0924",
@@ -173,39 +156,22 @@ MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids);
#endif /* CONFIG_PNP */
-#ifdef OPTi93X
-#define DEV_NAME "opti93x"
-#else
-#define DEV_NAME "opti92x"
-#endif
+#define DEV_NAME KBUILD_MODNAME
-static char * snd_opti9xx_names[] = {
+static const char * const snd_opti9xx_names[] = {
"unknown",
"82C928", "82C929",
"82C924", "82C925",
"82C930", "82C931", "82C933"
};
-
-static long __devinit snd_legacy_find_free_ioport(long *port_table, long size)
+static int snd_opti9xx_init(struct snd_opti9xx *chip,
+ unsigned short hardware)
{
- while (*port_table != -1) {
- if (request_region(*port_table, size, "ALSA test")) {
- release_region(*port_table, size);
- return *port_table;
- }
- port_table++;
- }
- return -1;
-}
-
-static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip,
- unsigned short hardware)
-{
- static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
+ static const int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
chip->hardware = hardware;
- strcpy(chip->name, snd_opti9xx_names[hardware]);
+ strscpy(chip->name, snd_opti9xx_names[hardware]);
spin_lock_init(&chip->lock);
@@ -245,17 +211,15 @@ static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip,
case OPTi9XX_HW_82C931:
case OPTi9XX_HW_82C933:
chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d;
- if (!chip->mc_indir_index) {
+ if (!chip->mc_indir_index)
chip->mc_indir_index = 0xe0e;
- chip->mc_indir_size = 2;
- }
chip->password = 0xe4;
chip->pwd_reg = 0;
break;
#endif /* OPTi93X */
default:
- snd_printk(KERN_ERR "chip %d not supported\n", hardware);
+ dev_err(chip->card->dev, "chip %d not supported\n", hardware);
return -ENODEV;
}
return 0;
@@ -264,10 +228,9 @@ static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip,
static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip,
unsigned char reg)
{
- unsigned long flags;
unsigned char retval = 0xff;
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
outb(chip->password, chip->mc_base + chip->pwd_reg);
switch (chip->hardware) {
@@ -280,6 +243,7 @@ static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip,
retval = inb(chip->mc_base + 9);
break;
}
+ fallthrough;
case OPTi9XX_HW_82C928:
case OPTi9XX_HW_82C929:
@@ -297,19 +261,16 @@ static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip,
#endif /* OPTi93X */
default:
- snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
+ dev_err(chip->card->dev, "chip %d not supported\n", chip->hardware);
}
- spin_unlock_irqrestore(&chip->lock, flags);
return retval;
}
static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
unsigned char value)
{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->lock, flags);
+ guard(spinlock_irqsave)(&chip->lock);
outb(chip->password, chip->mc_base + chip->pwd_reg);
switch (chip->hardware) {
@@ -322,6 +283,7 @@ static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
outb(value, chip->mc_base + 9);
break;
}
+ fallthrough;
case OPTi9XX_HW_82C928:
case OPTi9XX_HW_82C929:
@@ -339,19 +301,20 @@ static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
#endif /* OPTi93X */
default:
- snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
+ dev_err(chip->card->dev, "chip %d not supported\n", chip->hardware);
}
-
- spin_unlock_irqrestore(&chip->lock, flags);
}
-#define snd_opti9xx_write_mask(chip, reg, value, mask) \
- snd_opti9xx_write(chip, reg, \
- (snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask)))
+static inline void snd_opti9xx_write_mask(struct snd_opti9xx *chip,
+ unsigned char reg, unsigned char value, unsigned char mask)
+{
+ unsigned char oldval = snd_opti9xx_read(chip, reg);
+ snd_opti9xx_write(chip, reg, (oldval & ~mask) | (value & mask));
+}
-static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip,
+static int snd_opti9xx_configure(struct snd_opti9xx *chip,
long port,
int irq, int dma1, int dma2,
long mpu_port, int mpu_irq)
@@ -369,6 +332,7 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip,
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc);
/* enable wave audio */
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);
+ fallthrough;
case OPTi9XX_HW_82C925:
/* enable WSS mode */
@@ -403,6 +367,10 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip,
#else /* OPTi93X */
case OPTi9XX_HW_82C931:
+ /* disable 3D sound (set GPIO1 as output, low) */
+ snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(20), 0x04, 0x0c);
+ fallthrough;
+
case OPTi9XX_HW_82C933:
/*
* The BTC 1817DW has QS1000 wavetable which is connected
@@ -414,7 +382,9 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip,
* or digital input signal.
*/
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(26), 0x01, 0x01);
- case OPTi9XX_HW_82C930: /* FALL THROUGH */
+ fallthrough;
+
+ case OPTi9XX_HW_82C930:
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03);
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff);
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 |
@@ -425,7 +395,7 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip,
#endif /* OPTi93X */
default:
- snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
+ dev_err(chip->card->dev, "chip %d not supported\n", chip->hardware);
return -EINVAL;
}
@@ -448,7 +418,7 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip,
wss_base_bits = 0x02;
break;
default:
- snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n", port);
+ dev_warn(chip->card->dev, "WSS port 0x%lx not valid\n", port);
goto __skip_base;
}
snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
@@ -473,7 +443,7 @@ __skip_base:
irq_bits = 0x04;
break;
default:
- snd_printk(KERN_WARNING "WSS irq # %d not valid\n", irq);
+ dev_warn(chip->card->dev, "WSS irq # %d not valid\n", irq);
goto __skip_resources;
}
@@ -488,13 +458,13 @@ __skip_base:
dma_bits = 0x03;
break;
default:
- snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n", dma1);
+ dev_warn(chip->card->dev, "WSS dma1 # %d not valid\n", dma1);
goto __skip_resources;
}
#if defined(CS4231) || defined(OPTi93X)
if (dma1 == dma2) {
- snd_printk(KERN_ERR "don't want to share dmas\n");
+ dev_err(chip->card->dev, "don't want to share dmas\n");
return -EBUSY;
}
@@ -503,14 +473,14 @@ __skip_base:
case 1:
break;
default:
- snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n", dma2);
+ dev_warn(chip->card->dev, "WSS dma2 # %d not valid\n", dma2);
goto __skip_resources;
}
dma_bits |= 0x04;
#endif /* CS4231 || OPTi93X */
#ifndef OPTi93X
- outb(irq_bits << 3 | dma_bits, chip->wss_base);
+ outb(irq_bits << 3 | dma_bits, chip->wss_base);
#else /* OPTi93X */
snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits));
#endif /* OPTi93X */
@@ -534,8 +504,8 @@ __skip_resources:
mpu_port_bits = 0x00;
break;
default:
- snd_printk(KERN_WARNING
- "MPU-401 port 0x%lx not valid\n", mpu_port);
+ dev_warn(chip->card->dev,
+ "MPU-401 port 0x%lx not valid\n", mpu_port);
goto __skip_mpu;
}
@@ -553,8 +523,8 @@ __skip_resources:
mpu_irq_bits = 0x01;
break;
default:
- snd_printk(KERN_WARNING "MPU-401 irq # %d not valid\n",
- mpu_irq);
+ dev_warn(chip->card->dev, "MPU-401 irq # %d not valid\n",
+ mpu_irq);
goto __skip_mpu;
}
@@ -574,7 +544,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_step, -9300, 300, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_4bit_12db_max, -3300, 300, 0);
-static struct snd_kcontrol_new snd_opti93x_controls[] = {
+static const struct snd_kcontrol_new snd_opti93x_controls[] = {
WSS_DOUBLE("Master Playback Switch", 0,
OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
WSS_DOUBLE_TLV("Master Playback Volume", 0,
@@ -606,7 +576,7 @@ WSS_DOUBLE_TLV("Aux Playback Volume", 0,
db_scale_4bit_12db_max),
};
-static int __devinit snd_opti93x_mixer(struct snd_wss *chip)
+static int snd_opti93x_mixer(struct snd_wss *chip)
{
struct snd_card *card;
unsigned int idx;
@@ -618,35 +588,35 @@ static int __devinit snd_opti93x_mixer(struct snd_wss *chip)
card = chip->card;
- strcpy(card->mixername, chip->pcm->name);
+ strscpy(card->mixername, chip->pcm->name);
memset(&id1, 0, sizeof(id1));
memset(&id2, 0, sizeof(id2));
id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
/* reassign AUX0 switch to CD */
- strcpy(id1.name, "Aux Playback Switch");
- strcpy(id2.name, "CD Playback Switch");
+ strscpy(id1.name, "Aux Playback Switch");
+ strscpy(id2.name, "CD Playback Switch");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0) {
- snd_printk(KERN_ERR "Cannot rename opti93x control\n");
+ dev_err(card->dev, "Cannot rename opti93x control\n");
return err;
}
/* reassign AUX1 switch to FM */
- strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
- strcpy(id2.name, "FM Playback Switch");
+ strscpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+ strscpy(id2.name, "FM Playback Switch");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0) {
- snd_printk(KERN_ERR "Cannot rename opti93x control\n");
+ dev_err(card->dev, "Cannot rename opti93x control\n");
return err;
}
/* remove AUX1 volume */
- strcpy(id1.name, "Aux Playback Volume"); id1.index = 1;
+ strscpy(id1.name, "Aux Playback Volume"); id1.index = 1;
snd_ctl_remove_id(card, &id1);
/* Replace WSS volume controls with OPTi93x volume controls */
id1.index = 0;
for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) {
- strcpy(id1.name, snd_opti93x_controls[idx].name);
+ strscpy(id1.name, snd_opti93x_controls[idx].name);
snd_ctl_remove_id(card, &id1);
err = snd_ctl_add(card,
@@ -679,16 +649,15 @@ static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)
#endif /* OPTi93X */
-static int __devinit snd_opti9xx_read_check(struct snd_opti9xx *chip)
+static int snd_opti9xx_read_check(struct snd_card *card,
+ struct snd_opti9xx *chip)
{
unsigned char value;
-#ifdef OPTi93X
- unsigned long flags;
-#endif
- chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size,
- "OPTi9xx MC");
- if (chip->res_mc_base == NULL)
+ chip->res_mc_base =
+ devm_request_region(card->dev, chip->mc_base,
+ chip->mc_base_size, "OPTi9xx MC");
+ if (!chip->res_mc_base)
return -EBUSY;
#ifndef OPTi93X
value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1));
@@ -696,33 +665,33 @@ static int __devinit snd_opti9xx_read_check(struct snd_opti9xx *chip)
if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)))
return 0;
#else /* OPTi93X */
- chip->res_mc_indir = request_region(chip->mc_indir_index,
- chip->mc_indir_size,
- "OPTi93x MC");
- if (chip->res_mc_indir == NULL)
+ chip->res_mc_indir =
+ devm_request_region(card->dev, chip->mc_indir_index, 2,
+ "OPTi93x MC");
+ if (!chip->res_mc_indir)
return -EBUSY;
- spin_lock_irqsave(&chip->lock, flags);
- outb(chip->password, chip->mc_base + chip->pwd_reg);
- outb(((chip->mc_indir_index & 0x1f0) >> 4), chip->mc_base);
- spin_unlock_irqrestore(&chip->lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->lock) {
+ outb(chip->password, chip->mc_base + chip->pwd_reg);
+ outb(((chip->mc_indir_index & 0x1f0) >> 4), chip->mc_base);
+ }
value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7));
snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value);
if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value)
return 0;
- release_and_free_resource(chip->res_mc_indir);
+ devm_release_resource(card->dev, chip->res_mc_indir);
chip->res_mc_indir = NULL;
#endif /* OPTi93X */
- release_and_free_resource(chip->res_mc_base);
+ devm_release_resource(card->dev, chip->res_mc_base);
chip->res_mc_base = NULL;
return -ENODEV;
}
-static int __devinit snd_card_opti9xx_detect(struct snd_card *card,
- struct snd_opti9xx *chip)
+static int snd_card_opti9xx_detect(struct snd_card *card,
+ struct snd_opti9xx *chip)
{
int i, err;
@@ -735,7 +704,7 @@ static int __devinit snd_card_opti9xx_detect(struct snd_card *card,
if (err < 0)
return err;
- err = snd_opti9xx_read_check(chip);
+ err = snd_opti9xx_read_check(card, chip);
if (err == 0)
return 1;
#ifdef OPTi93X
@@ -746,9 +715,9 @@ static int __devinit snd_card_opti9xx_detect(struct snd_card *card,
}
#ifdef CONFIG_PNP
-static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *pid)
+static int snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *pid)
{
struct pnp_dev *pdev;
int err;
@@ -763,15 +732,16 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
+ dev_err(chip->card->dev, "AUDIO pnp configure failure: %d\n", err);
return err;
}
#ifdef OPTi93X
port = pnp_port_start(pdev, 0) - 4;
fm_port = pnp_port_start(pdev, 1) + 8;
- chip->mc_indir_index = pnp_port_start(pdev, 3) + 2;
- chip->mc_indir_size = pnp_port_len(pdev, 3) - 2;
+ /* adjust mc_indir_index - some cards report it at 0xe?d,
+ other at 0xe?c but it really is always at 0xe?e */
+ chip->mc_indir_index = (pnp_port_start(pdev, 3) & ~0xf) | 0xe;
#else
devmc = pnp_request_card_device(card, pid->devs[2].id, NULL);
if (devmc == NULL)
@@ -779,7 +749,7 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
err = pnp_activate_dev(devmc);
if (err < 0) {
- snd_printk(KERN_ERR "MC pnp configure failure: %d\n", err);
+ dev_err(chip->card->dev, "MC pnp configure failure: %d\n", err);
return err;
}
@@ -803,7 +773,7 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
if (devmpu && mpu_port > 0) {
err = pnp_activate_dev(devmpu);
if (err < 0) {
- snd_printk(KERN_ERR "MPU401 pnp configure failure\n");
+ dev_err(chip->card->dev, "MPU401 pnp configure failure\n");
mpu_port = -1;
} else {
mpu_port = pnp_port_start(devmpu, 0);
@@ -814,33 +784,13 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
}
#endif /* CONFIG_PNP */
-static void snd_card_opti9xx_free(struct snd_card *card)
-{
- struct snd_opti9xx *chip = card->private_data;
-
- if (chip) {
-#ifdef OPTi93X
- if (chip->irq > 0) {
- disable_irq(chip->irq);
- free_irq(chip->irq, chip);
- }
- release_and_free_resource(chip->res_mc_indir);
-#endif
- release_and_free_resource(chip->res_mc_base);
- }
-}
-
-static int __devinit snd_opti9xx_probe(struct snd_card *card)
+static int snd_opti9xx_probe(struct snd_card *card)
{
- static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
+ static const long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
int error;
int xdma2;
struct snd_opti9xx *chip = card->private_data;
struct snd_wss *codec;
-#ifdef CS4231
- struct snd_timer *timer;
-#endif
- struct snd_pcm *pcm;
struct snd_rawmidi *rmidi;
struct snd_hwdep *synth;
@@ -853,7 +803,7 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
if (port == SNDRV_AUTO_PORT) {
port = snd_legacy_find_free_ioport(possible_ports, 4);
if (port < 0) {
- snd_printk(KERN_ERR "unable to find a free WSS port\n");
+ dev_err(card->dev, "unable to find a free WSS port\n");
return -EBUSY;
}
}
@@ -871,10 +821,8 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
&codec);
if (error < 0)
return error;
-#ifdef OPTi93X
chip->codec = codec;
-#endif
- error = snd_wss_pcm(codec, 0, &pcm);
+ error = snd_wss_pcm(codec, 0);
if (error < 0)
return error;
error = snd_wss_mixer(codec);
@@ -886,38 +834,42 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
return error;
#endif
#ifdef CS4231
- error = snd_wss_timer(codec, 0, &timer);
+ error = snd_wss_timer(codec, 0);
if (error < 0)
return error;
#endif
#ifdef OPTi93X
- error = request_irq(irq, snd_opti93x_interrupt,
- IRQF_DISABLED, DEV_NAME" - WSS", chip);
+ error = devm_request_irq(card->dev, irq, snd_opti93x_interrupt,
+ 0, DEV_NAME" - WSS", chip);
if (error < 0) {
- snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", irq);
+ dev_err(card->dev, "opti9xx: can't grab IRQ %d\n", irq);
return error;
}
#endif
chip->irq = irq;
- strcpy(card->driver, chip->name);
+ card->sync_irq = chip->irq;
+ strscpy(card->driver, chip->name);
sprintf(card->shortname, "OPTi %s", card->driver);
#if defined(CS4231) || defined(OPTi93X)
- sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d",
- card->shortname, pcm->name,
- chip->wss_base + 4, irq, dma1, xdma2);
+ scnprintf(card->longname, sizeof(card->longname),
+ "%s, %s at 0x%lx, irq %d, dma %d&%d",
+ card->shortname, codec->pcm->name,
+ chip->wss_base + 4, irq, dma1, xdma2);
#else
- sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d",
- card->shortname, pcm->name, chip->wss_base + 4, irq, dma1);
+ scnprintf(card->longname, sizeof(card->longname),
+ "%s, %s at 0x%lx, irq %d, dma %d",
+ card->shortname, codec->pcm->name, chip->wss_base + 4, irq,
+ dma1);
#endif /* CS4231 || OPTi93X */
if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT)
rmidi = NULL;
else {
error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- mpu_port, 0, mpu_irq, IRQF_DISABLED, &rmidi);
+ mpu_port, 0, mpu_irq, &rmidi);
if (error)
- snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
- mpu_port);
+ dev_warn(card->dev, "no MPU-401 device at 0x%lx?\n",
+ mpu_port);
}
if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
@@ -940,8 +892,8 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
#endif /* !OPTi93X */
if (!opl3 && snd_opl3_create(card, fm_port, fm_port + 2,
OPL3_HW_AUTO, 0, &opl3) < 0) {
- snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n",
- fm_port, fm_port + 4 - 1);
+ dev_warn(card->dev, "no OPL device at 0x%lx-0x%lx\n",
+ fm_port, fm_port + 4 - 1);
}
if (opl3) {
error = snd_opl3_hwdep_new(opl3, 0, 1, &synth);
@@ -953,22 +905,21 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
return snd_card_register(card);
}
-static int snd_opti9xx_card_new(struct snd_card **cardp)
+static int snd_opti9xx_card_new(struct device *pdev, struct snd_card **cardp)
{
struct snd_card *card;
int err;
- err = snd_card_create(index, id, THIS_MODULE,
- sizeof(struct snd_opti9xx), &card);
+ err = snd_devm_card_new(pdev, index, id, THIS_MODULE,
+ sizeof(struct snd_opti9xx), &card);
if (err < 0)
return err;
- card->private_free = snd_card_opti9xx_free;
*cardp = card;
return 0;
}
-static int __devinit snd_opti9xx_isa_match(struct device *devptr,
- unsigned int dev)
+static int snd_opti9xx_isa_match(struct device *devptr,
+ unsigned int dev)
{
#ifdef CONFIG_PNP
if (snd_opti9xx_pnp_is_probed)
@@ -979,94 +930,131 @@ static int __devinit snd_opti9xx_isa_match(struct device *devptr,
return 1;
}
-static int __devinit snd_opti9xx_isa_probe(struct device *devptr,
- unsigned int dev)
+static int snd_opti9xx_isa_probe(struct device *devptr,
+ unsigned int dev)
{
struct snd_card *card;
int error;
- static long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1};
+ static const long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1};
#ifdef OPTi93X
- static int possible_irqs[] = {5, 9, 10, 11, 7, -1};
+ static const int possible_irqs[] = {5, 9, 10, 11, 7, -1};
#else
- static int possible_irqs[] = {9, 10, 11, 7, -1};
+ static const int possible_irqs[] = {9, 10, 11, 7, -1};
#endif /* OPTi93X */
- static int possible_mpu_irqs[] = {5, 9, 10, 7, -1};
- static int possible_dma1s[] = {3, 1, 0, -1};
+ static const int possible_mpu_irqs[] = {5, 9, 10, 7, -1};
+ static const int possible_dma1s[] = {3, 1, 0, -1};
#if defined(CS4231) || defined(OPTi93X)
- static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};
+ static const int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};
#endif /* CS4231 || OPTi93X */
if (mpu_port == SNDRV_AUTO_PORT) {
- if ((mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) {
- snd_printk(KERN_ERR "unable to find a free MPU401 port\n");
+ mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2);
+ if (mpu_port < 0) {
+ dev_err(devptr, "unable to find a free MPU401 port\n");
return -EBUSY;
}
}
if (irq == SNDRV_AUTO_IRQ) {
- if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
- snd_printk(KERN_ERR "unable to find a free IRQ\n");
+ irq = snd_legacy_find_free_irq(possible_irqs);
+ if (irq < 0) {
+ dev_err(devptr, "unable to find a free IRQ\n");
return -EBUSY;
}
}
if (mpu_irq == SNDRV_AUTO_IRQ) {
- if ((mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) {
- snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n");
+ mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs);
+ if (mpu_irq < 0) {
+ dev_err(devptr, "unable to find a free MPU401 IRQ\n");
return -EBUSY;
}
}
if (dma1 == SNDRV_AUTO_DMA) {
- if ((dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) {
- snd_printk(KERN_ERR "unable to find a free DMA1\n");
+ dma1 = snd_legacy_find_free_dma(possible_dma1s);
+ if (dma1 < 0) {
+ dev_err(devptr, "unable to find a free DMA1\n");
return -EBUSY;
}
}
#if defined(CS4231) || defined(OPTi93X)
if (dma2 == SNDRV_AUTO_DMA) {
- if ((dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4])) < 0) {
- snd_printk(KERN_ERR "unable to find a free DMA2\n");
+ dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4]);
+ if (dma2 < 0) {
+ dev_err(devptr, "unable to find a free DMA2\n");
return -EBUSY;
}
}
#endif
- error = snd_opti9xx_card_new(&card);
+ error = snd_opti9xx_card_new(devptr, &card);
if (error < 0)
return error;
- if ((error = snd_card_opti9xx_detect(card, card->private_data)) < 0) {
- snd_card_free(card);
+ error = snd_card_opti9xx_detect(card, card->private_data);
+ if (error < 0)
return error;
- }
- snd_card_set_dev(card, devptr);
- if ((error = snd_opti9xx_probe(card)) < 0) {
- snd_card_free(card);
+ error = snd_opti9xx_probe(card);
+ if (error < 0)
return error;
- }
dev_set_drvdata(devptr, card);
return 0;
}
-static int __devexit snd_opti9xx_isa_remove(struct device *devptr,
- unsigned int dev)
+#ifdef CONFIG_PM
+static int snd_opti9xx_suspend(struct snd_card *card)
+{
+ struct snd_opti9xx *chip = card->private_data;
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ chip->codec->suspend(chip->codec);
+ return 0;
+}
+
+static int snd_opti9xx_resume(struct snd_card *card)
{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
+ struct snd_opti9xx *chip = card->private_data;
+ int error, xdma2;
+#if defined(CS4231) || defined(OPTi93X)
+ xdma2 = dma2;
+#else
+ xdma2 = -1;
+#endif
+
+ error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2,
+ mpu_port, mpu_irq);
+ if (error)
+ return error;
+ chip->codec->resume(chip->codec);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
+static int snd_opti9xx_isa_suspend(struct device *dev, unsigned int n,
+ pm_message_t state)
+{
+ return snd_opti9xx_suspend(dev_get_drvdata(dev));
+}
+
+static int snd_opti9xx_isa_resume(struct device *dev, unsigned int n)
+{
+ return snd_opti9xx_resume(dev_get_drvdata(dev));
+}
+#endif
+
static struct isa_driver snd_opti9xx_driver = {
.match = snd_opti9xx_isa_match,
.probe = snd_opti9xx_isa_probe,
- .remove = __devexit_p(snd_opti9xx_isa_remove),
- /* FIXME: suspend/resume */
+#ifdef CONFIG_PM
+ .suspend = snd_opti9xx_isa_suspend,
+ .resume = snd_opti9xx_isa_resume,
+#endif
.driver = {
.name = DEV_NAME
},
};
#ifdef CONFIG_PNP
-static int __devinit snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
struct snd_card *card;
int error, hw;
@@ -1076,10 +1064,11 @@ static int __devinit snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
return -EBUSY;
if (! isapnp)
return -ENODEV;
- error = snd_opti9xx_card_new(&card);
+ error = snd_opti9xx_card_new(&pcard->card->dev, &card);
if (error < 0)
return error;
chip = card->private_data;
+ chip->card = card;
hw = snd_card_opti9xx_pnp(chip, pcard, pid);
switch (hw) {
@@ -1093,43 +1082,53 @@ static int __devinit snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
hw = OPTi9XX_HW_82C931;
break;
default:
- snd_card_free(card);
return -ENODEV;
}
- if ((error = snd_opti9xx_init(chip, hw))) {
- snd_card_free(card);
+ error = snd_opti9xx_init(chip, hw);
+ if (error)
return error;
- }
- error = snd_opti9xx_read_check(chip);
+ error = snd_opti9xx_read_check(card, chip);
if (error) {
- snd_printk(KERN_ERR "OPTI chip not found\n");
- snd_card_free(card);
+ dev_err(card->dev, "OPTI chip not found\n");
return error;
}
- snd_card_set_dev(card, &pcard->card->dev);
- if ((error = snd_opti9xx_probe(card)) < 0) {
- snd_card_free(card);
+ error = snd_opti9xx_probe(card);
+ if (error < 0)
return error;
- }
pnp_set_card_drvdata(pcard, card);
snd_opti9xx_pnp_is_probed = 1;
return 0;
}
-static void __devexit snd_opti9xx_pnp_remove(struct pnp_card_link * pcard)
+static void snd_opti9xx_pnp_remove(struct pnp_card_link *pcard)
{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
snd_opti9xx_pnp_is_probed = 0;
}
+#ifdef CONFIG_PM
+static int snd_opti9xx_pnp_suspend(struct pnp_card_link *pcard,
+ pm_message_t state)
+{
+ return snd_opti9xx_suspend(pnp_get_card_drvdata(pcard));
+}
+
+static int snd_opti9xx_pnp_resume(struct pnp_card_link *pcard)
+{
+ return snd_opti9xx_resume(pnp_get_card_drvdata(pcard));
+}
+#endif
+
static struct pnp_card_driver opti9xx_pnpc_driver = {
.flags = PNP_DRIVER_RES_DISABLE,
- .name = "opti9xx",
+ .name = DEV_NAME,
.id_table = snd_opti9xx_pnpids,
.probe = snd_opti9xx_pnp_probe,
- .remove = __devexit_p(snd_opti9xx_pnp_remove),
+ .remove = snd_opti9xx_pnp_remove,
+#ifdef CONFIG_PM
+ .suspend = snd_opti9xx_pnp_suspend,
+ .resume = snd_opti9xx_pnp_resume,
+#endif
};
#endif
diff --git a/sound/isa/sb/Makefile b/sound/isa/sb/Makefile
index 08b9fb974658..96a926feb17a 100644
--- a/sound/isa/sb/Makefile
+++ b/sound/isa/sb/Makefile
@@ -1,17 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-sb-common-objs := sb_common.o sb_mixer.o
-snd-sb8-dsp-objs := sb8_main.o sb8_midi.o
-snd-sb16-dsp-objs := sb16_main.o
-snd-sb16-csp-objs := sb16_csp.o
-snd-sb8-objs := sb8.o
-snd-sb16-objs := sb16.o
-snd-sbawe-objs := sbawe.o emu8000.o
-snd-emu8000-synth-objs := emu8000_synth.o emu8000_callback.o emu8000_patch.o emu8000_pcm.o
-snd-jazz16-objs := jazz16.o
+snd-sb-common-y := sb_common.o sb_mixer.o
+snd-sb8-dsp-y := sb8_main.o sb8_midi.o
+snd-sb16-dsp-y := sb16_main.o
+snd-sb16-csp-y := sb16_csp.o
+snd-sb8-y := sb8.o
+snd-sb16-y := sb16.o
+snd-sbawe-y := sbawe.o emu8000.o
+snd-emu8000-synth-y := emu8000_synth.o emu8000_callback.o emu8000_patch.o emu8000_pcm.o
+snd-jazz16-y := jazz16.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_SB_COMMON) += snd-sb-common.o
diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c
index 0c40951b6523..12c296ee34ec 100644
--- a/sound/isa/sb/emu8000.c
+++ b/sound/isa/sb/emu8000.c
@@ -1,35 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* and (c) 1999 Steve Ratcliffe <steve@parabola.demon.co.uk>
* Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
*
* Routines for control of EMU8000 chip
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/wait.h>
-#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/ioport.h>
+#include <linux/export.h>
#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/string.h>
#include <sound/core.h>
#include <sound/emu8000.h>
#include <sound/emu8000_reg.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/init.h>
#include <sound/control.h>
#include <sound/initval.h>
@@ -46,60 +35,49 @@
/* Write a word */
void snd_emu8000_poke(struct snd_emu8000 *emu, unsigned int port, unsigned int reg, unsigned int val)
{
- unsigned long flags;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irqsave)(&emu->reg_lock);
if (reg != emu->last_reg) {
outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
emu->last_reg = reg;
}
outw((unsigned short)val, port); /* Send data */
- spin_unlock_irqrestore(&emu->reg_lock, flags);
}
/* Read a word */
unsigned short snd_emu8000_peek(struct snd_emu8000 *emu, unsigned int port, unsigned int reg)
{
- unsigned short res;
- unsigned long flags;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irqsave)(&emu->reg_lock);
if (reg != emu->last_reg) {
outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
emu->last_reg = reg;
}
- res = inw(port); /* Read data */
- spin_unlock_irqrestore(&emu->reg_lock, flags);
- return res;
+ return inw(port); /* Read data */
}
/* Write a double word */
void snd_emu8000_poke_dw(struct snd_emu8000 *emu, unsigned int port, unsigned int reg, unsigned int val)
{
- unsigned long flags;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irqsave)(&emu->reg_lock);
if (reg != emu->last_reg) {
outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
emu->last_reg = reg;
}
outw((unsigned short)val, port); /* Send low word of data */
outw((unsigned short)(val>>16), port+2); /* Send high word of data */
- spin_unlock_irqrestore(&emu->reg_lock, flags);
}
/* Read a double word */
unsigned int snd_emu8000_peek_dw(struct snd_emu8000 *emu, unsigned int port, unsigned int reg)
{
unsigned short low;
- unsigned int res;
- unsigned long flags;
- spin_lock_irqsave(&emu->reg_lock, flags);
+
+ guard(spinlock_irqsave)(&emu->reg_lock);
if (reg != emu->last_reg) {
outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
emu->last_reg = reg;
}
low = inw(port); /* Read low word of data */
- res = low + (inw(port+2) << 16);
- spin_unlock_irqrestore(&emu->reg_lock, flags);
- return res;
+ return low + (inw(port+2) << 16);
}
/*
@@ -130,7 +108,7 @@ snd_emu8000_dma_chan(struct snd_emu8000 *emu, int ch, int mode)
/*
*/
-static void __devinit
+static void
snd_emu8000_read_wait(struct snd_emu8000 *emu)
{
while ((EMU8000_SMALR_READ(emu) & 0x80000000) != 0) {
@@ -142,7 +120,7 @@ snd_emu8000_read_wait(struct snd_emu8000 *emu)
/*
*/
-static void __devinit
+static void
snd_emu8000_write_wait(struct snd_emu8000 *emu)
{
while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) {
@@ -155,7 +133,7 @@ snd_emu8000_write_wait(struct snd_emu8000 *emu)
/*
* detect a card at the given port
*/
-static int __devinit
+static int
snd_emu8000_detect(struct snd_emu8000 *emu)
{
/* Initialise */
@@ -172,8 +150,8 @@ snd_emu8000_detect(struct snd_emu8000 *emu)
if ((EMU8000_HWCF2_READ(emu) & 0x0003) != 0x0003)
return -ENODEV;
- snd_printdd("EMU8000 [0x%lx]: Synth chip found\n",
- emu->port1);
+ dev_dbg(emu->card->dev, "EMU8000 [0x%lx]: Synth chip found\n",
+ emu->port1);
return 0;
}
@@ -181,7 +159,7 @@ snd_emu8000_detect(struct snd_emu8000 *emu)
/*
* intiailize audio channels
*/
-static void __devinit
+static void
init_audio(struct snd_emu8000 *emu)
{
int ch;
@@ -222,7 +200,7 @@ init_audio(struct snd_emu8000 *emu)
/*
* initialize DMA address
*/
-static void __devinit
+static void
init_dma(struct snd_emu8000 *emu)
{
EMU8000_SMALR_WRITE(emu, 0);
@@ -234,7 +212,7 @@ init_dma(struct snd_emu8000 *emu)
/*
* initialization arrays; from ADIP
*/
-static unsigned short init1[128] /*__devinitdata*/ = {
+static const unsigned short init1[128] = {
0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330,
0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730,
0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30,
@@ -256,7 +234,7 @@ static unsigned short init1[128] /*__devinitdata*/ = {
0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30,
};
-static unsigned short init2[128] /*__devinitdata*/ = {
+static const unsigned short init2[128] = {
0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330,
0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730,
0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30,
@@ -278,7 +256,7 @@ static unsigned short init2[128] /*__devinitdata*/ = {
0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30,
};
-static unsigned short init3[128] /*__devinitdata*/ = {
+static const unsigned short init3[128] = {
0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254,
0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234,
@@ -300,7 +278,7 @@ static unsigned short init3[128] /*__devinitdata*/ = {
0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570,
};
-static unsigned short init4[128] /*__devinitdata*/ = {
+static const unsigned short init4[128] = {
0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254,
0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234,
@@ -326,11 +304,11 @@ static unsigned short init4[128] /*__devinitdata*/ = {
* Taken from the oss driver, not obvious from the doc how this
* is meant to work
*/
-static void __devinit
-send_array(struct snd_emu8000 *emu, unsigned short *data, int size)
+static void
+send_array(struct snd_emu8000 *emu, const unsigned short *data, int size)
{
int i;
- unsigned short *p;
+ const unsigned short *p;
p = data;
for (i = 0; i < size; i++, p++)
@@ -348,7 +326,7 @@ send_array(struct snd_emu8000 *emu, unsigned short *data, int size)
* Send initialization arrays to start up, this just follows the
* initialisation sequence in the adip.
*/
-static void __devinit
+static void
init_arrays(struct snd_emu8000 *emu)
{
send_array(emu, init1, ARRAY_SIZE(init1)/4);
@@ -370,20 +348,19 @@ init_arrays(struct snd_emu8000 *emu)
/*
* Size the onboard memory.
- * This is written so as not to need arbitary delays after the write. It
+ * This is written so as not to need arbitrary delays after the write. It
* seems that the only way to do this is to use the one channel and keep
* reallocating between read and write.
*/
-static void __devinit
+static void
size_dram(struct snd_emu8000 *emu)
{
- int i, size, detected_size;
+ int i, size;
if (emu->dram_checked)
return;
size = 0;
- detected_size = 0;
/* write out a magic number */
snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);
@@ -391,10 +368,19 @@ size_dram(struct snd_emu8000 *emu)
EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET);
EMU8000_SMLD_WRITE(emu, UNIQUE_ID1);
snd_emu8000_init_fm(emu); /* This must really be here and not 2 lines back even */
+ snd_emu8000_write_wait(emu);
- while (size < EMU8000_MAX_DRAM) {
+ /*
+ * Detect first 512 KiB. If a write succeeds at the beginning of a
+ * 512 KiB page we assume that the whole page is there.
+ */
+ EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET);
+ EMU8000_SMLD_READ(emu); /* discard stale data */
+ if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1)
+ goto skip_detect; /* No RAM */
+ snd_emu8000_read_wait(emu);
- size += 512 * 1024; /* increment 512kbytes */
+ for (size = 512 * 1024; size < EMU8000_MAX_DRAM; size += 512 * 1024) {
/* Write a unique data on the test address.
* if the address is out of range, the data is written on
@@ -416,9 +402,6 @@ size_dram(struct snd_emu8000 *emu)
EMU8000_SMLD_READ(emu); /* discard stale data */
if (EMU8000_SMLD_READ(emu) != UNIQUE_ID2)
break; /* no memory at this address */
-
- detected_size = size;
-
snd_emu8000_read_wait(emu);
/*
@@ -431,8 +414,11 @@ size_dram(struct snd_emu8000 *emu)
if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1)
break; /* we must have wrapped around */
snd_emu8000_read_wait(emu);
+
+ /* Otherwise, it's valid memory. */
}
+skip_detect:
/* wait until FULL bit in SMAxW register is false */
for (i = 0; i < 10000; i++) {
if ((EMU8000_SMALW_READ(emu) & 0x80000000) == 0)
@@ -444,10 +430,10 @@ size_dram(struct snd_emu8000 *emu)
snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_CLOSE);
snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_CLOSE);
- snd_printdd("EMU8000 [0x%lx]: %d Kb on-board memory detected\n",
- emu->port1, detected_size/1024);
+ pr_info("EMU8000 [0x%lx]: %d KiB on-board DRAM detected\n",
+ emu->port1, size/1024);
- emu->mem_size = detected_size;
+ emu->mem_size = size;
emu->dram_checked = 1;
}
@@ -459,8 +445,6 @@ size_dram(struct snd_emu8000 *emu)
/*exported*/ void
snd_emu8000_init_fm(struct snd_emu8000 *emu)
{
- unsigned long flags;
-
/* Initialize the last two channels for DRAM refresh and producing
the reverb and chorus effects for Yamaha OPL-3 synthesizer */
@@ -482,12 +466,12 @@ snd_emu8000_init_fm(struct snd_emu8000 *emu)
snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0);
- spin_lock_irqsave(&emu->reg_lock, flags);
- while (!(inw(EMU8000_PTR(emu)) & 0x1000))
- ;
- while ((inw(EMU8000_PTR(emu)) & 0x1000))
- ;
- spin_unlock_irqrestore(&emu->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &emu->reg_lock) {
+ while (!(inw(EMU8000_PTR(emu)) & 0x1000))
+ ;
+ while ((inw(EMU8000_PTR(emu)) & 0x1000))
+ ;
+ }
snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0x4828);
/* this is really odd part.. */
outb(0x3C, EMU8000_PTR(emu));
@@ -502,7 +486,7 @@ snd_emu8000_init_fm(struct snd_emu8000 *emu)
/*
* The main initialization routine.
*/
-static void __devinit
+static void
snd_emu8000_init_hw(struct snd_emu8000 *emu)
{
int i;
@@ -552,7 +536,7 @@ snd_emu8000_init_hw(struct snd_emu8000 *emu)
* Bass/Treble Equalizer
*----------------------------------------------------------------*/
-static unsigned short bass_parm[12][3] = {
+static const unsigned short bass_parm[12][3] = {
{0xD26A, 0xD36A, 0x0000}, /* -12 dB */
{0xD25B, 0xD35B, 0x0000}, /* -8 */
{0xD24C, 0xD34C, 0x0000}, /* -6 */
@@ -567,7 +551,7 @@ static unsigned short bass_parm[12][3] = {
{0xC26A, 0xC36A, 0x0002}, /* +12 dB */
};
-static unsigned short treble_parm[12][9] = {
+static const unsigned short treble_parm[12][9] = {
{0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */
{0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
{0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
@@ -656,7 +640,7 @@ snd_emu8000_load_chorus_fx(struct snd_emu8000 *emu, int mode, const void __user
{
struct soundfont_chorus_fx rec;
if (mode < SNDRV_EMU8000_CHORUS_PREDEFINED || mode >= SNDRV_EMU8000_CHORUS_NUMBERS) {
- snd_printk(KERN_WARNING "invalid chorus mode %d for uploading\n", mode);
+ dev_warn(emu->card->dev, "invalid chorus mode %d for uploading\n", mode);
return -EINVAL;
}
if (len < (long)sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec)))
@@ -784,7 +768,7 @@ snd_emu8000_load_reverb_fx(struct snd_emu8000 *emu, int mode, const void __user
struct soundfont_reverb_fx rec;
if (mode < SNDRV_EMU8000_REVERB_PREDEFINED || mode >= SNDRV_EMU8000_REVERB_NUMBERS) {
- snd_printk(KERN_WARNING "invalid reverb mode %d for uploading\n", mode);
+ dev_warn(emu->card->dev, "invalid reverb mode %d for uploading\n", mode);
return -EINVAL;
}
if (len < (long)sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec)))
@@ -841,25 +825,24 @@ static int mixer_bass_treble_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int mixer_bass_treble_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned short val1;
val1 = ucontrol->value.integer.value[0] % 12;
- spin_lock_irqsave(&emu->control_lock, flags);
- if (kcontrol->private_value) {
- change = val1 != emu->treble_level;
- emu->treble_level = val1;
- } else {
- change = val1 != emu->bass_level;
- emu->bass_level = val1;
+ scoped_guard(spinlock_irqsave, &emu->control_lock) {
+ if (kcontrol->private_value) {
+ change = val1 != emu->treble_level;
+ emu->treble_level = val1;
+ } else {
+ change = val1 != emu->bass_level;
+ emu->bass_level = val1;
+ }
}
- spin_unlock_irqrestore(&emu->control_lock, flags);
snd_emu8000_update_equalizer(emu);
return change;
}
-static struct snd_kcontrol_new mixer_bass_control =
+static const struct snd_kcontrol_new mixer_bass_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Synth Tone Control - Bass",
@@ -869,7 +852,7 @@ static struct snd_kcontrol_new mixer_bass_control =
.private_value = 0,
};
-static struct snd_kcontrol_new mixer_treble_control =
+static const struct snd_kcontrol_new mixer_treble_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Synth Tone Control - Treble",
@@ -902,21 +885,20 @@ static int mixer_chorus_reverb_get(struct snd_kcontrol *kcontrol, struct snd_ctl
static int mixer_chorus_reverb_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned short val1;
- spin_lock_irqsave(&emu->control_lock, flags);
- if (kcontrol->private_value) {
- val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_CHORUS_NUMBERS;
- change = val1 != emu->chorus_mode;
- emu->chorus_mode = val1;
- } else {
- val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_REVERB_NUMBERS;
- change = val1 != emu->reverb_mode;
- emu->reverb_mode = val1;
+ scoped_guard(spinlock_irqsave, &emu->control_lock) {
+ if (kcontrol->private_value) {
+ val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_CHORUS_NUMBERS;
+ change = val1 != emu->chorus_mode;
+ emu->chorus_mode = val1;
+ } else {
+ val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_REVERB_NUMBERS;
+ change = val1 != emu->reverb_mode;
+ emu->reverb_mode = val1;
+ }
}
- spin_unlock_irqrestore(&emu->control_lock, flags);
if (change) {
if (kcontrol->private_value)
snd_emu8000_update_chorus_mode(emu);
@@ -926,7 +908,7 @@ static int mixer_chorus_reverb_put(struct snd_kcontrol *kcontrol, struct snd_ctl
return change;
}
-static struct snd_kcontrol_new mixer_chorus_mode_control =
+static const struct snd_kcontrol_new mixer_chorus_mode_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Chorus Mode",
@@ -936,7 +918,7 @@ static struct snd_kcontrol_new mixer_chorus_mode_control =
.private_value = 1,
};
-static struct snd_kcontrol_new mixer_reverb_mode_control =
+static const struct snd_kcontrol_new mixer_reverb_mode_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Reverb Mode",
@@ -969,26 +951,25 @@ static int mixer_fm_depth_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
static int mixer_fm_depth_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned short val1;
val1 = ucontrol->value.integer.value[0] % 256;
- spin_lock_irqsave(&emu->control_lock, flags);
- if (kcontrol->private_value) {
- change = val1 != emu->fm_chorus_depth;
- emu->fm_chorus_depth = val1;
- } else {
- change = val1 != emu->fm_reverb_depth;
- emu->fm_reverb_depth = val1;
+ scoped_guard(spinlock_irqsave, &emu->control_lock) {
+ if (kcontrol->private_value) {
+ change = val1 != emu->fm_chorus_depth;
+ emu->fm_chorus_depth = val1;
+ } else {
+ change = val1 != emu->fm_reverb_depth;
+ emu->fm_reverb_depth = val1;
+ }
}
- spin_unlock_irqrestore(&emu->control_lock, flags);
if (change)
snd_emu8000_init_fm(emu);
return change;
}
-static struct snd_kcontrol_new mixer_fm_chorus_depth_control =
+static const struct snd_kcontrol_new mixer_fm_chorus_depth_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "FM Chorus Depth",
@@ -998,7 +979,7 @@ static struct snd_kcontrol_new mixer_fm_chorus_depth_control =
.private_value = 1,
};
-static struct snd_kcontrol_new mixer_fm_reverb_depth_control =
+static const struct snd_kcontrol_new mixer_fm_reverb_depth_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "FM Reverb Depth",
@@ -1009,7 +990,7 @@ static struct snd_kcontrol_new mixer_fm_reverb_depth_control =
};
-static struct snd_kcontrol_new *mixer_defs[EMU8000_NUM_CONTROLS] = {
+static const struct snd_kcontrol_new *mixer_defs[EMU8000_NUM_CONTROLS] = {
&mixer_bass_control,
&mixer_treble_control,
&mixer_chorus_mode_control,
@@ -1021,9 +1002,10 @@ static struct snd_kcontrol_new *mixer_defs[EMU8000_NUM_CONTROLS] = {
/*
* create and attach mixer elements for WaveTable treble/bass controls
*/
-static int __devinit
+static int
snd_emu8000_create_mixer(struct snd_card *card, struct snd_emu8000 *emu)
{
+ struct snd_kcontrol *kctl;
int i, err = 0;
if (snd_BUG_ON(!emu || !card))
@@ -1033,55 +1015,30 @@ snd_emu8000_create_mixer(struct snd_card *card, struct snd_emu8000 *emu)
memset(emu->controls, 0, sizeof(emu->controls));
for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
- if ((err = snd_ctl_add(card, emu->controls[i] = snd_ctl_new1(mixer_defs[i], emu))) < 0)
+ kctl = snd_ctl_new1(mixer_defs[i], emu);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
goto __error;
+ emu->controls[i] = kctl;
}
return 0;
__error:
- for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
- down_write(&card->controls_rwsem);
- if (emu->controls[i])
- snd_ctl_remove(card, emu->controls[i]);
- up_write(&card->controls_rwsem);
- }
+ for (i = 0; i < EMU8000_NUM_CONTROLS; i++)
+ snd_ctl_remove(card, emu->controls[i]);
return err;
}
-
-/*
- * free resources
- */
-static int snd_emu8000_free(struct snd_emu8000 *hw)
-{
- release_and_free_resource(hw->res_port1);
- release_and_free_resource(hw->res_port2);
- release_and_free_resource(hw->res_port3);
- kfree(hw);
- return 0;
-}
-
-/*
- */
-static int snd_emu8000_dev_free(struct snd_device *device)
-{
- struct snd_emu8000 *hw = device->device_data;
- return snd_emu8000_free(hw);
-}
-
/*
* initialize and register emu8000 synth device.
*/
-int __devinit
+int
snd_emu8000_new(struct snd_card *card, int index, long port, int seq_ports,
struct snd_seq_device **awe_ret)
{
struct snd_seq_device *awe;
struct snd_emu8000 *hw;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_emu8000_dev_free,
- };
if (awe_ret)
*awe_ret = NULL;
@@ -1089,7 +1046,7 @@ snd_emu8000_new(struct snd_card *card, int index, long port, int seq_ports,
if (seq_ports <= 0)
return 0;
- hw = kzalloc(sizeof(*hw), GFP_KERNEL);
+ hw = devm_kzalloc(card->dev, sizeof(*hw), GFP_KERNEL);
if (hw == NULL)
return -ENOMEM;
spin_lock_init(&hw->reg_lock);
@@ -1097,11 +1054,11 @@ snd_emu8000_new(struct snd_card *card, int index, long port, int seq_ports,
hw->port1 = port;
hw->port2 = port + 0x400;
hw->port3 = port + 0x800;
- if (!(hw->res_port1 = request_region(hw->port1, 4, "Emu8000-1")) ||
- !(hw->res_port2 = request_region(hw->port2, 4, "Emu8000-2")) ||
- !(hw->res_port3 = request_region(hw->port3, 4, "Emu8000-3"))) {
- snd_printk(KERN_ERR "sbawe: can't grab ports 0x%lx, 0x%lx, 0x%lx\n", hw->port1, hw->port2, hw->port3);
- snd_emu8000_free(hw);
+ if (!devm_request_region(card->dev, hw->port1, 4, "Emu8000-1") ||
+ !devm_request_region(card->dev, hw->port2, 4, "Emu8000-2") ||
+ !devm_request_region(card->dev, hw->port3, 4, "Emu8000-3")) {
+ dev_err(card->dev, "sbawe: can't grab ports 0x%lx, 0x%lx, 0x%lx\n",
+ hw->port1, hw->port2, hw->port3);
return -EBUSY;
}
hw->mem_size = 0;
@@ -1114,25 +1071,17 @@ snd_emu8000_new(struct snd_card *card, int index, long port, int seq_ports,
hw->fm_chorus_depth = 0;
hw->fm_reverb_depth = 0;
- if (snd_emu8000_detect(hw) < 0) {
- snd_emu8000_free(hw);
+ if (snd_emu8000_detect(hw) < 0)
return -ENODEV;
- }
snd_emu8000_init_hw(hw);
- if ((err = snd_emu8000_create_mixer(card, hw)) < 0) {
- snd_emu8000_free(hw);
+ err = snd_emu8000_create_mixer(card, hw);
+ if (err < 0)
return err;
- }
-
- if ((err = snd_device_new(card, SNDRV_DEV_CODEC, hw, &ops)) < 0) {
- snd_emu8000_free(hw);
- return err;
- }
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
if (snd_seq_device_new(card, index, SNDRV_SEQ_DEV_ID_EMU8000,
sizeof(struct snd_emu8000*), &awe) >= 0) {
- strcpy(awe->name, "EMU-8000");
+ strscpy(awe->name, "EMU-8000");
*(struct snd_emu8000 **)SNDRV_SEQ_DEVICE_ARGPTR(awe) = hw;
}
#else
diff --git a/sound/isa/sb/emu8000_callback.c b/sound/isa/sb/emu8000_callback.c
index 9a3c71cc2e07..7609a5b640cb 100644
--- a/sound/isa/sb/emu8000_callback.c
+++ b/sound/isa/sb/emu8000_callback.c
@@ -1,25 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* synth callback routines for the emu8000 (AWE32/64)
*
* Copyright (C) 1999 Steve Ratcliffe
* Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "emu8000_local.h"
+#include <linux/export.h>
#include <sound/asoundef.h>
/*
@@ -35,7 +23,7 @@ static void reset_voice(struct snd_emux *emu, int ch);
static void terminate_voice(struct snd_emux_voice *vp);
static void sysex(struct snd_emux *emu, char *buf, int len, int parsed,
struct snd_midi_channel_set *chset);
-#ifdef CONFIG_SND_SEQUENCER_OSS
+#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
static int oss_ioctl(struct snd_emux *emu, int cmd, int p1, int p2);
#endif
static int load_fx(struct snd_emux *emu, int type, int mode,
@@ -61,7 +49,7 @@ static void snd_emu8000_tweak_voice(struct snd_emu8000 *emu, int ch);
/*
* set up operators
*/
-static struct snd_emux_operators emu8000_ops = {
+static const struct snd_emux_operators emu8000_ops = {
.owner = THIS_MODULE,
.get_voice = get_voice,
.prepare = start_voice,
@@ -75,7 +63,7 @@ static struct snd_emux_operators emu8000_ops = {
.sample_reset = snd_emu8000_sample_reset,
.load_fx = load_fx,
.sysex = sysex,
-#ifdef CONFIG_SND_SEQUENCER_OSS
+#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
.oss_ioctl = oss_ioctl,
#endif
};
@@ -174,7 +162,7 @@ get_voice(struct snd_emux *emu, struct snd_emux_port *port)
hw = emu->hw;
for (i = 0; i < END; i++) {
- best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */;
+ best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */
best[i].voice = -1;
}
@@ -476,7 +464,7 @@ sysex(struct snd_emux *emu, char *buf, int len, int parsed, struct snd_midi_chan
}
-#ifdef CONFIG_SND_SEQUENCER_OSS
+#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
/*
* OSS ioctl callback
*/
diff --git a/sound/isa/sb/emu8000_local.h b/sound/isa/sb/emu8000_local.h
index 7e87c349272f..f90526ab1a3e 100644
--- a/sound/isa/sb/emu8000_local.h
+++ b/sound/isa/sb/emu8000_local.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __EMU8000_LOCAL_H
#define __EMU8000_LOCAL_H
/*
@@ -5,20 +6,6 @@
*
* Copyright (C) 1999 Steve Ratcliffe
* Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/wait.h>
diff --git a/sound/isa/sb/emu8000_patch.c b/sound/isa/sb/emu8000_patch.c
index c99c6078be33..d60174ec8b39 100644
--- a/sound/isa/sb/emu8000_patch.c
+++ b/sound/isa/sb/emu8000_patch.c
@@ -1,26 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Patch routines for the emu8000 (AWE32/64)
*
* Copyright (C) 1999 Steve Ratcliffe
* Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "emu8000_local.h"
-#include <asm/uaccess.h>
+
+#include <linux/sched/signal.h>
+#include <linux/uaccess.h>
#include <linux/moduleparam.h>
static int emu8000_reset_addr;
@@ -159,16 +148,6 @@ snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
if (snd_BUG_ON(!sp))
return -EINVAL;
- if (sp->v.size == 0)
- return 0;
-
- /* be sure loop points start < end */
- if (sp->v.loopstart > sp->v.loopend) {
- int tmp = sp->v.loopstart;
- sp->v.loopstart = sp->v.loopend;
- sp->v.loopend = tmp;
- }
-
/* compute true data size to be loaded */
truesize = sp->v.size;
if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
@@ -178,25 +157,18 @@ snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
sp->block = snd_util_mem_alloc(hdr, truesize * 2);
if (sp->block == NULL) {
- /*snd_printd("EMU8000: out of memory\n");*/
/* not ENOMEM (for compatibility) */
return -ENOSPC;
}
if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) {
- if (!access_ok(VERIFY_READ, data, sp->v.size))
+ if (!access_ok(data, sp->v.size))
return -EFAULT;
} else {
- if (!access_ok(VERIFY_READ, data, sp->v.size * 2))
+ if (!access_ok(data, sp->v.size * 2))
return -EFAULT;
}
- /* recalculate address offset */
- sp->v.end -= sp->v.start;
- sp->v.loopstart -= sp->v.start;
- sp->v.loopend -= sp->v.start;
- sp->v.start = 0;
-
/* dram position (in word) -- mem_offset is byte */
dram_offset = EMU8000_DRAM_OFFSET + (sp->block->offset >> 1);
dram_start = dram_offset;
@@ -205,7 +177,8 @@ snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
sp->v.truesize = truesize * 2; /* in bytes */
snd_emux_terminate_all(emu->emu);
- if ((rc = snd_emu8000_open_dma(emu, EMU8000_RAM_WRITE)) != 0)
+ rc = snd_emu8000_open_dma(emu, EMU8000_RAM_WRITE);
+ if (rc)
return rc;
/* Set the address to start writing at */
diff --git a/sound/isa/sb/emu8000_pcm.c b/sound/isa/sb/emu8000_pcm.c
index 2f85c66f8e38..656a655d618d 100644
--- a/sound/isa/sb/emu8000_pcm.c
+++ b/sound/isa/sb/emu8000_pcm.c
@@ -1,24 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* pcm emulation on emu8000 wavetable
*
* Copyright (C) 2002 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "emu8000_local.h"
+
+#include <linux/sched/signal.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/initval.h>
@@ -155,7 +144,7 @@ static int calc_rate_offset(int hz)
/*
*/
-static struct snd_pcm_hardware emu8k_pcm_hw = {
+static const struct snd_pcm_hardware emu8k_pcm_hw = {
#ifdef USE_NONINTERLEAVE
.info = SNDRV_PCM_INFO_NONINTERLEAVED,
#else
@@ -191,33 +180,34 @@ static inline int emu8k_get_curpos(struct snd_emu8k_pcm *rec, int ch)
* timer interrupt handler
* check the current position and update the period if necessary.
*/
-static void emu8k_pcm_timer_func(unsigned long data)
+static void emu8k_pcm_timer_func(struct timer_list *t)
{
- struct snd_emu8k_pcm *rec = (struct snd_emu8k_pcm *)data;
+ struct snd_emu8k_pcm *rec = timer_container_of(rec, t, timer);
int ptr, delta;
+ bool period_elapsed = false;
+
+ scoped_guard(spinlock, &rec->timer_lock) {
+ /* update the current pointer */
+ ptr = emu8k_get_curpos(rec, 0);
+ if (ptr < rec->last_ptr)
+ delta = ptr + rec->buf_size - rec->last_ptr;
+ else
+ delta = ptr - rec->last_ptr;
+ rec->period_pos += delta;
+ rec->last_ptr = ptr;
+
+ /* reprogram timer */
+ mod_timer(&rec->timer, jiffies + 1);
+
+ /* update period */
+ if (rec->period_pos >= (int)rec->period_size) {
+ rec->period_pos %= rec->period_size;
+ period_elapsed = true;
+ }
+ }
- spin_lock(&rec->timer_lock);
- /* update the current pointer */
- ptr = emu8k_get_curpos(rec, 0);
- if (ptr < rec->last_ptr)
- delta = ptr + rec->buf_size - rec->last_ptr;
- else
- delta = ptr - rec->last_ptr;
- rec->period_pos += delta;
- rec->last_ptr = ptr;
-
- /* reprogram timer */
- rec->timer.expires = jiffies + 1;
- add_timer(&rec->timer);
-
- /* update period */
- if (rec->period_pos >= (int)rec->period_size) {
- rec->period_pos %= rec->period_size;
- spin_unlock(&rec->timer_lock);
+ if (period_elapsed)
snd_pcm_period_elapsed(rec->substream);
- return;
- }
- spin_unlock(&rec->timer_lock);
}
@@ -240,9 +230,7 @@ static int emu8k_pcm_open(struct snd_pcm_substream *subs)
runtime->private_data = rec;
spin_lock_init(&rec->timer_lock);
- init_timer(&rec->timer);
- rec->timer.function = emu8k_pcm_timer_func;
- rec->timer.data = (unsigned long)rec;
+ timer_setup(&rec->timer, emu8k_pcm_timer_func, 0);
runtime->hw = emu8k_pcm_hw;
runtime->hw.buffer_bytes_max = emu->mem_size - LOOP_BLANK_SIZE * 3;
@@ -250,7 +238,7 @@ static int emu8k_pcm_open(struct snd_pcm_substream *subs)
/* use timer to update periods.. (specified in msec) */
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
- (1000000 + HZ - 1) / HZ, UINT_MAX);
+ DIV_ROUND_UP(1000000, HZ), UINT_MAX);
return 0;
}
@@ -335,7 +323,6 @@ static void setup_voice(struct snd_emu8k_pcm *rec, int ch)
*/
static void start_voice(struct snd_emu8k_pcm *rec, int ch)
{
- unsigned long flags;
struct snd_emu8000 *hw = rec->emu;
unsigned int temp, aux;
int pt = calc_pitch_target(rec->pitch);
@@ -357,13 +344,11 @@ static void start_voice(struct snd_emu8k_pcm *rec, int ch)
EMU8000_CPF_WRITE(hw, ch, pt << 16);
/* start timer */
- spin_lock_irqsave(&rec->timer_lock, flags);
+ guard(spinlock_irqsave)(&rec->timer_lock);
if (! rec->timer_running) {
- rec->timer.expires = jiffies + 1;
- add_timer(&rec->timer);
+ mod_timer(&rec->timer, jiffies + 1);
rec->timer_running = 1;
}
- spin_unlock_irqrestore(&rec->timer_lock, flags);
}
/*
@@ -371,18 +356,16 @@ static void start_voice(struct snd_emu8k_pcm *rec, int ch)
*/
static void stop_voice(struct snd_emu8k_pcm *rec, int ch)
{
- unsigned long flags;
struct snd_emu8000 *hw = rec->emu;
EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F);
/* stop timer */
- spin_lock_irqsave(&rec->timer_lock, flags);
+ guard(spinlock_irqsave)(&rec->timer_lock);
if (rec->timer_running) {
- del_timer(&rec->timer);
+ timer_delete(&rec->timer);
rec->timer_running = 0;
}
- spin_unlock_irqrestore(&rec->timer_lock, flags);
}
static int emu8k_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
@@ -424,143 +407,107 @@ do { \
return -EAGAIN;\
} while (0)
+#define GET_VAL(sval, iter) \
+ do { \
+ if (!iter) \
+ sval = 0; \
+ else if (copy_from_iter(&sval, 2, iter) != 2) \
+ return -EFAULT; \
+ } while (0)
#ifdef USE_NONINTERLEAVE
-/* copy one channel block */
-static int emu8k_transfer_block(struct snd_emu8000 *emu, int offset, unsigned short *buf, int count)
-{
- EMU8000_SMALW_WRITE(emu, offset);
- while (count > 0) {
- unsigned short sval;
- CHECK_SCHEDULER();
- if (get_user(sval, buf))
- return -EFAULT;
- EMU8000_SMLD_WRITE(emu, sval);
- buf++;
- count--;
- }
- return 0;
-}
+#define LOOP_WRITE(rec, offset, iter, count) \
+ do { \
+ struct snd_emu8000 *emu = (rec)->emu; \
+ snd_emu8000_write_wait(emu, 1); \
+ EMU8000_SMALW_WRITE(emu, offset); \
+ while (count > 0) { \
+ unsigned short sval; \
+ CHECK_SCHEDULER(); \
+ GET_VAL(sval, iter); \
+ EMU8000_SMLD_WRITE(emu, sval); \
+ count--; \
+ } \
+ } while (0)
+
+/* copy one channel block */
static int emu8k_pcm_copy(struct snd_pcm_substream *subs,
- int voice,
- snd_pcm_uframes_t pos,
- void *src,
- snd_pcm_uframes_t count)
+ int voice, unsigned long pos,
+ struct iov_iter *src, unsigned long count)
{
struct snd_emu8k_pcm *rec = subs->runtime->private_data;
- struct snd_emu8000 *emu = rec->emu;
-
- snd_emu8000_write_wait(emu, 1);
- if (voice == -1) {
- unsigned short *buf = src;
- int i, err;
- count /= rec->voices;
- for (i = 0; i < rec->voices; i++) {
- err = emu8k_transfer_block(emu, pos + rec->loop_start[i], buf, count);
- if (err < 0)
- return err;
- buf += count;
- }
- return 0;
- } else {
- return emu8k_transfer_block(emu, pos + rec->loop_start[voice], src, count);
- }
-}
-/* make a channel block silence */
-static int emu8k_silence_block(struct snd_emu8000 *emu, int offset, int count)
-{
- EMU8000_SMALW_WRITE(emu, offset);
- while (count > 0) {
- CHECK_SCHEDULER();
- EMU8000_SMLD_WRITE(emu, 0);
- count--;
- }
+ /* convert to word unit */
+ pos = (pos << 1) + rec->loop_start[voice];
+ count <<= 1;
+ LOOP_WRITE(rec, pos, src, count);
return 0;
}
+/* make a channel block silence */
static int emu8k_pcm_silence(struct snd_pcm_substream *subs,
- int voice,
- snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
+ int voice, unsigned long pos, unsigned long count)
{
struct snd_emu8k_pcm *rec = subs->runtime->private_data;
- struct snd_emu8000 *emu = rec->emu;
-
- snd_emu8000_write_wait(emu, 1);
- if (voice == -1 && rec->voices == 1)
- voice = 0;
- if (voice == -1) {
- int err;
- err = emu8k_silence_block(emu, pos + rec->loop_start[0], count / 2);
- if (err < 0)
- return err;
- return emu8k_silence_block(emu, pos + rec->loop_start[1], count / 2);
- } else {
- return emu8k_silence_block(emu, pos + rec->loop_start[voice], count);
- }
+
+ /* convert to word unit */
+ pos = (pos << 1) + rec->loop_start[voice];
+ count <<= 1;
+ LOOP_WRITE(rec, pos, NULL, count);
+ return 0;
}
#else /* interleave */
+#define LOOP_WRITE(rec, pos, iter, count) \
+ do { \
+ struct snd_emu8000 *emu = rec->emu; \
+ snd_emu8000_write_wait(emu, 1); \
+ EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]); \
+ if (rec->voices > 1) \
+ EMU8000_SMARW_WRITE(emu, pos + rec->loop_start[1]); \
+ while (count > 0) { \
+ unsigned short sval; \
+ CHECK_SCHEDULER(); \
+ GET_VAL(sval, iter); \
+ EMU8000_SMLD_WRITE(emu, sval); \
+ if (rec->voices > 1) { \
+ CHECK_SCHEDULER(); \
+ GET_VAL(sval, iter); \
+ EMU8000_SMRD_WRITE(emu, sval); \
+ } \
+ count--; \
+ } \
+ } while (0)
+
+
/*
* copy the interleaved data can be done easily by using
* DMA "left" and "right" channels on emu8k engine.
*/
static int emu8k_pcm_copy(struct snd_pcm_substream *subs,
- int voice,
- snd_pcm_uframes_t pos,
- void __user *src,
- snd_pcm_uframes_t count)
+ int voice, unsigned long pos,
+ struct iov_iter *src, unsigned long count)
{
struct snd_emu8k_pcm *rec = subs->runtime->private_data;
- struct snd_emu8000 *emu = rec->emu;
- unsigned short __user *buf = src;
- snd_emu8000_write_wait(emu, 1);
- EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]);
- if (rec->voices > 1)
- EMU8000_SMARW_WRITE(emu, pos + rec->loop_start[1]);
-
- while (count-- > 0) {
- unsigned short sval;
- CHECK_SCHEDULER();
- if (get_user(sval, buf))
- return -EFAULT;
- EMU8000_SMLD_WRITE(emu, sval);
- buf++;
- if (rec->voices > 1) {
- CHECK_SCHEDULER();
- if (get_user(sval, buf))
- return -EFAULT;
- EMU8000_SMRD_WRITE(emu, sval);
- buf++;
- }
- }
+ /* convert to frames */
+ pos = bytes_to_frames(subs->runtime, pos);
+ count = bytes_to_frames(subs->runtime, count);
+ LOOP_WRITE(rec, pos, src, count);
return 0;
}
static int emu8k_pcm_silence(struct snd_pcm_substream *subs,
- int voice,
- snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
+ int voice, unsigned long pos, unsigned long count)
{
struct snd_emu8k_pcm *rec = subs->runtime->private_data;
- struct snd_emu8000 *emu = rec->emu;
- snd_emu8000_write_wait(emu, 1);
- EMU8000_SMALW_WRITE(emu, rec->loop_start[0] + pos);
- if (rec->voices > 1)
- EMU8000_SMARW_WRITE(emu, rec->loop_start[1] + pos);
- while (count-- > 0) {
- CHECK_SCHEDULER();
- EMU8000_SMLD_WRITE(emu, 0);
- if (rec->voices > 1) {
- CHECK_SCHEDULER();
- EMU8000_SMRD_WRITE(emu, 0);
- }
- }
+ /* convert to frames */
+ pos = bytes_to_frames(subs->runtime, pos);
+ count = bytes_to_frames(subs->runtime, count);
+ LOOP_WRITE(rec, pos, NULL, count);
return 0;
}
#endif
@@ -636,7 +583,8 @@ static int emu8k_pcm_prepare(struct snd_pcm_substream *subs)
int err, i, ch;
snd_emux_terminate_all(rec->emu->emu);
- if ((err = emu8k_open_dram_for_pcm(rec->emu, rec->voices)) != 0)
+ err = emu8k_open_dram_for_pcm(rec->emu, rec->voices);
+ if (err)
return err;
rec->dram_opened = 1;
@@ -667,17 +615,16 @@ static snd_pcm_uframes_t emu8k_pcm_pointer(struct snd_pcm_substream *subs)
}
-static struct snd_pcm_ops emu8k_pcm_ops = {
+static const struct snd_pcm_ops emu8k_pcm_ops = {
.open = emu8k_pcm_open,
.close = emu8k_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = emu8k_pcm_hw_params,
.hw_free = emu8k_pcm_hw_free,
.prepare = emu8k_pcm_prepare,
.trigger = emu8k_pcm_trigger,
.pointer = emu8k_pcm_pointer,
.copy = emu8k_pcm_copy,
- .silence = emu8k_pcm_silence,
+ .fill_silence = emu8k_pcm_silence,
};
@@ -692,7 +639,8 @@ int snd_emu8000_pcm_new(struct snd_card *card, struct snd_emu8000 *emu, int inde
struct snd_pcm *pcm;
int err;
- if ((err = snd_pcm_new(card, "Emu8000 PCM", index, 1, 0, &pcm)) < 0)
+ err = snd_pcm_new(card, "Emu8000 PCM", index, 1, 0, &pcm);
+ if (err < 0)
return err;
pcm->private_data = emu;
pcm->private_free = snd_emu8000_pcm_free;
diff --git a/sound/isa/sb/emu8000_synth.c b/sound/isa/sb/emu8000_synth.c
index 0c7905c85b76..9bec85ec55b4 100644
--- a/sound/isa/sb/emu8000_synth.c
+++ b/sound/isa/sb/emu8000_synth.c
@@ -1,27 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* and (c) 1999 Steve Ratcliffe <steve@parabola.demon.co.uk>
* Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
*
* Emu8000 synth plug-in routine
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "emu8000_local.h"
#include <linux/init.h>
+#include <linux/module.h>
#include <sound/initval.h>
MODULE_AUTHOR("Takashi Iwai, Steve Ratcliffe");
@@ -33,8 +21,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;
@@ -56,7 +45,7 @@ static int snd_emu8000_new_device(struct snd_seq_device *dev)
emu->num_ports = hw->seq_ports;
if (hw->memhdr) {
- snd_printk(KERN_ERR "memhdr is already initialized!?\n");
+ dev_err(hw->card->dev, "memhdr is already initialized!?\n");
snd_util_memhdr_free(hw->memhdr);
}
hw->memhdr = snd_util_memhdr_new(hw->mem_size);
@@ -92,8 +81,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)
@@ -102,10 +92,8 @@ static int snd_emu8000_delete_device(struct snd_seq_device *dev)
hw = dev->driver_data;
if (hw->pcm)
snd_device_free(dev->card, hw->pcm);
- if (hw->emu)
- snd_emux_free(hw->emu);
- if (hw->memhdr)
- snd_util_memhdr_free(hw->memhdr);
+ snd_emux_free(hw->emu);
+ snd_util_memhdr_free(hw->memhdr);
hw->emu = NULL;
hw->memhdr = NULL;
return 0;
@@ -115,21 +103,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/isa/sb/jazz16.c b/sound/isa/sb/jazz16.c
index 8ccbcddf08e1..69d9bfb6c14c 100644
--- a/sound/isa/sb/jazz16.c
+++ b/sound/isa/sb/jazz16.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/string.h>
#include <asm/dma.h>
#include <linux/isa.h>
#include <sound/core.h>
@@ -28,15 +29,12 @@
#define PFX "jazz16: "
MODULE_DESCRIPTION("Media Vision Jazz16");
-MODULE_SUPPORTED_DEVICE("{{Media Vision ??? },"
- "{RTL,RTL3000}}");
-
MODULE_AUTHOR("Krzysztof Helt <krzysztof.h1@wp.pl>");
MODULE_LICENSE("GPL");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static unsigned long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
static unsigned long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
@@ -50,17 +48,17 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for Media Vision Jazz16 based soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Media Vision Jazz16 based soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for jazz16 driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for jazz16 driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for jazz16 driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for jazz16 driver.");
-module_param_array(dma8, int, NULL, 0444);
+module_param_hw_array(dma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma8, "DMA8 # for jazz16 driver.");
-module_param_array(dma16, int, NULL, 0444);
+module_param_hw_array(dma16, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma16, "DMA16 # for jazz16 driver.");
#define SB_JAZZ16_WAKEUP 0xaf
@@ -78,13 +76,14 @@ static irqreturn_t jazz16_interrupt(int irq, void *chip)
return snd_sb8dsp_interrupt(chip);
}
-static int __devinit jazz16_configure_ports(unsigned long port,
- unsigned long mpu_port, int idx)
+static int jazz16_configure_ports(struct snd_card *card,
+ unsigned long port,
+ unsigned long mpu_port, int idx)
{
unsigned char val;
if (!request_region(0x201, 1, "jazz16 config")) {
- snd_printk(KERN_ERR "config port region is already in use.\n");
+ dev_err(card->dev, "config port region is already in use.\n");
return -EBUSY;
}
outb(SB_JAZZ16_WAKEUP - idx, 0x201);
@@ -99,15 +98,15 @@ static int __devinit jazz16_configure_ports(unsigned long port,
return 0;
}
-static int __devinit jazz16_detect_board(unsigned long port,
- unsigned long mpu_port)
+static int jazz16_detect_board(struct snd_card *card, unsigned long port,
+ unsigned long mpu_port)
{
int err;
int val;
- struct snd_sb chip;
+ struct snd_sb chip = {};
if (!request_region(port, 0x10, "jazz16")) {
- snd_printk(KERN_ERR "I/O port region is already in use.\n");
+ dev_err(card->dev, "I/O port region is already in use.\n");
return -EBUSY;
}
/* just to call snd_sbdsp_command/reset/get_byte() */
@@ -116,7 +115,7 @@ static int __devinit jazz16_detect_board(unsigned long port,
err = snd_sbdsp_reset(&chip);
if (err < 0)
for (val = 0; val < 4; val++) {
- err = jazz16_configure_ports(port, mpu_port, val);
+ err = jazz16_configure_ports(card, port, mpu_port, val);
if (err < 0)
break;
@@ -146,8 +145,8 @@ static int __devinit jazz16_detect_board(unsigned long port,
}
snd_sbdsp_get_byte(&chip);
err = snd_sbdsp_get_byte(&chip);
- snd_printd("Media Vision Jazz16 board detected: rev 0x%x, model 0x%x\n",
- val, err);
+ dev_dbg(card->dev, "Media Vision Jazz16 board detected: rev 0x%x, model 0x%x\n",
+ val, err);
err = 0;
@@ -156,11 +155,11 @@ err_unmap:
return err;
}
-static int __devinit jazz16_configure_board(struct snd_sb *chip, int mpu_irq)
+static int jazz16_configure_board(struct snd_sb *chip, int mpu_irq)
{
- static unsigned char jazz_irq_bits[] = { 0, 0, 2, 3, 0, 1, 0, 4,
+ static const unsigned char jazz_irq_bits[] = { 0, 0, 2, 3, 0, 1, 0, 4,
0, 2, 5, 0, 0, 0, 0, 6 };
- static unsigned char jazz_dma_bits[] = { 0, 1, 0, 2, 0, 3, 0, 4 };
+ static const unsigned char jazz_dma_bits[] = { 0, 1, 0, 2, 0, 3, 0, 4 };
if (jazz_dma_bits[chip->dma8] == 0 ||
jazz_dma_bits[chip->dma16] == 0 ||
@@ -183,54 +182,54 @@ static int __devinit jazz16_configure_board(struct snd_sb *chip, int mpu_irq)
return 0;
}
-static int __devinit snd_jazz16_match(struct device *devptr, unsigned int dev)
+static int snd_jazz16_match(struct device *devptr, unsigned int dev)
{
if (!enable[dev])
return 0;
if (port[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR "please specify port\n");
+ dev_err(devptr, "please specify port\n");
return 0;
} else if (port[dev] == 0x200 || (port[dev] & ~0x270)) {
- snd_printk(KERN_ERR "incorrect port specified\n");
+ dev_err(devptr, "incorrect port specified\n");
return 0;
}
if (dma8[dev] != SNDRV_AUTO_DMA &&
dma8[dev] != 1 && dma8[dev] != 3) {
- snd_printk(KERN_ERR "dma8 must be 1 or 3\n");
+ dev_err(devptr, "dma8 must be 1 or 3\n");
return 0;
}
if (dma16[dev] != SNDRV_AUTO_DMA &&
dma16[dev] != 5 && dma16[dev] != 7) {
- snd_printk(KERN_ERR "dma16 must be 5 or 7\n");
+ dev_err(devptr, "dma16 must be 5 or 7\n");
return 0;
}
if (mpu_port[dev] != SNDRV_AUTO_PORT &&
(mpu_port[dev] & ~0x030) != 0x300) {
- snd_printk(KERN_ERR "incorrect mpu_port specified\n");
+ dev_err(devptr, "incorrect mpu_port specified\n");
return 0;
}
if (mpu_irq[dev] != SNDRV_AUTO_DMA &&
mpu_irq[dev] != 2 && mpu_irq[dev] != 3 &&
mpu_irq[dev] != 5 && mpu_irq[dev] != 7) {
- snd_printk(KERN_ERR "mpu_irq must be 2, 3, 5 or 7\n");
+ dev_err(devptr, "mpu_irq must be 2, 3, 5 or 7\n");
return 0;
}
return 1;
}
-static int __devinit snd_jazz16_probe(struct device *devptr, unsigned int dev)
+static int snd_jazz16_probe(struct device *devptr, unsigned int dev)
{
struct snd_card *card;
struct snd_card_jazz16 *jazz16;
struct snd_sb *chip;
struct snd_opl3 *opl3;
- static int possible_irqs[] = {2, 3, 5, 7, 9, 10, 15, -1};
- static int possible_dmas8[] = {1, 3, -1};
- static int possible_dmas16[] = {5, 7, -1};
+ static const int possible_irqs[] = {2, 3, 5, 7, 9, 10, 15, -1};
+ static const int possible_dmas8[] = {1, 3, -1};
+ static const int possible_dmas16[] = {5, 7, -1};
int err, xirq, xdma8, xdma16, xmpu_port, xmpu_irq;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_jazz16), &card);
+ err = snd_devm_card_new(devptr, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_jazz16), &card);
if (err < 0)
return err;
@@ -240,37 +239,34 @@ static int __devinit snd_jazz16_probe(struct device *devptr, unsigned int dev)
if (xirq == SNDRV_AUTO_IRQ) {
xirq = snd_legacy_find_free_irq(possible_irqs);
if (xirq < 0) {
- snd_printk(KERN_ERR "unable to find a free IRQ\n");
- err = -EBUSY;
- goto err_free;
+ dev_err(devptr, "unable to find a free IRQ\n");
+ return -EBUSY;
}
}
xdma8 = dma8[dev];
if (xdma8 == SNDRV_AUTO_DMA) {
xdma8 = snd_legacy_find_free_dma(possible_dmas8);
if (xdma8 < 0) {
- snd_printk(KERN_ERR "unable to find a free DMA8\n");
- err = -EBUSY;
- goto err_free;
+ dev_err(devptr, "unable to find a free DMA8\n");
+ return -EBUSY;
}
}
xdma16 = dma16[dev];
if (xdma16 == SNDRV_AUTO_DMA) {
xdma16 = snd_legacy_find_free_dma(possible_dmas16);
if (xdma16 < 0) {
- snd_printk(KERN_ERR "unable to find a free DMA16\n");
- err = -EBUSY;
- goto err_free;
+ dev_err(devptr, "unable to find a free DMA16\n");
+ return -EBUSY;
}
}
xmpu_port = mpu_port[dev];
if (xmpu_port == SNDRV_AUTO_PORT)
xmpu_port = 0;
- err = jazz16_detect_board(port[dev], xmpu_port);
+ err = jazz16_detect_board(card, port[dev], xmpu_port);
if (err < 0) {
- printk(KERN_ERR "Media Vision Jazz16 board not detected\n");
- goto err_free;
+ dev_err(devptr, "Media Vision Jazz16 board not detected\n");
+ return err;
}
err = snd_sbdsp_create(card, port[dev], irq[dev],
jazz16_interrupt,
@@ -278,41 +274,41 @@ static int __devinit snd_jazz16_probe(struct device *devptr, unsigned int dev)
SB_HW_JAZZ16,
&chip);
if (err < 0)
- goto err_free;
+ return err;
xmpu_irq = mpu_irq[dev];
if (xmpu_irq == SNDRV_AUTO_IRQ || mpu_port[dev] == SNDRV_AUTO_PORT)
xmpu_irq = 0;
err = jazz16_configure_board(chip, xmpu_irq);
if (err < 0) {
- printk(KERN_ERR "Media Vision Jazz16 configuration failed\n");
- goto err_free;
+ dev_err(devptr, "Media Vision Jazz16 configuration failed\n");
+ return err;
}
jazz16->chip = chip;
- strcpy(card->driver, "jazz16");
- strcpy(card->shortname, "Media Vision Jazz16");
+ strscpy(card->driver, "jazz16");
+ strscpy(card->shortname, "Media Vision Jazz16");
sprintf(card->longname,
"Media Vision Jazz16 at 0x%lx, irq %d, dma8 %d, dma16 %d",
port[dev], xirq, xdma8, xdma16);
- err = snd_sb8dsp_pcm(chip, 0, NULL);
+ err = snd_sb8dsp_pcm(chip, 0);
if (err < 0)
- goto err_free;
+ return err;
err = snd_sbmixer_new(chip);
if (err < 0)
- goto err_free;
+ return err;
err = snd_opl3_create(card, chip->port, chip->port + 2,
OPL3_HW_AUTO, 1, &opl3);
if (err < 0)
- snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n",
- chip->port, chip->port + 2);
+ dev_warn(devptr, "no OPL device at 0x%lx-0x%lx\n",
+ chip->port, chip->port + 2);
else {
err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
if (err < 0)
- goto err_free;
+ return err;
}
if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
if (mpu_irq[dev] == SNDRV_AUTO_IRQ)
@@ -322,33 +318,17 @@ static int __devinit snd_jazz16_probe(struct device *devptr, unsigned int dev)
MPU401_HW_MPU401,
mpu_port[dev], 0,
mpu_irq[dev],
- mpu_irq[dev] >= 0 ? IRQF_DISABLED : 0,
NULL) < 0)
- snd_printk(KERN_ERR "no MPU-401 device at 0x%lx\n",
- mpu_port[dev]);
+ dev_err(devptr, "no MPU-401 device at 0x%lx\n",
+ mpu_port[dev]);
}
- snd_card_set_dev(card, devptr);
-
err = snd_card_register(card);
if (err < 0)
- goto err_free;
+ return err;
dev_set_drvdata(devptr, card);
return 0;
-
-err_free:
- snd_card_free(card);
- return err;
-}
-
-static int __devexit snd_jazz16_remove(struct device *devptr, unsigned int dev)
-{
- struct snd_card *card = dev_get_drvdata(devptr);
-
- dev_set_drvdata(devptr, NULL);
- snd_card_free(card);
- return 0;
}
#ifdef CONFIG_PM
@@ -360,7 +340,6 @@ static int snd_jazz16_suspend(struct device *pdev, unsigned int n,
struct snd_sb *chip = acard->chip;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_sbmixer_suspend(chip);
return 0;
}
@@ -381,7 +360,6 @@ static int snd_jazz16_resume(struct device *pdev, unsigned int n)
static struct isa_driver snd_jazz16_driver = {
.match = snd_jazz16_match,
.probe = snd_jazz16_probe,
- .remove = __devexit_p(snd_jazz16_remove),
#ifdef CONFIG_PM
.suspend = snd_jazz16_suspend,
.resume = snd_jazz16_resume,
@@ -391,15 +369,4 @@ static struct isa_driver snd_jazz16_driver = {
},
};
-static int __init alsa_card_jazz16_init(void)
-{
- return isa_register_driver(&snd_jazz16_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_jazz16_exit(void)
-{
- isa_unregister_driver(&snd_jazz16_driver);
-}
-
-module_init(alsa_card_jazz16_init)
-module_exit(alsa_card_jazz16_exit)
+module_isa_driver(snd_jazz16_driver, SNDRV_CARDS);
diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c
index 4d1c5a300ff8..208d1942a015 100644
--- a/sound/isa/sb/sb16.c
+++ b/sound/isa/sb/sb16.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for SoundBlaster 16/AWE32/AWE64 soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <asm/dma.h>
@@ -24,7 +9,8 @@
#include <linux/pnp.h>
#include <linux/err.h>
#include <linux/isa.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/string.h>
#include <sound/core.h>
#include <sound/sb.h>
#include <sound/sb16_csp.h>
@@ -36,41 +22,27 @@
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>
-#ifdef SNDRV_SBAWE
-#define PFX "sbawe: "
-#else
-#define PFX "sb16: "
-#endif
-
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
#ifndef SNDRV_SBAWE
MODULE_DESCRIPTION("Sound Blaster 16");
-MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB 16},"
- "{Creative Labs,SB Vibra16S},"
- "{Creative Labs,SB Vibra16C},"
- "{Creative Labs,SB Vibra16CL},"
- "{Creative Labs,SB Vibra16X}}");
#else
MODULE_DESCRIPTION("Sound Blaster AWE");
-MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB AWE 32},"
- "{Creative Labs,SB AWE 64},"
- "{Creative Labs,SB AWE 64 Gold}}");
#endif
#if 0
#define SNDRV_DEBUG_IRQ
#endif
-#if defined(SNDRV_SBAWE) && (defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)))
+#if defined(SNDRV_SBAWE) && IS_ENABLED(CONFIG_SND_SEQUENCER)
#define SNDRV_SBAWE_EMU8000
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
#endif
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260,0x280 */
static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x330,0x300 */
@@ -99,21 +71,21 @@ MODULE_PARM_DESC(enable, "Enable SoundBlaster 16 soundcard.");
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
#endif
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for SB16 driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for SB16 driver.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for SB16 PnP driver.");
#ifdef SNDRV_SBAWE_EMU8000
-module_param_array(awe_port, long, NULL, 0444);
+module_param_hw_array(awe_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(awe_port, "AWE port # for SB16 PnP driver.");
#endif
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for SB16 driver.");
-module_param_array(dma8, int, NULL, 0444);
+module_param_hw_array(dma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma8, "8-bit DMA # for SB16 driver.");
-module_param_array(dma16, int, NULL, 0444);
+module_param_hw_array(dma16, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma16, "16-bit DMA # for SB16 driver.");
module_param_array(mic_agc, int, NULL, 0444);
MODULE_PARM_DESC(mic_agc, "Mic Auto-Gain-Control switch.");
@@ -145,7 +117,7 @@ struct snd_card_sb16 {
#ifdef CONFIG_PNP
-static struct pnp_card_device_id snd_sb16_pnpids[] = {
+static const struct pnp_card_device_id snd_sb16_pnpids[] = {
#ifndef SNDRV_SBAWE
/* Sound Blaster 16 PnP */
{ .id = "CTL0024", .devs = { { "CTL0031" } } },
@@ -250,9 +222,9 @@ MODULE_DEVICE_TABLE(pnp_card, snd_sb16_pnpids);
#ifdef CONFIG_PNP
-static int __devinit snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard,
- struct pnp_card_link *card,
- const struct pnp_card_device_id *id)
+static int snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
{
struct pnp_dev *pdev;
int err;
@@ -269,7 +241,7 @@ static int __devinit snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard,
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
+ dev_err(&pdev->dev, "AUDIO pnp configure failure\n");
return err;
}
port[dev] = pnp_port_start(pdev, 0);
@@ -278,10 +250,10 @@ static int __devinit snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard,
dma8[dev] = pnp_dma(pdev, 0);
dma16[dev] = pnp_dma(pdev, 1);
irq[dev] = pnp_irq(pdev, 0);
- snd_printdd("pnp SB16: port=0x%lx, mpu port=0x%lx, fm port=0x%lx\n",
- port[dev], mpu_port[dev], fm_port[dev]);
- snd_printdd("pnp SB16: dma1=%i, dma2=%i, irq=%i\n",
- dma8[dev], dma16[dev], irq[dev]);
+ dev_dbg(&pdev->dev, "pnp SB16: port=0x%lx, mpu port=0x%lx, fm port=0x%lx\n",
+ port[dev], mpu_port[dev], fm_port[dev]);
+ dev_dbg(&pdev->dev, "pnp SB16: dma1=%i, dma2=%i, irq=%i\n",
+ dma8[dev], dma16[dev], irq[dev]);
#ifdef SNDRV_SBAWE_EMU8000
/* WaveTable initialization */
pdev = acard->devwt;
@@ -291,13 +263,13 @@ static int __devinit snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard,
goto __wt_error;
}
awe_port[dev] = pnp_port_start(pdev, 0);
- snd_printdd("pnp SB16: wavetable port=0x%llx\n",
- (unsigned long long)pnp_port_start(pdev, 0));
+ dev_dbg(&pdev->dev, "pnp SB16: wavetable port=0x%llx\n",
+ (unsigned long long)pnp_port_start(pdev, 0));
} else {
__wt_error:
if (pdev) {
pnp_release_card_device(pdev);
- snd_printk(KERN_ERR PFX "WaveTable pnp configure failure\n");
+ dev_err(&pdev->dev, "WaveTable pnp configure failure\n");
}
acard->devwt = NULL;
awe_port[dev] = -1;
@@ -308,36 +280,27 @@ __wt_error:
#endif /* CONFIG_PNP */
-static void snd_sb16_free(struct snd_card *card)
-{
- struct snd_card_sb16 *acard = card->private_data;
-
- if (acard == NULL)
- return;
- release_and_free_resource(acard->fm_res);
-}
-
#ifdef CONFIG_PNP
#define is_isapnp_selected(dev) isapnp[dev]
#else
#define is_isapnp_selected(dev) 0
#endif
-static int snd_sb16_card_new(int dev, struct snd_card **cardp)
+static int snd_sb16_card_new(struct device *devptr, int dev,
+ struct snd_card **cardp)
{
struct snd_card *card;
int err;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_card_sb16), &card);
+ err = snd_devm_card_new(devptr, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_sb16), &card);
if (err < 0)
return err;
- card->private_free = snd_sb16_free;
*cardp = card;
return 0;
}
-static int __devinit snd_sb16_probe(struct snd_card *card, int dev)
+static int snd_sb16_probe(struct snd_card *card, int dev)
{
int xirq, xdma8, xdma16;
struct snd_sb *chip;
@@ -347,41 +310,39 @@ static int __devinit snd_sb16_probe(struct snd_card *card, int dev)
#ifdef CONFIG_SND_SB16_CSP
struct snd_hwdep *xcsp = NULL;
#endif
- unsigned long flags;
int err;
xirq = irq[dev];
xdma8 = dma8[dev];
xdma16 = dma16[dev];
- if ((err = snd_sbdsp_create(card,
- port[dev],
- xirq,
- snd_sb16dsp_interrupt,
- xdma8,
- xdma16,
- SB_HW_AUTO,
- &chip)) < 0)
+ err = snd_sbdsp_create(card, port[dev], xirq, snd_sb16dsp_interrupt,
+ xdma8, xdma16, SB_HW_AUTO, &chip);
+ if (err < 0)
return err;
acard->chip = chip;
if (chip->hardware != SB_HW_16) {
- snd_printk(KERN_ERR PFX "SB 16 chip was not detected at 0x%lx\n", port[dev]);
+ dev_err(card->dev, "SB 16 chip was not detected at 0x%lx\n", port[dev]);
return -ENODEV;
}
chip->mpu_port = mpu_port[dev];
- if (! is_isapnp_selected(dev) && (err = snd_sb16dsp_configure(chip)) < 0)
- return err;
+ if (!is_isapnp_selected(dev)) {
+ err = snd_sb16dsp_configure(chip);
+ if (err < 0)
+ return err;
+ }
- if ((err = snd_sb16dsp_pcm(chip, 0, &chip->pcm)) < 0)
+ err = snd_sb16dsp_pcm(chip, 0);
+ if (err < 0)
return err;
- strcpy(card->driver,
+ strscpy(card->driver,
#ifdef SNDRV_SBAWE_EMU8000
awe_port[dev] > 0 ? "SB AWE" :
#endif
"SB16");
- strcpy(card->shortname, chip->name);
+ strscpy(card->shortname, chip->name);
sprintf(card->longname, "%s at 0x%lx, irq %i, dma ",
chip->name,
chip->port,
@@ -393,9 +354,11 @@ static int __devinit snd_sb16_probe(struct snd_card *card, int dev)
xdma8 >= 0 ? "&" : "", xdma16);
if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) {
- if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SB,
- chip->mpu_port, 0,
- xirq, 0, &chip->rmidi)) < 0)
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_SB,
+ chip->mpu_port,
+ MPU401_INFO_IRQ_HOOK, -1,
+ &chip->rmidi);
+ if (err < 0)
return err;
chip->rmidi_callback = snd_mpu401_uart_interrupt;
}
@@ -410,20 +373,22 @@ static int __devinit snd_sb16_probe(struct snd_card *card, int dev)
OPL3_HW_OPL3,
acard->fm_res != NULL || fm_port[dev] == port[dev],
&opl3) < 0) {
- snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
- fm_port[dev], fm_port[dev] + 2);
+ dev_err(card->dev, "no OPL device at 0x%lx-0x%lx\n",
+ fm_port[dev], fm_port[dev] + 2);
} else {
#ifdef SNDRV_SBAWE_EMU8000
int seqdev = awe_port[dev] > 0 ? 2 : 1;
#else
int seqdev = 1;
#endif
- if ((err = snd_opl3_hwdep_new(opl3, 0, seqdev, &synth)) < 0)
+ err = snd_opl3_hwdep_new(opl3, 0, seqdev, &synth);
+ if (err < 0)
return err;
}
}
- if ((err = snd_sbmixer_new(chip)) < 0)
+ err = snd_sbmixer_new(chip);
+ if (err < 0)
return err;
#ifdef CONFIG_SND_SB16_CSP
@@ -434,15 +399,20 @@ static int __devinit snd_sb16_probe(struct snd_card *card, int dev)
chip->csp = xcsp->private_data;
chip->hardware = SB_HW_16CSP;
} else {
- snd_printk(KERN_INFO PFX "warning - CSP chip not detected on soundcard #%i\n", dev + 1);
+ dev_info(card->dev,
+ "warning - CSP chip not detected on soundcard #%i\n",
+ dev + 1);
}
}
#endif
#ifdef SNDRV_SBAWE_EMU8000
if (awe_port[dev] > 0) {
- if ((err = snd_emu8000_new(card, 1, awe_port[dev],
- seq_ports[dev], NULL)) < 0) {
- snd_printk(KERN_ERR PFX "fatal error - EMU-8000 synthesizer not detected at 0x%lx\n", awe_port[dev]);
+ err = snd_emu8000_new(card, 1, awe_port[dev],
+ seq_ports[dev], NULL);
+ if (err < 0) {
+ dev_err(card->dev,
+ "fatal error - EMU-8000 synthesizer not detected at 0x%lx\n",
+ awe_port[dev]);
return err;
}
@@ -450,13 +420,14 @@ static int __devinit snd_sb16_probe(struct snd_card *card, int dev)
#endif
/* setup Mic AGC */
- spin_lock_irqsave(&chip->mixer_lock, flags);
- snd_sbmixer_write(chip, SB_DSP4_MIC_AGC,
- (snd_sbmixer_read(chip, SB_DSP4_MIC_AGC) & 0x01) |
- (mic_agc[dev] ? 0x00 : 0x01));
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->mixer_lock) {
+ snd_sbmixer_write(chip, SB_DSP4_MIC_AGC,
+ (snd_sbmixer_read(chip, SB_DSP4_MIC_AGC) & 0x01) |
+ (mic_agc[dev] ? 0x00 : 0x01));
+ }
- if ((err = snd_card_register(card)) < 0)
+ err = snd_card_register(card);
+ if (err < 0)
return err;
return 0;
@@ -469,7 +440,6 @@ static int snd_sb16_suspend(struct snd_card *card, pm_message_t state)
struct snd_sb *chip = acard->chip;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_sbmixer_suspend(chip);
return 0;
}
@@ -486,13 +456,13 @@ static int snd_sb16_resume(struct snd_card *card)
}
#endif
-static int __devinit snd_sb16_isa_probe1(int dev, struct device *pdev)
+static int snd_sb16_isa_probe1(int dev, struct device *pdev)
{
struct snd_card_sb16 *acard;
struct snd_card *card;
int err;
- err = snd_sb16_card_new(dev, &card);
+ err = snd_sb16_card_new(pdev, dev, &card);
if (err < 0)
return err;
@@ -500,49 +470,51 @@ static int __devinit snd_sb16_isa_probe1(int dev, struct device *pdev)
/* non-PnP FM port address is hardwired with base port address */
fm_port[dev] = port[dev];
/* block the 0x388 port to avoid PnP conflicts */
- acard->fm_res = request_region(0x388, 4, "SoundBlaster FM");
+ acard->fm_res = devm_request_region(card->dev, 0x388, 4,
+ "SoundBlaster FM");
#ifdef SNDRV_SBAWE_EMU8000
/* non-PnP AWE port address is hardwired with base port address */
awe_port[dev] = port[dev] + 0x400;
#endif
- snd_card_set_dev(card, pdev);
- if ((err = snd_sb16_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_sb16_probe(card, dev);
+ if (err < 0)
return err;
- }
dev_set_drvdata(pdev, card);
return 0;
}
-static int __devinit snd_sb16_isa_match(struct device *pdev, unsigned int dev)
+static int snd_sb16_isa_match(struct device *pdev, unsigned int dev)
{
return enable[dev] && !is_isapnp_selected(dev);
}
-static int __devinit snd_sb16_isa_probe(struct device *pdev, unsigned int dev)
+static int snd_sb16_isa_probe(struct device *pdev, unsigned int dev)
{
int err;
- static int possible_irqs[] = {5, 9, 10, 7, -1};
- static int possible_dmas8[] = {1, 3, 0, -1};
- static int possible_dmas16[] = {5, 6, 7, -1};
+ static const int possible_irqs[] = {5, 9, 10, 7, -1};
+ static const int possible_dmas8[] = {1, 3, 0, -1};
+ static const int possible_dmas16[] = {5, 6, 7, -1};
if (irq[dev] == SNDRV_AUTO_IRQ) {
- if ((irq[dev] = snd_legacy_find_free_irq(possible_irqs)) < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
+ irq[dev] = snd_legacy_find_free_irq(possible_irqs);
+ if (irq[dev] < 0) {
+ dev_err(pdev, "unable to find a free IRQ\n");
return -EBUSY;
}
}
if (dma8[dev] == SNDRV_AUTO_DMA) {
- if ((dma8[dev] = snd_legacy_find_free_dma(possible_dmas8)) < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free 8-bit DMA\n");
+ dma8[dev] = snd_legacy_find_free_dma(possible_dmas8);
+ if (dma8[dev] < 0) {
+ dev_err(pdev, "unable to find a free 8-bit DMA\n");
return -EBUSY;
}
}
if (dma16[dev] == SNDRV_AUTO_DMA) {
- if ((dma16[dev] = snd_legacy_find_free_dma(possible_dmas16)) < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free 16-bit DMA\n");
+ dma16[dev] = snd_legacy_find_free_dma(possible_dmas16);
+ if (dma16[dev] < 0) {
+ dev_err(pdev, "unable to find a free 16-bit DMA\n");
return -EBUSY;
}
}
@@ -550,7 +522,7 @@ static int __devinit snd_sb16_isa_probe(struct device *pdev, unsigned int dev)
if (port[dev] != SNDRV_AUTO_PORT)
return snd_sb16_isa_probe1(dev, pdev);
else {
- static int possible_ports[] = {0x220, 0x240, 0x260, 0x280};
+ static const int possible_ports[] = {0x220, 0x240, 0x260, 0x280};
int i;
for (i = 0; i < ARRAY_SIZE(possible_ports); i++) {
port[dev] = possible_ports[i];
@@ -562,13 +534,6 @@ static int __devinit snd_sb16_isa_probe(struct device *pdev, unsigned int dev)
}
}
-static int __devexit snd_sb16_isa_remove(struct device *pdev, unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(pdev));
- dev_set_drvdata(pdev, NULL);
- return 0;
-}
-
#ifdef CONFIG_PM
static int snd_sb16_isa_suspend(struct device *dev, unsigned int n,
pm_message_t state)
@@ -591,7 +556,6 @@ static int snd_sb16_isa_resume(struct device *dev, unsigned int n)
static struct isa_driver snd_sb16_isa_driver = {
.match = snd_sb16_isa_match,
.probe = snd_sb16_isa_probe,
- .remove = __devexit_p(snd_sb16_isa_remove),
#ifdef CONFIG_PM
.suspend = snd_sb16_isa_suspend,
.resume = snd_sb16_isa_resume,
@@ -603,8 +567,8 @@ static struct isa_driver snd_sb16_isa_driver = {
#ifdef CONFIG_PNP
-static int __devinit snd_sb16_pnp_detect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_sb16_pnp_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
static int dev;
struct snd_card *card;
@@ -613,15 +577,15 @@ static int __devinit snd_sb16_pnp_detect(struct pnp_card_link *pcard,
for ( ; dev < SNDRV_CARDS; dev++) {
if (!enable[dev] || !isapnp[dev])
continue;
- res = snd_sb16_card_new(dev, &card);
+ res = snd_sb16_card_new(&pcard->card->dev, dev, &card);
if (res < 0)
return res;
- snd_card_set_dev(card, &pcard->card->dev);
- if ((res = snd_card_sb16_pnp(dev, card->private_data, pcard, pid)) < 0 ||
- (res = snd_sb16_probe(card, dev)) < 0) {
- snd_card_free(card);
+ res = snd_card_sb16_pnp(dev, card->private_data, pcard, pid);
+ if (res < 0)
+ return res;
+ res = snd_sb16_probe(card, dev);
+ if (res < 0)
return res;
- }
pnp_set_card_drvdata(pcard, card);
dev++;
return 0;
@@ -630,12 +594,6 @@ static int __devinit snd_sb16_pnp_detect(struct pnp_card_link *pcard,
return -ENODEV;
}
-static void __devexit snd_sb16_pnp_remove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
-}
-
#ifdef CONFIG_PM
static int snd_sb16_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
{
@@ -656,7 +614,6 @@ static struct pnp_card_driver sb16_pnpc_driver = {
#endif
.id_table = snd_sb16_pnpids,
.probe = snd_sb16_pnp_detect,
- .remove = __devexit_p(snd_sb16_pnp_remove),
#ifdef CONFIG_PM
.suspend = snd_sb16_pnp_suspend,
.resume = snd_sb16_pnp_resume,
diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c
index bdc8dde4e4a2..9ad71a9fc18d 100644
--- a/sound/isa/sb/sb16_csp.c
+++ b/sound/isa/sb/sb16_csp.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 1999 by Uros Bizjak <uros@kss-loka.si>
* Takashi Iwai <tiwai@suse.de>
@@ -6,26 +7,13 @@
*
* CSP microcode loader:
* alsa-tools/sb16_csp/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/string_choices.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/info.h>
@@ -59,18 +47,18 @@ MODULE_FIRMWARE("sb16/ima_adpcm_capture.csp");
* RIFF data format
*/
struct riff_header {
- __u32 name;
- __u32 len;
+ __le32 name;
+ __le32 len;
};
struct desc_header {
struct riff_header info;
- __u16 func_nr;
- __u16 VOC_type;
- __u16 flags_play_rec;
- __u16 flags_16bit_8bit;
- __u16 flags_stereo_mono;
- __u16 flags_rates;
+ __le16 func_nr;
+ __le16 VOC_type;
+ __le16 flags_play_rec;
+ __le16 flags_16bit_8bit;
+ __le16 flags_stereo_mono;
+ __le16 flags_rates;
};
/*
@@ -92,7 +80,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
struct snd_sb_csp_microcode __user * code);
static int snd_sb_csp_unload(struct snd_sb_csp * p);
static int snd_sb_csp_load_user(struct snd_sb_csp * p, const unsigned char __user *buf, int size, int load_flags);
-static int snd_sb_csp_autoload(struct snd_sb_csp * p, int pcm_sfmt, int play_rec_mode);
+static int snd_sb_csp_autoload(struct snd_sb_csp * p, snd_pcm_format_t pcm_sfmt, int play_rec_mode);
static int snd_sb_csp_check_version(struct snd_sb_csp * p);
static int snd_sb_csp_use(struct snd_sb_csp * p);
@@ -115,7 +103,7 @@ static void info_read(struct snd_info_entry *entry, struct snd_info_buffer *buff
int snd_sb_csp_new(struct snd_sb *chip, int device, struct snd_hwdep ** rhwdep)
{
struct snd_sb_csp *p;
- int uninitialized_var(version);
+ int version;
int err;
struct snd_hwdep *hw;
@@ -125,10 +113,12 @@ int snd_sb_csp_new(struct snd_sb *chip, int device, struct snd_hwdep ** rhwdep)
if (csp_detect(chip, &version))
return -ENODEV;
- if ((err = snd_hwdep_new(chip->card, "SB16-CSP", device, &hw)) < 0)
+ err = snd_hwdep_new(chip->card, "SB16-CSP", device, &hw);
+ if (err < 0)
return err;
- if ((p = kzalloc(sizeof(*p), GFP_KERNEL)) == NULL) {
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p) {
snd_device_free(chip->card, hw);
return -ENOMEM;
}
@@ -207,6 +197,7 @@ static int snd_sb_csp_ioctl(struct snd_hwdep * hw, struct file *file, unsigned i
switch (cmd) {
/* get information */
case SNDRV_SB_CSP_IOCTL_INFO:
+ memset(&info, 0, sizeof(info));
*info.codec_name = *p->codec_name;
info.func_nr = p->func_nr;
info.acc_format = p->acc_format;
@@ -274,14 +265,10 @@ static int snd_sb_csp_release(struct snd_hwdep * hw, struct file *file)
*/
static int snd_sb_csp_use(struct snd_sb_csp * p)
{
- mutex_lock(&p->access_mutex);
- if (p->used) {
- mutex_unlock(&p->access_mutex);
+ guard(mutex)(&p->access_mutex);
+ if (p->used)
return -EAGAIN;
- }
p->used++;
- mutex_unlock(&p->access_mutex);
-
return 0;
}
@@ -291,10 +278,8 @@ static int snd_sb_csp_use(struct snd_sb_csp * p)
*/
static int snd_sb_csp_unuse(struct snd_sb_csp * p)
{
- mutex_lock(&p->access_mutex);
+ guard(mutex)(&p->access_mutex);
p->used--;
- mutex_unlock(&p->access_mutex);
-
return 0;
}
@@ -306,16 +291,16 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
struct snd_sb_csp_microcode __user * mcode)
{
struct snd_sb_csp_mc_header info;
+ struct device *dev = p->chip->card->dev;
unsigned char __user *data_ptr;
unsigned char __user *data_end;
unsigned short func_nr = 0;
struct riff_header file_h, item_h, code_h;
- __u32 item_type;
+ __le32 item_type;
struct desc_header funcdesc_h;
- unsigned long flags;
int err;
if (copy_from_user(&info, mcode, sizeof(info)))
@@ -324,9 +309,9 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
if (copy_from_user(&file_h, data_ptr, sizeof(file_h)))
return -EFAULT;
- if ((file_h.name != RIFF_HEADER) ||
+ if ((le32_to_cpu(file_h.name) != RIFF_HEADER) ||
(le32_to_cpu(file_h.len) >= SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE - sizeof(file_h))) {
- snd_printd("%s: Invalid RIFF header\n", __func__);
+ dev_dbg(dev, "%s: Invalid RIFF header\n", __func__);
return -EINVAL;
}
data_ptr += sizeof(file_h);
@@ -334,8 +319,8 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
if (copy_from_user(&item_type, data_ptr, sizeof(item_type)))
return -EFAULT;
- if (item_type != CSP__HEADER) {
- snd_printd("%s: Invalid RIFF file type\n", __func__);
+ if (le32_to_cpu(item_type) != CSP__HEADER) {
+ dev_dbg(dev, "%s: Invalid RIFF file type\n", __func__);
return -EINVAL;
}
data_ptr += sizeof (item_type);
@@ -344,12 +329,12 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
if (copy_from_user(&item_h, data_ptr, sizeof(item_h)))
return -EFAULT;
data_ptr += sizeof(item_h);
- if (item_h.name != LIST_HEADER)
+ if (le32_to_cpu(item_h.name) != LIST_HEADER)
continue;
if (copy_from_user(&item_type, data_ptr, sizeof(item_type)))
return -EFAULT;
- switch (item_type) {
+ switch (le32_to_cpu(item_type)) {
case FUNC_HEADER:
if (copy_from_user(&funcdesc_h, data_ptr + sizeof(item_type), sizeof(funcdesc_h)))
return -EFAULT;
@@ -376,7 +361,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
return -EFAULT;
/* init microcode blocks */
- if (code_h.name != INIT_HEADER)
+ if (le32_to_cpu(code_h.name) != INIT_HEADER)
break;
data_ptr += sizeof(code_h);
err = snd_sb_csp_load_user(p, data_ptr, le32_to_cpu(code_h.len),
@@ -389,8 +374,8 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
if (copy_from_user(&code_h, data_ptr, sizeof(code_h)))
return -EFAULT;
- if (code_h.name != MAIN_HEADER) {
- snd_printd("%s: Missing 'main' microcode\n", __func__);
+ if (le32_to_cpu(code_h.name) != MAIN_HEADER) {
+ dev_dbg(dev, "%s: Missing 'main' microcode\n", __func__);
return -EINVAL;
}
data_ptr += sizeof(code_h);
@@ -400,7 +385,7 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
return err;
/* fill in codec header */
- strlcpy(p->codec_name, info.codec_name, sizeof(p->codec_name));
+ strscpy(p->codec_name, info.codec_name, sizeof(p->codec_name));
p->func_nr = func_nr;
p->mode = le16_to_cpu(funcdesc_h.flags_play_rec);
switch (le16_to_cpu(funcdesc_h.VOC_type)) {
@@ -433,9 +418,9 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
default: /* other codecs are unsupported */
p->acc_format = p->acc_width = p->acc_rates = 0;
p->mode = 0;
- snd_printd("%s: Unsupported CSP codec type: 0x%04x\n",
- __func__,
- le16_to_cpu(funcdesc_h.VOC_type));
+ dev_dbg(dev, "%s: Unsupported CSP codec type: 0x%04x\n",
+ __func__,
+ le16_to_cpu(funcdesc_h.VOC_type));
return -EINVAL;
}
p->acc_channels = le16_to_cpu(funcdesc_h.flags_stereo_mono);
@@ -443,17 +428,16 @@ static int snd_sb_csp_riff_load(struct snd_sb_csp * p,
p->acc_rates = le16_to_cpu(funcdesc_h.flags_rates);
/* Decouple CSP from IRQ and DMAREQ lines */
- spin_lock_irqsave(&p->chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&p->chip->reg_lock);
set_mode_register(p->chip, 0xfc);
set_mode_register(p->chip, 0x00);
- spin_unlock_irqrestore(&p->chip->reg_lock, flags);
/* finished loading successfully */
p->running = SNDRV_SB_CSP_ST_LOADED; /* set LOADED flag */
return 0;
}
}
- snd_printd("%s: Function #%d not found\n", __func__, info.func_req);
+ dev_dbg(dev, "%s: Function #%d not found\n", __func__, info.func_req);
return -EINVAL;
}
@@ -556,10 +540,8 @@ static int set_mode_register(struct snd_sb *chip, unsigned char mode)
static int csp_detect(struct snd_sb *chip, int *version)
{
unsigned char csp_test1, csp_test2;
- unsigned long flags;
- int result = -ENODEV;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
set_codec_parameter(chip, 0x00, 0x00);
set_mode_register(chip, 0xfc); /* 0xfc = ?? */
@@ -568,23 +550,21 @@ static int csp_detect(struct snd_sb *chip, int *version)
set_register(chip, 0x83, ~csp_test1);
csp_test2 = read_register(chip, 0x83);
if (csp_test2 != (csp_test1 ^ 0xff))
- goto __fail;
+ return -ENODEV;
set_register(chip, 0x83, csp_test1);
csp_test2 = read_register(chip, 0x83);
if (csp_test2 != csp_test1)
- goto __fail;
+ return -ENODEV;
set_mode_register(chip, 0x00); /* 0x00 = ? */
*version = get_version(chip);
snd_sbdsp_reset(chip); /* reset DSP after getversion! */
if (*version >= 0x10 && *version <= 0x1f)
- result = 0; /* valid version id */
+ return 0; /* valid version id */
- __fail:
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return result;
+ return -ENODEV;
}
/*
@@ -607,7 +587,9 @@ static int get_version(struct snd_sb *chip)
static int snd_sb_csp_check_version(struct snd_sb_csp * p)
{
if (p->version < 0x10 || p->version > 0x1f) {
- snd_printd("%s: Invalid CSP version: 0x%x\n", __func__, p->version);
+ dev_dbg(p->chip->card->dev,
+ "%s: Invalid CSP version: 0x%x\n",
+ __func__, p->version);
return 1;
}
return 0;
@@ -620,14 +602,12 @@ static int snd_sb_csp_load(struct snd_sb_csp * p, const unsigned char *buf, int
{
int status, i;
int err;
- int result = -EIO;
- unsigned long flags;
- spin_lock_irqsave(&p->chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&p->chip->reg_lock);
snd_sbdsp_command(p->chip, 0x01); /* CSP download command */
if (snd_sbdsp_get_byte(p->chip)) {
- snd_printd("%s: Download command failed\n", __func__);
- goto __fail;
+ dev_dbg(p->chip->card->dev, "%s: Download command failed\n", __func__);
+ return -EIO;
}
/* Send CSP low byte (size - 1) */
snd_sbdsp_command(p->chip, (unsigned char)(size - 1));
@@ -637,10 +617,10 @@ static int snd_sb_csp_load(struct snd_sb_csp * p, const unsigned char *buf, int
/* load from kernel space */
while (size--) {
if (!snd_sbdsp_command(p->chip, *buf++))
- goto __fail;
+ return -EIO;
}
if (snd_sbdsp_get_byte(p->chip))
- goto __fail;
+ return -EIO;
if (load_flags & SNDRV_SB_CSP_LOAD_INITBLOCK) {
i = 0;
@@ -653,8 +633,10 @@ static int snd_sb_csp_load(struct snd_sb_csp * p, const unsigned char *buf, int
udelay (10);
}
if (status != 0x55) {
- snd_printd("%s: Microcode initialization failed\n", __func__);
- goto __fail;
+ dev_dbg(p->chip->card->dev,
+ "%s: Microcode initialization failed\n",
+ __func__);
+ return -EIO;
}
} else {
/*
@@ -662,24 +644,21 @@ static int snd_sb_csp_load(struct snd_sb_csp * p, const unsigned char *buf, int
* Start CSP chip if no 16bit DMA channel is set - some kind
* of autorun or perhaps a bugfix?
*/
- spin_lock(&p->chip->mixer_lock);
- status = snd_sbmixer_read(p->chip, SB_DSP4_DMASETUP);
- spin_unlock(&p->chip->mixer_lock);
+ scoped_guard(spinlock, &p->chip->mixer_lock) {
+ status = snd_sbmixer_read(p->chip, SB_DSP4_DMASETUP);
+ }
if (!(status & (SB_DMASETUP_DMA7 | SB_DMASETUP_DMA6 | SB_DMASETUP_DMA5))) {
err = (set_codec_parameter(p->chip, 0xaa, 0x00) ||
set_codec_parameter(p->chip, 0xff, 0x00));
snd_sbdsp_reset(p->chip); /* really! */
if (err)
- goto __fail;
+ return -EIO;
set_mode_register(p->chip, 0xc0); /* c0 = STOP */
set_mode_register(p->chip, 0x70); /* 70 = RUN */
}
}
- result = 0;
- __fail:
- spin_unlock_irqrestore(&p->chip->reg_lock, flags);
- return result;
+ return 0;
}
static int snd_sb_csp_load_user(struct snd_sb_csp * p, const unsigned char __user *buf, int size, int load_flags)
@@ -724,9 +703,8 @@ static int snd_sb_csp_firmware_load(struct snd_sb_csp *p, int index, int flags)
* autoload hardware codec if necessary
* return 0 if CSP is loaded and ready to run (p->running != 0)
*/
-static int snd_sb_csp_autoload(struct snd_sb_csp * p, int pcm_sfmt, int play_rec_mode)
+static int snd_sb_csp_autoload(struct snd_sb_csp * p, snd_pcm_format_t pcm_sfmt, int play_rec_mode)
{
- unsigned long flags;
int err = 0;
/* if CSP is running or manually loaded then exit */
@@ -734,7 +712,7 @@ static int snd_sb_csp_autoload(struct snd_sb_csp * p, int pcm_sfmt, int play_rec
return -EBUSY;
/* autoload microcode only if requested hardware codec is not already loaded */
- if (((1 << pcm_sfmt) & p->acc_format) && (play_rec_mode & p->mode)) {
+ if (((1U << (__force int)pcm_sfmt) & p->acc_format) && (play_rec_mode & p->mode)) {
p->running = SNDRV_SB_CSP_ST_AUTO;
} else {
switch (pcm_sfmt) {
@@ -767,10 +745,9 @@ static int snd_sb_csp_autoload(struct snd_sb_csp * p, int pcm_sfmt, int play_rec
default:
/* Decouple CSP from IRQ and DMAREQ lines */
if (p->running & SNDRV_SB_CSP_ST_AUTO) {
- spin_lock_irqsave(&p->chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&p->chip->reg_lock);
set_mode_register(p->chip, 0xfc);
set_mode_register(p->chip, 0x00);
- spin_unlock_irqrestore(&p->chip->reg_lock, flags);
p->running = 0; /* clear autoloaded flag */
}
return -EINVAL;
@@ -798,76 +775,77 @@ static int snd_sb_csp_autoload(struct snd_sb_csp * p, int pcm_sfmt, int play_rec
*/
static int snd_sb_csp_start(struct snd_sb_csp * p, int sample_width, int channels)
{
+ struct device *dev = p->chip->card->dev;
unsigned char s_type; /* sample type */
unsigned char mixL, mixR;
int result = -EIO;
- unsigned long flags;
if (!(p->running & (SNDRV_SB_CSP_ST_LOADED | SNDRV_SB_CSP_ST_AUTO))) {
- snd_printd("%s: Microcode not loaded\n", __func__);
+ dev_dbg(dev, "%s: Microcode not loaded\n", __func__);
return -ENXIO;
}
if (p->running & SNDRV_SB_CSP_ST_RUNNING) {
- snd_printd("%s: CSP already running\n", __func__);
+ dev_dbg(dev, "%s: CSP already running\n", __func__);
return -EBUSY;
}
if (!(sample_width & p->acc_width)) {
- snd_printd("%s: Unsupported PCM sample width\n", __func__);
+ dev_dbg(dev, "%s: Unsupported PCM sample width\n", __func__);
return -EINVAL;
}
if (!(channels & p->acc_channels)) {
- snd_printd("%s: Invalid number of channels\n", __func__);
+ dev_dbg(dev, "%s: Invalid number of channels\n", __func__);
return -EINVAL;
}
/* Mute PCM volume */
- spin_lock_irqsave(&p->chip->mixer_lock, flags);
- mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV);
- mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1);
- snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7);
- snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7);
-
- spin_lock(&p->chip->reg_lock);
- set_mode_register(p->chip, 0xc0); /* c0 = STOP */
- set_mode_register(p->chip, 0x70); /* 70 = RUN */
-
- s_type = 0x00;
- if (channels == SNDRV_SB_CSP_MONO)
- s_type = 0x11; /* 000n 000n (n = 1 if mono) */
- if (sample_width == SNDRV_SB_CSP_SAMPLE_8BIT)
- s_type |= 0x22; /* 00dX 00dX (d = 1 if 8 bit samples) */
-
- if (set_codec_parameter(p->chip, 0x81, s_type)) {
- snd_printd("%s: Set sample type command failed\n", __func__);
- goto __fail;
+ scoped_guard(spinlock_irqsave, &p->chip->mixer_lock) {
+ mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV);
+ mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1);
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7);
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7);
}
- if (set_codec_parameter(p->chip, 0x80, 0x00)) {
- snd_printd("%s: Codec start command failed\n", __func__);
- goto __fail;
- }
- p->run_width = sample_width;
- p->run_channels = channels;
- p->running |= SNDRV_SB_CSP_ST_RUNNING;
+ scoped_guard(spinlock, &p->chip->reg_lock) {
+ set_mode_register(p->chip, 0xc0); /* c0 = STOP */
+ set_mode_register(p->chip, 0x70); /* 70 = RUN */
- if (p->mode & SNDRV_SB_CSP_MODE_QSOUND) {
- set_codec_parameter(p->chip, 0xe0, 0x01);
- /* enable QSound decoder */
- set_codec_parameter(p->chip, 0x00, 0xff);
- set_codec_parameter(p->chip, 0x01, 0xff);
- p->running |= SNDRV_SB_CSP_ST_QSOUND;
- /* set QSound startup value */
- snd_sb_csp_qsound_transfer(p);
- }
- result = 0;
+ s_type = 0x00;
+ if (channels == SNDRV_SB_CSP_MONO)
+ s_type = 0x11; /* 000n 000n (n = 1 if mono) */
+ if (sample_width == SNDRV_SB_CSP_SAMPLE_8BIT)
+ s_type |= 0x22; /* 00dX 00dX (d = 1 if 8 bit samples) */
- __fail:
- spin_unlock(&p->chip->reg_lock);
+ if (set_codec_parameter(p->chip, 0x81, s_type)) {
+ dev_dbg(dev, "%s: Set sample type command failed\n", __func__);
+ break;
+ }
+ if (set_codec_parameter(p->chip, 0x80, 0x00)) {
+ dev_dbg(dev, "%s: Codec start command failed\n", __func__);
+ break;
+ }
+ p->run_width = sample_width;
+ p->run_channels = channels;
+
+ p->running |= SNDRV_SB_CSP_ST_RUNNING;
+
+ if (p->mode & SNDRV_SB_CSP_MODE_QSOUND) {
+ set_codec_parameter(p->chip, 0xe0, 0x01);
+ /* enable QSound decoder */
+ set_codec_parameter(p->chip, 0x00, 0xff);
+ set_codec_parameter(p->chip, 0x01, 0xff);
+ p->running |= SNDRV_SB_CSP_ST_QSOUND;
+ /* set QSound startup value */
+ snd_sb_csp_qsound_transfer(p);
+ }
+ result = 0;
+ }
/* restore PCM volume */
- snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL);
- snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR);
- spin_unlock_irqrestore(&p->chip->mixer_lock, flags);
+ if (result < 0) {
+ guard(spinlock_irqsave)(&p->chip->mixer_lock);
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL);
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR);
+ }
return result;
}
@@ -879,34 +857,35 @@ static int snd_sb_csp_stop(struct snd_sb_csp * p)
{
int result;
unsigned char mixL, mixR;
- unsigned long flags;
if (!(p->running & SNDRV_SB_CSP_ST_RUNNING))
return 0;
/* Mute PCM volume */
- spin_lock_irqsave(&p->chip->mixer_lock, flags);
- mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV);
- mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1);
- snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7);
- snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7);
+ scoped_guard(spinlock_irqsave, &p->chip->mixer_lock) {
+ mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV);
+ mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1);
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7);
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7);
+ }
- spin_lock(&p->chip->reg_lock);
- if (p->running & SNDRV_SB_CSP_ST_QSOUND) {
- set_codec_parameter(p->chip, 0xe0, 0x01);
- /* disable QSound decoder */
- set_codec_parameter(p->chip, 0x00, 0x00);
- set_codec_parameter(p->chip, 0x01, 0x00);
+ scoped_guard(spinlock, &p->chip->reg_lock) {
+ if (p->running & SNDRV_SB_CSP_ST_QSOUND) {
+ set_codec_parameter(p->chip, 0xe0, 0x01);
+ /* disable QSound decoder */
+ set_codec_parameter(p->chip, 0x00, 0x00);
+ set_codec_parameter(p->chip, 0x01, 0x00);
- p->running &= ~SNDRV_SB_CSP_ST_QSOUND;
+ p->running &= ~SNDRV_SB_CSP_ST_QSOUND;
+ }
+ result = set_mode_register(p->chip, 0xc0); /* c0 = STOP */
}
- result = set_mode_register(p->chip, 0xc0); /* c0 = STOP */
- spin_unlock(&p->chip->reg_lock);
/* restore PCM volume */
- snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL);
- snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR);
- spin_unlock_irqrestore(&p->chip->mixer_lock, flags);
+ scoped_guard(spinlock_irqsave, &p->chip->mixer_lock) {
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL);
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR);
+ }
if (!(result))
p->running &= ~(SNDRV_SB_CSP_ST_PAUSED | SNDRV_SB_CSP_ST_RUNNING);
@@ -919,14 +898,13 @@ static int snd_sb_csp_stop(struct snd_sb_csp * p)
static int snd_sb_csp_pause(struct snd_sb_csp * p)
{
int result;
- unsigned long flags;
if (!(p->running & SNDRV_SB_CSP_ST_RUNNING))
return -EBUSY;
- spin_lock_irqsave(&p->chip->reg_lock, flags);
- result = set_codec_parameter(p->chip, 0x80, 0xff);
- spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &p->chip->reg_lock) {
+ result = set_codec_parameter(p->chip, 0x80, 0xff);
+ }
if (!(result))
p->running |= SNDRV_SB_CSP_ST_PAUSED;
@@ -939,14 +917,13 @@ static int snd_sb_csp_pause(struct snd_sb_csp * p)
static int snd_sb_csp_restart(struct snd_sb_csp * p)
{
int result;
- unsigned long flags;
if (!(p->running & SNDRV_SB_CSP_ST_PAUSED))
return -EBUSY;
- spin_lock_irqsave(&p->chip->reg_lock, flags);
- result = set_codec_parameter(p->chip, 0x80, 0x00);
- spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &p->chip->reg_lock) {
+ result = set_codec_parameter(p->chip, 0x80, 0x00);
+ }
if (!(result))
p->running &= ~SNDRV_SB_CSP_ST_PAUSED;
@@ -972,15 +949,13 @@ static int snd_sb_qsound_switch_get(struct snd_kcontrol *kcontrol, struct snd_ct
static int snd_sb_qsound_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb_csp *p = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned char nval;
nval = ucontrol->value.integer.value[0] & 0x01;
- spin_lock_irqsave(&p->q_lock, flags);
+ guard(spinlock_irqsave)(&p->q_lock);
change = p->q_enabled != nval;
p->q_enabled = nval;
- spin_unlock_irqrestore(&p->q_lock, flags);
return change;
}
@@ -996,19 +971,16 @@ static int snd_sb_qsound_space_info(struct snd_kcontrol *kcontrol, struct snd_ct
static int snd_sb_qsound_space_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb_csp *p = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- spin_lock_irqsave(&p->q_lock, flags);
+ guard(spinlock_irqsave)(&p->q_lock);
ucontrol->value.integer.value[0] = p->qpos_left;
ucontrol->value.integer.value[1] = p->qpos_right;
- spin_unlock_irqrestore(&p->q_lock, flags);
return 0;
}
static int snd_sb_qsound_space_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb_csp *p = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned char nval1, nval2;
@@ -1018,16 +990,15 @@ static int snd_sb_qsound_space_put(struct snd_kcontrol *kcontrol, struct snd_ctl
nval2 = ucontrol->value.integer.value[1];
if (nval2 > SNDRV_SB_CSP_QSOUND_MAX_RIGHT)
nval2 = SNDRV_SB_CSP_QSOUND_MAX_RIGHT;
- spin_lock_irqsave(&p->q_lock, flags);
+ guard(spinlock_irqsave)(&p->q_lock);
change = p->qpos_left != nval1 || p->qpos_right != nval2;
p->qpos_left = nval1;
p->qpos_right = nval2;
p->qpos_changed = change;
- spin_unlock_irqrestore(&p->q_lock, flags);
return change;
}
-static struct snd_kcontrol_new snd_sb_qsound_switch = {
+static const struct snd_kcontrol_new snd_sb_qsound_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "3D Control - Switch",
.info = snd_sb_qsound_switch_info,
@@ -1035,7 +1006,7 @@ static struct snd_kcontrol_new snd_sb_qsound_switch = {
.put = snd_sb_qsound_switch_put
};
-static struct snd_kcontrol_new snd_sb_qsound_space = {
+static const struct snd_kcontrol_new snd_sb_qsound_space = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "3D Control - Space",
.info = snd_sb_qsound_space_info,
@@ -1046,6 +1017,7 @@ static struct snd_kcontrol_new snd_sb_qsound_space = {
static int snd_sb_qsound_build(struct snd_sb_csp * p)
{
struct snd_card *card;
+ struct snd_kcontrol *kctl;
int err;
if (snd_BUG_ON(!p))
@@ -1057,10 +1029,16 @@ static int snd_sb_qsound_build(struct snd_sb_csp * p)
spin_lock_init(&p->q_lock);
- if ((err = snd_ctl_add(card, p->qsound_switch = snd_ctl_new1(&snd_sb_qsound_switch, p))) < 0)
+ kctl = snd_ctl_new1(&snd_sb_qsound_switch, p);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
goto __error;
- if ((err = snd_ctl_add(card, p->qsound_space = snd_ctl_new1(&snd_sb_qsound_space, p))) < 0)
+ p->qsound_switch = kctl;
+ kctl = snd_ctl_new1(&snd_sb_qsound_space, p);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
goto __error;
+ p->qsound_space = kctl;
return 0;
@@ -1072,24 +1050,20 @@ static int snd_sb_qsound_build(struct snd_sb_csp * p)
static void snd_sb_qsound_destroy(struct snd_sb_csp * p)
{
struct snd_card *card;
- unsigned long flags;
if (snd_BUG_ON(!p))
return;
card = p->chip->card;
- down_write(&card->controls_rwsem);
- if (p->qsound_switch)
- snd_ctl_remove(card, p->qsound_switch);
- if (p->qsound_space)
- snd_ctl_remove(card, p->qsound_space);
- up_write(&card->controls_rwsem);
+ snd_ctl_remove(card, p->qsound_switch);
+ p->qsound_switch = NULL;
+ snd_ctl_remove(card, p->qsound_space);
+ p->qsound_space = NULL;
/* cancel pending transfer of QSound parameters */
- spin_lock_irqsave (&p->q_lock, flags);
+ guard(spinlock_irqsave)(&p->q_lock);
p->qpos_changed = 0;
- spin_unlock_irqrestore (&p->q_lock, flags);
}
/*
@@ -1100,7 +1074,7 @@ static int snd_sb_csp_qsound_transfer(struct snd_sb_csp * p)
{
int err = -ENXIO;
- spin_lock(&p->q_lock);
+ guard(spinlock)(&p->q_lock);
if (p->running & SNDRV_SB_CSP_ST_QSOUND) {
set_codec_parameter(p->chip, 0xe0, 0x01);
/* left channel */
@@ -1112,7 +1086,6 @@ static int snd_sb_csp_qsound_transfer(struct snd_sb_csp * p)
err = 0;
}
p->qpos_changed = 0;
- spin_unlock(&p->q_lock);
return err;
}
@@ -1124,10 +1097,9 @@ static int snd_sb_csp_qsound_transfer(struct snd_sb_csp * p)
static int init_proc_entry(struct snd_sb_csp * p, int device)
{
char name[16];
- struct snd_info_entry *entry;
+
sprintf(name, "cspD%d", device);
- if (! snd_card_proc_new(p->chip->card, name, &entry))
- snd_info_set_text_ops(entry, p, info_read);
+ snd_card_ro_proc_new(p->chip->card, name, p, info_read);
return 0;
}
@@ -1153,8 +1125,8 @@ static void info_read(struct snd_info_entry *entry, struct snd_info_buffer *buff
((p->acc_rates & SNDRV_SB_CSP_RATE_44100) ? "44100Hz" : ""));
}
if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) {
- snd_iprintf(buffer, "QSound decoder %sabled\n",
- p->q_enabled ? "en" : "dis");
+ snd_iprintf(buffer, "QSound decoder %s\n",
+ str_enabled_disabled(p->q_enabled));
} else {
snd_iprintf(buffer, "PCM format ID: 0x%x (%s/%s) [%s/%s] [%s/%s]\n",
p->acc_format,
@@ -1183,19 +1155,3 @@ static void info_read(struct snd_info_entry *entry, struct snd_info_buffer *buff
/* */
EXPORT_SYMBOL(snd_sb_csp_new);
-
-/*
- * INIT part
- */
-
-static int __init alsa_sb_csp_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_sb_csp_exit(void)
-{
-}
-
-module_init(alsa_sb_csp_init)
-module_exit(alsa_sb_csp_exit)
diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c
index 2a6cc1cfe945..4d64db4f5852 100644
--- a/sound/isa/sb/sb16_main.c
+++ b/sound/isa/sb/sb16_main.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of 16-bit SoundBlaster cards and clones
@@ -15,28 +16,13 @@
* 16bit DMA transfers from DSP chip (capture) until 8bit transfer
* to DSP chip (playback) starts. This bug can be avoided with
* "16bit DMA Allocation" setting set to Playback or Capture.
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <asm/dma.h>
#include <linux/init.h>
#include <linux/time.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/sb.h>
#include <sound/sb16_csp.h>
@@ -48,6 +34,9 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Routines for control of 16-bit SoundBlaster cards and clones");
MODULE_LICENSE("GPL");
+#define runtime_format_bits(runtime) \
+ ((unsigned int)pcm_format_to_bits((runtime)->format))
+
#ifdef CONFIG_SND_SB16_CSP
static void snd_sb16_csp_playback_prepare(struct snd_sb *chip, struct snd_pcm_runtime *runtime)
{
@@ -57,7 +46,7 @@ static void snd_sb16_csp_playback_prepare(struct snd_sb *chip, struct snd_pcm_ru
if (csp->running & SNDRV_SB_CSP_ST_LOADED) {
/* manually loaded codec */
if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) &&
- ((1U << runtime->format) == csp->acc_format)) {
+ (runtime_format_bits(runtime) == csp->acc_format)) {
/* Supported runtime PCM format for playback */
if (csp->ops.csp_use(csp) == 0) {
/* If CSP was successfully acquired */
@@ -65,7 +54,7 @@ static void snd_sb16_csp_playback_prepare(struct snd_sb *chip, struct snd_pcm_ru
}
} else if ((csp->mode & SNDRV_SB_CSP_MODE_QSOUND) && (csp->q_enabled)) {
/* QSound decoder is loaded and enabled */
- if ((1 << runtime->format) & (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+ if (runtime_format_bits(runtime) & (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE)) {
/* Only for simple PCM formats */
if (csp->ops.csp_use(csp) == 0) {
@@ -105,7 +94,7 @@ static void snd_sb16_csp_capture_prepare(struct snd_sb *chip, struct snd_pcm_run
if (csp->running & SNDRV_SB_CSP_ST_LOADED) {
/* manually loaded codec */
if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) &&
- ((1U << runtime->format) == csp->acc_format)) {
+ (runtime_format_bits(runtime) == csp->acc_format)) {
/* Supported runtime PCM format for capture */
if (csp->ops.csp_use(csp) == 0) {
/* If CSP was successfully acquired */
@@ -141,9 +130,8 @@ static void snd_sb16_csp_update(struct snd_sb *chip)
struct snd_sb_csp *csp = chip->csp;
if (csp->qpos_changed) {
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
csp->ops.csp_qsound_transfer (csp);
- spin_unlock(&chip->reg_lock);
}
}
}
@@ -224,9 +212,7 @@ static void snd_sb16_setup_rate(struct snd_sb *chip,
unsigned short rate,
int channel)
{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (chip->mode & (channel == SNDRV_PCM_STREAM_PLAYBACK ? SB_MODE_PLAYBACK_16 : SB_MODE_CAPTURE_16))
snd_sb_ack_16bit(chip);
else
@@ -240,24 +226,10 @@ static void snd_sb16_setup_rate(struct snd_sb *chip,
snd_sbdsp_command(chip, rate >> 8);
snd_sbdsp_command(chip, rate & 0xff);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-}
-
-static int snd_sb16_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_sb16_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_lib_free_pages(substream);
- return 0;
}
static int snd_sb16_playback_prepare(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned char format;
@@ -276,7 +248,7 @@ static int snd_sb16_playback_prepare(struct snd_pcm_substream *substream)
snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
count = snd_pcm_lib_period_bytes(substream);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (chip->mode & SB_MODE_PLAYBACK_16) {
count >>= 1;
count--;
@@ -293,7 +265,6 @@ static int snd_sb16_playback_prepare(struct snd_pcm_substream *substream)
snd_sbdsp_command(chip, count >> 8);
snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -301,9 +272,8 @@ static int snd_sb16_playback_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct snd_sb *chip = snd_pcm_substream_chip(substream);
- int result = 0;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -319,15 +289,13 @@ static int snd_sb16_playback_trigger(struct snd_pcm_substream *substream,
chip->mode &= ~SB_RATE_LOCK_PLAYBACK;
break;
default:
- result = -EINVAL;
+ return -EINVAL;
}
- spin_unlock(&chip->reg_lock);
- return result;
+ return 0;
}
static int snd_sb16_capture_prepare(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned char format;
@@ -345,7 +313,7 @@ static int snd_sb16_capture_prepare(struct snd_pcm_substream *substream)
snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
count = snd_pcm_lib_period_bytes(substream);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (chip->mode & SB_MODE_CAPTURE_16) {
count >>= 1;
count--;
@@ -362,7 +330,6 @@ static int snd_sb16_capture_prepare(struct snd_pcm_substream *substream)
snd_sbdsp_command(chip, count >> 8);
snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -370,9 +337,8 @@ static int snd_sb16_capture_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct snd_sb *chip = snd_pcm_substream_chip(substream);
- int result = 0;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -388,10 +354,9 @@ static int snd_sb16_capture_trigger(struct snd_pcm_substream *substream,
chip->mode &= ~SB_RATE_LOCK_CAPTURE;
break;
default:
- result = -EINVAL;
+ return -EINVAL;
}
- spin_unlock(&chip->reg_lock);
- return result;
+ return 0;
}
irqreturn_t snd_sb16dsp_interrupt(int irq, void *dev_id)
@@ -400,9 +365,9 @@ irqreturn_t snd_sb16dsp_interrupt(int irq, void *dev_id)
unsigned char status;
int ok;
- spin_lock(&chip->mixer_lock);
- status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
- spin_unlock(&chip->mixer_lock);
+ scoped_guard(spinlock, &chip->mixer_lock) {
+ status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
+ }
if ((status & SB_IRQTYPE_MPUIN) && chip->rmidi_callback)
chip->rmidi_callback(irq, chip->rmidi->private_data);
if (status & SB_IRQTYPE_8BIT) {
@@ -416,11 +381,11 @@ irqreturn_t snd_sb16dsp_interrupt(int irq, void *dev_id)
snd_pcm_period_elapsed(chip->capture_substream);
ok++;
}
- spin_lock(&chip->reg_lock);
- if (!ok)
- snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
- snd_sb_ack_8bit(chip);
- spin_unlock(&chip->reg_lock);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ if (!ok)
+ snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
+ snd_sb_ack_8bit(chip);
+ }
}
if (status & SB_IRQTYPE_16BIT) {
ok = 0;
@@ -433,11 +398,11 @@ irqreturn_t snd_sb16dsp_interrupt(int irq, void *dev_id)
snd_pcm_period_elapsed(chip->capture_substream);
ok++;
}
- spin_lock(&chip->reg_lock);
- if (!ok)
- snd_sbdsp_command(chip, SB_DSP_DMA16_OFF);
- snd_sb_ack_16bit(chip);
- spin_unlock(&chip->reg_lock);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ if (!ok)
+ snd_sbdsp_command(chip, SB_DSP_DMA16_OFF);
+ snd_sb_ack_16bit(chip);
+ }
}
return IRQ_HANDLED;
}
@@ -472,7 +437,7 @@ static snd_pcm_uframes_t snd_sb16_capture_pointer(struct snd_pcm_substream *subs
*/
-static struct snd_pcm_hardware snd_sb16_playback =
+static const struct snd_pcm_hardware snd_sb16_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -490,7 +455,7 @@ static struct snd_pcm_hardware snd_sb16_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_sb16_capture =
+static const struct snd_pcm_hardware snd_sb16_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -514,15 +479,12 @@ static struct snd_pcm_hardware snd_sb16_capture =
static int snd_sb16_playback_open(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- spin_lock_irqsave(&chip->open_lock, flags);
- if (chip->mode & SB_MODE_PLAYBACK) {
- spin_unlock_irqrestore(&chip->open_lock, flags);
+ guard(spinlock_irqsave)(&chip->open_lock);
+ if (chip->mode & SB_MODE_PLAYBACK)
return -EAGAIN;
- }
runtime->hw = snd_sb16_playback;
/* skip if 16 bit DMA was reserved for capture */
@@ -556,7 +518,6 @@ static int snd_sb16_playback_open(struct snd_pcm_substream *substream)
runtime->hw.period_bytes_max = 64 * 1024;
goto __open_ok;
}
- spin_unlock_irqrestore(&chip->open_lock, flags);
return -EAGAIN;
__open_ok:
@@ -570,34 +531,28 @@ static int snd_sb16_playback_open(struct snd_pcm_substream *substream)
if (chip->mode & SB_RATE_LOCK)
runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
chip->playback_substream = substream;
- spin_unlock_irqrestore(&chip->open_lock, flags);
return 0;
}
static int snd_sb16_playback_close(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
snd_sb16_csp_playback_close(chip);
- spin_lock_irqsave(&chip->open_lock, flags);
+ guard(spinlock_irqsave)(&chip->open_lock);
chip->playback_substream = NULL;
chip->mode &= ~SB_MODE_PLAYBACK;
- spin_unlock_irqrestore(&chip->open_lock, flags);
return 0;
}
static int snd_sb16_capture_open(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- spin_lock_irqsave(&chip->open_lock, flags);
- if (chip->mode & SB_MODE_CAPTURE) {
- spin_unlock_irqrestore(&chip->open_lock, flags);
+ guard(spinlock_irqsave)(&chip->open_lock);
+ if (chip->mode & SB_MODE_CAPTURE)
return -EAGAIN;
- }
runtime->hw = snd_sb16_capture;
/* skip if 16 bit DMA was reserved for playback */
@@ -631,7 +586,6 @@ static int snd_sb16_capture_open(struct snd_pcm_substream *substream)
runtime->hw.period_bytes_max = 64 * 1024;
goto __open_ok;
}
- spin_unlock_irqrestore(&chip->open_lock, flags);
return -EAGAIN;
__open_ok:
@@ -645,20 +599,17 @@ static int snd_sb16_capture_open(struct snd_pcm_substream *substream)
if (chip->mode & SB_RATE_LOCK)
runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
chip->capture_substream = substream;
- spin_unlock_irqrestore(&chip->open_lock, flags);
return 0;
}
static int snd_sb16_capture_close(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
snd_sb16_csp_capture_close(chip);
- spin_lock_irqsave(&chip->open_lock, flags);
+ guard(spinlock_irqsave)(&chip->open_lock);
chip->capture_substream = NULL;
chip->mode &= ~SB_MODE_CAPTURE;
- spin_unlock_irqrestore(&chip->open_lock, flags);
return 0;
}
@@ -701,48 +652,47 @@ static int snd_sb16_get_dma_mode(struct snd_sb *chip)
static int snd_sb16_dma_control_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {
+ static const char * const texts[3] = {
"Auto", "Playback", "Capture"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_sb16_dma_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.enumerated.item[0] = snd_sb16_get_dma_mode(chip);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
static int snd_sb16_dma_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
unsigned char nval, oval;
int change;
- if ((nval = ucontrol->value.enumerated.item[0]) > 2)
+ if (chip->mode & (SB_MODE_PLAYBACK | SB_MODE_CAPTURE))
+ return -EBUSY;
+
+ nval = ucontrol->value.enumerated.item[0];
+ if (nval > 2)
return -EINVAL;
- spin_lock_irqsave(&chip->reg_lock, flags);
- oval = snd_sb16_get_dma_mode(chip);
- change = nval != oval;
- snd_sb16_set_dma_mode(chip, nval);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ oval = snd_sb16_get_dma_mode(chip);
+ change = nval != oval;
+ snd_sb16_set_dma_mode(chip, nval);
+ }
+ if (change) {
+ snd_dma_disable(chip->dma8);
+ snd_dma_disable(chip->dma16);
+ }
return change;
}
-static struct snd_kcontrol_new snd_sb16_dma_control = {
+static const struct snd_kcontrol_new snd_sb16_dma_control = {
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "16-bit DMA Allocation",
.info = snd_sb16_dma_control_info,
@@ -756,15 +706,13 @@ static struct snd_kcontrol_new snd_sb16_dma_control = {
int snd_sb16dsp_configure(struct snd_sb * chip)
{
- unsigned long flags;
unsigned char irqreg = 0, dmareg = 0, mpureg;
unsigned char realirq, realdma, realmpureg;
/* note: mpu register should be present only on SB16 Vibra soundcards */
- // printk(KERN_DEBUG "codec->irq=%i, codec->dma8=%i, codec->dma16=%i\n", chip->irq, chip->dma8, chip->dma16);
- spin_lock_irqsave(&chip->mixer_lock, flags);
- mpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP) & ~0x06;
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->mixer_lock) {
+ mpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP) & ~0x06;
+ }
switch (chip->irq) {
case 2:
case 9:
@@ -822,62 +770,61 @@ int snd_sb16dsp_configure(struct snd_sb * chip)
default:
mpureg |= 0x02; /* disable MPU */
}
- spin_lock_irqsave(&chip->mixer_lock, flags);
-
- snd_sbmixer_write(chip, SB_DSP4_IRQSETUP, irqreg);
- realirq = snd_sbmixer_read(chip, SB_DSP4_IRQSETUP);
- snd_sbmixer_write(chip, SB_DSP4_DMASETUP, dmareg);
- realdma = snd_sbmixer_read(chip, SB_DSP4_DMASETUP);
+ scoped_guard(spinlock_irqsave, &chip->mixer_lock) {
+ snd_sbmixer_write(chip, SB_DSP4_IRQSETUP, irqreg);
+ realirq = snd_sbmixer_read(chip, SB_DSP4_IRQSETUP);
- snd_sbmixer_write(chip, SB_DSP4_MPUSETUP, mpureg);
- realmpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP);
+ snd_sbmixer_write(chip, SB_DSP4_DMASETUP, dmareg);
+ realdma = snd_sbmixer_read(chip, SB_DSP4_DMASETUP);
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ snd_sbmixer_write(chip, SB_DSP4_MPUSETUP, mpureg);
+ realmpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP);
+ }
if ((~realirq) & irqreg || (~realdma) & dmareg) {
- snd_printk(KERN_ERR "SB16 [0x%lx]: unable to set DMA & IRQ (PnP device?)\n", chip->port);
- snd_printk(KERN_ERR "SB16 [0x%lx]: wanted: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, realirq, realdma, realmpureg);
- snd_printk(KERN_ERR "SB16 [0x%lx]: got: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, irqreg, dmareg, mpureg);
+ dev_err(chip->card->dev,
+ "SB16 [0x%lx]: unable to set DMA & IRQ (PnP device?)\n",
+ chip->port);
+ dev_err(chip->card->dev,
+ "SB16 [0x%lx]: wanted: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n",
+ chip->port, realirq, realdma, realmpureg);
+ dev_err(chip->card->dev,
+ "SB16 [0x%lx]: got: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n",
+ chip->port, irqreg, dmareg, mpureg);
return -ENODEV;
}
return 0;
}
-static struct snd_pcm_ops snd_sb16_playback_ops = {
+static const struct snd_pcm_ops snd_sb16_playback_ops = {
.open = snd_sb16_playback_open,
.close = snd_sb16_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_sb16_hw_params,
- .hw_free = snd_sb16_hw_free,
.prepare = snd_sb16_playback_prepare,
.trigger = snd_sb16_playback_trigger,
.pointer = snd_sb16_playback_pointer,
};
-static struct snd_pcm_ops snd_sb16_capture_ops = {
+static const struct snd_pcm_ops snd_sb16_capture_ops = {
.open = snd_sb16_capture_open,
.close = snd_sb16_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_sb16_hw_params,
- .hw_free = snd_sb16_hw_free,
.prepare = snd_sb16_capture_prepare,
.trigger = snd_sb16_capture_trigger,
.pointer = snd_sb16_capture_pointer,
};
-int snd_sb16dsp_pcm(struct snd_sb * chip, int device, struct snd_pcm ** rpcm)
+int snd_sb16dsp_pcm(struct snd_sb *chip, int device)
{
struct snd_card *card = chip->card;
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
- if ((err = snd_pcm_new(card, "SB16 DSP", device, 1, 1, &pcm)) < 0)
+ err = snd_pcm_new(card, "SB16 DSP", device, 1, 1, &pcm);
+ if (err < 0)
return err;
sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff);
pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
pcm->private_data = chip;
+ chip->pcm = pcm;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb16_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb16_capture_ops);
@@ -887,12 +834,8 @@ int snd_sb16dsp_pcm(struct snd_sb * chip, int device, struct snd_pcm ** rpcm)
else
pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
- 64*1024, 128*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, 64*1024, 128*1024);
return 0;
}
@@ -906,19 +849,3 @@ EXPORT_SYMBOL(snd_sb16dsp_pcm);
EXPORT_SYMBOL(snd_sb16dsp_get_pcm_ops);
EXPORT_SYMBOL(snd_sb16dsp_configure);
EXPORT_SYMBOL(snd_sb16dsp_interrupt);
-
-/*
- * INIT part
- */
-
-static int __init alsa_sb16_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_sb16_exit(void)
-{
-}
-
-module_init(alsa_sb16_init)
-module_exit(alsa_sb16_exit)
diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c
index 2259e3f726a7..6d5131265913 100644
--- a/sound/isa/sb/sb8.c
+++ b/sound/isa/sb/sb8.c
@@ -1,29 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for SoundBlaster 1.0/2.0/Pro soundcards and compatible
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
#include <linux/ioport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/string.h>
#include <sound/core.h>
#include <sound/sb.h>
#include <sound/opl3.h>
@@ -32,11 +18,10 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Sound Blaster 1.0/2.0/Pro");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB 1.0/SB 2.0/SB Pro}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */
static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3 */
@@ -47,11 +32,11 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for Sound Blaster soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Sound Blaster soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for SB8 driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for SB8 driver.");
-module_param_array(dma8, int, NULL, 0444);
+module_param_hw_array(dma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma8, "8-bit DMA # for SB8 driver.");
struct snd_sb8 {
@@ -70,16 +55,7 @@ static irqreturn_t snd_sb8_interrupt(int irq, void *dev_id)
}
}
-static void snd_sb8_free(struct snd_card *card)
-{
- struct snd_sb8 *acard = card->private_data;
-
- if (acard == NULL)
- return;
- release_and_free_resource(acard->fm_res);
-}
-
-static int __devinit snd_sb8_match(struct device *pdev, unsigned int dev)
+static int snd_sb8_match(struct device *pdev, unsigned int dev)
{
if (!enable[dev])
return 0;
@@ -94,7 +70,7 @@ static int __devinit snd_sb8_match(struct device *pdev, unsigned int dev)
return 1;
}
-static int __devinit snd_sb8_probe(struct device *pdev, unsigned int dev)
+static int snd_sb8_probe(struct device *pdev, unsigned int dev)
{
struct snd_sb *chip;
struct snd_card *card;
@@ -102,27 +78,29 @@ static int __devinit snd_sb8_probe(struct device *pdev, unsigned int dev)
struct snd_opl3 *opl3;
int err;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct snd_sb8), &card);
+ err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_sb8), &card);
if (err < 0)
return err;
acard = card->private_data;
- card->private_free = snd_sb8_free;
- /* block the 0x388 port to avoid PnP conflicts */
- acard->fm_res = request_region(0x388, 4, "SoundBlaster FM");
+ /*
+ * Block the 0x388 port to avoid PnP conflicts.
+ * No need to check this value after request_region,
+ * as we never do anything with it.
+ */
+ acard->fm_res = devm_request_region(card->dev, 0x388, 4,
+ "SoundBlaster FM");
if (port[dev] != SNDRV_AUTO_PORT) {
- if ((err = snd_sbdsp_create(card, port[dev], irq[dev],
- snd_sb8_interrupt,
- dma8[dev],
- -1,
- SB_HW_AUTO,
- &chip)) < 0)
- goto _err;
+ err = snd_sbdsp_create(card, port[dev], irq[dev],
+ snd_sb8_interrupt, dma8[dev],
+ -1, SB_HW_AUTO, &chip);
+ if (err < 0)
+ return err;
} else {
/* auto-probe legacy ports */
- static unsigned long possible_ports[] = {
+ static const unsigned long possible_ports[] = {
0x220, 0x240, 0x260,
};
int i;
@@ -139,77 +117,65 @@ static int __devinit snd_sb8_probe(struct device *pdev, unsigned int dev)
break;
}
}
- if (i >= ARRAY_SIZE(possible_ports)) {
- err = -EINVAL;
- goto _err;
- }
+ if (i >= ARRAY_SIZE(possible_ports))
+ return -EINVAL;
}
acard->chip = chip;
if (chip->hardware >= SB_HW_16) {
if (chip->hardware == SB_HW_ALS100)
- snd_printk(KERN_WARNING "ALS100 chip detected at 0x%lx, try snd-als100 module\n",
- port[dev]);
+ dev_warn(pdev, "ALS100 chip detected at 0x%lx, try snd-als100 module\n",
+ port[dev]);
else
- snd_printk(KERN_WARNING "SB 16 chip detected at 0x%lx, try snd-sb16 module\n",
- port[dev]);
- err = -ENODEV;
- goto _err;
+ dev_warn(pdev, "SB 16 chip detected at 0x%lx, try snd-sb16 module\n",
+ port[dev]);
+ return -ENODEV;
}
- if ((err = snd_sb8dsp_pcm(chip, 0, NULL)) < 0)
- goto _err;
+ err = snd_sb8dsp_pcm(chip, 0);
+ if (err < 0)
+ return err;
- if ((err = snd_sbmixer_new(chip)) < 0)
- goto _err;
+ err = snd_sbmixer_new(chip);
+ if (err < 0)
+ return err;
if (chip->hardware == SB_HW_10 || chip->hardware == SB_HW_20) {
- if ((err = snd_opl3_create(card, chip->port + 8, 0,
- OPL3_HW_AUTO, 1,
- &opl3)) < 0) {
- snd_printk(KERN_WARNING "sb8: no OPL device at 0x%lx\n", chip->port + 8);
- }
+ err = snd_opl3_create(card, chip->port + 8, 0,
+ OPL3_HW_AUTO, 1, &opl3);
+ if (err < 0)
+ dev_warn(pdev, "sb8: no OPL device at 0x%lx\n", chip->port + 8);
} else {
- if ((err = snd_opl3_create(card, chip->port, chip->port + 2,
- OPL3_HW_AUTO, 1,
- &opl3)) < 0) {
- snd_printk(KERN_WARNING "sb8: no OPL device at 0x%lx-0x%lx\n",
+ err = snd_opl3_create(card, chip->port, chip->port + 2,
+ OPL3_HW_AUTO, 1, &opl3);
+ if (err < 0) {
+ dev_warn(pdev, "sb8: no OPL device at 0x%lx-0x%lx\n",
chip->port, chip->port + 2);
}
}
if (err >= 0) {
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0)
- goto _err;
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
+ return err;
}
- if ((err = snd_sb8dsp_midi(chip, 0, NULL)) < 0)
- goto _err;
+ err = snd_sb8dsp_midi(chip, 0);
+ if (err < 0)
+ return err;
- strcpy(card->driver, chip->hardware == SB_HW_PRO ? "SB Pro" : "SB8");
- strcpy(card->shortname, chip->name);
+ strscpy(card->driver, chip->hardware == SB_HW_PRO ? "SB Pro" : "SB8");
+ strscpy(card->shortname, chip->name);
sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
chip->name,
chip->port,
irq[dev], dma8[dev]);
- snd_card_set_dev(card, pdev);
-
- if ((err = snd_card_register(card)) < 0)
- goto _err;
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
dev_set_drvdata(pdev, card);
return 0;
-
- _err:
- snd_card_free(card);
- return err;
-}
-
-static int __devexit snd_sb8_remove(struct device *pdev, unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(pdev));
- dev_set_drvdata(pdev, NULL);
- return 0;
}
#ifdef CONFIG_PM
@@ -221,7 +187,6 @@ static int snd_sb8_suspend(struct device *dev, unsigned int n,
struct snd_sb *chip = acard->chip;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_sbmixer_suspend(chip);
return 0;
}
@@ -244,7 +209,6 @@ static int snd_sb8_resume(struct device *dev, unsigned int n)
static struct isa_driver snd_sb8_driver = {
.match = snd_sb8_match,
.probe = snd_sb8_probe,
- .remove = __devexit_p(snd_sb8_remove),
#ifdef CONFIG_PM
.suspend = snd_sb8_suspend,
.resume = snd_sb8_resume,
@@ -254,15 +218,4 @@ static struct isa_driver snd_sb8_driver = {
},
};
-static int __init alsa_card_sb8_init(void)
-{
- return isa_register_driver(&snd_sb8_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_sb8_exit(void)
-{
- isa_unregister_driver(&snd_sb8_driver);
-}
-
-module_init(alsa_card_sb8_init)
-module_exit(alsa_card_sb8_exit)
+module_isa_driver(snd_sb8_driver, SNDRV_CARDS);
diff --git a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c
index 7d84c9f34dc9..a4b5725255cf 100644
--- a/sound/isa/sb/sb8_main.c
+++ b/sound/isa/sb/sb8_main.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Uros Bizjak <uros@kss-loka.si>
@@ -5,21 +6,6 @@
* Routines for control of 8-bit SoundBlaster cards and clones
* Please note: I don't have access to old SB8 soundcards.
*
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
* --
*
* Thu Apr 29 20:36:17 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk>
@@ -30,10 +16,11 @@
* Cleaned up and rewrote lowlevel routines.
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <asm/dma.h>
#include <linux/init.h>
#include <linux/time.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/sb.h>
@@ -45,19 +32,19 @@ MODULE_LICENSE("GPL");
#define SB8_DEN(v) ((SB8_CLOCK + (v) / 2) / (v))
#define SB8_RATE(v) (SB8_CLOCK / SB8_DEN(v))
-static struct snd_ratnum clock = {
+static const struct snd_ratnum clock = {
.num = SB8_CLOCK,
.den_min = 1,
.den_max = 256,
.den_step = 1,
};
-static struct snd_pcm_hw_constraint_ratnums hw_constraints_clock = {
+static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clock = {
.nrats = 1,
.rats = &clock,
};
-static struct snd_ratnum stereo_clocks[] = {
+static const struct snd_ratnum stereo_clocks[] = {
{
.num = SB8_CLOCK,
.den_min = SB8_DEN(22050),
@@ -102,7 +89,6 @@ static int snd_sb8_hw_constraint_channels_rate(struct snd_pcm_hw_params *params,
static int snd_sb8_playback_prepare(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int mixreg, rate, size, count;
@@ -129,13 +115,13 @@ static int snd_sb8_playback_prepare(struct snd_pcm_substream *substream)
chip->playback_format = SB_DSP_HI_OUTPUT_AUTO;
break;
}
- /* fallthru */
+ fallthrough;
case SB_HW_201:
if (rate > 23000) {
chip->playback_format = SB_DSP_HI_OUTPUT_AUTO;
break;
}
- /* fallthru */
+ fallthrough;
case SB_HW_20:
chip->playback_format = SB_DSP_LO_OUTPUT_AUTO;
break;
@@ -155,48 +141,48 @@ static int snd_sb8_playback_prepare(struct snd_pcm_substream *substream)
}
size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream);
count = chip->p_period_size = snd_pcm_lib_period_bytes(substream);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON);
- if (chip->hardware == SB_HW_JAZZ16)
- snd_sbdsp_command(chip, format);
- else if (stereo) {
- /* set playback stereo mode */
- spin_lock(&chip->mixer_lock);
- mixreg = snd_sbmixer_read(chip, SB_DSP_STEREO_SW);
- snd_sbmixer_write(chip, SB_DSP_STEREO_SW, mixreg | 0x02);
- spin_unlock(&chip->mixer_lock);
-
- /* Soundblaster hardware programming reference guide, 3-23 */
- snd_sbdsp_command(chip, SB_DSP_DMA8_EXIT);
- runtime->dma_area[0] = 0x80;
- snd_dma_program(dma, runtime->dma_addr, 1, DMA_MODE_WRITE);
- /* force interrupt */
- snd_sbdsp_command(chip, SB_DSP_OUTPUT);
- snd_sbdsp_command(chip, 0);
- snd_sbdsp_command(chip, 0);
- }
- snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
- if (stereo) {
- snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
- spin_lock(&chip->mixer_lock);
- /* save output filter status and turn it off */
- mixreg = snd_sbmixer_read(chip, SB_DSP_PLAYBACK_FILT);
- snd_sbmixer_write(chip, SB_DSP_PLAYBACK_FILT, mixreg | 0x20);
- spin_unlock(&chip->mixer_lock);
- /* just use force_mode16 for temporary storate... */
- chip->force_mode16 = mixreg;
- } else {
- snd_sbdsp_command(chip, 256 - runtime->rate_den);
- }
- if (chip->playback_format != SB_DSP_OUTPUT) {
- if (chip->mode & SB_MODE_PLAYBACK_16)
- count /= 2;
- count--;
- snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
- snd_sbdsp_command(chip, count & 0xff);
- snd_sbdsp_command(chip, count >> 8);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON);
+ if (chip->hardware == SB_HW_JAZZ16)
+ snd_sbdsp_command(chip, format);
+ else if (stereo) {
+ /* set playback stereo mode */
+ scoped_guard(spinlock, &chip->mixer_lock) {
+ mixreg = snd_sbmixer_read(chip, SB_DSP_STEREO_SW);
+ snd_sbmixer_write(chip, SB_DSP_STEREO_SW, mixreg | 0x02);
+ }
+
+ /* Soundblaster hardware programming reference guide, 3-23 */
+ snd_sbdsp_command(chip, SB_DSP_DMA8_EXIT);
+ runtime->dma_area[0] = 0x80;
+ snd_dma_program(dma, runtime->dma_addr, 1, DMA_MODE_WRITE);
+ /* force interrupt */
+ snd_sbdsp_command(chip, SB_DSP_OUTPUT);
+ snd_sbdsp_command(chip, 0);
+ snd_sbdsp_command(chip, 0);
+ }
+ snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
+ if (stereo) {
+ snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
+ scoped_guard(spinlock, &chip->mixer_lock) {
+ /* save output filter status and turn it off */
+ mixreg = snd_sbmixer_read(chip, SB_DSP_PLAYBACK_FILT);
+ snd_sbmixer_write(chip, SB_DSP_PLAYBACK_FILT, mixreg | 0x20);
+ }
+ /* just use force_mode16 for temporary storate... */
+ chip->force_mode16 = mixreg;
+ } else {
+ snd_sbdsp_command(chip, 256 - runtime->rate_den);
+ }
+ if (chip->playback_format != SB_DSP_OUTPUT) {
+ if (chip->mode & SB_MODE_PLAYBACK_16)
+ count /= 2;
+ count--;
+ snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
+ snd_sbdsp_command(chip, count & 0xff);
+ snd_sbdsp_command(chip, count >> 8);
+ }
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_dma_program(dma, runtime->dma_addr,
size, DMA_MODE_WRITE | DMA_AUTOINIT);
return 0;
@@ -205,11 +191,10 @@ static int snd_sb8_playback_prepare(struct snd_pcm_substream *substream)
static int snd_sb8_playback_trigger(struct snd_pcm_substream *substream,
int cmd)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
unsigned int count;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
snd_sbdsp_command(chip, chip->playback_format);
@@ -224,35 +209,20 @@ static int snd_sb8_playback_trigger(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
snd_sbdsp_reset(chip);
if (runtime->channels > 1) {
- spin_lock(&chip->mixer_lock);
+ guard(spinlock)(&chip->mixer_lock);
/* restore output filter and set hardware to mono mode */
snd_sbmixer_write(chip, SB_DSP_STEREO_SW, chip->force_mode16 & ~0x02);
- spin_unlock(&chip->mixer_lock);
}
} else {
snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
}
snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return 0;
-}
-
-static int snd_sb8_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_sb8_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_lib_free_pages(substream);
return 0;
}
static int snd_sb8_capture_prepare(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int mixreg, rate, size, count;
@@ -286,7 +256,7 @@ static int snd_sb8_capture_prepare(struct snd_pcm_substream *substream)
chip->capture_format = SB_DSP_HI_INPUT_AUTO;
break;
}
- /* fallthru */
+ fallthrough;
case SB_HW_20:
chip->capture_format = SB_DSP_LO_INPUT_AUTO;
break;
@@ -306,34 +276,34 @@ static int snd_sb8_capture_prepare(struct snd_pcm_substream *substream)
}
size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream);
count = chip->c_period_size = snd_pcm_lib_period_bytes(substream);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
- if (chip->hardware == SB_HW_JAZZ16)
- snd_sbdsp_command(chip, format);
- else if (stereo)
- snd_sbdsp_command(chip, SB_DSP_STEREO_8BIT);
- snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
- if (stereo) {
- snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
- spin_lock(&chip->mixer_lock);
- /* save input filter status and turn it off */
- mixreg = snd_sbmixer_read(chip, SB_DSP_CAPTURE_FILT);
- snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, mixreg | 0x20);
- spin_unlock(&chip->mixer_lock);
- /* just use force_mode16 for temporary storate... */
- chip->force_mode16 = mixreg;
- } else {
- snd_sbdsp_command(chip, 256 - runtime->rate_den);
- }
- if (chip->capture_format != SB_DSP_INPUT) {
- if (chip->mode & SB_MODE_PLAYBACK_16)
- count /= 2;
- count--;
- snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
- snd_sbdsp_command(chip, count & 0xff);
- snd_sbdsp_command(chip, count >> 8);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
+ if (chip->hardware == SB_HW_JAZZ16)
+ snd_sbdsp_command(chip, format);
+ else if (stereo)
+ snd_sbdsp_command(chip, SB_DSP_STEREO_8BIT);
+ snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
+ if (stereo) {
+ snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
+ scoped_guard(spinlock, &chip->mixer_lock) {
+ /* save input filter status and turn it off */
+ mixreg = snd_sbmixer_read(chip, SB_DSP_CAPTURE_FILT);
+ snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, mixreg | 0x20);
+ }
+ /* just use force_mode16 for temporary storate... */
+ chip->force_mode16 = mixreg;
+ } else {
+ snd_sbdsp_command(chip, 256 - runtime->rate_den);
+ }
+ if (chip->capture_format != SB_DSP_INPUT) {
+ if (chip->mode & SB_MODE_PLAYBACK_16)
+ count /= 2;
+ count--;
+ snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
+ snd_sbdsp_command(chip, count & 0xff);
+ snd_sbdsp_command(chip, count >> 8);
+ }
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_dma_program(dma, runtime->dma_addr,
size, DMA_MODE_READ | DMA_AUTOINIT);
return 0;
@@ -342,11 +312,10 @@ static int snd_sb8_capture_prepare(struct snd_pcm_substream *substream)
static int snd_sb8_capture_trigger(struct snd_pcm_substream *substream,
int cmd)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
unsigned int count;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
snd_sbdsp_command(chip, chip->capture_format);
@@ -362,9 +331,9 @@ static int snd_sb8_capture_trigger(struct snd_pcm_substream *substream,
snd_sbdsp_reset(chip);
if (runtime->channels > 1) {
/* restore input filter status */
- spin_lock(&chip->mixer_lock);
- snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, chip->force_mode16);
- spin_unlock(&chip->mixer_lock);
+ scoped_guard(spinlock, &chip->mixer_lock) {
+ snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, chip->force_mode16);
+ }
/* set hardware to mono mode */
snd_sbdsp_command(chip, SB_DSP_MONO_8BIT);
}
@@ -373,24 +342,21 @@ static int snd_sb8_capture_trigger(struct snd_pcm_substream *substream,
}
snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
irqreturn_t snd_sb8dsp_interrupt(struct snd_sb *chip)
{
struct snd_pcm_substream *substream;
- struct snd_pcm_runtime *runtime;
snd_sb_ack_8bit(chip);
switch (chip->mode) {
case SB_MODE_PLAYBACK_16: /* ok.. playback is active */
if (chip->hardware != SB_HW_JAZZ16)
break;
- /* fallthru */
+ fallthrough;
case SB_MODE_PLAYBACK_8:
substream = chip->playback_substream;
- runtime = substream->runtime;
if (chip->playback_format == SB_DSP_OUTPUT)
snd_sb8_playback_trigger(substream, SNDRV_PCM_TRIGGER_START);
snd_pcm_period_elapsed(substream);
@@ -398,10 +364,9 @@ irqreturn_t snd_sb8dsp_interrupt(struct snd_sb *chip)
case SB_MODE_CAPTURE_16:
if (chip->hardware != SB_HW_JAZZ16)
break;
- /* fallthru */
+ fallthrough;
case SB_MODE_CAPTURE_8:
substream = chip->capture_substream;
- runtime = substream->runtime;
if (chip->capture_format == SB_DSP_INPUT)
snd_sb8_capture_trigger(substream, SNDRV_PCM_TRIGGER_START);
snd_pcm_period_elapsed(substream);
@@ -446,7 +411,7 @@ static snd_pcm_uframes_t snd_sb8_capture_pointer(struct snd_pcm_substream *subst
*/
-static struct snd_pcm_hardware snd_sb8_playback =
+static const struct snd_pcm_hardware snd_sb8_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -465,7 +430,7 @@ static struct snd_pcm_hardware snd_sb8_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_sb8_capture =
+static const struct snd_pcm_hardware snd_sb8_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -492,15 +457,12 @@ static int snd_sb8_open(struct snd_pcm_substream *substream)
{
struct snd_sb *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long flags;
- spin_lock_irqsave(&chip->open_lock, flags);
- if (chip->open) {
- spin_unlock_irqrestore(&chip->open_lock, flags);
- return -EAGAIN;
+ scoped_guard(spinlock_irqsave, &chip->open_lock) {
+ if (chip->open)
+ return -EAGAIN;
+ chip->open |= SB_OPEN_PCM;
}
- chip->open |= SB_OPEN_PCM;
- spin_unlock_irqrestore(&chip->open_lock, flags);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
chip->playback_substream = substream;
runtime->hw = snd_sb8_playback;
@@ -534,6 +496,7 @@ static int snd_sb8_open(struct snd_pcm_substream *substream)
} else {
runtime->hw.rate_max = 15000;
}
+ break;
default:
break;
}
@@ -552,18 +515,16 @@ static int snd_sb8_open(struct snd_pcm_substream *substream)
static int snd_sb8_close(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip = snd_pcm_substream_chip(substream);
chip->playback_substream = NULL;
chip->capture_substream = NULL;
- spin_lock_irqsave(&chip->open_lock, flags);
+ guard(spinlock_irqsave)(&chip->open_lock);
chip->open &= ~SB_OPEN_PCM;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
chip->mode &= ~SB_MODE_PLAYBACK;
else
chip->mode &= ~SB_MODE_CAPTURE;
- spin_unlock_irqrestore(&chip->open_lock, flags);
return 0;
}
@@ -571,38 +532,31 @@ static int snd_sb8_close(struct snd_pcm_substream *substream)
* Initialization part
*/
-static struct snd_pcm_ops snd_sb8_playback_ops = {
+static const struct snd_pcm_ops snd_sb8_playback_ops = {
.open = snd_sb8_open,
.close = snd_sb8_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_sb8_hw_params,
- .hw_free = snd_sb8_hw_free,
.prepare = snd_sb8_playback_prepare,
.trigger = snd_sb8_playback_trigger,
.pointer = snd_sb8_playback_pointer,
};
-static struct snd_pcm_ops snd_sb8_capture_ops = {
+static const struct snd_pcm_ops snd_sb8_capture_ops = {
.open = snd_sb8_open,
.close = snd_sb8_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_sb8_hw_params,
- .hw_free = snd_sb8_hw_free,
.prepare = snd_sb8_capture_prepare,
.trigger = snd_sb8_capture_trigger,
.pointer = snd_sb8_capture_pointer,
};
-int snd_sb8dsp_pcm(struct snd_sb *chip, int device, struct snd_pcm ** rpcm)
+int snd_sb8dsp_pcm(struct snd_sb *chip, int device)
{
struct snd_card *card = chip->card;
struct snd_pcm *pcm;
int err;
size_t max_prealloc = 64 * 1024;
- if (rpcm)
- *rpcm = NULL;
- if ((err = snd_pcm_new(card, "SB8 DSP", device, 1, 1, &pcm)) < 0)
+ err = snd_pcm_new(card, "SB8 DSP", device, 1, 1, &pcm);
+ if (err < 0)
return err;
sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff);
pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
@@ -613,12 +567,9 @@ int snd_sb8dsp_pcm(struct snd_sb *chip, int device, struct snd_pcm ** rpcm)
if (chip->dma8 > 3 || chip->dma16 >= 0)
max_prealloc = 128 * 1024;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
- 64*1024, max_prealloc);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, 64*1024, max_prealloc);
- if (rpcm)
- *rpcm = pcm;
return 0;
}
@@ -627,19 +578,3 @@ EXPORT_SYMBOL(snd_sb8dsp_interrupt);
/* sb8_midi.c */
EXPORT_SYMBOL(snd_sb8dsp_midi_interrupt);
EXPORT_SYMBOL(snd_sb8dsp_midi);
-
-/*
- * INIT part
- */
-
-static int __init alsa_sb8_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_sb8_exit(void)
-{
-}
-
-module_init(alsa_sb8_init)
-module_exit(alsa_sb8_exit)
diff --git a/sound/isa/sb/sb8_midi.c b/sound/isa/sb/sb8_midi.c
index 988a8b73475f..1d41f2470697 100644
--- a/sound/isa/sb/sb8_midi.c
+++ b/sound/isa/sb/sb8_midi.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of SoundBlaster cards - MIDI interface
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
* --
*
* Sun May 9 22:54:38 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk>
@@ -26,7 +13,8 @@
* Added full duplex UART mode for DSP version 2.0 and later.
*/
-#include <asm/io.h>
+#include <linux/io.h>
+#include <linux/string.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/sb.h>
@@ -47,7 +35,7 @@ irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip)
return IRQ_NONE;
}
- spin_lock(&chip->midi_input_lock);
+ guard(spinlock)(&chip->midi_input_lock);
while (max-- > 0) {
if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
byte = inb(SBP(chip, READ));
@@ -56,107 +44,90 @@ irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip)
}
}
}
- spin_unlock(&chip->midi_input_lock);
return IRQ_HANDLED;
}
static int snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip;
unsigned int valid_open_flags;
chip = substream->rmidi->private_data;
valid_open_flags = chip->hardware >= SB_HW_20
? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0;
- spin_lock_irqsave(&chip->open_lock, flags);
- if (chip->open & ~valid_open_flags) {
- spin_unlock_irqrestore(&chip->open_lock, flags);
- return -EAGAIN;
- }
- chip->open |= SB_OPEN_MIDI_INPUT;
- chip->midi_substream_input = substream;
- if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
- spin_unlock_irqrestore(&chip->open_lock, flags);
- snd_sbdsp_reset(chip); /* reset DSP */
- if (chip->hardware >= SB_HW_20)
- snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
- } else {
- spin_unlock_irqrestore(&chip->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->open_lock) {
+ if (chip->open & ~valid_open_flags)
+ return -EAGAIN;
+ chip->open |= SB_OPEN_MIDI_INPUT;
+ chip->midi_substream_input = substream;
+ if (chip->open & SB_OPEN_MIDI_OUTPUT)
+ return 0;
}
+ snd_sbdsp_reset(chip); /* reset DSP */
+ if (chip->hardware >= SB_HW_20)
+ snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
return 0;
}
static int snd_sb8dsp_midi_output_open(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip;
unsigned int valid_open_flags;
chip = substream->rmidi->private_data;
valid_open_flags = chip->hardware >= SB_HW_20
? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0;
- spin_lock_irqsave(&chip->open_lock, flags);
- if (chip->open & ~valid_open_flags) {
- spin_unlock_irqrestore(&chip->open_lock, flags);
- return -EAGAIN;
- }
- chip->open |= SB_OPEN_MIDI_OUTPUT;
- chip->midi_substream_output = substream;
- if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
- spin_unlock_irqrestore(&chip->open_lock, flags);
- snd_sbdsp_reset(chip); /* reset DSP */
- if (chip->hardware >= SB_HW_20)
- snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
- } else {
- spin_unlock_irqrestore(&chip->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->open_lock) {
+ if (chip->open & ~valid_open_flags)
+ return -EAGAIN;
+ chip->open |= SB_OPEN_MIDI_OUTPUT;
+ chip->midi_substream_output = substream;
+ if (chip->open & SB_OPEN_MIDI_INPUT)
+ return 0;
}
+ snd_sbdsp_reset(chip); /* reset DSP */
+ if (chip->hardware >= SB_HW_20)
+ snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
return 0;
}
static int snd_sb8dsp_midi_input_close(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip;
chip = substream->rmidi->private_data;
- spin_lock_irqsave(&chip->open_lock, flags);
- chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER);
- chip->midi_substream_input = NULL;
- if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
- spin_unlock_irqrestore(&chip->open_lock, flags);
- snd_sbdsp_reset(chip); /* reset DSP */
- } else {
- spin_unlock_irqrestore(&chip->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->open_lock) {
+ chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER);
+ chip->midi_substream_input = NULL;
+ if (chip->open & SB_OPEN_MIDI_OUTPUT)
+ return 0;
}
+ snd_sbdsp_reset(chip); /* reset DSP */
return 0;
}
static int snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip;
chip = substream->rmidi->private_data;
- spin_lock_irqsave(&chip->open_lock, flags);
- chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER);
- chip->midi_substream_output = NULL;
- if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
- spin_unlock_irqrestore(&chip->open_lock, flags);
- snd_sbdsp_reset(chip); /* reset DSP */
- } else {
- spin_unlock_irqrestore(&chip->open_lock, flags);
+ timer_delete_sync(&chip->midi_timer);
+ scoped_guard(spinlock_irqsave, &chip->open_lock) {
+ chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER);
+ chip->midi_substream_output = NULL;
+ if (chip->open & SB_OPEN_MIDI_INPUT)
+ return 0;
}
+ snd_sbdsp_reset(chip); /* reset DSP */
return 0;
}
static void snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct snd_sb *chip;
chip = substream->rmidi->private_data;
- spin_lock_irqsave(&chip->open_lock, flags);
+ guard(spinlock_irqsave)(&chip->open_lock);
if (up) {
if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) {
if (chip->hardware < SB_HW_20)
@@ -170,12 +141,10 @@ static void snd_sb8dsp_midi_input_trigger(struct snd_rawmidi_substream *substrea
chip->open &= ~SB_OPEN_MIDI_INPUT_TRIGGER;
}
}
- spin_unlock_irqrestore(&chip->open_lock, flags);
}
static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
struct snd_sb *chip;
char byte;
int max = 32;
@@ -183,11 +152,10 @@ static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream
/* how big is Tx FIFO? */
chip = substream->rmidi->private_data;
while (max-- > 0) {
- spin_lock_irqsave(&chip->open_lock, flags);
+ guard(spinlock_irqsave)(&chip->open_lock);
if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) {
chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
- del_timer(&chip->midi_timer);
- spin_unlock_irqrestore(&chip->open_lock, flags);
+ timer_delete(&chip->midi_timer);
break;
}
if (chip->hardware >= SB_HW_20) {
@@ -196,7 +164,6 @@ static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream
;
if (timeout == 0) {
/* Tx FIFO full - try again later */
- spin_unlock_irqrestore(&chip->open_lock, flags);
break;
}
outb(byte, SBP(chip, WRITE));
@@ -205,82 +172,72 @@ static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream
snd_sbdsp_command(chip, byte);
}
snd_rawmidi_transmit_ack(substream, 1);
- spin_unlock_irqrestore(&chip->open_lock, flags);
}
}
-static void snd_sb8dsp_midi_output_timer(unsigned long data)
+static void snd_sb8dsp_midi_output_timer(struct timer_list *t)
{
- struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *) data;
- struct snd_sb * chip = substream->rmidi->private_data;
- unsigned long flags;
+ struct snd_sb *chip = timer_container_of(chip, t, midi_timer);
+ struct snd_rawmidi_substream *substream = chip->midi_substream_output;
- spin_lock_irqsave(&chip->open_lock, flags);
- chip->midi_timer.expires = 1 + jiffies;
- add_timer(&chip->midi_timer);
- spin_unlock_irqrestore(&chip->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->open_lock) {
+ mod_timer(&chip->midi_timer, 1 + jiffies);
+ }
snd_sb8dsp_midi_output_write(substream);
}
static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct snd_sb *chip;
chip = substream->rmidi->private_data;
- spin_lock_irqsave(&chip->open_lock, flags);
- if (up) {
- if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) {
- init_timer(&chip->midi_timer);
- chip->midi_timer.function = snd_sb8dsp_midi_output_timer;
- chip->midi_timer.data = (unsigned long) substream;
- chip->midi_timer.expires = 1 + jiffies;
- add_timer(&chip->midi_timer);
- chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER;
- }
- } else {
- if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) {
- chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
+ scoped_guard(spinlock_irqsave, &chip->open_lock) {
+ if (up) {
+ if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) {
+ mod_timer(&chip->midi_timer, 1 + jiffies);
+ chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER;
+ }
+ } else {
+ if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) {
+ chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
+ }
}
}
- spin_unlock_irqrestore(&chip->open_lock, flags);
if (up)
snd_sb8dsp_midi_output_write(substream);
}
-static struct snd_rawmidi_ops snd_sb8dsp_midi_output =
+static const struct snd_rawmidi_ops snd_sb8dsp_midi_output =
{
.open = snd_sb8dsp_midi_output_open,
.close = snd_sb8dsp_midi_output_close,
.trigger = snd_sb8dsp_midi_output_trigger,
};
-static struct snd_rawmidi_ops snd_sb8dsp_midi_input =
+static const struct snd_rawmidi_ops snd_sb8dsp_midi_input =
{
.open = snd_sb8dsp_midi_input_open,
.close = snd_sb8dsp_midi_input_close,
.trigger = snd_sb8dsp_midi_input_trigger,
};
-int snd_sb8dsp_midi(struct snd_sb *chip, int device, struct snd_rawmidi ** rrawmidi)
+int snd_sb8dsp_midi(struct snd_sb *chip, int device)
{
struct snd_rawmidi *rmidi;
int err;
- if (rrawmidi)
- *rrawmidi = NULL;
- if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0)
+ err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi);
+ if (err < 0)
return err;
- strcpy(rmidi->name, "SB8 MIDI");
+ strscpy(rmidi->name, "SB8 MIDI");
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT;
if (chip->hardware >= SB_HW_20)
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
rmidi->private_data = chip;
+ timer_setup(&chip->midi_timer, snd_sb8dsp_midi_output_timer, 0);
chip->rmidi = rmidi;
- if (rrawmidi)
- *rrawmidi = rmidi;
return 0;
}
diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c
index eae6c1c0eff9..f2848559e6da 100644
--- a/sound/isa/sb/sb_common.c
+++ b/sound/isa/sb/sb_common.c
@@ -1,23 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Uros Bizjak <uros@kss-loka.si>
*
* Lowlevel routines for control of Sound Blaster cards
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
@@ -25,11 +11,12 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/sb.h>
#include <sound/initval.h>
-#include <asm/io.h>
#include <asm/dma.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -44,14 +31,14 @@ int snd_sbdsp_command(struct snd_sb *chip, unsigned char val)
{
int i;
#ifdef IO_DEBUG
- snd_printk(KERN_DEBUG "command 0x%x\n", val);
+ dev_dbg(chip->card->dev, "command 0x%x\n", val);
#endif
for (i = BUSY_LOOPS; i; i--)
if ((inb(SBP(chip, STATUS)) & 0x80) == 0) {
outb(val, SBP(chip, COMMAND));
return 1;
}
- snd_printd("%s [0x%lx]: timeout (0x%x)\n", __func__, chip->port, val);
+ dev_dbg(chip->card->dev, "%s [0x%lx]: timeout (0x%x)\n", __func__, chip->port, val);
return 0;
}
@@ -63,12 +50,12 @@ int snd_sbdsp_get_byte(struct snd_sb *chip)
if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
val = inb(SBP(chip, READ));
#ifdef IO_DEBUG
- snd_printk(KERN_DEBUG "get_byte 0x%x\n", val);
+ dev_dbg(chip->card->dev, "get_byte 0x%x\n", val);
#endif
return val;
}
}
- snd_printd("%s [0x%lx]: timeout\n", __func__, chip->port);
+ dev_dbg(chip->card->dev, "%s [0x%lx]: timeout\n", __func__, chip->port);
return -ENODEV;
}
@@ -87,13 +74,14 @@ int snd_sbdsp_reset(struct snd_sb *chip)
else
break;
}
- snd_printdd("%s [0x%lx] failed...\n", __func__, chip->port);
+ if (chip->card)
+ dev_dbg(chip->card->dev, "%s [0x%lx] failed...\n", __func__, chip->port);
return -ENODEV;
}
static int snd_sbdsp_version(struct snd_sb * chip)
{
- unsigned int result = -ENODEV;
+ unsigned int result;
snd_sbdsp_command(chip, SB_DSP_GET_VERSION);
result = (short) snd_sbdsp_get_byte(chip) << 8;
@@ -106,27 +94,22 @@ static int snd_sbdsp_probe(struct snd_sb * chip)
int version;
int major, minor;
char *str;
- unsigned long flags;
/*
* initialization sequence
*/
- spin_lock_irqsave(&chip->reg_lock, flags);
- if (snd_sbdsp_reset(chip) < 0) {
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return -ENODEV;
- }
- version = snd_sbdsp_version(chip);
- if (version < 0) {
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return -ENODEV;
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ if (snd_sbdsp_reset(chip) < 0)
+ return -ENODEV;
+ version = snd_sbdsp_version(chip);
+ if (version < 0)
+ return -ENODEV;
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
major = version >> 8;
minor = version & 0xff;
- snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n",
- chip->port, major, minor);
+ dev_dbg(chip->card->dev, "SB [0x%lx]: DSP chip found, version = %i.%i\n",
+ chip->port, major, minor);
switch (chip->hardware) {
case SB_HW_AUTO:
@@ -153,8 +136,8 @@ static int snd_sbdsp_probe(struct snd_sb * chip)
str = "16";
break;
default:
- snd_printk(KERN_INFO "SB [0x%lx]: unknown DSP chip version %i.%i\n",
- chip->port, major, minor);
+ dev_info(chip->card->dev, "SB [0x%lx]: unknown DSP chip version %i.%i\n",
+ chip->port, major, minor);
return -ENODEV;
}
break;
@@ -181,32 +164,6 @@ static int snd_sbdsp_probe(struct snd_sb * chip)
return 0;
}
-static int snd_sbdsp_free(struct snd_sb *chip)
-{
- if (chip->res_port)
- release_and_free_resource(chip->res_port);
- if (chip->irq >= 0)
- free_irq(chip->irq, (void *) chip);
-#ifdef CONFIG_ISA
- if (chip->dma8 >= 0) {
- disable_dma(chip->dma8);
- free_dma(chip->dma8);
- }
- if (chip->dma16 >= 0 && chip->dma16 != chip->dma8) {
- disable_dma(chip->dma16);
- free_dma(chip->dma16);
- }
-#endif
- kfree(chip);
- return 0;
-}
-
-static int snd_sbdsp_dev_free(struct snd_device *device)
-{
- struct snd_sb *chip = device->device_data;
- return snd_sbdsp_free(chip);
-}
-
int snd_sbdsp_create(struct snd_card *card,
unsigned long port,
int irq,
@@ -218,15 +175,12 @@ int snd_sbdsp_create(struct snd_card *card,
{
struct snd_sb *chip;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_sbdsp_dev_free,
- };
if (snd_BUG_ON(!r_chip))
return -EINVAL;
*r_chip = NULL;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL)
+ chip = devm_kzalloc(card->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
return -ENOMEM;
spin_lock_init(&chip->reg_lock);
spin_lock_init(&chip->open_lock);
@@ -237,30 +191,31 @@ int snd_sbdsp_create(struct snd_card *card,
chip->dma16 = -1;
chip->port = port;
- if (request_irq(irq, irq_handler,
- (hardware == SB_HW_ALS4000 ||
- hardware == SB_HW_CS5530) ?
- IRQF_SHARED : IRQF_DISABLED,
- "SoundBlaster", (void *) chip)) {
- snd_printk(KERN_ERR "sb: can't grab irq %d\n", irq);
- snd_sbdsp_free(chip);
+ if (devm_request_irq(card->dev, irq, irq_handler,
+ (hardware == SB_HW_ALS4000 ||
+ hardware == SB_HW_CS5530) ?
+ IRQF_SHARED : 0,
+ "SoundBlaster", (void *) chip)) {
+ dev_err(card->dev, "sb: can't grab irq %d\n", irq);
return -EBUSY;
}
chip->irq = irq;
+ card->sync_irq = chip->irq;
if (hardware == SB_HW_ALS4000)
goto __skip_allocation;
- if ((chip->res_port = request_region(port, 16, "SoundBlaster")) == NULL) {
- snd_printk(KERN_ERR "sb: can't grab port 0x%lx\n", port);
- snd_sbdsp_free(chip);
+ chip->res_port = devm_request_region(card->dev, port, 16,
+ "SoundBlaster");
+ if (!chip->res_port) {
+ dev_err(card->dev, "sb: can't grab port 0x%lx\n", port);
return -EBUSY;
}
#ifdef CONFIG_ISA
- if (dma8 >= 0 && request_dma(dma8, "SoundBlaster - 8bit")) {
- snd_printk(KERN_ERR "sb: can't grab DMA8 %d\n", dma8);
- snd_sbdsp_free(chip);
+ if (dma8 >= 0 && snd_devm_request_dma(card->dev, dma8,
+ "SoundBlaster - 8bit")) {
+ dev_err(card->dev, "sb: can't grab DMA8 %d\n", dma8);
return -EBUSY;
}
chip->dma8 = dma8;
@@ -268,9 +223,9 @@ int snd_sbdsp_create(struct snd_card *card,
if (hardware != SB_HW_ALS100 && (dma16 < 5 || dma16 > 7)) {
/* no duplex */
dma16 = -1;
- } else if (request_dma(dma16, "SoundBlaster - 16bit")) {
- snd_printk(KERN_ERR "sb: can't grab DMA16 %d\n", dma16);
- snd_sbdsp_free(chip);
+ } else if (snd_devm_request_dma(card->dev, dma16,
+ "SoundBlaster - 16bit")) {
+ dev_err(card->dev, "sb: can't grab DMA16 %d\n", dma16);
return -EBUSY;
}
}
@@ -280,14 +235,9 @@ int snd_sbdsp_create(struct snd_card *card,
__skip_allocation:
chip->card = card;
chip->hardware = hardware;
- if ((err = snd_sbdsp_probe(chip)) < 0) {
- snd_sbdsp_free(chip);
- return err;
- }
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_sbdsp_free(chip);
+ err = snd_sbdsp_probe(chip);
+ if (err < 0)
return err;
- }
*r_chip = chip;
return 0;
}
@@ -305,19 +255,3 @@ EXPORT_SYMBOL(snd_sbmixer_add_ctl);
EXPORT_SYMBOL(snd_sbmixer_suspend);
EXPORT_SYMBOL(snd_sbmixer_resume);
#endif
-
-/*
- * INIT part
- */
-
-static int __init alsa_sb_common_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_sb_common_exit(void)
-{
-}
-
-module_init(alsa_sb_common_init)
-module_exit(alsa_sb_common_exit)
diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c
index 6496822c1808..95173b18cee3 100644
--- a/sound/isa/sb/sb_mixer.c
+++ b/sound/isa/sb/sb_mixer.c
@@ -1,26 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for Sound Blaster mixer control
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/string.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/sb.h>
@@ -35,7 +21,7 @@ void snd_sbmixer_write(struct snd_sb *chip, unsigned char reg, unsigned char dat
outb(data, SBP(chip, MIXER_DATA));
udelay(10);
#ifdef IO_DEBUG
- snd_printk(KERN_DEBUG "mixer_write 0x%x 0x%x\n", reg, data);
+ dev_dbg(chip->card->dev, "mixer_write 0x%x 0x%x\n", reg, data);
#endif
}
@@ -48,7 +34,7 @@ unsigned char snd_sbmixer_read(struct snd_sb *chip, unsigned char reg)
result = inb(SBP(chip, MIXER_DATA));
udelay(10);
#ifdef IO_DEBUG
- snd_printk(KERN_DEBUG "mixer_read 0x%x 0x%x\n", reg, result);
+ dev_dbg(chip->card->dev, "mixer_read 0x%x 0x%x\n", reg, result);
#endif
return result;
}
@@ -71,15 +57,13 @@ static int snd_sbmixer_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl
static int snd_sbmixer_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 16) & 0xff;
int mask = (kcontrol->private_value >> 24) & 0xff;
unsigned char val;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
val = (snd_sbmixer_read(sb, reg) >> shift) & mask;
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
ucontrol->value.integer.value[0] = val;
return 0;
}
@@ -87,7 +71,6 @@ static int snd_sbmixer_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_sbmixer_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 16) & 0x07;
int mask = (kcontrol->private_value >> 24) & 0xff;
@@ -95,13 +78,12 @@ static int snd_sbmixer_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
unsigned char val, oval;
val = (ucontrol->value.integer.value[0] & mask) << shift;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
oval = snd_sbmixer_read(sb, reg);
val = (oval & ~(mask << shift)) | val;
change = val != oval;
if (change)
snd_sbmixer_write(sb, reg, val);
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
return change;
}
@@ -123,7 +105,6 @@ static int snd_sbmixer_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl
static int snd_sbmixer_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int left_shift = (kcontrol->private_value >> 16) & 0x07;
@@ -131,10 +112,9 @@ static int snd_sbmixer_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
int mask = (kcontrol->private_value >> 24) & 0xff;
unsigned char left, right;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
left = (snd_sbmixer_read(sb, left_reg) >> left_shift) & mask;
right = (snd_sbmixer_read(sb, right_reg) >> right_shift) & mask;
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
ucontrol->value.integer.value[0] = left;
ucontrol->value.integer.value[1] = right;
return 0;
@@ -143,7 +123,6 @@ static int snd_sbmixer_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int left_shift = (kcontrol->private_value >> 16) & 0x07;
@@ -154,7 +133,7 @@ static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
left = (ucontrol->value.integer.value[0] & mask) << left_shift;
right = (ucontrol->value.integer.value[1] & mask) << right_shift;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
if (left_reg == right_reg) {
oleft = snd_sbmixer_read(sb, left_reg);
left = (oleft & ~((mask << left_shift) | (mask << right_shift))) | left | right;
@@ -172,7 +151,6 @@ static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
snd_sbmixer_write(sb, right_reg, right);
}
}
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
return change;
}
@@ -182,28 +160,21 @@ static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
static int snd_dt019x_input_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[5] = {
+ static const char * const texts[5] = {
"CD", "Mic", "Line", "Synth", "Master"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 5;
- if (uinfo->value.enumerated.item > 4)
- uinfo->value.enumerated.item = 4;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 5, texts);
}
static int snd_dt019x_input_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
unsigned char oval;
- spin_lock_irqsave(&sb->mixer_lock, flags);
- oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
+ scoped_guard(spinlock_irqsave, &sb->mixer_lock) {
+ oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
+ }
switch (oval & 0x07) {
case SB_DT019X_CAP_CD:
ucontrol->value.enumerated.item[0] = 0;
@@ -234,7 +205,6 @@ static int snd_dt019x_input_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl
static int snd_dt019x_input_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned char nval, oval;
@@ -259,12 +229,11 @@ static int snd_dt019x_input_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl
default:
nval = SB_DT019X_CAP_MAIN;
}
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
change = nval != oval;
if (change)
snd_sbmixer_write(sb, SB_DT019X_CAPTURE_SW, nval);
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
return change;
}
@@ -275,30 +244,21 @@ static int snd_dt019x_input_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl
static int snd_als4k_mono_capture_route_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[3] = {
+ static const char * const texts[3] = {
"L chan only", "R chan only", "L ch/2 + R ch/2"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_als4k_mono_capture_route_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
unsigned char oval;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
oval = snd_sbmixer_read(sb, SB_ALS4000_MONO_IO_CTRL);
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
oval >>= 6;
if (oval > 2)
oval = 2;
@@ -311,13 +271,12 @@ static int snd_als4k_mono_capture_route_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned char nval, oval;
if (ucontrol->value.enumerated.item[0] > 2)
return -EINVAL;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
oval = snd_sbmixer_read(sb, SB_ALS4000_MONO_IO_CTRL);
nval = (oval & ~(3 << 6))
@@ -325,7 +284,6 @@ static int snd_als4k_mono_capture_route_put(struct snd_kcontrol *kcontrol,
change = nval != oval;
if (change)
snd_sbmixer_write(sb, SB_ALS4000_MONO_IO_CTRL, nval);
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
return change;
}
@@ -335,29 +293,21 @@ static int snd_als4k_mono_capture_route_put(struct snd_kcontrol *kcontrol,
static int snd_sb8mixer_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[3] = {
+ static const char * const texts[3] = {
"Mic", "CD", "Line"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_sb8mixer_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
unsigned char oval;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
switch ((oval >> 0x01) & 0x03) {
case SB_DSP_MIXS_CD:
ucontrol->value.enumerated.item[0] = 1;
@@ -375,7 +325,6 @@ static int snd_sb8mixer_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_el
static int snd_sb8mixer_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int change;
unsigned char nval, oval;
@@ -392,13 +341,12 @@ static int snd_sb8mixer_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_el
nval = SB_DSP_MIXS_MIC;
}
nval <<= 1;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
nval |= oval & ~0x06;
change = nval != oval;
if (change)
snd_sbmixer_write(sb, SB_DSP_CAPTURE_SOURCE, nval);
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
return change;
}
@@ -418,17 +366,15 @@ static int snd_sb16mixer_info_input_sw(struct snd_kcontrol *kcontrol, struct snd
static int snd_sb16mixer_get_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg1 = kcontrol->private_value & 0xff;
int reg2 = (kcontrol->private_value >> 8) & 0xff;
int left_shift = (kcontrol->private_value >> 16) & 0x0f;
int right_shift = (kcontrol->private_value >> 24) & 0x0f;
unsigned char val1, val2;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
val1 = snd_sbmixer_read(sb, reg1);
val2 = snd_sbmixer_read(sb, reg2);
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
ucontrol->value.integer.value[0] = (val1 >> left_shift) & 0x01;
ucontrol->value.integer.value[1] = (val2 >> left_shift) & 0x01;
ucontrol->value.integer.value[2] = (val1 >> right_shift) & 0x01;
@@ -439,7 +385,6 @@ static int snd_sb16mixer_get_input_sw(struct snd_kcontrol *kcontrol, struct snd_
static int snd_sb16mixer_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_sb *sb = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg1 = kcontrol->private_value & 0xff;
int reg2 = (kcontrol->private_value >> 8) & 0xff;
int left_shift = (kcontrol->private_value >> 16) & 0x0f;
@@ -447,7 +392,7 @@ static int snd_sb16mixer_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_
int change;
unsigned char val1, val2, oval1, oval2;
- spin_lock_irqsave(&sb->mixer_lock, flags);
+ guard(spinlock_irqsave)(&sb->mixer_lock);
oval1 = snd_sbmixer_read(sb, reg1);
oval2 = snd_sbmixer_read(sb, reg2);
val1 = oval1 & ~((1 << left_shift) | (1 << right_shift));
@@ -461,7 +406,6 @@ static int snd_sb16mixer_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_
snd_sbmixer_write(sb, reg1, val1);
snd_sbmixer_write(sb, reg2, val2);
}
- spin_unlock_irqrestore(&sb->mixer_lock, flags);
return change;
}
@@ -472,7 +416,7 @@ static int snd_sb16mixer_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_
*/
int snd_sbmixer_add_ctl(struct snd_sb *chip, const char *name, int index, int type, unsigned long value)
{
- static struct snd_kcontrol_new newctls[] = {
+ static const struct snd_kcontrol_new newctls[] = {
[SB_MIX_SINGLE] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_sbmixer_info_single,
@@ -516,10 +460,11 @@ int snd_sbmixer_add_ctl(struct snd_sb *chip, const char *name, int index, int ty
ctl = snd_ctl_new1(&newctls[type], chip);
if (! ctl)
return -ENOMEM;
- strlcpy(ctl->id.name, name, sizeof(ctl->id.name));
+ strscpy(ctl->id.name, name, sizeof(ctl->id.name));
ctl->id.index = index;
ctl->private_value = value;
- if ((err = snd_ctl_add(chip->card, ctl)) < 0)
+ err = snd_ctl_add(chip->card, ctl);
+ if (err < 0)
return err;
return 0;
}
@@ -528,14 +473,14 @@ int snd_sbmixer_add_ctl(struct snd_sb *chip, const char *name, int index, int ty
* SB 2.0 specific mixer elements
*/
-static struct sbmix_elem snd_sb20_controls[] = {
+static const struct sbmix_elem snd_sb20_controls[] = {
SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7),
SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3),
SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7),
SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7)
};
-static unsigned char snd_sb20_init_values[][2] = {
+static const unsigned char snd_sb20_init_values[][2] = {
{ SB_DSP20_MASTER_DEV, 0 },
{ SB_DSP20_FM_DEV, 0 },
};
@@ -543,7 +488,7 @@ static unsigned char snd_sb20_init_values[][2] = {
/*
* SB Pro specific mixer elements
*/
-static struct sbmix_elem snd_sbpro_controls[] = {
+static const struct sbmix_elem snd_sbpro_controls[] = {
SB_DOUBLE("Master Playback Volume",
SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7),
SB_DOUBLE("PCM Playback Volume",
@@ -563,7 +508,7 @@ static struct sbmix_elem snd_sbpro_controls[] = {
SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1)
};
-static unsigned char snd_sbpro_init_values[][2] = {
+static const unsigned char snd_sbpro_init_values[][2] = {
{ SB_DSP_MASTER_DEV, 0 },
{ SB_DSP_PCM_DEV, 0 },
{ SB_DSP_FM_DEV, 0 },
@@ -572,7 +517,7 @@ static unsigned char snd_sbpro_init_values[][2] = {
/*
* SB16 specific mixer elements
*/
-static struct sbmix_elem snd_sb16_controls[] = {
+static const struct sbmix_elem snd_sb16_controls[] = {
SB_DOUBLE("Master Playback Volume",
SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31),
SB_DOUBLE("PCM Playback Volume",
@@ -610,7 +555,7 @@ static struct sbmix_elem snd_sb16_controls[] = {
SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15)
};
-static unsigned char snd_sb16_init_values[][2] = {
+static const unsigned char snd_sb16_init_values[][2] = {
{ SB_DSP4_MASTER_DEV + 0, 0 },
{ SB_DSP4_MASTER_DEV + 1, 0 },
{ SB_DSP4_PCM_DEV + 0, 0 },
@@ -626,7 +571,7 @@ static unsigned char snd_sb16_init_values[][2] = {
/*
* DT019x specific mixer elements
*/
-static struct sbmix_elem snd_dt019x_controls[] = {
+static const struct sbmix_elem snd_dt019x_controls[] = {
/* ALS4000 below has some parts which we might be lacking,
* e.g. snd_als4000_ctl_mono_playback_switch - check it! */
SB_DOUBLE("Master Playback Volume",
@@ -656,7 +601,7 @@ static struct sbmix_elem snd_dt019x_controls[] = {
}
};
-static unsigned char snd_dt019x_init_values[][2] = {
+static const unsigned char snd_dt019x_init_values[][2] = {
{ SB_DT019X_MASTER_DEV, 0 },
{ SB_DT019X_PCM_DEV, 0 },
{ SB_DT019X_SYNTH_DEV, 0 },
@@ -671,7 +616,7 @@ static unsigned char snd_dt019x_init_values[][2] = {
/*
* ALS4000 specific mixer elements
*/
-static struct sbmix_elem snd_als4000_controls[] = {
+static const struct sbmix_elem snd_als4000_controls[] = {
SB_DOUBLE("PCM Playback Switch",
SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2, 1, 1),
SB_DOUBLE("Synth Playback Switch",
@@ -705,7 +650,7 @@ static struct sbmix_elem snd_als4000_controls[] = {
#endif
};
-static unsigned char snd_als4000_init_values[][2] = {
+static const unsigned char snd_als4000_init_values[][2] = {
{ SB_DSP4_MASTER_DEV + 0, 0 },
{ SB_DSP4_MASTER_DEV + 1, 0 },
{ SB_DSP4_PCM_DEV + 0, 0 },
@@ -723,26 +668,24 @@ static unsigned char snd_als4000_init_values[][2] = {
/*
*/
static int snd_sbmixer_init(struct snd_sb *chip,
- struct sbmix_elem *controls,
+ const struct sbmix_elem *controls,
int controls_count,
- unsigned char map[][2],
+ const unsigned char map[][2],
int map_count,
char *name)
{
- unsigned long flags;
struct snd_card *card = chip->card;
int idx, err;
/* mixer reset */
- spin_lock_irqsave(&chip->mixer_lock, flags);
- snd_sbmixer_write(chip, 0x00, 0x00);
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->mixer_lock) {
+ snd_sbmixer_write(chip, 0x00, 0x00);
+ }
/* mute and zero volume channels */
for (idx = 0; idx < map_count; idx++) {
- spin_lock_irqsave(&chip->mixer_lock, flags);
+ guard(spinlock_irqsave)(&chip->mixer_lock);
snd_sbmixer_write(chip, map[idx][0], map[idx][1]);
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
}
for (idx = 0; idx < controls_count; idx++) {
@@ -751,7 +694,7 @@ static int snd_sbmixer_init(struct snd_sb *chip,
return err;
}
snd_component_add(card, name);
- strcpy(card->mixername, name);
+ strscpy(card->mixername, name);
return 0;
}
@@ -770,33 +713,36 @@ int snd_sbmixer_new(struct snd_sb *chip)
return 0; /* no mixer chip on SB1.x */
case SB_HW_20:
case SB_HW_201:
- if ((err = snd_sbmixer_init(chip,
- snd_sb20_controls,
- ARRAY_SIZE(snd_sb20_controls),
- snd_sb20_init_values,
- ARRAY_SIZE(snd_sb20_init_values),
- "CTL1335")) < 0)
+ err = snd_sbmixer_init(chip,
+ snd_sb20_controls,
+ ARRAY_SIZE(snd_sb20_controls),
+ snd_sb20_init_values,
+ ARRAY_SIZE(snd_sb20_init_values),
+ "CTL1335");
+ if (err < 0)
return err;
break;
case SB_HW_PRO:
case SB_HW_JAZZ16:
- if ((err = snd_sbmixer_init(chip,
- snd_sbpro_controls,
- ARRAY_SIZE(snd_sbpro_controls),
- snd_sbpro_init_values,
- ARRAY_SIZE(snd_sbpro_init_values),
- "CTL1345")) < 0)
+ err = snd_sbmixer_init(chip,
+ snd_sbpro_controls,
+ ARRAY_SIZE(snd_sbpro_controls),
+ snd_sbpro_init_values,
+ ARRAY_SIZE(snd_sbpro_init_values),
+ "CTL1345");
+ if (err < 0)
return err;
break;
case SB_HW_16:
case SB_HW_ALS100:
case SB_HW_CS5530:
- if ((err = snd_sbmixer_init(chip,
- snd_sb16_controls,
- ARRAY_SIZE(snd_sb16_controls),
- snd_sb16_init_values,
- ARRAY_SIZE(snd_sb16_init_values),
- "CTL1745")) < 0)
+ err = snd_sbmixer_init(chip,
+ snd_sb16_controls,
+ ARRAY_SIZE(snd_sb16_controls),
+ snd_sb16_init_values,
+ ARRAY_SIZE(snd_sb16_init_values),
+ "CTL1745");
+ if (err < 0)
return err;
break;
case SB_HW_ALS4000:
@@ -809,37 +755,40 @@ int snd_sbmixer_new(struct snd_sb *chip)
"ALS4000");
if (err < 0)
return err;
- if ((err = snd_sbmixer_init(chip,
- snd_als4000_controls,
- ARRAY_SIZE(snd_als4000_controls),
- snd_als4000_init_values,
- ARRAY_SIZE(snd_als4000_init_values),
- "ALS4000")) < 0)
+ err = snd_sbmixer_init(chip,
+ snd_als4000_controls,
+ ARRAY_SIZE(snd_als4000_controls),
+ snd_als4000_init_values,
+ ARRAY_SIZE(snd_als4000_init_values),
+ "ALS4000");
+ if (err < 0)
return err;
break;
case SB_HW_DT019X:
- if ((err = snd_sbmixer_init(chip,
- snd_dt019x_controls,
- ARRAY_SIZE(snd_dt019x_controls),
- snd_dt019x_init_values,
- ARRAY_SIZE(snd_dt019x_init_values),
- "DT019X")) < 0)
+ err = snd_sbmixer_init(chip,
+ snd_dt019x_controls,
+ ARRAY_SIZE(snd_dt019x_controls),
+ snd_dt019x_init_values,
+ ARRAY_SIZE(snd_dt019x_init_values),
+ "DT019X");
+ if (err < 0)
+ return err;
break;
default:
- strcpy(card->mixername, "???");
+ strscpy(card->mixername, "???");
}
return 0;
}
#ifdef CONFIG_PM
-static unsigned char sb20_saved_regs[] = {
+static const unsigned char sb20_saved_regs[] = {
SB_DSP20_MASTER_DEV,
SB_DSP20_PCM_DEV,
SB_DSP20_FM_DEV,
SB_DSP20_CD_DEV,
};
-static unsigned char sbpro_saved_regs[] = {
+static const unsigned char sbpro_saved_regs[] = {
SB_DSP_MASTER_DEV,
SB_DSP_PCM_DEV,
SB_DSP_PLAYBACK_FILT,
@@ -851,7 +800,7 @@ static unsigned char sbpro_saved_regs[] = {
SB_DSP_CAPTURE_FILT,
};
-static unsigned char sb16_saved_regs[] = {
+static const unsigned char sb16_saved_regs[] = {
SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
SB_DSP4_3DSE,
SB_DSP4_BASS_DEV, SB_DSP4_BASS_DEV + 1,
@@ -869,7 +818,7 @@ static unsigned char sb16_saved_regs[] = {
SB_DSP4_MIC_AGC
};
-static unsigned char dt019x_saved_regs[] = {
+static const unsigned char dt019x_saved_regs[] = {
SB_DT019X_MASTER_DEV,
SB_DT019X_PCM_DEV,
SB_DT019X_SYNTH_DEV,
@@ -882,7 +831,7 @@ static unsigned char dt019x_saved_regs[] = {
SB_DT019X_CAPTURE_SW,
};
-static unsigned char als4000_saved_regs[] = {
+static const unsigned char als4000_saved_regs[] = {
/* please verify in dsheet whether regs to be added
are actually real H/W or just dummy */
SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
@@ -904,7 +853,7 @@ static unsigned char als4000_saved_regs[] = {
SB_ALS4000_CR3_CONFIGURATION,
};
-static void save_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
+static void save_mixer(struct snd_sb *chip, const unsigned char *regs, int num_regs)
{
unsigned char *val = chip->saved_regs;
if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))
@@ -913,7 +862,7 @@ static void save_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
*val++ = snd_sbmixer_read(chip, *regs++);
}
-static void restore_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)
+static void restore_mixer(struct snd_sb *chip, const unsigned char *regs, int num_regs)
{
unsigned char *val = chip->saved_regs;
if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))
diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c
index 9a8bbf6dd62a..6d618cc2ba45 100644
--- a/sound/isa/sc6000.c
+++ b/sound/isa/sc6000.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Gallant SC-6000 soundcard. This card is also known as
* Audio Excel DSP 16 or Zoltrix AV302.
@@ -9,20 +10,6 @@
*
* I don't have documentation for this card. I used the driver
* for OSS/Free included in the kernel source as reference.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
@@ -42,13 +29,10 @@
MODULE_AUTHOR("Krzysztof Helt");
MODULE_DESCRIPTION("Gallant SC-6000");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Gallant, SC-6000},"
- "{AudioExcel, Audio Excel DSP 16},"
- "{Zoltrix, AV302}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220, 0x240 */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5, 7, 9, 10, 11 */
static long mss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530, 0xe80 */
@@ -64,17 +48,17 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for sc-6000 based soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable sc-6000 based soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for sc-6000 driver.");
-module_param_array(mss_port, long, NULL, 0444);
+module_param_hw_array(mss_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mss_port, "MSS Port # for sc-6000 driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for sc-6000 driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for sc-6000 driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for sc-6000 driver.");
-module_param_array(dma, int, NULL, 0444);
+module_param_hw_array(dma, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma, "DMA # for sc-6000 driver.");
module_param_array(joystick, bool, NULL, 0444);
MODULE_PARM_DESC(joystick, "Enable gameport.");
@@ -121,7 +105,7 @@ MODULE_PARM_DESC(joystick, "Enable gameport.");
/*
* sc6000_irq_to_softcfg - Decode irq number into cfg code.
*/
-static __devinit unsigned char sc6000_irq_to_softcfg(int irq)
+static unsigned char sc6000_irq_to_softcfg(int irq)
{
unsigned char val = 0;
@@ -150,7 +134,7 @@ static __devinit unsigned char sc6000_irq_to_softcfg(int irq)
/*
* sc6000_dma_to_softcfg - Decode dma number into cfg code.
*/
-static __devinit unsigned char sc6000_dma_to_softcfg(int dma)
+static unsigned char sc6000_dma_to_softcfg(int dma)
{
unsigned char val = 0;
@@ -173,7 +157,7 @@ static __devinit unsigned char sc6000_dma_to_softcfg(int dma)
/*
* sc6000_mpu_irq_to_softcfg - Decode MPU-401 irq number into cfg code.
*/
-static __devinit unsigned char sc6000_mpu_irq_to_softcfg(int mpu_irq)
+static unsigned char sc6000_mpu_irq_to_softcfg(int mpu_irq)
{
unsigned char val = 0;
@@ -220,7 +204,7 @@ static int sc6000_read(char __iomem *vport)
}
-static int sc6000_write(char __iomem *vport, int cmd)
+static int sc6000_write(struct device *devptr, char __iomem *vport, int cmd)
{
unsigned char val;
int loop = 500000;
@@ -237,18 +221,19 @@ static int sc6000_write(char __iomem *vport, int cmd)
cpu_relax();
} while (loop--);
- snd_printk(KERN_ERR "DSP Command (0x%x) timeout.\n", cmd);
+ dev_err(devptr, "DSP Command (0x%x) timeout.\n", cmd);
return -EIO;
}
-static int __devinit sc6000_dsp_get_answer(char __iomem *vport, int command,
- char *data, int data_len)
+static int sc6000_dsp_get_answer(struct device *devptr,
+ char __iomem *vport, int command,
+ char *data, int data_len)
{
int len = 0;
- if (sc6000_write(vport, command)) {
- snd_printk(KERN_ERR "CMD 0x%x: failed!\n", command);
+ if (sc6000_write(devptr, vport, command)) {
+ dev_err(devptr, "CMD 0x%x: failed!\n", command);
return -EIO;
}
@@ -269,7 +254,7 @@ static int __devinit sc6000_dsp_get_answer(char __iomem *vport, int command,
return len ? len : -EIO;
}
-static int __devinit sc6000_dsp_reset(char __iomem *vport)
+static int sc6000_dsp_reset(char __iomem *vport)
{
iowrite8(1, vport + DSP_RESET);
udelay(10);
@@ -281,82 +266,86 @@ static int __devinit sc6000_dsp_reset(char __iomem *vport)
}
/* detection and initialization */
-static int __devinit sc6000_hw_cfg_write(char __iomem *vport, const int *cfg)
+static int sc6000_hw_cfg_write(struct device *devptr,
+ char __iomem *vport, const int *cfg)
{
- if (sc6000_write(vport, COMMAND_6C) < 0) {
- snd_printk(KERN_WARNING "CMD 0x%x: failed!\n", COMMAND_6C);
+ if (sc6000_write(devptr, vport, COMMAND_6C) < 0) {
+ dev_warn(devptr, "CMD 0x%x: failed!\n", COMMAND_6C);
return -EIO;
}
- if (sc6000_write(vport, COMMAND_5C) < 0) {
- snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_5C);
+ if (sc6000_write(devptr, vport, COMMAND_5C) < 0) {
+ dev_err(devptr, "CMD 0x%x: failed!\n", COMMAND_5C);
return -EIO;
}
- if (sc6000_write(vport, cfg[0]) < 0) {
- snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[0]);
+ if (sc6000_write(devptr, vport, cfg[0]) < 0) {
+ dev_err(devptr, "DATA 0x%x: failed!\n", cfg[0]);
return -EIO;
}
- if (sc6000_write(vport, cfg[1]) < 0) {
- snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[1]);
+ if (sc6000_write(devptr, vport, cfg[1]) < 0) {
+ dev_err(devptr, "DATA 0x%x: failed!\n", cfg[1]);
return -EIO;
}
- if (sc6000_write(vport, COMMAND_C5) < 0) {
- snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_C5);
+ if (sc6000_write(devptr, vport, COMMAND_C5) < 0) {
+ dev_err(devptr, "CMD 0x%x: failed!\n", COMMAND_C5);
return -EIO;
}
return 0;
}
-static int sc6000_cfg_write(char __iomem *vport, unsigned char softcfg)
+static int sc6000_cfg_write(struct device *devptr,
+ char __iomem *vport, unsigned char softcfg)
{
- if (sc6000_write(vport, WRITE_MDIRQ_CFG)) {
- snd_printk(KERN_ERR "CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
+ if (sc6000_write(devptr, vport, WRITE_MDIRQ_CFG)) {
+ dev_err(devptr, "CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
return -EIO;
}
- if (sc6000_write(vport, softcfg)) {
- snd_printk(KERN_ERR "sc6000_cfg_write: failed!\n");
+ if (sc6000_write(devptr, vport, softcfg)) {
+ dev_err(devptr, "%s: failed!\n", __func__);
return -EIO;
}
return 0;
}
-static int sc6000_setup_board(char __iomem *vport, int config)
+static int sc6000_setup_board(struct device *devptr,
+ char __iomem *vport, int config)
{
int loop = 10;
do {
- if (sc6000_write(vport, COMMAND_88)) {
- snd_printk(KERN_ERR "CMD 0x%x: failed!\n",
- COMMAND_88);
+ if (sc6000_write(devptr, vport, COMMAND_88)) {
+ dev_err(devptr, "CMD 0x%x: failed!\n",
+ COMMAND_88);
return -EIO;
}
} while ((sc6000_wait_data(vport) < 0) && loop--);
if (sc6000_read(vport) < 0) {
- snd_printk(KERN_ERR "sc6000_read after CMD 0x%x: failed\n",
- COMMAND_88);
+ dev_err(devptr, "sc6000_read after CMD 0x%x: failed\n",
+ COMMAND_88);
return -EIO;
}
- if (sc6000_cfg_write(vport, config))
+ if (sc6000_cfg_write(devptr, vport, config))
return -ENODEV;
return 0;
}
-static int __devinit sc6000_init_mss(char __iomem *vport, int config,
- char __iomem *vmss_port, int mss_config)
+static int sc6000_init_mss(struct device *devptr,
+ char __iomem *vport, int config,
+ char __iomem *vmss_port, int mss_config)
{
- if (sc6000_write(vport, DSP_INIT_MSS)) {
- snd_printk(KERN_ERR "sc6000_init_mss [0x%x]: failed!\n",
- DSP_INIT_MSS);
+ if (sc6000_write(devptr, vport, DSP_INIT_MSS)) {
+ dev_err(devptr, "%s [0x%x]: failed!\n", __func__,
+ DSP_INIT_MSS);
return -EIO;
}
msleep(10);
- if (sc6000_cfg_write(vport, config))
+ if (sc6000_cfg_write(devptr, vport, config))
return -EIO;
iowrite8(mss_config, vmss_port);
@@ -364,9 +353,10 @@ static int __devinit sc6000_init_mss(char __iomem *vport, int config,
return 0;
}
-static void __devinit sc6000_hw_cfg_encode(char __iomem *vport, int *cfg,
- long xport, long xmpu,
- long xmss_port, int joystick)
+static void sc6000_hw_cfg_encode(struct device *devptr,
+ char __iomem *vport, int *cfg,
+ long xport, long xmpu,
+ long xmss_port, int joystick)
{
cfg[0] = 0;
cfg[1] = 0;
@@ -383,11 +373,12 @@ static void __devinit sc6000_hw_cfg_encode(char __iomem *vport, int *cfg,
cfg[0] |= 0x02;
cfg[1] |= 0x80; /* enable WSS system */
cfg[1] &= ~0x40; /* disable IDE */
- snd_printd("hw cfg %x, %x\n", cfg[0], cfg[1]);
+ dev_dbg(devptr, "hw cfg %x, %x\n", cfg[0], cfg[1]);
}
-static int __devinit sc6000_init_board(char __iomem *vport,
- char __iomem *vmss_port, int dev)
+static int sc6000_init_board(struct device *devptr,
+ char __iomem *vport,
+ char __iomem *vmss_port, int dev)
{
char answer[15];
char version[2];
@@ -400,14 +391,14 @@ static int __devinit sc6000_init_board(char __iomem *vport,
err = sc6000_dsp_reset(vport);
if (err < 0) {
- snd_printk(KERN_ERR "sc6000_dsp_reset: failed!\n");
+ dev_err(devptr, "sc6000_dsp_reset: failed!\n");
return err;
}
memset(answer, 0, sizeof(answer));
- err = sc6000_dsp_get_answer(vport, GET_DSP_COPYRIGHT, answer, 15);
+ err = sc6000_dsp_get_answer(devptr, vport, GET_DSP_COPYRIGHT, answer, 15);
if (err <= 0) {
- snd_printk(KERN_ERR "sc6000_dsp_copyright: failed!\n");
+ dev_err(devptr, "sc6000_dsp_copyright: failed!\n");
return -ENODEV;
}
/*
@@ -415,59 +406,59 @@ static int __devinit sc6000_init_board(char __iomem *vport,
* if we have something different, we have to be warned.
*/
if (strncmp("SC-6000", answer, 7))
- snd_printk(KERN_WARNING "Warning: non SC-6000 audio card!\n");
+ dev_warn(devptr, "Warning: non SC-6000 audio card!\n");
- if (sc6000_dsp_get_answer(vport, GET_DSP_VERSION, version, 2) < 2) {
- snd_printk(KERN_ERR "sc6000_dsp_version: failed!\n");
+ if (sc6000_dsp_get_answer(devptr, vport, GET_DSP_VERSION, version, 2) < 2) {
+ dev_err(devptr, "sc6000_dsp_version: failed!\n");
return -ENODEV;
}
- printk(KERN_INFO PFX "Detected model: %s, DSP version %d.%d\n",
+ dev_info(devptr, "Detected model: %s, DSP version %d.%d\n",
answer, version[0], version[1]);
/* set configuration */
- sc6000_write(vport, COMMAND_5C);
+ sc6000_write(devptr, vport, COMMAND_5C);
if (sc6000_read(vport) < 0)
old = 1;
if (!old) {
int cfg[2];
- sc6000_hw_cfg_encode(vport, &cfg[0], port[dev], mpu_port[dev],
+ sc6000_hw_cfg_encode(devptr,
+ vport, &cfg[0], port[dev], mpu_port[dev],
mss_port[dev], joystick[dev]);
- if (sc6000_hw_cfg_write(vport, cfg) < 0) {
- snd_printk(KERN_ERR "sc6000_hw_cfg_write: failed!\n");
+ if (sc6000_hw_cfg_write(devptr, vport, cfg) < 0) {
+ dev_err(devptr, "sc6000_hw_cfg_write: failed!\n");
return -EIO;
}
}
- err = sc6000_setup_board(vport, config);
+ err = sc6000_setup_board(devptr, vport, config);
if (err < 0) {
- snd_printk(KERN_ERR "sc6000_setup_board: failed!\n");
+ dev_err(devptr, "sc6000_setup_board: failed!\n");
return -ENODEV;
}
sc6000_dsp_reset(vport);
if (!old) {
- sc6000_write(vport, COMMAND_60);
- sc6000_write(vport, 0x02);
+ sc6000_write(devptr, vport, COMMAND_60);
+ sc6000_write(devptr, vport, 0x02);
sc6000_dsp_reset(vport);
}
- err = sc6000_setup_board(vport, config);
+ err = sc6000_setup_board(devptr, vport, config);
if (err < 0) {
- snd_printk(KERN_ERR "sc6000_setup_board: failed!\n");
+ dev_err(devptr, "sc6000_setup_board: failed!\n");
return -ENODEV;
}
- err = sc6000_init_mss(vport, config, vmss_port, mss_config);
+ err = sc6000_init_mss(devptr, vport, config, vmss_port, mss_config);
if (err < 0) {
- snd_printk(KERN_ERR "Cannot initialize "
- "Microsoft Sound System mode.\n");
+ dev_err(devptr, "Cannot initialize Microsoft Sound System mode.\n");
return -ENODEV;
}
return 0;
}
-static int __devinit snd_sc6000_mixer(struct snd_wss *chip)
+static int snd_sc6000_mixer(struct snd_wss *chip)
{
struct snd_card *card = chip->card;
struct snd_ctl_elem_id id1, id2;
@@ -478,178 +469,176 @@ static int __devinit snd_sc6000_mixer(struct snd_wss *chip)
id1.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
/* reassign AUX0 to FM */
- strcpy(id1.name, "Aux Playback Switch");
- strcpy(id2.name, "FM Playback Switch");
+ strscpy(id1.name, "Aux Playback Switch");
+ strscpy(id2.name, "FM Playback Switch");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0)
return err;
- strcpy(id1.name, "Aux Playback Volume");
- strcpy(id2.name, "FM Playback Volume");
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "FM Playback Volume");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0)
return err;
/* reassign AUX1 to CD */
- strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
- strcpy(id2.name, "CD Playback Switch");
+ strscpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+ strscpy(id2.name, "CD Playback Switch");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0)
return err;
- strcpy(id1.name, "Aux Playback Volume");
- strcpy(id2.name, "CD Playback Volume");
+ strscpy(id1.name, "Aux Playback Volume");
+ strscpy(id2.name, "CD Playback Volume");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0)
return err;
return 0;
}
-static int __devinit snd_sc6000_match(struct device *devptr, unsigned int dev)
+static int snd_sc6000_match(struct device *devptr, unsigned int dev)
{
if (!enable[dev])
return 0;
if (port[dev] == SNDRV_AUTO_PORT) {
- printk(KERN_ERR PFX "specify IO port\n");
+ dev_err(devptr, "specify IO port\n");
return 0;
}
if (mss_port[dev] == SNDRV_AUTO_PORT) {
- printk(KERN_ERR PFX "specify MSS port\n");
+ dev_err(devptr, "specify MSS port\n");
return 0;
}
if (port[dev] != 0x220 && port[dev] != 0x240) {
- printk(KERN_ERR PFX "Port must be 0x220 or 0x240\n");
+ dev_err(devptr, "Port must be 0x220 or 0x240\n");
return 0;
}
if (mss_port[dev] != 0x530 && mss_port[dev] != 0xe80) {
- printk(KERN_ERR PFX "MSS port must be 0x530 or 0xe80\n");
+ dev_err(devptr, "MSS port must be 0x530 or 0xe80\n");
return 0;
}
if (irq[dev] != SNDRV_AUTO_IRQ && !sc6000_irq_to_softcfg(irq[dev])) {
- printk(KERN_ERR PFX "invalid IRQ %d\n", irq[dev]);
+ dev_err(devptr, "invalid IRQ %d\n", irq[dev]);
return 0;
}
if (dma[dev] != SNDRV_AUTO_DMA && !sc6000_dma_to_softcfg(dma[dev])) {
- printk(KERN_ERR PFX "invalid DMA %d\n", dma[dev]);
+ dev_err(devptr, "invalid DMA %d\n", dma[dev]);
return 0;
}
if (mpu_port[dev] != SNDRV_AUTO_PORT &&
(mpu_port[dev] & ~0x30L) != 0x300) {
- printk(KERN_ERR PFX "invalid MPU-401 port %lx\n",
+ dev_err(devptr, "invalid MPU-401 port %lx\n",
mpu_port[dev]);
return 0;
}
if (mpu_port[dev] != SNDRV_AUTO_PORT &&
mpu_irq[dev] != SNDRV_AUTO_IRQ && mpu_irq[dev] != 0 &&
!sc6000_mpu_irq_to_softcfg(mpu_irq[dev])) {
- printk(KERN_ERR PFX "invalid MPU-401 IRQ %d\n", mpu_irq[dev]);
+ dev_err(devptr, "invalid MPU-401 IRQ %d\n", mpu_irq[dev]);
return 0;
}
return 1;
}
-static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
+static void snd_sc6000_free(struct snd_card *card)
+{
+ char __iomem *vport = (char __force __iomem *)card->private_data;
+
+ if (vport)
+ sc6000_setup_board(card->dev, vport, 0);
+}
+
+static int __snd_sc6000_probe(struct device *devptr, unsigned int dev)
{
- static int possible_irqs[] = { 5, 7, 9, 10, 11, -1 };
- static int possible_dmas[] = { 1, 3, 0, -1 };
+ static const int possible_irqs[] = { 5, 7, 9, 10, 11, -1 };
+ static const int possible_dmas[] = { 1, 3, 0, -1 };
int err;
int xirq = irq[dev];
int xdma = dma[dev];
struct snd_card *card;
struct snd_wss *chip;
struct snd_opl3 *opl3;
- char __iomem **vport;
+ char __iomem *vport;
char __iomem *vmss_port;
-
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, sizeof(vport),
- &card);
+ err = snd_devm_card_new(devptr, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
if (err < 0)
return err;
- vport = card->private_data;
if (xirq == SNDRV_AUTO_IRQ) {
xirq = snd_legacy_find_free_irq(possible_irqs);
if (xirq < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
- err = -EBUSY;
- goto err_exit;
+ dev_err(devptr, "unable to find a free IRQ\n");
+ return -EBUSY;
}
}
if (xdma == SNDRV_AUTO_DMA) {
xdma = snd_legacy_find_free_dma(possible_dmas);
if (xdma < 0) {
- snd_printk(KERN_ERR PFX "unable to find a free DMA\n");
- err = -EBUSY;
- goto err_exit;
+ dev_err(devptr, "unable to find a free DMA\n");
+ return -EBUSY;
}
}
- if (!request_region(port[dev], 0x10, DRV_NAME)) {
- snd_printk(KERN_ERR PFX
- "I/O port region is already in use.\n");
- err = -EBUSY;
- goto err_exit;
+ if (!devm_request_region(devptr, port[dev], 0x10, DRV_NAME)) {
+ dev_err(devptr, "I/O port region is already in use.\n");
+ return -EBUSY;
}
- *vport = devm_ioport_map(devptr, port[dev], 0x10);
- if (*vport == NULL) {
- snd_printk(KERN_ERR PFX
- "I/O port cannot be iomaped.\n");
- err = -EBUSY;
- goto err_unmap1;
+ vport = devm_ioport_map(devptr, port[dev], 0x10);
+ if (!vport) {
+ dev_err(devptr, "I/O port cannot be iomapped.\n");
+ return -EBUSY;
}
+ card->private_data = (void __force *)vport;
/* to make it marked as used */
- if (!request_region(mss_port[dev], 4, DRV_NAME)) {
- snd_printk(KERN_ERR PFX
- "SC-6000 port I/O port region is already in use.\n");
- err = -EBUSY;
- goto err_unmap1;
+ if (!devm_request_region(devptr, mss_port[dev], 4, DRV_NAME)) {
+ dev_err(devptr,
+ "SC-6000 port I/O port region is already in use.\n");
+ return -EBUSY;
}
vmss_port = devm_ioport_map(devptr, mss_port[dev], 4);
if (!vmss_port) {
- snd_printk(KERN_ERR PFX
- "MSS port I/O cannot be iomaped.\n");
- err = -EBUSY;
- goto err_unmap2;
+ dev_err(devptr, "MSS port I/O cannot be iomapped.\n");
+ return -EBUSY;
}
- snd_printd("Initializing BASE[0x%lx] IRQ[%d] DMA[%d] MIRQ[%d]\n",
- port[dev], xirq, xdma,
- mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]);
+ dev_dbg(devptr, "Initializing BASE[0x%lx] IRQ[%d] DMA[%d] MIRQ[%d]\n",
+ port[dev], xirq, xdma,
+ mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]);
- err = sc6000_init_board(*vport, vmss_port, dev);
+ err = sc6000_init_board(devptr, vport, vmss_port, dev);
if (err < 0)
- goto err_unmap2;
+ return err;
+ card->private_free = snd_sc6000_free;
err = snd_wss_create(card, mss_port[dev] + 4, -1, xirq, xdma, -1,
WSS_HW_DETECT, 0, &chip);
if (err < 0)
- goto err_unmap2;
+ return err;
- err = snd_wss_pcm(chip, 0, NULL);
+ err = snd_wss_pcm(chip, 0);
if (err < 0) {
- snd_printk(KERN_ERR PFX
- "error creating new WSS PCM device\n");
- goto err_unmap2;
+ dev_err(devptr, "error creating new WSS PCM device\n");
+ return err;
}
err = snd_wss_mixer(chip);
if (err < 0) {
- snd_printk(KERN_ERR PFX "error creating new WSS mixer\n");
- goto err_unmap2;
+ dev_err(devptr, "error creating new WSS mixer\n");
+ return err;
}
err = snd_sc6000_mixer(chip);
if (err < 0) {
- snd_printk(KERN_ERR PFX "the mixer rewrite failed\n");
- goto err_unmap2;
+ dev_err(devptr, "the mixer rewrite failed\n");
+ return err;
}
if (snd_opl3_create(card,
0x388, 0x388 + 2,
OPL3_HW_AUTO, 0, &opl3) < 0) {
- snd_printk(KERN_ERR PFX "no OPL device at 0x%x-0x%x ?\n",
- 0x388, 0x388 + 2);
+ dev_err(devptr, "no OPL device at 0x%x-0x%x ?\n",
+ 0x388, 0x388 + 2);
} else {
err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
if (err < 0)
- goto err_unmap2;
+ return err;
}
if (mpu_port[dev] != SNDRV_AUTO_PORT) {
@@ -658,56 +647,32 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
if (snd_mpu401_uart_new(card, 0,
MPU401_HW_MPU401,
mpu_port[dev], 0,
- mpu_irq[dev], IRQF_DISABLED,
- NULL) < 0)
- snd_printk(KERN_ERR "no MPU-401 device at 0x%lx ?\n",
- mpu_port[dev]);
+ mpu_irq[dev], NULL) < 0)
+ dev_err(devptr, "no MPU-401 device at 0x%lx ?\n",
+ mpu_port[dev]);
}
- strcpy(card->driver, DRV_NAME);
- strcpy(card->shortname, "SC-6000");
+ strscpy(card->driver, DRV_NAME);
+ strscpy(card->shortname, "SC-6000");
sprintf(card->longname, "Gallant SC-6000 at 0x%lx, irq %d, dma %d",
mss_port[dev], xirq, xdma);
- snd_card_set_dev(card, devptr);
-
err = snd_card_register(card);
if (err < 0)
- goto err_unmap2;
+ return err;
dev_set_drvdata(devptr, card);
return 0;
-
-err_unmap2:
- sc6000_setup_board(*vport, 0);
- release_region(mss_port[dev], 4);
-err_unmap1:
- release_region(port[dev], 0x10);
-err_exit:
- snd_card_free(card);
- return err;
}
-static int __devexit snd_sc6000_remove(struct device *devptr, unsigned int dev)
+static int snd_sc6000_probe(struct device *devptr, unsigned int dev)
{
- struct snd_card *card = dev_get_drvdata(devptr);
- char __iomem **vport = card->private_data;
-
- if (sc6000_setup_board(*vport, 0) < 0)
- snd_printk(KERN_WARNING "sc6000_setup_board failed on exit!\n");
-
- release_region(port[dev], 0x10);
- release_region(mss_port[dev], 4);
-
- dev_set_drvdata(devptr, NULL);
- snd_card_free(card);
- return 0;
+ return snd_card_free_on_error(devptr, __snd_sc6000_probe(devptr, dev));
}
static struct isa_driver snd_sc6000_driver = {
.match = snd_sc6000_match,
.probe = snd_sc6000_probe,
- .remove = __devexit_p(snd_sc6000_remove),
/* FIXME: suspend/resume */
.driver = {
.name = DRV_NAME,
@@ -715,15 +680,4 @@ static struct isa_driver snd_sc6000_driver = {
};
-static int __init alsa_card_sc6000_init(void)
-{
- return isa_register_driver(&snd_sc6000_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_sc6000_exit(void)
-{
- isa_unregister_driver(&snd_sc6000_driver);
-}
-
-module_init(alsa_card_sc6000_init)
-module_exit(alsa_card_sc6000_exit)
+module_isa_driver(snd_sc6000_driver, SNDRV_CARDS);
diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c
index e2d5d2d3ed96..a31ca75774a6 100644
--- a/sound/isa/sscape.c
+++ b/sound/isa/sscape.c
@@ -1,34 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Low-level ALSA driver for the ENSONIQ SoundScape
* Copyright (c) by Chris Rankin
*
* This driver was written in part using information obtained from
* the OSS/Free SoundScape driver, written by Hannu Savolainen.
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/err.h>
+#include <linux/io.h>
#include <linux/isa.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/pnp.h>
#include <linux/spinlock.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/wss.h>
@@ -62,22 +49,22 @@ MODULE_PARM_DESC(index, "Index number for SoundScape soundcard");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "Description for SoundScape card");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for SoundScape driver.");
-module_param_array(wss_port, long, NULL, 0444);
+module_param_hw_array(wss_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(wss_port, "WSS Port # for SoundScape driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for SoundScape driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU401 IRQ # for SoundScape driver.");
-module_param_array(dma, int, NULL, 0444);
+module_param_hw_array(dma, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma, "DMA # for SoundScape driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for SoundScape driver.");
module_param_array(joystick, bool, NULL, 0444);
@@ -87,7 +74,7 @@ MODULE_PARM_DESC(joystick, "Enable gameport.");
static int isa_registered;
static int pnp_registered;
-static struct pnp_card_device_id sscape_pnpids[] = {
+static const struct pnp_card_device_id sscape_pnpids[] = {
{ .id = "ENS3081", .devs = { { "ENS0000" } } }, /* Soundscape PnP */
{ .id = "ENS4081", .devs = { { "ENS1011" } } }, /* VIVO90 */
{ .id = "" } /* end */
@@ -151,6 +138,7 @@ struct soundscape {
struct snd_wss *chip;
unsigned char midi_vol;
+ struct device *dev;
};
#define INVALID_IRQ ((unsigned)-1)
@@ -166,16 +154,17 @@ static inline struct soundscape *get_card_soundscape(struct snd_card *c)
* I think this means that the memory has to map to
* contiguous pages of physical memory.
*/
-static struct snd_dma_buffer *get_dmabuf(struct snd_dma_buffer *buf,
+static struct snd_dma_buffer *get_dmabuf(struct soundscape *s,
+ struct snd_dma_buffer *buf,
unsigned long size)
{
if (buf) {
if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
+ s->chip->card->dev,
size, buf) < 0) {
- snd_printk(KERN_ERR "sscape: Failed to allocate "
- "%lu bytes for DMA\n",
- size);
+ dev_err(s->dev,
+ "sscape: Failed to allocate %lu bytes for DMA\n",
+ size);
return NULL;
}
}
@@ -211,11 +200,8 @@ static inline void sscape_write_unsafe(unsigned io_base, enum GA_REG reg,
static void sscape_write(struct soundscape *s, enum GA_REG reg,
unsigned char val)
{
- unsigned long flags;
-
- spin_lock_irqsave(&s->lock, flags);
+ guard(spinlock_irqsave)(&s->lock);
sscape_write_unsafe(s->io_base, reg, val);
- spin_unlock_irqrestore(&s->lock, flags);
}
/*
@@ -320,7 +306,7 @@ static inline int verify_mpu401(const struct snd_mpu401 *mpu)
}
/*
- * This is apparently the standard way to initailise an MPU-401
+ * This is apparently the standard way to initialise an MPU-401
*/
static inline void initialise_mpu401(const struct snd_mpu401 *mpu)
{
@@ -340,18 +326,7 @@ static void activate_ad1845_unsafe(unsigned io_base)
}
/*
- * Do the necessary ALSA-level cleanup to deallocate our driver ...
- */
-static void soundscape_free(struct snd_card *c)
-{
- struct soundscape *sscape = get_card_soundscape(c);
- release_and_free_resource(sscape->io_res);
- release_and_free_resource(sscape->wss_res);
- free_dma(sscape->chip->dma1);
-}
-
-/*
- * Tell the SoundScape to begin a DMA tranfer using the given channel.
+ * Tell the SoundScape to begin a DMA transfer using the given channel.
* All locking issues are left to the caller.
*/
static void sscape_start_dma_unsafe(unsigned io_base, enum GA_REG reg)
@@ -389,12 +364,11 @@ static int obp_startup_ack(struct soundscape *s, unsigned timeout)
unsigned long end_time = jiffies + msecs_to_jiffies(timeout);
do {
- unsigned long flags;
int x;
- spin_lock_irqsave(&s->lock, flags);
- x = host_read_unsafe(s->io_base);
- spin_unlock_irqrestore(&s->lock, flags);
+ scoped_guard(spinlock_irqsave, &s->lock) {
+ x = host_read_unsafe(s->io_base);
+ }
if (x == 0xfe || x == 0xff)
return 1;
@@ -416,12 +390,11 @@ static int host_startup_ack(struct soundscape *s, unsigned timeout)
unsigned long end_time = jiffies + msecs_to_jiffies(timeout);
do {
- unsigned long flags;
int x;
- spin_lock_irqsave(&s->lock, flags);
- x = host_read_unsafe(s->io_base);
- spin_unlock_irqrestore(&s->lock, flags);
+ scoped_guard(spinlock_irqsave, &s->lock) {
+ x = host_read_unsafe(s->io_base);
+ }
if (x == 0xfe)
return 1;
@@ -437,71 +410,64 @@ static int host_startup_ack(struct soundscape *s, unsigned timeout)
static int upload_dma_data(struct soundscape *s, const unsigned char *data,
size_t size)
{
- unsigned long flags;
struct snd_dma_buffer dma;
int ret;
unsigned char val;
- if (!get_dmabuf(&dma, PAGE_ALIGN(32 * 1024)))
+ if (!get_dmabuf(s, &dma, PAGE_ALIGN(32 * 1024)))
return -ENOMEM;
- spin_lock_irqsave(&s->lock, flags);
-
- /*
- * Reset the board ...
- */
- val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
- sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val & 0x3f);
+ scoped_guard(spinlock_irqsave, &s->lock) {
- /*
- * Enable the DMA channels and configure them ...
- */
- val = (s->chip->dma1 << 4) | DMA_8BIT;
- sscape_write_unsafe(s->io_base, GA_DMAA_REG, val);
- sscape_write_unsafe(s->io_base, GA_DMAB_REG, 0x20);
-
- /*
- * Take the board out of reset ...
- */
- val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
- sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x80);
-
- /*
- * Upload the firmware to the SoundScape
- * board through the DMA channel ...
- */
- while (size != 0) {
- unsigned long len;
+ /*
+ * Reset the board ...
+ */
+ val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val & 0x3f);
- len = min(size, dma.bytes);
- memcpy(dma.area, data, len);
- data += len;
- size -= len;
+ /*
+ * Enable the DMA channels and configure them ...
+ */
+ val = (s->chip->dma1 << 4) | DMA_8BIT;
+ sscape_write_unsafe(s->io_base, GA_DMAA_REG, val);
+ sscape_write_unsafe(s->io_base, GA_DMAB_REG, 0x20);
- snd_dma_program(s->chip->dma1, dma.addr, len, DMA_MODE_WRITE);
- sscape_start_dma_unsafe(s->io_base, GA_DMAA_REG);
- if (!sscape_wait_dma_unsafe(s->io_base, GA_DMAA_REG, 5000)) {
- /*
- * Don't forget to release this spinlock we're holding
- */
- spin_unlock_irqrestore(&s->lock, flags);
+ /*
+ * Take the board out of reset ...
+ */
+ val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x80);
- snd_printk(KERN_ERR
- "sscape: DMA upload has timed out\n");
- ret = -EAGAIN;
- goto _release_dma;
- }
- } /* while */
+ /*
+ * Upload the firmware to the SoundScape
+ * board through the DMA channel ...
+ */
+ while (size != 0) {
+ unsigned long len;
+
+ len = min(size, dma.bytes);
+ memcpy(dma.area, data, len);
+ data += len;
+ size -= len;
+
+ snd_dma_program(s->chip->dma1, dma.addr, len, DMA_MODE_WRITE);
+ sscape_start_dma_unsafe(s->io_base, GA_DMAA_REG);
+ if (!sscape_wait_dma_unsafe(s->io_base, GA_DMAA_REG, 5000)) {
+ dev_err(s->dev, "sscape: DMA upload has timed out\n");
+ ret = -EAGAIN;
+ goto _release_dma;
+ }
+ } /* while */
- set_host_mode_unsafe(s->io_base);
- outb(0x0, s->io_base);
+ set_host_mode_unsafe(s->io_base);
+ outb(0x0, s->io_base);
- /*
- * Boot the board ... (I think)
- */
- val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
- sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x40);
- spin_unlock_irqrestore(&s->lock, flags);
+ /*
+ * Boot the board ... (I think)
+ */
+ val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
+ sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x40);
+ }
/*
* If all has gone well, then the board should acknowledge
@@ -510,12 +476,11 @@ static int upload_dma_data(struct soundscape *s, const unsigned char *data,
*/
ret = 0;
if (!obp_startup_ack(s, 5000)) {
- snd_printk(KERN_ERR "sscape: No response "
- "from on-board processor after upload\n");
+ dev_err(s->dev,
+ "sscape: No response from on-board processor after upload\n");
ret = -EAGAIN;
} else if (!host_startup_ack(s, 5000)) {
- snd_printk(KERN_ERR
- "sscape: SoundScape failed to initialise\n");
+ dev_err(s->dev, "sscape: SoundScape failed to initialise\n");
ret = -EAGAIN;
}
@@ -537,33 +502,30 @@ _release_dma:
static int sscape_upload_bootblock(struct snd_card *card)
{
struct soundscape *sscape = get_card_soundscape(card);
- unsigned long flags;
const struct firmware *init_fw = NULL;
int data = 0;
int ret;
ret = request_firmware(&init_fw, "scope.cod", card->dev);
if (ret < 0) {
- snd_printk(KERN_ERR "sscape: Error loading scope.cod");
+ dev_err(card->dev, "sscape: Error loading scope.cod");
return ret;
}
ret = upload_dma_data(sscape, init_fw->data, init_fw->size);
release_firmware(init_fw);
- spin_lock_irqsave(&sscape->lock, flags);
+ guard(spinlock_irqsave)(&sscape->lock);
if (ret == 0)
data = host_read_ctrl_unsafe(sscape->io_base, 100);
if (data & 0x10)
sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2f);
- spin_unlock_irqrestore(&sscape->lock, flags);
-
data &= 0xf;
if (ret == 0 && data > 7) {
- snd_printk(KERN_ERR
- "sscape: timeout reading firmware version\n");
+ dev_err(card->dev,
+ "sscape: timeout reading firmware version\n");
ret = -EAGAIN;
}
@@ -580,18 +542,18 @@ static int sscape_upload_microcode(struct snd_card *card, int version)
char name[14];
int err;
- snprintf(name, sizeof(name), "sndscape.co%d", version);
+ scnprintf(name, sizeof(name), "sndscape.co%d", version);
err = request_firmware(&init_fw, name, card->dev);
if (err < 0) {
- snd_printk(KERN_ERR "sscape: Error loading sndscape.co%d",
- version);
+ dev_err(card->dev, "sscape: Error loading sndscape.co%d",
+ version);
return err;
}
err = upload_dma_data(sscape, init_fw->data, init_fw->size);
if (err == 0)
- snd_printk(KERN_INFO "sscape: MIDI firmware loaded %d KBs\n",
- init_fw->size >> 10);
+ dev_info(card->dev, "sscape: MIDI firmware loaded %zu KBs\n",
+ init_fw->size >> 10);
release_firmware(init_fw);
@@ -617,11 +579,9 @@ static int sscape_midi_get(struct snd_kcontrol *kctl,
struct snd_wss *chip = snd_kcontrol_chip(kctl);
struct snd_card *card = chip->card;
register struct soundscape *s = get_card_soundscape(card);
- unsigned long flags;
- spin_lock_irqsave(&s->lock, flags);
+ guard(spinlock_irqsave)(&s->lock);
uctl->value.integer.value[0] = s->midi_vol;
- spin_unlock_irqrestore(&s->lock, flags);
return 0;
}
@@ -631,11 +591,10 @@ static int sscape_midi_put(struct snd_kcontrol *kctl,
struct snd_wss *chip = snd_kcontrol_chip(kctl);
struct snd_card *card = chip->card;
struct soundscape *s = get_card_soundscape(card);
- unsigned long flags;
int change;
unsigned char new_val;
- spin_lock_irqsave(&s->lock, flags);
+ guard(spinlock_irqsave)(&s->lock);
new_val = uctl->value.integer.value[0] & 127;
/*
@@ -666,11 +625,10 @@ __skip_change:
*/
set_midi_mode_unsafe(s->io_base);
- spin_unlock_irqrestore(&s->lock, flags);
return change;
}
-static struct snd_kcontrol_new midi_mixer_ctl = {
+static const struct snd_kcontrol_new midi_mixer_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "MIDI",
.info = sscape_midi_info,
@@ -683,7 +641,7 @@ static struct snd_kcontrol_new midi_mixer_ctl = {
* These IRQs are encoded as bit patterns so that they can be
* written to the control registers.
*/
-static unsigned __devinit get_irq_config(int sscape_type, int irq)
+static unsigned get_irq_config(int sscape_type, int irq)
{
static const int valid_irq[] = { 9, 5, 7, 10 };
static const int old_irq[] = { 9, 7, 5, 15 };
@@ -706,7 +664,7 @@ static unsigned __devinit get_irq_config(int sscape_type, int irq)
* Perform certain arcane port-checks to see whether there
* is a SoundScape board lurking behind the given ports.
*/
-static int __devinit detect_sscape(struct soundscape *s, long wss_io)
+static int detect_sscape(struct soundscape *s, long wss_io)
{
unsigned long flags;
unsigned d;
@@ -806,8 +764,8 @@ _done:
static int mpu401_open(struct snd_mpu401 *mpu)
{
if (!verify_mpu401(mpu)) {
- snd_printk(KERN_ERR "sscape: MIDI disabled, "
- "please load firmware\n");
+ dev_err(mpu->rmidi->card->dev,
+ "sscape: MIDI disabled, please load firmware\n");
return -ENODEV;
}
@@ -815,18 +773,17 @@ static int mpu401_open(struct snd_mpu401 *mpu)
}
/*
- * Initialse an MPU-401 subdevice for MIDI support on the SoundScape.
+ * Initialise an MPU-401 subdevice for MIDI support on the SoundScape.
*/
-static int __devinit create_mpu401(struct snd_card *card, int devnum,
- unsigned long port, int irq)
+static int create_mpu401(struct snd_card *card, int devnum,
+ unsigned long port, int irq)
{
struct soundscape *sscape = get_card_soundscape(card);
struct snd_rawmidi *rawmidi;
int err;
err = snd_mpu401_uart_new(card, devnum, MPU401_HW_MPU401, port,
- MPU401_INFO_INTEGRATED, irq, IRQF_DISABLED,
- &rawmidi);
+ MPU401_INFO_INTEGRATED, irq, &rawmidi);
if (err == 0) {
struct snd_mpu401 *mpu = rawmidi->private_data;
mpu->open_input = mpu401_open;
@@ -846,8 +803,8 @@ static int __devinit create_mpu401(struct snd_card *card, int devnum,
* try to support at least some of the extra bits by overriding
* some of the CS4231 callback.
*/
-static int __devinit create_ad1845(struct snd_card *card, unsigned port,
- int irq, int dma1, int dma2)
+static int create_ad1845(struct snd_card *card, unsigned port,
+ int irq, int dma1, int dma2)
{
register struct soundscape *sscape = get_card_soundscape(card);
struct snd_wss *chip;
@@ -877,9 +834,6 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,
err = snd_wss_create(card, port, -1, irq, dma1, dma2,
codec_type, WSS_HWSHARE_DMA1, &chip);
if (!err) {
- unsigned long flags;
- struct snd_pcm *pcm;
-
if (sscape->type != SSCAPE_VIVO) {
/*
* The input clock frequency on the SoundScape must
@@ -887,31 +841,31 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,
* to get the playback to sound correct ...
*/
snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_wss_out(chip, AD1845_CLOCK, 0x20);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_wss_out(chip, AD1845_CLOCK, 0x20);
+ }
snd_wss_mce_down(chip);
}
- err = snd_wss_pcm(chip, 0, &pcm);
+ err = snd_wss_pcm(chip, 0);
if (err < 0) {
- snd_printk(KERN_ERR "sscape: No PCM device "
- "for AD1845 chip\n");
+ dev_err(card->dev,
+ "sscape: No PCM device for AD1845 chip\n");
goto _error;
}
err = snd_wss_mixer(chip);
if (err < 0) {
- snd_printk(KERN_ERR "sscape: No mixer device "
- "for AD1845 chip\n");
+ dev_err(card->dev,
+ "sscape: No mixer device for AD1845 chip\n");
goto _error;
}
if (chip->hardware != WSS_HW_AD1848) {
- err = snd_wss_timer(chip, 0, NULL);
+ err = snd_wss_timer(chip, 0);
if (err < 0) {
- snd_printk(KERN_ERR "sscape: No timer device "
- "for AD1845 chip\n");
+ dev_err(card->dev,
+ "sscape: No timer device for AD1845 chip\n");
goto _error;
}
}
@@ -920,8 +874,8 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,
err = snd_ctl_add(card,
snd_ctl_new1(&midi_mixer_ctl, chip));
if (err < 0) {
- snd_printk(KERN_ERR "sscape: Could not create "
- "MIDI mixer control\n");
+ dev_err(card->dev,
+ "sscape: Could not create MIDI mixer control\n");
goto _error;
}
}
@@ -938,7 +892,7 @@ _error:
* Create an ALSA soundcard entry for the SoundScape, using
* the given list of port, IRQ and DMA resources.
*/
-static int __devinit create_sscape(int dev, struct snd_card *card)
+static int create_sscape(int dev, struct snd_card *card)
{
struct soundscape *sscape = get_card_soundscape(card);
unsigned dma_cfg;
@@ -946,7 +900,6 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
unsigned mpu_irq_cfg;
struct resource *io_res;
struct resource *wss_res;
- unsigned long flags;
int err;
int val;
const char *name;
@@ -955,30 +908,30 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
* Grab IO ports that we will need to probe so that we
* can detect and control this hardware ...
*/
- io_res = request_region(port[dev], 8, "SoundScape");
+ io_res = devm_request_region(card->dev, port[dev], 8, "SoundScape");
if (!io_res) {
- snd_printk(KERN_ERR
- "sscape: can't grab port 0x%lx\n", port[dev]);
+ dev_err(card->dev,
+ "sscape: can't grab port 0x%lx\n", port[dev]);
return -EBUSY;
}
wss_res = NULL;
if (sscape->type == SSCAPE_VIVO) {
- wss_res = request_region(wss_port[dev], 4, "SoundScape");
+ wss_res = devm_request_region(card->dev, wss_port[dev], 4,
+ "SoundScape");
if (!wss_res) {
- snd_printk(KERN_ERR "sscape: can't grab port 0x%lx\n",
- wss_port[dev]);
- err = -EBUSY;
- goto _release_region;
+ dev_err(card->dev, "sscape: can't grab port 0x%lx\n",
+ wss_port[dev]);
+ return -EBUSY;
}
}
/*
* Grab one DMA channel ...
*/
- err = request_dma(dma[dev], "SoundScape");
+ err = snd_devm_request_dma(card->dev, dma[dev], "SoundScape");
if (err < 0) {
- snd_printk(KERN_ERR "sscape: can't grab DMA %d\n", dma[dev]);
- goto _release_region;
+ dev_err(card->dev, "sscape: can't grab DMA %d\n", dma[dev]);
+ return err;
}
spin_lock_init(&sscape->lock);
@@ -987,10 +940,9 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
sscape->io_base = port[dev];
if (!detect_sscape(sscape, wss_port[dev])) {
- printk(KERN_ERR "sscape: hardware not detected at 0x%x\n",
+ dev_err(card->dev, "sscape: hardware not detected at 0x%x\n",
sscape->io_base);
- err = -ENODEV;
- goto _release_dma;
+ return -ENODEV;
}
switch (sscape->type) {
@@ -1011,21 +963,21 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
break;
}
- printk(KERN_INFO "sscape: %s card detected at 0x%x, using IRQ %d, DMA %d\n",
- name, sscape->io_base, irq[dev], dma[dev]);
+ dev_info(card->dev, "sscape: %s card detected at 0x%x, using IRQ %d, DMA %d\n",
+ name, sscape->io_base, irq[dev], dma[dev]);
/*
* Check that the user didn't pass us garbage data ...
*/
irq_cfg = get_irq_config(sscape->type, irq[dev]);
if (irq_cfg == INVALID_IRQ) {
- snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", irq[dev]);
+ dev_err(card->dev, "sscape: Invalid IRQ %d\n", irq[dev]);
return -ENXIO;
}
mpu_irq_cfg = get_irq_config(sscape->type, mpu_irq[dev]);
if (mpu_irq_cfg == INVALID_IRQ) {
- snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", mpu_irq[dev]);
+ dev_err(card->dev, "sscape: Invalid IRQ %d\n", mpu_irq[dev]);
return -ENXIO;
}
@@ -1033,34 +985,34 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
* Tell the on-board devices where their resources are (I think -
* I can't be sure without a datasheet ... So many magic values!)
*/
- spin_lock_irqsave(&sscape->lock, flags);
+ scoped_guard(spinlock_irqsave, &sscape->lock) {
- sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e);
- sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00);
+ sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e);
+ sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00);
- /*
- * Enable and configure the DMA channels ...
- */
- sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50);
- dma_cfg = (sscape->ic_type == IC_OPUS ? 0x40 : 0x70);
- sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg);
- sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20);
-
- mpu_irq_cfg |= mpu_irq_cfg << 2;
- val = sscape_read_unsafe(sscape->io_base, GA_HMCTL_REG) & 0xF7;
- if (joystick[dev])
- val |= 8;
- sscape_write_unsafe(sscape->io_base, GA_HMCTL_REG, val | 0x10);
- sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG, 0xf0 | mpu_irq_cfg);
- sscape_write_unsafe(sscape->io_base,
- GA_CDCFG_REG, 0x09 | DMA_8BIT
- | (dma[dev] << 4) | (irq_cfg << 1));
- /*
- * Enable the master IRQ ...
- */
- sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x80);
+ /*
+ * Enable and configure the DMA channels ...
+ */
+ sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50);
+ dma_cfg = (sscape->ic_type == IC_OPUS ? 0x40 : 0x70);
+ sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg);
+ sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20);
+
+ mpu_irq_cfg |= mpu_irq_cfg << 2;
+ val = sscape_read_unsafe(sscape->io_base, GA_HMCTL_REG) & 0xF7;
+ if (joystick[dev])
+ val |= 8;
+ sscape_write_unsafe(sscape->io_base, GA_HMCTL_REG, val | 0x10);
+ sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG, 0xf0 | mpu_irq_cfg);
+ sscape_write_unsafe(sscape->io_base,
+ GA_CDCFG_REG, 0x09 | DMA_8BIT
+ | (dma[dev] << 4) | (irq_cfg << 1));
+ /*
+ * Enable the master IRQ ...
+ */
+ sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x80);
- spin_unlock_irqrestore(&sscape->lock, flags);
+ }
/*
* We have now enabled the codec chip, and so we should
@@ -1069,13 +1021,13 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
err = create_ad1845(card, wss_port[dev], irq[dev],
dma[dev], dma2[dev]);
if (err < 0) {
- snd_printk(KERN_ERR
- "sscape: No AD1845 device at 0x%lx, IRQ %d\n",
- wss_port[dev], irq[dev]);
- goto _release_dma;
+ dev_err(card->dev,
+ "sscape: No AD1845 device at 0x%lx, IRQ %d\n",
+ wss_port[dev], irq[dev]);
+ return err;
}
- strcpy(card->driver, "SoundScape");
- strcpy(card->shortname, name);
+ strscpy(card->driver, "SoundScape");
+ strscpy(card->shortname, name);
snprintf(card->longname, sizeof(card->longname),
"%s at 0x%lx, IRQ %d, DMA1 %d, DMA2 %d\n",
name, sscape->chip->port, sscape->chip->irq,
@@ -1091,16 +1043,16 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
err = create_mpu401(card, MIDI_DEVNUM, port[dev],
mpu_irq[dev]);
if (err < 0) {
- snd_printk(KERN_ERR "sscape: Failed to create "
- "MPU-401 device at 0x%lx\n",
- port[dev]);
- goto _release_dma;
+ dev_err(card->dev,
+ "sscape: Failed to create MPU-401 device at 0x%lx\n",
+ port[dev]);
+ return err;
}
/*
* Initialize mixer
*/
- spin_lock_irqsave(&sscape->lock, flags);
+ guard(spinlock_irqsave)(&sscape->lock);
sscape->midi_vol = 0;
host_write_ctrl_unsafe(sscape->io_base,
CMD_SET_MIDI_VOL, 100);
@@ -1117,32 +1069,14 @@ static int __devinit create_sscape(int dev, struct snd_card *card)
host_write_ctrl_unsafe(sscape->io_base, CMD_ACK, 100);
set_midi_mode_unsafe(sscape->io_base);
- spin_unlock_irqrestore(&sscape->lock, flags);
}
}
- /*
- * Now that we have successfully created this sound card,
- * it is safe to store the pointer.
- * NOTE: we only register the sound card's "destructor"
- * function now that our "constructor" has completed.
- */
- card->private_free = soundscape_free;
-
return 0;
-
-_release_dma:
- free_dma(dma[dev]);
-
-_release_region:
- release_and_free_resource(wss_res);
- release_and_free_resource(io_res);
-
- return err;
}
-static int __devinit snd_sscape_match(struct device *pdev, unsigned int i)
+static int snd_sscape_match(struct device *pdev, unsigned int i)
{
/*
* Make sure we were given ALL of the other parameters.
@@ -1153,54 +1087,42 @@ static int __devinit snd_sscape_match(struct device *pdev, unsigned int i)
if (irq[i] == SNDRV_AUTO_IRQ ||
mpu_irq[i] == SNDRV_AUTO_IRQ ||
dma[i] == SNDRV_AUTO_DMA) {
- printk(KERN_INFO
- "sscape: insufficient parameters, "
- "need IO, IRQ, MPU-IRQ and DMA\n");
+ dev_info(pdev,
+ "sscape: insufficient parameters, need IO, IRQ, MPU-IRQ and DMA\n");
return 0;
}
return 1;
}
-static int __devinit snd_sscape_probe(struct device *pdev, unsigned int dev)
+static int snd_sscape_probe(struct device *pdev, unsigned int dev)
{
struct snd_card *card;
struct soundscape *sscape;
int ret;
- ret = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(struct soundscape), &card);
+ ret = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct soundscape), &card);
if (ret < 0)
return ret;
sscape = get_card_soundscape(card);
+ sscape->dev = pdev;
sscape->type = SSCAPE;
dma[dev] &= 0x03;
- snd_card_set_dev(card, pdev);
ret = create_sscape(dev, card);
if (ret < 0)
- goto _release_card;
+ return ret;
ret = snd_card_register(card);
if (ret < 0) {
- snd_printk(KERN_ERR "sscape: Failed to register sound card\n");
- goto _release_card;
+ dev_err(pdev, "sscape: Failed to register sound card\n");
+ return ret;
}
dev_set_drvdata(pdev, card);
return 0;
-
-_release_card:
- snd_card_free(card);
- return ret;
-}
-
-static int __devexit snd_sscape_remove(struct device *devptr, unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
- return 0;
}
#define DEV_NAME "sscape"
@@ -1208,7 +1130,6 @@ static int __devexit snd_sscape_remove(struct device *devptr, unsigned int dev)
static struct isa_driver snd_sscape_driver = {
.match = snd_sscape_match,
.probe = snd_sscape_probe,
- .remove = __devexit_p(snd_sscape_remove),
/* FIXME: suspend/resume */
.driver = {
.name = DEV_NAME
@@ -1216,7 +1137,7 @@ static struct isa_driver snd_sscape_driver = {
};
#ifdef CONFIG_PNP
-static inline int __devinit get_next_autoindex(int i)
+static inline int get_next_autoindex(int i)
{
while (i < SNDRV_CARDS && port[i] != SNDRV_AUTO_PORT)
++i;
@@ -1224,8 +1145,8 @@ static inline int __devinit get_next_autoindex(int i)
}
-static int __devinit sscape_pnp_detect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int sscape_pnp_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
static int idx = 0;
struct pnp_dev *dev;
@@ -1250,7 +1171,7 @@ static int __devinit sscape_pnp_detect(struct pnp_card_link *pcard,
if (!pnp_is_active(dev)) {
if (pnp_activate_dev(dev) < 0) {
- snd_printk(KERN_INFO "sscape: device is inactive\n");
+ dev_info(&dev->dev, "sscape: device is inactive\n");
return -EBUSY;
}
}
@@ -1259,12 +1180,14 @@ static int __devinit sscape_pnp_detect(struct pnp_card_link *pcard,
* Create a new ALSA sound card entry, in anticipation
* of detecting our hardware ...
*/
- ret = snd_card_create(index[idx], id[idx], THIS_MODULE,
- sizeof(struct soundscape), &card);
+ ret = snd_devm_card_new(&pcard->card->dev,
+ index[idx], id[idx], THIS_MODULE,
+ sizeof(struct soundscape), &card);
if (ret < 0)
return ret;
sscape = get_card_soundscape(card);
+ sscape->dev = card->dev;
/*
* Identify card model ...
@@ -1288,31 +1211,20 @@ static int __devinit sscape_pnp_detect(struct pnp_card_link *pcard,
wss_port[idx] = pnp_port_start(dev, 1);
dma2[idx] = pnp_dma(dev, 1);
}
- snd_card_set_dev(card, &pcard->card->dev);
ret = create_sscape(idx, card);
if (ret < 0)
- goto _release_card;
+ return ret;
ret = snd_card_register(card);
if (ret < 0) {
- snd_printk(KERN_ERR "sscape: Failed to register sound card\n");
- goto _release_card;
+ dev_err(card->dev, "sscape: Failed to register sound card\n");
+ return ret;
}
pnp_set_card_drvdata(pcard, card);
++idx;
return 0;
-
-_release_card:
- snd_card_free(card);
- return ret;
-}
-
-static void __devexit sscape_pnp_remove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
}
static struct pnp_card_driver sscape_pnpc_driver = {
@@ -1320,7 +1232,6 @@ static struct pnp_card_driver sscape_pnpc_driver = {
.name = "sscape",
.id_table = sscape_pnpids,
.probe = sscape_pnp_detect,
- .remove = __devexit_p(sscape_pnp_remove),
};
#endif /* CONFIG_PNP */
diff --git a/sound/isa/wavefront/Makefile b/sound/isa/wavefront/Makefile
index 601bdddd44d0..3ba85fb2e6cd 100644
--- a/sound/isa/wavefront/Makefile
+++ b/sound/isa/wavefront/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-wavefront-objs := wavefront.o wavefront_fx.o wavefront_synth.o wavefront_midi.o
+snd-wavefront-y := wavefront.o wavefront_fx.o wavefront_synth.o wavefront_midi.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_WAVEFRONT) += snd-wavefront.o
diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c
index 711670e4a425..07c68568091d 100644
--- a/sound/isa/wavefront/wavefront.c
+++ b/sound/isa/wavefront/wavefront.c
@@ -1,22 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA card-level driver for Turtle Beach Wavefront cards
* (Maui,Tropez,Tropez+)
*
* Copyright (c) 1997-1999 by Paul Barton-Davis <pbd@op.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
@@ -24,7 +11,7 @@
#include <linux/err.h>
#include <linux/isa.h>
#include <linux/pnp.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/opl3.h>
@@ -34,13 +21,12 @@
MODULE_AUTHOR("Paul Barton-Davis <pbd@op.net>");
MODULE_DESCRIPTION("Turtle Beach Wavefront");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Turtle Beach,Maui/Tropez/Tropez+}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
#ifdef CONFIG_PNP
-static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
#endif
static long cs4232_pcm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static int cs4232_pcm_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,11,12,15 */
@@ -51,7 +37,7 @@ static int ics2115_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,9,11,12,15 */
static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */
static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */
-static int use_cs4232_midi[SNDRV_CARDS];
+static bool use_cs4232_midi[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for WaveFront soundcard.");
@@ -63,23 +49,23 @@ MODULE_PARM_DESC(enable, "Enable WaveFront soundcard.");
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "ISA PnP detection for WaveFront soundcards.");
#endif
-module_param_array(cs4232_pcm_port, long, NULL, 0444);
+module_param_hw_array(cs4232_pcm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(cs4232_pcm_port, "Port # for CS4232 PCM interface.");
-module_param_array(cs4232_pcm_irq, int, NULL, 0444);
+module_param_hw_array(cs4232_pcm_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(cs4232_pcm_irq, "IRQ # for CS4232 PCM interface.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for CS4232 PCM interface.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for CS4232 PCM interface.");
-module_param_array(cs4232_mpu_port, long, NULL, 0444);
+module_param_hw_array(cs4232_mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(cs4232_mpu_port, "port # for CS4232 MPU-401 interface.");
-module_param_array(cs4232_mpu_irq, int, NULL, 0444);
+module_param_hw_array(cs4232_mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(cs4232_mpu_irq, "IRQ # for CS4232 MPU-401 interface.");
-module_param_array(ics2115_irq, int, NULL, 0444);
+module_param_hw_array(ics2115_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(ics2115_irq, "IRQ # for ICS2115.");
-module_param_array(ics2115_port, long, NULL, 0444);
+module_param_hw_array(ics2115_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(ics2115_port, "Port # for ICS2115.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port #.");
module_param_array(use_cs4232_midi, bool, NULL, 0444);
MODULE_PARM_DESC(use_cs4232_midi, "Use CS4232 MPU-401 interface (inaccessibly located inside your computer)");
@@ -88,7 +74,7 @@ MODULE_PARM_DESC(use_cs4232_midi, "Use CS4232 MPU-401 interface (inaccessibly lo
static int isa_registered;
static int pnp_registered;
-static struct pnp_card_device_id snd_wavefront_pnpids[] = {
+static const struct pnp_card_device_id snd_wavefront_pnpids[] = {
/* Tropez */
{ .id = "CSC7532", .devs = { { "CSC0000" }, { "CSC0010" }, { "PnPb006" }, { "CSC0004" } } },
/* Tropez+ */
@@ -98,7 +84,7 @@ static struct pnp_card_device_id snd_wavefront_pnpids[] = {
MODULE_DEVICE_TABLE(pnp_card, snd_wavefront_pnpids);
-static int __devinit
+static int
snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *card,
const struct pnp_card_device_id *id)
{
@@ -154,7 +140,7 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "PnP WSS pnp configure failure\n");
+ dev_err(&pdev->dev, "PnP WSS pnp configure failure\n");
return err;
}
@@ -170,7 +156,7 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "PnP ICS2115 pnp configure failure\n");
+ dev_err(&pdev->dev, "PnP ICS2115 pnp configure failure\n");
return err;
}
@@ -188,26 +174,27 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c
err = pnp_activate_dev(pdev);
if (err < 0) {
- snd_printk(KERN_ERR "PnP MPU401 pnp configure failure\n");
+ dev_err(&pdev->dev, "PnP MPU401 pnp configure failure\n");
cs4232_mpu_port[dev] = SNDRV_AUTO_PORT;
} else {
cs4232_mpu_port[dev] = pnp_port_start(pdev, 0);
cs4232_mpu_irq[dev] = pnp_irq(pdev, 0);
}
- snd_printk (KERN_INFO "CS4232 MPU: port=0x%lx, irq=%i\n",
- cs4232_mpu_port[dev],
- cs4232_mpu_irq[dev]);
+ dev_info(&pdev->dev, "CS4232 MPU: port=0x%lx, irq=%i\n",
+ cs4232_mpu_port[dev],
+ cs4232_mpu_irq[dev]);
}
- snd_printdd ("CS4232: pcm port=0x%lx, fm port=0x%lx, dma1=%i, dma2=%i, irq=%i\nICS2115: port=0x%lx, irq=%i\n",
- cs4232_pcm_port[dev],
- fm_port[dev],
- dma1[dev],
- dma2[dev],
- cs4232_pcm_irq[dev],
- ics2115_port[dev],
- ics2115_irq[dev]);
+ dev_dbg(&pdev->dev,
+ "CS4232: pcm port=0x%lx, fm port=0x%lx, dma1=%i, dma2=%i, irq=%i\nICS2115: port=0x%lx, irq=%i\n",
+ cs4232_pcm_port[dev],
+ fm_port[dev],
+ dma1[dev],
+ dma2[dev],
+ cs4232_pcm_irq[dev],
+ ics2115_port[dev],
+ ics2115_irq[dev]);
return 0;
}
@@ -231,10 +218,9 @@ static irqreturn_t snd_wavefront_ics2115_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static struct snd_hwdep * __devinit
-snd_wavefront_new_synth (struct snd_card *card,
- int hw_dev,
- snd_wavefront_card_t *acard)
+static struct snd_hwdep *snd_wavefront_new_synth(struct snd_card *card,
+ int hw_dev,
+ snd_wavefront_card_t *acard)
{
struct snd_hwdep *wavefront_synth;
@@ -248,7 +234,7 @@ snd_wavefront_new_synth (struct snd_card *card,
if (snd_hwdep_new(card, "WaveFront", hw_dev, &wavefront_synth) < 0)
return NULL;
- strcpy (wavefront_synth->name,
+ strscpy (wavefront_synth->name,
"WaveFront (ICS2115) wavetable synthesizer");
wavefront_synth->ops.open = snd_wavefront_synth_open;
wavefront_synth->ops.release = snd_wavefront_synth_release;
@@ -257,17 +243,16 @@ snd_wavefront_new_synth (struct snd_card *card,
return wavefront_synth;
}
-static struct snd_hwdep * __devinit
-snd_wavefront_new_fx (struct snd_card *card,
- int hw_dev,
- snd_wavefront_card_t *acard,
- unsigned long port)
+static struct snd_hwdep *snd_wavefront_new_fx(struct snd_card *card,
+ int hw_dev,
+ snd_wavefront_card_t *acard,
+ unsigned long port)
{
struct snd_hwdep *fx_processor;
if (snd_wavefront_fx_start (&acard->wavefront)) {
- snd_printk (KERN_ERR "cannot initialize YSS225 FX processor");
+ dev_err(card->dev, "cannot initialize YSS225 FX processor");
return NULL;
}
@@ -284,12 +269,11 @@ snd_wavefront_new_fx (struct snd_card *card,
static snd_wavefront_mpu_id internal_id = internal_mpu;
static snd_wavefront_mpu_id external_id = external_mpu;
-static struct snd_rawmidi *__devinit
-snd_wavefront_new_midi (struct snd_card *card,
- int midi_dev,
- snd_wavefront_card_t *acard,
- unsigned long port,
- snd_wavefront_mpu_id mpu)
+static struct snd_rawmidi *snd_wavefront_new_midi(struct snd_card *card,
+ int midi_dev,
+ snd_wavefront_card_t *acard,
+ unsigned long port,
+ snd_wavefront_mpu_id mpu)
{
struct snd_rawmidi *rmidi;
@@ -299,7 +283,7 @@ snd_wavefront_new_midi (struct snd_card *card,
first = 0;
acard->wavefront.midi.base = port;
if (snd_wavefront_midi_start (acard)) {
- snd_printk (KERN_ERR "cannot initialize MIDI interface\n");
+ dev_err(card->dev, "cannot initialize MIDI interface\n");
return NULL;
}
}
@@ -308,10 +292,10 @@ snd_wavefront_new_midi (struct snd_card *card,
return NULL;
if (mpu == internal_mpu) {
- strcpy(rmidi->name, "WaveFront MIDI (Internal)");
+ strscpy(rmidi->name, "WaveFront MIDI (Internal)");
rmidi->private_data = &internal_id;
} else {
- strcpy(rmidi->name, "WaveFront MIDI (External)");
+ strscpy(rmidi->name, "WaveFront MIDI (External)");
rmidi->private_data = &external_id;
}
@@ -325,26 +309,15 @@ snd_wavefront_new_midi (struct snd_card *card,
return rmidi;
}
-static void
-snd_wavefront_free(struct snd_card *card)
-{
- snd_wavefront_card_t *acard = (snd_wavefront_card_t *)card->private_data;
-
- if (acard) {
- release_and_free_resource(acard->wavefront.res_base);
- if (acard->wavefront.irq > 0)
- free_irq(acard->wavefront.irq, (void *)acard);
- }
-}
-
-static int snd_wavefront_card_new(int dev, struct snd_card **cardp)
+static int snd_wavefront_card_new(struct device *pdev, int dev,
+ struct snd_card **cardp)
{
struct snd_card *card;
snd_wavefront_card_t *acard;
int err;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(snd_wavefront_card_t), &card);
+ err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
+ sizeof(snd_wavefront_card_t), &card);
if (err < 0)
return err;
@@ -355,13 +328,12 @@ static int snd_wavefront_card_new(int dev, struct snd_card **cardp)
spin_lock_init(&acard->wavefront.midi.open);
spin_lock_init(&acard->wavefront.midi.virtual);
acard->wavefront.card = card;
- card->private_free = snd_wavefront_free;
*cardp = card;
return 0;
}
-static int __devinit
+static int
snd_wavefront_probe (struct snd_card *card, int dev)
{
snd_wavefront_card_t *acard = card->private_data;
@@ -378,15 +350,15 @@ snd_wavefront_probe (struct snd_card *card, int dev)
cs4232_pcm_irq[dev], dma1[dev], dma2[dev],
WSS_HW_DETECT, 0, &chip);
if (err < 0) {
- snd_printk(KERN_ERR "can't allocate WSS device\n");
+ dev_err(card->dev, "can't allocate WSS device\n");
return err;
}
- err = snd_wss_pcm(chip, 0, NULL);
+ err = snd_wss_pcm(chip, 0);
if (err < 0)
return err;
- err = snd_wss_timer(chip, 0, NULL);
+ err = snd_wss_timer(chip, 0);
if (err < 0)
return err;
@@ -398,7 +370,7 @@ snd_wavefront_probe (struct snd_card *card, int dev)
err = snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2,
OPL3_HW_OPL3_CS, 0, &opl3);
if (err < 0) {
- snd_printk (KERN_ERR "can't allocate or detect OPL3 synth\n");
+ dev_err(card->dev, "can't allocate or detect OPL3 synth\n");
return err;
}
@@ -410,29 +382,32 @@ snd_wavefront_probe (struct snd_card *card, int dev)
/* ------- ICS2115 Wavetable synth ------- */
- acard->wavefront.res_base = request_region(ics2115_port[dev], 16,
- "ICS2115");
+ acard->wavefront.res_base =
+ devm_request_region(card->dev, ics2115_port[dev], 16,
+ "ICS2115");
if (acard->wavefront.res_base == NULL) {
- snd_printk(KERN_ERR "unable to grab ICS2115 i/o region 0x%lx-0x%lx\n",
- ics2115_port[dev], ics2115_port[dev] + 16 - 1);
+ dev_err(card->dev, "unable to grab ICS2115 i/o region 0x%lx-0x%lx\n",
+ ics2115_port[dev], ics2115_port[dev] + 16 - 1);
return -EBUSY;
}
- if (request_irq(ics2115_irq[dev], snd_wavefront_ics2115_interrupt,
- IRQF_DISABLED, "ICS2115", acard)) {
- snd_printk(KERN_ERR "unable to use ICS2115 IRQ %d\n", ics2115_irq[dev]);
+ if (devm_request_irq(card->dev, ics2115_irq[dev],
+ snd_wavefront_ics2115_interrupt,
+ 0, "ICS2115", acard)) {
+ dev_err(card->dev, "unable to use ICS2115 IRQ %d\n", ics2115_irq[dev]);
return -EBUSY;
}
acard->wavefront.irq = ics2115_irq[dev];
+ card->sync_irq = acard->wavefront.irq;
acard->wavefront.base = ics2115_port[dev];
wavefront_synth = snd_wavefront_new_synth(card, hw_dev, acard);
if (wavefront_synth == NULL) {
- snd_printk (KERN_ERR "can't create WaveFront synth device\n");
+ dev_err(card->dev, "can't create WaveFront synth device\n");
return -ENOMEM;
}
- strcpy (wavefront_synth->name, "ICS2115 Wavetable MIDI Synthesizer");
+ strscpy (wavefront_synth->name, "ICS2115 Wavetable MIDI Synthesizer");
wavefront_synth->iface = SNDRV_HWDEP_IFACE_ICS2115;
hw_dev++;
@@ -440,7 +415,7 @@ snd_wavefront_probe (struct snd_card *card, int dev)
err = snd_wss_mixer(chip);
if (err < 0) {
- snd_printk (KERN_ERR "can't allocate mixer device\n");
+ dev_err(card->dev, "can't allocate mixer device\n");
return err;
}
@@ -449,10 +424,9 @@ snd_wavefront_probe (struct snd_card *card, int dev)
if (cs4232_mpu_port[dev] > 0 && cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) {
err = snd_mpu401_uart_new(card, midi_dev, MPU401_HW_CS4232,
cs4232_mpu_port[dev], 0,
- cs4232_mpu_irq[dev], IRQF_DISABLED,
- NULL);
+ cs4232_mpu_irq[dev], NULL);
if (err < 0) {
- snd_printk (KERN_ERR "can't allocate CS4232 MPU-401 device\n");
+ dev_err(card->dev, "can't allocate CS4232 MPU-401 device\n");
return err;
}
midi_dev++;
@@ -468,7 +442,7 @@ snd_wavefront_probe (struct snd_card *card, int dev)
ics2115_port[dev],
internal_mpu);
if (ics2115_internal_rmidi == NULL) {
- snd_printk (KERN_ERR "can't setup ICS2115 internal MIDI device\n");
+ dev_err(card->dev, "can't setup ICS2115 internal MIDI device\n");
return -ENOMEM;
}
midi_dev++;
@@ -484,7 +458,7 @@ snd_wavefront_probe (struct snd_card *card, int dev)
ics2115_port[dev],
external_mpu);
if (ics2115_external_rmidi == NULL) {
- snd_printk (KERN_ERR "can't setup ICS2115 external MIDI device\n");
+ dev_err(card->dev, "can't setup ICS2115 external MIDI device\n");
return -ENOMEM;
}
midi_dev++;
@@ -498,18 +472,18 @@ snd_wavefront_probe (struct snd_card *card, int dev)
acard,
ics2115_port[dev]);
if (fx_processor == NULL) {
- snd_printk (KERN_ERR "can't setup FX device\n");
+ dev_err(card->dev, "can't setup FX device\n");
return -ENOMEM;
}
hw_dev++;
- strcpy(card->driver, "Tropez+");
- strcpy(card->shortname, "Turtle Beach Tropez+");
+ strscpy(card->driver, "Tropez+");
+ strscpy(card->shortname, "Turtle Beach Tropez+");
} else {
/* Need a way to distinguish between Maui and Tropez */
- strcpy(card->driver, "WaveFront");
- strcpy(card->shortname, "Turtle Beach WaveFront");
+ strscpy(card->driver, "WaveFront");
+ strscpy(card->shortname, "Turtle Beach WaveFront");
}
/* ----- Register the card --------- */
@@ -542,8 +516,8 @@ snd_wavefront_probe (struct snd_card *card, int dev)
return snd_card_register(card);
}
-static int __devinit snd_wavefront_isa_match(struct device *pdev,
- unsigned int dev)
+static int snd_wavefront_isa_match(struct device *pdev,
+ unsigned int dev)
{
if (!enable[dev])
return 0;
@@ -552,49 +526,38 @@ static int __devinit snd_wavefront_isa_match(struct device *pdev,
return 0;
#endif
if (cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR "specify CS4232 port\n");
+ dev_err(pdev, "specify CS4232 port\n");
return 0;
}
if (ics2115_port[dev] == SNDRV_AUTO_PORT) {
- snd_printk(KERN_ERR "specify ICS2115 port\n");
+ dev_err(pdev, "specify ICS2115 port\n");
return 0;
}
return 1;
}
-static int __devinit snd_wavefront_isa_probe(struct device *pdev,
- unsigned int dev)
+static int snd_wavefront_isa_probe(struct device *pdev,
+ unsigned int dev)
{
struct snd_card *card;
int err;
- err = snd_wavefront_card_new(dev, &card);
+ err = snd_wavefront_card_new(pdev, dev, &card);
if (err < 0)
return err;
- snd_card_set_dev(card, pdev);
- if ((err = snd_wavefront_probe(card, dev)) < 0) {
- snd_card_free(card);
+ err = snd_wavefront_probe(card, dev);
+ if (err < 0)
return err;
- }
dev_set_drvdata(pdev, card);
return 0;
}
-static int __devexit snd_wavefront_isa_remove(struct device *devptr,
- unsigned int dev)
-{
- snd_card_free(dev_get_drvdata(devptr));
- dev_set_drvdata(devptr, NULL);
- return 0;
-}
-
#define DEV_NAME "wavefront"
static struct isa_driver snd_wavefront_driver = {
.match = snd_wavefront_isa_match,
.probe = snd_wavefront_isa_probe,
- .remove = __devexit_p(snd_wavefront_isa_remove),
/* FIXME: suspend, resume */
.driver = {
.name = DEV_NAME
@@ -603,8 +566,8 @@ static struct isa_driver snd_wavefront_driver = {
#ifdef CONFIG_PNP
-static int __devinit snd_wavefront_pnp_detect(struct pnp_card_link *pcard,
- const struct pnp_card_device_id *pid)
+static int snd_wavefront_pnp_detect(struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
{
static int dev;
struct snd_card *card;
@@ -617,20 +580,19 @@ static int __devinit snd_wavefront_pnp_detect(struct pnp_card_link *pcard,
if (dev >= SNDRV_CARDS)
return -ENODEV;
- res = snd_wavefront_card_new(dev, &card);
+ res = snd_wavefront_card_new(&pcard->card->dev, dev, &card);
if (res < 0)
return res;
if (snd_wavefront_pnp (dev, card->private_data, pcard, pid) < 0) {
if (cs4232_pcm_port[dev] == SNDRV_AUTO_PORT) {
- snd_printk (KERN_ERR "isapnp detection failed\n");
- snd_card_free (card);
+ dev_err(card->dev, "isapnp detection failed\n");
return -ENODEV;
}
}
- snd_card_set_dev(card, &pcard->card->dev);
- if ((res = snd_wavefront_probe(card, dev)) < 0)
+ res = snd_wavefront_probe(card, dev);
+ if (res < 0)
return res;
pnp_set_card_drvdata(pcard, card);
@@ -638,18 +600,11 @@ static int __devinit snd_wavefront_pnp_detect(struct pnp_card_link *pcard,
return 0;
}
-static void __devexit snd_wavefront_pnp_remove(struct pnp_card_link * pcard)
-{
- snd_card_free(pnp_get_card_drvdata(pcard));
- pnp_set_card_drvdata(pcard, NULL);
-}
-
static struct pnp_card_driver wavefront_pnpc_driver = {
.flags = PNP_DRIVER_RES_DISABLE,
.name = "wavefront",
.id_table = snd_wavefront_pnpids,
.probe = snd_wavefront_pnp_detect,
- .remove = __devexit_p(snd_wavefront_pnp_remove),
/* FIXME: suspend,resume */
};
diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c
index 657e2d6c01ac..beca35ce04f3 100644
--- a/sound/isa/wavefront/wavefront_fx.c
+++ b/sound/isa/wavefront/wavefront_fx.c
@@ -1,26 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 1998-2002 by Paul Davis <pbd@op.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <sound/core.h>
#include <sound/snd_wavefront.h>
@@ -50,7 +38,7 @@ wavefront_fx_idle (snd_wavefront_t *dev)
}
if (x & 0x80) {
- snd_printk ("FX device never idle.\n");
+ dev_err(dev->card->dev, "FX device never idle.\n");
return 0;
}
@@ -76,15 +64,15 @@ wavefront_fx_memset (snd_wavefront_t *dev,
unsigned short *data)
{
if (page < 0 || page > 7) {
- snd_printk ("FX memset: "
- "page must be >= 0 and <= 7\n");
- return -(EINVAL);
+ dev_err(dev->card->dev,
+ "FX memset: page must be >= 0 and <= 7\n");
+ return -EINVAL;
}
if (addr < 0 || addr > 0x7f) {
- snd_printk ("FX memset: "
- "addr must be >= 0 and <= 7f\n");
- return -(EINVAL);
+ dev_err(dev->card->dev,
+ "FX memset: addr must be >= 0 and <= 7f\n");
+ return -EINVAL;
}
if (cnt == 1) {
@@ -95,7 +83,7 @@ wavefront_fx_memset (snd_wavefront_t *dev,
outb ((data[0] >> 8), dev->fx_dsp_msb);
outb ((data[0] & 0xff), dev->fx_dsp_lsb);
- snd_printk ("FX: addr %d:%x set to 0x%x\n",
+ dev_err(dev->card->dev, "FX: addr %d:%x set to 0x%x\n",
page, addr, data[0]);
} else {
@@ -114,10 +102,10 @@ wavefront_fx_memset (snd_wavefront_t *dev,
}
if (i != cnt) {
- snd_printk ("FX memset "
- "(0x%x, 0x%x, 0x%lx, %d) incomplete\n",
- page, addr, (unsigned long) data, cnt);
- return -(EIO);
+ dev_err(dev->card->dev,
+ "FX memset (0x%x, 0x%x, 0x%lx, %d) incomplete\n",
+ page, addr, (unsigned long) data, cnt);
+ return -EIO;
}
}
@@ -135,7 +123,7 @@ snd_wavefront_fx_detect (snd_wavefront_t *dev)
*/
if (inb (dev->fx_status) & 0x80) {
- snd_printk ("Hmm, probably a Maui or Tropez.\n");
+ dev_err(dev->card->dev, "Hmm, probably a Maui or Tropez.\n");
return -1;
}
@@ -192,20 +180,20 @@ snd_wavefront_fx_ioctl (struct snd_hwdep *sdev, struct file *file,
case WFFX_MEMSET:
if (r.data[2] <= 0) {
- snd_printk ("cannot write "
- "<= 0 bytes to FX\n");
+ dev_err(dev->card->dev,
+ "cannot write <= 0 bytes to FX\n");
return -EIO;
} else if (r.data[2] == 1) {
pd = (unsigned short *) &r.data[3];
} else {
if (r.data[2] > 256) {
- snd_printk ("cannot write "
- "> 512 bytes to FX\n");
+ dev_err(dev->card->dev,
+ "cannot write > 512 bytes to FX\n");
return -EIO;
}
- page_data = memdup_user((unsigned char __user *)
- r.data[3],
- r.data[2] * sizeof(short));
+ page_data = memdup_array_user((unsigned char __user *)
+ r.data[3],
+ r.data[2], sizeof(short));
if (IS_ERR(page_data))
return PTR_ERR(page_data);
pd = page_data;
@@ -220,8 +208,8 @@ snd_wavefront_fx_ioctl (struct snd_hwdep *sdev, struct file *file,
break;
default:
- snd_printk ("FX: ioctl %d not yet supported\n",
- r.request);
+ dev_err(dev->card->dev, "FX: ioctl %d not yet supported\n",
+ r.request);
return -ENOTTY;
}
return err;
@@ -239,7 +227,7 @@ snd_wavefront_fx_ioctl (struct snd_hwdep *sdev, struct file *file,
that outputs it.
*/
-int __devinit
+int
snd_wavefront_fx_start (snd_wavefront_t *dev)
{
unsigned int i;
@@ -266,8 +254,8 @@ snd_wavefront_fx_start (snd_wavefront_t *dev)
goto out;
}
} else {
- snd_printk(KERN_ERR "invalid address"
- " in register data\n");
+ dev_err(dev->card->dev,
+ "invalid address in register data\n");
err = -1;
goto out;
}
diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c
index f14a7c0b6998..69d87c4cafae 100644
--- a/sound/isa/wavefront/wavefront_midi.c
+++ b/sound/isa/wavefront/wavefront_midi.c
@@ -1,9 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) by Paul Barton-Davis 1998-1999
- *
- * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this
- * software for more info.
*/
/* The low level driver for the WaveFront ICS2115 MIDI interface(s)
@@ -47,7 +44,7 @@
*
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/wait.h>
@@ -116,7 +113,6 @@ static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card)
{
snd_wavefront_midi_t *midi = &card->wavefront.midi;
snd_wavefront_mpu_id mpu;
- unsigned long flags;
unsigned char midi_byte;
int max = 256, mask = 1;
int timeout;
@@ -145,11 +141,9 @@ static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card)
break;
}
- spin_lock_irqsave (&midi->virtual, flags);
- if ((midi->mode[midi->output_mpu] & MPU401_MODE_OUTPUT) == 0) {
- spin_unlock_irqrestore (&midi->virtual, flags);
+ guard(spinlock_irqsave)(&midi->virtual);
+ if ((midi->mode[midi->output_mpu] & MPU401_MODE_OUTPUT) == 0)
goto __second;
- }
if (output_ready (midi)) {
if (snd_rawmidi_transmit(midi->substream_output[midi->output_mpu], &midi_byte, 1) == 1) {
if (!midi->isvirtual ||
@@ -160,17 +154,14 @@ static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card)
} else {
if (midi->istimer) {
if (--midi->istimer <= 0)
- del_timer(&midi->timer);
+ timer_delete(&midi->timer);
}
midi->mode[midi->output_mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
- spin_unlock_irqrestore (&midi->virtual, flags);
goto __second;
}
} else {
- spin_unlock_irqrestore (&midi->virtual, flags);
return;
}
- spin_unlock_irqrestore (&midi->virtual, flags);
}
__second:
@@ -188,15 +179,13 @@ static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card)
break;
}
- spin_lock_irqsave (&midi->virtual, flags);
+ guard(spinlock_irqsave)(&midi->virtual);
if (!midi->isvirtual)
mask = 0;
mpu = midi->output_mpu ^ mask;
mask = 0; /* don't invert the value from now */
- if ((midi->mode[mpu] & MPU401_MODE_OUTPUT) == 0) {
- spin_unlock_irqrestore (&midi->virtual, flags);
+ if ((midi->mode[mpu] & MPU401_MODE_OUTPUT) == 0)
return;
- }
if (snd_rawmidi_transmit_empty(midi->substream_output[mpu]))
goto __timer;
if (output_ready (midi)) {
@@ -215,23 +204,19 @@ static void snd_wavefront_midi_output_write(snd_wavefront_card_t *card)
__timer:
if (midi->istimer) {
if (--midi->istimer <= 0)
- del_timer(&midi->timer);
+ timer_delete(&midi->timer);
}
midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
- spin_unlock_irqrestore (&midi->virtual, flags);
return;
}
} else {
- spin_unlock_irqrestore (&midi->virtual, flags);
return;
}
- spin_unlock_irqrestore (&midi->virtual, flags);
}
}
static int snd_wavefront_midi_input_open(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
@@ -242,20 +227,19 @@ static int snd_wavefront_midi_input_open(struct snd_rawmidi_substream *substream
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
- if ((midi = get_wavefront_midi (substream)) == NULL)
+ midi = get_wavefront_midi(substream);
+ if (!midi)
return -EIO;
- spin_lock_irqsave (&midi->open, flags);
+ guard(spinlock_irqsave)(&midi->open);
midi->mode[mpu] |= MPU401_MODE_INPUT;
midi->substream_input[mpu] = substream;
- spin_unlock_irqrestore (&midi->open, flags);
return 0;
}
static int snd_wavefront_midi_output_open(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
@@ -266,20 +250,19 @@ static int snd_wavefront_midi_output_open(struct snd_rawmidi_substream *substrea
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
- if ((midi = get_wavefront_midi (substream)) == NULL)
+ midi = get_wavefront_midi(substream);
+ if (!midi)
return -EIO;
- spin_lock_irqsave (&midi->open, flags);
+ guard(spinlock_irqsave)(&midi->open);
midi->mode[mpu] |= MPU401_MODE_OUTPUT;
midi->substream_output[mpu] = substream;
- spin_unlock_irqrestore (&midi->open, flags);
return 0;
}
static int snd_wavefront_midi_input_close(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
@@ -290,19 +273,19 @@ static int snd_wavefront_midi_input_close(struct snd_rawmidi_substream *substrea
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
- if ((midi = get_wavefront_midi (substream)) == NULL)
+ midi = get_wavefront_midi(substream);
+ if (!midi)
return -EIO;
- spin_lock_irqsave (&midi->open, flags);
+ guard(spinlock_irqsave)(&midi->open);
+ midi->substream_input[mpu] = NULL;
midi->mode[mpu] &= ~MPU401_MODE_INPUT;
- spin_unlock_irqrestore (&midi->open, flags);
return 0;
}
static int snd_wavefront_midi_output_close(struct snd_rawmidi_substream *substream)
{
- unsigned long flags;
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
@@ -313,18 +296,18 @@ static int snd_wavefront_midi_output_close(struct snd_rawmidi_substream *substre
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
- if ((midi = get_wavefront_midi (substream)) == NULL)
+ midi = get_wavefront_midi(substream);
+ if (!midi)
return -EIO;
- spin_lock_irqsave (&midi->open, flags);
+ guard(spinlock_irqsave)(&midi->open);
+ midi->substream_output[mpu] = NULL;
midi->mode[mpu] &= ~MPU401_MODE_OUTPUT;
- spin_unlock_irqrestore (&midi->open, flags);
return 0;
}
static void snd_wavefront_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
@@ -336,35 +319,31 @@ static void snd_wavefront_midi_input_trigger(struct snd_rawmidi_substream *subst
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
- if ((midi = get_wavefront_midi (substream)) == NULL) {
+ midi = get_wavefront_midi(substream);
+ if (!midi)
return;
- }
- spin_lock_irqsave (&midi->virtual, flags);
+ guard(spinlock_irqsave)(&midi->virtual);
if (up) {
midi->mode[mpu] |= MPU401_MODE_INPUT_TRIGGER;
} else {
midi->mode[mpu] &= ~MPU401_MODE_INPUT_TRIGGER;
}
- spin_unlock_irqrestore (&midi->virtual, flags);
}
-static void snd_wavefront_midi_output_timer(unsigned long data)
+static void snd_wavefront_midi_output_timer(struct timer_list *t)
{
- snd_wavefront_card_t *card = (snd_wavefront_card_t *)data;
- snd_wavefront_midi_t *midi = &card->wavefront.midi;
- unsigned long flags;
+ snd_wavefront_midi_t *midi = timer_container_of(midi, t, timer);
+ snd_wavefront_card_t *card = midi->timer_card;
- spin_lock_irqsave (&midi->virtual, flags);
- midi->timer.expires = 1 + jiffies;
- add_timer(&midi->timer);
- spin_unlock_irqrestore (&midi->virtual, flags);
+ scoped_guard(spinlock_irqsave, &midi->virtual) {
+ mod_timer(&midi->timer, 1 + jiffies);
+ }
snd_wavefront_midi_output_write(card);
}
static void snd_wavefront_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
snd_wavefront_midi_t *midi;
snd_wavefront_mpu_id mpu;
@@ -376,27 +355,26 @@ static void snd_wavefront_midi_output_trigger(struct snd_rawmidi_substream *subs
mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data);
- if ((midi = get_wavefront_midi (substream)) == NULL) {
+ midi = get_wavefront_midi(substream);
+ if (!midi)
return;
- }
- spin_lock_irqsave (&midi->virtual, flags);
- if (up) {
- if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) {
- if (!midi->istimer) {
- init_timer(&midi->timer);
- midi->timer.function = snd_wavefront_midi_output_timer;
- midi->timer.data = (unsigned long) substream->rmidi->card->private_data;
- midi->timer.expires = 1 + jiffies;
- add_timer(&midi->timer);
+ scoped_guard(spinlock_irqsave, &midi->virtual) {
+ if (up) {
+ if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) {
+ if (!midi->istimer) {
+ timer_setup(&midi->timer,
+ snd_wavefront_midi_output_timer,
+ 0);
+ mod_timer(&midi->timer, 1 + jiffies);
+ }
+ midi->istimer++;
+ midi->mode[mpu] |= MPU401_MODE_OUTPUT_TRIGGER;
}
- midi->istimer++;
- midi->mode[mpu] |= MPU401_MODE_OUTPUT_TRIGGER;
+ } else {
+ midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
}
- } else {
- midi->mode[mpu] &= ~MPU401_MODE_OUTPUT_TRIGGER;
}
- spin_unlock_irqrestore (&midi->virtual, flags);
if (up)
snd_wavefront_midi_output_write((snd_wavefront_card_t *)substream->rmidi->card->private_data);
@@ -406,7 +384,6 @@ void
snd_wavefront_midi_interrupt (snd_wavefront_card_t *card)
{
- unsigned long flags;
snd_wavefront_midi_t *midi;
static struct snd_rawmidi_substream *substream = NULL;
static int mpu = external_mpu;
@@ -420,37 +397,37 @@ snd_wavefront_midi_interrupt (snd_wavefront_card_t *card)
return;
}
- spin_lock_irqsave (&midi->virtual, flags);
- while (--max) {
-
- if (input_avail (midi)) {
- byte = read_data (midi);
-
- if (midi->isvirtual) {
- if (byte == WF_EXTERNAL_SWITCH) {
- substream = midi->substream_input[external_mpu];
- mpu = external_mpu;
- } else if (byte == WF_INTERNAL_SWITCH) {
- substream = midi->substream_output[internal_mpu];
+ scoped_guard(spinlock_irqsave, &midi->virtual) {
+ while (--max) {
+
+ if (input_avail(midi)) {
+ byte = read_data(midi);
+
+ if (midi->isvirtual) {
+ if (byte == WF_EXTERNAL_SWITCH) {
+ substream = midi->substream_input[external_mpu];
+ mpu = external_mpu;
+ } else if (byte == WF_INTERNAL_SWITCH) {
+ substream = midi->substream_output[internal_mpu];
+ mpu = internal_mpu;
+ } /* else just leave it as it is */
+ } else {
+ substream = midi->substream_input[internal_mpu];
mpu = internal_mpu;
- } /* else just leave it as it is */
- } else {
- substream = midi->substream_input[internal_mpu];
- mpu = internal_mpu;
- }
+ }
- if (substream == NULL) {
- continue;
- }
+ if (substream == NULL) {
+ continue;
+ }
- if (midi->mode[mpu] & MPU401_MODE_INPUT_TRIGGER) {
- snd_rawmidi_receive(substream, &byte, 1);
+ if (midi->mode[mpu] & MPU401_MODE_INPUT_TRIGGER) {
+ snd_rawmidi_receive(substream, &byte, 1);
+ }
+ } else {
+ break;
}
- } else {
- break;
}
- }
- spin_unlock_irqrestore (&midi->virtual, flags);
+ }
snd_wavefront_midi_output_write(card);
}
@@ -472,16 +449,13 @@ void
snd_wavefront_midi_disable_virtual (snd_wavefront_card_t *card)
{
- unsigned long flags;
-
- spin_lock_irqsave (&card->wavefront.midi.virtual, flags);
+ guard(spinlock_irqsave)(&card->wavefront.midi.virtual);
// snd_wavefront_midi_input_close (card->ics2115_external_rmidi);
// snd_wavefront_midi_output_close (card->ics2115_external_rmidi);
card->wavefront.midi.isvirtual = 0;
- spin_unlock_irqrestore (&card->wavefront.midi.virtual, flags);
}
-int __devinit
+int
snd_wavefront_midi_start (snd_wavefront_card_t *card)
{
@@ -502,7 +476,8 @@ snd_wavefront_midi_start (snd_wavefront_card_t *card)
for (i = 0; i < 30000 && !output_ready (midi); i++);
if (!output_ready (midi)) {
- snd_printk ("MIDI interface not ready for command\n");
+ dev_err(card->wavefront.card->dev,
+ "MIDI interface not ready for command\n");
return -1;
}
@@ -524,7 +499,8 @@ snd_wavefront_midi_start (snd_wavefront_card_t *card)
}
if (!ok) {
- snd_printk ("cannot set UART mode for MIDI interface");
+ dev_err(card->wavefront.card->dev,
+ "cannot set UART mode for MIDI interface");
dev->interrupts_are_midi = 0;
return -1;
}
@@ -532,12 +508,13 @@ snd_wavefront_midi_start (snd_wavefront_card_t *card)
/* Route external MIDI to WaveFront synth (by default) */
if (snd_wavefront_cmd (dev, WFC_MISYNTH_ON, rbuf, wbuf)) {
- snd_printk ("can't enable MIDI-IN-2-synth routing.\n");
+ dev_warn(card->wavefront.card->dev,
+ "can't enable MIDI-IN-2-synth routing.\n");
/* XXX error ? */
}
/* Turn on Virtual MIDI, but first *always* turn it off,
- since otherwise consectutive reloads of the driver will
+ since otherwise consecutive reloads of the driver will
never cause the hardware to generate the initial "internal" or
"external" source bytes in the MIDI data stream. This
is pretty important, since the internal hardware generally will
@@ -548,27 +525,29 @@ snd_wavefront_midi_start (snd_wavefront_card_t *card)
*/
if (snd_wavefront_cmd (dev, WFC_VMIDI_OFF, rbuf, wbuf)) {
- snd_printk ("virtual MIDI mode not disabled\n");
+ dev_warn(card->wavefront.card->dev,
+ "virtual MIDI mode not disabled\n");
return 0; /* We're OK, but missing the external MIDI dev */
}
snd_wavefront_midi_enable_virtual (card);
if (snd_wavefront_cmd (dev, WFC_VMIDI_ON, rbuf, wbuf)) {
- snd_printk ("cannot enable virtual MIDI mode.\n");
+ dev_warn(card->wavefront.card->dev,
+ "cannot enable virtual MIDI mode.\n");
snd_wavefront_midi_disable_virtual (card);
}
return 0;
}
-struct snd_rawmidi_ops snd_wavefront_midi_output =
+const struct snd_rawmidi_ops snd_wavefront_midi_output =
{
.open = snd_wavefront_midi_output_open,
.close = snd_wavefront_midi_output_close,
.trigger = snd_wavefront_midi_output_trigger,
};
-struct snd_rawmidi_ops snd_wavefront_midi_input =
+const struct snd_rawmidi_ops snd_wavefront_midi_input =
{
.open = snd_wavefront_midi_input_open,
.close = snd_wavefront_midi_input_close,
diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c
index 4fb7b19ff393..0d78533e1cfd 100644
--- a/sound/isa/wavefront/wavefront_synth.c
+++ b/sound/isa/wavefront/wavefront_synth.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (C) by Paul Barton-Davis 1998-1999
*
* Some portions of this file are taken from work that is
* copyright (C) by Hannu Savolainen 1993-1996
- *
- * This program is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
*/
/*
@@ -20,15 +17,17 @@
*
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <linux/wait.h>
+#include <linux/sched/signal.h>
#include <linux/firmware.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/snd_wavefront.h>
#include <sound/initval.h>
@@ -117,7 +116,7 @@ MODULE_PARM_DESC(osrun_time, "how many seconds to wait for the ICS2115 OS");
#define DPRINT(cond, ...) \
if ((dev->debug & (cond)) == (cond)) { \
- snd_printk (__VA_ARGS__); \
+ pr_debug(__VA_ARGS__); \
}
#else
#define DPRINT(cond, args...)
@@ -340,8 +339,9 @@ snd_wavefront_cmd (snd_wavefront_t *dev,
int c;
struct wavefront_command *wfcmd;
- if ((wfcmd = wavefront_get_command (cmd)) == NULL) {
- snd_printk ("command 0x%x not supported.\n",
+ wfcmd = wavefront_get_command(cmd);
+ if (!wfcmd) {
+ dev_err(dev->card->dev, "command 0x%x not supported.\n",
cmd);
return 1;
}
@@ -392,7 +392,8 @@ snd_wavefront_cmd (snd_wavefront_t *dev,
for (i = 0; i < wfcmd->read_cnt; i++) {
- if ((c = wavefront_read (dev)) == -1) {
+ c = wavefront_read(dev);
+ if (c == -1) {
DPRINT (WF_DEBUG_IO, "bad read for byte "
"%d of 0x%x [%s].\n",
i, cmd, wfcmd->action);
@@ -402,7 +403,8 @@ snd_wavefront_cmd (snd_wavefront_t *dev,
/* Now handle errors. Lots of special cases here */
if (c == 0xff) {
- if ((c = wavefront_read (dev)) == -1) {
+ c = wavefront_read(dev);
+ if (c == -1) {
DPRINT (WF_DEBUG_IO, "bad read for "
"error byte at "
"read byte %d "
@@ -460,9 +462,9 @@ snd_wavefront_cmd (snd_wavefront_t *dev,
of the standard value.
*/
- if ((ack = wavefront_read (dev)) == 0) {
+ ack = wavefront_read(dev);
+ if (ack == 0)
ack = WF_ACK;
- }
if (ack != WF_ACK) {
if (ack == -1) {
@@ -476,7 +478,8 @@ snd_wavefront_cmd (snd_wavefront_t *dev,
if (ack == 0xff) { /* explicit error */
- if ((err = wavefront_read (dev)) == -1) {
+ err = wavefront_read(dev);
+ if (err == -1) {
DPRINT (WF_DEBUG_DATA,
"cannot read err "
"for 0x%x [%s].\n",
@@ -537,7 +540,7 @@ munge_int32 (unsigned int src,
/* Note: we leave the upper bits in place */
dst++;
- };
+ }
return dst;
};
@@ -578,8 +581,6 @@ demunge_buf (unsigned char *src, unsigned char *dst, unsigned int src_bytes)
int i;
unsigned char *end = src + src_bytes;
- end = src + src_bytes;
-
/* NOTE: src and dst *CAN* point to the same address */
for (i = 0; src != end; i++) {
@@ -604,9 +605,9 @@ wavefront_delete_sample (snd_wavefront_t *dev, int sample_num)
wbuf[0] = sample_num & 0x7f;
wbuf[1] = sample_num >> 7;
- if ((x = snd_wavefront_cmd (dev, WFC_DELETE_SAMPLE, NULL, wbuf)) == 0) {
+ x = snd_wavefront_cmd(dev, WFC_DELETE_SAMPLE, NULL, wbuf);
+ if (!x)
dev->sample_status[sample_num] = WF_ST_EMPTY;
- }
return x;
}
@@ -622,7 +623,7 @@ wavefront_get_sample_status (snd_wavefront_t *dev, int assume_rom)
/* check sample status */
if (snd_wavefront_cmd (dev, WFC_GET_NSAMPLES, rbuf, wbuf)) {
- snd_printk ("cannot request sample count.\n");
+ dev_err(dev->card->dev, "cannot request sample count.\n");
return -1;
}
@@ -634,8 +635,8 @@ wavefront_get_sample_status (snd_wavefront_t *dev, int assume_rom)
wbuf[1] = i >> 7;
if (snd_wavefront_cmd (dev, WFC_IDENTIFY_SAMPLE_TYPE, rbuf, wbuf)) {
- snd_printk(KERN_WARNING "cannot identify sample "
- "type of slot %d\n", i);
+ dev_warn(dev->card->dev,
+ "cannot identify sample type of slot %d\n", i);
dev->sample_status[i] = WF_ST_EMPTY;
continue;
}
@@ -660,9 +661,9 @@ wavefront_get_sample_status (snd_wavefront_t *dev, int assume_rom)
break;
default:
- snd_printk ("unknown sample type for "
- "slot %d (0x%x)\n",
- i, rbuf[0]);
+ dev_err(dev->card->dev,
+ "unknown sample type for slot %d (0x%x)\n",
+ i, rbuf[0]);
}
if (rbuf[0] != WF_ST_EMPTY) {
@@ -670,9 +671,10 @@ wavefront_get_sample_status (snd_wavefront_t *dev, int assume_rom)
}
}
- snd_printk ("%d samples used (%d real, %d aliases, %d multi), "
- "%d empty\n", dev->samples_used, sc_real, sc_alias, sc_multi,
- WF_MAX_SAMPLE - dev->samples_used);
+ dev_info(dev->card->dev,
+ "%d samples used (%d real, %d aliases, %d multi), %d empty\n",
+ dev->samples_used, sc_real, sc_alias, sc_multi,
+ WF_MAX_SAMPLE - dev->samples_used);
return (0);
@@ -692,8 +694,9 @@ wavefront_get_patch_status (snd_wavefront_t *dev)
patchnum[0] = i & 0x7f;
patchnum[1] = i >> 7;
- if ((x = snd_wavefront_cmd (dev, WFC_UPLOAD_PATCH, patchbuf,
- patchnum)) == 0) {
+ x = snd_wavefront_cmd(dev, WFC_UPLOAD_PATCH, patchbuf,
+ patchnum);
+ if (x == 0) {
dev->patch_status[i] |= WF_SLOT_FILLED;
p = (wavefront_patch *) patchbuf;
@@ -704,8 +707,8 @@ wavefront_get_patch_status (snd_wavefront_t *dev)
} else if (x == 3) { /* Bad patch number */
dev->patch_status[i] = 0;
} else {
- snd_printk ("upload patch "
- "error 0x%x\n", x);
+ dev_err(dev->card->dev,
+ "upload patch error 0x%x\n", x);
dev->patch_status[i] = 0;
return 1;
}
@@ -722,7 +725,8 @@ wavefront_get_patch_status (snd_wavefront_t *dev)
}
}
- snd_printk ("%d patch slots filled, %d in use\n", cnt, cnt2);
+ dev_info(dev->card->dev, "%d patch slots filled, %d in use\n",
+ cnt, cnt2);
return (0);
}
@@ -739,8 +743,9 @@ wavefront_get_program_status (snd_wavefront_t *dev)
for (i = 0; i < WF_MAX_PROGRAM; i++) {
prognum = i;
- if ((x = snd_wavefront_cmd (dev, WFC_UPLOAD_PROGRAM, progbuf,
- &prognum)) == 0) {
+ x = snd_wavefront_cmd(dev, WFC_UPLOAD_PROGRAM, progbuf,
+ &prognum);
+ if (x == 0) {
dev->prog_status[i] |= WF_SLOT_USED;
@@ -757,8 +762,8 @@ wavefront_get_program_status (snd_wavefront_t *dev)
} else if (x == 1) { /* Bad program number */
dev->prog_status[i] = 0;
} else {
- snd_printk ("upload program "
- "error 0x%x\n", x);
+ dev_err(dev->card->dev,
+ "upload program error 0x%x\n", x);
dev->prog_status[i] = 0;
}
}
@@ -769,7 +774,7 @@ wavefront_get_program_status (snd_wavefront_t *dev)
}
}
- snd_printk ("%d programs slots in use\n", cnt);
+ dev_info(dev->card->dev, "%d programs slots in use\n", cnt);
return (0);
}
@@ -784,15 +789,17 @@ wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header)
DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n",
header->number);
+ if (header->number >= ARRAY_SIZE(dev->patch_status))
+ return -EINVAL;
+
dev->patch_status[header->number] |= WF_SLOT_FILLED;
- bptr = buf;
bptr = munge_int32 (header->number, buf, 2);
munge_buf ((unsigned char *)&header->hdr.p, bptr, WF_PATCH_BYTES);
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PATCH, NULL, buf)) {
- snd_printk ("download patch failed\n");
- return -(EIO);
+ dev_err(dev->card->dev, "download patch failed\n");
+ return -EIO;
}
return (0);
@@ -808,6 +815,9 @@ wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header)
DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n",
header->number);
+ if (header->number >= ARRAY_SIZE(dev->prog_status))
+ return -EINVAL;
+
dev->prog_status[header->number] = WF_SLOT_USED;
/* XXX need to zero existing SLOT_USED bit for program_status[i]
@@ -829,8 +839,8 @@ wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header)
munge_buf ((unsigned char *)&header->hdr.pr, &buf[1], WF_PROGRAM_BYTES);
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PROGRAM, NULL, buf)) {
- snd_printk ("download patch failed\n");
- return -(EIO);
+ dev_err(dev->card->dev, "download patch failed\n");
+ return -EIO;
}
return (0);
@@ -843,7 +853,7 @@ wavefront_freemem (snd_wavefront_t *dev)
char rbuf[8];
if (snd_wavefront_cmd (dev, WFC_REPORT_FREE_MEMORY, rbuf, NULL)) {
- snd_printk ("can't get memory stats.\n");
+ dev_err(dev->card->dev, "can't get memory stats.\n");
return -1;
} else {
return demunge_int32 (rbuf, 4);
@@ -890,13 +900,16 @@ wavefront_send_sample (snd_wavefront_t *dev,
if (header->number == WAVEFRONT_FIND_FREE_SAMPLE_SLOT) {
int x;
- if ((x = wavefront_find_free_sample (dev)) < 0) {
+ x = wavefront_find_free_sample(dev);
+ if (x < 0)
return -ENOMEM;
- }
- snd_printk ("unspecified sample => %d\n", x);
+ dev_info(dev->card->dev, "unspecified sample => %d\n", x);
header->number = x;
}
+ if (header->number >= WF_MAX_SAMPLE)
+ return -EINVAL;
+
if (header->size) {
/* XXX it's a debatable point whether or not RDONLY semantics
@@ -924,9 +937,9 @@ wavefront_send_sample (snd_wavefront_t *dev,
if (dev->rom_samples_rdonly) {
if (dev->sample_status[header->number] & WF_SLOT_ROM) {
- snd_printk ("sample slot %d "
- "write protected\n",
- header->number);
+ dev_err(dev->card->dev,
+ "sample slot %d write protected\n",
+ header->number);
return -EACCES;
}
}
@@ -937,10 +950,10 @@ wavefront_send_sample (snd_wavefront_t *dev,
if (header->size) {
dev->freemem = wavefront_freemem (dev);
- if (dev->freemem < (int)header->size) {
- snd_printk ("insufficient memory to "
- "load %d byte sample.\n",
- header->size);
+ if (dev->freemem < 0 || dev->freemem < header->size) {
+ dev_err(dev->card->dev,
+ "insufficient memory to load %u byte sample.\n",
+ header->size);
return -ENOMEM;
}
@@ -949,9 +962,9 @@ wavefront_send_sample (snd_wavefront_t *dev,
skip = WF_GET_CHANNEL(&header->hdr.s);
if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) {
- snd_printk ("channel selection only "
- "possible on 16-bit samples");
- return -(EINVAL);
+ dev_err(dev->card->dev,
+ "channel selection only possible on 16-bit samples");
+ return -EINVAL;
}
switch (skip) {
@@ -1046,9 +1059,9 @@ wavefront_send_sample (snd_wavefront_t *dev,
header->size ?
WFC_DOWNLOAD_SAMPLE : WFC_DOWNLOAD_SAMPLE_HEADER,
NULL, sample_hdr)) {
- snd_printk ("sample %sdownload refused.\n",
- header->size ? "" : "header ");
- return -(EIO);
+ dev_err(dev->card->dev, "sample %sdownload refused.\n",
+ header->size ? "" : "header ");
+ return -EIO;
}
if (header->size == 0) {
@@ -1072,16 +1085,17 @@ wavefront_send_sample (snd_wavefront_t *dev,
}
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_BLOCK, NULL, NULL)) {
- snd_printk ("download block "
- "request refused.\n");
- return -(EIO);
+ dev_err(dev->card->dev,
+ "download block request refused.\n");
+ return -EIO;
}
for (i = 0; i < blocksize; i++) {
if (dataptr < data_end) {
- __get_user (sample_short, dataptr);
+ if (get_user(sample_short, dataptr))
+ return -EFAULT;
dataptr += skip;
if (data_is_unsigned) { /* GUS ? */
@@ -1130,16 +1144,17 @@ wavefront_send_sample (snd_wavefront_t *dev,
nothing to do with DMA at all.
*/
- if ((dma_ack = wavefront_read (dev)) != WF_DMA_ACK) {
+ dma_ack = wavefront_read(dev);
+ if (dma_ack != WF_DMA_ACK) {
if (dma_ack == -1) {
- snd_printk ("upload sample "
- "DMA ack timeout\n");
- return -(EIO);
+ dev_err(dev->card->dev,
+ "upload sample DMA ack timeout\n");
+ return -EIO;
} else {
- snd_printk ("upload sample "
- "DMA ack error 0x%x\n",
- dma_ack);
- return -(EIO);
+ dev_err(dev->card->dev,
+ "upload sample DMA ack error 0x%x\n",
+ dma_ack);
+ return -EIO;
}
}
}
@@ -1164,7 +1179,10 @@ wavefront_send_alias (snd_wavefront_t *dev, wavefront_patch_info *header)
"alias for %d\n",
header->number,
header->hdr.a.OriginalSample);
-
+
+ if (header->number >= WF_MAX_SAMPLE)
+ return -EINVAL;
+
munge_int32 (header->number, &alias_hdr[0], 2);
munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2);
munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset),
@@ -1179,8 +1197,8 @@ wavefront_send_alias (snd_wavefront_t *dev, wavefront_patch_info *header)
munge_int32 (*(&header->hdr.a.FrequencyBias+1), &alias_hdr[23], 2);
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_SAMPLE_ALIAS, NULL, alias_hdr)) {
- snd_printk ("download alias failed.\n");
- return -(EIO);
+ dev_err(dev->card->dev, "download alias failed.\n");
+ return -EIO;
}
dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS);
@@ -1195,7 +1213,10 @@ wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header)
int num_samples;
unsigned char *msample_hdr;
- msample_hdr = kmalloc(sizeof(WF_MSAMPLE_BYTES), GFP_KERNEL);
+ if (header->number >= WF_MAX_SAMPLE)
+ return -EINVAL;
+
+ msample_hdr = kmalloc(WF_MSAMPLE_BYTES, GFP_KERNEL);
if (! msample_hdr)
return -ENOMEM;
@@ -1229,9 +1250,9 @@ wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header)
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_MULTISAMPLE,
(unsigned char *) (long) ((num_samples*2)+3),
msample_hdr)) {
- snd_printk ("download of multisample failed.\n");
+ dev_err(dev->card->dev, "download of multisample failed.\n");
kfree(msample_hdr);
- return -(EIO);
+ return -EIO;
}
dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE);
@@ -1252,8 +1273,8 @@ wavefront_fetch_multisample (snd_wavefront_t *dev,
munge_int32 (header->number, number, 2);
if (snd_wavefront_cmd (dev, WFC_UPLOAD_MULTISAMPLE, log_ns, number)) {
- snd_printk ("upload multisample failed.\n");
- return -(EIO);
+ dev_err(dev->card->dev, "upload multisample failed.\n");
+ return -EIO;
}
DPRINT (WF_DEBUG_DATA, "msample %d has %d samples\n",
@@ -1269,17 +1290,19 @@ wavefront_fetch_multisample (snd_wavefront_t *dev,
char d[2];
int val;
- if ((val = wavefront_read (dev)) == -1) {
- snd_printk ("upload multisample failed "
- "during sample loop.\n");
- return -(EIO);
+ val = wavefront_read(dev);
+ if (val == -1) {
+ dev_err(dev->card->dev,
+ "upload multisample failed during sample loop.\n");
+ return -EIO;
}
d[0] = val;
- if ((val = wavefront_read (dev)) == -1) {
- snd_printk ("upload multisample failed "
- "during sample loop.\n");
- return -(EIO);
+ val = wavefront_read(dev);
+ if (val == -1) {
+ dev_err(dev->card->dev,
+ "upload multisample failed during sample loop.\n");
+ return -EIO;
}
d[1] = val;
@@ -1313,8 +1336,8 @@ wavefront_send_drum (snd_wavefront_t *dev, wavefront_patch_info *header)
}
if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_EDRUM_PROGRAM, NULL, drumbuf)) {
- snd_printk ("download drum failed.\n");
- return -(EIO);
+ dev_err(dev->card->dev, "download drum failed.\n");
+ return -EIO;
}
return (0);
@@ -1331,7 +1354,7 @@ wavefront_find_free_sample (snd_wavefront_t *dev)
return i;
}
}
- snd_printk ("no free sample slots!\n");
+ dev_err(dev->card->dev, "no free sample slots!\n");
return -1;
}
@@ -1347,7 +1370,7 @@ wavefront_find_free_patch (snd_wavefront_t *dev)
return i;
}
}
- snd_printk ("no free patch slots!\n");
+ dev_err(dev->card->dev, "no free patch slots!\n");
return -1;
}
#endif
@@ -1364,7 +1387,7 @@ wavefront_load_patch (snd_wavefront_t *dev, const char __user *addr)
if (copy_from_user (header, addr, sizeof(wavefront_patch_info) -
sizeof(wavefront_any))) {
- snd_printk ("bad address for load patch.\n");
+ dev_err(dev->card->dev, "bad address for load patch.\n");
err = -EFAULT;
goto __error;
}
@@ -1442,8 +1465,8 @@ wavefront_load_patch (snd_wavefront_t *dev, const char __user *addr)
break;
default:
- snd_printk ("unknown patch type %d.\n",
- header->subkey);
+ dev_err(dev->card->dev, "unknown patch type %d.\n",
+ header->subkey);
err = -EINVAL;
break;
}
@@ -1506,13 +1529,13 @@ wavefront_synth_control (snd_wavefront_card_t *acard,
switch (wc->cmd) {
case WFC_DISABLE_INTERRUPTS:
- snd_printk ("interrupts disabled.\n");
+ dev_dbg(dev->card->dev, "interrupts disabled.\n");
outb (0x80|0x20, dev->control_port);
dev->interrupts_are_midi = 1;
return 0;
case WFC_ENABLE_INTERRUPTS:
- snd_printk ("interrupts enabled.\n");
+ dev_dbg(dev->card->dev, "interrupts enabled.\n");
outb (0x80|0x40|0x20, dev->control_port);
dev->interrupts_are_midi = 1;
return 0;
@@ -1529,7 +1552,7 @@ wavefront_synth_control (snd_wavefront_card_t *acard,
case WFC_IDENTIFY_SLOT_TYPE:
i = wc->wbuf[0] | (wc->wbuf[1] << 7);
if (i <0 || i >= WF_MAX_SAMPLE) {
- snd_printk ("invalid slot ID %d\n",
+ dev_err(dev->card->dev, "invalid slot ID %d\n",
i);
wc->status = EINVAL;
return -EINVAL;
@@ -1540,7 +1563,7 @@ wavefront_synth_control (snd_wavefront_card_t *acard,
case WFC_DEBUG_DRIVER:
dev->debug = wc->wbuf[0];
- snd_printk ("debug = 0x%x\n", dev->debug);
+ dev_dbg(dev->card->dev, "debug = 0x%x\n", dev->debug);
return 0;
case WFC_UPLOAD_PATCH:
@@ -1557,8 +1580,8 @@ wavefront_synth_control (snd_wavefront_card_t *acard,
return 0;
case WFC_UPLOAD_SAMPLE_ALIAS:
- snd_printk ("support for sample alias upload "
- "being considered.\n");
+ dev_err(dev->card->dev,
+ "support for sample alias upload being considered.\n");
wc->status = EINVAL;
return -EINVAL;
}
@@ -1599,9 +1622,8 @@ wavefront_synth_control (snd_wavefront_card_t *acard,
break;
case WFC_UPLOAD_SAMPLE_ALIAS:
- snd_printk ("support for "
- "sample aliases still "
- "being considered.\n");
+ dev_err(dev->card->dev,
+ "support for sample aliases still being considered.\n");
break;
case WFC_VMIDI_OFF:
@@ -1719,10 +1741,10 @@ snd_wavefront_internal_interrupt (snd_wavefront_card_t *card)
return;
}
- spin_lock(&dev->irq_lock);
- dev->irq_ok = 1;
- dev->irq_cnt++;
- spin_unlock(&dev->irq_lock);
+ scoped_guard(spinlock, &dev->irq_lock) {
+ dev->irq_ok = 1;
+ dev->irq_cnt++;
+ }
wake_up(&dev->interrupt_sleeper);
}
@@ -1738,8 +1760,8 @@ snd_wavefront_internal_interrupt (snd_wavefront_card_t *card)
7 Unused
*/
-static int __devinit
-snd_wavefront_interrupt_bits (int irq)
+static int
+snd_wavefront_interrupt_bits(snd_wavefront_t *dev, int irq)
{
int bits;
@@ -1759,33 +1781,33 @@ snd_wavefront_interrupt_bits (int irq)
break;
default:
- snd_printk ("invalid IRQ %d\n", irq);
+ dev_err(dev->card->dev, "invalid IRQ %d\n", irq);
bits = -1;
}
return bits;
}
-static void __devinit
+static void
wavefront_should_cause_interrupt (snd_wavefront_t *dev,
int val, int port, unsigned long timeout)
{
- wait_queue_t wait;
+ wait_queue_entry_t wait;
init_waitqueue_entry(&wait, current);
- spin_lock_irq(&dev->irq_lock);
- add_wait_queue(&dev->interrupt_sleeper, &wait);
- dev->irq_ok = 0;
- outb (val,port);
- spin_unlock_irq(&dev->irq_lock);
+ scoped_guard(spinlock_irq, &dev->irq_lock) {
+ add_wait_queue(&dev->interrupt_sleeper, &wait);
+ dev->irq_ok = 0;
+ outb(val, port);
+ }
while (!dev->irq_ok && time_before(jiffies, timeout)) {
schedule_timeout_uninterruptible(1);
barrier();
}
}
-static int __devinit
+static int
wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
{
@@ -1794,7 +1816,7 @@ wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
/* IRQ already checked */
- bits = snd_wavefront_interrupt_bits (dev->irq);
+ bits = snd_wavefront_interrupt_bits(dev, dev->irq);
/* try reset of port */
@@ -1864,7 +1886,7 @@ wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
*/
if (!dev->irq_ok) {
- snd_printk ("intr not received after h/w un-reset.\n");
+ dev_err(dev->card->dev, "intr not received after h/w un-reset.\n");
goto gone_bad;
}
@@ -1888,17 +1910,18 @@ wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
dev->data_port, ramcheck_time*HZ);
if (!dev->irq_ok) {
- snd_printk ("post-RAM-check interrupt not received.\n");
+ dev_err(dev->card->dev, "post-RAM-check interrupt not received.\n");
goto gone_bad;
}
if (!wavefront_wait (dev, STAT_CAN_READ)) {
- snd_printk ("no response to HW version cmd.\n");
+ dev_err(dev->card->dev, "no response to HW version cmd.\n");
goto gone_bad;
}
- if ((hwv[0] = wavefront_read (dev)) == -1) {
- snd_printk ("board not responding correctly.\n");
+ hwv[0] = wavefront_read(dev);
+ if (hwv[0] == -1) {
+ dev_err(dev->card->dev, "board not responding correctly.\n");
goto gone_bad;
}
@@ -1908,12 +1931,13 @@ wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
and tell us about it either way.
*/
- if ((hwv[0] = wavefront_read (dev)) == -1) {
- snd_printk ("on-board RAM test failed "
- "(bad error code).\n");
+ hwv[0] = wavefront_read(dev);
+ if (hwv[0] == -1) {
+ dev_err(dev->card->dev,
+ "on-board RAM test failed (bad error code).\n");
} else {
- snd_printk ("on-board RAM test failed "
- "(error code: 0x%x).\n",
+ dev_err(dev->card->dev,
+ "on-board RAM test failed (error code: 0x%x).\n",
hwv[0]);
}
goto gone_bad;
@@ -1921,13 +1945,14 @@ wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
/* We're OK, just get the next byte of the HW version response */
- if ((hwv[1] = wavefront_read (dev)) == -1) {
- snd_printk ("incorrect h/w response.\n");
+ hwv[1] = wavefront_read(dev);
+ if (hwv[1] == -1) {
+ dev_err(dev->card->dev, "incorrect h/w response.\n");
goto gone_bad;
}
- snd_printk ("hardware version %d.%d\n",
- hwv[0], hwv[1]);
+ dev_info(dev->card->dev, "hardware version %d.%d\n",
+ hwv[0], hwv[1]);
return 0;
@@ -1936,7 +1961,7 @@ wavefront_reset_to_cleanliness (snd_wavefront_t *dev)
return (1);
}
-static int __devinit
+static int
wavefront_download_firmware (snd_wavefront_t *dev, char *path)
{
@@ -1947,7 +1972,7 @@ wavefront_download_firmware (snd_wavefront_t *dev, char *path)
err = request_firmware(&firmware, path, dev->card->dev);
if (err < 0) {
- snd_printk(KERN_ERR "firmware (%s) download failed!!!\n", path);
+ dev_err(dev->card->dev, "firmware (%s) download failed!!!\n", path);
return 1;
}
@@ -1958,16 +1983,16 @@ wavefront_download_firmware (snd_wavefront_t *dev, char *path)
if (section_length == 0)
break;
if (section_length < 0 || section_length > WF_SECTION_MAX) {
- snd_printk(KERN_ERR
- "invalid firmware section length %d\n",
- section_length);
+ dev_err(dev->card->dev,
+ "invalid firmware section length %d\n",
+ section_length);
goto failure;
}
buf++;
len++;
if (firmware->size < len + section_length) {
- snd_printk(KERN_ERR "firmware section read error.\n");
+ dev_err(dev->card->dev, "firmware section read error.\n");
goto failure;
}
@@ -1984,15 +2009,14 @@ wavefront_download_firmware (snd_wavefront_t *dev, char *path)
/* get ACK */
if (!wavefront_wait(dev, STAT_CAN_READ)) {
- snd_printk(KERN_ERR "time out for firmware ACK.\n");
+ dev_err(dev->card->dev, "time out for firmware ACK.\n");
goto failure;
}
err = inb(dev->data_port);
if (err != WF_ACK) {
- snd_printk(KERN_ERR
- "download of section #%d not "
- "acknowledged, ack = 0x%x\n",
- section_cnt_downloaded + 1, err);
+ dev_err(dev->card->dev,
+ "download of section #%d not acknowledged, ack = 0x%x\n",
+ section_cnt_downloaded + 1, err);
goto failure;
}
@@ -2004,19 +2028,19 @@ wavefront_download_firmware (snd_wavefront_t *dev, char *path)
failure:
release_firmware(firmware);
- snd_printk(KERN_ERR "firmware download failed!!!\n");
+ dev_err(dev->card->dev, "firmware download failed!!!\n");
return 1;
}
-static int __devinit
+static int
wavefront_do_reset (snd_wavefront_t *dev)
{
char voices[1];
if (wavefront_reset_to_cleanliness (dev)) {
- snd_printk ("hw reset failed.\n");
+ dev_err(dev->card->dev, "hw reset failed.\n");
goto gone_bad;
}
@@ -2040,7 +2064,7 @@ wavefront_do_reset (snd_wavefront_t *dev)
(osrun_time*HZ));
if (!dev->irq_ok) {
- snd_printk ("no post-OS interrupt.\n");
+ dev_err(dev->card->dev, "no post-OS interrupt.\n");
goto gone_bad;
}
@@ -2050,7 +2074,7 @@ wavefront_do_reset (snd_wavefront_t *dev)
dev->data_port, (10*HZ));
if (!dev->irq_ok) {
- snd_printk ("no post-OS interrupt(2).\n");
+ dev_err(dev->card->dev, "no post-OS interrupt(2).\n");
goto gone_bad;
}
@@ -2066,24 +2090,24 @@ wavefront_do_reset (snd_wavefront_t *dev)
about it.
*/
- if ((dev->freemem = wavefront_freemem (dev)) < 0) {
+ dev->freemem = wavefront_freemem(dev);
+ if (dev->freemem < 0)
goto gone_bad;
- }
- snd_printk ("available DRAM %dk\n", dev->freemem / 1024);
+ dev_info(dev->card->dev, "available DRAM %dk\n", dev->freemem / 1024);
if (wavefront_write (dev, 0xf0) ||
wavefront_write (dev, 1) ||
(wavefront_read (dev) < 0)) {
dev->debug = 0;
- snd_printk ("MPU emulation mode not set.\n");
+ dev_err(dev->card->dev, "MPU emulation mode not set.\n");
goto gone_bad;
}
voices[0] = 32;
if (snd_wavefront_cmd (dev, WFC_SET_NVOICES, NULL, voices)) {
- snd_printk ("cannot set number of voices to 32.\n");
+ dev_err(dev->card->dev, "cannot set number of voices to 32.\n");
goto gone_bad;
}
@@ -2098,7 +2122,7 @@ wavefront_do_reset (snd_wavefront_t *dev)
return 1;
}
-int __devinit
+int
snd_wavefront_start (snd_wavefront_t *dev)
{
@@ -2140,7 +2164,7 @@ snd_wavefront_start (snd_wavefront_t *dev)
return (0);
}
-int __devinit
+int
snd_wavefront_detect (snd_wavefront_card_t *card)
{
@@ -2163,8 +2187,8 @@ snd_wavefront_detect (snd_wavefront_card_t *card)
dev->fw_version[0] = rbuf[0];
dev->fw_version[1] = rbuf[1];
- snd_printk ("firmware %d.%d already loaded.\n",
- rbuf[0], rbuf[1]);
+ dev_info(dev->card->dev, "firmware %d.%d already loaded.\n",
+ rbuf[0], rbuf[1]);
/* check that a command actually works */
@@ -2173,22 +2197,24 @@ snd_wavefront_detect (snd_wavefront_card_t *card)
dev->hw_version[0] = rbuf[0];
dev->hw_version[1] = rbuf[1];
} else {
- snd_printk ("not raw, but no "
- "hardware version!\n");
+ dev_err(dev->card->dev,
+ "not raw, but no hardware version!\n");
return -1;
}
if (!wf_raw) {
return 0;
} else {
- snd_printk ("reloading firmware as you requested.\n");
+ dev_info(dev->card->dev,
+ "reloading firmware as you requested.\n");
dev->israw = 1;
}
} else {
dev->israw = 1;
- snd_printk ("no response to firmware probe, assume raw.\n");
+ dev_info(dev->card->dev,
+ "no response to firmware probe, assume raw.\n");
}
diff --git a/sound/isa/wss/Makefile b/sound/isa/wss/Makefile
index 454fee769a31..f23e71d0d5d4 100644
--- a/sound/isa/wss/Makefile
+++ b/sound/isa/wss/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2008 by Jaroslav Kysela <perex@perex.cz>
#
-snd-wss-lib-objs := wss_lib.o
+snd-wss-lib-y := wss_lib.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_WSS_LIB) += snd-wss-lib.o
diff --git a/sound/isa/wss/wss_lib.c b/sound/isa/wss/wss_lib.c
index 9191b32d9130..6cf88625bbc3 100644
--- a/sound/isa/wss/wss_lib.c
+++ b/sound/isa/wss/wss_lib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of CS4231(A)/CS4232/InterWave & compatible chips
@@ -7,21 +8,6 @@
* Yamaha OPL3-SA3 chip
* - CS4231 (GUS MAX) - still trouble with occasional noises
* - broken initialization?
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
@@ -30,12 +16,13 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/wss.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
-#include <asm/io.h>
#include <asm/dma.h>
#include <asm/irq.h>
@@ -51,7 +38,7 @@ MODULE_LICENSE("GPL");
* Some variables
*/
-static unsigned char freq_bits[14] = {
+static const unsigned char freq_bits[14] = {
/* 5510 */ 0x00 | CS4231_XTAL2,
/* 6620 */ 0x0E | CS4231_XTAL2,
/* 8000 */ 0x00 | CS4231_XTAL1,
@@ -68,12 +55,12 @@ static unsigned char freq_bits[14] = {
/* 48000 */ 0x0C | CS4231_XTAL1
};
-static unsigned int rates[14] = {
+static const unsigned int rates[14] = {
5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050,
27042, 32000, 33075, 37800, 44100, 48000
};
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
@@ -85,7 +72,7 @@ static int snd_wss_xrate(struct snd_pcm_runtime *runtime)
&hw_constraints_rates);
}
-static unsigned char snd_wss_original_image[32] =
+static const unsigned char snd_wss_original_image[32] =
{
0x00, /* 00/00 - lic */
0x00, /* 01/01 - ric */
@@ -121,7 +108,7 @@ static unsigned char snd_wss_original_image[32] =
0x00, /* 1f/31 - cbrl */
};
-static unsigned char snd_opti93x_original_image[32] =
+static const unsigned char snd_opti93x_original_image[32] =
{
0x00, /* 00/00 - l_mixout_outctrl */
0x00, /* 01/01 - r_mixout_outctrl */
@@ -200,15 +187,16 @@ void snd_wss_out(struct snd_wss *chip, unsigned char reg, unsigned char value)
snd_wss_wait(chip);
#ifdef CONFIG_SND_DEBUG
if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- snd_printk(KERN_DEBUG "out: auto calibration time out "
- "- reg = 0x%x, value = 0x%x\n", reg, value);
+ dev_dbg(chip->card->dev,
+ "out: auto calibration time out - reg = 0x%x, value = 0x%x\n",
+ reg, value);
#endif
wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
wss_outb(chip, CS4231P(REG), value);
chip->image[reg] = value;
mb();
- snd_printdd("codec out - reg 0x%x = 0x%x\n",
- chip->mce_bit | reg, value);
+ dev_dbg(chip->card->dev, "codec out - reg 0x%x = 0x%x\n",
+ chip->mce_bit | reg, value);
}
EXPORT_SYMBOL(snd_wss_out);
@@ -217,8 +205,8 @@ unsigned char snd_wss_in(struct snd_wss *chip, unsigned char reg)
snd_wss_wait(chip);
#ifdef CONFIG_SND_DEBUG
if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- snd_printk(KERN_DEBUG "in: auto calibration time out "
- "- reg = 0x%x\n", reg);
+ dev_dbg(chip->card->dev,
+ "in: auto calibration time out - reg = 0x%x\n", reg);
#endif
wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
mb();
@@ -235,7 +223,7 @@ void snd_cs4236_ext_out(struct snd_wss *chip, unsigned char reg,
wss_outb(chip, CS4231P(REG), val);
chip->eimage[CS4236_REG(reg)] = val;
#if 0
- printk(KERN_DEBUG "ext out : reg = 0x%x, val = 0x%x\n", reg, val);
+ dev_dbg(chip->card->dev, "ext out : reg = 0x%x, val = 0x%x\n", reg, val);
#endif
}
EXPORT_SYMBOL(snd_cs4236_ext_out);
@@ -251,8 +239,8 @@ unsigned char snd_cs4236_ext_in(struct snd_wss *chip, unsigned char reg)
{
unsigned char res;
res = wss_inb(chip, CS4231P(REG));
- printk(KERN_DEBUG "ext in : reg = 0x%x, val = 0x%x\n",
- reg, res);
+ dev_dbg(chip->card->dev, "ext in : reg = 0x%x, val = 0x%x\n",
+ reg, res);
return res;
}
#endif
@@ -263,87 +251,87 @@ EXPORT_SYMBOL(snd_cs4236_ext_in);
static void snd_wss_debug(struct snd_wss *chip)
{
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
"CS4231 REGS: INDEX = 0x%02x "
" STATUS = 0x%02x\n",
wss_inb(chip, CS4231P(REGSEL)),
wss_inb(chip, CS4231P(STATUS)));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x00: left input = 0x%02x "
" 0x10: alt 1 (CFIG 2) = 0x%02x\n",
snd_wss_in(chip, 0x00),
snd_wss_in(chip, 0x10));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x01: right input = 0x%02x "
" 0x11: alt 2 (CFIG 3) = 0x%02x\n",
snd_wss_in(chip, 0x01),
snd_wss_in(chip, 0x11));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x02: GF1 left input = 0x%02x "
" 0x12: left line in = 0x%02x\n",
snd_wss_in(chip, 0x02),
snd_wss_in(chip, 0x12));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x03: GF1 right input = 0x%02x "
" 0x13: right line in = 0x%02x\n",
snd_wss_in(chip, 0x03),
snd_wss_in(chip, 0x13));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x04: CD left input = 0x%02x "
" 0x14: timer low = 0x%02x\n",
snd_wss_in(chip, 0x04),
snd_wss_in(chip, 0x14));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x05: CD right input = 0x%02x "
" 0x15: timer high = 0x%02x\n",
snd_wss_in(chip, 0x05),
snd_wss_in(chip, 0x15));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x06: left output = 0x%02x "
" 0x16: left MIC (PnP) = 0x%02x\n",
snd_wss_in(chip, 0x06),
snd_wss_in(chip, 0x16));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x07: right output = 0x%02x "
" 0x17: right MIC (PnP) = 0x%02x\n",
snd_wss_in(chip, 0x07),
snd_wss_in(chip, 0x17));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x08: playback format = 0x%02x "
" 0x18: IRQ status = 0x%02x\n",
snd_wss_in(chip, 0x08),
snd_wss_in(chip, 0x18));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x09: iface (CFIG 1) = 0x%02x "
" 0x19: left line out = 0x%02x\n",
snd_wss_in(chip, 0x09),
snd_wss_in(chip, 0x19));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x0a: pin control = 0x%02x "
" 0x1a: mono control = 0x%02x\n",
snd_wss_in(chip, 0x0a),
snd_wss_in(chip, 0x1a));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x0b: init & status = 0x%02x "
" 0x1b: right line out = 0x%02x\n",
snd_wss_in(chip, 0x0b),
snd_wss_in(chip, 0x1b));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x0c: revision & mode = 0x%02x "
" 0x1c: record format = 0x%02x\n",
snd_wss_in(chip, 0x0c),
snd_wss_in(chip, 0x1c));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x0d: loopback = 0x%02x "
" 0x1d: var freq (PnP) = 0x%02x\n",
snd_wss_in(chip, 0x0d),
snd_wss_in(chip, 0x1d));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x0e: ply upr count = 0x%02x "
" 0x1e: ply lwr count = 0x%02x\n",
snd_wss_in(chip, 0x0e),
snd_wss_in(chip, 0x1e));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
" 0x0f: rec upr count = 0x%02x "
" 0x1f: rec lwr count = 0x%02x\n",
snd_wss_in(chip, 0x0f),
@@ -372,32 +360,29 @@ static void snd_wss_busy_wait(struct snd_wss *chip)
void snd_wss_mce_up(struct snd_wss *chip)
{
- unsigned long flags;
int timeout;
snd_wss_wait(chip);
#ifdef CONFIG_SND_DEBUG
if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- snd_printk(KERN_DEBUG
- "mce_up - auto calibration time out (0)\n");
+ dev_dbg(chip->card->dev,
+ "mce_up - auto calibration time out (0)\n");
#endif
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
chip->mce_bit |= CS4231_MCE;
timeout = wss_inb(chip, CS4231P(REGSEL));
if (timeout == 0x80)
- snd_printk(KERN_DEBUG "mce_up [0x%lx]: "
- "serious init problem - codec still busy\n",
- chip->port);
+ dev_dbg(chip->card->dev,
+ "mce_up [0x%lx]: serious init problem - codec still busy\n",
+ chip->port);
if (!(timeout & CS4231_MCE))
wss_outb(chip, CS4231P(REGSEL),
chip->mce_bit | (timeout & 0x1f));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
EXPORT_SYMBOL(snd_wss_mce_up);
void snd_wss_mce_down(struct snd_wss *chip)
{
- unsigned long flags;
unsigned long end_time;
int timeout;
int hw_mask = WSS_HW_CS4231_MASK | WSS_HW_CS4232_MASK | WSS_HW_AD1848;
@@ -406,30 +391,30 @@ void snd_wss_mce_down(struct snd_wss *chip)
#ifdef CONFIG_SND_DEBUG
if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
- snd_printk(KERN_DEBUG "mce_down [0x%lx] - "
- "auto calibration time out (0)\n",
- (long)CS4231P(REGSEL));
+ dev_dbg(chip->card->dev,
+ "mce_down [0x%lx] - auto calibration time out (0)\n",
+ (long)CS4231P(REGSEL));
#endif
- spin_lock_irqsave(&chip->reg_lock, flags);
- chip->mce_bit &= ~CS4231_MCE;
- timeout = wss_inb(chip, CS4231P(REGSEL));
- wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ chip->mce_bit &= ~CS4231_MCE;
+ timeout = wss_inb(chip, CS4231P(REGSEL));
+ wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
+ }
if (timeout == 0x80)
- snd_printk(KERN_DEBUG "mce_down [0x%lx]: "
- "serious init problem - codec still busy\n",
- chip->port);
+ dev_dbg(chip->card->dev,
+ "mce_down [0x%lx]: serious init problem - codec still busy\n",
+ chip->port);
if ((timeout & CS4231_MCE) == 0 || !(chip->hardware & hw_mask))
return;
/*
* Wait for (possible -- during init auto-calibration may not be set)
- * calibration process to start. Needs upto 5 sample periods on AD1848
+ * calibration process to start. Needs up to 5 sample periods on AD1848
* which at the slowest possible rate of 5.5125 kHz means 907 us.
*/
msleep(1);
- snd_printdd("(1) jiffies = %lu\n", jiffies);
+ dev_dbg(chip->card->dev, "(1) jiffies = %lu\n", jiffies);
/* check condition up to 250 ms */
end_time = jiffies + msecs_to_jiffies(250);
@@ -437,27 +422,29 @@ void snd_wss_mce_down(struct snd_wss *chip)
CS4231_CALIB_IN_PROGRESS) {
if (time_after(jiffies, end_time)) {
- snd_printk(KERN_ERR "mce_down - "
- "auto calibration time out (2)\n");
+ dev_err(chip->card->dev,
+ "mce_down - auto calibration time out (2)\n");
return;
}
msleep(1);
}
- snd_printdd("(2) jiffies = %lu\n", jiffies);
+ dev_dbg(chip->card->dev, "(2) jiffies = %lu\n", jiffies);
/* check condition up to 100 ms */
end_time = jiffies + msecs_to_jiffies(100);
while (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) {
if (time_after(jiffies, end_time)) {
- snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n");
+ dev_err(chip->card->dev,
+ "mce_down - auto calibration time out (3)\n");
return;
}
msleep(1);
}
- snd_printdd("(3) jiffies = %lu\n", jiffies);
- snd_printd("mce_down - exit = 0x%x\n", wss_inb(chip, CS4231P(REGSEL)));
+ dev_dbg(chip->card->dev, "(3) jiffies = %lu\n", jiffies);
+ dev_dbg(chip->card->dev, "mce_down - exit = 0x%x\n",
+ wss_inb(chip, CS4231P(REGSEL)));
}
EXPORT_SYMBOL(snd_wss_mce_down);
@@ -506,7 +493,7 @@ static int snd_wss_trigger(struct snd_pcm_substream *substream,
snd_pcm_trigger_done(s, substream);
}
}
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
if (do_start) {
chip->image[CS4231_IFACE_CTRL] |= what;
if (chip->trigger)
@@ -517,7 +504,6 @@ static int snd_wss_trigger(struct snd_pcm_substream *substream,
chip->trigger(chip, what, 0);
}
snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
- spin_unlock(&chip->reg_lock);
#if 0
snd_wss_debug(chip);
#endif
@@ -540,7 +526,7 @@ static unsigned char snd_wss_get_rate(unsigned int rate)
}
static unsigned char snd_wss_get_format(struct snd_wss *chip,
- int format,
+ snd_pcm_format_t format,
int channels)
{
unsigned char rformat;
@@ -556,21 +542,18 @@ static unsigned char snd_wss_get_format(struct snd_wss *chip,
if (channels > 1)
rformat |= CS4231_STEREO;
#if 0
- snd_printk(KERN_DEBUG "get_format: 0x%x (mode=0x%x)\n", format, mode);
+ dev_dbg(chip->card->dev, "get_format: 0x%x (mode=0x%x)\n", format, mode);
#endif
return rformat;
}
static void snd_wss_calibrate_mute(struct snd_wss *chip, int mute)
{
- unsigned long flags;
mute = mute ? 0x80 : 0;
- spin_lock_irqsave(&chip->reg_lock, flags);
- if (chip->calibrate_mute == mute) {
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
+ if (chip->calibrate_mute == mute)
return;
- }
if (!mute) {
snd_wss_dout(chip, CS4231_LEFT_INPUT,
chip->image[CS4231_LEFT_INPUT]);
@@ -618,20 +601,18 @@ static void snd_wss_calibrate_mute(struct snd_wss *chip, int mute)
mute | chip->image[CS4231_LINE_RIGHT_OUTPUT]);
}
chip->calibrate_mute = mute;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static void snd_wss_playback_format(struct snd_wss *chip,
struct snd_pcm_hw_params *params,
unsigned char pdfr)
{
- unsigned long flags;
int full_calib = 1;
- mutex_lock(&chip->mce_mutex);
+ guard(mutex)(&chip->mce_mutex);
if (chip->hardware == WSS_HW_CS4231A ||
(chip->hardware & WSS_HW_CS4232_MASK)) {
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (pdfr & 0x0f)) { /* rate is same? */
snd_wss_out(chip, CS4231_ALT_FEATURE_1,
chip->image[CS4231_ALT_FEATURE_1] | 0x10);
@@ -643,7 +624,6 @@ static void snd_wss_playback_format(struct snd_wss *chip,
udelay(100); /* Fixes audible clicks at least on GUS MAX */
full_calib = 0;
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
} else if (chip->hardware == WSS_HW_AD1845) {
unsigned rate = params_rate(params);
@@ -656,30 +636,28 @@ static void snd_wss_playback_format(struct snd_wss *chip,
* NOTE: We seem to need to write to the MSB before the LSB
* to get the correct sample frequency.
*/
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_wss_out(chip, CS4231_PLAYBK_FORMAT, (pdfr & 0xf0));
snd_wss_out(chip, AD1845_UPR_FREQ_SEL, (rate >> 8) & 0xff);
snd_wss_out(chip, AD1845_LWR_FREQ_SEL, rate & 0xff);
full_calib = 0;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
if (full_calib) {
snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- if (chip->hardware != WSS_HW_INTERWAVE && !chip->single_dma) {
- if (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE)
- pdfr = (pdfr & 0xf0) |
- (chip->image[CS4231_REC_FORMAT] & 0x0f);
- } else {
- chip->image[CS4231_PLAYBK_FORMAT] = pdfr;
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ if (chip->hardware != WSS_HW_INTERWAVE && !chip->single_dma) {
+ if (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE)
+ pdfr = (pdfr & 0xf0) |
+ (chip->image[CS4231_REC_FORMAT] & 0x0f);
+ } else {
+ chip->image[CS4231_PLAYBK_FORMAT] = pdfr;
+ }
+ snd_wss_out(chip, CS4231_PLAYBK_FORMAT, pdfr);
}
- snd_wss_out(chip, CS4231_PLAYBK_FORMAT, pdfr);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (chip->hardware == WSS_HW_OPL3SA2)
udelay(100); /* this seems to help */
snd_wss_mce_down(chip);
}
- mutex_unlock(&chip->mce_mutex);
}
static void snd_wss_capture_format(struct snd_wss *chip,
@@ -689,10 +667,10 @@ static void snd_wss_capture_format(struct snd_wss *chip,
unsigned long flags;
int full_calib = 1;
- mutex_lock(&chip->mce_mutex);
+ guard(mutex)(&chip->mce_mutex);
if (chip->hardware == WSS_HW_CS4231A ||
(chip->hardware & WSS_HW_CS4232_MASK)) {
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (cdfr & 0x0f) || /* rate is same? */
(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
snd_wss_out(chip, CS4231_ALT_FEATURE_1,
@@ -703,7 +681,6 @@ static void snd_wss_capture_format(struct snd_wss *chip,
chip->image[CS4231_ALT_FEATURE_1] &= ~0x20);
full_calib = 0;
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
} else if (chip->hardware == WSS_HW_AD1845) {
unsigned rate = params_rate(params);
@@ -716,12 +693,11 @@ static void snd_wss_capture_format(struct snd_wss *chip,
* NOTE: We seem to need to write to the MSB before the LSB
* to get the correct sample frequency.
*/
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_wss_out(chip, CS4231_REC_FORMAT, (cdfr & 0xf0));
snd_wss_out(chip, AD1845_UPR_FREQ_SEL, (rate >> 8) & 0xff);
snd_wss_out(chip, AD1845_LWR_FREQ_SEL, rate & 0xff);
full_calib = 0;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
if (full_calib) {
snd_wss_mce_up(chip);
@@ -746,7 +722,6 @@ static void snd_wss_capture_format(struct snd_wss *chip,
spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_wss_mce_down(chip);
}
- mutex_unlock(&chip->mce_mutex);
}
/*
@@ -764,10 +739,10 @@ static unsigned long snd_wss_timer_resolution(struct snd_timer *timer)
static int snd_wss_timer_start(struct snd_timer *timer)
{
- unsigned long flags;
unsigned int ticks;
struct snd_wss *chip = snd_timer_chip(timer);
- spin_lock_irqsave(&chip->reg_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->reg_lock);
ticks = timer->sticks;
if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 ||
(unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] ||
@@ -782,109 +757,100 @@ static int snd_wss_timer_start(struct snd_timer *timer)
chip->image[CS4231_ALT_FEATURE_1] |
CS4231_TIMER_ENABLE);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
static int snd_wss_timer_stop(struct snd_timer *timer)
{
- unsigned long flags;
struct snd_wss *chip = snd_timer_chip(timer);
- spin_lock_irqsave(&chip->reg_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->reg_lock);
chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE;
snd_wss_out(chip, CS4231_ALT_FEATURE_1,
chip->image[CS4231_ALT_FEATURE_1]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
static void snd_wss_init(struct snd_wss *chip)
{
- unsigned long flags;
-
snd_wss_calibrate_mute(chip, 1);
snd_wss_mce_down(chip);
#ifdef SNDRV_DEBUG_MCE
- snd_printk(KERN_DEBUG "init: (1)\n");
+ dev_dbg(chip->card->dev, "init: (1)\n");
#endif
snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE |
- CS4231_PLAYBACK_PIO |
- CS4231_RECORD_ENABLE |
- CS4231_RECORD_PIO |
- CS4231_CALIB_MODE);
- chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB;
- snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE |
+ CS4231_PLAYBACK_PIO |
+ CS4231_RECORD_ENABLE |
+ CS4231_RECORD_PIO |
+ CS4231_CALIB_MODE);
+ chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB;
+ snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
+ }
snd_wss_mce_down(chip);
#ifdef SNDRV_DEBUG_MCE
- snd_printk(KERN_DEBUG "init: (2)\n");
+ dev_dbg(chip->card->dev, "init: (2)\n");
#endif
snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- chip->image[CS4231_IFACE_CTRL] &= ~CS4231_AUTOCALIB;
- snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
- snd_wss_out(chip,
- CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ chip->image[CS4231_IFACE_CTRL] &= ~CS4231_AUTOCALIB;
+ snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
+ snd_wss_out(chip,
+ CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]);
+ }
snd_wss_mce_down(chip);
#ifdef SNDRV_DEBUG_MCE
- snd_printk(KERN_DEBUG "init: (3) - afei = 0x%x\n",
- chip->image[CS4231_ALT_FEATURE_1]);
+ dev_dbg(chip->card->dev, "init: (3) - afei = 0x%x\n",
+ chip->image[CS4231_ALT_FEATURE_1]);
#endif
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_wss_out(chip, CS4231_ALT_FEATURE_2,
- chip->image[CS4231_ALT_FEATURE_2]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_wss_out(chip, CS4231_ALT_FEATURE_2,
+ chip->image[CS4231_ALT_FEATURE_2]);
+ }
snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_wss_out(chip, CS4231_PLAYBK_FORMAT,
- chip->image[CS4231_PLAYBK_FORMAT]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_wss_out(chip, CS4231_PLAYBK_FORMAT,
+ chip->image[CS4231_PLAYBK_FORMAT]);
+ }
snd_wss_mce_down(chip);
#ifdef SNDRV_DEBUG_MCE
- snd_printk(KERN_DEBUG "init: (4)\n");
+ dev_dbg(chip->card->dev, "init: (4)\n");
#endif
snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- if (!(chip->hardware & WSS_HW_AD1848_MASK))
- snd_wss_out(chip, CS4231_REC_FORMAT,
- chip->image[CS4231_REC_FORMAT]);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ if (!(chip->hardware & WSS_HW_AD1848_MASK))
+ snd_wss_out(chip, CS4231_REC_FORMAT,
+ chip->image[CS4231_REC_FORMAT]);
+ }
snd_wss_mce_down(chip);
snd_wss_calibrate_mute(chip, 0);
#ifdef SNDRV_DEBUG_MCE
- snd_printk(KERN_DEBUG "init: (5)\n");
+ dev_dbg(chip->card->dev, "init: (5)\n");
#endif
}
static int snd_wss_open(struct snd_wss *chip, unsigned int mode)
{
- unsigned long flags;
-
- mutex_lock(&chip->open_mutex);
+ guard(mutex)(&chip->open_mutex);
if ((chip->mode & mode) ||
- ((chip->mode & WSS_MODE_OPEN) && chip->single_dma)) {
- mutex_unlock(&chip->open_mutex);
+ ((chip->mode & WSS_MODE_OPEN) && chip->single_dma))
return -EAGAIN;
- }
if (chip->mode & WSS_MODE_OPEN) {
chip->mode |= mode;
- mutex_unlock(&chip->open_mutex);
return 0;
}
/* ok. now enable and ack CODEC IRQ */
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (!(chip->hardware & WSS_HW_AD1848_MASK)) {
snd_wss_out(chip, CS4231_IRQ_STATUS,
CS4231_PLAYBACK_IRQ |
@@ -903,10 +869,8 @@ static int snd_wss_open(struct snd_wss *chip, unsigned int mode)
CS4231_TIMER_IRQ);
snd_wss_out(chip, CS4231_IRQ_STATUS, 0);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
chip->mode = mode;
- mutex_unlock(&chip->open_mutex);
return 0;
}
@@ -914,12 +878,10 @@ static void snd_wss_close(struct snd_wss *chip, unsigned int mode)
{
unsigned long flags;
- mutex_lock(&chip->open_mutex);
+ guard(mutex)(&chip->open_mutex);
chip->mode &= ~mode;
- if (chip->mode & WSS_MODE_OPEN) {
- mutex_unlock(&chip->open_mutex);
+ if (chip->mode & WSS_MODE_OPEN)
return;
- }
/* disable IRQ */
spin_lock_irqsave(&chip->reg_lock, flags);
if (!(chip->hardware & WSS_HW_AD1848_MASK))
@@ -953,7 +915,6 @@ static void snd_wss_close(struct snd_wss *chip, unsigned int mode)
spin_unlock_irqrestore(&chip->reg_lock, flags);
chip->mode = 0;
- mutex_unlock(&chip->open_mutex);
}
/*
@@ -974,7 +935,7 @@ static int snd_wss_timer_close(struct snd_timer *timer)
return 0;
}
-static struct snd_timer_hardware snd_wss_timer_table =
+static const struct snd_timer_hardware snd_wss_timer_table =
{
.flags = SNDRV_TIMER_HW_AUTO,
.resolution = 9945,
@@ -995,10 +956,7 @@ static int snd_wss_playback_hw_params(struct snd_pcm_substream *substream,
{
struct snd_wss *chip = snd_pcm_substream_chip(substream);
unsigned char new_pdfr;
- int err;
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
- return err;
new_pdfr = snd_wss_get_format(chip, params_format(hw_params),
params_channels(hw_params)) |
snd_wss_get_rate(params_rate(hw_params));
@@ -1006,27 +964,20 @@ static int snd_wss_playback_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int snd_wss_playback_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_wss_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_wss *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long flags;
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
chip->p_dma_size = size;
chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO);
snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
count = snd_wss_get_count(chip->image[CS4231_PLAYBK_FORMAT], count) - 1;
snd_wss_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count);
snd_wss_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
#if 0
snd_wss_debug(chip);
#endif
@@ -1038,10 +989,7 @@ static int snd_wss_capture_hw_params(struct snd_pcm_substream *substream,
{
struct snd_wss *chip = snd_pcm_substream_chip(substream);
unsigned char new_cdfr;
- int err;
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
- return err;
new_cdfr = snd_wss_get_format(chip, params_format(hw_params),
params_channels(hw_params)) |
snd_wss_get_rate(params_rate(hw_params));
@@ -1049,20 +997,14 @@ static int snd_wss_capture_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int snd_wss_capture_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_wss_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_wss *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long flags;
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
chip->c_dma_size = size;
chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
@@ -1082,18 +1024,16 @@ static int snd_wss_capture_prepare(struct snd_pcm_substream *substream)
snd_wss_out(chip, CS4231_REC_UPR_CNT,
(unsigned char) (count >> 8));
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
void snd_wss_overrange(struct snd_wss *chip)
{
- unsigned long flags;
unsigned char res;
- spin_lock_irqsave(&chip->reg_lock, flags);
- res = snd_wss_in(chip, CS4231_TEST_INIT);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ res = snd_wss_in(chip, CS4231_TEST_INIT);
+ }
if (res & (0x08 | 0x02)) /* detect overrange only above 0dB; may be user selectable? */
chip->capture_substream->runtime->overrange++;
}
@@ -1139,13 +1079,12 @@ irqreturn_t snd_wss_interrupt(int irq, void *dev_id)
}
}
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
status = ~CS4231_ALL_IRQS | ~status;
if (chip->hardware & WSS_HW_AD1848_MASK)
wss_outb(chip, CS4231P(STATUS), 0);
else
snd_wss_out(chip, CS4231_IRQ_STATUS, status);
- spin_unlock(&chip->reg_lock);
return IRQ_HANDLED;
}
EXPORT_SYMBOL(snd_wss_interrupt);
@@ -1179,10 +1118,8 @@ static snd_pcm_uframes_t snd_wss_capture_pointer(struct snd_pcm_substream *subst
static int snd_ad1848_probe(struct snd_wss *chip)
{
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
- unsigned long flags;
unsigned char r;
unsigned short hardware = 0;
- int err = 0;
int i;
while (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) {
@@ -1190,7 +1127,7 @@ static int snd_ad1848_probe(struct snd_wss *chip)
return -ENODEV;
cond_resched();
}
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
/* set CS423x MODE 1 */
snd_wss_dout(chip, CS4231_MISC_INFO, 0);
@@ -1199,19 +1136,15 @@ static int snd_ad1848_probe(struct snd_wss *chip)
r = snd_wss_in(chip, CS4231_RIGHT_INPUT);
if (r != 0x45) {
/* RMGE always high on AD1847 */
- if ((r & ~CS4231_ENABLE_MIC_GAIN) != 0x45) {
- err = -ENODEV;
- goto out;
- }
+ if ((r & ~CS4231_ENABLE_MIC_GAIN) != 0x45)
+ return -ENODEV;
hardware = WSS_HW_AD1847;
} else {
snd_wss_dout(chip, CS4231_LEFT_INPUT, 0xaa);
r = snd_wss_in(chip, CS4231_LEFT_INPUT);
/* L/RMGE always low on AT2320 */
- if ((r | CS4231_ENABLE_MIC_GAIN) != 0xaa) {
- err = -ENODEV;
- goto out;
- }
+ if ((r | CS4231_ENABLE_MIC_GAIN) != 0xaa)
+ return -ENODEV;
}
/* clear pending IRQ */
@@ -1220,11 +1153,11 @@ static int snd_ad1848_probe(struct snd_wss *chip)
mb();
if ((chip->hardware & WSS_HW_TYPE_MASK) != WSS_HW_DETECT)
- goto out;
+ return 0;
if (hardware) {
chip->hardware = hardware;
- goto out;
+ return 0;
}
r = snd_wss_in(chip, CS4231_MISC_INFO);
@@ -1253,14 +1186,11 @@ static int snd_ad1848_probe(struct snd_wss *chip)
chip->hardware = WSS_HW_AD1848;
out_mode:
snd_wss_dout(chip, CS4231_MISC_INFO, 0);
-out:
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return err;
+ return 0;
}
static int snd_wss_probe(struct snd_wss *chip)
{
- unsigned long flags;
int i, id, rev, regnum;
unsigned char *ptr;
unsigned int hw;
@@ -1276,21 +1206,21 @@ static int snd_wss_probe(struct snd_wss *chip)
if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
msleep(2);
else {
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_wss_out(chip, CS4231_MISC_INFO,
CS4231_MODE2);
id = snd_wss_in(chip, CS4231_MISC_INFO) & 0x0f;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (id == 0x0a)
break; /* this is valid value */
}
}
- snd_printdd("wss: port = 0x%lx, id = 0x%x\n", chip->port, id);
+ dev_dbg(chip->card->dev, "wss: port = 0x%lx, id = 0x%x\n",
+ chip->port, id);
if (id != 0x0a)
return -ENODEV; /* no valid device found */
rev = snd_wss_in(chip, CS4231_VERSION) & 0xe7;
- snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev);
+ dev_dbg(chip->card->dev, "CS4231: VERSION (I25) = 0x%x\n", rev);
if (rev == 0x80) {
unsigned char tmp = snd_wss_in(chip, 23);
snd_wss_out(chip, 23, ~tmp);
@@ -1309,16 +1239,16 @@ static int snd_wss_probe(struct snd_wss *chip)
} else if (rev == 0x03) {
chip->hardware = WSS_HW_CS4236B;
} else {
- snd_printk(KERN_ERR
- "unknown CS chip with version 0x%x\n", rev);
+ dev_err(chip->card->dev,
+ "unknown CS chip with version 0x%x\n", rev);
return -ENODEV; /* unknown CS4231 chip? */
}
}
- spin_lock_irqsave(&chip->reg_lock, flags);
- wss_inb(chip, CS4231P(STATUS)); /* clear any pendings IRQ */
- wss_outb(chip, CS4231P(STATUS), 0);
- mb();
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ wss_inb(chip, CS4231P(STATUS)); /* clear any pendings IRQ */
+ wss_outb(chip, CS4231P(STATUS), 0);
+ mb();
+ }
if (!(chip->hardware & WSS_HW_AD1848_MASK))
chip->image[CS4231_MISC_INFO] = CS4231_MODE2;
@@ -1353,10 +1283,10 @@ static int snd_wss_probe(struct snd_wss *chip)
ptr = (unsigned char *) &chip->image;
regnum = (chip->hardware & WSS_HW_AD1848_MASK) ? 16 : 32;
snd_wss_mce_down(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- for (i = 0; i < regnum; i++) /* ok.. fill all registers */
- snd_wss_out(chip, i, *ptr++);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ for (i = 0; i < regnum; i++) /* ok.. fill all registers */
+ snd_wss_out(chip, i, *ptr++);
+ }
snd_wss_mce_up(chip);
snd_wss_mce_down(chip);
@@ -1369,7 +1299,9 @@ static int snd_wss_probe(struct snd_wss *chip)
snd_cs4236_ext_out(chip, CS4236_VERSION, 0xff);
id = snd_cs4236_ext_in(chip, CS4236_VERSION);
snd_cs4236_ext_out(chip, CS4236_VERSION, rev);
- snd_printdd("CS4231: ext version; rev = 0x%x, id = 0x%x\n", rev, id);
+ dev_dbg(chip->card->dev,
+ "CS4231: ext version; rev = 0x%x, id = 0x%x\n",
+ rev, id);
if ((id & 0x1f) == 0x1d) { /* CS4235 */
chip->hardware = WSS_HW_CS4235;
switch (id >> 5) {
@@ -1378,10 +1310,9 @@ static int snd_wss_probe(struct snd_wss *chip)
case 6:
break;
default:
- snd_printk(KERN_WARNING
- "unknown CS4235 chip "
- "(enhanced version = 0x%x)\n",
- id);
+ dev_warn(chip->card->dev,
+ "unknown CS4235 chip (enhanced version = 0x%x)\n",
+ id);
}
} else if ((id & 0x1f) == 0x0b) { /* CS4236/B */
switch (id >> 5) {
@@ -1392,10 +1323,9 @@ static int snd_wss_probe(struct snd_wss *chip)
chip->hardware = WSS_HW_CS4236B;
break;
default:
- snd_printk(KERN_WARNING
- "unknown CS4236 chip "
- "(enhanced version = 0x%x)\n",
- id);
+ dev_warn(chip->card->dev,
+ "unknown CS4236 chip (enhanced version = 0x%x)\n",
+ id);
}
} else if ((id & 0x1f) == 0x08) { /* CS4237B */
chip->hardware = WSS_HW_CS4237B;
@@ -1406,10 +1336,9 @@ static int snd_wss_probe(struct snd_wss *chip)
case 7:
break;
default:
- snd_printk(KERN_WARNING
- "unknown CS4237B chip "
- "(enhanced version = 0x%x)\n",
- id);
+ dev_warn(chip->card->dev,
+ "unknown CS4237B chip (enhanced version = 0x%x)\n",
+ id);
}
} else if ((id & 0x1f) == 0x09) { /* CS4238B */
chip->hardware = WSS_HW_CS4238B;
@@ -1419,10 +1348,9 @@ static int snd_wss_probe(struct snd_wss *chip)
case 7:
break;
default:
- snd_printk(KERN_WARNING
- "unknown CS4238B chip "
- "(enhanced version = 0x%x)\n",
- id);
+ dev_warn(chip->card->dev,
+ "unknown CS4238B chip (enhanced version = 0x%x)\n",
+ id);
}
} else if ((id & 0x1f) == 0x1e) { /* CS4239 */
chip->hardware = WSS_HW_CS4239;
@@ -1432,15 +1360,14 @@ static int snd_wss_probe(struct snd_wss *chip)
case 6:
break;
default:
- snd_printk(KERN_WARNING
- "unknown CS4239 chip "
- "(enhanced version = 0x%x)\n",
- id);
+ dev_warn(chip->card->dev,
+ "unknown CS4239 chip (enhanced version = 0x%x)\n",
+ id);
}
} else {
- snd_printk(KERN_WARNING
- "unknown CS4236/CS423xB chip "
- "(enhanced version = 0x%x)\n", id);
+ dev_warn(chip->card->dev,
+ "unknown CS4236/CS423xB chip (enhanced version = 0x%x)\n",
+ id);
}
}
}
@@ -1451,11 +1378,10 @@ static int snd_wss_probe(struct snd_wss *chip)
*/
-static struct snd_pcm_hardware snd_wss_playback =
+static const struct snd_pcm_hardware snd_wss_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_SYNC_START),
.formats = (SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
@@ -1472,7 +1398,7 @@ static struct snd_pcm_hardware snd_wss_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_wss_capture =
+static const struct snd_pcm_hardware snd_wss_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -1523,7 +1449,8 @@ static int snd_wss_playback_open(struct snd_pcm_substream *substream)
snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max);
if (chip->claim_dma) {
- if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma1)) < 0)
+ err = chip->claim_dma(chip, chip->dma_private_data, chip->dma1);
+ if (err < 0)
return err;
}
@@ -1531,7 +1458,6 @@ static int snd_wss_playback_open(struct snd_pcm_substream *substream)
if (err < 0) {
if (chip->release_dma)
chip->release_dma(chip, chip->dma_private_data, chip->dma1);
- snd_free_pages(runtime->dma_area, runtime->dma_bytes);
return err;
}
chip->playback_substream = substream;
@@ -1564,7 +1490,8 @@ static int snd_wss_capture_open(struct snd_pcm_substream *substream)
snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max);
if (chip->claim_dma) {
- if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma2)) < 0)
+ err = chip->claim_dma(chip, chip->dma_private_data, chip->dma2);
+ if (err < 0)
return err;
}
@@ -1572,7 +1499,6 @@ static int snd_wss_capture_open(struct snd_pcm_substream *substream)
if (err < 0) {
if (chip->release_dma)
chip->release_dma(chip, chip->dma_private_data, chip->dma2);
- snd_free_pages(runtime->dma_area, runtime->dma_bytes);
return err;
}
chip->capture_substream = substream;
@@ -1625,13 +1551,11 @@ static void snd_wss_thinkpad_twiddle(struct snd_wss *chip, int on)
static void snd_wss_suspend(struct snd_wss *chip)
{
int reg;
- unsigned long flags;
- snd_pcm_suspend_all(chip->pcm);
- spin_lock_irqsave(&chip->reg_lock, flags);
- for (reg = 0; reg < 32; reg++)
- chip->image[reg] = snd_wss_in(chip, reg);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ for (reg = 0; reg < 32; reg++)
+ chip->image[reg] = snd_wss_in(chip, reg);
+ }
if (chip->thinkpad_flag)
snd_wss_thinkpad_twiddle(chip, 0);
}
@@ -1640,23 +1564,26 @@ static void snd_wss_suspend(struct snd_wss *chip)
static void snd_wss_resume(struct snd_wss *chip)
{
int reg;
- unsigned long flags;
/* int timeout; */
if (chip->thinkpad_flag)
snd_wss_thinkpad_twiddle(chip, 1);
snd_wss_mce_up(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- for (reg = 0; reg < 32; reg++) {
- switch (reg) {
- case CS4231_VERSION:
- break;
- default:
- snd_wss_out(chip, reg, chip->image[reg]);
- break;
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ for (reg = 0; reg < 32; reg++) {
+ switch (reg) {
+ case CS4231_VERSION:
+ break;
+ default:
+ snd_wss_out(chip, reg, chip->image[reg]);
+ break;
+ }
}
+ /* Yamaha needs this to resume properly */
+ if (chip->hardware == WSS_HW_OPL3SA2)
+ snd_wss_out(chip, CS4231_PLAYBK_FORMAT,
+ chip->image[CS4231_PLAYBK_FORMAT]);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
#if 1
snd_wss_mce_down(chip);
#else
@@ -1665,14 +1592,15 @@ static void snd_wss_resume(struct snd_wss *chip)
include rescheduling. -- iwai
*/
snd_wss_busy_wait(chip);
- spin_lock_irqsave(&chip->reg_lock, flags);
- chip->mce_bit &= ~CS4231_MCE;
- timeout = wss_inb(chip, CS4231P(REGSEL));
- wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ chip->mce_bit &= ~CS4231_MCE;
+ timeout = wss_inb(chip, CS4231P(REGSEL));
+ wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
+ }
if (timeout == 0x80)
- snd_printk(KERN_ERR "down [0x%lx]: serious init problem "
- "- codec still busy\n", chip->port);
+ dev_err(chip->card->dev
+ "down [0x%lx]: serious init problem - codec still busy\n",
+ chip->port);
if ((timeout & CS4231_MCE) == 0 ||
!(chip->hardware & (WSS_HW_CS4231_MASK | WSS_HW_CS4232_MASK))) {
return;
@@ -1682,36 +1610,6 @@ static void snd_wss_resume(struct snd_wss *chip)
}
#endif /* CONFIG_PM */
-static int snd_wss_free(struct snd_wss *chip)
-{
- release_and_free_resource(chip->res_port);
- release_and_free_resource(chip->res_cport);
- if (chip->irq >= 0) {
- disable_irq(chip->irq);
- if (!(chip->hwshare & WSS_HWSHARE_IRQ))
- free_irq(chip->irq, (void *) chip);
- }
- if (!(chip->hwshare & WSS_HWSHARE_DMA1) && chip->dma1 >= 0) {
- snd_dma_disable(chip->dma1);
- free_dma(chip->dma1);
- }
- if (!(chip->hwshare & WSS_HWSHARE_DMA2) &&
- chip->dma2 >= 0 && chip->dma2 != chip->dma1) {
- snd_dma_disable(chip->dma2);
- free_dma(chip->dma2);
- }
- if (chip->timer)
- snd_device_free(chip->card, chip->timer);
- kfree(chip);
- return 0;
-}
-
-static int snd_wss_dev_free(struct snd_device *device)
-{
- struct snd_wss *chip = device->device_data;
- return snd_wss_free(chip);
-}
-
const char *snd_wss_chip_id(struct snd_wss *chip)
{
switch (chip->hardware) {
@@ -1765,7 +1663,7 @@ static int snd_wss_new(struct snd_card *card,
struct snd_wss *chip;
*rchip = NULL;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ chip = devm_kzalloc(card->dev, sizeof(*chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
chip->hardware = hardware;
@@ -1801,9 +1699,6 @@ int snd_wss_create(struct snd_card *card,
unsigned short hwshare,
struct snd_wss **rchip)
{
- static struct snd_device_ops ops = {
- .dev_free = snd_wss_dev_free,
- };
struct snd_wss *chip;
int err;
@@ -1815,41 +1710,39 @@ int snd_wss_create(struct snd_card *card,
chip->dma1 = -1;
chip->dma2 = -1;
- chip->res_port = request_region(port, 4, "WSS");
+ chip->res_port = devm_request_region(card->dev, port, 4, "WSS");
if (!chip->res_port) {
- snd_printk(KERN_ERR "wss: can't grab port 0x%lx\n", port);
- snd_wss_free(chip);
+ dev_err(chip->card->dev, "wss: can't grab port 0x%lx\n", port);
return -EBUSY;
}
chip->port = port;
if ((long)cport >= 0) {
- chip->res_cport = request_region(cport, 8, "CS4232 Control");
+ chip->res_cport = devm_request_region(card->dev, cport, 8,
+ "CS4232 Control");
if (!chip->res_cport) {
- snd_printk(KERN_ERR
+ dev_err(chip->card->dev,
"wss: can't grab control port 0x%lx\n", cport);
- snd_wss_free(chip);
return -ENODEV;
}
}
chip->cport = cport;
if (!(hwshare & WSS_HWSHARE_IRQ))
- if (request_irq(irq, snd_wss_interrupt, IRQF_DISABLED,
- "WSS", (void *) chip)) {
- snd_printk(KERN_ERR "wss: can't grab IRQ %d\n", irq);
- snd_wss_free(chip);
+ if (devm_request_irq(card->dev, irq, snd_wss_interrupt, 0,
+ "WSS", (void *) chip)) {
+ dev_err(chip->card->dev, "wss: can't grab IRQ %d\n", irq);
return -EBUSY;
}
chip->irq = irq;
- if (!(hwshare & WSS_HWSHARE_DMA1) && request_dma(dma1, "WSS - 1")) {
- snd_printk(KERN_ERR "wss: can't grab DMA1 %d\n", dma1);
- snd_wss_free(chip);
+ card->sync_irq = chip->irq;
+ if (!(hwshare & WSS_HWSHARE_DMA1) &&
+ snd_devm_request_dma(card->dev, dma1, "WSS - 1")) {
+ dev_err(chip->card->dev, "wss: can't grab DMA1 %d\n", dma1);
return -EBUSY;
}
chip->dma1 = dma1;
- if (!(hwshare & WSS_HWSHARE_DMA2) && dma1 != dma2 &&
- dma2 >= 0 && request_dma(dma2, "WSS - 2")) {
- snd_printk(KERN_ERR "wss: can't grab DMA2 %d\n", dma2);
- snd_wss_free(chip);
+ if (!(hwshare & WSS_HWSHARE_DMA2) && dma1 != dma2 && dma2 >= 0 &&
+ snd_devm_request_dma(card->dev, dma2, "WSS - 2")) {
+ dev_err(chip->card->dev, "wss: can't grab DMA2 %d\n", dma2);
return -EBUSY;
}
if (dma1 == dma2 || dma2 < 0) {
@@ -1865,27 +1758,18 @@ int snd_wss_create(struct snd_card *card,
}
/* global setup */
- if (snd_wss_probe(chip) < 0) {
- snd_wss_free(chip);
+ if (snd_wss_probe(chip) < 0)
return -ENODEV;
- }
snd_wss_init(chip);
#if 0
if (chip->hardware & WSS_HW_CS4232_MASK) {
if (chip->res_cport == NULL)
- snd_printk(KERN_ERR "CS4232 control port features are "
- "not accessible\n");
+ dev_err(chip->card->dev,
+ "CS4232 control port features are not accessible\n");
}
#endif
- /* Register device */
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0) {
- snd_wss_free(chip);
- return err;
- }
-
#ifdef CONFIG_PM
/* Power Management */
chip->suspend = snd_wss_suspend;
@@ -1897,29 +1781,25 @@ int snd_wss_create(struct snd_card *card,
}
EXPORT_SYMBOL(snd_wss_create);
-static struct snd_pcm_ops snd_wss_playback_ops = {
+static const struct snd_pcm_ops snd_wss_playback_ops = {
.open = snd_wss_playback_open,
.close = snd_wss_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_wss_playback_hw_params,
- .hw_free = snd_wss_playback_hw_free,
.prepare = snd_wss_playback_prepare,
.trigger = snd_wss_trigger,
.pointer = snd_wss_playback_pointer,
};
-static struct snd_pcm_ops snd_wss_capture_ops = {
+static const struct snd_pcm_ops snd_wss_capture_ops = {
.open = snd_wss_capture_open,
.close = snd_wss_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_wss_capture_hw_params,
- .hw_free = snd_wss_capture_hw_free,
.prepare = snd_wss_capture_prepare,
.trigger = snd_wss_trigger,
.pointer = snd_wss_capture_pointer,
};
-int snd_wss_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm)
+int snd_wss_pcm(struct snd_wss *chip, int device)
{
struct snd_pcm *pcm;
int err;
@@ -1938,15 +1818,12 @@ int snd_wss_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm)
pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
if (chip->hardware != WSS_HW_INTERWAVE)
pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX;
- strcpy(pcm->name, snd_wss_chip_id(chip));
+ strscpy(pcm->name, snd_wss_chip_id(chip));
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_isa_data(),
- 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, chip->card->dev,
+ 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
chip->pcm = pcm;
- if (rpcm)
- *rpcm = pcm;
return 0;
}
EXPORT_SYMBOL(snd_wss_pcm);
@@ -1957,7 +1834,7 @@ static void snd_wss_timer_free(struct snd_timer *timer)
chip->timer = NULL;
}
-int snd_wss_timer(struct snd_wss *chip, int device, struct snd_timer **rtimer)
+int snd_wss_timer(struct snd_wss *chip, int device)
{
struct snd_timer *timer;
struct snd_timer_id tid;
@@ -1969,15 +1846,14 @@ int snd_wss_timer(struct snd_wss *chip, int device, struct snd_timer **rtimer)
tid.card = chip->card->number;
tid.device = device;
tid.subdevice = 0;
- if ((err = snd_timer_new(chip->card, "CS4231", &tid, &timer)) < 0)
+ err = snd_timer_new(chip->card, "CS4231", &tid, &timer);
+ if (err < 0)
return err;
- strcpy(timer->name, snd_wss_chip_id(chip));
+ strscpy(timer->name, snd_wss_chip_id(chip));
timer->private_data = chip;
timer->private_free = snd_wss_timer_free;
timer->hw = snd_wss_timer_table;
chip->timer = timer;
- if (rtimer)
- *rtimer = timer;
return 0;
}
EXPORT_SYMBOL(snd_wss_timer);
@@ -1989,25 +1865,20 @@ EXPORT_SYMBOL(snd_wss_timer);
static int snd_wss_info_mux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = {
+ static const char * const texts[4] = {
"Line", "Aux", "Mic", "Mix"
};
- static char *opl3sa_texts[4] = {
+ static const char * const opl3sa_texts[4] = {
"Line", "CD", "Mic", "Mix"
};
- static char *gusmax_texts[4] = {
+ static const char * const gusmax_texts[4] = {
"Line", "Synth", "Mic", "Mix"
};
- char **ptexts = texts;
+ const char * const *ptexts = texts;
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
if (snd_BUG_ON(!chip->card))
return -EINVAL;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 2;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3)
- uinfo->value.enumerated.item = 3;
if (!strcmp(chip->card->driver, "GUS MAX"))
ptexts = gusmax_texts;
switch (chip->hardware) {
@@ -2019,20 +1890,17 @@ static int snd_wss_info_mux(struct snd_kcontrol *kcontrol,
ptexts = opl3sa_texts;
break;
}
- strcpy(uinfo->value.enumerated.name, ptexts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 2, 4, ptexts);
}
static int snd_wss_get_mux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.enumerated.item[0] = (chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6;
ucontrol->value.enumerated.item[1] = (chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -2040,7 +1908,6 @@ static int snd_wss_put_mux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
unsigned short left, right;
int change;
@@ -2049,14 +1916,13 @@ static int snd_wss_put_mux(struct snd_kcontrol *kcontrol,
return -EINVAL;
left = ucontrol->value.enumerated.item[0] << 6;
right = ucontrol->value.enumerated.item[1] << 6;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left;
right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right;
change = left != chip->image[CS4231_LEFT_INPUT] ||
right != chip->image[CS4231_RIGHT_INPUT];
snd_wss_out(chip, CS4231_LEFT_INPUT, left);
snd_wss_out(chip, CS4231_RIGHT_INPUT, right);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
@@ -2077,15 +1943,13 @@ int snd_wss_get_single(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert)
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
return 0;
@@ -2096,7 +1960,6 @@ int snd_wss_put_single(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int reg = kcontrol->private_value & 0xff;
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
@@ -2108,11 +1971,10 @@ int snd_wss_put_single(struct snd_kcontrol *kcontrol,
if (invert)
val = mask - val;
val <<= shift;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
val = (chip->image[reg] & ~(mask << shift)) | val;
change = val != chip->image[reg];
snd_wss_out(chip, reg, val);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
EXPORT_SYMBOL(snd_wss_put_single);
@@ -2134,7 +1996,6 @@ int snd_wss_get_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -2142,10 +2003,9 @@ int snd_wss_get_double(struct snd_kcontrol *kcontrol,
int mask = (kcontrol->private_value >> 24) & 0xff;
int invert = (kcontrol->private_value >> 22) & 1;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (invert) {
ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
@@ -2158,7 +2018,6 @@ int snd_wss_put_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
- unsigned long flags;
int left_reg = kcontrol->private_value & 0xff;
int right_reg = (kcontrol->private_value >> 8) & 0xff;
int shift_left = (kcontrol->private_value >> 16) & 0x07;
@@ -2176,7 +2035,7 @@ int snd_wss_put_double(struct snd_kcontrol *kcontrol,
}
val1 <<= shift_left;
val2 <<= shift_right;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (left_reg != right_reg) {
val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
@@ -2190,7 +2049,6 @@ int snd_wss_put_double(struct snd_kcontrol *kcontrol,
change = val1 != chip->image[left_reg];
snd_wss_out(chip, left_reg, val1);
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return change;
}
EXPORT_SYMBOL(snd_wss_put_double);
@@ -2200,7 +2058,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
-static struct snd_kcontrol_new snd_wss_controls[] = {
+static const struct snd_kcontrol_new snd_wss_controls[] = {
WSS_DOUBLE("PCM Playback Switch", 0,
CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
WSS_DOUBLE_TLV("PCM Playback Volume", 0,
@@ -2259,7 +2117,7 @@ int snd_wss_mixer(struct snd_wss *chip)
card = chip->card;
- strcpy(card->mixername, chip->pcm->name);
+ strscpy(card->mixername, chip->pcm->name);
/* Use only the first 11 entries on AD1848 */
if (chip->hardware & WSS_HW_AD1848_MASK)
@@ -2285,19 +2143,3 @@ const struct snd_pcm_ops *snd_wss_get_pcm_ops(int direction)
&snd_wss_playback_ops : &snd_wss_capture_ops;
}
EXPORT_SYMBOL(snd_wss_get_pcm_ops);
-
-/*
- * INIT part
- */
-
-static int __init alsa_wss_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_wss_exit(void)
-{
-}
-
-module_init(alsa_wss_init);
-module_exit(alsa_wss_exit);
diff --git a/sound/last.c b/sound/last.c
index bdd0857b8871..f0bb98780e70 100644
--- a/sound/last.c
+++ b/sound/last.c
@@ -1,41 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Advanced Linux Sound Architecture
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#define SNDRV_MAIN_OBJECT_FILE
#include <linux/init.h>
#include <sound/core.h>
static int __init alsa_sound_last_init(void)
{
+ struct snd_card *card;
int idx, ok = 0;
printk(KERN_INFO "ALSA device list:\n");
- for (idx = 0; idx < SNDRV_CARDS; idx++)
- if (snd_cards[idx] != NULL) {
- printk(KERN_INFO " #%i: %s\n", idx, snd_cards[idx]->longname);
+ for (idx = 0; idx < SNDRV_CARDS; idx++) {
+ card = snd_card_ref(idx);
+ if (card) {
+ printk(KERN_INFO " #%i: %s\n", idx, card->longname);
+ snd_card_unref(card);
ok++;
}
+ }
if (ok == 0)
printk(KERN_INFO " No soundcards found.\n");
return 0;
}
-__initcall(alsa_sound_last_init);
+late_initcall_sync(alsa_sound_last_init);
diff --git a/sound/mips/Kconfig b/sound/mips/Kconfig
index a9823fad85c2..c484b1e42395 100644
--- a/sound/mips/Kconfig
+++ b/sound/mips/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# ALSA MIPS drivers
menuconfig SND_MIPS
@@ -12,23 +13,23 @@ if SND_MIPS
config SND_SGI_O2
tristate "SGI O2 Audio"
depends on SGI_IP32
- help
- Sound support for the SGI O2 Workstation.
+ select SND_PCM
+ help
+ Sound support for the SGI O2 Workstation.
config SND_SGI_HAL2
- tristate "SGI HAL2 Audio"
- depends on SGI_HAS_HAL2
- help
- Sound support for the SGI Indy and Indigo2 Workstation.
-
+ tristate "SGI HAL2 Audio"
+ depends on SGI_HAS_HAL2
+ select SND_PCM
+ help
+ Sound support for the SGI Indy and Indigo2 Workstation.
-config SND_AU1X00
- tristate "Au1x00 AC97 Port Driver"
- depends on SOC_AU1000 || SOC_AU1100 || SOC_AU1500
+config SND_N64
+ bool "N64 Audio"
+ depends on MACH_NINTENDO64 && SND=y
select SND_PCM
- select SND_AC97_CODEC
help
- ALSA Sound driver for the Au1x00's AC97 port.
+ Sound support for the N64.
endif # SND_MIPS
diff --git a/sound/mips/Makefile b/sound/mips/Makefile
index 861ec0a574b4..bfbf3bda487b 100644
--- a/sound/mips/Makefile
+++ b/sound/mips/Makefile
@@ -1,12 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
#
-snd-au1x00-objs := au1x00.o
-snd-sgi-o2-objs := sgio2audio.o ad1843.o
-snd-sgi-hal2-objs := hal2.o
+snd-sgi-o2-y := sgio2audio.o ad1843.o
+snd-sgi-hal2-y := hal2.o
# Toplevel Module Dependency
-obj-$(CONFIG_SND_AU1X00) += snd-au1x00.o
obj-$(CONFIG_SND_SGI_O2) += snd-sgi-o2.o
obj-$(CONFIG_SND_SGI_HAL2) += snd-sgi-hal2.o
+obj-$(CONFIG_SND_N64) += snd-n64.o
diff --git a/sound/mips/ad1843.c b/sound/mips/ad1843.c
index c624510ec374..19c28938f00f 100644
--- a/sound/mips/ad1843.c
+++ b/sound/mips/ad1843.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AD1843 low level driver
*
@@ -6,21 +7,6 @@
*
* inspired from vwsnd.c (SGI VW audio driver)
* Copyright 1999 Silicon Graphics, Inc. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -276,7 +262,7 @@ static void ad1843_write_multi(struct snd_ad1843 *ad1843, int argcount, ...)
if (reg == -1)
reg = fp->reg;
else
- BUG_ON(reg != fp->reg);
+ WARN_ON(reg != fp->reg);
m = ((1 << fp->nbits) - 1) << fp->lo_bit;
mask |= m;
bits |= (value << fp->lo_bit) & m;
diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c
deleted file mode 100644
index 446cf9748664..000000000000
--- a/sound/mips/au1x00.c
+++ /dev/null
@@ -1,695 +0,0 @@
-/*
- * BRIEF MODULE DESCRIPTION
- * Driver for AMD Au1000 MIPS Processor, AC'97 Sound Port
- *
- * Copyright 2004 Cooper Street Innovations Inc.
- * Author: Charles Eidsness <charles@cooper-street.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
- * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * History:
- *
- * 2004-09-09 Charles Eidsness -- Original verion -- based on
- * sa11xx-uda1341.c ALSA driver and the
- * au1000.c OSS driver.
- * 2004-09-09 Matt Porter -- Added support for ALSA 1.0.6
- *
- */
-
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/initval.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/ac97_codec.h>
-#include <asm/mach-au1x00/au1000.h>
-#include <asm/mach-au1x00/au1000_dma.h>
-
-MODULE_AUTHOR("Charles Eidsness <charles@cooper-street.com>");
-MODULE_DESCRIPTION("Au1000 AC'97 ALSA Driver");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{AMD,Au1000 AC'97}}");
-
-#define PLAYBACK 0
-#define CAPTURE 1
-#define AC97_SLOT_3 0x01
-#define AC97_SLOT_4 0x02
-#define AC97_SLOT_6 0x08
-#define AC97_CMD_IRQ 31
-#define READ 0
-#define WRITE 1
-#define READ_WAIT 2
-#define RW_DONE 3
-
-struct au1000_period
-{
- u32 start;
- u32 relative_end; /*realtive to start of buffer*/
- struct au1000_period * next;
-};
-
-/*Au1000 AC97 Port Control Reisters*/
-struct au1000_ac97_reg {
- u32 volatile config;
- u32 volatile status;
- u32 volatile data;
- u32 volatile cmd;
- u32 volatile cntrl;
-};
-
-struct audio_stream {
- struct snd_pcm_substream *substream;
- int dma;
- spinlock_t dma_lock;
- struct au1000_period * buffer;
- unsigned int period_size;
- unsigned int periods;
-};
-
-struct snd_au1000 {
- struct snd_card *card;
- struct au1000_ac97_reg volatile *ac97_ioport;
-
- struct resource *ac97_res_port;
- spinlock_t ac97_lock;
- struct snd_ac97 *ac97;
-
- struct snd_pcm *pcm;
- struct audio_stream *stream[2]; /* playback & capture */
-};
-
-/*--------------------------- Local Functions --------------------------------*/
-static void
-au1000_set_ac97_xmit_slots(struct snd_au1000 *au1000, long xmit_slots)
-{
- u32 volatile ac97_config;
-
- spin_lock(&au1000->ac97_lock);
- ac97_config = au1000->ac97_ioport->config;
- ac97_config = ac97_config & ~AC97C_XMIT_SLOTS_MASK;
- ac97_config |= (xmit_slots << AC97C_XMIT_SLOTS_BIT);
- au1000->ac97_ioport->config = ac97_config;
- spin_unlock(&au1000->ac97_lock);
-}
-
-static void
-au1000_set_ac97_recv_slots(struct snd_au1000 *au1000, long recv_slots)
-{
- u32 volatile ac97_config;
-
- spin_lock(&au1000->ac97_lock);
- ac97_config = au1000->ac97_ioport->config;
- ac97_config = ac97_config & ~AC97C_RECV_SLOTS_MASK;
- ac97_config |= (recv_slots << AC97C_RECV_SLOTS_BIT);
- au1000->ac97_ioport->config = ac97_config;
- spin_unlock(&au1000->ac97_lock);
-}
-
-
-static void
-au1000_release_dma_link(struct audio_stream *stream)
-{
- struct au1000_period * pointer;
- struct au1000_period * pointer_next;
-
- stream->period_size = 0;
- stream->periods = 0;
- pointer = stream->buffer;
- if (! pointer)
- return;
- do {
- pointer_next = pointer->next;
- kfree(pointer);
- pointer = pointer_next;
- } while (pointer != stream->buffer);
- stream->buffer = NULL;
-}
-
-static int
-au1000_setup_dma_link(struct audio_stream *stream, unsigned int period_bytes,
- unsigned int periods)
-{
- struct snd_pcm_substream *substream = stream->substream;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct au1000_period *pointer;
- unsigned long dma_start;
- int i;
-
- dma_start = virt_to_phys(runtime->dma_area);
-
- if (stream->period_size == period_bytes &&
- stream->periods == periods)
- return 0; /* not changed */
-
- au1000_release_dma_link(stream);
-
- stream->period_size = period_bytes;
- stream->periods = periods;
-
- stream->buffer = kmalloc(sizeof(struct au1000_period), GFP_KERNEL);
- if (! stream->buffer)
- return -ENOMEM;
- pointer = stream->buffer;
- for (i = 0; i < periods; i++) {
- pointer->start = (u32)(dma_start + (i * period_bytes));
- pointer->relative_end = (u32) (((i+1) * period_bytes) - 0x1);
- if (i < periods - 1) {
- pointer->next = kmalloc(sizeof(struct au1000_period), GFP_KERNEL);
- if (! pointer->next) {
- au1000_release_dma_link(stream);
- return -ENOMEM;
- }
- pointer = pointer->next;
- }
- }
- pointer->next = stream->buffer;
- return 0;
-}
-
-static void
-au1000_dma_stop(struct audio_stream *stream)
-{
- if (snd_BUG_ON(!stream->buffer))
- return;
- disable_dma(stream->dma);
-}
-
-static void
-au1000_dma_start(struct audio_stream *stream)
-{
- if (snd_BUG_ON(!stream->buffer))
- return;
-
- init_dma(stream->dma);
- if (get_dma_active_buffer(stream->dma) == 0) {
- clear_dma_done0(stream->dma);
- set_dma_addr0(stream->dma, stream->buffer->start);
- set_dma_count0(stream->dma, stream->period_size >> 1);
- set_dma_addr1(stream->dma, stream->buffer->next->start);
- set_dma_count1(stream->dma, stream->period_size >> 1);
- } else {
- clear_dma_done1(stream->dma);
- set_dma_addr1(stream->dma, stream->buffer->start);
- set_dma_count1(stream->dma, stream->period_size >> 1);
- set_dma_addr0(stream->dma, stream->buffer->next->start);
- set_dma_count0(stream->dma, stream->period_size >> 1);
- }
- enable_dma_buffers(stream->dma);
- start_dma(stream->dma);
-}
-
-static irqreturn_t
-au1000_dma_interrupt(int irq, void *dev_id)
-{
- struct audio_stream *stream = (struct audio_stream *) dev_id;
- struct snd_pcm_substream *substream = stream->substream;
-
- spin_lock(&stream->dma_lock);
- switch (get_dma_buffer_done(stream->dma)) {
- case DMA_D0:
- stream->buffer = stream->buffer->next;
- clear_dma_done0(stream->dma);
- set_dma_addr0(stream->dma, stream->buffer->next->start);
- set_dma_count0(stream->dma, stream->period_size >> 1);
- enable_dma_buffer0(stream->dma);
- break;
- case DMA_D1:
- stream->buffer = stream->buffer->next;
- clear_dma_done1(stream->dma);
- set_dma_addr1(stream->dma, stream->buffer->next->start);
- set_dma_count1(stream->dma, stream->period_size >> 1);
- enable_dma_buffer1(stream->dma);
- break;
- case (DMA_D0 | DMA_D1):
- printk(KERN_ERR "DMA %d missed interrupt.\n",stream->dma);
- au1000_dma_stop(stream);
- au1000_dma_start(stream);
- break;
- case (~DMA_D0 & ~DMA_D1):
- printk(KERN_ERR "DMA %d empty irq.\n",stream->dma);
- }
- spin_unlock(&stream->dma_lock);
- snd_pcm_period_elapsed(substream);
- return IRQ_HANDLED;
-}
-
-/*-------------------------- PCM Audio Streams -------------------------------*/
-
-static unsigned int rates[] = {8000, 11025, 16000, 22050};
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
- .count = ARRAY_SIZE(rates),
- .list = rates,
- .mask = 0,
-};
-
-static struct snd_pcm_hardware snd_au1000_hw =
-{
- .info = (SNDRV_PCM_INFO_INTERLEAVED | \
- SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050),
- .rate_min = 8000,
- .rate_max = 22050,
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = 128*1024,
- .period_bytes_min = 32,
- .period_bytes_max = 16*1024,
- .periods_min = 8,
- .periods_max = 255,
- .fifo_size = 16,
-};
-
-static int
-snd_au1000_playback_open(struct snd_pcm_substream *substream)
-{
- struct snd_au1000 *au1000 = substream->pcm->private_data;
-
- au1000->stream[PLAYBACK]->substream = substream;
- au1000->stream[PLAYBACK]->buffer = NULL;
- substream->private_data = au1000->stream[PLAYBACK];
- substream->runtime->hw = snd_au1000_hw;
- return (snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0);
-}
-
-static int
-snd_au1000_capture_open(struct snd_pcm_substream *substream)
-{
- struct snd_au1000 *au1000 = substream->pcm->private_data;
-
- au1000->stream[CAPTURE]->substream = substream;
- au1000->stream[CAPTURE]->buffer = NULL;
- substream->private_data = au1000->stream[CAPTURE];
- substream->runtime->hw = snd_au1000_hw;
- return (snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0);
-}
-
-static int
-snd_au1000_playback_close(struct snd_pcm_substream *substream)
-{
- struct snd_au1000 *au1000 = substream->pcm->private_data;
-
- au1000->stream[PLAYBACK]->substream = NULL;
- return 0;
-}
-
-static int
-snd_au1000_capture_close(struct snd_pcm_substream *substream)
-{
- struct snd_au1000 *au1000 = substream->pcm->private_data;
-
- au1000->stream[CAPTURE]->substream = NULL;
- return 0;
-}
-
-static int
-snd_au1000_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct audio_stream *stream = substream->private_data;
- int err;
-
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
- return au1000_setup_dma_link(stream,
- params_period_bytes(hw_params),
- params_periods(hw_params));
-}
-
-static int
-snd_au1000_hw_free(struct snd_pcm_substream *substream)
-{
- struct audio_stream *stream = substream->private_data;
- au1000_release_dma_link(stream);
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int
-snd_au1000_playback_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_au1000 *au1000 = substream->pcm->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- if (runtime->channels == 1)
- au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_4);
- else
- au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4);
- snd_ac97_set_rate(au1000->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
- return 0;
-}
-
-static int
-snd_au1000_capture_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_au1000 *au1000 = substream->pcm->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- if (runtime->channels == 1)
- au1000_set_ac97_recv_slots(au1000, AC97_SLOT_4);
- else
- au1000_set_ac97_recv_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4);
- snd_ac97_set_rate(au1000->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
- return 0;
-}
-
-static int
-snd_au1000_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct audio_stream *stream = substream->private_data;
- int err = 0;
-
- spin_lock(&stream->dma_lock);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- au1000_dma_start(stream);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- au1000_dma_stop(stream);
- break;
- default:
- err = -EINVAL;
- break;
- }
- spin_unlock(&stream->dma_lock);
- return err;
-}
-
-static snd_pcm_uframes_t
-snd_au1000_pointer(struct snd_pcm_substream *substream)
-{
- struct audio_stream *stream = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- long location;
-
- spin_lock(&stream->dma_lock);
- location = get_dma_residue(stream->dma);
- spin_unlock(&stream->dma_lock);
- location = stream->buffer->relative_end - location;
- if (location == -1)
- location = 0;
- return bytes_to_frames(runtime,location);
-}
-
-static struct snd_pcm_ops snd_card_au1000_playback_ops = {
- .open = snd_au1000_playback_open,
- .close = snd_au1000_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_au1000_hw_params,
- .hw_free = snd_au1000_hw_free,
- .prepare = snd_au1000_playback_prepare,
- .trigger = snd_au1000_trigger,
- .pointer = snd_au1000_pointer,
-};
-
-static struct snd_pcm_ops snd_card_au1000_capture_ops = {
- .open = snd_au1000_capture_open,
- .close = snd_au1000_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_au1000_hw_params,
- .hw_free = snd_au1000_hw_free,
- .prepare = snd_au1000_capture_prepare,
- .trigger = snd_au1000_trigger,
- .pointer = snd_au1000_pointer,
-};
-
-static int __devinit
-snd_au1000_pcm_new(struct snd_au1000 *au1000)
-{
- struct snd_pcm *pcm;
- int err;
- unsigned long flags;
-
- if ((err = snd_pcm_new(au1000->card, "AU1000 AC97 PCM", 0, 1, 1, &pcm)) < 0)
- return err;
-
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL), 128*1024, 128*1024);
-
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- &snd_card_au1000_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &snd_card_au1000_capture_ops);
-
- pcm->private_data = au1000;
- pcm->info_flags = 0;
- strcpy(pcm->name, "Au1000 AC97 PCM");
-
- spin_lock_init(&au1000->stream[PLAYBACK]->dma_lock);
- spin_lock_init(&au1000->stream[CAPTURE]->dma_lock);
-
- flags = claim_dma_lock();
- if ((au1000->stream[PLAYBACK]->dma = request_au1000_dma(DMA_ID_AC97C_TX,
- "AC97 TX", au1000_dma_interrupt, IRQF_DISABLED,
- au1000->stream[PLAYBACK])) < 0) {
- release_dma_lock(flags);
- return -EBUSY;
- }
- if ((au1000->stream[CAPTURE]->dma = request_au1000_dma(DMA_ID_AC97C_RX,
- "AC97 RX", au1000_dma_interrupt, IRQF_DISABLED,
- au1000->stream[CAPTURE])) < 0){
- release_dma_lock(flags);
- return -EBUSY;
- }
- /* enable DMA coherency in read/write DMA channels */
- set_dma_mode(au1000->stream[PLAYBACK]->dma,
- get_dma_mode(au1000->stream[PLAYBACK]->dma) & ~DMA_NC);
- set_dma_mode(au1000->stream[CAPTURE]->dma,
- get_dma_mode(au1000->stream[CAPTURE]->dma) & ~DMA_NC);
- release_dma_lock(flags);
- au1000->pcm = pcm;
- return 0;
-}
-
-
-/*-------------------------- AC97 CODEC Control ------------------------------*/
-
-static unsigned short
-snd_au1000_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
-{
- struct snd_au1000 *au1000 = ac97->private_data;
- u32 volatile cmd;
- u16 volatile data;
- int i;
-
- spin_lock(&au1000->ac97_lock);
-/* would rather use the interrupt than this polling but it works and I can't
-get the interrupt driven case to work efficiently */
- for (i = 0; i < 0x5000; i++)
- if (!(au1000->ac97_ioport->status & AC97C_CP))
- break;
- if (i == 0x5000)
- printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n");
-
- cmd = (u32) reg & AC97C_INDEX_MASK;
- cmd |= AC97C_READ;
- au1000->ac97_ioport->cmd = cmd;
-
- /* now wait for the data */
- for (i = 0; i < 0x5000; i++)
- if (!(au1000->ac97_ioport->status & AC97C_CP))
- break;
- if (i == 0x5000) {
- printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n");
- spin_unlock(&au1000->ac97_lock);
- return 0;
- }
-
- data = au1000->ac97_ioport->cmd & 0xffff;
- spin_unlock(&au1000->ac97_lock);
-
- return data;
-
-}
-
-
-static void
-snd_au1000_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
-{
- struct snd_au1000 *au1000 = ac97->private_data;
- u32 cmd;
- int i;
-
- spin_lock(&au1000->ac97_lock);
-/* would rather use the interrupt than this polling but it works and I can't
-get the interrupt driven case to work efficiently */
- for (i = 0; i < 0x5000; i++)
- if (!(au1000->ac97_ioport->status & AC97C_CP))
- break;
- if (i == 0x5000)
- printk(KERN_ERR "au1000 AC97: AC97 command write timeout\n");
-
- cmd = (u32) reg & AC97C_INDEX_MASK;
- cmd &= ~AC97C_READ;
- cmd |= ((u32) val << AC97C_WD_BIT);
- au1000->ac97_ioport->cmd = cmd;
- spin_unlock(&au1000->ac97_lock);
-}
-
-static int __devinit
-snd_au1000_ac97_new(struct snd_au1000 *au1000)
-{
- int err;
- struct snd_ac97_bus *pbus;
- struct snd_ac97_template ac97;
- static struct snd_ac97_bus_ops ops = {
- .write = snd_au1000_ac97_write,
- .read = snd_au1000_ac97_read,
- };
-
- if ((au1000->ac97_res_port = request_mem_region(CPHYSADDR(AC97C_CONFIG),
- 0x100000, "Au1x00 AC97")) == NULL) {
- snd_printk(KERN_ERR "ALSA AC97: can't grap AC97 port\n");
- return -EBUSY;
- }
- au1000->ac97_ioport = (struct au1000_ac97_reg *)
- KSEG1ADDR(au1000->ac97_res_port->start);
-
- spin_lock_init(&au1000->ac97_lock);
-
- /* configure pins for AC'97
- TODO: move to board_setup.c */
- au_writel(au_readl(SYS_PINFUNC) & ~0x02, SYS_PINFUNC);
-
- /* Initialise Au1000's AC'97 Control Block */
- au1000->ac97_ioport->cntrl = AC97C_RS | AC97C_CE;
- udelay(10);
- au1000->ac97_ioport->cntrl = AC97C_CE;
- udelay(10);
-
- /* Initialise External CODEC -- cold reset */
- au1000->ac97_ioport->config = AC97C_RESET;
- udelay(10);
- au1000->ac97_ioport->config = 0x0;
- mdelay(5);
-
- /* Initialise AC97 middle-layer */
- if ((err = snd_ac97_bus(au1000->card, 0, &ops, au1000, &pbus)) < 0)
- return err;
-
- memset(&ac97, 0, sizeof(ac97));
- ac97.private_data = au1000;
- if ((err = snd_ac97_mixer(pbus, &ac97, &au1000->ac97)) < 0)
- return err;
-
- return 0;
-}
-
-/*------------------------------ Setup / Destroy ----------------------------*/
-
-void
-snd_au1000_free(struct snd_card *card)
-{
- struct snd_au1000 *au1000 = card->private_data;
-
- if (au1000->ac97_res_port) {
- /* put internal AC97 block into reset */
- au1000->ac97_ioport->cntrl = AC97C_RS;
- au1000->ac97_ioport = NULL;
- release_and_free_resource(au1000->ac97_res_port);
- }
-
- if (au1000->stream[PLAYBACK]) {
- if (au1000->stream[PLAYBACK]->dma >= 0)
- free_au1000_dma(au1000->stream[PLAYBACK]->dma);
- kfree(au1000->stream[PLAYBACK]);
- }
-
- if (au1000->stream[CAPTURE]) {
- if (au1000->stream[CAPTURE]->dma >= 0)
- free_au1000_dma(au1000->stream[CAPTURE]->dma);
- kfree(au1000->stream[CAPTURE]);
- }
-}
-
-
-static struct snd_card *au1000_card;
-
-static int __init
-au1000_init(void)
-{
- int err;
- struct snd_card *card;
- struct snd_au1000 *au1000;
-
- err = snd_card_create(-1, "AC97", THIS_MODULE,
- sizeof(struct snd_au1000), &card);
- if (err < 0)
- return err;
-
- card->private_free = snd_au1000_free;
- au1000 = card->private_data;
- au1000->card = card;
-
- au1000->stream[PLAYBACK] = kmalloc(sizeof(struct audio_stream), GFP_KERNEL);
- au1000->stream[CAPTURE ] = kmalloc(sizeof(struct audio_stream), GFP_KERNEL);
- /* so that snd_au1000_free will work as intended */
- au1000->ac97_res_port = NULL;
- if (au1000->stream[PLAYBACK])
- au1000->stream[PLAYBACK]->dma = -1;
- if (au1000->stream[CAPTURE ])
- au1000->stream[CAPTURE ]->dma = -1;
-
- if (au1000->stream[PLAYBACK] == NULL ||
- au1000->stream[CAPTURE ] == NULL) {
- snd_card_free(card);
- return -ENOMEM;
- }
-
- if ((err = snd_au1000_ac97_new(au1000)) < 0 ) {
- snd_card_free(card);
- return err;
- }
-
- if ((err = snd_au1000_pcm_new(au1000)) < 0) {
- snd_card_free(card);
- return err;
- }
-
- strcpy(card->driver, "Au1000-AC97");
- strcpy(card->shortname, "AMD Au1000-AC97");
- sprintf(card->longname, "AMD Au1000--AC97 ALSA Driver");
-
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
- return err;
- }
-
- printk(KERN_INFO "ALSA AC97: Driver Initialized\n");
- au1000_card = card;
- return 0;
-}
-
-static void __exit au1000_exit(void)
-{
- snd_card_free(au1000_card);
-}
-
-module_init(au1000_init);
-module_exit(au1000_exit);
-
diff --git a/sound/mips/hal2.c b/sound/mips/hal2.c
index 453d343550a8..f88e6a6733a5 100644
--- a/sound/mips/hal2.c
+++ b/sound/mips/hal2.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for A2 audio system used in SGI machines
* Copyright (c) 2008 Thomas Bogendoerfer <tsbogend@alpha.fanken.de>
*
* Based on OSS code from Ladislav Michl <ladis@linux-mips.org>, which
* was based on code from Ulf Carlsson
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
*/
#include <linux/kernel.h>
#include <linux/init.h>
@@ -26,6 +13,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/module.h>
#include <asm/sgi/hpc3.h>
#include <asm/sgi/ip22.h>
@@ -218,6 +206,8 @@ static int hal2_gain_get(struct snd_kcontrol *kcontrol,
l = (tmp >> H2I_C2_L_GAIN_SHIFT) & 15;
r = (tmp >> H2I_C2_R_GAIN_SHIFT) & 15;
break;
+ default:
+ return -EINVAL;
}
ucontrol->value.integer.value[0] = l;
ucontrol->value.integer.value[1] = r;
@@ -255,11 +245,13 @@ static int hal2_gain_put(struct snd_kcontrol *kcontrol,
new |= (r << H2I_C2_R_GAIN_SHIFT);
hal2_i_write32(hal2, H2I_ADC_C2, new);
break;
+ default:
+ return -EINVAL;
}
return old != new;
}
-static struct snd_kcontrol_new hal2_ctrl_headphone __devinitdata = {
+static const struct snd_kcontrol_new hal2_ctrl_headphone = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Headphone Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -269,7 +261,7 @@ static struct snd_kcontrol_new hal2_ctrl_headphone __devinitdata = {
.put = hal2_gain_put,
};
-static struct snd_kcontrol_new hal2_ctrl_mic __devinitdata = {
+static const struct snd_kcontrol_new hal2_ctrl_mic = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mic Capture Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -279,7 +271,7 @@ static struct snd_kcontrol_new hal2_ctrl_mic __devinitdata = {
.put = hal2_gain_put,
};
-static int __devinit hal2_mixer_create(struct snd_hal2 *hal2)
+static int hal2_mixer_create(struct snd_hal2 *hal2)
{
int err;
@@ -449,22 +441,24 @@ static inline void hal2_stop_adc(struct snd_hal2 *hal2)
hal2->adc.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
}
-static int hal2_alloc_dmabuf(struct hal2_codec *codec)
+static int hal2_alloc_dmabuf(struct snd_hal2 *hal2, struct hal2_codec *codec,
+ enum dma_data_direction buffer_dir)
{
+ struct device *dev = hal2->card->dev;
struct hal2_desc *desc;
dma_addr_t desc_dma, buffer_dma;
int count = H2_BUF_SIZE / H2_BLOCK_SIZE;
int i;
- codec->buffer = dma_alloc_noncoherent(NULL, H2_BUF_SIZE,
- &buffer_dma, GFP_KERNEL);
+ codec->buffer = dma_alloc_noncoherent(dev, H2_BUF_SIZE, &buffer_dma,
+ buffer_dir, GFP_KERNEL);
if (!codec->buffer)
return -ENOMEM;
- desc = dma_alloc_noncoherent(NULL, count * sizeof(struct hal2_desc),
- &desc_dma, GFP_KERNEL);
+ desc = dma_alloc_noncoherent(dev, count * sizeof(struct hal2_desc),
+ &desc_dma, DMA_BIDIRECTIONAL, GFP_KERNEL);
if (!desc) {
- dma_free_noncoherent(NULL, H2_BUF_SIZE,
- codec->buffer, buffer_dma);
+ dma_free_noncoherent(dev, H2_BUF_SIZE, codec->buffer, buffer_dma,
+ buffer_dir);
return -ENOMEM;
}
codec->buffer_dma = buffer_dma;
@@ -477,25 +471,30 @@ static int hal2_alloc_dmabuf(struct hal2_codec *codec)
desc_dma : desc_dma + (i + 1) * sizeof(struct hal2_desc);
desc++;
}
- dma_cache_sync(NULL, codec->desc, count * sizeof(struct hal2_desc),
- DMA_TO_DEVICE);
+ dma_sync_single_for_device(dev, codec->desc_dma,
+ count * sizeof(struct hal2_desc),
+ DMA_BIDIRECTIONAL);
codec->desc_count = count;
return 0;
}
-static void hal2_free_dmabuf(struct hal2_codec *codec)
+static void hal2_free_dmabuf(struct snd_hal2 *hal2, struct hal2_codec *codec,
+ enum dma_data_direction buffer_dir)
{
- dma_free_noncoherent(NULL, codec->desc_count * sizeof(struct hal2_desc),
- codec->desc, codec->desc_dma);
- dma_free_noncoherent(NULL, H2_BUF_SIZE, codec->buffer,
- codec->buffer_dma);
+ struct device *dev = hal2->card->dev;
+
+ dma_free_noncoherent(dev, codec->desc_count * sizeof(struct hal2_desc),
+ codec->desc, codec->desc_dma, DMA_BIDIRECTIONAL);
+ dma_free_noncoherent(dev, H2_BUF_SIZE, codec->buffer, codec->buffer_dma,
+ buffer_dir);
}
-static struct snd_pcm_hardware hal2_pcm_hw = {
+static const struct snd_pcm_hardware hal2_pcm_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER),
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_SYNC_APPLPTR),
.formats = SNDRV_PCM_FMTBIT_S16_BE,
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
@@ -509,42 +508,20 @@ static struct snd_pcm_hardware hal2_pcm_hw = {
.periods_max = 1024,
};
-static int hal2_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- int err;
-
- err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
- if (err < 0)
- return err;
-
- return 0;
-}
-
-static int hal2_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int hal2_playback_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
- int err;
runtime->hw = hal2_pcm_hw;
-
- err = hal2_alloc_dmabuf(&hal2->dac);
- if (err)
- return err;
- return 0;
+ return hal2_alloc_dmabuf(hal2, &hal2->dac, DMA_TO_DEVICE);
}
static int hal2_playback_close(struct snd_pcm_substream *substream)
{
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
- hal2_free_dmabuf(&hal2->dac);
+ hal2_free_dmabuf(hal2, &hal2->dac, DMA_TO_DEVICE);
return 0;
}
@@ -558,6 +535,8 @@ static int hal2_playback_prepare(struct snd_pcm_substream *substream)
dac->sample_rate = hal2_compute_rate(dac, runtime->rate);
memset(&dac->pcm_indirect, 0, sizeof(dac->pcm_indirect));
dac->pcm_indirect.hw_buffer_size = H2_BUF_SIZE;
+ dac->pcm_indirect.hw_queue_size = H2_BUF_SIZE / 2;
+ dac->pcm_indirect.hw_io = dac->buffer_dma;
dac->pcm_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
dac->substream = substream;
hal2_setup_dac(hal2);
@@ -570,9 +549,6 @@ static int hal2_playback_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- hal2->dac.pcm_indirect.hw_io = hal2->dac.buffer_dma;
- hal2->dac.pcm_indirect.hw_data = 0;
- substream->ops->ack(substream);
hal2_start_dac(hal2);
break;
case SNDRV_PCM_TRIGGER_STOP:
@@ -601,7 +577,9 @@ static void hal2_playback_transfer(struct snd_pcm_substream *substream,
unsigned char *buf = hal2->dac.buffer + rec->hw_data;
memcpy(buf, substream->runtime->dma_area + rec->sw_data, bytes);
- dma_cache_sync(NULL, buf, bytes, DMA_TO_DEVICE);
+ dma_sync_single_for_device(hal2->card->dev,
+ hal2->dac.buffer_dma + rec->hw_data, bytes,
+ DMA_TO_DEVICE);
}
@@ -610,33 +588,25 @@ static int hal2_playback_ack(struct snd_pcm_substream *substream)
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
struct hal2_codec *dac = &hal2->dac;
- dac->pcm_indirect.hw_queue_size = H2_BUF_SIZE / 2;
- snd_pcm_indirect_playback_transfer(substream,
- &dac->pcm_indirect,
- hal2_playback_transfer);
- return 0;
+ return snd_pcm_indirect_playback_transfer(substream,
+ &dac->pcm_indirect,
+ hal2_playback_transfer);
}
static int hal2_capture_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
- struct hal2_codec *adc = &hal2->adc;
- int err;
runtime->hw = hal2_pcm_hw;
-
- err = hal2_alloc_dmabuf(adc);
- if (err)
- return err;
- return 0;
+ return hal2_alloc_dmabuf(hal2, &hal2->adc, DMA_FROM_DEVICE);
}
static int hal2_capture_close(struct snd_pcm_substream *substream)
{
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
- hal2_free_dmabuf(&hal2->adc);
+ hal2_free_dmabuf(hal2, &hal2->adc, DMA_FROM_DEVICE);
return 0;
}
@@ -651,6 +621,7 @@ static int hal2_capture_prepare(struct snd_pcm_substream *substream)
memset(&adc->pcm_indirect, 0, sizeof(adc->pcm_indirect));
adc->pcm_indirect.hw_buffer_size = H2_BUF_SIZE;
adc->pcm_indirect.hw_queue_size = H2_BUF_SIZE / 2;
+ adc->pcm_indirect.hw_io = adc->buffer_dma;
adc->pcm_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
adc->substream = substream;
hal2_setup_adc(hal2);
@@ -663,9 +634,6 @@ static int hal2_capture_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- hal2->adc.pcm_indirect.hw_io = hal2->adc.buffer_dma;
- hal2->adc.pcm_indirect.hw_data = 0;
- printk(KERN_DEBUG "buffer_dma %x\n", hal2->adc.buffer_dma);
hal2_start_adc(hal2);
break;
case SNDRV_PCM_TRIGGER_STOP:
@@ -693,7 +661,9 @@ static void hal2_capture_transfer(struct snd_pcm_substream *substream,
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
unsigned char *buf = hal2->adc.buffer + rec->hw_data;
- dma_cache_sync(NULL, buf, bytes, DMA_FROM_DEVICE);
+ dma_sync_single_for_cpu(hal2->card->dev,
+ hal2->adc.buffer_dma + rec->hw_data, bytes,
+ DMA_FROM_DEVICE);
memcpy(substream->runtime->dma_area + rec->sw_data, buf, bytes);
}
@@ -702,37 +672,30 @@ static int hal2_capture_ack(struct snd_pcm_substream *substream)
struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
struct hal2_codec *adc = &hal2->adc;
- snd_pcm_indirect_capture_transfer(substream,
- &adc->pcm_indirect,
- hal2_capture_transfer);
- return 0;
+ return snd_pcm_indirect_capture_transfer(substream,
+ &adc->pcm_indirect,
+ hal2_capture_transfer);
}
-static struct snd_pcm_ops hal2_playback_ops = {
+static const struct snd_pcm_ops hal2_playback_ops = {
.open = hal2_playback_open,
.close = hal2_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = hal2_pcm_hw_params,
- .hw_free = hal2_pcm_hw_free,
.prepare = hal2_playback_prepare,
.trigger = hal2_playback_trigger,
.pointer = hal2_playback_pointer,
.ack = hal2_playback_ack,
};
-static struct snd_pcm_ops hal2_capture_ops = {
+static const struct snd_pcm_ops hal2_capture_ops = {
.open = hal2_capture_open,
.close = hal2_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = hal2_pcm_hw_params,
- .hw_free = hal2_pcm_hw_free,
.prepare = hal2_capture_prepare,
.trigger = hal2_capture_trigger,
.pointer = hal2_capture_pointer,
.ack = hal2_capture_ack,
};
-static int __devinit hal2_pcm_create(struct snd_hal2 *hal2)
+static int hal2_pcm_create(struct snd_hal2 *hal2)
{
struct snd_pcm *pcm;
int err;
@@ -743,16 +706,15 @@ static int __devinit hal2_pcm_create(struct snd_hal2 *hal2)
return err;
pcm->private_data = hal2;
- strcpy(pcm->name, "SGI HAL2");
+ strscpy(pcm->name, "SGI HAL2");
/* set operators */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&hal2_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&hal2_capture_ops);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
- 0, 1024 * 1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ NULL, 0, 1024 * 1024);
return 0;
}
@@ -766,7 +728,7 @@ static int hal2_dev_free(struct snd_device *device)
return 0;
}
-static struct snd_device_ops hal2_ops = {
+static const struct snd_device_ops hal2_ops = {
.dev_free = hal2_dev_free,
};
@@ -811,7 +773,7 @@ static int hal2_create(struct snd_card *card, struct snd_hal2 **rchip)
struct hpc3_regs *hpc3 = hpc3c0;
int err;
- hal2 = kzalloc(sizeof(struct snd_hal2), GFP_KERNEL);
+ hal2 = kzalloc(sizeof(*hal2), GFP_KERNEL);
if (!hal2)
return -ENOMEM;
@@ -873,13 +835,13 @@ static int hal2_create(struct snd_card *card, struct snd_hal2 **rchip)
return 0;
}
-static int __devinit hal2_probe(struct platform_device *pdev)
+static int hal2_probe(struct platform_device *pdev)
{
struct snd_card *card;
struct snd_hal2 *chip;
int err;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_card_new(&pdev->dev, index, id, THIS_MODULE, 0, &card);
if (err < 0)
return err;
@@ -888,7 +850,6 @@ static int __devinit hal2_probe(struct platform_device *pdev)
snd_card_free(card);
return err;
}
- snd_card_set_dev(card, &pdev->dev);
err = hal2_pcm_create(chip);
if (err < 0) {
@@ -901,8 +862,8 @@ static int __devinit hal2_probe(struct platform_device *pdev)
return err;
}
- strcpy(card->driver, "SGI HAL2 Audio");
- strcpy(card->shortname, "SGI HAL2 Audio");
+ strscpy(card->driver, "SGI HAL2 Audio");
+ strscpy(card->shortname, "SGI HAL2 Audio");
sprintf(card->longname, "%s irq %i",
card->shortname,
SGI_HPCDMA_IRQ);
@@ -916,33 +877,19 @@ static int __devinit hal2_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit hal2_remove(struct platform_device *pdev)
+static void hal2_remove(struct platform_device *pdev)
{
struct snd_card *card = platform_get_drvdata(pdev);
snd_card_free(card);
- platform_set_drvdata(pdev, NULL);
- return 0;
}
static struct platform_driver hal2_driver = {
.probe = hal2_probe,
- .remove = __devexit_p(hal2_remove),
+ .remove = hal2_remove,
.driver = {
.name = "sgihal2",
- .owner = THIS_MODULE,
}
};
-static int __init alsa_card_hal2_init(void)
-{
- return platform_driver_register(&hal2_driver);
-}
-
-static void __exit alsa_card_hal2_exit(void)
-{
- platform_driver_unregister(&hal2_driver);
-}
-
-module_init(alsa_card_hal2_init);
-module_exit(alsa_card_hal2_exit);
+module_platform_driver(hal2_driver);
diff --git a/sound/mips/hal2.h b/sound/mips/hal2.h
index f19828bc64e0..f70cce9a1406 100644
--- a/sound/mips/hal2.h
+++ b/sound/mips/hal2.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __HAL2_H
#define __HAL2_H
@@ -5,20 +6,6 @@
* Driver for HAL2 sound processors
* Copyright (c) 1999 Ulf Carlsson <ulfc@bun.falkenberg.se>
* Copyright (c) 2001, 2002, 2003 Ladislav Michl <ladis@linux-mips.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
*/
#include <linux/types.h>
diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c
index 717604c00f0a..077fdf2181c1 100644
--- a/sound/mips/sgio2audio.c
+++ b/sound/mips/sgio2audio.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Sound driver for Silicon Graphics O2 Workstations A/V board audio.
*
@@ -5,21 +6,6 @@
* Copyright 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de>
* Mxier part taken from mace_audio.c:
* Copyright 2007 Thorben Jändling <tj.trevelyan@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -30,6 +16,8 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/module.h>
#include <asm/ip32/ip32_ints.h>
#include <asm/ip32/mace.h>
@@ -45,7 +33,6 @@
MODULE_AUTHOR("Vivien Chappelier <vivien.chappelier@linux-mips.org>");
MODULE_DESCRIPTION("SGI O2 Audio");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Silicon Graphics, O2 Audio}}");
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
@@ -116,9 +103,8 @@ static int read_ad1843_reg(void *priv, int reg)
{
struct snd_sgio2audio *chip = priv;
int val;
- unsigned long flags;
- spin_lock_irqsave(&chip->ad1843_lock, flags);
+ guard(spinlock_irqsave)(&chip->ad1843_lock);
writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) |
CODEC_CONTROL_READ, &mace->perif.audio.codec_control);
@@ -128,7 +114,6 @@ static int read_ad1843_reg(void *priv, int reg)
val = readq(&mace->perif.audio.codec_read);
- spin_unlock_irqrestore(&chip->ad1843_lock, flags);
return val;
}
@@ -139,9 +124,8 @@ static int write_ad1843_reg(void *priv, int reg, int word)
{
struct snd_sgio2audio *chip = priv;
int val;
- unsigned long flags;
- spin_lock_irqsave(&chip->ad1843_lock, flags);
+ guard(spinlock_irqsave)(&chip->ad1843_lock);
writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) |
(word << CODEC_CONTROL_WORD_SHIFT),
@@ -150,7 +134,6 @@ static int write_ad1843_reg(void *priv, int reg, int word)
val = readq(&mace->perif.audio.codec_control); /* flush bus */
udelay(200);
- spin_unlock_irqrestore(&chip->ad1843_lock, flags);
return 0;
}
@@ -200,17 +183,10 @@ static int sgio2audio_gain_put(struct snd_kcontrol *kcontrol,
static int sgio2audio_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[3] = {
+ static const char * const texts[3] = {
"Cam Mic", "Mic", "Line"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item >= 3)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int sgio2audio_source_get(struct snd_kcontrol *kcontrol,
@@ -236,7 +212,7 @@ static int sgio2audio_source_put(struct snd_kcontrol *kcontrol,
}
/* dac1/pcm0 mixer control */
-static struct snd_kcontrol_new sgio2audio_ctrl_pcm0 __devinitdata = {
+static const struct snd_kcontrol_new sgio2audio_ctrl_pcm0 = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
.index = 0,
@@ -248,7 +224,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_pcm0 __devinitdata = {
};
/* dac2/pcm1 mixer control */
-static struct snd_kcontrol_new sgio2audio_ctrl_pcm1 __devinitdata = {
+static const struct snd_kcontrol_new sgio2audio_ctrl_pcm1 = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
.index = 1,
@@ -260,7 +236,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_pcm1 __devinitdata = {
};
/* record level mixer control */
-static struct snd_kcontrol_new sgio2audio_ctrl_reclevel __devinitdata = {
+static const struct snd_kcontrol_new sgio2audio_ctrl_reclevel = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -271,7 +247,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_reclevel __devinitdata = {
};
/* record level source control */
-static struct snd_kcontrol_new sgio2audio_ctrl_recsource __devinitdata = {
+static const struct snd_kcontrol_new sgio2audio_ctrl_recsource = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -281,7 +257,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_recsource __devinitdata = {
};
/* line mixer control */
-static struct snd_kcontrol_new sgio2audio_ctrl_line __devinitdata = {
+static const struct snd_kcontrol_new sgio2audio_ctrl_line = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line Playback Volume",
.index = 0,
@@ -293,7 +269,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_line __devinitdata = {
};
/* cd mixer control */
-static struct snd_kcontrol_new sgio2audio_ctrl_cd __devinitdata = {
+static const struct snd_kcontrol_new sgio2audio_ctrl_cd = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line Playback Volume",
.index = 1,
@@ -305,7 +281,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_cd __devinitdata = {
};
/* mic mixer control */
-static struct snd_kcontrol_new sgio2audio_ctrl_mic __devinitdata = {
+static const struct snd_kcontrol_new sgio2audio_ctrl_mic = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mic Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -316,7 +292,7 @@ static struct snd_kcontrol_new sgio2audio_ctrl_mic __devinitdata = {
};
-static int __devinit snd_sgio2audio_new_mixer(struct snd_sgio2audio *chip)
+static int snd_sgio2audio_new_mixer(struct snd_sgio2audio *chip)
{
int err;
@@ -371,10 +347,9 @@ static int snd_sgio2audio_dma_pull_frag(struct snd_sgio2audio *chip,
u64 *src;
s16 *dst;
u64 x;
- unsigned long flags;
struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime;
- spin_lock_irqsave(&chip->channel[ch].lock, flags);
+ guard(spinlock_irqsave)(&chip->channel[ch].lock);
src_base = (unsigned long) chip->ring_base | (ch << CHANNEL_RING_SHIFT);
src_pos = readq(&mace->perif.audio.chan[ch].read_ptr);
@@ -403,7 +378,6 @@ static int snd_sgio2audio_dma_pull_frag(struct snd_sgio2audio *chip,
writeq(src_pos, &mace->perif.audio.chan[ch].read_ptr); /* in bytes */
chip->channel[ch].pos = dst_pos;
- spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
return ret;
}
@@ -419,10 +393,9 @@ static int snd_sgio2audio_dma_push_frag(struct snd_sgio2audio *chip,
int src_pos;
u64 *dst;
s16 *src;
- unsigned long flags;
struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime;
- spin_lock_irqsave(&chip->channel[ch].lock, flags);
+ guard(spinlock_irqsave)(&chip->channel[ch].lock);
dst_base = (unsigned long)chip->ring_base | (ch << CHANNEL_RING_SHIFT);
dst_pos = readq(&mace->perif.audio.chan[ch].write_ptr);
@@ -453,7 +426,6 @@ static int snd_sgio2audio_dma_push_frag(struct snd_sgio2audio *chip,
writeq(dst_pos, &mace->perif.audio.chan[ch].write_ptr); /* in bytes */
chip->channel[ch].pos = src_pos;
- spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
return ret;
}
@@ -538,7 +510,7 @@ static irqreturn_t snd_sgio2audio_error_isr(int irq, void *dev_id)
/* PCM part */
/* PCM hardware definition */
-static struct snd_pcm_hardware snd_sgio2audio_pcm_hw = {
+static const struct snd_pcm_hardware snd_sgio2audio_pcm_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -597,21 +569,6 @@ static int snd_sgio2audio_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-
-/* hw_params callback */
-static int snd_sgio2audio_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
-}
-
-/* hw_free callback */
-static int snd_sgio2audio_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_vmalloc_buffer(substream);
-}
-
/* prepare callback */
static int snd_sgio2audio_pcm_prepare(struct snd_pcm_substream *substream)
{
@@ -619,9 +576,8 @@ static int snd_sgio2audio_pcm_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
int ch = chan->idx;
- unsigned long flags;
- spin_lock_irqsave(&chip->channel[ch].lock, flags);
+ guard(spinlock_irqsave)(&chip->channel[ch].lock);
/* Setup the pseudo-dma transfer pointers. */
chip->channel[ch].pos = 0;
@@ -645,7 +601,6 @@ static int snd_sgio2audio_pcm_prepare(struct snd_pcm_substream *substream)
runtime->channels);
break;
}
- spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
return 0;
}
@@ -681,43 +636,28 @@ snd_sgio2audio_pcm_pointer(struct snd_pcm_substream *substream)
}
/* operators */
-static struct snd_pcm_ops snd_sgio2audio_playback1_ops = {
+static const struct snd_pcm_ops snd_sgio2audio_playback1_ops = {
.open = snd_sgio2audio_playback1_open,
.close = snd_sgio2audio_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_sgio2audio_pcm_hw_params,
- .hw_free = snd_sgio2audio_pcm_hw_free,
.prepare = snd_sgio2audio_pcm_prepare,
.trigger = snd_sgio2audio_pcm_trigger,
.pointer = snd_sgio2audio_pcm_pointer,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
-static struct snd_pcm_ops snd_sgio2audio_playback2_ops = {
+static const struct snd_pcm_ops snd_sgio2audio_playback2_ops = {
.open = snd_sgio2audio_playback2_open,
.close = snd_sgio2audio_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_sgio2audio_pcm_hw_params,
- .hw_free = snd_sgio2audio_pcm_hw_free,
.prepare = snd_sgio2audio_pcm_prepare,
.trigger = snd_sgio2audio_pcm_trigger,
.pointer = snd_sgio2audio_pcm_pointer,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
-static struct snd_pcm_ops snd_sgio2audio_capture_ops = {
+static const struct snd_pcm_ops snd_sgio2audio_capture_ops = {
.open = snd_sgio2audio_capture_open,
.close = snd_sgio2audio_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_sgio2audio_pcm_hw_params,
- .hw_free = snd_sgio2audio_pcm_hw_free,
.prepare = snd_sgio2audio_pcm_prepare,
.trigger = snd_sgio2audio_pcm_trigger,
.pointer = snd_sgio2audio_pcm_pointer,
- .page = snd_pcm_lib_get_vmalloc_page,
- .mmap = snd_pcm_lib_mmap_vmalloc,
};
/*
@@ -725,7 +665,7 @@ static struct snd_pcm_ops snd_sgio2audio_capture_ops = {
*/
/* create a pcm device */
-static int __devinit snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip)
+static int snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip)
{
struct snd_pcm *pcm;
int err;
@@ -736,13 +676,14 @@ static int __devinit snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip)
return err;
pcm->private_data = chip;
- strcpy(pcm->name, "SGI O2 DAC1");
+ strscpy(pcm->name, "SGI O2 DAC1");
/* set operators */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_sgio2audio_playback1_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_sgio2audio_capture_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
/* create second pcm device with one outputs and no input */
err = snd_pcm_new(chip->card, "SGI O2 Audio", 1, 1, 0, &pcm);
@@ -750,11 +691,12 @@ static int __devinit snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip)
return err;
pcm->private_data = chip;
- strcpy(pcm->name, "SGI O2 DAC2");
+ strscpy(pcm->name, "SGI O2 DAC2");
/* set operators */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_sgio2audio_playback2_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
return 0;
}
@@ -814,7 +756,7 @@ static int snd_sgio2audio_free(struct snd_sgio2audio *chip)
free_irq(snd_sgio2_isr_table[i].irq,
&chip->channel[snd_sgio2_isr_table[i].idx]);
- dma_free_coherent(NULL, MACEISA_RINGBUFFERS_SIZE,
+ dma_free_coherent(chip->card->dev, MACEISA_RINGBUFFERS_SIZE,
chip->ring_base, chip->ring_base_dma);
/* release card data */
@@ -829,12 +771,12 @@ static int snd_sgio2audio_dev_free(struct snd_device *device)
return snd_sgio2audio_free(chip);
}
-static struct snd_device_ops ops = {
+static const struct snd_device_ops ops = {
.dev_free = snd_sgio2audio_dev_free,
};
-static int __devinit snd_sgio2audio_create(struct snd_card *card,
- struct snd_sgio2audio **rchip)
+static int snd_sgio2audio_create(struct snd_card *card,
+ struct snd_sgio2audio **rchip)
{
struct snd_sgio2audio *chip;
int i, err;
@@ -846,14 +788,15 @@ static int __devinit snd_sgio2audio_create(struct snd_card *card,
if (!(readq(&mace->perif.audio.control) & AUDIO_CONTROL_CODEC_PRESENT))
return -ENOENT;
- chip = kzalloc(sizeof(struct snd_sgio2audio), GFP_KERNEL);
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
chip->card = card;
- chip->ring_base = dma_alloc_coherent(NULL, MACEISA_RINGBUFFERS_SIZE,
- &chip->ring_base_dma, GFP_USER);
+ chip->ring_base = dma_alloc_coherent(card->dev,
+ MACEISA_RINGBUFFERS_SIZE,
+ &chip->ring_base_dma, GFP_KERNEL);
if (chip->ring_base == NULL) {
printk(KERN_ERR
"sgio2audio: could not allocate ring buffers\n");
@@ -913,13 +856,13 @@ static int __devinit snd_sgio2audio_create(struct snd_card *card,
return 0;
}
-static int __devinit snd_sgio2audio_probe(struct platform_device *pdev)
+static int snd_sgio2audio_probe(struct platform_device *pdev)
{
struct snd_card *card;
struct snd_sgio2audio *chip;
int err;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_card_new(&pdev->dev, index, id, THIS_MODULE, 0, &card);
if (err < 0)
return err;
@@ -928,7 +871,6 @@ static int __devinit snd_sgio2audio_probe(struct platform_device *pdev)
snd_card_free(card);
return err;
}
- snd_card_set_dev(card, &pdev->dev);
err = snd_sgio2audio_new_pcm(chip);
if (err < 0) {
@@ -941,8 +883,8 @@ static int __devinit snd_sgio2audio_probe(struct platform_device *pdev)
return err;
}
- strcpy(card->driver, "SGI O2 Audio");
- strcpy(card->shortname, "SGI O2 Audio");
+ strscpy(card->driver, "SGI O2 Audio");
+ strscpy(card->shortname, "SGI O2 Audio");
sprintf(card->longname, "%s irq %i-%i",
card->shortname,
MACEISA_AUDIO1_DMAT_IRQ,
@@ -957,33 +899,19 @@ static int __devinit snd_sgio2audio_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit snd_sgio2audio_remove(struct platform_device *pdev)
+static void snd_sgio2audio_remove(struct platform_device *pdev)
{
struct snd_card *card = platform_get_drvdata(pdev);
snd_card_free(card);
- platform_set_drvdata(pdev, NULL);
- return 0;
}
static struct platform_driver sgio2audio_driver = {
.probe = snd_sgio2audio_probe,
- .remove = __devexit_p(snd_sgio2audio_remove),
- .driver = {
+ .remove = snd_sgio2audio_remove,
+ .driver = {
.name = "sgio2audio",
- .owner = THIS_MODULE,
}
};
-static int __init alsa_card_sgio2audio_init(void)
-{
- return platform_driver_register(&sgio2audio_driver);
-}
-
-static void __exit alsa_card_sgio2audio_exit(void)
-{
- platform_driver_unregister(&sgio2audio_driver);
-}
-
-module_init(alsa_card_sgio2audio_init)
-module_exit(alsa_card_sgio2audio_exit)
+module_platform_driver(sgio2audio_driver);
diff --git a/sound/mips/snd-n64.c b/sound/mips/snd-n64.c
new file mode 100644
index 000000000000..f17e63f2ff5a
--- /dev/null
+++ b/sound/mips/snd-n64.c
@@ -0,0 +1,369 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sound driver for Nintendo 64.
+ *
+ * Copyright 2021 Lauri Kasanen
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+MODULE_AUTHOR("Lauri Kasanen <cand@gmx.com>");
+MODULE_DESCRIPTION("N64 Audio");
+MODULE_LICENSE("GPL");
+
+#define AI_NTSC_DACRATE 48681812
+#define AI_STATUS_BUSY (1 << 30)
+#define AI_STATUS_FULL (1 << 31)
+
+#define AI_ADDR_REG 0
+#define AI_LEN_REG 1
+#define AI_CONTROL_REG 2
+#define AI_STATUS_REG 3
+#define AI_RATE_REG 4
+#define AI_BITCLOCK_REG 5
+
+#define MI_INTR_REG 2
+#define MI_MASK_REG 3
+
+#define MI_INTR_AI 0x04
+
+#define MI_MASK_CLR_AI 0x0010
+#define MI_MASK_SET_AI 0x0020
+
+
+struct n64audio {
+ u32 __iomem *ai_reg_base;
+ u32 __iomem *mi_reg_base;
+
+ void *ring_base;
+ dma_addr_t ring_base_dma;
+
+ struct snd_card *card;
+
+ struct {
+ struct snd_pcm_substream *substream;
+ int pos, nextpos;
+ u32 writesize;
+ u32 bufsize;
+ spinlock_t lock;
+ } chan;
+};
+
+static void n64audio_write_reg(struct n64audio *priv, const u8 reg, const u32 value)
+{
+ writel(value, priv->ai_reg_base + reg);
+}
+
+static void n64mi_write_reg(struct n64audio *priv, const u8 reg, const u32 value)
+{
+ writel(value, priv->mi_reg_base + reg);
+}
+
+static u32 n64mi_read_reg(struct n64audio *priv, const u8 reg)
+{
+ return readl(priv->mi_reg_base + reg);
+}
+
+static void n64audio_push(struct n64audio *priv)
+{
+ struct snd_pcm_runtime *runtime = priv->chan.substream->runtime;
+ u32 count;
+
+ guard(spinlock_irqsave)(&priv->chan.lock);
+
+ count = priv->chan.writesize;
+
+ memcpy(priv->ring_base + priv->chan.nextpos,
+ runtime->dma_area + priv->chan.nextpos, count);
+
+ /*
+ * The hw registers are double-buffered, and the IRQ fires essentially
+ * one period behind. The core only allows one period's distance, so we
+ * keep a private DMA buffer to afford two.
+ */
+ n64audio_write_reg(priv, AI_ADDR_REG, priv->ring_base_dma + priv->chan.nextpos);
+ barrier();
+ n64audio_write_reg(priv, AI_LEN_REG, count);
+
+ priv->chan.nextpos += count;
+ priv->chan.nextpos %= priv->chan.bufsize;
+
+ runtime->delay = runtime->period_size;
+}
+
+static irqreturn_t n64audio_isr(int irq, void *dev_id)
+{
+ struct n64audio *priv = dev_id;
+ const u32 intrs = n64mi_read_reg(priv, MI_INTR_REG);
+
+ // Check it's ours
+ if (!(intrs & MI_INTR_AI))
+ return IRQ_NONE;
+
+ n64audio_write_reg(priv, AI_STATUS_REG, 1);
+
+ if (priv->chan.substream && snd_pcm_running(priv->chan.substream)) {
+ scoped_guard(spinlock_irqsave, &priv->chan.lock) {
+ priv->chan.pos = priv->chan.nextpos;
+ }
+
+ snd_pcm_period_elapsed(priv->chan.substream);
+ if (priv->chan.substream && snd_pcm_running(priv->chan.substream))
+ n64audio_push(priv);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct snd_pcm_hardware n64audio_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER),
+ .formats = SNDRV_PCM_FMTBIT_S16_BE,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 32768,
+ .period_bytes_min = 1024,
+ .period_bytes_max = 32768,
+ .periods_min = 3,
+ // 3 periods lets the double-buffering hw read one buffer behind safely
+ .periods_max = 128,
+};
+
+static int hw_rule_period_size(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *c = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+ int changed = 0;
+
+ /*
+ * The DMA unit has errata on (start + len) & 0x3fff == 0x2000.
+ * This constraint makes sure that the period size is not a power of two,
+ * which combined with dma_alloc_coherent aligning the buffer to the largest
+ * PoT <= size guarantees it won't be hit.
+ */
+
+ if (is_power_of_2(c->min)) {
+ c->min += 2;
+ changed = 1;
+ }
+ if (is_power_of_2(c->max)) {
+ c->max -= 2;
+ changed = 1;
+ }
+ if (snd_interval_checkempty(c)) {
+ c->empty = 1;
+ return -EINVAL;
+ }
+
+ return changed;
+}
+
+static int n64audio_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ runtime->hw = n64audio_pcm_hw;
+ err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ hw_rule_period_size, NULL, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int n64audio_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct n64audio *priv = substream->pcm->private_data;
+ u32 rate;
+
+ rate = ((2 * AI_NTSC_DACRATE / runtime->rate) + 1) / 2 - 1;
+
+ n64audio_write_reg(priv, AI_RATE_REG, rate);
+
+ rate /= 66;
+ if (rate > 16)
+ rate = 16;
+ n64audio_write_reg(priv, AI_BITCLOCK_REG, rate - 1);
+
+ guard(spinlock_irq)(&priv->chan.lock);
+
+ /* Setup the pseudo-dma transfer pointers. */
+ priv->chan.pos = 0;
+ priv->chan.nextpos = 0;
+ priv->chan.substream = substream;
+ priv->chan.writesize = snd_pcm_lib_period_bytes(substream);
+ priv->chan.bufsize = snd_pcm_lib_buffer_bytes(substream);
+
+ return 0;
+}
+
+static int n64audio_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct n64audio *priv = substream->pcm->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ n64audio_push(substream->pcm->private_data);
+ n64audio_write_reg(priv, AI_CONTROL_REG, 1);
+ n64mi_write_reg(priv, MI_MASK_REG, MI_MASK_SET_AI);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ n64audio_write_reg(priv, AI_CONTROL_REG, 0);
+ n64mi_write_reg(priv, MI_MASK_REG, MI_MASK_CLR_AI);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static snd_pcm_uframes_t n64audio_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct n64audio *priv = substream->pcm->private_data;
+
+ return bytes_to_frames(substream->runtime,
+ priv->chan.pos);
+}
+
+static int n64audio_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct n64audio *priv = substream->pcm->private_data;
+
+ priv->chan.substream = NULL;
+
+ return 0;
+}
+
+static const struct snd_pcm_ops n64audio_pcm_ops = {
+ .open = n64audio_pcm_open,
+ .prepare = n64audio_pcm_prepare,
+ .trigger = n64audio_pcm_trigger,
+ .pointer = n64audio_pcm_pointer,
+ .close = n64audio_pcm_close,
+};
+
+/*
+ * The target device is embedded and RAM-constrained. We save RAM
+ * by initializing in __init code that gets dropped late in boot.
+ * For the same reason there is no module or unloading support.
+ */
+static int __init n64audio_probe(struct platform_device *pdev)
+{
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ struct n64audio *priv;
+ int err, irq;
+
+ err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1,
+ SNDRV_DEFAULT_STR1,
+ THIS_MODULE, sizeof(*priv), &card);
+ if (err < 0)
+ return err;
+
+ priv = card->private_data;
+
+ spin_lock_init(&priv->chan.lock);
+
+ priv->card = card;
+
+ priv->ring_base = dma_alloc_coherent(card->dev, 32 * 1024, &priv->ring_base_dma,
+ GFP_DMA|GFP_KERNEL);
+ if (!priv->ring_base) {
+ err = -ENOMEM;
+ goto fail_card;
+ }
+
+ priv->mi_reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->mi_reg_base)) {
+ err = PTR_ERR(priv->mi_reg_base);
+ goto fail_dma_alloc;
+ }
+
+ priv->ai_reg_base = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(priv->ai_reg_base)) {
+ err = PTR_ERR(priv->ai_reg_base);
+ goto fail_dma_alloc;
+ }
+
+ err = snd_pcm_new(card, "N64 Audio", 0, 1, 0, &pcm);
+ if (err < 0)
+ goto fail_dma_alloc;
+
+ pcm->private_data = priv;
+ strscpy(pcm->name, "N64 Audio");
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &n64audio_pcm_ops);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, card->dev, 0, 0);
+
+ strscpy(card->driver, "N64 Audio");
+ strscpy(card->shortname, "N64 Audio");
+ strscpy(card->longname, "N64 Audio");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ err = -EINVAL;
+ goto fail_dma_alloc;
+ }
+ if (devm_request_irq(&pdev->dev, irq, n64audio_isr,
+ IRQF_SHARED, "N64 Audio", priv)) {
+ err = -EBUSY;
+ goto fail_dma_alloc;
+ }
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto fail_dma_alloc;
+
+ return 0;
+
+fail_dma_alloc:
+ dma_free_coherent(card->dev, 32 * 1024, priv->ring_base, priv->ring_base_dma);
+
+fail_card:
+ snd_card_free(card);
+ return err;
+}
+
+static struct platform_driver n64audio_driver = {
+ .driver = {
+ .name = "n64audio",
+ },
+};
+
+static int __init n64audio_init(void)
+{
+ return platform_driver_probe(&n64audio_driver, n64audio_probe);
+}
+
+module_init(n64audio_init);
diff --git a/sound/oss/.gitignore b/sound/oss/.gitignore
index 7efb12b45502..ac678430408b 100644
--- a/sound/oss/.gitignore
+++ b/sound/oss/.gitignore
@@ -1,4 +1,3 @@
-#Ignore generated files
-maui_boot.h
+# SPDX-License-Identifier: GPL-2.0-only
pss_boot.h
trix_boot.h
diff --git a/sound/oss/CHANGELOG b/sound/oss/CHANGELOG
deleted file mode 100644
index 8706cd66ca1f..000000000000
--- a/sound/oss/CHANGELOG
+++ /dev/null
@@ -1,369 +0,0 @@
-Note these changes relate to Hannu's code and don't include the changes
-made outside of this for modularising the sound
-
-Changelog for version 3.8o
---------------------------
-
-Since 3.8h
-- Included support for OPL3-SA1 and SoftOSS
-
-Since 3.8
-- Fixed SNDCTL_DSP_GETOSPACE
-- Compatibility fixes for Linux 2.1.47
-
-Since 3.8-beta21
-- Fixed all known bugs (I think).
-
-Since 3.8-beta8
-- Lot of fixes to audio playback code in dmabuf.c
-
-Since 3.8-beta6
-- Fixed the famous Quake delay bug.
-
-Since 3.8-beta5
-- Fixed many bugs in audio playback.
-
-Since 3.8-beta4
-- Just minor changes.
-
-Since 3.8-beta1
-- Major rewrite of audio playback handling.
-- Added AWE32 support by Takashi Iwai (in ./lowlevel/).
-
-Since 3.7-beta#
-- Passing of ioctl() parameters between soundcard.c and other modules has been
-changed so that arg always points to kernel space.
-- Some bugfixes.
-
-Since 3.7-beta5
-- Disabled MIDI input with GUS PnP (Interwave). There seems to be constant
-stream of received 0x00 bytes when the MIDI receiver is enabled.
-
-Since 3.5
-- Changes almost everywhere.
-- Support for OPTi 82C924-based sound cards.
-
-Since 3.5.4-beta8
-- Fixed a bug in handling of non-fragment sized writes in 16 bit/stereo mode
- with GUS.
-- Limited minimum fragment size with some audio devices (GUS=512 and
- SB=32). These devices require more time to "recover" from processing
- of each fragment.
-
-Since 3.5.4-beta6/7
-- There seems to be problems in the OPTi 82C930 so cards based on this
- chip don't necessarily work yet. There are problems in detecting the
- MIDI interface. Also mixer volumes may be seriously wrong on some systems.
- You can safely use this driver version with C930 if it looks to work.
- However please don't complain if you have problems with it. C930 support
- should be fixed in future releases.
-- Got initialization of GUS PnP to work. With this version GUS PnP should
- work in GUS compatible mode after initialization using isapnptools.
-- Fixed a bug in handling of full duplex cards in write only mode. This has
- been causing "audio device opening" errors with RealAudio player.
-
-Since 3.5.4.beta5
-- Changes to OPTi 82C930 driver.
-- Major changes to the Soundscape driver. The driver requires now just one
- DMA channel. The extra audio/dsp device (the "Not functional" one) used
- for code download in the earlier versions has been eliminated. There is now
- just one /dev/dsp# device which is used both for code download and audio.
-
-Since 3.5.4.beta4
-- Minor changes.
-
-Since 3.5.4-beta2
-- Fixed silent playback with ESS 688/1688.
-- Got SB16 to work without the 16 bit DMA channel (only the 8 bit one
- is required for 8 and 16 bit modes).
-- Added the "lowlevel" subdirectory for additional low level drivers that
- are not part of USS core. See lowlevel/README for more info.
-- Included support for ACI mixer (by Markus Kuhn). ACI is a mixer used in
- miroPCM sound cards. See lowlevel/aci.readme for more info.
-- Support for Aztech Washington chipset (AZT2316 ASIC).
-
-Since 3.5.4-beta1
-- Reduced clicking with AD1848.
-- Support for OPTi 82C930. Only half duplex at this time. 16 bit playback
- is sometimes just white noise (occurs randomly).
-
-Since 3.5.2
-- Major changes to the SB/Jazz16/ESS driver (most parts rewritten).
- The most noticeable new feature is support for multiple SB cards at the same
- time.
-- Renamed sb16_midi.c to uart401.c. Also modified it to work also with
- other MPU401 UART compatible cards than SB16/ESS/Jazz.
-- Some changes which reduce clicking in audio playback.
-- Copying policy is now GPL.
-
-Since 3.5.1
-- TB Maui initialization support
-Since 3.5
-- Improved handling of playback underrun situations.
-
-Since 3.5-beta10
-- Bug fixing
-
-Since 3.5-beta9
-- Fixed for compatibility with Linux 1.3.70 and later.
-- Changed boot time passing of 16 bit DMA channel number to SB driver.
-
-Since 3.5-beta8
-- Minor changes
-
-Since 3.5-beta7
-- enhancements to configure program (by Jeff Tranter):
- - prompts are in same format as 1.3.x Linux kernel config program
- - on-line help for each question
- - fixed some compile warnings detected by gcc/g++ -Wall
- - minor grammatical changes to prompts
-
-Since 3.5-beta6
-- Fixed bugs in mmap() support.
-- Minor changes to Maui driver.
-
-Since 3.5-beta5
-- Fixed crash after recording with ESS688. It's generally a good
- idea to stop inbound DMA transfers before freeing the memory
- buffer.
-- Fixed handling of AD1845 codec (for example Shuttle Sound System).
-- Few other fixes.
-
-Since 3.5-beta4
-- Fixed bug in handling of uninitialized instruments with GUS.
-
-Since 3.5-beta3
-- Few changes which decrease popping at end/beginning of audio playback.
-
-Since 3.5-beta2
-- Removed MAD16+CS4231 hack made in previous version since it didn't
- help.
-- Fixed the above bug in proper way and in proper place. Many thanks
- to James Hightower.
-
-Since 3.5-beta1
-- Bug fixes.
-- Full duplex audio with MAD16+CS4231 may work now. The driver configures
- SB DMA of MAD16 so that it doesn't conflict with codec's DMA channels.
- The side effect is that all 8 bit DMA channels (0,1,3) are populated in
- duplex mode.
-
-Since 3.5-alpha9
-- Bug fixes (mostly in Jazz16 and ESS1688/688 supports).
-- Temporarily disabled recording with ESS1688/688 since it causes crash.
-- Changed audio buffer partitioning algorithm so that it selects
- smaller fragment size than earlier. This improves real time capabilities
- of the driver and makes recording to disk to work better. Unfortunately
- this change breaks some programs which assume that fragments cannot be
- shorter than 4096 bytes.
-
-Since 3.5-alpha8
-- Bug fixes
-
-Since 3.5-alpha7
-- Linux kernel compatible configuration (_EXPERIMENTAL_). Enable
- using command "cd /linux/drivers/sound;make script" and then
- just run kernel's make config normally.
-- Minor fixes to the SB support. Hopefully the driver works with
- all SB models now.
-- Added support for ESS ES1688 "AudioDrive" based cards.
-
-Since 3.5-alpha6
-- SB Pro and SB16 supports are no longer separately selectable options.
- Enabling SB enables them too.
-- Changed all #ifndef EXCLUDE_xx stuff to #ifdef CONFIG_xx. Modified
-configure to handle this.
-- Removed initialization messages from the
-modularized version. They can be enabled by using init_trace=1 in
-the insmod command line (insmod sound init_trace=1).
-- More AIX stuff.
-- Added support for synchronizing dsp/audio devices with /dev/sequencer.
-- mmap() support for dsp/audio devices.
-
-Since 3.5-alpha5
-- AIX port.
-- Changed some xxx_PATCH macros in soundcard.h to work with
- big endian machines.
-
-Since 3.5-alpha4
-- Removed the 'setfx' stuff from the version distributed with kernel
- sources. Running 'setfx' is required again.
-
-Since 3.5-alpha3
-- Moved stuff from the 'setfx' program to the AudioTrix Pro driver.
-
-Since 3.5-alpha2
-- Modifications to makefile and configure.c. Unnecessary sources
- are no longer compiled. Newly created local.h is also copied to
- /etc/soundconf. "make oldconfig" reads /etc/soundconf and produces
- new local.h which is compatible with current version of the driver.
-- Some fixes to the SB16 support.
-- Fixed random protection fault in gus_wave.c
-
-Since 3.5-alpha1
-- Modified to work with Linux-1.3.33 and later
-- Some minor changes
-
-Since 3.0.2
-- Support for CS4232 based PnP cards (AcerMagic S23 etc).
-- Full duplex support for some CS4231, CS4232 and AD1845 based cards
-(GUS MAX, AudioTrix Pro, AcerMagic S23 and many MAD16/Mozart cards
-having a codec mentioned above).
-- Almost fully rewritten loadable modules support.
-- Fixed some bugs.
-- Huge amount of testing (more testing is still required).
-- mmap() support (works with some cards). Requires much more testing.
-- Sample/patch/program loading for TB Maui/Tropez. No initialization
-since TB doesn't allow me to release that code.
-- Using CS4231 compatible codecs as timer for /dev/music.
-
-Since 3.0.1
-- Added allocation of I/O ports, DMA channels and interrupts
-to the initialization code. This may break modules support since
-the driver may not free some resources on unload. Should be fixed soon.
-
-Since 3.0
-- Some important bug fixes.
-- select() for /dev/dsp and /dev/audio (Linux only).
-(To use select() with read, you have to call read() to start
-the recording. Calling write() kills recording immediately so
-use select() carefully when you are writing a half duplex app.
-Full duplex mode is not implemented yet.) Select works also with
-/dev/sequencer and /dev/music. Maybe with /dev/midi## too.
-
-Since 3.0-beta2
-- Minor fixes.
-- Added Readme.cards
-
-Since 3.0-beta1
-- Minor fixes to the modules support.
-- Eliminated call to sb_free_irq() in ad1848.c
-- Rewritten MAD16&Mozart support (not tested with MAD16 Pro).
-- Fix to DMA initialization of PSS cards.
-- Some fixes to ad1848/cs42xx mixer support (GUS MAX, MSS, etc.)
-- Fixed some bugs in the PSS driver which caused I/O errors with
- the MSS mode (/dev/dsp).
-
-Since 3.0-950506
-- Recording with GUS MAX fixed. It works when the driver is configured
- to use two DMA channels with GUS MAX (16 bit ones recommended).
-
-Since 3.0-94xxxx
-- Too many changes
-
-Since 3.0-940818
-- Fixes for Linux 1.1.4x.
-- Disables Disney Sound System with SG NX Pro 16 (less noise).
-
-Since 2.90-2
-- Fixes to soundcard.h
-- Non blocking mode to /dev/sequencer
-- Experimental detection code for Ensoniq Soundscape.
-
-Since 2.90
-- Minor and major bug fixes
-
-Since pre-3.0-940712
-- GUS MAX support
-- Partially working MSS/WSS support (could work with some cards).
-- Hardware u-Law and A-Law support with AD1848/CS4248 and CS4231 codecs
- (GUS MAX, GUS16, WSS etc). Hardware ADPCM is possible with GUS16 and
- GUS MAX, but it doesn't work yet.
-Since pre-3.0-940426
-- AD1848/CS4248/CS4231 codec support (MSS, GUS MAX, Aztec, Orchid etc).
-This codec chip is used in various sound cards. This version is developed
-for the 16 bit daughtercard of GUS. It should work with other cards also
-if the following requirements are met:
- - The I/O, IRQ and DMA settings are jumper selectable or
- the card is initialized by booting DOS before booting Linux (etc.).
- - You add the IO, IRQ and DMA settings manually to the local.h.
- (Just define GUS16_BASE, GUS16_IRQ and GUS16_DMA). Note that
- the base address bust be the base address of the codec chip not the
- card itself. For the GUS16 these are the same but most MSS compatible
- cards have the codec located at card_base+4.
-- Some minor changes
-
-Since 2.5 (******* MAJOR REWRITE ***********)
-
-This version is based on v2.3. I have tried to maintain two versions
-together so that this one should have the same features than v2.5.
-Something may still be missing. If you notice such things, please let me
-know.
-
-The Readme.v30 contains more details.
-
-- /dev/midi## devices.
-- /dev/sequencer2
-
-Since 2.5-beta2
-- Some fine tuning to the GUS v3.7 mixer code.
-- Fixed speed limits for the plain SB (1.0 to 2.0).
-
-Since 2.5-beta
-- Fixed OPL-3 detection with SB. Caused problems with PAS16.
-- GUS v3.7 mixer support.
-
-Since 2.4
-- Mixer support for Sound Galaxy NX Pro (define __SGNXPRO__ on your local.h).
-- Fixed truncated sound on /dev/dsp when the device is closed.
-- Linear volume mode for GUS
-- Pitch bends larger than +/- 2 octaves.
-- MIDI recording for SB and SB Pro. (Untested).
-- Some other fixes.
-- SB16 MIDI and DSP drivers only initialized if SB16 actually installed.
-- Implemented better detection for OPL-3. This should be useful if you
- have an old SB Pro (the non-OPL-3 one) or a SB 2.0 clone which has a OPL-3.
-- SVR4.2 support by Ian Hartas. Initial ALPHA TEST version (untested).
-
-Since 2.3b
-- Fixed bug which made it impossible to make long recordings to disk.
- Recording was not restarted after a buffer overflow situation.
-- Limited mixer support for GUS.
-- Numerous improvements to the GUS driver by Andrew Robinson. Including
- some click removal etc.
-
-Since 2.3
-- Fixed some minor bugs in the SB16 driver.
-
-Since 2.2b
-- Full SB16 DSP support. 8/16 bit, mono/stereo
-- The SCO and FreeBSD versions should be in sync now. There are some
- problems with SB16 and GUS in the FreeBSD versions.
- The DMA buffer allocation of the SCO version has been polished but
- there could still be some problems. At least it hogs memory.
- The DMA channel
- configuration method used in the SCO/System is a hack.
-- Support for the MPU emulation of the SB16.
-- Some big arrays are now allocated boot time. This makes the BSS segment
- smaller which makes it possible to use the full driver with
- NetBSD. These arrays are not allocated if no suitable sound card is available.
-- Fixed a bug in the compute_and_set_volume in gus_wave.c
-- Fixed the too fast mono playback problem of SB Pro and PAS16.
-
-Since 2.2
-- Stereo recording for SB Pro. Somehow it was missing and nobody
- had noticed it earlier.
-- Minor polishing.
-- Interpreting of boot time arguments (sound=) for Linux.
-- Breakup of sb_dsp.c. Parts of the code has been moved to
- sb_mixer.c and sb_midi.c
-
-Since 2.1
-- Preliminary support for SB16.
- - The SB16 mixer is supported in its native mode.
- - Digitized voice capability up to 44.1 kHz/8 bit/mono
- (16 bit and stereo support coming in the next release).
-- Fixed some bugs in the digitized voice driver for PAS16.
-- Proper initialization of the SB emulation of latest PAS16 models.
-
-- Significantly improved /dev/dsp and /dev/audio support.
- - Now supports half duplex mode. It's now possible to record and
- playback without closing and reopening the device.
- - It's possible to use smaller buffers than earlier. There is a new
- ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &n) where n should be 1, 2 or 4.
- This call instructs the driver to use smaller buffers. The default
- buffer size (0.5 to 1.0 seconds) is divided by n. Should be called
- immediately after opening the device.
-
-Since 2.0
-Just cosmetic changes.
diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig
deleted file mode 100644
index 76c090218073..000000000000
--- a/sound/oss/Kconfig
+++ /dev/null
@@ -1,547 +0,0 @@
-# 18 Apr 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net>
-# More hacking for modularisation.
-#
-# Prompt user for primary drivers.
-
-config SOUND_BCM_CS4297A
- tristate "Crystal Sound CS4297a (for Swarm)"
- depends on SIBYTE_SWARM
- help
- The BCM91250A has a Crystal CS4297a on synchronous serial
- port B (in addition to the DB-9 serial port). Say Y or M
- here to enable the sound chip instead of the UART. Also
- note that CONFIG_KGDB should not be enabled at the same
- time, since it also attempts to use this UART port.
-
-config SOUND_VWSND
- tristate "SGI Visual Workstation Sound"
- depends on X86_VISWS
- help
- Say Y or M if you have an SGI Visual Workstation and you want to be
- able to use its on-board audio. Read
- <file:Documentation/sound/oss/vwsnd> for more info on this driver's
- capabilities.
-
-config SOUND_AU1550_AC97
- tristate "Au1550/Au1200 AC97 Sound"
- depends on SOC_AU1550 || SOC_AU1200
-
-config SOUND_MSNDCLAS
- tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey"
- depends on (m || !STANDALONE) && ISA
- help
- Say M here if you have a Turtle Beach MultiSound Classic, Tahiti or
- Monterey (not for the Pinnacle or Fiji).
-
- See <file:Documentation/sound/oss/MultiSound> for important information
- about this driver. Note that it has been discontinued, but the
- Voyetra Turtle Beach knowledge base entry for it is still available
- at <http://www.turtlebeach.com/site/kb_ftp/790.asp>.
-
-comment "Compiled-in MSND Classic support requires firmware during compilation."
- depends on SOUND_PRIME && SOUND_MSNDCLAS=y
-
-config MSNDCLAS_HAVE_BOOT
- bool
- depends on SOUND_MSNDCLAS=y && !STANDALONE
- default y
-
-config MSNDCLAS_INIT_FILE
- string "Full pathname of MSNDINIT.BIN firmware file"
- depends on SOUND_MSNDCLAS
- default "/etc/sound/msndinit.bin"
- help
- The MultiSound cards have two firmware files which are required for
- operation, and are not currently included. These files can be
- obtained from Turtle Beach. See
- <file:Documentation/sound/oss/MultiSound> for information on how to
- obtain this.
-
-config MSNDCLAS_PERM_FILE
- string "Full pathname of MSNDPERM.BIN firmware file"
- depends on SOUND_MSNDCLAS
- default "/etc/sound/msndperm.bin"
- help
- The MultiSound cards have two firmware files which are required for
- operation, and are not currently included. These files can be
- obtained from Turtle Beach. See
- <file:Documentation/sound/oss/MultiSound> for information on how to
- obtain this.
-
-config MSNDCLAS_IRQ
- int "MSND Classic IRQ 5, 7, 9, 10, 11, 12"
- depends on SOUND_MSNDCLAS=y
- default "5"
- help
- Interrupt Request line for the MultiSound Classic and related cards.
-
-config MSNDCLAS_MEM
- hex "MSND Classic memory B0000, C8000, D0000, D8000, E0000, E8000"
- depends on SOUND_MSNDCLAS=y
- default "D0000"
- help
- Memory-mapped I/O base address for the MultiSound Classic and
- related cards.
-
-config MSNDCLAS_IO
- hex "MSND Classic I/O 210, 220, 230, 240, 250, 260, 290, 3E0"
- depends on SOUND_MSNDCLAS=y
- default "290"
- help
- I/O port address for the MultiSound Classic and related cards.
-
-config SOUND_MSNDPIN
- tristate "Support for Turtle Beach MultiSound Pinnacle, Fiji"
- depends on (m || !STANDALONE) && ISA
- help
- Say M here if you have a Turtle Beach MultiSound Pinnacle or Fiji.
- See <file:Documentation/sound/oss/MultiSound> for important information
- about this driver. Note that it has been discontinued, but the
- Voyetra Turtle Beach knowledge base entry for it is still available
- at <http://www.turtlebeach.com/site/kb_ftp/600.asp>.
-
-comment "Compiled-in MSND Pinnacle support requires firmware during compilation."
- depends on SOUND_PRIME && SOUND_MSNDPIN=y
-
-config MSNDPIN_HAVE_BOOT
- bool
- depends on SOUND_MSNDPIN=y
- default y
-
-config MSNDPIN_INIT_FILE
- string "Full pathname of PNDSPINI.BIN firmware file"
- depends on SOUND_MSNDPIN
- default "/etc/sound/pndspini.bin"
- help
- The MultiSound cards have two firmware files which are required
- for operation, and are not currently included. These files can be
- obtained from Turtle Beach. See
- <file:Documentation/sound/oss/MultiSound> for information on how to
- obtain this.
-
-config MSNDPIN_PERM_FILE
- string "Full pathname of PNDSPERM.BIN firmware file"
- depends on SOUND_MSNDPIN
- default "/etc/sound/pndsperm.bin"
- help
- The MultiSound cards have two firmware files which are required for
- operation, and are not currently included. These files can be
- obtained from Turtle Beach. See
- <file:Documentation/sound/oss/MultiSound> for information on how to
- obtain this.
-
-config MSNDPIN_IRQ
- int "MSND Pinnacle IRQ 5, 7, 9, 10, 11, 12"
- depends on SOUND_MSNDPIN=y
- default "5"
- help
- Interrupt request line for the primary synthesizer on MultiSound
- Pinnacle and Fiji sound cards.
-
-config MSNDPIN_MEM
- hex "MSND Pinnacle memory B0000, C8000, D0000, D8000, E0000, E8000"
- depends on SOUND_MSNDPIN=y
- default "D0000"
- help
- Memory-mapped I/O base address for the primary synthesizer on
- MultiSound Pinnacle and Fiji sound cards.
-
-config MSNDPIN_IO
- hex "MSND Pinnacle I/O 210, 220, 230, 240, 250, 260, 290, 3E0"
- depends on SOUND_MSNDPIN=y
- default "290"
- help
- Memory-mapped I/O base address for the primary synthesizer on
- MultiSound Pinnacle and Fiji sound cards.
-
-config MSNDPIN_DIGITAL
- bool "MSND Pinnacle has S/PDIF I/O"
- depends on SOUND_MSNDPIN=y
- help
- If you have the S/PDIF daughter board for the Pinnacle or Fiji,
- answer Y here; otherwise, say N. If you have this, you will be able
- to play and record from the S/PDIF port (digital signal). See
- <file:Documentation/sound/oss/MultiSound> for information on how to make
- use of this capability.
-
-config MSNDPIN_NONPNP
- bool "MSND Pinnacle non-PnP Mode"
- depends on SOUND_MSNDPIN=y
- help
- The Pinnacle and Fiji card resources can be configured either with
- PnP, or through a configuration port. Say Y here if your card is NOT
- in PnP mode. For the Pinnacle, configuration in non-PnP mode allows
- use of the IDE and joystick peripherals on the card as well; these
- do not show up when the card is in PnP mode. Specifying zero for any
- resource of a device will disable the device. If you are running the
- card in PnP mode, you must say N here and use isapnptools to
- configure the card's resources.
-
-comment "MSND Pinnacle DSP section will be configured to above parameters."
- depends on SOUND_MSNDPIN=y && MSNDPIN_NONPNP
-
-config MSNDPIN_CFG
- hex "MSND Pinnacle config port 250,260,270"
- depends on MSNDPIN_NONPNP
- default "250"
- help
- This is the port which the Pinnacle and Fiji uses to configure the
- card's resources when not in PnP mode. If your card is in PnP mode,
- then be sure to say N to the previous option, "MSND Pinnacle Non-PnP
- Mode".
-
-comment "Pinnacle-specific Device Configuration (0 disables)"
- depends on SOUND_MSNDPIN=y && MSNDPIN_NONPNP
-
-config MSNDPIN_MPU_IO
- hex "MSND Pinnacle MPU I/O (e.g. 330)"
- depends on MSNDPIN_NONPNP
- default "0"
- help
- Memory-mapped I/O base address for the Kurzweil daughterboard
- synthesizer on MultiSound Pinnacle and Fiji sound cards.
-
-config MSNDPIN_MPU_IRQ
- int "MSND Pinnacle MPU IRQ (e.g. 9)"
- depends on MSNDPIN_NONPNP
- default "0"
- help
- Interrupt request number for the Kurzweil daughterboard
- synthesizer on MultiSound Pinnacle and Fiji sound cards.
-
-config MSNDPIN_IDE_IO0
- hex "MSND Pinnacle IDE I/O 0 (e.g. 170)"
- depends on MSNDPIN_NONPNP
- default "0"
- help
- CD-ROM drive 0 memory-mapped I/O base address for the MultiSound
- Pinnacle and Fiji sound cards.
-
-config MSNDPIN_IDE_IO1
- hex "MSND Pinnacle IDE I/O 1 (e.g. 376)"
- depends on MSNDPIN_NONPNP
- default "0"
- help
- CD-ROM drive 1 memory-mapped I/O base address for the MultiSound
- Pinnacle and Fiji sound cards.
-
-config MSNDPIN_IDE_IRQ
- int "MSND Pinnacle IDE IRQ (e.g. 15)"
- depends on MSNDPIN_NONPNP
- default "0"
- help
- Interrupt request number for the IDE CD-ROM interface on the
- MultiSound Pinnacle and Fiji sound cards.
-
-config MSNDPIN_JOYSTICK_IO
- hex "MSND Pinnacle joystick I/O (e.g. 200)"
- depends on MSNDPIN_NONPNP
- default "0"
- help
- Memory-mapped I/O base address for the joystick port on MultiSound
- Pinnacle and Fiji sound cards.
-
-config MSND_FIFOSIZE
- int "MSND buffer size (kB)"
- depends on SOUND_MSNDPIN=y || SOUND_MSNDCLAS=y
- default "128"
- help
- Configures the size of each audio buffer, in kilobytes, for
- recording and playing in the MultiSound drivers (both the Classic
- and Pinnacle). Larger values reduce the chance of data overruns at
- the expense of overall latency. If unsure, use the default.
-
-menuconfig SOUND_OSS
- tristate "OSS sound modules"
- depends on ISA_DMA_API && VIRT_TO_BUS
- help
- OSS is the Open Sound System suite of sound card drivers. They make
- sound programming easier since they provide a common API. Say Y or
- M here (the module will be called sound) if you haven't found a
- driver for your sound card above, then pick your driver from the
- list below.
-
-if SOUND_OSS
-
-config SOUND_TRACEINIT
- bool "Verbose initialisation"
- help
- Verbose soundcard initialization -- affects the format of autoprobe
- and initialization messages at boot time.
-
-config SOUND_DMAP
- bool "Persistent DMA buffers"
- ---help---
- Linux can often have problems allocating DMA buffers for ISA sound
- cards on machines with more than 16MB of RAM. This is because ISA
- DMA buffers must exist below the 16MB boundary and it is quite
- possible that a large enough free block in this region cannot be
- found after the machine has been running for a while. If you say Y
- here the DMA buffers (64Kb) will be allocated at boot time and kept
- until the shutdown. This option is only useful if you said Y to
- "OSS sound modules", above. If you said M to "OSS sound modules"
- then you can get the persistent DMA buffer functionality by passing
- the command-line argument "dmabuf=1" to the sound module.
-
- Say Y unless you have 16MB or more RAM or a PCI sound card.
-
-config SOUND_VMIDI
- tristate "Loopback MIDI device support"
- help
- Support for MIDI loopback on port 1 or 2.
-
-config SOUND_TRIX
- tristate "MediaTrix AudioTrix Pro support"
- help
- Answer Y if you have the AudioTriX Pro sound card manufactured
- by MediaTrix.
-
-config TRIX_HAVE_BOOT
- bool "Have TRXPRO.HEX firmware file"
- depends on SOUND_TRIX=y && !STANDALONE
- help
- The MediaTrix AudioTrix Pro has an on-board microcontroller which
- needs to be initialized by downloading the code from the file
- TRXPRO.HEX in the DOS driver directory. If you don't have the
- TRXPRO.HEX file handy you may skip this step. However, the SB and
- MPU-401 modes of AudioTrix Pro will not work without this file!
-
-config TRIX_BOOT_FILE
- string "Full pathname of TRXPRO.HEX firmware file"
- depends on TRIX_HAVE_BOOT
- default "/etc/sound/trxpro.hex"
- help
- Enter the full pathname of your TRXPRO.HEX file, starting from /.
-
-config SOUND_MSS
- tristate "Microsoft Sound System support"
- ---help---
- Again think carefully before answering Y to this question. It's
- safe to answer Y if you have the original Windows Sound System card
- made by Microsoft or Aztech SG 16 Pro (or NX16 Pro). Also you may
- say Y in case your card is NOT among these:
-
- ATI Stereo F/X, AdLib, Audio Excell DSP16, Cardinal DSP16,
- Ensoniq SoundScape (and compatibles made by Reveal and Spea),
- Gravis Ultrasound, Gravis Ultrasound ACE, Gravis Ultrasound Max,
- Gravis Ultrasound with 16 bit option, Logitech Sound Man 16,
- Logitech SoundMan Games, Logitech SoundMan Wave, MAD16 Pro (OPTi
- 82C929), Media Vision Jazz16, MediaTriX AudioTriX Pro, Microsoft
- Windows Sound System (MSS/WSS), Mozart (OAK OTI-601), Orchid
- SW32, Personal Sound System (PSS), Pro Audio Spectrum 16, Pro
- Audio Studio 16, Pro Sonic 16, Roland MPU-401 MIDI interface,
- Sound Blaster 1.0, Sound Blaster 16, Sound Blaster 16ASP, Sound
- Blaster 2.0, Sound Blaster AWE32, Sound Blaster Pro, TI TM4000M
- notebook, ThunderBoard, Turtle Beach Tropez, Yamaha FM
- synthesizers (OPL2, OPL3 and OPL4), 6850 UART MIDI Interface.
-
- For cards having native support in VoxWare, consult the card
- specific instructions in <file:Documentation/sound/oss/README.OSS>.
- Some drivers have their own MSS support and saying Y to this option
- will cause a conflict.
-
- If you compile the driver into the kernel, you have to add
- "ad1848=<io>,<irq>,<dma>,<dma2>[,<type>]" to the kernel command
- line.
-
-config SOUND_MPU401
- tristate "MPU-401 support (NOT for SB16)"
- ---help---
- Be careful with this question. The MPU401 interface is supported by
- all sound cards. However, some natively supported cards have their
- own driver for MPU401. Enabling this MPU401 option with these cards
- will cause a conflict. Also, enabling MPU401 on a system that
- doesn't really have a MPU401 could cause some trouble. If your card
- was in the list of supported cards, look at the card specific
- instructions in the <file:Documentation/sound/oss/README.OSS> file. It
- is safe to answer Y if you have a true MPU401 MIDI interface card.
-
- If you compile the driver into the kernel, you have to add
- "mpu401=<io>,<irq>" to the kernel command line.
-
-config SOUND_PAS
- tristate "ProAudioSpectrum 16 support"
- ---help---
- Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio
- 16 or Logitech SoundMan 16 sound card. Answer N if you have some
- other card made by Media Vision or Logitech since those are not
- PAS16 compatible. Please read <file:Documentation/sound/oss/PAS16>.
- It is not necessary to add Sound Blaster support separately; it
- is included in PAS support.
-
- If you compile the driver into the kernel, you have to add
- "pas2=<io>,<irq>,<dma>,<dma2>,<sbio>,<sbirq>,<sbdma>,<sbdma2>
- to the kernel command line.
-
-config PAS_JOYSTICK
- bool "Enable PAS16 joystick port"
- depends on SOUND_PAS=y
- help
- Say Y here to enable the Pro Audio Spectrum 16's auxiliary joystick
- port.
-
-config SOUND_PSS
- tristate "PSS (AD1848, ADSP-2115, ESC614) support"
- help
- Answer Y or M if you have an Orchid SW32, Cardinal DSP16, Beethoven
- ADSP-16 or some other card based on the PSS chipset (AD1848 codec +
- ADSP-2115 DSP chip + Echo ESC614 ASIC CHIP). For more information on
- how to compile it into the kernel or as a module see the file
- <file:Documentation/sound/oss/PSS>.
-
- If you compile the driver into the kernel, you have to add
- "pss=<io>,<mssio>,<mssirq>,<mssdma>,<mpuio>,<mpuirq>" to the kernel
- command line.
-
-config PSS_MIXER
- bool "Enable PSS mixer (Beethoven ADSP-16 and other compatible)"
- depends on SOUND_PSS
- help
- Answer Y for Beethoven ADSP-16. You may try to say Y also for other
- cards if they have master volume, bass, treble, and you can't
- control it under Linux. If you answer N for Beethoven ADSP-16, you
- can't control master volume, bass, treble and synth volume.
-
- If you said M to "PSS support" above, you may enable or disable this
- PSS mixer with the module parameter pss_mixer. For more information
- see the file <file:Documentation/sound/oss/PSS>.
-
-config PSS_HAVE_BOOT
- bool "Have DSPxxx.LD firmware file"
- depends on SOUND_PSS && !STANDALONE
- help
- If you have the DSPxxx.LD file or SYNTH.LD file for you card, say Y
- to include this file. Without this file the synth device (OPL) may
- not work.
-
-config PSS_BOOT_FILE
- string "Full pathname of DSPxxx.LD firmware file"
- depends on PSS_HAVE_BOOT
- default "/etc/sound/dsp001.ld"
- help
- Enter the full pathname of your DSPxxx.LD file or SYNTH.LD file,
- starting from /.
-
-config SOUND_SB
- tristate "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support"
- ---help---
- Answer Y if you have an original Sound Blaster card made by Creative
- Labs or a 100% hardware compatible clone (like the Thunderboard or
- SM Games). For an unknown card you may answer Y if the card claims
- to be Sound Blaster-compatible.
-
- Please read the file <file:Documentation/sound/oss/Soundblaster>.
-
- You should also say Y here for cards based on the Avance Logic
- ALS-007 and ALS-1X0 chips (read <file:Documentation/sound/oss/ALS>) and
- for cards based on ESS chips (read
- <file:Documentation/sound/oss/ESS1868> and
- <file:Documentation/sound/oss/ESS>). If you have an SB AWE 32 or SB AWE
- 64, say Y here and also to "AWE32 synth" below and read
- <file:Documentation/sound/oss/INSTALL.awe>. If you have an IBM Mwave
- card, say Y here and read <file:Documentation/sound/oss/mwave>.
-
- If you compile the driver into the kernel and don't want to use
- isapnp, you have to add "sb=<io>,<irq>,<dma>,<dma2>" to the kernel
- command line.
-
- You can say M here to compile this driver as a module; the module is
- called sb.
-
-config SOUND_YM3812
- tristate "Yamaha FM synthesizer (YM3812/OPL-3) support"
- ---help---
- Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4).
- Answering Y is usually a safe and recommended choice, however some
- cards may have software (TSR) FM emulation. Enabling FM support with
- these cards may cause trouble (I don't currently know of any such
- cards, however). Please read the file
- <file:Documentation/sound/oss/OPL3> if your card has an OPL3 chip.
-
- If you compile the driver into the kernel, you have to add
- "opl3=<io>" to the kernel command line.
-
- If unsure, say Y.
-
-config SOUND_UART6850
- tristate "6850 UART support"
- help
- This option enables support for MIDI interfaces based on the 6850
- UART chip. This interface is rarely found on sound cards. It's safe
- to answer N to this question.
-
- If you compile the driver into the kernel, you have to add
- "uart6850=<io>,<irq>" to the kernel command line.
-
-config SOUND_AEDSP16
- tristate "Gallant Audio Cards (SC-6000 and SC-6600 based)"
- ---help---
- Answer Y if you have a Gallant's Audio Excel DSP 16 card. This
- driver supports Audio Excel DSP 16 but not the III nor PnP versions
- of this card.
-
- The Gallant's Audio Excel DSP 16 card can emulate either an SBPro or
- a Microsoft Sound System card, so you should have said Y to either
- "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support"
- or "Microsoft Sound System support", above, and you need to answer
- the "MSS emulation" and "SBPro emulation" questions below
- accordingly. You should say Y to one and only one of these two
- questions.
-
- Read the <file:Documentation/sound/oss/README.OSS> file and the head of
- <file:sound/oss/aedsp16.c> as well as
- <file:Documentation/sound/oss/AudioExcelDSP16> to get more information
- about this driver and its configuration.
-
-config SC6600
- bool "SC-6600 based audio cards (new Audio Excel DSP 16)"
- depends on SOUND_AEDSP16
- help
- The SC6600 is the new version of DSP mounted on the Audio Excel DSP
- 16 cards. Find in the manual the FCC ID of your audio card and
- answer Y if you have an SC6600 DSP.
-
-config SC6600_JOY
- bool "Activate SC-6600 Joystick Interface"
- depends on SC6600
- help
- Say Y here in order to use the joystick interface of the Audio Excel
- DSP 16 card.
-
-config SC6600_CDROM
- int "SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)"
- depends on SC6600
- default "4"
- help
- This is used to activate the CD-ROM interface of the Audio Excel
- DSP 16 card. Enter: 0 for Sony, 1 for Panasonic, 2 for IDE, 4 for no
- CD-ROM present.
-
-config SC6600_CDROMBASE
- hex "SC-6600 CDROM Interface I/O Address"
- depends on SC6600
- default "0"
- help
- Base I/O port address for the CD-ROM interface of the Audio Excel
- DSP 16 card.
-
-config SOUND_VIDC
- tristate "VIDC 16-bit sound"
- depends on ARM && (ARCH_ACORN || ARCH_CLPS7500)
- help
- 16-bit support for the VIDC onboard sound hardware found on Acorn
- machines.
-
-config SOUND_WAVEARTIST
- tristate "Netwinder WaveArtist"
- depends on ARM && ARCH_NETWINDER
- help
- Say Y here to include support for the Rockwell WaveArtist sound
- system. This driver is mainly for the NetWinder.
-
-config SOUND_KAHLUA
- tristate "XpressAudio Sound Blaster emulation"
- depends on SOUND_SB
-
-endif # SOUND_OSS
-
diff --git a/sound/oss/Makefile b/sound/oss/Makefile
deleted file mode 100644
index 96f14dcd0cd1..000000000000
--- a/sound/oss/Makefile
+++ /dev/null
@@ -1,109 +0,0 @@
-# Makefile for the Linux sound card driver
-#
-# 18 Apr 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net>
-# Rewritten to use lists instead of if-statements.
-
-# Each configuration option enables a list of files.
-
-obj-$(CONFIG_SOUND_OSS) += sound.o
-
-# Please leave it as is, cause the link order is significant !
-
-obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o
-obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o
-obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o
-obj-$(CONFIG_SOUND_MSS) += ad1848.o
-obj-$(CONFIG_SOUND_PAS) += pas2.o sb.o sb_lib.o uart401.o
-obj-$(CONFIG_SOUND_SB) += sb.o sb_lib.o uart401.o
-obj-$(CONFIG_SOUND_KAHLUA) += kahlua.o
-obj-$(CONFIG_SOUND_MPU401) += mpu401.o
-obj-$(CONFIG_SOUND_UART6850) += uart6850.o
-obj-$(CONFIG_SOUND_YM3812) += opl3.o
-obj-$(CONFIG_SOUND_VMIDI) += v_midi.o
-obj-$(CONFIG_SOUND_VIDC) += vidc_mod.o
-obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o
-obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o
-obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o
-obj-$(CONFIG_SOUND_VWSND) += vwsnd.o
-obj-$(CONFIG_SOUND_AU1550_AC97) += au1550_ac97.o ac97_codec.o
-obj-$(CONFIG_SOUND_BCM_CS4297A) += swarm_cs4297a.o
-
-obj-$(CONFIG_DMASOUND) += dmasound/
-
-# Declare multi-part drivers.
-
-sound-objs := \
- dev_table.o soundcard.o \
- audio.o dmabuf.o \
- midi_synth.o midibuf.o \
- sequencer.o sound_timer.o sys_timer.o
-
-pas2-objs := pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o
-sb-objs := sb_card.o
-sb_lib-objs := sb_common.o sb_audio.o sb_midi.o sb_mixer.o sb_ess.o
-vidc_mod-objs := vidc.o vidc_fill.o
-
-hostprogs-y := bin2hex hex2hex
-
-# Files generated that shall be removed upon make clean
-clean-files := msndperm.c msndinit.c pndsperm.c pndspini.c \
- pss_boot.h trix_boot.h
-
-# Firmware files that need translation
-#
-# The translated files are protected by a file that keeps track
-# of what name was used to build them. If the name changes, they
-# will be forced to be remade.
-#
-
-# Turtle Beach MultiSound
-
-ifeq ($(CONFIG_MSNDCLAS_HAVE_BOOT),y)
- $(obj)/msnd_classic.o: $(obj)/msndperm.c $(obj)/msndinit.c
-
- $(obj)/msndperm.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_PERM_FILE)) $(obj)/bin2hex
- $(obj)/bin2hex msndperm < $< > $@
-
- $(obj)/msndinit.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_INIT_FILE)) $(obj)/bin2hex
- $(obj)/bin2hex msndinit < $< > $@
-endif
-
-ifeq ($(CONFIG_MSNDPIN_HAVE_BOOT),y)
- $(obj)/msnd_pinnacle.o: $(obj)/pndsperm.c $(obj)/pndspini.c
-
- $(obj)/pndsperm.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_PERM_FILE)) $(obj)/bin2hex
- $(obj)/bin2hex pndsperm < $< > $@
-
- $(obj)/pndspini.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_INIT_FILE)) $(obj)/bin2hex
- $(obj)/bin2hex pndspini < $< > $@
-endif
-
-# PSS (ECHO-ADI2111)
-
-$(obj)/pss.o: $(obj)/pss_boot.h
-
-ifeq ($(CONFIG_PSS_HAVE_BOOT),y)
- $(obj)/pss_boot.h: $(patsubst "%", %, $(CONFIG_PSS_BOOT_FILE)) $(obj)/bin2hex
- $(obj)/bin2hex pss_synth < $< > $@
-else
- $(obj)/pss_boot.h:
- ( \
- echo 'static unsigned char * pss_synth = NULL;'; \
- echo 'static int pss_synthLen = 0;'; \
- ) > $@
-endif
-
-# MediaTrix AudioTrix Pro
-
-$(obj)/trix.o: $(obj)/trix_boot.h
-
-ifeq ($(CONFIG_TRIX_HAVE_BOOT),y)
- $(obj)/trix_boot.h: $(patsubst "%", %, $(CONFIG_TRIX_BOOT_FILE)) $(obj)/hex2hex
- $(obj)/hex2hex -i trix_boot < $< > $@
-else
- $(obj)/trix_boot.h:
- ( \
- echo 'static unsigned char * trix_boot = NULL;'; \
- echo 'static int trix_boot_len = 0;'; \
- ) > $@
-endif
diff --git a/sound/oss/README.FIRST b/sound/oss/README.FIRST
deleted file mode 100644
index 90fdcf063d2d..000000000000
--- a/sound/oss/README.FIRST
+++ /dev/null
@@ -1,6 +0,0 @@
-The modular sound driver patches were funded by Red Hat Software
-(www.redhat.com). The sound driver here is thus a modified version of
-Hannu's code. Please bear that in mind when considering the appropriate
-forums for bug reporting.
-
-Alan Cox
diff --git a/sound/oss/ac97_codec.c b/sound/oss/ac97_codec.c
deleted file mode 100644
index 854c303264dc..000000000000
--- a/sound/oss/ac97_codec.c
+++ /dev/null
@@ -1,1203 +0,0 @@
-/*
- * ac97_codec.c: Generic AC97 mixer/modem module
- *
- * Derived from ac97 mixer in maestro and trident driver.
- *
- * Copyright 2000 Silicon Integrated System Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- **************************************************************************
- *
- * The Intel Audio Codec '97 specification is available at:
- * http://download.intel.com/support/motherboards/desktop/sb/ac97_r23.pdf
- *
- **************************************************************************
- *
- * History
- * May 02, 2003 Liam Girdwood <lrg@slimlogic.co.uk>
- * Removed non existant WM9700
- * Added support for WM9705, WM9708, WM9709, WM9710, WM9711
- * WM9712 and WM9717
- * Mar 28, 2002 Randolph Bentson <bentson@holmsjoen.com>
- * corrections to support WM9707 in ViewPad 1000
- * v0.4 Mar 15 2000 Ollie Lho
- * dual codecs support verified with 4 channels output
- * v0.3 Feb 22 2000 Ollie Lho
- * bug fix for record mask setting
- * v0.2 Feb 10 2000 Ollie Lho
- * add ac97_read_proc for /proc/driver/{vendor}/ac97
- * v0.1 Jan 14 2000 Ollie Lho <ollie@sis.com.tw>
- * Isolated from trident.c to support multiple ac97 codec
- */
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/bitops.h>
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/ac97_codec.h>
-#include <asm/uaccess.h>
-#include <linux/mutex.h>
-
-#define CODEC_ID_BUFSZ 14
-
-static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel);
-static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel,
- unsigned int left, unsigned int right);
-static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val );
-static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask);
-static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg);
-
-static int ac97_init_mixer(struct ac97_codec *codec);
-
-static int wolfson_init03(struct ac97_codec * codec);
-static int wolfson_init04(struct ac97_codec * codec);
-static int wolfson_init05(struct ac97_codec * codec);
-static int wolfson_init11(struct ac97_codec * codec);
-static int wolfson_init13(struct ac97_codec * codec);
-static int tritech_init(struct ac97_codec * codec);
-static int tritech_maestro_init(struct ac97_codec * codec);
-static int sigmatel_9708_init(struct ac97_codec *codec);
-static int sigmatel_9721_init(struct ac97_codec *codec);
-static int sigmatel_9744_init(struct ac97_codec *codec);
-static int ad1886_init(struct ac97_codec *codec);
-static int eapd_control(struct ac97_codec *codec, int);
-static int crystal_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
-static int cmedia_init(struct ac97_codec * codec);
-static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
-static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode);
-
-
-/*
- * AC97 operations.
- *
- * If you are adding a codec then you should be able to use
- * eapd_ops - any codec that supports EAPD amp control (most)
- * null_ops - any ancient codec that supports nothing
- *
- * The three functions are
- * init - used for non AC97 standard initialisation
- * amplifier - used to do amplifier control (1=on 0=off)
- * digital - switch to digital modes (0 = analog)
- *
- * Not all codecs support all features, not all drivers use all the
- * operations yet
- */
-
-static struct ac97_ops null_ops = { NULL, NULL, NULL };
-static struct ac97_ops default_ops = { NULL, eapd_control, NULL };
-static struct ac97_ops default_digital_ops = { NULL, eapd_control, generic_digital_control};
-static struct ac97_ops wolfson_ops03 = { wolfson_init03, NULL, NULL };
-static struct ac97_ops wolfson_ops04 = { wolfson_init04, NULL, NULL };
-static struct ac97_ops wolfson_ops05 = { wolfson_init05, NULL, NULL };
-static struct ac97_ops wolfson_ops11 = { wolfson_init11, NULL, NULL };
-static struct ac97_ops wolfson_ops13 = { wolfson_init13, NULL, NULL };
-static struct ac97_ops tritech_ops = { tritech_init, NULL, NULL };
-static struct ac97_ops tritech_m_ops = { tritech_maestro_init, NULL, NULL };
-static struct ac97_ops sigmatel_9708_ops = { sigmatel_9708_init, NULL, NULL };
-static struct ac97_ops sigmatel_9721_ops = { sigmatel_9721_init, NULL, NULL };
-static struct ac97_ops sigmatel_9744_ops = { sigmatel_9744_init, NULL, NULL };
-static struct ac97_ops crystal_digital_ops = { NULL, eapd_control, crystal_digital_control };
-static struct ac97_ops ad1886_ops = { ad1886_init, eapd_control, NULL };
-static struct ac97_ops cmedia_ops = { NULL, eapd_control, NULL};
-static struct ac97_ops cmedia_digital_ops = { cmedia_init, eapd_control, cmedia_digital_control};
-
-/* sorted by vendor/device id */
-static const struct {
- u32 id;
- char *name;
- struct ac97_ops *ops;
- int flags;
-} ac97_codec_ids[] = {
- {0x41445303, "Analog Devices AD1819", &null_ops},
- {0x41445340, "Analog Devices AD1881", &null_ops},
- {0x41445348, "Analog Devices AD1881A", &null_ops},
- {0x41445360, "Analog Devices AD1885", &default_ops},
- {0x41445361, "Analog Devices AD1886", &ad1886_ops},
- {0x41445370, "Analog Devices AD1981", &null_ops},
- {0x41445372, "Analog Devices AD1981A", &null_ops},
- {0x41445374, "Analog Devices AD1981B", &null_ops},
- {0x41445460, "Analog Devices AD1885", &default_ops},
- {0x41445461, "Analog Devices AD1886", &ad1886_ops},
- {0x414B4D00, "Asahi Kasei AK4540", &null_ops},
- {0x414B4D01, "Asahi Kasei AK4542", &null_ops},
- {0x414B4D02, "Asahi Kasei AK4543", &null_ops},
- {0x414C4326, "ALC100P", &null_ops},
- {0x414C4710, "ALC200/200P", &null_ops},
- {0x414C4720, "ALC650", &default_digital_ops},
- {0x434D4941, "CMedia", &cmedia_ops, AC97_NO_PCM_VOLUME },
- {0x434D4942, "CMedia", &cmedia_ops, AC97_NO_PCM_VOLUME },
- {0x434D4961, "CMedia", &cmedia_digital_ops, AC97_NO_PCM_VOLUME },
- {0x43525900, "Cirrus Logic CS4297", &default_ops},
- {0x43525903, "Cirrus Logic CS4297", &default_ops},
- {0x43525913, "Cirrus Logic CS4297A rev A", &default_ops},
- {0x43525914, "Cirrus Logic CS4297A rev B", &default_ops},
- {0x43525923, "Cirrus Logic CS4298", &null_ops},
- {0x4352592B, "Cirrus Logic CS4294", &null_ops},
- {0x4352592D, "Cirrus Logic CS4294", &null_ops},
- {0x43525931, "Cirrus Logic CS4299 rev A", &crystal_digital_ops},
- {0x43525933, "Cirrus Logic CS4299 rev C", &crystal_digital_ops},
- {0x43525934, "Cirrus Logic CS4299 rev D", &crystal_digital_ops},
- {0x43585430, "CXT48", &default_ops, AC97_DELUDED_MODEM },
- {0x43585442, "CXT66", &default_ops, AC97_DELUDED_MODEM },
- {0x44543031, "Diamond Technology DT0893", &default_ops},
- {0x45838308, "ESS Allegro ES1988", &null_ops},
- {0x49434511, "ICE1232", &null_ops}, /* I hope --jk */
- {0x4e534331, "National Semiconductor LM4549", &null_ops},
- {0x53494c22, "Silicon Laboratory Si3036", &null_ops},
- {0x53494c23, "Silicon Laboratory Si3038", &null_ops},
- {0x545200FF, "TriTech TR?????", &tritech_m_ops},
- {0x54524102, "TriTech TR28022", &null_ops},
- {0x54524103, "TriTech TR28023", &null_ops},
- {0x54524106, "TriTech TR28026", &null_ops},
- {0x54524108, "TriTech TR28028", &tritech_ops},
- {0x54524123, "TriTech TR A5", &null_ops},
- {0x574D4C03, "Wolfson WM9703/07/08/17", &wolfson_ops03},
- {0x574D4C04, "Wolfson WM9704M/WM9704Q", &wolfson_ops04},
- {0x574D4C05, "Wolfson WM9705/WM9710", &wolfson_ops05},
- {0x574D4C09, "Wolfson WM9709", &null_ops},
- {0x574D4C12, "Wolfson WM9711/9712", &wolfson_ops11},
- {0x574D4C13, "Wolfson WM9713", &wolfson_ops13, AC97_DEFAULT_POWER_OFF},
- {0x83847600, "SigmaTel STAC????", &null_ops},
- {0x83847604, "SigmaTel STAC9701/3/4/5", &null_ops},
- {0x83847605, "SigmaTel STAC9704", &null_ops},
- {0x83847608, "SigmaTel STAC9708", &sigmatel_9708_ops},
- {0x83847609, "SigmaTel STAC9721/23", &sigmatel_9721_ops},
- {0x83847644, "SigmaTel STAC9744/45", &sigmatel_9744_ops},
- {0x83847652, "SigmaTel STAC9752/53", &default_ops},
- {0x83847656, "SigmaTel STAC9756/57", &sigmatel_9744_ops},
- {0x83847666, "SigmaTel STAC9750T", &sigmatel_9744_ops},
- {0x83847684, "SigmaTel STAC9783/84?", &null_ops},
- {0x57454301, "Winbond 83971D", &null_ops},
-};
-
-/* this table has default mixer values for all OSS mixers. */
-static struct mixer_defaults {
- int mixer;
- unsigned int value;
-} mixer_defaults[SOUND_MIXER_NRDEVICES] = {
- /* all values 0 -> 100 in bytes */
- {SOUND_MIXER_VOLUME, 0x4343},
- {SOUND_MIXER_BASS, 0x4343},
- {SOUND_MIXER_TREBLE, 0x4343},
- {SOUND_MIXER_PCM, 0x4343},
- {SOUND_MIXER_SPEAKER, 0x4343},
- {SOUND_MIXER_LINE, 0x4343},
- {SOUND_MIXER_MIC, 0x0000},
- {SOUND_MIXER_CD, 0x4343},
- {SOUND_MIXER_ALTPCM, 0x4343},
- {SOUND_MIXER_IGAIN, 0x4343},
- {SOUND_MIXER_LINE1, 0x4343},
- {SOUND_MIXER_PHONEIN, 0x4343},
- {SOUND_MIXER_PHONEOUT, 0x4343},
- {SOUND_MIXER_VIDEO, 0x4343},
- {-1,0}
-};
-
-/* table to scale scale from OSS mixer value to AC97 mixer register value */
-static struct ac97_mixer_hw {
- unsigned char offset;
- int scale;
-} ac97_hw[SOUND_MIXER_NRDEVICES]= {
- [SOUND_MIXER_VOLUME] = {AC97_MASTER_VOL_STEREO,64},
- [SOUND_MIXER_BASS] = {AC97_MASTER_TONE, 16},
- [SOUND_MIXER_TREBLE] = {AC97_MASTER_TONE, 16},
- [SOUND_MIXER_PCM] = {AC97_PCMOUT_VOL, 32},
- [SOUND_MIXER_SPEAKER] = {AC97_PCBEEP_VOL, 16},
- [SOUND_MIXER_LINE] = {AC97_LINEIN_VOL, 32},
- [SOUND_MIXER_MIC] = {AC97_MIC_VOL, 32},
- [SOUND_MIXER_CD] = {AC97_CD_VOL, 32},
- [SOUND_MIXER_ALTPCM] = {AC97_HEADPHONE_VOL, 64},
- [SOUND_MIXER_IGAIN] = {AC97_RECORD_GAIN, 16},
- [SOUND_MIXER_LINE1] = {AC97_AUX_VOL, 32},
- [SOUND_MIXER_PHONEIN] = {AC97_PHONE_VOL, 32},
- [SOUND_MIXER_PHONEOUT] = {AC97_MASTER_VOL_MONO, 64},
- [SOUND_MIXER_VIDEO] = {AC97_VIDEO_VOL, 32},
-};
-
-/* the following tables allow us to go from OSS <-> ac97 quickly. */
-enum ac97_recsettings {
- AC97_REC_MIC=0,
- AC97_REC_CD,
- AC97_REC_VIDEO,
- AC97_REC_AUX,
- AC97_REC_LINE,
- AC97_REC_STEREO, /* combination of all enabled outputs.. */
- AC97_REC_MONO, /*.. or the mono equivalent */
- AC97_REC_PHONE
-};
-
-static const unsigned int ac97_rm2oss[] = {
- [AC97_REC_MIC] = SOUND_MIXER_MIC,
- [AC97_REC_CD] = SOUND_MIXER_CD,
- [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO,
- [AC97_REC_AUX] = SOUND_MIXER_LINE1,
- [AC97_REC_LINE] = SOUND_MIXER_LINE,
- [AC97_REC_STEREO]= SOUND_MIXER_IGAIN,
- [AC97_REC_PHONE] = SOUND_MIXER_PHONEIN
-};
-
-/* indexed by bit position */
-static const unsigned int ac97_oss_rm[] = {
- [SOUND_MIXER_MIC] = AC97_REC_MIC,
- [SOUND_MIXER_CD] = AC97_REC_CD,
- [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO,
- [SOUND_MIXER_LINE1] = AC97_REC_AUX,
- [SOUND_MIXER_LINE] = AC97_REC_LINE,
- [SOUND_MIXER_IGAIN] = AC97_REC_STEREO,
- [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE
-};
-
-static LIST_HEAD(codecs);
-static LIST_HEAD(codec_drivers);
-static DEFINE_MUTEX(codec_mutex);
-
-/* reads the given OSS mixer from the ac97 the caller must have insured that the ac97 knows
- about that given mixer, and should be holding a spinlock for the card */
-static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel)
-{
- u16 val;
- int ret = 0;
- int scale;
- struct ac97_mixer_hw *mh = &ac97_hw[oss_channel];
-
- val = codec->codec_read(codec , mh->offset);
-
- if (val & AC97_MUTE) {
- ret = 0;
- } else if (AC97_STEREO_MASK & (1 << oss_channel)) {
- /* nice stereo mixers .. */
- int left,right;
-
- left = (val >> 8) & 0x7f;
- right = val & 0x7f;
-
- if (oss_channel == SOUND_MIXER_IGAIN) {
- right = (right * 100) / mh->scale;
- left = (left * 100) / mh->scale;
- } else {
- /* these may have 5 or 6 bit resolution */
- if(oss_channel == SOUND_MIXER_VOLUME || oss_channel == SOUND_MIXER_ALTPCM)
- scale = (1 << codec->bit_resolution);
- else
- scale = mh->scale;
-
- right = 100 - ((right * 100) / scale);
- left = 100 - ((left * 100) / scale);
- }
- ret = left | (right << 8);
- } else if (oss_channel == SOUND_MIXER_SPEAKER) {
- ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale);
- } else if (oss_channel == SOUND_MIXER_PHONEIN) {
- ret = 100 - (((val & 0x1f) * 100) / mh->scale);
- } else if (oss_channel == SOUND_MIXER_PHONEOUT) {
- scale = (1 << codec->bit_resolution);
- ret = 100 - (((val & 0x1f) * 100) / scale);
- } else if (oss_channel == SOUND_MIXER_MIC) {
- ret = 100 - (((val & 0x1f) * 100) / mh->scale);
- /* the low bit is optional in the tone sliders and masking
- it lets us avoid the 0xf 'bypass'.. */
- } else if (oss_channel == SOUND_MIXER_BASS) {
- ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale);
- } else if (oss_channel == SOUND_MIXER_TREBLE) {
- ret = 100 - (((val & 0xe) * 100) / mh->scale);
- }
-
-#ifdef DEBUG
- printk("ac97_codec: read OSS mixer %2d (%s ac97 register 0x%02x), "
- "0x%04x -> 0x%04x\n",
- oss_channel, codec->id ? "Secondary" : "Primary",
- mh->offset, val, ret);
-#endif
-
- return ret;
-}
-
-/* write the OSS encoded volume to the given OSS encoded mixer, again caller's job to
- make sure all is well in arg land, call with spinlock held */
-static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel,
- unsigned int left, unsigned int right)
-{
- u16 val = 0;
- int scale;
- struct ac97_mixer_hw *mh = &ac97_hw[oss_channel];
-
-#ifdef DEBUG
- printk("ac97_codec: wrote OSS mixer %2d (%s ac97 register 0x%02x), "
- "left vol:%2d, right vol:%2d:",
- oss_channel, codec->id ? "Secondary" : "Primary",
- mh->offset, left, right);
-#endif
-
- if (AC97_STEREO_MASK & (1 << oss_channel)) {
- /* stereo mixers */
- if (left == 0 && right == 0) {
- val = AC97_MUTE;
- } else {
- if (oss_channel == SOUND_MIXER_IGAIN) {
- right = (right * mh->scale) / 100;
- left = (left * mh->scale) / 100;
- if (right >= mh->scale)
- right = mh->scale-1;
- if (left >= mh->scale)
- left = mh->scale-1;
- } else {
- /* these may have 5 or 6 bit resolution */
- if (oss_channel == SOUND_MIXER_VOLUME ||
- oss_channel == SOUND_MIXER_ALTPCM)
- scale = (1 << codec->bit_resolution);
- else
- scale = mh->scale;
-
- right = ((100 - right) * scale) / 100;
- left = ((100 - left) * scale) / 100;
- if (right >= scale)
- right = scale-1;
- if (left >= scale)
- left = scale-1;
- }
- val = (left << 8) | right;
- }
- } else if (oss_channel == SOUND_MIXER_BASS) {
- val = codec->codec_read(codec , mh->offset) & ~0x0f00;
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val |= (left << 8) & 0x0e00;
- } else if (oss_channel == SOUND_MIXER_TREBLE) {
- val = codec->codec_read(codec , mh->offset) & ~0x000f;
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val |= left & 0x000e;
- } else if(left == 0) {
- val = AC97_MUTE;
- } else if (oss_channel == SOUND_MIXER_SPEAKER) {
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val = left << 1;
- } else if (oss_channel == SOUND_MIXER_PHONEIN) {
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val = left;
- } else if (oss_channel == SOUND_MIXER_PHONEOUT) {
- scale = (1 << codec->bit_resolution);
- left = ((100 - left) * scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val = left;
- } else if (oss_channel == SOUND_MIXER_MIC) {
- val = codec->codec_read(codec , mh->offset) & ~0x801f;
- left = ((100 - left) * mh->scale) / 100;
- if (left >= mh->scale)
- left = mh->scale-1;
- val |= left;
- /* the low bit is optional in the tone sliders and masking
- it lets us avoid the 0xf 'bypass'.. */
- }
-#ifdef DEBUG
- printk(" 0x%04x", val);
-#endif
-
- codec->codec_write(codec, mh->offset, val);
-
-#ifdef DEBUG
- val = codec->codec_read(codec, mh->offset);
- printk(" -> 0x%04x\n", val);
-#endif
-}
-
-/* a thin wrapper for write_mixer */
-static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val )
-{
- unsigned int left,right;
-
- /* cleanse input a little */
- right = ((val >> 8) & 0xff) ;
- left = (val & 0xff) ;
-
- if (right > 100) right = 100;
- if (left > 100) left = 100;
-
- codec->mixer_state[oss_mixer] = (right << 8) | left;
- codec->write_mixer(codec, oss_mixer, left, right);
-}
-
-/* read or write the recmask, the ac97 can really have left and right recording
- inputs independantly set, but OSS doesn't seem to want us to express that to
- the user. the caller guarantees that we have a supported bit set, and they
- must be holding the card's spinlock */
-static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask)
-{
- unsigned int val;
-
- if (rw) {
- /* read it from the card */
- val = codec->codec_read(codec, AC97_RECORD_SELECT);
-#ifdef DEBUG
- printk("ac97_codec: ac97 recmask to set to 0x%04x\n", val);
-#endif
- return (1 << ac97_rm2oss[val & 0x07]);
- }
-
- /* else, write the first set in the mask as the
- output */
- /* clear out current set value first (AC97 supports only 1 input!) */
- val = (1 << ac97_rm2oss[codec->codec_read(codec, AC97_RECORD_SELECT) & 0x07]);
- if (mask != val)
- mask &= ~val;
-
- val = ffs(mask);
- val = ac97_oss_rm[val-1];
- val |= val << 8; /* set both channels */
-
-#ifdef DEBUG
- printk("ac97_codec: setting ac97 recmask to 0x%04x\n", val);
-#endif
-
- codec->codec_write(codec, AC97_RECORD_SELECT, val);
-
- return 0;
-};
-
-static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg)
-{
- int i, val = 0;
-
- if (cmd == SOUND_MIXER_INFO) {
- mixer_info info;
- memset(&info, 0, sizeof(info));
- strlcpy(info.id, codec->name, sizeof(info.id));
- strlcpy(info.name, codec->name, sizeof(info.name));
- info.modify_counter = codec->modcnt;
- if (copy_to_user((void __user *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
- if (cmd == SOUND_OLD_MIXER_INFO) {
- _old_mixer_info info;
- memset(&info, 0, sizeof(info));
- strlcpy(info.id, codec->name, sizeof(info.id));
- strlcpy(info.name, codec->name, sizeof(info.name));
- if (copy_to_user((void __user *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
-
- if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int))
- return -EINVAL;
-
- if (cmd == OSS_GETVERSION)
- return put_user(SOUND_VERSION, (int __user *)arg);
-
- if (_SIOC_DIR(cmd) == _SIOC_READ) {
- switch (_IOC_NR(cmd)) {
- case SOUND_MIXER_RECSRC: /* give them the current record source */
- if (!codec->recmask_io) {
- val = 0;
- } else {
- val = codec->recmask_io(codec, 1, 0);
- }
- break;
-
- case SOUND_MIXER_DEVMASK: /* give them the supported mixers */
- val = codec->supported_mixers;
- break;
-
- case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
- val = codec->record_sources;
- break;
-
- case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
- val = codec->stereo_mixers;
- break;
-
- case SOUND_MIXER_CAPS:
- val = SOUND_CAP_EXCL_INPUT;
- break;
-
- default: /* read a specific mixer */
- i = _IOC_NR(cmd);
-
- if (!supported_mixer(codec, i))
- return -EINVAL;
-
- /* do we ever want to touch the hardware? */
- /* val = codec->read_mixer(codec, i); */
- val = codec->mixer_state[i];
- break;
- }
- return put_user(val, (int __user *)arg);
- }
-
- if (_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) {
- codec->modcnt++;
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
-
- switch (_IOC_NR(cmd)) {
- case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
- if (!codec->recmask_io) return -EINVAL;
- if (!val) return 0;
- if (!(val &= codec->record_sources)) return -EINVAL;
-
- codec->recmask_io(codec, 0, val);
-
- return 0;
- default: /* write a specific mixer */
- i = _IOC_NR(cmd);
-
- if (!supported_mixer(codec, i))
- return -EINVAL;
-
- ac97_set_mixer(codec, i, val);
-
- return 0;
- }
- }
- return -EINVAL;
-}
-
-/**
- * codec_id - Turn id1/id2 into a PnP string
- * @id1: Vendor ID1
- * @id2: Vendor ID2
- * @buf: CODEC_ID_BUFSZ byte buffer
- *
- * Fills buf with a zero terminated PnP ident string for the id1/id2
- * pair. For convenience the return is the passed in buffer pointer.
- */
-
-static char *codec_id(u16 id1, u16 id2, char *buf)
-{
- if(id1&0x8080) {
- snprintf(buf, CODEC_ID_BUFSZ, "0x%04x:0x%04x", id1, id2);
- } else {
- buf[0] = (id1 >> 8);
- buf[1] = (id1 & 0xFF);
- buf[2] = (id2 >> 8);
- snprintf(buf+3, CODEC_ID_BUFSZ - 3, "%d", id2&0xFF);
- }
- return buf;
-}
-
-/**
- * ac97_check_modem - Check if the Codec is a modem
- * @codec: codec to check
- *
- * Return true if the device is an AC97 1.0 or AC97 2.0 modem
- */
-
-static int ac97_check_modem(struct ac97_codec *codec)
-{
- /* Check for an AC97 1.0 soft modem (ID1) */
- if(codec->codec_read(codec, AC97_RESET) & 2)
- return 1;
- /* Check for an AC97 2.x soft modem */
- codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L);
- if(codec->codec_read(codec, AC97_EXTENDED_MODEM_ID) & 1)
- return 1;
- return 0;
-}
-
-
-/**
- * ac97_alloc_codec - Allocate an AC97 codec
- *
- * Returns a new AC97 codec structure. AC97 codecs may become
- * refcounted soon so this interface is needed. Returns with
- * one reference taken.
- */
-
-struct ac97_codec *ac97_alloc_codec(void)
-{
- struct ac97_codec *codec = kzalloc(sizeof(struct ac97_codec), GFP_KERNEL);
- if(!codec)
- return NULL;
-
- spin_lock_init(&codec->lock);
- INIT_LIST_HEAD(&codec->list);
- return codec;
-}
-
-EXPORT_SYMBOL(ac97_alloc_codec);
-
-/**
- * ac97_release_codec - Release an AC97 codec
- * @codec: codec to release
- *
- * Release an allocated AC97 codec. This will be refcounted in
- * time but for the moment is trivial. Calls the unregister
- * handler if the codec is now defunct.
- */
-
-void ac97_release_codec(struct ac97_codec *codec)
-{
- /* Remove from the list first, we don't want to be
- "rediscovered" */
- mutex_lock(&codec_mutex);
- list_del(&codec->list);
- mutex_unlock(&codec_mutex);
- /*
- * The driver needs to deal with internal
- * locking to avoid accidents here.
- */
- if(codec->driver)
- codec->driver->remove(codec, codec->driver);
- kfree(codec);
-}
-
-EXPORT_SYMBOL(ac97_release_codec);
-
-/**
- * ac97_probe_codec - Initialize and setup AC97-compatible codec
- * @codec: (in/out) Kernel info for a single AC97 codec
- *
- * Reset the AC97 codec, then initialize the mixer and
- * the rest of the @codec structure.
- *
- * The codec_read and codec_write fields of @codec are
- * required to be setup and working when this function
- * is called. All other fields are set by this function.
- *
- * codec_wait field of @codec can optionally be provided
- * when calling this function. If codec_wait is not %NULL,
- * this function will call codec_wait any time it is
- * necessary to wait for the audio chip to reach the
- * codec-ready state. If codec_wait is %NULL, then
- * the default behavior is to call schedule_timeout.
- * Currently codec_wait is used to wait for AC97 codec
- * reset to complete.
- *
- * Some codecs will power down when a register reset is
- * performed. We now check for such codecs.
- *
- * Returns 1 (true) on success, or 0 (false) on failure.
- */
-
-int ac97_probe_codec(struct ac97_codec *codec)
-{
- u16 id1, id2;
- u16 audio;
- int i;
- char cidbuf[CODEC_ID_BUFSZ];
- u16 f;
- struct list_head *l;
- struct ac97_driver *d;
-
- /* wait for codec-ready state */
- if (codec->codec_wait)
- codec->codec_wait(codec);
- else
- udelay(10);
-
- /* will the codec power down if register reset ? */
- id1 = codec->codec_read(codec, AC97_VENDOR_ID1);
- id2 = codec->codec_read(codec, AC97_VENDOR_ID2);
- codec->name = NULL;
- codec->codec_ops = &null_ops;
- for (i = 0; i < ARRAY_SIZE(ac97_codec_ids); i++) {
- if (ac97_codec_ids[i].id == ((id1 << 16) | id2)) {
- codec->type = ac97_codec_ids[i].id;
- codec->name = ac97_codec_ids[i].name;
- codec->codec_ops = ac97_codec_ids[i].ops;
- codec->flags = ac97_codec_ids[i].flags;
- break;
- }
- }
-
- codec->model = (id1 << 16) | id2;
- if ((codec->flags & AC97_DEFAULT_POWER_OFF) == 0) {
- /* reset codec and wait for the ready bit before we continue */
- codec->codec_write(codec, AC97_RESET, 0L);
- if (codec->codec_wait)
- codec->codec_wait(codec);
- else
- udelay(10);
- }
-
- /* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should
- * be read zero.
- *
- * FIXME: is the following comment outdated? -jgarzik
- * Probing of AC97 in this way is not reliable, it is not even SAFE !!
- */
- if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) {
- printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n",
- (codec->id & 0x2) ? (codec->id&1 ? "4th" : "Tertiary")
- : (codec->id&1 ? "Secondary": "Primary"));
- return 0;
- }
-
- /* probe for Modem Codec */
- codec->modem = ac97_check_modem(codec);
-
- /* enable SPDIF */
- f = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- if((codec->codec_ops == &null_ops) && (f & 4))
- codec->codec_ops = &default_digital_ops;
-
- /* A device which thinks its a modem but isnt */
- if(codec->flags & AC97_DELUDED_MODEM)
- codec->modem = 0;
-
- if (codec->name == NULL)
- codec->name = "Unknown";
- printk(KERN_INFO "ac97_codec: AC97 %s codec, id: %s (%s)\n",
- codec->modem ? "Modem" : (audio ? "Audio" : ""),
- codec_id(id1, id2, cidbuf), codec->name);
-
- if(!ac97_init_mixer(codec))
- return 0;
-
- /*
- * Attach last so the caller can override the mixer
- * callbacks.
- */
-
- mutex_lock(&codec_mutex);
- list_add(&codec->list, &codecs);
-
- list_for_each(l, &codec_drivers) {
- d = list_entry(l, struct ac97_driver, list);
- if ((codec->model ^ d->codec_id) & d->codec_mask)
- continue;
- if(d->probe(codec, d) == 0)
- {
- codec->driver = d;
- break;
- }
- }
-
- mutex_unlock(&codec_mutex);
- return 1;
-}
-
-static int ac97_init_mixer(struct ac97_codec *codec)
-{
- u16 cap;
- int i;
-
- cap = codec->codec_read(codec, AC97_RESET);
-
- /* mixer masks */
- codec->supported_mixers = AC97_SUPPORTED_MASK;
- codec->stereo_mixers = AC97_STEREO_MASK;
- codec->record_sources = AC97_RECORD_MASK;
- if (!(cap & 0x04))
- codec->supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE);
- if (!(cap & 0x10))
- codec->supported_mixers &= ~SOUND_MASK_ALTPCM;
-
-
- /* detect bit resolution */
- codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0x2020);
- if(codec->codec_read(codec, AC97_MASTER_VOL_STEREO) == 0x2020)
- codec->bit_resolution = 6;
- else
- codec->bit_resolution = 5;
-
- /* generic OSS to AC97 wrapper */
- codec->read_mixer = ac97_read_mixer;
- codec->write_mixer = ac97_write_mixer;
- codec->recmask_io = ac97_recmask_io;
- codec->mixer_ioctl = ac97_mixer_ioctl;
-
- /* initialize mixer channel volumes */
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
- struct mixer_defaults *md = &mixer_defaults[i];
- if (md->mixer == -1)
- break;
- if (!supported_mixer(codec, md->mixer))
- continue;
- ac97_set_mixer(codec, md->mixer, md->value);
- }
-
- /* codec specific initialization for 4-6 channel output or secondary codec stuff */
- if (codec->codec_ops->init != NULL) {
- codec->codec_ops->init(codec);
- }
-
- /*
- * Volume is MUTE only on this device. We have to initialise
- * it but its useless beyond that.
- */
- if(codec->flags & AC97_NO_PCM_VOLUME)
- {
- codec->supported_mixers &= ~SOUND_MASK_PCM;
- printk(KERN_WARNING "AC97 codec does not have proper volume support.\n");
- }
- return 1;
-}
-
-#define AC97_SIGMATEL_ANALOG 0x6c /* Analog Special */
-#define AC97_SIGMATEL_DAC2INVERT 0x6e
-#define AC97_SIGMATEL_BIAS1 0x70
-#define AC97_SIGMATEL_BIAS2 0x72
-#define AC97_SIGMATEL_MULTICHN 0x74 /* Multi-Channel programming */
-#define AC97_SIGMATEL_CIC1 0x76
-#define AC97_SIGMATEL_CIC2 0x78
-
-
-static int sigmatel_9708_init(struct ac97_codec * codec)
-{
- u16 codec72, codec6c;
-
- codec72 = codec->codec_read(codec, AC97_SIGMATEL_BIAS2) & 0x8000;
- codec6c = codec->codec_read(codec, AC97_SIGMATEL_ANALOG);
-
- if ((codec72==0) && (codec6c==0)) {
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1000);
- codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0007);
- } else if ((codec72==0x8000) && (codec6c==0)) {
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1001);
- codec->codec_write(codec, AC97_SIGMATEL_DAC2INVERT, 0x0008);
- } else if ((codec72==0x8000) && (codec6c==0x0080)) {
- /* nothing */
- }
- codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000);
- return 0;
-}
-
-
-static int sigmatel_9721_init(struct ac97_codec * codec)
-{
- /* Only set up secondary codec */
- if (codec->id == 0)
- return 0;
-
- codec->codec_write(codec, AC97_SURROUND_MASTER, 0L);
-
- /* initialize SigmaTel STAC9721/23 as secondary codec, decoding AC link
- sloc 3,4 = 0x01, slot 7,8 = 0x00, */
- codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x00);
-
- /* we don't have the crystal when we are on an AMR card, so use
- BIT_CLK as our clock source. Write the magic word ABBA and read
- back to enable register 0x78 */
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_read(codec, AC97_SIGMATEL_CIC1);
-
- /* sync all the clocks*/
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x3802);
-
- return 0;
-}
-
-
-static int sigmatel_9744_init(struct ac97_codec * codec)
-{
- // patch for SigmaTel
- codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x0000); // is this correct? --jk
- codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba);
- codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0002);
- codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000);
- return 0;
-}
-
-static int cmedia_init(struct ac97_codec *codec)
-{
- /* Initialise the CMedia 9739 */
- /*
- We could set various options here
- Register 0x20 bit 0x100 sets mic as center bass
- Also do multi_channel_ctrl &=~0x3000 |=0x1000
-
- For now we set up the GPIO and PC beep
- */
-
- u16 v;
-
- /* MIC */
- codec->codec_write(codec, 0x64, 0x3000);
- v = codec->codec_read(codec, 0x64);
- v &= ~0x8000;
- codec->codec_write(codec, 0x64, v);
- codec->codec_write(codec, 0x70, 0x0100);
- codec->codec_write(codec, 0x72, 0x0020);
- return 0;
-}
-
-#define AC97_WM97XX_FMIXER_VOL 0x72
-#define AC97_WM97XX_RMIXER_VOL 0x74
-#define AC97_WM97XX_TEST 0x5a
-#define AC97_WM9704_RPCM_VOL 0x70
-#define AC97_WM9711_OUT3VOL 0x16
-
-static int wolfson_init03(struct ac97_codec * codec)
-{
- /* this is known to work for the ViewSonic ViewPad 1000 */
- codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
- codec->codec_write(codec, AC97_GENERAL_PURPOSE, 0x8000);
- return 0;
-}
-
-static int wolfson_init04(struct ac97_codec * codec)
-{
- codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
- codec->codec_write(codec, AC97_WM97XX_RMIXER_VOL, 0x0808);
-
- // patch for DVD noise
- codec->codec_write(codec, AC97_WM97XX_TEST, 0x0200);
-
- // init vol as PCM vol
- codec->codec_write(codec, AC97_WM9704_RPCM_VOL,
- codec->codec_read(codec, AC97_PCMOUT_VOL));
-
- /* set rear surround volume */
- codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000);
- return 0;
-}
-
-/* WM9705, WM9710 */
-static int wolfson_init05(struct ac97_codec * codec)
-{
- /* set front mixer volume */
- codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808);
- return 0;
-}
-
-/* WM9711, WM9712 */
-static int wolfson_init11(struct ac97_codec * codec)
-{
- /* stop pop's during suspend/resume */
- codec->codec_write(codec, AC97_WM97XX_TEST,
- codec->codec_read(codec, AC97_WM97XX_TEST) & 0xffbf);
-
- /* set out3 volume */
- codec->codec_write(codec, AC97_WM9711_OUT3VOL, 0x0808);
- return 0;
-}
-
-/* WM9713 */
-static int wolfson_init13(struct ac97_codec * codec)
-{
- codec->codec_write(codec, AC97_RECORD_GAIN, 0x00a0);
- codec->codec_write(codec, AC97_POWER_CONTROL, 0x0000);
- codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0xDA00);
- codec->codec_write(codec, AC97_EXTEND_MODEM_STAT, 0x3810);
- codec->codec_write(codec, AC97_PHONE_VOL, 0x0808);
- codec->codec_write(codec, AC97_PCBEEP_VOL, 0x0808);
-
- return 0;
-}
-
-static int tritech_init(struct ac97_codec * codec)
-{
- codec->codec_write(codec, 0x26, 0x0300);
- codec->codec_write(codec, 0x26, 0x0000);
- codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0000);
- codec->codec_write(codec, AC97_RESERVED_3A, 0x0000);
- return 0;
-}
-
-
-/* copied from drivers/sound/maestro.c */
-static int tritech_maestro_init(struct ac97_codec * codec)
-{
- /* no idea what this does */
- codec->codec_write(codec, 0x2A, 0x0001);
- codec->codec_write(codec, 0x2C, 0x0000);
- codec->codec_write(codec, 0x2C, 0XFFFF);
- return 0;
-}
-
-
-
-/*
- * Presario700 workaround
- * for Jack Sense/SPDIF Register mis-setting causing
- * no audible output
- * by Santiago Nullo 04/05/2002
- */
-
-#define AC97_AD1886_JACK_SENSE 0x72
-
-static int ad1886_init(struct ac97_codec * codec)
-{
- /* from AD1886 Specs */
- codec->codec_write(codec, AC97_AD1886_JACK_SENSE, 0x0010);
- return 0;
-}
-
-
-
-
-/*
- * This is basically standard AC97. It should work as a default for
- * almost all modern codecs. Note that some cards wire EAPD *backwards*
- * That side of it is up to the card driver not us to cope with.
- *
- */
-
-static int eapd_control(struct ac97_codec * codec, int on)
-{
- if(on)
- codec->codec_write(codec, AC97_POWER_CONTROL,
- codec->codec_read(codec, AC97_POWER_CONTROL)|0x8000);
- else
- codec->codec_write(codec, AC97_POWER_CONTROL,
- codec->codec_read(codec, AC97_POWER_CONTROL)&~0x8000);
- return 0;
-}
-
-static int generic_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
-{
- u16 reg;
-
- reg = codec->codec_read(codec, AC97_SPDIF_CONTROL);
-
- switch(rate)
- {
- /* Off by default */
- default:
- case 0:
- reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- codec->codec_write(codec, AC97_EXTENDED_STATUS, (reg & ~AC97_EA_SPDIF));
- if(rate == 0)
- return 0;
- return -EINVAL;
- case 1:
- reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K;
- break;
- case 2:
- reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K;
- break;
- case 3:
- reg = (reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K;
- break;
- }
-
- reg &= ~AC97_SC_CC_MASK;
- reg |= (mode & AUDIO_CCMASK) << 6;
-
- if(mode & AUDIO_DIGITAL)
- reg |= 2;
- if(mode & AUDIO_PRO)
- reg |= 1;
- if(mode & AUDIO_DRS)
- reg |= 0x4000;
-
- codec->codec_write(codec, AC97_SPDIF_CONTROL, reg);
-
- reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- reg &= (AC97_EA_SLOT_MASK);
- reg |= AC97_EA_VRA | AC97_EA_SPDIF | slots;
- codec->codec_write(codec, AC97_EXTENDED_STATUS, reg);
-
- reg = codec->codec_read(codec, AC97_EXTENDED_STATUS);
- if(!(reg & 0x0400))
- {
- codec->codec_write(codec, AC97_EXTENDED_STATUS, reg & ~ AC97_EA_SPDIF);
- return -EINVAL;
- }
- return 0;
-}
-
-/*
- * Crystal digital audio control (CS4299)
- */
-
-static int crystal_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
-{
- u16 cv;
-
- if(mode & AUDIO_DIGITAL)
- return -EINVAL;
-
- switch(rate)
- {
- case 0: cv = 0x0; break; /* SPEN off */
- case 48000: cv = 0x8004; break; /* 48KHz digital */
- case 44100: cv = 0x8104; break; /* 44.1KHz digital */
- case 32768: /* 32Khz */
- default:
- return -EINVAL;
- }
- codec->codec_write(codec, 0x68, cv);
- return 0;
-}
-
-/*
- * CMedia digital audio control
- * Needs more work.
- */
-
-static int cmedia_digital_control(struct ac97_codec *codec, int slots, int rate, int mode)
-{
- u16 cv;
-
- if(mode & AUDIO_DIGITAL)
- return -EINVAL;
-
- switch(rate)
- {
- case 0: cv = 0x0001; break; /* SPEN off */
- case 48000: cv = 0x0009; break; /* 48KHz digital */
- default:
- return -EINVAL;
- }
- codec->codec_write(codec, 0x2A, 0x05c4);
- codec->codec_write(codec, 0x6C, cv);
-
- /* Switch on mix to surround */
- cv = codec->codec_read(codec, 0x64);
- cv &= ~0x0200;
- if(mode)
- cv |= 0x0200;
- codec->codec_write(codec, 0x64, cv);
- return 0;
-}
-
-
-/* copied from drivers/sound/maestro.c */
-#if 0 /* there has been 1 person on the planet with a pt101 that we
- know of. If they care, they can put this back in :) */
-static int pt101_init(struct ac97_codec * codec)
-{
- printk(KERN_INFO "ac97_codec: PT101 Codec detected, initializing but _not_ installing mixer device.\n");
- /* who knows.. */
- codec->codec_write(codec, 0x2A, 0x0001);
- codec->codec_write(codec, 0x2C, 0x0000);
- codec->codec_write(codec, 0x2C, 0xFFFF);
- codec->codec_write(codec, 0x10, 0x9F1F);
- codec->codec_write(codec, 0x12, 0x0808);
- codec->codec_write(codec, 0x14, 0x9F1F);
- codec->codec_write(codec, 0x16, 0x9F1F);
- codec->codec_write(codec, 0x18, 0x0404);
- codec->codec_write(codec, 0x1A, 0x0000);
- codec->codec_write(codec, 0x1C, 0x0000);
- codec->codec_write(codec, 0x02, 0x0404);
- codec->codec_write(codec, 0x04, 0x0808);
- codec->codec_write(codec, 0x0C, 0x801F);
- codec->codec_write(codec, 0x0E, 0x801F);
- return 0;
-}
-#endif
-
-
-EXPORT_SYMBOL(ac97_probe_codec);
-
-MODULE_LICENSE("GPL");
-
diff --git a/sound/oss/ad1848.c b/sound/oss/ad1848.c
deleted file mode 100644
index 4d2a6ae978f7..000000000000
--- a/sound/oss/ad1848.c
+++ /dev/null
@@ -1,3069 +0,0 @@
-/*
- * sound/oss/ad1848.c
- *
- * The low level driver for the AD1848/CS4248 codec chip which
- * is used for example in the MS Sound System.
- *
- * The CS4231 which is used in the GUS MAX and some other cards is
- * upwards compatible with AD1848 and this driver is able to drive it.
- *
- * CS4231A and AD1845 are upward compatible with CS4231. However
- * the new features of these chips are different.
- *
- * CS4232 is a PnP audio chip which contains a CS4231A (and SB, MPU).
- * CS4232A is an improved version of CS4232.
- *
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * general sleep/wakeup clean up.
- * Alan Cox : reformatted. Fixed SMP bugs. Moved to kernel alloc/free
- * of irqs. Use dev_id.
- * Christoph Hellwig : adapted to module_init/module_exit
- * Aki Laukkanen : added power management support
- * Arnaldo C. de Melo : added missing restore_flags in ad1848_resume
- * Miguel Freitas : added ISA PnP support
- * Alan Cox : Added CS4236->4239 identification
- * Daniel T. Cobra : Alernate config/mixer for later chips
- * Alan Cox : Merged chip idents and config code
- *
- * TODO
- * APM save restore assist code on IBM thinkpad
- *
- * Status:
- * Tested. Believed fully functional.
- */
-
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/stddef.h>
-#include <linux/slab.h>
-#include <linux/isapnp.h>
-#include <linux/pnp.h>
-#include <linux/spinlock.h>
-
-#define DEB(x)
-#define DEB1(x)
-#include "sound_config.h"
-
-#include "ad1848.h"
-#include "ad1848_mixer.h"
-
-typedef struct
-{
- spinlock_t lock;
- int base;
- int irq;
- int dma1, dma2;
- int dual_dma; /* 1, when two DMA channels allocated */
- int subtype;
- unsigned char MCE_bit;
- unsigned char saved_regs[64]; /* Includes extended register space */
- int debug_flag;
-
- int audio_flags;
- int record_dev, playback_dev;
-
- int xfer_count;
- int audio_mode;
- int open_mode;
- int intr_active;
- char *chip_name, *name;
- int model;
-#define MD_1848 1
-#define MD_4231 2
-#define MD_4231A 3
-#define MD_1845 4
-#define MD_4232 5
-#define MD_C930 6
-#define MD_IWAVE 7
-#define MD_4235 8 /* Crystal Audio CS4235 */
-#define MD_1845_SSCAPE 9 /* Ensoniq Soundscape PNP*/
-#define MD_4236 10 /* 4236 and higher */
-#define MD_42xB 11 /* CS 42xB */
-#define MD_4239 12 /* CS4239 */
-
- /* Mixer parameters */
- int recmask;
- int supported_devices, orig_devices;
- int supported_rec_devices, orig_rec_devices;
- int *levels;
- short mixer_reroute[32];
- int dev_no;
- volatile unsigned long timer_ticks;
- int timer_running;
- int irq_ok;
- mixer_ents *mix_devices;
- int mixer_output_port;
-} ad1848_info;
-
-typedef struct ad1848_port_info
-{
- int open_mode;
- int speed;
- unsigned char speed_bits;
- int channels;
- int audio_format;
- unsigned char format_bits;
-}
-ad1848_port_info;
-
-static struct address_info cfg;
-static int nr_ad1848_devs;
-
-static int deskpro_xl;
-static int deskpro_m;
-static int soundpro;
-
-static volatile signed char irq2dev[17] = {
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1
-};
-
-#ifndef EXCLUDE_TIMERS
-static int timer_installed = -1;
-#endif
-
-static int loaded;
-
-static int ad_format_mask[13 /*devc->model */ ] =
-{
- 0,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* AD1845 */
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
- AFMT_U8 | AFMT_S16_LE /* CS4235 */,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW /* Ensoniq Soundscape*/,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM,
- AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM
-};
-
-static ad1848_info adev_info[MAX_AUDIO_DEV];
-
-#define io_Index_Addr(d) ((d)->base)
-#define io_Indexed_Data(d) ((d)->base+1)
-#define io_Status(d) ((d)->base+2)
-#define io_Polled_IO(d) ((d)->base+3)
-
-static struct {
- unsigned char flags;
-#define CAP_F_TIMER 0x01
-} capabilities [10 /*devc->model */ ] = {
- {0}
- ,{0} /* MD_1848 */
- ,{CAP_F_TIMER} /* MD_4231 */
- ,{CAP_F_TIMER} /* MD_4231A */
- ,{CAP_F_TIMER} /* MD_1845 */
- ,{CAP_F_TIMER} /* MD_4232 */
- ,{0} /* MD_C930 */
- ,{CAP_F_TIMER} /* MD_IWAVE */
- ,{0} /* MD_4235 */
- ,{CAP_F_TIMER} /* MD_1845_SSCAPE */
-};
-
-#ifdef CONFIG_PNP
-static int isapnp = 1;
-static int isapnpjump;
-static int reverse;
-
-static int audio_activated;
-#else
-static int isapnp;
-#endif
-
-
-
-static int ad1848_open(int dev, int mode);
-static void ad1848_close(int dev);
-static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag);
-static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag);
-static int ad1848_prepare_for_output(int dev, int bsize, int bcount);
-static int ad1848_prepare_for_input(int dev, int bsize, int bcount);
-static void ad1848_halt(int dev);
-static void ad1848_halt_input(int dev);
-static void ad1848_halt_output(int dev);
-static void ad1848_trigger(int dev, int bits);
-static irqreturn_t adintr(int irq, void *dev_id);
-
-#ifndef EXCLUDE_TIMERS
-static int ad1848_tmr_install(int dev);
-static void ad1848_tmr_reprogram(int dev);
-#endif
-
-static int ad_read(ad1848_info * devc, int reg)
-{
- int x;
- int timeout = 900000;
-
- while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */
- timeout--;
-
- if(reg < 32)
- {
- outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
- x = inb(io_Indexed_Data(devc));
- }
- else
- {
- int xreg, xra;
-
- xreg = (reg & 0xff) - 32;
- xra = (((xreg & 0x0f) << 4) & 0xf0) | 0x08 | ((xreg & 0x10) >> 2);
- outb(((unsigned char) (23 & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
- outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc));
- x = inb(io_Indexed_Data(devc));
- }
-
- return x;
-}
-
-static void ad_write(ad1848_info * devc, int reg, int data)
-{
- int timeout = 900000;
-
- while (timeout > 0 && inb(devc->base) == 0x80) /* Are we initializing */
- timeout--;
-
- if(reg < 32)
- {
- outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
- outb(((unsigned char) (data & 0xff)), io_Indexed_Data(devc));
- }
- else
- {
- int xreg, xra;
-
- xreg = (reg & 0xff) - 32;
- xra = (((xreg & 0x0f) << 4) & 0xf0) | 0x08 | ((xreg & 0x10) >> 2);
- outb(((unsigned char) (23 & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
- outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc));
- outb((unsigned char) (data & 0xff), io_Indexed_Data(devc));
- }
-}
-
-static void wait_for_calibration(ad1848_info * devc)
-{
- int timeout = 0;
-
- /*
- * Wait until the auto calibration process has finished.
- *
- * 1) Wait until the chip becomes ready (reads don't return 0x80).
- * 2) Wait until the ACI bit of I11 gets on and then off.
- */
-
- timeout = 100000;
- while (timeout > 0 && inb(devc->base) == 0x80)
- timeout--;
- if (inb(devc->base) & 0x80)
- printk(KERN_WARNING "ad1848: Auto calibration timed out(1).\n");
-
- timeout = 100;
- while (timeout > 0 && !(ad_read(devc, 11) & 0x20))
- timeout--;
- if (!(ad_read(devc, 11) & 0x20))
- return;
-
- timeout = 80000;
- while (timeout > 0 && (ad_read(devc, 11) & 0x20))
- timeout--;
- if (ad_read(devc, 11) & 0x20)
- if ((devc->model != MD_1845) && (devc->model != MD_1845_SSCAPE))
- printk(KERN_WARNING "ad1848: Auto calibration timed out(3).\n");
-}
-
-static void ad_mute(ad1848_info * devc)
-{
- int i;
- unsigned char prev;
-
- /*
- * Save old register settings and mute output channels
- */
-
- for (i = 6; i < 8; i++)
- {
- prev = devc->saved_regs[i] = ad_read(devc, i);
- }
-
-}
-
-static void ad_unmute(ad1848_info * devc)
-{
-}
-
-static void ad_enter_MCE(ad1848_info * devc)
-{
- int timeout = 1000;
- unsigned short prev;
-
- while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */
- timeout--;
-
- devc->MCE_bit = 0x40;
- prev = inb(io_Index_Addr(devc));
- if (prev & 0x40)
- {
- return;
- }
- outb((devc->MCE_bit), io_Index_Addr(devc));
-}
-
-static void ad_leave_MCE(ad1848_info * devc)
-{
- unsigned char prev, acal;
- int timeout = 1000;
-
- while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */
- timeout--;
-
- acal = ad_read(devc, 9);
-
- devc->MCE_bit = 0x00;
- prev = inb(io_Index_Addr(devc));
- outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */
-
- if ((prev & 0x40) == 0) /* Not in MCE mode */
- {
- return;
- }
- outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */
- if (acal & 0x08) /* Auto calibration is enabled */
- wait_for_calibration(devc);
-}
-
-static int ad1848_set_recmask(ad1848_info * devc, int mask)
-{
- unsigned char recdev;
- int i, n;
- unsigned long flags;
-
- mask &= devc->supported_rec_devices;
-
- /* Rename the mixer bits if necessary */
- for (i = 0; i < 32; i++)
- {
- if (devc->mixer_reroute[i] != i)
- {
- if (mask & (1 << i))
- {
- mask &= ~(1 << i);
- mask |= (1 << devc->mixer_reroute[i]);
- }
- }
- }
-
- n = 0;
- for (i = 0; i < 32; i++) /* Count selected device bits */
- if (mask & (1 << i))
- n++;
-
- spin_lock_irqsave(&devc->lock,flags);
- if (!soundpro) {
- if (n == 0)
- mask = SOUND_MASK_MIC;
- else if (n != 1) { /* Too many devices selected */
- mask &= ~devc->recmask; /* Filter out active settings */
-
- n = 0;
- for (i = 0; i < 32; i++) /* Count selected device bits */
- if (mask & (1 << i))
- n++;
-
- if (n != 1)
- mask = SOUND_MASK_MIC;
- }
- switch (mask) {
- case SOUND_MASK_MIC:
- recdev = 2;
- break;
-
- case SOUND_MASK_LINE:
- case SOUND_MASK_LINE3:
- recdev = 0;
- break;
-
- case SOUND_MASK_CD:
- case SOUND_MASK_LINE1:
- recdev = 1;
- break;
-
- case SOUND_MASK_IMIX:
- recdev = 3;
- break;
-
- default:
- mask = SOUND_MASK_MIC;
- recdev = 2;
- }
-
- recdev <<= 6;
- ad_write(devc, 0, (ad_read(devc, 0) & 0x3f) | recdev);
- ad_write(devc, 1, (ad_read(devc, 1) & 0x3f) | recdev);
- } else { /* soundpro */
- unsigned char val;
- int set_rec_bit;
- int j;
-
- for (i = 0; i < 32; i++) { /* For each bit */
- if ((devc->supported_rec_devices & (1 << i)) == 0)
- continue; /* Device not supported */
-
- for (j = LEFT_CHN; j <= RIGHT_CHN; j++) {
- if (devc->mix_devices[i][j].nbits == 0) /* Inexistent channel */
- continue;
-
- /*
- * This is tricky:
- * set_rec_bit becomes 1 if the corresponding bit in mask is set
- * then it gets flipped if the polarity is inverse
- */
- set_rec_bit = ((mask & (1 << i)) != 0) ^ devc->mix_devices[i][j].recpol;
-
- val = ad_read(devc, devc->mix_devices[i][j].recreg);
- val &= ~(1 << devc->mix_devices[i][j].recpos);
- val |= (set_rec_bit << devc->mix_devices[i][j].recpos);
- ad_write(devc, devc->mix_devices[i][j].recreg, val);
- }
- }
- }
- spin_unlock_irqrestore(&devc->lock,flags);
-
- /* Rename the mixer bits back if necessary */
- for (i = 0; i < 32; i++)
- {
- if (devc->mixer_reroute[i] != i)
- {
- if (mask & (1 << devc->mixer_reroute[i]))
- {
- mask &= ~(1 << devc->mixer_reroute[i]);
- mask |= (1 << i);
- }
- }
- }
- devc->recmask = mask;
- return mask;
-}
-
-static void change_bits(ad1848_info * devc, unsigned char *regval,
- unsigned char *muteval, int dev, int chn, int newval)
-{
- unsigned char mask;
- int shift;
- int mute;
- int mutemask;
- int set_mute_bit;
-
- set_mute_bit = (newval == 0) ^ devc->mix_devices[dev][chn].mutepol;
-
- if (devc->mix_devices[dev][chn].polarity == 1) /* Reverse */
- newval = 100 - newval;
-
- mask = (1 << devc->mix_devices[dev][chn].nbits) - 1;
- shift = devc->mix_devices[dev][chn].bitpos;
-
- if (devc->mix_devices[dev][chn].mutepos == 8)
- { /* if there is no mute bit */
- mute = 0; /* No mute bit; do nothing special */
- mutemask = ~0; /* No mute bit; do nothing special */
- }
- else
- {
- mute = (set_mute_bit << devc->mix_devices[dev][chn].mutepos);
- mutemask = ~(1 << devc->mix_devices[dev][chn].mutepos);
- }
-
- newval = (int) ((newval * mask) + 50) / 100; /* Scale it */
- *regval &= ~(mask << shift); /* Clear bits */
- *regval |= (newval & mask) << shift; /* Set new value */
-
- *muteval &= mutemask;
- *muteval |= mute;
-}
-
-static int ad1848_mixer_get(ad1848_info * devc, int dev)
-{
- if (!((1 << dev) & devc->supported_devices))
- return -EINVAL;
-
- dev = devc->mixer_reroute[dev];
-
- return devc->levels[dev];
-}
-
-static void ad1848_mixer_set_channel(ad1848_info *devc, int dev, int value, int channel)
-{
- int regoffs, muteregoffs;
- unsigned char val, muteval;
- unsigned long flags;
-
- regoffs = devc->mix_devices[dev][channel].regno;
- muteregoffs = devc->mix_devices[dev][channel].mutereg;
- val = ad_read(devc, regoffs);
-
- if (muteregoffs != regoffs) {
- muteval = ad_read(devc, muteregoffs);
- change_bits(devc, &val, &muteval, dev, channel, value);
- }
- else
- change_bits(devc, &val, &val, dev, channel, value);
-
- spin_lock_irqsave(&devc->lock,flags);
- ad_write(devc, regoffs, val);
- devc->saved_regs[regoffs] = val;
- if (muteregoffs != regoffs) {
- ad_write(devc, muteregoffs, muteval);
- devc->saved_regs[muteregoffs] = muteval;
- }
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static int ad1848_mixer_set(ad1848_info * devc, int dev, int value)
-{
- int left = value & 0x000000ff;
- int right = (value & 0x0000ff00) >> 8;
- int retvol;
-
- if (dev > 31)
- return -EINVAL;
-
- if (!(devc->supported_devices & (1 << dev)))
- return -EINVAL;
-
- dev = devc->mixer_reroute[dev];
-
- if (devc->mix_devices[dev][LEFT_CHN].nbits == 0)
- return -EINVAL;
-
- if (left > 100)
- left = 100;
- if (right > 100)
- right = 100;
-
- if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) /* Mono control */
- right = left;
-
- retvol = left | (right << 8);
-
- /* Scale volumes */
- left = mix_cvt[left];
- right = mix_cvt[right];
-
- devc->levels[dev] = retvol;
-
- /*
- * Set the left channel
- */
- ad1848_mixer_set_channel(devc, dev, left, LEFT_CHN);
-
- /*
- * Set the right channel
- */
- if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0)
- goto out;
- ad1848_mixer_set_channel(devc, dev, right, RIGHT_CHN);
-
- out:
- return retvol;
-}
-
-static void ad1848_mixer_reset(ad1848_info * devc)
-{
- int i;
- char name[32];
- unsigned long flags;
-
- devc->mix_devices = &(ad1848_mix_devices[0]);
-
- sprintf(name, "%s_%d", devc->chip_name, nr_ad1848_devs);
-
- for (i = 0; i < 32; i++)
- devc->mixer_reroute[i] = i;
-
- devc->supported_rec_devices = MODE1_REC_DEVICES;
-
- switch (devc->model)
- {
- case MD_4231:
- case MD_4231A:
- case MD_1845:
- case MD_1845_SSCAPE:
- devc->supported_devices = MODE2_MIXER_DEVICES;
- break;
-
- case MD_C930:
- devc->supported_devices = C930_MIXER_DEVICES;
- devc->mix_devices = &(c930_mix_devices[0]);
- break;
-
- case MD_IWAVE:
- devc->supported_devices = MODE3_MIXER_DEVICES;
- devc->mix_devices = &(iwave_mix_devices[0]);
- break;
-
- case MD_42xB:
- case MD_4239:
- devc->mix_devices = &(cs42xb_mix_devices[0]);
- devc->supported_devices = MODE3_MIXER_DEVICES;
- break;
- case MD_4232:
- case MD_4235:
- case MD_4236:
- devc->supported_devices = MODE3_MIXER_DEVICES;
- break;
-
- case MD_1848:
- if (soundpro) {
- devc->supported_devices = SPRO_MIXER_DEVICES;
- devc->supported_rec_devices = SPRO_REC_DEVICES;
- devc->mix_devices = &(spro_mix_devices[0]);
- break;
- }
-
- default:
- devc->supported_devices = MODE1_MIXER_DEVICES;
- }
-
- devc->orig_devices = devc->supported_devices;
- devc->orig_rec_devices = devc->supported_rec_devices;
-
- devc->levels = load_mixer_volumes(name, default_mixer_levels, 1);
-
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- {
- if (devc->supported_devices & (1 << i))
- ad1848_mixer_set(devc, i, devc->levels[i]);
- }
-
- ad1848_set_recmask(devc, SOUND_MASK_MIC);
-
- devc->mixer_output_port = devc->levels[31] | AUDIO_HEADPHONE | AUDIO_LINE_OUT;
-
- spin_lock_irqsave(&devc->lock,flags);
- if (!soundpro) {
- if (devc->mixer_output_port & AUDIO_SPEAKER)
- ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */
- else
- ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */
- } else {
- /*
- * From the "wouldn't it be nice if the mixer API had (better)
- * support for custom stuff" category
- */
- /* Enable surround mode and SB16 mixer */
- ad_write(devc, 16, 0x60);
- }
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static int ad1848_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- ad1848_info *devc = mixer_devs[dev]->devc;
- int val;
-
- if (cmd == SOUND_MIXER_PRIVATE1)
- {
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
-
- if (val != 0xffff)
- {
- unsigned long flags;
- val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT);
- devc->mixer_output_port = val;
- val |= AUDIO_HEADPHONE | AUDIO_LINE_OUT; /* Always on */
- devc->mixer_output_port = val;
- spin_lock_irqsave(&devc->lock,flags);
- if (val & AUDIO_SPEAKER)
- ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */
- else
- ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */
- spin_unlock_irqrestore(&devc->lock,flags);
- }
- val = devc->mixer_output_port;
- return put_user(val, (int __user *)arg);
- }
- if (cmd == SOUND_MIXER_PRIVATE2)
- {
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
- return(ad1848_control(AD1848_MIXER_REROUTE, val));
- }
- if (((cmd >> 8) & 0xff) == 'M')
- {
- if (_SIOC_DIR(cmd) & _SIOC_WRITE)
- {
- switch (cmd & 0xff)
- {
- case SOUND_MIXER_RECSRC:
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
- val = ad1848_set_recmask(devc, val);
- break;
-
- default:
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
- val = ad1848_mixer_set(devc, cmd & 0xff, val);
- break;
- }
- return put_user(val, (int __user *)arg);
- }
- else
- {
- switch (cmd & 0xff)
- {
- /*
- * Return parameters
- */
-
- case SOUND_MIXER_RECSRC:
- val = devc->recmask;
- break;
-
- case SOUND_MIXER_DEVMASK:
- val = devc->supported_devices;
- break;
-
- case SOUND_MIXER_STEREODEVS:
- val = devc->supported_devices;
- if (devc->model != MD_C930)
- val &= ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
- break;
-
- case SOUND_MIXER_RECMASK:
- val = devc->supported_rec_devices;
- break;
-
- case SOUND_MIXER_CAPS:
- val=SOUND_CAP_EXCL_INPUT;
- break;
-
- default:
- val = ad1848_mixer_get(devc, cmd & 0xff);
- break;
- }
- return put_user(val, (int __user *)arg);
- }
- }
- else
- return -EINVAL;
-}
-
-static int ad1848_set_speed(int dev, int arg)
-{
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- /*
- * The sampling speed is encoded in the least significant nibble of I8. The
- * LSB selects the clock source (0=24.576 MHz, 1=16.9344 MHz) and other
- * three bits select the divisor (indirectly):
- *
- * The available speeds are in the following table. Keep the speeds in
- * the increasing order.
- */
- typedef struct
- {
- int speed;
- unsigned char bits;
- }
- speed_struct;
-
- static speed_struct speed_table[] =
- {
- {5510, (0 << 1) | 1},
- {5510, (0 << 1) | 1},
- {6620, (7 << 1) | 1},
- {8000, (0 << 1) | 0},
- {9600, (7 << 1) | 0},
- {11025, (1 << 1) | 1},
- {16000, (1 << 1) | 0},
- {18900, (2 << 1) | 1},
- {22050, (3 << 1) | 1},
- {27420, (2 << 1) | 0},
- {32000, (3 << 1) | 0},
- {33075, (6 << 1) | 1},
- {37800, (4 << 1) | 1},
- {44100, (5 << 1) | 1},
- {48000, (6 << 1) | 0}
- };
-
- int i, n, selected = -1;
-
- n = sizeof(speed_table) / sizeof(speed_struct);
-
- if (arg <= 0)
- return portc->speed;
-
- if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) /* AD1845 has different timer than others */
- {
- if (arg < 4000)
- arg = 4000;
- if (arg > 50000)
- arg = 50000;
-
- portc->speed = arg;
- portc->speed_bits = speed_table[3].bits;
- return portc->speed;
- }
- if (arg < speed_table[0].speed)
- selected = 0;
- if (arg > speed_table[n - 1].speed)
- selected = n - 1;
-
- for (i = 1 /*really */ ; selected == -1 && i < n; i++)
- {
- if (speed_table[i].speed == arg)
- selected = i;
- else if (speed_table[i].speed > arg)
- {
- int diff1, diff2;
-
- diff1 = arg - speed_table[i - 1].speed;
- diff2 = speed_table[i].speed - arg;
-
- if (diff1 < diff2)
- selected = i - 1;
- else
- selected = i;
- }
- }
- if (selected == -1)
- {
- printk(KERN_WARNING "ad1848: Can't find speed???\n");
- selected = 3;
- }
- portc->speed = speed_table[selected].speed;
- portc->speed_bits = speed_table[selected].bits;
- return portc->speed;
-}
-
-static short ad1848_set_channels(int dev, short arg)
-{
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- if (arg != 1 && arg != 2)
- return portc->channels;
-
- portc->channels = arg;
- return arg;
-}
-
-static unsigned int ad1848_set_bits(int dev, unsigned int arg)
-{
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- static struct format_tbl
- {
- int format;
- unsigned char bits;
- }
- format2bits[] =
- {
- {
- 0, 0
- }
- ,
- {
- AFMT_MU_LAW, 1
- }
- ,
- {
- AFMT_A_LAW, 3
- }
- ,
- {
- AFMT_IMA_ADPCM, 5
- }
- ,
- {
- AFMT_U8, 0
- }
- ,
- {
- AFMT_S16_LE, 2
- }
- ,
- {
- AFMT_S16_BE, 6
- }
- ,
- {
- AFMT_S8, 0
- }
- ,
- {
- AFMT_U16_LE, 0
- }
- ,
- {
- AFMT_U16_BE, 0
- }
- };
- int i, n = sizeof(format2bits) / sizeof(struct format_tbl);
-
- if (arg == 0)
- return portc->audio_format;
-
- if (!(arg & ad_format_mask[devc->model]))
- arg = AFMT_U8;
-
- portc->audio_format = arg;
-
- for (i = 0; i < n; i++)
- if (format2bits[i].format == arg)
- {
- if ((portc->format_bits = format2bits[i].bits) == 0)
- return portc->audio_format = AFMT_U8; /* Was not supported */
-
- return arg;
- }
- /* Still hanging here. Something must be terribly wrong */
- portc->format_bits = 0;
- return portc->audio_format = AFMT_U8;
-}
-
-static struct audio_driver ad1848_audio_driver =
-{
- .owner = THIS_MODULE,
- .open = ad1848_open,
- .close = ad1848_close,
- .output_block = ad1848_output_block,
- .start_input = ad1848_start_input,
- .prepare_for_input = ad1848_prepare_for_input,
- .prepare_for_output = ad1848_prepare_for_output,
- .halt_io = ad1848_halt,
- .halt_input = ad1848_halt_input,
- .halt_output = ad1848_halt_output,
- .trigger = ad1848_trigger,
- .set_speed = ad1848_set_speed,
- .set_bits = ad1848_set_bits,
- .set_channels = ad1848_set_channels
-};
-
-static struct mixer_operations ad1848_mixer_operations =
-{
- .owner = THIS_MODULE,
- .id = "SOUNDPORT",
- .name = "AD1848/CS4248/CS4231",
- .ioctl = ad1848_mixer_ioctl
-};
-
-static int ad1848_open(int dev, int mode)
-{
- ad1848_info *devc;
- ad1848_port_info *portc;
- unsigned long flags;
-
- if (dev < 0 || dev >= num_audiodevs)
- return -ENXIO;
-
- devc = (ad1848_info *) audio_devs[dev]->devc;
- portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- /* here we don't have to protect against intr */
- spin_lock(&devc->lock);
- if (portc->open_mode || (devc->open_mode & mode))
- {
- spin_unlock(&devc->lock);
- return -EBUSY;
- }
- devc->dual_dma = 0;
-
- if (audio_devs[dev]->flags & DMA_DUPLEX)
- {
- devc->dual_dma = 1;
- }
- devc->intr_active = 0;
- devc->audio_mode = 0;
- devc->open_mode |= mode;
- portc->open_mode = mode;
- spin_unlock(&devc->lock);
- ad1848_trigger(dev, 0);
-
- if (mode & OPEN_READ)
- devc->record_dev = dev;
- if (mode & OPEN_WRITE)
- devc->playback_dev = dev;
-/*
- * Mute output until the playback really starts. This decreases clicking (hope so).
- */
- spin_lock_irqsave(&devc->lock,flags);
- ad_mute(devc);
- spin_unlock_irqrestore(&devc->lock,flags);
-
- return 0;
-}
-
-static void ad1848_close(int dev)
-{
- unsigned long flags;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- DEB(printk("ad1848_close(void)\n"));
-
- devc->intr_active = 0;
- ad1848_halt(dev);
-
- spin_lock_irqsave(&devc->lock,flags);
-
- devc->audio_mode = 0;
- devc->open_mode &= ~portc->open_mode;
- portc->open_mode = 0;
-
- ad_unmute(devc);
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag)
-{
- unsigned long flags, cnt;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- cnt = count;
-
- if (portc->audio_format == AFMT_IMA_ADPCM)
- {
- cnt /= 4;
- }
- else
- {
- if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
- cnt >>= 1;
- }
- if (portc->channels > 1)
- cnt >>= 1;
- cnt--;
-
- if ((devc->audio_mode & PCM_ENABLE_OUTPUT) && (audio_devs[dev]->flags & DMA_AUTOMODE) &&
- intrflag &&
- cnt == devc->xfer_count)
- {
- devc->audio_mode |= PCM_ENABLE_OUTPUT;
- devc->intr_active = 1;
- return; /*
- * Auto DMA mode on. No need to react
- */
- }
- spin_lock_irqsave(&devc->lock,flags);
-
- ad_write(devc, 15, (unsigned char) (cnt & 0xff));
- ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
-
- devc->xfer_count = cnt;
- devc->audio_mode |= PCM_ENABLE_OUTPUT;
- devc->intr_active = 1;
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag)
-{
- unsigned long flags, cnt;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- cnt = count;
- if (portc->audio_format == AFMT_IMA_ADPCM)
- {
- cnt /= 4;
- }
- else
- {
- if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
- cnt >>= 1;
- }
- if (portc->channels > 1)
- cnt >>= 1;
- cnt--;
-
- if ((devc->audio_mode & PCM_ENABLE_INPUT) && (audio_devs[dev]->flags & DMA_AUTOMODE) &&
- intrflag &&
- cnt == devc->xfer_count)
- {
- devc->audio_mode |= PCM_ENABLE_INPUT;
- devc->intr_active = 1;
- return; /*
- * Auto DMA mode on. No need to react
- */
- }
- spin_lock_irqsave(&devc->lock,flags);
-
- if (devc->model == MD_1848)
- {
- ad_write(devc, 15, (unsigned char) (cnt & 0xff));
- ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
- }
- else
- {
- ad_write(devc, 31, (unsigned char) (cnt & 0xff));
- ad_write(devc, 30, (unsigned char) ((cnt >> 8) & 0xff));
- }
-
- ad_unmute(devc);
-
- devc->xfer_count = cnt;
- devc->audio_mode |= PCM_ENABLE_INPUT;
- devc->intr_active = 1;
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static int ad1848_prepare_for_output(int dev, int bsize, int bcount)
-{
- int timeout;
- unsigned char fs, old_fs, tmp = 0;
- unsigned long flags;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- ad_mute(devc);
-
- spin_lock_irqsave(&devc->lock,flags);
- fs = portc->speed_bits | (portc->format_bits << 5);
-
- if (portc->channels > 1)
- fs |= 0x10;
-
- ad_enter_MCE(devc); /* Enables changes to the format select reg */
-
- if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) /* Use alternate speed select registers */
- {
- fs &= 0xf0; /* Mask off the rate select bits */
-
- ad_write(devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */
- ad_write(devc, 23, portc->speed & 0xff); /* Speed LSB */
- }
- old_fs = ad_read(devc, 8);
-
- if (devc->model == MD_4232 || devc->model >= MD_4236)
- {
- tmp = ad_read(devc, 16);
- ad_write(devc, 16, tmp | 0x30);
- }
- if (devc->model == MD_IWAVE)
- ad_write(devc, 17, 0xc2); /* Disable variable frequency select */
-
- ad_write(devc, 8, fs);
-
- /*
- * Write to I8 starts resynchronization. Wait until it completes.
- */
-
- timeout = 0;
- while (timeout < 100 && inb(devc->base) != 0x80)
- timeout++;
- timeout = 0;
- while (timeout < 10000 && inb(devc->base) == 0x80)
- timeout++;
-
- if (devc->model >= MD_4232)
- ad_write(devc, 16, tmp & ~0x30);
-
- ad_leave_MCE(devc); /*
- * Starts the calibration process.
- */
- spin_unlock_irqrestore(&devc->lock,flags);
- devc->xfer_count = 0;
-
-#ifndef EXCLUDE_TIMERS
- if (dev == timer_installed && devc->timer_running)
- if ((fs & 0x01) != (old_fs & 0x01))
- {
- ad1848_tmr_reprogram(dev);
- }
-#endif
- ad1848_halt_output(dev);
- return 0;
-}
-
-static int ad1848_prepare_for_input(int dev, int bsize, int bcount)
-{
- int timeout;
- unsigned char fs, old_fs, tmp = 0;
- unsigned long flags;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- if (devc->audio_mode)
- return 0;
-
- spin_lock_irqsave(&devc->lock,flags);
- fs = portc->speed_bits | (portc->format_bits << 5);
-
- if (portc->channels > 1)
- fs |= 0x10;
-
- ad_enter_MCE(devc); /* Enables changes to the format select reg */
-
- if ((devc->model == MD_1845) || (devc->model == MD_1845_SSCAPE)) /* Use alternate speed select registers */
- {
- fs &= 0xf0; /* Mask off the rate select bits */
-
- ad_write(devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */
- ad_write(devc, 23, portc->speed & 0xff); /* Speed LSB */
- }
- if (devc->model == MD_4232)
- {
- tmp = ad_read(devc, 16);
- ad_write(devc, 16, tmp | 0x30);
- }
- if (devc->model == MD_IWAVE)
- ad_write(devc, 17, 0xc2); /* Disable variable frequency select */
-
- /*
- * If mode >= 2 (CS4231), set I28. It's the capture format register.
- */
-
- if (devc->model != MD_1848)
- {
- old_fs = ad_read(devc, 28);
- ad_write(devc, 28, fs);
-
- /*
- * Write to I28 starts resynchronization. Wait until it completes.
- */
-
- timeout = 0;
- while (timeout < 100 && inb(devc->base) != 0x80)
- timeout++;
-
- timeout = 0;
- while (timeout < 10000 && inb(devc->base) == 0x80)
- timeout++;
-
- if (devc->model != MD_1848 && devc->model != MD_1845 && devc->model != MD_1845_SSCAPE)
- {
- /*
- * CS4231 compatible devices don't have separate sampling rate selection
- * register for recording an playback. The I8 register is shared so we have to
- * set the speed encoding bits of it too.
- */
- unsigned char tmp = portc->speed_bits | (ad_read(devc, 8) & 0xf0);
-
- ad_write(devc, 8, tmp);
- /*
- * Write to I8 starts resynchronization. Wait until it completes.
- */
- timeout = 0;
- while (timeout < 100 && inb(devc->base) != 0x80)
- timeout++;
-
- timeout = 0;
- while (timeout < 10000 && inb(devc->base) == 0x80)
- timeout++;
- }
- }
- else
- { /* For AD1848 set I8. */
-
- old_fs = ad_read(devc, 8);
- ad_write(devc, 8, fs);
- /*
- * Write to I8 starts resynchronization. Wait until it completes.
- */
- timeout = 0;
- while (timeout < 100 && inb(devc->base) != 0x80)
- timeout++;
- timeout = 0;
- while (timeout < 10000 && inb(devc->base) == 0x80)
- timeout++;
- }
-
- if (devc->model == MD_4232)
- ad_write(devc, 16, tmp & ~0x30);
-
- ad_leave_MCE(devc); /*
- * Starts the calibration process.
- */
- spin_unlock_irqrestore(&devc->lock,flags);
- devc->xfer_count = 0;
-
-#ifndef EXCLUDE_TIMERS
- if (dev == timer_installed && devc->timer_running)
- {
- if ((fs & 0x01) != (old_fs & 0x01))
- {
- ad1848_tmr_reprogram(dev);
- }
- }
-#endif
- ad1848_halt_input(dev);
- return 0;
-}
-
-static void ad1848_halt(int dev)
-{
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
-
- unsigned char bits = ad_read(devc, 9);
-
- if (bits & 0x01 && (portc->open_mode & OPEN_WRITE))
- ad1848_halt_output(dev);
-
- if (bits & 0x02 && (portc->open_mode & OPEN_READ))
- ad1848_halt_input(dev);
- devc->audio_mode = 0;
-}
-
-static void ad1848_halt_input(int dev)
-{
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- unsigned long flags;
-
- if (!(ad_read(devc, 9) & 0x02))
- return; /* Capture not enabled */
-
- spin_lock_irqsave(&devc->lock,flags);
-
- ad_mute(devc);
-
- {
- int tmout;
-
- if(!isa_dma_bridge_buggy)
- disable_dma(audio_devs[dev]->dmap_in->dma);
-
- for (tmout = 0; tmout < 100000; tmout++)
- if (ad_read(devc, 11) & 0x10)
- break;
- ad_write(devc, 9, ad_read(devc, 9) & ~0x02); /* Stop capture */
-
- if(!isa_dma_bridge_buggy)
- enable_dma(audio_devs[dev]->dmap_in->dma);
- devc->audio_mode &= ~PCM_ENABLE_INPUT;
- }
-
- outb(0, io_Status(devc)); /* Clear interrupt status */
- outb(0, io_Status(devc)); /* Clear interrupt status */
-
- devc->audio_mode &= ~PCM_ENABLE_INPUT;
-
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static void ad1848_halt_output(int dev)
-{
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- unsigned long flags;
-
- if (!(ad_read(devc, 9) & 0x01))
- return; /* Playback not enabled */
-
- spin_lock_irqsave(&devc->lock,flags);
-
- ad_mute(devc);
- {
- int tmout;
-
- if(!isa_dma_bridge_buggy)
- disable_dma(audio_devs[dev]->dmap_out->dma);
-
- for (tmout = 0; tmout < 100000; tmout++)
- if (ad_read(devc, 11) & 0x10)
- break;
- ad_write(devc, 9, ad_read(devc, 9) & ~0x01); /* Stop playback */
-
- if(!isa_dma_bridge_buggy)
- enable_dma(audio_devs[dev]->dmap_out->dma);
-
- devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
- }
-
- outb((0), io_Status(devc)); /* Clear interrupt status */
- outb((0), io_Status(devc)); /* Clear interrupt status */
-
- devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
-
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static void ad1848_trigger(int dev, int state)
-{
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
- unsigned long flags;
- unsigned char tmp, old;
-
- spin_lock_irqsave(&devc->lock,flags);
- state &= devc->audio_mode;
-
- tmp = old = ad_read(devc, 9);
-
- if (portc->open_mode & OPEN_READ)
- {
- if (state & PCM_ENABLE_INPUT)
- tmp |= 0x02;
- else
- tmp &= ~0x02;
- }
- if (portc->open_mode & OPEN_WRITE)
- {
- if (state & PCM_ENABLE_OUTPUT)
- tmp |= 0x01;
- else
- tmp &= ~0x01;
- }
- /* ad_mute(devc); */
- if (tmp != old)
- {
- ad_write(devc, 9, tmp);
- ad_unmute(devc);
- }
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static void ad1848_init_hw(ad1848_info * devc)
-{
- int i;
- int *init_values;
-
- /*
- * Initial values for the indirect registers of CS4248/AD1848.
- */
- static int init_values_a[] =
- {
- 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
- 0x00, 0x0c, 0x02, 0x00, 0x8a, 0x01, 0x00, 0x00,
-
- /* Positions 16 to 31 just for CS4231/2 and ad1845 */
- 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x1f, 0x40,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
-
- static int init_values_b[] =
- {
- /*
- Values for the newer chips
- Some of the register initialization values were changed. In
- order to get rid of the click that preceded PCM playback,
- calibration was disabled on the 10th byte. On that same byte,
- dual DMA was enabled; on the 11th byte, ADC dithering was
- enabled, since that is theoretically desirable; on the 13th
- byte, Mode 3 was selected, to enable access to extended
- registers.
- */
- 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
- 0x00, 0x00, 0x06, 0x00, 0xe0, 0x01, 0x00, 0x00,
- 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x1f, 0x40,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- };
-
- /*
- * Select initialisation data
- */
-
- init_values = init_values_a;
- if(devc->model >= MD_4236)
- init_values = init_values_b;
-
- for (i = 0; i < 16; i++)
- ad_write(devc, i, init_values[i]);
-
-
- ad_mute(devc); /* Initialize some variables */
- ad_unmute(devc); /* Leave it unmuted now */
-
- if (devc->model > MD_1848)
- {
- if (devc->model == MD_1845_SSCAPE)
- ad_write(devc, 12, ad_read(devc, 12) | 0x50);
- else
- ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */
-
- if (devc->model == MD_IWAVE)
- ad_write(devc, 12, 0x6c); /* Select codec mode 3 */
-
- if (devc->model != MD_1845_SSCAPE)
- for (i = 16; i < 32; i++)
- ad_write(devc, i, init_values[i]);
-
- if (devc->model == MD_IWAVE)
- ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */
- }
- if (devc->model > MD_1848)
- {
- if (devc->audio_flags & DMA_DUPLEX)
- ad_write(devc, 9, ad_read(devc, 9) & ~0x04); /* Dual DMA mode */
- else
- ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */
-
- if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE)
- ad_write(devc, 27, ad_read(devc, 27) | 0x08); /* Alternate freq select enabled */
-
- if (devc->model == MD_IWAVE)
- { /* Some magic Interwave specific initialization */
- ad_write(devc, 12, 0x6c); /* Select codec mode 3 */
- ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */
- ad_write(devc, 17, 0xc2); /* Alternate feature enable */
- }
- }
- else
- {
- devc->audio_flags &= ~DMA_DUPLEX;
- ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */
- if (soundpro)
- ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */
- }
-
- outb((0), io_Status(devc)); /* Clear pending interrupts */
-
- /*
- * Toggle the MCE bit. It completes the initialization phase.
- */
-
- ad_enter_MCE(devc); /* In case the bit was off */
- ad_leave_MCE(devc);
-
- ad1848_mixer_reset(devc);
-}
-
-int ad1848_detect(struct resource *ports, int *ad_flags, int *osp)
-{
- unsigned char tmp;
- ad1848_info *devc = &adev_info[nr_ad1848_devs];
- unsigned char tmp1 = 0xff, tmp2 = 0xff;
- int optiC930 = 0; /* OPTi 82C930 flag */
- int interwave = 0;
- int ad1847_flag = 0;
- int cs4248_flag = 0;
- int sscape_flag = 0;
- int io_base = ports->start;
-
- int i;
-
- DDB(printk("ad1848_detect(%x)\n", io_base));
-
- if (ad_flags)
- {
- if (*ad_flags == 0x12345678)
- {
- interwave = 1;
- *ad_flags = 0;
- }
-
- if (*ad_flags == 0x87654321)
- {
- sscape_flag = 1;
- *ad_flags = 0;
- }
-
- if (*ad_flags == 0x12345677)
- {
- cs4248_flag = 1;
- *ad_flags = 0;
- }
- }
- if (nr_ad1848_devs >= MAX_AUDIO_DEV)
- {
- printk(KERN_ERR "ad1848 - Too many audio devices\n");
- return 0;
- }
- spin_lock_init(&devc->lock);
- devc->base = io_base;
- devc->irq_ok = 0;
- devc->timer_running = 0;
- devc->MCE_bit = 0x40;
- devc->irq = 0;
- devc->open_mode = 0;
- devc->chip_name = devc->name = "AD1848";
- devc->model = MD_1848; /* AD1848 or CS4248 */
- devc->levels = NULL;
- devc->debug_flag = 0;
-
- /*
- * Check that the I/O address is in use.
- *
- * The bit 0x80 of the base I/O port is known to be 0 after the
- * chip has performed its power on initialization. Just assume
- * this has happened before the OS is starting.
- *
- * If the I/O address is unused, it typically returns 0xff.
- */
-
- if (inb(devc->base) == 0xff)
- {
- DDB(printk("ad1848_detect: The base I/O address appears to be dead\n"));
- }
-
- /*
- * Wait for the device to stop initialization
- */
-
- DDB(printk("ad1848_detect() - step 0\n"));
-
- for (i = 0; i < 10000000; i++)
- {
- unsigned char x = inb(devc->base);
-
- if (x == 0xff || !(x & 0x80))
- break;
- }
-
- DDB(printk("ad1848_detect() - step A\n"));
-
- if (inb(devc->base) == 0x80) /* Not ready. Let's wait */
- ad_leave_MCE(devc);
-
- if ((inb(devc->base) & 0x80) != 0x00) /* Not a AD1848 */
- {
- DDB(printk("ad1848 detect error - step A (%02x)\n", (int) inb(devc->base)));
- return 0;
- }
-
- /*
- * Test if it's possible to change contents of the indirect registers.
- * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only
- * so try to avoid using it.
- */
-
- DDB(printk("ad1848_detect() - step B\n"));
- ad_write(devc, 0, 0xaa);
- ad_write(devc, 1, 0x45); /* 0x55 with bit 0x10 clear */
-
- if ((tmp1 = ad_read(devc, 0)) != 0xaa || (tmp2 = ad_read(devc, 1)) != 0x45)
- {
- if (tmp2 == 0x65) /* AD1847 has couple of bits hardcoded to 1 */
- ad1847_flag = 1;
- else
- {
- DDB(printk("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2));
- return 0;
- }
- }
- DDB(printk("ad1848_detect() - step C\n"));
- ad_write(devc, 0, 0x45);
- ad_write(devc, 1, 0xaa);
-
- if ((tmp1 = ad_read(devc, 0)) != 0x45 || (tmp2 = ad_read(devc, 1)) != 0xaa)
- {
- if (tmp2 == 0x8a) /* AD1847 has few bits hardcoded to 1 */
- ad1847_flag = 1;
- else
- {
- DDB(printk("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2));
- return 0;
- }
- }
-
- /*
- * The indirect register I12 has some read only bits. Let's
- * try to change them.
- */
-
- DDB(printk("ad1848_detect() - step D\n"));
- tmp = ad_read(devc, 12);
- ad_write(devc, 12, (~tmp) & 0x0f);
-
- if ((tmp & 0x0f) != ((tmp1 = ad_read(devc, 12)) & 0x0f))
- {
- DDB(printk("ad1848 detect error - step D (%x)\n", tmp1));
- return 0;
- }
-
- /*
- * NOTE! Last 4 bits of the reg I12 tell the chip revision.
- * 0x01=RevB and 0x0A=RevC.
- */
-
- /*
- * The original AD1848/CS4248 has just 15 indirect registers. This means
- * that I0 and I16 should return the same value (etc.).
- * However this doesn't work with CS4248. Actually it seems to be impossible
- * to detect if the chip is a CS4231 or CS4248.
- * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails
- * with CS4231.
- */
-
- /*
- * OPTi 82C930 has mode2 control bit in another place. This test will fail
- * with it. Accept this situation as a possible indication of this chip.
- */
-
- DDB(printk("ad1848_detect() - step F\n"));
- ad_write(devc, 12, 0); /* Mode2=disabled */
-
- for (i = 0; i < 16; i++)
- {
- if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16)))
- {
- DDB(printk("ad1848 detect step F(%d/%x/%x) - OPTi chip???\n", i, tmp1, tmp2));
- if (!ad1847_flag)
- optiC930 = 1;
- break;
- }
- }
-
- /*
- * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40).
- * The bit 0x80 is always 1 in CS4248 and CS4231.
- */
-
- DDB(printk("ad1848_detect() - step G\n"));
-
- if (ad_flags && *ad_flags == 400)
- *ad_flags = 0;
- else
- ad_write(devc, 12, 0x40); /* Set mode2, clear 0x80 */
-
-
- if (ad_flags)
- *ad_flags = 0;
-
- tmp1 = ad_read(devc, 12);
- if (tmp1 & 0x80)
- {
- if (ad_flags)
- *ad_flags |= AD_F_CS4248;
-
- devc->chip_name = "CS4248"; /* Our best knowledge just now */
- }
- if (optiC930 || (tmp1 & 0xc0) == (0x80 | 0x40))
- {
- /*
- * CS4231 detected - is it?
- *
- * Verify that setting I0 doesn't change I16.
- */
-
- DDB(printk("ad1848_detect() - step H\n"));
- ad_write(devc, 16, 0); /* Set I16 to known value */
-
- ad_write(devc, 0, 0x45);
- if ((tmp1 = ad_read(devc, 16)) != 0x45) /* No change -> CS4231? */
- {
- ad_write(devc, 0, 0xaa);
- if ((tmp1 = ad_read(devc, 16)) == 0xaa) /* Rotten bits? */
- {
- DDB(printk("ad1848 detect error - step H(%x)\n", tmp1));
- return 0;
- }
-
- /*
- * Verify that some bits of I25 are read only.
- */
-
- DDB(printk("ad1848_detect() - step I\n"));
- tmp1 = ad_read(devc, 25); /* Original bits */
- ad_write(devc, 25, ~tmp1); /* Invert all bits */
- if ((ad_read(devc, 25) & 0xe7) == (tmp1 & 0xe7))
- {
- int id;
-
- /*
- * It's at least CS4231
- */
-
- devc->chip_name = "CS4231";
- devc->model = MD_4231;
-
- /*
- * It could be an AD1845 or CS4231A as well.
- * CS4231 and AD1845 report the same revision info in I25
- * while the CS4231A reports different.
- */
-
- id = ad_read(devc, 25);
- if ((id & 0xe7) == 0x80) /* Device busy??? */
- id = ad_read(devc, 25);
- if ((id & 0xe7) == 0x80) /* Device still busy??? */
- id = ad_read(devc, 25);
- DDB(printk("ad1848_detect() - step J (%02x/%02x)\n", id, ad_read(devc, 25)));
-
- if ((id & 0xe7) == 0x80) {
- /*
- * It must be a CS4231 or AD1845. The register I23 of
- * CS4231 is undefined and it appears to be read only.
- * AD1845 uses I23 for setting sample rate. Assume
- * the chip is AD1845 if I23 is changeable.
- */
-
- unsigned char tmp = ad_read(devc, 23);
- ad_write(devc, 23, ~tmp);
-
- if (interwave)
- {
- devc->model = MD_IWAVE;
- devc->chip_name = "IWave";
- }
- else if (ad_read(devc, 23) != tmp) /* AD1845 ? */
- {
- devc->chip_name = "AD1845";
- devc->model = MD_1845;
- }
- else if (cs4248_flag)
- {
- if (ad_flags)
- *ad_flags |= AD_F_CS4248;
- devc->chip_name = "CS4248";
- devc->model = MD_1848;
- ad_write(devc, 12, ad_read(devc, 12) & ~0x40); /* Mode2 off */
- }
- ad_write(devc, 23, tmp); /* Restore */
- }
- else
- {
- switch (id & 0x1f) {
- case 3: /* CS4236/CS4235/CS42xB/CS4239 */
- {
- int xid;
- ad_write(devc, 12, ad_read(devc, 12) | 0x60); /* switch to mode 3 */
- ad_write(devc, 23, 0x9c); /* select extended register 25 */
- xid = inb(io_Indexed_Data(devc));
- ad_write(devc, 12, ad_read(devc, 12) & ~0x60); /* back to mode 0 */
- switch (xid & 0x1f)
- {
- case 0x00:
- devc->chip_name = "CS4237B(B)";
- devc->model = MD_42xB;
- break;
- case 0x08:
- /* Seems to be a 4238 ?? */
- devc->chip_name = "CS4238";
- devc->model = MD_42xB;
- break;
- case 0x09:
- devc->chip_name = "CS4238B";
- devc->model = MD_42xB;
- break;
- case 0x0b:
- devc->chip_name = "CS4236B";
- devc->model = MD_4236;
- break;
- case 0x10:
- devc->chip_name = "CS4237B";
- devc->model = MD_42xB;
- break;
- case 0x1d:
- devc->chip_name = "CS4235";
- devc->model = MD_4235;
- break;
- case 0x1e:
- devc->chip_name = "CS4239";
- devc->model = MD_4239;
- break;
- default:
- printk("Chip ident is %X.\n", xid&0x1F);
- devc->chip_name = "CS42xx";
- devc->model = MD_4232;
- break;
- }
- }
- break;
-
- case 2: /* CS4232/CS4232A */
- devc->chip_name = "CS4232";
- devc->model = MD_4232;
- break;
-
- case 0:
- if ((id & 0xe0) == 0xa0)
- {
- devc->chip_name = "CS4231A";
- devc->model = MD_4231A;
- }
- else
- {
- devc->chip_name = "CS4321";
- devc->model = MD_4231;
- }
- break;
-
- default: /* maybe */
- DDB(printk("ad1848: I25 = %02x/%02x\n", ad_read(devc, 25), ad_read(devc, 25) & 0xe7));
- if (optiC930)
- {
- devc->chip_name = "82C930";
- devc->model = MD_C930;
- }
- else
- {
- devc->chip_name = "CS4231";
- devc->model = MD_4231;
- }
- }
- }
- }
- ad_write(devc, 25, tmp1); /* Restore bits */
-
- DDB(printk("ad1848_detect() - step K\n"));
- }
- } else if (tmp1 == 0x0a) {
- /*
- * Is it perhaps a SoundPro CMI8330?
- * If so, then we should be able to change indirect registers
- * greater than I15 after activating MODE2, even though reading
- * back I12 does not show it.
- */
-
- /*
- * Let's try comparing register values
- */
- for (i = 0; i < 16; i++) {
- if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16))) {
- DDB(printk("ad1848 detect step H(%d/%x/%x) - SoundPro chip?\n", i, tmp1, tmp2));
- soundpro = 1;
- devc->chip_name = "SoundPro CMI 8330";
- break;
- }
- }
- }
-
- DDB(printk("ad1848_detect() - step L\n"));
- if (ad_flags)
- {
- if (devc->model != MD_1848)
- *ad_flags |= AD_F_CS4231;
- }
- DDB(printk("ad1848_detect() - Detected OK\n"));
-
- if (devc->model == MD_1848 && ad1847_flag)
- devc->chip_name = "AD1847";
-
-
- if (sscape_flag == 1)
- devc->model = MD_1845_SSCAPE;
-
- return 1;
-}
-
-int ad1848_init (char *name, struct resource *ports, int irq, int dma_playback,
- int dma_capture, int share_dma, int *osp, struct module *owner)
-{
- /*
- * NOTE! If irq < 0, there is another driver which has allocated the IRQ
- * so that this driver doesn't need to allocate/deallocate it.
- * The actually used IRQ is ABS(irq).
- */
-
- int my_dev;
- char dev_name[100];
- int e;
-
- ad1848_info *devc = &adev_info[nr_ad1848_devs];
-
- ad1848_port_info *portc = NULL;
-
- devc->irq = (irq > 0) ? irq : 0;
- devc->open_mode = 0;
- devc->timer_ticks = 0;
- devc->dma1 = dma_playback;
- devc->dma2 = dma_capture;
- devc->subtype = cfg.card_subtype;
- devc->audio_flags = DMA_AUTOMODE;
- devc->playback_dev = devc->record_dev = 0;
- if (name != NULL)
- devc->name = name;
-
- if (name != NULL && name[0] != 0)
- sprintf(dev_name,
- "%s (%s)", name, devc->chip_name);
- else
- sprintf(dev_name,
- "Generic audio codec (%s)", devc->chip_name);
-
- rename_region(ports, devc->name);
-
- conf_printf2(dev_name, devc->base, devc->irq, dma_playback, dma_capture);
-
- if (devc->model == MD_1848 || devc->model == MD_C930)
- devc->audio_flags |= DMA_HARDSTOP;
-
- if (devc->model > MD_1848)
- {
- if (devc->dma1 == devc->dma2 || devc->dma2 == -1 || devc->dma1 == -1)
- devc->audio_flags &= ~DMA_DUPLEX;
- else
- devc->audio_flags |= DMA_DUPLEX;
- }
-
- portc = kmalloc(sizeof(ad1848_port_info), GFP_KERNEL);
- if(portc==NULL) {
- release_region(devc->base, 4);
- return -1;
- }
-
- if ((my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
- dev_name,
- &ad1848_audio_driver,
- sizeof(struct audio_driver),
- devc->audio_flags,
- ad_format_mask[devc->model],
- devc,
- dma_playback,
- dma_capture)) < 0)
- {
- release_region(devc->base, 4);
- kfree(portc);
- return -1;
- }
-
- audio_devs[my_dev]->portc = portc;
- audio_devs[my_dev]->mixer_dev = -1;
- if (owner)
- audio_devs[my_dev]->d->owner = owner;
- memset((char *) portc, 0, sizeof(*portc));
-
- nr_ad1848_devs++;
-
- ad1848_init_hw(devc);
-
- if (irq > 0)
- {
- devc->dev_no = my_dev;
- if (request_irq(devc->irq, adintr, 0, devc->name,
- (void *)(long)my_dev) < 0)
- {
- printk(KERN_WARNING "ad1848: Unable to allocate IRQ\n");
- /* Don't free it either then.. */
- devc->irq = 0;
- }
- if (capabilities[devc->model].flags & CAP_F_TIMER)
- {
-#ifndef CONFIG_SMP
- int x;
- unsigned char tmp = ad_read(devc, 16);
-#endif
-
- devc->timer_ticks = 0;
-
- ad_write(devc, 21, 0x00); /* Timer MSB */
- ad_write(devc, 20, 0x10); /* Timer LSB */
-#ifndef CONFIG_SMP
- ad_write(devc, 16, tmp | 0x40); /* Enable timer */
- for (x = 0; x < 100000 && devc->timer_ticks == 0; x++);
- ad_write(devc, 16, tmp & ~0x40); /* Disable timer */
-
- if (devc->timer_ticks == 0)
- printk(KERN_WARNING "ad1848: Interrupt test failed (IRQ%d)\n", irq);
- else
- {
- DDB(printk("Interrupt test OK\n"));
- devc->irq_ok = 1;
- }
-#else
- devc->irq_ok = 1;
-#endif
- }
- else
- devc->irq_ok = 1; /* Couldn't test. assume it's OK */
- } else if (irq < 0)
- irq2dev[-irq] = devc->dev_no = my_dev;
-
-#ifndef EXCLUDE_TIMERS
- if ((capabilities[devc->model].flags & CAP_F_TIMER) &&
- devc->irq_ok)
- ad1848_tmr_install(my_dev);
-#endif
-
- if (!share_dma)
- {
- if (sound_alloc_dma(dma_playback, devc->name))
- printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_playback);
-
- if (dma_capture != dma_playback)
- if (sound_alloc_dma(dma_capture, devc->name))
- printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_capture);
- }
-
- if ((e = sound_install_mixer(MIXER_DRIVER_VERSION,
- dev_name,
- &ad1848_mixer_operations,
- sizeof(struct mixer_operations),
- devc)) >= 0)
- {
- audio_devs[my_dev]->mixer_dev = e;
- if (owner)
- mixer_devs[e]->owner = owner;
- }
- return my_dev;
-}
-
-int ad1848_control(int cmd, int arg)
-{
- ad1848_info *devc;
- unsigned long flags;
-
- if (nr_ad1848_devs < 1)
- return -ENODEV;
-
- devc = &adev_info[nr_ad1848_devs - 1];
-
- switch (cmd)
- {
- case AD1848_SET_XTAL: /* Change clock frequency of AD1845 (only ) */
- if (devc->model != MD_1845 && devc->model != MD_1845_SSCAPE)
- return -EINVAL;
- spin_lock_irqsave(&devc->lock,flags);
- ad_enter_MCE(devc);
- ad_write(devc, 29, (ad_read(devc, 29) & 0x1f) | (arg << 5));
- ad_leave_MCE(devc);
- spin_unlock_irqrestore(&devc->lock,flags);
- break;
-
- case AD1848_MIXER_REROUTE:
- {
- int o = (arg >> 8) & 0xff;
- int n = arg & 0xff;
-
- if (o < 0 || o >= SOUND_MIXER_NRDEVICES)
- return -EINVAL;
-
- if (!(devc->supported_devices & (1 << o)) &&
- !(devc->supported_rec_devices & (1 << o)))
- return -EINVAL;
-
- if (n == SOUND_MIXER_NONE)
- { /* Just hide this control */
- ad1848_mixer_set(devc, o, 0); /* Shut up it */
- devc->supported_devices &= ~(1 << o);
- devc->supported_rec_devices &= ~(1 << o);
- break;
- }
-
- /* Make the mixer control identified by o to appear as n */
- if (n < 0 || n >= SOUND_MIXER_NRDEVICES)
- return -EINVAL;
-
- devc->mixer_reroute[n] = o; /* Rename the control */
- if (devc->supported_devices & (1 << o))
- devc->supported_devices |= (1 << n);
- if (devc->supported_rec_devices & (1 << o))
- devc->supported_rec_devices |= (1 << n);
-
- devc->supported_devices &= ~(1 << o);
- devc->supported_rec_devices &= ~(1 << o);
- }
- break;
- }
- return 0;
-}
-
-void ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int share_dma)
-{
- int i, mixer, dev = 0;
- ad1848_info *devc = NULL;
-
- for (i = 0; devc == NULL && i < nr_ad1848_devs; i++)
- {
- if (adev_info[i].base == io_base)
- {
- devc = &adev_info[i];
- dev = devc->dev_no;
- }
- }
-
- if (devc != NULL)
- {
- kfree(audio_devs[dev]->portc);
- release_region(devc->base, 4);
-
- if (!share_dma)
- {
- if (devc->irq > 0) /* There is no point in freeing irq, if it wasn't allocated */
- free_irq(devc->irq, (void *)(long)devc->dev_no);
-
- sound_free_dma(dma_playback);
-
- if (dma_playback != dma_capture)
- sound_free_dma(dma_capture);
-
- }
- mixer = audio_devs[devc->dev_no]->mixer_dev;
- if(mixer>=0)
- sound_unload_mixerdev(mixer);
-
- nr_ad1848_devs--;
- for ( ; i < nr_ad1848_devs ; i++)
- adev_info[i] = adev_info[i+1];
- }
- else
- printk(KERN_ERR "ad1848: Can't find device to be unloaded. Base=%x\n", io_base);
-}
-
-static irqreturn_t adintr(int irq, void *dev_id)
-{
- unsigned char status;
- ad1848_info *devc;
- int dev;
- int alt_stat = 0xff;
- unsigned char c930_stat = 0;
- int cnt = 0;
-
- dev = (long)dev_id;
- devc = (ad1848_info *) audio_devs[dev]->devc;
-
-interrupt_again: /* Jump back here if int status doesn't reset */
-
- status = inb(io_Status(devc));
-
- if (status == 0x80)
- printk(KERN_DEBUG "adintr: Why?\n");
- if (devc->model == MD_1848)
- outb((0), io_Status(devc)); /* Clear interrupt status */
-
- if (status & 0x01)
- {
- if (devc->model == MD_C930)
- { /* 82C930 has interrupt status register in MAD16 register MC11 */
-
- spin_lock(&devc->lock);
-
- /* 0xe0e is C930 address port
- * 0xe0f is C930 data port
- */
- outb(11, 0xe0e);
- c930_stat = inb(0xe0f);
- outb((~c930_stat), 0xe0f);
-
- spin_unlock(&devc->lock);
-
- alt_stat = (c930_stat << 2) & 0x30;
- }
- else if (devc->model != MD_1848)
- {
- spin_lock(&devc->lock);
- alt_stat = ad_read(devc, 24);
- ad_write(devc, 24, ad_read(devc, 24) & ~alt_stat); /* Selective ack */
- spin_unlock(&devc->lock);
- }
-
- if ((devc->open_mode & OPEN_READ) && (devc->audio_mode & PCM_ENABLE_INPUT) && (alt_stat & 0x20))
- {
- DMAbuf_inputintr(devc->record_dev);
- }
- if ((devc->open_mode & OPEN_WRITE) && (devc->audio_mode & PCM_ENABLE_OUTPUT) &&
- (alt_stat & 0x10))
- {
- DMAbuf_outputintr(devc->playback_dev, 1);
- }
- if (devc->model != MD_1848 && (alt_stat & 0x40)) /* Timer interrupt */
- {
- devc->timer_ticks++;
-#ifndef EXCLUDE_TIMERS
- if (timer_installed == dev && devc->timer_running)
- sound_timer_interrupt();
-#endif
- }
- }
-/*
- * Sometimes playback or capture interrupts occur while a timer interrupt
- * is being handled. The interrupt will not be retriggered if we don't
- * handle it now. Check if an interrupt is still pending and restart
- * the handler in this case.
- */
- if (inb(io_Status(devc)) & 0x01 && cnt++ < 4)
- {
- goto interrupt_again;
- }
- return IRQ_HANDLED;
-}
-
-/*
- * Experimental initialization sequence for the integrated sound system
- * of the Compaq Deskpro M.
- */
-
-static int init_deskpro_m(struct address_info *hw_config)
-{
- unsigned char tmp;
-
- if ((tmp = inb(0xc44)) == 0xff)
- {
- DDB(printk("init_deskpro_m: Dead port 0xc44\n"));
- return 0;
- }
-
- outb(0x10, 0xc44);
- outb(0x40, 0xc45);
- outb(0x00, 0xc46);
- outb(0xe8, 0xc47);
- outb(0x14, 0xc44);
- outb(0x40, 0xc45);
- outb(0x00, 0xc46);
- outb(0xe8, 0xc47);
- outb(0x10, 0xc44);
-
- return 1;
-}
-
-/*
- * Experimental initialization sequence for the integrated sound system
- * of Compaq Deskpro XL.
- */
-
-static int init_deskpro(struct address_info *hw_config)
-{
- unsigned char tmp;
-
- if ((tmp = inb(0xc44)) == 0xff)
- {
- DDB(printk("init_deskpro: Dead port 0xc44\n"));
- return 0;
- }
- outb((tmp | 0x04), 0xc44); /* Select bank 1 */
- if (inb(0xc44) != 0x04)
- {
- DDB(printk("init_deskpro: Invalid bank1 signature in port 0xc44\n"));
- return 0;
- }
- /*
- * OK. It looks like a Deskpro so let's proceed.
- */
-
- /*
- * I/O port 0xc44 Audio configuration register.
- *
- * bits 0xc0: Audio revision bits
- * 0x00 = Compaq Business Audio
- * 0x40 = MS Sound System Compatible (reset default)
- * 0x80 = Reserved
- * 0xc0 = Reserved
- * bit 0x20: No Wait State Enable
- * 0x00 = Disabled (reset default, DMA mode)
- * 0x20 = Enabled (programmed I/O mode)
- * bit 0x10: MS Sound System Decode Enable
- * 0x00 = Decoding disabled (reset default)
- * 0x10 = Decoding enabled
- * bit 0x08: FM Synthesis Decode Enable
- * 0x00 = Decoding Disabled (reset default)
- * 0x08 = Decoding enabled
- * bit 0x04 Bank select
- * 0x00 = Bank 0
- * 0x04 = Bank 1
- * bits 0x03 MSS Base address
- * 0x00 = 0x530 (reset default)
- * 0x01 = 0x604
- * 0x02 = 0xf40
- * 0x03 = 0xe80
- */
-
-#ifdef DEBUGXL
- /* Debug printing */
- printk("Port 0xc44 (before): ");
- outb((tmp & ~0x04), 0xc44);
- printk("%02x ", inb(0xc44));
- outb((tmp | 0x04), 0xc44);
- printk("%02x\n", inb(0xc44));
-#endif
-
- /* Set bank 1 of the register */
- tmp = 0x58; /* MSS Mode, MSS&FM decode enabled */
-
- switch (hw_config->io_base)
- {
- case 0x530:
- tmp |= 0x00;
- break;
- case 0x604:
- tmp |= 0x01;
- break;
- case 0xf40:
- tmp |= 0x02;
- break;
- case 0xe80:
- tmp |= 0x03;
- break;
- default:
- DDB(printk("init_deskpro: Invalid MSS port %x\n", hw_config->io_base));
- return 0;
- }
- outb((tmp & ~0x04), 0xc44); /* Write to bank=0 */
-
-#ifdef DEBUGXL
- /* Debug printing */
- printk("Port 0xc44 (after): ");
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk("%02x ", inb(0xc44));
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk("%02x\n", inb(0xc44));
-#endif
-
- /*
- * I/O port 0xc45 FM Address Decode/MSS ID Register.
- *
- * bank=0, bits 0xfe: FM synthesis Decode Compare bits 7:1 (default=0x88)
- * bank=0, bit 0x01: SBIC Power Control Bit
- * 0x00 = Powered up
- * 0x01 = Powered down
- * bank=1, bits 0xfc: MSS ID (default=0x40)
- */
-
-#ifdef DEBUGXL
- /* Debug printing */
- printk("Port 0xc45 (before): ");
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk("%02x ", inb(0xc45));
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk("%02x\n", inb(0xc45));
-#endif
-
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- outb((0x88), 0xc45); /* FM base 7:0 = 0x88 */
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- outb((0x10), 0xc45); /* MSS ID = 0x10 (MSS port returns 0x04) */
-
-#ifdef DEBUGXL
- /* Debug printing */
- printk("Port 0xc45 (after): ");
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk("%02x ", inb(0xc45));
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk("%02x\n", inb(0xc45));
-#endif
-
-
- /*
- * I/O port 0xc46 FM Address Decode/Address ASIC Revision Register.
- *
- * bank=0, bits 0xff: FM synthesis Decode Compare bits 15:8 (default=0x03)
- * bank=1, bits 0xff: Audio addressing ASIC id
- */
-
-#ifdef DEBUGXL
- /* Debug printing */
- printk("Port 0xc46 (before): ");
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk("%02x ", inb(0xc46));
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk("%02x\n", inb(0xc46));
-#endif
-
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- outb((0x03), 0xc46); /* FM base 15:8 = 0x03 */
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- outb((0x11), 0xc46); /* ASIC ID = 0x11 */
-
-#ifdef DEBUGXL
- /* Debug printing */
- printk("Port 0xc46 (after): ");
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk("%02x ", inb(0xc46));
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk("%02x\n", inb(0xc46));
-#endif
-
- /*
- * I/O port 0xc47 FM Address Decode Register.
- *
- * bank=0, bits 0xff: Decode enable selection for various FM address bits
- * bank=1, bits 0xff: Reserved
- */
-
-#ifdef DEBUGXL
- /* Debug printing */
- printk("Port 0xc47 (before): ");
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk("%02x ", inb(0xc47));
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk("%02x\n", inb(0xc47));
-#endif
-
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- outb((0x7c), 0xc47); /* FM decode enable bits = 0x7c */
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- outb((0x00), 0xc47); /* Reserved bank1 = 0x00 */
-
-#ifdef DEBUGXL
- /* Debug printing */
- printk("Port 0xc47 (after): ");
- outb((tmp & ~0x04), 0xc44); /* Select bank=0 */
- printk("%02x ", inb(0xc47));
- outb((tmp | 0x04), 0xc44); /* Select bank=1 */
- printk("%02x\n", inb(0xc47));
-#endif
-
- /*
- * I/O port 0xc6f = Audio Disable Function Register
- */
-
-#ifdef DEBUGXL
- printk("Port 0xc6f (before) = %02x\n", inb(0xc6f));
-#endif
-
- outb((0x80), 0xc6f);
-
-#ifdef DEBUGXL
- printk("Port 0xc6f (after) = %02x\n", inb(0xc6f));
-#endif
-
- return 1;
-}
-
-int probe_ms_sound(struct address_info *hw_config, struct resource *ports)
-{
- unsigned char tmp;
-
- DDB(printk("Entered probe_ms_sound(%x, %d)\n", hw_config->io_base, hw_config->card_subtype));
-
- if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */
- {
- /* check_opl3(0x388, hw_config); */
- return ad1848_detect(ports, NULL, hw_config->osp);
- }
-
- if (deskpro_xl && hw_config->card_subtype == 2) /* Compaq Deskpro XL */
- {
- if (!init_deskpro(hw_config))
- return 0;
- }
-
- if (deskpro_m) /* Compaq Deskpro M */
- {
- if (!init_deskpro_m(hw_config))
- return 0;
- }
-
- /*
- * Check if the IO port returns valid signature. The original MS Sound
- * system returns 0x04 while some cards (AudioTrix Pro for example)
- * return 0x00 or 0x0f.
- */
-
- if ((tmp = inb(hw_config->io_base + 3)) == 0xff) /* Bus float */
- {
- int ret;
-
- DDB(printk("I/O address is inactive (%x)\n", tmp));
- if (!(ret = ad1848_detect(ports, NULL, hw_config->osp)))
- return 0;
- return 1;
- }
- DDB(printk("MSS signature = %x\n", tmp & 0x3f));
- if ((tmp & 0x3f) != 0x04 &&
- (tmp & 0x3f) != 0x0f &&
- (tmp & 0x3f) != 0x00)
- {
- int ret;
-
- MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, (int) inb(hw_config->io_base + 3)));
- DDB(printk("Trying to detect codec anyway but IRQ/DMA may not work\n"));
- if (!(ret = ad1848_detect(ports, NULL, hw_config->osp)))
- return 0;
-
- hw_config->card_subtype = 1;
- return 1;
- }
- if ((hw_config->irq != 5) &&
- (hw_config->irq != 7) &&
- (hw_config->irq != 9) &&
- (hw_config->irq != 10) &&
- (hw_config->irq != 11) &&
- (hw_config->irq != 12))
- {
- printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq);
- return 0;
- }
- if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
- {
- printk(KERN_ERR "MSS: Bad DMA %d\n", hw_config->dma);
- return 0;
- }
- /*
- * Check that DMA0 is not in use with a 8 bit board.
- */
-
- if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80)
- {
- printk(KERN_ERR "MSS: Can't use DMA0 with a 8 bit card/slot\n");
- return 0;
- }
- if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80)
- {
- printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq);
- return 0;
- }
- return ad1848_detect(ports, NULL, hw_config->osp);
-}
-
-void attach_ms_sound(struct address_info *hw_config, struct resource *ports, struct module *owner)
-{
- static signed char interrupt_bits[12] =
- {
- -1, -1, -1, -1, -1, 0x00, -1, 0x08, -1, 0x10, 0x18, 0x20
- };
- signed char bits;
- char dma2_bit = 0;
-
- static char dma_bits[4] =
- {
- 1, 2, 0, 3
- };
-
- int config_port = hw_config->io_base + 0;
- int version_port = hw_config->io_base + 3;
- int dma = hw_config->dma;
- int dma2 = hw_config->dma2;
-
- if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */
- {
- hw_config->slots[0] = ad1848_init("MS Sound System", ports,
- hw_config->irq,
- hw_config->dma,
- hw_config->dma2, 0,
- hw_config->osp,
- owner);
- return;
- }
- /*
- * Set the IRQ and DMA addresses.
- */
-
- bits = interrupt_bits[hw_config->irq];
- if (bits == -1)
- {
- printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq);
- release_region(ports->start, 4);
- release_region(ports->start - 4, 4);
- return;
- }
- outb((bits | 0x40), config_port);
- if ((inb(version_port) & 0x40) == 0)
- printk(KERN_ERR "[MSS: IRQ Conflict?]\n");
-
-/*
- * Handle the capture DMA channel
- */
-
- if (dma2 != -1 && dma2 != dma)
- {
- if (!((dma == 0 && dma2 == 1) ||
- (dma == 1 && dma2 == 0) ||
- (dma == 3 && dma2 == 0)))
- { /* Unsupported combination. Try to swap channels */
- int tmp = dma;
-
- dma = dma2;
- dma2 = tmp;
- }
- if ((dma == 0 && dma2 == 1) ||
- (dma == 1 && dma2 == 0) ||
- (dma == 3 && dma2 == 0))
- {
- dma2_bit = 0x04; /* Enable capture DMA */
- }
- else
- {
- printk(KERN_WARNING "MSS: Invalid capture DMA\n");
- dma2 = dma;
- }
- }
- else
- {
- dma2 = dma;
- }
-
- hw_config->dma = dma;
- hw_config->dma2 = dma2;
-
- outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */
-
- hw_config->slots[0] = ad1848_init("MS Sound System", ports,
- hw_config->irq,
- dma, dma2, 0,
- hw_config->osp,
- THIS_MODULE);
-}
-
-void unload_ms_sound(struct address_info *hw_config)
-{
- ad1848_unload(hw_config->io_base + 4,
- hw_config->irq,
- hw_config->dma,
- hw_config->dma2, 0);
- sound_unload_audiodev(hw_config->slots[0]);
- release_region(hw_config->io_base, 4);
-}
-
-#ifndef EXCLUDE_TIMERS
-
-/*
- * Timer stuff (for /dev/music).
- */
-
-static unsigned int current_interval;
-
-static unsigned int ad1848_tmr_start(int dev, unsigned int usecs)
-{
- unsigned long flags;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
- unsigned long xtal_nsecs; /* nanoseconds per xtal oscillator tick */
- unsigned long divider;
-
- spin_lock_irqsave(&devc->lock,flags);
-
- /*
- * Length of the timer interval (in nanoseconds) depends on the
- * selected crystal oscillator. Check this from bit 0x01 of I8.
- *
- * AD1845 has just one oscillator which has cycle time of 10.050 us
- * (when a 24.576 MHz xtal oscillator is used).
- *
- * Convert requested interval to nanoseconds before computing
- * the timer divider.
- */
-
- if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE)
- xtal_nsecs = 10050;
- else if (ad_read(devc, 8) & 0x01)
- xtal_nsecs = 9920;
- else
- xtal_nsecs = 9969;
-
- divider = (usecs * 1000 + xtal_nsecs / 2) / xtal_nsecs;
-
- if (divider < 100) /* Don't allow shorter intervals than about 1ms */
- divider = 100;
-
- if (divider > 65535) /* Overflow check */
- divider = 65535;
-
- ad_write(devc, 21, (divider >> 8) & 0xff); /* Set upper bits */
- ad_write(devc, 20, divider & 0xff); /* Set lower bits */
- ad_write(devc, 16, ad_read(devc, 16) | 0x40); /* Start the timer */
- devc->timer_running = 1;
- spin_unlock_irqrestore(&devc->lock,flags);
-
- return current_interval = (divider * xtal_nsecs + 500) / 1000;
-}
-
-static void ad1848_tmr_reprogram(int dev)
-{
- /*
- * Audio driver has changed sampling rate so that a different xtal
- * oscillator was selected. We have to reprogram the timer rate.
- */
-
- ad1848_tmr_start(dev, current_interval);
- sound_timer_syncinterval(current_interval);
-}
-
-static void ad1848_tmr_disable(int dev)
-{
- unsigned long flags;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
-
- spin_lock_irqsave(&devc->lock,flags);
- ad_write(devc, 16, ad_read(devc, 16) & ~0x40);
- devc->timer_running = 0;
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static void ad1848_tmr_restart(int dev)
-{
- unsigned long flags;
- ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
-
- if (current_interval == 0)
- return;
-
- spin_lock_irqsave(&devc->lock,flags);
- ad_write(devc, 16, ad_read(devc, 16) | 0x40);
- devc->timer_running = 1;
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static struct sound_lowlev_timer ad1848_tmr =
-{
- 0,
- 2,
- ad1848_tmr_start,
- ad1848_tmr_disable,
- ad1848_tmr_restart
-};
-
-static int ad1848_tmr_install(int dev)
-{
- if (timer_installed != -1)
- return 0; /* Don't install another timer */
-
- timer_installed = ad1848_tmr.dev = dev;
- sound_timer_init(&ad1848_tmr, audio_devs[dev]->name);
-
- return 1;
-}
-#endif /* EXCLUDE_TIMERS */
-
-EXPORT_SYMBOL(ad1848_detect);
-EXPORT_SYMBOL(ad1848_init);
-EXPORT_SYMBOL(ad1848_unload);
-EXPORT_SYMBOL(ad1848_control);
-EXPORT_SYMBOL(probe_ms_sound);
-EXPORT_SYMBOL(attach_ms_sound);
-EXPORT_SYMBOL(unload_ms_sound);
-
-static int __initdata io = -1;
-static int __initdata irq = -1;
-static int __initdata dma = -1;
-static int __initdata dma2 = -1;
-static int __initdata type = 0;
-
-module_param(io, int, 0); /* I/O for a raw AD1848 card */
-module_param(irq, int, 0); /* IRQ to use */
-module_param(dma, int, 0); /* First DMA channel */
-module_param(dma2, int, 0); /* Second DMA channel */
-module_param(type, int, 0); /* Card type */
-module_param(deskpro_xl, bool, 0); /* Special magic for Deskpro XL boxen */
-module_param(deskpro_m, bool, 0); /* Special magic for Deskpro M box */
-module_param(soundpro, bool, 0); /* More special magic for SoundPro chips */
-
-#ifdef CONFIG_PNP
-module_param(isapnp, int, 0);
-module_param(isapnpjump, int, 0);
-module_param(reverse, bool, 0);
-MODULE_PARM_DESC(isapnp, "When set to 0, Plug & Play support will be disabled");
-MODULE_PARM_DESC(isapnpjump, "Jumps to a specific slot in the driver's PnP table. Use the source, Luke.");
-MODULE_PARM_DESC(reverse, "When set to 1, will reverse ISAPnP search order");
-
-static struct pnp_dev *ad1848_dev = NULL;
-
-/* Please add new entries at the end of the table */
-static struct {
- char *name;
- unsigned short card_vendor, card_device,
- vendor, function;
- short mss_io, irq, dma, dma2; /* index into isapnp table */
- int type;
-} ad1848_isapnp_list[] __initdata = {
- {"CMI 8330 SoundPRO",
- ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001),
- ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001),
- 0, 0, 0,-1, 0},
- {"CS4232 based card",
- ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000),
- 0, 0, 0, 1, 0},
- {"CS4232 based card",
- ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100),
- 0, 0, 0, 1, 0},
- {"OPL3-SA2 WSS mode",
- ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021),
- 1, 0, 0, 1, 1},
- {"Advanced Gravis InterWave Audio",
- ISAPNP_VENDOR('G','R','V'), ISAPNP_DEVICE(0x0001),
- ISAPNP_VENDOR('G','R','V'), ISAPNP_FUNCTION(0x0000),
- 0, 0, 0, 1, 0},
- {NULL}
-};
-
-static struct isapnp_device_id id_table[] __devinitdata = {
- { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001),
- ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 },
- { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000), 0 },
- { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100), 0 },
- /* The main driver for this card is opl3sa2
- { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
- ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021), 0 },
- */
- { ISAPNP_VENDOR('G','R','V'), ISAPNP_DEVICE(0x0001),
- ISAPNP_VENDOR('G','R','V'), ISAPNP_FUNCTION(0x0000), 0 },
- {0}
-};
-
-MODULE_DEVICE_TABLE(isapnp, id_table);
-
-static struct pnp_dev *activate_dev(char *devname, char *resname, struct pnp_dev *dev)
-{
- int err;
-
- err = pnp_device_attach(dev);
- if (err < 0)
- return(NULL);
-
- if((err = pnp_activate_dev(dev)) < 0) {
- printk(KERN_ERR "ad1848: %s %s config failed (out of resources?)[%d]\n", devname, resname, err);
-
- pnp_device_detach(dev);
-
- return(NULL);
- }
- audio_activated = 1;
- return(dev);
-}
-
-static struct pnp_dev __init *ad1848_init_generic(struct pnp_card *bus,
- struct address_info *hw_config, int slot)
-{
-
- /* Configure Audio device */
- if((ad1848_dev = pnp_find_dev(bus, ad1848_isapnp_list[slot].vendor, ad1848_isapnp_list[slot].function, NULL)))
- {
- if((ad1848_dev = activate_dev(ad1848_isapnp_list[slot].name, "ad1848", ad1848_dev)))
- {
- hw_config->io_base = pnp_port_start(ad1848_dev, ad1848_isapnp_list[slot].mss_io);
- hw_config->irq = pnp_irq(ad1848_dev, ad1848_isapnp_list[slot].irq);
- hw_config->dma = pnp_dma(ad1848_dev, ad1848_isapnp_list[slot].dma);
- if(ad1848_isapnp_list[slot].dma2 != -1)
- hw_config->dma2 = pnp_dma(ad1848_dev, ad1848_isapnp_list[slot].dma2);
- else
- hw_config->dma2 = -1;
- hw_config->card_subtype = ad1848_isapnp_list[slot].type;
- } else
- return(NULL);
- } else
- return(NULL);
-
- return(ad1848_dev);
-}
-
-static int __init ad1848_isapnp_init(struct address_info *hw_config, struct pnp_card *bus, int slot)
-{
- char *busname = bus->name[0] ? bus->name : ad1848_isapnp_list[slot].name;
-
- /* Initialize this baby. */
-
- if(ad1848_init_generic(bus, hw_config, slot)) {
- /* We got it. */
-
- printk(KERN_NOTICE "ad1848: PnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n",
- busname,
- hw_config->io_base, hw_config->irq, hw_config->dma,
- hw_config->dma2);
- return 1;
- }
- return 0;
-}
-
-static int __init ad1848_isapnp_probe(struct address_info *hw_config)
-{
- static int first = 1;
- int i;
-
- /* Count entries in sb_isapnp_list */
- for (i = 0; ad1848_isapnp_list[i].card_vendor != 0; i++);
- i--;
-
- /* Check and adjust isapnpjump */
- if( isapnpjump < 0 || isapnpjump > i) {
- isapnpjump = reverse ? i : 0;
- printk(KERN_ERR "ad1848: Valid range for isapnpjump is 0-%d. Adjusted to %d.\n", i, isapnpjump);
- }
-
- if(!first || !reverse)
- i = isapnpjump;
- first = 0;
- while(ad1848_isapnp_list[i].card_vendor != 0) {
- static struct pnp_card *bus = NULL;
-
- while ((bus = pnp_find_card(
- ad1848_isapnp_list[i].card_vendor,
- ad1848_isapnp_list[i].card_device,
- bus))) {
-
- if(ad1848_isapnp_init(hw_config, bus, i)) {
- isapnpjump = i; /* start next search from here */
- return 0;
- }
- }
- i += reverse ? -1 : 1;
- }
-
- return -ENODEV;
-}
-#endif
-
-
-static int __init init_ad1848(void)
-{
- printk(KERN_INFO "ad1848/cs4248 codec driver Copyright (C) by Hannu Savolainen 1993-1996\n");
-
-#ifdef CONFIG_PNP
- if(isapnp && (ad1848_isapnp_probe(&cfg) < 0) ) {
- printk(KERN_NOTICE "ad1848: No ISAPnP cards found, trying standard ones...\n");
- isapnp = 0;
- }
-#endif
-
- if(io != -1) {
- struct resource *ports;
- if( isapnp == 0 )
- {
- if(irq == -1 || dma == -1) {
- printk(KERN_WARNING "ad1848: must give I/O , IRQ and DMA.\n");
- return -EINVAL;
- }
-
- cfg.irq = irq;
- cfg.io_base = io;
- cfg.dma = dma;
- cfg.dma2 = dma2;
- cfg.card_subtype = type;
- }
-
- ports = request_region(io + 4, 4, "ad1848");
-
- if (!ports)
- return -EBUSY;
-
- if (!request_region(io, 4, "WSS config")) {
- release_region(io + 4, 4);
- return -EBUSY;
- }
-
- if (!probe_ms_sound(&cfg, ports)) {
- release_region(io + 4, 4);
- release_region(io, 4);
- return -ENODEV;
- }
- attach_ms_sound(&cfg, ports, THIS_MODULE);
- loaded = 1;
- }
- return 0;
-}
-
-static void __exit cleanup_ad1848(void)
-{
- if(loaded)
- unload_ms_sound(&cfg);
-
-#ifdef CONFIG_PNP
- if(ad1848_dev){
- if(audio_activated)
- pnp_device_detach(ad1848_dev);
- }
-#endif
-}
-
-module_init(init_ad1848);
-module_exit(cleanup_ad1848);
-
-#ifndef MODULE
-static int __init setup_ad1848(char *str)
-{
- /* io, irq, dma, dma2, type */
- int ints[6];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
- dma = ints[3];
- dma2 = ints[4];
- type = ints[5];
-
- return 1;
-}
-
-__setup("ad1848=", setup_ad1848);
-#endif
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/ad1848.h b/sound/oss/ad1848.h
deleted file mode 100644
index b95ebe28d426..000000000000
--- a/sound/oss/ad1848.h
+++ /dev/null
@@ -1,24 +0,0 @@
-
-#include <linux/interrupt.h>
-
-#define AD_F_CS4231 0x0001 /* Returned if a CS4232 (or compatible) detected */
-#define AD_F_CS4248 0x0001 /* Returned if a CS4248 (or compatible) detected */
-
-#define AD1848_SET_XTAL 1
-#define AD1848_MIXER_REROUTE 2
-
-#define AD1848_REROUTE(oldctl, newctl) \
- ad1848_control(AD1848_MIXER_REROUTE, ((oldctl)<<8)|(newctl))
-
-
-int ad1848_init(char *name, struct resource *ports, int irq, int dma_playback,
- int dma_capture, int share_dma, int *osp, struct module *owner);
-void ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int share_dma);
-
-int ad1848_detect (struct resource *ports, int *flags, int *osp);
-int ad1848_control(int cmd, int arg);
-
-void attach_ms_sound(struct address_info * hw_config, struct resource *ports, struct module * owner);
-
-int probe_ms_sound(struct address_info *hw_config, struct resource *ports);
-void unload_ms_sound(struct address_info *hw_info);
diff --git a/sound/oss/ad1848_mixer.h b/sound/oss/ad1848_mixer.h
deleted file mode 100644
index 2cf719b5fbbc..000000000000
--- a/sound/oss/ad1848_mixer.h
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * sound/oss/ad1848_mixer.h
- *
- * Definitions for the mixer of AD1848 and compatible codecs.
- */
-
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-
-
-/*
- * The AD1848 codec has generic input lines called Line, Aux1 and Aux2.
- * Sound card manufacturers have connected actual inputs (CD, synth, line,
- * etc) to these inputs in different order. Therefore it's difficult
- * to assign mixer channels to these inputs correctly. The following
- * contains two alternative mappings. The first one is for GUS MAX and
- * the second is just a generic one (line1, line2 and line3).
- * (Actually this is not a mapping but rather some kind of interleaving
- * solution).
- */
-#define MODE1_REC_DEVICES (SOUND_MASK_LINE3 | SOUND_MASK_MIC | \
- SOUND_MASK_LINE1 | SOUND_MASK_IMIX)
-
-#define SPRO_REC_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD | SOUND_MASK_LINE1)
-
-#define MODE1_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_MIC | \
- SOUND_MASK_LINE2 | \
- SOUND_MASK_IGAIN | \
- SOUND_MASK_PCM | SOUND_MASK_IMIX)
-
-#define MODE2_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \
- SOUND_MASK_MIC | \
- SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \
- SOUND_MASK_IGAIN | \
- SOUND_MASK_PCM | SOUND_MASK_IMIX)
-
-#define MODE3_MIXER_DEVICES (MODE2_MIXER_DEVICES | SOUND_MASK_VOLUME)
-
-/* OPTi 82C930 has no IMIX level control, but it can still be selected as an
- * input
- */
-#define C930_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \
- SOUND_MASK_MIC | SOUND_MASK_VOLUME | \
- SOUND_MASK_LINE3 | \
- SOUND_MASK_IGAIN | SOUND_MASK_PCM)
-
-#define SPRO_MIXER_DEVICES (SOUND_MASK_VOLUME | SOUND_MASK_PCM | \
- SOUND_MASK_LINE | SOUND_MASK_SYNTH | \
- SOUND_MASK_CD | SOUND_MASK_MIC | \
- SOUND_MASK_SPEAKER | SOUND_MASK_LINE1 | \
- SOUND_MASK_OGAIN)
-
-struct mixer_def {
- unsigned int regno:6; /* register number for volume */
- unsigned int polarity:1; /* volume polarity: 0=normal, 1=reversed */
- unsigned int bitpos:3; /* position of bits in register for volume */
- unsigned int nbits:3; /* number of bits in register for volume */
- unsigned int mutereg:6; /* register number for mute bit */
- unsigned int mutepol:1; /* mute polarity: 0=normal, 1=reversed */
- unsigned int mutepos:4; /* position of mute bit in register */
- unsigned int recreg:6; /* register number for recording bit */
- unsigned int recpol:1; /* recording polarity: 0=normal, 1=reversed */
- unsigned int recpos:4; /* position of recording bit in register */
-};
-
-static char mix_cvt[101] = {
- 0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42,
- 43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65,
- 65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79,
- 80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90,
- 91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99,
- 100
-};
-
-typedef struct mixer_def mixer_ent;
-typedef mixer_ent mixer_ents[2];
-
-/*
- * Most of the mixer entries work in backwards. Setting the polarity field
- * makes them to work correctly.
- *
- * The channel numbering used by individual sound cards is not fixed. Some
- * cards have assigned different meanings for the AUX1, AUX2 and LINE inputs.
- * The current version doesn't try to compensate this.
- */
-
-#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r, mute_bit) \
- [name] = {{reg_l, pola_l, pos_l, len_l, reg_l, 0, mute_bit, 0, 0, 8}, \
- {reg_r, pola_r, pos_r, len_r, reg_r, 0, mute_bit, 0, 0, 8}}
-
-#define MIX_ENT2(name, reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \
- rec_reg_l, rec_pola_l, rec_pos_l, \
- reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r, \
- rec_reg_r, rec_pola_r, rec_pos_r) \
- [name] = {{reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \
- rec_reg_l, rec_pola_l, rec_pos_l}, \
- {reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r, \
- rec_reg_r, rec_pola_r, rec_pos_r}}
-
-static mixer_ents ad1848_mix_devices[32] = {
- MIX_ENT(SOUND_MIXER_VOLUME, 27, 1, 0, 4, 29, 1, 0, 4, 8),
- MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7),
- MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1, 8),
- MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
- MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5, 7)
-};
-
-static mixer_ents iwave_mix_devices[32] = {
- MIX_ENT(SOUND_MIXER_VOLUME, 25, 1, 0, 5, 27, 1, 0, 5, 8),
- MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7),
- MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1, 8),
- MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_IMIX, 16, 1, 0, 5, 17, 1, 0, 5, 8),
- MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
- MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5, 7)
-};
-
-static mixer_ents cs42xb_mix_devices[32] = {
- /* Digital master volume actually has seven bits, but we only use
- six to avoid the discontinuity when the analog gain kicks in. */
- MIX_ENT(SOUND_MIXER_VOLUME, 46, 1, 0, 6, 47, 1, 0, 6, 7),
- MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7),
- MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_MIC, 34, 1, 0, 5, 35, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7),
- /* For the IMIX entry, it was not possible to use the MIX_ENT macro
- because the mute bit is in different positions for the two
- channels and requires reverse polarity. */
- [SOUND_MIXER_IMIX] = {{13, 1, 2, 6, 13, 1, 0, 0, 0, 8},
- {42, 1, 0, 6, 42, 1, 7, 0, 0, 8}},
- MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
- MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_LINE3, 38, 1, 0, 6, 39, 1, 0, 6, 7)
-};
-
-/* OPTi 82C930 has somewhat different port addresses.
- * Note: VOLUME == SPEAKER, SYNTH == LINE2, LINE == LINE3, CD == LINE1
- * VOLUME, SYNTH, LINE, CD are not enabled above.
- * MIC is level of mic monitoring direct to output. Same for CD, LINE, etc.
- */
-static mixer_ents c930_mix_devices[32] = {
- MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5, 7),
- MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4, 7),
- MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5, 7),
- MIX_ENT(SOUND_MIXER_SPEAKER, 22, 1, 1, 5, 23, 1, 1, 5, 7),
- MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4, 7),
- MIX_ENT(SOUND_MIXER_MIC, 20, 1, 1, 4, 21, 1, 1, 4, 7),
- MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4, 7),
- MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8),
- MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 1, 4, 3, 1, 1, 4, 7),
- MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 1, 4, 5, 1, 1, 4, 7),
- MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 1, 4, 19, 1, 1, 4, 7)
-};
-
-static mixer_ents spro_mix_devices[32] = {
- MIX_ENT (SOUND_MIXER_VOLUME, 19, 0, 4, 4, 19, 0, 0, 4, 8),
- MIX_ENT (SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT (SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT2(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 23, 0, 3, 0, 0, 8,
- 5, 1, 1, 4, 23, 0, 3, 0, 0, 8),
- MIX_ENT (SOUND_MIXER_PCM, 6, 1, 1, 4, 7, 1, 1, 4, 8),
- MIX_ENT (SOUND_MIXER_SPEAKER, 18, 0, 3, 2, 0, 0, 0, 0, 8),
- MIX_ENT2(SOUND_MIXER_LINE, 20, 0, 4, 4, 17, 1, 4, 16, 0, 2,
- 20, 0, 0, 4, 17, 1, 3, 16, 0, 1),
- MIX_ENT2(SOUND_MIXER_MIC, 18, 0, 0, 3, 17, 1, 0, 16, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
- MIX_ENT2(SOUND_MIXER_CD, 21, 0, 4, 4, 17, 1, 2, 16, 0, 4,
- 21, 0, 0, 4, 17, 1, 1, 16, 0, 3),
- MIX_ENT (SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT (SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT (SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT (SOUND_MIXER_IGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8),
- MIX_ENT (SOUND_MIXER_OGAIN, 17, 1, 6, 1, 0, 0, 0, 0, 8),
- /* This is external wavetable */
- MIX_ENT2(SOUND_MIXER_LINE1, 22, 0, 4, 4, 23, 1, 1, 23, 0, 4,
- 22, 0, 0, 4, 23, 1, 0, 23, 0, 5),
-};
-
-static int default_mixer_levels[32] =
-{
- 0x3232, /* Master Volume */
- 0x3232, /* Bass */
- 0x3232, /* Treble */
- 0x4b4b, /* FM */
- 0x3232, /* PCM */
- 0x1515, /* PC Speaker */
- 0x2020, /* Ext Line */
- 0x1010, /* Mic */
- 0x4b4b, /* CD */
- 0x0000, /* Recording monitor */
- 0x4b4b, /* Second PCM */
- 0x4b4b, /* Recording level */
- 0x4b4b, /* Input gain */
- 0x4b4b, /* Output gain */
- 0x2020, /* Line1 */
- 0x2020, /* Line2 */
- 0x1515 /* Line3 (usually line in)*/
-};
-
-#define LEFT_CHN 0
-#define RIGHT_CHN 1
-
-/*
- * Channel enable bits for ioctl(SOUND_MIXER_PRIVATE1)
- */
-
-#ifndef AUDIO_SPEAKER
-#define AUDIO_SPEAKER 0x01 /* Enable mono output */
-#define AUDIO_HEADPHONE 0x02 /* Sparc only */
-#define AUDIO_LINE_OUT 0x04 /* Sparc only */
-#endif
diff --git a/sound/oss/aedsp16.c b/sound/oss/aedsp16.c
deleted file mode 100644
index 35b5912cf3f8..000000000000
--- a/sound/oss/aedsp16.c
+++ /dev/null
@@ -1,1373 +0,0 @@
-/*
- sound/oss/aedsp16.c
-
- Audio Excel DSP 16 software configuration routines
- Copyright (C) 1995,1996,1997,1998 Riccardo Facchetti (fizban@tin.it)
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- */
-/*
- * Include the main OSS Lite header file. It include all the os, OSS Lite, etc
- * headers needed by this source.
- */
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include "sound_config.h"
-
-/*
-
- READ THIS
-
- This module started to configure the Audio Excel DSP 16 Sound Card.
- Now works with the SC-6000 (old aedsp16) and new SC-6600 based cards.
-
- NOTE: I have NO idea about Audio Excel DSP 16 III. If someone owns this
- audio card and want to see the kernel support for it, please contact me.
-
- Audio Excel DSP 16 is an SB pro II, Microsoft Sound System and MPU-401
- compatible card.
- It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq),
- so before this module, the only way to configure the DSP under linux was
- boot the MS-DOS loading the sound.sys device driver (this driver soft-
- configure the sound board hardware by massaging someone of its registers),
- and then ctrl-alt-del to boot linux with the DSP configured by the DOS
- driver.
-
- This module works configuring your Audio Excel DSP 16's irq, dma and
- mpu-401-irq. The OSS Lite routines rely on the fact that if the
- hardware is there, they can detect it. The problem with AEDSP16 is
- that no hardware can be found by the probe routines if the sound card
- is not configured properly. Sometimes the kernel probe routines can find
- an SBPRO even when the card is not configured (this is the standard setup
- of the card), but the SBPRO emulation don't work well if the card is not
- properly initialized. For this reason
-
- aedsp16_init_board()
-
- routine is called before the OSS Lite probe routines try to detect the
- hardware.
-
- NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS)
-
- NOTE: Now it works with SC-6000 and SC-6600 based audio cards. The new cards
- have no jumper switch at all. No more WSS or MPU-401 I/O port switches. They
- have to be configured by software.
-
- NOTE: The driver is merged with the new OSS Lite sound driver. It works
- as a lowlevel driver.
-
- The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS;
- the OSS Lite sound driver can be configured for SBPRO and MSS cards
- at the same time, but the aedsp16 can't be two cards!!
- When we configure it, we have to choose the SBPRO or the MSS emulation
- for AEDSP16. We also can install a *REAL* card of the other type (see [1]).
-
- NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO
- please let me know if it works.
-
- The MPU-401 support can be compiled in together with one of the other
- two operating modes.
-
- NOTE: This is something like plug-and-play: we have only to plug
- the AEDSP16 board in the socket, and then configure and compile
- a kernel that uses the AEDSP16 software configuration capability.
- No jumper setting is needed!
-
- For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3
- you have just to make config the OSS Lite package, configuring
- the AEDSP16 sound card, then activating the SBPro emulation mode
- and at last configuring IRQ and DMA.
- Compile the kernel and run it.
-
- NOTE: This means for SC-6000 cards that you can choose irq and dma,
- but not the I/O addresses. To change I/O addresses you have to set
- them with jumpers. For SC-6600 cards you have no jumpers so you have
- to set up your full card configuration in the make config.
-
- You can change the irq/dma/mirq settings WITHOUT THE NEED to open
- your computer and massage the jumpers (there are no irq/dma/mirq
- jumpers to be configured anyway, only I/O BASE values have to be
- configured with jumpers)
-
- For some ununderstandable reason, the card default of irq 7, dma 1,
- don't work for me. Seems to be an IRQ or DMA conflict. Under heavy
- HDD work, the kernel start to erupt out a lot of messages like:
-
- 'Sound: DMA timed out - IRQ/DRQ config error?'
-
- For what I can say, I have NOT any conflict at irq 7 (under linux I'm
- using the lp polling driver), and dma line 1 is unused as stated by
- /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so
- I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows!
- Anyway a setting of irq 10, dma 3 works really fine.
-
- NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know
- the emulation mode, all the installed hardware and the hardware
- configuration (irq and dma settings of all the hardware).
-
- This init module should work with SBPRO+MSS, when one of the two is
- the AEDSP16 emulation and the other the real card. (see [1])
- For example:
-
- AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other
- AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other
-
- MPU401 should work. (see [2])
-
- [1]
- ---
- Date: Mon, 29 Jul 1997 08:35:40 +0100
- From: Mr S J Greenaway <sjg95@unixfe.rl.ac.uk>
-
- [...]
- Just to let you know got my Audio Excel (emulating a MSS) working
- with my original SB16, thanks for the driver!
- [...]
- ---
-
- [2] Not tested by me for lack of hardware.
-
- TODO, WISHES AND TECH
-
- - About I/O ports allocation -
-
- Request the 2x0h region (port base) in any case if we are using this card.
-
- NOTE: the "aedsp16 (base)" string with which we are requesting the aedsp16
- port base region (see code) does not mean necessarily that we are emulating
- sbpro. Even if this region is the sbpro I/O ports region, we use this
- region to access the control registers of the card, and if emulating
- sbpro, I/O sbpro registers too. If we are emulating MSS, the sbpro
- registers are not used, in no way, to emulate an sbpro: they are
- used only for configuration purposes.
-
- Started Fri Mar 17 16:13:18 MET 1995
-
- v0.1 (ALPHA, was a user-level program called AudioExcelDSP16.c)
- - Initial code.
- v0.2 (ALPHA)
- - Cleanups.
- - Integrated with Linux voxware v 2.90-2 kernel sound driver.
- - SoundBlaster Pro mode configuration.
- - Microsoft Sound System mode configuration.
- - MPU-401 mode configuration.
- v0.3 (ALPHA)
- - Cleanups.
- - Rearranged the code to let aedsp16_init_board be more general.
- - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h
- inclusion too. We rely on os.h
- - Used the to get a variable
- len string (we are not sure about the len of Copyright string).
- This works with any SB and compatible.
- - Added the code to request_region at device init (should go in
- the main body of voxware).
- v0.4 (BETA)
- - Better configure.c patch for aedsp16 configuration (better
- logic of inclusion of AEDSP16 support)
- - Modified the conditional compilation to better support more than
- one sound card of the emulated type (read the NOTES above)
- - Moved the sb init routine from the attach to the very first
- probe in sb_card.c
- - Rearrangements and cleanups
- - Wiped out some unnecessary code and variables: this is kernel
- code so it is better save some TEXT and DATA
- - Fixed the request_region code. We must allocate the aedsp16 (sbpro)
- I/O ports in any case because they are used to access the DSP
- configuration registers and we can not allow anyone to get them.
- v0.5
- - cleanups on comments
- - prep for diffs against v3.0-proto-950402
- v0.6
- - removed the request_region()s when compiling the MODULE sound.o
- because we are not allowed (by the actual voxware structure) to
- release_region()
- v0.7 (pre ALPHA, not distributed)
- - started porting this module to kernel 1.3.84. Dummy probe/attach
- routines.
- v0.8 (ALPHA)
- - attached all the init routines.
- v0.9 (BETA)
- - Integrated with linux-pre2.0.7
- - Integrated with configuration scripts.
- - Cleaned up and beautyfied the code.
- v0.9.9 (BETA)
- - Thanks to Piercarlo Grandi: corrected the conditonal compilation code.
- Now only the code configured is compiled in, with some memory saving.
- v0.9.10
- - Integration into the sound/lowlevel/ section of the sound driver.
- - Re-organized the code.
- v0.9.11 (not distributed)
- - Rewritten the init interface-routines to initialize the AEDSP16 in
- one shot.
- - More cosmetics.
- - SC-6600 support.
- - More soft/hard configuration.
- v0.9.12
- - Refined the v0.9.11 code with conditional compilation to distinguish
- between SC-6000 and SC-6600 code.
- v1.0.0
- - Prep for merging with OSS Lite and Linux kernel 2.1.13
- - Corrected a bug in request/check/release region calls (thanks to the
- new kernel exception handling).
- v1.1
- - Revamped for integration with new modularized sound drivers: to enhance
- the flexibility of modular version, I have removed all the conditional
- compilation for SBPRO, MPU and MSS code. Now it is all managed with
- the ae_config structure.
- v1.2
- - Module informations added.
- - Removed aedsp16_delay_10msec(), now using mdelay(10)
- - All data and funcs moved to .*.init section.
- v1.3
- Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 2000/09/27
- - got rid of check_region
-
- Known Problems:
- - Audio Excel DSP 16 III don't work with this driver.
-
- Credits:
- Many thanks to Gerald Britton <gbritton@CapAccess.org>. He helped me a
- lot in testing the 0.9.11 and 0.9.12 versions of this driver.
-
- */
-
-
-#define VERSION "1.3" /* Version of Audio Excel DSP 16 driver */
-
-#undef AEDSP16_DEBUG /* Define this to 1 to enable debug code */
-#undef AEDSP16_DEBUG_MORE /* Define this to 1 to enable more debug */
-#undef AEDSP16_INFO /* Define this to 1 to enable info code */
-
-#if defined(AEDSP16_DEBUG)
-# define DBG(x) printk x
-# if defined(AEDSP16_DEBUG_MORE)
-# define DBG1(x) printk x
-# else
-# define DBG1(x)
-# endif
-#else
-# define DBG(x)
-# define DBG1(x)
-#endif
-
-/*
- * Misc definitions
- */
-#define TRUE 1
-#define FALSE 0
-
-/*
- * Region Size for request/check/release region.
- */
-#define IOBASE_REGION_SIZE 0x10
-
-/*
- * Hardware related defaults
- */
-#define DEF_AEDSP16_IOB 0x220 /* 0x220(default) 0x240 */
-#define DEF_AEDSP16_IRQ 7 /* 5 7(default) 9 10 11 */
-#define DEF_AEDSP16_MRQ 0 /* 5 7 9 10 0(default), 0 means disable */
-#define DEF_AEDSP16_DMA 1 /* 0 1(default) 3 */
-
-/*
- * Commands of AEDSP16's DSP (SBPRO+special).
- * Some of them are COMMAND_xx, in the future they may change.
- */
-#define WRITE_MDIRQ_CFG 0x50 /* Set M&I&DRQ mask (the real config) */
-#define COMMAND_52 0x52 /* */
-#define READ_HARD_CFG 0x58 /* Read Hardware Config (I/O base etc) */
-#define COMMAND_5C 0x5c /* */
-#define COMMAND_60 0x60 /* */
-#define COMMAND_66 0x66 /* */
-#define COMMAND_6C 0x6c /* */
-#define COMMAND_6E 0x6e /* */
-#define COMMAND_88 0x88 /* */
-#define DSP_INIT_MSS 0x8c /* Enable Microsoft Sound System mode */
-#define COMMAND_C5 0xc5 /* */
-#define GET_DSP_VERSION 0xe1 /* Get DSP Version */
-#define GET_DSP_COPYRIGHT 0xe3 /* Get DSP Copyright */
-
-/*
- * Offsets of AEDSP16 DSP I/O ports. The offset is added to base I/O port
- * to have the actual I/O port.
- * Register permissions are:
- * (wo) == Write Only
- * (ro) == Read Only
- * (w-) == Write
- * (r-) == Read
- */
-#define DSP_RESET 0x06 /* offset of DSP RESET (wo) */
-#define DSP_READ 0x0a /* offset of DSP READ (ro) */
-#define DSP_WRITE 0x0c /* offset of DSP WRITE (w-) */
-#define DSP_COMMAND 0x0c /* offset of DSP COMMAND (w-) */
-#define DSP_STATUS 0x0c /* offset of DSP STATUS (r-) */
-#define DSP_DATAVAIL 0x0e /* offset of DSP DATA AVAILABLE (ro) */
-
-
-#define RETRY 10 /* Various retry values on I/O opera- */
-#define STATUSRETRY 1000 /* tions. Sometimes we have to */
-#define HARDRETRY 500000 /* wait for previous cmd to complete */
-
-/*
- * Size of character arrays that store name and version of sound card
- */
-#define CARDNAMELEN 15 /* Size of the card's name in chars */
-#define CARDVERLEN 10 /* Size of the card's version in chars */
-#define CARDVERDIGITS 2 /* Number of digits in the version */
-
-#if defined(CONFIG_SC6600)
-/*
- * Bitmapped flags of hard configuration
- */
-/*
- * Decode macros (xl == low byte, xh = high byte)
- */
-#define IOBASE(xl) ((xl & 0x01)?0x240:0x220)
-#define JOY(xl) (xl & 0x02)
-#define MPUADDR(xl) ( \
- (xl & 0x0C)?0x330: \
- (xl & 0x08)?0x320: \
- (xl & 0x04)?0x310: \
- 0x300)
-#define WSSADDR(xl) ((xl & 0x10)?0xE80:0x530)
-#define CDROM(xh) (xh & 0x20)
-#define CDROMADDR(xh) (((xh & 0x1F) << 4) + 0x200)
-/*
- * Encode macros
- */
-#define BLDIOBASE(xl, val) { \
- xl &= ~0x01; \
- if (val == 0x240) \
- xl |= 0x01; \
- }
-#define BLDJOY(xl, val) { \
- xl &= ~0x02; \
- if (val == 1) \
- xl |= 0x02; \
- }
-#define BLDMPUADDR(xl, val) { \
- xl &= ~0x0C; \
- switch (val) { \
- case 0x330: \
- xl |= 0x0C; \
- break; \
- case 0x320: \
- xl |= 0x08; \
- break; \
- case 0x310: \
- xl |= 0x04; \
- break; \
- case 0x300: \
- xl |= 0x00; \
- break; \
- default: \
- xl |= 0x00; \
- break; \
- } \
- }
-#define BLDWSSADDR(xl, val) { \
- xl &= ~0x10; \
- if (val == 0xE80) \
- xl |= 0x10; \
- }
-#define BLDCDROM(xh, val) { \
- xh &= ~0x20; \
- if (val == 1) \
- xh |= 0x20; \
- }
-#define BLDCDROMADDR(xh, val) { \
- int tmp = val; \
- tmp -= 0x200; \
- tmp >>= 4; \
- tmp &= 0x1F; \
- xh |= tmp; \
- xh &= 0x7F; \
- xh |= 0x40; \
- }
-#endif /* CONFIG_SC6600 */
-
-/*
- * Bit mapped flags for calling aedsp16_init_board(), and saving the current
- * emulation mode.
- */
-#define INIT_NONE (0 )
-#define INIT_SBPRO (1<<0)
-#define INIT_MSS (1<<1)
-#define INIT_MPU401 (1<<2)
-
-static int soft_cfg __initdata = 0; /* bitmapped config */
-static int soft_cfg_mss __initdata = 0; /* bitmapped mss config */
-static int ver[CARDVERDIGITS] __initdata = {0, 0}; /* DSP Ver:
- hi->ver[0] lo->ver[1] */
-
-#if defined(CONFIG_SC6600)
-static int hard_cfg[2] /* lo<-hard_cfg[0] hi<-hard_cfg[1] */
- __initdata = { 0, 0};
-#endif /* CONFIG_SC6600 */
-
-#if defined(CONFIG_SC6600)
-/* Decoded hard configuration */
-struct d_hcfg {
- int iobase;
- int joystick;
- int mpubase;
- int wssbase;
- int cdrom;
- int cdrombase;
-};
-
-static struct d_hcfg decoded_hcfg __initdata = {0, };
-
-#endif /* CONFIG_SC6600 */
-
-/* orVals contain the values to be or'ed */
-struct orVals {
- int val; /* irq|mirq|dma */
- int or; /* soft_cfg |= TheStruct.or */
-};
-
-/* aedsp16_info contain the audio card configuration */
-struct aedsp16_info {
- int base_io; /* base I/O address for accessing card */
- int irq; /* irq value for DSP I/O */
- int mpu_irq; /* irq for mpu401 interface I/O */
- int dma; /* dma value for DSP I/O */
- int mss_base; /* base I/O for Microsoft Sound System */
- int mpu_base; /* base I/O for MPU-401 emulation */
- int init; /* Initialization status of the card */
-};
-
-/*
- * Magic values that the DSP will eat when configuring irq/mirq/dma
- */
-/* DSP IRQ conversion array */
-static struct orVals orIRQ[] __initdata = {
- {0x05, 0x28},
- {0x07, 0x08},
- {0x09, 0x10},
- {0x0a, 0x18},
- {0x0b, 0x20},
- {0x00, 0x00}
-};
-
-/* MPU-401 IRQ conversion array */
-static struct orVals orMIRQ[] __initdata = {
- {0x05, 0x04},
- {0x07, 0x44},
- {0x09, 0x84},
- {0x0a, 0xc4},
- {0x00, 0x00}
-};
-
-/* DMA Channels conversion array */
-static struct orVals orDMA[] __initdata = {
- {0x00, 0x01},
- {0x01, 0x02},
- {0x03, 0x03},
- {0x00, 0x00}
-};
-
-static struct aedsp16_info ae_config = {
- DEF_AEDSP16_IOB,
- DEF_AEDSP16_IRQ,
- DEF_AEDSP16_MRQ,
- DEF_AEDSP16_DMA,
- -1,
- -1,
- INIT_NONE
-};
-
-/*
- * Buffers to store audio card informations
- */
-static char DSPCopyright[CARDNAMELEN + 1] __initdata = {0, };
-static char DSPVersion[CARDVERLEN + 1] __initdata = {0, };
-
-static int __init aedsp16_wait_data(int port)
-{
- int loop = STATUSRETRY;
- unsigned char ret = 0;
-
- DBG1(("aedsp16_wait_data (0x%x): ", port));
-
- do {
- ret = inb(port + DSP_DATAVAIL);
- /*
- * Wait for data available (bit 7 of ret == 1)
- */
- } while (!(ret & 0x80) && loop--);
-
- if (ret & 0x80) {
- DBG1(("success.\n"));
- return TRUE;
- }
-
- DBG1(("failure.\n"));
- return FALSE;
-}
-
-static int __init aedsp16_read(int port)
-{
- int inbyte;
-
- DBG((" Read DSP Byte (0x%x): ", port));
-
- if (aedsp16_wait_data(port) == FALSE) {
- DBG(("failure.\n"));
- return -1;
- }
-
- inbyte = inb(port + DSP_READ);
-
- DBG(("read [0x%x]/{%c}.\n", inbyte, inbyte));
-
- return inbyte;
-}
-
-static int __init aedsp16_test_dsp(int port)
-{
- return ((aedsp16_read(port) == 0xaa) ? TRUE : FALSE);
-}
-
-static int __init aedsp16_dsp_reset(int port)
-{
- /*
- * Reset DSP
- */
-
- DBG(("Reset DSP:\n"));
-
- outb(1, (port + DSP_RESET));
- udelay(10);
- outb(0, (port + DSP_RESET));
- udelay(10);
- udelay(10);
- if (aedsp16_test_dsp(port) == TRUE) {
- DBG(("success.\n"));
- return TRUE;
- } else
- DBG(("failure.\n"));
- return FALSE;
-}
-
-static int __init aedsp16_write(int port, int cmd)
-{
- unsigned char ret;
- int loop = HARDRETRY;
-
- DBG((" Write DSP Byte (0x%x) [0x%x]: ", port, cmd));
-
- do {
- ret = inb(port + DSP_STATUS);
- /*
- * DSP ready to receive data if bit 7 of ret == 0
- */
- if (!(ret & 0x80)) {
- outb(cmd, port + DSP_COMMAND);
- DBG(("success.\n"));
- return 0;
- }
- } while (loop--);
-
- DBG(("timeout.\n"));
- printk("[AEDSP16] DSP Command (0x%x) timeout.\n", cmd);
-
- return -1;
-}
-
-#if defined(CONFIG_SC6600)
-
-#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
-void __init aedsp16_pinfo(void) {
- DBG(("\n Base address: %x\n", decoded_hcfg.iobase));
- DBG((" Joystick : %s present\n", decoded_hcfg.joystick?"":" not"));
- DBG((" WSS addr : %x\n", decoded_hcfg.wssbase));
- DBG((" MPU-401 addr: %x\n", decoded_hcfg.mpubase));
- DBG((" CDROM : %s present\n", (decoded_hcfg.cdrom!=4)?"":" not"));
- DBG((" CDROMADDR : %x\n\n", decoded_hcfg.cdrombase));
-}
-#endif
-
-static void __init aedsp16_hard_decode(void) {
-
- DBG((" aedsp16_hard_decode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
-
-/*
- * Decode Cfg Bytes.
- */
- decoded_hcfg.iobase = IOBASE(hard_cfg[0]);
- decoded_hcfg.joystick = JOY(hard_cfg[0]);
- decoded_hcfg.wssbase = WSSADDR(hard_cfg[0]);
- decoded_hcfg.mpubase = MPUADDR(hard_cfg[0]);
- decoded_hcfg.cdrom = CDROM(hard_cfg[1]);
- decoded_hcfg.cdrombase = CDROMADDR(hard_cfg[1]);
-
-#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
- printk(" Original sound card configuration:\n");
- aedsp16_pinfo();
-#endif
-
-/*
- * Now set up the real kernel configuration.
- */
- decoded_hcfg.iobase = ae_config.base_io;
- decoded_hcfg.wssbase = ae_config.mss_base;
- decoded_hcfg.mpubase = ae_config.mpu_base;
-
-#if defined(CONFIG_SC6600_JOY)
- decoded_hcfg.joystick = CONFIG_SC6600_JOY; /* Enable */
-#endif
-#if defined(CONFIG_SC6600_CDROM)
- decoded_hcfg.cdrom = CONFIG_SC6600_CDROM; /* 4:N-3:I-2:G-1:P-0:S */
-#endif
-#if defined(CONFIG_SC6600_CDROMBASE)
- decoded_hcfg.cdrombase = CONFIG_SC6600_CDROMBASE; /* 0 Disable */
-#endif
-
-#if defined(AEDSP16_DEBUG)
- DBG((" New Values:\n"));
- aedsp16_pinfo();
-#endif
-
- DBG(("success.\n"));
-}
-
-static void __init aedsp16_hard_encode(void) {
-
- DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
-
- hard_cfg[0] = 0;
- hard_cfg[1] = 0;
-
- hard_cfg[0] |= 0x20;
-
- BLDIOBASE (hard_cfg[0], decoded_hcfg.iobase);
- BLDWSSADDR(hard_cfg[0], decoded_hcfg.wssbase);
- BLDMPUADDR(hard_cfg[0], decoded_hcfg.mpubase);
- BLDJOY(hard_cfg[0], decoded_hcfg.joystick);
- BLDCDROM(hard_cfg[1], decoded_hcfg.cdrom);
- BLDCDROMADDR(hard_cfg[1], decoded_hcfg.cdrombase);
-
-#if defined(AEDSP16_DEBUG)
- aedsp16_pinfo();
-#endif
-
- DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
- DBG(("success.\n"));
-
-}
-
-static int __init aedsp16_hard_write(int port) {
-
- DBG(("aedsp16_hard_write:\n"));
-
- if (aedsp16_write(port, COMMAND_6C)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6C);
- DBG(("failure.\n"));
- return FALSE;
- }
- if (aedsp16_write(port, COMMAND_5C)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
- DBG(("failure.\n"));
- return FALSE;
- }
- if (aedsp16_write(port, hard_cfg[0])) {
- printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[0]);
- DBG(("failure.\n"));
- return FALSE;
- }
- if (aedsp16_write(port, hard_cfg[1])) {
- printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[1]);
- DBG(("failure.\n"));
- return FALSE;
- }
- if (aedsp16_write(port, COMMAND_C5)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_C5);
- DBG(("failure.\n"));
- return FALSE;
- }
-
- DBG(("success.\n"));
-
- return TRUE;
-}
-
-static int __init aedsp16_hard_read(int port) {
-
- DBG(("aedsp16_hard_read:\n"));
-
- if (aedsp16_write(port, READ_HARD_CFG)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", READ_HARD_CFG);
- DBG(("failure.\n"));
- return FALSE;
- }
-
- if ((hard_cfg[0] = aedsp16_read(port)) == -1) {
- printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
- READ_HARD_CFG);
- DBG(("failure.\n"));
- return FALSE;
- }
- if ((hard_cfg[1] = aedsp16_read(port)) == -1) {
- printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
- READ_HARD_CFG);
- DBG(("failure.\n"));
- return FALSE;
- }
- if (aedsp16_read(port) == -1) {
- printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
- READ_HARD_CFG);
- DBG(("failure.\n"));
- return FALSE;
- }
-
- DBG(("success.\n"));
-
- return TRUE;
-}
-
-static int __init aedsp16_ext_cfg_write(int port) {
-
- int extcfg, val;
-
- if (aedsp16_write(port, COMMAND_66)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_66);
- return FALSE;
- }
-
- extcfg = 7;
- if (decoded_hcfg.cdrom != 2)
- extcfg = 0x0F;
- if ((decoded_hcfg.cdrom == 4) ||
- (decoded_hcfg.cdrom == 3))
- extcfg &= ~2;
- if (decoded_hcfg.cdrombase == 0)
- extcfg &= ~2;
- if (decoded_hcfg.mpubase == 0)
- extcfg &= ~1;
-
- if (aedsp16_write(port, extcfg)) {
- printk("[AEDSP16] Write extcfg: failed!\n");
- return FALSE;
- }
- if (aedsp16_write(port, 0)) {
- printk("[AEDSP16] Write extcfg: failed!\n");
- return FALSE;
- }
- if (decoded_hcfg.cdrom == 3) {
- if (aedsp16_write(port, COMMAND_52)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52);
- return FALSE;
- }
- if ((val = aedsp16_read(port)) == -1) {
- printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n"
- , COMMAND_52);
- return FALSE;
- }
- val &= 0x7F;
- if (aedsp16_write(port, COMMAND_60)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60);
- return FALSE;
- }
- if (aedsp16_write(port, val)) {
- printk("[AEDSP16] Write val: failed!\n");
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
-#endif /* CONFIG_SC6600 */
-
-static int __init aedsp16_cfg_write(int port) {
- if (aedsp16_write(port, WRITE_MDIRQ_CFG)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
- return FALSE;
- }
- if (aedsp16_write(port, soft_cfg)) {
- printk("[AEDSP16] Initialization of (M)IRQ and DMA: failed!\n");
- return FALSE;
- }
- return TRUE;
-}
-
-static int __init aedsp16_init_mss(int port)
-{
- DBG(("aedsp16_init_mss:\n"));
-
- mdelay(10);
-
- if (aedsp16_write(port, DSP_INIT_MSS)) {
- printk("[AEDSP16] aedsp16_init_mss [0x%x]: failed!\n",
- DSP_INIT_MSS);
- DBG(("failure.\n"));
- return FALSE;
- }
-
- mdelay(10);
-
- if (aedsp16_cfg_write(port) == FALSE)
- return FALSE;
-
- outb(soft_cfg_mss, ae_config.mss_base);
-
- DBG(("success.\n"));
-
- return TRUE;
-}
-
-static int __init aedsp16_setup_board(int port) {
- int loop = RETRY;
-
-#if defined(CONFIG_SC6600)
- int val = 0;
-
- if (aedsp16_hard_read(port) == FALSE) {
- printk("[AEDSP16] aedsp16_hard_read: failed!\n");
- return FALSE;
- }
-
- if (aedsp16_write(port, COMMAND_52)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52);
- return FALSE;
- }
-
- if ((val = aedsp16_read(port)) == -1) {
- printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
- COMMAND_52);
- return FALSE;
- }
-#endif
-
- do {
- if (aedsp16_write(port, COMMAND_88)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_88);
- return FALSE;
- }
- mdelay(10);
- } while ((aedsp16_wait_data(port) == FALSE) && loop--);
-
- if (aedsp16_read(port) == -1) {
- printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
- COMMAND_88);
- return FALSE;
- }
-
-#if !defined(CONFIG_SC6600)
- if (aedsp16_write(port, COMMAND_5C)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
- return FALSE;
- }
-#endif
-
- if (aedsp16_cfg_write(port) == FALSE)
- return FALSE;
-
-#if defined(CONFIG_SC6600)
- if (aedsp16_write(port, COMMAND_60)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60);
- return FALSE;
- }
- if (aedsp16_write(port, val)) {
- printk("[AEDSP16] DATA 0x%x: failed!\n", val);
- return FALSE;
- }
- if (aedsp16_write(port, COMMAND_6E)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6E);
- return FALSE;
- }
- if (aedsp16_write(port, ver[0])) {
- printk("[AEDSP16] DATA 0x%x: failed!\n", ver[0]);
- return FALSE;
- }
- if (aedsp16_write(port, ver[1])) {
- printk("[AEDSP16] DATA 0x%x: failed!\n", ver[1]);
- return FALSE;
- }
-
- if (aedsp16_hard_write(port) == FALSE) {
- printk("[AEDSP16] aedsp16_hard_write: failed!\n");
- return FALSE;
- }
-
- if (aedsp16_write(port, COMMAND_5C)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
- return FALSE;
- }
-
-#if defined(THIS_IS_A_THING_I_HAVE_NOT_TESTED_YET)
- if (aedsp16_cfg_write(port) == FALSE)
- return FALSE;
-#endif
-
-#endif
-
- return TRUE;
-}
-
-static int __init aedsp16_stdcfg(int port) {
- if (aedsp16_write(port, WRITE_MDIRQ_CFG)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
- return FALSE;
- }
- /*
- * 0x0A == (IRQ 7, DMA 1, MIRQ 0)
- */
- if (aedsp16_write(port, 0x0A)) {
- printk("[AEDSP16] aedsp16_stdcfg: failed!\n");
- return FALSE;
- }
- return TRUE;
-}
-
-static int __init aedsp16_dsp_version(int port)
-{
- int len = 0;
- int ret;
-
- DBG(("Get DSP Version:\n"));
-
- if (aedsp16_write(ae_config.base_io, GET_DSP_VERSION)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_VERSION);
- DBG(("failed.\n"));
- return FALSE;
- }
-
- do {
- if ((ret = aedsp16_read(port)) == -1) {
- DBG(("failed.\n"));
- return FALSE;
- }
- /*
- * We already know how many int are stored (2), so we know when the
- * string is finished.
- */
- ver[len++] = ret;
- } while (len < CARDVERDIGITS);
- sprintf(DSPVersion, "%d.%d", ver[0], ver[1]);
-
- DBG(("success.\n"));
-
- return TRUE;
-}
-
-static int __init aedsp16_dsp_copyright(int port)
-{
- int len = 0;
- int ret;
-
- DBG(("Get DSP Copyright:\n"));
-
- if (aedsp16_write(ae_config.base_io, GET_DSP_COPYRIGHT)) {
- printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_COPYRIGHT);
- DBG(("failed.\n"));
- return FALSE;
- }
-
- do {
- if ((ret = aedsp16_read(port)) == -1) {
- /*
- * If no more data available, return to the caller, no error if len>0.
- * We have no other way to know when the string is finished.
- */
- if (len)
- break;
- else {
- DBG(("failed.\n"));
- return FALSE;
- }
- }
-
- DSPCopyright[len++] = ret;
-
- } while (len < CARDNAMELEN);
-
- DBG(("success.\n"));
-
- return TRUE;
-}
-
-static void __init aedsp16_init_tables(void)
-{
- int i = 0;
-
- memset(DSPCopyright, 0, CARDNAMELEN + 1);
- memset(DSPVersion, 0, CARDVERLEN + 1);
-
- for (i = 0; orIRQ[i].or; i++)
- if (orIRQ[i].val == ae_config.irq) {
- soft_cfg |= orIRQ[i].or;
- soft_cfg_mss |= orIRQ[i].or;
- }
-
- for (i = 0; orMIRQ[i].or; i++)
- if (orMIRQ[i].or == ae_config.mpu_irq)
- soft_cfg |= orMIRQ[i].or;
-
- for (i = 0; orDMA[i].or; i++)
- if (orDMA[i].val == ae_config.dma) {
- soft_cfg |= orDMA[i].or;
- soft_cfg_mss |= orDMA[i].or;
- }
-}
-
-static int __init aedsp16_init_board(void)
-{
- aedsp16_init_tables();
-
- if (aedsp16_dsp_reset(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_dsp_reset: failed!\n");
- return FALSE;
- }
- if (aedsp16_dsp_copyright(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_dsp_copyright: failed!\n");
- return FALSE;
- }
-
- /*
- * My AEDSP16 card return SC-6000 in DSPCopyright, so
- * if we have something different, we have to be warned.
- */
- if (strcmp("SC-6000", DSPCopyright))
- printk("[AEDSP16] Warning: non SC-6000 audio card!\n");
-
- if (aedsp16_dsp_version(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_dsp_version: failed!\n");
- return FALSE;
- }
-
- if (aedsp16_stdcfg(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_stdcfg: failed!\n");
- return FALSE;
- }
-
-#if defined(CONFIG_SC6600)
- if (aedsp16_hard_read(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_hard_read: failed!\n");
- return FALSE;
- }
-
- aedsp16_hard_decode();
-
- aedsp16_hard_encode();
-
- if (aedsp16_hard_write(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_hard_write: failed!\n");
- return FALSE;
- }
-
- if (aedsp16_ext_cfg_write(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_ext_cfg_write: failed!\n");
- return FALSE;
- }
-#endif /* CONFIG_SC6600 */
-
- if (aedsp16_setup_board(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] aedsp16_setup_board: failed!\n");
- return FALSE;
- }
-
- if (ae_config.mss_base != -1) {
- if (ae_config.init & INIT_MSS) {
- if (aedsp16_init_mss(ae_config.base_io) == FALSE) {
- printk("[AEDSP16] Can not initialize"
- "Microsoft Sound System mode.\n");
- return FALSE;
- }
- }
- }
-
-#if !defined(MODULE) || defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
-
- printk("Audio Excel DSP 16 init v%s (%s %s) [",
- VERSION, DSPCopyright,
- DSPVersion);
-
- if (ae_config.mpu_base != -1) {
- if (ae_config.init & INIT_MPU401) {
- printk("MPU401");
- if ((ae_config.init & INIT_MSS) ||
- (ae_config.init & INIT_SBPRO))
- printk(" ");
- }
- }
-
- if (ae_config.mss_base == -1) {
- if (ae_config.init & INIT_SBPRO) {
- printk("SBPro");
- if (ae_config.init & INIT_MSS)
- printk(" ");
- }
- }
-
- if (ae_config.mss_base != -1)
- if (ae_config.init & INIT_MSS)
- printk("MSS");
-
- printk("]\n");
-#endif /* MODULE || AEDSP16_INFO || AEDSP16_DEBUG */
-
- mdelay(10);
-
- return TRUE;
-}
-
-static int __init init_aedsp16_sb(void)
-{
- DBG(("init_aedsp16_sb: "));
-
-/*
- * If the card is already init'ed MSS, we can not init it to SBPRO too
- * because the board can not emulate simultaneously MSS and SBPRO.
- */
- if (ae_config.init & INIT_MSS)
- return FALSE;
- if (ae_config.init & INIT_SBPRO)
- return FALSE;
-
- ae_config.init |= INIT_SBPRO;
-
- DBG(("done.\n"));
-
- return TRUE;
-}
-
-static void uninit_aedsp16_sb(void)
-{
- DBG(("uninit_aedsp16_sb: "));
-
- ae_config.init &= ~INIT_SBPRO;
-
- DBG(("done.\n"));
-}
-
-static int __init init_aedsp16_mss(void)
-{
- DBG(("init_aedsp16_mss: "));
-
-/*
- * If the card is already init'ed SBPRO, we can not init it to MSS too
- * because the board can not emulate simultaneously MSS and SBPRO.
- */
- if (ae_config.init & INIT_SBPRO)
- return FALSE;
- if (ae_config.init & INIT_MSS)
- return FALSE;
-/*
- * We must allocate the CONFIG_AEDSP16_BASE region too because these are the
- * I/O ports to access card's control registers.
- */
- if (!(ae_config.init & INIT_MPU401)) {
- if (!request_region(ae_config.base_io, IOBASE_REGION_SIZE,
- "aedsp16 (base)")) {
- printk(
- "AEDSP16 BASE I/O port region is already in use.\n");
- return FALSE;
- }
- }
-
- ae_config.init |= INIT_MSS;
-
- DBG(("done.\n"));
-
- return TRUE;
-}
-
-static void uninit_aedsp16_mss(void)
-{
- DBG(("uninit_aedsp16_mss: "));
-
- if ((!(ae_config.init & INIT_MPU401)) &&
- (ae_config.init & INIT_MSS)) {
- release_region(ae_config.base_io, IOBASE_REGION_SIZE);
- DBG(("AEDSP16 base region released.\n"));
- }
-
- ae_config.init &= ~INIT_MSS;
- DBG(("done.\n"));
-}
-
-static int __init init_aedsp16_mpu(void)
-{
- DBG(("init_aedsp16_mpu: "));
-
- if (ae_config.init & INIT_MPU401)
- return FALSE;
-
-/*
- * We must request the CONFIG_AEDSP16_BASE region too because these are the I/O
- * ports to access card's control registers.
- */
- if (!(ae_config.init & (INIT_MSS | INIT_SBPRO))) {
- if (!request_region(ae_config.base_io, IOBASE_REGION_SIZE,
- "aedsp16 (base)")) {
- printk(
- "AEDSP16 BASE I/O port region is already in use.\n");
- return FALSE;
- }
- }
-
- ae_config.init |= INIT_MPU401;
-
- DBG(("done.\n"));
-
- return TRUE;
-}
-
-static void uninit_aedsp16_mpu(void)
-{
- DBG(("uninit_aedsp16_mpu: "));
-
- if ((!(ae_config.init & (INIT_MSS | INIT_SBPRO))) &&
- (ae_config.init & INIT_MPU401)) {
- release_region(ae_config.base_io, IOBASE_REGION_SIZE);
- DBG(("AEDSP16 base region released.\n"));
- }
-
- ae_config.init &= ~INIT_MPU401;
-
- DBG(("done.\n"));
-}
-
-static int __init init_aedsp16(void)
-{
- int initialized = FALSE;
-
- DBG(("Initializing BASE[0x%x] IRQ[%d] DMA[%d] MIRQ[%d]\n",
- ae_config.base_io,ae_config.irq,ae_config.dma,ae_config.mpu_irq));
-
- if (ae_config.mss_base == -1) {
- if (init_aedsp16_sb() == FALSE) {
- uninit_aedsp16_sb();
- } else {
- initialized = TRUE;
- }
- }
-
- if (ae_config.mpu_base != -1) {
- if (init_aedsp16_mpu() == FALSE) {
- uninit_aedsp16_mpu();
- } else {
- initialized = TRUE;
- }
- }
-
-/*
- * In the sequence of init routines, the MSS init MUST be the last!
- * This because of the special register programming the MSS mode needs.
- * A board reset would disable the MSS mode restoring the default SBPRO
- * mode.
- */
- if (ae_config.mss_base != -1) {
- if (init_aedsp16_mss() == FALSE) {
- uninit_aedsp16_mss();
- } else {
- initialized = TRUE;
- }
- }
-
- if (initialized)
- initialized = aedsp16_init_board();
- return initialized;
-}
-
-static void __exit uninit_aedsp16(void)
-{
- if (ae_config.mss_base != -1)
- uninit_aedsp16_mss();
- else
- uninit_aedsp16_sb();
- if (ae_config.mpu_base != -1)
- uninit_aedsp16_mpu();
-}
-
-static int __initdata io = -1;
-static int __initdata irq = -1;
-static int __initdata dma = -1;
-static int __initdata mpu_irq = -1;
-static int __initdata mss_base = -1;
-static int __initdata mpu_base = -1;
-
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O base address (0x220 0x240)");
-module_param(irq, int, 0);
-MODULE_PARM_DESC(irq, "IRQ line (5 7 9 10 11)");
-module_param(dma, int, 0);
-MODULE_PARM_DESC(dma, "dma line (0 1 3)");
-module_param(mpu_irq, int, 0);
-MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ line (5 7 9 10 0)");
-module_param(mss_base, int, 0);
-MODULE_PARM_DESC(mss_base, "MSS emulation I/O base address (0x530 0xE80)");
-module_param(mpu_base, int, 0);
-MODULE_PARM_DESC(mpu_base,"MPU-401 I/O base address (0x300 0x310 0x320 0x330)");
-MODULE_AUTHOR("Riccardo Facchetti <fizban@tin.it>");
-MODULE_DESCRIPTION("Audio Excel DSP 16 Driver Version " VERSION);
-MODULE_LICENSE("GPL");
-
-static int __init do_init_aedsp16(void) {
- printk("Audio Excel DSP 16 init driver Copyright (C) Riccardo Facchetti 1995-98\n");
- if (io == -1 || dma == -1 || irq == -1) {
- printk(KERN_INFO "aedsp16: I/O, IRQ and DMA are mandatory\n");
- return -EINVAL;
- }
-
- ae_config.base_io = io;
- ae_config.irq = irq;
- ae_config.dma = dma;
-
- ae_config.mss_base = mss_base;
- ae_config.mpu_base = mpu_base;
- ae_config.mpu_irq = mpu_irq;
-
- if (init_aedsp16() == FALSE) {
- printk(KERN_ERR "aedsp16: initialization failed\n");
- /*
- * XXX
- * What error should we return here ?
- */
- return -EINVAL;
- }
- return 0;
-}
-
-static void __exit cleanup_aedsp16(void) {
- uninit_aedsp16();
-}
-
-module_init(do_init_aedsp16);
-module_exit(cleanup_aedsp16);
-
-#ifndef MODULE
-static int __init setup_aedsp16(char *str)
-{
- /* io, irq, dma, mss_io, mpu_io, mpu_irq */
- int ints[7];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
- dma = ints[3];
- mss_base = ints[4];
- mpu_base = ints[5];
- mpu_irq = ints[6];
- return 1;
-}
-
-__setup("aedsp16=", setup_aedsp16);
-#endif
diff --git a/sound/oss/au1550_ac97.c b/sound/oss/au1550_ac97.c
deleted file mode 100644
index a8f626d99c5b..000000000000
--- a/sound/oss/au1550_ac97.c
+++ /dev/null
@@ -1,2147 +0,0 @@
-/*
- * au1550_ac97.c -- Sound driver for Alchemy Au1550 MIPS Internet Edge
- * Processor.
- *
- * Copyright 2004 Embedded Edge, LLC
- * dan@embeddededge.com
- *
- * Mostly copied from the au1000.c driver and some from the
- * PowerMac dbdma driver.
- * We assume the processor can do memory coherent DMA.
- *
- * Ported to 2.6 by Matt Porter <mporter@kernel.crashing.org>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
- * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#undef DEBUG
-
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/delay.h>
-#include <linux/sound.h>
-#include <linux/slab.h>
-#include <linux/soundcard.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/poll.h>
-#include <linux/bitops.h>
-#include <linux/spinlock.h>
-#include <linux/ac97_codec.h>
-#include <linux/mutex.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/hardirq.h>
-#include <asm/mach-au1x00/au1xxx_psc.h>
-#include <asm/mach-au1x00/au1xxx_dbdma.h>
-#include <asm/mach-au1x00/au1xxx.h>
-
-#undef OSS_DOCUMENTED_MIXER_SEMANTICS
-
-/* misc stuff */
-#define POLL_COUNT 0x50000
-#define AC97_EXT_DACS (AC97_EXTID_SDAC | AC97_EXTID_CDAC | AC97_EXTID_LDAC)
-
-/* The number of DBDMA ring descriptors to allocate. No sense making
- * this too large....if you can't keep up with a few you aren't likely
- * to be able to with lots of them, either.
- */
-#define NUM_DBDMA_DESCRIPTORS 4
-
-#define err(format, arg...) printk(KERN_ERR format "\n" , ## arg)
-
-/* Boot options
- * 0 = no VRA, 1 = use VRA if codec supports it
- */
-static DEFINE_MUTEX(au1550_ac97_mutex);
-static int vra = 1;
-module_param(vra, bool, 0);
-MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it");
-
-static struct au1550_state {
- /* soundcore stuff */
- int dev_audio;
-
- struct ac97_codec *codec;
- unsigned codec_base_caps; /* AC'97 reg 00h, "Reset Register" */
- unsigned codec_ext_caps; /* AC'97 reg 28h, "Extended Audio ID" */
- int no_vra; /* do not use VRA */
-
- spinlock_t lock;
- struct mutex open_mutex;
- struct mutex sem;
- fmode_t open_mode;
- wait_queue_head_t open_wait;
-
- struct dmabuf {
- u32 dmanr;
- unsigned sample_rate;
- unsigned src_factor;
- unsigned sample_size;
- int num_channels;
- int dma_bytes_per_sample;
- int user_bytes_per_sample;
- int cnt_factor;
-
- void *rawbuf;
- unsigned buforder;
- unsigned numfrag;
- unsigned fragshift;
- void *nextIn;
- void *nextOut;
- int count;
- unsigned total_bytes;
- unsigned error;
- wait_queue_head_t wait;
-
- /* redundant, but makes calculations easier */
- unsigned fragsize;
- unsigned dma_fragsize;
- unsigned dmasize;
- unsigned dma_qcount;
-
- /* OSS stuff */
- unsigned mapped:1;
- unsigned ready:1;
- unsigned stopped:1;
- unsigned ossfragshift;
- int ossmaxfrags;
- unsigned subdivision;
- } dma_dac, dma_adc;
-} au1550_state;
-
-static unsigned
-ld2(unsigned int x)
-{
- unsigned r = 0;
-
- if (x >= 0x10000) {
- x >>= 16;
- r += 16;
- }
- if (x >= 0x100) {
- x >>= 8;
- r += 8;
- }
- if (x >= 0x10) {
- x >>= 4;
- r += 4;
- }
- if (x >= 4) {
- x >>= 2;
- r += 2;
- }
- if (x >= 2)
- r++;
- return r;
-}
-
-static void
-au1550_delay(int msec)
-{
- if (in_interrupt())
- return;
-
- schedule_timeout_uninterruptible(msecs_to_jiffies(msec));
-}
-
-static u16
-rdcodec(struct ac97_codec *codec, u8 addr)
-{
- struct au1550_state *s = codec->private_data;
- unsigned long flags;
- u32 cmd, val;
- u16 data;
- int i;
-
- spin_lock_irqsave(&s->lock, flags);
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT)
- err("rdcodec: codec cmd pending expired!");
-
- cmd = (u32)PSC_AC97CDC_INDX(addr);
- cmd |= PSC_AC97CDC_RD; /* read command */
- au_writel(cmd, PSC_AC97CDC);
- au_sync();
-
- /* now wait for the data
- */
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT) {
- err("rdcodec: read poll expired!");
- data = 0;
- goto out;
- }
-
- /* wait for command done?
- */
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97EVNT);
- au_sync();
- if (val & PSC_AC97EVNT_CD)
- break;
- }
- if (i == POLL_COUNT) {
- err("rdcodec: read cmdwait expired!");
- data = 0;
- goto out;
- }
-
- data = au_readl(PSC_AC97CDC) & 0xffff;
- au_sync();
-
- /* Clear command done event.
- */
- au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT);
- au_sync();
-
- out:
- spin_unlock_irqrestore(&s->lock, flags);
-
- return data;
-}
-
-
-static void
-wrcodec(struct ac97_codec *codec, u8 addr, u16 data)
-{
- struct au1550_state *s = codec->private_data;
- unsigned long flags;
- u32 cmd, val;
- int i;
-
- spin_lock_irqsave(&s->lock, flags);
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT)
- err("wrcodec: codec cmd pending expired!");
-
- cmd = (u32)PSC_AC97CDC_INDX(addr);
- cmd |= (u32)data;
- au_writel(cmd, PSC_AC97CDC);
- au_sync();
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (!(val & PSC_AC97STAT_CP))
- break;
- }
- if (i == POLL_COUNT)
- err("wrcodec: codec cmd pending expired!");
-
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97EVNT);
- au_sync();
- if (val & PSC_AC97EVNT_CD)
- break;
- }
- if (i == POLL_COUNT)
- err("wrcodec: read cmdwait expired!");
-
- /* Clear command done event.
- */
- au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT);
- au_sync();
-
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-static void
-waitcodec(struct ac97_codec *codec)
-{
- u16 temp;
- u32 val;
- int i;
-
- /* codec_wait is used to wait for a ready state after
- * an AC97C_RESET.
- */
- au1550_delay(10);
-
- /* first poll the CODEC_READY tag bit
- */
- for (i = 0; i < POLL_COUNT; i++) {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- if (val & PSC_AC97STAT_CR)
- break;
- }
- if (i == POLL_COUNT) {
- err("waitcodec: CODEC_READY poll expired!");
- return;
- }
-
- /* get AC'97 powerdown control/status register
- */
- temp = rdcodec(codec, AC97_POWER_CONTROL);
-
- /* If anything is powered down, power'em up
- */
- if (temp & 0x7f00) {
- /* Power on
- */
- wrcodec(codec, AC97_POWER_CONTROL, 0);
- au1550_delay(100);
-
- /* Reread
- */
- temp = rdcodec(codec, AC97_POWER_CONTROL);
- }
-
- /* Check if Codec REF,ANL,DAC,ADC ready
- */
- if ((temp & 0x7f0f) != 0x000f)
- err("codec reg 26 status (0x%x) not ready!!", temp);
-}
-
-/* stop the ADC before calling */
-static void
-set_adc_rate(struct au1550_state *s, unsigned rate)
-{
- struct dmabuf *adc = &s->dma_adc;
- struct dmabuf *dac = &s->dma_dac;
- unsigned adc_rate, dac_rate;
- u16 ac97_extstat;
-
- if (s->no_vra) {
- /* calc SRC factor
- */
- adc->src_factor = ((96000 / rate) + 1) >> 1;
- adc->sample_rate = 48000 / adc->src_factor;
- return;
- }
-
- adc->src_factor = 1;
-
- ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
-
- rate = rate > 48000 ? 48000 : rate;
-
- /* enable VRA
- */
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ac97_extstat | AC97_EXTSTAT_VRA);
-
- /* now write the sample rate
- */
- wrcodec(s->codec, AC97_PCM_LR_ADC_RATE, (u16) rate);
-
- /* read it back for actual supported rate
- */
- adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE);
-
- pr_debug("set_adc_rate: set to %d Hz\n", adc_rate);
-
- /* some codec's don't allow unequal DAC and ADC rates, in which case
- * writing one rate reg actually changes both.
- */
- dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE);
- if (dac->num_channels > 2)
- wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, dac_rate);
- if (dac->num_channels > 4)
- wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, dac_rate);
-
- adc->sample_rate = adc_rate;
- dac->sample_rate = dac_rate;
-}
-
-/* stop the DAC before calling */
-static void
-set_dac_rate(struct au1550_state *s, unsigned rate)
-{
- struct dmabuf *dac = &s->dma_dac;
- struct dmabuf *adc = &s->dma_adc;
- unsigned adc_rate, dac_rate;
- u16 ac97_extstat;
-
- if (s->no_vra) {
- /* calc SRC factor
- */
- dac->src_factor = ((96000 / rate) + 1) >> 1;
- dac->sample_rate = 48000 / dac->src_factor;
- return;
- }
-
- dac->src_factor = 1;
-
- ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
-
- rate = rate > 48000 ? 48000 : rate;
-
- /* enable VRA
- */
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ac97_extstat | AC97_EXTSTAT_VRA);
-
- /* now write the sample rate
- */
- wrcodec(s->codec, AC97_PCM_FRONT_DAC_RATE, (u16) rate);
-
- /* I don't support different sample rates for multichannel,
- * so make these channels the same.
- */
- if (dac->num_channels > 2)
- wrcodec(s->codec, AC97_PCM_SURR_DAC_RATE, (u16) rate);
- if (dac->num_channels > 4)
- wrcodec(s->codec, AC97_PCM_LFE_DAC_RATE, (u16) rate);
- /* read it back for actual supported rate
- */
- dac_rate = rdcodec(s->codec, AC97_PCM_FRONT_DAC_RATE);
-
- pr_debug("set_dac_rate: set to %d Hz\n", dac_rate);
-
- /* some codec's don't allow unequal DAC and ADC rates, in which case
- * writing one rate reg actually changes both.
- */
- adc_rate = rdcodec(s->codec, AC97_PCM_LR_ADC_RATE);
-
- dac->sample_rate = dac_rate;
- adc->sample_rate = adc_rate;
-}
-
-static void
-stop_dac(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_dac;
- u32 stat;
- unsigned long flags;
-
- if (db->stopped)
- return;
-
- spin_lock_irqsave(&s->lock, flags);
-
- au_writel(PSC_AC97PCR_TP, PSC_AC97PCR);
- au_sync();
-
- /* Wait for Transmit Busy to show disabled.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_TB) != 0);
-
- au1xxx_dbdma_reset(db->dmanr);
-
- db->stopped = 1;
-
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-static void
-stop_adc(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_adc;
- unsigned long flags;
- u32 stat;
-
- if (db->stopped)
- return;
-
- spin_lock_irqsave(&s->lock, flags);
-
- au_writel(PSC_AC97PCR_RP, PSC_AC97PCR);
- au_sync();
-
- /* Wait for Receive Busy to show disabled.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_RB) != 0);
-
- au1xxx_dbdma_reset(db->dmanr);
-
- db->stopped = 1;
-
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-
-static void
-set_xmit_slots(int num_channels)
-{
- u32 ac97_config, stat;
-
- ac97_config = au_readl(PSC_AC97CFG);
- au_sync();
- ac97_config &= ~(PSC_AC97CFG_TXSLOT_MASK | PSC_AC97CFG_DE_ENABLE);
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- switch (num_channels) {
- case 6: /* stereo with surround and center/LFE,
- * slots 3,4,6,7,8,9
- */
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(6);
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(9);
-
- case 4: /* stereo with surround, slots 3,4,7,8 */
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(7);
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(8);
-
- case 2: /* stereo, slots 3,4 */
- case 1: /* mono */
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(3);
- ac97_config |= PSC_AC97CFG_TXSLOT_ENA(4);
- }
-
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- ac97_config |= PSC_AC97CFG_DE_ENABLE;
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- /* Wait for Device ready.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_DR) == 0);
-}
-
-static void
-set_recv_slots(int num_channels)
-{
- u32 ac97_config, stat;
-
- ac97_config = au_readl(PSC_AC97CFG);
- au_sync();
- ac97_config &= ~(PSC_AC97CFG_RXSLOT_MASK | PSC_AC97CFG_DE_ENABLE);
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- /* Always enable slots 3 and 4 (stereo). Slot 6 is
- * optional Mic ADC, which we don't support yet.
- */
- ac97_config |= PSC_AC97CFG_RXSLOT_ENA(3);
- ac97_config |= PSC_AC97CFG_RXSLOT_ENA(4);
-
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- ac97_config |= PSC_AC97CFG_DE_ENABLE;
- au_writel(ac97_config, PSC_AC97CFG);
- au_sync();
-
- /* Wait for Device ready.
- */
- do {
- stat = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((stat & PSC_AC97STAT_DR) == 0);
-}
-
-/* Hold spinlock for both start_dac() and start_adc() calls */
-static void
-start_dac(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_dac;
-
- if (!db->stopped)
- return;
-
- set_xmit_slots(db->num_channels);
- au_writel(PSC_AC97PCR_TC, PSC_AC97PCR);
- au_sync();
- au_writel(PSC_AC97PCR_TS, PSC_AC97PCR);
- au_sync();
-
- au1xxx_dbdma_start(db->dmanr);
-
- db->stopped = 0;
-}
-
-static void
-start_adc(struct au1550_state *s)
-{
- struct dmabuf *db = &s->dma_adc;
- int i;
-
- if (!db->stopped)
- return;
-
- /* Put two buffers on the ring to get things started.
- */
- for (i=0; i<2; i++) {
- au1xxx_dbdma_put_dest(db->dmanr, virt_to_phys(db->nextIn),
- db->dma_fragsize, DDMA_FLAGS_IE);
-
- db->nextIn += db->dma_fragsize;
- if (db->nextIn >= db->rawbuf + db->dmasize)
- db->nextIn -= db->dmasize;
- }
-
- set_recv_slots(db->num_channels);
- au1xxx_dbdma_start(db->dmanr);
- au_writel(PSC_AC97PCR_RC, PSC_AC97PCR);
- au_sync();
- au_writel(PSC_AC97PCR_RS, PSC_AC97PCR);
- au_sync();
-
- db->stopped = 0;
-}
-
-static int
-prog_dmabuf(struct au1550_state *s, struct dmabuf *db)
-{
- unsigned user_bytes_per_sec;
- unsigned bufs;
- unsigned rate = db->sample_rate;
-
- if (!db->rawbuf) {
- db->ready = db->mapped = 0;
- db->buforder = 5; /* 32 * PAGE_SIZE */
- db->rawbuf = kmalloc((PAGE_SIZE << db->buforder), GFP_KERNEL);
- if (!db->rawbuf)
- return -ENOMEM;
- }
-
- db->cnt_factor = 1;
- if (db->sample_size == 8)
- db->cnt_factor *= 2;
- if (db->num_channels == 1)
- db->cnt_factor *= 2;
- db->cnt_factor *= db->src_factor;
-
- db->count = 0;
- db->dma_qcount = 0;
- db->nextIn = db->nextOut = db->rawbuf;
-
- db->user_bytes_per_sample = (db->sample_size>>3) * db->num_channels;
- db->dma_bytes_per_sample = 2 * ((db->num_channels == 1) ?
- 2 : db->num_channels);
-
- user_bytes_per_sec = rate * db->user_bytes_per_sample;
- bufs = PAGE_SIZE << db->buforder;
- if (db->ossfragshift) {
- if ((1000 << db->ossfragshift) < user_bytes_per_sec)
- db->fragshift = ld2(user_bytes_per_sec/1000);
- else
- db->fragshift = db->ossfragshift;
- } else {
- db->fragshift = ld2(user_bytes_per_sec / 100 /
- (db->subdivision ? db->subdivision : 1));
- if (db->fragshift < 3)
- db->fragshift = 3;
- }
-
- db->fragsize = 1 << db->fragshift;
- db->dma_fragsize = db->fragsize * db->cnt_factor;
- db->numfrag = bufs / db->dma_fragsize;
-
- while (db->numfrag < 4 && db->fragshift > 3) {
- db->fragshift--;
- db->fragsize = 1 << db->fragshift;
- db->dma_fragsize = db->fragsize * db->cnt_factor;
- db->numfrag = bufs / db->dma_fragsize;
- }
-
- if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
- db->numfrag = db->ossmaxfrags;
-
- db->dmasize = db->dma_fragsize * db->numfrag;
- memset(db->rawbuf, 0, bufs);
-
- pr_debug("prog_dmabuf: rate=%d, samplesize=%d, channels=%d\n",
- rate, db->sample_size, db->num_channels);
- pr_debug("prog_dmabuf: fragsize=%d, cnt_factor=%d, dma_fragsize=%d\n",
- db->fragsize, db->cnt_factor, db->dma_fragsize);
- pr_debug("prog_dmabuf: numfrag=%d, dmasize=%d\n", db->numfrag, db->dmasize);
-
- db->ready = 1;
- return 0;
-}
-
-static int
-prog_dmabuf_adc(struct au1550_state *s)
-{
- stop_adc(s);
- return prog_dmabuf(s, &s->dma_adc);
-
-}
-
-static int
-prog_dmabuf_dac(struct au1550_state *s)
-{
- stop_dac(s);
- return prog_dmabuf(s, &s->dma_dac);
-}
-
-
-static void dac_dma_interrupt(int irq, void *dev_id)
-{
- struct au1550_state *s = (struct au1550_state *) dev_id;
- struct dmabuf *db = &s->dma_dac;
- u32 ac97c_stat;
-
- spin_lock(&s->lock);
-
- ac97c_stat = au_readl(PSC_AC97STAT);
- if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE))
- pr_debug("AC97C status = 0x%08x\n", ac97c_stat);
- db->dma_qcount--;
-
- if (db->count >= db->fragsize) {
- if (au1xxx_dbdma_put_source(db->dmanr,
- virt_to_phys(db->nextOut), db->fragsize,
- DDMA_FLAGS_IE) == 0) {
- err("qcount < 2 and no ring room!");
- }
- db->nextOut += db->fragsize;
- if (db->nextOut >= db->rawbuf + db->dmasize)
- db->nextOut -= db->dmasize;
- db->count -= db->fragsize;
- db->total_bytes += db->dma_fragsize;
- db->dma_qcount++;
- }
-
- /* wake up anybody listening */
- if (waitqueue_active(&db->wait))
- wake_up(&db->wait);
-
- spin_unlock(&s->lock);
-}
-
-
-static void adc_dma_interrupt(int irq, void *dev_id)
-{
- struct au1550_state *s = (struct au1550_state *)dev_id;
- struct dmabuf *dp = &s->dma_adc;
- u32 obytes;
- char *obuf;
-
- spin_lock(&s->lock);
-
- /* Pull the buffer from the dma queue.
- */
- au1xxx_dbdma_get_dest(dp->dmanr, (void *)(&obuf), &obytes);
-
- if ((dp->count + obytes) > dp->dmasize) {
- /* Overrun. Stop ADC and log the error
- */
- spin_unlock(&s->lock);
- stop_adc(s);
- dp->error++;
- err("adc overrun");
- return;
- }
-
- /* Put a new empty buffer on the destination DMA.
- */
- au1xxx_dbdma_put_dest(dp->dmanr, virt_to_phys(dp->nextIn),
- dp->dma_fragsize, DDMA_FLAGS_IE);
-
- dp->nextIn += dp->dma_fragsize;
- if (dp->nextIn >= dp->rawbuf + dp->dmasize)
- dp->nextIn -= dp->dmasize;
-
- dp->count += obytes;
- dp->total_bytes += obytes;
-
- /* wake up anybody listening
- */
- if (waitqueue_active(&dp->wait))
- wake_up(&dp->wait);
-
- spin_unlock(&s->lock);
-}
-
-static loff_t
-au1550_llseek(struct file *file, loff_t offset, int origin)
-{
- return -ESPIPE;
-}
-
-
-static int
-au1550_open_mixdev(struct inode *inode, struct file *file)
-{
- mutex_lock(&au1550_ac97_mutex);
- file->private_data = &au1550_state;
- mutex_unlock(&au1550_ac97_mutex);
- return 0;
-}
-
-static int
-au1550_release_mixdev(struct inode *inode, struct file *file)
-{
- return 0;
-}
-
-static int
-mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd,
- unsigned long arg)
-{
- return codec->mixer_ioctl(codec, cmd, arg);
-}
-
-static long
-au1550_ioctl_mixdev(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct au1550_state *s = file->private_data;
- struct ac97_codec *codec = s->codec;
- int ret;
-
- mutex_lock(&au1550_ac97_mutex);
- ret = mixdev_ioctl(codec, cmd, arg);
- mutex_unlock(&au1550_ac97_mutex);
-
- return ret;
-}
-
-static /*const */ struct file_operations au1550_mixer_fops = {
- .owner = THIS_MODULE,
- .llseek = au1550_llseek,
- .unlocked_ioctl = au1550_ioctl_mixdev,
- .open = au1550_open_mixdev,
- .release = au1550_release_mixdev,
-};
-
-static int
-drain_dac(struct au1550_state *s, int nonblock)
-{
- unsigned long flags;
- int count, tmo;
-
- if (s->dma_dac.mapped || !s->dma_dac.ready || s->dma_dac.stopped)
- return 0;
-
- for (;;) {
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_dac.count;
- spin_unlock_irqrestore(&s->lock, flags);
- if (count <= s->dma_dac.fragsize)
- break;
- if (signal_pending(current))
- break;
- if (nonblock)
- return -EBUSY;
- tmo = 1000 * count / (s->no_vra ?
- 48000 : s->dma_dac.sample_rate);
- tmo /= s->dma_dac.dma_bytes_per_sample;
- au1550_delay(tmo);
- }
- if (signal_pending(current))
- return -ERESTARTSYS;
- return 0;
-}
-
-static inline u8 S16_TO_U8(s16 ch)
-{
- return (u8) (ch >> 8) + 0x80;
-}
-static inline s16 U8_TO_S16(u8 ch)
-{
- return (s16) (ch - 0x80) << 8;
-}
-
-/*
- * Translates user samples to dma buffer suitable for AC'97 DAC data:
- * If mono, copy left channel to right channel in dma buffer.
- * If 8 bit samples, cvt to 16-bit before writing to dma buffer.
- * If interpolating (no VRA), duplicate every audio frame src_factor times.
- */
-static int
-translate_from_user(struct dmabuf *db, char* dmabuf, char* userbuf,
- int dmacount)
-{
- int sample, i;
- int interp_bytes_per_sample;
- int num_samples;
- int mono = (db->num_channels == 1);
- char usersample[12];
- s16 ch, dmasample[6];
-
- if (db->sample_size == 16 && !mono && db->src_factor == 1) {
- /* no translation necessary, just copy
- */
- if (copy_from_user(dmabuf, userbuf, dmacount))
- return -EFAULT;
- return dmacount;
- }
-
- interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor;
- num_samples = dmacount / interp_bytes_per_sample;
-
- for (sample = 0; sample < num_samples; sample++) {
- if (copy_from_user(usersample, userbuf,
- db->user_bytes_per_sample)) {
- return -EFAULT;
- }
-
- for (i = 0; i < db->num_channels; i++) {
- if (db->sample_size == 8)
- ch = U8_TO_S16(usersample[i]);
- else
- ch = *((s16 *) (&usersample[i * 2]));
- dmasample[i] = ch;
- if (mono)
- dmasample[i + 1] = ch; /* right channel */
- }
-
- /* duplicate every audio frame src_factor times
- */
- for (i = 0; i < db->src_factor; i++)
- memcpy(dmabuf, dmasample, db->dma_bytes_per_sample);
-
- userbuf += db->user_bytes_per_sample;
- dmabuf += interp_bytes_per_sample;
- }
-
- return num_samples * interp_bytes_per_sample;
-}
-
-/*
- * Translates AC'97 ADC samples to user buffer:
- * If mono, send only left channel to user buffer.
- * If 8 bit samples, cvt from 16 to 8 bit before writing to user buffer.
- * If decimating (no VRA), skip over src_factor audio frames.
- */
-static int
-translate_to_user(struct dmabuf *db, char* userbuf, char* dmabuf,
- int dmacount)
-{
- int sample, i;
- int interp_bytes_per_sample;
- int num_samples;
- int mono = (db->num_channels == 1);
- char usersample[12];
-
- if (db->sample_size == 16 && !mono && db->src_factor == 1) {
- /* no translation necessary, just copy
- */
- if (copy_to_user(userbuf, dmabuf, dmacount))
- return -EFAULT;
- return dmacount;
- }
-
- interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor;
- num_samples = dmacount / interp_bytes_per_sample;
-
- for (sample = 0; sample < num_samples; sample++) {
- for (i = 0; i < db->num_channels; i++) {
- if (db->sample_size == 8)
- usersample[i] =
- S16_TO_U8(*((s16 *) (&dmabuf[i * 2])));
- else
- *((s16 *) (&usersample[i * 2])) =
- *((s16 *) (&dmabuf[i * 2]));
- }
-
- if (copy_to_user(userbuf, usersample,
- db->user_bytes_per_sample)) {
- return -EFAULT;
- }
-
- userbuf += db->user_bytes_per_sample;
- dmabuf += interp_bytes_per_sample;
- }
-
- return num_samples * interp_bytes_per_sample;
-}
-
-/*
- * Copy audio data to/from user buffer from/to dma buffer, taking care
- * that we wrap when reading/writing the dma buffer. Returns actual byte
- * count written to or read from the dma buffer.
- */
-static int
-copy_dmabuf_user(struct dmabuf *db, char* userbuf, int count, int to_user)
-{
- char *bufptr = to_user ? db->nextOut : db->nextIn;
- char *bufend = db->rawbuf + db->dmasize;
- int cnt, ret;
-
- if (bufptr + count > bufend) {
- int partial = (int) (bufend - bufptr);
- if (to_user) {
- if ((cnt = translate_to_user(db, userbuf,
- bufptr, partial)) < 0)
- return cnt;
- ret = cnt;
- if ((cnt = translate_to_user(db, userbuf + partial,
- db->rawbuf,
- count - partial)) < 0)
- return cnt;
- ret += cnt;
- } else {
- if ((cnt = translate_from_user(db, bufptr, userbuf,
- partial)) < 0)
- return cnt;
- ret = cnt;
- if ((cnt = translate_from_user(db, db->rawbuf,
- userbuf + partial,
- count - partial)) < 0)
- return cnt;
- ret += cnt;
- }
- } else {
- if (to_user)
- ret = translate_to_user(db, userbuf, bufptr, count);
- else
- ret = translate_from_user(db, bufptr, userbuf, count);
- }
-
- return ret;
-}
-
-
-static ssize_t
-au1550_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
-{
- struct au1550_state *s = file->private_data;
- struct dmabuf *db = &s->dma_adc;
- DECLARE_WAITQUEUE(wait, current);
- ssize_t ret;
- unsigned long flags;
- int cnt, usercnt, avail;
-
- if (db->mapped)
- return -ENXIO;
- if (!access_ok(VERIFY_WRITE, buffer, count))
- return -EFAULT;
- ret = 0;
-
- count *= db->cnt_factor;
-
- mutex_lock(&s->sem);
- add_wait_queue(&db->wait, &wait);
-
- while (count > 0) {
- /* wait for samples in ADC dma buffer
- */
- do {
- spin_lock_irqsave(&s->lock, flags);
- if (db->stopped)
- start_adc(s);
- avail = db->count;
- if (avail <= 0)
- __set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irqrestore(&s->lock, flags);
- if (avail <= 0) {
- if (file->f_flags & O_NONBLOCK) {
- if (!ret)
- ret = -EAGAIN;
- goto out;
- }
- mutex_unlock(&s->sem);
- schedule();
- if (signal_pending(current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- goto out2;
- }
- mutex_lock(&s->sem);
- }
- } while (avail <= 0);
-
- /* copy from nextOut to user
- */
- if ((cnt = copy_dmabuf_user(db, buffer,
- count > avail ?
- avail : count, 1)) < 0) {
- if (!ret)
- ret = -EFAULT;
- goto out;
- }
-
- spin_lock_irqsave(&s->lock, flags);
- db->count -= cnt;
- db->nextOut += cnt;
- if (db->nextOut >= db->rawbuf + db->dmasize)
- db->nextOut -= db->dmasize;
- spin_unlock_irqrestore(&s->lock, flags);
-
- count -= cnt;
- usercnt = cnt / db->cnt_factor;
- buffer += usercnt;
- ret += usercnt;
- } /* while (count > 0) */
-
-out:
- mutex_unlock(&s->sem);
-out2:
- remove_wait_queue(&db->wait, &wait);
- set_current_state(TASK_RUNNING);
- return ret;
-}
-
-static ssize_t
-au1550_write(struct file *file, const char *buffer, size_t count, loff_t * ppos)
-{
- struct au1550_state *s = file->private_data;
- struct dmabuf *db = &s->dma_dac;
- DECLARE_WAITQUEUE(wait, current);
- ssize_t ret = 0;
- unsigned long flags;
- int cnt, usercnt, avail;
-
- pr_debug("write: count=%d\n", count);
-
- if (db->mapped)
- return -ENXIO;
- if (!access_ok(VERIFY_READ, buffer, count))
- return -EFAULT;
-
- count *= db->cnt_factor;
-
- mutex_lock(&s->sem);
- add_wait_queue(&db->wait, &wait);
-
- while (count > 0) {
- /* wait for space in playback buffer
- */
- do {
- spin_lock_irqsave(&s->lock, flags);
- avail = (int) db->dmasize - db->count;
- if (avail <= 0)
- __set_current_state(TASK_INTERRUPTIBLE);
- spin_unlock_irqrestore(&s->lock, flags);
- if (avail <= 0) {
- if (file->f_flags & O_NONBLOCK) {
- if (!ret)
- ret = -EAGAIN;
- goto out;
- }
- mutex_unlock(&s->sem);
- schedule();
- if (signal_pending(current)) {
- if (!ret)
- ret = -ERESTARTSYS;
- goto out2;
- }
- mutex_lock(&s->sem);
- }
- } while (avail <= 0);
-
- /* copy from user to nextIn
- */
- if ((cnt = copy_dmabuf_user(db, (char *) buffer,
- count > avail ?
- avail : count, 0)) < 0) {
- if (!ret)
- ret = -EFAULT;
- goto out;
- }
-
- spin_lock_irqsave(&s->lock, flags);
- db->count += cnt;
- db->nextIn += cnt;
- if (db->nextIn >= db->rawbuf + db->dmasize)
- db->nextIn -= db->dmasize;
-
- /* If the data is available, we want to keep two buffers
- * on the dma queue. If the queue count reaches zero,
- * we know the dma has stopped.
- */
- while ((db->dma_qcount < 2) && (db->count >= db->fragsize)) {
- if (au1xxx_dbdma_put_source(db->dmanr,
- virt_to_phys(db->nextOut), db->fragsize,
- DDMA_FLAGS_IE) == 0) {
- err("qcount < 2 and no ring room!");
- }
- db->nextOut += db->fragsize;
- if (db->nextOut >= db->rawbuf + db->dmasize)
- db->nextOut -= db->dmasize;
- db->total_bytes += db->dma_fragsize;
- if (db->dma_qcount == 0)
- start_dac(s);
- db->dma_qcount++;
- }
- spin_unlock_irqrestore(&s->lock, flags);
-
- count -= cnt;
- usercnt = cnt / db->cnt_factor;
- buffer += usercnt;
- ret += usercnt;
- } /* while (count > 0) */
-
-out:
- mutex_unlock(&s->sem);
-out2:
- remove_wait_queue(&db->wait, &wait);
- set_current_state(TASK_RUNNING);
- return ret;
-}
-
-
-/* No kernel lock - we have our own spinlock */
-static unsigned int
-au1550_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct au1550_state *s = file->private_data;
- unsigned long flags;
- unsigned int mask = 0;
-
- if (file->f_mode & FMODE_WRITE) {
- if (!s->dma_dac.ready)
- return 0;
- poll_wait(file, &s->dma_dac.wait, wait);
- }
- if (file->f_mode & FMODE_READ) {
- if (!s->dma_adc.ready)
- return 0;
- poll_wait(file, &s->dma_adc.wait, wait);
- }
-
- spin_lock_irqsave(&s->lock, flags);
-
- if (file->f_mode & FMODE_READ) {
- if (s->dma_adc.count >= (signed)s->dma_adc.dma_fragsize)
- mask |= POLLIN | POLLRDNORM;
- }
- if (file->f_mode & FMODE_WRITE) {
- if (s->dma_dac.mapped) {
- if (s->dma_dac.count >=
- (signed)s->dma_dac.dma_fragsize)
- mask |= POLLOUT | POLLWRNORM;
- } else {
- if ((signed) s->dma_dac.dmasize >=
- s->dma_dac.count + (signed)s->dma_dac.dma_fragsize)
- mask |= POLLOUT | POLLWRNORM;
- }
- }
- spin_unlock_irqrestore(&s->lock, flags);
- return mask;
-}
-
-static int
-au1550_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct au1550_state *s = file->private_data;
- struct dmabuf *db;
- unsigned long size;
- int ret = 0;
-
- mutex_lock(&au1550_ac97_mutex);
- mutex_lock(&s->sem);
- if (vma->vm_flags & VM_WRITE)
- db = &s->dma_dac;
- else if (vma->vm_flags & VM_READ)
- db = &s->dma_adc;
- else {
- ret = -EINVAL;
- goto out;
- }
- if (vma->vm_pgoff != 0) {
- ret = -EINVAL;
- goto out;
- }
- size = vma->vm_end - vma->vm_start;
- if (size > (PAGE_SIZE << db->buforder)) {
- ret = -EINVAL;
- goto out;
- }
- if (remap_pfn_range(vma, vma->vm_start, page_to_pfn(virt_to_page(db->rawbuf)),
- size, vma->vm_page_prot)) {
- ret = -EAGAIN;
- goto out;
- }
- vma->vm_flags &= ~VM_IO;
- db->mapped = 1;
-out:
- mutex_unlock(&s->sem);
- mutex_unlock(&au1550_ac97_mutex);
- return ret;
-}
-
-#ifdef DEBUG
-static struct ioctl_str_t {
- unsigned int cmd;
- const char *str;
-} ioctl_str[] = {
- {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"},
- {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"},
- {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"},
- {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"},
- {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"},
- {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"},
- {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"},
- {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"},
- {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"},
- {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"},
- {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"},
- {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"},
- {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"},
- {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"},
- {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"},
- {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"},
- {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"},
- {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"},
- {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"},
- {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"},
- {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"},
- {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"},
- {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"},
- {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"},
- {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"},
- {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"},
- {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"},
- {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"},
- {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"},
- {OSS_GETVERSION, "OSS_GETVERSION"},
- {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"},
- {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"},
- {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"},
- {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"}
-};
-#endif
-
-static int
-dma_count_done(struct dmabuf *db)
-{
- if (db->stopped)
- return 0;
-
- return db->dma_fragsize - au1xxx_get_dma_residue(db->dmanr);
-}
-
-
-static int
-au1550_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct au1550_state *s = file->private_data;
- unsigned long flags;
- audio_buf_info abinfo;
- count_info cinfo;
- int count;
- int val, mapped, ret, diff;
-
- mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
- ((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
-
-#ifdef DEBUG
- for (count = 0; count < ARRAY_SIZE(ioctl_str); count++) {
- if (ioctl_str[count].cmd == cmd)
- break;
- }
- if (count < ARRAY_SIZE(ioctl_str))
- pr_debug("ioctl %s, arg=0x%lxn", ioctl_str[count].str, arg);
- else
- pr_debug("ioctl 0x%x unknown, arg=0x%lx\n", cmd, arg);
-#endif
-
- switch (cmd) {
- case OSS_GETVERSION:
- return put_user(SOUND_VERSION, (int *) arg);
-
- case SNDCTL_DSP_SYNC:
- if (file->f_mode & FMODE_WRITE)
- return drain_dac(s, file->f_flags & O_NONBLOCK);
- return 0;
-
- case SNDCTL_DSP_SETDUPLEX:
- return 0;
-
- case SNDCTL_DSP_GETCAPS:
- return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME |
- DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
-
- case SNDCTL_DSP_RESET:
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- synchronize_irq();
- s->dma_dac.count = s->dma_dac.total_bytes = 0;
- s->dma_dac.nextIn = s->dma_dac.nextOut =
- s->dma_dac.rawbuf;
- }
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- synchronize_irq();
- s->dma_adc.count = s->dma_adc.total_bytes = 0;
- s->dma_adc.nextIn = s->dma_adc.nextOut =
- s->dma_adc.rawbuf;
- }
- return 0;
-
- case SNDCTL_DSP_SPEED:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val >= 0) {
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- set_adc_rate(s, val);
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- set_dac_rate(s, val);
- }
- if (s->open_mode & FMODE_READ)
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- if (s->open_mode & FMODE_WRITE)
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return put_user((file->f_mode & FMODE_READ) ?
- s->dma_adc.sample_rate :
- s->dma_dac.sample_rate,
- (int *)arg);
-
- case SNDCTL_DSP_STEREO:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.num_channels = val ? 2 : 1;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.num_channels = val ? 2 : 1;
- if (s->codec_ext_caps & AC97_EXT_DACS) {
- /* disable surround and center/lfe in AC'97
- */
- u16 ext_stat = rdcodec(s->codec,
- AC97_EXTENDED_STATUS);
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ext_stat | (AC97_EXTSTAT_PRI |
- AC97_EXTSTAT_PRJ |
- AC97_EXTSTAT_PRK));
- }
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return 0;
-
- case SNDCTL_DSP_CHANNELS:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != 0) {
- if (file->f_mode & FMODE_READ) {
- if (val < 0 || val > 2)
- return -EINVAL;
- stop_adc(s);
- s->dma_adc.num_channels = val;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- switch (val) {
- case 1:
- case 2:
- break;
- case 3:
- case 5:
- return -EINVAL;
- case 4:
- if (!(s->codec_ext_caps &
- AC97_EXTID_SDAC))
- return -EINVAL;
- break;
- case 6:
- if ((s->codec_ext_caps &
- AC97_EXT_DACS) != AC97_EXT_DACS)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
-
- stop_dac(s);
- if (val <= 2 &&
- (s->codec_ext_caps & AC97_EXT_DACS)) {
- /* disable surround and center/lfe
- * channels in AC'97
- */
- u16 ext_stat =
- rdcodec(s->codec,
- AC97_EXTENDED_STATUS);
- wrcodec(s->codec,
- AC97_EXTENDED_STATUS,
- ext_stat | (AC97_EXTSTAT_PRI |
- AC97_EXTSTAT_PRJ |
- AC97_EXTSTAT_PRK));
- } else if (val >= 4) {
- /* enable surround, center/lfe
- * channels in AC'97
- */
- u16 ext_stat =
- rdcodec(s->codec,
- AC97_EXTENDED_STATUS);
- ext_stat &= ~AC97_EXTSTAT_PRJ;
- if (val == 6)
- ext_stat &=
- ~(AC97_EXTSTAT_PRI |
- AC97_EXTSTAT_PRK);
- wrcodec(s->codec,
- AC97_EXTENDED_STATUS,
- ext_stat);
- }
-
- s->dma_dac.num_channels = val;
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- }
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_GETFMTS: /* Returns a mask */
- return put_user(AFMT_S16_LE | AFMT_U8, (int *) arg);
-
- case SNDCTL_DSP_SETFMT: /* Selects ONE fmt */
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != AFMT_QUERY) {
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- if (val == AFMT_S16_LE)
- s->dma_adc.sample_size = 16;
- else {
- val = AFMT_U8;
- s->dma_adc.sample_size = 8;
- }
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- if (val == AFMT_S16_LE)
- s->dma_dac.sample_size = 16;
- else {
- val = AFMT_U8;
- s->dma_dac.sample_size = 8;
- }
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- } else {
- if (file->f_mode & FMODE_READ)
- val = (s->dma_adc.sample_size == 16) ?
- AFMT_S16_LE : AFMT_U8;
- else
- val = (s->dma_dac.sample_size == 16) ?
- AFMT_S16_LE : AFMT_U8;
- }
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_POST:
- return 0;
-
- case SNDCTL_DSP_GETTRIGGER:
- val = 0;
- spin_lock_irqsave(&s->lock, flags);
- if (file->f_mode & FMODE_READ && !s->dma_adc.stopped)
- val |= PCM_ENABLE_INPUT;
- if (file->f_mode & FMODE_WRITE && !s->dma_dac.stopped)
- val |= PCM_ENABLE_OUTPUT;
- spin_unlock_irqrestore(&s->lock, flags);
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_SETTRIGGER:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- if (val & PCM_ENABLE_INPUT) {
- spin_lock_irqsave(&s->lock, flags);
- start_adc(s);
- spin_unlock_irqrestore(&s->lock, flags);
- } else
- stop_adc(s);
- }
- if (file->f_mode & FMODE_WRITE) {
- if (val & PCM_ENABLE_OUTPUT) {
- spin_lock_irqsave(&s->lock, flags);
- start_dac(s);
- spin_unlock_irqrestore(&s->lock, flags);
- } else
- stop_dac(s);
- }
- return 0;
-
- case SNDCTL_DSP_GETOSPACE:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- abinfo.fragsize = s->dma_dac.fragsize;
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_dac.count;
- count -= dma_count_done(&s->dma_dac);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- abinfo.bytes = (s->dma_dac.dmasize - count) /
- s->dma_dac.cnt_factor;
- abinfo.fragstotal = s->dma_dac.numfrag;
- abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;
- pr_debug("ioctl SNDCTL_DSP_GETOSPACE: bytes=%d, fragments=%d\n", abinfo.bytes, abinfo.fragments);
- return copy_to_user((void *) arg, &abinfo,
- sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_GETISPACE:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- abinfo.fragsize = s->dma_adc.fragsize;
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_adc.count;
- count += dma_count_done(&s->dma_adc);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- abinfo.bytes = count / s->dma_adc.cnt_factor;
- abinfo.fragstotal = s->dma_adc.numfrag;
- abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;
- return copy_to_user((void *) arg, &abinfo,
- sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_NONBLOCK:
- spin_lock(&file->f_lock);
- file->f_flags |= O_NONBLOCK;
- spin_unlock(&file->f_lock);
- return 0;
-
- case SNDCTL_DSP_GETODELAY:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- spin_lock_irqsave(&s->lock, flags);
- count = s->dma_dac.count;
- count -= dma_count_done(&s->dma_dac);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- count /= s->dma_dac.cnt_factor;
- return put_user(count, (int *) arg);
-
- case SNDCTL_DSP_GETIPTR:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- spin_lock_irqsave(&s->lock, flags);
- cinfo.bytes = s->dma_adc.total_bytes;
- count = s->dma_adc.count;
- if (!s->dma_adc.stopped) {
- diff = dma_count_done(&s->dma_adc);
- count += diff;
- cinfo.bytes += diff;
- cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) + diff -
- virt_to_phys(s->dma_adc.rawbuf);
- } else
- cinfo.ptr = virt_to_phys(s->dma_adc.nextIn) -
- virt_to_phys(s->dma_adc.rawbuf);
- if (s->dma_adc.mapped)
- s->dma_adc.count &= (s->dma_adc.dma_fragsize-1);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- cinfo.blocks = count >> s->dma_adc.fragshift;
- return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));
-
- case SNDCTL_DSP_GETOPTR:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- spin_lock_irqsave(&s->lock, flags);
- cinfo.bytes = s->dma_dac.total_bytes;
- count = s->dma_dac.count;
- if (!s->dma_dac.stopped) {
- diff = dma_count_done(&s->dma_dac);
- count -= diff;
- cinfo.bytes += diff;
- cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) + diff -
- virt_to_phys(s->dma_dac.rawbuf);
- } else
- cinfo.ptr = virt_to_phys(s->dma_dac.nextOut) -
- virt_to_phys(s->dma_dac.rawbuf);
- if (s->dma_dac.mapped)
- s->dma_dac.count &= (s->dma_dac.dma_fragsize-1);
- spin_unlock_irqrestore(&s->lock, flags);
- if (count < 0)
- count = 0;
- cinfo.blocks = count >> s->dma_dac.fragshift;
- return copy_to_user((void *) arg, &cinfo, sizeof(cinfo));
-
- case SNDCTL_DSP_GETBLKSIZE:
- if (file->f_mode & FMODE_WRITE)
- return put_user(s->dma_dac.fragsize, (int *) arg);
- else
- return put_user(s->dma_adc.fragsize, (int *) arg);
-
- case SNDCTL_DSP_SETFRAGMENT:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.ossfragshift = val & 0xffff;
- s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
- if (s->dma_adc.ossfragshift < 4)
- s->dma_adc.ossfragshift = 4;
- if (s->dma_adc.ossfragshift > 15)
- s->dma_adc.ossfragshift = 15;
- if (s->dma_adc.ossmaxfrags < 4)
- s->dma_adc.ossmaxfrags = 4;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.ossfragshift = val & 0xffff;
- s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
- if (s->dma_dac.ossfragshift < 4)
- s->dma_dac.ossfragshift = 4;
- if (s->dma_dac.ossfragshift > 15)
- s->dma_dac.ossfragshift = 15;
- if (s->dma_dac.ossmaxfrags < 4)
- s->dma_dac.ossmaxfrags = 4;
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return 0;
-
- case SNDCTL_DSP_SUBDIVIDE:
- if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
- (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
- return -EINVAL;
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != 1 && val != 2 && val != 4)
- return -EINVAL;
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.subdivision = val;
- if ((ret = prog_dmabuf_adc(s)))
- return ret;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.subdivision = val;
- if ((ret = prog_dmabuf_dac(s)))
- return ret;
- }
- return 0;
-
- case SOUND_PCM_READ_RATE:
- return put_user((file->f_mode & FMODE_READ) ?
- s->dma_adc.sample_rate :
- s->dma_dac.sample_rate,
- (int *)arg);
-
- case SOUND_PCM_READ_CHANNELS:
- if (file->f_mode & FMODE_READ)
- return put_user(s->dma_adc.num_channels, (int *)arg);
- else
- return put_user(s->dma_dac.num_channels, (int *)arg);
-
- case SOUND_PCM_READ_BITS:
- if (file->f_mode & FMODE_READ)
- return put_user(s->dma_adc.sample_size, (int *)arg);
- else
- return put_user(s->dma_dac.sample_size, (int *)arg);
-
- case SOUND_PCM_WRITE_FILTER:
- case SNDCTL_DSP_SETSYNCRO:
- case SOUND_PCM_READ_FILTER:
- return -EINVAL;
- }
-
- return mixdev_ioctl(s->codec, cmd, arg);
-}
-
-static long
-au1550_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- int ret;
-
- mutex_lock(&au1550_ac97_mutex);
- ret = au1550_ioctl(file, cmd, arg);
- mutex_unlock(&au1550_ac97_mutex);
-
- return ret;
-}
-
-static int
-au1550_open(struct inode *inode, struct file *file)
-{
- int minor = MINOR(inode->i_rdev);
- DECLARE_WAITQUEUE(wait, current);
- struct au1550_state *s = &au1550_state;
- int ret;
-
-#ifdef DEBUG
- if (file->f_flags & O_NONBLOCK)
- pr_debug("open: non-blocking\n");
- else
- pr_debug("open: blocking\n");
-#endif
-
- file->private_data = s;
- mutex_lock(&au1550_ac97_mutex);
- /* wait for device to become free */
- mutex_lock(&s->open_mutex);
- while (s->open_mode & file->f_mode) {
- ret = -EBUSY;
- if (file->f_flags & O_NONBLOCK)
- goto out;
- add_wait_queue(&s->open_wait, &wait);
- __set_current_state(TASK_INTERRUPTIBLE);
- mutex_unlock(&s->open_mutex);
- schedule();
- remove_wait_queue(&s->open_wait, &wait);
- set_current_state(TASK_RUNNING);
- ret = -ERESTARTSYS;
- if (signal_pending(current))
- goto out2;
- mutex_lock(&s->open_mutex);
- }
-
- stop_dac(s);
- stop_adc(s);
-
- if (file->f_mode & FMODE_READ) {
- s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags =
- s->dma_adc.subdivision = s->dma_adc.total_bytes = 0;
- s->dma_adc.num_channels = 1;
- s->dma_adc.sample_size = 8;
- set_adc_rate(s, 8000);
- if ((minor & 0xf) == SND_DEV_DSP16)
- s->dma_adc.sample_size = 16;
- }
-
- if (file->f_mode & FMODE_WRITE) {
- s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags =
- s->dma_dac.subdivision = s->dma_dac.total_bytes = 0;
- s->dma_dac.num_channels = 1;
- s->dma_dac.sample_size = 8;
- set_dac_rate(s, 8000);
- if ((minor & 0xf) == SND_DEV_DSP16)
- s->dma_dac.sample_size = 16;
- }
-
- if (file->f_mode & FMODE_READ) {
- if ((ret = prog_dmabuf_adc(s)))
- goto out;
- }
- if (file->f_mode & FMODE_WRITE) {
- if ((ret = prog_dmabuf_dac(s)))
- goto out;
- }
-
- s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
- mutex_init(&s->sem);
- ret = 0;
-out:
- mutex_unlock(&s->open_mutex);
-out2:
- mutex_unlock(&au1550_ac97_mutex);
- return ret;
-}
-
-static int
-au1550_release(struct inode *inode, struct file *file)
-{
- struct au1550_state *s = file->private_data;
-
- mutex_lock(&au1550_ac97_mutex);
-
- if (file->f_mode & FMODE_WRITE) {
- mutex_unlock(&au1550_ac97_mutex);
- drain_dac(s, file->f_flags & O_NONBLOCK);
- mutex_lock(&au1550_ac97_mutex);
- }
-
- mutex_lock(&s->open_mutex);
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- kfree(s->dma_dac.rawbuf);
- s->dma_dac.rawbuf = NULL;
- }
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- kfree(s->dma_adc.rawbuf);
- s->dma_adc.rawbuf = NULL;
- }
- s->open_mode &= ((~file->f_mode) & (FMODE_READ|FMODE_WRITE));
- mutex_unlock(&s->open_mutex);
- wake_up(&s->open_wait);
- mutex_unlock(&au1550_ac97_mutex);
- return 0;
-}
-
-static /*const */ struct file_operations au1550_audio_fops = {
- .owner = THIS_MODULE,
- .llseek = au1550_llseek,
- .read = au1550_read,
- .write = au1550_write,
- .poll = au1550_poll,
- .unlocked_ioctl = au1550_unlocked_ioctl,
- .mmap = au1550_mmap,
- .open = au1550_open,
- .release = au1550_release,
-};
-
-MODULE_AUTHOR("Advanced Micro Devices (AMD), dan@embeddededge.com");
-MODULE_DESCRIPTION("Au1550 AC97 Audio Driver");
-MODULE_LICENSE("GPL");
-
-
-static int __devinit
-au1550_probe(void)
-{
- struct au1550_state *s = &au1550_state;
- int val;
-
- memset(s, 0, sizeof(struct au1550_state));
-
- init_waitqueue_head(&s->dma_adc.wait);
- init_waitqueue_head(&s->dma_dac.wait);
- init_waitqueue_head(&s->open_wait);
- mutex_init(&s->open_mutex);
- spin_lock_init(&s->lock);
-
- s->codec = ac97_alloc_codec();
- if(s->codec == NULL) {
- err("Out of memory");
- return -1;
- }
- s->codec->private_data = s;
- s->codec->id = 0;
- s->codec->codec_read = rdcodec;
- s->codec->codec_write = wrcodec;
- s->codec->codec_wait = waitcodec;
-
- if (!request_mem_region(CPHYSADDR(AC97_PSC_SEL),
- 0x30, "Au1550 AC97")) {
- err("AC'97 ports in use");
- }
-
- /* Allocate the DMA Channels
- */
- if ((s->dma_dac.dmanr = au1xxx_dbdma_chan_alloc(DBDMA_MEM_CHAN,
- DBDMA_AC97_TX_CHAN, dac_dma_interrupt, (void *)s)) == 0) {
- err("Can't get DAC DMA");
- goto err_dma1;
- }
- au1xxx_dbdma_set_devwidth(s->dma_dac.dmanr, 16);
- if (au1xxx_dbdma_ring_alloc(s->dma_dac.dmanr,
- NUM_DBDMA_DESCRIPTORS) == 0) {
- err("Can't get DAC DMA descriptors");
- goto err_dma1;
- }
-
- if ((s->dma_adc.dmanr = au1xxx_dbdma_chan_alloc(DBDMA_AC97_RX_CHAN,
- DBDMA_MEM_CHAN, adc_dma_interrupt, (void *)s)) == 0) {
- err("Can't get ADC DMA");
- goto err_dma2;
- }
- au1xxx_dbdma_set_devwidth(s->dma_adc.dmanr, 16);
- if (au1xxx_dbdma_ring_alloc(s->dma_adc.dmanr,
- NUM_DBDMA_DESCRIPTORS) == 0) {
- err("Can't get ADC DMA descriptors");
- goto err_dma2;
- }
-
- pr_info("DAC: DMA%d, ADC: DMA%d", DBDMA_AC97_TX_CHAN, DBDMA_AC97_RX_CHAN);
-
- /* register devices */
-
- if ((s->dev_audio = register_sound_dsp(&au1550_audio_fops, -1)) < 0)
- goto err_dev1;
- if ((s->codec->dev_mixer =
- register_sound_mixer(&au1550_mixer_fops, -1)) < 0)
- goto err_dev2;
-
- /* The GPIO for the appropriate PSC was configured by the
- * board specific start up.
- *
- * configure PSC for AC'97
- */
- au_writel(0, AC97_PSC_CTRL); /* Disable PSC */
- au_sync();
- au_writel((PSC_SEL_CLK_SERCLK | PSC_SEL_PS_AC97MODE), AC97_PSC_SEL);
- au_sync();
-
- /* cold reset the AC'97
- */
- au_writel(PSC_AC97RST_RST, PSC_AC97RST);
- au_sync();
- au1550_delay(10);
- au_writel(0, PSC_AC97RST);
- au_sync();
-
- /* need to delay around 500msec(bleech) to give
- some CODECs enough time to wakeup */
- au1550_delay(500);
-
- /* warm reset the AC'97 to start the bitclk
- */
- au_writel(PSC_AC97RST_SNC, PSC_AC97RST);
- au_sync();
- udelay(100);
- au_writel(0, PSC_AC97RST);
- au_sync();
-
- /* Enable PSC
- */
- au_writel(PSC_CTRL_ENABLE, AC97_PSC_CTRL);
- au_sync();
-
- /* Wait for PSC ready.
- */
- do {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((val & PSC_AC97STAT_SR) == 0);
-
- /* Configure AC97 controller.
- * Deep FIFO, 16-bit sample, DMA, make sure DMA matches fifo size.
- */
- val = PSC_AC97CFG_SET_LEN(16);
- val |= PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8;
-
- /* Enable device so we can at least
- * talk over the AC-link.
- */
- au_writel(val, PSC_AC97CFG);
- au_writel(PSC_AC97MSK_ALLMASK, PSC_AC97MSK);
- au_sync();
- val |= PSC_AC97CFG_DE_ENABLE;
- au_writel(val, PSC_AC97CFG);
- au_sync();
-
- /* Wait for Device ready.
- */
- do {
- val = au_readl(PSC_AC97STAT);
- au_sync();
- } while ((val & PSC_AC97STAT_DR) == 0);
-
- /* codec init */
- if (!ac97_probe_codec(s->codec))
- goto err_dev3;
-
- s->codec_base_caps = rdcodec(s->codec, AC97_RESET);
- s->codec_ext_caps = rdcodec(s->codec, AC97_EXTENDED_ID);
- pr_info("AC'97 Base/Extended ID = %04x/%04x",
- s->codec_base_caps, s->codec_ext_caps);
-
- if (!(s->codec_ext_caps & AC97_EXTID_VRA)) {
- /* codec does not support VRA
- */
- s->no_vra = 1;
- } else if (!vra) {
- /* Boot option says disable VRA
- */
- u16 ac97_extstat = rdcodec(s->codec, AC97_EXTENDED_STATUS);
- wrcodec(s->codec, AC97_EXTENDED_STATUS,
- ac97_extstat & ~AC97_EXTSTAT_VRA);
- s->no_vra = 1;
- }
- if (s->no_vra)
- pr_info("no VRA, interpolating and decimating");
-
- /* set mic to be the recording source */
- val = SOUND_MASK_MIC;
- mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_RECSRC,
- (unsigned long) &val);
-
- return 0;
-
- err_dev3:
- unregister_sound_mixer(s->codec->dev_mixer);
- err_dev2:
- unregister_sound_dsp(s->dev_audio);
- err_dev1:
- au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
- err_dma2:
- au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
- err_dma1:
- release_mem_region(CPHYSADDR(AC97_PSC_SEL), 0x30);
-
- ac97_release_codec(s->codec);
- return -1;
-}
-
-static void __devinit
-au1550_remove(void)
-{
- struct au1550_state *s = &au1550_state;
-
- if (!s)
- return;
- synchronize_irq();
- au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
- au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
- release_mem_region(CPHYSADDR(AC97_PSC_SEL), 0x30);
- unregister_sound_dsp(s->dev_audio);
- unregister_sound_mixer(s->codec->dev_mixer);
- ac97_release_codec(s->codec);
-}
-
-static int __init
-init_au1550(void)
-{
- return au1550_probe();
-}
-
-static void __exit
-cleanup_au1550(void)
-{
- au1550_remove();
-}
-
-module_init(init_au1550);
-module_exit(cleanup_au1550);
-
-#ifndef MODULE
-
-static int __init
-au1550_setup(char *options)
-{
- char *this_opt;
-
- if (!options || !*options)
- return 0;
-
- while ((this_opt = strsep(&options, ","))) {
- if (!*this_opt)
- continue;
- if (!strncmp(this_opt, "vra", 3)) {
- vra = 1;
- }
- }
-
- return 1;
-}
-
-__setup("au1550_audio=", au1550_setup);
-
-#endif /* MODULE */
diff --git a/sound/oss/audio.c b/sound/oss/audio.c
deleted file mode 100644
index 7df48a25c4ee..000000000000
--- a/sound/oss/audio.c
+++ /dev/null
@@ -1,985 +0,0 @@
-/*
- * sound/oss/audio.c
- *
- * Device file manager for /dev/audio
- */
-
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-/*
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * Thomas Sailer : moved several static variables into struct audio_operations
- * (which is grossly misnamed btw.) because they have the same
- * lifetime as the rest in there and dynamic allocation saves
- * 12k or so
- * Thomas Sailer : use more logical O_NONBLOCK semantics
- * Daniel Rodriksson: reworked the use of the device specific copy_user
- * still generic
- * Horst von Brand: Add missing #include <linux/string.h>
- * Chris Rankin : Update the module-usage counter for the coprocessor,
- * and decrement the counters again if we cannot open
- * the audio device.
- */
-
-#include <linux/stddef.h>
-#include <linux/string.h>
-#include <linux/kmod.h>
-
-#include "sound_config.h"
-#include "ulaw.h"
-#include "coproc.h"
-
-#define NEUTRAL8 0x80
-#define NEUTRAL16 0x00
-
-
-static int dma_ioctl(int dev, unsigned int cmd, void __user *arg);
-
-static int set_format(int dev, int fmt)
-{
- if (fmt != AFMT_QUERY)
- {
- audio_devs[dev]->local_conversion = 0;
-
- if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */
- {
- if (fmt == AFMT_MU_LAW)
- {
- fmt = AFMT_U8;
- audio_devs[dev]->local_conversion = CNV_MU_LAW;
- }
- else
- fmt = AFMT_U8; /* This is always supported */
- }
- audio_devs[dev]->audio_format = audio_devs[dev]->d->set_bits(dev, fmt);
- audio_devs[dev]->local_format = fmt;
- }
- else
- return audio_devs[dev]->local_format;
-
- if (audio_devs[dev]->local_conversion)
- return audio_devs[dev]->local_conversion;
- else
- return audio_devs[dev]->local_format;
-}
-
-int audio_open(int dev, struct file *file)
-{
- int ret;
- int bits;
- int dev_type = dev & 0x0f;
- int mode = translate_mode(file);
- const struct audio_driver *driver;
- const struct coproc_operations *coprocessor;
-
- dev = dev >> 4;
-
- if (dev_type == SND_DEV_DSP16)
- bits = 16;
- else
- bits = 8;
-
- if (dev < 0 || dev >= num_audiodevs)
- return -ENXIO;
-
- driver = audio_devs[dev]->d;
-
- if (!try_module_get(driver->owner))
- return -ENODEV;
-
- if ((ret = DMAbuf_open(dev, mode)) < 0)
- goto error_1;
-
- if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) {
- if (!try_module_get(coprocessor->owner))
- goto error_2;
-
- if ((ret = coprocessor->open(coprocessor->devc, COPR_PCM)) < 0) {
- printk(KERN_WARNING "Sound: Can't access coprocessor device\n");
- goto error_3;
- }
- }
-
- audio_devs[dev]->local_conversion = 0;
-
- if (dev_type == SND_DEV_AUDIO)
- set_format(dev, AFMT_MU_LAW);
- else
- set_format(dev, bits);
-
- audio_devs[dev]->audio_mode = AM_NONE;
-
- return 0;
-
- /*
- * Clean-up stack: this is what needs (un)doing if
- * we can't open the audio device ...
- */
- error_3:
- module_put(coprocessor->owner);
-
- error_2:
- DMAbuf_release(dev, mode);
-
- error_1:
- module_put(driver->owner);
-
- return ret;
-}
-
-static void sync_output(int dev)
-{
- int p, i;
- int l;
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
- if (dmap->fragment_size <= 0)
- return;
- dmap->flags |= DMA_POST;
-
- /* Align the write pointer with fragment boundaries */
-
- if ((l = dmap->user_counter % dmap->fragment_size) > 0)
- {
- int len;
- unsigned long offs = dmap->user_counter % dmap->bytes_in_use;
-
- len = dmap->fragment_size - l;
- memset(dmap->raw_buf + offs, dmap->neutral_byte, len);
- DMAbuf_move_wrpointer(dev, len);
- }
-
- /*
- * Clean all unused buffer fragments.
- */
-
- p = dmap->qtail;
- dmap->flags |= DMA_POST;
-
- for (i = dmap->qlen + 1; i < dmap->nbufs; i++)
- {
- p = (p + 1) % dmap->nbufs;
- if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) >
- (dmap->raw_buf + dmap->buffsize))
- printk(KERN_ERR "audio: Buffer error 2\n");
-
- memset(dmap->raw_buf + p * dmap->fragment_size,
- dmap->neutral_byte,
- dmap->fragment_size);
- }
-
- dmap->flags |= DMA_DIRTY;
-}
-
-void audio_release(int dev, struct file *file)
-{
- const struct coproc_operations *coprocessor;
- int mode = translate_mode(file);
-
- dev = dev >> 4;
-
- /*
- * We do this in DMAbuf_release(). Why are we doing it
- * here? Why don't we test the file mode before setting
- * both flags? DMAbuf_release() does.
- * ...pester...pester...pester...
- */
- audio_devs[dev]->dmap_out->closing = 1;
- audio_devs[dev]->dmap_in->closing = 1;
-
- /*
- * We need to make sure we allocated the dmap_out buffer
- * before we go mucking around with it in sync_output().
- */
- if (mode & OPEN_WRITE)
- sync_output(dev);
-
- if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) {
- coprocessor->close(coprocessor->devc, COPR_PCM);
- module_put(coprocessor->owner);
- }
- DMAbuf_release(dev, mode);
-
- module_put(audio_devs[dev]->d->owner);
-}
-
-static void translate_bytes(const unsigned char *table, unsigned char *buff, int n)
-{
- unsigned long i;
-
- if (n <= 0)
- return;
-
- for (i = 0; i < n; ++i)
- buff[i] = table[buff[i]];
-}
-
-int audio_write(int dev, struct file *file, const char __user *buf, int count)
-{
- int c, p, l, buf_size, used, returned;
- int err;
- char *dma_buf;
-
- dev = dev >> 4;
-
- p = 0;
- c = count;
-
- if(count < 0)
- return -EINVAL;
-
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return -EPERM;
-
- if (audio_devs[dev]->flags & DMA_DUPLEX)
- audio_devs[dev]->audio_mode |= AM_WRITE;
- else
- audio_devs[dev]->audio_mode = AM_WRITE;
-
- if (!count) /* Flush output */
- {
- sync_output(dev);
- return 0;
- }
-
- while (c)
- {
- if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, !!(file->f_flags & O_NONBLOCK))) < 0)
- {
- /* Handle nonblocking mode */
- if ((file->f_flags & O_NONBLOCK) && err == -EAGAIN)
- return p? p : -EAGAIN; /* No more space. Return # of accepted bytes */
- return err;
- }
- l = c;
-
- if (l > buf_size)
- l = buf_size;
-
- returned = l;
- used = l;
- if (!audio_devs[dev]->d->copy_user)
- {
- if ((dma_buf + l) >
- (audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->dmap_out->buffsize))
- {
- printk(KERN_ERR "audio: Buffer error 3 (%lx,%d), (%lx, %d)\n", (long) dma_buf, l, (long) audio_devs[dev]->dmap_out->raw_buf, (int) audio_devs[dev]->dmap_out->buffsize);
- return -EDOM;
- }
- if (dma_buf < audio_devs[dev]->dmap_out->raw_buf)
- {
- printk(KERN_ERR "audio: Buffer error 13 (%lx<%lx)\n", (long) dma_buf, (long) audio_devs[dev]->dmap_out->raw_buf);
- return -EDOM;
- }
- if(copy_from_user(dma_buf, &(buf)[p], l))
- return -EFAULT;
- }
- else audio_devs[dev]->d->copy_user (dev,
- dma_buf, 0,
- buf, p,
- c, buf_size,
- &used, &returned,
- l);
- l = returned;
-
- if (audio_devs[dev]->local_conversion & CNV_MU_LAW)
- {
- translate_bytes(ulaw_dsp, (unsigned char *) dma_buf, l);
- }
- c -= used;
- p += used;
- DMAbuf_move_wrpointer(dev, l);
-
- }
-
- return count;
-}
-
-int audio_read(int dev, struct file *file, char __user *buf, int count)
-{
- int c, p, l;
- char *dmabuf;
- int buf_no;
-
- dev = dev >> 4;
- p = 0;
- c = count;
-
- if (!(audio_devs[dev]->open_mode & OPEN_READ))
- return -EPERM;
-
- if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
- sync_output(dev);
-
- if (audio_devs[dev]->flags & DMA_DUPLEX)
- audio_devs[dev]->audio_mode |= AM_READ;
- else
- audio_devs[dev]->audio_mode = AM_READ;
-
- while(c)
- {
- if ((buf_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l, !!(file->f_flags & O_NONBLOCK))) < 0)
- {
- /*
- * Nonblocking mode handling. Return current # of bytes
- */
-
- if (p > 0) /* Avoid throwing away data */
- return p; /* Return it instead */
-
- if ((file->f_flags & O_NONBLOCK) && buf_no == -EAGAIN)
- return -EAGAIN;
-
- return buf_no;
- }
- if (l > c)
- l = c;
-
- /*
- * Insert any local processing here.
- */
-
- if (audio_devs[dev]->local_conversion & CNV_MU_LAW)
- {
- translate_bytes(dsp_ulaw, (unsigned char *) dmabuf, l);
- }
-
- {
- char *fixit = dmabuf;
-
- if(copy_to_user(&(buf)[p], fixit, l))
- return -EFAULT;
- };
-
- DMAbuf_rmchars(dev, buf_no, l);
-
- p += l;
- c -= l;
- }
-
- return count - c;
-}
-
-int audio_ioctl(int dev, struct file *file, unsigned int cmd, void __user *arg)
-{
- int val, count;
- unsigned long flags;
- struct dma_buffparms *dmap;
- int __user *p = arg;
-
- dev = dev >> 4;
-
- if (_IOC_TYPE(cmd) == 'C') {
- if (audio_devs[dev]->coproc) /* Coprocessor ioctl */
- return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0);
- /* else
- printk(KERN_DEBUG"/dev/dsp%d: No coprocessor for this device\n", dev); */
- return -ENXIO;
- }
- else switch (cmd)
- {
- case SNDCTL_DSP_SYNC:
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return 0;
- if (audio_devs[dev]->dmap_out->fragment_size == 0)
- return 0;
- sync_output(dev);
- DMAbuf_sync(dev);
- DMAbuf_reset(dev);
- return 0;
-
- case SNDCTL_DSP_POST:
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return 0;
- if (audio_devs[dev]->dmap_out->fragment_size == 0)
- return 0;
- audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY;
- sync_output(dev);
- dma_ioctl(dev, SNDCTL_DSP_POST, NULL);
- return 0;
-
- case SNDCTL_DSP_RESET:
- audio_devs[dev]->audio_mode = AM_NONE;
- DMAbuf_reset(dev);
- return 0;
-
- case SNDCTL_DSP_GETFMTS:
- val = audio_devs[dev]->format_mask | AFMT_MU_LAW;
- break;
-
- case SNDCTL_DSP_SETFMT:
- if (get_user(val, p))
- return -EFAULT;
- val = set_format(dev, val);
- break;
-
- case SNDCTL_DSP_GETISPACE:
- if (!(audio_devs[dev]->open_mode & OPEN_READ))
- return 0;
- if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
- return -EBUSY;
- return dma_ioctl(dev, cmd, arg);
-
- case SNDCTL_DSP_GETOSPACE:
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return -EPERM;
- if ((audio_devs[dev]->audio_mode & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX))
- return -EBUSY;
- return dma_ioctl(dev, cmd, arg);
-
- case SNDCTL_DSP_NONBLOCK:
- spin_lock(&file->f_lock);
- file->f_flags |= O_NONBLOCK;
- spin_unlock(&file->f_lock);
- return 0;
-
- case SNDCTL_DSP_GETCAPS:
- val = 1 | DSP_CAP_MMAP; /* Revision level of this ioctl() */
- if (audio_devs[dev]->flags & DMA_DUPLEX &&
- audio_devs[dev]->open_mode == OPEN_READWRITE)
- val |= DSP_CAP_DUPLEX;
- if (audio_devs[dev]->coproc)
- val |= DSP_CAP_COPROC;
- if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */
- val |= DSP_CAP_BATCH;
- if (audio_devs[dev]->d->trigger) /* Supports SETTRIGGER */
- val |= DSP_CAP_TRIGGER;
- break;
-
- case SOUND_PCM_WRITE_RATE:
- if (get_user(val, p))
- return -EFAULT;
- val = audio_devs[dev]->d->set_speed(dev, val);
- break;
-
- case SOUND_PCM_READ_RATE:
- val = audio_devs[dev]->d->set_speed(dev, 0);
- break;
-
- case SNDCTL_DSP_STEREO:
- if (get_user(val, p))
- return -EFAULT;
- if (val > 1 || val < 0)
- return -EINVAL;
- val = audio_devs[dev]->d->set_channels(dev, val + 1) - 1;
- break;
-
- case SOUND_PCM_WRITE_CHANNELS:
- if (get_user(val, p))
- return -EFAULT;
- val = audio_devs[dev]->d->set_channels(dev, val);
- break;
-
- case SOUND_PCM_READ_CHANNELS:
- val = audio_devs[dev]->d->set_channels(dev, 0);
- break;
-
- case SOUND_PCM_READ_BITS:
- val = audio_devs[dev]->d->set_bits(dev, 0);
- break;
-
- case SNDCTL_DSP_SETDUPLEX:
- if (audio_devs[dev]->open_mode != OPEN_READWRITE)
- return -EPERM;
- return (audio_devs[dev]->flags & DMA_DUPLEX) ? 0 : -EIO;
-
- case SNDCTL_DSP_PROFILE:
- if (get_user(val, p))
- return -EFAULT;
- if (audio_devs[dev]->open_mode & OPEN_WRITE)
- audio_devs[dev]->dmap_out->applic_profile = val;
- if (audio_devs[dev]->open_mode & OPEN_READ)
- audio_devs[dev]->dmap_in->applic_profile = val;
- return 0;
-
- case SNDCTL_DSP_GETODELAY:
- dmap = audio_devs[dev]->dmap_out;
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return -EINVAL;
- if (!(dmap->flags & DMA_ALLOC_DONE))
- {
- val=0;
- break;
- }
-
- spin_lock_irqsave(&dmap->lock,flags);
- /* Compute number of bytes that have been played */
- count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT);
- if (count < dmap->fragment_size && dmap->qhead != 0)
- count += dmap->bytes_in_use; /* Pointer wrap not handled yet */
- count += dmap->byte_counter;
-
- /* Substract current count from the number of bytes written by app */
- count = dmap->user_counter - count;
- if (count < 0)
- count = 0;
- spin_unlock_irqrestore(&dmap->lock,flags);
- val = count;
- break;
-
- default:
- return dma_ioctl(dev, cmd, arg);
- }
- return put_user(val, p);
-}
-
-void audio_init_devices(void)
-{
- /*
- * NOTE! This routine could be called several times during boot.
- */
-}
-
-void reorganize_buffers(int dev, struct dma_buffparms *dmap, int recording)
-{
- /*
- * This routine breaks the physical device buffers to logical ones.
- */
-
- struct audio_operations *dsp_dev = audio_devs[dev];
-
- unsigned i, n;
- unsigned sr, nc, sz, bsz;
-
- sr = dsp_dev->d->set_speed(dev, 0);
- nc = dsp_dev->d->set_channels(dev, 0);
- sz = dsp_dev->d->set_bits(dev, 0);
-
- if (sz == 8)
- dmap->neutral_byte = NEUTRAL8;
- else
- dmap->neutral_byte = NEUTRAL16;
-
- if (sr < 1 || nc < 1 || sz < 1)
- {
-/* printk(KERN_DEBUG "Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);*/
- sr = DSP_DEFAULT_SPEED;
- nc = 1;
- sz = 8;
- }
-
- sz = sr * nc * sz;
-
- sz /= 8; /* #bits -> #bytes */
- dmap->data_rate = sz;
-
- if (!dmap->needs_reorg)
- return;
- dmap->needs_reorg = 0;
-
- if (dmap->fragment_size == 0)
- {
- /* Compute the fragment size using the default algorithm */
-
- /*
- * Compute a buffer size for time not exceeding 1 second.
- * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds
- * of sound (using the current speed, sample size and #channels).
- */
-
- bsz = dmap->buffsize;
- while (bsz > sz)
- bsz /= 2;
-
- if (bsz == dmap->buffsize)
- bsz /= 2; /* Needs at least 2 buffers */
-
- /*
- * Split the computed fragment to smaller parts. After 3.5a9
- * the default subdivision is 4 which should give better
- * results when recording.
- */
-
- if (dmap->subdivision == 0) /* Not already set */
- {
- dmap->subdivision = 4; /* Init to the default value */
-
- if ((bsz / dmap->subdivision) > 4096)
- dmap->subdivision *= 2;
- if ((bsz / dmap->subdivision) < 4096)
- dmap->subdivision = 1;
- }
- bsz /= dmap->subdivision;
-
- if (bsz < 16)
- bsz = 16; /* Just a sanity check */
-
- dmap->fragment_size = bsz;
- }
- else
- {
- /*
- * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or
- * the buffer size computation has already been done.
- */
- if (dmap->fragment_size > (dmap->buffsize / 2))
- dmap->fragment_size = (dmap->buffsize / 2);
- bsz = dmap->fragment_size;
- }
-
- if (audio_devs[dev]->min_fragment)
- if (bsz < (1 << audio_devs[dev]->min_fragment))
- bsz = 1 << audio_devs[dev]->min_fragment;
- if (audio_devs[dev]->max_fragment)
- if (bsz > (1 << audio_devs[dev]->max_fragment))
- bsz = 1 << audio_devs[dev]->max_fragment;
- bsz &= ~0x07; /* Force size which is multiple of 8 bytes */
-#ifdef OS_DMA_ALIGN_CHECK
- OS_DMA_ALIGN_CHECK(bsz);
-#endif
-
- n = dmap->buffsize / bsz;
- if (n > MAX_SUB_BUFFERS)
- n = MAX_SUB_BUFFERS;
- if (n > dmap->max_fragments)
- n = dmap->max_fragments;
-
- if (n < 2)
- {
- n = 2;
- bsz /= 2;
- }
- dmap->nbufs = n;
- dmap->bytes_in_use = n * bsz;
- dmap->fragment_size = bsz;
- dmap->max_byte_counter = (dmap->data_rate * 60 * 60) +
- dmap->bytes_in_use; /* Approximately one hour */
-
- if (dmap->raw_buf)
- {
- memset(dmap->raw_buf, dmap->neutral_byte, dmap->bytes_in_use);
- }
-
- for (i = 0; i < dmap->nbufs; i++)
- {
- dmap->counts[i] = 0;
- }
-
- dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY;
-}
-
-static int dma_subdivide(int dev, struct dma_buffparms *dmap, int fact)
-{
- if (fact == 0)
- {
- fact = dmap->subdivision;
- if (fact == 0)
- fact = 1;
- return fact;
- }
- if (dmap->subdivision != 0 || dmap->fragment_size) /* Too late to change */
- return -EINVAL;
-
- if (fact > MAX_REALTIME_FACTOR)
- return -EINVAL;
-
- if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
- return -EINVAL;
-
- dmap->subdivision = fact;
- return fact;
-}
-
-static int dma_set_fragment(int dev, struct dma_buffparms *dmap, int fact)
-{
- int bytes, count;
-
- if (fact == 0)
- return -EIO;
-
- if (dmap->subdivision != 0 ||
- dmap->fragment_size) /* Too late to change */
- return -EINVAL;
-
- bytes = fact & 0xffff;
- count = (fact >> 16) & 0x7fff;
-
- if (count == 0)
- count = MAX_SUB_BUFFERS;
- else if (count < MAX_SUB_BUFFERS)
- count++;
-
- if (bytes < 4 || bytes > 17) /* <16 || > 512k */
- return -EINVAL;
-
- if (count < 2)
- return -EINVAL;
-
- if (audio_devs[dev]->min_fragment > 0)
- if (bytes < audio_devs[dev]->min_fragment)
- bytes = audio_devs[dev]->min_fragment;
-
- if (audio_devs[dev]->max_fragment > 0)
- if (bytes > audio_devs[dev]->max_fragment)
- bytes = audio_devs[dev]->max_fragment;
-
-#ifdef OS_DMA_MINBITS
- if (bytes < OS_DMA_MINBITS)
- bytes = OS_DMA_MINBITS;
-#endif
-
- dmap->fragment_size = (1 << bytes);
- dmap->max_fragments = count;
-
- if (dmap->fragment_size > dmap->buffsize)
- dmap->fragment_size = dmap->buffsize;
-
- if (dmap->fragment_size == dmap->buffsize &&
- audio_devs[dev]->flags & DMA_AUTOMODE)
- dmap->fragment_size /= 2; /* Needs at least 2 buffers */
-
- dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */
- return bytes | ((count - 1) << 16);
-}
-
-static int dma_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out;
- struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in;
- struct dma_buffparms *dmap;
- audio_buf_info info;
- count_info cinfo;
- int fact, ret, changed, bits, count, err;
- unsigned long flags;
-
- switch (cmd)
- {
- case SNDCTL_DSP_SUBDIVIDE:
- ret = 0;
- if (get_user(fact, (int __user *)arg))
- return -EFAULT;
- if (audio_devs[dev]->open_mode & OPEN_WRITE)
- ret = dma_subdivide(dev, dmap_out, fact);
- if (ret < 0)
- return ret;
- if (audio_devs[dev]->open_mode != OPEN_WRITE ||
- (audio_devs[dev]->flags & DMA_DUPLEX &&
- audio_devs[dev]->open_mode & OPEN_READ))
- ret = dma_subdivide(dev, dmap_in, fact);
- if (ret < 0)
- return ret;
- break;
-
- case SNDCTL_DSP_GETISPACE:
- case SNDCTL_DSP_GETOSPACE:
- dmap = dmap_out;
- if (cmd == SNDCTL_DSP_GETISPACE && !(audio_devs[dev]->open_mode & OPEN_READ))
- return -EINVAL;
- if (cmd == SNDCTL_DSP_GETOSPACE && !(audio_devs[dev]->open_mode & OPEN_WRITE))
- return -EINVAL;
- if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
- dmap = dmap_in;
- if (dmap->mapping_flags & DMA_MAP_MAPPED)
- return -EINVAL;
- if (!(dmap->flags & DMA_ALLOC_DONE))
- reorganize_buffers(dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
- info.fragstotal = dmap->nbufs;
- if (cmd == SNDCTL_DSP_GETISPACE)
- info.fragments = dmap->qlen;
- else
- {
- if (!DMAbuf_space_in_queue(dev))
- info.fragments = 0;
- else
- {
- info.fragments = DMAbuf_space_in_queue(dev);
- if (audio_devs[dev]->d->local_qlen)
- {
- int tmp = audio_devs[dev]->d->local_qlen(dev);
- if (tmp && info.fragments)
- tmp--; /*
- * This buffer has been counted twice
- */
- info.fragments -= tmp;
- }
- }
- }
- if (info.fragments < 0)
- info.fragments = 0;
- else if (info.fragments > dmap->nbufs)
- info.fragments = dmap->nbufs;
-
- info.fragsize = dmap->fragment_size;
- info.bytes = info.fragments * dmap->fragment_size;
-
- if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
- info.bytes -= dmap->counts[dmap->qhead];
- else
- {
- info.fragments = info.bytes / dmap->fragment_size;
- info.bytes -= dmap->user_counter % dmap->fragment_size;
- }
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_SETTRIGGER:
- if (get_user(bits, (int __user *)arg))
- return -EFAULT;
- bits &= audio_devs[dev]->open_mode;
- if (audio_devs[dev]->d->trigger == NULL)
- return -EINVAL;
- if (!(audio_devs[dev]->flags & DMA_DUPLEX) && (bits & PCM_ENABLE_INPUT) &&
- (bits & PCM_ENABLE_OUTPUT))
- return -EINVAL;
-
- if (bits & PCM_ENABLE_INPUT)
- {
- spin_lock_irqsave(&dmap_in->lock,flags);
- changed = (audio_devs[dev]->enable_bits ^ bits) & PCM_ENABLE_INPUT;
- if (changed && audio_devs[dev]->go)
- {
- reorganize_buffers(dev, dmap_in, 1);
- if ((err = audio_devs[dev]->d->prepare_for_input(dev,
- dmap_in->fragment_size, dmap_in->nbufs)) < 0) {
- spin_unlock_irqrestore(&dmap_in->lock,flags);
- return err;
- }
- dmap_in->dma_mode = DMODE_INPUT;
- audio_devs[dev]->enable_bits |= PCM_ENABLE_INPUT;
- DMAbuf_activate_recording(dev, dmap_in);
- } else
- audio_devs[dev]->enable_bits &= ~PCM_ENABLE_INPUT;
- spin_unlock_irqrestore(&dmap_in->lock,flags);
- }
- if (bits & PCM_ENABLE_OUTPUT)
- {
- spin_lock_irqsave(&dmap_out->lock,flags);
- changed = (audio_devs[dev]->enable_bits ^ bits) & PCM_ENABLE_OUTPUT;
- if (changed &&
- (dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) &&
- audio_devs[dev]->go)
- {
- if (!(dmap_out->flags & DMA_ALLOC_DONE))
- reorganize_buffers(dev, dmap_out, 0);
- dmap_out->dma_mode = DMODE_OUTPUT;
- audio_devs[dev]->enable_bits |= PCM_ENABLE_OUTPUT;
- dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
- DMAbuf_launch_output(dev, dmap_out);
- } else
- audio_devs[dev]->enable_bits &= ~PCM_ENABLE_OUTPUT;
- spin_unlock_irqrestore(&dmap_out->lock,flags);
- }
-#if 0
- if (changed && audio_devs[dev]->d->trigger)
- audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go);
-#endif
- /* Falls through... */
-
- case SNDCTL_DSP_GETTRIGGER:
- ret = audio_devs[dev]->enable_bits;
- break;
-
- case SNDCTL_DSP_SETSYNCRO:
- if (!audio_devs[dev]->d->trigger)
- return -EINVAL;
- audio_devs[dev]->d->trigger(dev, 0);
- audio_devs[dev]->go = 0;
- return 0;
-
- case SNDCTL_DSP_GETIPTR:
- if (!(audio_devs[dev]->open_mode & OPEN_READ))
- return -EINVAL;
- spin_lock_irqsave(&dmap_in->lock,flags);
- cinfo.bytes = dmap_in->byte_counter;
- cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_in, DMODE_INPUT) & ~3;
- if (cinfo.ptr < dmap_in->fragment_size && dmap_in->qtail != 0)
- cinfo.bytes += dmap_in->bytes_in_use; /* Pointer wrap not handled yet */
- cinfo.blocks = dmap_in->qlen;
- cinfo.bytes += cinfo.ptr;
- if (dmap_in->mapping_flags & DMA_MAP_MAPPED)
- dmap_in->qlen = 0; /* Reset interrupt counter */
- spin_unlock_irqrestore(&dmap_in->lock,flags);
- if (copy_to_user(arg, &cinfo, sizeof(cinfo)))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_GETOPTR:
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return -EINVAL;
-
- spin_lock_irqsave(&dmap_out->lock,flags);
- cinfo.bytes = dmap_out->byte_counter;
- cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_out, DMODE_OUTPUT) & ~3;
- if (cinfo.ptr < dmap_out->fragment_size && dmap_out->qhead != 0)
- cinfo.bytes += dmap_out->bytes_in_use; /* Pointer wrap not handled yet */
- cinfo.blocks = dmap_out->qlen;
- cinfo.bytes += cinfo.ptr;
- if (dmap_out->mapping_flags & DMA_MAP_MAPPED)
- dmap_out->qlen = 0; /* Reset interrupt counter */
- spin_unlock_irqrestore(&dmap_out->lock,flags);
- if (copy_to_user(arg, &cinfo, sizeof(cinfo)))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_GETODELAY:
- if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
- return -EINVAL;
- if (!(dmap_out->flags & DMA_ALLOC_DONE))
- {
- ret=0;
- break;
- }
- spin_lock_irqsave(&dmap_out->lock,flags);
- /* Compute number of bytes that have been played */
- count = DMAbuf_get_buffer_pointer (dev, dmap_out, DMODE_OUTPUT);
- if (count < dmap_out->fragment_size && dmap_out->qhead != 0)
- count += dmap_out->bytes_in_use; /* Pointer wrap not handled yet */
- count += dmap_out->byte_counter;
- /* Substract current count from the number of bytes written by app */
- count = dmap_out->user_counter - count;
- if (count < 0)
- count = 0;
- spin_unlock_irqrestore(&dmap_out->lock,flags);
- ret = count;
- break;
-
- case SNDCTL_DSP_POST:
- if (audio_devs[dev]->dmap_out->qlen > 0)
- if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE))
- DMAbuf_launch_output(dev, audio_devs[dev]->dmap_out);
- return 0;
-
- case SNDCTL_DSP_GETBLKSIZE:
- dmap = dmap_out;
- if (audio_devs[dev]->open_mode & OPEN_WRITE)
- reorganize_buffers(dev, dmap_out, (audio_devs[dev]->open_mode == OPEN_READ));
- if (audio_devs[dev]->open_mode == OPEN_READ ||
- (audio_devs[dev]->flags & DMA_DUPLEX &&
- audio_devs[dev]->open_mode & OPEN_READ))
- reorganize_buffers(dev, dmap_in, (audio_devs[dev]->open_mode == OPEN_READ));
- if (audio_devs[dev]->open_mode == OPEN_READ)
- dmap = dmap_in;
- ret = dmap->fragment_size;
- break;
-
- case SNDCTL_DSP_SETFRAGMENT:
- ret = 0;
- if (get_user(fact, (int __user *)arg))
- return -EFAULT;
- if (audio_devs[dev]->open_mode & OPEN_WRITE)
- ret = dma_set_fragment(dev, dmap_out, fact);
- if (ret < 0)
- return ret;
- if (audio_devs[dev]->open_mode == OPEN_READ ||
- (audio_devs[dev]->flags & DMA_DUPLEX &&
- audio_devs[dev]->open_mode & OPEN_READ))
- ret = dma_set_fragment(dev, dmap_in, fact);
- if (ret < 0)
- return ret;
- if (!arg) /* don't know what this is good for, but preserve old semantics */
- return 0;
- break;
-
- default:
- if (!audio_devs[dev]->d->ioctl)
- return -EINVAL;
- return audio_devs[dev]->d->ioctl(dev, cmd, arg);
- }
- return put_user(ret, (int __user *)arg);
-}
diff --git a/sound/oss/bin2hex.c b/sound/oss/bin2hex.c
deleted file mode 100644
index b59109eb0db4..000000000000
--- a/sound/oss/bin2hex.c
+++ /dev/null
@@ -1,39 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-int main( int argc, const char * argv [] )
-{
- const char * varname;
- int i = 0;
- int c;
- int id = 0;
-
- if(argv[1] && strcmp(argv[1],"-i")==0)
- {
- argv++;
- argc--;
- id=1;
- }
-
- if(argc==1)
- {
- fprintf(stderr, "bin2hex: [-i] firmware\n");
- exit(1);
- }
-
- varname = argv[1];
- printf( "/* automatically generated by bin2hex */\n" );
- printf( "static unsigned char %s [] %s =\n{\n", varname , id?"__initdata":"");
-
- while ( ( c = getchar( ) ) != EOF )
- {
- if ( i != 0 && i % 10 == 0 )
- printf( "\n" );
- printf( "0x%02lx,", c & 0xFFl );
- i++;
- }
-
- printf( "};\nstatic int %sLen = %d;\n", varname, i );
- return 0;
-}
diff --git a/sound/oss/coproc.h b/sound/oss/coproc.h
deleted file mode 100644
index 7bec21bbdd88..000000000000
--- a/sound/oss/coproc.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * Definitions for various on board processors on the sound cards. For
- * example DSP processors.
- */
-
-/*
- * Coprocessor access types
- */
-#define COPR_CUSTOM 0x0001 /* Custom applications */
-#define COPR_MIDI 0x0002 /* MIDI (MPU-401) emulation */
-#define COPR_PCM 0x0004 /* Digitized voice applications */
-#define COPR_SYNTH 0x0008 /* Music synthesis */
diff --git a/sound/oss/dev_table.c b/sound/oss/dev_table.c
deleted file mode 100644
index 727bdb9ba2dc..000000000000
--- a/sound/oss/dev_table.c
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * sound/oss/dev_table.c
- *
- * Device call tables.
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-
-#include <linux/init.h>
-
-#include "sound_config.h"
-
-struct audio_operations *audio_devs[MAX_AUDIO_DEV];
-EXPORT_SYMBOL(audio_devs);
-
-int num_audiodevs;
-EXPORT_SYMBOL(num_audiodevs);
-
-struct mixer_operations *mixer_devs[MAX_MIXER_DEV];
-EXPORT_SYMBOL(mixer_devs);
-
-int num_mixers;
-EXPORT_SYMBOL(num_mixers);
-
-struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV];
-EXPORT_SYMBOL(synth_devs);
-
-int num_synths;
-
-struct midi_operations *midi_devs[MAX_MIDI_DEV];
-EXPORT_SYMBOL(midi_devs);
-
-int num_midis;
-EXPORT_SYMBOL(num_midis);
-
-struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = {
- &default_sound_timer, NULL
-};
-EXPORT_SYMBOL(sound_timer_devs);
-
-int num_sound_timers = 1;
-
-
-static int sound_alloc_audiodev(void);
-
-int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver,
- int driver_size, int flags, unsigned int format_mask,
- void *devc, int dma1, int dma2)
-{
- struct audio_driver *d;
- struct audio_operations *op;
- int num;
-
- if (vers != AUDIO_DRIVER_VERSION || driver_size > sizeof(struct audio_driver)) {
- printk(KERN_ERR "Sound: Incompatible audio driver for %s\n", name);
- return -(EINVAL);
- }
- num = sound_alloc_audiodev();
-
- if (num == -1) {
- printk(KERN_ERR "sound: Too many audio drivers\n");
- return -(EBUSY);
- }
- d = (struct audio_driver *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_driver)));
- sound_nblocks++;
- if (sound_nblocks >= MAX_MEM_BLOCKS)
- sound_nblocks = MAX_MEM_BLOCKS - 1;
-
- op = (struct audio_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_operations)));
- sound_nblocks++;
- if (sound_nblocks >= MAX_MEM_BLOCKS)
- sound_nblocks = MAX_MEM_BLOCKS - 1;
-
- if (d == NULL || op == NULL) {
- printk(KERN_ERR "Sound: Can't allocate driver for (%s)\n", name);
- sound_unload_audiodev(num);
- return -(ENOMEM);
- }
- memset((char *) op, 0, sizeof(struct audio_operations));
- init_waitqueue_head(&op->in_sleeper);
- init_waitqueue_head(&op->out_sleeper);
- init_waitqueue_head(&op->poll_sleeper);
- if (driver_size < sizeof(struct audio_driver))
- memset((char *) d, 0, sizeof(struct audio_driver));
-
- memcpy((char *) d, (char *) driver, driver_size);
-
- op->d = d;
- strlcpy(op->name, name, sizeof(op->name));
- op->flags = flags;
- op->format_mask = format_mask;
- op->devc = devc;
-
- /*
- * Hardcoded defaults
- */
- audio_devs[num] = op;
-
- DMAbuf_init(num, dma1, dma2);
-
- audio_init_devices();
- return num;
-}
-EXPORT_SYMBOL(sound_install_audiodrv);
-
-int sound_install_mixer(int vers, char *name, struct mixer_operations *driver,
- int driver_size, void *devc)
-{
- struct mixer_operations *op;
-
- int n = sound_alloc_mixerdev();
-
- if (n == -1) {
- printk(KERN_ERR "Sound: Too many mixer drivers\n");
- return -EBUSY;
- }
- if (vers != MIXER_DRIVER_VERSION ||
- driver_size > sizeof(struct mixer_operations)) {
- printk(KERN_ERR "Sound: Incompatible mixer driver for %s\n", name);
- return -EINVAL;
- }
-
- /* FIXME: This leaks a mixer_operations struct every time its called
- until you unload sound! */
-
- op = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct mixer_operations)));
- sound_nblocks++;
- if (sound_nblocks >= MAX_MEM_BLOCKS)
- sound_nblocks = MAX_MEM_BLOCKS - 1;
-
- if (op == NULL) {
- printk(KERN_ERR "Sound: Can't allocate mixer driver for (%s)\n", name);
- return -ENOMEM;
- }
- memset((char *) op, 0, sizeof(struct mixer_operations));
- memcpy((char *) op, (char *) driver, driver_size);
-
- strlcpy(op->name, name, sizeof(op->name));
- op->devc = devc;
-
- mixer_devs[n] = op;
- return n;
-}
-EXPORT_SYMBOL(sound_install_mixer);
-
-void sound_unload_audiodev(int dev)
-{
- if (dev != -1) {
- DMAbuf_deinit(dev);
- audio_devs[dev] = NULL;
- unregister_sound_dsp((dev<<4)+3);
- }
-}
-EXPORT_SYMBOL(sound_unload_audiodev);
-
-static int sound_alloc_audiodev(void)
-{
- int i = register_sound_dsp(&oss_sound_fops, -1);
- if(i==-1)
- return i;
- i>>=4;
- if(i>=num_audiodevs)
- num_audiodevs = i + 1;
- return i;
-}
-
-int sound_alloc_mididev(void)
-{
- int i = register_sound_midi(&oss_sound_fops, -1);
- if(i==-1)
- return i;
- i>>=4;
- if(i>=num_midis)
- num_midis = i + 1;
- return i;
-}
-EXPORT_SYMBOL(sound_alloc_mididev);
-
-int sound_alloc_synthdev(void)
-{
- int i;
-
- for (i = 0; i < MAX_SYNTH_DEV; i++) {
- if (synth_devs[i] == NULL) {
- if (i >= num_synths)
- num_synths++;
- return i;
- }
- }
- return -1;
-}
-EXPORT_SYMBOL(sound_alloc_synthdev);
-
-int sound_alloc_mixerdev(void)
-{
- int i = register_sound_mixer(&oss_sound_fops, -1);
- if(i==-1)
- return -1;
- i>>=4;
- if(i>=num_mixers)
- num_mixers = i + 1;
- return i;
-}
-EXPORT_SYMBOL(sound_alloc_mixerdev);
-
-int sound_alloc_timerdev(void)
-{
- int i;
-
- for (i = 0; i < MAX_TIMER_DEV; i++) {
- if (sound_timer_devs[i] == NULL) {
- if (i >= num_sound_timers)
- num_sound_timers++;
- return i;
- }
- }
- return -1;
-}
-EXPORT_SYMBOL(sound_alloc_timerdev);
-
-void sound_unload_mixerdev(int dev)
-{
- if (dev != -1) {
- mixer_devs[dev] = NULL;
- unregister_sound_mixer(dev<<4);
- num_mixers--;
- }
-}
-EXPORT_SYMBOL(sound_unload_mixerdev);
-
-void sound_unload_mididev(int dev)
-{
- if (dev != -1) {
- midi_devs[dev] = NULL;
- unregister_sound_midi((dev<<4)+2);
- }
-}
-EXPORT_SYMBOL(sound_unload_mididev);
-
-void sound_unload_synthdev(int dev)
-{
- if (dev != -1)
- synth_devs[dev] = NULL;
-}
-EXPORT_SYMBOL(sound_unload_synthdev);
-
-void sound_unload_timerdev(int dev)
-{
- if (dev != -1)
- sound_timer_devs[dev] = NULL;
-}
-EXPORT_SYMBOL(sound_unload_timerdev);
-
diff --git a/sound/oss/dev_table.h b/sound/oss/dev_table.h
deleted file mode 100644
index b7617bee6388..000000000000
--- a/sound/oss/dev_table.h
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- * dev_table.h
- *
- * Global definitions for device call tables
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-
-
-#ifndef _DEV_TABLE_H_
-#define _DEV_TABLE_H_
-
-#include <linux/spinlock.h>
-/*
- * Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h)
- * Numbers 1000 to N are reserved for driver's internal use.
- */
-
-#define SNDCARD_DESKPROXL 27 /* Compaq Deskpro XL */
-#define SNDCARD_VIDC 28 /* ARMs VIDC */
-#define SNDCARD_SBPNP 29
-#define SNDCARD_SOFTOSS 36
-#define SNDCARD_VMIDI 37
-#define SNDCARD_OPL3SA1 38 /* Note: clash in msnd.h */
-#define SNDCARD_OPL3SA1_SB 39
-#define SNDCARD_OPL3SA1_MPU 40
-#define SNDCARD_WAVEFRONT 41
-#define SNDCARD_OPL3SA2 42
-#define SNDCARD_OPL3SA2_MPU 43
-#define SNDCARD_WAVEARTIST 44 /* Waveartist */
-#define SNDCARD_OPL3SA2_MSS 45 /* Originally missed */
-#define SNDCARD_AD1816 88
-
-/*
- * NOTE! NOTE! NOTE! NOTE!
- *
- * If you modify this file, please check the dev_table.c also.
- *
- * NOTE! NOTE! NOTE! NOTE!
- */
-
-struct driver_info
-{
- char *driver_id;
- int card_subtype; /* Driver specific. Usually 0 */
- int card_type; /* From soundcard.h */
- char *name;
- void (*attach) (struct address_info *hw_config);
- int (*probe) (struct address_info *hw_config);
- void (*unload) (struct address_info *hw_config);
-};
-
-struct card_info
-{
- int card_type; /* Link (search key) to the driver list */
- struct address_info config;
- int enabled;
- void *for_driver_use;
-};
-
-
-/*
- * Device specific parameters (used only by dmabuf.c)
- */
-#define MAX_SUB_BUFFERS (32*MAX_REALTIME_FACTOR)
-
-#define DMODE_NONE 0
-#define DMODE_OUTPUT PCM_ENABLE_OUTPUT
-#define DMODE_INPUT PCM_ENABLE_INPUT
-
-struct dma_buffparms
-{
- int dma_mode; /* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */
- int closing;
-
- /*
- * Pointers to raw buffers
- */
-
- char *raw_buf;
- unsigned long raw_buf_phys;
- int buffsize;
-
- /*
- * Device state tables
- */
-
- unsigned long flags;
-#define DMA_BUSY 0x00000001
-#define DMA_RESTART 0x00000002
-#define DMA_ACTIVE 0x00000004
-#define DMA_STARTED 0x00000008
-#define DMA_EMPTY 0x00000010
-#define DMA_ALLOC_DONE 0x00000020
-#define DMA_SYNCING 0x00000040
-#define DMA_DIRTY 0x00000080
-#define DMA_POST 0x00000100
-#define DMA_NODMA 0x00000200
-#define DMA_NOTIMEOUT 0x00000400
-
- int open_mode;
-
- /*
- * Queue parameters.
- */
- int qlen;
- int qhead;
- int qtail;
- spinlock_t lock;
-
- int cfrag; /* Current incomplete fragment (write) */
-
- int nbufs;
- int counts[MAX_SUB_BUFFERS];
- int subdivision;
-
- int fragment_size;
- int needs_reorg;
- int max_fragments;
-
- int bytes_in_use;
-
- int underrun_count;
- unsigned long byte_counter;
- unsigned long user_counter;
- unsigned long max_byte_counter;
- int data_rate; /* Bytes/second */
-
- int mapping_flags;
-#define DMA_MAP_MAPPED 0x00000001
- char neutral_byte;
- int dma; /* DMA channel */
-
- int applic_profile; /* Application profile (APF_*) */
- /* Interrupt callback stuff */
- void (*audio_callback) (int dev, int parm);
- int callback_parm;
-
- int buf_flags[MAX_SUB_BUFFERS];
-#define BUFF_EOF 0x00000001 /* Increment eof count */
-#define BUFF_DIRTY 0x00000002 /* Buffer written */
-};
-
-/*
- * Structure for use with various microcontrollers and DSP processors
- * in the recent sound cards.
- */
-typedef struct coproc_operations
-{
- char name[64];
- struct module *owner;
- int (*open) (void *devc, int sub_device);
- void (*close) (void *devc, int sub_device);
- int (*ioctl) (void *devc, unsigned int cmd, void __user * arg, int local);
- void (*reset) (void *devc);
-
- void *devc; /* Driver specific info */
-} coproc_operations;
-
-struct audio_driver
-{
- struct module *owner;
- int (*open) (int dev, int mode);
- void (*close) (int dev);
- void (*output_block) (int dev, unsigned long buf,
- int count, int intrflag);
- void (*start_input) (int dev, unsigned long buf,
- int count, int intrflag);
- int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
- int (*prepare_for_input) (int dev, int bufsize, int nbufs);
- int (*prepare_for_output) (int dev, int bufsize, int nbufs);
- void (*halt_io) (int dev);
- int (*local_qlen)(int dev);
- void (*copy_user) (int dev,
- char *localbuf, int localoffs,
- const char __user *userbuf, int useroffs,
- int max_in, int max_out,
- int *used, int *returned,
- int len);
- void (*halt_input) (int dev);
- void (*halt_output) (int dev);
- void (*trigger) (int dev, int bits);
- int (*set_speed)(int dev, int speed);
- unsigned int (*set_bits)(int dev, unsigned int bits);
- short (*set_channels)(int dev, short channels);
- void (*postprocess_write)(int dev); /* Device spesific postprocessing for written data */
- void (*preprocess_read)(int dev); /* Device spesific preprocessing for read data */
- void (*mmap)(int dev);
-};
-
-struct audio_operations
-{
- char name[128];
- int flags;
-#define NOTHING_SPECIAL 0x00
-#define NEEDS_RESTART 0x01
-#define DMA_AUTOMODE 0x02
-#define DMA_DUPLEX 0x04
-#define DMA_PSEUDO_AUTOMODE 0x08
-#define DMA_HARDSTOP 0x10
-#define DMA_EXACT 0x40
-#define DMA_NORESET 0x80
- int format_mask; /* Bitmask for supported audio formats */
- void *devc; /* Driver specific info */
- struct audio_driver *d;
- void *portc; /* Driver specific info */
- struct dma_buffparms *dmap_in, *dmap_out;
- struct coproc_operations *coproc;
- int mixer_dev;
- int enable_bits;
- int open_mode;
- int go;
- int min_fragment; /* 0 == unlimited */
- int max_fragment; /* 0 == unlimited */
- int parent_dev; /* 0 -> no parent, 1 to n -> parent=parent_dev+1 */
-
- /* fields formerly in dmabuf.c */
- wait_queue_head_t in_sleeper;
- wait_queue_head_t out_sleeper;
- wait_queue_head_t poll_sleeper;
-
- /* fields formerly in audio.c */
- int audio_mode;
-
-#define AM_NONE 0
-#define AM_WRITE OPEN_WRITE
-#define AM_READ OPEN_READ
-
- int local_format;
- int audio_format;
- int local_conversion;
-#define CNV_MU_LAW 0x00000001
-
- /* large structures at the end to keep offsets small */
- struct dma_buffparms dmaps[2];
-};
-
-int *load_mixer_volumes(char *name, int *levels, int present);
-
-struct mixer_operations
-{
- struct module *owner;
- char id[16];
- char name[64];
- int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
-
- void *devc;
- int modify_counter;
-};
-
-struct synth_operations
-{
- struct module *owner;
- char *id; /* Unique identifier (ASCII) max 29 char */
- struct synth_info *info;
- int midi_dev;
- int synth_type;
- int synth_subtype;
-
- int (*open) (int dev, int mode);
- void (*close) (int dev);
- int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
- int (*kill_note) (int dev, int voice, int note, int velocity);
- int (*start_note) (int dev, int voice, int note, int velocity);
- int (*set_instr) (int dev, int voice, int instr);
- void (*reset) (int dev);
- void (*hw_control) (int dev, unsigned char *event);
- int (*load_patch) (int dev, int format, const char __user *addr,
- int offs, int count, int pmgr_flag);
- void (*aftertouch) (int dev, int voice, int pressure);
- void (*controller) (int dev, int voice, int ctrl_num, int value);
- void (*panning) (int dev, int voice, int value);
- void (*volume_method) (int dev, int mode);
- void (*bender) (int dev, int chn, int value);
- int (*alloc_voice) (int dev, int chn, int note, struct voice_alloc_info *alloc);
- void (*setup_voice) (int dev, int voice, int chn);
- int (*send_sysex)(int dev, unsigned char *bytes, int len);
-
- struct voice_alloc_info alloc;
- struct channel_info chn_info[16];
- int emulation;
-#define EMU_GM 1 /* General MIDI */
-#define EMU_XG 2 /* Yamaha XG */
-#define MAX_SYSEX_BUF 64
- unsigned char sysex_buf[MAX_SYSEX_BUF];
- int sysex_ptr;
-};
-
-struct midi_input_info
-{
- /* MIDI input scanner variables */
-#define MI_MAX 10
- volatile int m_busy;
- unsigned char m_buf[MI_MAX];
- unsigned char m_prev_status; /* For running status */
- int m_ptr;
-#define MST_INIT 0
-#define MST_DATA 1
-#define MST_SYSEX 2
- int m_state;
- int m_left;
-};
-
-struct midi_operations
-{
- struct module *owner;
- struct midi_info info;
- struct synth_operations *converter;
- struct midi_input_info in_info;
- int (*open) (int dev, int mode,
- void (*inputintr)(int dev, unsigned char data),
- void (*outputintr)(int dev)
- );
- void (*close) (int dev);
- int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
- int (*outputc) (int dev, unsigned char data);
- int (*start_read) (int dev);
- int (*end_read) (int dev);
- void (*kick)(int dev);
- int (*command) (int dev, unsigned char *data);
- int (*buffer_status) (int dev);
- int (*prefix_cmd) (int dev, unsigned char status);
- struct coproc_operations *coproc;
- void *devc;
-};
-
-struct sound_lowlev_timer
-{
- int dev;
- int priority;
- unsigned int (*tmr_start)(int dev, unsigned int usecs);
- void (*tmr_disable)(int dev);
- void (*tmr_restart)(int dev);
-};
-
-struct sound_timer_operations
-{
- struct module *owner;
- struct sound_timer_info info;
- int priority;
- int devlink;
- int (*open)(int dev, int mode);
- void (*close)(int dev);
- int (*event)(int dev, unsigned char *ev);
- unsigned long (*get_time)(int dev);
- int (*ioctl) (int dev, unsigned int cmd, void __user * arg);
- void (*arm_timer)(int dev, long time);
-};
-
-extern struct sound_timer_operations default_sound_timer;
-
-extern struct audio_operations *audio_devs[MAX_AUDIO_DEV];
-extern int num_audiodevs;
-extern struct mixer_operations *mixer_devs[MAX_MIXER_DEV];
-extern int num_mixers;
-extern struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV];
-extern int num_synths;
-extern struct midi_operations *midi_devs[MAX_MIDI_DEV];
-extern int num_midis;
-extern struct sound_timer_operations * sound_timer_devs[MAX_TIMER_DEV];
-extern int num_sound_timers;
-
-extern int sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc *info);
-void sound_timer_init (struct sound_lowlev_timer *t, char *name);
-void sound_dma_intr (int dev, struct dma_buffparms *dmap, int chan);
-
-#define AUDIO_DRIVER_VERSION 2
-#define MIXER_DRIVER_VERSION 2
-int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver,
- int driver_size, int flags, unsigned int format_mask,
- void *devc, int dma1, int dma2);
-int sound_install_mixer(int vers, char *name, struct mixer_operations *driver,
- int driver_size, void *devc);
-
-void sound_unload_audiodev(int dev);
-void sound_unload_mixerdev(int dev);
-void sound_unload_mididev(int dev);
-void sound_unload_synthdev(int dev);
-void sound_unload_timerdev(int dev);
-int sound_alloc_mixerdev(void);
-int sound_alloc_timerdev(void);
-int sound_alloc_synthdev(void);
-int sound_alloc_mididev(void);
-#endif /* _DEV_TABLE_H_ */
-
diff --git a/sound/oss/dmabuf.c b/sound/oss/dmabuf.c
deleted file mode 100644
index bcc3e8e07122..000000000000
--- a/sound/oss/dmabuf.c
+++ /dev/null
@@ -1,1268 +0,0 @@
-/*
- * sound/oss/dmabuf.c
- *
- * The DMA buffer manager for digitized voice applications
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- * Thomas Sailer : moved several static variables into struct audio_operations
- * (which is grossly misnamed btw.) because they have the same
- * lifetime as the rest in there and dynamic allocation saves
- * 12k or so
- * Thomas Sailer : remove {in,out}_sleep_flag. It was used for the sleeper to
- * determine if it was woken up by the expiring timeout or by
- * an explicit wake_up. The return value from schedule_timeout
- * can be used instead; if 0, the wakeup was due to the timeout.
- *
- * Rob Riggs Added persistent DMA buffers (1998/10/17)
- */
-
-#define BE_CONSERVATIVE
-#define SAMPLE_ROUNDUP 0
-
-#include <linux/mm.h>
-#include <linux/gfp.h>
-#include "sound_config.h"
-
-#define DMAP_FREE_ON_CLOSE 0
-#define DMAP_KEEP_ON_CLOSE 1
-extern int sound_dmap_flag;
-
-static void dma_reset_output(int dev);
-static void dma_reset_input(int dev);
-static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode);
-
-
-
-static int debugmem; /* switched off by default */
-static int dma_buffsize = DSP_BUFFSIZE;
-
-static long dmabuf_timeout(struct dma_buffparms *dmap)
-{
- long tmout;
-
- tmout = (dmap->fragment_size * HZ) / dmap->data_rate;
- tmout += HZ / 5; /* Some safety distance */
- if (tmout < (HZ / 2))
- tmout = HZ / 2;
- if (tmout > 20 * HZ)
- tmout = 20 * HZ;
- return tmout;
-}
-
-static int sound_alloc_dmap(struct dma_buffparms *dmap)
-{
- char *start_addr, *end_addr;
- int dma_pagesize;
- int sz, size;
- struct page *page;
-
- dmap->mapping_flags &= ~DMA_MAP_MAPPED;
-
- if (dmap->raw_buf != NULL)
- return 0; /* Already done */
- if (dma_buffsize < 4096)
- dma_buffsize = 4096;
- dma_pagesize = (dmap->dma < 4) ? (64 * 1024) : (128 * 1024);
-
- /*
- * Now check for the Cyrix problem.
- */
-
- if(isa_dma_bridge_buggy==2)
- dma_pagesize=32768;
-
- dmap->raw_buf = NULL;
- dmap->buffsize = dma_buffsize;
- if (dmap->buffsize > dma_pagesize)
- dmap->buffsize = dma_pagesize;
- start_addr = NULL;
- /*
- * Now loop until we get a free buffer. Try to get smaller buffer if
- * it fails. Don't accept smaller than 8k buffer for performance
- * reasons.
- */
- while (start_addr == NULL && dmap->buffsize > PAGE_SIZE) {
- for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1);
- dmap->buffsize = PAGE_SIZE * (1 << sz);
- start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA|__GFP_NOWARN, sz);
- if (start_addr == NULL)
- dmap->buffsize /= 2;
- }
-
- if (start_addr == NULL) {
- printk(KERN_WARNING "Sound error: Couldn't allocate DMA buffer\n");
- return -ENOMEM;
- } else {
- /* make some checks */
- end_addr = start_addr + dmap->buffsize - 1;
-
- if (debugmem)
- printk(KERN_DEBUG "sound: start 0x%lx, end 0x%lx\n", (long) start_addr, (long) end_addr);
-
- /* now check if it fits into the same dma-pagesize */
-
- if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1))
- || end_addr >= (char *) (MAX_DMA_ADDRESS)) {
- printk(KERN_ERR "sound: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, dmap->buffsize);
- return -EFAULT;
- }
- }
- dmap->raw_buf = start_addr;
- dmap->raw_buf_phys = virt_to_bus(start_addr);
-
- for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
- SetPageReserved(page);
- return 0;
-}
-
-static void sound_free_dmap(struct dma_buffparms *dmap)
-{
- int sz, size;
- struct page *page;
- unsigned long start_addr, end_addr;
-
- if (dmap->raw_buf == NULL)
- return;
- if (dmap->mapping_flags & DMA_MAP_MAPPED)
- return; /* Don't free mmapped buffer. Will use it next time */
- for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1);
-
- start_addr = (unsigned long) dmap->raw_buf;
- end_addr = start_addr + dmap->buffsize;
-
- for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++)
- ClearPageReserved(page);
-
- free_pages((unsigned long) dmap->raw_buf, sz);
- dmap->raw_buf = NULL;
-}
-
-
-/* Intel version !!!!!!!!! */
-
-static int sound_start_dma(struct dma_buffparms *dmap, unsigned long physaddr, int count, int dma_mode)
-{
- unsigned long flags;
- int chan = dmap->dma;
-
- /* printk( "Start DMA%d %d, %d\n", chan, (int)(physaddr-dmap->raw_buf_phys), count); */
-
- flags = claim_dma_lock();
- disable_dma(chan);
- clear_dma_ff(chan);
- set_dma_mode(chan, dma_mode);
- set_dma_addr(chan, physaddr);
- set_dma_count(chan, count);
- enable_dma(chan);
- release_dma_lock(flags);
-
- return 0;
-}
-
-static void dma_init_buffers(struct dma_buffparms *dmap)
-{
- dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
- dmap->byte_counter = 0;
- dmap->max_byte_counter = 8000 * 60 * 60;
- dmap->bytes_in_use = dmap->buffsize;
-
- dmap->dma_mode = DMODE_NONE;
- dmap->mapping_flags = 0;
- dmap->neutral_byte = 0x80;
- dmap->data_rate = 8000;
- dmap->cfrag = -1;
- dmap->closing = 0;
- dmap->nbufs = 1;
- dmap->flags = DMA_BUSY; /* Other flags off */
-}
-
-static int open_dmap(struct audio_operations *adev, int mode, struct dma_buffparms *dmap)
-{
- int err;
-
- if (dmap->flags & DMA_BUSY)
- return -EBUSY;
- if ((err = sound_alloc_dmap(dmap)) < 0)
- return err;
-
- if (dmap->raw_buf == NULL) {
- printk(KERN_WARNING "Sound: DMA buffers not available\n");
- return -ENOSPC; /* Memory allocation failed during boot */
- }
- if (dmap->dma >= 0 && sound_open_dma(dmap->dma, adev->name)) {
- printk(KERN_WARNING "Unable to grab(2) DMA%d for the audio driver\n", dmap->dma);
- return -EBUSY;
- }
- dma_init_buffers(dmap);
- spin_lock_init(&dmap->lock);
- dmap->open_mode = mode;
- dmap->subdivision = dmap->underrun_count = 0;
- dmap->fragment_size = 0;
- dmap->max_fragments = 65536; /* Just a large value */
- dmap->byte_counter = 0;
- dmap->max_byte_counter = 8000 * 60 * 60;
- dmap->applic_profile = APF_NORMAL;
- dmap->needs_reorg = 1;
- dmap->audio_callback = NULL;
- dmap->callback_parm = 0;
- return 0;
-}
-
-static void close_dmap(struct audio_operations *adev, struct dma_buffparms *dmap)
-{
- unsigned long flags;
-
- if (dmap->dma >= 0) {
- sound_close_dma(dmap->dma);
- flags=claim_dma_lock();
- disable_dma(dmap->dma);
- release_dma_lock(flags);
- }
- if (dmap->flags & DMA_BUSY)
- dmap->dma_mode = DMODE_NONE;
- dmap->flags &= ~DMA_BUSY;
-
- if (sound_dmap_flag == DMAP_FREE_ON_CLOSE)
- sound_free_dmap(dmap);
-}
-
-
-static unsigned int default_set_bits(int dev, unsigned int bits)
-{
- mm_segment_t fs = get_fs();
-
- set_fs(get_ds());
- audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SETFMT, (void __user *)&bits);
- set_fs(fs);
- return bits;
-}
-
-static int default_set_speed(int dev, int speed)
-{
- mm_segment_t fs = get_fs();
-
- set_fs(get_ds());
- audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SPEED, (void __user *)&speed);
- set_fs(fs);
- return speed;
-}
-
-static short default_set_channels(int dev, short channels)
-{
- int c = channels;
- mm_segment_t fs = get_fs();
-
- set_fs(get_ds());
- audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_CHANNELS, (void __user *)&c);
- set_fs(fs);
- return c;
-}
-
-static void check_driver(struct audio_driver *d)
-{
- if (d->set_speed == NULL)
- d->set_speed = default_set_speed;
- if (d->set_bits == NULL)
- d->set_bits = default_set_bits;
- if (d->set_channels == NULL)
- d->set_channels = default_set_channels;
-}
-
-int DMAbuf_open(int dev, int mode)
-{
- struct audio_operations *adev = audio_devs[dev];
- int retval;
- struct dma_buffparms *dmap_in = NULL;
- struct dma_buffparms *dmap_out = NULL;
-
- if (!adev)
- return -ENXIO;
- if (!(adev->flags & DMA_DUPLEX))
- adev->dmap_in = adev->dmap_out;
- check_driver(adev->d);
-
- if ((retval = adev->d->open(dev, mode)) < 0)
- return retval;
- dmap_out = adev->dmap_out;
- dmap_in = adev->dmap_in;
- if (dmap_in == dmap_out)
- adev->flags &= ~DMA_DUPLEX;
-
- if (mode & OPEN_WRITE) {
- if ((retval = open_dmap(adev, mode, dmap_out)) < 0) {
- adev->d->close(dev);
- return retval;
- }
- }
- adev->enable_bits = mode;
-
- if (mode == OPEN_READ || (mode != OPEN_WRITE && (adev->flags & DMA_DUPLEX))) {
- if ((retval = open_dmap(adev, mode, dmap_in)) < 0) {
- adev->d->close(dev);
- if (mode & OPEN_WRITE)
- close_dmap(adev, dmap_out);
- return retval;
- }
- }
- adev->open_mode = mode;
- adev->go = 1;
-
- adev->d->set_bits(dev, 8);
- adev->d->set_channels(dev, 1);
- adev->d->set_speed(dev, DSP_DEFAULT_SPEED);
- if (adev->dmap_out->dma_mode == DMODE_OUTPUT)
- memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte,
- adev->dmap_out->bytes_in_use);
- return 0;
-}
-/* MUST not hold the spinlock */
-void DMAbuf_reset(int dev)
-{
- if (audio_devs[dev]->open_mode & OPEN_WRITE)
- dma_reset_output(dev);
-
- if (audio_devs[dev]->open_mode & OPEN_READ)
- dma_reset_input(dev);
-}
-
-static void dma_reset_output(int dev)
-{
- struct audio_operations *adev = audio_devs[dev];
- unsigned long flags,f ;
- struct dma_buffparms *dmap = adev->dmap_out;
-
- if (!(dmap->flags & DMA_STARTED)) /* DMA is not active */
- return;
-
- /*
- * First wait until the current fragment has been played completely
- */
- spin_lock_irqsave(&dmap->lock,flags);
- adev->dmap_out->flags |= DMA_SYNCING;
-
- adev->dmap_out->underrun_count = 0;
- if (!signal_pending(current) && adev->dmap_out->qlen &&
- adev->dmap_out->underrun_count == 0){
- spin_unlock_irqrestore(&dmap->lock,flags);
- interruptible_sleep_on_timeout(&adev->out_sleeper,
- dmabuf_timeout(dmap));
- spin_lock_irqsave(&dmap->lock,flags);
- }
- adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
-
- /*
- * Finally shut the device off
- */
- if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_output)
- adev->d->halt_io(dev);
- else
- adev->d->halt_output(dev);
- adev->dmap_out->flags &= ~DMA_STARTED;
-
- f=claim_dma_lock();
- clear_dma_ff(dmap->dma);
- disable_dma(dmap->dma);
- release_dma_lock(f);
-
- dmap->byte_counter = 0;
- reorganize_buffers(dev, adev->dmap_out, 0);
- dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
- spin_unlock_irqrestore(&dmap->lock,flags);
-}
-
-static void dma_reset_input(int dev)
-{
- struct audio_operations *adev = audio_devs[dev];
- unsigned long flags;
- struct dma_buffparms *dmap = adev->dmap_in;
-
- spin_lock_irqsave(&dmap->lock,flags);
- if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_input)
- adev->d->halt_io(dev);
- else
- adev->d->halt_input(dev);
- adev->dmap_in->flags &= ~DMA_STARTED;
-
- dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
- dmap->byte_counter = 0;
- reorganize_buffers(dev, adev->dmap_in, 1);
- spin_unlock_irqrestore(&dmap->lock,flags);
-}
-/* MUST be called with holding the dmap->lock */
-void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap)
-{
- struct audio_operations *adev = audio_devs[dev];
-
- if (!((adev->enable_bits * adev->go) & PCM_ENABLE_OUTPUT))
- return; /* Don't start DMA yet */
- dmap->dma_mode = DMODE_OUTPUT;
-
- if (!(dmap->flags & DMA_ACTIVE) || !(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) {
- if (!(dmap->flags & DMA_STARTED)) {
- reorganize_buffers(dev, dmap, 0);
- if (adev->d->prepare_for_output(dev, dmap->fragment_size, dmap->nbufs))
- return;
- if (!(dmap->flags & DMA_NODMA))
- local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_WRITE);
- dmap->flags |= DMA_STARTED;
- }
- if (dmap->counts[dmap->qhead] == 0)
- dmap->counts[dmap->qhead] = dmap->fragment_size;
- dmap->dma_mode = DMODE_OUTPUT;
- adev->d->output_block(dev, dmap->raw_buf_phys + dmap->qhead * dmap->fragment_size,
- dmap->counts[dmap->qhead], 1);
- if (adev->d->trigger)
- adev->d->trigger(dev,adev->enable_bits * adev->go);
- }
- dmap->flags |= DMA_ACTIVE;
-}
-
-int DMAbuf_sync(int dev)
-{
- struct audio_operations *adev = audio_devs[dev];
- unsigned long flags;
- int n = 0;
- struct dma_buffparms *dmap;
-
- if (!adev->go && !(adev->enable_bits & PCM_ENABLE_OUTPUT))
- return 0;
-
- if (adev->dmap_out->dma_mode == DMODE_OUTPUT) {
- dmap = adev->dmap_out;
- spin_lock_irqsave(&dmap->lock,flags);
- if (dmap->qlen > 0 && !(dmap->flags & DMA_ACTIVE))
- DMAbuf_launch_output(dev, dmap);
- adev->dmap_out->flags |= DMA_SYNCING;
- adev->dmap_out->underrun_count = 0;
- while (!signal_pending(current) && n++ < adev->dmap_out->nbufs &&
- adev->dmap_out->qlen && adev->dmap_out->underrun_count == 0) {
- long t = dmabuf_timeout(dmap);
- spin_unlock_irqrestore(&dmap->lock,flags);
- /* FIXME: not safe may miss events */
- t = interruptible_sleep_on_timeout(&adev->out_sleeper, t);
- spin_lock_irqsave(&dmap->lock,flags);
- if (!t) {
- adev->dmap_out->flags &= ~DMA_SYNCING;
- spin_unlock_irqrestore(&dmap->lock,flags);
- return adev->dmap_out->qlen;
- }
- }
- adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
-
- /*
- * Some devices such as GUS have huge amount of on board RAM for the
- * audio data. We have to wait until the device has finished playing.
- */
-
- /* still holding the lock */
- if (adev->d->local_qlen) { /* Device has hidden buffers */
- while (!signal_pending(current) &&
- adev->d->local_qlen(dev)){
- spin_unlock_irqrestore(&dmap->lock,flags);
- interruptible_sleep_on_timeout(&adev->out_sleeper,
- dmabuf_timeout(dmap));
- spin_lock_irqsave(&dmap->lock,flags);
- }
- }
- spin_unlock_irqrestore(&dmap->lock,flags);
- }
- adev->dmap_out->dma_mode = DMODE_NONE;
- return adev->dmap_out->qlen;
-}
-
-int DMAbuf_release(int dev, int mode)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap;
- unsigned long flags;
-
- dmap = adev->dmap_out;
- if (adev->open_mode & OPEN_WRITE)
- adev->dmap_out->closing = 1;
-
- if (adev->open_mode & OPEN_READ){
- adev->dmap_in->closing = 1;
- dmap = adev->dmap_in;
- }
- if (adev->open_mode & OPEN_WRITE)
- if (!(adev->dmap_out->mapping_flags & DMA_MAP_MAPPED))
- if (!signal_pending(current) && (adev->dmap_out->dma_mode == DMODE_OUTPUT))
- DMAbuf_sync(dev);
- if (adev->dmap_out->dma_mode == DMODE_OUTPUT)
- memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, adev->dmap_out->bytes_in_use);
-
- DMAbuf_reset(dev);
- spin_lock_irqsave(&dmap->lock,flags);
- adev->d->close(dev);
-
- if (adev->open_mode & OPEN_WRITE)
- close_dmap(adev, adev->dmap_out);
-
- if (adev->open_mode == OPEN_READ ||
- (adev->open_mode != OPEN_WRITE &&
- (adev->flags & DMA_DUPLEX)))
- close_dmap(adev, adev->dmap_in);
- adev->open_mode = 0;
- spin_unlock_irqrestore(&dmap->lock,flags);
- return 0;
-}
-/* called with dmap->lock dold */
-int DMAbuf_activate_recording(int dev, struct dma_buffparms *dmap)
-{
- struct audio_operations *adev = audio_devs[dev];
- int err;
-
- if (!(adev->open_mode & OPEN_READ))
- return 0;
- if (!(adev->enable_bits & PCM_ENABLE_INPUT))
- return 0;
- if (dmap->dma_mode == DMODE_OUTPUT) { /* Direction change */
- /* release lock - it's not recursive */
- spin_unlock_irq(&dmap->lock);
- DMAbuf_sync(dev);
- DMAbuf_reset(dev);
- spin_lock_irq(&dmap->lock);
- dmap->dma_mode = DMODE_NONE;
- }
- if (!dmap->dma_mode) {
- reorganize_buffers(dev, dmap, 1);
- if ((err = adev->d->prepare_for_input(dev,
- dmap->fragment_size, dmap->nbufs)) < 0)
- return err;
- dmap->dma_mode = DMODE_INPUT;
- }
- if (!(dmap->flags & DMA_ACTIVE)) {
- if (dmap->needs_reorg)
- reorganize_buffers(dev, dmap, 0);
- local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ);
- adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size,
- dmap->fragment_size, 0);
- dmap->flags |= DMA_ACTIVE;
- if (adev->d->trigger)
- adev->d->trigger(dev, adev->enable_bits * adev->go);
- }
- return 0;
-}
-/* acquires lock */
-int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock)
-{
- struct audio_operations *adev = audio_devs[dev];
- unsigned long flags;
- int err = 0, n = 0;
- struct dma_buffparms *dmap = adev->dmap_in;
- int go;
-
- if (!(adev->open_mode & OPEN_READ))
- return -EIO;
- spin_lock_irqsave(&dmap->lock,flags);
- if (dmap->needs_reorg)
- reorganize_buffers(dev, dmap, 0);
- if (adev->dmap_in->mapping_flags & DMA_MAP_MAPPED) {
-/* printk(KERN_WARNING "Sound: Can't read from mmapped device (1)\n");*/
- spin_unlock_irqrestore(&dmap->lock,flags);
- return -EINVAL;
- } else while (dmap->qlen <= 0 && n++ < 10) {
- long timeout = MAX_SCHEDULE_TIMEOUT;
- if (!(adev->enable_bits & PCM_ENABLE_INPUT) || !adev->go) {
- spin_unlock_irqrestore(&dmap->lock,flags);
- return -EAGAIN;
- }
- if ((err = DMAbuf_activate_recording(dev, dmap)) < 0) {
- spin_unlock_irqrestore(&dmap->lock,flags);
- return err;
- }
- /* Wait for the next block */
-
- if (dontblock) {
- spin_unlock_irqrestore(&dmap->lock,flags);
- return -EAGAIN;
- }
- if ((go = adev->go))
- timeout = dmabuf_timeout(dmap);
-
- spin_unlock_irqrestore(&dmap->lock,flags);
- timeout = interruptible_sleep_on_timeout(&adev->in_sleeper,
- timeout);
- if (!timeout) {
- /* FIXME: include device name */
- err = -EIO;
- printk(KERN_WARNING "Sound: DMA (input) timed out - IRQ/DRQ config error?\n");
- dma_reset_input(dev);
- } else
- err = -EINTR;
- spin_lock_irqsave(&dmap->lock,flags);
- }
- spin_unlock_irqrestore(&dmap->lock,flags);
-
- if (dmap->qlen <= 0)
- return err ? err : -EINTR;
- *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]];
- *len = dmap->fragment_size - dmap->counts[dmap->qhead];
-
- return dmap->qhead;
-}
-
-int DMAbuf_rmchars(int dev, int buff_no, int c)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap = adev->dmap_in;
- int p = dmap->counts[dmap->qhead] + c;
-
- if (dmap->mapping_flags & DMA_MAP_MAPPED)
- {
-/* printk("Sound: Can't read from mmapped device (2)\n");*/
- return -EINVAL;
- }
- else if (dmap->qlen <= 0)
- return -EIO;
- else if (p >= dmap->fragment_size) { /* This buffer is completely empty */
- dmap->counts[dmap->qhead] = 0;
- dmap->qlen--;
- dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
- }
- else dmap->counts[dmap->qhead] = p;
-
- return 0;
-}
-/* MUST be called with dmap->lock hold */
-int DMAbuf_get_buffer_pointer(int dev, struct dma_buffparms *dmap, int direction)
-{
- /*
- * Try to approximate the active byte position of the DMA pointer within the
- * buffer area as well as possible.
- */
-
- int pos;
- unsigned long f;
-
- if (!(dmap->flags & DMA_ACTIVE))
- pos = 0;
- else {
- int chan = dmap->dma;
-
- f=claim_dma_lock();
- clear_dma_ff(chan);
-
- if(!isa_dma_bridge_buggy)
- disable_dma(dmap->dma);
-
- pos = get_dma_residue(chan);
-
- pos = dmap->bytes_in_use - pos;
-
- if (!(dmap->mapping_flags & DMA_MAP_MAPPED)) {
- if (direction == DMODE_OUTPUT) {
- if (dmap->qhead == 0)
- if (pos > dmap->fragment_size)
- pos = 0;
- } else {
- if (dmap->qtail == 0)
- if (pos > dmap->fragment_size)
- pos = 0;
- }
- }
- if (pos < 0)
- pos = 0;
- if (pos >= dmap->bytes_in_use)
- pos = 0;
-
- if(!isa_dma_bridge_buggy)
- enable_dma(dmap->dma);
-
- release_dma_lock(f);
- }
- /* printk( "%04x ", pos); */
-
- return pos;
-}
-
-/*
- * DMAbuf_start_devices() is called by the /dev/music driver to start
- * one or more audio devices at desired moment.
- */
-
-void DMAbuf_start_devices(unsigned int devmask)
-{
- struct audio_operations *adev;
- int dev;
-
- for (dev = 0; dev < num_audiodevs; dev++) {
- if (!(devmask & (1 << dev)))
- continue;
- if (!(adev = audio_devs[dev]))
- continue;
- if (adev->open_mode == 0)
- continue;
- if (adev->go)
- continue;
- /* OK to start the device */
- adev->go = 1;
- if (adev->d->trigger)
- adev->d->trigger(dev,adev->enable_bits * adev->go);
- }
-}
-/* via poll called without a lock ?*/
-int DMAbuf_space_in_queue(int dev)
-{
- struct audio_operations *adev = audio_devs[dev];
- int len, max, tmp;
- struct dma_buffparms *dmap = adev->dmap_out;
- int lim = dmap->nbufs;
-
- if (lim < 2)
- lim = 2;
-
- if (dmap->qlen >= lim) /* No space at all */
- return 0;
-
- /*
- * Verify that there are no more pending buffers than the limit
- * defined by the process.
- */
-
- max = dmap->max_fragments;
- if (max > lim)
- max = lim;
- len = dmap->qlen;
-
- if (adev->d->local_qlen) {
- tmp = adev->d->local_qlen(dev);
- if (tmp && len)
- tmp--; /* This buffer has been counted twice */
- len += tmp;
- }
- if (dmap->byte_counter % dmap->fragment_size) /* There is a partial fragment */
- len = len + 1;
-
- if (len >= max)
- return 0;
- return max - len;
-}
-/* MUST not hold the spinlock - this function may sleep */
-static int output_sleep(int dev, int dontblock)
-{
- struct audio_operations *adev = audio_devs[dev];
- int err = 0;
- struct dma_buffparms *dmap = adev->dmap_out;
- long timeout;
- long timeout_value;
-
- if (dontblock)
- return -EAGAIN;
- if (!(adev->enable_bits & PCM_ENABLE_OUTPUT))
- return -EAGAIN;
-
- /*
- * Wait for free space
- */
- if (signal_pending(current))
- return -EINTR;
- timeout = (adev->go && !(dmap->flags & DMA_NOTIMEOUT));
- if (timeout)
- timeout_value = dmabuf_timeout(dmap);
- else
- timeout_value = MAX_SCHEDULE_TIMEOUT;
- timeout_value = interruptible_sleep_on_timeout(&adev->out_sleeper,
- timeout_value);
- if (timeout != MAX_SCHEDULE_TIMEOUT && !timeout_value) {
- printk(KERN_WARNING "Sound: DMA (output) timed out - IRQ/DRQ config error?\n");
- dma_reset_output(dev);
- } else {
- if (signal_pending(current))
- err = -EINTR;
- }
- return err;
-}
-/* called with the lock held */
-static int find_output_space(int dev, char **buf, int *size)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap = adev->dmap_out;
- unsigned long active_offs;
- long len, offs;
- int maxfrags;
- int occupied_bytes = (dmap->user_counter % dmap->fragment_size);
-
- *buf = dmap->raw_buf;
- if (!(maxfrags = DMAbuf_space_in_queue(dev)) && !occupied_bytes)
- return 0;
-
-#ifdef BE_CONSERVATIVE
- active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size;
-#else
- active_offs = max(DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT), 0);
- /* Check for pointer wrapping situation */
- if (active_offs >= dmap->bytes_in_use)
- active_offs = 0;
- active_offs += dmap->byte_counter;
-#endif
-
- offs = (dmap->user_counter % dmap->bytes_in_use) & ~SAMPLE_ROUNDUP;
- if (offs < 0 || offs >= dmap->bytes_in_use) {
- printk(KERN_ERR "Sound: Got unexpected offs %ld. Giving up.\n", offs);
- printk("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use);
- return 0;
- }
- *buf = dmap->raw_buf + offs;
-
- len = active_offs + dmap->bytes_in_use - dmap->user_counter; /* Number of unused bytes in buffer */
-
- if ((offs + len) > dmap->bytes_in_use)
- len = dmap->bytes_in_use - offs;
- if (len < 0) {
- return 0;
- }
- if (len > ((maxfrags * dmap->fragment_size) - occupied_bytes))
- len = (maxfrags * dmap->fragment_size) - occupied_bytes;
- *size = len & ~SAMPLE_ROUNDUP;
- return (*size > 0);
-}
-/* acquires lock */
-int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock)
-{
- struct audio_operations *adev = audio_devs[dev];
- unsigned long flags;
- int err = -EIO;
- struct dma_buffparms *dmap = adev->dmap_out;
-
- if (dmap->mapping_flags & DMA_MAP_MAPPED) {
-/* printk(KERN_DEBUG "Sound: Can't write to mmapped device (3)\n");*/
- return -EINVAL;
- }
- spin_lock_irqsave(&dmap->lock,flags);
- if (dmap->needs_reorg)
- reorganize_buffers(dev, dmap, 0);
-
- if (dmap->dma_mode == DMODE_INPUT) { /* Direction change */
- spin_unlock_irqrestore(&dmap->lock,flags);
- DMAbuf_reset(dev);
- spin_lock_irqsave(&dmap->lock,flags);
- }
- dmap->dma_mode = DMODE_OUTPUT;
-
- while (find_output_space(dev, buf, size) <= 0) {
- spin_unlock_irqrestore(&dmap->lock,flags);
- if ((err = output_sleep(dev, dontblock)) < 0) {
- return err;
- }
- spin_lock_irqsave(&dmap->lock,flags);
- }
-
- spin_unlock_irqrestore(&dmap->lock,flags);
- return 0;
-}
-/* has to acquire dmap->lock */
-int DMAbuf_move_wrpointer(int dev, int l)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap = adev->dmap_out;
- unsigned long ptr;
- unsigned long end_ptr, p;
- int post;
- unsigned long flags;
-
- spin_lock_irqsave(&dmap->lock,flags);
- post= (dmap->flags & DMA_POST);
- ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
-
- dmap->flags &= ~DMA_POST;
- dmap->cfrag = -1;
- dmap->user_counter += l;
- dmap->flags |= DMA_DIRTY;
-
- if (dmap->byte_counter >= dmap->max_byte_counter) {
- /* Wrap the byte counters */
- long decr = dmap->byte_counter;
- dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use);
- decr -= dmap->byte_counter;
- dmap->user_counter -= decr;
- }
- end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
-
- p = (dmap->user_counter - 1) % dmap->bytes_in_use;
- dmap->neutral_byte = dmap->raw_buf[p];
-
- /* Update the fragment based bookkeeping too */
- while (ptr < end_ptr) {
- dmap->counts[dmap->qtail] = dmap->fragment_size;
- dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
- dmap->qlen++;
- ptr += dmap->fragment_size;
- }
-
- dmap->counts[dmap->qtail] = dmap->user_counter - ptr;
-
- /*
- * Let the low level driver perform some postprocessing to
- * the written data.
- */
- if (adev->d->postprocess_write)
- adev->d->postprocess_write(dev);
-
- if (!(dmap->flags & DMA_ACTIVE))
- if (dmap->qlen > 1 || (dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1)))
- DMAbuf_launch_output(dev, dmap);
-
- spin_unlock_irqrestore(&dmap->lock,flags);
- return 0;
-}
-
-int DMAbuf_start_dma(int dev, unsigned long physaddr, int count, int dma_mode)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in;
-
- if (dmap->raw_buf == NULL) {
- printk(KERN_ERR "sound: DMA buffer(1) == NULL\n");
- printk("Device %d, chn=%s\n", dev, (dmap == adev->dmap_out) ? "out" : "in");
- return 0;
- }
- if (dmap->dma < 0)
- return 0;
- sound_start_dma(dmap, physaddr, count, dma_mode);
- return count;
-}
-EXPORT_SYMBOL(DMAbuf_start_dma);
-
-static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode)
-{
- struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in;
-
- if (dmap->raw_buf == NULL) {
- printk(KERN_ERR "sound: DMA buffer(2) == NULL\n");
- printk(KERN_ERR "Device %s, chn=%s\n", adev->name, (dmap == adev->dmap_out) ? "out" : "in");
- return 0;
- }
- if (dmap->flags & DMA_NODMA)
- return 1;
- if (dmap->dma < 0)
- return 0;
- sound_start_dma(dmap, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode | DMA_AUTOINIT);
- dmap->flags |= DMA_STARTED;
- return count;
-}
-
-static void finish_output_interrupt(int dev, struct dma_buffparms *dmap)
-{
- struct audio_operations *adev = audio_devs[dev];
-
- if (dmap->audio_callback != NULL)
- dmap->audio_callback(dev, dmap->callback_parm);
- wake_up(&adev->out_sleeper);
- wake_up(&adev->poll_sleeper);
-}
-/* called with dmap->lock held in irq context*/
-static void do_outputintr(int dev, int dummy)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap = adev->dmap_out;
- int this_fragment;
-
- if (dmap->raw_buf == NULL) {
- printk(KERN_ERR "Sound: Error. Audio interrupt (%d) after freeing buffers.\n", dev);
- return;
- }
- if (dmap->mapping_flags & DMA_MAP_MAPPED) { /* Virtual memory mapped access */
- /* mmapped access */
- dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
- if (dmap->qhead == 0) { /* Wrapped */
- dmap->byte_counter += dmap->bytes_in_use;
- if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */
- long decr = dmap->byte_counter;
- dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use);
- decr -= dmap->byte_counter;
- dmap->user_counter -= decr;
- }
- }
- dmap->qlen++; /* Yes increment it (don't decrement) */
- if (!(adev->flags & DMA_AUTOMODE))
- dmap->flags &= ~DMA_ACTIVE;
- dmap->counts[dmap->qhead] = dmap->fragment_size;
- DMAbuf_launch_output(dev, dmap);
- finish_output_interrupt(dev, dmap);
- return;
- }
-
- dmap->qlen--;
- this_fragment = dmap->qhead;
- dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
-
- if (dmap->qhead == 0) { /* Wrapped */
- dmap->byte_counter += dmap->bytes_in_use;
- if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */
- long decr = dmap->byte_counter;
- dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use);
- decr -= dmap->byte_counter;
- dmap->user_counter -= decr;
- }
- }
- if (!(adev->flags & DMA_AUTOMODE))
- dmap->flags &= ~DMA_ACTIVE;
-
- /*
- * This is dmap->qlen <= 0 except when closing when
- * dmap->qlen < 0
- */
-
- while (dmap->qlen <= -dmap->closing) {
- dmap->underrun_count++;
- dmap->qlen++;
- if ((dmap->flags & DMA_DIRTY) && dmap->applic_profile != APF_CPUINTENS) {
- dmap->flags &= ~DMA_DIRTY;
- memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte,
- adev->dmap_out->buffsize);
- }
- dmap->user_counter += dmap->fragment_size;
- dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
- }
- if (dmap->qlen > 0)
- DMAbuf_launch_output(dev, dmap);
- finish_output_interrupt(dev, dmap);
-}
-/* called in irq context */
-void DMAbuf_outputintr(int dev, int notify_only)
-{
- struct audio_operations *adev = audio_devs[dev];
- unsigned long flags;
- struct dma_buffparms *dmap = adev->dmap_out;
-
- spin_lock_irqsave(&dmap->lock,flags);
- if (!(dmap->flags & DMA_NODMA)) {
- int chan = dmap->dma, pos, n;
- unsigned long f;
-
- f=claim_dma_lock();
-
- if(!isa_dma_bridge_buggy)
- disable_dma(dmap->dma);
- clear_dma_ff(chan);
- pos = dmap->bytes_in_use - get_dma_residue(chan);
- if(!isa_dma_bridge_buggy)
- enable_dma(dmap->dma);
- release_dma_lock(f);
-
- pos = pos / dmap->fragment_size; /* Actual qhead */
- if (pos < 0 || pos >= dmap->nbufs)
- pos = 0;
- n = 0;
- while (dmap->qhead != pos && n++ < dmap->nbufs)
- do_outputintr(dev, notify_only);
- }
- else
- do_outputintr(dev, notify_only);
- spin_unlock_irqrestore(&dmap->lock,flags);
-}
-EXPORT_SYMBOL(DMAbuf_outputintr);
-
-/* called with dmap->lock held in irq context */
-static void do_inputintr(int dev)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap = adev->dmap_in;
-
- if (dmap->raw_buf == NULL) {
- printk(KERN_ERR "Sound: Fatal error. Audio interrupt after freeing buffers.\n");
- return;
- }
- if (dmap->mapping_flags & DMA_MAP_MAPPED) {
- dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
- if (dmap->qtail == 0) { /* Wrapped */
- dmap->byte_counter += dmap->bytes_in_use;
- if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */
- long decr = dmap->byte_counter;
- dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
- decr -= dmap->byte_counter;
- dmap->user_counter -= decr;
- }
- }
- dmap->qlen++;
-
- if (!(adev->flags & DMA_AUTOMODE)) {
- if (dmap->needs_reorg)
- reorganize_buffers(dev, dmap, 0);
- local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_READ);
- adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size,
- dmap->fragment_size, 1);
- if (adev->d->trigger)
- adev->d->trigger(dev, adev->enable_bits * adev->go);
- }
- dmap->flags |= DMA_ACTIVE;
- } else if (dmap->qlen >= (dmap->nbufs - 1)) {
- printk(KERN_WARNING "Sound: Recording overrun\n");
- dmap->underrun_count++;
-
- /* Just throw away the oldest fragment but keep the engine running */
- dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
- dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
- } else if (dmap->qlen >= 0 && dmap->qlen < dmap->nbufs) {
- dmap->qlen++;
- dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
- if (dmap->qtail == 0) { /* Wrapped */
- dmap->byte_counter += dmap->bytes_in_use;
- if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */
- long decr = dmap->byte_counter;
- dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
- decr -= dmap->byte_counter;
- dmap->user_counter -= decr;
- }
- }
- }
- if (!(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) {
- local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ);
- adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, dmap->fragment_size, 1);
- if (adev->d->trigger)
- adev->d->trigger(dev,adev->enable_bits * adev->go);
- }
- dmap->flags |= DMA_ACTIVE;
- if (dmap->qlen > 0)
- {
- wake_up(&adev->in_sleeper);
- wake_up(&adev->poll_sleeper);
- }
-}
-/* called in irq context */
-void DMAbuf_inputintr(int dev)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap = adev->dmap_in;
- unsigned long flags;
-
- spin_lock_irqsave(&dmap->lock,flags);
-
- if (!(dmap->flags & DMA_NODMA)) {
- int chan = dmap->dma, pos, n;
- unsigned long f;
-
- f=claim_dma_lock();
- if(!isa_dma_bridge_buggy)
- disable_dma(dmap->dma);
- clear_dma_ff(chan);
- pos = dmap->bytes_in_use - get_dma_residue(chan);
- if(!isa_dma_bridge_buggy)
- enable_dma(dmap->dma);
- release_dma_lock(f);
-
- pos = pos / dmap->fragment_size; /* Actual qhead */
- if (pos < 0 || pos >= dmap->nbufs)
- pos = 0;
-
- n = 0;
- while (dmap->qtail != pos && ++n < dmap->nbufs)
- do_inputintr(dev);
- } else
- do_inputintr(dev);
- spin_unlock_irqrestore(&dmap->lock,flags);
-}
-EXPORT_SYMBOL(DMAbuf_inputintr);
-
-void DMAbuf_init(int dev, int dma1, int dma2)
-{
- struct audio_operations *adev = audio_devs[dev];
- /*
- * NOTE! This routine could be called several times.
- */
-
- if (adev && adev->dmap_out == NULL) {
- if (adev->d == NULL)
- panic("OSS: audio_devs[%d]->d == NULL\n", dev);
-
- if (adev->parent_dev) { /* Use DMA map of the parent dev */
- int parent = adev->parent_dev - 1;
- adev->dmap_out = audio_devs[parent]->dmap_out;
- adev->dmap_in = audio_devs[parent]->dmap_in;
- } else {
- adev->dmap_out = adev->dmap_in = &adev->dmaps[0];
- adev->dmap_out->dma = dma1;
- if (adev->flags & DMA_DUPLEX) {
- adev->dmap_in = &adev->dmaps[1];
- adev->dmap_in->dma = dma2;
- }
- }
- /* Persistent DMA buffers allocated here */
- if (sound_dmap_flag == DMAP_KEEP_ON_CLOSE) {
- if (adev->dmap_in->raw_buf == NULL)
- sound_alloc_dmap(adev->dmap_in);
- if (adev->dmap_out->raw_buf == NULL)
- sound_alloc_dmap(adev->dmap_out);
- }
- }
-}
-
-/* No kernel lock - DMAbuf_activate_recording protected by global cli/sti */
-static unsigned int poll_input(struct file * file, int dev, poll_table *wait)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap = adev->dmap_in;
-
- if (!(adev->open_mode & OPEN_READ))
- return 0;
- if (dmap->mapping_flags & DMA_MAP_MAPPED) {
- if (dmap->qlen)
- return POLLIN | POLLRDNORM;
- return 0;
- }
- if (dmap->dma_mode != DMODE_INPUT) {
- if (dmap->dma_mode == DMODE_NONE &&
- adev->enable_bits & PCM_ENABLE_INPUT &&
- !dmap->qlen && adev->go) {
- unsigned long flags;
-
- spin_lock_irqsave(&dmap->lock,flags);
- DMAbuf_activate_recording(dev, dmap);
- spin_unlock_irqrestore(&dmap->lock,flags);
- }
- return 0;
- }
- if (!dmap->qlen)
- return 0;
- return POLLIN | POLLRDNORM;
-}
-
-static unsigned int poll_output(struct file * file, int dev, poll_table *wait)
-{
- struct audio_operations *adev = audio_devs[dev];
- struct dma_buffparms *dmap = adev->dmap_out;
-
- if (!(adev->open_mode & OPEN_WRITE))
- return 0;
- if (dmap->mapping_flags & DMA_MAP_MAPPED) {
- if (dmap->qlen)
- return POLLOUT | POLLWRNORM;
- return 0;
- }
- if (dmap->dma_mode == DMODE_INPUT)
- return 0;
- if (dmap->dma_mode == DMODE_NONE)
- return POLLOUT | POLLWRNORM;
- if (!DMAbuf_space_in_queue(dev))
- return 0;
- return POLLOUT | POLLWRNORM;
-}
-
-unsigned int DMAbuf_poll(struct file * file, int dev, poll_table *wait)
-{
- struct audio_operations *adev = audio_devs[dev];
- poll_wait(file, &adev->poll_sleeper, wait);
- return poll_input(file, dev, wait) | poll_output(file, dev, wait);
-}
-
-void DMAbuf_deinit(int dev)
-{
- struct audio_operations *adev = audio_devs[dev];
- /* This routine is called when driver is being unloaded */
- if (!adev)
- return;
-
- /* Persistent DMA buffers deallocated here */
- if (sound_dmap_flag == DMAP_KEEP_ON_CLOSE) {
- sound_free_dmap(adev->dmap_out);
- if (adev->flags & DMA_DUPLEX)
- sound_free_dmap(adev->dmap_in);
- }
-}
diff --git a/sound/oss/dmasound/Kconfig b/sound/oss/dmasound/Kconfig
index f456574a964d..1a3339859840 100644
--- a/sound/oss/dmasound/Kconfig
+++ b/sound/oss/dmasound/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
config DMASOUND_ATARI
tristate "Atari DMA sound support"
depends on ATARI && SOUND
@@ -10,7 +11,7 @@ config DMASOUND_ATARI
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you
want). If you want to compile it as a module, say M here and read
- <file:Documentation/kbuild/modules.txt>.
+ <file:Documentation/kbuild/modules.rst>.
config DMASOUND_PAULA
tristate "Amiga DMA sound support"
@@ -24,7 +25,7 @@ config DMASOUND_PAULA
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you
want). If you want to compile it as a module, say M here and read
- <file:Documentation/kbuild/modules.txt>.
+ <file:Documentation/kbuild/modules.rst>.
config DMASOUND_Q40
tristate "Q40 sound support"
@@ -38,7 +39,7 @@ config DMASOUND_Q40
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you
want). If you want to compile it as a module, say M here and read
- <file:Documentation/kbuild/modules.txt>.
+ <file:Documentation/kbuild/modules.rst>.
config DMASOUND
tristate
diff --git a/sound/oss/dmasound/Makefile b/sound/oss/dmasound/Makefile
index 3c1531652d11..de8ae8346a09 100644
--- a/sound/oss/dmasound/Makefile
+++ b/sound/oss/dmasound/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for the DMA sound driver
#
diff --git a/sound/oss/dmasound/dmasound.h b/sound/oss/dmasound/dmasound.h
index 1308d8d34186..f065840c0efb 100644
--- a/sound/oss/dmasound/dmasound.h
+++ b/sound/oss/dmasound/dmasound.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _dmasound_h_
/*
* linux/sound/oss/dmasound/dmasound.h
@@ -87,11 +88,7 @@ static inline int ioctl_return(int __user *addr, int value)
*/
extern int dmasound_init(void);
-#ifdef MODULE
extern void dmasound_deinit(void);
-#else
-#define dmasound_deinit() do { } while (0)
-#endif
/* description of the set-up applies to either hard or soft settings */
@@ -113,9 +110,7 @@ typedef struct {
void *(*dma_alloc)(unsigned int, gfp_t);
void (*dma_free)(void *, unsigned int);
int (*irqinit)(void);
-#ifdef MODULE
void (*irqcleanup)(void);
-#endif
void (*init)(void);
void (*silence)(void);
int (*setFormat)(int);
@@ -239,7 +234,6 @@ struct sound_queue {
int busy, syncing, xruns, died;
};
-#define SLEEP(queue) interruptible_sleep_on_timeout(&queue, HZ)
#define WAKE_UP(queue) (wake_up_interruptible(&queue))
extern struct sound_queue dmasound_write_sq;
@@ -256,7 +250,4 @@ extern int dmasound_catchRadius;
#define SW_INPUT_VOLUME_SCALE 4
#define SW_INPUT_VOLUME_DEFAULT (128 / SW_INPUT_VOLUME_SCALE)
-extern int expand_read_bal; /* Balance factor for reading */
-extern uint software_input_volume; /* software implemented recording volume! */
-
#endif /* _dmasound_h_ */
diff --git a/sound/oss/dmasound/dmasound_atari.c b/sound/oss/dmasound/dmasound_atari.c
index 13c214466d3b..6188469de8af 100644
--- a/sound/oss/dmasound/dmasound_atari.c
+++ b/sound/oss/dmasound/dmasound_atari.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/sound/oss/dmasound/dmasound_atari.c
*
@@ -22,7 +23,7 @@
#include <linux/spinlock.h>
#include <linux/interrupt.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/atariints.h>
#include <asm/atari_stram.h>
@@ -851,7 +852,7 @@ static int __init AtaIrqInit(void)
st_mfp.tim_dt_a = 1; /* Cause interrupt after first event. */
st_mfp.tim_ct_a = 8; /* Turn on event counting. */
/* Register interrupt handler. */
- if (request_irq(IRQ_MFP_TIMA, AtaInterrupt, IRQ_TYPE_SLOW, "DMA sound",
+ if (request_irq(IRQ_MFP_TIMA, AtaInterrupt, 0, "DMA sound",
AtaInterrupt))
return 0;
st_mfp.int_en_a |= 0x20; /* Turn interrupt on. */
@@ -1431,25 +1432,25 @@ static int FalconMixerIoctl(u_int cmd, u_long arg)
{
int data;
switch (cmd) {
- case SOUND_MIXER_READ_RECMASK:
+ case SOUND_MIXER_READ_RECMASK:
return IOCTL_OUT(arg, SOUND_MASK_MIC);
- case SOUND_MIXER_READ_DEVMASK:
+ case SOUND_MIXER_READ_DEVMASK:
return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC | SOUND_MASK_SPEAKER);
- case SOUND_MIXER_READ_STEREODEVS:
+ case SOUND_MIXER_READ_STEREODEVS:
return IOCTL_OUT(arg, SOUND_MASK_VOLUME | SOUND_MASK_MIC);
- case SOUND_MIXER_READ_VOLUME:
+ case SOUND_MIXER_READ_VOLUME:
return IOCTL_OUT(arg,
VOLUME_ATT_TO_VOXWARE(dmasound.volume_left) |
VOLUME_ATT_TO_VOXWARE(dmasound.volume_right) << 8);
- case SOUND_MIXER_READ_CAPS:
+ case SOUND_MIXER_READ_CAPS:
return IOCTL_OUT(arg, SOUND_CAP_EXCL_INPUT);
- case SOUND_MIXER_WRITE_MIC:
+ case SOUND_MIXER_WRITE_MIC:
IOCTL_IN(arg, data);
tt_dmasnd.input_gain =
RECLEVEL_VOXWARE_TO_GAIN(data & 0xff) << 4 |
RECLEVEL_VOXWARE_TO_GAIN(data >> 8 & 0xff);
- /* fall thru, return set value */
- case SOUND_MIXER_READ_MIC:
+ fallthrough; /* return set value */
+ case SOUND_MIXER_READ_MIC:
return IOCTL_OUT(arg,
RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain >> 4 & 0xf) |
RECLEVEL_GAIN_TO_VOXWARE(tt_dmasnd.input_gain & 0xf) << 8);
@@ -1617,4 +1618,6 @@ static void __exit dmasound_atari_cleanup(void)
module_init(dmasound_atari_init);
module_exit(dmasound_atari_cleanup);
+
+MODULE_DESCRIPTION("Atari TT and Falcon DMA Sound Driver");
MODULE_LICENSE("GPL");
diff --git a/sound/oss/dmasound/dmasound_core.c b/sound/oss/dmasound/dmasound_core.c
index 87e2c72651f5..dea2d9b18fc9 100644
--- a/sound/oss/dmasound/dmasound_core.c
+++ b/sound/oss/dmasound/dmasound_core.c
@@ -182,8 +182,9 @@
#include <linux/soundcard.h>
#include <linux/poll.h>
#include <linux/mutex.h>
+#include <linux/sched/signal.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "dmasound.h"
@@ -203,14 +204,13 @@ module_param(numWriteBufs, int, 0);
static unsigned int writeBufSize = DEFAULT_BUFF_SIZE ; /* in bytes */
module_param(writeBufSize, int, 0);
+MODULE_DESCRIPTION("Atari/Amiga/Q40 core DMA sound driver");
MODULE_LICENSE("GPL");
-#ifdef MODULE
static int sq_unit = -1;
static int mixer_unit = -1;
static int state_unit = -1;
static int irq_installed;
-#endif /* MODULE */
/* control over who can modify resources shared between play/record */
static fmode_t shared_resource_owner;
@@ -354,8 +354,8 @@ static int mixer_ioctl(struct file *file, u_int cmd, u_long arg)
{
mixer_info info;
memset(&info, 0, sizeof(info));
- strlcpy(info.id, dmasound.mach.name2, sizeof(info.id));
- strlcpy(info.name, dmasound.mach.name2, sizeof(info.name));
+ strscpy(info.id, dmasound.mach.name2, sizeof(info.id));
+ strscpy(info.name, dmasound.mach.name2, sizeof(info.name));
info.modify_counter = mixer.modify_counter;
if (copy_to_user((void __user *)arg, &info, sizeof(info)))
return -EFAULT;
@@ -381,17 +381,14 @@ static long mixer_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
static const struct file_operations mixer_fops =
{
.owner = THIS_MODULE,
- .llseek = no_llseek,
.unlocked_ioctl = mixer_unlocked_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
.open = mixer_open,
.release = mixer_release,
};
static void mixer_init(void)
{
-#ifndef MODULE
- int mixer_unit;
-#endif
mixer_unit = register_sound_mixer(&mixer_fops, -1);
if (mixer_unit < 0)
return;
@@ -419,7 +416,7 @@ static int sq_allocate_buffers(struct sound_queue *sq, int num, int size)
return 0;
sq->numBufs = num;
sq->bufSize = size;
- sq->buffers = kmalloc (num * sizeof(char *), GFP_KERNEL);
+ sq->buffers = kmalloc_array (num, sizeof(char *), GFP_KERNEL);
if (!sq->buffers)
return -ENOMEM;
for (i = 0; i < num; i++) {
@@ -619,15 +616,27 @@ static ssize_t sq_write(struct file *file, const char __user *src, size_t uLeft,
}
while (uLeft) {
+ DEFINE_WAIT(wait);
+
while (write_sq.count >= write_sq.max_active) {
+ prepare_to_wait(&write_sq.action_queue, &wait, TASK_INTERRUPTIBLE);
sq_play();
- if (write_sq.non_blocking)
+ if (write_sq.non_blocking) {
+ finish_wait(&write_sq.action_queue, &wait);
return uWritten > 0 ? uWritten : -EAGAIN;
- SLEEP(write_sq.action_queue);
- if (signal_pending(current))
+ }
+ if (write_sq.count < write_sq.max_active)
+ break;
+
+ schedule_timeout(HZ);
+ if (signal_pending(current)) {
+ finish_wait(&write_sq.action_queue, &wait);
return uWritten > 0 ? uWritten : -EINTR;
+ }
}
+ finish_wait(&write_sq.action_queue, &wait);
+
/* Here, we can avoid disabling the interrupt by first
* copying and translating the data, and then updating
* the write_sq variables. Until this is done, the interrupt
@@ -657,9 +666,9 @@ static ssize_t sq_write(struct file *file, const char __user *src, size_t uLeft,
return uUsed < 0? uUsed: uWritten;
}
-static unsigned int sq_poll(struct file *file, struct poll_table_struct *wait)
+static __poll_t sq_poll(struct file *file, struct poll_table_struct *wait)
{
- unsigned int mask = 0;
+ __poll_t mask = 0;
int retVal;
if (write_sq.locked == 0) {
@@ -671,7 +680,7 @@ static unsigned int sq_poll(struct file *file, struct poll_table_struct *wait)
poll_wait(file, &write_sq.action_queue, wait);
if (file->f_mode & FMODE_WRITE)
if (write_sq.count < write_sq.max_active || write_sq.block_size - write_sq.rear_size > 0)
- mask |= POLLOUT | POLLWRNORM;
+ mask |= EPOLLOUT | EPOLLWRNORM;
return mask;
}
@@ -707,11 +716,8 @@ static int sq_open2(struct sound_queue *sq, struct file *file, fmode_t mode,
if (file->f_flags & O_NONBLOCK)
return rc;
rc = -EINTR;
- while (sq->busy) {
- SLEEP(sq->open_queue);
- if (signal_pending(current))
- return rc;
- }
+ if (wait_event_interruptible(sq->open_queue, !sq->busy))
+ return rc;
rc = 0;
#else
/* OSS manual says we will return EBUSY regardless
@@ -835,7 +841,7 @@ static void sq_reset(void)
shared_resources_initialised = 0 ;
}
-static int sq_fsync(struct file *filp, struct dentry *dentry)
+static int sq_fsync(void)
{
int rc = 0;
int timeout = 5;
@@ -844,7 +850,8 @@ static int sq_fsync(struct file *filp, struct dentry *dentry)
sq_play(); /* there may be an incomplete frame waiting */
while (write_sq.active) {
- SLEEP(write_sq.sync_queue);
+ wait_event_interruptible_timeout(write_sq.sync_queue,
+ !write_sq.active, HZ);
if (signal_pending(current)) {
/* While waiting for audio output to drain, an
* interrupt occurred. Stop audio output immediately
@@ -874,7 +881,7 @@ static int sq_release(struct inode *inode, struct file *file)
if (file->f_mode & FMODE_WRITE) {
if (write_sq.busy)
- rc = sq_fsync(file, file->f_path.dentry);
+ rc = sq_fsync();
sq_reset_output() ; /* make sure dma is stopped and all is quiet */
write_sq_release_buffers();
@@ -987,11 +994,9 @@ static int sq_ioctl(struct file *file, u_int cmd, u_long arg)
case SNDCTL_DSP_RESET:
sq_reset();
return 0;
- break ;
case SNDCTL_DSP_GETFMTS:
fmt = dmasound.mach.hardware_afmts ; /* this is what OSS says.. */
return IOCTL_OUT(arg, fmt);
- break ;
case SNDCTL_DSP_GETBLKSIZE:
/* this should tell the caller about bytes that the app can
read/write - the app doesn't care about our internal buffers.
@@ -1008,7 +1013,6 @@ static int sq_ioctl(struct file *file, u_int cmd, u_long arg)
size = write_sq.user_frag_size ;
}
return IOCTL_OUT(arg, size);
- break ;
case SNDCTL_DSP_POST:
/* all we are going to do is to tell the LL that any
partial frags can be queued for output.
@@ -1021,18 +1025,17 @@ static int sq_ioctl(struct file *file, u_int cmd, u_long arg)
case SNDCTL_DSP_SYNC:
/* This call, effectively, has the same behaviour as SNDCTL_DSP_RESET
except that it waits for output to finish before resetting
- everything - read, however, is killed imediately.
+ everything - read, however, is killed immediately.
*/
result = 0 ;
if (file->f_mode & FMODE_WRITE) {
- result = sq_fsync(file, file->f_path.dentry);
+ result = sq_fsync();
sq_reset_output() ;
}
/* if we are the shared resource owner then release them */
if (file->f_mode & shared_resource_owner)
shared_resources_initialised = 0 ;
return result ;
- break ;
case SOUND_PCM_READ_RATE:
return IOCTL_OUT(arg, dmasound.soft.speed);
case SNDCTL_DSP_SPEED:
@@ -1111,7 +1114,6 @@ static int sq_ioctl(struct file *file, u_int cmd, u_long arg)
the value is 'random' and that the user _must_ check the actual
frags values using SNDCTL_DSP_GETBLKSIZE or similar */
return IOCTL_OUT(arg, data);
- break ;
case SNDCTL_DSP_GETOSPACE:
/*
*/
@@ -1152,10 +1154,10 @@ static long sq_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
static const struct file_operations sq_fops =
{
.owner = THIS_MODULE,
- .llseek = no_llseek,
.write = sq_write,
.poll = sq_poll,
.unlocked_ioctl = sq_unlocked_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
.open = sq_open,
.release = sq_release,
};
@@ -1163,9 +1165,6 @@ static const struct file_operations sq_fops =
static int sq_init(void)
{
const struct file_operations *fops = &sq_fops;
-#ifndef MODULE
- int sq_unit;
-#endif
sq_unit = register_sound_dsp(fops, -1);
if (sq_unit < 0) {
@@ -1221,31 +1220,22 @@ static char *get_afmt_string(int afmt)
switch(afmt) {
case AFMT_MU_LAW:
return "mu-law";
- break;
case AFMT_A_LAW:
return "A-law";
- break;
case AFMT_U8:
return "unsigned 8 bit";
- break;
case AFMT_S8:
return "signed 8 bit";
- break;
case AFMT_S16_BE:
return "signed 16 bit BE";
- break;
case AFMT_U16_BE:
return "unsigned 16 bit BE";
- break;
case AFMT_S16_LE:
return "signed 16 bit LE";
- break;
case AFMT_U16_LE:
return "unsigned 16 bit LE";
- break;
case 0:
return "format not set" ;
- break ;
default:
break ;
}
@@ -1359,7 +1349,6 @@ static ssize_t state_read(struct file *file, char __user *buf, size_t count,
static const struct file_operations state_fops = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
.read = state_read,
.open = state_open,
.release = state_release,
@@ -1367,9 +1356,6 @@ static const struct file_operations state_fops = {
static int state_init(void)
{
-#ifndef MODULE
- int state_unit;
-#endif
state_unit = register_sound_special(&state_fops, SND_DEV_STATUS);
if (state_unit < 0)
return state_unit ;
@@ -1387,10 +1373,9 @@ static int state_init(void)
int dmasound_init(void)
{
int res ;
-#ifdef MODULE
+
if (irq_installed)
return -EBUSY;
-#endif
/* Set up sound queue, /dev/audio and /dev/dsp. */
@@ -1409,9 +1394,7 @@ int dmasound_init(void)
printk(KERN_ERR "DMA sound driver: Interrupt initialization failed\n");
return -ENODEV;
}
-#ifdef MODULE
irq_installed = 1;
-#endif
printk(KERN_INFO "%s DMA sound driver rev %03d installed\n",
dmasound.mach.name, (DMASOUND_CORE_REVISION<<4) +
@@ -1425,8 +1408,6 @@ int dmasound_init(void)
return 0;
}
-#ifdef MODULE
-
void dmasound_deinit(void)
{
if (irq_installed) {
@@ -1445,9 +1426,7 @@ void dmasound_deinit(void)
unregister_sound_dsp(sq_unit);
}
-#else /* !MODULE */
-
-static int dmasound_setup(char *str)
+static int __maybe_unused dmasound_setup(char *str)
{
int ints[6], size;
@@ -1465,13 +1444,13 @@ static int dmasound_setup(char *str)
printk("dmasound_setup: invalid catch radius, using default = %d\n", catchRadius);
else
catchRadius = ints[3];
- /* fall through */
+ fallthrough;
case 2:
if (ints[1] < MIN_BUFFERS)
printk("dmasound_setup: invalid number of buffers, using default = %d\n", numWriteBufs);
else
numWriteBufs = ints[1];
- /* fall through */
+ fallthrough;
case 1:
if ((size = ints[2]) < 256) /* check for small buffer specs */
size <<= 10 ;
@@ -1490,8 +1469,6 @@ static int dmasound_setup(char *str)
__setup("dmasound=", dmasound_setup);
-#endif /* !MODULE */
-
/*
* Conversion tables
*/
@@ -1578,9 +1555,7 @@ char dmasound_alaw2dma8[] = {
EXPORT_SYMBOL(dmasound);
EXPORT_SYMBOL(dmasound_init);
-#ifdef MODULE
EXPORT_SYMBOL(dmasound_deinit);
-#endif
EXPORT_SYMBOL(dmasound_write_sq);
EXPORT_SYMBOL(dmasound_catchRadius);
#ifdef HAS_8BIT_TABLES
diff --git a/sound/oss/dmasound/dmasound_paula.c b/sound/oss/dmasound/dmasound_paula.c
index 87910e992133..8d443a3663d3 100644
--- a/sound/oss/dmasound/dmasound_paula.c
+++ b/sound/oss/dmasound/dmasound_paula.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/sound/oss/dmasound/dmasound_paula.c
*
@@ -23,7 +24,7 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/setup.h>
#include <asm/amigahw.h>
#include <asm/amigaints.h>
@@ -719,33 +720,26 @@ static int __init amiga_audio_probe(struct platform_device *pdev)
return dmasound_init();
}
-static int __exit amiga_audio_remove(struct platform_device *pdev)
+static void __exit amiga_audio_remove(struct platform_device *pdev)
{
dmasound_deinit();
- return 0;
}
-static struct platform_driver amiga_audio_driver = {
+/*
+ * amiga_audio_remove() lives in .exit.text. For drivers registered via
+ * module_platform_driver_probe() this is ok because they cannot get unbound at
+ * runtime. So mark the driver struct with __refdata to prevent modpost
+ * triggering a section mismatch warning.
+ */
+static struct platform_driver amiga_audio_driver __refdata = {
.remove = __exit_p(amiga_audio_remove),
- .driver = {
+ .driver = {
.name = "amiga-audio",
- .owner = THIS_MODULE,
},
};
-static int __init amiga_audio_init(void)
-{
- return platform_driver_probe(&amiga_audio_driver, amiga_audio_probe);
-}
-
-module_init(amiga_audio_init);
-
-static void __exit amiga_audio_exit(void)
-{
- platform_driver_unregister(&amiga_audio_driver);
-}
-
-module_exit(amiga_audio_exit);
+module_platform_driver_probe(amiga_audio_driver, amiga_audio_probe);
+MODULE_DESCRIPTION("Amiga Paula DMA Sound Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:amiga-audio");
diff --git a/sound/oss/dmasound/dmasound_q40.c b/sound/oss/dmasound/dmasound_q40.c
index 99bcb21c2281..e25a78dd1bf2 100644
--- a/sound/oss/dmasound/dmasound_q40.c
+++ b/sound/oss/dmasound/dmasound_q40.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/sound/oss/dmasound/dmasound_q40.c
*
@@ -20,7 +21,7 @@
#include <linux/soundcard.h>
#include <linux/interrupt.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <asm/q40ints.h>
#include <asm/q40_master.h>
diff --git a/sound/oss/hex2hex.c b/sound/oss/hex2hex.c
deleted file mode 100644
index 041ef5c52bc2..000000000000
--- a/sound/oss/hex2hex.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * hex2hex reads stdin in Intel HEX format and produces an
- * (unsigned char) array which contains the bytes and writes it
- * to stdout using C syntax
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#define ABANDON(why) { fprintf(stderr, "%s\n", why); exit(1); }
-#define MAX_SIZE (256*1024)
-unsigned char buf[MAX_SIZE];
-
-static int loadhex(FILE *inf, unsigned char *buf)
-{
- int l=0, c, i;
-
- while ((c=getc(inf))!=EOF)
- {
- if (c == ':') /* Sync with beginning of line */
- {
- int n, check;
- unsigned char sum;
- int addr;
- int linetype;
-
- if (fscanf(inf, "%02x", &n) != 1)
- ABANDON("File format error");
- sum = n;
-
- if (fscanf(inf, "%04x", &addr) != 1)
- ABANDON("File format error");
- sum += addr/256;
- sum += addr%256;
-
- if (fscanf(inf, "%02x", &linetype) != 1)
- ABANDON("File format error");
- sum += linetype;
-
- if (linetype != 0)
- continue;
-
- for (i=0;i<n;i++)
- {
- if (fscanf(inf, "%02x", &c) != 1)
- ABANDON("File format error");
- if (addr >= MAX_SIZE)
- ABANDON("File too large");
- buf[addr++] = c;
- if (addr > l)
- l = addr;
- sum += c;
- }
-
- if (fscanf(inf, "%02x", &check) != 1)
- ABANDON("File format error");
-
- sum = ~sum + 1;
- if (check != sum)
- ABANDON("Line checksum error");
- }
- }
-
- return l;
-}
-
-int main( int argc, const char * argv [] )
-{
- const char * varline;
- int i,l;
- int id=0;
-
- if(argv[1] && strcmp(argv[1], "-i")==0)
- {
- argv++;
- argc--;
- id=1;
- }
- if(argv[1]==NULL)
- {
- fprintf(stderr,"hex2hex: [-i] filename\n");
- exit(1);
- }
- varline = argv[1];
- l = loadhex(stdin, buf);
-
- printf("/*\n *\t Computer generated file. Do not edit.\n */\n");
- printf("static int %s_len = %d;\n", varline, l);
- printf("static unsigned char %s[] %s = {\n", varline, id?"__initdata":"");
-
- for (i=0;i<l;i++)
- {
- if (i) printf(",");
- if (i && !(i % 16)) printf("\n");
- printf("0x%02x", buf[i]);
- }
-
- printf("\n};\n\n");
- return 0;
-}
diff --git a/sound/oss/kahlua.c b/sound/oss/kahlua.c
deleted file mode 100644
index 52d06a334e8f..000000000000
--- a/sound/oss/kahlua.c
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Initialisation code for Cyrix/NatSemi VSA1 softaudio
- *
- * (C) Copyright 2003 Red Hat Inc <alan@lxorguk.ukuu.org.uk>
- *
- * XpressAudio(tm) is used on the Cyrix MediaGX (now NatSemi Geode) systems.
- * The older version (VSA1) provides fairly good soundblaster emulation
- * although there are a couple of bugs: large DMA buffers break record,
- * and the MPU event handling seems suspect. VSA2 allows the native driver
- * to control the AC97 audio engine directly and requires a different driver.
- *
- * Thanks to National Semiconductor for providing the needed information
- * on the XpressAudio(tm) internals.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * TO DO:
- * Investigate whether we can portably support Cognac (5520) in the
- * same manner.
- */
-
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-
-#include "sound_config.h"
-
-#include "sb.h"
-
-/*
- * Read a soundblaster compatible mixer register.
- * In this case we are actually reading an SMI trap
- * not real hardware.
- */
-
-static u8 __devinit mixer_read(unsigned long io, u8 reg)
-{
- outb(reg, io + 4);
- udelay(20);
- reg = inb(io + 5);
- udelay(20);
- return reg;
-}
-
-static int __devinit probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)
-{
- struct address_info *hw_config;
- unsigned long base;
- void __iomem *mem;
- unsigned long io;
- u16 map;
- u8 irq, dma8, dma16;
- int oldquiet;
- extern int sb_be_quiet;
-
- base = pci_resource_start(pdev, 0);
- if(base == 0UL)
- return 1;
-
- mem = ioremap(base, 128);
- if (!mem)
- return 1;
- map = readw(mem + 0x18); /* Read the SMI enables */
- iounmap(mem);
-
- /* Map bits
- 0:1 * 0x20 + 0x200 = sb base
- 2 sb enable
- 3 adlib enable
- 5 MPU enable 0x330
- 6 MPU enable 0x300
-
- The other bits may be used internally so must be masked */
-
- io = 0x220 + 0x20 * (map & 3);
-
- if(map & (1<<2))
- printk(KERN_INFO "kahlua: XpressAudio at 0x%lx\n", io);
- else
- return 1;
-
- if(map & (1<<5))
- printk(KERN_INFO "kahlua: MPU at 0x300\n");
- else if(map & (1<<6))
- printk(KERN_INFO "kahlua: MPU at 0x330\n");
-
- irq = mixer_read(io, 0x80) & 0x0F;
- dma8 = mixer_read(io, 0x81);
-
- // printk("IRQ=%x MAP=%x DMA=%x\n", irq, map, dma8);
-
- if(dma8 & 0x20)
- dma16 = 5;
- else if(dma8 & 0x40)
- dma16 = 6;
- else if(dma8 & 0x80)
- dma16 = 7;
- else
- {
- printk(KERN_ERR "kahlua: No 16bit DMA enabled.\n");
- return 1;
- }
-
- if(dma8 & 0x01)
- dma8 = 0;
- else if(dma8 & 0x02)
- dma8 = 1;
- else if(dma8 & 0x08)
- dma8 = 3;
- else
- {
- printk(KERN_ERR "kahlua: No 8bit DMA enabled.\n");
- return 1;
- }
-
- if(irq & 1)
- irq = 9;
- else if(irq & 2)
- irq = 5;
- else if(irq & 4)
- irq = 7;
- else if(irq & 8)
- irq = 10;
- else
- {
- printk(KERN_ERR "kahlua: SB IRQ not set.\n");
- return 1;
- }
-
- printk(KERN_INFO "kahlua: XpressAudio on IRQ %d, DMA %d, %d\n",
- irq, dma8, dma16);
-
- hw_config = kzalloc(sizeof(struct address_info), GFP_KERNEL);
- if(hw_config == NULL)
- {
- printk(KERN_ERR "kahlua: out of memory.\n");
- return 1;
- }
-
- pci_set_drvdata(pdev, hw_config);
-
- hw_config->io_base = io;
- hw_config->irq = irq;
- hw_config->dma = dma8;
- hw_config->dma2 = dma16;
- hw_config->name = "Cyrix XpressAudio";
- hw_config->driver_use_1 = SB_NO_MIDI | SB_PCI_IRQ;
-
- if (!request_region(io, 16, "soundblaster"))
- goto err_out_free;
-
- if(sb_dsp_detect(hw_config, 0, 0, NULL)==0)
- {
- printk(KERN_ERR "kahlua: audio not responding.\n");
- release_region(io, 16);
- goto err_out_free;
- }
-
- oldquiet = sb_be_quiet;
- sb_be_quiet = 1;
- if(sb_dsp_init(hw_config, THIS_MODULE))
- {
- sb_be_quiet = oldquiet;
- goto err_out_free;
- }
- sb_be_quiet = oldquiet;
-
- return 0;
-
-err_out_free:
- pci_set_drvdata(pdev, NULL);
- kfree(hw_config);
- return 1;
-}
-
-static void __devexit remove_one(struct pci_dev *pdev)
-{
- struct address_info *hw_config = pci_get_drvdata(pdev);
- sb_dsp_unload(hw_config, 0);
- pci_set_drvdata(pdev, NULL);
- kfree(hw_config);
-}
-
-MODULE_AUTHOR("Alan Cox");
-MODULE_DESCRIPTION("Kahlua VSA1 PCI Audio");
-MODULE_LICENSE("GPL");
-
-/*
- * 5530 only. The 5510/5520 decode is different.
- */
-
-static DEFINE_PCI_DEVICE_TABLE(id_tbl) = {
- { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO), 0 },
- { }
-};
-
-MODULE_DEVICE_TABLE(pci, id_tbl);
-
-static struct pci_driver kahlua_driver = {
- .name = "kahlua",
- .id_table = id_tbl,
- .probe = probe_one,
- .remove = __devexit_p(remove_one),
-};
-
-
-static int __init kahlua_init_module(void)
-{
- printk(KERN_INFO "Cyrix Kahlua VSA1 XpressAudio support (c) Copyright 2003 Red Hat Inc\n");
- return pci_register_driver(&kahlua_driver);
-}
-
-static void __devexit kahlua_cleanup_module(void)
-{
- pci_unregister_driver(&kahlua_driver);
-}
-
-
-module_init(kahlua_init_module);
-module_exit(kahlua_cleanup_module);
-
diff --git a/sound/oss/midi_ctrl.h b/sound/oss/midi_ctrl.h
deleted file mode 100644
index 3353e5a67c24..000000000000
--- a/sound/oss/midi_ctrl.h
+++ /dev/null
@@ -1,22 +0,0 @@
-static unsigned char ctrl_def_values[128] =
-{
- 0x40,0x00,0x40,0x40, 0x40,0x40,0x40,0x7f, /* 0 to 7 */
- 0x40,0x40,0x40,0x7f, 0x40,0x40,0x40,0x40, /* 8 to 15 */
- 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 16 to 23 */
- 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 24 to 31 */
-
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 32 to 39 */
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 40 to 47 */
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 48 to 55 */
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 56 to 63 */
-
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 64 to 71 */
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 72 to 79 */
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 80 to 87 */
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 88 to 95 */
-
- 0x00,0x00,0x7f,0x7f, 0x7f,0x7f,0x00,0x00, /* 96 to 103 */
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 104 to 111 */
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 112 to 119 */
- 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 120 to 127 */
-};
diff --git a/sound/oss/midi_synth.c b/sound/oss/midi_synth.c
deleted file mode 100644
index 3c09374ea5bf..000000000000
--- a/sound/oss/midi_synth.c
+++ /dev/null
@@ -1,716 +0,0 @@
-/*
- * sound/oss/midi_synth.c
- *
- * High level midi sequencer manager for dumb MIDI interfaces.
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-/*
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * Andrew Veliath : fixed running status in MIDI input state machine
- */
-#define USE_SEQ_MACROS
-#define USE_SIMPLE_MACROS
-
-#include "sound_config.h"
-
-#define _MIDI_SYNTH_C_
-
-#include "midi_synth.h"
-
-static int midi2synth[MAX_MIDI_DEV];
-static int sysex_state[MAX_MIDI_DEV] =
-{0};
-static unsigned char prev_out_status[MAX_MIDI_DEV];
-
-#define STORE(cmd) \
-{ \
- int len; \
- unsigned char obuf[8]; \
- cmd; \
- seq_input_event(obuf, len); \
-}
-
-#define _seqbuf obuf
-#define _seqbufptr 0
-#define _SEQ_ADVBUF(x) len=x
-
-void
-do_midi_msg(int synthno, unsigned char *msg, int mlen)
-{
- switch (msg[0] & 0xf0)
- {
- case 0x90:
- if (msg[2] != 0)
- {
- STORE(SEQ_START_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2]));
- break;
- }
- msg[2] = 64;
-
- case 0x80:
- STORE(SEQ_STOP_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2]));
- break;
-
- case 0xA0:
- STORE(SEQ_KEY_PRESSURE(synthno, msg[0] & 0x0f, msg[1], msg[2]));
- break;
-
- case 0xB0:
- STORE(SEQ_CONTROL(synthno, msg[0] & 0x0f,
- msg[1], msg[2]));
- break;
-
- case 0xC0:
- STORE(SEQ_SET_PATCH(synthno, msg[0] & 0x0f, msg[1]));
- break;
-
- case 0xD0:
- STORE(SEQ_CHN_PRESSURE(synthno, msg[0] & 0x0f, msg[1]));
- break;
-
- case 0xE0:
- STORE(SEQ_BENDER(synthno, msg[0] & 0x0f,
- (msg[1] & 0x7f) | ((msg[2] & 0x7f) << 7)));
- break;
-
- default:
- /* printk( "MPU: Unknown midi channel message %02x\n", msg[0]); */
- ;
- }
-}
-EXPORT_SYMBOL(do_midi_msg);
-
-static void
-midi_outc(int midi_dev, int data)
-{
- int timeout;
-
- for (timeout = 0; timeout < 3200; timeout++)
- if (midi_devs[midi_dev]->outputc(midi_dev, (unsigned char) (data & 0xff)))
- {
- if (data & 0x80) /*
- * Status byte
- */
- prev_out_status[midi_dev] =
- (unsigned char) (data & 0xff); /*
- * Store for running status
- */
- return; /*
- * Mission complete
- */
- }
- /*
- * Sorry! No space on buffers.
- */
- printk("Midi send timed out\n");
-}
-
-static int
-prefix_cmd(int midi_dev, unsigned char status)
-{
- if ((char *) midi_devs[midi_dev]->prefix_cmd == NULL)
- return 1;
-
- return midi_devs[midi_dev]->prefix_cmd(midi_dev, status);
-}
-
-static void
-midi_synth_input(int orig_dev, unsigned char data)
-{
- int dev;
- struct midi_input_info *inc;
-
- static unsigned char len_tab[] = /* # of data bytes following a status
- */
- {
- 2, /* 8x */
- 2, /* 9x */
- 2, /* Ax */
- 2, /* Bx */
- 1, /* Cx */
- 1, /* Dx */
- 2, /* Ex */
- 0 /* Fx */
- };
-
- if (orig_dev < 0 || orig_dev > num_midis || midi_devs[orig_dev] == NULL)
- return;
-
- if (data == 0xfe) /* Ignore active sensing */
- return;
-
- dev = midi2synth[orig_dev];
- inc = &midi_devs[orig_dev]->in_info;
-
- switch (inc->m_state)
- {
- case MST_INIT:
- if (data & 0x80) /* MIDI status byte */
- {
- if ((data & 0xf0) == 0xf0) /* Common message */
- {
- switch (data)
- {
- case 0xf0: /* Sysex */
- inc->m_state = MST_SYSEX;
- break; /* Sysex */
-
- case 0xf1: /* MTC quarter frame */
- case 0xf3: /* Song select */
- inc->m_state = MST_DATA;
- inc->m_ptr = 1;
- inc->m_left = 1;
- inc->m_buf[0] = data;
- break;
-
- case 0xf2: /* Song position pointer */
- inc->m_state = MST_DATA;
- inc->m_ptr = 1;
- inc->m_left = 2;
- inc->m_buf[0] = data;
- break;
-
- default:
- inc->m_buf[0] = data;
- inc->m_ptr = 1;
- do_midi_msg(dev, inc->m_buf, inc->m_ptr);
- inc->m_ptr = 0;
- inc->m_left = 0;
- }
- } else
- {
- inc->m_state = MST_DATA;
- inc->m_ptr = 1;
- inc->m_left = len_tab[(data >> 4) - 8];
- inc->m_buf[0] = inc->m_prev_status = data;
- }
- } else if (inc->m_prev_status & 0x80) {
- /* Data byte (use running status) */
- inc->m_ptr = 2;
- inc->m_buf[1] = data;
- inc->m_buf[0] = inc->m_prev_status;
- inc->m_left = len_tab[(inc->m_buf[0] >> 4) - 8] - 1;
- if (inc->m_left > 0)
- inc->m_state = MST_DATA; /* Not done yet */
- else {
- inc->m_state = MST_INIT;
- do_midi_msg(dev, inc->m_buf, inc->m_ptr);
- inc->m_ptr = 0;
- }
- }
- break; /* MST_INIT */
-
- case MST_DATA:
- inc->m_buf[inc->m_ptr++] = data;
- if (--inc->m_left <= 0)
- {
- inc->m_state = MST_INIT;
- do_midi_msg(dev, inc->m_buf, inc->m_ptr);
- inc->m_ptr = 0;
- }
- break; /* MST_DATA */
-
- case MST_SYSEX:
- if (data == 0xf7) /* Sysex end */
- {
- inc->m_state = MST_INIT;
- inc->m_left = 0;
- inc->m_ptr = 0;
- }
- break; /* MST_SYSEX */
-
- default:
- printk("MIDI%d: Unexpected state %d (%02x)\n", orig_dev, inc->m_state, (int) data);
- inc->m_state = MST_INIT;
- }
-}
-
-static void
-leave_sysex(int dev)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
- int timeout = 0;
-
- if (!sysex_state[dev])
- return;
-
- sysex_state[dev] = 0;
-
- while (!midi_devs[orig_dev]->outputc(orig_dev, 0xf7) &&
- timeout < 1000)
- timeout++;
-
- sysex_state[dev] = 0;
-}
-
-static void
-midi_synth_output(int dev)
-{
- /*
- * Currently NOP
- */
-}
-
-int midi_synth_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- /*
- * int orig_dev = synth_devs[dev]->midi_dev;
- */
-
- switch (cmd) {
-
- case SNDCTL_SYNTH_INFO:
- if (__copy_to_user(arg, synth_devs[dev]->info, sizeof(struct synth_info)))
- return -EFAULT;
- return 0;
-
- case SNDCTL_SYNTH_MEMAVL:
- return 0x7fffffff;
-
- default:
- return -EINVAL;
- }
-}
-EXPORT_SYMBOL(midi_synth_ioctl);
-
-int
-midi_synth_kill_note(int dev, int channel, int note, int velocity)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
- int msg, chn;
-
- if (note < 0 || note > 127)
- return 0;
- if (channel < 0 || channel > 15)
- return 0;
- if (velocity < 0)
- velocity = 0;
- if (velocity > 127)
- velocity = 127;
-
- leave_sysex(dev);
-
- msg = prev_out_status[orig_dev] & 0xf0;
- chn = prev_out_status[orig_dev] & 0x0f;
-
- if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80))
- { /*
- * Use running status
- */
- if (!prefix_cmd(orig_dev, note))
- return 0;
-
- midi_outc(orig_dev, note);
-
- if (msg == 0x90) /*
- * Running status = Note on
- */
- midi_outc(orig_dev, 0); /*
- * Note on with velocity 0 == note
- * off
- */
- else
- midi_outc(orig_dev, velocity);
- } else
- {
- if (velocity == 64)
- {
- if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f)))
- return 0;
- midi_outc(orig_dev, 0x90 | (channel & 0x0f)); /*
- * Note on
- */
- midi_outc(orig_dev, note);
- midi_outc(orig_dev, 0); /*
- * Zero G
- */
- } else
- {
- if (!prefix_cmd(orig_dev, 0x80 | (channel & 0x0f)))
- return 0;
- midi_outc(orig_dev, 0x80 | (channel & 0x0f)); /*
- * Note off
- */
- midi_outc(orig_dev, note);
- midi_outc(orig_dev, velocity);
- }
- }
-
- return 0;
-}
-EXPORT_SYMBOL(midi_synth_kill_note);
-
-int
-midi_synth_set_instr(int dev, int channel, int instr_no)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
-
- if (instr_no < 0 || instr_no > 127)
- instr_no = 0;
- if (channel < 0 || channel > 15)
- return 0;
-
- leave_sysex(dev);
-
- if (!prefix_cmd(orig_dev, 0xc0 | (channel & 0x0f)))
- return 0;
- midi_outc(orig_dev, 0xc0 | (channel & 0x0f)); /*
- * Program change
- */
- midi_outc(orig_dev, instr_no);
-
- return 0;
-}
-EXPORT_SYMBOL(midi_synth_set_instr);
-
-int
-midi_synth_start_note(int dev, int channel, int note, int velocity)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
- int msg, chn;
-
- if (note < 0 || note > 127)
- return 0;
- if (channel < 0 || channel > 15)
- return 0;
- if (velocity < 0)
- velocity = 0;
- if (velocity > 127)
- velocity = 127;
-
- leave_sysex(dev);
-
- msg = prev_out_status[orig_dev] & 0xf0;
- chn = prev_out_status[orig_dev] & 0x0f;
-
- if (chn == channel && msg == 0x90)
- { /*
- * Use running status
- */
- if (!prefix_cmd(orig_dev, note))
- return 0;
- midi_outc(orig_dev, note);
- midi_outc(orig_dev, velocity);
- } else
- {
- if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f)))
- return 0;
- midi_outc(orig_dev, 0x90 | (channel & 0x0f)); /*
- * Note on
- */
- midi_outc(orig_dev, note);
- midi_outc(orig_dev, velocity);
- }
- return 0;
-}
-EXPORT_SYMBOL(midi_synth_start_note);
-
-void
-midi_synth_reset(int dev)
-{
-
- leave_sysex(dev);
-}
-EXPORT_SYMBOL(midi_synth_reset);
-
-int
-midi_synth_open(int dev, int mode)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
- int err;
- struct midi_input_info *inc;
-
- if (orig_dev < 0 || orig_dev >= num_midis || midi_devs[orig_dev] == NULL)
- return -ENXIO;
-
- midi2synth[orig_dev] = dev;
- sysex_state[dev] = 0;
- prev_out_status[orig_dev] = 0;
-
- if ((err = midi_devs[orig_dev]->open(orig_dev, mode,
- midi_synth_input, midi_synth_output)) < 0)
- return err;
- inc = &midi_devs[orig_dev]->in_info;
-
- /* save_flags(flags);
- cli();
- don't know against what irqhandler to protect*/
- inc->m_busy = 0;
- inc->m_state = MST_INIT;
- inc->m_ptr = 0;
- inc->m_left = 0;
- inc->m_prev_status = 0x00;
- /* restore_flags(flags); */
-
- return 1;
-}
-EXPORT_SYMBOL(midi_synth_open);
-
-void
-midi_synth_close(int dev)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
-
- leave_sysex(dev);
-
- /*
- * Shut up the synths by sending just single active sensing message.
- */
- midi_devs[orig_dev]->outputc(orig_dev, 0xfe);
-
- midi_devs[orig_dev]->close(orig_dev);
-}
-EXPORT_SYMBOL(midi_synth_close);
-
-void
-midi_synth_hw_control(int dev, unsigned char *event)
-{
-}
-EXPORT_SYMBOL(midi_synth_hw_control);
-
-int
-midi_synth_load_patch(int dev, int format, const char __user *addr,
- int offs, int count, int pmgr_flag)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
-
- struct sysex_info sysex;
- int i;
- unsigned long left, src_offs, eox_seen = 0;
- int first_byte = 1;
- int hdr_size = (unsigned long) &sysex.data[0] - (unsigned long) &sysex;
-
- leave_sysex(dev);
-
- if (!prefix_cmd(orig_dev, 0xf0))
- return 0;
-
- if (format != SYSEX_PATCH)
- {
-/* printk("MIDI Error: Invalid patch format (key) 0x%x\n", format);*/
- return -EINVAL;
- }
- if (count < hdr_size)
- {
-/* printk("MIDI Error: Patch header too short\n");*/
- return -EINVAL;
- }
- count -= hdr_size;
-
- /*
- * Copy the header from user space but ignore the first bytes which have
- * been transferred already.
- */
-
- if(copy_from_user(&((char *) &sysex)[offs], &(addr)[offs], hdr_size - offs))
- return -EFAULT;
-
- if (count < sysex.len)
- {
-/* printk(KERN_WARNING "MIDI Warning: Sysex record too short (%d<%d)\n", count, (int) sysex.len);*/
- sysex.len = count;
- }
- left = sysex.len;
- src_offs = 0;
-
- for (i = 0; i < left && !signal_pending(current); i++)
- {
- unsigned char data;
-
- if (get_user(data,
- (unsigned char __user *)(addr + hdr_size + i)))
- return -EFAULT;
-
- eox_seen = (i > 0 && data & 0x80); /* End of sysex */
-
- if (eox_seen && data != 0xf7)
- data = 0xf7;
-
- if (i == 0)
- {
- if (data != 0xf0)
- {
- printk(KERN_WARNING "midi_synth: Sysex start missing\n");
- return -EINVAL;
- }
- }
- while (!midi_devs[orig_dev]->outputc(orig_dev, (unsigned char) (data & 0xff)) &&
- !signal_pending(current))
- schedule();
-
- if (!first_byte && data & 0x80)
- return 0;
- first_byte = 0;
- }
-
- if (!eox_seen)
- midi_outc(orig_dev, 0xf7);
- return 0;
-}
-EXPORT_SYMBOL(midi_synth_load_patch);
-
-void midi_synth_panning(int dev, int channel, int pressure)
-{
-}
-EXPORT_SYMBOL(midi_synth_panning);
-
-void midi_synth_aftertouch(int dev, int channel, int pressure)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
- int msg, chn;
-
- if (pressure < 0 || pressure > 127)
- return;
- if (channel < 0 || channel > 15)
- return;
-
- leave_sysex(dev);
-
- msg = prev_out_status[orig_dev] & 0xf0;
- chn = prev_out_status[orig_dev] & 0x0f;
-
- if (msg != 0xd0 || chn != channel) /*
- * Test for running status
- */
- {
- if (!prefix_cmd(orig_dev, 0xd0 | (channel & 0x0f)))
- return;
- midi_outc(orig_dev, 0xd0 | (channel & 0x0f)); /*
- * Channel pressure
- */
- } else if (!prefix_cmd(orig_dev, pressure))
- return;
-
- midi_outc(orig_dev, pressure);
-}
-EXPORT_SYMBOL(midi_synth_aftertouch);
-
-void
-midi_synth_controller(int dev, int channel, int ctrl_num, int value)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
- int chn, msg;
-
- if (ctrl_num < 0 || ctrl_num > 127)
- return;
- if (channel < 0 || channel > 15)
- return;
-
- leave_sysex(dev);
-
- msg = prev_out_status[orig_dev] & 0xf0;
- chn = prev_out_status[orig_dev] & 0x0f;
-
- if (msg != 0xb0 || chn != channel)
- {
- if (!prefix_cmd(orig_dev, 0xb0 | (channel & 0x0f)))
- return;
- midi_outc(orig_dev, 0xb0 | (channel & 0x0f));
- } else if (!prefix_cmd(orig_dev, ctrl_num))
- return;
-
- midi_outc(orig_dev, ctrl_num);
- midi_outc(orig_dev, value & 0x7f);
-}
-EXPORT_SYMBOL(midi_synth_controller);
-
-void
-midi_synth_bender(int dev, int channel, int value)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
- int msg, prev_chn;
-
- if (channel < 0 || channel > 15)
- return;
-
- if (value < 0 || value > 16383)
- return;
-
- leave_sysex(dev);
-
- msg = prev_out_status[orig_dev] & 0xf0;
- prev_chn = prev_out_status[orig_dev] & 0x0f;
-
- if (msg != 0xd0 || prev_chn != channel) /*
- * Test for running status
- */
- {
- if (!prefix_cmd(orig_dev, 0xe0 | (channel & 0x0f)))
- return;
- midi_outc(orig_dev, 0xe0 | (channel & 0x0f));
- } else if (!prefix_cmd(orig_dev, value & 0x7f))
- return;
-
- midi_outc(orig_dev, value & 0x7f);
- midi_outc(orig_dev, (value >> 7) & 0x7f);
-}
-EXPORT_SYMBOL(midi_synth_bender);
-
-void
-midi_synth_setup_voice(int dev, int voice, int channel)
-{
-}
-EXPORT_SYMBOL(midi_synth_setup_voice);
-
-int
-midi_synth_send_sysex(int dev, unsigned char *bytes, int len)
-{
- int orig_dev = synth_devs[dev]->midi_dev;
- int i;
-
- for (i = 0; i < len; i++)
- {
- switch (bytes[i])
- {
- case 0xf0: /* Start sysex */
- if (!prefix_cmd(orig_dev, 0xf0))
- return 0;
- sysex_state[dev] = 1;
- break;
-
- case 0xf7: /* End sysex */
- if (!sysex_state[dev]) /* Orphan sysex end */
- return 0;
- sysex_state[dev] = 0;
- break;
-
- default:
- if (!sysex_state[dev])
- return 0;
-
- if (bytes[i] & 0x80) /* Error. Another message before sysex end */
- {
- bytes[i] = 0xf7; /* Sysex end */
- sysex_state[dev] = 0;
- }
- }
-
- if (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i]))
- {
-/*
- * Hardware level buffer is full. Abort the sysex message.
- */
-
- int timeout = 0;
-
- bytes[i] = 0xf7;
- sysex_state[dev] = 0;
-
- while (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i]) &&
- timeout < 1000)
- timeout++;
- }
- if (!sysex_state[dev])
- return 0;
- }
-
- return 0;
-}
-EXPORT_SYMBOL(midi_synth_send_sysex);
-
diff --git a/sound/oss/midi_synth.h b/sound/oss/midi_synth.h
deleted file mode 100644
index 6bc9d00bc77c..000000000000
--- a/sound/oss/midi_synth.h
+++ /dev/null
@@ -1,47 +0,0 @@
-int midi_synth_ioctl (int dev,
- unsigned int cmd, void __user * arg);
-int midi_synth_kill_note (int dev, int channel, int note, int velocity);
-int midi_synth_set_instr (int dev, int channel, int instr_no);
-int midi_synth_start_note (int dev, int channel, int note, int volume);
-void midi_synth_reset (int dev);
-int midi_synth_open (int dev, int mode);
-void midi_synth_close (int dev);
-void midi_synth_hw_control (int dev, unsigned char *event);
-int midi_synth_load_patch (int dev, int format, const char __user * addr,
- int offs, int count, int pmgr_flag);
-void midi_synth_panning (int dev, int channel, int pressure);
-void midi_synth_aftertouch (int dev, int channel, int pressure);
-void midi_synth_controller (int dev, int channel, int ctrl_num, int value);
-void midi_synth_bender (int dev, int chn, int value);
-void midi_synth_setup_voice (int dev, int voice, int chn);
-int midi_synth_send_sysex(int dev, unsigned char *bytes,int len);
-
-#ifndef _MIDI_SYNTH_C_
-static struct synth_info std_synth_info =
-{MIDI_SYNTH_NAME, 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, MIDI_SYNTH_CAPS};
-
-static struct synth_operations std_midi_synth =
-{
- .owner = THIS_MODULE,
- .id = "MIDI",
- .info = &std_synth_info,
- .midi_dev = 0,
- .synth_type = SYNTH_TYPE_MIDI,
- .synth_subtype = 0,
- .open = midi_synth_open,
- .close = midi_synth_close,
- .ioctl = midi_synth_ioctl,
- .kill_note = midi_synth_kill_note,
- .start_note = midi_synth_start_note,
- .set_instr = midi_synth_set_instr,
- .reset = midi_synth_reset,
- .hw_control = midi_synth_hw_control,
- .load_patch = midi_synth_load_patch,
- .aftertouch = midi_synth_aftertouch,
- .controller = midi_synth_controller,
- .panning = midi_synth_panning,
- .bender = midi_synth_bender,
- .setup_voice = midi_synth_setup_voice,
- .send_sysex = midi_synth_send_sysex
-};
-#endif
diff --git a/sound/oss/midibuf.c b/sound/oss/midibuf.c
deleted file mode 100644
index 782b3b84dac6..000000000000
--- a/sound/oss/midibuf.c
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * sound/oss/midibuf.c
- *
- * Device file manager for /dev/midi#
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-/*
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- */
-#include <linux/stddef.h>
-#include <linux/kmod.h>
-#include <linux/spinlock.h>
-#define MIDIBUF_C
-
-#include "sound_config.h"
-
-
-/*
- * Don't make MAX_QUEUE_SIZE larger than 4000
- */
-
-#define MAX_QUEUE_SIZE 4000
-
-static wait_queue_head_t midi_sleeper[MAX_MIDI_DEV];
-static wait_queue_head_t input_sleeper[MAX_MIDI_DEV];
-
-struct midi_buf
-{
- int len, head, tail;
- unsigned char queue[MAX_QUEUE_SIZE];
-};
-
-struct midi_parms
-{
- long prech_timeout; /*
- * Timeout before the first ch
- */
-};
-
-static struct midi_buf *midi_out_buf[MAX_MIDI_DEV] = {NULL};
-static struct midi_buf *midi_in_buf[MAX_MIDI_DEV] = {NULL};
-static struct midi_parms parms[MAX_MIDI_DEV];
-
-static void midi_poll(unsigned long dummy);
-
-
-static DEFINE_TIMER(poll_timer, midi_poll, 0, 0);
-
-static volatile int open_devs;
-static DEFINE_SPINLOCK(lock);
-
-#define DATA_AVAIL(q) (q->len)
-#define SPACE_AVAIL(q) (MAX_QUEUE_SIZE - q->len)
-
-#define QUEUE_BYTE(q, data) \
- if (SPACE_AVAIL(q)) \
- { \
- unsigned long flags; \
- spin_lock_irqsave(&lock, flags); \
- q->queue[q->tail] = (data); \
- q->len++; q->tail = (q->tail+1) % MAX_QUEUE_SIZE; \
- spin_unlock_irqrestore(&lock, flags); \
- }
-
-#define REMOVE_BYTE(q, data) \
- if (DATA_AVAIL(q)) \
- { \
- unsigned long flags; \
- spin_lock_irqsave(&lock, flags); \
- data = q->queue[q->head]; \
- q->len--; q->head = (q->head+1) % MAX_QUEUE_SIZE; \
- spin_unlock_irqrestore(&lock, flags); \
- }
-
-static void drain_midi_queue(int dev)
-{
-
- /*
- * Give the Midi driver time to drain its output queues
- */
-
- if (midi_devs[dev]->buffer_status != NULL)
- while (!signal_pending(current) && midi_devs[dev]->buffer_status(dev))
- interruptible_sleep_on_timeout(&midi_sleeper[dev],
- HZ/10);
-}
-
-static void midi_input_intr(int dev, unsigned char data)
-{
- if (midi_in_buf[dev] == NULL)
- return;
-
- if (data == 0xfe) /*
- * Active sensing
- */
- return; /*
- * Ignore
- */
-
- if (SPACE_AVAIL(midi_in_buf[dev])) {
- QUEUE_BYTE(midi_in_buf[dev], data);
- wake_up(&input_sleeper[dev]);
- }
-}
-
-static void midi_output_intr(int dev)
-{
- /*
- * Currently NOP
- */
-}
-
-static void midi_poll(unsigned long dummy)
-{
- unsigned long flags;
- int dev;
-
- spin_lock_irqsave(&lock, flags);
- if (open_devs)
- {
- for (dev = 0; dev < num_midis; dev++)
- if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL)
- {
- while (DATA_AVAIL(midi_out_buf[dev]))
- {
- int ok;
- int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head];
-
- spin_unlock_irqrestore(&lock,flags);/* Give some time to others */
- ok = midi_devs[dev]->outputc(dev, c);
- spin_lock_irqsave(&lock, flags);
- if (!ok)
- break;
- midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE;
- midi_out_buf[dev]->len--;
- }
-
- if (DATA_AVAIL(midi_out_buf[dev]) < 100)
- wake_up(&midi_sleeper[dev]);
- }
- poll_timer.expires = (1) + jiffies;
- add_timer(&poll_timer);
- /*
- * Come back later
- */
- }
- spin_unlock_irqrestore(&lock, flags);
-}
-
-int MIDIbuf_open(int dev, struct file *file)
-{
- int mode, err;
-
- dev = dev >> 4;
- mode = translate_mode(file);
-
- if (num_midis > MAX_MIDI_DEV)
- {
- printk(KERN_ERR "midi: Too many midi interfaces\n");
- num_midis = MAX_MIDI_DEV;
- }
- if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
- return -ENXIO;
- /*
- * Interrupts disabled. Be careful
- */
-
- module_put(midi_devs[dev]->owner);
-
- if ((err = midi_devs[dev]->open(dev, mode,
- midi_input_intr, midi_output_intr)) < 0)
- return err;
-
- parms[dev].prech_timeout = MAX_SCHEDULE_TIMEOUT;
- midi_in_buf[dev] = (struct midi_buf *) vmalloc(sizeof(struct midi_buf));
-
- if (midi_in_buf[dev] == NULL)
- {
- printk(KERN_WARNING "midi: Can't allocate buffer\n");
- midi_devs[dev]->close(dev);
- return -EIO;
- }
- midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0;
-
- midi_out_buf[dev] = (struct midi_buf *) vmalloc(sizeof(struct midi_buf));
-
- if (midi_out_buf[dev] == NULL)
- {
- printk(KERN_WARNING "midi: Can't allocate buffer\n");
- midi_devs[dev]->close(dev);
- vfree(midi_in_buf[dev]);
- midi_in_buf[dev] = NULL;
- return -EIO;
- }
- midi_out_buf[dev]->len = midi_out_buf[dev]->head = midi_out_buf[dev]->tail = 0;
- open_devs++;
-
- init_waitqueue_head(&midi_sleeper[dev]);
- init_waitqueue_head(&input_sleeper[dev]);
-
- if (open_devs < 2) /* This was first open */
- {
- poll_timer.expires = 1 + jiffies;
- add_timer(&poll_timer); /* Start polling */
- }
- return err;
-}
-
-void MIDIbuf_release(int dev, struct file *file)
-{
- int mode;
-
- dev = dev >> 4;
- mode = translate_mode(file);
-
- if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
- return;
-
- /*
- * Wait until the queue is empty
- */
-
- if (mode != OPEN_READ)
- {
- midi_devs[dev]->outputc(dev, 0xfe); /*
- * Active sensing to shut the
- * devices
- */
-
- while (!signal_pending(current) && DATA_AVAIL(midi_out_buf[dev]))
- interruptible_sleep_on(&midi_sleeper[dev]);
- /*
- * Sync
- */
-
- drain_midi_queue(dev); /*
- * Ensure the output queues are empty
- */
- }
-
- midi_devs[dev]->close(dev);
-
- open_devs--;
- if (open_devs == 0)
- del_timer_sync(&poll_timer);
- vfree(midi_in_buf[dev]);
- vfree(midi_out_buf[dev]);
- midi_in_buf[dev] = NULL;
- midi_out_buf[dev] = NULL;
-
- module_put(midi_devs[dev]->owner);
-}
-
-int MIDIbuf_write(int dev, struct file *file, const char __user *buf, int count)
-{
- int c, n, i;
- unsigned char tmp_data;
-
- dev = dev >> 4;
-
- if (!count)
- return 0;
-
- c = 0;
-
- while (c < count)
- {
- n = SPACE_AVAIL(midi_out_buf[dev]);
-
- if (n == 0) { /*
- * No space just now.
- */
-
- if (file->f_flags & O_NONBLOCK) {
- c = -EAGAIN;
- goto out;
- }
-
- interruptible_sleep_on(&midi_sleeper[dev]);
- if (signal_pending(current))
- {
- c = -EINTR;
- goto out;
- }
- n = SPACE_AVAIL(midi_out_buf[dev]);
- }
- if (n > (count - c))
- n = count - c;
-
- for (i = 0; i < n; i++)
- {
- /* BROKE BROKE BROKE - CANT DO THIS WITH CLI !! */
- /* yes, think the same, so I removed the cli() brackets
- QUEUE_BYTE is protected against interrupts */
- if (copy_from_user((char *) &tmp_data, &(buf)[c], 1)) {
- c = -EFAULT;
- goto out;
- }
- QUEUE_BYTE(midi_out_buf[dev], tmp_data);
- c++;
- }
- }
-out:
- return c;
-}
-
-
-int MIDIbuf_read(int dev, struct file *file, char __user *buf, int count)
-{
- int n, c = 0;
- unsigned char tmp_data;
-
- dev = dev >> 4;
-
- if (!DATA_AVAIL(midi_in_buf[dev])) { /*
- * No data yet, wait
- */
- if (file->f_flags & O_NONBLOCK) {
- c = -EAGAIN;
- goto out;
- }
- interruptible_sleep_on_timeout(&input_sleeper[dev],
- parms[dev].prech_timeout);
-
- if (signal_pending(current))
- c = -EINTR; /* The user is getting restless */
- }
- if (c == 0 && DATA_AVAIL(midi_in_buf[dev])) /*
- * Got some bytes
- */
- {
- n = DATA_AVAIL(midi_in_buf[dev]);
- if (n > count)
- n = count;
- c = 0;
-
- while (c < n)
- {
- char *fixit;
- REMOVE_BYTE(midi_in_buf[dev], tmp_data);
- fixit = (char *) &tmp_data;
- /* BROKE BROKE BROKE */
- /* yes removed the cli() brackets again
- should q->len,tail&head be atomic_t? */
- if (copy_to_user(&(buf)[c], fixit, 1)) {
- c = -EFAULT;
- goto out;
- }
- c++;
- }
- }
-out:
- return c;
-}
-
-int MIDIbuf_ioctl(int dev, struct file *file,
- unsigned int cmd, void __user *arg)
-{
- int val;
-
- dev = dev >> 4;
-
- if (((cmd >> 8) & 0xff) == 'C')
- {
- if (midi_devs[dev]->coproc) /* Coprocessor ioctl */
- return midi_devs[dev]->coproc->ioctl(midi_devs[dev]->coproc->devc, cmd, arg, 0);
-/* printk("/dev/midi%d: No coprocessor for this device\n", dev);*/
- return -ENXIO;
- }
- else
- {
- switch (cmd)
- {
- case SNDCTL_MIDI_PRETIME:
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
- if (val < 0)
- val = 0;
- val = (HZ * val) / 10;
- parms[dev].prech_timeout = val;
- return put_user(val, (int __user *)arg);
-
- default:
- if (!midi_devs[dev]->ioctl)
- return -EINVAL;
- return midi_devs[dev]->ioctl(dev, cmd, arg);
- }
- }
-}
-
-/* No kernel lock - fine */
-unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait)
-{
- unsigned int mask = 0;
-
- dev = dev >> 4;
-
- /* input */
- poll_wait(file, &input_sleeper[dev], wait);
- if (DATA_AVAIL(midi_in_buf[dev]))
- mask |= POLLIN | POLLRDNORM;
-
- /* output */
- poll_wait(file, &midi_sleeper[dev], wait);
- if (!SPACE_AVAIL(midi_out_buf[dev]))
- mask |= POLLOUT | POLLWRNORM;
-
- return mask;
-}
-
-
-int MIDIbuf_avail(int dev)
-{
- if (midi_in_buf[dev])
- return DATA_AVAIL (midi_in_buf[dev]);
- return 0;
-}
-EXPORT_SYMBOL(MIDIbuf_avail);
-
diff --git a/sound/oss/mpu401.c b/sound/oss/mpu401.c
deleted file mode 100644
index 25e4609f8339..000000000000
--- a/sound/oss/mpu401.c
+++ /dev/null
@@ -1,1806 +0,0 @@
-/*
- * sound/oss/mpu401.c
- *
- * The low level driver for Roland MPU-401 compatible Midi cards.
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Thomas Sailer ioctl code reworked (vmalloc/vfree removed)
- * Alan Cox modularisation, use normal request_irq, use dev_id
- * Bartlomiej Zolnierkiewicz removed some __init to allow using many drivers
- * Chris Rankin Update the module-usage counter for the coprocessor
- * Zwane Mwaikambo Changed attach/unload resource freeing
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#define USE_SEQ_MACROS
-#define USE_SIMPLE_MACROS
-
-#include "sound_config.h"
-
-#include "coproc.h"
-#include "mpu401.h"
-
-static int timer_mode = TMR_INTERNAL, timer_caps = TMR_INTERNAL;
-
-struct mpu_config
-{
- int base; /*
- * I/O base
- */
- int irq;
- int opened; /*
- * Open mode
- */
- int devno;
- int synthno;
- int uart_mode;
- int initialized;
- int mode;
-#define MODE_MIDI 1
-#define MODE_SYNTH 2
- unsigned char version, revision;
- unsigned int capabilities;
-#define MPU_CAP_INTLG 0x10000000
-#define MPU_CAP_SYNC 0x00000010
-#define MPU_CAP_FSK 0x00000020
-#define MPU_CAP_CLS 0x00000040
-#define MPU_CAP_SMPTE 0x00000080
-#define MPU_CAP_2PORT 0x00000001
- int timer_flag;
-
-#define MBUF_MAX 10
-#define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \
- {printk( "MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;}
- int m_busy;
- unsigned char m_buf[MBUF_MAX];
- int m_ptr;
- int m_state;
- int m_left;
- unsigned char last_status;
- void (*inputintr) (int dev, unsigned char data);
- int shared_irq;
- int *osp;
- spinlock_t lock;
- };
-
-#define DATAPORT(base) (base)
-#define COMDPORT(base) (base+1)
-#define STATPORT(base) (base+1)
-
-
-static void mpu401_close(int dev);
-
-static inline int mpu401_status(struct mpu_config *devc)
-{
- return inb(STATPORT(devc->base));
-}
-
-#define input_avail(devc) (!(mpu401_status(devc)&INPUT_AVAIL))
-#define output_ready(devc) (!(mpu401_status(devc)&OUTPUT_READY))
-
-static inline void write_command(struct mpu_config *devc, unsigned char cmd)
-{
- outb(cmd, COMDPORT(devc->base));
-}
-
-static inline int read_data(struct mpu_config *devc)
-{
- return inb(DATAPORT(devc->base));
-}
-
-static inline void write_data(struct mpu_config *devc, unsigned char byte)
-{
- outb(byte, DATAPORT(devc->base));
-}
-
-#define OUTPUT_READY 0x40
-#define INPUT_AVAIL 0x80
-#define MPU_ACK 0xFE
-#define MPU_RESET 0xFF
-#define UART_MODE_ON 0x3F
-
-static struct mpu_config dev_conf[MAX_MIDI_DEV];
-
-static int n_mpu_devs;
-
-static int reset_mpu401(struct mpu_config *devc);
-static void set_uart_mode(int dev, struct mpu_config *devc, int arg);
-
-static int mpu_timer_init(int midi_dev);
-static void mpu_timer_interrupt(void);
-static void timer_ext_event(struct mpu_config *devc, int event, int parm);
-
-static struct synth_info mpu_synth_info_proto = {
- "MPU-401 MIDI interface",
- 0,
- SYNTH_TYPE_MIDI,
- MIDI_TYPE_MPU401,
- 0, 128,
- 0, 128,
- SYNTH_CAP_INPUT
-};
-
-static struct synth_info mpu_synth_info[MAX_MIDI_DEV];
-
-/*
- * States for the input scanner
- */
-
-#define ST_INIT 0 /* Ready for timing byte or msg */
-#define ST_TIMED 1 /* Leading timing byte rcvd */
-#define ST_DATABYTE 2 /* Waiting for (nr_left) data bytes */
-
-#define ST_SYSMSG 100 /* System message (sysx etc). */
-#define ST_SYSEX 101 /* System exclusive msg */
-#define ST_MTC 102 /* Midi Time Code (MTC) qframe msg */
-#define ST_SONGSEL 103 /* Song select */
-#define ST_SONGPOS 104 /* Song position pointer */
-
-static unsigned char len_tab[] = /* # of data bytes following a status
- */
-{
- 2, /* 8x */
- 2, /* 9x */
- 2, /* Ax */
- 2, /* Bx */
- 1, /* Cx */
- 1, /* Dx */
- 2, /* Ex */
- 0 /* Fx */
-};
-
-#define STORE(cmd) \
-{ \
- int len; \
- unsigned char obuf[8]; \
- cmd; \
- seq_input_event(obuf, len); \
-}
-
-#define _seqbuf obuf
-#define _seqbufptr 0
-#define _SEQ_ADVBUF(x) len=x
-
-static int mpu_input_scanner(struct mpu_config *devc, unsigned char midic)
-{
-
- switch (devc->m_state)
- {
- case ST_INIT:
- switch (midic)
- {
- case 0xf8:
- /* Timer overflow */
- break;
-
- case 0xfc:
- printk("<all end>");
- break;
-
- case 0xfd:
- if (devc->timer_flag)
- mpu_timer_interrupt();
- break;
-
- case 0xfe:
- return MPU_ACK;
-
- case 0xf0:
- case 0xf1:
- case 0xf2:
- case 0xf3:
- case 0xf4:
- case 0xf5:
- case 0xf6:
- case 0xf7:
- printk("<Trk data rq #%d>", midic & 0x0f);
- break;
-
- case 0xf9:
- printk("<conductor rq>");
- break;
-
- case 0xff:
- devc->m_state = ST_SYSMSG;
- break;
-
- default:
- if (midic <= 0xef)
- {
- /* printk( "mpu time: %d ", midic); */
- devc->m_state = ST_TIMED;
- }
- else
- printk("<MPU: Unknown event %02x> ", midic);
- }
- break;
-
- case ST_TIMED:
- {
- int msg = ((int) (midic & 0xf0) >> 4);
-
- devc->m_state = ST_DATABYTE;
-
- if (msg < 8) /* Data byte */
- {
- /* printk( "midi msg (running status) "); */
- msg = ((int) (devc->last_status & 0xf0) >> 4);
- msg -= 8;
- devc->m_left = len_tab[msg] - 1;
-
- devc->m_ptr = 2;
- devc->m_buf[0] = devc->last_status;
- devc->m_buf[1] = midic;
-
- if (devc->m_left <= 0)
- {
- devc->m_state = ST_INIT;
- do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
- devc->m_ptr = 0;
- }
- }
- else if (msg == 0xf) /* MPU MARK */
- {
- devc->m_state = ST_INIT;
-
- switch (midic)
- {
- case 0xf8:
- /* printk( "NOP "); */
- break;
-
- case 0xf9:
- /* printk( "meas end "); */
- break;
-
- case 0xfc:
- /* printk( "data end "); */
- break;
-
- default:
- printk("Unknown MPU mark %02x\n", midic);
- }
- }
- else
- {
- devc->last_status = midic;
- /* printk( "midi msg "); */
- msg -= 8;
- devc->m_left = len_tab[msg];
-
- devc->m_ptr = 1;
- devc->m_buf[0] = midic;
-
- if (devc->m_left <= 0)
- {
- devc->m_state = ST_INIT;
- do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
- devc->m_ptr = 0;
- }
- }
- }
- break;
-
- case ST_SYSMSG:
- switch (midic)
- {
- case 0xf0:
- printk("<SYX>");
- devc->m_state = ST_SYSEX;
- break;
-
- case 0xf1:
- devc->m_state = ST_MTC;
- break;
-
- case 0xf2:
- devc->m_state = ST_SONGPOS;
- devc->m_ptr = 0;
- break;
-
- case 0xf3:
- devc->m_state = ST_SONGSEL;
- break;
-
- case 0xf6:
- /* printk( "tune_request\n"); */
- devc->m_state = ST_INIT;
-
- /*
- * Real time messages
- */
- case 0xf8:
- /* midi clock */
- devc->m_state = ST_INIT;
- timer_ext_event(devc, TMR_CLOCK, 0);
- break;
-
- case 0xfA:
- devc->m_state = ST_INIT;
- timer_ext_event(devc, TMR_START, 0);
- break;
-
- case 0xFB:
- devc->m_state = ST_INIT;
- timer_ext_event(devc, TMR_CONTINUE, 0);
- break;
-
- case 0xFC:
- devc->m_state = ST_INIT;
- timer_ext_event(devc, TMR_STOP, 0);
- break;
-
- case 0xFE:
- /* active sensing */
- devc->m_state = ST_INIT;
- break;
-
- case 0xff:
- /* printk( "midi hard reset"); */
- devc->m_state = ST_INIT;
- break;
-
- default:
- printk("unknown MIDI sysmsg %0x\n", midic);
- devc->m_state = ST_INIT;
- }
- break;
-
- case ST_MTC:
- devc->m_state = ST_INIT;
- printk("MTC frame %x02\n", midic);
- break;
-
- case ST_SYSEX:
- if (midic == 0xf7)
- {
- printk("<EOX>");
- devc->m_state = ST_INIT;
- }
- else
- printk("%02x ", midic);
- break;
-
- case ST_SONGPOS:
- BUFTEST(devc);
- devc->m_buf[devc->m_ptr++] = midic;
- if (devc->m_ptr == 2)
- {
- devc->m_state = ST_INIT;
- devc->m_ptr = 0;
- timer_ext_event(devc, TMR_SPP,
- ((devc->m_buf[1] & 0x7f) << 7) |
- (devc->m_buf[0] & 0x7f));
- }
- break;
-
- case ST_DATABYTE:
- BUFTEST(devc);
- devc->m_buf[devc->m_ptr++] = midic;
- if ((--devc->m_left) <= 0)
- {
- devc->m_state = ST_INIT;
- do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
- devc->m_ptr = 0;
- }
- break;
-
- default:
- printk("Bad state %d ", devc->m_state);
- devc->m_state = ST_INIT;
- }
- return 1;
-}
-
-static void mpu401_input_loop(struct mpu_config *devc)
-{
- unsigned long flags;
- int busy;
- int n;
-
- spin_lock_irqsave(&devc->lock,flags);
- busy = devc->m_busy;
- devc->m_busy = 1;
- spin_unlock_irqrestore(&devc->lock,flags);
-
- if (busy) /* Already inside the scanner */
- return;
-
- n = 50;
-
- while (input_avail(devc) && n-- > 0)
- {
- unsigned char c = read_data(devc);
-
- if (devc->mode == MODE_SYNTH)
- {
- mpu_input_scanner(devc, c);
- }
- else if (devc->opened & OPEN_READ && devc->inputintr != NULL)
- devc->inputintr(devc->devno, c);
- }
- devc->m_busy = 0;
-}
-
-static irqreturn_t mpuintr(int irq, void *dev_id)
-{
- struct mpu_config *devc;
- int dev = (int)(unsigned long) dev_id;
- int handled = 0;
-
- devc = &dev_conf[dev];
-
- if (input_avail(devc))
- {
- handled = 1;
- if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH))
- mpu401_input_loop(devc);
- else
- {
- /* Dummy read (just to acknowledge the interrupt) */
- read_data(devc);
- }
- }
- return IRQ_RETVAL(handled);
-}
-
-static int mpu401_open(int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
-)
-{
- int err;
- struct mpu_config *devc;
- struct coproc_operations *coprocessor;
-
- if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
- return -ENXIO;
-
- devc = &dev_conf[dev];
-
- if (devc->opened)
- return -EBUSY;
- /*
- * Verify that the device is really running.
- * Some devices (such as Ensoniq SoundScape don't
- * work before the on board processor (OBP) is initialized
- * by downloading its microcode.
- */
-
- if (!devc->initialized)
- {
- if (mpu401_status(devc) == 0xff) /* Bus float */
- {
- printk(KERN_ERR "mpu401: Device not initialized properly\n");
- return -EIO;
- }
- reset_mpu401(devc);
- }
-
- if ( (coprocessor = midi_devs[dev]->coproc) != NULL )
- {
- if (!try_module_get(coprocessor->owner)) {
- mpu401_close(dev);
- return -ENODEV;
- }
-
- if ((err = coprocessor->open(coprocessor->devc, COPR_MIDI)) < 0)
- {
- printk(KERN_WARNING "MPU-401: Can't access coprocessor device\n");
- mpu401_close(dev);
- return err;
- }
- }
-
- set_uart_mode(dev, devc, 1);
- devc->mode = MODE_MIDI;
- devc->synthno = 0;
-
- mpu401_input_loop(devc);
-
- devc->inputintr = input;
- devc->opened = mode;
-
- return 0;
-}
-
-static void mpu401_close(int dev)
-{
- struct mpu_config *devc;
- struct coproc_operations *coprocessor;
-
- devc = &dev_conf[dev];
- if (devc->uart_mode)
- reset_mpu401(devc); /*
- * This disables the UART mode
- */
- devc->mode = 0;
- devc->inputintr = NULL;
-
- coprocessor = midi_devs[dev]->coproc;
- if (coprocessor) {
- coprocessor->close(coprocessor->devc, COPR_MIDI);
- module_put(coprocessor->owner);
- }
- devc->opened = 0;
-}
-
-static int mpu401_out(int dev, unsigned char midi_byte)
-{
- int timeout;
- unsigned long flags;
-
- struct mpu_config *devc;
-
- devc = &dev_conf[dev];
-
- /*
- * Sometimes it takes about 30000 loops before the output becomes ready
- * (After reset). Normally it takes just about 10 loops.
- */
-
- for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
-
- spin_lock_irqsave(&devc->lock,flags);
- if (!output_ready(devc))
- {
- printk(KERN_WARNING "mpu401: Send data timeout\n");
- spin_unlock_irqrestore(&devc->lock,flags);
- return 0;
- }
- write_data(devc, midi_byte);
- spin_unlock_irqrestore(&devc->lock,flags);
- return 1;
-}
-
-static int mpu401_command(int dev, mpu_command_rec * cmd)
-{
- int i, timeout, ok;
- int ret = 0;
- unsigned long flags;
- struct mpu_config *devc;
-
- devc = &dev_conf[dev];
-
- if (devc->uart_mode) /*
- * Not possible in UART mode
- */
- {
- printk(KERN_WARNING "mpu401: commands not possible in the UART mode\n");
- return -EINVAL;
- }
- /*
- * Test for input since pending input seems to block the output.
- */
- if (input_avail(devc))
- mpu401_input_loop(devc);
-
- /*
- * Sometimes it takes about 50000 loops before the output becomes ready
- * (After reset). Normally it takes just about 10 loops.
- */
-
- timeout = 50000;
-retry:
- if (timeout-- <= 0)
- {
- printk(KERN_WARNING "mpu401: Command (0x%x) timeout\n", (int) cmd->cmd);
- return -EIO;
- }
- spin_lock_irqsave(&devc->lock,flags);
-
- if (!output_ready(devc))
- {
- spin_unlock_irqrestore(&devc->lock,flags);
- goto retry;
- }
- write_command(devc, cmd->cmd);
-
- ok = 0;
- for (timeout = 50000; timeout > 0 && !ok; timeout--)
- {
- if (input_avail(devc))
- {
- if (devc->opened && devc->mode == MODE_SYNTH)
- {
- if (mpu_input_scanner(devc, read_data(devc)) == MPU_ACK)
- ok = 1;
- }
- else
- {
- /* Device is not currently open. Use simpler method */
- if (read_data(devc) == MPU_ACK)
- ok = 1;
- }
- }
- }
- if (!ok)
- {
- spin_unlock_irqrestore(&devc->lock,flags);
- return -EIO;
- }
- if (cmd->nr_args)
- {
- for (i = 0; i < cmd->nr_args; i++)
- {
- for (timeout = 3000; timeout > 0 && !output_ready(devc); timeout--);
-
- if (!mpu401_out(dev, cmd->data[i]))
- {
- spin_unlock_irqrestore(&devc->lock,flags);
- printk(KERN_WARNING "mpu401: Command (0x%x), parm send failed.\n", (int) cmd->cmd);
- return -EIO;
- }
- }
- }
- ret = 0;
- cmd->data[0] = 0;
-
- if (cmd->nr_returns)
- {
- for (i = 0; i < cmd->nr_returns; i++)
- {
- ok = 0;
- for (timeout = 5000; timeout > 0 && !ok; timeout--)
- if (input_avail(devc))
- {
- cmd->data[i] = read_data(devc);
- ok = 1;
- }
- if (!ok)
- {
- spin_unlock_irqrestore(&devc->lock,flags);
- return -EIO;
- }
- }
- }
- spin_unlock_irqrestore(&devc->lock,flags);
- return ret;
-}
-
-static int mpu_cmd(int dev, int cmd, int data)
-{
- int ret;
-
- static mpu_command_rec rec;
-
- rec.cmd = cmd & 0xff;
- rec.nr_args = ((cmd & 0xf0) == 0xE0);
- rec.nr_returns = ((cmd & 0xf0) == 0xA0);
- rec.data[0] = data & 0xff;
-
- if ((ret = mpu401_command(dev, &rec)) < 0)
- return ret;
- return (unsigned char) rec.data[0];
-}
-
-static int mpu401_prefix_cmd(int dev, unsigned char status)
-{
- struct mpu_config *devc = &dev_conf[dev];
-
- if (devc->uart_mode)
- return 1;
-
- if (status < 0xf0)
- {
- if (mpu_cmd(dev, 0xD0, 0) < 0)
- return 0;
- return 1;
- }
- switch (status)
- {
- case 0xF0:
- if (mpu_cmd(dev, 0xDF, 0) < 0)
- return 0;
- return 1;
-
- default:
- return 0;
- }
-}
-
-static int mpu401_start_read(int dev)
-{
- return 0;
-}
-
-static int mpu401_end_read(int dev)
-{
- return 0;
-}
-
-static int mpu401_ioctl(int dev, unsigned cmd, void __user *arg)
-{
- struct mpu_config *devc;
- mpu_command_rec rec;
- int val, ret;
-
- devc = &dev_conf[dev];
- switch (cmd)
- {
- case SNDCTL_MIDI_MPUMODE:
- if (!(devc->capabilities & MPU_CAP_INTLG)) { /* No intelligent mode */
- printk(KERN_WARNING "mpu401: Intelligent mode not supported by the HW\n");
- return -EINVAL;
- }
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
- set_uart_mode(dev, devc, !val);
- return 0;
-
- case SNDCTL_MIDI_MPUCMD:
- if (copy_from_user(&rec, arg, sizeof(rec)))
- return -EFAULT;
- if ((ret = mpu401_command(dev, &rec)) < 0)
- return ret;
- if (copy_to_user(arg, &rec, sizeof(rec)))
- return -EFAULT;
- return 0;
-
- default:
- return -EINVAL;
- }
-}
-
-static void mpu401_kick(int dev)
-{
-}
-
-static int mpu401_buffer_status(int dev)
-{
- return 0; /*
- * No data in buffers
- */
-}
-
-static int mpu_synth_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- int midi_dev;
- struct mpu_config *devc;
-
- midi_dev = synth_devs[dev]->midi_dev;
-
- if (midi_dev < 0 || midi_dev >= num_midis || midi_devs[midi_dev] == NULL)
- return -ENXIO;
-
- devc = &dev_conf[midi_dev];
-
- switch (cmd)
- {
-
- case SNDCTL_SYNTH_INFO:
- if (copy_to_user(arg, &mpu_synth_info[midi_dev],
- sizeof(struct synth_info)))
- return -EFAULT;
- return 0;
-
- case SNDCTL_SYNTH_MEMAVL:
- return 0x7fffffff;
-
- default:
- return -EINVAL;
- }
-}
-
-static int mpu_synth_open(int dev, int mode)
-{
- int midi_dev, err;
- struct mpu_config *devc;
- struct coproc_operations *coprocessor;
-
- midi_dev = synth_devs[dev]->midi_dev;
-
- if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL)
- return -ENXIO;
-
- devc = &dev_conf[midi_dev];
-
- /*
- * Verify that the device is really running.
- * Some devices (such as Ensoniq SoundScape don't
- * work before the on board processor (OBP) is initialized
- * by downloading its microcode.
- */
-
- if (!devc->initialized)
- {
- if (mpu401_status(devc) == 0xff) /* Bus float */
- {
- printk(KERN_ERR "mpu401: Device not initialized properly\n");
- return -EIO;
- }
- reset_mpu401(devc);
- }
- if (devc->opened)
- return -EBUSY;
- devc->mode = MODE_SYNTH;
- devc->synthno = dev;
-
- devc->inputintr = NULL;
-
- coprocessor = midi_devs[midi_dev]->coproc;
- if (coprocessor) {
- if (!try_module_get(coprocessor->owner))
- return -ENODEV;
-
- if ((err = coprocessor->open(coprocessor->devc, COPR_MIDI)) < 0)
- {
- printk(KERN_WARNING "mpu401: Can't access coprocessor device\n");
- return err;
- }
- }
- devc->opened = mode;
- reset_mpu401(devc);
-
- if (mode & OPEN_READ)
- {
- mpu_cmd(midi_dev, 0x8B, 0); /* Enable data in stop mode */
- mpu_cmd(midi_dev, 0x34, 0); /* Return timing bytes in stop mode */
- mpu_cmd(midi_dev, 0x87, 0); /* Enable pitch & controller */
- }
- return 0;
-}
-
-static void mpu_synth_close(int dev)
-{
- int midi_dev;
- struct mpu_config *devc;
- struct coproc_operations *coprocessor;
-
- midi_dev = synth_devs[dev]->midi_dev;
-
- devc = &dev_conf[midi_dev];
- mpu_cmd(midi_dev, 0x15, 0); /* Stop recording, playback and MIDI */
- mpu_cmd(midi_dev, 0x8a, 0); /* Disable data in stopped mode */
-
- devc->inputintr = NULL;
-
- coprocessor = midi_devs[midi_dev]->coproc;
- if (coprocessor) {
- coprocessor->close(coprocessor->devc, COPR_MIDI);
- module_put(coprocessor->owner);
- }
- devc->opened = 0;
- devc->mode = 0;
-}
-
-#define MIDI_SYNTH_NAME "MPU-401 UART Midi"
-#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
-#include "midi_synth.h"
-
-static struct synth_operations mpu401_synth_proto =
-{
- .owner = THIS_MODULE,
- .id = "MPU401",
- .info = NULL,
- .midi_dev = 0,
- .synth_type = SYNTH_TYPE_MIDI,
- .synth_subtype = 0,
- .open = mpu_synth_open,
- .close = mpu_synth_close,
- .ioctl = mpu_synth_ioctl,
- .kill_note = midi_synth_kill_note,
- .start_note = midi_synth_start_note,
- .set_instr = midi_synth_set_instr,
- .reset = midi_synth_reset,
- .hw_control = midi_synth_hw_control,
- .load_patch = midi_synth_load_patch,
- .aftertouch = midi_synth_aftertouch,
- .controller = midi_synth_controller,
- .panning = midi_synth_panning,
- .bender = midi_synth_bender,
- .setup_voice = midi_synth_setup_voice,
- .send_sysex = midi_synth_send_sysex
-};
-
-static struct synth_operations *mpu401_synth_operations[MAX_MIDI_DEV];
-
-static struct midi_operations mpu401_midi_proto =
-{
- .owner = THIS_MODULE,
- .info = {"MPU-401 Midi", 0, MIDI_CAP_MPU401, SNDCARD_MPU401},
- .in_info = {0},
- .open = mpu401_open,
- .close = mpu401_close,
- .ioctl = mpu401_ioctl,
- .outputc = mpu401_out,
- .start_read = mpu401_start_read,
- .end_read = mpu401_end_read,
- .kick = mpu401_kick,
- .buffer_status = mpu401_buffer_status,
- .prefix_cmd = mpu401_prefix_cmd
-};
-
-static struct midi_operations mpu401_midi_operations[MAX_MIDI_DEV];
-
-static void mpu401_chk_version(int n, struct mpu_config *devc)
-{
- int tmp;
-
- devc->version = devc->revision = 0;
-
- tmp = mpu_cmd(n, 0xAC, 0);
- if (tmp < 0)
- return;
- if ((tmp & 0xf0) > 0x20) /* Why it's larger than 2.x ??? */
- return;
- devc->version = tmp;
-
- if ((tmp = mpu_cmd(n, 0xAD, 0)) < 0) {
- devc->version = 0;
- return;
- }
- devc->revision = tmp;
-}
-
-int attach_mpu401(struct address_info *hw_config, struct module *owner)
-{
- unsigned long flags;
- char revision_char;
-
- int m, ret;
- struct mpu_config *devc;
-
- hw_config->slots[1] = -1;
- m = sound_alloc_mididev();
- if (m == -1)
- {
- printk(KERN_WARNING "MPU-401: Too many midi devices detected\n");
- ret = -ENOMEM;
- goto out_err;
- }
- devc = &dev_conf[m];
- devc->base = hw_config->io_base;
- devc->osp = hw_config->osp;
- devc->irq = hw_config->irq;
- devc->opened = 0;
- devc->uart_mode = 0;
- devc->initialized = 0;
- devc->version = 0;
- devc->revision = 0;
- devc->capabilities = 0;
- devc->timer_flag = 0;
- devc->m_busy = 0;
- devc->m_state = ST_INIT;
- devc->shared_irq = hw_config->always_detect;
- devc->irq = hw_config->irq;
- spin_lock_init(&devc->lock);
-
- if (devc->irq < 0)
- {
- devc->irq *= -1;
- devc->shared_irq = 1;
- }
-
- if (!hw_config->always_detect)
- {
- /* Verify the hardware again */
- if (!reset_mpu401(devc))
- {
- printk(KERN_WARNING "mpu401: Device didn't respond\n");
- ret = -ENODEV;
- goto out_mididev;
- }
- if (!devc->shared_irq)
- {
- if (request_irq(devc->irq, mpuintr, 0, "mpu401",
- hw_config) < 0)
- {
- printk(KERN_WARNING "mpu401: Failed to allocate IRQ%d\n", devc->irq);
- ret = -ENOMEM;
- goto out_mididev;
- }
- }
- spin_lock_irqsave(&devc->lock,flags);
- mpu401_chk_version(m, devc);
- if (devc->version == 0)
- mpu401_chk_version(m, devc);
- spin_unlock_irqrestore(&devc->lock, flags);
- }
-
- if (devc->version != 0)
- if (mpu_cmd(m, 0xC5, 0) >= 0) /* Set timebase OK */
- if (mpu_cmd(m, 0xE0, 120) >= 0) /* Set tempo OK */
- devc->capabilities |= MPU_CAP_INTLG; /* Supports intelligent mode */
-
-
- mpu401_synth_operations[m] = kmalloc(sizeof(struct synth_operations), GFP_KERNEL);
-
- if (mpu401_synth_operations[m] == NULL)
- {
- printk(KERN_ERR "mpu401: Can't allocate memory\n");
- ret = -ENOMEM;
- goto out_irq;
- }
- if (!(devc->capabilities & MPU_CAP_INTLG)) /* No intelligent mode */
- {
- memcpy((char *) mpu401_synth_operations[m],
- (char *) &std_midi_synth,
- sizeof(struct synth_operations));
- }
- else
- {
- memcpy((char *) mpu401_synth_operations[m],
- (char *) &mpu401_synth_proto,
- sizeof(struct synth_operations));
- }
- if (owner)
- mpu401_synth_operations[m]->owner = owner;
-
- memcpy((char *) &mpu401_midi_operations[m],
- (char *) &mpu401_midi_proto,
- sizeof(struct midi_operations));
-
- mpu401_midi_operations[m].converter = mpu401_synth_operations[m];
-
- memcpy((char *) &mpu_synth_info[m],
- (char *) &mpu_synth_info_proto,
- sizeof(struct synth_info));
-
- n_mpu_devs++;
-
- if (devc->version == 0x20 && devc->revision >= 0x07) /* MusicQuest interface */
- {
- int ports = (devc->revision & 0x08) ? 32 : 16;
-
- devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE |
- MPU_CAP_CLS | MPU_CAP_2PORT;
-
- revision_char = (devc->revision == 0x7f) ? 'M' : ' ';
- sprintf(mpu_synth_info[m].name, "MQX-%d%c MIDI Interface #%d",
- ports,
- revision_char,
- n_mpu_devs);
- }
- else
- {
- revision_char = devc->revision ? devc->revision + '@' : ' ';
- if ((int) devc->revision > ('Z' - '@'))
- revision_char = '+';
-
- devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK;
-
- if (hw_config->name)
- sprintf(mpu_synth_info[m].name, "%s (MPU401)", hw_config->name);
- else
- sprintf(mpu_synth_info[m].name,
- "MPU-401 %d.%d%c MIDI #%d",
- (int) (devc->version & 0xf0) >> 4,
- devc->version & 0x0f,
- revision_char,
- n_mpu_devs);
- }
-
- strcpy(mpu401_midi_operations[m].info.name,
- mpu_synth_info[m].name);
-
- conf_printf(mpu_synth_info[m].name, hw_config);
-
- mpu401_synth_operations[m]->midi_dev = devc->devno = m;
- mpu401_synth_operations[devc->devno]->info = &mpu_synth_info[devc->devno];
-
- if (devc->capabilities & MPU_CAP_INTLG) /* Intelligent mode */
- hw_config->slots[2] = mpu_timer_init(m);
-
- midi_devs[m] = &mpu401_midi_operations[devc->devno];
-
- if (owner)
- midi_devs[m]->owner = owner;
-
- hw_config->slots[1] = m;
- sequencer_init();
-
- return 0;
-
-out_irq:
- free_irq(devc->irq, hw_config);
-out_mididev:
- sound_unload_mididev(m);
-out_err:
- release_region(hw_config->io_base, 2);
- return ret;
-}
-
-static int reset_mpu401(struct mpu_config *devc)
-{
- unsigned long flags;
- int ok, timeout, n;
- int timeout_limit;
-
- /*
- * Send the RESET command. Try again if no success at the first time.
- * (If the device is in the UART mode, it will not ack the reset cmd).
- */
-
- ok = 0;
-
- timeout_limit = devc->initialized ? 30000 : 100000;
- devc->initialized = 1;
-
- for (n = 0; n < 2 && !ok; n++)
- {
- for (timeout = timeout_limit; timeout > 0 && !ok; timeout--)
- ok = output_ready(devc);
-
- write_command(devc, MPU_RESET); /*
- * Send MPU-401 RESET Command
- */
-
- /*
- * Wait at least 25 msec. This method is not accurate so let's make the
- * loop bit longer. Cannot sleep since this is called during boot.
- */
-
- for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--)
- {
- spin_lock_irqsave(&devc->lock,flags);
- if (input_avail(devc))
- if (read_data(devc) == MPU_ACK)
- ok = 1;
- spin_unlock_irqrestore(&devc->lock,flags);
- }
-
- }
-
- devc->m_state = ST_INIT;
- devc->m_ptr = 0;
- devc->m_left = 0;
- devc->last_status = 0;
- devc->uart_mode = 0;
-
- return ok;
-}
-
-static void set_uart_mode(int dev, struct mpu_config *devc, int arg)
-{
- if (!arg && (devc->capabilities & MPU_CAP_INTLG))
- return;
- if ((devc->uart_mode == 0) == (arg == 0))
- return; /* Already set */
- reset_mpu401(devc); /* This exits the uart mode */
-
- if (arg)
- {
- if (mpu_cmd(dev, UART_MODE_ON, 0) < 0)
- {
- printk(KERN_ERR "mpu401: Can't enter UART mode\n");
- devc->uart_mode = 0;
- return;
- }
- }
- devc->uart_mode = arg;
-
-}
-
-int probe_mpu401(struct address_info *hw_config, struct resource *ports)
-{
- int ok = 0;
- struct mpu_config tmp_devc;
-
- tmp_devc.base = hw_config->io_base;
- tmp_devc.irq = hw_config->irq;
- tmp_devc.initialized = 0;
- tmp_devc.opened = 0;
- tmp_devc.osp = hw_config->osp;
-
- if (hw_config->always_detect)
- return 1;
-
- if (inb(hw_config->io_base + 1) == 0xff)
- {
- DDB(printk("MPU401: Port %x looks dead.\n", hw_config->io_base));
- return 0; /* Just bus float? */
- }
- ok = reset_mpu401(&tmp_devc);
-
- if (!ok)
- {
- DDB(printk("MPU401: Reset failed on port %x\n", hw_config->io_base));
- }
- return ok;
-}
-
-void unload_mpu401(struct address_info *hw_config)
-{
- void *p;
- int n=hw_config->slots[1];
-
- if (n != -1) {
- release_region(hw_config->io_base, 2);
- if (hw_config->always_detect == 0 && hw_config->irq > 0)
- free_irq(hw_config->irq, hw_config);
- p=mpu401_synth_operations[n];
- sound_unload_mididev(n);
- sound_unload_timerdev(hw_config->slots[2]);
- kfree(p);
- }
-}
-
-/*****************************************************
- * Timer stuff
- ****************************************************/
-
-static volatile int timer_initialized = 0, timer_open = 0, tmr_running = 0;
-static volatile int curr_tempo, curr_timebase, hw_timebase;
-static int max_timebase = 8; /* 8*24=192 ppqn */
-static volatile unsigned long next_event_time;
-static volatile unsigned long curr_ticks, curr_clocks;
-static unsigned long prev_event_time;
-static int metronome_mode;
-
-static unsigned long clocks2ticks(unsigned long clocks)
-{
- /*
- * The MPU-401 supports just a limited set of possible timebase values.
- * Since the applications require more choices, the driver has to
- * program the HW to do its best and to convert between the HW and
- * actual timebases.
- */
- return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase;
-}
-
-static void set_timebase(int midi_dev, int val)
-{
- int hw_val;
-
- if (val < 48)
- val = 48;
- if (val > 1000)
- val = 1000;
-
- hw_val = val;
- hw_val = (hw_val + 12) / 24;
- if (hw_val > max_timebase)
- hw_val = max_timebase;
-
- if (mpu_cmd(midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0)
- {
- printk(KERN_WARNING "mpu401: Can't set HW timebase to %d\n", hw_val * 24);
- return;
- }
- hw_timebase = hw_val * 24;
- curr_timebase = val;
-
-}
-
-static void tmr_reset(struct mpu_config *devc)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock,flags);
- next_event_time = (unsigned long) -1;
- prev_event_time = 0;
- curr_ticks = curr_clocks = 0;
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static void set_timer_mode(int midi_dev)
-{
- if (timer_mode & TMR_MODE_CLS)
- mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */
- else if (timer_mode & TMR_MODE_SMPTE)
- mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */
-
- if (timer_mode & TMR_INTERNAL)
- {
- mpu_cmd(midi_dev, 0x80, 0); /* Use MIDI sync */
- }
- else
- {
- if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS))
- {
- mpu_cmd(midi_dev, 0x82, 0); /* Use MIDI sync */
- mpu_cmd(midi_dev, 0x91, 0); /* Enable ext MIDI ctrl */
- }
- else if (timer_mode & TMR_MODE_FSK)
- mpu_cmd(midi_dev, 0x81, 0); /* Use FSK sync */
- }
-}
-
-static void stop_metronome(int midi_dev)
-{
- mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */
-}
-
-static void setup_metronome(int midi_dev)
-{
- int numerator, denominator;
- int clks_per_click, num_32nds_per_beat;
- int beats_per_measure;
-
- numerator = ((unsigned) metronome_mode >> 24) & 0xff;
- denominator = ((unsigned) metronome_mode >> 16) & 0xff;
- clks_per_click = ((unsigned) metronome_mode >> 8) & 0xff;
- num_32nds_per_beat = (unsigned) metronome_mode & 0xff;
- beats_per_measure = (numerator * 4) >> denominator;
-
- if (!metronome_mode)
- mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */
- else
- {
- mpu_cmd(midi_dev, 0xE4, clks_per_click);
- mpu_cmd(midi_dev, 0xE6, beats_per_measure);
- mpu_cmd(midi_dev, 0x83, 0); /* Enable metronome without accents */
- }
-}
-
-static int mpu_start_timer(int midi_dev)
-{
- struct mpu_config *devc= &dev_conf[midi_dev];
-
- tmr_reset(devc);
- set_timer_mode(midi_dev);
-
- if (tmr_running)
- return TIMER_NOT_ARMED; /* Already running */
-
- if (timer_mode & TMR_INTERNAL)
- {
- mpu_cmd(midi_dev, 0x02, 0); /* Send MIDI start */
- tmr_running = 1;
- return TIMER_NOT_ARMED;
- }
- else
- {
- mpu_cmd(midi_dev, 0x35, 0); /* Enable mode messages to PC */
- mpu_cmd(midi_dev, 0x38, 0); /* Enable sys common messages to PC */
- mpu_cmd(midi_dev, 0x39, 0); /* Enable real time messages to PC */
- mpu_cmd(midi_dev, 0x97, 0); /* Enable system exclusive messages to PC */
- }
- return TIMER_ARMED;
-}
-
-static int mpu_timer_open(int dev, int mode)
-{
- int midi_dev = sound_timer_devs[dev]->devlink;
- struct mpu_config *devc= &dev_conf[midi_dev];
-
- if (timer_open)
- return -EBUSY;
-
- tmr_reset(devc);
- curr_tempo = 50;
- mpu_cmd(midi_dev, 0xE0, 50);
- curr_timebase = hw_timebase = 120;
- set_timebase(midi_dev, 120);
- timer_open = 1;
- metronome_mode = 0;
- set_timer_mode(midi_dev);
-
- mpu_cmd(midi_dev, 0xe7, 0x04); /* Send all clocks to host */
- mpu_cmd(midi_dev, 0x95, 0); /* Enable clock to host */
-
- return 0;
-}
-
-static void mpu_timer_close(int dev)
-{
- int midi_dev = sound_timer_devs[dev]->devlink;
-
- timer_open = tmr_running = 0;
- mpu_cmd(midi_dev, 0x15, 0); /* Stop all */
- mpu_cmd(midi_dev, 0x94, 0); /* Disable clock to host */
- mpu_cmd(midi_dev, 0x8c, 0); /* Disable measure end messages to host */
- stop_metronome(midi_dev);
-}
-
-static int mpu_timer_event(int dev, unsigned char *event)
-{
- unsigned char command = event[1];
- unsigned long parm = *(unsigned int *) &event[4];
- int midi_dev = sound_timer_devs[dev]->devlink;
-
- switch (command)
- {
- case TMR_WAIT_REL:
- parm += prev_event_time;
- case TMR_WAIT_ABS:
- if (parm > 0)
- {
- long time;
-
- if (parm <= curr_ticks) /* It's the time */
- return TIMER_NOT_ARMED;
- time = parm;
- next_event_time = prev_event_time = time;
-
- return TIMER_ARMED;
- }
- break;
-
- case TMR_START:
- if (tmr_running)
- break;
- return mpu_start_timer(midi_dev);
-
- case TMR_STOP:
- mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */
- stop_metronome(midi_dev);
- tmr_running = 0;
- break;
-
- case TMR_CONTINUE:
- if (tmr_running)
- break;
- mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */
- setup_metronome(midi_dev);
- tmr_running = 1;
- break;
-
- case TMR_TEMPO:
- if (parm)
- {
- if (parm < 8)
- parm = 8;
- if (parm > 250)
- parm = 250;
- if (mpu_cmd(midi_dev, 0xE0, parm) < 0)
- printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) parm);
- curr_tempo = parm;
- }
- break;
-
- case TMR_ECHO:
- seq_copy_to_input(event, 8);
- break;
-
- case TMR_TIMESIG:
- if (metronome_mode) /* Metronome enabled */
- {
- metronome_mode = parm;
- setup_metronome(midi_dev);
- }
- break;
-
- default:;
- }
- return TIMER_NOT_ARMED;
-}
-
-static unsigned long mpu_timer_get_time(int dev)
-{
- if (!timer_open)
- return 0;
-
- return curr_ticks;
-}
-
-static int mpu_timer_ioctl(int dev, unsigned int command, void __user *arg)
-{
- int midi_dev = sound_timer_devs[dev]->devlink;
- int __user *p = (int __user *)arg;
-
- switch (command)
- {
- case SNDCTL_TMR_SOURCE:
- {
- int parm;
-
- if (get_user(parm, p))
- return -EFAULT;
- parm &= timer_caps;
-
- if (parm != 0)
- {
- timer_mode = parm;
-
- if (timer_mode & TMR_MODE_CLS)
- mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */
- else if (timer_mode & TMR_MODE_SMPTE)
- mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */
- }
- if (put_user(timer_mode, p))
- return -EFAULT;
- return timer_mode;
- }
- break;
-
- case SNDCTL_TMR_START:
- mpu_start_timer(midi_dev);
- return 0;
-
- case SNDCTL_TMR_STOP:
- tmr_running = 0;
- mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */
- stop_metronome(midi_dev);
- return 0;
-
- case SNDCTL_TMR_CONTINUE:
- if (tmr_running)
- return 0;
- tmr_running = 1;
- mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */
- return 0;
-
- case SNDCTL_TMR_TIMEBASE:
- {
- int val;
- if (get_user(val, p))
- return -EFAULT;
- if (val)
- set_timebase(midi_dev, val);
- if (put_user(curr_timebase, p))
- return -EFAULT;
- return curr_timebase;
- }
- break;
-
- case SNDCTL_TMR_TEMPO:
- {
- int val;
- int ret;
-
- if (get_user(val, p))
- return -EFAULT;
-
- if (val)
- {
- if (val < 8)
- val = 8;
- if (val > 250)
- val = 250;
- if ((ret = mpu_cmd(midi_dev, 0xE0, val)) < 0)
- {
- printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) val);
- return ret;
- }
- curr_tempo = val;
- }
- if (put_user(curr_tempo, p))
- return -EFAULT;
- return curr_tempo;
- }
- break;
-
- case SNDCTL_SEQ_CTRLRATE:
- {
- int val;
- if (get_user(val, p))
- return -EFAULT;
-
- if (val != 0) /* Can't change */
- return -EINVAL;
- val = ((curr_tempo * curr_timebase) + 30)/60;
- if (put_user(val, p))
- return -EFAULT;
- return val;
- }
- break;
-
- case SNDCTL_SEQ_GETTIME:
- if (put_user(curr_ticks, p))
- return -EFAULT;
- return curr_ticks;
-
- case SNDCTL_TMR_METRONOME:
- if (get_user(metronome_mode, p))
- return -EFAULT;
- setup_metronome(midi_dev);
- return 0;
-
- default:;
- }
- return -EINVAL;
-}
-
-static void mpu_timer_arm(int dev, long time)
-{
- if (time < 0)
- time = curr_ticks + 1;
- else if (time <= curr_ticks) /* It's the time */
- return;
- next_event_time = prev_event_time = time;
- return;
-}
-
-static struct sound_timer_operations mpu_timer =
-{
- .owner = THIS_MODULE,
- .info = {"MPU-401 Timer", 0},
- .priority = 10, /* Priority */
- .devlink = 0, /* Local device link */
- .open = mpu_timer_open,
- .close = mpu_timer_close,
- .event = mpu_timer_event,
- .get_time = mpu_timer_get_time,
- .ioctl = mpu_timer_ioctl,
- .arm_timer = mpu_timer_arm
-};
-
-static void mpu_timer_interrupt(void)
-{
- if (!timer_open)
- return;
-
- if (!tmr_running)
- return;
-
- curr_clocks++;
- curr_ticks = clocks2ticks(curr_clocks);
-
- if (curr_ticks >= next_event_time)
- {
- next_event_time = (unsigned long) -1;
- sequencer_timer(0);
- }
-}
-
-static void timer_ext_event(struct mpu_config *devc, int event, int parm)
-{
- int midi_dev = devc->devno;
-
- if (!devc->timer_flag)
- return;
-
- switch (event)
- {
- case TMR_CLOCK:
- printk("<MIDI clk>");
- break;
-
- case TMR_START:
- printk("Ext MIDI start\n");
- if (!tmr_running)
- {
- if (timer_mode & TMR_EXTERNAL)
- {
- tmr_running = 1;
- setup_metronome(midi_dev);
- next_event_time = 0;
- STORE(SEQ_START_TIMER());
- }
- }
- break;
-
- case TMR_STOP:
- printk("Ext MIDI stop\n");
- if (timer_mode & TMR_EXTERNAL)
- {
- tmr_running = 0;
- stop_metronome(midi_dev);
- STORE(SEQ_STOP_TIMER());
- }
- break;
-
- case TMR_CONTINUE:
- printk("Ext MIDI continue\n");
- if (timer_mode & TMR_EXTERNAL)
- {
- tmr_running = 1;
- setup_metronome(midi_dev);
- STORE(SEQ_CONTINUE_TIMER());
- }
- break;
-
- case TMR_SPP:
- printk("Songpos: %d\n", parm);
- if (timer_mode & TMR_EXTERNAL)
- {
- STORE(SEQ_SONGPOS(parm));
- }
- break;
- }
-}
-
-static int mpu_timer_init(int midi_dev)
-{
- struct mpu_config *devc;
- int n;
-
- devc = &dev_conf[midi_dev];
-
- if (timer_initialized)
- return -1; /* There is already a similar timer */
-
- timer_initialized = 1;
-
- mpu_timer.devlink = midi_dev;
- dev_conf[midi_dev].timer_flag = 1;
-
- n = sound_alloc_timerdev();
- if (n == -1)
- n = 0;
- sound_timer_devs[n] = &mpu_timer;
-
- if (devc->version < 0x20) /* Original MPU-401 */
- timer_caps = TMR_INTERNAL | TMR_EXTERNAL | TMR_MODE_FSK | TMR_MODE_MIDI;
- else
- {
- /*
- * The version number 2.0 is used (at least) by the
- * MusicQuest cards and the Roland Super-MPU.
- *
- * MusicQuest has given a special meaning to the bits of the
- * revision number. The Super-MPU returns 0.
- */
-
- if (devc->revision)
- timer_caps |= TMR_EXTERNAL | TMR_MODE_MIDI;
-
- if (devc->revision & 0x02)
- timer_caps |= TMR_MODE_CLS;
-
-
- if (devc->revision & 0x40)
- max_timebase = 10; /* Has the 216 and 240 ppqn modes */
- }
-
- timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps;
- return n;
-
-}
-
-EXPORT_SYMBOL(probe_mpu401);
-EXPORT_SYMBOL(attach_mpu401);
-EXPORT_SYMBOL(unload_mpu401);
-
-static struct address_info cfg;
-
-static int io = -1;
-static int irq = -1;
-
-module_param(irq, int, 0);
-module_param(io, int, 0);
-
-static int __init init_mpu401(void)
-{
- int ret;
- /* Can be loaded either for module use or to provide functions
- to others */
- if (io != -1 && irq != -1) {
- struct resource *ports;
- cfg.irq = irq;
- cfg.io_base = io;
- ports = request_region(io, 2, "mpu401");
- if (!ports)
- return -EBUSY;
- if (probe_mpu401(&cfg, ports) == 0) {
- release_region(io, 2);
- return -ENODEV;
- }
- if ((ret = attach_mpu401(&cfg, THIS_MODULE)))
- return ret;
- }
-
- return 0;
-}
-
-static void __exit cleanup_mpu401(void)
-{
- if (io != -1 && irq != -1) {
- /* Check for use by, for example, sscape driver */
- unload_mpu401(&cfg);
- }
-}
-
-module_init(init_mpu401);
-module_exit(cleanup_mpu401);
-
-#ifndef MODULE
-static int __init setup_mpu401(char *str)
-{
- /* io, irq */
- int ints[3];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
-
- return 1;
-}
-
-__setup("mpu401=", setup_mpu401);
-#endif
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/mpu401.h b/sound/oss/mpu401.h
deleted file mode 100644
index 0ad1e9ee74f7..000000000000
--- a/sound/oss/mpu401.h
+++ /dev/null
@@ -1,11 +0,0 @@
-
-/* From uart401.c */
-int probe_uart401 (struct address_info *hw_config, struct module *owner);
-void unload_uart401 (struct address_info *hw_config);
-
-irqreturn_t uart401intr (int irq, void *dev_id);
-
-/* From mpu401.c */
-int probe_mpu401(struct address_info *hw_config, struct resource *ports);
-int attach_mpu401(struct address_info * hw_config, struct module *owner);
-void unload_mpu401(struct address_info *hw_info);
diff --git a/sound/oss/msnd.c b/sound/oss/msnd.c
deleted file mode 100644
index c0cc951ba97d..000000000000
--- a/sound/oss/msnd.c
+++ /dev/null
@@ -1,413 +0,0 @@
-/*********************************************************************
- *
- * msnd.c - Driver Base
- *
- * Turtle Beach MultiSound Sound Card Driver for Linux
- *
- * Copyright (C) 1998 Andrew Veliath
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- ********************************************************************/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/vmalloc.h>
-#include <linux/types.h>
-#include <linux/delay.h>
-#include <linux/mm.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <linux/spinlock.h>
-#include <asm/irq.h>
-#include "msnd.h"
-
-#define LOGNAME "msnd"
-
-#define MSND_MAX_DEVS 4
-
-static multisound_dev_t *devs[MSND_MAX_DEVS];
-static int num_devs;
-
-int msnd_register(multisound_dev_t *dev)
-{
- int i;
-
- for (i = 0; i < MSND_MAX_DEVS; ++i)
- if (devs[i] == NULL)
- break;
-
- if (i == MSND_MAX_DEVS)
- return -ENOMEM;
-
- devs[i] = dev;
- ++num_devs;
- return 0;
-}
-
-void msnd_unregister(multisound_dev_t *dev)
-{
- int i;
-
- for (i = 0; i < MSND_MAX_DEVS; ++i)
- if (devs[i] == dev)
- break;
-
- if (i == MSND_MAX_DEVS) {
- printk(KERN_WARNING LOGNAME ": Unregistering unknown device\n");
- return;
- }
-
- devs[i] = NULL;
- --num_devs;
-}
-
-void msnd_init_queue(void __iomem *base, int start, int size)
-{
- writew(PCTODSP_BASED(start), base + JQS_wStart);
- writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize);
- writew(0, base + JQS_wHead);
- writew(0, base + JQS_wTail);
-}
-
-void msnd_fifo_init(msnd_fifo *f)
-{
- f->data = NULL;
-}
-
-void msnd_fifo_free(msnd_fifo *f)
-{
- vfree(f->data);
- f->data = NULL;
-}
-
-int msnd_fifo_alloc(msnd_fifo *f, size_t n)
-{
- msnd_fifo_free(f);
- f->data = vmalloc(n);
- f->n = n;
- f->tail = 0;
- f->head = 0;
- f->len = 0;
-
- if (!f->data)
- return -ENOMEM;
-
- return 0;
-}
-
-void msnd_fifo_make_empty(msnd_fifo *f)
-{
- f->len = f->tail = f->head = 0;
-}
-
-int msnd_fifo_write_io(msnd_fifo *f, char __iomem *buf, size_t len)
-{
- int count = 0;
-
- while ((count < len) && (f->len != f->n)) {
-
- int nwritten;
-
- if (f->head <= f->tail) {
- nwritten = len - count;
- if (nwritten > f->n - f->tail)
- nwritten = f->n - f->tail;
- }
- else {
- nwritten = f->head - f->tail;
- if (nwritten > len - count)
- nwritten = len - count;
- }
-
- memcpy_fromio(f->data + f->tail, buf, nwritten);
-
- count += nwritten;
- buf += nwritten;
- f->len += nwritten;
- f->tail += nwritten;
- f->tail %= f->n;
- }
-
- return count;
-}
-
-int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len)
-{
- int count = 0;
-
- while ((count < len) && (f->len != f->n)) {
-
- int nwritten;
-
- if (f->head <= f->tail) {
- nwritten = len - count;
- if (nwritten > f->n - f->tail)
- nwritten = f->n - f->tail;
- }
- else {
- nwritten = f->head - f->tail;
- if (nwritten > len - count)
- nwritten = len - count;
- }
-
- memcpy(f->data + f->tail, buf, nwritten);
-
- count += nwritten;
- buf += nwritten;
- f->len += nwritten;
- f->tail += nwritten;
- f->tail %= f->n;
- }
-
- return count;
-}
-
-int msnd_fifo_read_io(msnd_fifo *f, char __iomem *buf, size_t len)
-{
- int count = 0;
-
- while ((count < len) && (f->len > 0)) {
-
- int nread;
-
- if (f->tail <= f->head) {
- nread = len - count;
- if (nread > f->n - f->head)
- nread = f->n - f->head;
- }
- else {
- nread = f->tail - f->head;
- if (nread > len - count)
- nread = len - count;
- }
-
- memcpy_toio(buf, f->data + f->head, nread);
-
- count += nread;
- buf += nread;
- f->len -= nread;
- f->head += nread;
- f->head %= f->n;
- }
-
- return count;
-}
-
-int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len)
-{
- int count = 0;
-
- while ((count < len) && (f->len > 0)) {
-
- int nread;
-
- if (f->tail <= f->head) {
- nread = len - count;
- if (nread > f->n - f->head)
- nread = f->n - f->head;
- }
- else {
- nread = f->tail - f->head;
- if (nread > len - count)
- nread = len - count;
- }
-
- memcpy(buf, f->data + f->head, nread);
-
- count += nread;
- buf += nread;
- f->len -= nread;
- f->head += nread;
- f->head %= f->n;
- }
-
- return count;
-}
-
-static int msnd_wait_TXDE(multisound_dev_t *dev)
-{
- register unsigned int io = dev->io;
- register int timeout = 1000;
-
- while(timeout-- > 0)
- if (msnd_inb(io + HP_ISR) & HPISR_TXDE)
- return 0;
-
- return -EIO;
-}
-
-static int msnd_wait_HC0(multisound_dev_t *dev)
-{
- register unsigned int io = dev->io;
- register int timeout = 1000;
-
- while(timeout-- > 0)
- if (!(msnd_inb(io + HP_CVR) & HPCVR_HC))
- return 0;
-
- return -EIO;
-}
-
-int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&dev->lock, flags);
- if (msnd_wait_HC0(dev) == 0) {
- msnd_outb(cmd, dev->io + HP_CVR);
- spin_unlock_irqrestore(&dev->lock, flags);
- return 0;
- }
- spin_unlock_irqrestore(&dev->lock, flags);
-
- printk(KERN_DEBUG LOGNAME ": Send DSP command timeout\n");
-
- return -EIO;
-}
-
-int msnd_send_word(multisound_dev_t *dev, unsigned char high,
- unsigned char mid, unsigned char low)
-{
- register unsigned int io = dev->io;
-
- if (msnd_wait_TXDE(dev) == 0) {
- msnd_outb(high, io + HP_TXH);
- msnd_outb(mid, io + HP_TXM);
- msnd_outb(low, io + HP_TXL);
- return 0;
- }
-
- printk(KERN_DEBUG LOGNAME ": Send host word timeout\n");
-
- return -EIO;
-}
-
-int msnd_upload_host(multisound_dev_t *dev, char *bin, int len)
-{
- int i;
-
- if (len % 3 != 0) {
- printk(KERN_WARNING LOGNAME ": Upload host data not multiple of 3!\n");
- return -EINVAL;
- }
-
- for (i = 0; i < len; i += 3)
- if (msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2]) != 0)
- return -EIO;
-
- msnd_inb(dev->io + HP_RXL);
- msnd_inb(dev->io + HP_CVR);
-
- return 0;
-}
-
-int msnd_enable_irq(multisound_dev_t *dev)
-{
- unsigned long flags;
-
- if (dev->irq_ref++)
- return 0;
-
- printk(KERN_DEBUG LOGNAME ": Enabling IRQ\n");
-
- spin_lock_irqsave(&dev->lock, flags);
- if (msnd_wait_TXDE(dev) == 0) {
- msnd_outb(msnd_inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR);
- if (dev->type == msndClassic)
- msnd_outb(dev->irqid, dev->io + HP_IRQM);
- msnd_outb(msnd_inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR);
- msnd_outb(msnd_inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR);
- enable_irq(dev->irq);
- msnd_init_queue(dev->DSPQ, dev->dspq_data_buff, dev->dspq_buff_size);
- spin_unlock_irqrestore(&dev->lock, flags);
- return 0;
- }
- spin_unlock_irqrestore(&dev->lock, flags);
-
- printk(KERN_DEBUG LOGNAME ": Enable IRQ failed\n");
-
- return -EIO;
-}
-
-int msnd_disable_irq(multisound_dev_t *dev)
-{
- unsigned long flags;
-
- if (--dev->irq_ref > 0)
- return 0;
-
- if (dev->irq_ref < 0)
- printk(KERN_DEBUG LOGNAME ": IRQ ref count is %d\n", dev->irq_ref);
-
- printk(KERN_DEBUG LOGNAME ": Disabling IRQ\n");
-
- spin_lock_irqsave(&dev->lock, flags);
- if (msnd_wait_TXDE(dev) == 0) {
- msnd_outb(msnd_inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR);
- if (dev->type == msndClassic)
- msnd_outb(HPIRQ_NONE, dev->io + HP_IRQM);
- disable_irq(dev->irq);
- spin_unlock_irqrestore(&dev->lock, flags);
- return 0;
- }
- spin_unlock_irqrestore(&dev->lock, flags);
-
- printk(KERN_DEBUG LOGNAME ": Disable IRQ failed\n");
-
- return -EIO;
-}
-
-#ifndef LINUX20
-EXPORT_SYMBOL(msnd_register);
-EXPORT_SYMBOL(msnd_unregister);
-
-EXPORT_SYMBOL(msnd_init_queue);
-
-EXPORT_SYMBOL(msnd_fifo_init);
-EXPORT_SYMBOL(msnd_fifo_free);
-EXPORT_SYMBOL(msnd_fifo_alloc);
-EXPORT_SYMBOL(msnd_fifo_make_empty);
-EXPORT_SYMBOL(msnd_fifo_write_io);
-EXPORT_SYMBOL(msnd_fifo_read_io);
-EXPORT_SYMBOL(msnd_fifo_write);
-EXPORT_SYMBOL(msnd_fifo_read);
-
-EXPORT_SYMBOL(msnd_send_dsp_cmd);
-EXPORT_SYMBOL(msnd_send_word);
-EXPORT_SYMBOL(msnd_upload_host);
-
-EXPORT_SYMBOL(msnd_enable_irq);
-EXPORT_SYMBOL(msnd_disable_irq);
-#endif
-
-#ifdef MODULE
-MODULE_AUTHOR ("Andrew Veliath <andrewtv@usa.net>");
-MODULE_DESCRIPTION ("Turtle Beach MultiSound Driver Base");
-MODULE_LICENSE("GPL");
-
-
-int init_module(void)
-{
- return 0;
-}
-
-void cleanup_module(void)
-{
-}
-#endif
diff --git a/sound/oss/msnd.h b/sound/oss/msnd.h
deleted file mode 100644
index c8be47ec2b7e..000000000000
--- a/sound/oss/msnd.h
+++ /dev/null
@@ -1,278 +0,0 @@
-/*********************************************************************
- *
- * msnd.h
- *
- * Turtle Beach MultiSound Sound Card Driver for Linux
- *
- * Some parts of this header file were derived from the Turtle Beach
- * MultiSound Driver Development Kit.
- *
- * Copyright (C) 1998 Andrew Veliath
- * Copyright (C) 1993 Turtle Beach Systems, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- ********************************************************************/
-#ifndef __MSND_H
-#define __MSND_H
-
-#define VERSION "0.8.3.1"
-
-#define DEFSAMPLERATE DSP_DEFAULT_SPEED
-#define DEFSAMPLESIZE AFMT_U8
-#define DEFCHANNELS 1
-
-#define DEFFIFOSIZE 128
-
-#define SNDCARD_MSND 38
-
-#define SRAM_BANK_SIZE 0x8000
-#define SRAM_CNTL_START 0x7F00
-
-#define DSP_BASE_ADDR 0x4000
-#define DSP_BANK_BASE 0x4000
-
-#define HP_ICR 0x00
-#define HP_CVR 0x01
-#define HP_ISR 0x02
-#define HP_IVR 0x03
-#define HP_NU 0x04
-#define HP_INFO 0x04
-#define HP_TXH 0x05
-#define HP_RXH 0x05
-#define HP_TXM 0x06
-#define HP_RXM 0x06
-#define HP_TXL 0x07
-#define HP_RXL 0x07
-
-#define HP_ICR_DEF 0x00
-#define HP_CVR_DEF 0x12
-#define HP_ISR_DEF 0x06
-#define HP_IVR_DEF 0x0f
-#define HP_NU_DEF 0x00
-
-#define HP_IRQM 0x09
-
-#define HPR_BLRC 0x08
-#define HPR_SPR1 0x09
-#define HPR_SPR2 0x0A
-#define HPR_TCL0 0x0B
-#define HPR_TCL1 0x0C
-#define HPR_TCL2 0x0D
-#define HPR_TCL3 0x0E
-#define HPR_TCL4 0x0F
-
-#define HPICR_INIT 0x80
-#define HPICR_HM1 0x40
-#define HPICR_HM0 0x20
-#define HPICR_HF1 0x10
-#define HPICR_HF0 0x08
-#define HPICR_TREQ 0x02
-#define HPICR_RREQ 0x01
-
-#define HPCVR_HC 0x80
-
-#define HPISR_HREQ 0x80
-#define HPISR_DMA 0x40
-#define HPISR_HF3 0x10
-#define HPISR_HF2 0x08
-#define HPISR_TRDY 0x04
-#define HPISR_TXDE 0x02
-#define HPISR_RXDF 0x01
-
-#define HPIO_290 0
-#define HPIO_260 1
-#define HPIO_250 2
-#define HPIO_240 3
-#define HPIO_230 4
-#define HPIO_220 5
-#define HPIO_210 6
-#define HPIO_3E0 7
-
-#define HPMEM_NONE 0
-#define HPMEM_B000 1
-#define HPMEM_C800 2
-#define HPMEM_D000 3
-#define HPMEM_D400 4
-#define HPMEM_D800 5
-#define HPMEM_E000 6
-#define HPMEM_E800 7
-
-#define HPIRQ_NONE 0
-#define HPIRQ_5 1
-#define HPIRQ_7 2
-#define HPIRQ_9 3
-#define HPIRQ_10 4
-#define HPIRQ_11 5
-#define HPIRQ_12 6
-#define HPIRQ_15 7
-
-#define HIMT_PLAY_DONE 0x00
-#define HIMT_RECORD_DONE 0x01
-#define HIMT_MIDI_EOS 0x02
-#define HIMT_MIDI_OUT 0x03
-
-#define HIMT_MIDI_IN_UCHAR 0x0E
-#define HIMT_DSP 0x0F
-
-#define HDEX_BASE 0x92
-#define HDEX_PLAY_START (0 + HDEX_BASE)
-#define HDEX_PLAY_STOP (1 + HDEX_BASE)
-#define HDEX_PLAY_PAUSE (2 + HDEX_BASE)
-#define HDEX_PLAY_RESUME (3 + HDEX_BASE)
-#define HDEX_RECORD_START (4 + HDEX_BASE)
-#define HDEX_RECORD_STOP (5 + HDEX_BASE)
-#define HDEX_MIDI_IN_START (6 + HDEX_BASE)
-#define HDEX_MIDI_IN_STOP (7 + HDEX_BASE)
-#define HDEX_MIDI_OUT_START (8 + HDEX_BASE)
-#define HDEX_MIDI_OUT_STOP (9 + HDEX_BASE)
-#define HDEX_AUX_REQ (10 + HDEX_BASE)
-
-#define HIWORD(l) ((WORD)((((DWORD)(l)) >> 16) & 0xFFFF))
-#define LOWORD(l) ((WORD)(DWORD)(l))
-#define HIBYTE(w) ((BYTE)(((WORD)(w) >> 8) & 0xFF))
-#define LOBYTE(w) ((BYTE)(w))
-#define MAKELONG(low,hi) ((long)(((WORD)(low))|(((DWORD)((WORD)(hi)))<<16)))
-#define MAKEWORD(low,hi) ((WORD)(((BYTE)(low))|(((WORD)((BYTE)(hi)))<<8)))
-
-#define PCTODSP_OFFSET(w) (USHORT)((w)/2)
-#define PCTODSP_BASED(w) (USHORT)(((w)/2) + DSP_BASE_ADDR)
-#define DSPTOPC_BASED(w) (((w) - DSP_BASE_ADDR) * 2)
-
-#ifdef SLOWIO
-#define msnd_outb outb_p
-#define msnd_inb inb_p
-#else
-#define msnd_outb outb
-#define msnd_inb inb
-#endif
-
-/* JobQueueStruct */
-#define JQS_wStart 0x00
-#define JQS_wSize 0x02
-#define JQS_wHead 0x04
-#define JQS_wTail 0x06
-#define JQS__size 0x08
-
-/* DAQueueDataStruct */
-#define DAQDS_wStart 0x00
-#define DAQDS_wSize 0x02
-#define DAQDS_wFormat 0x04
-#define DAQDS_wSampleSize 0x06
-#define DAQDS_wChannels 0x08
-#define DAQDS_wSampleRate 0x0A
-#define DAQDS_wIntMsg 0x0C
-#define DAQDS_wFlags 0x0E
-#define DAQDS__size 0x10
-
-typedef u8 BYTE;
-typedef u16 USHORT;
-typedef u16 WORD;
-typedef u32 DWORD;
-typedef void __iomem * LPDAQD;
-
-/* Generic FIFO */
-typedef struct {
- size_t n, len;
- char *data;
- int head, tail;
-} msnd_fifo;
-
-typedef struct multisound_dev {
- /* Linux device info */
- char *name;
- int dsp_minor, mixer_minor;
- int ext_midi_dev, hdr_midi_dev;
-
- /* Hardware resources */
- int io, numio;
- int memid, irqid;
- int irq, irq_ref;
- unsigned char info;
- void __iomem *base;
-
- /* Motorola 56k DSP SMA */
- void __iomem *SMA;
- void __iomem *DAPQ, *DARQ, *MODQ, *MIDQ, *DSPQ;
- void __iomem *pwDSPQData, *pwMIDQData, *pwMODQData;
- int dspq_data_buff, dspq_buff_size;
-
- /* State variables */
- enum { msndClassic, msndPinnacle } type;
- fmode_t mode;
- unsigned long flags;
-#define F_RESETTING 0
-#define F_HAVEDIGITAL 1
-#define F_AUDIO_WRITE_INUSE 2
-#define F_WRITING 3
-#define F_WRITEBLOCK 4
-#define F_WRITEFLUSH 5
-#define F_AUDIO_READ_INUSE 6
-#define F_READING 7
-#define F_READBLOCK 8
-#define F_EXT_MIDI_INUSE 9
-#define F_HDR_MIDI_INUSE 10
-#define F_DISABLE_WRITE_NDELAY 11
- wait_queue_head_t writeblock;
- wait_queue_head_t readblock;
- wait_queue_head_t writeflush;
- spinlock_t lock;
- int nresets;
- unsigned long recsrc;
- int left_levels[32];
- int right_levels[32];
- int mixer_mod_count;
- int calibrate_signal;
- int play_sample_size, play_sample_rate, play_channels;
- int play_ndelay;
- int rec_sample_size, rec_sample_rate, rec_channels;
- int rec_ndelay;
- BYTE bCurrentMidiPatch;
-
- /* Digital audio FIFOs */
- msnd_fifo DAPF, DARF;
- int fifosize;
- int last_playbank, last_recbank;
-
- /* MIDI in callback */
- void (*midi_in_interrupt)(struct multisound_dev *);
-} multisound_dev_t;
-
-#ifndef mdelay
-# define mdelay(a) udelay((a) * 1000)
-#endif
-
-int msnd_register(multisound_dev_t *dev);
-void msnd_unregister(multisound_dev_t *dev);
-
-void msnd_init_queue(void __iomem *, int start, int size);
-
-void msnd_fifo_init(msnd_fifo *f);
-void msnd_fifo_free(msnd_fifo *f);
-int msnd_fifo_alloc(msnd_fifo *f, size_t n);
-void msnd_fifo_make_empty(msnd_fifo *f);
-int msnd_fifo_write_io(msnd_fifo *f, char __iomem *buf, size_t len);
-int msnd_fifo_read_io(msnd_fifo *f, char __iomem *buf, size_t len);
-int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len);
-int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len);
-
-int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd);
-int msnd_send_word(multisound_dev_t *dev, unsigned char high,
- unsigned char mid, unsigned char low);
-int msnd_upload_host(multisound_dev_t *dev, char *bin, int len);
-int msnd_enable_irq(multisound_dev_t *dev);
-int msnd_disable_irq(multisound_dev_t *dev);
-
-#endif /* __MSND_H */
diff --git a/sound/oss/msnd_classic.c b/sound/oss/msnd_classic.c
deleted file mode 100644
index 3b23a096fa4e..000000000000
--- a/sound/oss/msnd_classic.c
+++ /dev/null
@@ -1,3 +0,0 @@
-/* The work is in msnd_pinnacle.c, just define MSND_CLASSIC before it. */
-#define MSND_CLASSIC
-#include "msnd_pinnacle.c"
diff --git a/sound/oss/msnd_classic.h b/sound/oss/msnd_classic.h
deleted file mode 100644
index 1a17dde2f650..000000000000
--- a/sound/oss/msnd_classic.h
+++ /dev/null
@@ -1,185 +0,0 @@
-/*********************************************************************
- *
- * msnd_classic.h
- *
- * Turtle Beach MultiSound Sound Card Driver for Linux
- *
- * Some parts of this header file were derived from the Turtle Beach
- * MultiSound Driver Development Kit.
- *
- * Copyright (C) 1998 Andrew Veliath
- * Copyright (C) 1993 Turtle Beach Systems, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- ********************************************************************/
-#ifndef __MSND_CLASSIC_H
-#define __MSND_CLASSIC_H
-
-
-#define DSP_NUMIO 0x10
-
-#define HP_MEMM 0x08
-
-#define HP_BITM 0x0E
-#define HP_WAIT 0x0D
-#define HP_DSPR 0x0A
-#define HP_PROR 0x0B
-#define HP_BLKS 0x0C
-
-#define HPPRORESET_OFF 0
-#define HPPRORESET_ON 1
-
-#define HPDSPRESET_OFF 0
-#define HPDSPRESET_ON 1
-
-#define HPBLKSEL_0 0
-#define HPBLKSEL_1 1
-
-#define HPWAITSTATE_0 0
-#define HPWAITSTATE_1 1
-
-#define HPBITMODE_16 0
-#define HPBITMODE_8 1
-
-#define HIDSP_INT_PLAY_UNDER 0x00
-#define HIDSP_INT_RECORD_OVER 0x01
-#define HIDSP_INPUT_CLIPPING 0x02
-#define HIDSP_MIDI_IN_OVER 0x10
-#define HIDSP_MIDI_OVERRUN_ERR 0x13
-
-#define HDEXAR_CLEAR_PEAKS 1
-#define HDEXAR_IN_SET_POTS 2
-#define HDEXAR_AUX_SET_POTS 3
-#define HDEXAR_CAL_A_TO_D 4
-#define HDEXAR_RD_EXT_DSP_BITS 5
-
-#define TIME_PRO_RESET_DONE 0x028A
-#define TIME_PRO_SYSEX 0x0040
-#define TIME_PRO_RESET 0x0032
-
-#define AGND 0x01
-#define SIGNAL 0x02
-
-#define EXT_DSP_BIT_DCAL 0x0001
-#define EXT_DSP_BIT_MIDI_CON 0x0002
-
-#define BUFFSIZE 0x8000
-#define HOSTQ_SIZE 0x40
-
-#define SRAM_CNTL_START 0x7F00
-#define SMA_STRUCT_START 0x7F40
-
-#define DAP_BUFF_SIZE 0x2400
-#define DAR_BUFF_SIZE 0x2000
-
-#define DAPQ_STRUCT_SIZE 0x10
-#define DARQ_STRUCT_SIZE 0x10
-#define DAPQ_BUFF_SIZE (3 * 0x10)
-#define DARQ_BUFF_SIZE (3 * 0x10)
-#define MODQ_BUFF_SIZE 0x400
-#define MIDQ_BUFF_SIZE 0x200
-#define DSPQ_BUFF_SIZE 0x40
-
-#define DAPQ_DATA_BUFF 0x6C00
-#define DARQ_DATA_BUFF 0x6C30
-#define MODQ_DATA_BUFF 0x6C60
-#define MIDQ_DATA_BUFF 0x7060
-#define DSPQ_DATA_BUFF 0x7260
-
-#define DAPQ_OFFSET SRAM_CNTL_START
-#define DARQ_OFFSET (SRAM_CNTL_START + 0x08)
-#define MODQ_OFFSET (SRAM_CNTL_START + 0x10)
-#define MIDQ_OFFSET (SRAM_CNTL_START + 0x18)
-#define DSPQ_OFFSET (SRAM_CNTL_START + 0x20)
-
-#define MOP_SYNTH 0x10
-#define MOP_EXTOUT 0x32
-#define MOP_EXTTHRU 0x02
-#define MOP_OUTMASK 0x01
-
-#define MIP_EXTIN 0x01
-#define MIP_SYNTH 0x00
-#define MIP_INMASK 0x32
-
-/* Classic SMA Common Data */
-#define SMA_wCurrPlayBytes 0x0000
-#define SMA_wCurrRecordBytes 0x0002
-#define SMA_wCurrPlayVolLeft 0x0004
-#define SMA_wCurrPlayVolRight 0x0006
-#define SMA_wCurrInVolLeft 0x0008
-#define SMA_wCurrInVolRight 0x000a
-#define SMA_wUser_3 0x000c
-#define SMA_wUser_4 0x000e
-#define SMA_dwUser_5 0x0010
-#define SMA_dwUser_6 0x0014
-#define SMA_wUser_7 0x0018
-#define SMA_wReserved_A 0x001a
-#define SMA_wReserved_B 0x001c
-#define SMA_wReserved_C 0x001e
-#define SMA_wReserved_D 0x0020
-#define SMA_wReserved_E 0x0022
-#define SMA_wReserved_F 0x0024
-#define SMA_wReserved_G 0x0026
-#define SMA_wReserved_H 0x0028
-#define SMA_wCurrDSPStatusFlags 0x002a
-#define SMA_wCurrHostStatusFlags 0x002c
-#define SMA_wCurrInputTagBits 0x002e
-#define SMA_wCurrLeftPeak 0x0030
-#define SMA_wCurrRightPeak 0x0032
-#define SMA_wExtDSPbits 0x0034
-#define SMA_bExtHostbits 0x0036
-#define SMA_bBoardLevel 0x0037
-#define SMA_bInPotPosRight 0x0038
-#define SMA_bInPotPosLeft 0x0039
-#define SMA_bAuxPotPosRight 0x003a
-#define SMA_bAuxPotPosLeft 0x003b
-#define SMA_wCurrMastVolLeft 0x003c
-#define SMA_wCurrMastVolRight 0x003e
-#define SMA_bUser_12 0x0040
-#define SMA_bUser_13 0x0041
-#define SMA_wUser_14 0x0042
-#define SMA_wUser_15 0x0044
-#define SMA_wCalFreqAtoD 0x0046
-#define SMA_wUser_16 0x0048
-#define SMA_wUser_17 0x004a
-#define SMA__size 0x004c
-
-#ifdef HAVE_DSPCODEH
-# include "msndperm.c"
-# include "msndinit.c"
-# define PERMCODE msndperm
-# define INITCODE msndinit
-# define PERMCODESIZE sizeof(msndperm)
-# define INITCODESIZE sizeof(msndinit)
-#else
-# ifndef CONFIG_MSNDCLAS_INIT_FILE
-# define CONFIG_MSNDCLAS_INIT_FILE \
- "/etc/sound/msndinit.bin"
-# endif
-# ifndef CONFIG_MSNDCLAS_PERM_FILE
-# define CONFIG_MSNDCLAS_PERM_FILE \
- "/etc/sound/msndperm.bin"
-# endif
-# define PERMCODEFILE CONFIG_MSNDCLAS_PERM_FILE
-# define INITCODEFILE CONFIG_MSNDCLAS_INIT_FILE
-# define PERMCODE dspini
-# define INITCODE permini
-# define PERMCODESIZE sizeof_dspini
-# define INITCODESIZE sizeof_permini
-#endif
-#define LONGNAME "MultiSound (Classic/Monterey/Tahiti)"
-
-#endif /* __MSND_CLASSIC_H */
diff --git a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c
deleted file mode 100644
index 7b5c77b32a90..000000000000
--- a/sound/oss/msnd_pinnacle.c
+++ /dev/null
@@ -1,1931 +0,0 @@
-/*********************************************************************
- *
- * Turtle Beach MultiSound Sound Card Driver for Linux
- * Linux 2.0/2.2 Version
- *
- * msnd_pinnacle.c / msnd_classic.c
- *
- * -- If MSND_CLASSIC is defined:
- *
- * -> driver for Turtle Beach Classic/Monterey/Tahiti
- *
- * -- Else
- *
- * -> driver for Turtle Beach Pinnacle/Fiji
- *
- * Copyright (C) 1998 Andrew Veliath
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * 12-3-2000 Modified IO port validation Steve Sycamore
- *
- ********************************************************************/
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/mutex.h>
-#include <linux/gfp.h>
-#include <asm/irq.h>
-#include <asm/io.h>
-#include "sound_config.h"
-#include "sound_firmware.h"
-#ifdef MSND_CLASSIC
-# ifndef __alpha__
-# define SLOWIO
-# endif
-#endif
-#include "msnd.h"
-#ifdef MSND_CLASSIC
-# ifdef CONFIG_MSNDCLAS_HAVE_BOOT
-# define HAVE_DSPCODEH
-# endif
-# include "msnd_classic.h"
-# define LOGNAME "msnd_classic"
-#else
-# ifdef CONFIG_MSNDPIN_HAVE_BOOT
-# define HAVE_DSPCODEH
-# endif
-# include "msnd_pinnacle.h"
-# define LOGNAME "msnd_pinnacle"
-#endif
-
-#ifndef CONFIG_MSND_WRITE_NDELAY
-# define CONFIG_MSND_WRITE_NDELAY 1
-#endif
-
-#define get_play_delay_jiffies(size) ((size) * HZ * \
- dev.play_sample_size / 8 / \
- dev.play_sample_rate / \
- dev.play_channels)
-
-#define get_rec_delay_jiffies(size) ((size) * HZ * \
- dev.rec_sample_size / 8 / \
- dev.rec_sample_rate / \
- dev.rec_channels)
-
-static DEFINE_MUTEX(msnd_pinnacle_mutex);
-static multisound_dev_t dev;
-
-#ifndef HAVE_DSPCODEH
-static char *dspini, *permini;
-static int sizeof_dspini, sizeof_permini;
-#endif
-
-static int dsp_full_reset(void);
-static void dsp_write_flush(void);
-
-static __inline__ int chk_send_dsp_cmd(multisound_dev_t *dev, register BYTE cmd)
-{
- if (msnd_send_dsp_cmd(dev, cmd) == 0)
- return 0;
- dsp_full_reset();
- return msnd_send_dsp_cmd(dev, cmd);
-}
-
-static void reset_play_queue(void)
-{
- int n;
- LPDAQD lpDAQ;
-
- dev.last_playbank = -1;
- writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wHead);
- writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wTail);
-
- for (n = 0, lpDAQ = dev.base + DAPQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) {
- writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), lpDAQ + DAQDS_wStart);
- writew(0, lpDAQ + DAQDS_wSize);
- writew(1, lpDAQ + DAQDS_wFormat);
- writew(dev.play_sample_size, lpDAQ + DAQDS_wSampleSize);
- writew(dev.play_channels, lpDAQ + DAQDS_wChannels);
- writew(dev.play_sample_rate, lpDAQ + DAQDS_wSampleRate);
- writew(HIMT_PLAY_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg);
- writew(n, lpDAQ + DAQDS_wFlags);
- }
-}
-
-static void reset_record_queue(void)
-{
- int n;
- LPDAQD lpDAQ;
- unsigned long flags;
-
- dev.last_recbank = 2;
- writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DARQ + JQS_wHead);
- writew(PCTODSP_OFFSET(dev.last_recbank * DAQDS__size), dev.DARQ + JQS_wTail);
-
- /* Critical section: bank 1 access */
- spin_lock_irqsave(&dev.lock, flags);
- msnd_outb(HPBLKSEL_1, dev.io + HP_BLKS);
- memset_io(dev.base, 0, DAR_BUFF_SIZE * 3);
- msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS);
- spin_unlock_irqrestore(&dev.lock, flags);
-
- for (n = 0, lpDAQ = dev.base + DARQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) {
- writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, lpDAQ + DAQDS_wStart);
- writew(DAR_BUFF_SIZE, lpDAQ + DAQDS_wSize);
- writew(1, lpDAQ + DAQDS_wFormat);
- writew(dev.rec_sample_size, lpDAQ + DAQDS_wSampleSize);
- writew(dev.rec_channels, lpDAQ + DAQDS_wChannels);
- writew(dev.rec_sample_rate, lpDAQ + DAQDS_wSampleRate);
- writew(HIMT_RECORD_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg);
- writew(n, lpDAQ + DAQDS_wFlags);
- }
-}
-
-static void reset_queues(void)
-{
- if (dev.mode & FMODE_WRITE) {
- msnd_fifo_make_empty(&dev.DAPF);
- reset_play_queue();
- }
- if (dev.mode & FMODE_READ) {
- msnd_fifo_make_empty(&dev.DARF);
- reset_record_queue();
- }
-}
-
-static int dsp_set_format(struct file *file, int val)
-{
- int data, i;
- LPDAQD lpDAQ, lpDARQ;
-
- lpDAQ = dev.base + DAPQ_DATA_BUFF;
- lpDARQ = dev.base + DARQ_DATA_BUFF;
-
- switch (val) {
- case AFMT_U8:
- case AFMT_S16_LE:
- data = val;
- break;
- default:
- data = DEFSAMPLESIZE;
- break;
- }
-
- for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
- if (file->f_mode & FMODE_WRITE)
- writew(data, lpDAQ + DAQDS_wSampleSize);
- if (file->f_mode & FMODE_READ)
- writew(data, lpDARQ + DAQDS_wSampleSize);
- }
- if (file->f_mode & FMODE_WRITE)
- dev.play_sample_size = data;
- if (file->f_mode & FMODE_READ)
- dev.rec_sample_size = data;
-
- return data;
-}
-
-static int dsp_get_frag_size(void)
-{
- int size;
- size = dev.fifosize / 4;
- if (size > 32 * 1024)
- size = 32 * 1024;
- return size;
-}
-
-static int dsp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- int val, i, data, tmp;
- LPDAQD lpDAQ, lpDARQ;
- audio_buf_info abinfo;
- unsigned long flags;
- int __user *p = (int __user *)arg;
-
- lpDAQ = dev.base + DAPQ_DATA_BUFF;
- lpDARQ = dev.base + DARQ_DATA_BUFF;
-
- switch (cmd) {
- case SNDCTL_DSP_SUBDIVIDE:
- case SNDCTL_DSP_SETFRAGMENT:
- case SNDCTL_DSP_SETDUPLEX:
- case SNDCTL_DSP_POST:
- return 0;
-
- case SNDCTL_DSP_GETIPTR:
- case SNDCTL_DSP_GETOPTR:
- case SNDCTL_DSP_MAPINBUF:
- case SNDCTL_DSP_MAPOUTBUF:
- return -EINVAL;
-
- case SNDCTL_DSP_GETOSPACE:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- spin_lock_irqsave(&dev.lock, flags);
- abinfo.fragsize = dsp_get_frag_size();
- abinfo.bytes = dev.DAPF.n - dev.DAPF.len;
- abinfo.fragstotal = dev.DAPF.n / abinfo.fragsize;
- abinfo.fragments = abinfo.bytes / abinfo.fragsize;
- spin_unlock_irqrestore(&dev.lock, flags);
- return copy_to_user((void __user *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_GETISPACE:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- spin_lock_irqsave(&dev.lock, flags);
- abinfo.fragsize = dsp_get_frag_size();
- abinfo.bytes = dev.DARF.n - dev.DARF.len;
- abinfo.fragstotal = dev.DARF.n / abinfo.fragsize;
- abinfo.fragments = abinfo.bytes / abinfo.fragsize;
- spin_unlock_irqrestore(&dev.lock, flags);
- return copy_to_user((void __user *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_RESET:
- dev.nresets = 0;
- reset_queues();
- return 0;
-
- case SNDCTL_DSP_SYNC:
- dsp_write_flush();
- return 0;
-
- case SNDCTL_DSP_GETBLKSIZE:
- tmp = dsp_get_frag_size();
- if (put_user(tmp, p))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_GETFMTS:
- val = AFMT_S16_LE | AFMT_U8;
- if (put_user(val, p))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_SETFMT:
- if (get_user(val, p))
- return -EFAULT;
-
- if (file->f_mode & FMODE_WRITE)
- data = val == AFMT_QUERY
- ? dev.play_sample_size
- : dsp_set_format(file, val);
- else
- data = val == AFMT_QUERY
- ? dev.rec_sample_size
- : dsp_set_format(file, val);
-
- if (put_user(data, p))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_NONBLOCK:
- if (!test_bit(F_DISABLE_WRITE_NDELAY, &dev.flags) &&
- file->f_mode & FMODE_WRITE)
- dev.play_ndelay = 1;
- if (file->f_mode & FMODE_READ)
- dev.rec_ndelay = 1;
- return 0;
-
- case SNDCTL_DSP_GETCAPS:
- val = DSP_CAP_DUPLEX | DSP_CAP_BATCH;
- if (put_user(val, p))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_SPEED:
- if (get_user(val, p))
- return -EFAULT;
-
- if (val < 8000)
- val = 8000;
-
- if (val > 48000)
- val = 48000;
-
- data = val;
-
- for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
- if (file->f_mode & FMODE_WRITE)
- writew(data, lpDAQ + DAQDS_wSampleRate);
- if (file->f_mode & FMODE_READ)
- writew(data, lpDARQ + DAQDS_wSampleRate);
- }
- if (file->f_mode & FMODE_WRITE)
- dev.play_sample_rate = data;
- if (file->f_mode & FMODE_READ)
- dev.rec_sample_rate = data;
-
- if (put_user(data, p))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_CHANNELS:
- case SNDCTL_DSP_STEREO:
- if (get_user(val, p))
- return -EFAULT;
-
- if (cmd == SNDCTL_DSP_CHANNELS) {
- switch (val) {
- case 1:
- case 2:
- data = val;
- break;
- default:
- val = data = 2;
- break;
- }
- } else {
- switch (val) {
- case 0:
- data = 1;
- break;
- default:
- val = 1;
- case 1:
- data = 2;
- break;
- }
- }
-
- for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) {
- if (file->f_mode & FMODE_WRITE)
- writew(data, lpDAQ + DAQDS_wChannels);
- if (file->f_mode & FMODE_READ)
- writew(data, lpDARQ + DAQDS_wChannels);
- }
- if (file->f_mode & FMODE_WRITE)
- dev.play_channels = data;
- if (file->f_mode & FMODE_READ)
- dev.rec_channels = data;
-
- if (put_user(val, p))
- return -EFAULT;
- return 0;
- }
-
- return -EINVAL;
-}
-
-static int mixer_get(int d)
-{
- if (d > 31)
- return -EINVAL;
-
- switch (d) {
- case SOUND_MIXER_VOLUME:
- case SOUND_MIXER_PCM:
- case SOUND_MIXER_LINE:
- case SOUND_MIXER_IMIX:
- case SOUND_MIXER_LINE1:
-#ifndef MSND_CLASSIC
- case SOUND_MIXER_MIC:
- case SOUND_MIXER_SYNTH:
-#endif
- return (dev.left_levels[d] >> 8) * 100 / 0xff |
- (((dev.right_levels[d] >> 8) * 100 / 0xff) << 8);
- default:
- return 0;
- }
-}
-
-#define update_volm(a,b) \
- writew((dev.left_levels[a] >> 1) * \
- readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff, \
- dev.SMA + SMA_##b##Left); \
- writew((dev.right_levels[a] >> 1) * \
- readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff, \
- dev.SMA + SMA_##b##Right);
-
-#define update_potm(d,s,ar) \
- writeb((dev.left_levels[d] >> 8) * \
- readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff, \
- dev.SMA + SMA_##s##Left); \
- writeb((dev.right_levels[d] >> 8) * \
- readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff, \
- dev.SMA + SMA_##s##Right); \
- if (msnd_send_word(&dev, 0, 0, ar) == 0) \
- chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
-
-#define update_pot(d,s,ar) \
- writeb(dev.left_levels[d] >> 8, \
- dev.SMA + SMA_##s##Left); \
- writeb(dev.right_levels[d] >> 8, \
- dev.SMA + SMA_##s##Right); \
- if (msnd_send_word(&dev, 0, 0, ar) == 0) \
- chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
-
-static int mixer_set(int d, int value)
-{
- int left = value & 0x000000ff;
- int right = (value & 0x0000ff00) >> 8;
- int bLeft, bRight;
- int wLeft, wRight;
- int updatemaster = 0;
-
- if (d > 31)
- return -EINVAL;
-
- bLeft = left * 0xff / 100;
- wLeft = left * 0xffff / 100;
-
- bRight = right * 0xff / 100;
- wRight = right * 0xffff / 100;
-
- dev.left_levels[d] = wLeft;
- dev.right_levels[d] = wRight;
-
- switch (d) {
- /* master volume unscaled controls */
- case SOUND_MIXER_LINE: /* line pot control */
- /* scaled by IMIX in digital mix */
- writeb(bLeft, dev.SMA + SMA_bInPotPosLeft);
- writeb(bRight, dev.SMA + SMA_bInPotPosRight);
- if (msnd_send_word(&dev, 0, 0, HDEXAR_IN_SET_POTS) == 0)
- chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
- break;
-#ifndef MSND_CLASSIC
- case SOUND_MIXER_MIC: /* mic pot control */
- /* scaled by IMIX in digital mix */
- writeb(bLeft, dev.SMA + SMA_bMicPotPosLeft);
- writeb(bRight, dev.SMA + SMA_bMicPotPosRight);
- if (msnd_send_word(&dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0)
- chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
- break;
-#endif
- case SOUND_MIXER_VOLUME: /* master volume */
- writew(wLeft, dev.SMA + SMA_wCurrMastVolLeft);
- writew(wRight, dev.SMA + SMA_wCurrMastVolRight);
- /* fall through */
-
- case SOUND_MIXER_LINE1: /* aux pot control */
- /* scaled by master volume */
- /* fall through */
-
- /* digital controls */
- case SOUND_MIXER_SYNTH: /* synth vol (dsp mix) */
- case SOUND_MIXER_PCM: /* pcm vol (dsp mix) */
- case SOUND_MIXER_IMIX: /* input monitor (dsp mix) */
- /* scaled by master volume */
- updatemaster = 1;
- break;
-
- default:
- return 0;
- }
-
- if (updatemaster) {
- /* update master volume scaled controls */
- update_volm(SOUND_MIXER_PCM, wCurrPlayVol);
- update_volm(SOUND_MIXER_IMIX, wCurrInVol);
-#ifndef MSND_CLASSIC
- update_volm(SOUND_MIXER_SYNTH, wCurrMHdrVol);
-#endif
- update_potm(SOUND_MIXER_LINE1, bAuxPotPos, HDEXAR_AUX_SET_POTS);
- }
-
- return mixer_get(d);
-}
-
-static void mixer_setup(void)
-{
- update_pot(SOUND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS);
- update_potm(SOUND_MIXER_LINE1, bAuxPotPos, HDEXAR_AUX_SET_POTS);
- update_volm(SOUND_MIXER_PCM, wCurrPlayVol);
- update_volm(SOUND_MIXER_IMIX, wCurrInVol);
-#ifndef MSND_CLASSIC
- update_pot(SOUND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS);
- update_volm(SOUND_MIXER_SYNTH, wCurrMHdrVol);
-#endif
-}
-
-static unsigned long set_recsrc(unsigned long recsrc)
-{
- if (dev.recsrc == recsrc)
- return dev.recsrc;
-#ifdef HAVE_NORECSRC
- else if (recsrc == 0)
- dev.recsrc = 0;
-#endif
- else
- dev.recsrc ^= recsrc;
-
-#ifndef MSND_CLASSIC
- if (dev.recsrc & SOUND_MASK_IMIX) {
- if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0)
- chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
- }
- else if (dev.recsrc & SOUND_MASK_SYNTH) {
- if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_SYNTH_IN) == 0)
- chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
- }
- else if ((dev.recsrc & SOUND_MASK_DIGITAL1) && test_bit(F_HAVEDIGITAL, &dev.flags)) {
- if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_DAT_IN) == 0)
- chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
- }
- else {
-#ifdef HAVE_NORECSRC
- /* Select no input (?) */
- dev.recsrc = 0;
-#else
- dev.recsrc = SOUND_MASK_IMIX;
- if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0)
- chk_send_dsp_cmd(&dev, HDEX_AUX_REQ);
-#endif
- }
-#endif /* MSND_CLASSIC */
-
- return dev.recsrc;
-}
-
-static unsigned long force_recsrc(unsigned long recsrc)
-{
- dev.recsrc = 0;
- return set_recsrc(recsrc);
-}
-
-#define set_mixer_info() \
- memset(&info, 0, sizeof(info)); \
- strlcpy(info.id, "MSNDMIXER", sizeof(info.id)); \
- strlcpy(info.name, "MultiSound Mixer", sizeof(info.name));
-
-static int mixer_ioctl(unsigned int cmd, unsigned long arg)
-{
- if (cmd == SOUND_MIXER_INFO) {
- mixer_info info;
- set_mixer_info();
- info.modify_counter = dev.mixer_mod_count;
- if (copy_to_user((void __user *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- } else if (cmd == SOUND_OLD_MIXER_INFO) {
- _old_mixer_info info;
- set_mixer_info();
- if (copy_to_user((void __user *)arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- } else if (cmd == SOUND_MIXER_PRIVATE1) {
- dev.nresets = 0;
- dsp_full_reset();
- return 0;
- } else if (((cmd >> 8) & 0xff) == 'M') {
- int val = 0;
-
- if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
- switch (cmd & 0xff) {
- case SOUND_MIXER_RECSRC:
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
- val = set_recsrc(val);
- break;
-
- default:
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
- val = mixer_set(cmd & 0xff, val);
- break;
- }
- ++dev.mixer_mod_count;
- return put_user(val, (int __user *)arg);
- } else {
- switch (cmd & 0xff) {
- case SOUND_MIXER_RECSRC:
- val = dev.recsrc;
- break;
-
- case SOUND_MIXER_DEVMASK:
- case SOUND_MIXER_STEREODEVS:
- val = SOUND_MASK_PCM |
- SOUND_MASK_LINE |
- SOUND_MASK_IMIX |
- SOUND_MASK_LINE1 |
-#ifndef MSND_CLASSIC
- SOUND_MASK_MIC |
- SOUND_MASK_SYNTH |
-#endif
- SOUND_MASK_VOLUME;
- break;
-
- case SOUND_MIXER_RECMASK:
-#ifdef MSND_CLASSIC
- val = 0;
-#else
- val = SOUND_MASK_IMIX |
- SOUND_MASK_SYNTH;
- if (test_bit(F_HAVEDIGITAL, &dev.flags))
- val |= SOUND_MASK_DIGITAL1;
-#endif
- break;
-
- case SOUND_MIXER_CAPS:
- val = SOUND_CAP_EXCL_INPUT;
- break;
-
- default:
- if ((val = mixer_get(cmd & 0xff)) < 0)
- return -EINVAL;
- break;
- }
- }
-
- return put_user(val, (int __user *)arg);
- }
-
- return -EINVAL;
-}
-
-static long dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- int minor = iminor(file->f_path.dentry->d_inode);
- int ret;
-
- if (cmd == OSS_GETVERSION) {
- int sound_version = SOUND_VERSION;
- return put_user(sound_version, (int __user *)arg);
- }
-
- ret = -EINVAL;
-
- mutex_lock(&msnd_pinnacle_mutex);
- if (minor == dev.dsp_minor)
- ret = dsp_ioctl(file, cmd, arg);
- else if (minor == dev.mixer_minor)
- ret = mixer_ioctl(cmd, arg);
- mutex_unlock(&msnd_pinnacle_mutex);
-
- return ret;
-}
-
-static void dsp_write_flush(void)
-{
- if (!(dev.mode & FMODE_WRITE) || !test_bit(F_WRITING, &dev.flags))
- return;
- set_bit(F_WRITEFLUSH, &dev.flags);
- interruptible_sleep_on_timeout(
- &dev.writeflush,
- get_play_delay_jiffies(dev.DAPF.len));
- clear_bit(F_WRITEFLUSH, &dev.flags);
- if (!signal_pending(current)) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(get_play_delay_jiffies(DAP_BUFF_SIZE));
- }
- clear_bit(F_WRITING, &dev.flags);
-}
-
-static void dsp_halt(struct file *file)
-{
- if ((file ? file->f_mode : dev.mode) & FMODE_READ) {
- clear_bit(F_READING, &dev.flags);
- chk_send_dsp_cmd(&dev, HDEX_RECORD_STOP);
- msnd_disable_irq(&dev);
- if (file) {
- printk(KERN_DEBUG LOGNAME ": Stopping read for %p\n", file);
- dev.mode &= ~FMODE_READ;
- }
- clear_bit(F_AUDIO_READ_INUSE, &dev.flags);
- }
- if ((file ? file->f_mode : dev.mode) & FMODE_WRITE) {
- if (test_bit(F_WRITING, &dev.flags)) {
- dsp_write_flush();
- chk_send_dsp_cmd(&dev, HDEX_PLAY_STOP);
- }
- msnd_disable_irq(&dev);
- if (file) {
- printk(KERN_DEBUG LOGNAME ": Stopping write for %p\n", file);
- dev.mode &= ~FMODE_WRITE;
- }
- clear_bit(F_AUDIO_WRITE_INUSE, &dev.flags);
- }
-}
-
-static int dsp_release(struct file *file)
-{
- dsp_halt(file);
- return 0;
-}
-
-static int dsp_open(struct file *file)
-{
- if ((file ? file->f_mode : dev.mode) & FMODE_WRITE) {
- set_bit(F_AUDIO_WRITE_INUSE, &dev.flags);
- clear_bit(F_WRITING, &dev.flags);
- msnd_fifo_make_empty(&dev.DAPF);
- reset_play_queue();
- if (file) {
- printk(KERN_DEBUG LOGNAME ": Starting write for %p\n", file);
- dev.mode |= FMODE_WRITE;
- }
- msnd_enable_irq(&dev);
- }
- if ((file ? file->f_mode : dev.mode) & FMODE_READ) {
- set_bit(F_AUDIO_READ_INUSE, &dev.flags);
- clear_bit(F_READING, &dev.flags);
- msnd_fifo_make_empty(&dev.DARF);
- reset_record_queue();
- if (file) {
- printk(KERN_DEBUG LOGNAME ": Starting read for %p\n", file);
- dev.mode |= FMODE_READ;
- }
- msnd_enable_irq(&dev);
- }
- return 0;
-}
-
-static void set_default_play_audio_parameters(void)
-{
- dev.play_sample_size = DEFSAMPLESIZE;
- dev.play_sample_rate = DEFSAMPLERATE;
- dev.play_channels = DEFCHANNELS;
-}
-
-static void set_default_rec_audio_parameters(void)
-{
- dev.rec_sample_size = DEFSAMPLESIZE;
- dev.rec_sample_rate = DEFSAMPLERATE;
- dev.rec_channels = DEFCHANNELS;
-}
-
-static void set_default_audio_parameters(void)
-{
- set_default_play_audio_parameters();
- set_default_rec_audio_parameters();
-}
-
-static int dev_open(struct inode *inode, struct file *file)
-{
- int minor = iminor(inode);
- int err = 0;
-
- mutex_lock(&msnd_pinnacle_mutex);
- if (minor == dev.dsp_minor) {
- if ((file->f_mode & FMODE_WRITE &&
- test_bit(F_AUDIO_WRITE_INUSE, &dev.flags)) ||
- (file->f_mode & FMODE_READ &&
- test_bit(F_AUDIO_READ_INUSE, &dev.flags))) {
- err = -EBUSY;
- goto out;
- }
-
- if ((err = dsp_open(file)) >= 0) {
- dev.nresets = 0;
- if (file->f_mode & FMODE_WRITE) {
- set_default_play_audio_parameters();
- if (!test_bit(F_DISABLE_WRITE_NDELAY, &dev.flags))
- dev.play_ndelay = (file->f_flags & O_NDELAY) ? 1 : 0;
- else
- dev.play_ndelay = 0;
- }
- if (file->f_mode & FMODE_READ) {
- set_default_rec_audio_parameters();
- dev.rec_ndelay = (file->f_flags & O_NDELAY) ? 1 : 0;
- }
- }
- }
- else if (minor == dev.mixer_minor) {
- /* nothing */
- } else
- err = -EINVAL;
-out:
- mutex_unlock(&msnd_pinnacle_mutex);
- return err;
-}
-
-static int dev_release(struct inode *inode, struct file *file)
-{
- int minor = iminor(inode);
- int err = 0;
-
- mutex_lock(&msnd_pinnacle_mutex);
- if (minor == dev.dsp_minor)
- err = dsp_release(file);
- else if (minor == dev.mixer_minor) {
- /* nothing */
- } else
- err = -EINVAL;
- mutex_unlock(&msnd_pinnacle_mutex);
- return err;
-}
-
-static __inline__ int pack_DARQ_to_DARF(register int bank)
-{
- register int size, timeout = 3;
- register WORD wTmp;
- LPDAQD DAQD;
-
- /* Increment the tail and check for queue wrap */
- wTmp = readw(dev.DARQ + JQS_wTail) + PCTODSP_OFFSET(DAQDS__size);
- if (wTmp > readw(dev.DARQ + JQS_wSize))
- wTmp = 0;
- while (wTmp == readw(dev.DARQ + JQS_wHead) && timeout--)
- udelay(1);
- writew(wTmp, dev.DARQ + JQS_wTail);
-
- /* Get our digital audio queue struct */
- DAQD = bank * DAQDS__size + dev.base + DARQ_DATA_BUFF;
-
- /* Get length of data */
- size = readw(DAQD + DAQDS_wSize);
-
- /* Read data from the head (unprotected bank 1 access okay
- since this is only called inside an interrupt) */
- msnd_outb(HPBLKSEL_1, dev.io + HP_BLKS);
- msnd_fifo_write_io(
- &dev.DARF,
- dev.base + bank * DAR_BUFF_SIZE,
- size);
- msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS);
-
- return 1;
-}
-
-static __inline__ int pack_DAPF_to_DAPQ(register int start)
-{
- register WORD DAPQ_tail;
- register int protect = start, nbanks = 0;
- LPDAQD DAQD;
-
- DAPQ_tail = readw(dev.DAPQ + JQS_wTail);
- while (DAPQ_tail != readw(dev.DAPQ + JQS_wHead) || start) {
- register int bank_num = DAPQ_tail / PCTODSP_OFFSET(DAQDS__size);
- register int n;
- unsigned long flags;
-
- /* Write the data to the new tail */
- if (protect) {
- /* Critical section: protect fifo in non-interrupt */
- spin_lock_irqsave(&dev.lock, flags);
- n = msnd_fifo_read_io(
- &dev.DAPF,
- dev.base + bank_num * DAP_BUFF_SIZE,
- DAP_BUFF_SIZE);
- spin_unlock_irqrestore(&dev.lock, flags);
- } else {
- n = msnd_fifo_read_io(
- &dev.DAPF,
- dev.base + bank_num * DAP_BUFF_SIZE,
- DAP_BUFF_SIZE);
- }
- if (!n)
- break;
-
- if (start)
- start = 0;
-
- /* Get our digital audio queue struct */
- DAQD = bank_num * DAQDS__size + dev.base + DAPQ_DATA_BUFF;
-
- /* Write size of this bank */
- writew(n, DAQD + DAQDS_wSize);
- ++nbanks;
-
- /* Then advance the tail */
- DAPQ_tail = (++bank_num % 3) * PCTODSP_OFFSET(DAQDS__size);
- writew(DAPQ_tail, dev.DAPQ + JQS_wTail);
- /* Tell the DSP to play the bank */
- msnd_send_dsp_cmd(&dev, HDEX_PLAY_START);
- }
- return nbanks;
-}
-
-static int dsp_read(char __user *buf, size_t len)
-{
- int count = len;
- char *page = (char *)__get_free_page(GFP_KERNEL);
-
- if (!page)
- return -ENOMEM;
-
- while (count > 0) {
- int n, k;
- unsigned long flags;
-
- k = PAGE_SIZE;
- if (k > count)
- k = count;
-
- /* Critical section: protect fifo in non-interrupt */
- spin_lock_irqsave(&dev.lock, flags);
- n = msnd_fifo_read(&dev.DARF, page, k);
- spin_unlock_irqrestore(&dev.lock, flags);
- if (copy_to_user(buf, page, n)) {
- free_page((unsigned long)page);
- return -EFAULT;
- }
- buf += n;
- count -= n;
-
- if (n == k && count)
- continue;
-
- if (!test_bit(F_READING, &dev.flags) && dev.mode & FMODE_READ) {
- dev.last_recbank = -1;
- if (chk_send_dsp_cmd(&dev, HDEX_RECORD_START) == 0)
- set_bit(F_READING, &dev.flags);
- }
-
- if (dev.rec_ndelay) {
- free_page((unsigned long)page);
- return count == len ? -EAGAIN : len - count;
- }
-
- if (count > 0) {
- set_bit(F_READBLOCK, &dev.flags);
- if (!interruptible_sleep_on_timeout(
- &dev.readblock,
- get_rec_delay_jiffies(DAR_BUFF_SIZE)))
- clear_bit(F_READING, &dev.flags);
- clear_bit(F_READBLOCK, &dev.flags);
- if (signal_pending(current)) {
- free_page((unsigned long)page);
- return -EINTR;
- }
- }
- }
- free_page((unsigned long)page);
- return len - count;
-}
-
-static int dsp_write(const char __user *buf, size_t len)
-{
- int count = len;
- char *page = (char *)__get_free_page(GFP_KERNEL);
-
- if (!page)
- return -ENOMEM;
-
- while (count > 0) {
- int n, k;
- unsigned long flags;
-
- k = PAGE_SIZE;
- if (k > count)
- k = count;
-
- if (copy_from_user(page, buf, k)) {
- free_page((unsigned long)page);
- return -EFAULT;
- }
-
- /* Critical section: protect fifo in non-interrupt */
- spin_lock_irqsave(&dev.lock, flags);
- n = msnd_fifo_write(&dev.DAPF, page, k);
- spin_unlock_irqrestore(&dev.lock, flags);
- buf += n;
- count -= n;
-
- if (count && n == k)
- continue;
-
- if (!test_bit(F_WRITING, &dev.flags) && (dev.mode & FMODE_WRITE)) {
- dev.last_playbank = -1;
- if (pack_DAPF_to_DAPQ(1) > 0)
- set_bit(F_WRITING, &dev.flags);
- }
-
- if (dev.play_ndelay) {
- free_page((unsigned long)page);
- return count == len ? -EAGAIN : len - count;
- }
-
- if (count > 0) {
- set_bit(F_WRITEBLOCK, &dev.flags);
- interruptible_sleep_on_timeout(
- &dev.writeblock,
- get_play_delay_jiffies(DAP_BUFF_SIZE));
- clear_bit(F_WRITEBLOCK, &dev.flags);
- if (signal_pending(current)) {
- free_page((unsigned long)page);
- return -EINTR;
- }
- }
- }
-
- free_page((unsigned long)page);
- return len - count;
-}
-
-static ssize_t dev_read(struct file *file, char __user *buf, size_t count, loff_t *off)
-{
- int minor = iminor(file->f_path.dentry->d_inode);
- if (minor == dev.dsp_minor)
- return dsp_read(buf, count);
- else
- return -EINVAL;
-}
-
-static ssize_t dev_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
-{
- int minor = iminor(file->f_path.dentry->d_inode);
- if (minor == dev.dsp_minor)
- return dsp_write(buf, count);
- else
- return -EINVAL;
-}
-
-static __inline__ void eval_dsp_msg(register WORD wMessage)
-{
- switch (HIBYTE(wMessage)) {
- case HIMT_PLAY_DONE:
- if (dev.last_playbank == LOBYTE(wMessage) || !test_bit(F_WRITING, &dev.flags))
- break;
- dev.last_playbank = LOBYTE(wMessage);
-
- if (pack_DAPF_to_DAPQ(0) <= 0) {
- if (!test_bit(F_WRITEBLOCK, &dev.flags)) {
- if (test_and_clear_bit(F_WRITEFLUSH, &dev.flags))
- wake_up_interruptible(&dev.writeflush);
- }
- clear_bit(F_WRITING, &dev.flags);
- }
-
- if (test_bit(F_WRITEBLOCK, &dev.flags))
- wake_up_interruptible(&dev.writeblock);
- break;
-
- case HIMT_RECORD_DONE:
- if (dev.last_recbank == LOBYTE(wMessage))
- break;
- dev.last_recbank = LOBYTE(wMessage);
-
- pack_DARQ_to_DARF(dev.last_recbank);
-
- if (test_bit(F_READBLOCK, &dev.flags))
- wake_up_interruptible(&dev.readblock);
- break;
-
- case HIMT_DSP:
- switch (LOBYTE(wMessage)) {
-#ifndef MSND_CLASSIC
- case HIDSP_PLAY_UNDER:
-#endif
- case HIDSP_INT_PLAY_UNDER:
-/* printk(KERN_DEBUG LOGNAME ": Play underflow\n"); */
- clear_bit(F_WRITING, &dev.flags);
- break;
-
- case HIDSP_INT_RECORD_OVER:
-/* printk(KERN_DEBUG LOGNAME ": Record overflow\n"); */
- clear_bit(F_READING, &dev.flags);
- break;
-
- default:
-/* printk(KERN_DEBUG LOGNAME ": DSP message %d 0x%02x\n",
- LOBYTE(wMessage), LOBYTE(wMessage)); */
- break;
- }
- break;
-
- case HIMT_MIDI_IN_UCHAR:
- if (dev.midi_in_interrupt)
- (*dev.midi_in_interrupt)(&dev);
- break;
-
- default:
-/* printk(KERN_DEBUG LOGNAME ": HIMT message %d 0x%02x\n", HIBYTE(wMessage), HIBYTE(wMessage)); */
- break;
- }
-}
-
-static irqreturn_t intr(int irq, void *dev_id)
-{
- /* Send ack to DSP */
- msnd_inb(dev.io + HP_RXL);
-
- /* Evaluate queued DSP messages */
- while (readw(dev.DSPQ + JQS_wTail) != readw(dev.DSPQ + JQS_wHead)) {
- register WORD wTmp;
-
- eval_dsp_msg(readw(dev.pwDSPQData + 2*readw(dev.DSPQ + JQS_wHead)));
-
- if ((wTmp = readw(dev.DSPQ + JQS_wHead) + 1) > readw(dev.DSPQ + JQS_wSize))
- writew(0, dev.DSPQ + JQS_wHead);
- else
- writew(wTmp, dev.DSPQ + JQS_wHead);
- }
- return IRQ_HANDLED;
-}
-
-static const struct file_operations dev_fileops = {
- .owner = THIS_MODULE,
- .read = dev_read,
- .write = dev_write,
- .unlocked_ioctl = dev_ioctl,
- .open = dev_open,
- .release = dev_release,
- .llseek = noop_llseek,
-};
-
-static int reset_dsp(void)
-{
- int timeout = 100;
-
- msnd_outb(HPDSPRESET_ON, dev.io + HP_DSPR);
- mdelay(1);
-#ifndef MSND_CLASSIC
- dev.info = msnd_inb(dev.io + HP_INFO);
-#endif
- msnd_outb(HPDSPRESET_OFF, dev.io + HP_DSPR);
- mdelay(1);
- while (timeout-- > 0) {
- if (msnd_inb(dev.io + HP_CVR) == HP_CVR_DEF)
- return 0;
- mdelay(1);
- }
- printk(KERN_ERR LOGNAME ": Cannot reset DSP\n");
-
- return -EIO;
-}
-
-static int __init probe_multisound(void)
-{
-#ifndef MSND_CLASSIC
- char *xv, *rev = NULL;
- char *pin = "Pinnacle", *fiji = "Fiji";
- char *pinfiji = "Pinnacle/Fiji";
-#endif
-
- if (!request_region(dev.io, dev.numio, "probing")) {
- printk(KERN_ERR LOGNAME ": I/O port conflict\n");
- return -ENODEV;
- }
-
- if (reset_dsp() < 0) {
- release_region(dev.io, dev.numio);
- return -ENODEV;
- }
-
-#ifdef MSND_CLASSIC
- dev.name = "Classic/Tahiti/Monterey";
- printk(KERN_INFO LOGNAME ": %s, "
-#else
- switch (dev.info >> 4) {
- case 0xf: xv = "<= 1.15"; break;
- case 0x1: xv = "1.18/1.2"; break;
- case 0x2: xv = "1.3"; break;
- case 0x3: xv = "1.4"; break;
- default: xv = "unknown"; break;
- }
-
- switch (dev.info & 0x7) {
- case 0x0: rev = "I"; dev.name = pin; break;
- case 0x1: rev = "F"; dev.name = pin; break;
- case 0x2: rev = "G"; dev.name = pin; break;
- case 0x3: rev = "H"; dev.name = pin; break;
- case 0x4: rev = "E"; dev.name = fiji; break;
- case 0x5: rev = "C"; dev.name = fiji; break;
- case 0x6: rev = "D"; dev.name = fiji; break;
- case 0x7:
- rev = "A-B (Fiji) or A-E (Pinnacle)";
- dev.name = pinfiji;
- break;
- }
- printk(KERN_INFO LOGNAME ": %s revision %s, Xilinx version %s, "
-#endif /* MSND_CLASSIC */
- "I/O 0x%x-0x%x, IRQ %d, memory mapped to %p-%p\n",
- dev.name,
-#ifndef MSND_CLASSIC
- rev, xv,
-#endif
- dev.io, dev.io + dev.numio - 1,
- dev.irq,
- dev.base, dev.base + 0x7fff);
-
- release_region(dev.io, dev.numio);
- return 0;
-}
-
-static int init_sma(void)
-{
- static int initted;
- WORD mastVolLeft, mastVolRight;
- unsigned long flags;
-
-#ifdef MSND_CLASSIC
- msnd_outb(dev.memid, dev.io + HP_MEMM);
-#endif
- msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS);
- if (initted) {
- mastVolLeft = readw(dev.SMA + SMA_wCurrMastVolLeft);
- mastVolRight = readw(dev.SMA + SMA_wCurrMastVolRight);
- } else
- mastVolLeft = mastVolRight = 0;
- memset_io(dev.base, 0, 0x8000);
-
- /* Critical section: bank 1 access */
- spin_lock_irqsave(&dev.lock, flags);
- msnd_outb(HPBLKSEL_1, dev.io + HP_BLKS);
- memset_io(dev.base, 0, 0x8000);
- msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS);
- spin_unlock_irqrestore(&dev.lock, flags);
-
- dev.pwDSPQData = (dev.base + DSPQ_DATA_BUFF);
- dev.pwMODQData = (dev.base + MODQ_DATA_BUFF);
- dev.pwMIDQData = (dev.base + MIDQ_DATA_BUFF);
-
- /* Motorola 56k shared memory base */
- dev.SMA = dev.base + SMA_STRUCT_START;
-
- /* Digital audio play queue */
- dev.DAPQ = dev.base + DAPQ_OFFSET;
- msnd_init_queue(dev.DAPQ, DAPQ_DATA_BUFF, DAPQ_BUFF_SIZE);
-
- /* Digital audio record queue */
- dev.DARQ = dev.base + DARQ_OFFSET;
- msnd_init_queue(dev.DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE);
-
- /* MIDI out queue */
- dev.MODQ = dev.base + MODQ_OFFSET;
- msnd_init_queue(dev.MODQ, MODQ_DATA_BUFF, MODQ_BUFF_SIZE);
-
- /* MIDI in queue */
- dev.MIDQ = dev.base + MIDQ_OFFSET;
- msnd_init_queue(dev.MIDQ, MIDQ_DATA_BUFF, MIDQ_BUFF_SIZE);
-
- /* DSP -> host message queue */
- dev.DSPQ = dev.base + DSPQ_OFFSET;
- msnd_init_queue(dev.DSPQ, DSPQ_DATA_BUFF, DSPQ_BUFF_SIZE);
-
- /* Setup some DSP values */
-#ifndef MSND_CLASSIC
- writew(1, dev.SMA + SMA_wCurrPlayFormat);
- writew(dev.play_sample_size, dev.SMA + SMA_wCurrPlaySampleSize);
- writew(dev.play_channels, dev.SMA + SMA_wCurrPlayChannels);
- writew(dev.play_sample_rate, dev.SMA + SMA_wCurrPlaySampleRate);
-#endif
- writew(dev.play_sample_rate, dev.SMA + SMA_wCalFreqAtoD);
- writew(mastVolLeft, dev.SMA + SMA_wCurrMastVolLeft);
- writew(mastVolRight, dev.SMA + SMA_wCurrMastVolRight);
-#ifndef MSND_CLASSIC
- writel(0x00010000, dev.SMA + SMA_dwCurrPlayPitch);
- writel(0x00000001, dev.SMA + SMA_dwCurrPlayRate);
-#endif
- writew(0x303, dev.SMA + SMA_wCurrInputTagBits);
-
- initted = 1;
-
- return 0;
-}
-
-static int __init calibrate_adc(WORD srate)
-{
- writew(srate, dev.SMA + SMA_wCalFreqAtoD);
- if (dev.calibrate_signal == 0)
- writew(readw(dev.SMA + SMA_wCurrHostStatusFlags)
- | 0x0001, dev.SMA + SMA_wCurrHostStatusFlags);
- else
- writew(readw(dev.SMA + SMA_wCurrHostStatusFlags)
- & ~0x0001, dev.SMA + SMA_wCurrHostStatusFlags);
- if (msnd_send_word(&dev, 0, 0, HDEXAR_CAL_A_TO_D) == 0 &&
- chk_send_dsp_cmd(&dev, HDEX_AUX_REQ) == 0) {
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(HZ / 3);
- return 0;
- }
- printk(KERN_WARNING LOGNAME ": ADC calibration failed\n");
-
- return -EIO;
-}
-
-static int upload_dsp_code(void)
-{
- msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS);
-#ifndef HAVE_DSPCODEH
- INITCODESIZE = mod_firmware_load(INITCODEFILE, &INITCODE);
- if (!INITCODE) {
- printk(KERN_ERR LOGNAME ": Error loading " INITCODEFILE);
- return -EBUSY;
- }
-
- PERMCODESIZE = mod_firmware_load(PERMCODEFILE, &PERMCODE);
- if (!PERMCODE) {
- printk(KERN_ERR LOGNAME ": Error loading " PERMCODEFILE);
- vfree(INITCODE);
- return -EBUSY;
- }
-#endif
- memcpy_toio(dev.base, PERMCODE, PERMCODESIZE);
- if (msnd_upload_host(&dev, INITCODE, INITCODESIZE) < 0) {
- printk(KERN_WARNING LOGNAME ": Error uploading to DSP\n");
- return -ENODEV;
- }
-#ifdef HAVE_DSPCODEH
- printk(KERN_INFO LOGNAME ": DSP firmware uploaded (resident)\n");
-#else
- printk(KERN_INFO LOGNAME ": DSP firmware uploaded\n");
-#endif
-
-#ifndef HAVE_DSPCODEH
- vfree(INITCODE);
- vfree(PERMCODE);
-#endif
-
- return 0;
-}
-
-#ifdef MSND_CLASSIC
-static void reset_proteus(void)
-{
- msnd_outb(HPPRORESET_ON, dev.io + HP_PROR);
- mdelay(TIME_PRO_RESET);
- msnd_outb(HPPRORESET_OFF, dev.io + HP_PROR);
- mdelay(TIME_PRO_RESET_DONE);
-}
-#endif
-
-static int initialize(void)
-{
- int err, timeout;
-
-#ifdef MSND_CLASSIC
- msnd_outb(HPWAITSTATE_0, dev.io + HP_WAIT);
- msnd_outb(HPBITMODE_16, dev.io + HP_BITM);
-
- reset_proteus();
-#endif
- if ((err = init_sma()) < 0) {
- printk(KERN_WARNING LOGNAME ": Cannot initialize SMA\n");
- return err;
- }
-
- if ((err = reset_dsp()) < 0)
- return err;
-
- if ((err = upload_dsp_code()) < 0) {
- printk(KERN_WARNING LOGNAME ": Cannot upload DSP code\n");
- return err;
- }
-
- timeout = 200;
- while (readw(dev.base)) {
- mdelay(1);
- if (!timeout--) {
- printk(KERN_DEBUG LOGNAME ": DSP reset timeout\n");
- return -EIO;
- }
- }
-
- mixer_setup();
-
- return 0;
-}
-
-static int dsp_full_reset(void)
-{
- int rv;
-
- if (test_bit(F_RESETTING, &dev.flags) || ++dev.nresets > 10)
- return 0;
-
- set_bit(F_RESETTING, &dev.flags);
- printk(KERN_INFO LOGNAME ": DSP reset\n");
- dsp_halt(NULL); /* Unconditionally halt */
- if ((rv = initialize()))
- printk(KERN_WARNING LOGNAME ": DSP reset failed\n");
- force_recsrc(dev.recsrc);
- dsp_open(NULL);
- clear_bit(F_RESETTING, &dev.flags);
-
- return rv;
-}
-
-static int __init attach_multisound(void)
-{
- int err;
-
- if ((err = request_irq(dev.irq, intr, 0, dev.name, &dev)) < 0) {
- printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", dev.irq);
- return err;
- }
- if (request_region(dev.io, dev.numio, dev.name) == NULL) {
- free_irq(dev.irq, &dev);
- return -EBUSY;
- }
-
- err = dsp_full_reset();
- if (err < 0) {
- release_region(dev.io, dev.numio);
- free_irq(dev.irq, &dev);
- return err;
- }
-
- if ((err = msnd_register(&dev)) < 0) {
- printk(KERN_ERR LOGNAME ": Unable to register MultiSound\n");
- release_region(dev.io, dev.numio);
- free_irq(dev.irq, &dev);
- return err;
- }
-
- if ((dev.dsp_minor = register_sound_dsp(&dev_fileops, -1)) < 0) {
- printk(KERN_ERR LOGNAME ": Unable to register DSP operations\n");
- msnd_unregister(&dev);
- release_region(dev.io, dev.numio);
- free_irq(dev.irq, &dev);
- return dev.dsp_minor;
- }
-
- if ((dev.mixer_minor = register_sound_mixer(&dev_fileops, -1)) < 0) {
- printk(KERN_ERR LOGNAME ": Unable to register mixer operations\n");
- unregister_sound_mixer(dev.mixer_minor);
- msnd_unregister(&dev);
- release_region(dev.io, dev.numio);
- free_irq(dev.irq, &dev);
- return dev.mixer_minor;
- }
-
- dev.ext_midi_dev = dev.hdr_midi_dev = -1;
-
- disable_irq(dev.irq);
- calibrate_adc(dev.play_sample_rate);
-#ifndef MSND_CLASSIC
- force_recsrc(SOUND_MASK_IMIX);
-#endif
-
- return 0;
-}
-
-static void __exit unload_multisound(void)
-{
- release_region(dev.io, dev.numio);
- free_irq(dev.irq, &dev);
- unregister_sound_mixer(dev.mixer_minor);
- unregister_sound_dsp(dev.dsp_minor);
- msnd_unregister(&dev);
-}
-
-#ifndef MSND_CLASSIC
-
-/* Pinnacle/Fiji Logical Device Configuration */
-
-static int __init msnd_write_cfg(int cfg, int reg, int value)
-{
- msnd_outb(reg, cfg);
- msnd_outb(value, cfg + 1);
- if (value != msnd_inb(cfg + 1)) {
- printk(KERN_ERR LOGNAME ": msnd_write_cfg: I/O error\n");
- return -EIO;
- }
- return 0;
-}
-
-static int __init msnd_write_cfg_io0(int cfg, int num, WORD io)
-{
- if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
- return -EIO;
- if (msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io)))
- return -EIO;
- if (msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io)))
- return -EIO;
- return 0;
-}
-
-static int __init msnd_write_cfg_io1(int cfg, int num, WORD io)
-{
- if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
- return -EIO;
- if (msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io)))
- return -EIO;
- if (msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io)))
- return -EIO;
- return 0;
-}
-
-static int __init msnd_write_cfg_irq(int cfg, int num, WORD irq)
-{
- if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
- return -EIO;
- if (msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq)))
- return -EIO;
- if (msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE))
- return -EIO;
- return 0;
-}
-
-static int __init msnd_write_cfg_mem(int cfg, int num, int mem)
-{
- WORD wmem;
-
- mem >>= 8;
- mem &= 0xfff;
- wmem = (WORD)mem;
- if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
- return -EIO;
- if (msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem)))
- return -EIO;
- if (msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem)))
- return -EIO;
- if (wmem && msnd_write_cfg(cfg, IREG_MEMCONTROL, (MEMTYPE_HIADDR | MEMTYPE_16BIT)))
- return -EIO;
- return 0;
-}
-
-static int __init msnd_activate_logical(int cfg, int num)
-{
- if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
- return -EIO;
- if (msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE))
- return -EIO;
- return 0;
-}
-
-static int __init msnd_write_cfg_logical(int cfg, int num, WORD io0, WORD io1, WORD irq, int mem)
-{
- if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
- return -EIO;
- if (msnd_write_cfg_io0(cfg, num, io0))
- return -EIO;
- if (msnd_write_cfg_io1(cfg, num, io1))
- return -EIO;
- if (msnd_write_cfg_irq(cfg, num, irq))
- return -EIO;
- if (msnd_write_cfg_mem(cfg, num, mem))
- return -EIO;
- if (msnd_activate_logical(cfg, num))
- return -EIO;
- return 0;
-}
-
-typedef struct msnd_pinnacle_cfg_device {
- WORD io0, io1, irq;
- int mem;
-} msnd_pinnacle_cfg_t[4];
-
-static int __init msnd_pinnacle_cfg_devices(int cfg, int reset, msnd_pinnacle_cfg_t device)
-{
- int i;
-
- /* Reset devices if told to */
- if (reset) {
- printk(KERN_INFO LOGNAME ": Resetting all devices\n");
- for (i = 0; i < 4; ++i)
- if (msnd_write_cfg_logical(cfg, i, 0, 0, 0, 0))
- return -EIO;
- }
-
- /* Configure specified devices */
- for (i = 0; i < 4; ++i) {
-
- switch (i) {
- case 0: /* DSP */
- if (!(device[i].io0 && device[i].irq && device[i].mem))
- continue;
- break;
- case 1: /* MPU */
- if (!(device[i].io0 && device[i].irq))
- continue;
- printk(KERN_INFO LOGNAME
- ": Configuring MPU to I/O 0x%x IRQ %d\n",
- device[i].io0, device[i].irq);
- break;
- case 2: /* IDE */
- if (!(device[i].io0 && device[i].io1 && device[i].irq))
- continue;
- printk(KERN_INFO LOGNAME
- ": Configuring IDE to I/O 0x%x, 0x%x IRQ %d\n",
- device[i].io0, device[i].io1, device[i].irq);
- break;
- case 3: /* Joystick */
- if (!(device[i].io0))
- continue;
- printk(KERN_INFO LOGNAME
- ": Configuring joystick to I/O 0x%x\n",
- device[i].io0);
- break;
- }
-
- /* Configure the device */
- if (msnd_write_cfg_logical(cfg, i, device[i].io0, device[i].io1, device[i].irq, device[i].mem))
- return -EIO;
- }
-
- return 0;
-}
-#endif
-
-#ifdef MODULE
-MODULE_AUTHOR ("Andrew Veliath <andrewtv@usa.net>");
-MODULE_DESCRIPTION ("Turtle Beach " LONGNAME " Linux Driver");
-MODULE_LICENSE("GPL");
-
-static int io __initdata = -1;
-static int irq __initdata = -1;
-static int mem __initdata = -1;
-static int write_ndelay __initdata = -1;
-
-#ifndef MSND_CLASSIC
-/* Pinnacle/Fiji non-PnP Config Port */
-static int cfg __initdata = -1;
-
-/* Extra Peripheral Configuration */
-static int reset __initdata = 0;
-static int mpu_io __initdata = 0;
-static int mpu_irq __initdata = 0;
-static int ide_io0 __initdata = 0;
-static int ide_io1 __initdata = 0;
-static int ide_irq __initdata = 0;
-static int joystick_io __initdata = 0;
-
-/* If we have the digital daugherboard... */
-static int digital __initdata = 0;
-#endif
-
-static int fifosize __initdata = DEFFIFOSIZE;
-static int calibrate_signal __initdata = 0;
-
-#else /* not a module */
-
-static int write_ndelay __initdata = -1;
-
-#ifdef MSND_CLASSIC
-static int io __initdata = CONFIG_MSNDCLAS_IO;
-static int irq __initdata = CONFIG_MSNDCLAS_IRQ;
-static int mem __initdata = CONFIG_MSNDCLAS_MEM;
-#else /* Pinnacle/Fiji */
-
-static int io __initdata = CONFIG_MSNDPIN_IO;
-static int irq __initdata = CONFIG_MSNDPIN_IRQ;
-static int mem __initdata = CONFIG_MSNDPIN_MEM;
-
-/* Pinnacle/Fiji non-PnP Config Port */
-#ifdef CONFIG_MSNDPIN_NONPNP
-# ifndef CONFIG_MSNDPIN_CFG
-# define CONFIG_MSNDPIN_CFG 0x250
-# endif
-#else
-# ifdef CONFIG_MSNDPIN_CFG
-# undef CONFIG_MSNDPIN_CFG
-# endif
-# define CONFIG_MSNDPIN_CFG -1
-#endif
-static int cfg __initdata = CONFIG_MSNDPIN_CFG;
-/* If not a module, we don't need to bother with reset=1 */
-static int reset;
-
-/* Extra Peripheral Configuration (Default: Disable) */
-#ifndef CONFIG_MSNDPIN_MPU_IO
-# define CONFIG_MSNDPIN_MPU_IO 0
-#endif
-static int mpu_io __initdata = CONFIG_MSNDPIN_MPU_IO;
-
-#ifndef CONFIG_MSNDPIN_MPU_IRQ
-# define CONFIG_MSNDPIN_MPU_IRQ 0
-#endif
-static int mpu_irq __initdata = CONFIG_MSNDPIN_MPU_IRQ;
-
-#ifndef CONFIG_MSNDPIN_IDE_IO0
-# define CONFIG_MSNDPIN_IDE_IO0 0
-#endif
-static int ide_io0 __initdata = CONFIG_MSNDPIN_IDE_IO0;
-
-#ifndef CONFIG_MSNDPIN_IDE_IO1
-# define CONFIG_MSNDPIN_IDE_IO1 0
-#endif
-static int ide_io1 __initdata = CONFIG_MSNDPIN_IDE_IO1;
-
-#ifndef CONFIG_MSNDPIN_IDE_IRQ
-# define CONFIG_MSNDPIN_IDE_IRQ 0
-#endif
-static int ide_irq __initdata = CONFIG_MSNDPIN_IDE_IRQ;
-
-#ifndef CONFIG_MSNDPIN_JOYSTICK_IO
-# define CONFIG_MSNDPIN_JOYSTICK_IO 0
-#endif
-static int joystick_io __initdata = CONFIG_MSNDPIN_JOYSTICK_IO;
-
-/* Have SPDIF (Digital) Daughterboard */
-#ifndef CONFIG_MSNDPIN_DIGITAL
-# define CONFIG_MSNDPIN_DIGITAL 0
-#endif
-static int digital __initdata = CONFIG_MSNDPIN_DIGITAL;
-
-#endif /* MSND_CLASSIC */
-
-#ifndef CONFIG_MSND_FIFOSIZE
-# define CONFIG_MSND_FIFOSIZE DEFFIFOSIZE
-#endif
-static int fifosize __initdata = CONFIG_MSND_FIFOSIZE;
-
-#ifndef CONFIG_MSND_CALSIGNAL
-# define CONFIG_MSND_CALSIGNAL 0
-#endif
-static int
-calibrate_signal __initdata = CONFIG_MSND_CALSIGNAL;
-#endif /* MODULE */
-
-module_param (io, int, 0);
-module_param (irq, int, 0);
-module_param (mem, int, 0);
-module_param (write_ndelay, int, 0);
-module_param (fifosize, int, 0);
-module_param (calibrate_signal, int, 0);
-#ifndef MSND_CLASSIC
-module_param (digital, bool, 0);
-module_param (cfg, int, 0);
-module_param (reset, int, 0);
-module_param (mpu_io, int, 0);
-module_param (mpu_irq, int, 0);
-module_param (ide_io0, int, 0);
-module_param (ide_io1, int, 0);
-module_param (ide_irq, int, 0);
-module_param (joystick_io, int, 0);
-#endif
-
-static int __init msnd_init(void)
-{
- int err;
-#ifndef MSND_CLASSIC
- static msnd_pinnacle_cfg_t pinnacle_devs;
-#endif /* MSND_CLASSIC */
-
- printk(KERN_INFO LOGNAME ": Turtle Beach " LONGNAME " Linux Driver Version "
- VERSION ", Copyright (C) 1998 Andrew Veliath\n");
-
- if (io == -1 || irq == -1 || mem == -1)
- printk(KERN_WARNING LOGNAME ": io, irq and mem must be set\n");
-
-#ifdef MSND_CLASSIC
- if (io == -1 ||
- !(io == 0x290 ||
- io == 0x260 ||
- io == 0x250 ||
- io == 0x240 ||
- io == 0x230 ||
- io == 0x220 ||
- io == 0x210 ||
- io == 0x3e0)) {
- printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set to 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x290, or 0x3E0\n");
- return -EINVAL;
- }
-#else
- if (io == -1 ||
- io < 0x100 ||
- io > 0x3e0 ||
- (io % 0x10) != 0) {
- printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must within the range 0x100 to 0x3E0 and must be evenly divisible by 0x10\n");
- return -EINVAL;
- }
-#endif /* MSND_CLASSIC */
-
- if (irq == -1 ||
- !(irq == 5 ||
- irq == 7 ||
- irq == 9 ||
- irq == 10 ||
- irq == 11 ||
- irq == 12)) {
- printk(KERN_ERR LOGNAME ": \"irq\" - must be set to 5, 7, 9, 10, 11 or 12\n");
- return -EINVAL;
- }
-
- if (mem == -1 ||
- !(mem == 0xb0000 ||
- mem == 0xc8000 ||
- mem == 0xd0000 ||
- mem == 0xd8000 ||
- mem == 0xe0000 ||
- mem == 0xe8000)) {
- printk(KERN_ERR LOGNAME ": \"mem\" - must be set to "
- "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or 0xe8000\n");
- return -EINVAL;
- }
-
-#ifdef MSND_CLASSIC
- switch (irq) {
- case 5: dev.irqid = HPIRQ_5; break;
- case 7: dev.irqid = HPIRQ_7; break;
- case 9: dev.irqid = HPIRQ_9; break;
- case 10: dev.irqid = HPIRQ_10; break;
- case 11: dev.irqid = HPIRQ_11; break;
- case 12: dev.irqid = HPIRQ_12; break;
- }
-
- switch (mem) {
- case 0xb0000: dev.memid = HPMEM_B000; break;
- case 0xc8000: dev.memid = HPMEM_C800; break;
- case 0xd0000: dev.memid = HPMEM_D000; break;
- case 0xd8000: dev.memid = HPMEM_D800; break;
- case 0xe0000: dev.memid = HPMEM_E000; break;
- case 0xe8000: dev.memid = HPMEM_E800; break;
- }
-#else
- if (cfg == -1) {
- printk(KERN_INFO LOGNAME ": Assuming PnP mode\n");
- } else if (cfg != 0x250 && cfg != 0x260 && cfg != 0x270) {
- printk(KERN_INFO LOGNAME ": Config port must be 0x250, 0x260 or 0x270 (or unspecified for PnP mode)\n");
- return -EINVAL;
- } else {
- printk(KERN_INFO LOGNAME ": Non-PnP mode: configuring at port 0x%x\n", cfg);
-
- /* DSP */
- pinnacle_devs[0].io0 = io;
- pinnacle_devs[0].irq = irq;
- pinnacle_devs[0].mem = mem;
-
- /* The following are Pinnacle specific */
-
- /* MPU */
- pinnacle_devs[1].io0 = mpu_io;
- pinnacle_devs[1].irq = mpu_irq;
-
- /* IDE */
- pinnacle_devs[2].io0 = ide_io0;
- pinnacle_devs[2].io1 = ide_io1;
- pinnacle_devs[2].irq = ide_irq;
-
- /* Joystick */
- pinnacle_devs[3].io0 = joystick_io;
-
- if (!request_region(cfg, 2, "Pinnacle/Fiji Config")) {
- printk(KERN_ERR LOGNAME ": Config port 0x%x conflict\n", cfg);
- return -EIO;
- }
-
- if (msnd_pinnacle_cfg_devices(cfg, reset, pinnacle_devs)) {
- printk(KERN_ERR LOGNAME ": Device configuration error\n");
- release_region(cfg, 2);
- return -EIO;
- }
- release_region(cfg, 2);
- }
-#endif /* MSND_CLASSIC */
-
- if (fifosize < 16)
- fifosize = 16;
-
- if (fifosize > 1024)
- fifosize = 1024;
-
- set_default_audio_parameters();
-#ifdef MSND_CLASSIC
- dev.type = msndClassic;
-#else
- dev.type = msndPinnacle;
-#endif
- dev.io = io;
- dev.numio = DSP_NUMIO;
- dev.irq = irq;
- dev.base = ioremap(mem, 0x8000);
- dev.fifosize = fifosize * 1024;
- dev.calibrate_signal = calibrate_signal ? 1 : 0;
- dev.recsrc = 0;
- dev.dspq_data_buff = DSPQ_DATA_BUFF;
- dev.dspq_buff_size = DSPQ_BUFF_SIZE;
- if (write_ndelay == -1)
- write_ndelay = CONFIG_MSND_WRITE_NDELAY;
- if (write_ndelay)
- clear_bit(F_DISABLE_WRITE_NDELAY, &dev.flags);
- else
- set_bit(F_DISABLE_WRITE_NDELAY, &dev.flags);
-#ifndef MSND_CLASSIC
- if (digital)
- set_bit(F_HAVEDIGITAL, &dev.flags);
-#endif
- init_waitqueue_head(&dev.writeblock);
- init_waitqueue_head(&dev.readblock);
- init_waitqueue_head(&dev.writeflush);
- msnd_fifo_init(&dev.DAPF);
- msnd_fifo_init(&dev.DARF);
- spin_lock_init(&dev.lock);
- printk(KERN_INFO LOGNAME ": %u byte audio FIFOs (x2)\n", dev.fifosize);
- if ((err = msnd_fifo_alloc(&dev.DAPF, dev.fifosize)) < 0) {
- printk(KERN_ERR LOGNAME ": Couldn't allocate write FIFO\n");
- return err;
- }
-
- if ((err = msnd_fifo_alloc(&dev.DARF, dev.fifosize)) < 0) {
- printk(KERN_ERR LOGNAME ": Couldn't allocate read FIFO\n");
- msnd_fifo_free(&dev.DAPF);
- return err;
- }
-
- if ((err = probe_multisound()) < 0) {
- printk(KERN_ERR LOGNAME ": Probe failed\n");
- msnd_fifo_free(&dev.DAPF);
- msnd_fifo_free(&dev.DARF);
- return err;
- }
-
- if ((err = attach_multisound()) < 0) {
- printk(KERN_ERR LOGNAME ": Attach failed\n");
- msnd_fifo_free(&dev.DAPF);
- msnd_fifo_free(&dev.DARF);
- return err;
- }
-
- return 0;
-}
-
-static void __exit msdn_cleanup(void)
-{
- unload_multisound();
- msnd_fifo_free(&dev.DAPF);
- msnd_fifo_free(&dev.DARF);
-}
-
-module_init(msnd_init);
-module_exit(msdn_cleanup);
diff --git a/sound/oss/msnd_pinnacle.h b/sound/oss/msnd_pinnacle.h
deleted file mode 100644
index c18d66cbbe3f..000000000000
--- a/sound/oss/msnd_pinnacle.h
+++ /dev/null
@@ -1,246 +0,0 @@
-/*********************************************************************
- *
- * msnd_pinnacle.h
- *
- * Turtle Beach MultiSound Sound Card Driver for Linux
- *
- * Some parts of this header file were derived from the Turtle Beach
- * MultiSound Driver Development Kit.
- *
- * Copyright (C) 1998 Andrew Veliath
- * Copyright (C) 1993 Turtle Beach Systems, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- ********************************************************************/
-#ifndef __MSND_PINNACLE_H
-#define __MSND_PINNACLE_H
-
-
-#define DSP_NUMIO 0x08
-
-#define IREG_LOGDEVICE 0x07
-#define IREG_ACTIVATE 0x30
-#define LD_ACTIVATE 0x01
-#define LD_DISACTIVATE 0x00
-#define IREG_EECONTROL 0x3F
-#define IREG_MEMBASEHI 0x40
-#define IREG_MEMBASELO 0x41
-#define IREG_MEMCONTROL 0x42
-#define IREG_MEMRANGEHI 0x43
-#define IREG_MEMRANGELO 0x44
-#define MEMTYPE_8BIT 0x00
-#define MEMTYPE_16BIT 0x02
-#define MEMTYPE_RANGE 0x00
-#define MEMTYPE_HIADDR 0x01
-#define IREG_IO0_BASEHI 0x60
-#define IREG_IO0_BASELO 0x61
-#define IREG_IO1_BASEHI 0x62
-#define IREG_IO1_BASELO 0x63
-#define IREG_IRQ_NUMBER 0x70
-#define IREG_IRQ_TYPE 0x71
-#define IRQTYPE_HIGH 0x02
-#define IRQTYPE_LOW 0x00
-#define IRQTYPE_LEVEL 0x01
-#define IRQTYPE_EDGE 0x00
-
-#define HP_DSPR 0x04
-#define HP_BLKS 0x04
-
-#define HPDSPRESET_OFF 2
-#define HPDSPRESET_ON 0
-
-#define HPBLKSEL_0 2
-#define HPBLKSEL_1 3
-
-#define HIMT_DAT_OFF 0x03
-
-#define HIDSP_PLAY_UNDER 0x00
-#define HIDSP_INT_PLAY_UNDER 0x01
-#define HIDSP_SSI_TX_UNDER 0x02
-#define HIDSP_RECQ_OVERFLOW 0x08
-#define HIDSP_INT_RECORD_OVER 0x09
-#define HIDSP_SSI_RX_OVERFLOW 0x0a
-
-#define HIDSP_MIDI_IN_OVER 0x10
-
-#define HIDSP_MIDI_FRAME_ERR 0x11
-#define HIDSP_MIDI_PARITY_ERR 0x12
-#define HIDSP_MIDI_OVERRUN_ERR 0x13
-
-#define HIDSP_INPUT_CLIPPING 0x20
-#define HIDSP_MIX_CLIPPING 0x30
-#define HIDSP_DAT_IN_OFF 0x21
-
-#define HDEXAR_SET_ANA_IN 0
-#define HDEXAR_CLEAR_PEAKS 1
-#define HDEXAR_IN_SET_POTS 2
-#define HDEXAR_AUX_SET_POTS 3
-#define HDEXAR_CAL_A_TO_D 4
-#define HDEXAR_RD_EXT_DSP_BITS 5
-
-#define HDEXAR_SET_SYNTH_IN 4
-#define HDEXAR_READ_DAT_IN 5
-#define HDEXAR_MIC_SET_POTS 6
-#define HDEXAR_SET_DAT_IN 7
-
-#define HDEXAR_SET_SYNTH_48 8
-#define HDEXAR_SET_SYNTH_44 9
-
-#define TIME_PRO_RESET_DONE 0x028A
-#define TIME_PRO_SYSEX 0x001E
-#define TIME_PRO_RESET 0x0032
-
-#define AGND 0x01
-#define SIGNAL 0x02
-
-#define EXT_DSP_BIT_DCAL 0x0001
-#define EXT_DSP_BIT_MIDI_CON 0x0002
-
-#define BUFFSIZE 0x8000
-#define HOSTQ_SIZE 0x40
-
-#define SRAM_CNTL_START 0x7F00
-#define SMA_STRUCT_START 0x7F40
-
-#define DAP_BUFF_SIZE 0x2400
-#define DAR_BUFF_SIZE 0x2000
-
-#define DAPQ_STRUCT_SIZE 0x10
-#define DARQ_STRUCT_SIZE 0x10
-#define DAPQ_BUFF_SIZE (3 * 0x10)
-#define DARQ_BUFF_SIZE (3 * 0x10)
-#define MODQ_BUFF_SIZE 0x400
-#define MIDQ_BUFF_SIZE 0x800
-#define DSPQ_BUFF_SIZE 0x5A0
-
-#define DAPQ_DATA_BUFF 0x6C00
-#define DARQ_DATA_BUFF 0x6C30
-#define MODQ_DATA_BUFF 0x6C60
-#define MIDQ_DATA_BUFF 0x7060
-#define DSPQ_DATA_BUFF 0x7860
-
-#define DAPQ_OFFSET SRAM_CNTL_START
-#define DARQ_OFFSET (SRAM_CNTL_START + 0x08)
-#define MODQ_OFFSET (SRAM_CNTL_START + 0x10)
-#define MIDQ_OFFSET (SRAM_CNTL_START + 0x18)
-#define DSPQ_OFFSET (SRAM_CNTL_START + 0x20)
-
-#define MOP_WAVEHDR 0
-#define MOP_EXTOUT 1
-#define MOP_HWINIT 0xfe
-#define MOP_NONE 0xff
-#define MOP_MAX 1
-
-#define MIP_EXTIN 0
-#define MIP_WAVEHDR 1
-#define MIP_HWINIT 0xfe
-#define MIP_MAX 1
-
-/* Pinnacle/Fiji SMA Common Data */
-#define SMA_wCurrPlayBytes 0x0000
-#define SMA_wCurrRecordBytes 0x0002
-#define SMA_wCurrPlayVolLeft 0x0004
-#define SMA_wCurrPlayVolRight 0x0006
-#define SMA_wCurrInVolLeft 0x0008
-#define SMA_wCurrInVolRight 0x000a
-#define SMA_wCurrMHdrVolLeft 0x000c
-#define SMA_wCurrMHdrVolRight 0x000e
-#define SMA_dwCurrPlayPitch 0x0010
-#define SMA_dwCurrPlayRate 0x0014
-#define SMA_wCurrMIDIIOPatch 0x0018
-#define SMA_wCurrPlayFormat 0x001a
-#define SMA_wCurrPlaySampleSize 0x001c
-#define SMA_wCurrPlayChannels 0x001e
-#define SMA_wCurrPlaySampleRate 0x0020
-#define SMA_wCurrRecordFormat 0x0022
-#define SMA_wCurrRecordSampleSize 0x0024
-#define SMA_wCurrRecordChannels 0x0026
-#define SMA_wCurrRecordSampleRate 0x0028
-#define SMA_wCurrDSPStatusFlags 0x002a
-#define SMA_wCurrHostStatusFlags 0x002c
-#define SMA_wCurrInputTagBits 0x002e
-#define SMA_wCurrLeftPeak 0x0030
-#define SMA_wCurrRightPeak 0x0032
-#define SMA_bMicPotPosLeft 0x0034
-#define SMA_bMicPotPosRight 0x0035
-#define SMA_bMicPotMaxLeft 0x0036
-#define SMA_bMicPotMaxRight 0x0037
-#define SMA_bInPotPosLeft 0x0038
-#define SMA_bInPotPosRight 0x0039
-#define SMA_bAuxPotPosLeft 0x003a
-#define SMA_bAuxPotPosRight 0x003b
-#define SMA_bInPotMaxLeft 0x003c
-#define SMA_bInPotMaxRight 0x003d
-#define SMA_bAuxPotMaxLeft 0x003e
-#define SMA_bAuxPotMaxRight 0x003f
-#define SMA_bInPotMaxMethod 0x0040
-#define SMA_bAuxPotMaxMethod 0x0041
-#define SMA_wCurrMastVolLeft 0x0042
-#define SMA_wCurrMastVolRight 0x0044
-#define SMA_wCalFreqAtoD 0x0046
-#define SMA_wCurrAuxVolLeft 0x0048
-#define SMA_wCurrAuxVolRight 0x004a
-#define SMA_wCurrPlay1VolLeft 0x004c
-#define SMA_wCurrPlay1VolRight 0x004e
-#define SMA_wCurrPlay2VolLeft 0x0050
-#define SMA_wCurrPlay2VolRight 0x0052
-#define SMA_wCurrPlay3VolLeft 0x0054
-#define SMA_wCurrPlay3VolRight 0x0056
-#define SMA_wCurrPlay4VolLeft 0x0058
-#define SMA_wCurrPlay4VolRight 0x005a
-#define SMA_wCurrPlay1PeakLeft 0x005c
-#define SMA_wCurrPlay1PeakRight 0x005e
-#define SMA_wCurrPlay2PeakLeft 0x0060
-#define SMA_wCurrPlay2PeakRight 0x0062
-#define SMA_wCurrPlay3PeakLeft 0x0064
-#define SMA_wCurrPlay3PeakRight 0x0066
-#define SMA_wCurrPlay4PeakLeft 0x0068
-#define SMA_wCurrPlay4PeakRight 0x006a
-#define SMA_wCurrPlayPeakLeft 0x006c
-#define SMA_wCurrPlayPeakRight 0x006e
-#define SMA_wCurrDATSR 0x0070
-#define SMA_wCurrDATRXCHNL 0x0072
-#define SMA_wCurrDATTXCHNL 0x0074
-#define SMA_wCurrDATRXRate 0x0076
-#define SMA_dwDSPPlayCount 0x0078
-#define SMA__size 0x007c
-
-#ifdef HAVE_DSPCODEH
-# include "pndsperm.c"
-# include "pndspini.c"
-# define PERMCODE pndsperm
-# define INITCODE pndspini
-# define PERMCODESIZE sizeof(pndsperm)
-# define INITCODESIZE sizeof(pndspini)
-#else
-# ifndef CONFIG_MSNDPIN_INIT_FILE
-# define CONFIG_MSNDPIN_INIT_FILE \
- "/etc/sound/pndspini.bin"
-# endif
-# ifndef CONFIG_MSNDPIN_PERM_FILE
-# define CONFIG_MSNDPIN_PERM_FILE \
- "/etc/sound/pndsperm.bin"
-# endif
-# define PERMCODEFILE CONFIG_MSNDPIN_PERM_FILE
-# define INITCODEFILE CONFIG_MSNDPIN_INIT_FILE
-# define PERMCODE dspini
-# define INITCODE permini
-# define PERMCODESIZE sizeof_dspini
-# define INITCODESIZE sizeof_permini
-#endif
-#define LONGNAME "MultiSound (Pinnacle/Fiji)"
-
-#endif /* __MSND_PINNACLE_H */
diff --git a/sound/oss/opl3.c b/sound/oss/opl3.c
deleted file mode 100644
index 938c48c43585..000000000000
--- a/sound/oss/opl3.c
+++ /dev/null
@@ -1,1251 +0,0 @@
-/*
- * sound/oss/opl3.c
- *
- * A low level driver for Yamaha YM3812 and OPL-3 -chips
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Changes
- * Thomas Sailer ioctl code reworked (vmalloc/vfree removed)
- * Alan Cox modularisation, fixed sound_mem allocs.
- * Christoph Hellwig Adapted to module_init/module_exit
- * Arnaldo C. de Melo get rid of check_region, use request_region for
- * OPL4, release it on exit, some cleanups.
- *
- * Status
- * Believed to work. Badly needs rewriting a bit to support multiple
- * OPL3 devices.
- */
-
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-
-/*
- * Major improvements to the FM handling 30AUG92 by Rob Hooft,
- * hooft@chem.ruu.nl
- */
-
-#include "sound_config.h"
-
-#include "opl3_hw.h"
-
-#define MAX_VOICE 18
-#define OFFS_4OP 11
-
-struct voice_info
-{
- unsigned char keyon_byte;
- long bender;
- long bender_range;
- unsigned long orig_freq;
- unsigned long current_freq;
- int volume;
- int mode;
- int panning; /* 0xffff means not set */
-};
-
-typedef struct opl_devinfo
-{
- int base;
- int left_io, right_io;
- int nr_voice;
- int lv_map[MAX_VOICE];
-
- struct voice_info voc[MAX_VOICE];
- struct voice_alloc_info *v_alloc;
- struct channel_info *chn_info;
-
- struct sbi_instrument i_map[SBFM_MAXINSTR];
- struct sbi_instrument *act_i[MAX_VOICE];
-
- struct synth_info fm_info;
-
- int busy;
- int model;
- unsigned char cmask;
-
- int is_opl4;
-} opl_devinfo;
-
-static struct opl_devinfo *devc = NULL;
-
-static int detected_model;
-
-static int store_instr(int instr_no, struct sbi_instrument *instr);
-static void freq_to_fnum(int freq, int *block, int *fnum);
-static void opl3_command(int io_addr, unsigned int addr, unsigned int val);
-static int opl3_kill_note(int dev, int voice, int note, int velocity);
-
-static void enter_4op_mode(void)
-{
- int i;
- static int v4op[MAX_VOICE] = {
- 0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17
- };
-
- devc->cmask = 0x3f; /* Connect all possible 4 OP voice operators */
- opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x3f);
-
- for (i = 0; i < 3; i++)
- pv_map[i].voice_mode = 4;
- for (i = 3; i < 6; i++)
- pv_map[i].voice_mode = 0;
-
- for (i = 9; i < 12; i++)
- pv_map[i].voice_mode = 4;
- for (i = 12; i < 15; i++)
- pv_map[i].voice_mode = 0;
-
- for (i = 0; i < 12; i++)
- devc->lv_map[i] = v4op[i];
- devc->v_alloc->max_voice = devc->nr_voice = 12;
-}
-
-static int opl3_ioctl(int dev, unsigned int cmd, void __user * arg)
-{
- struct sbi_instrument ins;
-
- switch (cmd) {
- case SNDCTL_FM_LOAD_INSTR:
- printk(KERN_WARNING "Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n");
- if (copy_from_user(&ins, arg, sizeof(ins)))
- return -EFAULT;
- if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) {
- printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel);
- return -EINVAL;
- }
- return store_instr(ins.channel, &ins);
-
- case SNDCTL_SYNTH_INFO:
- devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice;
- if (copy_to_user(arg, &devc->fm_info, sizeof(devc->fm_info)))
- return -EFAULT;
- return 0;
-
- case SNDCTL_SYNTH_MEMAVL:
- return 0x7fffffff;
-
- case SNDCTL_FM_4OP_ENABLE:
- if (devc->model == 2)
- enter_4op_mode();
- return 0;
-
- default:
- return -EINVAL;
- }
-}
-
-static int opl3_detect(int ioaddr)
-{
- /*
- * This function returns 1 if the FM chip is present at the given I/O port
- * The detection algorithm plays with the timer built in the FM chip and
- * looks for a change in the status register.
- *
- * Note! The timers of the FM chip are not connected to AdLib (and compatible)
- * boards.
- *
- * Note2! The chip is initialized if detected.
- */
-
- unsigned char stat1, signature;
- int i;
-
- if (devc != NULL)
- {
- printk(KERN_ERR "opl3: Only one OPL3 supported.\n");
- return 0;
- }
-
- devc = kzalloc(sizeof(*devc), GFP_KERNEL);
-
- if (devc == NULL)
- {
- printk(KERN_ERR "opl3: Can't allocate memory for the device control "
- "structure \n ");
- return 0;
- }
-
- strcpy(devc->fm_info.name, "OPL2");
-
- if (!request_region(ioaddr, 4, devc->fm_info.name)) {
- printk(KERN_WARNING "opl3: I/O port 0x%x already in use\n", ioaddr);
- goto cleanup_devc;
- }
-
- devc->base = ioaddr;
-
- /* Reset timers 1 and 2 */
- opl3_command(ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK);
-
- /* Reset the IRQ of the FM chip */
- opl3_command(ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET);
-
- signature = stat1 = inb(ioaddr); /* Status register */
-
- if (signature != 0x00 && signature != 0x06 && signature != 0x02 &&
- signature != 0x0f)
- {
- MDB(printk(KERN_INFO "OPL3 not detected %x\n", signature));
- goto cleanup_region;
- }
-
- if (signature == 0x06) /* OPL2 */
- {
- detected_model = 2;
- }
- else if (signature == 0x00 || signature == 0x0f) /* OPL3 or OPL4 */
- {
- unsigned char tmp;
-
- detected_model = 3;
-
- /*
- * Detect availability of OPL4 (_experimental_). Works probably
- * only after a cold boot. In addition the OPL4 port
- * of the chip may not be connected to the PC bus at all.
- */
-
- opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0x00);
- opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, OPL3_ENABLE | OPL4_ENABLE);
-
- if ((tmp = inb(ioaddr)) == 0x02) /* Have a OPL4 */
- {
- detected_model = 4;
- }
-
- if (request_region(ioaddr - 8, 2, "OPL4")) /* OPL4 port was free */
- {
- int tmp;
-
- outb((0x02), ioaddr - 8); /* Select OPL4 ID register */
- udelay(10);
- tmp = inb(ioaddr - 7); /* Read it */
- udelay(10);
-
- if (tmp == 0x20) /* OPL4 should return 0x20 here */
- {
- detected_model = 4;
- outb((0xF8), ioaddr - 8); /* Select OPL4 FM mixer control */
- udelay(10);
- outb((0x1B), ioaddr - 7); /* Write value */
- udelay(10);
- }
- else
- { /* release OPL4 port */
- release_region(ioaddr - 8, 2);
- detected_model = 3;
- }
- }
- opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0);
- }
- for (i = 0; i < 9; i++)
- opl3_command(ioaddr, KEYON_BLOCK + i, 0); /*
- * Note off
- */
-
- opl3_command(ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT);
- opl3_command(ioaddr, PERCOSSION_REGISTER, 0x00); /*
- * Melodic mode.
- */
- return 1;
-cleanup_region:
- release_region(ioaddr, 4);
-cleanup_devc:
- kfree(devc);
- devc = NULL;
- return 0;
-}
-
-static int opl3_kill_note (int devno, int voice, int note, int velocity)
-{
- struct physical_voice_info *map;
-
- if (voice < 0 || voice >= devc->nr_voice)
- return 0;
-
- devc->v_alloc->map[voice] = 0;
-
- map = &pv_map[devc->lv_map[voice]];
- DEB(printk("Kill note %d\n", voice));
-
- if (map->voice_mode == 0)
- return 0;
-
- opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, devc->voc[voice].keyon_byte & ~0x20);
- devc->voc[voice].keyon_byte = 0;
- devc->voc[voice].bender = 0;
- devc->voc[voice].volume = 64;
- devc->voc[voice].panning = 0xffff; /* Not set */
- devc->voc[voice].bender_range = 200;
- devc->voc[voice].orig_freq = 0;
- devc->voc[voice].current_freq = 0;
- devc->voc[voice].mode = 0;
- return 0;
-}
-
-#define HIHAT 0
-#define CYMBAL 1
-#define TOMTOM 2
-#define SNARE 3
-#define BDRUM 4
-#define UNDEFINED TOMTOM
-#define DEFAULT TOMTOM
-
-static int store_instr(int instr_no, struct sbi_instrument *instr)
-{
- if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || devc->model != 2))
- printk(KERN_WARNING "FM warning: Invalid patch format field (key) 0x%x\n", instr->key);
- memcpy((char *) &(devc->i_map[instr_no]), (char *) instr, sizeof(*instr));
- return 0;
-}
-
-static int opl3_set_instr (int dev, int voice, int instr_no)
-{
- if (voice < 0 || voice >= devc->nr_voice)
- return 0;
- if (instr_no < 0 || instr_no >= SBFM_MAXINSTR)
- instr_no = 0; /* Acoustic piano (usually) */
-
- devc->act_i[voice] = &devc->i_map[instr_no];
- return 0;
-}
-
-/*
- * The next table looks magical, but it certainly is not. Its values have
- * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception
- * for i=0. This log-table converts a linear volume-scaling (0..127) to a
- * logarithmic scaling as present in the FM-synthesizer chips. so : Volume
- * 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative
- * volume -8 it was implemented as a table because it is only 128 bytes and
- * it saves a lot of log() calculations. (RH)
- */
-
-static char fm_volume_table[128] =
-{
- -64, -48, -40, -35, -32, -29, -27, -26,
- -24, -23, -21, -20, -19, -18, -18, -17,
- -16, -15, -15, -14, -13, -13, -12, -12,
- -11, -11, -10, -10, -10, -9, -9, -8,
- -8, -8, -7, -7, -7, -6, -6, -6,
- -5, -5, -5, -5, -4, -4, -4, -4,
- -3, -3, -3, -3, -2, -2, -2, -2,
- -2, -1, -1, -1, -1, 0, 0, 0,
- 0, 0, 0, 1, 1, 1, 1, 1,
- 1, 2, 2, 2, 2, 2, 2, 2,
- 3, 3, 3, 3, 3, 3, 3, 4,
- 4, 4, 4, 4, 4, 4, 4, 5,
- 5, 5, 5, 5, 5, 5, 5, 5,
- 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 8, 8, 8, 8, 8
-};
-
-static void calc_vol(unsigned char *regbyte, int volume, int main_vol)
-{
- int level = (~*regbyte & 0x3f);
-
- if (main_vol > 127)
- main_vol = 127;
- volume = (volume * main_vol) / 127;
-
- if (level)
- level += fm_volume_table[volume];
-
- if (level > 0x3f)
- level = 0x3f;
- if (level < 0)
- level = 0;
-
- *regbyte = (*regbyte & 0xc0) | (~level & 0x3f);
-}
-
-static void set_voice_volume(int voice, int volume, int main_vol)
-{
- unsigned char vol1, vol2, vol3, vol4;
- struct sbi_instrument *instr;
- struct physical_voice_info *map;
-
- if (voice < 0 || voice >= devc->nr_voice)
- return;
-
- map = &pv_map[devc->lv_map[voice]];
- instr = devc->act_i[voice];
-
- if (!instr)
- instr = &devc->i_map[0];
-
- if (instr->channel < 0)
- return;
-
- if (devc->voc[voice].mode == 0)
- return;
-
- if (devc->voc[voice].mode == 2)
- {
- vol1 = instr->operators[2];
- vol2 = instr->operators[3];
- if ((instr->operators[10] & 0x01))
- {
- calc_vol(&vol1, volume, main_vol);
- calc_vol(&vol2, volume, main_vol);
- }
- else
- {
- calc_vol(&vol2, volume, main_vol);
- }
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1);
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2);
- }
- else
- { /*
- * 4 OP voice
- */
- int connection;
-
- vol1 = instr->operators[2];
- vol2 = instr->operators[3];
- vol3 = instr->operators[OFFS_4OP + 2];
- vol4 = instr->operators[OFFS_4OP + 3];
-
- /*
- * The connection method for 4 OP devc->voc is defined by the rightmost
- * bits at the offsets 10 and 10+OFFS_4OP
- */
-
- connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
-
- switch (connection)
- {
- case 0:
- calc_vol(&vol4, volume, main_vol);
- break;
-
- case 1:
- calc_vol(&vol2, volume, main_vol);
- calc_vol(&vol4, volume, main_vol);
- break;
-
- case 2:
- calc_vol(&vol1, volume, main_vol);
- calc_vol(&vol4, volume, main_vol);
- break;
-
- case 3:
- calc_vol(&vol1, volume, main_vol);
- calc_vol(&vol3, volume, main_vol);
- calc_vol(&vol4, volume, main_vol);
- break;
-
- default:
- ;
- }
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1);
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2);
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], vol3);
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], vol4);
- }
-}
-
-static int opl3_start_note (int dev, int voice, int note, int volume)
-{
- unsigned char data, fpc;
- int block, fnum, freq, voice_mode, pan;
- struct sbi_instrument *instr;
- struct physical_voice_info *map;
-
- if (voice < 0 || voice >= devc->nr_voice)
- return 0;
-
- map = &pv_map[devc->lv_map[voice]];
- pan = devc->voc[voice].panning;
-
- if (map->voice_mode == 0)
- return 0;
-
- if (note == 255) /*
- * Just change the volume
- */
- {
- set_voice_volume(voice, volume, devc->voc[voice].volume);
- return 0;
- }
-
- /*
- * Kill previous note before playing
- */
-
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], 0xff); /*
- * Carrier
- * volume to
- * min
- */
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], 0xff); /*
- * Modulator
- * volume to
- */
-
- if (map->voice_mode == 4)
- {
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], 0xff);
- opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], 0xff);
- }
-
- opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00); /*
- * Note
- * off
- */
-
- instr = devc->act_i[voice];
-
- if (!instr)
- instr = &devc->i_map[0];
-
- if (instr->channel < 0)
- {
- printk(KERN_WARNING "opl3: Initializing voice %d with undefined instrument\n", voice);
- return 0;
- }
-
- if (map->voice_mode == 2 && instr->key == OPL3_PATCH)
- return 0; /*
- * Cannot play
- */
-
- voice_mode = map->voice_mode;
-
- if (voice_mode == 4)
- {
- int voice_shift;
-
- voice_shift = (map->ioaddr == devc->left_io) ? 0 : 3;
- voice_shift += map->voice_num;
-
- if (instr->key != OPL3_PATCH) /*
- * Just 2 OP patch
- */
- {
- voice_mode = 2;
- devc->cmask &= ~(1 << voice_shift);
- }
- else
- {
- devc->cmask |= (1 << voice_shift);
- }
-
- opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask);
- }
-
- /*
- * Set Sound Characteristics
- */
-
- opl3_command(map->ioaddr, AM_VIB + map->op[0], instr->operators[0]);
- opl3_command(map->ioaddr, AM_VIB + map->op[1], instr->operators[1]);
-
- /*
- * Set Attack/Decay
- */
-
- opl3_command(map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]);
- opl3_command(map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]);
-
- /*
- * Set Sustain/Release
- */
-
- opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]);
- opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]);
-
- /*
- * Set Wave Select
- */
-
- opl3_command(map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]);
- opl3_command(map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]);
-
- /*
- * Set Feedback/Connection
- */
-
- fpc = instr->operators[10];
-
- if (pan != 0xffff)
- {
- fpc &= ~STEREO_BITS;
- if (pan < -64)
- fpc |= VOICE_TO_LEFT;
- else
- if (pan > 64)
- fpc |= VOICE_TO_RIGHT;
- else
- fpc |= (VOICE_TO_LEFT | VOICE_TO_RIGHT);
- }
-
- if (!(fpc & 0x30))
- fpc |= 0x30; /*
- * Ensure that at least one chn is enabled
- */
- opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num, fpc);
-
- /*
- * If the voice is a 4 OP one, initialize the operators 3 and 4 also
- */
-
- if (voice_mode == 4)
- {
- /*
- * Set Sound Characteristics
- */
-
- opl3_command(map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]);
- opl3_command(map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]);
-
- /*
- * Set Attack/Decay
- */
-
- opl3_command(map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]);
- opl3_command(map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]);
-
- /*
- * Set Sustain/Release
- */
-
- opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]);
- opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]);
-
- /*
- * Set Wave Select
- */
-
- opl3_command(map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]);
- opl3_command(map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]);
-
- /*
- * Set Feedback/Connection
- */
-
- fpc = instr->operators[OFFS_4OP + 10];
- if (!(fpc & 0x30))
- fpc |= 0x30; /*
- * Ensure that at least one chn is enabled
- */
- opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc);
- }
-
- devc->voc[voice].mode = voice_mode;
- set_voice_volume(voice, volume, devc->voc[voice].volume);
-
- freq = devc->voc[voice].orig_freq = note_to_freq(note) / 1000;
-
- /*
- * Since the pitch bender may have been set before playing the note, we
- * have to calculate the bending now.
- */
-
- freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0);
- devc->voc[voice].current_freq = freq;
-
- freq_to_fnum(freq, &block, &fnum);
-
- /*
- * Play note
- */
-
- data = fnum & 0xff; /*
- * Least significant bits of fnumber
- */
- opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data);
-
- data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
- devc->voc[voice].keyon_byte = data;
- opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data);
- if (voice_mode == 4)
- opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data);
-
- return 0;
-}
-
-static void freq_to_fnum (int freq, int *block, int *fnum)
-{
- int f, octave;
-
- /*
- * Converts the note frequency to block and fnum values for the FM chip
- */
- /*
- * First try to compute the block -value (octave) where the note belongs
- */
-
- f = freq;
-
- octave = 5;
-
- if (f == 0)
- octave = 0;
- else if (f < 261)
- {
- while (f < 261)
- {
- octave--;
- f <<= 1;
- }
- }
- else if (f > 493)
- {
- while (f > 493)
- {
- octave++;
- f >>= 1;
- }
- }
-
- if (octave > 7)
- octave = 7;
-
- *fnum = freq * (1 << (20 - octave)) / 49716;
- *block = octave;
-}
-
-static void opl3_command (int io_addr, unsigned int addr, unsigned int val)
-{
- int i;
-
- /*
- * The original 2-OP synth requires a quite long delay after writing to a
- * register. The OPL-3 survives with just two INBs
- */
-
- outb(((unsigned char) (addr & 0xff)), io_addr);
-
- if (devc->model != 2)
- udelay(10);
- else
- for (i = 0; i < 2; i++)
- inb(io_addr);
-
- outb(((unsigned char) (val & 0xff)), io_addr + 1);
-
- if (devc->model != 2)
- udelay(30);
- else
- for (i = 0; i < 2; i++)
- inb(io_addr);
-}
-
-static void opl3_reset(int devno)
-{
- int i;
-
- for (i = 0; i < 18; i++)
- devc->lv_map[i] = i;
-
- for (i = 0; i < devc->nr_voice; i++)
- {
- opl3_command(pv_map[devc->lv_map[i]].ioaddr,
- KSL_LEVEL + pv_map[devc->lv_map[i]].op[0], 0xff);
-
- opl3_command(pv_map[devc->lv_map[i]].ioaddr,
- KSL_LEVEL + pv_map[devc->lv_map[i]].op[1], 0xff);
-
- if (pv_map[devc->lv_map[i]].voice_mode == 4)
- {
- opl3_command(pv_map[devc->lv_map[i]].ioaddr,
- KSL_LEVEL + pv_map[devc->lv_map[i]].op[2], 0xff);
-
- opl3_command(pv_map[devc->lv_map[i]].ioaddr,
- KSL_LEVEL + pv_map[devc->lv_map[i]].op[3], 0xff);
- }
-
- opl3_kill_note(devno, i, 0, 64);
- }
-
- if (devc->model == 2)
- {
- devc->v_alloc->max_voice = devc->nr_voice = 18;
-
- for (i = 0; i < 18; i++)
- pv_map[i].voice_mode = 2;
-
- }
-}
-
-static int opl3_open(int dev, int mode)
-{
- int i;
-
- if (devc->busy)
- return -EBUSY;
- devc->busy = 1;
-
- devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9;
- devc->v_alloc->timestamp = 0;
-
- for (i = 0; i < 18; i++)
- {
- devc->v_alloc->map[i] = 0;
- devc->v_alloc->alloc_times[i] = 0;
- }
-
- devc->cmask = 0x00; /*
- * Just 2 OP mode
- */
- if (devc->model == 2)
- opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask);
- return 0;
-}
-
-static void opl3_close(int dev)
-{
- devc->busy = 0;
- devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9;
-
- devc->fm_info.nr_drums = 0;
- devc->fm_info.perc_mode = 0;
-
- opl3_reset(dev);
-}
-
-static void opl3_hw_control(int dev, unsigned char *event)
-{
-}
-
-static int opl3_load_patch(int dev, int format, const char __user *addr,
- int offs, int count, int pmgr_flag)
-{
- struct sbi_instrument ins;
-
- if (count <sizeof(ins))
- {
- printk(KERN_WARNING "FM Error: Patch record too short\n");
- return -EINVAL;
- }
-
- /*
- * What the fuck is going on here? We leave junk in the beginning
- * of ins and then check the field pretty close to that beginning?
- */
- if(copy_from_user(&((char *) &ins)[offs], addr + offs, sizeof(ins) - offs))
- return -EFAULT;
-
- if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
- {
- printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel);
- return -EINVAL;
- }
- ins.key = format;
-
- return store_instr(ins.channel, &ins);
-}
-
-static void opl3_panning(int dev, int voice, int value)
-{
- devc->voc[voice].panning = value;
-}
-
-static void opl3_volume_method(int dev, int mode)
-{
-}
-
-#define SET_VIBRATO(cell) { \
- tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \
- if (pressure > 110) \
- tmp |= 0x40; /* Vibrato on */ \
- opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);}
-
-static void opl3_aftertouch(int dev, int voice, int pressure)
-{
- int tmp;
- struct sbi_instrument *instr;
- struct physical_voice_info *map;
-
- if (voice < 0 || voice >= devc->nr_voice)
- return;
-
- map = &pv_map[devc->lv_map[voice]];
-
- DEB(printk("Aftertouch %d\n", voice));
-
- if (map->voice_mode == 0)
- return;
-
- /*
- * Adjust the amount of vibrato depending the pressure
- */
-
- instr = devc->act_i[voice];
-
- if (!instr)
- instr = &devc->i_map[0];
-
- if (devc->voc[voice].mode == 4)
- {
- int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01);
-
- switch (connection)
- {
- case 0:
- SET_VIBRATO(4);
- break;
-
- case 1:
- SET_VIBRATO(2);
- SET_VIBRATO(4);
- break;
-
- case 2:
- SET_VIBRATO(1);
- SET_VIBRATO(4);
- break;
-
- case 3:
- SET_VIBRATO(1);
- SET_VIBRATO(3);
- SET_VIBRATO(4);
- break;
-
- }
- /*
- * Not implemented yet
- */
- }
- else
- {
- SET_VIBRATO(1);
-
- if ((instr->operators[10] & 0x01)) /*
- * Additive synthesis
- */
- SET_VIBRATO(2);
- }
-}
-
-#undef SET_VIBRATO
-
-static void bend_pitch(int dev, int voice, int value)
-{
- unsigned char data;
- int block, fnum, freq;
- struct physical_voice_info *map;
-
- map = &pv_map[devc->lv_map[voice]];
-
- if (map->voice_mode == 0)
- return;
-
- devc->voc[voice].bender = value;
- if (!value)
- return;
- if (!(devc->voc[voice].keyon_byte & 0x20))
- return; /*
- * Not keyed on
- */
-
- freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0);
- devc->voc[voice].current_freq = freq;
-
- freq_to_fnum(freq, &block, &fnum);
-
- data = fnum & 0xff; /*
- * Least significant bits of fnumber
- */
- opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data);
-
- data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3);
- devc->voc[voice].keyon_byte = data;
- opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data);
-}
-
-static void opl3_controller (int dev, int voice, int ctrl_num, int value)
-{
- if (voice < 0 || voice >= devc->nr_voice)
- return;
-
- switch (ctrl_num)
- {
- case CTRL_PITCH_BENDER:
- bend_pitch(dev, voice, value);
- break;
-
- case CTRL_PITCH_BENDER_RANGE:
- devc->voc[voice].bender_range = value;
- break;
-
- case CTL_MAIN_VOLUME:
- devc->voc[voice].volume = value / 128;
- break;
-
- case CTL_PAN:
- devc->voc[voice].panning = (value * 2) - 128;
- break;
- }
-}
-
-static void opl3_bender(int dev, int voice, int value)
-{
- if (voice < 0 || voice >= devc->nr_voice)
- return;
-
- bend_pitch(dev, voice, value - 8192);
-}
-
-static int opl3_alloc_voice(int dev, int chn, int note, struct voice_alloc_info *alloc)
-{
- int i, p, best, first, avail, best_time = 0x7fffffff;
- struct sbi_instrument *instr;
- int is4op;
- int instr_no;
-
- if (chn < 0 || chn > 15)
- instr_no = 0;
- else
- instr_no = devc->chn_info[chn].pgm_num;
-
- instr = &devc->i_map[instr_no];
- if (instr->channel < 0 || /* Instrument not loaded */
- devc->nr_voice != 12) /* Not in 4 OP mode */
- is4op = 0;
- else if (devc->nr_voice == 12) /* 4 OP mode */
- is4op = (instr->key == OPL3_PATCH);
- else
- is4op = 0;
-
- if (is4op)
- {
- first = p = 0;
- avail = 6;
- }
- else
- {
- if (devc->nr_voice == 12) /* 4 OP mode. Use the '2 OP only' operators first */
- first = p = 6;
- else
- first = p = 0;
- avail = devc->nr_voice;
- }
-
- /*
- * Now try to find a free voice
- */
- best = first;
-
- for (i = 0; i < avail; i++)
- {
- if (alloc->map[p] == 0)
- {
- return p;
- }
- if (alloc->alloc_times[p] < best_time) /* Find oldest playing note */
- {
- best_time = alloc->alloc_times[p];
- best = p;
- }
- p = (p + 1) % avail;
- }
-
- /*
- * Insert some kind of priority mechanism here.
- */
-
- if (best < 0)
- best = 0;
- if (best > devc->nr_voice)
- best -= devc->nr_voice;
-
- return best; /* All devc->voc in use. Select the first one. */
-}
-
-static void opl3_setup_voice(int dev, int voice, int chn)
-{
- struct channel_info *info =
- &synth_devs[dev]->chn_info[chn];
-
- opl3_set_instr(dev, voice, info->pgm_num);
-
- devc->voc[voice].bender = 0;
- devc->voc[voice].bender_range = info->bender_range;
- devc->voc[voice].volume = info->controllers[CTL_MAIN_VOLUME];
- devc->voc[voice].panning = (info->controllers[CTL_PAN] * 2) - 128;
-}
-
-static struct synth_operations opl3_operations =
-{
- .owner = THIS_MODULE,
- .id = "OPL",
- .info = NULL,
- .midi_dev = 0,
- .synth_type = SYNTH_TYPE_FM,
- .synth_subtype = FM_TYPE_ADLIB,
- .open = opl3_open,
- .close = opl3_close,
- .ioctl = opl3_ioctl,
- .kill_note = opl3_kill_note,
- .start_note = opl3_start_note,
- .set_instr = opl3_set_instr,
- .reset = opl3_reset,
- .hw_control = opl3_hw_control,
- .load_patch = opl3_load_patch,
- .aftertouch = opl3_aftertouch,
- .controller = opl3_controller,
- .panning = opl3_panning,
- .volume_method = opl3_volume_method,
- .bender = opl3_bender,
- .alloc_voice = opl3_alloc_voice,
- .setup_voice = opl3_setup_voice
-};
-
-static int opl3_init(int ioaddr, struct module *owner)
-{
- int i;
- int me;
-
- if (devc == NULL)
- {
- printk(KERN_ERR "opl3: Device control structure not initialized.\n");
- return -1;
- }
-
- if ((me = sound_alloc_synthdev()) == -1)
- {
- printk(KERN_WARNING "opl3: Too many synthesizers\n");
- return -1;
- }
-
- devc->nr_voice = 9;
-
- devc->fm_info.device = 0;
- devc->fm_info.synth_type = SYNTH_TYPE_FM;
- devc->fm_info.synth_subtype = FM_TYPE_ADLIB;
- devc->fm_info.perc_mode = 0;
- devc->fm_info.nr_voices = 9;
- devc->fm_info.nr_drums = 0;
- devc->fm_info.instr_bank_size = SBFM_MAXINSTR;
- devc->fm_info.capabilities = 0;
- devc->left_io = ioaddr;
- devc->right_io = ioaddr + 2;
-
- if (detected_model <= 2)
- devc->model = 1;
- else
- {
- devc->model = 2;
- if (detected_model == 4)
- devc->is_opl4 = 1;
- }
-
- opl3_operations.info = &devc->fm_info;
-
- synth_devs[me] = &opl3_operations;
-
- if (owner)
- synth_devs[me]->owner = owner;
-
- sequencer_init();
- devc->v_alloc = &opl3_operations.alloc;
- devc->chn_info = &opl3_operations.chn_info[0];
-
- if (devc->model == 2)
- {
- if (devc->is_opl4)
- strcpy(devc->fm_info.name, "Yamaha OPL4/OPL3 FM");
- else
- strcpy(devc->fm_info.name, "Yamaha OPL3");
-
- devc->v_alloc->max_voice = devc->nr_voice = 18;
- devc->fm_info.nr_drums = 0;
- devc->fm_info.synth_subtype = FM_TYPE_OPL3;
- devc->fm_info.capabilities |= SYNTH_CAP_OPL3;
-
- for (i = 0; i < 18; i++)
- {
- if (pv_map[i].ioaddr == USE_LEFT)
- pv_map[i].ioaddr = devc->left_io;
- else
- pv_map[i].ioaddr = devc->right_io;
- }
- opl3_command(devc->right_io, OPL3_MODE_REGISTER, OPL3_ENABLE);
- opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x00);
- }
- else
- {
- strcpy(devc->fm_info.name, "Yamaha OPL2");
- devc->v_alloc->max_voice = devc->nr_voice = 9;
- devc->fm_info.nr_drums = 0;
-
- for (i = 0; i < 18; i++)
- pv_map[i].ioaddr = devc->left_io;
- };
- conf_printf2(devc->fm_info.name, ioaddr, 0, -1, -1);
-
- for (i = 0; i < SBFM_MAXINSTR; i++)
- devc->i_map[i].channel = -1;
-
- return me;
-}
-
-static int me;
-
-static int io = -1;
-
-module_param(io, int, 0);
-
-static int __init init_opl3 (void)
-{
- printk(KERN_INFO "YM3812 and OPL-3 driver Copyright (C) by Hannu Savolainen, Rob Hooft 1993-1996\n");
-
- if (io != -1) /* User loading pure OPL3 module */
- {
- if (!opl3_detect(io))
- {
- return -ENODEV;
- }
-
- me = opl3_init(io, THIS_MODULE);
- }
-
- return 0;
-}
-
-static void __exit cleanup_opl3(void)
-{
- if (devc && io != -1)
- {
- if (devc->base) {
- release_region(devc->base,4);
- if (devc->is_opl4)
- release_region(devc->base - 8, 2);
- }
- kfree(devc);
- devc = NULL;
- sound_unload_synthdev(me);
- }
-}
-
-module_init(init_opl3);
-module_exit(cleanup_opl3);
-
-#ifndef MODULE
-static int __init setup_opl3(char *str)
-{
- /* io */
- int ints[2];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
-
- return 1;
-}
-
-__setup("opl3=", setup_opl3);
-#endif
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/opl3_hw.h b/sound/oss/opl3_hw.h
deleted file mode 100644
index 8b11c893e869..000000000000
--- a/sound/oss/opl3_hw.h
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * opl3_hw.h - Definitions of the OPL-3 registers
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * The OPL-3 mode is switched on by writing 0x01, to the offset 5
- * of the right side.
- *
- * Another special register at the right side is at offset 4. It contains
- * a bit mask defining which voices are used as 4 OP voices.
- *
- * The percussive mode is implemented in the left side only.
- *
- * With the above exceptions the both sides can be operated independently.
- *
- * A 4 OP voice can be created by setting the corresponding
- * bit at offset 4 of the right side.
- *
- * For example setting the rightmost bit (0x01) changes the
- * first voice on the right side to the 4 OP mode. The fourth
- * voice is made inaccessible.
- *
- * If a voice is set to the 2 OP mode, it works like 2 OP modes
- * of the original YM3812 (AdLib). In addition the voice can
- * be connected the left, right or both stereo channels. It can
- * even be left unconnected. This works with 4 OP voices also.
- *
- * The stereo connection bits are located in the FEEDBACK_CONNECTION
- * register of the voice (0xC0-0xC8). In 4 OP voices these bits are
- * in the second half of the voice.
- */
-
-/*
- * Register numbers for the global registers
- */
-
-#define TEST_REGISTER 0x01
-#define ENABLE_WAVE_SELECT 0x20
-
-#define TIMER1_REGISTER 0x02
-#define TIMER2_REGISTER 0x03
-#define TIMER_CONTROL_REGISTER 0x04 /* Left side */
-#define IRQ_RESET 0x80
-#define TIMER1_MASK 0x40
-#define TIMER2_MASK 0x20
-#define TIMER1_START 0x01
-#define TIMER2_START 0x02
-
-#define CONNECTION_SELECT_REGISTER 0x04 /* Right side */
-#define RIGHT_4OP_0 0x01
-#define RIGHT_4OP_1 0x02
-#define RIGHT_4OP_2 0x04
-#define LEFT_4OP_0 0x08
-#define LEFT_4OP_1 0x10
-#define LEFT_4OP_2 0x20
-
-#define OPL3_MODE_REGISTER 0x05 /* Right side */
-#define OPL3_ENABLE 0x01
-#define OPL4_ENABLE 0x02
-
-#define KBD_SPLIT_REGISTER 0x08 /* Left side */
-#define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */
-#define KEYBOARD_SPLIT 0x40
-
-#define PERCOSSION_REGISTER 0xbd /* Left side only */
-#define TREMOLO_DEPTH 0x80
-#define VIBRATO_DEPTH 0x40
-#define PERCOSSION_ENABLE 0x20
-#define BASSDRUM_ON 0x10
-#define SNAREDRUM_ON 0x08
-#define TOMTOM_ON 0x04
-#define CYMBAL_ON 0x02
-#define HIHAT_ON 0x01
-
-/*
- * Offsets to the register banks for operators. To get the
- * register number just add the operator offset to the bank offset
- *
- * AM/VIB/EG/KSR/Multiple (0x20 to 0x35)
- */
-#define AM_VIB 0x20
-#define TREMOLO_ON 0x80
-#define VIBRATO_ON 0x40
-#define SUSTAIN_ON 0x20
-#define KSR 0x10 /* Key scaling rate */
-#define MULTIPLE_MASK 0x0f /* Frequency multiplier */
-
- /*
- * KSL/Total level (0x40 to 0x55)
- */
-#define KSL_LEVEL 0x40
-#define KSL_MASK 0xc0 /* Envelope scaling bits */
-#define TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */
-
-/*
- * Attack / Decay rate (0x60 to 0x75)
- */
-#define ATTACK_DECAY 0x60
-#define ATTACK_MASK 0xf0
-#define DECAY_MASK 0x0f
-
-/*
- * Sustain level / Release rate (0x80 to 0x95)
- */
-#define SUSTAIN_RELEASE 0x80
-#define SUSTAIN_MASK 0xf0
-#define RELEASE_MASK 0x0f
-
-/*
- * Wave select (0xE0 to 0xF5)
- */
-#define WAVE_SELECT 0xe0
-
-/*
- * Offsets to the register banks for voices. Just add to the
- * voice number to get the register number.
- *
- * F-Number low bits (0xA0 to 0xA8).
- */
-#define FNUM_LOW 0xa0
-
-/*
- * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8)
- */
-#define KEYON_BLOCK 0xb0
-#define KEYON_BIT 0x20
-#define BLOCKNUM_MASK 0x1c
-#define FNUM_HIGH_MASK 0x03
-
-/*
- * Feedback / Connection (0xc0 to 0xc8)
- *
- * These registers have two new bits when the OPL-3 mode
- * is selected. These bits controls connecting the voice
- * to the stereo channels. For 4 OP voices this bit is
- * defined in the second half of the voice (add 3 to the
- * register offset).
- *
- * For 4 OP voices the connection bit is used in the
- * both halves (gives 4 ways to connect the operators).
- */
-#define FEEDBACK_CONNECTION 0xc0
-#define FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */
-#define CONNECTION_BIT 0x01
-/*
- * In the 4 OP mode there is four possible configurations how the
- * operators can be connected together (in 2 OP modes there is just
- * AM or FM). The 4 OP connection mode is defined by the rightmost
- * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halves.
- *
- * First half Second half Mode
- *
- * +---+
- * v |
- * 0 0 >+-1-+--2--3--4-->
- *
- *
- *
- * +---+
- * | |
- * 0 1 >+-1-+--2-+
- * |->
- * >--3----4-+
- *
- * +---+
- * | |
- * 1 0 >+-1-+-----+
- * |->
- * >--2--3--4-+
- *
- * +---+
- * | |
- * 1 1 >+-1-+--+
- * |
- * >--2--3-+->
- * |
- * >--4----+
- */
-#define STEREO_BITS 0x30 /* OPL-3 only */
-#define VOICE_TO_LEFT 0x10
-#define VOICE_TO_RIGHT 0x20
-
-/*
- * Definition table for the physical voices
- */
-
-struct physical_voice_info {
- unsigned char voice_num;
- unsigned char voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */
- unsigned short ioaddr; /* I/O port (left or right side) */
- unsigned char op[4]; /* Operator offsets */
- };
-
-/*
- * There is 18 possible 2 OP voices
- * (9 in the left and 9 in the right).
- * The first OP is the modulator and 2nd is the carrier.
- *
- * The first three voices in the both sides may be connected
- * with another voice to a 4 OP voice. For example voice 0
- * can be connected with voice 3. The operators of voice 3 are
- * used as operators 3 and 4 of the new 4 OP voice.
- * In this case the 2 OP voice number 0 is the 'first half' and
- * voice 3 is the second.
- */
-
-#define USE_LEFT 0
-#define USE_RIGHT 1
-
-static struct physical_voice_info pv_map[18] =
-{
-/* No Mode Side OP1 OP2 OP3 OP4 */
-/* --------------------------------------------------- */
- { 0, 2, USE_LEFT, {0x00, 0x03, 0x08, 0x0b}},
- { 1, 2, USE_LEFT, {0x01, 0x04, 0x09, 0x0c}},
- { 2, 2, USE_LEFT, {0x02, 0x05, 0x0a, 0x0d}},
-
- { 3, 2, USE_LEFT, {0x08, 0x0b, 0x00, 0x00}},
- { 4, 2, USE_LEFT, {0x09, 0x0c, 0x00, 0x00}},
- { 5, 2, USE_LEFT, {0x0a, 0x0d, 0x00, 0x00}},
-
- { 6, 2, USE_LEFT, {0x10, 0x13, 0x00, 0x00}}, /* Used by percussive voices */
- { 7, 2, USE_LEFT, {0x11, 0x14, 0x00, 0x00}}, /* if the percussive mode */
- { 8, 2, USE_LEFT, {0x12, 0x15, 0x00, 0x00}}, /* is selected */
-
- { 0, 2, USE_RIGHT, {0x00, 0x03, 0x08, 0x0b}},
- { 1, 2, USE_RIGHT, {0x01, 0x04, 0x09, 0x0c}},
- { 2, 2, USE_RIGHT, {0x02, 0x05, 0x0a, 0x0d}},
-
- { 3, 2, USE_RIGHT, {0x08, 0x0b, 0x00, 0x00}},
- { 4, 2, USE_RIGHT, {0x09, 0x0c, 0x00, 0x00}},
- { 5, 2, USE_RIGHT, {0x0a, 0x0d, 0x00, 0x00}},
-
- { 6, 2, USE_RIGHT, {0x10, 0x13, 0x00, 0x00}},
- { 7, 2, USE_RIGHT, {0x11, 0x14, 0x00, 0x00}},
- { 8, 2, USE_RIGHT, {0x12, 0x15, 0x00, 0x00}}
-};
-/*
- * DMA buffer calls
- */
diff --git a/sound/oss/os.h b/sound/oss/os.h
deleted file mode 100644
index a1a962d7f67d..000000000000
--- a/sound/oss/os.h
+++ /dev/null
@@ -1,46 +0,0 @@
-#define ALLOW_SELECT
-#undef NO_INLINE_ASM
-#define SHORT_BANNERS
-#define MANUAL_PNP
-#undef DO_TIMINGS
-
-#include <linux/module.h>
-
-#ifdef __KERNEL__
-#include <linux/string.h>
-#include <linux/fs.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <asm/param.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <asm/page.h>
-#include <asm/system.h>
-#include <linux/vmalloc.h>
-#include <asm/uaccess.h>
-#include <linux/poll.h>
-#include <linux/pci.h>
-#endif
-
-#include <linux/soundcard.h>
-
-#define FALSE 0
-#define TRUE 1
-
-extern int sound_alloc_dma(int chn, char *deviceID);
-extern int sound_open_dma(int chn, char *deviceID);
-extern void sound_free_dma(int chn);
-extern void sound_close_dma(int chn);
-
-extern void reprogram_timer(void);
-
-#define USE_AUTOINIT_DMA
-
-extern void *sound_mem_blocks[1024];
-extern int sound_nblocks;
-
-#undef PSEUDO_DMA_AUTOINIT
-#define ALLOW_BUFFER_MAPPING
-
-extern const struct file_operations oss_sound_fops;
diff --git a/sound/oss/pas2.h b/sound/oss/pas2.h
deleted file mode 100644
index fa12c55f560e..000000000000
--- a/sound/oss/pas2.h
+++ /dev/null
@@ -1,17 +0,0 @@
-
-/* From pas_card.c */
-int pas_set_intr(int mask);
-int pas_remove_intr(int mask);
-unsigned char pas_read(int ioaddr);
-void pas_write(unsigned char data, int ioaddr);
-
-/* From pas_audio.c */
-void pas_pcm_interrupt(unsigned char status, int cause);
-void pas_pcm_init(struct address_info *hw_config);
-
-/* From pas_mixer.c */
-int pas_init_mixer(void);
-
-/* From pas_midi.c */
-void pas_midi_init(void);
-void pas_midi_interrupt(void);
diff --git a/sound/oss/pas2_card.c b/sound/oss/pas2_card.c
deleted file mode 100644
index 7f377ec3486d..000000000000
--- a/sound/oss/pas2_card.c
+++ /dev/null
@@ -1,455 +0,0 @@
-/*
- * sound/oss/pas2_card.c
- *
- * Detection routine for the Pro Audio Spectrum cards.
- */
-
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include "sound_config.h"
-
-#include "pas2.h"
-#include "sb.h"
-
-static unsigned char dma_bits[] = {
- 4, 1, 2, 3, 0, 5, 6, 7
-};
-
-static unsigned char irq_bits[] = {
- 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11
-};
-
-static unsigned char sb_irq_bits[] = {
- 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20,
- 0x00, 0x08, 0x28, 0x30, 0x38, 0, 0
-};
-
-static unsigned char sb_dma_bits[] = {
- 0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0
-};
-
-/*
- * The Address Translation code is used to convert I/O register addresses to
- * be relative to the given base -register
- */
-
-int pas_translate_code = 0;
-static int pas_intr_mask;
-static int pas_irq;
-static int pas_sb_base;
-DEFINE_SPINLOCK(pas_lock);
-#ifndef CONFIG_PAS_JOYSTICK
-static int joystick;
-#else
-static int joystick = 1;
-#endif
-#ifdef SYMPHONY_PAS
-static int symphony = 1;
-#else
-static int symphony;
-#endif
-#ifdef BROKEN_BUS_CLOCK
-static int broken_bus_clock = 1;
-#else
-static int broken_bus_clock;
-#endif
-
-static struct address_info cfg;
-static struct address_info cfg2;
-
-char pas_model = 0;
-static char *pas_model_names[] = {
- "",
- "Pro AudioSpectrum+",
- "CDPC",
- "Pro AudioSpectrum 16",
- "Pro AudioSpectrum 16D"
-};
-
-/*
- * pas_read() and pas_write() are equivalents of inb and outb
- * These routines perform the I/O address translation required
- * to support other than the default base address
- */
-
-extern void mix_write(unsigned char data, int ioaddr);
-
-unsigned char pas_read(int ioaddr)
-{
- return inb(ioaddr + pas_translate_code);
-}
-
-void pas_write(unsigned char data, int ioaddr)
-{
- outb((data), ioaddr + pas_translate_code);
-}
-
-/******************* Begin of the Interrupt Handler ********************/
-
-static irqreturn_t pasintr(int irq, void *dev_id)
-{
- int status;
-
- status = pas_read(0x0B89);
- pas_write(status, 0x0B89); /* Clear interrupt */
-
- if (status & 0x08)
- {
- pas_pcm_interrupt(status, 1);
- status &= ~0x08;
- }
- if (status & 0x10)
- {
- pas_midi_interrupt();
- status &= ~0x10;
- }
- return IRQ_HANDLED;
-}
-
-int pas_set_intr(int mask)
-{
- if (!mask)
- return 0;
-
- pas_intr_mask |= mask;
-
- pas_write(pas_intr_mask, 0x0B8B);
- return 0;
-}
-
-int pas_remove_intr(int mask)
-{
- if (!mask)
- return 0;
-
- pas_intr_mask &= ~mask;
- pas_write(pas_intr_mask, 0x0B8B);
-
- return 0;
-}
-
-/******************* End of the Interrupt handler **********************/
-
-/******************* Begin of the Initialization Code ******************/
-
-static int __init config_pas_hw(struct address_info *hw_config)
-{
- char ok = 1;
- unsigned int_ptrs; /* scsi/sound interrupt pointers */
-
- pas_irq = hw_config->irq;
-
- pas_write(0x00, 0x0B8B);
- pas_write(0x36, 0x138B);
- pas_write(0x36, 0x1388);
- pas_write(0, 0x1388);
- pas_write(0x74, 0x138B);
- pas_write(0x74, 0x1389);
- pas_write(0, 0x1389);
-
- pas_write(0x80 | 0x40 | 0x20 | 1, 0x0B8A);
- pas_write(0x80 | 0x20 | 0x10 | 0x08 | 0x01, 0xF8A);
- pas_write(0x01 | 0x02 | 0x04 | 0x10 /*
- * |
- * 0x80
- */ , 0xB88);
-
- pas_write(0x80 | (joystick ? 0x40 : 0), 0xF388);
-
- if (pas_irq < 0 || pas_irq > 15)
- {
- printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq);
- hw_config->irq=-1;
- ok = 0;
- }
- else
- {
- int_ptrs = pas_read(0xF38A);
- int_ptrs = (int_ptrs & 0xf0) | irq_bits[pas_irq];
- pas_write(int_ptrs, 0xF38A);
- if (!irq_bits[pas_irq])
- {
- printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq);
- hw_config->irq=-1;
- ok = 0;
- }
- else
- {
- if (request_irq(pas_irq, pasintr, 0, "PAS16",hw_config) < 0) {
- printk(KERN_ERR "PAS16: Cannot allocate IRQ %d\n",pas_irq);
- hw_config->irq=-1;
- ok = 0;
- }
- }
- }
-
- if (hw_config->dma < 0 || hw_config->dma > 7)
- {
- printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma);
- hw_config->dma=-1;
- ok = 0;
- }
- else
- {
- pas_write(dma_bits[hw_config->dma], 0xF389);
- if (!dma_bits[hw_config->dma])
- {
- printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma);
- hw_config->dma=-1;
- ok = 0;
- }
- else
- {
- if (sound_alloc_dma(hw_config->dma, "PAS16"))
- {
- printk(KERN_ERR "pas2_card.c: Can't allocate DMA channel\n");
- hw_config->dma=-1;
- ok = 0;
- }
- }
- }
-
- /*
- * This fixes the timing problems of the PAS due to the Symphony chipset
- * as per Media Vision. Only define this if your PAS doesn't work correctly.
- */
-
- if(symphony)
- {
- outb((0x05), 0xa8);
- outb((0x60), 0xa9);
- }
-
- if(broken_bus_clock)
- pas_write(0x01 | 0x10 | 0x20 | 0x04, 0x8388);
- else
- /*
- * pas_write(0x01, 0x8388);
- */
- pas_write(0x01 | 0x10 | 0x20, 0x8388);
-
- pas_write(0x18, 0x838A); /* ??? */
- pas_write(0x20 | 0x01, 0x0B8A); /* Mute off, filter = 17.897 kHz */
- pas_write(8, 0xBF8A);
-
- mix_write(0x80 | 5, 0x078B);
- mix_write(5, 0x078B);
-
- {
- struct address_info *sb_config;
-
- sb_config = &cfg2;
- if (sb_config->io_base)
- {
- unsigned char irq_dma;
-
- /*
- * Turn on Sound Blaster compatibility
- * bit 1 = SB emulation
- * bit 0 = MPU401 emulation (CDPC only :-( )
- */
-
- pas_write(0x02, 0xF788);
-
- /*
- * "Emulation address"
- */
-
- pas_write((sb_config->io_base >> 4) & 0x0f, 0xF789);
- pas_sb_base = sb_config->io_base;
-
- if (!sb_dma_bits[sb_config->dma])
- printk(KERN_ERR "PAS16 Warning: Invalid SB DMA %d\n\n", sb_config->dma);
-
- if (!sb_irq_bits[sb_config->irq])
- printk(KERN_ERR "PAS16 Warning: Invalid SB IRQ %d\n\n", sb_config->irq);
-
- irq_dma = sb_dma_bits[sb_config->dma] |
- sb_irq_bits[sb_config->irq];
-
- pas_write(irq_dma, 0xFB8A);
- }
- else
- pas_write(0x00, 0xF788);
- }
-
- if (!ok)
- printk(KERN_WARNING "PAS16: Driver not enabled\n");
-
- return ok;
-}
-
-static int __init detect_pas_hw(struct address_info *hw_config)
-{
- unsigned char board_id, foo;
-
- /*
- * WARNING: Setting an option like W:1 or so that disables warm boot reset
- * of the card will screw up this detect code something fierce. Adding code
- * to handle this means possibly interfering with other cards on the bus if
- * you have something on base port 0x388. SO be forewarned.
- */
-
- outb((0xBC), 0x9A01); /* Activate first board */
- outb((hw_config->io_base >> 2), 0x9A01); /* Set base address */
- pas_translate_code = hw_config->io_base - 0x388;
- pas_write(1, 0xBF88); /* Select one wait states */
-
- board_id = pas_read(0x0B8B);
-
- if (board_id == 0xff)
- return 0;
-
- /*
- * We probably have a PAS-series board, now check for a PAS16-series board
- * by trying to change the board revision bits. PAS16-series hardware won't
- * let you do this - the bits are read-only.
- */
-
- foo = board_id ^ 0xe0;
-
- pas_write(foo, 0x0B8B);
- foo = pas_read(0x0B8B);
- pas_write(board_id, 0x0B8B);
-
- if (board_id != foo)
- return 0;
-
- pas_model = pas_read(0xFF88);
-
- return pas_model;
-}
-
-static void __init attach_pas_card(struct address_info *hw_config)
-{
- pas_irq = hw_config->irq;
-
- if (detect_pas_hw(hw_config))
- {
-
- if ((pas_model = pas_read(0xFF88)))
- {
- char temp[100];
-
- sprintf(temp,
- "%s rev %d", pas_model_names[(int) pas_model],
- pas_read(0x2789));
- conf_printf(temp, hw_config);
- }
- if (config_pas_hw(hw_config))
- {
- pas_pcm_init(hw_config);
- pas_midi_init();
- pas_init_mixer();
- }
- }
-}
-
-static inline int __init probe_pas(struct address_info *hw_config)
-{
- return detect_pas_hw(hw_config);
-}
-
-static void __exit unload_pas(struct address_info *hw_config)
-{
- extern int pas_audiodev;
- extern int pas2_mididev;
-
- if (hw_config->dma>0)
- sound_free_dma(hw_config->dma);
- if (hw_config->irq>0)
- free_irq(hw_config->irq, hw_config);
-
- if(pas_audiodev!=-1)
- sound_unload_mixerdev(audio_devs[pas_audiodev]->mixer_dev);
- if(pas2_mididev!=-1)
- sound_unload_mididev(pas2_mididev);
- if(pas_audiodev!=-1)
- sound_unload_audiodev(pas_audiodev);
-}
-
-static int __initdata io = -1;
-static int __initdata irq = -1;
-static int __initdata dma = -1;
-static int __initdata dma16 = -1; /* Set this for modules that need it */
-
-static int __initdata sb_io = 0;
-static int __initdata sb_irq = -1;
-static int __initdata sb_dma = -1;
-static int __initdata sb_dma16 = -1;
-
-module_param(io, int, 0);
-module_param(irq, int, 0);
-module_param(dma, int, 0);
-module_param(dma16, int, 0);
-
-module_param(sb_io, int, 0);
-module_param(sb_irq, int, 0);
-module_param(sb_dma, int, 0);
-module_param(sb_dma16, int, 0);
-
-module_param(joystick, bool, 0);
-module_param(symphony, bool, 0);
-module_param(broken_bus_clock, bool, 0);
-
-MODULE_LICENSE("GPL");
-
-static int __init init_pas2(void)
-{
- printk(KERN_INFO "Pro Audio Spectrum driver Copyright (C) by Hannu Savolainen 1993-1996\n");
-
- cfg.io_base = io;
- cfg.irq = irq;
- cfg.dma = dma;
- cfg.dma2 = dma16;
-
- cfg2.io_base = sb_io;
- cfg2.irq = sb_irq;
- cfg2.dma = sb_dma;
- cfg2.dma2 = sb_dma16;
-
- if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) {
- printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n");
- return -EINVAL;
- }
-
- if (!probe_pas(&cfg))
- return -ENODEV;
- attach_pas_card(&cfg);
-
- return 0;
-}
-
-static void __exit cleanup_pas2(void)
-{
- unload_pas(&cfg);
-}
-
-module_init(init_pas2);
-module_exit(cleanup_pas2);
-
-#ifndef MODULE
-static int __init setup_pas2(char *str)
-{
- /* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, sb_dma2 */
- int ints[9];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
- dma = ints[3];
- dma16 = ints[4];
-
- sb_io = ints[5];
- sb_irq = ints[6];
- sb_dma = ints[7];
- sb_dma16 = ints[8];
-
- return 1;
-}
-
-__setup("pas2=", setup_pas2);
-#endif
diff --git a/sound/oss/pas2_midi.c b/sound/oss/pas2_midi.c
deleted file mode 100644
index 1122d10a20c3..000000000000
--- a/sound/oss/pas2_midi.c
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * sound/oss/pas2_midi.c
- *
- * The low level driver for the PAS Midi Interface.
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- * Bartlomiej Zolnierkiewicz : Added __init to pas_init_mixer()
- */
-
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include "sound_config.h"
-
-#include "pas2.h"
-
-extern spinlock_t pas_lock;
-
-static int midi_busy, input_opened;
-static int my_dev;
-
-int pas2_mididev=-1;
-
-static unsigned char tmp_queue[256];
-static volatile int qlen;
-static volatile unsigned char qhead, qtail;
-
-static void (*midi_input_intr) (int dev, unsigned char data);
-
-static int pas_midi_open(int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
-)
-{
- int err;
- unsigned long flags;
- unsigned char ctrl;
-
-
- if (midi_busy)
- return -EBUSY;
-
- /*
- * Reset input and output FIFO pointers
- */
- pas_write(0x20 | 0x40,
- 0x178b);
-
- spin_lock_irqsave(&pas_lock, flags);
-
- if ((err = pas_set_intr(0x10)) < 0)
- {
- spin_unlock_irqrestore(&pas_lock, flags);
- return err;
- }
- /*
- * Enable input available and output FIFO empty interrupts
- */
-
- ctrl = 0;
- input_opened = 0;
- midi_input_intr = input;
-
- if (mode == OPEN_READ || mode == OPEN_READWRITE)
- {
- ctrl |= 0x04; /* Enable input */
- input_opened = 1;
- }
- if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
- {
- ctrl |= 0x08 | 0x10; /* Enable output */
- }
- pas_write(ctrl, 0x178b);
-
- /*
- * Acknowledge any pending interrupts
- */
-
- pas_write(0xff, 0x1B88);
-
- spin_unlock_irqrestore(&pas_lock, flags);
-
- midi_busy = 1;
- qlen = qhead = qtail = 0;
- return 0;
-}
-
-static void pas_midi_close(int dev)
-{
-
- /*
- * Reset FIFO pointers, disable intrs
- */
- pas_write(0x20 | 0x40, 0x178b);
-
- pas_remove_intr(0x10);
- midi_busy = 0;
-}
-
-static int dump_to_midi(unsigned char midi_byte)
-{
- int fifo_space, x;
-
- fifo_space = ((x = pas_read(0x1B89)) >> 4) & 0x0f;
-
- /*
- * The MIDI FIFO space register and it's documentation is nonunderstandable.
- * There seem to be no way to differentiate between buffer full and buffer
- * empty situations. For this reason we don't never write the buffer
- * completely full. In this way we can assume that 0 (or is it 15)
- * means that the buffer is empty.
- */
-
- if (fifo_space < 2 && fifo_space != 0) /* Full (almost) */
- return 0; /* Ask upper layers to retry after some time */
-
- pas_write(midi_byte, 0x178A);
-
- return 1;
-}
-
-static int pas_midi_out(int dev, unsigned char midi_byte)
-{
-
- unsigned long flags;
-
- /*
- * Drain the local queue first
- */
-
- spin_lock_irqsave(&pas_lock, flags);
-
- while (qlen && dump_to_midi(tmp_queue[qhead]))
- {
- qlen--;
- qhead++;
- }
-
- spin_unlock_irqrestore(&pas_lock, flags);
-
- /*
- * Output the byte if the local queue is empty.
- */
-
- if (!qlen)
- if (dump_to_midi(midi_byte))
- return 1;
-
- /*
- * Put to the local queue
- */
-
- if (qlen >= 256)
- return 0; /* Local queue full */
-
- spin_lock_irqsave(&pas_lock, flags);
-
- tmp_queue[qtail] = midi_byte;
- qlen++;
- qtail++;
-
- spin_unlock_irqrestore(&pas_lock, flags);
-
- return 1;
-}
-
-static int pas_midi_start_read(int dev)
-{
- return 0;
-}
-
-static int pas_midi_end_read(int dev)
-{
- return 0;
-}
-
-static void pas_midi_kick(int dev)
-{
-}
-
-static int pas_buffer_status(int dev)
-{
- return qlen;
-}
-
-#define MIDI_SYNTH_NAME "Pro Audio Spectrum Midi"
-#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
-#include "midi_synth.h"
-
-static struct midi_operations pas_midi_operations =
-{
- .owner = THIS_MODULE,
- .info = {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS},
- .converter = &std_midi_synth,
- .in_info = {0},
- .open = pas_midi_open,
- .close = pas_midi_close,
- .outputc = pas_midi_out,
- .start_read = pas_midi_start_read,
- .end_read = pas_midi_end_read,
- .kick = pas_midi_kick,
- .buffer_status = pas_buffer_status,
-};
-
-void __init pas_midi_init(void)
-{
- int dev = sound_alloc_mididev();
-
- if (dev == -1)
- {
- printk(KERN_WARNING "pas_midi_init: Too many midi devices detected\n");
- return;
- }
- std_midi_synth.midi_dev = my_dev = dev;
- midi_devs[dev] = &pas_midi_operations;
- pas2_mididev = dev;
- sequencer_init();
-}
-
-void pas_midi_interrupt(void)
-{
- unsigned char stat;
- int i, incount;
-
- stat = pas_read(0x1B88);
-
- if (stat & 0x04) /* Input data available */
- {
- incount = pas_read(0x1B89) & 0x0f; /* Input FIFO size */
- if (!incount)
- incount = 16;
-
- for (i = 0; i < incount; i++)
- if (input_opened)
- {
- midi_input_intr(my_dev, pas_read(0x178A));
- } else
- pas_read(0x178A); /* Flush */
- }
- if (stat & (0x08 | 0x10))
- {
- spin_lock(&pas_lock);/* called in irq context */
-
- while (qlen && dump_to_midi(tmp_queue[qhead]))
- {
- qlen--;
- qhead++;
- }
-
- spin_unlock(&pas_lock);
- }
- if (stat & 0x40)
- {
- printk(KERN_WARNING "MIDI output overrun %x,%x\n", pas_read(0x1B89), stat);
- }
- pas_write(stat, 0x1B88); /* Acknowledge interrupts */
-}
diff --git a/sound/oss/pas2_mixer.c b/sound/oss/pas2_mixer.c
deleted file mode 100644
index a0bcb85c3904..000000000000
--- a/sound/oss/pas2_mixer.c
+++ /dev/null
@@ -1,336 +0,0 @@
-
-/*
- * sound/oss/pas2_mixer.c
- *
- * Mixer routines for the Pro Audio Spectrum cards.
- */
-
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-/*
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * Bartlomiej Zolnierkiewicz : added __init to pas_init_mixer()
- */
-#include <linux/init.h>
-#include "sound_config.h"
-
-#include "pas2.h"
-
-#ifndef DEB
-#define DEB(what) /* (what) */
-#endif
-
-extern int pas_translate_code;
-extern char pas_model;
-extern int *pas_osp;
-extern int pas_audiodev;
-
-static int rec_devices = (SOUND_MASK_MIC); /* Default recording source */
-static int mode_control;
-
-#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD | SOUND_MASK_ALTPCM)
-
-#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD | SOUND_MASK_ALTPCM | SOUND_MASK_IMIX | \
- SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV)
-
-static int *levels;
-
-static int default_levels[32] =
-{
- 0x3232, /* Master Volume */
- 0x3232, /* Bass */
- 0x3232, /* Treble */
- 0x5050, /* FM */
- 0x4b4b, /* PCM */
- 0x3232, /* PC Speaker */
- 0x4b4b, /* Ext Line */
- 0x4b4b, /* Mic */
- 0x4b4b, /* CD */
- 0x6464, /* Recording monitor */
- 0x4b4b, /* SB PCM */
- 0x6464 /* Recording level */
-};
-
-void
-mix_write(unsigned char data, int ioaddr)
-{
- /*
- * The Revision D cards have a problem with their MVA508 interface. The
- * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and
- * MSBs out of the output byte and to do a 16-bit out to the mixer port -
- * 1. We need to do this because it isn't timing problem but chip access
- * sequence problem.
- */
-
- if (pas_model == 4)
- {
- outw(data | (data << 8), (ioaddr + pas_translate_code) - 1);
- outb((0x80), 0);
- } else
- pas_write(data, ioaddr);
-}
-
-static int
-mixer_output(int right_vol, int left_vol, int div, int bits,
- int mixer) /* Input or output mixer */
-{
- int left = left_vol * div / 100;
- int right = right_vol * div / 100;
-
-
- if (bits & 0x10)
- {
- left |= mixer;
- right |= mixer;
- }
- if (bits == 0x03 || bits == 0x04)
- {
- mix_write(0x80 | bits, 0x078B);
- mix_write(left, 0x078B);
- right_vol = left_vol;
- } else
- {
- mix_write(0x80 | 0x20 | bits, 0x078B);
- mix_write(left, 0x078B);
- mix_write(0x80 | 0x40 | bits, 0x078B);
- mix_write(right, 0x078B);
- }
-
- return (left_vol | (right_vol << 8));
-}
-
-static void
-set_mode(int new_mode)
-{
- mix_write(0x80 | 0x05, 0x078B);
- mix_write(new_mode, 0x078B);
-
- mode_control = new_mode;
-}
-
-static int
-pas_mixer_set(int whichDev, unsigned int level)
-{
- int left, right, devmask, changed, i, mixer = 0;
-
- DEB(printk("static int pas_mixer_set(int whichDev = %d, unsigned int level = %X)\n", whichDev, level));
-
- left = level & 0x7f;
- right = (level & 0x7f00) >> 8;
-
- if (whichDev < SOUND_MIXER_NRDEVICES) {
- if ((1 << whichDev) & rec_devices)
- mixer = 0x20;
- else
- mixer = 0x00;
- }
-
- switch (whichDev)
- {
- case SOUND_MIXER_VOLUME: /* Master volume (0-63) */
- levels[whichDev] = mixer_output(right, left, 63, 0x01, 0);
- break;
-
- /*
- * Note! Bass and Treble are mono devices. Will use just the left
- * channel.
- */
- case SOUND_MIXER_BASS: /* Bass (0-12) */
- levels[whichDev] = mixer_output(right, left, 12, 0x03, 0);
- break;
- case SOUND_MIXER_TREBLE: /* Treble (0-12) */
- levels[whichDev] = mixer_output(right, left, 12, 0x04, 0);
- break;
-
- case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-31) */
- levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x00, mixer);
- break;
- case SOUND_MIXER_PCM: /* PAS PCM (0-31) */
- levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x05, mixer);
- break;
- case SOUND_MIXER_ALTPCM: /* SB PCM (0-31) */
- levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x07, mixer);
- break;
- case SOUND_MIXER_SPEAKER: /* PC speaker (0-31) */
- levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x06, mixer);
- break;
- case SOUND_MIXER_LINE: /* External line (0-31) */
- levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x02, mixer);
- break;
- case SOUND_MIXER_CD: /* CD (0-31) */
- levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x03, mixer);
- break;
- case SOUND_MIXER_MIC: /* External microphone (0-31) */
- levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x04, mixer);
- break;
- case SOUND_MIXER_IMIX: /* Recording monitor (0-31) (Output mixer only) */
- levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x01,
- 0x00);
- break;
- case SOUND_MIXER_RECLEV: /* Recording level (0-15) */
- levels[whichDev] = mixer_output(right, left, 15, 0x02, 0);
- break;
-
-
- case SOUND_MIXER_RECSRC:
- devmask = level & POSSIBLE_RECORDING_DEVICES;
-
- changed = devmask ^ rec_devices;
- rec_devices = devmask;
-
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- if (changed & (1 << i))
- {
- pas_mixer_set(i, levels[i]);
- }
- return rec_devices;
- break;
-
- default:
- return -EINVAL;
- }
-
- return (levels[whichDev]);
-}
-
-/*****/
-
-static void
-pas_mixer_reset(void)
-{
- int foo;
-
- DEB(printk("pas2_mixer.c: void pas_mixer_reset(void)\n"));
-
- for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
- pas_mixer_set(foo, levels[foo]);
-
- set_mode(0x04 | 0x01);
-}
-
-static int pas_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- int level,v ;
- int __user *p = (int __user *)arg;
-
- DEB(printk("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
- if (cmd == SOUND_MIXER_PRIVATE1) { /* Set loudness bit */
- if (get_user(level, p))
- return -EFAULT;
- if (level == -1) /* Return current settings */
- level = (mode_control & 0x04);
- else {
- mode_control &= ~0x04;
- if (level)
- mode_control |= 0x04;
- set_mode(mode_control);
- }
- level = !!level;
- return put_user(level, p);
- }
- if (cmd == SOUND_MIXER_PRIVATE2) { /* Set enhance bit */
- if (get_user(level, p))
- return -EFAULT;
- if (level == -1) { /* Return current settings */
- if (!(mode_control & 0x03))
- level = 0;
- else
- level = ((mode_control & 0x03) + 1) * 20;
- } else {
- int i = 0;
-
- level &= 0x7f;
- if (level)
- i = (level / 20) - 1;
- mode_control &= ~0x03;
- mode_control |= i & 0x03;
- set_mode(mode_control);
- if (i)
- i = (i + 1) * 20;
- level = i;
- }
- return put_user(level, p);
- }
- if (cmd == SOUND_MIXER_PRIVATE3) { /* Set mute bit */
- if (get_user(level, p))
- return -EFAULT;
- if (level == -1) /* Return current settings */
- level = !(pas_read(0x0B8A) & 0x20);
- else {
- if (level)
- pas_write(pas_read(0x0B8A) & (~0x20), 0x0B8A);
- else
- pas_write(pas_read(0x0B8A) | 0x20, 0x0B8A);
-
- level = !(pas_read(0x0B8A) & 0x20);
- }
- return put_user(level, p);
- }
- if (((cmd >> 8) & 0xff) == 'M') {
- if (get_user(v, p))
- return -EFAULT;
- if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
- v = pas_mixer_set(cmd & 0xff, v);
- } else {
- switch (cmd & 0xff) {
- case SOUND_MIXER_RECSRC:
- v = rec_devices;
- break;
-
- case SOUND_MIXER_STEREODEVS:
- v = SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE);
- break;
-
- case SOUND_MIXER_DEVMASK:
- v = SUPPORTED_MIXER_DEVICES;
- break;
-
- case SOUND_MIXER_RECMASK:
- v = POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES;
- break;
-
- case SOUND_MIXER_CAPS:
- v = 0; /* No special capabilities */
- break;
-
- default:
- v = levels[cmd & 0xff];
- break;
- }
- }
- return put_user(v, p);
- }
- return -EINVAL;
-}
-
-static struct mixer_operations pas_mixer_operations =
-{
- .owner = THIS_MODULE,
- .id = "PAS16",
- .name = "Pro Audio Spectrum 16",
- .ioctl = pas_mixer_ioctl
-};
-
-int __init
-pas_init_mixer(void)
-{
- int d;
-
- levels = load_mixer_volumes("PAS16_1", default_levels, 1);
-
- pas_mixer_reset();
-
- if ((d = sound_alloc_mixerdev()) != -1)
- {
- audio_devs[pas_audiodev]->mixer_dev = d;
- mixer_devs[d] = &pas_mixer_operations;
- }
- return 1;
-}
diff --git a/sound/oss/pas2_pcm.c b/sound/oss/pas2_pcm.c
deleted file mode 100644
index 8f7d175767a2..000000000000
--- a/sound/oss/pas2_pcm.c
+++ /dev/null
@@ -1,437 +0,0 @@
-/*
- * pas2_pcm.c Audio routines for PAS16
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * Alan Cox : Swatted a double allocation of device bug. Made a few
- * more things module options.
- * Bartlomiej Zolnierkiewicz : Added __init to pas_pcm_init()
- */
-
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/timex.h>
-#include "sound_config.h"
-
-#include "pas2.h"
-
-#ifndef DEB
-#define DEB(WHAT)
-#endif
-
-#define PAS_PCM_INTRBITS (0x08)
-/*
- * Sample buffer timer interrupt enable
- */
-
-#define PCM_NON 0
-#define PCM_DAC 1
-#define PCM_ADC 2
-
-static unsigned long pcm_speed; /* sampling rate */
-static unsigned char pcm_channels = 1; /* channels (1 or 2) */
-static unsigned char pcm_bits = 8; /* bits/sample (8 or 16) */
-static unsigned char pcm_filter; /* filter FLAG */
-static unsigned char pcm_mode = PCM_NON;
-static unsigned long pcm_count;
-static unsigned short pcm_bitsok = 8; /* mask of OK bits */
-static int pcm_busy;
-int pas_audiodev = -1;
-static int open_mode;
-
-extern spinlock_t pas_lock;
-
-static int pcm_set_speed(int arg)
-{
- int foo, tmp;
- unsigned long flags;
-
- if (arg == 0)
- return pcm_speed;
-
- if (arg > 44100)
- arg = 44100;
- if (arg < 5000)
- arg = 5000;
-
- if (pcm_channels & 2)
- {
- foo = ((CLOCK_TICK_RATE / 2) + (arg / 2)) / arg;
- arg = ((CLOCK_TICK_RATE / 2) + (foo / 2)) / foo;
- }
- else
- {
- foo = (CLOCK_TICK_RATE + (arg / 2)) / arg;
- arg = (CLOCK_TICK_RATE + (foo / 2)) / foo;
- }
-
- pcm_speed = arg;
-
- tmp = pas_read(0x0B8A);
-
- /*
- * Set anti-aliasing filters according to sample rate. You really *NEED*
- * to enable this feature for all normal recording unless you want to
- * experiment with aliasing effects.
- * These filters apply to the selected "recording" source.
- * I (pfw) don't know the encoding of these 5 bits. The values shown
- * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/.
- *
- * I cleared bit 5 of these values, since that bit controls the master
- * mute flag. (Olav Wölfelschneider)
- *
- */
-#if !defined NO_AUTO_FILTER_SET
- tmp &= 0xe0;
- if (pcm_speed >= 2 * 17897)
- tmp |= 0x01;
- else if (pcm_speed >= 2 * 15909)
- tmp |= 0x02;
- else if (pcm_speed >= 2 * 11931)
- tmp |= 0x09;
- else if (pcm_speed >= 2 * 8948)
- tmp |= 0x11;
- else if (pcm_speed >= 2 * 5965)
- tmp |= 0x19;
- else if (pcm_speed >= 2 * 2982)
- tmp |= 0x04;
- pcm_filter = tmp;
-#endif
-
- spin_lock_irqsave(&pas_lock, flags);
-
- pas_write(tmp & ~(0x40 | 0x80), 0x0B8A);
- pas_write(0x00 | 0x30 | 0x04, 0x138B);
- pas_write(foo & 0xff, 0x1388);
- pas_write((foo >> 8) & 0xff, 0x1388);
- pas_write(tmp, 0x0B8A);
-
- spin_unlock_irqrestore(&pas_lock, flags);
-
- return pcm_speed;
-}
-
-static int pcm_set_channels(int arg)
-{
-
- if ((arg != 1) && (arg != 2))
- return pcm_channels;
-
- if (arg != pcm_channels)
- {
- pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A);
-
- pcm_channels = arg;
- pcm_set_speed(pcm_speed); /* The speed must be reinitialized */
- }
- return pcm_channels;
-}
-
-static int pcm_set_bits(int arg)
-{
- if (arg == 0)
- return pcm_bits;
-
- if ((arg & pcm_bitsok) != arg)
- return pcm_bits;
-
- if (arg != pcm_bits)
- {
- pas_write(pas_read(0x8389) ^ 0x04, 0x8389);
-
- pcm_bits = arg;
- }
- return pcm_bits;
-}
-
-static int pas_audio_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- int val, ret;
- int __user *p = arg;
-
- DEB(printk("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
-
- switch (cmd)
- {
- case SOUND_PCM_WRITE_RATE:
- if (get_user(val, p))
- return -EFAULT;
- ret = pcm_set_speed(val);
- break;
-
- case SOUND_PCM_READ_RATE:
- ret = pcm_speed;
- break;
-
- case SNDCTL_DSP_STEREO:
- if (get_user(val, p))
- return -EFAULT;
- ret = pcm_set_channels(val + 1) - 1;
- break;
-
- case SOUND_PCM_WRITE_CHANNELS:
- if (get_user(val, p))
- return -EFAULT;
- ret = pcm_set_channels(val);
- break;
-
- case SOUND_PCM_READ_CHANNELS:
- ret = pcm_channels;
- break;
-
- case SNDCTL_DSP_SETFMT:
- if (get_user(val, p))
- return -EFAULT;
- ret = pcm_set_bits(val);
- break;
-
- case SOUND_PCM_READ_BITS:
- ret = pcm_bits;
- break;
-
- default:
- return -EINVAL;
- }
- return put_user(ret, p);
-}
-
-static void pas_audio_reset(int dev)
-{
- DEB(printk("pas2_pcm.c: static void pas_audio_reset(void)\n"));
-
- pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); /* Disable PCM */
-}
-
-static int pas_audio_open(int dev, int mode)
-{
- int err;
- unsigned long flags;
-
- DEB(printk("pas2_pcm.c: static int pas_audio_open(int mode = %X)\n", mode));
-
- spin_lock_irqsave(&pas_lock, flags);
- if (pcm_busy)
- {
- spin_unlock_irqrestore(&pas_lock, flags);
- return -EBUSY;
- }
- pcm_busy = 1;
- spin_unlock_irqrestore(&pas_lock, flags);
-
- if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0)
- return err;
-
-
- pcm_count = 0;
- open_mode = mode;
-
- return 0;
-}
-
-static void pas_audio_close(int dev)
-{
- unsigned long flags;
-
- DEB(printk("pas2_pcm.c: static void pas_audio_close(void)\n"));
-
- spin_lock_irqsave(&pas_lock, flags);
-
- pas_audio_reset(dev);
- pas_remove_intr(PAS_PCM_INTRBITS);
- pcm_mode = PCM_NON;
-
- pcm_busy = 0;
- spin_unlock_irqrestore(&pas_lock, flags);
-}
-
-static void pas_audio_output_block(int dev, unsigned long buf, int count,
- int intrflag)
-{
- unsigned long flags, cnt;
-
- DEB(printk("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count));
-
- cnt = count;
- if (audio_devs[dev]->dmap_out->dma > 3)
- cnt >>= 1;
-
- if (audio_devs[dev]->flags & DMA_AUTOMODE &&
- intrflag &&
- cnt == pcm_count)
- return;
-
- spin_lock_irqsave(&pas_lock, flags);
-
- pas_write(pas_read(0xF8A) & ~0x40,
- 0xF8A);
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
-
- if (count != pcm_count)
- {
- pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
- pas_write(0x40 | 0x30 | 0x04, 0x138B);
- pas_write(count & 0xff, 0x1389);
- pas_write((count >> 8) & 0xff, 0x1389);
- pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
-
- pcm_count = count;
- }
- pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
-#ifdef NO_TRIGGER
- pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
-#endif
-
- pcm_mode = PCM_DAC;
-
- spin_unlock_irqrestore(&pas_lock, flags);
-}
-
-static void pas_audio_start_input(int dev, unsigned long buf, int count,
- int intrflag)
-{
- unsigned long flags;
- int cnt;
-
- DEB(printk("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count));
-
- cnt = count;
- if (audio_devs[dev]->dmap_out->dma > 3)
- cnt >>= 1;
-
- if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE &&
- intrflag &&
- cnt == pcm_count)
- return;
-
- spin_lock_irqsave(&pas_lock, flags);
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
-
- if (count != pcm_count)
- {
- pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
- pas_write(0x40 | 0x30 | 0x04, 0x138B);
- pas_write(count & 0xff, 0x1389);
- pas_write((count >> 8) & 0xff, 0x1389);
- pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
-
- pcm_count = count;
- }
- pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
-#ifdef NO_TRIGGER
- pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
-#endif
-
- pcm_mode = PCM_ADC;
-
- spin_unlock_irqrestore(&pas_lock, flags);
-}
-
-#ifndef NO_TRIGGER
-static void pas_audio_trigger(int dev, int state)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&pas_lock, flags);
- state &= open_mode;
-
- if (state & PCM_ENABLE_OUTPUT)
- pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
- else if (state & PCM_ENABLE_INPUT)
- pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
- else
- pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
-
- spin_unlock_irqrestore(&pas_lock, flags);
-}
-#endif
-
-static int pas_audio_prepare_for_input(int dev, int bsize, int bcount)
-{
- pas_audio_reset(dev);
- return 0;
-}
-
-static int pas_audio_prepare_for_output(int dev, int bsize, int bcount)
-{
- pas_audio_reset(dev);
- return 0;
-}
-
-static struct audio_driver pas_audio_driver =
-{
- .owner = THIS_MODULE,
- .open = pas_audio_open,
- .close = pas_audio_close,
- .output_block = pas_audio_output_block,
- .start_input = pas_audio_start_input,
- .ioctl = pas_audio_ioctl,
- .prepare_for_input = pas_audio_prepare_for_input,
- .prepare_for_output = pas_audio_prepare_for_output,
- .halt_io = pas_audio_reset,
- .trigger = pas_audio_trigger
-};
-
-void __init pas_pcm_init(struct address_info *hw_config)
-{
- DEB(printk("pas2_pcm.c: long pas_pcm_init()\n"));
-
- pcm_bitsok = 8;
- if (pas_read(0xEF8B) & 0x08)
- pcm_bitsok |= 16;
-
- pcm_set_speed(DSP_DEFAULT_SPEED);
-
- if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
- "Pro Audio Spectrum",
- &pas_audio_driver,
- sizeof(struct audio_driver),
- DMA_AUTOMODE,
- AFMT_U8 | AFMT_S16_LE,
- NULL,
- hw_config->dma,
- hw_config->dma)) < 0)
- printk(KERN_WARNING "PAS16: Too many PCM devices available\n");
-}
-
-void pas_pcm_interrupt(unsigned char status, int cause)
-{
- if (cause == 1)
- {
- /*
- * Halt the PCM first. Otherwise we don't have time to start a new
- * block before the PCM chip proceeds to the next sample
- */
-
- if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE))
- pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
-
- switch (pcm_mode)
- {
- case PCM_DAC:
- DMAbuf_outputintr(pas_audiodev, 1);
- break;
-
- case PCM_ADC:
- DMAbuf_inputintr(pas_audiodev);
- break;
-
- default:
- printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n");
- }
- }
-}
diff --git a/sound/oss/pss.c b/sound/oss/pss.c
deleted file mode 100644
index e19dd5dcc2de..000000000000
--- a/sound/oss/pss.c
+++ /dev/null
@@ -1,1266 +0,0 @@
-/*
- * sound/oss/pss.c
- *
- * The low level driver for the Personal Sound System (ECHO ESC614).
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Thomas Sailer ioctl code reworked (vmalloc/vfree removed)
- * Alan Cox modularisation, clean up.
- *
- * 98-02-21: Vladimir Michl <vladimir.michl@upol.cz>
- * Added mixer device for Beethoven ADSP-16 (master volume,
- * bass, treble, synth), only for speakers.
- * Fixed bug in pss_write (exchange parameters)
- * Fixed config port of SB
- * Requested two regions for PSS (PSS mixer, PSS config)
- * Modified pss_download_boot
- * To probe_pss_mss added test for initialize AD1848
- * 98-05-28: Vladimir Michl <vladimir.michl@upol.cz>
- * Fixed computation of mixer volumes
- * 04-05-1999: Anthony Barbachan <barbcode@xmen.cis.fordham.edu>
- * Added code that allows the user to enable his cdrom and/or
- * joystick through the module parameters pss_cdrom_port and
- * pss_enable_joystick. pss_cdrom_port takes a port address as its
- * argument. pss_enable_joystick takes either a 0 or a non-0 as its
- * argument.
- * 04-06-1999: Anthony Barbachan <barbcode@xmen.cis.fordham.edu>
- * Separated some code into new functions for easier reuse.
- * Cleaned up and streamlined new code. Added code to allow a user
- * to only use this driver for enabling non-sound components
- * through the new module parameter pss_no_sound (flag). Added
- * code that would allow a user to decide whether the driver should
- * reset the configured hardware settings for the PSS board through
- * the module parameter pss_keep_settings (flag). This flag will
- * allow a user to free up resources in use by this card if needbe,
- * furthermore it allows him to use this driver to just enable the
- * emulations and then be unloaded as it is no longer needed. Both
- * new settings are only available to this driver if compiled as a
- * module. The default settings of all new parameters are set to
- * load the driver as it did in previous versions.
- * 04-07-1999: Anthony Barbachan <barbcode@xmen.cis.fordham.edu>
- * Added module parameter pss_firmware to allow the user to tell
- * the driver where the firmware file is located. The default
- * setting is the previous hardcoded setting "/etc/sound/pss_synth".
- * 00-03-03: Christoph Hellwig <chhellwig@infradead.org>
- * Adapted to module_init/module_exit
- * 11-10-2000: Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
- * Added __init to probe_pss(), attach_pss() and probe_pss_mpu()
- * 02-Jan-2001: Chris Rankin
- * Specify that this module owns the coprocessor
- */
-
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-
-#include "sound_config.h"
-#include "sound_firmware.h"
-
-#include "ad1848.h"
-#include "mpu401.h"
-
-/*
- * PSS registers.
- */
-#define REG(x) (devc->base+x)
-#define PSS_DATA 0
-#define PSS_STATUS 2
-#define PSS_CONTROL 2
-#define PSS_ID 4
-#define PSS_IRQACK 4
-#define PSS_PIO 0x1a
-
-/*
- * Config registers
- */
-#define CONF_PSS 0x10
-#define CONF_WSS 0x12
-#define CONF_SB 0x14
-#define CONF_CDROM 0x16
-#define CONF_MIDI 0x18
-
-/*
- * Status bits.
- */
-#define PSS_FLAG3 0x0800
-#define PSS_FLAG2 0x0400
-#define PSS_FLAG1 0x1000
-#define PSS_FLAG0 0x0800
-#define PSS_WRITE_EMPTY 0x8000
-#define PSS_READ_FULL 0x4000
-
-/*
- * WSS registers
- */
-#define WSS_INDEX 4
-#define WSS_DATA 5
-
-/*
- * WSS status bits
- */
-#define WSS_INITIALIZING 0x80
-#define WSS_AUTOCALIBRATION 0x20
-
-#define NO_WSS_MIXER -1
-
-#include "coproc.h"
-
-#include "pss_boot.h"
-
-/* If compiled into kernel, it enable or disable pss mixer */
-#ifdef CONFIG_PSS_MIXER
-static int pss_mixer = 1;
-#else
-static int pss_mixer;
-#endif
-
-
-typedef struct pss_mixerdata {
- unsigned int volume_l;
- unsigned int volume_r;
- unsigned int bass;
- unsigned int treble;
- unsigned int synth;
-} pss_mixerdata;
-
-typedef struct pss_confdata {
- int base;
- int irq;
- int dma;
- int *osp;
- pss_mixerdata mixer;
- int ad_mixer_dev;
-} pss_confdata;
-
-static pss_confdata pss_data;
-static pss_confdata *devc = &pss_data;
-static DEFINE_SPINLOCK(lock);
-
-static int pss_initialized;
-static int nonstandard_microcode;
-static int pss_cdrom_port = -1; /* Parameter for the PSS cdrom port */
-static int pss_enable_joystick; /* Parameter for enabling the joystick */
-static coproc_operations pss_coproc_operations;
-
-static void pss_write(pss_confdata *devc, int data)
-{
- unsigned long i, limit;
-
- limit = jiffies + HZ/10; /* The timeout is 0.1 seconds */
- /*
- * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes
- * called while interrupts are disabled. This means that the timer is
- * disabled also. However the timeout situation is a abnormal condition.
- * Normally the DSP should be ready to accept commands after just couple of
- * loops.
- */
-
- for (i = 0; i < 5000000 && time_before(jiffies, limit); i++)
- {
- if (inw(REG(PSS_STATUS)) & PSS_WRITE_EMPTY)
- {
- outw(data, REG(PSS_DATA));
- return;
- }
- }
- printk(KERN_WARNING "PSS: DSP Command (%04x) Timeout.\n", data);
-}
-
-static int __init probe_pss(struct address_info *hw_config)
-{
- unsigned short id;
- int irq, dma;
-
- devc->base = hw_config->io_base;
- irq = devc->irq = hw_config->irq;
- dma = devc->dma = hw_config->dma;
- devc->osp = hw_config->osp;
-
- if (devc->base != 0x220 && devc->base != 0x240)
- if (devc->base != 0x230 && devc->base != 0x250) /* Some cards use these */
- return 0;
-
- if (!request_region(devc->base, 0x10, "PSS mixer, SB emulation")) {
- printk(KERN_ERR "PSS: I/O port conflict\n");
- return 0;
- }
- id = inw(REG(PSS_ID));
- if ((id >> 8) != 'E') {
- printk(KERN_ERR "No PSS signature detected at 0x%x (0x%x)\n", devc->base, id);
- release_region(devc->base, 0x10);
- return 0;
- }
- if (!request_region(devc->base + 0x10, 0x9, "PSS config")) {
- printk(KERN_ERR "PSS: I/O port conflict\n");
- release_region(devc->base, 0x10);
- return 0;
- }
- return 1;
-}
-
-static int set_irq(pss_confdata * devc, int dev, int irq)
-{
- static unsigned short irq_bits[16] =
- {
- 0x0000, 0x0000, 0x0000, 0x0008,
- 0x0000, 0x0010, 0x0000, 0x0018,
- 0x0000, 0x0020, 0x0028, 0x0030,
- 0x0038, 0x0000, 0x0000, 0x0000
- };
-
- unsigned short tmp, bits;
-
- if (irq < 0 || irq > 15)
- return 0;
-
- tmp = inw(REG(dev)) & ~0x38; /* Load confreg, mask IRQ bits out */
-
- if ((bits = irq_bits[irq]) == 0 && irq != 0)
- {
- printk(KERN_ERR "PSS: Invalid IRQ %d\n", irq);
- return 0;
- }
- outw(tmp | bits, REG(dev));
- return 1;
-}
-
-static void set_io_base(pss_confdata * devc, int dev, int base)
-{
- unsigned short tmp = inw(REG(dev)) & 0x003f;
- unsigned short bits = (base & 0x0ffc) << 4;
-
- outw(bits | tmp, REG(dev));
-}
-
-static int set_dma(pss_confdata * devc, int dev, int dma)
-{
- static unsigned short dma_bits[8] =
- {
- 0x0001, 0x0002, 0x0000, 0x0003,
- 0x0000, 0x0005, 0x0006, 0x0007
- };
-
- unsigned short tmp, bits;
-
- if (dma < 0 || dma > 7)
- return 0;
-
- tmp = inw(REG(dev)) & ~0x07; /* Load confreg, mask DMA bits out */
-
- if ((bits = dma_bits[dma]) == 0 && dma != 4)
- {
- printk(KERN_ERR "PSS: Invalid DMA %d\n", dma);
- return 0;
- }
- outw(tmp | bits, REG(dev));
- return 1;
-}
-
-static int pss_reset_dsp(pss_confdata * devc)
-{
- unsigned long i, limit = jiffies + HZ/10;
-
- outw(0x2000, REG(PSS_CONTROL));
- for (i = 0; i < 32768 && time_after_eq(limit, jiffies); i++)
- inw(REG(PSS_CONTROL));
- outw(0x0000, REG(PSS_CONTROL));
- return 1;
-}
-
-static int pss_put_dspword(pss_confdata * devc, unsigned short word)
-{
- int i, val;
-
- for (i = 0; i < 327680; i++)
- {
- val = inw(REG(PSS_STATUS));
- if (val & PSS_WRITE_EMPTY)
- {
- outw(word, REG(PSS_DATA));
- return 1;
- }
- }
- return 0;
-}
-
-static int pss_get_dspword(pss_confdata * devc, unsigned short *word)
-{
- int i, val;
-
- for (i = 0; i < 327680; i++)
- {
- val = inw(REG(PSS_STATUS));
- if (val & PSS_READ_FULL)
- {
- *word = inw(REG(PSS_DATA));
- return 1;
- }
- }
- return 0;
-}
-
-static int pss_download_boot(pss_confdata * devc, unsigned char *block, int size, int flags)
-{
- int i, val, count;
- unsigned long limit;
-
- if (flags & CPF_FIRST)
- {
-/*_____ Warn DSP software that a boot is coming */
- outw(0x00fe, REG(PSS_DATA));
-
- limit = jiffies + HZ/10;
- for (i = 0; i < 32768 && time_before(jiffies, limit); i++)
- if (inw(REG(PSS_DATA)) == 0x5500)
- break;
-
- outw(*block++, REG(PSS_DATA));
- pss_reset_dsp(devc);
- }
- count = 1;
- while ((flags&CPF_LAST) || count<size )
- {
- int j;
-
- for (j = 0; j < 327670; j++)
- {
-/*_____ Wait for BG to appear */
- if (inw(REG(PSS_STATUS)) & PSS_FLAG3)
- break;
- }
-
- if (j == 327670)
- {
- /* It's ok we timed out when the file was empty */
- if (count >= size && flags & CPF_LAST)
- break;
- else
- {
- printk("\n");
- printk(KERN_ERR "PSS: Download timeout problems, byte %d=%d\n", count, size);
- return 0;
- }
- }
-/*_____ Send the next byte */
- if (count >= size)
- {
- /* If not data in block send 0xffff */
- outw (0xffff, REG (PSS_DATA));
- }
- else
- {
- /*_____ Send the next byte */
- outw (*block++, REG (PSS_DATA));
- };
- count++;
- }
-
- if (flags & CPF_LAST)
- {
-/*_____ Why */
- outw(0, REG(PSS_DATA));
-
- limit = jiffies + HZ/10;
- for (i = 0; i < 32768 && time_after_eq(limit, jiffies); i++)
- val = inw(REG(PSS_STATUS));
-
- limit = jiffies + HZ/10;
- for (i = 0; i < 32768 && time_after_eq(limit, jiffies); i++)
- {
- val = inw(REG(PSS_STATUS));
- if (val & 0x4000)
- break;
- }
-
- /* now read the version */
- for (i = 0; i < 32000; i++)
- {
- val = inw(REG(PSS_STATUS));
- if (val & PSS_READ_FULL)
- break;
- }
- if (i == 32000)
- return 0;
-
- val = inw(REG(PSS_DATA));
- /* printk( "<PSS: microcode version %d.%d loaded>", val/16, val % 16); */
- }
- return 1;
-}
-
-/* Mixer */
-static void set_master_volume(pss_confdata *devc, int left, int right)
-{
- static unsigned char log_scale[101] = {
- 0xdb, 0xe0, 0xe3, 0xe5, 0xe7, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xed, 0xee,
- 0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3,
- 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7,
- 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9,
- 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
- 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
- 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
- 0xfe, 0xfe, 0xff, 0xff, 0xff
- };
- pss_write(devc, 0x0010);
- pss_write(devc, log_scale[left] | 0x0000);
- pss_write(devc, 0x0010);
- pss_write(devc, log_scale[right] | 0x0100);
-}
-
-static void set_synth_volume(pss_confdata *devc, int volume)
-{
- int vol = ((0x8000*volume)/100L);
- pss_write(devc, 0x0080);
- pss_write(devc, vol);
- pss_write(devc, 0x0081);
- pss_write(devc, vol);
-}
-
-static void set_bass(pss_confdata *devc, int level)
-{
- int vol = (int)(((0xfd - 0xf0) * level)/100L) + 0xf0;
- pss_write(devc, 0x0010);
- pss_write(devc, vol | 0x0200);
-};
-
-static void set_treble(pss_confdata *devc, int level)
-{
- int vol = (((0xfd - 0xf0) * level)/100L) + 0xf0;
- pss_write(devc, 0x0010);
- pss_write(devc, vol | 0x0300);
-};
-
-static void pss_mixer_reset(pss_confdata *devc)
-{
- set_master_volume(devc, 33, 33);
- set_bass(devc, 50);
- set_treble(devc, 50);
- set_synth_volume(devc, 30);
- pss_write (devc, 0x0010);
- pss_write (devc, 0x0800 | 0xce); /* Stereo */
-
- if(pss_mixer)
- {
- devc->mixer.volume_l = devc->mixer.volume_r = 33;
- devc->mixer.bass = 50;
- devc->mixer.treble = 50;
- devc->mixer.synth = 30;
- }
-}
-
-static int set_volume_mono(unsigned __user *p, unsigned int *aleft)
-{
- unsigned int left, volume;
- if (get_user(volume, p))
- return -EFAULT;
-
- left = volume & 0xff;
- if (left > 100)
- left = 100;
- *aleft = left;
- return 0;
-}
-
-static int set_volume_stereo(unsigned __user *p,
- unsigned int *aleft,
- unsigned int *aright)
-{
- unsigned int left, right, volume;
- if (get_user(volume, p))
- return -EFAULT;
-
- left = volume & 0xff;
- if (left > 100)
- left = 100;
- right = (volume >> 8) & 0xff;
- if (right > 100)
- right = 100;
- *aleft = left;
- *aright = right;
- return 0;
-}
-
-static int ret_vol_mono(int left)
-{
- return ((left << 8) | left);
-}
-
-static int ret_vol_stereo(int left, int right)
-{
- return ((right << 8) | left);
-}
-
-static int call_ad_mixer(pss_confdata *devc,unsigned int cmd, void __user *arg)
-{
- if (devc->ad_mixer_dev != NO_WSS_MIXER)
- return mixer_devs[devc->ad_mixer_dev]->ioctl(devc->ad_mixer_dev, cmd, arg);
- else
- return -EINVAL;
-}
-
-static int pss_mixer_ioctl (int dev, unsigned int cmd, void __user *arg)
-{
- pss_confdata *devc = mixer_devs[dev]->devc;
- int cmdf = cmd & 0xff;
-
- if ((cmdf != SOUND_MIXER_VOLUME) && (cmdf != SOUND_MIXER_BASS) &&
- (cmdf != SOUND_MIXER_TREBLE) && (cmdf != SOUND_MIXER_SYNTH) &&
- (cmdf != SOUND_MIXER_DEVMASK) && (cmdf != SOUND_MIXER_STEREODEVS) &&
- (cmdf != SOUND_MIXER_RECMASK) && (cmdf != SOUND_MIXER_CAPS) &&
- (cmdf != SOUND_MIXER_RECSRC))
- {
- return call_ad_mixer(devc, cmd, arg);
- }
-
- if (((cmd >> 8) & 0xff) != 'M')
- return -EINVAL;
-
- if (_SIOC_DIR (cmd) & _SIOC_WRITE)
- {
- switch (cmdf)
- {
- case SOUND_MIXER_RECSRC:
- if (devc->ad_mixer_dev != NO_WSS_MIXER)
- return call_ad_mixer(devc, cmd, arg);
- else
- {
- int v;
- if (get_user(v, (int __user *)arg))
- return -EFAULT;
- if (v != 0)
- return -EINVAL;
- return 0;
- }
- case SOUND_MIXER_VOLUME:
- if (set_volume_stereo(arg,
- &devc->mixer.volume_l,
- &devc->mixer.volume_r))
- return -EFAULT;
- set_master_volume(devc, devc->mixer.volume_l,
- devc->mixer.volume_r);
- return ret_vol_stereo(devc->mixer.volume_l,
- devc->mixer.volume_r);
-
- case SOUND_MIXER_BASS:
- if (set_volume_mono(arg, &devc->mixer.bass))
- return -EFAULT;
- set_bass(devc, devc->mixer.bass);
- return ret_vol_mono(devc->mixer.bass);
-
- case SOUND_MIXER_TREBLE:
- if (set_volume_mono(arg, &devc->mixer.treble))
- return -EFAULT;
- set_treble(devc, devc->mixer.treble);
- return ret_vol_mono(devc->mixer.treble);
-
- case SOUND_MIXER_SYNTH:
- if (set_volume_mono(arg, &devc->mixer.synth))
- return -EFAULT;
- set_synth_volume(devc, devc->mixer.synth);
- return ret_vol_mono(devc->mixer.synth);
-
- default:
- return -EINVAL;
- }
- }
- else
- {
- int val, and_mask = 0, or_mask = 0;
- /*
- * Return parameters
- */
- switch (cmdf)
- {
- case SOUND_MIXER_DEVMASK:
- if (call_ad_mixer(devc, cmd, arg) == -EINVAL)
- break;
- and_mask = ~0;
- or_mask = SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_SYNTH;
- break;
-
- case SOUND_MIXER_STEREODEVS:
- if (call_ad_mixer(devc, cmd, arg) == -EINVAL)
- break;
- and_mask = ~0;
- or_mask = SOUND_MASK_VOLUME;
- break;
-
- case SOUND_MIXER_RECMASK:
- if (devc->ad_mixer_dev != NO_WSS_MIXER)
- return call_ad_mixer(devc, cmd, arg);
- break;
-
- case SOUND_MIXER_CAPS:
- if (devc->ad_mixer_dev != NO_WSS_MIXER)
- return call_ad_mixer(devc, cmd, arg);
- or_mask = SOUND_CAP_EXCL_INPUT;
- break;
-
- case SOUND_MIXER_RECSRC:
- if (devc->ad_mixer_dev != NO_WSS_MIXER)
- return call_ad_mixer(devc, cmd, arg);
- break;
-
- case SOUND_MIXER_VOLUME:
- or_mask = ret_vol_stereo(devc->mixer.volume_l, devc->mixer.volume_r);
- break;
-
- case SOUND_MIXER_BASS:
- or_mask = ret_vol_mono(devc->mixer.bass);
- break;
-
- case SOUND_MIXER_TREBLE:
- or_mask = ret_vol_mono(devc->mixer.treble);
- break;
-
- case SOUND_MIXER_SYNTH:
- or_mask = ret_vol_mono(devc->mixer.synth);
- break;
- default:
- return -EINVAL;
- }
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
- val &= and_mask;
- val |= or_mask;
- if (put_user(val, (int __user *)arg))
- return -EFAULT;
- return val;
- }
-}
-
-static struct mixer_operations pss_mixer_operations =
-{
- .owner = THIS_MODULE,
- .id = "SOUNDPORT",
- .name = "PSS-AD1848",
- .ioctl = pss_mixer_ioctl
-};
-
-static void disable_all_emulations(void)
-{
- outw(0x0000, REG(CONF_PSS)); /* 0x0400 enables joystick */
- outw(0x0000, REG(CONF_WSS));
- outw(0x0000, REG(CONF_SB));
- outw(0x0000, REG(CONF_MIDI));
- outw(0x0000, REG(CONF_CDROM));
-}
-
-static void configure_nonsound_components(void)
-{
- /* Configure Joystick port */
-
- if(pss_enable_joystick)
- {
- outw(0x0400, REG(CONF_PSS)); /* 0x0400 enables joystick */
- printk(KERN_INFO "PSS: joystick enabled.\n");
- }
- else
- {
- printk(KERN_INFO "PSS: joystick port not enabled.\n");
- }
-
- /* Configure CDROM port */
-
- if (pss_cdrom_port == -1) { /* If cdrom port enablation wasn't requested */
- printk(KERN_INFO "PSS: CDROM port not enabled.\n");
- } else if (check_region(pss_cdrom_port, 2)) {
- printk(KERN_ERR "PSS: CDROM I/O port conflict.\n");
- } else {
- set_io_base(devc, CONF_CDROM, pss_cdrom_port);
- printk(KERN_INFO "PSS: CDROM I/O port set to 0x%x.\n", pss_cdrom_port);
- }
-}
-
-static int __init attach_pss(struct address_info *hw_config)
-{
- unsigned short id;
- char tmp[100];
-
- devc->base = hw_config->io_base;
- devc->irq = hw_config->irq;
- devc->dma = hw_config->dma;
- devc->osp = hw_config->osp;
- devc->ad_mixer_dev = NO_WSS_MIXER;
-
- if (!probe_pss(hw_config))
- return 0;
-
- id = inw(REG(PSS_ID)) & 0x00ff;
-
- /*
- * Disable all emulations. Will be enabled later (if required).
- */
-
- disable_all_emulations();
-
-#ifdef YOU_REALLY_WANT_TO_ALLOCATE_THESE_RESOURCES
- if (sound_alloc_dma(hw_config->dma, "PSS"))
- {
- printk("pss.c: Can't allocate DMA channel.\n");
- release_region(hw_config->io_base, 0x10);
- release_region(hw_config->io_base+0x10, 0x9);
- return 0;
- }
- if (!set_irq(devc, CONF_PSS, devc->irq))
- {
- printk("PSS: IRQ allocation error.\n");
- release_region(hw_config->io_base, 0x10);
- release_region(hw_config->io_base+0x10, 0x9);
- return 0;
- }
- if (!set_dma(devc, CONF_PSS, devc->dma))
- {
- printk(KERN_ERR "PSS: DMA allocation error\n");
- release_region(hw_config->io_base, 0x10);
- release_region(hw_config->io_base+0x10, 0x9);
- return 0;
- }
-#endif
-
- configure_nonsound_components();
- pss_initialized = 1;
- sprintf(tmp, "ECHO-PSS Rev. %d", id);
- conf_printf(tmp, hw_config);
- return 1;
-}
-
-static int __init probe_pss_mpu(struct address_info *hw_config)
-{
- struct resource *ports;
- int timeout;
-
- if (!pss_initialized)
- return 0;
-
- ports = request_region(hw_config->io_base, 2, "mpu401");
-
- if (!ports) {
- printk(KERN_ERR "PSS: MPU I/O port conflict\n");
- return 0;
- }
- set_io_base(devc, CONF_MIDI, hw_config->io_base);
- if (!set_irq(devc, CONF_MIDI, hw_config->irq)) {
- printk(KERN_ERR "PSS: MIDI IRQ allocation error.\n");
- goto fail;
- }
- if (!pss_synthLen) {
- printk(KERN_ERR "PSS: Can't enable MPU. MIDI synth microcode not available.\n");
- goto fail;
- }
- if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) {
- printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n");
- goto fail;
- }
-
- /*
- * Finally wait until the DSP algorithm has initialized itself and
- * deactivates receive interrupt.
- */
-
- for (timeout = 900000; timeout > 0; timeout--)
- {
- if ((inb(hw_config->io_base + 1) & 0x80) == 0) /* Input data avail */
- inb(hw_config->io_base); /* Discard it */
- else
- break; /* No more input */
- }
-
- if (!probe_mpu401(hw_config, ports))
- goto fail;
-
- attach_mpu401(hw_config, THIS_MODULE); /* Slot 1 */
- if (hw_config->slots[1] != -1) /* The MPU driver installed itself */
- midi_devs[hw_config->slots[1]]->coproc = &pss_coproc_operations;
- return 1;
-fail:
- release_region(hw_config->io_base, 2);
- return 0;
-}
-
-static int pss_coproc_open(void *dev_info, int sub_device)
-{
- switch (sub_device)
- {
- case COPR_MIDI:
- if (pss_synthLen == 0)
- {
- printk(KERN_ERR "PSS: MIDI synth microcode not available.\n");
- return -EIO;
- }
- if (nonstandard_microcode)
- if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
- {
- printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n");
- return -EIO;
- }
- nonstandard_microcode = 0;
- break;
-
- default:
- break;
- }
- return 0;
-}
-
-static void pss_coproc_close(void *dev_info, int sub_device)
-{
- return;
-}
-
-static void pss_coproc_reset(void *dev_info)
-{
- if (pss_synthLen)
- if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
- {
- printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n");
- }
- nonstandard_microcode = 0;
-}
-
-static int download_boot_block(void *dev_info, copr_buffer * buf)
-{
- if (buf->len <= 0 || buf->len > sizeof(buf->data))
- return -EINVAL;
-
- if (!pss_download_boot(devc, buf->data, buf->len, buf->flags))
- {
- printk(KERN_ERR "PSS: Unable to load microcode block to DSP.\n");
- return -EIO;
- }
- nonstandard_microcode = 1; /* The MIDI microcode has been overwritten */
- return 0;
-}
-
-static int pss_coproc_ioctl(void *dev_info, unsigned int cmd, void __user *arg, int local)
-{
- copr_buffer *buf;
- copr_msg *mbuf;
- copr_debug_buf dbuf;
- unsigned short tmp;
- unsigned long flags;
- unsigned short *data;
- int i, err;
- /* printk( "PSS coproc ioctl %x %x %d\n", cmd, arg, local); */
-
- switch (cmd)
- {
- case SNDCTL_COPR_RESET:
- pss_coproc_reset(dev_info);
- return 0;
-
- case SNDCTL_COPR_LOAD:
- buf = (copr_buffer *) vmalloc(sizeof(copr_buffer));
- if (buf == NULL)
- return -ENOSPC;
- if (copy_from_user(buf, arg, sizeof(copr_buffer))) {
- vfree(buf);
- return -EFAULT;
- }
- err = download_boot_block(dev_info, buf);
- vfree(buf);
- return err;
-
- case SNDCTL_COPR_SENDMSG:
- mbuf = (copr_msg *)vmalloc(sizeof(copr_msg));
- if (mbuf == NULL)
- return -ENOSPC;
- if (copy_from_user(mbuf, arg, sizeof(copr_msg))) {
- vfree(mbuf);
- return -EFAULT;
- }
- data = (unsigned short *)(mbuf->data);
- spin_lock_irqsave(&lock, flags);
- for (i = 0; i < mbuf->len; i++) {
- if (!pss_put_dspword(devc, *data++)) {
- spin_unlock_irqrestore(&lock,flags);
- mbuf->len = i; /* feed back number of WORDs sent */
- err = copy_to_user(arg, mbuf, sizeof(copr_msg));
- vfree(mbuf);
- return err ? -EFAULT : -EIO;
- }
- }
- spin_unlock_irqrestore(&lock,flags);
- vfree(mbuf);
- return 0;
-
- case SNDCTL_COPR_RCVMSG:
- err = 0;
- mbuf = (copr_msg *)vmalloc(sizeof(copr_msg));
- if (mbuf == NULL)
- return -ENOSPC;
- data = (unsigned short *)mbuf->data;
- spin_lock_irqsave(&lock, flags);
- for (i = 0; i < sizeof(mbuf->data)/sizeof(unsigned short); i++) {
- mbuf->len = i; /* feed back number of WORDs read */
- if (!pss_get_dspword(devc, data++)) {
- if (i == 0)
- err = -EIO;
- break;
- }
- }
- spin_unlock_irqrestore(&lock,flags);
- if (copy_to_user(arg, mbuf, sizeof(copr_msg)))
- err = -EFAULT;
- vfree(mbuf);
- return err;
-
- case SNDCTL_COPR_RDATA:
- if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
- return -EFAULT;
- spin_lock_irqsave(&lock, flags);
- if (!pss_put_dspword(devc, 0x00d0)) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- if (!pss_get_dspword(devc, &tmp)) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- dbuf.parm1 = tmp;
- spin_unlock_irqrestore(&lock,flags);
- if (copy_to_user(arg, &dbuf, sizeof(dbuf)))
- return -EFAULT;
- return 0;
-
- case SNDCTL_COPR_WDATA:
- if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
- return -EFAULT;
- spin_lock_irqsave(&lock, flags);
- if (!pss_put_dspword(devc, 0x00d1)) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- if (!pss_put_dspword(devc, (unsigned short) (dbuf.parm1 & 0xffff))) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- tmp = (unsigned int)dbuf.parm2 & 0xffff;
- if (!pss_put_dspword(devc, tmp)) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- spin_unlock_irqrestore(&lock,flags);
- return 0;
-
- case SNDCTL_COPR_WCODE:
- if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
- return -EFAULT;
- spin_lock_irqsave(&lock, flags);
- if (!pss_put_dspword(devc, 0x00d3)) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- tmp = (unsigned int)dbuf.parm2 & 0x00ff;
- if (!pss_put_dspword(devc, tmp)) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- tmp = ((unsigned int)dbuf.parm2 >> 8) & 0xffff;
- if (!pss_put_dspword(devc, tmp)) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- spin_unlock_irqrestore(&lock,flags);
- return 0;
-
- case SNDCTL_COPR_RCODE:
- if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
- return -EFAULT;
- spin_lock_irqsave(&lock, flags);
- if (!pss_put_dspword(devc, 0x00d2)) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- if (!pss_get_dspword(devc, &tmp)) { /* Read MSB */
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- dbuf.parm1 = tmp << 8;
- if (!pss_get_dspword(devc, &tmp)) { /* Read LSB */
- spin_unlock_irqrestore(&lock,flags);
- return -EIO;
- }
- dbuf.parm1 |= tmp & 0x00ff;
- spin_unlock_irqrestore(&lock,flags);
- if (copy_to_user(arg, &dbuf, sizeof(dbuf)))
- return -EFAULT;
- return 0;
-
- default:
- return -EINVAL;
- }
- return -EINVAL;
-}
-
-static coproc_operations pss_coproc_operations =
-{
- "ADSP-2115",
- THIS_MODULE,
- pss_coproc_open,
- pss_coproc_close,
- pss_coproc_ioctl,
- pss_coproc_reset,
- &pss_data
-};
-
-static int __init probe_pss_mss(struct address_info *hw_config)
-{
- volatile int timeout;
- struct resource *ports;
- int my_mix = -999; /* gcc shut up */
-
- if (!pss_initialized)
- return 0;
-
- if (!request_region(hw_config->io_base, 4, "WSS config")) {
- printk(KERN_ERR "PSS: WSS I/O port conflicts.\n");
- return 0;
- }
- ports = request_region(hw_config->io_base + 4, 4, "ad1848");
- if (!ports) {
- printk(KERN_ERR "PSS: WSS I/O port conflicts.\n");
- release_region(hw_config->io_base, 4);
- return 0;
- }
- set_io_base(devc, CONF_WSS, hw_config->io_base);
- if (!set_irq(devc, CONF_WSS, hw_config->irq)) {
- printk("PSS: WSS IRQ allocation error.\n");
- goto fail;
- }
- if (!set_dma(devc, CONF_WSS, hw_config->dma)) {
- printk(KERN_ERR "PSS: WSS DMA allocation error\n");
- goto fail;
- }
- /*
- * For some reason the card returns 0xff in the WSS status register
- * immediately after boot. Probably MIDI+SB emulation algorithm
- * downloaded to the ADSP2115 spends some time initializing the card.
- * Let's try to wait until it finishes this task.
- */
- for (timeout = 0; timeout < 100000 && (inb(hw_config->io_base + WSS_INDEX) &
- WSS_INITIALIZING); timeout++)
- ;
-
- outb((0x0b), hw_config->io_base + WSS_INDEX); /* Required by some cards */
-
- for (timeout = 0; (inb(hw_config->io_base + WSS_DATA) & WSS_AUTOCALIBRATION) &&
- (timeout < 100000); timeout++)
- ;
-
- if (!probe_ms_sound(hw_config, ports))
- goto fail;
-
- devc->ad_mixer_dev = NO_WSS_MIXER;
- if (pss_mixer)
- {
- if ((my_mix = sound_install_mixer (MIXER_DRIVER_VERSION,
- "PSS-SPEAKERS and AD1848 (through MSS audio codec)",
- &pss_mixer_operations,
- sizeof (struct mixer_operations),
- devc)) < 0)
- {
- printk(KERN_ERR "Could not install PSS mixer\n");
- goto fail;
- }
- }
- pss_mixer_reset(devc);
- attach_ms_sound(hw_config, ports, THIS_MODULE); /* Slot 0 */
-
- if (hw_config->slots[0] != -1)
- {
- /* The MSS driver installed itself */
- audio_devs[hw_config->slots[0]]->coproc = &pss_coproc_operations;
- if (pss_mixer && (num_mixers == (my_mix + 2)))
- {
- /* The MSS mixer installed */
- devc->ad_mixer_dev = audio_devs[hw_config->slots[0]]->mixer_dev;
- }
- }
- return 1;
-fail:
- release_region(hw_config->io_base + 4, 4);
- release_region(hw_config->io_base, 4);
- return 0;
-}
-
-static inline void __exit unload_pss(struct address_info *hw_config)
-{
- release_region(hw_config->io_base, 0x10);
- release_region(hw_config->io_base+0x10, 0x9);
-}
-
-static inline void __exit unload_pss_mpu(struct address_info *hw_config)
-{
- unload_mpu401(hw_config);
-}
-
-static inline void __exit unload_pss_mss(struct address_info *hw_config)
-{
- unload_ms_sound(hw_config);
-}
-
-
-static struct address_info cfg;
-static struct address_info cfg2;
-static struct address_info cfg_mpu;
-
-static int pss_io __initdata = -1;
-static int mss_io __initdata = -1;
-static int mss_irq __initdata = -1;
-static int mss_dma __initdata = -1;
-static int mpu_io __initdata = -1;
-static int mpu_irq __initdata = -1;
-static int pss_no_sound = 0; /* Just configure non-sound components */
-static int pss_keep_settings = 1; /* Keep hardware settings at module exit */
-static char *pss_firmware = "/etc/sound/pss_synth";
-
-module_param(pss_io, int, 0);
-MODULE_PARM_DESC(pss_io, "Set i/o base of PSS card (probably 0x220 or 0x240)");
-module_param(mss_io, int, 0);
-MODULE_PARM_DESC(mss_io, "Set WSS (audio) i/o base (0x530, 0x604, 0xE80, 0xF40, or other. Address must end in 0 or 4 and must be from 0x100 to 0xFF4)");
-module_param(mss_irq, int, 0);
-MODULE_PARM_DESC(mss_irq, "Set WSS (audio) IRQ (3, 5, 7, 9, 10, 11, 12)");
-module_param(mss_dma, int, 0);
-MODULE_PARM_DESC(mss_dma, "Set WSS (audio) DMA (0, 1, 3)");
-module_param(mpu_io, int, 0);
-MODULE_PARM_DESC(mpu_io, "Set MIDI i/o base (0x330 or other. Address must be on 4 location boundaries and must be from 0x100 to 0xFFC)");
-module_param(mpu_irq, int, 0);
-MODULE_PARM_DESC(mpu_irq, "Set MIDI IRQ (3, 5, 7, 9, 10, 11, 12)");
-module_param(pss_cdrom_port, int, 0);
-MODULE_PARM_DESC(pss_cdrom_port, "Set the PSS CDROM port i/o base (0x340 or other)");
-module_param(pss_enable_joystick, bool, 0);
-MODULE_PARM_DESC(pss_enable_joystick, "Enables the PSS joystick port (1 to enable, 0 to disable)");
-module_param(pss_no_sound, bool, 0);
-MODULE_PARM_DESC(pss_no_sound, "Configure sound compoents (0 - no, 1 - yes)");
-module_param(pss_keep_settings, bool, 0);
-MODULE_PARM_DESC(pss_keep_settings, "Keep hardware setting at driver unloading (0 - no, 1 - yes)");
-module_param(pss_firmware, charp, 0);
-MODULE_PARM_DESC(pss_firmware, "Location of the firmware file (default - /etc/sound/pss_synth)");
-module_param(pss_mixer, bool, 0);
-MODULE_PARM_DESC(pss_mixer, "Enable (1) or disable (0) PSS mixer (controlling of output volume, bass, treble, synth volume). The mixer is not available on all PSS cards.");
-MODULE_AUTHOR("Hannu Savolainen, Vladimir Michl");
-MODULE_DESCRIPTION("Module for PSS sound cards (based on AD1848, ADSP-2115 and ESC614). This module includes control of output amplifier and synth volume of the Beethoven ADSP-16 card (this may work with other PSS cards).");
-MODULE_LICENSE("GPL");
-
-
-static int fw_load = 0;
-static int pssmpu = 0, pssmss = 0;
-
-/*
- * Load a PSS sound card module
- */
-
-static int __init init_pss(void)
-{
-
- if(pss_no_sound) /* If configuring only nonsound components */
- {
- cfg.io_base = pss_io;
- if(!probe_pss(&cfg))
- return -ENODEV;
- printk(KERN_INFO "ECHO-PSS Rev. %d\n", inw(REG(PSS_ID)) & 0x00ff);
- printk(KERN_INFO "PSS: loading in no sound mode.\n");
- disable_all_emulations();
- configure_nonsound_components();
- release_region(pss_io, 0x10);
- release_region(pss_io + 0x10, 0x9);
- return 0;
- }
-
- cfg.io_base = pss_io;
-
- cfg2.io_base = mss_io;
- cfg2.irq = mss_irq;
- cfg2.dma = mss_dma;
-
- cfg_mpu.io_base = mpu_io;
- cfg_mpu.irq = mpu_irq;
-
- if (cfg.io_base == -1 || cfg2.io_base == -1 || cfg2.irq == -1 || cfg.dma == -1) {
- printk(KERN_INFO "pss: mss_io, mss_dma, mss_irq and pss_io must be set.\n");
- return -EINVAL;
- }
-
- if (!pss_synth) {
- fw_load = 1;
- pss_synthLen = mod_firmware_load(pss_firmware, (void *) &pss_synth);
- }
- if (!attach_pss(&cfg))
- return -ENODEV;
- /*
- * Attach stuff
- */
- if (probe_pss_mpu(&cfg_mpu))
- pssmpu = 1;
-
- if (probe_pss_mss(&cfg2))
- pssmss = 1;
-
- return 0;
-}
-
-static void __exit cleanup_pss(void)
-{
- if(!pss_no_sound)
- {
- if(fw_load && pss_synth)
- vfree(pss_synth);
- if(pssmss)
- unload_pss_mss(&cfg2);
- if(pssmpu)
- unload_pss_mpu(&cfg_mpu);
- unload_pss(&cfg);
- }
-
- if(!pss_keep_settings) /* Keep hardware settings if asked */
- {
- disable_all_emulations();
- printk(KERN_INFO "Resetting PSS sound card configurations.\n");
- }
-}
-
-module_init(init_pss);
-module_exit(cleanup_pss);
-
-#ifndef MODULE
-static int __init setup_pss(char *str)
-{
- /* io, mss_io, mss_irq, mss_dma, mpu_io, mpu_irq */
- int ints[7];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- pss_io = ints[1];
- mss_io = ints[2];
- mss_irq = ints[3];
- mss_dma = ints[4];
- mpu_io = ints[5];
- mpu_irq = ints[6];
-
- return 1;
-}
-
-__setup("pss=", setup_pss);
-#endif
diff --git a/sound/oss/sb.h b/sound/oss/sb.h
deleted file mode 100644
index 77e8891ce333..000000000000
--- a/sound/oss/sb.h
+++ /dev/null
@@ -1,185 +0,0 @@
-#define DSP_RESET (devc->base + 0x6)
-#define DSP_READ (devc->base + 0xA)
-#define DSP_WRITE (devc->base + 0xC)
-#define DSP_COMMAND (devc->base + 0xC)
-#define DSP_STATUS (devc->base + 0xC)
-#define DSP_DATA_AVAIL (devc->base + 0xE)
-#define DSP_DATA_AVL16 (devc->base + 0xF)
-#define MIXER_ADDR (devc->base + 0x4)
-#define MIXER_DATA (devc->base + 0x5)
-#define OPL3_LEFT (devc->base + 0x0)
-#define OPL3_RIGHT (devc->base + 0x2)
-#define OPL3_BOTH (devc->base + 0x8)
-/* DSP Commands */
-
-#define DSP_CMD_SPKON 0xD1
-#define DSP_CMD_SPKOFF 0xD3
-#define DSP_CMD_DMAON 0xD0
-#define DSP_CMD_DMAOFF 0xD4
-
-#define IMODE_NONE 0
-#define IMODE_OUTPUT PCM_ENABLE_OUTPUT
-#define IMODE_INPUT PCM_ENABLE_INPUT
-#define IMODE_INIT 3
-#define IMODE_MIDI 4
-
-#define NORMAL_MIDI 0
-#define UART_MIDI 1
-
-
-/*
- * Device models
- */
-#define MDL_NONE 0
-#define MDL_SB1 1 /* SB1.0 or 1.5 */
-#define MDL_SB2 2 /* SB2.0 */
-#define MDL_SB201 3 /* SB2.01 */
-#define MDL_SBPRO 4 /* SB Pro */
-#define MDL_SB16 5 /* SB16/32/AWE */
-#define MDL_SBPNP 6 /* SB16/32/AWE PnP */
-#define MDL_JAZZ 10 /* Media Vision Jazz16 */
-#define MDL_SMW 11 /* Logitech SoundMan Wave (Jazz16) */
-#define MDL_ESS 12 /* ESS ES688 and ES1688 */
-#define MDL_AZTECH 13 /* Aztech Sound Galaxy family */
-#define MDL_ES1868MIDI 14 /* MIDI port of ESS1868 */
-#define MDL_AEDSP 15 /* Audio Excel DSP 16 */
-#define MDL_ESSPCI 16 /* ESS PCI card */
-#define MDL_YMPCI 17 /* Yamaha PCI sb in emulation */
-
-#define SUBMDL_ALS007 42 /* ALS-007 differs from SB16 only in mixer */
- /* register assignment */
-#define SUBMDL_ALS100 43 /* ALS-100 allows sampling rates of up */
- /* to 48kHz */
-
-/*
- * Config flags
- */
-#define SB_NO_MIDI 0x00000001
-#define SB_NO_MIXER 0x00000002
-#define SB_NO_AUDIO 0x00000004
-#define SB_NO_RECORDING 0x00000008 /* No audio recording */
-#define SB_MIDI_ONLY (SB_NO_AUDIO|SB_NO_MIXER)
-#define SB_PCI_IRQ 0x00000010 /* PCI shared IRQ */
-
-struct mixer_def {
- unsigned int regno: 8;
- unsigned int bitoffs:4;
- unsigned int nbits:4;
-};
-
-typedef struct mixer_def mixer_tab[32][2];
-typedef struct mixer_def mixer_ent;
-
-struct sb_module_options
-{
- int esstype; /* ESS chip type */
- int acer; /* Do acer notebook init? */
- int sm_games; /* Logitech soundman games? */
-};
-
-typedef struct sb_devc {
- int dev;
-
- /* Hardware parameters */
- int *osp;
- int minor, major;
- int type;
- int model, submodel;
- int caps;
-# define SBCAP_STEREO 0x00000001
-# define SBCAP_16BITS 0x00000002
-
- /* Hardware resources */
- int base;
- int irq;
- int dma8, dma16;
-
- int pcibase; /* For ESS Maestro etc */
-
- /* State variables */
- int opened;
- /* new audio fields for full duplex support */
- int fullduplex;
- int duplex;
- int speed, bits, channels;
- volatile int irq_ok;
- volatile int intr_active, irq_mode;
- /* duplicate audio fields for full duplex support */
- volatile int intr_active_16, irq_mode_16;
-
- /* Mixer fields */
- int *levels;
- mixer_tab *iomap;
- size_t iomap_sz; /* number or records in the iomap table */
- int mixer_caps, recmask, outmask, supported_devices;
- int supported_rec_devices, supported_out_devices;
- int my_mixerdev;
- int sbmixnum;
-
- /* Audio fields */
- unsigned long trg_buf;
- int trigger_bits;
- int trg_bytes;
- int trg_intrflag;
- int trg_restart;
- /* duplicate audio fields for full duplex support */
- unsigned long trg_buf_16;
- int trigger_bits_16;
- int trg_bytes_16;
- int trg_intrflag_16;
- int trg_restart_16;
-
- unsigned char tconst;
-
- /* MIDI fields */
- int my_mididev;
- int input_opened;
- int midi_broken;
- void (*midi_input_intr) (int dev, unsigned char data);
- void *midi_irq_cookie; /* IRQ cookie for the midi */
-
- spinlock_t lock;
-
- struct sb_module_options sbmo; /* Module options */
-
- } sb_devc;
-
-/*
- * PCI card types
- */
-
-#define SB_PCI_ESSMAESTRO 1 /* ESS Maestro Legacy */
-#define SB_PCI_YAMAHA 2 /* Yamaha Legacy */
-
-/*
- * Functions
- */
-
-int sb_dsp_command (sb_devc *devc, unsigned char val);
-int sb_dsp_get_byte(sb_devc * devc);
-int sb_dsp_reset (sb_devc *devc);
-void sb_setmixer (sb_devc *devc, unsigned int port, unsigned int value);
-unsigned int sb_getmixer (sb_devc *devc, unsigned int port);
-int sb_dsp_detect (struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo);
-int sb_dsp_init (struct address_info *hw_config, struct module *owner);
-void sb_dsp_unload(struct address_info *hw_config, int sbmpu);
-int sb_mixer_init(sb_devc *devc, struct module *owner);
-void sb_mixer_unload(sb_devc *devc);
-void sb_mixer_set_stereo (sb_devc *devc, int mode);
-void smw_mixer_init(sb_devc *devc);
-void sb_dsp_midi_init (sb_devc *devc, struct module *owner);
-void sb_audio_init (sb_devc *devc, char *name, struct module *owner);
-void sb_midi_interrupt (sb_devc *devc);
-void sb_chgmixer (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val);
-int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right);
-
-int sb_audio_open(int dev, int mode);
-void sb_audio_close(int dev);
-
-/* From sb_common.c */
-void sb_dsp_disable_midi(int port);
-int probe_sbmpu (struct address_info *hw_config, struct module *owner);
-void unload_sbmpu (struct address_info *hw_config);
-
-void unload_sb16(struct address_info *hw_info);
-void unload_sb16midi(struct address_info *hw_info);
diff --git a/sound/oss/sb_audio.c b/sound/oss/sb_audio.c
deleted file mode 100644
index 733b014ec7d1..000000000000
--- a/sound/oss/sb_audio.c
+++ /dev/null
@@ -1,1098 +0,0 @@
-/*
- * sound/oss/sb_audio.c
- *
- * Audio routines for Sound Blaster compatible cards.
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- * Changes
- * Alan Cox : Formatting and clean ups
- *
- * Status
- * Mostly working. Weird uart bug causing irq storms
- *
- * Daniel J. Rodriksson: Changes to make sb16 work full duplex.
- * Maybe other 16 bit cards in this code could behave
- * the same.
- * Chris Rankin: Use spinlocks instead of CLI/STI
- */
-
-#include <linux/spinlock.h>
-
-#include "sound_config.h"
-
-#include "sb_mixer.h"
-#include "sb.h"
-
-#include "sb_ess.h"
-
-int sb_audio_open(int dev, int mode)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned long flags;
-
- if (devc == NULL)
- {
- printk(KERN_ERR "Sound Blaster: incomplete initialization.\n");
- return -ENXIO;
- }
- if (devc->caps & SB_NO_RECORDING && mode & OPEN_READ)
- {
- if (mode == OPEN_READ)
- return -EPERM;
- }
- spin_lock_irqsave(&devc->lock, flags);
- if (devc->opened)
- {
- spin_unlock_irqrestore(&devc->lock, flags);
- return -EBUSY;
- }
- if (devc->dma16 != -1 && devc->dma16 != devc->dma8 && !devc->duplex)
- {
- if (sound_open_dma(devc->dma16, "Sound Blaster 16 bit"))
- {
- spin_unlock_irqrestore(&devc->lock, flags);
- return -EBUSY;
- }
- }
- devc->opened = mode;
- spin_unlock_irqrestore(&devc->lock, flags);
-
- devc->irq_mode = IMODE_NONE;
- devc->irq_mode_16 = IMODE_NONE;
- devc->fullduplex = devc->duplex &&
- ((mode & OPEN_READ) && (mode & OPEN_WRITE));
- sb_dsp_reset(devc);
-
- /* At first glance this check isn't enough, some ESS chips might not
- * have a RECLEV. However if they don't common_mixer_set will refuse
- * cause devc->iomap has no register mapping for RECLEV
- */
- if (devc->model == MDL_ESS) ess_mixer_reload (devc, SOUND_MIXER_RECLEV);
-
- /* The ALS007 seems to require that the DSP be removed from the output */
- /* in order for recording to be activated properly. This is done by */
- /* setting the appropriate bits of the output control register 4ch to */
- /* zero. This code assumes that the output control registers are not */
- /* used anywhere else and therefore the DSP bits are *always* ON for */
- /* output and OFF for sampling. */
-
- if (devc->submodel == SUBMDL_ALS007)
- {
- if (mode & OPEN_READ)
- sb_setmixer(devc,ALS007_OUTPUT_CTRL2,
- sb_getmixer(devc,ALS007_OUTPUT_CTRL2) & 0xf9);
- else
- sb_setmixer(devc,ALS007_OUTPUT_CTRL2,
- sb_getmixer(devc,ALS007_OUTPUT_CTRL2) | 0x06);
- }
- return 0;
-}
-
-void sb_audio_close(int dev)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- /* fix things if mmap turned off fullduplex */
- if(devc->duplex
- && !devc->fullduplex
- && (devc->opened & OPEN_READ) && (devc->opened & OPEN_WRITE))
- {
- struct dma_buffparms *dmap_temp;
- dmap_temp = audio_devs[dev]->dmap_out;
- audio_devs[dev]->dmap_out = audio_devs[dev]->dmap_in;
- audio_devs[dev]->dmap_in = dmap_temp;
- }
- audio_devs[dev]->dmap_out->dma = devc->dma8;
- audio_devs[dev]->dmap_in->dma = ( devc->duplex ) ?
- devc->dma16 : devc->dma8;
-
- if (devc->dma16 != -1 && devc->dma16 != devc->dma8 && !devc->duplex)
- sound_close_dma(devc->dma16);
-
- /* For ALS007, turn DSP output back on if closing the device for read */
-
- if ((devc->submodel == SUBMDL_ALS007) && (devc->opened & OPEN_READ))
- {
- sb_setmixer(devc,ALS007_OUTPUT_CTRL2,
- sb_getmixer(devc,ALS007_OUTPUT_CTRL2) | 0x06);
- }
- devc->opened = 0;
-}
-
-static void sb_set_output_parms(int dev, unsigned long buf, int nr_bytes,
- int intrflag)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (!devc->fullduplex || devc->bits == AFMT_S16_LE)
- {
- devc->trg_buf = buf;
- devc->trg_bytes = nr_bytes;
- devc->trg_intrflag = intrflag;
- devc->irq_mode = IMODE_OUTPUT;
- }
- else
- {
- devc->trg_buf_16 = buf;
- devc->trg_bytes_16 = nr_bytes;
- devc->trg_intrflag_16 = intrflag;
- devc->irq_mode_16 = IMODE_OUTPUT;
- }
-}
-
-static void sb_set_input_parms(int dev, unsigned long buf, int count, int intrflag)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (!devc->fullduplex || devc->bits != AFMT_S16_LE)
- {
- devc->trg_buf = buf;
- devc->trg_bytes = count;
- devc->trg_intrflag = intrflag;
- devc->irq_mode = IMODE_INPUT;
- }
- else
- {
- devc->trg_buf_16 = buf;
- devc->trg_bytes_16 = count;
- devc->trg_intrflag_16 = intrflag;
- devc->irq_mode_16 = IMODE_INPUT;
- }
-}
-
-/*
- * SB1.x compatible routines
- */
-
-static void sb1_audio_output_block(int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
- unsigned long flags;
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
- count--;
-
- devc->irq_mode = IMODE_OUTPUT;
-
- spin_lock_irqsave(&devc->lock, flags);
- if (sb_dsp_command(devc, 0x14)) /* 8 bit DAC using DMA */
- {
- sb_dsp_command(devc, (unsigned char) (count & 0xff));
- sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
- }
- else
- printk(KERN_WARNING "Sound Blaster: unable to start DAC.\n");
- spin_unlock_irqrestore(&devc->lock, flags);
- devc->intr_active = 1;
-}
-
-static void sb1_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
- unsigned long flags;
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
-
- /*
- * Start a DMA input to the buffer pointed by dmaqtail
- */
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
- count--;
-
- devc->irq_mode = IMODE_INPUT;
-
- spin_lock_irqsave(&devc->lock, flags);
- if (sb_dsp_command(devc, 0x24)) /* 8 bit ADC using DMA */
- {
- sb_dsp_command(devc, (unsigned char) (count & 0xff));
- sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
- }
- else
- printk(KERN_ERR "Sound Blaster: unable to start ADC.\n");
- spin_unlock_irqrestore(&devc->lock, flags);
-
- devc->intr_active = 1;
-}
-
-static void sb1_audio_trigger(int dev, int bits)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- bits &= devc->irq_mode;
-
- if (!bits)
- sb_dsp_command(devc, 0xd0); /* Halt DMA */
- else
- {
- switch (devc->irq_mode)
- {
- case IMODE_INPUT:
- sb1_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
-
- case IMODE_OUTPUT:
- sb1_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
- }
- }
- devc->trigger_bits = bits;
-}
-
-static int sb1_audio_prepare_for_input(int dev, int bsize, int bcount)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock, flags);
- if (sb_dsp_command(devc, 0x40))
- sb_dsp_command(devc, devc->tconst);
- sb_dsp_command(devc, DSP_CMD_SPKOFF);
- spin_unlock_irqrestore(&devc->lock, flags);
-
- devc->trigger_bits = 0;
- return 0;
-}
-
-static int sb1_audio_prepare_for_output(int dev, int bsize, int bcount)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock, flags);
- if (sb_dsp_command(devc, 0x40))
- sb_dsp_command(devc, devc->tconst);
- sb_dsp_command(devc, DSP_CMD_SPKON);
- spin_unlock_irqrestore(&devc->lock, flags);
- devc->trigger_bits = 0;
- return 0;
-}
-
-static int sb1_audio_set_speed(int dev, int speed)
-{
- int max_speed = 23000;
- sb_devc *devc = audio_devs[dev]->devc;
- int tmp;
-
- if (devc->opened & OPEN_READ)
- max_speed = 13000;
-
- if (speed > 0)
- {
- if (speed < 4000)
- speed = 4000;
-
- if (speed > max_speed)
- speed = max_speed;
-
- devc->tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff;
- tmp = 256 - devc->tconst;
- speed = (1000000 + tmp / 2) / tmp;
-
- devc->speed = speed;
- }
- return devc->speed;
-}
-
-static short sb1_audio_set_channels(int dev, short channels)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- return devc->channels = 1;
-}
-
-static unsigned int sb1_audio_set_bits(int dev, unsigned int bits)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- return devc->bits = 8;
-}
-
-static void sb1_audio_halt_xfer(int dev)
-{
- unsigned long flags;
- sb_devc *devc = audio_devs[dev]->devc;
-
- spin_lock_irqsave(&devc->lock, flags);
- sb_dsp_reset(devc);
- spin_unlock_irqrestore(&devc->lock, flags);
-}
-
-/*
- * SB 2.0 and SB 2.01 compatible routines
- */
-
-static void sb20_audio_output_block(int dev, unsigned long buf, int nr_bytes,
- int intrflag)
-{
- unsigned long flags;
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned char cmd;
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
- count--;
-
- devc->irq_mode = IMODE_OUTPUT;
-
- spin_lock_irqsave(&devc->lock, flags);
- if (sb_dsp_command(devc, 0x48)) /* DSP Block size */
- {
- sb_dsp_command(devc, (unsigned char) (count & 0xff));
- sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
-
- if (devc->speed * devc->channels <= 23000)
- cmd = 0x1c; /* 8 bit PCM output */
- else
- cmd = 0x90; /* 8 bit high speed PCM output (SB2.01/Pro) */
-
- if (!sb_dsp_command(devc, cmd))
- printk(KERN_ERR "Sound Blaster: unable to start DAC.\n");
- }
- else
- printk(KERN_ERR "Sound Blaster: unable to start DAC.\n");
- spin_unlock_irqrestore(&devc->lock, flags);
- devc->intr_active = 1;
-}
-
-static void sb20_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
- unsigned long flags;
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned char cmd;
-
- /*
- * Start a DMA input to the buffer pointed by dmaqtail
- */
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
- count--;
-
- devc->irq_mode = IMODE_INPUT;
-
- spin_lock_irqsave(&devc->lock, flags);
- if (sb_dsp_command(devc, 0x48)) /* DSP Block size */
- {
- sb_dsp_command(devc, (unsigned char) (count & 0xff));
- sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff));
-
- if (devc->speed * devc->channels <= (devc->major == 3 ? 23000 : 13000))
- cmd = 0x2c; /* 8 bit PCM input */
- else
- cmd = 0x98; /* 8 bit high speed PCM input (SB2.01/Pro) */
-
- if (!sb_dsp_command(devc, cmd))
- printk(KERN_ERR "Sound Blaster: unable to start ADC.\n");
- }
- else
- printk(KERN_ERR "Sound Blaster: unable to start ADC.\n");
- spin_unlock_irqrestore(&devc->lock, flags);
- devc->intr_active = 1;
-}
-
-static void sb20_audio_trigger(int dev, int bits)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- bits &= devc->irq_mode;
-
- if (!bits)
- sb_dsp_command(devc, 0xd0); /* Halt DMA */
- else
- {
- switch (devc->irq_mode)
- {
- case IMODE_INPUT:
- sb20_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
-
- case IMODE_OUTPUT:
- sb20_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
- }
- }
- devc->trigger_bits = bits;
-}
-
-/*
- * SB2.01 specific speed setup
- */
-
-static int sb201_audio_set_speed(int dev, int speed)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- int tmp;
- int s = speed * devc->channels;
-
- if (speed > 0)
- {
- if (speed < 4000)
- speed = 4000;
- if (speed > 44100)
- speed = 44100;
- if (devc->opened & OPEN_READ && speed > 15000)
- speed = 15000;
- devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff;
- tmp = 256 - devc->tconst;
- speed = ((1000000 + tmp / 2) / tmp) / devc->channels;
-
- devc->speed = speed;
- }
- return devc->speed;
-}
-
-/*
- * SB Pro specific routines
- */
-
-static int sbpro_audio_prepare_for_input(int dev, int bsize, int bcount)
-{ /* For SB Pro and Jazz16 */
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned long flags;
- unsigned char bits = 0;
-
- if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
- audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma =
- devc->bits == 16 ? devc->dma16 : devc->dma8;
-
- if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
- if (devc->bits == AFMT_S16_LE)
- bits = 0x04; /* 16 bit mode */
-
- spin_lock_irqsave(&devc->lock, flags);
- if (sb_dsp_command(devc, 0x40))
- sb_dsp_command(devc, devc->tconst);
- sb_dsp_command(devc, DSP_CMD_SPKOFF);
- if (devc->channels == 1)
- sb_dsp_command(devc, 0xa0 | bits); /* Mono input */
- else
- sb_dsp_command(devc, 0xa8 | bits); /* Stereo input */
- spin_unlock_irqrestore(&devc->lock, flags);
-
- devc->trigger_bits = 0;
- return 0;
-}
-
-static int sbpro_audio_prepare_for_output(int dev, int bsize, int bcount)
-{ /* For SB Pro and Jazz16 */
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned long flags;
- unsigned char tmp;
- unsigned char bits = 0;
-
- if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
- audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma = devc->bits == 16 ? devc->dma16 : devc->dma8;
- if (devc->model == MDL_SBPRO)
- sb_mixer_set_stereo(devc, devc->channels == 2);
-
- spin_lock_irqsave(&devc->lock, flags);
- if (sb_dsp_command(devc, 0x40))
- sb_dsp_command(devc, devc->tconst);
- sb_dsp_command(devc, DSP_CMD_SPKON);
-
- if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
- {
- if (devc->bits == AFMT_S16_LE)
- bits = 0x04; /* 16 bit mode */
-
- if (devc->channels == 1)
- sb_dsp_command(devc, 0xa0 | bits); /* Mono output */
- else
- sb_dsp_command(devc, 0xa8 | bits); /* Stereo output */
- spin_unlock_irqrestore(&devc->lock, flags);
- }
- else
- {
- spin_unlock_irqrestore(&devc->lock, flags);
- tmp = sb_getmixer(devc, 0x0e);
- if (devc->channels == 1)
- tmp &= ~0x02;
- else
- tmp |= 0x02;
- sb_setmixer(devc, 0x0e, tmp);
- }
- devc->trigger_bits = 0;
- return 0;
-}
-
-static int sbpro_audio_set_speed(int dev, int speed)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (speed > 0)
- {
- if (speed < 4000)
- speed = 4000;
- if (speed > 44100)
- speed = 44100;
- if (devc->channels > 1 && speed > 22050)
- speed = 22050;
- sb201_audio_set_speed(dev, speed);
- }
- return devc->speed;
-}
-
-static short sbpro_audio_set_channels(int dev, short channels)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (channels == 1 || channels == 2)
- {
- if (channels != devc->channels)
- {
- devc->channels = channels;
- if (devc->model == MDL_SBPRO && devc->channels == 2)
- sbpro_audio_set_speed(dev, devc->speed);
- }
- }
- return devc->channels;
-}
-
-static int jazz16_audio_set_speed(int dev, int speed)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (speed > 0)
- {
- int tmp;
- int s = speed * devc->channels;
-
- if (speed < 5000)
- speed = 5000;
- if (speed > 44100)
- speed = 44100;
-
- devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff;
-
- tmp = 256 - devc->tconst;
- speed = ((1000000 + tmp / 2) / tmp) / devc->channels;
-
- devc->speed = speed;
- }
- return devc->speed;
-}
-
-/*
- * SB16 specific routines
- */
-
-static int sb16_audio_set_speed(int dev, int speed)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- int max_speed = devc->submodel == SUBMDL_ALS100 ? 48000 : 44100;
-
- if (speed > 0)
- {
- if (speed < 5000)
- speed = 5000;
-
- if (speed > max_speed)
- speed = max_speed;
-
- devc->speed = speed;
- }
- return devc->speed;
-}
-
-static unsigned int sb16_audio_set_bits(int dev, unsigned int bits)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (bits != 0)
- {
- if (bits == AFMT_U8 || bits == AFMT_S16_LE)
- devc->bits = bits;
- else
- devc->bits = AFMT_U8;
- }
-
- return devc->bits;
-}
-
-static int sb16_audio_prepare_for_input(int dev, int bsize, int bcount)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (!devc->fullduplex)
- {
- audio_devs[dev]->dmap_out->dma =
- audio_devs[dev]->dmap_in->dma =
- devc->bits == AFMT_S16_LE ?
- devc->dma16 : devc->dma8;
- }
- else if (devc->bits == AFMT_S16_LE)
- {
- audio_devs[dev]->dmap_out->dma = devc->dma8;
- audio_devs[dev]->dmap_in->dma = devc->dma16;
- }
- else
- {
- audio_devs[dev]->dmap_out->dma = devc->dma16;
- audio_devs[dev]->dmap_in->dma = devc->dma8;
- }
-
- devc->trigger_bits = 0;
- return 0;
-}
-
-static int sb16_audio_prepare_for_output(int dev, int bsize, int bcount)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (!devc->fullduplex)
- {
- audio_devs[dev]->dmap_out->dma =
- audio_devs[dev]->dmap_in->dma =
- devc->bits == AFMT_S16_LE ?
- devc->dma16 : devc->dma8;
- }
- else if (devc->bits == AFMT_S16_LE)
- {
- audio_devs[dev]->dmap_out->dma = devc->dma8;
- audio_devs[dev]->dmap_in->dma = devc->dma16;
- }
- else
- {
- audio_devs[dev]->dmap_out->dma = devc->dma16;
- audio_devs[dev]->dmap_in->dma = devc->dma8;
- }
-
- devc->trigger_bits = 0;
- return 0;
-}
-
-static void sb16_audio_output_block(int dev, unsigned long buf, int count,
- int intrflag)
-{
- unsigned long flags, cnt;
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned long bits;
-
- if (!devc->fullduplex || devc->bits == AFMT_S16_LE)
- {
- devc->irq_mode = IMODE_OUTPUT;
- devc->intr_active = 1;
- }
- else
- {
- devc->irq_mode_16 = IMODE_OUTPUT;
- devc->intr_active_16 = 1;
- }
-
- /* save value */
- spin_lock_irqsave(&devc->lock, flags);
- bits = devc->bits;
- if (devc->fullduplex)
- devc->bits = (devc->bits == AFMT_S16_LE) ?
- AFMT_U8 : AFMT_S16_LE;
- spin_unlock_irqrestore(&devc->lock, flags);
-
- cnt = count;
- if (devc->bits == AFMT_S16_LE)
- cnt >>= 1;
- cnt--;
-
- spin_lock_irqsave(&devc->lock, flags);
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
-
- sb_dsp_command(devc, 0x41);
- sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff));
- sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff));
-
- sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xb6 : 0xc6));
- sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) +
- (devc->bits == AFMT_S16_LE ? 0x10 : 0)));
- sb_dsp_command(devc, (unsigned char) (cnt & 0xff));
- sb_dsp_command(devc, (unsigned char) (cnt >> 8));
-
- /* restore real value after all programming */
- devc->bits = bits;
- spin_unlock_irqrestore(&devc->lock, flags);
-}
-
-
-/*
- * This fails on the Cyrix MediaGX. If you don't have the DMA enabled
- * before the first sample arrives it locks up. However even if you
- * do enable the DMA in time you just get DMA timeouts and missing
- * interrupts and stuff, so for now I've not bothered fixing this either.
- */
-
-static void sb16_audio_start_input(int dev, unsigned long buf, int count, int intrflag)
-{
- unsigned long flags, cnt;
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (!devc->fullduplex || devc->bits != AFMT_S16_LE)
- {
- devc->irq_mode = IMODE_INPUT;
- devc->intr_active = 1;
- }
- else
- {
- devc->irq_mode_16 = IMODE_INPUT;
- devc->intr_active_16 = 1;
- }
-
- cnt = count;
- if (devc->bits == AFMT_S16_LE)
- cnt >>= 1;
- cnt--;
-
- spin_lock_irqsave(&devc->lock, flags);
-
- /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
-
- sb_dsp_command(devc, 0x42);
- sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff));
- sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff));
-
- sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xbe : 0xce));
- sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) +
- (devc->bits == AFMT_S16_LE ? 0x10 : 0)));
- sb_dsp_command(devc, (unsigned char) (cnt & 0xff));
- sb_dsp_command(devc, (unsigned char) (cnt >> 8));
-
- spin_unlock_irqrestore(&devc->lock, flags);
-}
-
-static void sb16_audio_trigger(int dev, int bits)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- int bits_16 = bits & devc->irq_mode_16;
- bits &= devc->irq_mode;
-
- if (!bits && !bits_16)
- sb_dsp_command(devc, 0xd0); /* Halt DMA */
- else
- {
- if (bits)
- {
- switch (devc->irq_mode)
- {
- case IMODE_INPUT:
- sb16_audio_start_input(dev,
- devc->trg_buf,
- devc->trg_bytes,
- devc->trg_intrflag);
- break;
-
- case IMODE_OUTPUT:
- sb16_audio_output_block(dev,
- devc->trg_buf,
- devc->trg_bytes,
- devc->trg_intrflag);
- break;
- }
- }
- if (bits_16)
- {
- switch (devc->irq_mode_16)
- {
- case IMODE_INPUT:
- sb16_audio_start_input(dev,
- devc->trg_buf_16,
- devc->trg_bytes_16,
- devc->trg_intrflag_16);
- break;
-
- case IMODE_OUTPUT:
- sb16_audio_output_block(dev,
- devc->trg_buf_16,
- devc->trg_bytes_16,
- devc->trg_intrflag_16);
- break;
- }
- }
- }
-
- devc->trigger_bits = bits | bits_16;
-}
-
-static unsigned char lbuf8[2048];
-static signed short *lbuf16 = (signed short *)lbuf8;
-#define LBUFCOPYSIZE 1024
-static void
-sb16_copy_from_user(int dev,
- char *localbuf, int localoffs,
- const char __user *userbuf, int useroffs,
- int max_in, int max_out,
- int *used, int *returned,
- int len)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- int i, c, p, locallen;
- unsigned char *buf8;
- signed short *buf16;
-
- /* if not duplex no conversion */
- if (!devc->fullduplex)
- {
- if (copy_from_user(localbuf + localoffs,
- userbuf + useroffs, len))
- return;
- *used = len;
- *returned = len;
- }
- else if (devc->bits == AFMT_S16_LE)
- {
- /* 16 -> 8 */
- /* max_in >> 1, max number of samples in ( 16 bits ) */
- /* max_out, max number of samples out ( 8 bits ) */
- /* len, number of samples that will be taken ( 16 bits )*/
- /* c, count of samples remaining in buffer ( 16 bits )*/
- /* p, count of samples already processed ( 16 bits )*/
- len = ( (max_in >> 1) > max_out) ? max_out : (max_in >> 1);
- c = len;
- p = 0;
- buf8 = (unsigned char *)(localbuf + localoffs);
- while (c)
- {
- locallen = (c >= LBUFCOPYSIZE ? LBUFCOPYSIZE : c);
- /* << 1 in order to get 16 bit samples */
- if (copy_from_user(lbuf16,
- userbuf + useroffs + (p << 1),
- locallen << 1))
- return;
- for (i = 0; i < locallen; i++)
- {
- buf8[p+i] = ~((lbuf16[i] >> 8) & 0xff) ^ 0x80;
- }
- c -= locallen; p += locallen;
- }
- /* used = ( samples * 16 bits size ) */
- *used = max_in > ( max_out << 1) ? (max_out << 1) : max_in;
- /* returned = ( samples * 8 bits size ) */
- *returned = len;
- }
- else
- {
- /* 8 -> 16 */
- /* max_in, max number of samples in ( 8 bits ) */
- /* max_out >> 1, max number of samples out ( 16 bits ) */
- /* len, number of samples that will be taken ( 8 bits )*/
- /* c, count of samples remaining in buffer ( 8 bits )*/
- /* p, count of samples already processed ( 8 bits )*/
- len = max_in > (max_out >> 1) ? (max_out >> 1) : max_in;
- c = len;
- p = 0;
- buf16 = (signed short *)(localbuf + localoffs);
- while (c)
- {
- locallen = (c >= LBUFCOPYSIZE ? LBUFCOPYSIZE : c);
- if (copy_from_user(lbuf8,
- userbuf+useroffs + p,
- locallen))
- return;
- for (i = 0; i < locallen; i++)
- {
- buf16[p+i] = (~lbuf8[i] ^ 0x80) << 8;
- }
- c -= locallen; p += locallen;
- }
- /* used = ( samples * 8 bits size ) */
- *used = len;
- /* returned = ( samples * 16 bits size ) */
- *returned = len << 1;
- }
-}
-
-static void
-sb16_audio_mmap(int dev)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- devc->fullduplex = 0;
-}
-
-static struct audio_driver sb1_audio_driver = /* SB1.x */
-{
- .owner = THIS_MODULE,
- .open = sb_audio_open,
- .close = sb_audio_close,
- .output_block = sb_set_output_parms,
- .start_input = sb_set_input_parms,
- .prepare_for_input = sb1_audio_prepare_for_input,
- .prepare_for_output = sb1_audio_prepare_for_output,
- .halt_io = sb1_audio_halt_xfer,
- .trigger = sb1_audio_trigger,
- .set_speed = sb1_audio_set_speed,
- .set_bits = sb1_audio_set_bits,
- .set_channels = sb1_audio_set_channels
-};
-
-static struct audio_driver sb20_audio_driver = /* SB2.0 */
-{
- .owner = THIS_MODULE,
- .open = sb_audio_open,
- .close = sb_audio_close,
- .output_block = sb_set_output_parms,
- .start_input = sb_set_input_parms,
- .prepare_for_input = sb1_audio_prepare_for_input,
- .prepare_for_output = sb1_audio_prepare_for_output,
- .halt_io = sb1_audio_halt_xfer,
- .trigger = sb20_audio_trigger,
- .set_speed = sb1_audio_set_speed,
- .set_bits = sb1_audio_set_bits,
- .set_channels = sb1_audio_set_channels
-};
-
-static struct audio_driver sb201_audio_driver = /* SB2.01 */
-{
- .owner = THIS_MODULE,
- .open = sb_audio_open,
- .close = sb_audio_close,
- .output_block = sb_set_output_parms,
- .start_input = sb_set_input_parms,
- .prepare_for_input = sb1_audio_prepare_for_input,
- .prepare_for_output = sb1_audio_prepare_for_output,
- .halt_io = sb1_audio_halt_xfer,
- .trigger = sb20_audio_trigger,
- .set_speed = sb201_audio_set_speed,
- .set_bits = sb1_audio_set_bits,
- .set_channels = sb1_audio_set_channels
-};
-
-static struct audio_driver sbpro_audio_driver = /* SB Pro */
-{
- .owner = THIS_MODULE,
- .open = sb_audio_open,
- .close = sb_audio_close,
- .output_block = sb_set_output_parms,
- .start_input = sb_set_input_parms,
- .prepare_for_input = sbpro_audio_prepare_for_input,
- .prepare_for_output = sbpro_audio_prepare_for_output,
- .halt_io = sb1_audio_halt_xfer,
- .trigger = sb20_audio_trigger,
- .set_speed = sbpro_audio_set_speed,
- .set_bits = sb1_audio_set_bits,
- .set_channels = sbpro_audio_set_channels
-};
-
-static struct audio_driver jazz16_audio_driver = /* Jazz16 and SM Wave */
-{
- .owner = THIS_MODULE,
- .open = sb_audio_open,
- .close = sb_audio_close,
- .output_block = sb_set_output_parms,
- .start_input = sb_set_input_parms,
- .prepare_for_input = sbpro_audio_prepare_for_input,
- .prepare_for_output = sbpro_audio_prepare_for_output,
- .halt_io = sb1_audio_halt_xfer,
- .trigger = sb20_audio_trigger,
- .set_speed = jazz16_audio_set_speed,
- .set_bits = sb16_audio_set_bits,
- .set_channels = sbpro_audio_set_channels
-};
-
-static struct audio_driver sb16_audio_driver = /* SB16 */
-{
- .owner = THIS_MODULE,
- .open = sb_audio_open,
- .close = sb_audio_close,
- .output_block = sb_set_output_parms,
- .start_input = sb_set_input_parms,
- .prepare_for_input = sb16_audio_prepare_for_input,
- .prepare_for_output = sb16_audio_prepare_for_output,
- .halt_io = sb1_audio_halt_xfer,
- .copy_user = sb16_copy_from_user,
- .trigger = sb16_audio_trigger,
- .set_speed = sb16_audio_set_speed,
- .set_bits = sb16_audio_set_bits,
- .set_channels = sbpro_audio_set_channels,
- .mmap = sb16_audio_mmap
-};
-
-void sb_audio_init(sb_devc * devc, char *name, struct module *owner)
-{
- int audio_flags = 0;
- int format_mask = AFMT_U8;
-
- struct audio_driver *driver = &sb1_audio_driver;
-
- switch (devc->model)
- {
- case MDL_SB1: /* SB1.0 or SB 1.5 */
- DDB(printk("Will use standard SB1.x driver\n"));
- audio_flags = DMA_HARDSTOP;
- break;
-
- case MDL_SB2:
- DDB(printk("Will use SB2.0 driver\n"));
- audio_flags = DMA_AUTOMODE;
- driver = &sb20_audio_driver;
- break;
-
- case MDL_SB201:
- DDB(printk("Will use SB2.01 (high speed) driver\n"));
- audio_flags = DMA_AUTOMODE;
- driver = &sb201_audio_driver;
- break;
-
- case MDL_JAZZ:
- case MDL_SMW:
- DDB(printk("Will use Jazz16 driver\n"));
- audio_flags = DMA_AUTOMODE;
- format_mask |= AFMT_S16_LE;
- driver = &jazz16_audio_driver;
- break;
-
- case MDL_ESS:
- DDB(printk("Will use ESS ES688/1688 driver\n"));
- driver = ess_audio_init (devc, &audio_flags, &format_mask);
- break;
-
- case MDL_SB16:
- DDB(printk("Will use SB16 driver\n"));
- audio_flags = DMA_AUTOMODE;
- format_mask |= AFMT_S16_LE;
- if (devc->dma8 != devc->dma16 && devc->dma16 != -1)
- {
- audio_flags |= DMA_DUPLEX;
- devc->duplex = 1;
- }
- driver = &sb16_audio_driver;
- break;
-
- default:
- DDB(printk("Will use SB Pro driver\n"));
- audio_flags = DMA_AUTOMODE;
- driver = &sbpro_audio_driver;
- }
-
- if (owner)
- driver->owner = owner;
-
- if ((devc->dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
- name,driver, sizeof(struct audio_driver),
- audio_flags, format_mask, devc,
- devc->dma8,
- devc->duplex ? devc->dma16 : devc->dma8)) < 0)
- {
- printk(KERN_ERR "Sound Blaster: unable to install audio.\n");
- return;
- }
- audio_devs[devc->dev]->mixer_dev = devc->my_mixerdev;
- audio_devs[devc->dev]->min_fragment = 5;
-}
diff --git a/sound/oss/sb_card.c b/sound/oss/sb_card.c
deleted file mode 100644
index 84ef4d06c1c2..000000000000
--- a/sound/oss/sb_card.c
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * sound/oss/sb_card.c
- *
- * Detection routine for the ISA Sound Blaster and compatable sound
- * cards.
- *
- * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this
- * software for more info.
- *
- * This is a complete rewrite of the detection routines. This was
- * prompted by the PnP API change during v2.5 and the ugly state the
- * code was in.
- *
- * Copyright (C) by Paul Laufer 2002. Based on code originally by
- * Hannu Savolainen which was modified by many others over the
- * years. Authors specifically mentioned in the previous version were:
- * Daniel Stone, Alessandro Zummo, Jeff Garzik, Arnaldo Carvalho de
- * Melo, Daniel Church, and myself.
- *
- * 02-05-2003 Original Release, Paul Laufer <paul@laufernet.com>
- * 02-07-2003 Bug made it into first release. Take two.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include "sound_config.h"
-#include "sb_mixer.h"
-#include "sb.h"
-#ifdef CONFIG_PNP
-#include <linux/pnp.h>
-#endif /* CONFIG_PNP */
-#include "sb_card.h"
-
-MODULE_DESCRIPTION("OSS Soundblaster ISA PnP and legacy sound driver");
-MODULE_LICENSE("GPL");
-
-extern void *smw_free;
-
-static int __initdata mpu_io = 0;
-static int __initdata io = -1;
-static int __initdata irq = -1;
-static int __initdata dma = -1;
-static int __initdata dma16 = -1;
-static int __initdata type = 0; /* Can set this to a specific card type */
-static int __initdata esstype = 0; /* ESS chip type */
-static int __initdata acer = 0; /* Do acer notebook init? */
-static int __initdata sm_games = 0; /* Logitech soundman games? */
-
-static struct sb_card_config *legacy = NULL;
-
-#ifdef CONFIG_PNP
-static int pnp_registered;
-static int __initdata pnp = 1;
-/*
-static int __initdata uart401 = 0;
-*/
-#else
-static int __initdata pnp = 0;
-#endif
-
-module_param(io, int, 000);
-MODULE_PARM_DESC(io, "Soundblaster i/o base address (0x220,0x240,0x260,0x280)");
-module_param(irq, int, 000);
-MODULE_PARM_DESC(irq, "IRQ (5,7,9,10)");
-module_param(dma, int, 000);
-MODULE_PARM_DESC(dma, "8-bit DMA channel (0,1,3)");
-module_param(dma16, int, 000);
-MODULE_PARM_DESC(dma16, "16-bit DMA channel (5,6,7)");
-module_param(mpu_io, int, 000);
-MODULE_PARM_DESC(mpu_io, "MPU base address");
-module_param(type, int, 000);
-MODULE_PARM_DESC(type, "You can set this to specific card type (doesn't " \
- "work with pnp)");
-module_param(sm_games, int, 000);
-MODULE_PARM_DESC(sm_games, "Enable support for Logitech soundman games " \
- "(doesn't work with pnp)");
-module_param(esstype, int, 000);
-MODULE_PARM_DESC(esstype, "ESS chip type (doesn't work with pnp)");
-module_param(acer, int, 000);
-MODULE_PARM_DESC(acer, "Set this to detect cards in some ACER notebooks "\
- "(doesn't work with pnp)");
-
-#ifdef CONFIG_PNP
-module_param(pnp, int, 000);
-MODULE_PARM_DESC(pnp, "Went set to 0 will disable detection using PnP. "\
- "Default is 1.\n");
-/* Not done yet.... */
-/*
-module_param(uart401, int, 000);
-MODULE_PARM_DESC(uart401, "When set to 1, will attempt to detect and enable"\
- "the mpu on some clones");
-*/
-#endif /* CONFIG_PNP */
-
-/* OSS subsystem card registration shared by PnP and legacy routines */
-static int sb_register_oss(struct sb_card_config *scc, struct sb_module_options *sbmo)
-{
- if (!request_region(scc->conf.io_base, 16, "soundblaster")) {
- printk(KERN_ERR "sb: ports busy.\n");
- kfree(scc);
- return -EBUSY;
- }
-
- if (!sb_dsp_detect(&scc->conf, 0, 0, sbmo)) {
- release_region(scc->conf.io_base, 16);
- printk(KERN_ERR "sb: Failed DSP Detect.\n");
- kfree(scc);
- return -ENODEV;
- }
- if(!sb_dsp_init(&scc->conf, THIS_MODULE)) {
- printk(KERN_ERR "sb: Failed DSP init.\n");
- kfree(scc);
- return -ENODEV;
- }
- if(scc->mpucnf.io_base > 0) {
- scc->mpu = 1;
- printk(KERN_INFO "sb: Turning on MPU\n");
- if(!probe_sbmpu(&scc->mpucnf, THIS_MODULE))
- scc->mpu = 0;
- }
-
- return 1;
-}
-
-static void sb_unload(struct sb_card_config *scc)
-{
- sb_dsp_unload(&scc->conf, 0);
- if(scc->mpu)
- unload_sbmpu(&scc->mpucnf);
- kfree(scc);
-}
-
-/* Register legacy card with OSS subsystem */
-static int __init sb_init_legacy(void)
-{
- struct sb_module_options sbmo = {0};
-
- if((legacy = kzalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) {
- printk(KERN_ERR "sb: Error: Could not allocate memory\n");
- return -ENOMEM;
- }
-
- legacy->conf.io_base = io;
- legacy->conf.irq = irq;
- legacy->conf.dma = dma;
- legacy->conf.dma2 = dma16;
- legacy->conf.card_subtype = type;
-
- legacy->mpucnf.io_base = mpu_io;
- legacy->mpucnf.irq = -1;
- legacy->mpucnf.dma = -1;
- legacy->mpucnf.dma2 = -1;
-
- sbmo.esstype = esstype;
- sbmo.sm_games = sm_games;
- sbmo.acer = acer;
-
- return sb_register_oss(legacy, &sbmo);
-}
-
-#ifdef CONFIG_PNP
-
-/* Populate the OSS subsystem structures with information from PnP */
-static void sb_dev2cfg(struct pnp_dev *dev, struct sb_card_config *scc)
-{
- scc->conf.io_base = -1;
- scc->conf.irq = -1;
- scc->conf.dma = -1;
- scc->conf.dma2 = -1;
- scc->mpucnf.io_base = -1;
- scc->mpucnf.irq = -1;
- scc->mpucnf.dma = -1;
- scc->mpucnf.dma2 = -1;
-
- /* All clones layout their PnP tables differently and some use
- different logical devices for the MPU */
- if(!strncmp("CTL",scc->card_id,3)) {
- scc->conf.io_base = pnp_port_start(dev,0);
- scc->conf.irq = pnp_irq(dev,0);
- scc->conf.dma = pnp_dma(dev,0);
- scc->conf.dma2 = pnp_dma(dev,1);
- scc->mpucnf.io_base = pnp_port_start(dev,1);
- return;
- }
- if(!strncmp("tBA",scc->card_id,3)) {
- scc->conf.io_base = pnp_port_start(dev,0);
- scc->conf.irq = pnp_irq(dev,0);
- scc->conf.dma = pnp_dma(dev,0);
- scc->conf.dma2 = pnp_dma(dev,1);
- return;
- }
- if(!strncmp("ESS",scc->card_id,3)) {
- scc->conf.io_base = pnp_port_start(dev,0);
- scc->conf.irq = pnp_irq(dev,0);
- scc->conf.dma = pnp_dma(dev,0);
- scc->conf.dma2 = pnp_dma(dev,1);
- scc->mpucnf.io_base = pnp_port_start(dev,2);
- return;
- }
- if(!strncmp("CMI",scc->card_id,3)) {
- scc->conf.io_base = pnp_port_start(dev,0);
- scc->conf.irq = pnp_irq(dev,0);
- scc->conf.dma = pnp_dma(dev,0);
- scc->conf.dma2 = pnp_dma(dev,1);
- return;
- }
- if(!strncmp("RWB",scc->card_id,3)) {
- scc->conf.io_base = pnp_port_start(dev,0);
- scc->conf.irq = pnp_irq(dev,0);
- scc->conf.dma = pnp_dma(dev,0);
- return;
- }
- if(!strncmp("ALS",scc->card_id,3)) {
- if(!strncmp("ALS0007",scc->card_id,7)) {
- scc->conf.io_base = pnp_port_start(dev,0);
- scc->conf.irq = pnp_irq(dev,0);
- scc->conf.dma = pnp_dma(dev,0);
- } else {
- scc->conf.io_base = pnp_port_start(dev,0);
- scc->conf.irq = pnp_irq(dev,0);
- scc->conf.dma = pnp_dma(dev,1);
- scc->conf.dma2 = pnp_dma(dev,0);
- }
- return;
- }
- if(!strncmp("RTL",scc->card_id,3)) {
- scc->conf.io_base = pnp_port_start(dev,0);
- scc->conf.irq = pnp_irq(dev,0);
- scc->conf.dma = pnp_dma(dev,1);
- scc->conf.dma2 = pnp_dma(dev,0);
- }
-}
-
-static unsigned int sb_pnp_devices;
-
-/* Probe callback function for the PnP API */
-static int sb_pnp_probe(struct pnp_card_link *card, const struct pnp_card_device_id *card_id)
-{
- struct sb_card_config *scc;
- struct sb_module_options sbmo = {0}; /* Default to 0 for PnP */
- struct pnp_dev *dev = pnp_request_card_device(card, card_id->devs[0].id, NULL);
-
- if(!dev){
- return -EBUSY;
- }
-
- if((scc = kzalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) {
- printk(KERN_ERR "sb: Error: Could not allocate memory\n");
- return -ENOMEM;
- }
-
- printk(KERN_INFO "sb: PnP: Found Card Named = \"%s\", Card PnP id = " \
- "%s, Device PnP id = %s\n", card->card->name, card_id->id,
- dev->id->id);
-
- scc->card_id = card_id->id;
- scc->dev_id = dev->id->id;
- sb_dev2cfg(dev, scc);
-
- printk(KERN_INFO "sb: PnP: Detected at: io=0x%x, irq=%d, " \
- "dma=%d, dma16=%d\n", scc->conf.io_base, scc->conf.irq,
- scc->conf.dma, scc->conf.dma2);
-
- pnp_set_card_drvdata(card, scc);
- sb_pnp_devices++;
-
- return sb_register_oss(scc, &sbmo);
-}
-
-static void sb_pnp_remove(struct pnp_card_link *card)
-{
- struct sb_card_config *scc = pnp_get_card_drvdata(card);
-
- if(!scc)
- return;
-
- printk(KERN_INFO "sb: PnP: Removing %s\n", scc->card_id);
-
- sb_unload(scc);
-}
-
-static struct pnp_card_driver sb_pnp_driver = {
- .name = "OSS SndBlstr", /* 16 character limit */
- .id_table = sb_pnp_card_table,
- .probe = sb_pnp_probe,
- .remove = sb_pnp_remove,
-};
-MODULE_DEVICE_TABLE(pnp_card, sb_pnp_card_table);
-#endif /* CONFIG_PNP */
-
-static void sb_unregister_all(void)
-{
-#ifdef CONFIG_PNP
- if (pnp_registered)
- pnp_unregister_card_driver(&sb_pnp_driver);
-#endif
-}
-
-static int __init sb_init(void)
-{
- int lres = 0;
- int pres = 0;
-
- printk(KERN_INFO "sb: Init: Starting Probe...\n");
-
- if(io != -1 && irq != -1 && dma != -1) {
- printk(KERN_INFO "sb: Probing legacy card with io=%x, "\
- "irq=%d, dma=%d, dma16=%d\n",io, irq, dma, dma16);
- lres = sb_init_legacy();
- } else if((io != -1 || irq != -1 || dma != -1) ||
- (!pnp && (io == -1 && irq == -1 && dma == -1)))
- printk(KERN_ERR "sb: Error: At least io, irq, and dma "\
- "must be set for legacy cards.\n");
-
-#ifdef CONFIG_PNP
- if(pnp) {
- int err = pnp_register_card_driver(&sb_pnp_driver);
- if (!err)
- pnp_registered = 1;
- pres = sb_pnp_devices;
- }
-#endif
- printk(KERN_INFO "sb: Init: Done\n");
-
- /* If either PnP or Legacy registered a card then return
- * success */
- if (pres == 0 && lres <= 0) {
- sb_unregister_all();
- return -ENODEV;
- }
- return 0;
-}
-
-static void __exit sb_exit(void)
-{
- printk(KERN_INFO "sb: Unloading...\n");
-
- /* Unload legacy card */
- if (legacy) {
- printk (KERN_INFO "sb: Unloading legacy card\n");
- sb_unload(legacy);
- }
-
- sb_unregister_all();
-
- vfree(smw_free);
- smw_free = NULL;
-}
-
-module_init(sb_init);
-module_exit(sb_exit);
diff --git a/sound/oss/sb_card.h b/sound/oss/sb_card.h
deleted file mode 100644
index 5535cff800df..000000000000
--- a/sound/oss/sb_card.h
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * sound/oss/sb_card.h
- *
- * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this
- * software for more info.
- *
- * 02-05-2002 Original Release, Paul Laufer <paul@laufernet.com>
- */
-
-struct sb_card_config {
- struct address_info conf;
- struct address_info mpucnf;
- const char *card_id;
- const char *dev_id;
- int mpu;
-};
-
-#ifdef CONFIG_PNP
-
-/*
- * SoundBlaster PnP tables and structures.
- */
-
-/* Card PnP ID Table */
-static struct pnp_card_device_id sb_pnp_card_table[] = {
- /* Sound Blaster 16 */
- {.id = "CTL0024", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL0025", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL0026", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL0027", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL0028", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL0029", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL002a", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL002b", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL002c", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL00ed", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
- /* Sound Blaster 16 */
- {.id = "CTL0086", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
- /* Sound Blaster Vibra16S */
- {.id = "CTL0051", .driver_data = 0, .devs = { {.id="CTL0001"}, } },
- /* Sound Blaster Vibra16C */
- {.id = "CTL0070", .driver_data = 0, .devs = { {.id="CTL0001"}, } },
- /* Sound Blaster Vibra16CL */
- {.id = "CTL0080", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
- /* Sound Blaster Vibra16CL */
- {.id = "CTL00F0", .driver_data = 0, .devs = { {.id="CTL0043"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL0039", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL0042", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL0043", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL0044", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL0045", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL0046", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL0047", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL0048", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL0054", .driver_data = 0, .devs = { {.id="CTL0031"}, } },
- /* Sound Blaster AWE 32 */
- {.id = "CTL009C", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
- /* Createive SB32 PnP */
- {.id = "CTL009F", .driver_data = 0, .devs = { {.id="CTL0041"}, } },
- /* Sound Blaster AWE 64 */
- {.id = "CTL009D", .driver_data = 0, .devs = { {.id="CTL0042"}, } },
- /* Sound Blaster AWE 64 Gold */
- {.id = "CTL009E", .driver_data = 0, .devs = { {.id="CTL0044"}, } },
- /* Sound Blaster AWE 64 Gold */
- {.id = "CTL00B2", .driver_data = 0, .devs = { {.id="CTL0044"}, } },
- /* Sound Blaster AWE 64 */
- {.id = "CTL00C1", .driver_data = 0, .devs = { {.id="CTL0042"}, } },
- /* Sound Blaster AWE 64 */
- {.id = "CTL00C3", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
- /* Sound Blaster AWE 64 */
- {.id = "CTL00C5", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
- /* Sound Blaster AWE 64 */
- {.id = "CTL00C7", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
- /* Sound Blaster AWE 64 */
- {.id = "CTL00E4", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
- /* Sound Blaster AWE 64 */
- {.id = "CTL00E9", .driver_data = 0, .devs = { {.id="CTL0045"}, } },
- /* ESS 1868 */
- {.id = "ESS0968", .driver_data = 0, .devs = { {.id="ESS0968"}, } },
- /* ESS 1868 */
- {.id = "ESS1868", .driver_data = 0, .devs = { {.id="ESS1868"}, } },
- /* ESS 1868 */
- {.id = "ESS1868", .driver_data = 0, .devs = { {.id="ESS8611"}, } },
- /* ESS 1869 PnP AudioDrive */
- {.id = "ESS0003", .driver_data = 0, .devs = { {.id="ESS1869"}, } },
- /* ESS 1869 */
- {.id = "ESS1869", .driver_data = 0, .devs = { {.id="ESS1869"}, } },
- /* ESS 1878 */
- {.id = "ESS1878", .driver_data = 0, .devs = { {.id="ESS1878"}, } },
- /* ESS 1879 */
- {.id = "ESS1879", .driver_data = 0, .devs = { {.id="ESS1879"}, } },
- /* CMI 8330 SoundPRO */
- {.id = "CMI0001", .driver_data = 0, .devs = { {.id="@X@0001"},
- {.id="@H@0001"},
- {.id="@@@0001"}, } },
- /* Diamond DT0197H */
- {.id = "RWR1688", .driver_data = 0, .devs = { {.id="@@@0001"},
- {.id="@X@0001"},
- {.id="@H@0001"}, } },
- /* ALS007 */
- {.id = "ALS0007", .driver_data = 0, .devs = { {.id="@@@0001"},
- {.id="@X@0001"},
- {.id="@H@0001"}, } },
- /* ALS100 */
- {.id = "ALS0001", .driver_data = 0, .devs = { {.id="@@@0001"},
- {.id="@X@0001"},
- {.id="@H@0001"}, } },
- /* ALS110 */
- {.id = "ALS0110", .driver_data = 0, .devs = { {.id="@@@1001"},
- {.id="@X@1001"},
- {.id="@H@0001"}, } },
- /* ALS120 */
- {.id = "ALS0120", .driver_data = 0, .devs = { {.id="@@@2001"},
- {.id="@X@2001"},
- {.id="@H@0001"}, } },
- /* ALS200 */
- {.id = "ALS0200", .driver_data = 0, .devs = { {.id="@@@0020"},
- {.id="@X@0030"},
- {.id="@H@0001"}, } },
- /* ALS200 */
- {.id = "RTL3000", .driver_data = 0, .devs = { {.id="@@@2001"},
- {.id="@X@2001"},
- {.id="@H@0001"}, } },
- /* Sound Blaster 16 (Virtual PC 2004) */
- {.id = "tBA03b0", .driver_data = 0, .devs = { {.id="PNPb003"}, } },
- /* -end- */
- {.id = "", }
-};
-
-#endif
diff --git a/sound/oss/sb_common.c b/sound/oss/sb_common.c
deleted file mode 100644
index 7d42c5418d1b..000000000000
--- a/sound/oss/sb_common.c
+++ /dev/null
@@ -1,1292 +0,0 @@
-/*
- * sound/oss/sb_common.c
- *
- * Common routines for Sound Blaster compatible cards.
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Daniel J. Rodriksson: Modified sbintr to handle 8 and 16 bit interrupts
- * for full duplex support ( only sb16 by now )
- * Rolf Fokkens: Added (BETA?) support for ES1887 chips.
- * (fokkensr@vertis.nl) Which means: You can adjust the recording levels.
- *
- * 2000/01/18 - separated sb_card and sb_common -
- * Jeff Garzik <jgarzik@pobox.com>
- *
- * 2000/09/18 - got rid of attach_uart401
- * Arnaldo Carvalho de Melo <acme@conectiva.com.br>
- *
- * 2001/01/26 - replaced CLI/STI with spinlocks
- * Chris Rankin <rankinc@zipworld.com.au>
- */
-
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-
-#include "sound_config.h"
-#include "sound_firmware.h"
-
-#include "mpu401.h"
-
-#include "sb_mixer.h"
-#include "sb.h"
-#include "sb_ess.h"
-
-/*
- * global module flag
- */
-
-int sb_be_quiet;
-
-static sb_devc *detected_devc; /* For communication from probe to init */
-static sb_devc *last_devc; /* For MPU401 initialization */
-
-static unsigned char jazz_irq_bits[] = {
- 0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6
-};
-
-static unsigned char jazz_dma_bits[] = {
- 0, 1, 0, 2, 0, 3, 0, 4
-};
-
-void *smw_free;
-
-/*
- * Jazz16 chipset specific control variables
- */
-
-static int jazz16_base; /* Not detected */
-static unsigned char jazz16_bits; /* I/O relocation bits */
-static DEFINE_SPINLOCK(jazz16_lock);
-
-/*
- * Logitech Soundman Wave specific initialization code
- */
-
-#ifdef SMW_MIDI0001_INCLUDED
-#include "smw-midi0001.h"
-#else
-static unsigned char *smw_ucode;
-static int smw_ucodeLen;
-
-#endif
-
-static sb_devc *last_sb; /* Last sb loaded */
-
-int sb_dsp_command(sb_devc * devc, unsigned char val)
-{
- int i;
- unsigned long limit;
-
- limit = jiffies + HZ / 10; /* Timeout */
-
- /*
- * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes
- * called while interrupts are disabled. This means that the timer is
- * disabled also. However the timeout situation is a abnormal condition.
- * Normally the DSP should be ready to accept commands after just couple of
- * loops.
- */
-
- for (i = 0; i < 500000 && (limit-jiffies)>0; i++)
- {
- if ((inb(DSP_STATUS) & 0x80) == 0)
- {
- outb((val), DSP_COMMAND);
- return 1;
- }
- }
- printk(KERN_WARNING "Sound Blaster: DSP command(%x) timeout.\n", val);
- return 0;
-}
-
-int sb_dsp_get_byte(sb_devc * devc)
-{
- int i;
-
- for (i = 1000; i; i--)
- {
- if (inb(DSP_DATA_AVAIL) & 0x80)
- return inb(DSP_READ);
- }
- return 0xffff;
-}
-
-static void sb_intr (sb_devc *devc)
-{
- int status;
- unsigned char src = 0xff;
-
- if (devc->model == MDL_SB16)
- {
- src = sb_getmixer(devc, IRQ_STAT); /* Interrupt source register */
-
- if (src & 4) /* MPU401 interrupt */
- if(devc->midi_irq_cookie)
- uart401intr(devc->irq, devc->midi_irq_cookie);
-
- if (!(src & 3))
- return; /* Not a DSP interrupt */
- }
- if (devc->intr_active && (!devc->fullduplex || (src & 0x01)))
- {
- switch (devc->irq_mode)
- {
- case IMODE_OUTPUT:
- DMAbuf_outputintr(devc->dev, 1);
- break;
-
- case IMODE_INPUT:
- DMAbuf_inputintr(devc->dev);
- break;
-
- case IMODE_INIT:
- break;
-
- case IMODE_MIDI:
- sb_midi_interrupt(devc);
- break;
-
- default:
- /* printk(KERN_WARNING "Sound Blaster: Unexpected interrupt\n"); */
- ;
- }
- }
- else if (devc->intr_active_16 && (src & 0x02))
- {
- switch (devc->irq_mode_16)
- {
- case IMODE_OUTPUT:
- DMAbuf_outputintr(devc->dev, 1);
- break;
-
- case IMODE_INPUT:
- DMAbuf_inputintr(devc->dev);
- break;
-
- case IMODE_INIT:
- break;
-
- default:
- /* printk(KERN_WARNING "Sound Blaster: Unexpected interrupt\n"); */
- ;
- }
- }
- /*
- * Acknowledge interrupts
- */
-
- if (src & 0x01)
- status = inb(DSP_DATA_AVAIL);
-
- if (devc->model == MDL_SB16 && src & 0x02)
- status = inb(DSP_DATA_AVL16);
-}
-
-static void pci_intr(sb_devc *devc)
-{
- int src = inb(devc->pcibase+0x1A);
- src&=3;
- if(src)
- sb_intr(devc);
-}
-
-static irqreturn_t sbintr(int irq, void *dev_id)
-{
- sb_devc *devc = dev_id;
-
- devc->irq_ok = 1;
-
- switch (devc->model) {
- case MDL_ESSPCI:
- pci_intr (devc);
- break;
-
- case MDL_ESS:
- ess_intr (devc);
- break;
- default:
- sb_intr (devc);
- break;
- }
- return IRQ_HANDLED;
-}
-
-int sb_dsp_reset(sb_devc * devc)
-{
- int loopc;
-
- DEB(printk("Entered sb_dsp_reset()\n"));
-
- if (devc->model == MDL_ESS) return ess_dsp_reset (devc);
-
- /* This is only for non-ESS chips */
-
- outb(1, DSP_RESET);
-
- udelay(10);
- outb(0, DSP_RESET);
- udelay(30);
-
- for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++);
-
- if (inb(DSP_READ) != 0xAA)
- {
- DDB(printk("sb: No response to RESET\n"));
- return 0; /* Sorry */
- }
-
- DEB(printk("sb_dsp_reset() OK\n"));
-
- return 1;
-}
-
-static void dsp_get_vers(sb_devc * devc)
-{
- int i;
-
- unsigned long flags;
-
- DDB(printk("Entered dsp_get_vers()\n"));
- spin_lock_irqsave(&devc->lock, flags);
- devc->major = devc->minor = 0;
- sb_dsp_command(devc, 0xe1); /* Get version */
-
- for (i = 100000; i; i--)
- {
- if (inb(DSP_DATA_AVAIL) & 0x80)
- {
- if (devc->major == 0)
- devc->major = inb(DSP_READ);
- else
- {
- devc->minor = inb(DSP_READ);
- break;
- }
- }
- }
- spin_unlock_irqrestore(&devc->lock, flags);
- DDB(printk("DSP version %d.%02d\n", devc->major, devc->minor));
-}
-
-static int sb16_set_dma_hw(sb_devc * devc)
-{
- int bits;
-
- if (devc->dma8 != 0 && devc->dma8 != 1 && devc->dma8 != 3)
- {
- printk(KERN_ERR "SB16: Invalid 8 bit DMA (%d)\n", devc->dma8);
- return 0;
- }
- bits = (1 << devc->dma8);
-
- if (devc->dma16 >= 5 && devc->dma16 <= 7)
- bits |= (1 << devc->dma16);
-
- sb_setmixer(devc, DMA_NR, bits);
- return 1;
-}
-
-static void sb16_set_mpu_port(sb_devc * devc, struct address_info *hw_config)
-{
- /*
- * This routine initializes new MIDI port setup register of SB Vibra (CT2502).
- */
- unsigned char bits = sb_getmixer(devc, 0x84) & ~0x06;
-
- switch (hw_config->io_base)
- {
- case 0x300:
- sb_setmixer(devc, 0x84, bits | 0x04);
- break;
-
- case 0x330:
- sb_setmixer(devc, 0x84, bits | 0x00);
- break;
-
- default:
- sb_setmixer(devc, 0x84, bits | 0x02); /* Disable MPU */
- printk(KERN_ERR "SB16: Invalid MIDI I/O port %x\n", hw_config->io_base);
- }
-}
-
-static int sb16_set_irq_hw(sb_devc * devc, int level)
-{
- int ival;
-
- switch (level)
- {
- case 5:
- ival = 2;
- break;
- case 7:
- ival = 4;
- break;
- case 9:
- ival = 1;
- break;
- case 10:
- ival = 8;
- break;
- default:
- printk(KERN_ERR "SB16: Invalid IRQ%d\n", level);
- return 0;
- }
- sb_setmixer(devc, IRQ_NR, ival);
- return 1;
-}
-
-static void relocate_Jazz16(sb_devc * devc, struct address_info *hw_config)
-{
- unsigned char bits = 0;
- unsigned long flags;
-
- if (jazz16_base != 0 && jazz16_base != hw_config->io_base)
- return;
-
- switch (hw_config->io_base)
- {
- case 0x220:
- bits = 1;
- break;
- case 0x240:
- bits = 2;
- break;
- case 0x260:
- bits = 3;
- break;
- default:
- return;
- }
- bits = jazz16_bits = bits << 5;
- jazz16_base = hw_config->io_base;
-
- /*
- * Magic wake up sequence by writing to 0x201 (aka Joystick port)
- */
- spin_lock_irqsave(&jazz16_lock, flags);
- outb((0xAF), 0x201);
- outb((0x50), 0x201);
- outb((bits), 0x201);
- spin_unlock_irqrestore(&jazz16_lock, flags);
-}
-
-static int init_Jazz16(sb_devc * devc, struct address_info *hw_config)
-{
- char name[100];
- /*
- * First try to check that the card has Jazz16 chip. It identifies itself
- * by returning 0x12 as response to DSP command 0xfa.
- */
-
- if (!sb_dsp_command(devc, 0xfa))
- return 0;
-
- if (sb_dsp_get_byte(devc) != 0x12)
- return 0;
-
- /*
- * OK so far. Now configure the IRQ and DMA channel used by the card.
- */
- if (hw_config->irq < 1 || hw_config->irq > 15 || jazz_irq_bits[hw_config->irq] == 0)
- {
- printk(KERN_ERR "Jazz16: Invalid interrupt (IRQ%d)\n", hw_config->irq);
- return 0;
- }
- if (hw_config->dma < 0 || hw_config->dma > 3 || jazz_dma_bits[hw_config->dma] == 0)
- {
- printk(KERN_ERR "Jazz16: Invalid 8 bit DMA (DMA%d)\n", hw_config->dma);
- return 0;
- }
- if (hw_config->dma2 < 0)
- {
- printk(KERN_ERR "Jazz16: No 16 bit DMA channel defined\n");
- return 0;
- }
- if (hw_config->dma2 < 5 || hw_config->dma2 > 7 || jazz_dma_bits[hw_config->dma2] == 0)
- {
- printk(KERN_ERR "Jazz16: Invalid 16 bit DMA (DMA%d)\n", hw_config->dma2);
- return 0;
- }
- devc->dma16 = hw_config->dma2;
-
- if (!sb_dsp_command(devc, 0xfb))
- return 0;
-
- if (!sb_dsp_command(devc, jazz_dma_bits[hw_config->dma] |
- (jazz_dma_bits[hw_config->dma2] << 4)))
- return 0;
-
- if (!sb_dsp_command(devc, jazz_irq_bits[hw_config->irq]))
- return 0;
-
- /*
- * Now we have configured a standard Jazz16 device.
- */
- devc->model = MDL_JAZZ;
- strcpy(name, "Jazz16");
-
- hw_config->name = "Jazz16";
- devc->caps |= SB_NO_MIDI;
- return 1;
-}
-
-static void relocate_ess1688(sb_devc * devc)
-{
- unsigned char bits;
-
- switch (devc->base)
- {
- case 0x220:
- bits = 0x04;
- break;
- case 0x230:
- bits = 0x05;
- break;
- case 0x240:
- bits = 0x06;
- break;
- case 0x250:
- bits = 0x07;
- break;
- default:
- return; /* Wrong port */
- }
-
- DDB(printk("Doing ESS1688 address selection\n"));
-
- /*
- * ES1688 supports two alternative ways for software address config.
- * First try the so called Read-Sequence-Key method.
- */
-
- /* Reset the sequence logic */
- inb(0x229);
- inb(0x229);
- inb(0x229);
-
- /* Perform the read sequence */
- inb(0x22b);
- inb(0x229);
- inb(0x22b);
- inb(0x229);
- inb(0x229);
- inb(0x22b);
- inb(0x229);
-
- /* Select the base address by reading from it. Then probe using the port. */
- inb(devc->base);
- if (sb_dsp_reset(devc)) /* Bingo */
- return;
-
-#if 0 /* This causes system lockups (Nokia 386/25 at least) */
- /*
- * The last resort is the system control register method.
- */
-
- outb((0x00), 0xfb); /* 0xFB is the unlock register */
- outb((0x00), 0xe0); /* Select index 0 */
- outb((bits), 0xe1); /* Write the config bits */
- outb((0x00), 0xf9); /* 0xFB is the lock register */
-#endif
-}
-
-int sb_dsp_detect(struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo)
-{
- sb_devc sb_info;
- sb_devc *devc = &sb_info;
-
- memset((char *) &sb_info, 0, sizeof(sb_info)); /* Zero everything */
-
- /* Copy module options in place */
- if(sbmo) memcpy(&devc->sbmo, sbmo, sizeof(struct sb_module_options));
-
- sb_info.my_mididev = -1;
- sb_info.my_mixerdev = -1;
- sb_info.dev = -1;
-
- /*
- * Initialize variables
- */
-
- DDB(printk("sb_dsp_detect(%x) entered\n", hw_config->io_base));
-
- spin_lock_init(&devc->lock);
- devc->type = hw_config->card_subtype;
-
- devc->base = hw_config->io_base;
- devc->irq = hw_config->irq;
- devc->dma8 = hw_config->dma;
-
- devc->dma16 = -1;
- devc->pcibase = pciio;
-
- if(pci == SB_PCI_ESSMAESTRO)
- {
- devc->model = MDL_ESSPCI;
- devc->caps |= SB_PCI_IRQ;
- hw_config->driver_use_1 |= SB_PCI_IRQ;
- hw_config->card_subtype = MDL_ESSPCI;
- }
-
- if(pci == SB_PCI_YAMAHA)
- {
- devc->model = MDL_YMPCI;
- devc->caps |= SB_PCI_IRQ;
- hw_config->driver_use_1 |= SB_PCI_IRQ;
- hw_config->card_subtype = MDL_YMPCI;
-
- printk("Yamaha PCI mode.\n");
- }
-
- if (devc->sbmo.acer)
- {
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock, flags);
- inb(devc->base + 0x09);
- inb(devc->base + 0x09);
- inb(devc->base + 0x09);
- inb(devc->base + 0x0b);
- inb(devc->base + 0x09);
- inb(devc->base + 0x0b);
- inb(devc->base + 0x09);
- inb(devc->base + 0x09);
- inb(devc->base + 0x0b);
- inb(devc->base + 0x09);
- inb(devc->base + 0x00);
- spin_unlock_irqrestore(&devc->lock, flags);
- }
- /*
- * Detect the device
- */
-
- if (sb_dsp_reset(devc))
- dsp_get_vers(devc);
- else
- devc->major = 0;
-
- if (devc->type == 0 || devc->type == MDL_JAZZ || devc->type == MDL_SMW)
- if (devc->major == 0 || (devc->major == 3 && devc->minor == 1))
- relocate_Jazz16(devc, hw_config);
-
- if (devc->major == 0 && (devc->type == MDL_ESS || devc->type == 0))
- relocate_ess1688(devc);
-
- if (!sb_dsp_reset(devc))
- {
- DDB(printk("SB reset failed\n"));
-#ifdef MODULE
- printk(KERN_INFO "sb: dsp reset failed.\n");
-#endif
- return 0;
- }
- if (devc->major == 0)
- dsp_get_vers(devc);
-
- if (devc->major == 3 && devc->minor == 1)
- {
- if (devc->type == MDL_AZTECH) /* SG Washington? */
- {
- if (sb_dsp_command(devc, 0x09))
- if (sb_dsp_command(devc, 0x00)) /* Enter WSS mode */
- {
- int i;
-
- /* Have some delay */
- for (i = 0; i < 10000; i++)
- inb(DSP_DATA_AVAIL);
- devc->caps = SB_NO_AUDIO | SB_NO_MIDI; /* Mixer only */
- devc->model = MDL_AZTECH;
- }
- }
- }
-
- if(devc->type == MDL_ESSPCI)
- devc->model = MDL_ESSPCI;
-
- if(devc->type == MDL_YMPCI)
- {
- printk("YMPCI selected\n");
- devc->model = MDL_YMPCI;
- }
-
- /*
- * Save device information for sb_dsp_init()
- */
-
-
- detected_devc = kmalloc(sizeof(sb_devc), GFP_KERNEL);
- if (detected_devc == NULL)
- {
- printk(KERN_ERR "sb: Can't allocate memory for device information\n");
- return 0;
- }
- memcpy(detected_devc, devc, sizeof(sb_devc));
- MDB(printk(KERN_INFO "SB %d.%02d detected OK (%x)\n", devc->major, devc->minor, hw_config->io_base));
- return 1;
-}
-
-int sb_dsp_init(struct address_info *hw_config, struct module *owner)
-{
- sb_devc *devc;
- char name[100];
- extern int sb_be_quiet;
- int mixer22, mixer30;
-
-/*
- * Check if we had detected a SB device earlier
- */
- DDB(printk("sb_dsp_init(%x) entered\n", hw_config->io_base));
- name[0] = 0;
-
- if (detected_devc == NULL)
- {
- MDB(printk("No detected device\n"));
- return 0;
- }
- devc = detected_devc;
- detected_devc = NULL;
-
- if (devc->base != hw_config->io_base)
- {
- DDB(printk("I/O port mismatch\n"));
- release_region(devc->base, 16);
- return 0;
- }
- /*
- * Now continue initialization of the device
- */
-
- devc->caps = hw_config->driver_use_1;
-
- if (!((devc->caps & SB_NO_AUDIO) && (devc->caps & SB_NO_MIDI)) && hw_config->irq > 0)
- { /* IRQ setup */
-
- /*
- * ESS PCI cards do shared PCI IRQ stuff. Since they
- * will get shared PCI irq lines we must cope.
- */
-
- int i=(devc->caps&SB_PCI_IRQ)?IRQF_SHARED:0;
-
- if (request_irq(hw_config->irq, sbintr, i, "soundblaster", devc) < 0)
- {
- printk(KERN_ERR "SB: Can't allocate IRQ%d\n", hw_config->irq);
- release_region(devc->base, 16);
- return 0;
- }
- devc->irq_ok = 0;
-
- if (devc->major == 4)
- if (!sb16_set_irq_hw(devc, devc->irq)) /* Unsupported IRQ */
- {
- free_irq(devc->irq, devc);
- release_region(devc->base, 16);
- return 0;
- }
- if ((devc->type == 0 || devc->type == MDL_ESS) &&
- devc->major == 3 && devc->minor == 1)
- { /* Handle various chipsets which claim they are SB Pro compatible */
- if ((devc->type != 0 && devc->type != MDL_ESS) ||
- !ess_init(devc, hw_config))
- {
- if ((devc->type != 0 && devc->type != MDL_JAZZ &&
- devc->type != MDL_SMW) || !init_Jazz16(devc, hw_config))
- {
- DDB(printk("This is a genuine SB Pro\n"));
- }
- }
- }
- if (devc->major == 4 && devc->minor <= 11 ) /* Won't work */
- devc->irq_ok = 1;
- else
- {
- int n;
-
- for (n = 0; n < 3 && devc->irq_ok == 0; n++)
- {
- if (sb_dsp_command(devc, 0xf2)) /* Cause interrupt immediately */
- {
- int i;
-
- for (i = 0; !devc->irq_ok && i < 10000; i++);
- }
- }
- if (!devc->irq_ok)
- printk(KERN_WARNING "sb: Interrupt test on IRQ%d failed - Probable IRQ conflict\n", devc->irq);
- else
- {
- DDB(printk("IRQ test OK (IRQ%d)\n", devc->irq));
- }
- }
- } /* IRQ setup */
-
- last_sb = devc;
-
- switch (devc->major)
- {
- case 1: /* SB 1.0 or 1.5 */
- devc->model = hw_config->card_subtype = MDL_SB1;
- break;
-
- case 2: /* SB 2.x */
- if (devc->minor == 0)
- devc->model = hw_config->card_subtype = MDL_SB2;
- else
- devc->model = hw_config->card_subtype = MDL_SB201;
- break;
-
- case 3: /* SB Pro and most clones */
- switch (devc->model) {
- case 0:
- devc->model = hw_config->card_subtype = MDL_SBPRO;
- if (hw_config->name == NULL)
- hw_config->name = "Sound Blaster Pro (8 BIT ONLY)";
- break;
- case MDL_ESS:
- ess_dsp_init(devc, hw_config);
- break;
- }
- break;
-
- case 4:
- devc->model = hw_config->card_subtype = MDL_SB16;
- /*
- * ALS007 and ALS100 return DSP version 4.2 and have 2 post-reset !=0
- * registers at 0x3c and 0x4c (output ctrl registers on ALS007) whereas
- * a "standard" SB16 doesn't have a register at 0x4c. ALS100 actively
- * updates register 0x22 whenever 0x30 changes, as per the SB16 spec.
- * Since ALS007 doesn't, this can be used to differentiate the 2 cards.
- */
- if ((devc->minor == 2) && sb_getmixer(devc,0x3c) && sb_getmixer(devc,0x4c))
- {
- mixer30 = sb_getmixer(devc,0x30);
- sb_setmixer(devc,0x22,(mixer22=sb_getmixer(devc,0x22)) & 0x0f);
- sb_setmixer(devc,0x30,0xff);
- /* ALS100 will force 0x30 to 0xf8 like SB16; ALS007 will allow 0xff. */
- /* Register 0x22 & 0xf0 on ALS100 == 0xf0; on ALS007 it == 0x10. */
- if ((sb_getmixer(devc,0x30) != 0xff) || ((sb_getmixer(devc,0x22) & 0xf0) != 0x10))
- {
- devc->submodel = SUBMDL_ALS100;
- if (hw_config->name == NULL)
- hw_config->name = "Sound Blaster 16 (ALS-100)";
- }
- else
- {
- sb_setmixer(devc,0x3c,0x1f); /* Enable all inputs */
- sb_setmixer(devc,0x4c,0x1f);
- sb_setmixer(devc,0x22,mixer22); /* Restore 0x22 to original value */
- devc->submodel = SUBMDL_ALS007;
- if (hw_config->name == NULL)
- hw_config->name = "Sound Blaster 16 (ALS-007)";
- }
- sb_setmixer(devc,0x30,mixer30);
- }
- else if (hw_config->name == NULL)
- hw_config->name = "Sound Blaster 16";
-
- if (hw_config->dma2 == -1)
- devc->dma16 = devc->dma8;
- else if (hw_config->dma2 < 5 || hw_config->dma2 > 7)
- {
- printk(KERN_WARNING "SB16: Bad or missing 16 bit DMA channel\n");
- devc->dma16 = devc->dma8;
- }
- else
- devc->dma16 = hw_config->dma2;
-
- if(!sb16_set_dma_hw(devc)) {
- free_irq(devc->irq, devc);
- release_region(hw_config->io_base, 16);
- return 0;
- }
-
- devc->caps |= SB_NO_MIDI;
- }
-
- if (!(devc->caps & SB_NO_MIXER))
- if (devc->major == 3 || devc->major == 4)
- sb_mixer_init(devc, owner);
-
- if (!(devc->caps & SB_NO_MIDI))
- sb_dsp_midi_init(devc, owner);
-
- if (hw_config->name == NULL)
- hw_config->name = "Sound Blaster (8 BIT/MONO ONLY)";
-
- sprintf(name, "%s (%d.%02d)", hw_config->name, devc->major, devc->minor);
- conf_printf(name, hw_config);
-
- /*
- * Assuming that a sound card is Sound Blaster (compatible) is the most common
- * configuration error and the mother of all problems. Usually sound cards
- * emulate SB Pro but in addition they have a 16 bit native mode which should be
- * used in Unix. See Readme.cards for more information about configuring OSS/Free
- * properly.
- */
- if (devc->model <= MDL_SBPRO)
- {
- if (devc->major == 3 && devc->minor != 1) /* "True" SB Pro should have v3.1 (rare ones may have 3.2). */
- {
- printk(KERN_INFO "This sound card may not be fully Sound Blaster Pro compatible.\n");
- printk(KERN_INFO "In many cases there is another way to configure OSS so that\n");
- printk(KERN_INFO "it works properly with OSS (for example in 16 bit mode).\n");
- printk(KERN_INFO "Please ignore this message if you _really_ have a SB Pro.\n");
- }
- else if (!sb_be_quiet && devc->model == MDL_SBPRO)
- {
- printk(KERN_INFO "SB DSP version is just %d.%02d which means that your card is\n", devc->major, devc->minor);
- printk(KERN_INFO "several years old (8 bit only device) or alternatively the sound driver\n");
- printk(KERN_INFO "is incorrectly configured.\n");
- }
- }
- hw_config->card_subtype = devc->model;
- hw_config->slots[0]=devc->dev;
- last_devc = devc; /* For SB MPU detection */
-
- if (!(devc->caps & SB_NO_AUDIO) && devc->dma8 >= 0)
- {
- if (sound_alloc_dma(devc->dma8, "SoundBlaster8"))
- {
- printk(KERN_WARNING "Sound Blaster: Can't allocate 8 bit DMA channel %d\n", devc->dma8);
- }
- if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
- {
- if (sound_alloc_dma(devc->dma16, "SoundBlaster16"))
- printk(KERN_WARNING "Sound Blaster: can't allocate 16 bit DMA channel %d.\n", devc->dma16);
- }
- sb_audio_init(devc, name, owner);
- hw_config->slots[0]=devc->dev;
- }
- else
- {
- MDB(printk("Sound Blaster: no audio devices found.\n"));
- }
- return 1;
-}
-
-/* if (sbmpu) below we allow mpu401 to manage the midi devs
- otherwise we have to unload them. (Andrzej Krzysztofowicz) */
-
-void sb_dsp_unload(struct address_info *hw_config, int sbmpu)
-{
- sb_devc *devc;
-
- devc = audio_devs[hw_config->slots[0]]->devc;
-
- if (devc && devc->base == hw_config->io_base)
- {
- if ((devc->model & MDL_ESS) && devc->pcibase)
- release_region(devc->pcibase, 8);
-
- release_region(devc->base, 16);
-
- if (!(devc->caps & SB_NO_AUDIO))
- {
- sound_free_dma(devc->dma8);
- if (devc->dma16 >= 0)
- sound_free_dma(devc->dma16);
- }
- if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI))
- {
- if (devc->irq > 0)
- free_irq(devc->irq, devc);
-
- sb_mixer_unload(devc);
- /* We don't have to do this bit any more the UART401 is its own
- master -- Krzysztof Halasa */
- /* But we have to do it, if UART401 is not detected */
- if (!sbmpu)
- sound_unload_mididev(devc->my_mididev);
- sound_unload_audiodev(devc->dev);
- }
- kfree(devc);
- }
- else
- release_region(hw_config->io_base, 16);
-
- kfree(detected_devc);
-}
-
-/*
- * Mixer access routines
- *
- * ES1887 modifications: some mixer registers reside in the
- * range above 0xa0. These must be accessed in another way.
- */
-
-void sb_setmixer(sb_devc * devc, unsigned int port, unsigned int value)
-{
- unsigned long flags;
-
- if (devc->model == MDL_ESS) {
- ess_setmixer (devc, port, value);
- return;
- }
-
- spin_lock_irqsave(&devc->lock, flags);
-
- outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
- udelay(20);
- outb(((unsigned char) (value & 0xff)), MIXER_DATA);
- udelay(20);
-
- spin_unlock_irqrestore(&devc->lock, flags);
-}
-
-unsigned int sb_getmixer(sb_devc * devc, unsigned int port)
-{
- unsigned int val;
- unsigned long flags;
-
- if (devc->model == MDL_ESS) return ess_getmixer (devc, port);
-
- spin_lock_irqsave(&devc->lock, flags);
-
- outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
- udelay(20);
- val = inb(MIXER_DATA);
- udelay(20);
-
- spin_unlock_irqrestore(&devc->lock, flags);
-
- return val;
-}
-
-void sb_chgmixer
- (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val)
-{
- int value;
-
- value = sb_getmixer(devc, reg);
- value = (value & ~mask) | (val & mask);
- sb_setmixer(devc, reg, value);
-}
-
-/*
- * MPU401 MIDI initialization.
- */
-
-static void smw_putmem(sb_devc * devc, int base, int addr, unsigned char val)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&jazz16_lock, flags); /* NOT the SB card? */
-
- outb((addr & 0xff), base + 1); /* Low address bits */
- outb((addr >> 8), base + 2); /* High address bits */
- outb((val), base); /* Data */
-
- spin_unlock_irqrestore(&jazz16_lock, flags);
-}
-
-static unsigned char smw_getmem(sb_devc * devc, int base, int addr)
-{
- unsigned long flags;
- unsigned char val;
-
- spin_lock_irqsave(&jazz16_lock, flags); /* NOT the SB card? */
-
- outb((addr & 0xff), base + 1); /* Low address bits */
- outb((addr >> 8), base + 2); /* High address bits */
- val = inb(base); /* Data */
-
- spin_unlock_irqrestore(&jazz16_lock, flags);
- return val;
-}
-
-static int smw_midi_init(sb_devc * devc, struct address_info *hw_config)
-{
- int mpu_base = hw_config->io_base;
- int mp_base = mpu_base + 4; /* Microcontroller base */
- int i;
- unsigned char control;
-
-
- /*
- * Reset the microcontroller so that the RAM can be accessed
- */
-
- control = inb(mpu_base + 7);
- outb((control | 3), mpu_base + 7); /* Set last two bits to 1 (?) */
- outb(((control & 0xfe) | 2), mpu_base + 7); /* xxxxxxx0 resets the mc */
-
- mdelay(3); /* Wait at least 1ms */
-
- outb((control & 0xfc), mpu_base + 7); /* xxxxxx00 enables RAM */
-
- /*
- * Detect microcontroller by probing the 8k RAM area
- */
- smw_putmem(devc, mp_base, 0, 0x00);
- smw_putmem(devc, mp_base, 1, 0xff);
- udelay(10);
-
- if (smw_getmem(devc, mp_base, 0) != 0x00 || smw_getmem(devc, mp_base, 1) != 0xff)
- {
- DDB(printk("SM Wave: No microcontroller RAM detected (%02x, %02x)\n", smw_getmem(devc, mp_base, 0), smw_getmem(devc, mp_base, 1)));
- return 0; /* No RAM */
- }
- /*
- * There is RAM so assume it's really a SM Wave
- */
-
- devc->model = MDL_SMW;
- smw_mixer_init(devc);
-
-#ifdef MODULE
- if (!smw_ucode)
- {
- smw_ucodeLen = mod_firmware_load("/etc/sound/midi0001.bin", (void *) &smw_ucode);
- smw_free = smw_ucode;
- }
-#endif
- if (smw_ucodeLen > 0)
- {
- if (smw_ucodeLen != 8192)
- {
- printk(KERN_ERR "SM Wave: Invalid microcode (MIDI0001.BIN) length\n");
- return 1;
- }
- /*
- * Download microcode
- */
-
- for (i = 0; i < 8192; i++)
- smw_putmem(devc, mp_base, i, smw_ucode[i]);
-
- /*
- * Verify microcode
- */
-
- for (i = 0; i < 8192; i++)
- if (smw_getmem(devc, mp_base, i) != smw_ucode[i])
- {
- printk(KERN_ERR "SM Wave: Microcode verification failed\n");
- return 0;
- }
- }
- control = 0;
-#ifdef SMW_SCSI_IRQ
- /*
- * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt
- * is disabled by default.
- *
- * FIXME - make this a module option
- *
- * BTW the Zilog 5380 SCSI controller is located at MPU base + 0x10.
- */
- {
- static unsigned char scsi_irq_bits[] = {
- 0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0
- };
- control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6;
- }
-#endif
-
-#ifdef SMW_OPL4_ENABLE
- /*
- * Make the OPL4 chip visible on the PC bus at 0x380.
- *
- * There is no need to enable this feature since this driver
- * doesn't support OPL4 yet. Also there is no RAM in SM Wave so
- * enabling OPL4 is pretty useless.
- */
- control |= 0x10; /* Uses IRQ12 if bit 0x20 == 0 */
- /* control |= 0x20; Uncomment this if you want to use IRQ7 */
-#endif
- outb((control | 0x03), mpu_base + 7); /* xxxxxx11 restarts */
- hw_config->name = "SoundMan Wave";
- return 1;
-}
-
-static int init_Jazz16_midi(sb_devc * devc, struct address_info *hw_config)
-{
- int mpu_base = hw_config->io_base;
- int sb_base = devc->base;
- int irq = hw_config->irq;
-
- unsigned char bits = 0;
- unsigned long flags;
-
- if (irq < 0)
- irq *= -1;
-
- if (irq < 1 || irq > 15 ||
- jazz_irq_bits[irq] == 0)
- {
- printk(KERN_ERR "Jazz16: Invalid MIDI interrupt (IRQ%d)\n", irq);
- return 0;
- }
- switch (sb_base)
- {
- case 0x220:
- bits = 1;
- break;
- case 0x240:
- bits = 2;
- break;
- case 0x260:
- bits = 3;
- break;
- default:
- return 0;
- }
- bits = jazz16_bits = bits << 5;
- switch (mpu_base)
- {
- case 0x310:
- bits |= 1;
- break;
- case 0x320:
- bits |= 2;
- break;
- case 0x330:
- bits |= 3;
- break;
- default:
- printk(KERN_ERR "Jazz16: Invalid MIDI I/O port %x\n", mpu_base);
- return 0;
- }
- /*
- * Magic wake up sequence by writing to 0x201 (aka Joystick port)
- */
- spin_lock_irqsave(&jazz16_lock, flags);
- outb(0xAF, 0x201);
- outb(0x50, 0x201);
- outb(bits, 0x201);
- spin_unlock_irqrestore(&jazz16_lock, flags);
-
- hw_config->name = "Jazz16";
- smw_midi_init(devc, hw_config);
-
- if (!sb_dsp_command(devc, 0xfb))
- return 0;
-
- if (!sb_dsp_command(devc, jazz_dma_bits[devc->dma8] |
- (jazz_dma_bits[devc->dma16] << 4)))
- return 0;
-
- if (!sb_dsp_command(devc, jazz_irq_bits[devc->irq] |
- (jazz_irq_bits[irq] << 4)))
- return 0;
-
- return 1;
-}
-
-int probe_sbmpu(struct address_info *hw_config, struct module *owner)
-{
- sb_devc *devc = last_devc;
- int ret;
-
- if (last_devc == NULL)
- return 0;
-
- last_devc = NULL;
-
- if (hw_config->io_base <= 0)
- {
- /* The real vibra16 is fine about this, but we have to go
- wipe up after Cyrix again */
-
- if(devc->model == MDL_SB16 && devc->minor >= 12)
- {
- unsigned char bits = sb_getmixer(devc, 0x84) & ~0x06;
- sb_setmixer(devc, 0x84, bits | 0x02); /* Disable MPU */
- }
- return 0;
- }
-
-#if defined(CONFIG_SOUND_MPU401)
- if (devc->model == MDL_ESS)
- {
- struct resource *ports;
- ports = request_region(hw_config->io_base, 2, "mpu401");
- if (!ports) {
- printk(KERN_ERR "sbmpu: I/O port conflict (%x)\n", hw_config->io_base);
- return 0;
- }
- if (!ess_midi_init(devc, hw_config)) {
- release_region(hw_config->io_base, 2);
- return 0;
- }
- hw_config->name = "ESS1xxx MPU";
- devc->midi_irq_cookie = NULL;
- if (!probe_mpu401(hw_config, ports)) {
- release_region(hw_config->io_base, 2);
- return 0;
- }
- attach_mpu401(hw_config, owner);
- if (last_sb->irq == -hw_config->irq)
- last_sb->midi_irq_cookie =
- (void *)(long) hw_config->slots[1];
- return 1;
- }
-#endif
-
- switch (devc->model)
- {
- case MDL_SB16:
- if (hw_config->io_base != 0x300 && hw_config->io_base != 0x330)
- {
- printk(KERN_ERR "SB16: Invalid MIDI port %x\n", hw_config->io_base);
- return 0;
- }
- hw_config->name = "Sound Blaster 16";
- if (hw_config->irq < 3 || hw_config->irq == devc->irq)
- hw_config->irq = -devc->irq;
- if (devc->minor > 12) /* What is Vibra's version??? */
- sb16_set_mpu_port(devc, hw_config);
- break;
-
- case MDL_JAZZ:
- if (hw_config->irq < 3 || hw_config->irq == devc->irq)
- hw_config->irq = -devc->irq;
- if (!init_Jazz16_midi(devc, hw_config))
- return 0;
- break;
-
- case MDL_YMPCI:
- hw_config->name = "Yamaha PCI Legacy";
- printk("Yamaha PCI legacy UART401 check.\n");
- break;
- default:
- return 0;
- }
-
- ret = probe_uart401(hw_config, owner);
- if (ret)
- last_sb->midi_irq_cookie=midi_devs[hw_config->slots[4]]->devc;
- return ret;
-}
-
-void unload_sbmpu(struct address_info *hw_config)
-{
-#if defined(CONFIG_SOUND_MPU401)
- if (!strcmp (hw_config->name, "ESS1xxx MPU")) {
- unload_mpu401(hw_config);
- return;
- }
-#endif
- unload_uart401(hw_config);
-}
-
-EXPORT_SYMBOL(sb_dsp_init);
-EXPORT_SYMBOL(sb_dsp_detect);
-EXPORT_SYMBOL(sb_dsp_unload);
-EXPORT_SYMBOL(sb_be_quiet);
-EXPORT_SYMBOL(probe_sbmpu);
-EXPORT_SYMBOL(unload_sbmpu);
-EXPORT_SYMBOL(smw_free);
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/sb_ess.c b/sound/oss/sb_ess.c
deleted file mode 100644
index 9890cf2066ff..000000000000
--- a/sound/oss/sb_ess.c
+++ /dev/null
@@ -1,1831 +0,0 @@
-#undef FKS_LOGGING
-#undef FKS_TEST
-
-/*
- * tabs should be 4 spaces, in vi(m): set tabstop=4
- *
- * TODO: consistency speed calculations!!
- * cleanup!
- * ????: Did I break MIDI support?
- *
- * History:
- *
- * Rolf Fokkens (Dec 20 1998): ES188x recording level support on a per
- * fokkensr@vertis.nl input basis.
- * (Dec 24 1998): Recognition of ES1788, ES1887, ES1888,
- * ES1868, ES1869 and ES1878. Could be used for
- * specific handling in the future. All except
- * ES1887 and ES1888 and ES688 are handled like
- * ES1688.
- * (Dec 27 1998): RECLEV for all (?) ES1688+ chips. ES188x now
- * have the "Dec 20" support + RECLEV
- * (Jan 2 1999): Preparation for Full Duplex. This means
- * Audio 2 is now used for playback when dma16
- * is specified. The next step would be to use
- * Audio 1 and Audio 2 at the same time.
- * (Jan 9 1999): Put all ESS stuff into sb_ess.[ch], this
- * includes both the ESS stuff that has been in
- * sb_*[ch] before I touched it and the ESS support
- * I added later
- * (Jan 23 1999): Full Duplex seems to work. I wrote a small
- * test proggy which works OK. Haven't found
- * any applications to test it though. So why did
- * I bother to create it anyway?? :) Just for
- * fun.
- * (May 2 1999): I tried to be too smart by "introducing"
- * ess_calc_best_speed (). The idea was that two
- * dividers could be used to setup a samplerate,
- * ess_calc_best_speed () would choose the best.
- * This works for playback, but results in
- * recording problems for high samplerates. I
- * fixed this by removing ess_calc_best_speed ()
- * and just doing what the documentation says.
- * Andy Sloane (Jun 4 1999): Stole some code from ALSA to fix the playback
- * andy@guildsoftware.com speed on ES1869, ES1879, ES1887, and ES1888.
- * 1879's were previously ignored by this driver;
- * added (untested) support for those.
- * Cvetan Ivanov (Oct 27 1999): Fixed ess_dsp_init to call ess_set_dma_hw for
- * zezo@inet.bg _ALL_ ESS models, not only ES1887
- *
- * This files contains ESS chip specifics. It's based on the existing ESS
- * handling as it resided in sb_common.c, sb_mixer.c and sb_audio.c. This
- * file adds features like:
- * - Chip Identification (as shown in /proc/sound)
- * - RECLEV support for ES1688 and later
- * - 6 bits playback level support chips later than ES1688
- * - Recording level support on a per-device basis for ES1887
- * - Full-Duplex for ES1887
- *
- * Full duplex is enabled by specifying dma16. While the normal dma must
- * be one of 0, 1 or 3, dma16 can be one of 0, 1, 3 or 5. DMA 5 is a 16 bit
- * DMA channel, while the others are 8 bit..
- *
- * ESS detection isn't full proof (yet). If it fails an additional module
- * parameter esstype can be specified to be one of the following:
- * -1, 0, 688, 1688, 1868, 1869, 1788, 1887, 1888
- * -1 means: mimic 2.0 behaviour,
- * 0 means: auto detect.
- * others: explicitly specify chip
- * -1 is default, cause auto detect still doesn't work.
- */
-
-/*
- * About the documentation
- *
- * I don't know if the chips all are OK, but the documentation is buggy. 'cause
- * I don't have all the cips myself, there's a lot I cannot verify. I'll try to
- * keep track of my latest insights about his here. If you have additional info,
- * please enlighten me (fokkensr@vertis.nl)!
- *
- * I had the impression that ES1688 also has 6 bit master volume control. The
- * documentation about ES1888 (rev C, october '95) claims that ES1888 has
- * the following features ES1688 doesn't have:
- * - 6 bit master volume
- * - Full Duplex
- * So ES1688 apparently doesn't have 6 bit master volume control, but the
- * ES1688 does have RECLEV control. Makes me wonder: does ES688 have it too?
- * Without RECLEV ES688 won't be much fun I guess.
- *
- * From the ES1888 (rev C, october '95) documentation I got the impression
- * that registers 0x68 to 0x6e don't exist which means: no recording volume
- * controls. To my surprise the ES888 documentation (1/14/96) claims that
- * ES888 does have these record mixer registers, but that ES1888 doesn't have
- * 0x69 and 0x6b. So the rest should be there.
- *
- * I'm trying to get ES1887 Full Duplex. Audio 2 is playback only, while Audio 2
- * is both record and playback. I think I should use Audio 2 for all playback.
- *
- * The documentation is an adventure: it's close but not fully accurate. I
- * found out that after a reset some registers are *NOT* reset, though the
- * docs say the would be. Interesting ones are 0x7f, 0x7d and 0x7a. They are
- * related to the Audio 2 channel. I also was surprised about the consequences
- * of writing 0x00 to 0x7f (which should be done by reset): The ES1887 moves
- * into ES1888 mode. This means that it claims IRQ 11, which happens to be my
- * ISDN adapter. Needless to say it no longer worked. I now understand why
- * after rebooting 0x7f already was 0x05, the value of my choice: the BIOS
- * did it.
- *
- * Oh, and this is another trap: in ES1887 docs mixer register 0x70 is
- * described as if it's exactly the same as register 0xa1. This is *NOT* true.
- * The description of 0x70 in ES1869 docs is accurate however.
- * Well, the assumption about ES1869 was wrong: register 0x70 is very much
- * like register 0xa1, except that bit 7 is always 1, whatever you want
- * it to be.
- *
- * When using audio 2 mixer register 0x72 seems te be meaningless. Only 0xa2
- * has effect.
- *
- * Software reset not being able to reset all registers is great! Especially
- * the fact that register 0x78 isn't reset is great when you wanna change back
- * to single dma operation (simplex): audio 2 is still operational, and uses
- * the same dma as audio 1: your ess changes into a funny echo machine.
- *
- * Received the news that ES1688 is detected as a ES1788. Did some thinking:
- * the ES1887 detection scheme suggests in step 2 to try if bit 3 of register
- * 0x64 can be changed. This is inaccurate, first I inverted the * check: "If
- * can be modified, it's a 1688", which lead to a correct detection
- * of my ES1887. It resulted however in bad detection of 1688 (reported by mail)
- * and 1868 (if no PnP detection first): they result in a 1788 being detected.
- * I don't have docs on 1688, but I do have docs on 1868: The documentation is
- * probably inaccurate in the fact that I should check bit 2, not bit 3. This
- * is what I do now.
- */
-
-/*
- * About recognition of ESS chips
- *
- * The distinction of ES688, ES1688, ES1788, ES1887 and ES1888 is described in
- * a (preliminary ??) datasheet on ES1887. Its aim is to identify ES1887, but
- * during detection the text claims that "this chip may be ..." when a step
- * fails. This scheme is used to distinct between the above chips.
- * It appears however that some PnP chips like ES1868 are recognized as ES1788
- * by the ES1887 detection scheme. These PnP chips can be detected in another
- * way however: ES1868, ES1869 and ES1878 can be recognized (full proof I think)
- * by repeatedly reading mixer register 0x40. This is done by ess_identify in
- * sb_common.c.
- * This results in the following detection steps:
- * - distinct between ES688 and ES1688+ (as always done in this driver)
- * if ES688 we're ready
- * - try to detect ES1868, ES1869 or ES1878
- * if successful we're ready
- * - try to detect ES1888, ES1887 or ES1788
- * if successful we're ready
- * - Dunno. Must be 1688. Will do in general
- *
- * About RECLEV support:
- *
- * The existing ES1688 support didn't take care of the ES1688+ recording
- * levels very well. Whenever a device was selected (recmask) for recording
- * its recording level was loud, and it couldn't be changed. The fact that
- * internal register 0xb4 could take care of RECLEV, didn't work meaning until
- * its value was restored every time the chip was reset; this reset the
- * value of 0xb4 too. I guess that's what 4front also had (have?) trouble with.
- *
- * About ES1887 support:
- *
- * The ES1887 has separate registers to control the recording levels, for all
- * inputs. The ES1887 specific software makes these levels the same as their
- * corresponding playback levels, unless recmask says they aren't recorded. In
- * the latter case the recording volumes are 0.
- * Now recording levels of inputs can be controlled, by changing the playback
- * levels. Futhermore several devices can be recorded together (which is not
- * possible with the ES1688).
- * Besides the separate recording level control for each input, the common
- * recording level can also be controlled by RECLEV as described above.
- *
- * Not only ES1887 have this recording mixer. I know the following from the
- * documentation:
- * ES688 no
- * ES1688 no
- * ES1868 no
- * ES1869 yes
- * ES1878 no
- * ES1879 yes
- * ES1888 no/yes Contradicting documentation; most recent: yes
- * ES1946 yes This is a PCI chip; not handled by this driver
- */
-
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-
-#include "sound_config.h"
-#include "sb_mixer.h"
-#include "sb.h"
-
-#include "sb_ess.h"
-
-#define ESSTYPE_LIKE20 -1 /* Mimic 2.0 behaviour */
-#define ESSTYPE_DETECT 0 /* Mimic 2.0 behaviour */
-
-#define SUBMDL_ES1788 0x10 /* Subtype ES1788 for specific handling */
-#define SUBMDL_ES1868 0x11 /* Subtype ES1868 for specific handling */
-#define SUBMDL_ES1869 0x12 /* Subtype ES1869 for specific handling */
-#define SUBMDL_ES1878 0x13 /* Subtype ES1878 for specific handling */
-#define SUBMDL_ES1879 0x16 /* ES1879 was initially forgotten */
-#define SUBMDL_ES1887 0x14 /* Subtype ES1887 for specific handling */
-#define SUBMDL_ES1888 0x15 /* Subtype ES1888 for specific handling */
-
-#define SB_CAP_ES18XX_RATE 0x100
-
-#define ES1688_CLOCK1 795444 /* 128 - div */
-#define ES1688_CLOCK2 397722 /* 256 - div */
-#define ES18XX_CLOCK1 793800 /* 128 - div */
-#define ES18XX_CLOCK2 768000 /* 256 - div */
-
-#ifdef FKS_LOGGING
-static void ess_show_mixerregs (sb_devc *devc);
-#endif
-static int ess_read (sb_devc * devc, unsigned char reg);
-static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data);
-static void ess_chgmixer
- (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val);
-
-/****************************************************************************
- * *
- * ESS audio *
- * *
- ****************************************************************************/
-
-struct ess_command {short cmd; short data;};
-
-/*
- * Commands for initializing Audio 1 for input (record)
- */
-static struct ess_command ess_i08m[] = /* input 8 bit mono */
- { {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} };
-static struct ess_command ess_i16m[] = /* input 16 bit mono */
- { {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} };
-static struct ess_command ess_i08s[] = /* input 8 bit stereo */
- { {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} };
-static struct ess_command ess_i16s[] = /* input 16 bit stereo */
- { {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} };
-
-static struct ess_command *ess_inp_cmds[] =
- { ess_i08m, ess_i16m, ess_i08s, ess_i16s };
-
-
-/*
- * Commands for initializing Audio 1 for output (playback)
- */
-static struct ess_command ess_o08m[] = /* output 8 bit mono */
- { {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} };
-static struct ess_command ess_o16m[] = /* output 16 bit mono */
- { {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} };
-static struct ess_command ess_o08s[] = /* output 8 bit stereo */
- { {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} };
-static struct ess_command ess_o16s[] = /* output 16 bit stereo */
- { {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} };
-
-static struct ess_command *ess_out_cmds[] =
- { ess_o08m, ess_o16m, ess_o08s, ess_o16s };
-
-static void ess_exec_commands
- (sb_devc *devc, struct ess_command *cmdtab[])
-{
- struct ess_command *cmd;
-
- cmd = cmdtab [ ((devc->channels != 1) << 1) + (devc->bits != AFMT_U8) ];
-
- while (cmd->cmd != -1) {
- ess_write (devc, cmd->cmd, cmd->data);
- cmd++;
- }
-}
-
-static void ess_change
- (sb_devc *devc, unsigned int reg, unsigned int mask, unsigned int val)
-{
- int value;
-
- value = ess_read (devc, reg);
- value = (value & ~mask) | (val & mask);
- ess_write (devc, reg, value);
-}
-
-static void ess_set_output_parms
- (int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (devc->duplex) {
- devc->trg_buf_16 = buf;
- devc->trg_bytes_16 = nr_bytes;
- devc->trg_intrflag_16 = intrflag;
- devc->irq_mode_16 = IMODE_OUTPUT;
- } else {
- devc->trg_buf = buf;
- devc->trg_bytes = nr_bytes;
- devc->trg_intrflag = intrflag;
- devc->irq_mode = IMODE_OUTPUT;
- }
-}
-
-static void ess_set_input_parms
- (int dev, unsigned long buf, int count, int intrflag)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- devc->trg_buf = buf;
- devc->trg_bytes = count;
- devc->trg_intrflag = intrflag;
- devc->irq_mode = IMODE_INPUT;
-}
-
-static int ess_calc_div (int clock, int revert, int *speedp, int *diffp)
-{
- int divider;
- int speed, diff;
- int retval;
-
- speed = *speedp;
- divider = (clock + speed / 2) / speed;
- retval = revert - divider;
- if (retval > revert - 1) {
- retval = revert - 1;
- divider = revert - retval;
- }
- /* This line is suggested. Must be wrong I think
- *speedp = (clock + divider / 2) / divider;
- So I chose the next one */
-
- *speedp = clock / divider;
- diff = speed - *speedp;
- if (diff < 0) diff =-diff;
- *diffp = diff;
-
- return retval;
-}
-
-static int ess_calc_best_speed
- (int clock1, int rev1, int clock2, int rev2, int *divp, int *speedp)
-{
- int speed1 = *speedp, speed2 = *speedp;
- int div1, div2;
- int diff1, diff2;
- int retval;
-
- div1 = ess_calc_div (clock1, rev1, &speed1, &diff1);
- div2 = ess_calc_div (clock2, rev2, &speed2, &diff2);
-
- if (diff1 < diff2) {
- *divp = div1;
- *speedp = speed1;
- retval = 1;
- } else {
- /* *divp = div2; */
- *divp = 0x80 | div2;
- *speedp = speed2;
- retval = 2;
- }
-
- return retval;
-}
-
-/*
- * Depending on the audiochannel ESS devices can
- * have different clock settings. These are made consistent for duplex
- * however.
- * callers of ess_speed only do an audionum suggestion, which means
- * input suggests 1, output suggests 2. This suggestion is only true
- * however when doing duplex.
- */
-static void ess_common_speed (sb_devc *devc, int *speedp, int *divp)
-{
- int diff = 0, div;
-
- if (devc->duplex) {
- /*
- * The 0x80 is important for the first audio channel
- */
- if (devc->submodel == SUBMDL_ES1888) {
- div = 0x80 | ess_calc_div (795500, 256, speedp, &diff);
- } else {
- div = 0x80 | ess_calc_div (795500, 128, speedp, &diff);
- }
- } else if(devc->caps & SB_CAP_ES18XX_RATE) {
- if (devc->submodel == SUBMDL_ES1888) {
- ess_calc_best_speed(397700, 128, 795500, 256,
- &div, speedp);
- } else {
- ess_calc_best_speed(ES18XX_CLOCK1, 128, ES18XX_CLOCK2, 256,
- &div, speedp);
- }
- } else {
- if (*speedp > 22000) {
- div = 0x80 | ess_calc_div (ES1688_CLOCK1, 256, speedp, &diff);
- } else {
- div = 0x00 | ess_calc_div (ES1688_CLOCK2, 128, speedp, &diff);
- }
- }
- *divp = div;
-}
-
-static void ess_speed (sb_devc *devc, int audionum)
-{
- int speed;
- int div, div2;
-
- ess_common_speed (devc, &(devc->speed), &div);
-
-#ifdef FKS_REG_LOGGING
-printk (KERN_INFO "FKS: ess_speed (%d) b speed = %d, div=%x\n", audionum, devc->speed, div);
-#endif
-
- /* Set filter roll-off to 90% of speed/2 */
- speed = (devc->speed * 9) / 20;
-
- div2 = 256 - 7160000 / (speed * 82);
-
- if (!devc->duplex) audionum = 1;
-
- if (audionum == 1) {
- /* Change behaviour of register A1 *
- sb_chg_mixer(devc, 0x71, 0x20, 0x20)
- * For ES1869 only??? */
- ess_write (devc, 0xa1, div);
- ess_write (devc, 0xa2, div2);
- } else {
- ess_setmixer (devc, 0x70, div);
- /*
- * FKS: fascinating: 0x72 doesn't seem to work.
- */
- ess_write (devc, 0xa2, div2);
- ess_setmixer (devc, 0x72, div2);
- }
-}
-
-static int ess_audio_prepare_for_input(int dev, int bsize, int bcount)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- ess_speed(devc, 1);
-
- sb_dsp_command(devc, DSP_CMD_SPKOFF);
-
- ess_write (devc, 0xb8, 0x0e); /* Auto init DMA mode */
- ess_change (devc, 0xa8, 0x03, 3 - devc->channels); /* Mono/stereo */
- ess_write (devc, 0xb9, 2); /* Demand mode (4 bytes/DMA request) */
-
- ess_exec_commands (devc, ess_inp_cmds);
-
- ess_change (devc, 0xb1, 0xf0, 0x50);
- ess_change (devc, 0xb2, 0xf0, 0x50);
-
- devc->trigger_bits = 0;
- return 0;
-}
-
-static int ess_audio_prepare_for_output_audio1 (int dev, int bsize, int bcount)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- sb_dsp_reset(devc);
- ess_speed(devc, 1);
- ess_write (devc, 0xb8, 4); /* Auto init DMA mode */
- ess_change (devc, 0xa8, 0x03, 3 - devc->channels); /* Mono/stereo */
- ess_write (devc, 0xb9, 2); /* Demand mode (4 bytes/request) */
-
- ess_exec_commands (devc, ess_out_cmds);
-
- ess_change (devc, 0xb1, 0xf0, 0x50); /* Enable DMA */
- ess_change (devc, 0xb2, 0xf0, 0x50); /* Enable IRQ */
-
- sb_dsp_command(devc, DSP_CMD_SPKON); /* There be sound! */
-
- devc->trigger_bits = 0;
- return 0;
-}
-
-static int ess_audio_prepare_for_output_audio2 (int dev, int bsize, int bcount)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- unsigned char bits;
-
-/* FKS: qqq
- sb_dsp_reset(devc);
-*/
-
- /*
- * Auto-Initialize:
- * DMA mode + demand mode (8 bytes/request, yes I want it all!)
- * But leave 16-bit DMA bit untouched!
- */
- ess_chgmixer (devc, 0x78, 0xd0, 0xd0);
-
- ess_speed(devc, 2);
-
- /* bits 4:3 on ES1887 represent recording source. Keep them! */
- bits = ess_getmixer (devc, 0x7a) & 0x18;
-
- /* Set stereo/mono */
- if (devc->channels != 1) bits |= 0x02;
-
- /* Init DACs; UNSIGNED mode for 8 bit; SIGNED mode for 16 bit */
- if (devc->bits != AFMT_U8) bits |= 0x05; /* 16 bit */
-
- /* Enable DMA, IRQ will be shared (hopefully)*/
- bits |= 0x60;
-
- ess_setmixer (devc, 0x7a, bits);
-
- ess_mixer_reload (devc, SOUND_MIXER_PCM); /* There be sound! */
-
- devc->trigger_bits = 0;
- return 0;
-}
-
-static int ess_audio_prepare_for_output(int dev, int bsize, int bcount)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
-#ifdef FKS_REG_LOGGING
-printk(KERN_INFO "ess_audio_prepare_for_output: dma_out=%d,dma_in=%d\n"
-, audio_devs[dev]->dmap_out->dma, audio_devs[dev]->dmap_in->dma);
-#endif
-
- if (devc->duplex) {
- return ess_audio_prepare_for_output_audio2 (dev, bsize, bcount);
- } else {
- return ess_audio_prepare_for_output_audio1 (dev, bsize, bcount);
- }
-}
-
-static void ess_audio_halt_xfer(int dev)
-{
- unsigned long flags;
- sb_devc *devc = audio_devs[dev]->devc;
-
- spin_lock_irqsave(&devc->lock, flags);
- sb_dsp_reset(devc);
- spin_unlock_irqrestore(&devc->lock, flags);
-
- /*
- * Audio 2 may still be operational! Creates awful sounds!
- */
- if (devc->duplex) ess_chgmixer(devc, 0x78, 0x03, 0x00);
-}
-
-static void ess_audio_start_input
- (int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
- short c = -nr_bytes;
-
- /*
- * Start a DMA input to the buffer pointed by dmaqtail
- */
-
- if (audio_devs[dev]->dmap_in->dma > 3) count >>= 1;
- count--;
-
- devc->irq_mode = IMODE_INPUT;
-
- ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff));
- ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
-
- ess_change (devc, 0xb8, 0x0f, 0x0f); /* Go */
- devc->intr_active = 1;
-}
-
-static void ess_audio_output_block_audio1
- (int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
- short c = -nr_bytes;
-
- if (audio_devs[dev]->dmap_out->dma > 3)
- count >>= 1;
- count--;
-
- devc->irq_mode = IMODE_OUTPUT;
-
- ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff));
- ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff));
-
- ess_change (devc, 0xb8, 0x05, 0x05); /* Go */
- devc->intr_active = 1;
-}
-
-static void ess_audio_output_block_audio2
- (int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
- int count = nr_bytes;
- sb_devc *devc = audio_devs[dev]->devc;
- short c = -nr_bytes;
-
- if (audio_devs[dev]->dmap_out->dma > 3) count >>= 1;
- count--;
-
- ess_setmixer (devc, 0x74, (unsigned char) ((unsigned short) c & 0xff));
- ess_setmixer (devc, 0x76, (unsigned char) (((unsigned short) c >> 8) & 0xff));
- ess_chgmixer (devc, 0x78, 0x03, 0x03); /* Go */
-
- devc->irq_mode_16 = IMODE_OUTPUT;
- devc->intr_active_16 = 1;
-}
-
-static void ess_audio_output_block
- (int dev, unsigned long buf, int nr_bytes, int intrflag)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (devc->duplex) {
- ess_audio_output_block_audio2 (dev, buf, nr_bytes, intrflag);
- } else {
- ess_audio_output_block_audio1 (dev, buf, nr_bytes, intrflag);
- }
-}
-
-/*
- * FKS: the if-statements for both bits and bits_16 are quite alike.
- * Combine this...
- */
-static void ess_audio_trigger(int dev, int bits)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- int bits_16 = bits & devc->irq_mode_16;
- bits &= devc->irq_mode;
-
- if (!bits && !bits_16) {
- /* FKS oh oh.... wrong?? for dma 16? */
- sb_dsp_command(devc, 0xd0); /* Halt DMA */
- }
-
- if (bits) {
- switch (devc->irq_mode)
- {
- case IMODE_INPUT:
- ess_audio_start_input(dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
-
- case IMODE_OUTPUT:
- ess_audio_output_block(dev, devc->trg_buf, devc->trg_bytes,
- devc->trg_intrflag);
- break;
- }
- }
-
- if (bits_16) {
- switch (devc->irq_mode_16) {
- case IMODE_INPUT:
- ess_audio_start_input(dev, devc->trg_buf_16, devc->trg_bytes_16,
- devc->trg_intrflag_16);
- break;
-
- case IMODE_OUTPUT:
- ess_audio_output_block(dev, devc->trg_buf_16, devc->trg_bytes_16,
- devc->trg_intrflag_16);
- break;
- }
- }
-
- devc->trigger_bits = bits | bits_16;
-}
-
-static int ess_audio_set_speed(int dev, int speed)
-{
- sb_devc *devc = audio_devs[dev]->devc;
- int minspeed, maxspeed, dummydiv;
-
- if (speed > 0) {
- minspeed = (devc->duplex ? 6215 : 5000 );
- maxspeed = (devc->duplex ? 44100 : 48000);
- if (speed < minspeed) speed = minspeed;
- if (speed > maxspeed) speed = maxspeed;
-
- ess_common_speed (devc, &speed, &dummydiv);
-
- devc->speed = speed;
- }
- return devc->speed;
-}
-
-/*
- * FKS: This is a one-on-one copy of sb1_audio_set_bits
- */
-static unsigned int ess_audio_set_bits(int dev, unsigned int bits)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (bits != 0) {
- if (bits == AFMT_U8 || bits == AFMT_S16_LE) {
- devc->bits = bits;
- } else {
- devc->bits = AFMT_U8;
- }
- }
-
- return devc->bits;
-}
-
-/*
- * FKS: This is a one-on-one copy of sbpro_audio_set_channels
- * (*) Modified it!!
- */
-static short ess_audio_set_channels(int dev, short channels)
-{
- sb_devc *devc = audio_devs[dev]->devc;
-
- if (channels == 1 || channels == 2) devc->channels = channels;
-
- return devc->channels;
-}
-
-static struct audio_driver ess_audio_driver = /* ESS ES688/1688 */
-{
- .owner = THIS_MODULE,
- .open = sb_audio_open,
- .close = sb_audio_close,
- .output_block = ess_set_output_parms,
- .start_input = ess_set_input_parms,
- .prepare_for_input = ess_audio_prepare_for_input,
- .prepare_for_output = ess_audio_prepare_for_output,
- .halt_io = ess_audio_halt_xfer,
- .trigger = ess_audio_trigger,
- .set_speed = ess_audio_set_speed,
- .set_bits = ess_audio_set_bits,
- .set_channels = ess_audio_set_channels
-};
-
-/*
- * ess_audio_init must be called from sb_audio_init
- */
-struct audio_driver *ess_audio_init
- (sb_devc *devc, int *audio_flags, int *format_mask)
-{
- *audio_flags = DMA_AUTOMODE;
- *format_mask |= AFMT_S16_LE;
-
- if (devc->duplex) {
- int tmp_dma;
- /*
- * sb_audio_init thinks dma8 is for playback and
- * dma16 is for record. Not now! So swap them.
- */
- tmp_dma = devc->dma16;
- devc->dma16 = devc->dma8;
- devc->dma8 = tmp_dma;
-
- *audio_flags |= DMA_DUPLEX;
- }
-
- return &ess_audio_driver;
-}
-
-/****************************************************************************
- * *
- * ESS common *
- * *
- ****************************************************************************/
-static void ess_handle_channel
- (char *channel, int dev, int intr_active, unsigned char flag, int irq_mode)
-{
- if (!intr_active || !flag) return;
-#ifdef FKS_REG_LOGGING
-printk(KERN_INFO "FKS: ess_handle_channel %s irq_mode=%d\n", channel, irq_mode);
-#endif
- switch (irq_mode) {
- case IMODE_OUTPUT:
- DMAbuf_outputintr (dev, 1);
- break;
-
- case IMODE_INPUT:
- DMAbuf_inputintr (dev);
- break;
-
- case IMODE_INIT:
- break;
-
- default:;
- /* printk(KERN_WARNING "ESS: Unexpected interrupt\n"); */
- }
-}
-
-/*
- * FKS: TODO!!! Finish this!
- *
- * I think midi stuff uses uart401, without interrupts.
- * So IMODE_MIDI isn't a value for devc->irq_mode.
- */
-void ess_intr (sb_devc *devc)
-{
- int status;
- unsigned char src;
-
- if (devc->submodel == SUBMDL_ES1887) {
- src = ess_getmixer (devc, 0x7f) >> 4;
- } else {
- src = 0xff;
- }
-
-#ifdef FKS_REG_LOGGING
-printk(KERN_INFO "FKS: sbintr src=%x\n",(int)src);
-#endif
- ess_handle_channel
- ( "Audio 1"
- , devc->dev, devc->intr_active , src & 0x01, devc->irq_mode );
- ess_handle_channel
- ( "Audio 2"
- , devc->dev, devc->intr_active_16, src & 0x02, devc->irq_mode_16);
- /*
- * Acknowledge interrupts
- */
- if (devc->submodel == SUBMDL_ES1887 && (src & 0x02)) {
- ess_chgmixer (devc, 0x7a, 0x80, 0x00);
- }
-
- if (src & 0x01) {
- status = inb(DSP_DATA_AVAIL);
- }
-}
-
-static void ess_extended (sb_devc * devc)
-{
- /* Enable extended mode */
-
- sb_dsp_command(devc, 0xc6);
-}
-
-static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data)
-{
-#ifdef FKS_REG_LOGGING
-printk(KERN_INFO "FKS: write reg %x: %x\n", reg, data);
-#endif
- /* Write a byte to an extended mode register of ES1688 */
-
- if (!sb_dsp_command(devc, reg))
- return 0;
-
- return sb_dsp_command(devc, data);
-}
-
-static int ess_read (sb_devc * devc, unsigned char reg)
-{
- /* Read a byte from an extended mode register of ES1688 */
-
- /* Read register command */
- if (!sb_dsp_command(devc, 0xc0)) return -1;
-
- if (!sb_dsp_command(devc, reg )) return -1;
-
- return sb_dsp_get_byte(devc);
-}
-
-int ess_dsp_reset(sb_devc * devc)
-{
- int loopc;
-
-#ifdef FKS_REG_LOGGING
-printk(KERN_INFO "FKS: ess_dsp_reset 1\n");
-ess_show_mixerregs (devc);
-#endif
-
- DEB(printk("Entered ess_dsp_reset()\n"));
-
- outb(3, DSP_RESET); /* Reset FIFO too */
-
- udelay(10);
- outb(0, DSP_RESET);
- udelay(30);
-
- for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++);
-
- if (inb(DSP_READ) != 0xAA) {
- DDB(printk("sb: No response to RESET\n"));
- return 0; /* Sorry */
- }
- ess_extended (devc);
-
- DEB(printk("sb_dsp_reset() OK\n"));
-
-#ifdef FKS_LOGGING
-printk(KERN_INFO "FKS: dsp_reset 2\n");
-ess_show_mixerregs (devc);
-#endif
-
- return 1;
-}
-
-static int ess_irq_bits (int irq)
-{
- switch (irq) {
- case 2:
- case 9:
- return 0;
-
- case 5:
- return 1;
-
- case 7:
- return 2;
-
- case 10:
- return 3;
-
- default:
- printk(KERN_ERR "ESS1688: Invalid IRQ %d\n", irq);
- return -1;
- }
-}
-
-/*
- * Set IRQ configuration register for all ESS models
- */
-static int ess_common_set_irq_hw (sb_devc * devc)
-{
- int irq_bits;
-
- if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return 0;
-
- if (!ess_write (devc, 0xb1, 0x50 | (irq_bits << 2))) {
- printk(KERN_ERR "ES1688: Failed to write to IRQ config register\n");
- return 0;
- }
- return 1;
-}
-
-/*
- * I wanna use modern ES1887 mixer irq handling. Funny is the
- * fact that my BIOS wants the same. But suppose someone's BIOS
- * doesn't do this!
- * This is independent of duplex. If there's a 1887 this will
- * prevent it from going into 1888 mode.
- */
-static void ess_es1887_set_irq_hw (sb_devc * devc)
-{
- int irq_bits;
-
- if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return;
-
- ess_chgmixer (devc, 0x7f, 0x0f, 0x01 | ((irq_bits + 1) << 1));
-}
-
-static int ess_set_irq_hw (sb_devc * devc)
-{
- if (devc->submodel == SUBMDL_ES1887) ess_es1887_set_irq_hw (devc);
-
- return ess_common_set_irq_hw (devc);
-}
-
-#ifdef FKS_TEST
-
-/*
- * FKS_test:
- * for ES1887: 00, 18, non wr bits: 0001 1000
- * for ES1868: 00, b8, non wr bits: 1011 1000
- * for ES1888: 00, f8, non wr bits: 1111 1000
- * for ES1688: 00, f8, non wr bits: 1111 1000
- * + ES968
- */
-
-static void FKS_test (sb_devc * devc)
-{
- int val1, val2;
- val1 = ess_getmixer (devc, 0x64);
- ess_setmixer (devc, 0x64, ~val1);
- val2 = ess_getmixer (devc, 0x64) ^ ~val1;
- ess_setmixer (devc, 0x64, val1);
- val1 ^= ess_getmixer (devc, 0x64);
-printk (KERN_INFO "FKS: FKS_test %02x, %02x\n", (val1 & 0x0ff), (val2 & 0x0ff));
-};
-#endif
-
-static unsigned int ess_identify (sb_devc * devc)
-{
- unsigned int val;
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock, flags);
- outb(((unsigned char) (0x40 & 0xff)), MIXER_ADDR);
-
- udelay(20);
- val = inb(MIXER_DATA) << 8;
- udelay(20);
- val |= inb(MIXER_DATA);
- udelay(20);
- spin_unlock_irqrestore(&devc->lock, flags);
-
- return val;
-}
-
-/*
- * ESS technology describes a detection scheme in their docs. It involves
- * fiddling with the bits in certain mixer registers. ess_probe is supposed
- * to help.
- *
- * FKS: tracing shows ess_probe writes wrong value to 0x64. Bit 3 reads 1, but
- * should be written 0 only. Check this.
- */
-static int ess_probe (sb_devc * devc, int reg, int xorval)
-{
- int val1, val2, val3;
-
- val1 = ess_getmixer (devc, reg);
- val2 = val1 ^ xorval;
- ess_setmixer (devc, reg, val2);
- val3 = ess_getmixer (devc, reg);
- ess_setmixer (devc, reg, val1);
-
- return (val2 == val3);
-}
-
-int ess_init(sb_devc * devc, struct address_info *hw_config)
-{
- unsigned char cfg;
- int ess_major = 0, ess_minor = 0;
- int i;
- static char name[100], modelname[10];
-
- /*
- * Try to detect ESS chips.
- */
-
- sb_dsp_command(devc, 0xe7); /* Return identification */
-
- for (i = 1000; i; i--) {
- if (inb(DSP_DATA_AVAIL) & 0x80) {
- if (ess_major == 0) {
- ess_major = inb(DSP_READ);
- } else {
- ess_minor = inb(DSP_READ);
- break;
- }
- }
- }
-
- if (ess_major == 0) return 0;
-
- if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) {
- sprintf(name, "ESS ES488 AudioDrive (rev %d)",
- ess_minor & 0x0f);
- hw_config->name = name;
- devc->model = MDL_SBPRO;
- return 1;
- }
-
- /*
- * This the detection heuristic of ESS technology, though somewhat
- * changed to actually make it work.
- * This results in the following detection steps:
- * - distinct between ES688 and ES1688+ (as always done in this driver)
- * if ES688 we're ready
- * - try to detect ES1868, ES1869 or ES1878 (ess_identify)
- * if successful we're ready
- * - try to detect ES1888, ES1887 or ES1788 (aim: detect ES1887)
- * if successful we're ready
- * - Dunno. Must be 1688. Will do in general
- *
- * This is the most BETA part of the software: Will the detection
- * always work?
- */
- devc->model = MDL_ESS;
- devc->submodel = ess_minor & 0x0f;
-
- if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) {
- char *chip = NULL;
- int submodel = -1;
-
- switch (devc->sbmo.esstype) {
- case ESSTYPE_DETECT:
- case ESSTYPE_LIKE20:
- break;
- case 688:
- submodel = 0x00;
- break;
- case 1688:
- submodel = 0x08;
- break;
- case 1868:
- submodel = SUBMDL_ES1868;
- break;
- case 1869:
- submodel = SUBMDL_ES1869;
- break;
- case 1788:
- submodel = SUBMDL_ES1788;
- break;
- case 1878:
- submodel = SUBMDL_ES1878;
- break;
- case 1879:
- submodel = SUBMDL_ES1879;
- break;
- case 1887:
- submodel = SUBMDL_ES1887;
- break;
- case 1888:
- submodel = SUBMDL_ES1888;
- break;
- default:
- printk (KERN_ERR "Invalid esstype=%d specified\n", devc->sbmo.esstype);
- return 0;
- };
- if (submodel != -1) {
- devc->submodel = submodel;
- sprintf (modelname, "ES%d", devc->sbmo.esstype);
- chip = modelname;
- };
- if (chip == NULL && (ess_minor & 0x0f) < 8) {
- chip = "ES688";
- };
-#ifdef FKS_TEST
-FKS_test (devc);
-#endif
- /*
- * If Nothing detected yet, and we want 2.0 behaviour...
- * Then let's assume it's ES1688.
- */
- if (chip == NULL && devc->sbmo.esstype == ESSTYPE_LIKE20) {
- chip = "ES1688";
- };
-
- if (chip == NULL) {
- int type;
-
- type = ess_identify (devc);
-
- switch (type) {
- case 0x1868:
- chip = "ES1868";
- devc->submodel = SUBMDL_ES1868;
- break;
- case 0x1869:
- chip = "ES1869";
- devc->submodel = SUBMDL_ES1869;
- break;
- case 0x1878:
- chip = "ES1878";
- devc->submodel = SUBMDL_ES1878;
- break;
- case 0x1879:
- chip = "ES1879";
- devc->submodel = SUBMDL_ES1879;
- break;
- default:
- if ((type & 0x00ff) != ((type >> 8) & 0x00ff)) {
- printk ("ess_init: Unrecognized %04x\n", type);
- }
- };
- };
-#if 0
- /*
- * this one failed:
- * the probing of bit 4 is another thought: from ES1788 and up, all
- * chips seem to have hardware volume control. Bit 4 is readonly to
- * check if a hardware volume interrupt has fired.
- * Cause ES688/ES1688 don't have this feature, bit 4 might be writeable
- * for these chips.
- */
- if (chip == NULL && !ess_probe(devc, 0x64, (1 << 4))) {
-#endif
- /*
- * the probing of bit 2 is my idea. The ES1887 docs want me to probe
- * bit 3. This results in ES1688 being detected as ES1788.
- * Bit 2 is for "Enable HWV IRQE", but as ES(1)688 chips don't have
- * HardWare Volume, I think they don't have this IRQE.
- */
- if (chip == NULL && ess_probe(devc, 0x64, (1 << 2))) {
- if (ess_probe (devc, 0x70, 0x7f)) {
- if (ess_probe (devc, 0x64, (1 << 5))) {
- chip = "ES1887";
- devc->submodel = SUBMDL_ES1887;
- } else {
- chip = "ES1888";
- devc->submodel = SUBMDL_ES1888;
- }
- } else {
- chip = "ES1788";
- devc->submodel = SUBMDL_ES1788;
- }
- };
- if (chip == NULL) {
- chip = "ES1688";
- };
-
- printk ( KERN_INFO "ESS chip %s %s%s\n"
- , chip
- , ( devc->sbmo.esstype == ESSTYPE_DETECT || devc->sbmo.esstype == ESSTYPE_LIKE20
- ? "detected"
- : "specified"
- )
- , ( devc->sbmo.esstype == ESSTYPE_LIKE20
- ? " (kernel 2.0 compatible)"
- : ""
- )
- );
-
- sprintf(name,"ESS %s AudioDrive (rev %d)", chip, ess_minor & 0x0f);
- } else {
- strcpy(name, "Jazz16");
- }
-
- /* AAS: info stolen from ALSA: these boards have different clocks */
- switch(devc->submodel) {
-/* APPARENTLY NOT 1869 AND 1887
- case SUBMDL_ES1869:
- case SUBMDL_ES1887:
-*/
- case SUBMDL_ES1888:
- devc->caps |= SB_CAP_ES18XX_RATE;
- break;
- }
-
- hw_config->name = name;
- /* FKS: sb_dsp_reset to enable extended mode???? */
- sb_dsp_reset(devc); /* Turn on extended mode */
-
- /*
- * Enable joystick and OPL3
- */
- cfg = ess_getmixer (devc, 0x40);
- ess_setmixer (devc, 0x40, cfg | 0x03);
- if (devc->submodel >= 8) { /* ES1688 */
- devc->caps |= SB_NO_MIDI; /* ES1688 uses MPU401 MIDI mode */
- }
- sb_dsp_reset (devc);
-
- /*
- * This is important! If it's not done, the IRQ probe in sb_dsp_init
- * may fail.
- */
- return ess_set_irq_hw (devc);
-}
-
-static int ess_set_dma_hw(sb_devc * devc)
-{
- unsigned char cfg, dma_bits = 0, dma16_bits;
- int dma;
-
-#ifdef FKS_LOGGING
-printk(KERN_INFO "ess_set_dma_hw: dma8=%d,dma16=%d,dup=%d\n"
-, devc->dma8, devc->dma16, devc->duplex);
-#endif
-
- /*
- * FKS: It seems as if this duplex flag isn't set yet. Check it.
- */
- dma = devc->dma8;
-
- if (dma > 3 || dma < 0 || dma == 2) {
- dma_bits = 0;
- printk(KERN_ERR "ESS1688: Invalid DMA8 %d\n", dma);
- return 0;
- } else {
- /* Extended mode DMA enable */
- cfg = 0x50;
-
- if (dma == 3) {
- dma_bits = 3;
- } else {
- dma_bits = dma + 1;
- }
- }
-
- if (!ess_write (devc, 0xb2, cfg | (dma_bits << 2))) {
- printk(KERN_ERR "ESS1688: Failed to write to DMA config register\n");
- return 0;
- }
-
- if (devc->duplex) {
- dma = devc->dma16;
- dma16_bits = 0;
-
- if (dma >= 0) {
- switch (dma) {
- case 0:
- dma_bits = 0x04;
- break;
- case 1:
- dma_bits = 0x05;
- break;
- case 3:
- dma_bits = 0x06;
- break;
- case 5:
- dma_bits = 0x07;
- dma16_bits = 0x20;
- break;
- default:
- printk(KERN_ERR "ESS1887: Invalid DMA16 %d\n", dma);
- return 0;
- };
- ess_chgmixer (devc, 0x78, 0x20, dma16_bits);
- ess_chgmixer (devc, 0x7d, 0x07, dma_bits);
- }
- }
- return 1;
-}
-
-/*
- * This one is called from sb_dsp_init.
- *
- * Return values:
- * 0: Failed
- * 1: Succeeded or doesn't apply (not SUBMDL_ES1887)
- */
-int ess_dsp_init (sb_devc *devc, struct address_info *hw_config)
-{
- /*
- * Caller also checks this, but anyway
- */
- if (devc->model != MDL_ESS) {
- printk (KERN_INFO "ess_dsp_init for non ESS chip\n");
- return 1;
- }
- /*
- * This for ES1887 to run Full Duplex. Actually ES1888
- * is allowed to do so too. I have no idea yet if this
- * will work for ES1888 however.
- *
- * For SB16 having both dma8 and dma16 means enable
- * Full Duplex. Let's try this for ES1887 too
- *
- */
- if (devc->submodel == SUBMDL_ES1887) {
- if (hw_config->dma2 != -1) {
- devc->dma16 = hw_config->dma2;
- }
- /*
- * devc->duplex initialization is put here, cause
- * ess_set_dma_hw needs it.
- */
- if (devc->dma8 != devc->dma16 && devc->dma16 != -1) {
- devc->duplex = 1;
- }
- }
- if (!ess_set_dma_hw (devc)) {
- free_irq(devc->irq, devc);
- return 0;
- }
- return 1;
-}
-
-/****************************************************************************
- * *
- * ESS mixer *
- * *
- ****************************************************************************/
-
-#define ES688_RECORDING_DEVICES \
- ( SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD )
-#define ES688_MIXER_DEVICES \
- ( SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE \
- | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME \
- | SOUND_MASK_LINE2 | SOUND_MASK_SPEAKER )
-
-#define ES1688_RECORDING_DEVICES \
- ( ES688_RECORDING_DEVICES )
-#define ES1688_MIXER_DEVICES \
- ( ES688_MIXER_DEVICES | SOUND_MASK_RECLEV )
-
-#define ES1887_RECORDING_DEVICES \
- ( ES1688_RECORDING_DEVICES | SOUND_MASK_LINE2 | SOUND_MASK_SYNTH)
-#define ES1887_MIXER_DEVICES \
- ( ES1688_MIXER_DEVICES )
-
-/*
- * Mixer registers of ES1887
- *
- * These registers specifically take care of recording levels. To make the
- * mapping from playback devices to recording devices every recording
- * devices = playback device + ES_REC_MIXER_RECDIFF
- */
-#define ES_REC_MIXER_RECBASE (SOUND_MIXER_LINE3 + 1)
-#define ES_REC_MIXER_RECDIFF (ES_REC_MIXER_RECBASE - SOUND_MIXER_SYNTH)
-
-#define ES_REC_MIXER_RECSYNTH (SOUND_MIXER_SYNTH + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECPCM (SOUND_MIXER_PCM + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECSPEAKER (SOUND_MIXER_SPEAKER + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECLINE (SOUND_MIXER_LINE + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECMIC (SOUND_MIXER_MIC + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECCD (SOUND_MIXER_CD + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECIMIX (SOUND_MIXER_IMIX + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECALTPCM (SOUND_MIXER_ALTPCM + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECRECLEV (SOUND_MIXER_RECLEV + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECIGAIN (SOUND_MIXER_IGAIN + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECOGAIN (SOUND_MIXER_OGAIN + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECLINE1 (SOUND_MIXER_LINE1 + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECLINE2 (SOUND_MIXER_LINE2 + ES_REC_MIXER_RECDIFF)
-#define ES_REC_MIXER_RECLINE3 (SOUND_MIXER_LINE3 + ES_REC_MIXER_RECDIFF)
-
-static mixer_tab es688_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME, 0x32, 7, 4, 0x32, 3, 4),
-MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4),
-MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4),
-MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0)
-};
-
-/*
- * The ES1688 specifics... hopefully correct...
- * - 6 bit master volume
- * I was wrong, ES1888 docs say ES1688 didn't have it.
- * - RECLEV control
- * These may apply to ES688 too. I have no idea.
- */
-static mixer_tab es1688_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME, 0x32, 7, 4, 0x32, 3, 4),
-MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4),
-MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4),
-MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4),
-MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0)
-};
-
-static mixer_tab es1688later_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME, 0x60, 5, 6, 0x62, 5, 6),
-MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4),
-MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4),
-MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4),
-MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0)
-};
-
-/*
- * This one is for all ESS chips with a record mixer.
- * It's not used (yet) however
- */
-static mixer_tab es_rec_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME, 0x60, 5, 6, 0x62, 5, 6),
-MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4),
-MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4),
-MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4),
-MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECSYNTH, 0x6b, 7, 4, 0x6b, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECSPEAKER, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECLINE, 0x6e, 7, 4, 0x6e, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECMIC, 0x68, 7, 4, 0x68, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECCD, 0x6a, 7, 4, 0x6a, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECIMIX, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECRECLEV, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECIGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECOGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECLINE1, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECLINE2, 0x6c, 7, 4, 0x6c, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECLINE3, 0x00, 0, 0, 0x00, 0, 0)
-};
-
-/*
- * This one is for ES1887. It's little different from es_rec_mix: it
- * has 0x7c for PCM playback level. This is because ES1887 uses
- * Audio 2 for playback.
- */
-static mixer_tab es1887_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME, 0x60, 5, 6, 0x62, 5, 6),
-MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM, 0x7c, 7, 4, 0x7c, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4),
-MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4),
-MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4),
-MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECSYNTH, 0x6b, 7, 4, 0x6b, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECSPEAKER, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECLINE, 0x6e, 7, 4, 0x6e, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECMIC, 0x68, 7, 4, 0x68, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECCD, 0x6a, 7, 4, 0x6a, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECIMIX, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECRECLEV, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECIGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECOGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECLINE1, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(ES_REC_MIXER_RECLINE2, 0x6c, 7, 4, 0x6c, 3, 4),
-MIX_ENT(ES_REC_MIXER_RECLINE3, 0x00, 0, 0, 0x00, 0, 0)
-};
-
-static int ess_has_rec_mixer (int submodel)
-{
- switch (submodel) {
- case SUBMDL_ES1887:
- return 1;
- default:
- return 0;
- };
-};
-
-#ifdef FKS_LOGGING
-static int ess_mixer_mon_regs[]
- = { 0x70, 0x71, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7d, 0x7f
- , 0xa1, 0xa2, 0xa4, 0xa5, 0xa8, 0xa9
- , 0xb1, 0xb2, 0xb4, 0xb5, 0xb6, 0xb7, 0xb9
- , 0x00};
-
-static void ess_show_mixerregs (sb_devc *devc)
-{
- int *mp = ess_mixer_mon_regs;
-
-return;
-
- while (*mp != 0) {
- printk (KERN_INFO "res (%x)=%x\n", *mp, (int)(ess_getmixer (devc, *mp)));
- mp++;
- }
-}
-#endif
-
-void ess_setmixer (sb_devc * devc, unsigned int port, unsigned int value)
-{
- unsigned long flags;
-
-#ifdef FKS_LOGGING
-printk(KERN_INFO "FKS: write mixer %x: %x\n", port, value);
-#endif
-
- spin_lock_irqsave(&devc->lock, flags);
- if (port >= 0xa0) {
- ess_write (devc, port, value);
- } else {
- outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
-
- udelay(20);
- outb(((unsigned char) (value & 0xff)), MIXER_DATA);
- udelay(20);
- };
- spin_unlock_irqrestore(&devc->lock, flags);
-}
-
-unsigned int ess_getmixer (sb_devc * devc, unsigned int port)
-{
- unsigned int val;
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock, flags);
-
- if (port >= 0xa0) {
- val = ess_read (devc, port);
- } else {
- outb(((unsigned char) (port & 0xff)), MIXER_ADDR);
-
- udelay(20);
- val = inb(MIXER_DATA);
- udelay(20);
- }
- spin_unlock_irqrestore(&devc->lock, flags);
-
- return val;
-}
-
-static void ess_chgmixer
- (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val)
-{
- int value;
-
- value = ess_getmixer (devc, reg);
- value = (value & ~mask) | (val & mask);
- ess_setmixer (devc, reg, value);
-}
-
-/*
- * ess_mixer_init must be called from sb_mixer_init
- */
-void ess_mixer_init (sb_devc * devc)
-{
- devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
-
- /*
- * Take care of ES1887 specifics...
- */
- switch (devc->submodel) {
- case SUBMDL_ES1887:
- devc->supported_devices = ES1887_MIXER_DEVICES;
- devc->supported_rec_devices = ES1887_RECORDING_DEVICES;
-#ifdef FKS_LOGGING
-printk (KERN_INFO "FKS: ess_mixer_init dup = %d\n", devc->duplex);
-#endif
- if (devc->duplex) {
- devc->iomap = &es1887_mix;
- devc->iomap_sz = ARRAY_SIZE(es1887_mix);
- } else {
- devc->iomap = &es_rec_mix;
- devc->iomap_sz = ARRAY_SIZE(es_rec_mix);
- }
- break;
- default:
- if (devc->submodel < 8) {
- devc->supported_devices = ES688_MIXER_DEVICES;
- devc->supported_rec_devices = ES688_RECORDING_DEVICES;
- devc->iomap = &es688_mix;
- devc->iomap_sz = ARRAY_SIZE(es688_mix);
- } else {
- /*
- * es1688 has 4 bits master vol.
- * later chips have 6 bits (?)
- */
- devc->supported_devices = ES1688_MIXER_DEVICES;
- devc->supported_rec_devices = ES1688_RECORDING_DEVICES;
- if (devc->submodel < 0x10) {
- devc->iomap = &es1688_mix;
- devc->iomap_sz = ARRAY_SIZE(es688_mix);
- } else {
- devc->iomap = &es1688later_mix;
- devc->iomap_sz = ARRAY_SIZE(es1688later_mix);
- }
- }
- }
-}
-
-/*
- * Changing playback levels at an ESS chip with record mixer means having to
- * take care of recording levels of recorded inputs (devc->recmask) too!
- */
-int ess_mixer_set(sb_devc *devc, int dev, int left, int right)
-{
- if (ess_has_rec_mixer (devc->submodel) && (devc->recmask & (1 << dev))) {
- sb_common_mixer_set (devc, dev + ES_REC_MIXER_RECDIFF, left, right);
- }
- return sb_common_mixer_set (devc, dev, left, right);
-}
-
-/*
- * After a sb_dsp_reset extended register 0xb4 (RECLEV) is reset too. After
- * sb_dsp_reset RECLEV has to be restored. This is where ess_mixer_reload
- * helps.
- */
-void ess_mixer_reload (sb_devc *devc, int dev)
-{
- int left, right, value;
-
- value = devc->levels[dev];
- left = value & 0x000000ff;
- right = (value & 0x0000ff00) >> 8;
-
- sb_common_mixer_set(devc, dev, left, right);
-}
-
-static int es_rec_set_recmask(sb_devc * devc, int mask)
-{
- int i, i_mask, cur_mask, diff_mask;
- int value, left, right;
-
-#ifdef FKS_LOGGING
-printk (KERN_INFO "FKS: es_rec_set_recmask mask = %x\n", mask);
-#endif
- /*
- * Changing the recmask on an ESS chip with recording mixer means:
- * (1) Find the differences
- * (2) For "turned-on" inputs: make the recording level the playback level
- * (3) For "turned-off" inputs: make the recording level zero
- */
- cur_mask = devc->recmask;
- diff_mask = (cur_mask ^ mask);
-
- for (i = 0; i < 32; i++) {
- i_mask = (1 << i);
- if (diff_mask & i_mask) { /* Difference? (1) */
- if (mask & i_mask) { /* Turn it on (2) */
- value = devc->levels[i];
- left = value & 0x000000ff;
- right = (value & 0x0000ff00) >> 8;
- } else { /* Turn it off (3) */
- left = 0;
- right = 0;
- }
- sb_common_mixer_set(devc, i + ES_REC_MIXER_RECDIFF, left, right);
- }
- }
- return mask;
-}
-
-int ess_set_recmask(sb_devc * devc, int *mask)
-{
- /* This applies to ESS chips with record mixers only! */
-
- if (ess_has_rec_mixer (devc->submodel)) {
- *mask = es_rec_set_recmask (devc, *mask);
- return 1; /* Applied */
- } else {
- return 0; /* Not applied */
- }
-}
-
-/*
- * ess_mixer_reset must be called from sb_mixer_reset
- */
-int ess_mixer_reset (sb_devc * devc)
-{
- /*
- * Separate actions for ESS chips with a record mixer:
- */
- if (ess_has_rec_mixer (devc->submodel)) {
- switch (devc->submodel) {
- case SUBMDL_ES1887:
- /*
- * Separate actions for ES1887:
- * Change registers 7a and 1c to make the record mixer the
- * actual recording source.
- */
- ess_chgmixer(devc, 0x7a, 0x18, 0x08);
- ess_chgmixer(devc, 0x1c, 0x07, 0x07);
- break;
- };
- /*
- * Call set_recmask for proper initialization
- */
- devc->recmask = devc->supported_rec_devices;
- es_rec_set_recmask(devc, 0);
- devc->recmask = 0;
-
- return 1; /* We took care of recmask. */
- } else {
- return 0; /* We didn't take care; caller do it */
- }
-}
-
-/****************************************************************************
- * *
- * ESS midi *
- * *
- ****************************************************************************/
-
-/*
- * FKS: IRQ may be shared. Hm. And if so? Then What?
- */
-int ess_midi_init(sb_devc * devc, struct address_info *hw_config)
-{
- unsigned char cfg, tmp;
-
- cfg = ess_getmixer (devc, 0x40) & 0x03;
-
- if (devc->submodel < 8) {
- ess_setmixer (devc, 0x40, cfg | 0x03); /* Enable OPL3 & joystick */
- return 0; /* ES688 doesn't support MPU401 mode */
- }
- tmp = (hw_config->io_base & 0x0f0) >> 4;
-
- if (tmp > 3) {
- ess_setmixer (devc, 0x40, cfg);
- return 0;
- }
- cfg |= tmp << 3;
-
- tmp = 1; /* MPU enabled without interrupts */
-
- /* May be shared: if so the value is -ve */
-
- switch (abs(hw_config->irq)) {
- case 9:
- tmp = 0x4;
- break;
- case 5:
- tmp = 0x5;
- break;
- case 7:
- tmp = 0x6;
- break;
- case 10:
- tmp = 0x7;
- break;
- default:
- return 0;
- }
-
- cfg |= tmp << 5;
- ess_setmixer (devc, 0x40, cfg | 0x03);
-
- return 1;
-}
-
diff --git a/sound/oss/sb_ess.h b/sound/oss/sb_ess.h
deleted file mode 100644
index 38aa072e01f2..000000000000
--- a/sound/oss/sb_ess.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Created: 9-Jan-1999 Rolf Fokkens
- */
-
-extern void ess_intr
- (sb_devc *devc);
-extern int ess_dsp_init
- (sb_devc *devc, struct address_info *hw_config);
-
-extern struct audio_driver *ess_audio_init
- (sb_devc *devc, int *audio_flags, int *format_mask);
-extern int ess_midi_init
- (sb_devc *devc, struct address_info *hw_config);
-extern void ess_mixer_init
- (sb_devc *devc);
-
-extern int ess_init
- (sb_devc *devc, struct address_info *hw_config);
-extern int ess_dsp_reset
- (sb_devc *devc);
-
-extern void ess_setmixer
- (sb_devc *devc, unsigned int port, unsigned int value);
-extern unsigned int ess_getmixer
- (sb_devc *devc, unsigned int port);
-extern int ess_mixer_set
- (sb_devc *devc, int dev, int left, int right);
-extern int ess_mixer_reset
- (sb_devc *devc);
-extern void ess_mixer_reload
- (sb_devc * devc, int dev);
-extern int ess_set_recmask
- (sb_devc *devc, int *mask);
-
diff --git a/sound/oss/sb_midi.c b/sound/oss/sb_midi.c
deleted file mode 100644
index f139028e85c0..000000000000
--- a/sound/oss/sb_midi.c
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * sound/oss/sb_midi.c
- *
- * The low level driver for the Sound Blaster DS chips.
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-
-#include "sound_config.h"
-
-#include "sb.h"
-#undef SB_TEST_IRQ
-
-/*
- * The DSP channel can be used either for input or output. Variable
- * 'sb_irq_mode' will be set when the program calls read or write first time
- * after open. Current version doesn't support mode changes without closing
- * and reopening the device. Support for this feature may be implemented in a
- * future version of this driver.
- */
-
-
-static int sb_midi_open(int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
-)
-{
- sb_devc *devc = midi_devs[dev]->devc;
- unsigned long flags;
-
- if (devc == NULL)
- return -ENXIO;
-
- spin_lock_irqsave(&devc->lock, flags);
- if (devc->opened)
- {
- spin_unlock_irqrestore(&devc->lock, flags);
- return -EBUSY;
- }
- devc->opened = 1;
- spin_unlock_irqrestore(&devc->lock, flags);
-
- devc->irq_mode = IMODE_MIDI;
- devc->midi_broken = 0;
-
- sb_dsp_reset(devc);
-
- if (!sb_dsp_command(devc, 0x35)) /* Start MIDI UART mode */
- {
- devc->opened = 0;
- return -EIO;
- }
- devc->intr_active = 1;
-
- if (mode & OPEN_READ)
- {
- devc->input_opened = 1;
- devc->midi_input_intr = input;
- }
- return 0;
-}
-
-static void sb_midi_close(int dev)
-{
- sb_devc *devc = midi_devs[dev]->devc;
- unsigned long flags;
-
- if (devc == NULL)
- return;
-
- spin_lock_irqsave(&devc->lock, flags);
- sb_dsp_reset(devc);
- devc->intr_active = 0;
- devc->input_opened = 0;
- devc->opened = 0;
- spin_unlock_irqrestore(&devc->lock, flags);
-}
-
-static int sb_midi_out(int dev, unsigned char midi_byte)
-{
- sb_devc *devc = midi_devs[dev]->devc;
-
- if (devc == NULL)
- return 1;
-
- if (devc->midi_broken)
- return 1;
-
- if (!sb_dsp_command(devc, midi_byte))
- {
- devc->midi_broken = 1;
- return 1;
- }
- return 1;
-}
-
-static int sb_midi_start_read(int dev)
-{
- return 0;
-}
-
-static int sb_midi_end_read(int dev)
-{
- sb_devc *devc = midi_devs[dev]->devc;
-
- if (devc == NULL)
- return -ENXIO;
-
- sb_dsp_reset(devc);
- devc->intr_active = 0;
- return 0;
-}
-
-static int sb_midi_ioctl(int dev, unsigned cmd, void __user *arg)
-{
- return -EINVAL;
-}
-
-void sb_midi_interrupt(sb_devc * devc)
-{
- unsigned long flags;
- unsigned char data;
-
- if (devc == NULL)
- return;
-
- spin_lock_irqsave(&devc->lock, flags);
-
- data = inb(DSP_READ);
- if (devc->input_opened)
- devc->midi_input_intr(devc->my_mididev, data);
-
- spin_unlock_irqrestore(&devc->lock, flags);
-}
-
-#define MIDI_SYNTH_NAME "Sound Blaster Midi"
-#define MIDI_SYNTH_CAPS 0
-#include "midi_synth.h"
-
-static struct midi_operations sb_midi_operations =
-{
- .owner = THIS_MODULE,
- .info = {"Sound Blaster", 0, 0, SNDCARD_SB},
- .converter = &std_midi_synth,
- .in_info = {0},
- .open = sb_midi_open,
- .close = sb_midi_close,
- .ioctl = sb_midi_ioctl,
- .outputc = sb_midi_out,
- .start_read = sb_midi_start_read,
- .end_read = sb_midi_end_read,
-};
-
-void sb_dsp_midi_init(sb_devc * devc, struct module *owner)
-{
- int dev;
-
- if (devc->model < 2) /* No MIDI support for SB 1.x */
- return;
-
- dev = sound_alloc_mididev();
-
- if (dev == -1)
- {
- printk(KERN_ERR "sb_midi: too many MIDI devices detected\n");
- return;
- }
- std_midi_synth.midi_dev = devc->my_mididev = dev;
- midi_devs[dev] = kmalloc(sizeof(struct midi_operations), GFP_KERNEL);
- if (midi_devs[dev] == NULL)
- {
- printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n");
- sound_unload_mididev(dev);
- return;
- }
- memcpy((char *) midi_devs[dev], (char *) &sb_midi_operations,
- sizeof(struct midi_operations));
-
- if (owner)
- midi_devs[dev]->owner = owner;
-
- midi_devs[dev]->devc = devc;
-
-
- midi_devs[dev]->converter = kmalloc(sizeof(struct synth_operations), GFP_KERNEL);
- if (midi_devs[dev]->converter == NULL)
- {
- printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n");
- kfree(midi_devs[dev]);
- sound_unload_mididev(dev);
- return;
- }
- memcpy((char *) midi_devs[dev]->converter, (char *) &std_midi_synth,
- sizeof(struct synth_operations));
-
- midi_devs[dev]->converter->id = "SBMIDI";
- sequencer_init();
-}
diff --git a/sound/oss/sb_mixer.c b/sound/oss/sb_mixer.c
deleted file mode 100644
index 2039d31b7e22..000000000000
--- a/sound/oss/sb_mixer.c
+++ /dev/null
@@ -1,770 +0,0 @@
-/*
- * sound/oss/sb_mixer.c
- *
- * The low level mixer driver for the Sound Blaster compatible cards.
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * Rolf Fokkens (Dec 20 1998) : Moved ESS stuff into sb_ess.[ch]
- * Stanislav Voronyi <stas@esc.kharkov.com> : Support for AWE 3DSE device (Jun 7 1999)
- */
-
-#include <linux/slab.h>
-
-#include "sound_config.h"
-
-#define __SB_MIXER_C__
-
-#include "sb.h"
-#include "sb_mixer.h"
-
-#include "sb_ess.h"
-
-#define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
-
-/* Same as SB Pro, unless I find otherwise */
-#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
-
-#define SBPRO_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD | SOUND_MASK_VOLUME)
-
-/* SG NX Pro has treble and bass settings on the mixer. The 'speaker'
- * channel is the COVOX/DisneySoundSource emulation volume control
- * on the mixer. It does NOT control speaker volume. Should have own
- * mask eventually?
- */
-#define SGNXPRO_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \
- SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER )
-
-#define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD)
-
-#define SB16_OUTFILTER_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD)
-
-#define SB16_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
- SOUND_MASK_CD | \
- SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \
- SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | \
- SOUND_MASK_IMIX)
-
-/* These are the only devices that are working at the moment. Others could
- * be added once they are identified and a method is found to control them.
- */
-#define ALS007_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | \
- SOUND_MASK_PCM | SOUND_MASK_MIC | \
- SOUND_MASK_CD | \
- SOUND_MASK_VOLUME)
-
-static mixer_tab sbpro_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
-MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0)
-};
-
-static mixer_tab sb16_mix = {
-MIX_ENT(SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5),
-MIX_ENT(SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4),
-MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 7, 4, 0x45, 7, 4),
-MIX_ENT(SOUND_MIXER_SYNTH, 0x34, 7, 5, 0x35, 7, 5),
-MIX_ENT(SOUND_MIXER_PCM, 0x32, 7, 5, 0x33, 7, 5),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 7, 2, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE, 0x38, 7, 5, 0x39, 7, 5),
-MIX_ENT(SOUND_MIXER_MIC, 0x3a, 7, 5, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_CD, 0x36, 7, 5, 0x37, 7, 5),
-MIX_ENT(SOUND_MIXER_IMIX, 0x3c, 0, 1, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 7, 2, 0x40, 7, 2), /* Obsolete. Use IGAIN */
-MIX_ENT(SOUND_MIXER_IGAIN, 0x3f, 7, 2, 0x40, 7, 2),
-MIX_ENT(SOUND_MIXER_OGAIN, 0x41, 7, 2, 0x42, 7, 2)
-};
-
-static mixer_tab als007_mix =
-{
-MIX_ENT(SOUND_MIXER_VOLUME, 0x62, 7, 4, 0x62, 3, 4),
-MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_SYNTH, 0x66, 7, 4, 0x66, 3, 4),
-MIX_ENT(SOUND_MIXER_PCM, 0x64, 7, 4, 0x64, 3, 4),
-MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_LINE, 0x6e, 7, 4, 0x6e, 3, 4),
-MIX_ENT(SOUND_MIXER_MIC, 0x6a, 2, 3, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_CD, 0x68, 7, 4, 0x68, 3, 4),
-MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0), /* Obsolete. Use IGAIN */
-MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
-MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0)
-};
-
-
-/* SM_GAMES Master volume is lower and PCM & FM volumes
- higher than with SB Pro. This improves the
- sound quality */
-
-static int smg_default_levels[32] =
-{
- 0x2020, /* Master Volume */
- 0x4b4b, /* Bass */
- 0x4b4b, /* Treble */
- 0x6464, /* FM */
- 0x6464, /* PCM */
- 0x4b4b, /* PC Speaker */
- 0x4b4b, /* Ext Line */
- 0x0000, /* Mic */
- 0x4b4b, /* CD */
- 0x4b4b, /* Recording monitor */
- 0x4b4b, /* SB PCM */
- 0x4b4b, /* Recording level */
- 0x4b4b, /* Input gain */
- 0x4b4b, /* Output gain */
- 0x4040, /* Line1 */
- 0x4040, /* Line2 */
- 0x1515 /* Line3 */
-};
-
-static int sb_default_levels[32] =
-{
- 0x5a5a, /* Master Volume */
- 0x4b4b, /* Bass */
- 0x4b4b, /* Treble */
- 0x4b4b, /* FM */
- 0x4b4b, /* PCM */
- 0x4b4b, /* PC Speaker */
- 0x4b4b, /* Ext Line */
- 0x1010, /* Mic */
- 0x4b4b, /* CD */
- 0x0000, /* Recording monitor */
- 0x4b4b, /* SB PCM */
- 0x4b4b, /* Recording level */
- 0x4b4b, /* Input gain */
- 0x4b4b, /* Output gain */
- 0x4040, /* Line1 */
- 0x4040, /* Line2 */
- 0x1515 /* Line3 */
-};
-
-static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] =
-{
- 0x00, /* SOUND_MIXER_VOLUME */
- 0x00, /* SOUND_MIXER_BASS */
- 0x00, /* SOUND_MIXER_TREBLE */
- 0x40, /* SOUND_MIXER_SYNTH */
- 0x00, /* SOUND_MIXER_PCM */
- 0x00, /* SOUND_MIXER_SPEAKER */
- 0x10, /* SOUND_MIXER_LINE */
- 0x01, /* SOUND_MIXER_MIC */
- 0x04, /* SOUND_MIXER_CD */
- 0x00, /* SOUND_MIXER_IMIX */
- 0x00, /* SOUND_MIXER_ALTPCM */
- 0x00, /* SOUND_MIXER_RECLEV */
- 0x00, /* SOUND_MIXER_IGAIN */
- 0x00 /* SOUND_MIXER_OGAIN */
-};
-
-static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] =
-{
- 0x00, /* SOUND_MIXER_VOLUME */
- 0x00, /* SOUND_MIXER_BASS */
- 0x00, /* SOUND_MIXER_TREBLE */
- 0x20, /* SOUND_MIXER_SYNTH */
- 0x00, /* SOUND_MIXER_PCM */
- 0x00, /* SOUND_MIXER_SPEAKER */
- 0x08, /* SOUND_MIXER_LINE */
- 0x01, /* SOUND_MIXER_MIC */
- 0x02, /* SOUND_MIXER_CD */
- 0x00, /* SOUND_MIXER_IMIX */
- 0x00, /* SOUND_MIXER_ALTPCM */
- 0x00, /* SOUND_MIXER_RECLEV */
- 0x00, /* SOUND_MIXER_IGAIN */
- 0x00 /* SOUND_MIXER_OGAIN */
-};
-
-static char smw_mix_regs[] = /* Left mixer registers */
-{
- 0x0b, /* SOUND_MIXER_VOLUME */
- 0x0d, /* SOUND_MIXER_BASS */
- 0x0d, /* SOUND_MIXER_TREBLE */
- 0x05, /* SOUND_MIXER_SYNTH */
- 0x09, /* SOUND_MIXER_PCM */
- 0x00, /* SOUND_MIXER_SPEAKER */
- 0x03, /* SOUND_MIXER_LINE */
- 0x01, /* SOUND_MIXER_MIC */
- 0x07, /* SOUND_MIXER_CD */
- 0x00, /* SOUND_MIXER_IMIX */
- 0x00, /* SOUND_MIXER_ALTPCM */
- 0x00, /* SOUND_MIXER_RECLEV */
- 0x00, /* SOUND_MIXER_IGAIN */
- 0x00, /* SOUND_MIXER_OGAIN */
- 0x00, /* SOUND_MIXER_LINE1 */
- 0x00, /* SOUND_MIXER_LINE2 */
- 0x00 /* SOUND_MIXER_LINE3 */
-};
-
-static int sbmixnum = 1;
-
-static void sb_mixer_reset(sb_devc * devc);
-
-void sb_mixer_set_stereo(sb_devc * devc, int mode)
-{
- sb_chgmixer(devc, OUT_FILTER, STEREO_DAC, (mode ? STEREO_DAC : MONO_DAC));
-}
-
-static int detect_mixer(sb_devc * devc)
-{
- /* Just trust the mixer is there */
- return 1;
-}
-
-static void change_bits(sb_devc * devc, unsigned char *regval, int dev, int chn, int newval)
-{
- unsigned char mask;
- int shift;
-
- mask = (1 << (*devc->iomap)[dev][chn].nbits) - 1;
- newval = (int) ((newval * mask) + 50) / 100; /* Scale */
-
- shift = (*devc->iomap)[dev][chn].bitoffs - (*devc->iomap)[dev][LEFT_CHN].nbits + 1;
-
- *regval &= ~(mask << shift); /* Mask out previous value */
- *regval |= (newval & mask) << shift; /* Set the new value */
-}
-
-static int sb_mixer_get(sb_devc * devc, int dev)
-{
- if (!((1 << dev) & devc->supported_devices))
- return -EINVAL;
- return devc->levels[dev];
-}
-
-void smw_mixer_init(sb_devc * devc)
-{
- int i;
-
- sb_setmixer(devc, 0x00, 0x18); /* Mute unused (Telephone) line */
- sb_setmixer(devc, 0x10, 0x38); /* Config register 2 */
-
- devc->supported_devices = 0;
- for (i = 0; i < sizeof(smw_mix_regs); i++)
- if (smw_mix_regs[i] != 0)
- devc->supported_devices |= (1 << i);
-
- devc->supported_rec_devices = devc->supported_devices &
- ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_PCM | SOUND_MASK_VOLUME);
- sb_mixer_reset(devc);
-}
-
-int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right)
-{
- int regoffs;
- unsigned char val;
-
- if ((dev < 0) || (dev >= devc->iomap_sz))
- return -EINVAL;
-
- regoffs = (*devc->iomap)[dev][LEFT_CHN].regno;
-
- if (regoffs == 0)
- return -EINVAL;
-
- val = sb_getmixer(devc, regoffs);
- change_bits(devc, &val, dev, LEFT_CHN, left);
-
- if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs) /*
- * Change register
- */
- {
- sb_setmixer(devc, regoffs, val); /*
- * Save the old one
- */
- regoffs = (*devc->iomap)[dev][RIGHT_CHN].regno;
-
- if (regoffs == 0)
- return left | (left << 8); /*
- * Just left channel present
- */
-
- val = sb_getmixer(devc, regoffs); /*
- * Read the new one
- */
- }
- change_bits(devc, &val, dev, RIGHT_CHN, right);
-
- sb_setmixer(devc, regoffs, val);
-
- return left | (right << 8);
-}
-
-static int smw_mixer_set(sb_devc * devc, int dev, int left, int right)
-{
- int reg, val;
-
- switch (dev)
- {
- case SOUND_MIXER_VOLUME:
- sb_setmixer(devc, 0x0b, 96 - (96 * left / 100)); /* 96=mute, 0=max */
- sb_setmixer(devc, 0x0c, 96 - (96 * right / 100));
- break;
-
- case SOUND_MIXER_BASS:
- case SOUND_MIXER_TREBLE:
- devc->levels[dev] = left | (right << 8);
- /* Set left bass and treble values */
- val = ((devc->levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / (unsigned) 100) << 4;
- val |= ((devc->levels[SOUND_MIXER_BASS] & 0xff) * 16 / (unsigned) 100) & 0x0f;
- sb_setmixer(devc, 0x0d, val);
-
- /* Set right bass and treble values */
- val = (((devc->levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / (unsigned) 100) << 4;
- val |= (((devc->levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / (unsigned) 100) & 0x0f;
- sb_setmixer(devc, 0x0e, val);
-
- break;
-
- default:
- /* bounds check */
- if (dev < 0 || dev >= ARRAY_SIZE(smw_mix_regs))
- return -EINVAL;
- reg = smw_mix_regs[dev];
- if (reg == 0)
- return -EINVAL;
- sb_setmixer(devc, reg, (24 - (24 * left / 100)) | 0x20); /* 24=mute, 0=max */
- sb_setmixer(devc, reg + 1, (24 - (24 * right / 100)) | 0x40);
- }
-
- devc->levels[dev] = left | (right << 8);
- return left | (right << 8);
-}
-
-static int sb_mixer_set(sb_devc * devc, int dev, int value)
-{
- int left = value & 0x000000ff;
- int right = (value & 0x0000ff00) >> 8;
- int retval;
-
- if (left > 100)
- left = 100;
- if (right > 100)
- right = 100;
-
- if ((dev < 0) || (dev > 31))
- return -EINVAL;
-
- if (!(devc->supported_devices & (1 << dev))) /*
- * Not supported
- */
- return -EINVAL;
-
- /* Differentiate depending on the chipsets */
- switch (devc->model) {
- case MDL_SMW:
- retval = smw_mixer_set(devc, dev, left, right);
- break;
- case MDL_ESS:
- retval = ess_mixer_set(devc, dev, left, right);
- break;
- default:
- retval = sb_common_mixer_set(devc, dev, left, right);
- }
- if (retval >= 0) devc->levels[dev] = retval;
-
- return retval;
-}
-
-/*
- * set_recsrc doesn't apply to ES188x
- */
-static void set_recsrc(sb_devc * devc, int src)
-{
- sb_setmixer(devc, RECORD_SRC, (sb_getmixer(devc, RECORD_SRC) & ~7) | (src & 0x7));
-}
-
-static int set_recmask(sb_devc * devc, int mask)
-{
- int devmask, i;
- unsigned char regimageL, regimageR;
-
- devmask = mask & devc->supported_rec_devices;
-
- switch (devc->model)
- {
- case MDL_SBPRO:
- case MDL_ESS:
- case MDL_JAZZ:
- case MDL_SMW:
- if (devc->model == MDL_ESS && ess_set_recmask (devc, &devmask)) {
- break;
- };
- if (devmask != SOUND_MASK_MIC &&
- devmask != SOUND_MASK_LINE &&
- devmask != SOUND_MASK_CD)
- {
- /*
- * More than one device selected. Drop the
- * previous selection
- */
- devmask &= ~devc->recmask;
- }
- if (devmask != SOUND_MASK_MIC &&
- devmask != SOUND_MASK_LINE &&
- devmask != SOUND_MASK_CD)
- {
- /*
- * More than one device selected. Default to
- * mic
- */
- devmask = SOUND_MASK_MIC;
- }
- if (devmask ^ devc->recmask) /*
- * Input source changed
- */
- {
- switch (devmask)
- {
- case SOUND_MASK_MIC:
- set_recsrc(devc, SRC__MIC);
- break;
-
- case SOUND_MASK_LINE:
- set_recsrc(devc, SRC__LINE);
- break;
-
- case SOUND_MASK_CD:
- set_recsrc(devc, SRC__CD);
- break;
-
- default:
- set_recsrc(devc, SRC__MIC);
- }
- }
- break;
-
- case MDL_SB16:
- if (!devmask)
- devmask = SOUND_MASK_MIC;
-
- if (devc->submodel == SUBMDL_ALS007)
- {
- switch (devmask)
- {
- case SOUND_MASK_LINE:
- sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_LINE);
- break;
- case SOUND_MASK_CD:
- sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_CD);
- break;
- case SOUND_MASK_SYNTH:
- sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_SYNTH);
- break;
- default: /* Also takes care of SOUND_MASK_MIC case */
- sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_MIC);
- break;
- }
- }
- else
- {
- regimageL = regimageR = 0;
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- {
- if ((1 << i) & devmask)
- {
- regimageL |= sb16_recmasks_L[i];
- regimageR |= sb16_recmasks_R[i];
- }
- sb_setmixer (devc, SB16_IMASK_L, regimageL);
- sb_setmixer (devc, SB16_IMASK_R, regimageR);
- }
- }
- break;
- }
- devc->recmask = devmask;
- return devc->recmask;
-}
-
-static int set_outmask(sb_devc * devc, int mask)
-{
- int devmask, i;
- unsigned char regimage;
-
- devmask = mask & devc->supported_out_devices;
-
- switch (devc->model)
- {
- case MDL_SB16:
- if (devc->submodel == SUBMDL_ALS007)
- break;
- else
- {
- regimage = 0;
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- {
- if ((1 << i) & devmask)
- {
- regimage |= (sb16_recmasks_L[i] | sb16_recmasks_R[i]);
- }
- sb_setmixer (devc, SB16_OMASK, regimage);
- }
- }
- break;
- default:
- break;
- }
-
- devc->outmask = devmask;
- return devc->outmask;
-}
-
-static int sb_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- sb_devc *devc = mixer_devs[dev]->devc;
- int val, ret;
- int __user *p = arg;
-
- /*
- * Use ioctl(fd, SOUND_MIXER_AGC, &mode) to turn AGC off (0) or on (1).
- * Use ioctl(fd, SOUND_MIXER_3DSE, &mode) to turn 3DSE off (0) or on (1)
- * or mode==2 put 3DSE state to mode.
- */
- if (devc->model == MDL_SB16) {
- if (cmd == SOUND_MIXER_AGC)
- {
- if (get_user(val, p))
- return -EFAULT;
- sb_setmixer(devc, 0x43, (~val) & 0x01);
- return 0;
- }
- if (cmd == SOUND_MIXER_3DSE)
- {
- /* I put here 15, but I don't know the exact version.
- At least my 4.13 havn't 3DSE, 4.16 has it. */
- if (devc->minor < 15)
- return -EINVAL;
- if (get_user(val, p))
- return -EFAULT;
- if (val == 0 || val == 1)
- sb_chgmixer(devc, AWE_3DSE, 0x01, val);
- else if (val == 2)
- {
- ret = sb_getmixer(devc, AWE_3DSE)&0x01;
- return put_user(ret, p);
- }
- else
- return -EINVAL;
- return 0;
- }
- }
- if (((cmd >> 8) & 0xff) == 'M')
- {
- if (_SIOC_DIR(cmd) & _SIOC_WRITE)
- {
- if (get_user(val, p))
- return -EFAULT;
- switch (cmd & 0xff)
- {
- case SOUND_MIXER_RECSRC:
- ret = set_recmask(devc, val);
- break;
-
- case SOUND_MIXER_OUTSRC:
- ret = set_outmask(devc, val);
- break;
-
- default:
- ret = sb_mixer_set(devc, cmd & 0xff, val);
- }
- }
- else switch (cmd & 0xff)
- {
- case SOUND_MIXER_RECSRC:
- ret = devc->recmask;
- break;
-
- case SOUND_MIXER_OUTSRC:
- ret = devc->outmask;
- break;
-
- case SOUND_MIXER_DEVMASK:
- ret = devc->supported_devices;
- break;
-
- case SOUND_MIXER_STEREODEVS:
- ret = devc->supported_devices;
- /* The ESS seems to have stereo mic controls */
- if (devc->model == MDL_ESS)
- ret &= ~(SOUND_MASK_SPEAKER|SOUND_MASK_IMIX);
- else if (devc->model != MDL_JAZZ && devc->model != MDL_SMW)
- ret &= ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
- break;
-
- case SOUND_MIXER_RECMASK:
- ret = devc->supported_rec_devices;
- break;
-
- case SOUND_MIXER_OUTMASK:
- ret = devc->supported_out_devices;
- break;
-
- case SOUND_MIXER_CAPS:
- ret = devc->mixer_caps;
- break;
-
- default:
- ret = sb_mixer_get(devc, cmd & 0xff);
- break;
- }
- return put_user(ret, p);
- } else
- return -EINVAL;
-}
-
-static struct mixer_operations sb_mixer_operations =
-{
- .owner = THIS_MODULE,
- .id = "SB",
- .name = "Sound Blaster",
- .ioctl = sb_mixer_ioctl
-};
-
-static struct mixer_operations als007_mixer_operations =
-{
- .owner = THIS_MODULE,
- .id = "ALS007",
- .name = "Avance ALS-007",
- .ioctl = sb_mixer_ioctl
-};
-
-static void sb_mixer_reset(sb_devc * devc)
-{
- char name[32];
- int i;
-
- sprintf(name, "SB_%d", devc->sbmixnum);
-
- if (devc->sbmo.sm_games)
- devc->levels = load_mixer_volumes(name, smg_default_levels, 1);
- else
- devc->levels = load_mixer_volumes(name, sb_default_levels, 1);
-
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- sb_mixer_set(devc, i, devc->levels[i]);
-
- if (devc->model != MDL_ESS || !ess_mixer_reset (devc)) {
- set_recmask(devc, SOUND_MASK_MIC);
- };
-}
-
-int sb_mixer_init(sb_devc * devc, struct module *owner)
-{
- int mixer_type = 0;
- int m;
-
- devc->sbmixnum = sbmixnum++;
- devc->levels = NULL;
-
- sb_setmixer(devc, 0x00, 0); /* Reset mixer */
-
- if (!(mixer_type = detect_mixer(devc)))
- return 0; /* No mixer. Why? */
-
- switch (devc->model)
- {
- case MDL_ESSPCI:
- case MDL_YMPCI:
- case MDL_SBPRO:
- case MDL_AZTECH:
- case MDL_JAZZ:
- devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
- devc->supported_devices = SBPRO_MIXER_DEVICES;
- devc->supported_rec_devices = SBPRO_RECORDING_DEVICES;
- devc->iomap = &sbpro_mix;
- devc->iomap_sz = ARRAY_SIZE(sbpro_mix);
- break;
-
- case MDL_ESS:
- ess_mixer_init (devc);
- break;
-
- case MDL_SMW:
- devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
- devc->supported_devices = 0;
- devc->supported_rec_devices = 0;
- devc->iomap = &sbpro_mix;
- devc->iomap_sz = ARRAY_SIZE(sbpro_mix);
- smw_mixer_init(devc);
- break;
-
- case MDL_SB16:
- devc->mixer_caps = 0;
- devc->supported_rec_devices = SB16_RECORDING_DEVICES;
- devc->supported_out_devices = SB16_OUTFILTER_DEVICES;
- if (devc->submodel != SUBMDL_ALS007)
- {
- devc->supported_devices = SB16_MIXER_DEVICES;
- devc->iomap = &sb16_mix;
- devc->iomap_sz = ARRAY_SIZE(sb16_mix);
- }
- else
- {
- devc->supported_devices = ALS007_MIXER_DEVICES;
- devc->iomap = &als007_mix;
- devc->iomap_sz = ARRAY_SIZE(als007_mix);
- }
- break;
-
- default:
- printk(KERN_WARNING "sb_mixer: Unsupported mixer type %d\n", devc->model);
- return 0;
- }
-
- m = sound_alloc_mixerdev();
- if (m == -1)
- return 0;
-
- mixer_devs[m] = kmalloc(sizeof(struct mixer_operations), GFP_KERNEL);
- if (mixer_devs[m] == NULL)
- {
- printk(KERN_ERR "sb_mixer: Can't allocate memory\n");
- sound_unload_mixerdev(m);
- return 0;
- }
-
- if (devc->submodel != SUBMDL_ALS007)
- memcpy ((char *) mixer_devs[m], (char *) &sb_mixer_operations, sizeof (struct mixer_operations));
- else
- memcpy ((char *) mixer_devs[m], (char *) &als007_mixer_operations, sizeof (struct mixer_operations));
-
- mixer_devs[m]->devc = devc;
-
- if (owner)
- mixer_devs[m]->owner = owner;
-
- devc->my_mixerdev = m;
- sb_mixer_reset(devc);
- return 1;
-}
-
-void sb_mixer_unload(sb_devc *devc)
-{
- if (devc->my_mixerdev == -1)
- return;
-
- kfree(mixer_devs[devc->my_mixerdev]);
- sound_unload_mixerdev(devc->my_mixerdev);
- sbmixnum--;
-}
diff --git a/sound/oss/sb_mixer.h b/sound/oss/sb_mixer.h
deleted file mode 100644
index 4b9425f085e3..000000000000
--- a/sound/oss/sb_mixer.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * sound/oss/sb_mixer.h
- *
- * Definitions for the SB Pro and SB16 mixers
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-
-/*
- * Modified:
- * Hunyue Yau Jan 6 1994
- * Added defines for the Sound Galaxy NX Pro mixer.
- *
- * Rolf Fokkens Dec 20 1998
- * Added defines for some ES188x chips.
- *
- * Rolf Fokkens Dec 27 1998
- * Moved static stuff to sb_mixer.c
- *
- */
-/*
- * Mixer registers
- *
- * NOTE! RECORD_SRC == IN_FILTER
- */
-
-/*
- * Mixer registers of SB Pro
- */
-#define VOC_VOL 0x04
-#define MIC_VOL 0x0A
-#define MIC_MIX 0x0A
-#define RECORD_SRC 0x0C
-#define IN_FILTER 0x0C
-#define OUT_FILTER 0x0E
-#define MASTER_VOL 0x22
-#define FM_VOL 0x26
-#define CD_VOL 0x28
-#define LINE_VOL 0x2E
-#define IRQ_NR 0x80
-#define DMA_NR 0x81
-#define IRQ_STAT 0x82
-#define OPSW 0x3c
-
-/*
- * Additional registers on the SG NX Pro
- */
-#define COVOX_VOL 0x42
-#define TREBLE_LVL 0x44
-#define BASS_LVL 0x46
-
-#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */
-#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */
-#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */
-#define FILT_OFF (1 << 5)
-
-#define MONO_DAC 0x00
-#define STEREO_DAC 0x02
-
-/*
- * Mixer registers of SB16
- */
-#define SB16_OMASK 0x3c
-#define SB16_IMASK_L 0x3d
-#define SB16_IMASK_R 0x3e
-
-#define LEFT_CHN 0
-#define RIGHT_CHN 1
-
-/*
- * 3DSE register of AWE32/64
- */
-#define AWE_3DSE 0x90
-
-/*
- * Mixer registers of ALS007
- */
-#define ALS007_RECORD_SRC 0x6c
-#define ALS007_OUTPUT_CTRL1 0x3c
-#define ALS007_OUTPUT_CTRL2 0x4c
-
-#define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r) \
- {{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}}
-
-/*
- * Recording sources (SB Pro)
- */
-
-#define SRC__MIC 1 /* Select Microphone recording source */
-#define SRC__CD 3 /* Select CD recording source */
-#define SRC__LINE 7 /* Use Line-in for recording source */
-
-/*
- * Recording sources for ALS-007
- */
-
-#define ALS007_MIC 4
-#define ALS007_LINE 6
-#define ALS007_CD 2
-#define ALS007_SYNTH 7
diff --git a/sound/oss/sequencer.c b/sound/oss/sequencer.c
deleted file mode 100644
index e85789e53816..000000000000
--- a/sound/oss/sequencer.c
+++ /dev/null
@@ -1,1671 +0,0 @@
-/*
- * sound/oss/sequencer.c
- *
- * The sequencer personality manager.
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-/*
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * Alan Cox : reformatted and fixed a pair of null pointer bugs
- */
-#include <linux/kmod.h>
-#include <linux/spinlock.h>
-#include "sound_config.h"
-
-#include "midi_ctrl.h"
-
-static int sequencer_ok;
-static struct sound_timer_operations *tmr;
-static int tmr_no = -1; /* Currently selected timer */
-static int pending_timer = -1; /* For timer change operation */
-extern unsigned long seq_time;
-
-static int obsolete_api_used;
-static DEFINE_SPINLOCK(lock);
-
-/*
- * Local counts for number of synth and MIDI devices. These are initialized
- * by the sequencer_open.
- */
-static int max_mididev;
-static int max_synthdev;
-
-/*
- * The seq_mode gives the operating mode of the sequencer:
- * 1 = level1 (the default)
- * 2 = level2 (extended capabilities)
- */
-
-#define SEQ_1 1
-#define SEQ_2 2
-static int seq_mode = SEQ_1;
-
-static DECLARE_WAIT_QUEUE_HEAD(seq_sleeper);
-static DECLARE_WAIT_QUEUE_HEAD(midi_sleeper);
-
-static int midi_opened[MAX_MIDI_DEV];
-
-static int midi_written[MAX_MIDI_DEV];
-
-static unsigned long prev_input_time;
-static int prev_event_time;
-
-#include "tuning.h"
-
-#define EV_SZ 8
-#define IEV_SZ 8
-
-static unsigned char *queue;
-static unsigned char *iqueue;
-
-static volatile int qhead, qtail, qlen;
-static volatile int iqhead, iqtail, iqlen;
-static volatile int seq_playing;
-static volatile int sequencer_busy;
-static int output_threshold;
-static long pre_event_timeout;
-static unsigned synth_open_mask;
-
-static int seq_queue(unsigned char *note, char nonblock);
-static void seq_startplay(void);
-static int seq_sync(void);
-static void seq_reset(void);
-
-#if MAX_SYNTH_DEV > 15
-#error Too many synthesizer devices enabled.
-#endif
-
-int sequencer_read(int dev, struct file *file, char __user *buf, int count)
-{
- int c = count, p = 0;
- int ev_len;
- unsigned long flags;
-
- dev = dev >> 4;
-
- ev_len = seq_mode == SEQ_1 ? 4 : 8;
-
- spin_lock_irqsave(&lock,flags);
-
- if (!iqlen)
- {
- spin_unlock_irqrestore(&lock,flags);
- if (file->f_flags & O_NONBLOCK) {
- return -EAGAIN;
- }
-
- interruptible_sleep_on_timeout(&midi_sleeper,
- pre_event_timeout);
- spin_lock_irqsave(&lock,flags);
- if (!iqlen)
- {
- spin_unlock_irqrestore(&lock,flags);
- return 0;
- }
- }
- while (iqlen && c >= ev_len)
- {
- char *fixit = (char *) &iqueue[iqhead * IEV_SZ];
- spin_unlock_irqrestore(&lock,flags);
- if (copy_to_user(&(buf)[p], fixit, ev_len))
- return count - c;
- p += ev_len;
- c -= ev_len;
-
- spin_lock_irqsave(&lock,flags);
- iqhead = (iqhead + 1) % SEQ_MAX_QUEUE;
- iqlen--;
- }
- spin_unlock_irqrestore(&lock,flags);
- return count - c;
-}
-
-static void sequencer_midi_output(int dev)
-{
- /*
- * Currently NOP
- */
-}
-
-void seq_copy_to_input(unsigned char *event_rec, int len)
-{
- unsigned long flags;
-
- /*
- * Verify that the len is valid for the current mode.
- */
-
- if (len != 4 && len != 8)
- return;
- if ((seq_mode == SEQ_1) != (len == 4))
- return;
-
- if (iqlen >= (SEQ_MAX_QUEUE - 1))
- return; /* Overflow */
-
- spin_lock_irqsave(&lock,flags);
- memcpy(&iqueue[iqtail * IEV_SZ], event_rec, len);
- iqlen++;
- iqtail = (iqtail + 1) % SEQ_MAX_QUEUE;
- wake_up(&midi_sleeper);
- spin_unlock_irqrestore(&lock,flags);
-}
-EXPORT_SYMBOL(seq_copy_to_input);
-
-static void sequencer_midi_input(int dev, unsigned char data)
-{
- unsigned int tstamp;
- unsigned char event_rec[4];
-
- if (data == 0xfe) /* Ignore active sensing */
- return;
-
- tstamp = jiffies - seq_time;
-
- if (tstamp != prev_input_time)
- {
- tstamp = (tstamp << 8) | SEQ_WAIT;
- seq_copy_to_input((unsigned char *) &tstamp, 4);
- prev_input_time = tstamp;
- }
- event_rec[0] = SEQ_MIDIPUTC;
- event_rec[1] = data;
- event_rec[2] = dev;
- event_rec[3] = 0;
-
- seq_copy_to_input(event_rec, 4);
-}
-
-void seq_input_event(unsigned char *event_rec, int len)
-{
- unsigned long this_time;
-
- if (seq_mode == SEQ_2)
- this_time = tmr->get_time(tmr_no);
- else
- this_time = jiffies - seq_time;
-
- if (this_time != prev_input_time)
- {
- unsigned char tmp_event[8];
-
- tmp_event[0] = EV_TIMING;
- tmp_event[1] = TMR_WAIT_ABS;
- tmp_event[2] = 0;
- tmp_event[3] = 0;
- *(unsigned int *) &tmp_event[4] = this_time;
-
- seq_copy_to_input(tmp_event, 8);
- prev_input_time = this_time;
- }
- seq_copy_to_input(event_rec, len);
-}
-EXPORT_SYMBOL(seq_input_event);
-
-int sequencer_write(int dev, struct file *file, const char __user *buf, int count)
-{
- unsigned char event_rec[EV_SZ], ev_code;
- int p = 0, c, ev_size;
- int mode = translate_mode(file);
-
- dev = dev >> 4;
-
- DEB(printk("sequencer_write(dev=%d, count=%d)\n", dev, count));
-
- if (mode == OPEN_READ)
- return -EIO;
-
- c = count;
-
- while (c >= 4)
- {
- if (copy_from_user((char *) event_rec, &(buf)[p], 4))
- goto out;
- ev_code = event_rec[0];
-
- if (ev_code == SEQ_FULLSIZE)
- {
- int err, fmt;
-
- dev = *(unsigned short *) &event_rec[2];
- if (dev < 0 || dev >= max_synthdev || synth_devs[dev] == NULL)
- return -ENXIO;
-
- if (!(synth_open_mask & (1 << dev)))
- return -ENXIO;
-
- fmt = (*(short *) &event_rec[0]) & 0xffff;
- err = synth_devs[dev]->load_patch(dev, fmt, buf, p + 4, c, 0);
- if (err < 0)
- return err;
-
- return err;
- }
- if (ev_code >= 128)
- {
- if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED)
- {
- printk(KERN_WARNING "Sequencer: Invalid level 2 event %x\n", ev_code);
- return -EINVAL;
- }
- ev_size = 8;
-
- if (c < ev_size)
- {
- if (!seq_playing)
- seq_startplay();
- return count - c;
- }
- if (copy_from_user((char *)&event_rec[4],
- &(buf)[p + 4], 4))
- goto out;
-
- }
- else
- {
- if (seq_mode == SEQ_2)
- {
- printk(KERN_WARNING "Sequencer: 4 byte event in level 2 mode\n");
- return -EINVAL;
- }
- ev_size = 4;
-
- if (event_rec[0] != SEQ_MIDIPUTC)
- obsolete_api_used = 1;
- }
-
- if (event_rec[0] == SEQ_MIDIPUTC)
- {
- if (!midi_opened[event_rec[2]])
- {
- int err, mode;
- int dev = event_rec[2];
-
- if (dev >= max_mididev || midi_devs[dev]==NULL)
- {
- /*printk("Sequencer Error: Nonexistent MIDI device %d\n", dev);*/
- return -ENXIO;
- }
- mode = translate_mode(file);
-
- if ((err = midi_devs[dev]->open(dev, mode,
- sequencer_midi_input, sequencer_midi_output)) < 0)
- {
- seq_reset();
- printk(KERN_WARNING "Sequencer Error: Unable to open Midi #%d\n", dev);
- return err;
- }
- midi_opened[dev] = 1;
- }
- }
- if (!seq_queue(event_rec, (file->f_flags & (O_NONBLOCK) ? 1 : 0)))
- {
- int processed = count - c;
-
- if (!seq_playing)
- seq_startplay();
-
- if (!processed && (file->f_flags & O_NONBLOCK))
- return -EAGAIN;
- else
- return processed;
- }
- p += ev_size;
- c -= ev_size;
- }
-
- if (!seq_playing)
- seq_startplay();
-out:
- return count;
-}
-
-static int seq_queue(unsigned char *note, char nonblock)
-{
-
- /*
- * Test if there is space in the queue
- */
-
- if (qlen >= SEQ_MAX_QUEUE)
- if (!seq_playing)
- seq_startplay(); /*
- * Give chance to drain the queue
- */
-
- if (!nonblock && qlen >= SEQ_MAX_QUEUE && !waitqueue_active(&seq_sleeper)) {
- /*
- * Sleep until there is enough space on the queue
- */
- interruptible_sleep_on(&seq_sleeper);
- }
- if (qlen >= SEQ_MAX_QUEUE)
- {
- return 0; /*
- * To be sure
- */
- }
- memcpy(&queue[qtail * EV_SZ], note, EV_SZ);
-
- qtail = (qtail + 1) % SEQ_MAX_QUEUE;
- qlen++;
-
- return 1;
-}
-
-static int extended_event(unsigned char *q)
-{
- int dev = q[2];
-
- if (dev < 0 || dev >= max_synthdev)
- return -ENXIO;
-
- if (!(synth_open_mask & (1 << dev)))
- return -ENXIO;
-
- switch (q[1])
- {
- case SEQ_NOTEOFF:
- synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]);
- break;
-
- case SEQ_NOTEON:
- if (q[4] > 127 && q[4] != 255)
- return 0;
-
- if (q[5] == 0)
- {
- synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]);
- break;
- }
- synth_devs[dev]->start_note(dev, q[3], q[4], q[5]);
- break;
-
- case SEQ_PGMCHANGE:
- synth_devs[dev]->set_instr(dev, q[3], q[4]);
- break;
-
- case SEQ_AFTERTOUCH:
- synth_devs[dev]->aftertouch(dev, q[3], q[4]);
- break;
-
- case SEQ_BALANCE:
- synth_devs[dev]->panning(dev, q[3], (char) q[4]);
- break;
-
- case SEQ_CONTROLLER:
- synth_devs[dev]->controller(dev, q[3], q[4], (short) (q[5] | (q[6] << 8)));
- break;
-
- case SEQ_VOLMODE:
- if (synth_devs[dev]->volume_method != NULL)
- synth_devs[dev]->volume_method(dev, q[3]);
- break;
-
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int find_voice(int dev, int chn, int note)
-{
- unsigned short key;
- int i;
-
- key = (chn << 8) | (note + 1);
- for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
- if (synth_devs[dev]->alloc.map[i] == key)
- return i;
- return -1;
-}
-
-static int alloc_voice(int dev, int chn, int note)
-{
- unsigned short key;
- int voice;
-
- key = (chn << 8) | (note + 1);
-
- voice = synth_devs[dev]->alloc_voice(dev, chn, note,
- &synth_devs[dev]->alloc);
- synth_devs[dev]->alloc.map[voice] = key;
- synth_devs[dev]->alloc.alloc_times[voice] =
- synth_devs[dev]->alloc.timestamp++;
- return voice;
-}
-
-static void seq_chn_voice_event(unsigned char *event_rec)
-{
-#define dev event_rec[1]
-#define cmd event_rec[2]
-#define chn event_rec[3]
-#define note event_rec[4]
-#define parm event_rec[5]
-
- int voice = -1;
-
- if ((int) dev > max_synthdev || synth_devs[dev] == NULL)
- return;
- if (!(synth_open_mask & (1 << dev)))
- return;
- if (!synth_devs[dev])
- return;
-
- if (seq_mode == SEQ_2)
- {
- if (synth_devs[dev]->alloc_voice)
- voice = find_voice(dev, chn, note);
-
- if (cmd == MIDI_NOTEON && parm == 0)
- {
- cmd = MIDI_NOTEOFF;
- parm = 64;
- }
- }
-
- switch (cmd)
- {
- case MIDI_NOTEON:
- if (note > 127 && note != 255) /* Not a seq2 feature */
- return;
-
- if (voice == -1 && seq_mode == SEQ_2 && synth_devs[dev]->alloc_voice)
- {
- /* Internal synthesizer (FM, GUS, etc) */
- voice = alloc_voice(dev, chn, note);
- }
- if (voice == -1)
- voice = chn;
-
- if (seq_mode == SEQ_2 && (int) dev < num_synths)
- {
- /*
- * The MIDI channel 10 is a percussive channel. Use the note
- * number to select the proper patch (128 to 255) to play.
- */
-
- if (chn == 9)
- {
- synth_devs[dev]->set_instr(dev, voice, 128 + note);
- synth_devs[dev]->chn_info[chn].pgm_num = 128 + note;
- }
- synth_devs[dev]->setup_voice(dev, voice, chn);
- }
- synth_devs[dev]->start_note(dev, voice, note, parm);
- break;
-
- case MIDI_NOTEOFF:
- if (voice == -1)
- voice = chn;
- synth_devs[dev]->kill_note(dev, voice, note, parm);
- break;
-
- case MIDI_KEY_PRESSURE:
- if (voice == -1)
- voice = chn;
- synth_devs[dev]->aftertouch(dev, voice, parm);
- break;
-
- default:;
- }
-#undef dev
-#undef cmd
-#undef chn
-#undef note
-#undef parm
-}
-
-
-static void seq_chn_common_event(unsigned char *event_rec)
-{
- unsigned char dev = event_rec[1];
- unsigned char cmd = event_rec[2];
- unsigned char chn = event_rec[3];
- unsigned char p1 = event_rec[4];
-
- /* unsigned char p2 = event_rec[5]; */
- unsigned short w14 = *(short *) &event_rec[6];
-
- if ((int) dev > max_synthdev || synth_devs[dev] == NULL)
- return;
- if (!(synth_open_mask & (1 << dev)))
- return;
- if (!synth_devs[dev])
- return;
-
- switch (cmd)
- {
- case MIDI_PGM_CHANGE:
- if (seq_mode == SEQ_2)
- {
- synth_devs[dev]->chn_info[chn].pgm_num = p1;
- if ((int) dev >= num_synths)
- synth_devs[dev]->set_instr(dev, chn, p1);
- }
- else
- synth_devs[dev]->set_instr(dev, chn, p1);
-
- break;
-
- case MIDI_CTL_CHANGE:
- if (seq_mode == SEQ_2)
- {
- if (chn > 15 || p1 > 127)
- break;
-
- synth_devs[dev]->chn_info[chn].controllers[p1] = w14 & 0x7f;
-
- if (p1 < 32) /* Setting MSB should clear LSB to 0 */
- synth_devs[dev]->chn_info[chn].controllers[p1 + 32] = 0;
-
- if ((int) dev < num_synths)
- {
- int val = w14 & 0x7f;
- int i, key;
-
- if (p1 < 64) /* Combine MSB and LSB */
- {
- val = ((synth_devs[dev]->
- chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7)
- | (synth_devs[dev]->
- chn_info[chn].controllers[p1 | 32] & 0x7f);
- p1 &= ~32;
- }
- /* Handle all playing notes on this channel */
-
- key = ((int) chn << 8);
-
- for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
- if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key)
- synth_devs[dev]->controller(dev, i, p1, val);
- }
- else
- synth_devs[dev]->controller(dev, chn, p1, w14);
- }
- else /* Mode 1 */
- synth_devs[dev]->controller(dev, chn, p1, w14);
- break;
-
- case MIDI_PITCH_BEND:
- if (seq_mode == SEQ_2)
- {
- synth_devs[dev]->chn_info[chn].bender_value = w14;
-
- if ((int) dev < num_synths)
- {
- /* Handle all playing notes on this channel */
- int i, key;
-
- key = (chn << 8);
-
- for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++)
- if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key)
- synth_devs[dev]->bender(dev, i, w14);
- }
- else
- synth_devs[dev]->bender(dev, chn, w14);
- }
- else /* MODE 1 */
- synth_devs[dev]->bender(dev, chn, w14);
- break;
-
- default:;
- }
-}
-
-static int seq_timing_event(unsigned char *event_rec)
-{
- unsigned char cmd = event_rec[1];
- unsigned int parm = *(int *) &event_rec[4];
-
- if (seq_mode == SEQ_2)
- {
- int ret;
-
- if ((ret = tmr->event(tmr_no, event_rec)) == TIMER_ARMED)
- if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
- wake_up(&seq_sleeper);
- return ret;
- }
- switch (cmd)
- {
- case TMR_WAIT_REL:
- parm += prev_event_time;
-
- /*
- * NOTE! No break here. Execution of TMR_WAIT_REL continues in the
- * next case (TMR_WAIT_ABS)
- */
-
- case TMR_WAIT_ABS:
- if (parm > 0)
- {
- long time;
-
- time = parm;
- prev_event_time = time;
-
- seq_playing = 1;
- request_sound_timer(time);
-
- if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
- wake_up(&seq_sleeper);
- return TIMER_ARMED;
- }
- break;
-
- case TMR_START:
- seq_time = jiffies;
- prev_input_time = 0;
- prev_event_time = 0;
- break;
-
- case TMR_STOP:
- break;
-
- case TMR_CONTINUE:
- break;
-
- case TMR_TEMPO:
- break;
-
- case TMR_ECHO:
- if (seq_mode == SEQ_2)
- seq_copy_to_input(event_rec, 8);
- else
- {
- parm = (parm << 8 | SEQ_ECHO);
- seq_copy_to_input((unsigned char *) &parm, 4);
- }
- break;
-
- default:;
- }
-
- return TIMER_NOT_ARMED;
-}
-
-static void seq_local_event(unsigned char *event_rec)
-{
- unsigned char cmd = event_rec[1];
- unsigned int parm = *((unsigned int *) &event_rec[4]);
-
- switch (cmd)
- {
- case LOCL_STARTAUDIO:
- DMAbuf_start_devices(parm);
- break;
-
- default:;
- }
-}
-
-static void seq_sysex_message(unsigned char *event_rec)
-{
- unsigned int dev = event_rec[1];
- int i, l = 0;
- unsigned char *buf = &event_rec[2];
-
- if (dev > max_synthdev)
- return;
- if (!(synth_open_mask & (1 << dev)))
- return;
- if (!synth_devs[dev])
- return;
-
- l = 0;
- for (i = 0; i < 6 && buf[i] != 0xff; i++)
- l = i + 1;
-
- if (!synth_devs[dev]->send_sysex)
- return;
- if (l > 0)
- synth_devs[dev]->send_sysex(dev, buf, l);
-}
-
-static int play_event(unsigned char *q)
-{
- /*
- * NOTE! This routine returns
- * 0 = normal event played.
- * 1 = Timer armed. Suspend playback until timer callback.
- * 2 = MIDI output buffer full. Restore queue and suspend until timer
- */
- unsigned int *delay;
-
- switch (q[0])
- {
- case SEQ_NOTEOFF:
- if (synth_open_mask & (1 << 0))
- if (synth_devs[0])
- synth_devs[0]->kill_note(0, q[1], 255, q[3]);
- break;
-
- case SEQ_NOTEON:
- if (q[4] < 128 || q[4] == 255)
- if (synth_open_mask & (1 << 0))
- if (synth_devs[0])
- synth_devs[0]->start_note(0, q[1], q[2], q[3]);
- break;
-
- case SEQ_WAIT:
- delay = (unsigned int *) q; /*
- * Bytes 1 to 3 are containing the *
- * delay in 'ticks'
- */
- *delay = (*delay >> 8) & 0xffffff;
-
- if (*delay > 0)
- {
- long time;
-
- seq_playing = 1;
- time = *delay;
- prev_event_time = time;
-
- request_sound_timer(time);
-
- if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
- wake_up(&seq_sleeper);
- /*
- * The timer is now active and will reinvoke this function
- * after the timer expires. Return to the caller now.
- */
- return 1;
- }
- break;
-
- case SEQ_PGMCHANGE:
- if (synth_open_mask & (1 << 0))
- if (synth_devs[0])
- synth_devs[0]->set_instr(0, q[1], q[2]);
- break;
-
- case SEQ_SYNCTIMER: /*
- * Reset timer
- */
- seq_time = jiffies;
- prev_input_time = 0;
- prev_event_time = 0;
- break;
-
- case SEQ_MIDIPUTC: /*
- * Put a midi character
- */
- if (midi_opened[q[2]])
- {
- int dev;
-
- dev = q[2];
-
- if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
- break;
-
- if (!midi_devs[dev]->outputc(dev, q[1]))
- {
- /*
- * Output FIFO is full. Wait one timer cycle and try again.
- */
-
- seq_playing = 1;
- request_sound_timer(-1);
- return 2;
- }
- else
- midi_written[dev] = 1;
- }
- break;
-
- case SEQ_ECHO:
- seq_copy_to_input(q, 4); /*
- * Echo back to the process
- */
- break;
-
- case SEQ_PRIVATE:
- if ((int) q[1] < max_synthdev)
- synth_devs[q[1]]->hw_control(q[1], q);
- break;
-
- case SEQ_EXTENDED:
- extended_event(q);
- break;
-
- case EV_CHN_VOICE:
- seq_chn_voice_event(q);
- break;
-
- case EV_CHN_COMMON:
- seq_chn_common_event(q);
- break;
-
- case EV_TIMING:
- if (seq_timing_event(q) == TIMER_ARMED)
- {
- return 1;
- }
- break;
-
- case EV_SEQ_LOCAL:
- seq_local_event(q);
- break;
-
- case EV_SYSEX:
- seq_sysex_message(q);
- break;
-
- default:;
- }
- return 0;
-}
-
-/* called also as timer in irq context */
-static void seq_startplay(void)
-{
- int this_one, action;
- unsigned long flags;
-
- while (qlen > 0)
- {
-
- spin_lock_irqsave(&lock,flags);
- qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE;
- qlen--;
- spin_unlock_irqrestore(&lock,flags);
-
- seq_playing = 1;
-
- if ((action = play_event(&queue[this_one * EV_SZ])))
- { /* Suspend playback. Next timer routine invokes this routine again */
- if (action == 2)
- {
- qlen++;
- qhead = this_one;
- }
- return;
- }
- }
-
- seq_playing = 0;
-
- if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
- wake_up(&seq_sleeper);
-}
-
-static void reset_controllers(int dev, unsigned char *controller, int update_dev)
-{
- int i;
- for (i = 0; i < 128; i++)
- controller[i] = ctrl_def_values[i];
-}
-
-static void setup_mode2(void)
-{
- int dev;
-
- max_synthdev = num_synths;
-
- for (dev = 0; dev < num_midis; dev++)
- {
- if (midi_devs[dev] && midi_devs[dev]->converter != NULL)
- {
- synth_devs[max_synthdev++] = midi_devs[dev]->converter;
- }
- }
-
- for (dev = 0; dev < max_synthdev; dev++)
- {
- int chn;
-
- synth_devs[dev]->sysex_ptr = 0;
- synth_devs[dev]->emulation = 0;
-
- for (chn = 0; chn < 16; chn++)
- {
- synth_devs[dev]->chn_info[chn].pgm_num = 0;
- reset_controllers(dev,
- synth_devs[dev]->chn_info[chn].controllers,0);
- synth_devs[dev]->chn_info[chn].bender_value = (1 << 7); /* Neutral */
- synth_devs[dev]->chn_info[chn].bender_range = 200;
- }
- }
- max_mididev = 0;
- seq_mode = SEQ_2;
-}
-
-int sequencer_open(int dev, struct file *file)
-{
- int retval, mode, i;
- int level, tmp;
-
- if (!sequencer_ok)
- sequencer_init();
-
- level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1;
-
- dev = dev >> 4;
- mode = translate_mode(file);
-
- DEB(printk("sequencer_open(dev=%d)\n", dev));
-
- if (!sequencer_ok)
- {
-/* printk("Sound card: sequencer not initialized\n");*/
- return -ENXIO;
- }
- if (dev) /* Patch manager device (obsolete) */
- return -ENXIO;
-
- if(synth_devs[dev] == NULL)
- request_module("synth0");
-
- if (mode == OPEN_READ)
- {
- if (!num_midis)
- {
- /*printk("Sequencer: No MIDI devices. Input not possible\n");*/
- sequencer_busy = 0;
- return -ENXIO;
- }
- }
- if (sequencer_busy)
- {
- return -EBUSY;
- }
- sequencer_busy = 1;
- obsolete_api_used = 0;
-
- max_mididev = num_midis;
- max_synthdev = num_synths;
- pre_event_timeout = MAX_SCHEDULE_TIMEOUT;
- seq_mode = SEQ_1;
-
- if (pending_timer != -1)
- {
- tmr_no = pending_timer;
- pending_timer = -1;
- }
- if (tmr_no == -1) /* Not selected yet */
- {
- int i, best;
-
- best = -1;
- for (i = 0; i < num_sound_timers; i++)
- if (sound_timer_devs[i] && sound_timer_devs[i]->priority > best)
- {
- tmr_no = i;
- best = sound_timer_devs[i]->priority;
- }
- if (tmr_no == -1) /* Should not be */
- tmr_no = 0;
- }
- tmr = sound_timer_devs[tmr_no];
-
- if (level == 2)
- {
- if (tmr == NULL)
- {
- /*printk("sequencer: No timer for level 2\n");*/
- sequencer_busy = 0;
- return -ENXIO;
- }
- setup_mode2();
- }
- if (!max_synthdev && !max_mididev)
- {
- sequencer_busy=0;
- return -ENXIO;
- }
-
- synth_open_mask = 0;
-
- for (i = 0; i < max_mididev; i++)
- {
- midi_opened[i] = 0;
- midi_written[i] = 0;
- }
-
- for (i = 0; i < max_synthdev; i++)
- {
- if (synth_devs[i]==NULL)
- continue;
-
- if (!try_module_get(synth_devs[i]->owner))
- continue;
-
- if ((tmp = synth_devs[i]->open(i, mode)) < 0)
- {
- printk(KERN_WARNING "Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp);
- if (synth_devs[i]->midi_dev)
- printk(KERN_WARNING "(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev);
- }
- else
- {
- synth_open_mask |= (1 << i);
- if (synth_devs[i]->midi_dev)
- midi_opened[synth_devs[i]->midi_dev] = 1;
- }
- }
-
- seq_time = jiffies;
-
- prev_input_time = 0;
- prev_event_time = 0;
-
- if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE))
- {
- /*
- * Initialize midi input devices
- */
-
- for (i = 0; i < max_mididev; i++)
- if (!midi_opened[i] && midi_devs[i])
- {
- if (!try_module_get(midi_devs[i]->owner))
- continue;
-
- if ((retval = midi_devs[i]->open(i, mode,
- sequencer_midi_input, sequencer_midi_output)) >= 0)
- {
- midi_opened[i] = 1;
- }
- }
- }
-
- if (seq_mode == SEQ_2) {
- if (try_module_get(tmr->owner))
- tmr->open(tmr_no, seq_mode);
- }
-
- init_waitqueue_head(&seq_sleeper);
- init_waitqueue_head(&midi_sleeper);
- output_threshold = SEQ_MAX_QUEUE / 2;
-
- return 0;
-}
-
-static void seq_drain_midi_queues(void)
-{
- int i, n;
-
- /*
- * Give the Midi drivers time to drain their output queues
- */
-
- n = 1;
-
- while (!signal_pending(current) && n)
- {
- n = 0;
-
- for (i = 0; i < max_mididev; i++)
- if (midi_opened[i] && midi_written[i])
- if (midi_devs[i]->buffer_status != NULL)
- if (midi_devs[i]->buffer_status(i))
- n++;
-
- /*
- * Let's have a delay
- */
-
- if (n)
- interruptible_sleep_on_timeout(&seq_sleeper,
- HZ/10);
- }
-}
-
-void sequencer_release(int dev, struct file *file)
-{
- int i;
- int mode = translate_mode(file);
-
- dev = dev >> 4;
-
- DEB(printk("sequencer_release(dev=%d)\n", dev));
-
- /*
- * Wait until the queue is empty (if we don't have nonblock)
- */
-
- if (mode != OPEN_READ && !(file->f_flags & O_NONBLOCK))
- {
- while (!signal_pending(current) && qlen > 0)
- {
- seq_sync();
- interruptible_sleep_on_timeout(&seq_sleeper,
- 3*HZ);
- /* Extra delay */
- }
- }
-
- if (mode != OPEN_READ)
- seq_drain_midi_queues(); /*
- * Ensure the output queues are empty
- */
- seq_reset();
- if (mode != OPEN_READ)
- seq_drain_midi_queues(); /*
- * Flush the all notes off messages
- */
-
- for (i = 0; i < max_synthdev; i++)
- {
- if (synth_open_mask & (1 << i)) /*
- * Actually opened
- */
- if (synth_devs[i])
- {
- synth_devs[i]->close(i);
-
- module_put(synth_devs[i]->owner);
-
- if (synth_devs[i]->midi_dev)
- midi_opened[synth_devs[i]->midi_dev] = 0;
- }
- }
-
- for (i = 0; i < max_mididev; i++)
- {
- if (midi_opened[i]) {
- midi_devs[i]->close(i);
- module_put(midi_devs[i]->owner);
- }
- }
-
- if (seq_mode == SEQ_2) {
- tmr->close(tmr_no);
- module_put(tmr->owner);
- }
-
- if (obsolete_api_used)
- printk(KERN_WARNING "/dev/music: Obsolete (4 byte) API was used by %s\n", current->comm);
- sequencer_busy = 0;
-}
-
-static int seq_sync(void)
-{
- if (qlen && !seq_playing && !signal_pending(current))
- seq_startplay();
-
- if (qlen > 0)
- interruptible_sleep_on_timeout(&seq_sleeper, HZ);
- return qlen;
-}
-
-static void midi_outc(int dev, unsigned char data)
-{
- /*
- * NOTE! Calls sleep(). Don't call this from interrupt.
- */
-
- int n;
- unsigned long flags;
-
- /*
- * This routine sends one byte to the Midi channel.
- * If the output FIFO is full, it waits until there
- * is space in the queue
- */
-
- n = 3 * HZ; /* Timeout */
-
- spin_lock_irqsave(&lock,flags);
- while (n && !midi_devs[dev]->outputc(dev, data)) {
- interruptible_sleep_on_timeout(&seq_sleeper, HZ/25);
- n--;
- }
- spin_unlock_irqrestore(&lock,flags);
-}
-
-static void seq_reset(void)
-{
- /*
- * NOTE! Calls sleep(). Don't call this from interrupt.
- */
-
- int i;
- int chn;
- unsigned long flags;
-
- sound_stop_timer();
-
- seq_time = jiffies;
- prev_input_time = 0;
- prev_event_time = 0;
-
- qlen = qhead = qtail = 0;
- iqlen = iqhead = iqtail = 0;
-
- for (i = 0; i < max_synthdev; i++)
- if (synth_open_mask & (1 << i))
- if (synth_devs[i])
- synth_devs[i]->reset(i);
-
- if (seq_mode == SEQ_2)
- {
- for (chn = 0; chn < 16; chn++)
- for (i = 0; i < max_synthdev; i++)
- if (synth_open_mask & (1 << i))
- if (synth_devs[i])
- {
- synth_devs[i]->controller(i, chn, 123, 0); /* All notes off */
- synth_devs[i]->controller(i, chn, 121, 0); /* Reset all ctl */
- synth_devs[i]->bender(i, chn, 1 << 13); /* Bender off */
- }
- }
- else /* seq_mode == SEQ_1 */
- {
- for (i = 0; i < max_mididev; i++)
- if (midi_written[i]) /*
- * Midi used. Some notes may still be playing
- */
- {
- /*
- * Sending just a ACTIVE SENSING message should be enough to stop all
- * playing notes. Since there are devices not recognizing the
- * active sensing, we have to send some all notes off messages also.
- */
- midi_outc(i, 0xfe);
-
- for (chn = 0; chn < 16; chn++)
- {
- midi_outc(i, (unsigned char) (0xb0 + (chn & 0x0f))); /* control change */
- midi_outc(i, 0x7b); /* All notes off */
- midi_outc(i, 0); /* Dummy parameter */
- }
-
- midi_devs[i]->close(i);
-
- midi_written[i] = 0;
- midi_opened[i] = 0;
- }
- }
-
- seq_playing = 0;
-
- spin_lock_irqsave(&lock,flags);
-
- if (waitqueue_active(&seq_sleeper)) {
- /* printk( "Sequencer Warning: Unexpected sleeping process - Waking up\n"); */
- wake_up(&seq_sleeper);
- }
- spin_unlock_irqrestore(&lock,flags);
-}
-
-static void seq_panic(void)
-{
- /*
- * This routine is called by the application in case the user
- * wants to reset the system to the default state.
- */
-
- seq_reset();
-
- /*
- * Since some of the devices don't recognize the active sensing and
- * all notes off messages, we have to shut all notes manually.
- *
- * TO BE IMPLEMENTED LATER
- */
-
- /*
- * Also return the controllers to their default states
- */
-}
-
-int sequencer_ioctl(int dev, struct file *file, unsigned int cmd, void __user *arg)
-{
- int midi_dev, orig_dev, val, err;
- int mode = translate_mode(file);
- struct synth_info inf;
- struct seq_event_rec event_rec;
- unsigned long flags;
- int __user *p = arg;
-
- orig_dev = dev = dev >> 4;
-
- switch (cmd)
- {
- case SNDCTL_TMR_TIMEBASE:
- case SNDCTL_TMR_TEMPO:
- case SNDCTL_TMR_START:
- case SNDCTL_TMR_STOP:
- case SNDCTL_TMR_CONTINUE:
- case SNDCTL_TMR_METRONOME:
- case SNDCTL_TMR_SOURCE:
- if (seq_mode != SEQ_2)
- return -EINVAL;
- return tmr->ioctl(tmr_no, cmd, arg);
-
- case SNDCTL_TMR_SELECT:
- if (seq_mode != SEQ_2)
- return -EINVAL;
- if (get_user(pending_timer, p))
- return -EFAULT;
- if (pending_timer < 0 || pending_timer >= num_sound_timers || sound_timer_devs[pending_timer] == NULL)
- {
- pending_timer = -1;
- return -EINVAL;
- }
- val = pending_timer;
- break;
-
- case SNDCTL_SEQ_PANIC:
- seq_panic();
- return -EINVAL;
-
- case SNDCTL_SEQ_SYNC:
- if (mode == OPEN_READ)
- return 0;
- while (qlen > 0 && !signal_pending(current))
- seq_sync();
- return qlen ? -EINTR : 0;
-
- case SNDCTL_SEQ_RESET:
- seq_reset();
- return 0;
-
- case SNDCTL_SEQ_TESTMIDI:
- if (__get_user(midi_dev, p))
- return -EFAULT;
- if (midi_dev < 0 || midi_dev >= max_mididev || !midi_devs[midi_dev])
- return -ENXIO;
-
- if (!midi_opened[midi_dev] &&
- (err = midi_devs[midi_dev]->open(midi_dev, mode, sequencer_midi_input,
- sequencer_midi_output)) < 0)
- return err;
- midi_opened[midi_dev] = 1;
- return 0;
-
- case SNDCTL_SEQ_GETINCOUNT:
- if (mode == OPEN_WRITE)
- return 0;
- val = iqlen;
- break;
-
- case SNDCTL_SEQ_GETOUTCOUNT:
- if (mode == OPEN_READ)
- return 0;
- val = SEQ_MAX_QUEUE - qlen;
- break;
-
- case SNDCTL_SEQ_GETTIME:
- if (seq_mode == SEQ_2)
- return tmr->ioctl(tmr_no, cmd, arg);
- val = jiffies - seq_time;
- break;
-
- case SNDCTL_SEQ_CTRLRATE:
- /*
- * If *arg == 0, just return the current rate
- */
- if (seq_mode == SEQ_2)
- return tmr->ioctl(tmr_no, cmd, arg);
-
- if (get_user(val, p))
- return -EFAULT;
- if (val != 0)
- return -EINVAL;
- val = HZ;
- break;
-
- case SNDCTL_SEQ_RESETSAMPLES:
- case SNDCTL_SYNTH_REMOVESAMPLE:
- case SNDCTL_SYNTH_CONTROL:
- if (get_user(dev, p))
- return -EFAULT;
- if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
- return -ENXIO;
- if (!(synth_open_mask & (1 << dev)) && !orig_dev)
- return -EBUSY;
- return synth_devs[dev]->ioctl(dev, cmd, arg);
-
- case SNDCTL_SEQ_NRSYNTHS:
- val = max_synthdev;
- break;
-
- case SNDCTL_SEQ_NRMIDIS:
- val = max_mididev;
- break;
-
- case SNDCTL_SYNTH_MEMAVL:
- if (get_user(dev, p))
- return -EFAULT;
- if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
- return -ENXIO;
- if (!(synth_open_mask & (1 << dev)) && !orig_dev)
- return -EBUSY;
- val = synth_devs[dev]->ioctl(dev, cmd, arg);
- break;
-
- case SNDCTL_FM_4OP_ENABLE:
- if (get_user(dev, p))
- return -EFAULT;
- if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL)
- return -ENXIO;
- if (!(synth_open_mask & (1 << dev)))
- return -ENXIO;
- synth_devs[dev]->ioctl(dev, cmd, arg);
- return 0;
-
- case SNDCTL_SYNTH_INFO:
- if (get_user(dev, &((struct synth_info __user *)arg)->device))
- return -EFAULT;
- if (dev < 0 || dev >= max_synthdev)
- return -ENXIO;
- if (!(synth_open_mask & (1 << dev)) && !orig_dev)
- return -EBUSY;
- return synth_devs[dev]->ioctl(dev, cmd, arg);
-
- /* Like SYNTH_INFO but returns ID in the name field */
- case SNDCTL_SYNTH_ID:
- if (get_user(dev, &((struct synth_info __user *)arg)->device))
- return -EFAULT;
- if (dev < 0 || dev >= max_synthdev)
- return -ENXIO;
- if (!(synth_open_mask & (1 << dev)) && !orig_dev)
- return -EBUSY;
- memcpy(&inf, synth_devs[dev]->info, sizeof(inf));
- strlcpy(inf.name, synth_devs[dev]->id, sizeof(inf.name));
- inf.device = dev;
- return copy_to_user(arg, &inf, sizeof(inf))?-EFAULT:0;
-
- case SNDCTL_SEQ_OUTOFBAND:
- if (copy_from_user(&event_rec, arg, sizeof(event_rec)))
- return -EFAULT;
- spin_lock_irqsave(&lock,flags);
- play_event(event_rec.arr);
- spin_unlock_irqrestore(&lock,flags);
- return 0;
-
- case SNDCTL_MIDI_INFO:
- if (get_user(dev, &((struct midi_info __user *)arg)->device))
- return -EFAULT;
- if (dev < 0 || dev >= max_mididev || !midi_devs[dev])
- return -ENXIO;
- midi_devs[dev]->info.device = dev;
- return copy_to_user(arg, &midi_devs[dev]->info, sizeof(struct midi_info))?-EFAULT:0;
-
- case SNDCTL_SEQ_THRESHOLD:
- if (get_user(val, p))
- return -EFAULT;
- if (val < 1)
- val = 1;
- if (val >= SEQ_MAX_QUEUE)
- val = SEQ_MAX_QUEUE - 1;
- output_threshold = val;
- return 0;
-
- case SNDCTL_MIDI_PRETIME:
- if (get_user(val, p))
- return -EFAULT;
- if (val < 0)
- val = 0;
- val = (HZ * val) / 10;
- pre_event_timeout = val;
- break;
-
- default:
- if (mode == OPEN_READ)
- return -EIO;
- if (!synth_devs[0])
- return -ENXIO;
- if (!(synth_open_mask & (1 << 0)))
- return -ENXIO;
- if (!synth_devs[0]->ioctl)
- return -EINVAL;
- return synth_devs[0]->ioctl(0, cmd, arg);
- }
- return put_user(val, p);
-}
-
-/* No kernel lock - we're using the global irq lock here */
-unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait)
-{
- unsigned long flags;
- unsigned int mask = 0;
-
- dev = dev >> 4;
-
- spin_lock_irqsave(&lock,flags);
- /* input */
- poll_wait(file, &midi_sleeper, wait);
- if (iqlen)
- mask |= POLLIN | POLLRDNORM;
-
- /* output */
- poll_wait(file, &seq_sleeper, wait);
- if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
- mask |= POLLOUT | POLLWRNORM;
- spin_unlock_irqrestore(&lock,flags);
- return mask;
-}
-
-
-void sequencer_timer(unsigned long dummy)
-{
- seq_startplay();
-}
-EXPORT_SYMBOL(sequencer_timer);
-
-int note_to_freq(int note_num)
-{
-
- /*
- * This routine converts a midi note to a frequency (multiplied by 1000)
- */
-
- int note, octave, note_freq;
- static int notes[] =
- {
- 261632, 277189, 293671, 311132, 329632, 349232,
- 369998, 391998, 415306, 440000, 466162, 493880
- };
-
-#define BASE_OCTAVE 5
-
- octave = note_num / 12;
- note = note_num % 12;
-
- note_freq = notes[note];
-
- if (octave < BASE_OCTAVE)
- note_freq >>= (BASE_OCTAVE - octave);
- else if (octave > BASE_OCTAVE)
- note_freq <<= (octave - BASE_OCTAVE);
-
- /*
- * note_freq >>= 1;
- */
-
- return note_freq;
-}
-EXPORT_SYMBOL(note_to_freq);
-
-unsigned long compute_finetune(unsigned long base_freq, int bend, int range,
- int vibrato_cents)
-{
- unsigned long amount;
- int negative, semitones, cents, multiplier = 1;
-
- if (!bend)
- return base_freq;
- if (!range)
- return base_freq;
-
- if (!base_freq)
- return base_freq;
-
- if (range >= 8192)
- range = 8192;
-
- bend = bend * range / 8192; /* Convert to cents */
- bend += vibrato_cents;
-
- if (!bend)
- return base_freq;
-
- negative = bend < 0 ? 1 : 0;
-
- if (bend < 0)
- bend *= -1;
- if (bend > range)
- bend = range;
-
- /*
- if (bend > 2399)
- bend = 2399;
- */
- while (bend > 2399)
- {
- multiplier *= 4;
- bend -= 2400;
- }
-
- semitones = bend / 100;
- cents = bend % 100;
-
- amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents]) / 10000;
-
- if (negative)
- return (base_freq * 10000) / amount; /* Bend down */
- else
- return (base_freq * amount) / 10000; /* Bend up */
-}
-EXPORT_SYMBOL(compute_finetune);
-
-void sequencer_init(void)
-{
- if (sequencer_ok)
- return;
- queue = (unsigned char *)vmalloc(SEQ_MAX_QUEUE * EV_SZ);
- if (queue == NULL)
- {
- printk(KERN_ERR "sequencer: Can't allocate memory for sequencer output queue\n");
- return;
- }
- iqueue = (unsigned char *)vmalloc(SEQ_MAX_QUEUE * IEV_SZ);
- if (iqueue == NULL)
- {
- printk(KERN_ERR "sequencer: Can't allocate memory for sequencer input queue\n");
- vfree(queue);
- return;
- }
- sequencer_ok = 1;
-}
-EXPORT_SYMBOL(sequencer_init);
-
-void sequencer_unload(void)
-{
- vfree(queue);
- vfree(iqueue);
- queue = iqueue = NULL;
-}
diff --git a/sound/oss/sound_calls.h b/sound/oss/sound_calls.h
deleted file mode 100644
index 87d8ad4a0340..000000000000
--- a/sound/oss/sound_calls.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * DMA buffer calls
- */
-
-int DMAbuf_open(int dev, int mode);
-int DMAbuf_release(int dev, int mode);
-int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock);
-int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock);
-int DMAbuf_rmchars(int dev, int buff_no, int c);
-int DMAbuf_start_output(int dev, int buff_no, int l);
-int DMAbuf_move_wrpointer(int dev, int l);
-/* int DMAbuf_ioctl(int dev, unsigned int cmd, void __user *arg, int local); */
-void DMAbuf_init(int dev, int dma1, int dma2);
-void DMAbuf_deinit(int dev);
-int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
-void DMAbuf_inputintr(int dev);
-void DMAbuf_outputintr(int dev, int underflow_flag);
-struct dma_buffparms;
-int DMAbuf_space_in_queue (int dev);
-int DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap);
-int DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap, int direction);
-void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap);
-unsigned int DMAbuf_poll(struct file *file, int dev, poll_table *wait);
-void DMAbuf_start_devices(unsigned int devmask);
-void DMAbuf_reset (int dev);
-int DMAbuf_sync (int dev);
-
-/*
- * System calls for /dev/dsp and /dev/audio (audio.c)
- */
-
-int audio_read (int dev, struct file *file, char __user *buf, int count);
-int audio_write (int dev, struct file *file, const char __user *buf, int count);
-int audio_open (int dev, struct file *file);
-void audio_release (int dev, struct file *file);
-int audio_ioctl (int dev, struct file *file,
- unsigned int cmd, void __user *arg);
-void audio_init_devices (void);
-void reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording);
-
-/*
- * System calls for the /dev/sequencer
- */
-
-int sequencer_read (int dev, struct file *file, char __user *buf, int count);
-int sequencer_write (int dev, struct file *file, const char __user *buf, int count);
-int sequencer_open (int dev, struct file *file);
-void sequencer_release (int dev, struct file *file);
-int sequencer_ioctl (int dev, struct file *file, unsigned int cmd, void __user *arg);
-unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait);
-
-void sequencer_init (void);
-void sequencer_unload (void);
-void sequencer_timer(unsigned long dummy);
-int note_to_freq(int note_num);
-unsigned long compute_finetune(unsigned long base_freq, int bend, int range,
- int vibrato_bend);
-void seq_input_event(unsigned char *event, int len);
-void seq_copy_to_input (unsigned char *event, int len);
-
-/*
- * System calls for the /dev/midi
- */
-
-int MIDIbuf_read (int dev, struct file *file, char __user *buf, int count);
-int MIDIbuf_write (int dev, struct file *file, const char __user *buf, int count);
-int MIDIbuf_open (int dev, struct file *file);
-void MIDIbuf_release (int dev, struct file *file);
-int MIDIbuf_ioctl (int dev, struct file *file, unsigned int cmd, void __user *arg);
-unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait);
-int MIDIbuf_avail(int dev);
-
-void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count);
-
-
-/* From soundcard.c */
-void request_sound_timer (int count);
-void sound_stop_timer(void);
-void conf_printf(char *name, struct address_info *hw_config);
-void conf_printf2(char *name, int base, int irq, int dma, int dma2);
-
-/* From sound_timer.c */
-void sound_timer_interrupt(void);
-void sound_timer_syncinterval(unsigned int new_usecs);
-
-/* From midi_synth.c */
-void do_midi_msg (int synthno, unsigned char *msg, int mlen);
diff --git a/sound/oss/sound_config.h b/sound/oss/sound_config.h
deleted file mode 100644
index 9d35c4c65b9b..000000000000
--- a/sound/oss/sound_config.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/* sound_config.h
- *
- * A driver for sound cards, misc. configuration parameters.
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-
-
-#ifndef _SOUND_CONFIG_H_
-#define _SOUND_CONFIG_H_
-
-#include <linux/fs.h>
-#include <linux/sound.h>
-
-#include "os.h"
-#include "soundvers.h"
-
-
-#ifndef SND_DEFAULT_ENABLE
-#define SND_DEFAULT_ENABLE 1
-#endif
-
-#ifndef MAX_REALTIME_FACTOR
-#define MAX_REALTIME_FACTOR 4
-#endif
-
-/*
- * Use always 64k buffer size. There is no reason to use shorter.
- */
-#undef DSP_BUFFSIZE
-#define DSP_BUFFSIZE (64*1024)
-
-#ifndef DSP_BUFFCOUNT
-#define DSP_BUFFCOUNT 1 /* 1 is recommended. */
-#endif
-
-#define FM_MONO 0x388 /* This is the I/O address used by AdLib */
-
-#ifndef CONFIG_PAS_BASE
-#define CONFIG_PAS_BASE 0x388
-#endif
-
-/* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the
- driver. (There is no need to alter this) */
-#define SEQ_MAX_QUEUE 1024
-
-#define SBFM_MAXINSTR (256) /* Size of the FM Instrument bank */
-/* 128 instruments for general MIDI setup and 16 unassigned */
-
-#define SND_NDEVS 256 /* Number of supported devices */
-
-#define DSP_DEFAULT_SPEED 8000
-
-#define MAX_AUDIO_DEV 5
-#define MAX_MIXER_DEV 5
-#define MAX_SYNTH_DEV 5
-#define MAX_MIDI_DEV 6
-#define MAX_TIMER_DEV 4
-
-struct address_info {
- int io_base;
- int irq;
- int dma;
- int dma2;
- int always_detect; /* 1=Trust me, it's there */
- char *name;
- int driver_use_1; /* Driver defined field 1 */
- int driver_use_2; /* Driver defined field 2 */
- int *osp; /* OS specific info */
- int card_subtype; /* Driver specific. Usually 0 */
- void *memptr; /* Module memory chainer */
- int slots[6]; /* To remember driver slot ids */
-};
-
-#define SYNTH_MAX_VOICES 32
-
-struct voice_alloc_info {
- int max_voice;
- int used_voices;
- int ptr; /* For device specific use */
- unsigned short map[SYNTH_MAX_VOICES]; /* (ch << 8) | (note+1) */
- int timestamp;
- int alloc_times[SYNTH_MAX_VOICES];
- };
-
-struct channel_info {
- int pgm_num;
- int bender_value;
- int bender_range;
- unsigned char controllers[128];
- };
-
-/*
- * Process wakeup reasons
- */
-#define WK_NONE 0x00
-#define WK_WAKEUP 0x01
-#define WK_TIMEOUT 0x02
-#define WK_SIGNAL 0x04
-#define WK_SLEEP 0x08
-#define WK_SELECT 0x10
-#define WK_ABORT 0x20
-
-#define OPEN_READ PCM_ENABLE_INPUT
-#define OPEN_WRITE PCM_ENABLE_OUTPUT
-#define OPEN_READWRITE (OPEN_READ|OPEN_WRITE)
-
-static inline int translate_mode(struct file *file)
-{
- if (OPEN_READ == (__force int)FMODE_READ &&
- OPEN_WRITE == (__force int)FMODE_WRITE)
- return (__force int)(file->f_mode & (FMODE_READ | FMODE_WRITE));
- else
- return ((file->f_mode & FMODE_READ) ? OPEN_READ : 0) |
- ((file->f_mode & FMODE_WRITE) ? OPEN_WRITE : 0);
-}
-
-#include "sound_calls.h"
-#include "dev_table.h"
-
-#ifndef DEB
-#define DEB(x)
-#endif
-
-#ifndef DDB
-#define DDB(x) do {} while (0)
-#endif
-
-#ifndef MDB
-#ifdef MODULE
-#define MDB(x) x
-#else
-#define MDB(x)
-#endif
-#endif
-
-#define TIMER_ARMED 121234
-#define TIMER_NOT_ARMED 1
-
-#define MAX_MEM_BLOCKS 1024
-
-#endif
diff --git a/sound/oss/sound_firmware.h b/sound/oss/sound_firmware.h
deleted file mode 100644
index 0a0cbfdfb855..000000000000
--- a/sound/oss/sound_firmware.h
+++ /dev/null
@@ -1,2 +0,0 @@
-extern int mod_firmware_load(const char *fn, char **fp);
-
diff --git a/sound/oss/sound_timer.c b/sound/oss/sound_timer.c
deleted file mode 100644
index 48cda6c4c257..000000000000
--- a/sound/oss/sound_timer.c
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * sound/oss/sound_timer.c
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-/*
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- */
-#include <linux/string.h>
-#include <linux/spinlock.h>
-
-#include "sound_config.h"
-
-static volatile int initialized, opened, tmr_running;
-static volatile time_t tmr_offs, tmr_ctr;
-static volatile unsigned long ticks_offs;
-static volatile int curr_tempo, curr_timebase;
-static volatile unsigned long curr_ticks;
-static volatile unsigned long next_event_time;
-static unsigned long prev_event_time;
-static volatile unsigned long usecs_per_tmr; /* Length of the current interval */
-
-static struct sound_lowlev_timer *tmr;
-static DEFINE_SPINLOCK(lock);
-
-static unsigned long tmr2ticks(int tmr_value)
-{
- /*
- * Convert timer ticks to MIDI ticks
- */
-
- unsigned long tmp;
- unsigned long scale;
-
- tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */
- scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */
- return (tmp + (scale / 2)) / scale;
-}
-
-void reprogram_timer(void)
-{
- unsigned long usecs_per_tick;
-
- /*
- * The user is changing the timer rate before setting a timer
- * slap, bad bad not allowed.
- */
-
- if(!tmr)
- return;
-
- usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
-
- /*
- * Don't kill the system by setting too high timer rate
- */
- if (usecs_per_tick < 2000)
- usecs_per_tick = 2000;
-
- usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick);
-}
-
-void sound_timer_syncinterval(unsigned int new_usecs)
-{
- /*
- * This routine is called by the hardware level if
- * the clock frequency has changed for some reason.
- */
- tmr_offs = tmr_ctr;
- ticks_offs += tmr2ticks(tmr_ctr);
- tmr_ctr = 0;
- usecs_per_tmr = new_usecs;
-}
-EXPORT_SYMBOL(sound_timer_syncinterval);
-
-static void tmr_reset(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&lock,flags);
- tmr_offs = 0;
- ticks_offs = 0;
- tmr_ctr = 0;
- next_event_time = (unsigned long) -1;
- prev_event_time = 0;
- curr_ticks = 0;
- spin_unlock_irqrestore(&lock,flags);
-}
-
-static int timer_open(int dev, int mode)
-{
- if (opened)
- return -EBUSY;
- tmr_reset();
- curr_tempo = 60;
- curr_timebase = 100;
- opened = 1;
- reprogram_timer();
- return 0;
-}
-
-static void timer_close(int dev)
-{
- opened = tmr_running = 0;
- tmr->tmr_disable(tmr->dev);
-}
-
-static int timer_event(int dev, unsigned char *event)
-{
- unsigned char cmd = event[1];
- unsigned long parm = *(int *) &event[4];
-
- switch (cmd)
- {
- case TMR_WAIT_REL:
- parm += prev_event_time;
- case TMR_WAIT_ABS:
- if (parm > 0)
- {
- long time;
-
- if (parm <= curr_ticks) /* It's the time */
- return TIMER_NOT_ARMED;
- time = parm;
- next_event_time = prev_event_time = time;
- return TIMER_ARMED;
- }
- break;
-
- case TMR_START:
- tmr_reset();
- tmr_running = 1;
- reprogram_timer();
- break;
-
- case TMR_STOP:
- tmr_running = 0;
- break;
-
- case TMR_CONTINUE:
- tmr_running = 1;
- reprogram_timer();
- break;
-
- case TMR_TEMPO:
- if (parm)
- {
- if (parm < 8)
- parm = 8;
- if (parm > 250)
- parm = 250;
- tmr_offs = tmr_ctr;
- ticks_offs += tmr2ticks(tmr_ctr);
- tmr_ctr = 0;
- curr_tempo = parm;
- reprogram_timer();
- }
- break;
-
- case TMR_ECHO:
- seq_copy_to_input(event, 8);
- break;
-
- default:;
- }
- return TIMER_NOT_ARMED;
-}
-
-static unsigned long timer_get_time(int dev)
-{
- if (!opened)
- return 0;
- return curr_ticks;
-}
-
-static int timer_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- int __user *p = arg;
- int val;
-
- switch (cmd)
- {
- case SNDCTL_TMR_SOURCE:
- val = TMR_INTERNAL;
- break;
-
- case SNDCTL_TMR_START:
- tmr_reset();
- tmr_running = 1;
- return 0;
-
- case SNDCTL_TMR_STOP:
- tmr_running = 0;
- return 0;
-
- case SNDCTL_TMR_CONTINUE:
- tmr_running = 1;
- return 0;
-
- case SNDCTL_TMR_TIMEBASE:
- if (get_user(val, p))
- return -EFAULT;
- if (val)
- {
- if (val < 1)
- val = 1;
- if (val > 1000)
- val = 1000;
- curr_timebase = val;
- }
- val = curr_timebase;
- break;
-
- case SNDCTL_TMR_TEMPO:
- if (get_user(val, p))
- return -EFAULT;
- if (val)
- {
- if (val < 8)
- val = 8;
- if (val > 250)
- val = 250;
- tmr_offs = tmr_ctr;
- ticks_offs += tmr2ticks(tmr_ctr);
- tmr_ctr = 0;
- curr_tempo = val;
- reprogram_timer();
- }
- val = curr_tempo;
- break;
-
- case SNDCTL_SEQ_CTRLRATE:
- if (get_user(val, p))
- return -EFAULT;
- if (val != 0) /* Can't change */
- return -EINVAL;
- val = ((curr_tempo * curr_timebase) + 30) / 60;
- break;
-
- case SNDCTL_SEQ_GETTIME:
- val = curr_ticks;
- break;
-
- case SNDCTL_TMR_METRONOME:
- default:
- return -EINVAL;
- }
- return put_user(val, p);
-}
-
-static void timer_arm(int dev, long time)
-{
- if (time < 0)
- time = curr_ticks + 1;
- else if (time <= curr_ticks) /* It's the time */
- return;
-
- next_event_time = prev_event_time = time;
- return;
-}
-
-static struct sound_timer_operations sound_timer =
-{
- .owner = THIS_MODULE,
- .info = {"Sound Timer", 0},
- .priority = 1, /* Priority */
- .devlink = 0, /* Local device link */
- .open = timer_open,
- .close = timer_close,
- .event = timer_event,
- .get_time = timer_get_time,
- .ioctl = timer_ioctl,
- .arm_timer = timer_arm
-};
-
-void sound_timer_interrupt(void)
-{
- unsigned long flags;
-
- if (!opened)
- return;
-
- tmr->tmr_restart(tmr->dev);
-
- if (!tmr_running)
- return;
-
- spin_lock_irqsave(&lock,flags);
- tmr_ctr++;
- curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
-
- if (curr_ticks >= next_event_time)
- {
- next_event_time = (unsigned long) -1;
- sequencer_timer(0);
- }
- spin_unlock_irqrestore(&lock,flags);
-}
-EXPORT_SYMBOL(sound_timer_interrupt);
-
-void sound_timer_init(struct sound_lowlev_timer *t, char *name)
-{
- int n;
-
- if (initialized)
- {
- if (t->priority <= tmr->priority)
- return; /* There is already a similar or better timer */
- tmr = t;
- return;
- }
- initialized = 1;
- tmr = t;
-
- n = sound_alloc_timerdev();
- if (n == -1)
- n = 0; /* Overwrite the system timer */
- strcpy(sound_timer.info.name, name);
- sound_timer_devs[n] = &sound_timer;
-}
-EXPORT_SYMBOL(sound_timer_init);
-
diff --git a/sound/oss/soundcard.c b/sound/oss/soundcard.c
deleted file mode 100644
index 46c0d03dbecc..000000000000
--- a/sound/oss/soundcard.c
+++ /dev/null
@@ -1,755 +0,0 @@
-/*
- * linux/sound/oss/soundcard.c
- *
- * Sound card driver for Linux
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- *
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * integrated sound_switch.c
- * Stefan Reinauer : integrated /proc/sound (equals to /dev/sndstat,
- * which should disappear in the near future)
- * Eric Dumas : devfs support (22-Jan-98) <dumas@linux.eu.org> with
- * fixups by C. Scott Ananian <cananian@alumni.princeton.edu>
- * Richard Gooch : moved common (non OSS-specific) devices to sound_core.c
- * Rob Riggs : Added persistent DMA buffers support (1998/10/17)
- * Christoph Hellwig : Some cleanup work (2000/03/01)
- */
-
-
-#include "sound_config.h"
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/fcntl.h>
-#include <linux/ctype.h>
-#include <linux/stddef.h>
-#include <linux/kmod.h>
-#include <linux/kernel.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <linux/wait.h>
-#include <linux/ioport.h>
-#include <linux/major.h>
-#include <linux/delay.h>
-#include <linux/proc_fs.h>
-#include <linux/mutex.h>
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/device.h>
-
-/*
- * This ought to be moved into include/asm/dma.h
- */
-#ifndef valid_dma
-#define valid_dma(n) ((n) >= 0 && (n) < MAX_DMA_CHANNELS && (n) != 4)
-#endif
-
-/*
- * Table for permanently allocated memory (used when unloading the module)
- */
-void * sound_mem_blocks[MAX_MEM_BLOCKS];
-static DEFINE_MUTEX(soundcard_mutex);
-int sound_nblocks = 0;
-
-/* Persistent DMA buffers */
-#ifdef CONFIG_SOUND_DMAP
-int sound_dmap_flag = 1;
-#else
-int sound_dmap_flag = 0;
-#endif
-
-static char dma_alloc_map[MAX_DMA_CHANNELS];
-
-#define DMA_MAP_UNAVAIL 0
-#define DMA_MAP_FREE 1
-#define DMA_MAP_BUSY 2
-
-
-unsigned long seq_time = 0; /* Time for /dev/sequencer */
-extern struct class *sound_class;
-
-/*
- * Table for configurable mixer volume handling
- */
-static mixer_vol_table mixer_vols[MAX_MIXER_DEV];
-static int num_mixer_volumes;
-
-int *load_mixer_volumes(char *name, int *levels, int present)
-{
- int i, n;
-
- for (i = 0; i < num_mixer_volumes; i++) {
- if (strcmp(name, mixer_vols[i].name) == 0) {
- if (present)
- mixer_vols[i].num = i;
- return mixer_vols[i].levels;
- }
- }
- if (num_mixer_volumes >= MAX_MIXER_DEV) {
- printk(KERN_ERR "Sound: Too many mixers (%s)\n", name);
- return levels;
- }
- n = num_mixer_volumes++;
-
- strcpy(mixer_vols[n].name, name);
-
- if (present)
- mixer_vols[n].num = n;
- else
- mixer_vols[n].num = -1;
-
- for (i = 0; i < 32; i++)
- mixer_vols[n].levels[i] = levels[i];
- return mixer_vols[n].levels;
-}
-EXPORT_SYMBOL(load_mixer_volumes);
-
-static int set_mixer_levels(void __user * arg)
-{
- /* mixer_vol_table is 174 bytes, so IMHO no reason to not allocate it on the stack */
- mixer_vol_table buf;
-
- if (__copy_from_user(&buf, arg, sizeof(buf)))
- return -EFAULT;
- load_mixer_volumes(buf.name, buf.levels, 0);
- if (__copy_to_user(arg, &buf, sizeof(buf)))
- return -EFAULT;
- return 0;
-}
-
-static int get_mixer_levels(void __user * arg)
-{
- int n;
-
- if (__get_user(n, (int __user *)(&(((mixer_vol_table __user *)arg)->num))))
- return -EFAULT;
- if (n < 0 || n >= num_mixer_volumes)
- return -EINVAL;
- if (__copy_to_user(arg, &mixer_vols[n], sizeof(mixer_vol_table)))
- return -EFAULT;
- return 0;
-}
-
-/* 4K page size but our output routines use some slack for overruns */
-#define PROC_BLOCK_SIZE (3*1024)
-
-static ssize_t sound_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
-{
- int dev = iminor(file->f_path.dentry->d_inode);
- int ret = -EINVAL;
-
- /*
- * The OSS drivers aren't remotely happy without this locking,
- * and unless someone fixes them when they are about to bite the
- * big one anyway, we might as well bandage here..
- */
-
- mutex_lock(&soundcard_mutex);
-
- DEB(printk("sound_read(dev=%d, count=%d)\n", dev, count));
- switch (dev & 0x0f) {
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- ret = audio_read(dev, file, buf, count);
- break;
-
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- ret = sequencer_read(dev, file, buf, count);
- break;
-
- case SND_DEV_MIDIN:
- ret = MIDIbuf_read(dev, file, buf, count);
- }
- mutex_unlock(&soundcard_mutex);
- return ret;
-}
-
-static ssize_t sound_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
-{
- int dev = iminor(file->f_path.dentry->d_inode);
- int ret = -EINVAL;
-
- mutex_lock(&soundcard_mutex);
- DEB(printk("sound_write(dev=%d, count=%d)\n", dev, count));
- switch (dev & 0x0f) {
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- ret = sequencer_write(dev, file, buf, count);
- break;
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- ret = audio_write(dev, file, buf, count);
- break;
-
- case SND_DEV_MIDIN:
- ret = MIDIbuf_write(dev, file, buf, count);
- break;
- }
- mutex_unlock(&soundcard_mutex);
- return ret;
-}
-
-static int sound_open(struct inode *inode, struct file *file)
-{
- int dev = iminor(inode);
- int retval;
-
- DEB(printk("sound_open(dev=%d)\n", dev));
- if ((dev >= SND_NDEVS) || (dev < 0)) {
- printk(KERN_ERR "Invalid minor device %d\n", dev);
- return -ENXIO;
- }
- mutex_lock(&soundcard_mutex);
- switch (dev & 0x0f) {
- case SND_DEV_CTL:
- dev >>= 4;
- if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) {
- request_module("mixer%d", dev);
- }
- retval = -ENXIO;
- if (dev && (dev >= num_mixers || mixer_devs[dev] == NULL))
- break;
-
- if (!try_module_get(mixer_devs[dev]->owner))
- break;
-
- retval = 0;
- break;
-
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- retval = sequencer_open(dev, file);
- break;
-
- case SND_DEV_MIDIN:
- retval = MIDIbuf_open(dev, file);
- break;
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- retval = audio_open(dev, file);
- break;
-
- default:
- printk(KERN_ERR "Invalid minor device %d\n", dev);
- retval = -ENXIO;
- }
-
- mutex_unlock(&soundcard_mutex);
- return retval;
-}
-
-static int sound_release(struct inode *inode, struct file *file)
-{
- int dev = iminor(inode);
-
- mutex_lock(&soundcard_mutex);
- DEB(printk("sound_release(dev=%d)\n", dev));
- switch (dev & 0x0f) {
- case SND_DEV_CTL:
- module_put(mixer_devs[dev >> 4]->owner);
- break;
-
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- sequencer_release(dev, file);
- break;
-
- case SND_DEV_MIDIN:
- MIDIbuf_release(dev, file);
- break;
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- audio_release(dev, file);
- break;
-
- default:
- printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev);
- }
- mutex_unlock(&soundcard_mutex);
-
- return 0;
-}
-
-static int get_mixer_info(int dev, void __user *arg)
-{
- mixer_info info;
- memset(&info, 0, sizeof(info));
- strlcpy(info.id, mixer_devs[dev]->id, sizeof(info.id));
- strlcpy(info.name, mixer_devs[dev]->name, sizeof(info.name));
- info.modify_counter = mixer_devs[dev]->modify_counter;
- if (__copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
-}
-
-static int get_old_mixer_info(int dev, void __user *arg)
-{
- _old_mixer_info info;
- memset(&info, 0, sizeof(info));
- strlcpy(info.id, mixer_devs[dev]->id, sizeof(info.id));
- strlcpy(info.name, mixer_devs[dev]->name, sizeof(info.name));
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
-}
-
-static int sound_mixer_ioctl(int mixdev, unsigned int cmd, void __user *arg)
-{
- if (mixdev < 0 || mixdev >= MAX_MIXER_DEV)
- return -ENXIO;
- /* Try to load the mixer... */
- if (mixer_devs[mixdev] == NULL) {
- request_module("mixer%d", mixdev);
- }
- if (mixdev >= num_mixers || !mixer_devs[mixdev])
- return -ENXIO;
- if (cmd == SOUND_MIXER_INFO)
- return get_mixer_info(mixdev, arg);
- if (cmd == SOUND_OLD_MIXER_INFO)
- return get_old_mixer_info(mixdev, arg);
- if (_SIOC_DIR(cmd) & _SIOC_WRITE)
- mixer_devs[mixdev]->modify_counter++;
- if (!mixer_devs[mixdev]->ioctl)
- return -EINVAL;
- return mixer_devs[mixdev]->ioctl(mixdev, cmd, arg);
-}
-
-static long sound_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- int len = 0, dtype;
- int dev = iminor(file->f_dentry->d_inode);
- long ret = -EINVAL;
- void __user *p = (void __user *)arg;
-
- if (_SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0) {
- /*
- * Have to validate the address given by the process.
- */
- len = _SIOC_SIZE(cmd);
- if (len < 1 || len > 65536 || !p)
- return -EFAULT;
- if (_SIOC_DIR(cmd) & _SIOC_WRITE)
- if (!access_ok(VERIFY_READ, p, len))
- return -EFAULT;
- if (_SIOC_DIR(cmd) & _SIOC_READ)
- if (!access_ok(VERIFY_WRITE, p, len))
- return -EFAULT;
- }
- DEB(printk("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
- if (cmd == OSS_GETVERSION)
- return __put_user(SOUND_VERSION, (int __user *)p);
-
- mutex_lock(&soundcard_mutex);
- if (_IOC_TYPE(cmd) == 'M' && num_mixers > 0 && /* Mixer ioctl */
- (dev & 0x0f) != SND_DEV_CTL) {
- dtype = dev & 0x0f;
- switch (dtype) {
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- ret = sound_mixer_ioctl(audio_devs[dev >> 4]->mixer_dev,
- cmd, p);
- break;
- default:
- ret = sound_mixer_ioctl(dev >> 4, cmd, p);
- break;
- }
- mutex_unlock(&soundcard_mutex);
- return ret;
- }
-
- switch (dev & 0x0f) {
- case SND_DEV_CTL:
- if (cmd == SOUND_MIXER_GETLEVELS)
- ret = get_mixer_levels(p);
- else if (cmd == SOUND_MIXER_SETLEVELS)
- ret = set_mixer_levels(p);
- else
- ret = sound_mixer_ioctl(dev >> 4, cmd, p);
- break;
-
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- ret = sequencer_ioctl(dev, file, cmd, p);
- break;
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- ret = audio_ioctl(dev, file, cmd, p);
- break;
-
- case SND_DEV_MIDIN:
- ret = MIDIbuf_ioctl(dev, file, cmd, p);
- break;
-
- }
- mutex_unlock(&soundcard_mutex);
- return ret;
-}
-
-static unsigned int sound_poll(struct file *file, poll_table * wait)
-{
- struct inode *inode = file->f_path.dentry->d_inode;
- int dev = iminor(inode);
-
- DEB(printk("sound_poll(dev=%d)\n", dev));
- switch (dev & 0x0f) {
- case SND_DEV_SEQ:
- case SND_DEV_SEQ2:
- return sequencer_poll(dev, file, wait);
-
- case SND_DEV_MIDIN:
- return MIDIbuf_poll(dev, file, wait);
-
- case SND_DEV_DSP:
- case SND_DEV_DSP16:
- case SND_DEV_AUDIO:
- return DMAbuf_poll(file, dev >> 4, wait);
- }
- return 0;
-}
-
-static int sound_mmap(struct file *file, struct vm_area_struct *vma)
-{
- int dev_class;
- unsigned long size;
- struct dma_buffparms *dmap = NULL;
- int dev = iminor(file->f_path.dentry->d_inode);
-
- dev_class = dev & 0x0f;
- dev >>= 4;
-
- if (dev_class != SND_DEV_DSP && dev_class != SND_DEV_DSP16 && dev_class != SND_DEV_AUDIO) {
- printk(KERN_ERR "Sound: mmap() not supported for other than audio devices\n");
- return -EINVAL;
- }
- mutex_lock(&soundcard_mutex);
- if (vma->vm_flags & VM_WRITE) /* Map write and read/write to the output buf */
- dmap = audio_devs[dev]->dmap_out;
- else if (vma->vm_flags & VM_READ)
- dmap = audio_devs[dev]->dmap_in;
- else {
- printk(KERN_ERR "Sound: Undefined mmap() access\n");
- mutex_unlock(&soundcard_mutex);
- return -EINVAL;
- }
-
- if (dmap == NULL) {
- printk(KERN_ERR "Sound: mmap() error. dmap == NULL\n");
- mutex_unlock(&soundcard_mutex);
- return -EIO;
- }
- if (dmap->raw_buf == NULL) {
- printk(KERN_ERR "Sound: mmap() called when raw_buf == NULL\n");
- mutex_unlock(&soundcard_mutex);
- return -EIO;
- }
- if (dmap->mapping_flags) {
- printk(KERN_ERR "Sound: mmap() called twice for the same DMA buffer\n");
- mutex_unlock(&soundcard_mutex);
- return -EIO;
- }
- if (vma->vm_pgoff != 0) {
- printk(KERN_ERR "Sound: mmap() offset must be 0.\n");
- mutex_unlock(&soundcard_mutex);
- return -EINVAL;
- }
- size = vma->vm_end - vma->vm_start;
-
- if (size != dmap->bytes_in_use) {
- printk(KERN_WARNING "Sound: mmap() size = %ld. Should be %d\n", size, dmap->bytes_in_use);
- }
- if (remap_pfn_range(vma, vma->vm_start,
- virt_to_phys(dmap->raw_buf) >> PAGE_SHIFT,
- vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
- mutex_unlock(&soundcard_mutex);
- return -EAGAIN;
- }
-
- dmap->mapping_flags |= DMA_MAP_MAPPED;
-
- if( audio_devs[dev]->d->mmap)
- audio_devs[dev]->d->mmap(dev);
-
- memset(dmap->raw_buf,
- dmap->neutral_byte,
- dmap->bytes_in_use);
- mutex_unlock(&soundcard_mutex);
- return 0;
-}
-
-const struct file_operations oss_sound_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = sound_read,
- .write = sound_write,
- .poll = sound_poll,
- .unlocked_ioctl = sound_ioctl,
- .mmap = sound_mmap,
- .open = sound_open,
- .release = sound_release,
-};
-
-/*
- * Create the required special subdevices
- */
-
-static int create_special_devices(void)
-{
- int seq1,seq2;
- seq1=register_sound_special(&oss_sound_fops, 1);
- if(seq1==-1)
- goto bad;
- seq2=register_sound_special(&oss_sound_fops, 8);
- if(seq2!=-1)
- return 0;
- unregister_sound_special(1);
-bad:
- return -1;
-}
-
-
-/* These device names follow the official Linux device list,
- * Documentation/devices.txt. Let us know if there are other
- * common names we should support for compatibility.
- * Only those devices not created by the generic code in sound_core.c are
- * registered here.
- */
-static const struct {
- unsigned short minor;
- char *name;
- umode_t mode;
- int *num;
-} dev_list[] = { /* list of minor devices */
-/* seems to be some confusion here -- this device is not in the device list */
- {SND_DEV_DSP16, "dspW", S_IWUGO | S_IRUSR | S_IRGRP,
- &num_audiodevs},
- {SND_DEV_AUDIO, "audio", S_IWUGO | S_IRUSR | S_IRGRP,
- &num_audiodevs},
-};
-
-static int dmabuf;
-static int dmabug;
-
-module_param(dmabuf, int, 0444);
-module_param(dmabug, int, 0444);
-
-static int __init oss_init(void)
-{
- int err;
- int i, j;
-
-#ifdef CONFIG_PCI
- if(dmabug)
- isa_dma_bridge_buggy = dmabug;
-#endif
-
- err = create_special_devices();
- if (err) {
- printk(KERN_ERR "sound: driver already loaded/included in kernel\n");
- return err;
- }
-
- /* Protecting the innocent */
- sound_dmap_flag = (dmabuf > 0 ? 1 : 0);
-
- for (i = 0; i < ARRAY_SIZE(dev_list); i++) {
- device_create(sound_class, NULL,
- MKDEV(SOUND_MAJOR, dev_list[i].minor), NULL,
- "%s", dev_list[i].name);
-
- if (!dev_list[i].num)
- continue;
-
- for (j = 1; j < *dev_list[i].num; j++)
- device_create(sound_class, NULL,
- MKDEV(SOUND_MAJOR,
- dev_list[i].minor + (j*0x10)),
- NULL, "%s%d", dev_list[i].name, j);
- }
-
- if (sound_nblocks >= MAX_MEM_BLOCKS - 1)
- printk(KERN_ERR "Sound warning: Deallocation table was too small.\n");
-
- return 0;
-}
-
-static void __exit oss_cleanup(void)
-{
- int i, j;
-
- for (i = 0; i < ARRAY_SIZE(dev_list); i++) {
- device_destroy(sound_class, MKDEV(SOUND_MAJOR, dev_list[i].minor));
- if (!dev_list[i].num)
- continue;
- for (j = 1; j < *dev_list[i].num; j++)
- device_destroy(sound_class, MKDEV(SOUND_MAJOR, dev_list[i].minor + (j*0x10)));
- }
-
- unregister_sound_special(1);
- unregister_sound_special(8);
-
- sound_stop_timer();
-
- sequencer_unload();
-
- for (i = 0; i < MAX_DMA_CHANNELS; i++)
- if (dma_alloc_map[i] != DMA_MAP_UNAVAIL) {
- printk(KERN_ERR "Sound: Hmm, DMA%d was left allocated - fixed\n", i);
- sound_free_dma(i);
- }
-
- for (i = 0; i < sound_nblocks; i++)
- vfree(sound_mem_blocks[i]);
-
-}
-
-module_init(oss_init);
-module_exit(oss_cleanup);
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("OSS Sound subsystem");
-MODULE_AUTHOR("Hannu Savolainen, et al.");
-
-
-int sound_alloc_dma(int chn, char *deviceID)
-{
- int err;
-
- if ((err = request_dma(chn, deviceID)) != 0)
- return err;
-
- dma_alloc_map[chn] = DMA_MAP_FREE;
-
- return 0;
-}
-EXPORT_SYMBOL(sound_alloc_dma);
-
-int sound_open_dma(int chn, char *deviceID)
-{
- if (!valid_dma(chn)) {
- printk(KERN_ERR "sound_open_dma: Invalid DMA channel %d\n", chn);
- return 1;
- }
-
- if (dma_alloc_map[chn] != DMA_MAP_FREE) {
- printk("sound_open_dma: DMA channel %d busy or not allocated (%d)\n", chn, dma_alloc_map[chn]);
- return 1;
- }
- dma_alloc_map[chn] = DMA_MAP_BUSY;
- return 0;
-}
-EXPORT_SYMBOL(sound_open_dma);
-
-void sound_free_dma(int chn)
-{
- if (dma_alloc_map[chn] == DMA_MAP_UNAVAIL) {
- /* printk( "sound_free_dma: Bad access to DMA channel %d\n", chn); */
- return;
- }
- free_dma(chn);
- dma_alloc_map[chn] = DMA_MAP_UNAVAIL;
-}
-EXPORT_SYMBOL(sound_free_dma);
-
-void sound_close_dma(int chn)
-{
- if (dma_alloc_map[chn] != DMA_MAP_BUSY) {
- printk(KERN_ERR "sound_close_dma: Bad access to DMA channel %d\n", chn);
- return;
- }
- dma_alloc_map[chn] = DMA_MAP_FREE;
-}
-EXPORT_SYMBOL(sound_close_dma);
-
-static void do_sequencer_timer(unsigned long dummy)
-{
- sequencer_timer(0);
-}
-
-
-static DEFINE_TIMER(seq_timer, do_sequencer_timer, 0, 0);
-
-void request_sound_timer(int count)
-{
- extern unsigned long seq_time;
-
- if (count < 0) {
- seq_timer.expires = (-count) + jiffies;
- add_timer(&seq_timer);
- return;
- }
- count += seq_time;
-
- count -= jiffies;
-
- if (count < 1)
- count = 1;
-
- seq_timer.expires = (count) + jiffies;
- add_timer(&seq_timer);
-}
-
-void sound_stop_timer(void)
-{
- del_timer(&seq_timer);
-}
-
-void conf_printf(char *name, struct address_info *hw_config)
-{
-#ifndef CONFIG_SOUND_TRACEINIT
- return;
-#else
- printk("<%s> at 0x%03x", name, hw_config->io_base);
-
- if (hw_config->irq)
- printk(" irq %d", (hw_config->irq > 0) ? hw_config->irq : -hw_config->irq);
-
- if (hw_config->dma != -1 || hw_config->dma2 != -1)
- {
- printk(" dma %d", hw_config->dma);
- if (hw_config->dma2 != -1)
- printk(",%d", hw_config->dma2);
- }
- printk("\n");
-#endif
-}
-EXPORT_SYMBOL(conf_printf);
-
-void conf_printf2(char *name, int base, int irq, int dma, int dma2)
-{
-#ifndef CONFIG_SOUND_TRACEINIT
- return;
-#else
- printk("<%s> at 0x%03x", name, base);
-
- if (irq)
- printk(" irq %d", (irq > 0) ? irq : -irq);
-
- if (dma != -1 || dma2 != -1)
- {
- printk(" dma %d", dma);
- if (dma2 != -1)
- printk(",%d", dma2);
- }
- printk("\n");
-#endif
-}
-EXPORT_SYMBOL(conf_printf2);
-
diff --git a/sound/oss/soundvers.h b/sound/oss/soundvers.h
deleted file mode 100644
index e9084d2f46a9..000000000000
--- a/sound/oss/soundvers.h
+++ /dev/null
@@ -1,2 +0,0 @@
-#define SOUND_VERSION_STRING "3.8s2++-971130"
-#define SOUND_INTERNAL_VERSION 0x030804
diff --git a/sound/oss/swarm_cs4297a.c b/sound/oss/swarm_cs4297a.c
deleted file mode 100644
index 44357d877a27..000000000000
--- a/sound/oss/swarm_cs4297a.c
+++ /dev/null
@@ -1,2768 +0,0 @@
-/*******************************************************************************
-*
-* "swarm_cs4297a.c" -- Cirrus Logic-Crystal CS4297a linux audio driver.
-*
-* Copyright (C) 2001 Broadcom Corporation.
-* Copyright (C) 2000,2001 Cirrus Logic Corp.
-* -- adapted from drivers by Thomas Sailer,
-* -- but don't bug him; Problems should go to:
-* -- tom woller (twoller@crystal.cirrus.com) or
-* (audio@crystal.cirrus.com).
-* -- adapted from cs4281 PCI driver for cs4297a on
-* BCM1250 Synchronous Serial interface
-* (Kip Walker, Broadcom Corp.)
-* Copyright (C) 2004 Maciej W. Rozycki
-* Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org)
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*
-* Module command line parameters:
-* none
-*
-* Supported devices:
-* /dev/dsp standard /dev/dsp device, (mostly) OSS compatible
-* /dev/mixer standard /dev/mixer device, (mostly) OSS compatible
-* /dev/midi simple MIDI UART interface, no ioctl
-*
-* Modification History
-* 08/20/00 trw - silence and no stopping DAC until release
-* 08/23/00 trw - added CS_DBG statements, fix interrupt hang issue on DAC stop.
-* 09/18/00 trw - added 16bit only record with conversion
-* 09/24/00 trw - added Enhanced Full duplex (separate simultaneous
-* capture/playback rates)
-* 10/03/00 trw - fixed mmap (fixed GRECORD and the XMMS mmap test plugin
-* libOSSm.so)
-* 10/11/00 trw - modified for 2.4.0-test9 kernel enhancements (NR_MAP removal)
-* 11/03/00 trw - fixed interrupt loss/stutter, added debug.
-* 11/10/00 bkz - added __devinit to cs4297a_hw_init()
-* 11/10/00 trw - fixed SMP and capture spinlock hang.
-* 12/04/00 trw - cleaned up CSDEBUG flags and added "defaultorder" moduleparm.
-* 12/05/00 trw - fixed polling (myth2), and added underrun swptr fix.
-* 12/08/00 trw - added PM support.
-* 12/14/00 trw - added wrapper code, builds under 2.4.0, 2.2.17-20, 2.2.17-8
-* (RH/Dell base), 2.2.18, 2.2.12. cleaned up code mods by ident.
-* 12/19/00 trw - added PM support for 2.2 base (apm_callback). other PM cleanup.
-* 12/21/00 trw - added fractional "defaultorder" inputs. if >100 then use
-* defaultorder-100 as power of 2 for the buffer size. example:
-* 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size.
-*
-*******************************************************************************/
-
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/delay.h>
-#include <linux/sound.h>
-#include <linux/slab.h>
-#include <linux/soundcard.h>
-#include <linux/ac97_codec.h>
-#include <linux/pci.h>
-#include <linux/bitops.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/poll.h>
-#include <linux/mutex.h>
-#include <linux/kernel.h>
-
-#include <asm/byteorder.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-
-#include <asm/sibyte/sb1250_regs.h>
-#include <asm/sibyte/sb1250_int.h>
-#include <asm/sibyte/sb1250_dma.h>
-#include <asm/sibyte/sb1250_scd.h>
-#include <asm/sibyte/sb1250_syncser.h>
-#include <asm/sibyte/sb1250_mac.h>
-#include <asm/sibyte/sb1250.h>
-
-struct cs4297a_state;
-
-static DEFINE_MUTEX(swarm_cs4297a_mutex);
-static void stop_dac(struct cs4297a_state *s);
-static void stop_adc(struct cs4297a_state *s);
-static void start_dac(struct cs4297a_state *s);
-static void start_adc(struct cs4297a_state *s);
-#undef OSS_DOCUMENTED_MIXER_SEMANTICS
-
-// ---------------------------------------------------------------------
-
-#define CS4297a_MAGIC 0xf00beef1
-
-// buffer order determines the size of the dma buffer for the driver.
-// under Linux, a smaller buffer allows more responsiveness from many of the
-// applications (e.g. games). A larger buffer allows some of the apps (esound)
-// to not underrun the dma buffer as easily. As default, use 32k (order=3)
-// rather than 64k as some of the games work more responsively.
-// log base 2( buff sz = 32k).
-
-//
-// Turn on/off debugging compilation by commenting out "#define CSDEBUG"
-//
-#define CSDEBUG 0
-#if CSDEBUG
-#define CSDEBUG_INTERFACE 1
-#else
-#undef CSDEBUG_INTERFACE
-#endif
-//
-// cs_debugmask areas
-//
-#define CS_INIT 0x00000001 // initialization and probe functions
-#define CS_ERROR 0x00000002 // tmp debugging bit placeholder
-#define CS_INTERRUPT 0x00000004 // interrupt handler (separate from all other)
-#define CS_FUNCTION 0x00000008 // enter/leave functions
-#define CS_WAVE_WRITE 0x00000010 // write information for wave
-#define CS_WAVE_READ 0x00000020 // read information for wave
-#define CS_AC97 0x00000040 // AC97 register access
-#define CS_DESCR 0x00000080 // descriptor management
-#define CS_OPEN 0x00000400 // all open functions in the driver
-#define CS_RELEASE 0x00000800 // all release functions in the driver
-#define CS_PARMS 0x00001000 // functional and operational parameters
-#define CS_IOCTL 0x00002000 // ioctl (non-mixer)
-#define CS_TMP 0x10000000 // tmp debug mask bit
-
-//
-// CSDEBUG is usual mode is set to 1, then use the
-// cs_debuglevel and cs_debugmask to turn on or off debugging.
-// Debug level of 1 has been defined to be kernel errors and info
-// that should be printed on any released driver.
-//
-#if CSDEBUG
-#define CS_DBGOUT(mask,level,x) if((cs_debuglevel >= (level)) && ((mask) & cs_debugmask) ) {x;}
-#else
-#define CS_DBGOUT(mask,level,x)
-#endif
-
-#if CSDEBUG
-static unsigned long cs_debuglevel = 4; // levels range from 1-9
-static unsigned long cs_debugmask = CS_INIT /*| CS_IOCTL*/;
-module_param(cs_debuglevel, int, 0);
-module_param(cs_debugmask, int, 0);
-#endif
-#define CS_TRUE 1
-#define CS_FALSE 0
-
-#define CS_TYPE_ADC 0
-#define CS_TYPE_DAC 1
-
-#define SER_BASE (A_SER_BASE_1 + KSEG1)
-#define SS_CSR(t) (SER_BASE+t)
-#define SS_TXTBL(t) (SER_BASE+R_SER_TX_TABLE_BASE+(t*8))
-#define SS_RXTBL(t) (SER_BASE+R_SER_RX_TABLE_BASE+(t*8))
-
-#define FRAME_BYTES 32
-#define FRAME_SAMPLE_BYTES 4
-
-/* Should this be variable? */
-#define SAMPLE_BUF_SIZE (16*1024)
-#define SAMPLE_FRAME_COUNT (SAMPLE_BUF_SIZE / FRAME_SAMPLE_BYTES)
-/* The driver can explode/shrink the frames to/from a smaller sample
- buffer */
-#define DMA_BLOAT_FACTOR 1
-#define DMA_DESCR (SAMPLE_FRAME_COUNT / DMA_BLOAT_FACTOR)
-#define DMA_BUF_SIZE (DMA_DESCR * FRAME_BYTES)
-
-/* Use the maxmium count (255 == 5.1 ms between interrupts) */
-#define DMA_INT_CNT ((1 << S_DMA_INT_PKTCNT) - 1)
-
-/* Figure this out: how many TX DMAs ahead to schedule a reg access */
-#define REG_LATENCY 150
-
-#define FRAME_TX_US 20
-
-#define SERDMA_NEXTBUF(d,f) (((d)->f+1) % (d)->ringsz)
-
-static const char invalid_magic[] =
- KERN_CRIT "cs4297a: invalid magic value\n";
-
-#define VALIDATE_STATE(s) \
-({ \
- if (!(s) || (s)->magic != CS4297a_MAGIC) { \
- printk(invalid_magic); \
- return -ENXIO; \
- } \
-})
-
-struct list_head cs4297a_devs = { &cs4297a_devs, &cs4297a_devs };
-
-typedef struct serdma_descr_s {
- u64 descr_a;
- u64 descr_b;
-} serdma_descr_t;
-
-typedef unsigned long paddr_t;
-
-typedef struct serdma_s {
- unsigned ringsz;
- serdma_descr_t *descrtab;
- serdma_descr_t *descrtab_end;
- paddr_t descrtab_phys;
-
- serdma_descr_t *descr_add;
- serdma_descr_t *descr_rem;
-
- u64 *dma_buf; // buffer for DMA contents (frames)
- paddr_t dma_buf_phys;
- u16 *sample_buf; // tmp buffer for sample conversions
- u16 *sb_swptr;
- u16 *sb_hwptr;
- u16 *sb_end;
-
- dma_addr_t dmaaddr;
-// unsigned buforder; // Log base 2 of 'dma_buf' size in bytes..
- unsigned numfrag; // # of 'fragments' in the buffer.
- unsigned fragshift; // Log base 2 of fragment size.
- unsigned hwptr, swptr;
- unsigned total_bytes; // # bytes process since open.
- unsigned blocks; // last returned blocks value GETOPTR
- unsigned wakeup; // interrupt occurred on block
- int count;
- unsigned underrun; // underrun flag
- unsigned error; // over/underrun
- wait_queue_head_t wait;
- wait_queue_head_t reg_wait;
- // redundant, but makes calculations easier
- unsigned fragsize; // 2**fragshift..
- unsigned sbufsz; // 2**buforder.
- unsigned fragsamples;
- // OSS stuff
- unsigned mapped:1; // Buffer mapped in cs4297a_mmap()?
- unsigned ready:1; // prog_dmabuf_dac()/adc() successful?
- unsigned endcleared:1;
- unsigned type:1; // adc or dac buffer (CS_TYPE_XXX)
- unsigned ossfragshift;
- int ossmaxfrags;
- unsigned subdivision;
-} serdma_t;
-
-struct cs4297a_state {
- // magic
- unsigned int magic;
-
- struct list_head list;
-
- // soundcore stuff
- int dev_audio;
- int dev_mixer;
-
- // hardware resources
- unsigned int irq;
-
- struct {
- unsigned int rx_ovrrn; /* FIFO */
- unsigned int rx_overflow; /* staging buffer */
- unsigned int tx_underrun;
- unsigned int rx_bad;
- unsigned int rx_good;
- } stats;
-
- // mixer registers
- struct {
- unsigned short vol[10];
- unsigned int recsrc;
- unsigned int modcnt;
- unsigned short micpreamp;
- } mix;
-
- // wave stuff
- struct properties {
- unsigned fmt;
- unsigned fmt_original; // original requested format
- unsigned channels;
- unsigned rate;
- } prop_dac, prop_adc;
- unsigned conversion:1; // conversion from 16 to 8 bit in progress
- unsigned ena;
- spinlock_t lock;
- struct mutex open_mutex;
- struct mutex open_sem_adc;
- struct mutex open_sem_dac;
- fmode_t open_mode;
- wait_queue_head_t open_wait;
- wait_queue_head_t open_wait_adc;
- wait_queue_head_t open_wait_dac;
-
- dma_addr_t dmaaddr_sample_buf;
- unsigned buforder_sample_buf; // Log base 2 of 'dma_buf' size in bytes..
-
- serdma_t dma_dac, dma_adc;
-
- volatile u16 read_value;
- volatile u16 read_reg;
- volatile u64 reg_request;
-};
-
-#if 1
-#define prog_codec(a,b)
-#define dealloc_dmabuf(a,b);
-#endif
-
-static int prog_dmabuf_adc(struct cs4297a_state *s)
-{
- s->dma_adc.ready = 1;
- return 0;
-}
-
-
-static int prog_dmabuf_dac(struct cs4297a_state *s)
-{
- s->dma_dac.ready = 1;
- return 0;
-}
-
-static void clear_advance(void *buf, unsigned bsize, unsigned bptr,
- unsigned len, unsigned char c)
-{
- if (bptr + len > bsize) {
- unsigned x = bsize - bptr;
- memset(((char *) buf) + bptr, c, x);
- bptr = 0;
- len -= x;
- }
- CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO
- "cs4297a: clear_advance(): memset %d at 0x%.8x for %d size \n",
- (unsigned)c, (unsigned)((char *) buf) + bptr, len));
- memset(((char *) buf) + bptr, c, len);
-}
-
-#if CSDEBUG
-
-// DEBUG ROUTINES
-
-#define SOUND_MIXER_CS_GETDBGLEVEL _SIOWR('M',120, int)
-#define SOUND_MIXER_CS_SETDBGLEVEL _SIOWR('M',121, int)
-#define SOUND_MIXER_CS_GETDBGMASK _SIOWR('M',122, int)
-#define SOUND_MIXER_CS_SETDBGMASK _SIOWR('M',123, int)
-
-static void cs_printioctl(unsigned int x)
-{
- unsigned int i;
- unsigned char vidx;
- // Index of mixtable1[] member is Device ID
- // and must be <= SOUND_MIXER_NRDEVICES.
- // Value of array member is index into s->mix.vol[]
- static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = {
- [SOUND_MIXER_PCM] = 1, // voice
- [SOUND_MIXER_LINE1] = 2, // AUX
- [SOUND_MIXER_CD] = 3, // CD
- [SOUND_MIXER_LINE] = 4, // Line
- [SOUND_MIXER_SYNTH] = 5, // FM
- [SOUND_MIXER_MIC] = 6, // Mic
- [SOUND_MIXER_SPEAKER] = 7, // Speaker
- [SOUND_MIXER_RECLEV] = 8, // Recording level
- [SOUND_MIXER_VOLUME] = 9 // Master Volume
- };
-
- switch (x) {
- case SOUND_MIXER_CS_GETDBGMASK:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_CS_GETDBGMASK:\n"));
- break;
- case SOUND_MIXER_CS_GETDBGLEVEL:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_CS_GETDBGLEVEL:\n"));
- break;
- case SOUND_MIXER_CS_SETDBGMASK:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_CS_SETDBGMASK:\n"));
- break;
- case SOUND_MIXER_CS_SETDBGLEVEL:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_CS_SETDBGLEVEL:\n"));
- break;
- case OSS_GETVERSION:
- CS_DBGOUT(CS_IOCTL, 4, printk("OSS_GETVERSION:\n"));
- break;
- case SNDCTL_DSP_SYNC:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SYNC:\n"));
- break;
- case SNDCTL_DSP_SETDUPLEX:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETDUPLEX:\n"));
- break;
- case SNDCTL_DSP_GETCAPS:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETCAPS:\n"));
- break;
- case SNDCTL_DSP_RESET:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_RESET:\n"));
- break;
- case SNDCTL_DSP_SPEED:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SPEED:\n"));
- break;
- case SNDCTL_DSP_STEREO:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_STEREO:\n"));
- break;
- case SNDCTL_DSP_CHANNELS:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CHANNELS:\n"));
- break;
- case SNDCTL_DSP_GETFMTS:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETFMTS:\n"));
- break;
- case SNDCTL_DSP_SETFMT:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFMT:\n"));
- break;
- case SNDCTL_DSP_POST:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_POST:\n"));
- break;
- case SNDCTL_DSP_GETTRIGGER:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETTRIGGER:\n"));
- break;
- case SNDCTL_DSP_SETTRIGGER:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETTRIGGER:\n"));
- break;
- case SNDCTL_DSP_GETOSPACE:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOSPACE:\n"));
- break;
- case SNDCTL_DSP_GETISPACE:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETISPACE:\n"));
- break;
- case SNDCTL_DSP_NONBLOCK:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_NONBLOCK:\n"));
- break;
- case SNDCTL_DSP_GETODELAY:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETODELAY:\n"));
- break;
- case SNDCTL_DSP_GETIPTR:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETIPTR:\n"));
- break;
- case SNDCTL_DSP_GETOPTR:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOPTR:\n"));
- break;
- case SNDCTL_DSP_GETBLKSIZE:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETBLKSIZE:\n"));
- break;
- case SNDCTL_DSP_SETFRAGMENT:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SNDCTL_DSP_SETFRAGMENT:\n"));
- break;
- case SNDCTL_DSP_SUBDIVIDE:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SUBDIVIDE:\n"));
- break;
- case SOUND_PCM_READ_RATE:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_RATE:\n"));
- break;
- case SOUND_PCM_READ_CHANNELS:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_PCM_READ_CHANNELS:\n"));
- break;
- case SOUND_PCM_READ_BITS:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_BITS:\n"));
- break;
- case SOUND_PCM_WRITE_FILTER:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_PCM_WRITE_FILTER:\n"));
- break;
- case SNDCTL_DSP_SETSYNCRO:
- CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETSYNCRO:\n"));
- break;
- case SOUND_PCM_READ_FILTER:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER:\n"));
- break;
- case SOUND_MIXER_PRIVATE1:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1:\n"));
- break;
- case SOUND_MIXER_PRIVATE2:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE2:\n"));
- break;
- case SOUND_MIXER_PRIVATE3:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE3:\n"));
- break;
- case SOUND_MIXER_PRIVATE4:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE4:\n"));
- break;
- case SOUND_MIXER_PRIVATE5:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE5:\n"));
- break;
- case SOUND_MIXER_INFO:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_INFO:\n"));
- break;
- case SOUND_OLD_MIXER_INFO:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_OLD_MIXER_INFO:\n"));
- break;
-
- default:
- switch (_IOC_NR(x)) {
- case SOUND_MIXER_VOLUME:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_VOLUME:\n"));
- break;
- case SOUND_MIXER_SPEAKER:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_SPEAKER:\n"));
- break;
- case SOUND_MIXER_RECLEV:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_RECLEV:\n"));
- break;
- case SOUND_MIXER_MIC:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_MIC:\n"));
- break;
- case SOUND_MIXER_SYNTH:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_SYNTH:\n"));
- break;
- case SOUND_MIXER_RECSRC:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_RECSRC:\n"));
- break;
- case SOUND_MIXER_DEVMASK:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_DEVMASK:\n"));
- break;
- case SOUND_MIXER_RECMASK:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_RECMASK:\n"));
- break;
- case SOUND_MIXER_STEREODEVS:
- CS_DBGOUT(CS_IOCTL, 4,
- printk("SOUND_MIXER_STEREODEVS:\n"));
- break;
- case SOUND_MIXER_CAPS:
- CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CAPS:\n"));
- break;
- default:
- i = _IOC_NR(x);
- if (i >= SOUND_MIXER_NRDEVICES
- || !(vidx = mixtable1[i])) {
- CS_DBGOUT(CS_IOCTL, 4, printk
- ("UNKNOWN IOCTL: 0x%.8x NR=%d\n",
- x, i));
- } else {
- CS_DBGOUT(CS_IOCTL, 4, printk
- ("SOUND_MIXER_IOCTL AC9x: 0x%.8x NR=%d\n",
- x, i));
- }
- break;
- }
- }
-}
-#endif
-
-
-static int ser_init(struct cs4297a_state *s)
-{
- int i;
-
- CS_DBGOUT(CS_INIT, 2,
- printk(KERN_INFO "cs4297a: Setting up serial parameters\n"));
-
- __raw_writeq(M_SYNCSER_CMD_RX_RESET | M_SYNCSER_CMD_TX_RESET, SS_CSR(R_SER_CMD));
-
- __raw_writeq(M_SYNCSER_MSB_FIRST, SS_CSR(R_SER_MODE));
- __raw_writeq(32, SS_CSR(R_SER_MINFRM_SZ));
- __raw_writeq(32, SS_CSR(R_SER_MAXFRM_SZ));
-
- __raw_writeq(1, SS_CSR(R_SER_TX_RD_THRSH));
- __raw_writeq(4, SS_CSR(R_SER_TX_WR_THRSH));
- __raw_writeq(8, SS_CSR(R_SER_RX_RD_THRSH));
-
- /* This looks good from experimentation */
- __raw_writeq((M_SYNCSER_TXSYNC_INT | V_SYNCSER_TXSYNC_DLY(0) | M_SYNCSER_TXCLK_EXT |
- M_SYNCSER_RXSYNC_INT | V_SYNCSER_RXSYNC_DLY(1) | M_SYNCSER_RXCLK_EXT | M_SYNCSER_RXSYNC_EDGE),
- SS_CSR(R_SER_LINE_MODE));
-
- /* This looks good from experimentation */
- __raw_writeq(V_SYNCSER_SEQ_COUNT(14) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_STROBE,
- SS_TXTBL(0));
- __raw_writeq(V_SYNCSER_SEQ_COUNT(15) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE,
- SS_TXTBL(1));
- __raw_writeq(V_SYNCSER_SEQ_COUNT(13) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE,
- SS_TXTBL(2));
- __raw_writeq(V_SYNCSER_SEQ_COUNT( 0) | M_SYNCSER_SEQ_ENABLE |
- M_SYNCSER_SEQ_STROBE | M_SYNCSER_SEQ_LAST, SS_TXTBL(3));
-
- __raw_writeq(V_SYNCSER_SEQ_COUNT(14) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_STROBE,
- SS_RXTBL(0));
- __raw_writeq(V_SYNCSER_SEQ_COUNT(15) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE,
- SS_RXTBL(1));
- __raw_writeq(V_SYNCSER_SEQ_COUNT(13) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE,
- SS_RXTBL(2));
- __raw_writeq(V_SYNCSER_SEQ_COUNT( 0) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_STROBE |
- M_SYNCSER_SEQ_LAST, SS_RXTBL(3));
-
- for (i=4; i<16; i++) {
- /* Just in case... */
- __raw_writeq(M_SYNCSER_SEQ_LAST, SS_TXTBL(i));
- __raw_writeq(M_SYNCSER_SEQ_LAST, SS_RXTBL(i));
- }
-
- return 0;
-}
-
-static int init_serdma(serdma_t *dma)
-{
- CS_DBGOUT(CS_INIT, 2,
- printk(KERN_ERR "cs4297a: desc - %d sbufsize - %d dbufsize - %d\n",
- DMA_DESCR, SAMPLE_BUF_SIZE, DMA_BUF_SIZE));
-
- /* Descriptors */
- dma->ringsz = DMA_DESCR;
- dma->descrtab = kzalloc(dma->ringsz * sizeof(serdma_descr_t), GFP_KERNEL);
- if (!dma->descrtab) {
- printk(KERN_ERR "cs4297a: kzalloc descrtab failed\n");
- return -1;
- }
- dma->descrtab_end = dma->descrtab + dma->ringsz;
- /* XXX bloddy mess, use proper DMA API here ... */
- dma->descrtab_phys = CPHYSADDR((long)dma->descrtab);
- dma->descr_add = dma->descr_rem = dma->descrtab;
-
- /* Frame buffer area */
- dma->dma_buf = kzalloc(DMA_BUF_SIZE, GFP_KERNEL);
- if (!dma->dma_buf) {
- printk(KERN_ERR "cs4297a: kzalloc dma_buf failed\n");
- kfree(dma->descrtab);
- return -1;
- }
- dma->dma_buf_phys = CPHYSADDR((long)dma->dma_buf);
-
- /* Samples buffer area */
- dma->sbufsz = SAMPLE_BUF_SIZE;
- dma->sample_buf = kmalloc(dma->sbufsz, GFP_KERNEL);
- if (!dma->sample_buf) {
- printk(KERN_ERR "cs4297a: kmalloc sample_buf failed\n");
- kfree(dma->descrtab);
- kfree(dma->dma_buf);
- return -1;
- }
- dma->sb_swptr = dma->sb_hwptr = dma->sample_buf;
- dma->sb_end = (u16 *)((void *)dma->sample_buf + dma->sbufsz);
- dma->fragsize = dma->sbufsz >> 1;
-
- CS_DBGOUT(CS_INIT, 4,
- printk(KERN_ERR "cs4297a: descrtab - %08x dma_buf - %x sample_buf - %x\n",
- (int)dma->descrtab, (int)dma->dma_buf,
- (int)dma->sample_buf));
-
- return 0;
-}
-
-static int dma_init(struct cs4297a_state *s)
-{
- int i;
-
- CS_DBGOUT(CS_INIT, 2,
- printk(KERN_INFO "cs4297a: Setting up DMA\n"));
-
- if (init_serdma(&s->dma_adc) ||
- init_serdma(&s->dma_dac))
- return -1;
-
- if (__raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_RX))||
- __raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_TX))) {
- panic("DMA state corrupted?!");
- }
-
- /* Initialize now - the descr/buffer pairings will never
- change... */
- for (i=0; i<DMA_DESCR; i++) {
- s->dma_dac.descrtab[i].descr_a = M_DMA_SERRX_SOP | V_DMA_DSCRA_A_SIZE(1) |
- (s->dma_dac.dma_buf_phys + i*FRAME_BYTES);
- s->dma_dac.descrtab[i].descr_b = V_DMA_DSCRB_PKT_SIZE(FRAME_BYTES);
- s->dma_adc.descrtab[i].descr_a = V_DMA_DSCRA_A_SIZE(1) |
- (s->dma_adc.dma_buf_phys + i*FRAME_BYTES);
- s->dma_adc.descrtab[i].descr_b = 0;
- }
-
- __raw_writeq((M_DMA_EOP_INT_EN | V_DMA_INT_PKTCNT(DMA_INT_CNT) |
- V_DMA_RINGSZ(DMA_DESCR) | M_DMA_TDX_EN),
- SS_CSR(R_SER_DMA_CONFIG0_RX));
- __raw_writeq(M_DMA_L2CA, SS_CSR(R_SER_DMA_CONFIG1_RX));
- __raw_writeq(s->dma_adc.descrtab_phys, SS_CSR(R_SER_DMA_DSCR_BASE_RX));
-
- __raw_writeq(V_DMA_RINGSZ(DMA_DESCR), SS_CSR(R_SER_DMA_CONFIG0_TX));
- __raw_writeq(M_DMA_L2CA | M_DMA_NO_DSCR_UPDT, SS_CSR(R_SER_DMA_CONFIG1_TX));
- __raw_writeq(s->dma_dac.descrtab_phys, SS_CSR(R_SER_DMA_DSCR_BASE_TX));
-
- /* Prep the receive DMA descriptor ring */
- __raw_writeq(DMA_DESCR, SS_CSR(R_SER_DMA_DSCR_COUNT_RX));
-
- __raw_writeq(M_SYNCSER_DMA_RX_EN | M_SYNCSER_DMA_TX_EN, SS_CSR(R_SER_DMA_ENABLE));
-
- __raw_writeq((M_SYNCSER_RX_SYNC_ERR | M_SYNCSER_RX_OVERRUN | M_SYNCSER_RX_EOP_COUNT),
- SS_CSR(R_SER_INT_MASK));
-
- /* Enable the rx/tx; let the codec warm up to the sync and
- start sending good frames before the receive FIFO is
- enabled */
- __raw_writeq(M_SYNCSER_CMD_TX_EN, SS_CSR(R_SER_CMD));
- udelay(1000);
- __raw_writeq(M_SYNCSER_CMD_RX_EN | M_SYNCSER_CMD_TX_EN, SS_CSR(R_SER_CMD));
-
- /* XXXKW is this magic? (the "1" part) */
- while ((__raw_readq(SS_CSR(R_SER_STATUS)) & 0xf1) != 1)
- ;
-
- CS_DBGOUT(CS_INIT, 4,
- printk(KERN_INFO "cs4297a: status: %08x\n",
- (unsigned int)(__raw_readq(SS_CSR(R_SER_STATUS)) & 0xffffffff)));
-
- return 0;
-}
-
-static int serdma_reg_access(struct cs4297a_state *s, u64 data)
-{
- serdma_t *d = &s->dma_dac;
- u64 *data_p;
- unsigned swptr;
- unsigned long flags;
- serdma_descr_t *descr;
-
- if (s->reg_request) {
- printk(KERN_ERR "cs4297a: attempt to issue multiple reg_access\n");
- return -1;
- }
-
- if (s->ena & FMODE_WRITE) {
- /* Since a writer has the DSP open, we have to mux the
- request in */
- s->reg_request = data;
- interruptible_sleep_on(&s->dma_dac.reg_wait);
- /* XXXKW how can I deal with the starvation case where
- the opener isn't writing? */
- } else {
- /* Be safe when changing ring pointers */
- spin_lock_irqsave(&s->lock, flags);
- if (d->hwptr != d->swptr) {
- printk(KERN_ERR "cs4297a: reg access found bookkeeping error (hw/sw = %d/%d\n",
- d->hwptr, d->swptr);
- spin_unlock_irqrestore(&s->lock, flags);
- return -1;
- }
- swptr = d->swptr;
- d->hwptr = d->swptr = (d->swptr + 1) % d->ringsz;
- spin_unlock_irqrestore(&s->lock, flags);
-
- descr = &d->descrtab[swptr];
- data_p = &d->dma_buf[swptr * 4];
- *data_p = cpu_to_be64(data);
- __raw_writeq(1, SS_CSR(R_SER_DMA_DSCR_COUNT_TX));
- CS_DBGOUT(CS_DESCR, 4,
- printk(KERN_INFO "cs4297a: add_tx %p (%x -> %x)\n",
- data_p, swptr, d->hwptr));
- }
-
- CS_DBGOUT(CS_FUNCTION, 6,
- printk(KERN_INFO "cs4297a: serdma_reg_access()-\n"));
-
- return 0;
-}
-
-//****************************************************************************
-// "cs4297a_read_ac97" -- Reads an AC97 register
-//****************************************************************************
-static int cs4297a_read_ac97(struct cs4297a_state *s, u32 offset,
- u32 * value)
-{
- CS_DBGOUT(CS_AC97, 1,
- printk(KERN_INFO "cs4297a: read reg %2x\n", offset));
- if (serdma_reg_access(s, (0xCLL << 60) | (1LL << 47) | ((u64)(offset & 0x7F) << 40)))
- return -1;
-
- interruptible_sleep_on(&s->dma_adc.reg_wait);
- *value = s->read_value;
- CS_DBGOUT(CS_AC97, 2,
- printk(KERN_INFO "cs4297a: rdr reg %x -> %x\n", s->read_reg, s->read_value));
-
- return 0;
-}
-
-
-//****************************************************************************
-// "cs4297a_write_ac97()"-- writes an AC97 register
-//****************************************************************************
-static int cs4297a_write_ac97(struct cs4297a_state *s, u32 offset,
- u32 value)
-{
- CS_DBGOUT(CS_AC97, 1,
- printk(KERN_INFO "cs4297a: write reg %2x -> %04x\n", offset, value));
- return (serdma_reg_access(s, (0xELL << 60) | ((u64)(offset & 0x7F) << 40) | ((value & 0xffff) << 12)));
-}
-
-static void stop_dac(struct cs4297a_state *s)
-{
- unsigned long flags;
-
- CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO "cs4297a: stop_dac():\n"));
- spin_lock_irqsave(&s->lock, flags);
- s->ena &= ~FMODE_WRITE;
-#if 0
- /* XXXKW what do I really want here? My theory for now is
- that I just flip the "ena" bit, and the interrupt handler
- will stop processing the xmit channel */
- __raw_writeq((s->ena & FMODE_READ) ? M_SYNCSER_DMA_RX_EN : 0,
- SS_CSR(R_SER_DMA_ENABLE));
-#endif
-
- spin_unlock_irqrestore(&s->lock, flags);
-}
-
-
-static void start_dac(struct cs4297a_state *s)
-{
- unsigned long flags;
-
- CS_DBGOUT(CS_FUNCTION, 3, printk(KERN_INFO "cs4297a: start_dac()+\n"));
- spin_lock_irqsave(&s->lock, flags);
- if (!(s->ena & FMODE_WRITE) && (s->dma_dac.mapped ||
- (s->dma_dac.count > 0
- && s->dma_dac.ready))) {
- s->ena |= FMODE_WRITE;
- /* XXXKW what do I really want here? My theory for
- now is that I just flip the "ena" bit, and the
- interrupt handler will start processing the xmit
- channel */
-
- CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 8, printk(KERN_INFO
- "cs4297a: start_dac(): start dma\n"));
-
- }
- spin_unlock_irqrestore(&s->lock, flags);
- CS_DBGOUT(CS_FUNCTION, 3,
- printk(KERN_INFO "cs4297a: start_dac()-\n"));
-}
-
-
-static void stop_adc(struct cs4297a_state *s)
-{
- unsigned long flags;
-
- CS_DBGOUT(CS_FUNCTION, 3,
- printk(KERN_INFO "cs4297a: stop_adc()+\n"));
-
- spin_lock_irqsave(&s->lock, flags);
- s->ena &= ~FMODE_READ;
-
- if (s->conversion == 1) {
- s->conversion = 0;
- s->prop_adc.fmt = s->prop_adc.fmt_original;
- }
- /* Nothing to do really, I need to keep the DMA going
- XXXKW when do I get here, and is there more I should do? */
- spin_unlock_irqrestore(&s->lock, flags);
- CS_DBGOUT(CS_FUNCTION, 3,
- printk(KERN_INFO "cs4297a: stop_adc()-\n"));
-}
-
-
-static void start_adc(struct cs4297a_state *s)
-{
- unsigned long flags;
-
- CS_DBGOUT(CS_FUNCTION, 2,
- printk(KERN_INFO "cs4297a: start_adc()+\n"));
-
- if (!(s->ena & FMODE_READ) &&
- (s->dma_adc.mapped || s->dma_adc.count <=
- (signed) (s->dma_adc.sbufsz - 2 * s->dma_adc.fragsize))
- && s->dma_adc.ready) {
- if (s->prop_adc.fmt & AFMT_S8 || s->prop_adc.fmt & AFMT_U8) {
- //
- // now only use 16 bit capture, due to truncation issue
- // in the chip, noticable distortion occurs.
- // allocate buffer and then convert from 16 bit to
- // 8 bit for the user buffer.
- //
- s->prop_adc.fmt_original = s->prop_adc.fmt;
- if (s->prop_adc.fmt & AFMT_S8) {
- s->prop_adc.fmt &= ~AFMT_S8;
- s->prop_adc.fmt |= AFMT_S16_LE;
- }
- if (s->prop_adc.fmt & AFMT_U8) {
- s->prop_adc.fmt &= ~AFMT_U8;
- s->prop_adc.fmt |= AFMT_U16_LE;
- }
- //
- // prog_dmabuf_adc performs a stop_adc() but that is
- // ok since we really haven't started the DMA yet.
- //
- prog_codec(s, CS_TYPE_ADC);
-
- prog_dmabuf_adc(s);
- s->conversion = 1;
- }
- spin_lock_irqsave(&s->lock, flags);
- s->ena |= FMODE_READ;
- /* Nothing to do really, I am probably already
- DMAing... XXXKW when do I get here, and is there
- more I should do? */
- spin_unlock_irqrestore(&s->lock, flags);
-
- CS_DBGOUT(CS_PARMS, 6, printk(KERN_INFO
- "cs4297a: start_adc(): start adc\n"));
- }
- CS_DBGOUT(CS_FUNCTION, 2,
- printk(KERN_INFO "cs4297a: start_adc()-\n"));
-
-}
-
-
-// call with spinlock held!
-static void cs4297a_update_ptr(struct cs4297a_state *s, int intflag)
-{
- int good_diff, diff, diff2;
- u64 *data_p, data;
- u32 *s_ptr;
- unsigned hwptr;
- u32 status;
- serdma_t *d;
- serdma_descr_t *descr;
-
- // update ADC pointer
- status = intflag ? __raw_readq(SS_CSR(R_SER_STATUS)) : 0;
-
- if ((s->ena & FMODE_READ) || (status & (M_SYNCSER_RX_EOP_COUNT))) {
- d = &s->dma_adc;
- hwptr = (unsigned) (((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_RX)) & M_DMA_CURDSCR_ADDR) -
- d->descrtab_phys) / sizeof(serdma_descr_t));
-
- if (s->ena & FMODE_READ) {
- CS_DBGOUT(CS_FUNCTION, 2,
- printk(KERN_INFO "cs4297a: upd_rcv sw->hw->hw %x/%x/%x (int-%d)n",
- d->swptr, d->hwptr, hwptr, intflag));
- /* Number of DMA buffers available for software: */
- diff2 = diff = (d->ringsz + hwptr - d->hwptr) % d->ringsz;
- d->hwptr = hwptr;
- good_diff = 0;
- s_ptr = (u32 *)&(d->dma_buf[d->swptr*4]);
- descr = &d->descrtab[d->swptr];
- while (diff2--) {
- u64 data = be64_to_cpu(*(u64 *)s_ptr);
- u64 descr_a;
- u16 left, right;
- descr_a = descr->descr_a;
- descr->descr_a &= ~M_DMA_SERRX_SOP;
- if ((descr_a & M_DMA_DSCRA_A_ADDR) != CPHYSADDR((long)s_ptr)) {
- printk(KERN_ERR "cs4297a: RX Bad address (read)\n");
- }
- if (((data & 0x9800000000000000) != 0x9800000000000000) ||
- (!(descr_a & M_DMA_SERRX_SOP)) ||
- (G_DMA_DSCRB_PKT_SIZE(descr->descr_b) != FRAME_BYTES)) {
- s->stats.rx_bad++;
- printk(KERN_DEBUG "cs4297a: RX Bad attributes (read)\n");
- continue;
- }
- s->stats.rx_good++;
- if ((data >> 61) == 7) {
- s->read_value = (data >> 12) & 0xffff;
- s->read_reg = (data >> 40) & 0x7f;
- wake_up(&d->reg_wait);
- }
- if (d->count && (d->sb_hwptr == d->sb_swptr)) {
- s->stats.rx_overflow++;
- printk(KERN_DEBUG "cs4297a: RX overflow\n");
- continue;
- }
- good_diff++;
- left = ((be32_to_cpu(s_ptr[1]) & 0xff) << 8) |
- ((be32_to_cpu(s_ptr[2]) >> 24) & 0xff);
- right = (be32_to_cpu(s_ptr[2]) >> 4) & 0xffff;
- *d->sb_hwptr++ = cpu_to_be16(left);
- *d->sb_hwptr++ = cpu_to_be16(right);
- if (d->sb_hwptr == d->sb_end)
- d->sb_hwptr = d->sample_buf;
- descr++;
- if (descr == d->descrtab_end) {
- descr = d->descrtab;
- s_ptr = (u32 *)s->dma_adc.dma_buf;
- } else {
- s_ptr += 8;
- }
- }
- d->total_bytes += good_diff * FRAME_SAMPLE_BYTES;
- d->count += good_diff * FRAME_SAMPLE_BYTES;
- if (d->count > d->sbufsz) {
- printk(KERN_ERR "cs4297a: bogus receive overflow!!\n");
- }
- d->swptr = (d->swptr + diff) % d->ringsz;
- __raw_writeq(diff, SS_CSR(R_SER_DMA_DSCR_COUNT_RX));
- if (d->mapped) {
- if (d->count >= (signed) d->fragsize)
- wake_up(&d->wait);
- } else {
- if (d->count > 0) {
- CS_DBGOUT(CS_WAVE_READ, 4,
- printk(KERN_INFO
- "cs4297a: update count -> %d\n", d->count));
- wake_up(&d->wait);
- }
- }
- } else {
- /* Receive is going even if no one is
- listening (for register accesses and to
- avoid FIFO overrun) */
- diff2 = diff = (hwptr + d->ringsz - d->hwptr) % d->ringsz;
- if (!diff) {
- printk(KERN_ERR "cs4297a: RX full or empty?\n");
- }
-
- descr = &d->descrtab[d->swptr];
- data_p = &d->dma_buf[d->swptr*4];
-
- /* Force this to happen at least once; I got
- here because of an interrupt, so there must
- be a buffer to process. */
- do {
- data = be64_to_cpu(*data_p);
- if ((descr->descr_a & M_DMA_DSCRA_A_ADDR) != CPHYSADDR((long)data_p)) {
- printk(KERN_ERR "cs4297a: RX Bad address %d (%llx %lx)\n", d->swptr,
- (long long)(descr->descr_a & M_DMA_DSCRA_A_ADDR),
- (long)CPHYSADDR((long)data_p));
- }
- if (!(data & (1LL << 63)) ||
- !(descr->descr_a & M_DMA_SERRX_SOP) ||
- (G_DMA_DSCRB_PKT_SIZE(descr->descr_b) != FRAME_BYTES)) {
- s->stats.rx_bad++;
- printk(KERN_DEBUG "cs4297a: RX Bad attributes\n");
- } else {
- s->stats.rx_good++;
- if ((data >> 61) == 7) {
- s->read_value = (data >> 12) & 0xffff;
- s->read_reg = (data >> 40) & 0x7f;
- wake_up(&d->reg_wait);
- }
- }
- descr->descr_a &= ~M_DMA_SERRX_SOP;
- descr++;
- d->swptr++;
- data_p += 4;
- if (descr == d->descrtab_end) {
- descr = d->descrtab;
- d->swptr = 0;
- data_p = d->dma_buf;
- }
- __raw_writeq(1, SS_CSR(R_SER_DMA_DSCR_COUNT_RX));
- } while (--diff);
- d->hwptr = hwptr;
-
- CS_DBGOUT(CS_DESCR, 6,
- printk(KERN_INFO "cs4297a: hw/sw %x/%x\n", d->hwptr, d->swptr));
- }
-
- CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO
- "cs4297a: cs4297a_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n",
- (unsigned)s, d->hwptr,
- d->total_bytes, d->count));
- }
-
- /* XXXKW worry about s->reg_request -- there is a starvation
- case if s->ena has FMODE_WRITE on, but the client isn't
- doing writes */
-
- // update DAC pointer
- //
- // check for end of buffer, means that we are going to wait for another interrupt
- // to allow silence to fill the fifos on the part, to keep pops down to a minimum.
- //
- if (s->ena & FMODE_WRITE) {
- serdma_t *d = &s->dma_dac;
- hwptr = (unsigned) (((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) -
- d->descrtab_phys) / sizeof(serdma_descr_t));
- diff = (d->ringsz + hwptr - d->hwptr) % d->ringsz;
- CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO
- "cs4297a: cs4297a_update_ptr(): hw/hw/sw %x/%x/%x diff %d count %d\n",
- d->hwptr, hwptr, d->swptr, diff, d->count));
- d->hwptr = hwptr;
- /* XXXKW stereo? conversion? Just assume 2 16-bit samples for now */
- d->total_bytes += diff * FRAME_SAMPLE_BYTES;
- if (d->mapped) {
- d->count += diff * FRAME_SAMPLE_BYTES;
- if (d->count >= d->fragsize) {
- d->wakeup = 1;
- wake_up(&d->wait);
- if (d->count > d->sbufsz)
- d->count &= d->sbufsz - 1;
- }
- } else {
- d->count -= diff * FRAME_SAMPLE_BYTES;
- if (d->count <= 0) {
- //
- // fill with silence, and do not shut down the DAC.
- // Continue to play silence until the _release.
- //
- CS_DBGOUT(CS_WAVE_WRITE, 6, printk(KERN_INFO
- "cs4297a: cs4297a_update_ptr(): memset %d at 0x%.8x for %d size \n",
- (unsigned)(s->prop_dac.fmt &
- (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0,
- (unsigned)d->dma_buf,
- d->ringsz));
- memset(d->dma_buf, 0, d->ringsz * FRAME_BYTES);
- if (d->count < 0) {
- d->underrun = 1;
- s->stats.tx_underrun++;
- d->count = 0;
- CS_DBGOUT(CS_ERROR, 9, printk(KERN_INFO
- "cs4297a: cs4297a_update_ptr(): underrun\n"));
- }
- } else if (d->count <=
- (signed) d->fragsize
- && !d->endcleared) {
- /* XXXKW what is this for? */
- clear_advance(d->dma_buf,
- d->sbufsz,
- d->swptr,
- d->fragsize,
- 0);
- d->endcleared = 1;
- }
- if ( (d->count <= (signed) d->sbufsz/2) || intflag)
- {
- CS_DBGOUT(CS_WAVE_WRITE, 4,
- printk(KERN_INFO
- "cs4297a: update count -> %d\n", d->count));
- wake_up(&d->wait);
- }
- }
- CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO
- "cs4297a: cs4297a_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n",
- (unsigned) s, d->hwptr,
- d->total_bytes, d->count));
- }
-}
-
-static int mixer_ioctl(struct cs4297a_state *s, unsigned int cmd,
- unsigned long arg)
-{
- // Index to mixer_src[] is value of AC97 Input Mux Select Reg.
- // Value of array member is recording source Device ID Mask.
- static const unsigned int mixer_src[8] = {
- SOUND_MASK_MIC, SOUND_MASK_CD, 0, SOUND_MASK_LINE1,
- SOUND_MASK_LINE, SOUND_MASK_VOLUME, 0, 0
- };
-
- // Index of mixtable1[] member is Device ID
- // and must be <= SOUND_MIXER_NRDEVICES.
- // Value of array member is index into s->mix.vol[]
- static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = {
- [SOUND_MIXER_PCM] = 1, // voice
- [SOUND_MIXER_LINE1] = 2, // AUX
- [SOUND_MIXER_CD] = 3, // CD
- [SOUND_MIXER_LINE] = 4, // Line
- [SOUND_MIXER_SYNTH] = 5, // FM
- [SOUND_MIXER_MIC] = 6, // Mic
- [SOUND_MIXER_SPEAKER] = 7, // Speaker
- [SOUND_MIXER_RECLEV] = 8, // Recording level
- [SOUND_MIXER_VOLUME] = 9 // Master Volume
- };
-
- static const unsigned mixreg[] = {
- AC97_PCMOUT_VOL,
- AC97_AUX_VOL,
- AC97_CD_VOL,
- AC97_LINEIN_VOL
- };
- unsigned char l, r, rl, rr, vidx;
- unsigned char attentbl[11] =
- { 63, 42, 26, 17, 14, 11, 8, 6, 4, 2, 0 };
- unsigned temp1;
- int i, val;
-
- VALIDATE_STATE(s);
- CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO
- "cs4297a: mixer_ioctl(): s=0x%.8x cmd=0x%.8x\n",
- (unsigned) s, cmd));
-#if CSDEBUG
- cs_printioctl(cmd);
-#endif
-#if CSDEBUG_INTERFACE
-
- if ((cmd == SOUND_MIXER_CS_GETDBGMASK) ||
- (cmd == SOUND_MIXER_CS_SETDBGMASK) ||
- (cmd == SOUND_MIXER_CS_GETDBGLEVEL) ||
- (cmd == SOUND_MIXER_CS_SETDBGLEVEL))
- {
- switch (cmd) {
-
- case SOUND_MIXER_CS_GETDBGMASK:
- return put_user(cs_debugmask,
- (unsigned long *) arg);
-
- case SOUND_MIXER_CS_GETDBGLEVEL:
- return put_user(cs_debuglevel,
- (unsigned long *) arg);
-
- case SOUND_MIXER_CS_SETDBGMASK:
- if (get_user(val, (unsigned long *) arg))
- return -EFAULT;
- cs_debugmask = val;
- return 0;
-
- case SOUND_MIXER_CS_SETDBGLEVEL:
- if (get_user(val, (unsigned long *) arg))
- return -EFAULT;
- cs_debuglevel = val;
- return 0;
- default:
- CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO
- "cs4297a: mixer_ioctl(): ERROR unknown debug cmd\n"));
- return 0;
- }
- }
-#endif
-
- if (cmd == SOUND_MIXER_PRIVATE1) {
- return -EINVAL;
- }
- if (cmd == SOUND_MIXER_PRIVATE2) {
- // enable/disable/query spatializer
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != -1) {
- temp1 = (val & 0x3f) >> 2;
- cs4297a_write_ac97(s, AC97_3D_CONTROL, temp1);
- cs4297a_read_ac97(s, AC97_GENERAL_PURPOSE,
- &temp1);
- cs4297a_write_ac97(s, AC97_GENERAL_PURPOSE,
- temp1 | 0x2000);
- }
- cs4297a_read_ac97(s, AC97_3D_CONTROL, &temp1);
- return put_user((temp1 << 2) | 3, (int *) arg);
- }
- if (cmd == SOUND_MIXER_INFO) {
- mixer_info info;
- memset(&info, 0, sizeof(info));
- strlcpy(info.id, "CS4297a", sizeof(info.id));
- strlcpy(info.name, "Crystal CS4297a", sizeof(info.name));
- info.modify_counter = s->mix.modcnt;
- if (copy_to_user((void *) arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
- if (cmd == SOUND_OLD_MIXER_INFO) {
- _old_mixer_info info;
- memset(&info, 0, sizeof(info));
- strlcpy(info.id, "CS4297a", sizeof(info.id));
- strlcpy(info.name, "Crystal CS4297a", sizeof(info.name));
- if (copy_to_user((void *) arg, &info, sizeof(info)))
- return -EFAULT;
- return 0;
- }
- if (cmd == OSS_GETVERSION)
- return put_user(SOUND_VERSION, (int *) arg);
-
- if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int))
- return -EINVAL;
-
- // If ioctl has only the SIOC_READ bit(bit 31)
- // on, process the only-read commands.
- if (_SIOC_DIR(cmd) == _SIOC_READ) {
- switch (_IOC_NR(cmd)) {
- case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source
- cs4297a_read_ac97(s, AC97_RECORD_SELECT,
- &temp1);
- return put_user(mixer_src[temp1 & 7], (int *) arg);
-
- case SOUND_MIXER_DEVMASK: // Arg contains a bit for each supported device
- return put_user(SOUND_MASK_PCM | SOUND_MASK_LINE |
- SOUND_MASK_VOLUME | SOUND_MASK_RECLEV,
- (int *) arg);
-
- case SOUND_MIXER_RECMASK: // Arg contains a bit for each supported recording source
- return put_user(SOUND_MASK_LINE | SOUND_MASK_VOLUME,
- (int *) arg);
-
- case SOUND_MIXER_STEREODEVS: // Mixer channels supporting stereo
- return put_user(SOUND_MASK_PCM | SOUND_MASK_LINE |
- SOUND_MASK_VOLUME | SOUND_MASK_RECLEV,
- (int *) arg);
-
- case SOUND_MIXER_CAPS:
- return put_user(SOUND_CAP_EXCL_INPUT, (int *) arg);
-
- default:
- i = _IOC_NR(cmd);
- if (i >= SOUND_MIXER_NRDEVICES
- || !(vidx = mixtable1[i]))
- return -EINVAL;
- return put_user(s->mix.vol[vidx - 1], (int *) arg);
- }
- }
- // If ioctl doesn't have both the SIOC_READ and
- // the SIOC_WRITE bit set, return invalid.
- if (_SIOC_DIR(cmd) != (_SIOC_READ | _SIOC_WRITE))
- return -EINVAL;
-
- // Increment the count of volume writes.
- s->mix.modcnt++;
-
- // Isolate the command; it must be a write.
- switch (_IOC_NR(cmd)) {
-
- case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source
- if (get_user(val, (int *) arg))
- return -EFAULT;
- i = hweight32(val); // i = # bits on in val.
- if (i != 1) // One & only 1 bit must be on.
- return 0;
- for (i = 0; i < sizeof(mixer_src) / sizeof(int); i++) {
- if (val == mixer_src[i]) {
- temp1 = (i << 8) | i;
- cs4297a_write_ac97(s,
- AC97_RECORD_SELECT,
- temp1);
- return 0;
- }
- }
- return 0;
-
- case SOUND_MIXER_VOLUME:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- l = val & 0xff;
- if (l > 100)
- l = 100; // Max soundcard.h vol is 100.
- if (l < 6) {
- rl = 63;
- l = 0;
- } else
- rl = attentbl[(10 * l) / 100]; // Convert 0-100 vol to 63-0 atten.
-
- r = (val >> 8) & 0xff;
- if (r > 100)
- r = 100; // Max right volume is 100, too
- if (r < 6) {
- rr = 63;
- r = 0;
- } else
- rr = attentbl[(10 * r) / 100]; // Convert volume to attenuation.
-
- if ((rl > 60) && (rr > 60)) // If both l & r are 'low',
- temp1 = 0x8000; // turn on the mute bit.
- else
- temp1 = 0;
-
- temp1 |= (rl << 8) | rr;
-
- cs4297a_write_ac97(s, AC97_MASTER_VOL_STEREO, temp1);
- cs4297a_write_ac97(s, AC97_PHONE_VOL, temp1);
-
-#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
- s->mix.vol[8] = ((unsigned int) r << 8) | l;
-#else
- s->mix.vol[8] = val;
-#endif
- return put_user(s->mix.vol[8], (int *) arg);
-
- case SOUND_MIXER_SPEAKER:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- l = val & 0xff;
- if (l > 100)
- l = 100;
- if (l < 3) {
- rl = 0;
- l = 0;
- } else {
- rl = (l * 2 - 5) / 13; // Convert 0-100 range to 0-15.
- l = (rl * 13 + 5) / 2;
- }
-
- if (rl < 3) {
- temp1 = 0x8000;
- rl = 0;
- } else
- temp1 = 0;
- rl = 15 - rl; // Convert volume to attenuation.
- temp1 |= rl << 1;
- cs4297a_write_ac97(s, AC97_PCBEEP_VOL, temp1);
-
-#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
- s->mix.vol[6] = l << 8;
-#else
- s->mix.vol[6] = val;
-#endif
- return put_user(s->mix.vol[6], (int *) arg);
-
- case SOUND_MIXER_RECLEV:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- l = val & 0xff;
- if (l > 100)
- l = 100;
- r = (val >> 8) & 0xff;
- if (r > 100)
- r = 100;
- rl = (l * 2 - 5) / 13; // Convert 0-100 scale to 0-15.
- rr = (r * 2 - 5) / 13;
- if (rl < 3 && rr < 3)
- temp1 = 0x8000;
- else
- temp1 = 0;
-
- temp1 = temp1 | (rl << 8) | rr;
- cs4297a_write_ac97(s, AC97_RECORD_GAIN, temp1);
-
-#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
- s->mix.vol[7] = ((unsigned int) r << 8) | l;
-#else
- s->mix.vol[7] = val;
-#endif
- return put_user(s->mix.vol[7], (int *) arg);
-
- case SOUND_MIXER_MIC:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- l = val & 0xff;
- if (l > 100)
- l = 100;
- if (l < 1) {
- l = 0;
- rl = 0;
- } else {
- rl = ((unsigned) l * 5 - 4) / 16; // Convert 0-100 range to 0-31.
- l = (rl * 16 + 4) / 5;
- }
- cs4297a_read_ac97(s, AC97_MIC_VOL, &temp1);
- temp1 &= 0x40; // Isolate 20db gain bit.
- if (rl < 3) {
- temp1 |= 0x8000;
- rl = 0;
- }
- rl = 31 - rl; // Convert volume to attenuation.
- temp1 |= rl;
- cs4297a_write_ac97(s, AC97_MIC_VOL, temp1);
-
-#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
- s->mix.vol[5] = val << 8;
-#else
- s->mix.vol[5] = val;
-#endif
- return put_user(s->mix.vol[5], (int *) arg);
-
-
- case SOUND_MIXER_SYNTH:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- l = val & 0xff;
- if (l > 100)
- l = 100;
- if (get_user(val, (int *) arg))
- return -EFAULT;
- r = (val >> 8) & 0xff;
- if (r > 100)
- r = 100;
- rl = (l * 2 - 11) / 3; // Convert 0-100 range to 0-63.
- rr = (r * 2 - 11) / 3;
- if (rl < 3) // If l is low, turn on
- temp1 = 0x0080; // the mute bit.
- else
- temp1 = 0;
-
- rl = 63 - rl; // Convert vol to attenuation.
-// writel(temp1 | rl, s->pBA0 + FMLVC);
- if (rr < 3) // If rr is low, turn on
- temp1 = 0x0080; // the mute bit.
- else
- temp1 = 0;
- rr = 63 - rr; // Convert vol to attenuation.
-// writel(temp1 | rr, s->pBA0 + FMRVC);
-
-#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
- s->mix.vol[4] = (r << 8) | l;
-#else
- s->mix.vol[4] = val;
-#endif
- return put_user(s->mix.vol[4], (int *) arg);
-
-
- default:
- CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO
- "cs4297a: mixer_ioctl(): default\n"));
-
- i = _IOC_NR(cmd);
- if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i]))
- return -EINVAL;
- if (get_user(val, (int *) arg))
- return -EFAULT;
- l = val & 0xff;
- if (l > 100)
- l = 100;
- if (l < 1) {
- l = 0;
- rl = 31;
- } else
- rl = (attentbl[(l * 10) / 100]) >> 1;
-
- r = (val >> 8) & 0xff;
- if (r > 100)
- r = 100;
- if (r < 1) {
- r = 0;
- rr = 31;
- } else
- rr = (attentbl[(r * 10) / 100]) >> 1;
- if ((rl > 30) && (rr > 30))
- temp1 = 0x8000;
- else
- temp1 = 0;
- temp1 = temp1 | (rl << 8) | rr;
- cs4297a_write_ac97(s, mixreg[vidx - 1], temp1);
-
-#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
- s->mix.vol[vidx - 1] = ((unsigned int) r << 8) | l;
-#else
- s->mix.vol[vidx - 1] = val;
-#endif
- return put_user(s->mix.vol[vidx - 1], (int *) arg);
- }
-}
-
-
-// ---------------------------------------------------------------------
-
-static int cs4297a_open_mixdev(struct inode *inode, struct file *file)
-{
- int minor = iminor(inode);
- struct cs4297a_state *s=NULL;
- struct list_head *entry;
-
- CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
- printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()+\n"));
-
- mutex_lock(&swarm_cs4297a_mutex);
- list_for_each(entry, &cs4297a_devs)
- {
- s = list_entry(entry, struct cs4297a_state, list);
- if(s->dev_mixer == minor)
- break;
- }
- if (!s)
- {
- CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2,
- printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- -ENODEV\n"));
-
- mutex_unlock(&swarm_cs4297a_mutex);
- return -ENODEV;
- }
- VALIDATE_STATE(s);
- file->private_data = s;
-
- CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4,
- printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- 0\n"));
- mutex_unlock(&swarm_cs4297a_mutex);
-
- return nonseekable_open(inode, file);
-}
-
-
-static int cs4297a_release_mixdev(struct inode *inode, struct file *file)
-{
- struct cs4297a_state *s =
- (struct cs4297a_state *) file->private_data;
-
- VALIDATE_STATE(s);
- return 0;
-}
-
-
-static int cs4297a_ioctl_mixdev(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- int ret;
- mutex_lock(&swarm_cs4297a_mutex);
- ret = mixer_ioctl((struct cs4297a_state *) file->private_data, cmd,
- arg);
- mutex_unlock(&swarm_cs4297a_mutex);
- return ret;
-}
-
-
-// ******************************************************************************************
-// Mixer file operations struct.
-// ******************************************************************************************
-static const struct file_operations cs4297a_mixer_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .unlocked_ioctl = cs4297a_ioctl_mixdev,
- .open = cs4297a_open_mixdev,
- .release = cs4297a_release_mixdev,
-};
-
-// ---------------------------------------------------------------------
-
-
-static int drain_adc(struct cs4297a_state *s, int nonblock)
-{
- /* This routine serves no purpose currently - any samples
- sitting in the receive queue will just be processed by the
- background consumer. This would be different if DMA
- actually stopped when there were no clients. */
- return 0;
-}
-
-static int drain_dac(struct cs4297a_state *s, int nonblock)
-{
- DECLARE_WAITQUEUE(wait, current);
- unsigned long flags;
- unsigned hwptr;
- unsigned tmo;
- int count;
-
- if (s->dma_dac.mapped)
- return 0;
- if (nonblock)
- return -EBUSY;
- add_wait_queue(&s->dma_dac.wait, &wait);
- while ((count = __raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_TX))) ||
- (s->dma_dac.count > 0)) {
- if (!signal_pending(current)) {
- set_current_state(TASK_INTERRUPTIBLE);
- /* XXXKW is this calculation working? */
- tmo = ((count * FRAME_TX_US) * HZ) / 1000000;
- schedule_timeout(tmo + 1);
- } else {
- /* XXXKW do I care if there is a signal pending? */
- }
- }
- spin_lock_irqsave(&s->lock, flags);
- /* Reset the bookkeeping */
- hwptr = (int)(((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) -
- s->dma_dac.descrtab_phys) / sizeof(serdma_descr_t));
- s->dma_dac.hwptr = s->dma_dac.swptr = hwptr;
- spin_unlock_irqrestore(&s->lock, flags);
- remove_wait_queue(&s->dma_dac.wait, &wait);
- current->state = TASK_RUNNING;
- return 0;
-}
-
-
-// ---------------------------------------------------------------------
-
-static ssize_t cs4297a_read(struct file *file, char *buffer, size_t count,
- loff_t * ppos)
-{
- struct cs4297a_state *s =
- (struct cs4297a_state *) file->private_data;
- ssize_t ret;
- unsigned long flags;
- int cnt, count_fr, cnt_by;
- unsigned copied = 0;
-
- CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2,
- printk(KERN_INFO "cs4297a: cs4297a_read()+ %d \n", count));
-
- VALIDATE_STATE(s);
- if (s->dma_adc.mapped)
- return -ENXIO;
- if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s)))
- return ret;
- if (!access_ok(VERIFY_WRITE, buffer, count))
- return -EFAULT;
- ret = 0;
-//
-// "count" is the amount of bytes to read (from app), is decremented each loop
-// by the amount of bytes that have been returned to the user buffer.
-// "cnt" is the running total of each read from the buffer (changes each loop)
-// "buffer" points to the app's buffer
-// "ret" keeps a running total of the amount of bytes that have been copied
-// to the user buffer.
-// "copied" is the total bytes copied into the user buffer for each loop.
-//
- while (count > 0) {
- CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO
- "_read() count>0 count=%d .count=%d .swptr=%d .hwptr=%d \n",
- count, s->dma_adc.count,
- s->dma_adc.swptr, s->dma_adc.hwptr));
- spin_lock_irqsave(&s->lock, flags);
-
- /* cnt will be the number of available samples (16-bit
- stereo); it starts out as the maxmimum consequetive
- samples */
- cnt = (s->dma_adc.sb_end - s->dma_adc.sb_swptr) / 2;
- count_fr = s->dma_adc.count / FRAME_SAMPLE_BYTES;
-
- // dma_adc.count is the current total bytes that have not been read.
- // if the amount of unread bytes from the current sw pointer to the
- // end of the buffer is greater than the current total bytes that
- // have not been read, then set the "cnt" (unread bytes) to the
- // amount of unread bytes.
-
- if (count_fr < cnt)
- cnt = count_fr;
- cnt_by = cnt * FRAME_SAMPLE_BYTES;
- spin_unlock_irqrestore(&s->lock, flags);
- //
- // if we are converting from 8/16 then we need to copy
- // twice the number of 16 bit bytes then 8 bit bytes.
- //
- if (s->conversion) {
- if (cnt_by > (count * 2)) {
- cnt = (count * 2) / FRAME_SAMPLE_BYTES;
- cnt_by = count * 2;
- }
- } else {
- if (cnt_by > count) {
- cnt = count / FRAME_SAMPLE_BYTES;
- cnt_by = count;
- }
- }
- //
- // "cnt" NOW is the smaller of the amount that will be read,
- // and the amount that is requested in this read (or partial).
- // if there are no bytes in the buffer to read, then start the
- // ADC and wait for the interrupt handler to wake us up.
- //
- if (cnt <= 0) {
-
- // start up the dma engine and then continue back to the top of
- // the loop when wake up occurs.
- start_adc(s);
- if (file->f_flags & O_NONBLOCK)
- return ret ? ret : -EAGAIN;
- interruptible_sleep_on(&s->dma_adc.wait);
- if (signal_pending(current))
- return ret ? ret : -ERESTARTSYS;
- continue;
- }
- // there are bytes in the buffer to read.
- // copy from the hw buffer over to the user buffer.
- // user buffer is designated by "buffer"
- // virtual address to copy from is dma_buf+swptr
- // the "cnt" is the number of bytes to read.
-
- CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO
- "_read() copy_to cnt=%d count=%d ", cnt_by, count));
- CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO
- " .sbufsz=%d .count=%d buffer=0x%.8x ret=%d\n",
- s->dma_adc.sbufsz, s->dma_adc.count,
- (unsigned) buffer, ret));
-
- if (copy_to_user (buffer, ((void *)s->dma_adc.sb_swptr), cnt_by))
- return ret ? ret : -EFAULT;
- copied = cnt_by;
-
- /* Return the descriptors */
- spin_lock_irqsave(&s->lock, flags);
- CS_DBGOUT(CS_FUNCTION, 2,
- printk(KERN_INFO "cs4297a: upd_rcv sw->hw %x/%x\n", s->dma_adc.swptr, s->dma_adc.hwptr));
- s->dma_adc.count -= cnt_by;
- s->dma_adc.sb_swptr += cnt * 2;
- if (s->dma_adc.sb_swptr == s->dma_adc.sb_end)
- s->dma_adc.sb_swptr = s->dma_adc.sample_buf;
- spin_unlock_irqrestore(&s->lock, flags);
- count -= copied;
- buffer += copied;
- ret += copied;
- start_adc(s);
- }
- CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2,
- printk(KERN_INFO "cs4297a: cs4297a_read()- %d\n", ret));
- return ret;
-}
-
-
-static ssize_t cs4297a_write(struct file *file, const char *buffer,
- size_t count, loff_t * ppos)
-{
- struct cs4297a_state *s =
- (struct cs4297a_state *) file->private_data;
- ssize_t ret;
- unsigned long flags;
- unsigned swptr, hwptr;
- int cnt;
-
- CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2,
- printk(KERN_INFO "cs4297a: cs4297a_write()+ count=%d\n",
- count));
- VALIDATE_STATE(s);
-
- if (s->dma_dac.mapped)
- return -ENXIO;
- if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s)))
- return ret;
- if (!access_ok(VERIFY_READ, buffer, count))
- return -EFAULT;
- ret = 0;
- while (count > 0) {
- serdma_t *d = &s->dma_dac;
- int copy_cnt;
- u32 *s_tmpl;
- u32 *t_tmpl;
- u32 left, right;
- int swap = (s->prop_dac.fmt == AFMT_S16_LE) || (s->prop_dac.fmt == AFMT_U16_LE);
-
- /* XXXXXX this is broken for BLOAT_FACTOR */
- spin_lock_irqsave(&s->lock, flags);
- if (d->count < 0) {
- d->count = 0;
- d->swptr = d->hwptr;
- }
- if (d->underrun) {
- d->underrun = 0;
- hwptr = (unsigned) (((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) -
- d->descrtab_phys) / sizeof(serdma_descr_t));
- d->swptr = d->hwptr = hwptr;
- }
- swptr = d->swptr;
- cnt = d->sbufsz - (swptr * FRAME_SAMPLE_BYTES);
- /* Will this write fill up the buffer? */
- if (d->count + cnt > d->sbufsz)
- cnt = d->sbufsz - d->count;
- spin_unlock_irqrestore(&s->lock, flags);
- if (cnt > count)
- cnt = count;
- if (cnt <= 0) {
- start_dac(s);
- if (file->f_flags & O_NONBLOCK)
- return ret ? ret : -EAGAIN;
- interruptible_sleep_on(&d->wait);
- if (signal_pending(current))
- return ret ? ret : -ERESTARTSYS;
- continue;
- }
- if (copy_from_user(d->sample_buf, buffer, cnt))
- return ret ? ret : -EFAULT;
-
- copy_cnt = cnt;
- s_tmpl = (u32 *)d->sample_buf;
- t_tmpl = (u32 *)(d->dma_buf + (swptr * 4));
-
- /* XXXKW assuming 16-bit stereo! */
- do {
- u32 tmp;
-
- t_tmpl[0] = cpu_to_be32(0x98000000);
-
- tmp = be32_to_cpu(s_tmpl[0]);
- left = tmp & 0xffff;
- right = tmp >> 16;
- if (swap) {
- left = swab16(left);
- right = swab16(right);
- }
- t_tmpl[1] = cpu_to_be32(left >> 8);
- t_tmpl[2] = cpu_to_be32(((left & 0xff) << 24) |
- (right << 4));
-
- s_tmpl++;
- t_tmpl += 8;
- copy_cnt -= 4;
- } while (copy_cnt);
-
- /* Mux in any pending read/write accesses */
- if (s->reg_request) {
- *(u64 *)(d->dma_buf + (swptr * 4)) |=
- cpu_to_be64(s->reg_request);
- s->reg_request = 0;
- wake_up(&s->dma_dac.reg_wait);
- }
-
- CS_DBGOUT(CS_WAVE_WRITE, 4,
- printk(KERN_INFO
- "cs4297a: copy in %d to swptr %x\n", cnt, swptr));
-
- swptr = (swptr + (cnt/FRAME_SAMPLE_BYTES)) % d->ringsz;
- __raw_writeq(cnt/FRAME_SAMPLE_BYTES, SS_CSR(R_SER_DMA_DSCR_COUNT_TX));
- spin_lock_irqsave(&s->lock, flags);
- d->swptr = swptr;
- d->count += cnt;
- d->endcleared = 0;
- spin_unlock_irqrestore(&s->lock, flags);
- count -= cnt;
- buffer += cnt;
- ret += cnt;
- start_dac(s);
- }
- CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2,
- printk(KERN_INFO "cs4297a: cs4297a_write()- %d\n", ret));
- return ret;
-}
-
-
-static unsigned int cs4297a_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct cs4297a_state *s =
- (struct cs4297a_state *) file->private_data;
- unsigned long flags;
- unsigned int mask = 0;
-
- CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
- printk(KERN_INFO "cs4297a: cs4297a_poll()+\n"));
- VALIDATE_STATE(s);
- if (file->f_mode & FMODE_WRITE) {
- CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
- printk(KERN_INFO
- "cs4297a: cs4297a_poll() wait on FMODE_WRITE\n"));
- if(!s->dma_dac.ready && prog_dmabuf_dac(s))
- return 0;
- poll_wait(file, &s->dma_dac.wait, wait);
- }
- if (file->f_mode & FMODE_READ) {
- CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
- printk(KERN_INFO
- "cs4297a: cs4297a_poll() wait on FMODE_READ\n"));
- if(!s->dma_dac.ready && prog_dmabuf_adc(s))
- return 0;
- poll_wait(file, &s->dma_adc.wait, wait);
- }
- spin_lock_irqsave(&s->lock, flags);
- cs4297a_update_ptr(s,CS_FALSE);
- if (file->f_mode & FMODE_WRITE) {
- if (s->dma_dac.mapped) {
- if (s->dma_dac.count >=
- (signed) s->dma_dac.fragsize) {
- if (s->dma_dac.wakeup)
- mask |= POLLOUT | POLLWRNORM;
- else
- mask = 0;
- s->dma_dac.wakeup = 0;
- }
- } else {
- if ((signed) (s->dma_dac.sbufsz/2) >= s->dma_dac.count)
- mask |= POLLOUT | POLLWRNORM;
- }
- } else if (file->f_mode & FMODE_READ) {
- if (s->dma_adc.mapped) {
- if (s->dma_adc.count >= (signed) s->dma_adc.fragsize)
- mask |= POLLIN | POLLRDNORM;
- } else {
- if (s->dma_adc.count > 0)
- mask |= POLLIN | POLLRDNORM;
- }
- }
- spin_unlock_irqrestore(&s->lock, flags);
- CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
- printk(KERN_INFO "cs4297a: cs4297a_poll()- 0x%.8x\n",
- mask));
- return mask;
-}
-
-
-static int cs4297a_mmap(struct file *file, struct vm_area_struct *vma)
-{
- /* XXXKW currently no mmap support */
- return -EINVAL;
- return 0;
-}
-
-
-static int cs4297a_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct cs4297a_state *s =
- (struct cs4297a_state *) file->private_data;
- unsigned long flags;
- audio_buf_info abinfo;
- count_info cinfo;
- int val, mapped, ret;
-
- CS_DBGOUT(CS_FUNCTION|CS_IOCTL, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): file=0x%.8x cmd=0x%.8x\n",
- (unsigned) file, cmd));
-#if CSDEBUG
- cs_printioctl(cmd);
-#endif
- VALIDATE_STATE(s);
- mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
- ((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
- switch (cmd) {
- case OSS_GETVERSION:
- CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): SOUND_VERSION=0x%.8x\n",
- SOUND_VERSION));
- return put_user(SOUND_VERSION, (int *) arg);
-
- case SNDCTL_DSP_SYNC:
- CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): DSP_SYNC\n"));
- if (file->f_mode & FMODE_WRITE)
- return drain_dac(s,
- 0 /*file->f_flags & O_NONBLOCK */
- );
- return 0;
-
- case SNDCTL_DSP_SETDUPLEX:
- return 0;
-
- case SNDCTL_DSP_GETCAPS:
- return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME |
- DSP_CAP_TRIGGER | DSP_CAP_MMAP,
- (int *) arg);
-
- case SNDCTL_DSP_RESET:
- CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): DSP_RESET\n"));
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- synchronize_irq(s->irq);
- s->dma_dac.count = s->dma_dac.total_bytes =
- s->dma_dac.blocks = s->dma_dac.wakeup = 0;
- s->dma_dac.swptr = s->dma_dac.hwptr =
- (int)(((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) -
- s->dma_dac.descrtab_phys) / sizeof(serdma_descr_t));
- }
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- synchronize_irq(s->irq);
- s->dma_adc.count = s->dma_adc.total_bytes =
- s->dma_adc.blocks = s->dma_dac.wakeup = 0;
- s->dma_adc.swptr = s->dma_adc.hwptr =
- (int)(((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_RX)) & M_DMA_CURDSCR_ADDR) -
- s->dma_adc.descrtab_phys) / sizeof(serdma_descr_t));
- }
- return 0;
-
- case SNDCTL_DSP_SPEED:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): DSP_SPEED val=%d -> 48000\n", val));
- val = 48000;
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_STEREO:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): DSP_STEREO val=%d\n", val));
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.ready = 0;
- s->prop_adc.channels = val ? 2 : 1;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.ready = 0;
- s->prop_dac.channels = val ? 2 : 1;
- }
- return 0;
-
- case SNDCTL_DSP_CHANNELS:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): DSP_CHANNELS val=%d\n",
- val));
- if (val != 0) {
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.ready = 0;
- if (val >= 2)
- s->prop_adc.channels = 2;
- else
- s->prop_adc.channels = 1;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.ready = 0;
- if (val >= 2)
- s->prop_dac.channels = 2;
- else
- s->prop_dac.channels = 1;
- }
- }
-
- if (file->f_mode & FMODE_WRITE)
- val = s->prop_dac.channels;
- else if (file->f_mode & FMODE_READ)
- val = s->prop_adc.channels;
-
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_GETFMTS: // Returns a mask
- CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): DSP_GETFMT val=0x%.8x\n",
- AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 |
- AFMT_U8));
- return put_user(AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 |
- AFMT_U8, (int *) arg);
-
- case SNDCTL_DSP_SETFMT:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): DSP_SETFMT val=0x%.8x\n",
- val));
- if (val != AFMT_QUERY) {
- if (file->f_mode & FMODE_READ) {
- stop_adc(s);
- s->dma_adc.ready = 0;
- if (val != AFMT_S16_LE
- && val != AFMT_U16_LE && val != AFMT_S8
- && val != AFMT_U8)
- val = AFMT_U8;
- s->prop_adc.fmt = val;
- s->prop_adc.fmt_original = s->prop_adc.fmt;
- }
- if (file->f_mode & FMODE_WRITE) {
- stop_dac(s);
- s->dma_dac.ready = 0;
- if (val != AFMT_S16_LE
- && val != AFMT_U16_LE && val != AFMT_S8
- && val != AFMT_U8)
- val = AFMT_U8;
- s->prop_dac.fmt = val;
- s->prop_dac.fmt_original = s->prop_dac.fmt;
- }
- } else {
- if (file->f_mode & FMODE_WRITE)
- val = s->prop_dac.fmt_original;
- else if (file->f_mode & FMODE_READ)
- val = s->prop_adc.fmt_original;
- }
- CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): DSP_SETFMT return val=0x%.8x\n",
- val));
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_POST:
- CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): DSP_POST\n"));
- return 0;
-
- case SNDCTL_DSP_GETTRIGGER:
- val = 0;
- if (file->f_mode & s->ena & FMODE_READ)
- val |= PCM_ENABLE_INPUT;
- if (file->f_mode & s->ena & FMODE_WRITE)
- val |= PCM_ENABLE_OUTPUT;
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_SETTRIGGER:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (file->f_mode & FMODE_READ) {
- if (val & PCM_ENABLE_INPUT) {
- if (!s->dma_adc.ready
- && (ret = prog_dmabuf_adc(s)))
- return ret;
- start_adc(s);
- } else
- stop_adc(s);
- }
- if (file->f_mode & FMODE_WRITE) {
- if (val & PCM_ENABLE_OUTPUT) {
- if (!s->dma_dac.ready
- && (ret = prog_dmabuf_dac(s)))
- return ret;
- start_dac(s);
- } else
- stop_dac(s);
- }
- return 0;
-
- case SNDCTL_DSP_GETOSPACE:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)))
- return val;
- spin_lock_irqsave(&s->lock, flags);
- cs4297a_update_ptr(s,CS_FALSE);
- abinfo.fragsize = s->dma_dac.fragsize;
- if (s->dma_dac.mapped)
- abinfo.bytes = s->dma_dac.sbufsz;
- else
- abinfo.bytes =
- s->dma_dac.sbufsz - s->dma_dac.count;
- abinfo.fragstotal = s->dma_dac.numfrag;
- abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;
- CS_DBGOUT(CS_FUNCTION | CS_PARMS, 4, printk(KERN_INFO
- "cs4297a: cs4297a_ioctl(): GETOSPACE .fragsize=%d .bytes=%d .fragstotal=%d .fragments=%d\n",
- abinfo.fragsize,abinfo.bytes,abinfo.fragstotal,
- abinfo.fragments));
- spin_unlock_irqrestore(&s->lock, flags);
- return copy_to_user((void *) arg, &abinfo,
- sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_GETISPACE:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)))
- return val;
- spin_lock_irqsave(&s->lock, flags);
- cs4297a_update_ptr(s,CS_FALSE);
- if (s->conversion) {
- abinfo.fragsize = s->dma_adc.fragsize / 2;
- abinfo.bytes = s->dma_adc.count / 2;
- abinfo.fragstotal = s->dma_adc.numfrag;
- abinfo.fragments =
- abinfo.bytes >> (s->dma_adc.fragshift - 1);
- } else {
- abinfo.fragsize = s->dma_adc.fragsize;
- abinfo.bytes = s->dma_adc.count;
- abinfo.fragstotal = s->dma_adc.numfrag;
- abinfo.fragments =
- abinfo.bytes >> s->dma_adc.fragshift;
- }
- spin_unlock_irqrestore(&s->lock, flags);
- return copy_to_user((void *) arg, &abinfo,
- sizeof(abinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_NONBLOCK:
- spin_lock(&file->f_lock);
- file->f_flags |= O_NONBLOCK;
- spin_unlock(&file->f_lock);
- return 0;
-
- case SNDCTL_DSP_GETODELAY:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- if(!s->dma_dac.ready && prog_dmabuf_dac(s))
- return 0;
- spin_lock_irqsave(&s->lock, flags);
- cs4297a_update_ptr(s,CS_FALSE);
- val = s->dma_dac.count;
- spin_unlock_irqrestore(&s->lock, flags);
- return put_user(val, (int *) arg);
-
- case SNDCTL_DSP_GETIPTR:
- if (!(file->f_mode & FMODE_READ))
- return -EINVAL;
- if(!s->dma_adc.ready && prog_dmabuf_adc(s))
- return 0;
- spin_lock_irqsave(&s->lock, flags);
- cs4297a_update_ptr(s,CS_FALSE);
- cinfo.bytes = s->dma_adc.total_bytes;
- if (s->dma_adc.mapped) {
- cinfo.blocks =
- (cinfo.bytes >> s->dma_adc.fragshift) -
- s->dma_adc.blocks;
- s->dma_adc.blocks =
- cinfo.bytes >> s->dma_adc.fragshift;
- } else {
- if (s->conversion) {
- cinfo.blocks =
- s->dma_adc.count /
- 2 >> (s->dma_adc.fragshift - 1);
- } else
- cinfo.blocks =
- s->dma_adc.count >> s->dma_adc.
- fragshift;
- }
- if (s->conversion)
- cinfo.ptr = s->dma_adc.hwptr / 2;
- else
- cinfo.ptr = s->dma_adc.hwptr;
- if (s->dma_adc.mapped)
- s->dma_adc.count &= s->dma_adc.fragsize - 1;
- spin_unlock_irqrestore(&s->lock, flags);
- return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_GETOPTR:
- if (!(file->f_mode & FMODE_WRITE))
- return -EINVAL;
- if(!s->dma_dac.ready && prog_dmabuf_dac(s))
- return 0;
- spin_lock_irqsave(&s->lock, flags);
- cs4297a_update_ptr(s,CS_FALSE);
- cinfo.bytes = s->dma_dac.total_bytes;
- if (s->dma_dac.mapped) {
- cinfo.blocks =
- (cinfo.bytes >> s->dma_dac.fragshift) -
- s->dma_dac.blocks;
- s->dma_dac.blocks =
- cinfo.bytes >> s->dma_dac.fragshift;
- } else {
- cinfo.blocks =
- s->dma_dac.count >> s->dma_dac.fragshift;
- }
- cinfo.ptr = s->dma_dac.hwptr;
- if (s->dma_dac.mapped)
- s->dma_dac.count &= s->dma_dac.fragsize - 1;
- spin_unlock_irqrestore(&s->lock, flags);
- return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0;
-
- case SNDCTL_DSP_GETBLKSIZE:
- if (file->f_mode & FMODE_WRITE) {
- if ((val = prog_dmabuf_dac(s)))
- return val;
- return put_user(s->dma_dac.fragsize, (int *) arg);
- }
- if ((val = prog_dmabuf_adc(s)))
- return val;
- if (s->conversion)
- return put_user(s->dma_adc.fragsize / 2,
- (int *) arg);
- else
- return put_user(s->dma_adc.fragsize, (int *) arg);
-
- case SNDCTL_DSP_SETFRAGMENT:
- if (get_user(val, (int *) arg))
- return -EFAULT;
- return 0; // Say OK, but do nothing.
-
- case SNDCTL_DSP_SUBDIVIDE:
- if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision)
- || (file->f_mode & FMODE_WRITE
- && s->dma_dac.subdivision)) return -EINVAL;
- if (get_user(val, (int *) arg))
- return -EFAULT;
- if (val != 1 && val != 2 && val != 4)
- return -EINVAL;
- if (file->f_mode & FMODE_READ)
- s->dma_adc.subdivision = val;
- else if (file->f_mode & FMODE_WRITE)
- s->dma_dac.subdivision = val;
- return 0;
-
- case SOUND_PCM_READ_RATE:
- if (file->f_mode & FMODE_READ)
- return put_user(s->prop_adc.rate, (int *) arg);
- else if (file->f_mode & FMODE_WRITE)
- return put_user(s->prop_dac.rate, (int *) arg);
-
- case SOUND_PCM_READ_CHANNELS:
- if (file->f_mode & FMODE_READ)
- return put_user(s->prop_adc.channels, (int *) arg);
- else if (file->f_mode & FMODE_WRITE)
- return put_user(s->prop_dac.channels, (int *) arg);
-
- case SOUND_PCM_READ_BITS:
- if (file->f_mode & FMODE_READ)
- return
- put_user(
- (s->prop_adc.
- fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16,
- (int *) arg);
- else if (file->f_mode & FMODE_WRITE)
- return
- put_user(
- (s->prop_dac.
- fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16,
- (int *) arg);
-
- case SOUND_PCM_WRITE_FILTER:
- case SNDCTL_DSP_SETSYNCRO:
- case SOUND_PCM_READ_FILTER:
- return -EINVAL;
- }
- return mixer_ioctl(s, cmd, arg);
-}
-
-static long cs4297a_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
-{
- int ret;
-
- mutex_lock(&swarm_cs4297a_mutex);
- ret = cs4297a_ioctl(file, cmd, arg);
- mutex_unlock(&swarm_cs4297a_mutex);
-
- return ret;
-}
-
-static int cs4297a_release(struct inode *inode, struct file *file)
-{
- struct cs4297a_state *s =
- (struct cs4297a_state *) file->private_data;
-
- CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk(KERN_INFO
- "cs4297a: cs4297a_release(): inode=0x%.8x file=0x%.8x f_mode=0x%x\n",
- (unsigned) inode, (unsigned) file, file->f_mode));
- VALIDATE_STATE(s);
-
- if (file->f_mode & FMODE_WRITE) {
- drain_dac(s, file->f_flags & O_NONBLOCK);
- mutex_lock(&s->open_sem_dac);
- stop_dac(s);
- dealloc_dmabuf(s, &s->dma_dac);
- s->open_mode &= ~FMODE_WRITE;
- mutex_unlock(&s->open_sem_dac);
- wake_up(&s->open_wait_dac);
- }
- if (file->f_mode & FMODE_READ) {
- drain_adc(s, file->f_flags & O_NONBLOCK);
- mutex_lock(&s->open_sem_adc);
- stop_adc(s);
- dealloc_dmabuf(s, &s->dma_adc);
- s->open_mode &= ~FMODE_READ;
- mutex_unlock(&s->open_sem_adc);
- wake_up(&s->open_wait_adc);
- }
- return 0;
-}
-
-static int cs4297a_locked_open(struct inode *inode, struct file *file)
-{
- int minor = iminor(inode);
- struct cs4297a_state *s=NULL;
- struct list_head *entry;
-
- CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO
- "cs4297a: cs4297a_open(): inode=0x%.8x file=0x%.8x f_mode=0x%x\n",
- (unsigned) inode, (unsigned) file, file->f_mode));
- CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO
- "cs4297a: status = %08x\n", (int)__raw_readq(SS_CSR(R_SER_STATUS_DEBUG))));
-
- list_for_each(entry, &cs4297a_devs)
- {
- s = list_entry(entry, struct cs4297a_state, list);
-
- if (!((s->dev_audio ^ minor) & ~0xf))
- break;
- }
- if (entry == &cs4297a_devs)
- return -ENODEV;
- if (!s) {
- CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO
- "cs4297a: cs4297a_open(): Error - unable to find audio state struct\n"));
- return -ENODEV;
- }
- VALIDATE_STATE(s);
- file->private_data = s;
-
- // wait for device to become free
- if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) {
- CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, printk(KERN_INFO
- "cs4297a: cs4297a_open(): Error - must open READ and/or WRITE\n"));
- return -ENODEV;
- }
- if (file->f_mode & FMODE_WRITE) {
- if (__raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_TX)) != 0) {
- printk(KERN_ERR "cs4297a: TX pipe needs to drain\n");
- while (__raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_TX)))
- ;
- }
-
- mutex_lock(&s->open_sem_dac);
- while (s->open_mode & FMODE_WRITE) {
- if (file->f_flags & O_NONBLOCK) {
- mutex_unlock(&s->open_sem_dac);
- return -EBUSY;
- }
- mutex_unlock(&s->open_sem_dac);
- interruptible_sleep_on(&s->open_wait_dac);
-
- if (signal_pending(current)) {
- printk("open - sig pending\n");
- return -ERESTARTSYS;
- }
- mutex_lock(&s->open_sem_dac);
- }
- }
- if (file->f_mode & FMODE_READ) {
- mutex_lock(&s->open_sem_adc);
- while (s->open_mode & FMODE_READ) {
- if (file->f_flags & O_NONBLOCK) {
- mutex_unlock(&s->open_sem_adc);
- return -EBUSY;
- }
- mutex_unlock(&s->open_sem_adc);
- interruptible_sleep_on(&s->open_wait_adc);
-
- if (signal_pending(current)) {
- printk("open - sig pending\n");
- return -ERESTARTSYS;
- }
- mutex_lock(&s->open_sem_adc);
- }
- }
- s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
- if (file->f_mode & FMODE_READ) {
- s->prop_adc.fmt = AFMT_S16_BE;
- s->prop_adc.fmt_original = s->prop_adc.fmt;
- s->prop_adc.channels = 2;
- s->prop_adc.rate = 48000;
- s->conversion = 0;
- s->ena &= ~FMODE_READ;
- s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags =
- s->dma_adc.subdivision = 0;
- mutex_unlock(&s->open_sem_adc);
-
- if (prog_dmabuf_adc(s)) {
- CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR
- "cs4297a: adc Program dmabufs failed.\n"));
- cs4297a_release(inode, file);
- return -ENOMEM;
- }
- }
- if (file->f_mode & FMODE_WRITE) {
- s->prop_dac.fmt = AFMT_S16_BE;
- s->prop_dac.fmt_original = s->prop_dac.fmt;
- s->prop_dac.channels = 2;
- s->prop_dac.rate = 48000;
- s->conversion = 0;
- s->ena &= ~FMODE_WRITE;
- s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags =
- s->dma_dac.subdivision = 0;
- mutex_unlock(&s->open_sem_dac);
-
- if (prog_dmabuf_dac(s)) {
- CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR
- "cs4297a: dac Program dmabufs failed.\n"));
- cs4297a_release(inode, file);
- return -ENOMEM;
- }
- }
- CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2,
- printk(KERN_INFO "cs4297a: cs4297a_open()- 0\n"));
- return nonseekable_open(inode, file);
-}
-
-static int cs4297a_open(struct inode *inode, struct file *file)
-{
- int ret;
-
- mutex_lock(&swarm_cs4297a_mutex);
- ret = cs4297a_open(inode, file);
- mutex_unlock(&swarm_cs4297a_mutex);
-
- return ret;
-}
-
-// ******************************************************************************************
-// Wave (audio) file operations struct.
-// ******************************************************************************************
-static const struct file_operations cs4297a_audio_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = cs4297a_read,
- .write = cs4297a_write,
- .poll = cs4297a_poll,
- .unlocked_ioctl = cs4297a_unlocked_ioctl,
- .mmap = cs4297a_mmap,
- .open = cs4297a_open,
- .release = cs4297a_release,
-};
-
-static void cs4297a_interrupt(int irq, void *dev_id)
-{
- struct cs4297a_state *s = (struct cs4297a_state *) dev_id;
- u32 status;
-
- status = __raw_readq(SS_CSR(R_SER_STATUS_DEBUG));
-
- CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO
- "cs4297a: cs4297a_interrupt() HISR=0x%.8x\n", status));
-
-#if 0
- /* XXXKW what check *should* be done here? */
- if (!(status & (M_SYNCSER_RX_EOP_COUNT | M_SYNCSER_RX_OVERRUN | M_SYNCSER_RX_SYNC_ERR))) {
- status = __raw_readq(SS_CSR(R_SER_STATUS));
- printk(KERN_ERR "cs4297a: unexpected interrupt (status %08x)\n", status);
- return;
- }
-#endif
-
- if (status & M_SYNCSER_RX_SYNC_ERR) {
- status = __raw_readq(SS_CSR(R_SER_STATUS));
- printk(KERN_ERR "cs4297a: rx sync error (status %08x)\n", status);
- return;
- }
-
- if (status & M_SYNCSER_RX_OVERRUN) {
- int newptr, i;
- s->stats.rx_ovrrn++;
- printk(KERN_ERR "cs4297a: receive FIFO overrun\n");
-
- /* Fix things up: get the receive descriptor pool
- clean and give them back to the hardware */
- while (__raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_RX)))
- ;
- newptr = (unsigned) (((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_RX)) & M_DMA_CURDSCR_ADDR) -
- s->dma_adc.descrtab_phys) / sizeof(serdma_descr_t));
- for (i=0; i<DMA_DESCR; i++) {
- s->dma_adc.descrtab[i].descr_a &= ~M_DMA_SERRX_SOP;
- }
- s->dma_adc.swptr = s->dma_adc.hwptr = newptr;
- s->dma_adc.count = 0;
- s->dma_adc.sb_swptr = s->dma_adc.sb_hwptr = s->dma_adc.sample_buf;
- __raw_writeq(DMA_DESCR, SS_CSR(R_SER_DMA_DSCR_COUNT_RX));
- }
-
- spin_lock(&s->lock);
- cs4297a_update_ptr(s,CS_TRUE);
- spin_unlock(&s->lock);
-
- CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO
- "cs4297a: cs4297a_interrupt()-\n"));
-}
-
-#if 0
-static struct initvol {
- int mixch;
- int vol;
-} initvol[] __initdata = {
-
- {SOUND_MIXER_WRITE_VOLUME, 0x4040},
- {SOUND_MIXER_WRITE_PCM, 0x4040},
- {SOUND_MIXER_WRITE_SYNTH, 0x4040},
- {SOUND_MIXER_WRITE_CD, 0x4040},
- {SOUND_MIXER_WRITE_LINE, 0x4040},
- {SOUND_MIXER_WRITE_LINE1, 0x4040},
- {SOUND_MIXER_WRITE_RECLEV, 0x0000},
- {SOUND_MIXER_WRITE_SPEAKER, 0x4040},
- {SOUND_MIXER_WRITE_MIC, 0x0000}
-};
-#endif
-
-static int __init cs4297a_init(void)
-{
- struct cs4297a_state *s;
- u32 pwr, id;
- mm_segment_t fs;
- int rval;
-#ifndef CONFIG_BCM_CS4297A_CSWARM
- u64 cfg;
- int mdio_val;
-#endif
-
- CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO
- "cs4297a: cs4297a_init_module()+ \n"));
-
-#ifndef CONFIG_BCM_CS4297A_CSWARM
- mdio_val = __raw_readq(KSEG1 + A_MAC_REGISTER(2, R_MAC_MDIO)) &
- (M_MAC_MDIO_DIR|M_MAC_MDIO_OUT);
-
- /* Check syscfg for synchronous serial on port 1 */
- cfg = __raw_readq(KSEG1 + A_SCD_SYSTEM_CFG);
- if (!(cfg & M_SYS_SER1_ENABLE)) {
- __raw_writeq(cfg | M_SYS_SER1_ENABLE, KSEG1+A_SCD_SYSTEM_CFG);
- cfg = __raw_readq(KSEG1 + A_SCD_SYSTEM_CFG);
- if (!(cfg & M_SYS_SER1_ENABLE)) {
- printk(KERN_INFO "cs4297a: serial port 1 not configured for synchronous operation\n");
- return -1;
- }
-
- printk(KERN_INFO "cs4297a: serial port 1 switching to synchronous operation\n");
-
- /* Force the codec (on SWARM) to reset by clearing
- GENO, preserving MDIO (no effect on CSWARM) */
- __raw_writeq(mdio_val, KSEG1+A_MAC_REGISTER(2, R_MAC_MDIO));
- udelay(10);
- }
-
- /* Now set GENO */
- __raw_writeq(mdio_val | M_MAC_GENC, KSEG1+A_MAC_REGISTER(2, R_MAC_MDIO));
- /* Give the codec some time to finish resetting (start the bit clock) */
- udelay(100);
-#endif
-
- if (!(s = kzalloc(sizeof(struct cs4297a_state), GFP_KERNEL))) {
- CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
- "cs4297a: probe() no memory for state struct.\n"));
- return -1;
- }
- s->magic = CS4297a_MAGIC;
- init_waitqueue_head(&s->dma_adc.wait);
- init_waitqueue_head(&s->dma_dac.wait);
- init_waitqueue_head(&s->dma_adc.reg_wait);
- init_waitqueue_head(&s->dma_dac.reg_wait);
- init_waitqueue_head(&s->open_wait);
- init_waitqueue_head(&s->open_wait_adc);
- init_waitqueue_head(&s->open_wait_dac);
- mutex_init(&s->open_sem_adc);
- mutex_init(&s->open_sem_dac);
- spin_lock_init(&s->lock);
-
- s->irq = K_INT_SER_1;
-
- if (request_irq
- (s->irq, cs4297a_interrupt, 0, "Crystal CS4297a", s)) {
- CS_DBGOUT(CS_INIT | CS_ERROR, 1,
- printk(KERN_ERR "cs4297a: irq %u in use\n", s->irq));
- goto err_irq;
- }
- if ((s->dev_audio = register_sound_dsp(&cs4297a_audio_fops, -1)) <
- 0) {
- CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
- "cs4297a: probe() register_sound_dsp() failed.\n"));
- goto err_dev1;
- }
- if ((s->dev_mixer = register_sound_mixer(&cs4297a_mixer_fops, -1)) <
- 0) {
- CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
- "cs4297a: probe() register_sound_mixer() failed.\n"));
- goto err_dev2;
- }
-
- if (ser_init(s) || dma_init(s)) {
- CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
- "cs4297a: ser_init failed.\n"));
- goto err_dev3;
- }
-
- do {
- udelay(4000);
- rval = cs4297a_read_ac97(s, AC97_POWER_CONTROL, &pwr);
- } while (!rval && (pwr != 0xf));
-
- if (!rval) {
- char *sb1250_duart_present;
-
- fs = get_fs();
- set_fs(KERNEL_DS);
-#if 0
- val = SOUND_MASK_LINE;
- mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long) &val);
- for (i = 0; i < ARRAY_SIZE(initvol); i++) {
- val = initvol[i].vol;
- mixer_ioctl(s, initvol[i].mixch, (unsigned long) &val);
- }
-// cs4297a_write_ac97(s, 0x18, 0x0808);
-#else
- // cs4297a_write_ac97(s, 0x5e, 0x180);
- cs4297a_write_ac97(s, 0x02, 0x0808);
- cs4297a_write_ac97(s, 0x18, 0x0808);
-#endif
- set_fs(fs);
-
- list_add(&s->list, &cs4297a_devs);
-
- cs4297a_read_ac97(s, AC97_VENDOR_ID1, &id);
-
- sb1250_duart_present = symbol_get(sb1250_duart_present);
- if (sb1250_duart_present)
- sb1250_duart_present[1] = 0;
-
- printk(KERN_INFO "cs4297a: initialized (vendor id = %x)\n", id);
-
- CS_DBGOUT(CS_INIT | CS_FUNCTION, 2,
- printk(KERN_INFO "cs4297a: cs4297a_init_module()-\n"));
-
- return 0;
- }
-
- err_dev3:
- unregister_sound_mixer(s->dev_mixer);
- err_dev2:
- unregister_sound_dsp(s->dev_audio);
- err_dev1:
- free_irq(s->irq, s);
- err_irq:
- kfree(s);
-
- printk(KERN_INFO "cs4297a: initialization failed\n");
-
- return -1;
-}
-
-static void __exit cs4297a_cleanup(void)
-{
- /*
- XXXKW
- disable_irq, free_irq
- drain DMA queue
- disable DMA
- disable TX/RX
- free memory
- */
- CS_DBGOUT(CS_INIT | CS_FUNCTION, 2,
- printk(KERN_INFO "cs4297a: cleanup_cs4297a() finished\n"));
-}
-
-// ---------------------------------------------------------------------
-
-MODULE_AUTHOR("Kip Walker, Broadcom Corp.");
-MODULE_DESCRIPTION("Cirrus Logic CS4297a Driver for Broadcom SWARM board");
-
-// ---------------------------------------------------------------------
-
-module_init(cs4297a_init);
-module_exit(cs4297a_cleanup);
diff --git a/sound/oss/sys_timer.c b/sound/oss/sys_timer.c
deleted file mode 100644
index 8db6aefe15e4..000000000000
--- a/sound/oss/sys_timer.c
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * sound/oss/sys_timer.c
- *
- * The default timer for the Level 2 sequencer interface
- * Uses the (1/HZ sec) timer of kernel.
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-/*
- * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed)
- * Andrew Veliath : adapted tmr2ticks from level 1 sequencer (avoid overflow)
- */
-#include <linux/spinlock.h>
-#include "sound_config.h"
-
-static volatile int opened, tmr_running;
-static volatile time_t tmr_offs, tmr_ctr;
-static volatile unsigned long ticks_offs;
-static volatile int curr_tempo, curr_timebase;
-static volatile unsigned long curr_ticks;
-static volatile unsigned long next_event_time;
-static unsigned long prev_event_time;
-
-static void poll_def_tmr(unsigned long dummy);
-static DEFINE_SPINLOCK(lock);
-static DEFINE_TIMER(def_tmr, poll_def_tmr, 0, 0);
-
-static unsigned long
-tmr2ticks(int tmr_value)
-{
- /*
- * Convert timer ticks to MIDI ticks
- */
-
- unsigned long tmp;
- unsigned long scale;
-
- /* tmr_value (ticks per sec) *
- 1000000 (usecs per sec) / HZ (ticks per sec) -=> usecs */
- tmp = tmr_value * (1000000 / HZ);
- scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */
- return (tmp + scale / 2) / scale;
-}
-
-static void
-poll_def_tmr(unsigned long dummy)
-{
-
- if (opened)
- {
-
- {
- def_tmr.expires = (1) + jiffies;
- add_timer(&def_tmr);
- };
-
- if (tmr_running)
- {
- spin_lock(&lock);
- tmr_ctr++;
- curr_ticks = ticks_offs + tmr2ticks(tmr_ctr);
-
- if (curr_ticks >= next_event_time)
- {
- next_event_time = (unsigned long) -1;
- sequencer_timer(0);
- }
- spin_unlock(&lock);
- }
- }
-}
-
-static void
-tmr_reset(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&lock,flags);
- tmr_offs = 0;
- ticks_offs = 0;
- tmr_ctr = 0;
- next_event_time = (unsigned long) -1;
- prev_event_time = 0;
- curr_ticks = 0;
- spin_unlock_irqrestore(&lock,flags);
-}
-
-static int
-def_tmr_open(int dev, int mode)
-{
- if (opened)
- return -EBUSY;
-
- tmr_reset();
- curr_tempo = 60;
- curr_timebase = 100;
- opened = 1;
- {
- def_tmr.expires = (1) + jiffies;
- add_timer(&def_tmr);
- };
-
- return 0;
-}
-
-static void
-def_tmr_close(int dev)
-{
- opened = tmr_running = 0;
- del_timer(&def_tmr);
-}
-
-static int
-def_tmr_event(int dev, unsigned char *event)
-{
- unsigned char cmd = event[1];
- unsigned long parm = *(int *) &event[4];
-
- switch (cmd)
- {
- case TMR_WAIT_REL:
- parm += prev_event_time;
- case TMR_WAIT_ABS:
- if (parm > 0)
- {
- long time;
-
- if (parm <= curr_ticks) /* It's the time */
- return TIMER_NOT_ARMED;
-
- time = parm;
- next_event_time = prev_event_time = time;
-
- return TIMER_ARMED;
- }
- break;
-
- case TMR_START:
- tmr_reset();
- tmr_running = 1;
- break;
-
- case TMR_STOP:
- tmr_running = 0;
- break;
-
- case TMR_CONTINUE:
- tmr_running = 1;
- break;
-
- case TMR_TEMPO:
- if (parm)
- {
- if (parm < 8)
- parm = 8;
- if (parm > 360)
- parm = 360;
- tmr_offs = tmr_ctr;
- ticks_offs += tmr2ticks(tmr_ctr);
- tmr_ctr = 0;
- curr_tempo = parm;
- }
- break;
-
- case TMR_ECHO:
- seq_copy_to_input(event, 8);
- break;
-
- default:;
- }
-
- return TIMER_NOT_ARMED;
-}
-
-static unsigned long
-def_tmr_get_time(int dev)
-{
- if (!opened)
- return 0;
-
- return curr_ticks;
-}
-
-/* same as sound_timer.c:timer_ioctl!? */
-static int def_tmr_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- int __user *p = arg;
- int val;
-
- switch (cmd) {
- case SNDCTL_TMR_SOURCE:
- return __put_user(TMR_INTERNAL, p);
-
- case SNDCTL_TMR_START:
- tmr_reset();
- tmr_running = 1;
- return 0;
-
- case SNDCTL_TMR_STOP:
- tmr_running = 0;
- return 0;
-
- case SNDCTL_TMR_CONTINUE:
- tmr_running = 1;
- return 0;
-
- case SNDCTL_TMR_TIMEBASE:
- if (__get_user(val, p))
- return -EFAULT;
- if (val) {
- if (val < 1)
- val = 1;
- if (val > 1000)
- val = 1000;
- curr_timebase = val;
- }
- return __put_user(curr_timebase, p);
-
- case SNDCTL_TMR_TEMPO:
- if (__get_user(val, p))
- return -EFAULT;
- if (val) {
- if (val < 8)
- val = 8;
- if (val > 250)
- val = 250;
- tmr_offs = tmr_ctr;
- ticks_offs += tmr2ticks(tmr_ctr);
- tmr_ctr = 0;
- curr_tempo = val;
- reprogram_timer();
- }
- return __put_user(curr_tempo, p);
-
- case SNDCTL_SEQ_CTRLRATE:
- if (__get_user(val, p))
- return -EFAULT;
- if (val != 0) /* Can't change */
- return -EINVAL;
- val = ((curr_tempo * curr_timebase) + 30) / 60;
- return __put_user(val, p);
-
- case SNDCTL_SEQ_GETTIME:
- return __put_user(curr_ticks, p);
-
- case SNDCTL_TMR_METRONOME:
- /* NOP */
- break;
-
- default:;
- }
- return -EINVAL;
-}
-
-static void
-def_tmr_arm(int dev, long time)
-{
- if (time < 0)
- time = curr_ticks + 1;
- else if (time <= curr_ticks) /* It's the time */
- return;
-
- next_event_time = prev_event_time = time;
-
- return;
-}
-
-struct sound_timer_operations default_sound_timer =
-{
- .owner = THIS_MODULE,
- .info = {"System clock", 0},
- .priority = 0, /* Priority */
- .devlink = 0, /* Local device link */
- .open = def_tmr_open,
- .close = def_tmr_close,
- .event = def_tmr_event,
- .get_time = def_tmr_get_time,
- .ioctl = def_tmr_ioctl,
- .arm_timer = def_tmr_arm
-};
diff --git a/sound/oss/trix.c b/sound/oss/trix.c
deleted file mode 100644
index e04169e8e3f8..000000000000
--- a/sound/oss/trix.c
+++ /dev/null
@@ -1,525 +0,0 @@
-/*
- * sound/oss/trix.c
- *
- * Low level driver for the MediaTrix AudioTrix Pro
- * (MT-0002-PC Control Chip)
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- * Changes
- * Alan Cox Modularisation, cleanup.
- * Christoph Hellwig Adapted to module_init/module_exit
- * Arnaldo C. de Melo Got rid of attach_uart401
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-
-#include "sound_config.h"
-#include "sb.h"
-#include "sound_firmware.h"
-
-#include "ad1848.h"
-#include "mpu401.h"
-
-#include "trix_boot.h"
-
-static int mpu;
-
-static int joystick;
-
-static unsigned char trix_read(int addr)
-{
- outb(((unsigned char) addr), 0x390); /* MT-0002-PC ASIC address */
- return inb(0x391); /* MT-0002-PC ASIC data */
-}
-
-static void trix_write(int addr, int data)
-{
- outb(((unsigned char) addr), 0x390); /* MT-0002-PC ASIC address */
- outb(((unsigned char) data), 0x391); /* MT-0002-PC ASIC data */
-}
-
-static void download_boot(int base)
-{
- int i = 0, n = trix_boot_len;
-
- if (trix_boot_len == 0)
- return;
-
- trix_write(0xf8, 0x00); /* ??????? */
- outb((0x01), base + 6); /* Clear the internal data pointer */
- outb((0x00), base + 6); /* Restart */
-
- /*
- * Write the boot code to the RAM upload/download register.
- * Each write increments the internal data pointer.
- */
- outb((0x01), base + 6); /* Clear the internal data pointer */
- outb((0x1A), 0x390); /* Select RAM download/upload port */
-
- for (i = 0; i < n; i++)
- outb((trix_boot[i]), 0x391);
- for (i = n; i < 10016; i++) /* Clear up to first 16 bytes of data RAM */
- outb((0x00), 0x391);
- outb((0x00), base + 6); /* Reset */
- outb((0x50), 0x390); /* ?????? */
-
-}
-
-static int trix_set_wss_port(struct address_info *hw_config)
-{
- unsigned char addr_bits;
-
- if (trix_read(0x15) != 0x71) /* No ASIC signature */
- {
- MDB(printk(KERN_ERR "No AudioTrix ASIC signature found\n"));
- return 0;
- }
-
- /*
- * Reset some registers.
- */
-
- trix_write(0x13, 0);
- trix_write(0x14, 0);
-
- /*
- * Configure the ASIC to place the codec to the proper I/O location
- */
-
- switch (hw_config->io_base)
- {
- case 0x530:
- addr_bits = 0;
- break;
- case 0x604:
- addr_bits = 1;
- break;
- case 0xE80:
- addr_bits = 2;
- break;
- case 0xF40:
- addr_bits = 3;
- break;
- default:
- return 0;
- }
-
- trix_write(0x19, (trix_read(0x19) & 0x03) | addr_bits);
- return 1;
-}
-
-/*
- * Probe and attach routines for the Windows Sound System mode of
- * AudioTrix Pro
- */
-
-static int __init init_trix_wss(struct address_info *hw_config)
-{
- static unsigned char dma_bits[4] = {
- 1, 2, 0, 3
- };
- struct resource *ports;
- int config_port = hw_config->io_base + 0;
- int dma1 = hw_config->dma, dma2 = hw_config->dma2;
- int old_num_mixers = num_mixers;
- u8 config, bits;
- int ret;
-
- switch(hw_config->irq) {
- case 7:
- bits = 8;
- break;
- case 9:
- bits = 0x10;
- break;
- case 10:
- bits = 0x18;
- break;
- case 11:
- bits = 0x20;
- break;
- default:
- printk(KERN_ERR "AudioTrix: Bad WSS IRQ %d\n", hw_config->irq);
- return 0;
- }
-
- switch (dma1) {
- case 0:
- case 1:
- case 3:
- break;
- default:
- printk(KERN_ERR "AudioTrix: Bad WSS DMA %d\n", dma1);
- return 0;
- }
-
- switch (dma2) {
- case -1:
- case 0:
- case 1:
- case 3:
- break;
- default:
- printk(KERN_ERR "AudioTrix: Bad capture DMA %d\n", dma2);
- return 0;
- }
-
- /*
- * Check if the IO port returns valid signature. The original MS Sound
- * system returns 0x04 while some cards (AudioTrix Pro for example)
- * return 0x00.
- */
- ports = request_region(hw_config->io_base + 4, 4, "ad1848");
- if (!ports) {
- printk(KERN_ERR "AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base);
- return 0;
- }
-
- if (!request_region(hw_config->io_base, 4, "MSS config")) {
- printk(KERN_ERR "AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base);
- release_region(hw_config->io_base + 4, 4);
- return 0;
- }
-
- if (!trix_set_wss_port(hw_config))
- goto fail;
-
- config = inb(hw_config->io_base + 3);
-
- if ((config & 0x3f) != 0x00)
- {
- MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x\n", hw_config->io_base));
- goto fail;
- }
-
- /*
- * Check that DMA0 is not in use with a 8 bit board.
- */
-
- if (dma1 == 0 && config & 0x80)
- {
- printk(KERN_ERR "AudioTrix: Can't use DMA0 with a 8 bit card slot\n");
- goto fail;
- }
- if (hw_config->irq > 9 && config & 0x80)
- {
- printk(KERN_ERR "AudioTrix: Can't use IRQ%d with a 8 bit card slot\n", hw_config->irq);
- goto fail;
- }
-
- ret = ad1848_detect(ports, NULL, hw_config->osp);
- if (!ret)
- goto fail;
-
- if (joystick==1)
- trix_write(0x15, 0x80);
-
- /*
- * Set the IRQ and DMA addresses.
- */
-
- outb((bits | 0x40), config_port);
-
- if (dma2 == -1 || dma2 == dma1)
- {
- bits |= dma_bits[dma1];
- dma2 = dma1;
- }
- else
- {
- unsigned char tmp;
-
- tmp = trix_read(0x13) & ~30;
- trix_write(0x13, tmp | 0x80 | (dma1 << 4));
-
- tmp = trix_read(0x14) & ~30;
- trix_write(0x14, tmp | 0x80 | (dma2 << 4));
- }
-
- outb((bits), config_port); /* Write IRQ+DMA setup */
-
- hw_config->slots[0] = ad1848_init("AudioTrix Pro", ports,
- hw_config->irq,
- dma1,
- dma2,
- 0,
- hw_config->osp,
- THIS_MODULE);
-
- if (num_mixers > old_num_mixers) /* Mixer got installed */
- {
- AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); /* Line in */
- AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD);
- AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* OPL4 */
- AD1848_REROUTE(SOUND_MIXER_SPEAKER, SOUND_MIXER_ALTPCM); /* SB */
- }
- return 1;
-
-fail:
- release_region(hw_config->io_base, 4);
- release_region(hw_config->io_base + 4, 4);
- return 0;
-}
-
-static int __init probe_trix_sb(struct address_info *hw_config)
-{
-
- int tmp;
- unsigned char conf;
- extern int sb_be_quiet;
- int old_quiet;
- static signed char irq_translate[] = {
- -1, -1, -1, 0, 1, 2, -1, 3
- };
-
- if (trix_boot_len == 0)
- return 0; /* No boot code -> no fun */
-
- if ((hw_config->io_base & 0xffffff8f) != 0x200)
- return 0;
-
- tmp = hw_config->irq;
- if (tmp > 7)
- return 0;
- if (irq_translate[tmp] == -1)
- return 0;
-
- tmp = hw_config->dma;
- if (tmp != 1 && tmp != 3)
- return 0;
-
- if (!request_region(hw_config->io_base, 16, "soundblaster")) {
- printk(KERN_ERR "AudioTrix: SB I/O port conflict (%x)\n", hw_config->io_base);
- return 0;
- }
-
- conf = 0x84; /* DMA and IRQ enable */
- conf |= hw_config->io_base & 0x70; /* I/O address bits */
- conf |= irq_translate[hw_config->irq];
- if (hw_config->dma == 3)
- conf |= 0x08;
- trix_write(0x1b, conf);
-
- download_boot(hw_config->io_base);
-
- hw_config->name = "AudioTrix SB";
- if (!sb_dsp_detect(hw_config, 0, 0, NULL)) {
- release_region(hw_config->io_base, 16);
- return 0;
- }
-
- hw_config->driver_use_1 = SB_NO_MIDI | SB_NO_MIXER | SB_NO_RECORDING;
-
- /* Prevent false alarms */
- old_quiet = sb_be_quiet;
- sb_be_quiet = 1;
-
- sb_dsp_init(hw_config, THIS_MODULE);
-
- sb_be_quiet = old_quiet;
- return 1;
-}
-
-static int __init probe_trix_mpu(struct address_info *hw_config)
-{
- unsigned char conf;
- static int irq_bits[] = {
- -1, -1, -1, 1, 2, 3, -1, 4, -1, 5
- };
-
- if (hw_config->irq > 9)
- {
- printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq);
- return 0;
- }
- if (irq_bits[hw_config->irq] == -1)
- {
- printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq);
- return 0;
- }
- switch (hw_config->io_base)
- {
- case 0x330:
- conf = 0x00;
- break;
- case 0x370:
- conf = 0x04;
- break;
- case 0x3b0:
- conf = 0x08;
- break;
- case 0x3f0:
- conf = 0x0c;
- break;
- default:
- return 0; /* Invalid port */
- }
-
- conf |= irq_bits[hw_config->irq] << 4;
- trix_write(0x19, (trix_read(0x19) & 0x83) | conf);
- hw_config->name = "AudioTrix Pro";
- return probe_uart401(hw_config, THIS_MODULE);
-}
-
-static void __exit unload_trix_wss(struct address_info *hw_config)
-{
- int dma2 = hw_config->dma2;
-
- if (dma2 == -1)
- dma2 = hw_config->dma;
-
- release_region(0x390, 2);
- release_region(hw_config->io_base, 4);
-
- ad1848_unload(hw_config->io_base + 4,
- hw_config->irq,
- hw_config->dma,
- dma2,
- 0);
- sound_unload_audiodev(hw_config->slots[0]);
-}
-
-static inline void __exit unload_trix_mpu(struct address_info *hw_config)
-{
- unload_uart401(hw_config);
-}
-
-static inline void __exit unload_trix_sb(struct address_info *hw_config)
-{
- sb_dsp_unload(hw_config, mpu);
-}
-
-static struct address_info cfg;
-static struct address_info cfg2;
-static struct address_info cfg_mpu;
-
-static int sb;
-static int fw_load;
-
-static int __initdata io = -1;
-static int __initdata irq = -1;
-static int __initdata dma = -1;
-static int __initdata dma2 = -1; /* Set this for modules that need it */
-static int __initdata sb_io = -1;
-static int __initdata sb_dma = -1;
-static int __initdata sb_irq = -1;
-static int __initdata mpu_io = -1;
-static int __initdata mpu_irq = -1;
-
-module_param(io, int, 0);
-module_param(irq, int, 0);
-module_param(dma, int, 0);
-module_param(dma2, int, 0);
-module_param(sb_io, int, 0);
-module_param(sb_dma, int, 0);
-module_param(sb_irq, int, 0);
-module_param(mpu_io, int, 0);
-module_param(mpu_irq, int, 0);
-module_param(joystick, bool, 0);
-
-static int __init init_trix(void)
-{
- printk(KERN_INFO "MediaTrix audio driver Copyright (C) by Hannu Savolainen 1993-1996\n");
-
- cfg.io_base = io;
- cfg.irq = irq;
- cfg.dma = dma;
- cfg.dma2 = dma2;
-
- cfg2.io_base = sb_io;
- cfg2.irq = sb_irq;
- cfg2.dma = sb_dma;
-
- cfg_mpu.io_base = mpu_io;
- cfg_mpu.irq = mpu_irq;
-
- if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) {
- printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n");
- return -EINVAL;
- }
-
- if (cfg2.io_base != -1 && (cfg2.irq == -1 || cfg2.dma == -1)) {
- printk(KERN_INFO "CONFIG_SB_IRQ and CONFIG_SB_DMA must be specified if SB_IO is set.\n");
- return -EINVAL;
- }
- if (cfg_mpu.io_base != -1 && cfg_mpu.irq == -1) {
- printk(KERN_INFO "CONFIG_MPU_IRQ must be specified if MPU_IO is set.\n");
- return -EINVAL;
- }
- if (!trix_boot)
- {
- fw_load = 1;
- trix_boot_len = mod_firmware_load("/etc/sound/trxpro.bin",
- (char **) &trix_boot);
- }
-
- if (!request_region(0x390, 2, "AudioTrix")) {
- printk(KERN_ERR "AudioTrix: Config port I/O conflict\n");
- return -ENODEV;
- }
-
- if (!init_trix_wss(&cfg)) {
- release_region(0x390, 2);
- return -ENODEV;
- }
-
- /*
- * We must attach in the right order to get the firmware
- * loaded up in time.
- */
-
- if (cfg2.io_base != -1) {
- sb = probe_trix_sb(&cfg2);
- }
-
- if (cfg_mpu.io_base != -1)
- mpu = probe_trix_mpu(&cfg_mpu);
-
- return 0;
-}
-
-static void __exit cleanup_trix(void)
-{
- if (fw_load && trix_boot)
- vfree(trix_boot);
- if (sb)
- unload_trix_sb(&cfg2);
- if (mpu)
- unload_trix_mpu(&cfg_mpu);
- unload_trix_wss(&cfg);
-}
-
-module_init(init_trix);
-module_exit(cleanup_trix);
-
-#ifndef MODULE
-static int __init setup_trix (char *str)
-{
- /* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, mpu_io, mpu_irq */
- int ints[9];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
- dma = ints[3];
- dma2 = ints[4];
- sb_io = ints[5];
- sb_irq = ints[6];
- sb_dma = ints[6];
- mpu_io = ints[7];
- mpu_irq = ints[8];
-
- return 1;
-}
-
-__setup("trix=", setup_trix);
-#endif
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/tuning.h b/sound/oss/tuning.h
deleted file mode 100644
index a73e3dd39f9a..000000000000
--- a/sound/oss/tuning.h
+++ /dev/null
@@ -1,23 +0,0 @@
-static unsigned short semitone_tuning[24] =
-{
-/* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983,
-/* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784,
-/* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755
-};
-
-static unsigned short cent_tuning[100] =
-{
-/* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041,
-/* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087,
-/* 16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134,
-/* 24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181,
-/* 32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228,
-/* 40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275,
-/* 48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323,
-/* 56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371,
-/* 64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419,
-/* 72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467,
-/* 80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515,
-/* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564,
-/* 96 */ 10570, 10576, 10582, 10589
-};
diff --git a/sound/oss/uart401.c b/sound/oss/uart401.c
deleted file mode 100644
index 8e514a676a0d..000000000000
--- a/sound/oss/uart401.c
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- * sound/oss/uart401.c
- *
- * MPU-401 UART driver (formerly uart401_midi.c)
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- * Changes:
- * Alan Cox Reformatted, removed sound_mem usage, use normal Linux
- * interrupt allocation. Protect against bogus unload
- * Fixed to allow IRQ > 15
- * Christoph Hellwig Adapted to module_init/module_exit
- * Arnaldo C. de Melo got rid of check_region
- *
- * Status:
- * Untested
- */
-
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include "sound_config.h"
-
-#include "mpu401.h"
-
-typedef struct uart401_devc
-{
- int base;
- int irq;
- int *osp;
- void (*midi_input_intr) (int dev, unsigned char data);
- int opened, disabled;
- volatile unsigned char input_byte;
- int my_dev;
- int share_irq;
- spinlock_t lock;
-}
-uart401_devc;
-
-#define DATAPORT (devc->base)
-#define COMDPORT (devc->base+1)
-#define STATPORT (devc->base+1)
-
-static int uart401_status(uart401_devc * devc)
-{
- return inb(STATPORT);
-}
-
-#define input_avail(devc) (!(uart401_status(devc)&INPUT_AVAIL))
-#define output_ready(devc) (!(uart401_status(devc)&OUTPUT_READY))
-
-static void uart401_cmd(uart401_devc * devc, unsigned char cmd)
-{
- outb((cmd), COMDPORT);
-}
-
-static int uart401_read(uart401_devc * devc)
-{
- return inb(DATAPORT);
-}
-
-static void uart401_write(uart401_devc * devc, unsigned char byte)
-{
- outb((byte), DATAPORT);
-}
-
-#define OUTPUT_READY 0x40
-#define INPUT_AVAIL 0x80
-#define MPU_ACK 0xFE
-#define MPU_RESET 0xFF
-#define UART_MODE_ON 0x3F
-
-static int reset_uart401(uart401_devc * devc);
-static void enter_uart_mode(uart401_devc * devc);
-
-static void uart401_input_loop(uart401_devc * devc)
-{
- int work_limit=30000;
-
- while (input_avail(devc) && --work_limit)
- {
- unsigned char c = uart401_read(devc);
-
- if (c == MPU_ACK)
- devc->input_byte = c;
- else if (devc->opened & OPEN_READ && devc->midi_input_intr)
- devc->midi_input_intr(devc->my_dev, c);
- }
- if(work_limit==0)
- printk(KERN_WARNING "Too much work in interrupt on uart401 (0x%X). UART jabbering ??\n", devc->base);
-}
-
-irqreturn_t uart401intr(int irq, void *dev_id)
-{
- uart401_devc *devc = dev_id;
-
- if (devc == NULL)
- {
- printk(KERN_ERR "uart401: bad devc\n");
- return IRQ_NONE;
- }
-
- if (input_avail(devc))
- uart401_input_loop(devc);
- return IRQ_HANDLED;
-}
-
-static int
-uart401_open(int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
-)
-{
- uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
-
- if (devc->opened)
- return -EBUSY;
-
- /* Flush the UART */
-
- while (input_avail(devc))
- uart401_read(devc);
-
- devc->midi_input_intr = input;
- devc->opened = mode;
- enter_uart_mode(devc);
- devc->disabled = 0;
-
- return 0;
-}
-
-static void uart401_close(int dev)
-{
- uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
-
- reset_uart401(devc);
- devc->opened = 0;
-}
-
-static int uart401_out(int dev, unsigned char midi_byte)
-{
- int timeout;
- unsigned long flags;
- uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
-
- if (devc->disabled)
- return 1;
- /*
- * Test for input since pending input seems to block the output.
- */
-
- spin_lock_irqsave(&devc->lock,flags);
- if (input_avail(devc))
- uart401_input_loop(devc);
-
- spin_unlock_irqrestore(&devc->lock,flags);
-
- /*
- * Sometimes it takes about 13000 loops before the output becomes ready
- * (After reset). Normally it takes just about 10 loops.
- */
-
- for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
-
- if (!output_ready(devc))
- {
- printk(KERN_WARNING "uart401: Timeout - Device not responding\n");
- devc->disabled = 1;
- reset_uart401(devc);
- enter_uart_mode(devc);
- return 1;
- }
- uart401_write(devc, midi_byte);
- return 1;
-}
-
-static inline int uart401_start_read(int dev)
-{
- return 0;
-}
-
-static inline int uart401_end_read(int dev)
-{
- return 0;
-}
-
-static inline void uart401_kick(int dev)
-{
-}
-
-static inline int uart401_buffer_status(int dev)
-{
- return 0;
-}
-
-#define MIDI_SYNTH_NAME "MPU-401 UART"
-#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
-#include "midi_synth.h"
-
-static const struct midi_operations uart401_operations =
-{
- .owner = THIS_MODULE,
- .info = {"MPU-401 (UART) MIDI", 0, 0, SNDCARD_MPU401},
- .converter = &std_midi_synth,
- .in_info = {0},
- .open = uart401_open,
- .close = uart401_close,
- .outputc = uart401_out,
- .start_read = uart401_start_read,
- .end_read = uart401_end_read,
- .kick = uart401_kick,
- .buffer_status = uart401_buffer_status,
-};
-
-static void enter_uart_mode(uart401_devc * devc)
-{
- int ok, timeout;
- unsigned long flags;
-
- spin_lock_irqsave(&devc->lock,flags);
- for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
-
- devc->input_byte = 0;
- uart401_cmd(devc, UART_MODE_ON);
-
- ok = 0;
- for (timeout = 50000; timeout > 0 && !ok; timeout--)
- if (devc->input_byte == MPU_ACK)
- ok = 1;
- else if (input_avail(devc))
- if (uart401_read(devc) == MPU_ACK)
- ok = 1;
-
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static int reset_uart401(uart401_devc * devc)
-{
- int ok, timeout, n;
-
- /*
- * Send the RESET command. Try again if no success at the first time.
- */
-
- ok = 0;
-
- for (n = 0; n < 2 && !ok; n++)
- {
- for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
- devc->input_byte = 0;
- uart401_cmd(devc, MPU_RESET);
-
- /*
- * Wait at least 25 msec. This method is not accurate so let's make the
- * loop bit longer. Cannot sleep since this is called during boot.
- */
-
- for (timeout = 50000; timeout > 0 && !ok; timeout--)
- {
- if (devc->input_byte == MPU_ACK) /* Interrupt */
- ok = 1;
- else if (input_avail(devc))
- {
- if (uart401_read(devc) == MPU_ACK)
- ok = 1;
- }
- }
- }
-
-
- if (ok)
- {
- DEB(printk("Reset UART401 OK\n"));
- }
- else
- DDB(printk("Reset UART401 failed - No hardware detected.\n"));
-
- if (ok)
- uart401_input_loop(devc); /*
- * Flush input before enabling interrupts
- */
-
- return ok;
-}
-
-int probe_uart401(struct address_info *hw_config, struct module *owner)
-{
- uart401_devc *devc;
- char *name = "MPU-401 (UART) MIDI";
- int ok = 0;
- unsigned long flags;
-
- DDB(printk("Entered probe_uart401()\n"));
-
- /* Default to "not found" */
- hw_config->slots[4] = -1;
-
- if (!request_region(hw_config->io_base, 4, "MPU-401 UART")) {
- printk(KERN_INFO "uart401: could not request_region(%d, 4)\n", hw_config->io_base);
- return 0;
- }
-
- devc = kmalloc(sizeof(uart401_devc), GFP_KERNEL);
- if (!devc) {
- printk(KERN_WARNING "uart401: Can't allocate memory\n");
- goto cleanup_region;
- }
-
- devc->base = hw_config->io_base;
- devc->irq = hw_config->irq;
- devc->osp = hw_config->osp;
- devc->midi_input_intr = NULL;
- devc->opened = 0;
- devc->input_byte = 0;
- devc->my_dev = 0;
- devc->share_irq = 0;
- spin_lock_init(&devc->lock);
-
- spin_lock_irqsave(&devc->lock,flags);
- ok = reset_uart401(devc);
- spin_unlock_irqrestore(&devc->lock,flags);
-
- if (!ok)
- goto cleanup_devc;
-
- if (hw_config->name)
- name = hw_config->name;
-
- if (devc->irq < 0) {
- devc->share_irq = 1;
- devc->irq *= -1;
- } else
- devc->share_irq = 0;
-
- if (!devc->share_irq)
- if (request_irq(devc->irq, uart401intr, 0, "MPU-401 UART", devc) < 0) {
- printk(KERN_WARNING "uart401: Failed to allocate IRQ%d\n", devc->irq);
- devc->share_irq = 1;
- }
- devc->my_dev = sound_alloc_mididev();
- enter_uart_mode(devc);
-
- if (devc->my_dev == -1) {
- printk(KERN_INFO "uart401: Too many midi devices detected\n");
- goto cleanup_irq;
- }
- conf_printf(name, hw_config);
- midi_devs[devc->my_dev] = kmalloc(sizeof(struct midi_operations), GFP_KERNEL);
- if (!midi_devs[devc->my_dev]) {
- printk(KERN_ERR "uart401: Failed to allocate memory\n");
- goto cleanup_unload_mididev;
- }
- memcpy(midi_devs[devc->my_dev], &uart401_operations, sizeof(struct midi_operations));
-
- if (owner)
- midi_devs[devc->my_dev]->owner = owner;
-
- midi_devs[devc->my_dev]->devc = devc;
- midi_devs[devc->my_dev]->converter = kmalloc(sizeof(struct synth_operations), GFP_KERNEL);
- if (!midi_devs[devc->my_dev]->converter) {
- printk(KERN_WARNING "uart401: Failed to allocate memory\n");
- goto cleanup_midi_devs;
- }
- memcpy(midi_devs[devc->my_dev]->converter, &std_midi_synth, sizeof(struct synth_operations));
- strcpy(midi_devs[devc->my_dev]->info.name, name);
- midi_devs[devc->my_dev]->converter->id = "UART401";
- midi_devs[devc->my_dev]->converter->midi_dev = devc->my_dev;
-
- if (owner)
- midi_devs[devc->my_dev]->converter->owner = owner;
-
- hw_config->slots[4] = devc->my_dev;
- sequencer_init();
- devc->opened = 0;
- return 1;
-cleanup_midi_devs:
- kfree(midi_devs[devc->my_dev]);
-cleanup_unload_mididev:
- sound_unload_mididev(devc->my_dev);
-cleanup_irq:
- if (!devc->share_irq)
- free_irq(devc->irq, devc);
-cleanup_devc:
- kfree(devc);
-cleanup_region:
- release_region(hw_config->io_base, 4);
- return 0;
-}
-
-void unload_uart401(struct address_info *hw_config)
-{
- uart401_devc *devc;
- int n=hw_config->slots[4];
-
- /* Not set up */
- if(n==-1 || midi_devs[n]==NULL)
- return;
-
- /* Not allocated (erm ??) */
-
- devc = midi_devs[hw_config->slots[4]]->devc;
- if (devc == NULL)
- return;
-
- reset_uart401(devc);
- release_region(hw_config->io_base, 4);
-
- if (!devc->share_irq)
- free_irq(devc->irq, devc);
- if (devc)
- {
- kfree(midi_devs[devc->my_dev]->converter);
- kfree(midi_devs[devc->my_dev]);
- kfree(devc);
- devc = NULL;
- }
- /* This kills midi_devs[x] */
- sound_unload_mididev(hw_config->slots[4]);
-}
-
-EXPORT_SYMBOL(probe_uart401);
-EXPORT_SYMBOL(unload_uart401);
-EXPORT_SYMBOL(uart401intr);
-
-static struct address_info cfg_mpu;
-
-static int io = -1;
-static int irq = -1;
-
-module_param(io, int, 0444);
-module_param(irq, int, 0444);
-
-
-static int __init init_uart401(void)
-{
- cfg_mpu.irq = irq;
- cfg_mpu.io_base = io;
-
- /* Can be loaded either for module use or to provide functions
- to others */
- if (cfg_mpu.io_base != -1 && cfg_mpu.irq != -1) {
- printk(KERN_INFO "MPU-401 UART driver Copyright (C) Hannu Savolainen 1993-1997");
- if (!probe_uart401(&cfg_mpu, THIS_MODULE))
- return -ENODEV;
- }
-
- return 0;
-}
-
-static void __exit cleanup_uart401(void)
-{
- if (cfg_mpu.io_base != -1 && cfg_mpu.irq != -1)
- unload_uart401(&cfg_mpu);
-}
-
-module_init(init_uart401);
-module_exit(cleanup_uart401);
-
-#ifndef MODULE
-static int __init setup_uart401(char *str)
-{
- /* io, irq */
- int ints[3];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
-
- return 1;
-}
-
-__setup("uart401=", setup_uart401);
-#endif
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/uart6850.c b/sound/oss/uart6850.c
deleted file mode 100644
index f3f914aa92ee..000000000000
--- a/sound/oss/uart6850.c
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * sound/oss/uart6850.c
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- * Extended by Alan Cox for Red Hat Software. Now a loadable MIDI driver.
- * 28/4/97 - (C) Copyright Alan Cox. Released under the GPL version 2.
- *
- * Alan Cox: Updated for new modular code. Removed snd_* irq handling. Now
- * uses native linux resources
- * Christoph Hellwig: Adapted to module_init/module_exit
- * Jeff Garzik: Made it work again, in theory
- * FIXME: If the request_irq() succeeds, the probe succeeds. Ug.
- *
- * Status: Testing required (no shit -jgarzik)
- *
- *
- */
-
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-/* Mon Nov 22 22:38:35 MET 1993 marco@driq.home.usn.nl:
- * added 6850 support, used with COVOX SoundMaster II and custom cards.
- */
-
-#include "sound_config.h"
-
-static int uart6850_base = 0x330;
-
-static int *uart6850_osp;
-
-#define DATAPORT (uart6850_base)
-#define COMDPORT (uart6850_base+1)
-#define STATPORT (uart6850_base+1)
-
-static int uart6850_status(void)
-{
- return inb(STATPORT);
-}
-
-#define input_avail() (uart6850_status()&INPUT_AVAIL)
-#define output_ready() (uart6850_status()&OUTPUT_READY)
-
-static void uart6850_cmd(unsigned char cmd)
-{
- outb(cmd, COMDPORT);
-}
-
-static int uart6850_read(void)
-{
- return inb(DATAPORT);
-}
-
-static void uart6850_write(unsigned char byte)
-{
- outb(byte, DATAPORT);
-}
-
-#define OUTPUT_READY 0x02 /* Mask for data ready Bit */
-#define INPUT_AVAIL 0x01 /* Mask for Data Send Ready Bit */
-
-#define UART_RESET 0x95
-#define UART_MODE_ON 0x03
-
-static int uart6850_opened;
-static int uart6850_irq;
-static int uart6850_detected;
-static int my_dev;
-static DEFINE_SPINLOCK(lock);
-
-static void (*midi_input_intr) (int dev, unsigned char data);
-static void poll_uart6850(unsigned long dummy);
-
-
-static DEFINE_TIMER(uart6850_timer, poll_uart6850, 0, 0);
-
-static void uart6850_input_loop(void)
-{
- int count = 10;
-
- while (count)
- {
- /*
- * Not timed out
- */
- if (input_avail())
- {
- unsigned char c = uart6850_read();
- count = 100;
- if (uart6850_opened & OPEN_READ)
- midi_input_intr(my_dev, c);
- }
- else
- {
- while (!input_avail() && count)
- count--;
- }
- }
-}
-
-static irqreturn_t m6850intr(int irq, void *dev_id)
-{
- if (input_avail())
- uart6850_input_loop();
- return IRQ_HANDLED;
-}
-
-/*
- * It looks like there is no input interrupts in the UART mode. Let's try
- * polling.
- */
-
-static void poll_uart6850(unsigned long dummy)
-{
- unsigned long flags;
-
- if (!(uart6850_opened & OPEN_READ))
- return; /* Device has been closed */
-
- spin_lock_irqsave(&lock,flags);
- if (input_avail())
- uart6850_input_loop();
-
- uart6850_timer.expires = 1 + jiffies;
- add_timer(&uart6850_timer);
-
- /*
- * Come back later
- */
-
- spin_unlock_irqrestore(&lock,flags);
-}
-
-static int uart6850_open(int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
-)
-{
- if (uart6850_opened)
- {
-/* printk("Midi6850: Midi busy\n");*/
- return -EBUSY;
- };
-
- uart6850_cmd(UART_RESET);
- uart6850_input_loop();
- midi_input_intr = input;
- uart6850_opened = mode;
- poll_uart6850(0); /*
- * Enable input polling
- */
-
- return 0;
-}
-
-static void uart6850_close(int dev)
-{
- uart6850_cmd(UART_MODE_ON);
- del_timer(&uart6850_timer);
- uart6850_opened = 0;
-}
-
-static int uart6850_out(int dev, unsigned char midi_byte)
-{
- int timeout;
- unsigned long flags;
-
- /*
- * Test for input since pending input seems to block the output.
- */
-
- spin_lock_irqsave(&lock,flags);
-
- if (input_avail())
- uart6850_input_loop();
-
- spin_unlock_irqrestore(&lock,flags);
-
- /*
- * Sometimes it takes about 13000 loops before the output becomes ready
- * (After reset). Normally it takes just about 10 loops.
- */
-
- for (timeout = 30000; timeout > 0 && !output_ready(); timeout--); /*
- * Wait
- */
- if (!output_ready())
- {
- printk(KERN_WARNING "Midi6850: Timeout\n");
- return 0;
- }
- uart6850_write(midi_byte);
- return 1;
-}
-
-static inline int uart6850_command(int dev, unsigned char *midi_byte)
-{
- return 1;
-}
-
-static inline int uart6850_start_read(int dev)
-{
- return 0;
-}
-
-static inline int uart6850_end_read(int dev)
-{
- return 0;
-}
-
-static inline void uart6850_kick(int dev)
-{
-}
-
-static inline int uart6850_buffer_status(int dev)
-{
- return 0; /*
- * No data in buffers
- */
-}
-
-#define MIDI_SYNTH_NAME "6850 UART Midi"
-#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
-#include "midi_synth.h"
-
-static struct midi_operations uart6850_operations =
-{
- .owner = THIS_MODULE,
- .info = {"6850 UART", 0, 0, SNDCARD_UART6850},
- .converter = &std_midi_synth,
- .in_info = {0},
- .open = uart6850_open,
- .close = uart6850_close,
- .outputc = uart6850_out,
- .start_read = uart6850_start_read,
- .end_read = uart6850_end_read,
- .kick = uart6850_kick,
- .command = uart6850_command,
- .buffer_status = uart6850_buffer_status
-};
-
-
-static void __init attach_uart6850(struct address_info *hw_config)
-{
- int ok, timeout;
- unsigned long flags;
-
- if (!uart6850_detected)
- return;
-
- if ((my_dev = sound_alloc_mididev()) == -1)
- {
- printk(KERN_INFO "uart6850: Too many midi devices detected\n");
- return;
- }
- uart6850_base = hw_config->io_base;
- uart6850_osp = hw_config->osp;
- uart6850_irq = hw_config->irq;
-
- spin_lock_irqsave(&lock,flags);
-
- for (timeout = 30000; timeout > 0 && !output_ready(); timeout--); /*
- * Wait
- */
- uart6850_cmd(UART_MODE_ON);
- ok = 1;
- spin_unlock_irqrestore(&lock,flags);
-
- conf_printf("6850 Midi Interface", hw_config);
-
- std_midi_synth.midi_dev = my_dev;
- hw_config->slots[4] = my_dev;
- midi_devs[my_dev] = &uart6850_operations;
- sequencer_init();
-}
-
-static inline int reset_uart6850(void)
-{
- uart6850_read();
- return 1; /*
- * OK
- */
-}
-
-static int __init probe_uart6850(struct address_info *hw_config)
-{
- int ok;
-
- uart6850_osp = hw_config->osp;
- uart6850_base = hw_config->io_base;
- uart6850_irq = hw_config->irq;
-
- if (request_irq(uart6850_irq, m6850intr, 0, "MIDI6850", NULL) < 0)
- return 0;
-
- ok = reset_uart6850();
- uart6850_detected = ok;
- return ok;
-}
-
-static void __exit unload_uart6850(struct address_info *hw_config)
-{
- free_irq(hw_config->irq, NULL);
- sound_unload_mididev(hw_config->slots[4]);
-}
-
-static struct address_info cfg_mpu;
-
-static int __initdata io = -1;
-static int __initdata irq = -1;
-
-module_param(io, int, 0);
-module_param(irq, int, 0);
-
-static int __init init_uart6850(void)
-{
- cfg_mpu.io_base = io;
- cfg_mpu.irq = irq;
-
- if (cfg_mpu.io_base == -1 || cfg_mpu.irq == -1) {
- printk(KERN_INFO "uart6850: irq and io must be set.\n");
- return -EINVAL;
- }
-
- if (probe_uart6850(&cfg_mpu))
- return -ENODEV;
- attach_uart6850(&cfg_mpu);
-
- return 0;
-}
-
-static void __exit cleanup_uart6850(void)
-{
- unload_uart6850(&cfg_mpu);
-}
-
-module_init(init_uart6850);
-module_exit(cleanup_uart6850);
-
-#ifndef MODULE
-static int __init setup_uart6850(char *str)
-{
- /* io, irq */
- int ints[3];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
-
- return 1;
-}
-__setup("uart6850=", setup_uart6850);
-#endif
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/ulaw.h b/sound/oss/ulaw.h
deleted file mode 100644
index 0ff8c0a3bda0..000000000000
--- a/sound/oss/ulaw.h
+++ /dev/null
@@ -1,69 +0,0 @@
-static unsigned char ulaw_dsp[] = {
- 3, 7, 11, 15, 19, 23, 27, 31,
- 35, 39, 43, 47, 51, 55, 59, 63,
- 66, 68, 70, 72, 74, 76, 78, 80,
- 82, 84, 86, 88, 90, 92, 94, 96,
- 98, 99, 100, 101, 102, 103, 104, 105,
- 106, 107, 108, 109, 110, 111, 112, 113,
- 113, 114, 114, 115, 115, 116, 116, 117,
- 117, 118, 118, 119, 119, 120, 120, 121,
- 121, 121, 122, 122, 122, 122, 123, 123,
- 123, 123, 124, 124, 124, 124, 125, 125,
- 125, 125, 125, 125, 126, 126, 126, 126,
- 126, 126, 126, 126, 127, 127, 127, 127,
- 127, 127, 127, 127, 127, 127, 127, 127,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 253, 249, 245, 241, 237, 233, 229, 225,
- 221, 217, 213, 209, 205, 201, 197, 193,
- 190, 188, 186, 184, 182, 180, 178, 176,
- 174, 172, 170, 168, 166, 164, 162, 160,
- 158, 157, 156, 155, 154, 153, 152, 151,
- 150, 149, 148, 147, 146, 145, 144, 143,
- 143, 142, 142, 141, 141, 140, 140, 139,
- 139, 138, 138, 137, 137, 136, 136, 135,
- 135, 135, 134, 134, 134, 134, 133, 133,
- 133, 133, 132, 132, 132, 132, 131, 131,
- 131, 131, 131, 131, 130, 130, 130, 130,
- 130, 130, 130, 130, 129, 129, 129, 129,
- 129, 129, 129, 129, 129, 129, 129, 129,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
- 128, 128, 128, 128, 128, 128, 128, 128,
-};
-
-static unsigned char dsp_ulaw[] = {
- 0, 0, 0, 0, 0, 1, 1, 1,
- 1, 2, 2, 2, 2, 3, 3, 3,
- 3, 4, 4, 4, 4, 5, 5, 5,
- 5, 6, 6, 6, 6, 7, 7, 7,
- 7, 8, 8, 8, 8, 9, 9, 9,
- 9, 10, 10, 10, 10, 11, 11, 11,
- 11, 12, 12, 12, 12, 13, 13, 13,
- 13, 14, 14, 14, 14, 15, 15, 15,
- 15, 16, 16, 17, 17, 18, 18, 19,
- 19, 20, 20, 21, 21, 22, 22, 23,
- 23, 24, 24, 25, 25, 26, 26, 27,
- 27, 28, 28, 29, 29, 30, 30, 31,
- 31, 32, 33, 34, 35, 36, 37, 38,
- 39, 40, 41, 42, 43, 44, 45, 46,
- 47, 49, 51, 53, 55, 57, 59, 61,
- 63, 66, 70, 74, 78, 84, 92, 104,
- 254, 231, 219, 211, 205, 201, 197, 193,
- 190, 188, 186, 184, 182, 180, 178, 176,
- 175, 174, 173, 172, 171, 170, 169, 168,
- 167, 166, 165, 164, 163, 162, 161, 160,
- 159, 159, 158, 158, 157, 157, 156, 156,
- 155, 155, 154, 154, 153, 153, 152, 152,
- 151, 151, 150, 150, 149, 149, 148, 148,
- 147, 147, 146, 146, 145, 145, 144, 144,
- 143, 143, 143, 143, 142, 142, 142, 142,
- 141, 141, 141, 141, 140, 140, 140, 140,
- 139, 139, 139, 139, 138, 138, 138, 138,
- 137, 137, 137, 137, 136, 136, 136, 136,
- 135, 135, 135, 135, 134, 134, 134, 134,
- 133, 133, 133, 133, 132, 132, 132, 132,
- 131, 131, 131, 131, 130, 130, 130, 130,
- 129, 129, 129, 129, 128, 128, 128, 128,
-};
diff --git a/sound/oss/v_midi.c b/sound/oss/v_midi.c
deleted file mode 100644
index f0b4151d9b17..000000000000
--- a/sound/oss/v_midi.c
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * sound/oss/v_midi.c
- *
- * The low level driver for the Sound Blaster DS chips.
- *
- *
- * Copyright (C) by Hannu Savolainen 1993-1996
- *
- * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- * ??
- *
- * Changes
- * Alan Cox Modularisation, changed memory allocations
- * Christoph Hellwig Adapted to module_init/module_exit
- *
- * Status
- * Untested
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include "sound_config.h"
-
-#include "v_midi.h"
-
-static vmidi_devc *v_devc[2] = { NULL, NULL};
-static int midi1,midi2;
-static void *midi_mem = NULL;
-
-/*
- * The DSP channel can be used either for input or output. Variable
- * 'sb_irq_mode' will be set when the program calls read or write first time
- * after open. Current version doesn't support mode changes without closing
- * and reopening the device. Support for this feature may be implemented in a
- * future version of this driver.
- */
-
-
-static int v_midi_open (int dev, int mode,
- void (*input) (int dev, unsigned char data),
- void (*output) (int dev)
-)
-{
- vmidi_devc *devc = midi_devs[dev]->devc;
- unsigned long flags;
-
- if (devc == NULL)
- return -(ENXIO);
-
- spin_lock_irqsave(&devc->lock,flags);
- if (devc->opened)
- {
- spin_unlock_irqrestore(&devc->lock,flags);
- return -(EBUSY);
- }
- devc->opened = 1;
- spin_unlock_irqrestore(&devc->lock,flags);
-
- devc->intr_active = 1;
-
- if (mode & OPEN_READ)
- {
- devc->input_opened = 1;
- devc->midi_input_intr = input;
- }
-
- return 0;
-}
-
-static void v_midi_close (int dev)
-{
- vmidi_devc *devc = midi_devs[dev]->devc;
- unsigned long flags;
-
- if (devc == NULL)
- return;
-
- spin_lock_irqsave(&devc->lock,flags);
- devc->intr_active = 0;
- devc->input_opened = 0;
- devc->opened = 0;
- spin_unlock_irqrestore(&devc->lock,flags);
-}
-
-static int v_midi_out (int dev, unsigned char midi_byte)
-{
- vmidi_devc *devc = midi_devs[dev]->devc;
- vmidi_devc *pdevc;
-
- if (devc == NULL)
- return -ENXIO;
-
- pdevc = midi_devs[devc->pair_mididev]->devc;
- if (pdevc->input_opened > 0){
- if (MIDIbuf_avail(pdevc->my_mididev) > 500)
- return 0;
- pdevc->midi_input_intr (pdevc->my_mididev, midi_byte);
- }
- return 1;
-}
-
-static inline int v_midi_start_read (int dev)
-{
- return 0;
-}
-
-static int v_midi_end_read (int dev)
-{
- vmidi_devc *devc = midi_devs[dev]->devc;
- if (devc == NULL)
- return -ENXIO;
-
- devc->intr_active = 0;
- return 0;
-}
-
-/* why -EPERM and not -EINVAL?? */
-
-static inline int v_midi_ioctl (int dev, unsigned cmd, void __user *arg)
-{
- return -EPERM;
-}
-
-
-#define MIDI_SYNTH_NAME "Loopback MIDI"
-#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
-
-#include "midi_synth.h"
-
-static struct midi_operations v_midi_operations =
-{
- .owner = THIS_MODULE,
- .info = {"Loopback MIDI Port 1", 0, 0, SNDCARD_VMIDI},
- .converter = &std_midi_synth,
- .in_info = {0},
- .open = v_midi_open,
- .close = v_midi_close,
- .ioctl = v_midi_ioctl,
- .outputc = v_midi_out,
- .start_read = v_midi_start_read,
- .end_read = v_midi_end_read,
-};
-
-static struct midi_operations v_midi_operations2 =
-{
- .owner = THIS_MODULE,
- .info = {"Loopback MIDI Port 2", 0, 0, SNDCARD_VMIDI},
- .converter = &std_midi_synth,
- .in_info = {0},
- .open = v_midi_open,
- .close = v_midi_close,
- .ioctl = v_midi_ioctl,
- .outputc = v_midi_out,
- .start_read = v_midi_start_read,
- .end_read = v_midi_end_read,
-};
-
-/*
- * We kmalloc just one of these - it makes life simpler and the code
- * cleaner and the memory handling far more efficient
- */
-
-struct vmidi_memory
-{
- /* Must be first */
- struct midi_operations m_ops[2];
- struct synth_operations s_ops[2];
- struct vmidi_devc v_ops[2];
-};
-
-static void __init attach_v_midi (struct address_info *hw_config)
-{
- struct vmidi_memory *m;
- /* printk("Attaching v_midi device.....\n"); */
-
- midi1 = sound_alloc_mididev();
- if (midi1 == -1)
- {
- printk(KERN_ERR "v_midi: Too many midi devices detected\n");
- return;
- }
-
- m = kmalloc(sizeof(struct vmidi_memory), GFP_KERNEL);
- if (m == NULL)
- {
- printk(KERN_WARNING "Loopback MIDI: Failed to allocate memory\n");
- sound_unload_mididev(midi1);
- return;
- }
-
- midi_mem = m;
-
- midi_devs[midi1] = &m->m_ops[0];
-
-
- midi2 = sound_alloc_mididev();
- if (midi2 == -1)
- {
- printk (KERN_ERR "v_midi: Too many midi devices detected\n");
- kfree(m);
- sound_unload_mididev(midi1);
- return;
- }
-
- midi_devs[midi2] = &m->m_ops[1];
-
- /* printk("VMIDI1: %d VMIDI2: %d\n",midi1,midi2); */
-
- /* for MIDI-1 */
- v_devc[0] = &m->v_ops[0];
- memcpy ((char *) midi_devs[midi1], (char *) &v_midi_operations,
- sizeof (struct midi_operations));
-
- v_devc[0]->my_mididev = midi1;
- v_devc[0]->pair_mididev = midi2;
- v_devc[0]->opened = v_devc[0]->input_opened = 0;
- v_devc[0]->intr_active = 0;
- v_devc[0]->midi_input_intr = NULL;
- spin_lock_init(&v_devc[0]->lock);
-
- midi_devs[midi1]->devc = v_devc[0];
-
- midi_devs[midi1]->converter = &m->s_ops[0];
- std_midi_synth.midi_dev = midi1;
- memcpy ((char *) midi_devs[midi1]->converter, (char *) &std_midi_synth,
- sizeof (struct synth_operations));
- midi_devs[midi1]->converter->id = "V_MIDI 1";
-
- /* for MIDI-2 */
- v_devc[1] = &m->v_ops[1];
-
- memcpy ((char *) midi_devs[midi2], (char *) &v_midi_operations2,
- sizeof (struct midi_operations));
-
- v_devc[1]->my_mididev = midi2;
- v_devc[1]->pair_mididev = midi1;
- v_devc[1]->opened = v_devc[1]->input_opened = 0;
- v_devc[1]->intr_active = 0;
- v_devc[1]->midi_input_intr = NULL;
- spin_lock_init(&v_devc[1]->lock);
-
- midi_devs[midi2]->devc = v_devc[1];
- midi_devs[midi2]->converter = &m->s_ops[1];
-
- std_midi_synth.midi_dev = midi2;
- memcpy ((char *) midi_devs[midi2]->converter, (char *) &std_midi_synth,
- sizeof (struct synth_operations));
- midi_devs[midi2]->converter->id = "V_MIDI 2";
-
- sequencer_init();
- /* printk("Attached v_midi device\n"); */
-}
-
-static inline int __init probe_v_midi(struct address_info *hw_config)
-{
- return(1); /* always OK */
-}
-
-
-static void __exit unload_v_midi(struct address_info *hw_config)
-{
- sound_unload_mididev(midi1);
- sound_unload_mididev(midi2);
- kfree(midi_mem);
-}
-
-static struct address_info cfg; /* dummy */
-
-static int __init init_vmidi(void)
-{
- printk("MIDI Loopback device driver\n");
- if (!probe_v_midi(&cfg))
- return -ENODEV;
- attach_v_midi(&cfg);
-
- return 0;
-}
-
-static void __exit cleanup_vmidi(void)
-{
- unload_v_midi(&cfg);
-}
-
-module_init(init_vmidi);
-module_exit(cleanup_vmidi);
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/v_midi.h b/sound/oss/v_midi.h
deleted file mode 100644
index 08e2185ee816..000000000000
--- a/sound/oss/v_midi.h
+++ /dev/null
@@ -1,14 +0,0 @@
-typedef struct vmidi_devc {
- int dev;
-
- /* State variables */
- int opened;
- spinlock_t lock;
-
- /* MIDI fields */
- int my_mididev;
- int pair_mididev;
- int input_opened;
- int intr_active;
- void (*midi_input_intr) (int dev, unsigned char data);
- } vmidi_devc;
diff --git a/sound/oss/vidc.c b/sound/oss/vidc.c
deleted file mode 100644
index f0e0caa53200..000000000000
--- a/sound/oss/vidc.c
+++ /dev/null
@@ -1,558 +0,0 @@
-/*
- * linux/drivers/sound/vidc.c
- *
- * Copyright (C) 1997-2000 by Russell King <rmk@arm.linux.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * VIDC20 audio driver.
- *
- * The VIDC20 sound hardware consists of the VIDC20 itself, a DAC and a DMA
- * engine. The DMA transfers fixed-format (16-bit little-endian linear)
- * samples to the VIDC20, which then transfers this data serially to the
- * DACs. The samplerate is controlled by the VIDC.
- *
- * We currently support a mixer device, but it is currently non-functional.
- */
-
-#include <linux/gfp.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-
-#include <mach/hardware.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <asm/hardware/iomd.h>
-#include <asm/irq.h>
-#include <asm/system.h>
-
-#include "sound_config.h"
-#include "vidc.h"
-
-#ifndef _SIOC_TYPE
-#define _SIOC_TYPE(x) _IOC_TYPE(x)
-#endif
-#ifndef _SIOC_NR
-#define _SIOC_NR(x) _IOC_NR(x)
-#endif
-
-#define VIDC_SOUND_CLOCK (250000)
-#define VIDC_SOUND_CLOCK_EXT (176400)
-
-/*
- * When using SERIAL SOUND mode (external DAC), the number of physical
- * channels is fixed at 2.
- */
-static int vidc_busy;
-static int vidc_adev;
-static int vidc_audio_rate;
-static char vidc_audio_format;
-static char vidc_audio_channels;
-
-static unsigned char vidc_level_l[SOUND_MIXER_NRDEVICES] = {
- 85, /* master */
- 50, /* bass */
- 50, /* treble */
- 0, /* synth */
- 75, /* pcm */
- 0, /* speaker */
- 100, /* ext line */
- 0, /* mic */
- 100, /* CD */
- 0,
-};
-
-static unsigned char vidc_level_r[SOUND_MIXER_NRDEVICES] = {
- 85, /* master */
- 50, /* bass */
- 50, /* treble */
- 0, /* synth */
- 75, /* pcm */
- 0, /* speaker */
- 100, /* ext line */
- 0, /* mic */
- 100, /* CD */
- 0,
-};
-
-static unsigned int vidc_audio_volume_l; /* left PCM vol, 0 - 65536 */
-static unsigned int vidc_audio_volume_r; /* right PCM vol, 0 - 65536 */
-
-extern void vidc_update_filler(int bits, int channels);
-extern int softoss_dev;
-
-static void
-vidc_mixer_set(int mdev, unsigned int level)
-{
- unsigned int lev_l = level & 0x007f;
- unsigned int lev_r = (level & 0x7f00) >> 8;
- unsigned int mlev_l, mlev_r;
-
- if (lev_l > 100)
- lev_l = 100;
- if (lev_r > 100)
- lev_r = 100;
-
-#define SCALE(lev,master) ((lev) * (master) * 65536 / 10000)
-
- mlev_l = vidc_level_l[SOUND_MIXER_VOLUME];
- mlev_r = vidc_level_r[SOUND_MIXER_VOLUME];
-
- switch (mdev) {
- case SOUND_MIXER_VOLUME:
- case SOUND_MIXER_PCM:
- vidc_level_l[mdev] = lev_l;
- vidc_level_r[mdev] = lev_r;
-
- vidc_audio_volume_l = SCALE(lev_l, mlev_l);
- vidc_audio_volume_r = SCALE(lev_r, mlev_r);
-/*printk("VIDC: PCM vol %05X %05X\n", vidc_audio_volume_l, vidc_audio_volume_r);*/
- break;
- }
-#undef SCALE
-}
-
-static int vidc_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
-{
- unsigned int val;
- unsigned int mdev;
-
- if (_SIOC_TYPE(cmd) != 'M')
- return -EINVAL;
-
- mdev = _SIOC_NR(cmd);
-
- if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
- if (get_user(val, (unsigned int __user *)arg))
- return -EFAULT;
-
- if (mdev < SOUND_MIXER_NRDEVICES)
- vidc_mixer_set(mdev, val);
- else
- return -EINVAL;
- }
-
- /*
- * Return parameters
- */
- switch (mdev) {
- case SOUND_MIXER_RECSRC:
- val = 0;
- break;
-
- case SOUND_MIXER_DEVMASK:
- val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH;
- break;
-
- case SOUND_MIXER_STEREODEVS:
- val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH;
- break;
-
- case SOUND_MIXER_RECMASK:
- val = 0;
- break;
-
- case SOUND_MIXER_CAPS:
- val = 0;
- break;
-
- default:
- if (mdev < SOUND_MIXER_NRDEVICES)
- val = vidc_level_l[mdev] | vidc_level_r[mdev] << 8;
- else
- return -EINVAL;
- }
-
- return put_user(val, (unsigned int __user *)arg) ? -EFAULT : 0;
-}
-
-static unsigned int vidc_audio_set_format(int dev, unsigned int fmt)
-{
- switch (fmt) {
- default:
- fmt = AFMT_S16_LE;
- case AFMT_U8:
- case AFMT_S8:
- case AFMT_S16_LE:
- vidc_audio_format = fmt;
- vidc_update_filler(vidc_audio_format, vidc_audio_channels);
- case AFMT_QUERY:
- break;
- }
- return vidc_audio_format;
-}
-
-#define my_abs(i) ((i)<0 ? -(i) : (i))
-
-static int vidc_audio_set_speed(int dev, int rate)
-{
- if (rate) {
- unsigned int hwctrl, hwrate, hwrate_ext, rate_int, rate_ext;
- unsigned int diff_int, diff_ext;
- unsigned int newsize, new2size;
-
- hwctrl = 0x00000003;
-
- /* Using internal clock */
- hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1;
- if (hwrate < 3)
- hwrate = 3;
- if (hwrate > 255)
- hwrate = 255;
-
- /* Using exernal clock */
- hwrate_ext = (((VIDC_SOUND_CLOCK_EXT * 2) / rate) + 1) >> 1;
- if (hwrate_ext < 3)
- hwrate_ext = 3;
- if (hwrate_ext > 255)
- hwrate_ext = 255;
-
- rate_int = VIDC_SOUND_CLOCK / hwrate;
- rate_ext = VIDC_SOUND_CLOCK_EXT / hwrate_ext;
-
- /* Chose between external and internal clock */
- diff_int = my_abs(rate_ext-rate);
- diff_ext = my_abs(rate_int-rate);
- if (diff_ext < diff_int) {
- /*printk("VIDC: external %d %d %d\n", rate, rate_ext, hwrate_ext);*/
- hwrate=hwrate_ext;
- hwctrl=0x00000002;
- /* Allow roughly 0.4% tolerance */
- if (diff_ext > (rate/256))
- rate=rate_ext;
- } else {
- /*printk("VIDC: internal %d %d %d\n", rate, rate_int, hwrate);*/
- hwctrl=0x00000003;
- /* Allow rougly 0.4% tolerance */
- if (diff_int > (rate/256))
- rate=rate_int;
- }
-
- vidc_writel(0xb0000000 | (hwrate - 2));
- vidc_writel(0xb1000000 | hwctrl);
-
- newsize = (10000 / hwrate) & ~3;
- if (newsize < 208)
- newsize = 208;
- if (newsize > 4096)
- newsize = 4096;
- for (new2size = 128; new2size < newsize; new2size <<= 1);
- if (new2size - newsize > newsize - (new2size >> 1))
- new2size >>= 1;
- if (new2size > 4096) {
- printk(KERN_ERR "VIDC: error: dma buffer (%d) %d > 4K\n",
- newsize, new2size);
- new2size = 4096;
- }
- /*printk("VIDC: dma size %d\n", new2size);*/
- dma_bufsize = new2size;
- vidc_audio_rate = rate;
- }
- return vidc_audio_rate;
-}
-
-static short vidc_audio_set_channels(int dev, short channels)
-{
- switch (channels) {
- default:
- channels = 2;
- case 1:
- case 2:
- vidc_audio_channels = channels;
- vidc_update_filler(vidc_audio_format, vidc_audio_channels);
- case 0:
- break;
- }
- return vidc_audio_channels;
-}
-
-/*
- * Open the device
- */
-static int vidc_audio_open(int dev, int mode)
-{
- /* This audio device does not have recording capability */
- if (mode == OPEN_READ)
- return -EPERM;
-
- if (vidc_busy)
- return -EBUSY;
-
- vidc_busy = 1;
- return 0;
-}
-
-/*
- * Close the device
- */
-static void vidc_audio_close(int dev)
-{
- vidc_busy = 0;
-}
-
-/*
- * Output a block via DMA to sound device.
- *
- * We just set the DMA start and count; the DMA interrupt routine
- * will take care of formatting the samples (via the appropriate
- * vidc_filler routine), and flag via vidc_audio_dma_interrupt when
- * more data is required.
- */
-static void
-vidc_audio_output_block(int dev, unsigned long buf, int total_count, int one)
-{
- struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
- unsigned long flags;
-
- local_irq_save(flags);
- dma_start = buf - (unsigned long)dmap->raw_buf_phys + (unsigned long)dmap->raw_buf;
- dma_count = total_count;
- local_irq_restore(flags);
-}
-
-static void
-vidc_audio_start_input(int dev, unsigned long buf, int count, int intrflag)
-{
-}
-
-static int vidc_audio_prepare_for_input(int dev, int bsize, int bcount)
-{
- return -EINVAL;
-}
-
-static irqreturn_t vidc_audio_dma_interrupt(void)
-{
- DMAbuf_outputintr(vidc_adev, 1);
- return IRQ_HANDLED;
-}
-
-/*
- * Prepare for outputting samples.
- *
- * Each buffer that will be passed will be `bsize' bytes long,
- * with a total of `bcount' buffers.
- */
-static int vidc_audio_prepare_for_output(int dev, int bsize, int bcount)
-{
- struct audio_operations *adev = audio_devs[dev];
-
- dma_interrupt = NULL;
- adev->dmap_out->flags |= DMA_NODMA;
-
- return 0;
-}
-
-/*
- * Stop our current operation.
- */
-static void vidc_audio_reset(int dev)
-{
- dma_interrupt = NULL;
-}
-
-static int vidc_audio_local_qlen(int dev)
-{
- return /*dma_count !=*/ 0;
-}
-
-static void vidc_audio_trigger(int dev, int enable_bits)
-{
- struct audio_operations *adev = audio_devs[dev];
-
- if (enable_bits & PCM_ENABLE_OUTPUT) {
- if (!(adev->dmap_out->flags & DMA_ACTIVE)) {
- unsigned long flags;
-
- local_irq_save(flags);
-
- /* prevent recusion */
- adev->dmap_out->flags |= DMA_ACTIVE;
-
- dma_interrupt = vidc_audio_dma_interrupt;
- vidc_sound_dma_irq(0, NULL);
- iomd_writeb(DMA_CR_E | 0x10, IOMD_SD0CR);
-
- local_irq_restore(flags);
- }
- }
-}
-
-static struct audio_driver vidc_audio_driver =
-{
- .owner = THIS_MODULE,
- .open = vidc_audio_open,
- .close = vidc_audio_close,
- .output_block = vidc_audio_output_block,
- .start_input = vidc_audio_start_input,
- .prepare_for_input = vidc_audio_prepare_for_input,
- .prepare_for_output = vidc_audio_prepare_for_output,
- .halt_io = vidc_audio_reset,
- .local_qlen = vidc_audio_local_qlen,
- .trigger = vidc_audio_trigger,
- .set_speed = vidc_audio_set_speed,
- .set_bits = vidc_audio_set_format,
- .set_channels = vidc_audio_set_channels
-};
-
-static struct mixer_operations vidc_mixer_operations = {
- .owner = THIS_MODULE,
- .id = "VIDC",
- .name = "VIDCsound",
- .ioctl = vidc_mixer_ioctl
-};
-
-void vidc_update_filler(int format, int channels)
-{
-#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3))
-
- switch (TYPE(format, channels)) {
- default:
- case TYPE(AFMT_U8, 1):
- vidc_filler = vidc_fill_1x8_u;
- break;
-
- case TYPE(AFMT_U8, 2):
- vidc_filler = vidc_fill_2x8_u;
- break;
-
- case TYPE(AFMT_S8, 1):
- vidc_filler = vidc_fill_1x8_s;
- break;
-
- case TYPE(AFMT_S8, 2):
- vidc_filler = vidc_fill_2x8_s;
- break;
-
- case TYPE(AFMT_S16_LE, 1):
- vidc_filler = vidc_fill_1x16_s;
- break;
-
- case TYPE(AFMT_S16_LE, 2):
- vidc_filler = vidc_fill_2x16_s;
- break;
- }
-}
-
-static void __init attach_vidc(struct address_info *hw_config)
-{
- char name[32];
- int i, adev;
-
- sprintf(name, "VIDC %d-bit sound", hw_config->card_subtype);
- conf_printf(name, hw_config);
- memset(dma_buf, 0, sizeof(dma_buf));
-
- adev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, name,
- &vidc_audio_driver, sizeof(vidc_audio_driver),
- DMA_AUTOMODE, AFMT_U8 | AFMT_S8 | AFMT_S16_LE,
- NULL, hw_config->dma, hw_config->dma2);
-
- if (adev < 0)
- goto audio_failed;
-
- /*
- * 1024 bytes => 64 buffers
- */
- audio_devs[adev]->min_fragment = 10;
- audio_devs[adev]->mixer_dev = num_mixers;
-
- audio_devs[adev]->mixer_dev =
- sound_install_mixer(MIXER_DRIVER_VERSION,
- name, &vidc_mixer_operations,
- sizeof(vidc_mixer_operations), NULL);
-
- if (audio_devs[adev]->mixer_dev < 0)
- goto mixer_failed;
-
- for (i = 0; i < 2; i++) {
- dma_buf[i] = get_zeroed_page(GFP_KERNEL);
- if (!dma_buf[i]) {
- printk(KERN_ERR "%s: can't allocate required buffers\n",
- name);
- goto mem_failed;
- }
- dma_pbuf[i] = virt_to_phys((void *)dma_buf[i]);
- }
-
- if (sound_alloc_dma(hw_config->dma, hw_config->name)) {
- printk(KERN_ERR "%s: DMA %d is in use\n", name, hw_config->dma);
- goto dma_failed;
- }
-
- if (request_irq(hw_config->irq, vidc_sound_dma_irq, 0,
- hw_config->name, &dma_start)) {
- printk(KERN_ERR "%s: IRQ %d is in use\n", name, hw_config->irq);
- goto irq_failed;
- }
- vidc_adev = adev;
- vidc_mixer_set(SOUND_MIXER_VOLUME, (85 | 85 << 8));
-
- return;
-
-irq_failed:
- sound_free_dma(hw_config->dma);
-dma_failed:
-mem_failed:
- for (i = 0; i < 2; i++)
- free_page(dma_buf[i]);
- sound_unload_mixerdev(audio_devs[adev]->mixer_dev);
-mixer_failed:
- sound_unload_audiodev(adev);
-audio_failed:
- return;
-}
-
-static int __init probe_vidc(struct address_info *hw_config)
-{
- hw_config->irq = IRQ_DMAS0;
- hw_config->dma = DMA_VIRTUAL_SOUND;
- hw_config->dma2 = -1;
- hw_config->card_subtype = 16;
- hw_config->name = "VIDC20";
- return 1;
-}
-
-static void __exit unload_vidc(struct address_info *hw_config)
-{
- int i, adev = vidc_adev;
-
- vidc_adev = -1;
-
- free_irq(hw_config->irq, &dma_start);
- sound_free_dma(hw_config->dma);
-
- if (adev >= 0) {
- sound_unload_mixerdev(audio_devs[adev]->mixer_dev);
- sound_unload_audiodev(adev);
- for (i = 0; i < 2; i++)
- free_page(dma_buf[i]);
- }
-}
-
-static struct address_info cfg;
-
-static int __init init_vidc(void)
-{
- if (probe_vidc(&cfg) == 0)
- return -ENODEV;
-
- attach_vidc(&cfg);
-
- return 0;
-}
-
-static void __exit cleanup_vidc(void)
-{
- unload_vidc(&cfg);
-}
-
-module_init(init_vidc);
-module_exit(cleanup_vidc);
-
-MODULE_AUTHOR("Russell King");
-MODULE_DESCRIPTION("VIDC20 audio driver");
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/vidc.h b/sound/oss/vidc.h
deleted file mode 100644
index 0d1424751ecd..000000000000
--- a/sound/oss/vidc.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * linux/drivers/sound/vidc.h
- *
- * Copyright (C) 1997 Russell King <rmk@arm.linux.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * VIDC sound function prototypes
- */
-
-/* vidc_fill.S */
-
-/*
- * Filler routines for different channels and sample sizes
- */
-
-extern unsigned long vidc_fill_1x8_u(unsigned long ibuf, unsigned long iend,
- unsigned long obuf, int mask);
-extern unsigned long vidc_fill_2x8_u(unsigned long ibuf, unsigned long iend,
- unsigned long obuf, int mask);
-extern unsigned long vidc_fill_1x8_s(unsigned long ibuf, unsigned long iend,
- unsigned long obuf, int mask);
-extern unsigned long vidc_fill_2x8_s(unsigned long ibuf, unsigned long iend,
- unsigned long obuf, int mask);
-extern unsigned long vidc_fill_1x16_s(unsigned long ibuf, unsigned long iend,
- unsigned long obuf, int mask);
-extern unsigned long vidc_fill_2x16_s(unsigned long ibuf, unsigned long iend,
- unsigned long obuf, int mask);
-
-/*
- * DMA Interrupt handler
- */
-
-extern irqreturn_t vidc_sound_dma_irq(int irqnr, void *ref);
-
-/*
- * Filler routine pointer
- */
-
-extern unsigned long (*vidc_filler) (unsigned long ibuf, unsigned long iend,
- unsigned long obuf, int mask);
-
-/*
- * Virtual DMA buffer exhausted
- */
-
-extern irqreturn_t (*dma_interrupt) (void);
-
-/*
- * Virtual DMA buffer addresses
- */
-
-extern unsigned long dma_start, dma_count, dma_bufsize;
-extern unsigned long dma_buf[2], dma_pbuf[2];
-
-/* vidc_synth.c */
-
-extern void vidc_synth_init(struct address_info *hw_config);
-extern void vidc_synth_exit(struct address_info *hw_config);
-extern int vidc_synth_get_volume(void);
-extern int vidc_synth_set_volume(int vol);
diff --git a/sound/oss/vidc_fill.S b/sound/oss/vidc_fill.S
deleted file mode 100644
index bed34921d176..000000000000
--- a/sound/oss/vidc_fill.S
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * linux/drivers/sound/vidc_fill.S
- *
- * Copyright (C) 1997 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Filler routines for DMA buffers
- */
-#include <linux/linkage.h>
-#include <asm/assembler.h>
-#include <mach/hardware.h>
-#include <asm/hardware/iomd.h>
-
- .text
-
-ENTRY(vidc_fill_1x8_u)
- mov ip, #0xff00
-1: cmp r0, r1
- bge vidc_clear
- ldrb r4, [r0], #1
- eor r4, r4, #0x80
- and r4, ip, r4, lsl #8
- orr r4, r4, r4, lsl #16
- str r4, [r2], #4
- cmp r2, r3
- blt 1b
- mov pc, lr
-
-ENTRY(vidc_fill_2x8_u)
- mov ip, #0xff00
-1: cmp r0, r1
- bge vidc_clear
- ldr r4, [r0], #2
- and r5, r4, ip
- and r4, ip, r4, lsl #8
- orr r4, r4, r5, lsl #16
- orr r4, r4, r4, lsr #8
- str r4, [r2], #4
- cmp r2, r3
- blt 1b
- mov pc, lr
-
-ENTRY(vidc_fill_1x8_s)
- mov ip, #0xff00
-1: cmp r0, r1
- bge vidc_clear
- ldrb r4, [r0], #1
- and r4, ip, r4, lsl #8
- orr r4, r4, r4, lsl #16
- str r4, [r2], #4
- cmp r2, r3
- blt 1b
- mov pc, lr
-
-ENTRY(vidc_fill_2x8_s)
- mov ip, #0xff00
-1: cmp r0, r1
- bge vidc_clear
- ldr r4, [r0], #2
- and r5, r4, ip
- and r4, ip, r4, lsl #8
- orr r4, r4, r5, lsl #16
- orr r4, r4, r4, lsr #8
- str r4, [r2], #4
- cmp r2, r3
- blt 1b
- mov pc, lr
-
-ENTRY(vidc_fill_1x16_s)
- mov ip, #0xff00
- orr ip, ip, ip, lsr #8
-1: cmp r0, r1
- bge vidc_clear
- ldr r5, [r0], #2
- and r4, r5, ip
- orr r4, r4, r4, lsl #16
- str r4, [r2], #4
- cmp r0, r1
- addlt r0, r0, #2
- andlt r4, r5, ip, lsl #16
- orrlt r4, r4, r4, lsr #16
- strlt r4, [r2], #4
- cmp r2, r3
- blt 1b
- mov pc, lr
-
-ENTRY(vidc_fill_2x16_s)
- mov ip, #0xff00
- orr ip, ip, ip, lsr #8
-1: cmp r0, r1
- bge vidc_clear
- ldr r4, [r0], #4
- str r4, [r2], #4
- cmp r0, r1
- ldrlt r4, [r0], #4
- strlt r4, [r2], #4
- cmp r2, r3
- blt 1b
- mov pc, lr
-
-ENTRY(vidc_fill_noaudio)
- mov r0, #0
- mov r1, #0
-2: mov r4, #0
- mov r5, #0
-1: cmp r2, r3
- stmltia r2!, {r0, r1, r4, r5}
- blt 1b
- mov pc, lr
-
-ENTRY(vidc_clear)
- mov r0, #0
- mov r1, #0
- tst r2, #4
- str r0, [r2], #4
- tst r2, #8
- stmia r2!, {r0, r1}
- b 2b
-
-/*
- * Call filler routines with:
- * r0 = phys address
- * r1 = phys end
- * r2 = buffer
- * Returns:
- * r0 = new buffer address
- * r2 = new buffer finish
- * r4 = corrupted
- * r5 = corrupted
- * ip = corrupted
- */
-
-ENTRY(vidc_sound_dma_irq)
- stmfd sp!, {r4 - r8, lr}
- ldr r8, =dma_start
- ldmia r8, {r0, r1, r2, r3, r4, r5}
- teq r1, #0
- adreq r4, vidc_fill_noaudio
- moveq r7, #1 << 31
- movne r7, #0
- mov ip, #IOMD_BASE & 0xff000000
- orr ip, ip, #IOMD_BASE & 0x00ff0000
- ldrb r6, [ip, #IOMD_SD0ST]
- tst r6, #DMA_ST_OFL @ Check for overrun
- eorne r6, r6, #DMA_ST_AB
- tst r6, #DMA_ST_AB
- moveq r2, r3 @ DMAing A, update B
- add r3, r2, r5 @ End of DMA buffer
- add r1, r1, r0 @ End of virtual DMA buffer
- mov lr, pc
- mov pc, r4 @ Call fill routine (uses r4, ip)
- sub r1, r1, r0 @ Remaining length
- stmia r8, {r0, r1}
- mov r0, #0
- tst r2, #4 @ Round buffer up to 4 words
- strne r0, [r2], #4
- tst r2, #8
- strne r0, [r2], #4
- strne r0, [r2], #4
- sub r2, r2, #16
- mov r2, r2, lsl #20
- movs r2, r2, lsr #20
- orreq r2, r2, #1 << 30 @ Set L bit
- orr r2, r2, r7
- ldmdb r8, {r3, r4, r5}
- tst r6, #DMA_ST_AB
- mov ip, #IOMD_BASE & 0xff000000
- orr ip, ip, #IOMD_BASE & 0x00ff0000
- streq r4, [ip, #IOMD_SD0CURB]
- strne r5, [ip, #IOMD_SD0CURA]
- streq r2, [ip, #IOMD_SD0ENDB]
- strne r2, [ip, #IOMD_SD0ENDA]
- ldr lr, [ip, #IOMD_SD0ST]
- tst lr, #DMA_ST_OFL
- bne 1f
- tst r6, #DMA_ST_AB
- strne r4, [ip, #IOMD_SD0CURB]
- streq r5, [ip, #IOMD_SD0CURA]
- strne r2, [ip, #IOMD_SD0ENDB]
- streq r2, [ip, #IOMD_SD0ENDA]
-1: teq r7, #0
- mov r0, #0x10
- strneb r0, [ip, #IOMD_SD0CR]
- ldmfd sp!, {r4 - r8, lr}
- mov r0, #1 @ IRQ_HANDLED
- teq r1, #0 @ If we have no more
- movne pc, lr
- teq r3, #0
- movne pc, r3 @ Call interrupt routine
- mov pc, lr
-
- .data
- .globl dma_interrupt
-dma_interrupt:
- .long 0 @ r3
- .globl dma_pbuf
-dma_pbuf:
- .long 0 @ r4
- .long 0 @ r5
- .globl dma_start
-dma_start:
- .long 0 @ r0
- .globl dma_count
-dma_count:
- .long 0 @ r1
- .globl dma_buf
-dma_buf:
- .long 0 @ r2
- .long 0 @ r3
- .globl vidc_filler
-vidc_filler:
- .long vidc_fill_noaudio @ r4
- .globl dma_bufsize
-dma_bufsize:
- .long 0x1000 @ r5
diff --git a/sound/oss/vwsnd.c b/sound/oss/vwsnd.c
deleted file mode 100644
index 643f1113b1d8..000000000000
--- a/sound/oss/vwsnd.c
+++ /dev/null
@@ -1,3498 +0,0 @@
-/*
- * Sound driver for Silicon Graphics 320 and 540 Visual Workstations'
- * onboard audio. See notes in Documentation/sound/oss/vwsnd .
- *
- * Copyright 1999 Silicon Graphics, Inc. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#undef VWSND_DEBUG /* define for debugging */
-
-/*
- * XXX to do -
- *
- * External sync.
- * Rename swbuf, hwbuf, u&i, hwptr&swptr to something rational.
- * Bug - if select() called before read(), pcm_setup() not called.
- * Bug - output doesn't stop soon enough if process killed.
- */
-
-/*
- * Things to test -
- *
- * Will readv/writev work? Write a test.
- *
- * insmod/rmmod 100 million times.
- *
- * Run I/O until int ptrs wrap around (roughly 6.2 hours @ DAT
- * rate).
- *
- * Concurrent threads banging on mixer simultaneously, both UP
- * and SMP kernels. Especially, watch for thread A changing
- * OUTSRC while thread B changes gain -- both write to the same
- * ad1843 register.
- *
- * What happens if a client opens /dev/audio then forks?
- * Do two procs have /dev/audio open? Test.
- *
- * Pump audio through the CD, MIC and line inputs and verify that
- * they mix/mute into the output.
- *
- * Apps:
- * amp
- * mpg123
- * x11amp
- * mxv
- * kmedia
- * esound
- * need more input apps
- *
- * Run tests while bombarding with signals. setitimer(2) will do it... */
-
-/*
- * This driver is organized in nine sections.
- * The nine sections are:
- *
- * debug stuff
- * low level lithium access
- * high level lithium access
- * AD1843 access
- * PCM I/O
- * audio driver
- * mixer driver
- * probe/attach/unload
- * initialization and loadable kernel module interface
- *
- * That is roughly the order of increasing abstraction, so forward
- * dependencies are minimal.
- */
-
-/*
- * Locking Notes
- *
- * INC_USE_COUNT and DEC_USE_COUNT keep track of the number of
- * open descriptors to this driver. They store it in vwsnd_use_count.
- * The global device list, vwsnd_dev_list, is immutable when the IN_USE
- * is true.
- *
- * devc->open_lock is a semaphore that is used to enforce the
- * single reader/single writer rule for /dev/audio. The rule is
- * that each device may have at most one reader and one writer.
- * Open will block until the previous client has closed the
- * device, unless O_NONBLOCK is specified.
- *
- * The semaphore devc->io_mutex serializes PCM I/O syscalls. This
- * is unnecessary in Linux 2.2, because the kernel lock
- * serializes read, write, and ioctl globally, but it's there,
- * ready for the brave, new post-kernel-lock world.
- *
- * Locking between interrupt and baselevel is handled by the
- * "lock" spinlock in vwsnd_port (one lock each for read and
- * write). Each half holds the lock just long enough to see what
- * area it owns and update its pointers. See pcm_output() and
- * pcm_input() for most of the gory stuff.
- *
- * devc->mix_mutex serializes all mixer ioctls. This is also
- * redundant because of the kernel lock.
- *
- * The lowest level lock is lith->lithium_lock. It is a
- * spinlock which is held during the two-register tango of
- * reading/writing an AD1843 register. See
- * li_{read,write}_ad1843_reg().
- */
-
-/*
- * Sample Format Notes
- *
- * Lithium's DMA engine has two formats: 16-bit 2's complement
- * and 8-bit unsigned . 16-bit transfers the data unmodified, 2
- * bytes per sample. 8-bit unsigned transfers 1 byte per sample
- * and XORs each byte with 0x80. Lithium can input or output
- * either mono or stereo in either format.
- *
- * The AD1843 has four formats: 16-bit 2's complement, 8-bit
- * unsigned, 8-bit mu-Law and 8-bit A-Law.
- *
- * This driver supports five formats: AFMT_S8, AFMT_U8,
- * AFMT_MU_LAW, AFMT_A_LAW, and AFMT_S16_LE.
- *
- * For AFMT_U8 output, we keep the AD1843 in 16-bit mode, and
- * rely on Lithium's XOR to translate between U8 and S8.
- *
- * For AFMT_S8, AFMT_MU_LAW and AFMT_A_LAW output, we have to XOR
- * the 0x80 bit in software to compensate for Lithium's XOR.
- * This happens in pcm_copy_{in,out}().
- *
- * Changes:
- * 11-10-2000 Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
- * Added some __init/__exit
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-
-#include <linux/spinlock.h>
-#include <linux/wait.h>
-#include <linux/interrupt.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-
-#include <asm/visws/cobalt.h>
-
-#include "sound_config.h"
-
-/*****************************************************************************/
-/* debug stuff */
-
-#ifdef VWSND_DEBUG
-
-static DEFINE_MUTEX(vwsnd_mutex);
-static int shut_up = 1;
-
-/*
- * dbgassert - called when an assertion fails.
- */
-
-static void dbgassert(const char *fcn, int line, const char *expr)
-{
- if (in_interrupt())
- panic("ASSERTION FAILED IN INTERRUPT, %s:%s:%d %s\n",
- __FILE__, fcn, line, expr);
- else {
- int x;
- printk(KERN_ERR "ASSERTION FAILED, %s:%s:%d %s\n",
- __FILE__, fcn, line, expr);
- x = * (volatile int *) 0; /* force proc to exit */
- }
-}
-
-/*
- * Bunch of useful debug macros:
- *
- * ASSERT - print unless e nonzero (panic if in interrupt)
- * DBGDO - include arbitrary code if debugging
- * DBGX - debug print raw (w/o function name)
- * DBGP - debug print w/ function name
- * DBGE - debug print function entry
- * DBGC - debug print function call
- * DBGR - debug print function return
- * DBGXV - debug print raw when verbose
- * DBGPV - debug print when verbose
- * DBGEV - debug print function entry when verbose
- * DBGRV - debug print function return when verbose
- */
-
-#define ASSERT(e) ((e) ? (void) 0 : dbgassert(__func__, __LINE__, #e))
-#define DBGDO(x) x
-#define DBGX(fmt, args...) (in_interrupt() ? 0 : printk(KERN_ERR fmt, ##args))
-#define DBGP(fmt, args...) (DBGX("%s: " fmt, __func__ , ##args))
-#define DBGE(fmt, args...) (DBGX("%s" fmt, __func__ , ##args))
-#define DBGC(rtn) (DBGP("calling %s\n", rtn))
-#define DBGR() (DBGP("returning\n"))
-#define DBGXV(fmt, args...) (shut_up ? 0 : DBGX(fmt, ##args))
-#define DBGPV(fmt, args...) (shut_up ? 0 : DBGP(fmt, ##args))
-#define DBGEV(fmt, args...) (shut_up ? 0 : DBGE(fmt, ##args))
-#define DBGCV(rtn) (shut_up ? 0 : DBGC(rtn))
-#define DBGRV() (shut_up ? 0 : DBGR())
-
-#else /* !VWSND_DEBUG */
-
-#define ASSERT(e) ((void) 0)
-#define DBGDO(x) /* don't */
-#define DBGX(fmt, args...) ((void) 0)
-#define DBGP(fmt, args...) ((void) 0)
-#define DBGE(fmt, args...) ((void) 0)
-#define DBGC(rtn) ((void) 0)
-#define DBGR() ((void) 0)
-#define DBGPV(fmt, args...) ((void) 0)
-#define DBGXV(fmt, args...) ((void) 0)
-#define DBGEV(fmt, args...) ((void) 0)
-#define DBGCV(rtn) ((void) 0)
-#define DBGRV() ((void) 0)
-
-#endif /* !VWSND_DEBUG */
-
-/*****************************************************************************/
-/* low level lithium access */
-
-/*
- * We need to talk to Lithium registers on three pages. Here are
- * the pages' offsets from the base address (0xFF001000).
- */
-
-enum {
- LI_PAGE0_OFFSET = 0x01000 - 0x1000, /* FF001000 */
- LI_PAGE1_OFFSET = 0x0F000 - 0x1000, /* FF00F000 */
- LI_PAGE2_OFFSET = 0x10000 - 0x1000, /* FF010000 */
-};
-
-/* low-level lithium data */
-
-typedef struct lithium {
- void * page0; /* virtual addresses */
- void * page1;
- void * page2;
- spinlock_t lock; /* protects codec and UST/MSC access */
-} lithium_t;
-
-/*
- * li_destroy destroys the lithium_t structure and vm mappings.
- */
-
-static void li_destroy(lithium_t *lith)
-{
- if (lith->page0) {
- iounmap(lith->page0);
- lith->page0 = NULL;
- }
- if (lith->page1) {
- iounmap(lith->page1);
- lith->page1 = NULL;
- }
- if (lith->page2) {
- iounmap(lith->page2);
- lith->page2 = NULL;
- }
-}
-
-/*
- * li_create initializes the lithium_t structure and sets up vm mappings
- * to access the registers.
- * Returns 0 on success, -errno on failure.
- */
-
-static int __init li_create(lithium_t *lith, unsigned long baseaddr)
-{
- spin_lock_init(&lith->lock);
- lith->page0 = ioremap_nocache(baseaddr + LI_PAGE0_OFFSET, PAGE_SIZE);
- lith->page1 = ioremap_nocache(baseaddr + LI_PAGE1_OFFSET, PAGE_SIZE);
- lith->page2 = ioremap_nocache(baseaddr + LI_PAGE2_OFFSET, PAGE_SIZE);
- if (!lith->page0 || !lith->page1 || !lith->page2) {
- li_destroy(lith);
- return -ENOMEM;
- }
- return 0;
-}
-
-/*
- * basic register accessors - read/write long/byte
- */
-
-static __inline__ unsigned long li_readl(lithium_t *lith, int off)
-{
- return * (volatile unsigned long *) (lith->page0 + off);
-}
-
-static __inline__ unsigned char li_readb(lithium_t *lith, int off)
-{
- return * (volatile unsigned char *) (lith->page0 + off);
-}
-
-static __inline__ void li_writel(lithium_t *lith, int off, unsigned long val)
-{
- * (volatile unsigned long *) (lith->page0 + off) = val;
-}
-
-static __inline__ void li_writeb(lithium_t *lith, int off, unsigned char val)
-{
- * (volatile unsigned char *) (lith->page0 + off) = val;
-}
-
-/*****************************************************************************/
-/* High Level Lithium Access */
-
-/*
- * Lithium DMA Notes
- *
- * Lithium has two dedicated DMA channels for audio. They are known
- * as comm1 and comm2 (communication areas 1 and 2). Comm1 is for
- * input, and comm2 is for output. Each is controlled by three
- * registers: BASE (base address), CFG (config) and CCTL
- * (config/control).
- *
- * Each DMA channel points to a physically contiguous ring buffer in
- * main memory of up to 8 Kbytes. (This driver always uses 8 Kb.)
- * There are three pointers into the ring buffer: read, write, and
- * trigger. The pointers are 8 bits each. Each pointer points to
- * 32-byte "chunks" of data. The DMA engine moves 32 bytes at a time,
- * so there is no finer-granularity control.
- *
- * In comm1, the hardware updates the write ptr, and software updates
- * the read ptr. In comm2, it's the opposite: hardware updates the
- * read ptr, and software updates the write ptr. I designate the
- * hardware-updated ptr as the hwptr, and the software-updated ptr as
- * the swptr.
- *
- * The trigger ptr and trigger mask are used to trigger interrupts.
- * From the Lithium spec, section 5.6.8, revision of 12/15/1998:
- *
- * Trigger Mask Value
- *
- * A three bit wide field that represents a power of two mask
- * that is used whenever the trigger pointer is compared to its
- * respective read or write pointer. A value of zero here
- * implies a mask of 0xFF and a value of seven implies a mask
- * 0x01. This value can be used to sub-divide the ring buffer
- * into pie sections so that interrupts monitor the progress of
- * hardware from section to section.
- *
- * My interpretation of that is, whenever the hw ptr is updated, it is
- * compared with the trigger ptr, and the result is masked by the
- * trigger mask. (Actually, by the complement of the trigger mask.)
- * If the result is zero, an interrupt is triggered. I.e., interrupt
- * if ((hwptr & ~mask) == (trptr & ~mask)). The mask is formed from
- * the trigger register value as mask = (1 << (8 - tmreg)) - 1.
- *
- * In yet different words, setting tmreg to 0 causes an interrupt after
- * every 256 DMA chunks (8192 bytes) or once per traversal of the
- * ring buffer. Setting it to 7 caues an interrupt every 2 DMA chunks
- * (64 bytes) or 128 times per traversal of the ring buffer.
- */
-
-/* Lithium register offsets and bit definitions */
-
-#define LI_HOST_CONTROLLER 0x000
-# define LI_HC_RESET 0x00008000
-# define LI_HC_LINK_ENABLE 0x00004000
-# define LI_HC_LINK_FAILURE 0x00000004
-# define LI_HC_LINK_CODEC 0x00000002
-# define LI_HC_LINK_READY 0x00000001
-
-#define LI_INTR_STATUS 0x010
-#define LI_INTR_MASK 0x014
-# define LI_INTR_LINK_ERR 0x00008000
-# define LI_INTR_COMM2_TRIG 0x00000008
-# define LI_INTR_COMM2_UNDERFLOW 0x00000004
-# define LI_INTR_COMM1_TRIG 0x00000002
-# define LI_INTR_COMM1_OVERFLOW 0x00000001
-
-#define LI_CODEC_COMMAND 0x018
-# define LI_CC_BUSY 0x00008000
-# define LI_CC_DIR 0x00000080
-# define LI_CC_DIR_RD LI_CC_DIR
-# define LI_CC_DIR_WR (!LI_CC_DIR)
-# define LI_CC_ADDR_MASK 0x0000007F
-
-#define LI_CODEC_DATA 0x01C
-
-#define LI_COMM1_BASE 0x100
-#define LI_COMM1_CTL 0x104
-# define LI_CCTL_RESET 0x80000000
-# define LI_CCTL_SIZE 0x70000000
-# define LI_CCTL_DMA_ENABLE 0x08000000
-# define LI_CCTL_TMASK 0x07000000 /* trigger mask */
-# define LI_CCTL_TPTR 0x00FF0000 /* trigger pointer */
-# define LI_CCTL_RPTR 0x0000FF00
-# define LI_CCTL_WPTR 0x000000FF
-#define LI_COMM1_CFG 0x108
-# define LI_CCFG_LOCK 0x00008000
-# define LI_CCFG_SLOT 0x00000070
-# define LI_CCFG_DIRECTION 0x00000008
-# define LI_CCFG_DIR_IN (!LI_CCFG_DIRECTION)
-# define LI_CCFG_DIR_OUT LI_CCFG_DIRECTION
-# define LI_CCFG_MODE 0x00000004
-# define LI_CCFG_MODE_MONO (!LI_CCFG_MODE)
-# define LI_CCFG_MODE_STEREO LI_CCFG_MODE
-# define LI_CCFG_FORMAT 0x00000003
-# define LI_CCFG_FMT_8BIT 0x00000000
-# define LI_CCFG_FMT_16BIT 0x00000001
-#define LI_COMM2_BASE 0x10C
-#define LI_COMM2_CTL 0x110
- /* bit definitions are the same as LI_COMM1_CTL */
-#define LI_COMM2_CFG 0x114
- /* bit definitions are the same as LI_COMM1_CFG */
-
-#define LI_UST_LOW 0x200 /* 64-bit Unadjusted System Time is */
-#define LI_UST_HIGH 0x204 /* microseconds since boot */
-
-#define LI_AUDIO1_UST 0x300 /* UST-MSC pairs */
-#define LI_AUDIO1_MSC 0x304 /* MSC (Media Stream Counter) */
-#define LI_AUDIO2_UST 0x308 /* counts samples actually */
-#define LI_AUDIO2_MSC 0x30C /* processed as of time UST */
-
-/*
- * Lithium's DMA engine operates on chunks of 32 bytes. We call that
- * a DMACHUNK.
- */
-
-#define DMACHUNK_SHIFT 5
-#define DMACHUNK_SIZE (1 << DMACHUNK_SHIFT)
-#define BYTES_TO_CHUNKS(bytes) ((bytes) >> DMACHUNK_SHIFT)
-#define CHUNKS_TO_BYTES(chunks) ((chunks) << DMACHUNK_SHIFT)
-
-/*
- * Two convenient macros to shift bitfields into/out of position.
- *
- * Observe that (mask & -mask) is (1 << low_set_bit_of(mask)).
- * As long as mask is constant, we trust the compiler will change the
- * multipy and divide into shifts.
- */
-
-#define SHIFT_FIELD(val, mask) (((val) * ((mask) & -(mask))) & (mask))
-#define UNSHIFT_FIELD(val, mask) (((val) & (mask)) / ((mask) & -(mask)))
-
-/*
- * dma_chan_desc is invariant information about a Lithium
- * DMA channel. There are two instances, li_comm1 and li_comm2.
- *
- * Note that the CCTL register fields are write ptr and read ptr, but what
- * we care about are which pointer is updated by software and which by
- * hardware.
- */
-
-typedef struct dma_chan_desc {
- int basereg;
- int cfgreg;
- int ctlreg;
- int hwptrreg;
- int swptrreg;
- int ustreg;
- int mscreg;
- unsigned long swptrmask;
- int ad1843_slot;
- int direction; /* LI_CCTL_DIR_IN/OUT */
-} dma_chan_desc_t;
-
-static const dma_chan_desc_t li_comm1 = {
- LI_COMM1_BASE, /* base register offset */
- LI_COMM1_CFG, /* config register offset */
- LI_COMM1_CTL, /* control register offset */
- LI_COMM1_CTL + 0, /* hw ptr reg offset (write ptr) */
- LI_COMM1_CTL + 1, /* sw ptr reg offset (read ptr) */
- LI_AUDIO1_UST, /* ust reg offset */
- LI_AUDIO1_MSC, /* msc reg offset */
- LI_CCTL_RPTR, /* sw ptr bitmask in ctlval */
- 2, /* ad1843 serial slot */
- LI_CCFG_DIR_IN /* direction */
-};
-
-static const dma_chan_desc_t li_comm2 = {
- LI_COMM2_BASE, /* base register offset */
- LI_COMM2_CFG, /* config register offset */
- LI_COMM2_CTL, /* control register offset */
- LI_COMM2_CTL + 1, /* hw ptr reg offset (read ptr) */
- LI_COMM2_CTL + 0, /* sw ptr reg offset (writr ptr) */
- LI_AUDIO2_UST, /* ust reg offset */
- LI_AUDIO2_MSC, /* msc reg offset */
- LI_CCTL_WPTR, /* sw ptr bitmask in ctlval */
- 2, /* ad1843 serial slot */
- LI_CCFG_DIR_OUT /* direction */
-};
-
-/*
- * dma_chan is variable information about a Lithium DMA channel.
- *
- * The desc field points to invariant information.
- * The lith field points to a lithium_t which is passed
- * to li_read* and li_write* to access the registers.
- * The *val fields shadow the lithium registers' contents.
- */
-
-typedef struct dma_chan {
- const dma_chan_desc_t *desc;
- lithium_t *lith;
- unsigned long baseval;
- unsigned long cfgval;
- unsigned long ctlval;
-} dma_chan_t;
-
-/*
- * ustmsc is a UST/MSC pair (Unadjusted System Time/Media Stream Counter).
- * UST is time in microseconds since the system booted, and MSC is a
- * counter that increments with every audio sample.
- */
-
-typedef struct ustmsc {
- unsigned long long ust;
- unsigned long msc;
-} ustmsc_t;
-
-/*
- * li_ad1843_wait waits until lithium says the AD1843 register
- * exchange is not busy. Returns 0 on success, -EBUSY on timeout.
- *
- * Locking: must be called with lithium_lock held.
- */
-
-static int li_ad1843_wait(lithium_t *lith)
-{
- unsigned long later = jiffies + 2;
- while (li_readl(lith, LI_CODEC_COMMAND) & LI_CC_BUSY)
- if (time_after_eq(jiffies, later))
- return -EBUSY;
- return 0;
-}
-
-/*
- * li_read_ad1843_reg returns the current contents of a 16 bit AD1843 register.
- *
- * Returns unsigned register value on success, -errno on failure.
- */
-
-static int li_read_ad1843_reg(lithium_t *lith, int reg)
-{
- int val;
-
- ASSERT(!in_interrupt());
- spin_lock(&lith->lock);
- {
- val = li_ad1843_wait(lith);
- if (val == 0) {
- li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_RD | reg);
- val = li_ad1843_wait(lith);
- }
- if (val == 0)
- val = li_readl(lith, LI_CODEC_DATA);
- }
- spin_unlock(&lith->lock);
-
- DBGXV("li_read_ad1843_reg(lith=0x%p, reg=%d) returns 0x%04x\n",
- lith, reg, val);
-
- return val;
-}
-
-/*
- * li_write_ad1843_reg writes the specified value to a 16 bit AD1843 register.
- */
-
-static void li_write_ad1843_reg(lithium_t *lith, int reg, int newval)
-{
- spin_lock(&lith->lock);
- {
- if (li_ad1843_wait(lith) == 0) {
- li_writel(lith, LI_CODEC_DATA, newval);
- li_writel(lith, LI_CODEC_COMMAND, LI_CC_DIR_WR | reg);
- }
- }
- spin_unlock(&lith->lock);
-}
-
-/*
- * li_setup_dma calculates all the register settings for DMA in a particular
- * mode. It takes too many arguments.
- */
-
-static void li_setup_dma(dma_chan_t *chan,
- const dma_chan_desc_t *desc,
- lithium_t *lith,
- unsigned long buffer_paddr,
- int bufshift,
- int fragshift,
- int channels,
- int sampsize)
-{
- unsigned long mode, format;
- unsigned long size, tmask;
-
- DBGEV("(chan=0x%p, desc=0x%p, lith=0x%p, buffer_paddr=0x%lx, "
- "bufshift=%d, fragshift=%d, channels=%d, sampsize=%d)\n",
- chan, desc, lith, buffer_paddr,
- bufshift, fragshift, channels, sampsize);
-
- /* Reset the channel first. */
-
- li_writel(lith, desc->ctlreg, LI_CCTL_RESET);
-
- ASSERT(channels == 1 || channels == 2);
- if (channels == 2)
- mode = LI_CCFG_MODE_STEREO;
- else
- mode = LI_CCFG_MODE_MONO;
- ASSERT(sampsize == 1 || sampsize == 2);
- if (sampsize == 2)
- format = LI_CCFG_FMT_16BIT;
- else
- format = LI_CCFG_FMT_8BIT;
- chan->desc = desc;
- chan->lith = lith;
-
- /*
- * Lithium DMA address register takes a 40-bit physical
- * address, right-shifted by 8 so it fits in 32 bits. Bit 37
- * must be set -- it enables cache coherence.
- */
-
- ASSERT(!(buffer_paddr & 0xFF));
- chan->baseval = (buffer_paddr >> 8) | 1 << (37 - 8);
-
- chan->cfgval = ((chan->cfgval & ~LI_CCFG_LOCK) |
- SHIFT_FIELD(desc->ad1843_slot, LI_CCFG_SLOT) |
- desc->direction |
- mode |
- format);
-
- size = bufshift - 6;
- tmask = 13 - fragshift; /* See Lithium DMA Notes above. */
- ASSERT(size >= 2 && size <= 7);
- ASSERT(tmask >= 1 && tmask <= 7);
- chan->ctlval = ((chan->ctlval & ~LI_CCTL_RESET) |
- SHIFT_FIELD(size, LI_CCTL_SIZE) |
- (chan->ctlval & ~LI_CCTL_DMA_ENABLE) |
- SHIFT_FIELD(tmask, LI_CCTL_TMASK) |
- SHIFT_FIELD(0, LI_CCTL_TPTR));
-
- DBGPV("basereg 0x%x = 0x%lx\n", desc->basereg, chan->baseval);
- DBGPV("cfgreg 0x%x = 0x%lx\n", desc->cfgreg, chan->cfgval);
- DBGPV("ctlreg 0x%x = 0x%lx\n", desc->ctlreg, chan->ctlval);
-
- li_writel(lith, desc->basereg, chan->baseval);
- li_writel(lith, desc->cfgreg, chan->cfgval);
- li_writel(lith, desc->ctlreg, chan->ctlval);
-
- DBGRV();
-}
-
-static void li_shutdown_dma(dma_chan_t *chan)
-{
- lithium_t *lith = chan->lith;
- void * lith1 = lith->page1;
-
- DBGEV("(chan=0x%p)\n", chan);
-
- chan->ctlval &= ~LI_CCTL_DMA_ENABLE;
- DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval);
- li_writel(lith, chan->desc->ctlreg, chan->ctlval);
-
- /*
- * Offset 0x500 on Lithium page 1 is an undocumented,
- * unsupported register that holds the zero sample value.
- * Lithium is supposed to output zero samples when DMA is
- * inactive, and repeat the last sample when DMA underflows.
- * But it has a bug, where, after underflow occurs, the zero
- * sample is not reset.
- *
- * I expect this to break in a future rev of Lithium.
- */
-
- if (lith1 && chan->desc->direction == LI_CCFG_DIR_OUT)
- * (volatile unsigned long *) (lith1 + 0x500) = 0;
-}
-
-/*
- * li_activate_dma always starts dma at the beginning of the buffer.
- *
- * N.B., these may be called from interrupt.
- */
-
-static __inline__ void li_activate_dma(dma_chan_t *chan)
-{
- chan->ctlval |= LI_CCTL_DMA_ENABLE;
- DBGPV("ctlval = 0x%lx\n", chan->ctlval);
- li_writel(chan->lith, chan->desc->ctlreg, chan->ctlval);
-}
-
-static void li_deactivate_dma(dma_chan_t *chan)
-{
- lithium_t *lith = chan->lith;
- void * lith2 = lith->page2;
-
- chan->ctlval &= ~(LI_CCTL_DMA_ENABLE | LI_CCTL_RPTR | LI_CCTL_WPTR);
- DBGPV("ctlval = 0x%lx\n", chan->ctlval);
- DBGPV("ctlreg 0x%x = 0x%lx\n", chan->desc->ctlreg, chan->ctlval);
- li_writel(lith, chan->desc->ctlreg, chan->ctlval);
-
- /*
- * Offsets 0x98 and 0x9C on Lithium page 2 are undocumented,
- * unsupported registers that are internal copies of the DMA
- * read and write pointers. Because of a Lithium bug, these
- * registers aren't zeroed correctly when DMA is shut off. So
- * we whack them directly.
- *
- * I expect this to break in a future rev of Lithium.
- */
-
- if (lith2 && chan->desc->direction == LI_CCFG_DIR_OUT) {
- * (volatile unsigned long *) (lith2 + 0x98) = 0;
- * (volatile unsigned long *) (lith2 + 0x9C) = 0;
- }
-}
-
-/*
- * read/write the ring buffer pointers. These routines' arguments and results
- * are byte offsets from the beginning of the ring buffer.
- */
-
-static __inline__ int li_read_swptr(dma_chan_t *chan)
-{
- const unsigned long mask = chan->desc->swptrmask;
-
- return CHUNKS_TO_BYTES(UNSHIFT_FIELD(chan->ctlval, mask));
-}
-
-static __inline__ int li_read_hwptr(dma_chan_t *chan)
-{
- return CHUNKS_TO_BYTES(li_readb(chan->lith, chan->desc->hwptrreg));
-}
-
-static __inline__ void li_write_swptr(dma_chan_t *chan, int val)
-{
- const unsigned long mask = chan->desc->swptrmask;
-
- ASSERT(!(val & ~CHUNKS_TO_BYTES(0xFF)));
- val = BYTES_TO_CHUNKS(val);
- chan->ctlval = (chan->ctlval & ~mask) | SHIFT_FIELD(val, mask);
- li_writeb(chan->lith, chan->desc->swptrreg, val);
-}
-
-/* li_read_USTMSC() returns a UST/MSC pair for the given channel. */
-
-static void li_read_USTMSC(dma_chan_t *chan, ustmsc_t *ustmsc)
-{
- lithium_t *lith = chan->lith;
- const dma_chan_desc_t *desc = chan->desc;
- unsigned long now_low, now_high0, now_high1, chan_ust;
-
- spin_lock(&lith->lock);
- {
- /*
- * retry until we do all five reads without the
- * high word changing. (High word increments
- * every 2^32 microseconds, i.e., not often)
- */
- do {
- now_high0 = li_readl(lith, LI_UST_HIGH);
- now_low = li_readl(lith, LI_UST_LOW);
-
- /*
- * Lithium guarantees these two reads will be
- * atomic -- ust will not increment after msc
- * is read.
- */
-
- ustmsc->msc = li_readl(lith, desc->mscreg);
- chan_ust = li_readl(lith, desc->ustreg);
-
- now_high1 = li_readl(lith, LI_UST_HIGH);
- } while (now_high0 != now_high1);
- }
- spin_unlock(&lith->lock);
- ustmsc->ust = ((unsigned long long) now_high0 << 32 | chan_ust);
-}
-
-static void li_enable_interrupts(lithium_t *lith, unsigned int mask)
-{
- DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask);
-
- /* clear any already-pending interrupts. */
-
- li_writel(lith, LI_INTR_STATUS, mask);
-
- /* enable the interrupts. */
-
- mask |= li_readl(lith, LI_INTR_MASK);
- li_writel(lith, LI_INTR_MASK, mask);
-}
-
-static void li_disable_interrupts(lithium_t *lith, unsigned int mask)
-{
- unsigned int keepmask;
-
- DBGEV("(lith=0x%p, mask=0x%x)\n", lith, mask);
-
- /* disable the interrupts */
-
- keepmask = li_readl(lith, LI_INTR_MASK) & ~mask;
- li_writel(lith, LI_INTR_MASK, keepmask);
-
- /* clear any pending interrupts. */
-
- li_writel(lith, LI_INTR_STATUS, mask);
-}
-
-/* Get the interrupt status and clear all pending interrupts. */
-
-static unsigned int li_get_clear_intr_status(lithium_t *lith)
-{
- unsigned int status;
-
- status = li_readl(lith, LI_INTR_STATUS);
- li_writel(lith, LI_INTR_STATUS, ~0);
- return status & li_readl(lith, LI_INTR_MASK);
-}
-
-static int li_init(lithium_t *lith)
-{
- /* 1. System power supplies stabilize. */
-
- /* 2. Assert the ~RESET signal. */
-
- li_writel(lith, LI_HOST_CONTROLLER, LI_HC_RESET);
- udelay(1);
-
- /* 3. Deassert the ~RESET signal and enter a wait period to allow
- the AD1843 internal clocks and the external crystal oscillator
- to stabilize. */
-
- li_writel(lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE);
- udelay(1);
-
- return 0;
-}
-
-/*****************************************************************************/
-/* AD1843 access */
-
-/*
- * AD1843 bitfield definitions. All are named as in the AD1843 data
- * sheet, with ad1843_ prepended and individual bit numbers removed.
- *
- * E.g., bits LSS0 through LSS2 become ad1843_LSS.
- *
- * Only the bitfields we need are defined.
- */
-
-typedef struct ad1843_bitfield {
- char reg;
- char lo_bit;
- char nbits;
-} ad1843_bitfield_t;
-
-static const ad1843_bitfield_t
- ad1843_PDNO = { 0, 14, 1 }, /* Converter Power-Down Flag */
- ad1843_INIT = { 0, 15, 1 }, /* Clock Initialization Flag */
- ad1843_RIG = { 2, 0, 4 }, /* Right ADC Input Gain */
- ad1843_RMGE = { 2, 4, 1 }, /* Right ADC Mic Gain Enable */
- ad1843_RSS = { 2, 5, 3 }, /* Right ADC Source Select */
- ad1843_LIG = { 2, 8, 4 }, /* Left ADC Input Gain */
- ad1843_LMGE = { 2, 12, 1 }, /* Left ADC Mic Gain Enable */
- ad1843_LSS = { 2, 13, 3 }, /* Left ADC Source Select */
- ad1843_RX1M = { 4, 0, 5 }, /* Right Aux 1 Mix Gain/Atten */
- ad1843_RX1MM = { 4, 7, 1 }, /* Right Aux 1 Mix Mute */
- ad1843_LX1M = { 4, 8, 5 }, /* Left Aux 1 Mix Gain/Atten */
- ad1843_LX1MM = { 4, 15, 1 }, /* Left Aux 1 Mix Mute */
- ad1843_RX2M = { 5, 0, 5 }, /* Right Aux 2 Mix Gain/Atten */
- ad1843_RX2MM = { 5, 7, 1 }, /* Right Aux 2 Mix Mute */
- ad1843_LX2M = { 5, 8, 5 }, /* Left Aux 2 Mix Gain/Atten */
- ad1843_LX2MM = { 5, 15, 1 }, /* Left Aux 2 Mix Mute */
- ad1843_RMCM = { 7, 0, 5 }, /* Right Mic Mix Gain/Atten */
- ad1843_RMCMM = { 7, 7, 1 }, /* Right Mic Mix Mute */
- ad1843_LMCM = { 7, 8, 5 }, /* Left Mic Mix Gain/Atten */
- ad1843_LMCMM = { 7, 15, 1 }, /* Left Mic Mix Mute */
- ad1843_HPOS = { 8, 4, 1 }, /* Headphone Output Voltage Swing */
- ad1843_HPOM = { 8, 5, 1 }, /* Headphone Output Mute */
- ad1843_RDA1G = { 9, 0, 6 }, /* Right DAC1 Analog/Digital Gain */
- ad1843_RDA1GM = { 9, 7, 1 }, /* Right DAC1 Analog Mute */
- ad1843_LDA1G = { 9, 8, 6 }, /* Left DAC1 Analog/Digital Gain */
- ad1843_LDA1GM = { 9, 15, 1 }, /* Left DAC1 Analog Mute */
- ad1843_RDA1AM = { 11, 7, 1 }, /* Right DAC1 Digital Mute */
- ad1843_LDA1AM = { 11, 15, 1 }, /* Left DAC1 Digital Mute */
- ad1843_ADLC = { 15, 0, 2 }, /* ADC Left Sample Rate Source */
- ad1843_ADRC = { 15, 2, 2 }, /* ADC Right Sample Rate Source */
- ad1843_DA1C = { 15, 8, 2 }, /* DAC1 Sample Rate Source */
- ad1843_C1C = { 17, 0, 16 }, /* Clock 1 Sample Rate Select */
- ad1843_C2C = { 20, 0, 16 }, /* Clock 1 Sample Rate Select */
- ad1843_DAADL = { 25, 4, 2 }, /* Digital ADC Left Source Select */
- ad1843_DAADR = { 25, 6, 2 }, /* Digital ADC Right Source Select */
- ad1843_DRSFLT = { 25, 15, 1 }, /* Digital Reampler Filter Mode */
- ad1843_ADLF = { 26, 0, 2 }, /* ADC Left Channel Data Format */
- ad1843_ADRF = { 26, 2, 2 }, /* ADC Right Channel Data Format */
- ad1843_ADTLK = { 26, 4, 1 }, /* ADC Transmit Lock Mode Select */
- ad1843_SCF = { 26, 7, 1 }, /* SCLK Frequency Select */
- ad1843_DA1F = { 26, 8, 2 }, /* DAC1 Data Format Select */
- ad1843_DA1SM = { 26, 14, 1 }, /* DAC1 Stereo/Mono Mode Select */
- ad1843_ADLEN = { 27, 0, 1 }, /* ADC Left Channel Enable */
- ad1843_ADREN = { 27, 1, 1 }, /* ADC Right Channel Enable */
- ad1843_AAMEN = { 27, 4, 1 }, /* Analog to Analog Mix Enable */
- ad1843_ANAEN = { 27, 7, 1 }, /* Analog Channel Enable */
- ad1843_DA1EN = { 27, 8, 1 }, /* DAC1 Enable */
- ad1843_DA2EN = { 27, 9, 1 }, /* DAC2 Enable */
- ad1843_C1EN = { 28, 11, 1 }, /* Clock Generator 1 Enable */
- ad1843_C2EN = { 28, 12, 1 }, /* Clock Generator 2 Enable */
- ad1843_PDNI = { 28, 15, 1 }; /* Converter Power Down */
-
-/*
- * The various registers of the AD1843 use three different formats for
- * specifying gain. The ad1843_gain structure parameterizes the
- * formats.
- */
-
-typedef struct ad1843_gain {
-
- int negative; /* nonzero if gain is negative. */
- const ad1843_bitfield_t *lfield;
- const ad1843_bitfield_t *rfield;
-
-} ad1843_gain_t;
-
-static const ad1843_gain_t ad1843_gain_RECLEV
- = { 0, &ad1843_LIG, &ad1843_RIG };
-static const ad1843_gain_t ad1843_gain_LINE
- = { 1, &ad1843_LX1M, &ad1843_RX1M };
-static const ad1843_gain_t ad1843_gain_CD
- = { 1, &ad1843_LX2M, &ad1843_RX2M };
-static const ad1843_gain_t ad1843_gain_MIC
- = { 1, &ad1843_LMCM, &ad1843_RMCM };
-static const ad1843_gain_t ad1843_gain_PCM
- = { 1, &ad1843_LDA1G, &ad1843_RDA1G };
-
-/* read the current value of an AD1843 bitfield. */
-
-static int ad1843_read_bits(lithium_t *lith, const ad1843_bitfield_t *field)
-{
- int w = li_read_ad1843_reg(lith, field->reg);
- int val = w >> field->lo_bit & ((1 << field->nbits) - 1);
-
- DBGXV("ad1843_read_bits(lith=0x%p, field->{%d %d %d}) returns 0x%x\n",
- lith, field->reg, field->lo_bit, field->nbits, val);
-
- return val;
-}
-
-/*
- * write a new value to an AD1843 bitfield and return the old value.
- */
-
-static int ad1843_write_bits(lithium_t *lith,
- const ad1843_bitfield_t *field,
- int newval)
-{
- int w = li_read_ad1843_reg(lith, field->reg);
- int mask = ((1 << field->nbits) - 1) << field->lo_bit;
- int oldval = (w & mask) >> field->lo_bit;
- int newbits = (newval << field->lo_bit) & mask;
- w = (w & ~mask) | newbits;
- (void) li_write_ad1843_reg(lith, field->reg, w);
-
- DBGXV("ad1843_write_bits(lith=0x%p, field->{%d %d %d}, val=0x%x) "
- "returns 0x%x\n",
- lith, field->reg, field->lo_bit, field->nbits, newval,
- oldval);
-
- return oldval;
-}
-
-/*
- * ad1843_read_multi reads multiple bitfields from the same AD1843
- * register. It uses a single read cycle to do it. (Reading the
- * ad1843 requires 256 bit times at 12.288 MHz, or nearly 20
- * microseconds.)
- *
- * Called ike this.
- *
- * ad1843_read_multi(lith, nfields,
- * &ad1843_FIELD1, &val1,
- * &ad1843_FIELD2, &val2, ...);
- */
-
-static void ad1843_read_multi(lithium_t *lith, int argcount, ...)
-{
- va_list ap;
- const ad1843_bitfield_t *fp;
- int w = 0, mask, *value, reg = -1;
-
- va_start(ap, argcount);
- while (--argcount >= 0) {
- fp = va_arg(ap, const ad1843_bitfield_t *);
- value = va_arg(ap, int *);
- if (reg == -1) {
- reg = fp->reg;
- w = li_read_ad1843_reg(lith, reg);
- }
- ASSERT(reg == fp->reg);
- mask = (1 << fp->nbits) - 1;
- *value = w >> fp->lo_bit & mask;
- }
- va_end(ap);
-}
-
-/*
- * ad1843_write_multi stores multiple bitfields into the same AD1843
- * register. It uses one read and one write cycle to do it.
- *
- * Called like this.
- *
- * ad1843_write_multi(lith, nfields,
- * &ad1843_FIELD1, val1,
- * &ad1843_FIELF2, val2, ...);
- */
-
-static void ad1843_write_multi(lithium_t *lith, int argcount, ...)
-{
- va_list ap;
- int reg;
- const ad1843_bitfield_t *fp;
- int value;
- int w, m, mask, bits;
-
- mask = 0;
- bits = 0;
- reg = -1;
-
- va_start(ap, argcount);
- while (--argcount >= 0) {
- fp = va_arg(ap, const ad1843_bitfield_t *);
- value = va_arg(ap, int);
- if (reg == -1)
- reg = fp->reg;
- ASSERT(fp->reg == reg);
- m = ((1 << fp->nbits) - 1) << fp->lo_bit;
- mask |= m;
- bits |= (value << fp->lo_bit) & m;
- }
- va_end(ap);
- ASSERT(!(bits & ~mask));
- if (~mask & 0xFFFF)
- w = li_read_ad1843_reg(lith, reg);
- else
- w = 0;
- w = (w & ~mask) | bits;
- (void) li_write_ad1843_reg(lith, reg, w);
-}
-
-/*
- * ad1843_get_gain reads the specified register and extracts the gain value
- * using the supplied gain type. It returns the gain in OSS format.
- */
-
-static int ad1843_get_gain(lithium_t *lith, const ad1843_gain_t *gp)
-{
- int lg, rg;
- unsigned short mask = (1 << gp->lfield->nbits) - 1;
-
- ad1843_read_multi(lith, 2, gp->lfield, &lg, gp->rfield, &rg);
- if (gp->negative) {
- lg = mask - lg;
- rg = mask - rg;
- }
- lg = (lg * 100 + (mask >> 1)) / mask;
- rg = (rg * 100 + (mask >> 1)) / mask;
- return lg << 0 | rg << 8;
-}
-
-/*
- * Set an audio channel's gain. Converts from OSS format to AD1843's
- * format.
- *
- * Returns the new gain, which may be lower than the old gain.
- */
-
-static int ad1843_set_gain(lithium_t *lith,
- const ad1843_gain_t *gp,
- int newval)
-{
- unsigned short mask = (1 << gp->lfield->nbits) - 1;
-
- int lg = newval >> 0 & 0xFF;
- int rg = newval >> 8;
- if (lg < 0 || lg > 100 || rg < 0 || rg > 100)
- return -EINVAL;
- lg = (lg * mask + (mask >> 1)) / 100;
- rg = (rg * mask + (mask >> 1)) / 100;
- if (gp->negative) {
- lg = mask - lg;
- rg = mask - rg;
- }
- ad1843_write_multi(lith, 2, gp->lfield, lg, gp->rfield, rg);
- return ad1843_get_gain(lith, gp);
-}
-
-/* Returns the current recording source, in OSS format. */
-
-static int ad1843_get_recsrc(lithium_t *lith)
-{
- int ls = ad1843_read_bits(lith, &ad1843_LSS);
-
- switch (ls) {
- case 1:
- return SOUND_MASK_MIC;
- case 2:
- return SOUND_MASK_LINE;
- case 3:
- return SOUND_MASK_CD;
- case 6:
- return SOUND_MASK_PCM;
- default:
- ASSERT(0);
- return -1;
- }
-}
-
-/*
- * Enable/disable digital resample mode in the AD1843.
- *
- * The AD1843 requires that ADL, ADR, DA1 and DA2 be powered down
- * while switching modes. So we save DA1's state (DA2's state is not
- * interesting), power them down, switch into/out of resample mode,
- * power them up, and restore state.
- *
- * This will cause audible glitches if D/A or A/D is going on, so the
- * driver disallows that (in mixer_write_ioctl()).
- *
- * The open question is, is this worth doing? I'm leaving it in,
- * because it's written, but...
- */
-
-static void ad1843_set_resample_mode(lithium_t *lith, int onoff)
-{
- /* Save DA1 mute and gain (addr 9 is DA1 analog gain/attenuation) */
- int save_da1 = li_read_ad1843_reg(lith, 9);
-
- /* Power down A/D and D/A. */
- ad1843_write_multi(lith, 4,
- &ad1843_DA1EN, 0,
- &ad1843_DA2EN, 0,
- &ad1843_ADLEN, 0,
- &ad1843_ADREN, 0);
-
- /* Switch mode */
- ASSERT(onoff == 0 || onoff == 1);
- ad1843_write_bits(lith, &ad1843_DRSFLT, onoff);
-
- /* Power up A/D and D/A. */
- ad1843_write_multi(lith, 3,
- &ad1843_DA1EN, 1,
- &ad1843_ADLEN, 1,
- &ad1843_ADREN, 1);
-
- /* Restore DA1 mute and gain. */
- li_write_ad1843_reg(lith, 9, save_da1);
-}
-
-/*
- * Set recording source. Arg newsrc specifies an OSS channel mask.
- *
- * The complication is that when we switch into/out of loopback mode
- * (i.e., src = SOUND_MASK_PCM), we change the AD1843 into/out of
- * digital resampling mode.
- *
- * Returns newsrc on success, -errno on failure.
- */
-
-static int ad1843_set_recsrc(lithium_t *lith, int newsrc)
-{
- int bits;
- int oldbits;
-
- switch (newsrc) {
- case SOUND_MASK_PCM:
- bits = 6;
- break;
-
- case SOUND_MASK_MIC:
- bits = 1;
- break;
-
- case SOUND_MASK_LINE:
- bits = 2;
- break;
-
- case SOUND_MASK_CD:
- bits = 3;
- break;
-
- default:
- return -EINVAL;
- }
- oldbits = ad1843_read_bits(lith, &ad1843_LSS);
- if (newsrc == SOUND_MASK_PCM && oldbits != 6) {
- DBGP("enabling digital resample mode\n");
- ad1843_set_resample_mode(lith, 1);
- ad1843_write_multi(lith, 2,
- &ad1843_DAADL, 2,
- &ad1843_DAADR, 2);
- } else if (newsrc != SOUND_MASK_PCM && oldbits == 6) {
- DBGP("disabling digital resample mode\n");
- ad1843_set_resample_mode(lith, 0);
- ad1843_write_multi(lith, 2,
- &ad1843_DAADL, 0,
- &ad1843_DAADR, 0);
- }
- ad1843_write_multi(lith, 2, &ad1843_LSS, bits, &ad1843_RSS, bits);
- return newsrc;
-}
-
-/*
- * Return current output sources, in OSS format.
- */
-
-static int ad1843_get_outsrc(lithium_t *lith)
-{
- int pcm, line, mic, cd;
-
- pcm = ad1843_read_bits(lith, &ad1843_LDA1GM) ? 0 : SOUND_MASK_PCM;
- line = ad1843_read_bits(lith, &ad1843_LX1MM) ? 0 : SOUND_MASK_LINE;
- cd = ad1843_read_bits(lith, &ad1843_LX2MM) ? 0 : SOUND_MASK_CD;
- mic = ad1843_read_bits(lith, &ad1843_LMCMM) ? 0 : SOUND_MASK_MIC;
-
- return pcm | line | cd | mic;
-}
-
-/*
- * Set output sources. Arg is a mask of active sources in OSS format.
- *
- * Returns source mask on success, -errno on failure.
- */
-
-static int ad1843_set_outsrc(lithium_t *lith, int mask)
-{
- int pcm, line, mic, cd;
-
- if (mask & ~(SOUND_MASK_PCM | SOUND_MASK_LINE |
- SOUND_MASK_CD | SOUND_MASK_MIC))
- return -EINVAL;
- pcm = (mask & SOUND_MASK_PCM) ? 0 : 1;
- line = (mask & SOUND_MASK_LINE) ? 0 : 1;
- mic = (mask & SOUND_MASK_MIC) ? 0 : 1;
- cd = (mask & SOUND_MASK_CD) ? 0 : 1;
-
- ad1843_write_multi(lith, 2, &ad1843_LDA1GM, pcm, &ad1843_RDA1GM, pcm);
- ad1843_write_multi(lith, 2, &ad1843_LX1MM, line, &ad1843_RX1MM, line);
- ad1843_write_multi(lith, 2, &ad1843_LX2MM, cd, &ad1843_RX2MM, cd);
- ad1843_write_multi(lith, 2, &ad1843_LMCMM, mic, &ad1843_RMCMM, mic);
-
- return mask;
-}
-
-/* Setup ad1843 for D/A conversion. */
-
-static void ad1843_setup_dac(lithium_t *lith,
- int framerate,
- int fmt,
- int channels)
-{
- int ad_fmt = 0, ad_mode = 0;
-
- DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n",
- lith, framerate, fmt, channels);
-
- switch (fmt) {
- case AFMT_S8: ad_fmt = 1; break;
- case AFMT_U8: ad_fmt = 1; break;
- case AFMT_S16_LE: ad_fmt = 1; break;
- case AFMT_MU_LAW: ad_fmt = 2; break;
- case AFMT_A_LAW: ad_fmt = 3; break;
- default: ASSERT(0);
- }
-
- switch (channels) {
- case 2: ad_mode = 0; break;
- case 1: ad_mode = 1; break;
- default: ASSERT(0);
- }
-
- DBGPV("ad_mode = %d, ad_fmt = %d\n", ad_mode, ad_fmt);
- ASSERT(framerate >= 4000 && framerate <= 49000);
- ad1843_write_bits(lith, &ad1843_C1C, framerate);
- ad1843_write_multi(lith, 2,
- &ad1843_DA1SM, ad_mode, &ad1843_DA1F, ad_fmt);
-}
-
-static void ad1843_shutdown_dac(lithium_t *lith)
-{
- ad1843_write_bits(lith, &ad1843_DA1F, 1);
-}
-
-static void ad1843_setup_adc(lithium_t *lith, int framerate, int fmt, int channels)
-{
- int da_fmt = 0;
-
- DBGEV("(lith=0x%p, framerate=%d, fmt=%d, channels=%d)\n",
- lith, framerate, fmt, channels);
-
- switch (fmt) {
- case AFMT_S8: da_fmt = 1; break;
- case AFMT_U8: da_fmt = 1; break;
- case AFMT_S16_LE: da_fmt = 1; break;
- case AFMT_MU_LAW: da_fmt = 2; break;
- case AFMT_A_LAW: da_fmt = 3; break;
- default: ASSERT(0);
- }
-
- DBGPV("da_fmt = %d\n", da_fmt);
- ASSERT(framerate >= 4000 && framerate <= 49000);
- ad1843_write_bits(lith, &ad1843_C2C, framerate);
- ad1843_write_multi(lith, 2,
- &ad1843_ADLF, da_fmt, &ad1843_ADRF, da_fmt);
-}
-
-static void ad1843_shutdown_adc(lithium_t *lith)
-{
- /* nothing to do */
-}
-
-/*
- * Fully initialize the ad1843. As described in the AD1843 data
- * sheet, section "START-UP SEQUENCE". The numbered comments are
- * subsection headings from the data sheet. See the data sheet, pages
- * 52-54, for more info.
- *
- * return 0 on success, -errno on failure. */
-
-static int __init ad1843_init(lithium_t *lith)
-{
- unsigned long later;
- int err;
-
- err = li_init(lith);
- if (err)
- return err;
-
- if (ad1843_read_bits(lith, &ad1843_INIT) != 0) {
- printk(KERN_ERR "vwsnd sound: AD1843 won't initialize\n");
- return -EIO;
- }
-
- ad1843_write_bits(lith, &ad1843_SCF, 1);
-
- /* 4. Put the conversion resources into standby. */
-
- ad1843_write_bits(lith, &ad1843_PDNI, 0);
- later = jiffies + HZ / 2; /* roughly half a second */
- DBGDO(shut_up++);
- while (ad1843_read_bits(lith, &ad1843_PDNO)) {
- if (time_after(jiffies, later)) {
- printk(KERN_ERR
- "vwsnd audio: AD1843 won't power up\n");
- return -EIO;
- }
- schedule();
- }
- DBGDO(shut_up--);
-
- /* 5. Power up the clock generators and enable clock output pins. */
-
- ad1843_write_multi(lith, 2, &ad1843_C1EN, 1, &ad1843_C2EN, 1);
-
- /* 6. Configure conversion resources while they are in standby. */
-
- /* DAC1 uses clock 1 as source, ADC uses clock 2. Always. */
-
- ad1843_write_multi(lith, 3,
- &ad1843_DA1C, 1,
- &ad1843_ADLC, 2,
- &ad1843_ADRC, 2);
-
- /* 7. Enable conversion resources. */
-
- ad1843_write_bits(lith, &ad1843_ADTLK, 1);
- ad1843_write_multi(lith, 5,
- &ad1843_ANAEN, 1,
- &ad1843_AAMEN, 1,
- &ad1843_DA1EN, 1,
- &ad1843_ADLEN, 1,
- &ad1843_ADREN, 1);
-
- /* 8. Configure conversion resources while they are enabled. */
-
- ad1843_write_bits(lith, &ad1843_DA1C, 1);
-
- /* Unmute all channels. */
-
- ad1843_set_outsrc(lith,
- (SOUND_MASK_PCM | SOUND_MASK_LINE |
- SOUND_MASK_MIC | SOUND_MASK_CD));
- ad1843_write_multi(lith, 2, &ad1843_LDA1AM, 0, &ad1843_RDA1AM, 0);
-
- /* Set default recording source to Line In and set
- * mic gain to +20 dB.
- */
-
- ad1843_set_recsrc(lith, SOUND_MASK_LINE);
- ad1843_write_multi(lith, 2, &ad1843_LMGE, 1, &ad1843_RMGE, 1);
-
- /* Set Speaker Out level to +/- 4V and unmute it. */
-
- ad1843_write_multi(lith, 2, &ad1843_HPOS, 1, &ad1843_HPOM, 0);
-
- return 0;
-}
-
-/*****************************************************************************/
-/* PCM I/O */
-
-#define READ_INTR_MASK (LI_INTR_COMM1_TRIG | LI_INTR_COMM1_OVERFLOW)
-#define WRITE_INTR_MASK (LI_INTR_COMM2_TRIG | LI_INTR_COMM2_UNDERFLOW)
-
-typedef enum vwsnd_port_swstate { /* software state */
- SW_OFF,
- SW_INITIAL,
- SW_RUN,
- SW_DRAIN,
-} vwsnd_port_swstate_t;
-
-typedef enum vwsnd_port_hwstate { /* hardware state */
- HW_STOPPED,
- HW_RUNNING,
-} vwsnd_port_hwstate_t;
-
-/*
- * These flags are read by ISR, but only written at baseline.
- */
-
-typedef enum vwsnd_port_flags {
- DISABLED = 1 << 0,
- ERFLOWN = 1 << 1, /* overflown or underflown */
- HW_BUSY = 1 << 2,
-} vwsnd_port_flags_t;
-
-/*
- * vwsnd_port is the per-port data structure. Each device has two
- * ports, one for input and one for output.
- *
- * Locking:
- *
- * port->lock protects: hwstate, flags, swb_[iu]_avail.
- *
- * devc->io_mutex protects: swstate, sw_*, swb_[iu]_idx.
- *
- * everything else is only written by open/release or
- * pcm_{setup,shutdown}(), which are serialized by a
- * combination of devc->open_mutex and devc->io_mutex.
- */
-
-typedef struct vwsnd_port {
-
- spinlock_t lock;
- wait_queue_head_t queue;
- vwsnd_port_swstate_t swstate;
- vwsnd_port_hwstate_t hwstate;
- vwsnd_port_flags_t flags;
-
- int sw_channels;
- int sw_samplefmt;
- int sw_framerate;
- int sample_size;
- int frame_size;
- unsigned int zero_word; /* zero for the sample format */
-
- int sw_fragshift;
- int sw_fragcount;
- int sw_subdivshift;
-
- unsigned int hw_fragshift;
- unsigned int hw_fragsize;
- unsigned int hw_fragcount;
-
- int hwbuf_size;
- unsigned long hwbuf_paddr;
- unsigned long hwbuf_vaddr;
- void * hwbuf; /* hwbuf == hwbuf_vaddr */
- int hwbuf_max; /* max bytes to preload */
-
- void * swbuf;
- unsigned int swbuf_size; /* size in bytes */
- unsigned int swb_u_idx; /* index of next user byte */
- unsigned int swb_i_idx; /* index of next intr byte */
- unsigned int swb_u_avail; /* # bytes avail to user */
- unsigned int swb_i_avail; /* # bytes avail to intr */
-
- dma_chan_t chan;
-
- /* Accounting */
-
- int byte_count;
- int frag_count;
- int MSC_offset;
-
-} vwsnd_port_t;
-
-/* vwsnd_dev is the per-device data structure. */
-
-typedef struct vwsnd_dev {
- struct vwsnd_dev *next_dev;
- int audio_minor; /* minor number of audio device */
- int mixer_minor; /* minor number of mixer device */
-
- struct mutex open_mutex;
- struct mutex io_mutex;
- struct mutex mix_mutex;
- fmode_t open_mode;
- wait_queue_head_t open_wait;
-
- lithium_t lith;
-
- vwsnd_port_t rport;
- vwsnd_port_t wport;
-} vwsnd_dev_t;
-
-static vwsnd_dev_t *vwsnd_dev_list; /* linked list of all devices */
-
-static atomic_t vwsnd_use_count = ATOMIC_INIT(0);
-
-# define INC_USE_COUNT (atomic_inc(&vwsnd_use_count))
-# define DEC_USE_COUNT (atomic_dec(&vwsnd_use_count))
-# define IN_USE (atomic_read(&vwsnd_use_count) != 0)
-
-/*
- * Lithium can only DMA multiples of 32 bytes. Its DMA buffer may
- * be up to 8 Kb. This driver always uses 8 Kb.
- *
- * Memory bug workaround -- I'm not sure what's going on here, but
- * somehow pcm_copy_out() was triggering segv's going on to the next
- * page of the hw buffer. So, I make the hw buffer one size bigger
- * than we actually use. That way, the following page is allocated
- * and mapped, and no error. I suspect that something is broken
- * in Cobalt, but haven't really investigated. HBO is the actual
- * size of the buffer, and HWBUF_ORDER is what we allocate.
- */
-
-#define HWBUF_SHIFT 13
-#define HWBUF_SIZE (1 << HWBUF_SHIFT)
-# define HBO (HWBUF_SHIFT > PAGE_SHIFT ? HWBUF_SHIFT - PAGE_SHIFT : 0)
-# define HWBUF_ORDER (HBO + 1) /* next size bigger */
-#define MIN_SPEED 4000
-#define MAX_SPEED 49000
-
-#define MIN_FRAGSHIFT (DMACHUNK_SHIFT + 1)
-#define MAX_FRAGSHIFT (PAGE_SHIFT)
-#define MIN_FRAGSIZE (1 << MIN_FRAGSHIFT)
-#define MAX_FRAGSIZE (1 << MAX_FRAGSHIFT)
-#define MIN_FRAGCOUNT(fragsize) 3
-#define MAX_FRAGCOUNT(fragsize) (32 * PAGE_SIZE / (fragsize))
-#define DEFAULT_FRAGSHIFT 12
-#define DEFAULT_FRAGCOUNT 16
-#define DEFAULT_SUBDIVSHIFT 0
-
-/*
- * The software buffer (swbuf) is a ring buffer shared between user
- * level and interrupt level. Each level owns some of the bytes in
- * the buffer, and may give bytes away by calling swb_inc_{u,i}().
- * User level calls _u for user, and interrupt level calls _i for
- * interrupt.
- *
- * port->swb_{u,i}_avail is the number of bytes available to that level.
- *
- * port->swb_{u,i}_idx is the index of the first available byte in the
- * buffer.
- *
- * Each level calls swb_inc_{u,i}() to atomically increment its index,
- * recalculate the number of bytes available for both sides, and
- * return the number of bytes available. Since each side can only
- * give away bytes, the other side can only increase the number of
- * bytes available to this side. Each side updates its own index
- * variable, swb_{u,i}_idx, so no lock is needed to read it.
- *
- * To query the number of bytes available, call swb_inc_{u,i} with an
- * increment of zero.
- */
-
-static __inline__ unsigned int __swb_inc_u(vwsnd_port_t *port, int inc)
-{
- if (inc) {
- port->swb_u_idx += inc;
- port->swb_u_idx %= port->swbuf_size;
- port->swb_u_avail -= inc;
- port->swb_i_avail += inc;
- }
- return port->swb_u_avail;
-}
-
-static __inline__ unsigned int swb_inc_u(vwsnd_port_t *port, int inc)
-{
- unsigned long flags;
- unsigned int ret;
-
- spin_lock_irqsave(&port->lock, flags);
- {
- ret = __swb_inc_u(port, inc);
- }
- spin_unlock_irqrestore(&port->lock, flags);
- return ret;
-}
-
-static __inline__ unsigned int __swb_inc_i(vwsnd_port_t *port, int inc)
-{
- if (inc) {
- port->swb_i_idx += inc;
- port->swb_i_idx %= port->swbuf_size;
- port->swb_i_avail -= inc;
- port->swb_u_avail += inc;
- }
- return port->swb_i_avail;
-}
-
-static __inline__ unsigned int swb_inc_i(vwsnd_port_t *port, int inc)
-{
- unsigned long flags;
- unsigned int ret;
-
- spin_lock_irqsave(&port->lock, flags);
- {
- ret = __swb_inc_i(port, inc);
- }
- spin_unlock_irqrestore(&port->lock, flags);
- return ret;
-}
-
-/*
- * pcm_setup - this routine initializes all port state after
- * mode-setting ioctls have been done, but before the first I/O is
- * done.
- *
- * Locking: called with devc->io_mutex held.
- *
- * Returns 0 on success, -errno on failure.
- */
-
-static int pcm_setup(vwsnd_dev_t *devc,
- vwsnd_port_t *rport,
- vwsnd_port_t *wport)
-{
- vwsnd_port_t *aport = rport ? rport : wport;
- int sample_size;
- unsigned int zero_word;
-
- DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport);
-
- ASSERT(aport != NULL);
- if (aport->swbuf != NULL)
- return 0;
- switch (aport->sw_samplefmt) {
- case AFMT_MU_LAW:
- sample_size = 1;
- zero_word = 0xFFFFFFFF ^ 0x80808080;
- break;
-
- case AFMT_A_LAW:
- sample_size = 1;
- zero_word = 0xD5D5D5D5 ^ 0x80808080;
- break;
-
- case AFMT_U8:
- sample_size = 1;
- zero_word = 0x80808080;
- break;
-
- case AFMT_S8:
- sample_size = 1;
- zero_word = 0x00000000;
- break;
-
- case AFMT_S16_LE:
- sample_size = 2;
- zero_word = 0x00000000;
- break;
-
- default:
- sample_size = 0; /* prevent compiler warning */
- zero_word = 0;
- ASSERT(0);
- }
- aport->sample_size = sample_size;
- aport->zero_word = zero_word;
- aport->frame_size = aport->sw_channels * aport->sample_size;
- aport->hw_fragshift = aport->sw_fragshift - aport->sw_subdivshift;
- aport->hw_fragsize = 1 << aport->hw_fragshift;
- aport->hw_fragcount = aport->sw_fragcount << aport->sw_subdivshift;
- ASSERT(aport->hw_fragsize >= MIN_FRAGSIZE);
- ASSERT(aport->hw_fragsize <= MAX_FRAGSIZE);
- ASSERT(aport->hw_fragcount >= MIN_FRAGCOUNT(aport->hw_fragsize));
- ASSERT(aport->hw_fragcount <= MAX_FRAGCOUNT(aport->hw_fragsize));
- if (rport) {
- int hwfrags, swfrags;
- rport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE;
- hwfrags = rport->hwbuf_max >> aport->hw_fragshift;
- swfrags = aport->hw_fragcount - hwfrags;
- if (swfrags < 2)
- swfrags = 2;
- rport->swbuf_size = swfrags * aport->hw_fragsize;
- DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags);
- DBGPV("read hwbuf_max = %d, swbuf_size = %d\n",
- rport->hwbuf_max, rport->swbuf_size);
- }
- if (wport) {
- int hwfrags, swfrags;
- int total_bytes = aport->hw_fragcount * aport->hw_fragsize;
- wport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE;
- if (wport->hwbuf_max > total_bytes)
- wport->hwbuf_max = total_bytes;
- hwfrags = wport->hwbuf_max >> aport->hw_fragshift;
- DBGPV("hwfrags = %d\n", hwfrags);
- swfrags = aport->hw_fragcount - hwfrags;
- if (swfrags < 2)
- swfrags = 2;
- wport->swbuf_size = swfrags * aport->hw_fragsize;
- DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags);
- DBGPV("write hwbuf_max = %d, swbuf_size = %d\n",
- wport->hwbuf_max, wport->swbuf_size);
- }
-
- aport->swb_u_idx = 0;
- aport->swb_i_idx = 0;
- aport->byte_count = 0;
-
- /*
- * Is this a Cobalt bug? We need to make this buffer extend
- * one page further than we actually use -- somehow memcpy
- * causes an exceptoin otherwise. I suspect there's a bug in
- * Cobalt (or somewhere) where it's generating a fault on a
- * speculative load or something. Obviously, I haven't taken
- * the time to track it down.
- */
-
- aport->swbuf = vmalloc(aport->swbuf_size + PAGE_SIZE);
- if (!aport->swbuf)
- return -ENOMEM;
- if (rport && wport) {
- ASSERT(aport == rport);
- ASSERT(wport->swbuf == NULL);
- /* One extra page - see comment above. */
- wport->swbuf = vmalloc(aport->swbuf_size + PAGE_SIZE);
- if (!wport->swbuf) {
- vfree(aport->swbuf);
- aport->swbuf = NULL;
- return -ENOMEM;
- }
- wport->sample_size = rport->sample_size;
- wport->zero_word = rport->zero_word;
- wport->frame_size = rport->frame_size;
- wport->hw_fragshift = rport->hw_fragshift;
- wport->hw_fragsize = rport->hw_fragsize;
- wport->hw_fragcount = rport->hw_fragcount;
- wport->swbuf_size = rport->swbuf_size;
- wport->hwbuf_max = rport->hwbuf_max;
- wport->swb_u_idx = rport->swb_u_idx;
- wport->swb_i_idx = rport->swb_i_idx;
- wport->byte_count = rport->byte_count;
- }
- if (rport) {
- rport->swb_u_avail = 0;
- rport->swb_i_avail = rport->swbuf_size;
- rport->swstate = SW_RUN;
- li_setup_dma(&rport->chan,
- &li_comm1,
- &devc->lith,
- rport->hwbuf_paddr,
- HWBUF_SHIFT,
- rport->hw_fragshift,
- rport->sw_channels,
- rport->sample_size);
- ad1843_setup_adc(&devc->lith,
- rport->sw_framerate,
- rport->sw_samplefmt,
- rport->sw_channels);
- li_enable_interrupts(&devc->lith, READ_INTR_MASK);
- if (!(rport->flags & DISABLED)) {
- ustmsc_t ustmsc;
- rport->hwstate = HW_RUNNING;
- li_activate_dma(&rport->chan);
- li_read_USTMSC(&rport->chan, &ustmsc);
- rport->MSC_offset = ustmsc.msc;
- }
- }
- if (wport) {
- if (wport->hwbuf_max > wport->swbuf_size)
- wport->hwbuf_max = wport->swbuf_size;
- wport->flags &= ~ERFLOWN;
- wport->swb_u_avail = wport->swbuf_size;
- wport->swb_i_avail = 0;
- wport->swstate = SW_RUN;
- li_setup_dma(&wport->chan,
- &li_comm2,
- &devc->lith,
- wport->hwbuf_paddr,
- HWBUF_SHIFT,
- wport->hw_fragshift,
- wport->sw_channels,
- wport->sample_size);
- ad1843_setup_dac(&devc->lith,
- wport->sw_framerate,
- wport->sw_samplefmt,
- wport->sw_channels);
- li_enable_interrupts(&devc->lith, WRITE_INTR_MASK);
- }
- DBGRV();
- return 0;
-}
-
-/*
- * pcm_shutdown_port - shut down one port (direction) for PCM I/O.
- * Only called from pcm_shutdown.
- */
-
-static void pcm_shutdown_port(vwsnd_dev_t *devc,
- vwsnd_port_t *aport,
- unsigned int mask)
-{
- unsigned long flags;
- vwsnd_port_hwstate_t hwstate;
- DECLARE_WAITQUEUE(wait, current);
-
- aport->swstate = SW_INITIAL;
- add_wait_queue(&aport->queue, &wait);
- while (1) {
- set_current_state(TASK_UNINTERRUPTIBLE);
- spin_lock_irqsave(&aport->lock, flags);
- {
- hwstate = aport->hwstate;
- }
- spin_unlock_irqrestore(&aport->lock, flags);
- if (hwstate == HW_STOPPED)
- break;
- schedule();
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&aport->queue, &wait);
- li_disable_interrupts(&devc->lith, mask);
- if (aport == &devc->rport)
- ad1843_shutdown_adc(&devc->lith);
- else /* aport == &devc->wport) */
- ad1843_shutdown_dac(&devc->lith);
- li_shutdown_dma(&aport->chan);
- vfree(aport->swbuf);
- aport->swbuf = NULL;
- aport->byte_count = 0;
-}
-
-/*
- * pcm_shutdown undoes what pcm_setup did.
- * Also sets the ports' swstate to newstate.
- */
-
-static void pcm_shutdown(vwsnd_dev_t *devc,
- vwsnd_port_t *rport,
- vwsnd_port_t *wport)
-{
- DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport);
-
- if (rport && rport->swbuf) {
- DBGPV("shutting down rport\n");
- pcm_shutdown_port(devc, rport, READ_INTR_MASK);
- }
- if (wport && wport->swbuf) {
- DBGPV("shutting down wport\n");
- pcm_shutdown_port(devc, wport, WRITE_INTR_MASK);
- }
- DBGRV();
-}
-
-static void pcm_copy_in(vwsnd_port_t *rport, int swidx, int hwidx, int nb)
-{
- char *src = rport->hwbuf + hwidx;
- char *dst = rport->swbuf + swidx;
- int fmt = rport->sw_samplefmt;
-
- DBGPV("swidx = %d, hwidx = %d\n", swidx, hwidx);
- ASSERT(rport->hwbuf != NULL);
- ASSERT(rport->swbuf != NULL);
- ASSERT(nb > 0 && (nb % 32) == 0);
- ASSERT(swidx % 32 == 0 && hwidx % 32 == 0);
- ASSERT(swidx >= 0 && swidx + nb <= rport->swbuf_size);
- ASSERT(hwidx >= 0 && hwidx + nb <= rport->hwbuf_size);
-
- if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) {
-
- /* See Sample Format Notes above. */
-
- char *end = src + nb;
- while (src < end)
- *dst++ = *src++ ^ 0x80;
- } else
- memcpy(dst, src, nb);
-}
-
-static void pcm_copy_out(vwsnd_port_t *wport, int swidx, int hwidx, int nb)
-{
- char *src = wport->swbuf + swidx;
- char *dst = wport->hwbuf + hwidx;
- int fmt = wport->sw_samplefmt;
-
- ASSERT(nb > 0 && (nb % 32) == 0);
- ASSERT(wport->hwbuf != NULL);
- ASSERT(wport->swbuf != NULL);
- ASSERT(swidx % 32 == 0 && hwidx % 32 == 0);
- ASSERT(swidx >= 0 && swidx + nb <= wport->swbuf_size);
- ASSERT(hwidx >= 0 && hwidx + nb <= wport->hwbuf_size);
- if (fmt == AFMT_MU_LAW || fmt == AFMT_A_LAW || fmt == AFMT_S8) {
-
- /* See Sample Format Notes above. */
-
- char *end = src + nb;
- while (src < end)
- *dst++ = *src++ ^ 0x80;
- } else
- memcpy(dst, src, nb);
-}
-
-/*
- * pcm_output() is called both from baselevel and from interrupt level.
- * This is where audio frames are copied into the hardware-accessible
- * ring buffer.
- *
- * Locking note: The part of this routine that figures out what to do
- * holds wport->lock. The longer part releases wport->lock, but sets
- * wport->flags & HW_BUSY. Afterward, it reacquires wport->lock, and
- * checks for more work to do.
- *
- * If another thread calls pcm_output() while HW_BUSY is set, it
- * returns immediately, knowing that the thread that set HW_BUSY will
- * look for more work to do before returning.
- *
- * This has the advantage that port->lock is held for several short
- * periods instead of one long period. Also, when pcm_output is
- * called from base level, it reenables interrupts.
- */
-
-static void pcm_output(vwsnd_dev_t *devc, int erflown, int nb)
-{
- vwsnd_port_t *wport = &devc->wport;
- const int hwmax = wport->hwbuf_max;
- const int hwsize = wport->hwbuf_size;
- const int swsize = wport->swbuf_size;
- const int fragsize = wport->hw_fragsize;
- unsigned long iflags;
-
- DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb);
- spin_lock_irqsave(&wport->lock, iflags);
- if (erflown)
- wport->flags |= ERFLOWN;
- (void) __swb_inc_u(wport, nb);
- if (wport->flags & HW_BUSY) {
- spin_unlock_irqrestore(&wport->lock, iflags);
- DBGPV("returning: HW BUSY\n");
- return;
- }
- if (wport->flags & DISABLED) {
- spin_unlock_irqrestore(&wport->lock, iflags);
- DBGPV("returning: DISABLED\n");
- return;
- }
- wport->flags |= HW_BUSY;
- while (1) {
- int swptr, hwptr, hw_avail, sw_avail, swidx;
- vwsnd_port_hwstate_t hwstate = wport->hwstate;
- vwsnd_port_swstate_t swstate = wport->swstate;
- int hw_unavail;
- ustmsc_t ustmsc;
-
- hwptr = li_read_hwptr(&wport->chan);
- swptr = li_read_swptr(&wport->chan);
- hw_unavail = (swptr - hwptr + hwsize) % hwsize;
- hw_avail = (hwmax - hw_unavail) & -fragsize;
- sw_avail = wport->swb_i_avail & -fragsize;
- if (sw_avail && swstate == SW_RUN) {
- if (wport->flags & ERFLOWN) {
- wport->flags &= ~ERFLOWN;
- }
- } else if (swstate == SW_INITIAL ||
- swstate == SW_OFF ||
- (swstate == SW_DRAIN &&
- !sw_avail &&
- (wport->flags & ERFLOWN))) {
- DBGP("stopping. hwstate = %d\n", hwstate);
- if (hwstate != HW_STOPPED) {
- li_deactivate_dma(&wport->chan);
- wport->hwstate = HW_STOPPED;
- }
- wake_up(&wport->queue);
- break;
- }
- if (!sw_avail || !hw_avail)
- break;
- spin_unlock_irqrestore(&wport->lock, iflags);
-
- /*
- * We gave up the port lock, but we have the HW_BUSY flag.
- * Proceed without accessing any nonlocal state.
- * Do not exit the loop -- must check for more work.
- */
-
- swidx = wport->swb_i_idx;
- nb = hw_avail;
- if (nb > sw_avail)
- nb = sw_avail;
- if (nb > hwsize - swptr)
- nb = hwsize - swptr; /* don't overflow hwbuf */
- if (nb > swsize - swidx)
- nb = swsize - swidx; /* don't overflow swbuf */
- ASSERT(nb > 0);
- if (nb % fragsize) {
- DBGP("nb = %d, fragsize = %d\n", nb, fragsize);
- DBGP("hw_avail = %d\n", hw_avail);
- DBGP("sw_avail = %d\n", sw_avail);
- DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr);
- DBGP("swsize = %d, swidx = %d\n", swsize, swidx);
- }
- ASSERT(!(nb % fragsize));
- DBGPV("copying swb[%d..%d] to hwb[%d..%d]\n",
- swidx, swidx + nb, swptr, swptr + nb);
- pcm_copy_out(wport, swidx, swptr, nb);
- li_write_swptr(&wport->chan, (swptr + nb) % hwsize);
- spin_lock_irqsave(&wport->lock, iflags);
- if (hwstate == HW_STOPPED) {
- DBGPV("starting\n");
- li_activate_dma(&wport->chan);
- wport->hwstate = HW_RUNNING;
- li_read_USTMSC(&wport->chan, &ustmsc);
- ASSERT(wport->byte_count % wport->frame_size == 0);
- wport->MSC_offset = ustmsc.msc - wport->byte_count / wport->frame_size;
- }
- __swb_inc_i(wport, nb);
- wport->byte_count += nb;
- wport->frag_count += nb / fragsize;
- ASSERT(nb % fragsize == 0);
- wake_up(&wport->queue);
- }
- wport->flags &= ~HW_BUSY;
- spin_unlock_irqrestore(&wport->lock, iflags);
- DBGRV();
-}
-
-/*
- * pcm_input() is called both from baselevel and from interrupt level.
- * This is where audio frames are copied out of the hardware-accessible
- * ring buffer.
- *
- * Locking note: The part of this routine that figures out what to do
- * holds rport->lock. The longer part releases rport->lock, but sets
- * rport->flags & HW_BUSY. Afterward, it reacquires rport->lock, and
- * checks for more work to do.
- *
- * If another thread calls pcm_input() while HW_BUSY is set, it
- * returns immediately, knowing that the thread that set HW_BUSY will
- * look for more work to do before returning.
- *
- * This has the advantage that port->lock is held for several short
- * periods instead of one long period. Also, when pcm_input is
- * called from base level, it reenables interrupts.
- */
-
-static void pcm_input(vwsnd_dev_t *devc, int erflown, int nb)
-{
- vwsnd_port_t *rport = &devc->rport;
- const int hwmax = rport->hwbuf_max;
- const int hwsize = rport->hwbuf_size;
- const int swsize = rport->swbuf_size;
- const int fragsize = rport->hw_fragsize;
- unsigned long iflags;
-
- DBGEV("(devc=0x%p, erflown=%d, nb=%d)\n", devc, erflown, nb);
-
- spin_lock_irqsave(&rport->lock, iflags);
- if (erflown)
- rport->flags |= ERFLOWN;
- (void) __swb_inc_u(rport, nb);
- if (rport->flags & HW_BUSY || !rport->swbuf) {
- spin_unlock_irqrestore(&rport->lock, iflags);
- DBGPV("returning: HW BUSY or !swbuf\n");
- return;
- }
- if (rport->flags & DISABLED) {
- spin_unlock_irqrestore(&rport->lock, iflags);
- DBGPV("returning: DISABLED\n");
- return;
- }
- rport->flags |= HW_BUSY;
- while (1) {
- int swptr, hwptr, hw_avail, sw_avail, swidx;
- vwsnd_port_hwstate_t hwstate = rport->hwstate;
- vwsnd_port_swstate_t swstate = rport->swstate;
-
- hwptr = li_read_hwptr(&rport->chan);
- swptr = li_read_swptr(&rport->chan);
- hw_avail = (hwptr - swptr + hwsize) % hwsize & -fragsize;
- if (hw_avail > hwmax)
- hw_avail = hwmax;
- sw_avail = rport->swb_i_avail & -fragsize;
- if (swstate != SW_RUN) {
- DBGP("stopping. hwstate = %d\n", hwstate);
- if (hwstate != HW_STOPPED) {
- li_deactivate_dma(&rport->chan);
- rport->hwstate = HW_STOPPED;
- }
- wake_up(&rport->queue);
- break;
- }
- if (!sw_avail || !hw_avail)
- break;
- spin_unlock_irqrestore(&rport->lock, iflags);
-
- /*
- * We gave up the port lock, but we have the HW_BUSY flag.
- * Proceed without accessing any nonlocal state.
- * Do not exit the loop -- must check for more work.
- */
-
- swidx = rport->swb_i_idx;
- nb = hw_avail;
- if (nb > sw_avail)
- nb = sw_avail;
- if (nb > hwsize - swptr)
- nb = hwsize - swptr; /* don't overflow hwbuf */
- if (nb > swsize - swidx)
- nb = swsize - swidx; /* don't overflow swbuf */
- ASSERT(nb > 0);
- if (nb % fragsize) {
- DBGP("nb = %d, fragsize = %d\n", nb, fragsize);
- DBGP("hw_avail = %d\n", hw_avail);
- DBGP("sw_avail = %d\n", sw_avail);
- DBGP("hwsize = %d, swptr = %d\n", hwsize, swptr);
- DBGP("swsize = %d, swidx = %d\n", swsize, swidx);
- }
- ASSERT(!(nb % fragsize));
- DBGPV("copying hwb[%d..%d] to swb[%d..%d]\n",
- swptr, swptr + nb, swidx, swidx + nb);
- pcm_copy_in(rport, swidx, swptr, nb);
- li_write_swptr(&rport->chan, (swptr + nb) % hwsize);
- spin_lock_irqsave(&rport->lock, iflags);
- __swb_inc_i(rport, nb);
- rport->byte_count += nb;
- rport->frag_count += nb / fragsize;
- ASSERT(nb % fragsize == 0);
- wake_up(&rport->queue);
- }
- rport->flags &= ~HW_BUSY;
- spin_unlock_irqrestore(&rport->lock, iflags);
- DBGRV();
-}
-
-/*
- * pcm_flush_frag() writes zero samples to fill the current fragment,
- * then flushes it to the hardware.
- *
- * It is only meaningful to flush output, not input.
- */
-
-static void pcm_flush_frag(vwsnd_dev_t *devc)
-{
- vwsnd_port_t *wport = &devc->wport;
-
- DBGPV("swstate = %d\n", wport->swstate);
- if (wport->swstate == SW_RUN) {
- int idx = wport->swb_u_idx;
- int end = (idx + wport->hw_fragsize - 1)
- >> wport->hw_fragshift
- << wport->hw_fragshift;
- int nb = end - idx;
- DBGPV("clearing %d bytes\n", nb);
- if (nb)
- memset(wport->swbuf + idx,
- (char) wport->zero_word,
- nb);
- wport->swstate = SW_DRAIN;
- pcm_output(devc, 0, nb);
- }
- DBGRV();
-}
-
-/*
- * Wait for output to drain. This sleeps uninterruptibly because
- * there is nothing intelligent we can do if interrupted. This
- * means the process will be delayed in responding to the signal.
- */
-
-static void pcm_write_sync(vwsnd_dev_t *devc)
-{
- vwsnd_port_t *wport = &devc->wport;
- DECLARE_WAITQUEUE(wait, current);
- unsigned long flags;
- vwsnd_port_hwstate_t hwstate;
-
- DBGEV("(devc=0x%p)\n", devc);
- add_wait_queue(&wport->queue, &wait);
- while (1) {
- set_current_state(TASK_UNINTERRUPTIBLE);
- spin_lock_irqsave(&wport->lock, flags);
- {
- hwstate = wport->hwstate;
- }
- spin_unlock_irqrestore(&wport->lock, flags);
- if (hwstate == HW_STOPPED)
- break;
- schedule();
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&wport->queue, &wait);
- DBGPV("swstate = %d, hwstate = %d\n", wport->swstate, wport->hwstate);
- DBGRV();
-}
-
-/*****************************************************************************/
-/* audio driver */
-
-/*
- * seek on an audio device always fails.
- */
-
-static void vwsnd_audio_read_intr(vwsnd_dev_t *devc, unsigned int status)
-{
- int overflown = status & LI_INTR_COMM1_OVERFLOW;
-
- if (status & READ_INTR_MASK)
- pcm_input(devc, overflown, 0);
-}
-
-static void vwsnd_audio_write_intr(vwsnd_dev_t *devc, unsigned int status)
-{
- int underflown = status & LI_INTR_COMM2_UNDERFLOW;
-
- if (status & WRITE_INTR_MASK)
- pcm_output(devc, underflown, 0);
-}
-
-static irqreturn_t vwsnd_audio_intr(int irq, void *dev_id)
-{
- vwsnd_dev_t *devc = dev_id;
- unsigned int status;
-
- DBGEV("(irq=%d, dev_id=0x%p)\n", irq, dev_id);
-
- status = li_get_clear_intr_status(&devc->lith);
- vwsnd_audio_read_intr(devc, status);
- vwsnd_audio_write_intr(devc, status);
- return IRQ_HANDLED;
-}
-
-static ssize_t vwsnd_audio_do_read(struct file *file,
- char *buffer,
- size_t count,
- loff_t *ppos)
-{
- vwsnd_dev_t *devc = file->private_data;
- vwsnd_port_t *rport = ((file->f_mode & FMODE_READ) ?
- &devc->rport : NULL);
- int ret, nb;
-
- DBGEV("(file=0x%p, buffer=0x%p, count=%d, ppos=0x%p)\n",
- file, buffer, count, ppos);
-
- if (!rport)
- return -EINVAL;
-
- if (rport->swbuf == NULL) {
- vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ?
- &devc->wport : NULL;
- ret = pcm_setup(devc, rport, wport);
- if (ret < 0)
- return ret;
- }
-
- if (!access_ok(VERIFY_READ, buffer, count))
- return -EFAULT;
- ret = 0;
- while (count) {
- DECLARE_WAITQUEUE(wait, current);
- add_wait_queue(&rport->queue, &wait);
- while ((nb = swb_inc_u(rport, 0)) == 0) {
- DBGPV("blocking\n");
- set_current_state(TASK_INTERRUPTIBLE);
- if (rport->flags & DISABLED ||
- file->f_flags & O_NONBLOCK) {
- current->state = TASK_RUNNING;
- remove_wait_queue(&rport->queue, &wait);
- return ret ? ret : -EAGAIN;
- }
- schedule();
- if (signal_pending(current)) {
- current->state = TASK_RUNNING;
- remove_wait_queue(&rport->queue, &wait);
- return ret ? ret : -ERESTARTSYS;
- }
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&rport->queue, &wait);
- pcm_input(devc, 0, 0);
- /* nb bytes are available in userbuf. */
- if (nb > count)
- nb = count;
- DBGPV("nb = %d\n", nb);
- if (copy_to_user(buffer, rport->swbuf + rport->swb_u_idx, nb))
- return -EFAULT;
- (void) swb_inc_u(rport, nb);
- buffer += nb;
- count -= nb;
- ret += nb;
- }
- DBGPV("returning %d\n", ret);
- return ret;
-}
-
-static ssize_t vwsnd_audio_read(struct file *file,
- char *buffer,
- size_t count,
- loff_t *ppos)
-{
- vwsnd_dev_t *devc = file->private_data;
- ssize_t ret;
-
- mutex_lock(&devc->io_mutex);
- ret = vwsnd_audio_do_read(file, buffer, count, ppos);
- mutex_unlock(&devc->io_mutex);
- return ret;
-}
-
-static ssize_t vwsnd_audio_do_write(struct file *file,
- const char *buffer,
- size_t count,
- loff_t *ppos)
-{
- vwsnd_dev_t *devc = file->private_data;
- vwsnd_port_t *wport = ((file->f_mode & FMODE_WRITE) ?
- &devc->wport : NULL);
- int ret, nb;
-
- DBGEV("(file=0x%p, buffer=0x%p, count=%d, ppos=0x%p)\n",
- file, buffer, count, ppos);
-
- if (!wport)
- return -EINVAL;
-
- if (wport->swbuf == NULL) {
- vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ?
- &devc->rport : NULL;
- ret = pcm_setup(devc, rport, wport);
- if (ret < 0)
- return ret;
- }
- if (!access_ok(VERIFY_WRITE, buffer, count))
- return -EFAULT;
- ret = 0;
- while (count) {
- DECLARE_WAITQUEUE(wait, current);
- add_wait_queue(&wport->queue, &wait);
- while ((nb = swb_inc_u(wport, 0)) == 0) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (wport->flags & DISABLED ||
- file->f_flags & O_NONBLOCK) {
- current->state = TASK_RUNNING;
- remove_wait_queue(&wport->queue, &wait);
- return ret ? ret : -EAGAIN;
- }
- schedule();
- if (signal_pending(current)) {
- current->state = TASK_RUNNING;
- remove_wait_queue(&wport->queue, &wait);
- return ret ? ret : -ERESTARTSYS;
- }
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&wport->queue, &wait);
- /* nb bytes are available in userbuf. */
- if (nb > count)
- nb = count;
- DBGPV("nb = %d\n", nb);
- if (copy_from_user(wport->swbuf + wport->swb_u_idx, buffer, nb))
- return -EFAULT;
- pcm_output(devc, 0, nb);
- buffer += nb;
- count -= nb;
- ret += nb;
- }
- DBGPV("returning %d\n", ret);
- return ret;
-}
-
-static ssize_t vwsnd_audio_write(struct file *file,
- const char *buffer,
- size_t count,
- loff_t *ppos)
-{
- vwsnd_dev_t *devc = file->private_data;
- ssize_t ret;
-
- mutex_lock(&devc->io_mutex);
- ret = vwsnd_audio_do_write(file, buffer, count, ppos);
- mutex_unlock(&devc->io_mutex);
- return ret;
-}
-
-/* No kernel lock - fine */
-static unsigned int vwsnd_audio_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
- vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ?
- &devc->rport : NULL;
- vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ?
- &devc->wport : NULL;
- unsigned int mask = 0;
-
- DBGEV("(file=0x%p, wait=0x%p)\n", file, wait);
-
- ASSERT(rport || wport);
- if (rport) {
- poll_wait(file, &rport->queue, wait);
- if (swb_inc_u(rport, 0))
- mask |= (POLLIN | POLLRDNORM);
- }
- if (wport) {
- poll_wait(file, &wport->queue, wait);
- if (wport->swbuf == NULL || swb_inc_u(wport, 0))
- mask |= (POLLOUT | POLLWRNORM);
- }
-
- DBGPV("returning 0x%x\n", mask);
- return mask;
-}
-
-static int vwsnd_audio_do_ioctl(struct file *file,
- unsigned int cmd,
- unsigned long arg)
-{
- vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
- vwsnd_port_t *rport = (file->f_mode & FMODE_READ) ?
- &devc->rport : NULL;
- vwsnd_port_t *wport = (file->f_mode & FMODE_WRITE) ?
- &devc->wport : NULL;
- vwsnd_port_t *aport = rport ? rport : wport;
- struct audio_buf_info buf_info;
- struct count_info info;
- unsigned long flags;
- int ival;
-
-
- DBGEV("(file=0x%p, cmd=0x%x, arg=0x%lx)\n",
- file, cmd, arg);
- switch (cmd) {
- case OSS_GETVERSION: /* _SIOR ('M', 118, int) */
- DBGX("OSS_GETVERSION\n");
- ival = SOUND_VERSION;
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_GETCAPS: /* _SIOR ('P',15, int) */
- DBGX("SNDCTL_DSP_GETCAPS\n");
- ival = DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER;
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_GETFMTS: /* _SIOR ('P',11, int) */
- DBGX("SNDCTL_DSP_GETFMTS\n");
- ival = (AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW |
- AFMT_U8 | AFMT_S8);
- return put_user(ival, (int *) arg);
- break;
-
- case SOUND_PCM_READ_RATE: /* _SIOR ('P', 2, int) */
- DBGX("SOUND_PCM_READ_RATE\n");
- ival = aport->sw_framerate;
- return put_user(ival, (int *) arg);
-
- case SOUND_PCM_READ_CHANNELS: /* _SIOR ('P', 6, int) */
- DBGX("SOUND_PCM_READ_CHANNELS\n");
- ival = aport->sw_channels;
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_SPEED: /* _SIOWR('P', 2, int) */
- if (get_user(ival, (int *) arg))
- return -EFAULT;
- DBGX("SNDCTL_DSP_SPEED %d\n", ival);
- if (ival) {
- if (aport->swstate != SW_INITIAL) {
- DBGX("SNDCTL_DSP_SPEED failed: swstate = %d\n",
- aport->swstate);
- return -EINVAL;
- }
- if (ival < MIN_SPEED)
- ival = MIN_SPEED;
- if (ival > MAX_SPEED)
- ival = MAX_SPEED;
- if (rport)
- rport->sw_framerate = ival;
- if (wport)
- wport->sw_framerate = ival;
- } else
- ival = aport->sw_framerate;
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_STEREO: /* _SIOWR('P', 3, int) */
- if (get_user(ival, (int *) arg))
- return -EFAULT;
- DBGX("SNDCTL_DSP_STEREO %d\n", ival);
- if (ival != 0 && ival != 1)
- return -EINVAL;
- if (aport->swstate != SW_INITIAL)
- return -EINVAL;
- if (rport)
- rport->sw_channels = ival + 1;
- if (wport)
- wport->sw_channels = ival + 1;
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_CHANNELS: /* _SIOWR('P', 6, int) */
- if (get_user(ival, (int *) arg))
- return -EFAULT;
- DBGX("SNDCTL_DSP_CHANNELS %d\n", ival);
- if (ival != 1 && ival != 2)
- return -EINVAL;
- if (aport->swstate != SW_INITIAL)
- return -EINVAL;
- if (rport)
- rport->sw_channels = ival;
- if (wport)
- wport->sw_channels = ival;
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_GETBLKSIZE: /* _SIOWR('P', 4, int) */
- ival = pcm_setup(devc, rport, wport);
- if (ival < 0) {
- DBGX("SNDCTL_DSP_GETBLKSIZE failed, errno %d\n", ival);
- return ival;
- }
- ival = 1 << aport->sw_fragshift;
- DBGX("SNDCTL_DSP_GETBLKSIZE returning %d\n", ival);
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_SETFRAGMENT: /* _SIOWR('P',10, int) */
- if (get_user(ival, (int *) arg))
- return -EFAULT;
- DBGX("SNDCTL_DSP_SETFRAGMENT %d:%d\n",
- ival >> 16, ival & 0xFFFF);
- if (aport->swstate != SW_INITIAL)
- return -EINVAL;
- {
- int sw_fragshift = ival & 0xFFFF;
- int sw_subdivshift = aport->sw_subdivshift;
- int hw_fragshift = sw_fragshift - sw_subdivshift;
- int sw_fragcount = (ival >> 16) & 0xFFFF;
- int hw_fragsize;
- if (hw_fragshift < MIN_FRAGSHIFT)
- hw_fragshift = MIN_FRAGSHIFT;
- if (hw_fragshift > MAX_FRAGSHIFT)
- hw_fragshift = MAX_FRAGSHIFT;
- sw_fragshift = hw_fragshift + aport->sw_subdivshift;
- hw_fragsize = 1 << hw_fragshift;
- if (sw_fragcount < MIN_FRAGCOUNT(hw_fragsize))
- sw_fragcount = MIN_FRAGCOUNT(hw_fragsize);
- if (sw_fragcount > MAX_FRAGCOUNT(hw_fragsize))
- sw_fragcount = MAX_FRAGCOUNT(hw_fragsize);
- DBGPV("sw_fragshift = %d\n", sw_fragshift);
- DBGPV("rport = 0x%p, wport = 0x%p\n", rport, wport);
- if (rport) {
- rport->sw_fragshift = sw_fragshift;
- rport->sw_fragcount = sw_fragcount;
- }
- if (wport) {
- wport->sw_fragshift = sw_fragshift;
- wport->sw_fragcount = sw_fragcount;
- }
- ival = sw_fragcount << 16 | sw_fragshift;
- }
- DBGX("SNDCTL_DSP_SETFRAGMENT returns %d:%d\n",
- ival >> 16, ival & 0xFFFF);
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_SUBDIVIDE: /* _SIOWR('P', 9, int) */
- if (get_user(ival, (int *) arg))
- return -EFAULT;
- DBGX("SNDCTL_DSP_SUBDIVIDE %d\n", ival);
- if (aport->swstate != SW_INITIAL)
- return -EINVAL;
- {
- int subdivshift;
- int hw_fragshift, hw_fragsize, hw_fragcount;
- switch (ival) {
- case 1: subdivshift = 0; break;
- case 2: subdivshift = 1; break;
- case 4: subdivshift = 2; break;
- default: return -EINVAL;
- }
- hw_fragshift = aport->sw_fragshift - subdivshift;
- if (hw_fragshift < MIN_FRAGSHIFT ||
- hw_fragshift > MAX_FRAGSHIFT)
- return -EINVAL;
- hw_fragsize = 1 << hw_fragshift;
- hw_fragcount = aport->sw_fragcount >> subdivshift;
- if (hw_fragcount < MIN_FRAGCOUNT(hw_fragsize) ||
- hw_fragcount > MAX_FRAGCOUNT(hw_fragsize))
- return -EINVAL;
- if (rport)
- rport->sw_subdivshift = subdivshift;
- if (wport)
- wport->sw_subdivshift = subdivshift;
- }
- return 0;
-
- case SNDCTL_DSP_SETFMT: /* _SIOWR('P',5, int) */
- if (get_user(ival, (int *) arg))
- return -EFAULT;
- DBGX("SNDCTL_DSP_SETFMT %d\n", ival);
- if (ival != AFMT_QUERY) {
- if (aport->swstate != SW_INITIAL) {
- DBGP("SETFMT failed, swstate = %d\n",
- aport->swstate);
- return -EINVAL;
- }
- switch (ival) {
- case AFMT_MU_LAW:
- case AFMT_A_LAW:
- case AFMT_U8:
- case AFMT_S8:
- case AFMT_S16_LE:
- if (rport)
- rport->sw_samplefmt = ival;
- if (wport)
- wport->sw_samplefmt = ival;
- break;
- default:
- return -EINVAL;
- }
- }
- ival = aport->sw_samplefmt;
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_GETOSPACE: /* _SIOR ('P',12, audio_buf_info) */
- DBGXV("SNDCTL_DSP_GETOSPACE\n");
- if (!wport)
- return -EINVAL;
- ival = pcm_setup(devc, rport, wport);
- if (ival < 0)
- return ival;
- ival = swb_inc_u(wport, 0);
- buf_info.fragments = ival >> wport->sw_fragshift;
- buf_info.fragstotal = wport->sw_fragcount;
- buf_info.fragsize = 1 << wport->sw_fragshift;
- buf_info.bytes = ival;
- DBGXV("SNDCTL_DSP_GETOSPACE returns { %d %d %d %d }\n",
- buf_info.fragments, buf_info.fragstotal,
- buf_info.fragsize, buf_info.bytes);
- if (copy_to_user((void *) arg, &buf_info, sizeof buf_info))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_GETISPACE: /* _SIOR ('P',13, audio_buf_info) */
- DBGX("SNDCTL_DSP_GETISPACE\n");
- if (!rport)
- return -EINVAL;
- ival = pcm_setup(devc, rport, wport);
- if (ival < 0)
- return ival;
- ival = swb_inc_u(rport, 0);
- buf_info.fragments = ival >> rport->sw_fragshift;
- buf_info.fragstotal = rport->sw_fragcount;
- buf_info.fragsize = 1 << rport->sw_fragshift;
- buf_info.bytes = ival;
- DBGX("SNDCTL_DSP_GETISPACE returns { %d %d %d %d }\n",
- buf_info.fragments, buf_info.fragstotal,
- buf_info.fragsize, buf_info.bytes);
- if (copy_to_user((void *) arg, &buf_info, sizeof buf_info))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_NONBLOCK: /* _SIO ('P',14) */
- DBGX("SNDCTL_DSP_NONBLOCK\n");
- spin_lock(&file->f_lock);
- file->f_flags |= O_NONBLOCK;
- spin_unlock(&file->f_lock);
- return 0;
-
- case SNDCTL_DSP_RESET: /* _SIO ('P', 0) */
- DBGX("SNDCTL_DSP_RESET\n");
- /*
- * Nothing special needs to be done for input. Input
- * samples sit in swbuf, but it will be reinitialized
- * to empty when pcm_setup() is called.
- */
- if (wport && wport->swbuf) {
- wport->swstate = SW_INITIAL;
- pcm_output(devc, 0, 0);
- pcm_write_sync(devc);
- }
- pcm_shutdown(devc, rport, wport);
- return 0;
-
- case SNDCTL_DSP_SYNC: /* _SIO ('P', 1) */
- DBGX("SNDCTL_DSP_SYNC\n");
- if (wport) {
- pcm_flush_frag(devc);
- pcm_write_sync(devc);
- }
- pcm_shutdown(devc, rport, wport);
- return 0;
-
- case SNDCTL_DSP_POST: /* _SIO ('P', 8) */
- DBGX("SNDCTL_DSP_POST\n");
- if (!wport)
- return -EINVAL;
- pcm_flush_frag(devc);
- return 0;
-
- case SNDCTL_DSP_GETIPTR: /* _SIOR ('P', 17, count_info) */
- DBGX("SNDCTL_DSP_GETIPTR\n");
- if (!rport)
- return -EINVAL;
- spin_lock_irqsave(&rport->lock, flags);
- {
- ustmsc_t ustmsc;
- if (rport->hwstate == HW_RUNNING) {
- ASSERT(rport->swstate == SW_RUN);
- li_read_USTMSC(&rport->chan, &ustmsc);
- info.bytes = ustmsc.msc - rport->MSC_offset;
- info.bytes *= rport->frame_size;
- } else {
- info.bytes = rport->byte_count;
- }
- info.blocks = rport->frag_count;
- info.ptr = 0; /* not implemented */
- rport->frag_count = 0;
- }
- spin_unlock_irqrestore(&rport->lock, flags);
- if (copy_to_user((void *) arg, &info, sizeof info))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_GETOPTR: /* _SIOR ('P',18, count_info) */
- DBGX("SNDCTL_DSP_GETOPTR\n");
- if (!wport)
- return -EINVAL;
- spin_lock_irqsave(&wport->lock, flags);
- {
- ustmsc_t ustmsc;
- if (wport->hwstate == HW_RUNNING) {
- ASSERT(wport->swstate == SW_RUN);
- li_read_USTMSC(&wport->chan, &ustmsc);
- info.bytes = ustmsc.msc - wport->MSC_offset;
- info.bytes *= wport->frame_size;
- } else {
- info.bytes = wport->byte_count;
- }
- info.blocks = wport->frag_count;
- info.ptr = 0; /* not implemented */
- wport->frag_count = 0;
- }
- spin_unlock_irqrestore(&wport->lock, flags);
- if (copy_to_user((void *) arg, &info, sizeof info))
- return -EFAULT;
- return 0;
-
- case SNDCTL_DSP_GETODELAY: /* _SIOR ('P', 23, int) */
- DBGX("SNDCTL_DSP_GETODELAY\n");
- if (!wport)
- return -EINVAL;
- spin_lock_irqsave(&wport->lock, flags);
- {
- int fsize = wport->frame_size;
- ival = wport->swb_i_avail / fsize;
- if (wport->hwstate == HW_RUNNING) {
- int swptr, hwptr, hwframes, hwbytes, hwsize;
- int totalhwbytes;
- ustmsc_t ustmsc;
-
- hwsize = wport->hwbuf_size;
- swptr = li_read_swptr(&wport->chan);
- li_read_USTMSC(&wport->chan, &ustmsc);
- hwframes = ustmsc.msc - wport->MSC_offset;
- totalhwbytes = hwframes * fsize;
- hwptr = totalhwbytes % hwsize;
- hwbytes = (swptr - hwptr + hwsize) % hwsize;
- ival += hwbytes / fsize;
- }
- }
- spin_unlock_irqrestore(&wport->lock, flags);
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_PROFILE: /* _SIOW ('P', 23, int) */
- DBGX("SNDCTL_DSP_PROFILE\n");
-
- /*
- * Thomas Sailer explains SNDCTL_DSP_PROFILE
- * (private email, March 24, 1999):
- *
- * This gives the sound driver a hint on what it
- * should do with partial fragments
- * (i.e. fragments partially filled with write).
- * This can direct the driver to zero them or
- * leave them alone. But don't ask me what this
- * is good for, my driver just zeroes the last
- * fragment before the receiver stops, no idea
- * what good for any other behaviour could
- * be. Implementing it as NOP seems safe.
- */
-
- break;
-
- case SNDCTL_DSP_GETTRIGGER: /* _SIOR ('P',16, int) */
- DBGX("SNDCTL_DSP_GETTRIGGER\n");
- ival = 0;
- if (rport) {
- spin_lock_irqsave(&rport->lock, flags);
- {
- if (!(rport->flags & DISABLED))
- ival |= PCM_ENABLE_INPUT;
- }
- spin_unlock_irqrestore(&rport->lock, flags);
- }
- if (wport) {
- spin_lock_irqsave(&wport->lock, flags);
- {
- if (!(wport->flags & DISABLED))
- ival |= PCM_ENABLE_OUTPUT;
- }
- spin_unlock_irqrestore(&wport->lock, flags);
- }
- return put_user(ival, (int *) arg);
-
- case SNDCTL_DSP_SETTRIGGER: /* _SIOW ('P',16, int) */
- if (get_user(ival, (int *) arg))
- return -EFAULT;
- DBGX("SNDCTL_DSP_SETTRIGGER %d\n", ival);
-
- /*
- * If user is disabling I/O and port is not in initial
- * state, fail with EINVAL.
- */
-
- if (((rport && !(ival & PCM_ENABLE_INPUT)) ||
- (wport && !(ival & PCM_ENABLE_OUTPUT))) &&
- aport->swstate != SW_INITIAL)
- return -EINVAL;
-
- if (rport) {
- vwsnd_port_hwstate_t hwstate;
- spin_lock_irqsave(&rport->lock, flags);
- {
- hwstate = rport->hwstate;
- if (ival & PCM_ENABLE_INPUT)
- rport->flags &= ~DISABLED;
- else
- rport->flags |= DISABLED;
- }
- spin_unlock_irqrestore(&rport->lock, flags);
- if (hwstate != HW_RUNNING && ival & PCM_ENABLE_INPUT) {
-
- if (rport->swstate == SW_INITIAL)
- pcm_setup(devc, rport, wport);
- else
- li_activate_dma(&rport->chan);
- }
- }
- if (wport) {
- vwsnd_port_flags_t pflags;
- spin_lock_irqsave(&wport->lock, flags);
- {
- pflags = wport->flags;
- if (ival & PCM_ENABLE_OUTPUT)
- wport->flags &= ~DISABLED;
- else
- wport->flags |= DISABLED;
- }
- spin_unlock_irqrestore(&wport->lock, flags);
- if (pflags & DISABLED && ival & PCM_ENABLE_OUTPUT) {
- if (wport->swstate == SW_RUN)
- pcm_output(devc, 0, 0);
- }
- }
- return 0;
-
- default:
- DBGP("unknown ioctl 0x%x\n", cmd);
- return -EINVAL;
- }
- DBGP("unimplemented ioctl 0x%x\n", cmd);
- return -EINVAL;
-}
-
-static long vwsnd_audio_ioctl(struct file *file,
- unsigned int cmd,
- unsigned long arg)
-{
- vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
- int ret;
-
- mutex_lock(&vwsnd_mutex);
- mutex_lock(&devc->io_mutex);
- ret = vwsnd_audio_do_ioctl(file, cmd, arg);
- mutex_unlock(&devc->io_mutex);
- mutex_unlock(&vwsnd_mutex);
-
- return ret;
-}
-
-/* No mmap. */
-
-static int vwsnd_audio_mmap(struct file *file, struct vm_area_struct *vma)
-{
- DBGE("(file=0x%p, vma=0x%p)\n", file, vma);
- return -ENODEV;
-}
-
-/*
- * Open the audio device for read and/or write.
- *
- * Returns 0 on success, -errno on failure.
- */
-
-static int vwsnd_audio_open(struct inode *inode, struct file *file)
-{
- vwsnd_dev_t *devc;
- int minor = iminor(inode);
- int sw_samplefmt;
-
- DBGE("(inode=0x%p, file=0x%p)\n", inode, file);
-
- mutex_lock(&vwsnd_mutex);
- INC_USE_COUNT;
- for (devc = vwsnd_dev_list; devc; devc = devc->next_dev)
- if ((devc->audio_minor & ~0x0F) == (minor & ~0x0F))
- break;
-
- if (devc == NULL) {
- DEC_USE_COUNT;
- mutex_unlock(&vwsnd_mutex);
- return -ENODEV;
- }
-
- mutex_lock(&devc->open_mutex);
- while (devc->open_mode & file->f_mode) {
- mutex_unlock(&devc->open_mutex);
- if (file->f_flags & O_NONBLOCK) {
- DEC_USE_COUNT;
- mutex_unlock(&vwsnd_mutex);
- return -EBUSY;
- }
- interruptible_sleep_on(&devc->open_wait);
- if (signal_pending(current)) {
- DEC_USE_COUNT;
- mutex_unlock(&vwsnd_mutex);
- return -ERESTARTSYS;
- }
- mutex_lock(&devc->open_mutex);
- }
- devc->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
- mutex_unlock(&devc->open_mutex);
-
- /* get default sample format from minor number. */
-
- sw_samplefmt = 0;
- if ((minor & 0xF) == SND_DEV_DSP)
- sw_samplefmt = AFMT_U8;
- else if ((minor & 0xF) == SND_DEV_AUDIO)
- sw_samplefmt = AFMT_MU_LAW;
- else if ((minor & 0xF) == SND_DEV_DSP16)
- sw_samplefmt = AFMT_S16_LE;
- else
- ASSERT(0);
-
- /* Initialize vwsnd_ports. */
-
- mutex_lock(&devc->io_mutex);
- {
- if (file->f_mode & FMODE_READ) {
- devc->rport.swstate = SW_INITIAL;
- devc->rport.flags = 0;
- devc->rport.sw_channels = 1;
- devc->rport.sw_samplefmt = sw_samplefmt;
- devc->rport.sw_framerate = 8000;
- devc->rport.sw_fragshift = DEFAULT_FRAGSHIFT;
- devc->rport.sw_fragcount = DEFAULT_FRAGCOUNT;
- devc->rport.sw_subdivshift = DEFAULT_SUBDIVSHIFT;
- devc->rport.byte_count = 0;
- devc->rport.frag_count = 0;
- }
- if (file->f_mode & FMODE_WRITE) {
- devc->wport.swstate = SW_INITIAL;
- devc->wport.flags = 0;
- devc->wport.sw_channels = 1;
- devc->wport.sw_samplefmt = sw_samplefmt;
- devc->wport.sw_framerate = 8000;
- devc->wport.sw_fragshift = DEFAULT_FRAGSHIFT;
- devc->wport.sw_fragcount = DEFAULT_FRAGCOUNT;
- devc->wport.sw_subdivshift = DEFAULT_SUBDIVSHIFT;
- devc->wport.byte_count = 0;
- devc->wport.frag_count = 0;
- }
- }
- mutex_unlock(&devc->io_mutex);
-
- file->private_data = devc;
- DBGRV();
- mutex_unlock(&vwsnd_mutex);
- return 0;
-}
-
-/*
- * Release (close) the audio device.
- */
-
-static int vwsnd_audio_release(struct inode *inode, struct file *file)
-{
- vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
- vwsnd_port_t *wport = NULL, *rport = NULL;
- int err = 0;
-
- mutex_lock(&vwsnd_mutex);
- mutex_lock(&devc->io_mutex);
- {
- DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
-
- if (file->f_mode & FMODE_READ)
- rport = &devc->rport;
- if (file->f_mode & FMODE_WRITE) {
- wport = &devc->wport;
- pcm_flush_frag(devc);
- pcm_write_sync(devc);
- }
- pcm_shutdown(devc, rport, wport);
- if (rport)
- rport->swstate = SW_OFF;
- if (wport)
- wport->swstate = SW_OFF;
- }
- mutex_unlock(&devc->io_mutex);
-
- mutex_lock(&devc->open_mutex);
- {
- devc->open_mode &= ~file->f_mode;
- }
- mutex_unlock(&devc->open_mutex);
- wake_up(&devc->open_wait);
- DEC_USE_COUNT;
- DBGR();
- mutex_unlock(&vwsnd_mutex);
- return err;
-}
-
-static const struct file_operations vwsnd_audio_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = vwsnd_audio_read,
- .write = vwsnd_audio_write,
- .poll = vwsnd_audio_poll,
- .unlocked_ioctl = vwsnd_audio_ioctl,
- .mmap = vwsnd_audio_mmap,
- .open = vwsnd_audio_open,
- .release = vwsnd_audio_release,
-};
-
-/*****************************************************************************/
-/* mixer driver */
-
-/* open the mixer device. */
-
-static int vwsnd_mixer_open(struct inode *inode, struct file *file)
-{
- vwsnd_dev_t *devc;
-
- DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
-
- INC_USE_COUNT;
- mutex_lock(&vwsnd_mutex);
- for (devc = vwsnd_dev_list; devc; devc = devc->next_dev)
- if (devc->mixer_minor == iminor(inode))
- break;
-
- if (devc == NULL) {
- DEC_USE_COUNT;
- mutex_unlock(&vwsnd_mutex);
- return -ENODEV;
- }
- file->private_data = devc;
- mutex_unlock(&vwsnd_mutex);
- return 0;
-}
-
-/* release (close) the mixer device. */
-
-static int vwsnd_mixer_release(struct inode *inode, struct file *file)
-{
- DBGEV("(inode=0x%p, file=0x%p)\n", inode, file);
- DEC_USE_COUNT;
- return 0;
-}
-
-/* mixer_read_ioctl handles all read ioctls on the mixer device. */
-
-static int mixer_read_ioctl(vwsnd_dev_t *devc, unsigned int nr, void __user *arg)
-{
- int val = -1;
-
- DBGEV("(devc=0x%p, nr=0x%x, arg=0x%p)\n", devc, nr, arg);
-
- switch (nr) {
- case SOUND_MIXER_CAPS:
- val = SOUND_CAP_EXCL_INPUT;
- break;
-
- case SOUND_MIXER_DEVMASK:
- val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
- SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV);
- break;
-
- case SOUND_MIXER_STEREODEVS:
- val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
- SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_RECLEV);
- break;
-
- case SOUND_MIXER_OUTMASK:
- val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
- SOUND_MASK_MIC | SOUND_MASK_CD);
- break;
-
- case SOUND_MIXER_RECMASK:
- val = (SOUND_MASK_PCM | SOUND_MASK_LINE |
- SOUND_MASK_MIC | SOUND_MASK_CD);
- break;
-
- case SOUND_MIXER_PCM:
- val = ad1843_get_gain(&devc->lith, &ad1843_gain_PCM);
- break;
-
- case SOUND_MIXER_LINE:
- val = ad1843_get_gain(&devc->lith, &ad1843_gain_LINE);
- break;
-
- case SOUND_MIXER_MIC:
- val = ad1843_get_gain(&devc->lith, &ad1843_gain_MIC);
- break;
-
- case SOUND_MIXER_CD:
- val = ad1843_get_gain(&devc->lith, &ad1843_gain_CD);
- break;
-
- case SOUND_MIXER_RECLEV:
- val = ad1843_get_gain(&devc->lith, &ad1843_gain_RECLEV);
- break;
-
- case SOUND_MIXER_RECSRC:
- val = ad1843_get_recsrc(&devc->lith);
- break;
-
- case SOUND_MIXER_OUTSRC:
- val = ad1843_get_outsrc(&devc->lith);
- break;
-
- default:
- return -EINVAL;
- }
- return put_user(val, (int __user *) arg);
-}
-
-/* mixer_write_ioctl handles all write ioctls on the mixer device. */
-
-static int mixer_write_ioctl(vwsnd_dev_t *devc, unsigned int nr, void __user *arg)
-{
- int val;
- int err;
-
- DBGEV("(devc=0x%p, nr=0x%x, arg=0x%p)\n", devc, nr, arg);
-
- err = get_user(val, (int __user *) arg);
- if (err)
- return -EFAULT;
- switch (nr) {
- case SOUND_MIXER_PCM:
- val = ad1843_set_gain(&devc->lith, &ad1843_gain_PCM, val);
- break;
-
- case SOUND_MIXER_LINE:
- val = ad1843_set_gain(&devc->lith, &ad1843_gain_LINE, val);
- break;
-
- case SOUND_MIXER_MIC:
- val = ad1843_set_gain(&devc->lith, &ad1843_gain_MIC, val);
- break;
-
- case SOUND_MIXER_CD:
- val = ad1843_set_gain(&devc->lith, &ad1843_gain_CD, val);
- break;
-
- case SOUND_MIXER_RECLEV:
- val = ad1843_set_gain(&devc->lith, &ad1843_gain_RECLEV, val);
- break;
-
- case SOUND_MIXER_RECSRC:
- if (devc->rport.swbuf || devc->wport.swbuf)
- return -EBUSY; /* can't change recsrc while running */
- val = ad1843_set_recsrc(&devc->lith, val);
- break;
-
- case SOUND_MIXER_OUTSRC:
- val = ad1843_set_outsrc(&devc->lith, val);
- break;
-
- default:
- return -EINVAL;
- }
- if (val < 0)
- return val;
- return put_user(val, (int __user *) arg);
-}
-
-/* This is the ioctl entry to the mixer driver. */
-
-static long vwsnd_mixer_ioctl(struct file *file,
- unsigned int cmd,
- unsigned long arg)
-{
- vwsnd_dev_t *devc = (vwsnd_dev_t *) file->private_data;
- const unsigned int nrmask = _IOC_NRMASK << _IOC_NRSHIFT;
- const unsigned int nr = (cmd & nrmask) >> _IOC_NRSHIFT;
- int retval;
-
- DBGEV("(devc=0x%p, cmd=0x%x, arg=0x%lx)\n", devc, cmd, arg);
-
- mutex_lock(&vwsnd_mutex);
- mutex_lock(&devc->mix_mutex);
- {
- if ((cmd & ~nrmask) == MIXER_READ(0))
- retval = mixer_read_ioctl(devc, nr, (void __user *) arg);
- else if ((cmd & ~nrmask) == MIXER_WRITE(0))
- retval = mixer_write_ioctl(devc, nr, (void __user *) arg);
- else
- retval = -EINVAL;
- }
- mutex_unlock(&devc->mix_mutex);
- mutex_unlock(&vwsnd_mutex);
- return retval;
-}
-
-static const struct file_operations vwsnd_mixer_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .unlocked_ioctl = vwsnd_mixer_ioctl,
- .open = vwsnd_mixer_open,
- .release = vwsnd_mixer_release,
-};
-
-/*****************************************************************************/
-/* probe/attach/unload */
-
-/* driver probe routine. Return nonzero if hardware is found. */
-
-static int __init probe_vwsnd(struct address_info *hw_config)
-{
- lithium_t lith;
- int w;
- unsigned long later;
-
- DBGEV("(hw_config=0x%p)\n", hw_config);
-
- /* XXX verify lithium present (to prevent crash on non-vw) */
-
- if (li_create(&lith, hw_config->io_base) != 0) {
- printk(KERN_WARNING "probe_vwsnd: can't map lithium\n");
- return 0;
- }
- later = jiffies + 2;
- li_writel(&lith, LI_HOST_CONTROLLER, LI_HC_LINK_ENABLE);
- do {
- w = li_readl(&lith, LI_HOST_CONTROLLER);
- } while (w == LI_HC_LINK_ENABLE && time_before(jiffies, later));
-
- li_destroy(&lith);
-
- DBGPV("HC = 0x%04x\n", w);
-
- if ((w == LI_HC_LINK_ENABLE) || (w & LI_HC_LINK_CODEC)) {
-
- /* This may indicate a beta machine with no audio,
- * or a future machine with different audio.
- * On beta-release 320 w/ no audio, HC == 0x4000 */
-
- printk(KERN_WARNING "probe_vwsnd: audio codec not found\n");
- return 0;
- }
-
- if (w & LI_HC_LINK_FAILURE) {
- printk(KERN_WARNING "probe_vwsnd: can't init audio codec\n");
- return 0;
- }
-
- printk(KERN_INFO "vwsnd: lithium audio at mmio %#x irq %d\n",
- hw_config->io_base, hw_config->irq);
-
- return 1;
-}
-
-/*
- * driver attach routine. Initialize driver data structures and
- * initialize hardware. A new vwsnd_dev_t is allocated and put
- * onto the global list, vwsnd_dev_list.
- *
- * Return +minor_dev on success, -errno on failure.
- */
-
-static int __init attach_vwsnd(struct address_info *hw_config)
-{
- vwsnd_dev_t *devc = NULL;
- int err = -ENOMEM;
-
- DBGEV("(hw_config=0x%p)\n", hw_config);
-
- devc = kmalloc(sizeof (vwsnd_dev_t), GFP_KERNEL);
- if (devc == NULL)
- goto fail0;
-
- err = li_create(&devc->lith, hw_config->io_base);
- if (err)
- goto fail1;
-
- init_waitqueue_head(&devc->open_wait);
-
- devc->rport.hwbuf_size = HWBUF_SIZE;
- devc->rport.hwbuf_vaddr = __get_free_pages(GFP_KERNEL, HWBUF_ORDER);
- if (!devc->rport.hwbuf_vaddr)
- goto fail2;
- devc->rport.hwbuf = (void *) devc->rport.hwbuf_vaddr;
- devc->rport.hwbuf_paddr = virt_to_phys(devc->rport.hwbuf);
-
- /*
- * Quote from the NT driver:
- *
- * // WARNING!!! HACK to setup output dma!!!
- * // This is required because even on output there is some data
- * // trickling into the input DMA channel. This is a bug in the
- * // Lithium microcode.
- * // --sde
- *
- * We set the input side's DMA base address here. It will remain
- * valid until the driver is unloaded.
- */
-
- li_writel(&devc->lith, LI_COMM1_BASE,
- devc->rport.hwbuf_paddr >> 8 | 1 << (37 - 8));
-
- devc->wport.hwbuf_size = HWBUF_SIZE;
- devc->wport.hwbuf_vaddr = __get_free_pages(GFP_KERNEL, HWBUF_ORDER);
- if (!devc->wport.hwbuf_vaddr)
- goto fail3;
- devc->wport.hwbuf = (void *) devc->wport.hwbuf_vaddr;
- devc->wport.hwbuf_paddr = virt_to_phys(devc->wport.hwbuf);
- DBGP("wport hwbuf = 0x%p\n", devc->wport.hwbuf);
-
- DBGDO(shut_up++);
- err = ad1843_init(&devc->lith);
- DBGDO(shut_up--);
- if (err)
- goto fail4;
-
- /* install interrupt handler */
-
- err = request_irq(hw_config->irq, vwsnd_audio_intr, 0, "vwsnd", devc);
- if (err)
- goto fail5;
-
- /* register this device's drivers. */
-
- devc->audio_minor = register_sound_dsp(&vwsnd_audio_fops, -1);
- if ((err = devc->audio_minor) < 0) {
- DBGDO(printk(KERN_WARNING
- "attach_vwsnd: register_sound_dsp error %d\n",
- err));
- goto fail6;
- }
- devc->mixer_minor = register_sound_mixer(&vwsnd_mixer_fops,
- devc->audio_minor >> 4);
- if ((err = devc->mixer_minor) < 0) {
- DBGDO(printk(KERN_WARNING
- "attach_vwsnd: register_sound_mixer error %d\n",
- err));
- goto fail7;
- }
-
- /* Squirrel away device indices for unload routine. */
-
- hw_config->slots[0] = devc->audio_minor;
-
- /* Initialize as much of *devc as possible */
-
- mutex_init(&devc->open_mutex);
- mutex_init(&devc->io_mutex);
- mutex_init(&devc->mix_mutex);
- devc->open_mode = 0;
- spin_lock_init(&devc->rport.lock);
- init_waitqueue_head(&devc->rport.queue);
- devc->rport.swstate = SW_OFF;
- devc->rport.hwstate = HW_STOPPED;
- devc->rport.flags = 0;
- devc->rport.swbuf = NULL;
- spin_lock_init(&devc->wport.lock);
- init_waitqueue_head(&devc->wport.queue);
- devc->wport.swstate = SW_OFF;
- devc->wport.hwstate = HW_STOPPED;
- devc->wport.flags = 0;
- devc->wport.swbuf = NULL;
-
- /* Success. Link us onto the local device list. */
-
- devc->next_dev = vwsnd_dev_list;
- vwsnd_dev_list = devc;
- return devc->audio_minor;
-
- /* So many ways to fail. Undo what we did. */
-
- fail7:
- unregister_sound_dsp(devc->audio_minor);
- fail6:
- free_irq(hw_config->irq, devc);
- fail5:
- fail4:
- free_pages(devc->wport.hwbuf_vaddr, HWBUF_ORDER);
- fail3:
- free_pages(devc->rport.hwbuf_vaddr, HWBUF_ORDER);
- fail2:
- li_destroy(&devc->lith);
- fail1:
- kfree(devc);
- fail0:
- return err;
-}
-
-static int __exit unload_vwsnd(struct address_info *hw_config)
-{
- vwsnd_dev_t *devc, **devcp;
-
- DBGE("()\n");
-
- devcp = &vwsnd_dev_list;
- while ((devc = *devcp)) {
- if (devc->audio_minor == hw_config->slots[0]) {
- *devcp = devc->next_dev;
- break;
- }
- devcp = &devc->next_dev;
- }
-
- if (!devc)
- return -ENODEV;
-
- unregister_sound_mixer(devc->mixer_minor);
- unregister_sound_dsp(devc->audio_minor);
- free_irq(hw_config->irq, devc);
- free_pages(devc->wport.hwbuf_vaddr, HWBUF_ORDER);
- free_pages(devc->rport.hwbuf_vaddr, HWBUF_ORDER);
- li_destroy(&devc->lith);
- kfree(devc);
-
- return 0;
-}
-
-/*****************************************************************************/
-/* initialization and loadable kernel module interface */
-
-static struct address_info the_hw_config = {
- 0xFF001000, /* lithium phys addr */
- CO_IRQ(CO_APIC_LI_AUDIO) /* irq */
-};
-
-MODULE_DESCRIPTION("SGI Visual Workstation sound module");
-MODULE_AUTHOR("Bob Miller <kbob@sgi.com>");
-MODULE_LICENSE("GPL");
-
-static int __init init_vwsnd(void)
-{
- int err;
-
- DBGXV("\n");
- DBGXV("sound::vwsnd::init_module()\n");
-
- if (!probe_vwsnd(&the_hw_config))
- return -ENODEV;
-
- err = attach_vwsnd(&the_hw_config);
- if (err < 0)
- return err;
- return 0;
-}
-
-static void __exit cleanup_vwsnd(void)
-{
- DBGX("sound::vwsnd::cleanup_module()\n");
-
- unload_vwsnd(&the_hw_config);
-}
-
-module_init(init_vwsnd);
-module_exit(cleanup_vwsnd);
diff --git a/sound/oss/waveartist.c b/sound/oss/waveartist.c
deleted file mode 100644
index 52468742d9f2..000000000000
--- a/sound/oss/waveartist.c
+++ /dev/null
@@ -1,2025 +0,0 @@
-/*
- * linux/sound/oss/waveartist.c
- *
- * The low level driver for the RWA010 Rockwell Wave Artist
- * codec chip used in the Rebel.com NetWinder.
- *
- * Cleaned up and integrated into 2.1 by Russell King (rmk@arm.linux.org.uk)
- * and Pat Beirne (patb@corel.ca)
- *
- *
- * Copyright (C) by Rebel.com 1998-1999
- *
- * RWA010 specs received under NDA from Rockwell
- *
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- *
- * Changes:
- * 11-10-2000 Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
- * Added __init to waveartist_init()
- */
-
-/* Debugging */
-#define DEBUG_CMD 1
-#define DEBUG_OUT 2
-#define DEBUG_IN 4
-#define DEBUG_INTR 8
-#define DEBUG_MIXER 16
-#define DEBUG_TRIGGER 32
-
-#define debug_flg (0)
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/spinlock.h>
-#include <linux/bitops.h>
-
-#include <asm/system.h>
-
-#include "sound_config.h"
-#include "waveartist.h"
-
-#ifdef CONFIG_ARM
-#include <mach/hardware.h>
-#include <asm/mach-types.h>
-#endif
-
-#ifndef NO_DMA
-#define NO_DMA 255
-#endif
-
-#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH |\
- SOUND_MASK_PCM |\
- SOUND_MASK_LINE |\
- SOUND_MASK_MIC |\
- SOUND_MASK_LINE1 |\
- SOUND_MASK_RECLEV |\
- SOUND_MASK_VOLUME |\
- SOUND_MASK_IMIX)
-
-static unsigned short levels[SOUND_MIXER_NRDEVICES] = {
- 0x5555, /* Master Volume */
- 0x0000, /* Bass */
- 0x0000, /* Treble */
- 0x2323, /* Synth (FM) */
- 0x4b4b, /* PCM */
- 0x6464, /* PC Speaker */
- 0x0000, /* Ext Line */
- 0x0000, /* Mic */
- 0x0000, /* CD */
- 0x6464, /* Recording monitor */
- 0x0000, /* SB PCM (ALT PCM) */
- 0x0000, /* Recording level */
- 0x6464, /* Input gain */
- 0x6464, /* Output gain */
- 0x0000, /* Line1 (Aux1) */
- 0x0000, /* Line2 (Aux2) */
- 0x0000, /* Line3 (Aux3) */
- 0x0000, /* Digital1 */
- 0x0000, /* Digital2 */
- 0x0000, /* Digital3 */
- 0x0000, /* Phone In */
- 0x6464, /* Phone Out */
- 0x0000, /* Video */
- 0x0000, /* Radio */
- 0x0000 /* Monitor */
-};
-
-typedef struct {
- struct address_info hw; /* hardware */
- char *chip_name;
-
- int xfer_count;
- int audio_mode;
- int open_mode;
- int audio_flags;
- int record_dev;
- int playback_dev;
- int dev_no;
-
- /* Mixer parameters */
- const struct waveartist_mixer_info *mix;
-
- unsigned short *levels; /* cache of volume settings */
- int recmask; /* currently enabled recording device! */
-
-#ifdef CONFIG_ARCH_NETWINDER
- signed int slider_vol; /* hardware slider volume */
- unsigned int handset_detect :1;
- unsigned int telephone_detect:1;
- unsigned int no_autoselect :1;/* handset/telephone autoselects a path */
- unsigned int spkr_mute_state :1;/* set by ioctl or autoselect */
- unsigned int line_mute_state :1;/* set by ioctl or autoselect */
- unsigned int use_slider :1;/* use slider setting for o/p vol */
-#endif
-} wavnc_info;
-
-/*
- * This is the implementation specific mixer information.
- */
-struct waveartist_mixer_info {
- unsigned int supported_devs; /* Supported devices */
- unsigned int recording_devs; /* Recordable devies */
- unsigned int stereo_devs; /* Stereo devices */
-
- unsigned int (*select_input)(wavnc_info *, unsigned int,
- unsigned char *, unsigned char *);
- int (*decode_mixer)(wavnc_info *, int,
- unsigned char, unsigned char);
- int (*get_mixer)(wavnc_info *, int);
-};
-
-typedef struct wavnc_port_info {
- int open_mode;
- int speed;
- int channels;
- int audio_format;
-} wavnc_port_info;
-
-static int nr_waveartist_devs;
-static wavnc_info adev_info[MAX_AUDIO_DEV];
-static DEFINE_SPINLOCK(waveartist_lock);
-
-#ifndef CONFIG_ARCH_NETWINDER
-#define machine_is_netwinder() 0
-#else
-static struct timer_list vnc_timer;
-static void vnc_configure_mixer(wavnc_info *devc, unsigned int input_mask);
-static int vnc_private_ioctl(int dev, unsigned int cmd, int __user *arg);
-static void vnc_slider_tick(unsigned long data);
-#endif
-
-static inline void
-waveartist_set_ctlr(struct address_info *hw, unsigned char clear, unsigned char set)
-{
- unsigned int ctlr_port = hw->io_base + CTLR;
-
- clear = ~clear & inb(ctlr_port);
-
- outb(clear | set, ctlr_port);
-}
-
-/* Toggle IRQ acknowledge line
- */
-static inline void
-waveartist_iack(wavnc_info *devc)
-{
- unsigned int ctlr_port = devc->hw.io_base + CTLR;
- int old_ctlr;
-
- old_ctlr = inb(ctlr_port) & ~IRQ_ACK;
-
- outb(old_ctlr | IRQ_ACK, ctlr_port);
- outb(old_ctlr, ctlr_port);
-}
-
-static inline int
-waveartist_sleep(int timeout_ms)
-{
- unsigned int timeout = msecs_to_jiffies(timeout_ms*100);
- return schedule_timeout_interruptible(timeout);
-}
-
-static int
-waveartist_reset(wavnc_info *devc)
-{
- struct address_info *hw = &devc->hw;
- unsigned int timeout, res = -1;
-
- waveartist_set_ctlr(hw, -1, RESET);
- waveartist_sleep(2);
- waveartist_set_ctlr(hw, RESET, 0);
-
- timeout = 500;
- do {
- mdelay(2);
-
- if (inb(hw->io_base + STATR) & CMD_RF) {
- res = inw(hw->io_base + CMDR);
- if (res == 0x55aa)
- break;
- }
- } while (--timeout);
-
- if (timeout == 0) {
- printk(KERN_WARNING "WaveArtist: reset timeout ");
- if (res != (unsigned int)-1)
- printk("(res=%04X)", res);
- printk("\n");
- return 1;
- }
- return 0;
-}
-
-/* Helper function to send and receive words
- * from WaveArtist. It handles all the handshaking
- * and can send or receive multiple words.
- */
-static int
-waveartist_cmd(wavnc_info *devc,
- int nr_cmd, unsigned int *cmd,
- int nr_resp, unsigned int *resp)
-{
- unsigned int io_base = devc->hw.io_base;
- unsigned int timed_out = 0;
- unsigned int i;
-
- if (debug_flg & DEBUG_CMD) {
- printk("waveartist_cmd: cmd=");
-
- for (i = 0; i < nr_cmd; i++)
- printk("%04X ", cmd[i]);
-
- printk("\n");
- }
-
- if (inb(io_base + STATR) & CMD_RF) {
- int old_data;
-
- /* flush the port
- */
-
- old_data = inw(io_base + CMDR);
-
- if (debug_flg & DEBUG_CMD)
- printk("flushed %04X...", old_data);
-
- udelay(10);
- }
-
- for (i = 0; !timed_out && i < nr_cmd; i++) {
- int count;
-
- for (count = 5000; count; count--)
- if (inb(io_base + STATR) & CMD_WE)
- break;
-
- if (!count)
- timed_out = 1;
- else
- outw(cmd[i], io_base + CMDR);
- }
-
- for (i = 0; !timed_out && i < nr_resp; i++) {
- int count;
-
- for (count = 5000; count; count--)
- if (inb(io_base + STATR) & CMD_RF)
- break;
-
- if (!count)
- timed_out = 1;
- else
- resp[i] = inw(io_base + CMDR);
- }
-
- if (debug_flg & DEBUG_CMD) {
- if (!timed_out) {
- printk("waveartist_cmd: resp=");
-
- for (i = 0; i < nr_resp; i++)
- printk("%04X ", resp[i]);
-
- printk("\n");
- } else
- printk("waveartist_cmd: timed out\n");
- }
-
- return timed_out ? 1 : 0;
-}
-
-/*
- * Send one command word
- */
-static inline int
-waveartist_cmd1(wavnc_info *devc, unsigned int cmd)
-{
- return waveartist_cmd(devc, 1, &cmd, 0, NULL);
-}
-
-/*
- * Send one command, receive one word
- */
-static inline unsigned int
-waveartist_cmd1_r(wavnc_info *devc, unsigned int cmd)
-{
- unsigned int ret;
-
- waveartist_cmd(devc, 1, &cmd, 1, &ret);
-
- return ret;
-}
-
-/*
- * Send a double command, receive one
- * word (and throw it away)
- */
-static inline int
-waveartist_cmd2(wavnc_info *devc, unsigned int cmd, unsigned int arg)
-{
- unsigned int vals[2];
-
- vals[0] = cmd;
- vals[1] = arg;
-
- return waveartist_cmd(devc, 2, vals, 1, vals);
-}
-
-/*
- * Send a triple command
- */
-static inline int
-waveartist_cmd3(wavnc_info *devc, unsigned int cmd,
- unsigned int arg1, unsigned int arg2)
-{
- unsigned int vals[3];
-
- vals[0] = cmd;
- vals[1] = arg1;
- vals[2] = arg2;
-
- return waveartist_cmd(devc, 3, vals, 0, NULL);
-}
-
-static int
-waveartist_getrev(wavnc_info *devc, char *rev)
-{
- unsigned int temp[2];
- unsigned int cmd = WACMD_GETREV;
-
- waveartist_cmd(devc, 1, &cmd, 2, temp);
-
- rev[0] = temp[0] >> 8;
- rev[1] = temp[0] & 255;
- rev[2] = '\0';
-
- return temp[0];
-}
-
-static void waveartist_halt_output(int dev);
-static void waveartist_halt_input(int dev);
-static void waveartist_halt(int dev);
-static void waveartist_trigger(int dev, int state);
-
-static int
-waveartist_open(int dev, int mode)
-{
- wavnc_info *devc;
- wavnc_port_info *portc;
- unsigned long flags;
-
- if (dev < 0 || dev >= num_audiodevs)
- return -ENXIO;
-
- devc = (wavnc_info *) audio_devs[dev]->devc;
- portc = (wavnc_port_info *) audio_devs[dev]->portc;
-
- spin_lock_irqsave(&waveartist_lock, flags);
- if (portc->open_mode || (devc->open_mode & mode)) {
- spin_unlock_irqrestore(&waveartist_lock, flags);
- return -EBUSY;
- }
-
- devc->audio_mode = 0;
- devc->open_mode |= mode;
- portc->open_mode = mode;
- waveartist_trigger(dev, 0);
-
- if (mode & OPEN_READ)
- devc->record_dev = dev;
- if (mode & OPEN_WRITE)
- devc->playback_dev = dev;
- spin_unlock_irqrestore(&waveartist_lock, flags);
-
- return 0;
-}
-
-static void
-waveartist_close(int dev)
-{
- wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc;
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
- unsigned long flags;
-
- spin_lock_irqsave(&waveartist_lock, flags);
-
- waveartist_halt(dev);
-
- devc->audio_mode = 0;
- devc->open_mode &= ~portc->open_mode;
- portc->open_mode = 0;
-
- spin_unlock_irqrestore(&waveartist_lock, flags);
-}
-
-static void
-waveartist_output_block(int dev, unsigned long buf, int __count, int intrflag)
-{
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
- wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc;
- unsigned long flags;
- unsigned int count = __count;
-
- if (debug_flg & DEBUG_OUT)
- printk("waveartist: output block, buf=0x%lx, count=0x%x...\n",
- buf, count);
- /*
- * 16 bit data
- */
- if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))
- count >>= 1;
-
- if (portc->channels > 1)
- count >>= 1;
-
- count -= 1;
-
- if (devc->audio_mode & PCM_ENABLE_OUTPUT &&
- audio_devs[dev]->flags & DMA_AUTOMODE &&
- intrflag &&
- count == devc->xfer_count) {
- devc->audio_mode |= PCM_ENABLE_OUTPUT;
- return; /*
- * Auto DMA mode on. No need to react
- */
- }
-
- spin_lock_irqsave(&waveartist_lock, flags);
-
- /*
- * set sample count
- */
- waveartist_cmd2(devc, WACMD_OUTPUTSIZE, count);
-
- devc->xfer_count = count;
- devc->audio_mode |= PCM_ENABLE_OUTPUT;
-
- spin_unlock_irqrestore(&waveartist_lock, flags);
-}
-
-static void
-waveartist_start_input(int dev, unsigned long buf, int __count, int intrflag)
-{
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
- wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc;
- unsigned long flags;
- unsigned int count = __count;
-
- if (debug_flg & DEBUG_IN)
- printk("waveartist: start input, buf=0x%lx, count=0x%x...\n",
- buf, count);
-
- if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
- count >>= 1;
-
- if (portc->channels > 1)
- count >>= 1;
-
- count -= 1;
-
- if (devc->audio_mode & PCM_ENABLE_INPUT &&
- audio_devs[dev]->flags & DMA_AUTOMODE &&
- intrflag &&
- count == devc->xfer_count) {
- devc->audio_mode |= PCM_ENABLE_INPUT;
- return; /*
- * Auto DMA mode on. No need to react
- */
- }
-
- spin_lock_irqsave(&waveartist_lock, flags);
-
- /*
- * set sample count
- */
- waveartist_cmd2(devc, WACMD_INPUTSIZE, count);
-
- devc->xfer_count = count;
- devc->audio_mode |= PCM_ENABLE_INPUT;
-
- spin_unlock_irqrestore(&waveartist_lock, flags);
-}
-
-static int
-waveartist_ioctl(int dev, unsigned int cmd, void __user * arg)
-{
- return -EINVAL;
-}
-
-static unsigned int
-waveartist_get_speed(wavnc_port_info *portc)
-{
- unsigned int speed;
-
- /*
- * program the speed, channels, bits
- */
- if (portc->speed == 8000)
- speed = 0x2E71;
- else if (portc->speed == 11025)
- speed = 0x4000;
- else if (portc->speed == 22050)
- speed = 0x8000;
- else if (portc->speed == 44100)
- speed = 0x0;
- else {
- /*
- * non-standard - just calculate
- */
- speed = portc->speed << 16;
-
- speed = (speed / 44100) & 65535;
- }
-
- return speed;
-}
-
-static unsigned int
-waveartist_get_bits(wavnc_port_info *portc)
-{
- unsigned int bits;
-
- if (portc->audio_format == AFMT_S16_LE)
- bits = 1;
- else if (portc->audio_format == AFMT_S8)
- bits = 0;
- else
- bits = 2; //default AFMT_U8
-
- return bits;
-}
-
-static int
-waveartist_prepare_for_input(int dev, int bsize, int bcount)
-{
- unsigned long flags;
- wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc;
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
- unsigned int speed, bits;
-
- if (devc->audio_mode)
- return 0;
-
- speed = waveartist_get_speed(portc);
- bits = waveartist_get_bits(portc);
-
- spin_lock_irqsave(&waveartist_lock, flags);
-
- if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits))
- printk(KERN_WARNING "waveartist: error setting the "
- "record format to %d\n", portc->audio_format);
-
- if (waveartist_cmd2(devc, WACMD_INPUTCHANNELS, portc->channels))
- printk(KERN_WARNING "waveartist: error setting record "
- "to %d channels\n", portc->channels);
-
- /*
- * write cmd SetSampleSpeedTimeConstant
- */
- if (waveartist_cmd2(devc, WACMD_INPUTSPEED, speed))
- printk(KERN_WARNING "waveartist: error setting the record "
- "speed to %dHz.\n", portc->speed);
-
- if (waveartist_cmd2(devc, WACMD_INPUTDMA, 1))
- printk(KERN_WARNING "waveartist: error setting the record "
- "data path to 0x%X\n", 1);
-
- if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits))
- printk(KERN_WARNING "waveartist: error setting the record "
- "format to %d\n", portc->audio_format);
-
- devc->xfer_count = 0;
- spin_unlock_irqrestore(&waveartist_lock, flags);
- waveartist_halt_input(dev);
-
- if (debug_flg & DEBUG_INTR) {
- printk("WA CTLR reg: 0x%02X.\n",
- inb(devc->hw.io_base + CTLR));
- printk("WA STAT reg: 0x%02X.\n",
- inb(devc->hw.io_base + STATR));
- printk("WA IRQS reg: 0x%02X.\n",
- inb(devc->hw.io_base + IRQSTAT));
- }
-
- return 0;
-}
-
-static int
-waveartist_prepare_for_output(int dev, int bsize, int bcount)
-{
- unsigned long flags;
- wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc;
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
- unsigned int speed, bits;
-
- /*
- * program the speed, channels, bits
- */
- speed = waveartist_get_speed(portc);
- bits = waveartist_get_bits(portc);
-
- spin_lock_irqsave(&waveartist_lock, flags);
-
- if (waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed) &&
- waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed))
- printk(KERN_WARNING "waveartist: error setting the playback "
- "speed to %dHz.\n", portc->speed);
-
- if (waveartist_cmd2(devc, WACMD_OUTPUTCHANNELS, portc->channels))
- printk(KERN_WARNING "waveartist: error setting the playback "
- "to %d channels\n", portc->channels);
-
- if (waveartist_cmd2(devc, WACMD_OUTPUTDMA, 0))
- printk(KERN_WARNING "waveartist: error setting the playback "
- "data path to 0x%X\n", 0);
-
- if (waveartist_cmd2(devc, WACMD_OUTPUTFORMAT, bits))
- printk(KERN_WARNING "waveartist: error setting the playback "
- "format to %d\n", portc->audio_format);
-
- devc->xfer_count = 0;
- spin_unlock_irqrestore(&waveartist_lock, flags);
- waveartist_halt_output(dev);
-
- if (debug_flg & DEBUG_INTR) {
- printk("WA CTLR reg: 0x%02X.\n",inb(devc->hw.io_base + CTLR));
- printk("WA STAT reg: 0x%02X.\n",inb(devc->hw.io_base + STATR));
- printk("WA IRQS reg: 0x%02X.\n",inb(devc->hw.io_base + IRQSTAT));
- }
-
- return 0;
-}
-
-static void
-waveartist_halt(int dev)
-{
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
- wavnc_info *devc;
-
- if (portc->open_mode & OPEN_WRITE)
- waveartist_halt_output(dev);
-
- if (portc->open_mode & OPEN_READ)
- waveartist_halt_input(dev);
-
- devc = (wavnc_info *) audio_devs[dev]->devc;
- devc->audio_mode = 0;
-}
-
-static void
-waveartist_halt_input(int dev)
-{
- wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc;
- unsigned long flags;
-
- spin_lock_irqsave(&waveartist_lock, flags);
-
- /*
- * Stop capture
- */
- waveartist_cmd1(devc, WACMD_INPUTSTOP);
-
- devc->audio_mode &= ~PCM_ENABLE_INPUT;
-
- /*
- * Clear interrupt by toggling
- * the IRQ_ACK bit in CTRL
- */
- if (inb(devc->hw.io_base + STATR) & IRQ_REQ)
- waveartist_iack(devc);
-
-// devc->audio_mode &= ~PCM_ENABLE_INPUT;
-
- spin_unlock_irqrestore(&waveartist_lock, flags);
-}
-
-static void
-waveartist_halt_output(int dev)
-{
- wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc;
- unsigned long flags;
-
- spin_lock_irqsave(&waveartist_lock, flags);
-
- waveartist_cmd1(devc, WACMD_OUTPUTSTOP);
-
- devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
-
- /*
- * Clear interrupt by toggling
- * the IRQ_ACK bit in CTRL
- */
- if (inb(devc->hw.io_base + STATR) & IRQ_REQ)
- waveartist_iack(devc);
-
-// devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
-
- spin_unlock_irqrestore(&waveartist_lock, flags);
-}
-
-static void
-waveartist_trigger(int dev, int state)
-{
- wavnc_info *devc = (wavnc_info *) audio_devs[dev]->devc;
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
- unsigned long flags;
-
- if (debug_flg & DEBUG_TRIGGER) {
- printk("wavnc: audio trigger ");
- if (state & PCM_ENABLE_INPUT)
- printk("in ");
- if (state & PCM_ENABLE_OUTPUT)
- printk("out");
- printk("\n");
- }
-
- spin_lock_irqsave(&waveartist_lock, flags);
-
- state &= devc->audio_mode;
-
- if (portc->open_mode & OPEN_READ &&
- state & PCM_ENABLE_INPUT)
- /*
- * enable ADC Data Transfer to PC
- */
- waveartist_cmd1(devc, WACMD_INPUTSTART);
-
- if (portc->open_mode & OPEN_WRITE &&
- state & PCM_ENABLE_OUTPUT)
- /*
- * enable DAC data transfer from PC
- */
- waveartist_cmd1(devc, WACMD_OUTPUTSTART);
-
- spin_unlock_irqrestore(&waveartist_lock, flags);
-}
-
-static int
-waveartist_set_speed(int dev, int arg)
-{
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
-
- if (arg <= 0)
- return portc->speed;
-
- if (arg < 5000)
- arg = 5000;
- if (arg > 44100)
- arg = 44100;
-
- portc->speed = arg;
- return portc->speed;
-
-}
-
-static short
-waveartist_set_channels(int dev, short arg)
-{
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
-
- if (arg != 1 && arg != 2)
- return portc->channels;
-
- portc->channels = arg;
- return arg;
-}
-
-static unsigned int
-waveartist_set_bits(int dev, unsigned int arg)
-{
- wavnc_port_info *portc = (wavnc_port_info *) audio_devs[dev]->portc;
-
- if (arg == 0)
- return portc->audio_format;
-
- if ((arg != AFMT_U8) && (arg != AFMT_S16_LE) && (arg != AFMT_S8))
- arg = AFMT_U8;
-
- portc->audio_format = arg;
-
- return arg;
-}
-
-static struct audio_driver waveartist_audio_driver = {
- .owner = THIS_MODULE,
- .open = waveartist_open,
- .close = waveartist_close,
- .output_block = waveartist_output_block,
- .start_input = waveartist_start_input,
- .ioctl = waveartist_ioctl,
- .prepare_for_input = waveartist_prepare_for_input,
- .prepare_for_output = waveartist_prepare_for_output,
- .halt_io = waveartist_halt,
- .halt_input = waveartist_halt_input,
- .halt_output = waveartist_halt_output,
- .trigger = waveartist_trigger,
- .set_speed = waveartist_set_speed,
- .set_bits = waveartist_set_bits,
- .set_channels = waveartist_set_channels
-};
-
-
-static irqreturn_t
-waveartist_intr(int irq, void *dev_id)
-{
- wavnc_info *devc = dev_id;
- int irqstatus, status;
-
- spin_lock(&waveartist_lock);
- irqstatus = inb(devc->hw.io_base + IRQSTAT);
- status = inb(devc->hw.io_base + STATR);
-
- if (debug_flg & DEBUG_INTR)
- printk("waveartist_intr: stat=%02x, irqstat=%02x\n",
- status, irqstatus);
-
- if (status & IRQ_REQ) /* Clear interrupt */
- waveartist_iack(devc);
- else
- printk(KERN_WARNING "waveartist: unexpected interrupt\n");
-
- if (irqstatus & 0x01) {
- int temp = 1;
-
- /* PCM buffer done
- */
- if ((status & DMA0) && (devc->audio_mode & PCM_ENABLE_OUTPUT)) {
- DMAbuf_outputintr(devc->playback_dev, 1);
- temp = 0;
- }
- if ((status & DMA1) && (devc->audio_mode & PCM_ENABLE_INPUT)) {
- DMAbuf_inputintr(devc->record_dev);
- temp = 0;
- }
- if (temp) //default:
- printk(KERN_WARNING "waveartist: Unknown interrupt\n");
- }
- if (irqstatus & 0x2)
- // We do not use SB mode natively...
- printk(KERN_WARNING "waveartist: Unexpected SB interrupt...\n");
- spin_unlock(&waveartist_lock);
- return IRQ_HANDLED;
-}
-
-/* -------------------------------------------------------------------------
- * Mixer stuff
- */
-struct mix_ent {
- unsigned char reg_l;
- unsigned char reg_r;
- unsigned char shift;
- unsigned char max;
-};
-
-static const struct mix_ent mix_devs[SOUND_MIXER_NRDEVICES] = {
- { 2, 6, 1, 7 }, /* SOUND_MIXER_VOLUME */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_BASS */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_TREBLE */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_SYNTH */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_PCM */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_SPEAKER */
- { 0, 4, 6, 31 }, /* SOUND_MIXER_LINE */
- { 2, 6, 4, 3 }, /* SOUND_MIXER_MIC */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_CD */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_IMIX */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_ALTPCM */
-#if 0
- { 3, 7, 0, 10 }, /* SOUND_MIXER_RECLEV */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_IGAIN */
-#else
- { 0, 0, 0, 0 }, /* SOUND_MIXER_RECLEV */
- { 3, 7, 0, 7 }, /* SOUND_MIXER_IGAIN */
-#endif
- { 0, 0, 0, 0 }, /* SOUND_MIXER_OGAIN */
- { 0, 4, 1, 31 }, /* SOUND_MIXER_LINE1 */
- { 1, 5, 6, 31 }, /* SOUND_MIXER_LINE2 */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_LINE3 */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL1 */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL2 */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL3 */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_PHONEIN */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_PHONEOUT */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_VIDEO */
- { 0, 0, 0, 0 }, /* SOUND_MIXER_RADIO */
- { 0, 0, 0, 0 } /* SOUND_MIXER_MONITOR */
-};
-
-static void
-waveartist_mixer_update(wavnc_info *devc, int whichDev)
-{
- unsigned int lev_left, lev_right;
-
- lev_left = devc->levels[whichDev] & 0xff;
- lev_right = devc->levels[whichDev] >> 8;
-
- if (lev_left > 100)
- lev_left = 100;
- if (lev_right > 100)
- lev_right = 100;
-
-#define SCALE(lev,max) ((lev) * (max) / 100)
-
- if (machine_is_netwinder() && whichDev == SOUND_MIXER_PHONEOUT)
- whichDev = SOUND_MIXER_VOLUME;
-
- if (mix_devs[whichDev].reg_l || mix_devs[whichDev].reg_r) {
- const struct mix_ent *mix = mix_devs + whichDev;
- unsigned int mask, left, right;
-
- mask = mix->max << mix->shift;
- lev_left = SCALE(lev_left, mix->max) << mix->shift;
- lev_right = SCALE(lev_right, mix->max) << mix->shift;
-
- /* read left setting */
- left = waveartist_cmd1_r(devc, WACMD_GET_LEVEL |
- mix->reg_l << 8);
-
- /* read right setting */
- right = waveartist_cmd1_r(devc, WACMD_GET_LEVEL |
- mix->reg_r << 8);
-
- left = (left & ~mask) | (lev_left & mask);
- right = (right & ~mask) | (lev_right & mask);
-
- /* write left,right back */
- waveartist_cmd3(devc, WACMD_SET_MIXER, left, right);
- } else {
- switch(whichDev) {
- case SOUND_MIXER_PCM:
- waveartist_cmd3(devc, WACMD_SET_LEVEL,
- SCALE(lev_left, 32767),
- SCALE(lev_right, 32767));
- break;
-
- case SOUND_MIXER_SYNTH:
- waveartist_cmd3(devc, 0x0100 | WACMD_SET_LEVEL,
- SCALE(lev_left, 32767),
- SCALE(lev_right, 32767));
- break;
- }
- }
-}
-
-/*
- * Set the ADC MUX to the specified values. We do NOT do any
- * checking of the values passed, since we assume that the
- * relevant *_select_input function has done that for us.
- */
-static void
-waveartist_set_adc_mux(wavnc_info *devc, char left_dev, char right_dev)
-{
- unsigned int reg_08, reg_09;
-
- reg_08 = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x0800);
- reg_09 = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x0900);
-
- reg_08 = (reg_08 & ~0x3f) | right_dev << 3 | left_dev;
-
- waveartist_cmd3(devc, WACMD_SET_MIXER, reg_08, reg_09);
-}
-
-/*
- * Decode a recording mask into a mixer selection as follows:
- *
- * OSS Source WA Source Actual source
- * SOUND_MASK_IMIX Mixer Mixer output (same as AD1848)
- * SOUND_MASK_LINE Line Line in
- * SOUND_MASK_LINE1 Aux 1 Aux 1 in
- * SOUND_MASK_LINE2 Aux 2 Aux 2 in
- * SOUND_MASK_MIC Mic Microphone
- */
-static unsigned int
-waveartist_select_input(wavnc_info *devc, unsigned int recmask,
- unsigned char *dev_l, unsigned char *dev_r)
-{
- unsigned int recdev = ADC_MUX_NONE;
-
- if (recmask & SOUND_MASK_IMIX) {
- recmask = SOUND_MASK_IMIX;
- recdev = ADC_MUX_MIXER;
- } else if (recmask & SOUND_MASK_LINE2) {
- recmask = SOUND_MASK_LINE2;
- recdev = ADC_MUX_AUX2;
- } else if (recmask & SOUND_MASK_LINE1) {
- recmask = SOUND_MASK_LINE1;
- recdev = ADC_MUX_AUX1;
- } else if (recmask & SOUND_MASK_LINE) {
- recmask = SOUND_MASK_LINE;
- recdev = ADC_MUX_LINE;
- } else if (recmask & SOUND_MASK_MIC) {
- recmask = SOUND_MASK_MIC;
- recdev = ADC_MUX_MIC;
- }
-
- *dev_l = *dev_r = recdev;
-
- return recmask;
-}
-
-static int
-waveartist_decode_mixer(wavnc_info *devc, int dev, unsigned char lev_l,
- unsigned char lev_r)
-{
- switch (dev) {
- case SOUND_MIXER_VOLUME:
- case SOUND_MIXER_SYNTH:
- case SOUND_MIXER_PCM:
- case SOUND_MIXER_LINE:
- case SOUND_MIXER_MIC:
- case SOUND_MIXER_IGAIN:
- case SOUND_MIXER_LINE1:
- case SOUND_MIXER_LINE2:
- devc->levels[dev] = lev_l | lev_r << 8;
- break;
-
- case SOUND_MIXER_IMIX:
- break;
-
- default:
- dev = -EINVAL;
- break;
- }
-
- return dev;
-}
-
-static int waveartist_get_mixer(wavnc_info *devc, int dev)
-{
- return devc->levels[dev];
-}
-
-static const struct waveartist_mixer_info waveartist_mixer = {
- .supported_devs = SUPPORTED_MIXER_DEVICES | SOUND_MASK_IGAIN,
- .recording_devs = SOUND_MASK_LINE | SOUND_MASK_MIC |
- SOUND_MASK_LINE1 | SOUND_MASK_LINE2 |
- SOUND_MASK_IMIX,
- .stereo_devs = (SUPPORTED_MIXER_DEVICES | SOUND_MASK_IGAIN) & ~
- (SOUND_MASK_SPEAKER | SOUND_MASK_IMIX),
- .select_input = waveartist_select_input,
- .decode_mixer = waveartist_decode_mixer,
- .get_mixer = waveartist_get_mixer,
-};
-
-static void
-waveartist_set_recmask(wavnc_info *devc, unsigned int recmask)
-{
- unsigned char dev_l, dev_r;
-
- recmask &= devc->mix->recording_devs;
-
- /*
- * If more than one recording device selected,
- * disable the device that is currently in use.
- */
- if (hweight32(recmask) > 1)
- recmask &= ~devc->recmask;
-
- /*
- * Translate the recording device mask into
- * the ADC multiplexer settings.
- */
- devc->recmask = devc->mix->select_input(devc, recmask,
- &dev_l, &dev_r);
-
- waveartist_set_adc_mux(devc, dev_l, dev_r);
-}
-
-static int
-waveartist_set_mixer(wavnc_info *devc, int dev, unsigned int level)
-{
- unsigned int lev_left = level & 0x00ff;
- unsigned int lev_right = (level & 0xff00) >> 8;
-
- if (lev_left > 100)
- lev_left = 100;
- if (lev_right > 100)
- lev_right = 100;
-
- /*
- * Mono devices have their right volume forced to their
- * left volume. (from ALSA driver OSS emulation).
- */
- if (!(devc->mix->stereo_devs & (1 << dev)))
- lev_right = lev_left;
-
- dev = devc->mix->decode_mixer(devc, dev, lev_left, lev_right);
-
- if (dev >= 0)
- waveartist_mixer_update(devc, dev);
-
- return dev < 0 ? dev : 0;
-}
-
-static int
-waveartist_mixer_ioctl(int dev, unsigned int cmd, void __user * arg)
-{
- wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
- int ret = 0, val, nr;
-
- /*
- * All SOUND_MIXER_* ioctls use type 'M'
- */
- if (((cmd >> 8) & 255) != 'M')
- return -ENOIOCTLCMD;
-
-#ifdef CONFIG_ARCH_NETWINDER
- if (machine_is_netwinder()) {
- ret = vnc_private_ioctl(dev, cmd, arg);
- if (ret != -ENOIOCTLCMD)
- return ret;
- else
- ret = 0;
- }
-#endif
-
- nr = cmd & 0xff;
-
- if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
- if (get_user(val, (int __user *)arg))
- return -EFAULT;
-
- switch (nr) {
- case SOUND_MIXER_RECSRC:
- waveartist_set_recmask(devc, val);
- break;
-
- default:
- ret = -EINVAL;
- if (nr < SOUND_MIXER_NRDEVICES &&
- devc->mix->supported_devs & (1 << nr))
- ret = waveartist_set_mixer(devc, nr, val);
- }
- }
-
- if (ret == 0 && _SIOC_DIR(cmd) & _SIOC_READ) {
- ret = -EINVAL;
-
- switch (nr) {
- case SOUND_MIXER_RECSRC:
- ret = devc->recmask;
- break;
-
- case SOUND_MIXER_DEVMASK:
- ret = devc->mix->supported_devs;
- break;
-
- case SOUND_MIXER_STEREODEVS:
- ret = devc->mix->stereo_devs;
- break;
-
- case SOUND_MIXER_RECMASK:
- ret = devc->mix->recording_devs;
- break;
-
- case SOUND_MIXER_CAPS:
- ret = SOUND_CAP_EXCL_INPUT;
- break;
-
- default:
- if (nr < SOUND_MIXER_NRDEVICES)
- ret = devc->mix->get_mixer(devc, nr);
- break;
- }
-
- if (ret >= 0)
- ret = put_user(ret, (int __user *)arg) ? -EFAULT : 0;
- }
-
- return ret;
-}
-
-static struct mixer_operations waveartist_mixer_operations =
-{
- .owner = THIS_MODULE,
- .id = "WaveArtist",
- .name = "WaveArtist",
- .ioctl = waveartist_mixer_ioctl
-};
-
-static void
-waveartist_mixer_reset(wavnc_info *devc)
-{
- int i;
-
- if (debug_flg & DEBUG_MIXER)
- printk("%s: mixer_reset\n", devc->hw.name);
-
- /*
- * reset mixer cmd
- */
- waveartist_cmd1(devc, WACMD_RST_MIXER);
-
- /*
- * set input for ADC to come from 'quiet'
- * turn on default modes
- */
- waveartist_cmd3(devc, WACMD_SET_MIXER, 0x9800, 0xa836);
-
- /*
- * set mixer input select to none, RX filter gains 0 dB
- */
- waveartist_cmd3(devc, WACMD_SET_MIXER, 0x4c00, 0x8c00);
-
- /*
- * set bit 0 reg 2 to 1 - unmute MonoOut
- */
- waveartist_cmd3(devc, WACMD_SET_MIXER, 0x2801, 0x6800);
-
- /* set default input device = internal mic
- * current recording device = none
- */
- waveartist_set_recmask(devc, 0);
-
- for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
- waveartist_mixer_update(devc, i);
-}
-
-static int __init waveartist_init(wavnc_info *devc)
-{
- wavnc_port_info *portc;
- char rev[3], dev_name[64];
- int my_dev;
-
- if (waveartist_reset(devc))
- return -ENODEV;
-
- sprintf(dev_name, "%s (%s", devc->hw.name, devc->chip_name);
-
- if (waveartist_getrev(devc, rev)) {
- strcat(dev_name, " rev. ");
- strcat(dev_name, rev);
- }
- strcat(dev_name, ")");
-
- conf_printf2(dev_name, devc->hw.io_base, devc->hw.irq,
- devc->hw.dma, devc->hw.dma2);
-
- portc = kzalloc(sizeof(wavnc_port_info), GFP_KERNEL);
- if (portc == NULL)
- goto nomem;
-
- my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, dev_name,
- &waveartist_audio_driver, sizeof(struct audio_driver),
- devc->audio_flags, AFMT_U8 | AFMT_S16_LE | AFMT_S8,
- devc, devc->hw.dma, devc->hw.dma2);
-
- if (my_dev < 0)
- goto free;
-
- audio_devs[my_dev]->portc = portc;
-
- waveartist_mixer_reset(devc);
-
- /*
- * clear any pending interrupt
- */
- waveartist_iack(devc);
-
- if (request_irq(devc->hw.irq, waveartist_intr, 0, devc->hw.name, devc) < 0) {
- printk(KERN_ERR "%s: IRQ %d in use\n",
- devc->hw.name, devc->hw.irq);
- goto uninstall;
- }
-
- if (sound_alloc_dma(devc->hw.dma, devc->hw.name)) {
- printk(KERN_ERR "%s: Can't allocate DMA%d\n",
- devc->hw.name, devc->hw.dma);
- goto uninstall_irq;
- }
-
- if (devc->hw.dma != devc->hw.dma2 && devc->hw.dma2 != NO_DMA)
- if (sound_alloc_dma(devc->hw.dma2, devc->hw.name)) {
- printk(KERN_ERR "%s: can't allocate DMA%d\n",
- devc->hw.name, devc->hw.dma2);
- goto uninstall_dma;
- }
-
- waveartist_set_ctlr(&devc->hw, 0, DMA1_IE | DMA0_IE);
-
- audio_devs[my_dev]->mixer_dev =
- sound_install_mixer(MIXER_DRIVER_VERSION,
- dev_name,
- &waveartist_mixer_operations,
- sizeof(struct mixer_operations),
- devc);
-
- return my_dev;
-
-uninstall_dma:
- sound_free_dma(devc->hw.dma);
-
-uninstall_irq:
- free_irq(devc->hw.irq, devc);
-
-uninstall:
- sound_unload_audiodev(my_dev);
-
-free:
- kfree(portc);
-
-nomem:
- return -1;
-}
-
-static int __init probe_waveartist(struct address_info *hw_config)
-{
- wavnc_info *devc = &adev_info[nr_waveartist_devs];
-
- if (nr_waveartist_devs >= MAX_AUDIO_DEV) {
- printk(KERN_WARNING "waveartist: too many audio devices\n");
- return 0;
- }
-
- if (!request_region(hw_config->io_base, 15, hw_config->name)) {
- printk(KERN_WARNING "WaveArtist: I/O port conflict\n");
- return 0;
- }
-
- if (hw_config->irq > 15 || hw_config->irq < 0) {
- release_region(hw_config->io_base, 15);
- printk(KERN_WARNING "WaveArtist: Bad IRQ %d\n",
- hw_config->irq);
- return 0;
- }
-
- if (hw_config->dma != 3) {
- release_region(hw_config->io_base, 15);
- printk(KERN_WARNING "WaveArtist: Bad DMA %d\n",
- hw_config->dma);
- return 0;
- }
-
- hw_config->name = "WaveArtist";
- devc->hw = *hw_config;
- devc->open_mode = 0;
- devc->chip_name = "RWA-010";
-
- return 1;
-}
-
-static void __init
-attach_waveartist(struct address_info *hw, const struct waveartist_mixer_info *mix)
-{
- wavnc_info *devc = &adev_info[nr_waveartist_devs];
-
- /*
- * NOTE! If irq < 0, there is another driver which has allocated the
- * IRQ so that this driver doesn't need to allocate/deallocate it.
- * The actually used IRQ is ABS(irq).
- */
- devc->hw = *hw;
- devc->hw.irq = (hw->irq > 0) ? hw->irq : 0;
- devc->open_mode = 0;
- devc->playback_dev = 0;
- devc->record_dev = 0;
- devc->audio_flags = DMA_AUTOMODE;
- devc->levels = levels;
-
- if (hw->dma != hw->dma2 && hw->dma2 != NO_DMA)
- devc->audio_flags |= DMA_DUPLEX;
-
- devc->mix = mix;
- devc->dev_no = waveartist_init(devc);
-
- if (devc->dev_no < 0)
- release_region(hw->io_base, 15);
- else {
-#ifdef CONFIG_ARCH_NETWINDER
- if (machine_is_netwinder()) {
- init_timer(&vnc_timer);
- vnc_timer.function = vnc_slider_tick;
- vnc_timer.expires = jiffies;
- vnc_timer.data = nr_waveartist_devs;
- add_timer(&vnc_timer);
-
- vnc_configure_mixer(devc, 0);
-
- devc->no_autoselect = 1;
- }
-#endif
- nr_waveartist_devs += 1;
- }
-}
-
-static void __exit unload_waveartist(struct address_info *hw)
-{
- wavnc_info *devc = NULL;
- int i;
-
- for (i = 0; i < nr_waveartist_devs; i++)
- if (hw->io_base == adev_info[i].hw.io_base) {
- devc = adev_info + i;
- break;
- }
-
- if (devc != NULL) {
- int mixer;
-
-#ifdef CONFIG_ARCH_NETWINDER
- if (machine_is_netwinder())
- del_timer(&vnc_timer);
-#endif
-
- release_region(devc->hw.io_base, 15);
-
- waveartist_set_ctlr(&devc->hw, DMA1_IE|DMA0_IE, 0);
-
- if (devc->hw.irq >= 0)
- free_irq(devc->hw.irq, devc);
-
- sound_free_dma(devc->hw.dma);
-
- if (devc->hw.dma != devc->hw.dma2 &&
- devc->hw.dma2 != NO_DMA)
- sound_free_dma(devc->hw.dma2);
-
- mixer = audio_devs[devc->dev_no]->mixer_dev;
-
- if (mixer >= 0)
- sound_unload_mixerdev(mixer);
-
- if (devc->dev_no >= 0)
- sound_unload_audiodev(devc->dev_no);
-
- nr_waveartist_devs -= 1;
-
- for (; i < nr_waveartist_devs; i++)
- adev_info[i] = adev_info[i + 1];
- } else
- printk(KERN_WARNING "waveartist: can't find device "
- "to unload\n");
-}
-
-#ifdef CONFIG_ARCH_NETWINDER
-
-/*
- * Rebel.com Netwinder specifics...
- */
-
-#include <asm/hardware/dec21285.h>
-
-#define VNC_TIMER_PERIOD (HZ/4) //check slider 4 times/sec
-
-#define MIXER_PRIVATE3_RESET 0x53570000
-#define MIXER_PRIVATE3_READ 0x53570001
-#define MIXER_PRIVATE3_WRITE 0x53570002
-
-#define VNC_MUTE_INTERNAL_SPKR 0x01 //the sw mute on/off control bit
-#define VNC_MUTE_LINE_OUT 0x10
-#define VNC_PHONE_DETECT 0x20
-#define VNC_HANDSET_DETECT 0x40
-#define VNC_DISABLE_AUTOSWITCH 0x80
-
-static inline void
-vnc_mute_spkr(wavnc_info *devc)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&nw_gpio_lock, flags);
- nw_cpld_modify(CPLD_UNMUTE, devc->spkr_mute_state ? 0 : CPLD_UNMUTE);
- spin_unlock_irqrestore(&nw_gpio_lock, flags);
-}
-
-static void
-vnc_mute_lout(wavnc_info *devc)
-{
- unsigned int left, right;
-
- left = waveartist_cmd1_r(devc, WACMD_GET_LEVEL);
- right = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x400);
-
- if (devc->line_mute_state) {
- left &= ~1;
- right &= ~1;
- } else {
- left |= 1;
- right |= 1;
- }
- waveartist_cmd3(devc, WACMD_SET_MIXER, left, right);
-
-}
-
-static int
-vnc_volume_slider(wavnc_info *devc)
-{
- static signed int old_slider_volume;
- unsigned long flags;
- signed int volume = 255;
-
- *CSR_TIMER1_LOAD = 0x00ffffff;
-
- spin_lock_irqsave(&waveartist_lock, flags);
-
- outb(0xFF, 0x201);
- *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1;
-
- while (volume && (inb(0x201) & 0x01))
- volume--;
-
- *CSR_TIMER1_CNTL = 0;
-
- spin_unlock_irqrestore(&waveartist_lock,flags);
-
- volume = 0x00ffffff - *CSR_TIMER1_VALUE;
-
-
-#ifndef REVERSE
- volume = 150 - (volume >> 5);
-#else
- volume = (volume >> 6) - 25;
-#endif
-
- if (volume < 0)
- volume = 0;
-
- if (volume > 100)
- volume = 100;
-
- /*
- * slider quite often reads +-8, so debounce this random noise
- */
- if (abs(volume - old_slider_volume) > 7) {
- old_slider_volume = volume;
-
- if (debug_flg & DEBUG_MIXER)
- printk(KERN_DEBUG "Slider volume: %d.\n", volume);
- }
-
- return old_slider_volume;
-}
-
-/*
- * Decode a recording mask into a mixer selection on the NetWinder
- * as follows:
- *
- * OSS Source WA Source Actual source
- * SOUND_MASK_IMIX Mixer Mixer output (same as AD1848)
- * SOUND_MASK_LINE Line Line in
- * SOUND_MASK_LINE1 Left Mic Handset
- * SOUND_MASK_PHONEIN Left Aux Telephone microphone
- * SOUND_MASK_MIC Right Mic Builtin microphone
- */
-static unsigned int
-netwinder_select_input(wavnc_info *devc, unsigned int recmask,
- unsigned char *dev_l, unsigned char *dev_r)
-{
- unsigned int recdev_l = ADC_MUX_NONE, recdev_r = ADC_MUX_NONE;
-
- if (recmask & SOUND_MASK_IMIX) {
- recmask = SOUND_MASK_IMIX;
- recdev_l = ADC_MUX_MIXER;
- recdev_r = ADC_MUX_MIXER;
- } else if (recmask & SOUND_MASK_LINE) {
- recmask = SOUND_MASK_LINE;
- recdev_l = ADC_MUX_LINE;
- recdev_r = ADC_MUX_LINE;
- } else if (recmask & SOUND_MASK_LINE1) {
- recmask = SOUND_MASK_LINE1;
- waveartist_cmd1(devc, WACMD_SET_MONO); /* left */
- recdev_l = ADC_MUX_MIC;
- recdev_r = ADC_MUX_NONE;
- } else if (recmask & SOUND_MASK_PHONEIN) {
- recmask = SOUND_MASK_PHONEIN;
- waveartist_cmd1(devc, WACMD_SET_MONO); /* left */
- recdev_l = ADC_MUX_AUX1;
- recdev_r = ADC_MUX_NONE;
- } else if (recmask & SOUND_MASK_MIC) {
- recmask = SOUND_MASK_MIC;
- waveartist_cmd1(devc, WACMD_SET_MONO | 0x100); /* right */
- recdev_l = ADC_MUX_NONE;
- recdev_r = ADC_MUX_MIC;
- }
-
- *dev_l = recdev_l;
- *dev_r = recdev_r;
-
- return recmask;
-}
-
-static int
-netwinder_decode_mixer(wavnc_info *devc, int dev, unsigned char lev_l,
- unsigned char lev_r)
-{
- switch (dev) {
- case SOUND_MIXER_VOLUME:
- case SOUND_MIXER_SYNTH:
- case SOUND_MIXER_PCM:
- case SOUND_MIXER_LINE:
- case SOUND_MIXER_IGAIN:
- devc->levels[dev] = lev_l | lev_r << 8;
- break;
-
- case SOUND_MIXER_MIC: /* right mic only */
- devc->levels[SOUND_MIXER_MIC] &= 0xff;
- devc->levels[SOUND_MIXER_MIC] |= lev_l << 8;
- break;
-
- case SOUND_MIXER_LINE1: /* left mic only */
- devc->levels[SOUND_MIXER_MIC] &= 0xff00;
- devc->levels[SOUND_MIXER_MIC] |= lev_l;
- dev = SOUND_MIXER_MIC;
- break;
-
- case SOUND_MIXER_PHONEIN: /* left aux only */
- devc->levels[SOUND_MIXER_LINE1] = lev_l;
- dev = SOUND_MIXER_LINE1;
- break;
-
- case SOUND_MIXER_IMIX:
- case SOUND_MIXER_PHONEOUT:
- break;
-
- default:
- dev = -EINVAL;
- break;
- }
- return dev;
-}
-
-static int netwinder_get_mixer(wavnc_info *devc, int dev)
-{
- int levels;
-
- switch (dev) {
- case SOUND_MIXER_VOLUME:
- case SOUND_MIXER_SYNTH:
- case SOUND_MIXER_PCM:
- case SOUND_MIXER_LINE:
- case SOUND_MIXER_IGAIN:
- levels = devc->levels[dev];
- break;
-
- case SOUND_MIXER_MIC: /* builtin mic: right mic only */
- levels = devc->levels[SOUND_MIXER_MIC] >> 8;
- levels |= levels << 8;
- break;
-
- case SOUND_MIXER_LINE1: /* handset mic: left mic only */
- levels = devc->levels[SOUND_MIXER_MIC] & 0xff;
- levels |= levels << 8;
- break;
-
- case SOUND_MIXER_PHONEIN: /* phone mic: left aux1 only */
- levels = devc->levels[SOUND_MIXER_LINE1] & 0xff;
- levels |= levels << 8;
- break;
-
- default:
- levels = 0;
- }
-
- return levels;
-}
-
-/*
- * Waveartist specific mixer information.
- */
-static const struct waveartist_mixer_info netwinder_mixer = {
- .supported_devs = SOUND_MASK_VOLUME | SOUND_MASK_SYNTH |
- SOUND_MASK_PCM | SOUND_MASK_SPEAKER |
- SOUND_MASK_LINE | SOUND_MASK_MIC |
- SOUND_MASK_IMIX | SOUND_MASK_LINE1 |
- SOUND_MASK_PHONEIN | SOUND_MASK_PHONEOUT|
- SOUND_MASK_IGAIN,
-
- .recording_devs = SOUND_MASK_LINE | SOUND_MASK_MIC |
- SOUND_MASK_IMIX | SOUND_MASK_LINE1 |
- SOUND_MASK_PHONEIN,
-
- .stereo_devs = SOUND_MASK_VOLUME | SOUND_MASK_SYNTH |
- SOUND_MASK_PCM | SOUND_MASK_LINE |
- SOUND_MASK_IMIX | SOUND_MASK_IGAIN,
-
- .select_input = netwinder_select_input,
- .decode_mixer = netwinder_decode_mixer,
- .get_mixer = netwinder_get_mixer,
-};
-
-static void
-vnc_configure_mixer(wavnc_info *devc, unsigned int recmask)
-{
- if (!devc->no_autoselect) {
- if (devc->handset_detect) {
- recmask = SOUND_MASK_LINE1;
- devc->spkr_mute_state = devc->line_mute_state = 1;
- } else if (devc->telephone_detect) {
- recmask = SOUND_MASK_PHONEIN;
- devc->spkr_mute_state = devc->line_mute_state = 1;
- } else {
- /* unless someone has asked for LINE-IN,
- * we default to MIC
- */
- if ((devc->recmask & SOUND_MASK_LINE) == 0)
- devc->recmask = SOUND_MASK_MIC;
- devc->spkr_mute_state = devc->line_mute_state = 0;
- }
- vnc_mute_spkr(devc);
- vnc_mute_lout(devc);
-
- if (recmask != devc->recmask)
- waveartist_set_recmask(devc, recmask);
- }
-}
-
-static int
-vnc_slider(wavnc_info *devc)
-{
- signed int slider_volume;
- unsigned int temp, old_hs, old_td;
-
- /*
- * read the "buttons" state.
- * Bit 4 = 0 means handset present
- * Bit 5 = 1 means phone offhook
- */
- temp = inb(0x201);
-
- old_hs = devc->handset_detect;
- old_td = devc->telephone_detect;
-
- devc->handset_detect = !(temp & 0x10);
- devc->telephone_detect = !!(temp & 0x20);
-
- if (!devc->no_autoselect &&
- (old_hs != devc->handset_detect ||
- old_td != devc->telephone_detect))
- vnc_configure_mixer(devc, devc->recmask);
-
- slider_volume = vnc_volume_slider(devc);
-
- /*
- * If we're using software controlled volume, and
- * the slider moves by more than 20%, then we
- * switch back to slider controlled volume.
- */
- if (abs(devc->slider_vol - slider_volume) > 20)
- devc->use_slider = 1;
-
- /*
- * use only left channel
- */
- temp = levels[SOUND_MIXER_VOLUME] & 0xFF;
-
- if (slider_volume != temp && devc->use_slider) {
- devc->slider_vol = slider_volume;
-
- waveartist_set_mixer(devc, SOUND_MIXER_VOLUME,
- slider_volume | slider_volume << 8);
-
- return 1;
- }
-
- return 0;
-}
-
-static void
-vnc_slider_tick(unsigned long data)
-{
- int next_timeout;
-
- if (vnc_slider(adev_info + data))
- next_timeout = 5; // mixer reported change
- else
- next_timeout = VNC_TIMER_PERIOD;
-
- mod_timer(&vnc_timer, jiffies + next_timeout);
-}
-
-static int
-vnc_private_ioctl(int dev, unsigned int cmd, int __user * arg)
-{
- wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;
- int val;
-
- switch (cmd) {
- case SOUND_MIXER_PRIVATE1:
- {
- u_int prev_spkr_mute, prev_line_mute, prev_auto_state;
- int val;
-
- if (get_user(val, arg))
- return -EFAULT;
-
- /* check if parameter is logical */
- if (val & ~(VNC_MUTE_INTERNAL_SPKR |
- VNC_MUTE_LINE_OUT |
- VNC_DISABLE_AUTOSWITCH))
- return -EINVAL;
-
- prev_auto_state = devc->no_autoselect;
- prev_spkr_mute = devc->spkr_mute_state;
- prev_line_mute = devc->line_mute_state;
-
- devc->no_autoselect = (val & VNC_DISABLE_AUTOSWITCH) ? 1 : 0;
- devc->spkr_mute_state = (val & VNC_MUTE_INTERNAL_SPKR) ? 1 : 0;
- devc->line_mute_state = (val & VNC_MUTE_LINE_OUT) ? 1 : 0;
-
- if (prev_spkr_mute != devc->spkr_mute_state)
- vnc_mute_spkr(devc);
-
- if (prev_line_mute != devc->line_mute_state)
- vnc_mute_lout(devc);
-
- if (prev_auto_state != devc->no_autoselect)
- vnc_configure_mixer(devc, devc->recmask);
-
- return 0;
- }
-
- case SOUND_MIXER_PRIVATE2:
- if (get_user(val, arg))
- return -EFAULT;
-
- switch (val) {
-#define VNC_SOUND_PAUSE 0x53 //to pause the DSP
-#define VNC_SOUND_RESUME 0x57 //to unpause the DSP
- case VNC_SOUND_PAUSE:
- waveartist_cmd1(devc, 0x16);
- break;
-
- case VNC_SOUND_RESUME:
- waveartist_cmd1(devc, 0x18);
- break;
-
- default:
- return -EINVAL;
- }
- return 0;
-
- /* private ioctl to allow bulk access to waveartist */
- case SOUND_MIXER_PRIVATE3:
- {
- unsigned long flags;
- int mixer_reg[15], i, val;
-
- if (get_user(val, arg))
- return -EFAULT;
- if (copy_from_user(mixer_reg, (void *)val, sizeof(mixer_reg)))
- return -EFAULT;
-
- switch (mixer_reg[14]) {
- case MIXER_PRIVATE3_RESET:
- waveartist_mixer_reset(devc);
- break;
-
- case MIXER_PRIVATE3_WRITE:
- waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[0], mixer_reg[4]);
- waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[1], mixer_reg[5]);
- waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[2], mixer_reg[6]);
- waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[3], mixer_reg[7]);
- waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[8], mixer_reg[9]);
-
- waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[10], mixer_reg[11]);
- waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[12], mixer_reg[13]);
- break;
-
- case MIXER_PRIVATE3_READ:
- spin_lock_irqsave(&waveartist_lock, flags);
-
- for (i = 0x30; i < 14 << 8; i += 1 << 8)
- waveartist_cmd(devc, 1, &i, 1, mixer_reg + (i >> 8));
-
- spin_unlock_irqrestore(&waveartist_lock, flags);
-
- if (copy_to_user((void *)val, mixer_reg, sizeof(mixer_reg)))
- return -EFAULT;
- break;
-
- default:
- return -EINVAL;
- }
- return 0;
- }
-
- /* read back the state from PRIVATE1 */
- case SOUND_MIXER_PRIVATE4:
- val = (devc->spkr_mute_state ? VNC_MUTE_INTERNAL_SPKR : 0) |
- (devc->line_mute_state ? VNC_MUTE_LINE_OUT : 0) |
- (devc->handset_detect ? VNC_HANDSET_DETECT : 0) |
- (devc->telephone_detect ? VNC_PHONE_DETECT : 0) |
- (devc->no_autoselect ? VNC_DISABLE_AUTOSWITCH : 0);
-
- return put_user(val, arg) ? -EFAULT : 0;
- }
-
- if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
- /*
- * special case for master volume: if we
- * received this call - switch from hw
- * volume control to a software volume
- * control, till the hw volume is modified
- * to signal that user wants to be back in
- * hardware...
- */
- if ((cmd & 0xff) == SOUND_MIXER_VOLUME)
- devc->use_slider = 0;
-
- /* speaker output */
- if ((cmd & 0xff) == SOUND_MIXER_SPEAKER) {
- unsigned int val, l, r;
-
- if (get_user(val, arg))
- return -EFAULT;
-
- l = val & 0x7f;
- r = (val & 0x7f00) >> 8;
- val = (l + r) / 2;
- devc->levels[SOUND_MIXER_SPEAKER] = val | (val << 8);
- devc->spkr_mute_state = (val <= 50);
- vnc_mute_spkr(devc);
- return 0;
- }
- }
-
- return -ENOIOCTLCMD;
-}
-
-#endif
-
-static struct address_info cfg;
-
-static int attached;
-
-static int __initdata io = 0;
-static int __initdata irq = 0;
-static int __initdata dma = 0;
-static int __initdata dma2 = 0;
-
-
-static int __init init_waveartist(void)
-{
- const struct waveartist_mixer_info *mix;
-
- if (!io && machine_is_netwinder()) {
- /*
- * The NetWinder WaveArtist is at a fixed address.
- * If the user does not supply an address, use the
- * well-known parameters.
- */
- io = 0x250;
- irq = 12;
- dma = 3;
- dma2 = 7;
- }
-
- mix = &waveartist_mixer;
-#ifdef CONFIG_ARCH_NETWINDER
- if (machine_is_netwinder())
- mix = &netwinder_mixer;
-#endif
-
- cfg.io_base = io;
- cfg.irq = irq;
- cfg.dma = dma;
- cfg.dma2 = dma2;
-
- if (!probe_waveartist(&cfg))
- return -ENODEV;
-
- attach_waveartist(&cfg, mix);
- attached = 1;
-
- return 0;
-}
-
-static void __exit cleanup_waveartist(void)
-{
- if (attached)
- unload_waveartist(&cfg);
-}
-
-module_init(init_waveartist);
-module_exit(cleanup_waveartist);
-
-#ifndef MODULE
-static int __init setup_waveartist(char *str)
-{
- /* io, irq, dma, dma2 */
- int ints[5];
-
- str = get_options(str, ARRAY_SIZE(ints), ints);
-
- io = ints[1];
- irq = ints[2];
- dma = ints[3];
- dma2 = ints[4];
-
- return 1;
-}
-__setup("waveartist=", setup_waveartist);
-#endif
-
-MODULE_DESCRIPTION("Rockwell WaveArtist RWA-010 sound driver");
-module_param(io, int, 0); /* IO base */
-module_param(irq, int, 0); /* IRQ */
-module_param(dma, int, 0); /* DMA */
-module_param(dma2, int, 0); /* DMA2 */
-MODULE_LICENSE("GPL");
diff --git a/sound/oss/waveartist.h b/sound/oss/waveartist.h
deleted file mode 100644
index dac4ca910d95..000000000000
--- a/sound/oss/waveartist.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * linux/sound/oss/waveartist.h
- *
- * def file for Rockwell RWA010 chip set, as installed in Rebel.com NetWinder
- */
-
-//registers
-#define CMDR 0
-#define DATR 2
-#define CTLR 4
-#define STATR 5
-#define IRQSTAT 12
-
-//bit defs
-//reg STATR
-#define CMD_WE 0x80
-#define CMD_RF 0x40
-#define DAT_WE 0x20
-#define DAT_RF 0x10
-
-#define IRQ_REQ 0x08
-#define DMA1 0x04
-#define DMA0 0x02
-
-//bit defs
-//reg CTLR
-#define CMD_WEIE 0x80
-#define CMD_RFIE 0x40
-#define DAT_WEIE 0x20
-#define DAT_RFIE 0x10
-
-#define RESET 0x08
-#define DMA1_IE 0x04
-#define DMA0_IE 0x02
-#define IRQ_ACK 0x01
-
-//commands
-
-#define WACMD_SYSTEMID 0x00
-#define WACMD_GETREV 0x00
-#define WACMD_INPUTFORMAT 0x10 //0-8S, 1-16S, 2-8U
-#define WACMD_INPUTCHANNELS 0x11 //1-Mono, 2-Stereo
-#define WACMD_INPUTSPEED 0x12 //sampling rate
-#define WACMD_INPUTDMA 0x13 //0-8bit, 1-16bit, 2-PIO
-#define WACMD_INPUTSIZE 0x14 //samples to interrupt
-#define WACMD_INPUTSTART 0x15 //start ADC
-#define WACMD_INPUTPAUSE 0x16 //pause ADC
-#define WACMD_INPUTSTOP 0x17 //stop ADC
-#define WACMD_INPUTRESUME 0x18 //resume ADC
-#define WACMD_INPUTPIO 0x19 //PIO ADC
-
-#define WACMD_OUTPUTFORMAT 0x20 //0-8S, 1-16S, 2-8U
-#define WACMD_OUTPUTCHANNELS 0x21 //1-Mono, 2-Stereo
-#define WACMD_OUTPUTSPEED 0x22 //sampling rate
-#define WACMD_OUTPUTDMA 0x23 //0-8bit, 1-16bit, 2-PIO
-#define WACMD_OUTPUTSIZE 0x24 //samples to interrupt
-#define WACMD_OUTPUTSTART 0x25 //start ADC
-#define WACMD_OUTPUTPAUSE 0x26 //pause ADC
-#define WACMD_OUTPUTSTOP 0x27 //stop ADC
-#define WACMD_OUTPUTRESUME 0x28 //resume ADC
-#define WACMD_OUTPUTPIO 0x29 //PIO ADC
-
-#define WACMD_GET_LEVEL 0x30
-#define WACMD_SET_LEVEL 0x31
-#define WACMD_SET_MIXER 0x32
-#define WACMD_RST_MIXER 0x33
-#define WACMD_SET_MONO 0x34
-
-/*
- * Definitions for left/right recording input mux
- */
-#define ADC_MUX_NONE 0
-#define ADC_MUX_MIXER 1
-#define ADC_MUX_LINE 2
-#define ADC_MUX_AUX2 3
-#define ADC_MUX_AUX1 4
-#define ADC_MUX_MIC 5
-
-/*
- * Definitions for mixer gain settings
- */
-#define MIX_GAIN_LINE 0 /* line in */
-#define MIX_GAIN_AUX1 1 /* aux1 */
-#define MIX_GAIN_AUX2 2 /* aux2 */
-#define MIX_GAIN_XMIC 3 /* crossover mic */
-#define MIX_GAIN_MIC 4 /* normal mic */
-#define MIX_GAIN_PREMIC 5 /* preamp mic */
-#define MIX_GAIN_OUT 6 /* output */
-#define MIX_GAIN_MONO 7 /* mono in */
-
-int wa_sendcmd(unsigned int cmd);
-int wa_writecmd(unsigned int cmd, unsigned int arg);
diff --git a/sound/parisc/Kconfig b/sound/parisc/Kconfig
index 9b61d95010f0..425ceaf9c990 100644
--- a/sound/parisc/Kconfig
+++ b/sound/parisc/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# ALSA PA-RISC drivers
menuconfig SND_GSC
diff --git a/sound/parisc/Makefile b/sound/parisc/Makefile
index b91e750aba02..84c71490fb72 100644
--- a/sound/parisc/Makefile
+++ b/sound/parisc/Makefile
@@ -1,8 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
#
-snd-harmony-objs := harmony.o
+snd-harmony-y := harmony.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_HARMONY) += snd-harmony.o
diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c
index f47f9e226b08..4b5a54da25fb 100644
--- a/sound/parisc/harmony.c
+++ b/sound/parisc/harmony.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Hewlett-Packard Harmony audio driver
*
* This is a driver for the Harmony audio chipset found
@@ -13,26 +14,12 @@
* Copyright 2003 (c) Laurent Canet
* Copyright 2004 (c) Stuart Brady
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2, as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
* Notes:
* - graveyard and silence buffers last for lifetime of
* the driver. playback and capture buffers are allocated
* per _open()/_close().
*
* TODO:
- *
*/
#include <linux/init.h>
@@ -44,6 +31,7 @@
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/dma-mapping.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -52,7 +40,6 @@
#include <sound/initval.h>
#include <sound/info.h>
-#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/parisc-device.h>
@@ -66,7 +53,7 @@ module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for Harmony driver.");
-static struct parisc_device_id snd_harmony_devtable[] = {
+static const struct parisc_device_id snd_harmony_devtable[] __initconst = {
/* bushmaster / flounder */
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007A },
/* 712 / 715 */
@@ -83,14 +70,14 @@ MODULE_DEVICE_TABLE(parisc, snd_harmony_devtable);
#define NAME "harmony"
#define PFX NAME ": "
-static unsigned int snd_harmony_rates[] = {
+static const unsigned int snd_harmony_rates[] = {
5512, 6615, 8000, 9600,
11025, 16000, 18900, 22050,
27428, 32000, 33075, 37800,
44100, 48000
};
-static unsigned int rate_bits[14] = {
+static const unsigned int rate_bits[14] = {
HARMONY_SR_5KHZ, HARMONY_SR_6KHZ, HARMONY_SR_8KHZ,
HARMONY_SR_9KHZ, HARMONY_SR_11KHZ, HARMONY_SR_16KHZ,
HARMONY_SR_18KHZ, HARMONY_SR_22KHZ, HARMONY_SR_27KHZ,
@@ -98,7 +85,7 @@ static unsigned int rate_bits[14] = {
HARMONY_SR_44KHZ, HARMONY_SR_48KHZ
};
-static struct snd_pcm_hw_constraint_list hw_constraint_rates = {
+static const struct snd_pcm_hw_constraint_list hw_constraint_rates = {
.count = ARRAY_SIZE(snd_harmony_rates),
.list = snd_harmony_rates,
.mask = 0,
@@ -153,32 +140,25 @@ harmony_enable_interrupts(struct snd_harmony *h)
static void
harmony_mute(struct snd_harmony *h)
{
- unsigned long flags;
-
- spin_lock_irqsave(&h->mixer_lock, flags);
+ guard(spinlock_irqsave)(&h->mixer_lock);
harmony_wait_for_control(h);
harmony_write(h, HARMONY_GAINCTL, HARMONY_GAIN_SILENCE);
- spin_unlock_irqrestore(&h->mixer_lock, flags);
}
static void
harmony_unmute(struct snd_harmony *h)
{
- unsigned long flags;
-
- spin_lock_irqsave(&h->mixer_lock, flags);
+ guard(spinlock_irqsave)(&h->mixer_lock);
harmony_wait_for_control(h);
harmony_write(h, HARMONY_GAINCTL, h->st.gain);
- spin_unlock_irqrestore(&h->mixer_lock, flags);
}
static void
harmony_set_control(struct snd_harmony *h)
{
u32 ctrl;
- unsigned long flags;
- spin_lock_irqsave(&h->lock, flags);
+ guard(spinlock_irqsave)(&h->lock);
ctrl = (HARMONY_CNTL_C |
(h->st.format << 6) |
@@ -187,8 +167,6 @@ harmony_set_control(struct snd_harmony *h)
harmony_wait_for_control(h);
harmony_write(h, HARMONY_CNTL, ctrl);
-
- spin_unlock_irqrestore(&h->lock, flags);
}
static irqreturn_t
@@ -197,53 +175,53 @@ snd_harmony_interrupt(int irq, void *dev)
u32 dstatus;
struct snd_harmony *h = dev;
- spin_lock(&h->lock);
- harmony_disable_interrupts(h);
- harmony_wait_for_control(h);
- dstatus = harmony_read(h, HARMONY_DSTATUS);
- spin_unlock(&h->lock);
+ scoped_guard(spinlock, &h->lock) {
+ harmony_disable_interrupts(h);
+ harmony_wait_for_control(h);
+ dstatus = harmony_read(h, HARMONY_DSTATUS);
+ }
if (dstatus & HARMONY_DSTATUS_PN) {
if (h->psubs && h->st.playing) {
- spin_lock(&h->lock);
- h->pbuf.buf += h->pbuf.count; /* PAGE_SIZE */
- h->pbuf.buf %= h->pbuf.size; /* MAX_BUFS*PAGE_SIZE */
-
- harmony_write(h, HARMONY_PNXTADD,
- h->pbuf.addr + h->pbuf.buf);
- h->stats.play_intr++;
- spin_unlock(&h->lock);
+ scoped_guard(spinlock, &h->lock) {
+ h->pbuf.buf += h->pbuf.count; /* PAGE_SIZE */
+ h->pbuf.buf %= h->pbuf.size; /* MAX_BUFS*PAGE_SIZE */
+
+ harmony_write(h, HARMONY_PNXTADD,
+ h->pbuf.addr + h->pbuf.buf);
+ h->stats.play_intr++;
+ }
snd_pcm_period_elapsed(h->psubs);
} else {
- spin_lock(&h->lock);
- harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);
- h->stats.silence_intr++;
- spin_unlock(&h->lock);
+ scoped_guard(spinlock, &h->lock) {
+ harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);
+ h->stats.silence_intr++;
+ }
}
}
if (dstatus & HARMONY_DSTATUS_RN) {
if (h->csubs && h->st.capturing) {
- spin_lock(&h->lock);
- h->cbuf.buf += h->cbuf.count;
- h->cbuf.buf %= h->cbuf.size;
-
- harmony_write(h, HARMONY_RNXTADD,
- h->cbuf.addr + h->cbuf.buf);
- h->stats.rec_intr++;
- spin_unlock(&h->lock);
+ scoped_guard(spinlock, &h->lock) {
+ h->cbuf.buf += h->cbuf.count;
+ h->cbuf.buf %= h->cbuf.size;
+
+ harmony_write(h, HARMONY_RNXTADD,
+ h->cbuf.addr + h->cbuf.buf);
+ h->stats.rec_intr++;
+ }
snd_pcm_period_elapsed(h->csubs);
} else {
- spin_lock(&h->lock);
- harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);
- h->stats.graveyard_intr++;
- spin_unlock(&h->lock);
+ scoped_guard(spinlock, &h->lock) {
+ harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);
+ h->stats.graveyard_intr++;
+ }
}
}
- spin_lock(&h->lock);
- harmony_enable_interrupts(h);
- spin_unlock(&h->lock);
+ scoped_guard(spinlock, &h->lock) {
+ harmony_enable_interrupts(h);
+ }
return IRQ_HANDLED;
}
@@ -260,7 +238,7 @@ snd_harmony_rate_bits(int rate)
return HARMONY_SR_44KHZ;
}
-static struct snd_pcm_hardware snd_harmony_playback =
+static const struct snd_pcm_hardware snd_harmony_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP_VALID |
@@ -281,7 +259,7 @@ static struct snd_pcm_hardware snd_harmony_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_harmony_capture =
+static const struct snd_pcm_hardware snd_harmony_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP_VALID |
@@ -310,7 +288,7 @@ snd_harmony_playback_trigger(struct snd_pcm_substream *ss, int cmd)
if (h->st.capturing)
return -EBUSY;
- spin_lock(&h->lock);
+ guard(spinlock)(&h->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
h->st.playing = 1;
@@ -329,11 +307,9 @@ snd_harmony_playback_trigger(struct snd_pcm_substream *ss, int cmd)
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_SUSPEND:
default:
- spin_unlock(&h->lock);
snd_BUG();
return -EINVAL;
}
- spin_unlock(&h->lock);
return 0;
}
@@ -346,7 +322,7 @@ snd_harmony_capture_trigger(struct snd_pcm_substream *ss, int cmd)
if (h->st.playing)
return -EBUSY;
- spin_lock(&h->lock);
+ guard(spinlock)(&h->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
h->st.capturing = 1;
@@ -365,11 +341,9 @@ snd_harmony_capture_trigger(struct snd_pcm_substream *ss, int cmd)
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_SUSPEND:
default:
- spin_unlock(&h->lock);
snd_BUG();
return -EINVAL;
}
- spin_unlock(&h->lock);
return 0;
}
@@ -576,43 +550,17 @@ snd_harmony_capture_close(struct snd_pcm_substream *ss)
return 0;
}
-static int
-snd_harmony_hw_params(struct snd_pcm_substream *ss,
- struct snd_pcm_hw_params *hw)
-{
- int err;
- struct snd_harmony *h = snd_pcm_substream_chip(ss);
-
- err = snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw));
- if (err > 0 && h->dma.type == SNDRV_DMA_TYPE_CONTINUOUS)
- ss->runtime->dma_addr = __pa(ss->runtime->dma_area);
-
- return err;
-}
-
-static int
-snd_harmony_hw_free(struct snd_pcm_substream *ss)
-{
- return snd_pcm_lib_free_pages(ss);
-}
-
-static struct snd_pcm_ops snd_harmony_playback_ops = {
+static const struct snd_pcm_ops snd_harmony_playback_ops = {
.open = snd_harmony_playback_open,
.close = snd_harmony_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_harmony_hw_params,
- .hw_free = snd_harmony_hw_free,
.prepare = snd_harmony_playback_prepare,
.trigger = snd_harmony_playback_trigger,
.pointer = snd_harmony_playback_pointer,
};
-static struct snd_pcm_ops snd_harmony_capture_ops = {
+static const struct snd_pcm_ops snd_harmony_capture_ops = {
.open = snd_harmony_capture_open,
.close = snd_harmony_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_harmony_hw_params,
- .hw_free = snd_harmony_hw_free,
.prepare = snd_harmony_capture_prepare,
.trigger = snd_harmony_capture_trigger,
.pointer = snd_harmony_capture_pointer,
@@ -640,7 +588,7 @@ snd_harmony_pcm_init(struct snd_harmony *h)
pcm->private_data = h;
pcm->info_flags = 0;
- strcpy(pcm->name, "harmony");
+ strscpy(pcm->name, "harmony");
h->pcm = pcm;
h->psubs = NULL;
@@ -669,14 +617,8 @@ snd_harmony_pcm_init(struct snd_harmony *h)
}
/* pre-allocate space for DMA */
- err = snd_pcm_lib_preallocate_pages_for_all(pcm, h->dma.type,
- h->dma.dev,
- MAX_BUF_SIZE,
- MAX_BUF_SIZE);
- if (err < 0) {
- printk(KERN_ERR PFX "buffer allocation error: %d\n", err);
- return err;
- }
+ snd_pcm_set_managed_buffer_all(pcm, h->dma.type, h->dma.dev,
+ MAX_BUF_SIZE, MAX_BUF_SIZE);
h->st.format = snd_harmony_set_data_format(h,
SNDRV_PCM_FORMAT_S16_BE, 1);
@@ -719,7 +661,7 @@ snd_harmony_volume_get(struct snd_kcontrol *kc,
int invert = (kc->private_value >> 24) & 0xff;
int left, right;
- spin_lock_irq(&h->mixer_lock);
+ guard(spinlock_irq)(&h->mixer_lock);
left = (h->st.gain >> shift_left) & mask;
right = (h->st.gain >> shift_right) & mask;
@@ -732,8 +674,6 @@ snd_harmony_volume_get(struct snd_kcontrol *kc,
if (shift_left != shift_right)
ucontrol->value.integer.value[1] = right;
- spin_unlock_irq(&h->mixer_lock);
-
return 0;
}
@@ -749,7 +689,7 @@ snd_harmony_volume_put(struct snd_kcontrol *kc,
int left, right;
int old_gain = h->st.gain;
- spin_lock_irq(&h->mixer_lock);
+ guard(spinlock_irq)(&h->mixer_lock);
left = ucontrol->value.integer.value[0] & mask;
if (invert)
@@ -767,8 +707,6 @@ snd_harmony_volume_put(struct snd_kcontrol *kc,
snd_harmony_set_new_gain(h);
- spin_unlock_irq(&h->mixer_lock);
-
return h->st.gain != old_gain;
}
@@ -776,15 +714,9 @@ static int
snd_harmony_captureroute_info(struct snd_kcontrol *kc,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "Line", "Mic" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ static const char * const texts[2] = { "Line", "Mic" };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int
@@ -794,13 +726,11 @@ snd_harmony_captureroute_get(struct snd_kcontrol *kc,
struct snd_harmony *h = snd_kcontrol_chip(kc);
int value;
- spin_lock_irq(&h->mixer_lock);
+ guard(spinlock_irq)(&h->mixer_lock);
value = (h->st.gain >> HARMONY_GAIN_IS_SHIFT) & 1;
ucontrol->value.enumerated.item[0] = value;
- spin_unlock_irq(&h->mixer_lock);
-
return 0;
}
@@ -812,7 +742,7 @@ snd_harmony_captureroute_put(struct snd_kcontrol *kc,
int value;
int old_gain = h->st.gain;
- spin_lock_irq(&h->mixer_lock);
+ guard(spinlock_irq)(&h->mixer_lock);
value = ucontrol->value.enumerated.item[0] & 1;
h->st.gain &= ~HARMONY_GAIN_IS_MASK;
@@ -820,8 +750,6 @@ snd_harmony_captureroute_put(struct snd_kcontrol *kc,
snd_harmony_set_new_gain(h);
- spin_unlock_irq(&h->mixer_lock);
-
return h->st.gain != old_gain;
}
@@ -834,7 +762,7 @@ snd_harmony_captureroute_put(struct snd_kcontrol *kc,
.private_value = ((left_shift) | ((right_shift) << 8) | \
((mask) << 16) | ((invert) << 24)) }
-static struct snd_kcontrol_new snd_harmony_controls[] = {
+static const struct snd_kcontrol_new snd_harmony_controls[] = {
HARMONY_VOLUME("Master Playback Volume", HARMONY_GAIN_LO_SHIFT,
HARMONY_GAIN_RO_SHIFT, HARMONY_GAIN_OUT, 1),
HARMONY_VOLUME("Capture Volume", HARMONY_GAIN_LI_SHIFT,
@@ -856,7 +784,7 @@ static struct snd_kcontrol_new snd_harmony_controls[] = {
HARMONY_GAIN_HE_SHIFT, 1, 0),
};
-static void __devinit
+static void
snd_harmony_mixer_reset(struct snd_harmony *h)
{
harmony_mute(h);
@@ -865,7 +793,7 @@ snd_harmony_mixer_reset(struct snd_harmony *h)
harmony_unmute(h);
}
-static int __devinit
+static int
snd_harmony_mixer_init(struct snd_harmony *h)
{
struct snd_card *card;
@@ -874,7 +802,7 @@ snd_harmony_mixer_init(struct snd_harmony *h)
if (snd_BUG_ON(!h))
return -EINVAL;
card = h->card;
- strcpy(card->mixername, "Harmony Gain control interface");
+ strscpy(card->mixername, "Harmony Gain control interface");
for (idx = 0; idx < HARMONY_CONTROLS; idx++) {
err = snd_ctl_add(card,
@@ -899,11 +827,7 @@ snd_harmony_free(struct snd_harmony *h)
if (h->irq >= 0)
free_irq(h->irq, h);
- if (h->iobase)
- iounmap(h->iobase);
-
- parisc_set_drvdata(h->dev, NULL);
-
+ iounmap(h->iobase);
kfree(h);
return 0;
}
@@ -915,14 +839,14 @@ snd_harmony_dev_free(struct snd_device *dev)
return snd_harmony_free(h);
}
-static int __devinit
+static int
snd_harmony_create(struct snd_card *card,
struct parisc_device *padev,
struct snd_harmony **rchip)
{
int err;
struct snd_harmony *h;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_harmony_dev_free,
};
@@ -936,7 +860,7 @@ snd_harmony_create(struct snd_card *card,
h->card = card;
h->dev = padev;
h->irq = -1;
- h->iobase = ioremap_nocache(padev->hpa.start, HARMONY_SIZE);
+ h->iobase = ioremap(padev->hpa.start, HARMONY_SIZE);
if (h->iobase == NULL) {
printk(KERN_ERR PFX "unable to remap hpa 0x%lx\n",
(unsigned long)padev->hpa.start);
@@ -956,12 +880,9 @@ snd_harmony_create(struct snd_card *card,
spin_lock_init(&h->mixer_lock);
spin_lock_init(&h->lock);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
- h, &ops)) < 0) {
- goto free_and_ret;
- }
-
- snd_card_set_dev(card, &padev->dev);
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, h, &ops);
+ if (err < 0)
+ goto free_and_ret;
*rchip = h;
@@ -972,14 +893,14 @@ free_and_ret:
return err;
}
-static int __devinit
+static int __init
snd_harmony_probe(struct parisc_device *padev)
{
int err;
struct snd_card *card;
struct snd_harmony *h;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_card_new(&padev->dev, index, id, THIS_MODULE, 0, &card);
if (err < 0)
return err;
@@ -995,8 +916,8 @@ snd_harmony_probe(struct parisc_device *padev)
if (err < 0)
goto free_and_ret;
- strcpy(card->driver, "harmony");
- strcpy(card->shortname, "Harmony");
+ strscpy(card->driver, "harmony");
+ strscpy(card->shortname, "Harmony");
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, h->hpa, h->irq);
@@ -1012,19 +933,17 @@ free_and_ret:
return err;
}
-static int __devexit
+static void __exit
snd_harmony_remove(struct parisc_device *padev)
{
snd_card_free(parisc_get_drvdata(padev));
- parisc_set_drvdata(padev, NULL);
- return 0;
}
-static struct parisc_driver snd_harmony_driver = {
+static struct parisc_driver snd_harmony_driver __refdata = {
.name = "harmony",
.id_table = snd_harmony_devtable,
.probe = snd_harmony_probe,
- .remove = __devexit_p(snd_harmony_remove),
+ .remove = __exit_p(snd_harmony_remove),
};
static int __init
diff --git a/sound/parisc/harmony.h b/sound/parisc/harmony.h
index 2e434523fedf..f4c29a2e32e8 100644
--- a/sound/parisc/harmony.h
+++ b/sound/parisc/harmony.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Hewlett-Packard Harmony audio driver
* Copyright (C) 2004, Kyle McMartin <kyle@parisc-linux.org>
*/
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 12e34653b8a8..e0996a9d90b0 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# ALSA PCI drivers
menuconfig SND_PCI
@@ -25,6 +26,7 @@ config SND_ALS300
select SND_PCM
select SND_AC97_CODEC
select SND_OPL3_LIB
+ depends on ZONE_DMA && HAS_IOPORT
help
Say 'Y' or 'M' to include support for Avance Logic ALS300/ALS300+
@@ -34,6 +36,7 @@ config SND_ALS300
config SND_ALS4000
tristate "Avance Logic ALS4000"
depends on ISA_DMA_API
+ depends on HAS_IOPORT
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_PCM
@@ -49,6 +52,7 @@ config SND_ALI5451
tristate "ALi M5451 PCI Audio Controller"
select SND_MPU401_UART
select SND_AC97_CODEC
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y here to include support for the integrated AC97 sound
device on motherboards using the ALi M5451 Audio Controller
@@ -93,6 +97,7 @@ config SND_ATIIXP_MODEM
config SND_AU8810
tristate "Aureal Advantage"
+ depends on HAS_IOPORT
select SND_MPU401_UART
select SND_AC97_CODEC
help
@@ -107,6 +112,7 @@ config SND_AU8810
config SND_AU8820
tristate "Aureal Vortex"
+ depends on HAS_IOPORT
select SND_MPU401_UART
select SND_AC97_CODEC
help
@@ -120,6 +126,7 @@ config SND_AU8820
config SND_AU8830
tristate "Aureal Vortex 2"
+ depends on HAS_IOPORT
select SND_MPU401_UART
select SND_AC97_CODEC
help
@@ -152,10 +159,18 @@ config SND_AZT3328
select SND_MPU401_UART
select SND_PCM
select SND_RAWMIDI
+ select SND_AC97_CODEC
+ select SND_TIMER
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y here to include support for Aztech AZF3328 (PCI168)
soundcards.
+ Supported features: AC97-"conformant" mixer, MPU401/OPL3, analog I/O
+ (16bit/8bit, many sample rates [<= 66.2kHz], NO hardware mixing),
+ Digital Enhanced Game Port, 1.024MHz multimedia sequencer timer,
+ ext. codec (I2S port), onboard amp (4W/4Ohms/ch), suspend/resume.
+
To compile this driver as a module, choose M here: the module
will be called snd-azt3328.
@@ -165,7 +180,7 @@ config SND_BT87X
help
If you want to record audio from TV cards based on
Brooktree Bt878/Bt879 chips, say Y here and read
- <file:Documentation/sound/alsa/Bt87x.txt>.
+ <file:Documentation/sound/cards/bt87x.rst>.
To compile this driver as a module, choose M here: the module
will be called snd-bt87x.
@@ -182,6 +197,7 @@ config SND_BT87X_OVERCLOCK
config SND_CA0106
tristate "SB Audigy LS / Live 24bit"
+ depends on HAS_IOPORT
select SND_AC97_CODEC
select SND_RAWMIDI
select SND_VMASTER
@@ -194,22 +210,24 @@ config SND_CA0106
config SND_CMIPCI
tristate "C-Media 8338, 8738, 8768, 8770"
+ depends on HAS_IOPORT
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_PCM
help
If you want to use soundcards based on C-Media CMI8338, CMI8738,
CMI8768 or CMI8770 chips, say Y here and read
- <file:Documentation/sound/alsa/CMIPCI.txt>.
+ <file:Documentation/sound/cards/cmipci.rst>.
To compile this driver as a module, choose M here: the module
will be called snd-cmipci.
config SND_OXYGEN_LIB
- tristate
+ tristate
config SND_OXYGEN
- tristate "C-Media 8788 (Oxygen)"
+ tristate "C-Media 8786, 8787, 8788 (Oxygen)"
+ depends on HAS_IOPORT
select SND_OXYGEN_LIB
select SND_PCM
select SND_MPU401_UART
@@ -217,19 +235,25 @@ config SND_OXYGEN
Say Y here to include support for sound cards based on the
C-Media CMI8788 (Oxygen HD Audio) chip:
* Asound A-8788
+ * Asus Xonar DG/DGX
* AuzenTech X-Meridian
+ * AuzenTech X-Meridian 2G
* Bgears b-Enspirer
* Club3D Theatron DTS
* HT-Omega Claro (plus)
* HT-Omega Claro halo (XT)
+ * Kuroutoshikou CMI8787-HG2PCI
* Razer Barracuda AC-1
* Sondigo Inferno
+ * TempoTec/MediaTek HiFier Fantasia
+ * TempoTec/MediaTek HiFier Serenade
To compile this driver as a module, choose M here: the module
will be called snd-oxygen.
config SND_CS4281
tristate "Cirrus Logic (Sound Fusion) CS4281"
+ depends on HAS_IOPORT
select SND_OPL3_LIB
select SND_RAWMIDI
select SND_AC97_CODEC
@@ -241,8 +265,10 @@ config SND_CS4281
config SND_CS46XX
tristate "Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x"
+ depends on HAS_IOPORT
select SND_RAWMIDI
select SND_AC97_CODEC
+ select FW_LOADER
help
Say Y here to include support for Cirrus Logic CS4610/CS4612/
CS4614/CS4615/CS4622/CS4624/CS4630/CS4280 chips.
@@ -261,7 +287,8 @@ config SND_CS46XX_NEW_DSP
config SND_CS5530
tristate "CS5530 Audio"
- depends on ISA_DMA_API
+ depends on ISA_DMA_API && (X86_32 || COMPILE_TEST)
+ depends on !M68K
select SND_SB16_DSP
help
Say Y here to include support for audio on Cyrix/NatSemi CS5530 chips.
@@ -271,6 +298,8 @@ config SND_CS5530
config SND_CS5535AUDIO
tristate "CS5535/CS5536 Audio"
+ depends on X86_32 || MIPS || COMPILE_TEST
+ depends on HAS_IOPORT
select SND_PCM
select SND_AC97_CODEC
help
@@ -288,6 +317,7 @@ config SND_CS5535AUDIO
config SND_CTXFI
tristate "Creative Sound Blaster X-Fi"
+ depends on HAS_IOPORT
select SND_PCM
help
If you want to use soundcards based on Creative Sound Blastr X-Fi
@@ -442,26 +472,37 @@ config SND_INDIGODJX
will be called snd-indigodjx
config SND_EMU10K1
- tristate "Emu10k1 (SB Live!, Audigy, E-mu APS)"
+ tristate "Emu10k1 (SB Live!, Audigy, E-MU APS/0404/1010/1212/1616/1820)"
select FW_LOADER
select SND_HWDEP
select SND_RAWMIDI
select SND_AC97_CODEC
+ select SND_TIMER
+ select SND_SEQ_DEVICE if SND_SEQUENCER != n
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y to include support for Sound Blaster PCI 512, Live!,
- Audigy and E-mu APS (partially supported) soundcards.
+ Audigy and E-MU APS/0404/1010/1212/1616/1820 soundcards.
The confusing multitude of mixer controls is documented in
- <file:Documentation/sound/alsa/SB-Live-mixer.txt> and
- <file:Documentation/sound/alsa/Audigy-mixer.txt>.
+ <file:Documentation/sound/cards/sb-live-mixer.rst> and
+ <file:Documentation/sound/cards/audigy-mixer.rst>.
To compile this driver as a module, choose M here: the module
will be called snd-emu10k1.
+# select SEQ stuff to min(SND_SEQUENCER,SND_XXX)
+config SND_EMU10K1_SEQ
+ def_tristate SND_SEQUENCER && SND_EMU10K1
+ select SND_SEQ_MIDI_EMUL
+ select SND_SEQ_VIRMIDI
+ select SND_SYNTH_EMUX
+
config SND_EMU10K1X
tristate "Emu10k1X (Dell OEM Version)"
select SND_AC97_CODEC
select SND_RAWMIDI
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y here to include support for the Dell OEM version of the
Sound Blaster Live!.
@@ -471,6 +512,7 @@ config SND_EMU10K1X
config SND_ENS1370
tristate "(Creative) Ensoniq AudioPCI 1370"
+ depends on HAS_IOPORT
select SND_RAWMIDI
select SND_PCM
help
@@ -481,6 +523,7 @@ config SND_ENS1370
config SND_ENS1371
tristate "(Creative) Ensoniq AudioPCI 1371/1373"
+ depends on HAS_IOPORT
select SND_RAWMIDI
select SND_AC97_CODEC
help
@@ -495,6 +538,7 @@ config SND_ES1938
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y here to include support for soundcards based on ESS Solo-1
(ES1938, ES1946, ES1969) chips.
@@ -506,6 +550,7 @@ config SND_ES1968
tristate "ESS ES1968/1978 (Maestro-1/2/2E)"
select SND_MPU401_UART
select SND_AC97_CODEC
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y here to include support for soundcards based on ESS Maestro
1/2/2E chips.
@@ -523,8 +568,21 @@ config SND_ES1968_INPUT
If you say N the buttons will directly control the master volume.
It is recommended to say Y.
+config SND_ES1968_RADIO
+ bool "Enable TEA5757 radio tuner support for es1968"
+ depends on SND_ES1968
+ depends on MEDIA_RADIO_SUPPORT
+ depends on VIDEO_DEV=y || VIDEO_DEV=SND_ES1968
+ select RADIO_ADAPTERS
+ select RADIO_TEA575X
+
+ help
+ Say Y here to include support for TEA5757 radio tuner integrated on
+ some MediaForte cards (e.g. SF64-PCE2).
+
config SND_FM801
tristate "ForteMedia FM801"
+ depends on HAS_IOPORT
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
@@ -538,21 +596,18 @@ config SND_FM801
config SND_FM801_TEA575X_BOOL
bool "ForteMedia FM801 + TEA5757 tuner"
depends on SND_FM801
- depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_FM801
+ depends on MEDIA_RADIO_SUPPORT
+ depends on VIDEO_DEV=y || VIDEO_DEV=SND_FM801
+ select RADIO_ADAPTERS
+ select RADIO_TEA575X
help
Say Y here to include support for soundcards based on the ForteMedia
- FM801 chip with a TEA5757 tuner connected to GPIO1-3 pins (Media
- Forte SF256-PCS-02) into the snd-fm801 driver.
-
-config SND_FM801_TEA575X
- tristate
- depends on SND_FM801_TEA575X_BOOL
- default SND_FM801
-
-source "sound/pci/hda/Kconfig"
+ FM801 chip with a TEA5757 tuner (MediaForte SF256-PCS, SF256-PCP and
+ SF64-PCR) into the snd-fm801 driver.
config SND_HDSP
tristate "RME Hammerfall DSP Audio"
+ select FW_LOADER
select SND_HWDEP
select SND_RAWMIDI
select SND_PCM
@@ -567,34 +622,23 @@ comment "Don't forget to add built-in firmwares for HDSP driver"
depends on SND_HDSP=y
config SND_HDSPM
- tristate "RME Hammerfall DSP MADI"
+ tristate "RME Hammerfall DSP MADI/RayDAT/AIO"
select SND_HWDEP
select SND_RAWMIDI
select SND_PCM
help
- Say Y here to include support for RME Hammerfall DSP MADI
- soundcards.
+ Say Y here to include support for RME Hammerfall DSP MADI,
+ RayDAT and AIO soundcards.
To compile this driver as a module, choose M here: the module
will be called snd-hdspm.
-config SND_HIFIER
- tristate "TempoTec HiFier Fantasia"
- select SND_OXYGEN_LIB
- select SND_PCM
- select SND_MPU401_UART
- help
- Say Y here to include support for the MediaTek/TempoTec HiFier
- Fantasia sound card.
-
- To compile this driver as a module, choose M here: the module
- will be called snd-hifier.
-
config SND_ICE1712
tristate "ICEnsemble ICE1712 (Envy24)"
select SND_MPU401_UART
select SND_AC97_CODEC
select BITREVERSE
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y here to include support for soundcards based on the
ICE1712 (Envy24) chip.
@@ -610,6 +654,7 @@ config SND_ICE1712
config SND_ICE1724
tristate "ICE/VT1724/1720 (Envy24HT/PT)"
+ depends on HAS_IOPORT
select SND_RAWMIDI
select SND_AC97_CODEC
select SND_VMASTER
@@ -623,7 +668,7 @@ config SND_ICE1724
AudioTrak Prodigy 192, 7.1 (HIFI/LT/XT), HD2; Hercules
Fortissimo IV; ESI Juli@; Pontis MS300; EGO-SYS WaveTerminal
192M; Albatron K8X800 Pro II; Chaintech ZNF3-150/250, 9CJS,
- AV-710; Shuttle SN25P.
+ AV-710; Shuttle SN25P; Philips PSC724 Ultimate Edge.
To compile this driver as a module, choose M here: the module
will be called snd-ice1724.
@@ -659,8 +704,18 @@ config SND_KORG1212
To compile this driver as a module, choose M here: the module
will be called snd-korg1212.
+config SND_LOLA
+ tristate "Digigram Lola"
+ select SND_PCM
+ help
+ Say Y to include support for Digigram Lola boards.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-lola.
+
config SND_LX6464ES
tristate "Digigram LX6464ES"
+ depends on HAS_IOPORT_MAP
select SND_PCM
help
Say Y here to include support for Digigram LX6464ES boards.
@@ -672,6 +727,7 @@ config SND_LX6464ES
config SND_MAESTRO3
tristate "ESS Allegro/Maestro3"
select SND_AC97_CODEC
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y here to include support for soundcards based on ESS Maestro 3
(Allegro) chips.
@@ -691,11 +747,12 @@ config SND_MAESTRO3_INPUT
config SND_MIXART
tristate "Digigram miXart"
+ select FW_LOADER
select SND_HWDEP
select SND_PCM
help
If you want to use Digigram miXart soundcards, say Y here and
- read <file:Documentation/sound/alsa/MIXART.txt>.
+ read <file:Documentation/sound/cards/mixart.rst>.
To compile this driver as a module, choose M here: the module
will be called snd-mixart.
@@ -711,6 +768,8 @@ config SND_NM256
config SND_PCXHR
tristate "Digigram PCXHR"
+ depends on HAS_IOPORT
+ select FW_LOADER
select SND_PCM
select SND_HWDEP
help
@@ -721,6 +780,7 @@ config SND_PCXHR
config SND_RIPTIDE
tristate "Conexant Riptide"
+ depends on HAS_IOPORT
select FW_LOADER
select SND_OPL3_LIB
select SND_MPU401_UART
@@ -762,10 +822,21 @@ config SND_RME9652
To compile this driver as a module, choose M here: the module
will be called snd-rme9652.
+config SND_SE6X
+ tristate "Studio Evolution SE6X"
+ depends on SND_OXYGEN=n && SND_VIRTUOSO=n # PCI ID conflict
+ depends on HAS_IOPORT
+ select SND_OXYGEN_LIB
+ select SND_PCM
+ select SND_MPU401_UART
+ help
+ Say Y or M here only if you actually have this sound card.
+
config SND_SIS7019
tristate "SiS 7019 Audio Accelerator"
- depends on X86 && !X86_64
+ depends on X86_32
select SND_AC97_CODEC
+ depends on ZONE_DMA
help
Say Y here to include support for the SiS 7019 Audio Accelerator.
@@ -777,6 +848,7 @@ config SND_SONICVIBES
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y here to include support for soundcards based on the S3
SonicVibes chip.
@@ -788,6 +860,7 @@ config SND_TRIDENT
tristate "Trident 4D-Wave DX/NX; SiS 7018"
select SND_MPU401_UART
select SND_AC97_CODEC
+ depends on ZONE_DMA && HAS_IOPORT
help
Say Y here to include support for soundcards based on Trident
4D-Wave DX/NX or SiS 7018 chips.
@@ -797,6 +870,7 @@ config SND_TRIDENT
config SND_VIA82XX
tristate "VIA 82C686A/B, 8233/8235 AC97 Controller"
+ depends on HAS_IOPORT
select SND_MPU401_UART
select SND_AC97_CODEC
help
@@ -808,6 +882,7 @@ config SND_VIA82XX
config SND_VIA82XX_MODEM
tristate "VIA 82C686A/B, 8233 based Modems"
+ depends on HAS_IOPORT
select SND_AC97_CODEC
help
Say Y here to include support for the integrated MC97 modem on
@@ -818,22 +893,24 @@ config SND_VIA82XX_MODEM
config SND_VIRTUOSO
tristate "Asus Virtuoso 66/100/200 (Xonar)"
+ depends on HAS_IOPORT
select SND_OXYGEN_LIB
select SND_PCM
select SND_MPU401_UART
- select SND_JACK if INPUT=y || INPUT=SND
+ select SND_JACK
help
Say Y here to include support for sound cards based on the
- Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS,
- Essence ST (Deluxe), and Essence STX.
- Support for the HDAV1.3 (Deluxe) is incomplete; for the
- HDAV1.3 Slim and Xense, missing.
+ Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS, DSX,
+ Essence ST (Deluxe), and Essence STX (II).
+ Support for the HDAV1.3 (Deluxe) and HDAV1.3 Slim is experimental;
+ for the Xense, missing.
To compile this driver as a module, choose M here: the module
will be called snd-virtuoso.
config SND_VX222
tristate "Digigram VX222"
+ depends on HAS_IOPORT
select SND_VX_LIB
help
Say Y here to include support for Digigram VX222 soundcards.
@@ -843,9 +920,11 @@ config SND_VX222
config SND_YMFPCI
tristate "Yamaha YMF724/740/744/754"
+ depends on HAS_IOPORT
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
+ select SND_TIMER
help
Say Y here to include support for Yamaha PCI audio chips -
YMF724, YMF724F, YMF740, YMF740C, YMF744, YMF754.
diff --git a/sound/pci/Makefile b/sound/pci/Makefile
index 9cf4348ec137..9d5e8e12ae73 100644
--- a/sound/pci/Makefile
+++ b/sound/pci/Makefile
@@ -1,32 +1,33 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ad1889-objs := ad1889.o
-snd-als300-objs := als300.o
-snd-als4000-objs := als4000.o
-snd-atiixp-objs := atiixp.o
-snd-atiixp-modem-objs := atiixp_modem.o
-snd-azt3328-objs := azt3328.o
-snd-bt87x-objs := bt87x.o
-snd-cmipci-objs := cmipci.o
-snd-cs4281-objs := cs4281.o
-snd-cs5530-objs := cs5530.o
-snd-ens1370-objs := ens1370.o ak4531_codec.o
-snd-ens1371-objs := ens1371.o
-snd-es1938-objs := es1938.o
-snd-es1968-objs := es1968.o
-snd-fm801-objs := fm801.o
-snd-intel8x0-objs := intel8x0.o
-snd-intel8x0m-objs := intel8x0m.o
-snd-maestro3-objs := maestro3.o
-snd-rme32-objs := rme32.o
-snd-rme96-objs := rme96.o
-snd-sis7019-objs := sis7019.o
-snd-sonicvibes-objs := sonicvibes.o
-snd-via82xx-objs := via82xx.o
-snd-via82xx-modem-objs := via82xx_modem.o
+snd-ad1889-y := ad1889.o
+snd-als300-y := als300.o
+snd-als4000-y := als4000.o
+snd-atiixp-y := atiixp.o
+snd-atiixp-modem-y := atiixp_modem.o
+snd-azt3328-y := azt3328.o
+snd-bt87x-y := bt87x.o
+snd-cmipci-y := cmipci.o
+snd-cs4281-y := cs4281.o
+snd-cs5530-y := cs5530.o
+snd-ens1370-y := ens1370.o ak4531_codec.o
+snd-ens1371-y := ens1371.o
+snd-es1938-y := es1938.o
+snd-es1968-y := es1968.o
+snd-fm801-y := fm801.o
+snd-intel8x0-y := intel8x0.o
+snd-intel8x0m-y := intel8x0m.o
+snd-maestro3-y := maestro3.o
+snd-rme32-y := rme32.o
+snd-rme96-y := rme96.o
+snd-sis7019-y := sis7019.o
+snd-sonicvibes-y := sonicvibes.o
+snd-via82xx-y := via82xx.o
+snd-via82xx-modem-y := via82xx_modem.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AD1889) += snd-ad1889.o
@@ -64,10 +65,10 @@ obj-$(CONFIG_SND) += \
ca0106/ \
cs46xx/ \
cs5535audio/ \
+ lola/ \
lx6464es/ \
echoaudio/ \
emu10k1/ \
- hda/ \
ice1712/ \
korg1212/ \
mixart/ \
diff --git a/sound/pci/ac97/Makefile b/sound/pci/ac97/Makefile
index 41fa322f0971..c74e769f3523 100644
--- a/sound/pci/ac97/Makefile
+++ b/sound/pci/ac97/Makefile
@@ -1,10 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
snd-ac97-codec-y := ac97_codec.o ac97_pcm.o
-snd-ac97-codec-$(CONFIG_PROC_FS) += ac97_proc.o
+snd-ac97-codec-$(CONFIG_SND_PROC_FS) += ac97_proc.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AC97_CODEC) += snd-ac97-codec.o
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index a7630e9edf8a..c54bdefa5afe 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -1,32 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Universal interface for Audio Codec '97
*
* For more details look to AC '97 component specification revision 2.2
* by Intel Corporation (http://developer.intel.com).
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -42,7 +27,7 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Universal interface for Audio Codec '97");
MODULE_LICENSE("GPL");
-static int enable_loopback;
+static bool enable_loopback;
module_param(enable_loopback, bool, 0444);
MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control");
@@ -71,6 +56,12 @@ static const struct ac97_codec_id snd_ac97_codec_id_vendors[] = {
{ 0x414b4d00, 0xffffff00, "Asahi Kasei", NULL, NULL },
{ 0x414c4300, 0xffffff00, "Realtek", NULL, NULL },
{ 0x414c4700, 0xffffff00, "Realtek", NULL, NULL },
+/*
+ * This is an _inofficial_ Aztech Labs entry
+ * (value might differ from unknown official Aztech ID),
+ * currently used by the AC97 emulation of the almost-AC97 PCI168 card.
+ */
+{ 0x415a5400, 0xffffff00, "Aztech Labs (emulated)", NULL, NULL },
{ 0x434d4900, 0xffffff00, "C-Media Electronics", NULL, NULL },
{ 0x43525900, 0xffffff00, "Cirrus Logic", NULL, NULL },
{ 0x43585400, 0xffffff00, "Conexant", NULL, NULL },
@@ -127,6 +118,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
{ 0x414c4781, 0xffffffff, "ALC658D", NULL, NULL }, /* already patched */
{ 0x414c4780, 0xfffffff0, "ALC658", patch_alc655, NULL },
{ 0x414c4790, 0xfffffff0, "ALC850", patch_alc850, NULL },
+{ 0x415a5401, 0xffffffff, "AZF3328", patch_aztech_azf3328, NULL },
{ 0x434d4941, 0xffffffff, "CMI9738", patch_cm9738, NULL },
{ 0x434d4961, 0xffffffff, "CMI9739", patch_cm9739, NULL },
{ 0x434d4969, 0xffffffff, "CMI9780", patch_cm9780, NULL },
@@ -160,7 +152,6 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
{ 0x4e534300, 0xffffffff, "LM4540,43,45,46,48", NULL, NULL }, // only guess --jk
{ 0x4e534331, 0xffffffff, "LM4549", NULL, NULL },
{ 0x4e534350, 0xffffffff, "LM4550", patch_lm4550, NULL }, // volume wrap fix
-{ 0x50534304, 0xffffffff, "UCB1400", patch_ucb1400, NULL },
{ 0x53494c20, 0xffffffe0, "Si3036,8", mpatch_si3036, mpatch_si3036, AC97_MODEM_PATCH },
{ 0x53544d02, 0xffffffff, "ST7597", NULL, NULL },
{ 0x54524102, 0xffffffff, "TR28022", NULL, NULL },
@@ -168,7 +159,9 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
{ 0x54524106, 0xffffffff, "TR28026", NULL, NULL },
{ 0x54524108, 0xffffffff, "TR28028", patch_tritech_tr28028, NULL }, // added by xin jin [07/09/99]
{ 0x54524123, 0xffffffff, "TR28602", NULL, NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)]
+{ 0x54584e03, 0xffffffff, "TLV320AIC27", NULL, NULL },
{ 0x54584e20, 0xffffffff, "TLC320AD9xC", NULL, NULL },
+{ 0x56494120, 0xfffffff0, "VIA1613", patch_vt1613, NULL },
{ 0x56494161, 0xffffffff, "VIA1612A", NULL, NULL }, // modified ICE1232 with S/PDIF
{ 0x56494170, 0xffffffff, "VIA1617A", patch_vt1617a, NULL }, // modified VT1616 with S/PDIF
{ 0x56494182, 0xffffffff, "VIA1618", patch_vt1618, NULL },
@@ -206,6 +199,12 @@ static void update_power_regs(struct snd_ac97 *ac97);
#define ac97_is_power_save_mode(ac97) 0
#endif
+#define ac97_err(ac97, fmt, args...) \
+ dev_err((ac97)->bus->card->dev, fmt, ##args)
+#define ac97_warn(ac97, fmt, args...) \
+ dev_warn((ac97)->bus->card->dev, fmt, ##args)
+#define ac97_dbg(ac97, fmt, args...) \
+ dev_dbg((ac97)->bus->card->dev, fmt, ##args)
/*
* I/O routines
@@ -218,11 +217,11 @@ static int snd_ac97_valid_reg(struct snd_ac97 *ac97, unsigned short reg)
case AC97_ID_ST_AC97_ID4:
if (reg == 0x08)
return 0;
- /* fall through */
+ fallthrough;
case AC97_ID_ST7597:
if (reg == 0x22 || reg == 0x7a)
return 1;
- /* fall through */
+ fallthrough;
case AC97_ID_AK4540:
case AC97_ID_AK4542:
if (reg <= 0x1c || reg == 0x20 || reg == 0x26 || reg >= 0x7c)
@@ -292,7 +291,7 @@ EXPORT_SYMBOL(snd_ac97_write);
* Reads a value from the given register. This will invoke the read
* callback directly after the register check.
*
- * Returns the read value.
+ * Return: The read value.
*/
unsigned short snd_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
@@ -327,11 +326,10 @@ void snd_ac97_write_cache(struct snd_ac97 *ac97, unsigned short reg, unsigned sh
{
if (!snd_ac97_valid_reg(ac97, reg))
return;
- mutex_lock(&ac97->reg_mutex);
+ guard(mutex)(&ac97->reg_mutex);
ac97->regs[reg] = value;
ac97->bus->ops->write(ac97, reg, value);
set_bit(reg, ac97->reg_accessed);
- mutex_unlock(&ac97->reg_mutex);
}
EXPORT_SYMBOL(snd_ac97_write_cache);
@@ -345,7 +343,7 @@ EXPORT_SYMBOL(snd_ac97_write_cache);
* Compares the value with the register cache and updates the value
* only when the value is changed.
*
- * Returns 1 if the value is changed, 0 if no change, or a negative
+ * Return: 1 if the value is changed, 0 if no change, or a negative
* code on failure.
*/
int snd_ac97_update(struct snd_ac97 *ac97, unsigned short reg, unsigned short value)
@@ -354,14 +352,13 @@ int snd_ac97_update(struct snd_ac97 *ac97, unsigned short reg, unsigned short va
if (!snd_ac97_valid_reg(ac97, reg))
return -EINVAL;
- mutex_lock(&ac97->reg_mutex);
+ guard(mutex)(&ac97->reg_mutex);
change = ac97->regs[reg] != value;
if (change) {
ac97->regs[reg] = value;
ac97->bus->ops->write(ac97, reg, value);
}
set_bit(reg, ac97->reg_accessed);
- mutex_unlock(&ac97->reg_mutex);
return change;
}
@@ -377,19 +374,15 @@ EXPORT_SYMBOL(snd_ac97_update);
* Updates the masked-bits on the given register only when the value
* is changed.
*
- * Returns 1 if the bits are changed, 0 if no change, or a negative
+ * Return: 1 if the bits are changed, 0 if no change, or a negative
* code on failure.
*/
int snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value)
{
- int change;
-
if (!snd_ac97_valid_reg(ac97, reg))
return -EINVAL;
- mutex_lock(&ac97->reg_mutex);
- change = snd_ac97_update_bits_nolock(ac97, reg, mask, value);
- mutex_unlock(&ac97->reg_mutex);
- return change;
+ guard(mutex)(&ac97->reg_mutex);
+ return snd_ac97_update_bits_nolock(ac97, reg, mask, value);
}
EXPORT_SYMBOL(snd_ac97_update_bits);
@@ -417,12 +410,12 @@ static int snd_ac97_ad18xx_update_pcm_bits(struct snd_ac97 *ac97, int codec, uns
int change;
unsigned short old, new, cfg;
- mutex_lock(&ac97->page_mutex);
+ guard(mutex)(&ac97->page_mutex);
old = ac97->spec.ad18xx.pcmreg[codec];
new = (old & ~mask) | (value & mask);
change = old != new;
if (change) {
- mutex_lock(&ac97->reg_mutex);
+ guard(mutex)(&ac97->reg_mutex);
cfg = snd_ac97_read_cache(ac97, AC97_AD_SERIAL_CFG);
ac97->spec.ad18xx.pcmreg[codec] = new;
/* select single codec */
@@ -434,9 +427,7 @@ static int snd_ac97_ad18xx_update_pcm_bits(struct snd_ac97 *ac97, int codec, uns
/* select all codecs */
ac97->bus->ops->write(ac97, AC97_AD_SERIAL_CFG,
cfg | 0x7000);
- mutex_unlock(&ac97->reg_mutex);
}
- mutex_unlock(&ac97->page_mutex);
return change;
}
@@ -449,14 +440,8 @@ static int snd_ac97_info_enum_double(struct snd_kcontrol *kcontrol,
{
struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
- uinfo->value.enumerated.items = e->mask;
-
- if (uinfo->value.enumerated.item > e->mask - 1)
- uinfo->value.enumerated.item = e->mask - 1;
- strcpy(uinfo->value.enumerated.name, e->texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, e->shift_l == e->shift_r ? 1 : 2,
+ e->mask, e->texts);
}
static int snd_ac97_get_enum_double(struct snd_kcontrol *kcontrol,
@@ -590,9 +575,9 @@ static int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol,
snd_ac97_page_restore(ac97, page_save);
#ifdef CONFIG_SND_AC97_POWER_SAVE
/* check analog mixer power-down */
- if ((val_mask & 0x8000) &&
+ if ((val_mask & AC97_PD_EAPD) &&
(kcontrol->private_value & (1<<30))) {
- if (val & 0x8000)
+ if (val & AC97_PD_EAPD)
ac97->power_up &= ~(1 << (reg>>1));
else
ac97->power_up |= 1 << (reg>>1);
@@ -602,11 +587,6 @@ static int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol,
return err;
}
-static const struct snd_kcontrol_new snd_ac97_controls_master_mono[2] = {
-AC97_SINGLE("Master Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
-AC97_SINGLE("Master Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1)
-};
-
static const struct snd_kcontrol_new snd_ac97_controls_tone[2] = {
AC97_SINGLE("Tone Control - Bass", AC97_MASTER_TONE, 8, 15, 1),
AC97_SINGLE("Tone Control - Treble", AC97_MASTER_TONE, 0, 15, 1)
@@ -728,12 +708,11 @@ static int snd_ac97_spdif_default_get(struct snd_kcontrol *kcontrol, struct snd_
{
struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ac97->reg_mutex);
+ guard(mutex)(&ac97->reg_mutex);
ucontrol->value.iec958.status[0] = ac97->spdif_status & 0xff;
ucontrol->value.iec958.status[1] = (ac97->spdif_status >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (ac97->spdif_status >> 16) & 0xff;
ucontrol->value.iec958.status[3] = (ac97->spdif_status >> 24) & 0xff;
- mutex_unlock(&ac97->reg_mutex);
return 0;
}
@@ -772,7 +751,7 @@ static int snd_ac97_spdif_default_put(struct snd_kcontrol *kcontrol, struct snd_
}
}
- mutex_lock(&ac97->reg_mutex);
+ guard(mutex)(&ac97->reg_mutex);
change = ac97->spdif_status != new;
ac97->spdif_status = new;
@@ -806,7 +785,6 @@ static int snd_ac97_spdif_default_put(struct snd_kcontrol *kcontrol, struct snd_
snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); /* turn on again */
}
}
- mutex_unlock(&ac97->reg_mutex);
return change;
}
@@ -815,7 +793,7 @@ static int snd_ac97_put_spsa(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
{
struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
int reg = kcontrol->private_value & 0xff;
- int shift = (kcontrol->private_value >> 8) & 0xff;
+ int shift = (kcontrol->private_value >> 8) & 0x0f;
int mask = (kcontrol->private_value >> 16) & 0xff;
// int invert = (kcontrol->private_value >> 24) & 0xff;
unsigned short value, old, new;
@@ -823,7 +801,7 @@ static int snd_ac97_put_spsa(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
value = (ucontrol->value.integer.value[0] & mask);
- mutex_lock(&ac97->reg_mutex);
+ guard(mutex)(&ac97->reg_mutex);
mask <<= shift;
value <<= shift;
old = snd_ac97_read_cache(ac97, reg);
@@ -837,7 +815,6 @@ static int snd_ac97_put_spsa(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
if (extst & AC97_EA_SPDIF)
snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); /* turn on again */
}
- mutex_unlock(&ac97->reg_mutex);
return change;
}
@@ -948,10 +925,9 @@ static int snd_ac97_ad18xx_pcm_get_volume(struct snd_kcontrol *kcontrol, struct
struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
int codec = kcontrol->private_value & 3;
- mutex_lock(&ac97->page_mutex);
- ucontrol->value.integer.value[0] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 0) & 31);
- ucontrol->value.integer.value[1] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 8) & 31);
- mutex_unlock(&ac97->page_mutex);
+ guard(mutex)(&ac97->page_mutex);
+ ucontrol->value.integer.value[0] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 8) & 31);
+ ucontrol->value.integer.value[1] = 31 - ((ac97->spec.ad18xx.pcmreg[codec] >> 0) & 31);
return 0;
}
@@ -1014,8 +990,7 @@ static int snd_ac97_free(struct snd_ac97 *ac97)
{
if (ac97) {
#ifdef CONFIG_SND_AC97_POWER_SAVE
- cancel_delayed_work(&ac97->power_work);
- flush_scheduled_work();
+ cancel_delayed_work_sync(&ac97->power_work);
#endif
snd_ac97_proc_done(ac97);
if (ac97->bus)
@@ -1036,20 +1011,20 @@ static int snd_ac97_dev_free(struct snd_device *device)
static int snd_ac97_try_volume_mix(struct snd_ac97 * ac97, int reg)
{
- unsigned short val, mask = 0x8000;
+ unsigned short val, mask = AC97_MUTE_MASK_MONO;
if (! snd_ac97_valid_reg(ac97, reg))
return 0;
switch (reg) {
case AC97_MASTER_TONE:
- return ac97->caps & 0x04 ? 1 : 0;
+ return ac97->caps & AC97_BC_BASS_TREBLE ? 1 : 0;
case AC97_HEADPHONE:
- return ac97->caps & 0x10 ? 1 : 0;
+ return ac97->caps & AC97_BC_HEADPHONE ? 1 : 0;
case AC97_REC_GAIN_MIC:
- return ac97->caps & 0x01 ? 1 : 0;
+ return ac97->caps & AC97_BC_DEDICATED_MIC ? 1 : 0;
case AC97_3D_CONTROL:
- if (ac97->caps & 0x7c00) {
+ if (ac97->caps & AC97_BC_3D_TECH_ID_MASK) {
val = snd_ac97_read(ac97, reg);
/* if nonzero - fixed and we can't set it */
return val == 0;
@@ -1105,7 +1080,10 @@ static void check_volume_resolution(struct snd_ac97 *ac97, int reg, unsigned cha
*lo_max = *hi_max = 0;
for (i = 0 ; i < ARRAY_SIZE(cbit); i++) {
unsigned short val;
- snd_ac97_write(ac97, reg, 0x8080 | cbit[i] | (cbit[i] << 8));
+ snd_ac97_write(
+ ac97, reg,
+ AC97_MUTE_MASK_STEREO | cbit[i] | (cbit[i] << 8)
+ );
/* Do the read twice due to buffers on some ac97 codecs.
* e.g. The STAC9704 returns exactly what you wrote to the register
* if you read it immediately. This causes the detect routine to fail.
@@ -1140,14 +1118,14 @@ static void snd_ac97_change_volume_params2(struct snd_ac97 * ac97, int reg, int
unsigned short val, val1;
*max = 63;
- val = 0x8080 | (0x20 << shift);
+ val = AC97_MUTE_MASK_STEREO | (0x20 << shift);
snd_ac97_write(ac97, reg, val);
val1 = snd_ac97_read(ac97, reg);
if (val != val1) {
*max = 31;
}
/* reset volume to zero */
- snd_ac97_write_cache(ac97, reg, 0x8080);
+ snd_ac97_write_cache(ac97, reg, AC97_MUTE_MASK_STEREO);
}
static inline int printable(unsigned int x)
@@ -1184,16 +1162,16 @@ static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg,
if (! snd_ac97_valid_reg(ac97, reg))
return 0;
- mute_mask = 0x8000;
+ mute_mask = AC97_MUTE_MASK_MONO;
val = snd_ac97_read(ac97, reg);
if (check_stereo || (ac97->flags & AC97_STEREO_MUTES)) {
/* check whether both mute bits work */
- val1 = val | 0x8080;
+ val1 = val | AC97_MUTE_MASK_STEREO;
snd_ac97_write(ac97, reg, val1);
if (val1 == snd_ac97_read(ac97, reg))
- mute_mask = 0x8080;
+ mute_mask = AC97_MUTE_MASK_STEREO;
}
- if (mute_mask == 0x8080) {
+ if (mute_mask == AC97_MUTE_MASK_STEREO) {
struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 15, 7, 1, 1);
if (check_amix)
tmp.private_value |= (1 << 30);
@@ -1262,6 +1240,8 @@ static int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigne
tmp.index = ac97->num;
kctl = snd_ctl_new1(&tmp, ac97);
}
+ if (!kctl)
+ return -ENOMEM;
if (reg >= AC97_PHONE && reg <= AC97_PCM)
set_tlv_db_scale(kctl, db_scale_5bit_12db_max);
else
@@ -1269,9 +1249,11 @@ static int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigne
err = snd_ctl_add(card, kctl);
if (err < 0)
return err;
- snd_ac97_write_cache(ac97, reg,
- (snd_ac97_read(ac97, reg) & 0x8080) |
- lo_max | (hi_max << 8));
+ snd_ac97_write_cache(
+ ac97, reg,
+ (snd_ac97_read(ac97, reg) & AC97_MUTE_MASK_STEREO)
+ | lo_max | (hi_max << 8)
+ );
return 0;
}
@@ -1283,7 +1265,7 @@ static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx,
struct snd_ac97 *ac97)
{
int err;
- char name[44];
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
unsigned char lo_max, hi_max;
if (! snd_ac97_valid_reg(ac97, reg))
@@ -1291,15 +1273,17 @@ static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx,
if (snd_ac97_try_bit(ac97, reg, 15)) {
sprintf(name, "%s Switch", pfx);
- if ((err = snd_ac97_cmute_new_stereo(card, name, reg,
- check_stereo, check_amix,
- ac97)) < 0)
+ err = snd_ac97_cmute_new_stereo(card, name, reg,
+ check_stereo, check_amix,
+ ac97);
+ if (err < 0)
return err;
}
check_volume_resolution(ac97, reg, &lo_max, &hi_max);
if (lo_max) {
sprintf(name, "%s Volume", pfx);
- if ((err = snd_ac97_cvol_new(card, name, reg, lo_max, hi_max, ac97)) < 0)
+ err = snd_ac97_cvol_new(card, name, reg, lo_max, hi_max, ac97);
+ if (err < 0)
return err;
}
return 0;
@@ -1333,14 +1317,16 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
return err;
}
- ac97->regs[AC97_CENTER_LFE_MASTER] = 0x8080;
+ ac97->regs[AC97_CENTER_LFE_MASTER] = AC97_MUTE_MASK_STEREO;
/* build center controls */
if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER))
&& !(ac97->flags & AC97_AD_MULTI)) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_center[0], ac97))) < 0)
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_center[0], ac97));
+ if (err < 0)
return err;
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_center[1], ac97))) < 0)
+ err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_center[1], ac97));
+ if (err < 0)
return err;
snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 0, &max);
kctl->private_value &= ~(0xff << 16);
@@ -1352,9 +1338,11 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build LFE controls */
if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER+1))
&& !(ac97->flags & AC97_AD_MULTI)) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_lfe[0], ac97))) < 0)
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_lfe[0], ac97));
+ if (err < 0)
return err;
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_lfe[1], ac97))) < 0)
+ err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_lfe[1], ac97));
+ if (err < 0)
return err;
snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 8, &max);
kctl->private_value &= ~(0xff << 16);
@@ -1367,23 +1355,26 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
if ((snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER))
&& !(ac97->flags & AC97_AD_MULTI)) {
/* Surround Master (0x38) is with stereo mutes */
- if ((err = snd_ac97_cmix_new_stereo(card, "Surround Playback",
- AC97_SURROUND_MASTER, 1, 0,
- ac97)) < 0)
+ err = snd_ac97_cmix_new_stereo(card, "Surround Playback",
+ AC97_SURROUND_MASTER, 1, 0,
+ ac97);
+ if (err < 0)
return err;
}
/* build headphone controls */
if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE)) {
- if ((err = snd_ac97_cmix_new(card, "Headphone Playback",
- AC97_HEADPHONE, 0, ac97)) < 0)
+ err = snd_ac97_cmix_new(card, "Headphone Playback",
+ AC97_HEADPHONE, 0, ac97);
+ if (err < 0)
return err;
}
/* build master mono controls */
if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_MONO)) {
- if ((err = snd_ac97_cmix_new(card, "Master Mono Playback",
- AC97_MASTER_MONO, 0, ac97)) < 0)
+ err = snd_ac97_cmix_new(card, "Master Mono Playback",
+ AC97_MASTER_MONO, 0, ac97);
+ if (err < 0)
return err;
}
@@ -1391,7 +1382,9 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
if (!(ac97->flags & AC97_HAS_NO_TONE)) {
if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_TONE)) {
for (idx = 0; idx < 2; idx++) {
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_tone[idx], ac97))) < 0)
+ kctl = snd_ac97_cnew(&snd_ac97_controls_tone[idx], ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
if (ac97->id == AC97_ID_YMF743 ||
ac97->id == AC97_ID_YMF753) {
@@ -1407,19 +1400,27 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
if (!(ac97->flags & AC97_HAS_NO_PC_BEEP) &&
((ac97->flags & AC97_HAS_PC_BEEP) ||
snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) {
- for (idx = 0; idx < 2; idx++)
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0)
+ for (idx = 0; idx < 2; idx++) {
+ kctl = snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
+ }
set_tlv_db_scale(kctl, db_scale_4bit);
- snd_ac97_write_cache(ac97, AC97_PC_BEEP,
- snd_ac97_read(ac97, AC97_PC_BEEP) | 0x801e);
+ snd_ac97_write_cache(
+ ac97,
+ AC97_PC_BEEP,
+ (snd_ac97_read(ac97, AC97_PC_BEEP)
+ | AC97_MUTE_MASK_MONO | 0x001e)
+ );
}
/* build Phone controls */
if (!(ac97->flags & AC97_HAS_NO_PHONE)) {
if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) {
- if ((err = snd_ac97_cmix_new(card, "Phone Playback",
- AC97_PHONE, 1, ac97)) < 0)
+ err = snd_ac97_cmix_new(card, "Phone Playback",
+ AC97_PHONE, 1, ac97);
+ if (err < 0)
return err;
}
}
@@ -1427,26 +1428,30 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build MIC controls */
if (!(ac97->flags & AC97_HAS_NO_MIC)) {
if (snd_ac97_try_volume_mix(ac97, AC97_MIC)) {
- if ((err = snd_ac97_cmix_new(card, "Mic Playback",
- AC97_MIC, 1, ac97)) < 0)
+ err = snd_ac97_cmix_new(card, "Mic Playback",
+ AC97_MIC, 1, ac97);
+ if (err < 0)
return err;
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_boost, ac97))) < 0)
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_boost, ac97));
+ if (err < 0)
return err;
}
}
/* build Line controls */
if (snd_ac97_try_volume_mix(ac97, AC97_LINE)) {
- if ((err = snd_ac97_cmix_new(card, "Line Playback",
- AC97_LINE, 1, ac97)) < 0)
+ err = snd_ac97_cmix_new(card, "Line Playback",
+ AC97_LINE, 1, ac97);
+ if (err < 0)
return err;
}
/* build CD controls */
if (!(ac97->flags & AC97_HAS_NO_CD)) {
if (snd_ac97_try_volume_mix(ac97, AC97_CD)) {
- if ((err = snd_ac97_cmix_new(card, "CD Playback",
- AC97_CD, 1, ac97)) < 0)
+ err = snd_ac97_cmix_new(card, "CD Playback",
+ AC97_CD, 1, ac97);
+ if (err < 0)
return err;
}
}
@@ -1454,8 +1459,9 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build Video controls */
if (!(ac97->flags & AC97_HAS_NO_VIDEO)) {
if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) {
- if ((err = snd_ac97_cmix_new(card, "Video Playback",
- AC97_VIDEO, 1, ac97)) < 0)
+ err = snd_ac97_cmix_new(card, "Video Playback",
+ AC97_VIDEO, 1, ac97);
+ if (err < 0)
return err;
}
}
@@ -1463,8 +1469,9 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build Aux controls */
if (!(ac97->flags & AC97_HAS_NO_AUX)) {
if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) {
- if ((err = snd_ac97_cmix_new(card, "Aux Playback",
- AC97_AUX, 1, ac97)) < 0)
+ err = snd_ac97_cmix_new(card, "Aux Playback",
+ AC97_AUX, 1, ac97);
+ if (err < 0)
return err;
}
}
@@ -1476,26 +1483,38 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
init_val = 0x9f9f;
else
init_val = 0x9f1f;
- for (idx = 0; idx < 2; idx++)
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97))) < 0)
+ for (idx = 0; idx < 2; idx++) {
+ kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
+ }
set_tlv_db_scale(kctl, db_scale_5bit);
ac97->spec.ad18xx.pcmreg[0] = init_val;
if (ac97->scaps & AC97_SCAP_SURROUND_DAC) {
- for (idx = 0; idx < 2; idx++)
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97))) < 0)
+ for (idx = 0; idx < 2; idx++) {
+ kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
+ }
set_tlv_db_scale(kctl, db_scale_5bit);
ac97->spec.ad18xx.pcmreg[1] = init_val;
}
if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) {
- for (idx = 0; idx < 2; idx++)
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97))) < 0)
+ for (idx = 0; idx < 2; idx++) {
+ kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
+ }
set_tlv_db_scale(kctl, db_scale_5bit);
- for (idx = 0; idx < 2; idx++)
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[idx], ac97))) < 0)
+ for (idx = 0; idx < 2; idx++) {
+ kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[idx], ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
+ }
set_tlv_db_scale(kctl, db_scale_5bit);
ac97->spec.ad18xx.pcmreg[2] = init_val;
}
@@ -1516,7 +1535,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
/* build Capture controls */
if (!(ac97->flags & AC97_HAS_NO_REC_GAIN)) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97))) < 0)
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97));
+ if (err < 0)
return err;
if (snd_ac97_try_bit(ac97, AC97_REC_GAIN, 15)) {
err = snd_ac97_cmute_new(card, "Capture Switch",
@@ -1524,7 +1544,9 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
if (err < 0)
return err;
}
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97))) < 0)
+ kctl = snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
set_tlv_db_scale(kctl, db_scale_rec_gain);
snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000);
@@ -1532,52 +1554,62 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
}
/* build MIC Capture controls */
if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) {
- for (idx = 0; idx < 2; idx++)
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97))) < 0)
+ for (idx = 0; idx < 2; idx++) {
+ kctl = snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
+ }
set_tlv_db_scale(kctl, db_scale_rec_gain);
snd_ac97_write_cache(ac97, AC97_REC_GAIN_MIC, 0x0000);
}
/* build PCM out path & mute control */
if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 15)) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_PCM_OUT], ac97))) < 0)
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_PCM_OUT], ac97));
+ if (err < 0)
return err;
}
/* build Simulated Stereo Enhancement control */
- if (ac97->caps & 0x0008) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_STEREO_ENHANCEMENT], ac97))) < 0)
+ if (ac97->caps & AC97_BC_SIM_STEREO) {
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_STEREO_ENHANCEMENT], ac97));
+ if (err < 0)
return err;
}
/* build 3D Stereo Enhancement control */
if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 13)) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_3D], ac97))) < 0)
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_3D], ac97));
+ if (err < 0)
return err;
}
/* build Loudness control */
- if (ac97->caps & 0x0020) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOUDNESS], ac97))) < 0)
+ if (ac97->caps & AC97_BC_LOUDNESS) {
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOUDNESS], ac97));
+ if (err < 0)
return err;
}
/* build Mono output select control */
if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 9)) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MONO], ac97))) < 0)
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MONO], ac97));
+ if (err < 0)
return err;
}
/* build Mic select control */
if (snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 8)) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MIC], ac97))) < 0)
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_MIC], ac97));
+ if (err < 0)
return err;
}
/* build ADC/DAC loopback control */
if (enable_loopback && snd_ac97_try_bit(ac97, AC97_GENERAL_PURPOSE, 7)) {
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOOPBACK], ac97))) < 0)
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOOPBACK], ac97));
+ if (err < 0)
return err;
}
@@ -1593,11 +1625,15 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
snd_ac97_write(ac97, AC97_3D_CONTROL, val);
val = snd_ac97_read(ac97, AC97_3D_CONTROL);
val = val == 0x0606;
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+ kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
if (val)
kctl->private_value = AC97_3D_CONTROL | (9 << 8) | (7 << 16);
- if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[1], ac97))) < 0)
+ kctl = snd_ac97_cnew(&snd_ac97_controls_3d[1], ac97);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
if (val)
kctl->private_value = AC97_3D_CONTROL | (1 << 8) | (7 << 16);
@@ -1614,14 +1650,18 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
if ((ac97->ext_id & AC97_EI_SPDIF) && !(ac97->scaps & AC97_SCAP_NO_SPDIF)) {
if (ac97->build_ops->build_spdif) {
- if ((err = ac97->build_ops->build_spdif(ac97)) < 0)
+ err = ac97->build_ops->build_spdif(ac97);
+ if (err < 0)
return err;
} else {
- for (idx = 0; idx < 5; idx++)
- if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97))) < 0)
+ for (idx = 0; idx < 5; idx++) {
+ err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_spdif[idx], ac97));
+ if (err < 0)
return err;
+ }
if (ac97->build_ops->build_post_spdif) {
- if ((err = ac97->build_ops->build_post_spdif(ac97)) < 0)
+ err = ac97->build_ops->build_post_spdif(ac97);
+ if (err < 0)
return err;
}
/* set default PCM S/PDIF params */
@@ -1633,9 +1673,11 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
}
/* build chip specific controls */
- if (ac97->build_ops->build_specific)
- if ((err = ac97->build_ops->build_specific(ac97)) < 0)
+ if (ac97->build_ops->build_specific) {
+ err = ac97->build_ops->build_specific(ac97);
+ if (err < 0)
return err;
+ }
if (snd_ac97_try_bit(ac97, AC97_POWERDOWN, 15)) {
kctl = snd_ac97_cnew(&snd_ac97_control_eapd, ac97);
@@ -1643,7 +1685,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
return -ENOMEM;
if (ac97->scaps & AC97_SCAP_INV_EAPD)
set_inv_eapd(ac97, kctl);
- if ((err = snd_ctl_add(card, kctl)) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
}
@@ -1655,7 +1698,7 @@ static int snd_ac97_modem_build(struct snd_card *card, struct snd_ac97 * ac97)
int err, idx;
/*
- printk(KERN_DEBUG "AC97_GPIO_CFG = %x\n",
+ ac97_dbg(ac97, "AC97_GPIO_CFG = %x\n",
snd_ac97_read(ac97,AC97_GPIO_CFG));
*/
snd_ac97_write(ac97, AC97_GPIO_CFG, 0xffff & ~(AC97_GPIO_LINE1_OH));
@@ -1665,14 +1708,18 @@ static int snd_ac97_modem_build(struct snd_card *card, struct snd_ac97 * ac97)
snd_ac97_write(ac97, AC97_MISC_AFE, 0x0);
/* build modem switches */
- for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_modem_switches); idx++)
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ac97_controls_modem_switches[idx], ac97))) < 0)
+ for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_modem_switches); idx++) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_ac97_controls_modem_switches[idx], ac97));
+ if (err < 0)
return err;
+ }
/* build chip specific controls */
- if (ac97->build_ops->build_specific)
- if ((err = ac97->build_ops->build_specific(ac97)) < 0)
+ if (ac97->build_ops->build_specific) {
+ err = ac97->build_ops->build_specific(ac97);
+ if (err < 0)
return err;
+ }
return 0;
}
@@ -1754,10 +1801,10 @@ static unsigned int snd_ac97_determine_spdif_rates(struct snd_ac97 *ac97)
{
unsigned int result = 0;
int i;
- static unsigned short ctl_bits[] = {
+ static const unsigned short ctl_bits[] = {
AC97_SC_SPSR_44K, AC97_SC_SPSR_32K, AC97_SC_SPSR_48K
};
- static unsigned int rate_bits[] = {
+ static const unsigned int rate_bits[] = {
SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_32000, SNDRV_PCM_RATE_48000
};
@@ -1781,7 +1828,8 @@ static const struct ac97_codec_id *look_for_codec_id(const struct ac97_codec_id
return NULL;
}
-void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int modem)
+void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name,
+ size_t maxlen, int modem)
{
const struct ac97_codec_id *pid;
@@ -1793,7 +1841,7 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m
if (! pid)
return;
- strcpy(name, pid->name);
+ strscpy(name, pid->name, maxlen);
if (ac97 && pid->patch) {
if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
(! modem && ! (pid->flags & AC97_MODEM_PATCH)))
@@ -1802,24 +1850,26 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m
pid = look_for_codec_id(snd_ac97_codec_ids, id);
if (pid) {
- strcat(name, " ");
- strcat(name, pid->name);
+ strlcat(name, " ", maxlen);
+ strlcat(name, pid->name, maxlen);
if (pid->mask != 0xffffffff)
- sprintf(name + strlen(name), " rev %d", id & ~pid->mask);
+ sprintf(name + strlen(name), " rev %u", id & ~pid->mask);
if (ac97 && pid->patch) {
if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
(! modem && ! (pid->flags & AC97_MODEM_PATCH)))
pid->patch(ac97);
}
- } else
- sprintf(name + strlen(name), " id %x", id & 0xff);
+ } else {
+ int l = strlen(name);
+ snprintf(name + l, maxlen - l, " id %x", id & 0xff);
+ }
}
/**
* snd_ac97_get_short_name - retrieve codec name
* @ac97: the codec instance
*
- * Returns the short identifying name of the codec.
+ * Return: The short identifying name of the codec.
*/
const char *snd_ac97_get_short_name(struct snd_ac97 *ac97)
{
@@ -1888,19 +1938,20 @@ static int ac97_reset_wait(struct snd_ac97 *ac97, int timeout, int with_modem)
* write). The other callbacks, wait and reset, are not mandatory.
*
* The clock is set to 48000. If another clock is needed, set
- * (*rbus)->clock manually.
+ * ``(*rbus)->clock`` manually.
*
* The AC97 bus instance is registered as a low-level device, so you don't
* have to release it manually.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
-int snd_ac97_bus(struct snd_card *card, int num, struct snd_ac97_bus_ops *ops,
+int snd_ac97_bus(struct snd_card *card, int num,
+ const struct snd_ac97_bus_ops *ops,
void *private_data, struct snd_ac97_bus **rbus)
{
int err;
struct snd_ac97_bus *bus;
- static struct snd_device_ops dev_ops = {
+ static const struct snd_device_ops dev_ops = {
.dev_free = snd_ac97_bus_dev_free,
};
@@ -1916,7 +1967,8 @@ int snd_ac97_bus(struct snd_card *card, int num, struct snd_ac97_bus_ops *ops,
bus->clock = 48000;
spin_lock_init(&bus->bus_lock);
snd_ac97_bus_proc_init(bus);
- if ((err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops)) < 0) {
+ err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops);
+ if (err < 0) {
snd_ac97_bus_free(bus);
return err;
}
@@ -1944,8 +1996,10 @@ static int snd_ac97_dev_register(struct snd_device *device)
dev_set_name(&ac97->dev, "%d-%d:%s",
ac97->bus->card->number, ac97->num,
snd_ac97_get_short_name(ac97));
- if ((err = device_register(&ac97->dev)) < 0) {
- snd_printk(KERN_ERR "Can't register ac97 bus\n");
+ err = device_register(&ac97->dev);
+ if (err < 0) {
+ ac97_err(ac97, "Can't register ac97 bus\n");
+ put_device(&ac97->dev);
ac97->dev.bus = NULL;
return err;
}
@@ -1962,7 +2016,7 @@ static int snd_ac97_dev_disconnect(struct snd_device *device)
}
/* build_ops to do nothing */
-static struct snd_ac97_build_ops null_build_ops;
+static const struct snd_ac97_build_ops null_build_ops;
#ifdef CONFIG_SND_AC97_POWER_SAVE
static void do_update_power(struct work_struct *work)
@@ -1989,7 +2043,7 @@ static void do_update_power(struct work_struct *work)
* The ac97 instance is registered as a low-level device, so you don't
* have to release it manually.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, struct snd_ac97 **rac97)
{
@@ -2000,16 +2054,15 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
unsigned long end_time;
unsigned int reg;
const struct ac97_codec_id *pid;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_ac97_dev_free,
.dev_register = snd_ac97_dev_register,
.dev_disconnect = snd_ac97_dev_disconnect,
};
- if (rac97)
- *rac97 = NULL;
- if (snd_BUG_ON(!bus || !template))
+ if (snd_BUG_ON(!bus || !template || !rac97))
return -EINVAL;
+ *rac97 = NULL;
if (snd_BUG_ON(template->num >= 4))
return -EINVAL;
if (bus->codec[template->num])
@@ -2071,7 +2124,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
msecs_to_jiffies(500), 1);
}
if (err < 0) {
- snd_printk(KERN_WARNING "AC'97 %d does not respond - RESET\n", ac97->num);
+ ac97_warn(ac97, "AC'97 %d does not respond - RESET\n",
+ ac97->num);
/* proceed anyway - it's often non-critical */
}
}
@@ -2080,7 +2134,9 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2);
if (! (ac97->scaps & AC97_SCAP_DETECT_BY_VENDOR) &&
(ac97->id == 0x00000000 || ac97->id == 0xffffffff)) {
- snd_printk(KERN_ERR "AC'97 %d access is not valid [0x%x], removing mixer.\n", ac97->num, ac97->id);
+ ac97_err(ac97,
+ "AC'97 %d access is not valid [0x%x], removing mixer.\n",
+ ac97->num, ac97->id);
snd_ac97_free(ac97);
return -EIO;
}
@@ -2092,7 +2148,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO) && !(ac97->scaps & AC97_SCAP_AUDIO)) {
/* test if we can write to the record gain volume register */
snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a06);
- if (((err = snd_ac97_read(ac97, AC97_REC_GAIN)) & 0x7fff) == 0x0a06)
+ err = snd_ac97_read(ac97, AC97_REC_GAIN);
+ if ((err & 0x7fff) == 0x0a06)
ac97->scaps |= AC97_SCAP_AUDIO;
}
if (ac97->scaps & AC97_SCAP_AUDIO) {
@@ -2113,7 +2170,9 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
if (!ac97_is_audio(ac97) && !ac97_is_modem(ac97)) {
if (!(ac97->scaps & (AC97_SCAP_SKIP_AUDIO|AC97_SCAP_SKIP_MODEM)))
- snd_printk(KERN_ERR "AC'97 %d access error (not audio or modem codec)\n", ac97->num);
+ ac97_err(ac97,
+ "AC'97 %d access error (not audio or modem codec)\n",
+ ac97->num);
snd_ac97_free(ac97);
return -EACCES;
}
@@ -2138,7 +2197,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
goto __ready_ok;
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_WARNING "AC'97 %d analog subsections not ready\n", ac97->num);
+ ac97_warn(ac97,
+ "AC'97 %d analog subsections not ready\n", ac97->num);
}
/* FIXME: add powerdown control */
@@ -2170,7 +2230,10 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
goto __ready_ok;
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_WARNING "MC'97 %d converters and GPIO not ready (0x%x)\n", ac97->num, snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS));
+ ac97_warn(ac97,
+ "MC'97 %d converters and GPIO not ready (0x%x)\n",
+ ac97->num,
+ snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS));
}
__ready_ok:
@@ -2223,15 +2286,15 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
/* additional initializations */
if (bus->ops->init)
bus->ops->init(ac97);
- snd_ac97_get_name(ac97, ac97->id, name, !ac97_is_audio(ac97));
- snd_ac97_get_name(NULL, ac97->id, name, !ac97_is_audio(ac97)); // ac97->id might be changed in the special setup code
+ snd_ac97_get_name(ac97, ac97->id, name, sizeof(name), !ac97_is_audio(ac97));
+ snd_ac97_get_name(NULL, ac97->id, name, sizeof(name), !ac97_is_audio(ac97)); // ac97->id might be changed in the special setup code
if (! ac97->build_ops)
ac97->build_ops = &null_build_ops;
if (ac97_is_audio(ac97)) {
char comp[16];
if (card->mixername[0] == '\0') {
- strcpy(card->mixername, name);
+ strscpy(card->mixername, name);
} else {
if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) {
strcat(card->mixername, ",");
@@ -2239,7 +2302,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
}
}
sprintf(comp, "AC97a:%08x", ac97->id);
- if ((err = snd_component_add(card, comp)) < 0) {
+ err = snd_component_add(card, comp);
+ if (err < 0) {
snd_ac97_free(ac97);
return err;
}
@@ -2251,7 +2315,7 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
if (ac97_is_modem(ac97)) {
char comp[16];
if (card->mixername[0] == '\0') {
- strcpy(card->mixername, name);
+ strscpy(card->mixername, name);
} else {
if (strlen(card->mixername) + 1 + strlen(name) + 1 <= sizeof(card->mixername)) {
strcat(card->mixername, ",");
@@ -2259,7 +2323,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
}
}
sprintf(comp, "AC97m:%08x", ac97->id);
- if ((err = snd_component_add(card, comp)) < 0) {
+ err = snd_component_add(card, comp);
+ if (err < 0) {
snd_ac97_free(ac97);
return err;
}
@@ -2271,7 +2336,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
if (ac97_is_audio(ac97))
update_power_regs(ac97);
snd_ac97_proc_init(ac97);
- if ((err = snd_device_new(card, SNDRV_DEV_CODEC, ac97, &ops)) < 0) {
+ err = snd_device_new(card, SNDRV_DEV_CODEC, ac97, &ops);
+ if (err < 0) {
snd_ac97_free(ac97);
return err;
}
@@ -2337,7 +2403,7 @@ struct ac97_power_reg {
enum { PWIDX_ADC, PWIDX_FRONT, PWIDX_CLFE, PWIDX_SURR, PWIDX_MIC, PWIDX_SIZE };
-static struct ac97_power_reg power_regs[PWIDX_SIZE] = {
+static const struct ac97_power_reg power_regs[PWIDX_SIZE] = {
[PWIDX_ADC] = { AC97_PCM_LR_ADC_RATE, AC97_POWERDOWN, AC97_PD_PR0},
[PWIDX_FRONT] = { AC97_PCM_FRONT_DAC_RATE, AC97_POWERDOWN, AC97_PD_PR1},
[PWIDX_CLFE] = { AC97_PCM_LFE_DAC_RATE, AC97_EXTENDED_STATUS,
@@ -2356,6 +2422,8 @@ static struct ac97_power_reg power_regs[PWIDX_SIZE] = {
* @powerup: non-zero when power up the part
*
* Update the AC97 powerdown register bits of the given part.
+ *
+ * Return: Zero.
*/
int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup)
{
@@ -2384,8 +2452,7 @@ int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup)
* (for avoiding loud click noises for many (OSS) apps
* that open/close frequently)
*/
- schedule_delayed_work(&ac97->power_work,
- msecs_to_jiffies(power_save * 1000));
+ schedule_delayed_work(&ac97->power_work, secs_to_jiffies(power_save));
else {
cancel_delayed_work(&ac97->power_work);
update_power_regs(ac97);
@@ -2456,8 +2523,7 @@ void snd_ac97_suspend(struct snd_ac97 *ac97)
if (ac97->build_ops->suspend)
ac97->build_ops->suspend(ac97);
#ifdef CONFIG_SND_AC97_POWER_SAVE
- cancel_delayed_work(&ac97->power_work);
- flush_scheduled_work();
+ cancel_delayed_work_sync(&ac97->power_work);
#endif
snd_ac97_powerdown(ac97);
}
@@ -2544,8 +2610,8 @@ void snd_ac97_resume(struct snd_ac97 *ac97)
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
/* FIXME: extra delay */
- ac97->bus->ops->write(ac97, AC97_MASTER, 0x8000);
- if (snd_ac97_read(ac97, AC97_MASTER) != 0x8000)
+ ac97->bus->ops->write(ac97, AC97_MASTER, AC97_MUTE_MASK_MONO);
+ if (snd_ac97_read(ac97, AC97_MASTER) != AC97_MUTE_MASK_MONO)
msleep(250);
} else {
end_time = jiffies + msecs_to_jiffies(100);
@@ -2578,11 +2644,18 @@ EXPORT_SYMBOL(snd_ac97_resume);
*/
static void set_ctl_name(char *dst, const char *src, const char *suffix)
{
- if (suffix)
- sprintf(dst, "%s %s", src, suffix);
- else
- strcpy(dst, src);
-}
+ const size_t msize = SNDRV_CTL_ELEM_ID_NAME_MAXLEN;
+
+ if (suffix) {
+ if (snprintf(dst, msize, "%s %s", src, suffix) >= msize)
+ pr_warn("ALSA: AC97 control name '%s %s' truncated to '%s'\n",
+ src, suffix, dst);
+ } else {
+ if (strscpy(dst, src, msize) < 0)
+ pr_warn("ALSA: AC97 control name '%s' truncated to '%s'\n",
+ src, dst);
+ }
+}
/* remove the control with the given name and optional suffix */
static int snd_ac97_remove_ctl(struct snd_ac97 *ac97, const char *name,
@@ -2609,8 +2682,11 @@ static int snd_ac97_rename_ctl(struct snd_ac97 *ac97, const char *src,
const char *dst, const char *suffix)
{
struct snd_kcontrol *kctl = ctl_find(ac97, src, suffix);
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
if (kctl) {
- set_ctl_name(kctl->id.name, dst, suffix);
+ set_ctl_name(name, dst, suffix);
+ snd_ctl_rename(ac97->bus->card, kctl, name);
return 0;
}
return -ENOENT;
@@ -2629,11 +2705,17 @@ static int snd_ac97_swap_ctl(struct snd_ac97 *ac97, const char *s1,
const char *s2, const char *suffix)
{
struct snd_kcontrol *kctl1, *kctl2;
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+
kctl1 = ctl_find(ac97, s1, suffix);
kctl2 = ctl_find(ac97, s2, suffix);
if (kctl1 && kctl2) {
- set_ctl_name(kctl1->id.name, s2, suffix);
- set_ctl_name(kctl2->id.name, s1, suffix);
+ set_ctl_name(name, s2, suffix);
+ snd_ctl_rename(ac97->bus->card, kctl1, name);
+
+ set_ctl_name(name, s1, suffix);
+ snd_ctl_rename(ac97->bus->card, kctl2, name);
+
return 0;
}
return -ENOENT;
@@ -2704,7 +2786,7 @@ static int tune_ad_sharing(struct snd_ac97 *ac97)
{
unsigned short scfg;
if ((ac97->id & 0xffffff00) != 0x41445300) {
- snd_printk(KERN_ERR "ac97_quirk AD_SHARING is only for AD codecs\n");
+ ac97_err(ac97, "ac97_quirk AD_SHARING is only for AD codecs\n");
return -EINVAL;
}
/* Turn on OMS bit to route microphone to back panel */
@@ -2720,7 +2802,8 @@ AC97_SINGLE("Jack Detect", AC97_ALC650_CLOCK, 5, 1, 0);
static int tune_alc_jack(struct snd_ac97 *ac97)
{
if ((ac97->id & 0xffffff00) != 0x414c4700) {
- snd_printk(KERN_ERR "ac97_quirk ALC_JACK is only for Realtek codecs\n");
+ ac97_err(ac97,
+ "ac97_quirk ALC_JACK is only for Realtek codecs\n");
return -EINVAL;
}
snd_ac97_update_bits(ac97, 0x7a, 0x20, 0x20); /* select jack detect function */
@@ -2749,12 +2832,12 @@ static int master_mute_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
int rshift = (kcontrol->private_value >> 12) & 0x0f;
unsigned short mask;
if (shift != rshift)
- mask = 0x8080;
+ mask = AC97_MUTE_MASK_STEREO;
else
- mask = 0x8000;
- snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000,
+ mask = AC97_MUTE_MASK_MONO;
+ snd_ac97_update_bits(ac97, AC97_POWERDOWN, AC97_PD_EAPD,
(ac97->regs[AC97_MASTER] & mask) == mask ?
- 0x8000 : 0);
+ AC97_PD_EAPD : 0);
}
return err;
}
@@ -2767,7 +2850,10 @@ static int tune_mute_led(struct snd_ac97 *ac97)
return -ENOENT;
msw->put = master_mute_sw_put;
snd_ac97_remove_ctl(ac97, "External Amplifier", NULL);
- snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */
+ snd_ac97_update_bits(
+ ac97, AC97_POWERDOWN,
+ AC97_PD_EAPD, AC97_PD_EAPD /* mute LED on */
+ );
ac97->scaps |= AC97_SCAP_EAPD_LED;
return 0;
}
@@ -2782,12 +2868,12 @@ static int hp_master_mute_sw_put(struct snd_kcontrol *kcontrol,
int rshift = (kcontrol->private_value >> 12) & 0x0f;
unsigned short mask;
if (shift != rshift)
- mask = 0x8080;
+ mask = AC97_MUTE_MASK_STEREO;
else
- mask = 0x8000;
- snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000,
+ mask = AC97_MUTE_MASK_MONO;
+ snd_ac97_update_bits(ac97, AC97_POWERDOWN, AC97_PD_EAPD,
(ac97->regs[AC97_MASTER] & mask) == mask ?
- 0x8000 : 0);
+ AC97_PD_EAPD : 0);
}
return err;
}
@@ -2803,7 +2889,10 @@ static int tune_hp_mute_led(struct snd_ac97 *ac97)
snd_ac97_remove_ctl(ac97, "External Amplifier", NULL);
snd_ac97_remove_ctl(ac97, "Headphone Playback", "Switch");
snd_ac97_remove_ctl(ac97, "Headphone Playback", "Volume");
- snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */
+ snd_ac97_update_bits(
+ ac97, AC97_POWERDOWN,
+ AC97_PD_EAPD, AC97_PD_EAPD /* mute LED on */
+ );
return 0;
}
@@ -2812,7 +2901,7 @@ struct quirk_table {
int (*func)(struct snd_ac97 *);
};
-static struct quirk_table applicable_quirks[] = {
+static const struct quirk_table applicable_quirks[] = {
{ "none", NULL },
{ "hp_only", tune_hp_only },
{ "swap_hp", tune_swap_hp },
@@ -2840,7 +2929,7 @@ static int apply_quirk(struct snd_ac97 *ac97, int type)
static int apply_quirk_str(struct snd_ac97 *ac97, const char *typestr)
{
int i;
- struct quirk_table *q;
+ const struct quirk_table *q;
for (i = 0; i < ARRAY_SIZE(applicable_quirks); i++) {
q = &applicable_quirks[i];
@@ -2863,10 +2952,11 @@ static int apply_quirk_str(struct snd_ac97 *ac97, const char *typestr)
* headphone (true line-out) control as "Master".
* The quirk-list must be terminated with a zero-filled entry.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
-int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, const char *override)
+int snd_ac97_tune_hardware(struct snd_ac97 *ac97,
+ const struct ac97_quirk *quirk, const char *override)
{
int result;
@@ -2874,7 +2964,8 @@ int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, cons
if (override && strcmp(override, "-1") && strcmp(override, "default")) {
result = apply_quirk_str(ac97, override);
if (result < 0)
- snd_printk(KERN_ERR "applying quirk type %s failed (%d)\n", override, result);
+ ac97_err(ac97, "applying quirk type %s failed (%d)\n",
+ override, result);
return result;
}
@@ -2888,10 +2979,14 @@ int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, cons
quirk->subdevice == (quirk->mask & ac97->subsystem_device)) {
if (quirk->codec_id && quirk->codec_id != ac97->id)
continue;
- snd_printdd("ac97 quirk for %s (%04x:%04x)\n", quirk->name, ac97->subsystem_vendor, ac97->subsystem_device);
+ ac97_dbg(ac97, "ac97 quirk for %s (%04x:%04x)\n",
+ quirk->name, ac97->subsystem_vendor,
+ ac97->subsystem_device);
result = apply_quirk(ac97, quirk->type);
if (result < 0)
- snd_printk(KERN_ERR "applying quirk type %d for %s failed (%d)\n", quirk->type, quirk->name, result);
+ ac97_err(ac97,
+ "applying quirk type %d for %s failed (%d)\n",
+ quirk->type, quirk->name, result);
return result;
}
}
@@ -2899,19 +2994,3 @@ int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, cons
}
EXPORT_SYMBOL(snd_ac97_tune_hardware);
-
-/*
- * INIT part
- */
-
-static int __init alsa_ac97_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_ac97_exit(void)
-{
-}
-
-module_init(alsa_ac97_init)
-module_exit(alsa_ac97_exit)
diff --git a/sound/pci/ac97/ac97_id.h b/sound/pci/ac97/ac97_id.h
index d603147c4a96..ed6ed048bc50 100644
--- a/sound/pci/ac97/ac97_id.h
+++ b/sound/pci/ac97/ac97_id.h
@@ -1,25 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Universal interface for Audio Codec '97
*
* For more details look to AC '97 component specification revision 2.2
* by Intel Corporation (http://developer.intel.com).
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define AC97_ID_AK4540 0x414b4d00
diff --git a/sound/pci/ac97/ac97_local.h b/sound/pci/ac97/ac97_local.h
index c276a5e3f7ac..965284eb4b33 100644
--- a/sound/pci/ac97/ac97_local.h
+++ b/sound/pci/ac97/ac97_local.h
@@ -1,34 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Universal interface for Audio Codec '97
*
* For more details look to AC '97 component specification revision 2.2
* by Intel Corporation (http://developer.intel.com).
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name,
- int modem);
+ size_t maxlen, int modem);
int snd_ac97_update_bits_nolock(struct snd_ac97 *ac97, unsigned short reg,
unsigned short mask, unsigned short value);
/* ac97_proc.c */
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
void snd_ac97_bus_proc_init(struct snd_ac97_bus * ac97);
void snd_ac97_bus_proc_done(struct snd_ac97_bus * ac97);
void snd_ac97_proc_init(struct snd_ac97 * ac97);
diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
index e68c98ef4041..64cc39dd2008 100644
--- a/sound/pci/ac97/ac97_patch.c
+++ b/sound/pci/ac97/ac97_patch.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Universal interface for Audio Codec '97
@@ -5,28 +6,22 @@
* For more details look to AC '97 component specification revision 2.2
* by Intel Corporation (http://developer.intel.com) and to datasheets
* for specific codecs.
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include "ac97_local.h"
#include "ac97_patch.h"
/*
+ * Forward declarations
+ */
+
+static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97,
+ const char *name);
+static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
+ const unsigned int *tlv,
+ const char * const *followers);
+
+/*
* Chip specific initialization
*/
@@ -34,9 +29,11 @@ static int patch_build_controls(struct snd_ac97 * ac97, const struct snd_kcontro
{
int idx, err;
- for (idx = 0; idx < count; idx++)
- if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&controls[idx], ac97))) < 0)
+ for (idx = 0; idx < count; idx++) {
+ err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&controls[idx], ac97));
+ if (err < 0)
return err;
+ }
return 0;
}
@@ -44,12 +41,9 @@ static int patch_build_controls(struct snd_ac97 * ac97, const struct snd_kcontro
static void reset_tlv(struct snd_ac97 *ac97, const char *name,
const unsigned int *tlv)
{
- struct snd_ctl_elem_id sid;
struct snd_kcontrol *kctl;
- memset(&sid, 0, sizeof(sid));
- strcpy(sid.name, name);
- sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kctl = snd_ctl_find_id(ac97->bus->card, &sid);
+
+ kctl = snd_ctl_find_id_mixer(ac97->bus->card, name);
if (kctl && kctl->tlv.p)
kctl->tlv.p = tlv;
}
@@ -60,34 +54,22 @@ static int ac97_update_bits_page(struct snd_ac97 *ac97, unsigned short reg, unsi
unsigned short page_save;
int ret;
- mutex_lock(&ac97->page_mutex);
+ guard(mutex)(&ac97->page_mutex);
page_save = snd_ac97_read(ac97, AC97_INT_PAGING) & AC97_PAGE_MASK;
snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page);
ret = snd_ac97_update_bits(ac97, reg, mask, value);
snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page_save);
- mutex_unlock(&ac97->page_mutex); /* unlock paging */
return ret;
}
/*
* shared line-in/mic controls
*/
-static int ac97_enum_text_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo,
- const char **texts, unsigned int nums)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = nums;
- if (uinfo->value.enumerated.item > nums - 1)
- uinfo->value.enumerated.item = nums - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
-
static int ac97_surround_jack_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[] = { "Shared", "Independent" };
- return ac97_enum_text_info(kcontrol, uinfo, texts, 2);
+ static const char * const texts[] = { "Shared", "Independent" };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int ac97_surround_jack_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -114,9 +96,9 @@ static int ac97_surround_jack_mode_put(struct snd_kcontrol *kcontrol, struct snd
static int ac97_channel_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static const char *texts[] = { "2ch", "4ch", "6ch", "8ch" };
- return ac97_enum_text_info(kcontrol, uinfo, texts,
- kcontrol->private_value);
+ static const char * const texts[] = { "2ch", "4ch", "6ch", "8ch" };
+
+ return snd_ctl_enum_info(uinfo, 1, kcontrol->private_value, texts);
}
static int ac97_channel_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -231,17 +213,11 @@ static inline int alc850_is_aux_back_surround(struct snd_ac97 *ac97)
static int snd_ac97_ymf7x3_info_speaker(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {
+ static const char * const texts[3] = {
"Standard", "Small", "Smaller"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_ac97_ymf7x3_get_speaker(struct snd_kcontrol *kcontrol,
@@ -284,15 +260,9 @@ static const struct snd_kcontrol_new snd_ac97_ymf7x3_controls_speaker =
static int snd_ac97_ymf7x3_spdif_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "AC-Link", "A/D Converter" };
+ static const char * const texts[2] = { "AC-Link", "A/D Converter" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_ac97_ymf7x3_spdif_source_get(struct snd_kcontrol *kcontrol,
@@ -327,7 +297,7 @@ static int patch_yamaha_ymf7x3_3d(struct snd_ac97 *ac97)
err = snd_ctl_add(ac97->bus->card, kctl);
if (err < 0)
return err;
- strcpy(kctl->id.name, "3D Control - Wide");
+ strscpy(kctl->id.name, "3D Control - Wide");
kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 9, 7, 0);
snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
err = snd_ctl_add(ac97->bus->card,
@@ -371,7 +341,7 @@ static int patch_yamaha_ymf743_build_spdif(struct snd_ac97 *ac97)
return 0;
}
-static struct snd_ac97_build_ops patch_yamaha_ymf743_ops = {
+static const struct snd_ac97_build_ops patch_yamaha_ymf743_ops = {
.build_spdif = patch_yamaha_ymf743_build_spdif,
.build_3d = patch_yamaha_ymf7x3_3d,
};
@@ -392,15 +362,9 @@ static int patch_yamaha_ymf743(struct snd_ac97 *ac97)
There is also a bit to mute S/PDIF output in a vendor-specific register. */
static int snd_ac97_ymf753_spdif_output_pin_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = { "Disabled", "Pin 43", "Pin 48" };
+ static const char * const texts[3] = { "Disabled", "Pin 43", "Pin 48" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_ac97_ymf753_spdif_output_pin_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -450,12 +414,13 @@ static int patch_yamaha_ymf753_post_spdif(struct snd_ac97 * ac97)
{
int err;
- if ((err = patch_build_controls(ac97, snd_ac97_ymf753_controls_spdif, ARRAY_SIZE(snd_ac97_ymf753_controls_spdif))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_ymf753_controls_spdif, ARRAY_SIZE(snd_ac97_ymf753_controls_spdif));
+ if (err < 0)
return err;
return 0;
}
-static struct snd_ac97_build_ops patch_yamaha_ymf753_ops = {
+static const struct snd_ac97_build_ops patch_yamaha_ymf753_ops = {
.build_3d = patch_yamaha_ymf7x3_3d,
.build_post_spdif = patch_yamaha_ymf753_post_spdif
};
@@ -495,14 +460,15 @@ static int patch_wolfson_wm9703_specific(struct snd_ac97 * ac97)
int err, i;
for (i = 0; i < ARRAY_SIZE(wm97xx_snd_ac97_controls); i++) {
- if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm97xx_snd_ac97_controls[i], ac97))) < 0)
+ err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm97xx_snd_ac97_controls[i], ac97));
+ if (err < 0)
return err;
}
snd_ac97_write_cache(ac97, AC97_WM97XX_FMIXER_VOL, 0x0808);
return 0;
}
-static struct snd_ac97_build_ops patch_wolfson_wm9703_ops = {
+static const struct snd_ac97_build_ops patch_wolfson_wm9703_ops = {
.build_specific = patch_wolfson_wm9703_specific,
};
@@ -525,7 +491,8 @@ static int patch_wolfson_wm9704_specific(struct snd_ac97 * ac97)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(wm9704_snd_ac97_controls); i++) {
- if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm9704_snd_ac97_controls[i], ac97))) < 0)
+ err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm9704_snd_ac97_controls[i], ac97));
+ if (err < 0)
return err;
}
/* patch for DVD noise */
@@ -533,7 +500,7 @@ static int patch_wolfson_wm9704_specific(struct snd_ac97 * ac97)
return 0;
}
-static struct snd_ac97_build_ops patch_wolfson_wm9704_ops = {
+static const struct snd_ac97_build_ops patch_wolfson_wm9704_ops = {
.build_specific = patch_wolfson_wm9704_specific,
};
@@ -665,7 +632,8 @@ static int patch_wolfson_wm9711_specific(struct snd_ac97 * ac97)
int err, i;
for (i = 0; i < ARRAY_SIZE(wm9711_snd_ac97_controls); i++) {
- if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm9711_snd_ac97_controls[i], ac97))) < 0)
+ err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm9711_snd_ac97_controls[i], ac97));
+ if (err < 0)
return err;
}
snd_ac97_write_cache(ac97, AC97_CODEC_CLASS_REV, 0x0808);
@@ -677,7 +645,7 @@ static int patch_wolfson_wm9711_specific(struct snd_ac97 * ac97)
return 0;
}
-static struct snd_ac97_build_ops patch_wolfson_wm9711_ops = {
+static const struct snd_ac97_build_ops patch_wolfson_wm9711_ops = {
.build_specific = patch_wolfson_wm9711_specific,
};
@@ -832,7 +800,8 @@ static int patch_wolfson_wm9713_3d (struct snd_ac97 * ac97)
int err, i;
for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_3d); i++) {
- if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_3d[i], ac97))) < 0)
+ err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_3d[i], ac97));
+ if (err < 0)
return err;
}
return 0;
@@ -843,7 +812,8 @@ static int patch_wolfson_wm9713_specific(struct snd_ac97 * ac97)
int err, i;
for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls); i++) {
- if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls[i], ac97))) < 0)
+ err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls[i], ac97));
+ if (err < 0)
return err;
}
snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x0808);
@@ -871,7 +841,7 @@ static void patch_wolfson_wm9713_resume (struct snd_ac97 * ac97)
}
#endif
-static struct snd_ac97_build_ops patch_wolfson_wm9713_ops = {
+static const struct snd_ac97_build_ops patch_wolfson_wm9713_ops = {
.build_specific = patch_wolfson_wm9713_specific,
.build_3d = patch_wolfson_wm9713_3d,
#ifdef CONFIG_PM
@@ -917,9 +887,10 @@ static int patch_sigmatel_stac9700_3d(struct snd_ac97 * ac97)
struct snd_kcontrol *kctl;
int err;
- if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+ err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97));
+ if (err < 0)
return err;
- strcpy(kctl->id.name, "3D Control Sigmatel - Depth");
+ strscpy(kctl->id.name, "3D Control Sigmatel - Depth");
kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0);
snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
return 0;
@@ -930,13 +901,17 @@ static int patch_sigmatel_stac9708_3d(struct snd_ac97 * ac97)
struct snd_kcontrol *kctl;
int err;
- if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+ kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97);
+ err = snd_ctl_add(ac97->bus->card, kctl);
+ if (err < 0)
return err;
- strcpy(kctl->id.name, "3D Control Sigmatel - Depth");
+ strscpy(kctl->id.name, "3D Control Sigmatel - Depth");
kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 0, 3, 0);
- if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0)
+ kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97);
+ err = snd_ctl_add(ac97->bus->card, kctl);
+ if (err < 0)
return err;
- strcpy(kctl->id.name, "3D Control Sigmatel - Rear Depth");
+ strscpy(kctl->id.name, "3D Control Sigmatel - Rear Depth");
kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0);
snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000);
return 0;
@@ -961,22 +936,30 @@ static int patch_sigmatel_stac97xx_specific(struct snd_ac97 * ac97)
int err;
snd_ac97_write_cache(ac97, AC97_SIGMATEL_ANALOG, snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG) & ~0x0003);
- if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 1))
- if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_controls[0], 1)) < 0)
+ if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 1)) {
+ err = patch_build_controls(ac97, &snd_ac97_sigmatel_controls[0], 1);
+ if (err < 0)
return err;
- if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 0))
- if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_controls[1], 1)) < 0)
+ }
+ if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_ANALOG, 0)) {
+ err = patch_build_controls(ac97, &snd_ac97_sigmatel_controls[1], 1);
+ if (err < 0)
return err;
- if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_DAC2INVERT, 2))
- if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_4speaker, 1)) < 0)
+ }
+ if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_DAC2INVERT, 2)) {
+ err = patch_build_controls(ac97, &snd_ac97_sigmatel_4speaker, 1);
+ if (err < 0)
return err;
- if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_DAC2INVERT, 3))
- if ((err = patch_build_controls(ac97, &snd_ac97_sigmatel_phaseinvert, 1)) < 0)
+ }
+ if (snd_ac97_try_bit(ac97, AC97_SIGMATEL_DAC2INVERT, 3)) {
+ err = patch_build_controls(ac97, &snd_ac97_sigmatel_phaseinvert, 1);
+ if (err < 0)
return err;
+ }
return 0;
}
-static struct snd_ac97_build_ops patch_sigmatel_stac9700_ops = {
+static const struct snd_ac97_build_ops patch_sigmatel_stac9700_ops = {
.build_3d = patch_sigmatel_stac9700_3d,
.build_specific = patch_sigmatel_stac97xx_specific
};
@@ -992,12 +975,11 @@ static int snd_ac97_stac9708_put_bias(struct snd_kcontrol *kcontrol, struct snd_
struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
int err;
- mutex_lock(&ac97->page_mutex);
+ guard(mutex)(&ac97->page_mutex);
snd_ac97_write(ac97, AC97_SIGMATEL_BIAS1, 0xabba);
err = snd_ac97_update_bits(ac97, AC97_SIGMATEL_BIAS2, 0x0010,
(ucontrol->value.integer.value[0] & 1) << 4);
snd_ac97_write(ac97, AC97_SIGMATEL_BIAS1, 0);
- mutex_unlock(&ac97->page_mutex);
return err;
}
@@ -1018,12 +1000,13 @@ static int patch_sigmatel_stac9708_specific(struct snd_ac97 *ac97)
snd_ac97_remove_ctl(ac97, "PCM Out Path & Mute", NULL);
snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Sigmatel Surround Playback");
- if ((err = patch_build_controls(ac97, &snd_ac97_stac9708_bias_control, 1)) < 0)
+ err = patch_build_controls(ac97, &snd_ac97_stac9708_bias_control, 1);
+ if (err < 0)
return err;
return patch_sigmatel_stac97xx_specific(ac97);
}
-static struct snd_ac97_build_ops patch_sigmatel_stac9708_ops = {
+static const struct snd_ac97_build_ops patch_sigmatel_stac9708_ops = {
.build_3d = patch_sigmatel_stac9708_3d,
.build_specific = patch_sigmatel_stac9708_specific
};
@@ -1094,16 +1077,11 @@ static int patch_sigmatel_stac9756(struct snd_ac97 * ac97)
static int snd_ac97_stac9758_output_jack_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[5] = { "Input/Disabled", "Front Output",
+ static const char * const texts[5] = {
+ "Input/Disabled", "Front Output",
"Rear Output", "Center/LFE Output", "Mixer Output" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 5;
- if (uinfo->value.enumerated.item > 4)
- uinfo->value.enumerated.item = 4;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 5, texts);
}
static int snd_ac97_stac9758_output_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1138,16 +1116,11 @@ static int snd_ac97_stac9758_output_jack_put(struct snd_kcontrol *kcontrol, stru
static int snd_ac97_stac9758_input_jack_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[7] = { "Mic2 Jack", "Mic1 Jack", "Line In Jack",
+ static const char * const texts[7] = {
+ "Mic2 Jack", "Mic1 Jack", "Line In Jack",
"Front Jack", "Rear Jack", "Center/LFE Jack", "Mute" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 7;
- if (uinfo->value.enumerated.item > 6)
- uinfo->value.enumerated.item = 6;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 7, texts);
}
static int snd_ac97_stac9758_input_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1172,15 +1145,11 @@ static int snd_ac97_stac9758_input_jack_put(struct snd_kcontrol *kcontrol, struc
static int snd_ac97_stac9758_phonesel_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = { "None", "Front Jack", "Rear Jack" };
+ static const char * const texts[3] = {
+ "None", "Front Jack", "Rear Jack"
+ };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_ac97_stac9758_phonesel_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1252,32 +1221,32 @@ static int patch_sigmatel_stac9758_specific(struct snd_ac97 *ac97)
return 0;
}
-static struct snd_ac97_build_ops patch_sigmatel_stac9758_ops = {
+static const struct snd_ac97_build_ops patch_sigmatel_stac9758_ops = {
.build_3d = patch_sigmatel_stac9700_3d,
.build_specific = patch_sigmatel_stac9758_specific
};
static int patch_sigmatel_stac9758(struct snd_ac97 * ac97)
{
- static unsigned short regs[4] = {
+ static const unsigned short regs[4] = {
AC97_SIGMATEL_OUTSEL,
AC97_SIGMATEL_IOMISC,
AC97_SIGMATEL_INSEL,
AC97_SIGMATEL_VARIOUS
};
- static unsigned short def_regs[4] = {
+ static const unsigned short def_regs[4] = {
/* OUTSEL */ 0xd794, /* CL:CL, SR:SR, LO:MX, LI:DS, MI:DS */
/* IOMISC */ 0x2001,
/* INSEL */ 0x0201, /* LI:LI, MI:M1 */
/* VARIOUS */ 0x0040
};
- static unsigned short m675_regs[4] = {
+ static const unsigned short m675_regs[4] = {
/* OUTSEL */ 0xfc70, /* CL:MX, SR:MX, LO:DS, LI:MX, MI:DS */
/* IOMISC */ 0x2102, /* HP amp on */
/* INSEL */ 0x0203, /* LI:LI, MI:FR */
/* VARIOUS */ 0x0041 /* stereo mic */
};
- unsigned short *pregs = def_regs;
+ const unsigned short *pregs = def_regs;
int i;
/* Gateway M675 notebook */
@@ -1310,14 +1279,17 @@ static int patch_cirrus_build_spdif(struct snd_ac97 * ac97)
int err;
/* con mask, pro mask, default */
- if ((err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3)) < 0)
+ err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3);
+ if (err < 0)
return err;
/* switch, spsa */
- if ((err = patch_build_controls(ac97, &snd_ac97_cirrus_controls_spdif[0], 1)) < 0)
+ err = patch_build_controls(ac97, &snd_ac97_cirrus_controls_spdif[0], 1);
+ if (err < 0)
return err;
switch (ac97->id & AC97_ID_CS_MASK) {
case AC97_ID_CS4205:
- if ((err = patch_build_controls(ac97, &snd_ac97_cirrus_controls_spdif[1], 1)) < 0)
+ err = patch_build_controls(ac97, &snd_ac97_cirrus_controls_spdif[1], 1);
+ if (err < 0)
return err;
break;
}
@@ -1327,7 +1299,7 @@ static int patch_cirrus_build_spdif(struct snd_ac97 * ac97)
return 0;
}
-static struct snd_ac97_build_ops patch_cirrus_ops = {
+static const struct snd_ac97_build_ops patch_cirrus_ops = {
.build_spdif = patch_cirrus_build_spdif
};
@@ -1372,10 +1344,12 @@ static int patch_conexant_build_spdif(struct snd_ac97 * ac97)
int err;
/* con mask, pro mask, default */
- if ((err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3)) < 0)
+ err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3);
+ if (err < 0)
return err;
/* switch */
- if ((err = patch_build_controls(ac97, &snd_ac97_conexant_controls_spdif[0], 1)) < 0)
+ err = patch_build_controls(ac97, &snd_ac97_conexant_controls_spdif[0], 1);
+ if (err < 0)
return err;
/* set default PCM S/PDIF params */
/* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */
@@ -1384,7 +1358,7 @@ static int patch_conexant_build_spdif(struct snd_ac97 * ac97)
return 0;
}
-static struct snd_ac97_build_ops patch_conexant_ops = {
+static const struct snd_ac97_build_ops patch_conexant_ops = {
.build_spdif = patch_conexant_build_spdif
};
@@ -1404,12 +1378,12 @@ static int patch_cx20551(struct snd_ac97 *ac97)
}
/*
- * Analog Device AD18xx, AD19xx codecs
+ * Analog Devices AD18xx, AD19xx codecs
*/
#ifdef CONFIG_PM
static void ad18xx_resume(struct snd_ac97 *ac97)
{
- static unsigned short setup_regs[] = {
+ static const unsigned short setup_regs[] = {
AC97_AD_MISC, AC97_AD_SERIAL_CFG, AC97_AD_JACK_SPDIF,
};
int i, codec;
@@ -1518,7 +1492,7 @@ static unsigned short patch_ad1881_unchained(struct snd_ac97 * ac97, int idx, un
static int patch_ad1881_chained1(struct snd_ac97 * ac97, int idx, unsigned short codec_bits)
{
- static int cfg_bits[3] = { 1<<12, 1<<14, 1<<13 };
+ static const int cfg_bits[3] = { 1<<12, 1<<14, 1<<13 };
unsigned short val;
snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, cfg_bits[idx]);
@@ -1560,7 +1534,7 @@ static void patch_ad1881_chained(struct snd_ac97 * ac97, int unchained_idx, int
}
}
-static struct snd_ac97_build_ops patch_ad1881_build_ops = {
+static const struct snd_ac97_build_ops patch_ad1881_build_ops = {
#ifdef CONFIG_PM
.resume = ad18xx_resume
#endif
@@ -1640,14 +1614,15 @@ static int patch_ad1885_specific(struct snd_ac97 * ac97)
{
int err;
- if ((err = patch_build_controls(ac97, snd_ac97_controls_ad1885, ARRAY_SIZE(snd_ac97_controls_ad1885))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_controls_ad1885, ARRAY_SIZE(snd_ac97_controls_ad1885));
+ if (err < 0)
return err;
reset_tlv(ac97, "Headphone Playback Volume",
db_scale_6bit_6db_max);
return 0;
}
-static struct snd_ac97_build_ops patch_ad1885_build_ops = {
+static const struct snd_ac97_build_ops patch_ad1885_build_ops = {
.build_specific = &patch_ad1885_specific,
#ifdef CONFIG_PM
.resume = ad18xx_resume
@@ -1674,7 +1649,7 @@ static int patch_ad1886_specific(struct snd_ac97 * ac97)
return 0;
}
-static struct snd_ac97_build_ops patch_ad1886_build_ops = {
+static const struct snd_ac97_build_ops patch_ad1886_build_ops = {
.build_specific = &patch_ad1886_specific,
#ifdef CONFIG_PM
.resume = ad18xx_resume
@@ -1795,15 +1770,9 @@ static int patch_ad1886(struct snd_ac97 * ac97)
static int snd_ac97_ad198x_spdif_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "AC-Link", "A/D Converter" };
+ static const char * const texts[2] = { "AC-Link", "A/D Converter" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_ac97_ad198x_spdif_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1845,10 +1814,10 @@ static const struct snd_kcontrol_new snd_ac97_ad1981x_jack_sense[] = {
AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0),
};
-/* black list to avoid HP/Line jack-sense controls
+/* deny list to avoid HP/Line jack-sense controls
* (SS vendor << 16 | device)
*/
-static unsigned int ad1981_jacks_blacklist[] = {
+static const unsigned int ad1981_jacks_denylist[] = {
0x10140523, /* Thinkpad R40 */
0x10140534, /* Thinkpad X31 */
0x10140537, /* Thinkpad T41p */
@@ -1875,13 +1844,13 @@ static int check_list(struct snd_ac97 *ac97, const unsigned int *list)
static int patch_ad1981a_specific(struct snd_ac97 * ac97)
{
- if (check_list(ac97, ad1981_jacks_blacklist))
+ if (check_list(ac97, ad1981_jacks_denylist))
return 0;
return patch_build_controls(ac97, snd_ac97_ad1981x_jack_sense,
ARRAY_SIZE(snd_ac97_ad1981x_jack_sense));
}
-static struct snd_ac97_build_ops patch_ad1981a_build_ops = {
+static const struct snd_ac97_build_ops patch_ad1981a_build_ops = {
.build_post_spdif = patch_ad198x_post_spdif,
.build_specific = patch_ad1981a_specific,
#ifdef CONFIG_PM
@@ -1889,10 +1858,10 @@ static struct snd_ac97_build_ops patch_ad1981a_build_ops = {
#endif
};
-/* white list to enable HP jack-sense bits
+/* allow list to enable HP jack-sense bits
* (SS vendor << 16 | device)
*/
-static unsigned int ad1981_jacks_whitelist[] = {
+static const unsigned int ad1981_jacks_allowlist[] = {
0x0e11005a, /* HP nc4000/4010 */
0x103c0890, /* HP nc6000 */
0x103c0938, /* HP nc4220 */
@@ -1900,13 +1869,14 @@ static unsigned int ad1981_jacks_whitelist[] = {
0x103c0944, /* HP nc6220 */
0x103c0934, /* HP nc8220 */
0x103c006d, /* HP nx9105 */
+ 0x103c300d, /* HP Compaq dc5100 SFF(PT003AW) */
0x17340088, /* FSC Scenic-W */
0 /* end */
};
static void check_ad1981_hp_jack_sense(struct snd_ac97 *ac97)
{
- if (check_list(ac97, ad1981_jacks_whitelist))
+ if (check_list(ac97, ad1981_jacks_allowlist))
/* enable headphone jack sense */
snd_ac97_update_bits(ac97, AC97_AD_JACK_SPDIF, 1<<11, 1<<11);
}
@@ -1928,15 +1898,16 @@ static int patch_ad1981b_specific(struct snd_ac97 *ac97)
{
int err;
- if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
+ err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1);
+ if (err < 0)
return err;
- if (check_list(ac97, ad1981_jacks_blacklist))
+ if (check_list(ac97, ad1981_jacks_denylist))
return 0;
return patch_build_controls(ac97, snd_ac97_ad1981x_jack_sense,
ARRAY_SIZE(snd_ac97_ad1981x_jack_sense));
}
-static struct snd_ac97_build_ops patch_ad1981b_build_ops = {
+static const struct snd_ac97_build_ops patch_ad1981b_build_ops = {
.build_post_spdif = patch_ad198x_post_spdif,
.build_specific = patch_ad1981b_specific,
#ifdef CONFIG_PM
@@ -1984,15 +1955,9 @@ static int snd_ac97_ad1888_lohpsel_put(struct snd_kcontrol *kcontrol, struct snd
static int snd_ac97_ad1888_downmix_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {"Off", "6 -> 4", "6 -> 2"};
+ static const char * const texts[3] = {"Off", "6 -> 4", "6 -> 2"};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_ac97_ad1888_downmix_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -2075,7 +2040,7 @@ static int patch_ad1888_specific(struct snd_ac97 *ac97)
return patch_build_controls(ac97, snd_ac97_ad1888_controls, ARRAY_SIZE(snd_ac97_ad1888_controls));
}
-static struct snd_ac97_build_ops patch_ad1888_build_ops = {
+static const struct snd_ac97_build_ops patch_ad1888_build_ops = {
.build_post_spdif = patch_ad198x_post_spdif,
.build_specific = patch_ad1888_specific,
#ifdef CONFIG_PM
@@ -2119,12 +2084,13 @@ static int patch_ad1980_specific(struct snd_ac97 *ac97)
{
int err;
- if ((err = patch_ad1888_specific(ac97)) < 0)
+ err = patch_ad1888_specific(ac97);
+ if (err < 0)
return err;
return patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1);
}
-static struct snd_ac97_build_ops patch_ad1980_build_ops = {
+static const struct snd_ac97_build_ops patch_ad1980_build_ops = {
.build_post_spdif = patch_ad198x_post_spdif,
.build_specific = patch_ad1980_specific,
#ifdef CONFIG_PM
@@ -2143,16 +2109,11 @@ static int patch_ad1980(struct snd_ac97 * ac97)
static int snd_ac97_ad1985_vrefout_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = {"High-Z", "3.7 V", "2.25 V", "0 V"};
+ static const char * const texts[4] = {
+ "High-Z", "3.7 V", "2.25 V", "0 V"
+ };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
}
static int snd_ac97_ad1985_vrefout_get(struct snd_kcontrol *kcontrol,
@@ -2232,14 +2193,15 @@ static int patch_ad1985_specific(struct snd_ac97 *ac97)
"Master Surround Playback");
snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback");
- if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
+ err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1);
+ if (err < 0)
return err;
return patch_build_controls(ac97, snd_ac97_ad1985_controls,
ARRAY_SIZE(snd_ac97_ad1985_controls));
}
-static struct snd_ac97_build_ops patch_ad1985_build_ops = {
+static const struct snd_ac97_build_ops patch_ad1985_build_ops = {
.build_post_spdif = patch_ad198x_post_spdif,
.build_specific = patch_ad1985_specific,
#ifdef CONFIG_PM
@@ -2524,14 +2486,15 @@ static int patch_ad1986_specific(struct snd_ac97 *ac97)
{
int err;
- if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
+ err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1);
+ if (err < 0)
return err;
return patch_build_controls(ac97, snd_ac97_ad1986_controls,
ARRAY_SIZE(snd_ac97_ad1985_controls));
}
-static struct snd_ac97_build_ops patch_ad1986_build_ops = {
+static const struct snd_ac97_build_ops patch_ad1986_build_ops = {
.build_post_spdif = patch_ad198x_post_spdif,
.build_specific = patch_ad1986_specific,
#ifdef CONFIG_PM
@@ -2585,6 +2548,21 @@ static void alc650_update_jacks(struct snd_ac97 *ac97)
shared ? 0 : 0x100);
}
+static int alc650_swap_surround_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
+ struct snd_pcm_chmap *map = ac97->chmaps[SNDRV_PCM_STREAM_PLAYBACK];
+
+ if (map) {
+ if (ucontrol->value.integer.value[0])
+ map->chmap = snd_pcm_std_chmaps;
+ else
+ map->chmap = snd_pcm_alt_chmaps;
+ }
+ return snd_ac97_put_volsw(kcontrol, ucontrol);
+}
+
static const struct snd_kcontrol_new snd_ac97_controls_alc650[] = {
AC97_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0),
AC97_SINGLE("Surround Down Mix", AC97_ALC650_MULTICH, 1, 1, 0),
@@ -2598,7 +2576,14 @@ static const struct snd_kcontrol_new snd_ac97_controls_alc650[] = {
/* 9: Line-In/Surround share */
/* 10: Mic/CLFE share */
/* 11-13: in IEC958 controls */
- AC97_SINGLE("Swap Surround Slot", AC97_ALC650_MULTICH, 14, 1, 0),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Swap Surround Slot",
+ .info = snd_ac97_info_volsw,
+ .get = snd_ac97_get_volsw,
+ .put = alc650_swap_surround_put,
+ .private_value = AC97_SINGLE_VALUE(AC97_ALC650_MULTICH, 14, 1, 0),
+ },
#if 0 /* always set in patch_alc650 */
AC97_SINGLE("IEC958 Input Clock Enable", AC97_ALC650_CLOCK, 0, 1, 0),
AC97_SINGLE("IEC958 Input Pin Enable", AC97_ALC650_CLOCK, 1, 1, 0),
@@ -2624,10 +2609,12 @@ static int patch_alc650_specific(struct snd_ac97 * ac97)
{
int err;
- if ((err = patch_build_controls(ac97, snd_ac97_controls_alc650, ARRAY_SIZE(snd_ac97_controls_alc650))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_controls_alc650, ARRAY_SIZE(snd_ac97_controls_alc650));
+ if (err < 0)
return err;
if (ac97->ext_id & AC97_EI_SPDIF) {
- if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc650, ARRAY_SIZE(snd_ac97_spdif_controls_alc650))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc650, ARRAY_SIZE(snd_ac97_spdif_controls_alc650));
+ if (err < 0)
return err;
}
if (ac97->id != AC97_ID_ALC650F)
@@ -2636,7 +2623,7 @@ static int patch_alc650_specific(struct snd_ac97 * ac97)
return 0;
}
-static struct snd_ac97_build_ops patch_alc650_ops = {
+static const struct snd_ac97_build_ops patch_alc650_ops = {
.build_specific = patch_alc650_specific,
.update_jacks = alc650_update_jacks
};
@@ -2724,20 +2711,18 @@ static const struct snd_kcontrol_new snd_ac97_controls_alc655[] = {
static int alc655_iec958_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts_655[3] = { "PCM", "Analog In", "IEC958 In" };
- static char *texts_658[4] = { "PCM", "Analog1 In", "Analog2 In", "IEC958 In" };
+ static const char * const texts_655[3] = {
+ "PCM", "Analog In", "IEC958 In"
+ };
+ static const char * const texts_658[4] = {
+ "PCM", "Analog1 In", "Analog2 In", "IEC958 In"
+ };
struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = ac97->spec.dev_flags ? 4 : 3;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- ac97->spec.dev_flags ?
- texts_658[uinfo->value.enumerated.item] :
- texts_655[uinfo->value.enumerated.item]);
- return 0;
+ if (ac97->spec.dev_flags)
+ return snd_ctl_enum_info(uinfo, 1, 4, texts_658);
+ else
+ return snd_ctl_enum_info(uinfo, 1, 3, texts_655);
}
static int alc655_iec958_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -2779,16 +2764,18 @@ static int patch_alc655_specific(struct snd_ac97 * ac97)
{
int err;
- if ((err = patch_build_controls(ac97, snd_ac97_controls_alc655, ARRAY_SIZE(snd_ac97_controls_alc655))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_controls_alc655, ARRAY_SIZE(snd_ac97_controls_alc655));
+ if (err < 0)
return err;
if (ac97->ext_id & AC97_EI_SPDIF) {
- if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc655, ARRAY_SIZE(snd_ac97_spdif_controls_alc655))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc655, ARRAY_SIZE(snd_ac97_spdif_controls_alc655));
+ if (err < 0)
return err;
}
return 0;
}
-static struct snd_ac97_build_ops patch_alc655_ops = {
+static const struct snd_ac97_build_ops patch_alc655_ops = {
.build_specific = patch_alc655_specific,
.update_jacks = alc655_update_jacks
};
@@ -2891,16 +2878,18 @@ static int patch_alc850_specific(struct snd_ac97 *ac97)
{
int err;
- if ((err = patch_build_controls(ac97, snd_ac97_controls_alc850, ARRAY_SIZE(snd_ac97_controls_alc850))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_controls_alc850, ARRAY_SIZE(snd_ac97_controls_alc850));
+ if (err < 0)
return err;
if (ac97->ext_id & AC97_EI_SPDIF) {
- if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc655, ARRAY_SIZE(snd_ac97_spdif_controls_alc655))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc655, ARRAY_SIZE(snd_ac97_spdif_controls_alc655));
+ if (err < 0)
return err;
}
return 0;
}
-static struct snd_ac97_build_ops patch_alc850_ops = {
+static const struct snd_ac97_build_ops patch_alc850_ops = {
.build_specific = patch_alc850_specific,
.update_jacks = alc850_update_jacks
};
@@ -2940,6 +2929,49 @@ static int patch_alc850(struct snd_ac97 *ac97)
return 0;
}
+static int patch_aztech_azf3328_specific(struct snd_ac97 *ac97)
+{
+ struct snd_kcontrol *kctl_3d_center =
+ snd_ac97_find_mixer_ctl(ac97, "3D Control - Center");
+ struct snd_kcontrol *kctl_3d_depth =
+ snd_ac97_find_mixer_ctl(ac97, "3D Control - Depth");
+
+ /*
+ * 3D register is different from AC97 standard layout
+ * (also do some renaming, to resemble Windows driver naming)
+ */
+ if (kctl_3d_center) {
+ kctl_3d_center->private_value =
+ AC97_SINGLE_VALUE(AC97_3D_CONTROL, 1, 0x07, 0);
+ snd_ac97_rename_vol_ctl(ac97,
+ "3D Control - Center", "3D Control - Width"
+ );
+ }
+ if (kctl_3d_depth)
+ kctl_3d_depth->private_value =
+ AC97_SINGLE_VALUE(AC97_3D_CONTROL, 8, 0x03, 0);
+
+ /* Aztech Windows driver calls the
+ equivalent control "Modem Playback", thus rename it: */
+ snd_ac97_rename_vol_ctl(ac97,
+ "Master Mono Playback", "Modem Playback"
+ );
+ snd_ac97_rename_vol_ctl(ac97,
+ "Headphone Playback", "FM Synth Playback"
+ );
+
+ return 0;
+}
+
+static const struct snd_ac97_build_ops patch_aztech_azf3328_ops = {
+ .build_specific = patch_aztech_azf3328_specific
+};
+
+static int patch_aztech_azf3328(struct snd_ac97 *ac97)
+{
+ ac97->build_ops = &patch_aztech_azf3328_ops;
+ return 0;
+}
/*
* C-Media CM97xx codecs
@@ -2962,7 +2994,7 @@ static int patch_cm9738_specific(struct snd_ac97 * ac97)
return patch_build_controls(ac97, snd_ac97_cm9738_controls, ARRAY_SIZE(snd_ac97_cm9738_controls));
}
-static struct snd_ac97_build_ops patch_cm9738_ops = {
+static const struct snd_ac97_build_ops patch_cm9738_ops = {
.build_specific = patch_cm9738_specific,
.update_jacks = cm9738_update_jacks
};
@@ -2980,15 +3012,9 @@ static int patch_cm9738(struct snd_ac97 * ac97)
static int snd_ac97_cmedia_spdif_playback_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "Analog", "Digital" };
+ static const char * const texts[] = { "Analog", "Digital" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_ac97_cmedia_spdif_playback_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -3053,7 +3079,7 @@ static int patch_cm9739_post_spdif(struct snd_ac97 * ac97)
return patch_build_controls(ac97, snd_ac97_cm9739_controls_spdif, ARRAY_SIZE(snd_ac97_cm9739_controls_spdif));
}
-static struct snd_ac97_build_ops patch_cm9739_ops = {
+static const struct snd_ac97_build_ops patch_cm9739_ops = {
.build_specific = patch_cm9739_specific,
.build_post_spdif = patch_cm9739_post_spdif,
.update_jacks = cm9739_update_jacks
@@ -3085,7 +3111,7 @@ static int patch_cm9739(struct snd_ac97 * ac97)
/* set-up multi channel */
/* bit 14: 0 = SPDIF, 1 = EAPD */
/* bit 13: enable internal vref output for mic */
- /* bit 12: disable center/lfe (swithable) */
+ /* bit 12: disable center/lfe (switchable) */
/* bit 10: disable surround/line (switchable) */
/* bit 9: mix 2 surround off */
/* bit 4: undocumented; 0 mutes the CM9739A, which defaults to 1 */
@@ -3123,22 +3149,22 @@ static void cm9761_update_jacks(struct snd_ac97 *ac97)
/* FIXME: check the bits for each model
* model 83 is confirmed to work
*/
- static unsigned short surr_on[3][2] = {
+ static const unsigned short surr_on[3][2] = {
{ 0x0008, 0x0000 }, /* 9761-78 & 82 */
{ 0x0000, 0x0008 }, /* 9761-82 rev.B */
{ 0x0000, 0x0008 }, /* 9761-83 */
};
- static unsigned short clfe_on[3][2] = {
+ static const unsigned short clfe_on[3][2] = {
{ 0x0000, 0x1000 }, /* 9761-78 & 82 */
{ 0x1000, 0x0000 }, /* 9761-82 rev.B */
{ 0x0000, 0x1000 }, /* 9761-83 */
};
- static unsigned short surr_shared[3][2] = {
+ static const unsigned short surr_shared[3][2] = {
{ 0x0000, 0x0400 }, /* 9761-78 & 82 */
{ 0x0000, 0x0400 }, /* 9761-82 rev.B */
{ 0x0000, 0x0400 }, /* 9761-83 */
};
- static unsigned short clfe_shared[3][2] = {
+ static const unsigned short clfe_shared[3][2] = {
{ 0x2000, 0x0880 }, /* 9761-78 & 82 */
{ 0x0000, 0x2880 }, /* 9761-82 rev.B */
{ 0x2000, 0x0800 }, /* 9761-83 */
@@ -3160,15 +3186,9 @@ static const struct snd_kcontrol_new snd_ac97_cm9761_controls[] = {
static int cm9761_spdif_out_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "AC-Link", "ADC", "SPDIF-In" };
+ static const char * const texts[] = { "AC-Link", "ADC", "SPDIF-In" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2)
- uinfo->value.enumerated.item = 2;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int cm9761_spdif_out_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -3195,7 +3215,9 @@ static int cm9761_spdif_out_source_put(struct snd_kcontrol *kcontrol, struct snd
ucontrol->value.enumerated.item[0] == 1 ? 0x2 : 0);
}
-static const char *cm9761_dac_clock[] = { "AC-Link", "SPDIF-In", "Both" };
+static const char * const cm9761_dac_clock[] = {
+ "AC-Link", "SPDIF-In", "Both"
+};
static const struct ac97_enum cm9761_dac_clock_enum =
AC97_ENUM_SINGLE(AC97_CM9761_SPDIF_CTRL, 9, 3, cm9761_dac_clock);
@@ -3227,7 +3249,7 @@ static int patch_cm9761_specific(struct snd_ac97 * ac97)
return patch_build_controls(ac97, snd_ac97_cm9761_controls, ARRAY_SIZE(snd_ac97_cm9761_controls));
}
-static struct snd_ac97_build_ops patch_cm9761_ops = {
+static const struct snd_ac97_build_ops patch_cm9761_ops = {
.build_specific = patch_cm9761_specific,
.build_post_spdif = patch_cm9761_post_spdif,
.update_jacks = cm9761_update_jacks
@@ -3309,7 +3331,9 @@ static int patch_cm9761(struct snd_ac97 *ac97)
#define AC97_CM9780_MULTI_CHAN 0x66
#define AC97_CM9780_SPDIF 0x6c
-static const char *cm9780_ch_select[] = { "Front", "Side", "Center/LFE", "Rear" };
+static const char * const cm9780_ch_select[] = {
+ "Front", "Side", "Center/LFE", "Rear"
+};
static const struct ac97_enum cm9780_ch_select_enum =
AC97_ENUM_SINGLE(AC97_CM9780_MULTI_CHAN, 6, 4, cm9780_ch_select);
static const struct snd_kcontrol_new cm9780_controls[] = {
@@ -3323,7 +3347,7 @@ static int patch_cm9780_specific(struct snd_ac97 *ac97)
return patch_build_controls(ac97, cm9780_controls, ARRAY_SIZE(cm9780_controls));
}
-static struct snd_ac97_build_ops patch_cm9780_ops = {
+static const struct snd_ac97_build_ops patch_cm9780_ops = {
.build_specific = patch_cm9780_specific,
.build_post_spdif = patch_cm9761_post_spdif /* identical with CM9761 */
};
@@ -3346,6 +3370,33 @@ static int patch_cm9780(struct snd_ac97 *ac97)
}
/*
+ * VIA VT1613 codec
+ */
+static const struct snd_kcontrol_new snd_ac97_controls_vt1613[] = {
+AC97_SINGLE("DC Offset removal", 0x5a, 10, 1, 0),
+};
+
+static int patch_vt1613_specific(struct snd_ac97 *ac97)
+{
+ return patch_build_controls(ac97, &snd_ac97_controls_vt1613[0],
+ ARRAY_SIZE(snd_ac97_controls_vt1613));
+};
+
+static const struct snd_ac97_build_ops patch_vt1613_ops = {
+ .build_specific = patch_vt1613_specific
+};
+
+static int patch_vt1613(struct snd_ac97 *ac97)
+{
+ ac97->build_ops = &patch_vt1613_ops;
+
+ ac97->flags |= AC97_HAS_NO_VIDEO;
+ ac97->caps |= AC97_BC_HEADPHONE;
+
+ return 0;
+}
+
+/*
* VIA VT1616 codec
*/
static const struct snd_kcontrol_new snd_ac97_controls_vt1616[] = {
@@ -3355,7 +3406,7 @@ AC97_SINGLE("Downmix LFE and Center to Front", 0x5a, 12, 1, 0),
AC97_SINGLE("Downmix Surround to Front", 0x5a, 11, 1, 0),
};
-static const char *slave_vols_vt1616[] = {
+static const char * const follower_vols_vt1616[] = {
"Front Playback Volume",
"Surround Playback Volume",
"Center Playback Volume",
@@ -3363,7 +3414,7 @@ static const char *slave_vols_vt1616[] = {
NULL
};
-static const char *slave_sws_vt1616[] = {
+static const char * const follower_sws_vt1616[] = {
"Front Playback Switch",
"Surround Playback Switch",
"Center Playback Switch",
@@ -3375,19 +3426,15 @@ static const char *slave_sws_vt1616[] = {
static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97,
const char *name)
{
- struct snd_ctl_elem_id id;
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, name);
- return snd_ctl_find_id(ac97->bus->card, &id);
+ return snd_ctl_find_id_mixer(ac97->bus->card, name);
}
-/* create a virtual master control and add slaves */
+/* create a virtual master control and add followers */
static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
- const unsigned int *tlv, const char **slaves)
+ const unsigned int *tlv,
+ const char * const *followers)
{
struct snd_kcontrol *kctl;
- const char **s;
int err;
kctl = snd_ctl_make_virtual_master(name, tlv);
@@ -3397,19 +3444,7 @@ static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name,
if (err < 0)
return err;
- for (s = slaves; *s; s++) {
- struct snd_kcontrol *sctl;
-
- sctl = snd_ac97_find_mixer_ctl(ac97, *s);
- if (!sctl) {
- snd_printdd("Cannot find slave %s, skipped\n", *s);
- continue;
- }
- err = snd_ctl_add_slave(kctl, sctl);
- if (err < 0)
- return err;
- }
- return 0;
+ return snd_ctl_add_followers(ac97->bus->card, kctl, followers);
}
static int patch_vt1616_specific(struct snd_ac97 * ac97)
@@ -3417,10 +3452,13 @@ static int patch_vt1616_specific(struct snd_ac97 * ac97)
struct snd_kcontrol *kctl;
int err;
- if (snd_ac97_try_bit(ac97, 0x5a, 9))
- if ((err = patch_build_controls(ac97, &snd_ac97_controls_vt1616[0], 1)) < 0)
+ if (snd_ac97_try_bit(ac97, 0x5a, 9)) {
+ err = patch_build_controls(ac97, &snd_ac97_controls_vt1616[0], 1);
+ if (err < 0)
return err;
- if ((err = patch_build_controls(ac97, &snd_ac97_controls_vt1616[1], ARRAY_SIZE(snd_ac97_controls_vt1616) - 1)) < 0)
+ }
+ err = patch_build_controls(ac97, &snd_ac97_controls_vt1616[1], ARRAY_SIZE(snd_ac97_controls_vt1616) - 1);
+ if (err < 0)
return err;
/* There is already a misnamed master switch. Rename it. */
@@ -3431,19 +3469,19 @@ static int patch_vt1616_specific(struct snd_ac97 * ac97)
snd_ac97_rename_vol_ctl(ac97, "Master Playback", "Front Playback");
err = snd_ac97_add_vmaster(ac97, "Master Playback Volume",
- kctl->tlv.p, slave_vols_vt1616);
+ kctl->tlv.p, follower_vols_vt1616);
if (err < 0)
return err;
err = snd_ac97_add_vmaster(ac97, "Master Playback Switch",
- NULL, slave_sws_vt1616);
+ NULL, follower_sws_vt1616);
if (err < 0)
return err;
return 0;
}
-static struct snd_ac97_build_ops patch_vt1616_ops = {
+static const struct snd_ac97_build_ops patch_vt1616_ops = {
.build_specific = patch_vt1616_specific
};
@@ -3476,11 +3514,12 @@ static int snd_ac97_vt1617a_smart51_info(struct snd_kcontrol *kcontrol,
* is SM51EN *AND* it's Bit14, not Bit15 so the table is very
* counter-intuitive */
- static const char* texts[] = { "LineIn Mic1", "LineIn Mic1 Mic3",
+ static const char * const texts[] = {"LineIn Mic1", "LineIn Mic1 Mic3",
"Surr LFE/C Mic3", "LineIn LFE/C Mic3",
"LineIn Mic2", "LineIn Mic2 Mic1",
"Surr LFE Mic1", "Surr LFE Mic1 Mic2"};
- return ac97_enum_text_info(kcontrol, uinfo, texts, 8);
+
+ return snd_ctl_enum_info(uinfo, 1, 8, texts);
}
static int snd_ac97_vt1617a_smart51_get(struct snd_kcontrol *kcontrol,
@@ -3609,12 +3648,12 @@ static int patch_vt1617a(struct snd_ac97 * ac97)
struct vt1618_uaj_item {
unsigned short mask;
unsigned short shift;
- const char *items[4];
+ const char * const items[4];
};
/* This list reflects the vt1618 docs for Vendor Defined Register 0x60. */
-static struct vt1618_uaj_item vt1618_uaj[3] = {
+static const struct vt1618_uaj_item vt1618_uaj[3] = {
{
/* speaker jack */
.mask = 0x03,
@@ -3644,9 +3683,8 @@ static struct vt1618_uaj_item vt1618_uaj[3] = {
static int snd_ac97_vt1618_UAJ_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- return ac97_enum_text_info(kcontrol, uinfo,
- vt1618_uaj[kcontrol->private_value].items,
- 4);
+ return snd_ctl_enum_info(uinfo, 1, 4,
+ vt1618_uaj[kcontrol->private_value].items);
}
/* All of the vt1618 Universal Audio Jack twiddlers are on
@@ -3659,7 +3697,7 @@ static int snd_ac97_vt1618_UAJ_get(struct snd_kcontrol *kcontrol,
unsigned short datpag, uaj;
struct snd_ac97 *pac97 = snd_kcontrol_chip(kcontrol);
- mutex_lock(&pac97->page_mutex);
+ guard(mutex)(&pac97->page_mutex);
datpag = snd_ac97_read(pac97, AC97_INT_PAGING) & AC97_PAGE_MASK;
snd_ac97_update_bits(pac97, AC97_INT_PAGING, AC97_PAGE_MASK, 0);
@@ -3668,7 +3706,6 @@ static int snd_ac97_vt1618_UAJ_get(struct snd_kcontrol *kcontrol,
vt1618_uaj[kcontrol->private_value].mask;
snd_ac97_update_bits(pac97, AC97_INT_PAGING, AC97_PAGE_MASK, datpag);
- mutex_unlock(&pac97->page_mutex);
ucontrol->value.enumerated.item[0] = uaj >>
vt1618_uaj[kcontrol->private_value].shift;
@@ -3691,9 +3728,9 @@ static int snd_ac97_vt1618_UAJ_put(struct snd_kcontrol *kcontrol,
static int snd_ac97_vt1618_aux_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static const char *txt_aux[] = {"Aux In", "Back Surr Out"};
+ static const char * const txt_aux[] = {"Aux In", "Back Surr Out"};
- return ac97_enum_text_info(kcontrol, uinfo, txt_aux, 2);
+ return snd_ctl_enum_info(uinfo, 1, 2, txt_aux);
}
static int snd_ac97_vt1618_aux_get(struct snd_kcontrol *kcontrol,
@@ -3790,14 +3827,16 @@ static const struct snd_kcontrol_new snd_ac97_spdif_controls_it2646[] = {
static int patch_it2646_specific(struct snd_ac97 * ac97)
{
int err;
- if ((err = patch_build_controls(ac97, snd_ac97_controls_it2646, ARRAY_SIZE(snd_ac97_controls_it2646))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_controls_it2646, ARRAY_SIZE(snd_ac97_controls_it2646));
+ if (err < 0)
return err;
- if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_it2646, ARRAY_SIZE(snd_ac97_spdif_controls_it2646))) < 0)
+ err = patch_build_controls(ac97, snd_ac97_spdif_controls_it2646, ARRAY_SIZE(snd_ac97_spdif_controls_it2646));
+ if (err < 0)
return err;
return 0;
}
-static struct snd_ac97_build_ops patch_it2646_ops = {
+static const struct snd_ac97_build_ops patch_it2646_ops = {
.build_specific = patch_it2646_specific,
.update_jacks = it2646_update_jacks
};
@@ -3825,13 +3864,15 @@ AC97_DOUBLE("Modem Speaker Volume", 0x5c, 14, 12, 3, 1)
static int patch_si3036_specific(struct snd_ac97 * ac97)
{
int idx, err;
- for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_si3036); idx++)
- if ((err = snd_ctl_add(ac97->bus->card, snd_ctl_new1(&snd_ac97_controls_si3036[idx], ac97))) < 0)
+ for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_si3036); idx++) {
+ err = snd_ctl_add(ac97->bus->card, snd_ctl_new1(&snd_ac97_controls_si3036[idx], ac97));
+ if (err < 0)
return err;
+ }
return 0;
}
-static struct snd_ac97_build_ops patch_si3036_ops = {
+static const struct snd_ac97_build_ops patch_si3036_ops = {
.build_specific = patch_si3036_specific,
};
@@ -3851,7 +3892,7 @@ static int mpatch_si3036(struct snd_ac97 * ac97)
* check_volume_resolution().
*/
-static struct snd_ac97_res_table lm4550_restbl[] = {
+static const struct snd_ac97_res_table lm4550_restbl[] = {
{ AC97_MASTER, 0x1f1f },
{ AC97_HEADPHONE, 0x1f1f },
{ AC97_MASTER_MONO, 0x001f },
@@ -3872,41 +3913,3 @@ static int patch_lm4550(struct snd_ac97 *ac97)
ac97->res_table = lm4550_restbl;
return 0;
}
-
-/*
- * UCB1400 codec (http://www.semiconductors.philips.com/acrobat_download/datasheets/UCB1400-02.pdf)
- */
-static const struct snd_kcontrol_new snd_ac97_controls_ucb1400[] = {
-/* enable/disable headphone driver which allows direct connection to
- stereo headphone without the use of external DC blocking
- capacitors */
-AC97_SINGLE("Headphone Driver", 0x6a, 6, 1, 0),
-/* Filter used to compensate the DC offset is added in the ADC to remove idle
- tones from the audio band. */
-AC97_SINGLE("DC Filter", 0x6a, 4, 1, 0),
-/* Control smart-low-power mode feature. Allows automatic power down
- of unused blocks in the ADC analog front end and the PLL. */
-AC97_SINGLE("Smart Low Power Mode", 0x6c, 4, 3, 0),
-};
-
-static int patch_ucb1400_specific(struct snd_ac97 * ac97)
-{
- int idx, err;
- for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_ucb1400); idx++)
- if ((err = snd_ctl_add(ac97->bus->card, snd_ctl_new1(&snd_ac97_controls_ucb1400[idx], ac97))) < 0)
- return err;
- return 0;
-}
-
-static struct snd_ac97_build_ops patch_ucb1400_ops = {
- .build_specific = patch_ucb1400_specific,
-};
-
-static int patch_ucb1400(struct snd_ac97 * ac97)
-{
- ac97->build_ops = &patch_ucb1400_ops;
- /* enable headphone driver and smart low power mode by default */
- snd_ac97_write_cache(ac97, 0x6a, 0x0050);
- snd_ac97_write_cache(ac97, 0x6c, 0x0030);
- return 0;
-}
diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h
index 47bf8dfe8276..a8cd89cfd6ff 100644
--- a/sound/pci/ac97/ac97_patch.h
+++ b/sound/pci/ac97/ac97_patch.h
@@ -1,25 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Universal interface for Audio Codec '97
*
* For more details look to AC '97 component specification revision 2.2
* by Intel Corporation (http://developer.intel.com).
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define AC97_SINGLE_VALUE(reg,shift,mask,invert) \
@@ -49,7 +34,7 @@ struct ac97_enum {
unsigned char shift_l;
unsigned char shift_r;
unsigned short mask;
- const char **texts;
+ const char * const *texts;
};
#define AC97_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \
diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c
index 48cbda9378c5..4715d88ff8f4 100644
--- a/sound/pci/ac97/ac97_pcm.c
+++ b/sound/pci/ac97/ac97_pcm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Universal interface for Audio Codec '97
@@ -5,28 +6,13 @@
* For more details look to AC '97 component specification revision 2.2
* by Intel Corporation (http://developer.intel.com) and to datasheets
* for specific codecs.
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mutex.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -40,7 +26,7 @@
* PCM support
*/
-static unsigned char rate_reg_tables[2][4][9] = {
+static const unsigned char rate_reg_tables[2][4][9] = {
{
/* standard rates */
{
@@ -143,7 +129,7 @@ static unsigned char rate_reg_tables[2][4][9] = {
}};
/* FIXME: more various mappings for ADC? */
-static unsigned char rate_cregs[9] = {
+static const unsigned char rate_cregs[9] = {
AC97_PCM_LR_ADC_RATE, /* 3 */
AC97_PCM_LR_ADC_RATE, /* 4 */
0xff, /* 5 */
@@ -206,7 +192,7 @@ static int set_spdif_rate(struct snd_ac97 *ac97, unsigned short rate)
mask = AC97_SC_SPSR_MASK;
}
- mutex_lock(&ac97->reg_mutex);
+ guard(mutex)(&ac97->reg_mutex);
old = snd_ac97_read(ac97, reg) & mask;
if (old != bits) {
snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);
@@ -231,7 +217,6 @@ static int set_spdif_rate(struct snd_ac97 *ac97, unsigned short rate)
ac97->spdif_status = sbits;
}
snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF);
- mutex_unlock(&ac97->reg_mutex);
return 0;
}
@@ -245,14 +230,14 @@ static int set_spdif_rate(struct snd_ac97 *ac97, unsigned short rate)
* If the codec doesn't support VAR, the rate must be 48000 (except
* for SPDIF).
*
- * The valid registers are AC97_PMC_MIC_ADC_RATE,
+ * The valid registers are AC97_PCM_MIC_ADC_RATE,
* AC97_PCM_FRONT_DAC_RATE, AC97_PCM_LR_ADC_RATE.
* AC97_PCM_SURR_DAC_RATE and AC97_PCM_LFE_DAC_RATE are accepted
* if the codec supports them.
* AC97_SPDIF is accepted as a pseudo register to modify the SPDIF
* status bits.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_set_rate(struct snd_ac97 *ac97, int reg, unsigned int rate)
{
@@ -439,6 +424,8 @@ static unsigned int get_rates(struct ac97_pcm *pcm, unsigned int cidx, unsigned
* It assigns available AC97 slots for given PCMs. If none or only
* some slots are available, pcm->xxx.slots and pcm->xxx.rslots[] members
* are reduced and might be zero.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_pcm_assign(struct snd_ac97_bus *bus,
unsigned short pcms_count,
@@ -561,6 +548,8 @@ EXPORT_SYMBOL(snd_ac97_pcm_assign);
* @slots: a subset of allocated slots (snd_ac97_pcm_assign) for this pcm
*
* It locks the specified slots and sets the given rate to AC97 registers.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate,
enum ac97_pcm_cfg cfg, unsigned short slots)
@@ -581,31 +570,31 @@ int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate,
return err;
}
}
- spin_lock_irq(&pcm->bus->bus_lock);
- for (i = 3; i < 12; i++) {
- if (!(slots & (1 << i)))
- continue;
- ok_flag = 0;
- for (cidx = 0; cidx < 4; cidx++) {
- if (bus->used_slots[pcm->stream][cidx] & (1 << i)) {
- spin_unlock_irq(&pcm->bus->bus_lock);
- err = -EBUSY;
- goto error;
+ scoped_guard(spinlock_irq, &pcm->bus->bus_lock) {
+ for (i = 3; i < 12; i++) {
+ if (!(slots & (1 << i)))
+ continue;
+ ok_flag = 0;
+ for (cidx = 0; cidx < 4; cidx++) {
+ if (bus->used_slots[pcm->stream][cidx] & (1 << i)) {
+ err = -EBUSY;
+ goto error;
+ }
+ if (pcm->r[r].rslots[cidx] & (1 << i)) {
+ bus->used_slots[pcm->stream][cidx] |= (1 << i);
+ ok_flag++;
+ }
}
- if (pcm->r[r].rslots[cidx] & (1 << i)) {
- bus->used_slots[pcm->stream][cidx] |= (1 << i);
- ok_flag++;
+ if (!ok_flag) {
+ dev_err(bus->card->dev,
+ "cannot find configuration for AC97 slot %i\n",
+ i);
+ err = -EAGAIN;
+ goto error;
}
}
- if (!ok_flag) {
- spin_unlock_irq(&pcm->bus->bus_lock);
- snd_printk(KERN_ERR "cannot find configuration for AC97 slot %i\n", i);
- err = -EAGAIN;
- goto error;
- }
+ pcm->cur_dbl = r;
}
- pcm->cur_dbl = r;
- spin_unlock_irq(&pcm->bus->bus_lock);
for (i = 3; i < 12; i++) {
if (!(slots & (1 << i)))
continue;
@@ -613,15 +602,20 @@ int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate,
if (pcm->r[r].rslots[cidx] & (1 << i)) {
reg = get_slot_reg(pcm, cidx, i, r);
if (reg == 0xff) {
- snd_printk(KERN_ERR "invalid AC97 slot %i?\n", i);
+ dev_err(bus->card->dev,
+ "invalid AC97 slot %i?\n", i);
continue;
}
if (reg_ok[cidx] & (1 << (reg - AC97_PCM_FRONT_DAC_RATE)))
continue;
- //printk(KERN_DEBUG "setting ac97 reg 0x%x to rate %d\n", reg, rate);
+ dev_dbg(bus->card->dev,
+ "setting ac97 reg 0x%x to rate %d\n",
+ reg, rate);
err = snd_ac97_set_rate(pcm->r[r].codec[cidx], reg, rate);
if (err < 0)
- snd_printk(KERN_ERR "error in snd_ac97_set_rate: cidx=%d, reg=0x%x, rate=%d, err=%d\n", cidx, reg, rate, err);
+ dev_err(bus->card->dev,
+ "error in snd_ac97_set_rate: cidx=%d, reg=0x%x, rate=%d, err=%d\n",
+ cidx, reg, rate, err);
else
reg_ok[cidx] |= (1 << (reg - AC97_PCM_FRONT_DAC_RATE));
}
@@ -643,6 +637,8 @@ EXPORT_SYMBOL(snd_ac97_pcm_open);
* @pcm: the ac97 pcm instance
*
* It frees the locked AC97 slots.
+ *
+ * Return: Zero.
*/
int snd_ac97_pcm_close(struct ac97_pcm *pcm)
{
@@ -666,7 +662,7 @@ int snd_ac97_pcm_close(struct ac97_pcm *pcm)
#endif
bus = pcm->bus;
- spin_lock_irq(&pcm->bus->bus_lock);
+ guard(spinlock_irq)(&pcm->bus->bus_lock);
for (i = 3; i < 12; i++) {
if (!(slots & (1 << i)))
continue;
@@ -675,7 +671,6 @@ int snd_ac97_pcm_close(struct ac97_pcm *pcm)
}
pcm->aslots = 0;
pcm->cur_dbl = 0;
- spin_unlock_irq(&pcm->bus->bus_lock);
return 0;
}
@@ -717,6 +712,8 @@ static int double_rate_hw_constraint_channels(struct snd_pcm_hw_params *params,
*
* Installs the hardware constraint rules to prevent using double rates and
* more than two channels at the same time.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime)
{
diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c
index 6320bf084e47..1c9d76994b3a 100644
--- a/sound/pci/ac97/ac97_proc.c
+++ b/sound/pci/ac97/ac97_proc.c
@@ -1,25 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Universal interface for Audio Codec '97
*
* For more details look to AC '97 component specification revision 2.2
* by Intel Corporation (http://developer.intel.com).
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/mutex.h>
@@ -113,7 +98,7 @@ static void snd_ac97_proc_read_main(struct snd_ac97 *ac97, struct snd_info_buffe
static const char *spdif_rates_cs4205[4] = { " Rate=48kHz", " Rate=44.1kHz", " Rate=res", " Rate=res" };
static const char *double_rate_slots[4] = { "10/11", "7/8", "reserved", "reserved" };
- snd_ac97_get_name(NULL, ac97->id, name, 0);
+ snd_ac97_get_name(NULL, ac97->id, name, sizeof(name), 0);
snd_iprintf(buffer, "%d-%d/%d: %s\n\n", ac97->addr, ac97->num, subidx, name);
if ((ac97->scaps & AC97_SCAP_AUDIO) == 0)
@@ -176,12 +161,12 @@ static void snd_ac97_proc_read_main(struct snd_ac97 *ac97, struct snd_info_buffe
"Mic select : %s\n"
"ADC/DAC loopback : %s\n",
val & 0x8000 ? "post" : "pre",
- val & 0x4000 ? "on" : "off",
- val & 0x2000 ? "on" : "off",
- val & 0x1000 ? "on" : "off",
+ str_on_off(val & 0x4000),
+ str_on_off(val & 0x2000),
+ str_on_off(val & 0x1000),
val & 0x0200 ? "Mic" : "MIX",
val & 0x0100 ? "Mic2" : "Mic1",
- val & 0x0080 ? "on" : "off");
+ str_on_off(val & 0x0080));
if (ac97->ext_id & AC97_EI_DRA)
snd_iprintf(buffer, "Double rate slots: %s\n",
double_rate_slots[(val >> 10) & 3]);
@@ -344,7 +329,7 @@ static void snd_ac97_proc_read(struct snd_info_entry *entry, struct snd_info_buf
{
struct snd_ac97 *ac97 = entry->private_data;
- mutex_lock(&ac97->page_mutex);
+ guard(mutex)(&ac97->page_mutex);
if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) { // Analog Devices AD1881/85/86
int idx;
for (idx = 0; idx < 3; idx++)
@@ -370,7 +355,6 @@ static void snd_ac97_proc_read(struct snd_info_entry *entry, struct snd_info_buf
} else {
snd_ac97_proc_read_main(ac97, buffer, 0);
}
- mutex_unlock(&ac97->page_mutex);
}
#ifdef CONFIG_SND_DEBUG
@@ -380,7 +364,8 @@ static void snd_ac97_proc_regs_write(struct snd_info_entry *entry, struct snd_in
struct snd_ac97 *ac97 = entry->private_data;
char line[64];
unsigned int reg, val;
- mutex_lock(&ac97->page_mutex);
+
+ guard(mutex)(&ac97->page_mutex);
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x", &reg, &val) != 2)
continue;
@@ -388,7 +373,6 @@ static void snd_ac97_proc_regs_write(struct snd_info_entry *entry, struct snd_in
if (reg < 0x80 && (reg & 1) == 0 && val <= 0xffff)
snd_ac97_write_cache(ac97, reg, val);
}
- mutex_unlock(&ac97->page_mutex);
}
#endif
@@ -407,7 +391,7 @@ static void snd_ac97_proc_regs_read(struct snd_info_entry *entry,
{
struct snd_ac97 *ac97 = entry->private_data;
- mutex_lock(&ac97->page_mutex);
+ guard(mutex)(&ac97->page_mutex);
if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) { // Analog Devices AD1881/85/86
int idx;
@@ -423,7 +407,6 @@ static void snd_ac97_proc_regs_read(struct snd_info_entry *entry,
} else {
snd_ac97_proc_regs_read_main(ac97, buffer, 0);
}
- mutex_unlock(&ac97->page_mutex);
}
void snd_ac97_proc_init(struct snd_ac97 * ac97)
@@ -436,25 +419,20 @@ void snd_ac97_proc_init(struct snd_ac97 * ac97)
return;
prefix = ac97_is_audio(ac97) ? "ac97" : "mc97";
sprintf(name, "%s#%d-%d", prefix, ac97->addr, ac97->num);
- if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) {
+ entry = snd_info_create_card_entry(ac97->bus->card, name,
+ ac97->bus->proc);
+ if (entry)
snd_info_set_text_ops(entry, ac97, snd_ac97_proc_read);
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
ac97->proc = entry;
sprintf(name, "%s#%d-%d+regs", prefix, ac97->addr, ac97->num);
- if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) {
+ entry = snd_info_create_card_entry(ac97->bus->card, name,
+ ac97->bus->proc);
+ if (entry) {
snd_info_set_text_ops(entry, ac97, snd_ac97_proc_regs_read);
#ifdef CONFIG_SND_DEBUG
- entry->mode |= S_IWUSR;
+ entry->mode |= 0200;
entry->c.text.write = snd_ac97_proc_regs_write;
#endif
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
}
ac97->proc_regs = entry;
}
@@ -473,13 +451,10 @@ void snd_ac97_bus_proc_init(struct snd_ac97_bus * bus)
char name[32];
sprintf(name, "codec97#%d", bus->num);
- if ((entry = snd_info_create_card_entry(bus->card, name, bus->card->proc_root)) != NULL) {
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
+ entry = snd_info_create_card_entry(bus->card, name,
+ bus->card->proc_root);
+ if (entry)
+ entry->mode = S_IFDIR | 0555;
bus->proc = entry;
}
diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c
index 4382d0fa6b9a..f4ec404c0d15 100644
--- a/sound/pci/ad1889.c
+++ b/sound/pci/ad1889.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Analog Devices 1889 audio driver
*
* This is a driver for the AD1889 PCI audio chipset found
@@ -7,19 +8,6 @@
* Copyright (C) 2005, Thibaut Varene <varenet@parisc-linux.org>
* Based on the OSS AD1889 driver by Randolph Chung <tausq@debian.org>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2, as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
* TODO:
* Do we need to take care of CCS register?
* Maybe we could use finer grained locking (separate locks for pb/cap)?
@@ -29,7 +17,7 @@
* PM support
* MIDI support
* Game Port support
- * SG DMA support (this will need *alot* of work)
+ * SG DMA support (this will need *a lot* of work)
*/
#include <linux/init.h>
@@ -39,14 +27,14 @@
#include <linux/interrupt.h>
#include <linux/compiler.h>
#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/ac97_codec.h>
-#include <asm/io.h>
-
#include "ad1889.h"
#include "ac97/ac97_id.h"
@@ -55,7 +43,6 @@
MODULE_AUTHOR("Kyle McMartin <kyle@parisc-linux.org>, Thibaut Varene <t-bone@parisc-linux.org>");
MODULE_DESCRIPTION("Analog Devices AD1889 ALSA sound driver");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Analog Devices,AD1889}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
module_param_array(index, int, NULL, 0444);
@@ -65,7 +52,7 @@ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for the AD1889 soundcard.");
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable AD1889 soundcard.");
@@ -76,9 +63,6 @@ MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");
#define DEVNAME "ad1889"
#define PFX DEVNAME ": "
-/* let's use the global sound debug interfaces */
-#define ad1889_debug(fmt, arg...) snd_printd(KERN_DEBUG fmt, ## arg)
-
/* keep track of some hw registers */
struct ad1889_register_state {
u16 reg; /* reg setup */
@@ -261,32 +245,18 @@ snd_ad1889_ac97_ready(struct snd_ad1889 *chip)
while (!(ad1889_readw(chip, AD_AC97_ACIC) & AD_AC97_ACIC_ACRDY)
&& --retry)
- mdelay(1);
+ usleep_range(1000, 2000);
if (!retry) {
- snd_printk(KERN_ERR PFX "[%s] Link is not ready.\n",
- __func__);
+ dev_err(chip->card->dev, "[%s] Link is not ready.\n",
+ __func__);
return -EIO;
}
- ad1889_debug("[%s] ready after %d ms\n", __func__, 400 - retry);
+ dev_dbg(chip->card->dev, "[%s] ready after %d ms\n", __func__, 400 - retry);
return 0;
}
-static int
-snd_ad1889_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-static int
-snd_ad1889_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-static struct snd_pcm_hardware snd_ad1889_playback_hw = {
+static const struct snd_pcm_hardware snd_ad1889_playback_hw = {
.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BLOCK_TRANSFER,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
@@ -303,7 +273,7 @@ static struct snd_pcm_hardware snd_ad1889_playback_hw = {
/*.fifo_size = 0,*/
};
-static struct snd_pcm_hardware snd_ad1889_capture_hw = {
+static const struct snd_pcm_hardware snd_ad1889_capture_hw = {
.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BLOCK_TRANSFER,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
@@ -383,7 +353,7 @@ snd_ad1889_playback_prepare(struct snd_pcm_substream *ss)
reg |= AD_DS_WSMC_WAST;
/* let's make sure we don't clobber ourselves */
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
chip->wave.size = size;
chip->wave.reg = reg;
@@ -402,11 +372,9 @@ snd_ad1889_playback_prepare(struct snd_pcm_substream *ss)
/* writes flush */
ad1889_readw(chip, AD_DS_WSMC);
- spin_unlock_irq(&chip->lock);
-
- ad1889_debug("prepare playback: addr = 0x%x, count = %u, "
- "size = %u, reg = 0x%x, rate = %u\n", chip->wave.addr,
- count, size, reg, rt->rate);
+ dev_dbg(chip->card->dev,
+ "prepare playback: addr = 0x%x, count = %u, size = %u, reg = 0x%x, rate = %u\n",
+ chip->wave.addr, count, size, reg, rt->rate);
return 0;
}
@@ -433,7 +401,7 @@ snd_ad1889_capture_prepare(struct snd_pcm_substream *ss)
reg |= AD_DS_RAMC_ADST;
/* let's make sure we don't clobber ourselves */
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
chip->ramc.size = size;
chip->ramc.reg = reg;
@@ -449,11 +417,9 @@ snd_ad1889_capture_prepare(struct snd_pcm_substream *ss)
/* writes flush */
ad1889_readw(chip, AD_DS_RAMC);
- spin_unlock_irq(&chip->lock);
-
- ad1889_debug("prepare capture: addr = 0x%x, count = %u, "
- "size = %u, reg = 0x%x, rate = %u\n", chip->ramc.addr,
- count, size, reg, rt->rate);
+ dev_dbg(chip->card->dev,
+ "prepare capture: addr = 0x%x, count = %u, size = %u, reg = 0x%x, rate = %u\n",
+ chip->ramc.addr, count, size, reg, rt->rate);
return 0;
}
@@ -574,23 +540,17 @@ snd_ad1889_capture_pointer(struct snd_pcm_substream *ss)
return bytes_to_frames(ss->runtime, ptr);
}
-static struct snd_pcm_ops snd_ad1889_playback_ops = {
+static const struct snd_pcm_ops snd_ad1889_playback_ops = {
.open = snd_ad1889_playback_open,
.close = snd_ad1889_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ad1889_hw_params,
- .hw_free = snd_ad1889_hw_free,
.prepare = snd_ad1889_playback_prepare,
.trigger = snd_ad1889_playback_trigger,
.pointer = snd_ad1889_playback_pointer,
};
-static struct snd_pcm_ops snd_ad1889_capture_ops = {
+static const struct snd_pcm_ops snd_ad1889_capture_ops = {
.open = snd_ad1889_capture_open,
.close = snd_ad1889_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ad1889_hw_params,
- .hw_free = snd_ad1889_hw_free,
.prepare = snd_ad1889_capture_prepare,
.trigger = snd_ad1889_capture_trigger,
.pointer = snd_ad1889_capture_pointer,
@@ -613,7 +573,8 @@ snd_ad1889_interrupt(int irq, void *dev_id)
return IRQ_NONE;
if (st & (AD_DMA_DISR_PMAI|AD_DMA_DISR_PTAI))
- ad1889_debug("Unexpected master or target abort interrupt!\n");
+ dev_dbg(chip->card->dev,
+ "Unexpected master or target abort interrupt!\n");
if ((st & AD_DMA_DISR_WAVI) && chip->psubs)
snd_pcm_period_elapsed(chip->psubs);
@@ -623,15 +584,12 @@ snd_ad1889_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int __devinit
-snd_ad1889_pcm_init(struct snd_ad1889 *chip, int device, struct snd_pcm **rpcm)
+static int
+snd_ad1889_pcm_init(struct snd_ad1889 *chip, int device)
{
int err;
struct snd_pcm *pcm;
- if (rpcm)
- *rpcm = NULL;
-
err = snd_pcm_new(chip->card, chip->card->driver, device, 1, 1, &pcm);
if (err < 0)
return err;
@@ -643,25 +601,15 @@ snd_ad1889_pcm_init(struct snd_ad1889 *chip, int device, struct snd_pcm **rpcm)
pcm->private_data = chip;
pcm->info_flags = 0;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
chip->pcm = pcm;
chip->psubs = NULL;
chip->csubs = NULL;
- err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- BUFFER_BYTES_MAX / 2,
- BUFFER_BYTES_MAX);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
+ BUFFER_BYTES_MAX / 2, BUFFER_BYTES_MAX);
- if (err < 0) {
- snd_printk(KERN_ERR PFX "buffer allocation error: %d\n", err);
- return err;
- }
-
- if (rpcm)
- *rpcm = pcm;
-
return 0;
}
@@ -674,7 +622,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
reg = ad1889_readw(chip, AD_DS_WSMC);
snd_iprintf(buffer, "Wave output: %s\n",
- (reg & AD_DS_WSMC_WAEN) ? "enabled" : "disabled");
+ str_enabled_disabled(reg & AD_DS_WSMC_WAEN));
snd_iprintf(buffer, "Wave Channels: %s\n",
(reg & AD_DS_WSMC_WAST) ? "stereo" : "mono");
snd_iprintf(buffer, "Wave Quality: %d-bit linear\n",
@@ -682,7 +630,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
/* WARQ is at offset 12 */
tmp = (reg & AD_DS_WSMC_WARQ) ?
- (((reg & AD_DS_WSMC_WARQ >> 12) & 0x01) ? 12 : 18) : 4;
+ ((((reg & AD_DS_WSMC_WARQ) >> 12) & 0x01) ? 12 : 18) : 4;
tmp /= (reg & AD_DS_WSMC_WAST) ? 2 : 1;
snd_iprintf(buffer, "Wave FIFO: %d %s words\n\n", tmp,
@@ -690,11 +638,11 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
snd_iprintf(buffer, "Synthesis output: %s\n",
- reg & AD_DS_WSMC_SYEN ? "enabled" : "disabled");
+ str_enabled_disabled(reg & AD_DS_WSMC_SYEN));
/* SYRQ is at offset 4 */
tmp = (reg & AD_DS_WSMC_SYRQ) ?
- (((reg & AD_DS_WSMC_SYRQ >> 4) & 0x01) ? 12 : 18) : 4;
+ ((((reg & AD_DS_WSMC_SYRQ) >> 4) & 0x01) ? 12 : 18) : 4;
tmp /= (reg & AD_DS_WSMC_WAST) ? 2 : 1;
snd_iprintf(buffer, "Synthesis FIFO: %d %s words\n\n", tmp,
@@ -702,7 +650,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
reg = ad1889_readw(chip, AD_DS_RAMC);
snd_iprintf(buffer, "ADC input: %s\n",
- (reg & AD_DS_RAMC_ADEN) ? "enabled" : "disabled");
+ str_enabled_disabled(reg & AD_DS_RAMC_ADEN));
snd_iprintf(buffer, "ADC Channels: %s\n",
(reg & AD_DS_RAMC_ADST) ? "stereo" : "mono");
snd_iprintf(buffer, "ADC Quality: %d-bit linear\n",
@@ -710,18 +658,18 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
/* ACRQ is at offset 4 */
tmp = (reg & AD_DS_RAMC_ACRQ) ?
- (((reg & AD_DS_RAMC_ACRQ >> 4) & 0x01) ? 12 : 18) : 4;
+ ((((reg & AD_DS_RAMC_ACRQ) >> 4) & 0x01) ? 12 : 18) : 4;
tmp /= (reg & AD_DS_RAMC_ADST) ? 2 : 1;
snd_iprintf(buffer, "ADC FIFO: %d %s words\n\n", tmp,
(reg & AD_DS_RAMC_ADST) ? "stereo" : "mono");
snd_iprintf(buffer, "Resampler input: %s\n",
- reg & AD_DS_RAMC_REEN ? "enabled" : "disabled");
+ str_enabled_disabled(reg & AD_DS_RAMC_REEN));
/* RERQ is at offset 12 */
tmp = (reg & AD_DS_RAMC_RERQ) ?
- (((reg & AD_DS_RAMC_RERQ >> 12) & 0x01) ? 12 : 18) : 4;
+ ((((reg & AD_DS_RAMC_RERQ) >> 12) & 0x01) ? 12 : 18) : 4;
tmp /= (reg & AD_DS_RAMC_ADST) ? 2 : 1;
snd_iprintf(buffer, "Resampler FIFO: %d %s words\n\n", tmp,
@@ -738,7 +686,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
reg = ad1889_readw(chip, AD_DS_WADA);
snd_iprintf(buffer, "Right: %s, -%d dB\n",
(reg & AD_DS_WADA_RWAM) ? "mute" : "unmute",
- ((reg & AD_DS_WADA_RWAA) >> 8) * 3);
+ (reg & AD_DS_WADA_RWAA) * 3);
reg = ad1889_readw(chip, AD_DS_WAS);
snd_iprintf(buffer, "Wave samplerate: %u Hz\n", reg);
@@ -746,16 +694,14 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
snd_iprintf(buffer, "Resampler samplerate: %u Hz\n", reg);
}
-static void __devinit
+static void
snd_ad1889_proc_init(struct snd_ad1889 *chip)
{
- struct snd_info_entry *entry;
-
- if (!snd_card_proc_new(chip->card, chip->card->driver, &entry))
- snd_info_set_text_ops(entry, chip, snd_ad1889_proc_read);
+ snd_card_ro_proc_new(chip->card, chip->card->driver,
+ chip, snd_ad1889_proc_read);
}
-static struct ac97_quirk ac97_quirks[] = {
+static const struct ac97_quirk ac97_quirks[] = {
{
.subvendor = 0x11d4, /* AD */
.subdevice = 0x1889, /* AD1889 */
@@ -766,7 +712,7 @@ static struct ac97_quirk ac97_quirks[] = {
{ } /* terminator */
};
-static void __devinit
+static void
snd_ad1889_ac97_xinit(struct snd_ad1889 *chip)
{
u16 reg;
@@ -790,26 +736,12 @@ snd_ad1889_ac97_xinit(struct snd_ad1889 *chip)
}
-static void
-snd_ad1889_ac97_bus_free(struct snd_ac97_bus *bus)
-{
- struct snd_ad1889 *chip = bus->private_data;
- chip->ac97_bus = NULL;
-}
-
-static void
-snd_ad1889_ac97_free(struct snd_ac97 *ac97)
-{
- struct snd_ad1889 *chip = ac97->private_data;
- chip->ac97 = NULL;
-}
-
-static int __devinit
+static int
snd_ad1889_ac97_init(struct snd_ad1889 *chip, const char *quirk_override)
{
int err;
struct snd_ac97_template ac97;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_ad1889_ac97_write,
.read = snd_ad1889_ac97_read,
};
@@ -821,11 +753,8 @@ snd_ad1889_ac97_init(struct snd_ad1889 *chip, const char *quirk_override)
if (err < 0)
return err;
- chip->ac97_bus->private_free = snd_ad1889_ac97_bus_free;
-
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
- ac97.private_free = snd_ad1889_ac97_free;
ac97.pci = chip->pci;
err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97);
@@ -837,13 +766,12 @@ snd_ad1889_ac97_init(struct snd_ad1889 *chip, const char *quirk_override)
return 0;
}
-static int
-snd_ad1889_free(struct snd_ad1889 *chip)
+static void
+snd_ad1889_free(struct snd_card *card)
{
- if (chip->irq < 0)
- goto skip_hw;
+ struct snd_ad1889 *chip = card->private_data;
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
ad1889_mute(chip);
@@ -853,133 +781,64 @@ snd_ad1889_free(struct snd_ad1889 *chip)
/* clear DISR. If we don't, we'd better jump off the Eiffel Tower */
ad1889_writel(chip, AD_DMA_DISR, AD_DMA_DISR_PTAI | AD_DMA_DISR_PMAI);
ad1889_readl(chip, AD_DMA_DISR); /* flush, dammit! */
-
- spin_unlock_irq(&chip->lock);
-
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
-
-skip_hw:
- if (chip->iobase)
- iounmap(chip->iobase);
-
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
-
- kfree(chip);
- return 0;
}
static int
-snd_ad1889_dev_free(struct snd_device *device)
-{
- struct snd_ad1889 *chip = device->device_data;
- return snd_ad1889_free(chip);
-}
-
-static int __devinit
-snd_ad1889_init(struct snd_ad1889 *chip)
-{
- ad1889_writew(chip, AD_DS_CCS, AD_DS_CCS_CLKEN); /* turn on clock */
- ad1889_readw(chip, AD_DS_CCS); /* flush posted write */
-
- mdelay(10);
-
- /* enable Master and Target abort interrupts */
- ad1889_writel(chip, AD_DMA_DISR, AD_DMA_DISR_PMAE | AD_DMA_DISR_PTAE);
-
- return 0;
-}
-
-static int __devinit
-snd_ad1889_create(struct snd_card *card,
- struct pci_dev *pci,
- struct snd_ad1889 **rchip)
+snd_ad1889_create(struct snd_card *card, struct pci_dev *pci)
{
+ struct snd_ad1889 *chip = card->private_data;
int err;
- struct snd_ad1889 *chip;
- static struct snd_device_ops ops = {
- .dev_free = snd_ad1889_dev_free,
- };
-
- *rchip = NULL;
-
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
/* check PCI availability (32bit DMA) */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
- printk(KERN_ERR PFX "error setting 32-bit DMA mask.\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
+ dev_err(card->dev, "error setting 32-bit DMA mask.\n");
return -ENXIO;
}
- /* allocate chip specific data with zero-filled memory */
- if ((chip = kzalloc(sizeof(*chip), GFP_KERNEL)) == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
chip->card = card;
- card->private_data = chip;
chip->pci = pci;
chip->irq = -1;
/* (1) PCI resource allocation */
- if ((err = pci_request_regions(pci, card->driver)) < 0)
- goto free_and_ret;
+ chip->iobase = pcim_iomap_region(pci, 0, card->driver);
+ if (IS_ERR(chip->iobase))
+ return PTR_ERR(chip->iobase);
chip->bar = pci_resource_start(pci, 0);
- chip->iobase = pci_ioremap_bar(pci, 0);
- if (chip->iobase == NULL) {
- printk(KERN_ERR PFX "unable to reserve region.\n");
- err = -EBUSY;
- goto free_and_ret;
- }
pci_set_master(pci);
spin_lock_init(&chip->lock); /* only now can we call ad1889_free */
- if (request_irq(pci->irq, snd_ad1889_interrupt,
- IRQF_SHARED, card->driver, chip)) {
- printk(KERN_ERR PFX "cannot obtain IRQ %d\n", pci->irq);
- snd_ad1889_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_ad1889_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "cannot obtain IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
- synchronize_irq(chip->irq);
+ card->sync_irq = chip->irq;
+ card->private_free = snd_ad1889_free;
/* (2) initialization of the chip hardware */
- if ((err = snd_ad1889_init(chip)) < 0) {
- snd_ad1889_free(chip);
- return err;
- }
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_ad1889_free(chip);
- return err;
- }
+ ad1889_writew(chip, AD_DS_CCS, AD_DS_CCS_CLKEN); /* turn on clock */
+ ad1889_readw(chip, AD_DS_CCS); /* flush posted write */
- snd_card_set_dev(card, &pci->dev);
+ usleep_range(10000, 11000);
- *rchip = chip;
+ /* enable Master and Target abort interrupts */
+ ad1889_writel(chip, AD_DMA_DISR, AD_DMA_DISR_PMAE | AD_DMA_DISR_PTAE);
return 0;
-
-free_and_ret:
- kfree(chip);
- pci_disable_device(pci);
-
- return err;
}
-static int __devinit
-snd_ad1889_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int
+__snd_ad1889_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
int err;
static int devno;
@@ -995,18 +854,19 @@ snd_ad1889_probe(struct pci_dev *pci,
}
/* (2) */
- err = snd_card_create(index[devno], id[devno], THIS_MODULE, 0, &card);
- /* XXX REVISIT: we can probably allocate chip in this call */
+ err = snd_devm_card_new(&pci->dev, index[devno], id[devno], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- strcpy(card->driver, "AD1889");
- strcpy(card->shortname, "Analog Devices AD1889");
+ strscpy(card->driver, "AD1889");
+ strscpy(card->shortname, "Analog Devices AD1889");
/* (3) */
- err = snd_ad1889_create(card, pci, &chip);
+ err = snd_ad1889_create(card, pci);
if (err < 0)
- goto free_and_ret;
+ return err;
/* (4) */
sprintf(card->longname, "%s at 0x%lx irq %i",
@@ -1016,11 +876,11 @@ snd_ad1889_probe(struct pci_dev *pci,
/* register AC97 mixer */
err = snd_ad1889_ac97_init(chip, ac97_quirk[devno]);
if (err < 0)
- goto free_and_ret;
+ return err;
- err = snd_ad1889_pcm_init(chip, 0, NULL);
+ err = snd_ad1889_pcm_init(chip, 0);
if (err < 0)
- goto free_and_ret;
+ return err;
/* register proc interface */
snd_ad1889_proc_init(chip);
@@ -1028,50 +888,31 @@ snd_ad1889_probe(struct pci_dev *pci,
/* (6) */
err = snd_card_register(card);
if (err < 0)
- goto free_and_ret;
+ return err;
/* (7) */
pci_set_drvdata(pci, card);
devno++;
return 0;
-
-free_and_ret:
- snd_card_free(card);
- return err;
}
-static void __devexit
-snd_ad1889_remove(struct pci_dev *pci)
+static int snd_ad1889_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_ad1889_probe(pci, pci_id));
}
-static DEFINE_PCI_DEVICE_TABLE(snd_ad1889_ids) = {
+static const struct pci_device_id snd_ad1889_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_ANALOG_DEVICES, PCI_DEVICE_ID_AD1889JS) },
{ 0, },
};
MODULE_DEVICE_TABLE(pci, snd_ad1889_ids);
static struct pci_driver ad1889_pci_driver = {
- .name = "AD1889 Audio",
+ .name = KBUILD_MODNAME,
.id_table = snd_ad1889_ids,
.probe = snd_ad1889_probe,
- .remove = __devexit_p(snd_ad1889_remove),
};
-static int __init
-alsa_ad1889_init(void)
-{
- return pci_register_driver(&ad1889_pci_driver);
-}
-
-static void __exit
-alsa_ad1889_fini(void)
-{
- pci_unregister_driver(&ad1889_pci_driver);
-}
-
-module_init(alsa_ad1889_init);
-module_exit(alsa_ad1889_fini);
+module_pci_driver(ad1889_pci_driver);
diff --git a/sound/pci/ad1889.h b/sound/pci/ad1889.h
index 5e6dad5341a1..d6e8d6c19adc 100644
--- a/sound/pci/ad1889.h
+++ b/sound/pci/ad1889.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/* Analog Devices 1889 audio driver
* Copyright (C) 2004, Kyle McMartin <kyle@parisc-linux.org>
*/
diff --git a/sound/pci/ak4531_codec.c b/sound/pci/ak4531_codec.c
index fd135e3d8a84..cdad47e4098d 100644
--- a/sound/pci/ak4531_codec.c
+++ b/sound/pci/ak4531_codec.c
@@ -1,28 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Universal routines for AK4531 codec
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mutex.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/ak4531_codec.h>
@@ -34,11 +20,7 @@ MODULE_DESCRIPTION("Universal routines for AK4531 codec");
MODULE_LICENSE("GPL");
*/
-#ifdef CONFIG_PROC_FS
static void snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531);
-#else
-#define snd_ak4531_proc_init(card,ak)
-#endif
/*
*
@@ -95,9 +77,8 @@ static int snd_ak4531_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
int invert = (kcontrol->private_value >> 22) & 1;
int val;
- mutex_lock(&ak4531->reg_mutex);
+ guard(mutex)(&ak4531->reg_mutex);
val = (ak4531->regs[reg] >> shift) & mask;
- mutex_unlock(&ak4531->reg_mutex);
if (invert) {
val = mask - val;
}
@@ -120,11 +101,10 @@ static int snd_ak4531_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
val = mask - val;
}
val <<= shift;
- mutex_lock(&ak4531->reg_mutex);
+ guard(mutex)(&ak4531->reg_mutex);
val = (ak4531->regs[reg] & ~(mask << shift)) | val;
change = val != ak4531->regs[reg];
ak4531->write(ak4531, reg, ak4531->regs[reg] = val);
- mutex_unlock(&ak4531->reg_mutex);
return change;
}
@@ -164,10 +144,9 @@ static int snd_ak4531_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
int invert = (kcontrol->private_value >> 22) & 1;
int left, right;
- mutex_lock(&ak4531->reg_mutex);
+ guard(mutex)(&ak4531->reg_mutex);
left = (ak4531->regs[left_reg] >> left_shift) & mask;
right = (ak4531->regs[right_reg] >> right_shift) & mask;
- mutex_unlock(&ak4531->reg_mutex);
if (invert) {
left = mask - left;
right = mask - right;
@@ -197,7 +176,7 @@ static int snd_ak4531_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
}
left <<= left_shift;
right <<= right_shift;
- mutex_lock(&ak4531->reg_mutex);
+ guard(mutex)(&ak4531->reg_mutex);
if (left_reg == right_reg) {
left = (ak4531->regs[left_reg] & ~((mask << left_shift) | (mask << right_shift))) | left | right;
change = left != ak4531->regs[left_reg];
@@ -209,7 +188,6 @@ static int snd_ak4531_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
ak4531->write(ak4531, left_reg, ak4531->regs[left_reg] = left);
ak4531->write(ak4531, right_reg, ak4531->regs[right_reg] = right);
}
- mutex_unlock(&ak4531->reg_mutex);
return change;
}
@@ -236,12 +214,11 @@ static int snd_ak4531_get_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl
int left_shift = (kcontrol->private_value >> 16) & 0x0f;
int right_shift = (kcontrol->private_value >> 24) & 0x0f;
- mutex_lock(&ak4531->reg_mutex);
+ guard(mutex)(&ak4531->reg_mutex);
ucontrol->value.integer.value[0] = (ak4531->regs[reg1] >> left_shift) & 1;
ucontrol->value.integer.value[1] = (ak4531->regs[reg2] >> left_shift) & 1;
ucontrol->value.integer.value[2] = (ak4531->regs[reg1] >> right_shift) & 1;
ucontrol->value.integer.value[3] = (ak4531->regs[reg2] >> right_shift) & 1;
- mutex_unlock(&ak4531->reg_mutex);
return 0;
}
@@ -255,7 +232,7 @@ static int snd_ak4531_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl
int change;
int val1, val2;
- mutex_lock(&ak4531->reg_mutex);
+ guard(mutex)(&ak4531->reg_mutex);
val1 = ak4531->regs[reg1] & ~((1 << left_shift) | (1 << right_shift));
val2 = ak4531->regs[reg2] & ~((1 << left_shift) | (1 << right_shift));
val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift;
@@ -265,7 +242,6 @@ static int snd_ak4531_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl
change = val1 != ak4531->regs[reg1] || val2 != ak4531->regs[reg2];
ak4531->write(ak4531, reg1, ak4531->regs[reg1] = val1);
ak4531->write(ak4531, reg2, ak4531->regs[reg2] = val2);
- mutex_unlock(&ak4531->reg_mutex);
return change;
}
@@ -273,7 +249,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_master, -6200, 200, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_mono, -2800, 400, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_input, -5000, 200, 0);
-static struct snd_kcontrol_new snd_ak4531_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ak4531_controls[] = {
AK4531_DOUBLE_TLV("Master Playback Switch", 0,
AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1,
@@ -353,7 +329,7 @@ static int snd_ak4531_dev_free(struct snd_device *device)
return snd_ak4531_free(ak4531);
}
-static u8 snd_ak4531_initial_map[0x19 + 1] = {
+static const u8 snd_ak4531_initial_map[0x19 + 1] = {
0x9f, /* 00: Master Volume Lch */
0x9f, /* 01: Master Volume Rch */
0x9f, /* 02: Voice Volume Lch */
@@ -382,14 +358,14 @@ static u8 snd_ak4531_initial_map[0x19 + 1] = {
0x01 /* 19: Mic Amp Setup */
};
-int __devinit snd_ak4531_mixer(struct snd_card *card,
- struct snd_ak4531 *_ak4531,
- struct snd_ak4531 **rak4531)
+int snd_ak4531_mixer(struct snd_card *card,
+ struct snd_ak4531 *_ak4531,
+ struct snd_ak4531 **rak4531)
{
unsigned int idx;
int err;
struct snd_ak4531 *ak4531;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = snd_ak4531_dev_free,
};
@@ -402,11 +378,12 @@ int __devinit snd_ak4531_mixer(struct snd_card *card,
return -ENOMEM;
*ak4531 = *_ak4531;
mutex_init(&ak4531->reg_mutex);
- if ((err = snd_component_add(card, "AK4531")) < 0) {
+ err = snd_component_add(card, "AK4531");
+ if (err < 0) {
snd_ak4531_free(ak4531);
return err;
}
- strcpy(card->mixername, "Asahi Kasei AK4531");
+ strscpy(card->mixername, "Asahi Kasei AK4531");
ak4531->write(ak4531, AK4531_RESET, 0x03); /* no RST, PD */
udelay(100);
ak4531->write(ak4531, AK4531_CLOCK, 0x00); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off LRCLK2 PLL */
@@ -416,13 +393,15 @@ int __devinit snd_ak4531_mixer(struct snd_card *card,
ak4531->write(ak4531, idx, ak4531->regs[idx] = snd_ak4531_initial_map[idx]); /* recording source is mixer */
}
for (idx = 0; idx < ARRAY_SIZE(snd_ak4531_controls); idx++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ak4531_controls[idx], ak4531))) < 0) {
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_ak4531_controls[idx], ak4531));
+ if (err < 0) {
snd_ak4531_free(ak4531);
return err;
}
}
snd_ak4531_proc_init(card, ak4531);
- if ((err = snd_device_new(card, SNDRV_DEV_CODEC, ak4531, &ops)) < 0) {
+ err = snd_device_new(card, SNDRV_DEV_CODEC, ak4531, &ops);
+ if (err < 0) {
snd_ak4531_free(ak4531);
return err;
}
@@ -465,7 +444,6 @@ void snd_ak4531_resume(struct snd_ak4531 *ak4531)
}
#endif
-#ifdef CONFIG_PROC_FS
/*
* /proc interface
*/
@@ -482,12 +460,8 @@ static void snd_ak4531_proc_read(struct snd_info_entry *entry,
ak4531->regs[AK4531_MIC_GAIN] & 1 ? "+30dB" : "+0dB");
}
-static void __devinit
+static void
snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(card, "ak4531", &entry))
- snd_info_set_text_ops(entry, ak4531, snd_ak4531_proc_read);
+ snd_card_ro_proc_new(card, "ak4531", ak4531, snd_ak4531_proc_read);
}
-#endif
diff --git a/sound/pci/ali5451/Makefile b/sound/pci/ali5451/Makefile
index 713459c12d22..e319a4c1d6b2 100644
--- a/sound/pci/ali5451/Makefile
+++ b/sound/pci/ali5451/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ali5451-objs := ali5451.o
+snd-ali5451-y := ali5451.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ALI5451) += snd-ali5451.o
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index 5c6e322a48f0..571d89a6a8da 100644
--- a/sound/pci/ali5451/ali5451.c
+++ b/sound/pci/ali5451/ali5451.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Matt Wu <Matt_Wu@acersoftech.com.cn>
* Apr 26, 2001
@@ -8,30 +9,15 @@
*
* TODO:
* --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public Lcodecnse as published by
- * the Free Software Foundation; either version 2 of the Lcodecnse, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public Lcodecnse for more details.
- *
- * You should have received a copy of the GNU General Public Lcodecnse
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -43,12 +29,11 @@
MODULE_AUTHOR("Matt Wu <Matt_Wu@acersoftech.com.cn>");
MODULE_DESCRIPTION("ALI M5451");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ALI,M5451,pci},{ALI,M5451}}");
static int index = SNDRV_DEFAULT_IDX1; /* Index */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
static int pcm_channels = 32;
-static int spdif;
+static bool spdif;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for ALI M5451 PCI Audio.");
@@ -60,23 +45,11 @@ module_param(spdif, bool, 0444);
MODULE_PARM_DESC(spdif, "Support SPDIF I/O");
/* just for backward compatibility */
-static int enable;
+static bool enable;
module_param(enable, bool, 0444);
/*
- * Debug part definitions
- */
-
-/* #define ALI_DEBUG */
-
-#ifdef ALI_DEBUG
-#define snd_ali_printk(format, args...) printk(KERN_DEBUG format, ##args);
-#else
-#define snd_ali_printk(format, args...)
-#endif
-
-/*
* Constants definition
*/
@@ -270,12 +243,10 @@ struct snd_ali {
spinlock_t reg_lock;
spinlock_t voice_alloc;
-#ifdef CONFIG_PM
- struct snd_ali_image *image;
-#endif
+ struct snd_ali_image image;
};
-static DEFINE_PCI_DEVICE_TABLE(snd_ali_ids) = {
+static const struct pci_device_id snd_ali_ids[] = {
{PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5451), 0, 0, 0},
{0, }
};
@@ -321,7 +292,7 @@ static int snd_ali_codec_ready(struct snd_ali *codec,
}
snd_ali_5451_poke(codec, port, res & ~0x8000);
- snd_printdd("ali_codec_ready: codec is not ready.\n ");
+ dev_dbg(codec->card->dev, "ali_codec_ready: codec is not ready.\n");
return -EIO;
}
@@ -342,7 +313,7 @@ static int snd_ali_stimer_ready(struct snd_ali *codec)
schedule_timeout_uninterruptible(1);
}
- snd_printk(KERN_ERR "ali_stimer_read: stimer is not ready.\n");
+ dev_err(codec->card->dev, "ali_stimer_read: stimer is not ready.\n");
return -EIO;
}
@@ -354,7 +325,8 @@ static void snd_ali_codec_poke(struct snd_ali *codec,int secondary,
unsigned int port;
if (reg >= 0x80) {
- snd_printk(KERN_ERR "ali_codec_poke: reg(%xh) invalid.\n", reg);
+ dev_err(codec->card->dev,
+ "ali_codec_poke: reg(%xh) invalid.\n", reg);
return;
}
@@ -385,7 +357,8 @@ static unsigned short snd_ali_codec_peek(struct snd_ali *codec,
unsigned int port;
if (reg >= 0x80) {
- snd_printk(KERN_ERR "ali_codec_peek: reg(%xh) invalid.\n", reg);
+ dev_err(codec->card->dev,
+ "ali_codec_peek: reg(%xh) invalid.\n", reg);
return ~0;
}
@@ -417,7 +390,7 @@ static void snd_ali_codec_write(struct snd_ac97 *ac97,
{
struct snd_ali *codec = ac97->private_data;
- snd_ali_printk("codec_write: reg=%xh data=%xh.\n", reg, val);
+ dev_dbg(codec->card->dev, "codec_write: reg=%xh data=%xh.\n", reg, val);
if (reg == AC97_GPIO_STATUS) {
outl((val << ALI_AC97_GPIO_DATA_SHIFT) | ALI_AC97_GPIO_ENABLE,
ALI_REG(codec, ALI_AC97_GPIO));
@@ -433,7 +406,7 @@ static unsigned short snd_ali_codec_read(struct snd_ac97 *ac97,
{
struct snd_ali *codec = ac97->private_data;
- snd_ali_printk("codec_read reg=%xh.\n", reg);
+ dev_dbg(codec->card->dev, "codec_read reg=%xh.\n", reg);
return snd_ali_codec_peek(codec, ac97->num, reg);
}
@@ -451,10 +424,10 @@ static int snd_ali_reset_5451(struct snd_ali *codec)
if (pci_dev) {
pci_read_config_dword(pci_dev, 0x7c, &dwVal);
pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000);
- udelay(5000);
+ mdelay(5);
pci_read_config_dword(pci_dev, 0x7c, &dwVal);
pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff);
- udelay(5000);
+ mdelay(5);
}
pci_dev = codec->pci;
@@ -463,18 +436,18 @@ static int snd_ali_reset_5451(struct snd_ali *codec)
udelay(500);
pci_read_config_dword(pci_dev, 0x44, &dwVal);
pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff);
- udelay(5000);
+ mdelay(5);
wCount = 200;
while(wCount--) {
wReg = snd_ali_codec_peek(codec, 0, AC97_POWERDOWN);
if ((wReg & 0x000f) == 0x000f)
return 0;
- udelay(5000);
+ mdelay(5);
}
/* non-fatal if you have a non PM capable codec */
- /* snd_printk(KERN_WARNING "ali5451: reset time out\n"); */
+ /* dev_warn(codec->card->dev, "ali5451: reset time out\n"); */
return 0;
}
@@ -528,7 +501,7 @@ static void snd_ali_disable_voice_irq(struct snd_ali *codec,
unsigned int mask;
struct snd_ali_channel_control *pchregs = &(codec->chregs);
- snd_ali_printk("disable_voice_irq channel=%d\n",channel);
+ dev_dbg(codec->card->dev, "disable_voice_irq channel=%d\n", channel);
mask = 1 << (channel & 0x1f);
pchregs->data.ainten = inl(ALI_REG(codec, pchregs->regs.ainten));
@@ -541,7 +514,7 @@ static int snd_ali_alloc_pcm_channel(struct snd_ali *codec, int channel)
unsigned int idx = channel & 0x1f;
if (codec->synth.chcnt >= ALI_CHANNELS){
- snd_printk(KERN_ERR
+ dev_err(codec->card->dev,
"ali_alloc_pcm_channel: no free channels.\n");
return -1;
}
@@ -549,7 +522,7 @@ static int snd_ali_alloc_pcm_channel(struct snd_ali *codec, int channel)
if (!(codec->synth.chmap & (1 << idx))) {
codec->synth.chmap |= 1 << idx;
codec->synth.chcnt++;
- snd_ali_printk("alloc_pcm_channel no. %d.\n",idx);
+ dev_dbg(codec->card->dev, "alloc_pcm_channel no. %d.\n", idx);
return idx;
}
return -1;
@@ -560,7 +533,8 @@ static int snd_ali_find_free_channel(struct snd_ali * codec, int rec)
int idx;
int result = -1;
- snd_ali_printk("find_free_channel: for %s\n",rec ? "rec" : "pcm");
+ dev_dbg(codec->card->dev,
+ "find_free_channel: for %s\n", rec ? "rec" : "pcm");
/* recording */
if (rec) {
@@ -575,8 +549,8 @@ static int snd_ali_find_free_channel(struct snd_ali * codec, int rec)
if (result >= 0)
return result;
else {
- snd_printk(KERN_ERR "ali_find_free_channel: "
- "record channel is busy now.\n");
+ dev_err(codec->card->dev,
+ "ali_find_free_channel: record channel is busy now.\n");
return -1;
}
}
@@ -590,8 +564,8 @@ static int snd_ali_find_free_channel(struct snd_ali * codec, int rec)
if (result >= 0)
return result;
else
- snd_printk(KERN_ERR "ali_find_free_channel: "
- "S/PDIF out channel is in busy now.\n");
+ dev_err(codec->card->dev,
+ "ali_find_free_channel: S/PDIF out channel is in busy now.\n");
}
for (idx = 0; idx < ALI_CHANNELS; idx++) {
@@ -599,7 +573,7 @@ static int snd_ali_find_free_channel(struct snd_ali * codec, int rec)
if (result >= 0)
return result;
}
- snd_printk(KERN_ERR "ali_find_free_channel: no free channels.\n");
+ dev_err(codec->card->dev, "ali_find_free_channel: no free channels.\n");
return -1;
}
@@ -607,14 +581,15 @@ static void snd_ali_free_channel_pcm(struct snd_ali *codec, int channel)
{
unsigned int idx = channel & 0x0000001f;
- snd_ali_printk("free_channel_pcm channel=%d\n",channel);
+ dev_dbg(codec->card->dev, "free_channel_pcm channel=%d\n", channel);
if (channel < 0 || channel >= ALI_CHANNELS)
return;
if (!(codec->synth.chmap & (1 << idx))) {
- snd_printk(KERN_ERR "ali_free_channel_pcm: "
- "channel %d is not in use.\n", channel);
+ dev_err(codec->card->dev,
+ "ali_free_channel_pcm: channel %d is not in use.\n",
+ channel);
return;
} else {
codec->synth.chmap &= ~(1 << idx);
@@ -626,7 +601,7 @@ static void snd_ali_stop_voice(struct snd_ali *codec, unsigned int channel)
{
unsigned int mask = 1 << (channel & 0x1f);
- snd_ali_printk("stop_voice: channel=%d\n",channel);
+ dev_dbg(codec->card->dev, "stop_voice: channel=%d\n", channel);
outl(mask, ALI_REG(codec, codec->chregs.regs.stop));
}
@@ -667,7 +642,7 @@ static void snd_ali_detect_spdif_rate(struct snd_ali *codec)
}
if (count > 50000) {
- snd_printk(KERN_ERR "ali_detect_spdif_rate: timeout!\n");
+ dev_err(codec->card->dev, "ali_detect_spdif_rate: timeout!\n");
return;
}
@@ -682,7 +657,7 @@ static void snd_ali_detect_spdif_rate(struct snd_ali *codec)
}
if (count > 50000) {
- snd_printk(KERN_ERR "ali_detect_spdif_rate: timeout!\n");
+ dev_err(codec->card->dev, "ali_detect_spdif_rate: timeout!\n");
return;
}
@@ -855,12 +830,8 @@ static void snd_ali_disable_spdif_out(struct snd_ali *codec)
static void snd_ali_update_ptr(struct snd_ali *codec, int channel)
{
struct snd_ali_voice *pvoice;
- struct snd_pcm_runtime *runtime;
struct snd_ali_channel_control *pchregs;
unsigned int old, mask;
-#ifdef ALI_DEBUG
- unsigned int temp, cspf;
-#endif
pchregs = &(codec->chregs);
@@ -872,21 +843,17 @@ static void snd_ali_update_ptr(struct snd_ali *codec, int channel)
return;
pvoice = &codec->synth.voices[channel];
- runtime = pvoice->substream->runtime;
udelay(100);
spin_lock(&codec->reg_lock);
if (pvoice->pcm && pvoice->substream) {
/* pcm interrupt */
-#ifdef ALI_DEBUG
- outb((u8)(pvoice->number), ALI_REG(codec, ALI_GC_CIR));
- temp = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
- cspf = (inl(ALI_REG(codec, ALI_CSPF)) & mask) == mask;
-#endif
if (pvoice->running) {
- snd_ali_printk("update_ptr: cso=%4.4x cspf=%d.\n",
- (u16)temp, cspf);
+ dev_dbg(codec->card->dev,
+ "update_ptr: cso=%4.4x cspf=%d.\n",
+ inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)),
+ (inl(ALI_REG(codec, ALI_CSPF)) & mask) == mask);
spin_unlock(&codec->reg_lock);
snd_pcm_period_elapsed(pvoice->substream);
spin_lock(&codec->reg_lock);
@@ -942,15 +909,14 @@ static struct snd_ali_voice *snd_ali_alloc_voice(struct snd_ali * codec,
struct snd_ali_voice *pvoice;
int idx;
- snd_ali_printk("alloc_voice: type=%d rec=%d\n", type, rec);
+ dev_dbg(codec->card->dev, "alloc_voice: type=%d rec=%d\n", type, rec);
- spin_lock_irq(&codec->voice_alloc);
+ guard(spinlock_irq)(&codec->voice_alloc);
if (type == SNDRV_ALI_VOICE_TYPE_PCM) {
idx = channel > 0 ? snd_ali_alloc_pcm_channel(codec, channel) :
snd_ali_find_free_channel(codec,rec);
if (idx < 0) {
- snd_printk(KERN_ERR "ali_alloc_voice: err.\n");
- spin_unlock_irq(&codec->voice_alloc);
+ dev_err(codec->card->dev, "ali_alloc_voice: err.\n");
return NULL;
}
pvoice = &(codec->synth.voices[idx]);
@@ -958,10 +924,8 @@ static struct snd_ali_voice *snd_ali_alloc_voice(struct snd_ali * codec,
pvoice->use = 1;
pvoice->pcm = 1;
pvoice->mode = rec;
- spin_unlock_irq(&codec->voice_alloc);
return pvoice;
}
- spin_unlock_irq(&codec->voice_alloc);
return NULL;
}
@@ -972,20 +936,20 @@ static void snd_ali_free_voice(struct snd_ali * codec,
void (*private_free)(void *);
void *private_data;
- snd_ali_printk("free_voice: channel=%d\n",pvoice->number);
+ dev_dbg(codec->card->dev, "free_voice: channel=%d\n", pvoice->number);
if (!pvoice->use)
return;
snd_ali_clear_voices(codec, pvoice->number, pvoice->number);
- spin_lock_irq(&codec->voice_alloc);
- private_free = pvoice->private_free;
- private_data = pvoice->private_data;
- pvoice->private_free = NULL;
- pvoice->private_data = NULL;
- if (pvoice->pcm)
- snd_ali_free_channel_pcm(codec, pvoice->number);
- pvoice->use = pvoice->pcm = pvoice->synth = 0;
- pvoice->substream = NULL;
- spin_unlock_irq(&codec->voice_alloc);
+ scoped_guard(spinlock_irq, &codec->voice_alloc) {
+ private_free = pvoice->private_free;
+ private_data = pvoice->private_data;
+ pvoice->private_free = NULL;
+ pvoice->private_data = NULL;
+ if (pvoice->pcm)
+ snd_ali_free_channel_pcm(codec, pvoice->number);
+ pvoice->use = pvoice->pcm = pvoice->synth = 0;
+ pvoice->substream = NULL;
+ }
if (private_free)
private_free(private_data);
}
@@ -1100,7 +1064,7 @@ static int snd_ali_trigger(struct snd_pcm_substream *substream,
{
struct snd_ali *codec = snd_pcm_substream_chip(substream);
struct snd_pcm_substream *s;
- unsigned int what, whati, capture_flag;
+ unsigned int what, whati;
struct snd_ali_voice *pvoice, *evoice;
unsigned int val;
int do_start;
@@ -1118,7 +1082,7 @@ static int snd_ali_trigger(struct snd_pcm_substream *substream,
return -EINVAL;
}
- what = whati = capture_flag = 0;
+ what = whati = 0;
snd_pcm_group_for_each_entry(s, substream) {
if ((struct snd_ali *) snd_pcm_substream_chip(s) == codec) {
pvoice = s->runtime->private_data;
@@ -1140,11 +1104,9 @@ static int snd_ali_trigger(struct snd_pcm_substream *substream,
evoice->running = 0;
}
snd_pcm_trigger_done(s, substream);
- if (pvoice->mode)
- capture_flag = 1;
}
}
- spin_lock(&codec->reg_lock);
+ guard(spinlock)(&codec->reg_lock);
if (!do_start)
outl(what, ALI_REG(codec, ALI_STOP));
val = inl(ALI_REG(codec, ALI_AINTEN));
@@ -1155,8 +1117,7 @@ static int snd_ali_trigger(struct snd_pcm_substream *substream,
outl(val, ALI_REG(codec, ALI_AINTEN));
if (do_start)
outl(what, ALI_REG(codec, ALI_START));
- snd_ali_printk("trigger: what=%xh whati=%xh\n", what, whati);
- spin_unlock(&codec->reg_lock);
+ dev_dbg(codec->card->dev, "trigger: what=%xh whati=%xh\n", what, whati);
return 0;
}
@@ -1168,13 +1129,7 @@ static int snd_ali_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ali_voice *pvoice = runtime->private_data;
struct snd_ali_voice *evoice = pvoice->extra;
- int err;
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
-
/* voice management */
if (params_buffer_size(hw_params) / 2 !=
@@ -1205,7 +1160,6 @@ static int snd_ali_playback_hw_free(struct snd_pcm_substream *substream)
struct snd_ali_voice *pvoice = runtime->private_data;
struct snd_ali_voice *evoice = pvoice ? pvoice->extra : NULL;
- snd_pcm_lib_free_pages(substream);
if (evoice) {
snd_ali_free_voice(codec, evoice);
pvoice->extra = NULL;
@@ -1213,18 +1167,6 @@ static int snd_ali_playback_hw_free(struct snd_pcm_substream *substream)
return 0;
}
-static int snd_ali_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-static int snd_ali_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_ali_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_ali *codec = snd_pcm_substream_chip(substream);
@@ -1241,10 +1183,10 @@ static int snd_ali_playback_prepare(struct snd_pcm_substream *substream)
unsigned int VOL;
unsigned int EC;
- snd_ali_printk("playback_prepare ...\n");
+ dev_dbg(codec->card->dev, "playback_prepare ...\n");
+
+ guard(spinlock_irq)(&codec->reg_lock);
- spin_lock_irq(&codec->reg_lock);
-
/* set Delta (rate) value */
Delta = snd_ali_convert_rate(runtime->rate, 0);
@@ -1268,7 +1210,7 @@ static int snd_ali_playback_prepare(struct snd_pcm_substream *substream)
/* set target ESO for channel */
pvoice->eso = runtime->buffer_size;
- snd_ali_printk("playback_prepare: eso=%xh count=%xh\n",
+ dev_dbg(codec->card->dev, "playback_prepare: eso=%xh count=%xh\n",
pvoice->eso, pvoice->count);
/* set ESO to capture first MIDLP interrupt */
@@ -1280,8 +1222,9 @@ static int snd_ali_playback_prepare(struct snd_pcm_substream *substream)
PAN = 0;
VOL = 0;
EC = 0;
- snd_ali_printk("playback_prepare:\n");
- snd_ali_printk("ch=%d, Rate=%d Delta=%xh,GVSEL=%xh,PAN=%xh,CTRL=%xh\n",
+ dev_dbg(codec->card->dev, "playback_prepare:\n");
+ dev_dbg(codec->card->dev,
+ "ch=%d, Rate=%d Delta=%xh,GVSEL=%xh,PAN=%xh,CTRL=%xh\n",
pvoice->number,runtime->rate,Delta,GVSEL,PAN,CTRL);
snd_ali_write_voice_regs(codec,
pvoice->number,
@@ -1312,7 +1255,6 @@ static int snd_ali_playback_prepare(struct snd_pcm_substream *substream)
CTRL,
EC);
}
- spin_unlock_irq(&codec->reg_lock);
return 0;
}
@@ -1334,7 +1276,7 @@ static int snd_ali_prepare(struct snd_pcm_substream *substream)
spin_lock_irq(&codec->reg_lock);
- snd_ali_printk("ali_prepare...\n");
+ dev_dbg(codec->card->dev, "ali_prepare...\n");
snd_ali_enable_special_channel(codec,pvoice->number);
@@ -1353,15 +1295,16 @@ static int snd_ali_prepare(struct snd_pcm_substream *substream)
rate = snd_ali_get_spdif_in_rate(codec);
if (rate == 0) {
- snd_printk(KERN_WARNING "ali_capture_preapre: "
- "spdif rate detect err!\n");
+ dev_warn(codec->card->dev,
+ "ali_capture_prepare: spdif rate detect err!\n");
rate = 48000;
}
spin_lock_irq(&codec->reg_lock);
bValue = inb(ALI_REG(codec,ALI_SPDIF_CTRL));
if (bValue & 0x10) {
outb(bValue,ALI_REG(codec,ALI_SPDIF_CTRL));
- printk(KERN_WARNING "clear SPDIF parity error flag.\n");
+ dev_warn(codec->card->dev,
+ "clear SPDIF parity error flag.\n");
}
if (rate != 48000)
@@ -1412,16 +1355,14 @@ snd_ali_playback_pointer(struct snd_pcm_substream *substream)
struct snd_ali_voice *pvoice = runtime->private_data;
unsigned int cso;
- spin_lock(&codec->reg_lock);
- if (!pvoice->running) {
- spin_unlock(&codec->reg_lock);
+ guard(spinlock)(&codec->reg_lock);
+ if (!pvoice->running)
return 0;
- }
outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR));
cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
- spin_unlock(&codec->reg_lock);
- snd_ali_printk("playback pointer returned cso=%xh.\n", cso);
+ dev_dbg(codec->card->dev, "playback pointer returned cso=%xh.\n", cso);
+ cso %= runtime->buffer_size;
return cso;
}
@@ -1433,19 +1374,17 @@ static snd_pcm_uframes_t snd_ali_pointer(struct snd_pcm_substream *substream)
struct snd_ali_voice *pvoice = runtime->private_data;
unsigned int cso;
- spin_lock(&codec->reg_lock);
- if (!pvoice->running) {
- spin_unlock_irq(&codec->reg_lock);
+ guard(spinlock)(&codec->reg_lock);
+ if (!pvoice->running)
return 0;
- }
outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR));
cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
- spin_unlock(&codec->reg_lock);
+ cso %= runtime->buffer_size;
return cso;
}
-static struct snd_pcm_hardware snd_ali_playback =
+static const struct snd_pcm_hardware snd_ali_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1471,7 +1410,7 @@ static struct snd_pcm_hardware snd_ali_playback =
* Capture support device description
*/
-static struct snd_pcm_hardware snd_ali_capture =
+static const struct snd_pcm_hardware snd_ali_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1496,16 +1435,13 @@ static struct snd_pcm_hardware snd_ali_capture =
static void snd_ali_pcm_free_substream(struct snd_pcm_runtime *runtime)
{
struct snd_ali_voice *pvoice = runtime->private_data;
- struct snd_ali *codec;
- if (pvoice) {
- codec = pvoice->codec;
+ if (pvoice)
snd_ali_free_voice(pvoice->codec, pvoice);
- }
}
static int snd_ali_open(struct snd_pcm_substream *substream, int rec,
- int channel, struct snd_pcm_hardware *phw)
+ int channel, const struct snd_pcm_hardware *phw)
{
struct snd_ali *codec = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -1552,10 +1488,9 @@ static int snd_ali_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_ali_playback_ops = {
+static const struct snd_pcm_ops snd_ali_playback_ops = {
.open = snd_ali_playback_open,
.close = snd_ali_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_ali_playback_hw_params,
.hw_free = snd_ali_playback_hw_free,
.prepare = snd_ali_playback_prepare,
@@ -1563,12 +1498,9 @@ static struct snd_pcm_ops snd_ali_playback_ops = {
.pointer = snd_ali_playback_pointer,
};
-static struct snd_pcm_ops snd_ali_capture_ops = {
+static const struct snd_pcm_ops snd_ali_capture_ops = {
.open = snd_ali_capture_open,
.close = snd_ali_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ali_hw_params,
- .hw_free = snd_ali_hw_free,
.prepare = snd_ali_prepare,
.trigger = snd_ali_trigger,
.pointer = snd_ali_pointer,
@@ -1586,10 +1518,10 @@ static int snd_ali_modem_hw_params(struct snd_pcm_substream *substream,
snd_ac97_write(chip->ac97[modem_num], AC97_LINE1_RATE,
params_rate(hw_params));
snd_ac97_write(chip->ac97[modem_num], AC97_LINE1_LEVEL, 0);
- return snd_ali_hw_params(substream, hw_params);
+ return 0;
}
-static struct snd_pcm_hardware snd_ali_modem =
+static const struct snd_pcm_hardware snd_ali_modem =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1614,8 +1546,8 @@ static struct snd_pcm_hardware snd_ali_modem =
static int snd_ali_modem_open(struct snd_pcm_substream *substream, int rec,
int channel)
{
- static unsigned int rates[] = {8000, 9600, 12000, 16000};
- static struct snd_pcm_hw_constraint_list hw_constraint_rates = {
+ static const unsigned int rates[] = {8000, 9600, 12000, 16000};
+ static const struct snd_pcm_hw_constraint_list hw_constraint_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
@@ -1638,23 +1570,19 @@ static int snd_ali_modem_capture_open(struct snd_pcm_substream *substream)
return snd_ali_modem_open(substream, 1, ALI_MODEM_IN_CHANNEL);
}
-static struct snd_pcm_ops snd_ali_modem_playback_ops = {
+static const struct snd_pcm_ops snd_ali_modem_playback_ops = {
.open = snd_ali_modem_playback_open,
.close = snd_ali_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_ali_modem_hw_params,
- .hw_free = snd_ali_hw_free,
.prepare = snd_ali_prepare,
.trigger = snd_ali_trigger,
.pointer = snd_ali_pointer,
};
-static struct snd_pcm_ops snd_ali_modem_capture_ops = {
+static const struct snd_pcm_ops snd_ali_modem_capture_ops = {
.open = snd_ali_modem_capture_open,
.close = snd_ali_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_ali_modem_hw_params,
- .hw_free = snd_ali_hw_free,
.prepare = snd_ali_prepare,
.trigger = snd_ali_trigger,
.pointer = snd_ali_pointer,
@@ -1665,8 +1593,8 @@ struct ali_pcm_description {
char *name;
unsigned int playback_num;
unsigned int capture_num;
- struct snd_pcm_ops *playback_ops;
- struct snd_pcm_ops *capture_ops;
+ const struct snd_pcm_ops *playback_ops;
+ const struct snd_pcm_ops *capture_ops;
unsigned short class;
};
@@ -1678,8 +1606,8 @@ static void snd_ali_pcm_free(struct snd_pcm *pcm)
}
-static int __devinit snd_ali_pcm(struct snd_ali * codec, int device,
- struct ali_pcm_description *desc)
+static int snd_ali_pcm(struct snd_ali *codec, int device,
+ struct ali_pcm_description *desc)
{
struct snd_pcm *pcm;
int err;
@@ -1687,7 +1615,8 @@ static int __devinit snd_ali_pcm(struct snd_ali * codec, int device,
err = snd_pcm_new(codec->card, desc->name, device,
desc->playback_num, desc->capture_num, &pcm);
if (err < 0) {
- snd_printk(KERN_ERR "snd_ali_pcm: err called snd_pcm_new.\n");
+ dev_err(codec->card->dev,
+ "snd_ali_pcm: err called snd_pcm_new.\n");
return err;
}
pcm->private_data = codec;
@@ -1699,14 +1628,13 @@ static int __devinit snd_ali_pcm(struct snd_ali * codec, int device,
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
desc->capture_ops);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(codec->pci),
- 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &codec->pci->dev, 64*1024, 128*1024);
pcm->info_flags = 0;
pcm->dev_class = desc->class;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
- strcpy(pcm->name, desc->name);
+ strscpy(pcm->name, desc->name);
codec->pcm[0] = pcm;
return 0;
}
@@ -1727,7 +1655,7 @@ static struct ali_pcm_description ali_pcms[] = {
}
};
-static int __devinit snd_ali_build_pcms(struct snd_ali *codec)
+static int snd_ali_build_pcms(struct snd_ali *codec)
{
int i, err;
for (i = 0; i < codec->num_of_codecs && i < ARRAY_SIZE(ali_pcms); i++) {
@@ -1749,12 +1677,12 @@ static int __devinit snd_ali_build_pcms(struct snd_ali *codec)
static int snd_ali5451_spdif_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_ali *codec = kcontrol->private_data;
+ struct snd_ali *codec = snd_kcontrol_chip(kcontrol);
unsigned int spdif_enable;
spdif_enable = ucontrol->value.integer.value[0] ? 1 : 0;
- spin_lock_irq(&codec->reg_lock);
+ guard(spinlock_irq)(&codec->reg_lock);
switch (kcontrol->private_value) {
case 0:
spdif_enable = (codec->spdif_mask & 0x02) ? 1 : 0;
@@ -1770,19 +1698,18 @@ static int snd_ali5451_spdif_get(struct snd_kcontrol *kcontrol,
break;
}
ucontrol->value.integer.value[0] = spdif_enable;
- spin_unlock_irq(&codec->reg_lock);
return 0;
}
static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_ali *codec = kcontrol->private_data;
+ struct snd_ali *codec = snd_kcontrol_chip(kcontrol);
unsigned int change = 0, spdif_enable = 0;
spdif_enable = ucontrol->value.integer.value[0] ? 1 : 0;
- spin_lock_irq(&codec->reg_lock);
+ guard(spinlock_irq)(&codec->reg_lock);
switch (kcontrol->private_value) {
case 0:
change = (codec->spdif_mask & 0x02) ? 1 : 0;
@@ -1827,12 +1754,11 @@ static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol,
default:
break;
}
- spin_unlock_irq(&codec->reg_lock);
return change;
}
-static struct snd_kcontrol_new snd_ali5451_mixer_spdif[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ali5451_mixer_spdif[] = {
/* spdif aplayback switch */
/* FIXME: "IEC958 Playback Switch" may conflict with one on ac97_codec */
ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH), 0, 0),
@@ -1842,12 +1768,12 @@ static struct snd_kcontrol_new snd_ali5451_mixer_spdif[] __devinitdata = {
ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, 2)
};
-static int __devinit snd_ali_mixer(struct snd_ali * codec)
+static int snd_ali_mixer(struct snd_ali *codec)
{
struct snd_ac97_template ac97;
unsigned int idx;
int i, err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_ali_codec_write,
.read = snd_ali_codec_read,
};
@@ -1863,7 +1789,7 @@ static int __devinit snd_ali_mixer(struct snd_ali * codec)
ac97.num = i;
err = snd_ac97_mixer(codec->ac97_bus, &ac97, &codec->ac97[i]);
if (err < 0) {
- snd_printk(KERN_ERR
+ dev_err(codec->card->dev,
"ali mixer %d creating error.\n", i);
if (i == 0)
return err;
@@ -1883,25 +1809,18 @@ static int __devinit snd_ali_mixer(struct snd_ali * codec)
return 0;
}
-#ifdef CONFIG_PM
-static int ali_suspend(struct pci_dev *pci, pm_message_t state)
+static int ali_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_ali *chip = card->private_data;
- struct snd_ali_image *im;
+ struct snd_ali_image *im = &chip->image;
int i, j;
- im = chip->image;
- if (!im)
- return 0;
-
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- for (i = 0; i < chip->num_of_codecs; i++) {
- snd_pcm_suspend_all(chip->pcm[i]);
+ for (i = 0; i < chip->num_of_codecs; i++)
snd_ac97_suspend(chip->ac97[i]);
- }
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
im->regs[ALI_MISCINT >> 2] = inl(ALI_REG(chip, ALI_MISCINT));
/* im->regs[ALI_START >> 2] = inl(ALI_REG(chip, ALI_START)); */
@@ -1925,56 +1844,35 @@ static int ali_suspend(struct pci_dev *pci, pm_message_t state)
/* stop all HW channel */
outl(0xffffffff, ALI_REG(chip, ALI_STOP));
- spin_unlock_irq(&chip->reg_lock);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int ali_resume(struct pci_dev *pci)
+static int ali_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_ali *chip = card->private_data;
- struct snd_ali_image *im;
+ struct snd_ali_image *im = &chip->image;
int i, j;
- im = chip->image;
- if (!im)
- return 0;
-
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "ali5451: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
- spin_lock_irq(&chip->reg_lock);
-
- for (i = 0; i < ALI_CHANNELS; i++) {
- outb(i, ALI_REG(chip, ALI_GC_CIR));
- for (j = 0; j < ALI_CHANNEL_REGS; j++)
- outl(im->channel_regs[i][j], ALI_REG(chip, j*4 + 0xe0));
- }
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ for (i = 0; i < ALI_CHANNELS; i++) {
+ outb(i, ALI_REG(chip, ALI_GC_CIR));
+ for (j = 0; j < ALI_CHANNEL_REGS; j++)
+ outl(im->channel_regs[i][j], ALI_REG(chip, j*4 + 0xe0));
+ }
- for (i = 0; i < ALI_GLOBAL_REGS; i++) {
- if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP) ||
- (i*4 == ALI_START))
- continue;
- outl(im->regs[i], ALI_REG(chip, i*4));
+ for (i = 0; i < ALI_GLOBAL_REGS; i++) {
+ if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP) ||
+ (i*4 == ALI_START))
+ continue;
+ outl(im->regs[i], ALI_REG(chip, i*4));
+ }
+
+ /* start HW channel */
+ outl(im->regs[ALI_START >> 2], ALI_REG(chip, ALI_START));
+ /* restore IRQ enable bits */
+ outl(im->regs[ALI_MISCINT >> 2], ALI_REG(chip, ALI_MISCINT));
}
-
- /* start HW channel */
- outl(im->regs[ALI_START >> 2], ALI_REG(chip, ALI_START));
- /* restore IRQ enable bits */
- outl(im->regs[ALI_MISCINT >> 2], ALI_REG(chip, ALI_MISCINT));
-
- spin_unlock_irq(&chip->reg_lock);
for (i = 0 ; i < chip->num_of_codecs; i++)
snd_ac97_resume(chip->ac97[i]);
@@ -1982,24 +1880,17 @@ static int ali_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
-static int snd_ali_free(struct snd_ali * codec)
+static DEFINE_SIMPLE_DEV_PM_OPS(ali_pm, ali_suspend, ali_resume);
+
+static void snd_ali_free(struct snd_card *card)
{
+ struct snd_ali *codec = card->private_data;
+
if (codec->hw_initialized)
snd_ali_disable_address_interrupt(codec);
- if (codec->irq >= 0)
- free_irq(codec->irq, codec);
- if (codec->port)
- pci_release_regions(codec->pci);
- pci_disable_device(codec->pci);
-#ifdef CONFIG_PM
- kfree(codec->image);
-#endif
pci_dev_put(codec->pci_m1533);
pci_dev_put(codec->pci_m7101);
- kfree(codec);
- return 0;
}
static int snd_ali_chip_init(struct snd_ali *codec)
@@ -2008,10 +1899,10 @@ static int snd_ali_chip_init(struct snd_ali *codec)
unsigned char temp;
struct pci_dev *pci_dev;
- snd_ali_printk("chip initializing ... \n");
+ dev_dbg(codec->card->dev, "chip initializing ...\n");
if (snd_ali_reset_5451(codec)) {
- snd_printk(KERN_ERR "ali_chip_init: reset 5451 error.\n");
+ dev_err(codec->card->dev, "ali_chip_init: reset 5451 error.\n");
return -1;
}
@@ -2057,7 +1948,7 @@ static int snd_ali_chip_init(struct snd_ali *codec)
ALI_REG(codec, ALI_SCTRL));
}
- snd_ali_printk("chip initialize succeed.\n");
+ dev_dbg(codec->card->dev, "chip initialize succeed.\n");
return 0;
}
@@ -2072,75 +1963,55 @@ static void snd_ali_proc_read(struct snd_info_entry *entry,
snd_iprintf(buf, "%02x: %08x\n", i, inl(ALI_REG(codec, i)));
}
-static void __devinit snd_ali_proc_init(struct snd_ali *codec)
+static void snd_ali_proc_init(struct snd_ali *codec)
{
- struct snd_info_entry *entry;
- if (!snd_card_proc_new(codec->card, "ali5451", &entry))
- snd_info_set_text_ops(entry, codec, snd_ali_proc_read);
+ snd_card_ro_proc_new(codec->card, "ali5451", codec, snd_ali_proc_read);
}
-static int __devinit snd_ali_resources(struct snd_ali *codec)
+static int snd_ali_resources(struct snd_ali *codec)
{
int err;
- snd_ali_printk("resources allocation ...\n");
- err = pci_request_regions(codec->pci, "ALI 5451");
+ dev_dbg(codec->card->dev, "resources allocation ...\n");
+ err = pcim_request_all_regions(codec->pci, "ALI 5451");
if (err < 0)
return err;
codec->port = pci_resource_start(codec->pci, 0);
- if (request_irq(codec->pci->irq, snd_ali_card_interrupt,
- IRQF_SHARED, "ALI 5451", codec)) {
- snd_printk(KERN_ERR "Unable to request irq.\n");
+ if (devm_request_irq(&codec->pci->dev, codec->pci->irq,
+ snd_ali_card_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, codec)) {
+ dev_err(codec->card->dev, "Unable to request irq.\n");
return -EBUSY;
}
codec->irq = codec->pci->irq;
- snd_ali_printk("resources allocated.\n");
- return 0;
-}
-static int snd_ali_dev_free(struct snd_device *device)
-{
- struct snd_ali *codec = device->device_data;
- snd_ali_free(codec);
+ codec->card->sync_irq = codec->irq;
+ dev_dbg(codec->card->dev, "resources allocated.\n");
return 0;
}
-static int __devinit snd_ali_create(struct snd_card *card,
- struct pci_dev *pci,
- int pcm_streams,
- int spdif_support,
- struct snd_ali ** r_ali)
+static int snd_ali_create(struct snd_card *card,
+ struct pci_dev *pci,
+ int pcm_streams,
+ int spdif_support)
{
- struct snd_ali *codec;
+ struct snd_ali *codec = card->private_data;
int i, err;
unsigned short cmdw;
- static struct snd_device_ops ops = {
- .dev_free = snd_ali_dev_free,
- };
- *r_ali = NULL;
-
- snd_ali_printk("creating ...\n");
+ dev_dbg(card->dev, "creating ...\n");
/* enable PCI device */
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
/* check, if we can restrict PCI DMA transfers to 31 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(31)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(31)) < 0) {
- snd_printk(KERN_ERR "architecture does not support "
- "31bit PCI busmaster DMA\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(31))) {
+ dev_err(card->dev,
+ "architecture does not support 31bit PCI busmaster DMA\n");
return -ENXIO;
}
- codec = kzalloc(sizeof(*codec), GFP_KERNEL);
- if (!codec) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
spin_lock_init(&codec->reg_lock);
spin_lock_init(&codec->voice_alloc);
@@ -2161,14 +2032,10 @@ static int __devinit snd_ali_create(struct snd_card *card,
cmdw |= PCI_COMMAND_IO;
pci_write_config_word(pci, PCI_COMMAND, cmdw);
}
- pci_set_master(pci);
- if (snd_ali_resources(codec)) {
- snd_ali_free(codec);
+ if (snd_ali_resources(codec))
return -EBUSY;
- }
-
- synchronize_irq(pci->irq);
+ card->private_free = snd_ali_free;
codec->synth.chmap = 0;
codec->synth.chcnt = 0;
@@ -2194,126 +2061,90 @@ static int __devinit snd_ali_create(struct snd_card *card,
/* M1533: southbridge */
codec->pci_m1533 = pci_get_device(0x10b9, 0x1533, NULL);
if (!codec->pci_m1533) {
- snd_printk(KERN_ERR "ali5451: cannot find ALi 1533 chip.\n");
- snd_ali_free(codec);
+ dev_err(card->dev, "cannot find ALi 1533 chip.\n");
return -ENODEV;
}
/* M7101: power management */
codec->pci_m7101 = pci_get_device(0x10b9, 0x7101, NULL);
if (!codec->pci_m7101 && codec->revision == ALI_5451_V02) {
- snd_printk(KERN_ERR "ali5451: cannot find ALi 7101 chip.\n");
- snd_ali_free(codec);
+ dev_err(card->dev, "cannot find ALi 7101 chip.\n");
return -ENODEV;
}
- snd_ali_printk("snd_device_new is called.\n");
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops);
- if (err < 0) {
- snd_ali_free(codec);
- return err;
- }
-
- snd_card_set_dev(card, &pci->dev);
-
/* initialise synth voices*/
for (i = 0; i < ALI_CHANNELS; i++)
codec->synth.voices[i].number = i;
err = snd_ali_chip_init(codec);
if (err < 0) {
- snd_printk(KERN_ERR "ali create: chip init error.\n");
+ dev_err(card->dev, "ali create: chip init error.\n");
return err;
}
-#ifdef CONFIG_PM
- codec->image = kmalloc(sizeof(*codec->image), GFP_KERNEL);
- if (!codec->image)
- snd_printk(KERN_WARNING "can't allocate apm buffer\n");
-#endif
-
snd_ali_enable_address_interrupt(codec);
codec->hw_initialized = 1;
-
- *r_ali = codec;
- snd_ali_printk("created.\n");
return 0;
}
-static int __devinit snd_ali_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_ali_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
struct snd_card *card;
struct snd_ali *codec;
int err;
- snd_ali_printk("probe ...\n");
+ dev_dbg(&pci->dev, "probe ...\n");
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index, id, THIS_MODULE,
+ sizeof(*codec), &card);
if (err < 0)
return err;
+ codec = card->private_data;
- err = snd_ali_create(card, pci, pcm_channels, spdif, &codec);
+ err = snd_ali_create(card, pci, pcm_channels, spdif);
if (err < 0)
- goto error;
- card->private_data = codec;
+ return err;
- snd_ali_printk("mixer building ...\n");
+ dev_dbg(&pci->dev, "mixer building ...\n");
err = snd_ali_mixer(codec);
if (err < 0)
- goto error;
+ return err;
- snd_ali_printk("pcm building ...\n");
+ dev_dbg(&pci->dev, "pcm building ...\n");
err = snd_ali_build_pcms(codec);
if (err < 0)
- goto error;
+ return err;
snd_ali_proc_init(codec);
- strcpy(card->driver, "ALI5451");
- strcpy(card->shortname, "ALI 5451");
+ strscpy(card->driver, "ALI5451");
+ strscpy(card->shortname, "ALI 5451");
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, codec->port, codec->irq);
- snd_ali_printk("register card.\n");
+ dev_dbg(&pci->dev, "register card.\n");
err = snd_card_register(card);
if (err < 0)
- goto error;
+ return err;
pci_set_drvdata(pci, card);
return 0;
-
- error:
- snd_card_free(card);
- return err;
}
-static void __devexit snd_ali_remove(struct pci_dev *pci)
+static int snd_ali_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_ali_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "ALI 5451",
+static struct pci_driver ali5451_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_ali_ids,
.probe = snd_ali_probe,
- .remove = __devexit_p(snd_ali_remove),
-#ifdef CONFIG_PM
- .suspend = ali_suspend,
- .resume = ali_resume,
-#endif
+ .driver = {
+ .pm = &ali_pm,
+ },
};
-static int __init alsa_card_ali_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ali_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ali_init)
-module_exit(alsa_card_ali_exit)
+module_pci_driver(ali5451_driver);
diff --git a/sound/pci/als300.c b/sound/pci/als300.c
index d7653cb7ac60..733e84def5a7 100644
--- a/sound/pci/als300.c
+++ b/sound/pci/als300.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* als300.c - driver for Avance Logic ALS300/ALS300+ soundcards.
* Copyright (C) 2005 by Ash Willis <ashwillis@programmer.net>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
* TODO
* 4 channel playback for ALS300+
* gameport
@@ -32,13 +19,12 @@
#include <linux/delay.h>
#include <linux/init.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
-
-#include <asm/io.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -87,19 +73,8 @@
#define PLAYBACK_BLOCK_COUNTER 0x9A
#define RECORD_BLOCK_COUNTER 0x9B
-#define DEBUG_CALLS 0
#define DEBUG_PLAY_REC 0
-#if DEBUG_CALLS
-#define snd_als300_dbgcalls(format, args...) printk(KERN_DEBUG format, ##args)
-#define snd_als300_dbgcallenter() printk(KERN_ERR "--> %s\n", __func__)
-#define snd_als300_dbgcallleave() printk(KERN_ERR "<-- %s\n", __func__)
-#else
-#define snd_als300_dbgcalls(format, args...)
-#define snd_als300_dbgcallenter()
-#define snd_als300_dbgcallleave()
-#endif
-
#if DEBUG_PLAY_REC
#define snd_als300_dbgplay(format, args...) printk(KERN_ERR format, ##args)
#else
@@ -111,11 +86,17 @@ enum {DEVICE_ALS300, DEVICE_ALS300_PLUS};
MODULE_AUTHOR("Ash Willis <ashwillis@programmer.net>");
MODULE_DESCRIPTION("Avance Logic ALS300");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Avance Logic,ALS300},{Avance Logic,ALS300+}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for ALS300 sound card.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for ALS300 sound card.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable ALS300 sound card.");
struct snd_als300 {
unsigned long port;
@@ -145,7 +126,7 @@ struct snd_als300_substream_data {
int block_counter_register;
};
-static DEFINE_PCI_DEVICE_TABLE(snd_als300_ids) = {
+static const struct pci_device_id snd_als300_ids[] = {
{ 0x4005, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALS300 },
{ 0x4005, 0x0308, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALS300_PLUS },
{ 0, }
@@ -170,7 +151,6 @@ static inline void snd_als300_gcr_write(unsigned long port,
static void snd_als300_set_irq_flag(struct snd_als300 *chip, int cmd)
{
u32 tmp = snd_als300_gcr_read(chip->port, MISC_CONTROL);
- snd_als300_dbgcallenter();
/* boolean XOR check, since old vs. new hardware have
directly reversed bit setting for ENABLE and DISABLE.
@@ -181,26 +161,13 @@ static void snd_als300_set_irq_flag(struct snd_als300 *chip, int cmd)
else
tmp &= ~IRQ_SET_BIT;
snd_als300_gcr_write(chip->port, MISC_CONTROL, tmp);
- snd_als300_dbgcallleave();
}
-static int snd_als300_free(struct snd_als300 *chip)
+static void snd_als300_free(struct snd_card *card)
{
- snd_als300_dbgcallenter();
- snd_als300_set_irq_flag(chip, IRQ_DISABLE);
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- snd_als300_dbgcallleave();
- return 0;
-}
+ struct snd_als300 *chip = card->private_data;
-static int snd_als300_dev_free(struct snd_device *device)
-{
- struct snd_als300 *chip = device->device_data;
- return snd_als300_free(chip);
+ snd_als300_set_irq_flag(chip, IRQ_DISABLE);
}
static irqreturn_t snd_als300_interrupt(int irq, void *dev_id)
@@ -271,14 +238,6 @@ static irqreturn_t snd_als300plus_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static void __devexit snd_als300_remove(struct pci_dev *pci)
-{
- snd_als300_dbgcallenter();
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
- snd_als300_dbgcallleave();
-}
-
static unsigned short snd_als300_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
@@ -319,19 +278,18 @@ static int snd_als300_ac97(struct snd_als300 *chip)
struct snd_ac97_bus *bus;
struct snd_ac97_template ac97;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_als300_ac97_write,
.read = snd_als300_ac97_read,
};
- snd_als300_dbgcallenter();
- if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus);
+ if (err < 0)
return err;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
- snd_als300_dbgcallleave();
return snd_ac97_mixer(bus, &ac97, &chip->ac97);
}
@@ -342,7 +300,7 @@ static int snd_als300_ac97(struct snd_als300 *chip)
* the card when it is running outside of legacy
* mode.
*/
-static struct snd_pcm_hardware snd_als300_playback_hw =
+static const struct snd_pcm_hardware snd_als300_playback_hw =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -361,7 +319,7 @@ static struct snd_pcm_hardware snd_als300_playback_hw =
.periods_max = 2,
};
-static struct snd_pcm_hardware snd_als300_capture_hw =
+static const struct snd_pcm_hardware snd_als300_capture_hw =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -387,13 +345,13 @@ static int snd_als300_playback_open(struct snd_pcm_substream *substream)
struct snd_als300_substream_data *data = kzalloc(sizeof(*data),
GFP_KERNEL);
- snd_als300_dbgcallenter();
+ if (!data)
+ return -ENOMEM;
chip->playback_substream = substream;
runtime->hw = snd_als300_playback_hw;
runtime->private_data = data;
data->control_register = PLAYBACK_CONTROL;
data->block_counter_register = PLAYBACK_BLOCK_COUNTER;
- snd_als300_dbgcallleave();
return 0;
}
@@ -403,11 +361,8 @@ static int snd_als300_playback_close(struct snd_pcm_substream *substream)
struct snd_als300_substream_data *data;
data = substream->runtime->private_data;
- snd_als300_dbgcallenter();
kfree(data);
chip->playback_substream = NULL;
- snd_pcm_lib_free_pages(substream);
- snd_als300_dbgcallleave();
return 0;
}
@@ -418,13 +373,13 @@ static int snd_als300_capture_open(struct snd_pcm_substream *substream)
struct snd_als300_substream_data *data = kzalloc(sizeof(*data),
GFP_KERNEL);
- snd_als300_dbgcallenter();
+ if (!data)
+ return -ENOMEM;
chip->capture_substream = substream;
runtime->hw = snd_als300_capture_hw;
runtime->private_data = data;
data->control_register = RECORD_CONTROL;
data->block_counter_register = RECORD_BLOCK_COUNTER;
- snd_als300_dbgcallleave();
return 0;
}
@@ -434,26 +389,11 @@ static int snd_als300_capture_close(struct snd_pcm_substream *substream)
struct snd_als300_substream_data *data;
data = substream->runtime->private_data;
- snd_als300_dbgcallenter();
kfree(data);
chip->capture_substream = NULL;
- snd_pcm_lib_free_pages(substream);
- snd_als300_dbgcallleave();
return 0;
}
-static int snd_als300_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-static int snd_als300_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_als300_playback_prepare(struct snd_pcm_substream *substream)
{
u32 tmp;
@@ -462,8 +402,7 @@ static int snd_als300_playback_prepare(struct snd_pcm_substream *substream)
unsigned short period_bytes = snd_pcm_lib_period_bytes(substream);
unsigned short buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
- snd_als300_dbgcallenter();
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
tmp = snd_als300_gcr_read(chip->port, PLAYBACK_CONTROL);
tmp &= ~TRANSFER_START;
@@ -480,8 +419,6 @@ static int snd_als300_playback_prepare(struct snd_pcm_substream *substream)
runtime->dma_addr);
snd_als300_gcr_write(chip->port, PLAYBACK_END,
runtime->dma_addr + buffer_bytes - 1);
- spin_unlock_irq(&chip->reg_lock);
- snd_als300_dbgcallleave();
return 0;
}
@@ -493,8 +430,7 @@ static int snd_als300_capture_prepare(struct snd_pcm_substream *substream)
unsigned short period_bytes = snd_pcm_lib_period_bytes(substream);
unsigned short buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
- snd_als300_dbgcallenter();
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
tmp = snd_als300_gcr_read(chip->port, RECORD_CONTROL);
tmp &= ~TRANSFER_START;
@@ -511,8 +447,6 @@ static int snd_als300_capture_prepare(struct snd_pcm_substream *substream)
runtime->dma_addr);
snd_als300_gcr_write(chip->port, RECORD_END,
runtime->dma_addr + buffer_bytes - 1);
- spin_unlock_irq(&chip->reg_lock);
- snd_als300_dbgcallleave();
return 0;
}
@@ -527,8 +461,7 @@ static int snd_als300_trigger(struct snd_pcm_substream *substream, int cmd)
data = substream->runtime->private_data;
reg = data->control_register;
- snd_als300_dbgcallenter();
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -557,8 +490,6 @@ static int snd_als300_trigger(struct snd_pcm_substream *substream, int cmd)
snd_als300_dbgplay("TRIGGER INVALID\n");
ret = -EINVAL;
}
- spin_unlock(&chip->reg_lock);
- snd_als300_dbgcallleave();
return ret;
}
@@ -572,11 +503,10 @@ static snd_pcm_uframes_t snd_als300_pointer(struct snd_pcm_substream *substream)
data = substream->runtime->private_data;
period_bytes = snd_pcm_lib_period_bytes(substream);
- snd_als300_dbgcallenter();
- spin_lock(&chip->reg_lock);
- current_ptr = (u16) snd_als300_gcr_read(chip->port,
- data->block_counter_register) + 4;
- spin_unlock(&chip->reg_lock);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ current_ptr = (u16) snd_als300_gcr_read(chip->port,
+ data->block_counter_register) + 4;
+ }
if (current_ptr > period_bytes)
current_ptr = 0;
else
@@ -585,43 +515,35 @@ static snd_pcm_uframes_t snd_als300_pointer(struct snd_pcm_substream *substream)
if (data->period_flipflop == 0)
current_ptr += period_bytes;
snd_als300_dbgplay("Pointer (bytes): %d\n", current_ptr);
- snd_als300_dbgcallleave();
return bytes_to_frames(substream->runtime, current_ptr);
}
-static struct snd_pcm_ops snd_als300_playback_ops = {
+static const struct snd_pcm_ops snd_als300_playback_ops = {
.open = snd_als300_playback_open,
.close = snd_als300_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_als300_pcm_hw_params,
- .hw_free = snd_als300_pcm_hw_free,
.prepare = snd_als300_playback_prepare,
.trigger = snd_als300_trigger,
.pointer = snd_als300_pointer,
};
-static struct snd_pcm_ops snd_als300_capture_ops = {
+static const struct snd_pcm_ops snd_als300_capture_ops = {
.open = snd_als300_capture_open,
.close = snd_als300_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_als300_pcm_hw_params,
- .hw_free = snd_als300_pcm_hw_free,
.prepare = snd_als300_capture_prepare,
.trigger = snd_als300_trigger,
.pointer = snd_als300_pointer,
};
-static int __devinit snd_als300_new_pcm(struct snd_als300 *chip)
+static int snd_als300_new_pcm(struct snd_als300 *chip)
{
struct snd_pcm *pcm;
int err;
- snd_als300_dbgcallenter();
err = snd_pcm_new(chip->card, "ALS300", 0, 1, 1, &pcm);
if (err < 0)
return err;
pcm->private_data = chip;
- strcpy(pcm->name, "ALS300");
+ strscpy(pcm->name, "ALS300");
chip->pcm = pcm;
/* set operators */
@@ -631,19 +553,16 @@ static int __devinit snd_als300_new_pcm(struct snd_als300 *chip)
&snd_als300_capture_ops);
/* pre-allocation of buffers */
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 64*1024);
- snd_als300_dbgcallleave();
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
+ 64*1024, 64*1024);
return 0;
}
static void snd_als300_init(struct snd_als300 *chip)
{
- unsigned long flags;
u32 tmp;
- snd_als300_dbgcallenter();
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
chip->revision = (snd_als300_gcr_read(chip->port, MISC_CONTROL) >> 16)
& 0x0000000F;
/* Setup DRAM */
@@ -668,52 +587,35 @@ static void snd_als300_init(struct snd_als300 *chip)
tmp = snd_als300_gcr_read(chip->port, PLAYBACK_CONTROL);
snd_als300_gcr_write(chip->port, PLAYBACK_CONTROL,
tmp & ~TRANSFER_START);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_als300_dbgcallleave();
}
-static int __devinit snd_als300_create(struct snd_card *card,
- struct pci_dev *pci, int chip_type,
- struct snd_als300 **rchip)
+static int snd_als300_create(struct snd_card *card,
+ struct pci_dev *pci, int chip_type)
{
- struct snd_als300 *chip;
+ struct snd_als300 *chip = card->private_data;
void *irq_handler;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_als300_dev_free,
- };
- *rchip = NULL;
-
- snd_als300_dbgcallenter();
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
- printk(KERN_ERR "error setting 28bit DMA mask\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(28))) {
+ dev_err(card->dev, "error setting 28bit DMA mask\n");
return -ENXIO;
}
pci_set_master(pci);
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
chip->card = card;
chip->pci = pci;
chip->irq = -1;
chip->chip_type = chip_type;
spin_lock_init(&chip->reg_lock);
- if ((err = pci_request_regions(pci, "ALS300")) < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pcim_request_all_regions(pci, "ALS300");
+ if (err < 0)
return err;
- }
+
chip->port = pci_resource_start(pci, 0);
if (chip->chip_type == DEVICE_ALS300_PLUS)
@@ -721,83 +623,57 @@ static int __devinit snd_als300_create(struct snd_card *card,
else
irq_handler = snd_als300_interrupt;
- if (request_irq(pci->irq, irq_handler, IRQF_SHARED,
- card->shortname, chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_als300_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, irq_handler, IRQF_SHARED,
+ KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
-
+ card->sync_irq = chip->irq;
+ card->private_free = snd_als300_free;
snd_als300_init(chip);
err = snd_als300_ac97(chip);
if (err < 0) {
- snd_printk(KERN_WARNING "Could not create ac97\n");
- snd_als300_free(chip);
- return err;
- }
-
- if ((err = snd_als300_new_pcm(chip)) < 0) {
- snd_printk(KERN_WARNING "Could not create PCM\n");
- snd_als300_free(chip);
+ dev_err(card->dev, "Could not create ac97\n");
return err;
}
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
- chip, &ops)) < 0) {
- snd_als300_free(chip);
+ err = snd_als300_new_pcm(chip);
+ if (err < 0) {
+ dev_err(card->dev, "Could not create PCM\n");
return err;
}
- snd_card_set_dev(card, &pci->dev);
-
- *rchip = chip;
- snd_als300_dbgcallleave();
return 0;
}
-#ifdef CONFIG_PM
-static int snd_als300_suspend(struct pci_dev *pci, pm_message_t state)
+static int snd_als300_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_als300 *chip = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_ac97_suspend(chip->ac97);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_als300_resume(struct pci_dev *pci)
+static int snd_als300_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_als300 *chip = card->private_data;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "als300: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
snd_als300_init(chip);
snd_ac97_resume(chip->ac97);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif
-static int __devinit snd_als300_probe(struct pci_dev *pci,
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_als300_pm, snd_als300_suspend, snd_als300_resume);
+
+static int snd_als300_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
static int dev;
@@ -812,20 +688,19 @@ static int __devinit snd_als300_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
-
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
chip_type = pci_id->driver_data;
- if ((err = snd_als300_create(card, pci, chip_type, &chip)) < 0) {
- snd_card_free(card);
- return err;
- }
- card->private_data = chip;
+ err = snd_als300_create(card, pci, chip_type);
+ if (err < 0)
+ goto error;
- strcpy(card->driver, "ALS300");
+ strscpy(card->driver, "ALS300");
if (chip->chip_type == DEVICE_ALS300_PLUS)
/* don't know much about ALS300+ yet
* print revision number for now */
@@ -836,35 +711,26 @@ static int __devinit snd_als300_probe(struct pci_dev *pci,
sprintf(card->longname, "%s at 0x%lx irq %i",
card->shortname, chip->port, chip->irq);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+
pci_set_drvdata(pci, card);
dev++;
return 0;
+
+ error:
+ snd_card_free(card);
+ return err;
}
-static struct pci_driver driver = {
- .name = "ALS300",
+static struct pci_driver als300_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_als300_ids,
.probe = snd_als300_probe,
- .remove = __devexit_p(snd_als300_remove),
-#ifdef CONFIG_PM
- .suspend = snd_als300_suspend,
- .resume = snd_als300_resume,
-#endif
+ .driver = {
+ .pm = &snd_als300_pm,
+ },
};
-static int __init alsa_card_als300_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_als300_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_als300_init)
-module_exit(alsa_card_als300_exit)
+module_pci_driver(als300_driver);
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
index 0e247cb90ecc..33034e07b3d6 100644
--- a/sound/pci/als4000.c
+++ b/sound/pci/als4000.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* card-als4000.c - driver for Avance Logic ALS4000 based soundcards.
* Copyright (C) 2000 by Bart Hartgers <bart@etpmod.phys.tue.nl>,
@@ -6,21 +7,6 @@
*
* Framework borrowed from Massimo Piccioni's card-als100.c.
*
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
* NOTES
*
* Since Avance does not provide any meaningful documentation, and I
@@ -65,11 +51,11 @@
* - power management? (card can do voice wakeup according to datasheet!!)
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/gameport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -82,15 +68,14 @@
MODULE_AUTHOR("Bart Hartgers <bart@etpmod.phys.tue.nl>, Andreas Mohr");
MODULE_DESCRIPTION("Avance Logic ALS4000");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Avance Logic,ALS4000}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
#ifdef SUPPORT_JOYSTICK
static int joystick_port[SNDRV_CARDS];
#endif
@@ -102,7 +87,7 @@ MODULE_PARM_DESC(id, "ID string for ALS4000 soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable ALS4000 soundcard.");
#ifdef SUPPORT_JOYSTICK
-module_param_array(joystick_port, int, NULL, 0444);
+module_param_hw_array(joystick_port, int, ioport, NULL, 0444);
MODULE_PARM_DESC(joystick_port, "Joystick port address for ALS4000 soundcard. (0 = disabled)");
#endif
@@ -116,7 +101,7 @@ struct snd_card_als4000 {
#endif
};
-static DEFINE_PCI_DEVICE_TABLE(snd_als4000_ids) = {
+static const struct pci_device_id snd_als4000_ids[] = {
{ 0x4005, 0x4000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ALS4000 */
{ 0, }
};
@@ -368,18 +353,6 @@ CMD_SIGNED|CMD_STEREO, /* ALS4000_FORMAT_S16L_STEREO */
};
#define capture_cmd(chip) (capture_cmd_vals[(chip)->capture_format])
-static int snd_als4000_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_als4000_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_lib_free_pages(substream);
- return 0;
-}
-
static int snd_als4000_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_sb *chip = snd_pcm_substream_chip(substream);
@@ -396,14 +369,14 @@ static int snd_als4000_capture_prepare(struct snd_pcm_substream *substream)
count >>= 1;
count--;
- spin_lock_irq(&chip->reg_lock);
- snd_als4000_set_rate(chip, runtime->rate);
- snd_als4000_set_capture_dma(chip, runtime->dma_addr, size);
- spin_unlock_irq(&chip->reg_lock);
- spin_lock_irq(&chip->mixer_lock);
- snd_als4_cr_write(chip, ALS4K_CR1C_FIFO2_BLOCK_LENGTH_LO, count & 0xff);
- snd_als4_cr_write(chip, ALS4K_CR1D_FIFO2_BLOCK_LENGTH_HI, count >> 8);
- spin_unlock_irq(&chip->mixer_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ snd_als4000_set_rate(chip, runtime->rate);
+ snd_als4000_set_capture_dma(chip, runtime->dma_addr, size);
+ }
+ scoped_guard(spinlock_irq, &chip->mixer_lock) {
+ snd_als4_cr_write(chip, ALS4K_CR1C_FIFO2_BLOCK_LENGTH_LO, count & 0xff);
+ snd_als4_cr_write(chip, ALS4K_CR1D_FIFO2_BLOCK_LENGTH_HI, count >> 8);
+ }
return 0;
}
@@ -429,7 +402,7 @@ static int snd_als4000_playback_prepare(struct snd_pcm_substream *substream)
* reordering, ...). Something seems to get enabled on playback
* that I haven't found out how to disable again, which then causes
* the switching pops to reach the speakers the next time here. */
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
snd_als4000_set_rate(chip, runtime->rate);
snd_als4000_set_playback_dma(chip, runtime->dma_addr, size);
@@ -440,7 +413,6 @@ static int snd_als4000_playback_prepare(struct snd_pcm_substream *substream)
snd_sbdsp_command(chip, count & 0xff);
snd_sbdsp_command(chip, count >> 8);
snd_sbdsp_command(chip, playback_cmd(chip).dma_off);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -456,7 +428,7 @@ static int snd_als4000_capture_trigger(struct snd_pcm_substream *substream, int
Probably need to take reg_lock as outer (or inner??) lock, too.
(or serialize both lock operations? probably not, though... - racy?)
*/
- spin_lock(&chip->mixer_lock);
+ guard(spinlock)(&chip->mixer_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -474,7 +446,6 @@ static int snd_als4000_capture_trigger(struct snd_pcm_substream *substream, int
result = -EINVAL;
break;
}
- spin_unlock(&chip->mixer_lock);
return result;
}
@@ -483,7 +454,7 @@ static int snd_als4000_playback_trigger(struct snd_pcm_substream *substream, int
struct snd_sb *chip = snd_pcm_substream_chip(substream);
int result = 0;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -499,7 +470,6 @@ static int snd_als4000_playback_trigger(struct snd_pcm_substream *substream, int
result = -EINVAL;
break;
}
- spin_unlock(&chip->reg_lock);
return result;
}
@@ -508,9 +478,9 @@ static snd_pcm_uframes_t snd_als4000_capture_pointer(struct snd_pcm_substream *s
struct snd_sb *chip = snd_pcm_substream_chip(substream);
unsigned int result;
- spin_lock(&chip->reg_lock);
- result = snd_als4k_gcr_read(chip, ALS4K_GCRA4_FIFO2_CURRENT_ADDR);
- spin_unlock(&chip->reg_lock);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ result = snd_als4k_gcr_read(chip, ALS4K_GCRA4_FIFO2_CURRENT_ADDR);
+ }
result &= 0xffff;
return bytes_to_frames( substream->runtime, result );
}
@@ -520,9 +490,9 @@ static snd_pcm_uframes_t snd_als4000_playback_pointer(struct snd_pcm_substream *
struct snd_sb *chip = snd_pcm_substream_chip(substream);
unsigned result;
- spin_lock(&chip->reg_lock);
- result = snd_als4k_gcr_read(chip, ALS4K_GCRA0_FIFO1_CURRENT_ADDR);
- spin_unlock(&chip->reg_lock);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ result = snd_als4k_gcr_read(chip, ALS4K_GCRA0_FIFO1_CURRENT_ADDR);
+ }
result &= 0xffff;
return bytes_to_frames( substream->runtime, result );
}
@@ -563,10 +533,10 @@ static irqreturn_t snd_als4000_interrupt(int irq, void *dev_id)
snd_als4k_iobase_writeb(chip->alt_port,
ALS4K_IOB_0E_IRQTYPE_SB_CR1E_MPU, pci_irqstatus);
- spin_lock(&chip->mixer_lock);
- /* SPECS_PAGE: 20 */
- sb_irqstatus = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
- spin_unlock(&chip->mixer_lock);
+ scoped_guard(spinlock, &chip->mixer_lock) {
+ /* SPECS_PAGE: 20 */
+ sb_irqstatus = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
+ }
if (sb_irqstatus & SB_IRQTYPE_8BIT)
snd_sb_ack_8bit(chip);
@@ -578,7 +548,7 @@ static irqreturn_t snd_als4000_interrupt(int irq, void *dev_id)
snd_als4k_iobase_readb(chip->alt_port,
ALS4K_IOB_16_ACK_FOR_CR1E);
- /* printk(KERN_INFO "als4000: irq 0x%04x 0x%04x\n",
+ /* dev_dbg(chip->card->dev, "als4000: irq 0x%04x 0x%04x\n",
pci_irqstatus, sb_irqstatus); */
/* only ack the things we actually handled above */
@@ -592,7 +562,7 @@ static irqreturn_t snd_als4000_interrupt(int irq, void *dev_id)
/*****************************************************************/
-static struct snd_pcm_hardware snd_als4000_playback =
+static const struct snd_pcm_hardware snd_als4000_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -611,7 +581,7 @@ static struct snd_pcm_hardware snd_als4000_playback =
.fifo_size = 0
};
-static struct snd_pcm_hardware snd_als4000_capture =
+static const struct snd_pcm_hardware snd_als4000_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
@@ -647,7 +617,6 @@ static int snd_als4000_playback_close(struct snd_pcm_substream *substream)
struct snd_sb *chip = snd_pcm_substream_chip(substream);
chip->playback_substream = NULL;
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -666,35 +635,28 @@ static int snd_als4000_capture_close(struct snd_pcm_substream *substream)
struct snd_sb *chip = snd_pcm_substream_chip(substream);
chip->capture_substream = NULL;
- snd_pcm_lib_free_pages(substream);
return 0;
}
/******************************************************************/
-static struct snd_pcm_ops snd_als4000_playback_ops = {
+static const struct snd_pcm_ops snd_als4000_playback_ops = {
.open = snd_als4000_playback_open,
.close = snd_als4000_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_als4000_hw_params,
- .hw_free = snd_als4000_hw_free,
.prepare = snd_als4000_playback_prepare,
.trigger = snd_als4000_playback_trigger,
.pointer = snd_als4000_playback_pointer
};
-static struct snd_pcm_ops snd_als4000_capture_ops = {
+static const struct snd_pcm_ops snd_als4000_capture_ops = {
.open = snd_als4000_capture_open,
.close = snd_als4000_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_als4000_hw_params,
- .hw_free = snd_als4000_hw_free,
.prepare = snd_als4000_capture_prepare,
.trigger = snd_als4000_capture_trigger,
.pointer = snd_als4000_capture_pointer
};
-static int __devinit snd_als4000_pcm(struct snd_sb *chip, int device)
+static int snd_als4000_pcm(struct snd_sb *chip, int device)
{
struct snd_pcm *pcm;
int err;
@@ -707,8 +669,8 @@ static int __devinit snd_als4000_pcm(struct snd_sb *chip, int device)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_als4000_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_als4000_capture_ops);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
- 64*1024, 64*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev, 64*1024, 64*1024);
chip->pcm = pcm;
@@ -744,18 +706,18 @@ static void snd_als4000_configure(struct snd_sb *chip)
int i;
/* do some more configuration */
- spin_lock_irq(&chip->mixer_lock);
- tmp = snd_als4_cr_read(chip, ALS4K_CR0_SB_CONFIG);
- snd_als4_cr_write(chip, ALS4K_CR0_SB_CONFIG,
- tmp|ALS4K_CR0_MX80_81_REG_WRITE_ENABLE);
- /* always select DMA channel 0, since we do not actually use DMA
- * SPECS_PAGE: 19/20 */
- snd_sbmixer_write(chip, SB_DSP4_DMASETUP, SB_DMASETUP_DMA0);
- snd_als4_cr_write(chip, ALS4K_CR0_SB_CONFIG,
- tmp & ~ALS4K_CR0_MX80_81_REG_WRITE_ENABLE);
- spin_unlock_irq(&chip->mixer_lock);
+ scoped_guard(spinlock_irq, &chip->mixer_lock) {
+ tmp = snd_als4_cr_read(chip, ALS4K_CR0_SB_CONFIG);
+ snd_als4_cr_write(chip, ALS4K_CR0_SB_CONFIG,
+ tmp|ALS4K_CR0_MX80_81_REG_WRITE_ENABLE);
+ /* always select DMA channel 0, since we do not actually use DMA
+ * SPECS_PAGE: 19/20 */
+ snd_sbmixer_write(chip, SB_DSP4_DMASETUP, SB_DMASETUP_DMA0);
+ snd_als4_cr_write(chip, ALS4K_CR0_SB_CONFIG,
+ tmp & ~ALS4K_CR0_MX80_81_REG_WRITE_ENABLE);
+ }
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
/* enable interrupts */
snd_als4k_gcr_write(chip, ALS4K_GCR8C_MISC_CTRL,
ALS4K_GCR8C_IRQ_MASK_CTRL_ENABLE);
@@ -766,11 +728,10 @@ static void snd_als4000_configure(struct snd_sb *chip)
/* enable burst mode to prevent dropouts during high PCI bus usage */
snd_als4k_gcr_write(chip, ALS4K_GCR99_DMA_EMULATION_CTRL,
(snd_als4k_gcr_read(chip, ALS4K_GCR99_DMA_EMULATION_CTRL) & ~0x07) | 0x04);
- spin_unlock_irq(&chip->reg_lock);
}
#ifdef SUPPORT_JOYSTICK
-static int __devinit snd_als4000_create_gameport(struct snd_card_als4000 *acard, int dev)
+static int snd_als4000_create_gameport(struct snd_card_als4000 *acard, int dev)
{
struct gameport *gp;
struct resource *r;
@@ -781,24 +742,25 @@ static int __devinit snd_als4000_create_gameport(struct snd_card_als4000 *acard,
if (joystick_port[dev] == 1) { /* auto-detect */
for (io_port = 0x200; io_port <= 0x218; io_port += 8) {
- r = request_region(io_port, 8, "ALS4000 gameport");
+ r = devm_request_region(&acard->pci->dev, io_port, 8,
+ "ALS4000 gameport");
if (r)
break;
}
} else {
io_port = joystick_port[dev];
- r = request_region(io_port, 8, "ALS4000 gameport");
+ r = devm_request_region(&acard->pci->dev, io_port, 8,
+ "ALS4000 gameport");
}
if (!r) {
- printk(KERN_WARNING "als4000: cannot reserve joystick ports\n");
+ dev_warn(&acard->pci->dev, "cannot reserve joystick ports\n");
return -EBUSY;
}
acard->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "als4000: cannot allocate memory for gameport\n");
- release_and_free_resource(r);
+ dev_err(&acard->pci->dev, "cannot allocate memory for gameport\n");
return -ENOMEM;
}
@@ -806,7 +768,6 @@ static int __devinit snd_als4000_create_gameport(struct snd_card_als4000 *acard,
gameport_set_phys(gp, "pci%s/gameport0", pci_name(acard->pci));
gameport_set_dev_parent(gp, &acard->pci->dev);
gp->io = io_port;
- gameport_set_port_data(gp, r);
/* Enable legacy joystick port */
snd_als4000_set_addr(acard->iobase, 0, 0, 0, 1);
@@ -819,15 +780,11 @@ static int __devinit snd_als4000_create_gameport(struct snd_card_als4000 *acard,
static void snd_als4000_free_gameport(struct snd_card_als4000 *acard)
{
if (acard->gameport) {
- struct resource *r = gameport_get_port_data(acard->gameport);
-
gameport_unregister_port(acard->gameport);
acard->gameport = NULL;
/* disable joystick */
snd_als4000_set_addr(acard->iobase, 0, 0, 0, 0);
-
- release_and_free_resource(r);
}
}
#else
@@ -843,12 +800,10 @@ static void snd_card_als4000_free( struct snd_card *card )
snd_als4k_gcr_write_addr(acard->iobase, ALS4K_GCR8C_MISC_CTRL, 0);
/* free resources */
snd_als4000_free_gameport(acard);
- pci_release_regions(acard->pci);
- pci_disable_device(acard->pci);
}
-static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_card_als4000_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -867,35 +822,30 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
}
/* enable PCI device */
- if ((err = pci_enable_device(pci)) < 0) {
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- }
+
/* check, if we can restrict PCI DMA transfers to 24 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) {
- snd_printk(KERN_ERR "architecture does not support 24bit PCI busmaster DMA\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(24))) {
+ dev_err(&pci->dev, "architecture does not support 24bit PCI busmaster DMA\n");
return -ENXIO;
}
- if ((err = pci_request_regions(pci, "ALS4000")) < 0) {
- pci_disable_device(pci);
+ err = pcim_request_all_regions(pci, "ALS4000");
+ if (err < 0)
return err;
- }
iobase = pci_resource_start(pci, 0);
pci_read_config_word(pci, PCI_COMMAND, &word);
pci_write_config_word(pci, PCI_COMMAND, word | PCI_COMMAND_IO);
pci_set_master(pci);
- err = snd_card_create(index[dev], id[dev], THIS_MODULE,
- sizeof(*acard) /* private_data: acard */,
- &card);
- if (err < 0) {
- pci_release_regions(pci);
- pci_disable_device(pci);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*acard) /* private_data: acard */,
+ &card);
+ if (err < 0)
return err;
- }
acard = card->private_data;
acard->pci = pci;
@@ -905,37 +855,38 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
/* disable all legacy ISA stuff */
snd_als4000_set_addr(acard->iobase, 0, 0, 0, 0);
- if ((err = snd_sbdsp_create(card,
- iobase + ALS4K_IOB_10_ADLIB_ADDR0,
- pci->irq,
+ err = snd_sbdsp_create(card,
+ iobase + ALS4K_IOB_10_ADLIB_ADDR0,
+ pci->irq,
/* internally registered as IRQF_SHARED in case of ALS4000 SB */
- snd_als4000_interrupt,
- -1,
- -1,
- SB_HW_ALS4000,
- &chip)) < 0) {
- goto out_err;
- }
+ snd_als4000_interrupt,
+ -1,
+ -1,
+ SB_HW_ALS4000,
+ &chip);
+ if (err < 0)
+ return err;
acard->chip = chip;
chip->pci = pci;
chip->alt_port = iobase;
- snd_card_set_dev(card, &pci->dev);
snd_als4000_configure(chip);
- strcpy(card->driver, "ALS4000");
- strcpy(card->shortname, "Avance Logic ALS4000");
+ strscpy(card->driver, "ALS4000");
+ strscpy(card->shortname, "Avance Logic ALS4000");
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, chip->alt_port, chip->irq);
- if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_ALS4000,
- iobase + ALS4K_IOB_30_MIDI_DATA,
- MPU401_INFO_INTEGRATED,
- pci->irq, 0, &chip->rmidi)) < 0) {
- printk(KERN_ERR "als4000: no MPU-401 device at 0x%lx?\n",
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_ALS4000,
+ iobase + ALS4K_IOB_30_MIDI_DATA,
+ MPU401_INFO_INTEGRATED |
+ MPU401_INFO_IRQ_HOOK,
+ -1, &chip->rmidi);
+ if (err < 0) {
+ dev_err(&pci->dev, "no MPU-401 device at 0x%lx?\n",
iobase + ALS4K_IOB_30_MIDI_DATA);
- goto out_err;
+ return err;
}
/* FIXME: ALS4000 has interesting MPU401 configuration features
* at ALS4K_CR1A_MPU401_UART_MODE_CONTROL
@@ -943,83 +894,62 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
* however there doesn't seem to be an ALSA API for this...
* SPECS_PAGE: 21 */
- if ((err = snd_als4000_pcm(chip, 0)) < 0) {
- goto out_err;
- }
- if ((err = snd_sbmixer_new(chip)) < 0) {
- goto out_err;
- }
+ err = snd_als4000_pcm(chip, 0);
+ if (err < 0)
+ return err;
+
+ err = snd_sbmixer_new(chip);
+ if (err < 0)
+ return err;
if (snd_opl3_create(card,
iobase + ALS4K_IOB_10_ADLIB_ADDR0,
iobase + ALS4K_IOB_12_ADLIB_ADDR2,
OPL3_HW_AUTO, 1, &opl3) < 0) {
- printk(KERN_ERR "als4000: no OPL device at 0x%lx-0x%lx?\n",
+ dev_err(&pci->dev, "no OPL device at 0x%lx-0x%lx?\n",
iobase + ALS4K_IOB_10_ADLIB_ADDR0,
iobase + ALS4K_IOB_12_ADLIB_ADDR2);
} else {
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
- goto out_err;
- }
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
+ return err;
}
snd_als4000_create_gameport(acard, dev);
- if ((err = snd_card_register(card)) < 0) {
- goto out_err;
- }
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
+
pci_set_drvdata(pci, card);
dev++;
- err = 0;
- goto out;
-
-out_err:
- snd_card_free(card);
-
-out:
- return err;
+ return 0;
}
-static void __devexit snd_card_als4000_remove(struct pci_dev *pci)
+static int snd_card_als4000_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_card_als4000_probe(pci, pci_id));
}
-#ifdef CONFIG_PM
-static int snd_als4000_suspend(struct pci_dev *pci, pm_message_t state)
+static int snd_als4000_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_card_als4000 *acard = card->private_data;
struct snd_sb *chip = acard->chip;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_sbmixer_suspend(chip);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_als4000_resume(struct pci_dev *pci)
+static int snd_als4000_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_card_als4000 *acard = card->private_data;
struct snd_sb *chip = acard->chip;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "als4000: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
snd_als4000_configure(chip);
snd_sbdsp_reset(chip);
snd_sbmixer_resume(chip);
@@ -1032,29 +962,16 @@ static int snd_als4000_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_als4000_pm, snd_als4000_suspend, snd_als4000_resume);
-static struct pci_driver driver = {
- .name = "ALS4000",
+static struct pci_driver als4000_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_als4000_ids,
.probe = snd_card_als4000_probe,
- .remove = __devexit_p(snd_card_als4000_remove),
-#ifdef CONFIG_PM
- .suspend = snd_als4000_suspend,
- .resume = snd_als4000_resume,
-#endif
+ .driver = {
+ .pm = &snd_als4000_pm,
+ },
};
-static int __init alsa_card_als4000_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_als4000_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_als4000_init)
-module_exit(alsa_card_als4000_exit)
+module_pci_driver(als4000_driver);
diff --git a/sound/pci/asihpi/Makefile b/sound/pci/asihpi/Makefile
index 391830a4556c..d558a974fa7e 100644
--- a/sound/pci/asihpi/Makefile
+++ b/sound/pci/asihpi/Makefile
@@ -1,4 +1,5 @@
-snd-asihpi-objs := asihpi.o hpioctl.o hpimsginit.o\
+# SPDX-License-Identifier: GPL-2.0-only
+snd-asihpi-y := asihpi.o hpioctl.o hpimsginit.o\
hpicmn.o hpifunc.o hpidebug.o hpidspcd.o\
hpios.o hpi6000.o hpi6205.o hpimsgx.o
diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c
index c80b0b863c54..fd0a67b772d1 100644
--- a/sound/pci/asihpi/asihpi.c
+++ b/sound/pci/asihpi/asihpi.c
@@ -1,49 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Asihpi soundcard
- * Copyright (c) by AudioScience Inc <alsa@audioscience.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
+ * Copyright (c) by AudioScience Inc <support@audioscience.com>
*
* The following is not a condition of use, merely a request:
* If you modify this program, particularly if you fix errors, AudioScience Inc
* would appreciate it if you grant us the right to use those modifications
* for any purpose including commercial applications.
*/
-/* >0: print Hw params, timer vars. >1: print stream write/copy sizes */
-#define REALLY_VERBOSE_LOGGING 0
-
-#if REALLY_VERBOSE_LOGGING
-#define VPRINTK1 snd_printd
-#else
-#define VPRINTK1(...)
-#endif
-
-#if REALLY_VERBOSE_LOGGING > 1
-#define VPRINTK2 snd_printd
-#else
-#define VPRINTK2(...)
-#endif
-
-#ifndef ASI_STYLE_NAMES
-/* not sure how ALSA style name should look */
-#define ASI_STYLE_NAMES 1
-#endif
#include "hpi_internal.h"
+#include "hpi_version.h"
#include "hpimsginit.h"
#include "hpioctl.h"
+#include "hpicmn.h"
#include <linux/pci.h>
#include <linux/init.h>
@@ -51,6 +21,7 @@
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/wait.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -60,38 +31,44 @@
#include <sound/tlv.h>
#include <sound/hwdep.h>
-
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AudioScience inc. <support@audioscience.com>");
-MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx");
+MODULE_DESCRIPTION("AudioScience ALSA ASI5xxx ASI6xxx ASI87xx ASI89xx "
+ HPI_VER_STRING);
+
+#ifdef ASIHPI_VERBOSE_DEBUG
+#define asihpi_dbg(format, args...) pr_debug(format, ##args)
+#else
+#define asihpi_dbg(format, args...) do { } while (0)
+#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
-static int enable_hpi_hwdep = 1;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable_hpi_hwdep = 1;
-module_param_array(index, int, NULL, S_IRUGO);
+module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "ALSA index value for AudioScience soundcard.");
-module_param_array(id, charp, NULL, S_IRUGO);
+module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ALSA ID string for AudioScience soundcard.");
-module_param_array(enable, bool, NULL, S_IRUGO);
+module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "ALSA enable AudioScience soundcard.");
-module_param(enable_hpi_hwdep, bool, S_IRUGO|S_IWUSR);
+module_param(enable_hpi_hwdep, bool, 0644);
MODULE_PARM_DESC(enable_hpi_hwdep,
"ALSA enable HPI hwdep for AudioScience soundcard ");
/* identify driver */
#ifdef KERNEL_ALSA_BUILD
-static char *build_info = "built using headers from kernel source";
-module_param(build_info, charp, S_IRUGO);
-MODULE_PARM_DESC(build_info, "built using headers from kernel source");
+static char *build_info = "Built using headers from kernel source";
+module_param(build_info, charp, 0444);
+MODULE_PARM_DESC(build_info, "Built using headers from kernel source");
#else
-static char *build_info = "built within ALSA source";
-module_param(build_info, charp, S_IRUGO);
-MODULE_PARM_DESC(build_info, "built within ALSA source");
+static char *build_info = "Built within ALSA source";
+module_param(build_info, charp, 0444);
+MODULE_PARM_DESC(build_info, "Built within ALSA source");
#endif
/* set to 1 to dump every control from adapter to log */
@@ -100,23 +77,17 @@ static const int mixer_dump;
#define DEFAULT_SAMPLERATE 44100
static int adapter_fs = DEFAULT_SAMPLERATE;
-static struct hpi_hsubsys *ss; /* handle to HPI audio subsystem */
-
/* defaults */
#define PERIODS_MIN 2
-#define PERIOD_BYTES_MIN 2304
+#define PERIOD_BYTES_MIN 2048
#define BUFFER_BYTES_MAX (512 * 1024)
-/*#define TIMER_MILLISECONDS 20
-#define FORCE_TIMER_JIFFIES ((TIMER_MILLISECONDS * HZ + 999)/1000)
-*/
-
#define MAX_CLOCKSOURCES (HPI_SAMPLECLOCK_SOURCE_LAST + 1 + 7)
struct clk_source {
int source;
int index;
- char *name;
+ const char *name;
};
struct clk_cache {
@@ -129,22 +100,28 @@ struct clk_cache {
struct snd_card_asihpi {
struct snd_card *card;
struct pci_dev *pci;
- u16 adapter_index;
- u32 serial_number;
- u16 type;
- u16 version;
- u16 num_outstreams;
- u16 num_instreams;
+ struct hpi_adapter *hpi;
+
+ /* In low latency mode there is only one stream, a pointer to its
+ * private data is stored here on trigger and cleared on stop.
+ * The interrupt handler uses it as a parameter when calling
+ * snd_card_asihpi_timer_function().
+ */
+ struct snd_card_asihpi_pcm *llmode_streampriv;
+ void (*pcm_start)(struct snd_pcm_substream *substream);
+ void (*pcm_stop)(struct snd_pcm_substream *substream);
u32 h_mixer;
struct clk_cache cc;
- u16 support_mmap;
+ u16 can_dma;
u16 support_grouping;
u16 support_mrx;
u16 update_interval_frames;
u16 in_max_chans;
u16 out_max_chans;
+ u16 in_min_chans;
+ u16 out_min_chans;
};
/* Per stream data */
@@ -152,11 +129,13 @@ struct snd_card_asihpi_pcm {
struct timer_list timer;
unsigned int respawn_timer;
unsigned int hpi_buffer_attached;
- unsigned int pcm_size;
- unsigned int pcm_count;
+ unsigned int buffer_bytes;
+ unsigned int period_bytes;
unsigned int bytes_per_sec;
- unsigned int pcm_irq_pos; /* IRQ position */
- unsigned int pcm_buf_pos; /* position in buffer */
+ unsigned int pcm_buf_host_rw_ofs; /* Host R/W pos */
+ unsigned int pcm_buf_dma_ofs; /* DMA R/W offset in buffer */
+ unsigned int pcm_buf_elapsed_dma_ofs; /* DMA R/W offset in buffer */
+ unsigned int drained_count;
struct snd_pcm_substream *substream;
u32 h_stream;
struct hpi_format format;
@@ -167,7 +146,6 @@ struct snd_card_asihpi_pcm {
/* Functions to allow driver to give a buffer to HPI for busmastering */
static u16 hpi_stream_host_buffer_attach(
- struct hpi_hsubsys *hS,
u32 h_stream, /* handle to outstream. */
u32 size_in_bytes, /* size in bytes of bus mastering buffer */
u32 pci_address
@@ -194,10 +172,7 @@ static u16 hpi_stream_host_buffer_attach(
return hr.error;
}
-static u16 hpi_stream_host_buffer_detach(
- struct hpi_hsubsys *hS,
- u32 h_stream
-)
+static u16 hpi_stream_host_buffer_detach(u32 h_stream)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -218,24 +193,23 @@ static u16 hpi_stream_host_buffer_detach(
return hr.error;
}
-static inline u16 hpi_stream_start(struct hpi_hsubsys *hS, u32 h_stream)
+static inline u16 hpi_stream_start(u32 h_stream)
{
if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
- return hpi_outstream_start(hS, h_stream);
+ return hpi_outstream_start(h_stream);
else
- return hpi_instream_start(hS, h_stream);
+ return hpi_instream_start(h_stream);
}
-static inline u16 hpi_stream_stop(struct hpi_hsubsys *hS, u32 h_stream)
+static inline u16 hpi_stream_stop(u32 h_stream)
{
if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
- return hpi_outstream_stop(hS, h_stream);
+ return hpi_outstream_stop(h_stream);
else
- return hpi_instream_stop(hS, h_stream);
+ return hpi_instream_stop(h_stream);
}
static inline u16 hpi_stream_get_info_ex(
- struct hpi_hsubsys *hS,
u32 h_stream,
u16 *pw_state,
u32 *pbuffer_size,
@@ -244,49 +218,40 @@ static inline u16 hpi_stream_get_info_ex(
u32 *pauxiliary_data
)
{
+ u16 e;
if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
- return hpi_outstream_get_info_ex(hS, h_stream, pw_state,
+ e = hpi_outstream_get_info_ex(h_stream, pw_state,
pbuffer_size, pdata_in_buffer,
psample_count, pauxiliary_data);
else
- return hpi_instream_get_info_ex(hS, h_stream, pw_state,
+ e = hpi_instream_get_info_ex(h_stream, pw_state,
pbuffer_size, pdata_in_buffer,
psample_count, pauxiliary_data);
+ return e;
}
-static inline u16 hpi_stream_group_add(struct hpi_hsubsys *hS,
+static inline u16 hpi_stream_group_add(
u32 h_master,
u32 h_stream)
{
if (hpi_handle_object(h_master) == HPI_OBJ_OSTREAM)
- return hpi_outstream_group_add(hS, h_master, h_stream);
- else
- return hpi_instream_group_add(hS, h_master, h_stream);
-}
-
-static inline u16 hpi_stream_group_reset(struct hpi_hsubsys *hS,
- u32 h_stream)
-{
- if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
- return hpi_outstream_group_reset(hS, h_stream);
+ return hpi_outstream_group_add(h_master, h_stream);
else
- return hpi_instream_group_reset(hS, h_stream);
+ return hpi_instream_group_add(h_master, h_stream);
}
-static inline u16 hpi_stream_group_get_map(struct hpi_hsubsys *hS,
- u32 h_stream, u32 *mo, u32 *mi)
+static inline u16 hpi_stream_group_reset(u32 h_stream)
{
if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM)
- return hpi_outstream_group_get_map(hS, h_stream, mo, mi);
+ return hpi_outstream_group_reset(h_stream);
else
- return hpi_instream_group_get_map(hS, h_stream, mo, mi);
+ return hpi_instream_group_reset(h_stream);
}
static u16 handle_error(u16 err, int line, char *filename)
{
if (err)
- printk(KERN_WARNING
- "in file %s, line %d: HPI error %d\n",
+ pr_warn("in file %s, line %d: HPI error %d\n",
filename, line, err);
return err;
}
@@ -294,53 +259,58 @@ static u16 handle_error(u16 err, int line, char *filename)
#define hpi_handle_error(x) handle_error(x, __LINE__, __FILE__)
/***************************** GENERAL PCM ****************/
-#if REALLY_VERBOSE_LOGGING
-static void print_hwparams(struct snd_pcm_hw_params *p)
-{
- snd_printd("HWPARAMS \n");
- snd_printd("samplerate %d \n", params_rate(p));
- snd_printd("channels %d \n", params_channels(p));
- snd_printd("format %d \n", params_format(p));
- snd_printd("subformat %d \n", params_subformat(p));
- snd_printd("buffer bytes %d \n", params_buffer_bytes(p));
- snd_printd("period bytes %d \n", params_period_bytes(p));
- snd_printd("access %d \n", params_access(p));
- snd_printd("period_size %d \n", params_period_size(p));
- snd_printd("periods %d \n", params_periods(p));
- snd_printd("buffer_size %d \n", params_buffer_size(p));
+
+static void print_hwparams(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *p)
+{
+ struct device *dev = substream->pcm->card->dev;
+ char name[16];
+
+ snd_pcm_debug_name(substream, name, sizeof(name));
+ dev_dbg(dev, "%s HWPARAMS\n", name);
+ dev_dbg(dev, " samplerate=%dHz channels=%d format=%d subformat=%d\n",
+ params_rate(p), params_channels(p),
+ params_format(p), params_subformat(p));
+ dev_dbg(dev, " buffer=%dB period=%dB period_size=%dB periods=%d\n",
+ params_buffer_bytes(p), params_period_bytes(p),
+ params_period_size(p), params_periods(p));
+ dev_dbg(dev, " buffer_size=%d access=%d data_rate=%dB/s\n",
+ params_buffer_size(p), params_access(p),
+ params_rate(p) * params_channels(p) *
+ snd_pcm_format_width(params_format(p)) / 8);
}
-#else
-#define print_hwparams(x)
-#endif
-static snd_pcm_format_t hpi_to_alsa_formats[] = {
- -1, /* INVALID */
+#define INVALID_FORMAT (__force snd_pcm_format_t)(-1)
+
+static const snd_pcm_format_t hpi_to_alsa_formats[] = {
+ INVALID_FORMAT, /* INVALID */
SNDRV_PCM_FORMAT_U8, /* HPI_FORMAT_PCM8_UNSIGNED 1 */
SNDRV_PCM_FORMAT_S16, /* HPI_FORMAT_PCM16_SIGNED 2 */
- -1, /* HPI_FORMAT_MPEG_L1 3 */
+ INVALID_FORMAT, /* HPI_FORMAT_MPEG_L1 3 */
SNDRV_PCM_FORMAT_MPEG, /* HPI_FORMAT_MPEG_L2 4 */
SNDRV_PCM_FORMAT_MPEG, /* HPI_FORMAT_MPEG_L3 5 */
- -1, /* HPI_FORMAT_DOLBY_AC2 6 */
- -1, /* HPI_FORMAT_DOLBY_AC3 7 */
+ INVALID_FORMAT, /* HPI_FORMAT_DOLBY_AC2 6 */
+ INVALID_FORMAT, /* HPI_FORMAT_DOLBY_AC3 7 */
SNDRV_PCM_FORMAT_S16_BE,/* HPI_FORMAT_PCM16_BIGENDIAN 8 */
- -1, /* HPI_FORMAT_AA_TAGIT1_HITS 9 */
- -1, /* HPI_FORMAT_AA_TAGIT1_INSERTS 10 */
+ INVALID_FORMAT, /* HPI_FORMAT_AA_TAGIT1_HITS 9 */
+ INVALID_FORMAT, /* HPI_FORMAT_AA_TAGIT1_INSERTS 10 */
SNDRV_PCM_FORMAT_S32, /* HPI_FORMAT_PCM32_SIGNED 11 */
- -1, /* HPI_FORMAT_RAW_BITSTREAM 12 */
- -1, /* HPI_FORMAT_AA_TAGIT1_HITS_EX1 13 */
+ INVALID_FORMAT, /* HPI_FORMAT_RAW_BITSTREAM 12 */
+ INVALID_FORMAT, /* HPI_FORMAT_AA_TAGIT1_HITS_EX1 13 */
SNDRV_PCM_FORMAT_FLOAT, /* HPI_FORMAT_PCM32_FLOAT 14 */
#if 1
/* ALSA can't handle 3 byte sample size together with power-of-2
* constraint on buffer_bytes, so disable this format
*/
- -1
+ INVALID_FORMAT
#else
- /* SNDRV_PCM_FORMAT_S24_3LE */ /* { HPI_FORMAT_PCM24_SIGNED 15 */
+ /* SNDRV_PCM_FORMAT_S24_3LE */ /* HPI_FORMAT_PCM24_SIGNED 15 */
#endif
};
-static int snd_card_asihpi_format_alsa2hpi(snd_pcm_format_t alsa_format,
+static int snd_card_asihpi_format_alsa2hpi(struct snd_card_asihpi *asihpi,
+ snd_pcm_format_t alsa_format,
u16 *hpi_format)
{
u16 format;
@@ -353,8 +323,8 @@ static int snd_card_asihpi_format_alsa2hpi(snd_pcm_format_t alsa_format,
}
}
- snd_printd(KERN_WARNING "failed match for alsa format %d\n",
- alsa_format);
+ dev_dbg(asihpi->card->dev, "failed match for alsa format %d\n",
+ alsa_format);
*hpi_format = 0;
return -EINVAL;
}
@@ -378,21 +348,21 @@ static void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi,
} else {
/* on cards without SRC,
valid rates are determined by sampleclock */
- err = hpi_mixer_get_control(ss, asihpi->h_mixer,
+ err = hpi_mixer_get_control(asihpi->h_mixer,
HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
HPI_CONTROL_SAMPLECLOCK, &h_control);
if (err) {
- snd_printk(KERN_ERR
- "no local sampleclock, err %d\n", err);
+ dev_err(&asihpi->pci->dev,
+ "No local sampleclock, err %d\n", err);
}
- for (idx = 0; idx < 100; idx++) {
- if (hpi_sample_clock_query_local_rate(ss,
- h_control, idx, &sample_rate)) {
- if (!idx)
- snd_printk(KERN_ERR
- "local rate query failed\n");
-
+ for (idx = -1; idx < 100; idx++) {
+ if (idx == -1) {
+ if (hpi_sample_clock_get_sample_rate(h_control,
+ &sample_rate))
+ continue;
+ } else if (hpi_sample_clock_query_local_rate(h_control,
+ idx, &sample_rate)) {
break;
}
@@ -445,8 +415,6 @@ static void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi,
}
}
- /* printk(KERN_INFO "Supported rates %X %d %d\n",
- rates, rate_min, rate_max); */
pcmhw->rates = rates;
pcmhw->rate_min = rate_min;
pcmhw->rate_max = rate_max;
@@ -463,54 +431,41 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
int width;
unsigned int bytes_per_sec;
- print_hwparams(params);
- err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
- if (err < 0)
- return err;
- err = snd_card_asihpi_format_alsa2hpi(params_format(params), &format);
+ print_hwparams(substream, params);
+ err = snd_card_asihpi_format_alsa2hpi(card, params_format(params), &format);
if (err)
return err;
- VPRINTK1(KERN_INFO "format %d, %d chans, %d_hz\n",
- format, params_channels(params),
- params_rate(params));
-
hpi_handle_error(hpi_format_create(&dpcm->format,
params_channels(params),
format, params_rate(params), 0, 0));
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- if (hpi_instream_reset(ss, dpcm->h_stream) != 0)
+ if (hpi_instream_reset(dpcm->h_stream) != 0)
return -EINVAL;
- if (hpi_instream_set_format(ss,
+ if (hpi_instream_set_format(
dpcm->h_stream, &dpcm->format) != 0)
return -EINVAL;
}
dpcm->hpi_buffer_attached = 0;
- if (card->support_mmap) {
-
- err = hpi_stream_host_buffer_attach(ss, dpcm->h_stream,
+ if (card->can_dma) {
+ err = hpi_stream_host_buffer_attach(dpcm->h_stream,
params_buffer_bytes(params), runtime->dma_addr);
if (err == 0) {
- snd_printd(KERN_INFO
- "stream_host_buffer_attach succeeded %u %lu\n",
+ dev_dbg(card->card->dev,
+ "stream_host_buffer_attach success %u %lu\n",
params_buffer_bytes(params),
(unsigned long)runtime->dma_addr);
} else {
- snd_printd(KERN_INFO
- "stream_host_buffer_attach error %d\n",
- err);
+ dev_dbg(card->card->dev,
+ "stream_host_buffer_attach error %d\n", err);
return -ENOMEM;
}
- err = hpi_stream_get_info_ex(ss, dpcm->h_stream, NULL,
- &dpcm->hpi_buffer_attached,
- NULL, NULL, NULL);
-
- snd_printd(KERN_INFO "stream_host_buffer_attach status 0x%x\n",
- dpcm->hpi_buffer_attached);
+ hpi_stream_get_info_ex(dpcm->h_stream, NULL,
+ &dpcm->hpi_buffer_attached, NULL, NULL, NULL);
}
bytes_per_sec = params_rate(params) * params_channels(params);
width = snd_pcm_format_width(params_format(params));
@@ -520,16 +475,29 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
dpcm->bytes_per_sec = bytes_per_sec;
- dpcm->pcm_size = params_buffer_bytes(params);
- dpcm->pcm_count = params_period_bytes(params);
- snd_printd(KERN_INFO "pcm_size=%d, pcm_count=%d, bps=%d\n",
- dpcm->pcm_size, dpcm->pcm_count, bytes_per_sec);
+ dpcm->buffer_bytes = params_buffer_bytes(params);
+ dpcm->period_bytes = params_period_bytes(params);
- dpcm->pcm_irq_pos = 0;
- dpcm->pcm_buf_pos = 0;
return 0;
}
+static int
+snd_card_asihpi_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
+ if (dpcm->hpi_buffer_attached)
+ hpi_stream_host_buffer_detach(dpcm->h_stream);
+
+ return 0;
+}
+
+static void snd_card_asihpi_runtime_free(struct snd_pcm_runtime *runtime)
+{
+ struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
+ kfree(dpcm);
+}
+
static void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream *
substream)
{
@@ -537,12 +505,11 @@ static void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream *
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
int expiry;
- expiry = (dpcm->pcm_count * HZ / dpcm->bytes_per_sec);
- /* wait longer the first time, for samples to propagate */
- expiry = max(expiry, 20);
- dpcm->timer.expires = jiffies + expiry;
+ expiry = HZ / 200;
+
+ expiry = max(expiry, 1); /* don't let it be zero! */
+ mod_timer(&dpcm->timer, jiffies + expiry);
dpcm->respawn_timer = 1;
- add_timer(&dpcm->timer);
}
static void snd_card_asihpi_pcm_timer_stop(struct snd_pcm_substream *substream)
@@ -551,7 +518,35 @@ static void snd_card_asihpi_pcm_timer_stop(struct snd_pcm_substream *substream)
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
dpcm->respawn_timer = 0;
- del_timer(&dpcm->timer);
+ timer_delete(&dpcm->timer);
+}
+
+static void snd_card_asihpi_pcm_int_start(struct snd_pcm_substream *substream)
+{
+ struct snd_card_asihpi_pcm *dpcm;
+ struct snd_card_asihpi *card;
+
+ dpcm = (struct snd_card_asihpi_pcm *)substream->runtime->private_data;
+ card = snd_pcm_substream_chip(substream);
+
+ WARN_ON(in_interrupt());
+ card->llmode_streampriv = dpcm;
+
+ hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index,
+ HPI_ADAPTER_PROPERTY_IRQ_RATE,
+ card->update_interval_frames, 0));
+}
+
+static void snd_card_asihpi_pcm_int_stop(struct snd_pcm_substream *substream)
+{
+ struct snd_card_asihpi *card;
+
+ card = snd_pcm_substream_chip(substream);
+
+ hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index,
+ HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0));
+
+ card->llmode_streampriv = NULL;
}
static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
@@ -561,38 +556,45 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
struct snd_pcm_substream *s;
u16 e;
+ char name[16];
+
+ snd_pcm_debug_name(substream, name, sizeof(name));
- snd_printd("trigger %dstream %d\n",
- substream->stream, substream->number);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
+ dev_dbg(card->card->dev, "%s trigger start\n", name);
snd_pcm_group_for_each_entry(s, substream) {
- struct snd_card_asihpi_pcm *ds;
- ds = s->runtime->private_data;
+ struct snd_pcm_runtime *runtime = s->runtime;
+ struct snd_card_asihpi_pcm *ds = runtime->private_data;
if (snd_pcm_substream_chip(s) != card)
continue;
- if ((s->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
- (card->support_mmap)) {
+ /* don't link Cap and Play */
+ if (substream->stream != s->stream)
+ continue;
+
+ ds->drained_count = 0;
+ if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
/* How do I know how much valid data is present
- * in buffer? Just guessing 2 periods, but if
+ * in buffer? Must be at least one period!
+ * Guessing 2 periods, but if
* buffer is bigger it may contain even more
* data??
*/
- unsigned int preload = ds->pcm_count * 2;
- VPRINTK2("preload %d\n", preload);
+ unsigned int preload = ds->period_bytes * 1;
+ asihpi_dbg("%d preload %d\n", s->number, preload);
hpi_handle_error(hpi_outstream_write_buf(
- ss, ds->h_stream,
- &s->runtime->dma_area[0],
+ ds->h_stream,
+ &runtime->dma_area[0],
preload,
&ds->format));
+ ds->pcm_buf_host_rw_ofs = preload;
}
if (card->support_grouping) {
- VPRINTK1("\t_group %dstream %d\n", s->stream,
- s->number);
- e = hpi_stream_group_add(ss,
+ dev_dbg(card->card->dev, "%d group\n", s->number);
+ e = hpi_stream_group_add(
dpcm->h_stream,
ds->h_stream);
if (!e) {
@@ -604,99 +606,83 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream,
} else
break;
}
- snd_printd("start\n");
/* start the master stream */
- snd_card_asihpi_pcm_timer_start(substream);
- hpi_handle_error(hpi_stream_start(ss, dpcm->h_stream));
+ card->pcm_start(substream);
+ if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ||
+ !card->can_dma)
+ hpi_handle_error(hpi_stream_start(dpcm->h_stream));
break;
case SNDRV_PCM_TRIGGER_STOP:
- snd_card_asihpi_pcm_timer_stop(substream);
+ dev_dbg(card->card->dev, "%s trigger stop\n", name);
+ card->pcm_stop(substream);
snd_pcm_group_for_each_entry(s, substream) {
if (snd_pcm_substream_chip(s) != card)
continue;
+ /* don't link Cap and Play */
+ if (substream->stream != s->stream)
+ continue;
/*? workaround linked streams don't
transition to SETUP 20070706*/
- s->runtime->status->state = SNDRV_PCM_STATE_SETUP;
+ __snd_pcm_set_state(s->runtime, SNDRV_PCM_STATE_SETUP);
if (card->support_grouping) {
- VPRINTK1("\t_group %dstream %d\n", s->stream,
- s->number);
+ dev_dbg(card->card->dev, "%d group\n", s->number);
snd_pcm_trigger_done(s, substream);
} else
break;
}
- snd_printd("stop\n");
/* _prepare and _hwparams reset the stream */
- hpi_handle_error(hpi_stream_stop(ss, dpcm->h_stream));
+ hpi_handle_error(hpi_stream_stop(dpcm->h_stream));
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
hpi_handle_error(
- hpi_outstream_reset(ss, dpcm->h_stream));
+ hpi_outstream_reset(dpcm->h_stream));
if (card->support_grouping)
- hpi_handle_error(hpi_stream_group_reset(ss,
- dpcm->h_stream));
+ hpi_handle_error(hpi_stream_group_reset(dpcm->h_stream));
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- snd_printd("pause release\n");
- hpi_handle_error(hpi_stream_start(ss, dpcm->h_stream));
- snd_card_asihpi_pcm_timer_start(substream);
+ dev_dbg(card->card->dev, "%s trigger pause release\n", name);
+ card->pcm_start(substream);
+ hpi_handle_error(hpi_stream_start(dpcm->h_stream));
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- snd_printd("pause\n");
- snd_card_asihpi_pcm_timer_stop(substream);
- hpi_handle_error(hpi_stream_stop(ss, dpcm->h_stream));
+ dev_dbg(card->card->dev, "%s trigger pause push\n", name);
+ card->pcm_stop(substream);
+ hpi_handle_error(hpi_stream_stop(dpcm->h_stream));
break;
default:
- snd_printd("\tINVALID\n");
+ dev_dbg(card->card->dev, "\tINVALID\n");
return -EINVAL;
}
return 0;
}
-static int
-snd_card_asihpi_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- if (dpcm->hpi_buffer_attached)
- hpi_stream_host_buffer_detach(ss, dpcm->h_stream);
-
- snd_pcm_lib_free_pages(substream);
- return 0;
-}
-
-static void snd_card_asihpi_runtime_free(struct snd_pcm_runtime *runtime)
-{
- struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- kfree(dpcm);
-}
-
/*algorithm outline
Without linking degenerates to getting single stream pos etc
Without mmap 2nd loop degenerates to snd_pcm_period_elapsed
*/
/*
-buf_pos=get_buf_pos(s);
+pcm_buf_dma_ofs=get_buf_pos(s);
for_each_linked_stream(s) {
- buf_pos=get_buf_pos(s);
- min_buf_pos = modulo_min(min_buf_pos, buf_pos, pcm_size)
- new_data = min(new_data, calc_new_data(buf_pos,irq_pos)
+ pcm_buf_dma_ofs=get_buf_pos(s);
+ min_buf_pos = modulo_min(min_buf_pos, pcm_buf_dma_ofs, buffer_bytes)
+ new_data = min(new_data, calc_new_data(pcm_buf_dma_ofs,irq_pos)
}
timer.expires = jiffies + predict_next_period_ready(min_buf_pos);
for_each_linked_stream(s) {
- s->buf_pos = min_buf_pos;
- if (new_data > pcm_count) {
+ s->pcm_buf_dma_ofs = min_buf_pos;
+ if (new_data > period_bytes) {
if (mmap) {
- irq_pos = (irq_pos + pcm_count) % pcm_size;
+ irq_pos = (irq_pos + period_bytes) % buffer_bytes;
if (playback) {
- write(pcm_count);
+ write(period_bytes);
} else {
- read(pcm_count);
+ read(period_bytes);
}
}
snd_pcm_period_elapsed(s);
@@ -721,136 +707,216 @@ static inline unsigned int modulo_min(unsigned int a, unsigned int b,
/** Timer function, equivalent to interrupt service routine for cards
*/
-static void snd_card_asihpi_timer_function(unsigned long data)
+static void snd_card_asihpi_timer_function(struct timer_list *t)
{
- struct snd_card_asihpi_pcm *dpcm = (struct snd_card_asihpi_pcm *)data;
- struct snd_card_asihpi *card = snd_pcm_substream_chip(dpcm->substream);
+ struct snd_card_asihpi_pcm *dpcm = timer_container_of(dpcm, t, timer);
+ struct snd_pcm_substream *substream = dpcm->substream;
+ struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime;
struct snd_pcm_substream *s;
unsigned int newdata = 0;
- unsigned int buf_pos, min_buf_pos = 0;
+ unsigned int pcm_buf_dma_ofs, min_buf_pos = 0;
unsigned int remdata, xfercount, next_jiffies;
int first = 1;
u16 state;
- u32 buffer_size, data_avail, samples_played, aux;
+ u32 buffer_size, bytes_avail, samples_played, on_card_bytes;
+ char name[16];
+
+
+ snd_pcm_debug_name(substream, name, sizeof(name));
/* find minimum newdata and buffer pos in group */
- snd_pcm_group_for_each_entry(s, dpcm->substream) {
+ snd_pcm_group_for_each_entry(s, substream) {
struct snd_card_asihpi_pcm *ds = s->runtime->private_data;
runtime = s->runtime;
if (snd_pcm_substream_chip(s) != card)
continue;
- hpi_handle_error(hpi_stream_get_info_ex(ss,
+ /* don't link Cap and Play */
+ if (substream->stream != s->stream)
+ continue;
+
+ hpi_handle_error(hpi_stream_get_info_ex(
ds->h_stream, &state,
- &buffer_size, &data_avail,
- &samples_played, &aux));
+ &buffer_size, &bytes_avail,
+ &samples_played, &on_card_bytes));
/* number of bytes in on-card buffer */
- runtime->delay = aux;
+ runtime->delay = on_card_bytes;
- if (state == HPI_STATE_DRAINED) {
- snd_printd(KERN_WARNING "outstream %d drained\n",
- s->number);
- snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN);
- return;
- }
+ if (!card->can_dma)
+ on_card_bytes = bytes_avail;
if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- buf_pos = frames_to_bytes(runtime, samples_played);
- } else {
- buf_pos = data_avail + ds->pcm_irq_pos;
- }
+ pcm_buf_dma_ofs = ds->pcm_buf_host_rw_ofs - bytes_avail;
+ if (state == HPI_STATE_STOPPED) {
+ if (bytes_avail == 0) {
+ hpi_handle_error(hpi_stream_start(ds->h_stream));
+ dev_dbg(card->card->dev,
+ "P%d start\n", s->number);
+ ds->drained_count = 0;
+ }
+ } else if (state == HPI_STATE_DRAINED) {
+ dev_dbg(card->card->dev,
+ "P%d drained\n", s->number);
+ ds->drained_count++;
+ if (ds->drained_count > 20) {
+ snd_pcm_stop_xrun(s);
+ continue;
+ }
+ } else {
+ ds->drained_count = 0;
+ }
+ } else
+ pcm_buf_dma_ofs = bytes_avail + ds->pcm_buf_host_rw_ofs;
if (first) {
/* can't statically init min when wrap is involved */
- min_buf_pos = buf_pos;
- newdata = (buf_pos - ds->pcm_irq_pos) % ds->pcm_size;
+ min_buf_pos = pcm_buf_dma_ofs;
+ newdata = (pcm_buf_dma_ofs - ds->pcm_buf_elapsed_dma_ofs) % ds->buffer_bytes;
first = 0;
} else {
min_buf_pos =
- modulo_min(min_buf_pos, buf_pos, UINT_MAX+1L);
+ modulo_min(min_buf_pos, pcm_buf_dma_ofs, UINT_MAX+1L);
newdata = min(
- (buf_pos - ds->pcm_irq_pos) % ds->pcm_size,
+ (pcm_buf_dma_ofs - ds->pcm_buf_elapsed_dma_ofs) % ds->buffer_bytes,
newdata);
}
- VPRINTK1("PB timer hw_ptr x%04lX, appl_ptr x%04lX\n",
+ asihpi_dbg(
+ "timer1, %s, %d, S=%d, elap=%d, rw=%d, dsp=%d, left=%d, aux=%d, space=%d, hw_ptr=%ld, appl_ptr=%ld\n",
+ name, s->number, state,
+ ds->pcm_buf_elapsed_dma_ofs,
+ ds->pcm_buf_host_rw_ofs,
+ pcm_buf_dma_ofs,
+ (int)bytes_avail,
+
+ (int)on_card_bytes,
+ buffer_size-bytes_avail,
(unsigned long)frames_to_bytes(runtime,
runtime->status->hw_ptr),
(unsigned long)frames_to_bytes(runtime,
- runtime->control->appl_ptr));
- VPRINTK1("%d S=%d, irq=%04X, pos=x%04X, left=x%04X,"
- " aux=x%04X space=x%04X\n", s->number,
- state, ds->pcm_irq_pos, buf_pos, (int)data_avail,
- (int)aux, buffer_size-data_avail);
+ runtime->control->appl_ptr)
+ );
}
+ pcm_buf_dma_ofs = min_buf_pos;
- remdata = newdata % dpcm->pcm_count;
- xfercount = newdata - remdata; /* a multiple of pcm_count */
- next_jiffies = ((dpcm->pcm_count-remdata) * HZ / dpcm->bytes_per_sec)+1;
- next_jiffies = max(next_jiffies, 2U * HZ / 1000U);
+ remdata = newdata % dpcm->period_bytes;
+ xfercount = newdata - remdata; /* a multiple of period_bytes */
+ /* come back when on_card_bytes has decreased enough to allow
+ write to happen, or when data has been consumed to make another
+ period
+ */
+ if (xfercount && (on_card_bytes > dpcm->period_bytes))
+ next_jiffies = ((on_card_bytes - dpcm->period_bytes) * HZ / dpcm->bytes_per_sec);
+ else
+ next_jiffies = ((dpcm->period_bytes - remdata) * HZ / dpcm->bytes_per_sec);
+
+ next_jiffies = max(next_jiffies, 1U);
dpcm->timer.expires = jiffies + next_jiffies;
- VPRINTK1("jif %d buf pos x%04X newdata x%04X xc x%04X\n",
- next_jiffies, min_buf_pos, newdata, xfercount);
+ asihpi_dbg("timer2, jif=%d, buf_pos=%d, newdata=%d, xfer=%d\n",
+ next_jiffies, pcm_buf_dma_ofs, newdata, xfercount);
- snd_pcm_group_for_each_entry(s, dpcm->substream) {
+ snd_pcm_group_for_each_entry(s, substream) {
struct snd_card_asihpi_pcm *ds = s->runtime->private_data;
- ds->pcm_buf_pos = min_buf_pos;
- if (xfercount) {
- if (card->support_mmap) {
- ds->pcm_irq_pos = ds->pcm_irq_pos + xfercount;
- if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- VPRINTK2("write OS%d x%04x\n",
+ /* don't link Cap and Play */
+ if (substream->stream != s->stream)
+ continue;
+
+ /* Store dma offset for use by pointer callback */
+ ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs;
+
+ if (xfercount &&
+ /* Limit use of on card fifo for playback */
+ ((on_card_bytes <= ds->period_bytes) ||
+ (s->stream == SNDRV_PCM_STREAM_CAPTURE)))
+
+ {
+
+ unsigned int buf_ofs = ds->pcm_buf_host_rw_ofs % ds->buffer_bytes;
+ unsigned int xfer1, xfer2;
+ char *pd = &s->runtime->dma_area[buf_ofs];
+
+ if (card->can_dma) { /* buffer wrap is handled at lower level */
+ xfer1 = xfercount;
+ xfer2 = 0;
+ } else {
+ xfer1 = min(xfercount, ds->buffer_bytes - buf_ofs);
+ xfer2 = xfercount - xfer1;
+ }
+
+ if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ asihpi_dbg("write1, P=%d, xfer=%d, buf_ofs=%d\n",
+ s->number, xfer1, buf_ofs);
+ hpi_handle_error(
+ hpi_outstream_write_buf(
+ ds->h_stream, pd, xfer1,
+ &ds->format));
+
+ if (xfer2) {
+ pd = s->runtime->dma_area;
+
+ asihpi_dbg("write2, P=%d, xfer=%d, buf_ofs=%d\n",
s->number,
- ds->pcm_count);
+ xfercount - xfer1, buf_ofs);
hpi_handle_error(
hpi_outstream_write_buf(
- ss, ds->h_stream,
- &s->runtime->
- dma_area[0],
- xfercount,
+ ds->h_stream, pd,
+ xfercount - xfer1,
&ds->format));
- } else {
- VPRINTK2("read IS%d x%04x\n",
- s->number,
- dpcm->pcm_count);
+ }
+ } else {
+ asihpi_dbg("read1, C=%d, xfer=%d\n",
+ s->number, xfer1);
+ hpi_handle_error(
+ hpi_instream_read_buf(
+ ds->h_stream,
+ pd, xfer1));
+ if (xfer2) {
+ pd = s->runtime->dma_area;
+ asihpi_dbg("read2, C=%d, xfer=%d\n",
+ s->number, xfer2);
hpi_handle_error(
hpi_instream_read_buf(
- ss, ds->h_stream,
- NULL, xfercount));
+ ds->h_stream,
+ pd, xfer2));
}
- } /* else R/W will be handled by read/write callbacks */
+ }
+ /* ? host_rw_ofs always ahead of elapsed_dma_ofs by preload size? */
+ ds->pcm_buf_host_rw_ofs += xfercount;
+ ds->pcm_buf_elapsed_dma_ofs += xfercount;
snd_pcm_period_elapsed(s);
}
}
- if (dpcm->respawn_timer)
+ if (!card->hpi->interrupt_mode && dpcm->respawn_timer)
add_timer(&dpcm->timer);
}
-/***************************** PLAYBACK OPS ****************/
-static int snd_card_asihpi_playback_ioctl(struct snd_pcm_substream *substream,
- unsigned int cmd, void *arg)
+static void snd_card_asihpi_isr(struct hpi_adapter *a)
{
- /* snd_printd(KERN_INFO "Playback ioctl %d\n", cmd); */
- return snd_pcm_lib_ioctl(substream, cmd, arg);
+ struct snd_card_asihpi *asihpi;
+
+ WARN_ON(!a || !a->snd_card || !a->snd_card->private_data);
+ asihpi = (struct snd_card_asihpi *)a->snd_card->private_data;
+ if (asihpi->llmode_streampriv)
+ snd_card_asihpi_timer_function(
+ &asihpi->llmode_streampriv->timer);
}
+/***************************** PLAYBACK OPS ****************/
static int snd_card_asihpi_playback_prepare(struct snd_pcm_substream *
substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- snd_printd(KERN_INFO "playback prepare %d\n", substream->number);
-
- hpi_handle_error(hpi_outstream_reset(ss, dpcm->h_stream));
- dpcm->pcm_irq_pos = 0;
- dpcm->pcm_buf_pos = 0;
-
+ hpi_handle_error(hpi_outstream_reset(dpcm->h_stream));
+ dpcm->pcm_buf_host_rw_ofs = 0;
+ dpcm->pcm_buf_dma_ofs = 0;
+ dpcm->pcm_buf_elapsed_dma_ofs = 0;
return 0;
}
@@ -860,116 +926,101 @@ snd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
snd_pcm_uframes_t ptr;
+ char name[16];
+ snd_pcm_debug_name(substream, name, sizeof(name));
- u32 samples_played;
- u16 err;
-
- if (!snd_pcm_stream_linked(substream)) {
- /* NOTE, can use samples played for playback position here and
- * in timer fn because it LAGS the actual read pointer, and is a
- * better representation of actual playout position
- */
- err = hpi_outstream_get_info_ex(ss, dpcm->h_stream, NULL,
- NULL, NULL,
- &samples_played, NULL);
- hpi_handle_error(err);
-
- dpcm->pcm_buf_pos = frames_to_bytes(runtime, samples_played);
- }
- /* else must return most conservative value found in timer func
- * by looping over all streams
- */
-
- ptr = bytes_to_frames(runtime, dpcm->pcm_buf_pos % dpcm->pcm_size);
- VPRINTK2("playback_pointer=%04ld\n", (unsigned long)ptr);
+ ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes);
+ asihpi_dbg("%s, pointer=%ld\n", name, (unsigned long)ptr);
return ptr;
}
-static void snd_card_asihpi_playback_format(struct snd_card_asihpi *asihpi,
- u32 h_stream,
- struct snd_pcm_hardware *pcmhw)
+static u64 snd_card_asihpi_playback_formats(struct snd_card_asihpi *asihpi,
+ u32 h_stream)
{
struct hpi_format hpi_format;
u16 format;
u16 err;
u32 h_control;
u32 sample_rate = 48000;
+ u64 formats = 0;
/* on cards without SRC, must query at valid rate,
* maybe set by external sync
*/
- err = hpi_mixer_get_control(ss, asihpi->h_mixer,
+ err = hpi_mixer_get_control(asihpi->h_mixer,
HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
HPI_CONTROL_SAMPLECLOCK, &h_control);
if (!err)
- err = hpi_sample_clock_get_sample_rate(ss, h_control,
+ err = hpi_sample_clock_get_sample_rate(h_control,
&sample_rate);
for (format = HPI_FORMAT_PCM8_UNSIGNED;
format <= HPI_FORMAT_PCM24_SIGNED; format++) {
- err = hpi_format_create(&hpi_format,
- 2, format, sample_rate, 128000, 0);
+ err = hpi_format_create(&hpi_format, asihpi->out_max_chans,
+ format, sample_rate, 128000, 0);
if (!err)
- err = hpi_outstream_query_format(ss, h_stream,
- &hpi_format);
- if (!err && (hpi_to_alsa_formats[format] != -1))
- pcmhw->formats |=
- (1ULL << hpi_to_alsa_formats[format]);
+ err = hpi_outstream_query_format(h_stream, &hpi_format);
+ if (!err && (hpi_to_alsa_formats[format] != INVALID_FORMAT))
+ formats |= pcm_format_to_bits(hpi_to_alsa_formats[format]);
}
+ return formats;
}
-static struct snd_pcm_hardware snd_card_asihpi_playback = {
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = BUFFER_BYTES_MAX,
- .period_bytes_min = PERIOD_BYTES_MIN,
- .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
- .periods_min = PERIODS_MIN,
- .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
- .fifo_size = 0,
-};
-
static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm;
struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
+ struct snd_pcm_hardware snd_card_asihpi_playback;
int err;
dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
if (dpcm == NULL)
return -ENOMEM;
- err =
- hpi_outstream_open(ss, card->adapter_index,
+ err = hpi_outstream_open(card->hpi->adapter->index,
substream->number, &dpcm->h_stream);
hpi_handle_error(err);
- if (err)
+ if (err) {
kfree(dpcm);
- if (err == HPI_ERROR_OBJ_ALREADY_OPEN)
- return -EBUSY;
- if (err)
+ if (err == HPI_ERROR_OBJ_ALREADY_OPEN)
+ return -EBUSY;
return -EIO;
+ }
/*? also check ASI5000 samplerate source
If external, only support external rate.
- If internal and other stream playing, cant switch
+ If internal and other stream playing, can't switch
*/
- init_timer(&dpcm->timer);
- dpcm->timer.data = (unsigned long) dpcm;
- dpcm->timer.function = snd_card_asihpi_timer_function;
+ timer_setup(&dpcm->timer, snd_card_asihpi_timer_function, 0);
dpcm->substream = substream;
runtime->private_data = dpcm;
runtime->private_free = snd_card_asihpi_runtime_free;
- snd_card_asihpi_playback.channels_max = card->out_max_chans;
- /*?snd_card_asihpi_playback.period_bytes_min =
- card->out_max_chans * 4096; */
+ memset(&snd_card_asihpi_playback, 0, sizeof(snd_card_asihpi_playback));
+ if (!card->hpi->interrupt_mode) {
+ snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX;
+ snd_card_asihpi_playback.period_bytes_min = PERIOD_BYTES_MIN;
+ snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
+ snd_card_asihpi_playback.periods_min = PERIODS_MIN;
+ snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN;
+ } else {
+ size_t pbmin = card->update_interval_frames *
+ card->out_max_chans;
+ snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX;
+ snd_card_asihpi_playback.period_bytes_min = pbmin;
+ snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
+ snd_card_asihpi_playback.periods_min = PERIODS_MIN;
+ snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / pbmin;
+ }
- snd_card_asihpi_playback_format(card, dpcm->h_stream,
- &snd_card_asihpi_playback);
+ /* snd_card_asihpi_playback.fifo_size = 0; */
+ snd_card_asihpi_playback.channels_max = card->out_max_chans;
+ snd_card_asihpi_playback.channels_min = card->out_min_chans;
+ snd_card_asihpi_playback.formats =
+ snd_card_asihpi_playback_formats(card, dpcm->h_stream);
snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_playback);
@@ -977,19 +1028,19 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
SNDRV_PCM_INFO_DOUBLE |
SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_PAUSE;
-
- if (card->support_mmap)
- snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID;
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID;
- if (card->support_grouping)
+ if (card->support_grouping) {
snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_SYNC_START;
+ snd_pcm_set_sync(substream);
+ }
/* struct is copied, so can create initializer dynamically */
runtime->hw = snd_card_asihpi_playback;
- if (card->support_mmap)
+ if (card->can_dma)
err = snd_pcm_hw_constraint_pow2(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
if (err < 0)
@@ -997,12 +1048,9 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream)
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
card->update_interval_frames);
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- card->update_interval_frames * 4, UINT_MAX);
- snd_pcm_set_sync(substream);
-
- snd_printd(KERN_INFO "playback open\n");
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ card->update_interval_frames, UINT_MAX);
return 0;
}
@@ -1012,71 +1060,13 @@ static int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- hpi_handle_error(hpi_outstream_close(ss, dpcm->h_stream));
- snd_printd(KERN_INFO "playback close\n");
-
- return 0;
-}
-
-static int snd_card_asihpi_playback_copy(struct snd_pcm_substream *substream,
- int channel,
- snd_pcm_uframes_t pos,
- void __user *src,
- snd_pcm_uframes_t count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- unsigned int len;
-
- len = frames_to_bytes(runtime, count);
-
- if (copy_from_user(runtime->dma_area, src, len))
- return -EFAULT;
-
- VPRINTK2(KERN_DEBUG "playback copy%d %u bytes\n",
- substream->number, len);
-
- hpi_handle_error(hpi_outstream_write_buf(ss, dpcm->h_stream,
- runtime->dma_area, len, &dpcm->format));
-
+ hpi_handle_error(hpi_outstream_close(dpcm->h_stream));
return 0;
}
-static int snd_card_asihpi_playback_silence(struct snd_pcm_substream *
- substream, int channel,
- snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
-{
- unsigned int len;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
-
- len = frames_to_bytes(runtime, count);
- snd_printd(KERN_INFO "playback silence %u bytes\n", len);
-
- memset(runtime->dma_area, 0, len);
- hpi_handle_error(hpi_outstream_write_buf(ss, dpcm->h_stream,
- runtime->dma_area, len, &dpcm->format));
- return 0;
-}
-
-static struct snd_pcm_ops snd_card_asihpi_playback_ops = {
+static const struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = {
.open = snd_card_asihpi_playback_open,
.close = snd_card_asihpi_playback_close,
- .ioctl = snd_card_asihpi_playback_ioctl,
- .hw_params = snd_card_asihpi_pcm_hw_params,
- .hw_free = snd_card_asihpi_hw_free,
- .prepare = snd_card_asihpi_playback_prepare,
- .trigger = snd_card_asihpi_trigger,
- .pointer = snd_card_asihpi_playback_pointer,
- .copy = snd_card_asihpi_playback_copy,
- .silence = snd_card_asihpi_playback_silence,
-};
-
-static struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = {
- .open = snd_card_asihpi_playback_open,
- .close = snd_card_asihpi_playback_close,
- .ioctl = snd_card_asihpi_playback_ioctl,
.hw_params = snd_card_asihpi_pcm_hw_params,
.hw_free = snd_card_asihpi_hw_free,
.prepare = snd_card_asihpi_playback_prepare,
@@ -1090,20 +1080,15 @@ snd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
+ char name[16];
+ snd_pcm_debug_name(substream, name, sizeof(name));
- VPRINTK2("capture pointer %d=%d\n",
- substream->number, dpcm->pcm_buf_pos);
- /* NOTE Unlike playback can't use actual dwSamplesPlayed
+ asihpi_dbg("%s, pointer=%d\n", name, dpcm->pcm_buf_dma_ofs);
+ /* NOTE Unlike playback can't use actual samples_played
for the capture position, because those samples aren't yet in
the local buffer available for reading.
*/
- return bytes_to_frames(runtime, dpcm->pcm_buf_pos % dpcm->pcm_size);
-}
-
-static int snd_card_asihpi_capture_ioctl(struct snd_pcm_substream *substream,
- unsigned int cmd, void *arg)
-{
- return snd_pcm_lib_ioctl(substream, cmd, arg);
+ return bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes);
}
static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream)
@@ -1111,107 +1096,110 @@ static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- hpi_handle_error(hpi_instream_reset(ss, dpcm->h_stream));
- dpcm->pcm_irq_pos = 0;
- dpcm->pcm_buf_pos = 0;
+ hpi_handle_error(hpi_instream_reset(dpcm->h_stream));
+ dpcm->pcm_buf_host_rw_ofs = 0;
+ dpcm->pcm_buf_dma_ofs = 0;
+ dpcm->pcm_buf_elapsed_dma_ofs = 0;
- snd_printd("capture prepare %d\n", substream->number);
return 0;
}
-
-
-static void snd_card_asihpi_capture_format(struct snd_card_asihpi *asihpi,
- u32 h_stream,
- struct snd_pcm_hardware *pcmhw)
+static u64 snd_card_asihpi_capture_formats(struct snd_card_asihpi *asihpi,
+ u32 h_stream)
{
- struct hpi_format hpi_format;
+ struct hpi_format hpi_format;
u16 format;
u16 err;
u32 h_control;
u32 sample_rate = 48000;
+ u64 formats = 0;
/* on cards without SRC, must query at valid rate,
maybe set by external sync */
- err = hpi_mixer_get_control(ss, asihpi->h_mixer,
+ err = hpi_mixer_get_control(asihpi->h_mixer,
HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
HPI_CONTROL_SAMPLECLOCK, &h_control);
if (!err)
- err = hpi_sample_clock_get_sample_rate(ss, h_control,
+ err = hpi_sample_clock_get_sample_rate(h_control,
&sample_rate);
for (format = HPI_FORMAT_PCM8_UNSIGNED;
format <= HPI_FORMAT_PCM24_SIGNED; format++) {
- err = hpi_format_create(&hpi_format, 2, format,
- sample_rate, 128000, 0);
+ err = hpi_format_create(&hpi_format, asihpi->in_max_chans,
+ format, sample_rate, 128000, 0);
if (!err)
- err = hpi_instream_query_format(ss, h_stream,
- &hpi_format);
- if (!err)
- pcmhw->formats |=
- (1ULL << hpi_to_alsa_formats[format]);
+ err = hpi_instream_query_format(h_stream, &hpi_format);
+ if (!err && (hpi_to_alsa_formats[format] != INVALID_FORMAT))
+ formats |= pcm_format_to_bits(hpi_to_alsa_formats[format]);
}
+ return formats;
}
-
-static struct snd_pcm_hardware snd_card_asihpi_capture = {
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = BUFFER_BYTES_MAX,
- .period_bytes_min = PERIOD_BYTES_MIN,
- .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
- .periods_min = PERIODS_MIN,
- .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
- .fifo_size = 0,
-};
-
static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_card_asihpi *card = snd_pcm_substream_chip(substream);
struct snd_card_asihpi_pcm *dpcm;
+ struct snd_pcm_hardware snd_card_asihpi_capture;
int err;
dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
if (dpcm == NULL)
return -ENOMEM;
- snd_printd("hpi_instream_open adapter %d stream %d\n",
- card->adapter_index, substream->number);
+
+ dev_dbg(card->card->dev, "capture open adapter %d stream %d\n",
+ card->hpi->adapter->index, substream->number);
err = hpi_handle_error(
- hpi_instream_open(ss, card->adapter_index,
+ hpi_instream_open(card->hpi->adapter->index,
substream->number, &dpcm->h_stream));
- if (err)
+ if (err) {
kfree(dpcm);
- if (err == HPI_ERROR_OBJ_ALREADY_OPEN)
- return -EBUSY;
- if (err)
+ if (err == HPI_ERROR_OBJ_ALREADY_OPEN)
+ return -EBUSY;
return -EIO;
+ }
-
- init_timer(&dpcm->timer);
- dpcm->timer.data = (unsigned long) dpcm;
- dpcm->timer.function = snd_card_asihpi_timer_function;
+ timer_setup(&dpcm->timer, snd_card_asihpi_timer_function, 0);
dpcm->substream = substream;
runtime->private_data = dpcm;
runtime->private_free = snd_card_asihpi_runtime_free;
+ memset(&snd_card_asihpi_capture, 0, sizeof(snd_card_asihpi_capture));
+ if (!card->hpi->interrupt_mode) {
+ snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX;
+ snd_card_asihpi_capture.period_bytes_min = PERIOD_BYTES_MIN;
+ snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
+ snd_card_asihpi_capture.periods_min = PERIODS_MIN;
+ snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN;
+ } else {
+ size_t pbmin = card->update_interval_frames *
+ card->out_max_chans;
+ snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX;
+ snd_card_asihpi_capture.period_bytes_min = pbmin;
+ snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN;
+ snd_card_asihpi_capture.periods_min = PERIODS_MIN;
+ snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / pbmin;
+ }
+ /* snd_card_asihpi_capture.fifo_size = 0; */
snd_card_asihpi_capture.channels_max = card->in_max_chans;
- snd_card_asihpi_capture_format(card, dpcm->h_stream,
- &snd_card_asihpi_capture);
+ snd_card_asihpi_capture.channels_min = card->in_min_chans;
+ snd_card_asihpi_capture.formats =
+ snd_card_asihpi_capture_formats(card, dpcm->h_stream);
snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_capture);
- snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED;
+ snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID;
- if (card->support_mmap)
- snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID;
+ if (card->support_grouping)
+ snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_SYNC_START;
runtime->hw = snd_card_asihpi_capture;
- if (card->support_mmap)
+ if (card->can_dma)
err = snd_pcm_hw_constraint_pow2(runtime, 0,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
if (err < 0)
@@ -1220,7 +1208,7 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream)
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
card->update_interval_frames);
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- card->update_interval_frames * 2, UINT_MAX);
+ card->update_interval_frames, UINT_MAX);
snd_pcm_set_sync(substream);
@@ -1231,37 +1219,13 @@ static int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream)
{
struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data;
- hpi_handle_error(hpi_instream_close(ss, dpcm->h_stream));
+ hpi_handle_error(hpi_instream_close(dpcm->h_stream));
return 0;
}
-static int snd_card_asihpi_capture_copy(struct snd_pcm_substream *substream,
- int channel, snd_pcm_uframes_t pos,
- void __user *dst, snd_pcm_uframes_t count)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_card_asihpi_pcm *dpcm = runtime->private_data;
- u32 data_size;
-
- data_size = frames_to_bytes(runtime, count);
-
- VPRINTK2("capture copy%d %d bytes\n", substream->number, data_size);
- hpi_handle_error(hpi_instream_read_buf(ss, dpcm->h_stream,
- runtime->dma_area, data_size));
-
- /* Used by capture_pointer */
- dpcm->pcm_irq_pos = dpcm->pcm_irq_pos + data_size;
-
- if (copy_to_user(dst, runtime->dma_area, data_size))
- return -EFAULT;
-
- return 0;
-}
-
-static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
+static const struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
.open = snd_card_asihpi_capture_open,
.close = snd_card_asihpi_capture_close,
- .ioctl = snd_card_asihpi_capture_ioctl,
.hw_params = snd_card_asihpi_pcm_hw_params,
.hw_free = snd_card_asihpi_hw_free,
.prepare = snd_card_asihpi_capture_prepare,
@@ -1269,51 +1233,37 @@ static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
.pointer = snd_card_asihpi_capture_pointer,
};
-static struct snd_pcm_ops snd_card_asihpi_capture_ops = {
- .open = snd_card_asihpi_capture_open,
- .close = snd_card_asihpi_capture_close,
- .ioctl = snd_card_asihpi_capture_ioctl,
- .hw_params = snd_card_asihpi_pcm_hw_params,
- .hw_free = snd_card_asihpi_hw_free,
- .prepare = snd_card_asihpi_capture_prepare,
- .trigger = snd_card_asihpi_trigger,
- .pointer = snd_card_asihpi_capture_pointer,
- .copy = snd_card_asihpi_capture_copy
-};
-
-static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi,
- int device, int substreams)
+static int snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi, int device)
{
struct snd_pcm *pcm;
int err;
+ u16 num_instreams, num_outstreams, x16;
+ u32 x32;
+
+ err = hpi_adapter_get_info(asihpi->hpi->adapter->index,
+ &num_outstreams, &num_instreams,
+ &x16, &x32, &x16);
- err = snd_pcm_new(asihpi->card, "asihpi PCM", device,
- asihpi->num_outstreams, asihpi->num_instreams,
- &pcm);
+ err = snd_pcm_new(asihpi->card, "Asihpi PCM", device,
+ num_outstreams, num_instreams, &pcm);
if (err < 0)
return err;
+
/* pointer to ops struct is stored, dont change ops afterwards! */
- if (asihpi->support_mmap) {
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- &snd_card_asihpi_playback_mmap_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &snd_card_asihpi_capture_mmap_ops);
- } else {
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- &snd_card_asihpi_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &snd_card_asihpi_capture_ops);
- }
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &snd_card_asihpi_playback_mmap_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_card_asihpi_capture_mmap_ops);
pcm->private_data = asihpi;
pcm->info_flags = 0;
- strcpy(pcm->name, "asihpi PCM");
+ strscpy(pcm->name, "Asihpi PCM");
/*? do we want to emulate MMAP for non-BBM cards?
Jack doesn't work with ALSAs MMAP emulation - WHY NOT? */
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(asihpi->pci),
- 64*1024, BUFFER_BYTES_MAX);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &asihpi->pci->dev,
+ 64*1024, BUFFER_BYTES_MAX);
return 0;
}
@@ -1327,11 +1277,10 @@ struct hpi_control {
u16 dst_node_type;
u16 dst_node_index;
u16 band;
- char name[44]; /* copied to snd_ctl_elem_id.name[44]; */
+ char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* copied to snd_ctl_elem_id.name[44]; */
};
-static char *asihpi_tuner_band_names[] =
-{
+static const char * const asihpi_tuner_band_names[] = {
"invalid",
"AM",
"FM mono",
@@ -1342,78 +1291,53 @@ static char *asihpi_tuner_band_names[] =
"TV PAL I",
"TV PAL DK",
"TV SECAM",
+ "TV DAB",
};
-
+/* Number of strings must match the enumerations for HPI_TUNER_BAND in hpi.h */
compile_time_assert(
(ARRAY_SIZE(asihpi_tuner_band_names) ==
(HPI_TUNER_BAND_LAST+1)),
assert_tuner_band_names_size);
-#if ASI_STYLE_NAMES
-static char *asihpi_src_names[] =
-{
+static const char * const asihpi_src_names[] = {
"no source",
- "outstream",
- "line_in",
- "aes_in",
- "tuner",
+ "PCM",
+ "Line",
+ "Digital",
+ "Tuner",
"RF",
- "clock",
- "bitstr",
- "mic",
- "cobranet",
- "analog_in",
- "adapter",
+ "Clock",
+ "Bitstream",
+ "Mic",
+ "Net",
+ "Analog",
+ "Adapter",
+ "RTP",
+ "Internal",
+ "AVB",
+ "BLU-Link"
};
-#else
-static char *asihpi_src_names[] =
-{
- "no source",
- "PCM playback",
- "line in",
- "digital in",
- "tuner",
- "RF",
- "clock",
- "bitstream",
- "mic",
- "cobranet in",
- "analog in",
- "adapter",
-};
-#endif
-
+/* Number of strings must match the enumerations for HPI_SOURCENODES in hpi.h */
compile_time_assert(
(ARRAY_SIZE(asihpi_src_names) ==
(HPI_SOURCENODE_LAST_INDEX-HPI_SOURCENODE_NONE+1)),
assert_src_names_size);
-#if ASI_STYLE_NAMES
-static char *asihpi_dst_names[] =
-{
+static const char * const asihpi_dst_names[] = {
"no destination",
- "instream",
- "line_out",
- "aes_out",
+ "PCM",
+ "Line",
+ "Digital",
"RF",
- "speaker" ,
- "cobranet",
- "analog_out",
+ "Speaker",
+ "Net",
+ "Analog",
+ "RTP",
+ "AVB",
+ "Internal",
+ "BLU-Link"
};
-#else
-static char *asihpi_dst_names[] =
-{
- "no destination",
- "PCM capture",
- "line out",
- "digital out",
- "RF",
- "speaker",
- "cobranet out",
- "analog out"
-};
-#endif
-
+/* Number of strings must match the enumerations for HPI_DESTNODES in hpi.h */
compile_time_assert(
(ARRAY_SIZE(asihpi_dst_names) ==
(HPI_DESTNODE_LAST_INDEX-HPI_DESTNODE_NONE+1)),
@@ -1428,7 +1352,7 @@ static inline int ctl_add(struct snd_card *card, struct snd_kcontrol_new *ctl,
if (err < 0)
return err;
else if (mixer_dump)
- snd_printk(KERN_INFO "added %s(%d)\n", ctl->name, ctl->index);
+ dev_info(&asihpi->pci->dev, "added %s(%d)\n", ctl->name, ctl->index);
return 0;
}
@@ -1438,29 +1362,44 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control,
struct hpi_control *hpi_ctl,
char *name)
{
+ char *dir;
memset(snd_control, 0, sizeof(*snd_control));
snd_control->name = hpi_ctl->name;
snd_control->private_value = hpi_ctl->h_control;
snd_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
snd_control->index = 0;
+ if (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE == HPI_SOURCENODE_CLOCK_SOURCE)
+ dir = ""; /* clock is neither capture nor playback */
+ else if (hpi_ctl->dst_node_type + HPI_DESTNODE_NONE == HPI_DESTNODE_ISTREAM)
+ dir = "Capture "; /* On or towards a PCM capture destination*/
+ else if ((hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) &&
+ (!hpi_ctl->dst_node_type))
+ dir = "Capture "; /* On a source node that is not PCM playback */
+ else if (hpi_ctl->src_node_type &&
+ (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) &&
+ (hpi_ctl->dst_node_type))
+ dir = "Monitor Playback "; /* Between an input and an output */
+ else
+ dir = "Playback "; /* PCM Playback source, or output node */
+
if (hpi_ctl->src_node_type && hpi_ctl->dst_node_type)
- sprintf(hpi_ctl->name, "%s%d to %s%d %s",
+ sprintf(hpi_ctl->name, "%s %d %s %d %s%s",
asihpi_src_names[hpi_ctl->src_node_type],
hpi_ctl->src_node_index,
asihpi_dst_names[hpi_ctl->dst_node_type],
hpi_ctl->dst_node_index,
- name);
+ dir, name);
else if (hpi_ctl->dst_node_type) {
- sprintf(hpi_ctl->name, "%s%d %s",
+ sprintf(hpi_ctl->name, "%s %d %s%s",
asihpi_dst_names[hpi_ctl->dst_node_type],
hpi_ctl->dst_node_index,
- name);
+ dir, name);
} else {
- sprintf(hpi_ctl->name, "%s%d %s",
+ sprintf(hpi_ctl->name, "%s %d %s%s",
asihpi_src_names[hpi_ctl->src_node_type],
hpi_ctl->src_node_index,
- name);
+ dir, name);
}
}
@@ -1472,13 +1411,14 @@ static int snd_asihpi_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
u32 h_control = kcontrol->private_value;
+ u32 count;
u16 err;
/* native gains are in millibels */
short min_gain_mB;
short max_gain_mB;
short step_gain_mB;
- err = hpi_volume_query_range(ss, h_control,
+ err = hpi_volume_query_range(h_control,
&min_gain_mB, &max_gain_mB, &step_gain_mB);
if (err) {
max_gain_mB = 0;
@@ -1486,8 +1426,12 @@ static int snd_asihpi_volume_info(struct snd_kcontrol *kcontrol,
step_gain_mB = VOL_STEP_mB;
}
+ err = hpi_meter_query_channels(h_control, &count);
+ if (err)
+ count = HPI_MAX_CHANNELS;
+
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 2;
+ uinfo->count = count;
uinfo->value.integer.min = min_gain_mB / VOL_STEP_mB;
uinfo->value.integer.max = max_gain_mB / VOL_STEP_mB;
uinfo->value.integer.step = step_gain_mB / VOL_STEP_mB;
@@ -1500,7 +1444,7 @@ static int snd_asihpi_volume_get(struct snd_kcontrol *kcontrol,
u32 h_control = kcontrol->private_value;
short an_gain_mB[HPI_MAX_CHANNELS];
- hpi_handle_error(hpi_volume_get_gain(ss, h_control, an_gain_mB));
+ hpi_handle_error(hpi_volume_get_gain(h_control, an_gain_mB));
ucontrol->value.integer.value[0] = an_gain_mB[0] / VOL_STEP_mB;
ucontrol->value.integer.value[1] = an_gain_mB[1] / VOL_STEP_mB;
@@ -1510,7 +1454,6 @@ static int snd_asihpi_volume_get(struct snd_kcontrol *kcontrol,
static int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- int change;
u32 h_control = kcontrol->private_value;
short an_gain_mB[HPI_MAX_CHANNELS];
@@ -1521,20 +1464,47 @@ static int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol,
/* change = asihpi->mixer_volume[addr][0] != left ||
asihpi->mixer_volume[addr][1] != right;
*/
- change = 1;
- hpi_handle_error(hpi_volume_set_gain(ss, h_control, an_gain_mB));
- return change;
+ hpi_handle_error(hpi_volume_set_gain(h_control, an_gain_mB));
+ return 1;
}
static const DECLARE_TLV_DB_SCALE(db_scale_100, -10000, VOL_STEP_mB, 0);
-static int __devinit snd_asihpi_volume_add(struct snd_card_asihpi *asihpi,
- struct hpi_control *hpi_ctl)
+#define snd_asihpi_volume_mute_info snd_ctl_boolean_mono_info
+
+static int snd_asihpi_volume_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 h_control = kcontrol->private_value;
+ u32 mute;
+
+ hpi_handle_error(hpi_volume_get_mute(h_control, &mute));
+ ucontrol->value.integer.value[0] = mute ? 0 : 1;
+
+ return 0;
+}
+
+static int snd_asihpi_volume_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 h_control = kcontrol->private_value;
+ /* HPI currently only supports all or none muting of multichannel volume
+ ALSA Switch element has opposite sense to HPI mute: on==unmuted, off=muted
+ */
+ int mute = ucontrol->value.integer.value[0] ? 0 : HPI_BITMASK_ALL_CHANNELS;
+ hpi_handle_error(hpi_volume_set_mute(h_control, mute));
+ return 1;
+}
+
+static int snd_asihpi_volume_add(struct snd_card_asihpi *asihpi,
+ struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
+ int err;
+ u32 mute;
- asihpi_ctl_init(&snd_control, hpi_ctl, "volume");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Volume");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ;
snd_control.info = snd_asihpi_volume_info;
@@ -1542,7 +1512,19 @@ static int __devinit snd_asihpi_volume_add(struct snd_card_asihpi *asihpi,
snd_control.put = snd_asihpi_volume_put;
snd_control.tlv.p = db_scale_100;
- return ctl_add(card, &snd_control, asihpi);
+ err = ctl_add(card, &snd_control, asihpi);
+ if (err)
+ return err;
+
+ if (hpi_volume_get_mute(hpi_ctl->h_control, &mute) == 0) {
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Switch");
+ snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ snd_control.info = snd_asihpi_volume_mute_info;
+ snd_control.get = snd_asihpi_volume_mute_get;
+ snd_control.put = snd_asihpi_volume_mute_put;
+ err = ctl_add(card, &snd_control, asihpi);
+ }
+ return err;
}
/*------------------------------------------------------------
@@ -1558,7 +1540,7 @@ static int snd_asihpi_level_info(struct snd_kcontrol *kcontrol,
short step_gain_mB;
err =
- hpi_level_query_range(ss, h_control, &min_gain_mB,
+ hpi_level_query_range(h_control, &min_gain_mB,
&max_gain_mB, &step_gain_mB);
if (err) {
max_gain_mB = 2400;
@@ -1580,7 +1562,7 @@ static int snd_asihpi_level_get(struct snd_kcontrol *kcontrol,
u32 h_control = kcontrol->private_value;
short an_gain_mB[HPI_MAX_CHANNELS];
- hpi_handle_error(hpi_level_get_gain(ss, h_control, an_gain_mB));
+ hpi_handle_error(hpi_level_get_gain(h_control, an_gain_mB));
ucontrol->value.integer.value[0] =
an_gain_mB[0] / HPI_UNITS_PER_dB;
ucontrol->value.integer.value[1] =
@@ -1604,20 +1586,20 @@ static int snd_asihpi_level_put(struct snd_kcontrol *kcontrol,
asihpi->mixer_level[addr][1] != right;
*/
change = 1;
- hpi_handle_error(hpi_level_set_gain(ss, h_control, an_gain_mB));
+ hpi_handle_error(hpi_level_set_gain(h_control, an_gain_mB));
return change;
}
static const DECLARE_TLV_DB_SCALE(db_scale_level, -1000, 100, 0);
-static int __devinit snd_asihpi_level_add(struct snd_card_asihpi *asihpi,
- struct hpi_control *hpi_ctl)
+static int snd_asihpi_level_add(struct snd_card_asihpi *asihpi,
+ struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
/* can't use 'volume' cos some nodes have volume as well */
- asihpi_ctl_init(&snd_control, hpi_ctl, "level");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Level");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ;
snd_control.info = snd_asihpi_level_info;
@@ -1633,38 +1615,23 @@ static int __devinit snd_asihpi_level_add(struct snd_card_asihpi *asihpi,
------------------------------------------------------------*/
/* AESEBU format */
-static char *asihpi_aesebu_format_names[] =
-{
- "N/A",
- "S/PDIF",
- "AES/EBU",
-};
+static const char * const asihpi_aesebu_format_names[] = {
+ "N/A", "S/PDIF", "AES/EBU" };
static int snd_asihpi_aesebu_format_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
-
- strcpy(uinfo->value.enumerated.name,
- asihpi_aesebu_format_names[uinfo->value.enumerated.item]);
-
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, asihpi_aesebu_format_names);
}
static int snd_asihpi_aesebu_format_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol,
- u16 (*func)(const struct hpi_hsubsys *, u32, u16 *))
+ u16 (*func)(u32, u16 *))
{
u32 h_control = kcontrol->private_value;
u16 source, err;
- err = func(ss, h_control, &source);
+ err = func(h_control, &source);
/* default to N/A */
ucontrol->value.enumerated.item[0] = 0;
@@ -1681,7 +1648,7 @@ static int snd_asihpi_aesebu_format_get(struct snd_kcontrol *kcontrol,
static int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol,
- u16 (*func)(const struct hpi_hsubsys *, u32, u16))
+ u16 (*func)(u32, u16))
{
u32 h_control = kcontrol->private_value;
@@ -1693,7 +1660,7 @@ static int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol,
if (ucontrol->value.enumerated.item[0] == 2)
source = HPI_AESEBU_FORMAT_AESEBU;
- if (func(ss, h_control, source) != 0)
+ if (func(h_control, source) != 0)
return -EINVAL;
return 1;
@@ -1702,13 +1669,13 @@ static int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol,
static int snd_asihpi_aesebu_rx_format_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
return snd_asihpi_aesebu_format_get(kcontrol, ucontrol,
- HPI_AESEBU__receiver_get_format);
+ hpi_aesebu_receiver_get_format);
}
static int snd_asihpi_aesebu_rx_format_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
return snd_asihpi_aesebu_format_put(kcontrol, ucontrol,
- HPI_AESEBU__receiver_set_format);
+ hpi_aesebu_receiver_set_format);
}
static int snd_asihpi_aesebu_rxstatus_info(struct snd_kcontrol *kcontrol,
@@ -1730,19 +1697,19 @@ static int snd_asihpi_aesebu_rxstatus_get(struct snd_kcontrol *kcontrol,
u32 h_control = kcontrol->private_value;
u16 status;
- hpi_handle_error(HPI_AESEBU__receiver_get_error_status(
- ss, h_control, &status));
+ hpi_handle_error(hpi_aesebu_receiver_get_error_status(
+ h_control, &status));
ucontrol->value.integer.value[0] = status;
return 0;
}
-static int __devinit snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi,
- struct hpi_control *hpi_ctl)
+static int snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi,
+ struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
- asihpi_ctl_init(&snd_control, hpi_ctl, "format");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Format");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
snd_control.info = snd_asihpi_aesebu_format_info;
snd_control.get = snd_asihpi_aesebu_rx_format_get;
@@ -1752,7 +1719,7 @@ static int __devinit snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi,
if (ctl_add(card, &snd_control, asihpi) < 0)
return -EINVAL;
- asihpi_ctl_init(&snd_control, hpi_ctl, "status");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Status");
snd_control.access =
SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ;
snd_control.info = snd_asihpi_aesebu_rxstatus_info;
@@ -1764,23 +1731,23 @@ static int __devinit snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi,
static int snd_asihpi_aesebu_tx_format_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
return snd_asihpi_aesebu_format_get(kcontrol, ucontrol,
- HPI_AESEBU__transmitter_get_format);
+ hpi_aesebu_transmitter_get_format);
}
static int snd_asihpi_aesebu_tx_format_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) {
return snd_asihpi_aesebu_format_put(kcontrol, ucontrol,
- HPI_AESEBU__transmitter_set_format);
+ hpi_aesebu_transmitter_set_format);
}
-static int __devinit snd_asihpi_aesebu_tx_add(struct snd_card_asihpi *asihpi,
- struct hpi_control *hpi_ctl)
+static int snd_asihpi_aesebu_tx_add(struct snd_card_asihpi *asihpi,
+ struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
- asihpi_ctl_init(&snd_control, hpi_ctl, "format");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Format");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
snd_control.info = snd_asihpi_aesebu_format_info;
snd_control.get = snd_asihpi_aesebu_tx_format_get;
@@ -1804,7 +1771,7 @@ static int snd_asihpi_tuner_gain_info(struct snd_kcontrol *kcontrol,
u16 gain_range[3];
for (idx = 0; idx < 3; idx++) {
- err = hpi_tuner_query_gain(ss, h_control,
+ err = hpi_tuner_query_gain(h_control,
idx, &gain_range[idx]);
if (err != 0)
return err;
@@ -1827,7 +1794,7 @@ static int snd_asihpi_tuner_gain_get(struct snd_kcontrol *kcontrol,
u32 h_control = kcontrol->private_value;
short gain;
- hpi_handle_error(hpi_tuner_get_gain(ss, h_control, &gain));
+ hpi_handle_error(hpi_tuner_get_gain(h_control, &gain));
ucontrol->value.integer.value[0] = gain / HPI_UNITS_PER_dB;
return 0;
@@ -1843,7 +1810,7 @@ static int snd_asihpi_tuner_gain_put(struct snd_kcontrol *kcontrol,
short gain;
gain = (ucontrol->value.integer.value[0]) * HPI_UNITS_PER_dB;
- hpi_handle_error(hpi_tuner_set_gain(ss, h_control, gain));
+ hpi_handle_error(hpi_tuner_set_gain(h_control, gain));
return 1;
}
@@ -1857,7 +1824,7 @@ static int asihpi_tuner_band_query(struct snd_kcontrol *kcontrol,
u32 i;
for (i = 0; i < len; i++) {
- err = hpi_tuner_query_band(ss,
+ err = hpi_tuner_query_band(
h_control, i, &band_list[i]);
if (err != 0)
break;
@@ -1881,22 +1848,7 @@ static int snd_asihpi_tuner_band_info(struct snd_kcontrol *kcontrol,
if (num_bands < 0)
return num_bands;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = num_bands;
-
- if (num_bands > 0) {
- if (uinfo->value.enumerated.item >=
- uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
-
- strcpy(uinfo->value.enumerated.name,
- asihpi_tuner_band_names[
- tuner_bands[uinfo->value.enumerated.item]]);
-
- }
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, num_bands, asihpi_tuner_band_names);
}
static int snd_asihpi_tuner_band_get(struct snd_kcontrol *kcontrol,
@@ -1908,12 +1860,12 @@ static int snd_asihpi_tuner_band_get(struct snd_kcontrol *kcontrol,
*/
u16 band, idx;
u16 tuner_bands[HPI_TUNER_BAND_LAST];
- u32 num_bands = 0;
+ __always_unused u32 num_bands;
num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
HPI_TUNER_BAND_LAST);
- hpi_handle_error(hpi_tuner_get_band(ss, h_control, &band));
+ hpi_handle_error(hpi_tuner_get_band(h_control, &band));
ucontrol->value.enumerated.item[0] = -1;
for (idx = 0; idx < HPI_TUNER_BAND_LAST; idx++)
@@ -1932,15 +1884,19 @@ static int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol,
struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
*/
u32 h_control = kcontrol->private_value;
+ unsigned int idx;
u16 band;
u16 tuner_bands[HPI_TUNER_BAND_LAST];
- u32 num_bands = 0;
+ __always_unused u32 num_bands;
num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands,
HPI_TUNER_BAND_LAST);
- band = tuner_bands[ucontrol->value.enumerated.item[0]];
- hpi_handle_error(hpi_tuner_set_band(ss, h_control, band));
+ idx = ucontrol->value.enumerated.item[0];
+ if (idx >= ARRAY_SIZE(tuner_bands))
+ idx = ARRAY_SIZE(tuner_bands) - 1;
+ band = tuner_bands[idx];
+ hpi_handle_error(hpi_tuner_set_band(h_control, band));
return 1;
}
@@ -1965,7 +1921,7 @@ static int snd_asihpi_tuner_freq_info(struct snd_kcontrol *kcontrol,
for (band_iter = 0; band_iter < num_bands; band_iter++) {
for (idx = 0; idx < 3; idx++) {
- err = hpi_tuner_query_frequency(ss, h_control,
+ err = hpi_tuner_query_frequency(h_control,
idx, tuner_bands[band_iter],
&temp_freq_range[idx]);
if (err != 0)
@@ -1998,7 +1954,7 @@ static int snd_asihpi_tuner_freq_get(struct snd_kcontrol *kcontrol,
u32 h_control = kcontrol->private_value;
u32 freq;
- hpi_handle_error(hpi_tuner_get_frequency(ss, h_control, &freq));
+ hpi_handle_error(hpi_tuner_get_frequency(h_control, &freq));
ucontrol->value.integer.value[0] = freq;
return 0;
@@ -2011,14 +1967,14 @@ static int snd_asihpi_tuner_freq_put(struct snd_kcontrol *kcontrol,
u32 freq;
freq = ucontrol->value.integer.value[0];
- hpi_handle_error(hpi_tuner_set_frequency(ss, h_control, freq));
+ hpi_handle_error(hpi_tuner_set_frequency(h_control, freq));
return 1;
}
/* Tuner control group initializer */
-static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi,
- struct hpi_control *hpi_ctl)
+static int snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi,
+ struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
@@ -2026,8 +1982,8 @@ static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi,
snd_control.private_value = hpi_ctl->h_control;
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
- if (!hpi_tuner_get_gain(ss, hpi_ctl->h_control, NULL)) {
- asihpi_ctl_init(&snd_control, hpi_ctl, "gain");
+ if (!hpi_tuner_get_gain(hpi_ctl->h_control, NULL)) {
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Gain");
snd_control.info = snd_asihpi_tuner_gain_info;
snd_control.get = snd_asihpi_tuner_gain_get;
snd_control.put = snd_asihpi_tuner_gain_put;
@@ -2036,7 +1992,7 @@ static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi,
return -EINVAL;
}
- asihpi_ctl_init(&snd_control, hpi_ctl, "band");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Band");
snd_control.info = snd_asihpi_tuner_band_info;
snd_control.get = snd_asihpi_tuner_band_get;
snd_control.put = snd_asihpi_tuner_band_put;
@@ -2044,7 +2000,7 @@ static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi,
if (ctl_add(card, &snd_control, asihpi) < 0)
return -EINVAL;
- asihpi_ctl_init(&snd_control, hpi_ctl, "freq");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Freq");
snd_control.info = snd_asihpi_tuner_freq_info;
snd_control.get = snd_asihpi_tuner_freq_get;
snd_control.put = snd_asihpi_tuner_freq_put;
@@ -2058,15 +2014,22 @@ static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi,
static int snd_asihpi_meter_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
+ u32 h_control = kcontrol->private_value;
+ u32 count;
+ u16 err;
+ err = hpi_meter_query_channels(h_control, &count);
+ if (err)
+ count = HPI_MAX_CHANNELS;
+
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = HPI_MAX_CHANNELS;
+ uinfo->count = count;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 0x7FFFFFFF;
return 0;
}
/* linear values for 10dB steps */
-static int log2lin[] = {
+static const int log2lin[] = {
0x7FFFFFFF, /* 0dB */
679093956,
214748365,
@@ -2095,7 +2058,7 @@ static int snd_asihpi_meter_get(struct snd_kcontrol *kcontrol,
short an_gain_mB[HPI_MAX_CHANNELS], i;
u16 err;
- err = hpi_meter_get_peak(ss, h_control, an_gain_mB);
+ err = hpi_meter_get_peak(h_control, an_gain_mB);
for (i = 0; i < HPI_MAX_CHANNELS; i++) {
if (err) {
@@ -2114,13 +2077,13 @@ static int snd_asihpi_meter_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int __devinit snd_asihpi_meter_add(struct snd_card_asihpi *asihpi,
- struct hpi_control *hpi_ctl, int subidx)
+static int snd_asihpi_meter_add(struct snd_card_asihpi *asihpi,
+ struct hpi_control *hpi_ctl, int subidx)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
- asihpi_ctl_init(&snd_control, hpi_ctl, "meter");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Meter");
snd_control.access =
SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ;
snd_control.info = snd_asihpi_meter_info;
@@ -2140,7 +2103,7 @@ static int snd_card_asihpi_mux_count_sources(struct snd_kcontrol *snd_control)
struct hpi_control hpi_ctl;
int s, err;
for (s = 0; s < 32; s++) {
- err = hpi_multiplexer_query_source(ss, h_control, s,
+ err = hpi_multiplexer_query_source(h_control, s,
&hpi_ctl.
src_node_type,
&hpi_ctl.
@@ -2154,7 +2117,6 @@ static int snd_card_asihpi_mux_count_sources(struct snd_kcontrol *snd_control)
static int snd_asihpi_mux_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- int err;
u16 src_node_type, src_node_index;
u32 h_control = kcontrol->private_value;
@@ -2167,10 +2129,9 @@ static int snd_asihpi_mux_info(struct snd_kcontrol *kcontrol,
uinfo->value.enumerated.item =
uinfo->value.enumerated.items - 1;
- err =
- hpi_multiplexer_query_source(ss, h_control,
- uinfo->value.enumerated.item,
- &src_node_type, &src_node_index);
+ hpi_multiplexer_query_source(h_control,
+ uinfo->value.enumerated.item,
+ &src_node_type, &src_node_index);
sprintf(uinfo->value.enumerated.name, "%s %d",
asihpi_src_names[src_node_type - HPI_SOURCENODE_NONE],
@@ -2186,11 +2147,11 @@ static int snd_asihpi_mux_get(struct snd_kcontrol *kcontrol,
u16 src_node_type, src_node_index;
int s;
- hpi_handle_error(hpi_multiplexer_get_source(ss, h_control,
+ hpi_handle_error(hpi_multiplexer_get_source(h_control,
&source_type, &source_index));
/* Should cache this search result! */
for (s = 0; s < 256; s++) {
- if (hpi_multiplexer_query_source(ss, h_control, s,
+ if (hpi_multiplexer_query_source(h_control, s,
&src_node_type, &src_node_index))
break;
@@ -2200,9 +2161,8 @@ static int snd_asihpi_mux_get(struct snd_kcontrol *kcontrol,
return 0;
}
}
- snd_printd(KERN_WARNING
- "control %x failed to match mux source %hu %hu\n",
- h_control, source_type, source_index);
+ pr_warn("%s: Control %x failed to match mux source %hu %hu\n",
+ __func__, h_control, source_type, source_index);
ucontrol->value.enumerated.item[0] = 0;
return 0;
}
@@ -2217,28 +2177,24 @@ static int snd_asihpi_mux_put(struct snd_kcontrol *kcontrol,
change = 1;
- e = hpi_multiplexer_query_source(ss, h_control,
+ e = hpi_multiplexer_query_source(h_control,
ucontrol->value.enumerated.item[0],
&source_type, &source_index);
if (!e)
hpi_handle_error(
- hpi_multiplexer_set_source(ss, h_control,
+ hpi_multiplexer_set_source(h_control,
source_type, source_index));
return change;
}
-static int __devinit snd_asihpi_mux_add(struct snd_card_asihpi *asihpi,
- struct hpi_control *hpi_ctl)
+static int snd_asihpi_mux_add(struct snd_card_asihpi *asihpi,
+ struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
-#if ASI_STYLE_NAMES
- asihpi_ctl_init(&snd_control, hpi_ctl, "multiplexer");
-#else
- asihpi_ctl_init(&snd_control, hpi_ctl, "route");
-#endif
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Route");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
snd_control.info = snd_asihpi_mux_info;
snd_control.get = snd_asihpi_mux_get;
@@ -2254,35 +2210,33 @@ static int __devinit snd_asihpi_mux_add(struct snd_card_asihpi *asihpi,
static int snd_asihpi_cmode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *mode_names[HPI_CHANNEL_MODE_LAST] = {
- "normal", "swap",
- "from_left", "from_right",
- "to_left", "to_right"
+ static const char * const mode_names[HPI_CHANNEL_MODE_LAST + 1] = {
+ "invalid",
+ "Normal", "Swap",
+ "From Left", "From Right",
+ "To Left", "To Right"
};
u32 h_control = kcontrol->private_value;
u16 mode;
int i;
+ const char *mapped_names[6];
+ int valid_modes = 0;
/* HPI channel mode values can be from 1 to 6
Some adapters only support a contiguous subset
*/
for (i = 0; i < HPI_CHANNEL_MODE_LAST; i++)
- if (hpi_channel_mode_query_mode(
- ss, h_control, i, &mode))
- break;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = i;
-
- if (uinfo->value.enumerated.item >= i)
- uinfo->value.enumerated.item = i - 1;
+ if (!hpi_channel_mode_query_mode(
+ h_control, i, &mode)) {
+ mapped_names[valid_modes] = mode_names[mode];
+ valid_modes++;
+ }
- strcpy(uinfo->value.enumerated.name,
- mode_names[uinfo->value.enumerated.item]);
+ if (!valid_modes)
+ return -EINVAL;
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, valid_modes, mapped_names);
}
static int snd_asihpi_cmode_get(struct snd_kcontrol *kcontrol,
@@ -2291,7 +2245,7 @@ static int snd_asihpi_cmode_get(struct snd_kcontrol *kcontrol,
u32 h_control = kcontrol->private_value;
u16 mode;
- if (hpi_channel_mode_get(ss, h_control, &mode))
+ if (hpi_channel_mode_get(h_control, &mode))
mode = 1;
ucontrol->value.enumerated.item[0] = mode - 1;
@@ -2307,19 +2261,19 @@ static int snd_asihpi_cmode_put(struct snd_kcontrol *kcontrol,
change = 1;
- hpi_handle_error(hpi_channel_mode_set(ss, h_control,
+ hpi_handle_error(hpi_channel_mode_set(h_control,
ucontrol->value.enumerated.item[0] + 1));
return change;
}
-static int __devinit snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi,
- struct hpi_control *hpi_ctl)
+static int snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi,
+ struct hpi_control *hpi_ctl)
{
struct snd_card *card = asihpi->card;
struct snd_kcontrol_new snd_control;
- asihpi_ctl_init(&snd_control, hpi_ctl, "channel mode");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Mode");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
snd_control.info = snd_asihpi_cmode_info;
snd_control.get = snd_asihpi_cmode_get;
@@ -2331,21 +2285,22 @@ static int __devinit snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi,
/*------------------------------------------------------------
Sampleclock source controls
------------------------------------------------------------*/
-
-static char *sampleclock_sources[MAX_CLOCKSOURCES] =
- { "N/A", "local PLL", "AES/EBU sync", "word external", "word header",
- "SMPTE", "AES/EBU in1", "auto", "network", "invalid",
- "prev module",
- "AES/EBU in2", "AES/EBU in3", "AES/EBU in4", "AES/EBU in5",
- "AES/EBU in6", "AES/EBU in7", "AES/EBU in8"};
-
-
+static const char * const sampleclock_sources[] = {
+ "N/A", "Local PLL", "Digital Sync", "Word External", "Word Header",
+ "SMPTE", "Digital1", "Auto", "Network", "Invalid",
+ "Prev Module", "BLU-Link",
+ "Digital2", "Digital3", "Digital4", "Digital5",
+ "Digital6", "Digital7", "Digital8"};
+
+ /* Number of strings must match expected enumerated values */
+ compile_time_assert(
+ (ARRAY_SIZE(sampleclock_sources) == MAX_CLOCKSOURCES),
+ assert_sampleclock_sources_size);
static int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- struct snd_card_asihpi *asihpi =
- (struct snd_card_asihpi *)(kcontrol->private_data);
+ struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
struct clk_cache *clkcache = &asihpi->cc;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
@@ -2355,7 +2310,7 @@ static int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol,
uinfo->value.enumerated.item =
uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
+ strscpy(uinfo->value.enumerated.name,
clkcache->s[uinfo->value.enumerated.item].name);
return 0;
}
@@ -2363,19 +2318,18 @@ static int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol,
static int snd_asihpi_clksrc_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_card_asihpi *asihpi =
- (struct snd_card_asihpi *)(kcontrol->private_data);
+ struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
struct clk_cache *clkcache = &asihpi->cc;
u32 h_control = kcontrol->private_value;
u16 source, srcindex = 0;
int i;
ucontrol->value.enumerated.item[0] = 0;
- if (hpi_sample_clock_get_source(ss, h_control, &source))
+ if (hpi_sample_clock_get_source(h_control, &source))
source = 0;
if (source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT)
- if (hpi_sample_clock_get_source_index(ss, h_control, &srcindex))
+ if (hpi_sample_clock_get_source_index(h_control, &srcindex))
srcindex = 0;
for (i = 0; i < clkcache->count; i++)
@@ -2391,10 +2345,10 @@ static int snd_asihpi_clksrc_get(struct snd_kcontrol *kcontrol,
static int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_card_asihpi *asihpi =
- (struct snd_card_asihpi *)(kcontrol->private_data);
+ struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol);
struct clk_cache *clkcache = &asihpi->cc;
- int change, item;
+ unsigned int item;
+ int change;
u32 h_control = kcontrol->private_value;
change = 1;
@@ -2402,11 +2356,11 @@ static int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol,
if (item >= clkcache->count)
item = clkcache->count-1;
- hpi_handle_error(hpi_sample_clock_set_source(ss,
+ hpi_handle_error(hpi_sample_clock_set_source(
h_control, clkcache->s[item].source));
if (clkcache->s[item].source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT)
- hpi_handle_error(hpi_sample_clock_set_source_index(ss,
+ hpi_handle_error(hpi_sample_clock_set_source_index(
h_control, clkcache->s[item].index));
return change;
}
@@ -2434,7 +2388,7 @@ static int snd_asihpi_clklocal_get(struct snd_kcontrol *kcontrol,
u32 rate;
u16 e;
- e = hpi_sample_clock_get_local_rate(ss, h_control, &rate);
+ e = hpi_sample_clock_get_local_rate(h_control, &rate);
if (!e)
ucontrol->value.integer.value[0] = rate;
else
@@ -2452,7 +2406,7 @@ static int snd_asihpi_clklocal_put(struct snd_kcontrol *kcontrol,
asihpi->mixer_clkrate[addr][1] != right;
*/
change = 1;
- hpi_handle_error(hpi_sample_clock_set_local_rate(ss, h_control,
+ hpi_handle_error(hpi_sample_clock_set_local_rate(h_control,
ucontrol->value.integer.value[0]));
return change;
}
@@ -2476,7 +2430,7 @@ static int snd_asihpi_clkrate_get(struct snd_kcontrol *kcontrol,
u32 rate;
u16 e;
- e = hpi_sample_clock_get_sample_rate(ss, h_control, &rate);
+ e = hpi_sample_clock_get_sample_rate(h_control, &rate);
if (!e)
ucontrol->value.integer.value[0] = rate;
else
@@ -2484,24 +2438,28 @@ static int snd_asihpi_clkrate_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
- struct hpi_control *hpi_ctl)
+static int snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
+ struct hpi_control *hpi_ctl)
{
- struct snd_card *card = asihpi->card;
+ struct snd_card *card;
struct snd_kcontrol_new snd_control;
- struct clk_cache *clkcache = &asihpi->cc;
+ struct clk_cache *clkcache;
u32 hSC = hpi_ctl->h_control;
int has_aes_in = 0;
int i, j;
u16 source;
+ if (snd_BUG_ON(!asihpi))
+ return -EINVAL;
+ card = asihpi->card;
+ clkcache = &asihpi->cc;
snd_control.private_value = hpi_ctl->h_control;
clkcache->has_local = 0;
for (i = 0; i <= HPI_SAMPLECLOCK_SOURCE_LAST; i++) {
- if (hpi_sample_clock_query_source(ss, hSC,
+ if (hpi_sample_clock_query_source(hSC,
i, &source))
break;
clkcache->s[i].source = source;
@@ -2515,7 +2473,7 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
if (has_aes_in)
/* already will have picked up index 0 above */
for (j = 1; j < 8; j++) {
- if (hpi_sample_clock_query_source_index(ss, hSC,
+ if (hpi_sample_clock_query_source_index(hSC,
j, HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT,
&source))
break;
@@ -2528,7 +2486,7 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
}
clkcache->count = i;
- asihpi_ctl_init(&snd_control, hpi_ctl, "source");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Source");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ;
snd_control.info = snd_asihpi_clksrc_info;
snd_control.get = snd_asihpi_clksrc_get;
@@ -2538,7 +2496,7 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
if (clkcache->has_local) {
- asihpi_ctl_init(&snd_control, hpi_ctl, "local_rate");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Localrate");
snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ;
snd_control.info = snd_asihpi_clklocal_info;
snd_control.get = snd_asihpi_clklocal_get;
@@ -2549,7 +2507,7 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
return -EINVAL;
}
- asihpi_ctl_init(&snd_control, hpi_ctl, "rate");
+ asihpi_ctl_init(&snd_control, hpi_ctl, "Rate");
snd_control.access =
SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ;
snd_control.info = snd_asihpi_clkrate_info;
@@ -2561,9 +2519,9 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi,
Mixer
------------------------------------------------------------*/
-static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
+static int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
{
- struct snd_card *card = asihpi->card;
+ struct snd_card *card;
unsigned int idx = 0;
unsigned int subindex = 0;
int err;
@@ -2571,10 +2529,11 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
if (snd_BUG_ON(!asihpi))
return -EINVAL;
- strcpy(card->mixername, "asihpi mixer");
+ card = asihpi->card;
+ strscpy(card->mixername, "Asihpi Mixer");
err =
- hpi_mixer_open(ss, asihpi->adapter_index,
+ hpi_mixer_open(asihpi->hpi->adapter->index,
&asihpi->h_mixer);
hpi_handle_error(err);
if (err)
@@ -2585,7 +2544,7 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
for (idx = 0; idx < 2000; idx++) {
err = hpi_mixer_get_control_by_index(
- ss, asihpi->h_mixer,
+ asihpi->h_mixer,
idx,
&hpi_ctl.src_node_type,
&hpi_ctl.src_node_index,
@@ -2596,8 +2555,8 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
if (err) {
if (err == HPI_ERROR_CONTROL_DISABLED) {
if (mixer_dump)
- snd_printk(KERN_INFO
- "disabled HPI control(%d)\n",
+ dev_info(&asihpi->pci->dev,
+ "Disabled HPI Control(%d)\n",
idx);
continue;
} else
@@ -2661,9 +2620,8 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
case HPI_CONTROL_COMPANDER:
default:
if (mixer_dump)
- snd_printk(KERN_INFO
- "untranslated HPI control"
- "(%d) %d %d %d %d %d\n",
+ dev_info(&asihpi->pci->dev,
+ "Untranslated HPI Control (%d) %d %d %d %d %d\n",
idx,
hpi_ctl.control_type,
hpi_ctl.src_node_type,
@@ -2671,14 +2629,14 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi)
hpi_ctl.dst_node_type,
hpi_ctl.dst_node_index);
continue;
- };
+ }
if (err < 0)
return err;
}
if (HPI_ERROR_INVALID_OBJ_INDEX != err)
hpi_handle_error(err);
- snd_printk(KERN_INFO "%d mixer controls found\n", idx);
+ dev_info(&asihpi->pci->dev, "%d mixer controls found\n", idx);
return 0;
}
@@ -2692,49 +2650,53 @@ snd_asihpi_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_card_asihpi *asihpi = entry->private_data;
- u16 version;
u32 h_control;
u32 rate = 0;
u16 source = 0;
+
+ u16 num_outstreams;
+ u16 num_instreams;
+ u16 version;
+ u32 serial_number;
+ u16 type;
+
int err;
snd_iprintf(buffer, "ASIHPI driver proc file\n");
+
+ hpi_handle_error(hpi_adapter_get_info(asihpi->hpi->adapter->index,
+ &num_outstreams, &num_instreams,
+ &version, &serial_number, &type));
+
snd_iprintf(buffer,
- "adapter ID=%4X\n_index=%d\n"
- "num_outstreams=%d\n_num_instreams=%d\n",
- asihpi->type, asihpi->adapter_index,
- asihpi->num_outstreams, asihpi->num_instreams);
+ "Adapter type ASI%4X\nHardware Index %d\n"
+ "%d outstreams\n%d instreams\n",
+ type, asihpi->hpi->adapter->index,
+ num_outstreams, num_instreams);
- version = asihpi->version;
snd_iprintf(buffer,
- "serial#=%d\n_hw version %c%d\nDSP code version %03d\n",
- asihpi->serial_number, ((version >> 3) & 0xf) + 'A',
- version & 0x7,
+ "Serial#%d\nHardware version %c%d\nDSP code version %03d\n",
+ serial_number, ((version >> 3) & 0xf) + 'A', version & 0x7,
((version >> 13) * 100) + ((version >> 7) & 0x3f));
- err = hpi_mixer_get_control(ss, asihpi->h_mixer,
+ err = hpi_mixer_get_control(asihpi->h_mixer,
HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
HPI_CONTROL_SAMPLECLOCK, &h_control);
if (!err) {
- err = hpi_sample_clock_get_sample_rate(ss,
- h_control, &rate);
- err += hpi_sample_clock_get_source(ss, h_control, &source);
+ err = hpi_sample_clock_get_sample_rate(h_control, &rate);
+ err += hpi_sample_clock_get_source(h_control, &source);
if (!err)
- snd_iprintf(buffer, "sample_clock=%d_hz, source %s\n",
+ snd_iprintf(buffer, "Sample Clock %dHz, source %s\n",
rate, sampleclock_sources[source]);
}
-
}
-
-static void __devinit snd_asihpi_proc_init(struct snd_card_asihpi *asihpi)
+static void snd_asihpi_proc_init(struct snd_card_asihpi *asihpi)
{
- struct snd_info_entry *entry;
-
- if (!snd_card_proc_new(asihpi->card, "info", &entry))
- snd_info_set_text_ops(entry, asihpi, snd_asihpi_proc_read);
+ snd_card_ro_proc_new(asihpi->card, "info", asihpi,
+ snd_asihpi_proc_read);
}
/*------------------------------------------------------------
@@ -2771,133 +2733,114 @@ static int snd_asihpi_hpi_ioctl(struct snd_hwdep *hw, struct file *file,
/* results in /dev/snd/hwC#D0 file for each card with index #
also /proc/asound/hwdep will contain '#-00: asihpi (HPI) for each card'
*/
-static int __devinit snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi,
- int device, struct snd_hwdep **rhwdep)
+static int snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi, int device)
{
struct snd_hwdep *hw;
int err;
- if (rhwdep)
- *rhwdep = NULL;
err = snd_hwdep_new(asihpi->card, "HPI", device, &hw);
if (err < 0)
return err;
- strcpy(hw->name, "asihpi (HPI)");
+ strscpy(hw->name, "asihpi (HPI)");
hw->iface = SNDRV_HWDEP_IFACE_LAST;
hw->ops.open = snd_asihpi_hpi_open;
hw->ops.ioctl = snd_asihpi_hpi_ioctl;
hw->ops.release = snd_asihpi_hpi_release;
hw->private_data = asihpi;
- if (rhwdep)
- *rhwdep = hw;
return 0;
}
/*------------------------------------------------------------
CARD
------------------------------------------------------------*/
-static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
- const struct pci_device_id *pci_id)
+static int snd_asihpi_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
{
int err;
-
- u16 version;
- int pcm_substreams;
-
- struct hpi_adapter *hpi_card;
+ struct hpi_adapter *hpi;
struct snd_card *card;
struct snd_card_asihpi *asihpi;
u32 h_control;
u32 h_stream;
+ u32 adapter_index;
static int dev;
if (dev >= SNDRV_CARDS)
return -ENODEV;
- /* Should this be enable[hpi_card->index] ? */
+ /* Should this be enable[hpi->index] ? */
if (!enable[dev]) {
dev++;
return -ENOENT;
}
+ /* Initialise low-level HPI driver */
err = asihpi_adapter_probe(pci_dev, pci_id);
if (err < 0)
return err;
- hpi_card = pci_get_drvdata(pci_dev);
+ hpi = pci_get_drvdata(pci_dev);
+ adapter_index = hpi->adapter->index;
/* first try to give the card the same index as its hardware index */
- err = snd_card_create(hpi_card->index,
- id[hpi_card->index], THIS_MODULE,
- sizeof(struct snd_card_asihpi),
- &card);
+ err = snd_card_new(&pci_dev->dev, adapter_index, id[adapter_index],
+ THIS_MODULE, sizeof(struct snd_card_asihpi), &card);
if (err < 0) {
/* if that fails, try the default index==next available */
- err =
- snd_card_create(index[dev], id[dev],
- THIS_MODULE,
- sizeof(struct snd_card_asihpi),
- &card);
+ err = snd_card_new(&pci_dev->dev, index[dev], id[dev],
+ THIS_MODULE, sizeof(struct snd_card_asihpi),
+ &card);
if (err < 0)
return err;
- snd_printk(KERN_WARNING
- "**** WARNING **** adapter index %d->ALSA index %d\n",
- hpi_card->index, card->number);
+ dev_warn(&pci_dev->dev, "Adapter index %d->ALSA index %d\n",
+ adapter_index, card->number);
}
- asihpi = (struct snd_card_asihpi *) card->private_data;
+ asihpi = card->private_data;
asihpi->card = card;
- asihpi->pci = hpi_card->pci;
- asihpi->adapter_index = hpi_card->index;
- hpi_handle_error(hpi_adapter_get_info(ss,
- asihpi->adapter_index,
- &asihpi->num_outstreams,
- &asihpi->num_instreams,
- &asihpi->version,
- &asihpi->serial_number, &asihpi->type));
-
- version = asihpi->version;
- snd_printk(KERN_INFO "adapter ID=%4X index=%d num_outstreams=%d "
- "num_instreams=%d S/N=%d\n"
- "hw version %c%d DSP code version %03d\n",
- asihpi->type, asihpi->adapter_index,
- asihpi->num_outstreams,
- asihpi->num_instreams, asihpi->serial_number,
- ((version >> 3) & 0xf) + 'A',
- version & 0x7,
- ((version >> 13) * 100) + ((version >> 7) & 0x3f));
-
- pcm_substreams = asihpi->num_outstreams;
- if (pcm_substreams < asihpi->num_instreams)
- pcm_substreams = asihpi->num_instreams;
-
- err = hpi_adapter_get_property(ss, asihpi->adapter_index,
+ asihpi->pci = pci_dev;
+ asihpi->hpi = hpi;
+ hpi->snd_card = card;
+
+ err = hpi_adapter_get_property(adapter_index,
HPI_ADAPTER_PROPERTY_CAPS1,
NULL, &asihpi->support_grouping);
if (err)
asihpi->support_grouping = 0;
- err = hpi_adapter_get_property(ss, asihpi->adapter_index,
+ err = hpi_adapter_get_property(adapter_index,
HPI_ADAPTER_PROPERTY_CAPS2,
&asihpi->support_mrx, NULL);
if (err)
asihpi->support_mrx = 0;
- err = hpi_adapter_get_property(ss, asihpi->adapter_index,
+ err = hpi_adapter_get_property(adapter_index,
HPI_ADAPTER_PROPERTY_INTERVAL,
NULL, &asihpi->update_interval_frames);
if (err)
asihpi->update_interval_frames = 512;
- hpi_handle_error(hpi_instream_open(ss, asihpi->adapter_index,
+ if (hpi->interrupt_mode) {
+ asihpi->pcm_start = snd_card_asihpi_pcm_int_start;
+ asihpi->pcm_stop = snd_card_asihpi_pcm_int_stop;
+ hpi->interrupt_callback = snd_card_asihpi_isr;
+ } else {
+ asihpi->pcm_start = snd_card_asihpi_pcm_timer_start;
+ asihpi->pcm_stop = snd_card_asihpi_pcm_timer_stop;
+ }
+
+ hpi_handle_error(hpi_instream_open(adapter_index,
0, &h_stream));
- err = hpi_instream_host_buffer_free(ss, h_stream);
- asihpi->support_mmap = (!err);
+ err = hpi_instream_host_buffer_free(h_stream);
+ asihpi->can_dma = (!err);
- hpi_handle_error(hpi_instream_close(ss, h_stream));
+ hpi_handle_error(hpi_instream_close(h_stream));
- err = hpi_adapter_get_property(ss, asihpi->adapter_index,
+ if (!asihpi->can_dma)
+ asihpi->update_interval_frames *= 2;
+
+ err = hpi_adapter_get_property(adapter_index,
HPI_ADAPTER_PROPERTY_CURCHANNELS,
&asihpi->in_max_chans, &asihpi->out_max_chans);
if (err) {
@@ -2905,69 +2848,83 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev,
asihpi->out_max_chans = 2;
}
- snd_printk(KERN_INFO "supports mmap:%d grouping:%d mrx:%d\n",
- asihpi->support_mmap,
+ if (asihpi->out_max_chans > 2) { /* assume LL mode */
+ asihpi->out_min_chans = asihpi->out_max_chans;
+ asihpi->in_min_chans = asihpi->in_max_chans;
+ asihpi->support_grouping = 0;
+ } else {
+ asihpi->out_min_chans = 1;
+ asihpi->in_min_chans = 1;
+ }
+
+ dev_info(&pci_dev->dev, "Has dma:%d, grouping:%d, mrx:%d, uif:%d\n",
+ asihpi->can_dma,
asihpi->support_grouping,
- asihpi->support_mrx
+ asihpi->support_mrx,
+ asihpi->update_interval_frames
);
-
- err = snd_card_asihpi_pcm_new(asihpi, 0, pcm_substreams);
+ err = snd_card_asihpi_pcm_new(asihpi, 0);
if (err < 0) {
- snd_printk(KERN_ERR "pcm_new failed\n");
+ dev_err(&pci_dev->dev, "pcm_new failed\n");
goto __nodev;
}
err = snd_card_asihpi_mixer_new(asihpi);
if (err < 0) {
- snd_printk(KERN_ERR "mixer_new failed\n");
+ dev_err(&pci_dev->dev, "mixer_new failed\n");
goto __nodev;
}
- err = hpi_mixer_get_control(ss, asihpi->h_mixer,
+ err = hpi_mixer_get_control(asihpi->h_mixer,
HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0,
HPI_CONTROL_SAMPLECLOCK, &h_control);
if (!err)
err = hpi_sample_clock_set_local_rate(
- ss, h_control, adapter_fs);
+ h_control, adapter_fs);
snd_asihpi_proc_init(asihpi);
/* always create, can be enabled or disabled dynamically
by enable_hwdep module param*/
- snd_asihpi_hpi_new(asihpi, 0, NULL);
+ snd_asihpi_hpi_new(asihpi, 0);
- if (asihpi->support_mmap)
- strcpy(card->driver, "ASIHPI-MMAP");
- else
- strcpy(card->driver, "ASIHPI");
+ strscpy(card->driver, "ASIHPI");
- sprintf(card->shortname, "AudioScience ASI%4X", asihpi->type);
+ sprintf(card->shortname, "AudioScience ASI%4X",
+ asihpi->hpi->adapter->type);
sprintf(card->longname, "%s %i",
- card->shortname, asihpi->adapter_index);
+ card->shortname, adapter_index);
err = snd_card_register(card);
+
if (!err) {
- hpi_card->snd_card_asihpi = card;
dev++;
return 0;
}
__nodev:
snd_card_free(card);
- snd_printk(KERN_ERR "snd_asihpi_probe error %d\n", err);
+ dev_err(&pci_dev->dev, "snd_asihpi_probe error %d\n", err);
return err;
}
-static void __devexit snd_asihpi_remove(struct pci_dev *pci_dev)
+static void snd_asihpi_remove(struct pci_dev *pci_dev)
{
- struct hpi_adapter *hpi_card = pci_get_drvdata(pci_dev);
+ struct hpi_adapter *hpi = pci_get_drvdata(pci_dev);
- snd_card_free(hpi_card->snd_card_asihpi);
- hpi_card->snd_card_asihpi = NULL;
+ /* Stop interrupts */
+ if (hpi->interrupt_mode) {
+ hpi->interrupt_callback = NULL;
+ hpi_handle_error(hpi_adapter_set_property(hpi->adapter->index,
+ HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0));
+ }
+
+ snd_card_free(hpi->snd_card);
+ hpi->snd_card = NULL;
asihpi_adapter_remove(pci_dev);
}
-static DEFINE_PCI_DEVICE_TABLE(asihpi_pci_tbl) = {
+static const struct pci_device_id asihpi_pci_tbl[] = {
{HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_DSP6205,
HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0,
(kernel_ulong_t)HPI_6205},
@@ -2979,14 +2936,10 @@ static DEFINE_PCI_DEVICE_TABLE(asihpi_pci_tbl) = {
MODULE_DEVICE_TABLE(pci, asihpi_pci_tbl);
static struct pci_driver driver = {
- .name = "asihpi",
+ .name = KBUILD_MODNAME,
.id_table = asihpi_pci_tbl,
.probe = snd_asihpi_probe,
- .remove = __devexit_p(snd_asihpi_remove),
-#ifdef CONFIG_PM
-/* .suspend = snd_asihpi_suspend,
- .resume = snd_asihpi_resume, */
-#endif
+ .remove = snd_asihpi_remove,
};
static int __init snd_asihpi_init(void)
diff --git a/sound/pci/asihpi/hpi.h b/sound/pci/asihpi/hpi.h
index 23399d02f666..04a5cf6572cd 100644
--- a/sound/pci/asihpi/hpi.h
+++ b/sound/pci/asihpi/hpi.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/** \file hpi.h
@@ -24,46 +13,20 @@
The HPI is a low-level hardware abstraction layer to all
AudioScience digital audio adapters
-*/
-/*
- You must define one operating system that the HPI is to be compiled under
- HPI_OS_WIN32_USER 32bit Windows
- HPI_OS_DSP_C6000 DSP TI C6000 (automatically set)
- HPI_OS_WDM Windows WDM kernel driver
- HPI_OS_LINUX Linux userspace
- HPI_OS_LINUX_KERNEL Linux kernel (automatically set)
(C) Copyright AudioScience Inc. 1998-2010
-******************************************************************************/
-#ifndef _HPI_H_
-#define _HPI_H_
-/* HPI Version
-If HPI_VER_MINOR is odd then its a development release not intended for the
-public. If HPI_VER_MINOR is even then is a release version
-i.e 3.05.02 is a development version
*/
-#define HPI_VERSION_CONSTRUCTOR(maj, min, rel) \
- ((maj << 16) + (min << 8) + rel)
-#define HPI_VER_MAJOR(v) ((int)(v >> 16))
-#define HPI_VER_MINOR(v) ((int)((v >> 8) & 0xFF))
-#define HPI_VER_RELEASE(v) ((int)(v & 0xFF))
-
-/* Use single digits for versions less that 10 to avoid octal. */
-#define HPI_VER HPI_VERSION_CONSTRUCTOR(4L, 4, 1)
-#define HPI_VER_STRING "4.04.01"
-
-/* Library version as documented in hpi-api-versions.txt */
-#define HPI_LIB_VER HPI_VERSION_CONSTRUCTOR(9, 0, 0)
+#ifndef _HPI_H_
+#define _HPI_H_
#include <linux/types.h>
-#define HPI_EXCLUDE_DEPRECATED
+#define HPI_BUILD_KERNEL_MODE
/******************************************************************************/
-/******************************************************************************/
/******** HPI API DEFINITIONS *****/
/******************************************************************************/
-/******************************************************************************/
+
/*******************************************/
/** Audio format types
\ingroup stream
@@ -174,7 +137,6 @@ The range is +1.0 to -1.0, which corresponds to digital fullscale.
HPI_FORMAT_UNDEFINED = 0xffff
};
-/******************************************* in/out Stream states */
/*******************************************/
/** Stream States
\ingroup stream
@@ -194,7 +156,7 @@ enum HPI_STREAM_STATES {
cards to be ready. */
HPI_STATE_WAIT = 6
};
-/******************************************* mixer source node types */
+/*******************************************/
/** Source node types
\ingroup mixer
*/
@@ -219,12 +181,18 @@ enum HPI_SOURCENODES {
HPI_SOURCENODE_COBRANET = 109,
HPI_SOURCENODE_ANALOG = 110, /**< analog input node. */
HPI_SOURCENODE_ADAPTER = 111, /**< adapter node. */
+ /** RTP stream input node - This node is a destination for
+ packets of RTP audio samples from other devices. */
+ HPI_SOURCENODE_RTP_DESTINATION = 112,
+ HPI_SOURCENODE_INTERNAL = 113, /**< node internal to the device. */
+ HPI_SOURCENODE_AVB = 114, /**< AVB input stream */
+ HPI_SOURCENODE_BLULINK = 115, /**< BLU-link input channel */
/* !!!Update this AND hpidebug.h if you add a new sourcenode type!!! */
- HPI_SOURCENODE_LAST_INDEX = 111 /**< largest ID */
+ HPI_SOURCENODE_LAST_INDEX = 115 /**< largest ID */
/* AX6 max sourcenode types = 15 */
};
-/******************************************* mixer dest node types */
+/*******************************************/
/** Destination node types
\ingroup mixer
*/
@@ -236,7 +204,7 @@ enum HPI_DESTNODES {
HPI_DESTNODE_NONE = 200,
/** In Stream (Record) node. */
HPI_DESTNODE_ISTREAM = 201,
- HPI_DESTNODE_LINEOUT = 202, /**< line out node. */
+ HPI_DESTNODE_LINEOUT = 202, /**< line out node. */
HPI_DESTNODE_AESEBU_OUT = 203, /**< AES/EBU output node. */
HPI_DESTNODE_RF = 204, /**< RF output node. */
HPI_DESTNODE_SPEAKER = 205, /**< speaker output node. */
@@ -244,9 +212,14 @@ enum HPI_DESTNODES {
Audio samples from the device are sent out on the Cobranet network.*/
HPI_DESTNODE_COBRANET = 206,
HPI_DESTNODE_ANALOG = 207, /**< analog output node. */
-
+ /** RTP stream output node - This node is a source for
+ packets of RTP audio samples that are sent to other devices. */
+ HPI_DESTNODE_RTP_SOURCE = 208,
+ HPI_DESTNODE_AVB = 209, /**< AVB output stream */
+ HPI_DESTNODE_INTERNAL = 210, /**< node internal to the device. */
+ HPI_DESTNODE_BLULINK = 211, /**< BLU-link output channel. */
/* !!!Update this AND hpidebug.h if you add a new destnode type!!! */
- HPI_DESTNODE_LAST_INDEX = 207 /**< largest ID */
+ HPI_DESTNODE_LAST_INDEX = 211 /**< largest ID */
/* AX6 max destnode types = 15 */
};
@@ -262,11 +235,11 @@ enum HPI_CONTROLS {
HPI_CONTROL_MUTE = 4, /*mute control - not used at present. */
HPI_CONTROL_MULTIPLEXER = 5, /**< multiplexer control. */
- HPI_CONTROL_AESEBU_TRANSMITTER = 6, /**< AES/EBU transmitter control. */
- HPI_CONTROL_AESEBUTX = HPI_CONTROL_AESEBU_TRANSMITTER,
+ HPI_CONTROL_AESEBU_TRANSMITTER = 6, /**< AES/EBU transmitter control */
+ HPI_CONTROL_AESEBUTX = 6, /* HPI_CONTROL_AESEBU_TRANSMITTER */
HPI_CONTROL_AESEBU_RECEIVER = 7, /**< AES/EBU receiver control. */
- HPI_CONTROL_AESEBURX = HPI_CONTROL_AESEBU_RECEIVER,
+ HPI_CONTROL_AESEBURX = 7, /* HPI_CONTROL_AESEBU_RECEIVER */
HPI_CONTROL_LEVEL = 8, /**< level/trim control - works in d_bu. */
HPI_CONTROL_TUNER = 9, /**< tuner control. */
@@ -281,7 +254,7 @@ enum HPI_CONTROLS {
HPI_CONTROL_SAMPLECLOCK = 17, /**< sample clock control. */
HPI_CONTROL_MICROPHONE = 18, /**< microphone control. */
HPI_CONTROL_PARAMETRIC_EQ = 19, /**< parametric EQ control. */
- HPI_CONTROL_EQUALIZER = HPI_CONTROL_PARAMETRIC_EQ,
+ HPI_CONTROL_EQUALIZER = 19, /*HPI_CONTROL_PARAMETRIC_EQ */
HPI_CONTROL_COMPANDER = 20, /**< compander control. */
HPI_CONTROL_COBRANET = 21, /**< cobranet control. */
@@ -296,10 +269,7 @@ enum HPI_CONTROLS {
/* WARNING types 256 or greater impact bit packing in all AX6 DSP code */
};
-/* Shorthand names that match attribute names */
-
-/******************************************* ADAPTER ATTRIBUTES ****/
-
+/*******************************************/
/** Adapter properties
These are used in HPI_AdapterSetProperty() and HPI_AdapterGetProperty()
\ingroup adapter
@@ -330,12 +300,21 @@ by the driver and is not passed on to the DSP at all.
Indicates the state of the adapter's SSX2 setting. This setting is stored in
non-volatile memory on the adapter. A typical call sequence would be to use
HPI_ADAPTER_PROPERTY_SSX2_SETTING to set SSX2 on the adapter and then to reload
-the driver. The driver would query HPI_ADAPTER_PROPERTY_SSX2_SETTING during startup
-and if SSX2 is set, it would then call HPI_ADAPTER_PROPERTY_ENABLE_SSX2 to enable
-SSX2 stream mapping within the kernel level of the driver.
+the driver. The driver would query HPI_ADAPTER_PROPERTY_SSX2_SETTING during
+startup and if SSX2 is set, it would then call HPI_ADAPTER_PROPERTY_ENABLE_SSX2
+to enable SSX2 stream mapping within the kernel level of the driver.
*/
HPI_ADAPTER_PROPERTY_SSX2_SETTING = 4,
+/** Enables/disables PCI(e) IRQ.
+A setting of 0 indicates that no interrupts are being generated. A DSP boot
+this property is set to 0. Setting to a non-zero value specifies the number
+of frames of audio that should be processed between interrupts. This property
+should be set to multiple of the mixer interval as read back from the
+HPI_ADAPTER_PROPERTY_INTERVAL property.
+*/
+ HPI_ADAPTER_PROPERTY_IRQ_RATE = 5,
+
/** Base number for readonly properties */
HPI_ADAPTER_PROPERTY_READONLYBASE = 256,
@@ -440,21 +419,42 @@ return value is true (1) or false (0). If the current adapter
mode is MONO SSX2 is disabled, even though this property will
return true.
*/
- HPI_ADAPTER_PROPERTY_SUPPORTS_SSX2 = 271
+ HPI_ADAPTER_PROPERTY_SUPPORTS_SSX2 = 271,
+/** Readonly supports PCI(e) IRQ.
+Indicates that the adapter in it's current mode supports interrupts
+across the host bus. Note, this does not imply that interrupts are
+enabled. Instead it indicates that they can be enabled.
+*/
+ HPI_ADAPTER_PROPERTY_SUPPORTS_IRQ = 272,
+/** Readonly supports firmware updating.
+Indicates that the adapter implements an interface to update firmware
+on the adapter.
+*/
+ HPI_ADAPTER_PROPERTY_SUPPORTS_FW_UPDATE = 273,
+/** Readonly Firmware IDs
+Identifiy firmware independent of individual adapter type.
+May be used as a filter for firmware update images.
+Property 1 = Bootloader ID
+Property 2 = Main program ID
+*/
+ HPI_ADAPTER_PROPERTY_FIRMWARE_ID = 274
};
/** Adapter mode commands
-Used in wQueryOrSet field of HPI_AdapterSetModeEx().
+Used in wQueryOrSet parameter of HPI_AdapterSetModeEx().
\ingroup adapter
*/
enum HPI_ADAPTER_MODE_CMDS {
+ /** Set the mode to the given parameter */
HPI_ADAPTER_MODE_SET = 0,
+ /** Return 0 or error depending whether mode is valid,
+ but don't set the mode */
HPI_ADAPTER_MODE_QUERY = 1
};
/** Adapter Modes
- These are used by HPI_AdapterSetModeEx()
+ These are used by HPI_AdapterSetModeEx()
\warning - more than 16 possible modes breaks
a bitmask in the Windows WAVE DLL
@@ -625,14 +625,17 @@ enum HPI_MIXER_STORE_COMMAND {
HPI_MIXER_STORE_ENABLE = 4,
/** Disable auto storage of some control settings. */
HPI_MIXER_STORE_DISABLE = 5,
-/** Save the attributes of a single control. */
+/** Unimplemented - save the attributes of a single control. */
HPI_MIXER_STORE_SAVE_SINGLE = 6
};
-/************************************* CONTROL ATTRIBUTE VALUES ****/
+/****************************/
+/* CONTROL ATTRIBUTE VALUES */
+/****************************/
+
/** Used by mixer plugin enable functions
-E.g. HPI_ParametricEQ_SetState()
+E.g. HPI_ParametricEq_SetState()
\ingroup mixer
*/
enum HPI_SWITCH_STATES {
@@ -641,6 +644,7 @@ enum HPI_SWITCH_STATES {
};
/* Volume control special gain values */
+
/** volumes units are 100ths of a dB
\ingroup volume
*/
@@ -650,6 +654,11 @@ enum HPI_SWITCH_STATES {
*/
#define HPI_GAIN_OFF (-100 * HPI_UNITS_PER_dB)
+/** channel mask specifying all channels
+\ingroup volume
+*/
+#define HPI_BITMASK_ALL_CHANNELS (0xFFFFFFFF)
+
/** value returned for no signal
\ingroup meter
*/
@@ -667,7 +676,7 @@ enum HPI_VOLUME_AUTOFADES {
/** The physical encoding format of the AESEBU I/O.
-Used in HPI_AESEBU_Transmitter_SetFormat(), HPI_AESEBU_Receiver_SetFormat()
+Used in HPI_Aesebu_Transmitter_SetFormat(), HPI_Aesebu_Receiver_SetFormat()
along with related Get and Query functions
\ingroup aestx
*/
@@ -680,7 +689,7 @@ enum HPI_AESEBU_FORMATS {
/** AES/EBU error status bits
-Returned by HPI_AESEBU_Receiver_GetErrorStatus()
+Returned by HPI_Aesebu_Receiver_GetErrorStatus()
\ingroup aesrx
*/
enum HPI_AESEBU_ERRORS {
@@ -709,7 +718,7 @@ enum HPI_AESEBU_ERRORS {
#define HPI_PAD_TITLE_LEN 64
/** The text string containing the comment. */
#define HPI_PAD_COMMENT_LEN 256
-/** The PTY when the tuner has not recieved any PTY. */
+/** The PTY when the tuner has not received any PTY. */
#define HPI_PAD_PROGRAM_TYPE_INVALID 0xffff
/** \} */
@@ -737,7 +746,8 @@ enum HPI_TUNER_BAND {
HPI_TUNER_BAND_TV_PAL_I = 7, /**< PAL-I TV band*/
HPI_TUNER_BAND_TV_PAL_DK = 8, /**< PAL-D/K TV band*/
HPI_TUNER_BAND_TV_SECAM_L = 9, /**< SECAM-L TV band*/
- HPI_TUNER_BAND_LAST = 9 /**< the index of the last tuner band. */
+ HPI_TUNER_BAND_DAB = 10,
+ HPI_TUNER_BAND_LAST = 10 /**< the index of the last tuner band. */
};
/** Tuner mode attributes
@@ -767,14 +777,6 @@ enum HPI_TUNER_MODE_VALUES {
HPI_TUNER_MODE_RDS_RBDS = 2 /**< RDS - RBDS mode */
};
-/** Tuner Level settings
-\ingroup tuner
-*/
-enum HPI_TUNER_LEVEL {
- HPI_TUNER_LEVEL_AVERAGE = 0,
- HPI_TUNER_LEVEL_RAW = 1
-};
-
/** Tuner Status Bits
These bitfield values are returned by a call to HPI_Tuner_GetStatus().
@@ -783,13 +785,13 @@ Multiple fields are returned from a single call.
*/
enum HPI_TUNER_STATUS_BITS {
HPI_TUNER_VIDEO_COLOR_PRESENT = 0x0001, /**< video color is present. */
- HPI_TUNER_VIDEO_IS_60HZ = 0x0020, /**< 60 hz video detected. */
- HPI_TUNER_VIDEO_HORZ_SYNC_MISSING = 0x0040, /**< video HSYNC is missing. */
- HPI_TUNER_VIDEO_STATUS_VALID = 0x0100, /**< video status is valid. */
- HPI_TUNER_PLL_LOCKED = 0x1000, /**< the tuner's PLL is locked. */
- HPI_TUNER_FM_STEREO = 0x2000, /**< tuner reports back FM stereo. */
- HPI_TUNER_DIGITAL = 0x0200, /**< tuner reports digital programming. */
- HPI_TUNER_MULTIPROGRAM = 0x0400 /**< tuner reports multiple programs. */
+ HPI_TUNER_VIDEO_IS_60HZ = 0x0020, /**< 60 hz video detected. */
+ HPI_TUNER_VIDEO_HORZ_SYNC_MISSING = 0x0040, /**< video HSYNC is missing. */
+ HPI_TUNER_VIDEO_STATUS_VALID = 0x0100, /**< video status is valid. */
+ HPI_TUNER_DIGITAL = 0x0200, /**< tuner reports digital programming. */
+ HPI_TUNER_MULTIPROGRAM = 0x0400, /**< tuner reports multiple programs. */
+ HPI_TUNER_PLL_LOCKED = 0x1000, /**< the tuner's PLL is locked. */
+ HPI_TUNER_FM_STEREO = 0x2000 /**< tuner reports back FM stereo. */
};
/** Channel Modes
@@ -835,11 +837,13 @@ enum HPI_SAMPLECLOCK_SOURCES {
HPI_SAMPLECLOCK_SOURCE_NETWORK = 8,
/** From previous adjacent module (ASI2416 only)*/
HPI_SAMPLECLOCK_SOURCE_PREV_MODULE = 10,
+/** Blu link sample clock*/
+ HPI_SAMPLECLOCK_SOURCE_BLULINK = 11,
/*! Update this if you add a new clock source.*/
- HPI_SAMPLECLOCK_SOURCE_LAST = 10
+ HPI_SAMPLECLOCK_SOURCE_LAST = 11
};
-/** Equalizer filter types. Used by HPI_ParametricEQ_SetBand()
+/** Equalizer filter types. Used by HPI_ParametricEq_SetBand()
\ingroup parmeq
*/
enum HPI_FILTER_TYPE {
@@ -882,7 +886,7 @@ enum HPI_ERROR_CODES {
HPI_ERROR_INVALID_OBJ = 101,
/** Function does not exist. */
HPI_ERROR_INVALID_FUNC = 102,
- /** The specified object (adapter/Stream) does not exist. */
+ /** The specified object does not exist. */
HPI_ERROR_INVALID_OBJ_INDEX = 103,
/** Trying to access an object that has not been opened yet. */
HPI_ERROR_OBJ_NOT_OPEN = 104,
@@ -890,8 +894,7 @@ enum HPI_ERROR_CODES {
HPI_ERROR_OBJ_ALREADY_OPEN = 105,
/** PCI, ISA resource not valid. */
HPI_ERROR_INVALID_RESOURCE = 106,
- /** GetInfo call from SubSysFindAdapters failed. */
- HPI_ERROR_SUBSYSFINDADAPTERS_GETINFO = 107,
+ /* HPI_ERROR_SUBSYSFINDADAPTERS_GETINFO= 107 */
/** Default response was never updated with actual error code. */
HPI_ERROR_INVALID_RESPONSE = 108,
/** wSize field of response was not updated,
@@ -899,38 +902,44 @@ enum HPI_ERROR_CODES {
HPI_ERROR_PROCESSING_MESSAGE = 109,
/** The network did not respond in a timely manner. */
HPI_ERROR_NETWORK_TIMEOUT = 110,
- /** An HPI handle is invalid (uninitialised?). */
+ /* An HPI handle is invalid (uninitialised?). */
HPI_ERROR_INVALID_HANDLE = 111,
/** A function or attribute has not been implemented yet. */
HPI_ERROR_UNIMPLEMENTED = 112,
- /** There are too many clients attempting to access a network resource. */
+ /** There are too many clients attempting
+ to access a network resource. */
HPI_ERROR_NETWORK_TOO_MANY_CLIENTS = 113,
- /** Response buffer passed to HPI_Message was smaller than returned response */
+ /** Response buffer passed to HPI_Message
+ was smaller than returned response.
+ wSpecificError field of hpi response contains the required size.
+ */
HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL = 114,
/** The returned response did not match the sent message */
HPI_ERROR_RESPONSE_MISMATCH = 115,
+ /** A control setting that should have been cached was not. */
+ HPI_ERROR_CONTROL_CACHING = 116,
+ /** A message buffer in the path to the adapter was smaller
+ than the message size.
+ wSpecificError field of hpi response contains the actual size.
+ */
+ HPI_ERROR_MESSAGE_BUFFER_TOO_SMALL = 117,
- /** Too many adapters.*/
- HPI_ERROR_TOO_MANY_ADAPTERS = 200,
+ /* HPI_ERROR_TOO_MANY_ADAPTERS= 200 */
/** Bad adpater. */
HPI_ERROR_BAD_ADAPTER = 201,
/** Adapter number out of range or not set properly. */
HPI_ERROR_BAD_ADAPTER_NUMBER = 202,
/** 2 adapters with the same adapter number. */
- HPI_DUPLICATE_ADAPTER_NUMBER = 203,
- /** DSP code failed to bootload. */
+ HPI_ERROR_DUPLICATE_ADAPTER_NUMBER = 203,
+ /** DSP code failed to bootload. Usually a DSP memory test failure. */
HPI_ERROR_DSP_BOOTLOAD = 204,
- /** Adapter failed DSP code self test. */
- HPI_ERROR_DSP_SELFTEST = 205,
/** Couldn't find or open the DSP code file. */
HPI_ERROR_DSP_FILE_NOT_FOUND = 206,
/** Internal DSP hardware error. */
HPI_ERROR_DSP_HARDWARE = 207,
- /** Could not allocate memory in DOS. */
- HPI_ERROR_DOS_MEMORY_ALLOC = 208,
/** Could not allocate memory */
HPI_ERROR_MEMORY_ALLOC = 208,
- /** Failed to correctly load/config PLD .*/
+ /** Failed to correctly load/config PLD. (unused) */
HPI_ERROR_PLD_LOAD = 209,
/** Unexpected end of file, block length too big etc. */
HPI_ERROR_DSP_FILE_FORMAT = 210,
@@ -939,8 +948,7 @@ enum HPI_ERROR_CODES {
HPI_ERROR_DSP_FILE_ACCESS_DENIED = 211,
/** First DSP code section header not found in DSP file. */
HPI_ERROR_DSP_FILE_NO_HEADER = 212,
- /** File read operation on DSP code file failed. */
- HPI_ERROR_DSP_FILE_READ_ERROR = 213,
+ /* HPI_ERROR_DSP_FILE_READ_ERROR= 213, */
/** DSP code for adapter family not found. */
HPI_ERROR_DSP_SECTION_NOT_FOUND = 214,
/** Other OS specific error opening DSP file. */
@@ -950,23 +958,24 @@ enum HPI_ERROR_CODES {
/** DSP code section header had size == 0. */
HPI_ERROR_DSP_FILE_NULL_HEADER = 217,
- /** Base number for flash errors. */
- HPI_ERROR_FLASH = 220,
+ /* HPI_ERROR_FLASH = 220, */
/** Flash has bad checksum */
- HPI_ERROR_BAD_CHECKSUM = (HPI_ERROR_FLASH + 1),
- HPI_ERROR_BAD_SEQUENCE = (HPI_ERROR_FLASH + 2),
- HPI_ERROR_FLASH_ERASE = (HPI_ERROR_FLASH + 3),
- HPI_ERROR_FLASH_PROGRAM = (HPI_ERROR_FLASH + 4),
- HPI_ERROR_FLASH_VERIFY = (HPI_ERROR_FLASH + 5),
- HPI_ERROR_FLASH_TYPE = (HPI_ERROR_FLASH + 6),
- HPI_ERROR_FLASH_START = (HPI_ERROR_FLASH + 7),
+ HPI_ERROR_BAD_CHECKSUM = 221,
+ HPI_ERROR_BAD_SEQUENCE = 222,
+ HPI_ERROR_FLASH_ERASE = 223,
+ HPI_ERROR_FLASH_PROGRAM = 224,
+ HPI_ERROR_FLASH_VERIFY = 225,
+ HPI_ERROR_FLASH_TYPE = 226,
+ HPI_ERROR_FLASH_START = 227,
+ HPI_ERROR_FLASH_READ = 228,
+ HPI_ERROR_FLASH_READ_NO_FILE = 229,
+ HPI_ERROR_FLASH_SIZE = 230,
/** Reserved for OEMs. */
HPI_ERROR_RESERVED_1 = 290,
- /** Stream does not exist. */
- HPI_ERROR_INVALID_STREAM = 300,
+ /* HPI_ERROR_INVALID_STREAM = 300 use HPI_ERROR_INVALID_OBJ_INDEX */
/** Invalid compression format. */
HPI_ERROR_INVALID_FORMAT = 301,
/** Invalid format samplerate */
@@ -977,21 +986,19 @@ enum HPI_ERROR_CODES {
HPI_ERROR_INVALID_BITRATE = 304,
/** Invalid datasize used for stream read/write. */
HPI_ERROR_INVALID_DATASIZE = 305,
- /** Stream buffer is full during stream write. */
- HPI_ERROR_BUFFER_FULL = 306,
- /** Stream buffer is empty during stream read. */
- HPI_ERROR_BUFFER_EMPTY = 307,
- /** Invalid datasize used for stream read/write. */
- HPI_ERROR_INVALID_DATA_TRANSFER = 308,
+ /* HPI_ERROR_BUFFER_FULL = 306 use HPI_ERROR_INVALID_DATASIZE */
+ /* HPI_ERROR_BUFFER_EMPTY = 307 use HPI_ERROR_INVALID_DATASIZE */
+ /** Null data pointer used for stream read/write. */
+ HPI_ERROR_INVALID_DATA_POINTER = 308,
/** Packet ordering error for stream read/write. */
HPI_ERROR_INVALID_PACKET_ORDER = 309,
/** Object can't do requested operation in its current
- state, eg set format, change rec mux state while recording.*/
+ state, eg set format, change rec mux state while recording.*/
HPI_ERROR_INVALID_OPERATION = 310,
- /** Where an SRG is shared amongst streams, an incompatible samplerate is one
- that is different to any currently playing or recording stream. */
+ /** Where a SRG is shared amongst streams, an incompatible samplerate
+ is one that is different to any currently active stream. */
HPI_ERROR_INCOMPATIBLE_SAMPLERATE = 311,
/** Adapter mode is illegal.*/
HPI_ERROR_BAD_ADAPTER_MODE = 312,
@@ -1004,6 +1011,10 @@ enum HPI_ERROR_CODES {
HPI_ERROR_NO_INTERADAPTER_GROUPS = 314,
/** Streams on different DSPs cannot be grouped. */
HPI_ERROR_NO_INTERDSP_GROUPS = 315,
+ /** Stream wait cancelled before threshold reached. */
+ HPI_ERROR_WAIT_CANCELLED = 316,
+ /** A character string is invalid. */
+ HPI_ERROR_INVALID_STRING = 317,
/** Invalid mixer node for this adapter. */
HPI_ERROR_INVALID_NODE = 400,
@@ -1017,6 +1028,7 @@ enum HPI_ERROR_CODES {
HPI_ERROR_CONTROL_DISABLED = 404,
/** I2C transaction failed due to a missing ACK. */
HPI_ERROR_CONTROL_I2C_MISSING_ACK = 405,
+ HPI_ERROR_I2C_MISSING_ACK = 405,
/** Control is busy, or coming out of
reset and cannot be accessed at this time. */
HPI_ERROR_CONTROL_NOT_READY = 407,
@@ -1027,14 +1039,18 @@ enum HPI_ERROR_CODES {
HPI_ERROR_NVMEM_FAIL = 452,
/** I2C */
- HPI_ERROR_I2C_MISSING_ACK = HPI_ERROR_CONTROL_I2C_MISSING_ACK,
HPI_ERROR_I2C_BAD_ADR = 460,
- /** Entity errors */
+ /** Entity type did not match requested type */
HPI_ERROR_ENTITY_TYPE_MISMATCH = 470,
+ /** Entity item count did not match requested count */
HPI_ERROR_ENTITY_ITEM_COUNT = 471,
+ /** Entity type is not one of the valid types */
HPI_ERROR_ENTITY_TYPE_INVALID = 472,
+ /** Entity role is not one of the valid roles */
HPI_ERROR_ENTITY_ROLE_INVALID = 473,
+ /** Entity size doesn't match target size */
+ HPI_ERROR_ENTITY_SIZE_MISMATCH = 474,
/* AES18 specific errors were 500..507 */
@@ -1044,18 +1060,24 @@ enum HPI_ERROR_CODES {
/** hpioct32.c can't obtain mutex */
HPI_ERROR_MUTEX_TIMEOUT = 700,
- /** errors from HPI backends have values >= this */
+ /** Backend errors used to be greater than this.
+ \deprecated Now, all backends return only errors defined here in hpi.h
+ */
HPI_ERROR_BACKEND_BASE = 900,
- /** indicates a cached u16 value is invalid. */
- HPI_ERROR_ILLEGAL_CACHE_VALUE = 0xffff
+ /** Communication with DSP failed */
+ HPI_ERROR_DSP_COMMUNICATION = 900
+ /* Note that the dsp communication error is set to this value so that
+ it remains compatible with any software that expects such errors
+ to be backend errors i.e. >= 900.
+ Do not define any new error codes with values > 900.
+ */
};
/** \defgroup maximums HPI maximum values
\{
*/
-/** Maximum number of adapters per HPI sub-system
- WARNING: modifying this value changes the response structure size.*/
+/** Maximum number of PCI HPI adapters */
#define HPI_MAX_ADAPTERS 20
/** Maximum number of in or out streams per adapter */
#define HPI_MAX_STREAMS 16
@@ -1066,6 +1088,9 @@ enum HPI_ERROR_CODES {
#define HPI_MAX_ANC_BYTES_PER_FRAME (64)
#define HPI_STRING_LEN 16
+/** Networked adapters have index >= 100 */
+#define HPI_MIN_NETWORK_ADAPTER_IDX 100
+
/** Velocity units */
#define HPI_OSTREAM_VELOCITY_UNITS 4096
/** OutStream timescale units */
@@ -1075,7 +1100,7 @@ enum HPI_ERROR_CODES {
/**\}*/
-/* ////////////////////////////////////////////////////////////////////// */
+/**************/
/* STRUCTURES */
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(push, 1)
@@ -1087,14 +1112,14 @@ enum HPI_ERROR_CODES {
struct hpi_format {
u32 sample_rate;
/**< 11025, 32000, 44100 ... */
- u32 bit_rate; /**< for MPEG */
+ u32 bit_rate; /**< for MPEG */
u32 attributes;
/**< Stereo/JointStereo/Mono */
u16 mode_legacy;
/**< Legacy ancillary mode or idle bit */
- u16 unused; /**< unused */
- u16 channels; /**< 1,2..., (or ancillary mode or idle bit */
- u16 format; /**< HPI_FORMAT_PCM16, _MPEG etc. see #HPI_FORMATS. */
+ u16 unused; /**< Unused */
+ u16 channels; /**< 1,2..., (or ancillary mode or idle bit */
+ u16 format; /**< HPI_FORMAT_PCM16, _MPEG etc. see #HPI_FORMATS. */
};
struct hpi_anc_frame {
@@ -1106,930 +1131,578 @@ struct hpi_anc_frame {
*/
struct hpi_async_event {
u16 event_type; /**< type of event. \sa async_event */
- u16 sequence; /**< sequence number, allows lost event detection */
- u32 state; /**< new state */
- u32 h_object; /**< handle to the object returning the event. */
+ u16 sequence; /**< Sequence number, allows lost event detection */
+ u32 state; /**< New state */
+ u32 h_object; /**< handle to the object returning the event. */
union {
struct {
u16 index; /**< GPIO bit index. */
} gpio;
struct {
u16 node_index; /**< what node is the control on ? */
- u16 node_type; /**< what type of node is the control on ? */
+ u16 node_type; /**< what type of node is the control on ? */
} control;
} u;
};
-/*/////////////////////////////////////////////////////////////////////////// */
-/* Public HPI Entity related definitions */
-
-struct hpi_entity;
-
-enum e_entity_type {
- entity_type_null,
- entity_type_sequence, /* sequence of potentially heterogeneous TLV entities */
-
- entity_type_reference, /* refers to a TLV entity or NULL */
-
- entity_type_int, /* 32 bit */
- entity_type_float, /* ieee754 binary 32 bit encoding */
- entity_type_double,
-
- entity_type_cstring,
- entity_type_octet,
- entity_type_ip4_address,
- entity_type_ip6_address,
- entity_type_mac_address,
-
- LAST_ENTITY_TYPE
-};
-
-enum e_entity_role {
- entity_role_null,
- entity_role_value,
- entity_role_classname,
-
- entity_role_units,
- entity_role_flags,
- entity_role_range,
-
- entity_role_mapping,
- entity_role_enum,
-
- entity_role_instance_of,
- entity_role_depends_on,
- entity_role_member_of_group,
- entity_role_value_constraint,
- entity_role_parameter_port,
-
- entity_role_block,
- entity_role_node_group,
- entity_role_audio_port,
- entity_role_clock_port,
- LAST_ENTITY_ROLE
-};
-
-/* skip host side function declarations for
- DSP compile and documentation extraction */
-
-struct hpi_hsubsys {
- int not_really_used;
-};
-
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(pop)
#endif
-/*////////////////////////////////////////////////////////////////////////// */
+/*****************/
/* HPI FUNCTIONS */
+/*****************/
-/*/////////////////////////// */
-/* DATA and FORMAT and STREAM */
-
+/* Stream */
u16 hpi_stream_estimate_buffer_size(struct hpi_format *pF,
u32 host_polling_rate_in_milli_seconds, u32 *recommended_buffer_size);
-/*/////////// */
-/* SUB SYSTEM */
-struct hpi_hsubsys *hpi_subsys_create(void
- );
-
-void hpi_subsys_free(const struct hpi_hsubsys *ph_subsys);
-
-u16 hpi_subsys_get_version(const struct hpi_hsubsys *ph_subsys,
- u32 *pversion);
-
-u16 hpi_subsys_get_version_ex(const struct hpi_hsubsys *ph_subsys,
- u32 *pversion_ex);
-
-u16 hpi_subsys_get_info(const struct hpi_hsubsys *ph_subsys, u32 *pversion,
- u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length);
-
-u16 hpi_subsys_find_adapters(const struct hpi_hsubsys *ph_subsys,
- u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length);
-
-u16 hpi_subsys_get_num_adapters(const struct hpi_hsubsys *ph_subsys,
- int *pn_num_adapters);
+/*************/
+/* SubSystem */
+/*************/
-u16 hpi_subsys_get_adapter(const struct hpi_hsubsys *ph_subsys, int iterator,
- u32 *padapter_index, u16 *pw_adapter_type);
+u16 hpi_subsys_get_version_ex(u32 *pversion_ex);
-u16 hpi_subsys_ssx2_bypass(const struct hpi_hsubsys *ph_subsys, u16 bypass);
+u16 hpi_subsys_get_num_adapters(int *pn_num_adapters);
-u16 hpi_subsys_set_host_network_interface(const struct hpi_hsubsys *ph_subsys,
- const char *sz_interface);
+u16 hpi_subsys_get_adapter(int iterator, u32 *padapter_index,
+ u16 *pw_adapter_type);
-/*///////// */
-/* ADAPTER */
+/***********/
+/* Adapter */
+/***********/
-u16 hpi_adapter_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index);
+u16 hpi_adapter_open(u16 adapter_index);
-u16 hpi_adapter_close(const struct hpi_hsubsys *ph_subsys, u16 adapter_index);
+u16 hpi_adapter_close(u16 adapter_index);
-u16 hpi_adapter_get_info(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 *pw_num_outstreams, u16 *pw_num_instreams,
- u16 *pw_version, u32 *pserial_number, u16 *pw_adapter_type);
+u16 hpi_adapter_get_info(u16 adapter_index, u16 *pw_num_outstreams,
+ u16 *pw_num_instreams, u16 *pw_version, u32 *pserial_number,
+ u16 *pw_adapter_type);
-u16 hpi_adapter_get_module_by_index(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 module_index, u16 *pw_num_outputs,
- u16 *pw_num_inputs, u16 *pw_version, u32 *pserial_number,
- u16 *pw_module_type, u32 *ph_module);
+u16 hpi_adapter_get_module_by_index(u16 adapter_index, u16 module_index,
+ u16 *pw_num_outputs, u16 *pw_num_inputs, u16 *pw_version,
+ u32 *pserial_number, u16 *pw_module_type, u32 *ph_module);
-u16 hpi_adapter_set_mode(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 adapter_mode);
+u16 hpi_adapter_set_mode(u16 adapter_index, u32 adapter_mode);
-u16 hpi_adapter_set_mode_ex(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 adapter_mode, u16 query_or_set);
+u16 hpi_adapter_set_mode_ex(u16 adapter_index, u32 adapter_mode,
+ u16 query_or_set);
-u16 hpi_adapter_get_mode(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 *padapter_mode);
+u16 hpi_adapter_get_mode(u16 adapter_index, u32 *padapter_mode);
-u16 hpi_adapter_get_assert(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 *assert_present, char *psz_assert,
- u16 *pw_line_number);
+u16 hpi_adapter_set_property(u16 adapter_index, u16 property, u16 paramter1,
+ u16 paramter2);
-u16 hpi_adapter_get_assert_ex(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 *assert_present, char *psz_assert,
- u32 *pline_number, u16 *pw_assert_on_dsp);
+u16 hpi_adapter_get_property(u16 adapter_index, u16 property,
+ u16 *pw_paramter1, u16 *pw_paramter2);
-u16 hpi_adapter_test_assert(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 assert_id);
+u16 hpi_adapter_enumerate_property(u16 adapter_index, u16 index,
+ u16 what_to_enumerate, u16 property_index, u32 *psetting);
+/*************/
+/* OutStream */
+/*************/
+u16 hpi_outstream_open(u16 adapter_index, u16 outstream_index,
+ u32 *ph_outstream);
-u16 hpi_adapter_enable_capability(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 capability, u32 key);
+u16 hpi_outstream_close(u32 h_outstream);
-u16 hpi_adapter_self_test(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index);
+u16 hpi_outstream_get_info_ex(u32 h_outstream, u16 *pw_state,
+ u32 *pbuffer_size, u32 *pdata_to_play, u32 *psamples_played,
+ u32 *pauxiliary_data_to_play);
-u16 hpi_adapter_debug_read(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 dsp_address, char *p_bytes, int *count_bytes);
+u16 hpi_outstream_write_buf(u32 h_outstream, const u8 *pb_write_buf,
+ u32 bytes_to_write, const struct hpi_format *p_format);
-u16 hpi_adapter_set_property(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 property, u16 paramter1, u16 paramter2);
+u16 hpi_outstream_start(u32 h_outstream);
-u16 hpi_adapter_get_property(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 property, u16 *pw_paramter1,
- u16 *pw_paramter2);
+u16 hpi_outstream_wait_start(u32 h_outstream);
-u16 hpi_adapter_enumerate_property(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 index, u16 what_to_enumerate,
- u16 property_index, u32 *psetting);
+u16 hpi_outstream_stop(u32 h_outstream);
-/*////////////// */
-/* NonVol Memory */
-u16 hpi_nv_memory_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u32 *ph_nv_memory, u16 *pw_size_in_bytes);
+u16 hpi_outstream_sinegen(u32 h_outstream);
-u16 hpi_nv_memory_read_byte(const struct hpi_hsubsys *ph_subsys,
- u32 h_nv_memory, u16 index, u16 *pw_data);
+u16 hpi_outstream_reset(u32 h_outstream);
-u16 hpi_nv_memory_write_byte(const struct hpi_hsubsys *ph_subsys,
- u32 h_nv_memory, u16 index, u16 data);
+u16 hpi_outstream_query_format(u32 h_outstream, struct hpi_format *p_format);
-/*////////////// */
-/* Digital I/O */
-u16 hpi_gpio_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u32 *ph_gpio, u16 *pw_number_input_bits, u16 *pw_number_output_bits);
+u16 hpi_outstream_set_format(u32 h_outstream, struct hpi_format *p_format);
-u16 hpi_gpio_read_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
- u16 bit_index, u16 *pw_bit_data);
+u16 hpi_outstream_set_punch_in_out(u32 h_outstream, u32 punch_in_sample,
+ u32 punch_out_sample);
-u16 hpi_gpio_read_all_bits(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
- u16 aw_all_bit_data[4]
- );
-
-u16 hpi_gpio_write_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
- u16 bit_index, u16 bit_data);
-
-u16 hpi_gpio_write_status(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
- u16 aw_all_bit_data[4]
- );
-
-/**********************/
-/* Async Event Object */
-/**********************/
-u16 hpi_async_event_open(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 *ph_async);
-
-u16 hpi_async_event_close(const struct hpi_hsubsys *ph_subsys, u32 h_async);
-
-u16 hpi_async_event_wait(const struct hpi_hsubsys *ph_subsys, u32 h_async,
- u16 maximum_events, struct hpi_async_event *p_events,
- u16 *pw_number_returned);
-
-u16 hpi_async_event_get_count(const struct hpi_hsubsys *ph_subsys,
- u32 h_async, u16 *pw_count);
-
-u16 hpi_async_event_get(const struct hpi_hsubsys *ph_subsys, u32 h_async,
- u16 maximum_events, struct hpi_async_event *p_events,
- u16 *pw_number_returned);
-
-/*/////////// */
-/* WATCH-DOG */
-u16 hpi_watchdog_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u32 *ph_watchdog);
-
-u16 hpi_watchdog_set_time(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog,
- u32 time_millisec);
-
-u16 hpi_watchdog_ping(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog);
-
-/**************/
-/* OUT STREAM */
-/**************/
-u16 hpi_outstream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u16 outstream_index, u32 *ph_outstream);
-
-u16 hpi_outstream_close(const struct hpi_hsubsys *ph_subsys, u32 h_outstream);
-
-u16 hpi_outstream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_to_play,
- u32 *psamples_played, u32 *pauxiliary_data_to_play);
-
-u16 hpi_outstream_write_buf(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, const u8 *pb_write_buf, u32 bytes_to_write,
- const struct hpi_format *p_format);
+u16 hpi_outstream_set_velocity(u32 h_outstream, short velocity);
-u16 hpi_outstream_start(const struct hpi_hsubsys *ph_subsys, u32 h_outstream);
+u16 hpi_outstream_ancillary_reset(u32 h_outstream, u16 mode);
-u16 hpi_outstream_wait_start(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream);
+u16 hpi_outstream_ancillary_get_info(u32 h_outstream, u32 *pframes_available);
-u16 hpi_outstream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_outstream);
-
-u16 hpi_outstream_sinegen(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream);
-
-u16 hpi_outstream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_outstream);
-
-u16 hpi_outstream_query_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, struct hpi_format *p_format);
-
-u16 hpi_outstream_set_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, struct hpi_format *p_format);
-
-u16 hpi_outstream_set_punch_in_out(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 punch_in_sample, u32 punch_out_sample);
-
-u16 hpi_outstream_set_velocity(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, short velocity);
-
-u16 hpi_outstream_ancillary_reset(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u16 mode);
-
-u16 hpi_outstream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 *pframes_available);
-
-u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, struct hpi_anc_frame *p_anc_frame_buffer,
+u16 hpi_outstream_ancillary_read(u32 h_outstream,
+ struct hpi_anc_frame *p_anc_frame_buffer,
u32 anc_frame_buffer_size_in_bytes,
u32 number_of_ancillary_frames_to_read);
-u16 hpi_outstream_set_time_scale(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 time_scaleX10000);
+u16 hpi_outstream_set_time_scale(u32 h_outstream, u32 time_scaleX10000);
-u16 hpi_outstream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 size_in_bytes);
+u16 hpi_outstream_host_buffer_allocate(u32 h_outstream, u32 size_in_bytes);
-u16 hpi_outstream_host_buffer_free(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream);
+u16 hpi_outstream_host_buffer_free(u32 h_outstream);
-u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 h_stream);
+u16 hpi_outstream_group_add(u32 h_outstream, u32 h_stream);
-u16 hpi_outstream_group_get_map(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 *poutstream_map, u32 *pinstream_map);
+u16 hpi_outstream_group_get_map(u32 h_outstream, u32 *poutstream_map,
+ u32 *pinstream_map);
-u16 hpi_outstream_group_reset(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream);
+u16 hpi_outstream_group_reset(u32 h_outstream);
-/*////////// */
-/* IN_STREAM */
-u16 hpi_instream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u16 instream_index, u32 *ph_instream);
+/************/
+/* InStream */
+/************/
+u16 hpi_instream_open(u16 adapter_index, u16 instream_index,
+ u32 *ph_instream);
-u16 hpi_instream_close(const struct hpi_hsubsys *ph_subsys, u32 h_instream);
+u16 hpi_instream_close(u32 h_instream);
-u16 hpi_instream_query_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, const struct hpi_format *p_format);
+u16 hpi_instream_query_format(u32 h_instream,
+ const struct hpi_format *p_format);
-u16 hpi_instream_set_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, const struct hpi_format *p_format);
+u16 hpi_instream_set_format(u32 h_instream,
+ const struct hpi_format *p_format);
-u16 hpi_instream_read_buf(const struct hpi_hsubsys *ph_subsys, u32 h_instream,
- u8 *pb_read_buf, u32 bytes_to_read);
+u16 hpi_instream_read_buf(u32 h_instream, u8 *pb_read_buf, u32 bytes_to_read);
-u16 hpi_instream_start(const struct hpi_hsubsys *ph_subsys, u32 h_instream);
+u16 hpi_instream_start(u32 h_instream);
-u16 hpi_instream_wait_start(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream);
+u16 hpi_instream_wait_start(u32 h_instream);
-u16 hpi_instream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_instream);
+u16 hpi_instream_stop(u32 h_instream);
-u16 hpi_instream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_instream);
+u16 hpi_instream_reset(u32 h_instream);
-u16 hpi_instream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_recorded,
- u32 *psamples_recorded, u32 *pauxiliary_data_recorded);
+u16 hpi_instream_get_info_ex(u32 h_instream, u16 *pw_state, u32 *pbuffer_size,
+ u32 *pdata_recorded, u32 *psamples_recorded,
+ u32 *pauxiliary_data_recorded);
-u16 hpi_instream_ancillary_reset(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u16 bytes_per_frame, u16 mode, u16 alignment,
- u16 idle_bit);
+u16 hpi_instream_ancillary_reset(u32 h_instream, u16 bytes_per_frame,
+ u16 mode, u16 alignment, u16 idle_bit);
-u16 hpi_instream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u32 *pframe_space);
+u16 hpi_instream_ancillary_get_info(u32 h_instream, u32 *pframe_space);
-u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, const struct hpi_anc_frame *p_anc_frame_buffer,
+u16 hpi_instream_ancillary_write(u32 h_instream,
+ const struct hpi_anc_frame *p_anc_frame_buffer,
u32 anc_frame_buffer_size_in_bytes,
u32 number_of_ancillary_frames_to_write);
-u16 hpi_instream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u32 size_in_bytes);
+u16 hpi_instream_host_buffer_allocate(u32 h_instream, u32 size_in_bytes);
-u16 hpi_instream_host_buffer_free(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream);
+u16 hpi_instream_host_buffer_free(u32 h_instream);
-u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u32 h_stream);
+u16 hpi_instream_group_add(u32 h_instream, u32 h_stream);
-u16 hpi_instream_group_get_map(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u32 *poutstream_map, u32 *pinstream_map);
+u16 hpi_instream_group_get_map(u32 h_instream, u32 *poutstream_map,
+ u32 *pinstream_map);
-u16 hpi_instream_group_reset(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream);
+u16 hpi_instream_group_reset(u32 h_instream);
/*********/
-/* MIXER */
+/* Mixer */
/*********/
-u16 hpi_mixer_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u32 *ph_mixer);
-
-u16 hpi_mixer_close(const struct hpi_hsubsys *ph_subsys, u32 h_mixer);
-
-u16 hpi_mixer_get_control(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
- u16 src_node_type, u16 src_node_type_index, u16 dst_node_type,
- u16 dst_node_type_index, u16 control_type, u32 *ph_control);
-
-u16 hpi_mixer_get_control_by_index(const struct hpi_hsubsys *ph_subsys,
- u32 h_mixer, u16 control_index, u16 *pw_src_node_type,
- u16 *pw_src_node_index, u16 *pw_dst_node_type, u16 *pw_dst_node_index,
- u16 *pw_control_type, u32 *ph_control);
-
-u16 hpi_mixer_store(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
- enum HPI_MIXER_STORE_COMMAND command, u16 index);
-/*************************/
-/* mixer CONTROLS */
-/*************************/
-/*************************/
-/* volume control */
-/*************************/
-u16 hpi_volume_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_gain0_01dB[HPI_MAX_CHANNELS]
+u16 hpi_mixer_open(u16 adapter_index, u32 *ph_mixer);
+
+u16 hpi_mixer_close(u32 h_mixer);
+
+u16 hpi_mixer_get_control(u32 h_mixer, u16 src_node_type,
+ u16 src_node_type_index, u16 dst_node_type, u16 dst_node_type_index,
+ u16 control_type, u32 *ph_control);
+
+u16 hpi_mixer_get_control_by_index(u32 h_mixer, u16 control_index,
+ u16 *pw_src_node_type, u16 *pw_src_node_index, u16 *pw_dst_node_type,
+ u16 *pw_dst_node_index, u16 *pw_control_type, u32 *ph_control);
+
+u16 hpi_mixer_store(u32 h_mixer, enum HPI_MIXER_STORE_COMMAND command,
+ u16 index);
+/************/
+/* Controls */
+/************/
+/******************/
+/* Volume control */
+/******************/
+u16 hpi_volume_set_gain(u32 h_control, short an_gain0_01dB[HPI_MAX_CHANNELS]
);
-u16 hpi_volume_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
+u16 hpi_volume_get_gain(u32 h_control,
short an_gain0_01dB_out[HPI_MAX_CHANNELS]
);
+u16 hpi_volume_set_mute(u32 h_control, u32 mute);
+
+u16 hpi_volume_get_mute(u32 h_control, u32 *mute);
+
#define hpi_volume_get_range hpi_volume_query_range
-u16 hpi_volume_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB);
+u16 hpi_volume_query_range(u32 h_control, short *min_gain_01dB,
+ short *max_gain_01dB, short *step_gain_01dB);
-u16 hpi_volume_query_channels(const struct hpi_hsubsys *ph_subsys,
- const u32 h_volume, u32 *p_channels);
+u16 hpi_volume_query_channels(const u32 h_control, u32 *p_channels);
-u16 hpi_volume_auto_fade(const struct hpi_hsubsys *ph_subsys, u32 h_control,
+u16 hpi_volume_auto_fade(u32 h_control,
short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms);
-u16 hpi_volume_auto_fade_profile(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, short an_stop_gain0_01dB[HPI_MAX_CHANNELS],
- u32 duration_ms, u16 profile);
+u16 hpi_volume_auto_fade_profile(u32 h_control,
+ short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms,
+ u16 profile);
-/*************************/
-/* level control */
-/*************************/
-u16 hpi_level_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB);
+u16 hpi_volume_query_auto_fade_profile(const u32 h_control, const u32 i,
+ u16 *profile);
+
+/*****************/
+/* Level control */
+/*****************/
+u16 hpi_level_query_range(u32 h_control, short *min_gain_01dB,
+ short *max_gain_01dB, short *step_gain_01dB);
-u16 hpi_level_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_gain0_01dB[HPI_MAX_CHANNELS]
+u16 hpi_level_set_gain(u32 h_control, short an_gain0_01dB[HPI_MAX_CHANNELS]
);
-u16 hpi_level_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
+u16 hpi_level_get_gain(u32 h_control,
short an_gain0_01dB_out[HPI_MAX_CHANNELS]
);
-/*************************/
-/* meter control */
-/*************************/
-u16 hpi_meter_query_channels(const struct hpi_hsubsys *ph_subsys,
- const u32 h_meter, u32 *p_channels);
+/*****************/
+/* Meter control */
+/*****************/
+u16 hpi_meter_query_channels(const u32 h_meter, u32 *p_channels);
-u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control,
+u16 hpi_meter_get_peak(u32 h_control,
short an_peak0_01dB_out[HPI_MAX_CHANNELS]
);
-u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_peak0_01dB_out[HPI_MAX_CHANNELS]
+u16 hpi_meter_get_rms(u32 h_control, short an_peak0_01dB_out[HPI_MAX_CHANNELS]
);
-u16 hpi_meter_set_peak_ballistics(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 attack, u16 decay);
+u16 hpi_meter_set_peak_ballistics(u32 h_control, u16 attack, u16 decay);
-u16 hpi_meter_set_rms_ballistics(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 attack, u16 decay);
+u16 hpi_meter_set_rms_ballistics(u32 h_control, u16 attack, u16 decay);
-u16 hpi_meter_get_peak_ballistics(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *attack, u16 *decay);
+u16 hpi_meter_get_peak_ballistics(u32 h_control, u16 *attack, u16 *decay);
-u16 hpi_meter_get_rms_ballistics(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *attack, u16 *decay);
+u16 hpi_meter_get_rms_ballistics(u32 h_control, u16 *attack, u16 *decay);
-/*************************/
-/* channel mode control */
-/*************************/
-u16 hpi_channel_mode_query_mode(const struct hpi_hsubsys *ph_subsys,
- const u32 h_mode, const u32 index, u16 *pw_mode);
+/************************/
+/* ChannelMode control */
+/************************/
+u16 hpi_channel_mode_query_mode(const u32 h_mode, const u32 index,
+ u16 *pw_mode);
-u16 hpi_channel_mode_set(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 mode);
+u16 hpi_channel_mode_set(u32 h_control, u16 mode);
-u16 hpi_channel_mode_get(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 *mode);
+u16 hpi_channel_mode_get(u32 h_control, u16 *mode);
-/*************************/
-/* Tuner control */
-/*************************/
-u16 hpi_tuner_query_band(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, const u32 index, u16 *pw_band);
-
-u16 hpi_tuner_set_band(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 band);
+/*****************/
+/* Tuner control */
+/*****************/
+u16 hpi_tuner_query_band(const u32 h_tuner, const u32 index, u16 *pw_band);
-u16 hpi_tuner_get_band(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 *pw_band);
+u16 hpi_tuner_set_band(u32 h_control, u16 band);
-u16 hpi_tuner_query_frequency(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, const u32 index, const u16 band, u32 *pfreq);
+u16 hpi_tuner_get_band(u32 h_control, u16 *pw_band);
-u16 hpi_tuner_set_frequency(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 freq_ink_hz);
+u16 hpi_tuner_query_frequency(const u32 h_tuner, const u32 index,
+ const u16 band, u32 *pfreq);
-u16 hpi_tuner_get_frequency(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pw_freq_ink_hz);
+u16 hpi_tuner_set_frequency(u32 h_control, u32 freq_ink_hz);
-u16 hpi_tuner_getRF_level(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *pw_level);
+u16 hpi_tuner_get_frequency(u32 h_control, u32 *pw_freq_ink_hz);
-u16 hpi_tuner_get_rawRF_level(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, short *pw_level);
+u16 hpi_tuner_get_rf_level(u32 h_control, short *pw_level);
-u16 hpi_tuner_query_gain(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, const u32 index, u16 *pw_gain);
+u16 hpi_tuner_get_raw_rf_level(u32 h_control, short *pw_level);
-u16 hpi_tuner_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short gain);
+u16 hpi_tuner_query_gain(const u32 h_tuner, const u32 index, u16 *pw_gain);
-u16 hpi_tuner_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *pn_gain);
+u16 hpi_tuner_set_gain(u32 h_control, short gain);
-u16 hpi_tuner_get_status(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 *pw_status_mask, u16 *pw_status);
+u16 hpi_tuner_get_gain(u32 h_control, short *pn_gain);
-u16 hpi_tuner_set_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 mode, u32 value);
+u16 hpi_tuner_get_status(u32 h_control, u16 *pw_status_mask, u16 *pw_status);
-u16 hpi_tuner_get_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 mode, u32 *pn_value);
+u16 hpi_tuner_set_mode(u32 h_control, u32 mode, u32 value);
-u16 hpi_tuner_getRDS(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- char *p_rds_data);
+u16 hpi_tuner_get_mode(u32 h_control, u32 mode, u32 *pn_value);
-u16 hpi_tuner_query_deemphasis(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, const u32 index, const u16 band, u32 *pdeemphasis);
+u16 hpi_tuner_get_rds(u32 h_control, char *p_rds_data);
-u16 hpi_tuner_set_deemphasis(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 deemphasis);
-u16 hpi_tuner_get_deemphasis(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pdeemphasis);
+u16 hpi_tuner_query_deemphasis(const u32 h_tuner, const u32 index,
+ const u16 band, u32 *pdeemphasis);
-u16 hpi_tuner_query_program(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, u32 *pbitmap_program);
+u16 hpi_tuner_set_deemphasis(u32 h_control, u32 deemphasis);
+u16 hpi_tuner_get_deemphasis(u32 h_control, u32 *pdeemphasis);
-u16 hpi_tuner_set_program(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 program);
+u16 hpi_tuner_query_program(const u32 h_tuner, u32 *pbitmap_program);
-u16 hpi_tuner_get_program(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 *pprogram);
+u16 hpi_tuner_set_program(u32 h_control, u32 program);
-u16 hpi_tuner_get_hd_radio_dsp_version(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, char *psz_dsp_version, const u32 string_size);
+u16 hpi_tuner_get_program(u32 h_control, u32 *pprogram);
-u16 hpi_tuner_get_hd_radio_sdk_version(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, char *psz_sdk_version, const u32 string_size);
+u16 hpi_tuner_get_hd_radio_dsp_version(u32 h_control, char *psz_dsp_version,
+ const u32 string_size);
-u16 hpi_tuner_get_hd_radio_signal_quality(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pquality);
+u16 hpi_tuner_get_hd_radio_sdk_version(u32 h_control, char *psz_sdk_version,
+ const u32 string_size);
-u16 hpi_tuner_get_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pblend);
+u16 hpi_tuner_get_hd_radio_signal_quality(u32 h_control, u32 *pquality);
-u16 hpi_tuner_set_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, const u32 blend);
+u16 hpi_tuner_get_hd_radio_signal_blend(u32 h_control, u32 *pblend);
-/****************************/
-/* PADs control */
-/****************************/
+u16 hpi_tuner_set_hd_radio_signal_blend(u32 h_control, const u32 blend);
-u16 HPI_PAD__get_channel_name(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, char *psz_string, const u32 string_length);
+/***************/
+/* PAD control */
+/***************/
-u16 HPI_PAD__get_artist(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- char *psz_string, const u32 string_length);
+u16 hpi_pad_get_channel_name(u32 h_control, char *psz_string,
+ const u32 string_length);
-u16 HPI_PAD__get_title(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- char *psz_string, const u32 string_length);
+u16 hpi_pad_get_artist(u32 h_control, char *psz_string,
+ const u32 string_length);
-u16 HPI_PAD__get_comment(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- char *psz_string, const u32 string_length);
+u16 hpi_pad_get_title(u32 h_control, char *psz_string,
+ const u32 string_length);
-u16 HPI_PAD__get_program_type(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *ppTY);
+u16 hpi_pad_get_comment(u32 h_control, char *psz_string,
+ const u32 string_length);
-u16 HPI_PAD__get_rdsPI(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 *ppI);
+u16 hpi_pad_get_program_type(u32 h_control, u32 *ppTY);
-u16 HPI_PAD__get_program_type_string(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, const u32 data_type, const u32 pTY, char *psz_string,
- const u32 string_length);
+u16 hpi_pad_get_rdsPI(u32 h_control, u32 *ppI);
/****************************/
/* AES/EBU Receiver control */
/****************************/
-u16 HPI_AESEBU__receiver_query_format(const struct hpi_hsubsys *ph_subsys,
- const u32 h_aes_rx, const u32 index, u16 *pw_format);
+u16 hpi_aesebu_receiver_query_format(const u32 h_aes_rx, const u32 index,
+ u16 *pw_format);
-u16 HPI_AESEBU__receiver_set_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 source);
+u16 hpi_aesebu_receiver_set_format(u32 h_control, u16 source);
-u16 HPI_AESEBU__receiver_get_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_source);
+u16 hpi_aesebu_receiver_get_format(u32 h_control, u16 *pw_source);
-u16 HPI_AESEBU__receiver_get_sample_rate(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *psample_rate);
+u16 hpi_aesebu_receiver_get_sample_rate(u32 h_control, u32 *psample_rate);
-u16 HPI_AESEBU__receiver_get_user_data(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 *pw_data);
+u16 hpi_aesebu_receiver_get_user_data(u32 h_control, u16 index, u16 *pw_data);
-u16 HPI_AESEBU__receiver_get_channel_status(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u16 index, u16 *pw_data);
+u16 hpi_aesebu_receiver_get_channel_status(u32 h_control, u16 index,
+ u16 *pw_data);
-u16 HPI_AESEBU__receiver_get_error_status(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_error_data);
+u16 hpi_aesebu_receiver_get_error_status(u32 h_control, u16 *pw_error_data);
/*******************************/
/* AES/EBU Transmitter control */
/*******************************/
-u16 HPI_AESEBU__transmitter_set_sample_rate(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u32 sample_rate);
+u16 hpi_aesebu_transmitter_set_sample_rate(u32 h_control, u32 sample_rate);
-u16 HPI_AESEBU__transmitter_set_user_data(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 data);
+u16 hpi_aesebu_transmitter_set_user_data(u32 h_control, u16 index, u16 data);
-u16 HPI_AESEBU__transmitter_set_channel_status(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u16 index, u16 data);
+u16 hpi_aesebu_transmitter_set_channel_status(u32 h_control, u16 index,
+ u16 data);
-u16 HPI_AESEBU__transmitter_get_channel_status(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u16 index, u16 *pw_data);
+u16 hpi_aesebu_transmitter_get_channel_status(u32 h_control, u16 index,
+ u16 *pw_data);
-u16 HPI_AESEBU__transmitter_query_format(const struct hpi_hsubsys *ph_subsys,
- const u32 h_aes_tx, const u32 index, u16 *pw_format);
+u16 hpi_aesebu_transmitter_query_format(const u32 h_aes_tx, const u32 index,
+ u16 *pw_format);
-u16 HPI_AESEBU__transmitter_set_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 output_format);
+u16 hpi_aesebu_transmitter_set_format(u32 h_control, u16 output_format);
-u16 HPI_AESEBU__transmitter_get_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_output_format);
+u16 hpi_aesebu_transmitter_get_format(u32 h_control, u16 *pw_output_format);
/***********************/
-/* multiplexer control */
+/* Multiplexer control */
/***********************/
-u16 hpi_multiplexer_set_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 source_node_type, u16 source_node_index);
+u16 hpi_multiplexer_set_source(u32 h_control, u16 source_node_type,
+ u16 source_node_index);
-u16 hpi_multiplexer_get_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *source_node_type, u16 *source_node_index);
-
-u16 hpi_multiplexer_query_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 *source_node_type,
+u16 hpi_multiplexer_get_source(u32 h_control, u16 *source_node_type,
u16 *source_node_index);
+u16 hpi_multiplexer_query_source(u32 h_control, u16 index,
+ u16 *source_node_type, u16 *source_node_index);
+
/***************/
-/* VOX control */
+/* Vox control */
/***************/
-u16 hpi_vox_set_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_gain0_01dB);
+u16 hpi_vox_set_threshold(u32 h_control, short an_gain0_01dB);
-u16 hpi_vox_get_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *an_gain0_01dB);
+u16 hpi_vox_get_threshold(u32 h_control, short *an_gain0_01dB);
/*********************/
/* Bitstream control */
/*********************/
-u16 hpi_bitstream_set_clock_edge(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 edge_type);
+u16 hpi_bitstream_set_clock_edge(u32 h_control, u16 edge_type);
-u16 hpi_bitstream_set_data_polarity(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 polarity);
+u16 hpi_bitstream_set_data_polarity(u32 h_control, u16 polarity);
-u16 hpi_bitstream_get_activity(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_clk_activity, u16 *pw_data_activity);
+u16 hpi_bitstream_get_activity(u32 h_control, u16 *pw_clk_activity,
+ u16 *pw_data_activity);
/***********************/
/* SampleClock control */
/***********************/
-u16 hpi_sample_clock_query_source(const struct hpi_hsubsys *ph_subsys,
- const u32 h_clock, const u32 index, u16 *pw_source);
+u16 hpi_sample_clock_query_source(const u32 h_clock, const u32 index,
+ u16 *pw_source);
-u16 hpi_sample_clock_set_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 source);
+u16 hpi_sample_clock_set_source(u32 h_control, u16 source);
-u16 hpi_sample_clock_get_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_source);
+u16 hpi_sample_clock_get_source(u32 h_control, u16 *pw_source);
-u16 hpi_sample_clock_query_source_index(const struct hpi_hsubsys *ph_subsys,
- const u32 h_clock, const u32 index, const u32 source,
- u16 *pw_source_index);
+u16 hpi_sample_clock_query_source_index(const u32 h_clock, const u32 index,
+ const u32 source, u16 *pw_source_index);
-u16 hpi_sample_clock_set_source_index(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 source_index);
+u16 hpi_sample_clock_set_source_index(u32 h_control, u16 source_index);
-u16 hpi_sample_clock_get_source_index(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_source_index);
+u16 hpi_sample_clock_get_source_index(u32 h_control, u16 *pw_source_index);
-u16 hpi_sample_clock_get_sample_rate(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *psample_rate);
+u16 hpi_sample_clock_get_sample_rate(u32 h_control, u32 *psample_rate);
-u16 hpi_sample_clock_query_local_rate(const struct hpi_hsubsys *ph_subsys,
- const u32 h_clock, const u32 index, u32 *psource);
+u16 hpi_sample_clock_query_local_rate(const u32 h_clock, const u32 index,
+ u32 *psource);
-u16 hpi_sample_clock_set_local_rate(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 sample_rate);
+u16 hpi_sample_clock_set_local_rate(u32 h_control, u32 sample_rate);
-u16 hpi_sample_clock_get_local_rate(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *psample_rate);
+u16 hpi_sample_clock_get_local_rate(u32 h_control, u32 *psample_rate);
-u16 hpi_sample_clock_set_auto(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 enable);
+u16 hpi_sample_clock_set_auto(u32 h_control, u32 enable);
-u16 hpi_sample_clock_get_auto(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *penable);
+u16 hpi_sample_clock_get_auto(u32 h_control, u32 *penable);
-u16 hpi_sample_clock_set_local_rate_lock(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 lock);
+u16 hpi_sample_clock_set_local_rate_lock(u32 h_control, u32 lock);
-u16 hpi_sample_clock_get_local_rate_lock(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *plock);
+u16 hpi_sample_clock_get_local_rate_lock(u32 h_control, u32 *plock);
/***********************/
/* Microphone control */
/***********************/
-u16 hpi_microphone_set_phantom_power(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 on_off);
+u16 hpi_microphone_set_phantom_power(u32 h_control, u16 on_off);
-u16 hpi_microphone_get_phantom_power(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_on_off);
+u16 hpi_microphone_get_phantom_power(u32 h_control, u16 *pw_on_off);
-/*******************************
- Parametric Equalizer control
-*******************************/
-u16 hpi_parametricEQ__get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_number_of_bands, u16 *pw_enabled);
+/********************************/
+/* Parametric Equalizer control */
+/********************************/
+u16 hpi_parametric_eq_get_info(u32 h_control, u16 *pw_number_of_bands,
+ u16 *pw_enabled);
-u16 hpi_parametricEQ__set_state(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 on_off);
+u16 hpi_parametric_eq_set_state(u32 h_control, u16 on_off);
-u16 hpi_parametricEQ__set_band(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 type, u32 frequency_hz, short q100,
- short gain0_01dB);
+u16 hpi_parametric_eq_set_band(u32 h_control, u16 index, u16 type,
+ u32 frequency_hz, short q100, short gain0_01dB);
-u16 hpi_parametricEQ__get_band(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 *pn_type, u32 *pfrequency_hz,
- short *pnQ100, short *pn_gain0_01dB);
+u16 hpi_parametric_eq_get_band(u32 h_control, u16 index, u16 *pn_type,
+ u32 *pfrequency_hz, short *pnQ100, short *pn_gain0_01dB);
-u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, short coeffs[5]
+u16 hpi_parametric_eq_get_coeffs(u32 h_control, u16 index, short coeffs[5]
);
-/*******************************
- Compressor Expander control
-*******************************/
-
-u16 hpi_compander_set_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 on);
-
-u16 hpi_compander_get_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pon);
-
-u16 hpi_compander_set_makeup_gain(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, short makeup_gain0_01dB);
-
-u16 hpi_compander_get_makeup_gain(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, short *pn_makeup_gain0_01dB);
-
-u16 hpi_compander_set_attack_time_constant(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u32 index, u32 attack);
-
-u16 hpi_compander_get_attack_time_constant(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u32 index, u32 *pw_attack);
-
-u16 hpi_compander_set_decay_time_constant(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 index, u32 decay);
-
-u16 hpi_compander_get_decay_time_constant(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 index, u32 *pw_decay);
-
-u16 hpi_compander_set_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 index, short threshold0_01dB);
-
-u16 hpi_compander_get_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 index, short *pn_threshold0_01dB);
-
-u16 hpi_compander_set_ratio(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 index, u32 ratio100);
-
-u16 hpi_compander_get_ratio(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 index, u32 *pw_ratio100);
-
-/*******************************
- Cobranet HMI control
-*******************************/
-u16 hpi_cobranet_hmi_write(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 hmi_address, u32 byte_count, u8 *pb_data);
-
-u16 hpi_cobranet_hmi_read(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 hmi_address, u32 max_byte_count, u32 *pbyte_count, u8 *pb_data);
-
-u16 hpi_cobranet_hmi_get_status(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pstatus, u32 *preadable_size,
- u32 *pwriteable_size);
-
-/*Read the current IP address
-*/
-u16 hpi_cobranet_getI_paddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pi_paddress);
-
-/* Write the current IP address
-*/
-u16 hpi_cobranet_setI_paddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 i_paddress);
+/*******************************/
+/* Compressor Expander control */
+/*******************************/
-/* Read the static IP address
-*/
-u16 hpi_cobranet_get_staticI_paddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pi_paddress);
+u16 hpi_compander_set_enable(u32 h_control, u32 on);
-/* Write the static IP address
-*/
-u16 hpi_cobranet_set_staticI_paddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 i_paddress);
+u16 hpi_compander_get_enable(u32 h_control, u32 *pon);
-/* Read the MAC address
-*/
-u16 hpi_cobranet_getMA_caddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pmAC_MS_bs, u32 *pmAC_LS_bs);
+u16 hpi_compander_set_makeup_gain(u32 h_control, short makeup_gain0_01dB);
-/*******************************
- Tone Detector control
-*******************************/
-u16 hpi_tone_detector_get_state(const struct hpi_hsubsys *ph_subsys, u32 hC,
- u32 *state);
+u16 hpi_compander_get_makeup_gain(u32 h_control, short *pn_makeup_gain0_01dB);
-u16 hpi_tone_detector_set_enable(const struct hpi_hsubsys *ph_subsys, u32 hC,
- u32 enable);
+u16 hpi_compander_set_attack_time_constant(u32 h_control, u32 index,
+ u32 attack);
-u16 hpi_tone_detector_get_enable(const struct hpi_hsubsys *ph_subsys, u32 hC,
- u32 *enable);
+u16 hpi_compander_get_attack_time_constant(u32 h_control, u32 index,
+ u32 *pw_attack);
-u16 hpi_tone_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 event_enable);
+u16 hpi_compander_set_decay_time_constant(u32 h_control, u32 index,
+ u32 decay);
-u16 hpi_tone_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 *event_enable);
+u16 hpi_compander_get_decay_time_constant(u32 h_control, u32 index,
+ u32 *pw_decay);
-u16 hpi_tone_detector_set_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 hC, int threshold);
+u16 hpi_compander_set_threshold(u32 h_control, u32 index,
+ short threshold0_01dB);
-u16 hpi_tone_detector_get_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 hC, int *threshold);
+u16 hpi_compander_get_threshold(u32 h_control, u32 index,
+ short *pn_threshold0_01dB);
-u16 hpi_tone_detector_get_frequency(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 index, u32 *frequency);
+u16 hpi_compander_set_ratio(u32 h_control, u32 index, u32 ratio100);
-/*******************************
- Silence Detector control
-*******************************/
-u16 hpi_silence_detector_get_state(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 *state);
+u16 hpi_compander_get_ratio(u32 h_control, u32 index, u32 *pw_ratio100);
-u16 hpi_silence_detector_set_enable(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 enable);
+/********************/
+/* Cobranet control */
+/********************/
+u16 hpi_cobranet_hmi_write(u32 h_control, u32 hmi_address, u32 byte_count,
+ u8 *pb_data);
-u16 hpi_silence_detector_get_enable(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 *enable);
+u16 hpi_cobranet_hmi_read(u32 h_control, u32 hmi_address, u32 max_byte_count,
+ u32 *pbyte_count, u8 *pb_data);
-u16 hpi_silence_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 event_enable);
+u16 hpi_cobranet_hmi_get_status(u32 h_control, u32 *pstatus,
+ u32 *preadable_size, u32 *pwriteable_size);
-u16 hpi_silence_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 *event_enable);
+u16 hpi_cobranet_get_ip_address(u32 h_control, u32 *pdw_ip_address);
-u16 hpi_silence_detector_set_delay(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 delay);
+u16 hpi_cobranet_set_ip_address(u32 h_control, u32 dw_ip_address);
-u16 hpi_silence_detector_get_delay(const struct hpi_hsubsys *ph_subsys,
- u32 hC, u32 *delay);
+u16 hpi_cobranet_get_static_ip_address(u32 h_control, u32 *pdw_ip_address);
-u16 hpi_silence_detector_set_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 hC, int threshold);
+u16 hpi_cobranet_set_static_ip_address(u32 h_control, u32 dw_ip_address);
-u16 hpi_silence_detector_get_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 hC, int *threshold);
+u16 hpi_cobranet_get_macaddress(u32 h_control, u32 *p_mac_msbs,
+ u32 *p_mac_lsbs);
-/*******************************
- Universal control
-*******************************/
-u16 hpi_entity_find_next(struct hpi_entity *container_entity,
- enum e_entity_type type, enum e_entity_role role, int recursive_flag,
- struct hpi_entity **current_match);
-
-u16 hpi_entity_copy_value_from(struct hpi_entity *entity,
- enum e_entity_type type, size_t item_count, void *value_dst_p);
+/*************************/
+/* Tone Detector control */
+/*************************/
+u16 hpi_tone_detector_get_state(u32 hC, u32 *state);
-u16 hpi_entity_unpack(struct hpi_entity *entity, enum e_entity_type *type,
- size_t *items, enum e_entity_role *role, void **value);
+u16 hpi_tone_detector_set_enable(u32 hC, u32 enable);
-u16 hpi_entity_alloc_and_pack(const enum e_entity_type type,
- const size_t item_count, const enum e_entity_role role, void *value,
- struct hpi_entity **entity);
+u16 hpi_tone_detector_get_enable(u32 hC, u32 *enable);
-void hpi_entity_free(struct hpi_entity *entity);
+u16 hpi_tone_detector_set_event_enable(u32 hC, u32 event_enable);
-u16 hpi_universal_info(const struct hpi_hsubsys *ph_subsys, u32 hC,
- struct hpi_entity **info);
+u16 hpi_tone_detector_get_event_enable(u32 hC, u32 *event_enable);
-u16 hpi_universal_get(const struct hpi_hsubsys *ph_subsys, u32 hC,
- struct hpi_entity **value);
+u16 hpi_tone_detector_set_threshold(u32 hC, int threshold);
-u16 hpi_universal_set(const struct hpi_hsubsys *ph_subsys, u32 hC,
- struct hpi_entity *value);
+u16 hpi_tone_detector_get_threshold(u32 hC, int *threshold);
-/*/////////// */
-/* DSP CLOCK */
-/*/////////// */
-u16 hpi_clock_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u32 *ph_dsp_clock);
+u16 hpi_tone_detector_get_frequency(u32 hC, u32 index, u32 *frequency);
-u16 hpi_clock_set_time(const struct hpi_hsubsys *ph_subsys, u32 h_clock,
- u16 hour, u16 minute, u16 second, u16 milli_second);
+/****************************/
+/* Silence Detector control */
+/****************************/
+u16 hpi_silence_detector_get_state(u32 hC, u32 *state);
-u16 hpi_clock_get_time(const struct hpi_hsubsys *ph_subsys, u32 h_clock,
- u16 *pw_hour, u16 *pw_minute, u16 *pw_second, u16 *pw_milli_second);
+u16 hpi_silence_detector_set_enable(u32 hC, u32 enable);
-/*/////////// */
-/* PROFILE */
-/*/////////// */
-u16 hpi_profile_open_all(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 profile_index, u32 *ph_profile,
- u16 *pw_max_profiles);
+u16 hpi_silence_detector_get_enable(u32 hC, u32 *enable);
-u16 hpi_profile_get(const struct hpi_hsubsys *ph_subsys, u32 h_profile,
- u16 index, u16 *pw_seconds, u32 *pmicro_seconds, u32 *pcall_count,
- u32 *pmax_micro_seconds, u32 *pmin_micro_seconds);
+u16 hpi_silence_detector_set_event_enable(u32 hC, u32 event_enable);
-u16 hpi_profile_start_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile);
+u16 hpi_silence_detector_get_event_enable(u32 hC, u32 *event_enable);
-u16 hpi_profile_stop_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile);
+u16 hpi_silence_detector_set_delay(u32 hC, u32 delay);
-u16 hpi_profile_get_name(const struct hpi_hsubsys *ph_subsys, u32 h_profile,
- u16 index, char *sz_profile_name, u16 profile_name_length);
+u16 hpi_silence_detector_get_delay(u32 hC, u32 *delay);
-u16 hpi_profile_get_utilization(const struct hpi_hsubsys *ph_subsys,
- u32 h_profile, u32 *putilization);
+u16 hpi_silence_detector_set_threshold(u32 hC, int threshold);
-/*//////////////////// */
-/* UTILITY functions */
+u16 hpi_silence_detector_get_threshold(u32 hC, int *threshold);
+/*********************/
+/* Utility functions */
+/*********************/
u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
u32 sample_rate, u32 bit_rate, u32 attributes);
-/* Until it's verified, this function is for Windows OSs only */
-
-#endif /*_H_HPI_ */
-/*
-///////////////////////////////////////////////////////////////////////////////
-// See CVS for history. Last complete set in rev 1.146
-////////////////////////////////////////////////////////////////////////////////
-*/
+#endif /*_HPI_H_ */
diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c
index f7e374ec4414..b08578c93c6a 100644
--- a/sound/pci/asihpi/hpi6000.c
+++ b/sound/pci/asihpi/hpi6000.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Hardware Programming Interface (HPI) for AudioScience ASI6200 series adapters.
These PCI bus adapters are based on the TI C6711 DSP.
@@ -43,16 +32,17 @@
#define HPI_HIF_ERROR_MASK 0x4000
/* HPI6000 specific error codes */
+#define HPI6000_ERROR_BASE 900 /* not actually used anywhere */
-#define HPI6000_ERROR_BASE 900
+/* operational/messaging errors */
#define HPI6000_ERROR_MSG_RESP_IDLE_TIMEOUT 901
-#define HPI6000_ERROR_MSG_RESP_SEND_MSG_ACK 902
+#define HPI6000_ERROR_RESP_GET_LEN 902
#define HPI6000_ERROR_MSG_RESP_GET_RESP_ACK 903
#define HPI6000_ERROR_MSG_GET_ADR 904
#define HPI6000_ERROR_RESP_GET_ADR 905
#define HPI6000_ERROR_MSG_RESP_BLOCKWRITE32 906
#define HPI6000_ERROR_MSG_RESP_BLOCKREAD32 907
-#define HPI6000_ERROR_MSG_INVALID_DSP_INDEX 908
+
#define HPI6000_ERROR_CONTROL_CACHE_PARAMS 909
#define HPI6000_ERROR_SEND_DATA_IDLE_TIMEOUT 911
@@ -62,7 +52,6 @@
#define HPI6000_ERROR_SEND_DATA_CMD 915
#define HPI6000_ERROR_SEND_DATA_WRITE 916
#define HPI6000_ERROR_SEND_DATA_IDLECMD 917
-#define HPI6000_ERROR_SEND_DATA_VERIFY 918
#define HPI6000_ERROR_GET_DATA_IDLE_TIMEOUT 921
#define HPI6000_ERROR_GET_DATA_ACK 922
@@ -76,9 +65,8 @@
#define HPI6000_ERROR_MSG_RESP_GETRESPCMD 961
#define HPI6000_ERROR_MSG_RESP_IDLECMD 962
-#define HPI6000_ERROR_MSG_RESP_BLOCKVERIFY32 963
-/* adapter init errors */
+/* Initialisation/bootload errors */
#define HPI6000_ERROR_UNHANDLED_SUBSYS_ID 930
/* can't access PCI2040 */
@@ -201,8 +189,8 @@ static void hpi_read_block(struct dsp_obj *pdo, u32 address, u32 *pdata,
static void subsys_create_adapter(struct hpi_message *phm,
struct hpi_response *phr);
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr);
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr);
static void adapter_get_asserts(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
@@ -210,6 +198,8 @@ static void adapter_get_asserts(struct hpi_adapter_obj *pao,
static short create_adapter_obj(struct hpi_adapter_obj *pao,
u32 *pos_error_code);
+static void delete_adapter_obj(struct hpi_adapter_obj *pao);
+
/* local globals */
static u16 gw_pci_read_asserts; /* used to count PCI2040 errors */
@@ -217,23 +207,10 @@ static u16 gw_pci_write_asserts; /* used to count PCI2040 errors */
static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
{
-
switch (phm->function) {
- case HPI_SUBSYS_OPEN:
- case HPI_SUBSYS_CLOSE:
- case HPI_SUBSYS_GET_INFO:
- case HPI_SUBSYS_DRIVER_UNLOAD:
- case HPI_SUBSYS_DRIVER_LOAD:
- case HPI_SUBSYS_FIND_ADAPTERS:
- /* messages that should not get here */
- phr->error = HPI_ERROR_UNIMPLEMENTED;
- break;
case HPI_SUBSYS_CREATE_ADAPTER:
subsys_create_adapter(phm, phr);
break;
- case HPI_SUBSYS_DELETE_ADAPTER:
- subsys_delete_adapter(phm, phr);
- break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
@@ -243,6 +220,7 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
static void control_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
+ struct hpi_hw_obj *phw = pao->priv;
switch (phm->function) {
case HPI_CONTROL_GET_STATE:
@@ -251,27 +229,29 @@ static void control_message(struct hpi_adapter_obj *pao,
err = hpi6000_update_control_cache(pao, phm);
if (err) {
- phr->error = err;
+ if (err >= HPI_ERROR_BACKEND_BASE) {
+ phr->error =
+ HPI_ERROR_CONTROL_CACHING;
+ phr->specific_error = err;
+ } else {
+ phr->error = err;
+ }
break;
}
- if (hpi_check_control_cache(((struct hpi_hw_obj *)
- pao->priv)->p_cache, phm,
- phr))
+ if (hpi_check_control_cache(phw->p_cache, phm, phr))
break;
}
hw_message(pao, phm, phr);
break;
- case HPI_CONTROL_GET_INFO:
- hw_message(pao, phm, phr);
- break;
case HPI_CONTROL_SET_STATE:
hw_message(pao, phm, phr);
- hpi_sync_control_cache(((struct hpi_hw_obj *)pao->priv)->
- p_cache, phm, phr);
+ hpi_cmn_control_cache_sync_to_msg(phw->p_cache, phm, phr);
break;
+
+ case HPI_CONTROL_GET_INFO:
default:
- phr->error = HPI_ERROR_INVALID_FUNC;
+ hw_message(pao, phm, phr);
break;
}
}
@@ -280,26 +260,16 @@ static void adapter_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
- case HPI_ADAPTER_GET_INFO:
- hw_message(pao, phm, phr);
- break;
case HPI_ADAPTER_GET_ASSERT:
adapter_get_asserts(pao, phm, phr);
break;
- case HPI_ADAPTER_OPEN:
- case HPI_ADAPTER_CLOSE:
- case HPI_ADAPTER_TEST_ASSERT:
- case HPI_ADAPTER_SELFTEST:
- case HPI_ADAPTER_GET_MODE:
- case HPI_ADAPTER_SET_MODE:
- case HPI_ADAPTER_FIND_OBJECT:
- case HPI_ADAPTER_GET_PROPERTY:
- case HPI_ADAPTER_SET_PROPERTY:
- case HPI_ADAPTER_ENUM_PROPERTY:
- hw_message(pao, phm, phr);
+
+ case HPI_ADAPTER_DELETE:
+ adapter_delete(pao, phm, phr);
break;
+
default:
- phr->error = HPI_ERROR_INVALID_FUNC;
+ hw_message(pao, phm, phr);
break;
}
}
@@ -311,7 +281,7 @@ static void outstream_message(struct hpi_adapter_obj *pao,
case HPI_OSTREAM_HOSTBUFFER_ALLOC:
case HPI_OSTREAM_HOSTBUFFER_FREE:
/* Don't let these messages go to the HW function because
- * they're called without allocating the spinlock.
+ * they're called without locking the spinlock.
* For the HPI6000 adapters the HW would return
* HPI_ERROR_INVALID_FUNC anyway.
*/
@@ -331,7 +301,7 @@ static void instream_message(struct hpi_adapter_obj *pao,
case HPI_ISTREAM_HOSTBUFFER_ALLOC:
case HPI_ISTREAM_HOSTBUFFER_FREE:
/* Don't let these messages go to the HW function because
- * they're called without allocating the spinlock.
+ * they're called without locking the spinlock.
* For the HPI6000 adapters the HW would return
* HPI_ERROR_INVALID_FUNC anyway.
*/
@@ -352,26 +322,22 @@ void HPI_6000(struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_adapter_obj *pao = NULL;
- /* subsytem messages get executed by every HPI. */
- /* All other messages are ignored unless the adapter index matches */
- /* an adapter in the HPI */
- HPI_DEBUG_LOG(DEBUG, "O %d,F %x\n", phm->object, phm->function);
-
- /* if Dsp has crashed then do not communicate with it any more */
if (phm->object != HPI_OBJ_SUBSYSTEM) {
pao = hpi_find_adapter(phm->adapter_index);
if (!pao) {
- HPI_DEBUG_LOG(DEBUG,
- " %d,%d refused, for another HPI?\n",
- phm->object, phm->function);
+ hpi_init_response(phr, phm->object, phm->function,
+ HPI_ERROR_BAD_ADAPTER_NUMBER);
+ HPI_DEBUG_LOG(DEBUG, "invalid adapter index: %d \n",
+ phm->adapter_index);
return;
}
+ /* Don't even try to communicate with crashed DSP */
if (pao->dsp_crashed >= 10) {
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_DSP_HARDWARE);
- HPI_DEBUG_LOG(DEBUG, " %d,%d dsp crashed.\n",
- phm->object, phm->function);
+ HPI_DEBUG_LOG(DEBUG, "adapter %d dsp crashed\n",
+ phm->adapter_index);
return;
}
}
@@ -381,7 +347,7 @@ void HPI_6000(struct hpi_message *phm, struct hpi_response *phr)
HPI_ERROR_PROCESSING_MESSAGE);
switch (phm->type) {
- case HPI_TYPE_MESSAGE:
+ case HPI_TYPE_REQUEST:
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
subsys_message(phm, phr);
@@ -422,7 +388,7 @@ void HPI_6000(struct hpi_message *phm, struct hpi_response *phr)
/* SUBSYSTEM */
/* create an adapter object and initialise it based on resource information
- * passed in in the message
+ * passed in the message
* NOTE - you cannot use this function AND the FindAdapters function at the
* same time, the application must use only one of them to get the adapters
*/
@@ -433,39 +399,34 @@ static void subsys_create_adapter(struct hpi_message *phm,
struct hpi_adapter_obj ao;
struct hpi_adapter_obj *pao;
u32 os_error_code;
- short error = 0;
+ u16 err = 0;
u32 dsp_index = 0;
HPI_DEBUG_LOG(VERBOSE, "subsys_create_adapter\n");
memset(&ao, 0, sizeof(ao));
- /* this HPI only creates adapters for TI/PCI2040 based devices */
- if (phm->u.s.resource.bus_type != HPI_BUS_PCI)
- return;
- if (phm->u.s.resource.r.pci->vendor_id != HPI_PCI_VENDOR_ID_TI)
- return;
- if (phm->u.s.resource.r.pci->device_id != HPI_PCI_DEV_ID_PCI2040)
- return;
-
ao.priv = kzalloc(sizeof(struct hpi_hw_obj), GFP_KERNEL);
if (!ao.priv) {
- HPI_DEBUG_LOG(ERROR, "cant get mem for adapter object\n");
+ HPI_DEBUG_LOG(ERROR, "can't get mem for adapter object\n");
phr->error = HPI_ERROR_MEMORY_ALLOC;
return;
}
/* create the adapter object based on the resource information */
- /*? memcpy(&ao.Pci,&phm->u.s.Resource.r.Pci,sizeof(ao.Pci)); */
ao.pci = *phm->u.s.resource.r.pci;
- error = create_adapter_obj(&ao, &os_error_code);
- if (!error)
- error = hpi_add_adapter(&ao);
- if (error) {
+ err = create_adapter_obj(&ao, &os_error_code);
+ if (err) {
+ delete_adapter_obj(&ao);
+ if (err >= HPI_ERROR_BACKEND_BASE) {
+ phr->error = HPI_ERROR_DSP_BOOTLOAD;
+ phr->specific_error = err;
+ } else {
+ phr->error = err;
+ }
+
phr->u.s.data = os_error_code;
- kfree(ao.priv);
- phr->error = error;
return;
}
/* need to update paParentAdapter */
@@ -473,39 +434,25 @@ static void subsys_create_adapter(struct hpi_message *phm,
if (!pao) {
/* We just added this adapter, why can't we find it!? */
HPI_DEBUG_LOG(ERROR, "lost adapter after boot\n");
- phr->error = 950;
+ phr->error = HPI_ERROR_BAD_ADAPTER;
return;
}
for (dsp_index = 0; dsp_index < MAX_DSPS; dsp_index++) {
- struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
+ struct hpi_hw_obj *phw = pao->priv;
phw->ado[dsp_index].pa_parent_adapter = pao;
}
- phr->u.s.aw_adapter_list[ao.index] = ao.adapter_type;
+ phr->u.s.adapter_type = ao.type;
phr->u.s.adapter_index = ao.index;
- phr->u.s.num_adapters++;
phr->error = 0;
}
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr)
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr)
{
- struct hpi_adapter_obj *pao = NULL;
- struct hpi_hw_obj *phw;
-
- pao = hpi_find_adapter(phm->adapter_index);
- if (!pao)
- return;
-
- phw = (struct hpi_hw_obj *)pao->priv;
-
- if (pao->has_control_cache)
- hpi_free_control_cache(phw->p_cache);
-
+ delete_adapter_obj(pao);
hpi_delete_adapter(pao);
- kfree(phw);
-
phr->error = 0;
}
@@ -517,10 +464,7 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao,
u32 dsp_index = 0;
u32 control_cache_size = 0;
u32 control_cache_count = 0;
- struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
-
- /* init error reporting */
- pao->dsp_crashed = 0;
+ struct hpi_hw_obj *phw = pao->priv;
/* The PCI2040 has the following address map */
/* BAR0 - 4K = HPI control and status registers on PCI2040 (HPI CSR) */
@@ -575,36 +519,36 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao,
/* get info about the adapter by asking the adapter */
/* send a HPI_ADAPTER_GET_INFO message */
{
- struct hpi_message hM;
- struct hpi_response hR0; /* response from DSP 0 */
- struct hpi_response hR1; /* response from DSP 1 */
+ struct hpi_message hm;
+ struct hpi_response hr0; /* response from DSP 0 */
+ struct hpi_response hr1; /* response from DSP 1 */
u16 error = 0;
HPI_DEBUG_LOG(VERBOSE, "send ADAPTER_GET_INFO\n");
- memset(&hM, 0, sizeof(hM));
- hM.type = HPI_TYPE_MESSAGE;
- hM.size = sizeof(struct hpi_message);
- hM.object = HPI_OBJ_ADAPTER;
- hM.function = HPI_ADAPTER_GET_INFO;
- hM.adapter_index = 0;
- memset(&hR0, 0, sizeof(hR0));
- memset(&hR1, 0, sizeof(hR1));
- hR0.size = sizeof(hR0);
- hR1.size = sizeof(hR1);
-
- error = hpi6000_message_response_sequence(pao, 0, &hM, &hR0);
- if (hR0.error) {
- HPI_DEBUG_LOG(DEBUG, "message error %d\n", hR0.error);
- return hR0.error;
+ memset(&hm, 0, sizeof(hm));
+ hm.type = HPI_TYPE_REQUEST;
+ hm.size = sizeof(struct hpi_message);
+ hm.object = HPI_OBJ_ADAPTER;
+ hm.function = HPI_ADAPTER_GET_INFO;
+ hm.adapter_index = 0;
+ memset(&hr0, 0, sizeof(hr0));
+ memset(&hr1, 0, sizeof(hr1));
+ hr0.size = sizeof(hr0);
+ hr1.size = sizeof(hr1);
+
+ error = hpi6000_message_response_sequence(pao, 0, &hm, &hr0);
+ if (hr0.error) {
+ HPI_DEBUG_LOG(DEBUG, "message error %d\n", hr0.error);
+ return hr0.error;
}
if (phw->num_dsp == 2) {
- error = hpi6000_message_response_sequence(pao, 1, &hM,
- &hR1);
+ error = hpi6000_message_response_sequence(pao, 1, &hm,
+ &hr1);
if (error)
return error;
}
- pao->adapter_type = hR0.u.a.adapter_type;
- pao->index = hR0.u.a.adapter_index;
+ pao->type = hr0.u.ax.info.adapter_type;
+ pao->index = hr0.u.ax.info.adapter_index;
}
memset(&phw->control_cache[0], 0,
@@ -618,20 +562,36 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao,
control_cache_count =
hpi_read_word(&phw->ado[0],
HPI_HIF_ADDR(control_cache_count));
- pao->has_control_cache = 1;
phw->p_cache =
hpi_alloc_control_cache(control_cache_count,
- control_cache_size, (struct hpi_control_cache_info *)
+ control_cache_size, (unsigned char *)
&phw->control_cache[0]
);
- } else
- pao->has_control_cache = 0;
+ if (phw->p_cache)
+ pao->has_control_cache = 1;
+ }
- HPI_DEBUG_LOG(DEBUG, "get adapter info ASI%04X index %d\n",
- pao->adapter_type, pao->index);
- pao->open = 0; /* upon creation the adapter is closed */
- return 0;
+ HPI_DEBUG_LOG(DEBUG, "get adapter info ASI%04X index %d\n", pao->type,
+ pao->index);
+
+ if (phw->p_cache)
+ phw->p_cache->adap_idx = pao->index;
+
+ return hpi_add_adapter(pao);
+}
+
+static void delete_adapter_obj(struct hpi_adapter_obj *pao)
+{
+ struct hpi_hw_obj *phw = pao->priv;
+
+ if (pao->has_control_cache)
+ hpi_free_control_cache(phw->p_cache);
+
+ /* reset DSPs on adapter */
+ iowrite32(0x0003000F, phw->dw2040_HPICSR + HPI_RESET);
+
+ kfree(phw);
}
/************************************************************************/
@@ -643,11 +603,13 @@ static void adapter_get_asserts(struct hpi_adapter_obj *pao,
#ifndef HIDE_PCI_ASSERTS
/* if we have PCI2040 asserts then collect them */
if ((gw_pci_read_asserts > 0) || (gw_pci_write_asserts > 0)) {
- phr->u.a.serial_number =
+ phr->u.ax.assert.p1 =
gw_pci_read_asserts * 100 + gw_pci_write_asserts;
- phr->u.a.adapter_index = 1; /* assert count */
- phr->u.a.adapter_type = -1; /* "dsp index" */
- strcpy(phr->u.a.sz_adapter_assert, "PCI2040 error");
+ phr->u.ax.assert.p2 = 0;
+ phr->u.ax.assert.count = 1; /* assert count */
+ phr->u.ax.assert.dsp_index = -1; /* "dsp index" */
+ strscpy(phr->u.ax.assert.sz_message, "PCI2040 error");
+ phr->u.ax.assert.dsp_msg_addr = 0;
gw_pci_read_asserts = 0;
gw_pci_write_asserts = 0;
phr->error = 0;
@@ -664,7 +626,7 @@ static void adapter_get_asserts(struct hpi_adapter_obj *pao,
static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
u32 *pos_error_code)
{
- struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
+ struct hpi_hw_obj *phw = pao->priv;
short error;
u32 timeout;
u32 read = 0;
@@ -684,10 +646,10 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
/* NOTE don't use wAdapterType in this routine. It is not setup yet */
- switch (pao->pci.subsys_device_id) {
+ switch (pao->pci.pci_dev->subsystem_device) {
case 0x5100:
case 0x5110: /* ASI5100 revB or higher with C6711D */
- case 0x5200: /* ASI5200 PC_ie version of ASI5100 */
+ case 0x5200: /* ASI5200 PCIe version of ASI5100 */
case 0x6100:
case 0x6200:
boot_load_family = HPI_ADAPTER_FAMILY_ASI(0x6200);
@@ -707,8 +669,9 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
* note that bits 4..15 are read-only and so should always return zero,
* even though we wrote 1 to them
*/
- for (i = 0; i < 1000; i++)
- delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
+ hpios_delay_micro_seconds(1000);
+ delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
+
if (delay != dw2040_reset) {
HPI_DEBUG_LOG(ERROR, "INIT_PCI2040 %x %x\n", dw2040_reset,
delay);
@@ -741,8 +704,7 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
dw2040_reset = dw2040_reset & (~0x00000008);
iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET);
/*delay to allow DSP to get going */
- for (i = 0; i < 100; i++)
- delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
+ hpios_delay_micro_seconds(100);
/* loop through all DSPs, downloading DSP code */
for (dsp_index = 0; dsp_index < phw->num_dsp; dsp_index++) {
@@ -781,27 +743,27 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
*/
/* bypass PLL */
hpi_write_word(pdo, 0x01B7C100, 0x0000);
- for (i = 0; i < 100; i++)
- delay = ioread32(phw->dw2040_HPICSR +
- HPI_RESET);
+ hpios_delay_micro_seconds(100);
/* ** use default of PLL x7 ** */
/* EMIF = 225/3=75MHz */
hpi_write_word(pdo, 0x01B7C120, 0x8002);
+ hpios_delay_micro_seconds(100);
+
/* peri = 225/2 */
hpi_write_word(pdo, 0x01B7C11C, 0x8001);
+ hpios_delay_micro_seconds(100);
+
/* cpu = 225/1 */
hpi_write_word(pdo, 0x01B7C118, 0x8000);
- /* ~200us delay */
- for (i = 0; i < 2000; i++)
- delay = ioread32(phw->dw2040_HPICSR +
- HPI_RESET);
+
+ /* ~2ms delay */
+ hpios_delay_micro_seconds(2000);
+
/* PLL not bypassed */
hpi_write_word(pdo, 0x01B7C100, 0x0001);
- /* ~200us delay */
- for (i = 0; i < 2000; i++)
- delay = ioread32(phw->dw2040_HPICSR +
- HPI_RESET);
+ /* ~2ms delay */
+ hpios_delay_micro_seconds(2000);
}
/* test r/w to internal DSP memory
@@ -925,9 +887,7 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
}
/* delay a little to allow SDRAM and DSP to "get going" */
-
- for (i = 0; i < 1000; i++)
- delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
+ hpios_delay_micro_seconds(1000);
/* test access to SDRAM */
{
@@ -973,11 +933,8 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
}
/* write the DSP code down into the DSPs memory */
- /*HpiDspCode_Open(nBootLoadFamily,&DspCode,pdwOsErrorCode); */
- dsp_code.ps_dev = pao->pci.p_os_data;
-
- error = hpi_dsp_code_open(boot_load_family, &dsp_code,
- pos_error_code);
+ error = hpi_dsp_code_open(boot_load_family, pao->pci.pci_dev,
+ &dsp_code, pos_error_code);
if (error)
return error;
@@ -1071,8 +1028,7 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
/* step 3. Start code by sending interrupt */
iowrite32(0x00030003, pdo->prHPI_control);
- for (i = 0; i < 10000; i++)
- delay = ioread32(phw->dw2040_HPICSR + HPI_RESET);
+ hpios_delay_micro_seconds(10000);
/* wait for a non-zero value in hostcmd -
* indicating initialization is complete
@@ -1099,7 +1055,7 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
* locks up with a bluescreen (NOT GPF or pagefault).
*/
else
- hpios_delay_micro_seconds(1000);
+ hpios_delay_micro_seconds(10000);
}
if (timeout == 0)
return HPI6000_ERROR_INIT_NOACK;
@@ -1130,14 +1086,14 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
mask = 0xFFFFFF00L;
/* ASI5100 uses AX6 code, */
/* but has no PLD r/w register to test */
- if (HPI_ADAPTER_FAMILY_ASI(pao->pci.
- subsys_device_id) ==
+ if (HPI_ADAPTER_FAMILY_ASI(pao->pci.pci_dev->
+ subsystem_device) ==
HPI_ADAPTER_FAMILY_ASI(0x5100))
mask = 0x00000000L;
/* ASI5200 uses AX6 code, */
/* but has no PLD r/w register to test */
- if (HPI_ADAPTER_FAMILY_ASI(pao->pci.
- subsys_device_id) ==
+ if (HPI_ADAPTER_FAMILY_ASI(pao->pci.pci_dev->
+ subsystem_device) ==
HPI_ADAPTER_FAMILY_ASI(0x5200))
mask = 0x00000000L;
break;
@@ -1202,7 +1158,7 @@ static u32 hpi_read_word(struct dsp_obj *pdo, u32 address)
u32 data = 0;
if (hpi_set_address(pdo, address))
- return 0; /*? no way to return error */
+ return 0; /*? No way to return error */
/* take care of errata in revB DSP (2.0.1) */
data = ioread32(pdo->prHPI_data);
@@ -1251,8 +1207,8 @@ static void hpi_read_block(struct dsp_obj *pdo, u32 address, u32 *pdata,
static u16 hpi6000_dsp_block_write32(struct hpi_adapter_obj *pao,
u16 dsp_index, u32 hpi_address, u32 *source, u32 count)
{
- struct dsp_obj *pdo =
- &(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
+ struct hpi_hw_obj *phw = pao->priv;
+ struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 time_out = PCI_TIMEOUT;
int c6711_burst_size = 128;
u32 local_hpi_address = hpi_address;
@@ -1289,15 +1245,14 @@ static u16 hpi6000_dsp_block_write32(struct hpi_adapter_obj *pao,
static u16 hpi6000_dsp_block_read32(struct hpi_adapter_obj *pao,
u16 dsp_index, u32 hpi_address, u32 *dest, u32 count)
{
- struct dsp_obj *pdo =
- &(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
+ struct hpi_hw_obj *phw = pao->priv;
+ struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 time_out = PCI_TIMEOUT;
int c6711_burst_size = 16;
u32 local_hpi_address = hpi_address;
int local_count = count;
int xfer_size;
u32 *pdata = dest;
- u32 loop_count = 0;
while (local_count) {
if (local_count > c6711_burst_size)
@@ -1317,7 +1272,6 @@ static u16 hpi6000_dsp_block_read32(struct hpi_adapter_obj *pao,
pdata += xfer_size;
local_hpi_address += sizeof(u32) * xfer_size;
local_count -= xfer_size;
- loop_count++;
}
if (time_out)
@@ -1329,7 +1283,7 @@ static u16 hpi6000_dsp_block_read32(struct hpi_adapter_obj *pao,
static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
u16 dsp_index, struct hpi_message *phm, struct hpi_response *phr)
{
- struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
+ struct hpi_hw_obj *phw = pao->priv;
struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 timeout;
u16 ack;
@@ -1338,10 +1292,6 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
u32 *p_data;
u16 error = 0;
- /* does the DSP we are referencing exist? */
- if (dsp_index >= phw->num_dsp)
- return HPI6000_ERROR_MSG_INVALID_DSP_INDEX;
-
ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_IDLE);
if (ack & HPI_HIF_ERROR_MASK) {
pao->dsp_crashed++;
@@ -1349,9 +1299,7 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
}
pao->dsp_crashed = 0;
- /* send the message */
-
- /* get the address and size */
+ /* get the message address and size */
if (phw->message_buffer_address_on_dsp == 0) {
timeout = TIMEOUT;
do {
@@ -1366,10 +1314,9 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
} else
address = phw->message_buffer_address_on_dsp;
- /* dwLength = sizeof(struct hpi_message); */
length = phm->size;
- /* send it */
+ /* send the message */
p_data = (u32 *)phm;
if (hpi6000_dsp_block_write32(pao, dsp_index, address, p_data,
(u16)length / 4))
@@ -1383,7 +1330,7 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
if (ack & HPI_HIF_ERROR_MASK)
return HPI6000_ERROR_MSG_RESP_GET_RESP_ACK;
- /* get the address and size */
+ /* get the response address */
if (phw->response_buffer_address_on_dsp == 0) {
timeout = TIMEOUT;
do {
@@ -1405,9 +1352,12 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao,
length = hpi_read_word(pdo, HPI_HIF_ADDR(length));
} while (hpi6000_check_PCI2040_error_flag(pao, H6READ) && --timeout);
if (!timeout)
- length = sizeof(struct hpi_response);
+ return HPI6000_ERROR_RESP_GET_LEN;
+
+ if (length > phr->size)
+ return HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
- /* get it */
+ /* get the response */
p_data = (u32 *)phr;
if (hpi6000_dsp_block_read32(pao, dsp_index, address, p_data,
(u16)length / 4))
@@ -1452,8 +1402,8 @@ static short hpi6000_send_data_check_adr(u32 address, u32 length_in_dwords)
static short hpi6000_send_data(struct hpi_adapter_obj *pao, u16 dsp_index,
struct hpi_message *phm, struct hpi_response *phr)
{
- struct dsp_obj *pdo =
- &(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
+ struct hpi_hw_obj *phw = pao->priv;
+ struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 data_sent = 0;
u16 ack;
u32 length, address;
@@ -1525,8 +1475,8 @@ static short hpi6000_send_data(struct hpi_adapter_obj *pao, u16 dsp_index,
static short hpi6000_get_data(struct hpi_adapter_obj *pao, u16 dsp_index,
struct hpi_message *phm, struct hpi_response *phr)
{
- struct dsp_obj *pdo =
- &(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
+ struct hpi_hw_obj *phw = pao->priv;
+ struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 data_got = 0;
u16 ack;
u32 length, address;
@@ -1589,8 +1539,8 @@ static void hpi6000_send_dsp_interrupt(struct dsp_obj *pdo)
static short hpi6000_send_host_command(struct hpi_adapter_obj *pao,
u16 dsp_index, u32 host_cmd)
{
- struct dsp_obj *pdo =
- &(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
+ struct hpi_hw_obj *phw = pao->priv;
+ struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 timeout = TIMEOUT;
/* set command */
@@ -1615,7 +1565,7 @@ static short hpi6000_check_PCI2040_error_flag(struct hpi_adapter_obj *pao,
{
u32 hPI_error;
- struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
+ struct hpi_hw_obj *phw = pao->priv;
/* read the error bits from the PCI2040 */
hPI_error = ioread32(phw->dw2040_HPICSR + HPI_ERROR_REPORT);
@@ -1635,8 +1585,8 @@ static short hpi6000_check_PCI2040_error_flag(struct hpi_adapter_obj *pao,
static short hpi6000_wait_dsp_ack(struct hpi_adapter_obj *pao, u16 dsp_index,
u32 ack_value)
{
- struct dsp_obj *pdo =
- &(*(struct hpi_hw_obj *)pao->priv).ado[dsp_index];
+ struct hpi_hw_obj *phw = pao->priv;
+ struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 ack = 0L;
u32 timeout;
u32 hPIC = 0L;
@@ -1678,7 +1628,7 @@ static short hpi6000_update_control_cache(struct hpi_adapter_obj *pao,
struct hpi_message *phm)
{
const u16 dsp_index = 0;
- struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv;
+ struct hpi_hw_obj *phw = pao->priv;
struct dsp_obj *pdo = &phw->ado[dsp_index];
u32 timeout;
u32 cache_dirty_flag;
@@ -1778,7 +1728,8 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
{
u16 error = 0;
u16 dsp_index = 0;
- u16 num_dsp = ((struct hpi_hw_obj *)pao->priv)->num_dsp;
+ struct hpi_hw_obj *phw = pao->priv;
+ u16 num_dsp = phw->num_dsp;
if (num_dsp < 2)
dsp_index = 0;
@@ -1803,17 +1754,11 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
hpios_dsplock_lock(pao);
error = hpi6000_message_response_sequence(pao, dsp_index, phm, phr);
- /* maybe an error response */
- if (error) {
- /* something failed in the HPI/DSP interface */
- phr->error = error;
- /* just the header of the response is valid */
- phr->size = sizeof(struct hpi_response_header);
+ if (error) /* something failed in the HPI/DSP interface */
goto err;
- }
- if (phr->error != 0) /* something failed in the DSP */
- goto err;
+ if (phr->error) /* something failed in the DSP */
+ goto out;
switch (phm->function) {
case HPI_OSTREAM_WRITE:
@@ -1825,21 +1770,30 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
error = hpi6000_get_data(pao, dsp_index, phm, phr);
break;
case HPI_ADAPTER_GET_ASSERT:
- phr->u.a.adapter_index = 0; /* dsp 0 default */
+ phr->u.ax.assert.dsp_index = 0; /* dsp 0 default */
if (num_dsp == 2) {
- if (!phr->u.a.adapter_type) {
+ if (!phr->u.ax.assert.count) {
/* no assert from dsp 0, check dsp 1 */
error = hpi6000_message_response_sequence(pao,
1, phm, phr);
- phr->u.a.adapter_index = 1;
+ phr->u.ax.assert.dsp_index = 1;
}
}
}
- if (error)
- phr->error = error;
-
err:
+ if (error) {
+ if (error >= HPI_ERROR_BACKEND_BASE) {
+ phr->error = HPI_ERROR_DSP_COMMUNICATION;
+ phr->specific_error = error;
+ } else {
+ phr->error = error;
+ }
+
+ /* just the header of the response is valid */
+ phr->size = sizeof(struct hpi_response_header);
+ }
+out:
hpios_dsplock_unlock(pao);
return;
}
diff --git a/sound/pci/asihpi/hpi6000.h b/sound/pci/asihpi/hpi6000.h
index 4c7d507c0ecd..4a5256a6e4af 100644
--- a/sound/pci/asihpi/hpi6000.h
+++ b/sound/pci/asihpi/hpi6000.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*****************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Public declarations for DSP Proramming Interface to TI C6701
diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c
index 22c5fc625533..c7d7eff86727 100644
--- a/sound/pci/asihpi/hpi6205.c
+++ b/sound/pci/asihpi/hpi6205.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Hardware Programming Interface (HPI) for AudioScience
ASI50xx, AS51xx, ASI6xxx, ASI87xx ASI89xx series adapters.
@@ -38,27 +27,29 @@
/*****************************************************************************/
/* HPI6205 specific error codes */
-#define HPI6205_ERROR_BASE 1000
-/*#define HPI6205_ERROR_MEM_ALLOC 1001 */
-#define HPI6205_ERROR_6205_NO_IRQ 1002
-#define HPI6205_ERROR_6205_INIT_FAILED 1003
-/*#define HPI6205_ERROR_MISSING_DSPCODE 1004 */
-#define HPI6205_ERROR_UNKNOWN_PCI_DEVICE 1005
-#define HPI6205_ERROR_6205_REG 1006
-#define HPI6205_ERROR_6205_DSPPAGE 1007
-#define HPI6205_ERROR_BAD_DSPINDEX 1008
-#define HPI6205_ERROR_C6713_HPIC 1009
-#define HPI6205_ERROR_C6713_HPIA 1010
-#define HPI6205_ERROR_C6713_PLL 1011
-#define HPI6205_ERROR_DSP_INTMEM 1012
-#define HPI6205_ERROR_DSP_EXTMEM 1013
-#define HPI6205_ERROR_DSP_PLD 1014
+#define HPI6205_ERROR_BASE 1000 /* not actually used anywhere */
+
+/* operational/messaging errors */
#define HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT 1015
#define HPI6205_ERROR_MSG_RESP_TIMEOUT 1016
-#define HPI6205_ERROR_6205_EEPROM 1017
-#define HPI6205_ERROR_DSP_EMIF 1018
-#define hpi6205_error(dsp_index, err) (err)
+/* initialization/bootload errors */
+#define HPI6205_ERROR_6205_NO_IRQ 1002
+#define HPI6205_ERROR_6205_INIT_FAILED 1003
+#define HPI6205_ERROR_6205_REG 1006
+#define HPI6205_ERROR_6205_DSPPAGE 1007
+#define HPI6205_ERROR_C6713_HPIC 1009
+#define HPI6205_ERROR_C6713_HPIA 1010
+#define HPI6205_ERROR_C6713_PLL 1011
+#define HPI6205_ERROR_DSP_INTMEM 1012
+#define HPI6205_ERROR_DSP_EXTMEM 1013
+#define HPI6205_ERROR_DSP_PLD 1014
+#define HPI6205_ERROR_6205_EEPROM 1017
+#define HPI6205_ERROR_DSP_EMIF1 1018
+#define HPI6205_ERROR_DSP_EMIF2 1019
+#define HPI6205_ERROR_DSP_EMIF3 1020
+#define HPI6205_ERROR_DSP_EMIF4 1021
+
/*****************************************************************************/
/* for C6205 PCI i/f */
/* Host Status Register (HSR) bitfields */
@@ -128,9 +119,6 @@ struct hpi_hw_obj {
u32 outstream_host_buffer_size[HPI_MAX_STREAMS];
struct consistent_dma_area h_control_cache;
- struct consistent_dma_area h_async_event_buffer;
-/* struct hpi_control_cache_single *pControlCache; */
- struct hpi_async_event *p_async_event_buffer;
struct hpi_control_cache *p_cache;
};
@@ -156,14 +144,17 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
static void subsys_create_adapter(struct hpi_message *phm,
struct hpi_response *phr);
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr);
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr);
static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
u32 *pos_error_code);
static void delete_adapter_obj(struct hpi_adapter_obj *pao);
+static int adapter_irq_query_and_clear(struct hpi_adapter_obj *pao,
+ u32 message);
+
static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr);
@@ -208,8 +199,8 @@ static void instream_start(struct hpi_adapter_obj *pao,
static u32 boot_loader_read_mem32(struct hpi_adapter_obj *pao, int dsp_index,
u32 address);
-static u16 boot_loader_write_mem32(struct hpi_adapter_obj *pao, int dsp_index,
- u32 address, u32 data);
+static void boot_loader_write_mem32(struct hpi_adapter_obj *pao,
+ int dsp_index, u32 address, u32 data);
static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao,
int dsp_index);
@@ -227,25 +218,13 @@ static u16 boot_loader_test_pld(struct hpi_adapter_obj *pao, int dsp_index);
/*****************************************************************************/
-static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
+static void subsys_message(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr)
{
-
switch (phm->function) {
- case HPI_SUBSYS_OPEN:
- case HPI_SUBSYS_CLOSE:
- case HPI_SUBSYS_GET_INFO:
- case HPI_SUBSYS_DRIVER_UNLOAD:
- case HPI_SUBSYS_DRIVER_LOAD:
- case HPI_SUBSYS_FIND_ADAPTERS:
- /* messages that should not get here */
- phr->error = HPI_ERROR_UNIMPLEMENTED;
- break;
case HPI_SUBSYS_CREATE_ADAPTER:
subsys_create_adapter(phm, phr);
break;
- case HPI_SUBSYS_DELETE_ADAPTER:
- subsys_delete_adapter(phm, phr);
- break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
break;
@@ -257,15 +236,22 @@ static void control_message(struct hpi_adapter_obj *pao,
{
struct hpi_hw_obj *phw = pao->priv;
+ u16 pending_cache_error = 0;
switch (phm->function) {
case HPI_CONTROL_GET_STATE:
if (pao->has_control_cache) {
- rmb(); /* make sure we see updates DM_aed from DSP */
- if (hpi_check_control_cache(phw->p_cache, phm, phr))
+ rmb(); /* make sure we see updates DMAed from DSP */
+ if (hpi_check_control_cache(phw->p_cache, phm, phr)) {
break;
+ } else if (phm->u.c.attribute == HPI_METER_PEAK) {
+ pending_cache_error =
+ HPI_ERROR_CONTROL_CACHING;
+ }
}
hw_message(pao, phm, phr);
+ if (pending_cache_error && !phr->error)
+ phr->error = pending_cache_error;
break;
case HPI_CONTROL_GET_INFO:
hw_message(pao, phm, phr);
@@ -273,7 +259,8 @@ static void control_message(struct hpi_adapter_obj *pao,
case HPI_CONTROL_SET_STATE:
hw_message(pao, phm, phr);
if (pao->has_control_cache)
- hpi_sync_control_cache(phw->p_cache, phm, phr);
+ hpi_cmn_control_cache_sync_to_msg(phw->p_cache, phm,
+ phr);
break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
@@ -285,6 +272,9 @@ static void adapter_message(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
+ case HPI_ADAPTER_DELETE:
+ adapter_delete(pao, phm, phr);
+ break;
default:
hw_message(pao, phm, phr);
break;
@@ -296,9 +286,9 @@ static void outstream_message(struct hpi_adapter_obj *pao,
{
if (phm->obj_index >= HPI_MAX_STREAMS) {
- phr->error = HPI_ERROR_INVALID_STREAM;
+ phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
HPI_DEBUG_LOG(WARNING,
- "message referencing invalid stream %d "
+ "Message referencing invalid stream %d "
"on adapter index %d\n", phm->obj_index,
phm->adapter_index);
return;
@@ -340,9 +330,9 @@ static void instream_message(struct hpi_adapter_obj *pao,
{
if (phm->obj_index >= HPI_MAX_STREAMS) {
- phr->error = HPI_ERROR_INVALID_STREAM;
+ phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
HPI_DEBUG_LOG(WARNING,
- "message referencing invalid stream %d "
+ "Message referencing invalid stream %d "
"on adapter index %d\n", phm->obj_index,
phm->adapter_index);
return;
@@ -377,59 +367,36 @@ static void instream_message(struct hpi_adapter_obj *pao,
/** Entry point to this HPI backend
* All calls to the HPI start here
*/
-void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
+static
+void _HPI_6205(struct hpi_adapter_obj *pao, struct hpi_message *phm,
+ struct hpi_response *phr)
{
- struct hpi_adapter_obj *pao = NULL;
-
- /* subsytem messages are processed by every HPI.
- * All other messages are ignored unless the adapter index matches
- * an adapter in the HPI
- */
- HPI_DEBUG_LOG(DEBUG, "HPI obj=%d, func=%d\n", phm->object,
- phm->function);
-
- /* if Dsp has crashed then do not communicate with it any more */
- if (phm->object != HPI_OBJ_SUBSYSTEM) {
- pao = hpi_find_adapter(phm->adapter_index);
- if (!pao) {
- HPI_DEBUG_LOG(DEBUG,
- " %d,%d refused, for another HPI?\n",
- phm->object, phm->function);
- return;
- }
-
- if ((pao->dsp_crashed >= 10)
- && (phm->function != HPI_ADAPTER_DEBUG_READ)) {
- /* allow last resort debug read even after crash */
- hpi_init_response(phr, phm->object, phm->function,
- HPI_ERROR_DSP_HARDWARE);
- HPI_DEBUG_LOG(WARNING, " %d,%d dsp crashed.\n",
- phm->object, phm->function);
- return;
- }
+ if (pao && (pao->dsp_crashed >= 10)
+ && (phm->function != HPI_ADAPTER_DEBUG_READ)) {
+ /* allow last resort debug read even after crash */
+ hpi_init_response(phr, phm->object, phm->function,
+ HPI_ERROR_DSP_HARDWARE);
+ HPI_DEBUG_LOG(WARNING, " %d,%d dsp crashed.\n", phm->object,
+ phm->function);
+ return;
}
/* Init default response */
if (phm->function != HPI_SUBSYS_CREATE_ADAPTER)
- hpi_init_response(phr, phm->object, phm->function,
- HPI_ERROR_PROCESSING_MESSAGE);
+ phr->error = HPI_ERROR_PROCESSING_MESSAGE;
HPI_DEBUG_LOG(VERBOSE, "start of switch\n");
switch (phm->type) {
- case HPI_TYPE_MESSAGE:
+ case HPI_TYPE_REQUEST:
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
- subsys_message(phm, phr);
+ subsys_message(pao, phm, phr);
break;
case HPI_OBJ_ADAPTER:
- phr->size =
- sizeof(struct hpi_response_header) +
- sizeof(struct hpi_adapter_res);
adapter_message(pao, phm, phr);
break;
- case HPI_OBJ_CONTROLEX:
case HPI_OBJ_CONTROL:
control_message(pao, phm, phr);
break;
@@ -454,11 +421,31 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
}
}
+void HPI_6205(struct hpi_message *phm, struct hpi_response *phr)
+{
+ struct hpi_adapter_obj *pao = NULL;
+
+ if (phm->object != HPI_OBJ_SUBSYSTEM) {
+ /* normal messages must have valid adapter index */
+ pao = hpi_find_adapter(phm->adapter_index);
+ } else {
+ /* subsys messages don't address an adapter */
+ phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
+ return;
+ }
+
+ if (pao)
+ _HPI_6205(pao, phm, phr);
+ else
+ hpi_init_response(phr, phm->object, phm->function,
+ HPI_ERROR_BAD_ADAPTER_NUMBER);
+}
+
/*****************************************************************************/
/* SUBSYSTEM */
/** Create an adapter object and initialise it based on resource information
- * passed in in the message
+ * passed in the message
* *** NOTE - you cannot use this function AND the FindAdapters function at the
* same time, the application must use only one of them to get the adapters ***
*/
@@ -474,51 +461,43 @@ static void subsys_create_adapter(struct hpi_message *phm,
memset(&ao, 0, sizeof(ao));
- /* this HPI only creates adapters for TI/PCI devices */
- if (phm->u.s.resource.bus_type != HPI_BUS_PCI)
- return;
- if (phm->u.s.resource.r.pci->vendor_id != HPI_PCI_VENDOR_ID_TI)
- return;
- if (phm->u.s.resource.r.pci->device_id != HPI_PCI_DEV_ID_DSP6205)
- return;
-
ao.priv = kzalloc(sizeof(struct hpi_hw_obj), GFP_KERNEL);
if (!ao.priv) {
- HPI_DEBUG_LOG(ERROR, "cant get mem for adapter object\n");
+ HPI_DEBUG_LOG(ERROR, "can't get mem for adapter object\n");
phr->error = HPI_ERROR_MEMORY_ALLOC;
return;
}
ao.pci = *phm->u.s.resource.r.pci;
err = create_adapter_obj(&ao, &os_error_code);
- if (!err)
- err = hpi_add_adapter(&ao);
if (err) {
- phr->u.s.data = os_error_code;
delete_adapter_obj(&ao);
- phr->error = err;
+ if (err >= HPI_ERROR_BACKEND_BASE) {
+ phr->error = HPI_ERROR_DSP_BOOTLOAD;
+ phr->specific_error = err;
+ } else {
+ phr->error = err;
+ }
+ phr->u.s.data = os_error_code;
return;
}
- phr->u.s.aw_adapter_list[ao.index] = ao.adapter_type;
+ phr->u.s.adapter_type = ao.type;
phr->u.s.adapter_index = ao.index;
- phr->u.s.num_adapters++;
phr->error = 0;
}
/** delete an adapter - required by WDM driver */
-static void subsys_delete_adapter(struct hpi_message *phm,
- struct hpi_response *phr)
+static void adapter_delete(struct hpi_adapter_obj *pao,
+ struct hpi_message *phm, struct hpi_response *phr)
{
- struct hpi_adapter_obj *pao;
struct hpi_hw_obj *phw;
- pao = hpi_find_adapter(phm->adapter_index);
if (!pao) {
phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
return;
}
- phw = (struct hpi_hw_obj *)pao->priv;
+ phw = pao->priv;
/* reset adapter h/w */
/* Reset C6713 #1 */
boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 0);
@@ -526,6 +505,7 @@ static void subsys_delete_adapter(struct hpi_message *phm,
iowrite32(C6205_HDCR_WARMRESET, phw->prHDCR);
delete_adapter_obj(pao);
+ hpi_delete_adapter(pao);
phr->error = 0;
}
@@ -538,10 +518,6 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
struct hpi_hw_obj *phw = pao->priv;
struct bus_master_interface *interface;
u32 phys_addr;
-#ifndef HPI6205_NO_HSR_POLL
- u32 time_out = HPI6205_TIMEOUT;
- u32 temp1;
-#endif
int i;
u16 err;
@@ -566,7 +542,7 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
if (hpios_locked_mem_alloc(&phw->h_locked_mem,
sizeof(struct bus_master_interface),
- pao->pci.p_os_data))
+ pao->pci.pci_dev))
phw->p_interface_buffer = NULL;
else if (hpios_locked_mem_get_virt_addr(&phw->h_locked_mem,
(void *)&phw->p_interface_buffer))
@@ -582,58 +558,39 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
}
err = adapter_boot_load_dsp(pao, pos_error_code);
- if (err)
+ if (err) {
+ HPI_DEBUG_LOG(ERROR, "DSP code load failed\n");
/* no need to clean up as SubSysCreateAdapter */
/* calls DeleteAdapter on error. */
return err;
-
+ }
HPI_DEBUG_LOG(INFO, "load DSP code OK\n");
/* allow boot load even if mem alloc wont work */
if (!phw->p_interface_buffer)
- return hpi6205_error(0, HPI_ERROR_MEMORY_ALLOC);
+ return HPI_ERROR_MEMORY_ALLOC;
interface = phw->p_interface_buffer;
-#ifndef HPI6205_NO_HSR_POLL
- /* wait for first interrupt indicating the DSP init is done */
- time_out = HPI6205_TIMEOUT * 10;
- temp1 = 0;
- while (((temp1 & C6205_HSR_INTSRC) == 0) && --time_out)
- temp1 = ioread32(phw->prHSR);
-
- if (temp1 & C6205_HSR_INTSRC)
- HPI_DEBUG_LOG(INFO,
- "interrupt confirming DSP code running OK\n");
- else {
- HPI_DEBUG_LOG(ERROR,
- "timed out waiting for interrupt "
- "confirming DSP code running\n");
- return hpi6205_error(0, HPI6205_ERROR_6205_NO_IRQ);
- }
-
- /* reset the interrupt */
- iowrite32(C6205_HSR_INTSRC, phw->prHSR);
-#endif
-
/* make sure the DSP has started ok */
if (!wait_dsp_ack(phw, H620_HIF_RESET, HPI6205_TIMEOUT * 10)) {
HPI_DEBUG_LOG(ERROR, "timed out waiting reset state \n");
- return hpi6205_error(0, HPI6205_ERROR_6205_INIT_FAILED);
+ return HPI6205_ERROR_6205_INIT_FAILED;
}
/* Note that *pao, *phw are zeroed after allocation,
* so pointers and flags are NULL by default.
* Allocate bus mastering control cache buffer and tell the DSP about it
*/
if (interface->control_cache.number_of_controls) {
- void *p_control_cache_virtual;
+ u8 *p_control_cache_virtual;
err = hpios_locked_mem_alloc(&phw->h_control_cache,
interface->control_cache.size_in_bytes,
- pao->pci.p_os_data);
+ pao->pci.pci_dev);
if (!err)
err = hpios_locked_mem_get_virt_addr(&phw->
- h_control_cache, &p_control_cache_virtual);
+ h_control_cache,
+ (void *)&p_control_cache_virtual);
if (!err) {
memset(p_control_cache_virtual, 0,
interface->control_cache.size_in_bytes);
@@ -642,8 +599,10 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
hpi_alloc_control_cache(interface->
control_cache.number_of_controls,
interface->control_cache.size_in_bytes,
- (struct hpi_control_cache_info *)
p_control_cache_virtual);
+
+ if (!phw->p_cache)
+ err = HPI_ERROR_MEMORY_ALLOC;
}
if (!err) {
err = hpios_locked_mem_get_phys_addr(&phw->
@@ -660,96 +619,64 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao,
pao->has_control_cache = 0;
}
}
- /* allocate bus mastering async buffer and tell the DSP about it */
- if (interface->async_buffer.b.size) {
- err = hpios_locked_mem_alloc(&phw->h_async_event_buffer,
- interface->async_buffer.b.size *
- sizeof(struct hpi_async_event), pao->pci.p_os_data);
- if (!err)
- err = hpios_locked_mem_get_virt_addr
- (&phw->h_async_event_buffer, (void *)
- &phw->p_async_event_buffer);
- if (!err)
- memset((void *)phw->p_async_event_buffer, 0,
- interface->async_buffer.b.size *
- sizeof(struct hpi_async_event));
- if (!err) {
- err = hpios_locked_mem_get_phys_addr
- (&phw->h_async_event_buffer, &phys_addr);
- interface->async_buffer.physical_address32 =
- phys_addr;
- }
- if (err) {
- if (hpios_locked_mem_valid(&phw->
- h_async_event_buffer)) {
- hpios_locked_mem_free
- (&phw->h_async_event_buffer);
- phw->p_async_event_buffer = NULL;
- }
- }
- }
send_dsp_command(phw, H620_HIF_IDLE);
{
- struct hpi_message hM;
- struct hpi_response hR;
- u32 max_streams;
+ struct hpi_message hm;
+ struct hpi_response hr;
HPI_DEBUG_LOG(VERBOSE, "init ADAPTER_GET_INFO\n");
- memset(&hM, 0, sizeof(hM));
- hM.type = HPI_TYPE_MESSAGE;
- hM.size = sizeof(hM);
- hM.object = HPI_OBJ_ADAPTER;
- hM.function = HPI_ADAPTER_GET_INFO;
- hM.adapter_index = 0;
- memset(&hR, 0, sizeof(hR));
- hR.size = sizeof(hR);
-
- err = message_response_sequence(pao, &hM, &hR);
+ memset(&hm, 0, sizeof(hm));
+ /* wAdapterIndex == version == 0 */
+ hm.type = HPI_TYPE_REQUEST;
+ hm.size = sizeof(hm);
+ hm.object = HPI_OBJ_ADAPTER;
+ hm.function = HPI_ADAPTER_GET_INFO;
+
+ memset(&hr, 0, sizeof(hr));
+ hr.size = sizeof(hr);
+
+ err = message_response_sequence(pao, &hm, &hr);
if (err) {
HPI_DEBUG_LOG(ERROR, "message transport error %d\n",
err);
return err;
}
- if (hR.error)
- return hR.error;
-
- pao->adapter_type = hR.u.a.adapter_type;
- pao->index = hR.u.a.adapter_index;
+ if (hr.error)
+ return hr.error;
- max_streams = hR.u.a.num_outstreams + hR.u.a.num_instreams;
-
- hpios_locked_mem_prepare((max_streams * 6) / 10, max_streams,
- 65536, pao->pci.p_os_data);
+ pao->type = hr.u.ax.info.adapter_type;
+ pao->index = hr.u.ax.info.adapter_index;
HPI_DEBUG_LOG(VERBOSE,
"got adapter info type %x index %d serial %d\n",
- hR.u.a.adapter_type, hR.u.a.adapter_index,
- hR.u.a.serial_number);
+ hr.u.ax.info.adapter_type, hr.u.ax.info.adapter_index,
+ hr.u.ax.info.serial_number);
}
- pao->open = 0; /* upon creation the adapter is closed */
+ if (phw->p_cache)
+ phw->p_cache->adap_idx = pao->index;
HPI_DEBUG_LOG(INFO, "bootload DSP OK\n");
- return 0;
+
+ pao->irq_query_and_clear = adapter_irq_query_and_clear;
+ pao->instream_host_buffer_status =
+ phw->p_interface_buffer->instream_host_buffer_status;
+ pao->outstream_host_buffer_status =
+ phw->p_interface_buffer->outstream_host_buffer_status;
+
+ return hpi_add_adapter(pao);
}
/** Free memory areas allocated by adapter
- * this routine is called from SubSysDeleteAdapter,
+ * this routine is called from AdapterDelete,
* and SubSysCreateAdapter if duplicate index
*/
static void delete_adapter_obj(struct hpi_adapter_obj *pao)
{
- struct hpi_hw_obj *phw;
+ struct hpi_hw_obj *phw = pao->priv;
int i;
- phw = pao->priv;
-
- if (hpios_locked_mem_valid(&phw->h_async_event_buffer)) {
- hpios_locked_mem_free(&phw->h_async_event_buffer);
- phw->p_async_event_buffer = NULL;
- }
-
if (hpios_locked_mem_valid(&phw->h_control_cache)) {
hpios_locked_mem_free(&phw->h_control_cache);
hpi_free_control_cache(phw->p_cache);
@@ -773,11 +700,25 @@ static void delete_adapter_obj(struct hpi_adapter_obj *pao)
[i]);
phw->outstream_host_buffer_size[i] = 0;
}
+ kfree(phw);
+}
- hpios_locked_mem_unprepare(pao->pci.p_os_data);
+/*****************************************************************************/
+/* Adapter functions */
+static int adapter_irq_query_and_clear(struct hpi_adapter_obj *pao,
+ u32 message)
+{
+ struct hpi_hw_obj *phw = pao->priv;
+ u32 hsr = 0;
- hpi_delete_adapter(pao);
- kfree(phw);
+ hsr = ioread32(phw->prHSR);
+ if (hsr & C6205_HSR_INTSRC) {
+ /* reset the interrupt from the DSP */
+ iowrite32(C6205_HSR_INTSRC, phw->prHSR);
+ return HPI_IRQ_MIXER;
+ }
+
+ return HPI_IRQ_NONE;
}
/*****************************************************************************/
@@ -822,7 +763,7 @@ static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao,
err = hpios_locked_mem_alloc(&phw->outstream_host_buffers
[phm->obj_index], phm->u.d.u.buffer.buffer_size,
- pao->pci.p_os_data);
+ pao->pci.pci_dev);
if (err) {
phr->error = HPI_ERROR_INVALID_DATASIZE;
@@ -859,7 +800,7 @@ static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao,
if (phm->u.d.u.buffer.buffer_size & (phm->u.d.u.buffer.
buffer_size - 1)) {
HPI_DEBUG_LOG(ERROR,
- "buffer size must be 2^N not %d\n",
+ "Buffer size must be 2^N not %d\n",
phm->u.d.u.buffer.buffer_size);
phr->error = HPI_ERROR_INVALID_DATASIZE;
return;
@@ -870,9 +811,10 @@ static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao,
obj_index];
status->samples_processed = 0;
status->stream_state = HPI_STATE_STOPPED;
- status->dSP_index = 0;
- status->host_index = status->dSP_index;
+ status->dsp_index = 0;
+ status->host_index = status->dsp_index;
status->size_in_bytes = phm->u.d.u.buffer.buffer_size;
+ status->auxiliary_data_available = 0;
hw_message(pao, phm, phr);
@@ -944,7 +886,7 @@ static void outstream_host_buffer_free(struct hpi_adapter_obj *pao,
static u32 outstream_get_space_available(struct hpi_hostbuffer_status *status)
{
return status->size_in_bytes - (status->host_index -
- status->dSP_index);
+ status->dsp_index);
}
static void outstream_write(struct hpi_adapter_obj *pao,
@@ -964,51 +906,6 @@ static void outstream_write(struct hpi_adapter_obj *pao,
hpi_init_response(phr, phm->object, phm->function, 0);
status = &interface->outstream_host_buffer_status[phm->obj_index];
- if (phw->flag_outstream_just_reset[phm->obj_index]) {
- /* First OutStremWrite() call following reset will write data to the
- adapter's buffers, reducing delay before stream can start. The DSP
- takes care of setting the stream data format using format information
- embedded in phm.
- */
- int partial_write = 0;
- unsigned int original_size = 0;
-
- phw->flag_outstream_just_reset[phm->obj_index] = 0;
-
- /* Send the first buffer to the DSP the old way. */
- /* Limit size of first transfer - */
- /* expect that this will not usually be triggered. */
- if (phm->u.d.u.data.data_size > HPI6205_SIZEOF_DATA) {
- partial_write = 1;
- original_size = phm->u.d.u.data.data_size;
- phm->u.d.u.data.data_size = HPI6205_SIZEOF_DATA;
- }
- /* write it */
- phm->function = HPI_OSTREAM_WRITE;
- hw_message(pao, phm, phr);
-
- if (phr->error)
- return;
-
- /* update status information that the DSP would typically
- * update (and will update next time the DSP
- * buffer update task reads data from the host BBM buffer)
- */
- status->auxiliary_data_available = phm->u.d.u.data.data_size;
- status->host_index += phm->u.d.u.data.data_size;
- status->dSP_index += phm->u.d.u.data.data_size;
-
- /* if we did a full write, we can return from here. */
- if (!partial_write)
- return;
-
- /* tweak buffer parameters and let the rest of the */
- /* buffer land in internal BBM buffer */
- phm->u.d.u.data.data_size =
- original_size - HPI6205_SIZEOF_DATA;
- phm->u.d.u.data.pb_data += HPI6205_SIZEOF_DATA;
- }
-
space_available = outstream_get_space_available(status);
if (space_available < phm->u.d.u.data.data_size) {
phr->error = HPI_ERROR_INVALID_DATASIZE;
@@ -1045,6 +942,24 @@ static void outstream_write(struct hpi_adapter_obj *pao,
memcpy(p_bbm_data, p_app_data + l_first_write,
phm->u.d.u.data.data_size - l_first_write);
}
+
+ /*
+ * This version relies on the DSP code triggering an OStream buffer
+ * update immediately following a SET_FORMAT call. The host has
+ * already written data into the BBM buffer, but the DSP won't know
+ * about it until dwHostIndex is adjusted.
+ */
+ if (phw->flag_outstream_just_reset[phm->obj_index]) {
+ /* Format can only change after reset. Must tell DSP. */
+ u16 function = phm->function;
+ phw->flag_outstream_just_reset[phm->obj_index] = 0;
+ phm->function = HPI_OSTREAM_SET_FORMAT;
+ hw_message(pao, phm, phr); /* send the format to the DSP */
+ phm->function = function;
+ if (phr->error)
+ return;
+ }
+
status->host_index += phm->u.d.u.data.data_size;
}
@@ -1130,7 +1045,7 @@ static void instream_host_buffer_allocate(struct hpi_adapter_obj *pao,
err = hpios_locked_mem_alloc(&phw->instream_host_buffers[phm->
obj_index], phm->u.d.u.buffer.buffer_size,
- pao->pci.p_os_data);
+ pao->pci.pci_dev);
if (err) {
phr->error = HPI_ERROR_INVALID_DATASIZE;
@@ -1161,7 +1076,7 @@ static void instream_host_buffer_allocate(struct hpi_adapter_obj *pao,
if (phm->u.d.u.buffer.buffer_size & (phm->u.d.u.buffer.
buffer_size - 1)) {
HPI_DEBUG_LOG(ERROR,
- "buffer size must be 2^N not %d\n",
+ "Buffer size must be 2^N not %d\n",
phm->u.d.u.buffer.buffer_size);
phr->error = HPI_ERROR_INVALID_DATASIZE;
return;
@@ -1173,11 +1088,13 @@ static void instream_host_buffer_allocate(struct hpi_adapter_obj *pao,
obj_index];
status->samples_processed = 0;
status->stream_state = HPI_STATE_STOPPED;
- status->dSP_index = 0;
- status->host_index = status->dSP_index;
+ status->dsp_index = 0;
+ status->host_index = status->dsp_index;
status->size_in_bytes = phm->u.d.u.buffer.buffer_size;
+ status->auxiliary_data_available = 0;
hw_message(pao, phm, phr);
+
if (phr->error
&& hpios_locked_mem_valid(&phw->
instream_host_buffers[phm->obj_index])) {
@@ -1253,7 +1170,7 @@ static void instream_start(struct hpi_adapter_obj *pao,
static u32 instream_get_bytes_available(struct hpi_hostbuffer_status *status)
{
- return status->dSP_index - status->host_index;
+ return status->dsp_index - status->host_index;
}
static void instream_read(struct hpi_adapter_obj *pao,
@@ -1342,33 +1259,37 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
struct hpi_hw_obj *phw = pao->priv;
struct dsp_code dsp_code;
u16 boot_code_id[HPI6205_MAX_FILES_TO_LOAD];
- u16 firmware_id = pao->pci.subsys_device_id;
u32 temp;
int dsp = 0, i = 0;
u16 err = 0;
boot_code_id[0] = HPI_ADAPTER_ASI(0x6205);
- /* special cases where firmware_id != subsys ID */
- switch (firmware_id) {
+ boot_code_id[1] = pao->pci.pci_dev->subsystem_device;
+ boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(boot_code_id[1]);
+
+ /* fix up cases where bootcode id[1] != subsys id */
+ switch (boot_code_id[1]) {
case HPI_ADAPTER_FAMILY_ASI(0x5000):
- boot_code_id[0] = firmware_id;
- firmware_id = 0;
+ boot_code_id[0] = boot_code_id[1];
+ boot_code_id[1] = 0;
break;
case HPI_ADAPTER_FAMILY_ASI(0x5300):
case HPI_ADAPTER_FAMILY_ASI(0x5400):
case HPI_ADAPTER_FAMILY_ASI(0x6300):
- firmware_id = HPI_ADAPTER_FAMILY_ASI(0x6400);
+ boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6400);
break;
+ case HPI_ADAPTER_FAMILY_ASI(0x5500):
case HPI_ADAPTER_FAMILY_ASI(0x5600):
case HPI_ADAPTER_FAMILY_ASI(0x6500):
- firmware_id = HPI_ADAPTER_FAMILY_ASI(0x6600);
+ boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6600);
break;
case HPI_ADAPTER_FAMILY_ASI(0x8800):
- firmware_id = HPI_ADAPTER_FAMILY_ASI(0x8900);
+ boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x8900);
+ break;
+ default:
break;
}
- boot_code_id[1] = firmware_id;
/* reset DSP by writing a 1 to the WARMRESET bit */
temp = C6205_HDCR_WARMRESET;
@@ -1379,7 +1300,7 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
temp = ioread32(phw->prHSR);
if ((temp & (C6205_HSR_CFGERR | C6205_HSR_EEREAD)) !=
C6205_HSR_EEREAD)
- return hpi6205_error(0, HPI6205_ERROR_6205_EEPROM);
+ return HPI6205_ERROR_6205_EEPROM;
temp |= 0x04;
/* disable PINTA interrupt */
iowrite32(temp, phw->prHSR);
@@ -1387,27 +1308,27 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
/* check control register reports PCI boot mode */
temp = ioread32(phw->prHDCR);
if (!(temp & C6205_HDCR_PCIBOOT))
- return hpi6205_error(0, HPI6205_ERROR_6205_REG);
+ return HPI6205_ERROR_6205_REG;
- /* try writing a couple of numbers to the DSP page register */
+ /* try writing a few numbers to the DSP page register */
/* and reading them back. */
- temp = 1;
+ temp = 3;
iowrite32(temp, phw->prDSPP);
if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP))
- return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE);
+ return HPI6205_ERROR_6205_DSPPAGE;
temp = 2;
iowrite32(temp, phw->prDSPP);
if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP))
- return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE);
- temp = 3;
+ return HPI6205_ERROR_6205_DSPPAGE;
+ temp = 1;
iowrite32(temp, phw->prDSPP);
if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP))
- return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE);
+ return HPI6205_ERROR_6205_DSPPAGE;
/* reset DSP page to the correct number */
temp = 0;
iowrite32(temp, phw->prDSPP);
if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP))
- return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE);
+ return HPI6205_ERROR_6205_DSPPAGE;
phw->dsp_page = 0;
/* release 6713 from reset before 6205 is bootloaded.
@@ -1417,17 +1338,21 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
if (boot_code_id[1] != 0) {
/* DSP 1 is a C6713 */
/* CLKX0 <- '1' release the C6205 bootmode pulldowns */
- boot_loader_write_mem32(pao, 0, (0x018C0024L), 0x00002202);
+ boot_loader_write_mem32(pao, 0, 0x018C0024, 0x00002202);
hpios_delay_micro_seconds(100);
/* Reset the 6713 #1 - revB */
boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 0);
-
- /* dummy read every 4 words for 6205 advisory 1.4.4 */
- boot_loader_read_mem32(pao, 0, 0);
-
+ /* value of bit 3 is unknown after DSP reset, other bits shoudl be 0 */
+ if (0 != (boot_loader_read_mem32(pao, 0,
+ (C6205_BAR0_TIMER1_CTL)) & ~8))
+ return HPI6205_ERROR_6205_REG;
hpios_delay_micro_seconds(100);
+
/* Release C6713 from reset - revB */
boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 4);
+ if (4 != (boot_loader_read_mem32(pao, 0,
+ (C6205_BAR0_TIMER1_CTL)) & ~8))
+ return HPI6205_ERROR_6205_REG;
hpios_delay_micro_seconds(100);
}
@@ -1453,9 +1378,8 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
return err;
/* write the DSP code down into the DSPs memory */
- dsp_code.ps_dev = pao->pci.p_os_data;
- err = hpi_dsp_code_open(boot_code_id[dsp], &dsp_code,
- pos_error_code);
+ err = hpi_dsp_code_open(boot_code_id[dsp], pao->pci.pci_dev,
+ &dsp_code, pos_error_code);
if (err)
return err;
@@ -1482,10 +1406,8 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
if (err)
break;
for (i = 0; i < (int)length; i++) {
- err = boot_loader_write_mem32(pao, dsp,
- address, *pcode);
- if (err)
- break;
+ boot_loader_write_mem32(pao, dsp, address,
+ *pcode);
/* dummy read every 4 words */
/* for 6205 advisory 1.4.4 */
if (i % 4 == 0)
@@ -1559,7 +1481,7 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao,
host_mailbox_address_on_dsp = 0x80000000;
while ((physicalPC_iaddress != physicalPC_iaddress_verify)
&& time_out--) {
- err = boot_loader_write_mem32(pao, 0,
+ boot_loader_write_mem32(pao, 0,
host_mailbox_address_on_dsp,
physicalPC_iaddress);
physicalPC_iaddress_verify =
@@ -1629,11 +1551,10 @@ static u32 boot_loader_read_mem32(struct hpi_adapter_obj *pao, int dsp_index,
return data;
}
-static u16 boot_loader_write_mem32(struct hpi_adapter_obj *pao, int dsp_index,
- u32 address, u32 data)
+static void boot_loader_write_mem32(struct hpi_adapter_obj *pao,
+ int dsp_index, u32 address, u32 data)
{
struct hpi_hw_obj *phw = pao->priv;
- u16 err = 0;
__iomem u32 *p_data;
/* u32 dwVerifyData=0; */
@@ -1673,15 +1594,11 @@ static u16 boot_loader_write_mem32(struct hpi_adapter_obj *pao, int dsp_index,
/* dummy read every 4 words for 6205 advisory 1.4.4 */
boot_loader_read_mem32(pao, 0, 0);
- } else
- err = hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX);
- return err;
+ }
}
static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
{
- u16 err = 0;
-
if (dsp_index == 0) {
u32 setting;
@@ -1709,8 +1626,7 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
boot_loader_write_mem32(pao, dsp_index, 0x01800008, setting);
if (setting != boot_loader_read_mem32(pao, dsp_index,
0x01800008))
- return hpi6205_error(dsp_index,
- HPI6205_ERROR_DSP_EMIF);
+ return HPI6205_ERROR_DSP_EMIF1;
/* EMIF CE1 setup - 32 bit async. This is 6713 #1 HPI, */
/* which occupies D15..0. 6713 starts at 27MHz, so need */
@@ -1723,8 +1639,7 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
boot_loader_write_mem32(pao, dsp_index, 0x01800004, setting);
if (setting != boot_loader_read_mem32(pao, dsp_index,
0x01800004))
- return hpi6205_error(dsp_index,
- HPI6205_ERROR_DSP_EMIF);
+ return HPI6205_ERROR_DSP_EMIF2;
/* EMIF CE2 setup - 32 bit async. This is 6713 #2 HPI, */
/* which occupies D15..0. 6713 starts at 27MHz, so need */
@@ -1736,8 +1651,7 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
boot_loader_write_mem32(pao, dsp_index, 0x01800010, setting);
if (setting != boot_loader_read_mem32(pao, dsp_index,
0x01800010))
- return hpi6205_error(dsp_index,
- HPI6205_ERROR_DSP_EMIF);
+ return HPI6205_ERROR_DSP_EMIF3;
/* EMIF CE3 setup - 32 bit async. */
/* This is the PLD on the ASI5000 cards only */
@@ -1748,8 +1662,7 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
boot_loader_write_mem32(pao, dsp_index, 0x01800014, setting);
if (setting != boot_loader_read_mem32(pao, dsp_index,
0x01800014))
- return hpi6205_error(dsp_index,
- HPI6205_ERROR_DSP_EMIF);
+ return HPI6205_ERROR_DSP_EMIF4;
/* set EMIF SDRAM control for 2Mx32 SDRAM (512x32x4 bank) */
/* need to use this else DSP code crashes? */
@@ -1773,12 +1686,9 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
read_data =
0xFFF7 & boot_loader_read_mem32(pao, 0, HPICL_ADDR);
if (write_data != read_data) {
- err = hpi6205_error(dsp_index,
- HPI6205_ERROR_C6713_HPIC);
HPI_DEBUG_LOG(ERROR, "HPICL %x %x\n", write_data,
read_data);
-
- return err;
+ return HPI6205_ERROR_C6713_HPIC;
}
/* HPIA - walking ones test */
write_data = 1;
@@ -1796,11 +1706,9 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
HPIAH_ADDR))
<< 16);
if (read_data != write_data) {
- err = hpi6205_error(dsp_index,
- HPI6205_ERROR_C6713_HPIA);
HPI_DEBUG_LOG(ERROR, "HPIA %x %x\n",
write_data, read_data);
- return err;
+ return HPI6205_ERROR_C6713_HPIA;
}
write_data = write_data << 1;
}
@@ -1845,30 +1753,81 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index)
/* PLL should not be bypassed! */
if ((boot_loader_read_mem32(pao, dsp_index, 0x01B7C100) & 0xF)
!= 0x0001) {
- err = hpi6205_error(dsp_index,
- HPI6205_ERROR_C6713_PLL);
- return err;
+ return HPI6205_ERROR_C6713_PLL;
}
/* setup C67x EMIF (note this is the only use of
BAR1 via BootLoader_WriteMem32) */
boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_GCTL,
0x000034A8);
+
+ /* EMIF CE0 setup - 2Mx32 Sync DRAM
+ 31..28 Wr setup
+ 27..22 Wr strobe
+ 21..20 Wr hold
+ 19..16 Rd setup
+ 15..14 -
+ 13..8 Rd strobe
+ 7..4 MTYPE 0011 Sync DRAM 32bits
+ 3 Wr hold MSB
+ 2..0 Rd hold
+ */
boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_CE0,
0x00000030);
+
+ /* EMIF SDRAM Extension
+ 0x00
+ 31-21 0000b 0000b 000b
+ 20 WR2RD = 2cycles-1 = 1b
+
+ 19-18 WR2DEAC = 3cycle-1 = 10b
+ 17 WR2WR = 2cycle-1 = 1b
+ 16-15 R2WDQM = 4cycle-1 = 11b
+ 14-12 RD2WR = 6cycles-1 = 101b
+
+ 11-10 RD2DEAC = 4cycle-1 = 11b
+ 9 RD2RD = 2cycle-1 = 1b
+ 8-7 THZP = 3cycle-1 = 10b
+ 6-5 TWR = 2cycle-1 = 01b (tWR = 17ns)
+ 4 TRRD = 2cycle = 0b (tRRD = 14ns)
+ 3-1 TRAS = 5cycle-1 = 100b (Tras=42ns)
+ 1 CAS latency = 3cyc = 1b
+ (for Micron 2M32-7 operating at 100MHz)
+ */
boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_SDRAMEXT,
0x001BDF29);
+
+ /* EMIF SDRAM control - set up for a 2Mx32 SDRAM (512x32x4 bank)
+ 31 - 0b -
+ 30 SDBSZ 1b 4 bank
+ 29..28 SDRSZ 00b 11 row address pins
+
+ 27..26 SDCSZ 01b 8 column address pins
+ 25 RFEN 1b refersh enabled
+ 24 INIT 1b init SDRAM!
+
+ 23..20 TRCD 0001b (Trcd/Tcyc)-1 = (20/10)-1 = 1
+
+ 19..16 TRP 0001b (Trp/Tcyc)-1 = (20/10)-1 = 1
+
+ 15..12 TRC 0110b (Trc/Tcyc)-1 = (70/10)-1 = 6
+
+ 11..0 - 0000b 0000b 0000b
+ */
boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_SDRAMCTL,
- 0x47117000);
+ 0x47116000);
+
+ /* SDRAM refresh timing
+ Need 4,096 refresh cycles every 64ms = 15.625us = 1562cycles of 100MHz = 0x61A
+ */
boot_loader_write_mem32(pao, dsp_index,
C6713_EMIF_SDRAMTIMING, 0x00000410);
hpios_delay_micro_seconds(1000);
} else if (dsp_index == 2) {
/* DSP 2 is a C6713 */
+ }
- } else
- err = hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX);
- return err;
+ return 0;
}
static u16 boot_loader_test_memory(struct hpi_adapter_obj *pao, int dsp_index,
@@ -1894,7 +1853,7 @@ static u16 boot_loader_test_memory(struct hpi_adapter_obj *pao, int dsp_index,
test_addr);
if (data != test_data) {
HPI_DEBUG_LOG(VERBOSE,
- "memtest error details "
+ "Memtest error details "
"%08x %08x %08x %i\n", test_addr,
test_data, data, dsp_index);
return 1; /* error */
@@ -1914,7 +1873,7 @@ static u16 boot_loader_test_memory(struct hpi_adapter_obj *pao, int dsp_index,
data = boot_loader_read_mem32(pao, dsp_index, test_addr);
if (data != test_data) {
HPI_DEBUG_LOG(VERBOSE,
- "memtest error details "
+ "Memtest error details "
"%08x %08x %08x %i\n", test_addr, test_data,
data, dsp_index);
return 1; /* error */
@@ -1944,8 +1903,8 @@ static u16 boot_loader_test_internal_memory(struct hpi_adapter_obj *pao,
/* 64K data mem */
err = boot_loader_test_memory(pao, dsp_index,
0x80000000, 0x10000);
- } else if ((dsp_index == 1) || (dsp_index == 2)) {
- /* DSP 1&2 are a C6713 */
+ } else if (dsp_index == 1) {
+ /* DSP 1 is a C6713 */
/* 192K internal mem */
err = boot_loader_test_memory(pao, dsp_index, 0x00000000,
0x30000);
@@ -1953,11 +1912,10 @@ static u16 boot_loader_test_internal_memory(struct hpi_adapter_obj *pao,
/* 64K internal mem / L2 cache */
err = boot_loader_test_memory(pao, dsp_index,
0x00030000, 0x10000);
- } else
- return hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX);
+ }
if (err)
- return hpi6205_error(dsp_index, HPI6205_ERROR_DSP_INTMEM);
+ return HPI6205_ERROR_DSP_INTMEM;
else
return 0;
}
@@ -1970,24 +1928,23 @@ static u16 boot_loader_test_external_memory(struct hpi_adapter_obj *pao,
if (dsp_index == 0) {
/* only test for SDRAM if an ASI5000 card */
- if (pao->pci.subsys_device_id == 0x5000) {
+ if (pao->pci.pci_dev->subsystem_device == 0x5000) {
/* DSP 0 is always C6205 */
dRAM_start_address = 0x00400000;
dRAM_size = 0x200000;
/*dwDRAMinc=1024; */
} else
return 0;
- } else if ((dsp_index == 1) || (dsp_index == 2)) {
+ } else if (dsp_index == 1) {
/* DSP 1 is a C6713 */
dRAM_start_address = 0x80000000;
dRAM_size = 0x200000;
/*dwDRAMinc=1024; */
- } else
- return hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX);
+ }
if (boot_loader_test_memory(pao, dsp_index, dRAM_start_address,
dRAM_size))
- return hpi6205_error(dsp_index, HPI6205_ERROR_DSP_EXTMEM);
+ return HPI6205_ERROR_DSP_EXTMEM;
return 0;
}
@@ -1996,28 +1953,25 @@ static u16 boot_loader_test_pld(struct hpi_adapter_obj *pao, int dsp_index)
u32 data = 0;
if (dsp_index == 0) {
/* only test for DSP0 PLD on ASI5000 card */
- if (pao->pci.subsys_device_id == 0x5000) {
+ if (pao->pci.pci_dev->subsystem_device == 0x5000) {
/* PLD is located at CE3=0x03000000 */
data = boot_loader_read_mem32(pao, dsp_index,
0x03000008);
if ((data & 0xF) != 0x5)
- return hpi6205_error(dsp_index,
- HPI6205_ERROR_DSP_PLD);
+ return HPI6205_ERROR_DSP_PLD;
data = boot_loader_read_mem32(pao, dsp_index,
0x0300000C);
if ((data & 0xF) != 0xA)
- return hpi6205_error(dsp_index,
- HPI6205_ERROR_DSP_PLD);
+ return HPI6205_ERROR_DSP_PLD;
}
} else if (dsp_index == 1) {
/* DSP 1 is a C6713 */
- if (pao->pci.subsys_device_id == 0x8700) {
+ if (pao->pci.pci_dev->subsystem_device == 0x8700) {
/* PLD is located at CE1=0x90000000 */
data = boot_loader_read_mem32(pao, dsp_index,
0x90000010);
if ((data & 0xFF) != 0xAA)
- return hpi6205_error(dsp_index,
- HPI6205_ERROR_DSP_PLD);
+ return HPI6205_ERROR_DSP_PLD;
/* 8713 - LED on */
boot_loader_write_mem32(pao, dsp_index, 0x90000000,
0x02);
@@ -2035,14 +1989,11 @@ static short hpi6205_transfer_data(struct hpi_adapter_obj *pao, u8 *p_data,
struct hpi_hw_obj *phw = pao->priv;
u32 data_transferred = 0;
u16 err = 0;
-#ifndef HPI6205_NO_HSR_POLL
- u32 time_out;
-#endif
u32 temp2;
struct bus_master_interface *interface = phw->p_interface_buffer;
if (!p_data)
- return HPI_ERROR_INVALID_DATA_TRANSFER;
+ return HPI_ERROR_INVALID_DATA_POINTER;
data_size &= ~3L; /* round data_size down to nearest 4 bytes */
@@ -2062,14 +2013,10 @@ static short hpi6205_transfer_data(struct hpi_adapter_obj *pao, u8 *p_data,
interface->transfer_size_in_bytes = this_copy;
-#ifdef HPI6205_NO_HSR_POLL
/* DSP must change this back to nOperation */
interface->dsp_ack = H620_HIF_IDLE;
-#endif
-
send_dsp_command(phw, operation);
-#ifdef HPI6205_NO_HSR_POLL
temp2 = wait_dsp_ack(phw, operation, HPI6205_TIMEOUT);
HPI_DEBUG_LOG(DEBUG, "spun %d times for data xfer of %d\n",
HPI6205_TIMEOUT - temp2, this_copy);
@@ -2077,45 +2024,11 @@ static short hpi6205_transfer_data(struct hpi_adapter_obj *pao, u8 *p_data,
if (!temp2) {
/* timed out */
HPI_DEBUG_LOG(ERROR,
- "timed out waiting for " "state %d got %d\n",
+ "Timed out waiting for " "state %d got %d\n",
operation, interface->dsp_ack);
break;
}
-#else
- /* spin waiting on the result */
- time_out = HPI6205_TIMEOUT;
- temp2 = 0;
- while ((temp2 == 0) && time_out--) {
- /* give 16k bus mastering transfer time to happen */
- /*(16k / 132Mbytes/s = 122usec) */
- hpios_delay_micro_seconds(20);
- temp2 = ioread32(phw->prHSR);
- temp2 &= C6205_HSR_INTSRC;
- }
- HPI_DEBUG_LOG(DEBUG, "spun %d times for data xfer of %d\n",
- HPI6205_TIMEOUT - time_out, this_copy);
- if (temp2 == C6205_HSR_INTSRC) {
- HPI_DEBUG_LOG(VERBOSE,
- "interrupt from HIF <data> OK\n");
- /*
- if(interface->dwDspAck != nOperation) {
- HPI_DEBUG_LOG(DEBUG("interface->dwDspAck=%d,
- expected %d \n",
- interface->dwDspAck,nOperation);
- }
- */
- }
-/* need to handle this differently... */
- else {
- HPI_DEBUG_LOG(ERROR,
- "interrupt from HIF <data> BAD\n");
- err = HPI_ERROR_DSP_HARDWARE;
- }
-
- /* reset the interrupt from the DSP */
- iowrite32(C6205_HSR_INTSRC, phw->prHSR);
-#endif
if (operation == H620_HIF_GET_DATA)
memcpy(&p_data[data_transferred],
(void *)&interface->u.b_data[0], this_copy);
@@ -2154,7 +2067,6 @@ static int wait_dsp_ack(struct hpi_hw_obj *phw, int state, int timeout_us)
static void send_dsp_command(struct hpi_hw_obj *phw, int cmd)
{
struct bus_master_interface *interface = phw->p_interface_buffer;
-
u32 r;
interface->host_cmd = cmd;
@@ -2172,31 +2084,39 @@ static unsigned int message_count;
static u16 message_response_sequence(struct hpi_adapter_obj *pao,
struct hpi_message *phm, struct hpi_response *phr)
{
-#ifndef HPI6205_NO_HSR_POLL
- u32 temp2;
-#endif
u32 time_out, time_out2;
struct hpi_hw_obj *phw = pao->priv;
struct bus_master_interface *interface = phw->p_interface_buffer;
u16 err = 0;
message_count++;
- /* Assume buffer of type struct bus_master_interface
+ if (phm->size > sizeof(interface->u.message_buffer)) {
+ phr->error = HPI_ERROR_MESSAGE_BUFFER_TOO_SMALL;
+ phr->specific_error = sizeof(interface->u.message_buffer);
+ phr->size = sizeof(struct hpi_response_header);
+ HPI_DEBUG_LOG(ERROR,
+ "message len %d too big for buffer %zd \n", phm->size,
+ sizeof(interface->u.message_buffer));
+ return 0;
+ }
+
+ /* Assume buffer of type struct bus_master_interface_62
is allocated "noncacheable" */
if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) {
HPI_DEBUG_LOG(DEBUG, "timeout waiting for idle\n");
- return hpi6205_error(0, HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT);
+ return HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT;
}
- interface->u.message_buffer = *phm;
+
+ memcpy(&interface->u.message_buffer, phm, phm->size);
/* signal we want a response */
send_dsp_command(phw, H620_HIF_GET_RESP);
time_out2 = wait_dsp_ack(phw, H620_HIF_GET_RESP, HPI6205_TIMEOUT);
- if (time_out2 == 0) {
+ if (!time_out2) {
HPI_DEBUG_LOG(ERROR,
- "(%u) timed out waiting for " "GET_RESP state [%x]\n",
+ "(%u) Timed out waiting for " "GET_RESP state [%x]\n",
message_count, interface->dsp_ack);
} else {
HPI_DEBUG_LOG(VERBOSE,
@@ -2206,58 +2126,39 @@ static u16 message_response_sequence(struct hpi_adapter_obj *pao,
/* spin waiting on HIF interrupt flag (end of msg process) */
time_out = HPI6205_TIMEOUT;
-#ifndef HPI6205_NO_HSR_POLL
- temp2 = 0;
- while ((temp2 == 0) && --time_out) {
- temp2 = ioread32(phw->prHSR);
- temp2 &= C6205_HSR_INTSRC;
- hpios_delay_micro_seconds(1);
- }
- if (temp2 == C6205_HSR_INTSRC) {
- rmb(); /* ensure we see latest value for dsp_ack */
- if ((interface->dsp_ack != H620_HIF_GET_RESP)) {
- HPI_DEBUG_LOG(DEBUG,
- "(%u)interface->dsp_ack(0x%x) != "
- "H620_HIF_GET_RESP, t=%u\n", message_count,
- interface->dsp_ack,
- HPI6205_TIMEOUT - time_out);
- } else {
- HPI_DEBUG_LOG(VERBOSE,
- "(%u)int with GET_RESP after %u\n",
- message_count, HPI6205_TIMEOUT - time_out);
+ /* read the result */
+ if (time_out) {
+ if (interface->u.response_buffer.response.size <= phr->size)
+ memcpy(phr, &interface->u.response_buffer,
+ interface->u.response_buffer.response.size);
+ else {
+ HPI_DEBUG_LOG(ERROR,
+ "response len %d too big for buffer %d\n",
+ interface->u.response_buffer.response.size,
+ phr->size);
+ memcpy(phr, &interface->u.response_buffer,
+ sizeof(struct hpi_response_header));
+ phr->error = HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
+ phr->specific_error =
+ interface->u.response_buffer.response.size;
+ phr->size = sizeof(struct hpi_response_header);
}
-
- } else {
- /* can we do anything else in response to the error ? */
- HPI_DEBUG_LOG(ERROR,
- "interrupt from HIF module BAD (function %x)\n",
- phm->function);
}
-
- /* reset the interrupt from the DSP */
- iowrite32(C6205_HSR_INTSRC, phw->prHSR);
-#endif
-
- /* read the result */
- if (time_out != 0)
- *phr = interface->u.response_buffer;
-
/* set interface back to idle */
send_dsp_command(phw, H620_HIF_IDLE);
- if ((time_out == 0) || (time_out2 == 0)) {
+ if (!time_out || !time_out2) {
HPI_DEBUG_LOG(DEBUG, "something timed out!\n");
- return hpi6205_error(0, HPI6205_ERROR_MSG_RESP_TIMEOUT);
+ return HPI6205_ERROR_MSG_RESP_TIMEOUT;
}
/* special case for adapter close - */
/* wait for the DSP to indicate it is idle */
if (phm->function == HPI_ADAPTER_CLOSE) {
if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) {
HPI_DEBUG_LOG(DEBUG,
- "timeout waiting for idle "
+ "Timeout waiting for idle "
"(on adapter_close)\n");
- return hpi6205_error(0,
- HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT);
+ return HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT;
}
}
err = hpi_validate_response(phm, phr);
@@ -2277,7 +2178,13 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
/* maybe an error response */
if (err) {
/* something failed in the HPI/DSP interface */
- phr->error = err;
+ if (err >= HPI_ERROR_BACKEND_BASE) {
+ phr->error = HPI_ERROR_DSP_COMMUNICATION;
+ phr->specific_error = err;
+ } else {
+ phr->error = err;
+ }
+
pao->dsp_crashed++;
/* just the header of the response is valid */
@@ -2302,23 +2209,6 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm,
phm->u.d.u.data.data_size, H620_HIF_GET_DATA);
break;
- case HPI_CONTROL_SET_STATE:
- if (phm->object == HPI_OBJ_CONTROLEX
- && phm->u.cx.attribute == HPI_COBRANET_SET_DATA)
- err = hpi6205_transfer_data(pao,
- phm->u.cx.u.cobranet_bigdata.pb_data,
- phm->u.cx.u.cobranet_bigdata.byte_count,
- H620_HIF_SEND_DATA);
- break;
-
- case HPI_CONTROL_GET_STATE:
- if (phm->object == HPI_OBJ_CONTROLEX
- && phm->u.cx.attribute == HPI_COBRANET_GET_DATA)
- err = hpi6205_transfer_data(pao,
- phm->u.cx.u.cobranet_bigdata.pb_data,
- phr->u.cx.u.cobranet_data.byte_count,
- H620_HIF_GET_DATA);
- break;
}
phr->error = err;
diff --git a/sound/pci/asihpi/hpi6205.h b/sound/pci/asihpi/hpi6205.h
index 1adae0857cda..cc70e99a40e9 100644
--- a/sound/pci/asihpi/hpi6205.h
+++ b/sound/pci/asihpi/hpi6205.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*****************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Host Interface module for an ASI6205 based
bus mastering PCI adapter.
@@ -25,9 +14,6 @@ Copyright AudioScience, Inc., 2003
#ifndef _HPI6205_H_
#define _HPI6205_H_
-/* transitional conditional compile shared between host and DSP */
-/* #define HPI6205_NO_HSR_POLL */
-
#include "hpi_internal.h"
/***********************************************************
@@ -73,15 +59,28 @@ The Host located memory buffer that the 6205 will bus master
in and out of.
************************************************************/
#define HPI6205_SIZEOF_DATA (16*1024)
+
+struct message_buffer_6205 {
+ struct hpi_message message;
+ char data[256];
+};
+
+struct response_buffer_6205 {
+ struct hpi_response response;
+ char data[256];
+};
+
+union buffer_6205 {
+ struct message_buffer_6205 message_buffer;
+ struct response_buffer_6205 response_buffer;
+ u8 b_data[HPI6205_SIZEOF_DATA];
+};
+
struct bus_master_interface {
u32 host_cmd;
u32 dsp_ack;
u32 transfer_size_in_bytes;
- union {
- struct hpi_message message_buffer;
- struct hpi_response response_buffer;
- u8 b_data[HPI6205_SIZEOF_DATA];
- } u;
+ union buffer_6205 u;
struct controlcache_6205 control_cache;
struct async_event_buffer_6205 async_buffer;
struct hpi_hostbuffer_status
diff --git a/sound/pci/asihpi/hpi_internal.h b/sound/pci/asihpi/hpi_internal.h
index 16f502d459de..e569e3b33b8e 100644
--- a/sound/pci/asihpi/hpi_internal.h
+++ b/sound/pci/asihpi/hpi_internal.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2012 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
HPI internal definitions
@@ -25,19 +14,14 @@ HPI internal definitions
#define _HPI_INTERNAL_H_
#include "hpi.h"
+
/** maximum number of memory regions mapped to an adapter */
#define HPI_MAX_ADAPTER_MEM_SPACES (2)
-/* Each OS needs its own hpios.h, or specific define as above */
+/* Each OS needs its own hpios.h */
#include "hpios.h"
/* physical memory allocation */
-void hpios_locked_mem_init(void
- );
-void hpios_locked_mem_free_all(void
- );
-#define hpios_locked_mem_prepare(a, b, c, d);
-#define hpios_locked_mem_unprepare(a)
/** Allocate and map an area of locked memory for bus master DMA operations.
@@ -49,7 +33,7 @@ HpiOs_LockedMem_GetPyhsAddr() will always succed on the returned handle.
*/
u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_locked_mem_handle,
/**< memory handle */
- u32 size, /**< size in bytes to allocate */
+ u32 size, /**< Size in bytes to allocate */
struct pci_dev *p_os_reference
/**< OS specific data required for memory allocation */
);
@@ -69,7 +53,7 @@ If handle is invalid *pPhysicalAddr is set to zero and return 1
u16 hpios_locked_mem_get_phys_addr(struct consistent_dma_area
*locked_mem_handle, u32 *p_physical_addr);
-/** Get the CPU address of of memory represented by LockedMemHandle.
+/** Get the CPU address of memory represented by LockedMemHandle.
If handle is NULL *ppvVirtualAddr is set to NULL and return 1
*/
@@ -96,41 +80,6 @@ typedef void hpi_handler_func(struct hpi_message *, struct hpi_response *);
#define compile_time_assert(cond, msg) \
typedef char ASSERT_##msg[(cond) ? 1 : -1]
-/*/////////////////////////////////////////////////////////////////////////// */
-/* Private HPI Entity related definitions */
-
-#define STR_SIZE_FIELD_MAX 65535U
-#define STR_TYPE_FIELD_MAX 255U
-#define STR_ROLE_FIELD_MAX 255U
-
-struct hpi_entity_str {
- u16 size;
- u8 type;
- u8 role;
-};
-
-#if defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable : 4200)
-#endif
-
-struct hpi_entity {
- struct hpi_entity_str header;
-#if ! defined(HPI_OS_DSP_C6000) || (defined(HPI_OS_DSP_C6000) && (__TI_COMPILER_VERSION__ > 6000008))
- /* DSP C6000 compiler v6.0.8 and lower
- do not support flexible array member */
- u8 value[];
-#else
- /* NOTE! Using sizeof(struct hpi_entity) will give erroneous results */
-#define HPI_INTERNAL_WARN_ABOUT_ENTITY_VALUE
- u8 value[1];
-#endif
-};
-
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
/******************************************* bus types */
enum HPI_BUSES {
HPI_BUS_ISAPNP = 1,
@@ -139,206 +88,155 @@ enum HPI_BUSES {
HPI_BUS_NET = 4
};
+enum HPI_SUBSYS_OPTIONS {
+ /* 0, 256 are invalid, 1..255 reserved for global options */
+ HPI_SUBSYS_OPT_NET_ENABLE = 257,
+ HPI_SUBSYS_OPT_NET_BROADCAST = 258,
+ HPI_SUBSYS_OPT_NET_UNICAST = 259,
+ HPI_SUBSYS_OPT_NET_ADDR = 260,
+ HPI_SUBSYS_OPT_NET_MASK = 261,
+ HPI_SUBSYS_OPT_NET_ADAPTER_ADDRESS_ADD = 262
+};
+
+/** Volume flags
+*/
+enum HPI_VOLUME_FLAGS {
+ /** Set if the volume control is muted */
+ HPI_VOLUME_FLAG_MUTED = (1 << 0),
+ /** Set if the volume control has a mute function */
+ HPI_VOLUME_FLAG_HAS_MUTE = (1 << 1),
+ /** Set if volume control can do autofading */
+ HPI_VOLUME_FLAG_HAS_AUTOFADE = (1 << 2)
+ /* Note Flags >= (1<<8) are for DSP internal use only */
+};
+
/******************************************* CONTROL ATTRIBUTES ****/
/* (in order of control type ID */
/* This allows for 255 control types, 256 unique attributes each */
-#define HPI_CTL_ATTR(ctl, ai) (HPI_CONTROL_##ctl * 0x100 + ai)
+#define HPI_CTL_ATTR(ctl, ai) ((HPI_CONTROL_##ctl << 8) + ai)
/* Get the sub-index of the attribute for a control type */
-#define HPI_CTL_ATTR_INDEX(i) (i&0xff)
+#define HPI_CTL_ATTR_INDEX(i) (i & 0xff)
/* Extract the control from the control attribute */
-#define HPI_CTL_ATTR_CONTROL(i) (i>>8)
-
-/* Generic control attributes. */
-
-/** Enable a control.
-0=disable, 1=enable
-\note generic to all mixer plugins?
-*/
-#define HPI_GENERIC_ENABLE HPI_CTL_ATTR(GENERIC, 1)
+#define HPI_CTL_ATTR_CONTROL(i) (i >> 8)
/** Enable event generation for a control.
0=disable, 1=enable
\note generic to all controls that can generate events
*/
-#define HPI_GENERIC_EVENT_ENABLE HPI_CTL_ATTR(GENERIC, 2)
-
-/* Volume Control attributes */
-#define HPI_VOLUME_GAIN HPI_CTL_ATTR(VOLUME, 1)
-#define HPI_VOLUME_AUTOFADE HPI_CTL_ATTR(VOLUME, 2)
-
-/** For HPI_ControlQuery() to get the number of channels of a volume control*/
-#define HPI_VOLUME_NUM_CHANNELS HPI_CTL_ATTR(VOLUME, 6)
-#define HPI_VOLUME_RANGE HPI_CTL_ATTR(VOLUME, 10)
-
-/** Level Control attributes */
-#define HPI_LEVEL_GAIN HPI_CTL_ATTR(LEVEL, 1)
-#define HPI_LEVEL_RANGE HPI_CTL_ATTR(LEVEL, 10)
-
-/* Meter Control attributes */
-/** return RMS signal level */
-#define HPI_METER_RMS HPI_CTL_ATTR(METER, 1)
-/** return peak signal level */
-#define HPI_METER_PEAK HPI_CTL_ATTR(METER, 2)
-/** ballistics for ALL rms meters on adapter */
-#define HPI_METER_RMS_BALLISTICS HPI_CTL_ATTR(METER, 3)
-/** ballistics for ALL peak meters on adapter */
-#define HPI_METER_PEAK_BALLISTICS HPI_CTL_ATTR(METER, 4)
-
-/** For HPI_ControlQuery() to get the number of channels of a meter control*/
-#define HPI_METER_NUM_CHANNELS HPI_CTL_ATTR(METER, 5)
-
-/* Multiplexer control attributes */
-#define HPI_MULTIPLEXER_SOURCE HPI_CTL_ATTR(MULTIPLEXER, 1)
-#define HPI_MULTIPLEXER_QUERYSOURCE HPI_CTL_ATTR(MULTIPLEXER, 2)
-
-/** AES/EBU transmitter control attributes */
-/** AESEBU or SPDIF */
-#define HPI_AESEBUTX_FORMAT HPI_CTL_ATTR(AESEBUTX, 1)
-#define HPI_AESEBUTX_SAMPLERATE HPI_CTL_ATTR(AESEBUTX, 3)
-#define HPI_AESEBUTX_CHANNELSTATUS HPI_CTL_ATTR(AESEBUTX, 4)
-#define HPI_AESEBUTX_USERDATA HPI_CTL_ATTR(AESEBUTX, 5)
-
-/** AES/EBU receiver control attributes */
-#define HPI_AESEBURX_FORMAT HPI_CTL_ATTR(AESEBURX, 1)
-#define HPI_AESEBURX_ERRORSTATUS HPI_CTL_ATTR(AESEBURX, 2)
-#define HPI_AESEBURX_SAMPLERATE HPI_CTL_ATTR(AESEBURX, 3)
-#define HPI_AESEBURX_CHANNELSTATUS HPI_CTL_ATTR(AESEBURX, 4)
-#define HPI_AESEBURX_USERDATA HPI_CTL_ATTR(AESEBURX, 5)
-
-/** \defgroup tuner_defs Tuners
-\{
-*/
-/** \defgroup tuner_attrs Tuner control attributes
-\{
-*/
-#define HPI_TUNER_BAND HPI_CTL_ATTR(TUNER, 1)
-#define HPI_TUNER_FREQ HPI_CTL_ATTR(TUNER, 2)
-#define HPI_TUNER_LEVEL HPI_CTL_ATTR(TUNER, 3)
-#define HPI_TUNER_AUDIOMUTE HPI_CTL_ATTR(TUNER, 4)
-/* use TUNER_STATUS instead */
-#define HPI_TUNER_VIDEO_STATUS HPI_CTL_ATTR(TUNER, 5)
-#define HPI_TUNER_GAIN HPI_CTL_ATTR(TUNER, 6)
-#define HPI_TUNER_STATUS HPI_CTL_ATTR(TUNER, 7)
-#define HPI_TUNER_MODE HPI_CTL_ATTR(TUNER, 8)
-/** RDS data. */
-#define HPI_TUNER_RDS HPI_CTL_ATTR(TUNER, 9)
-/** Audio pre-emphasis. */
-#define HPI_TUNER_DEEMPHASIS HPI_CTL_ATTR(TUNER, 10)
-/** HD Radio tuner program control. */
-#define HPI_TUNER_PROGRAM HPI_CTL_ATTR(TUNER, 11)
-/** HD Radio tuner digital signal quality. */
-#define HPI_TUNER_HDRADIO_SIGNAL_QUALITY HPI_CTL_ATTR(TUNER, 12)
-/** HD Radio SDK firmware version. */
-#define HPI_TUNER_HDRADIO_SDK_VERSION HPI_CTL_ATTR(TUNER, 13)
-/** HD Radio DSP firmware version. */
-#define HPI_TUNER_HDRADIO_DSP_VERSION HPI_CTL_ATTR(TUNER, 14)
-/** HD Radio signal blend (force analog, or automatic). */
-#define HPI_TUNER_HDRADIO_BLEND HPI_CTL_ATTR(TUNER, 15)
-
-/** \} */
-
-/** \defgroup pads_attrs Tuner PADs control attributes
-\{
-*/
-/** The text string containing the station/channel combination. */
-#define HPI_PAD_CHANNEL_NAME HPI_CTL_ATTR(PAD, 1)
-/** The text string containing the artist. */
-#define HPI_PAD_ARTIST HPI_CTL_ATTR(PAD, 2)
-/** The text string containing the title. */
-#define HPI_PAD_TITLE HPI_CTL_ATTR(PAD, 3)
-/** The text string containing the comment. */
-#define HPI_PAD_COMMENT HPI_CTL_ATTR(PAD, 4)
-/** The integer containing the PTY code. */
-#define HPI_PAD_PROGRAM_TYPE HPI_CTL_ATTR(PAD, 5)
-/** The integer containing the program identification. */
-#define HPI_PAD_PROGRAM_ID HPI_CTL_ATTR(PAD, 6)
-/** The integer containing whether traffic information is supported.
-Contains either 1 or 0. */
-#define HPI_PAD_TA_SUPPORT HPI_CTL_ATTR(PAD, 7)
-/** The integer containing whether traffic announcement is in progress.
-Contains either 1 or 0. */
-#define HPI_PAD_TA_ACTIVE HPI_CTL_ATTR(PAD, 8)
-/** \} */
-/** \} */
-
-/* VOX control attributes */
-#define HPI_VOX_THRESHOLD HPI_CTL_ATTR(VOX, 1)
-
-/*?? channel mode used hpi_multiplexer_source attribute == 1 */
-#define HPI_CHANNEL_MODE_MODE HPI_CTL_ATTR(CHANNEL_MODE, 1)
-
-/** \defgroup channel_modes Channel Modes
-Used for HPI_ChannelModeSet/Get()
-\{
+
+/** Unique identifiers for every control attribute
*/
-/** Left channel out = left channel in, Right channel out = right channel in. */
-#define HPI_CHANNEL_MODE_NORMAL 1
-/** Left channel out = right channel in, Right channel out = left channel in. */
-#define HPI_CHANNEL_MODE_SWAP 2
-/** Left channel out = left channel in, Right channel out = left channel in. */
-#define HPI_CHANNEL_MODE_LEFT_TO_STEREO 3
-/** Left channel out = right channel in, Right channel out = right channel in.*/
-#define HPI_CHANNEL_MODE_RIGHT_TO_STEREO 4
-/** Left channel out = (left channel in + right channel in)/2,
- Right channel out = mute. */
-#define HPI_CHANNEL_MODE_STEREO_TO_LEFT 5
-/** Left channel out = mute,
- Right channel out = (right channel in + left channel in)/2. */
-#define HPI_CHANNEL_MODE_STEREO_TO_RIGHT 6
-#define HPI_CHANNEL_MODE_LAST 6
-/** \} */
-
-/* Bitstream control set attributes */
-#define HPI_BITSTREAM_DATA_POLARITY HPI_CTL_ATTR(BITSTREAM, 1)
-#define HPI_BITSTREAM_CLOCK_EDGE HPI_CTL_ATTR(BITSTREAM, 2)
-#define HPI_BITSTREAM_CLOCK_SOURCE HPI_CTL_ATTR(BITSTREAM, 3)
+enum HPI_CONTROL_ATTRIBUTES {
+ HPI_GENERIC_ENABLE = HPI_CTL_ATTR(GENERIC, 1),
+ HPI_GENERIC_EVENT_ENABLE = HPI_CTL_ATTR(GENERIC, 2),
+
+ HPI_VOLUME_GAIN = HPI_CTL_ATTR(VOLUME, 1),
+ HPI_VOLUME_AUTOFADE = HPI_CTL_ATTR(VOLUME, 2),
+ HPI_VOLUME_MUTE = HPI_CTL_ATTR(VOLUME, 3),
+ HPI_VOLUME_GAIN_AND_FLAGS = HPI_CTL_ATTR(VOLUME, 4),
+ HPI_VOLUME_NUM_CHANNELS = HPI_CTL_ATTR(VOLUME, 6),
+ HPI_VOLUME_RANGE = HPI_CTL_ATTR(VOLUME, 10),
+
+ HPI_METER_RMS = HPI_CTL_ATTR(METER, 1),
+ HPI_METER_PEAK = HPI_CTL_ATTR(METER, 2),
+ HPI_METER_RMS_BALLISTICS = HPI_CTL_ATTR(METER, 3),
+ HPI_METER_PEAK_BALLISTICS = HPI_CTL_ATTR(METER, 4),
+ HPI_METER_NUM_CHANNELS = HPI_CTL_ATTR(METER, 5),
+
+ HPI_MULTIPLEXER_SOURCE = HPI_CTL_ATTR(MULTIPLEXER, 1),
+ HPI_MULTIPLEXER_QUERYSOURCE = HPI_CTL_ATTR(MULTIPLEXER, 2),
+
+ HPI_AESEBUTX_FORMAT = HPI_CTL_ATTR(AESEBUTX, 1),
+ HPI_AESEBUTX_SAMPLERATE = HPI_CTL_ATTR(AESEBUTX, 3),
+ HPI_AESEBUTX_CHANNELSTATUS = HPI_CTL_ATTR(AESEBUTX, 4),
+ HPI_AESEBUTX_USERDATA = HPI_CTL_ATTR(AESEBUTX, 5),
+
+ HPI_AESEBURX_FORMAT = HPI_CTL_ATTR(AESEBURX, 1),
+ HPI_AESEBURX_ERRORSTATUS = HPI_CTL_ATTR(AESEBURX, 2),
+ HPI_AESEBURX_SAMPLERATE = HPI_CTL_ATTR(AESEBURX, 3),
+ HPI_AESEBURX_CHANNELSTATUS = HPI_CTL_ATTR(AESEBURX, 4),
+ HPI_AESEBURX_USERDATA = HPI_CTL_ATTR(AESEBURX, 5),
+
+ HPI_LEVEL_GAIN = HPI_CTL_ATTR(LEVEL, 1),
+ HPI_LEVEL_RANGE = HPI_CTL_ATTR(LEVEL, 10),
+
+ HPI_TUNER_BAND = HPI_CTL_ATTR(TUNER, 1),
+ HPI_TUNER_FREQ = HPI_CTL_ATTR(TUNER, 2),
+ HPI_TUNER_LEVEL_AVG = HPI_CTL_ATTR(TUNER, 3),
+ HPI_TUNER_LEVEL_RAW = HPI_CTL_ATTR(TUNER, 4),
+ HPI_TUNER_SNR = HPI_CTL_ATTR(TUNER, 5),
+ HPI_TUNER_GAIN = HPI_CTL_ATTR(TUNER, 6),
+ HPI_TUNER_STATUS = HPI_CTL_ATTR(TUNER, 7),
+ HPI_TUNER_MODE = HPI_CTL_ATTR(TUNER, 8),
+ HPI_TUNER_RDS = HPI_CTL_ATTR(TUNER, 9),
+ HPI_TUNER_DEEMPHASIS = HPI_CTL_ATTR(TUNER, 10),
+ HPI_TUNER_PROGRAM = HPI_CTL_ATTR(TUNER, 11),
+ HPI_TUNER_HDRADIO_SIGNAL_QUALITY = HPI_CTL_ATTR(TUNER, 12),
+ HPI_TUNER_HDRADIO_SDK_VERSION = HPI_CTL_ATTR(TUNER, 13),
+ HPI_TUNER_HDRADIO_DSP_VERSION = HPI_CTL_ATTR(TUNER, 14),
+ HPI_TUNER_HDRADIO_BLEND = HPI_CTL_ATTR(TUNER, 15),
+
+ HPI_VOX_THRESHOLD = HPI_CTL_ATTR(VOX, 1),
+
+ HPI_CHANNEL_MODE_MODE = HPI_CTL_ATTR(CHANNEL_MODE, 1),
+
+ HPI_BITSTREAM_DATA_POLARITY = HPI_CTL_ATTR(BITSTREAM, 1),
+ HPI_BITSTREAM_CLOCK_EDGE = HPI_CTL_ATTR(BITSTREAM, 2),
+ HPI_BITSTREAM_CLOCK_SOURCE = HPI_CTL_ATTR(BITSTREAM, 3),
+ HPI_BITSTREAM_ACTIVITY = HPI_CTL_ATTR(BITSTREAM, 4),
+
+ HPI_SAMPLECLOCK_SOURCE = HPI_CTL_ATTR(SAMPLECLOCK, 1),
+ HPI_SAMPLECLOCK_SAMPLERATE = HPI_CTL_ATTR(SAMPLECLOCK, 2),
+ HPI_SAMPLECLOCK_SOURCE_INDEX = HPI_CTL_ATTR(SAMPLECLOCK, 3),
+ HPI_SAMPLECLOCK_LOCAL_SAMPLERATE = HPI_CTL_ATTR(SAMPLECLOCK, 4),
+ HPI_SAMPLECLOCK_AUTO = HPI_CTL_ATTR(SAMPLECLOCK, 5),
+ HPI_SAMPLECLOCK_LOCAL_LOCK = HPI_CTL_ATTR(SAMPLECLOCK, 6),
+
+ HPI_MICROPHONE_PHANTOM_POWER = HPI_CTL_ATTR(MICROPHONE, 1),
+
+ HPI_EQUALIZER_NUM_FILTERS = HPI_CTL_ATTR(EQUALIZER, 1),
+ HPI_EQUALIZER_FILTER = HPI_CTL_ATTR(EQUALIZER, 2),
+ HPI_EQUALIZER_COEFFICIENTS = HPI_CTL_ATTR(EQUALIZER, 3),
+
+ HPI_COMPANDER_PARAMS = HPI_CTL_ATTR(COMPANDER, 1),
+ HPI_COMPANDER_MAKEUPGAIN = HPI_CTL_ATTR(COMPANDER, 2),
+ HPI_COMPANDER_THRESHOLD = HPI_CTL_ATTR(COMPANDER, 3),
+ HPI_COMPANDER_RATIO = HPI_CTL_ATTR(COMPANDER, 4),
+ HPI_COMPANDER_ATTACK = HPI_CTL_ATTR(COMPANDER, 5),
+ HPI_COMPANDER_DECAY = HPI_CTL_ATTR(COMPANDER, 6),
+
+ HPI_COBRANET_SET = HPI_CTL_ATTR(COBRANET, 1),
+ HPI_COBRANET_GET = HPI_CTL_ATTR(COBRANET, 2),
+ HPI_COBRANET_GET_STATUS = HPI_CTL_ATTR(COBRANET, 5),
+ HPI_COBRANET_SEND_PACKET = HPI_CTL_ATTR(COBRANET, 6),
+ HPI_COBRANET_GET_PACKET = HPI_CTL_ATTR(COBRANET, 7),
+
+ HPI_TONEDETECTOR_THRESHOLD = HPI_CTL_ATTR(TONEDETECTOR, 1),
+ HPI_TONEDETECTOR_STATE = HPI_CTL_ATTR(TONEDETECTOR, 2),
+ HPI_TONEDETECTOR_FREQUENCY = HPI_CTL_ATTR(TONEDETECTOR, 3),
+
+ HPI_SILENCEDETECTOR_THRESHOLD = HPI_CTL_ATTR(SILENCEDETECTOR, 1),
+ HPI_SILENCEDETECTOR_STATE = HPI_CTL_ATTR(SILENCEDETECTOR, 2),
+ HPI_SILENCEDETECTOR_DELAY = HPI_CTL_ATTR(SILENCEDETECTOR, 3),
+
+ HPI_PAD_CHANNEL_NAME = HPI_CTL_ATTR(PAD, 1),
+ HPI_PAD_ARTIST = HPI_CTL_ATTR(PAD, 2),
+ HPI_PAD_TITLE = HPI_CTL_ATTR(PAD, 3),
+ HPI_PAD_COMMENT = HPI_CTL_ATTR(PAD, 4),
+ HPI_PAD_PROGRAM_TYPE = HPI_CTL_ATTR(PAD, 5),
+ HPI_PAD_PROGRAM_ID = HPI_CTL_ATTR(PAD, 6),
+ HPI_PAD_TA_SUPPORT = HPI_CTL_ATTR(PAD, 7),
+ HPI_PAD_TA_ACTIVE = HPI_CTL_ATTR(PAD, 8),
+
+ HPI_UNIVERSAL_ENTITY = HPI_CTL_ATTR(UNIVERSAL, 1)
+};
#define HPI_POLARITY_POSITIVE 0
#define HPI_POLARITY_NEGATIVE 1
-/* Bitstream control get attributes */
-#define HPI_BITSTREAM_ACTIVITY 1
-
-/* SampleClock control attributes */
-#define HPI_SAMPLECLOCK_SOURCE HPI_CTL_ATTR(SAMPLECLOCK, 1)
-#define HPI_SAMPLECLOCK_SAMPLERATE HPI_CTL_ATTR(SAMPLECLOCK, 2)
-#define HPI_SAMPLECLOCK_SOURCE_INDEX HPI_CTL_ATTR(SAMPLECLOCK, 3)
-#define HPI_SAMPLECLOCK_LOCAL_SAMPLERATE\
- HPI_CTL_ATTR(SAMPLECLOCK, 4)
-#define HPI_SAMPLECLOCK_AUTO HPI_CTL_ATTR(SAMPLECLOCK, 5)
-#define HPI_SAMPLECLOCK_LOCAL_LOCK HPI_CTL_ATTR(SAMPLECLOCK, 6)
-
-/* Microphone control attributes */
-#define HPI_MICROPHONE_PHANTOM_POWER HPI_CTL_ATTR(MICROPHONE, 1)
-
-/** Equalizer control attributes */
-/** Used to get number of filters in an EQ. (Can't set) */
-#define HPI_EQUALIZER_NUM_FILTERS HPI_CTL_ATTR(EQUALIZER, 1)
-/** Set/get the filter by type, freq, Q, gain */
-#define HPI_EQUALIZER_FILTER HPI_CTL_ATTR(EQUALIZER, 2)
-/** Get the biquad coefficients */
-#define HPI_EQUALIZER_COEFFICIENTS HPI_CTL_ATTR(EQUALIZER, 3)
-
-/* Note compander also uses HPI_GENERIC_ENABLE */
-#define HPI_COMPANDER_PARAMS HPI_CTL_ATTR(COMPANDER, 1)
-#define HPI_COMPANDER_MAKEUPGAIN HPI_CTL_ATTR(COMPANDER, 2)
-#define HPI_COMPANDER_THRESHOLD HPI_CTL_ATTR(COMPANDER, 3)
-#define HPI_COMPANDER_RATIO HPI_CTL_ATTR(COMPANDER, 4)
-#define HPI_COMPANDER_ATTACK HPI_CTL_ATTR(COMPANDER, 5)
-#define HPI_COMPANDER_DECAY HPI_CTL_ATTR(COMPANDER, 6)
-
-/* Cobranet control attributes. */
-#define HPI_COBRANET_SET HPI_CTL_ATTR(COBRANET, 1)
-#define HPI_COBRANET_GET HPI_CTL_ATTR(COBRANET, 2)
-#define HPI_COBRANET_SET_DATA HPI_CTL_ATTR(COBRANET, 3)
-#define HPI_COBRANET_GET_DATA HPI_CTL_ATTR(COBRANET, 4)
-#define HPI_COBRANET_GET_STATUS HPI_CTL_ATTR(COBRANET, 5)
-#define HPI_COBRANET_SEND_PACKET HPI_CTL_ATTR(COBRANET, 6)
-#define HPI_COBRANET_GET_PACKET HPI_CTL_ATTR(COBRANET, 7)
-
/*------------------------------------------------------------
Cobranet Chip Bridge - copied from HMI.H
------------------------------------------------------------*/
@@ -380,7 +278,7 @@ Used for HPI_ChannelModeSet/Get()
/* These defines are used to fill in protocol information for an Ethernet packet
sent using HMI on CS18102 */
-/** ID supplied by Cirrius for ASI packets. */
+/** ID supplied by Cirrus for ASI packets. */
#define HPI_ETHERNET_PACKET_ID 0x85
/** Simple packet - no special routing required */
#define HPI_ETHERNET_PACKET_V1 0x01
@@ -393,71 +291,24 @@ Used for HPI_ChannelModeSet/Get()
/** This packet must make its way to the host across the HPI interface */
#define HPI_ETHERNET_PACKET_HOSTED_VIA_HPI_V1 0x41
-#define HPI_ETHERNET_UDP_PORT (44600) /*!< UDP messaging port */
-
-/** Base network time out is set to 100 milli-seconds. */
-#define HPI_ETHERNET_TIMEOUT_MS (100)
-
-/** \defgroup tonedet_attr Tonedetector attributes
-\{
-Used by HPI_ToneDetector_Set() and HPI_ToneDetector_Get()
-*/
-
-/** Set the threshold level of a tonedetector,
-Threshold is a -ve number in units of dB/100,
-*/
-#define HPI_TONEDETECTOR_THRESHOLD HPI_CTL_ATTR(TONEDETECTOR, 1)
-
-/** Get the current state of tonedetection
-The result is a bitmap of detected tones. pairs of bits represent the left
-and right channels, with left channel in LSB.
-The lowest frequency detector state is in the LSB
-*/
-#define HPI_TONEDETECTOR_STATE HPI_CTL_ATTR(TONEDETECTOR, 2)
-
-/** Get the frequency of a tonedetector band.
-*/
-#define HPI_TONEDETECTOR_FREQUENCY HPI_CTL_ATTR(TONEDETECTOR, 3)
+#define HPI_ETHERNET_UDP_PORT 44600 /**< HPI UDP service */
-/**\}*/
+/** Default network timeout in milli-seconds. */
+#define HPI_ETHERNET_TIMEOUT_MS 500
-/** \defgroup silencedet_attr SilenceDetector attributes
-\{
-*/
-
-/** Get the current state of tonedetection
-The result is a bitmap with 1s for silent channels. Left channel is in LSB
-*/
-#define HPI_SILENCEDETECTOR_STATE \
- HPI_CTL_ATTR(SILENCEDETECTOR, 2)
-
-/** Set the threshold level of a SilenceDetector,
-Threshold is a -ve number in units of dB/100,
-*/
-#define HPI_SILENCEDETECTOR_THRESHOLD \
- HPI_CTL_ATTR(SILENCEDETECTOR, 1)
-
-/** get/set the silence time before the detector triggers
-*/
-#define HPI_SILENCEDETECTOR_DELAY \
- HPI_CTL_ATTR(SILENCEDETECTOR, 3)
-
-/**\}*/
-
-/* Locked memory buffer alloc/free phases */
-/** use one message to allocate or free physical memory */
-#define HPI_BUFFER_CMD_EXTERNAL 0
-/** alloc physical memory */
-#define HPI_BUFFER_CMD_INTERNAL_ALLOC 1
-/** send physical memory address to adapter */
-#define HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER 2
-/** notify adapter to stop using physical buffer */
-#define HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER 3
-/** free physical buffer */
-#define HPI_BUFFER_CMD_INTERNAL_FREE 4
-
-/******************************************* CONTROLX ATTRIBUTES ****/
-/* NOTE: All controlx attributes must be unique, unlike control attributes */
+/** Locked memory buffer alloc/free phases */
+enum HPI_BUFFER_CMDS {
+ /** use one message to allocate or free physical memory */
+ HPI_BUFFER_CMD_EXTERNAL = 0,
+ /** alloc physical memory */
+ HPI_BUFFER_CMD_INTERNAL_ALLOC = 1,
+ /** send physical memory address to adapter */
+ HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER = 2,
+ /** notify adapter to stop using physical buffer */
+ HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER = 3,
+ /** free physical buffer */
+ HPI_BUFFER_CMD_INTERNAL_FREE = 4
+};
/*****************************************************************************/
/*****************************************************************************/
@@ -482,6 +333,12 @@ Threshold is a -ve number in units of dB/100,
#define HPI_USB_W2K_TAG 0x57495341 /* "ASIW" */
#define HPI_USB_LINUX_TAG 0x4C495341 /* "ASIL" */
+/** Invalid Adapter index
+Used in HPI messages that are not addressed to a specific adapter
+Used in DLL to indicate device not present
+*/
+#define HPI_ADAPTER_INDEX_INVALID 0xFFFF
+
/** First 2 hex digits define the adapter family */
#define HPI_ADAPTER_FAMILY_MASK 0xff00
#define HPI_MODULE_FAMILY_MASK 0xfff0
@@ -490,178 +347,189 @@ Threshold is a -ve number in units of dB/100,
#define HPI_MODULE_FAMILY_ASI(f) (f & HPI_MODULE_FAMILY_MASK)
#define HPI_ADAPTER_ASI(f) (f)
-/******************************************* message types */
-#define HPI_TYPE_MESSAGE 1
-#define HPI_TYPE_RESPONSE 2
-#define HPI_TYPE_DATA 3
-#define HPI_TYPE_SSX2BYPASS_MESSAGE 4
-
-/******************************************* object types */
-#define HPI_OBJ_SUBSYSTEM 1
-#define HPI_OBJ_ADAPTER 2
-#define HPI_OBJ_OSTREAM 3
-#define HPI_OBJ_ISTREAM 4
-#define HPI_OBJ_MIXER 5
-#define HPI_OBJ_NODE 6
-#define HPI_OBJ_CONTROL 7
-#define HPI_OBJ_NVMEMORY 8
-#define HPI_OBJ_GPIO 9
-#define HPI_OBJ_WATCHDOG 10
-#define HPI_OBJ_CLOCK 11
-#define HPI_OBJ_PROFILE 12
-#define HPI_OBJ_CONTROLEX 13
-#define HPI_OBJ_ASYNCEVENT 14
-
-#define HPI_OBJ_MAXINDEX 14
-
-/******************************************* methods/functions */
-
-#define HPI_OBJ_FUNCTION_SPACING 0x100
-#define HPI_MAKE_INDEX(obj, index) (obj * HPI_OBJ_FUNCTION_SPACING + index)
+enum HPI_MESSAGE_TYPES {
+ HPI_TYPE_REQUEST = 1,
+ HPI_TYPE_RESPONSE = 2,
+ HPI_TYPE_DATA = 3,
+ HPI_TYPE_SSX2BYPASS_MESSAGE = 4,
+ HPI_TYPE_COMMAND = 5,
+ HPI_TYPE_NOTIFICATION = 6
+};
+
+enum HPI_OBJECT_TYPES {
+ HPI_OBJ_SUBSYSTEM = 1,
+ HPI_OBJ_ADAPTER = 2,
+ HPI_OBJ_OSTREAM = 3,
+ HPI_OBJ_ISTREAM = 4,
+ HPI_OBJ_MIXER = 5,
+ HPI_OBJ_NODE = 6,
+ HPI_OBJ_CONTROL = 7,
+ HPI_OBJ_NVMEMORY = 8,
+ HPI_OBJ_GPIO = 9,
+ HPI_OBJ_WATCHDOG = 10,
+ HPI_OBJ_CLOCK = 11,
+ HPI_OBJ_PROFILE = 12,
+ /* HPI_ OBJ_ CONTROLEX = 13, */
+ HPI_OBJ_ASYNCEVENT = 14
+#define HPI_OBJ_MAXINDEX 14
+};
+
+#define HPI_OBJ_FUNCTION_SPACING 0x100
+#define HPI_FUNC_ID(obj, i) (HPI_OBJ_##obj * HPI_OBJ_FUNCTION_SPACING + i)
+
#define HPI_EXTRACT_INDEX(fn) (fn & 0xff)
-/* SUB-SYSTEM */
-#define HPI_SUBSYS_OPEN HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 1)
-#define HPI_SUBSYS_GET_VERSION HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 2)
-#define HPI_SUBSYS_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 3)
-#define HPI_SUBSYS_FIND_ADAPTERS HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 4)
-#define HPI_SUBSYS_CREATE_ADAPTER HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 5)
-#define HPI_SUBSYS_CLOSE HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 6)
-#define HPI_SUBSYS_DELETE_ADAPTER HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 7)
-#define HPI_SUBSYS_DRIVER_LOAD HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 8)
-#define HPI_SUBSYS_DRIVER_UNLOAD HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 9)
-#define HPI_SUBSYS_READ_PORT_8 HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 10)
-#define HPI_SUBSYS_WRITE_PORT_8 HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 11)
-#define HPI_SUBSYS_GET_NUM_ADAPTERS HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 12)
-#define HPI_SUBSYS_GET_ADAPTER HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 13)
-#define HPI_SUBSYS_SET_NETWORK_INTERFACE HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 14)
-#define HPI_SUBSYS_FUNCTION_COUNT 14
-/* ADAPTER */
-#define HPI_ADAPTER_OPEN HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 1)
-#define HPI_ADAPTER_CLOSE HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 2)
-#define HPI_ADAPTER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 3)
-#define HPI_ADAPTER_GET_ASSERT HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 4)
-#define HPI_ADAPTER_TEST_ASSERT HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 5)
-#define HPI_ADAPTER_SET_MODE HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 6)
-#define HPI_ADAPTER_GET_MODE HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 7)
-#define HPI_ADAPTER_ENABLE_CAPABILITY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 8)
-#define HPI_ADAPTER_SELFTEST HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 9)
-#define HPI_ADAPTER_FIND_OBJECT HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 10)
-#define HPI_ADAPTER_QUERY_FLASH HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 11)
-#define HPI_ADAPTER_START_FLASH HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 12)
-#define HPI_ADAPTER_PROGRAM_FLASH HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 13)
-#define HPI_ADAPTER_SET_PROPERTY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 14)
-#define HPI_ADAPTER_GET_PROPERTY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 15)
-#define HPI_ADAPTER_ENUM_PROPERTY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 16)
-#define HPI_ADAPTER_MODULE_INFO HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 17)
-#define HPI_ADAPTER_DEBUG_READ HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 18)
-#define HPI_ADAPTER_FUNCTION_COUNT 18
-/* OUTPUT STREAM */
-#define HPI_OSTREAM_OPEN HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 1)
-#define HPI_OSTREAM_CLOSE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 2)
-#define HPI_OSTREAM_WRITE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 3)
-#define HPI_OSTREAM_START HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 4)
-#define HPI_OSTREAM_STOP HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 5)
-#define HPI_OSTREAM_RESET HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 6)
-#define HPI_OSTREAM_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 7)
-#define HPI_OSTREAM_QUERY_FORMAT HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 8)
-#define HPI_OSTREAM_DATA HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 9)
-#define HPI_OSTREAM_SET_VELOCITY HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 10)
-#define HPI_OSTREAM_SET_PUNCHINOUT HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 11)
-#define HPI_OSTREAM_SINEGEN HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 12)
-#define HPI_OSTREAM_ANC_RESET HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 13)
-#define HPI_OSTREAM_ANC_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 14)
-#define HPI_OSTREAM_ANC_READ HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 15)
-#define HPI_OSTREAM_SET_TIMESCALE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 16)
-#define HPI_OSTREAM_SET_FORMAT HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 17)
-#define HPI_OSTREAM_HOSTBUFFER_ALLOC HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 18)
-#define HPI_OSTREAM_HOSTBUFFER_FREE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 19)
-#define HPI_OSTREAM_GROUP_ADD HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 20)
-#define HPI_OSTREAM_GROUP_GETMAP HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 21)
-#define HPI_OSTREAM_GROUP_RESET HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 22)
-#define HPI_OSTREAM_HOSTBUFFER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 23)
-#define HPI_OSTREAM_WAIT_START HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 24)
-#define HPI_OSTREAM_FUNCTION_COUNT 24
-/* INPUT STREAM */
-#define HPI_ISTREAM_OPEN HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 1)
-#define HPI_ISTREAM_CLOSE HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 2)
-#define HPI_ISTREAM_SET_FORMAT HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 3)
-#define HPI_ISTREAM_READ HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 4)
-#define HPI_ISTREAM_START HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 5)
-#define HPI_ISTREAM_STOP HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 6)
-#define HPI_ISTREAM_RESET HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 7)
-#define HPI_ISTREAM_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 8)
-#define HPI_ISTREAM_QUERY_FORMAT HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 9)
-#define HPI_ISTREAM_ANC_RESET HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 10)
-#define HPI_ISTREAM_ANC_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 11)
-#define HPI_ISTREAM_ANC_WRITE HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 12)
-#define HPI_ISTREAM_HOSTBUFFER_ALLOC HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 13)
-#define HPI_ISTREAM_HOSTBUFFER_FREE HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 14)
-#define HPI_ISTREAM_GROUP_ADD HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 15)
-#define HPI_ISTREAM_GROUP_GETMAP HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 16)
-#define HPI_ISTREAM_GROUP_RESET HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 17)
-#define HPI_ISTREAM_HOSTBUFFER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 18)
-#define HPI_ISTREAM_WAIT_START HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 19)
-#define HPI_ISTREAM_FUNCTION_COUNT 19
-/* MIXER */
+enum HPI_FUNCTION_IDS {
+ HPI_SUBSYS_OPEN = HPI_FUNC_ID(SUBSYSTEM, 1),
+ HPI_SUBSYS_GET_VERSION = HPI_FUNC_ID(SUBSYSTEM, 2),
+ HPI_SUBSYS_GET_INFO = HPI_FUNC_ID(SUBSYSTEM, 3),
+ HPI_SUBSYS_CREATE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 5),
+ HPI_SUBSYS_CLOSE = HPI_FUNC_ID(SUBSYSTEM, 6),
+ HPI_SUBSYS_DRIVER_LOAD = HPI_FUNC_ID(SUBSYSTEM, 8),
+ HPI_SUBSYS_DRIVER_UNLOAD = HPI_FUNC_ID(SUBSYSTEM, 9),
+ HPI_SUBSYS_GET_NUM_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 12),
+ HPI_SUBSYS_GET_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 13),
+ HPI_SUBSYS_SET_NETWORK_INTERFACE = HPI_FUNC_ID(SUBSYSTEM, 14),
+ HPI_SUBSYS_OPTION_INFO = HPI_FUNC_ID(SUBSYSTEM, 15),
+ HPI_SUBSYS_OPTION_GET = HPI_FUNC_ID(SUBSYSTEM, 16),
+ HPI_SUBSYS_OPTION_SET = HPI_FUNC_ID(SUBSYSTEM, 17),
+#define HPI_SUBSYS_FUNCTION_COUNT 17
+
+ HPI_ADAPTER_OPEN = HPI_FUNC_ID(ADAPTER, 1),
+ HPI_ADAPTER_CLOSE = HPI_FUNC_ID(ADAPTER, 2),
+ HPI_ADAPTER_GET_INFO = HPI_FUNC_ID(ADAPTER, 3),
+ HPI_ADAPTER_GET_ASSERT = HPI_FUNC_ID(ADAPTER, 4),
+ HPI_ADAPTER_TEST_ASSERT = HPI_FUNC_ID(ADAPTER, 5),
+ HPI_ADAPTER_SET_MODE = HPI_FUNC_ID(ADAPTER, 6),
+ HPI_ADAPTER_GET_MODE = HPI_FUNC_ID(ADAPTER, 7),
+ HPI_ADAPTER_ENABLE_CAPABILITY = HPI_FUNC_ID(ADAPTER, 8),
+ HPI_ADAPTER_SELFTEST = HPI_FUNC_ID(ADAPTER, 9),
+ HPI_ADAPTER_FIND_OBJECT = HPI_FUNC_ID(ADAPTER, 10),
+ HPI_ADAPTER_QUERY_FLASH = HPI_FUNC_ID(ADAPTER, 11),
+ HPI_ADAPTER_START_FLASH = HPI_FUNC_ID(ADAPTER, 12),
+ HPI_ADAPTER_PROGRAM_FLASH = HPI_FUNC_ID(ADAPTER, 13),
+ HPI_ADAPTER_SET_PROPERTY = HPI_FUNC_ID(ADAPTER, 14),
+ HPI_ADAPTER_GET_PROPERTY = HPI_FUNC_ID(ADAPTER, 15),
+ HPI_ADAPTER_ENUM_PROPERTY = HPI_FUNC_ID(ADAPTER, 16),
+ HPI_ADAPTER_MODULE_INFO = HPI_FUNC_ID(ADAPTER, 17),
+ HPI_ADAPTER_DEBUG_READ = HPI_FUNC_ID(ADAPTER, 18),
+ HPI_ADAPTER_IRQ_QUERY_AND_CLEAR = HPI_FUNC_ID(ADAPTER, 19),
+ HPI_ADAPTER_IRQ_CALLBACK = HPI_FUNC_ID(ADAPTER, 20),
+ HPI_ADAPTER_DELETE = HPI_FUNC_ID(ADAPTER, 21),
+ HPI_ADAPTER_READ_FLASH = HPI_FUNC_ID(ADAPTER, 22),
+ HPI_ADAPTER_END_FLASH = HPI_FUNC_ID(ADAPTER, 23),
+ HPI_ADAPTER_FILESTORE_DELETE_ALL = HPI_FUNC_ID(ADAPTER, 24),
+#define HPI_ADAPTER_FUNCTION_COUNT 24
+
+ HPI_OSTREAM_OPEN = HPI_FUNC_ID(OSTREAM, 1),
+ HPI_OSTREAM_CLOSE = HPI_FUNC_ID(OSTREAM, 2),
+ HPI_OSTREAM_WRITE = HPI_FUNC_ID(OSTREAM, 3),
+ HPI_OSTREAM_START = HPI_FUNC_ID(OSTREAM, 4),
+ HPI_OSTREAM_STOP = HPI_FUNC_ID(OSTREAM, 5),
+ HPI_OSTREAM_RESET = HPI_FUNC_ID(OSTREAM, 6),
+ HPI_OSTREAM_GET_INFO = HPI_FUNC_ID(OSTREAM, 7),
+ HPI_OSTREAM_QUERY_FORMAT = HPI_FUNC_ID(OSTREAM, 8),
+ HPI_OSTREAM_DATA = HPI_FUNC_ID(OSTREAM, 9),
+ HPI_OSTREAM_SET_VELOCITY = HPI_FUNC_ID(OSTREAM, 10),
+ HPI_OSTREAM_SET_PUNCHINOUT = HPI_FUNC_ID(OSTREAM, 11),
+ HPI_OSTREAM_SINEGEN = HPI_FUNC_ID(OSTREAM, 12),
+ HPI_OSTREAM_ANC_RESET = HPI_FUNC_ID(OSTREAM, 13),
+ HPI_OSTREAM_ANC_GET_INFO = HPI_FUNC_ID(OSTREAM, 14),
+ HPI_OSTREAM_ANC_READ = HPI_FUNC_ID(OSTREAM, 15),
+ HPI_OSTREAM_SET_TIMESCALE = HPI_FUNC_ID(OSTREAM, 16),
+ HPI_OSTREAM_SET_FORMAT = HPI_FUNC_ID(OSTREAM, 17),
+ HPI_OSTREAM_HOSTBUFFER_ALLOC = HPI_FUNC_ID(OSTREAM, 18),
+ HPI_OSTREAM_HOSTBUFFER_FREE = HPI_FUNC_ID(OSTREAM, 19),
+ HPI_OSTREAM_GROUP_ADD = HPI_FUNC_ID(OSTREAM, 20),
+ HPI_OSTREAM_GROUP_GETMAP = HPI_FUNC_ID(OSTREAM, 21),
+ HPI_OSTREAM_GROUP_RESET = HPI_FUNC_ID(OSTREAM, 22),
+ HPI_OSTREAM_HOSTBUFFER_GET_INFO = HPI_FUNC_ID(OSTREAM, 23),
+ HPI_OSTREAM_WAIT_START = HPI_FUNC_ID(OSTREAM, 24),
+ HPI_OSTREAM_WAIT = HPI_FUNC_ID(OSTREAM, 25),
+#define HPI_OSTREAM_FUNCTION_COUNT 25
+
+ HPI_ISTREAM_OPEN = HPI_FUNC_ID(ISTREAM, 1),
+ HPI_ISTREAM_CLOSE = HPI_FUNC_ID(ISTREAM, 2),
+ HPI_ISTREAM_SET_FORMAT = HPI_FUNC_ID(ISTREAM, 3),
+ HPI_ISTREAM_READ = HPI_FUNC_ID(ISTREAM, 4),
+ HPI_ISTREAM_START = HPI_FUNC_ID(ISTREAM, 5),
+ HPI_ISTREAM_STOP = HPI_FUNC_ID(ISTREAM, 6),
+ HPI_ISTREAM_RESET = HPI_FUNC_ID(ISTREAM, 7),
+ HPI_ISTREAM_GET_INFO = HPI_FUNC_ID(ISTREAM, 8),
+ HPI_ISTREAM_QUERY_FORMAT = HPI_FUNC_ID(ISTREAM, 9),
+ HPI_ISTREAM_ANC_RESET = HPI_FUNC_ID(ISTREAM, 10),
+ HPI_ISTREAM_ANC_GET_INFO = HPI_FUNC_ID(ISTREAM, 11),
+ HPI_ISTREAM_ANC_WRITE = HPI_FUNC_ID(ISTREAM, 12),
+ HPI_ISTREAM_HOSTBUFFER_ALLOC = HPI_FUNC_ID(ISTREAM, 13),
+ HPI_ISTREAM_HOSTBUFFER_FREE = HPI_FUNC_ID(ISTREAM, 14),
+ HPI_ISTREAM_GROUP_ADD = HPI_FUNC_ID(ISTREAM, 15),
+ HPI_ISTREAM_GROUP_GETMAP = HPI_FUNC_ID(ISTREAM, 16),
+ HPI_ISTREAM_GROUP_RESET = HPI_FUNC_ID(ISTREAM, 17),
+ HPI_ISTREAM_HOSTBUFFER_GET_INFO = HPI_FUNC_ID(ISTREAM, 18),
+ HPI_ISTREAM_WAIT_START = HPI_FUNC_ID(ISTREAM, 19),
+ HPI_ISTREAM_WAIT = HPI_FUNC_ID(ISTREAM, 20),
+#define HPI_ISTREAM_FUNCTION_COUNT 20
+
/* NOTE:
GET_NODE_INFO, SET_CONNECTION, GET_CONNECTIONS are not currently used */
-#define HPI_MIXER_OPEN HPI_MAKE_INDEX(HPI_OBJ_MIXER, 1)
-#define HPI_MIXER_CLOSE HPI_MAKE_INDEX(HPI_OBJ_MIXER, 2)
-#define HPI_MIXER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_MIXER, 3)
-#define HPI_MIXER_GET_NODE_INFO HPI_MAKE_INDEX(HPI_OBJ_MIXER, 4)
-#define HPI_MIXER_GET_CONTROL HPI_MAKE_INDEX(HPI_OBJ_MIXER, 5)
-#define HPI_MIXER_SET_CONNECTION HPI_MAKE_INDEX(HPI_OBJ_MIXER, 6)
-#define HPI_MIXER_GET_CONNECTIONS HPI_MAKE_INDEX(HPI_OBJ_MIXER, 7)
-#define HPI_MIXER_GET_CONTROL_BY_INDEX HPI_MAKE_INDEX(HPI_OBJ_MIXER, 8)
-#define HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX HPI_MAKE_INDEX(HPI_OBJ_MIXER, 9)
-#define HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES HPI_MAKE_INDEX(HPI_OBJ_MIXER, 10)
-#define HPI_MIXER_STORE HPI_MAKE_INDEX(HPI_OBJ_MIXER, 11)
-#define HPI_MIXER_FUNCTION_COUNT 11
-/* MIXER CONTROLS */
-#define HPI_CONTROL_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_CONTROL, 1)
-#define HPI_CONTROL_GET_STATE HPI_MAKE_INDEX(HPI_OBJ_CONTROL, 2)
-#define HPI_CONTROL_SET_STATE HPI_MAKE_INDEX(HPI_OBJ_CONTROL, 3)
+ HPI_MIXER_OPEN = HPI_FUNC_ID(MIXER, 1),
+ HPI_MIXER_CLOSE = HPI_FUNC_ID(MIXER, 2),
+ HPI_MIXER_GET_INFO = HPI_FUNC_ID(MIXER, 3),
+ HPI_MIXER_GET_NODE_INFO = HPI_FUNC_ID(MIXER, 4),
+ HPI_MIXER_GET_CONTROL = HPI_FUNC_ID(MIXER, 5),
+ HPI_MIXER_SET_CONNECTION = HPI_FUNC_ID(MIXER, 6),
+ HPI_MIXER_GET_CONNECTIONS = HPI_FUNC_ID(MIXER, 7),
+ HPI_MIXER_GET_CONTROL_BY_INDEX = HPI_FUNC_ID(MIXER, 8),
+ HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX = HPI_FUNC_ID(MIXER, 9),
+ HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES = HPI_FUNC_ID(MIXER, 10),
+ HPI_MIXER_STORE = HPI_FUNC_ID(MIXER, 11),
+ HPI_MIXER_GET_CACHE_INFO = HPI_FUNC_ID(MIXER, 12),
+ HPI_MIXER_GET_BLOCK_HANDLE = HPI_FUNC_ID(MIXER, 13),
+ HPI_MIXER_GET_PARAMETER_HANDLE = HPI_FUNC_ID(MIXER, 14),
+#define HPI_MIXER_FUNCTION_COUNT 14
+
+ HPI_CONTROL_GET_INFO = HPI_FUNC_ID(CONTROL, 1),
+ HPI_CONTROL_GET_STATE = HPI_FUNC_ID(CONTROL, 2),
+ HPI_CONTROL_SET_STATE = HPI_FUNC_ID(CONTROL, 3),
#define HPI_CONTROL_FUNCTION_COUNT 3
-/* NONVOL MEMORY */
-#define HPI_NVMEMORY_OPEN HPI_MAKE_INDEX(HPI_OBJ_NVMEMORY, 1)
-#define HPI_NVMEMORY_READ_BYTE HPI_MAKE_INDEX(HPI_OBJ_NVMEMORY, 2)
-#define HPI_NVMEMORY_WRITE_BYTE HPI_MAKE_INDEX(HPI_OBJ_NVMEMORY, 3)
+
+ HPI_NVMEMORY_OPEN = HPI_FUNC_ID(NVMEMORY, 1),
+ HPI_NVMEMORY_READ_BYTE = HPI_FUNC_ID(NVMEMORY, 2),
+ HPI_NVMEMORY_WRITE_BYTE = HPI_FUNC_ID(NVMEMORY, 3),
#define HPI_NVMEMORY_FUNCTION_COUNT 3
-/* GPIO */
-#define HPI_GPIO_OPEN HPI_MAKE_INDEX(HPI_OBJ_GPIO, 1)
-#define HPI_GPIO_READ_BIT HPI_MAKE_INDEX(HPI_OBJ_GPIO, 2)
-#define HPI_GPIO_WRITE_BIT HPI_MAKE_INDEX(HPI_OBJ_GPIO, 3)
-#define HPI_GPIO_READ_ALL HPI_MAKE_INDEX(HPI_OBJ_GPIO, 4)
-#define HPI_GPIO_WRITE_STATUS HPI_MAKE_INDEX(HPI_OBJ_GPIO, 5)
+
+ HPI_GPIO_OPEN = HPI_FUNC_ID(GPIO, 1),
+ HPI_GPIO_READ_BIT = HPI_FUNC_ID(GPIO, 2),
+ HPI_GPIO_WRITE_BIT = HPI_FUNC_ID(GPIO, 3),
+ HPI_GPIO_READ_ALL = HPI_FUNC_ID(GPIO, 4),
+ HPI_GPIO_WRITE_STATUS = HPI_FUNC_ID(GPIO, 5),
#define HPI_GPIO_FUNCTION_COUNT 5
-/* ASYNC EVENT */
-#define HPI_ASYNCEVENT_OPEN HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 1)
-#define HPI_ASYNCEVENT_CLOSE HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 2)
-#define HPI_ASYNCEVENT_WAIT HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 3)
-#define HPI_ASYNCEVENT_GETCOUNT HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 4)
-#define HPI_ASYNCEVENT_GET HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 5)
-#define HPI_ASYNCEVENT_SENDEVENTS HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 6)
+
+ HPI_ASYNCEVENT_OPEN = HPI_FUNC_ID(ASYNCEVENT, 1),
+ HPI_ASYNCEVENT_CLOSE = HPI_FUNC_ID(ASYNCEVENT, 2),
+ HPI_ASYNCEVENT_WAIT = HPI_FUNC_ID(ASYNCEVENT, 3),
+ HPI_ASYNCEVENT_GETCOUNT = HPI_FUNC_ID(ASYNCEVENT, 4),
+ HPI_ASYNCEVENT_GET = HPI_FUNC_ID(ASYNCEVENT, 5),
+ HPI_ASYNCEVENT_SENDEVENTS = HPI_FUNC_ID(ASYNCEVENT, 6),
#define HPI_ASYNCEVENT_FUNCTION_COUNT 6
-/* WATCH-DOG */
-#define HPI_WATCHDOG_OPEN HPI_MAKE_INDEX(HPI_OBJ_WATCHDOG, 1)
-#define HPI_WATCHDOG_SET_TIME HPI_MAKE_INDEX(HPI_OBJ_WATCHDOG, 2)
-#define HPI_WATCHDOG_PING HPI_MAKE_INDEX(HPI_OBJ_WATCHDOG, 3)
-/* CLOCK */
-#define HPI_CLOCK_OPEN HPI_MAKE_INDEX(HPI_OBJ_CLOCK, 1)
-#define HPI_CLOCK_SET_TIME HPI_MAKE_INDEX(HPI_OBJ_CLOCK, 2)
-#define HPI_CLOCK_GET_TIME HPI_MAKE_INDEX(HPI_OBJ_CLOCK, 3)
-/* PROFILE */
-#define HPI_PROFILE_OPEN_ALL HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 1)
-#define HPI_PROFILE_START_ALL HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 2)
-#define HPI_PROFILE_STOP_ALL HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 3)
-#define HPI_PROFILE_GET HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 4)
-#define HPI_PROFILE_GET_IDLECOUNT HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 5)
-#define HPI_PROFILE_GET_NAME HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 6)
-#define HPI_PROFILE_GET_UTILIZATION HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 7)
+
+ HPI_WATCHDOG_OPEN = HPI_FUNC_ID(WATCHDOG, 1),
+ HPI_WATCHDOG_SET_TIME = HPI_FUNC_ID(WATCHDOG, 2),
+ HPI_WATCHDOG_PING = HPI_FUNC_ID(WATCHDOG, 3),
+
+ HPI_CLOCK_OPEN = HPI_FUNC_ID(CLOCK, 1),
+ HPI_CLOCK_SET_TIME = HPI_FUNC_ID(CLOCK, 2),
+ HPI_CLOCK_GET_TIME = HPI_FUNC_ID(CLOCK, 3),
+
+ HPI_PROFILE_OPEN_ALL = HPI_FUNC_ID(PROFILE, 1),
+ HPI_PROFILE_START_ALL = HPI_FUNC_ID(PROFILE, 2),
+ HPI_PROFILE_STOP_ALL = HPI_FUNC_ID(PROFILE, 3),
+ HPI_PROFILE_GET = HPI_FUNC_ID(PROFILE, 4),
+ HPI_PROFILE_GET_IDLECOUNT = HPI_FUNC_ID(PROFILE, 5),
+ HPI_PROFILE_GET_NAME = HPI_FUNC_ID(PROFILE, 6),
+ HPI_PROFILE_GET_UTILIZATION = HPI_FUNC_ID(PROFILE, 7)
#define HPI_PROFILE_FUNCTION_COUNT 7
-/* ////////////////////////////////////////////////////////////////////// */
-/* PRIVATE ATTRIBUTES */
+};
/* ////////////////////////////////////////////////////////////////////// */
/* STRUCTURES */
@@ -672,42 +540,33 @@ Threshold is a -ve number in units of dB/100,
/** PCI bus resource */
struct hpi_pci {
u32 __iomem *ap_mem_base[HPI_MAX_ADAPTER_MEM_SPACES];
- struct pci_dev *p_os_data;
+ struct pci_dev *pci_dev;
+};
-#ifndef HPI64BIT /* keep structure size constant */
- u32 padding[HPI_MAX_ADAPTER_MEM_SPACES + 1];
-#endif
- u16 vendor_id;
- u16 device_id;
- u16 subsys_vendor_id;
- u16 subsys_device_id;
- u16 bus_number;
- u16 device_number;
- u32 interrupt;
+/** Adapter specification resource */
+struct hpi_adapter_specification {
+ u32 type;
+ u8 modules[4];
};
struct hpi_resource {
union {
const struct hpi_pci *pci;
const char *net_if;
+ struct hpi_adapter_specification adapter_spec;
+ const void *sw_if;
} r;
-#ifndef HPI64BIT /* keep structure size constant */
- u32 pad_to64;
-#endif
u16 bus_type; /* HPI_BUS_PNPISA, _PCI, _USB etc */
u16 padding;
-
};
/** Format info used inside struct hpi_message
Not the same as public API struct hpi_format */
struct hpi_msg_format {
- u32 sample_rate;
- /**< 11025, 32000, 44100 ... */
- u32 bit_rate; /**< for MPEG */
- u32 attributes;
- /**< Stereo/JointStereo/Mono */
- u16 channels; /**< 1,2..., (or ancillary mode or idle bit */
+ u32 sample_rate; /**< 11025, 32000, 44100 etc. */
+ u32 bit_rate; /**< for MPEG */
+ u32 attributes; /**< stereo/joint_stereo/mono */
+ u16 channels; /**< 1,2..., (or ancillary mode or idle bit */
u16 format; /**< HPI_FORMAT_PCM16, _MPEG etc. see \ref HPI_FORMATS. */
};
@@ -716,7 +575,7 @@ struct hpi_msg_format {
struct hpi_msg_data {
struct hpi_msg_format format;
u8 *pb_data;
-#ifndef HPI64BIT
+#ifndef CONFIG_64BIT
u32 padding;
#endif
u32 data_size;
@@ -729,7 +588,7 @@ struct hpi_data_legacy32 {
u32 data_size;
};
-#ifdef HPI64BIT
+#ifdef CONFIG_64BIT
/* Compatibility version of struct hpi_data*/
struct hpi_data_compat32 {
struct hpi_msg_format format;
@@ -740,9 +599,9 @@ struct hpi_data_compat32 {
#endif
struct hpi_buffer {
- /** placehoder for backward compatability (see dwBufferSize) */
+ /** placeholder for backward compatibility (see dwBufferSize) */
struct hpi_msg_format reserved;
- u32 command; /**< HPI_BUFFER_CMD_xxx*/
+ u32 command; /**< HPI_BUFFER_CMD_xxx*/
u32 pci_address; /**< PCI physical address of buffer for DSP DMA */
u32 buffer_size; /**< must line up with data_size of HPI_DATA*/
};
@@ -754,7 +613,7 @@ struct hpi_hostbuffer_status {
u32 auxiliary_data_available;
u32 stream_state;
/* DSP index in to the host bus master buffer. */
- u32 dSP_index;
+ u32 dsp_index;
/* Host index in to the host bus master buffer. */
u32 host_index;
u32 size_in_bytes;
@@ -777,60 +636,54 @@ struct hpi_subsys_msg {
struct hpi_subsys_res {
u32 version;
- u32 data; /* used to return extended version */
- u16 num_adapters; /* number of adapters */
+ u32 data; /* extended version */
+ u16 num_adapters;
u16 adapter_index;
- u16 aw_adapter_list[HPI_MAX_ADAPTERS];
-};
-
-struct hpi_adapter_msg {
- u32 adapter_mode; /* adapter mode */
- u16 assert_id; /* assert number for "test assert" call
- object_index for find object call
- query_or_set for hpi_adapter_set_mode_ex() */
- u16 object_type; /* for adapter find object call */
+ u16 adapter_type;
+ u16 pad16;
};
union hpi_adapterx_msg {
- struct hpi_adapter_msg adapter;
struct {
- u32 offset;
- } query_flash;
+ u32 dsp_address;
+ u32 count_bytes;
+ } debug_read;
struct {
- u32 offset;
- u32 length;
- u32 key;
- } start_flash;
+ u32 adapter_mode;
+ u16 query_or_set;
+ } mode;
struct {
- u32 checksum;
- u16 sequence;
- u16 length;
- u16 offset; /**< offset from start of msg to data */
- u16 unused;
- } program_flash;
+ u16 index;
+ } module_info;
+ struct {
+ u16 index;
+ u16 what;
+ u16 property_index;
+ } property_enum;
struct {
u16 property;
u16 parameter1;
u16 parameter2;
} property_set;
struct {
- u16 index;
- u16 what;
- u16 property_index;
- } property_enum;
+ u32 pad32;
+ u16 key1;
+ u16 key2;
+ } restart;
struct {
- u16 index;
- } module_info;
+ u32 pad32;
+ u16 value;
+ } test_assert;
struct {
- u32 dsp_address;
- u32 count_bytes;
- } debug_read;
+ u32 message;
+ } irq;
+ u32 pad[3];
};
struct hpi_adapter_res {
u32 serial_number;
u16 adapter_type;
- u16 adapter_index; /* is this needed? also used for dsp_index */
+ u16 adapter_index;
u16 num_instreams;
u16 num_outstreams;
u16 num_mixers;
@@ -839,19 +692,25 @@ struct hpi_adapter_res {
};
union hpi_adapterx_res {
- struct hpi_adapter_res adapter;
+ struct hpi_adapter_res info;
struct {
- u32 checksum;
- u32 length;
- u32 version;
- } query_flash;
+ u32 p1;
+ u16 count;
+ u16 dsp_index;
+ u32 p2;
+ u32 dsp_msg_addr;
+ char sz_message[HPI_STRING_LEN];
+ } assert;
struct {
- u16 sequence;
- } program_flash;
+ u32 adapter_mode;
+ } mode;
struct {
u16 parameter1;
u16 parameter2;
} property_get;
+ struct {
+ u32 yes;
+ } irq_query;
};
struct hpi_stream_msg {
@@ -863,6 +722,7 @@ struct hpi_stream_msg {
u32 time_scale;
struct hpi_buffer buffer;
struct hpi_streamid stream;
+ u32 threshold_bytes;
} u;
};
@@ -911,7 +771,7 @@ struct hpi_stream_res {
struct hpi_mixer_msg {
u16 control_index;
u16 control_type; /* = HPI_CONTROL_METER _VOLUME etc */
- u16 padding1; /* maintain alignment of subsequent fields */
+ u16 padding1; /* Maintain alignment of subsequent fields */
u16 node_type1; /* = HPI_SOURCENODE_LINEIN etc */
u16 node_index1; /* = 0..N */
u16 node_type2;
@@ -949,6 +809,11 @@ union hpi_mixerx_res {
u32 p_data; /* pointer to data array */
u16 more_to_do; /* indicates if there is more to do */
} gcabi;
+ struct {
+ u32 total_controls; /* count of controls in the mixer */
+ u32 cache_controls; /* count of controls in the cac */
+ u32 cache_bytes; /* size of cache */
+ } cache_info;
};
struct hpi_control_msg {
@@ -1000,107 +865,29 @@ union hpi_control_union_res {
u32 band;
u32 frequency;
u32 gain;
- u32 level;
u32 deemphasis;
struct {
u32 data[2];
u32 bLER;
} rds;
+ short s_level;
+ struct {
+ u16 value;
+ u16 mask;
+ } status;
} tuner;
struct {
char sz_data[8];
u32 remaining_chars;
} chars8;
char c_data12[12];
-};
-
-/* HPI_CONTROLX_STRUCTURES */
-
-/* Message */
-
-/** Used for all HMI variables where max length <= 8 bytes
-*/
-struct hpi_controlx_msg_cobranet_data {
- u32 hmi_address;
- u32 byte_count;
- u32 data[2];
-};
-
-/** Used for string data, and for packet bridge
-*/
-struct hpi_controlx_msg_cobranet_bigdata {
- u32 hmi_address;
- u32 byte_count;
- u8 *pb_data;
-#ifndef HPI64BIT
- u32 padding;
-#endif
-};
-
-/** Used for PADS control reading of string fields.
-*/
-struct hpi_controlx_msg_pad_data {
- u32 field;
- u32 byte_count;
- u8 *pb_data;
-#ifndef HPI64BIT
- u32 padding;
-#endif
-};
-
-/** Used for generic data
-*/
-
-struct hpi_controlx_msg_generic {
- u32 param1;
- u32 param2;
-};
-
-struct hpi_controlx_msg {
- u16 attribute; /* control attribute or property */
- u16 saved_index;
union {
- struct hpi_controlx_msg_cobranet_data cobranet_data;
- struct hpi_controlx_msg_cobranet_bigdata cobranet_bigdata;
- struct hpi_controlx_msg_generic generic;
- struct hpi_controlx_msg_pad_data pad_data;
- /*struct param_value universal_value; */
- /* nothing extra to send for status read */
- } u;
-};
-
-/* Response */
-/**
-*/
-struct hpi_controlx_res_cobranet_data {
- u32 byte_count;
- u32 data[2];
-};
-
-struct hpi_controlx_res_cobranet_bigdata {
- u32 byte_count;
-};
-
-struct hpi_controlx_res_cobranet_status {
- u32 status;
- u32 readable_size;
- u32 writeable_size;
-};
-
-struct hpi_controlx_res_generic {
- u32 param1;
- u32 param2;
-};
-
-struct hpi_controlx_res {
- union {
- struct hpi_controlx_res_cobranet_bigdata cobranet_bigdata;
- struct hpi_controlx_res_cobranet_data cobranet_data;
- struct hpi_controlx_res_cobranet_status cobranet_status;
- struct hpi_controlx_res_generic generic;
- /*struct param_info universal_info; */
- /*struct param_value universal_value; */
- } u;
+ struct {
+ u32 status;
+ u32 readable_size;
+ u32 writeable_size;
+ } status;
+ } cobranet;
};
struct hpi_nvmemory_msg {
@@ -1178,11 +965,11 @@ struct hpi_profile_res_open {
};
struct hpi_profile_res_time {
- u32 micro_seconds;
+ u32 total_tick_count;
u32 call_count;
- u32 max_micro_seconds;
- u32 min_micro_seconds;
- u16 seconds;
+ u32 max_tick_count;
+ u32 ticks_per_millisecond;
+ u16 profile_interval;
};
struct hpi_profile_res_name {
@@ -1218,7 +1005,6 @@ struct hpi_message {
u16 obj_index; /* */
union {
struct hpi_subsys_msg s;
- struct hpi_adapter_msg a;
union hpi_adapterx_msg ax;
struct hpi_stream_msg d;
struct hpi_mixer_msg m;
@@ -1227,7 +1013,6 @@ struct hpi_message {
/* identical to struct hpi_control_msg,
but field naming is improved */
struct hpi_control_union_msg cu;
- struct hpi_controlx_msg cx; /* extended mixer control; */
struct hpi_nvmemory_msg n;
struct hpi_gpio_msg l; /* digital i/o */
struct hpi_watchdog_msg w;
@@ -1239,7 +1024,7 @@ struct hpi_message {
};
#define HPI_MESSAGE_SIZE_BY_OBJECT { \
- sizeof(struct hpi_message_header) , /* default, no object type 0 */ \
+ sizeof(struct hpi_message_header) , /* Default, no object type 0 */ \
sizeof(struct hpi_message_header) + sizeof(struct hpi_subsys_msg),\
sizeof(struct hpi_message_header) + sizeof(union hpi_adapterx_msg),\
sizeof(struct hpi_message_header) + sizeof(struct hpi_stream_msg),\
@@ -1252,10 +1037,15 @@ struct hpi_message {
sizeof(struct hpi_message_header) + sizeof(struct hpi_watchdog_msg),\
sizeof(struct hpi_message_header) + sizeof(struct hpi_clock_msg),\
sizeof(struct hpi_message_header) + sizeof(struct hpi_profile_msg),\
- sizeof(struct hpi_message_header) + sizeof(struct hpi_controlx_msg),\
+ sizeof(struct hpi_message_header), /* controlx obj removed */ \
sizeof(struct hpi_message_header) + sizeof(struct hpi_async_msg) \
}
+/*
+Note that the wSpecificError error field should be inspected and potentially
+reported whenever HPI_ERROR_DSP_COMMUNICATION or HPI_ERROR_DSP_BOOTLOAD is
+returned in wError.
+*/
struct hpi_response_header {
u16 size;
u8 type; /* HPI_TYPE_RESPONSE */
@@ -1277,7 +1067,6 @@ struct hpi_response {
u16 specific_error; /* adapter specific error */
union {
struct hpi_subsys_res s;
- struct hpi_adapter_res a;
union hpi_adapterx_res ax;
struct hpi_stream_res d;
struct hpi_mixer_res m;
@@ -1285,7 +1074,6 @@ struct hpi_response {
struct hpi_control_res c; /* mixer control; */
/* identical to hpi_control_res, but field naming is improved */
union hpi_control_union_res cu;
- struct hpi_controlx_res cx; /* extended mixer control; */
struct hpi_nvmemory_res n;
struct hpi_gpio_res l; /* digital i/o */
struct hpi_watchdog_res w;
@@ -1297,7 +1085,7 @@ struct hpi_response {
};
#define HPI_RESPONSE_SIZE_BY_OBJECT { \
- sizeof(struct hpi_response_header) ,/* default, no object type 0 */ \
+ sizeof(struct hpi_response_header) ,/* Default, no object type 0 */ \
sizeof(struct hpi_response_header) + sizeof(struct hpi_subsys_res),\
sizeof(struct hpi_response_header) + sizeof(union hpi_adapterx_res),\
sizeof(struct hpi_response_header) + sizeof(struct hpi_stream_res),\
@@ -1310,11 +1098,11 @@ struct hpi_response {
sizeof(struct hpi_response_header) + sizeof(struct hpi_watchdog_res),\
sizeof(struct hpi_response_header) + sizeof(struct hpi_clock_res),\
sizeof(struct hpi_response_header) + sizeof(struct hpi_profile_res),\
- sizeof(struct hpi_response_header) + sizeof(struct hpi_controlx_res),\
+ sizeof(struct hpi_response_header), /* controlx obj removed */ \
sizeof(struct hpi_response_header) + sizeof(struct hpi_async_res) \
}
-/*********************** version 1 message/response *****************************/
+/*********************** version 1 message/response **************************/
#define HPINET_ETHERNET_DATA_SIZE (1500)
#define HPINET_IP_HDR_SIZE (20)
#define HPINET_IP_DATA_SIZE (HPINET_ETHERNET_DATA_SIZE - HPINET_IP_HDR_SIZE)
@@ -1335,63 +1123,33 @@ struct hpi_res_adapter_get_info {
struct hpi_adapter_res p;
};
-/* padding is so these are same size as v0 hpi_message */
-struct hpi_msg_adapter_query_flash {
- struct hpi_message_header h;
- u32 offset;
- u8 pad_to_version0_size[sizeof(struct hpi_message) - /* V0 res */
- sizeof(struct hpi_message_header) - 1 * sizeof(u32)];
-};
-
-/* padding is so these are same size as v0 hpi_response */
-struct hpi_res_adapter_query_flash {
+struct hpi_res_adapter_debug_read {
struct hpi_response_header h;
- u32 checksum;
- u32 length;
- u32 version;
- u8 pad_to_version0_size[sizeof(struct hpi_response) - /* V0 res */
- sizeof(struct hpi_response_header) - 3 * sizeof(u32)];
+ u8 bytes[1024];
};
-struct hpi_msg_adapter_start_flash {
- struct hpi_message_header h;
- u32 offset;
- u32 length;
- u32 key;
- u8 pad_to_version0_size[sizeof(struct hpi_message) - /* V0 res */
- sizeof(struct hpi_message_header) - 3 * sizeof(u32)];
-};
-
-struct hpi_res_adapter_start_flash {
- struct hpi_response_header h;
- u8 pad_to_version0_size[sizeof(struct hpi_response) - /* V0 res */
- sizeof(struct hpi_response_header)];
+struct hpi_msg_cobranet_hmi {
+ u16 attribute;
+ u16 padding;
+ u32 hmi_address;
+ u32 byte_count;
};
-struct hpi_msg_adapter_program_flash_payload {
- u32 checksum;
- u16 sequence;
- u16 length;
- u16 offset; /**< offset from start of msg to data */
- u16 unused;
- /* ensure sizeof(header + payload) == sizeof(hpi_message_V0)
- because old firmware expects data after message of this size */
- u8 pad_to_version0_size[sizeof(struct hpi_message) - /* V0 message */
- sizeof(struct hpi_message_header) - sizeof(u32) -
- 4 * sizeof(u16)];
+struct hpi_msg_cobranet_hmiwrite {
+ struct hpi_message_header h;
+ struct hpi_msg_cobranet_hmi p;
+ u8 bytes[256];
};
-struct hpi_msg_adapter_program_flash {
+struct hpi_msg_cobranet_hmiread {
struct hpi_message_header h;
- struct hpi_msg_adapter_program_flash_payload p;
- u32 data[256];
+ struct hpi_msg_cobranet_hmi p;
};
-struct hpi_res_adapter_program_flash {
+struct hpi_res_cobranet_hmiread {
struct hpi_response_header h;
- u16 sequence;
- u8 pad_to_version0_size[sizeof(struct hpi_response) - /* V0 res */
- sizeof(struct hpi_response_header) - sizeof(u16)];
+ u32 byte_count;
+ u8 bytes[256];
};
#if 1
@@ -1414,30 +1172,16 @@ struct hpi_response_header_v1 {
};
#endif
-/* STRV HPI Packet */
-struct hpi_msg_strv {
- struct hpi_message_header h;
- struct hpi_entity strv;
-};
-
-struct hpi_res_strv {
- struct hpi_response_header h;
- struct hpi_entity strv;
-};
-#define MIN_STRV_PACKET_SIZE sizeof(struct hpi_res_strv)
-
struct hpi_msg_payload_v0 {
struct hpi_message_header h;
union {
struct hpi_subsys_msg s;
- struct hpi_adapter_msg a;
union hpi_adapterx_msg ax;
struct hpi_stream_msg d;
struct hpi_mixer_msg m;
union hpi_mixerx_msg mx;
struct hpi_control_msg c;
struct hpi_control_union_msg cu;
- struct hpi_controlx_msg cx;
struct hpi_nvmemory_msg n;
struct hpi_gpio_msg l;
struct hpi_watchdog_msg w;
@@ -1451,14 +1195,12 @@ struct hpi_res_payload_v0 {
struct hpi_response_header h;
union {
struct hpi_subsys_res s;
- struct hpi_adapter_res a;
union hpi_adapterx_res ax;
struct hpi_stream_res d;
struct hpi_mixer_res m;
union hpi_mixerx_res mx;
struct hpi_control_res c;
union hpi_control_union_res cu;
- struct hpi_controlx_res cx;
struct hpi_nvmemory_res n;
struct hpi_gpio_res l;
struct hpi_watchdog_res w;
@@ -1471,13 +1213,13 @@ struct hpi_res_payload_v0 {
union hpi_message_buffer_v1 {
struct hpi_message m0; /* version 0 */
struct hpi_message_header_v1 h;
- unsigned char buf[HPI_MAX_PAYLOAD_SIZE];
+ u8 buf[HPI_MAX_PAYLOAD_SIZE];
};
union hpi_response_buffer_v1 {
struct hpi_response r0; /* version 0 */
struct hpi_response_header_v1 h;
- unsigned char buf[HPI_MAX_PAYLOAD_SIZE];
+ u8 buf[HPI_MAX_PAYLOAD_SIZE];
};
compile_time_assert((sizeof(union hpi_message_buffer_v1) <=
@@ -1499,6 +1241,11 @@ struct hpi_control_defn {
/*////////////////////////////////////////////////////////////////////////// */
/* declarations for control caching (internal to HPI<->DSP interaction) */
+/** indicates a cached u16 value is invalid. */
+#define HPI_CACHE_INVALID_UINT16 0xFFFF
+/** indicates a cached short value is invalid. */
+#define HPI_CACHE_INVALID_SHORT -32768
+
/** A compact representation of (part of) a controls state.
Used for efficient transfer of the control state
between DSP and host or across a network
@@ -1512,67 +1259,106 @@ struct hpi_control_cache_info {
u16 control_index;
};
-struct hpi_control_cache_single {
+struct hpi_control_cache_vol {
+ struct hpi_control_cache_info i;
+ short an_log[2];
+ unsigned short flags;
+ char padding[2];
+};
+
+struct hpi_control_cache_meter {
+ struct hpi_control_cache_info i;
+ short an_log_peak[2];
+ short an_logRMS[2];
+};
+
+struct hpi_control_cache_channelmode {
+ struct hpi_control_cache_info i;
+ u16 mode;
+ char temp_padding[6];
+};
+
+struct hpi_control_cache_mux {
+ struct hpi_control_cache_info i;
+ u16 source_node_type;
+ u16 source_node_index;
+ char temp_padding[4];
+};
+
+struct hpi_control_cache_level {
+ struct hpi_control_cache_info i;
+ short an_log[2];
+ char temp_padding[4];
+};
+
+struct hpi_control_cache_tuner {
+ struct hpi_control_cache_info i;
+ u32 freq_ink_hz;
+ u16 band;
+ short s_level_avg;
+};
+
+struct hpi_control_cache_aes3rx {
struct hpi_control_cache_info i;
+ u32 error_status;
+ u32 format;
+};
+
+struct hpi_control_cache_aes3tx {
+ struct hpi_control_cache_info i;
+ u32 format;
+ char temp_padding[4];
+};
+
+struct hpi_control_cache_tonedetector {
+ struct hpi_control_cache_info i;
+ u16 state;
+ char temp_padding[6];
+};
+
+struct hpi_control_cache_silencedetector {
+ struct hpi_control_cache_info i;
+ u32 state;
+ char temp_padding[4];
+};
+
+struct hpi_control_cache_sampleclock {
+ struct hpi_control_cache_info i;
+ u16 source;
+ u16 source_index;
+ u32 sample_rate;
+};
+
+struct hpi_control_cache_microphone {
+ struct hpi_control_cache_info i;
+ u16 phantom_state;
+ char temp_padding[6];
+};
+
+struct hpi_control_cache_single {
union {
- struct { /* volume */
- short an_log[2];
- } v;
- struct { /* peak meter */
- short an_log_peak[2];
- short an_logRMS[2];
- } p;
- struct { /* channel mode */
- u16 mode;
- } m;
- struct { /* multiplexer */
- u16 source_node_type;
- u16 source_node_index;
- } x;
- struct { /* level/trim */
- short an_log[2];
- } l;
- struct { /* tuner - partial caching.
- some attributes go to the DSP. */
- u32 freq_ink_hz;
- u16 band;
- u16 level;
- } t;
- struct { /* AESEBU rx status */
- u32 error_status;
- u32 source;
- } aes3rx;
- struct { /* AESEBU tx */
- u32 format;
- } aes3tx;
- struct { /* tone detector */
- u16 state;
- } tone;
- struct { /* silence detector */
- u32 state;
- u32 count;
- } silence;
- struct { /* sample clock */
- u16 source;
- u16 source_index;
- u32 sample_rate;
- } clk;
- struct { /* microphone control */
- u16 state;
- } phantom_power;
- struct { /* generic control */
- u32 dw1;
- u32 dw2;
- } g;
+ struct hpi_control_cache_info i;
+ struct hpi_control_cache_vol vol;
+ struct hpi_control_cache_meter meter;
+ struct hpi_control_cache_channelmode mode;
+ struct hpi_control_cache_mux mux;
+ struct hpi_control_cache_level level;
+ struct hpi_control_cache_tuner tuner;
+ struct hpi_control_cache_aes3rx aes3rx;
+ struct hpi_control_cache_aes3tx aes3tx;
+ struct hpi_control_cache_tonedetector tone;
+ struct hpi_control_cache_silencedetector silence;
+ struct hpi_control_cache_sampleclock clk;
+ struct hpi_control_cache_microphone microphone;
} u;
};
struct hpi_control_cache_pad {
struct hpi_control_cache_info i;
u32 field_valid_flags;
- u8 c_channel[8];
- u8 c_artist[40];
- u8 c_title[40];
+ u8 c_channel[40];
+ u8 c_artist[100];
+ u8 c_title[100];
u8 c_comment[200];
u32 pTY;
u32 pI;
@@ -1580,11 +1366,10 @@ struct hpi_control_cache_pad {
u32 traffic_anouncement;
};
-/*/////////////////////////////////////////////////////////////////////////// */
-/* declarations for 2^N sized FIFO buffer (internal to HPI<->DSP interaction) */
+/* 2^N sized FIFO buffer (internal to HPI<->DSP interaction) */
struct hpi_fifo_buffer {
u32 size;
- u32 dSP_index;
+ u32 dsp_index;
u32 host_index;
};
@@ -1606,29 +1391,15 @@ u32 hpi_indexes_to_handle(const char c_object, const u16 adapter_index,
/*////////////////////////////////////////////////////////////////////////// */
/* main HPI entry point */
-hpi_handler_func hpi_send_recv;
-
-/* UDP message */
-void hpi_send_recvUDP(struct hpi_message *phm, struct hpi_response *phr,
- const unsigned int timeout);
+void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr);
/* used in PnP OS/driver */
-u16 hpi_subsys_create_adapter(const struct hpi_hsubsys *ph_subsys,
- const struct hpi_resource *p_resource, u16 *pw_adapter_index);
-
-u16 hpi_subsys_delete_adapter(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index);
-
-u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u8 **pp_buffer,
+u16 hpi_outstream_host_buffer_get_info(u32 h_outstream, u8 **pp_buffer,
struct hpi_hostbuffer_status **pp_status);
-u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u8 **pp_buffer,
+u16 hpi_instream_host_buffer_get_info(u32 h_instream, u8 **pp_buffer,
struct hpi_hostbuffer_status **pp_status);
-u16 hpi_adapter_restart(u16 adapter_index);
-
/*
The following 3 functions were last declared in header files for
driver 3.10. HPI_ControlQuery() used to be the recommended way
@@ -1642,9 +1413,7 @@ void hpi_stream_response_to_legacy(struct hpi_stream_res *pSR);
/*////////////////////////////////////////////////////////////////////////// */
/* declarations for individual HPI entry points */
-hpi_handler_func HPI_1000;
hpi_handler_func HPI_6000;
hpi_handler_func HPI_6205;
-hpi_handler_func HPI_COMMON;
#endif /* _HPI_INTERNAL_H_ */
diff --git a/sound/pci/asihpi/hpi_version.h b/sound/pci/asihpi/hpi_version.h
new file mode 100644
index 000000000000..016bc55457e3
--- /dev/null
+++ b/sound/pci/asihpi/hpi_version.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/** HPI Version Definitions
+Development releases have odd minor version.
+Production releases have even minor version.
+
+\file hpi_version.h
+*/
+
+#ifndef _HPI_VERSION_H
+#define _HPI_VERSION_H
+
+/* Use single digits for versions less that 10 to avoid octal. */
+/* *** HPI_VER is the only edit required to update version *** */
+/** HPI version */
+#define HPI_VER HPI_VERSION_CONSTRUCTOR(4, 14, 3)
+
+/** HPI version string in dotted decimal format */
+#define HPI_VER_STRING "4.14.03"
+
+/** Library version as documented in hpi-api-versions.txt */
+#define HPI_LIB_VER HPI_VERSION_CONSTRUCTOR(10, 4, 0)
+
+/** Construct hpi version number from major, minor, release numbers */
+#define HPI_VERSION_CONSTRUCTOR(maj, min, r) ((maj << 16) + (min << 8) + r)
+
+/** Extract major version from hpi version number */
+#define HPI_VER_MAJOR(v) ((int)(v >> 16))
+/** Extract minor version from hpi version number */
+#define HPI_VER_MINOR(v) ((int)((v >> 8) & 0xFF))
+/** Extract release from hpi version number */
+#define HPI_VER_RELEASE(v) ((int)(v & 0xFF))
+
+#endif
diff --git a/sound/pci/asihpi/hpicmn.c b/sound/pci/asihpi/hpicmn.c
index dda4f1c6f658..7d1abaedb46a 100644
--- a/sound/pci/asihpi/hpicmn.c
+++ b/sound/pci/asihpi/hpicmn.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\file hpicmn.c
@@ -26,6 +15,8 @@
#include "hpi_internal.h"
#include "hpidebug.h"
+#include "hpimsginit.h"
+
#include "hpicmn.h"
struct hpi_adapters_list {
@@ -37,26 +28,38 @@ struct hpi_adapters_list {
static struct hpi_adapters_list adapters;
/**
-* Given an HPI Message that was sent out and a response that was received,
-* validate that the response has the correct fields filled in,
-* i.e ObjectType, Function etc
-**/
+ * hpi_validate_response - Given an HPI Message that was sent out and
+ * a response that was received, validate that the response has the
+ * correct fields filled in, i.e ObjectType, Function etc
+ * @phm: message
+ * @phr: response
+ */
u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr)
{
- u16 error = 0;
+ if (phr->type != HPI_TYPE_RESPONSE) {
+ HPI_DEBUG_LOG(ERROR, "header type %d invalid\n", phr->type);
+ return HPI_ERROR_INVALID_RESPONSE;
+ }
- if ((phr->type != HPI_TYPE_RESPONSE)
- || (phr->object != phm->object)
- || (phr->function != phm->function))
- error = HPI_ERROR_INVALID_RESPONSE;
+ if (phr->object != phm->object) {
+ HPI_DEBUG_LOG(ERROR, "header object %d invalid\n",
+ phr->object);
+ return HPI_ERROR_INVALID_RESPONSE;
+ }
+
+ if (phr->function != phm->function) {
+ HPI_DEBUG_LOG(ERROR, "header function %d invalid\n",
+ phr->function);
+ return HPI_ERROR_INVALID_RESPONSE;
+ }
- return error;
+ return 0;
}
u16 hpi_add_adapter(struct hpi_adapter_obj *pao)
{
u16 retval = 0;
- /*HPI_ASSERT(pao->wAdapterType); */
+ /*HPI_ASSERT(pao->type); */
hpios_alistlock_lock(&adapters);
@@ -65,9 +68,19 @@ u16 hpi_add_adapter(struct hpi_adapter_obj *pao)
goto unlock;
}
- if (adapters.adapter[pao->index].adapter_type) {
- {
- retval = HPI_DUPLICATE_ADAPTER_NUMBER;
+ if (adapters.adapter[pao->index].type) {
+ int a;
+ for (a = HPI_MAX_ADAPTERS - 1; a >= 0; a--) {
+ if (!adapters.adapter[a].type) {
+ HPI_DEBUG_LOG(WARNING,
+ "ASI%X duplicate index %d moved to %d\n",
+ pao->type, pao->index, a);
+ pao->index = a;
+ break;
+ }
+ }
+ if (a < 0) {
+ retval = HPI_ERROR_DUPLICATE_ADAPTER_NUMBER;
goto unlock;
}
}
@@ -76,36 +89,42 @@ u16 hpi_add_adapter(struct hpi_adapter_obj *pao)
adapters.gw_num_adapters++;
unlock:
- hpios_alistlock_un_lock(&adapters);
+ hpios_alistlock_unlock(&adapters);
return retval;
}
void hpi_delete_adapter(struct hpi_adapter_obj *pao)
{
- memset(pao, 0, sizeof(struct hpi_adapter_obj));
+ if (!pao->type) {
+ HPI_DEBUG_LOG(ERROR, "removing null adapter?\n");
+ return;
+ }
hpios_alistlock_lock(&adapters);
- adapters.gw_num_adapters--; /* dec the number of adapters */
- hpios_alistlock_un_lock(&adapters);
+ if (adapters.adapter[pao->index].type)
+ adapters.gw_num_adapters--;
+ memset(&adapters.adapter[pao->index], 0, sizeof(adapters.adapter[0]));
+ hpios_alistlock_unlock(&adapters);
}
/**
-* FindAdapter returns a pointer to the struct hpi_adapter_obj with
-* index wAdapterIndex in an HPI_ADAPTERS_LIST structure.
-*
-*/
+ * hpi_find_adapter - FindAdapter returns a pointer to the struct
+ * hpi_adapter_obj with index wAdapterIndex in an HPI_ADAPTERS_LIST
+ * structure.
+ * @adapter_index: value in [0, HPI_MAX_ADAPTERS[
+ */
struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index)
{
struct hpi_adapter_obj *pao = NULL;
if (adapter_index >= HPI_MAX_ADAPTERS) {
- HPI_DEBUG_LOG(VERBOSE, "find_adapter invalid index %d ",
+ HPI_DEBUG_LOG(VERBOSE, "find_adapter invalid index %d\n",
adapter_index);
return NULL;
}
pao = &adapters.adapter[adapter_index];
- if (pao->adapter_type != 0) {
+ if (pao->type != 0) {
/*
HPI_DEBUG_LOG(VERBOSE, "Found adapter index %d\n",
wAdapterIndex);
@@ -121,55 +140,37 @@ struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index)
}
/**
-*
-* wipe an HPI_ADAPTERS_LIST structure.
-*
-**/
-static void wipe_adapter_list(void
- )
+ * wipe_adapter_list - wipe an HPI_ADAPTERS_LIST structure.
+ *
+ */
+static void wipe_adapter_list(void)
{
memset(&adapters, 0, sizeof(adapters));
}
-/**
-* SubSysGetAdapters fills awAdapterList in an struct hpi_response structure
-* with all adapters in the given HPI_ADAPTERS_LIST.
-*
-*/
-static void subsys_get_adapters(struct hpi_response *phr)
+static void subsys_get_adapter(struct hpi_message *phm,
+ struct hpi_response *phr)
{
- /* fill in the response adapter array with the position */
- /* identified by the adapter number/index of the adapters in */
- /* this HPI */
- /* i.e. if we have an A120 with it's jumper set to */
- /* Adapter Number 2 then put an Adapter type A120 in the */
- /* array in position 1 */
- /* NOTE: AdapterNumber is 1..N, Index is 0..N-1 */
-
- /* input: NONE */
- /* output: wNumAdapters */
- /* awAdapter[] */
- /* */
-
- short i;
- struct hpi_adapter_obj *pao = NULL;
-
- HPI_DEBUG_LOG(VERBOSE, "subsys_get_adapters\n");
+ int count = phm->obj_index;
+ u16 index = 0;
- /* for each adapter, place it's type in the position of the array */
- /* corresponding to it's adapter number */
- for (i = 0; i < adapters.gw_num_adapters; i++) {
- pao = &adapters.adapter[i];
- if (phr->u.s.aw_adapter_list[pao->index] != 0) {
- phr->error = HPI_DUPLICATE_ADAPTER_NUMBER;
- phr->specific_error = pao->index;
- return;
+ /* find the nCount'th nonzero adapter in array */
+ for (index = 0; index < HPI_MAX_ADAPTERS; index++) {
+ if (adapters.adapter[index].type) {
+ if (!count)
+ break;
+ count--;
}
- phr->u.s.aw_adapter_list[pao->index] = pao->adapter_type;
}
- phr->u.s.num_adapters = adapters.gw_num_adapters;
- phr->error = 0; /* the function completed OK; */
+ if (index < HPI_MAX_ADAPTERS) {
+ phr->u.s.adapter_index = adapters.adapter[index].index;
+ phr->u.s.adapter_type = adapters.adapter[index].type;
+ } else {
+ phr->u.s.adapter_index = 0;
+ phr->u.s.adapter_type = 0;
+ phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
+ }
}
static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC)
@@ -178,67 +179,107 @@ static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC)
int cached = 0;
if (!pC)
return 0;
- if ((!pC->init) && (pC->p_cache != NULL) && (pC->control_count)
- && (pC->cache_size_in_bytes)
- ) {
- u32 *p_master_cache;
- pC->init = 1;
-
- p_master_cache = (u32 *)pC->p_cache;
- HPI_DEBUG_LOG(VERBOSE, "check %d controls\n",
+
+ if (pC->init)
+ return pC->init;
+
+ if (!pC->p_cache)
+ return 0;
+
+ if (pC->control_count && pC->cache_size_in_bytes) {
+ char *p_master_cache;
+ unsigned int byte_count = 0;
+
+ p_master_cache = (char *)pC->p_cache;
+ HPI_DEBUG_LOG(DEBUG, "check %d controls\n",
pC->control_count);
for (i = 0; i < pC->control_count; i++) {
struct hpi_control_cache_info *info =
(struct hpi_control_cache_info *)
- p_master_cache;
+ &p_master_cache[byte_count];
+ u16 control_index = info->control_index;
+
+ if (control_index >= pC->control_count) {
+ HPI_DEBUG_LOG(INFO,
+ "adap %d control index %d out of range, cache not ready?\n",
+ pC->adap_idx, control_index);
+ return 0;
+ }
+
+ if (!info->size_in32bit_words) {
+ if (!i) {
+ HPI_DEBUG_LOG(INFO,
+ "adap %d cache not ready?\n",
+ pC->adap_idx);
+ return 0;
+ }
+ /* The cache is invalid.
+ * Minimum valid entry size is
+ * sizeof(struct hpi_control_cache_info)
+ */
+ HPI_DEBUG_LOG(ERROR,
+ "adap %d zero size cache entry %d\n",
+ pC->adap_idx, i);
+ break;
+ }
if (info->control_type) {
- pC->p_info[i] = info;
+ pC->p_info[control_index] = info;
cached++;
- } else
- pC->p_info[i] = NULL;
+ } else { /* dummy cache entry */
+ pC->p_info[control_index] = NULL;
+ }
- if (info->size_in32bit_words)
- p_master_cache += info->size_in32bit_words;
- else
- p_master_cache +=
- sizeof(struct
- hpi_control_cache_single) /
- sizeof(u32);
+ byte_count += info->size_in32bit_words * 4;
HPI_DEBUG_LOG(VERBOSE,
- "cached %d, pinfo %p index %d type %d\n",
- cached, pC->p_info[i], info->control_index,
- info->control_type);
+ "cached %d, pinfo %p index %d type %d size %d\n",
+ cached, pC->p_info[info->control_index],
+ info->control_index, info->control_type,
+ info->size_in32bit_words);
+
+ /* quit loop early if whole cache has been scanned.
+ * dwControlCount is the maximum possible entries
+ * but some may be absent from the cache
+ */
+ if (byte_count >= pC->cache_size_in_bytes)
+ break;
+ /* have seen last control index */
+ if (info->control_index == pC->control_count - 1)
+ break;
}
- /*
- We didn't find anything to cache, so try again later !
- */
- if (!cached)
- pC->init = 0;
+
+ if (byte_count != pC->cache_size_in_bytes)
+ HPI_DEBUG_LOG(WARNING,
+ "adap %d bytecount %d != cache size %d\n",
+ pC->adap_idx, byte_count,
+ pC->cache_size_in_bytes);
+ else
+ HPI_DEBUG_LOG(DEBUG,
+ "adap %d cache good, bytecount == cache size = %d\n",
+ pC->adap_idx, byte_count);
+
+ pC->init = (u16)cached;
}
return pC->init;
}
/** Find a control.
*/
-static short find_control(struct hpi_message *phm,
- struct hpi_control_cache *p_cache, struct hpi_control_cache_info **pI,
- u16 *pw_control_index)
+static short find_control(u16 control_index,
+ struct hpi_control_cache *p_cache, struct hpi_control_cache_info **pI)
{
- *pw_control_index = phm->obj_index;
-
if (!control_cache_alloc_check(p_cache)) {
HPI_DEBUG_LOG(VERBOSE,
- "control_cache_alloc_check() failed. adap%d ci%d\n",
- phm->adapter_index, *pw_control_index);
+ "control_cache_alloc_check() failed %d\n",
+ control_index);
return 0;
}
- *pI = p_cache->p_info[*pw_control_index];
+ *pI = p_cache->p_info[control_index];
if (!*pI) {
- HPI_DEBUG_LOG(VERBOSE, "uncached adap %d, control %d\n",
- phm->adapter_index, *pw_control_index);
+ HPI_DEBUG_LOG(VERBOSE, "Uncached Control %d\n",
+ control_index);
return 0;
} else {
HPI_DEBUG_LOG(VERBOSE, "find_control() type %d\n",
@@ -247,25 +288,6 @@ static short find_control(struct hpi_message *phm,
return 1;
}
-/** Used by the kernel driver to figure out if a buffer needs mapping.
- */
-short hpi_check_buffer_mapping(struct hpi_control_cache *p_cache,
- struct hpi_message *phm, void **p, unsigned int *pN)
-{
- *pN = 0;
- *p = NULL;
- if ((phm->function == HPI_CONTROL_GET_STATE)
- && (phm->object == HPI_OBJ_CONTROLEX)
- ) {
- u16 control_index;
- struct hpi_control_cache_info *pI;
-
- if (!find_control(phm, p_cache, &pI, &control_index))
- return 0;
- }
- return 0;
-}
-
/* allow unified treatment of several string fields within struct */
#define HPICMN_PAD_OFS_AND_SIZE(m) {\
offsetof(struct hpi_control_cache_pad, m), \
@@ -276,7 +298,7 @@ struct pad_ofs_size {
unsigned int field_size;
};
-static struct pad_ofs_size pad_desc[] = {
+static const struct pad_ofs_size pad_desc[] = {
HPICMN_PAD_OFS_AND_SIZE(c_channel), /* HPI_PAD_CHANNEL_NAME */
HPICMN_PAD_OFS_AND_SIZE(c_artist), /* HPI_PAD_ARTIST */
HPICMN_PAD_OFS_AND_SIZE(c_title), /* HPI_PAD_TITLE */
@@ -286,79 +308,94 @@ static struct pad_ofs_size pad_desc[] = {
/** CheckControlCache checks the cache and fills the struct hpi_response
* accordingly. It returns one if a cache hit occurred, zero otherwise.
*/
-short hpi_check_control_cache(struct hpi_control_cache *p_cache,
+short hpi_check_control_cache_single(struct hpi_control_cache_single *pC,
struct hpi_message *phm, struct hpi_response *phr)
{
+ size_t response_size;
short found = 1;
- u16 control_index;
- struct hpi_control_cache_info *pI;
- struct hpi_control_cache_single *pC;
- struct hpi_control_cache_pad *p_pad;
-
- if (!find_control(phm, p_cache, &pI, &control_index))
- return 0;
-
- phr->error = 0;
- /* pC is the default cached control strucure. May be cast to
- something else in the following switch statement.
- */
- pC = (struct hpi_control_cache_single *)pI;
- p_pad = (struct hpi_control_cache_pad *)pI;
+ /* set the default response size */
+ response_size =
+ sizeof(struct hpi_response_header) +
+ sizeof(struct hpi_control_res);
- switch (pI->control_type) {
+ switch (pC->u.i.control_type) {
case HPI_CONTROL_METER:
if (phm->u.c.attribute == HPI_METER_PEAK) {
- phr->u.c.an_log_value[0] = pC->u.p.an_log_peak[0];
- phr->u.c.an_log_value[1] = pC->u.p.an_log_peak[1];
+ phr->u.c.an_log_value[0] = pC->u.meter.an_log_peak[0];
+ phr->u.c.an_log_value[1] = pC->u.meter.an_log_peak[1];
} else if (phm->u.c.attribute == HPI_METER_RMS) {
- phr->u.c.an_log_value[0] = pC->u.p.an_logRMS[0];
- phr->u.c.an_log_value[1] = pC->u.p.an_logRMS[1];
+ if (pC->u.meter.an_logRMS[0] ==
+ HPI_CACHE_INVALID_SHORT) {
+ phr->error =
+ HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
+ phr->u.c.an_log_value[0] = HPI_METER_MINIMUM;
+ phr->u.c.an_log_value[1] = HPI_METER_MINIMUM;
+ } else {
+ phr->u.c.an_log_value[0] =
+ pC->u.meter.an_logRMS[0];
+ phr->u.c.an_log_value[1] =
+ pC->u.meter.an_logRMS[1];
+ }
} else
found = 0;
break;
case HPI_CONTROL_VOLUME:
if (phm->u.c.attribute == HPI_VOLUME_GAIN) {
- phr->u.c.an_log_value[0] = pC->u.v.an_log[0];
- phr->u.c.an_log_value[1] = pC->u.v.an_log[1];
- } else
+ phr->u.c.an_log_value[0] = pC->u.vol.an_log[0];
+ phr->u.c.an_log_value[1] = pC->u.vol.an_log[1];
+ } else if (phm->u.c.attribute == HPI_VOLUME_MUTE) {
+ if (pC->u.vol.flags & HPI_VOLUME_FLAG_HAS_MUTE) {
+ if (pC->u.vol.flags & HPI_VOLUME_FLAG_MUTED)
+ phr->u.c.param1 =
+ HPI_BITMASK_ALL_CHANNELS;
+ else
+ phr->u.c.param1 = 0;
+ } else {
+ phr->error =
+ HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
+ phr->u.c.param1 = 0;
+ }
+ } else {
found = 0;
+ }
break;
case HPI_CONTROL_MULTIPLEXER:
if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) {
- phr->u.c.param1 = pC->u.x.source_node_type;
- phr->u.c.param2 = pC->u.x.source_node_index;
+ phr->u.c.param1 = pC->u.mux.source_node_type;
+ phr->u.c.param2 = pC->u.mux.source_node_index;
} else {
found = 0;
}
break;
case HPI_CONTROL_CHANNEL_MODE:
if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE)
- phr->u.c.param1 = pC->u.m.mode;
+ phr->u.c.param1 = pC->u.mode.mode;
else
found = 0;
break;
case HPI_CONTROL_LEVEL:
if (phm->u.c.attribute == HPI_LEVEL_GAIN) {
- phr->u.c.an_log_value[0] = pC->u.l.an_log[0];
- phr->u.c.an_log_value[1] = pC->u.l.an_log[1];
+ phr->u.c.an_log_value[0] = pC->u.level.an_log[0];
+ phr->u.c.an_log_value[1] = pC->u.level.an_log[1];
} else
found = 0;
break;
case HPI_CONTROL_TUNER:
if (phm->u.c.attribute == HPI_TUNER_FREQ)
- phr->u.c.param1 = pC->u.t.freq_ink_hz;
+ phr->u.c.param1 = pC->u.tuner.freq_ink_hz;
else if (phm->u.c.attribute == HPI_TUNER_BAND)
- phr->u.c.param1 = pC->u.t.band;
- else if ((phm->u.c.attribute == HPI_TUNER_LEVEL)
- && (phm->u.c.param1 == HPI_TUNER_LEVEL_AVERAGE))
- if (pC->u.t.level == HPI_ERROR_ILLEGAL_CACHE_VALUE) {
- phr->u.c.param1 = 0;
+ phr->u.c.param1 = pC->u.tuner.band;
+ else if (phm->u.c.attribute == HPI_TUNER_LEVEL_AVG)
+ if (pC->u.tuner.s_level_avg ==
+ HPI_CACHE_INVALID_SHORT) {
+ phr->u.cu.tuner.s_level = 0;
phr->error =
HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
} else
- phr->u.c.param1 = pC->u.t.level;
+ phr->u.cu.tuner.s_level =
+ pC->u.tuner.s_level_avg;
else
found = 0;
break;
@@ -366,7 +403,7 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
if (phm->u.c.attribute == HPI_AESEBURX_ERRORSTATUS)
phr->u.c.param1 = pC->u.aes3rx.error_status;
else if (phm->u.c.attribute == HPI_AESEBURX_FORMAT)
- phr->u.c.param1 = pC->u.aes3rx.source;
+ phr->u.c.param1 = pC->u.aes3rx.format;
else
found = 0;
break;
@@ -385,13 +422,12 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
case HPI_CONTROL_SILENCEDETECTOR:
if (phm->u.c.attribute == HPI_SILENCEDETECTOR_STATE) {
phr->u.c.param1 = pC->u.silence.state;
- phr->u.c.param2 = pC->u.silence.count;
} else
found = 0;
break;
case HPI_CONTROL_MICROPHONE:
if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER)
- phr->u.c.param1 = pC->u.phantom_power.state;
+ phr->u.c.param1 = pC->u.microphone.phantom_state;
else
found = 0;
break;
@@ -400,7 +436,7 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
phr->u.c.param1 = pC->u.clk.source;
else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX) {
if (pC->u.clk.source_index ==
- HPI_ERROR_ILLEGAL_CACHE_VALUE) {
+ HPI_CACHE_INVALID_UINT16) {
phr->u.c.param1 = 0;
phr->error =
HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
@@ -411,60 +447,63 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
else
found = 0;
break;
- case HPI_CONTROL_PAD:
+ case HPI_CONTROL_PAD:{
+ struct hpi_control_cache_pad *p_pad;
+ p_pad = (struct hpi_control_cache_pad *)pC;
- if (!(p_pad->field_valid_flags & (1 <<
- HPI_CTL_ATTR_INDEX(phm->u.c.
- attribute)))) {
- phr->error = HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
- break;
- }
-
- if (phm->u.c.attribute == HPI_PAD_PROGRAM_ID)
- phr->u.c.param1 = p_pad->pI;
- else if (phm->u.c.attribute == HPI_PAD_PROGRAM_TYPE)
- phr->u.c.param1 = p_pad->pTY;
- else {
- unsigned int index =
- HPI_CTL_ATTR_INDEX(phm->u.c.attribute) - 1;
- unsigned int offset = phm->u.c.param1;
- unsigned int pad_string_len, field_size;
- char *pad_string;
- unsigned int tocopy;
-
- HPI_DEBUG_LOG(VERBOSE, "PADS HPI_PADS_ %d\n",
- phm->u.c.attribute);
-
- if (index > ARRAY_SIZE(pad_desc) - 1) {
+ if (!(p_pad->field_valid_flags & (1 <<
+ HPI_CTL_ATTR_INDEX(phm->u.c.
+ attribute)))) {
phr->error =
HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
break;
}
- pad_string = ((char *)p_pad) + pad_desc[index].offset;
- field_size = pad_desc[index].field_size;
- /* Ensure null terminator */
- pad_string[field_size - 1] = 0;
-
- pad_string_len = strlen(pad_string) + 1;
-
- if (offset > pad_string_len) {
- phr->error = HPI_ERROR_INVALID_CONTROL_VALUE;
- break;
+ if (phm->u.c.attribute == HPI_PAD_PROGRAM_ID)
+ phr->u.c.param1 = p_pad->pI;
+ else if (phm->u.c.attribute == HPI_PAD_PROGRAM_TYPE)
+ phr->u.c.param1 = p_pad->pTY;
+ else {
+ unsigned int index =
+ HPI_CTL_ATTR_INDEX(phm->u.c.
+ attribute) - 1;
+ unsigned int offset = phm->u.c.param1;
+ unsigned int pad_string_len, field_size;
+ char *pad_string;
+ unsigned int tocopy;
+
+ if (index > ARRAY_SIZE(pad_desc) - 1) {
+ phr->error =
+ HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
+ break;
+ }
+
+ pad_string =
+ ((char *)p_pad) +
+ pad_desc[index].offset;
+ field_size = pad_desc[index].field_size;
+ /* Ensure null terminator */
+ pad_string[field_size - 1] = 0;
+
+ pad_string_len = strlen(pad_string) + 1;
+
+ if (offset > pad_string_len) {
+ phr->error =
+ HPI_ERROR_INVALID_CONTROL_VALUE;
+ break;
+ }
+
+ tocopy = pad_string_len - offset;
+ if (tocopy > sizeof(phr->u.cu.chars8.sz_data))
+ tocopy = sizeof(phr->u.cu.chars8.
+ sz_data);
+
+ memcpy(phr->u.cu.chars8.sz_data,
+ &pad_string[offset], tocopy);
+
+ phr->u.cu.chars8.remaining_chars =
+ pad_string_len - offset - tocopy;
}
-
- tocopy = pad_string_len - offset;
- if (tocopy > sizeof(phr->u.cu.chars8.sz_data))
- tocopy = sizeof(phr->u.cu.chars8.sz_data);
-
- HPI_DEBUG_LOG(VERBOSE,
- "PADS memcpy(%d), offset %d \n", tocopy,
- offset);
- memcpy(phr->u.cu.chars8.sz_data, &pad_string[offset],
- tocopy);
-
- phr->u.cu.chars8.remaining_chars =
- pad_string_len - offset - tocopy;
}
break;
default:
@@ -472,77 +511,83 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache,
break;
}
- if (found)
- HPI_DEBUG_LOG(VERBOSE,
- "cached adap %d, ctl %d, type %d, attr %d\n",
- phm->adapter_index, pI->control_index,
- pI->control_type, phm->u.c.attribute);
- else
- HPI_DEBUG_LOG(VERBOSE,
- "uncached adap %d, ctl %d, ctl type %d\n",
- phm->adapter_index, pI->control_index,
- pI->control_type);
+ HPI_DEBUG_LOG(VERBOSE, "%s Adap %d, Ctl %d, Type %d, Attr %d\n",
+ found ? "Cached" : "Uncached", phm->adapter_index,
+ pC->u.i.control_index, pC->u.i.control_type,
+ phm->u.c.attribute);
- if (found)
- phr->size =
- sizeof(struct hpi_response_header) +
- sizeof(struct hpi_control_res);
+ if (found) {
+ phr->size = (u16)response_size;
+ phr->type = HPI_TYPE_RESPONSE;
+ phr->object = phm->object;
+ phr->function = phm->function;
+ }
return found;
}
-/** Updates the cache with Set values.
-
-Only update if no error.
-Volume and Level return the limited values in the response, so use these
-Multiplexer does so use sent values
-*/
-void hpi_sync_control_cache(struct hpi_control_cache *p_cache,
+short hpi_check_control_cache(struct hpi_control_cache *p_cache,
struct hpi_message *phm, struct hpi_response *phr)
{
- u16 control_index;
- struct hpi_control_cache_single *pC;
struct hpi_control_cache_info *pI;
- if (phr->error)
- return;
+ if (!find_control(phm->obj_index, p_cache, &pI)) {
+ HPI_DEBUG_LOG(VERBOSE,
+ "HPICMN find_control() failed for adap %d\n",
+ phm->adapter_index);
+ return 0;
+ }
- if (!find_control(phm, p_cache, &pI, &control_index))
- return;
+ phr->error = 0;
+ phr->specific_error = 0;
+ phr->version = 0;
- /* pC is the default cached control strucure.
- May be cast to something else in the following switch statement.
- */
- pC = (struct hpi_control_cache_single *)pI;
+ return hpi_check_control_cache_single((struct hpi_control_cache_single
+ *)pI, phm, phr);
+}
+
+/** Updates the cache with Set values.
- switch (pI->control_type) {
+Only update if no error.
+Volume and Level return the limited values in the response, so use these
+Multiplexer does so use sent values
+*/
+void hpi_cmn_control_cache_sync_to_msg_single(struct hpi_control_cache_single
+ *pC, struct hpi_message *phm, struct hpi_response *phr)
+{
+ switch (pC->u.i.control_type) {
case HPI_CONTROL_VOLUME:
if (phm->u.c.attribute == HPI_VOLUME_GAIN) {
- pC->u.v.an_log[0] = phr->u.c.an_log_value[0];
- pC->u.v.an_log[1] = phr->u.c.an_log_value[1];
+ pC->u.vol.an_log[0] = phr->u.c.an_log_value[0];
+ pC->u.vol.an_log[1] = phr->u.c.an_log_value[1];
+ } else if (phm->u.c.attribute == HPI_VOLUME_MUTE) {
+ if (phm->u.c.param1)
+ pC->u.vol.flags |= HPI_VOLUME_FLAG_MUTED;
+ else
+ pC->u.vol.flags &= ~HPI_VOLUME_FLAG_MUTED;
}
break;
case HPI_CONTROL_MULTIPLEXER:
/* mux does not return its setting on Set command. */
if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) {
- pC->u.x.source_node_type = (u16)phm->u.c.param1;
- pC->u.x.source_node_index = (u16)phm->u.c.param2;
+ pC->u.mux.source_node_type = (u16)phm->u.c.param1;
+ pC->u.mux.source_node_index = (u16)phm->u.c.param2;
}
break;
case HPI_CONTROL_CHANNEL_MODE:
/* mode does not return its setting on Set command. */
if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE)
- pC->u.m.mode = (u16)phm->u.c.param1;
+ pC->u.mode.mode = (u16)phm->u.c.param1;
break;
case HPI_CONTROL_LEVEL:
if (phm->u.c.attribute == HPI_LEVEL_GAIN) {
- pC->u.v.an_log[0] = phr->u.c.an_log_value[0];
- pC->u.v.an_log[1] = phr->u.c.an_log_value[1];
+ pC->u.vol.an_log[0] = phr->u.c.an_log_value[0];
+ pC->u.vol.an_log[1] = phr->u.c.an_log_value[1];
}
break;
case HPI_CONTROL_MICROPHONE:
if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER)
- pC->u.phantom_power.state = (u16)phm->u.c.param1;
+ pC->u.microphone.phantom_state = (u16)phm->u.c.param1;
break;
case HPI_CONTROL_AESEBU_TRANSMITTER:
if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT)
@@ -550,7 +595,7 @@ void hpi_sync_control_cache(struct hpi_control_cache *p_cache,
break;
case HPI_CONTROL_AESEBU_RECEIVER:
if (phm->u.c.attribute == HPI_AESEBURX_FORMAT)
- pC->u.aes3rx.source = phm->u.c.param1;
+ pC->u.aes3rx.format = phm->u.c.param1;
break;
case HPI_CONTROL_SAMPLECLOCK:
if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE)
@@ -565,53 +610,84 @@ void hpi_sync_control_cache(struct hpi_control_cache *p_cache,
}
}
-struct hpi_control_cache *hpi_alloc_control_cache(const u32
- number_of_controls, const u32 size_in_bytes,
- struct hpi_control_cache_info *pDSP_control_buffer)
+void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache,
+ struct hpi_message *phm, struct hpi_response *phr)
+{
+ struct hpi_control_cache_single *pC;
+ struct hpi_control_cache_info *pI;
+
+ if (phr->error)
+ return;
+
+ if (!find_control(phm->obj_index, p_cache, &pI)) {
+ HPI_DEBUG_LOG(VERBOSE,
+ "HPICMN find_control() failed for adap %d\n",
+ phm->adapter_index);
+ return;
+ }
+
+ /* pC is the default cached control strucure.
+ May be cast to something else in the following switch statement.
+ */
+ pC = (struct hpi_control_cache_single *)pI;
+
+ hpi_cmn_control_cache_sync_to_msg_single(pC, phm, phr);
+}
+
+/** Allocate control cache.
+
+\return Cache pointer, or NULL if allocation fails.
+*/
+struct hpi_control_cache *hpi_alloc_control_cache(const u32 control_count,
+ const u32 size_in_bytes, u8 *p_dsp_control_buffer)
{
struct hpi_control_cache *p_cache =
kmalloc(sizeof(*p_cache), GFP_KERNEL);
+ if (!p_cache)
+ return NULL;
+
+ p_cache->p_info =
+ kcalloc(control_count, sizeof(*p_cache->p_info), GFP_KERNEL);
+ if (!p_cache->p_info) {
+ kfree(p_cache);
+ return NULL;
+ }
+
p_cache->cache_size_in_bytes = size_in_bytes;
- p_cache->control_count = number_of_controls;
- p_cache->p_cache =
- (struct hpi_control_cache_single *)pDSP_control_buffer;
+ p_cache->control_count = control_count;
+ p_cache->p_cache = p_dsp_control_buffer;
p_cache->init = 0;
- p_cache->p_info =
- kmalloc(sizeof(*p_cache->p_info) * p_cache->control_count,
- GFP_KERNEL);
return p_cache;
}
void hpi_free_control_cache(struct hpi_control_cache *p_cache)
{
- if (p_cache->init) {
+ if (p_cache) {
kfree(p_cache->p_info);
- p_cache->p_info = NULL;
- p_cache->init = 0;
kfree(p_cache);
}
}
static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
{
+ hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, phm->function, 0);
switch (phm->function) {
case HPI_SUBSYS_OPEN:
case HPI_SUBSYS_CLOSE:
case HPI_SUBSYS_DRIVER_UNLOAD:
- phr->error = 0;
break;
case HPI_SUBSYS_DRIVER_LOAD:
wipe_adapter_list();
hpios_alistlock_init(&adapters);
- phr->error = 0;
break;
- case HPI_SUBSYS_GET_INFO:
- subsys_get_adapters(phr);
+ case HPI_SUBSYS_GET_ADAPTER:
+ subsys_get_adapter(phm, phr);
+ break;
+ case HPI_SUBSYS_GET_NUM_ADAPTERS:
+ phr->u.s.num_adapters = adapters.gw_num_adapters;
break;
case HPI_SUBSYS_CREATE_ADAPTER:
- case HPI_SUBSYS_DELETE_ADAPTER:
- phr->error = 0;
break;
default:
phr->error = HPI_ERROR_INVALID_FUNC;
@@ -622,7 +698,7 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
void HPI_COMMON(struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->type) {
- case HPI_TYPE_MESSAGE:
+ case HPI_TYPE_REQUEST:
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
subsys_message(phm, phr);
diff --git a/sound/pci/asihpi/hpicmn.h b/sound/pci/asihpi/hpicmn.h
index 6229022f56cb..8ec656cf8848 100644
--- a/sound/pci/asihpi/hpicmn.h
+++ b/sound/pci/asihpi/hpicmn.h
@@ -1,64 +1,71 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
+*/
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+struct hpi_adapter_obj;
-*/
+/* a function that takes an adapter obj and returns an int */
+typedef int adapter_int_func(struct hpi_adapter_obj *pao, u32 message);
+
+#define HPI_IRQ_NONE (0)
+#define HPI_IRQ_MESSAGE (1)
+#define HPI_IRQ_MIXER (2)
struct hpi_adapter_obj {
struct hpi_pci pci; /* PCI info - bus#,dev#,address etc */
- u16 adapter_type; /* ASI6701 etc */
- u16 index; /* */
- u16 open; /* =1 when adapter open */
- u16 mixer_open;
+ u16 type; /* 0x6644 == ASI6644 etc */
+ u16 index;
struct hpios_spinlock dsp_lock;
u16 dsp_crashed;
u16 has_control_cache;
void *priv;
+ adapter_int_func *irq_query_and_clear;
+ struct hpi_hostbuffer_status *instream_host_buffer_status;
+ struct hpi_hostbuffer_status *outstream_host_buffer_status;
};
struct hpi_control_cache {
- u32 init; /**< indicates whether the
- structures are initialized */
+ /** indicates whether the structures are initialized */
+ u16 init;
+ u16 adap_idx;
u32 control_count;
u32 cache_size_in_bytes;
- struct hpi_control_cache_info
- **p_info; /**< pointer to allocated memory of
- lookup pointers. */
- struct hpi_control_cache_single
- *p_cache; /**< pointer to DSP's control cache. */
+ /** pointer to allocated memory of lookup pointers. */
+ struct hpi_control_cache_info **p_info;
+ /** pointer to DSP's control cache. */
+ u8 *p_cache;
};
struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index);
+
u16 hpi_add_adapter(struct hpi_adapter_obj *pao);
void hpi_delete_adapter(struct hpi_adapter_obj *pao);
short hpi_check_control_cache(struct hpi_control_cache *pC,
struct hpi_message *phm, struct hpi_response *phr);
+
+short hpi_check_control_cache_single(struct hpi_control_cache_single *pC,
+ struct hpi_message *phm, struct hpi_response *phr);
+
struct hpi_control_cache *hpi_alloc_control_cache(const u32
- number_of_controls, const u32 size_in_bytes,
- struct hpi_control_cache_info
- *pDSP_control_buffer);
+ number_of_controls, const u32 size_in_bytes, u8 *pDSP_control_buffer);
+
void hpi_free_control_cache(struct hpi_control_cache *p_cache);
-void hpi_sync_control_cache(struct hpi_control_cache *pC,
+void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *pC,
struct hpi_message *phm, struct hpi_response *phr);
+
+void hpi_cmn_control_cache_sync_to_msg_single(struct hpi_control_cache_single
+ *pC, struct hpi_message *phm, struct hpi_response *phr);
+
u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr);
-short hpi_check_buffer_mapping(struct hpi_control_cache *p_cache,
- struct hpi_message *phm, void **p, unsigned int *pN);
+
+hpi_handler_func HPI_COMMON;
diff --git a/sound/pci/asihpi/hpidebug.c b/sound/pci/asihpi/hpidebug.c
index 949836ec913a..9570d9a44fe8 100644
--- a/sound/pci/asihpi/hpidebug.c
+++ b/sound/pci/asihpi/hpidebug.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Debug macro translation.
@@ -45,161 +34,14 @@ int hpi_debug_level_get(void)
return hpi_debug_level;
}
-#ifdef HPIOS_DEBUG_PRINT
-/* implies OS has no printf-like function */
-#include <stdarg.h>
-
-void hpi_debug_printf(char *fmt, ...)
-{
- va_list arglist;
- char buffer[128];
-
- va_start(arglist, fmt);
-
- if (buffer[0])
- HPIOS_DEBUG_PRINT(buffer);
- va_end(arglist);
-}
-#endif
-
-struct treenode {
- void *array;
- unsigned int num_elements;
-};
-
-#define make_treenode_from_array(nodename, array) \
-static void *tmp_strarray_##nodename[] = array; \
-static struct treenode nodename = { \
- &tmp_strarray_##nodename, \
- ARRAY_SIZE(tmp_strarray_##nodename) \
-};
-
-#define get_treenode_elem(node_ptr, idx, type) \
- (&(*((type *)(node_ptr)->array)[idx]))
-
-make_treenode_from_array(hpi_control_type_strings, HPI_CONTROL_TYPE_STRINGS)
-
- make_treenode_from_array(hpi_subsys_strings, HPI_SUBSYS_STRINGS)
- make_treenode_from_array(hpi_adapter_strings, HPI_ADAPTER_STRINGS)
- make_treenode_from_array(hpi_istream_strings, HPI_ISTREAM_STRINGS)
- make_treenode_from_array(hpi_ostream_strings, HPI_OSTREAM_STRINGS)
- make_treenode_from_array(hpi_mixer_strings, HPI_MIXER_STRINGS)
- make_treenode_from_array(hpi_node_strings,
- {
- "NODE is invalid object"})
-
- make_treenode_from_array(hpi_control_strings, HPI_CONTROL_STRINGS)
- make_treenode_from_array(hpi_nvmemory_strings, HPI_OBJ_STRINGS)
- make_treenode_from_array(hpi_digitalio_strings, HPI_DIGITALIO_STRINGS)
- make_treenode_from_array(hpi_watchdog_strings, HPI_WATCHDOG_STRINGS)
- make_treenode_from_array(hpi_clock_strings, HPI_CLOCK_STRINGS)
- make_treenode_from_array(hpi_profile_strings, HPI_PROFILE_STRINGS)
- make_treenode_from_array(hpi_asyncevent_strings, HPI_ASYNCEVENT_STRINGS)
-#define HPI_FUNCTION_STRINGS \
-{ \
- &hpi_subsys_strings,\
- &hpi_adapter_strings,\
- &hpi_ostream_strings,\
- &hpi_istream_strings,\
- &hpi_mixer_strings,\
- &hpi_node_strings,\
- &hpi_control_strings,\
- &hpi_nvmemory_strings,\
- &hpi_digitalio_strings,\
- &hpi_watchdog_strings,\
- &hpi_clock_strings,\
- &hpi_profile_strings,\
- &hpi_control_strings, \
- &hpi_asyncevent_strings \
-}
- make_treenode_from_array(hpi_function_strings, HPI_FUNCTION_STRINGS)
-
- compile_time_assert(HPI_OBJ_MAXINDEX == 14, obj_list_doesnt_match);
-
-static char *hpi_function_string(unsigned int function)
-{
- unsigned int object;
- struct treenode *tmp;
-
- object = function / HPI_OBJ_FUNCTION_SPACING;
- function = function - object * HPI_OBJ_FUNCTION_SPACING;
-
- if (object == 0 || object == HPI_OBJ_NODE
- || object > hpi_function_strings.num_elements)
- return "invalid object";
-
- tmp = get_treenode_elem(&hpi_function_strings, object - 1,
- struct treenode *);
-
- if (function == 0 || function > tmp->num_elements)
- return "invalid function";
-
- return get_treenode_elem(tmp, function - 1, char *);
-}
-
void hpi_debug_message(struct hpi_message *phm, char *sz_fileline)
{
if (phm) {
- if ((phm->object <= HPI_OBJ_MAXINDEX) && phm->object) {
- u16 index = 0;
- u16 attrib = 0;
- int is_control = 0;
-
- index = phm->obj_index;
- switch (phm->object) {
- case HPI_OBJ_ADAPTER:
- case HPI_OBJ_PROFILE:
- break;
- case HPI_OBJ_MIXER:
- if (phm->function ==
- HPI_MIXER_GET_CONTROL_BY_INDEX)
- index = phm->u.m.control_index;
- break;
- case HPI_OBJ_OSTREAM:
- case HPI_OBJ_ISTREAM:
- break;
-
- case HPI_OBJ_CONTROLEX:
- case HPI_OBJ_CONTROL:
- if (phm->version == 1)
- attrib = HPI_CTL_ATTR(UNIVERSAL, 1);
- else
- attrib = phm->u.c.attribute;
- is_control = 1;
- break;
- default:
- break;
- }
-
- if (is_control && (attrib & 0xFF00)) {
- int control_type = (attrib & 0xFF00) >> 8;
- int attr_index = HPI_CTL_ATTR_INDEX(attrib);
- /* note the KERN facility level
- is in szFileline already */
- printk("%s adapter %d %s "
- "ctrl_index x%04x %s %d\n",
- sz_fileline, phm->adapter_index,
- hpi_function_string(phm->function),
- index,
- get_treenode_elem
- (&hpi_control_type_strings,
- control_type, char *),
- attr_index);
-
- } else
- printk("%s adapter %d %s "
- "idx x%04x attr x%04x \n",
- sz_fileline, phm->adapter_index,
- hpi_function_string(phm->function),
- index, attrib);
- } else {
- printk("adap=%d, invalid obj=%d, func=0x%x\n",
- phm->adapter_index, phm->object,
- phm->function);
- }
- } else
- printk(KERN_ERR
- "NULL message pointer to hpi_debug_message!\n");
+ printk(KERN_DEBUG "HPI_MSG%d,%d,%d,%d,%d\n", phm->version,
+ phm->adapter_index, phm->obj_index, phm->function,
+ phm->u.c.attribute);
+ }
+
}
void hpi_debug_data(u16 *pdata, u32 len)
@@ -210,7 +52,7 @@ void hpi_debug_data(u16 *pdata, u32 len)
int lines;
int cols = 8;
- lines = (len + cols - 1) / cols;
+ lines = DIV_ROUND_UP(len, cols);
if (lines > 8)
lines = 8;
@@ -218,8 +60,8 @@ void hpi_debug_data(u16 *pdata, u32 len)
printk(KERN_DEBUG "%p:", (pdata + i));
for (k = 0; k < cols && i < len; i++, k++)
- printk("%s%04x", k == 0 ? "" : " ", pdata[i]);
+ printk(KERN_CONT "%s%04x", k == 0 ? "" : " ", pdata[i]);
- printk("\n");
+ printk(KERN_CONT "\n");
}
}
diff --git a/sound/pci/asihpi/hpidebug.h b/sound/pci/asihpi/hpidebug.h
index a2f0952a99f0..c6dfc229213d 100644
--- a/sound/pci/asihpi/hpidebug.h
+++ b/sound/pci/asihpi/hpidebug.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*****************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Debug macros.
@@ -37,26 +26,18 @@ enum { HPI_DEBUG_LEVEL_ERROR = 0, /* always log errors */
#define HPI_DEBUG_LEVEL_DEFAULT HPI_DEBUG_LEVEL_NOTICE
/* an OS can define an extra flag string that is appended to
- the start of each message, eg see hpios_linux.h */
+ the start of each message, eg see linux kernel hpios.h */
#ifdef SOURCEFILE_NAME
+#undef FILE_LINE
#define FILE_LINE SOURCEFILE_NAME ":" __stringify(__LINE__) " "
-#else
-#define FILE_LINE __FILE__ ":" __stringify(__LINE__) " "
-#endif
-
-#if defined(HPI_DEBUG) && defined(_WINDOWS)
-#define HPI_DEBUGBREAK() debug_break()
-#else
-#define HPI_DEBUGBREAK()
#endif
#define HPI_DEBUG_ASSERT(expression) \
do { \
- if (!(expression)) {\
- printk(KERN_ERR FILE_LINE\
- "ASSERT " __stringify(expression));\
- HPI_DEBUGBREAK();\
+ if (!(expression)) { \
+ printk(KERN_ERR FILE_LINE \
+ " ASSERT " __stringify(expression)); \
} \
} while (0)
@@ -64,7 +45,7 @@ enum { HPI_DEBUG_LEVEL_ERROR = 0, /* always log errors */
do { \
if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \
printk(HPI_DEBUG_FLAG_##level \
- FILE_LINE __VA_ARGS__); \
+ FILE_LINE " " __VA_ARGS__); \
} \
} while (0)
@@ -78,28 +59,27 @@ void hpi_debug_message(struct hpi_message *phm, char *sz_fileline);
void hpi_debug_data(u16 *pdata, u32 len);
-#define HPI_DEBUG_DATA(pdata, len) \
- do { \
+#define HPI_DEBUG_DATA(pdata, len) \
+ do { \
if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) \
hpi_debug_data(pdata, len); \
} while (0)
-#define HPI_DEBUG_MESSAGE(level, phm) \
- do { \
- if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \
- hpi_debug_message(phm,HPI_DEBUG_FLAG_##level \
- FILE_LINE __stringify(level));\
- } \
+#define HPI_DEBUG_MESSAGE(level, phm) \
+ do { \
+ if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \
+ hpi_debug_message(phm, HPI_DEBUG_FLAG_##level \
+ FILE_LINE " " __stringify(level)); \
+ } \
} while (0)
-#define HPI_DEBUG_RESPONSE(phr) \
- do { \
- if ((hpi_debug_level >= HPI_DEBUG_LEVEL_DEBUG) && (phr->error))\
- HPI_DEBUG_LOG(ERROR, \
- "HPI response - error# %d\n", \
- phr->error); \
- else if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) \
- HPI_DEBUG_LOG(VERBOSE, "HPI response OK\n");\
+#define HPI_DEBUG_RESPONSE(phr) \
+ do { \
+ if (((hpi_debug_level >= HPI_DEBUG_LEVEL_DEBUG) && \
+ (phr->error)) ||\
+ (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE)) \
+ printk(KERN_DEBUG "HPI_RES%d,%d,%d\n", \
+ phr->version, phr->error, phr->specific_error); \
} while (0)
#ifndef compile_time_assert
@@ -107,279 +87,4 @@ void hpi_debug_data(u16 *pdata, u32 len);
typedef char msg[(cond) ? 1 : -1]
#endif
- /* check that size is exactly some number */
-#define function_count_check(sym, size) \
- compile_time_assert((sym##_FUNCTION_COUNT) == (size),\
- strings_match_defs_##sym)
-
-/* These strings should be generated using a macro which defines
- the corresponding symbol values. */
-#define HPI_OBJ_STRINGS \
-{ \
- "HPI_OBJ_SUBSYSTEM", \
- "HPI_OBJ_ADAPTER", \
- "HPI_OBJ_OSTREAM", \
- "HPI_OBJ_ISTREAM", \
- "HPI_OBJ_MIXER", \
- "HPI_OBJ_NODE", \
- "HPI_OBJ_CONTROL", \
- "HPI_OBJ_NVMEMORY", \
- "HPI_OBJ_DIGITALIO", \
- "HPI_OBJ_WATCHDOG", \
- "HPI_OBJ_CLOCK", \
- "HPI_OBJ_PROFILE", \
- "HPI_OBJ_CONTROLEX" \
-}
-
-#define HPI_SUBSYS_STRINGS \
-{ \
- "HPI_SUBSYS_OPEN", \
- "HPI_SUBSYS_GET_VERSION", \
- "HPI_SUBSYS_GET_INFO", \
- "HPI_SUBSYS_FIND_ADAPTERS", \
- "HPI_SUBSYS_CREATE_ADAPTER",\
- "HPI_SUBSYS_CLOSE", \
- "HPI_SUBSYS_DELETE_ADAPTER", \
- "HPI_SUBSYS_DRIVER_LOAD", \
- "HPI_SUBSYS_DRIVER_UNLOAD", \
- "HPI_SUBSYS_READ_PORT_8", \
- "HPI_SUBSYS_WRITE_PORT_8", \
- "HPI_SUBSYS_GET_NUM_ADAPTERS",\
- "HPI_SUBSYS_GET_ADAPTER", \
- "HPI_SUBSYS_SET_NETWORK_INTERFACE"\
-}
-function_count_check(HPI_SUBSYS, 14);
-
-#define HPI_ADAPTER_STRINGS \
-{ \
- "HPI_ADAPTER_OPEN", \
- "HPI_ADAPTER_CLOSE", \
- "HPI_ADAPTER_GET_INFO", \
- "HPI_ADAPTER_GET_ASSERT", \
- "HPI_ADAPTER_TEST_ASSERT", \
- "HPI_ADAPTER_SET_MODE", \
- "HPI_ADAPTER_GET_MODE", \
- "HPI_ADAPTER_ENABLE_CAPABILITY",\
- "HPI_ADAPTER_SELFTEST", \
- "HPI_ADAPTER_FIND_OBJECT", \
- "HPI_ADAPTER_QUERY_FLASH", \
- "HPI_ADAPTER_START_FLASH", \
- "HPI_ADAPTER_PROGRAM_FLASH", \
- "HPI_ADAPTER_SET_PROPERTY", \
- "HPI_ADAPTER_GET_PROPERTY", \
- "HPI_ADAPTER_ENUM_PROPERTY", \
- "HPI_ADAPTER_MODULE_INFO", \
- "HPI_ADAPTER_DEBUG_READ" \
-}
-
-function_count_check(HPI_ADAPTER, 18);
-
-#define HPI_OSTREAM_STRINGS \
-{ \
- "HPI_OSTREAM_OPEN", \
- "HPI_OSTREAM_CLOSE", \
- "HPI_OSTREAM_WRITE", \
- "HPI_OSTREAM_START", \
- "HPI_OSTREAM_STOP", \
- "HPI_OSTREAM_RESET", \
- "HPI_OSTREAM_GET_INFO", \
- "HPI_OSTREAM_QUERY_FORMAT", \
- "HPI_OSTREAM_DATA", \
- "HPI_OSTREAM_SET_VELOCITY", \
- "HPI_OSTREAM_SET_PUNCHINOUT", \
- "HPI_OSTREAM_SINEGEN", \
- "HPI_OSTREAM_ANC_RESET", \
- "HPI_OSTREAM_ANC_GET_INFO", \
- "HPI_OSTREAM_ANC_READ", \
- "HPI_OSTREAM_SET_TIMESCALE",\
- "HPI_OSTREAM_SET_FORMAT", \
- "HPI_OSTREAM_HOSTBUFFER_ALLOC", \
- "HPI_OSTREAM_HOSTBUFFER_FREE", \
- "HPI_OSTREAM_GROUP_ADD",\
- "HPI_OSTREAM_GROUP_GETMAP", \
- "HPI_OSTREAM_GROUP_RESET", \
- "HPI_OSTREAM_HOSTBUFFER_GET_INFO", \
- "HPI_OSTREAM_WAIT_START", \
-}
-function_count_check(HPI_OSTREAM, 24);
-
-#define HPI_ISTREAM_STRINGS \
-{ \
- "HPI_ISTREAM_OPEN", \
- "HPI_ISTREAM_CLOSE", \
- "HPI_ISTREAM_SET_FORMAT", \
- "HPI_ISTREAM_READ", \
- "HPI_ISTREAM_START", \
- "HPI_ISTREAM_STOP", \
- "HPI_ISTREAM_RESET", \
- "HPI_ISTREAM_GET_INFO", \
- "HPI_ISTREAM_QUERY_FORMAT", \
- "HPI_ISTREAM_ANC_RESET", \
- "HPI_ISTREAM_ANC_GET_INFO", \
- "HPI_ISTREAM_ANC_WRITE", \
- "HPI_ISTREAM_HOSTBUFFER_ALLOC",\
- "HPI_ISTREAM_HOSTBUFFER_FREE", \
- "HPI_ISTREAM_GROUP_ADD", \
- "HPI_ISTREAM_GROUP_GETMAP", \
- "HPI_ISTREAM_GROUP_RESET", \
- "HPI_ISTREAM_HOSTBUFFER_GET_INFO", \
- "HPI_ISTREAM_WAIT_START", \
-}
-function_count_check(HPI_ISTREAM, 19);
-
-#define HPI_MIXER_STRINGS \
-{ \
- "HPI_MIXER_OPEN", \
- "HPI_MIXER_CLOSE", \
- "HPI_MIXER_GET_INFO", \
- "HPI_MIXER_GET_NODE_INFO", \
- "HPI_MIXER_GET_CONTROL", \
- "HPI_MIXER_SET_CONNECTION", \
- "HPI_MIXER_GET_CONNECTIONS", \
- "HPI_MIXER_GET_CONTROL_BY_INDEX", \
- "HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX", \
- "HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES", \
- "HPI_MIXER_STORE", \
-}
-function_count_check(HPI_MIXER, 11);
-
-#define HPI_CONTROL_STRINGS \
-{ \
- "HPI_CONTROL_GET_INFO", \
- "HPI_CONTROL_GET_STATE", \
- "HPI_CONTROL_SET_STATE" \
-}
-function_count_check(HPI_CONTROL, 3);
-
-#define HPI_NVMEMORY_STRINGS \
-{ \
- "HPI_NVMEMORY_OPEN", \
- "HPI_NVMEMORY_READ_BYTE", \
- "HPI_NVMEMORY_WRITE_BYTE" \
-}
-function_count_check(HPI_NVMEMORY, 3);
-
-#define HPI_DIGITALIO_STRINGS \
-{ \
- "HPI_GPIO_OPEN", \
- "HPI_GPIO_READ_BIT", \
- "HPI_GPIO_WRITE_BIT", \
- "HPI_GPIO_READ_ALL", \
- "HPI_GPIO_WRITE_STATUS"\
-}
-function_count_check(HPI_GPIO, 5);
-
-#define HPI_WATCHDOG_STRINGS \
-{ \
- "HPI_WATCHDOG_OPEN", \
- "HPI_WATCHDOG_SET_TIME", \
- "HPI_WATCHDOG_PING" \
-}
-
-#define HPI_CLOCK_STRINGS \
-{ \
- "HPI_CLOCK_OPEN", \
- "HPI_CLOCK_SET_TIME", \
- "HPI_CLOCK_GET_TIME" \
-}
-
-#define HPI_PROFILE_STRINGS \
-{ \
- "HPI_PROFILE_OPEN_ALL", \
- "HPI_PROFILE_START_ALL", \
- "HPI_PROFILE_STOP_ALL", \
- "HPI_PROFILE_GET", \
- "HPI_PROFILE_GET_IDLECOUNT", \
- "HPI_PROFILE_GET_NAME", \
- "HPI_PROFILE_GET_UTILIZATION" \
-}
-function_count_check(HPI_PROFILE, 7);
-
-#define HPI_ASYNCEVENT_STRINGS \
-{ \
- "HPI_ASYNCEVENT_OPEN",\
- "HPI_ASYNCEVENT_CLOSE ",\
- "HPI_ASYNCEVENT_WAIT",\
- "HPI_ASYNCEVENT_GETCOUNT",\
- "HPI_ASYNCEVENT_GET",\
- "HPI_ASYNCEVENT_SENDEVENTS"\
-}
-function_count_check(HPI_ASYNCEVENT, 6);
-
-#define HPI_CONTROL_TYPE_STRINGS \
-{ \
- "null control", \
- "HPI_CONTROL_CONNECTION", \
- "HPI_CONTROL_VOLUME", \
- "HPI_CONTROL_METER", \
- "HPI_CONTROL_MUTE", \
- "HPI_CONTROL_MULTIPLEXER", \
- "HPI_CONTROL_AESEBU_TRANSMITTER", \
- "HPI_CONTROL_AESEBU_RECEIVER", \
- "HPI_CONTROL_LEVEL", \
- "HPI_CONTROL_TUNER", \
- "HPI_CONTROL_ONOFFSWITCH", \
- "HPI_CONTROL_VOX", \
- "HPI_CONTROL_AES18_TRANSMITTER", \
- "HPI_CONTROL_AES18_RECEIVER", \
- "HPI_CONTROL_AES18_BLOCKGENERATOR", \
- "HPI_CONTROL_CHANNEL_MODE", \
- "HPI_CONTROL_BITSTREAM", \
- "HPI_CONTROL_SAMPLECLOCK", \
- "HPI_CONTROL_MICROPHONE", \
- "HPI_CONTROL_PARAMETRIC_EQ", \
- "HPI_CONTROL_COMPANDER", \
- "HPI_CONTROL_COBRANET", \
- "HPI_CONTROL_TONE_DETECT", \
- "HPI_CONTROL_SILENCE_DETECT", \
- "HPI_CONTROL_PAD", \
- "HPI_CONTROL_SRC" ,\
- "HPI_CONTROL_UNIVERSAL" \
-}
-
-compile_time_assert((HPI_CONTROL_LAST_INDEX + 1 == 27),
- controltype_strings_match_defs);
-
-#define HPI_SOURCENODE_STRINGS \
-{ \
- "no source", \
- "HPI_SOURCENODE_OSTREAM", \
- "HPI_SOURCENODE_LINEIN", \
- "HPI_SOURCENODE_AESEBU_IN", \
- "HPI_SOURCENODE_TUNER", \
- "HPI_SOURCENODE_RF", \
- "HPI_SOURCENODE_CLOCK_SOURCE", \
- "HPI_SOURCENODE_RAW_BITSTREAM", \
- "HPI_SOURCENODE_MICROPHONE", \
- "HPI_SOURCENODE_COBRANET", \
- "HPI_SOURCENODE_ANALOG", \
- "HPI_SOURCENODE_ADAPTER" \
-}
-
-compile_time_assert((HPI_SOURCENODE_LAST_INDEX - HPI_SOURCENODE_NONE + 1) ==
- (12), sourcenode_strings_match_defs);
-
-#define HPI_DESTNODE_STRINGS \
-{ \
- "no destination", \
- "HPI_DESTNODE_ISTREAM", \
- "HPI_DESTNODE_LINEOUT", \
- "HPI_DESTNODE_AESEBU_OUT", \
- "HPI_DESTNODE_RF", \
- "HPI_DESTNODE_SPEAKER", \
- "HPI_DESTNODE_COBRANET", \
- "HPI_DESTNODE_ANALOG" \
-}
-compile_time_assert((HPI_DESTNODE_LAST_INDEX - HPI_DESTNODE_NONE + 1) == (8),
- destnode_strings_match_defs);
-
-#define HPI_CONTROL_CHANNEL_MODE_STRINGS \
-{ \
- "XXX HPI_CHANNEL_MODE_ERROR XXX", \
- "HPI_CHANNEL_MODE_NORMAL", \
- "HPI_CHANNEL_MODE_SWAP", \
- "HPI_CHANNEL_MODE_LEFT_ONLY", \
- "HPI_CHANNEL_MODE_RIGHT_ONLY" \
-}
-
-#endif /* _HPIDEBUG_H */
+#endif /* _HPIDEBUG_H_ */
diff --git a/sound/pci/asihpi/hpidspcd.c b/sound/pci/asihpi/hpidspcd.c
index 9b10d9a5c255..9acc0ac75eca 100644
--- a/sound/pci/asihpi/hpidspcd.c
+++ b/sound/pci/asihpi/hpidspcd.c
@@ -1,172 +1,131 @@
-/***********************************************************************/
-/*!
+// SPDX-License-Identifier: GPL-2.0-only
+/***********************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Functions for reading DSP code using hotplug firmware loader
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-\file
-Functions for reading DSP code to load into DSP
-
-(Linux only:) If DSPCODE_FIRMWARE_LOADER is defined, code is read using
-hotplug firmware loader from individual dsp code files
-
-If neither of the above is defined, code is read from linked arrays.
-DSPCODE_ARRAY is defined.
-
-HPI_INCLUDE_**** must be defined
-and the appropriate hzz?????.c or hex?????.c linked in
-
- */
-/***********************************************************************/
+***********************************************************************/
#define SOURCEFILE_NAME "hpidspcd.c"
#include "hpidspcd.h"
#include "hpidebug.h"
+#include "hpi_version.h"
-/**
- Header structure for binary dsp code file (see asidsp.doc)
- This structure must match that used in s2bin.c for generation of asidsp.bin
- */
-
-#ifndef DISABLE_PRAGMA_PACK1
-#pragma pack(push, 1)
-#endif
-
-struct code_header {
- u32 size;
- char type[4];
- u32 adapter;
- u32 version;
- u32 crc;
+struct dsp_code_private {
+ /** Firmware descriptor */
+ const struct firmware *firmware;
+ struct pci_dev *dev;
};
-#ifndef DISABLE_PRAGMA_PACK1
-#pragma pack(pop)
-#endif
-
-#define HPI_VER_DECIMAL ((int)(HPI_VER_MAJOR(HPI_VER) * 10000 + \
- HPI_VER_MINOR(HPI_VER) * 100 + HPI_VER_RELEASE(HPI_VER)))
-
-/***********************************************************************/
-#include "linux/pci.h"
/*-------------------------------------------------------------------*/
-short hpi_dsp_code_open(u32 adapter, struct dsp_code *ps_dsp_code,
- u32 *pos_error_code)
+short hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code,
+ u32 *os_error_code)
{
- const struct firmware *ps_firmware = ps_dsp_code->ps_firmware;
+ const struct firmware *firmware;
+ struct pci_dev *dev = os_data;
struct code_header header;
char fw_name[20];
+ short err_ret = HPI_ERROR_DSP_FILE_NOT_FOUND;
int err;
sprintf(fw_name, "asihpi/dsp%04x.bin", adapter);
- HPI_DEBUG_LOG(INFO, "requesting firmware for %s\n", fw_name);
- err = request_firmware(&ps_firmware, fw_name,
- &ps_dsp_code->ps_dev->dev);
- if (err != 0) {
- HPI_DEBUG_LOG(ERROR, "%d, request_firmware failed for %s\n",
+ err = request_firmware(&firmware, fw_name, &dev->dev);
+
+ if (err || !firmware) {
+ dev_err(&dev->dev, "%d, request_firmware failed for %s\n",
err, fw_name);
goto error1;
}
- if (ps_firmware->size < sizeof(header)) {
- HPI_DEBUG_LOG(ERROR, "header size too small %s\n", fw_name);
+ if (firmware->size < sizeof(header)) {
+ dev_err(&dev->dev, "Header size too small %s\n", fw_name);
goto error2;
}
- memcpy(&header, ps_firmware->data, sizeof(header));
- if (header.adapter != adapter) {
- HPI_DEBUG_LOG(ERROR, "adapter type incorrect %4x != %4x\n",
- header.adapter, adapter);
+ memcpy(&header, firmware->data, sizeof(header));
+
+ if ((header.type != 0x45444F43) || /* "CODE" */
+ (header.adapter != adapter)
+ || (header.size != firmware->size)) {
+ dev_err(&dev->dev,
+ "Invalid firmware header size %d != file %zd\n",
+ header.size, firmware->size);
goto error2;
}
- if (header.size != ps_firmware->size) {
- HPI_DEBUG_LOG(ERROR, "code size wrong %d != %ld\n",
- header.size, (unsigned long)ps_firmware->size);
+
+ if (HPI_VER_MAJOR(header.version) != HPI_VER_MAJOR(HPI_VER)) {
+ /* Major version change probably means Host-DSP protocol change */
+ dev_err(&dev->dev,
+ "Incompatible firmware version DSP image %X != Driver %X\n",
+ header.version, HPI_VER);
goto error2;
}
- if (header.version / 10000 != HPI_VER_DECIMAL / 10000) {
- HPI_DEBUG_LOG(ERROR,
- "firmware major version mismatch "
- "DSP image %d != driver %d\n", header.version,
- HPI_VER_DECIMAL);
- goto error2;
+ if (header.version != HPI_VER) {
+ dev_warn(&dev->dev,
+ "Firmware version mismatch: DSP image %X != Driver %X\n",
+ header.version, HPI_VER);
}
- if (header.version != HPI_VER_DECIMAL) {
- HPI_DEBUG_LOG(WARNING,
- "version mismatch DSP image %d != driver %d\n",
- header.version, HPI_VER_DECIMAL);
- /* goto error2; still allow driver to load */
+ HPI_DEBUG_LOG(DEBUG, "dsp code %s opened\n", fw_name);
+ dsp_code->pvt = kmalloc(sizeof(*dsp_code->pvt), GFP_KERNEL);
+ if (!dsp_code->pvt) {
+ err_ret = HPI_ERROR_MEMORY_ALLOC;
+ goto error2;
}
- HPI_DEBUG_LOG(INFO, "dsp code %s opened\n", fw_name);
- ps_dsp_code->ps_firmware = ps_firmware;
- ps_dsp_code->block_length = header.size / sizeof(u32);
- ps_dsp_code->word_count = sizeof(header) / sizeof(u32);
- ps_dsp_code->version = header.version;
- ps_dsp_code->crc = header.crc;
+ dsp_code->pvt->dev = dev;
+ dsp_code->pvt->firmware = firmware;
+ dsp_code->header = header;
+ dsp_code->block_length = header.size / sizeof(u32);
+ dsp_code->word_count = sizeof(header) / sizeof(u32);
return 0;
error2:
- release_firmware(ps_firmware);
+ release_firmware(firmware);
error1:
- ps_dsp_code->ps_firmware = NULL;
- ps_dsp_code->block_length = 0;
- return HPI_ERROR_DSP_FILE_NOT_FOUND;
+ dsp_code->block_length = 0;
+ return err_ret;
}
/*-------------------------------------------------------------------*/
-void hpi_dsp_code_close(struct dsp_code *ps_dsp_code)
+void hpi_dsp_code_close(struct dsp_code *dsp_code)
{
- if (ps_dsp_code->ps_firmware != NULL) {
- HPI_DEBUG_LOG(DEBUG, "dsp code closed\n");
- release_firmware(ps_dsp_code->ps_firmware);
- ps_dsp_code->ps_firmware = NULL;
- }
+ HPI_DEBUG_LOG(DEBUG, "dsp code closed\n");
+ release_firmware(dsp_code->pvt->firmware);
+ kfree(dsp_code->pvt);
}
/*-------------------------------------------------------------------*/
-void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code)
+void hpi_dsp_code_rewind(struct dsp_code *dsp_code)
{
/* Go back to start of data, after header */
- ps_dsp_code->word_count = sizeof(struct code_header) / sizeof(u32);
+ dsp_code->word_count = sizeof(struct code_header) / sizeof(u32);
}
/*-------------------------------------------------------------------*/
-short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code, u32 *pword)
+short hpi_dsp_code_read_word(struct dsp_code *dsp_code, u32 *pword)
{
- if (ps_dsp_code->word_count + 1 > ps_dsp_code->block_length)
- return (HPI_ERROR_DSP_FILE_FORMAT);
+ if (dsp_code->word_count + 1 > dsp_code->block_length)
+ return HPI_ERROR_DSP_FILE_FORMAT;
- *pword = ((u32 *)(ps_dsp_code->ps_firmware->data))[ps_dsp_code->
+ *pword = ((u32 *)(dsp_code->pvt->firmware->data))[dsp_code->
word_count];
- ps_dsp_code->word_count++;
+ dsp_code->word_count++;
return 0;
}
/*-------------------------------------------------------------------*/
short hpi_dsp_code_read_block(size_t words_requested,
- struct dsp_code *ps_dsp_code, u32 **ppblock)
+ struct dsp_code *dsp_code, u32 **ppblock)
{
- if (ps_dsp_code->word_count + words_requested >
- ps_dsp_code->block_length)
+ if (dsp_code->word_count + words_requested > dsp_code->block_length)
return HPI_ERROR_DSP_FILE_FORMAT;
*ppblock =
- ((u32 *)(ps_dsp_code->ps_firmware->data)) +
- ps_dsp_code->word_count;
- ps_dsp_code->word_count += words_requested;
+ ((u32 *)(dsp_code->pvt->firmware->data)) +
+ dsp_code->word_count;
+ dsp_code->word_count += words_requested;
return 0;
}
diff --git a/sound/pci/asihpi/hpidspcd.h b/sound/pci/asihpi/hpidspcd.h
index d7c240398225..9f1468ed7096 100644
--- a/sound/pci/asihpi/hpidspcd.h
+++ b/sound/pci/asihpi/hpidspcd.h
@@ -1,38 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/***********************************************************************/
-/**
+/*
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\file
Functions for reading DSP code to load into DSP
- hpi_dspcode_defines HPI DSP code loading method
-Define exactly one of these to select how the DSP code is supplied to
-the adapter.
-
-End users writing applications that use the HPI interface do not have to
-use any of the below defines; they are only necessary for building drivers
-
-HPI_DSPCODE_FILE:
-DSP code is supplied as a file that is opened and read from by the driver.
-
-HPI_DSPCODE_FIRMWARE:
-DSP code is read using the hotplug firmware loader module.
- Only valid when compiling the HPI kernel driver under Linux.
*/
/***********************************************************************/
#ifndef _HPIDSPCD_H_
@@ -40,37 +16,52 @@ DSP code is read using the hotplug firmware loader module.
#include "hpi_internal.h"
-#ifndef DISABLE_PRAGMA_PACK1
-#pragma pack(push, 1)
-#endif
+/** Header structure for dsp firmware file
+ This structure must match that used in s2bin.c for generation of asidsp.bin
+ */
+/*#ifndef DISABLE_PRAGMA_PACK1 */
+/*#pragma pack(push, 1) */
+/*#endif */
+struct code_header {
+ /** Size in bytes including header */
+ u32 size;
+ /** File type tag "CODE" == 0x45444F43 */
+ u32 type;
+ /** Adapter model number */
+ u32 adapter;
+ /** Firmware version*/
+ u32 version;
+ /** Data checksum */
+ u32 checksum;
+};
+/*#ifndef DISABLE_PRAGMA_PACK1 */
+/*#pragma pack(pop) */
+/*#endif */
+
+/*? Don't need the pragmas? */
+compile_time_assert((sizeof(struct code_header) == 20), code_header_size);
/** Descriptor for dspcode from firmware loader */
struct dsp_code {
- /** Firmware descriptor */
- const struct firmware *ps_firmware;
- struct pci_dev *ps_dev;
+ /** copy of file header */
+ struct code_header header;
/** Expected number of words in the whole dsp code,INCL header */
- long int block_length;
+ u32 block_length;
/** Number of words read so far */
- long int word_count;
- /** Version read from dsp code file */
- u32 version;
- /** CRC read from dsp code file */
- u32 crc;
-};
+ u32 word_count;
-#ifndef DISABLE_PRAGMA_PACK1
-#pragma pack(pop)
-#endif
+ /** internal state of DSP code reader */
+ struct dsp_code_private *pvt;
+};
-/** Prepare *psDspCode to refer to the requuested adapter.
- Searches the file, or selects the appropriate linked array
+/** Prepare *psDspCode to refer to the requested adapter's firmware.
+Code file name is obtained from HpiOs_GetDspCodePath
\return 0 for success, or error code if requested code is not available
*/
short hpi_dsp_code_open(
/** Code identifier, usually adapter family */
- u32 adapter,
+ u32 adapter, void *pci_dev,
/** Pointer to DSP code control structure */
struct dsp_code *ps_dsp_code,
/** Pointer to dword to receive OS specific error code */
@@ -87,7 +78,7 @@ void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code);
*/
short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code,
/**< DSP code descriptor */
- u32 *pword /**< where to store the read word */
+ u32 *pword /**< Where to store the read word */
);
/** Get a block of dsp code into an internal buffer, and provide a pointer to
diff --git a/sound/pci/asihpi/hpifunc.c b/sound/pci/asihpi/hpifunc.c
index 1e92eb6dd509..24047fafef51 100644
--- a/sound/pci/asihpi/hpifunc.c
+++ b/sound/pci/asihpi/hpifunc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
#include "hpi_internal.h"
#include "hpimsginit.h"
@@ -30,16 +31,25 @@ u32 hpi_indexes_to_handle(const char c_object, const u16 adapter_index,
return handle.w;
}
-void hpi_handle_to_indexes(const u32 handle, u16 *pw_adapter_index,
- u16 *pw_object_index)
+static u16 hpi_handle_indexes(const u32 h, u16 *p1, u16 *p2)
{
union handle_word uhandle;
- uhandle.w = handle;
+ if (!h)
+ return HPI_ERROR_INVALID_HANDLE;
+
+ uhandle.w = h;
+
+ *p1 = (u16)uhandle.h.adapter_index;
+ if (p2)
+ *p2 = (u16)uhandle.h.obj_index;
- if (pw_adapter_index)
- *pw_adapter_index = (u16)uhandle.h.adapter_index;
- if (pw_object_index)
- *pw_object_index = (u16)uhandle.h.obj_index;
+ return 0;
+}
+
+void hpi_handle_to_indexes(const u32 handle, u16 *pw_adapter_index,
+ u16 *pw_object_index)
+{
+ hpi_handle_indexes(handle, pw_adapter_index, pw_object_index);
}
char hpi_handle_object(const u32 handle)
@@ -49,22 +59,6 @@ char hpi_handle_object(const u32 handle)
return (char)uhandle.h.obj_type;
}
-#define u32TOINDEX(h, i1) \
-do {\
- if (h == 0) \
- return HPI_ERROR_INVALID_OBJ; \
- else \
- hpi_handle_to_indexes(h, i1, NULL); \
-} while (0)
-
-#define u32TOINDEXES(h, i1, i2) \
-do {\
- if (h == 0) \
- return HPI_ERROR_INVALID_OBJ; \
- else \
- hpi_handle_to_indexes(h, i1, i2);\
-} while (0)
-
void hpi_format_to_msg(struct hpi_msg_format *pMF,
const struct hpi_format *pF)
{
@@ -94,52 +88,13 @@ void hpi_stream_response_to_legacy(struct hpi_stream_res *pSR)
pSR->u.legacy_stream_info.state = pSR->u.stream_info.state;
}
-static struct hpi_hsubsys gh_subsys;
-
-struct hpi_hsubsys *hpi_subsys_create(void)
-{
- struct hpi_message hm;
- struct hpi_response hr;
-
- memset(&gh_subsys, 0, sizeof(struct hpi_hsubsys));
-
- {
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_OPEN);
- hpi_send_recv(&hm, &hr);
-
- if (hr.error == 0)
- return &gh_subsys;
-
- }
- return NULL;
-}
-
-void hpi_subsys_free(const struct hpi_hsubsys *ph_subsys)
+static inline void hpi_send_recvV1(struct hpi_message_header *m,
+ struct hpi_response_header *r)
{
- struct hpi_message hm;
- struct hpi_response hr;
-
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_CLOSE);
- hpi_send_recv(&hm, &hr);
-
+ hpi_send_recv((struct hpi_message *)m, (struct hpi_response *)r);
}
-u16 hpi_subsys_get_version(const struct hpi_hsubsys *ph_subsys, u32 *pversion)
-{
- struct hpi_message hm;
- struct hpi_response hr;
-
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_GET_VERSION);
- hpi_send_recv(&hm, &hr);
- *pversion = hr.u.s.version;
- return hr.error;
-}
-
-u16 hpi_subsys_get_version_ex(const struct hpi_hsubsys *ph_subsys,
- u32 *pversion_ex)
+u16 hpi_subsys_get_version_ex(u32 *pversion_ex)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -151,79 +106,7 @@ u16 hpi_subsys_get_version_ex(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_subsys_get_info(const struct hpi_hsubsys *ph_subsys, u32 *pversion,
- u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_GET_INFO);
-
- hpi_send_recv(&hm, &hr);
-
- *pversion = hr.u.s.version;
- if (list_length > HPI_MAX_ADAPTERS)
- memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list,
- HPI_MAX_ADAPTERS);
- else
- memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list, list_length);
- *pw_num_adapters = hr.u.s.num_adapters;
- return hr.error;
-}
-
-u16 hpi_subsys_find_adapters(const struct hpi_hsubsys *ph_subsys,
- u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_FIND_ADAPTERS);
-
- hpi_send_recv(&hm, &hr);
-
- if (list_length > HPI_MAX_ADAPTERS) {
- memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list,
- HPI_MAX_ADAPTERS * sizeof(u16));
- memset(&aw_adapter_list[HPI_MAX_ADAPTERS], 0,
- (list_length - HPI_MAX_ADAPTERS) * sizeof(u16));
- } else
- memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list,
- list_length * sizeof(u16));
- *pw_num_adapters = hr.u.s.num_adapters;
-
- return hr.error;
-}
-
-u16 hpi_subsys_create_adapter(const struct hpi_hsubsys *ph_subsys,
- const struct hpi_resource *p_resource, u16 *pw_adapter_index)
-{
- struct hpi_message hm;
- struct hpi_response hr;
-
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_CREATE_ADAPTER);
- hm.u.s.resource = *p_resource;
-
- hpi_send_recv(&hm, &hr);
-
- *pw_adapter_index = hr.u.s.adapter_index;
- return hr.error;
-}
-
-u16 hpi_subsys_delete_adapter(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_DELETE_ADAPTER);
- hm.adapter_index = adapter_index;
- hpi_send_recv(&hm, &hr);
- return hr.error;
-}
-
-u16 hpi_subsys_get_num_adapters(const struct hpi_hsubsys *ph_subsys,
- int *pn_num_adapters)
+u16 hpi_subsys_get_num_adapters(int *pn_num_adapters)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -234,35 +117,22 @@ u16 hpi_subsys_get_num_adapters(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_subsys_get_adapter(const struct hpi_hsubsys *ph_subsys, int iterator,
- u32 *padapter_index, u16 *pw_adapter_type)
+u16 hpi_subsys_get_adapter(int iterator, u32 *padapter_index,
+ u16 *pw_adapter_type)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_GET_ADAPTER);
- hm.adapter_index = (u16)iterator;
+ hm.obj_index = (u16)iterator;
hpi_send_recv(&hm, &hr);
*padapter_index = (int)hr.u.s.adapter_index;
- *pw_adapter_type = hr.u.s.aw_adapter_list[0];
- return hr.error;
-}
+ *pw_adapter_type = hr.u.s.adapter_type;
-u16 hpi_subsys_set_host_network_interface(const struct hpi_hsubsys *ph_subsys,
- const char *sz_interface)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_SET_NETWORK_INTERFACE);
- if (sz_interface == NULL)
- return HPI_ERROR_INVALID_RESOURCE;
- hm.u.s.resource.r.net_if = sz_interface;
- hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_adapter_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index)
+u16 hpi_adapter_open(u16 adapter_index)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -276,7 +146,7 @@ u16 hpi_adapter_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index)
}
-u16 hpi_adapter_close(const struct hpi_hsubsys *ph_subsys, u16 adapter_index)
+u16 hpi_adapter_close(u16 adapter_index)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -289,15 +159,14 @@ u16 hpi_adapter_close(const struct hpi_hsubsys *ph_subsys, u16 adapter_index)
return hr.error;
}
-u16 hpi_adapter_set_mode(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 adapter_mode)
+u16 hpi_adapter_set_mode(u16 adapter_index, u32 adapter_mode)
{
- return hpi_adapter_set_mode_ex(ph_subsys, adapter_index, adapter_mode,
+ return hpi_adapter_set_mode_ex(adapter_index, adapter_mode,
HPI_ADAPTER_MODE_SET);
}
-u16 hpi_adapter_set_mode_ex(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 adapter_mode, u16 query_or_set)
+u16 hpi_adapter_set_mode_ex(u16 adapter_index, u32 adapter_mode,
+ u16 query_or_set)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -305,14 +174,13 @@ u16 hpi_adapter_set_mode_ex(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
HPI_ADAPTER_SET_MODE);
hm.adapter_index = adapter_index;
- hm.u.a.adapter_mode = adapter_mode;
- hm.u.a.assert_id = query_or_set;
+ hm.u.ax.mode.adapter_mode = adapter_mode;
+ hm.u.ax.mode.query_or_set = query_or_set;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_adapter_get_mode(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 *padapter_mode)
+u16 hpi_adapter_get_mode(u16 adapter_index, u32 *padapter_mode)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -321,13 +189,13 @@ u16 hpi_adapter_get_mode(const struct hpi_hsubsys *ph_subsys,
hm.adapter_index = adapter_index;
hpi_send_recv(&hm, &hr);
if (padapter_mode)
- *padapter_mode = hr.u.a.serial_number;
+ *padapter_mode = hr.u.ax.mode.adapter_mode;
return hr.error;
}
-u16 hpi_adapter_get_info(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 *pw_num_outstreams, u16 *pw_num_instreams,
- u16 *pw_version, u32 *pserial_number, u16 *pw_adapter_type)
+u16 hpi_adapter_get_info(u16 adapter_index, u16 *pw_num_outstreams,
+ u16 *pw_num_instreams, u16 *pw_version, u32 *pserial_number,
+ u16 *pw_adapter_type)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -337,18 +205,17 @@ u16 hpi_adapter_get_info(const struct hpi_hsubsys *ph_subsys,
hpi_send_recv(&hm, &hr);
- *pw_adapter_type = hr.u.a.adapter_type;
- *pw_num_outstreams = hr.u.a.num_outstreams;
- *pw_num_instreams = hr.u.a.num_instreams;
- *pw_version = hr.u.a.version;
- *pserial_number = hr.u.a.serial_number;
+ *pw_adapter_type = hr.u.ax.info.adapter_type;
+ *pw_num_outstreams = hr.u.ax.info.num_outstreams;
+ *pw_num_instreams = hr.u.ax.info.num_instreams;
+ *pw_version = hr.u.ax.info.version;
+ *pserial_number = hr.u.ax.info.serial_number;
return hr.error;
}
-u16 hpi_adapter_get_module_by_index(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 module_index, u16 *pw_num_outputs,
- u16 *pw_num_inputs, u16 *pw_version, u32 *pserial_number,
- u16 *pw_module_type, u32 *ph_module)
+u16 hpi_adapter_get_module_by_index(u16 adapter_index, u16 module_index,
+ u16 *pw_num_outputs, u16 *pw_num_inputs, u16 *pw_version,
+ u32 *pserial_number, u16 *pw_module_type, u32 *ph_module)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -360,173 +227,18 @@ u16 hpi_adapter_get_module_by_index(const struct hpi_hsubsys *ph_subsys,
hpi_send_recv(&hm, &hr);
- *pw_module_type = hr.u.a.adapter_type;
- *pw_num_outputs = hr.u.a.num_outstreams;
- *pw_num_inputs = hr.u.a.num_instreams;
- *pw_version = hr.u.a.version;
- *pserial_number = hr.u.a.serial_number;
+ *pw_module_type = hr.u.ax.info.adapter_type;
+ *pw_num_outputs = hr.u.ax.info.num_outstreams;
+ *pw_num_inputs = hr.u.ax.info.num_instreams;
+ *pw_version = hr.u.ax.info.version;
+ *pserial_number = hr.u.ax.info.serial_number;
*ph_module = 0;
return hr.error;
}
-u16 hpi_adapter_get_assert(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 *assert_present, char *psz_assert,
- u16 *pw_line_number)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_GET_ASSERT);
- hm.adapter_index = adapter_index;
- hpi_send_recv(&hm, &hr);
-
- *assert_present = 0;
-
- if (!hr.error) {
-
- *pw_line_number = (u16)hr.u.a.serial_number;
- if (*pw_line_number) {
-
- int i;
- char *src = (char *)hr.u.a.sz_adapter_assert;
- char *dst = psz_assert;
-
- *assert_present = 1;
-
- for (i = 0; i < HPI_STRING_LEN; i++) {
- char c;
- c = *src++;
- *dst++ = c;
- if (c == 0)
- break;
- }
-
- }
- }
- return hr.error;
-}
-
-u16 hpi_adapter_get_assert_ex(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 *assert_present, char *psz_assert,
- u32 *pline_number, u16 *pw_assert_on_dsp)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_GET_ASSERT);
- hm.adapter_index = adapter_index;
-
- hpi_send_recv(&hm, &hr);
-
- *assert_present = 0;
-
- if (!hr.error) {
-
- *pline_number = hr.u.a.serial_number;
-
- *assert_present = hr.u.a.adapter_type;
-
- *pw_assert_on_dsp = hr.u.a.adapter_index;
-
- if (!*assert_present && *pline_number)
-
- *assert_present = 1;
-
- if (*assert_present) {
-
- int i;
- char *src = (char *)hr.u.a.sz_adapter_assert;
- char *dst = psz_assert;
-
- for (i = 0; i < HPI_STRING_LEN; i++) {
- char c;
- c = *src++;
- *dst++ = c;
- if (c == 0)
- break;
- }
-
- } else {
- *psz_assert = 0;
- }
- }
- return hr.error;
-}
-
-u16 hpi_adapter_test_assert(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 assert_id)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_TEST_ASSERT);
- hm.adapter_index = adapter_index;
- hm.u.a.assert_id = assert_id;
-
- hpi_send_recv(&hm, &hr);
-
- return hr.error;
-}
-
-u16 hpi_adapter_enable_capability(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 capability, u32 key)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_ENABLE_CAPABILITY);
- hm.adapter_index = adapter_index;
- hm.u.a.assert_id = capability;
- hm.u.a.adapter_mode = key;
-
- hpi_send_recv(&hm, &hr);
-
- return hr.error;
-}
-
-u16 hpi_adapter_self_test(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_SELFTEST);
- hm.adapter_index = adapter_index;
- hpi_send_recv(&hm, &hr);
- return hr.error;
-}
-
-u16 hpi_adapter_debug_read(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 dsp_address, char *p_buffer, int *count_bytes)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_DEBUG_READ);
-
- hr.size = sizeof(hr);
-
- hm.adapter_index = adapter_index;
- hm.u.ax.debug_read.dsp_address = dsp_address;
-
- if (*count_bytes > (int)sizeof(hr.u.bytes))
- *count_bytes = sizeof(hr.u.bytes);
-
- hm.u.ax.debug_read.count_bytes = *count_bytes;
-
- hpi_send_recv(&hm, &hr);
-
- if (!hr.error) {
- *count_bytes = hr.size - 12;
- memcpy(p_buffer, &hr.u.bytes, *count_bytes);
- } else
- *count_bytes = 0;
- return hr.error;
-}
-
-u16 hpi_adapter_set_property(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 property, u16 parameter1, u16 parameter2)
+u16 hpi_adapter_set_property(u16 adapter_index, u16 property, u16 parameter1,
+ u16 parameter2)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -542,9 +254,8 @@ u16 hpi_adapter_set_property(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_adapter_get_property(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 property, u16 *pw_parameter1,
- u16 *pw_parameter2)
+u16 hpi_adapter_get_property(u16 adapter_index, u16 property,
+ u16 *pw_parameter1, u16 *pw_parameter2)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -564,9 +275,8 @@ u16 hpi_adapter_get_property(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_adapter_enumerate_property(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 index, u16 what_to_enumerate,
- u16 property_index, u32 *psetting)
+u16 hpi_adapter_enumerate_property(u16 adapter_index, u16 index,
+ u16 what_to_enumerate, u16 property_index, u32 *psetting)
{
return 0;
}
@@ -574,7 +284,7 @@ u16 hpi_adapter_enumerate_property(const struct hpi_hsubsys *ph_subsys,
u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
u32 sample_rate, u32 bit_rate, u32 attributes)
{
- u16 error = 0;
+ u16 err = 0;
struct hpi_msg_format fmt;
switch (channels) {
@@ -586,8 +296,8 @@ u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
case 16:
break;
default:
- error = HPI_ERROR_INVALID_CHANNELS;
- return error;
+ err = HPI_ERROR_INVALID_CHANNELS;
+ return err;
}
fmt.channels = channels;
@@ -610,17 +320,17 @@ u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
case HPI_FORMAT_OEM2:
break;
default:
- error = HPI_ERROR_INVALID_FORMAT;
- return error;
+ err = HPI_ERROR_INVALID_FORMAT;
+ return err;
}
fmt.format = format;
if (sample_rate < 8000L) {
- error = HPI_ERROR_INCOMPATIBLE_SAMPLERATE;
+ err = HPI_ERROR_INCOMPATIBLE_SAMPLERATE;
sample_rate = 8000L;
}
if (sample_rate > 200000L) {
- error = HPI_ERROR_INCOMPATIBLE_SAMPLERATE;
+ err = HPI_ERROR_INCOMPATIBLE_SAMPLERATE;
sample_rate = 200000L;
}
fmt.sample_rate = sample_rate;
@@ -651,10 +361,10 @@ u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
if ((channels == 1)
&& (attributes != HPI_MPEG_MODE_DEFAULT)) {
attributes = HPI_MPEG_MODE_DEFAULT;
- error = HPI_ERROR_INVALID_FORMAT;
+ err = HPI_ERROR_INVALID_FORMAT;
} else if (attributes > HPI_MPEG_MODE_DUALCHANNEL) {
attributes = HPI_MPEG_MODE_DEFAULT;
- error = HPI_ERROR_INVALID_FORMAT;
+ err = HPI_ERROR_INVALID_FORMAT;
}
fmt.attributes = attributes;
break;
@@ -663,7 +373,7 @@ u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format,
}
hpi_msg_to_format(p_format, &fmt);
- return error;
+ return err;
}
u16 hpi_stream_estimate_buffer_size(struct hpi_format *p_format,
@@ -712,8 +422,8 @@ u16 hpi_stream_estimate_buffer_size(struct hpi_format *p_format,
return 0;
}
-u16 hpi_outstream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u16 outstream_index, u32 *ph_outstream)
+u16 hpi_outstream_open(u16 adapter_index, u16 outstream_index,
+ u32 *ph_outstream)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -733,38 +443,41 @@ u16 hpi_outstream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
return hr.error;
}
-u16 hpi_outstream_close(const struct hpi_hsubsys *ph_subsys, u32 h_outstream)
+u16 hpi_outstream_close(u32 h_outstream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_HOSTBUFFER_FREE);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
+
hpi_send_recv(&hm, &hr);
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_GROUP_RESET);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index);
hpi_send_recv(&hm, &hr);
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_CLOSE);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index);
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_outstream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_to_play,
- u32 *psamples_played, u32 *pauxiliary_data_to_play)
+u16 hpi_outstream_get_info_ex(u32 h_outstream, u16 *pw_state,
+ u32 *pbuffer_size, u32 *pdata_to_play, u32 *psamples_played,
+ u32 *pauxiliary_data_to_play)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_GET_INFO);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
@@ -782,15 +495,15 @@ u16 hpi_outstream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_write_buf(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, const u8 *pb_data, u32 bytes_to_write,
- const struct hpi_format *p_format)
+u16 hpi_outstream_write_buf(u32 h_outstream, const u8 *pb_data,
+ u32 bytes_to_write, const struct hpi_format *p_format)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_WRITE);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.data.pb_data = (u8 *)pb_data;
hm.u.d.u.data.data_size = bytes_to_write;
@@ -801,82 +514,85 @@ u16 hpi_outstream_write_buf(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_start(const struct hpi_hsubsys *ph_subsys, u32 h_outstream)
+u16 hpi_outstream_start(u32 h_outstream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_START);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_outstream_wait_start(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream)
+u16 hpi_outstream_wait_start(u32 h_outstream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_WAIT_START);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_outstream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_outstream)
+u16 hpi_outstream_stop(u32 h_outstream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_STOP);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_outstream_sinegen(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream)
+u16 hpi_outstream_sinegen(u32 h_outstream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_SINEGEN);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_outstream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_outstream)
+u16 hpi_outstream_reset(u32 h_outstream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_RESET);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_outstream_query_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, struct hpi_format *p_format)
+u16 hpi_outstream_query_format(u32 h_outstream, struct hpi_format *p_format)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_QUERY_FORMAT);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_format_to_msg(&hm.u.d.u.data.format, p_format);
@@ -885,15 +601,15 @@ u16 hpi_outstream_query_format(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_set_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, struct hpi_format *p_format)
+u16 hpi_outstream_set_format(u32 h_outstream, struct hpi_format *p_format)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_SET_FORMAT);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_format_to_msg(&hm.u.d.u.data.format, p_format);
@@ -902,15 +618,15 @@ u16 hpi_outstream_set_format(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_set_velocity(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, short velocity)
+u16 hpi_outstream_set_velocity(u32 h_outstream, short velocity)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_SET_VELOCITY);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.velocity = velocity;
hpi_send_recv(&hm, &hr);
@@ -918,15 +634,16 @@ u16 hpi_outstream_set_velocity(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_set_punch_in_out(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 punch_in_sample, u32 punch_out_sample)
+u16 hpi_outstream_set_punch_in_out(u32 h_outstream, u32 punch_in_sample,
+ u32 punch_out_sample)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_SET_PUNCHINOUT);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.pio.punch_in_sample = punch_in_sample;
hm.u.d.u.pio.punch_out_sample = punch_out_sample;
@@ -936,29 +653,29 @@ u16 hpi_outstream_set_punch_in_out(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_ancillary_reset(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u16 mode)
+u16 hpi_outstream_ancillary_reset(u32 h_outstream, u16 mode)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_ANC_RESET);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.data.format.channels = mode;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_outstream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 *pframes_available)
+u16 hpi_outstream_ancillary_get_info(u32 h_outstream, u32 *pframes_available)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_ANC_GET_INFO);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
if (hr.error == 0) {
if (pframes_available)
@@ -969,8 +686,8 @@ u16 hpi_outstream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, struct hpi_anc_frame *p_anc_frame_buffer,
+u16 hpi_outstream_ancillary_read(u32 h_outstream,
+ struct hpi_anc_frame *p_anc_frame_buffer,
u32 anc_frame_buffer_size_in_bytes,
u32 number_of_ancillary_frames_to_read)
{
@@ -979,7 +696,8 @@ u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_ANC_READ);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.data.pb_data = (u8 *)p_anc_frame_buffer;
hm.u.d.u.data.data_size =
number_of_ancillary_frames_to_read *
@@ -987,19 +705,19 @@ u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys,
if (hm.u.d.u.data.data_size <= anc_frame_buffer_size_in_bytes)
hpi_send_recv(&hm, &hr);
else
- hr.error = HPI_ERROR_INVALID_DATA_TRANSFER;
+ hr.error = HPI_ERROR_INVALID_DATASIZE;
return hr.error;
}
-u16 hpi_outstream_set_time_scale(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 time_scale)
+u16 hpi_outstream_set_time_scale(u32 h_outstream, u32 time_scale)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_SET_TIMESCALE);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.time_scale = time_scale;
@@ -1008,22 +726,21 @@ u16 hpi_outstream_set_time_scale(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 size_in_bytes)
+u16 hpi_outstream_host_buffer_allocate(u32 h_outstream, u32 size_in_bytes)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_HOSTBUFFER_ALLOC);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.data.data_size = size_in_bytes;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u8 **pp_buffer,
+u16 hpi_outstream_host_buffer_get_info(u32 h_outstream, u8 **pp_buffer,
struct hpi_hostbuffer_status **pp_status)
{
struct hpi_message hm;
@@ -1031,7 +748,8 @@ u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_HOSTBUFFER_GET_INFO);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
if (hr.error == 0) {
@@ -1043,21 +761,20 @@ u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_host_buffer_free(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream)
+u16 hpi_outstream_host_buffer_free(u32 h_outstream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_HOSTBUFFER_FREE);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 h_stream)
+u16 hpi_outstream_group_add(u32 h_outstream, u32 h_stream)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -1066,22 +783,22 @@ u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_GROUP_ADD);
- hr.error = 0;
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
+
+ if (hpi_handle_indexes(h_stream, &adapter,
+ &hm.u.d.u.stream.stream_index))
+ return HPI_ERROR_INVALID_HANDLE;
+
c_obj_type = hpi_handle_object(h_stream);
switch (c_obj_type) {
case HPI_OBJ_OSTREAM:
- hm.u.d.u.stream.object_type = HPI_OBJ_OSTREAM;
- u32TOINDEXES(h_stream, &adapter,
- &hm.u.d.u.stream.stream_index);
- break;
case HPI_OBJ_ISTREAM:
- hm.u.d.u.stream.object_type = HPI_OBJ_ISTREAM;
- u32TOINDEXES(h_stream, &adapter,
- &hm.u.d.u.stream.stream_index);
+ hm.u.d.u.stream.object_type = c_obj_type;
break;
default:
- return HPI_ERROR_INVALID_STREAM;
+ return HPI_ERROR_INVALID_OBJ;
}
if (adapter != hm.adapter_index)
return HPI_ERROR_NO_INTERADAPTER_GROUPS;
@@ -1090,15 +807,16 @@ u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_group_get_map(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream, u32 *poutstream_map, u32 *pinstream_map)
+u16 hpi_outstream_group_get_map(u32 h_outstream, u32 *poutstream_map,
+ u32 *pinstream_map)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_GROUP_GETMAP);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
if (poutstream_map)
@@ -1109,21 +827,20 @@ u16 hpi_outstream_group_get_map(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_outstream_group_reset(const struct hpi_hsubsys *ph_subsys,
- u32 h_outstream)
+u16 hpi_outstream_group_reset(u32 h_outstream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_GROUP_RESET);
- u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_instream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u16 instream_index, u32 *ph_instream)
+u16 hpi_instream_open(u16 adapter_index, u16 instream_index, u32 *ph_instream)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -1145,38 +862,40 @@ u16 hpi_instream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
return hr.error;
}
-u16 hpi_instream_close(const struct hpi_hsubsys *ph_subsys, u32 h_instream)
+u16 hpi_instream_close(u32 h_instream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_HOSTBUFFER_FREE);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_GROUP_RESET);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index);
hpi_send_recv(&hm, &hr);
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_CLOSE);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index);
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_instream_query_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, const struct hpi_format *p_format)
+u16 hpi_instream_query_format(u32 h_instream,
+ const struct hpi_format *p_format)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_QUERY_FORMAT);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_format_to_msg(&hm.u.d.u.data.format, p_format);
hpi_send_recv(&hm, &hr);
@@ -1184,15 +903,15 @@ u16 hpi_instream_query_format(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_instream_set_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, const struct hpi_format *p_format)
+u16 hpi_instream_set_format(u32 h_instream, const struct hpi_format *p_format)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_SET_FORMAT);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_format_to_msg(&hm.u.d.u.data.format, p_format);
hpi_send_recv(&hm, &hr);
@@ -1200,15 +919,15 @@ u16 hpi_instream_set_format(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_instream_read_buf(const struct hpi_hsubsys *ph_subsys, u32 h_instream,
- u8 *pb_data, u32 bytes_to_read)
+u16 hpi_instream_read_buf(u32 h_instream, u8 *pb_data, u32 bytes_to_read)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_READ);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.data.data_size = bytes_to_read;
hm.u.d.u.data.pb_data = pb_data;
@@ -1217,72 +936,76 @@ u16 hpi_instream_read_buf(const struct hpi_hsubsys *ph_subsys, u32 h_instream,
return hr.error;
}
-u16 hpi_instream_start(const struct hpi_hsubsys *ph_subsys, u32 h_instream)
+u16 hpi_instream_start(u32 h_instream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_START);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_instream_wait_start(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream)
+u16 hpi_instream_wait_start(u32 h_instream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_WAIT_START);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_instream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_instream)
+u16 hpi_instream_stop(u32 h_instream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_STOP);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_instream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_instream)
+u16 hpi_instream_reset(u32 h_instream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_RESET);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_instream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_recorded,
- u32 *psamples_recorded, u32 *pauxiliary_data_recorded)
+u16 hpi_instream_get_info_ex(u32 h_instream, u16 *pw_state, u32 *pbuffer_size,
+ u32 *pdata_recorded, u32 *psamples_recorded,
+ u32 *pauxiliary_data_recorded)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_GET_INFO);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
@@ -1300,15 +1023,15 @@ u16 hpi_instream_get_info_ex(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_instream_ancillary_reset(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u16 bytes_per_frame, u16 mode, u16 alignment,
- u16 idle_bit)
+u16 hpi_instream_ancillary_reset(u32 h_instream, u16 bytes_per_frame,
+ u16 mode, u16 alignment, u16 idle_bit)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_ANC_RESET);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.data.format.attributes = bytes_per_frame;
hm.u.d.u.data.format.format = (mode << 8) | (alignment & 0xff);
hm.u.d.u.data.format.channels = idle_bit;
@@ -1316,14 +1039,14 @@ u16 hpi_instream_ancillary_reset(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_instream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u32 *pframe_space)
+u16 hpi_instream_ancillary_get_info(u32 h_instream, u32 *pframe_space)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_ANC_GET_INFO);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
if (pframe_space)
*pframe_space =
@@ -1333,8 +1056,8 @@ u16 hpi_instream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, const struct hpi_anc_frame *p_anc_frame_buffer,
+u16 hpi_instream_ancillary_write(u32 h_instream,
+ const struct hpi_anc_frame *p_anc_frame_buffer,
u32 anc_frame_buffer_size_in_bytes,
u32 number_of_ancillary_frames_to_write)
{
@@ -1343,7 +1066,8 @@ u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_ANC_WRITE);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.data.pb_data = (u8 *)p_anc_frame_buffer;
hm.u.d.u.data.data_size =
number_of_ancillary_frames_to_write *
@@ -1351,12 +1075,11 @@ u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys,
if (hm.u.d.u.data.data_size <= anc_frame_buffer_size_in_bytes)
hpi_send_recv(&hm, &hr);
else
- hr.error = HPI_ERROR_INVALID_DATA_TRANSFER;
+ hr.error = HPI_ERROR_INVALID_DATASIZE;
return hr.error;
}
-u16 hpi_instream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u32 size_in_bytes)
+u16 hpi_instream_host_buffer_allocate(u32 h_instream, u32 size_in_bytes)
{
struct hpi_message hm;
@@ -1364,14 +1087,14 @@ u16 hpi_instream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_HOSTBUFFER_ALLOC);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.d.u.data.data_size = size_in_bytes;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u8 **pp_buffer,
+u16 hpi_instream_host_buffer_get_info(u32 h_instream, u8 **pp_buffer,
struct hpi_hostbuffer_status **pp_status)
{
struct hpi_message hm;
@@ -1379,7 +1102,8 @@ u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_HOSTBUFFER_GET_INFO);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
if (hr.error == 0) {
@@ -1391,8 +1115,7 @@ u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_instream_host_buffer_free(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream)
+u16 hpi_instream_host_buffer_free(u32 h_instream)
{
struct hpi_message hm;
@@ -1400,13 +1123,13 @@ u16 hpi_instream_host_buffer_free(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_HOSTBUFFER_FREE);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u32 h_stream)
+u16 hpi_instream_group_add(u32 h_instream, u32 h_stream)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -1416,22 +1139,23 @@ u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_GROUP_ADD);
hr.error = 0;
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
+
+ if (hpi_handle_indexes(h_stream, &adapter,
+ &hm.u.d.u.stream.stream_index))
+ return HPI_ERROR_INVALID_HANDLE;
+
c_obj_type = hpi_handle_object(h_stream);
switch (c_obj_type) {
case HPI_OBJ_OSTREAM:
- hm.u.d.u.stream.object_type = HPI_OBJ_OSTREAM;
- u32TOINDEXES(h_stream, &adapter,
- &hm.u.d.u.stream.stream_index);
- break;
case HPI_OBJ_ISTREAM:
- hm.u.d.u.stream.object_type = HPI_OBJ_ISTREAM;
- u32TOINDEXES(h_stream, &adapter,
- &hm.u.d.u.stream.stream_index);
+ hm.u.d.u.stream.object_type = c_obj_type;
break;
default:
- return HPI_ERROR_INVALID_STREAM;
+ return HPI_ERROR_INVALID_OBJ;
}
if (adapter != hm.adapter_index)
@@ -1441,15 +1165,16 @@ u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_instream_group_get_map(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream, u32 *poutstream_map, u32 *pinstream_map)
+u16 hpi_instream_group_get_map(u32 h_instream, u32 *poutstream_map,
+ u32 *pinstream_map)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_HOSTBUFFER_FREE);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
if (poutstream_map)
@@ -1460,21 +1185,20 @@ u16 hpi_instream_group_get_map(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_instream_group_reset(const struct hpi_hsubsys *ph_subsys,
- u32 h_instream)
+u16 hpi_instream_group_reset(u32 h_instream)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_GROUP_RESET);
- u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_mixer_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u32 *ph_mixer)
+u16 hpi_mixer_open(u16 adapter_index, u32 *ph_mixer)
{
struct hpi_message hm;
struct hpi_response hr;
@@ -1492,25 +1216,29 @@ u16 hpi_mixer_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
return hr.error;
}
-u16 hpi_mixer_close(const struct hpi_hsubsys *ph_subsys, u32 h_mixer)
+u16 hpi_mixer_close(u32 h_mixer)
{
struct hpi_message hm;
struct hpi_response hr;
+
hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_CLOSE);
- u32TOINDEX(h_mixer, &hm.adapter_index);
+ if (hpi_handle_indexes(h_mixer, &hm.adapter_index, NULL))
+ return HPI_ERROR_INVALID_HANDLE;
+
hpi_send_recv(&hm, &hr);
return hr.error;
}
-u16 hpi_mixer_get_control(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
- u16 src_node_type, u16 src_node_type_index, u16 dst_node_type,
- u16 dst_node_type_index, u16 control_type, u32 *ph_control)
+u16 hpi_mixer_get_control(u32 h_mixer, u16 src_node_type,
+ u16 src_node_type_index, u16 dst_node_type, u16 dst_node_type_index,
+ u16 control_type, u32 *ph_control)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER,
HPI_MIXER_GET_CONTROL);
- u32TOINDEX(h_mixer, &hm.adapter_index);
+ if (hpi_handle_indexes(h_mixer, &hm.adapter_index, NULL))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.m.node_type1 = src_node_type;
hm.u.m.node_index1 = src_node_type_index;
hm.u.m.node_type2 = dst_node_type;
@@ -1528,16 +1256,16 @@ u16 hpi_mixer_get_control(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
return hr.error;
}
-u16 hpi_mixer_get_control_by_index(const struct hpi_hsubsys *ph_subsys,
- u32 h_mixer, u16 control_index, u16 *pw_src_node_type,
- u16 *pw_src_node_index, u16 *pw_dst_node_type, u16 *pw_dst_node_index,
- u16 *pw_control_type, u32 *ph_control)
+u16 hpi_mixer_get_control_by_index(u32 h_mixer, u16 control_index,
+ u16 *pw_src_node_type, u16 *pw_src_node_index, u16 *pw_dst_node_type,
+ u16 *pw_dst_node_index, u16 *pw_control_type, u32 *ph_control)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER,
HPI_MIXER_GET_CONTROL_BY_INDEX);
- u32TOINDEX(h_mixer, &hm.adapter_index);
+ if (hpi_handle_indexes(h_mixer, &hm.adapter_index, NULL))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.m.control_index = control_index;
hpi_send_recv(&hm, &hr);
@@ -1562,13 +1290,14 @@ u16 hpi_mixer_get_control_by_index(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_mixer_store(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
- enum HPI_MIXER_STORE_COMMAND command, u16 index)
+u16 hpi_mixer_store(u32 h_mixer, enum HPI_MIXER_STORE_COMMAND command,
+ u16 index)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_STORE);
- u32TOINDEX(h_mixer, &hm.adapter_index);
+ if (hpi_handle_indexes(h_mixer, &hm.adapter_index, NULL))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.mx.store.command = command;
hm.u.mx.store.index = index;
hpi_send_recv(&hm, &hr);
@@ -1576,16 +1305,16 @@ u16 hpi_mixer_store(const struct hpi_hsubsys *ph_subsys, u32 h_mixer,
}
static
-u16 hpi_control_param_set(const struct hpi_hsubsys *ph_subsys,
- const u32 h_control, const u16 attrib, const u32 param1,
- const u32 param2)
+u16 hpi_control_param_set(const u32 h_control, const u16 attrib,
+ const u32 param1, const u32 param2)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_SET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = attrib;
hm.u.c.param1 = param1;
hm.u.c.param2 = param2;
@@ -1601,7 +1330,8 @@ static u16 hpi_control_log_set2(u32 h_control, u16 attrib, short sv0,
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_SET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = attrib;
hm.u.c.an_log_value[0] = sv0;
hm.u.c.an_log_value[1] = sv1;
@@ -1610,16 +1340,16 @@ static u16 hpi_control_log_set2(u32 h_control, u16 attrib, short sv0,
}
static
-u16 hpi_control_param_get(const struct hpi_hsubsys *ph_subsys,
- const u32 h_control, const u16 attrib, u32 param1, u32 param2,
- u32 *pparam1, u32 *pparam2)
+u16 hpi_control_param_get(const u32 h_control, const u16 attrib, u32 param1,
+ u32 param2, u32 *pparam1, u32 *pparam2)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = attrib;
hm.u.c.param1 = param1;
hm.u.c.param2 = param2;
@@ -1632,19 +1362,20 @@ u16 hpi_control_param_get(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-#define hpi_control_param1_get(s, h, a, p1) \
- hpi_control_param_get(s, h, a, 0, 0, p1, NULL)
-#define hpi_control_param2_get(s, h, a, p1, p2) \
- hpi_control_param_get(s, h, a, 0, 0, p1, p2)
+#define hpi_control_param1_get(h, a, p1) \
+ hpi_control_param_get(h, a, 0, 0, p1, NULL)
+#define hpi_control_param2_get(h, a, p1, p2) \
+ hpi_control_param_get(h, a, 0, 0, p1, p2)
-static u16 hpi_control_log_get2(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 attrib, short *sv0, short *sv1)
+static u16 hpi_control_log_get2(u32 h_control, u16 attrib, short *sv0,
+ short *sv1)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = attrib;
hpi_send_recv(&hm, &hr);
@@ -1655,8 +1386,7 @@ static u16 hpi_control_log_get2(const struct hpi_hsubsys *ph_subsys,
}
static
-u16 hpi_control_query(const struct hpi_hsubsys *ph_subsys,
- const u32 h_control, const u16 attrib, const u32 index,
+u16 hpi_control_query(const u32 h_control, const u16 attrib, const u32 index,
const u32 param, u32 *psetting)
{
struct hpi_message hm;
@@ -1664,7 +1394,8 @@ u16 hpi_control_query(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_INFO);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = attrib;
hm.u.c.param1 = index;
@@ -1682,7 +1413,7 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute,
unsigned int sub_string_index = 0, j = 0;
char c = 0;
unsigned int n = 0;
- u16 hE = 0;
+ u16 err = 0;
if ((string_length < 1) || (string_length > 256))
return HPI_ERROR_INVALID_CONTROL_VALUE;
@@ -1693,7 +1424,9 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute,
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index,
+ &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = attribute;
hm.u.c.param1 = sub_string_index;
hm.u.c.param2 = 0;
@@ -1705,7 +1438,7 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute,
return HPI_ERROR_INVALID_CONTROL_VALUE;
if (hr.error) {
- hE = hr.error;
+ err = hr.error;
break;
}
for (j = 0; j < 8; j++) {
@@ -1714,7 +1447,7 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute,
n++;
if (n >= string_length) {
psz_string[string_length - 1] = 0;
- hE = HPI_ERROR_INVALID_CONTROL_VALUE;
+ err = HPI_ERROR_INVALID_CONTROL_VALUE;
break;
}
if (c == 0)
@@ -1730,57 +1463,52 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute,
if (c == 0)
break;
}
- return hE;
+ return err;
}
-u16 HPI_AESEBU__receiver_query_format(const struct hpi_hsubsys *ph_subsys,
- const u32 h_aes_rx, const u32 index, u16 *pw_format)
+u16 hpi_aesebu_receiver_query_format(const u32 h_aes_rx, const u32 index,
+ u16 *pw_format)
{
u32 qr;
u16 err;
- err = hpi_control_query(ph_subsys, h_aes_rx, HPI_AESEBURX_FORMAT,
- index, 0, &qr);
+ err = hpi_control_query(h_aes_rx, HPI_AESEBURX_FORMAT, index, 0, &qr);
*pw_format = (u16)qr;
return err;
}
-u16 HPI_AESEBU__receiver_set_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 format)
+u16 hpi_aesebu_receiver_set_format(u32 h_control, u16 format)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_AESEBURX_FORMAT, format, 0);
+ return hpi_control_param_set(h_control, HPI_AESEBURX_FORMAT, format,
+ 0);
}
-u16 HPI_AESEBU__receiver_get_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_format)
+u16 hpi_aesebu_receiver_get_format(u32 h_control, u16 *pw_format)
{
u16 err;
u32 param;
- err = hpi_control_param1_get(ph_subsys, h_control,
- HPI_AESEBURX_FORMAT, &param);
+ err = hpi_control_param1_get(h_control, HPI_AESEBURX_FORMAT, &param);
if (!err && pw_format)
*pw_format = (u16)param;
return err;
}
-u16 HPI_AESEBU__receiver_get_sample_rate(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *psample_rate)
+u16 hpi_aesebu_receiver_get_sample_rate(u32 h_control, u32 *psample_rate)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_AESEBURX_SAMPLERATE, psample_rate);
+ return hpi_control_param1_get(h_control, HPI_AESEBURX_SAMPLERATE,
+ psample_rate);
}
-u16 HPI_AESEBU__receiver_get_user_data(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 *pw_data)
+u16 hpi_aesebu_receiver_get_user_data(u32 h_control, u16 index, u16 *pw_data)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_AESEBURX_USERDATA;
hm.u.c.param1 = index;
@@ -1791,14 +1519,15 @@ u16 HPI_AESEBU__receiver_get_user_data(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 HPI_AESEBU__receiver_get_channel_status(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u16 index, u16 *pw_data)
+u16 hpi_aesebu_receiver_get_channel_status(u32 h_control, u16 index,
+ u16 *pw_data)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_AESEBURX_CHANNELSTATUS;
hm.u.c.param1 = index;
@@ -1809,101 +1538,93 @@ u16 HPI_AESEBU__receiver_get_channel_status(const struct hpi_hsubsys
return hr.error;
}
-u16 HPI_AESEBU__receiver_get_error_status(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_error_data)
+u16 hpi_aesebu_receiver_get_error_status(u32 h_control, u16 *pw_error_data)
{
u32 error_data = 0;
- u16 error = 0;
+ u16 err = 0;
- error = hpi_control_param1_get(ph_subsys, h_control,
- HPI_AESEBURX_ERRORSTATUS, &error_data);
+ err = hpi_control_param1_get(h_control, HPI_AESEBURX_ERRORSTATUS,
+ &error_data);
if (pw_error_data)
*pw_error_data = (u16)error_data;
- return error;
+ return err;
}
-u16 HPI_AESEBU__transmitter_set_sample_rate(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u32 sample_rate)
+u16 hpi_aesebu_transmitter_set_sample_rate(u32 h_control, u32 sample_rate)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_AESEBUTX_SAMPLERATE, sample_rate, 0);
+ return hpi_control_param_set(h_control, HPI_AESEBUTX_SAMPLERATE,
+ sample_rate, 0);
}
-u16 HPI_AESEBU__transmitter_set_user_data(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 data)
+u16 hpi_aesebu_transmitter_set_user_data(u32 h_control, u16 index, u16 data)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_AESEBUTX_USERDATA, index, data);
+ return hpi_control_param_set(h_control, HPI_AESEBUTX_USERDATA, index,
+ data);
}
-u16 HPI_AESEBU__transmitter_set_channel_status(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u16 index, u16 data)
+u16 hpi_aesebu_transmitter_set_channel_status(u32 h_control, u16 index,
+ u16 data)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_AESEBUTX_CHANNELSTATUS, index, data);
+ return hpi_control_param_set(h_control, HPI_AESEBUTX_CHANNELSTATUS,
+ index, data);
}
-u16 HPI_AESEBU__transmitter_get_channel_status(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, u16 index, u16 *pw_data)
+u16 hpi_aesebu_transmitter_get_channel_status(u32 h_control, u16 index,
+ u16 *pw_data)
{
return HPI_ERROR_INVALID_OPERATION;
}
-u16 HPI_AESEBU__transmitter_query_format(const struct hpi_hsubsys *ph_subsys,
- const u32 h_aes_tx, const u32 index, u16 *pw_format)
+u16 hpi_aesebu_transmitter_query_format(const u32 h_aes_tx, const u32 index,
+ u16 *pw_format)
{
u32 qr;
u16 err;
- err = hpi_control_query(ph_subsys, h_aes_tx, HPI_AESEBUTX_FORMAT,
- index, 0, &qr);
+ err = hpi_control_query(h_aes_tx, HPI_AESEBUTX_FORMAT, index, 0, &qr);
*pw_format = (u16)qr;
return err;
}
-u16 HPI_AESEBU__transmitter_set_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 output_format)
+u16 hpi_aesebu_transmitter_set_format(u32 h_control, u16 output_format)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_AESEBUTX_FORMAT, output_format, 0);
+ return hpi_control_param_set(h_control, HPI_AESEBUTX_FORMAT,
+ output_format, 0);
}
-u16 HPI_AESEBU__transmitter_get_format(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_output_format)
+u16 hpi_aesebu_transmitter_get_format(u32 h_control, u16 *pw_output_format)
{
u16 err;
u32 param;
- err = hpi_control_param1_get(ph_subsys, h_control,
- HPI_AESEBUTX_FORMAT, &param);
+ err = hpi_control_param1_get(h_control, HPI_AESEBUTX_FORMAT, &param);
if (!err && pw_output_format)
*pw_output_format = (u16)param;
return err;
}
-u16 hpi_bitstream_set_clock_edge(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 edge_type)
+u16 hpi_bitstream_set_clock_edge(u32 h_control, u16 edge_type)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_BITSTREAM_CLOCK_EDGE, edge_type, 0);
+ return hpi_control_param_set(h_control, HPI_BITSTREAM_CLOCK_EDGE,
+ edge_type, 0);
}
-u16 hpi_bitstream_set_data_polarity(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 polarity)
+u16 hpi_bitstream_set_data_polarity(u32 h_control, u16 polarity)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_BITSTREAM_DATA_POLARITY, polarity, 0);
+ return hpi_control_param_set(h_control, HPI_BITSTREAM_DATA_POLARITY,
+ polarity, 0);
}
-u16 hpi_bitstream_get_activity(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_clk_activity, u16 *pw_data_activity)
+u16 hpi_bitstream_get_activity(u32 h_control, u16 *pw_clk_activity,
+ u16 *pw_data_activity)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_BITSTREAM_ACTIVITY;
hpi_send_recv(&hm, &hr);
if (pw_clk_activity)
@@ -1913,299 +1634,293 @@ u16 hpi_bitstream_get_activity(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_channel_mode_query_mode(const struct hpi_hsubsys *ph_subsys,
- const u32 h_mode, const u32 index, u16 *pw_mode)
+u16 hpi_channel_mode_query_mode(const u32 h_mode, const u32 index,
+ u16 *pw_mode)
{
u32 qr;
u16 err;
- err = hpi_control_query(ph_subsys, h_mode, HPI_CHANNEL_MODE_MODE,
- index, 0, &qr);
+ err = hpi_control_query(h_mode, HPI_CHANNEL_MODE_MODE, index, 0, &qr);
*pw_mode = (u16)qr;
return err;
}
-u16 hpi_channel_mode_set(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 mode)
+u16 hpi_channel_mode_set(u32 h_control, u16 mode)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_CHANNEL_MODE_MODE, mode, 0);
+ return hpi_control_param_set(h_control, HPI_CHANNEL_MODE_MODE, mode,
+ 0);
}
-u16 hpi_channel_mode_get(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 *mode)
+u16 hpi_channel_mode_get(u32 h_control, u16 *mode)
{
u32 mode32 = 0;
- u16 error = hpi_control_param1_get(ph_subsys, h_control,
+ u16 err = hpi_control_param1_get(h_control,
HPI_CHANNEL_MODE_MODE, &mode32);
if (mode)
*mode = (u16)mode32;
- return error;
+ return err;
}
-u16 hpi_cobranet_hmi_write(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 hmi_address, u32 byte_count, u8 *pb_data)
+u16 hpi_cobranet_hmi_write(u32 h_control, u32 hmi_address, u32 byte_count,
+ u8 *pb_data)
{
- struct hpi_message hm;
- struct hpi_response hr;
+ struct hpi_msg_cobranet_hmiwrite hm;
+ struct hpi_response_header hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX,
- HPI_CONTROL_SET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ hpi_init_message_responseV1(&hm.h, sizeof(hm), &hr, sizeof(hr),
+ HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE);
- hm.u.cx.u.cobranet_data.byte_count = byte_count;
- hm.u.cx.u.cobranet_data.hmi_address = hmi_address;
+ if (hpi_handle_indexes(h_control, &hm.h.adapter_index,
+ &hm.h.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
- if (byte_count <= 8) {
- memcpy(hm.u.cx.u.cobranet_data.data, pb_data, byte_count);
- hm.u.cx.attribute = HPI_COBRANET_SET;
- } else {
- hm.u.cx.u.cobranet_bigdata.pb_data = pb_data;
- hm.u.cx.attribute = HPI_COBRANET_SET_DATA;
- }
+ if (byte_count > sizeof(hm.bytes))
+ return HPI_ERROR_MESSAGE_BUFFER_TOO_SMALL;
- hpi_send_recv(&hm, &hr);
+ hm.p.attribute = HPI_COBRANET_SET;
+ hm.p.byte_count = byte_count;
+ hm.p.hmi_address = hmi_address;
+ memcpy(hm.bytes, pb_data, byte_count);
+ hm.h.size = (u16)(sizeof(hm.h) + sizeof(hm.p) + byte_count);
+ hpi_send_recvV1(&hm.h, &hr);
return hr.error;
}
-u16 hpi_cobranet_hmi_read(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 hmi_address, u32 max_byte_count, u32 *pbyte_count, u8 *pb_data)
+u16 hpi_cobranet_hmi_read(u32 h_control, u32 hmi_address, u32 max_byte_count,
+ u32 *pbyte_count, u8 *pb_data)
{
- struct hpi_message hm;
- struct hpi_response hr;
+ struct hpi_msg_cobranet_hmiread hm;
+ struct hpi_res_cobranet_hmiread hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX,
- HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ hpi_init_message_responseV1(&hm.h, sizeof(hm), &hr.h, sizeof(hr),
+ HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
- hm.u.cx.u.cobranet_data.byte_count = max_byte_count;
- hm.u.cx.u.cobranet_data.hmi_address = hmi_address;
+ if (hpi_handle_indexes(h_control, &hm.h.adapter_index,
+ &hm.h.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
- if (max_byte_count <= 8) {
- hm.u.cx.attribute = HPI_COBRANET_GET;
- } else {
- hm.u.cx.u.cobranet_bigdata.pb_data = pb_data;
- hm.u.cx.attribute = HPI_COBRANET_GET_DATA;
- }
+ if (max_byte_count > sizeof(hr.bytes))
+ return HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
- hpi_send_recv(&hm, &hr);
- if (!hr.error && pb_data) {
+ hm.p.attribute = HPI_COBRANET_GET;
+ hm.p.byte_count = max_byte_count;
+ hm.p.hmi_address = hmi_address;
- *pbyte_count = hr.u.cx.u.cobranet_data.byte_count;
+ hpi_send_recvV1(&hm.h, &hr.h);
- if (*pbyte_count < max_byte_count)
- max_byte_count = *pbyte_count;
+ if (!hr.h.error && pb_data) {
+ if (hr.byte_count > sizeof(hr.bytes))
- if (hm.u.cx.attribute == HPI_COBRANET_GET) {
- memcpy(pb_data, hr.u.cx.u.cobranet_data.data,
- max_byte_count);
- } else {
+ return HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
- }
+ *pbyte_count = hr.byte_count;
+
+ if (hr.byte_count < max_byte_count)
+ max_byte_count = *pbyte_count;
+ memcpy(pb_data, hr.bytes, max_byte_count);
}
- return hr.error;
+ return hr.h.error;
}
-u16 hpi_cobranet_hmi_get_status(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pstatus, u32 *preadable_size,
- u32 *pwriteable_size)
+u16 hpi_cobranet_hmi_get_status(u32 h_control, u32 *pstatus,
+ u32 *preadable_size, u32 *pwriteable_size)
{
struct hpi_message hm;
struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX,
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
- hm.u.cx.attribute = HPI_COBRANET_GET_STATUS;
+ hm.u.c.attribute = HPI_COBRANET_GET_STATUS;
hpi_send_recv(&hm, &hr);
if (!hr.error) {
if (pstatus)
- *pstatus = hr.u.cx.u.cobranet_status.status;
+ *pstatus = hr.u.cu.cobranet.status.status;
if (preadable_size)
*preadable_size =
- hr.u.cx.u.cobranet_status.readable_size;
+ hr.u.cu.cobranet.status.readable_size;
if (pwriteable_size)
*pwriteable_size =
- hr.u.cx.u.cobranet_status.writeable_size;
+ hr.u.cu.cobranet.status.writeable_size;
}
return hr.error;
}
-u16 hpi_cobranet_getI_paddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pi_paddress)
+u16 hpi_cobranet_get_ip_address(u32 h_control, u32 *pdw_ip_address)
{
u32 byte_count;
u32 iP;
- u16 error;
+ u16 err;
- error = hpi_cobranet_hmi_read(ph_subsys, h_control,
+ err = hpi_cobranet_hmi_read(h_control,
HPI_COBRANET_HMI_cobra_ip_mon_currentIP, 4, &byte_count,
(u8 *)&iP);
- *pi_paddress =
+ *pdw_ip_address =
((iP & 0xff000000) >> 8) | ((iP & 0x00ff0000) << 8) | ((iP &
0x0000ff00) >> 8) | ((iP & 0x000000ff) << 8);
- if (error)
- *pi_paddress = 0;
+ if (err)
+ *pdw_ip_address = 0;
- return error;
+ return err;
}
-u16 hpi_cobranet_setI_paddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 i_paddress)
+u16 hpi_cobranet_set_ip_address(u32 h_control, u32 dw_ip_address)
{
u32 iP;
- u16 error;
+ u16 err;
- iP = ((i_paddress & 0xff000000) >> 8) | ((i_paddress & 0x00ff0000) <<
- 8) | ((i_paddress & 0x0000ff00) >> 8) | ((i_paddress &
- 0x000000ff) << 8);
+ iP = ((dw_ip_address & 0xff000000) >> 8) | ((dw_ip_address &
+ 0x00ff0000) << 8) | ((dw_ip_address & 0x0000ff00) >>
+ 8) | ((dw_ip_address & 0x000000ff) << 8);
- error = hpi_cobranet_hmi_write(ph_subsys, h_control,
+ err = hpi_cobranet_hmi_write(h_control,
HPI_COBRANET_HMI_cobra_ip_mon_currentIP, 4, (u8 *)&iP);
- return error;
+ return err;
}
-u16 hpi_cobranet_get_staticI_paddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pi_paddress)
+u16 hpi_cobranet_get_static_ip_address(u32 h_control, u32 *pdw_ip_address)
{
u32 byte_count;
u32 iP;
- u16 error;
- error = hpi_cobranet_hmi_read(ph_subsys, h_control,
+ u16 err;
+ err = hpi_cobranet_hmi_read(h_control,
HPI_COBRANET_HMI_cobra_ip_mon_staticIP, 4, &byte_count,
(u8 *)&iP);
- *pi_paddress =
+ *pdw_ip_address =
((iP & 0xff000000) >> 8) | ((iP & 0x00ff0000) << 8) | ((iP &
0x0000ff00) >> 8) | ((iP & 0x000000ff) << 8);
- if (error)
- *pi_paddress = 0;
+ if (err)
+ *pdw_ip_address = 0;
- return error;
+ return err;
}
-u16 hpi_cobranet_set_staticI_paddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 i_paddress)
+u16 hpi_cobranet_set_static_ip_address(u32 h_control, u32 dw_ip_address)
{
u32 iP;
- u16 error;
+ u16 err;
- iP = ((i_paddress & 0xff000000) >> 8) | ((i_paddress & 0x00ff0000) <<
- 8) | ((i_paddress & 0x0000ff00) >> 8) | ((i_paddress &
- 0x000000ff) << 8);
+ iP = ((dw_ip_address & 0xff000000) >> 8) | ((dw_ip_address &
+ 0x00ff0000) << 8) | ((dw_ip_address & 0x0000ff00) >>
+ 8) | ((dw_ip_address & 0x000000ff) << 8);
- error = hpi_cobranet_hmi_write(ph_subsys, h_control,
+ err = hpi_cobranet_hmi_write(h_control,
HPI_COBRANET_HMI_cobra_ip_mon_staticIP, 4, (u8 *)&iP);
- return error;
+ return err;
}
-u16 hpi_cobranet_getMA_caddress(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pmAC_MS_bs, u32 *pmAC_LS_bs)
+u16 hpi_cobranet_get_macaddress(u32 h_control, u32 *p_mac_msbs,
+ u32 *p_mac_lsbs)
{
u32 byte_count;
- u16 error;
- u32 mAC;
+ u16 err;
+ u32 mac;
- error = hpi_cobranet_hmi_read(ph_subsys, h_control,
+ err = hpi_cobranet_hmi_read(h_control,
HPI_COBRANET_HMI_cobra_if_phy_address, 4, &byte_count,
- (u8 *)&mAC);
- *pmAC_MS_bs =
- ((mAC & 0xff000000) >> 8) | ((mAC & 0x00ff0000) << 8) | ((mAC
- & 0x0000ff00) >> 8) | ((mAC & 0x000000ff) << 8);
- error += hpi_cobranet_hmi_read(ph_subsys, h_control,
- HPI_COBRANET_HMI_cobra_if_phy_address + 1, 4, &byte_count,
- (u8 *)&mAC);
- *pmAC_LS_bs =
- ((mAC & 0xff000000) >> 8) | ((mAC & 0x00ff0000) << 8) | ((mAC
- & 0x0000ff00) >> 8) | ((mAC & 0x000000ff) << 8);
-
- if (error) {
- *pmAC_MS_bs = 0;
- *pmAC_LS_bs = 0;
+ (u8 *)&mac);
+
+ if (!err) {
+ *p_mac_msbs =
+ ((mac & 0xff000000) >> 8) | ((mac & 0x00ff0000) << 8)
+ | ((mac & 0x0000ff00) >> 8) | ((mac & 0x000000ff) <<
+ 8);
+
+ err = hpi_cobranet_hmi_read(h_control,
+ HPI_COBRANET_HMI_cobra_if_phy_address + 1, 4,
+ &byte_count, (u8 *)&mac);
}
- return error;
+ if (!err) {
+ *p_mac_lsbs =
+ ((mac & 0xff000000) >> 8) | ((mac & 0x00ff0000) << 8)
+ | ((mac & 0x0000ff00) >> 8) | ((mac & 0x000000ff) <<
+ 8);
+ } else {
+ *p_mac_msbs = 0;
+ *p_mac_lsbs = 0;
+ }
+
+ return err;
}
-u16 hpi_compander_set_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 enable)
+u16 hpi_compander_set_enable(u32 h_control, u32 enable)
{
- return hpi_control_param_set(ph_subsys, h_control, HPI_GENERIC_ENABLE,
- enable, 0);
+ return hpi_control_param_set(h_control, HPI_GENERIC_ENABLE, enable,
+ 0);
}
-u16 hpi_compander_get_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *enable)
+u16 hpi_compander_get_enable(u32 h_control, u32 *enable)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_GENERIC_ENABLE, enable);
+ return hpi_control_param1_get(h_control, HPI_GENERIC_ENABLE, enable);
}
-u16 hpi_compander_set_makeup_gain(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, short makeup_gain0_01dB)
+u16 hpi_compander_set_makeup_gain(u32 h_control, short makeup_gain0_01dB)
{
return hpi_control_log_set2(h_control, HPI_COMPANDER_MAKEUPGAIN,
makeup_gain0_01dB, 0);
}
-u16 hpi_compander_get_makeup_gain(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, short *makeup_gain0_01dB)
+u16 hpi_compander_get_makeup_gain(u32 h_control, short *makeup_gain0_01dB)
{
- return hpi_control_log_get2(ph_subsys, h_control,
- HPI_COMPANDER_MAKEUPGAIN, makeup_gain0_01dB, NULL);
+ return hpi_control_log_get2(h_control, HPI_COMPANDER_MAKEUPGAIN,
+ makeup_gain0_01dB, NULL);
}
-u16 hpi_compander_set_attack_time_constant(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, unsigned int index, u32 attack)
+u16 hpi_compander_set_attack_time_constant(u32 h_control, unsigned int index,
+ u32 attack)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_COMPANDER_ATTACK, attack, index);
+ return hpi_control_param_set(h_control, HPI_COMPANDER_ATTACK, attack,
+ index);
}
-u16 hpi_compander_get_attack_time_constant(const struct hpi_hsubsys
- *ph_subsys, u32 h_control, unsigned int index, u32 *attack)
+u16 hpi_compander_get_attack_time_constant(u32 h_control, unsigned int index,
+ u32 *attack)
{
- return hpi_control_param_get(ph_subsys, h_control,
- HPI_COMPANDER_ATTACK, 0, index, attack, NULL);
+ return hpi_control_param_get(h_control, HPI_COMPANDER_ATTACK, 0,
+ index, attack, NULL);
}
-u16 hpi_compander_set_decay_time_constant(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, unsigned int index, u32 decay)
+u16 hpi_compander_set_decay_time_constant(u32 h_control, unsigned int index,
+ u32 decay)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_COMPANDER_DECAY, decay, index);
+ return hpi_control_param_set(h_control, HPI_COMPANDER_DECAY, decay,
+ index);
}
-u16 hpi_compander_get_decay_time_constant(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, unsigned int index, u32 *decay)
+u16 hpi_compander_get_decay_time_constant(u32 h_control, unsigned int index,
+ u32 *decay)
{
- return hpi_control_param_get(ph_subsys, h_control,
- HPI_COMPANDER_DECAY, 0, index, decay, NULL);
+ return hpi_control_param_get(h_control, HPI_COMPANDER_DECAY, 0, index,
+ decay, NULL);
}
-u16 hpi_compander_set_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, unsigned int index, short threshold0_01dB)
+u16 hpi_compander_set_threshold(u32 h_control, unsigned int index,
+ short threshold0_01dB)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_SET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_COMPANDER_THRESHOLD;
hm.u.c.param2 = index;
hm.u.c.an_log_value[0] = threshold0_01dB;
@@ -2215,15 +1930,16 @@ u16 hpi_compander_set_threshold(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_compander_get_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, unsigned int index, short *threshold0_01dB)
+u16 hpi_compander_get_threshold(u32 h_control, unsigned int index,
+ short *threshold0_01dB)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_COMPANDER_THRESHOLD;
hm.u.c.param2 = index;
@@ -2233,29 +1949,28 @@ u16 hpi_compander_get_threshold(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_compander_set_ratio(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 index, u32 ratio100)
+u16 hpi_compander_set_ratio(u32 h_control, u32 index, u32 ratio100)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_COMPANDER_RATIO, ratio100, index);
+ return hpi_control_param_set(h_control, HPI_COMPANDER_RATIO, ratio100,
+ index);
}
-u16 hpi_compander_get_ratio(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 index, u32 *ratio100)
+u16 hpi_compander_get_ratio(u32 h_control, u32 index, u32 *ratio100)
{
- return hpi_control_param_get(ph_subsys, h_control,
- HPI_COMPANDER_RATIO, 0, index, ratio100, NULL);
+ return hpi_control_param_get(h_control, HPI_COMPANDER_RATIO, 0, index,
+ ratio100, NULL);
}
-u16 hpi_level_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB)
+u16 hpi_level_query_range(u32 h_control, short *min_gain_01dB,
+ short *max_gain_01dB, short *step_gain_01dB)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_LEVEL_RANGE;
hpi_send_recv(&hm, &hr);
@@ -2273,31 +1988,27 @@ u16 hpi_level_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
return hr.error;
}
-u16 hpi_level_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_gain0_01dB[HPI_MAX_CHANNELS]
+u16 hpi_level_set_gain(u32 h_control, short an_gain0_01dB[HPI_MAX_CHANNELS]
)
{
return hpi_control_log_set2(h_control, HPI_LEVEL_GAIN,
an_gain0_01dB[0], an_gain0_01dB[1]);
}
-u16 hpi_level_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_gain0_01dB[HPI_MAX_CHANNELS]
+u16 hpi_level_get_gain(u32 h_control, short an_gain0_01dB[HPI_MAX_CHANNELS]
)
{
- return hpi_control_log_get2(ph_subsys, h_control, HPI_LEVEL_GAIN,
+ return hpi_control_log_get2(h_control, HPI_LEVEL_GAIN,
&an_gain0_01dB[0], &an_gain0_01dB[1]);
}
-u16 hpi_meter_query_channels(const struct hpi_hsubsys *ph_subsys,
- const u32 h_meter, u32 *p_channels)
+u16 hpi_meter_query_channels(const u32 h_meter, u32 *p_channels)
{
- return hpi_control_query(ph_subsys, h_meter, HPI_METER_NUM_CHANNELS,
- 0, 0, p_channels);
+ return hpi_control_query(h_meter, HPI_METER_NUM_CHANNELS, 0, 0,
+ p_channels);
}
-u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_peakdB[HPI_MAX_CHANNELS]
+u16 hpi_meter_get_peak(u32 h_control, short an_peakdB[HPI_MAX_CHANNELS]
)
{
short i = 0;
@@ -2307,8 +2018,8 @@ u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control,
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
- hm.obj_index = hm.obj_index;
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_METER_PEAK;
hpi_send_recv(&hm, &hr);
@@ -2322,8 +2033,7 @@ u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control,
return hr.error;
}
-u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_rmsdB[HPI_MAX_CHANNELS]
+u16 hpi_meter_get_rms(u32 h_control, short an_rmsdB[HPI_MAX_CHANNELS]
)
{
short i = 0;
@@ -2333,7 +2043,8 @@ u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control,
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_METER_RMS;
hpi_send_recv(&hm, &hr);
@@ -2348,22 +2059,20 @@ u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control,
return hr.error;
}
-u16 hpi_meter_set_rms_ballistics(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 attack, u16 decay)
+u16 hpi_meter_set_rms_ballistics(u32 h_control, u16 attack, u16 decay)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_METER_RMS_BALLISTICS, attack, decay);
+ return hpi_control_param_set(h_control, HPI_METER_RMS_BALLISTICS,
+ attack, decay);
}
-u16 hpi_meter_get_rms_ballistics(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pn_attack, u16 *pn_decay)
+u16 hpi_meter_get_rms_ballistics(u32 h_control, u16 *pn_attack, u16 *pn_decay)
{
u32 attack;
u32 decay;
u16 error;
- error = hpi_control_param2_get(ph_subsys, h_control,
- HPI_METER_RMS_BALLISTICS, &attack, &decay);
+ error = hpi_control_param2_get(h_control, HPI_METER_RMS_BALLISTICS,
+ &attack, &decay);
if (pn_attack)
*pn_attack = (unsigned short)attack;
@@ -2373,22 +2082,21 @@ u16 hpi_meter_get_rms_ballistics(const struct hpi_hsubsys *ph_subsys,
return error;
}
-u16 hpi_meter_set_peak_ballistics(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 attack, u16 decay)
+u16 hpi_meter_set_peak_ballistics(u32 h_control, u16 attack, u16 decay)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_METER_PEAK_BALLISTICS, attack, decay);
+ return hpi_control_param_set(h_control, HPI_METER_PEAK_BALLISTICS,
+ attack, decay);
}
-u16 hpi_meter_get_peak_ballistics(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pn_attack, u16 *pn_decay)
+u16 hpi_meter_get_peak_ballistics(u32 h_control, u16 *pn_attack,
+ u16 *pn_decay)
{
u32 attack;
u32 decay;
u16 error;
- error = hpi_control_param2_get(ph_subsys, h_control,
- HPI_METER_PEAK_BALLISTICS, &attack, &decay);
+ error = hpi_control_param2_get(h_control, HPI_METER_PEAK_BALLISTICS,
+ &attack, &decay);
if (pn_attack)
*pn_attack = (short)attack;
@@ -2398,55 +2106,53 @@ u16 hpi_meter_get_peak_ballistics(const struct hpi_hsubsys *ph_subsys,
return error;
}
-u16 hpi_microphone_set_phantom_power(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 on_off)
+u16 hpi_microphone_set_phantom_power(u32 h_control, u16 on_off)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_MICROPHONE_PHANTOM_POWER, (u32)on_off, 0);
+ return hpi_control_param_set(h_control, HPI_MICROPHONE_PHANTOM_POWER,
+ (u32)on_off, 0);
}
-u16 hpi_microphone_get_phantom_power(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_on_off)
+u16 hpi_microphone_get_phantom_power(u32 h_control, u16 *pw_on_off)
{
u16 error = 0;
u32 on_off = 0;
- error = hpi_control_param1_get(ph_subsys, h_control,
+ error = hpi_control_param1_get(h_control,
HPI_MICROPHONE_PHANTOM_POWER, &on_off);
if (pw_on_off)
*pw_on_off = (u16)on_off;
return error;
}
-u16 hpi_multiplexer_set_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 source_node_type, u16 source_node_index)
+u16 hpi_multiplexer_set_source(u32 h_control, u16 source_node_type,
+ u16 source_node_index)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_MULTIPLEXER_SOURCE, source_node_type, source_node_index);
+ return hpi_control_param_set(h_control, HPI_MULTIPLEXER_SOURCE,
+ source_node_type, source_node_index);
}
-u16 hpi_multiplexer_get_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *source_node_type, u16 *source_node_index)
+u16 hpi_multiplexer_get_source(u32 h_control, u16 *source_node_type,
+ u16 *source_node_index)
{
u32 node, index;
- u16 error = hpi_control_param2_get(ph_subsys, h_control,
+ u16 err = hpi_control_param2_get(h_control,
HPI_MULTIPLEXER_SOURCE, &node,
&index);
if (source_node_type)
*source_node_type = (u16)node;
if (source_node_index)
*source_node_index = (u16)index;
- return error;
+ return err;
}
-u16 hpi_multiplexer_query_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 *source_node_type,
- u16 *source_node_index)
+u16 hpi_multiplexer_query_source(u32 h_control, u16 index,
+ u16 *source_node_type, u16 *source_node_index)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_MULTIPLEXER_QUERYSOURCE;
hm.u.c.param1 = index;
@@ -2459,15 +2165,15 @@ u16 hpi_multiplexer_query_source(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_parametricEQ__get_info(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_number_of_bands, u16 *pw_on_off)
+u16 hpi_parametric_eq_get_info(u32 h_control, u16 *pw_number_of_bands,
+ u16 *pw_on_off)
{
u32 oB = 0;
u32 oO = 0;
u16 error = 0;
- error = hpi_control_param2_get(ph_subsys, h_control,
- HPI_EQUALIZER_NUM_FILTERS, &oO, &oB);
+ error = hpi_control_param2_get(h_control, HPI_EQUALIZER_NUM_FILTERS,
+ &oO, &oB);
if (pw_number_of_bands)
*pw_number_of_bands = (u16)oB;
if (pw_on_off)
@@ -2475,23 +2181,22 @@ u16 hpi_parametricEQ__get_info(const struct hpi_hsubsys *ph_subsys,
return error;
}
-u16 hpi_parametricEQ__set_state(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 on_off)
+u16 hpi_parametric_eq_set_state(u32 h_control, u16 on_off)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_EQUALIZER_NUM_FILTERS, on_off, 0);
+ return hpi_control_param_set(h_control, HPI_EQUALIZER_NUM_FILTERS,
+ on_off, 0);
}
-u16 hpi_parametricEQ__get_band(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 *pn_type, u32 *pfrequency_hz,
- short *pnQ100, short *pn_gain0_01dB)
+u16 hpi_parametric_eq_get_band(u32 h_control, u16 index, u16 *pn_type,
+ u32 *pfrequency_hz, short *pnQ100, short *pn_gain0_01dB)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_EQUALIZER_FILTER;
hm.u.c.param2 = index;
@@ -2509,16 +2214,16 @@ u16 hpi_parametricEQ__get_band(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_parametricEQ__set_band(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, u16 type, u32 frequency_hz, short q100,
- short gain0_01dB)
+u16 hpi_parametric_eq_set_band(u32 h_control, u16 index, u16 type,
+ u32 frequency_hz, short q100, short gain0_01dB)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_SET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.param1 = frequency_hz;
hm.u.c.param2 = (index & 0xFFFFL) + ((u32)type << 16);
@@ -2531,8 +2236,7 @@ u16 hpi_parametricEQ__set_band(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 index, short coeffs[5]
+u16 hpi_parametric_eq_get_coeffs(u32 h_control, u16 index, short coeffs[5]
)
{
struct hpi_message hm;
@@ -2540,7 +2244,8 @@ u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys,
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_EQUALIZER_COEFFICIENTS;
hm.u.c.param2 = index;
@@ -2555,444 +2260,385 @@ u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_sample_clock_query_source(const struct hpi_hsubsys *ph_subsys,
- const u32 h_clock, const u32 index, u16 *pw_source)
+u16 hpi_sample_clock_query_source(const u32 h_clock, const u32 index,
+ u16 *pw_source)
{
u32 qr;
u16 err;
- err = hpi_control_query(ph_subsys, h_clock, HPI_SAMPLECLOCK_SOURCE,
- index, 0, &qr);
+ err = hpi_control_query(h_clock, HPI_SAMPLECLOCK_SOURCE, index, 0,
+ &qr);
*pw_source = (u16)qr;
return err;
}
-u16 hpi_sample_clock_set_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 source)
+u16 hpi_sample_clock_set_source(u32 h_control, u16 source)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_SAMPLECLOCK_SOURCE, source, 0);
+ return hpi_control_param_set(h_control, HPI_SAMPLECLOCK_SOURCE,
+ source, 0);
}
-u16 hpi_sample_clock_get_source(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_source)
+u16 hpi_sample_clock_get_source(u32 h_control, u16 *pw_source)
{
- u16 error = 0;
+ u16 err = 0;
u32 source = 0;
- error = hpi_control_param1_get(ph_subsys, h_control,
- HPI_SAMPLECLOCK_SOURCE, &source);
- if (!error)
+ err = hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_SOURCE,
+ &source);
+ if (!err)
if (pw_source)
*pw_source = (u16)source;
- return error;
+ return err;
}
-u16 hpi_sample_clock_query_source_index(const struct hpi_hsubsys *ph_subsys,
- const u32 h_clock, const u32 index, const u32 source,
- u16 *pw_source_index)
+u16 hpi_sample_clock_query_source_index(const u32 h_clock, const u32 index,
+ const u32 source, u16 *pw_source_index)
{
u32 qr;
u16 err;
- err = hpi_control_query(ph_subsys, h_clock,
- HPI_SAMPLECLOCK_SOURCE_INDEX, index, source, &qr);
+ err = hpi_control_query(h_clock, HPI_SAMPLECLOCK_SOURCE_INDEX, index,
+ source, &qr);
*pw_source_index = (u16)qr;
return err;
}
-u16 hpi_sample_clock_set_source_index(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 source_index)
+u16 hpi_sample_clock_set_source_index(u32 h_control, u16 source_index)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_SAMPLECLOCK_SOURCE_INDEX, source_index, 0);
+ return hpi_control_param_set(h_control, HPI_SAMPLECLOCK_SOURCE_INDEX,
+ source_index, 0);
}
-u16 hpi_sample_clock_get_source_index(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u16 *pw_source_index)
+u16 hpi_sample_clock_get_source_index(u32 h_control, u16 *pw_source_index)
{
- u16 error = 0;
+ u16 err = 0;
u32 source_index = 0;
- error = hpi_control_param1_get(ph_subsys, h_control,
- HPI_SAMPLECLOCK_SOURCE_INDEX, &source_index);
- if (!error)
+ err = hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_SOURCE_INDEX,
+ &source_index);
+ if (!err)
if (pw_source_index)
*pw_source_index = (u16)source_index;
- return error;
+ return err;
}
-u16 hpi_sample_clock_query_local_rate(const struct hpi_hsubsys *ph_subsys,
- const u32 h_clock, const u32 index, u32 *prate)
+u16 hpi_sample_clock_query_local_rate(const u32 h_clock, const u32 index,
+ u32 *prate)
{
- u16 err;
- err = hpi_control_query(ph_subsys, h_clock,
- HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, index, 0, prate);
-
- return err;
+ return hpi_control_query(h_clock, HPI_SAMPLECLOCK_LOCAL_SAMPLERATE,
+ index, 0, prate);
}
-u16 hpi_sample_clock_set_local_rate(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 sample_rate)
+u16 hpi_sample_clock_set_local_rate(u32 h_control, u32 sample_rate)
{
- return hpi_control_param_set(ph_subsys, h_control,
+ return hpi_control_param_set(h_control,
HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, sample_rate, 0);
}
-u16 hpi_sample_clock_get_local_rate(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *psample_rate)
+u16 hpi_sample_clock_get_local_rate(u32 h_control, u32 *psample_rate)
{
- u16 error = 0;
+ u16 err = 0;
u32 sample_rate = 0;
- error = hpi_control_param1_get(ph_subsys, h_control,
+ err = hpi_control_param1_get(h_control,
HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, &sample_rate);
- if (!error)
+ if (!err)
if (psample_rate)
*psample_rate = sample_rate;
- return error;
+ return err;
}
-u16 hpi_sample_clock_get_sample_rate(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *psample_rate)
+u16 hpi_sample_clock_get_sample_rate(u32 h_control, u32 *psample_rate)
{
- u16 error = 0;
+ u16 err = 0;
u32 sample_rate = 0;
- error = hpi_control_param1_get(ph_subsys, h_control,
- HPI_SAMPLECLOCK_SAMPLERATE, &sample_rate);
- if (!error)
+ err = hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_SAMPLERATE,
+ &sample_rate);
+ if (!err)
if (psample_rate)
*psample_rate = sample_rate;
- return error;
+ return err;
}
-u16 hpi_sample_clock_set_auto(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 enable)
+u16 hpi_sample_clock_set_auto(u32 h_control, u32 enable)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_SAMPLECLOCK_AUTO, enable, 0);
+ return hpi_control_param_set(h_control, HPI_SAMPLECLOCK_AUTO, enable,
+ 0);
}
-u16 hpi_sample_clock_get_auto(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *penable)
+u16 hpi_sample_clock_get_auto(u32 h_control, u32 *penable)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_SAMPLECLOCK_AUTO, penable);
+ return hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_AUTO,
+ penable);
}
-u16 hpi_sample_clock_set_local_rate_lock(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 lock)
+u16 hpi_sample_clock_set_local_rate_lock(u32 h_control, u32 lock)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_SAMPLECLOCK_LOCAL_LOCK, lock, 0);
+ return hpi_control_param_set(h_control, HPI_SAMPLECLOCK_LOCAL_LOCK,
+ lock, 0);
}
-u16 hpi_sample_clock_get_local_rate_lock(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *plock)
+u16 hpi_sample_clock_get_local_rate_lock(u32 h_control, u32 *plock)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_SAMPLECLOCK_LOCAL_LOCK, plock);
+ return hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_LOCAL_LOCK,
+ plock);
}
-u16 hpi_tone_detector_get_frequency(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 index, u32 *frequency)
+u16 hpi_tone_detector_get_frequency(u32 h_control, u32 index, u32 *frequency)
{
- return hpi_control_param_get(ph_subsys, h_control,
- HPI_TONEDETECTOR_FREQUENCY, index, 0, frequency, NULL);
+ return hpi_control_param_get(h_control, HPI_TONEDETECTOR_FREQUENCY,
+ index, 0, frequency, NULL);
}
-u16 hpi_tone_detector_get_state(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *state)
+u16 hpi_tone_detector_get_state(u32 h_control, u32 *state)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_TONEDETECTOR_STATE, state);
+ return hpi_control_param1_get(h_control, HPI_TONEDETECTOR_STATE,
+ state);
}
-u16 hpi_tone_detector_set_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 enable)
+u16 hpi_tone_detector_set_enable(u32 h_control, u32 enable)
{
- return hpi_control_param_set(ph_subsys, h_control, HPI_GENERIC_ENABLE,
- (u32)enable, 0);
+ return hpi_control_param_set(h_control, HPI_GENERIC_ENABLE, enable,
+ 0);
}
-u16 hpi_tone_detector_get_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *enable)
+u16 hpi_tone_detector_get_enable(u32 h_control, u32 *enable)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_GENERIC_ENABLE, enable);
+ return hpi_control_param1_get(h_control, HPI_GENERIC_ENABLE, enable);
}
-u16 hpi_tone_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 event_enable)
+u16 hpi_tone_detector_set_event_enable(u32 h_control, u32 event_enable)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_GENERIC_EVENT_ENABLE, (u32)event_enable, 0);
+ return hpi_control_param_set(h_control, HPI_GENERIC_EVENT_ENABLE,
+ (u32)event_enable, 0);
}
-u16 hpi_tone_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *event_enable)
+u16 hpi_tone_detector_get_event_enable(u32 h_control, u32 *event_enable)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_GENERIC_EVENT_ENABLE, event_enable);
+ return hpi_control_param1_get(h_control, HPI_GENERIC_EVENT_ENABLE,
+ event_enable);
}
-u16 hpi_tone_detector_set_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, int threshold)
+u16 hpi_tone_detector_set_threshold(u32 h_control, int threshold)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_TONEDETECTOR_THRESHOLD, (u32)threshold, 0);
+ return hpi_control_param_set(h_control, HPI_TONEDETECTOR_THRESHOLD,
+ (u32)threshold, 0);
}
-u16 hpi_tone_detector_get_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, int *threshold)
+u16 hpi_tone_detector_get_threshold(u32 h_control, int *threshold)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_TONEDETECTOR_THRESHOLD, (u32 *)threshold);
+ return hpi_control_param1_get(h_control, HPI_TONEDETECTOR_THRESHOLD,
+ (u32 *)threshold);
}
-u16 hpi_silence_detector_get_state(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *state)
+u16 hpi_silence_detector_get_state(u32 h_control, u32 *state)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_SILENCEDETECTOR_STATE, state);
+ return hpi_control_param1_get(h_control, HPI_SILENCEDETECTOR_STATE,
+ state);
}
-u16 hpi_silence_detector_set_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 enable)
+u16 hpi_silence_detector_set_enable(u32 h_control, u32 enable)
{
- return hpi_control_param_set(ph_subsys, h_control, HPI_GENERIC_ENABLE,
- (u32)enable, 0);
+ return hpi_control_param_set(h_control, HPI_GENERIC_ENABLE, enable,
+ 0);
}
-u16 hpi_silence_detector_get_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *enable)
+u16 hpi_silence_detector_get_enable(u32 h_control, u32 *enable)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_GENERIC_ENABLE, enable);
+ return hpi_control_param1_get(h_control, HPI_GENERIC_ENABLE, enable);
}
-u16 hpi_silence_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 event_enable)
+u16 hpi_silence_detector_set_event_enable(u32 h_control, u32 event_enable)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_GENERIC_EVENT_ENABLE, event_enable, 0);
+ return hpi_control_param_set(h_control, HPI_GENERIC_EVENT_ENABLE,
+ event_enable, 0);
}
-u16 hpi_silence_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *event_enable)
+u16 hpi_silence_detector_get_event_enable(u32 h_control, u32 *event_enable)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_GENERIC_EVENT_ENABLE, event_enable);
+ return hpi_control_param1_get(h_control, HPI_GENERIC_EVENT_ENABLE,
+ event_enable);
}
-u16 hpi_silence_detector_set_delay(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 delay)
+u16 hpi_silence_detector_set_delay(u32 h_control, u32 delay)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_SILENCEDETECTOR_DELAY, delay, 0);
+ return hpi_control_param_set(h_control, HPI_SILENCEDETECTOR_DELAY,
+ delay, 0);
}
-u16 hpi_silence_detector_get_delay(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *delay)
+u16 hpi_silence_detector_get_delay(u32 h_control, u32 *delay)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_SILENCEDETECTOR_DELAY, delay);
+ return hpi_control_param1_get(h_control, HPI_SILENCEDETECTOR_DELAY,
+ delay);
}
-u16 hpi_silence_detector_set_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, int threshold)
+u16 hpi_silence_detector_set_threshold(u32 h_control, int threshold)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_SILENCEDETECTOR_THRESHOLD, threshold, 0);
+ return hpi_control_param_set(h_control, HPI_SILENCEDETECTOR_THRESHOLD,
+ threshold, 0);
}
-u16 hpi_silence_detector_get_threshold(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, int *threshold)
+u16 hpi_silence_detector_get_threshold(u32 h_control, int *threshold)
{
- return hpi_control_param1_get(ph_subsys, h_control,
+ return hpi_control_param1_get(h_control,
HPI_SILENCEDETECTOR_THRESHOLD, (u32 *)threshold);
}
-u16 hpi_tuner_query_band(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, const u32 index, u16 *pw_band)
+u16 hpi_tuner_query_band(const u32 h_tuner, const u32 index, u16 *pw_band)
{
u32 qr;
u16 err;
- err = hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_BAND, index, 0,
- &qr);
+ err = hpi_control_query(h_tuner, HPI_TUNER_BAND, index, 0, &qr);
*pw_band = (u16)qr;
return err;
}
-u16 hpi_tuner_set_band(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 band)
+u16 hpi_tuner_set_band(u32 h_control, u16 band)
{
- return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_BAND,
- band, 0);
+ return hpi_control_param_set(h_control, HPI_TUNER_BAND, band, 0);
}
-u16 hpi_tuner_get_band(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 *pw_band)
+u16 hpi_tuner_get_band(u32 h_control, u16 *pw_band)
{
u32 band = 0;
u16 error = 0;
- error = hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_BAND,
- &band);
+ error = hpi_control_param1_get(h_control, HPI_TUNER_BAND, &band);
if (pw_band)
*pw_band = (u16)band;
return error;
}
-u16 hpi_tuner_query_frequency(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, const u32 index, const u16 band, u32 *pfreq)
+u16 hpi_tuner_query_frequency(const u32 h_tuner, const u32 index,
+ const u16 band, u32 *pfreq)
{
- return hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_FREQ, index,
- band, pfreq);
+ return hpi_control_query(h_tuner, HPI_TUNER_FREQ, index, band, pfreq);
}
-u16 hpi_tuner_set_frequency(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 freq_ink_hz)
+u16 hpi_tuner_set_frequency(u32 h_control, u32 freq_ink_hz)
{
- return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_FREQ,
- freq_ink_hz, 0);
+ return hpi_control_param_set(h_control, HPI_TUNER_FREQ, freq_ink_hz,
+ 0);
}
-u16 hpi_tuner_get_frequency(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pw_freq_ink_hz)
+u16 hpi_tuner_get_frequency(u32 h_control, u32 *pw_freq_ink_hz)
{
- return hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_FREQ,
+ return hpi_control_param1_get(h_control, HPI_TUNER_FREQ,
pw_freq_ink_hz);
}
-u16 hpi_tuner_query_gain(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, const u32 index, u16 *pw_gain)
+u16 hpi_tuner_query_gain(const u32 h_tuner, const u32 index, u16 *pw_gain)
{
u32 qr;
u16 err;
- err = hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_BAND, index, 0,
- &qr);
+ err = hpi_control_query(h_tuner, HPI_TUNER_BAND, index, 0, &qr);
*pw_gain = (u16)qr;
return err;
}
-u16 hpi_tuner_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short gain)
+u16 hpi_tuner_set_gain(u32 h_control, short gain)
{
- return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_GAIN,
- gain, 0);
+ return hpi_control_param_set(h_control, HPI_TUNER_GAIN, gain, 0);
}
-u16 hpi_tuner_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *pn_gain)
+u16 hpi_tuner_get_gain(u32 h_control, short *pn_gain)
{
u32 gain = 0;
u16 error = 0;
- error = hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_GAIN,
- &gain);
+ error = hpi_control_param1_get(h_control, HPI_TUNER_GAIN, &gain);
if (pn_gain)
*pn_gain = (u16)gain;
return error;
}
-u16 hpi_tuner_getRF_level(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *pw_level)
+u16 hpi_tuner_get_rf_level(u32 h_control, short *pw_level)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
- hm.u.c.attribute = HPI_TUNER_LEVEL;
- hm.u.c.param1 = HPI_TUNER_LEVEL_AVERAGE;
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
+ hm.u.cu.attribute = HPI_TUNER_LEVEL_AVG;
hpi_send_recv(&hm, &hr);
if (pw_level)
- *pw_level = (short)hr.u.c.param1;
+ *pw_level = hr.u.cu.tuner.s_level;
return hr.error;
}
-u16 hpi_tuner_get_rawRF_level(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, short *pw_level)
+u16 hpi_tuner_get_raw_rf_level(u32 h_control, short *pw_level)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
- hm.u.c.attribute = HPI_TUNER_LEVEL;
- hm.u.c.param1 = HPI_TUNER_LEVEL_RAW;
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
+ hm.u.cu.attribute = HPI_TUNER_LEVEL_RAW;
hpi_send_recv(&hm, &hr);
if (pw_level)
- *pw_level = (short)hr.u.c.param1;
+ *pw_level = hr.u.cu.tuner.s_level;
return hr.error;
}
-u16 hpi_tuner_query_deemphasis(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, const u32 index, const u16 band, u32 *pdeemphasis)
+u16 hpi_tuner_query_deemphasis(const u32 h_tuner, const u32 index,
+ const u16 band, u32 *pdeemphasis)
{
- return hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_DEEMPHASIS,
- index, band, pdeemphasis);
+ return hpi_control_query(h_tuner, HPI_TUNER_DEEMPHASIS, index, band,
+ pdeemphasis);
}
-u16 hpi_tuner_set_deemphasis(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 deemphasis)
+u16 hpi_tuner_set_deemphasis(u32 h_control, u32 deemphasis)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_TUNER_DEEMPHASIS, deemphasis, 0);
+ return hpi_control_param_set(h_control, HPI_TUNER_DEEMPHASIS,
+ deemphasis, 0);
}
-u16 hpi_tuner_get_deemphasis(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pdeemphasis)
+u16 hpi_tuner_get_deemphasis(u32 h_control, u32 *pdeemphasis)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_TUNER_DEEMPHASIS, pdeemphasis);
+ return hpi_control_param1_get(h_control, HPI_TUNER_DEEMPHASIS,
+ pdeemphasis);
}
-u16 hpi_tuner_query_program(const struct hpi_hsubsys *ph_subsys,
- const u32 h_tuner, u32 *pbitmap_program)
+u16 hpi_tuner_query_program(const u32 h_tuner, u32 *pbitmap_program)
{
- return hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_PROGRAM, 0, 0,
+ return hpi_control_query(h_tuner, HPI_TUNER_PROGRAM, 0, 0,
pbitmap_program);
}
-u16 hpi_tuner_set_program(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 program)
+u16 hpi_tuner_set_program(u32 h_control, u32 program)
{
- return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_PROGRAM,
- program, 0);
+ return hpi_control_param_set(h_control, HPI_TUNER_PROGRAM, program,
+ 0);
}
-u16 hpi_tuner_get_program(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 *pprogram)
+u16 hpi_tuner_get_program(u32 h_control, u32 *pprogram)
{
- return hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_PROGRAM,
- pprogram);
+ return hpi_control_param1_get(h_control, HPI_TUNER_PROGRAM, pprogram);
}
-u16 hpi_tuner_get_hd_radio_dsp_version(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, char *psz_dsp_version, const u32 string_size)
+u16 hpi_tuner_get_hd_radio_dsp_version(u32 h_control, char *psz_dsp_version,
+ const u32 string_size)
{
return hpi_control_get_string(h_control,
HPI_TUNER_HDRADIO_DSP_VERSION, psz_dsp_version, string_size);
}
-u16 hpi_tuner_get_hd_radio_sdk_version(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, char *psz_sdk_version, const u32 string_size)
+u16 hpi_tuner_get_hd_radio_sdk_version(u32 h_control, char *psz_sdk_version,
+ const u32 string_size)
{
return hpi_control_get_string(h_control,
HPI_TUNER_HDRADIO_SDK_VERSION, psz_sdk_version, string_size);
}
-u16 hpi_tuner_get_status(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u16 *pw_status_mask, u16 *pw_status)
+u16 hpi_tuner_get_status(u32 h_control, u16 *pw_status_mask, u16 *pw_status)
{
u32 status = 0;
u16 error = 0;
- error = hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_STATUS,
- &status);
+ error = hpi_control_param1_get(h_control, HPI_TUNER_STATUS, &status);
if (pw_status) {
if (!error) {
*pw_status_mask = (u16)(status >> 16);
@@ -3005,50 +2651,44 @@ u16 hpi_tuner_get_status(const struct hpi_hsubsys *ph_subsys, u32 h_control,
return error;
}
-u16 hpi_tuner_set_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 mode, u32 value)
+u16 hpi_tuner_set_mode(u32 h_control, u32 mode, u32 value)
{
- return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_MODE,
- mode, value);
+ return hpi_control_param_set(h_control, HPI_TUNER_MODE, mode, value);
}
-u16 hpi_tuner_get_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 mode, u32 *pn_value)
+u16 hpi_tuner_get_mode(u32 h_control, u32 mode, u32 *pn_value)
{
- return hpi_control_param_get(ph_subsys, h_control, HPI_TUNER_MODE,
- mode, 0, pn_value, NULL);
+ return hpi_control_param_get(h_control, HPI_TUNER_MODE, mode, 0,
+ pn_value, NULL);
}
-u16 hpi_tuner_get_hd_radio_signal_quality(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pquality)
+u16 hpi_tuner_get_hd_radio_signal_quality(u32 h_control, u32 *pquality)
{
- return hpi_control_param1_get(ph_subsys, h_control,
+ return hpi_control_param1_get(h_control,
HPI_TUNER_HDRADIO_SIGNAL_QUALITY, pquality);
}
-u16 hpi_tuner_get_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *pblend)
+u16 hpi_tuner_get_hd_radio_signal_blend(u32 h_control, u32 *pblend)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_TUNER_HDRADIO_BLEND, pblend);
+ return hpi_control_param1_get(h_control, HPI_TUNER_HDRADIO_BLEND,
+ pblend);
}
-u16 hpi_tuner_set_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, const u32 blend)
+u16 hpi_tuner_set_hd_radio_signal_blend(u32 h_control, const u32 blend)
{
- return hpi_control_param_set(ph_subsys, h_control,
- HPI_TUNER_HDRADIO_BLEND, blend, 0);
+ return hpi_control_param_set(h_control, HPI_TUNER_HDRADIO_BLEND,
+ blend, 0);
}
-u16 hpi_tuner_getRDS(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- char *p_data)
+u16 hpi_tuner_get_rds(u32 h_control, char *p_data)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_TUNER_RDS;
hpi_send_recv(&hm, &hr);
if (p_data) {
@@ -3059,80 +2699,82 @@ u16 hpi_tuner_getRDS(const struct hpi_hsubsys *ph_subsys, u32 h_control,
return hr.error;
}
-u16 HPI_PAD__get_channel_name(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, char *psz_string, const u32 data_length)
+u16 hpi_pad_get_channel_name(u32 h_control, char *psz_string,
+ const u32 data_length)
{
return hpi_control_get_string(h_control, HPI_PAD_CHANNEL_NAME,
psz_string, data_length);
}
-u16 HPI_PAD__get_artist(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- char *psz_string, const u32 data_length)
+u16 hpi_pad_get_artist(u32 h_control, char *psz_string, const u32 data_length)
{
return hpi_control_get_string(h_control, HPI_PAD_ARTIST, psz_string,
data_length);
}
-u16 HPI_PAD__get_title(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- char *psz_string, const u32 data_length)
+u16 hpi_pad_get_title(u32 h_control, char *psz_string, const u32 data_length)
{
return hpi_control_get_string(h_control, HPI_PAD_TITLE, psz_string,
data_length);
}
-u16 HPI_PAD__get_comment(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- char *psz_string, const u32 data_length)
+u16 hpi_pad_get_comment(u32 h_control, char *psz_string,
+ const u32 data_length)
{
return hpi_control_get_string(h_control, HPI_PAD_COMMENT, psz_string,
data_length);
}
-u16 HPI_PAD__get_program_type(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, u32 *ppTY)
+u16 hpi_pad_get_program_type(u32 h_control, u32 *ppTY)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_PAD_PROGRAM_TYPE, ppTY);
+ return hpi_control_param1_get(h_control, HPI_PAD_PROGRAM_TYPE, ppTY);
}
-u16 HPI_PAD__get_rdsPI(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- u32 *ppI)
+u16 hpi_pad_get_rdsPI(u32 h_control, u32 *ppI)
{
- return hpi_control_param1_get(ph_subsys, h_control,
- HPI_PAD_PROGRAM_ID, ppI);
+ return hpi_control_param1_get(h_control, HPI_PAD_PROGRAM_ID, ppI);
}
-u16 hpi_volume_query_channels(const struct hpi_hsubsys *ph_subsys,
- const u32 h_volume, u32 *p_channels)
+u16 hpi_volume_query_channels(const u32 h_volume, u32 *p_channels)
{
- return hpi_control_query(ph_subsys, h_volume, HPI_VOLUME_NUM_CHANNELS,
- 0, 0, p_channels);
+ return hpi_control_query(h_volume, HPI_VOLUME_NUM_CHANNELS, 0, 0,
+ p_channels);
}
-u16 hpi_volume_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_log_gain[HPI_MAX_CHANNELS]
+u16 hpi_volume_set_gain(u32 h_control, short an_log_gain[HPI_MAX_CHANNELS]
)
{
return hpi_control_log_set2(h_control, HPI_VOLUME_GAIN,
an_log_gain[0], an_log_gain[1]);
}
-u16 hpi_volume_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_log_gain[HPI_MAX_CHANNELS]
+u16 hpi_volume_get_gain(u32 h_control, short an_log_gain[HPI_MAX_CHANNELS]
)
{
- return hpi_control_log_get2(ph_subsys, h_control, HPI_VOLUME_GAIN,
+ return hpi_control_log_get2(h_control, HPI_VOLUME_GAIN,
&an_log_gain[0], &an_log_gain[1]);
}
-u16 hpi_volume_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB)
+u16 hpi_volume_set_mute(u32 h_control, u32 mute)
+{
+ return hpi_control_param_set(h_control, HPI_VOLUME_MUTE, mute, 0);
+}
+
+u16 hpi_volume_get_mute(u32 h_control, u32 *mute)
+{
+ return hpi_control_param1_get(h_control, HPI_VOLUME_MUTE, mute);
+}
+
+u16 hpi_volume_query_range(u32 h_control, short *min_gain_01dB,
+ short *max_gain_01dB, short *step_gain_01dB)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_VOLUME_RANGE;
hpi_send_recv(&hm, &hr);
@@ -3150,16 +2792,17 @@ u16 hpi_volume_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control,
return hr.error;
}
-u16 hpi_volume_auto_fade_profile(const struct hpi_hsubsys *ph_subsys,
- u32 h_control, short an_stop_gain0_01dB[HPI_MAX_CHANNELS],
- u32 duration_ms, u16 profile)
+u16 hpi_volume_auto_fade_profile(u32 h_control,
+ short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms,
+ u16 profile)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_SET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
memcpy(hm.u.c.an_log_value, an_stop_gain0_01dB,
sizeof(short) * HPI_MAX_CHANNELS);
@@ -3173,21 +2816,31 @@ u16 hpi_volume_auto_fade_profile(const struct hpi_hsubsys *ph_subsys,
return hr.error;
}
-u16 hpi_volume_auto_fade(const struct hpi_hsubsys *ph_subsys, u32 h_control,
+u16 hpi_volume_auto_fade(u32 h_control,
short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms)
{
- return hpi_volume_auto_fade_profile(ph_subsys, h_control,
- an_stop_gain0_01dB, duration_ms, HPI_VOLUME_AUTOFADE_LOG);
+ return hpi_volume_auto_fade_profile(h_control, an_stop_gain0_01dB,
+ duration_ms, HPI_VOLUME_AUTOFADE_LOG);
}
-u16 hpi_vox_set_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short an_gain0_01dB)
+u16 hpi_volume_query_auto_fade_profile(const u32 h_volume, const u32 i,
+ u16 *profile)
+{
+ u16 e;
+ u32 u;
+ e = hpi_control_query(h_volume, HPI_VOLUME_AUTOFADE, i, 0, &u);
+ *profile = (u16)u;
+ return e;
+}
+
+u16 hpi_vox_set_threshold(u32 h_control, short an_gain0_01dB)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_SET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_VOX_THRESHOLD;
hm.u.c.an_log_value[0] = an_gain0_01dB;
@@ -3197,14 +2850,14 @@ u16 hpi_vox_set_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
return hr.error;
}
-u16 hpi_vox_get_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
- short *an_gain0_01dB)
+u16 hpi_vox_get_threshold(u32 h_control, short *an_gain0_01dB)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL,
HPI_CONTROL_GET_STATE);
- u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index);
+ if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index))
+ return HPI_ERROR_INVALID_HANDLE;
hm.u.c.attribute = HPI_VOX_THRESHOLD;
hpi_send_recv(&hm, &hr);
@@ -3213,728 +2866,3 @@ u16 hpi_vox_get_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control,
return hr.error;
}
-
-static size_t strv_packet_size = MIN_STRV_PACKET_SIZE;
-
-static size_t entity_type_to_size[LAST_ENTITY_TYPE] = {
- 0,
- sizeof(struct hpi_entity),
- sizeof(void *),
-
- sizeof(int),
- sizeof(float),
- sizeof(double),
-
- sizeof(char),
- sizeof(char),
-
- 4 * sizeof(char),
- 16 * sizeof(char),
- 6 * sizeof(char),
-};
-
-static inline size_t hpi_entity_size(struct hpi_entity *entity_ptr)
-{
- return entity_ptr->header.size;
-}
-
-static inline size_t hpi_entity_header_size(struct hpi_entity *entity_ptr)
-{
- return sizeof(entity_ptr->header);
-}
-
-static inline size_t hpi_entity_value_size(struct hpi_entity *entity_ptr)
-{
- return hpi_entity_size(entity_ptr) -
- hpi_entity_header_size(entity_ptr);
-}
-
-static inline size_t hpi_entity_item_count(struct hpi_entity *entity_ptr)
-{
- return hpi_entity_value_size(entity_ptr) /
- entity_type_to_size[entity_ptr->header.type];
-}
-
-static inline struct hpi_entity *hpi_entity_ptr_to_next(struct hpi_entity
- *entity_ptr)
-{
- return (void *)(((u8 *)entity_ptr) + hpi_entity_size(entity_ptr));
-}
-
-static inline u16 hpi_entity_check_type(const enum e_entity_type t)
-{
- if (t >= 0 && t < STR_TYPE_FIELD_MAX)
- return 0;
- return HPI_ERROR_ENTITY_TYPE_INVALID;
-}
-
-static inline u16 hpi_entity_check_role(const enum e_entity_role r)
-{
- if (r >= 0 && r < STR_ROLE_FIELD_MAX)
- return 0;
- return HPI_ERROR_ENTITY_ROLE_INVALID;
-}
-
-static u16 hpi_entity_get_next(struct hpi_entity *entity, int recursive_flag,
- void *guard_p, struct hpi_entity **next)
-{
- HPI_DEBUG_ASSERT(entity != NULL);
- HPI_DEBUG_ASSERT(next != NULL);
- HPI_DEBUG_ASSERT(hpi_entity_size(entity) != 0);
-
- if (guard_p <= (void *)entity) {
- *next = NULL;
- return 0;
- }
-
- if (recursive_flag && entity->header.type == entity_type_sequence)
- *next = (struct hpi_entity *)entity->value;
- else
- *next = (struct hpi_entity *)hpi_entity_ptr_to_next(entity);
-
- if (guard_p <= (void *)*next) {
- *next = NULL;
- return 0;
- }
-
- HPI_DEBUG_ASSERT(guard_p >= (void *)hpi_entity_ptr_to_next(*next));
- return 0;
-}
-
-u16 hpi_entity_find_next(struct hpi_entity *container_entity,
- enum e_entity_type type, enum e_entity_role role, int recursive_flag,
- struct hpi_entity **current_match)
-{
- struct hpi_entity *tmp = NULL;
- void *guard_p = NULL;
-
- HPI_DEBUG_ASSERT(container_entity != NULL);
- guard_p = hpi_entity_ptr_to_next(container_entity);
-
- if (*current_match != NULL)
- hpi_entity_get_next(*current_match, recursive_flag, guard_p,
- &tmp);
- else
- hpi_entity_get_next(container_entity, 1, guard_p, &tmp);
-
- while (tmp) {
- u16 err;
-
- HPI_DEBUG_ASSERT((void *)tmp >= (void *)container_entity);
-
- if ((!type || tmp->header.type == type) && (!role
- || tmp->header.role == role)) {
- *current_match = tmp;
- return 0;
- }
-
- err = hpi_entity_get_next(tmp, recursive_flag, guard_p,
- current_match);
- if (err)
- return err;
-
- tmp = *current_match;
- }
-
- *current_match = NULL;
- return 0;
-}
-
-void hpi_entity_free(struct hpi_entity *entity)
-{
- kfree(entity);
-}
-
-static u16 hpi_entity_alloc_and_copy(struct hpi_entity *src,
- struct hpi_entity **dst)
-{
- size_t buf_size;
- HPI_DEBUG_ASSERT(dst != NULL);
- HPI_DEBUG_ASSERT(src != NULL);
-
- buf_size = hpi_entity_size(src);
- *dst = kmalloc(buf_size, GFP_KERNEL);
- if (*dst == NULL)
- return HPI_ERROR_MEMORY_ALLOC;
- memcpy(*dst, src, buf_size);
- return 0;
-}
-
-u16 hpi_universal_info(const struct hpi_hsubsys *ph_subsys, u32 hC,
- struct hpi_entity **info)
-{
- struct hpi_msg_strv hm;
- struct hpi_res_strv *phr;
- u16 hpi_err;
- int remaining_attempts = 2;
- size_t resp_packet_size = 1024;
-
- *info = NULL;
-
- while (remaining_attempts--) {
- phr = kmalloc(resp_packet_size, GFP_KERNEL);
- HPI_DEBUG_ASSERT(phr != NULL);
-
- hpi_init_message_responseV1(&hm.h, (u16)sizeof(hm), &phr->h,
- (u16)resp_packet_size, HPI_OBJ_CONTROL,
- HPI_CONTROL_GET_INFO);
- u32TOINDEXES(hC, &hm.h.adapter_index, &hm.h.obj_index);
-
- hm.strv.header.size = sizeof(hm.strv);
- phr->strv.header.size = resp_packet_size - sizeof(phr->h);
-
- hpi_send_recv((struct hpi_message *)&hm.h,
- (struct hpi_response *)&phr->h);
- if (phr->h.error == HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL) {
-
- HPI_DEBUG_ASSERT(phr->h.specific_error >
- MIN_STRV_PACKET_SIZE
- && phr->h.specific_error < 1500);
- resp_packet_size = phr->h.specific_error;
- } else {
- remaining_attempts = 0;
- if (!phr->h.error)
- hpi_entity_alloc_and_copy(&phr->strv, info);
- }
-
- hpi_err = phr->h.error;
- kfree(phr);
- }
-
- return hpi_err;
-}
-
-u16 hpi_universal_get(const struct hpi_hsubsys *ph_subsys, u32 hC,
- struct hpi_entity **value)
-{
- struct hpi_msg_strv hm;
- struct hpi_res_strv *phr;
- u16 hpi_err;
- int remaining_attempts = 2;
-
- *value = NULL;
-
- while (remaining_attempts--) {
- phr = kmalloc(strv_packet_size, GFP_KERNEL);
- if (!phr)
- return HPI_ERROR_MEMORY_ALLOC;
-
- hpi_init_message_responseV1(&hm.h, (u16)sizeof(hm), &phr->h,
- (u16)strv_packet_size, HPI_OBJ_CONTROL,
- HPI_CONTROL_GET_STATE);
- u32TOINDEXES(hC, &hm.h.adapter_index, &hm.h.obj_index);
-
- hm.strv.header.size = sizeof(hm.strv);
- phr->strv.header.size = strv_packet_size - sizeof(phr->h);
-
- hpi_send_recv((struct hpi_message *)&hm.h,
- (struct hpi_response *)&phr->h);
- if (phr->h.error == HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL) {
-
- HPI_DEBUG_ASSERT(phr->h.specific_error >
- MIN_STRV_PACKET_SIZE
- && phr->h.specific_error < 1000);
- strv_packet_size = phr->h.specific_error;
- } else {
- remaining_attempts = 0;
- if (!phr->h.error)
- hpi_entity_alloc_and_copy(&phr->strv, value);
- }
-
- hpi_err = phr->h.error;
- kfree(phr);
- }
-
- return hpi_err;
-}
-
-u16 hpi_universal_set(const struct hpi_hsubsys *ph_subsys, u32 hC,
- struct hpi_entity *value)
-{
- struct hpi_msg_strv *phm;
- struct hpi_res_strv hr;
-
- phm = kmalloc(sizeof(phm->h) + value->header.size, GFP_KERNEL);
- HPI_DEBUG_ASSERT(phm != NULL);
-
- hpi_init_message_responseV1(&phm->h,
- sizeof(phm->h) + value->header.size, &hr.h, sizeof(hr),
- HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE);
- u32TOINDEXES(hC, &phm->h.adapter_index, &phm->h.obj_index);
- hr.strv.header.size = sizeof(hr.strv);
-
- memcpy(&phm->strv, value, value->header.size);
- hpi_send_recv((struct hpi_message *)&phm->h,
- (struct hpi_response *)&hr.h);
-
- return hr.h.error;
-}
-
-u16 hpi_entity_alloc_and_pack(const enum e_entity_type type,
- const size_t item_count, const enum e_entity_role role, void *value,
- struct hpi_entity **entity)
-{
- size_t bytes_to_copy, total_size;
- u16 hE = 0;
- *entity = NULL;
-
- hE = hpi_entity_check_type(type);
- if (hE)
- return hE;
-
- HPI_DEBUG_ASSERT(role > entity_role_null && type < LAST_ENTITY_TYPE);
-
- bytes_to_copy = entity_type_to_size[type] * item_count;
- total_size = hpi_entity_header_size(*entity) + bytes_to_copy;
-
- HPI_DEBUG_ASSERT(total_size >= hpi_entity_header_size(*entity)
- && total_size < STR_SIZE_FIELD_MAX);
-
- *entity = kmalloc(total_size, GFP_KERNEL);
- if (*entity == NULL)
- return HPI_ERROR_MEMORY_ALLOC;
- memcpy((*entity)->value, value, bytes_to_copy);
- (*entity)->header.size =
- hpi_entity_header_size(*entity) + bytes_to_copy;
- (*entity)->header.type = type;
- (*entity)->header.role = role;
- return 0;
-}
-
-u16 hpi_entity_copy_value_from(struct hpi_entity *entity,
- enum e_entity_type type, size_t item_count, void *value_dst_p)
-{
- size_t bytes_to_copy;
-
- if (entity->header.type != type)
- return HPI_ERROR_ENTITY_TYPE_MISMATCH;
-
- if (hpi_entity_item_count(entity) != item_count)
- return HPI_ERROR_ENTITY_ITEM_COUNT;
-
- bytes_to_copy = entity_type_to_size[type] * item_count;
- memcpy(value_dst_p, entity->value, bytes_to_copy);
- return 0;
-}
-
-u16 hpi_entity_unpack(struct hpi_entity *entity, enum e_entity_type *type,
- size_t *item_count, enum e_entity_role *role, void **value)
-{
- u16 err = 0;
- HPI_DEBUG_ASSERT(entity != NULL);
-
- if (type)
- *type = entity->header.type;
-
- if (role)
- *role = entity->header.role;
-
- if (value)
- *value = entity->value;
-
- if (item_count != NULL) {
- if (entity->header.type == entity_type_sequence) {
- void *guard_p = hpi_entity_ptr_to_next(entity);
- struct hpi_entity *next = NULL;
- void *contents = entity->value;
-
- *item_count = 0;
- while (contents < guard_p) {
- (*item_count)++;
- err = hpi_entity_get_next(contents, 0,
- guard_p, &next);
- if (next == NULL || err)
- break;
- contents = next;
- }
- } else {
- *item_count = hpi_entity_item_count(entity);
- }
- }
- return err;
-}
-
-u16 hpi_gpio_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u32 *ph_gpio, u16 *pw_number_input_bits, u16 *pw_number_output_bits)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_OPEN);
- hm.adapter_index = adapter_index;
-
- hpi_send_recv(&hm, &hr);
-
- if (hr.error == 0) {
- *ph_gpio =
- hpi_indexes_to_handle(HPI_OBJ_GPIO, adapter_index, 0);
- if (pw_number_input_bits)
- *pw_number_input_bits = hr.u.l.number_input_bits;
- if (pw_number_output_bits)
- *pw_number_output_bits = hr.u.l.number_output_bits;
- } else
- *ph_gpio = 0;
- return hr.error;
-}
-
-u16 hpi_gpio_read_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
- u16 bit_index, u16 *pw_bit_data)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_READ_BIT);
- u32TOINDEX(h_gpio, &hm.adapter_index);
- hm.u.l.bit_index = bit_index;
-
- hpi_send_recv(&hm, &hr);
-
- *pw_bit_data = hr.u.l.bit_data[0];
- return hr.error;
-}
-
-u16 hpi_gpio_read_all_bits(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
- u16 aw_all_bit_data[4]
- )
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_READ_ALL);
- u32TOINDEX(h_gpio, &hm.adapter_index);
-
- hpi_send_recv(&hm, &hr);
-
- if (aw_all_bit_data) {
- aw_all_bit_data[0] = hr.u.l.bit_data[0];
- aw_all_bit_data[1] = hr.u.l.bit_data[1];
- aw_all_bit_data[2] = hr.u.l.bit_data[2];
- aw_all_bit_data[3] = hr.u.l.bit_data[3];
- }
- return hr.error;
-}
-
-u16 hpi_gpio_write_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
- u16 bit_index, u16 bit_data)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_WRITE_BIT);
- u32TOINDEX(h_gpio, &hm.adapter_index);
- hm.u.l.bit_index = bit_index;
- hm.u.l.bit_data = bit_data;
-
- hpi_send_recv(&hm, &hr);
-
- return hr.error;
-}
-
-u16 hpi_gpio_write_status(const struct hpi_hsubsys *ph_subsys, u32 h_gpio,
- u16 aw_all_bit_data[4]
- )
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO,
- HPI_GPIO_WRITE_STATUS);
- u32TOINDEX(h_gpio, &hm.adapter_index);
-
- hpi_send_recv(&hm, &hr);
-
- if (aw_all_bit_data) {
- aw_all_bit_data[0] = hr.u.l.bit_data[0];
- aw_all_bit_data[1] = hr.u.l.bit_data[1];
- aw_all_bit_data[2] = hr.u.l.bit_data[2];
- aw_all_bit_data[3] = hr.u.l.bit_data[3];
- }
- return hr.error;
-}
-
-u16 hpi_async_event_open(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u32 *ph_async)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT,
- HPI_ASYNCEVENT_OPEN);
- hm.adapter_index = adapter_index;
-
- hpi_send_recv(&hm, &hr);
-
- if (hr.error == 0)
-
- *ph_async =
- hpi_indexes_to_handle(HPI_OBJ_ASYNCEVENT,
- adapter_index, 0);
- else
- *ph_async = 0;
- return hr.error;
-
-}
-
-u16 hpi_async_event_close(const struct hpi_hsubsys *ph_subsys, u32 h_async)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT,
- HPI_ASYNCEVENT_OPEN);
- u32TOINDEX(h_async, &hm.adapter_index);
-
- hpi_send_recv(&hm, &hr);
-
- return hr.error;
-}
-
-u16 hpi_async_event_wait(const struct hpi_hsubsys *ph_subsys, u32 h_async,
- u16 maximum_events, struct hpi_async_event *p_events,
- u16 *pw_number_returned)
-{
-
- return 0;
-}
-
-u16 hpi_async_event_get_count(const struct hpi_hsubsys *ph_subsys,
- u32 h_async, u16 *pw_count)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT,
- HPI_ASYNCEVENT_GETCOUNT);
- u32TOINDEX(h_async, &hm.adapter_index);
-
- hpi_send_recv(&hm, &hr);
-
- if (hr.error == 0)
- if (pw_count)
- *pw_count = hr.u.as.u.count.count;
-
- return hr.error;
-}
-
-u16 hpi_async_event_get(const struct hpi_hsubsys *ph_subsys, u32 h_async,
- u16 maximum_events, struct hpi_async_event *p_events,
- u16 *pw_number_returned)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT,
- HPI_ASYNCEVENT_GET);
- u32TOINDEX(h_async, &hm.adapter_index);
-
- hpi_send_recv(&hm, &hr);
- if (!hr.error) {
- memcpy(p_events, &hr.u.as.u.event,
- sizeof(struct hpi_async_event));
- *pw_number_returned = 1;
- }
-
- return hr.error;
-}
-
-u16 hpi_nv_memory_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u32 *ph_nv_memory, u16 *pw_size_in_bytes)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_NVMEMORY,
- HPI_NVMEMORY_OPEN);
- hm.adapter_index = adapter_index;
-
- hpi_send_recv(&hm, &hr);
-
- if (hr.error == 0) {
- *ph_nv_memory =
- hpi_indexes_to_handle(HPI_OBJ_NVMEMORY, adapter_index,
- 0);
- if (pw_size_in_bytes)
- *pw_size_in_bytes = hr.u.n.size_in_bytes;
- } else
- *ph_nv_memory = 0;
- return hr.error;
-}
-
-u16 hpi_nv_memory_read_byte(const struct hpi_hsubsys *ph_subsys,
- u32 h_nv_memory, u16 index, u16 *pw_data)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_NVMEMORY,
- HPI_NVMEMORY_READ_BYTE);
- u32TOINDEX(h_nv_memory, &hm.adapter_index);
- hm.u.n.address = index;
-
- hpi_send_recv(&hm, &hr);
-
- *pw_data = hr.u.n.data;
- return hr.error;
-}
-
-u16 hpi_nv_memory_write_byte(const struct hpi_hsubsys *ph_subsys,
- u32 h_nv_memory, u16 index, u16 data)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_NVMEMORY,
- HPI_NVMEMORY_WRITE_BYTE);
- u32TOINDEX(h_nv_memory, &hm.adapter_index);
- hm.u.n.address = index;
- hm.u.n.data = data;
-
- hpi_send_recv(&hm, &hr);
-
- return hr.error;
-}
-
-u16 hpi_profile_open_all(const struct hpi_hsubsys *ph_subsys,
- u16 adapter_index, u16 profile_index, u32 *ph_profile,
- u16 *pw_max_profiles)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE,
- HPI_PROFILE_OPEN_ALL);
- hm.adapter_index = adapter_index;
- hm.obj_index = profile_index;
- hpi_send_recv(&hm, &hr);
-
- *pw_max_profiles = hr.u.p.u.o.max_profiles;
- if (hr.error == 0)
- *ph_profile =
- hpi_indexes_to_handle(HPI_OBJ_PROFILE, adapter_index,
- profile_index);
- else
- *ph_profile = 0;
- return hr.error;
-}
-
-u16 hpi_profile_get(const struct hpi_hsubsys *ph_subsys, u32 h_profile,
- u16 bin_index, u16 *pw_seconds, u32 *pmicro_seconds, u32 *pcall_count,
- u32 *pmax_micro_seconds, u32 *pmin_micro_seconds)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE, HPI_PROFILE_GET);
- u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index);
- hm.u.p.bin_index = bin_index;
- hpi_send_recv(&hm, &hr);
- if (pw_seconds)
- *pw_seconds = hr.u.p.u.t.seconds;
- if (pmicro_seconds)
- *pmicro_seconds = hr.u.p.u.t.micro_seconds;
- if (pcall_count)
- *pcall_count = hr.u.p.u.t.call_count;
- if (pmax_micro_seconds)
- *pmax_micro_seconds = hr.u.p.u.t.max_micro_seconds;
- if (pmin_micro_seconds)
- *pmin_micro_seconds = hr.u.p.u.t.min_micro_seconds;
- return hr.error;
-}
-
-u16 hpi_profile_get_utilization(const struct hpi_hsubsys *ph_subsys,
- u32 h_profile, u32 *putilization)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE,
- HPI_PROFILE_GET_UTILIZATION);
- u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index);
- hpi_send_recv(&hm, &hr);
- if (hr.error) {
- if (putilization)
- *putilization = 0;
- } else {
- if (putilization)
- *putilization = hr.u.p.u.t.call_count;
- }
- return hr.error;
-}
-
-u16 hpi_profile_get_name(const struct hpi_hsubsys *ph_subsys, u32 h_profile,
- u16 bin_index, char *sz_name, u16 name_length)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE,
- HPI_PROFILE_GET_NAME);
- u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index);
- hm.u.p.bin_index = bin_index;
- hpi_send_recv(&hm, &hr);
- if (hr.error) {
- if (sz_name)
- strcpy(sz_name, "??");
- } else {
- if (sz_name)
- memcpy(sz_name, (char *)hr.u.p.u.n.sz_name,
- name_length);
- }
- return hr.error;
-}
-
-u16 hpi_profile_start_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE,
- HPI_PROFILE_START_ALL);
- u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index);
- hpi_send_recv(&hm, &hr);
-
- return hr.error;
-}
-
-u16 hpi_profile_stop_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE,
- HPI_PROFILE_STOP_ALL);
- u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index);
- hpi_send_recv(&hm, &hr);
-
- return hr.error;
-}
-
-u16 hpi_watchdog_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index,
- u32 *ph_watchdog)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_WATCHDOG,
- HPI_WATCHDOG_OPEN);
- hm.adapter_index = adapter_index;
-
- hpi_send_recv(&hm, &hr);
-
- if (hr.error == 0)
- *ph_watchdog =
- hpi_indexes_to_handle(HPI_OBJ_WATCHDOG, adapter_index,
- 0);
- else
- *ph_watchdog = 0;
- return hr.error;
-}
-
-u16 hpi_watchdog_set_time(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog,
- u32 time_millisec)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_WATCHDOG,
- HPI_WATCHDOG_SET_TIME);
- u32TOINDEX(h_watchdog, &hm.adapter_index);
- hm.u.w.time_ms = time_millisec;
-
- hpi_send_recv(&hm, &hr);
-
- return hr.error;
-}
-
-u16 hpi_watchdog_ping(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog)
-{
- struct hpi_message hm;
- struct hpi_response hr;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_WATCHDOG,
- HPI_WATCHDOG_PING);
- u32TOINDEX(h_watchdog, &hm.adapter_index);
-
- hpi_send_recv(&hm, &hr);
-
- return hr.error;
-}
diff --git a/sound/pci/asihpi/hpimsginit.c b/sound/pci/asihpi/hpimsginit.c
index 8e1d099ed7e4..a3fdcbf4990e 100644
--- a/sound/pci/asihpi/hpimsginit.c
+++ b/sound/pci/asihpi/hpimsginit.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Hardware Programming Interface (HPI) Utility functions.
@@ -23,6 +12,7 @@
#include "hpi_internal.h"
#include "hpimsginit.h"
+#include <linux/nospec.h>
/* The actual message size for each object type */
static u16 msg_size[HPI_OBJ_MAXINDEX + 1] = HPI_MESSAGE_SIZE_BY_OBJECT;
@@ -32,40 +22,32 @@ static u16 res_size[HPI_OBJ_MAXINDEX + 1] = HPI_RESPONSE_SIZE_BY_OBJECT;
static u16 gwSSX2_bypass;
/** \internal
- * Used by ASIO driver to disable SSX2 for a single process
- * \param phSubSys Pointer to HPI subsystem handle.
- * \param wBypass New bypass setting 0 = off, nonzero = on
- * \return Previous bypass setting.
- */
-u16 hpi_subsys_ssx2_bypass(const struct hpi_hsubsys *ph_subsys, u16 bypass)
-{
- u16 old_value = gwSSX2_bypass;
-
- gwSSX2_bypass = bypass;
-
- return old_value;
-}
-
-/** \internal
* initialize the HPI message structure
*/
static void hpi_init_message(struct hpi_message *phm, u16 object,
u16 function)
{
- memset(phm, 0, sizeof(*phm));
- if ((object > 0) && (object <= HPI_OBJ_MAXINDEX))
- phm->size = msg_size[object];
- else
- phm->size = sizeof(*phm);
+ u16 size;
+
+ if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) {
+ object = array_index_nospec(object, HPI_OBJ_MAXINDEX + 1);
+ size = msg_size[object];
+ } else {
+ size = sizeof(*phm);
+ }
+
+ memset(phm, 0, size);
+ phm->size = size;
if (gwSSX2_bypass)
phm->type = HPI_TYPE_SSX2BYPASS_MESSAGE;
else
- phm->type = HPI_TYPE_MESSAGE;
+ phm->type = HPI_TYPE_REQUEST;
phm->object = object;
phm->function = function;
phm->version = 0;
- /* Expect adapter index to be set by caller */
+ phm->adapter_index = HPI_ADAPTER_INDEX_INVALID;
+ /* Expect actual adapter index to be set by caller */
}
/** \internal
@@ -74,12 +56,18 @@ static void hpi_init_message(struct hpi_message *phm, u16 object,
void hpi_init_response(struct hpi_response *phr, u16 object, u16 function,
u16 error)
{
+ u16 size;
+
+ if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) {
+ object = array_index_nospec(object, HPI_OBJ_MAXINDEX + 1);
+ size = res_size[object];
+ } else {
+ size = sizeof(*phr);
+ }
+
memset(phr, 0, sizeof(*phr));
+ phr->size = size;
phr->type = HPI_TYPE_RESPONSE;
- if ((object > 0) && (object <= HPI_OBJ_MAXINDEX))
- phr->size = res_size[object];
- else
- phr->size = sizeof(*phr);
phr->object = object;
phr->function = function;
phr->error = error;
@@ -100,10 +88,10 @@ void hpi_init_message_response(struct hpi_message *phm,
static void hpi_init_messageV1(struct hpi_message_header *phm, u16 size,
u16 object, u16 function)
{
- memset(phm, 0, sizeof(*phm));
+ memset(phm, 0, size);
if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) {
phm->size = size;
- phm->type = HPI_TYPE_MESSAGE;
+ phm->type = HPI_TYPE_REQUEST;
phm->object = object;
phm->function = function;
phm->version = 1;
@@ -114,7 +102,9 @@ static void hpi_init_messageV1(struct hpi_message_header *phm, u16 size,
void hpi_init_responseV1(struct hpi_response_header *phr, u16 size,
u16 object, u16 function)
{
- memset(phr, 0, sizeof(*phr));
+ (void)object;
+ (void)function;
+ memset(phr, 0, size);
phr->size = size;
phr->version = 1;
phr->type = HPI_TYPE_RESPONSE;
diff --git a/sound/pci/asihpi/hpimsginit.h b/sound/pci/asihpi/hpimsginit.h
index 864ad020c9b3..c64126b30f8d 100644
--- a/sound/pci/asihpi/hpimsginit.h
+++ b/sound/pci/asihpi/hpimsginit.h
@@ -1,31 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Hardware Programming Interface (HPI) Utility functions
(C) Copyright AudioScience Inc. 2007
*******************************************************************************/
/* Initialise response headers, or msg/response pairs.
-Note that it is valid to just init a response e.g. when a lower level is preparing
-a response to a message.
-However, when sending a message, a matching response buffer always must be prepared
+Note that it is valid to just init a response e.g. when a lower level is
+preparing a response to a message.
+However, when sending a message, a matching response buffer must always be
+prepared.
*/
+#ifndef _HPIMSGINIT_H_
+#define _HPIMSGINIT_H_
+
void hpi_init_response(struct hpi_response *phr, u16 object, u16 function,
u16 error);
@@ -38,3 +31,5 @@ void hpi_init_responseV1(struct hpi_response_header *phr, u16 size,
void hpi_init_message_responseV1(struct hpi_message_header *phm, u16 msg_size,
struct hpi_response_header *phr, u16 res_size, u16 object,
u16 function);
+
+#endif /* _HPIMSGINIT_H_ */
diff --git a/sound/pci/asihpi/hpimsgx.c b/sound/pci/asihpi/hpimsgx.c
index f01ab964f602..b68e6bfbbfba 100644
--- a/sound/pci/asihpi/hpimsgx.c
+++ b/sound/pci/asihpi/hpimsgx.c
@@ -1,38 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-Extended Message Function With Response Cacheing
+Extended Message Function With Response Caching
(C) Copyright AudioScience Inc. 2002
*****************************************************************************/
#define SOURCEFILE_NAME "hpimsgx.c"
#include "hpi_internal.h"
+#include "hpi_version.h"
#include "hpimsginit.h"
+#include "hpicmn.h"
#include "hpimsgx.h"
#include "hpidebug.h"
-static struct pci_device_id asihpi_pci_tbl[] = {
+static const struct pci_device_id asihpi_pci_tbl[] = {
#include "hpipcida.h"
};
static struct hpios_spinlock msgx_lock;
static hpi_handler_func *hpi_entry_points[HPI_MAX_ADAPTERS];
+static int logging_enabled = 1;
static hpi_handler_func *hpi_lookup_entry_point_function(const struct hpi_pci
*pci_info)
@@ -42,22 +34,24 @@ static hpi_handler_func *hpi_lookup_entry_point_function(const struct hpi_pci
for (i = 0; asihpi_pci_tbl[i].vendor != 0; i++) {
if (asihpi_pci_tbl[i].vendor != PCI_ANY_ID
- && asihpi_pci_tbl[i].vendor != pci_info->vendor_id)
+ && asihpi_pci_tbl[i].vendor !=
+ pci_info->pci_dev->vendor)
continue;
if (asihpi_pci_tbl[i].device != PCI_ANY_ID
- && asihpi_pci_tbl[i].device != pci_info->device_id)
+ && asihpi_pci_tbl[i].device !=
+ pci_info->pci_dev->device)
continue;
if (asihpi_pci_tbl[i].subvendor != PCI_ANY_ID
&& asihpi_pci_tbl[i].subvendor !=
- pci_info->subsys_vendor_id)
+ pci_info->pci_dev->subsystem_vendor)
continue;
if (asihpi_pci_tbl[i].subdevice != PCI_ANY_ID
&& asihpi_pci_tbl[i].subdevice !=
- pci_info->subsys_device_id)
+ pci_info->pci_dev->subsystem_device)
continue;
- HPI_DEBUG_LOG(DEBUG, " %x,%lu\n", i,
- asihpi_pci_tbl[i].driver_data);
+ /* HPI_DEBUG_LOG(DEBUG, " %x,%lx\n", i,
+ asihpi_pci_tbl[i].driver_data); */
return (hpi_handler_func *) asihpi_pci_tbl[i].driver_data;
}
@@ -67,21 +61,12 @@ static hpi_handler_func *hpi_lookup_entry_point_function(const struct hpi_pci
static inline void hw_entry_point(struct hpi_message *phm,
struct hpi_response *phr)
{
-
- hpi_handler_func *ep;
-
- if (phm->adapter_index < HPI_MAX_ADAPTERS) {
- ep = (hpi_handler_func *) hpi_entry_points[phm->
- adapter_index];
- if (ep) {
- HPI_DEBUG_MESSAGE(DEBUG, phm);
- ep(phm, phr);
- HPI_DEBUG_RESPONSE(phr);
- return;
- }
- }
- hpi_init_response(phr, phm->object, phm->function,
- HPI_ERROR_PROCESSING_MESSAGE);
+ if ((phm->adapter_index < HPI_MAX_ADAPTERS)
+ && hpi_entry_points[phm->adapter_index])
+ hpi_entry_points[phm->adapter_index] (phm, phr);
+ else
+ hpi_init_response(phr, phm->object, phm->function,
+ HPI_ERROR_PROCESSING_MESSAGE);
}
static void adapter_open(struct hpi_message *phm, struct hpi_response *phr);
@@ -100,6 +85,7 @@ static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner);
static void HPIMSGX__reset(u16 adapter_index);
+
static u16 HPIMSGX__init(struct hpi_message *phm, struct hpi_response *phr);
static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner);
@@ -107,11 +93,6 @@ static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner);
#pragma pack(push, 1)
#endif
-struct hpi_subsys_response {
- struct hpi_response_header h;
- struct hpi_subsys_res s;
-};
-
struct hpi_adapter_response {
struct hpi_response_header h;
struct hpi_adapter_res a;
@@ -153,8 +134,6 @@ static struct hpi_stream_response
static struct hpi_mixer_response rESP_HPI_MIXER_OPEN[HPI_MAX_ADAPTERS];
-static struct hpi_subsys_response gRESP_HPI_SUBSYS_FIND_ADAPTERS;
-
static struct adapter_info aDAPTER_INFO[HPI_MAX_ADAPTERS];
/* use these to keep track of opens from user mode apps/DLLs */
@@ -167,6 +146,11 @@ static struct asi_open_state
static void subsys_message(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
+ if (phm->adapter_index != HPI_ADAPTER_INDEX_INVALID)
+ HPI_DEBUG_LOG(WARNING,
+ "suspicious adapter index %d in subsys message 0x%x.\n",
+ phm->adapter_index, phm->function);
+
switch (phm->function) {
case HPI_SUBSYS_GET_VERSION:
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
@@ -188,7 +172,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr,
/* Initialize this module's internal state */
hpios_msgxlock_init(&msgx_lock);
memset(&hpi_entry_points, 0, sizeof(hpi_entry_points));
- hpios_locked_mem_init();
/* Init subsys_findadapters response to no-adapters */
HPIMSGX__reset(HPIMSGX_ALLADAPTERS);
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
@@ -199,90 +182,23 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr,
case HPI_SUBSYS_DRIVER_UNLOAD:
HPI_COMMON(phm, phr);
HPIMSGX__cleanup(HPIMSGX_ALLADAPTERS, h_owner);
- hpios_locked_mem_free_all();
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_DRIVER_UNLOAD, 0);
return;
- case HPI_SUBSYS_GET_INFO:
- HPI_COMMON(phm, phr);
- break;
-
- case HPI_SUBSYS_FIND_ADAPTERS:
- memcpy(phr, &gRESP_HPI_SUBSYS_FIND_ADAPTERS,
- sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS));
- break;
case HPI_SUBSYS_GET_NUM_ADAPTERS:
- memcpy(phr, &gRESP_HPI_SUBSYS_FIND_ADAPTERS,
- sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS));
- phr->function = HPI_SUBSYS_GET_NUM_ADAPTERS;
- break;
case HPI_SUBSYS_GET_ADAPTER:
- {
- int count = phm->adapter_index;
- int index = 0;
- hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_GET_ADAPTER, 0);
-
- /* This is complicated by the fact that we want to
- * "skip" 0's in the adapter list.
- * First, make sure we are pointing to a
- * non-zero adapter type.
- */
- while (gRESP_HPI_SUBSYS_FIND_ADAPTERS.
- s.aw_adapter_list[index] == 0) {
- index++;
- if (index >= HPI_MAX_ADAPTERS)
- break;
- }
- while (count) {
- /* move on to the next adapter */
- index++;
- if (index >= HPI_MAX_ADAPTERS)
- break;
- while (gRESP_HPI_SUBSYS_FIND_ADAPTERS.
- s.aw_adapter_list[index] == 0) {
- index++;
- if (index >= HPI_MAX_ADAPTERS)
- break;
- }
- count--;
- }
+ HPI_COMMON(phm, phr);
+ break;
- if (index < HPI_MAX_ADAPTERS) {
- phr->u.s.adapter_index = (u16)index;
- phr->u.s.aw_adapter_list[0] =
- gRESP_HPI_SUBSYS_FIND_ADAPTERS.
- s.aw_adapter_list[index];
- } else {
- phr->u.s.adapter_index = 0;
- phr->u.s.aw_adapter_list[0] = 0;
- phr->error = HPI_ERROR_BAD_ADAPTER_NUMBER;
- }
- break;
- }
case HPI_SUBSYS_CREATE_ADAPTER:
HPIMSGX__init(phm, phr);
break;
- case HPI_SUBSYS_DELETE_ADAPTER:
- HPIMSGX__cleanup(phm->adapter_index, h_owner);
- {
- struct hpi_message hm;
- struct hpi_response hr;
- /* call to HPI_ADAPTER_CLOSE */
- hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_CLOSE);
- hm.adapter_index = phm->adapter_index;
- hw_entry_point(&hm, &hr);
- }
- hw_entry_point(phm, phr);
- gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.
- aw_adapter_list[phm->adapter_index]
- = 0;
- hpi_entry_points[phm->adapter_index] = NULL;
- break;
+
default:
- hw_entry_point(phm, phr);
+ /* Must explicitly handle every subsys message in this switch */
+ hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, phm->function,
+ HPI_ERROR_INVALID_FUNC);
break;
}
}
@@ -297,6 +213,19 @@ static void adapter_message(struct hpi_message *phm, struct hpi_response *phr,
case HPI_ADAPTER_CLOSE:
adapter_close(phm, phr);
break;
+ case HPI_ADAPTER_DELETE:
+ HPIMSGX__cleanup(phm->adapter_index, h_owner);
+ {
+ struct hpi_message hm;
+ struct hpi_response hr;
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_CLOSE);
+ hm.adapter_index = phm->adapter_index;
+ hw_entry_point(&hm, &hr);
+ }
+ hw_entry_point(phm, phr);
+ break;
+
default:
hw_entry_point(phm, phr);
break;
@@ -368,9 +297,11 @@ static void instream_message(struct hpi_message *phm,
void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
- HPI_DEBUG_MESSAGE(DEBUG, phm);
- if (phm->type != HPI_TYPE_MESSAGE) {
+ if (logging_enabled)
+ HPI_DEBUG_MESSAGE(DEBUG, phm);
+
+ if (phm->type != HPI_TYPE_REQUEST) {
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_INVALID_TYPE);
return;
@@ -408,34 +339,14 @@ void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr,
hw_entry_point(phm, phr);
break;
}
- HPI_DEBUG_RESPONSE(phr);
-#if 1
- if (phr->error >= HPI_ERROR_BACKEND_BASE) {
- void *ep = NULL;
- char *ep_name;
-
- HPI_DEBUG_MESSAGE(ERROR, phm);
-
- if (phm->adapter_index < HPI_MAX_ADAPTERS)
- ep = hpi_entry_points[phm->adapter_index];
-
- /* Don't need this? Have adapter index in debug info
- Know at driver load time index->backend mapping */
- if (ep == HPI_6000)
- ep_name = "HPI_6000";
- else if (ep == HPI_6205)
- ep_name = "HPI_6205";
- else
- ep_name = "unknown";
-
- HPI_DEBUG_LOG(ERROR, "HPI %s response - error# %d\n", ep_name,
- phr->error);
-
- if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE)
- hpi_debug_data((u16 *)phm,
- sizeof(*phm) / sizeof(u16));
+
+ if (logging_enabled)
+ HPI_DEBUG_RESPONSE(phr);
+
+ if (phr->error >= HPI_ERROR_DSP_COMMUNICATION) {
+ hpi_debug_level_set(HPI_DEBUG_LEVEL_ERROR);
+ logging_enabled = 0;
}
-#endif
}
static void adapter_open(struct hpi_message *phm, struct hpi_response *phr)
@@ -484,7 +395,7 @@ static void instream_open(struct hpi_message *phm, struct hpi_response *phr,
else {
instream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 1;
- hpios_msgxlock_un_lock(&msgx_lock);
+ hpios_msgxlock_unlock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
@@ -509,7 +420,7 @@ static void instream_open(struct hpi_message *phm, struct hpi_response *phr,
sizeof(rESP_HPI_ISTREAM_OPEN[0][0]));
}
}
- hpios_msgxlock_un_lock(&msgx_lock);
+ hpios_msgxlock_unlock(&msgx_lock);
}
static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
@@ -530,7 +441,7 @@ static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
phm->wAdapterIndex, phm->wObjIndex, hOwner); */
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner = NULL;
- hpios_msgxlock_un_lock(&msgx_lock);
+ hpios_msgxlock_unlock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_RESET);
@@ -556,7 +467,7 @@ static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
obj_index].h_owner);
phr->error = HPI_ERROR_OBJ_NOT_OPEN;
}
- hpios_msgxlock_un_lock(&msgx_lock);
+ hpios_msgxlock_unlock(&msgx_lock);
}
static void outstream_open(struct hpi_message *phm, struct hpi_response *phr,
@@ -581,7 +492,7 @@ static void outstream_open(struct hpi_message *phm, struct hpi_response *phr,
else {
outstream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 1;
- hpios_msgxlock_un_lock(&msgx_lock);
+ hpios_msgxlock_unlock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
@@ -606,7 +517,7 @@ static void outstream_open(struct hpi_message *phm, struct hpi_response *phr,
sizeof(rESP_HPI_OSTREAM_OPEN[0][0]));
}
}
- hpios_msgxlock_un_lock(&msgx_lock);
+ hpios_msgxlock_unlock(&msgx_lock);
}
static void outstream_close(struct hpi_message *phm, struct hpi_response *phr,
@@ -628,7 +539,7 @@ static void outstream_close(struct hpi_message *phm, struct hpi_response *phr,
phm->wAdapterIndex, phm->wObjIndex, hOwner); */
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner = NULL;
- hpios_msgxlock_un_lock(&msgx_lock);
+ hpios_msgxlock_unlock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_RESET);
@@ -654,7 +565,7 @@ static void outstream_close(struct hpi_message *phm, struct hpi_response *phr,
obj_index].h_owner);
phr->error = HPI_ERROR_OBJ_NOT_OPEN;
}
- hpios_msgxlock_un_lock(&msgx_lock);
+ hpios_msgxlock_unlock(&msgx_lock);
}
static u16 adapter_prepare(u16 adapter)
@@ -683,16 +594,9 @@ static u16 adapter_prepare(u16 adapter)
if (hr.error)
return hr.error;
- aDAPTER_INFO[adapter].num_outstreams = hr.u.a.num_outstreams;
- aDAPTER_INFO[adapter].num_instreams = hr.u.a.num_instreams;
- aDAPTER_INFO[adapter].type = hr.u.a.adapter_type;
-
- gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list[adapter] =
- hr.u.a.adapter_type;
- gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters++;
- if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters > HPI_MAX_ADAPTERS)
- gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters =
- HPI_MAX_ADAPTERS;
+ aDAPTER_INFO[adapter].num_outstreams = hr.u.ax.info.num_outstreams;
+ aDAPTER_INFO[adapter].num_instreams = hr.u.ax.info.num_instreams;
+ aDAPTER_INFO[adapter].type = hr.u.ax.info.adapter_type;
/* call to HPI_OSTREAM_OPEN */
for (i = 0; i < aDAPTER_INFO[adapter].num_outstreams; i++) {
@@ -727,7 +631,7 @@ static u16 adapter_prepare(u16 adapter)
memcpy(&rESP_HPI_MIXER_OPEN[adapter], &hr,
sizeof(rESP_HPI_MIXER_OPEN[0]));
- return gRESP_HPI_SUBSYS_FIND_ADAPTERS.h.error;
+ return 0;
}
static void HPIMSGX__reset(u16 adapter_index)
@@ -737,12 +641,6 @@ static void HPIMSGX__reset(u16 adapter_index)
struct hpi_response hr;
if (adapter_index == HPIMSGX_ALLADAPTERS) {
- /* reset all responses to contain errors */
- hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_FIND_ADAPTERS, 0);
- memcpy(&gRESP_HPI_SUBSYS_FIND_ADAPTERS, &hr,
- sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS));
-
for (adapter = 0; adapter < HPI_MAX_ADAPTERS; adapter++) {
hpi_init_response(&hr, HPI_OBJ_ADAPTER,
@@ -783,12 +681,6 @@ static void HPIMSGX__reset(u16 adapter_index)
rESP_HPI_ISTREAM_OPEN[adapter_index][i].h.error =
HPI_ERROR_INVALID_OBJ;
}
- if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.
- s.aw_adapter_list[adapter_index]) {
- gRESP_HPI_SUBSYS_FIND_ADAPTERS.
- s.aw_adapter_list[adapter_index] = 0;
- gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters--;
- }
}
}
@@ -802,15 +694,9 @@ static u16 HPIMSGX__init(struct hpi_message *phm,
hpi_handler_func *entry_point_func;
struct hpi_response hr;
- if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters >= HPI_MAX_ADAPTERS)
- return HPI_ERROR_BAD_ADAPTER_NUMBER;
-
/* Init response here so we can pass in previous adapter list */
hpi_init_response(&hr, phm->object, phm->function,
HPI_ERROR_INVALID_OBJ);
- memcpy(hr.u.s.aw_adapter_list,
- gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list,
- sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list));
entry_point_func =
hpi_lookup_entry_point_function(phm->u.s.resource.r.pci);
@@ -822,8 +708,8 @@ static u16 HPIMSGX__init(struct hpi_message *phm,
phr->error = HPI_ERROR_PROCESSING_MESSAGE;
return phr->error;
}
- if (hr.error == 0) {
- /* the adapter was created succesfully
+ if (hr.error == 0 && hr.u.s.adapter_index < HPI_MAX_ADAPTERS) {
+ /* the adapter was created successfully
save the mapping for future use */
hpi_entry_points[hr.u.s.adapter_index] = entry_point_func;
/* prepare adapter (pre-open streams etc.) */
@@ -860,7 +746,7 @@ static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner)
struct hpi_response hr;
HPI_DEBUG_LOG(DEBUG,
- "close adapter %d ostream %d\n",
+ "Close adapter %d ostream %d\n",
adapter, i);
hpi_init_message_response(&hm, &hr,
@@ -884,7 +770,7 @@ static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner)
struct hpi_response hr;
HPI_DEBUG_LOG(DEBUG,
- "close adapter %d istream %d\n",
+ "Close adapter %d istream %d\n",
adapter, i);
hpi_init_message_response(&hm, &hr,
diff --git a/sound/pci/asihpi/hpimsgx.h b/sound/pci/asihpi/hpimsgx.h
index fd49e7542a88..14c01cc91d71 100644
--- a/sound/pci/asihpi/hpimsgx.h
+++ b/sound/pci/asihpi/hpimsgx.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
HPI Extended Message Handler Functions
diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c
index 62895a719fcb..9fb0c8e503df 100644
--- a/sound/pci/asihpi/hpioctl.c
+++ b/sound/pci/asihpi/hpioctl.c
@@ -1,36 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*******************************************************************************
-
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
+ Common Linux HPI ioctl and module probe/remove functions
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
+ Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com>
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-Common Linux HPI ioctl and module probe/remove functions
*******************************************************************************/
#define SOURCEFILE_NAME "hpioctl.c"
#include "hpi_internal.h"
+#include "hpi_version.h"
#include "hpimsginit.h"
#include "hpidebug.h"
#include "hpimsgx.h"
#include "hpioctl.h"
+#include "hpicmn.h"
#include <linux/fs.h>
+#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/moduleparam.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
+#include <linux/pci.h>
#include <linux/stringify.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/nospec.h>
#ifdef MODULE_FIRMWARE
MODULE_FIRMWARE("asihpi/dsp5000.bin");
@@ -43,14 +39,14 @@ MODULE_FIRMWARE("asihpi/dsp8900.bin");
#endif
static int prealloc_stream_buf;
-module_param(prealloc_stream_buf, int, S_IRUGO);
+module_param(prealloc_stream_buf, int, 0444);
MODULE_PARM_DESC(prealloc_stream_buf,
- "preallocate size for per-adapter stream buffer");
+ "Preallocate size for per-adapter stream buffer");
/* Allow the debug level to be changed after module load.
E.g. echo 2 > /sys/module/asihpi/parameters/hpiDebugLevel
*/
-module_param(hpi_debug_level, int, S_IRUGO | S_IWUSR);
+module_param(hpi_debug_level, int, 0644);
MODULE_PARM_DESC(hpi_debug_level, "debug verbosity 0..5");
/* List of adapters found */
@@ -62,9 +58,7 @@ static struct hpi_adapter adapters[HPI_MAX_ADAPTERS];
static void hpi_send_recv_f(struct hpi_message *phm, struct hpi_response *phr,
struct file *file)
{
- int adapter = phm->adapter_index;
-
- if ((adapter >= HPI_MAX_ADAPTERS || adapter < 0)
+ if ((phm->adapter_index >= HPI_MAX_ADAPTERS)
&& (phm->object != HPI_OBJ_SUBSYSTEM))
phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
else
@@ -103,16 +97,16 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
void __user *puhr;
union hpi_message_buffer_v1 *hm;
union hpi_response_buffer_v1 *hr;
+ u16 msg_size;
u16 res_max_size;
u32 uncopied_bytes;
- struct hpi_adapter *pa = NULL;
int err = 0;
if (cmd != HPI_IOCTL_LINUX)
return -EINVAL;
hm = kmalloc(sizeof(*hm), GFP_KERNEL);
- hr = kmalloc(sizeof(*hr), GFP_KERNEL);
+ hr = kzalloc(sizeof(*hr), GFP_KERNEL);
if (!hm || !hr) {
err = -ENOMEM;
goto out;
@@ -121,29 +115,32 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
phpi_ioctl_data = (struct hpi_ioctl_linux __user *)arg;
/* Read the message and response pointers from user space. */
- if (get_user(puhm, &phpi_ioctl_data->phm) ||
- get_user(puhr, &phpi_ioctl_data->phr)) {
+ if (get_user(puhm, &phpi_ioctl_data->phm)
+ || get_user(puhr, &phpi_ioctl_data->phr)) {
err = -EFAULT;
goto out;
}
/* Now read the message size and data from user space. */
- if (get_user(hm->h.size, (u16 __user *)puhm)) {
+ if (get_user(msg_size, (u16 __user *)puhm)) {
err = -EFAULT;
goto out;
}
- if (hm->h.size > sizeof(*hm))
- hm->h.size = sizeof(*hm);
+ if (msg_size > sizeof(*hm))
+ msg_size = sizeof(*hm);
- /*printk(KERN_INFO "message size %d\n", hm->h.wSize); */
+ /* printk(KERN_INFO "message size %d\n", hm->h.wSize); */
- uncopied_bytes = copy_from_user(hm, puhm, hm->h.size);
+ uncopied_bytes = copy_from_user(hm, puhm, msg_size);
if (uncopied_bytes) {
HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes);
err = -EFAULT;
goto out;
}
+ /* Override h.size in case it is changed between two userspace fetches */
+ hm->h.size = msg_size;
+
if (get_user(res_max_size, (u16 __user *)puhr)) {
err = -EFAULT;
goto out;
@@ -155,38 +152,40 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
goto out;
}
- pa = &adapters[hm->h.adapter_index];
- hr->h.size = 0;
- if (hm->h.object == HPI_OBJ_SUBSYSTEM) {
- switch (hm->h.function) {
- case HPI_SUBSYS_CREATE_ADAPTER:
- case HPI_SUBSYS_DELETE_ADAPTER:
- /* Application must not use these functions! */
- hr->h.size = sizeof(hr->h);
- hr->h.error = HPI_ERROR_INVALID_OPERATION;
- hr->h.function = hm->h.function;
- uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
- if (uncopied_bytes)
- err = -EFAULT;
- else
- err = 0;
- goto out;
+ res_max_size = min_t(size_t, res_max_size, sizeof(*hr));
+
+ switch (hm->h.function) {
+ case HPI_SUBSYS_CREATE_ADAPTER:
+ case HPI_ADAPTER_DELETE:
+ /* Application must not use these functions! */
+ hr->h.size = sizeof(hr->h);
+ hr->h.error = HPI_ERROR_INVALID_OPERATION;
+ hr->h.function = hm->h.function;
+ uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
+ if (uncopied_bytes)
+ err = -EFAULT;
+ else
+ err = 0;
+ goto out;
+ }
- default:
- hpi_send_recv_f(&hm->m0, &hr->r0, file);
- }
+ hr->h.size = res_max_size;
+ if (hm->h.object == HPI_OBJ_SUBSYSTEM) {
+ hpi_send_recv_f(&hm->m0, &hr->r0, file);
} else {
u16 __user *ptr = NULL;
u32 size = 0;
-
/* -1=no data 0=read from user mem, 1=write to user mem */
int wrflag = -1;
- u32 adapter = hm->h.adapter_index;
+ struct hpi_adapter *pa = NULL;
- if ((hm->h.adapter_index > HPI_MAX_ADAPTERS) || (!pa->type)) {
- hpi_init_response(&hr->r0, HPI_OBJ_ADAPTER,
- HPI_ADAPTER_OPEN,
- HPI_ERROR_BAD_ADAPTER_NUMBER);
+ if (hm->h.adapter_index < ARRAY_SIZE(adapters))
+ pa = &adapters[array_index_nospec(hm->h.adapter_index,
+ ARRAY_SIZE(adapters))];
+
+ if (!pa || !pa->adapter || !pa->adapter->type) {
+ hpi_init_response(&hr->r0, hm->h.object,
+ hm->h.function, HPI_ERROR_BAD_ADAPTER_NUMBER);
uncopied_bytes =
copy_to_user(puhr, hr, sizeof(hr->h));
@@ -197,7 +196,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
goto out;
}
- if (mutex_lock_interruptible(&adapters[adapter].mutex)) {
+ if (mutex_lock_interruptible(&pa->mutex)) {
err = -EINTR;
goto out;
}
@@ -216,7 +215,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
*/
if (pa->buffer_size < size) {
HPI_DEBUG_LOG(DEBUG,
- "realloc adapter %d stream "
+ "Realloc adapter %d stream "
"buffer from %zd to %d\n",
hm->h.adapter_index,
pa->buffer_size, size);
@@ -233,8 +232,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
"stream buffer size %d\n",
size);
- mutex_unlock(&adapters
- [adapter].mutex);
+ mutex_unlock(&pa->mutex);
err = -EINVAL;
goto out;
}
@@ -259,7 +257,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
copy_from_user(pa->p_buffer, ptr, size);
if (uncopied_bytes)
HPI_DEBUG_LOG(WARNING,
- "missed %d of %d "
+ "Missed %d of %d "
"bytes from user\n", uncopied_bytes,
size);
}
@@ -271,11 +269,11 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
copy_to_user(ptr, pa->p_buffer, size);
if (uncopied_bytes)
HPI_DEBUG_LOG(WARNING,
- "missed %d of %d " "bytes to user\n",
+ "Missed %d of %d " "bytes to user\n",
uncopied_bytes, size);
}
- mutex_unlock(&adapters[adapter].mutex);
+ mutex_unlock(&pa->mutex);
}
/* on return response size must be set */
@@ -290,9 +288,9 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (hr->h.size > res_max_size) {
HPI_DEBUG_LOG(ERROR, "response too big %d %d\n", hr->h.size,
res_max_size);
- /*HPI_DEBUG_MESSAGE(ERROR, hm); */
- err = -EFAULT;
- goto out;
+ hr->h.error = HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
+ hr->h.specific_error = hr->h.size;
+ hr->h.size = sizeof(hr->h);
}
uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
@@ -308,70 +306,97 @@ out:
return err;
}
-int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
- const struct pci_device_id *pci_id)
+static int asihpi_irq_count;
+
+static irqreturn_t asihpi_isr(int irq, void *dev_id)
{
- int err, idx, nm;
+ struct hpi_adapter *a = dev_id;
+ int handled;
+
+ if (!a->adapter->irq_query_and_clear) {
+ pr_err("asihpi_isr ASI%04X:%d no handler\n", a->adapter->type,
+ a->adapter->index);
+ return IRQ_NONE;
+ }
+
+ handled = a->adapter->irq_query_and_clear(a->adapter, 0);
+
+ if (!handled)
+ return IRQ_NONE;
+
+ asihpi_irq_count++;
+ /* printk(KERN_INFO "asihpi_isr %d ASI%04X:%d irq handled\n",
+ asihpi_irq_count, a->adapter->type, a->adapter->index); */
+
+ if (a->interrupt_callback)
+ return IRQ_WAKE_THREAD;
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t asihpi_isr_thread(int irq, void *dev_id)
+{
+ struct hpi_adapter *a = dev_id;
+
+ if (a->interrupt_callback)
+ a->interrupt_callback(a);
+ return IRQ_HANDLED;
+}
+
+int asihpi_adapter_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ int idx, nm, low_latency_mode = 0, irq_supported = 0;
+ int adapter_index;
unsigned int memlen;
struct hpi_message hm;
struct hpi_response hr;
struct hpi_adapter adapter;
- struct hpi_pci pci;
+ struct hpi_pci pci = { 0 };
memset(&adapter, 0, sizeof(adapter));
- printk(KERN_DEBUG "probe PCI device (%04x:%04x,%04x:%04x,%04x)\n",
- pci_dev->vendor, pci_dev->device, pci_dev->subsystem_vendor,
+ dev_dbg(&pci_dev->dev,
+ "probe %04x:%04x,%04x:%04x,%04x\n", pci_dev->vendor,
+ pci_dev->device, pci_dev->subsystem_vendor,
pci_dev->subsystem_device, pci_dev->devfn);
+ if (pcim_enable_device(pci_dev) < 0) {
+ dev_err(&pci_dev->dev,
+ "pci_enable_device failed, disabling device\n");
+ return -EIO;
+ }
+
+ pci_set_master(pci_dev); /* also sets latency timer if < 16 */
+
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_CREATE_ADAPTER);
hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CREATE_ADAPTER,
HPI_ERROR_PROCESSING_MESSAGE);
- hm.adapter_index = -1; /* an invalid index */
-
- /* fill in HPI_PCI information from kernel provided information */
- adapter.pci = pci_dev;
+ hm.adapter_index = HPI_ADAPTER_INDEX_INVALID;
nm = HPI_MAX_ADAPTER_MEM_SPACES;
for (idx = 0; idx < nm; idx++) {
- HPI_DEBUG_LOG(INFO, "resource %d %s %08llx-%08llx %04llx\n",
- idx, pci_dev->resource[idx].name,
- (unsigned long long)pci_resource_start(pci_dev, idx),
- (unsigned long long)pci_resource_end(pci_dev, idx),
- (unsigned long long)pci_resource_flags(pci_dev, idx));
+ HPI_DEBUG_LOG(INFO, "resource %d %pR\n", idx,
+ &pci_dev->resource[idx]);
if (pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM) {
memlen = pci_resource_len(pci_dev, idx);
- adapter.ap_remapped_mem_base[idx] =
+ pci.ap_mem_base[idx] =
ioremap(pci_resource_start(pci_dev, idx),
memlen);
- if (!adapter.ap_remapped_mem_base[idx]) {
+ if (!pci.ap_mem_base[idx]) {
HPI_DEBUG_LOG(ERROR,
"ioremap failed, aborting\n");
/* unmap previously mapped pci mem space */
goto err;
}
}
-
- pci.ap_mem_base[idx] = adapter.ap_remapped_mem_base[idx];
}
- /* could replace Pci with direct pointer to pci_dev for linux
- Instead wrap accessor functions for IDs etc.
- Would it work for windows?
- */
- pci.bus_number = pci_dev->bus->number;
- pci.vendor_id = (u16)pci_dev->vendor;
- pci.device_id = (u16)pci_dev->device;
- pci.subsys_vendor_id = (u16)(pci_dev->subsystem_vendor & 0xffff);
- pci.subsys_device_id = (u16)(pci_dev->subsystem_device & 0xffff);
- pci.device_number = pci_dev->devfn;
- pci.interrupt = pci_dev->irq;
- pci.p_os_data = pci_dev;
-
+ pci.pci_dev = pci_dev;
hm.u.s.resource.bus_type = HPI_BUS_PCI;
hm.u.s.resource.r.pci = &pci;
@@ -380,6 +405,9 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
if (hr.error)
goto err;
+ adapter_index = hr.u.s.adapter_index;
+ adapter.adapter = hpi_find_adapter(adapter_index);
+
if (prealloc_stream_buf) {
adapter.p_buffer = vmalloc(prealloc_stream_buf);
if (!adapter.p_buffer) {
@@ -391,32 +419,100 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
}
}
- adapter.index = hr.u.s.adapter_index;
- adapter.type = hr.u.s.aw_adapter_list[adapter.index];
- hm.adapter_index = adapter.index;
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_OPEN);
+ hm.adapter_index = adapter.adapter->index;
+ hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
- err = hpi_adapter_open(NULL, adapter.index);
- if (err)
+ if (hr.error) {
+ HPI_DEBUG_LOG(ERROR, "HPI_ADAPTER_OPEN failed, aborting\n");
goto err;
+ }
+
+ /* Check if current mode == Low Latency mode */
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_GET_MODE);
+ hm.adapter_index = adapter.adapter->index;
+ hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
+
+ if (!hr.error
+ && hr.u.ax.mode.adapter_mode == HPI_ADAPTER_MODE_LOW_LATENCY)
+ low_latency_mode = 1;
+ else
+ dev_info(&pci_dev->dev,
+ "Adapter at index %d is not in low latency mode\n",
+ adapter.adapter->index);
+
+ /* Check if IRQs are supported */
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_GET_PROPERTY);
+ hm.adapter_index = adapter.adapter->index;
+ hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_SUPPORTS_IRQ;
+ hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
+ if (hr.error || !hr.u.ax.property_get.parameter1) {
+ dev_info(&pci_dev->dev,
+ "IRQs not supported by adapter at index %d\n",
+ adapter.adapter->index);
+ } else {
+ irq_supported = 1;
+ }
- adapter.snd_card_asihpi = NULL;
/* WARNING can't init mutex in 'adapter'
* and then copy it to adapters[] ?!?!
*/
- adapters[hr.u.s.adapter_index] = adapter;
- mutex_init(&adapters[adapter.index].mutex);
- pci_set_drvdata(pci_dev, &adapters[adapter.index]);
+ adapters[adapter_index] = adapter;
+ mutex_init(&adapters[adapter_index].mutex);
+ pci_set_drvdata(pci_dev, &adapters[adapter_index]);
+
+ if (low_latency_mode && irq_supported) {
+ if (!adapter.adapter->irq_query_and_clear) {
+ dev_err(&pci_dev->dev,
+ "no IRQ handler for adapter %d, aborting\n",
+ adapter.adapter->index);
+ goto err;
+ }
- printk(KERN_INFO "probe found adapter ASI%04X HPI index #%d.\n",
- adapter.type, adapter.index);
+ /* Disable IRQ generation on DSP side by setting the rate to 0 */
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_SET_PROPERTY);
+ hm.adapter_index = adapter.adapter->index;
+ hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_IRQ_RATE;
+ hm.u.ax.property_set.parameter1 = 0;
+ hm.u.ax.property_set.parameter2 = 0;
+ hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
+ if (hr.error) {
+ HPI_DEBUG_LOG(ERROR,
+ "HPI_ADAPTER_GET_MODE failed, aborting\n");
+ goto err;
+ }
+
+ /* Note: request_irq calls asihpi_isr here */
+ if (request_threaded_irq(pci_dev->irq, asihpi_isr,
+ asihpi_isr_thread, IRQF_SHARED,
+ "asihpi", &adapters[adapter_index])) {
+ dev_err(&pci_dev->dev, "request_irq(%d) failed\n",
+ pci_dev->irq);
+ goto err;
+ }
+
+ adapters[adapter_index].interrupt_mode = 1;
+
+ dev_info(&pci_dev->dev, "using irq %d\n", pci_dev->irq);
+ adapters[adapter_index].irq = pci_dev->irq;
+ } else {
+ dev_info(&pci_dev->dev, "using polled mode\n");
+ }
+
+ dev_info(&pci_dev->dev, "probe succeeded for ASI%04X HPI index %d\n",
+ adapter.adapter->type, adapter_index);
return 0;
err:
- for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; idx++) {
- if (adapter.ap_remapped_mem_base[idx]) {
- iounmap(adapter.ap_remapped_mem_base[idx]);
- adapter.ap_remapped_mem_base[idx] = NULL;
+ while (--idx >= 0) {
+ if (pci.ap_mem_base[idx]) {
+ iounmap(pci.ap_mem_base[idx]);
+ pci.ap_mem_base[idx] = NULL;
}
}
@@ -429,41 +525,48 @@ err:
return -ENODEV;
}
-void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev)
+void asihpi_adapter_remove(struct pci_dev *pci_dev)
{
int idx;
struct hpi_message hm;
struct hpi_response hr;
struct hpi_adapter *pa;
- pa = (struct hpi_adapter *)pci_get_drvdata(pci_dev);
+ struct hpi_pci pci;
- hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
- HPI_SUBSYS_DELETE_ADAPTER);
- hm.adapter_index = pa->index;
+ pa = pci_get_drvdata(pci_dev);
+ pci = pa->adapter->pci;
+
+ /* Disable IRQ generation on DSP side */
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_SET_PROPERTY);
+ hm.adapter_index = pa->adapter->index;
+ hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_IRQ_RATE;
+ hm.u.ax.property_set.parameter1 = 0;
+ hm.u.ax.property_set.parameter2 = 0;
+ hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
+
+ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
+ HPI_ADAPTER_DELETE);
+ hm.adapter_index = pa->adapter->index;
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
/* unmap PCI memory space, mapped during device init. */
- for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; idx++) {
- if (pa->ap_remapped_mem_base[idx]) {
- iounmap(pa->ap_remapped_mem_base[idx]);
- pa->ap_remapped_mem_base[idx] = NULL;
- }
- }
+ for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; ++idx)
+ iounmap(pci.ap_mem_base[idx]);
- if (pa->p_buffer) {
- pa->buffer_size = 0;
- vfree(pa->p_buffer);
- }
+ if (pa->irq)
+ free_irq(pa->irq, pa);
- pci_set_drvdata(pci_dev, NULL);
- /*
- printk(KERN_INFO "PCI device (%04x:%04x,%04x:%04x,%04x),"
- " HPI index # %d, removed.\n",
- pci_dev->vendor, pci_dev->device,
- pci_dev->subsystem_vendor,
- pci_dev->subsystem_device, pci_dev->devfn,
- pa->index);
- */
+ vfree(pa->p_buffer);
+
+ if (1)
+ dev_info(&pci_dev->dev,
+ "remove %04x:%04x,%04x:%04x,%04x, HPI index %d\n",
+ pci_dev->vendor, pci_dev->device,
+ pci_dev->subsystem_vendor, pci_dev->subsystem_device,
+ pci_dev->devfn, pa->adapter->index);
+
+ memset(pa, 0, sizeof(*pa));
}
void __init asihpi_init(void)
diff --git a/sound/pci/asihpi/hpioctl.h b/sound/pci/asihpi/hpioctl.h
index 847f72f03fe1..f2ee172da56c 100644
--- a/sound/pci/asihpi/hpioctl.h
+++ b/sound/pci/asihpi/hpioctl.h
@@ -1,27 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Linux HPI ioctl, and shared module init functions
*******************************************************************************/
-int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev,
- const struct pci_device_id *pci_id);
-void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev);
+int asihpi_adapter_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id);
+void asihpi_adapter_remove(struct pci_dev *pci_dev);
void __init asihpi_init(void);
void __exit asihpi_exit(void);
diff --git a/sound/pci/asihpi/hpios.c b/sound/pci/asihpi/hpios.c
index 742ee12a9e17..6fe60d13e24b 100644
--- a/sound/pci/asihpi/hpios.c
+++ b/sound/pci/asihpi/hpios.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2012 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
HPI Operating System function implementation for Linux
@@ -39,13 +28,9 @@ void hpios_delay_micro_seconds(u32 num_micro_sec)
}
-void hpios_locked_mem_init(void)
-{
-}
-
-/** Allocated an area of locked memory for bus master DMA operations.
+/** Allocate an area of locked memory for bus master DMA operations.
-On error, return -ENOMEM, and *pMemArea.size = 0
+If allocation fails, return 1, and *pMemArea.size = 0
*/
u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_mem_area, u32 size,
struct pci_dev *pdev)
@@ -53,7 +38,7 @@ u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_mem_area, u32 size,
/*?? any benefit in using managed dmam_alloc_coherent? */
p_mem_area->vaddr =
dma_alloc_coherent(&pdev->dev, size, &p_mem_area->dma_handle,
- GFP_DMA32 | GFP_KERNEL);
+ GFP_KERNEL);
if (p_mem_area->vaddr) {
HPI_DEBUG_LOG(DEBUG, "allocated %d bytes, dma 0x%x vma %p\n",
@@ -66,7 +51,7 @@ u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_mem_area, u32 size,
HPI_DEBUG_LOG(WARNING,
"failed to allocate %d bytes locked memory\n", size);
p_mem_area->size = 0;
- return -ENOMEM;
+ return 1;
}
}
@@ -85,7 +70,3 @@ u16 hpios_locked_mem_free(struct consistent_dma_area *p_mem_area)
return 1;
}
}
-
-void hpios_locked_mem_free_all(void)
-{
-}
diff --git a/sound/pci/asihpi/hpios.h b/sound/pci/asihpi/hpios.h
index 370f39b43f85..9e551bc46264 100644
--- a/sound/pci/asihpi/hpios.h
+++ b/sound/pci/asihpi/hpios.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
HPI Operating System Specific macros for Linux Kernel driver
@@ -27,12 +16,9 @@ HPI Operating System Specific macros for Linux Kernel driver
#define HPI_OS_LINUX_KERNEL
#define HPI_OS_DEFINED
-#define HPI_KERNEL_MODE
-
-#define HPI_REASSIGN_DUPLICATE_ADAPTER_IDX
+#define HPI_BUILD_KERNEL_MODE
#include <linux/io.h>
-#include <asm/system.h>
#include <linux/ioctl.h>
#include <linux/kernel.h>
#include <linux/string.h>
@@ -40,13 +26,10 @@ HPI Operating System Specific macros for Linux Kernel driver
#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
+#include <linux/mutex.h>
#define HPI_NO_OS_FILE_OPS
-#ifdef CONFIG_64BIT
-#define HPI64BIT
-#endif
-
/** Details of a memory area allocated with pci_alloc_consistent
Need all info for parameters to pci_free_consistent
*/
@@ -84,7 +67,7 @@ struct hpi_ioctl_linux {
};
/* Conflict?: H is already used by a number of drivers hid, bluetooth hci,
- and some sound drivers sb16, hdsp, emu10k. AFAIK 0xFC is ununsed command
+ and some sound drivers sb16, hdsp, emu10k. AFAIK 0xFC is unused command
*/
#define HPI_IOCTL_LINUX _IOWR('H', 0xFC, struct hpi_ioctl_linux)
@@ -135,35 +118,37 @@ static inline void cond_unlock(struct hpios_spinlock *l)
#define hpios_msgxlock_init(obj) spin_lock_init(&(obj)->lock)
#define hpios_msgxlock_lock(obj) cond_lock(obj)
-#define hpios_msgxlock_un_lock(obj) cond_unlock(obj)
+#define hpios_msgxlock_unlock(obj) cond_unlock(obj)
#define hpios_dsplock_init(obj) spin_lock_init(&(obj)->dsp_lock.lock)
#define hpios_dsplock_lock(obj) cond_lock(&(obj)->dsp_lock)
#define hpios_dsplock_unlock(obj) cond_unlock(&(obj)->dsp_lock)
#ifdef CONFIG_SND_DEBUG
-#define HPI_DEBUG
+#define HPI_BUILD_DEBUG
#endif
#define HPI_ALIST_LOCKING
#define hpios_alistlock_init(obj) spin_lock_init(&((obj)->list_lock.lock))
#define hpios_alistlock_lock(obj) spin_lock(&((obj)->list_lock.lock))
-#define hpios_alistlock_un_lock(obj) spin_unlock(&((obj)->list_lock.lock))
+#define hpios_alistlock_unlock(obj) spin_unlock(&((obj)->list_lock.lock))
+struct snd_card;
+
+/** pci drvdata points to an instance of this struct */
struct hpi_adapter {
+ struct hpi_adapter_obj *adapter;
+ struct snd_card *snd_card;
+
+ int irq;
+ int interrupt_mode;
+ void (*interrupt_callback) (struct hpi_adapter *);
+
/* mutex prevents contention for one card
between multiple user programs (via ioctl) */
struct mutex mutex;
- u16 index;
- u16 type;
-
- /* ALSA card structure */
- void *snd_card_asihpi;
-
char *p_buffer;
size_t buffer_size;
- struct pci_dev *pci;
- void __iomem *ap_remapped_mem_base[HPI_MAX_ADAPTER_MEM_SPACES];
};
#endif
diff --git a/sound/pci/asihpi/hpipcida.h b/sound/pci/asihpi/hpipcida.h
index bb30868ce1a3..0673e8278070 100644
--- a/sound/pci/asihpi/hpipcida.h
+++ b/sound/pci/asihpi/hpipcida.h
@@ -1,20 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/******************************************************************************
AudioScience HPI driver
- Copyright (C) 1997-2010 AudioScience Inc. <support@audioscience.com>
+ Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation;
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Array initializer for PCI card IDs
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
index 49d572a7b235..2a0c59d5afa5 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -1,31 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ATI IXP 150/200/250/300 AC97 controllers
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -37,13 +23,12 @@
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("ATI IXP AC97 controller");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ATI,IXP150/200/250/300/400/600}}");
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
static int ac97_clock = 48000;
static char *ac97_quirk;
-static int spdif_aclink = 1;
+static bool spdif_aclink = 1;
static int ac97_codec = -1;
module_param(index, int, 0444);
@@ -60,7 +45,7 @@ module_param(spdif_aclink, bool, 0444);
MODULE_PARM_DESC(spdif_aclink, "S/PDIF over AC-link.");
/* just for backward compatibility */
-static int enable;
+static bool enable;
module_param(enable, bool, 0444);
@@ -207,10 +192,10 @@ struct atiixp;
*/
struct atiixp_dma_desc {
- u32 addr; /* DMA buffer address */
+ __le32 addr; /* DMA buffer address */
u16 status; /* status bits */
u16 size; /* size of the packet in dwords */
- u32 next; /* address of the next packet descriptor */
+ __le32 next; /* address of the next packet descriptor */
};
/*
@@ -286,7 +271,7 @@ struct atiixp {
/*
*/
-static DEFINE_PCI_DEVICE_TABLE(snd_atiixp_ids) = {
+static const struct pci_device_id snd_atiixp_ids[] = {
{ PCI_VDEVICE(ATI, 0x4341), 0 }, /* SB200 */
{ PCI_VDEVICE(ATI, 0x4361), 0 }, /* SB300 */
{ PCI_VDEVICE(ATI, 0x4370), 0 }, /* SB400 */
@@ -296,7 +281,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_atiixp_ids) = {
MODULE_DEVICE_TABLE(pci, snd_atiixp_ids);
-static struct snd_pci_quirk atiixp_quirks[] __devinitdata = {
+static const struct snd_pci_quirk atiixp_quirks[] = {
SND_PCI_QUIRK(0x105b, 0x0c81, "Foxconn RC4107MA-RS2", 0),
SND_PCI_QUIRK(0x15bd, 0x3100, "DFI RS482", 0),
{ } /* terminator */
@@ -360,14 +345,13 @@ static int atiixp_build_dma_packets(struct atiixp *chip, struct atiixp_dma *dma,
{
unsigned int i;
u32 addr, desc_addr;
- unsigned long flags;
if (periods > ATI_MAX_DESCRIPTORS)
return -ENOMEM;
if (dma->desc_buf.area == NULL) {
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
+ &chip->pci->dev,
ATI_DESC_LIST_SIZE,
&dma->desc_buf) < 0)
return -ENOMEM;
@@ -378,11 +362,11 @@ static int atiixp_build_dma_packets(struct atiixp *chip, struct atiixp_dma *dma,
return 0;
/* reset DMA before changing the descriptor table */
- spin_lock_irqsave(&chip->reg_lock, flags);
- writel(0, chip->remap_addr + dma->ops->llp_offset);
- dma->ops->enable_dma(chip, 0);
- dma->ops->enable_dma(chip, 1);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ writel(0, chip->remap_addr + dma->ops->llp_offset);
+ dma->ops->enable_dma(chip, 0);
+ dma->ops->enable_dma(chip, 1);
+ }
/* fill the entries */
addr = (u32)substream->runtime->dma_addr;
@@ -432,7 +416,7 @@ static int snd_atiixp_acquire_codec(struct atiixp *chip)
while (atiixp_read(chip, PHYS_OUT_ADDR) & ATI_REG_PHYS_OUT_ADDR_EN) {
if (! timeout--) {
- snd_printk(KERN_WARNING "atiixp: codec acquire timeout\n");
+ dev_warn(chip->card->dev, "codec acquire timeout\n");
return -EBUSY;
}
udelay(1);
@@ -463,7 +447,7 @@ static unsigned short snd_atiixp_codec_read(struct atiixp *chip, unsigned short
} while (--timeout);
/* time out may happen during reset */
if (reg < 0x7c)
- snd_printk(KERN_WARNING "atiixp: codec read timeout (reg %x)\n", reg);
+ dev_warn(chip->card->dev, "codec read timeout (reg %x)\n", reg);
return 0xffff;
}
@@ -522,8 +506,8 @@ static int snd_atiixp_aclink_reset(struct atiixp *chip)
atiixp_read(chip, CMD);
mdelay(1);
atiixp_update(chip, CMD, ATI_REG_CMD_AC_RESET, ATI_REG_CMD_AC_RESET);
- if (--timeout) {
- snd_printk(KERN_ERR "atiixp: codec reset timeout\n");
+ if (!--timeout) {
+ dev_err(chip->card->dev, "codec reset timeout\n");
break;
}
}
@@ -535,7 +519,6 @@ static int snd_atiixp_aclink_reset(struct atiixp *chip)
return 0;
}
-#ifdef CONFIG_PM
static int snd_atiixp_aclink_down(struct atiixp *chip)
{
// if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */
@@ -545,7 +528,6 @@ static int snd_atiixp_aclink_down(struct atiixp *chip)
ATI_REG_CMD_POWERDOWN);
return 0;
}
-#endif
/*
* auto-detection of codecs
@@ -561,21 +543,21 @@ static int snd_atiixp_aclink_down(struct atiixp *chip)
ATI_REG_ISR_CODEC2_NOT_READY)
#define CODEC_CHECK_BITS (ALL_CODEC_NOT_READY|ATI_REG_ISR_NEW_FRAME)
-static int __devinit ac97_probing_bugs(struct pci_dev *pci)
+static int ac97_probing_bugs(struct pci_dev *pci)
{
const struct snd_pci_quirk *q;
q = snd_pci_quirk_lookup(pci, atiixp_quirks);
if (q) {
- snd_printdd(KERN_INFO "Atiixp quirk for %s. "
- "Forcing codec %d\n", q->name, q->value);
+ dev_dbg(&pci->dev, "atiixp quirk for %s. Forcing codec %d\n",
+ snd_pci_quirk_name(q), q->value);
return q->value;
}
/* this hardware doesn't need workarounds. Probe for codec */
return -1;
}
-static int __devinit snd_atiixp_codec_detect(struct atiixp *chip)
+static int snd_atiixp_codec_detect(struct atiixp *chip)
{
int timeout;
@@ -599,7 +581,7 @@ static int __devinit snd_atiixp_codec_detect(struct atiixp *chip)
atiixp_write(chip, IER, 0); /* disable irqs */
if ((chip->codec_not_ready_bits & ALL_CODEC_NOT_READY) == ALL_CODEC_NOT_READY) {
- snd_printk(KERN_ERR "atiixp: no codec detected!\n");
+ dev_err(chip->card->dev, "no codec detected!\n");
return -ENXIO;
}
return 0;
@@ -675,7 +657,7 @@ static snd_pcm_uframes_t snd_atiixp_pcm_pointer(struct snd_pcm_substream *substr
continue;
return bytes_to_frames(runtime, curptr);
}
- snd_printd("atiixp: invalid DMA pointer read 0x%x (buf=%x)\n",
+ dev_dbg(chip->card->dev, "invalid DMA pointer read 0x%x (buf=%x)\n",
readl(chip->remap_addr + dma->ops->dt_cur), dma->buf_addr);
return 0;
}
@@ -687,8 +669,8 @@ static void snd_atiixp_xrun_dma(struct atiixp *chip, struct atiixp_dma *dma)
{
if (! dma->substream || ! dma->running)
return;
- snd_printdd("atiixp: XRUN detected (DMA %d)\n", dma->ops->type);
- snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN);
+ dev_dbg(chip->card->dev, "XRUN detected (DMA %d)\n", dma->ops->type);
+ snd_pcm_stop_xrun(dma->substream);
}
/*
@@ -728,11 +710,15 @@ static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
!dma->ops->flush_dma))
return -EINVAL;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
+ if (dma->running && dma->suspended &&
+ cmd == SNDRV_PCM_TRIGGER_RESUME)
+ writel(dma->saved_curptr, chip->remap_addr +
+ dma->ops->dt_cur);
dma->ops->enable_transfer(chip, 1);
dma->running = 1;
dma->suspended = 0;
@@ -740,9 +726,12 @@ static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
+ dma->suspended = cmd == SNDRV_PCM_TRIGGER_SUSPEND;
+ if (dma->running && dma->suspended)
+ dma->saved_curptr = readl(chip->remap_addr +
+ dma->ops->dt_cur);
dma->ops->enable_transfer(chip, 0);
dma->running = 0;
- dma->suspended = cmd == SNDRV_PCM_TRIGGER_SUSPEND;
break;
default:
err = -EINVAL;
@@ -755,7 +744,6 @@ static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
snd_atiixp_check_bus_busy(chip);
}
}
- spin_unlock(&chip->reg_lock);
return err;
}
@@ -869,7 +857,7 @@ static int snd_atiixp_spdif_prepare(struct snd_pcm_substream *substream)
{
struct atiixp *chip = snd_pcm_substream_chip(substream);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
if (chip->spdif_over_aclink) {
unsigned int data;
/* enable slots 10/11 */
@@ -887,7 +875,6 @@ static int snd_atiixp_spdif_prepare(struct snd_pcm_substream *substream)
atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_CONFIG_MASK, 0);
atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_SPDF, 0);
}
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -897,21 +884,21 @@ static int snd_atiixp_playback_prepare(struct snd_pcm_substream *substream)
struct atiixp *chip = snd_pcm_substream_chip(substream);
unsigned int data;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
data = atiixp_read(chip, OUT_DMA_SLOT) & ~ATI_REG_OUT_DMA_SLOT_MASK;
switch (substream->runtime->channels) {
case 8:
data |= ATI_REG_OUT_DMA_SLOT_BIT(10) |
ATI_REG_OUT_DMA_SLOT_BIT(11);
- /* fallthru */
+ fallthrough;
case 6:
data |= ATI_REG_OUT_DMA_SLOT_BIT(7) |
ATI_REG_OUT_DMA_SLOT_BIT(8);
- /* fallthru */
+ fallthrough;
case 4:
data |= ATI_REG_OUT_DMA_SLOT_BIT(6) |
ATI_REG_OUT_DMA_SLOT_BIT(9);
- /* fallthru */
+ fallthrough;
default:
data |= ATI_REG_OUT_DMA_SLOT_BIT(3) |
ATI_REG_OUT_DMA_SLOT_BIT(4);
@@ -932,7 +919,6 @@ static int snd_atiixp_playback_prepare(struct snd_pcm_substream *substream)
atiixp_update(chip, 6CH_REORDER, ATI_REG_6CH_REORDER_EN,
substream->runtime->channels >= 6 ? ATI_REG_6CH_REORDER_EN: 0);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -941,11 +927,10 @@ static int snd_atiixp_capture_prepare(struct snd_pcm_substream *substream)
{
struct atiixp *chip = snd_pcm_substream_chip(substream);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_IN,
substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE ?
ATI_REG_CMD_INTERLEAVE_IN : 0);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -959,9 +944,6 @@ static int snd_atiixp_pcm_hw_params(struct snd_pcm_substream *substream,
struct atiixp_dma *dma = substream->runtime->private_data;
int err;
- err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
dma->buf_addr = substream->runtime->dma_addr;
dma->buf_bytes = params_buffer_bytes(hw_params);
@@ -1001,7 +983,6 @@ static int snd_atiixp_pcm_hw_free(struct snd_pcm_substream *substream)
dma->pcm_open_flag = 0;
}
atiixp_clear_dma_packets(chip, dma, substream);
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -1009,7 +990,7 @@ static int snd_atiixp_pcm_hw_free(struct snd_pcm_substream *substream)
/*
* pcm hardware definition, identical for all DMA types
*/
-static struct snd_pcm_hardware snd_atiixp_pcm_hw =
+static const struct snd_pcm_hardware snd_atiixp_pcm_hw =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1051,14 +1032,15 @@ static int snd_atiixp_pcm_open(struct snd_pcm_substream *substream,
/* direct SPDIF */
runtime->hw.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
}
- if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
return err;
runtime->private_data = dma;
/* enable DMA bits */
- spin_lock_irq(&chip->reg_lock);
- dma->ops->enable_dma(chip, 1);
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ dma->ops->enable_dma(chip, 1);
+ }
dma->opened = 1;
return 0;
@@ -1071,9 +1053,9 @@ static int snd_atiixp_pcm_close(struct snd_pcm_substream *substream,
/* disable DMA bits */
if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma))
return -EINVAL;
- spin_lock_irq(&chip->reg_lock);
- dma->ops->enable_dma(chip, 0);
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ dma->ops->enable_dma(chip, 0);
+ }
dma->substream = NULL;
dma->opened = 0;
return 0;
@@ -1086,9 +1068,8 @@ static int snd_atiixp_playback_open(struct snd_pcm_substream *substream)
struct atiixp *chip = snd_pcm_substream_chip(substream);
int err;
- mutex_lock(&chip->open_mutex);
+ guard(mutex)(&chip->open_mutex);
err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 0);
- mutex_unlock(&chip->open_mutex);
if (err < 0)
return err;
substream->runtime->hw.channels_max = chip->max_channels;
@@ -1102,11 +1083,9 @@ static int snd_atiixp_playback_open(struct snd_pcm_substream *substream)
static int snd_atiixp_playback_close(struct snd_pcm_substream *substream)
{
struct atiixp *chip = snd_pcm_substream_chip(substream);
- int err;
- mutex_lock(&chip->open_mutex);
- err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);
- mutex_unlock(&chip->open_mutex);
- return err;
+
+ guard(mutex)(&chip->open_mutex);
+ return snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);
}
static int snd_atiixp_capture_open(struct snd_pcm_substream *substream)
@@ -1124,34 +1103,29 @@ static int snd_atiixp_capture_close(struct snd_pcm_substream *substream)
static int snd_atiixp_spdif_open(struct snd_pcm_substream *substream)
{
struct atiixp *chip = snd_pcm_substream_chip(substream);
- int err;
- mutex_lock(&chip->open_mutex);
+
+ guard(mutex)(&chip->open_mutex);
if (chip->spdif_over_aclink) /* share DMA_PLAYBACK */
- err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 2);
+ return snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 2);
else
- err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_SPDIF], -1);
- mutex_unlock(&chip->open_mutex);
- return err;
+ return snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_SPDIF], -1);
}
static int snd_atiixp_spdif_close(struct snd_pcm_substream *substream)
{
struct atiixp *chip = snd_pcm_substream_chip(substream);
- int err;
- mutex_lock(&chip->open_mutex);
+
+ guard(mutex)(&chip->open_mutex);
if (chip->spdif_over_aclink)
- err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);
+ return snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);
else
- err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_SPDIF]);
- mutex_unlock(&chip->open_mutex);
- return err;
+ return snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_SPDIF]);
}
/* AC97 playback */
-static struct snd_pcm_ops snd_atiixp_playback_ops = {
+static const struct snd_pcm_ops snd_atiixp_playback_ops = {
.open = snd_atiixp_playback_open,
.close = snd_atiixp_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_atiixp_pcm_hw_params,
.hw_free = snd_atiixp_pcm_hw_free,
.prepare = snd_atiixp_playback_prepare,
@@ -1160,10 +1134,9 @@ static struct snd_pcm_ops snd_atiixp_playback_ops = {
};
/* AC97 capture */
-static struct snd_pcm_ops snd_atiixp_capture_ops = {
+static const struct snd_pcm_ops snd_atiixp_capture_ops = {
.open = snd_atiixp_capture_open,
.close = snd_atiixp_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_atiixp_pcm_hw_params,
.hw_free = snd_atiixp_pcm_hw_free,
.prepare = snd_atiixp_capture_prepare,
@@ -1172,10 +1145,9 @@ static struct snd_pcm_ops snd_atiixp_capture_ops = {
};
/* SPDIF playback */
-static struct snd_pcm_ops snd_atiixp_spdif_ops = {
+static const struct snd_pcm_ops snd_atiixp_spdif_ops = {
.open = snd_atiixp_spdif_open,
.close = snd_atiixp_spdif_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_atiixp_pcm_hw_params,
.hw_free = snd_atiixp_pcm_hw_free,
.prepare = snd_atiixp_spdif_prepare,
@@ -1183,7 +1155,7 @@ static struct snd_pcm_ops snd_atiixp_spdif_ops = {
.pointer = snd_atiixp_pcm_pointer,
};
-static struct ac97_pcm atiixp_pcm_defs[] __devinitdata = {
+static const struct ac97_pcm atiixp_pcm_defs[] = {
/* front PCM */
{
.exclusive = 1,
@@ -1219,7 +1191,7 @@ static struct ac97_pcm atiixp_pcm_defs[] __devinitdata = {
},
};
-static struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
+static const struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
.type = ATI_DMA_PLAYBACK,
.llp_offset = ATI_REG_OUT_DMA_LINKPTR,
.dt_cur = ATI_REG_OUT_DMA_DT_CUR,
@@ -1228,7 +1200,7 @@ static struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
.flush_dma = atiixp_out_flush_dma,
};
-static struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
+static const struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
.type = ATI_DMA_CAPTURE,
.llp_offset = ATI_REG_IN_DMA_LINKPTR,
.dt_cur = ATI_REG_IN_DMA_DT_CUR,
@@ -1237,7 +1209,7 @@ static struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
.flush_dma = atiixp_in_flush_dma,
};
-static struct atiixp_dma_ops snd_atiixp_spdif_dma_ops = {
+static const struct atiixp_dma_ops snd_atiixp_spdif_dma_ops = {
.type = ATI_DMA_SPDIF,
.llp_offset = ATI_REG_SPDF_DMA_LINKPTR,
.dt_cur = ATI_REG_SPDF_DMA_DT_CUR,
@@ -1247,9 +1219,10 @@ static struct atiixp_dma_ops snd_atiixp_spdif_dma_ops = {
};
-static int __devinit snd_atiixp_pcm_new(struct atiixp *chip)
+static int snd_atiixp_pcm_new(struct atiixp *chip)
{
struct snd_pcm *pcm;
+ struct snd_pcm_chmap *chmap;
struct snd_ac97_bus *pbus = chip->ac97_bus;
int err, i, num_pcms;
@@ -1286,12 +1259,19 @@ static int __devinit snd_atiixp_pcm_new(struct atiixp *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_atiixp_capture_ops);
pcm->private_data = chip;
- strcpy(pcm->name, "ATI IXP AC97");
+ strscpy(pcm->name, "ATI IXP AC97");
chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev, 64*1024, 128*1024);
+
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps, chip->max_channels, 0,
+ &chmap);
+ if (err < 0)
+ return err;
+ chmap->channel_mask = SND_PCM_CHMAP_MASK_2468;
+ chip->ac97[0]->chmaps[SNDRV_PCM_STREAM_PLAYBACK] = chmap;
/* no SPDIF support on codec? */
if (chip->pcms[ATI_PCM_SPDIF] && ! chip->pcms[ATI_PCM_SPDIF]->rates)
@@ -1309,14 +1289,13 @@ static int __devinit snd_atiixp_pcm_new(struct atiixp *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_spdif_ops);
pcm->private_data = chip;
if (chip->spdif_over_aclink)
- strcpy(pcm->name, "ATI IXP IEC958 (AC97)");
+ strscpy(pcm->name, "ATI IXP IEC958 (AC97)");
else
- strcpy(pcm->name, "ATI IXP IEC958 (Direct)");
+ strscpy(pcm->name, "ATI IXP IEC958 (Direct)");
chip->pcmdevs[ATI_PCMDEV_DIGITAL] = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev, 64*1024, 128*1024);
/* pre-select AC97 SPDIF slots 10/11 */
for (i = 0; i < NUM_ATI_CODECS; i++) {
@@ -1364,10 +1343,9 @@ static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id)
if (status & CODEC_CHECK_BITS) {
unsigned int detected;
detected = status & CODEC_CHECK_BITS;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
chip->codec_not_ready_bits |= detected;
atiixp_update(chip, IER, detected, 0); /* disable the detected irqs */
- spin_unlock(&chip->reg_lock);
}
/* ack */
@@ -1381,7 +1359,7 @@ static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id)
* ac97 mixer section
*/
-static struct ac97_quirk ac97_quirks[] __devinitdata = {
+static const struct ac97_quirk ac97_quirks[] = {
{
.subvendor = 0x103c,
.subdevice = 0x006b,
@@ -1403,18 +1381,18 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {
{ } /* terminator */
};
-static int __devinit snd_atiixp_mixer_new(struct atiixp *chip, int clock,
- const char *quirk_override)
+static int snd_atiixp_mixer_new(struct atiixp *chip, int clock,
+ const char *quirk_override)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
int i, err;
int codec_count;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_atiixp_ac97_write,
.read = snd_atiixp_ac97_read,
};
- static unsigned int codec_skip[NUM_ATI_CODECS] = {
+ static const unsigned int codec_skip[NUM_ATI_CODECS] = {
ATI_REG_ISR_CODEC0_NOT_READY,
ATI_REG_ISR_CODEC1_NOT_READY,
ATI_REG_ISR_CODEC2_NOT_READY,
@@ -1423,7 +1401,8 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp *chip, int clock,
if (snd_atiixp_codec_detect(chip) < 0)
return -ENXIO;
- if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus);
+ if (err < 0)
return err;
pbus->clock = clock;
chip->ac97_bus = pbus;
@@ -1439,16 +1418,18 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp *chip, int clock,
ac97.scaps = AC97_SCAP_SKIP_MODEM | AC97_SCAP_POWER_SAVE;
if (! chip->spdif_over_aclink)
ac97.scaps |= AC97_SCAP_NO_SPDIF;
- if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) {
+ err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i]);
+ if (err < 0) {
chip->ac97[i] = NULL; /* to be sure */
- snd_printdd("atiixp: codec %d not available for audio\n", i);
+ dev_dbg(chip->card->dev,
+ "codec %d not available for audio\n", i);
continue;
}
codec_count++;
}
if (! codec_count) {
- snd_printk(KERN_ERR "atiixp: no codec available\n");
+ dev_err(chip->card->dev, "no codec available\n");
return -ENODEV;
}
@@ -1458,52 +1439,29 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp *chip, int clock,
}
-#ifdef CONFIG_PM
/*
* power management
*/
-static int snd_atiixp_suspend(struct pci_dev *pci, pm_message_t state)
+static int snd_atiixp_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct atiixp *chip = card->private_data;
int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- for (i = 0; i < NUM_ATI_PCMDEVS; i++)
- if (chip->pcmdevs[i]) {
- struct atiixp_dma *dma = &chip->dmas[i];
- if (dma->substream && dma->running)
- dma->saved_curptr = readl(chip->remap_addr +
- dma->ops->dt_cur);
- snd_pcm_suspend_all(chip->pcmdevs[i]);
- }
for (i = 0; i < NUM_ATI_CODECS; i++)
snd_ac97_suspend(chip->ac97[i]);
snd_atiixp_aclink_down(chip);
snd_atiixp_chip_stop(chip);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_atiixp_resume(struct pci_dev *pci)
+static int snd_atiixp_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct atiixp *chip = card->private_data;
int i;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "atiixp: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
snd_atiixp_aclink_reset(chip);
snd_atiixp_chip_start(chip);
@@ -1518,18 +1476,15 @@ static int snd_atiixp_resume(struct pci_dev *pci)
dma->substream->ops->prepare(dma->substream);
writel((u32)dma->desc_buf.addr | ATI_REG_LINKPTR_EN,
chip->remap_addr + dma->ops->llp_offset);
- writel(dma->saved_curptr, chip->remap_addr +
- dma->ops->dt_cur);
}
}
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume);
-#ifdef CONFIG_PROC_FS
/*
* proc interface for register dump
*/
@@ -1544,134 +1499,89 @@ static void snd_atiixp_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "%02x: %08x\n", i, readl(chip->remap_addr + i));
}
-static void __devinit snd_atiixp_proc_init(struct atiixp *chip)
+static void snd_atiixp_proc_init(struct atiixp *chip)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(chip->card, "atiixp", &entry))
- snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read);
+ snd_card_ro_proc_new(chip->card, "atiixp", chip, snd_atiixp_proc_read);
}
-#else /* !CONFIG_PROC_FS */
-#define snd_atiixp_proc_init(chip)
-#endif
/*
* destructor
*/
-static int snd_atiixp_free(struct atiixp *chip)
-{
- if (chip->irq < 0)
- goto __hw_end;
- snd_atiixp_chip_stop(chip);
-
- __hw_end:
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- if (chip->remap_addr)
- iounmap(chip->remap_addr);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
-}
-
-static int snd_atiixp_dev_free(struct snd_device *device)
+static void snd_atiixp_free(struct snd_card *card)
{
- struct atiixp *chip = device->device_data;
- return snd_atiixp_free(chip);
+ snd_atiixp_chip_stop(card->private_data);
}
/*
* constructor for chip instance
*/
-static int __devinit snd_atiixp_create(struct snd_card *card,
- struct pci_dev *pci,
- struct atiixp **r_chip)
+static int snd_atiixp_init(struct snd_card *card, struct pci_dev *pci)
{
- static struct snd_device_ops ops = {
- .dev_free = snd_atiixp_dev_free,
- };
- struct atiixp *chip;
+ struct atiixp *chip = card->private_data;
int err;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
spin_lock_init(&chip->reg_lock);
mutex_init(&chip->open_mutex);
chip->card = card;
chip->pci = pci;
chip->irq = -1;
- if ((err = pci_request_regions(pci, "ATI IXP AC97")) < 0) {
- pci_disable_device(pci);
- kfree(chip);
- return err;
- }
+ chip->remap_addr = pcim_iomap_region(pci, 0, "ATI IXP AC97");
+ if (IS_ERR(chip->remap_addr))
+ return PTR_ERR(chip->remap_addr);
chip->addr = pci_resource_start(pci, 0);
- chip->remap_addr = pci_ioremap_bar(pci, 0);
- if (chip->remap_addr == NULL) {
- snd_printk(KERN_ERR "AC'97 space ioremap problem\n");
- snd_atiixp_free(chip);
- return -EIO;
- }
- if (request_irq(pci->irq, snd_atiixp_interrupt, IRQF_SHARED,
- card->shortname, chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_atiixp_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_atiixp_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_atiixp_free;
pci_set_master(pci);
- synchronize_irq(chip->irq);
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_atiixp_free(chip);
- return err;
- }
-
- snd_card_set_dev(card, &pci->dev);
- *r_chip = chip;
return 0;
}
-static int __devinit snd_atiixp_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_atiixp_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
struct snd_card *card;
struct atiixp *chip;
int err;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index, id, THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- strcpy(card->driver, spdif_aclink ? "ATIIXP" : "ATIIXP-SPDMA");
- strcpy(card->shortname, "ATI IXP");
- if ((err = snd_atiixp_create(card, pci, &chip)) < 0)
- goto __error;
- card->private_data = chip;
+ strscpy(card->driver, spdif_aclink ? "ATIIXP" : "ATIIXP-SPDMA");
+ strscpy(card->shortname, "ATI IXP");
+ err = snd_atiixp_init(card, pci);
+ if (err < 0)
+ return err;
- if ((err = snd_atiixp_aclink_reset(chip)) < 0)
- goto __error;
+ err = snd_atiixp_aclink_reset(chip);
+ if (err < 0)
+ return err;
chip->spdif_over_aclink = spdif_aclink;
- if ((err = snd_atiixp_mixer_new(chip, ac97_clock, ac97_quirk)) < 0)
- goto __error;
+ err = snd_atiixp_mixer_new(chip, ac97_clock, ac97_quirk);
+ if (err < 0)
+ return err;
- if ((err = snd_atiixp_pcm_new(chip)) < 0)
- goto __error;
+ err = snd_atiixp_pcm_new(chip);
+ if (err < 0)
+ return err;
snd_atiixp_proc_init(chip);
@@ -1683,44 +1593,27 @@ static int __devinit snd_atiixp_probe(struct pci_dev *pci,
chip->ac97[0] ? snd_ac97_get_short_name(chip->ac97[0]) : "?",
chip->addr, chip->irq);
- if ((err = snd_card_register(card)) < 0)
- goto __error;
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
pci_set_drvdata(pci, card);
return 0;
-
- __error:
- snd_card_free(card);
- return err;
}
-static void __devexit snd_atiixp_remove(struct pci_dev *pci)
+static int snd_atiixp_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_atiixp_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "ATI IXP AC97 controller",
+static struct pci_driver atiixp_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_atiixp_ids,
.probe = snd_atiixp_probe,
- .remove = __devexit_p(snd_atiixp_remove),
-#ifdef CONFIG_PM
- .suspend = snd_atiixp_suspend,
- .resume = snd_atiixp_resume,
-#endif
+ .driver = {
+ .pm = &snd_atiixp_pm,
+ },
};
-
-static int __init alsa_card_atiixp_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_atiixp_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_atiixp_init)
-module_exit(alsa_card_atiixp_exit)
+module_pci_driver(atiixp_driver);
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index 91d7036b6411..91f31e2ad3d3 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -1,31 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ATI IXP 150/200/250 AC97 modem controllers
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -37,7 +23,6 @@
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("ATI IXP MC97 controller");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ATI,IXP150/200/250}}");
static int index = -2; /* Exclude the first card */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
@@ -51,7 +36,7 @@ module_param(ac97_clock, int, 0444);
MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz).");
/* just for backward compatibility */
-static int enable;
+static bool enable;
module_param(enable, bool, 0444);
@@ -183,10 +168,10 @@ struct atiixp_modem;
*/
struct atiixp_dma_desc {
- u32 addr; /* DMA buffer address */
+ __le32 addr; /* DMA buffer address */
u16 status; /* status bits */
u16 size; /* size of the packet in dwords */
- u32 next; /* address of the next packet descriptor */
+ __le32 next; /* address of the next packet descriptor */
};
/*
@@ -261,7 +246,7 @@ struct atiixp_modem {
/*
*/
-static DEFINE_PCI_DEVICE_TABLE(snd_atiixp_ids) = {
+static const struct pci_device_id snd_atiixp_ids[] = {
{ PCI_VDEVICE(ATI, 0x434d), 0 }, /* SB200 */
{ PCI_VDEVICE(ATI, 0x4378), 0 }, /* SB400 */
{ 0, }
@@ -329,13 +314,12 @@ static int atiixp_build_dma_packets(struct atiixp_modem *chip,
{
unsigned int i;
u32 addr, desc_addr;
- unsigned long flags;
if (periods > ATI_MAX_DESCRIPTORS)
return -ENOMEM;
if (dma->desc_buf.area == NULL) {
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
ATI_DESC_LIST_SIZE, &dma->desc_buf) < 0)
return -ENOMEM;
dma->period_bytes = dma->periods = 0; /* clear */
@@ -345,11 +329,11 @@ static int atiixp_build_dma_packets(struct atiixp_modem *chip,
return 0;
/* reset DMA before changing the descriptor table */
- spin_lock_irqsave(&chip->reg_lock, flags);
- writel(0, chip->remap_addr + dma->ops->llp_offset);
- dma->ops->enable_dma(chip, 0);
- dma->ops->enable_dma(chip, 1);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ writel(0, chip->remap_addr + dma->ops->llp_offset);
+ dma->ops->enable_dma(chip, 0);
+ dma->ops->enable_dma(chip, 1);
+ }
/* fill the entries */
addr = (u32)substream->runtime->dma_addr;
@@ -400,7 +384,7 @@ static int snd_atiixp_acquire_codec(struct atiixp_modem *chip)
while (atiixp_read(chip, PHYS_OUT_ADDR) & ATI_REG_PHYS_OUT_ADDR_EN) {
if (! timeout--) {
- snd_printk(KERN_WARNING "atiixp-modem: codec acquire timeout\n");
+ dev_warn(chip->card->dev, "codec acquire timeout\n");
return -EBUSY;
}
udelay(1);
@@ -433,7 +417,7 @@ static unsigned short snd_atiixp_codec_read(struct atiixp_modem *chip,
} while (--timeout);
/* time out may happen during reset */
if (reg < 0x7c)
- snd_printk(KERN_WARNING "atiixp-modem: codec read timeout (reg %x)\n", reg);
+ dev_warn(chip->card->dev, "codec read timeout (reg %x)\n", reg);
return 0xffff;
}
@@ -498,8 +482,8 @@ static int snd_atiixp_aclink_reset(struct atiixp_modem *chip)
atiixp_read(chip, CMD);
msleep(1);
atiixp_update(chip, CMD, ATI_REG_CMD_AC_RESET, ATI_REG_CMD_AC_RESET);
- if (--timeout) {
- snd_printk(KERN_ERR "atiixp-modem: codec reset timeout\n");
+ if (!--timeout) {
+ dev_err(chip->card->dev, "codec reset timeout\n");
break;
}
}
@@ -511,7 +495,6 @@ static int snd_atiixp_aclink_reset(struct atiixp_modem *chip)
return 0;
}
-#ifdef CONFIG_PM
static int snd_atiixp_aclink_down(struct atiixp_modem *chip)
{
// if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */
@@ -521,7 +504,6 @@ static int snd_atiixp_aclink_down(struct atiixp_modem *chip)
ATI_REG_CMD_POWERDOWN);
return 0;
}
-#endif
/*
* auto-detection of codecs
@@ -553,7 +535,7 @@ static int snd_atiixp_codec_detect(struct atiixp_modem *chip)
atiixp_write(chip, IER, 0); /* disable irqs */
if ((chip->codec_not_ready_bits & ALL_CODEC_NOT_READY) == ALL_CODEC_NOT_READY) {
- snd_printk(KERN_ERR "atiixp-modem: no codec detected!\n");
+ dev_err(chip->card->dev, "no codec detected!\n");
return -ENXIO;
}
return 0;
@@ -624,7 +606,7 @@ static snd_pcm_uframes_t snd_atiixp_pcm_pointer(struct snd_pcm_substream *substr
continue;
return bytes_to_frames(runtime, curptr);
}
- snd_printd("atiixp-modem: invalid DMA pointer read 0x%x (buf=%x)\n",
+ dev_dbg(chip->card->dev, "invalid DMA pointer read 0x%x (buf=%x)\n",
readl(chip->remap_addr + dma->ops->dt_cur), dma->buf_addr);
return 0;
}
@@ -637,8 +619,8 @@ static void snd_atiixp_xrun_dma(struct atiixp_modem *chip,
{
if (! dma->substream || ! dma->running)
return;
- snd_printdd("atiixp-modem: XRUN detected (DMA %d)\n", dma->ops->type);
- snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN);
+ dev_dbg(chip->card->dev, "XRUN detected (DMA %d)\n", dma->ops->type);
+ snd_pcm_stop_xrun(dma->substream);
}
/*
@@ -678,7 +660,7 @@ static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
!dma->ops->flush_dma))
return -EINVAL;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch(cmd) {
case SNDRV_PCM_TRIGGER_START:
dma->ops->enable_transfer(chip, 1);
@@ -699,7 +681,6 @@ static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
snd_atiixp_check_bus_busy(chip);
}
}
- spin_unlock(&chip->reg_lock);
return err;
}
@@ -770,13 +751,12 @@ static int snd_atiixp_playback_prepare(struct snd_pcm_substream *substream)
struct atiixp_modem *chip = snd_pcm_substream_chip(substream);
unsigned int data;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
/* set output threshold */
data = atiixp_read(chip, MODEM_OUT_FIFO);
data &= ~ATI_REG_MODEM_OUT1_DMA_THRESHOLD_MASK;
data |= 0x04 << ATI_REG_MODEM_OUT1_DMA_THRESHOLD_SHIFT;
atiixp_write(chip, MODEM_OUT_FIFO, data);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -797,9 +777,6 @@ static int snd_atiixp_pcm_hw_params(struct snd_pcm_substream *substream,
int err;
int i;
- err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
dma->buf_addr = substream->runtime->dma_addr;
dma->buf_bytes = params_buffer_bytes(hw_params);
@@ -826,7 +803,6 @@ static int snd_atiixp_pcm_hw_free(struct snd_pcm_substream *substream)
struct atiixp_dma *dma = substream->runtime->private_data;
atiixp_clear_dma_packets(chip, dma, substream);
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -834,7 +810,7 @@ static int snd_atiixp_pcm_hw_free(struct snd_pcm_substream *substream)
/*
* pcm hardware definition, identical for all DMA types
*/
-static struct snd_pcm_hardware snd_atiixp_pcm_hw =
+static const struct snd_pcm_hardware snd_atiixp_pcm_hw =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -860,8 +836,8 @@ static int snd_atiixp_pcm_open(struct snd_pcm_substream *substream,
struct atiixp_modem *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- static unsigned int rates[] = { 8000, 9600, 12000, 16000 };
- static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+ static const unsigned int rates[] = { 8000, 9600, 12000, 16000 };
+ static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
@@ -875,19 +851,19 @@ static int snd_atiixp_pcm_open(struct snd_pcm_substream *substream,
dma->substream = substream;
runtime->hw = snd_atiixp_pcm_hw;
dma->ac97_pcm_type = pcm_type;
- if ((err = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &hw_constraints_rates)) < 0)
+ err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &hw_constraints_rates);
+ if (err < 0)
return err;
- if ((err = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
return err;
runtime->private_data = dma;
/* enable DMA bits */
- spin_lock_irq(&chip->reg_lock);
- dma->ops->enable_dma(chip, 1);
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ dma->ops->enable_dma(chip, 1);
+ }
dma->opened = 1;
return 0;
@@ -900,9 +876,9 @@ static int snd_atiixp_pcm_close(struct snd_pcm_substream *substream,
/* disable DMA bits */
if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma))
return -EINVAL;
- spin_lock_irq(&chip->reg_lock);
- dma->ops->enable_dma(chip, 0);
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ dma->ops->enable_dma(chip, 0);
+ }
dma->substream = NULL;
dma->opened = 0;
return 0;
@@ -913,24 +889,17 @@ static int snd_atiixp_pcm_close(struct snd_pcm_substream *substream,
static int snd_atiixp_playback_open(struct snd_pcm_substream *substream)
{
struct atiixp_modem *chip = snd_pcm_substream_chip(substream);
- int err;
- mutex_lock(&chip->open_mutex);
- err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 0);
- mutex_unlock(&chip->open_mutex);
- if (err < 0)
- return err;
- return 0;
+ guard(mutex)(&chip->open_mutex);
+ return snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 0);
}
static int snd_atiixp_playback_close(struct snd_pcm_substream *substream)
{
struct atiixp_modem *chip = snd_pcm_substream_chip(substream);
- int err;
- mutex_lock(&chip->open_mutex);
- err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);
- mutex_unlock(&chip->open_mutex);
- return err;
+
+ guard(mutex)(&chip->open_mutex);
+ return snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);
}
static int snd_atiixp_capture_open(struct snd_pcm_substream *substream)
@@ -947,10 +916,9 @@ static int snd_atiixp_capture_close(struct snd_pcm_substream *substream)
/* AC97 playback */
-static struct snd_pcm_ops snd_atiixp_playback_ops = {
+static const struct snd_pcm_ops snd_atiixp_playback_ops = {
.open = snd_atiixp_playback_open,
.close = snd_atiixp_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_atiixp_pcm_hw_params,
.hw_free = snd_atiixp_pcm_hw_free,
.prepare = snd_atiixp_playback_prepare,
@@ -959,10 +927,9 @@ static struct snd_pcm_ops snd_atiixp_playback_ops = {
};
/* AC97 capture */
-static struct snd_pcm_ops snd_atiixp_capture_ops = {
+static const struct snd_pcm_ops snd_atiixp_capture_ops = {
.open = snd_atiixp_capture_open,
.close = snd_atiixp_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_atiixp_pcm_hw_params,
.hw_free = snd_atiixp_pcm_hw_free,
.prepare = snd_atiixp_capture_prepare,
@@ -970,7 +937,7 @@ static struct snd_pcm_ops snd_atiixp_capture_ops = {
.pointer = snd_atiixp_pcm_pointer,
};
-static struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
+static const struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
.type = ATI_DMA_PLAYBACK,
.llp_offset = ATI_REG_MODEM_OUT_DMA1_LINKPTR,
.dt_cur = ATI_REG_MODEM_OUT_DMA1_DT_CUR,
@@ -979,7 +946,7 @@ static struct atiixp_dma_ops snd_atiixp_playback_dma_ops = {
.flush_dma = atiixp_out_flush_dma,
};
-static struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
+static const struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
.type = ATI_DMA_CAPTURE,
.llp_offset = ATI_REG_MODEM_IN_DMA_LINKPTR,
.dt_cur = ATI_REG_MODEM_IN_DMA_DT_CUR,
@@ -988,7 +955,7 @@ static struct atiixp_dma_ops snd_atiixp_capture_dma_ops = {
.flush_dma = atiixp_in_flush_dma,
};
-static int __devinit snd_atiixp_pcm_new(struct atiixp_modem *chip)
+static int snd_atiixp_pcm_new(struct atiixp_modem *chip)
{
struct snd_pcm *pcm;
int err;
@@ -1005,12 +972,11 @@ static int __devinit snd_atiixp_pcm_new(struct atiixp_modem *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_atiixp_capture_ops);
pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
pcm->private_data = chip;
- strcpy(pcm->name, "ATI IXP MC97");
+ strscpy(pcm->name, "ATI IXP MC97");
chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev, 64*1024, 128*1024);
return 0;
}
@@ -1044,10 +1010,9 @@ static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id)
if (status & CODEC_CHECK_BITS) {
unsigned int detected;
detected = status & CODEC_CHECK_BITS;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
chip->codec_not_ready_bits |= detected;
atiixp_update(chip, IER, detected, 0); /* disable the detected irqs */
- spin_unlock(&chip->reg_lock);
}
/* ack */
@@ -1061,17 +1026,17 @@ static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id)
* ac97 mixer section
*/
-static int __devinit snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock)
+static int snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
int i, err;
int codec_count;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_atiixp_ac97_write,
.read = snd_atiixp_ac97_read,
};
- static unsigned int codec_skip[NUM_ATI_CODECS] = {
+ static const unsigned int codec_skip[NUM_ATI_CODECS] = {
ATI_REG_ISR_CODEC0_NOT_READY,
ATI_REG_ISR_CODEC1_NOT_READY,
ATI_REG_ISR_CODEC2_NOT_READY,
@@ -1080,7 +1045,8 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock)
if (snd_atiixp_codec_detect(chip) < 0)
return -ENXIO;
- if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus);
+ if (err < 0)
return err;
pbus->clock = clock;
chip->ac97_bus = pbus;
@@ -1094,16 +1060,18 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock)
ac97.pci = chip->pci;
ac97.num = i;
ac97.scaps = AC97_SCAP_SKIP_AUDIO | AC97_SCAP_POWER_SAVE;
- if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) {
+ err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i]);
+ if (err < 0) {
chip->ac97[i] = NULL; /* to be sure */
- snd_printdd("atiixp-modem: codec %d not available for modem\n", i);
+ dev_dbg(chip->card->dev,
+ "codec %d not available for modem\n", i);
continue;
}
codec_count++;
}
if (! codec_count) {
- snd_printk(KERN_ERR "atiixp-modem: no codec available\n");
+ dev_err(chip->card->dev, "no codec available\n");
return -ENODEV;
}
@@ -1113,46 +1081,29 @@ static int __devinit snd_atiixp_mixer_new(struct atiixp_modem *chip, int clock)
}
-#ifdef CONFIG_PM
/*
* power management
*/
-static int snd_atiixp_suspend(struct pci_dev *pci, pm_message_t state)
+static int snd_atiixp_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct atiixp_modem *chip = card->private_data;
int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- for (i = 0; i < NUM_ATI_PCMDEVS; i++)
- snd_pcm_suspend_all(chip->pcmdevs[i]);
for (i = 0; i < NUM_ATI_CODECS; i++)
snd_ac97_suspend(chip->ac97[i]);
snd_atiixp_aclink_down(chip);
snd_atiixp_chip_stop(chip);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_atiixp_resume(struct pci_dev *pci)
+static int snd_atiixp_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct atiixp_modem *chip = card->private_data;
int i;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "atiixp-modem: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
snd_atiixp_aclink_reset(chip);
snd_atiixp_chip_start(chip);
@@ -1162,10 +1113,9 @@ static int snd_atiixp_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_atiixp_pm, snd_atiixp_suspend, snd_atiixp_resume);
-#ifdef CONFIG_PROC_FS
/*
* proc interface for register dump
*/
@@ -1180,132 +1130,88 @@ static void snd_atiixp_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "%02x: %08x\n", i, readl(chip->remap_addr + i));
}
-static void __devinit snd_atiixp_proc_init(struct atiixp_modem *chip)
+static void snd_atiixp_proc_init(struct atiixp_modem *chip)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(chip->card, "atiixp-modem", &entry))
- snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read);
+ snd_card_ro_proc_new(chip->card, "atiixp-modem", chip,
+ snd_atiixp_proc_read);
}
-#else
-#define snd_atiixp_proc_init(chip)
-#endif
/*
* destructor
*/
-static int snd_atiixp_free(struct atiixp_modem *chip)
-{
- if (chip->irq < 0)
- goto __hw_end;
- snd_atiixp_chip_stop(chip);
-
- __hw_end:
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- if (chip->remap_addr)
- iounmap(chip->remap_addr);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
-}
-
-static int snd_atiixp_dev_free(struct snd_device *device)
+static void snd_atiixp_free(struct snd_card *card)
{
- struct atiixp_modem *chip = device->device_data;
- return snd_atiixp_free(chip);
+ snd_atiixp_chip_stop(card->private_data);
}
/*
* constructor for chip instance
*/
-static int __devinit snd_atiixp_create(struct snd_card *card,
- struct pci_dev *pci,
- struct atiixp_modem **r_chip)
+static int snd_atiixp_init(struct snd_card *card, struct pci_dev *pci)
{
- static struct snd_device_ops ops = {
- .dev_free = snd_atiixp_dev_free,
- };
- struct atiixp_modem *chip;
+ struct atiixp_modem *chip = card->private_data;
int err;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
spin_lock_init(&chip->reg_lock);
mutex_init(&chip->open_mutex);
chip->card = card;
chip->pci = pci;
chip->irq = -1;
- if ((err = pci_request_regions(pci, "ATI IXP MC97")) < 0) {
- kfree(chip);
- pci_disable_device(pci);
- return err;
- }
+ chip->remap_addr = pcim_iomap_region(pci, 0, "ATI IXP MC97");
+ if (IS_ERR(chip->remap_addr))
+ return PTR_ERR(chip->remap_addr);
chip->addr = pci_resource_start(pci, 0);
- chip->remap_addr = pci_ioremap_bar(pci, 0);
- if (chip->remap_addr == NULL) {
- snd_printk(KERN_ERR "AC'97 space ioremap problem\n");
- snd_atiixp_free(chip);
- return -EIO;
- }
- if (request_irq(pci->irq, snd_atiixp_interrupt, IRQF_SHARED,
- card->shortname, chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_atiixp_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_atiixp_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_atiixp_free;
pci_set_master(pci);
- synchronize_irq(chip->irq);
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_atiixp_free(chip);
- return err;
- }
- snd_card_set_dev(card, &pci->dev);
-
- *r_chip = chip;
return 0;
}
-static int __devinit snd_atiixp_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_atiixp_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
struct snd_card *card;
struct atiixp_modem *chip;
int err;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index, id, THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- strcpy(card->driver, "ATIIXP-MODEM");
- strcpy(card->shortname, "ATI IXP Modem");
- if ((err = snd_atiixp_create(card, pci, &chip)) < 0)
- goto __error;
- card->private_data = chip;
+ strscpy(card->driver, "ATIIXP-MODEM");
+ strscpy(card->shortname, "ATI IXP Modem");
+ err = snd_atiixp_init(card, pci);
+ if (err < 0)
+ return err;
- if ((err = snd_atiixp_aclink_reset(chip)) < 0)
- goto __error;
+ err = snd_atiixp_aclink_reset(chip);
+ if (err < 0)
+ return err;
- if ((err = snd_atiixp_mixer_new(chip, ac97_clock)) < 0)
- goto __error;
+ err = snd_atiixp_mixer_new(chip, ac97_clock);
+ if (err < 0)
+ return err;
- if ((err = snd_atiixp_pcm_new(chip)) < 0)
- goto __error;
+ err = snd_atiixp_pcm_new(chip);
+ if (err < 0)
+ return err;
snd_atiixp_proc_init(chip);
@@ -1314,44 +1220,27 @@ static int __devinit snd_atiixp_probe(struct pci_dev *pci,
sprintf(card->longname, "%s rev %x at 0x%lx, irq %i",
card->shortname, pci->revision, chip->addr, chip->irq);
- if ((err = snd_card_register(card)) < 0)
- goto __error;
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
pci_set_drvdata(pci, card);
return 0;
-
- __error:
- snd_card_free(card);
- return err;
}
-static void __devexit snd_atiixp_remove(struct pci_dev *pci)
+static int snd_atiixp_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_atiixp_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "ATI IXP MC97 controller",
+static struct pci_driver atiixp_modem_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_atiixp_ids,
.probe = snd_atiixp_probe,
- .remove = __devexit_p(snd_atiixp_remove),
-#ifdef CONFIG_PM
- .suspend = snd_atiixp_suspend,
- .resume = snd_atiixp_resume,
-#endif
+ .driver = {
+ .pm = &snd_atiixp_pm,
+ },
};
-
-static int __init alsa_card_atiixp_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_atiixp_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_atiixp_init)
-module_exit(alsa_card_atiixp_exit)
+module_pci_driver(atiixp_modem_driver);
diff --git a/sound/pci/au88x0/Makefile b/sound/pci/au88x0/Makefile
index d0a66bc5d4a7..5ec5abdee28d 100644
--- a/sound/pci/au88x0/Makefile
+++ b/sound/pci/au88x0/Makefile
@@ -1,6 +1,7 @@
-snd-au8810-objs := au8810.o
-snd-au8820-objs := au8820.o
-snd-au8830-objs := au8830.o
+# SPDX-License-Identifier: GPL-2.0
+snd-au8810-y := au8810.o
+snd-au8820-y := au8820.o
+snd-au8830-y := au8830.o
obj-$(CONFIG_SND_AU8810) += snd-au8810.o
obj-$(CONFIG_SND_AU8820) += snd-au8820.o
diff --git a/sound/pci/au88x0/au8810.c b/sound/pci/au88x0/au8810.c
index aa51cc7771dd..b2bfa50bfe30 100644
--- a/sound/pci/au88x0/au8810.c
+++ b/sound/pci/au88x0/au8810.c
@@ -1,6 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
#include "au8810.h"
#include "au88x0.h"
-static DEFINE_PCI_DEVICE_TABLE(snd_vortex_ids) = {
+static const struct pci_device_id snd_vortex_ids[] = {
{PCI_VDEVICE(AUREAL, PCI_DEVICE_ID_AUREAL_ADVANTAGE), 1,},
{0,}
};
diff --git a/sound/pci/au88x0/au8810.h b/sound/pci/au88x0/au8810.h
index 5d69c31fe3f4..94f11032067e 100644
--- a/sound/pci/au88x0/au8810.h
+++ b/sound/pci/au88x0/au8810.h
@@ -1,10 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
Aureal Advantage Soundcard driver.
*/
#define CHIP_AU8810
-#define CARD_NAME "Aureal Advantage 3D Sound Processor"
+#define CARD_NAME "Aureal Advantage"
#define CARD_NAME_SHORT "au8810"
#define NR_ADB 0x10
diff --git a/sound/pci/au88x0/au8820.c b/sound/pci/au88x0/au8820.c
index 2f321e7306cd..dbc2263b49c6 100644
--- a/sound/pci/au88x0/au8820.c
+++ b/sound/pci/au88x0/au8820.c
@@ -1,6 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
#include "au8820.h"
#include "au88x0.h"
-static DEFINE_PCI_DEVICE_TABLE(snd_vortex_ids) = {
+static const struct pci_device_id snd_vortex_ids[] = {
{PCI_VDEVICE(AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_1), 0,},
{0,}
};
diff --git a/sound/pci/au88x0/au8820.h b/sound/pci/au88x0/au8820.h
index abbe85e4f7a9..8a128e8febbb 100644
--- a/sound/pci/au88x0/au8820.h
+++ b/sound/pci/au88x0/au8820.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
Aureal Vortex Soundcard driver.
@@ -11,7 +12,7 @@
#define CHIP_AU8820
-#define CARD_NAME "Aureal Vortex 3D Sound Processor"
+#define CARD_NAME "Aureal Vortex"
#define CARD_NAME_SHORT "au8820"
/* Number of ADB and WT channels */
diff --git a/sound/pci/au88x0/au8830.c b/sound/pci/au88x0/au8830.c
index 279b78f06d22..e963c4e2f026 100644
--- a/sound/pci/au88x0/au8830.c
+++ b/sound/pci/au88x0/au8830.c
@@ -1,6 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
#include "au8830.h"
#include "au88x0.h"
-static DEFINE_PCI_DEVICE_TABLE(snd_vortex_ids) = {
+static const struct pci_device_id snd_vortex_ids[] = {
{PCI_VDEVICE(AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_2), 0,},
{0,}
};
diff --git a/sound/pci/au88x0/au8830.h b/sound/pci/au88x0/au8830.h
index 04ece1b1c218..40f671ffd45a 100644
--- a/sound/pci/au88x0/au8830.h
+++ b/sound/pci/au88x0/au8830.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
Aureal Vortex Soundcard driver.
@@ -11,7 +12,7 @@
#define CHIP_AU8830
-#define CARD_NAME "Aureal Vortex 2 3D Sound Processor"
+#define CARD_NAME "Aureal Vortex 2"
#define CARD_NAME_SHORT "au8830"
#define NR_ADB 0x20
diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c
index 7b72c88e449d..bb02945793f0 100644
--- a/sound/pci/au88x0/au88x0.c
+++ b/sound/pci/au88x0/au88x0.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for the Aureal Vortex family of soundprocessors.
* Author: Manuel Jander (mjander@embedded.cl)
@@ -9,7 +10,7 @@
* Thanks to the ALSA developers, they helped a lot working out
* the ALSA part.
* Thanks also to Sourceforge for maintaining the old binary drivers,
- * and the forum, where developers could comunicate.
+ * and the forum, where developers could communicate.
*
* Now at least i can play Legacy DOOM with MIDI music :-)
*/
@@ -19,14 +20,14 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <sound/initval.h>
// module parameters (see "Module Parameters")
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
static int pcifix[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 255 };
module_param_array(index, int, NULL, 0444);
@@ -40,19 +41,17 @@ MODULE_PARM_DESC(pcifix, "Enable VIA-workaround for " CARD_NAME " soundcard.");
MODULE_DESCRIPTION("Aureal vortex");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Aureal Semiconductor Inc., Aureal Vortex Sound Processor}}");
-
MODULE_DEVICE_TABLE(pci, snd_vortex_ids);
static void vortex_fix_latency(struct pci_dev *vortex)
{
int rc;
- if (!(rc = pci_write_config_byte(vortex, 0x40, 0xff))) {
- printk(KERN_INFO CARD_NAME
- ": vortex latency is 0xff\n");
+ rc = pci_write_config_byte(vortex, 0x40, 0xff);
+ if (!rc) {
+ dev_info(&vortex->dev, "vortex latency is 0xff\n");
} else {
- printk(KERN_WARNING CARD_NAME
- ": could not set vortex latency: pci error 0x%x\n", rc);
+ dev_warn(&vortex->dev,
+ "could not set vortex latency: pci error 0x%x\n", rc);
}
}
@@ -67,18 +66,20 @@ static void vortex_fix_agp_bridge(struct pci_dev *via)
* read the config and it is not already set
*/
- if (!(rc = pci_read_config_byte(via, 0x42, &value))
- && ((value & 0x10)
- || !(rc = pci_write_config_byte(via, 0x42, value | 0x10)))) {
- printk(KERN_INFO CARD_NAME
- ": bridge config is 0x%x\n", value | 0x10);
+ rc = pci_read_config_byte(via, 0x42, &value);
+ if (!rc) {
+ if (!(value & 0x10))
+ rc = pci_write_config_byte(via, 0x42, value | 0x10);
+ }
+ if (!rc) {
+ dev_info(&via->dev, "bridge config is 0x%x\n", value | 0x10);
} else {
- printk(KERN_WARNING CARD_NAME
- ": could not set vortex latency: pci error 0x%x\n", rc);
+ dev_warn(&via->dev,
+ "could not set vortex latency: pci error 0x%x\n", rc);
}
}
-static void __devinit snd_vortex_workaround(struct pci_dev *vortex, int fix)
+static void snd_vortex_workaround(struct pci_dev *vortex, int fix)
{
struct pci_dev *via = NULL;
@@ -97,21 +98,24 @@ static void __devinit snd_vortex_workaround(struct pci_dev *vortex, int fix)
PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL);
}
if (via) {
- printk(KERN_INFO CARD_NAME ": Activating latency workaround...\n");
+ dev_info(&vortex->dev,
+ "Activating latency workaround...\n");
vortex_fix_latency(vortex);
vortex_fix_agp_bridge(via);
}
} else {
if (fix & 0x1)
vortex_fix_latency(vortex);
- if ((fix & 0x2) && (via = pci_get_device(PCI_VENDOR_ID_VIA,
- PCI_DEVICE_ID_VIA_8365_1, NULL)))
- vortex_fix_agp_bridge(via);
- if ((fix & 0x4) && (via = pci_get_device(PCI_VENDOR_ID_VIA,
- PCI_DEVICE_ID_VIA_82C598_1, NULL)))
- vortex_fix_agp_bridge(via);
- if ((fix & 0x8) && (via = pci_get_device(PCI_VENDOR_ID_AMD,
- PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL)))
+ if (fix & 0x2)
+ via = pci_get_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_8365_1, NULL);
+ else if (fix & 0x4)
+ via = pci_get_device(PCI_VENDOR_ID_VIA,
+ PCI_DEVICE_ID_VIA_82C598_1, NULL);
+ else if (fix & 0x8)
+ via = pci_get_device(PCI_VENDOR_ID_AMD,
+ PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL);
+ if (via)
vortex_fix_agp_bridge(via);
}
pci_dev_put(via);
@@ -119,56 +123,35 @@ static void __devinit snd_vortex_workaround(struct pci_dev *vortex, int fix)
// component-destructor
// (see "Management of Cards and Components")
-static int snd_vortex_dev_free(struct snd_device *device)
+static void snd_vortex_free(struct snd_card *card)
{
- vortex_t *vortex = device->device_data;
+ vortex_t *vortex = card->private_data;
vortex_gameport_unregister(vortex);
vortex_core_shutdown(vortex);
- // Take down PCI interface.
- free_irq(vortex->irq, vortex);
- iounmap(vortex->mmio);
- pci_release_regions(vortex->pci_dev);
- pci_disable_device(vortex->pci_dev);
- kfree(vortex);
-
- return 0;
}
// chip-specific constructor
// (see "Management of Cards and Components")
-static int __devinit
-snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip)
+static int
+snd_vortex_create(struct snd_card *card, struct pci_dev *pci)
{
- vortex_t *chip;
+ vortex_t *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_vortex_dev_free,
- };
-
- *rchip = NULL;
// check PCI availability (DMA).
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
- printk(KERN_ERR "error to set DMA mask\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
+ dev_err(card->dev, "error to set DMA mask\n");
return -ENXIO;
}
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
chip->card = card;
// initialize the stuff
chip->pci_dev = pci;
- chip->io = pci_resource_start(pci, 0);
chip->vendor = pci->vendor;
chip->device = pci->device;
chip->card = card;
@@ -177,65 +160,39 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip)
// (1) PCI resource allocation
// Get MMIO area
//
- if ((err = pci_request_regions(pci, CARD_NAME_SHORT)) != 0)
- goto regions_out;
-
- chip->mmio = pci_ioremap_bar(pci, 0);
- if (!chip->mmio) {
- printk(KERN_ERR "MMIO area remap failed.\n");
- err = -ENOMEM;
- goto ioremap_out;
- }
+ chip->mmio = pcim_iomap_region(pci, 0, KBUILD_MODNAME);
+ if (IS_ERR(chip->mmio))
+ return PTR_ERR(chip->mmio);
+
+ chip->io = pci_resource_start(pci, 0);
/* Init audio core.
* This must be done before we do request_irq otherwise we can get spurious
* interrupts that we do not handle properly and make a mess of things */
- if ((err = vortex_core_init(chip)) != 0) {
- printk(KERN_ERR "hw core init failed\n");
- goto core_out;
+ err = vortex_core_init(chip);
+ if (err) {
+ dev_err(card->dev, "hw core init failed\n");
+ return err;
}
- if ((err = request_irq(pci->irq, vortex_interrupt,
- IRQF_SHARED, CARD_NAME_SHORT,
- chip)) != 0) {
- printk(KERN_ERR "cannot grab irq\n");
- goto irq_out;
+ err = devm_request_irq(&pci->dev, pci->irq, vortex_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
+ if (err) {
+ dev_err(card->dev, "cannot grab irq\n");
+ return err;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_vortex_free;
pci_set_master(pci);
// End of PCI setup.
-
- // Register alsa root device.
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- goto alloc_out;
- }
-
- snd_card_set_dev(card, &pci->dev);
-
- *rchip = chip;
-
return 0;
-
- alloc_out:
- free_irq(chip->irq, chip);
- irq_out:
- vortex_core_shutdown(chip);
- core_out:
- iounmap(chip->mmio);
- ioremap_out:
- pci_release_regions(chip->pci_dev);
- regions_out:
- pci_disable_device(chip->pci_dev);
- //FIXME: this not the right place to unregister the gameport
- vortex_gameport_unregister(chip);
- kfree(chip);
- return err;
}
// constructor -- see "Constructor" sub-section
-static int __devinit
-snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+static int
+__snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -250,64 +207,57 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
return -ENOENT;
}
// (2)
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
// (3)
- if ((err = snd_vortex_create(card, pci, &chip)) < 0) {
- snd_card_free(card);
+ err = snd_vortex_create(card, pci);
+ if (err < 0)
return err;
- }
snd_vortex_workaround(pci, pcifix[dev]);
// Card details needed in snd_vortex_midi
- strcpy(card->driver, CARD_NAME_SHORT);
+ strscpy(card->driver, CARD_NAME_SHORT);
sprintf(card->shortname, "Aureal Vortex %s", CARD_NAME_SHORT);
sprintf(card->longname, "%s at 0x%lx irq %i",
card->shortname, chip->io, chip->irq);
// (4) Alloc components.
+ err = snd_vortex_mixer(chip);
+ if (err < 0)
+ return err;
// ADB pcm.
- if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_ADB)) < 0) {
- snd_card_free(card);
+ err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_PCM);
+ if (err < 0)
return err;
- }
#ifndef CHIP_AU8820
// ADB SPDIF
- if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_SPDIF, 1)) < 0) {
- snd_card_free(card);
+ err = snd_vortex_new_pcm(chip, VORTEX_PCM_SPDIF, 1);
+ if (err < 0)
return err;
- }
// A3D
- if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_A3D, NR_A3D)) < 0) {
- snd_card_free(card);
+ err = snd_vortex_new_pcm(chip, VORTEX_PCM_A3D, NR_A3D);
+ if (err < 0)
return err;
- }
#endif
/*
// ADB I2S
if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_I2S, 1)) < 0) {
- snd_card_free(card);
return err;
}
*/
#ifndef CHIP_AU8810
// WT pcm.
- if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_WT, NR_WT)) < 0) {
- snd_card_free(card);
+ err = snd_vortex_new_pcm(chip, VORTEX_PCM_WT, NR_WT);
+ if (err < 0)
return err;
- }
#endif
- // snd_ac97_mixer and Vortex mixer.
- if ((err = snd_vortex_mixer(chip)) < 0) {
- snd_card_free(card);
- return err;
- }
- if ((err = snd_vortex_midi(chip)) < 0) {
- snd_card_free(card);
+ err = snd_vortex_midi(chip);
+ if (err < 0)
return err;
- }
vortex_gameport_register(chip);
@@ -315,12 +265,12 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_VORTEX_SYNTH,
sizeof(snd_vortex_synth_arg_t), &wave) < 0
|| wave == NULL) {
- snd_printk(KERN_ERR "Can't initialize Aureal wavetable synth\n");
+ dev_err(card->dev, "Can't initialize Aureal wavetable synth\n");
} else {
snd_vortex_synth_arg_t *arg;
arg = SNDRV_SEQ_DEVICE_ARGPTR(wave);
- strcpy(wave->name, "Aureal Synth");
+ strscpy(wave->name, "Aureal Synth");
arg->hwptr = vortex;
arg->index = 1;
arg->seq_ports = seq_ports[dev];
@@ -329,35 +279,28 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
#endif
// (5)
- if ((err = pci_read_config_word(pci, PCI_DEVICE_ID,
- &(chip->device))) < 0) {
- snd_card_free(card);
- return err;
- }
- if ((err = pci_read_config_word(pci, PCI_VENDOR_ID,
- &(chip->vendor))) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = pci_read_config_word(pci, PCI_DEVICE_ID, &chip->device);
+ if (err)
+ return pcibios_err_to_errno(err);
+ err = pci_read_config_word(pci, PCI_VENDOR_ID, &chip->vendor);
+ if (err)
+ return pcibios_err_to_errno(err);
chip->rev = pci->revision;
#ifdef CHIP_AU8830
if ((chip->rev) != 0xfe && (chip->rev) != 0xfa) {
- printk(KERN_ALERT
- "vortex: The revision (%x) of your card has not been seen before.\n",
+ dev_alert(card->dev,
+ "The revision (%x) of your card has not been seen before.\n",
chip->rev);
- printk(KERN_ALERT
- "vortex: Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n");
- snd_card_free(card);
- err = -ENODEV;
- return err;
+ dev_alert(card->dev,
+ "Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n");
+ return -ENODEV;
}
#endif
// (6)
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
// (7)
pci_set_drvdata(pci, card);
dev++;
@@ -366,32 +309,17 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
return 0;
}
-// destructor -- see "Destructor" sub-section
-static void __devexit snd_vortex_remove(struct pci_dev *pci)
+static int
+snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_vortex_probe(pci, pci_id));
}
// pci_driver definition
-static struct pci_driver driver = {
- .name = CARD_NAME_SHORT,
+static struct pci_driver vortex_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_vortex_ids,
.probe = snd_vortex_probe,
- .remove = __devexit_p(snd_vortex_remove),
};
-// initialization of the module
-static int __init alsa_card_vortex_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-// clean up the module
-static void __exit alsa_card_vortex_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_vortex_init)
-module_exit(alsa_card_vortex_exit)
+module_pci_driver(vortex_driver);
diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h
index cf46bba563cf..6cbb2bc4a048 100644
--- a/sound/pci/au88x0/au88x0.h
+++ b/sound/pci/au88x0/au88x0.h
@@ -1,33 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Library General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __SOUND_AU88X0_H
#define __SOUND_AU88X0_H
-#ifdef __KERNEL__
#include <linux/pci.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/rawmidi.h>
#include <sound/mpu401.h>
#include <sound/hwdep.h>
#include <sound/ac97_codec.h>
-
-#endif
+#include <sound/tlv.h>
#ifndef CHIP_AU8820
#include "au88x0_eq.h"
@@ -104,7 +90,16 @@
#define MIX_PLAYB(x) (vortex->mixplayb[x])
#define MIX_SPDIF(x) (vortex->mixspdif[x])
-#define NR_WTPB 0x20 /* WT channels per eahc bank. */
+#define NR_WTPB 0x20 /* WT channels per each bank. */
+#define NR_PCM 0x10
+
+struct pcm_vol {
+ struct snd_kcontrol *kctl;
+ int active;
+ int dma;
+ int mixin[4];
+ int vol[4];
+};
/* Structs */
typedef struct {
@@ -146,7 +141,7 @@ struct snd_vortex {
#ifndef CHIP_AU8810
stream_t dma_wt[NR_WT];
wt_voice_t wt_voice[NR_WT]; /* WT register cache. */
- char mixwt[(NR_WT / NR_WTPB) * 6]; /* WT mixin objects */
+ s8 mixwt[(NR_WT / NR_WTPB) * 6]; /* WT mixin objects */
#endif
/* Global resources */
@@ -167,6 +162,7 @@ struct snd_vortex {
/* Xtalk canceler */
int xt_mode; /* 1: speakers, 0:headphones. */
#endif
+ struct pcm_vol pcm_vol[NR_PCM];
int isquad; /* cache of extended ID codec flag. */
@@ -211,7 +207,7 @@ static void vortex_adbdma_startfifo(vortex_t * vortex, int adbdma);
//static void vortex_adbdma_stopfifo(vortex_t *vortex, int adbdma);
static void vortex_adbdma_pausefifo(vortex_t * vortex, int adbdma);
static void vortex_adbdma_resumefifo(vortex_t * vortex, int adbdma);
-static int inline vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma);
+static inline int vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma);
static void vortex_adbdma_resetup(vortex_t *vortex, int adbdma);
#ifndef CHIP_AU8810
@@ -219,7 +215,7 @@ static void vortex_wtdma_startfifo(vortex_t * vortex, int wtdma);
static void vortex_wtdma_stopfifo(vortex_t * vortex, int wtdma);
static void vortex_wtdma_pausefifo(vortex_t * vortex, int wtdma);
static void vortex_wtdma_resumefifo(vortex_t * vortex, int wtdma);
-static int inline vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma);
+static inline int vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma);
#endif
/* global stuff. */
@@ -233,14 +229,14 @@ static int vortex_core_init(vortex_t * card);
static int vortex_core_shutdown(vortex_t * card);
static void vortex_enable_int(vortex_t * card);
static irqreturn_t vortex_interrupt(int irq, void *dev_id);
-static int vortex_alsafmt_aspfmt(int alsafmt);
+static int vortex_alsafmt_aspfmt(snd_pcm_format_t alsafmt, vortex_t *v);
/* Connection stuff. */
static void vortex_connect_default(vortex_t * vortex, int en);
static int vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch,
- int dir, int type);
-static char vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out,
- int restype);
+ int dir, int type, int subdev);
+static int vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out,
+ int restype);
#ifndef CHIP_AU8810
static int vortex_wt_allocroute(vortex_t * vortex, int dma, int nr_ch);
static void vortex_wt_connect(vortex_t * vortex, int en);
@@ -268,7 +264,7 @@ static void vortex_mix_setvolumebyte(vortex_t * vortex, unsigned char mix,
static void vortex_Vort3D_enable(vortex_t * v);
static void vortex_Vort3D_disable(vortex_t * v);
static void vortex_Vort3D_connect(vortex_t * vortex, int en);
-static void vortex_Vort3D_InitializeSource(a3dsrc_t * a, int en);
+static void vortex_Vort3D_InitializeSource(a3dsrc_t *a, int en, vortex_t *v);
#endif
/* Driver stuff. */
diff --git a/sound/pci/au88x0/au88x0_a3d.c b/sound/pci/au88x0/au88x0_a3d.c
index f4aa8ff6f5f9..d5cafaa229f1 100644
--- a/sound/pci/au88x0/au88x0_a3d.c
+++ b/sound/pci/au88x0/au88x0_a3d.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/***************************************************************************
* au88x0_a3d.c
*
@@ -9,19 +10,6 @@
****************************************************************************/
/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Library General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "au88x0_a3d.h"
@@ -53,7 +41,7 @@ a3dsrc_GetTimeConsts(a3dsrc_t * a, short *HrtfTrack, short *ItdTrack,
}
#endif
-/* Atmospheric absorbtion. */
+/* Atmospheric absorption. */
static void
a3dsrc_SetAtmosTarget(a3dsrc_t * a, short aa, short b, short c, short d,
@@ -463,7 +451,7 @@ static void a3dsrc_ZeroSliceIO(a3dsrc_t * a)
static void a3dsrc_ZeroState(a3dsrc_t * a)
{
/*
- printk(KERN_DEBUG "vortex: ZeroState slice: %d, source %d\n",
+ pr_debug( "vortex: ZeroState slice: %d, source %d\n",
a->slice, a->source);
*/
a3dsrc_SetAtmosState(a, 0, 0, 0, 0);
@@ -484,12 +472,13 @@ static void a3dsrc_ZeroState(a3dsrc_t * a)
}
/* Reset entire A3D engine */
-static void a3dsrc_ZeroStateA3D(a3dsrc_t * a)
+static void a3dsrc_ZeroStateA3D(a3dsrc_t *a, vortex_t *v)
{
int i, var, var2;
if ((a->vortex) == NULL) {
- printk(KERN_ERR "vortex: ZeroStateA3D: ERROR: a->vortex is NULL\n");
+ dev_err(v->card->dev,
+ "ZeroStateA3D: ERROR: a->vortex is NULL\n");
return;
}
@@ -594,14 +583,14 @@ static int Vort3DRend_Initialize(vortex_t * v, unsigned short mode)
static int vortex_a3d_register_controls(vortex_t * vortex);
static void vortex_a3d_unregister_controls(vortex_t * vortex);
/* A3D base support init/shudown */
-static void __devinit vortex_Vort3D_enable(vortex_t * v)
+static void vortex_Vort3D_enable(vortex_t *v)
{
int i;
Vort3DRend_Initialize(v, XT_HEADPHONE);
for (i = 0; i < NR_A3D; i++) {
vortex_A3dSourceHw_Initialize(v, i % 4, i >> 2);
- a3dsrc_ZeroStateA3D(&(v->a3d[0]));
+ a3dsrc_ZeroStateA3D(&v->a3d[0], v);
}
/* Register ALSA controls */
vortex_a3d_register_controls(v);
@@ -628,15 +617,15 @@ static void vortex_Vort3D_connect(vortex_t * v, int en)
v->mixxtlk[0] =
vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN);
if (v->mixxtlk[0] < 0) {
- printk
- ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n");
+ dev_warn(v->card->dev,
+ "vortex_Vort3D: ERROR: not enough free mixer resources.\n");
return;
}
v->mixxtlk[1] =
vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN);
if (v->mixxtlk[1] < 0) {
- printk
- ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n");
+ dev_warn(v->card->dev,
+ "vortex_Vort3D: ERROR: not enough free mixer resources.\n");
return;
}
#endif
@@ -676,11 +665,11 @@ static void vortex_Vort3D_connect(vortex_t * v, int en)
}
/* Initialize one single A3D source. */
-static void vortex_Vort3D_InitializeSource(a3dsrc_t * a, int en)
+static void vortex_Vort3D_InitializeSource(a3dsrc_t *a, int en, vortex_t *v)
{
if (a->vortex == NULL) {
- printk
- ("vortex: Vort3D_InitializeSource: A3D source not initialized\n");
+ dev_warn(v->card->dev,
+ "Vort3D_InitializeSource: A3D source not initialized\n");
return;
}
if (en) {
@@ -765,7 +754,7 @@ snd_vortex_a3d_filter_info(struct snd_kcontrol *kcontrol,
static int
snd_vortex_a3d_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- //a3dsrc_t *a = kcontrol->private_data;
+ //a3dsrc_t *a = snd_kcontrol_chip(kcontrol);
/* No read yet. Would this be really useable/needed ? */
return 0;
@@ -775,8 +764,8 @@ static int
snd_vortex_a3d_hrtf_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- a3dsrc_t *a = kcontrol->private_data;
- int changed = 1, i;
+ a3dsrc_t *a = snd_kcontrol_chip(kcontrol);
+ int i;
int coord[6];
for (i = 0; i < 6; i++)
coord[i] = ucontrol->value.integer.value[i];
@@ -785,16 +774,16 @@ snd_vortex_a3d_hrtf_put(struct snd_kcontrol *kcontrol,
vortex_a3d_coord2hrtf(a->hrtf[1], coord);
a3dsrc_SetHrtfTarget(a, a->hrtf[0], a->hrtf[1]);
a3dsrc_SetHrtfCurrent(a, a->hrtf[0], a->hrtf[1]);
- return changed;
+ return 1;
}
static int
snd_vortex_a3d_itd_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- a3dsrc_t *a = kcontrol->private_data;
+ a3dsrc_t *a = snd_kcontrol_chip(kcontrol);
int coord[6];
- int i, changed = 1;
+ int i;
for (i = 0; i < 6; i++)
coord[i] = ucontrol->value.integer.value[i];
/* Translate orientation coordinates to a3d params. */
@@ -804,15 +793,14 @@ snd_vortex_a3d_itd_put(struct snd_kcontrol *kcontrol,
a3dsrc_SetItdTarget(a, a->itd[0], a->itd[1]);
a3dsrc_SetItdCurrent(a, a->itd[0], a->itd[1]);
a3dsrc_SetItdDline(a, a->dline);
- return changed;
+ return 1;
}
static int
snd_vortex_a3d_ild_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- a3dsrc_t *a = kcontrol->private_data;
- int changed = 1;
+ a3dsrc_t *a = snd_kcontrol_chip(kcontrol);
int l, r;
/* There may be some scale tranlation needed here. */
l = ucontrol->value.integer.value[0];
@@ -821,31 +809,31 @@ snd_vortex_a3d_ild_put(struct snd_kcontrol *kcontrol,
/* Left Right panning. */
a3dsrc_SetGainTarget(a, l, r);
a3dsrc_SetGainCurrent(a, l, r);
- return changed;
+ return 1;
}
static int
snd_vortex_a3d_filter_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- a3dsrc_t *a = kcontrol->private_data;
- int i, changed = 1;
+ a3dsrc_t *a = snd_kcontrol_chip(kcontrol);
+ int i;
int params[6];
for (i = 0; i < 6; i++)
params[i] = ucontrol->value.integer.value[i];
/* Translate generic filter params to a3d filter params. */
vortex_a3d_translate_filter(a->filter, params);
- /* Atmospheric absorbtion and filtering. */
+ /* Atmospheric absorption and filtering. */
a3dsrc_SetAtmosTarget(a, a->filter[0],
a->filter[1], a->filter[2],
a->filter[3], a->filter[4]);
a3dsrc_SetAtmosCurrent(a, a->filter[0],
a->filter[1], a->filter[2],
a->filter[3], a->filter[4]);
- return changed;
+ return 1;
}
-static struct snd_kcontrol_new vortex_a3d_kcontrol __devinitdata = {
+static const struct snd_kcontrol_new vortex_a3d_kcontrol = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Playback PCM advanced processing",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -855,52 +843,56 @@ static struct snd_kcontrol_new vortex_a3d_kcontrol __devinitdata = {
};
/* Control (un)registration. */
-static int __devinit vortex_a3d_register_controls(vortex_t * vortex)
+static int vortex_a3d_register_controls(vortex_t *vortex)
{
struct snd_kcontrol *kcontrol;
int err, i;
/* HRTF controls. */
for (i = 0; i < NR_A3D; i++) {
- if ((kcontrol =
- snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
+ kcontrol = snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i]);
+ if (!kcontrol)
return -ENOMEM;
kcontrol->id.numid = CTRLID_HRTF;
kcontrol->info = snd_vortex_a3d_hrtf_info;
kcontrol->put = snd_vortex_a3d_hrtf_put;
- if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
return err;
}
/* ITD controls. */
for (i = 0; i < NR_A3D; i++) {
- if ((kcontrol =
- snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
+ kcontrol = snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i]);
+ if (!kcontrol)
return -ENOMEM;
kcontrol->id.numid = CTRLID_ITD;
kcontrol->info = snd_vortex_a3d_itd_info;
kcontrol->put = snd_vortex_a3d_itd_put;
- if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
return err;
}
/* ILD (gains) controls. */
for (i = 0; i < NR_A3D; i++) {
- if ((kcontrol =
- snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
+ kcontrol = snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i]);
+ if (!kcontrol)
return -ENOMEM;
kcontrol->id.numid = CTRLID_GAINS;
kcontrol->info = snd_vortex_a3d_ild_info;
kcontrol->put = snd_vortex_a3d_ild_put;
- if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
return err;
}
/* Filter controls. */
for (i = 0; i < NR_A3D; i++) {
- if ((kcontrol =
- snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL)
+ kcontrol = snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i]);
+ if (!kcontrol)
return -ENOMEM;
kcontrol->id.numid = CTRLID_FILTER;
kcontrol->info = snd_vortex_a3d_filter_info;
kcontrol->put = snd_vortex_a3d_filter_put;
- if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
return err;
}
return 0;
diff --git a/sound/pci/au88x0/au88x0_a3d.h b/sound/pci/au88x0/au88x0_a3d.h
index 0584c65bcab0..4078173da4a5 100644
--- a/sound/pci/au88x0/au88x0_a3d.h
+++ b/sound/pci/au88x0/au88x0_a3d.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/***************************************************************************
* au88x0_a3d.h
*
@@ -7,19 +8,6 @@
****************************************************************************/
/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Library General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _AU88X0_A3D_H
diff --git a/sound/pci/au88x0/au88x0_a3ddata.c b/sound/pci/au88x0/au88x0_a3ddata.c
index 6fab4bba5a05..a5da3b3a546a 100644
--- a/sound/pci/au88x0/au88x0_a3ddata.c
+++ b/sound/pci/au88x0/au88x0_a3ddata.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/***************************************************************************
* au88x0_a3ddata.c
*
@@ -7,19 +8,6 @@
****************************************************************************/
/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Library General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* Constant initializer values. */
@@ -33,7 +21,7 @@ static const a3d_Hrtf_t A3dHrirZeros = {
0, 0, 0
};
-static const a3d_Hrtf_t A3dHrirImpulse = {
+static __maybe_unused const a3d_Hrtf_t A3dHrirImpulse = {
0x7fff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,
0, 0, 0, 0,
@@ -42,7 +30,7 @@ static const a3d_Hrtf_t A3dHrirImpulse = {
0, 0, 0
};
-static const a3d_Hrtf_t A3dHrirOnes = {
+static __maybe_unused const a3d_Hrtf_t A3dHrirOnes = {
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
0x7fff,
0x7fff,
@@ -59,7 +47,7 @@ static const a3d_Hrtf_t A3dHrirOnes = {
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff
};
-static const a3d_Hrtf_t A3dHrirSatTest = {
+static __maybe_unused const a3d_Hrtf_t A3dHrirSatTest = {
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
0x7fff,
0x7fff,
@@ -71,7 +59,7 @@ static const a3d_Hrtf_t A3dHrirSatTest = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
-static const a3d_Hrtf_t A3dHrirDImpulse = {
+static __maybe_unused const a3d_Hrtf_t A3dHrirDImpulse = {
0, 0x7fff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,
0, 0, 0, 0,
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
index 23f49f356e0f..e5d867637336 100644
--- a/sound/pci/au88x0/au88x0_core.c
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -1,17 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Library General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
@@ -285,8 +273,8 @@ vortex_mixer_addWTD(vortex_t * vortex, unsigned char mix, unsigned char ch)
temp = hwread(vortex->mmio, prev);
//printk(KERN_INFO "vortex: mixAddWTD: while addr=%x, val=%x\n", prev, temp);
if ((++lifeboat) > 0xf) {
- printk(KERN_ERR
- "vortex_mixer_addWTD: lifeboat overflow\n");
+ dev_err(vortex->card->dev,
+ "vortex_mixer_addWTD: lifeboat overflow\n");
return 0;
}
}
@@ -303,7 +291,7 @@ vortex_mixer_delWTD(vortex_t * vortex, unsigned char mix, unsigned char ch)
eax = hwread(vortex->mmio, VORTEX_MIXER_SR);
if (((1 << ch) & eax) == 0) {
- printk(KERN_ERR "mix ALARM %x\n", eax);
+ dev_err(vortex->card->dev, "mix ALARM %x\n", eax);
return 0;
}
ebp = VORTEX_MIXER_CHNBASE + (ch << 2);
@@ -324,8 +312,8 @@ vortex_mixer_delWTD(vortex_t * vortex, unsigned char mix, unsigned char ch)
//printk(KERN_INFO "vortex: mixdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src);
while ((edx & 0xf) != mix) {
if ((esi) > 0xf) {
- printk(KERN_ERR
- "vortex: mixdelWTD: error lifeboat overflow\n");
+ dev_err(vortex->card->dev,
+ "mixdelWTD: error lifeboat overflow\n");
return 0;
}
esp14 = ebx;
@@ -492,7 +480,7 @@ vortex_src_persist_convratio(vortex_t * vortex, unsigned char src, int ratio)
hwwrite(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2), ratio);
temp = hwread(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2));
if ((++lifeboat) > 0x9) {
- printk(KERN_ERR "Vortex: Src cvr fail\n");
+ dev_err(vortex->card->dev, "Src cvr fail\n");
break;
}
}
@@ -545,7 +533,7 @@ vortex_src_checkratio(vortex_t * vortex, unsigned char src,
hwwrite(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2), desired_ratio);
if ((lifeboat++) > 15) {
- printk(KERN_ERR "Vortex: could not set src-%d from %d to %d\n",
+ pr_err( "Vortex: could not set src-%d from %d to %d\n",
src, hw_ratio, desired_ratio);
break;
}
@@ -684,8 +672,8 @@ vortex_src_addWTD(vortex_t * vortex, unsigned char src, unsigned char ch)
temp = hwread(vortex->mmio, prev);
//printk(KERN_INFO "vortex: srcAddWTD: while addr=%x, val=%x\n", prev, temp);
if ((++lifeboat) > 0xf) {
- printk(KERN_ERR
- "vortex_src_addWTD: lifeboat overflow\n");
+ dev_err(vortex->card->dev,
+ "vortex_src_addWTD: lifeboat overflow\n");
return 0;
}
}
@@ -703,7 +691,7 @@ vortex_src_delWTD(vortex_t * vortex, unsigned char src, unsigned char ch)
eax = hwread(vortex->mmio, VORTEX_SRCBLOCK_SR);
if (((1 << ch) & eax) == 0) {
- printk(KERN_ERR "src alarm\n");
+ dev_err(vortex->card->dev, "src alarm\n");
return 0;
}
ebp = VORTEX_SRC_CHNBASE + (ch << 2);
@@ -724,8 +712,8 @@ vortex_src_delWTD(vortex_t * vortex, unsigned char src, unsigned char ch)
//printk(KERN_INFO "vortex: srcdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src);
while ((edx & 0xf) != src) {
if ((esi) > 0xf) {
- printk
- ("vortex: srcdelWTD: error, lifeboat overflow\n");
+ dev_warn(vortex->card->dev,
+ "srcdelWTD: error, lifeboat overflow\n");
return 0;
}
esp14 = ebx;
@@ -805,7 +793,7 @@ static void vortex_fifo_setadbvalid(vortex_t * vortex, int fifo, int en)
}
static void
-vortex_fifo_setadbctrl(vortex_t * vortex, int fifo, int b, int priority,
+vortex_fifo_setadbctrl(vortex_t * vortex, int fifo, int stereo, int priority,
int empty, int valid, int f)
{
int temp, lifeboat = 0;
@@ -819,8 +807,8 @@ vortex_fifo_setadbctrl(vortex_t * vortex, int fifo, int b, int priority,
do {
temp = hwread(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2));
if (lifeboat++ > 0xbb8) {
- printk(KERN_ERR
- "Vortex: vortex_fifo_setadbctrl fail\n");
+ dev_err(vortex->card->dev,
+ "vortex_fifo_setadbctrl fail\n");
break;
}
}
@@ -837,7 +825,7 @@ vortex_fifo_setadbctrl(vortex_t * vortex, int fifo, int b, int priority,
#else
temp = (this_4 & 0x3f) << 0xc;
#endif
- temp = (temp & 0xfffffffd) | ((b & 1) << 1);
+ temp = (temp & 0xfffffffd) | ((stereo & 1) << 1);
temp = (temp & 0xfffffff3) | ((priority & 3) << 2);
temp = (temp & 0xffffffef) | ((valid & 1) << 4);
temp |= FIFO_U1;
@@ -915,7 +903,8 @@ vortex_fifo_setwtctrl(vortex_t * vortex, int fifo, int ctrl, int priority,
do {
temp = hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2));
if (lifeboat++ > 0xbb8) {
- printk(KERN_ERR "Vortex: vortex_fifo_setwtctrl fail\n");
+ dev_err(vortex->card->dev,
+ "vortex_fifo_setwtctrl fail\n");
break;
}
}
@@ -970,7 +959,7 @@ vortex_fifo_setwtctrl(vortex_t * vortex, int fifo, int ctrl, int priority,
do {
temp = hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2));
if (lifeboat++ > 0xbb8) {
- printk(KERN_ERR "Vortex: vortex_fifo_setwtctrl fail (hanging)\n");
+ pr_err( "Vortex: vortex_fifo_setwtctrl fail (hanging)\n");
break;
}
} while ((temp & FIFO_RDONLY)&&(temp & FIFO_VALID)&&(temp != 0xFFFFFFFF));
@@ -1042,7 +1031,7 @@ static void vortex_fifo_init(vortex_t * vortex)
for (x = NR_ADB - 1; x >= 0; x--) {
hwwrite(vortex->mmio, addr, (FIFO_U0 | FIFO_U1));
if (hwread(vortex->mmio, addr) != (FIFO_U0 | FIFO_U1))
- printk(KERN_ERR "bad adb fifo reset!");
+ dev_err(vortex->card->dev, "bad adb fifo reset!\n");
vortex_fifo_clearadbdata(vortex, x, FIFO_SIZE);
addr -= 4;
}
@@ -1053,9 +1042,9 @@ static void vortex_fifo_init(vortex_t * vortex)
for (x = NR_WT - 1; x >= 0; x--) {
hwwrite(vortex->mmio, addr, FIFO_U0);
if (hwread(vortex->mmio, addr) != FIFO_U0)
- printk(KERN_ERR
- "bad wt fifo reset (0x%08x, 0x%08x)!\n",
- addr, hwread(vortex->mmio, addr));
+ dev_err(vortex->card->dev,
+ "bad wt fifo reset (0x%08x, 0x%08x)!\n",
+ addr, hwread(vortex->mmio, addr));
vortex_fifo_clearwtdata(vortex, x, FIFO_SIZE);
addr -= 4;
}
@@ -1114,6 +1103,7 @@ vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
hwwrite(vortex->mmio,
VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0xc,
snd_pcm_sgbuf_get_addr(dma->substream, psize * 3));
+ fallthrough;
/* 3 pages */
case 3:
dma->cfg0 |= 0x12000000;
@@ -1121,12 +1111,14 @@ vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
hwwrite(vortex->mmio,
VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0x8,
snd_pcm_sgbuf_get_addr(dma->substream, psize * 2));
+ fallthrough;
/* 2 pages */
case 2:
dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | (psize - 1);
hwwrite(vortex->mmio,
VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0x4,
snd_pcm_sgbuf_get_addr(dma->substream, psize));
+ fallthrough;
/* 1 page */
case 1:
dma->cfg0 |= 0x80000000 | 0x40000000 | ((psize - 1) << 0xc);
@@ -1136,7 +1128,7 @@ vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
break;
}
/*
- printk(KERN_DEBUG "vortex: cfg0 = 0x%x\nvortex: cfg1=0x%x\n",
+ pr_debug( "vortex: cfg0 = 0x%x\nvortex: cfg1=0x%x\n",
dma->cfg0, dma->cfg1);
*/
hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFCFG0 + (adbdma << 3), dma->cfg0);
@@ -1148,11 +1140,11 @@ vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,
static void
vortex_adbdma_setmode(vortex_t * vortex, int adbdma, int ie, int dir,
- int fmt, int d, u32 offset)
+ int fmt, int stereo, u32 offset)
{
stream_t *dma = &vortex->dma_adb[adbdma];
- dma->dma_unknown = d;
+ dma->dma_unknown = stereo;
dma->dma_ctrl =
((offset & OFFSET_MASK) | (dma->dma_ctrl & ~OFFSET_MASK));
/* Enable PCMOUT interrupts. */
@@ -1203,7 +1195,7 @@ static int vortex_adbdma_bufshift(vortex_t * vortex, int adbdma)
VORTEX_ADBDMA_BUFBASE + (((adbdma << 2) + pp) << 2),
snd_pcm_sgbuf_get_addr(dma->substream,
dma->period_bytes * p));
- /* Force write thru cache. */
+ /* Force write through cache. */
hwread(vortex->mmio, VORTEX_ADBDMA_BUFBASE +
(((adbdma << 2) + pp) << 2));
}
@@ -1213,8 +1205,9 @@ static int vortex_adbdma_bufshift(vortex_t * vortex, int adbdma)
if (dma->period_virt >= dma->nr_periods)
dma->period_virt -= dma->nr_periods;
if (delta != 1)
- printk(KERN_INFO "vortex: %d virt=%d, real=%d, delta=%d\n",
- adbdma, dma->period_virt, dma->period_real, delta);
+ dev_info(vortex->card->dev,
+ "%d virt=%d, real=%d, delta=%d\n",
+ adbdma, dma->period_virt, dma->period_real, delta);
return delta;
}
@@ -1244,19 +1237,27 @@ static void vortex_adbdma_resetup(vortex_t *vortex, int adbdma) {
VORTEX_ADBDMA_BUFBASE + (((adbdma << 2) + pp) << 2),
snd_pcm_sgbuf_get_addr(dma->substream,
dma->period_bytes * p));
- /* Force write thru cache. */
+ /* Force write through cache. */
hwread(vortex->mmio, VORTEX_ADBDMA_BUFBASE + (((adbdma << 2)+pp) << 2));
}
}
-static int inline vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma)
+static inline int vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma)
{
stream_t *dma = &vortex->dma_adb[adbdma];
- int temp;
+ int temp, page, delta;
temp = hwread(vortex->mmio, VORTEX_ADBDMA_STAT + (adbdma << 2));
- temp = (dma->period_virt * dma->period_bytes) + (temp & (dma->period_bytes - 1));
- return temp;
+ page = (temp & ADB_SUBBUF_MASK) >> ADB_SUBBUF_SHIFT;
+ if (dma->nr_periods >= 4)
+ delta = (page - dma->period_real) & 3;
+ else {
+ delta = (page - dma->period_real);
+ if (delta < 0)
+ delta += dma->nr_periods;
+ }
+ return (dma->period_virt + delta) * dma->period_bytes
+ + (temp & (dma->period_bytes - 1));
}
static void vortex_adbdma_startfifo(vortex_t * vortex, int adbdma)
@@ -1328,7 +1329,6 @@ static void vortex_adbdma_pausefifo(vortex_t * vortex, int adbdma)
dma->fifo_status = FIFO_PAUSE;
}
-#if 0 // Using pause instead
static void vortex_adbdma_stopfifo(vortex_t * vortex, int adbdma)
{
stream_t *dma = &vortex->dma_adb[adbdma];
@@ -1343,7 +1343,6 @@ static void vortex_adbdma_stopfifo(vortex_t * vortex, int adbdma)
dma->fifo_enabled = 0;
}
-#endif
/* WTDMA */
#ifndef CHIP_AU8810
@@ -1382,17 +1381,20 @@ vortex_wtdma_setbuffers(vortex_t * vortex, int wtdma,
dma->cfg1 |= 0x88000000 | 0x44000000 | 0x30000000 | (psize-1);
hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0xc,
snd_pcm_sgbuf_get_addr(dma->substream, psize * 3));
+ fallthrough;
/* 3 pages */
case 3:
dma->cfg0 |= 0x12000000;
dma->cfg1 |= 0x80000000 | 0x40000000 | ((psize-1) << 0xc);
hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0x8,
snd_pcm_sgbuf_get_addr(dma->substream, psize * 2));
+ fallthrough;
/* 2 pages */
case 2:
dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | (psize-1);
hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0x4,
snd_pcm_sgbuf_get_addr(dma->substream, psize));
+ fallthrough;
/* 1 page */
case 1:
dma->cfg0 |= 0x80000000 | 0x40000000 | ((psize-1) << 0xc);
@@ -1436,9 +1438,8 @@ static int vortex_wtdma_bufshift(vortex_t * vortex, int wtdma)
int page, p, pp, delta, i;
page =
- (hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2)) &
- WT_SUBBUF_MASK)
- >> WT_SUBBUF_SHIFT;
+ (hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2))
+ >> WT_SUBBUF_SHIFT) & WT_SUBBUF_MASK;
if (dma->nr_periods >= 4)
delta = (page - dma->period_real) & 3;
else {
@@ -1465,7 +1466,7 @@ static int vortex_wtdma_bufshift(vortex_t * vortex, int wtdma)
(((wtdma << 2) + pp) << 2),
snd_pcm_sgbuf_get_addr(dma->substream,
dma->period_bytes * p));
- /* Force write thru cache. */
+ /* Force write through cache. */
hwread(vortex->mmio, VORTEX_WTDMA_BUFBASE +
(((wtdma << 2) + pp) << 2));
}
@@ -1476,8 +1477,8 @@ static int vortex_wtdma_bufshift(vortex_t * vortex, int wtdma)
dma->period_real = page;
if (delta != 1)
- printk(KERN_WARNING "vortex: wt virt = %d, delta = %d\n",
- dma->period_virt, delta);
+ dev_warn(vortex->card->dev, "wt virt = %d, delta = %d\n",
+ dma->period_virt, delta);
return delta;
}
@@ -1498,7 +1499,7 @@ static int vortex_wtdma_getcursubuffer(vortex_t * vortex, int wtdma)
POS_SHIFT) & POS_MASK);
}
#endif
-static int inline vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma)
+static inline int vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma)
{
stream_t *dma = &vortex->dma_wt[wtdma];
int temp;
@@ -1661,9 +1662,9 @@ vortex_adb_addroutes(vortex_t * vortex, unsigned char channel,
hwread(vortex->mmio,
VORTEX_ADB_RTBASE + (temp << 2)) & ADB_MASK;
if ((lifeboat++) > ADB_MASK) {
- printk(KERN_ERR
- "vortex_adb_addroutes: unending route! 0x%x\n",
- *route);
+ dev_err(vortex->card->dev,
+ "vortex_adb_addroutes: unending route! 0x%x\n",
+ *route);
return;
}
}
@@ -1697,9 +1698,9 @@ vortex_adb_delroutes(vortex_t * vortex, unsigned char channel,
hwread(vortex->mmio,
VORTEX_ADB_RTBASE + (prev << 2)) & ADB_MASK;
if (((lifeboat++) > ADB_MASK) || (temp == ADB_MASK)) {
- printk(KERN_ERR
- "vortex_adb_delroutes: route not found! 0x%x\n",
- route0);
+ dev_err(vortex->card->dev,
+ "vortex_adb_delroutes: route not found! 0x%x\n",
+ route0);
return;
}
}
@@ -1853,7 +1854,7 @@ vortex_connection_mixin_mix(vortex_t * vortex, int en, unsigned char mixin,
vortex_mix_disableinput(vortex, mix, mixin, a);
}
-// Connect absolut address to mixin.
+// Connect absolute address to mixin.
static void
vortex_connection_adb_mixin(vortex_t * vortex, int en,
unsigned char channel, unsigned char source,
@@ -1879,7 +1880,7 @@ vortex_connection_src_src_adbdma(vortex_t * vortex, int en,
ADB_DMA(adbdma));
}
-// mix to absolut address.
+// mix to absolute address.
static void
vortex_connection_mix_adb(vortex_t * vortex, int en, unsigned char ch,
unsigned char mix, unsigned char dest)
@@ -1961,7 +1962,7 @@ vortex_connect_codecplay(vortex_t * vortex, int en, unsigned char mixers[])
ADB_CODECOUT(0 + 4));
vortex_connection_mix_adb(vortex, en, 0x11, mixers[3],
ADB_CODECOUT(1 + 4));
- /* printk(KERN_DEBUG "SDAC detected "); */
+ /* pr_debug( "SDAC detected "); */
}
#else
// Use plain direct output to codec.
@@ -1988,7 +1989,7 @@ vortex_connect_codecrec(vortex_t * vortex, int en, unsigned char mixin0,
// Higher level ADB audio path (de)allocator.
/* Resource manager */
-static int resnum[VORTEX_RESOURCE_LAST] =
+static const int resnum[VORTEX_RESOURCE_LAST] =
{ NR_ADB, NR_SRC, NR_MIXIN, NR_MIXOUT, NR_A3D };
/*
Checkout/Checkin resource of given type.
@@ -1997,7 +1998,7 @@ static int resnum[VORTEX_RESOURCE_LAST] =
out: Mean checkout if != 0. Else mean Checkin resource.
restype: Indicates type of resource to be checked in or out.
*/
-static char
+static int
vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
{
int i, qty = resnum[restype], resinuse = 0;
@@ -2016,7 +2017,7 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
else
vortex->dma_adb[i].resources[restype] |= (1 << i);
/*
- printk(KERN_DEBUG
+ pr_debug(
"vortex: ResManager: type %d out %d\n",
restype, i);
*/
@@ -2031,7 +2032,7 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
if (resmap[restype] & (1 << i)) {
resmap[restype] &= ~(1 << i);
/*
- printk(KERN_DEBUG
+ pr_debug(
"vortex: ResManager: type %d in %d\n",
restype, i);
*/
@@ -2039,13 +2040,13 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
}
}
}
- printk(KERN_ERR "vortex: FATAL: ResManager: resource type %d exhausted.\n", restype);
+ dev_err(vortex->card->dev,
+ "FATAL: ResManager: resource type %d exhausted.\n",
+ restype);
return -ENOMEM;
}
/* Default Connections */
-static int
-vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type);
static void vortex_connect_default(vortex_t * vortex, int en)
{
@@ -2105,15 +2106,13 @@ static void vortex_connect_default(vortex_t * vortex, int en)
Return: Return allocated DMA or same DMA passed as "dma" when dma >= 0.
*/
static int
-vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
+vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
+ int type, int subdev)
{
stream_t *stream;
int i, en;
+ struct pcm_vol *p;
- if ((nr_ch == 3)
- || ((dir == SNDRV_PCM_STREAM_CAPTURE) && (nr_ch > 2)))
- return -EBUSY;
-
if (dma >= 0) {
en = 0;
vortex_adb_checkinout(vortex,
@@ -2121,9 +2120,9 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
VORTEX_RESOURCE_DMA);
} else {
en = 1;
- if ((dma =
- vortex_adb_checkinout(vortex, NULL, en,
- VORTEX_RESOURCE_DMA)) < 0)
+ dma = vortex_adb_checkinout(vortex, NULL, en,
+ VORTEX_RESOURCE_DMA);
+ if (dma < 0)
return -EBUSY;
}
@@ -2141,22 +2140,23 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
/* Get SRC and MIXER hardware resources. */
if (stream->type != VORTEX_PCM_SPDIF) {
for (i = 0; i < nr_ch; i++) {
- if ((src[i] = vortex_adb_checkinout(vortex,
- stream->resources, en,
- VORTEX_RESOURCE_SRC)) < 0) {
+ src[i] = vortex_adb_checkinout(vortex,
+ stream->resources, en,
+ VORTEX_RESOURCE_SRC);
+ if (src[i] < 0) {
memset(stream->resources, 0,
- sizeof(unsigned char) *
- VORTEX_RESOURCE_LAST);
+ sizeof(stream->resources));
return -EBUSY;
}
if (stream->type != VORTEX_PCM_A3D) {
- if ((mix[i] = vortex_adb_checkinout(vortex,
- stream->resources,
- en,
- VORTEX_RESOURCE_MIXIN)) < 0) {
+ mix[i] = vortex_adb_checkinout(vortex,
+ stream->resources,
+ en,
+ VORTEX_RESOURCE_MIXIN);
+ if (mix[i] < 0) {
memset(stream->resources,
0,
- sizeof(unsigned char) * VORTEX_RESOURCE_LAST);
+ sizeof(stream->resources));
return -EBUSY;
}
}
@@ -2164,18 +2164,19 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
}
#ifndef CHIP_AU8820
if (stream->type == VORTEX_PCM_A3D) {
- if ((a3d =
- vortex_adb_checkinout(vortex,
- stream->resources, en,
- VORTEX_RESOURCE_A3D)) < 0) {
+ a3d = vortex_adb_checkinout(vortex,
+ stream->resources, en,
+ VORTEX_RESOURCE_A3D);
+ if (a3d < 0) {
memset(stream->resources, 0,
- sizeof(unsigned char) *
- VORTEX_RESOURCE_LAST);
- printk(KERN_ERR "vortex: out of A3D sources. Sorry\n");
+ sizeof(stream->resources));
+ dev_err(vortex->card->dev,
+ "out of A3D sources. Sorry\n");
return -EBUSY;
}
/* (De)Initialize A3D hardware source. */
- vortex_Vort3D_InitializeSource(&(vortex->a3d[a3d]), en);
+ vortex_Vort3D_InitializeSource(&vortex->a3d[a3d], en,
+ vortex);
}
/* Make SPDIF out exclusive to "spdif" device when in use. */
if ((stream->type == VORTEX_PCM_SPDIF) && (en)) {
@@ -2244,6 +2245,14 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
MIX_DEFIGAIN);
#endif
}
+ if (stream->type == VORTEX_PCM_ADB && en) {
+ p = &vortex->pcm_vol[subdev];
+ p->dma = dma;
+ for (i = 0; i < nr_ch; i++)
+ p->mixin[i] = mix[i];
+ for (i = 0; i < ch_top; i++)
+ p->vol[i] = 0;
+ }
}
#ifndef CHIP_AU8820
else {
@@ -2266,25 +2275,25 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
} else {
int src[2], mix[2];
+ if (nr_ch < 1)
+ return -EINVAL;
+
/* Get SRC and MIXER hardware resources. */
for (i = 0; i < nr_ch; i++) {
- if ((mix[i] =
- vortex_adb_checkinout(vortex,
- stream->resources, en,
- VORTEX_RESOURCE_MIXOUT))
- < 0) {
+ mix[i] = vortex_adb_checkinout(vortex,
+ stream->resources, en,
+ VORTEX_RESOURCE_MIXOUT);
+ if (mix[i] < 0) {
memset(stream->resources, 0,
- sizeof(unsigned char) *
- VORTEX_RESOURCE_LAST);
+ sizeof(stream->resources));
return -EBUSY;
}
- if ((src[i] =
- vortex_adb_checkinout(vortex,
- stream->resources, en,
- VORTEX_RESOURCE_SRC)) < 0) {
+ src[i] = vortex_adb_checkinout(vortex,
+ stream->resources, en,
+ VORTEX_RESOURCE_SRC);
+ if (src[i] < 0) {
memset(stream->resources, 0,
- sizeof(unsigned char) *
- VORTEX_RESOURCE_LAST);
+ sizeof(stream->resources));
return -EBUSY;
}
}
@@ -2411,7 +2420,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
hwread(vortex->mmio, VORTEX_IRQ_SOURCE);
// Is at least one IRQ flag set?
if (source == 0) {
- printk(KERN_ERR "vortex: missing irq source\n");
+ dev_err(vortex->card->dev, "missing irq source\n");
return IRQ_NONE;
}
@@ -2419,19 +2428,19 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
// Attend every interrupt source.
if (unlikely(source & IRQ_ERR_MASK)) {
if (source & IRQ_FATAL) {
- printk(KERN_ERR "vortex: IRQ fatal error\n");
+ dev_err(vortex->card->dev, "IRQ fatal error\n");
}
if (source & IRQ_PARITY) {
- printk(KERN_ERR "vortex: IRQ parity error\n");
+ dev_err(vortex->card->dev, "IRQ parity error\n");
}
if (source & IRQ_REG) {
- printk(KERN_ERR "vortex: IRQ reg error\n");
+ dev_err(vortex->card->dev, "IRQ reg error\n");
}
if (source & IRQ_FIFO) {
- printk(KERN_ERR "vortex: IRQ fifo error\n");
+ dev_err(vortex->card->dev, "IRQ fifo error\n");
}
if (source & IRQ_DMA) {
- printk(KERN_ERR "vortex: IRQ dma error\n");
+ dev_err(vortex->card->dev, "IRQ dma error\n");
}
handled = 1;
}
@@ -2451,7 +2460,12 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
#ifndef CHIP_AU8810
for (i = 0; i < NR_WT; i++) {
if (vortex->dma_wt[i].fifo_status == FIFO_START) {
- if (vortex_wtdma_bufshift(vortex, i)) ;
+ /* FIXME: we ignore the return value from
+ * vortex_wtdma_bufshift() below as the delta
+ * calculation seems not working for wavetable
+ * by some reason
+ */
+ vortex_wtdma_bufshift(vortex, i);
spin_unlock(&vortex->lock);
snd_pcm_period_elapsed(vortex->dma_wt[i].
substream);
@@ -2467,14 +2481,14 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id)
hwread(vortex->mmio, VORTEX_IRQ_STAT);
handled = 1;
}
- if (source & IRQ_MIDI) {
+ if ((source & IRQ_MIDI) && vortex->rmidi) {
snd_mpu401_uart_interrupt(vortex->irq,
vortex->rmidi->private_data);
handled = 1;
}
if (!handled) {
- printk(KERN_ERR "vortex: unknown irq source %x\n", source);
+ dev_err(vortex->card->dev, "unknown irq source %x\n", source);
}
return IRQ_RETVAL(handled);
}
@@ -2531,7 +2545,7 @@ vortex_codec_write(struct snd_ac97 * codec, unsigned short addr, unsigned short
while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) {
udelay(100);
if (lifeboat++ > POLL_COUNT) {
- printk(KERN_ERR "vortex: ac97 codec stuck busy\n");
+ dev_err(card->card->dev, "ac97 codec stuck busy\n");
return;
}
}
@@ -2557,7 +2571,7 @@ static unsigned short vortex_codec_read(struct snd_ac97 * codec, unsigned short
while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) {
udelay(100);
if (lifeboat++ > POLL_COUNT) {
- printk(KERN_ERR "vortex: ac97 codec stuck busy\n");
+ dev_err(card->card->dev, "ac97 codec stuck busy\n");
return 0xffff;
}
}
@@ -2571,7 +2585,8 @@ static unsigned short vortex_codec_read(struct snd_ac97 * codec, unsigned short
udelay(100);
data = hwread(card->mmio, VORTEX_CODEC_IO);
if (lifeboat++ > POLL_COUNT) {
- printk(KERN_ERR "vortex: ac97 address never arrived\n");
+ dev_err(card->card->dev,
+ "ac97 address never arrived\n");
return 0xffff;
}
} while ((data & VORTEX_CODEC_ADDMASK) !=
@@ -2608,7 +2623,7 @@ static void vortex_spdif_init(vortex_t * vortex, int spdif_sr, int spdif_mode)
else
edi = 0x1ffff;
} else {
- i = edi = 0x800;
+ edi = 0x800;
}
/* this_04 and this_08 are the CASp4Src's (samplerate converters) */
vortex_src_setupchannel(vortex, this_04, edi, 0, 1,
@@ -2665,10 +2680,10 @@ static void vortex_spdif_init(vortex_t * vortex, int spdif_sr, int spdif_mode)
/* Initialization */
-static int __devinit vortex_core_init(vortex_t * vortex)
+static int vortex_core_init(vortex_t *vortex)
{
- printk(KERN_INFO "Vortex: init.... ");
+ dev_info(vortex->card->dev, "init started\n");
/* Hardware Init. */
hwwrite(vortex->mmio, VORTEX_CTRL, 0xffffffff);
msleep(5);
@@ -2713,7 +2728,7 @@ static int __devinit vortex_core_init(vortex_t * vortex)
//vortex_enable_timer_int(vortex);
//vortex_disable_timer_int(vortex);
- printk(KERN_INFO "done.\n");
+ dev_info(vortex->card->dev, "init.... done.\n");
spin_lock_init(&vortex->lock);
return 0;
@@ -2722,7 +2737,7 @@ static int __devinit vortex_core_init(vortex_t * vortex)
static int vortex_core_shutdown(vortex_t * vortex)
{
- printk(KERN_INFO "Vortex: shutdown...");
+ dev_info(vortex->card->dev, "shutdown started\n");
#ifndef CHIP_AU8820
vortex_eq_free(vortex);
vortex_Vort3D_disable(vortex);
@@ -2744,13 +2759,13 @@ static int vortex_core_shutdown(vortex_t * vortex)
msleep(5);
hwwrite(vortex->mmio, VORTEX_IRQ_SOURCE, 0xffff);
- printk(KERN_INFO "done.\n");
+ dev_info(vortex->card->dev, "shutdown.... done.\n");
return 0;
}
/* Alsa support. */
-static int vortex_alsafmt_aspfmt(int alsafmt)
+static int vortex_alsafmt_aspfmt(snd_pcm_format_t alsafmt, vortex_t *v)
{
int fmt;
@@ -2778,7 +2793,8 @@ static int vortex_alsafmt_aspfmt(int alsafmt)
break;
default:
fmt = 0x8;
- printk(KERN_ERR "vortex: format unsupported %d\n", alsafmt);
+ dev_err(v->card->dev,
+ "format unsupported %d\n", alsafmt);
break;
}
return fmt;
diff --git a/sound/pci/au88x0/au88x0_eq.c b/sound/pci/au88x0/au88x0_eq.c
index 38602b85874d..81a63b5bb31c 100644
--- a/sound/pci/au88x0/au88x0_eq.c
+++ b/sound/pci/au88x0/au88x0_eq.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/***************************************************************************
* au88x0_eq.c
* Aureal Vortex Hardware EQ control/access.
@@ -15,19 +16,6 @@
****************************************************************************/
/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Library General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
@@ -63,7 +51,7 @@ static inline u16 sign_invert(u16 a)
return -a;
}
-static void vortex_EqHw_SetLeftCoefs(vortex_t * vortex, u16 coefs[])
+static void vortex_EqHw_SetLeftCoefs(vortex_t *vortex, const u16 coefs[])
{
eqhw_t *eqhw = &(vortex->eq.this04);
int i = 0, n /*esp2c */;
@@ -85,7 +73,7 @@ static void vortex_EqHw_SetLeftCoefs(vortex_t * vortex, u16 coefs[])
}
}
-static void vortex_EqHw_SetRightCoefs(vortex_t * vortex, u16 coefs[])
+static void vortex_EqHw_SetRightCoefs(vortex_t *vortex, const u16 coefs[])
{
eqhw_t *eqhw = &(vortex->eq.this04);
int i = 0, n /*esp2c */;
@@ -108,7 +96,7 @@ static void vortex_EqHw_SetRightCoefs(vortex_t * vortex, u16 coefs[])
}
-static void vortex_EqHw_SetLeftStates(vortex_t * vortex, u16 a[], u16 b[])
+static void vortex_EqHw_SetLeftStates(vortex_t *vortex, const u16 a[], const u16 b[])
{
eqhw_t *eqhw = &(vortex->eq.this04);
int i = 0, ebx;
@@ -125,7 +113,7 @@ static void vortex_EqHw_SetLeftStates(vortex_t * vortex, u16 a[], u16 b[])
}
}
-static void vortex_EqHw_SetRightStates(vortex_t * vortex, u16 a[], u16 b[])
+static void vortex_EqHw_SetRightStates(vortex_t *vortex, const u16 a[], const u16 b[])
{
eqhw_t *eqhw = &(vortex->eq.this04);
int i = 0, ebx;
@@ -218,7 +206,7 @@ vortex_EqHw_SetRightGainsSingleTarget(vortex_t * vortex, u16 index, u16 b)
hwwrite(vortex->mmio, 0x2b20c + (index * 0x30), b);
}
-static void vortex_EqHw_SetLeftGainsTarget(vortex_t * vortex, u16 a[])
+static void vortex_EqHw_SetLeftGainsTarget(vortex_t *vortex, const u16 a[])
{
eqhw_t *eqhw = &(vortex->eq.this04);
int ebx;
@@ -228,7 +216,7 @@ static void vortex_EqHw_SetLeftGainsTarget(vortex_t * vortex, u16 a[])
}
}
-static void vortex_EqHw_SetRightGainsTarget(vortex_t * vortex, u16 a[])
+static void vortex_EqHw_SetRightGainsTarget(vortex_t *vortex, const u16 a[])
{
eqhw_t *eqhw = &(vortex->eq.this04);
int ebx;
@@ -238,7 +226,7 @@ static void vortex_EqHw_SetRightGainsTarget(vortex_t * vortex, u16 a[])
}
}
-static void vortex_EqHw_SetLeftGainsCurrent(vortex_t * vortex, u16 a[])
+static void vortex_EqHw_SetLeftGainsCurrent(vortex_t *vortex, const u16 a[])
{
eqhw_t *eqhw = &(vortex->eq.this04);
int ebx;
@@ -248,7 +236,7 @@ static void vortex_EqHw_SetLeftGainsCurrent(vortex_t * vortex, u16 a[])
}
}
-static void vortex_EqHw_SetRightGainsCurrent(vortex_t * vortex, u16 a[])
+static void vortex_EqHw_SetRightGainsCurrent(vortex_t *vortex, const u16 a[])
{
eqhw_t *eqhw = &(vortex->eq.this04);
int ebx;
@@ -321,7 +309,7 @@ static void vortex_EqHw_GetRightGainsCurrent(vortex_t * vortex, u16 a[])
#endif
/* EQ band levels settings */
-static void vortex_EqHw_SetLevels(vortex_t * vortex, u16 peaks[])
+static void vortex_EqHw_SetLevels(vortex_t *vortex, const u16 peaks[])
{
eqhw_t *eqhw = &(vortex->eq.this04);
int i;
@@ -580,13 +568,13 @@ static int vortex_Eqlzr_SetAllBandsFromActiveCoeffSet(vortex_t * vortex)
eqlzr_t *eq = &(vortex->eq);
vortex_EqHw_SetLeftGainsTarget(vortex, eq->this130);
- vortex_EqHw_SetRightGainsTarget(vortex, &(eq->this130[eq->this10]));
+ vortex_EqHw_SetRightGainsTarget(vortex, eq->this130 + eq->this10);
return 0;
}
static int
-vortex_Eqlzr_SetAllBands(vortex_t * vortex, u16 gains[], s32 count)
+vortex_Eqlzr_SetAllBands(vortex_t *vortex, const u16 gains[], s32 count)
{
eqlzr_t *eq = &(vortex->eq);
int i;
@@ -757,7 +745,7 @@ snd_vortex_eqtoggle_put(struct snd_kcontrol *kcontrol,
return 1; /* Allways changes */
}
-static struct snd_kcontrol_new vortex_eqtoggle_kcontrol __devinitdata = {
+static const struct snd_kcontrol_new vortex_eqtoggle_kcontrol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "EQ Enable",
.index = 0,
@@ -815,7 +803,7 @@ snd_vortex_eq_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucon
return changed;
}
-static struct snd_kcontrol_new vortex_eq_kcontrol __devinitdata = {
+static const struct snd_kcontrol_new vortex_eq_kcontrol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = " .",
.index = 0,
@@ -845,7 +833,8 @@ snd_vortex_peaks_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u
vortex_Eqlzr_GetAllPeaks(vortex, peaks, &count);
if (count != 20) {
- printk(KERN_ERR "vortex: peak count error 20 != %d \n", count);
+ dev_err(vortex->card->dev,
+ "peak count error 20 != %d\n", count);
return -1;
}
for (i = 0; i < 20; i++)
@@ -854,7 +843,7 @@ snd_vortex_peaks_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u
return 0;
}
-static struct snd_kcontrol_new vortex_levels_kcontrol __devinitdata = {
+static const struct snd_kcontrol_new vortex_levels_kcontrol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "EQ Peaks",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
@@ -863,7 +852,7 @@ static struct snd_kcontrol_new vortex_levels_kcontrol __devinitdata = {
};
/* EQ band gain labels. */
-static char *EqBandLabels[10] __devinitdata = {
+static const char * const EqBandLabels[10] = {
"EQ0 31Hz\0",
"EQ1 63Hz\0",
"EQ2 125Hz\0",
@@ -877,35 +866,40 @@ static char *EqBandLabels[10] __devinitdata = {
};
/* ALSA driver entry points. Init and exit. */
-static int __devinit vortex_eq_init(vortex_t * vortex)
+static int vortex_eq_init(vortex_t *vortex)
{
struct snd_kcontrol *kcontrol;
int err, i;
vortex_Eqlzr_init(vortex);
- if ((kcontrol =
- snd_ctl_new1(&vortex_eqtoggle_kcontrol, vortex)) == NULL)
+ kcontrol = snd_ctl_new1(&vortex_eqtoggle_kcontrol, vortex);
+ if (!kcontrol)
return -ENOMEM;
kcontrol->private_value = 0;
- if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
return err;
/* EQ gain controls */
for (i = 0; i < 10; i++) {
- if ((kcontrol =
- snd_ctl_new1(&vortex_eq_kcontrol, vortex)) == NULL)
+ kcontrol = snd_ctl_new1(&vortex_eq_kcontrol, vortex);
+ if (!kcontrol)
return -ENOMEM;
- strcpy(kcontrol->id.name, EqBandLabels[i]);
+ snprintf(kcontrol->id.name, sizeof(kcontrol->id.name),
+ "%s Playback Volume", EqBandLabels[i]);
kcontrol->private_value = i;
- if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
return err;
//vortex->eqctrl[i] = kcontrol;
}
/* EQ band levels */
- if ((kcontrol = snd_ctl_new1(&vortex_levels_kcontrol, vortex)) == NULL)
+ kcontrol = snd_ctl_new1(&vortex_levels_kcontrol, vortex);
+ if (!kcontrol)
return -ENOMEM;
- if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0)
+ err = snd_ctl_add(vortex->card, kcontrol);
+ if (err < 0)
return err;
return 0;
diff --git a/sound/pci/au88x0/au88x0_eq.h b/sound/pci/au88x0/au88x0_eq.h
index 474cd0046294..797cdae1db98 100644
--- a/sound/pci/au88x0/au88x0_eq.h
+++ b/sound/pci/au88x0/au88x0_eq.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef AU88X0_EQ_H
#define AU88X0_EQ_H
diff --git a/sound/pci/au88x0/au88x0_eqdata.c b/sound/pci/au88x0/au88x0_eqdata.c
index ce8dca8ce1e2..a74f266f0bd0 100644
--- a/sound/pci/au88x0/au88x0_eqdata.c
+++ b/sound/pci/au88x0/au88x0_eqdata.c
@@ -1,6 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/* Data structs */
-static u16 asEqCoefsZeros[50] = {
+static const u16 asEqCoefsZeros[50] = {
0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
@@ -13,7 +14,7 @@ static u16 asEqCoefsZeros[50] = {
0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
};
-static u16 asEqCoefsPipes[64] = {
+static const u16 asEqCoefsPipes[64] = {
0x0000, 0x0000,
0x0000, 0x0666, 0x0000, 0x0000, 0x0666,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
@@ -32,7 +33,7 @@ static u16 asEqCoefsPipes[64] = {
};
/* More coef sets can be found in the win2k "inf" file. */
-static auxxEqCoeffSet_t asEqCoefsNormal = {
+static const auxxEqCoeffSet_t asEqCoefsNormal = {
.LeftCoefs = {
0x7e60, 0xc19e, 0x0001, 0x0002, 0x0001,
0x7fa0, 0xc05f, 0x004f, 0x0000, 0xffb1,
@@ -65,7 +66,7 @@ static auxxEqCoeffSet_t asEqCoefsNormal = {
0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96}
};
-static u16 eq_gains_normal[20] = {
+static const u16 eq_gains_normal[20] = {
0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96,
0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96,
0x3e96, 0x3e96, 0x3e96, 0x3e96, 0x3e96,
@@ -73,22 +74,22 @@ static u16 eq_gains_normal[20] = {
};
/* _rodatab60 */
-static u16 eq_gains_zero[10] = {
+static const u16 eq_gains_zero[10] = {
0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000
};
/* _rodatab7c: ProgramPipe */
-static u16 eq_gains_current[12] = {
+static const u16 eq_gains_current[12] = {
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
0x7fff,
0x7fff, 0x7fff, 0x7fff
};
/* _rodatab78 */
-static u16 eq_states_zero[2] = { 0x0000, 0x0000 };
+static const u16 eq_states_zero[2] = { 0x0000, 0x0000 };
-static u16 asEqOutStateZeros[48] = {
+static const u16 asEqOutStateZeros[48] = {
0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000,
@@ -104,7 +105,7 @@ static u16 asEqOutStateZeros[48] = {
};
/*_rodataba0:*/
-static u16 eq_levels[64] = {
+static const u16 eq_levels[64] = {
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c
index e291aa59742e..51c154e34026 100644
--- a/sound/pci/au88x0/au88x0_game.c
+++ b/sound/pci/au88x0/au88x0_game.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Manuel Jander.
*
@@ -5,20 +6,6 @@
* Vojtech Pavlik
* Raymond Ingles
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
* Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
@@ -34,8 +21,9 @@
#include <sound/core.h>
#include "au88x0.h"
#include <linux/gameport.h>
+#include <linux/export.h>
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define VORTEX_GAME_DWAIT 20 /* 20 ms */
@@ -91,15 +79,16 @@ static int vortex_game_open(struct gameport *gameport, int mode)
return 0;
}
-static int __devinit vortex_gameport_register(vortex_t * vortex)
+static int vortex_gameport_register(vortex_t *vortex)
{
struct gameport *gp;
vortex->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "vortex: cannot allocate memory for gameport\n");
+ dev_err(vortex->card->dev,
+ "cannot allocate memory for gameport\n");
return -ENOMEM;
- };
+ }
gameport_set_name(gp, "AU88x0 Gameport");
gameport_set_phys(gp, "pci%s/gameport0", pci_name(vortex->pci_dev));
diff --git a/sound/pci/au88x0/au88x0_mixer.c b/sound/pci/au88x0/au88x0_mixer.c
index 557c782ae4fc..00781a7fd28c 100644
--- a/sound/pci/au88x0/au88x0_mixer.c
+++ b/sound/pci/au88x0/au88x0_mixer.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Vortex Mixer support.
*
@@ -10,17 +11,27 @@
#include <sound/core.h>
#include "au88x0.h"
-static int __devinit snd_vortex_mixer(vortex_t * vortex)
+static int remove_ctl(struct snd_card *card, const char *name)
+{
+ struct snd_ctl_elem_id id;
+ memset(&id, 0, sizeof(id));
+ strscpy(id.name, name);
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ return snd_ctl_remove_id(card, &id);
+}
+
+static int snd_vortex_mixer(vortex_t *vortex)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = vortex_codec_write,
.read = vortex_codec_read,
};
- if ((err = snd_ac97_bus(vortex->card, 0, &ops, NULL, &pbus)) < 0)
+ err = snd_ac97_bus(vortex->card, 0, &ops, NULL, &pbus);
+ if (err < 0)
return err;
memset(&ac97, 0, sizeof(ac97));
// Initialize AC97 codec stuff.
@@ -28,5 +39,7 @@ static int __devinit snd_vortex_mixer(vortex_t * vortex)
ac97.scaps = AC97_SCAP_NO_SPDIF;
err = snd_ac97_mixer(pbus, &ac97, &vortex->codec);
vortex->isquad = ((vortex->codec == NULL) ? 0 : (vortex->codec->ext_id&0x80));
+ remove_ctl(vortex->card, "Master Mono Playback Volume");
+ remove_ctl(vortex->card, "Master Mono Playback Switch");
return err;
}
diff --git a/sound/pci/au88x0/au88x0_mpu401.c b/sound/pci/au88x0/au88x0_mpu401.c
index 0dc8d259d1ed..164f6b7039ab 100644
--- a/sound/pci/au88x0/au88x0_mpu401.c
+++ b/sound/pci/au88x0/au88x0_mpu401.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of MPU-401 in UART mode
*
* Modified for the Aureal Vortex based Soundcards
* by Manuel Jander (mjande@embedded.cl).
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/time.h>
@@ -41,7 +27,7 @@
#define MPU401_ENTER_UART 0x3f
#define MPU401_ACK 0xfe
-static int __devinit snd_vortex_midi(vortex_t * vortex)
+static int snd_vortex_midi(vortex_t *vortex)
{
struct snd_rawmidi *rmidi;
int temp, mode;
@@ -73,7 +59,7 @@ static int __devinit snd_vortex_midi(vortex_t * vortex)
/* Check if anything is OK. */
temp = hwread(vortex->mmio, VORTEX_MIDI_DATA);
if (temp != MPU401_ACK /*0xfe */ ) {
- printk(KERN_ERR "midi port doesn't acknowledge!\n");
+ dev_err(vortex->card->dev, "midi port doesn't acknowledge!\n");
return -ENODEV;
}
/* Enable MPU401 interrupts. */
@@ -82,9 +68,9 @@ static int __devinit snd_vortex_midi(vortex_t * vortex)
/* Create MPU401 instance. */
#ifdef VORTEX_MPU401_LEGACY
- if ((temp =
- snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_MPU401, 0x330,
- 0, 0, 0, &rmidi)) != 0) {
+ temp = snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_MPU401, 0x330,
+ MPU401_INFO_IRQ_HOOK, -1, &rmidi);
+ if (temp) {
hwwrite(vortex->mmio, VORTEX_CTRL,
(hwread(vortex->mmio, VORTEX_CTRL) &
~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN);
@@ -92,10 +78,10 @@ static int __devinit snd_vortex_midi(vortex_t * vortex)
}
#else
port = (unsigned long)(vortex->mmio + VORTEX_MIDI_DATA);
- if ((temp =
- snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_AUREAL, port,
- MPU401_INFO_INTEGRATED | MPU401_INFO_MMIO,
- 0, 0, &rmidi)) != 0) {
+ temp = snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_AUREAL, port,
+ MPU401_INFO_INTEGRATED | MPU401_INFO_MMIO |
+ MPU401_INFO_IRQ_HOOK, -1, &rmidi);
+ if (temp) {
hwwrite(vortex->mmio, VORTEX_CTRL,
(hwread(vortex->mmio, VORTEX_CTRL) &
~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN);
diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c
index b9d2f202cf9b..546f71220604 100644
--- a/sound/pci/au88x0/au88x0_pcm.c
+++ b/sound/pci/au88x0/au88x0_pcm.c
@@ -1,17 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Library General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
@@ -30,7 +18,7 @@
#define VORTEX_PCM_TYPE(x) (x->name[40])
/* hardware definition */
-static struct snd_pcm_hardware snd_vortex_playback_hw_adb = {
+static const struct snd_pcm_hardware snd_vortex_playback_hw_adb = {
.info =
(SNDRV_PCM_INFO_MMAP | /* SNDRV_PCM_INFO_RESUME | */
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED |
@@ -42,20 +30,16 @@ static struct snd_pcm_hardware snd_vortex_playback_hw_adb = {
.rate_min = 5000,
.rate_max = 48000,
.channels_min = 1,
-#ifdef CHIP_AU8830
- .channels_max = 4,
-#else
.channels_max = 2,
-#endif
.buffer_bytes_max = 0x10000,
- .period_bytes_min = 0x1,
+ .period_bytes_min = 0x20,
.period_bytes_max = 0x1000,
.periods_min = 2,
- .periods_max = 32,
+ .periods_max = 1024,
};
#ifndef CHIP_AU8820
-static struct snd_pcm_hardware snd_vortex_playback_hw_a3d = {
+static const struct snd_pcm_hardware snd_vortex_playback_hw_a3d = {
.info =
(SNDRV_PCM_INFO_MMAP | /* SNDRV_PCM_INFO_RESUME | */
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED |
@@ -75,7 +59,7 @@ static struct snd_pcm_hardware snd_vortex_playback_hw_a3d = {
.periods_max = 64,
};
#endif
-static struct snd_pcm_hardware snd_vortex_playback_hw_spdif = {
+static const struct snd_pcm_hardware snd_vortex_playback_hw_spdif = {
.info =
(SNDRV_PCM_INFO_MMAP | /* SNDRV_PCM_INFO_RESUME | */
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED |
@@ -98,7 +82,7 @@ static struct snd_pcm_hardware snd_vortex_playback_hw_spdif = {
};
#ifndef CHIP_AU8810
-static struct snd_pcm_hardware snd_vortex_playback_hw_wt = {
+static const struct snd_pcm_hardware snd_vortex_playback_hw_wt = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
@@ -115,6 +99,29 @@ static struct snd_pcm_hardware snd_vortex_playback_hw_wt = {
.periods_max = 64,
};
#endif
+#ifdef CHIP_AU8830
+static const unsigned int au8830_channels[3] = {
+ 1, 2, 4,
+};
+
+static const struct snd_pcm_hw_constraint_list hw_constraints_au8830_channels = {
+ .count = ARRAY_SIZE(au8830_channels),
+ .list = au8830_channels,
+ .mask = 0,
+};
+#endif
+
+static void vortex_notify_pcm_vol_change(struct snd_card *card,
+ struct snd_kcontrol *kctl, int activate)
+{
+ if (activate)
+ kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ else
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE |
+ SNDRV_CTL_EVENT_MASK_INFO, &(kctl->id));
+}
+
/* open callback */
static int snd_vortex_pcm_open(struct snd_pcm_substream *substream)
{
@@ -123,16 +130,19 @@ static int snd_vortex_pcm_open(struct snd_pcm_substream *substream)
int err;
/* Force equal size periods */
- if ((err =
- snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ err = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
return err;
/* Avoid PAGE_SIZE boundary to fall inside of a period. */
- if ((err =
- snd_pcm_hw_constraint_pow2(runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
+ err = snd_pcm_hw_constraint_pow2(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES);
+ if (err < 0)
return err;
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 64);
+
if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
#ifndef CHIP_AU8820
if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_A3D) {
@@ -156,6 +166,16 @@ static int snd_vortex_pcm_open(struct snd_pcm_substream *substream)
if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB
|| VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_I2S)
runtime->hw = snd_vortex_playback_hw_adb;
+#ifdef CHIP_AU8830
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ VORTEX_IS_QUAD(vortex) &&
+ VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
+ runtime->hw.channels_max = 4;
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &hw_constraints_au8830_channels);
+ }
+#endif
substream->runtime->private_data = NULL;
}
#ifndef CHIP_AU8810
@@ -189,17 +209,9 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
{
vortex_t *chip = snd_pcm_substream_chip(substream);
stream_t *stream = (stream_t *) (substream->runtime->private_data);
- int err;
- // Alloc buffer memory.
- err =
- snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
- if (err < 0) {
- printk(KERN_ERR "Vortex: pcm page alloc failed!\n");
- return err;
- }
/*
- printk(KERN_INFO "Vortex: periods %d, period_bytes %d, channels = %d\n", params_periods(hw_params),
+ pr_info( "Vortex: periods %d, period_bytes %d, channels = %d\n", params_periods(hw_params),
params_period_bytes(hw_params), params_channels(hw_params));
*/
spin_lock_irq(&chip->lock);
@@ -210,12 +222,14 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
if (stream != NULL)
vortex_adb_allocroute(chip, stream->dma,
stream->nr_ch, stream->dir,
- stream->type);
+ stream->type,
+ substream->number);
/* Alloc routes. */
dma =
vortex_adb_allocroute(chip, -1,
params_channels(hw_params),
- substream->stream, type);
+ substream->stream, type,
+ substream->number);
if (dma < 0) {
spin_unlock_irq(&chip->lock);
return dma;
@@ -226,6 +240,11 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
vortex_adbdma_setbuffers(chip, dma,
params_period_bytes(hw_params),
params_periods(hw_params));
+ if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
+ chip->pcm_vol[substream->number].active = 1;
+ vortex_notify_pcm_vol_change(chip->card,
+ chip->pcm_vol[substream->number].kctl, 1);
+ }
}
#ifndef CHIP_AU8810
else {
@@ -255,10 +274,18 @@ static int snd_vortex_pcm_hw_free(struct snd_pcm_substream *substream)
spin_lock_irq(&chip->lock);
// Delete audio routes.
if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
- if (stream != NULL)
+ if (stream != NULL) {
+ if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
+ chip->pcm_vol[substream->number].active = 0;
+ vortex_notify_pcm_vol_change(chip->card,
+ chip->pcm_vol[substream->number].kctl,
+ 0);
+ }
vortex_adb_allocroute(chip, stream->dma,
stream->nr_ch, stream->dir,
- stream->type);
+ stream->type,
+ substream->number);
+ }
}
#ifndef CHIP_AU8810
else {
@@ -269,7 +296,7 @@ static int snd_vortex_pcm_hw_free(struct snd_pcm_substream *substream)
substream->runtime->private_data = NULL;
spin_unlock_irq(&chip->lock);
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
/* prepare callback */
@@ -285,11 +312,11 @@ static int snd_vortex_pcm_prepare(struct snd_pcm_substream *substream)
dir = 1;
else
dir = 0;
- fmt = vortex_alsafmt_aspfmt(runtime->format);
+ fmt = vortex_alsafmt_aspfmt(runtime->format, chip);
spin_lock_irq(&chip->lock);
if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
- vortex_adbdma_setmode(chip, dma, 1, dir, fmt, 0 /*? */ ,
- 0);
+ vortex_adbdma_setmode(chip, dma, 1, dir, fmt,
+ runtime->channels == 1 ? 0 : 1, 0);
vortex_adbdma_setstartbuffer(chip, dma, 0);
if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_SPDIF)
vortex_adb_setsrc(chip, dma, runtime->rate, dir);
@@ -324,7 +351,7 @@ static int snd_vortex_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
}
#ifndef CHIP_AU8810
else {
- printk(KERN_INFO "vortex: wt start %d\n", dma);
+ dev_info(chip->card->dev, "wt start %d\n", dma);
vortex_wtdma_startfifo(chip, dma);
}
#endif
@@ -334,11 +361,10 @@ static int snd_vortex_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
//printk(KERN_INFO "vortex: stop %d\n", dma);
stream->fifo_enabled = 0;
if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT)
- vortex_adbdma_pausefifo(chip, dma);
- //vortex_adbdma_stopfifo(chip, dma);
+ vortex_adbdma_stopfifo(chip, dma);
#ifndef CHIP_AU8810
else {
- printk(KERN_INFO "vortex: wt stop %d\n", dma);
+ dev_info(chip->card->dev, "wt stop %d\n", dma);
vortex_wtdma_stopfifo(chip, dma);
}
#endif
@@ -386,34 +412,35 @@ static snd_pcm_uframes_t snd_vortex_pcm_pointer(struct snd_pcm_substream *substr
#endif
//printk(KERN_INFO "vortex: pointer = 0x%x\n", current_ptr);
spin_unlock(&chip->lock);
- return (bytes_to_frames(substream->runtime, current_ptr));
+ current_ptr = bytes_to_frames(substream->runtime, current_ptr);
+ if (current_ptr >= substream->runtime->buffer_size)
+ current_ptr = 0;
+ return current_ptr;
}
/* operators */
-static struct snd_pcm_ops snd_vortex_playback_ops = {
+static const struct snd_pcm_ops snd_vortex_playback_ops = {
.open = snd_vortex_pcm_open,
.close = snd_vortex_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_vortex_pcm_hw_params,
.hw_free = snd_vortex_pcm_hw_free,
.prepare = snd_vortex_pcm_prepare,
.trigger = snd_vortex_pcm_trigger,
.pointer = snd_vortex_pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
/*
* definitions of capture are omitted here...
*/
-static char *vortex_pcm_prettyname[VORTEX_PCM_LAST] = {
- "AU88x0 ADB",
- "AU88x0 SPDIF",
- "AU88x0 A3D",
- "AU88x0 WT",
- "AU88x0 I2S",
+static const char * const vortex_pcm_prettyname[VORTEX_PCM_LAST] = {
+ CARD_NAME " ADB",
+ CARD_NAME " SPDIF",
+ CARD_NAME " A3D",
+ CARD_NAME " WT",
+ CARD_NAME " I2S",
};
-static char *vortex_pcm_name[VORTEX_PCM_LAST] = {
+static const char * const vortex_pcm_name[VORTEX_PCM_LAST] = {
"adb",
"spdif",
"a3d",
@@ -470,7 +497,7 @@ static int snd_vortex_spdif_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
}
/* spdif controls */
-static struct snd_kcontrol_new snd_vortex_mixer_spdif[] __devinitdata = {
+static const struct snd_kcontrol_new snd_vortex_mixer_spdif[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -487,8 +514,85 @@ static struct snd_kcontrol_new snd_vortex_mixer_spdif[] __devinitdata = {
},
};
+/* subdevice PCM Volume control */
+
+static int snd_vortex_pcm_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+ uinfo->value.integer.min = -128;
+ uinfo->value.integer.max = 32;
+ return 0;
+}
+
+static int snd_vortex_pcm_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ int subdev = kcontrol->id.subdevice;
+ struct pcm_vol *p = &vortex->pcm_vol[subdev];
+ int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+ for (i = 0; i < max_chn; i++)
+ ucontrol->value.integer.value[i] = p->vol[i];
+ return 0;
+}
+
+static int snd_vortex_pcm_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ int changed = 0;
+ int mixin;
+ unsigned char vol;
+ vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+ int subdev = kcontrol->id.subdevice;
+ struct pcm_vol *p = &vortex->pcm_vol[subdev];
+ int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+ for (i = 0; i < max_chn; i++) {
+ if (p->vol[i] != ucontrol->value.integer.value[i]) {
+ p->vol[i] = ucontrol->value.integer.value[i];
+ if (p->active) {
+ switch (vortex->dma_adb[p->dma].nr_ch) {
+ case 1:
+ mixin = p->mixin[0];
+ break;
+ case 2:
+ default:
+ mixin = p->mixin[(i < 2) ? i : (i - 2)];
+ break;
+ case 4:
+ mixin = p->mixin[i];
+ break;
+ }
+ vol = p->vol[i];
+ vortex_mix_setinputvolumebyte(vortex,
+ vortex->mixplayb[i], mixin, vol);
+ }
+ changed = 1;
+ }
+ }
+ return changed;
+}
+
+static const DECLARE_TLV_DB_MINMAX(vortex_pcm_vol_db_scale, -9600, 2400);
+
+static const struct snd_kcontrol_new snd_vortex_pcm_vol = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "PCM Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+ .info = snd_vortex_pcm_vol_info,
+ .get = snd_vortex_pcm_vol_get,
+ .put = snd_vortex_pcm_vol_put,
+ .tlv = { .p = vortex_pcm_vol_db_scale },
+};
+
/* create a pcm device */
-static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
+static int snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
{
struct snd_pcm *pcm;
struct snd_kcontrol *kctl;
@@ -499,7 +603,7 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
return -ENODEV;
/* idx indicates which kind of PCM device. ADB, SPDIF, I2S and A3D share the
- * same dma engine. WT uses it own separate dma engine whcih cant capture. */
+ * same dma engine. WT uses it own separate dma engine which can't capture. */
if (idx == VORTEX_PCM_ADB)
nr_capt = nr;
else
@@ -508,7 +612,8 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
nr_capt, &pcm);
if (err < 0)
return err;
- strcpy(pcm->name, vortex_pcm_name[idx]);
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s %s", CARD_NAME_SHORT, vortex_pcm_name[idx]);
chip->pcm[idx] = pcm;
// This is an evil hack, but it saves a lot of duplicated code.
VORTEX_PCM_TYPE(pcm) = idx;
@@ -522,16 +627,54 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
/* pre-allocation of Scatter-Gather buffers */
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci_dev),
- 0x10000, 0x10000);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &chip->pci_dev->dev, 0x10000, 0x10000);
+
+ switch (VORTEX_PCM_TYPE(pcm)) {
+ case VORTEX_PCM_ADB:
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_std_chmaps,
+ VORTEX_IS_QUAD(chip) ? 4 : 2,
+ 0, NULL);
+ if (err < 0)
+ return err;
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ snd_pcm_std_chmaps, 2, 0, NULL);
+ if (err < 0)
+ return err;
+ break;
+#ifdef CHIP_AU8830
+ case VORTEX_PCM_A3D:
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_std_chmaps, 1, 0, NULL);
+ if (err < 0)
+ return err;
+ break;
+#endif
+ }
if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_SPDIF) {
for (i = 0; i < ARRAY_SIZE(snd_vortex_mixer_spdif); i++) {
kctl = snd_ctl_new1(&snd_vortex_mixer_spdif[i], chip);
if (!kctl)
return -ENOMEM;
- if ((err = snd_ctl_add(chip->card, kctl)) < 0)
+ err = snd_ctl_add(chip->card, kctl);
+ if (err < 0)
+ return err;
+ }
+ }
+ if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_ADB) {
+ for (i = 0; i < NR_PCM; i++) {
+ chip->pcm_vol[i].active = 0;
+ chip->pcm_vol[i].dma = -1;
+ kctl = snd_ctl_new1(&snd_vortex_pcm_vol, chip);
+ if (!kctl)
+ return -ENOMEM;
+ chip->pcm_vol[i].kctl = kctl;
+ kctl->id.device = 0;
+ kctl->id.subdevice = i;
+ err = snd_ctl_add(chip->card, kctl);
+ if (err < 0)
return err;
}
}
diff --git a/sound/pci/au88x0/au88x0_synth.c b/sound/pci/au88x0/au88x0_synth.c
index 2805e34bd41d..84d961e7c79c 100644
--- a/sound/pci/au88x0/au88x0_synth.c
+++ b/sound/pci/au88x0/au88x0_synth.c
@@ -1,17 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Library General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
@@ -58,7 +46,7 @@ static void vortex_wt_setdsout(vortex_t * vortex, u32 wt, int en)
if (en)
temp |= (1 << (wt & 0x1f));
else
- temp &= (1 << ~(wt & 0x1f));
+ temp &= ~(1 << (wt & 0x1f));
hwwrite(vortex->mmio, WT_DSREG((wt >= 0x20) ? 1 : 0), temp);
}
@@ -90,7 +78,7 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch)
hwwrite(vortex->mmio, WT_PARM(wt, 2), 0);
temp = hwread(vortex->mmio, WT_PARM(wt, 3));
- printk(KERN_DEBUG "vortex: WT PARM3: %x\n", temp);
+ dev_dbg(vortex->card->dev, "WT PARM3: %x\n", temp);
//hwwrite(vortex->mmio, WT_PARM(wt, 3), temp);
hwwrite(vortex->mmio, WT_DELAY(wt, 0), 0);
@@ -98,7 +86,8 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch)
hwwrite(vortex->mmio, WT_DELAY(wt, 2), 0);
hwwrite(vortex->mmio, WT_DELAY(wt, 3), 0);
- printk(KERN_DEBUG "vortex: WT GMODE: %x\n", hwread(vortex->mmio, WT_GMODE(wt)));
+ dev_dbg(vortex->card->dev, "WT GMODE: %x\n",
+ hwread(vortex->mmio, WT_GMODE(wt)));
hwwrite(vortex->mmio, WT_PARM(wt, 2), 0xffffffff);
hwwrite(vortex->mmio, WT_PARM(wt, 3), 0xcff1c810);
@@ -106,7 +95,8 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch)
voice->parm0 = voice->parm1 = 0xcfb23e2f;
hwwrite(vortex->mmio, WT_PARM(wt, 0), voice->parm0);
hwwrite(vortex->mmio, WT_PARM(wt, 1), voice->parm1);
- printk(KERN_DEBUG "vortex: WT GMODE 2 : %x\n", hwread(vortex->mmio, WT_GMODE(wt)));
+ dev_dbg(vortex->card->dev, "WT GMODE 2 : %x\n",
+ hwread(vortex->mmio, WT_GMODE(wt)));
return 0;
}
@@ -196,14 +186,15 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
if ((reg == 5) || ((reg >= 7) && (reg <= 10)) || (reg == 0xc)) {
if (wt >= (NR_WT / NR_WT_PB)) {
- printk
- ("vortex: WT SetReg: bank out of range. reg=0x%x, wt=%d\n",
- reg, wt);
+ dev_warn(vortex->card->dev,
+ "WT SetReg: bank out of range. reg=0x%x, wt=%d\n",
+ reg, wt);
return 0;
}
} else {
if (wt >= NR_WT) {
- printk(KERN_ERR "vortex: WT SetReg: voice out of range\n");
+ dev_err(vortex->card->dev,
+ "WT SetReg: voice out of range\n");
return 0;
}
}
@@ -214,65 +205,57 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
/* Voice specific parameters */
case 0: /* running */
/*
- printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
WT_RUN(wt), (int)val);
*/
hwwrite(vortex->mmio, WT_RUN(wt), val);
return 0xc;
- break;
case 1: /* param 0 */
/*
- printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
WT_PARM(wt,0), (int)val);
*/
hwwrite(vortex->mmio, WT_PARM(wt, 0), val);
return 0xc;
- break;
case 2: /* param 1 */
/*
- printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
WT_PARM(wt,1), (int)val);
*/
hwwrite(vortex->mmio, WT_PARM(wt, 1), val);
return 0xc;
- break;
case 3: /* param 2 */
/*
- printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
WT_PARM(wt,2), (int)val);
*/
hwwrite(vortex->mmio, WT_PARM(wt, 2), val);
return 0xc;
- break;
case 4: /* param 3 */
/*
- printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
WT_PARM(wt,3), (int)val);
*/
hwwrite(vortex->mmio, WT_PARM(wt, 3), val);
return 0xc;
- break;
case 6: /* mute */
/*
- printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
WT_MUTE(wt), (int)val);
*/
hwwrite(vortex->mmio, WT_MUTE(wt), val);
return 0xc;
- break;
case 0xb:
- { /* delay */
- /*
- printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n",
- WT_DELAY(wt,0), (int)val);
- */
- hwwrite(vortex->mmio, WT_DELAY(wt, 3), val);
- hwwrite(vortex->mmio, WT_DELAY(wt, 2), val);
- hwwrite(vortex->mmio, WT_DELAY(wt, 1), val);
- hwwrite(vortex->mmio, WT_DELAY(wt, 0), val);
- return 0xc;
- }
- break;
+ /* delay */
+ /*
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n",
+ WT_DELAY(wt,0), (int)val);
+ */
+ hwwrite(vortex->mmio, WT_DELAY(wt, 3), val);
+ hwwrite(vortex->mmio, WT_DELAY(wt, 2), val);
+ hwwrite(vortex->mmio, WT_DELAY(wt, 1), val);
+ hwwrite(vortex->mmio, WT_DELAY(wt, 0), val);
+ return 0xc;
/* Global WT block parameters */
case 5: /* sramp */
ecx = WT_SRAMP(wt);
@@ -291,10 +274,9 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt,
break;
default:
return 0;
- break;
}
/*
- printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", ecx, (int)val);
+ pr_debug( "vortex: WT SetReg(0x%x) = 0x%08x\n", ecx, (int)val);
*/
hwwrite(vortex->mmio, ecx, val);
return 1;
diff --git a/sound/pci/au88x0/au88x0_wt.h b/sound/pci/au88x0/au88x0_wt.h
index 38d98f88a95c..7b2cffad8643 100644
--- a/sound/pci/au88x0/au88x0_wt.h
+++ b/sound/pci/au88x0/au88x0_wt.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/***************************************************************************
* WT register offsets.
*
diff --git a/sound/pci/au88x0/au88x0_xtalk.c b/sound/pci/au88x0/au88x0_xtalk.c
index b4151e208b71..27859536d7c0 100644
--- a/sound/pci/au88x0/au88x0_xtalk.c
+++ b/sound/pci/au88x0/au88x0_xtalk.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/***************************************************************************
* au88x0_cxtalk.c
*
@@ -7,19 +8,6 @@
****************************************************************************/
/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Library General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "au88x0_xtalk.h"
@@ -29,94 +17,112 @@
static short const sXtalkWideKLeftEq = 0x269C;
static short const sXtalkWideKRightEq = 0x269C;
static short const sXtalkWideKLeftXt = 0xF25E;
-static short const sXtalkWideKRightXt = 0xF25E;
+static __maybe_unused short const sXtalkWideKRightXt = 0xF25E;
static short const sXtalkWideShiftLeftEq = 1;
static short const sXtalkWideShiftRightEq = 1;
static short const sXtalkWideShiftLeftXt = 0;
-static short const sXtalkWideShiftRightXt = 0;
+static __maybe_unused short const sXtalkWideShiftRightXt = 0;
static unsigned short const wXtalkWideLeftDelay = 0xd;
static unsigned short const wXtalkWideRightDelay = 0xd;
static short const sXtalkNarrowKLeftEq = 0x468D;
static short const sXtalkNarrowKRightEq = 0x468D;
static short const sXtalkNarrowKLeftXt = 0xF82E;
-static short const sXtalkNarrowKRightXt = 0xF82E;
+static __maybe_unused short const sXtalkNarrowKRightXt = 0xF82E;
static short const sXtalkNarrowShiftLeftEq = 0x3;
static short const sXtalkNarrowShiftRightEq = 0x3;
static short const sXtalkNarrowShiftLeftXt = 0;
-static short const sXtalkNarrowShiftRightXt = 0;
+static __maybe_unused short const sXtalkNarrowShiftRightXt = 0;
static unsigned short const wXtalkNarrowLeftDelay = 0x7;
static unsigned short const wXtalkNarrowRightDelay = 0x7;
-static xtalk_gains_t const asXtalkGainsDefault = {
- 0x4000, 0x4000, 4000, 0x4000, 4000, 0x4000, 4000, 0x4000, 4000,
- 0x4000
+static __maybe_unused xtalk_gains_t const asXtalkGainsDefault = {
+ 0x4000, 0x4000, 0x4000, 0x4000, 0x4000,
+ 0x4000, 0x4000, 0x4000, 0x4000, 0x4000
};
-static xtalk_gains_t const asXtalkGainsTest = {
- 0x8000, 0x7FFF, 0, 0xFFFF, 0x0001, 0xC000, 0x4000, 0xFFFE, 0x0002,
- 0
+static __maybe_unused xtalk_gains_t const asXtalkGainsTest = {
+ 0x7fff, 0x8000, 0x0000, 0x0000, 0x0001,
+ 0xffff, 0x4000, 0xc000, 0x0002, 0xfffe
};
-static xtalk_gains_t const asXtalkGains1Chan = {
- 0x7FFF, 0, 0, 0, 0x7FFF, 0, 0, 0, 0, 0
+
+static __maybe_unused xtalk_gains_t const asXtalkGains1Chan = {
+ 0x7FFF, 0, 0, 0, 0,
+ 0x7FFF, 0, 0, 0, 0,
};
// Input gain for 4 A3D slices. One possible input pair is left zero.
static xtalk_gains_t const asXtalkGainsAllChan = {
- 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF,
- 0
- //0x7FFF,0x7FFF,0x7FFF,0x7FFF,0x7fff,0x7FFF,0x7FFF,0x7FFF,0x7FFF,0x7fff
+ 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0,
+ 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0
+};
+
+static xtalk_gains_t const asXtalkGainsZeros = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
-static xtalk_gains_t const asXtalkGainsZeros;
-static xtalk_dline_t const alXtalkDlineZeros;
-static xtalk_dline_t const alXtalkDlineTest = {
- 0xFC18, 0x03E8FFFF, 0x186A0, 0x7960FFFE, 1, 0xFFFFFFFF,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+static xtalk_dline_t const alXtalkDlineZeros = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+static __maybe_unused xtalk_dline_t const alXtalkDlineTest = {
+ 0x0000fc18, 0xfff03e8, 0x000186a0, 0xfffe7960, 1, 0xffffffff, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static xtalk_instate_t const asXtalkInStateZeros = {
0, 0, 0, 0
};
-static xtalk_instate_t const asXtalkInStateZeros;
-static xtalk_instate_t const asXtalkInStateTest =
- { 0xFF80, 0x0080, 0xFFFF, 0x0001 };
-static xtalk_state_t const asXtalkOutStateZeros;
+static __maybe_unused xtalk_instate_t const asXtalkInStateTest = {
+ 0x0080, 0xff80, 0x0001, 0xffff
+};
+
+static xtalk_state_t const asXtalkOutStateZeros = {
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+};
static short const sDiamondKLeftEq = 0x401d;
static short const sDiamondKRightEq = 0x401d;
static short const sDiamondKLeftXt = 0xF90E;
-static short const sDiamondKRightXt = 0xF90E;
-static short const sDiamondShiftLeftEq = 1; /* 0xF90E Is this a bug ??? */
+static __maybe_unused short const sDiamondKRightXt = 0xF90E;
+static short const sDiamondShiftLeftEq = 1;
static short const sDiamondShiftRightEq = 1;
static short const sDiamondShiftLeftXt = 0;
-static short const sDiamondShiftRightXt = 0;
+static __maybe_unused short const sDiamondShiftRightXt = 0;
static unsigned short const wDiamondLeftDelay = 0xb;
static unsigned short const wDiamondRightDelay = 0xb;
static xtalk_coefs_t const asXtalkWideCoefsLeftEq = {
{0xEC4C, 0xDCE9, 0xFDC2, 0xFEEC, 0},
{0x5F60, 0xCBCB, 0xFC26, 0x0305, 0},
- {0x340B, 0xf504, 0x6CE8, 0x0D23, 0x00E4},
- {0xD500, 0x8D76, 0xACC7, 0x5B05, 0x00FA},
+ {0x340B, 0xe8f5, 0x236c, 0xe40d, 0},
+ {0x76d5, 0xc78d, 0x05ac, 0xfa5b, 0},
{0x7F04, 0xC0FA, 0x0263, 0xFDA2, 0}
};
static xtalk_coefs_t const asXtalkWideCoefsRightEq = {
{0xEC4C, 0xDCE9, 0xFDC2, 0xFEEC, 0},
{0x5F60, 0xCBCB, 0xFC26, 0x0305, 0},
- {0x340B, 0xF504, 0x6CE8, 0x0D23, 0x00E4},
- {0xD500, 0x8D76, 0xACC7, 0x5B05, 0x00FA},
+ {0x340B, 0xe8f5, 0x236c, 0xe40d, 0},
+ {0x76d5, 0xc78d, 0x05ac, 0xfa5b, 0},
{0x7F04, 0xC0FA, 0x0263, 0xFDA2, 0}
};
static xtalk_coefs_t const asXtalkWideCoefsLeftXt = {
- {0x86C3, 0x7B55, 0x89C3, 0x005B, 0x0047},
- {0x6000, 0x206A, 0xC6CA, 0x40FF, 0},
- {0x1100, 0x1164, 0xA1D7, 0x90FC, 0x0001},
- {0xDC00, 0x9E77, 0xB8C7, 0x0AFF, 0},
+ {0x55c6, 0xc97b, 0x005b, 0x0047, 0},
+ {0x6a60, 0xca20, 0xffc6, 0x0040, 0},
+ {0x6411, 0xd711, 0xfca1, 0x0190, 0},
+ {0x77dc, 0xc79e, 0xffb8, 0x000a, 0},
{0, 0, 0, 0, 0}
};
-static xtalk_coefs_t const asXtalkWideCoefsRightXt = {
- {0x86C3, 0x7B55, 0x89C3, 0x005B, 0x0047},
- {0x6000, 0x206A, 0xC6CA, 0x40FF, 0},
- {0x1100, 0x1164, 0xA1D7, 0x90FC, 0x0001},
- {0xDC00, 0x9E77, 0xB8C7, 0x0AFF, 0},
+static __maybe_unused xtalk_coefs_t const asXtalkWideCoefsRightXt = {
+ {0x55c6, 0xc97b, 0x005b, 0x0047, 0},
+ {0x6a60, 0xca20, 0xffc6, 0x0040, 0},
+ {0x6411, 0xd711, 0xfca1, 0x0190, 0},
+ {0x77dc, 0xc79e, 0xffb8, 0x000a, 0},
{0, 0, 0, 0, 0}
};
static xtalk_coefs_t const asXtalkNarrowCoefsLeftEq = {
@@ -143,7 +149,7 @@ static xtalk_coefs_t const asXtalkNarrowCoefsLeftXt = {
{0, 0, 0, 0, 0}
};
-static xtalk_coefs_t const asXtalkNarrowCoefsRightXt = {
+static __maybe_unused xtalk_coefs_t const asXtalkNarrowCoefsRightXt = {
{0x3CB2, 0xDF49, 0xF6EA, 0x095B, 0},
{0x6777, 0xC915, 0xFEAF, 0x00B1, 0},
{0x7762, 0xC7D9, 0x025B, 0xFDA6, 0},
@@ -151,7 +157,14 @@ static xtalk_coefs_t const asXtalkNarrowCoefsRightXt = {
{0, 0, 0, 0, 0}
};
-static xtalk_coefs_t const asXtalkCoefsZeros;
+static xtalk_coefs_t const asXtalkCoefsZeros = {
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0}
+};
+
static xtalk_coefs_t const asXtalkCoefsPipe = {
{0, 0, 0x0FA0, 0, 0},
{0, 0, 0x0FA0, 0, 0},
@@ -159,7 +172,7 @@ static xtalk_coefs_t const asXtalkCoefsPipe = {
{0, 0, 0x0FA0, 0, 0},
{0, 0, 0x1180, 0, 0},
};
-static xtalk_coefs_t const asXtalkCoefsNegPipe = {
+static __maybe_unused xtalk_coefs_t const asXtalkCoefsNegPipe = {
{0, 0, 0xF380, 0, 0},
{0, 0, 0xF380, 0, 0},
{0, 0, 0xF380, 0, 0},
@@ -167,7 +180,7 @@ static xtalk_coefs_t const asXtalkCoefsNegPipe = {
{0, 0, 0xF200, 0, 0}
};
-static xtalk_coefs_t const asXtalkCoefsNumTest = {
+static __maybe_unused xtalk_coefs_t const asXtalkCoefsNumTest = {
{0, 0, 0xF380, 0x8000, 0x6D60},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
@@ -175,7 +188,7 @@ static xtalk_coefs_t const asXtalkCoefsNumTest = {
{0, 0, 0, 0, 0}
};
-static xtalk_coefs_t const asXtalkCoefsDenTest = {
+static __maybe_unused xtalk_coefs_t const asXtalkCoefsDenTest = {
{0xC000, 0x2000, 0x4000, 0, 0},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
@@ -183,10 +196,10 @@ static xtalk_coefs_t const asXtalkCoefsDenTest = {
{0, 0, 0, 0, 0}
};
-static xtalk_state_t const asXtalkOutStateTest = {
+static __maybe_unused xtalk_state_t const asXtalkOutStateTest = {
{0x7FFF, 0x0004, 0xFFFC, 0},
{0xFE00, 0x0008, 0xFFF8, 0x4000},
- {0x200, 0x0010, 0xFFF0, 0xC000},
+ {0x0200, 0x0010, 0xFFF0, 0xC000},
{0x8000, 0x0020, 0xFFE0, 0},
{0, 0, 0, 0}
};
@@ -215,7 +228,7 @@ static xtalk_coefs_t const asDiamondCoefsLeftXt = {
{0, 0, 0, 0, 0}
};
-static xtalk_coefs_t const asDiamondCoefsRightXt = {
+static __maybe_unused xtalk_coefs_t const asDiamondCoefsRightXt = {
{0x3B50, 0xFE08, 0xF959, 0x0060, 0},
{0x9FCB, 0xD8F1, 0x00A2, 0x003A, 0},
{0, 0, 0, 0, 0},
@@ -306,10 +319,10 @@ vortex_XtalkHw_SetLeftEQStates(vortex_t * vortex,
hwwrite(vortex->mmio, 0x2421C + i * 0x24, coefs[i][2]);
hwwrite(vortex->mmio, 0x24220 + i * 0x24, coefs[i][3]);
}
- hwwrite(vortex->mmio, 0x244F8 + i * 0x24, arg_0[0]);
- hwwrite(vortex->mmio, 0x244FC + i * 0x24, arg_0[1]);
- hwwrite(vortex->mmio, 0x24500 + i * 0x24, arg_0[2]);
- hwwrite(vortex->mmio, 0x24504 + i * 0x24, arg_0[3]);
+ hwwrite(vortex->mmio, 0x244F8, arg_0[0]);
+ hwwrite(vortex->mmio, 0x244FC, arg_0[1]);
+ hwwrite(vortex->mmio, 0x24500, arg_0[2]);
+ hwwrite(vortex->mmio, 0x24504, arg_0[3]);
}
static void
@@ -325,10 +338,10 @@ vortex_XtalkHw_SetRightEQStates(vortex_t * vortex,
hwwrite(vortex->mmio, 0x242D0 + i * 0x24, coefs[i][2]);
hwwrite(vortex->mmio, 0x244D4 + i * 0x24, coefs[i][3]);
}
- hwwrite(vortex->mmio, 0x24508 + i * 0x24, arg_0[0]);
- hwwrite(vortex->mmio, 0x2450C + i * 0x24, arg_0[1]);
- hwwrite(vortex->mmio, 0x24510 + i * 0x24, arg_0[2]);
- hwwrite(vortex->mmio, 0x24514 + i * 0x24, arg_0[3]);
+ hwwrite(vortex->mmio, 0x24508, arg_0[0]);
+ hwwrite(vortex->mmio, 0x2450C, arg_0[1]);
+ hwwrite(vortex->mmio, 0x24510, arg_0[2]);
+ hwwrite(vortex->mmio, 0x24514, arg_0[3]);
}
static void
@@ -344,10 +357,10 @@ vortex_XtalkHw_SetLeftXTStates(vortex_t * vortex,
hwwrite(vortex->mmio, 0x24384 + i * 0x24, coefs[i][2]);
hwwrite(vortex->mmio, 0x24388 + i * 0x24, coefs[i][3]);
}
- hwwrite(vortex->mmio, 0x24518 + i * 0x24, arg_0[0]);
- hwwrite(vortex->mmio, 0x2451C + i * 0x24, arg_0[1]);
- hwwrite(vortex->mmio, 0x24520 + i * 0x24, arg_0[2]);
- hwwrite(vortex->mmio, 0x24524 + i * 0x24, arg_0[3]);
+ hwwrite(vortex->mmio, 0x24518, arg_0[0]);
+ hwwrite(vortex->mmio, 0x2451C, arg_0[1]);
+ hwwrite(vortex->mmio, 0x24520, arg_0[2]);
+ hwwrite(vortex->mmio, 0x24524, arg_0[3]);
}
static void
@@ -363,10 +376,10 @@ vortex_XtalkHw_SetRightXTStates(vortex_t * vortex,
hwwrite(vortex->mmio, 0x24438 + i * 0x24, coefs[i][2]);
hwwrite(vortex->mmio, 0x2443C + i * 0x24, coefs[i][3]);
}
- hwwrite(vortex->mmio, 0x24528 + i * 0x24, arg_0[0]);
- hwwrite(vortex->mmio, 0x2452C + i * 0x24, arg_0[1]);
- hwwrite(vortex->mmio, 0x24530 + i * 0x24, arg_0[2]);
- hwwrite(vortex->mmio, 0x24534 + i * 0x24, arg_0[3]);
+ hwwrite(vortex->mmio, 0x24528, arg_0[0]);
+ hwwrite(vortex->mmio, 0x2452C, arg_0[1]);
+ hwwrite(vortex->mmio, 0x24530, arg_0[2]);
+ hwwrite(vortex->mmio, 0x24534, arg_0[3]);
}
#if 0
@@ -450,10 +463,10 @@ vortex_XtalkHw_GetLeftEQStates(vortex_t * vortex, xtalk_instate_t arg_0,
coefs[i][2] = hwread(vortex->mmio, 0x2421C + i * 0x24);
coefs[i][3] = hwread(vortex->mmio, 0x24220 + i * 0x24);
}
- arg_0[0] = hwread(vortex->mmio, 0x244F8 + i * 0x24);
- arg_0[1] = hwread(vortex->mmio, 0x244FC + i * 0x24);
- arg_0[2] = hwread(vortex->mmio, 0x24500 + i * 0x24);
- arg_0[3] = hwread(vortex->mmio, 0x24504 + i * 0x24);
+ arg_0[0] = hwread(vortex->mmio, 0x244F8);
+ arg_0[1] = hwread(vortex->mmio, 0x244FC);
+ arg_0[2] = hwread(vortex->mmio, 0x24500);
+ arg_0[3] = hwread(vortex->mmio, 0x24504);
}
static void
@@ -468,10 +481,10 @@ vortex_XtalkHw_GetRightEQStates(vortex_t * vortex, xtalk_instate_t arg_0,
coefs[i][2] = hwread(vortex->mmio, 0x242D0 + i * 0x24);
coefs[i][3] = hwread(vortex->mmio, 0x242D4 + i * 0x24);
}
- arg_0[0] = hwread(vortex->mmio, 0x24508 + i * 0x24);
- arg_0[1] = hwread(vortex->mmio, 0x2450C + i * 0x24);
- arg_0[2] = hwread(vortex->mmio, 0x24510 + i * 0x24);
- arg_0[3] = hwread(vortex->mmio, 0x24514 + i * 0x24);
+ arg_0[0] = hwread(vortex->mmio, 0x24508);
+ arg_0[1] = hwread(vortex->mmio, 0x2450C);
+ arg_0[2] = hwread(vortex->mmio, 0x24510);
+ arg_0[3] = hwread(vortex->mmio, 0x24514);
}
static void
@@ -486,10 +499,10 @@ vortex_XtalkHw_GetLeftXTStates(vortex_t * vortex, xtalk_instate_t arg_0,
coefs[i][2] = hwread(vortex->mmio, 0x24384 + i * 0x24);
coefs[i][3] = hwread(vortex->mmio, 0x24388 + i * 0x24);
}
- arg_0[0] = hwread(vortex->mmio, 0x24518 + i * 0x24);
- arg_0[1] = hwread(vortex->mmio, 0x2451C + i * 0x24);
- arg_0[2] = hwread(vortex->mmio, 0x24520 + i * 0x24);
- arg_0[3] = hwread(vortex->mmio, 0x24524 + i * 0x24);
+ arg_0[0] = hwread(vortex->mmio, 0x24518);
+ arg_0[1] = hwread(vortex->mmio, 0x2451C);
+ arg_0[2] = hwread(vortex->mmio, 0x24520);
+ arg_0[3] = hwread(vortex->mmio, 0x24524);
}
static void
@@ -504,10 +517,10 @@ vortex_XtalkHw_GetRightXTStates(vortex_t * vortex, xtalk_instate_t arg_0,
coefs[i][2] = hwread(vortex->mmio, 0x24438 + i * 0x24);
coefs[i][3] = hwread(vortex->mmio, 0x2443C + i * 0x24);
}
- arg_0[0] = hwread(vortex->mmio, 0x24528 + i * 0x24);
- arg_0[1] = hwread(vortex->mmio, 0x2452C + i * 0x24);
- arg_0[2] = hwread(vortex->mmio, 0x24530 + i * 0x24);
- arg_0[3] = hwread(vortex->mmio, 0x24534 + i * 0x24);
+ arg_0[0] = hwread(vortex->mmio, 0x24528);
+ arg_0[1] = hwread(vortex->mmio, 0x2452C);
+ arg_0[2] = hwread(vortex->mmio, 0x24530);
+ arg_0[3] = hwread(vortex->mmio, 0x24534);
}
#endif
diff --git a/sound/pci/au88x0/au88x0_xtalk.h b/sound/pci/au88x0/au88x0_xtalk.h
index 7f4534b94d00..4791bd4fec2c 100644
--- a/sound/pci/au88x0/au88x0_xtalk.h
+++ b/sound/pci/au88x0/au88x0_xtalk.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/***************************************************************************
* au88x0_cxtalk.h
*
@@ -7,19 +8,6 @@
****************************************************************************/
/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Library General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* The crosstalk canceler supports 5 stereo input channels. The result is
diff --git a/sound/pci/aw2/Makefile b/sound/pci/aw2/Makefile
index 842335d3b735..c246f7c7f2bf 100644
--- a/sound/pci/aw2/Makefile
+++ b/sound/pci/aw2/Makefile
@@ -1,3 +1,4 @@
-snd-aw2-objs := aw2-alsa.o aw2-saa7146.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-aw2-y := aw2-alsa.o aw2-saa7146.o
obj-$(CONFIG_SND_AW2) += snd-aw2.o
diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c
index c15002242d98..e2c501f4394c 100644
--- a/sound/pci/aw2/aw2-alsa.c
+++ b/sound/pci/aw2/aw2-alsa.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*****************************************************************************
*
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
@@ -5,20 +6,6 @@
*
* This file is part of the Audiowerk2 ALSA driver
*
- * The Audiowerk2 ALSA driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2.
- *
- * The Audiowerk2 ALSA driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Audiowerk2 ALSA driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- *
*****************************************************************************/
#include <linux/init.h>
#include <linux/pci.h>
@@ -27,6 +14,7 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/io.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
@@ -51,7 +39,7 @@ MODULE_LICENSE("GPL");
* TYPEDEFS
********************************/
/* hardware definition */
-static struct snd_pcm_hardware snd_aw2_playback_hw = {
+static const struct snd_pcm_hardware snd_aw2_playback_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
@@ -68,7 +56,7 @@ static struct snd_pcm_hardware snd_aw2_playback_hw = {
.periods_max = 1024,
};
-static struct snd_pcm_hardware snd_aw2_capture_hw = {
+static const struct snd_pcm_hardware snd_aw2_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID),
@@ -111,21 +99,13 @@ struct aw2 {
/*********************************
* FUNCTION DECLARATIONS
********************************/
-static int __init alsa_card_aw2_init(void);
-static void __exit alsa_card_aw2_exit(void);
-static int snd_aw2_dev_free(struct snd_device *device);
-static int __devinit snd_aw2_create(struct snd_card *card,
- struct pci_dev *pci, struct aw2 **rchip);
-static int __devinit snd_aw2_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id);
-static void __devexit snd_aw2_remove(struct pci_dev *pci);
+static int snd_aw2_create(struct snd_card *card, struct pci_dev *pci);
+static int snd_aw2_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id);
static int snd_aw2_pcm_playback_open(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_playback_close(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_capture_open(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_capture_close(struct snd_pcm_substream *substream);
-static int snd_aw2_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params);
-static int snd_aw2_pcm_hw_free(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream);
static int snd_aw2_pcm_trigger_playback(struct snd_pcm_substream *substream,
@@ -136,7 +116,7 @@ static snd_pcm_uframes_t snd_aw2_pcm_pointer_playback(struct snd_pcm_substream
*substream);
static snd_pcm_uframes_t snd_aw2_pcm_pointer_capture(struct snd_pcm_substream
*substream);
-static int __devinit snd_aw2_new_pcm(struct aw2 *chip);
+static int snd_aw2_new_pcm(struct aw2 *chip);
static int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
@@ -152,7 +132,7 @@ static int snd_aw2_control_switch_capture_put(struct snd_kcontrol *kcontrol,
********************************/
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Audiowerk2 soundcard.");
@@ -161,7 +141,7 @@ MODULE_PARM_DESC(id, "ID string for the Audiowerk2 soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Audiowerk2 soundcard.");
-static DEFINE_PCI_DEVICE_TABLE(snd_aw2_ids) = {
+static const struct pci_device_id snd_aw2_ids[] = {
{PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, 0, 0,
0, 0, 0},
{0}
@@ -170,38 +150,33 @@ static DEFINE_PCI_DEVICE_TABLE(snd_aw2_ids) = {
MODULE_DEVICE_TABLE(pci, snd_aw2_ids);
/* pci_driver definition */
-static struct pci_driver driver = {
- .name = "Emagic Audiowerk 2",
+static struct pci_driver aw2_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_aw2_ids,
.probe = snd_aw2_probe,
- .remove = __devexit_p(snd_aw2_remove),
};
+module_pci_driver(aw2_driver);
+
/* operators for playback PCM alsa interface */
-static struct snd_pcm_ops snd_aw2_playback_ops = {
+static const struct snd_pcm_ops snd_aw2_playback_ops = {
.open = snd_aw2_pcm_playback_open,
.close = snd_aw2_pcm_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_aw2_pcm_hw_params,
- .hw_free = snd_aw2_pcm_hw_free,
.prepare = snd_aw2_pcm_prepare_playback,
.trigger = snd_aw2_pcm_trigger_playback,
.pointer = snd_aw2_pcm_pointer_playback,
};
/* operators for capture PCM alsa interface */
-static struct snd_pcm_ops snd_aw2_capture_ops = {
+static const struct snd_pcm_ops snd_aw2_capture_ops = {
.open = snd_aw2_pcm_capture_open,
.close = snd_aw2_pcm_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_aw2_pcm_hw_params,
- .hw_free = snd_aw2_pcm_hw_free,
.prepare = snd_aw2_pcm_prepare_capture,
.trigger = snd_aw2_pcm_trigger_capture,
.pointer = snd_aw2_pcm_pointer_capture,
};
-static struct snd_kcontrol_new aw2_control __devinitdata = {
+static const struct snd_kcontrol_new aw2_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Capture Route",
.index = 0,
@@ -216,77 +191,33 @@ static struct snd_kcontrol_new aw2_control __devinitdata = {
* FUNCTION IMPLEMENTATIONS
********************************/
-/* initialization of the module */
-static int __init alsa_card_aw2_init(void)
-{
- snd_printdd(KERN_DEBUG "aw2: Load aw2 module\n");
- return pci_register_driver(&driver);
-}
-
-/* clean up the module */
-static void __exit alsa_card_aw2_exit(void)
-{
- snd_printdd(KERN_DEBUG "aw2: Unload aw2 module\n");
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_aw2_init);
-module_exit(alsa_card_aw2_exit);
-
/* component-destructor */
-static int snd_aw2_dev_free(struct snd_device *device)
+static void snd_aw2_free(struct snd_card *card)
{
- struct aw2 *chip = device->device_data;
+ struct aw2 *chip = card->private_data;
/* Free hardware */
snd_aw2_saa7146_free(&chip->saa7146);
-
- /* release the irq */
- if (chip->irq >= 0)
- free_irq(chip->irq, (void *)chip);
- /* release the i/o ports & memory */
- if (chip->iobase_virt)
- iounmap(chip->iobase_virt);
-
- pci_release_regions(chip->pci);
- /* disable the PCI entry */
- pci_disable_device(chip->pci);
- /* release the data */
- kfree(chip);
-
- return 0;
}
/* chip-specific constructor */
-static int __devinit snd_aw2_create(struct snd_card *card,
- struct pci_dev *pci, struct aw2 **rchip)
+static int snd_aw2_create(struct snd_card *card,
+ struct pci_dev *pci)
{
- struct aw2 *chip;
+ struct aw2 *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_aw2_dev_free,
- };
-
- *rchip = NULL;
/* initialize the PCI entry */
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
pci_set_master(pci);
/* check PCI availability (32bit DMA) */
- if ((pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0) ||
- (pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0)) {
- printk(KERN_ERR "aw2: Impossible to set 32bit mask DMA\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
+ dev_err(card->dev, "Impossible to set 32bit mask DMA\n");
return -ENXIO;
}
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
/* initialize the stuff */
chip->card = card;
@@ -294,62 +225,31 @@ static int __devinit snd_aw2_create(struct snd_card *card,
chip->irq = -1;
/* (1) PCI resource allocation */
- err = pci_request_regions(pci, "Audiowerk2");
- if (err < 0) {
- pci_disable_device(pci);
- kfree(chip);
- return err;
- }
+ chip->iobase_virt = pcim_iomap_region(pci, 0, "Audiowerk2");
+ if (IS_ERR(chip->iobase_virt))
+ return PTR_ERR(chip->iobase_virt);
chip->iobase_phys = pci_resource_start(pci, 0);
- chip->iobase_virt =
- ioremap_nocache(chip->iobase_phys,
- pci_resource_len(pci, 0));
-
- if (chip->iobase_virt == NULL) {
- printk(KERN_ERR "aw2: unable to remap memory region");
- pci_release_regions(pci);
- pci_disable_device(pci);
- kfree(chip);
- return -ENOMEM;
- }
/* (2) initialization of the chip hardware */
snd_aw2_saa7146_setup(&chip->saa7146, chip->iobase_virt);
- if (request_irq(pci->irq, snd_aw2_saa7146_interrupt,
- IRQF_SHARED, "Audiowerk2", chip)) {
- printk(KERN_ERR "aw2: Cannot grab irq %d\n", pci->irq);
-
- iounmap(chip->iobase_virt);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_aw2_saa7146_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "Cannot grab irq %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_aw2_free;
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0) {
- free_irq(chip->irq, (void *)chip);
- iounmap(chip->iobase_virt);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- return err;
- }
-
- snd_card_set_dev(card, &pci->dev);
- *rchip = chip;
-
- printk(KERN_INFO
- "Audiowerk 2 sound card (saa7146 chipset) detected and "
- "managed\n");
+ dev_info(card->dev,
+ "Audiowerk 2 sound card (saa7146 chipset) detected and managed\n");
return 0;
}
/* constructor */
-static int __devinit snd_aw2_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_aw2_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -365,24 +265,24 @@ static int __devinit snd_aw2_probe(struct pci_dev *pci,
}
/* (2) Create card instance */
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
/* (3) Create main component */
- err = snd_aw2_create(card, pci, &chip);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_aw2_create(card, pci);
+ if (err < 0)
+ goto error;
/* initialize mutex */
mutex_init(&chip->mtx);
/* init spinlock */
spin_lock_init(&chip->reg_lock);
/* (4) Define driver ID and name string */
- strcpy(card->driver, "aw2");
- strcpy(card->shortname, "Audiowerk2");
+ strscpy(card->driver, "aw2");
+ strscpy(card->shortname, "Audiowerk2");
sprintf(card->longname, "%s with SAA7146 irq %i",
card->shortname, chip->irq);
@@ -392,23 +292,18 @@ static int __devinit snd_aw2_probe(struct pci_dev *pci,
/* (6) Register card instance */
err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
+ if (err < 0)
+ goto error;
/* (7) Set PCI driver data */
pci_set_drvdata(pci, card);
dev++;
return 0;
-}
-/* destructor */
-static void __devexit snd_aw2_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ error:
+ snd_card_free(card);
+ return err;
}
/* open callback */
@@ -416,7 +311,7 @@ static int snd_aw2_pcm_playback_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- snd_printdd(KERN_DEBUG "aw2: Playback_open\n");
+ dev_dbg(substream->pcm->card->dev, "Playback_open\n");
runtime->hw = snd_aw2_playback_hw;
return 0;
}
@@ -432,7 +327,7 @@ static int snd_aw2_pcm_capture_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- snd_printdd(KERN_DEBUG "aw2: Capture_open\n");
+ dev_dbg(substream->pcm->card->dev, "Capture_open\n");
runtime->hw = snd_aw2_capture_hw;
return 0;
}
@@ -444,20 +339,6 @@ static int snd_aw2_pcm_capture_close(struct snd_pcm_substream *substream)
return 0;
}
- /* hw_params callback */
-static int snd_aw2_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-/* hw_free callback */
-static int snd_aw2_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
/* prepare callback for playback */
static int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream)
{
@@ -466,7 +347,7 @@ static int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned long period_size, buffer_size;
- mutex_lock(&chip->mtx);
+ guard(mutex)(&chip->mtx);
period_size = snd_pcm_lib_period_bytes(substream);
buffer_size = snd_pcm_lib_buffer_bytes(substream);
@@ -482,8 +363,6 @@ static int snd_aw2_pcm_prepare_playback(struct snd_pcm_substream *substream)
snd_pcm_period_elapsed,
(void *)substream);
- mutex_unlock(&chip->mtx);
-
return 0;
}
@@ -495,7 +374,7 @@ static int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned long period_size, buffer_size;
- mutex_lock(&chip->mtx);
+ guard(mutex)(&chip->mtx);
period_size = snd_pcm_lib_period_bytes(substream);
buffer_size = snd_pcm_lib_buffer_bytes(substream);
@@ -511,8 +390,6 @@ static int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream)
snd_pcm_period_elapsed,
(void *)substream);
- mutex_unlock(&chip->mtx);
-
return 0;
}
@@ -520,10 +397,10 @@ static int snd_aw2_pcm_prepare_capture(struct snd_pcm_substream *substream)
static int snd_aw2_pcm_trigger_playback(struct snd_pcm_substream *substream,
int cmd)
{
- int status = 0;
struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
struct aw2 *chip = pcm_device->chip;
- spin_lock(&chip->reg_lock);
+
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
snd_aw2_saa7146_pcm_trigger_start_playback(&chip->saa7146,
@@ -536,20 +413,19 @@ static int snd_aw2_pcm_trigger_playback(struct snd_pcm_substream *substream,
stream_number);
break;
default:
- status = -EINVAL;
+ return -EINVAL;
}
- spin_unlock(&chip->reg_lock);
- return status;
+ return 0;
}
/* capture trigger callback */
static int snd_aw2_pcm_trigger_capture(struct snd_pcm_substream *substream,
int cmd)
{
- int status = 0;
struct aw2_pcm_device *pcm_device = snd_pcm_substream_chip(substream);
struct aw2 *chip = pcm_device->chip;
- spin_lock(&chip->reg_lock);
+
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
snd_aw2_saa7146_pcm_trigger_start_capture(&chip->saa7146,
@@ -562,10 +438,9 @@ static int snd_aw2_pcm_trigger_capture(struct snd_pcm_substream *substream,
stream_number);
break;
default:
- status = -EINVAL;
+ return -EINVAL;
}
- spin_unlock(&chip->reg_lock);
- return status;
+ return 0;
}
/* playback pointer callback */
@@ -607,7 +482,7 @@ static snd_pcm_uframes_t snd_aw2_pcm_pointer_capture(struct snd_pcm_substream
}
/* create a pcm device */
-static int __devinit snd_aw2_new_pcm(struct aw2 *chip)
+static int snd_aw2_new_pcm(struct aw2 *chip)
{
struct snd_pcm *pcm_playback_ana;
struct snd_pcm *pcm_playback_num;
@@ -620,7 +495,7 @@ static int __devinit snd_aw2_new_pcm(struct aw2 *chip)
err = snd_pcm_new(chip->card, "Audiowerk2 analog playback", 0, 1, 0,
&pcm_playback_ana);
if (err < 0) {
- printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err);
+ dev_err(chip->card->dev, "snd_pcm_new error (0x%X)\n", err);
return err;
}
@@ -628,7 +503,7 @@ static int __devinit snd_aw2_new_pcm(struct aw2 *chip)
pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_ANA];
/* Set PCM device name */
- strcpy(pcm_playback_ana->name, "Analog playback");
+ strscpy(pcm_playback_ana->name, "Analog playback");
/* Associate private data to PCM device */
pcm_playback_ana->private_data = pcm_device;
/* set operators of PCM device */
@@ -644,27 +519,23 @@ static int __devinit snd_aw2_new_pcm(struct aw2 *chip)
/* pre-allocation of buffers */
/* Preallocate continuous pages. */
- err = snd_pcm_lib_preallocate_pages_for_all(pcm_playback_ana,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data
- (chip->pci),
- 64 * 1024, 64 * 1024);
- if (err)
- printk(KERN_ERR "aw2: snd_pcm_lib_preallocate_pages_for_all "
- "error (0x%X)\n", err);
+ snd_pcm_set_managed_buffer_all(pcm_playback_ana,
+ SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev,
+ 64 * 1024, 64 * 1024);
err = snd_pcm_new(chip->card, "Audiowerk2 digital playback", 1, 1, 0,
&pcm_playback_num);
if (err < 0) {
- printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err);
+ dev_err(chip->card->dev, "snd_pcm_new error (0x%X)\n", err);
return err;
}
/* Creation ok */
pcm_device = &chip->device_playback[NUM_STREAM_PLAYBACK_DIG];
/* Set PCM device name */
- strcpy(pcm_playback_num->name, "Digital playback");
+ strscpy(pcm_playback_num->name, "Digital playback");
/* Associate private data to PCM device */
pcm_playback_num->private_data = pcm_device;
/* set operators of PCM device */
@@ -680,23 +551,16 @@ static int __devinit snd_aw2_new_pcm(struct aw2 *chip)
/* pre-allocation of buffers */
/* Preallocate continuous pages. */
- err = snd_pcm_lib_preallocate_pages_for_all(pcm_playback_num,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data
- (chip->pci),
- 64 * 1024, 64 * 1024);
- if (err)
- printk(KERN_ERR
- "aw2: snd_pcm_lib_preallocate_pages_for_all error "
- "(0x%X)\n", err);
-
-
+ snd_pcm_set_managed_buffer_all(pcm_playback_num,
+ SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev,
+ 64 * 1024, 64 * 1024);
err = snd_pcm_new(chip->card, "Audiowerk2 capture", 2, 0, 1,
&pcm_capture);
if (err < 0) {
- printk(KERN_ERR "aw2: snd_pcm_new error (0x%X)\n", err);
+ dev_err(chip->card->dev, "snd_pcm_new error (0x%X)\n", err);
return err;
}
@@ -704,7 +568,7 @@ static int __devinit snd_aw2_new_pcm(struct aw2 *chip)
pcm_device = &chip->device_capture[NUM_STREAM_CAPTURE_ANA];
/* Set PCM device name */
- strcpy(pcm_capture->name, "Capture");
+ strscpy(pcm_capture->name, "Capture");
/* Associate private data to PCM device */
pcm_capture->private_data = pcm_device;
/* set operators of PCM device */
@@ -720,21 +584,15 @@ static int __devinit snd_aw2_new_pcm(struct aw2 *chip)
/* pre-allocation of buffers */
/* Preallocate continuous pages. */
- err = snd_pcm_lib_preallocate_pages_for_all(pcm_capture,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data
- (chip->pci),
- 64 * 1024, 64 * 1024);
- if (err)
- printk(KERN_ERR
- "aw2: snd_pcm_lib_preallocate_pages_for_all error "
- "(0x%X)\n", err);
-
+ snd_pcm_set_managed_buffer_all(pcm_capture,
+ SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev,
+ 64 * 1024, 64 * 1024);
/* Create control */
err = snd_ctl_add(chip->card, snd_ctl_new1(&aw2_control, chip));
if (err < 0) {
- printk(KERN_ERR "aw2: snd_ctl_add error (0x%X)\n", err);
+ dev_err(chip->card->dev, "snd_ctl_add error (0x%X)\n", err);
return err;
}
@@ -744,19 +602,10 @@ static int __devinit snd_aw2_new_pcm(struct aw2 *chip)
static int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = {
+ static const char * const texts[2] = {
"Analog", "Digital"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) {
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- }
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_aw2_control_switch_capture_get(struct snd_kcontrol *kcontrol,
diff --git a/sound/pci/aw2/aw2-saa7146.c b/sound/pci/aw2/aw2-saa7146.c
index 8afd8b5d1ac7..c84f1a45194f 100644
--- a/sound/pci/aw2/aw2-saa7146.c
+++ b/sound/pci/aw2/aw2-saa7146.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*****************************************************************************
*
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
@@ -5,20 +6,6 @@
*
* This file is part of the Audiowerk2 ALSA driver
*
- * The Audiowerk2 ALSA driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2.
- *
- * The Audiowerk2 ALSA driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Audiowerk2 ALSA driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- *
*****************************************************************************/
#define AW2_SAA7146_M
@@ -27,8 +14,7 @@
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
-#include <asm/system.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
@@ -205,8 +191,7 @@ void snd_aw2_saa7146_pcm_init_playback(struct snd_aw2_saa7146 *chip,
/* Define upper limit for DMA access */
WRITEREG(dma_addr + buffer_size, ProtA1_out);
} else {
- printk(KERN_ERR
- "aw2: snd_aw2_saa7146_pcm_init_playback: "
+ pr_err("aw2: snd_aw2_saa7146_pcm_init_playback: "
"Substream number is not 0 or 1 -> not managed\n");
}
}
@@ -252,8 +237,7 @@ void snd_aw2_saa7146_pcm_init_capture(struct snd_aw2_saa7146 *chip,
/* Define upper limit for DMA access */
WRITEREG(dma_addr + buffer_size, ProtA1_in);
} else {
- printk(KERN_ERR
- "aw2: snd_aw2_saa7146_pcm_init_capture: "
+ pr_err("aw2: snd_aw2_saa7146_pcm_init_capture: "
"Substream number is not 0 -> not managed\n");
}
}
@@ -346,7 +330,7 @@ void snd_aw2_saa7146_pcm_trigger_stop_capture(struct snd_aw2_saa7146 *chip,
irqreturn_t snd_aw2_saa7146_interrupt(int irq, void *dev_id)
{
unsigned int isr;
- unsigned int iicsta;
+ __always_unused unsigned int iicsta;
struct snd_aw2_saa7146 *chip = dev_id;
isr = READREG(ISR);
diff --git a/sound/pci/aw2/aw2-saa7146.h b/sound/pci/aw2/aw2-saa7146.h
index 5b35e358937f..3a3de56b9b07 100644
--- a/sound/pci/aw2/aw2-saa7146.h
+++ b/sound/pci/aw2/aw2-saa7146.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*****************************************************************************
*
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
@@ -5,20 +6,6 @@
*
* This file is part of the Audiowerk2 ALSA driver
*
- * The Audiowerk2 ALSA driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2.
- *
- * The Audiowerk2 ALSA driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Audiowerk2 ALSA driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- *
*****************************************************************************/
#ifndef AW2_SAA7146_H
@@ -32,11 +19,12 @@
#define NUM_STREAM_CAPTURE_ANA 0
-typedef void (*snd_aw2_saa7146_it_cb) (void *);
+struct snd_pcm_substream;
+typedef void (*snd_aw2_saa7146_it_cb) (struct snd_pcm_substream *);
struct snd_aw2_saa7146_cb_param {
snd_aw2_saa7146_it_cb p_it_callback;
- void *p_callback_param;
+ struct snd_pcm_substream *p_callback_param;
};
/* definition of the chip-specific record */
diff --git a/sound/pci/aw2/aw2-tsl.c b/sound/pci/aw2/aw2-tsl.c
index 459b0311ea31..2f35b08809a3 100644
--- a/sound/pci/aw2/aw2-tsl.c
+++ b/sound/pci/aw2/aw2-tsl.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*****************************************************************************
*
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
@@ -7,20 +8,6 @@
*
* This file is part of the Audiowerk2 ALSA driver
*
- * The Audiowerk2 ALSA driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2.
- *
- * The Audiowerk2 ALSA driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Audiowerk2 ALSA driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- *
*****************************************************************************/
#define TSL_WS0 (1UL << 31)
@@ -72,7 +59,7 @@
/* SD3: >-------<_4-L___>-------<_4-R___> */
/* WS4: -------\_______________/--------- */
-static int tsl1[8] = {
+static const int tsl1[8] = {
1 * TSL_SDW_A1 | 3 * TSL_BSEL_A1 |
0 * TSL_DIS_A1 | 0 * TSL_DOD_A1 | TSL_LF_A1,
@@ -98,7 +85,7 @@ static int tsl1[8] = {
0 * TSL_DOD_A1 | TSL_WS1 | TSL_WS0 | TSL_SF_A1 | TSL_EOS,
};
-static int tsl2[8] = {
+static const int tsl2[8] = {
0 * TSL_SDW_A2 | 3 * TSL_BSEL_A2 | 2 * TSL_DOD_A2 | TSL_LF_A2,
0 * TSL_SDW_A2 | 2 * TSL_BSEL_A2 | 2 * TSL_DOD_A2,
0 * TSL_SDW_A2 | 3 * TSL_BSEL_A2 | 2 * TSL_DOD_A2,
diff --git a/sound/pci/aw2/saa7146.h b/sound/pci/aw2/saa7146.h
index ce0ab5f9ee9c..90492a4c4db1 100644
--- a/sound/pci/aw2/saa7146.h
+++ b/sound/pci/aw2/saa7146.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*****************************************************************************
*
* Copyright (C) 2008 Cedric Bregardis <cedric.bregardis@free.fr> and
@@ -5,20 +6,6 @@
*
* This file is part of the Audiowerk2 ALSA driver
*
- * The Audiowerk2 ALSA driver is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2.
- *
- * The Audiowerk2 ALSA driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Audiowerk2 ALSA driver; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- *
*****************************************************************************/
/* SAA7146 registers */
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 4679ed83a43b..6cdf76e2b7d2 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -1,6 +1,6 @@
-/*
- * azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
- * Copyright (C) 2002, 2005 - 2009 by Andreas Mohr <andi AT lisas.de>
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
+ * Copyright (C) 2002, 2005 - 2011 by Andreas Mohr <andi AT lisas.de>
*
* Framework borrowed from Bart Hartgers's als4000.c.
* Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
@@ -17,21 +17,6 @@
* despite the high level of Internet ignorance - as usual :-P -
* about very good support for this card - on Linux!)
*
- * GPL LICENSE
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
* NOTES
* Since Aztech does not provide any chipset documentation,
* even on repeated request to various addresses,
@@ -66,6 +51,13 @@
* addresses illegally. So far unfortunately it looks like the very flexible
* ALSA AC97 support is still not enough to easily compensate for such a
* grave layout violation despite all tweaks and quirks mechanisms it offers.
+ * Well, not quite: now ac97 layer is much improved (bus-specific ops!),
+ * thus I was able to implement support - it's actually working quite well.
+ * An interesting item might be Aztech AMR 2800-W, since it's an AC97
+ * modem card which might reveal the Aztech-specific codec ID which
+ * we might want to pretend, too. Dito PCI168's brother, PCI368,
+ * where the advertising datasheet says it's AC97-based and has a
+ * Digital Enhanced Game Port.
* - builtin genuine OPL3 - verified to work fine, 20080506
* - full duplex 16bit playback/record at independent sampling rate
* - MPU401 (+ legacy address support, claimed by one official spec sheet)
@@ -134,7 +126,7 @@
* Possible remedies:
* - use speaker (amplifier) output instead of headphone output
* (in case crackling is due to overloaded output clipping)
- * - plug card into a different PCI slot, preferrably one that isn't shared
+ * - plug card into a different PCI slot, preferably one that isn't shared
* too much (this helps a lot, but not completely!)
* - get rid of PCI VGA card, use AGP instead
* - upgrade or downgrade BIOS
@@ -173,13 +165,14 @@
* - use MMIO (memory-mapped I/O)? Slightly faster access, e.g. for gameport.
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/init.h>
+#include <linux/bug.h> /* WARN_ONCE */
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/gameport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -188,27 +181,37 @@
#include <sound/mpu401.h>
#include <sound/opl3.h>
#include <sound/initval.h>
+/*
+ * Config switch, to use ALSA's AC97 layer instead of old custom mixer crap.
+ * If the AC97 compatibility parts we needed to implement locally turn out
+ * to work nicely, then remove the old implementation eventually.
+ */
+#define AZF_USE_AC97_LAYER 1
+
+#ifdef AZF_USE_AC97_LAYER
+#include <sound/ac97_codec.h>
+#endif
#include "azt3328.h"
MODULE_AUTHOR("Andreas Mohr <andi AT lisas.de>");
MODULE_DESCRIPTION("Aztech AZF3328 (PCI168)");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_GAMEPORT 1
#endif
/* === Debug settings ===
Further diagnostic functionality than the settings below
- does not need to be provided, since one can easily write a bash script
+ does not need to be provided, since one can easily write a POSIX shell script
to dump the card's I/O ports (those listed in lspci -v -v):
- function dump()
+ dump()
{
local descr=$1; local addr=$2; local count=$3
echo "${descr}: ${count} @ ${addr}:"
- dd if=/dev/port skip=$[${addr}] count=${count} bs=1 2>/dev/null| hexdump -C
+ dd if=/dev/port skip=`printf %d ${addr}` count=${count} bs=1 \
+ 2>/dev/null| hexdump -C
}
and then use something like
"dump joy200 0x200 8", "dump mpu388 0x388 4", "dump joy 0xb400 8",
@@ -216,65 +219,10 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
possibly within a "while true; do ... sleep 1; done" loop.
Tweaking ports could be done using
VALSTRING="`printf "%02x" $value`"
- printf "\x""$VALSTRING"|dd of=/dev/port seek=$[${addr}] bs=1 2>/dev/null
+ printf "\x""$VALSTRING"|dd of=/dev/port seek=`printf %d ${addr}` bs=1 \
+ 2>/dev/null
*/
-#define DEBUG_MISC 0
-#define DEBUG_CALLS 0
-#define DEBUG_MIXER 0
-#define DEBUG_CODEC 0
-#define DEBUG_IO 0
-#define DEBUG_TIMER 0
-#define DEBUG_GAME 0
-#define DEBUG_PM 0
-#define MIXER_TESTING 0
-
-#if DEBUG_MISC
-#define snd_azf3328_dbgmisc(format, args...) printk(KERN_DEBUG format, ##args)
-#else
-#define snd_azf3328_dbgmisc(format, args...)
-#endif
-
-#if DEBUG_CALLS
-#define snd_azf3328_dbgcalls(format, args...) printk(format, ##args)
-#define snd_azf3328_dbgcallenter() printk(KERN_DEBUG "--> %s\n", __func__)
-#define snd_azf3328_dbgcallleave() printk(KERN_DEBUG "<-- %s\n", __func__)
-#else
-#define snd_azf3328_dbgcalls(format, args...)
-#define snd_azf3328_dbgcallenter()
-#define snd_azf3328_dbgcallleave()
-#endif
-
-#if DEBUG_MIXER
-#define snd_azf3328_dbgmixer(format, args...) printk(KERN_DEBUG format, ##args)
-#else
-#define snd_azf3328_dbgmixer(format, args...)
-#endif
-
-#if DEBUG_CODEC
-#define snd_azf3328_dbgcodec(format, args...) printk(KERN_DEBUG format, ##args)
-#else
-#define snd_azf3328_dbgcodec(format, args...)
-#endif
-
-#if DEBUG_MISC
-#define snd_azf3328_dbgtimer(format, args...) printk(KERN_DEBUG format, ##args)
-#else
-#define snd_azf3328_dbgtimer(format, args...)
-#endif
-
-#if DEBUG_GAME
-#define snd_azf3328_dbggame(format, args...) printk(KERN_DEBUG format, ##args)
-#else
-#define snd_azf3328_dbggame(format, args...)
-#endif
-
-#if DEBUG_PM
-#define snd_azf3328_dbgpm(format, args...) printk(KERN_DEBUG format, ##args)
-#else
-#define snd_azf3328_dbgpm(format, args...)
-#endif
-
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard.");
@@ -283,7 +231,7 @@ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for AZF3328 soundcard.");
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable AZF3328 soundcard.");
@@ -291,19 +239,23 @@ static int seqtimer_scaling = 128;
module_param(seqtimer_scaling, int, 0444);
MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");
-struct snd_azf3328_codec_data {
- unsigned long io_base;
- struct snd_pcm_substream *substream;
- bool running;
- const char *name;
-};
-
enum snd_azf3328_codec_type {
+ /* warning: fixed indices (also used for bitmask checks!) */
AZF_CODEC_PLAYBACK = 0,
AZF_CODEC_CAPTURE = 1,
AZF_CODEC_I2S_OUT = 2,
};
+struct snd_azf3328_codec_data {
+ unsigned long io_base; /* keep first! (avoid offset calc) */
+ unsigned int dma_base; /* helper to avoid an indirection in hotpath */
+ spinlock_t *lock; /* TODO: convert to our own per-codec lock member */
+ struct snd_pcm_substream *substream;
+ bool running;
+ enum snd_azf3328_codec_type type;
+ const char *name;
+};
+
struct snd_azf3328 {
/* often-used fields towards beginning, then grouped */
@@ -322,6 +274,10 @@ struct snd_azf3328 {
/* playback, recording and I2S out codecs */
struct snd_azf3328_codec_data codecs[3];
+#ifdef AZF_USE_AC97_LAYER
+ struct snd_ac97 *ac97;
+#endif
+
struct snd_card *card;
struct snd_rawmidi *rmidi;
@@ -339,7 +295,6 @@ struct snd_azf3328 {
* CONFIG_PM register storage below, but that's slightly difficult. */
u16 shadow_reg_ctrl_6AH;
-#ifdef CONFIG_PM
/* register value containers for power management
* Note: not always full I/O range preserved (similar to Win driver!) */
u32 saved_regs_ctrl[AZF_ALIGN(AZF_IO_SIZE_CTRL_PM) / 4];
@@ -347,10 +302,9 @@ struct snd_azf3328 {
u32 saved_regs_mpu[AZF_ALIGN(AZF_IO_SIZE_MPU_PM) / 4];
u32 saved_regs_opl3[AZF_ALIGN(AZF_IO_SIZE_OPL3_PM) / 4];
u32 saved_regs_mixer[AZF_ALIGN(AZF_IO_SIZE_MIXER_PM) / 4];
-#endif
};
-static DEFINE_PCI_DEVICE_TABLE(snd_azf3328_ids) = {
+static const struct pci_device_id snd_azf3328_ids[] = {
{ 0x122D, 0x50DC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* PCI168/3328 */
{ 0x122D, 0x80DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* 3328 */
{ 0, }
@@ -362,6 +316,9 @@ MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
static int
snd_azf3328_io_reg_setb(unsigned reg, u8 mask, bool do_set)
{
+ /* Well, strictly spoken, the inb/outb sequence isn't atomic
+ and would need locking. However we currently don't care
+ since it potentially complicates matters. */
u8 prev = inb(reg), new;
new = (do_set) ? (prev|mask) : (prev & ~mask);
@@ -405,12 +362,18 @@ snd_azf3328_codec_inw(const struct snd_azf3328_codec_data *codec, unsigned reg)
}
static inline void
-snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec,
- unsigned reg,
- u32 value
+snd_azf3328_codec_outl_multi(const struct snd_azf3328_codec_data *codec,
+ unsigned reg, const void *buffer, int count
)
{
- outl(value, codec->io_base + reg);
+ unsigned long addr = codec->io_base + reg;
+ if (count) {
+ const u32 *buf = buffer;
+ do {
+ outl(*buf++, addr);
+ addr += 4;
+ } while (--count);
+ }
}
static inline u32
@@ -431,6 +394,12 @@ snd_azf3328_ctrl_inb(const struct snd_azf3328 *chip, unsigned reg)
return inb(chip->ctrl_io + reg);
}
+static inline u16
+snd_azf3328_ctrl_inw(const struct snd_azf3328 *chip, unsigned reg)
+{
+ return inw(chip->ctrl_io + reg);
+}
+
static inline void
snd_azf3328_ctrl_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
{
@@ -443,25 +412,25 @@ snd_azf3328_ctrl_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value)
outl(value, chip->ctrl_io + reg);
}
-static inline void
+static inline void __maybe_unused
snd_azf3328_game_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
{
outb(value, chip->game_io + reg);
}
-static inline void
+static inline void __maybe_unused
snd_azf3328_game_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
{
outw(value, chip->game_io + reg);
}
-static inline u8
+static inline u8 __maybe_unused
snd_azf3328_game_inb(const struct snd_azf3328 *chip, unsigned reg)
{
return inb(chip->game_io + reg);
}
-static inline u16
+static inline u16 __maybe_unused
snd_azf3328_game_inw(const struct snd_azf3328 *chip, unsigned reg)
{
return inw(chip->game_io + reg);
@@ -482,7 +451,7 @@ snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg)
#define AZF_MUTE_BIT 0x80
static bool
-snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip,
+snd_azf3328_mixer_mute_control(const struct snd_azf3328 *chip,
unsigned reg, bool do_mute
)
{
@@ -497,6 +466,321 @@ snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip,
return (do_mute) ? !updated : updated;
}
+static inline bool
+snd_azf3328_mixer_mute_control_master(const struct snd_azf3328 *chip,
+ bool do_mute
+)
+{
+ return snd_azf3328_mixer_mute_control(
+ chip,
+ IDX_MIXER_PLAY_MASTER,
+ do_mute
+ );
+}
+
+static inline bool
+snd_azf3328_mixer_mute_control_pcm(const struct snd_azf3328 *chip,
+ bool do_mute
+)
+{
+ return snd_azf3328_mixer_mute_control(
+ chip,
+ IDX_MIXER_WAVEOUT,
+ do_mute
+ );
+}
+
+static inline void
+snd_azf3328_mixer_reset(const struct snd_azf3328 *chip)
+{
+ /* reset (close) mixer:
+ * first mute master volume, then reset
+ */
+ snd_azf3328_mixer_mute_control_master(chip, 1);
+ snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
+}
+
+#ifdef AZF_USE_AC97_LAYER
+
+static inline void
+snd_azf3328_mixer_ac97_map_unsupported(const struct snd_azf3328 *chip,
+ unsigned short reg, const char *mode)
+{
+ /* need to add some more or less clever emulation? */
+ dev_warn(chip->card->dev,
+ "missing %s emulation for AC97 register 0x%02x!\n",
+ mode, reg);
+}
+
+/*
+ * Need to have _special_ AC97 mixer hardware register index mapper,
+ * to compensate for the issue of a rather AC97-incompatible hardware layout.
+ */
+#define AZF_REG_MASK 0x3f
+#define AZF_AC97_REG_UNSUPPORTED 0x8000
+#define AZF_AC97_REG_REAL_IO_READ 0x4000
+#define AZF_AC97_REG_REAL_IO_WRITE 0x2000
+#define AZF_AC97_REG_REAL_IO_RW \
+ (AZF_AC97_REG_REAL_IO_READ | AZF_AC97_REG_REAL_IO_WRITE)
+#define AZF_AC97_REG_EMU_IO_READ 0x0400
+#define AZF_AC97_REG_EMU_IO_WRITE 0x0200
+#define AZF_AC97_REG_EMU_IO_RW \
+ (AZF_AC97_REG_EMU_IO_READ | AZF_AC97_REG_EMU_IO_WRITE)
+static unsigned short
+snd_azf3328_mixer_ac97_map_reg_idx(unsigned short reg)
+{
+ static const struct {
+ unsigned short azf_reg;
+ } azf_reg_mapper[] = {
+ /* Especially when taking into consideration
+ * mono/stereo-based sequence of azf vs. AC97 control series,
+ * it's quite obvious that azf simply got rid
+ * of the AC97_HEADPHONE control at its intended offset,
+ * thus shifted _all_ controls by one,
+ * and _then_ simply added it as an FMSYNTH control at the end,
+ * to make up for the offset.
+ * This means we'll have to translate indices here as
+ * needed and then do some tiny AC97 patch action
+ * (snd_ac97_rename_vol_ctl() etc.) - that's it.
+ */
+ { /* AC97_RESET */ IDX_MIXER_RESET
+ | AZF_AC97_REG_REAL_IO_WRITE
+ | AZF_AC97_REG_EMU_IO_READ },
+ { /* AC97_MASTER */ IDX_MIXER_PLAY_MASTER },
+ /* note large shift: AC97_HEADPHONE to IDX_MIXER_FMSYNTH! */
+ { /* AC97_HEADPHONE */ IDX_MIXER_FMSYNTH },
+ { /* AC97_MASTER_MONO */ IDX_MIXER_MODEMOUT },
+ { /* AC97_MASTER_TONE */ IDX_MIXER_BASSTREBLE },
+ { /* AC97_PC_BEEP */ IDX_MIXER_PCBEEP },
+ { /* AC97_PHONE */ IDX_MIXER_MODEMIN },
+ { /* AC97_MIC */ IDX_MIXER_MIC },
+ { /* AC97_LINE */ IDX_MIXER_LINEIN },
+ { /* AC97_CD */ IDX_MIXER_CDAUDIO },
+ { /* AC97_VIDEO */ IDX_MIXER_VIDEO },
+ { /* AC97_AUX */ IDX_MIXER_AUX },
+ { /* AC97_PCM */ IDX_MIXER_WAVEOUT },
+ { /* AC97_REC_SEL */ IDX_MIXER_REC_SELECT },
+ { /* AC97_REC_GAIN */ IDX_MIXER_REC_VOLUME },
+ { /* AC97_REC_GAIN_MIC */ AZF_AC97_REG_EMU_IO_RW },
+ { /* AC97_GENERAL_PURPOSE */ IDX_MIXER_ADVCTL2 },
+ { /* AC97_3D_CONTROL */ IDX_MIXER_ADVCTL1 },
+ };
+
+ unsigned short reg_azf = AZF_AC97_REG_UNSUPPORTED;
+
+ /* azf3328 supports the low-numbered and low-spec:ed range
+ of AC97 regs only */
+ if (reg <= AC97_3D_CONTROL) {
+ unsigned short reg_idx = reg / 2;
+ reg_azf = azf_reg_mapper[reg_idx].azf_reg;
+ /* a translation-only entry means it's real read/write: */
+ if (!(reg_azf & ~AZF_REG_MASK))
+ reg_azf |= AZF_AC97_REG_REAL_IO_RW;
+ } else {
+ switch (reg) {
+ case AC97_POWERDOWN:
+ reg_azf = AZF_AC97_REG_EMU_IO_RW;
+ break;
+ case AC97_EXTENDED_ID:
+ reg_azf = AZF_AC97_REG_EMU_IO_READ;
+ break;
+ case AC97_EXTENDED_STATUS:
+ /* I don't know what the h*ll AC97 layer
+ * would consult this _extended_ register for
+ * given a base-AC97-advertised card,
+ * but let's just emulate it anyway :-P
+ */
+ reg_azf = AZF_AC97_REG_EMU_IO_RW;
+ break;
+ case AC97_VENDOR_ID1:
+ case AC97_VENDOR_ID2:
+ reg_azf = AZF_AC97_REG_EMU_IO_READ;
+ break;
+ }
+ }
+ return reg_azf;
+}
+
+static const unsigned short
+azf_emulated_ac97_caps =
+ AC97_BC_DEDICATED_MIC |
+ AC97_BC_BASS_TREBLE |
+ /* Headphone is an FM Synth control here */
+ AC97_BC_HEADPHONE |
+ /* no AC97_BC_LOUDNESS! */
+ /* mask 0x7c00 is
+ vendor-specific 3D enhancement
+ vendor indicator.
+ Since there actually _is_ an
+ entry for Aztech Labs
+ (13), make damn sure
+ to indicate it. */
+ (13 << 10);
+
+static const unsigned short
+azf_emulated_ac97_powerdown =
+ /* pretend everything to be active */
+ AC97_PD_ADC_STATUS |
+ AC97_PD_DAC_STATUS |
+ AC97_PD_MIXER_STATUS |
+ AC97_PD_VREF_STATUS;
+
+/*
+ * Emulated, _inofficial_ vendor ID
+ * (there might be some devices such as the MR 2800-W
+ * which could reveal the real Aztech AC97 ID).
+ * We choose to use "AZT" prefix, and then use 1 to indicate PCI168
+ * (better don't use 0x68 since there's a PCI368 as well).
+ */
+static const unsigned int
+azf_emulated_ac97_vendor_id = 0x415a5401;
+
+static unsigned short
+snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97)
+{
+ const struct snd_azf3328 *chip = ac97->private_data;
+ unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
+ unsigned short reg_val = 0;
+ bool unsupported = false;
+
+ dev_dbg(chip->card->dev, "snd_azf3328_mixer_ac97_read reg_ac97 %u\n",
+ reg_ac97);
+ if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
+ unsupported = true;
+ else {
+ if (reg_azf & AZF_AC97_REG_REAL_IO_READ)
+ reg_val = snd_azf3328_mixer_inw(chip,
+ reg_azf & AZF_REG_MASK);
+ else {
+ /*
+ * Proceed with dummy I/O read,
+ * to ensure compatible timing where this may matter.
+ * (ALSA AC97 layer usually doesn't call I/O functions
+ * due to intelligent I/O caching anyway)
+ * Choose a mixer register that's thoroughly unrelated
+ * to common audio (try to minimize distortion).
+ */
+ snd_azf3328_mixer_inw(chip, IDX_MIXER_SOMETHING30H);
+ }
+
+ if (reg_azf & AZF_AC97_REG_EMU_IO_READ) {
+ switch (reg_ac97) {
+ case AC97_RESET:
+ reg_val |= azf_emulated_ac97_caps;
+ break;
+ case AC97_POWERDOWN:
+ reg_val |= azf_emulated_ac97_powerdown;
+ break;
+ case AC97_EXTENDED_ID:
+ case AC97_EXTENDED_STATUS:
+ /* AFAICS we simply can't support anything: */
+ reg_val |= 0;
+ break;
+ case AC97_VENDOR_ID1:
+ reg_val = azf_emulated_ac97_vendor_id >> 16;
+ break;
+ case AC97_VENDOR_ID2:
+ reg_val = azf_emulated_ac97_vendor_id & 0xffff;
+ break;
+ default:
+ unsupported = true;
+ break;
+ }
+ }
+ }
+ if (unsupported)
+ snd_azf3328_mixer_ac97_map_unsupported(chip, reg_ac97, "read");
+
+ return reg_val;
+}
+
+static void
+snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97,
+ unsigned short reg_ac97, unsigned short val)
+{
+ const struct snd_azf3328 *chip = ac97->private_data;
+ unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97);
+ bool unsupported = false;
+
+ dev_dbg(chip->card->dev,
+ "snd_azf3328_mixer_ac97_write reg_ac97 %u val %u\n",
+ reg_ac97, val);
+ if (reg_azf & AZF_AC97_REG_UNSUPPORTED)
+ unsupported = true;
+ else {
+ if (reg_azf & AZF_AC97_REG_REAL_IO_WRITE)
+ snd_azf3328_mixer_outw(
+ chip,
+ reg_azf & AZF_REG_MASK,
+ val
+ );
+ else
+ if (reg_azf & AZF_AC97_REG_EMU_IO_WRITE) {
+ switch (reg_ac97) {
+ case AC97_REC_GAIN_MIC:
+ case AC97_POWERDOWN:
+ case AC97_EXTENDED_STATUS:
+ /*
+ * Silently swallow these writes.
+ * Since for most registers our card doesn't
+ * actually support a comparable feature,
+ * this is exactly what we should do here.
+ * The AC97 layer's I/O caching probably
+ * automatically takes care of all the rest...
+ * (remembers written values etc.)
+ */
+ break;
+ default:
+ unsupported = true;
+ break;
+ }
+ }
+ }
+ if (unsupported)
+ snd_azf3328_mixer_ac97_map_unsupported(chip, reg_ac97, "write");
+}
+
+static int
+snd_azf3328_mixer_new(struct snd_azf3328 *chip)
+{
+ struct snd_ac97_bus *bus;
+ struct snd_ac97_template ac97;
+ static const struct snd_ac97_bus_ops ops = {
+ .write = snd_azf3328_mixer_ac97_write,
+ .read = snd_azf3328_mixer_ac97_read,
+ };
+ int rc;
+
+ memset(&ac97, 0, sizeof(ac97));
+ ac97.scaps = AC97_SCAP_SKIP_MODEM
+ | AC97_SCAP_AUDIO /* we support audio! */
+ | AC97_SCAP_NO_SPDIF;
+ ac97.private_data = chip;
+ ac97.pci = chip->pci;
+
+ /*
+ * ALSA's AC97 layer has terrible init crackling issues,
+ * unfortunately, and since it makes use of AC97_RESET,
+ * there's no use trying to mute Master Playback proactively.
+ */
+
+ rc = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus);
+ if (!rc)
+ rc = snd_ac97_mixer(bus, &ac97, &chip->ac97);
+ /*
+ * Make sure to complain loudly in case of AC97 init failure,
+ * since failure may happen quite often,
+ * due to this card being a very quirky AC97 "lookalike".
+ */
+ if (rc)
+ dev_err(chip->card->dev, "AC97 init failed, err %d!\n", rc);
+
+ /* If we return an error here, then snd_card_free() should
+ * free up any ac97 codecs that got created, as well as the bus.
+ */
+ return rc;
+}
+#else /* AZF_USE_AC97_LAYER */
static void
snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip,
unsigned reg,
@@ -509,8 +793,6 @@ snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip,
unsigned char curr_vol_left = 0, curr_vol_right = 0;
int left_change = 0, right_change = 0;
- snd_azf3328_dbgcallenter();
-
if (chan_sel & SET_CHAN_LEFT) {
curr_vol_left = inb(portbase + 1);
@@ -551,7 +833,6 @@ snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip,
if (delay)
mdelay(delay);
} while ((left_change) || (right_change));
- snd_azf3328_dbgcallleave();
}
/*
@@ -629,14 +910,12 @@ snd_azf3328_info_mixer(struct snd_kcontrol *kcontrol,
{
struct azf3328_mixer_reg reg;
- snd_azf3328_dbgcallenter();
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
uinfo->type = reg.mask == 1 ?
SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = reg.stereo + 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = reg.mask;
- snd_azf3328_dbgcallleave();
return 0;
}
@@ -648,7 +927,6 @@ snd_azf3328_get_mixer(struct snd_kcontrol *kcontrol,
struct azf3328_mixer_reg reg;
u16 oreg, val;
- snd_azf3328_dbgcallenter();
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
oreg = snd_azf3328_mixer_inw(chip, reg.reg);
@@ -662,12 +940,11 @@ snd_azf3328_get_mixer(struct snd_kcontrol *kcontrol,
val = reg.mask - val;
ucontrol->value.integer.value[1] = val;
}
- snd_azf3328_dbgmixer("get: %02x is %04x -> vol %02lx|%02lx "
- "(shift %02d|%02d, mask %02x, inv. %d, stereo %d)\n",
+ dev_dbg(chip->card->dev,
+ "get: %02x is %04x -> vol %02lx|%02lx (shift %02d|%02d, mask %02x, inv. %d, stereo %d)\n",
reg.reg, oreg,
ucontrol->value.integer.value[0], ucontrol->value.integer.value[1],
reg.lchan_shift, reg.rchan_shift, reg.mask, reg.invert, reg.stereo);
- snd_azf3328_dbgcallleave();
return 0;
}
@@ -679,7 +956,6 @@ snd_azf3328_put_mixer(struct snd_kcontrol *kcontrol,
struct azf3328_mixer_reg reg;
u16 oreg, nreg, val;
- snd_azf3328_dbgcallenter();
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
oreg = snd_azf3328_mixer_inw(chip, reg.reg);
val = ucontrol->value.integer.value[0] & reg.mask;
@@ -703,12 +979,11 @@ snd_azf3328_put_mixer(struct snd_kcontrol *kcontrol,
else
snd_azf3328_mixer_outw(chip, reg.reg, nreg);
- snd_azf3328_dbgmixer("put: %02x to %02lx|%02lx, "
- "oreg %04x; shift %02d|%02d -> nreg %04x; after: %04x\n",
+ dev_dbg(chip->card->dev,
+ "put: %02x to %02lx|%02lx, oreg %04x; shift %02d|%02d -> nreg %04x; after: %04x\n",
reg.reg, ucontrol->value.integer.value[0], ucontrol->value.integer.value[1],
oreg, reg.lchan_shift, reg.rchan_shift,
nreg, snd_azf3328_mixer_inw(chip, reg.reg));
- snd_azf3328_dbgcallleave();
return (nreg != oreg);
}
@@ -733,11 +1008,6 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol,
const char * const *p = NULL;
snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1;
- uinfo->value.enumerated.items = reg.enum_c;
- if (uinfo->value.enumerated.item > reg.enum_c - 1U)
- uinfo->value.enumerated.item = reg.enum_c - 1U;
if (reg.reg == IDX_MIXER_ADVCTL2) {
switch(reg.lchan_shift) {
case 8: /* modem out sel */
@@ -750,12 +1020,12 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol,
p = texts4;
break;
}
- } else
- if (reg.reg == IDX_MIXER_REC_SELECT)
+ } else if (reg.reg == IDX_MIXER_REC_SELECT)
p = texts3;
- strcpy(uinfo->value.enumerated.name, p[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo,
+ (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1,
+ reg.enum_c, p);
}
static int
@@ -774,7 +1044,8 @@ snd_azf3328_get_mixer_enum(struct snd_kcontrol *kcontrol,
} else
ucontrol->value.enumerated.item[0] = (val >> reg.lchan_shift) & (reg.enum_c - 1);
- snd_azf3328_dbgmixer("get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)\n",
+ dev_dbg(chip->card->dev,
+ "get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)\n",
reg.reg, val, ucontrol->value.enumerated.item[0], ucontrol->value.enumerated.item[1],
reg.lchan_shift, reg.enum_c);
return 0;
@@ -806,11 +1077,12 @@ snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
snd_azf3328_mixer_outw(chip, reg.reg, val);
nreg = val;
- snd_azf3328_dbgmixer("put_enum: %02x to %04x, oreg %04x\n", reg.reg, val, oreg);
+ dev_dbg(chip->card->dev,
+ "put_enum: %02x to %04x, oreg %04x\n", reg.reg, val, oreg);
return (nreg != oreg);
}
-static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_azf3328_mixer_controls[] = {
AZF3328_MIXER_SWITCH("Master Playback Switch", IDX_MIXER_PLAY_MASTER, 15, 1),
AZF3328_MIXER_VOL_STEREO("Master Playback Volume", IDX_MIXER_PLAY_MASTER, 0x1f, 1),
AZF3328_MIXER_SWITCH("PCM Playback Switch", IDX_MIXER_WAVEOUT, 15, 1),
@@ -868,7 +1140,7 @@ static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = {
#endif
};
-static u16 __devinitdata snd_azf3328_init_values[][2] = {
+static const u16 snd_azf3328_init_values[][2] = {
{ IDX_MIXER_PLAY_MASTER, MIXER_MUTE_MASK|0x1f1f },
{ IDX_MIXER_MODEMOUT, MIXER_MUTE_MASK|0x1f1f },
{ IDX_MIXER_BASSTREBLE, 0x0000 },
@@ -884,7 +1156,7 @@ static u16 __devinitdata snd_azf3328_init_values[][2] = {
{ IDX_MIXER_REC_VOLUME, MIXER_MUTE_MASK|0x0707 },
};
-static int __devinit
+static int
snd_azf3328_mixer_new(struct snd_azf3328 *chip)
{
struct snd_card *card;
@@ -892,7 +1164,6 @@ snd_azf3328_mixer_new(struct snd_azf3328 *chip)
unsigned int idx;
int err;
- snd_azf3328_dbgcallenter();
if (snd_BUG_ON(!chip || !chip->card))
return -EINVAL;
@@ -912,69 +1183,47 @@ snd_azf3328_mixer_new(struct snd_azf3328 *chip)
sw = snd_azf3328_mixer_controls;
for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_mixer_controls);
++idx, ++sw) {
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(sw, chip))) < 0)
+ err = snd_ctl_add(chip->card, snd_ctl_new1(sw, chip));
+ if (err < 0)
return err;
}
snd_component_add(card, "AZF3328 mixer");
- strcpy(card->mixername, "AZF3328 mixer");
-
- snd_azf3328_dbgcallleave();
- return 0;
-}
+ strscpy(card->mixername, "AZF3328 mixer");
-static int
-snd_azf3328_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- int res;
- snd_azf3328_dbgcallenter();
- res = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
- snd_azf3328_dbgcallleave();
- return res;
-}
-
-static int
-snd_azf3328_hw_free(struct snd_pcm_substream *substream)
-{
- snd_azf3328_dbgcallenter();
- snd_pcm_lib_free_pages(substream);
- snd_azf3328_dbgcallleave();
return 0;
}
+#endif /* AZF_USE_AC97_LAYER */
static void
-snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
- enum snd_azf3328_codec_type codec_type,
+snd_azf3328_codec_setfmt(struct snd_azf3328_codec_data *codec,
enum azf_freq_t bitrate,
unsigned int format_width,
unsigned int channels
)
{
- unsigned long flags;
- const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
u16 val = 0xff00;
+ u8 freq = 0;
- snd_azf3328_dbgcallenter();
switch (bitrate) {
- case AZF_FREQ_4000: val |= SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
- case AZF_FREQ_4800: val |= SOUNDFORMAT_FREQ_SUSPECTED_4800; break;
+ case AZF_FREQ_4000: freq = SOUNDFORMAT_FREQ_SUSPECTED_4000; break;
+ case AZF_FREQ_4800: freq = SOUNDFORMAT_FREQ_SUSPECTED_4800; break;
case AZF_FREQ_5512:
/* the AZF3328 names it "5510" for some strange reason */
- val |= SOUNDFORMAT_FREQ_5510; break;
- case AZF_FREQ_6620: val |= SOUNDFORMAT_FREQ_6620; break;
- case AZF_FREQ_8000: val |= SOUNDFORMAT_FREQ_8000; break;
- case AZF_FREQ_9600: val |= SOUNDFORMAT_FREQ_9600; break;
- case AZF_FREQ_11025: val |= SOUNDFORMAT_FREQ_11025; break;
- case AZF_FREQ_13240: val |= SOUNDFORMAT_FREQ_SUSPECTED_13240; break;
- case AZF_FREQ_16000: val |= SOUNDFORMAT_FREQ_16000; break;
- case AZF_FREQ_22050: val |= SOUNDFORMAT_FREQ_22050; break;
- case AZF_FREQ_32000: val |= SOUNDFORMAT_FREQ_32000; break;
+ freq = SOUNDFORMAT_FREQ_5510; break;
+ case AZF_FREQ_6620: freq = SOUNDFORMAT_FREQ_6620; break;
+ case AZF_FREQ_8000: freq = SOUNDFORMAT_FREQ_8000; break;
+ case AZF_FREQ_9600: freq = SOUNDFORMAT_FREQ_9600; break;
+ case AZF_FREQ_11025: freq = SOUNDFORMAT_FREQ_11025; break;
+ case AZF_FREQ_13240: freq = SOUNDFORMAT_FREQ_SUSPECTED_13240; break;
+ case AZF_FREQ_16000: freq = SOUNDFORMAT_FREQ_16000; break;
+ case AZF_FREQ_22050: freq = SOUNDFORMAT_FREQ_22050; break;
+ case AZF_FREQ_32000: freq = SOUNDFORMAT_FREQ_32000; break;
default:
- snd_printk(KERN_WARNING "unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
- /* fall-through */
- case AZF_FREQ_44100: val |= SOUNDFORMAT_FREQ_44100; break;
- case AZF_FREQ_48000: val |= SOUNDFORMAT_FREQ_48000; break;
- case AZF_FREQ_66200: val |= SOUNDFORMAT_FREQ_SUSPECTED_66200; break;
+ pr_warn("azf3328: unknown bitrate %d, assuming 44.1kHz!\n", bitrate);
+ fallthrough;
+ case AZF_FREQ_44100: freq = SOUNDFORMAT_FREQ_44100; break;
+ case AZF_FREQ_48000: freq = SOUNDFORMAT_FREQ_48000; break;
+ case AZF_FREQ_66200: freq = SOUNDFORMAT_FREQ_SUSPECTED_66200; break;
}
/* val = 0xff07; 3m27.993s (65301Hz; -> 64000Hz???) hmm, 66120, 65967, 66123 */
/* val = 0xff09; 17m15.098s (13123,478Hz; -> 12000Hz???) hmm, 13237.2Hz? */
@@ -986,13 +1235,15 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
/* val = 0xff0d; 41m23.135s (5523,600Hz; -> 5512Hz???) */
/* val = 0xff0e; 28m30.777s (8017Hz; -> 8000Hz???) */
+ val |= freq;
+
if (channels == 2)
val |= SOUNDFORMAT_FLAG_2CHANNELS;
if (format_width == 16)
val |= SOUNDFORMAT_FLAG_16BIT;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(codec->lock);
/* set bitrate/format */
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val);
@@ -1004,7 +1255,8 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
* (FIXME: yes, it works, but what exactly am I doing here?? :)
* FIXME: does this have some side effects for full-duplex
* or other dramatic side effects? */
- if (codec_type == AZF_CODEC_PLAYBACK) /* only do it for playback */
+ /* do it for non-capture codecs only */
+ if (codec->type != AZF_CODEC_CAPTURE)
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) |
DMA_RUN_SOMETHING1 |
@@ -1013,21 +1265,17 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
DMA_EPILOGUE_SOMETHING |
DMA_SOMETHING_ELSE
);
-
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_azf3328_dbgcallleave();
}
static inline void
-snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328 *chip,
- enum snd_azf3328_codec_type codec_type
+snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328_codec_data *codec
)
{
/* choose lowest frequency for low power consumption.
* While this will cause louder noise due to rather coarse frequency,
* it should never matter since output should always
* get disabled properly when idle anyway. */
- snd_azf3328_codec_setfmt(chip, codec_type, AZF_FREQ_4000, 8, 1);
+ snd_azf3328_codec_setfmt(codec, AZF_FREQ_4000, 8, 1);
}
static void
@@ -1041,15 +1289,16 @@ snd_azf3328_ctrl_reg_6AH_update(struct snd_azf3328 *chip,
chip->shadow_reg_ctrl_6AH |= bitmask;
else
chip->shadow_reg_ctrl_6AH &= ~bitmask;
- snd_azf3328_dbgcodec("6AH_update mask 0x%04x do_mask %d: val 0x%04x\n",
- bitmask, do_mask, chip->shadow_reg_ctrl_6AH);
+ dev_dbg(chip->card->dev,
+ "6AH_update mask 0x%04x do_mask %d: val 0x%04x\n",
+ bitmask, do_mask, chip->shadow_reg_ctrl_6AH);
snd_azf3328_ctrl_outw(chip, IDX_IO_6AH, chip->shadow_reg_ctrl_6AH);
}
static inline void
snd_azf3328_ctrl_enable_codecs(struct snd_azf3328 *chip, bool enable)
{
- snd_azf3328_dbgcodec("codec_enable %d\n", enable);
+ dev_dbg(chip->card->dev, "codec_enable %d\n", enable);
/* no idea what exactly is being done here, but I strongly assume it's
* PM related */
snd_azf3328_ctrl_reg_6AH_update(
@@ -1066,7 +1315,7 @@ snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
bool need_change = (codec->running != enable);
- snd_azf3328_dbgcodec(
+ dev_dbg(chip->card->dev,
"codec_activity: %s codec, enable %d, need_change %d\n",
codec->name, enable, need_change
);
@@ -1094,219 +1343,228 @@ snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
.running)
&& (!chip->codecs[peer_codecs[codec_type].other2]
.running));
- }
- if (call_function)
+ }
+ if (call_function)
snd_azf3328_ctrl_enable_codecs(chip, enable);
/* ...and adjust clock, too
* (reduce noise and power consumption) */
if (!enable)
- snd_azf3328_codec_setfmt_lowpower(
- chip,
- codec_type
- );
+ snd_azf3328_codec_setfmt_lowpower(codec);
codec->running = enable;
}
}
static void
snd_azf3328_codec_setdmaa(struct snd_azf3328 *chip,
- enum snd_azf3328_codec_type codec_type,
- unsigned long addr,
- unsigned int count,
- unsigned int size
+ struct snd_azf3328_codec_data *codec,
+ unsigned long addr,
+ unsigned int period_bytes,
+ unsigned int buffer_bytes
)
{
- const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
- snd_azf3328_dbgcallenter();
+ WARN_ONCE(period_bytes & 1, "odd period length!?\n");
+ WARN_ONCE(buffer_bytes != 2 * period_bytes,
+ "missed our input expectations! %u vs. %u\n",
+ buffer_bytes, period_bytes);
if (!codec->running) {
/* AZF3328 uses a two buffer pointer DMA transfer approach */
- unsigned long flags, addr_area2;
-
/* width 32bit (prevent overflow): */
- u32 count_areas, lengths;
+ u32 area_length;
+ struct codec_setup_io {
+ u32 dma_start_1;
+ u32 dma_start_2;
+ u32 dma_lengths;
+ } __packed setup_io;
- count_areas = size/2;
- addr_area2 = addr+count_areas;
- count_areas--; /* max. index */
- snd_azf3328_dbgcodec("setdma: buffers %08lx[%u] / %08lx[%u]\n",
- addr, count_areas, addr_area2, count_areas);
+ area_length = buffer_bytes/2;
+
+ setup_io.dma_start_1 = addr;
+ setup_io.dma_start_2 = addr+area_length;
+
+ dev_dbg(chip->card->dev,
+ "setdma: buffers %08x[%u] / %08x[%u], %u, %u\n",
+ setup_io.dma_start_1, area_length,
+ setup_io.dma_start_2, area_length,
+ period_bytes, buffer_bytes);
+
+ /* Hmm, are we really supposed to decrement this by 1??
+ Most definitely certainly not: configuring full length does
+ work properly (i.e. likely better), and BTW we
+ violated possibly differing frame sizes with this...
+
+ area_length--; |* max. index *|
+ */
/* build combined I/O buffer length word */
- lengths = (count_areas << 16) | (count_areas);
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_1, addr);
- snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_2,
- addr_area2);
- snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_LENGTHS,
- lengths);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ setup_io.dma_lengths = (area_length << 16) | (area_length);
+
+ guard(spinlock_irqsave)(codec->lock);
+ snd_azf3328_codec_outl_multi(
+ codec, IDX_IO_CODEC_DMA_START_1, &setup_io, 3
+ );
}
- snd_azf3328_dbgcallleave();
}
static int
-snd_azf3328_codec_prepare(struct snd_pcm_substream *substream)
+snd_azf3328_pcm_prepare(struct snd_pcm_substream *substream)
{
-#if 0
- struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_azf3328_codec_data *codec = runtime->private_data;
+#if 0
unsigned int size = snd_pcm_lib_buffer_bytes(substream);
unsigned int count = snd_pcm_lib_period_bytes(substream);
#endif
- snd_azf3328_dbgcallenter();
+ codec->dma_base = runtime->dma_addr;
+
#if 0
- snd_azf3328_codec_setfmt(chip, AZF_CODEC_...,
+ snd_azf3328_codec_setfmt(codec,
runtime->rate,
snd_pcm_format_width(runtime->format),
runtime->channels);
- snd_azf3328_codec_setdmaa(chip, AZF_CODEC_...,
+ snd_azf3328_codec_setdmaa(chip, codec,
runtime->dma_addr, count, size);
#endif
- snd_azf3328_dbgcallleave();
return 0;
}
static int
-snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
- struct snd_pcm_substream *substream, int cmd)
+snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
- const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_azf3328_codec_data *codec = runtime->private_data;
int result = 0;
u16 flags1;
- bool previously_muted = 0;
- bool is_playback_codec = (AZF_CODEC_PLAYBACK == codec_type);
-
- snd_azf3328_dbgcalls("snd_azf3328_codec_trigger cmd %d\n", cmd);
+ bool previously_muted = false;
+ bool is_main_mixer_playback_codec = (AZF_CODEC_PLAYBACK == codec->type);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- snd_azf3328_dbgcodec("START %s\n", codec->name);
+ dev_dbg(chip->card->dev, "START PCM %s\n", codec->name);
- if (is_playback_codec) {
+ if (is_main_mixer_playback_codec) {
/* mute WaveOut (avoid clicking during setup) */
previously_muted =
- snd_azf3328_mixer_set_mute(
- chip, IDX_MIXER_WAVEOUT, 1
+ snd_azf3328_mixer_mute_control_pcm(
+ chip, 1
);
}
- snd_azf3328_codec_setfmt(chip, codec_type,
+ snd_azf3328_codec_setfmt(codec,
runtime->rate,
snd_pcm_format_width(runtime->format),
runtime->channels);
- spin_lock(&chip->reg_lock);
- /* first, remember current value: */
- flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
+ scoped_guard(spinlock, codec->lock) {
+ /* first, remember current value: */
+ flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
- /* stop transfer */
- flags1 &= ~DMA_RESUME;
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
+ /* stop transfer */
+ flags1 &= ~DMA_RESUME;
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
- /* FIXME: clear interrupts or what??? */
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
- spin_unlock(&chip->reg_lock);
+ /* FIXME: clear interrupts or what??? */
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
+ }
- snd_azf3328_codec_setdmaa(chip, codec_type, runtime->dma_addr,
+ snd_azf3328_codec_setdmaa(chip, codec, runtime->dma_addr,
snd_pcm_lib_period_bytes(substream),
snd_pcm_lib_buffer_bytes(substream)
);
- spin_lock(&chip->reg_lock);
+ scoped_guard(spinlock, codec->lock) {
#ifdef WIN9X
- /* FIXME: enable playback/recording??? */
- flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
-
- /* start transfer again */
- /* FIXME: what is this value (0x0010)??? */
- flags1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
+ /* FIXME: enable playback/recording??? */
+ flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
+
+ /* start transfer again */
+ /* FIXME: what is this value (0x0010)??? */
+ flags1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
#else /* NT4 */
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
- 0x0000);
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
- DMA_RUN_SOMETHING1);
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
- DMA_RUN_SOMETHING1 |
- DMA_RUN_SOMETHING2);
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
- DMA_RESUME |
- SOMETHING_ALMOST_ALWAYS_SET |
- DMA_EPILOGUE_SOMETHING |
- DMA_SOMETHING_ELSE);
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+ 0x0000);
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+ DMA_RUN_SOMETHING1);
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+ DMA_RUN_SOMETHING1 |
+ DMA_RUN_SOMETHING2);
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+ DMA_RESUME |
+ SOMETHING_ALMOST_ALWAYS_SET |
+ DMA_EPILOGUE_SOMETHING |
+ DMA_SOMETHING_ELSE);
#endif
- spin_unlock(&chip->reg_lock);
- snd_azf3328_ctrl_codec_activity(chip, codec_type, 1);
+ }
+ snd_azf3328_ctrl_codec_activity(chip, codec->type, 1);
- if (is_playback_codec) {
+ if (is_main_mixer_playback_codec) {
/* now unmute WaveOut */
if (!previously_muted)
- snd_azf3328_mixer_set_mute(
- chip, IDX_MIXER_WAVEOUT, 0
+ snd_azf3328_mixer_mute_control_pcm(
+ chip, 0
);
}
- snd_azf3328_dbgcodec("STARTED %s\n", codec->name);
+ dev_dbg(chip->card->dev, "PCM STARTED %s\n", codec->name);
break;
case SNDRV_PCM_TRIGGER_RESUME:
- snd_azf3328_dbgcodec("RESUME %s\n", codec->name);
+ dev_dbg(chip->card->dev, "PCM RESUME %s\n", codec->name);
/* resume codec if we were active */
- spin_lock(&chip->reg_lock);
- if (codec->running)
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
- snd_azf3328_codec_inw(
- codec, IDX_IO_CODEC_DMA_FLAGS
- ) | DMA_RESUME
- );
- spin_unlock(&chip->reg_lock);
+ scoped_guard(spinlock, codec->lock) {
+ if (codec->running)
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+ snd_azf3328_codec_inw(
+ codec, IDX_IO_CODEC_DMA_FLAGS
+ ) | DMA_RESUME
+ );
+ }
break;
case SNDRV_PCM_TRIGGER_STOP:
- snd_azf3328_dbgcodec("STOP %s\n", codec->name);
+ dev_dbg(chip->card->dev, "PCM STOP %s\n", codec->name);
- if (is_playback_codec) {
+ if (is_main_mixer_playback_codec) {
/* mute WaveOut (avoid clicking during setup) */
previously_muted =
- snd_azf3328_mixer_set_mute(
- chip, IDX_MIXER_WAVEOUT, 1
+ snd_azf3328_mixer_mute_control_pcm(
+ chip, 1
);
}
- spin_lock(&chip->reg_lock);
- /* first, remember current value: */
- flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
+ scoped_guard(spinlock, codec->lock) {
+ /* first, remember current value: */
+ flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
- /* stop transfer */
- flags1 &= ~DMA_RESUME;
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
+ /* stop transfer */
+ flags1 &= ~DMA_RESUME;
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
- /* hmm, is this really required? we're resetting the same bit
- * immediately thereafter... */
- flags1 |= DMA_RUN_SOMETHING1;
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
+ /* hmm, is this really required? we're resetting the same bit
+ * immediately thereafter... */
+ flags1 |= DMA_RUN_SOMETHING1;
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
- flags1 &= ~DMA_RUN_SOMETHING1;
- snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
- spin_unlock(&chip->reg_lock);
- snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
+ flags1 &= ~DMA_RUN_SOMETHING1;
+ snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
+ }
+ snd_azf3328_ctrl_codec_activity(chip, codec->type, 0);
- if (is_playback_codec) {
+ if (is_main_mixer_playback_codec) {
/* now unmute WaveOut */
if (!previously_muted)
- snd_azf3328_mixer_set_mute(
- chip, IDX_MIXER_WAVEOUT, 0
+ snd_azf3328_mixer_mute_control_pcm(
+ chip, 0
);
}
- snd_azf3328_dbgcodec("STOPPED %s\n", codec->name);
+ dev_dbg(chip->card->dev, "PCM STOPPED %s\n", codec->name);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
- snd_azf3328_dbgcodec("SUSPEND %s\n", codec->name);
+ dev_dbg(chip->card->dev, "PCM SUSPEND %s\n", codec->name);
/* make sure codec is stopped */
snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
snd_azf3328_codec_inw(
@@ -1315,81 +1573,42 @@ snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
+ WARN(1, "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
+ WARN(1, "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
break;
default:
- snd_printk(KERN_ERR "FIXME: unknown trigger mode!\n");
+ WARN(1, "FIXME: unknown trigger mode!\n");
return -EINVAL;
}
- snd_azf3328_dbgcallleave();
return result;
}
-static int
-snd_azf3328_codec_playback_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- return snd_azf3328_codec_trigger(AZF_CODEC_PLAYBACK, substream, cmd);
-}
-
-static int
-snd_azf3328_codec_capture_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- return snd_azf3328_codec_trigger(AZF_CODEC_CAPTURE, substream, cmd);
-}
-
-static int
-snd_azf3328_codec_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- return snd_azf3328_codec_trigger(AZF_CODEC_I2S_OUT, substream, cmd);
-}
-
static snd_pcm_uframes_t
-snd_azf3328_codec_pointer(struct snd_pcm_substream *substream,
- enum snd_azf3328_codec_type codec_type
+snd_azf3328_pcm_pointer(struct snd_pcm_substream *substream
)
{
- const struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
- const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
- unsigned long bufptr, result;
+ const struct snd_azf3328_codec_data *codec =
+ substream->runtime->private_data;
+ unsigned long result;
snd_pcm_uframes_t frmres;
-#ifdef QUERY_HARDWARE
- bufptr = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
-#else
- bufptr = substream->runtime->dma_addr;
-#endif
result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS);
/* calculate offset */
- result -= bufptr;
+#ifdef QUERY_HARDWARE
+ result -= snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
+#else
+ result -= codec->dma_base;
+#endif
frmres = bytes_to_frames( substream->runtime, result);
- snd_azf3328_dbgcodec("%s @ 0x%8lx, frames %8ld\n",
- codec->name, result, frmres);
+ dev_dbg(substream->pcm->card->dev, "%08li %s @ 0x%8lx, frames %8ld\n",
+ jiffies, codec->name, result, frmres);
return frmres;
}
-static snd_pcm_uframes_t
-snd_azf3328_codec_playback_pointer(struct snd_pcm_substream *substream)
-{
- return snd_azf3328_codec_pointer(substream, AZF_CODEC_PLAYBACK);
-}
-
-static snd_pcm_uframes_t
-snd_azf3328_codec_capture_pointer(struct snd_pcm_substream *substream)
-{
- return snd_azf3328_codec_pointer(substream, AZF_CODEC_CAPTURE);
-}
-
-static snd_pcm_uframes_t
-snd_azf3328_codec_i2s_out_pointer(struct snd_pcm_substream *substream)
-{
- return snd_azf3328_codec_pointer(substream, AZF_CODEC_I2S_OUT);
-}
-
/******************************************************************/
#ifdef SUPPORT_GAMEPORT
@@ -1449,7 +1668,7 @@ snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
* skeleton handler only
* (we do not want axis reading in interrupt handler - too much load!)
*/
- snd_azf3328_dbggame("gameport irq\n");
+ dev_dbg(chip->card->dev, "gameport irq\n");
/* this should ACK the gameport IRQ properly, hopefully. */
snd_azf3328_game_inw(chip, IDX_GAME_AXIS_VALUE);
@@ -1461,7 +1680,7 @@ snd_azf3328_gameport_open(struct gameport *gameport, int mode)
struct snd_azf3328 *chip = gameport_get_port_data(gameport);
int res;
- snd_azf3328_dbggame("gameport_open, mode %d\n", mode);
+ dev_dbg(chip->card->dev, "gameport_open, mode %d\n", mode);
switch (mode) {
case GAMEPORT_MODE_COOKED:
case GAMEPORT_MODE_RAW:
@@ -1484,7 +1703,7 @@ snd_azf3328_gameport_close(struct gameport *gameport)
{
struct snd_azf3328 *chip = gameport_get_port_data(gameport);
- snd_azf3328_dbggame("gameport_close\n");
+ dev_dbg(chip->card->dev, "gameport_close\n");
snd_azf3328_gameport_set_counter_frequency(chip,
GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
snd_azf3328_gameport_axis_circuit_enable(chip, 0);
@@ -1499,12 +1718,11 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
struct snd_azf3328 *chip = gameport_get_port_data(gameport);
int i;
u8 val;
- unsigned long flags;
if (snd_BUG_ON(!chip))
return 0;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
val = snd_azf3328_game_inb(chip, IDX_GAME_LEGACY_COMPATIBLE);
*buttons = (~(val) >> 4) & 0xf;
@@ -1531,7 +1749,7 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
}
}
- /* trigger next axes sampling, to be evaluated the next time we
+ /* trigger next sampling of axes, to be evaluated the next time we
* enter this function */
/* for some very, very strange reason we cannot enable
@@ -1541,7 +1759,6 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
for (i = 0; i < ARRAY_SIZE(chip->axes); i++) {
axes[i] = chip->axes[i];
@@ -1549,21 +1766,20 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
axes[i] = -1;
}
- snd_azf3328_dbggame("cooked_read: axes %d %d %d %d buttons %d\n",
- axes[0], axes[1], axes[2], axes[3], *buttons
- );
+ dev_dbg(chip->card->dev, "cooked_read: axes %d %d %d %d buttons %d\n",
+ axes[0], axes[1], axes[2], axes[3], *buttons);
return 0;
}
-static int __devinit
+static int
snd_azf3328_gameport(struct snd_azf3328 *chip, int dev)
{
struct gameport *gp;
chip->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "azt3328: cannot alloc memory for gameport\n");
+ dev_err(chip->card->dev, "cannot alloc memory for gameport\n");
return -ENOMEM;
}
@@ -1607,57 +1823,55 @@ snd_azf3328_gameport_free(struct snd_azf3328 *chip) { }
static inline void
snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
{
- printk(KERN_WARNING "huh, game port IRQ occurred!?\n");
+ dev_warn(chip->card->dev, "huh, game port IRQ occurred!?\n");
}
#endif /* SUPPORT_GAMEPORT */
/******************************************************************/
static inline void
-snd_azf3328_irq_log_unknown_type(u8 which)
+snd_azf3328_irq_log_unknown_type(struct snd_azf3328 *chip, u8 which)
{
- snd_azf3328_dbgcodec(
- "azt3328: unknown IRQ type (%x) occurred, please report!\n",
- which
- );
+ dev_dbg(chip->card->dev,
+ "unknown IRQ type (%x) occurred, please report!\n",
+ which);
}
static inline void
-snd_azf3328_codec_interrupt(struct snd_azf3328 *chip, u8 status)
+snd_azf3328_pcm_interrupt(struct snd_azf3328 *chip,
+ const struct snd_azf3328_codec_data *first_codec,
+ u8 status
+)
{
u8 which;
enum snd_azf3328_codec_type codec_type;
- const struct snd_azf3328_codec_data *codec;
+ const struct snd_azf3328_codec_data *codec = first_codec;
for (codec_type = AZF_CODEC_PLAYBACK;
codec_type <= AZF_CODEC_I2S_OUT;
- ++codec_type) {
+ ++codec_type, ++codec) {
/* skip codec if there's no interrupt for it */
if (!(status & (1 << codec_type)))
continue;
- codec = &chip->codecs[codec_type];
-
- spin_lock(&chip->reg_lock);
- which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE);
- /* ack all IRQ types immediately */
- snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which);
- spin_unlock(&chip->reg_lock);
+ scoped_guard(spinlock, codec->lock) {
+ which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE);
+ /* ack all IRQ types immediately */
+ snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which);
+ }
- if ((chip->pcm[codec_type]) && (codec->substream)) {
+ if (codec->substream) {
snd_pcm_period_elapsed(codec->substream);
- snd_azf3328_dbgcodec("%s period done (#%x), @ %x\n",
+ dev_dbg(chip->card->dev, "%s period done (#%x), @ %x\n",
codec->name,
which,
snd_azf3328_codec_inl(
- codec, IDX_IO_CODEC_DMA_CURRPOS
- )
- );
+ codec, IDX_IO_CODEC_DMA_CURRPOS));
} else
- printk(KERN_WARNING "azt3328: irq handler problem!\n");
+ dev_warn(chip->card->dev, "irq handler problem!\n");
if (which & IRQ_SOMETHING)
- snd_azf3328_irq_log_unknown_type(which);
+ snd_azf3328_irq_log_unknown_type(chip, which);
}
}
@@ -1666,9 +1880,7 @@ snd_azf3328_interrupt(int irq, void *dev_id)
{
struct snd_azf3328 *chip = dev_id;
u8 status;
-#if DEBUG_CODEC
static unsigned long irq_count;
-#endif
status = snd_azf3328_ctrl_inb(chip, IDX_IO_IRQSTATUS);
@@ -1679,28 +1891,27 @@ snd_azf3328_interrupt(int irq, void *dev_id)
))
return IRQ_NONE; /* must be interrupt for another device */
- snd_azf3328_dbgcodec(
+ dev_dbg(chip->card->dev,
"irq_count %ld! IDX_IO_IRQSTATUS %04x\n",
irq_count++ /* debug-only */,
- status
- );
+ status);
if (status & IRQ_TIMER) {
- /* snd_azf3328_dbgcodec("timer %ld\n",
+ /* dev_dbg(chip->card->dev, "timer %ld\n",
snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE)
& TIMER_VALUE_MASK
); */
if (chip->timer)
snd_timer_interrupt(chip->timer, chip->timer->sticks);
/* ACK timer */
- spin_lock(&chip->reg_lock);
- snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07);
- spin_unlock(&chip->reg_lock);
- snd_azf3328_dbgcodec("azt3328: timer IRQ\n");
+ scoped_guard(spinlock, &chip->reg_lock) {
+ snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07);
+ }
+ dev_dbg(chip->card->dev, "timer IRQ\n");
}
if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT))
- snd_azf3328_codec_interrupt(chip, status);
+ snd_azf3328_pcm_interrupt(chip, chip->codecs, status);
if (status & IRQ_GAMEPORT)
snd_azf3328_gameport_interrupt(chip);
@@ -1712,7 +1923,7 @@ snd_azf3328_interrupt(int irq, void *dev_id)
/* hmm, do we have to ack the IRQ here somehow?
* If so, then I don't know how yet... */
- snd_azf3328_dbgcodec("azt3328: MPU401 IRQ\n");
+ dev_dbg(chip->card->dev, "MPU401 IRQ\n");
}
return IRQ_HANDLED;
}
@@ -1740,11 +1951,15 @@ static const struct snd_pcm_hardware snd_azf3328_hardware =
.rate_max = AZF_FREQ_66200,
.channels_min = 1,
.channels_max = 2,
- .buffer_bytes_max = 65536,
- .period_bytes_min = 64,
- .period_bytes_max = 65536,
- .periods_min = 1,
- .periods_max = 1024,
+ .buffer_bytes_max = (64*1024),
+ .period_bytes_min = 1024,
+ .period_bytes_max = (32*1024),
+ /* We simply have two DMA areas (instead of a list of descriptors
+ such as other cards); I believe that this is a fixed hardware
+ attribute and there isn't much driver magic to be done to expand it.
+ Thus indicate that we have at least and at most 2 periods. */
+ .periods_min = 2,
+ .periods_max = 2,
/* FIXME: maybe that card actually has a FIFO?
* Hmm, it seems newer revisions do have one, but we still don't know
* its size... */
@@ -1752,7 +1967,7 @@ static const struct snd_pcm_hardware snd_azf3328_hardware =
};
-static unsigned int snd_azf3328_fixed_rates[] = {
+static const unsigned int snd_azf3328_fixed_rates[] = {
AZF_FREQ_4000,
AZF_FREQ_4800,
AZF_FREQ_5512,
@@ -1769,7 +1984,7 @@ static unsigned int snd_azf3328_fixed_rates[] = {
AZF_FREQ_66200
};
-static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = {
+static const struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = {
.count = ARRAY_SIZE(snd_azf3328_fixed_rates),
.list = snd_azf3328_fixed_rates,
.mask = 0,
@@ -1784,113 +1999,83 @@ snd_azf3328_pcm_open(struct snd_pcm_substream *substream,
{
struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
- snd_azf3328_dbgcallenter();
- chip->codecs[codec_type].substream = substream;
+ codec->substream = substream;
/* same parameters for all our codecs - at least we think so... */
runtime->hw = snd_azf3328_hardware;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&snd_azf3328_hw_constraints_rates);
- snd_azf3328_dbgcallleave();
+ runtime->private_data = codec;
return 0;
}
static int
-snd_azf3328_playback_open(struct snd_pcm_substream *substream)
+snd_azf3328_pcm_playback_open(struct snd_pcm_substream *substream)
{
return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK);
}
static int
-snd_azf3328_capture_open(struct snd_pcm_substream *substream)
+snd_azf3328_pcm_capture_open(struct snd_pcm_substream *substream)
{
return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE);
}
static int
-snd_azf3328_i2s_out_open(struct snd_pcm_substream *substream)
+snd_azf3328_pcm_i2s_out_open(struct snd_pcm_substream *substream)
{
return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT);
}
static int
-snd_azf3328_pcm_close(struct snd_pcm_substream *substream,
- enum snd_azf3328_codec_type codec_type
+snd_azf3328_pcm_close(struct snd_pcm_substream *substream
)
{
- struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
+ struct snd_azf3328_codec_data *codec =
+ substream->runtime->private_data;
- snd_azf3328_dbgcallenter();
- chip->codecs[codec_type].substream = NULL;
- snd_azf3328_dbgcallleave();
+ codec->substream = NULL;
return 0;
}
-static int
-snd_azf3328_playback_close(struct snd_pcm_substream *substream)
-{
- return snd_azf3328_pcm_close(substream, AZF_CODEC_PLAYBACK);
-}
-
-static int
-snd_azf3328_capture_close(struct snd_pcm_substream *substream)
-{
- return snd_azf3328_pcm_close(substream, AZF_CODEC_CAPTURE);
-}
-
-static int
-snd_azf3328_i2s_out_close(struct snd_pcm_substream *substream)
-{
- return snd_azf3328_pcm_close(substream, AZF_CODEC_I2S_OUT);
-}
-
/******************************************************************/
-static struct snd_pcm_ops snd_azf3328_playback_ops = {
- .open = snd_azf3328_playback_open,
- .close = snd_azf3328_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_azf3328_hw_params,
- .hw_free = snd_azf3328_hw_free,
- .prepare = snd_azf3328_codec_prepare,
- .trigger = snd_azf3328_codec_playback_trigger,
- .pointer = snd_azf3328_codec_playback_pointer
+static const struct snd_pcm_ops snd_azf3328_playback_ops = {
+ .open = snd_azf3328_pcm_playback_open,
+ .close = snd_azf3328_pcm_close,
+ .prepare = snd_azf3328_pcm_prepare,
+ .trigger = snd_azf3328_pcm_trigger,
+ .pointer = snd_azf3328_pcm_pointer
};
-static struct snd_pcm_ops snd_azf3328_capture_ops = {
- .open = snd_azf3328_capture_open,
- .close = snd_azf3328_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_azf3328_hw_params,
- .hw_free = snd_azf3328_hw_free,
- .prepare = snd_azf3328_codec_prepare,
- .trigger = snd_azf3328_codec_capture_trigger,
- .pointer = snd_azf3328_codec_capture_pointer
+static const struct snd_pcm_ops snd_azf3328_capture_ops = {
+ .open = snd_azf3328_pcm_capture_open,
+ .close = snd_azf3328_pcm_close,
+ .prepare = snd_azf3328_pcm_prepare,
+ .trigger = snd_azf3328_pcm_trigger,
+ .pointer = snd_azf3328_pcm_pointer
};
-static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
- .open = snd_azf3328_i2s_out_open,
- .close = snd_azf3328_i2s_out_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_azf3328_hw_params,
- .hw_free = snd_azf3328_hw_free,
- .prepare = snd_azf3328_codec_prepare,
- .trigger = snd_azf3328_codec_i2s_out_trigger,
- .pointer = snd_azf3328_codec_i2s_out_pointer
+static const struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
+ .open = snd_azf3328_pcm_i2s_out_open,
+ .close = snd_azf3328_pcm_close,
+ .prepare = snd_azf3328_pcm_prepare,
+ .trigger = snd_azf3328_pcm_trigger,
+ .pointer = snd_azf3328_pcm_pointer
};
-static int __devinit
+static int
snd_azf3328_pcm(struct snd_azf3328 *chip)
{
-enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */
+ /* pcm devices */
+ enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS };
struct snd_pcm *pcm;
int err;
- snd_azf3328_dbgcallenter();
-
err = snd_pcm_new(chip->card, "AZF3328 DSP", AZF_PCMDEV_STD,
1, 1, &pcm);
if (err < 0)
@@ -1902,14 +2087,13 @@ enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */
pcm->private_data = chip;
pcm->info_flags = 0;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
/* same pcm object for playback/capture (see snd_pcm_new() above) */
chip->pcm[AZF_CODEC_PLAYBACK] = pcm;
chip->pcm[AZF_CODEC_CAPTURE] = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- 64*1024, 64*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
+ 64*1024, 64*1024);
err = snd_pcm_new(chip->card, "AZF3328 I2S OUT", AZF_PCMDEV_I2S_OUT,
1, 0, &pcm);
@@ -1920,14 +2104,12 @@ enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */
pcm->private_data = chip;
pcm->info_flags = 0;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
chip->pcm[AZF_CODEC_I2S_OUT] = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- 64*1024, 64*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
+ 64*1024, 64*1024);
- snd_azf3328_dbgcallleave();
return 0;
}
@@ -1947,10 +2129,8 @@ static int
snd_azf3328_timer_start(struct snd_timer *timer)
{
struct snd_azf3328 *chip;
- unsigned long flags;
unsigned int delay;
- snd_azf3328_dbgcallenter();
chip = snd_timer_chip(timer);
delay = ((timer->sticks * seqtimer_scaling) - 1) & TIMER_VALUE_MASK;
if (delay < 49) {
@@ -1958,15 +2138,13 @@ snd_azf3328_timer_start(struct snd_timer *timer)
* this timing tweak
* (we need to do it to avoid a lockup, though) */
- snd_azf3328_dbgtimer("delay was too low (%d)!\n", delay);
+ dev_dbg(chip->card->dev, "delay was too low (%d)!\n", delay);
delay = 49; /* minimum time is 49 ticks */
}
- snd_azf3328_dbgtimer("setting timer countdown value %d, add COUNTDOWN|IRQ\n", delay);
+ dev_dbg(chip->card->dev, "setting timer countdown value %d\n", delay);
delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_azf3328_dbgcallleave();
return 0;
}
@@ -1974,16 +2152,17 @@ static int
snd_azf3328_timer_stop(struct snd_timer *timer)
{
struct snd_azf3328 *chip;
- unsigned long flags;
- snd_azf3328_dbgcallenter();
chip = snd_timer_chip(timer);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
/* disable timer countdown and interrupt */
- /* FIXME: should we write TIMER_IRQ_ACK here? */
- snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- snd_azf3328_dbgcallleave();
+ /* Hmm, should we write TIMER_IRQ_ACK here?
+ YES indeed, otherwise a rogue timer operation - which prompts
+ ALSA(?) to call repeated stop() in vain, but NOT start() -
+ will never end (value 0x03 is kept shown in control byte).
+ Simply manually poking 0x04 _once_ immediately successfully stops
+ the hardware/ALSA interrupt activity. */
+ snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x04);
return 0;
}
@@ -1992,10 +2171,8 @@ static int
snd_azf3328_timer_precise_resolution(struct snd_timer *timer,
unsigned long *num, unsigned long *den)
{
- snd_azf3328_dbgcallenter();
*num = 1;
*den = 1024000 / seqtimer_scaling;
- snd_azf3328_dbgcallleave();
return 0;
}
@@ -2008,14 +2185,13 @@ static struct snd_timer_hardware snd_azf3328_timer_hw = {
.precise_resolution = snd_azf3328_timer_precise_resolution,
};
-static int __devinit
+static int
snd_azf3328_timer(struct snd_azf3328 *chip, int device)
{
struct snd_timer *timer = NULL;
struct snd_timer_id tid;
int err;
- snd_azf3328_dbgcallenter();
tid.dev_class = SNDRV_TIMER_CLASS_CARD;
tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
tid.card = chip->card->number;
@@ -2029,7 +2205,7 @@ snd_azf3328_timer(struct snd_azf3328 *chip, int device)
if (err < 0)
goto out;
- strcpy(timer->name, "AZF3328 timer");
+ strscpy(timer->name, "AZF3328 timer");
timer->private_data = chip;
timer->hw = snd_azf3328_timer_hw;
@@ -2040,44 +2216,20 @@ snd_azf3328_timer(struct snd_azf3328 *chip, int device)
err = 0;
out:
- snd_azf3328_dbgcallleave();
return err;
}
/******************************************************************/
-static int
-snd_azf3328_free(struct snd_azf3328 *chip)
+static void
+snd_azf3328_free(struct snd_card *card)
{
- if (chip->irq < 0)
- goto __end_hw;
+ struct snd_azf3328 *chip = card->private_data;
- /* reset (close) mixer:
- * first mute master volume, then reset
- */
- snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
- snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000);
+ snd_azf3328_mixer_reset(chip);
snd_azf3328_timer_stop(chip->timer);
snd_azf3328_gameport_free(chip);
-
- if (chip->irq >= 0)
- synchronize_irq(chip->irq);
-__end_hw:
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
-
- kfree(chip);
- return 0;
-}
-
-static int
-snd_azf3328_dev_free(struct snd_device *device)
-{
- struct snd_azf3328 *chip = device->device_data;
- return snd_azf3328_free(chip);
}
#if 0
@@ -2106,34 +2258,34 @@ snd_azf3328_test_bit(unsigned unsigned reg, int bit)
static inline void
snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
{
-#if DEBUG_MISC
u16 tmp;
- snd_azf3328_dbgmisc(
+ dev_dbg(chip->card->dev,
"ctrl_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, "
"opl3_io 0x%lx, mixer_io 0x%lx, irq %d\n",
chip->ctrl_io, chip->game_io, chip->mpu_io,
- chip->opl3_io, chip->mixer_io, chip->irq
- );
+ chip->opl3_io, chip->mixer_io, chip->irq);
- snd_azf3328_dbgmisc("game %02x %02x %02x %02x %02x %02x\n",
+ dev_dbg(chip->card->dev,
+ "game %02x %02x %02x %02x %02x %02x\n",
snd_azf3328_game_inb(chip, 0),
snd_azf3328_game_inb(chip, 1),
snd_azf3328_game_inb(chip, 2),
snd_azf3328_game_inb(chip, 3),
snd_azf3328_game_inb(chip, 4),
- snd_azf3328_game_inb(chip, 5)
- );
+ snd_azf3328_game_inb(chip, 5));
for (tmp = 0; tmp < 0x07; tmp += 1)
- snd_azf3328_dbgmisc("mpu_io 0x%04x\n", inb(chip->mpu_io + tmp));
+ dev_dbg(chip->card->dev,
+ "mpu_io 0x%04x\n", inb(chip->mpu_io + tmp));
for (tmp = 0; tmp <= 0x07; tmp += 1)
- snd_azf3328_dbgmisc("0x%02x: game200 0x%04x, game208 0x%04x\n",
+ dev_dbg(chip->card->dev,
+ "0x%02x: game200 0x%04x, game208 0x%04x\n",
tmp, inb(0x200 + tmp), inb(0x208 + tmp));
for (tmp = 0; tmp <= 0x01; tmp += 1)
- snd_azf3328_dbgmisc(
+ dev_dbg(chip->card->dev,
"0x%02x: mpu300 0x%04x, mpu310 0x%04x, mpu320 0x%04x, "
"mpu330 0x%04x opl388 0x%04x opl38c 0x%04x\n",
tmp,
@@ -2142,64 +2294,50 @@ snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
inb(0x320 + tmp),
inb(0x330 + tmp),
inb(0x388 + tmp),
- inb(0x38c + tmp)
- );
+ inb(0x38c + tmp));
for (tmp = 0; tmp < AZF_IO_SIZE_CTRL; tmp += 2)
- snd_azf3328_dbgmisc("ctrl 0x%02x: 0x%04x\n",
- tmp, snd_azf3328_ctrl_inw(chip, tmp)
- );
+ dev_dbg(chip->card->dev,
+ "ctrl 0x%02x: 0x%04x\n",
+ tmp, snd_azf3328_ctrl_inw(chip, tmp));
for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2)
- snd_azf3328_dbgmisc("mixer 0x%02x: 0x%04x\n",
- tmp, snd_azf3328_mixer_inw(chip, tmp)
- );
-#endif /* DEBUG_MISC */
+ dev_dbg(chip->card->dev,
+ "mixer 0x%02x: 0x%04x\n",
+ tmp, snd_azf3328_mixer_inw(chip, tmp));
}
-static int __devinit
+static int
snd_azf3328_create(struct snd_card *card,
struct pci_dev *pci,
- unsigned long device_type,
- struct snd_azf3328 **rchip)
+ unsigned long device_type)
{
- struct snd_azf3328 *chip;
+ struct snd_azf3328 *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_azf3328_dev_free,
- };
u8 dma_init;
enum snd_azf3328_codec_type codec_type;
+ struct snd_azf3328_codec_data *codec_setup;
- *rchip = NULL;
-
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- err = -ENOMEM;
- goto out_err;
- }
spin_lock_init(&chip->reg_lock);
chip->card = card;
chip->pci = pci;
chip->irq = -1;
/* check if we can restrict PCI DMA transfers to 24 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) {
- snd_printk(KERN_ERR "architecture does not support "
- "24bit PCI busmaster DMA\n"
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(24))) {
+ dev_err(card->dev,
+ "architecture does not support 24bit PCI busmaster DMA\n"
);
- err = -ENXIO;
- goto out_err;
+ return -ENXIO;
}
- err = pci_request_regions(pci, "Aztech AZF3328");
+ err = pcim_request_all_regions(pci, "Aztech AZF3328");
if (err < 0)
- goto out_err;
+ return err;
chip->ctrl_io = pci_resource_start(pci, 0);
chip->game_io = pci_resource_start(pci, 1);
@@ -2207,36 +2345,40 @@ snd_azf3328_create(struct snd_card *card,
chip->opl3_io = pci_resource_start(pci, 3);
chip->mixer_io = pci_resource_start(pci, 4);
- chip->codecs[AZF_CODEC_PLAYBACK].io_base =
- chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
- chip->codecs[AZF_CODEC_PLAYBACK].name = "PLAYBACK";
- chip->codecs[AZF_CODEC_CAPTURE].io_base =
- chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
- chip->codecs[AZF_CODEC_CAPTURE].name = "CAPTURE";
- chip->codecs[AZF_CODEC_I2S_OUT].io_base =
- chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
- chip->codecs[AZF_CODEC_I2S_OUT].name = "I2S_OUT";
-
- if (request_irq(pci->irq, snd_azf3328_interrupt,
- IRQF_SHARED, card->shortname, chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- err = -EBUSY;
- goto out_err;
+ codec_setup = &chip->codecs[AZF_CODEC_PLAYBACK];
+ codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
+ codec_setup->lock = &chip->reg_lock;
+ codec_setup->type = AZF_CODEC_PLAYBACK;
+ codec_setup->name = "PLAYBACK";
+
+ codec_setup = &chip->codecs[AZF_CODEC_CAPTURE];
+ codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
+ codec_setup->lock = &chip->reg_lock;
+ codec_setup->type = AZF_CODEC_CAPTURE;
+ codec_setup->name = "CAPTURE";
+
+ codec_setup = &chip->codecs[AZF_CODEC_I2S_OUT];
+ codec_setup->io_base = chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
+ codec_setup->lock = &chip->reg_lock;
+ codec_setup->type = AZF_CODEC_I2S_OUT;
+ codec_setup->name = "I2S_OUT";
+
+ if (devm_request_irq(&pci->dev, pci->irq, snd_azf3328_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
+ return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_azf3328_free;
pci_set_master(pci);
- synchronize_irq(chip->irq);
snd_azf3328_debug_show_ports(chip);
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0)
- goto out_err;
-
/* create mixer interface & switches */
err = snd_azf3328_mixer_new(chip);
if (err < 0)
- goto out_err;
+ return err;
/* standard codec init stuff */
/* default DMA init value */
@@ -2247,35 +2389,21 @@ snd_azf3328_create(struct snd_card *card,
struct snd_azf3328_codec_data *codec =
&chip->codecs[codec_type];
- /* shutdown codecs to save power */
+ /* shutdown codecs to reduce power / noise */
/* have ...ctrl_codec_activity() act properly */
- codec->running = 1;
+ codec->running = true;
snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(codec->lock);
snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS,
dma_init);
- spin_unlock_irq(&chip->reg_lock);
}
- snd_card_set_dev(card, &pci->dev);
-
- *rchip = chip;
-
- err = 0;
- goto out;
-
-out_err:
- if (chip)
- snd_azf3328_free(chip);
- pci_disable_device(pci);
-
-out:
- return err;
+ return 0;
}
-static int __devinit
-snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+static int
+__snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -2283,7 +2411,6 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
struct snd_opl3 *opl3;
int err;
- snd_azf3328_dbgcallenter();
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
@@ -2291,240 +2418,214 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- strcpy(card->driver, "AZF3328");
- strcpy(card->shortname, "Aztech AZF3328 (PCI168)");
+ strscpy(card->driver, "AZF3328");
+ strscpy(card->shortname, "Aztech AZF3328 (PCI168)");
- err = snd_azf3328_create(card, pci, pci_id->driver_data, &chip);
+ err = snd_azf3328_create(card, pci, pci_id->driver_data);
if (err < 0)
- goto out_err;
-
- card->private_data = chip;
+ return err;
/* chose to use MPU401_HW_AZT2320 ID instead of MPU401_HW_MPU401,
since our hardware ought to be similar, thus use same ID. */
err = snd_mpu401_uart_new(
card, 0,
- MPU401_HW_AZT2320, chip->mpu_io, MPU401_INFO_INTEGRATED,
- pci->irq, 0, &chip->rmidi
+ MPU401_HW_AZT2320, chip->mpu_io,
+ MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
+ -1, &chip->rmidi
);
if (err < 0) {
- snd_printk(KERN_ERR "azf3328: no MPU-401 device at 0x%lx?\n",
+ dev_err(card->dev, "no MPU-401 device at 0x%lx?\n",
chip->mpu_io
);
- goto out_err;
+ return err;
}
err = snd_azf3328_timer(chip, 0);
if (err < 0)
- goto out_err;
+ return err;
err = snd_azf3328_pcm(chip);
if (err < 0)
- goto out_err;
+ return err;
if (snd_opl3_create(card, chip->opl3_io, chip->opl3_io+2,
OPL3_HW_AUTO, 1, &opl3) < 0) {
- snd_printk(KERN_ERR "azf3328: no OPL3 device at 0x%lx-0x%lx?\n",
+ dev_err(card->dev, "no OPL3 device at 0x%lx-0x%lx?\n",
chip->opl3_io, chip->opl3_io+2
);
} else {
/* need to use IDs 1, 2 since ID 0 is snd_azf3328_timer above */
err = snd_opl3_timer_new(opl3, 1, 2);
if (err < 0)
- goto out_err;
+ return err;
err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
if (err < 0)
- goto out_err;
+ return err;
+ opl3->private_data = chip;
}
- opl3->private_data = chip;
-
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, chip->ctrl_io, chip->irq);
err = snd_card_register(card);
if (err < 0)
- goto out_err;
+ return err;
#ifdef MODULE
- printk(KERN_INFO
-"azt3328: Sound driver for Aztech AZF3328-based soundcards such as PCI168.\n"
-"azt3328: Hardware was completely undocumented, unfortunately.\n"
-"azt3328: Feel free to contact andi AT lisas.de for bug reports etc.!\n"
-"azt3328: User-scalable sequencer timer set to %dHz (1024000Hz / %d).\n",
- 1024000 / seqtimer_scaling, seqtimer_scaling);
+ dev_info(card->dev,
+ "Sound driver for Aztech AZF3328-based soundcards such as PCI168.\n");
+ dev_info(card->dev,
+ "Hardware was completely undocumented, unfortunately.\n");
+ dev_info(card->dev,
+ "Feel free to contact andi AT lisas.de for bug reports etc.!\n");
+ dev_info(card->dev,
+ "User-scalable sequencer timer set to %dHz (1024000Hz / %d).\n",
+ 1024000 / seqtimer_scaling, seqtimer_scaling);
#endif
snd_azf3328_gameport(chip, dev);
pci_set_drvdata(pci, card);
dev++;
-
- err = 0;
- goto out;
-
-out_err:
- snd_printk(KERN_ERR "azf3328: something failed, exiting\n");
- snd_card_free(card);
-
-out:
- snd_azf3328_dbgcallleave();
- return err;
+ return 0;
}
-static void __devexit
-snd_azf3328_remove(struct pci_dev *pci)
+static int
+snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
- snd_azf3328_dbgcallenter();
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
- snd_azf3328_dbgcallleave();
+ return snd_card_free_on_error(&pci->dev, __snd_azf3328_probe(pci, pci_id));
}
-#ifdef CONFIG_PM
static inline void
-snd_azf3328_suspend_regs(unsigned long io_addr, unsigned count, u32 *saved_regs)
+snd_azf3328_suspend_regs(const struct snd_azf3328 *chip,
+ unsigned long io_addr, unsigned count, u32 *saved_regs)
{
unsigned reg;
for (reg = 0; reg < count; ++reg) {
*saved_regs = inl(io_addr);
- snd_azf3328_dbgpm("suspend: io 0x%04lx: 0x%08x\n",
+ dev_dbg(chip->card->dev, "suspend: io 0x%04lx: 0x%08x\n",
io_addr, *saved_regs);
++saved_regs;
io_addr += sizeof(*saved_regs);
}
}
+static inline void
+snd_azf3328_resume_regs(const struct snd_azf3328 *chip,
+ const u32 *saved_regs,
+ unsigned long io_addr,
+ unsigned count
+)
+{
+ unsigned reg;
+
+ for (reg = 0; reg < count; ++reg) {
+ outl(*saved_regs, io_addr);
+ dev_dbg(chip->card->dev,
+ "resume: io 0x%04lx: 0x%08x --> 0x%08x\n",
+ io_addr, *saved_regs, inl(io_addr));
+ ++saved_regs;
+ io_addr += sizeof(*saved_regs);
+ }
+}
+
+static inline void
+snd_azf3328_suspend_ac97(struct snd_azf3328 *chip)
+{
+#ifdef AZF_USE_AC97_LAYER
+ snd_ac97_suspend(chip->ac97);
+#else
+ snd_azf3328_suspend_regs(chip, chip->mixer_io,
+ ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer);
+
+ /* make sure to disable master volume etc. to prevent looping sound */
+ snd_azf3328_mixer_mute_control_master(chip, 1);
+ snd_azf3328_mixer_mute_control_pcm(chip, 1);
+#endif /* AZF_USE_AC97_LAYER */
+}
+
+static inline void
+snd_azf3328_resume_ac97(const struct snd_azf3328 *chip)
+{
+#ifdef AZF_USE_AC97_LAYER
+ snd_ac97_resume(chip->ac97);
+#else
+ snd_azf3328_resume_regs(chip, chip->saved_regs_mixer, chip->mixer_io,
+ ARRAY_SIZE(chip->saved_regs_mixer));
+
+ /* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02)
+ and IDX_MIXER_RESET (offset 0x00) get touched at the same time,
+ resulting in a mixer reset condition persisting until _after_
+ master vol was restored. Thus master vol needs an extra restore. */
+ outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2);
+#endif /* AZF_USE_AC97_LAYER */
+}
+
static int
-snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
+snd_azf3328_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_azf3328 *chip = card->private_data;
u16 *saved_regs_ctrl_u16;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
- snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);
+ snd_azf3328_suspend_ac97(chip);
- snd_azf3328_suspend_regs(chip->mixer_io,
- ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer);
-
- /* make sure to disable master volume etc. to prevent looping sound */
- snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
- snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
-
- snd_azf3328_suspend_regs(chip->ctrl_io,
+ snd_azf3328_suspend_regs(chip, chip->ctrl_io,
ARRAY_SIZE(chip->saved_regs_ctrl), chip->saved_regs_ctrl);
/* manually store the one currently relevant write-only reg, too */
saved_regs_ctrl_u16 = (u16 *)chip->saved_regs_ctrl;
saved_regs_ctrl_u16[IDX_IO_6AH / 2] = chip->shadow_reg_ctrl_6AH;
- snd_azf3328_suspend_regs(chip->game_io,
+ snd_azf3328_suspend_regs(chip, chip->game_io,
ARRAY_SIZE(chip->saved_regs_game), chip->saved_regs_game);
- snd_azf3328_suspend_regs(chip->mpu_io,
+ snd_azf3328_suspend_regs(chip, chip->mpu_io,
ARRAY_SIZE(chip->saved_regs_mpu), chip->saved_regs_mpu);
- snd_azf3328_suspend_regs(chip->opl3_io,
+ snd_azf3328_suspend_regs(chip, chip->opl3_io,
ARRAY_SIZE(chip->saved_regs_opl3), chip->saved_regs_opl3);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static inline void
-snd_azf3328_resume_regs(const u32 *saved_regs,
- unsigned long io_addr,
- unsigned count
-)
-{
- unsigned reg;
-
- for (reg = 0; reg < count; ++reg) {
- outl(*saved_regs, io_addr);
- snd_azf3328_dbgpm("resume: io 0x%04lx: 0x%08x --> 0x%08x\n",
- io_addr, *saved_regs, inl(io_addr));
- ++saved_regs;
- io_addr += sizeof(*saved_regs);
- }
-}
-
static int
-snd_azf3328_resume(struct pci_dev *pci)
+snd_azf3328_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
const struct snd_azf3328 *chip = card->private_data;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "azt3328: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
- snd_azf3328_resume_regs(chip->saved_regs_game, chip->game_io,
+ snd_azf3328_resume_regs(chip, chip->saved_regs_game, chip->game_io,
ARRAY_SIZE(chip->saved_regs_game));
- snd_azf3328_resume_regs(chip->saved_regs_mpu, chip->mpu_io,
+ snd_azf3328_resume_regs(chip, chip->saved_regs_mpu, chip->mpu_io,
ARRAY_SIZE(chip->saved_regs_mpu));
- snd_azf3328_resume_regs(chip->saved_regs_opl3, chip->opl3_io,
+ snd_azf3328_resume_regs(chip, chip->saved_regs_opl3, chip->opl3_io,
ARRAY_SIZE(chip->saved_regs_opl3));
- snd_azf3328_resume_regs(chip->saved_regs_mixer, chip->mixer_io,
- ARRAY_SIZE(chip->saved_regs_mixer));
-
- /* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02)
- and IDX_MIXER_RESET (offset 0x00) get touched at the same time,
- resulting in a mixer reset condition persisting until _after_
- master vol was restored. Thus master vol needs an extra restore. */
- outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2);
+ snd_azf3328_resume_ac97(chip);
- snd_azf3328_resume_regs(chip->saved_regs_ctrl, chip->ctrl_io,
+ snd_azf3328_resume_regs(chip, chip->saved_regs_ctrl, chip->ctrl_io,
ARRAY_SIZE(chip->saved_regs_ctrl));
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_azf3328_pm, snd_azf3328_suspend, snd_azf3328_resume);
-static struct pci_driver driver = {
- .name = "AZF3328",
+static struct pci_driver azf3328_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_azf3328_ids,
.probe = snd_azf3328_probe,
- .remove = __devexit_p(snd_azf3328_remove),
-#ifdef CONFIG_PM
- .suspend = snd_azf3328_suspend,
- .resume = snd_azf3328_resume,
-#endif
+ .driver = {
+ .pm = &snd_azf3328_pm,
+ },
};
-static int __init
-alsa_card_azf3328_init(void)
-{
- int err;
- snd_azf3328_dbgcallenter();
- err = pci_register_driver(&driver);
- snd_azf3328_dbgcallleave();
- return err;
-}
-
-static void __exit
-alsa_card_azf3328_exit(void)
-{
- snd_azf3328_dbgcallenter();
- pci_unregister_driver(&driver);
- snd_azf3328_dbgcallleave();
-}
-
-module_init(alsa_card_azf3328_init)
-module_exit(alsa_card_azf3328_exit)
+module_pci_driver(azf3328_driver);
diff --git a/sound/pci/azt3328.h b/sound/pci/azt3328.h
index 6f46b97650cc..6f9022784499 100644
--- a/sound/pci/azt3328.h
+++ b/sound/pci/azt3328.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SOUND_AZT3328_H
#define __SOUND_AZT3328_H
diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
index 37e1b5df5ab8..383def1f2af7 100644
--- a/sound/pci/bt87x.c
+++ b/sound/pci/bt87x.c
@@ -1,33 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* bt87x.c - Brooktree Bt878/Bt879 driver for ALSA
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
*
* based on btaudio.c by Gerd Knorr <kraxel@bytesex.org>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/bitops.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -37,14 +23,12 @@
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_DESCRIPTION("Brooktree Bt87x audio driver");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Brooktree,Bt878},"
- "{Brooktree,Bt879}}");
static int index[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -2}; /* Exclude the first card */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
static int digital_rate[SNDRV_CARDS]; /* digital input rate */
-static int load_all; /* allow to load the non-whitelisted cards */
+static bool load_all; /* allow to load cards not the allowlist */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Bt87x soundcard");
@@ -55,7 +39,7 @@ MODULE_PARM_DESC(enable, "Enable Bt87x soundcard");
module_param_array(digital_rate, int, NULL, 0444);
MODULE_PARM_DESC(digital_rate, "Digital input rate for Bt87x soundcard");
module_param(load_all, bool, 0444);
-MODULE_PARM_DESC(load_all, "Allow to load the non-whitelisted cards");
+MODULE_PARM_DESC(load_all, "Allow to load cards not on the allowlist");
/* register offsets */
@@ -164,7 +148,7 @@ struct snd_bt87x_board {
unsigned no_digital:1; /* No digital input */
};
-static __devinitdata struct snd_bt87x_board snd_bt87x_boards[] = {
+static const struct snd_bt87x_board snd_bt87x_boards[] = {
[SND_BT87X_BOARD_UNKNOWN] = {
.dig_rate = 32000, /* just a guess */
},
@@ -228,14 +212,14 @@ static int snd_bt87x_create_risc(struct snd_bt87x *chip, struct snd_pcm_substrea
unsigned int periods, unsigned int period_bytes)
{
unsigned int i, offset;
- u32 *risc;
+ __le32 *risc;
if (chip->dma_risc.area == NULL) {
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
PAGE_ALIGN(MAX_RISC_SIZE), &chip->dma_risc) < 0)
return -ENOMEM;
}
- risc = (u32 *)chip->dma_risc.area;
+ risc = (__le32 *)chip->dma_risc.area;
offset = 0;
*risc++ = cpu_to_le32(RISC_SYNC | RISC_SYNC_FM1);
*risc++ = cpu_to_le32(0);
@@ -285,25 +269,26 @@ static void snd_bt87x_free_risc(struct snd_bt87x *chip)
static void snd_bt87x_pci_error(struct snd_bt87x *chip, unsigned int status)
{
- u16 pci_status;
+ int pci_status = pci_status_get_and_clear_errors(chip->pci);
- pci_read_config_word(chip->pci, PCI_STATUS, &pci_status);
- pci_status &= PCI_STATUS_PARITY | PCI_STATUS_SIG_TARGET_ABORT |
- PCI_STATUS_REC_TARGET_ABORT | PCI_STATUS_REC_MASTER_ABORT |
- PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY;
- pci_write_config_word(chip->pci, PCI_STATUS, pci_status);
if (pci_status != PCI_STATUS_DETECTED_PARITY)
- snd_printk(KERN_ERR "Aieee - PCI error! status %#08x, PCI status %#04x\n",
+ dev_err(chip->card->dev,
+ "Aieee - PCI error! status %#08x, PCI status %#04x\n",
status & ERROR_INTERRUPTS, pci_status);
else {
- snd_printk(KERN_ERR "Aieee - PCI parity error detected!\n");
+ dev_err(chip->card->dev,
+ "Aieee - PCI parity error detected!\n");
/* error 'handling' similar to aic7xxx_pci.c: */
chip->pci_parity_errors++;
if (chip->pci_parity_errors > 20) {
- snd_printk(KERN_ERR "Too many PCI parity errors observed.\n");
- snd_printk(KERN_ERR "Some device on this bus is generating bad parity.\n");
- snd_printk(KERN_ERR "This is an error *observed by*, not *generated by*, this card.\n");
- snd_printk(KERN_ERR "PCI parity error checking has been disabled.\n");
+ dev_err(chip->card->dev,
+ "Too many PCI parity errors observed.\n");
+ dev_err(chip->card->dev,
+ "Some device on this bus is generating bad parity.\n");
+ dev_err(chip->card->dev,
+ "This is an error *observed by*, not *generated by*, this card.\n");
+ dev_err(chip->card->dev,
+ "PCI parity error checking has been disabled.\n");
chip->interrupt_mask &= ~(INT_PPERR | INT_RIPERR);
snd_bt87x_writel(chip, REG_INT_MASK, chip->interrupt_mask);
}
@@ -323,9 +308,11 @@ static irqreturn_t snd_bt87x_interrupt(int irq, void *dev_id)
if (irq_status & ERROR_INTERRUPTS) {
if (irq_status & (INT_FBUS | INT_FTRGT))
- snd_printk(KERN_WARNING "FIFO overrun, status %#08x\n", status);
+ dev_warn(chip->card->dev,
+ "FIFO overrun, status %#08x\n", status);
if (irq_status & INT_OCERR)
- snd_printk(KERN_ERR "internal RISC error, status %#08x\n", status);
+ dev_err(chip->card->dev,
+ "internal RISC error, status %#08x\n", status);
if (irq_status & (INT_PPERR | INT_RIPERR | INT_PABORT))
snd_bt87x_pci_error(chip, irq_status);
}
@@ -338,14 +325,15 @@ static irqreturn_t snd_bt87x_interrupt(int irq, void *dev_id)
current_block = chip->current_line * 16 / chip->lines;
irq_block = status >> INT_RISCS_SHIFT;
if (current_block != irq_block)
- chip->current_line = (irq_block * chip->lines + 15) / 16;
+ chip->current_line = DIV_ROUND_UP(irq_block * chip->lines,
+ 16);
snd_pcm_period_elapsed(chip->substream);
}
return IRQ_HANDLED;
}
-static struct snd_pcm_hardware snd_bt87x_digital_hw = {
+static const struct snd_pcm_hardware snd_bt87x_digital_hw = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -362,7 +350,7 @@ static struct snd_pcm_hardware snd_bt87x_digital_hw = {
.periods_max = 255,
};
-static struct snd_pcm_hardware snd_bt87x_analog_hw = {
+static const struct snd_pcm_hardware snd_bt87x_analog_hw = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -393,13 +381,13 @@ static int snd_bt87x_set_digital_hw(struct snd_bt87x *chip, struct snd_pcm_runti
static int snd_bt87x_set_analog_hw(struct snd_bt87x *chip, struct snd_pcm_runtime *runtime)
{
- static struct snd_ratnum analog_clock = {
+ static const struct snd_ratnum analog_clock = {
.num = ANALOG_CLOCK,
.den_min = CLOCK_DIV_MIN,
.den_max = CLOCK_DIV_MAX,
.den_step = 1
};
- static struct snd_pcm_hw_constraint_ratnums constraint_rates = {
+ static const struct snd_pcm_hw_constraint_ratnums constraint_rates = {
.nrats = 1,
.rats = &analog_clock
};
@@ -435,7 +423,7 @@ static int snd_bt87x_pcm_open(struct snd_pcm_substream *substream)
_error:
clear_bit(0, &chip->opened);
- smp_mb__after_clear_bit();
+ smp_mb__after_atomic();
return err;
}
@@ -443,14 +431,14 @@ static int snd_bt87x_close(struct snd_pcm_substream *substream)
{
struct snd_bt87x *chip = snd_pcm_substream_chip(substream);
- spin_lock_irq(&chip->reg_lock);
- chip->reg_control |= CTL_A_PWRDN;
- snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ chip->reg_control |= CTL_A_PWRDN;
+ snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
+ }
chip->substream = NULL;
clear_bit(0, &chip->opened);
- smp_mb__after_clear_bit();
+ smp_mb__after_atomic();
return 0;
}
@@ -458,12 +446,7 @@ static int snd_bt87x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_bt87x *chip = snd_pcm_substream_chip(substream);
- int err;
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
return snd_bt87x_create_risc(chip, substream,
params_periods(hw_params),
params_period_bytes(hw_params));
@@ -474,7 +457,6 @@ static int snd_bt87x_hw_free(struct snd_pcm_substream *substream)
struct snd_bt87x *chip = snd_pcm_substream_chip(substream);
snd_bt87x_free_risc(chip);
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -484,20 +466,19 @@ static int snd_bt87x_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int decimation;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->reg_control &= ~(CTL_DA_SDR_MASK | CTL_DA_SBR);
decimation = (ANALOG_CLOCK + runtime->rate / 4) / runtime->rate;
chip->reg_control |= decimation << CTL_DA_SDR_SHIFT;
if (runtime->format == SNDRV_PCM_FORMAT_S8)
chip->reg_control |= CTL_DA_SBR;
snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
static int snd_bt87x_start(struct snd_bt87x *chip)
{
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
chip->current_line = 0;
chip->reg_control |= CTL_FIFO_ENABLE | CTL_RISC_ENABLE | CTL_ACAP_EN;
snd_bt87x_writel(chip, REG_RISC_STRT_ADD, chip->dma_risc.addr);
@@ -505,18 +486,16 @@ static int snd_bt87x_start(struct snd_bt87x *chip)
chip->line_bytes | (chip->lines << 16));
snd_bt87x_writel(chip, REG_INT_MASK, chip->interrupt_mask);
snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
- spin_unlock(&chip->reg_lock);
return 0;
}
static int snd_bt87x_stop(struct snd_bt87x *chip)
{
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
chip->reg_control &= ~(CTL_FIFO_ENABLE | CTL_RISC_ENABLE | CTL_ACAP_EN);
snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
snd_bt87x_writel(chip, REG_INT_MASK, 0);
snd_bt87x_writel(chip, REG_INT_STAT, MY_INTERRUPTS);
- spin_unlock(&chip->reg_lock);
return 0;
}
@@ -542,16 +521,14 @@ static snd_pcm_uframes_t snd_bt87x_pointer(struct snd_pcm_substream *substream)
return (snd_pcm_uframes_t)bytes_to_frames(runtime, chip->current_line * chip->line_bytes);
}
-static struct snd_pcm_ops snd_bt87x_pcm_ops = {
+static const struct snd_pcm_ops snd_bt87x_pcm_ops = {
.open = snd_bt87x_pcm_open,
.close = snd_bt87x_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_bt87x_hw_params,
.hw_free = snd_bt87x_hw_free,
.prepare = snd_bt87x_prepare,
.trigger = snd_bt87x_trigger,
.pointer = snd_bt87x_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
static int snd_bt87x_capture_volume_info(struct snd_kcontrol *kcontrol,
@@ -580,17 +557,16 @@ static int snd_bt87x_capture_volume_put(struct snd_kcontrol *kcontrol,
u32 old_control;
int changed;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
old_control = chip->reg_control;
chip->reg_control = (chip->reg_control & ~CTL_A_GAIN_MASK)
| (value->value.integer.value[0] << CTL_A_GAIN_SHIFT);
snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
changed = old_control != chip->reg_control;
- spin_unlock_irq(&chip->reg_lock);
return changed;
}
-static struct snd_kcontrol_new snd_bt87x_capture_volume = {
+static const struct snd_kcontrol_new snd_bt87x_capture_volume = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Volume",
.info = snd_bt87x_capture_volume_info,
@@ -616,17 +592,16 @@ static int snd_bt87x_capture_boost_put(struct snd_kcontrol *kcontrol,
u32 old_control;
int changed;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
old_control = chip->reg_control;
chip->reg_control = (chip->reg_control & ~CTL_A_G2X)
| (value->value.integer.value[0] ? CTL_A_G2X : 0);
snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
changed = chip->reg_control != old_control;
- spin_unlock_irq(&chip->reg_lock);
return changed;
}
-static struct snd_kcontrol_new snd_bt87x_capture_boost = {
+static const struct snd_kcontrol_new snd_bt87x_capture_boost = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Boost",
.info = snd_bt87x_capture_boost_info,
@@ -637,15 +612,9 @@ static struct snd_kcontrol_new snd_bt87x_capture_boost = {
static int snd_bt87x_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *info)
{
- static char *texts[3] = {"TV Tuner", "FM", "Mic/Line"};
+ static const char *const texts[3] = {"TV Tuner", "FM", "Mic/Line"};
- info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- info->count = 1;
- info->value.enumerated.items = 3;
- if (info->value.enumerated.item > 2)
- info->value.enumerated.item = 2;
- strcpy(info->value.enumerated.name, texts[info->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(info, 1, 3, texts);
}
static int snd_bt87x_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -664,17 +633,16 @@ static int snd_bt87x_capture_source_put(struct snd_kcontrol *kcontrol,
u32 old_control;
int changed;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
old_control = chip->reg_control;
chip->reg_control = (chip->reg_control & ~CTL_A_SEL_MASK)
| (value->value.enumerated.item[0] << CTL_A_SEL_SHIFT);
snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control);
changed = chip->reg_control != old_control;
- spin_unlock_irq(&chip->reg_lock);
return changed;
}
-static struct snd_kcontrol_new snd_bt87x_capture_source = {
+static const struct snd_kcontrol_new snd_bt87x_capture_source = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.info = snd_bt87x_capture_source_info,
@@ -682,27 +650,14 @@ static struct snd_kcontrol_new snd_bt87x_capture_source = {
.put = snd_bt87x_capture_source_put,
};
-static int snd_bt87x_free(struct snd_bt87x *chip)
-{
- if (chip->mmio)
- snd_bt87x_stop(chip);
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- if (chip->mmio)
- iounmap(chip->mmio);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
-}
-
-static int snd_bt87x_dev_free(struct snd_device *device)
+static void snd_bt87x_free(struct snd_card *card)
{
- struct snd_bt87x *chip = device->device_data;
- return snd_bt87x_free(chip);
+ struct snd_bt87x *chip = card->private_data;
+
+ snd_bt87x_stop(chip);
}
-static int __devinit snd_bt87x_pcm(struct snd_bt87x *chip, int device, char *name)
+static int snd_bt87x_pcm(struct snd_bt87x *chip, int device, char *name)
{
int err;
struct snd_pcm *pcm;
@@ -711,52 +666,33 @@ static int __devinit snd_bt87x_pcm(struct snd_bt87x *chip, int device, char *nam
if (err < 0)
return err;
pcm->private_data = chip;
- strcpy(pcm->name, name);
+ strscpy(pcm->name, name);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_bt87x_pcm_ops);
- return snd_pcm_lib_preallocate_pages_for_all(pcm,
- SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci),
- 128 * 1024,
- ALIGN(255 * 4092, 1024));
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &chip->pci->dev,
+ 128 * 1024,
+ ALIGN(255 * 4092, 1024));
+ return 0;
}
-static int __devinit snd_bt87x_create(struct snd_card *card,
- struct pci_dev *pci,
- struct snd_bt87x **rchip)
+static int snd_bt87x_create(struct snd_card *card,
+ struct pci_dev *pci)
{
- struct snd_bt87x *chip;
+ struct snd_bt87x *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_bt87x_dev_free
- };
- *rchip = NULL;
-
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (!chip) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
chip->card = card;
chip->pci = pci;
chip->irq = -1;
spin_lock_init(&chip->reg_lock);
- if ((err = pci_request_regions(pci, "Bt87x audio")) < 0) {
- kfree(chip);
- pci_disable_device(pci);
- return err;
- }
- chip->mmio = pci_ioremap_bar(pci, 0);
- if (!chip->mmio) {
- snd_printk(KERN_ERR "cannot remap io memory\n");
- err = -ENOMEM;
- goto fail;
- }
+ chip->mmio = pcim_iomap_region(pci, 0, "Bt87x audio");
+ if (IS_ERR(chip->mmio))
+ return PTR_ERR(chip->mmio);
chip->reg_control = CTL_A_PWRDN | CTL_DA_ES2 |
CTL_PKTP_16 | (15 << CTL_DA_SDR_SHIFT);
@@ -765,27 +701,18 @@ static int __devinit snd_bt87x_create(struct snd_card *card,
snd_bt87x_writel(chip, REG_INT_MASK, 0);
snd_bt87x_writel(chip, REG_INT_STAT, MY_INTERRUPTS);
- err = request_irq(pci->irq, snd_bt87x_interrupt, IRQF_SHARED,
- "Bt87x audio", chip);
+ err = devm_request_irq(&pci->dev, pci->irq, snd_bt87x_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip);
if (err < 0) {
- snd_printk(KERN_ERR "cannot grab irq %d\n", pci->irq);
- goto fail;
+ dev_err(card->dev, "cannot grab irq %d\n", pci->irq);
+ return err;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_bt87x_free;
pci_set_master(pci);
- synchronize_irq(chip->irq);
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0)
- goto fail;
- snd_card_set_dev(card, &pci->dev);
- *rchip = chip;
return 0;
-
-fail:
- snd_bt87x_free(chip);
- return err;
}
#define BT_DEVICE(chip, subvend, subdev, id) \
@@ -795,7 +722,7 @@ fail:
.driver_data = SND_BT87X_BOARD_ ## id }
/* driver_data is the card id for that device */
-static DEFINE_PCI_DEVICE_TABLE(snd_bt87x_ids) = {
+static const struct pci_device_id snd_bt87x_ids[] = {
/* Hauppauge WinTV series */
BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x0070, 0x13eb, GENERIC),
/* Hauppauge WinTV series */
@@ -828,7 +755,7 @@ MODULE_DEVICE_TABLE(pci, snd_bt87x_ids);
* (DVB cards use the audio function to transfer MPEG data) */
static struct {
unsigned short subvendor, subdevice;
-} blacklist[] __devinitdata = {
+} denylist[] = {
{0x0071, 0x0101}, /* Nebula Electronics DigiTV */
{0x11bd, 0x001c}, /* Pinnacle PCTV Sat */
{0x11bd, 0x0026}, /* Pinnacle PCTV SAT CI */
@@ -844,8 +771,8 @@ static struct {
static struct pci_driver driver;
-/* return the id of the card, or a negative value if it's blacklisted */
-static int __devinit snd_bt87x_detect_card(struct pci_dev *pci)
+/* return the id of the card, or a negative value if it's on the denylist */
+static int snd_bt87x_detect_card(struct pci_dev *pci)
{
int i;
const struct pci_device_id *supported;
@@ -854,24 +781,25 @@ static int __devinit snd_bt87x_detect_card(struct pci_dev *pci)
if (supported && supported->driver_data > 0)
return supported->driver_data;
- for (i = 0; i < ARRAY_SIZE(blacklist); ++i)
- if (blacklist[i].subvendor == pci->subsystem_vendor &&
- blacklist[i].subdevice == pci->subsystem_device) {
- snd_printdd(KERN_INFO "card %#04x-%#04x:%#04x has no audio\n",
+ for (i = 0; i < ARRAY_SIZE(denylist); ++i)
+ if (denylist[i].subvendor == pci->subsystem_vendor &&
+ denylist[i].subdevice == pci->subsystem_device) {
+ dev_dbg(&pci->dev,
+ "card %#04x-%#04x:%#04x has no audio\n",
pci->device, pci->subsystem_vendor, pci->subsystem_device);
return -EBUSY;
}
- snd_printk(KERN_INFO "unknown card %#04x-%#04x:%#04x\n",
+ dev_info(&pci->dev, "unknown card %#04x-%#04x:%#04x\n",
pci->device, pci->subsystem_vendor, pci->subsystem_device);
- snd_printk(KERN_DEBUG "please mail id, board name, and, "
+ dev_info(&pci->dev, "please mail id, board name, and, "
"if it works, the correct digital_rate option to "
"<alsa-devel@alsa-project.org>\n");
return SND_BT87X_BOARD_UNKNOWN;
}
-static int __devinit snd_bt87x_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_bt87x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -894,13 +822,15 @@ static int __devinit snd_bt87x_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- err = snd_bt87x_create(card, pci, &chip);
+ err = snd_bt87x_create(card, pci);
if (err < 0)
- goto _error;
+ return err;
memcpy(&chip->board, &snd_bt87x_boards[boardid], sizeof(chip->board));
@@ -912,69 +842,64 @@ static int __devinit snd_bt87x_probe(struct pci_dev *pci,
err = snd_bt87x_pcm(chip, DEVICE_DIGITAL, "Bt87x Digital");
if (err < 0)
- goto _error;
+ return err;
}
if (!chip->board.no_analog) {
err = snd_bt87x_pcm(chip, DEVICE_ANALOG, "Bt87x Analog");
if (err < 0)
- goto _error;
+ return err;
err = snd_ctl_add(card, snd_ctl_new1(
&snd_bt87x_capture_volume, chip));
if (err < 0)
- goto _error;
+ return err;
err = snd_ctl_add(card, snd_ctl_new1(
&snd_bt87x_capture_boost, chip));
if (err < 0)
- goto _error;
+ return err;
err = snd_ctl_add(card, snd_ctl_new1(
&snd_bt87x_capture_source, chip));
if (err < 0)
- goto _error;
+ return err;
}
- snd_printk(KERN_INFO "bt87x%d: Using board %d, %sanalog, %sdigital "
+ dev_info(card->dev, "bt87x%d: Using board %d, %sanalog, %sdigital "
"(rate %d Hz)\n", dev, boardid,
chip->board.no_analog ? "no " : "",
chip->board.no_digital ? "no " : "", chip->board.dig_rate);
- strcpy(card->driver, "Bt87x");
+ strscpy(card->driver, "Bt87x");
sprintf(card->shortname, "Brooktree Bt%x", pci->device);
sprintf(card->longname, "%s at %#llx, irq %i",
card->shortname, (unsigned long long)pci_resource_start(pci, 0),
chip->irq);
- strcpy(card->mixername, "Bt87x");
+ strscpy(card->mixername, "Bt87x");
err = snd_card_register(card);
if (err < 0)
- goto _error;
+ return err;
pci_set_drvdata(pci, card);
++dev;
return 0;
-
-_error:
- snd_card_free(card);
- return err;
}
-static void __devexit snd_bt87x_remove(struct pci_dev *pci)
+static int snd_bt87x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_bt87x_probe(pci, pci_id));
}
/* default entries for all Bt87x cards - it's not exported */
/* driver_data is set to 0 to call detection */
-static DEFINE_PCI_DEVICE_TABLE(snd_bt87x_default_ids) = {
+static const struct pci_device_id snd_bt87x_default_ids[] = {
BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, PCI_ANY_ID, PCI_ANY_ID, UNKNOWN),
BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_879, PCI_ANY_ID, PCI_ANY_ID, UNKNOWN),
{ }
};
static struct pci_driver driver = {
- .name = "Bt87x",
+ .name = KBUILD_MODNAME,
.id_table = snd_bt87x_ids,
.probe = snd_bt87x_probe,
- .remove = __devexit_p(snd_bt87x_remove),
};
static int __init alsa_card_bt87x_init(void)
diff --git a/sound/pci/ca0106/Makefile b/sound/pci/ca0106/Makefile
index dcbae7b31546..693dc4d80925 100644
--- a/sound/pci/ca0106/Makefile
+++ b/sound/pci/ca0106/Makefile
@@ -1,3 +1,5 @@
-snd-ca0106-objs := ca0106_main.o ca0106_proc.o ca0106_mixer.o ca_midi.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-ca0106-y := ca0106_main.o ca0106_mixer.o ca_midi.o
+snd-ca0106-$(CONFIG_SND_PROC_FS) += ca0106_proc.o
obj-$(CONFIG_SND_CA0106) += snd-ca0106.o
diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h
index f19c11077255..991b1c5d41d5 100644
--- a/sound/pci/ca0106/ca0106.h
+++ b/sound/pci/ca0106/ca0106.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
@@ -50,39 +51,23 @@
* 0.0.22
* Add support for mute control on SB Live 24bit (cards w/ SPI DAC)
*
- *
- * This code was initally based on code from ALSA's emu10k1x.c which is:
+ * This code was initially based on code from ALSA's emu10k1x.c which is:
* Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/************************************************************************************************/
/* PCI function 0 registers, address = <val> + PCIBASE0 */
/************************************************************************************************/
-#define PTR 0x00 /* Indexed register set pointer register */
+#define CA0106_PTR 0x00 /* Indexed register set pointer register */
/* NOTE: The CHANNELNUM and ADDRESS words can */
/* be modified independently of each other. */
/* CNL[1:0], ADDR[27:16] */
-#define DATA 0x04 /* Indexed register set data register */
+#define CA0106_DATA 0x04 /* Indexed register set data register */
/* DATA[31:0] */
-#define IPR 0x08 /* Global interrupt pending register */
+#define CA0106_IPR 0x08 /* Global interrupt pending register */
/* Clear pending interrupts by writing a 1 to */
/* the relevant bits and zero to the other bits */
#define IPR_MIDI_RX_B 0x00020000 /* MIDI UART-B Receive buffer non-empty */
@@ -103,7 +88,7 @@
#define IPR_MIDI_TX_A 0x00000002 /* MIDI UART-A Transmit buffer empty */
#define IPR_PCI 0x00000001 /* PCI Bus error */
-#define INTE 0x0c /* Interrupt enable register */
+#define CA0106_INTE 0x0c /* Interrupt enable register */
#define INTE_MIDI_RX_B 0x00020000 /* MIDI UART-B Receive buffer non-empty */
#define INTE_MIDI_TX_B 0x00010000 /* MIDI UART-B Transmit buffer empty */
@@ -123,8 +108,8 @@
#define INTE_MIDI_TX_A 0x00000002 /* MIDI UART-A Transmit buffer empty */
#define INTE_PCI 0x00000001 /* PCI Bus error */
-#define UNKNOWN10 0x10 /* Unknown ??. Defaults to 0 */
-#define HCFG 0x14 /* Hardware config register */
+#define CA0106_UNKNOWN10 0x10 /* Unknown ??. Defaults to 0 */
+#define CA0106_HCFG 0x14 /* Hardware config register */
/* 0x1000 causes AC3 to fails. It adds a dither bit. */
#define HCFG_STAC 0x10000000 /* Special mode for STAC9460 Codec. */
@@ -148,7 +133,7 @@
#define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */
/* Should be set to 1 when the EMU10K1 is */
/* completely initialized. */
-#define GPIO 0x18 /* Defaults: 005f03a3-Analog, 005f02a2-SPDIF. */
+#define CA0106_GPIO 0x18 /* Defaults: 005f03a3-Analog, 005f02a2-SPDIF. */
/* Here pins 0,1,2,3,4,,6 are output. 5,7 are input */
/* For the Audigy LS, pin 0 (or bit 8) controls the SPDIF/Analog jack. */
/* SB Live 24bit:
@@ -167,15 +152,15 @@
* GPO [15:8] Default 0x9. (Default to SPDIF jack enabled for SPDIF)
* GPO Enable [23:16] Default 0x0f. Setting a bit to 1, causes the pin to be an output pin.
*/
-#define AC97DATA 0x1c /* AC97 register set data register (16 bit) */
+#define CA0106_AC97DATA 0x1c /* AC97 register set data register (16 bit) */
-#define AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */
+#define CA0106_AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */
/********************************************************************************************************/
/* CA0106 pointer-offset register set, accessed through the PTR and DATA registers */
/********************************************************************************************************/
-/* Initally all registers from 0x00 to 0x3f have zero contents. */
+/* Initially all registers from 0x00 to 0x3f have zero contents. */
#define PLAYBACK_LIST_ADDR 0x00 /* Base DMA address of a list of pointers to each period/size */
/* One list entry: 4 bytes for DMA address,
* 4 bytes for period_size << 16.
@@ -188,7 +173,7 @@
#define PLAYBACK_LIST_PTR 0x02 /* Pointer to the current period being played */
/* PTR[5:0], Default: 0x0 */
#define PLAYBACK_UNKNOWN3 0x03 /* Not used ?? */
-#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA addresss */
+#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA address */
/* DMA[31:0], Default: 0x0 */
#define PLAYBACK_PERIOD_SIZE 0x05 /* Playback period size. win2000 uses 0x04000000 */
/* SIZE[31:16], Default: 0x0 */
@@ -223,7 +208,7 @@
* The jack has 4 poles. I will call 1 - Tip, 2 - Next to 1, 3 - Next to 2, 4 - Next to 3
* For Analogue: 1 -> Center Speaker, 2 -> Sub Woofer, 3 -> Ground, 4 -> Ground
* For Digital: 1 -> Front SPDIF, 2 -> Rear SPDIF, 3 -> Center/Subwoofer SPDIF, 4 -> Ground.
- * Standard 4 pole Video A/V cable with RCA outputs: 1 -> White, 2 -> Yellow, 3 -> Sheild on all three, 4 -> Red.
+ * Standard 4 pole Video A/V cable with RCA outputs: 1 -> White, 2 -> Yellow, 3 -> Shield on all three, 4 -> Red.
* So, from this you can see that you cannot use a Standard 4 pole Video A/V cable with the SB Audigy LS card.
*/
/* The Front SPDIF PCM gets mixed with samples from the AC97 codec, so can only work for Stereo PCM and not AC3/DTS
@@ -582,7 +567,7 @@
#define SPI_PL_BIT_R_R (2<<7) /* right channel = right */
#define SPI_PL_BIT_R_C (3<<7) /* right channel = (L+R)/2 */
#define SPI_IZD_REG 2
-#define SPI_IZD_BIT (1<<4) /* infinite zero detect */
+#define SPI_IZD_BIT (0<<4) /* infinite zero detect */
#define SPI_FMT_REG 3
#define SPI_FMT_BIT_RJ (0<<0) /* right justified mode */
@@ -678,11 +663,10 @@ struct snd_ca0106_details {
// definition of the chip-specific record
struct snd_ca0106 {
struct snd_card *card;
- struct snd_ca0106_details *details;
+ const struct snd_ca0106_details *details;
struct pci_dev *pci;
unsigned long port;
- struct resource *res_port;
int irq;
unsigned int serial; /* serial number */
@@ -703,14 +687,14 @@ struct snd_ca0106 {
u8 i2c_capture_volume[4][2];
int capture_mic_line_in;
- struct snd_dma_buffer buffer;
+ struct snd_dma_buffer *buffer;
struct snd_ca_midi midi;
struct snd_ca_midi midi2;
u16 spi_dac_reg[16];
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
#define NUM_SAVED_VOLUMES 9
unsigned int saved_vol[NUM_SAVED_VOLUMES];
#endif
@@ -733,7 +717,7 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu, u32 reg, u32 value);
int snd_ca0106_spi_write(struct snd_ca0106 * emu,
unsigned int data);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
void snd_ca0106_mixer_suspend(struct snd_ca0106 *chip);
void snd_ca0106_mixer_resume(struct snd_ca0106 *chip);
#else
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index d2d12c08f937..41774e2ef53f 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
@@ -117,30 +118,15 @@
* DAC: Unknown
* Trying to handle it like the SB0410.
*
- * This code was initally based on code from ALSA's emu10k1x.c which is:
+ * This code was initially based on code from ALSA's emu10k1x.c which is:
* Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/initval.h>
@@ -151,12 +137,11 @@
MODULE_AUTHOR("James Courtier-Dutton <James@superbug.demon.co.uk>");
MODULE_DESCRIPTION("CA0106");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Creative,SB CA0106 chip}}");
// module parameters (see "Module Parameters")
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
static uint subsystem[SNDRV_CARDS]; /* Force card subsystem model */
module_param_array(index, int, NULL, 0444);
@@ -170,7 +155,7 @@ MODULE_PARM_DESC(subsystem, "Force card subsystem model.");
#include "ca0106.h"
-static struct snd_ca0106_details ca0106_chip_details[] = {
+static const struct snd_ca0106_details ca0106_chip_details[] = {
/* Sound Blaster X-Fi Extreme Audio. This does not have an AC97. 53SB079000000 */
/* It is really just a normal SB Live 24bit. */
/* Tested:
@@ -296,7 +281,7 @@ static struct snd_ca0106_details ca0106_chip_details[] = {
};
/* hardware definition */
-static struct snd_pcm_hardware snd_ca0106_playback_hw = {
+static const struct snd_pcm_hardware snd_ca0106_playback_hw = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -317,7 +302,7 @@ static struct snd_pcm_hardware snd_ca0106_playback_hw = {
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_ca0106_capture_hw = {
+static const struct snd_pcm_hardware snd_ca0106_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -347,16 +332,13 @@ unsigned int snd_ca0106_ptr_read(struct snd_ca0106 * emu,
unsigned int reg,
unsigned int chn)
{
- unsigned long flags;
- unsigned int regptr, val;
+ unsigned int regptr;
regptr = (reg << 16) | chn;
- spin_lock_irqsave(&emu->emu_lock, flags);
- outl(regptr, emu->port + PTR);
- val = inl(emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- return val;
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ outl(regptr, emu->port + CA0106_PTR);
+ return inl(emu->port + CA0106_DATA);
}
void snd_ca0106_ptr_write(struct snd_ca0106 *emu,
@@ -365,14 +347,12 @@ void snd_ca0106_ptr_write(struct snd_ca0106 *emu,
unsigned int data)
{
unsigned int regptr;
- unsigned long flags;
regptr = (reg << 16) | chn;
- spin_lock_irqsave(&emu->emu_lock, flags);
- outl(regptr, emu->port + PTR);
- outl(data, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ outl(regptr, emu->port + CA0106_PTR);
+ outl(data, emu->port + CA0106_DATA);
}
int snd_ca0106_spi_write(struct snd_ca0106 * emu,
@@ -417,13 +397,13 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu,
int status;
int retry;
if ((reg > 0x7f) || (value > 0x1ff)) {
- snd_printk(KERN_ERR "i2c_write: invalid values.\n");
+ dev_err(emu->card->dev, "i2c_write: invalid values.\n");
return -EINVAL;
}
tmp = reg << 25 | value << 16;
/*
- snd_printk(KERN_DEBUG "I2C-write:reg=0x%x, value=0x%x\n", reg, value);
+ dev_dbg(emu->card->dev, "I2C-write:reg=0x%x, value=0x%x\n", reg, value);
*/
/* Not sure what this I2C channel controls. */
/* snd_ca0106_ptr_write(emu, I2C_D0, 0, tmp); */
@@ -442,7 +422,7 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu,
/* Wait till the transaction ends */
while (1) {
status = snd_ca0106_ptr_read(emu, I2C_A, 0);
- /*snd_printk(KERN_DEBUG "I2C:status=0x%x\n", status);*/
+ /*dev_dbg(emu->card->dev, "I2C:status=0x%x\n", status);*/
timeout++;
if ((status & I2C_A_ADC_START) == 0)
break;
@@ -456,7 +436,7 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu,
}
if (retry == 10) {
- snd_printk(KERN_ERR "Writing to ADC failed!\n");
+ dev_err(emu->card->dev, "Writing to ADC failed!\n");
return -EINVAL;
}
@@ -466,24 +446,20 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu,
static void snd_ca0106_intr_enable(struct snd_ca0106 *emu, unsigned int intrenb)
{
- unsigned long flags;
unsigned int intr_enable;
- spin_lock_irqsave(&emu->emu_lock, flags);
- intr_enable = inl(emu->port + INTE) | intrenb;
- outl(intr_enable, emu->port + INTE);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ intr_enable = inl(emu->port + CA0106_INTE) | intrenb;
+ outl(intr_enable, emu->port + CA0106_INTE);
}
static void snd_ca0106_intr_disable(struct snd_ca0106 *emu, unsigned int intrenb)
{
- unsigned long flags;
unsigned int intr_enable;
- spin_lock_irqsave(&emu->emu_lock, flags);
- intr_enable = inl(emu->port + INTE) & ~intrenb;
- outl(intr_enable, emu->port + INTE);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ intr_enable = inl(emu->port + CA0106_INTE) & ~intrenb;
+ outl(intr_enable, emu->port + CA0106_INTE);
}
@@ -516,7 +492,8 @@ static void restore_spdif_bits(struct snd_ca0106 *chip, int idx)
}
}
-static int snd_ca0106_channel_dac(struct snd_ca0106_details *details,
+static int snd_ca0106_channel_dac(struct snd_ca0106 *chip,
+ const struct snd_ca0106_details *details,
int channel_id)
{
switch (channel_id) {
@@ -529,7 +506,7 @@ static int snd_ca0106_channel_dac(struct snd_ca0106_details *details,
case PCM_UNKNOWN_CHANNEL:
return (details->spi_dac & 0x000f) >> (4 * 0);
default:
- snd_printk(KERN_DEBUG "ca0106: unknown channel_id %d\n",
+ dev_dbg(chip->card->dev, "ca0106: unknown channel_id %d\n",
channel_id);
}
return 0;
@@ -539,7 +516,7 @@ static int snd_ca0106_pcm_power_dac(struct snd_ca0106 *chip, int channel_id,
int power)
{
if (chip->details->spi_dac) {
- const int dac = snd_ca0106_channel_dac(chip->details,
+ const int dac = snd_ca0106_channel_dac(chip, chip->details,
channel_id);
const int reg = spi_dacd_reg[dac];
const int bit = spi_dacd_bit[dac];
@@ -550,7 +527,8 @@ static int snd_ca0106_pcm_power_dac(struct snd_ca0106 *chip, int channel_id,
else
/* Power down */
chip->spi_dac_reg[reg] |= bit;
- return snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]);
+ if (snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]) != 0)
+ return -ENXIO;
}
return 0;
}
@@ -583,14 +561,16 @@ static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substr
channel->use = 1;
/*
- printk(KERN_DEBUG "open:channel_id=%d, chip=%p, channel=%p\n",
+ dev_dbg(chip->card->dev, "open:channel_id=%d, chip=%p, channel=%p\n",
channel_id, chip, channel);
*/
//channel->interrupt = snd_ca0106_pcm_channel_interrupt;
channel->epcm = epcm;
- if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
return err;
- if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+ err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64);
+ if (err < 0)
return err;
snd_pcm_set_sync(substream);
@@ -659,10 +639,9 @@ static int snd_ca0106_pcm_open_capture_channel(struct snd_pcm_substream *substre
int err;
epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
- if (epcm == NULL) {
- snd_printk(KERN_ERR "open_capture_channel: failed epcm alloc\n");
+ if (!epcm)
return -ENOMEM;
- }
+
epcm->emu = chip;
epcm->substream = substream;
epcm->channel_id=channel_id;
@@ -677,15 +656,17 @@ static int snd_ca0106_pcm_open_capture_channel(struct snd_pcm_substream *substre
channel->use = 1;
/*
- printk(KERN_DEBUG "open:channel_id=%d, chip=%p, channel=%p\n",
+ dev_dbg(chip->card->dev, "open:channel_id=%d, chip=%p, channel=%p\n",
channel_id, chip, channel);
*/
//channel->interrupt = snd_ca0106_pcm_channel_interrupt;
channel->epcm = epcm;
- if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
return err;
//snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes);
- if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+ err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64);
+ if (err < 0)
return err;
return 0;
}
@@ -721,34 +702,6 @@ static int snd_ca0106_pcm_open_3_capture(struct snd_pcm_substream *substream)
return snd_ca0106_pcm_open_capture_channel(substream, 3);
}
-/* hw_params callback */
-static int snd_ca0106_pcm_hw_params_playback(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-/* hw_free callback */
-static int snd_ca0106_pcm_hw_free_playback(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-/* hw_params callback */
-static int snd_ca0106_pcm_hw_params_capture(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-/* hw_free callback */
-static int snd_ca0106_pcm_hw_free_capture(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
/* prepare playback callback */
static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
{
@@ -756,7 +709,7 @@ static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ca0106_pcm *epcm = runtime->private_data;
int channel = epcm->channel_id;
- u32 *table_base = (u32 *)(emu->buffer.area+(8*16*channel));
+ u32 *table_base = (u32 *)(emu->buffer->area+(8*16*channel));
u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
u32 hcfg_mask = HCFG_PLAYBACK_S32_LE;
u32 hcfg_set = 0x00000000;
@@ -771,7 +724,7 @@ static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
int i;
#if 0 /* debug */
- snd_printk(KERN_DEBUG
+ dev_dbg(emu->card->dev,
"prepare:channel_number=%d, rate=%d, format=0x%x, "
"channels=%d, buffer_size=%ld, period_size=%ld, "
"periods=%u, frames_to_bytes=%d\n",
@@ -779,10 +732,12 @@ static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
runtime->channels, runtime->buffer_size,
runtime->period_size, runtime->periods,
frames_to_bytes(runtime, 1));
- snd_printk(KERN_DEBUG "dma_addr=%x, dma_area=%p, table_base=%p\n",
+ dev_dbg(emu->card->dev,
+ "dma_addr=%x, dma_area=%p, table_base=%p\n",
runtime->dma_addr, runtime->dma_area, table_base);
- snd_printk(KERN_DEBUG "dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
- emu->buffer.addr, emu->buffer.area, emu->buffer.bytes);
+ dev_dbg(emu->card->dev,
+ "dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
+ emu->buffer->addr, emu->buffer->area, emu->buffer->bytes);
#endif /* debug */
/* Rate can be set per channel. */
/* reg40 control host to fifo */
@@ -822,9 +777,9 @@ static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
hcfg_set = 0;
break;
}
- hcfg = inl(emu->port + HCFG) ;
+ hcfg = inl(emu->port + CA0106_HCFG) ;
hcfg = (hcfg & ~hcfg_mask) | hcfg_set;
- outl(hcfg, emu->port + HCFG);
+ outl(hcfg, emu->port + CA0106_HCFG);
reg40 = snd_ca0106_ptr_read(emu, 0x40, 0);
reg40 = (reg40 & ~reg40_mask) | reg40_set;
snd_ca0106_ptr_write(emu, 0x40, 0, reg40);
@@ -832,13 +787,13 @@ static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream)
reg71 = (reg71 & ~reg71_mask) | reg71_set;
snd_ca0106_ptr_write(emu, 0x71, 0, reg71);
- /* FIXME: Check emu->buffer.size before actually writing to it. */
+ /* FIXME: Check emu->buffer->size before actually writing to it. */
for(i=0; i < runtime->periods; i++) {
table_base[i*2] = runtime->dma_addr + (i * period_size_bytes);
table_base[i*2+1] = period_size_bytes << 16;
}
- snd_ca0106_ptr_write(emu, PLAYBACK_LIST_ADDR, channel, emu->buffer.addr+(8*16*channel));
+ snd_ca0106_ptr_write(emu, PLAYBACK_LIST_ADDR, channel, emu->buffer->addr+(8*16*channel));
snd_ca0106_ptr_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19);
snd_ca0106_ptr_write(emu, PLAYBACK_LIST_PTR, channel, 0);
snd_ca0106_ptr_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr);
@@ -876,7 +831,7 @@ static int snd_ca0106_pcm_prepare_capture(struct snd_pcm_substream *substream)
u32 reg71;
#if 0 /* debug */
- snd_printk(KERN_DEBUG
+ dev_dbg(emu->card->dev,
"prepare:channel_number=%d, rate=%d, format=0x%x, "
"channels=%d, buffer_size=%ld, period_size=%ld, "
"periods=%u, frames_to_bytes=%d\n",
@@ -884,10 +839,12 @@ static int snd_ca0106_pcm_prepare_capture(struct snd_pcm_substream *substream)
runtime->channels, runtime->buffer_size,
runtime->period_size, runtime->periods,
frames_to_bytes(runtime, 1));
- snd_printk(KERN_DEBUG "dma_addr=%x, dma_area=%p, table_base=%p\n",
+ dev_dbg(emu->card->dev,
+ "dma_addr=%x, dma_area=%p, table_base=%p\n",
runtime->dma_addr, runtime->dma_area, table_base);
- snd_printk(KERN_DEBUG "dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
- emu->buffer.addr, emu->buffer.area, emu->buffer.bytes);
+ dev_dbg(emu->card->dev,
+ "dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
+ emu->buffer->addr, emu->buffer->area, emu->buffer->bytes);
#endif /* debug */
/* reg71 controls ADC rate. */
switch (runtime->rate) {
@@ -922,9 +879,9 @@ static int snd_ca0106_pcm_prepare_capture(struct snd_pcm_substream *substream)
hcfg_set = 0;
break;
}
- hcfg = inl(emu->port + HCFG) ;
+ hcfg = inl(emu->port + CA0106_HCFG) ;
hcfg = (hcfg & ~hcfg_mask) | hcfg_set;
- outl(hcfg, emu->port + HCFG);
+ outl(hcfg, emu->port + CA0106_HCFG);
reg71 = snd_ca0106_ptr_read(emu, 0x71, 0);
reg71 = (reg71 & ~reg71_mask) | reg71_set;
snd_ca0106_ptr_write(emu, 0x71, 0, reg71);
@@ -934,7 +891,7 @@ static int snd_ca0106_pcm_prepare_capture(struct snd_pcm_substream *substream)
/*
- printk(KERN_DEBUG
+ dev_dbg(emu->card->dev,
"prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, "
"buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",
channel, runtime->rate, runtime->format, runtime->channels,
@@ -982,13 +939,13 @@ static int snd_ca0106_pcm_trigger_playback(struct snd_pcm_substream *substream,
runtime = s->runtime;
epcm = runtime->private_data;
channel = epcm->channel_id;
- /* snd_printk(KERN_DEBUG "channel=%d\n", channel); */
+ /* dev_dbg(emu->card->dev, "channel=%d\n", channel); */
epcm->running = running;
basic |= (0x1 << channel);
extended |= (0x10 << channel);
snd_pcm_trigger_done(s, substream);
}
- /* snd_printk(KERN_DEBUG "basic=0x%x, extended=0x%x\n",basic, extended); */
+ /* dev_dbg(emu->card->dev, "basic=0x%x, extended=0x%x\n",basic, extended); */
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -1070,7 +1027,7 @@ snd_ca0106_pcm_pointer_playback(struct snd_pcm_substream *substream)
return ptr;
prev_ptr = ptr;
} while (--timeout);
- snd_printk(KERN_WARNING "ca0106: unstable DMA pointer!\n");
+ dev_warn(emu->card->dev, "ca0106: unstable DMA pointer!\n");
return 0;
}
@@ -1082,7 +1039,7 @@ snd_ca0106_pcm_pointer_capture(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_ca0106_pcm *epcm = runtime->private_data;
snd_pcm_uframes_t ptr, ptr1, ptr2 = 0;
- int channel = channel=epcm->channel_id;
+ int channel = epcm->channel_id;
if (!epcm->running)
return 0;
@@ -1093,7 +1050,7 @@ snd_ca0106_pcm_pointer_capture(struct snd_pcm_substream *substream)
if (ptr >= runtime->buffer_size)
ptr -= runtime->buffer_size;
/*
- printk(KERN_DEBUG "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, "
+ dev_dbg(emu->card->dev, "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, "
"buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n",
ptr1, ptr2, ptr, (int)runtime->buffer_size,
(int)runtime->period_size, (int)runtime->frame_bits,
@@ -1103,89 +1060,65 @@ snd_ca0106_pcm_pointer_capture(struct snd_pcm_substream *substream)
}
/* operators */
-static struct snd_pcm_ops snd_ca0106_playback_front_ops = {
+static const struct snd_pcm_ops snd_ca0106_playback_front_ops = {
.open = snd_ca0106_pcm_open_playback_front,
.close = snd_ca0106_pcm_close_playback,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ca0106_pcm_hw_params_playback,
- .hw_free = snd_ca0106_pcm_hw_free_playback,
.prepare = snd_ca0106_pcm_prepare_playback,
.trigger = snd_ca0106_pcm_trigger_playback,
.pointer = snd_ca0106_pcm_pointer_playback,
};
-static struct snd_pcm_ops snd_ca0106_capture_0_ops = {
+static const struct snd_pcm_ops snd_ca0106_capture_0_ops = {
.open = snd_ca0106_pcm_open_0_capture,
.close = snd_ca0106_pcm_close_capture,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ca0106_pcm_hw_params_capture,
- .hw_free = snd_ca0106_pcm_hw_free_capture,
.prepare = snd_ca0106_pcm_prepare_capture,
.trigger = snd_ca0106_pcm_trigger_capture,
.pointer = snd_ca0106_pcm_pointer_capture,
};
-static struct snd_pcm_ops snd_ca0106_capture_1_ops = {
+static const struct snd_pcm_ops snd_ca0106_capture_1_ops = {
.open = snd_ca0106_pcm_open_1_capture,
.close = snd_ca0106_pcm_close_capture,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ca0106_pcm_hw_params_capture,
- .hw_free = snd_ca0106_pcm_hw_free_capture,
.prepare = snd_ca0106_pcm_prepare_capture,
.trigger = snd_ca0106_pcm_trigger_capture,
.pointer = snd_ca0106_pcm_pointer_capture,
};
-static struct snd_pcm_ops snd_ca0106_capture_2_ops = {
+static const struct snd_pcm_ops snd_ca0106_capture_2_ops = {
.open = snd_ca0106_pcm_open_2_capture,
.close = snd_ca0106_pcm_close_capture,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ca0106_pcm_hw_params_capture,
- .hw_free = snd_ca0106_pcm_hw_free_capture,
.prepare = snd_ca0106_pcm_prepare_capture,
.trigger = snd_ca0106_pcm_trigger_capture,
.pointer = snd_ca0106_pcm_pointer_capture,
};
-static struct snd_pcm_ops snd_ca0106_capture_3_ops = {
+static const struct snd_pcm_ops snd_ca0106_capture_3_ops = {
.open = snd_ca0106_pcm_open_3_capture,
.close = snd_ca0106_pcm_close_capture,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ca0106_pcm_hw_params_capture,
- .hw_free = snd_ca0106_pcm_hw_free_capture,
.prepare = snd_ca0106_pcm_prepare_capture,
.trigger = snd_ca0106_pcm_trigger_capture,
.pointer = snd_ca0106_pcm_pointer_capture,
};
-static struct snd_pcm_ops snd_ca0106_playback_center_lfe_ops = {
+static const struct snd_pcm_ops snd_ca0106_playback_center_lfe_ops = {
.open = snd_ca0106_pcm_open_playback_center_lfe,
.close = snd_ca0106_pcm_close_playback,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ca0106_pcm_hw_params_playback,
- .hw_free = snd_ca0106_pcm_hw_free_playback,
.prepare = snd_ca0106_pcm_prepare_playback,
.trigger = snd_ca0106_pcm_trigger_playback,
.pointer = snd_ca0106_pcm_pointer_playback,
};
-static struct snd_pcm_ops snd_ca0106_playback_unknown_ops = {
+static const struct snd_pcm_ops snd_ca0106_playback_unknown_ops = {
.open = snd_ca0106_pcm_open_playback_unknown,
.close = snd_ca0106_pcm_close_playback,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ca0106_pcm_hw_params_playback,
- .hw_free = snd_ca0106_pcm_hw_free_playback,
.prepare = snd_ca0106_pcm_prepare_playback,
.trigger = snd_ca0106_pcm_trigger_playback,
.pointer = snd_ca0106_pcm_pointer_playback,
};
-static struct snd_pcm_ops snd_ca0106_playback_rear_ops = {
+static const struct snd_pcm_ops snd_ca0106_playback_rear_ops = {
.open = snd_ca0106_pcm_open_playback_rear,
.close = snd_ca0106_pcm_close_playback,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ca0106_pcm_hw_params_playback,
- .hw_free = snd_ca0106_pcm_hw_free_playback,
.prepare = snd_ca0106_pcm_prepare_playback,
.trigger = snd_ca0106_pcm_trigger_playback,
.pointer = snd_ca0106_pcm_pointer_playback,
@@ -1196,26 +1129,20 @@ static unsigned short snd_ca0106_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
struct snd_ca0106 *emu = ac97->private_data;
- unsigned long flags;
- unsigned short val;
-
- spin_lock_irqsave(&emu->emu_lock, flags);
- outb(reg, emu->port + AC97ADDRESS);
- val = inw(emu->port + AC97DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- return val;
+
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ outb(reg, emu->port + CA0106_AC97ADDRESS);
+ return inw(emu->port + CA0106_AC97DATA);
}
static void snd_ca0106_ac97_write(struct snd_ac97 *ac97,
unsigned short reg, unsigned short val)
{
struct snd_ca0106 *emu = ac97->private_data;
- unsigned long flags;
- spin_lock_irqsave(&emu->emu_lock, flags);
- outb(reg, emu->port + AC97ADDRESS);
- outw(val, emu->port + AC97DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ outb(reg, emu->port + CA0106_AC97ADDRESS);
+ outw(val, emu->port + CA0106_AC97DATA);
}
static int snd_ca0106_ac97(struct snd_ca0106 *chip)
@@ -1223,12 +1150,13 @@ static int snd_ca0106_ac97(struct snd_ca0106 *chip)
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_ca0106_ac97_write,
.read = snd_ca0106_ac97_read,
};
- if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus);
+ if (err < 0)
return err;
pbus->no_vra = 1; /* we don't need VRA */
@@ -1240,32 +1168,11 @@ static int snd_ca0106_ac97(struct snd_ca0106 *chip)
static void ca0106_stop_chip(struct snd_ca0106 *chip);
-static int snd_ca0106_free(struct snd_ca0106 *chip)
+static void snd_ca0106_free(struct snd_card *card)
{
- if (chip->res_port != NULL) {
- /* avoid access to already used hardware */
- ca0106_stop_chip(chip);
- }
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- // release the data
-#if 1
- if (chip->buffer.area)
- snd_dma_free_pages(&chip->buffer);
-#endif
-
- // release the i/o port
- release_and_free_resource(chip->res_port);
-
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
-}
+ struct snd_ca0106 *chip = card->private_data;
-static int snd_ca0106_dev_free(struct snd_device *device)
-{
- struct snd_ca0106 *chip = device->device_data;
- return snd_ca0106_free(chip);
+ ca0106_stop_chip(chip);
}
static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
@@ -1278,15 +1185,15 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
unsigned int stat76;
struct snd_ca0106_channel *pchannel;
- status = inl(chip->port + IPR);
+ status = inl(chip->port + CA0106_IPR);
if (! status)
return IRQ_NONE;
stat76 = snd_ca0106_ptr_read(chip, EXTENDED_INT, 0);
/*
- snd_printk(KERN_DEBUG "interrupt status = 0x%08x, stat76=0x%08x\n",
+ dev_dbg(emu->card->dev, "interrupt status = 0x%08x, stat76=0x%08x\n",
status, stat76);
- snd_printk(KERN_DEBUG "ptr=0x%08x\n",
+ dev_dbg(emu->card->dev, "ptr=0x%08x\n",
snd_ca0106_ptr_read(chip, PLAYBACK_POINTER, 0));
*/
mask = 0x11; /* 0x1 for one half, 0x10 for the other half period. */
@@ -1296,11 +1203,13 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
/* FIXME: Select the correct substream for period elapsed */
if(pchannel->use) {
snd_pcm_period_elapsed(pchannel->epcm->substream);
- //printk(KERN_INFO "interrupt [%d] used\n", i);
+ /* dev_dbg(emu->card->dev, "interrupt [%d] used\n", i); */
}
}
- //printk(KERN_INFO "channel=%p\n",pchannel);
- //printk(KERN_INFO "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number);
+ /*
+ dev_dbg(emu->card->dev, "channel=%p\n", pchannel);
+ dev_dbg(emu->card->dev, "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number);
+ */
mask <<= 1;
}
mask = 0x110000; /* 0x1 for one half, 0x10 for the other half period. */
@@ -1310,11 +1219,13 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
/* FIXME: Select the correct substream for period elapsed */
if(pchannel->use) {
snd_pcm_period_elapsed(pchannel->epcm->substream);
- //printk(KERN_INFO "interrupt [%d] used\n", i);
+ /* dev_dbg(emu->card->dev, "interrupt [%d] used\n", i); */
}
}
- //printk(KERN_INFO "channel=%p\n",pchannel);
- //printk(KERN_INFO "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number);
+ /*
+ dev_dbg(emu->card->dev, "channel=%p\n", pchannel);
+ dev_dbg(emu->card->dev, "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number);
+ */
mask <<= 1;
}
@@ -1329,15 +1240,34 @@ static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id)
}
// acknowledge the interrupt if necessary
- outl(status, chip->port+IPR);
+ outl(status, chip->port + CA0106_IPR);
return IRQ_HANDLED;
}
-static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
+static const struct snd_pcm_chmap_elem surround_map[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
+static const struct snd_pcm_chmap_elem clfe_map[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
+ { }
+};
+
+static const struct snd_pcm_chmap_elem side_map[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
+ { }
+};
+
+static int snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
{
struct snd_pcm *pcm;
struct snd_pcm_substream *substream;
+ const struct snd_pcm_chmap_elem *map = NULL;
int err;
err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm);
@@ -1350,51 +1280,56 @@ static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device)
case 0:
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_front_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_0_ops);
+ map = snd_pcm_std_chmaps;
break;
case 1:
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_rear_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_1_ops);
+ map = surround_map;
break;
case 2:
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_center_lfe_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_2_ops);
+ map = clfe_map;
break;
case 3:
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_unknown_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_3_ops);
+ map = side_map;
break;
}
pcm->info_flags = 0;
- strcpy(pcm->name, "CA0106");
+ strscpy(pcm->name, "CA0106");
for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
substream;
substream = substream->next) {
- if ((err = snd_pcm_lib_preallocate_pages(substream,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(emu->pci),
- 64*1024, 64*1024)) < 0) /* FIXME: 32*1024 for sound buffer, between 32and64 for Periods table. */
- return err;
+ snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
+ &emu->pci->dev,
+ 64*1024, 64*1024);
}
for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
substream;
substream = substream->next) {
- if ((err = snd_pcm_lib_preallocate_pages(substream,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(emu->pci),
- 64*1024, 64*1024)) < 0)
- return err;
+ snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
+ &emu->pci->dev,
+ 64*1024, 64*1024);
}
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, 2,
+ 1 << 2, NULL);
+ if (err < 0)
+ return err;
+
emu->pcm[device] = pcm;
return 0;
}
#define SPI_REG(reg, value) (((reg) << SPI_REG_SHIFT) | (value))
-static unsigned int spi_dac_init[] = {
+static const unsigned int spi_dac_init[] = {
SPI_REG(SPI_LDA1_REG, SPI_DA_BIT_0dB), /* 0dB dig. attenuation */
SPI_REG(SPI_RDA1_REG, SPI_DA_BIT_0dB),
SPI_REG(SPI_PL_REG, SPI_PL_BIT_L_L | SPI_PL_BIT_R_R | SPI_IZD_BIT),
@@ -1412,7 +1347,7 @@ static unsigned int spi_dac_init[] = {
SPI_REG(SPI_DACD4_REG, SPI_DACD4_BIT),
};
-static unsigned int i2c_adc_init[][2] = {
+static const unsigned int i2c_adc_init[][2] = {
{ 0x17, 0x00 }, /* Reset */
{ 0x07, 0x00 }, /* Timeout */
{ 0x0b, 0x22 }, /* Interface control */
@@ -1433,7 +1368,7 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
int ch;
unsigned int def_bits;
- outl(0, chip->port + INTE);
+ outl(0, chip->port + CA0106_INTE);
/*
* Init to 0x02109204 :
@@ -1470,8 +1405,8 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000);
/* Write 0x8000 to AC97_REC_GAIN to mute it. */
- outb(AC97_REC_GAIN, chip->port + AC97ADDRESS);
- outw(0x8000, chip->port + AC97DATA);
+ outb(AC97_REC_GAIN, chip->port + CA0106_AC97ADDRESS);
+ outw(0x8000, chip->port + CA0106_AC97DATA);
#if 0 /* FIXME: what are these? */
snd_ca0106_ptr_write(chip, SPCS0, 0, 0x2108006);
snd_ca0106_ptr_write(chip, 0x42, 0, 0x2108006);
@@ -1545,37 +1480,37 @@ static void ca0106_init_chip(struct snd_ca0106 *chip, int resume)
/* FIXME: Still need to find out what the other GPIO bits do.
* E.g. For digital spdif out.
*/
- outl(0x0, chip->port+GPIO);
- /* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */
- outl(0x005f5301, chip->port+GPIO); /* Analog */
+ outl(0x0, chip->port + CA0106_GPIO);
+ /* outl(0x00f0e000, chip->port + CA0106_GPIO); */ /* Analog */
+ outl(0x005f5301, chip->port + CA0106_GPIO); /* Analog */
} else if (chip->details->gpio_type == 1) {
/* The SB0410 and SB0413 use GPIO differently. */
/* FIXME: Still need to find out what the other GPIO bits do.
* E.g. For digital spdif out.
*/
- outl(0x0, chip->port+GPIO);
- /* outl(0x00f0e000, chip->port+GPIO); */ /* Analog */
- outl(0x005f5301, chip->port+GPIO); /* Analog */
+ outl(0x0, chip->port + CA0106_GPIO);
+ /* outl(0x00f0e000, chip->port + CA0106_GPIO); */ /* Analog */
+ outl(0x005f5301, chip->port + CA0106_GPIO); /* Analog */
} else {
- outl(0x0, chip->port+GPIO);
- outl(0x005f03a3, chip->port+GPIO); /* Analog */
- /* outl(0x005f02a2, chip->port+GPIO); */ /* SPDIF */
+ outl(0x0, chip->port + CA0106_GPIO);
+ outl(0x005f03a3, chip->port + CA0106_GPIO); /* Analog */
+ /* outl(0x005f02a2, chip->port + CA0106_GPIO); */ /* SPDIF */
}
snd_ca0106_intr_enable(chip, 0x105); /* Win2000 uses 0x1e0 */
/* outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG); */
/* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */
- /* outl(0x00001409, chip->port+HCFG); */
- /* outl(0x00000009, chip->port+HCFG); */
+ /* outl(0x00001409, chip->port + CA0106_HCFG); */
+ /* outl(0x00000009, chip->port + CA0106_HCFG); */
/* AC97 2.0, Enable outputs. */
- outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG);
+ outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port + CA0106_HCFG);
if (chip->details->i2c_adc == 1) {
/* The SB0410 and SB0413 use I2C to control ADC. */
int size, n;
size = ARRAY_SIZE(i2c_adc_init);
- /* snd_printk(KERN_DEBUG "I2C:array size=0x%x\n", size); */
+ /* dev_dbg(emu->card->dev, "I2C:array size=0x%x\n", size); */
for (n = 0; n < size; n++)
snd_ca0106_i2c_write(chip, i2c_adc_init[n][0],
i2c_adc_init[n][1]);
@@ -1610,84 +1545,65 @@ static void ca0106_stop_chip(struct snd_ca0106 *chip)
{
/* disable interrupts */
snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0);
- outl(0, chip->port + INTE);
+ outl(0, chip->port + CA0106_INTE);
snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0);
udelay(1000);
/* disable audio */
/* outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); */
- outl(0, chip->port + HCFG);
+ outl(0, chip->port + CA0106_HCFG);
/* FIXME: We need to stop and DMA transfers here.
* But as I am not sure how yet, we cannot from the dma pages.
* So we can fix: snd-malloc: Memory leak? pages not freed = 8
*/
}
-static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
- struct pci_dev *pci,
- struct snd_ca0106 **rchip)
+static int snd_ca0106_create(int dev, struct snd_card *card,
+ struct pci_dev *pci)
{
- struct snd_ca0106 *chip;
- struct snd_ca0106_details *c;
+ struct snd_ca0106 *chip = card->private_data;
+ const struct snd_ca0106_details *c;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_ca0106_dev_free,
- };
- *rchip = NULL;
-
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
- printk(KERN_ERR "error to set 32bit mask DMA\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
+ dev_err(card->dev, "error to set 32bit mask DMA\n");
return -ENXIO;
}
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
chip->card = card;
chip->pci = pci;
chip->irq = -1;
spin_lock_init(&chip->emu_lock);
+ err = pcim_request_all_regions(pci, "snd_ca0106");
+ if (err < 0)
+ return err;
chip->port = pci_resource_start(pci, 0);
- chip->res_port = request_region(chip->port, 0x20, "snd_ca0106");
- if (!chip->res_port) {
- snd_ca0106_free(chip);
- printk(KERN_ERR "cannot allocate the port\n");
- return -EBUSY;
- }
- if (request_irq(pci->irq, snd_ca0106_interrupt,
- IRQF_SHARED, "snd_ca0106", chip)) {
- snd_ca0106_free(chip);
- printk(KERN_ERR "cannot grab irq\n");
+ if (devm_request_irq(&pci->dev, pci->irq, snd_ca0106_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "cannot grab irq\n");
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
/* This stores the periods table. */
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- 1024, &chip->buffer) < 0) {
- snd_ca0106_free(chip);
+ chip->buffer = snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV, 1024);
+ if (!chip->buffer)
return -ENOMEM;
- }
pci_set_master(pci);
/* read serial */
pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
- printk(KERN_INFO "snd-ca0106: Model %04x Rev %08x Serial %08x\n",
+ dev_info(card->dev, "Model %04x Rev %08x Serial %08x\n",
chip->model, pci->revision, chip->serial);
- strcpy(card->driver, "CA0106");
- strcpy(card->shortname, "CA0106");
+ strscpy(card->driver, "CA0106");
+ strscpy(card->shortname, "CA0106");
for (c = ca0106_chip_details; c->serial; c++) {
if (subsystem[dev]) {
@@ -1698,7 +1614,7 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
}
chip->details = c;
if (subsystem[dev]) {
- printk(KERN_INFO "snd-ca0106: Sound card name=%s, "
+ dev_info(card->dev, "Sound card name=%s, "
"subsystem=0x%x. Forced to subsystem=0x%x\n",
c->name, chip->serial, subsystem[dev]);
}
@@ -1707,13 +1623,6 @@ static int __devinit snd_ca0106_create(int dev, struct snd_card *card,
c->name, chip->port, chip->irq);
ca0106_init_chip(chip, 0);
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0) {
- snd_ca0106_free(chip);
- return err;
- }
- *rchip = chip;
return 0;
}
@@ -1749,7 +1658,7 @@ static int ca0106_dev_id_port(void *dev_id)
return ((struct snd_ca0106 *)dev_id)->port;
}
-static int __devinit snd_ca0106_midi(struct snd_ca0106 *chip, unsigned int channel)
+static int snd_ca0106_midi(struct snd_ca0106 *chip, unsigned int channel)
{
struct snd_ca_midi *midi;
char *name;
@@ -1793,15 +1702,16 @@ static int __devinit snd_ca0106_midi(struct snd_ca0106 *chip, unsigned int chann
midi->dev_id = chip;
- if ((err = ca_midi_init(chip, midi, 0, name)) < 0)
+ err = ca_midi_init(chip, midi, 0, name);
+ if (err < 0)
return err;
return 0;
}
-static int __devinit snd_ca0106_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_ca0106_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -1815,100 +1725,79 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- err = snd_ca0106_create(dev, card, pci, &chip);
+ err = snd_ca0106_create(dev, card, pci);
if (err < 0)
- goto error;
- card->private_data = chip;
+ return err;
+ card->private_free = snd_ca0106_free;
for (i = 0; i < 4; i++) {
err = snd_ca0106_pcm(chip, i);
if (err < 0)
- goto error;
+ return err;
}
if (chip->details->ac97 == 1) {
/* The SB0410 and SB0413 do not have an AC97 chip. */
err = snd_ca0106_ac97(chip);
if (err < 0)
- goto error;
+ return err;
}
err = snd_ca0106_mixer(chip);
if (err < 0)
- goto error;
+ return err;
- snd_printdd("ca0106: probe for MIDI channel A ...");
+ dev_dbg(card->dev, "probe for MIDI channel A ...");
err = snd_ca0106_midi(chip, CA0106_MIDI_CHAN_A);
if (err < 0)
- goto error;
- snd_printdd(" done.\n");
+ return err;
+ dev_dbg(card->dev, " done.\n");
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
snd_ca0106_proc_init(chip);
#endif
- snd_card_set_dev(card, &pci->dev);
-
err = snd_card_register(card);
if (err < 0)
- goto error;
+ return err;
pci_set_drvdata(pci, card);
dev++;
return 0;
-
- error:
- snd_card_free(card);
- return err;
}
-static void __devexit snd_ca0106_remove(struct pci_dev *pci)
+static int snd_ca0106_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_ca0106_probe(pci, pci_id));
}
-#ifdef CONFIG_PM
-static int snd_ca0106_suspend(struct pci_dev *pci, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int snd_ca0106_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_ca0106 *chip = card->private_data;
- int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- for (i = 0; i < 4; i++)
- snd_pcm_suspend_all(chip->pcm[i]);
if (chip->details->ac97)
snd_ac97_suspend(chip->ac97);
snd_ca0106_mixer_suspend(chip);
ca0106_stop_chip(chip);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_ca0106_resume(struct pci_dev *pci)
+static int snd_ca0106_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_ca0106 *chip = card->private_data;
int i;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
-
- if (pci_enable_device(pci) < 0) {
- snd_card_disconnect(card);
- return -EIO;
- }
-
- pci_set_master(pci);
-
ca0106_init_chip(chip, 1);
if (chip->details->ac97)
@@ -1922,38 +1811,28 @@ static int snd_ca0106_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
+
+static SIMPLE_DEV_PM_OPS(snd_ca0106_pm, snd_ca0106_suspend, snd_ca0106_resume);
+#define SND_CA0106_PM_OPS &snd_ca0106_pm
+#else
+#define SND_CA0106_PM_OPS NULL
#endif
// PCI IDs
-static DEFINE_PCI_DEVICE_TABLE(snd_ca0106_ids) = {
+static const struct pci_device_id snd_ca0106_ids[] = {
{ PCI_VDEVICE(CREATIVE, 0x0007), 0 }, /* Audigy LS or Live 24bit */
{ 0, }
};
MODULE_DEVICE_TABLE(pci, snd_ca0106_ids);
// pci_driver definition
-static struct pci_driver driver = {
- .name = "CA0106",
+static struct pci_driver ca0106_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_ca0106_ids,
.probe = snd_ca0106_probe,
- .remove = __devexit_p(snd_ca0106_remove),
-#ifdef CONFIG_PM
- .suspend = snd_ca0106_suspend,
- .resume = snd_ca0106_resume,
-#endif
+ .driver = {
+ .pm = SND_CA0106_PM_OPS,
+ },
};
-// initialization of the module
-static int __init alsa_card_ca0106_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-// clean up the module
-static void __exit alsa_card_ca0106_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ca0106_init)
-module_exit(alsa_card_ca0106_exit)
+module_pci_driver(ca0106_driver);
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
index 630aa4998189..f7b6b2db889b 100644
--- a/sound/pci/ca0106/ca0106_mixer.c
+++ b/sound/pci/ca0106/ca0106_mixer.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
@@ -42,23 +43,8 @@
* 0.0.18
* Add support for mute control on SB Live 24bit (cards w/ SPI DAC)
*
- * This code was initally based on code from ALSA's emu10k1x.c which is:
+ * This code was initially based on code from ALSA's emu10k1x.c which is:
* Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
#include <linux/init.h>
@@ -70,7 +56,7 @@
#include <sound/ac97_codec.h>
#include <sound/info.h>
#include <sound/tlv.h>
-#include <asm/io.h>
+#include <linux/io.h>
#include "ca0106.h"
@@ -84,8 +70,8 @@ static void ca0106_spdif_enable(struct snd_ca0106 *emu)
snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000;
snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
- val = inl(emu->port + GPIO) & ~0x101;
- outl(val, emu->port + GPIO);
+ val = inl(emu->port + CA0106_GPIO) & ~0x101;
+ outl(val, emu->port + CA0106_GPIO);
} else {
/* Analog */
@@ -93,8 +79,8 @@ static void ca0106_spdif_enable(struct snd_ca0106 *emu)
snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
val = snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000;
snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0, val);
- val = inl(emu->port + GPIO) | 0x101;
- outl(val, emu->port + GPIO);
+ val = inl(emu->port + CA0106_GPIO) | 0x101;
+ outl(val, emu->port + CA0106_GPIO);
}
}
@@ -133,14 +119,14 @@ static void ca0106_set_capture_mic_line_in(struct snd_ca0106 *emu)
if (emu->capture_mic_line_in) {
/* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
- tmp = inl(emu->port+GPIO) & ~0x400;
+ tmp = inl(emu->port + CA0106_GPIO) & ~0x400;
tmp = tmp | 0x400;
- outl(tmp, emu->port+GPIO);
+ outl(tmp, emu->port + CA0106_GPIO);
/* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); */
} else {
/* snd_ca0106_i2c_write(emu, ADC_MUX, 0); */ /* Mute input */
- tmp = inl(emu->port+GPIO) & ~0x400;
- outl(tmp, emu->port+GPIO);
+ tmp = inl(emu->port + CA0106_GPIO) & ~0x400;
+ outl(tmp, emu->port + CA0106_GPIO);
/* snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); */
}
}
@@ -185,17 +171,11 @@ static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol,
static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[6] = {
+ static const char * const texts[6] = {
"IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 6;
- if (uinfo->value.enumerated.item > 5)
- uinfo->value.enumerated.item = 5;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 6, texts);
}
static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -228,17 +208,11 @@ static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[6] = {
+ static const char * const texts[4] = {
"Phone", "Mic", "Line in", "Aux"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
}
static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -273,29 +247,17 @@ static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "Side out", "Line in" };
+ static const char * const texts[2] = { "Side out", "Line in" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "Line in", "Mic in" };
+ static const char * const texts[2] = { "Line in", "Mic in" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol,
@@ -325,7 +287,7 @@ static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata =
+static const struct snd_kcontrol_new snd_ca0106_capture_mic_line_in =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Shared Mic/Line in Capture Switch",
@@ -334,7 +296,7 @@ static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata =
.put = snd_ca0106_capture_mic_line_in_put
};
-static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out __devinitdata =
+static const struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Shared Line in/Side out Capture Switch",
@@ -588,7 +550,7 @@ static int spi_mute_put(struct snd_kcontrol *kcontrol,
.private_value = ((chid) << 8) | (reg) \
}
-static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ca0106_volume_ctls[] = {
CA_VOLUME("Analog Front Playback Volume",
CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2),
CA_VOLUME("Analog Rear Playback Volume",
@@ -669,7 +631,7 @@ static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
.private_value = chid \
}
-static struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ca0106_volume_i2c_adc_ctls[] = {
I2C_VOLUME("Phone Capture Volume", 0),
I2C_VOLUME("Mic Capture Volume", 1),
I2C_VOLUME("Line in Capture Volume", 2),
@@ -691,8 +653,8 @@ static const int spi_dmute_bit[] = {
SPI_DMUTE4_BIT,
};
-static struct snd_kcontrol_new __devinit
-snd_ca0106_volume_spi_dac_ctl(struct snd_ca0106_details *details,
+static struct snd_kcontrol_new
+snd_ca0106_volume_spi_dac_ctl(const struct snd_ca0106_details *details,
int channel_id)
{
struct snd_kcontrol_new spi_switch = {0};
@@ -735,30 +697,20 @@ snd_ca0106_volume_spi_dac_ctl(struct snd_ca0106_details *details,
return spi_switch;
}
-static int __devinit remove_ctl(struct snd_card *card, const char *name)
+static int remove_ctl(struct snd_card *card, const char *name)
{
struct snd_ctl_elem_id id;
memset(&id, 0, sizeof(id));
- strcpy(id.name, name);
+ strscpy(id.name, name);
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
return snd_ctl_remove_id(card, &id);
}
-static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, const char *name)
+static int rename_ctl(struct snd_card *card, const char *src, const char *dst)
{
- struct snd_ctl_elem_id sid;
- memset(&sid, 0, sizeof(sid));
- /* FIXME: strcpy is bad. */
- strcpy(sid.name, name);
- sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- return snd_ctl_find_id(card, &sid);
-}
-
-static int __devinit rename_ctl(struct snd_card *card, const char *src, const char *dst)
-{
- struct snd_kcontrol *kctl = ctl_find(card, src);
+ struct snd_kcontrol *kctl = snd_ctl_find_id_mixer(card, src);
if (kctl) {
- strcpy(kctl->id.name, dst);
+ snd_ctl_rename(card, kctl, dst);
return 0;
}
return -ENOENT;
@@ -774,10 +726,10 @@ static int __devinit rename_ctl(struct snd_card *card, const char *src, const ch
} \
} while (0)
-static __devinitdata
+static
DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 25, 1);
-static char *slave_vols[] __devinitdata = {
+static const char * const follower_vols[] = {
"Analog Front Playback Volume",
"Analog Rear Playback Volume",
"Analog Center/LFE Playback Volume",
@@ -790,7 +742,7 @@ static char *slave_vols[] __devinitdata = {
NULL
};
-static char *slave_sws[] __devinitdata = {
+static const char * const follower_sws[] = {
"Analog Front Playback Switch",
"Analog Rear Playback Switch",
"Analog Center/LFE Playback Switch",
@@ -799,23 +751,13 @@ static char *slave_sws[] __devinitdata = {
NULL
};
-static void __devinit add_slaves(struct snd_card *card,
- struct snd_kcontrol *master, char **list)
-{
- for (; *list; list++) {
- struct snd_kcontrol *slave = ctl_find(card, *list);
- if (slave)
- snd_ctl_add_slave(master, slave);
- }
-}
-
-int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
+int snd_ca0106_mixer(struct snd_ca0106 *emu)
{
int err;
struct snd_card *card = emu->card;
- char **c;
+ const char * const *c;
struct snd_kcontrol *vmaster;
- static char *ca0106_remove_ctls[] = {
+ static const char * const ca0106_remove_ctls[] = {
"Master Mono Playback Switch",
"Master Mono Playback Volume",
"3D Control - Switch",
@@ -839,7 +781,7 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
"Surround Phase Inversion Playback Switch",
NULL
};
- static char *ca0106_rename_ctls[] = {
+ static const char * const ca0106_rename_ctls[] = {
"Master Playback Switch", "Capture Switch",
"Master Playback Volume", "Capture Volume",
"Line Playback Switch", "AC97 Line Capture Switch",
@@ -890,7 +832,9 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
err = snd_ctl_add(card, vmaster);
if (err < 0)
return err;
- add_slaves(card, vmaster, slave_vols);
+ err = snd_ctl_add_followers(card, vmaster, follower_vols);
+ if (err < 0)
+ return err;
if (emu->details->spi_dac) {
vmaster = snd_ctl_make_virtual_master("Master Playback Switch",
@@ -900,20 +844,22 @@ int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
err = snd_ctl_add(card, vmaster);
if (err < 0)
return err;
- add_slaves(card, vmaster, slave_sws);
+ err = snd_ctl_add_followers(card, vmaster, follower_sws);
+ if (err < 0)
+ return err;
}
- strcpy(card->mixername, "CA0106");
+ strscpy(card->mixername, "CA0106");
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
struct ca0106_vol_tbl {
unsigned int channel_id;
unsigned int reg;
};
-static struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = {
+static const struct ca0106_vol_tbl saved_volumes[NUM_SAVED_VOLUMES] = {
{ CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2 },
{ CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2 },
{ CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2 },
@@ -953,4 +899,4 @@ void snd_ca0106_mixer_resume(struct snd_ca0106 *chip)
if (chip->details->i2c_adc)
ca0106_set_capture_mic_line_in(chip);
}
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c
index ba96428c9f4c..c181e4954579 100644
--- a/sound/pci/ca0106/ca0106_proc.c
+++ b/sound/pci/ca0106/ca0106_proc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
@@ -42,47 +43,30 @@
* 0.0.18
* Implement support for Line-in capture on SB Live 24bit.
*
- * This code was initally based on code from ALSA's emu10k1x.c which is:
+ * This code was initially based on code from ALSA's emu10k1x.c which is:
* Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/moduleparam.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/info.h>
#include <sound/asoundef.h>
-#include <asm/io.h>
#include "ca0106.h"
-#ifdef CONFIG_PROC_FS
-
struct snd_ca0106_category_str {
int val;
const char *name;
};
-static struct snd_ca0106_category_str snd_ca0106_con_category[] = {
+static const struct snd_ca0106_category_str snd_ca0106_con_category[] = {
{ IEC958_AES1_CON_DAT, "DAT" },
{ IEC958_AES1_CON_VCR, "VCR" },
{ IEC958_AES1_CON_MICROPHONE, "microphone" },
@@ -297,16 +281,14 @@ static void snd_ca0106_proc_reg_write32(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_ca0106 *emu = entry->private_data;
- unsigned long flags;
char line[64];
u32 reg, val;
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x", &reg, &val) != 2)
continue;
if (reg < 0x40 && val <= 0xffffffff) {
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
outl(val, emu->port + (reg & 0xfffffffc));
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
}
}
@@ -316,13 +298,13 @@ static void snd_ca0106_proc_reg_read32(struct snd_info_entry *entry,
{
struct snd_ca0106 *emu = entry->private_data;
unsigned long value;
- unsigned long flags;
int i;
+
snd_iprintf(buffer, "Registers:\n\n");
for(i = 0; i < 0x20; i+=4) {
- spin_lock_irqsave(&emu->emu_lock, flags);
- value = inl(emu->port + i);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ scoped_guard(spinlock_irqsave, &emu->emu_lock) {
+ value = inl(emu->port + i);
+ }
snd_iprintf(buffer, "Register %02X: %08lX\n", i, value);
}
}
@@ -332,13 +314,13 @@ static void snd_ca0106_proc_reg_read16(struct snd_info_entry *entry,
{
struct snd_ca0106 *emu = entry->private_data;
unsigned int value;
- unsigned long flags;
int i;
+
snd_iprintf(buffer, "Registers:\n\n");
for(i = 0; i < 0x20; i+=2) {
- spin_lock_irqsave(&emu->emu_lock, flags);
- value = inw(emu->port + i);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ scoped_guard(spinlock_irqsave, &emu->emu_lock) {
+ value = inw(emu->port + i);
+ }
snd_iprintf(buffer, "Register %02X: %04X\n", i, value);
}
}
@@ -348,13 +330,13 @@ static void snd_ca0106_proc_reg_read8(struct snd_info_entry *entry,
{
struct snd_ca0106 *emu = entry->private_data;
unsigned int value;
- unsigned long flags;
int i;
+
snd_iprintf(buffer, "Registers:\n\n");
for(i = 0; i < 0x20; i+=1) {
- spin_lock_irqsave(&emu->emu_lock, flags);
- value = inb(emu->port + i);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ scoped_guard(spinlock_irqsave, &emu->emu_lock) {
+ value = inb(emu->port + i);
+ }
snd_iprintf(buffer, "Register %02X: %02X\n", i, value);
}
}
@@ -424,34 +406,22 @@ static void snd_ca0106_proc_i2c_write(struct snd_info_entry *entry,
}
}
-int __devinit snd_ca0106_proc_init(struct snd_ca0106 * emu)
+int snd_ca0106_proc_init(struct snd_ca0106 *emu)
{
- struct snd_info_entry *entry;
-
- if(! snd_card_proc_new(emu->card, "iec958", &entry))
- snd_info_set_text_ops(entry, emu, snd_ca0106_proc_iec958);
- if(! snd_card_proc_new(emu->card, "ca0106_reg32", &entry)) {
- snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read32);
- entry->c.text.write = snd_ca0106_proc_reg_write32;
- entry->mode |= S_IWUSR;
- }
- if(! snd_card_proc_new(emu->card, "ca0106_reg16", &entry))
- snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read16);
- if(! snd_card_proc_new(emu->card, "ca0106_reg8", &entry))
- snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read8);
- if(! snd_card_proc_new(emu->card, "ca0106_regs1", &entry)) {
- snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read1);
- entry->c.text.write = snd_ca0106_proc_reg_write;
- entry->mode |= S_IWUSR;
- }
- if(! snd_card_proc_new(emu->card, "ca0106_i2c", &entry)) {
- entry->c.text.write = snd_ca0106_proc_i2c_write;
- entry->private_data = emu;
- entry->mode |= S_IWUSR;
- }
- if(! snd_card_proc_new(emu->card, "ca0106_regs2", &entry))
- snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read2);
+ snd_card_ro_proc_new(emu->card, "iec958", emu, snd_ca0106_proc_iec958);
+ snd_card_rw_proc_new(emu->card, "ca0106_reg32", emu,
+ snd_ca0106_proc_reg_read32,
+ snd_ca0106_proc_reg_write32);
+ snd_card_ro_proc_new(emu->card, "ca0106_reg16", emu,
+ snd_ca0106_proc_reg_read16);
+ snd_card_ro_proc_new(emu->card, "ca0106_reg8", emu,
+ snd_ca0106_proc_reg_read8);
+ snd_card_rw_proc_new(emu->card, "ca0106_regs1", emu,
+ snd_ca0106_proc_reg_read1,
+ snd_ca0106_proc_reg_write);
+ snd_card_rw_proc_new(emu->card, "ca0106_i2c", emu, NULL,
+ snd_ca0106_proc_i2c_write);
+ snd_card_ro_proc_new(emu->card, "ca0106_regs2", emu,
+ snd_ca0106_proc_reg_read2);
return 0;
}
-
-#endif /* CONFIG_PROC_FS */
diff --git a/sound/pci/ca0106/ca_midi.c b/sound/pci/ca0106/ca_midi.c
index c7885117da33..6efd93abddb5 100644
--- a/sound/pci/ca0106/ca_midi.c
+++ b/sound/pci/ca0106/ca_midi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 10/16/2005 Tilman Kranz <tilde@tk-sls.de>
* Creative Audio MIDI, for the CA0106 Driver
@@ -8,22 +9,6 @@
* tested with ca0106.
* mpu401: Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* emu10k1x: Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- *
*/
#include <linux/spinlock.h>
@@ -46,7 +31,7 @@ static void ca_midi_clear_rx(struct snd_ca_midi *midi)
ca_midi_read_data(midi);
#ifdef CONFIG_SND_DEBUG
if (timeout <= 0)
- snd_printk(KERN_ERR "ca_midi_clear_rx: timeout (status = 0x%x)\n",
+ pr_err("ca_midi_clear_rx: timeout (status = 0x%x)\n",
ca_midi_read_stat(midi));
#endif
}
@@ -60,60 +45,56 @@ static void ca_midi_interrupt(struct snd_ca_midi *midi, unsigned int status)
return;
}
- spin_lock(&midi->input_lock);
- if ((status & midi->ipr_rx) && ca_midi_input_avail(midi)) {
- if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
- ca_midi_clear_rx(midi);
- } else {
- byte = ca_midi_read_data(midi);
- if(midi->substream_input)
- snd_rawmidi_receive(midi->substream_input, &byte, 1);
-
-
+ scoped_guard(spinlock, &midi->input_lock) {
+ if ((status & midi->ipr_rx) && ca_midi_input_avail(midi)) {
+ if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
+ ca_midi_clear_rx(midi);
+ } else {
+ byte = ca_midi_read_data(midi);
+ if (midi->substream_input)
+ snd_rawmidi_receive(midi->substream_input, &byte, 1);
+ }
}
}
- spin_unlock(&midi->input_lock);
- spin_lock(&midi->output_lock);
- if ((status & midi->ipr_tx) && ca_midi_output_ready(midi)) {
- if (midi->substream_output &&
- snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
- ca_midi_write_data(midi, byte);
- } else {
- midi->interrupt_disable(midi,midi->tx_enable);
+ scoped_guard(spinlock, &midi->output_lock) {
+ if ((status & midi->ipr_tx) && ca_midi_output_ready(midi)) {
+ if (midi->substream_output &&
+ snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
+ ca_midi_write_data(midi, byte);
+ } else {
+ midi->interrupt_disable(midi, midi->tx_enable);
+ }
}
}
- spin_unlock(&midi->output_lock);
-
}
static void ca_midi_cmd(struct snd_ca_midi *midi, unsigned char cmd, int ack)
{
- unsigned long flags;
int timeout, ok;
- spin_lock_irqsave(&midi->input_lock, flags);
- ca_midi_write_data(midi, 0x00);
- /* ca_midi_clear_rx(midi); */
-
- ca_midi_write_cmd(midi, cmd);
- if (ack) {
- ok = 0;
- timeout = 10000;
- while (!ok && timeout-- > 0) {
- if (ca_midi_input_avail(midi)) {
- if (ca_midi_read_data(midi) == midi->ack)
- ok = 1;
+ scoped_guard(spinlock_irqsave, &midi->input_lock) {
+ ca_midi_write_data(midi, 0x00);
+ /* ca_midi_clear_rx(midi); */
+
+ ca_midi_write_cmd(midi, cmd);
+ if (ack) {
+ ok = 0;
+ timeout = 10000;
+ while (!ok && timeout-- > 0) {
+ if (ca_midi_input_avail(midi)) {
+ if (ca_midi_read_data(midi) == midi->ack)
+ ok = 1;
+ }
}
- }
- if (!ok && ca_midi_read_data(midi) == midi->ack)
+ if (!ok && ca_midi_read_data(midi) == midi->ack)
+ ok = 1;
+ } else {
ok = 1;
- } else {
- ok = 1;
+ }
}
- spin_unlock_irqrestore(&midi->input_lock, flags);
if (!ok)
- snd_printk(KERN_ERR "ca_midi_cmd: 0x%x failed at 0x%x (status = 0x%x, data = 0x%x)!!!\n",
+ pr_err("ca_midi_cmd: 0x%x failed at 0x%x (status = 0x%x, data = 0x%x)!!!\n",
cmd,
midi->get_dev_id_port(midi->dev_id),
ca_midi_read_stat(midi),
@@ -123,83 +104,69 @@ static void ca_midi_cmd(struct snd_ca_midi *midi, unsigned char cmd, int ack)
static int ca_midi_input_open(struct snd_rawmidi_substream *substream)
{
struct snd_ca_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
if (snd_BUG_ON(!midi->dev_id))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- midi->midi_mode |= CA_MIDI_MODE_INPUT;
- midi->substream_input = substream;
- if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- ca_midi_cmd(midi, midi->reset, 1);
- ca_midi_cmd(midi, midi->enter_uart, 1);
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &midi->open_lock) {
+ midi->midi_mode |= CA_MIDI_MODE_INPUT;
+ midi->substream_input = substream;
+ if (midi->midi_mode & CA_MIDI_MODE_OUTPUT)
+ return 0;
}
+ ca_midi_cmd(midi, midi->reset, 1);
+ ca_midi_cmd(midi, midi->enter_uart, 1);
return 0;
}
static int ca_midi_output_open(struct snd_rawmidi_substream *substream)
{
struct snd_ca_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
if (snd_BUG_ON(!midi->dev_id))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- midi->midi_mode |= CA_MIDI_MODE_OUTPUT;
- midi->substream_output = substream;
- if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- ca_midi_cmd(midi, midi->reset, 1);
- ca_midi_cmd(midi, midi->enter_uart, 1);
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &midi->open_lock) {
+ midi->midi_mode |= CA_MIDI_MODE_OUTPUT;
+ midi->substream_output = substream;
+ if (midi->midi_mode & CA_MIDI_MODE_INPUT)
+ return 0;
}
+ ca_midi_cmd(midi, midi->reset, 1);
+ ca_midi_cmd(midi, midi->enter_uart, 1);
return 0;
}
static int ca_midi_input_close(struct snd_rawmidi_substream *substream)
{
struct snd_ca_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
if (snd_BUG_ON(!midi->dev_id))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- midi->interrupt_disable(midi,midi->rx_enable);
- midi->midi_mode &= ~CA_MIDI_MODE_INPUT;
- midi->substream_input = NULL;
- if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- ca_midi_cmd(midi, midi->reset, 0);
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &midi->open_lock) {
+ midi->interrupt_disable(midi, midi->rx_enable);
+ midi->midi_mode &= ~CA_MIDI_MODE_INPUT;
+ midi->substream_input = NULL;
+ if (midi->midi_mode & CA_MIDI_MODE_OUTPUT)
+ return 0;
}
+ ca_midi_cmd(midi, midi->reset, 0);
return 0;
}
static int ca_midi_output_close(struct snd_rawmidi_substream *substream)
{
struct snd_ca_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
if (snd_BUG_ON(!midi->dev_id))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
-
- midi->interrupt_disable(midi,midi->tx_enable);
- midi->midi_mode &= ~CA_MIDI_MODE_OUTPUT;
- midi->substream_output = NULL;
-
- if (!(midi->midi_mode & CA_MIDI_MODE_INPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- ca_midi_cmd(midi, midi->reset, 0);
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &midi->open_lock) {
+ midi->interrupt_disable(midi, midi->tx_enable);
+ midi->midi_mode &= ~CA_MIDI_MODE_OUTPUT;
+ midi->substream_output = NULL;
+ if (midi->midi_mode & CA_MIDI_MODE_INPUT)
+ return 0;
}
+ ca_midi_cmd(midi, midi->reset, 0);
return 0;
}
@@ -220,7 +187,6 @@ static void ca_midi_input_trigger(struct snd_rawmidi_substream *substream, int u
static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
struct snd_ca_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
if (snd_BUG_ON(!midi->dev_id))
return;
@@ -229,25 +195,23 @@ static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int
int max = 4;
unsigned char byte;
- spin_lock_irqsave(&midi->output_lock, flags);
+ scoped_guard(spinlock_irqsave, &midi->output_lock) {
- /* try to send some amount of bytes here before interrupts */
- while (max > 0) {
- if (ca_midi_output_ready(midi)) {
- if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT) ||
- snd_rawmidi_transmit(substream, &byte, 1) != 1) {
- /* no more data */
- spin_unlock_irqrestore(&midi->output_lock, flags);
- return;
+ /* try to send some amount of bytes here before interrupts */
+ while (max > 0) {
+ if (ca_midi_output_ready(midi)) {
+ if (!(midi->midi_mode & CA_MIDI_MODE_OUTPUT) ||
+ snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+ /* no more data */
+ return;
+ }
+ ca_midi_write_data(midi, byte);
+ max--;
+ } else {
+ break;
}
- ca_midi_write_data(midi, byte);
- max--;
- } else {
- break;
}
}
-
- spin_unlock_irqrestore(&midi->output_lock, flags);
midi->interrupt_enable(midi,midi->tx_enable);
} else {
@@ -255,14 +219,14 @@ static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int
}
}
-static struct snd_rawmidi_ops ca_midi_output =
+static const struct snd_rawmidi_ops ca_midi_output =
{
.open = ca_midi_output_open,
.close = ca_midi_output_close,
.trigger = ca_midi_output_trigger,
};
-static struct snd_rawmidi_ops ca_midi_input =
+static const struct snd_rawmidi_ops ca_midi_input =
{
.open = ca_midi_input_open,
.close = ca_midi_input_close,
@@ -286,12 +250,13 @@ static void ca_rmidi_free(struct snd_rawmidi *rmidi)
ca_midi_free(rmidi->private_data);
}
-int __devinit ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name)
+int ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, char *name)
{
struct snd_rawmidi *rmidi;
int err;
- if ((err = snd_rawmidi_new(midi->get_dev_id_card(midi->dev_id), name, device, 1, 1, &rmidi)) < 0)
+ err = snd_rawmidi_new(midi->get_dev_id_card(midi->dev_id), name, device, 1, 1, &rmidi);
+ if (err < 0)
return err;
midi->dev_id = dev_id;
@@ -301,7 +266,7 @@ int __devinit ca_midi_init(void *dev_id, struct snd_ca_midi *midi, int device, c
spin_lock_init(&midi->input_lock);
spin_lock_init(&midi->output_lock);
- strcpy(rmidi->name, name);
+ strscpy(rmidi->name, name);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &ca_midi_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &ca_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
diff --git a/sound/pci/ca0106/ca_midi.h b/sound/pci/ca0106/ca_midi.h
index 922ed3e3731e..1d0476c8af62 100644
--- a/sound/pci/ca0106/ca_midi.h
+++ b/sound/pci/ca0106/ca_midi.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 10/16/2005 Tilman Kranz <tilde@tk-sls.de>
* Creative Audio MIDI, for the CA0106 Driver
@@ -5,21 +6,6 @@
*
* Changelog:
* See ca_midi.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/spinlock.h>
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 329968edca9b..0666be543474 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -1,33 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for C-Media CMI8338 and 8738 PCI soundcards.
* Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Does not work. Warning may block system in capture mode */
/* #define USE_VAR48KRATE */
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/gameport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/info.h>
@@ -43,21 +30,17 @@
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("C-Media CMI8x38 PCI");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8738},"
- "{C-Media,CMI8738B},"
- "{C-Media,CMI8338A},"
- "{C-Media,CMI8338B}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
-static long mpu_port[SNDRV_CARDS];
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
+static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
static long fm_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1};
-static int soft_ac3[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1};
+static bool soft_ac3[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1};
#ifdef SUPPORT_JOYSTICK
static int joystick_port[SNDRV_CARDS];
#endif
@@ -68,14 +51,14 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for C-Media PCI soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable C-Media PCI soundcard.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port.");
module_param_array(soft_ac3, bool, NULL, 0444);
-MODULE_PARM_DESC(soft_ac3, "Sofware-conversion of raw SPDIF packets (model 033 only).");
+MODULE_PARM_DESC(soft_ac3, "Software-conversion of raw SPDIF packets (model 033 only).");
#ifdef SUPPORT_JOYSTICK
-module_param_array(joystick_port, int, NULL, 0444);
+module_param_hw_array(joystick_port, int, ioport, NULL, 0444);
MODULE_PARM_DESC(joystick_port, "Joystick port address.");
#endif
@@ -315,7 +298,6 @@ MODULE_PARM_DESC(joystick_port, "Joystick port address.");
#define CM_MICGAINZ 0x01 /* mic boost */
#define CM_MICGAINZ_SHIFT 0
-#define CM_REG_MIXER3 0x24
#define CM_REG_AUX_VOL 0x26
#define CM_VAUXL_MASK 0xf0
#define CM_VAUXR_MASK 0x0f
@@ -504,10 +486,8 @@ struct cmipci {
spinlock_t reg_lock;
-#ifdef CONFIG_PM
unsigned int saved_regs[0x20];
unsigned char saved_mixers[0x20];
-#endif
};
@@ -599,7 +579,7 @@ static int snd_cmipci_clear_bit_b(struct cmipci *cm, unsigned int cmd, unsigned
* calculate frequency
*/
-static unsigned int rates[] = { 5512, 11025, 22050, 44100, 8000, 16000, 32000, 48000 };
+static const unsigned int rates[] = { 5512, 11025, 22050, 44100, 8000, 16000, 32000, 48000 };
static unsigned int snd_cmipci_rate_freq(unsigned int rate)
{
@@ -656,8 +636,8 @@ out:
}
/*
- * Program pll register bits, I assume that the 8 registers 0xf8 upto 0xff
- * are mapped onto the 8 ADC/DAC sampling frequency which can be choosen
+ * Program pll register bits, I assume that the 8 registers 0xf8 up to 0xff
+ * are mapped onto the 8 ADC/DAC sampling frequency which can be chosen
* at the register CM_REG_FUNCTRL1 (0x04).
* Problem: other ways are also possible (any information about that?)
*/
@@ -666,7 +646,7 @@ static void snd_cmipci_set_pll(struct cmipci *cm, unsigned int rate, unsigned in
unsigned int reg = CM_REG_PLL + slot;
/*
* Guess that this programs at reg. 0x04 the pos 15:13/12:10
- * for DSFC/ASFC (000 upto 111).
+ * for DSFC/ASFC (000 up to 111).
*/
/* FIXME: Init (Do we've to set an other register first before programming?) */
@@ -679,27 +659,18 @@ static void snd_cmipci_set_pll(struct cmipci *cm, unsigned int rate, unsigned in
}
#endif /* USE_VAR48KRATE */
-static int snd_cmipci_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
static int snd_cmipci_playback2_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct cmipci *cm = snd_pcm_substream_chip(substream);
if (params_channels(hw_params) > 2) {
- mutex_lock(&cm->open_mutex);
- if (cm->opened[CM_CH_PLAY]) {
- mutex_unlock(&cm->open_mutex);
+ guard(mutex)(&cm->open_mutex);
+ if (cm->opened[CM_CH_PLAY])
return -EBUSY;
- }
/* reserve the channel A */
cm->opened[CM_CH_PLAY] = CM_OPEN_PLAYBACK_MULTI;
- mutex_unlock(&cm->open_mutex);
}
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ return 0;
}
static void snd_cmipci_ch_reset(struct cmipci *cm, int ch)
@@ -710,27 +681,22 @@ static void snd_cmipci_ch_reset(struct cmipci *cm, int ch)
udelay(10);
}
-static int snd_cmipci_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
/*
*/
-static unsigned int hw_channels[] = {1, 2, 4, 6, 8};
-static struct snd_pcm_hw_constraint_list hw_constraints_channels_4 = {
+static const unsigned int hw_channels[] = {1, 2, 4, 6, 8};
+static const struct snd_pcm_hw_constraint_list hw_constraints_channels_4 = {
.count = 3,
.list = hw_channels,
.mask = 0,
};
-static struct snd_pcm_hw_constraint_list hw_constraints_channels_6 = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_channels_6 = {
.count = 4,
.list = hw_channels,
.mask = 0,
};
-static struct snd_pcm_hw_constraint_list hw_constraints_channels_8 = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_channels_8 = {
.count = 5,
.list = hw_channels,
.mask = 0,
@@ -746,7 +712,7 @@ static int set_dac_channels(struct cmipci *cm, struct cmipci_pcm *rec, int chann
}
if (cm->can_multi_ch) {
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
if (channels > 2) {
snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG);
snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC);
@@ -769,7 +735,6 @@ static int set_dac_channels(struct cmipci *cm, struct cmipci_pcm *rec, int chann
snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);
else
snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);
- spin_unlock_irq(&cm->reg_lock);
}
return 0;
}
@@ -796,7 +761,7 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
if (runtime->channels > 1)
rec->fmt |= 0x01;
if (rec->is_dac && set_dac_channels(cm, rec, runtime->channels) < 0) {
- snd_printd("cannot set dac channels\n");
+ dev_dbg(cm->card->dev, "cannot set dac channels\n");
return -EINVAL;
}
@@ -810,7 +775,7 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
period_size = (period_size * runtime->channels) / 2;
}
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
/* set buffer address */
reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1;
@@ -827,7 +792,7 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
else
cm->ctrl |= val;
snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
- //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl);
+ /* dev_dbg(cm->card->dev, "functrl0 = %08x\n", cm->ctrl); */
/* set sample rate */
freq = 0;
@@ -850,7 +815,7 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
val |= (freq << CM_ASFC_SHIFT) & CM_ASFC_MASK;
}
snd_cmipci_write(cm, CM_REG_FUNCTRL1, val);
- //snd_printd("cmipci: functrl1 = %08x\n", val);
+ dev_dbg(cm->card->dev, "functrl1 = %08x\n", val);
/* set format */
val = snd_cmipci_read(cm, CM_REG_CHFORMAT);
@@ -866,7 +831,7 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
val |= freq_ext << (rec->ch * 2);
}
snd_cmipci_write(cm, CM_REG_CHFORMAT, val);
- //snd_printd("cmipci: chformat = %08x\n", val);
+ dev_dbg(cm->card->dev, "chformat = %08x\n", val);
if (!rec->is_dac && cm->chip_version) {
if (runtime->rate > 44100)
@@ -876,7 +841,6 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,
}
rec->running = 0;
- spin_unlock_irq(&cm->reg_lock);
return 0;
}
@@ -888,14 +852,13 @@ static int snd_cmipci_pcm_trigger(struct cmipci *cm, struct cmipci_pcm *rec,
int cmd)
{
unsigned int inthld, chen, reset, pause;
- int result = 0;
inthld = CM_CH0_INT_EN << rec->ch;
chen = CM_CHEN0 << rec->ch;
reset = CM_RST_CH0 << rec->ch;
pause = CM_PAUSE0 << rec->ch;
- spin_lock(&cm->reg_lock);
+ guard(spinlock)(&cm->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
rec->running = 1;
@@ -904,7 +867,7 @@ static int snd_cmipci_pcm_trigger(struct cmipci *cm, struct cmipci_pcm *rec,
cm->ctrl |= chen;
/* enable channel */
snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
- //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl);
+ dev_dbg(cm->card->dev, "functrl0 = %08x\n", cm->ctrl);
break;
case SNDRV_PCM_TRIGGER_STOP:
rec->running = 0;
@@ -927,11 +890,9 @@ static int snd_cmipci_pcm_trigger(struct cmipci *cm, struct cmipci_pcm *rec,
snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
break;
default:
- result = -EINVAL;
- break;
+ return -EINVAL;
}
- spin_unlock(&cm->reg_lock);
- return result;
+ return 0;
}
/*
@@ -952,7 +913,7 @@ static snd_pcm_uframes_t snd_cmipci_pcm_pointer(struct cmipci *cm, struct cmipci
if (rem < rec->dma_size)
goto ok;
}
- printk(KERN_ERR "cmipci: invalid PCM pointer: %#x\n", rem);
+ dev_err(cm->card->dev, "invalid PCM pointer: %#x\n", rem);
return SNDRV_PCM_POS_XRUN;
ok:
ptr = (rec->dma_size - (rem + 1)) >> rec->shift;
@@ -1021,10 +982,9 @@ static int snd_cmipci_spdif_default_get(struct snd_kcontrol *kcontrol,
struct cmipci *chip = snd_kcontrol_chip(kcontrol);
int i;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
for (i = 0; i < 4; i++)
ucontrol->value.iec958.status[i] = (chip->dig_status >> (i * 8)) & 0xff;
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -1036,16 +996,15 @@ static int snd_cmipci_spdif_default_put(struct snd_kcontrol *kcontrol,
unsigned int val;
val = 0;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
for (i = 0; i < 4; i++)
val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
change = val != chip->dig_status;
chip->dig_status = val;
- spin_unlock_irq(&chip->reg_lock);
return change;
}
-static struct snd_kcontrol_new snd_cmipci_spdif_default __devinitdata =
+static const struct snd_kcontrol_new snd_cmipci_spdif_default =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -1072,7 +1031,7 @@ static int snd_cmipci_spdif_mask_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_cmipci_spdif_mask __devinitdata =
+static const struct snd_kcontrol_new snd_cmipci_spdif_mask =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1095,10 +1054,9 @@ static int snd_cmipci_spdif_stream_get(struct snd_kcontrol *kcontrol,
struct cmipci *chip = snd_kcontrol_chip(kcontrol);
int i;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
for (i = 0; i < 4; i++)
ucontrol->value.iec958.status[i] = (chip->dig_pcm_status >> (i * 8)) & 0xff;
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -1110,16 +1068,15 @@ static int snd_cmipci_spdif_stream_put(struct snd_kcontrol *kcontrol,
unsigned int val;
val = 0;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
for (i = 0; i < 4; i++)
val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
change = val != chip->dig_pcm_status;
chip->dig_pcm_status = val;
- spin_unlock_irq(&chip->reg_lock);
return change;
}
-static struct snd_kcontrol_new snd_cmipci_spdif_stream __devinitdata =
+static const struct snd_kcontrol_new snd_cmipci_spdif_stream =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1139,7 +1096,7 @@ static int save_mixer_state(struct cmipci *cm)
struct snd_ctl_elem_value *val;
unsigned int i;
- val = kmalloc(sizeof(*val), GFP_ATOMIC);
+ val = kmalloc(sizeof(*val), GFP_KERNEL);
if (!val)
return -ENOMEM;
for (i = 0; i < CM_SAVED_MIXERS; i++) {
@@ -1253,11 +1210,13 @@ static int setup_spdif_playback(struct cmipci *cm, struct snd_pcm_substream *sub
rate = subs->runtime->rate;
- if (up && do_ac3)
- if ((err = save_mixer_state(cm)) < 0)
+ if (up && do_ac3) {
+ err = save_mixer_state(cm);
+ if (err < 0)
return err;
+ }
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
cm->spdif_playback_avail = up;
if (up) {
/* they are controlled via "IEC958 Output Switch" */
@@ -1283,7 +1242,6 @@ static int setup_spdif_playback(struct cmipci *cm, struct snd_pcm_substream *sub
snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF);
setup_ac3(cm, subs, 0, 0);
}
- spin_unlock_irq(&cm->reg_lock);
return 0;
}
@@ -1304,7 +1262,8 @@ static int snd_cmipci_playback_prepare(struct snd_pcm_substream *substream)
substream->runtime->channels == 2);
if (do_spdif && cm->can_ac3_hw)
do_ac3 = cm->dig_pcm_status & IEC958_AES0_NONAUDIO;
- if ((err = setup_spdif_playback(cm, substream, do_spdif, do_ac3)) < 0)
+ err = setup_spdif_playback(cm, substream, do_spdif, do_ac3);
+ if (err < 0)
return err;
return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream);
}
@@ -1319,7 +1278,8 @@ static int snd_cmipci_playback_spdif_prepare(struct snd_pcm_substream *substream
do_ac3 = cm->dig_pcm_status & IEC958_AES0_NONAUDIO;
else
do_ac3 = 1; /* doesn't matter */
- if ((err = setup_spdif_playback(cm, substream, 1, do_ac3)) < 0)
+ err = setup_spdif_playback(cm, substream, 1, do_ac3);
+ if (err < 0)
return err;
return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream);
}
@@ -1347,32 +1307,32 @@ static void snd_cmipci_silence_hack(struct cmipci *cm, struct cmipci_pcm *rec)
/* configure for 16 bits, 2 channels, 8 kHz */
if (runtime->channels > 2)
set_dac_channels(cm, rec, 2);
- spin_lock_irq(&cm->reg_lock);
- val = snd_cmipci_read(cm, CM_REG_FUNCTRL1);
- val &= ~(CM_ASFC_MASK << (rec->ch * 3));
- val |= (4 << CM_ASFC_SHIFT) << (rec->ch * 3);
- snd_cmipci_write(cm, CM_REG_FUNCTRL1, val);
- val = snd_cmipci_read(cm, CM_REG_CHFORMAT);
- val &= ~(CM_CH0FMT_MASK << (rec->ch * 2));
- val |= (3 << CM_CH0FMT_SHIFT) << (rec->ch * 2);
- if (cm->can_96k)
- val &= ~(CM_CH0_SRATE_MASK << (rec->ch * 2));
- snd_cmipci_write(cm, CM_REG_CHFORMAT, val);
+ scoped_guard(spinlock_irq, &cm->reg_lock) {
+ val = snd_cmipci_read(cm, CM_REG_FUNCTRL1);
+ val &= ~(CM_ASFC_MASK << (rec->ch * 3));
+ val |= (4 << CM_ASFC_SHIFT) << (rec->ch * 3);
+ snd_cmipci_write(cm, CM_REG_FUNCTRL1, val);
+ val = snd_cmipci_read(cm, CM_REG_CHFORMAT);
+ val &= ~(CM_CH0FMT_MASK << (rec->ch * 2));
+ val |= (3 << CM_CH0FMT_SHIFT) << (rec->ch * 2);
+ if (cm->can_96k)
+ val &= ~(CM_CH0_SRATE_MASK << (rec->ch * 2));
+ snd_cmipci_write(cm, CM_REG_CHFORMAT, val);
- /* start stream (we don't need interrupts) */
- cm->ctrl |= CM_CHEN0 << rec->ch;
- snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
- spin_unlock_irq(&cm->reg_lock);
+ /* start stream (we don't need interrupts) */
+ cm->ctrl |= CM_CHEN0 << rec->ch;
+ snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
+ }
msleep(1);
/* stop and reset stream */
- spin_lock_irq(&cm->reg_lock);
- cm->ctrl &= ~(CM_CHEN0 << rec->ch);
- val = CM_RST_CH0 << rec->ch;
- snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | val);
- snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~val);
- spin_unlock_irq(&cm->reg_lock);
+ scoped_guard(spinlock_irq, &cm->reg_lock) {
+ cm->ctrl &= ~(CM_CHEN0 << rec->ch);
+ val = CM_RST_CH0 << rec->ch;
+ snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | val);
+ snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~val);
+ }
rec->needs_silencing = 0;
}
@@ -1384,14 +1344,14 @@ static int snd_cmipci_playback_hw_free(struct snd_pcm_substream *substream)
setup_spdif_playback(cm, substream, 0, 0);
restore_mixer_state(cm);
snd_cmipci_silence_hack(cm, &cm->channel[0]);
- return snd_cmipci_hw_free(substream);
+ return 0;
}
static int snd_cmipci_playback2_hw_free(struct snd_pcm_substream *substream)
{
struct cmipci *cm = snd_pcm_substream_chip(substream);
snd_cmipci_silence_hack(cm, &cm->channel[1]);
- return snd_cmipci_hw_free(substream);
+ return 0;
}
/* capture */
@@ -1406,20 +1366,19 @@ static int snd_cmipci_capture_spdif_prepare(struct snd_pcm_substream *substream)
{
struct cmipci *cm = snd_pcm_substream_chip(substream);
- spin_lock_irq(&cm->reg_lock);
- snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF);
- if (cm->can_96k) {
- if (substream->runtime->rate > 48000)
- snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS);
+ scoped_guard(spinlock_irq, &cm->reg_lock) {
+ snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF);
+ if (cm->can_96k) {
+ if (substream->runtime->rate > 48000)
+ snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS);
+ else
+ snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS);
+ }
+ if (snd_pcm_format_width(substream->runtime->format) > 16)
+ snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
else
- snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS);
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
}
- if (snd_pcm_format_width(substream->runtime->format) > 16)
- snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
- else
- snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
-
- spin_unlock_irq(&cm->reg_lock);
return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream);
}
@@ -1428,12 +1387,11 @@ static int snd_cmipci_capture_spdif_hw_free(struct snd_pcm_substream *subs)
{
struct cmipci *cm = snd_pcm_substream_chip(subs);
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF);
snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL);
- spin_unlock_irq(&cm->reg_lock);
- return snd_cmipci_hw_free(subs);
+ return 0;
}
@@ -1451,14 +1409,14 @@ static irqreturn_t snd_cmipci_interrupt(int irq, void *dev_id)
return IRQ_NONE;
/* acknowledge interrupt */
- spin_lock(&cm->reg_lock);
- if (status & CM_CHINT0)
- mask |= CM_CH0_INT_EN;
- if (status & CM_CHINT1)
- mask |= CM_CH1_INT_EN;
- snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, mask);
- snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, mask);
- spin_unlock(&cm->reg_lock);
+ scoped_guard(spinlock, &cm->reg_lock) {
+ if (status & CM_CHINT0)
+ mask |= CM_CH0_INT_EN;
+ if (status & CM_CHINT1)
+ mask |= CM_CH1_INT_EN;
+ snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, mask);
+ snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, mask);
+ }
if (cm->rmidi && (status & CM_UARTINT))
snd_mpu401_uart_interrupt(irq, cm->rmidi->private_data);
@@ -1477,7 +1435,7 @@ static irqreturn_t snd_cmipci_interrupt(int irq, void *dev_id)
*/
/* playback on channel A */
-static struct snd_pcm_hardware snd_cmipci_playback =
+static const struct snd_pcm_hardware snd_cmipci_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
@@ -1497,7 +1455,7 @@ static struct snd_pcm_hardware snd_cmipci_playback =
};
/* capture on channel B */
-static struct snd_pcm_hardware snd_cmipci_capture =
+static const struct snd_pcm_hardware snd_cmipci_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
@@ -1517,7 +1475,7 @@ static struct snd_pcm_hardware snd_cmipci_capture =
};
/* playback on channel B - stereo 16bit only? */
-static struct snd_pcm_hardware snd_cmipci_playback2 =
+static const struct snd_pcm_hardware snd_cmipci_playback2 =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
@@ -1537,7 +1495,7 @@ static struct snd_pcm_hardware snd_cmipci_playback2 =
};
/* spdif playback on channel A */
-static struct snd_pcm_hardware snd_cmipci_playback_spdif =
+static const struct snd_pcm_hardware snd_cmipci_playback_spdif =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
@@ -1557,7 +1515,7 @@ static struct snd_pcm_hardware snd_cmipci_playback_spdif =
};
/* spdif playback on channel A (32bit, IEC958 subframes) */
-static struct snd_pcm_hardware snd_cmipci_playback_iec958_subframe =
+static const struct snd_pcm_hardware snd_cmipci_playback_iec958_subframe =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
@@ -1577,7 +1535,7 @@ static struct snd_pcm_hardware snd_cmipci_playback_iec958_subframe =
};
/* spdif capture on channel B */
-static struct snd_pcm_hardware snd_cmipci_capture_spdif =
+static const struct snd_pcm_hardware snd_cmipci_capture_spdif =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
@@ -1597,14 +1555,6 @@ static struct snd_pcm_hardware snd_cmipci_capture_spdif =
.fifo_size = 0,
};
-static unsigned int rate_constraints[] = { 5512, 8000, 11025, 16000, 22050,
- 32000, 44100, 48000, 88200, 96000, 128000 };
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
- .count = ARRAY_SIZE(rate_constraints),
- .list = rate_constraints,
- .mask = 0,
-};
-
/*
* check device open/close
*/
@@ -1617,21 +1567,17 @@ static int open_device_check(struct cmipci *cm, int mode, struct snd_pcm_substre
* pcm framework doesn't pass file pointer before actually opened,
* we can't know whether blocking mode or not in open callback..
*/
- mutex_lock(&cm->open_mutex);
- if (cm->opened[ch]) {
- mutex_unlock(&cm->open_mutex);
+ guard(mutex)(&cm->open_mutex);
+ if (cm->opened[ch])
return -EBUSY;
- }
cm->opened[ch] = mode;
cm->channel[ch].substream = subs;
if (! (mode & CM_OPEN_DAC)) {
/* disable dual DAC mode */
cm->channel[ch].is_dac = 0;
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC);
- spin_unlock_irq(&cm->reg_lock);
}
- mutex_unlock(&cm->open_mutex);
return 0;
}
@@ -1639,7 +1585,7 @@ static void close_device_check(struct cmipci *cm, int mode)
{
int ch = mode & CM_OPEN_CH_MASK;
- mutex_lock(&cm->open_mutex);
+ guard(mutex)(&cm->open_mutex);
if (cm->opened[ch] == mode) {
if (cm->channel[ch].substream) {
snd_cmipci_ch_reset(cm, ch);
@@ -1650,12 +1596,10 @@ static void close_device_check(struct cmipci *cm, int mode)
if (! cm->channel[ch].is_dac) {
/* enable dual DAC mode again */
cm->channel[ch].is_dac = 1;
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC);
- spin_unlock_irq(&cm->reg_lock);
}
}
- mutex_unlock(&cm->open_mutex);
}
/*
@@ -1667,7 +1611,8 @@ static int snd_cmipci_playback_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- if ((err = open_device_check(cm, CM_OPEN_PLAYBACK, substream)) < 0)
+ err = open_device_check(cm, CM_OPEN_PLAYBACK, substream);
+ if (err < 0)
return err;
runtime->hw = snd_cmipci_playback;
if (cm->chip_version == 68) {
@@ -1675,11 +1620,9 @@ static int snd_cmipci_playback_open(struct snd_pcm_substream *substream)
SNDRV_PCM_RATE_96000;
runtime->hw.rate_max = 96000;
} else if (cm->chip_version == 55) {
- err = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
- if (err < 0)
- return err;
- runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
+ runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_128000;
runtime->hw.rate_max = 128000;
}
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
@@ -1693,18 +1636,17 @@ static int snd_cmipci_capture_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- if ((err = open_device_check(cm, CM_OPEN_CAPTURE, substream)) < 0)
+ err = open_device_check(cm, CM_OPEN_CAPTURE, substream);
+ if (err < 0)
return err;
runtime->hw = snd_cmipci_capture;
if (cm->chip_version == 68) { // 8768 only supports 44k/48k recording
runtime->hw.rate_min = 41000;
runtime->hw.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000;
} else if (cm->chip_version == 55) {
- err = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
- if (err < 0)
- return err;
- runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
+ runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_128000;
runtime->hw.rate_max = 128000;
}
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
@@ -1717,10 +1659,12 @@ static int snd_cmipci_playback2_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- if ((err = open_device_check(cm, CM_OPEN_PLAYBACK2, substream)) < 0) /* use channel B */
+ /* use channel B */
+ err = open_device_check(cm, CM_OPEN_PLAYBACK2, substream);
+ if (err < 0)
return err;
runtime->hw = snd_cmipci_playback2;
- mutex_lock(&cm->open_mutex);
+ guard(mutex)(&cm->open_mutex);
if (! cm->opened[CM_CH_PLAY]) {
if (cm->can_multi_ch) {
runtime->hw.channels_max = cm->max_channels;
@@ -1732,17 +1676,14 @@ static int snd_cmipci_playback2_open(struct snd_pcm_substream *substream)
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_8);
}
}
- mutex_unlock(&cm->open_mutex);
if (cm->chip_version == 68) {
runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
SNDRV_PCM_RATE_96000;
runtime->hw.rate_max = 96000;
} else if (cm->chip_version == 55) {
- err = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
- if (err < 0)
- return err;
- runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
+ runtime->hw.rates |= SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_128000;
runtime->hw.rate_max = 128000;
}
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000);
@@ -1755,7 +1696,9 @@ static int snd_cmipci_playback_spdif_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- if ((err = open_device_check(cm, CM_OPEN_SPDIF_PLAYBACK, substream)) < 0) /* use channel A */
+ /* use channel A */
+ err = open_device_check(cm, CM_OPEN_SPDIF_PLAYBACK, substream);
+ if (err < 0)
return err;
if (cm->can_ac3_hw) {
runtime->hw = snd_cmipci_playback_spdif;
@@ -1782,7 +1725,9 @@ static int snd_cmipci_capture_spdif_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- if ((err = open_device_check(cm, CM_OPEN_SPDIF_CAPTURE, substream)) < 0) /* use channel B */
+ /* use channel B */
+ err = open_device_check(cm, CM_OPEN_SPDIF_CAPTURE, substream);
+ if (err < 0)
return err;
runtime->hw = snd_cmipci_capture_spdif;
if (cm->can_96k && !(cm->chip_version == 68)) {
@@ -1838,32 +1783,26 @@ static int snd_cmipci_capture_spdif_close(struct snd_pcm_substream *substream)
/*
*/
-static struct snd_pcm_ops snd_cmipci_playback_ops = {
+static const struct snd_pcm_ops snd_cmipci_playback_ops = {
.open = snd_cmipci_playback_open,
.close = snd_cmipci_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_cmipci_hw_params,
.hw_free = snd_cmipci_playback_hw_free,
.prepare = snd_cmipci_playback_prepare,
.trigger = snd_cmipci_playback_trigger,
.pointer = snd_cmipci_playback_pointer,
};
-static struct snd_pcm_ops snd_cmipci_capture_ops = {
+static const struct snd_pcm_ops snd_cmipci_capture_ops = {
.open = snd_cmipci_capture_open,
.close = snd_cmipci_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_cmipci_hw_params,
- .hw_free = snd_cmipci_hw_free,
.prepare = snd_cmipci_capture_prepare,
.trigger = snd_cmipci_capture_trigger,
.pointer = snd_cmipci_capture_pointer,
};
-static struct snd_pcm_ops snd_cmipci_playback2_ops = {
+static const struct snd_pcm_ops snd_cmipci_playback2_ops = {
.open = snd_cmipci_playback2_open,
.close = snd_cmipci_playback2_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cmipci_playback2_hw_params,
.hw_free = snd_cmipci_playback2_hw_free,
.prepare = snd_cmipci_capture_prepare, /* channel B */
@@ -1871,22 +1810,18 @@ static struct snd_pcm_ops snd_cmipci_playback2_ops = {
.pointer = snd_cmipci_capture_pointer, /* channel B */
};
-static struct snd_pcm_ops snd_cmipci_playback_spdif_ops = {
+static const struct snd_pcm_ops snd_cmipci_playback_spdif_ops = {
.open = snd_cmipci_playback_spdif_open,
.close = snd_cmipci_playback_spdif_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_cmipci_hw_params,
.hw_free = snd_cmipci_playback_hw_free,
.prepare = snd_cmipci_playback_spdif_prepare, /* set up rate */
.trigger = snd_cmipci_playback_trigger,
.pointer = snd_cmipci_playback_pointer,
};
-static struct snd_pcm_ops snd_cmipci_capture_spdif_ops = {
+static const struct snd_pcm_ops snd_cmipci_capture_spdif_ops = {
.open = snd_cmipci_capture_spdif_open,
.close = snd_cmipci_capture_spdif_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_cmipci_hw_params,
.hw_free = snd_cmipci_capture_spdif_hw_free,
.prepare = snd_cmipci_capture_spdif_prepare,
.trigger = snd_cmipci_capture_trigger,
@@ -1897,7 +1832,7 @@ static struct snd_pcm_ops snd_cmipci_capture_spdif_ops = {
/*
*/
-static int __devinit snd_cmipci_pcm_new(struct cmipci *cm, int device)
+static int snd_cmipci_pcm_new(struct cmipci *cm, int device)
{
struct snd_pcm *pcm;
int err;
@@ -1911,16 +1846,16 @@ static int __devinit snd_cmipci_pcm_new(struct cmipci *cm, int device)
pcm->private_data = cm;
pcm->info_flags = 0;
- strcpy(pcm->name, "C-Media PCI DAC/ADC");
+ strscpy(pcm->name, "C-Media PCI DAC/ADC");
cm->pcm = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(cm->pci), 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &cm->pci->dev, 64*1024, 128*1024);
return 0;
}
-static int __devinit snd_cmipci_pcm2_new(struct cmipci *cm, int device)
+static int snd_cmipci_pcm2_new(struct cmipci *cm, int device)
{
struct snd_pcm *pcm;
int err;
@@ -1933,16 +1868,16 @@ static int __devinit snd_cmipci_pcm2_new(struct cmipci *cm, int device)
pcm->private_data = cm;
pcm->info_flags = 0;
- strcpy(pcm->name, "C-Media PCI 2nd DAC");
+ strscpy(pcm->name, "C-Media PCI 2nd DAC");
cm->pcm2 = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(cm->pci), 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &cm->pci->dev, 64*1024, 128*1024);
return 0;
}
-static int __devinit snd_cmipci_pcm_spdif_new(struct cmipci *cm, int device)
+static int snd_cmipci_pcm_spdif_new(struct cmipci *cm, int device)
{
struct snd_pcm *pcm;
int err;
@@ -1956,11 +1891,17 @@ static int __devinit snd_cmipci_pcm_spdif_new(struct cmipci *cm, int device)
pcm->private_data = cm;
pcm->info_flags = 0;
- strcpy(pcm->name, "C-Media PCI IEC958");
+ strscpy(pcm->name, "C-Media PCI IEC958");
cm->pcm_spdif = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(cm->pci), 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &cm->pci->dev, 64*1024, 128*1024);
+
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps, cm->max_channels, 0,
+ NULL);
+ if (err < 0)
+ return err;
return 0;
}
@@ -2047,7 +1988,7 @@ static int snd_cmipci_get_volume(struct snd_kcontrol *kcontrol,
int val;
cmipci_sb_reg_decode(&reg, kcontrol->private_value);
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
val = (snd_cmipci_mixer_read(cm, reg.left_reg) >> reg.left_shift) & reg.mask;
if (reg.invert)
val = reg.mask - val;
@@ -2056,9 +1997,8 @@ static int snd_cmipci_get_volume(struct snd_kcontrol *kcontrol,
val = (snd_cmipci_mixer_read(cm, reg.right_reg) >> reg.right_shift) & reg.mask;
if (reg.invert)
val = reg.mask - val;
- ucontrol->value.integer.value[1] = val;
+ ucontrol->value.integer.value[1] = val;
}
- spin_unlock_irq(&cm->reg_lock);
return 0;
}
@@ -2082,7 +2022,7 @@ static int snd_cmipci_put_volume(struct snd_kcontrol *kcontrol,
right <<= reg.right_shift;
} else
right = 0;
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
oleft = snd_cmipci_mixer_read(cm, reg.left_reg);
left |= oleft & ~(reg.mask << reg.left_shift);
change = left != oleft;
@@ -2097,7 +2037,6 @@ static int snd_cmipci_put_volume(struct snd_kcontrol *kcontrol,
snd_cmipci_mixer_write(cm, reg.right_reg, right);
} else
snd_cmipci_mixer_write(cm, reg.left_reg, left);
- spin_unlock_irq(&cm->reg_lock);
return change;
}
@@ -2129,10 +2068,9 @@ static int snd_cmipci_get_input_sw(struct snd_kcontrol *kcontrol,
int val1, val2;
cmipci_sb_reg_decode(&reg, kcontrol->private_value);
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
val1 = snd_cmipci_mixer_read(cm, reg.left_reg);
val2 = snd_cmipci_mixer_read(cm, reg.right_reg);
- spin_unlock_irq(&cm->reg_lock);
ucontrol->value.integer.value[0] = (val1 >> reg.left_shift) & 1;
ucontrol->value.integer.value[1] = (val2 >> reg.left_shift) & 1;
ucontrol->value.integer.value[2] = (val1 >> reg.right_shift) & 1;
@@ -2149,7 +2087,7 @@ static int snd_cmipci_put_input_sw(struct snd_kcontrol *kcontrol,
int val1, val2, oval1, oval2;
cmipci_sb_reg_decode(&reg, kcontrol->private_value);
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
oval1 = snd_cmipci_mixer_read(cm, reg.left_reg);
oval2 = snd_cmipci_mixer_read(cm, reg.right_reg);
val1 = oval1 & ~((1 << reg.left_shift) | (1 << reg.right_shift));
@@ -2161,7 +2099,6 @@ static int snd_cmipci_put_input_sw(struct snd_kcontrol *kcontrol,
change = val1 != oval1 || val2 != oval2;
snd_cmipci_mixer_write(cm, reg.left_reg, val1);
snd_cmipci_mixer_write(cm, reg.right_reg, val2);
- spin_unlock_irq(&cm->reg_lock);
return change;
}
@@ -2219,7 +2156,7 @@ static int snd_cmipci_get_native_mixer(struct snd_kcontrol *kcontrol,
unsigned char oreg, val;
cmipci_sb_reg_decode(&reg, kcontrol->private_value);
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
oreg = inb(cm->iobase + reg.left_reg);
val = (oreg >> reg.left_shift) & reg.mask;
if (reg.invert)
@@ -2231,7 +2168,6 @@ static int snd_cmipci_get_native_mixer(struct snd_kcontrol *kcontrol,
val = reg.mask - val;
ucontrol->value.integer.value[1] = val;
}
- spin_unlock_irq(&cm->reg_lock);
return 0;
}
@@ -2243,7 +2179,7 @@ static int snd_cmipci_put_native_mixer(struct snd_kcontrol *kcontrol,
unsigned char oreg, nreg, val;
cmipci_sb_reg_decode(&reg, kcontrol->private_value);
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
oreg = inb(cm->iobase + reg.left_reg);
val = ucontrol->value.integer.value[0] & reg.mask;
if (reg.invert)
@@ -2258,7 +2194,6 @@ static int snd_cmipci_put_native_mixer(struct snd_kcontrol *kcontrol,
nreg |= (val << reg.right_shift);
}
outb(nreg, cm->iobase + reg.left_reg);
- spin_unlock_irq(&cm->reg_lock);
return (nreg != oreg);
}
@@ -2284,7 +2219,7 @@ static int snd_cmipci_put_native_mixer_sensitive(struct snd_kcontrol *kcontrol,
}
-static struct snd_kcontrol_new snd_cmipci_mixers[] __devinitdata = {
+static const struct snd_kcontrol_new snd_cmipci_mixers[] = {
CMIPCI_SB_VOL_STEREO("Master Playback Volume", SB_DSP4_MASTER_DEV, 3, 31),
CMIPCI_MIXER_SW_MONO("3D Control - Switch", CM_REG_MIXER1, CM_X3DEN_SHIFT, 0),
CMIPCI_SB_VOL_STEREO("PCM Playback Volume", SB_DSP4_PCM_DEV, 3, 31),
@@ -2345,10 +2280,9 @@ static int _snd_cmipci_uswitch_get(struct snd_kcontrol *kcontrol,
unsigned int val;
struct cmipci *cm = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
if (args->ac3_sensitive && cm->mixer_insensitive) {
ucontrol->value.integer.value[0] = 0;
- spin_unlock_irq(&cm->reg_lock);
return 0;
}
if (args->is_byte)
@@ -2356,7 +2290,6 @@ static int _snd_cmipci_uswitch_get(struct snd_kcontrol *kcontrol,
else
val = snd_cmipci_read(cm, args->reg);
ucontrol->value.integer.value[0] = ((val & args->mask) == args->mask_on) ? 1 : 0;
- spin_unlock_irq(&cm->reg_lock);
return 0;
}
@@ -2378,10 +2311,9 @@ static int _snd_cmipci_uswitch_put(struct snd_kcontrol *kcontrol,
int change;
struct cmipci *cm = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
if (args->ac3_sensitive && cm->mixer_insensitive) {
/* ignored */
- spin_unlock_irq(&cm->reg_lock);
return 0;
}
if (args->is_byte)
@@ -2401,7 +2333,6 @@ static int _snd_cmipci_uswitch_put(struct snd_kcontrol *kcontrol,
else
snd_cmipci_write(cm, args->reg, val);
}
- spin_unlock_irq(&cm->reg_lock);
return change;
}
@@ -2507,14 +2438,12 @@ static int snd_cmipci_line_in_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct cmipci *cm = snd_kcontrol_chip(kcontrol);
- static char *texts[3] = { "Line-In", "Rear Output", "Bass Output" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = cm->chip_version >= 39 ? 3 : 2;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ static const char *const texts[3] = {
+ "Line-In", "Rear Output", "Bass Output"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1,
+ cm->chip_version >= 39 ? 3 : 2, texts);
}
static inline unsigned int get_line_in_mode(struct cmipci *cm)
@@ -2536,9 +2465,8 @@ static int snd_cmipci_line_in_mode_get(struct snd_kcontrol *kcontrol,
{
struct cmipci *cm = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
ucontrol->value.enumerated.item[0] = get_line_in_mode(cm);
- spin_unlock_irq(&cm->reg_lock);
return 0;
}
@@ -2548,7 +2476,7 @@ static int snd_cmipci_line_in_mode_put(struct snd_kcontrol *kcontrol,
struct cmipci *cm = snd_kcontrol_chip(kcontrol);
int change;
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
if (ucontrol->value.enumerated.item[0] == 2)
change = snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_CENTR2LIN | CM_BASE2LIN);
else
@@ -2557,32 +2485,26 @@ static int snd_cmipci_line_in_mode_put(struct snd_kcontrol *kcontrol,
change |= snd_cmipci_set_bit_b(cm, CM_REG_MIXER1, CM_REAR2LIN);
else
change |= snd_cmipci_clear_bit_b(cm, CM_REG_MIXER1, CM_REAR2LIN);
- spin_unlock_irq(&cm->reg_lock);
return change;
}
static int snd_cmipci_mic_in_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "Mic-In", "Center/LFE Output" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ static const char *const texts[2] = { "Mic-In", "Center/LFE Output" };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_cmipci_mic_in_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct cmipci *cm = snd_kcontrol_chip(kcontrol);
+
/* same bit as spdi_phase */
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
ucontrol->value.enumerated.item[0] =
(snd_cmipci_read_b(cm, CM_REG_MISC) & CM_SPDIF_INVERSE) ? 1 : 0;
- spin_unlock_irq(&cm->reg_lock);
return 0;
}
@@ -2592,17 +2514,16 @@ static int snd_cmipci_mic_in_mode_put(struct snd_kcontrol *kcontrol,
struct cmipci *cm = snd_kcontrol_chip(kcontrol);
int change;
- spin_lock_irq(&cm->reg_lock);
+ guard(spinlock_irq)(&cm->reg_lock);
if (ucontrol->value.enumerated.item[0])
change = snd_cmipci_set_bit_b(cm, CM_REG_MISC, CM_SPDIF_INVERSE);
else
change = snd_cmipci_clear_bit_b(cm, CM_REG_MISC, CM_SPDIF_INVERSE);
- spin_unlock_irq(&cm->reg_lock);
return change;
}
/* both for CM8338/8738 */
-static struct snd_kcontrol_new snd_cmipci_mixer_switches[] __devinitdata = {
+static const struct snd_kcontrol_new snd_cmipci_mixer_switches[] = {
DEFINE_MIXER_SWITCH("Four Channel Mode", fourch),
{
.name = "Line-In Mode",
@@ -2614,11 +2535,11 @@ static struct snd_kcontrol_new snd_cmipci_mixer_switches[] __devinitdata = {
};
/* for non-multichannel chips */
-static struct snd_kcontrol_new snd_cmipci_nomulti_switch __devinitdata =
+static const struct snd_kcontrol_new snd_cmipci_nomulti_switch =
DEFINE_MIXER_SWITCH("Exchange DAC", exchange_dac);
/* only for CM8738 */
-static struct snd_kcontrol_new snd_cmipci_8738_mixer_switches[] __devinitdata = {
+static const struct snd_kcontrol_new snd_cmipci_8738_mixer_switches[] = {
#if 0 /* controlled in pcm device */
DEFINE_MIXER_SWITCH("IEC958 In Record", spdif_in),
DEFINE_MIXER_SWITCH("IEC958 Out", spdif_out),
@@ -2640,14 +2561,14 @@ static struct snd_kcontrol_new snd_cmipci_8738_mixer_switches[] __devinitdata =
};
/* only for model 033/037 */
-static struct snd_kcontrol_new snd_cmipci_old_mixer_switches[] __devinitdata = {
+static const struct snd_kcontrol_new snd_cmipci_old_mixer_switches[] = {
DEFINE_MIXER_SWITCH("IEC958 Mix Analog", spdif_dac_out),
DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase),
DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel1),
};
/* only for model 039 or later */
-static struct snd_kcontrol_new snd_cmipci_extra_mixer_switches[] __devinitdata = {
+static const struct snd_kcontrol_new snd_cmipci_extra_mixer_switches[] = {
DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel2),
DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase2),
{
@@ -2660,14 +2581,14 @@ static struct snd_kcontrol_new snd_cmipci_extra_mixer_switches[] __devinitdata =
};
/* card control switches */
-static struct snd_kcontrol_new snd_cmipci_modem_switch __devinitdata =
+static const struct snd_kcontrol_new snd_cmipci_modem_switch =
DEFINE_CARD_SWITCH("Modem", modem);
-static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device)
+static int snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device)
{
struct snd_card *card;
- struct snd_kcontrol_new *sw;
+ const struct snd_kcontrol_new *sw;
struct snd_kcontrol *kctl;
unsigned int idx;
int err;
@@ -2677,11 +2598,11 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic
card = cm->card;
- strcpy(card->mixername, "CMedia PCI");
+ strscpy(card->mixername, "CMedia PCI");
- spin_lock_irq(&cm->reg_lock);
- snd_cmipci_mixer_write(cm, 0x00, 0x00); /* mixer reset */
- spin_unlock_irq(&cm->reg_lock);
+ scoped_guard(spinlock_irq, &cm->reg_lock) {
+ snd_cmipci_mixer_write(cm, 0x00, 0x00); /* mixer reset */
+ }
for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_mixers); idx++) {
if (cm->chip_version == 68) { // 8768 has no PCM volume
@@ -2689,7 +2610,8 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic
"PCM Playback Volume"))
continue;
}
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cmipci_mixers[idx], cm))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cmipci_mixers[idx], cm));
+ if (err < 0)
return err;
}
@@ -2714,15 +2636,21 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic
return err;
}
if (cm->can_ac3_hw) {
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_default, cm))) < 0)
- return err;
+ kctl = snd_ctl_new1(&snd_cmipci_spdif_default, cm);
kctl->id.device = pcm_spdif_device;
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_mask, cm))) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
+ kctl = snd_ctl_new1(&snd_cmipci_spdif_mask, cm);
kctl->id.device = pcm_spdif_device;
- if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_stream, cm))) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
+ kctl = snd_ctl_new1(&snd_cmipci_spdif_stream, cm);
kctl->id.device = pcm_spdif_device;
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
}
if (cm->chip_version <= 37) {
sw = snd_cmipci_old_mixer_switches;
@@ -2755,12 +2683,8 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic
}
for (idx = 0; idx < CM_SAVED_MIXERS; idx++) {
- struct snd_ctl_elem_id elem_id;
struct snd_kcontrol *ctl;
- memset(&elem_id, 0, sizeof(elem_id));
- elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(elem_id.name, cm_saved_mixer[idx].name);
- ctl = snd_ctl_find_id(cm->card, &elem_id);
+ ctl = snd_ctl_find_id_mixer(cm->card, cm_saved_mixer[idx].name);
if (ctl)
cm->mixer_res_ctl[idx] = ctl;
}
@@ -2773,7 +2697,6 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic
* proc interface
*/
-#ifdef CONFIG_PROC_FS
static void snd_cmipci_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -2792,19 +2715,12 @@ static void snd_cmipci_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "\n");
}
-static void __devinit snd_cmipci_proc_init(struct cmipci *cm)
+static void snd_cmipci_proc_init(struct cmipci *cm)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(cm->card, "cmipci", &entry))
- snd_info_set_text_ops(entry, cm, snd_cmipci_proc_read);
+ snd_card_ro_proc_new(cm->card, "cmipci", cm, snd_cmipci_proc_read);
}
-#else /* !CONFIG_PROC_FS */
-static inline void snd_cmipci_proc_init(struct cmipci *cm) {}
-#endif
-
-static DEFINE_PCI_DEVICE_TABLE(snd_cmipci_ids) = {
+static const struct pci_device_id snd_cmipci_ids[] = {
{PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A), 0},
{PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B), 0},
{PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738), 0},
@@ -2818,7 +2734,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_cmipci_ids) = {
* check chip version and capabilities
* driver name is modified according to the chip model
*/
-static void __devinit query_chip(struct cmipci *cm)
+static void query_chip(struct cmipci *cm)
{
unsigned int detect;
@@ -2867,9 +2783,9 @@ static void __devinit query_chip(struct cmipci *cm)
}
#ifdef SUPPORT_JOYSTICK
-static int __devinit snd_cmipci_create_gameport(struct cmipci *cm, int dev)
+static int snd_cmipci_create_gameport(struct cmipci *cm, int dev)
{
- static int ports[] = { 0x201, 0x200, 0 }; /* FIXME: majority is 0x201? */
+ static const int ports[] = { 0x201, 0x200, 0 }; /* FIXME: majority is 0x201? */
struct gameport *gp;
struct resource *r = NULL;
int i, io_port = 0;
@@ -2880,31 +2796,31 @@ static int __devinit snd_cmipci_create_gameport(struct cmipci *cm, int dev)
if (joystick_port[dev] == 1) { /* auto-detect */
for (i = 0; ports[i]; i++) {
io_port = ports[i];
- r = request_region(io_port, 1, "CMIPCI gameport");
+ r = devm_request_region(&cm->pci->dev, io_port, 1,
+ "CMIPCI gameport");
if (r)
break;
}
} else {
io_port = joystick_port[dev];
- r = request_region(io_port, 1, "CMIPCI gameport");
+ r = devm_request_region(&cm->pci->dev, io_port, 1,
+ "CMIPCI gameport");
}
if (!r) {
- printk(KERN_WARNING "cmipci: cannot reserve joystick ports\n");
+ dev_warn(cm->card->dev, "cannot reserve joystick ports\n");
return -EBUSY;
}
cm->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "cmipci: cannot allocate memory for gameport\n");
- release_and_free_resource(r);
+ dev_err(cm->card->dev, "cannot allocate memory for gameport\n");
return -ENOMEM;
}
gameport_set_name(gp, "C-Media Gameport");
gameport_set_phys(gp, "pci%s/gameport0", pci_name(cm->pci));
gameport_set_dev_parent(gp, &cm->pci->dev);
gp->io = io_port;
- gameport_set_port_data(gp, r);
snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_JYSTK_EN);
@@ -2916,13 +2832,10 @@ static int __devinit snd_cmipci_create_gameport(struct cmipci *cm, int dev)
static void snd_cmipci_free_gameport(struct cmipci *cm)
{
if (cm->gameport) {
- struct resource *r = gameport_get_port_data(cm->gameport);
-
gameport_unregister_port(cm->gameport);
cm->gameport = NULL;
snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_JYSTK_EN);
- release_and_free_resource(r);
}
}
#else
@@ -2930,37 +2843,25 @@ static inline int snd_cmipci_create_gameport(struct cmipci *cm, int dev) { retur
static inline void snd_cmipci_free_gameport(struct cmipci *cm) { }
#endif
-static int snd_cmipci_free(struct cmipci *cm)
+static void snd_cmipci_free(struct snd_card *card)
{
- if (cm->irq >= 0) {
- snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN);
- snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT);
- snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0); /* disable ints */
- snd_cmipci_ch_reset(cm, CM_CH_PLAY);
- snd_cmipci_ch_reset(cm, CM_CH_CAPT);
- snd_cmipci_write(cm, CM_REG_FUNCTRL0, 0); /* disable channels */
- snd_cmipci_write(cm, CM_REG_FUNCTRL1, 0);
+ struct cmipci *cm = card->private_data;
- /* reset mixer */
- snd_cmipci_mixer_write(cm, 0, 0);
+ snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN);
+ snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT);
+ snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0); /* disable ints */
+ snd_cmipci_ch_reset(cm, CM_CH_PLAY);
+ snd_cmipci_ch_reset(cm, CM_CH_CAPT);
+ snd_cmipci_write(cm, CM_REG_FUNCTRL0, 0); /* disable channels */
+ snd_cmipci_write(cm, CM_REG_FUNCTRL1, 0);
- free_irq(cm->irq, cm);
- }
+ /* reset mixer */
+ snd_cmipci_mixer_write(cm, 0, 0);
snd_cmipci_free_gameport(cm);
- pci_release_regions(cm->pci);
- pci_disable_device(cm->pci);
- kfree(cm);
- return 0;
}
-static int snd_cmipci_dev_free(struct snd_device *device)
-{
- struct cmipci *cm = device->device_data;
- return snd_cmipci_free(cm);
-}
-
-static int __devinit snd_cmipci_create_fm(struct cmipci *cm, long fm_port)
+static int snd_cmipci_create_fm(struct cmipci *cm, long fm_port)
{
long iosynth;
unsigned int val;
@@ -2996,13 +2897,15 @@ static int __devinit snd_cmipci_create_fm(struct cmipci *cm, long fm_port)
if (snd_opl3_create(cm->card, iosynth, iosynth + 2,
OPL3_HW_OPL3, 0, &opl3) < 0) {
- printk(KERN_ERR "cmipci: no OPL device at %#lx, "
- "skipping...\n", iosynth);
+ dev_err(cm->card->dev,
+ "no OPL device at %#lx, skipping...\n",
+ iosynth);
goto disable_fm;
}
}
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
- printk(KERN_ERR "cmipci: cannot create OPL3 hwdep\n");
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0) {
+ dev_err(cm->card->dev, "cannot create OPL3 hwdep\n");
return err;
}
return 0;
@@ -3013,35 +2916,25 @@ static int __devinit snd_cmipci_create_fm(struct cmipci *cm, long fm_port)
return 0;
}
-static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pci,
- int dev, struct cmipci **rcmipci)
+static int snd_cmipci_create(struct snd_card *card, struct pci_dev *pci,
+ int dev)
{
- struct cmipci *cm;
+ struct cmipci *cm = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_cmipci_dev_free,
- };
unsigned int val;
long iomidi = 0;
int integrated_midi = 0;
char modelstr[16];
int pcm_index, pcm_spdif_index;
- static DEFINE_PCI_DEVICE_TABLE(intel_82437vx) = {
+ static const struct pci_device_id intel_82437vx[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437VX) },
{ },
};
- *rcmipci = NULL;
-
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- cm = kzalloc(sizeof(*cm), GFP_KERNEL);
- if (cm == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
spin_lock_init(&cm->reg_lock);
mutex_init(&cm->open_mutex);
cm->device = pci->device;
@@ -3052,20 +2945,19 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
cm->channel[1].ch = 1;
cm->channel[0].is_dac = cm->channel[1].is_dac = 1; /* dual DAC mode */
- if ((err = pci_request_regions(pci, card->driver)) < 0) {
- kfree(cm);
- pci_disable_device(pci);
+ err = pcim_request_all_regions(pci, card->driver);
+ if (err < 0)
return err;
- }
cm->iobase = pci_resource_start(pci, 0);
- if (request_irq(pci->irq, snd_cmipci_interrupt,
- IRQF_SHARED, card->driver, cm)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_cmipci_free(cm);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_cmipci_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, cm)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
cm->irq = pci->irq;
+ card->sync_irq = cm->irq;
+ card->private_free = snd_cmipci_free;
pci_set_master(cm->pci);
@@ -3081,11 +2973,12 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
pci->device != PCI_DEVICE_ID_CMEDIA_CM8338B)
query_chip(cm);
/* added -MCx suffix for chip supporting multi-channels */
- if (cm->can_multi_ch)
- sprintf(cm->card->driver + strlen(cm->card->driver),
- "-MC%d", cm->max_channels);
- else if (cm->can_ac3_sw)
- strcpy(cm->card->driver + strlen(cm->card->driver), "-SWIEC");
+ if (cm->can_multi_ch) {
+ int l = strlen(cm->card->driver);
+ scnprintf(cm->card->driver + l, sizeof(cm->card->driver) - l,
+ "-MC%d", cm->max_channels);
+ } else if (cm->can_ac3_sw)
+ strlcat(cm->card->driver, "-SWIEC", sizeof(cm->card->driver));
cm->dig_status = SNDRV_PCM_DEFAULT_CON_SPDIF;
cm->dig_pcm_status = SNDRV_PCM_DEFAULT_CON_SPDIF;
@@ -3157,23 +3050,21 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
}
}
}
- sprintf(card->shortname, "C-Media CMI%d", val);
+ sprintf(card->shortname, "C-Media CMI%u", val);
if (cm->chip_version < 68)
- sprintf(modelstr, " (model %d)", cm->chip_version);
+ scnprintf(modelstr, sizeof(modelstr),
+ " (model %d)", cm->chip_version);
else
modelstr[0] = '\0';
- sprintf(card->longname, "%s%s at %#lx, irq %i",
- card->shortname, modelstr, cm->iobase, cm->irq);
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, cm, &ops)) < 0) {
- snd_cmipci_free(cm);
- return err;
- }
+ scnprintf(card->longname, sizeof(card->longname),
+ "%s%s at %#lx, irq %i",
+ card->shortname, modelstr, cm->iobase, cm->irq);
if (cm->chip_version >= 39) {
val = snd_cmipci_read_b(cm, CM_REG_MPU_PCI + 1);
if (val != 0x00 && val != 0xff) {
- iomidi = cm->iobase + CM_REG_MPU_PCI;
+ if (mpu_port[dev])
+ iomidi = cm->iobase + CM_REG_MPU_PCI;
integrated_midi = 1;
}
}
@@ -3193,8 +3084,9 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
/* enable UART */
snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_UART_EN);
if (inb(iomidi + 1) == 0xff) {
- snd_printk(KERN_ERR "cannot enable MPU-401 port"
- " at %#lx\n", iomidi);
+ dev_err(cm->card->dev,
+ "cannot enable MPU-401 port at %#lx\n",
+ iomidi);
snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1,
CM_UART_EN);
iomidi = 0;
@@ -3215,30 +3107,36 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
/* create pcm devices */
pcm_index = pcm_spdif_index = 0;
- if ((err = snd_cmipci_pcm_new(cm, pcm_index)) < 0)
+ err = snd_cmipci_pcm_new(cm, pcm_index);
+ if (err < 0)
return err;
pcm_index++;
- if ((err = snd_cmipci_pcm2_new(cm, pcm_index)) < 0)
+ err = snd_cmipci_pcm2_new(cm, pcm_index);
+ if (err < 0)
return err;
pcm_index++;
if (cm->can_ac3_hw || cm->can_ac3_sw) {
pcm_spdif_index = pcm_index;
- if ((err = snd_cmipci_pcm_spdif_new(cm, pcm_index)) < 0)
+ err = snd_cmipci_pcm_spdif_new(cm, pcm_index);
+ if (err < 0)
return err;
}
/* create mixer interface & switches */
- if ((err = snd_cmipci_mixer_new(cm, pcm_spdif_index)) < 0)
+ err = snd_cmipci_mixer_new(cm, pcm_spdif_index);
+ if (err < 0)
return err;
if (iomidi > 0) {
- if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
- iomidi,
- (integrated_midi ?
- MPU401_INFO_INTEGRATED : 0),
- cm->irq, 0, &cm->rmidi)) < 0) {
- printk(KERN_ERR "cmipci: no UART401 device at 0x%lx\n", iomidi);
- }
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
+ iomidi,
+ (integrated_midi ?
+ MPU401_INFO_INTEGRATED : 0) |
+ MPU401_INFO_IRQ_HOOK,
+ -1, &cm->rmidi);
+ if (err < 0)
+ dev_err(cm->card->dev,
+ "no UART401 device at 0x%lx\n", iomidi);
}
#ifdef USE_VAR48KRATE
@@ -3254,9 +3152,6 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
if (snd_cmipci_create_gameport(cm, dev) < 0)
snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_JYSTK_EN);
- snd_card_set_dev(card, &pci->dev);
-
- *rcmipci = cm;
return 0;
}
@@ -3265,12 +3160,11 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
MODULE_DEVICE_TABLE(pci, snd_cmipci_ids);
-static int __devinit snd_cmipci_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_cmipci_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
- struct cmipci *cm;
int err;
if (dev >= SNDRV_CARDS)
@@ -3280,60 +3174,54 @@ static int __devinit snd_cmipci_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(struct cmipci), &card);
if (err < 0)
return err;
switch (pci->device) {
case PCI_DEVICE_ID_CMEDIA_CM8738:
case PCI_DEVICE_ID_CMEDIA_CM8738B:
- strcpy(card->driver, "CMI8738");
+ strscpy(card->driver, "CMI8738");
break;
case PCI_DEVICE_ID_CMEDIA_CM8338A:
case PCI_DEVICE_ID_CMEDIA_CM8338B:
- strcpy(card->driver, "CMI8338");
+ strscpy(card->driver, "CMI8338");
break;
default:
- strcpy(card->driver, "CMIPCI");
+ strscpy(card->driver, "CMIPCI");
break;
}
- if ((err = snd_cmipci_create(card, pci, dev, &cm)) < 0) {
- snd_card_free(card);
- return err;
- }
- card->private_data = cm;
+ err = snd_cmipci_create(card, pci, dev);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
- return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
+ error:
+ snd_card_free(card);
+ return err;
}
-static void __devexit snd_cmipci_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
-}
-
-
-#ifdef CONFIG_PM
/*
* power management
*/
-static unsigned char saved_regs[] = {
+static const unsigned char saved_regs[] = {
CM_REG_FUNCTRL1, CM_REG_CHFORMAT, CM_REG_LEGACY_CTRL, CM_REG_MISC_CTRL,
- CM_REG_MIXER0, CM_REG_MIXER1, CM_REG_MIXER2, CM_REG_MIXER3, CM_REG_PLL,
+ CM_REG_MIXER0, CM_REG_MIXER1, CM_REG_MIXER2, CM_REG_AUX_VOL, CM_REG_PLL,
CM_REG_CH0_FRAME1, CM_REG_CH0_FRAME2,
CM_REG_CH1_FRAME1, CM_REG_CH1_FRAME2, CM_REG_EXT_MISC,
CM_REG_INT_STATUS, CM_REG_INT_HLDCLR, CM_REG_FUNCTRL0,
};
-static unsigned char saved_mixers[] = {
+static const unsigned char saved_mixers[] = {
SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,
SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,
SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,
@@ -3344,18 +3232,14 @@ static unsigned char saved_mixers[] = {
SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,
};
-static int snd_cmipci_suspend(struct pci_dev *pci, pm_message_t state)
+static int snd_cmipci_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct cmipci *cm = card->private_data;
int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(cm->pcm);
- snd_pcm_suspend_all(cm->pcm2);
- snd_pcm_suspend_all(cm->pcm_spdif);
-
/* save registers */
for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
cm->saved_regs[i] = snd_cmipci_read(cm, saved_regs[i]);
@@ -3364,29 +3248,15 @@ static int snd_cmipci_suspend(struct pci_dev *pci, pm_message_t state)
/* disable ints */
snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_cmipci_resume(struct pci_dev *pci)
+static int snd_cmipci_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct cmipci *cm = card->private_data;
int i;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "cmipci: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
/* reset / initialize to a sane state */
snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0);
snd_cmipci_ch_reset(cm, CM_CH_PLAY);
@@ -3402,28 +3272,16 @@ static int snd_cmipci_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
-static struct pci_driver driver = {
- .name = "C-Media PCI",
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_cmipci_pm, snd_cmipci_suspend, snd_cmipci_resume);
+
+static struct pci_driver cmipci_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_cmipci_ids,
.probe = snd_cmipci_probe,
- .remove = __devexit_p(snd_cmipci_remove),
-#ifdef CONFIG_PM
- .suspend = snd_cmipci_suspend,
- .resume = snd_cmipci_resume,
-#endif
+ .driver = {
+ .pm = &snd_cmipci_pm,
+ },
};
-static int __init alsa_card_cmipci_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cmipci_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cmipci_init)
-module_exit(alsa_card_cmipci_exit)
+module_pci_driver(cmipci_driver);
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index 6772070ed492..d00b2c9fb1e3 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -1,32 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Cirrus Logic CS4281 based PCI soundcard
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/gameport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -40,12 +25,11 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Cirrus Logic CS4281");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Cirrus Logic,CS4281}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
-static int dual_codec[SNDRV_CARDS]; /* dual codec */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
+static bool dual_codec[SNDRV_CARDS]; /* dual codec */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for CS4281 soundcard.");
@@ -486,15 +470,12 @@ struct cs4281 {
struct gameport *gameport;
-#ifdef CONFIG_PM
u32 suspend_regs[SUSPEND_REGISTERS];
-#endif
-
};
static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id);
-static DEFINE_PCI_DEVICE_TABLE(snd_cs4281_ids) = {
+static const struct pci_device_id snd_cs4281_ids[] = {
{ PCI_VDEVICE(CIRRUS, 0x6005), 0, }, /* CS4281 */
{ 0, }
};
@@ -564,7 +545,8 @@ static void snd_cs4281_ac97_write(struct snd_ac97 *ac97,
return;
}
}
- snd_printk(KERN_ERR "AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val);
+ dev_err(chip->card->dev,
+ "AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val);
}
static unsigned short snd_cs4281_ac97_read(struct snd_ac97 *ac97,
@@ -624,7 +606,8 @@ static unsigned short snd_cs4281_ac97_read(struct snd_ac97 *ac97,
goto __ok1;
}
- snd_printk(KERN_ERR "AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg);
+ dev_err(chip->card->dev,
+ "AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg);
result = 0xffff;
goto __end;
@@ -643,7 +626,8 @@ static unsigned short snd_cs4281_ac97_read(struct snd_ac97 *ac97,
udelay(10);
}
- snd_printk(KERN_ERR "AC'97 read problem (ACSTS_VSTS), reg = 0x%x\n", reg);
+ dev_err(chip->card->dev,
+ "AC'97 read problem (ACSTS_VSTS), reg = 0x%x\n", reg);
result = 0xffff;
goto __end;
@@ -667,7 +651,7 @@ static int snd_cs4281_trigger(struct snd_pcm_substream *substream, int cmd)
struct cs4281_dma *dma = substream->runtime->private_data;
struct cs4281 *chip = snd_pcm_substream_chip(substream);
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
dma->valDCR |= BA0_DCR_MSK;
@@ -694,19 +678,17 @@ static int snd_cs4281_trigger(struct snd_pcm_substream *substream, int cmd)
dma->valFCR &= ~BA0_FCR_FEN;
break;
default:
- spin_unlock(&chip->reg_lock);
return -EINVAL;
}
snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR);
snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR);
snd_cs4281_pokeBA0(chip, dma->regDCR, dma->valDCR);
- spin_unlock(&chip->reg_lock);
return 0;
}
static unsigned int snd_cs4281_rate(unsigned int rate, unsigned int *real_rate)
{
- unsigned int val = ~0;
+ unsigned int val;
if (real_rate)
*real_rate = rate;
@@ -719,9 +701,8 @@ static unsigned int snd_cs4281_rate(unsigned int rate, unsigned int *real_rate)
case 44100: return 1;
case 48000: return 0;
default:
- goto __variable;
+ break;
}
- __variable:
val = 1536000 / rate;
if (real_rate)
*real_rate = 1536000 / val;
@@ -793,26 +774,14 @@ static void snd_cs4281_mode(struct cs4281 *chip, struct cs4281_dma *dma,
snd_cs4281_pokeBA0(chip, dma->regFSIC, 0);
}
-static int snd_cs4281_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_cs4281_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_cs4281_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct cs4281_dma *dma = runtime->private_data;
struct cs4281 *chip = snd_pcm_substream_chip(substream);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
snd_cs4281_mode(chip, dma, runtime, 0, 1);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -822,9 +791,8 @@ static int snd_cs4281_capture_prepare(struct snd_pcm_substream *substream)
struct cs4281_dma *dma = runtime->private_data;
struct cs4281 *chip = snd_pcm_substream_chip(substream);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
snd_cs4281_mode(chip, dma, runtime, 1, 1);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -835,15 +803,16 @@ static snd_pcm_uframes_t snd_cs4281_pointer(struct snd_pcm_substream *substream)
struct cs4281 *chip = snd_pcm_substream_chip(substream);
/*
- printk(KERN_DEBUG "DCC = 0x%x, buffer_size = 0x%x, jiffies = %li\n",
- snd_cs4281_peekBA0(chip, dma->regDCC), runtime->buffer_size,
+ dev_dbg(chip->card->dev,
+ "DCC = 0x%x, buffer_size = 0x%x, jiffies = %li\n",
+ snd_cs4281_peekBA0(chip, dma->regDCC), runtime->buffer_size,
jiffies);
*/
return runtime->buffer_size -
snd_cs4281_peekBA0(chip, dma->regDCC) - 1;
}
-static struct snd_pcm_hardware snd_cs4281_playback =
+static const struct snd_pcm_hardware snd_cs4281_playback =
{
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -868,7 +837,7 @@ static struct snd_pcm_hardware snd_cs4281_playback =
.fifo_size = CS4281_FIFO_SIZE,
};
-static struct snd_pcm_hardware snd_cs4281_capture =
+static const struct snd_pcm_hardware snd_cs4281_capture =
{
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -947,36 +916,27 @@ static int snd_cs4281_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_cs4281_playback_ops = {
+static const struct snd_pcm_ops snd_cs4281_playback_ops = {
.open = snd_cs4281_playback_open,
.close = snd_cs4281_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_cs4281_hw_params,
- .hw_free = snd_cs4281_hw_free,
.prepare = snd_cs4281_playback_prepare,
.trigger = snd_cs4281_trigger,
.pointer = snd_cs4281_pointer,
};
-static struct snd_pcm_ops snd_cs4281_capture_ops = {
+static const struct snd_pcm_ops snd_cs4281_capture_ops = {
.open = snd_cs4281_capture_open,
.close = snd_cs4281_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_cs4281_hw_params,
- .hw_free = snd_cs4281_hw_free,
.prepare = snd_cs4281_capture_prepare,
.trigger = snd_cs4281_trigger,
.pointer = snd_cs4281_pointer,
};
-static int __devinit snd_cs4281_pcm(struct cs4281 * chip, int device,
- struct snd_pcm ** rpcm)
+static int snd_cs4281_pcm(struct cs4281 *chip, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
err = snd_pcm_new(chip->card, "CS4281", device, 1, 1, &pcm);
if (err < 0)
return err;
@@ -986,14 +946,12 @@ static int __devinit snd_cs4281_pcm(struct cs4281 * chip, int device,
pcm->private_data = chip;
pcm->info_flags = 0;
- strcpy(pcm->name, "CS4281");
+ strscpy(pcm->name, "CS4281");
chip->pcm = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 512*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
+ 64*1024, 512*1024);
- if (rpcm)
- *rpcm = pcm;
return 0;
}
@@ -1056,7 +1014,7 @@ static int snd_cs4281_put_volume(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_SCALE(db_scale_dsp, -4650, 150, 0);
-static struct snd_kcontrol_new snd_cs4281_fm_vol =
+static const struct snd_kcontrol_new snd_cs4281_fm_vol =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Synth Playback Volume",
@@ -1067,7 +1025,7 @@ static struct snd_kcontrol_new snd_cs4281_fm_vol =
.tlv = { .p = db_scale_dsp },
};
-static struct snd_kcontrol_new snd_cs4281_pcm_vol =
+static const struct snd_kcontrol_new snd_cs4281_pcm_vol =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Stream Playback Volume",
@@ -1093,33 +1051,38 @@ static void snd_cs4281_mixer_free_ac97(struct snd_ac97 *ac97)
chip->ac97 = NULL;
}
-static int __devinit snd_cs4281_mixer(struct cs4281 * chip)
+static int snd_cs4281_mixer(struct cs4281 *chip)
{
struct snd_card *card = chip->card;
struct snd_ac97_template ac97;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_cs4281_ac97_write,
.read = snd_cs4281_ac97_read,
};
- if ((err = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+ err = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus);
+ if (err < 0)
return err;
chip->ac97_bus->private_free = snd_cs4281_mixer_free_ac97_bus;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
ac97.private_free = snd_cs4281_mixer_free_ac97;
- if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
+ err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97);
+ if (err < 0)
return err;
if (chip->dual_codec) {
ac97.num = 1;
- if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_secondary)) < 0)
+ err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_secondary);
+ if (err < 0)
return err;
}
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4281_fm_vol, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4281_fm_vol, chip));
+ if (err < 0)
return err;
- if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4281_pcm_vol, chip))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4281_pcm_vol, chip));
+ if (err < 0)
return err;
return 0;
}
@@ -1163,20 +1126,19 @@ static ssize_t snd_cs4281_BA1_read(struct snd_info_entry *entry,
return count;
}
-static struct snd_info_entry_ops snd_cs4281_proc_ops_BA0 = {
+static const struct snd_info_entry_ops snd_cs4281_proc_ops_BA0 = {
.read = snd_cs4281_BA0_read,
};
-static struct snd_info_entry_ops snd_cs4281_proc_ops_BA1 = {
+static const struct snd_info_entry_ops snd_cs4281_proc_ops_BA1 = {
.read = snd_cs4281_BA1_read,
};
-static void __devinit snd_cs4281_proc_init(struct cs4281 * chip)
+static void snd_cs4281_proc_init(struct cs4281 *chip)
{
struct snd_info_entry *entry;
- if (! snd_card_proc_new(chip->card, "cs4281", &entry))
- snd_info_set_text_ops(entry, chip, snd_cs4281_proc_read);
+ snd_card_ro_proc_new(chip->card, "cs4281", chip, snd_cs4281_proc_read);
if (! snd_card_proc_new(chip->card, "cs4281_BA0", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = chip;
@@ -1195,7 +1157,7 @@ static void __devinit snd_cs4281_proc_init(struct cs4281 * chip)
* joystick support
*/
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
static void snd_cs4281_gameport_trigger(struct gameport *gameport)
{
@@ -1259,13 +1221,14 @@ static int snd_cs4281_gameport_open(struct gameport *gameport, int mode)
return 0;
}
-static int __devinit snd_cs4281_create_gameport(struct cs4281 *chip)
+static int snd_cs4281_create_gameport(struct cs4281 *chip)
{
struct gameport *gp;
chip->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "cs4281: cannot allocate memory for gameport\n");
+ dev_err(chip->card->dev,
+ "cannot allocate memory for gameport\n");
return -ENOMEM;
}
@@ -1296,14 +1259,13 @@ static void snd_cs4281_free_gameport(struct cs4281 *chip)
#else
static inline int snd_cs4281_create_gameport(struct cs4281 *chip) { return -ENOSYS; }
static inline void snd_cs4281_free_gameport(struct cs4281 *chip) { }
-#endif /* CONFIG_GAMEPORT || (MODULE && CONFIG_GAMEPORT_MODULE) */
+#endif /* IS_REACHABLE(CONFIG_GAMEPORT) */
-static int snd_cs4281_free(struct cs4281 *chip)
+static void snd_cs4281_free(struct snd_card *card)
{
- snd_cs4281_free_gameport(chip);
+ struct cs4281 *chip = card->private_data;
- if (chip->irq >= 0)
- synchronize_irq(chip->irq);
+ snd_cs4281_free_gameport(chip);
/* Mask interrupts */
snd_cs4281_pokeBA0(chip, BA0_HIMR, 0x7fffffff);
@@ -1311,100 +1273,55 @@ static int snd_cs4281_free(struct cs4281 *chip)
snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0);
/* Sound System Power Management - Turn Everything OFF */
snd_cs4281_pokeBA0(chip, BA0_SSPM, 0);
- /* PCI interface - D3 state */
- pci_set_power_state(chip->pci, 3);
-
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- if (chip->ba0)
- iounmap(chip->ba0);
- if (chip->ba1)
- iounmap(chip->ba1);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
-
- kfree(chip);
- return 0;
-}
-
-static int snd_cs4281_dev_free(struct snd_device *device)
-{
- struct cs4281 *chip = device->device_data;
- return snd_cs4281_free(chip);
}
static int snd_cs4281_chip_init(struct cs4281 *chip); /* defined below */
-static int __devinit snd_cs4281_create(struct snd_card *card,
- struct pci_dev *pci,
- struct cs4281 ** rchip,
- int dual_codec)
+static int snd_cs4281_create(struct snd_card *card,
+ struct pci_dev *pci,
+ int dual_codec)
{
- struct cs4281 *chip;
- unsigned int tmp;
+ struct cs4281 *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_cs4281_dev_free,
- };
- *rchip = NULL;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
spin_lock_init(&chip->reg_lock);
chip->card = card;
chip->pci = pci;
chip->irq = -1;
pci_set_master(pci);
if (dual_codec < 0 || dual_codec > 3) {
- snd_printk(KERN_ERR "invalid dual_codec option %d\n", dual_codec);
+ dev_err(card->dev, "invalid dual_codec option %d\n", dual_codec);
dual_codec = 0;
}
chip->dual_codec = dual_codec;
- if ((err = pci_request_regions(pci, "CS4281")) < 0) {
- kfree(chip);
- pci_disable_device(pci);
- return err;
- }
+ chip->ba0 = pcim_iomap_region(pci, 0, "CS4281");
+ if (IS_ERR(chip->ba0))
+ return PTR_ERR(chip->ba0);
chip->ba0_addr = pci_resource_start(pci, 0);
- chip->ba1_addr = pci_resource_start(pci, 1);
- chip->ba0 = pci_ioremap_bar(pci, 0);
- chip->ba1 = pci_ioremap_bar(pci, 1);
- if (!chip->ba0 || !chip->ba1) {
- snd_cs4281_free(chip);
- return -ENOMEM;
- }
+ chip->ba1 = pcim_iomap_region(pci, 1, "CS4281");
+ if (IS_ERR(chip->ba1))
+ return PTR_ERR(chip->ba1);
+ chip->ba1_addr = pci_resource_start(pci, 1);
- if (request_irq(pci->irq, snd_cs4281_interrupt, IRQF_SHARED,
- "CS4281", chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_cs4281_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_cs4281_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -ENOMEM;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_cs4281_free;
- tmp = snd_cs4281_chip_init(chip);
- if (tmp) {
- snd_cs4281_free(chip);
- return tmp;
- }
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_cs4281_free(chip);
+ err = snd_cs4281_chip_init(chip);
+ if (err)
return err;
- }
snd_cs4281_proc_init(chip);
-
- snd_card_set_dev(card, &pci->dev);
-
- *rchip = chip;
return 0;
}
@@ -1425,7 +1342,8 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
snd_cs4281_pokeBA0(chip, BA0_CFLR, BA0_CFLR_DEFAULT);
tmp = snd_cs4281_peekBA0(chip, BA0_CFLR);
if (tmp != BA0_CFLR_DEFAULT) {
- snd_printk(KERN_ERR "CFLR setup failed (0x%x)\n", tmp);
+ dev_err(chip->card->dev,
+ "CFLR setup failed (0x%x)\n", tmp);
return -EIO;
}
}
@@ -1435,12 +1353,16 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
* space between 0e4h and 0ffh to be written. */
snd_cs4281_pokeBA0(chip, BA0_CWPR, 0x4281);
- if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC1)) != (BA0_SERC1_SO1EN | BA0_SERC1_AC97)) {
- snd_printk(KERN_ERR "SERC1 AC'97 check failed (0x%x)\n", tmp);
+ tmp = snd_cs4281_peekBA0(chip, BA0_SERC1);
+ if (tmp != (BA0_SERC1_SO1EN | BA0_SERC1_AC97)) {
+ dev_err(chip->card->dev,
+ "SERC1 AC'97 check failed (0x%x)\n", tmp);
return -EIO;
}
- if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC2)) != (BA0_SERC2_SI1EN | BA0_SERC2_AC97)) {
- snd_printk(KERN_ERR "SERC2 AC'97 check failed (0x%x)\n", tmp);
+ tmp = snd_cs4281_peekBA0(chip, BA0_SERC2);
+ if (tmp != (BA0_SERC2_SI1EN | BA0_SERC2_AC97)) {
+ dev_err(chip->card->dev,
+ "SERC2 AC'97 check failed (0x%x)\n", tmp);
return -EIO;
}
@@ -1502,7 +1424,7 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_ERR "DLLRDY not seen\n");
+ dev_err(chip->card->dev, "DLLRDY not seen\n");
return -EIO;
__ok0:
@@ -1528,7 +1450,9 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_ERR "never read codec ready from AC'97 (0x%x)\n", snd_cs4281_peekBA0(chip, BA0_ACSTS));
+ dev_err(chip->card->dev,
+ "never read codec ready from AC'97 (0x%x)\n",
+ snd_cs4281_peekBA0(chip, BA0_ACSTS));
return -EIO;
__ok1:
@@ -1539,7 +1463,8 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
goto __codec2_ok;
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_INFO "secondary codec doesn't respond. disable it...\n");
+ dev_info(chip->card->dev,
+ "secondary codec doesn't respond. disable it...\n");
chip->dual_codec = 0;
__codec2_ok: ;
}
@@ -1569,7 +1494,7 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
if (--retry_count > 0)
goto __retry;
- snd_printk(KERN_ERR "never read ISV3 and ISV4 from AC'97\n");
+ dev_err(chip->card->dev, "never read ISV3 and ISV4 from AC'97\n");
return -EIO;
__ok2:
@@ -1632,7 +1557,6 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
BA0_HISR_DMA(1) |
BA0_HISR_DMA(2) |
BA0_HISR_DMA(3)));
- synchronize_irq(chip->irq);
return 0;
}
@@ -1652,7 +1576,7 @@ static int snd_cs4281_midi_input_open(struct snd_rawmidi_substream *substream)
{
struct cs4281 *chip = substream->rmidi->private_data;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->midcr |= BA0_MIDCR_RXE;
chip->midi_input = substream;
if (!(chip->uartm & CS4281_MODE_OUTPUT)) {
@@ -1660,7 +1584,6 @@ static int snd_cs4281_midi_input_open(struct snd_rawmidi_substream *substream)
} else {
snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -1668,7 +1591,7 @@ static int snd_cs4281_midi_input_close(struct snd_rawmidi_substream *substream)
{
struct cs4281 *chip = substream->rmidi->private_data;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->midcr &= ~(BA0_MIDCR_RXE | BA0_MIDCR_RIE);
chip->midi_input = NULL;
if (!(chip->uartm & CS4281_MODE_OUTPUT)) {
@@ -1677,7 +1600,6 @@ static int snd_cs4281_midi_input_close(struct snd_rawmidi_substream *substream)
snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
chip->uartm &= ~CS4281_MODE_INPUT;
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -1685,7 +1607,7 @@ static int snd_cs4281_midi_output_open(struct snd_rawmidi_substream *substream)
{
struct cs4281 *chip = substream->rmidi->private_data;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->uartm |= CS4281_MODE_OUTPUT;
chip->midcr |= BA0_MIDCR_TXE;
chip->midi_output = substream;
@@ -1694,7 +1616,6 @@ static int snd_cs4281_midi_output_open(struct snd_rawmidi_substream *substream)
} else {
snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -1702,7 +1623,7 @@ static int snd_cs4281_midi_output_close(struct snd_rawmidi_substream *substream)
{
struct cs4281 *chip = substream->rmidi->private_data;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->midcr &= ~(BA0_MIDCR_TXE | BA0_MIDCR_TIE);
chip->midi_output = NULL;
if (!(chip->uartm & CS4281_MODE_INPUT)) {
@@ -1711,16 +1632,14 @@ static int snd_cs4281_midi_output_close(struct snd_rawmidi_substream *substream)
snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
chip->uartm &= ~CS4281_MODE_OUTPUT;
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
static void snd_cs4281_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct cs4281 *chip = substream->rmidi->private_data;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (up) {
if ((chip->midcr & BA0_MIDCR_RIE) == 0) {
chip->midcr |= BA0_MIDCR_RIE;
@@ -1732,16 +1651,14 @@ static void snd_cs4281_midi_input_trigger(struct snd_rawmidi_substream *substrea
snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static void snd_cs4281_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct cs4281 *chip = substream->rmidi->private_data;
unsigned char byte;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (up) {
if ((chip->midcr & BA0_MIDCR_TIE) == 0) {
chip->midcr |= BA0_MIDCR_TIE;
@@ -1762,41 +1679,36 @@ static void snd_cs4281_midi_output_trigger(struct snd_rawmidi_substream *substre
snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
-static struct snd_rawmidi_ops snd_cs4281_midi_output =
+static const struct snd_rawmidi_ops snd_cs4281_midi_output =
{
.open = snd_cs4281_midi_output_open,
.close = snd_cs4281_midi_output_close,
.trigger = snd_cs4281_midi_output_trigger,
};
-static struct snd_rawmidi_ops snd_cs4281_midi_input =
+static const struct snd_rawmidi_ops snd_cs4281_midi_input =
{
.open = snd_cs4281_midi_input_open,
.close = snd_cs4281_midi_input_close,
.trigger = snd_cs4281_midi_input_trigger,
};
-static int __devinit snd_cs4281_midi(struct cs4281 * chip, int device,
- struct snd_rawmidi **rrawmidi)
+static int snd_cs4281_midi(struct cs4281 *chip, int device)
{
struct snd_rawmidi *rmidi;
int err;
- if (rrawmidi)
- *rrawmidi = NULL;
- if ((err = snd_rawmidi_new(chip->card, "CS4281", device, 1, 1, &rmidi)) < 0)
+ err = snd_rawmidi_new(chip->card, "CS4281", device, 1, 1, &rmidi);
+ if (err < 0)
return err;
- strcpy(rmidi->name, "CS4281");
+ strscpy(rmidi->name, "CS4281");
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs4281_midi_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs4281_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
rmidi->private_data = chip;
chip->rmidi = rmidi;
- if (rrawmidi)
- *rrawmidi = rmidi;
return 0;
}
@@ -1819,10 +1731,12 @@ static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id)
}
if (status & (BA0_HISR_DMA(0)|BA0_HISR_DMA(1)|BA0_HISR_DMA(2)|BA0_HISR_DMA(3))) {
- for (dma = 0; dma < 4; dma++)
+ for (dma = 0; dma < 4; dma++) {
+ bool period_elapsed = false;
+ cdma = &chip->dma[dma];
+
if (status & BA0_HISR_DMA(dma)) {
- cdma = &chip->dma[dma];
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
/* ack DMA IRQ */
val = snd_cs4281_peekBA0(chip, cdma->regHDSR);
/* workaround, sometimes CS4281 acknowledges */
@@ -1831,24 +1745,24 @@ static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id)
if ((val & BA0_HDSR_DHTC) && !(cdma->frag & 1)) {
cdma->frag--;
chip->spurious_dhtc_irq++;
- spin_unlock(&chip->reg_lock);
continue;
}
if ((val & BA0_HDSR_DTC) && (cdma->frag & 1)) {
cdma->frag--;
chip->spurious_dtc_irq++;
- spin_unlock(&chip->reg_lock);
continue;
}
- spin_unlock(&chip->reg_lock);
- snd_pcm_period_elapsed(cdma->substream);
+ period_elapsed = true;
}
+ if (period_elapsed)
+ snd_pcm_period_elapsed(cdma->substream);
+ }
}
if ((status & BA0_HISR_MIDI) && chip->rmidi) {
unsigned char c;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_RBE) == 0) {
c = snd_cs4281_peekBA0(chip, BA0_MIDRP);
if ((chip->midcr & BA0_MIDCR_RIE) == 0)
@@ -1865,7 +1779,6 @@ static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id)
}
snd_cs4281_pokeBA0(chip, BA0_MIDWP, c);
}
- spin_unlock(&chip->reg_lock);
}
/* EOI to the PCI part... reenables interrupts */
@@ -1881,7 +1794,6 @@ static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id)
static void snd_cs4281_opl3_command(struct snd_opl3 *opl3, unsigned short cmd,
unsigned char val)
{
- unsigned long flags;
struct cs4281 *chip = opl3->private_data;
void __iomem *port;
@@ -1890,19 +1802,17 @@ static void snd_cs4281_opl3_command(struct snd_opl3 *opl3, unsigned short cmd,
else
port = chip->ba0 + BA0_B0AP; /* left port */
- spin_lock_irqsave(&opl3->reg_lock, flags);
+ guard(spinlock_irqsave)(&opl3->reg_lock);
writel((unsigned int)cmd, port);
udelay(10);
writel((unsigned int)val, port + 4);
udelay(30);
-
- spin_unlock_irqrestore(&opl3->reg_lock, flags);
}
-static int __devinit snd_cs4281_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_cs4281_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -1917,69 +1827,61 @@ static int __devinit snd_cs4281_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- if ((err = snd_cs4281_create(card, pci, &chip, dual_codec[dev])) < 0) {
- snd_card_free(card);
+ err = snd_cs4281_create(card, pci, dual_codec[dev]);
+ if (err < 0)
return err;
- }
- card->private_data = chip;
- if ((err = snd_cs4281_mixer(chip)) < 0) {
- snd_card_free(card);
+ err = snd_cs4281_mixer(chip);
+ if (err < 0)
return err;
- }
- if ((err = snd_cs4281_pcm(chip, 0, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_cs4281_pcm(chip, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_cs4281_midi(chip, 0, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_cs4281_midi(chip, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_opl3_new(card, OPL3_HW_OPL3_CS4281, &opl3)) < 0) {
- snd_card_free(card);
+ err = snd_opl3_new(card, OPL3_HW_OPL3_CS4281, &opl3);
+ if (err < 0)
return err;
- }
opl3->private_data = chip;
opl3->command = snd_cs4281_opl3_command;
snd_opl3_init(opl3);
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
return err;
- }
snd_cs4281_create_gameport(chip);
- strcpy(card->driver, "CS4281");
- strcpy(card->shortname, "Cirrus Logic CS4281");
+ strscpy(card->driver, "CS4281");
+ strscpy(card->shortname, "Cirrus Logic CS4281");
sprintf(card->longname, "%s at 0x%lx, irq %d",
card->shortname,
chip->ba0_addr,
chip->irq);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_cs4281_remove(struct pci_dev *pci)
+static int snd_cs4281_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_cs4281_probe(pci, pci_id));
}
/*
* Power Management
*/
-#ifdef CONFIG_PM
-
-static int saved_regs[SUSPEND_REGISTERS] = {
+static const int saved_regs[SUSPEND_REGISTERS] = {
BA0_JSCTL,
BA0_GPIOR,
BA0_SSCR,
@@ -1997,16 +1899,14 @@ static int saved_regs[SUSPEND_REGISTERS] = {
#define CLKCR1_CKRA 0x00010000L
-static int cs4281_suspend(struct pci_dev *pci, pm_message_t state)
+static int cs4281_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct cs4281 *chip = card->private_data;
u32 ulCLK;
unsigned int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
-
snd_ac97_suspend(chip->ac97);
snd_ac97_suspend(chip->ac97_secondary);
@@ -2037,30 +1937,16 @@ static int cs4281_suspend(struct pci_dev *pci, pm_message_t state)
ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1);
ulCLK &= ~CLKCR1_CKRA;
snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int cs4281_resume(struct pci_dev *pci)
+static int cs4281_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct cs4281 *chip = card->private_data;
unsigned int i;
u32 ulCLK;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "cs4281: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1);
ulCLK |= CLKCR1_CKRA;
snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK);
@@ -2082,28 +1968,16 @@ static int cs4281_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
-static struct pci_driver driver = {
- .name = "CS4281",
+static DEFINE_SIMPLE_DEV_PM_OPS(cs4281_pm, cs4281_suspend, cs4281_resume);
+
+static struct pci_driver cs4281_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_cs4281_ids,
.probe = snd_cs4281_probe,
- .remove = __devexit_p(snd_cs4281_remove),
-#ifdef CONFIG_PM
- .suspend = cs4281_suspend,
- .resume = cs4281_resume,
-#endif
+ .driver = {
+ .pm = &cs4281_pm,
+ },
};
-static int __init alsa_card_cs4281_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs4281_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs4281_init)
-module_exit(alsa_card_cs4281_exit)
+module_pci_driver(cs4281_driver);
diff --git a/sound/pci/cs46xx/Makefile b/sound/pci/cs46xx/Makefile
index 67e811ec8539..3ed2ceb404e5 100644
--- a/sound/pci/cs46xx/Makefile
+++ b/sound/pci/cs46xx/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c
index 767fa7f06cd0..9c1995737eb7 100644
--- a/sound/pci/cs46xx/cs46xx.c
+++ b/sound/pci/cs46xx/cs46xx.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -28,28 +13,21 @@
#include <linux/pci.h>
#include <linux/time.h>
#include <linux/init.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
-#include <sound/cs46xx.h>
+#include "cs46xx.h"
#include <sound/initval.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Cirrus Logic Sound Fusion CS46XX");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Cirrus Logic,Sound Fusion (CS4280)},"
- "{Cirrus Logic,Sound Fusion (CS4610)},"
- "{Cirrus Logic,Sound Fusion (CS4612)},"
- "{Cirrus Logic,Sound Fusion (CS4615)},"
- "{Cirrus Logic,Sound Fusion (CS4622)},"
- "{Cirrus Logic,Sound Fusion (CS4624)},"
- "{Cirrus Logic,Sound Fusion (CS4630)}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
-static int external_amp[SNDRV_CARDS];
-static int thinkpad[SNDRV_CARDS];
-static int mmap_valid[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool external_amp[SNDRV_CARDS];
+static bool thinkpad[SNDRV_CARDS];
+static bool mmap_valid[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the CS46xx soundcard.");
@@ -58,13 +36,13 @@ MODULE_PARM_DESC(id, "ID string for the CS46xx soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable CS46xx soundcard.");
module_param_array(external_amp, bool, NULL, 0444);
-MODULE_PARM_DESC(external_amp, "Force to enable external amplifer.");
+MODULE_PARM_DESC(external_amp, "Force to enable external amplifier.");
module_param_array(thinkpad, bool, NULL, 0444);
MODULE_PARM_DESC(thinkpad, "Force to enable Thinkpad's CLKRUN control.");
module_param_array(mmap_valid, bool, NULL, 0444);
MODULE_PARM_DESC(mmap_valid, "Support OSS mmap.");
-static DEFINE_PCI_DEVICE_TABLE(snd_cs46xx_ids) = {
+static const struct pci_device_id snd_cs46xx_ids[] = {
{ PCI_VDEVICE(CIRRUS, 0x6001), 0, }, /* CS4280 */
{ PCI_VDEVICE(CIRRUS, 0x6003), 0, }, /* CS4612 */
{ PCI_VDEVICE(CIRRUS, 0x6004), 0, }, /* CS4615 */
@@ -73,8 +51,8 @@ static DEFINE_PCI_DEVICE_TABLE(snd_cs46xx_ids) = {
MODULE_DEVICE_TABLE(pci, snd_cs46xx_ids);
-static int __devinit snd_card_cs46xx_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_card_cs46xx_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -88,99 +66,77 @@ static int __devinit snd_card_cs46xx_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
- if ((err = snd_cs46xx_create(card, pci,
- external_amp[dev], thinkpad[dev],
- &chip)) < 0) {
- snd_card_free(card);
- return err;
- }
+ chip = card->private_data;
+ err = snd_cs46xx_create(card, pci,
+ external_amp[dev], thinkpad[dev]);
+ if (err < 0)
+ goto error;
card->private_data = chip;
chip->accept_valid = mmap_valid[dev];
- if ((err = snd_cs46xx_pcm(chip, 0, NULL)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_cs46xx_pcm(chip, 0);
+ if (err < 0)
+ goto error;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
- if ((err = snd_cs46xx_pcm_rear(chip,1, NULL)) < 0) {
- snd_card_free(card);
- return err;
- }
- if ((err = snd_cs46xx_pcm_iec958(chip,2,NULL)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_cs46xx_pcm_rear(chip, 1);
+ if (err < 0)
+ goto error;
+ err = snd_cs46xx_pcm_iec958(chip, 2);
+ if (err < 0)
+ goto error;
#endif
- if ((err = snd_cs46xx_mixer(chip, 2)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_cs46xx_mixer(chip, 2);
+ if (err < 0)
+ goto error;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
if (chip->nr_ac97_codecs ==2) {
- if ((err = snd_cs46xx_pcm_center_lfe(chip,3,NULL)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_cs46xx_pcm_center_lfe(chip, 3);
+ if (err < 0)
+ goto error;
}
#endif
- if ((err = snd_cs46xx_midi(chip, 0, NULL)) < 0) {
- snd_card_free(card);
- return err;
- }
- if ((err = snd_cs46xx_start_dsp(chip)) < 0) {
- snd_card_free(card);
- return err;
- }
-
+ err = snd_cs46xx_midi(chip, 0);
+ if (err < 0)
+ goto error;
+ err = snd_cs46xx_start_dsp(chip);
+ if (err < 0)
+ goto error;
snd_cs46xx_gameport(chip);
- strcpy(card->driver, "CS46xx");
- strcpy(card->shortname, "Sound Fusion CS46xx");
+ strscpy(card->driver, "CS46xx");
+ strscpy(card->shortname, "Sound Fusion CS46xx");
sprintf(card->longname, "%s at 0x%lx/0x%lx, irq %i",
card->shortname,
chip->ba0_addr,
chip->ba1_addr,
chip->irq);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
pci_set_drvdata(pci, card);
dev++;
return 0;
-}
-static void __devexit snd_card_cs46xx_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ error:
+ snd_card_free(card);
+ return err;
}
-static struct pci_driver driver = {
- .name = "Sound Fusion CS46xx",
+static struct pci_driver cs46xx_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_cs46xx_ids,
.probe = snd_card_cs46xx_probe,
- .remove = __devexit_p(snd_card_cs46xx_remove),
-#ifdef CONFIG_PM
- .suspend = snd_cs46xx_suspend,
- .resume = snd_cs46xx_resume,
+#ifdef CONFIG_PM_SLEEP
+ .driver = {
+ .pm = &snd_cs46xx_pm,
+ },
#endif
};
-static int __init alsa_card_cs46xx_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs46xx_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs46xx_init)
-module_exit(alsa_card_cs46xx_exit)
+module_pci_driver(cs46xx_driver);
diff --git a/sound/pci/cs46xx/cs46xx.h b/sound/pci/cs46xx/cs46xx.h
new file mode 100644
index 000000000000..c4f0a0b94270
--- /dev/null
+++ b/sound/pci/cs46xx/cs46xx.h
@@ -0,0 +1,1732 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __SOUND_CS46XX_H
+#define __SOUND_CS46XX_H
+
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
+ * Cirrus Logic, Inc.
+ * Definitions for Cirrus Logic CS46xx chips
+ */
+
+#include <sound/pcm.h>
+#include <sound/pcm-indirect.h>
+#include <sound/rawmidi.h>
+#include <sound/ac97_codec.h>
+#include "cs46xx_dsp_spos.h"
+
+/*
+ * Direct registers
+ */
+
+/*
+ * The following define the offsets of the registers accessed via base address
+ * register zero on the CS46xx part.
+ */
+#define BA0_HISR 0x00000000
+#define BA0_HSR0 0x00000004
+#define BA0_HICR 0x00000008
+#define BA0_DMSR 0x00000100
+#define BA0_HSAR 0x00000110
+#define BA0_HDAR 0x00000114
+#define BA0_HDMR 0x00000118
+#define BA0_HDCR 0x0000011C
+#define BA0_PFMC 0x00000200
+#define BA0_PFCV1 0x00000204
+#define BA0_PFCV2 0x00000208
+#define BA0_PCICFG00 0x00000300
+#define BA0_PCICFG04 0x00000304
+#define BA0_PCICFG08 0x00000308
+#define BA0_PCICFG0C 0x0000030C
+#define BA0_PCICFG10 0x00000310
+#define BA0_PCICFG14 0x00000314
+#define BA0_PCICFG18 0x00000318
+#define BA0_PCICFG1C 0x0000031C
+#define BA0_PCICFG20 0x00000320
+#define BA0_PCICFG24 0x00000324
+#define BA0_PCICFG28 0x00000328
+#define BA0_PCICFG2C 0x0000032C
+#define BA0_PCICFG30 0x00000330
+#define BA0_PCICFG34 0x00000334
+#define BA0_PCICFG38 0x00000338
+#define BA0_PCICFG3C 0x0000033C
+#define BA0_CLKCR1 0x00000400
+#define BA0_CLKCR2 0x00000404
+#define BA0_PLLM 0x00000408
+#define BA0_PLLCC 0x0000040C
+#define BA0_FRR 0x00000410
+#define BA0_CFL1 0x00000414
+#define BA0_CFL2 0x00000418
+#define BA0_SERMC1 0x00000420
+#define BA0_SERMC2 0x00000424
+#define BA0_SERC1 0x00000428
+#define BA0_SERC2 0x0000042C
+#define BA0_SERC3 0x00000430
+#define BA0_SERC4 0x00000434
+#define BA0_SERC5 0x00000438
+#define BA0_SERBSP 0x0000043C
+#define BA0_SERBST 0x00000440
+#define BA0_SERBCM 0x00000444
+#define BA0_SERBAD 0x00000448
+#define BA0_SERBCF 0x0000044C
+#define BA0_SERBWP 0x00000450
+#define BA0_SERBRP 0x00000454
+#ifndef NO_CS4612
+#define BA0_ASER_FADDR 0x00000458
+#endif
+#define BA0_ACCTL 0x00000460
+#define BA0_ACSTS 0x00000464
+#define BA0_ACOSV 0x00000468
+#define BA0_ACCAD 0x0000046C
+#define BA0_ACCDA 0x00000470
+#define BA0_ACISV 0x00000474
+#define BA0_ACSAD 0x00000478
+#define BA0_ACSDA 0x0000047C
+#define BA0_JSPT 0x00000480
+#define BA0_JSCTL 0x00000484
+#define BA0_JSC1 0x00000488
+#define BA0_JSC2 0x0000048C
+#define BA0_MIDCR 0x00000490
+#define BA0_MIDSR 0x00000494
+#define BA0_MIDWP 0x00000498
+#define BA0_MIDRP 0x0000049C
+#define BA0_JSIO 0x000004A0
+#ifndef NO_CS4612
+#define BA0_ASER_MASTER 0x000004A4
+#endif
+#define BA0_CFGI 0x000004B0
+#define BA0_SSVID 0x000004B4
+#define BA0_GPIOR 0x000004B8
+#ifndef NO_CS4612
+#define BA0_EGPIODR 0x000004BC
+#define BA0_EGPIOPTR 0x000004C0
+#define BA0_EGPIOTR 0x000004C4
+#define BA0_EGPIOWR 0x000004C8
+#define BA0_EGPIOSR 0x000004CC
+#define BA0_SERC6 0x000004D0
+#define BA0_SERC7 0x000004D4
+#define BA0_SERACC 0x000004D8
+#define BA0_ACCTL2 0x000004E0
+#define BA0_ACSTS2 0x000004E4
+#define BA0_ACOSV2 0x000004E8
+#define BA0_ACCAD2 0x000004EC
+#define BA0_ACCDA2 0x000004F0
+#define BA0_ACISV2 0x000004F4
+#define BA0_ACSAD2 0x000004F8
+#define BA0_ACSDA2 0x000004FC
+#define BA0_IOTAC0 0x00000500
+#define BA0_IOTAC1 0x00000504
+#define BA0_IOTAC2 0x00000508
+#define BA0_IOTAC3 0x0000050C
+#define BA0_IOTAC4 0x00000510
+#define BA0_IOTAC5 0x00000514
+#define BA0_IOTAC6 0x00000518
+#define BA0_IOTAC7 0x0000051C
+#define BA0_IOTAC8 0x00000520
+#define BA0_IOTAC9 0x00000524
+#define BA0_IOTAC10 0x00000528
+#define BA0_IOTAC11 0x0000052C
+#define BA0_IOTFR0 0x00000540
+#define BA0_IOTFR1 0x00000544
+#define BA0_IOTFR2 0x00000548
+#define BA0_IOTFR3 0x0000054C
+#define BA0_IOTFR4 0x00000550
+#define BA0_IOTFR5 0x00000554
+#define BA0_IOTFR6 0x00000558
+#define BA0_IOTFR7 0x0000055C
+#define BA0_IOTFIFO 0x00000580
+#define BA0_IOTRRD 0x00000584
+#define BA0_IOTFP 0x00000588
+#define BA0_IOTCR 0x0000058C
+#define BA0_DPCID 0x00000590
+#define BA0_DPCIA 0x00000594
+#define BA0_DPCIC 0x00000598
+#define BA0_PCPCIR 0x00000600
+#define BA0_PCPCIG 0x00000604
+#define BA0_PCPCIEN 0x00000608
+#define BA0_EPCIPMC 0x00000610
+#endif
+
+/*
+ * The following define the offsets of the registers and memories accessed via
+ * base address register one on the CS46xx part.
+ */
+#define BA1_SP_DMEM0 0x00000000
+#define BA1_SP_DMEM1 0x00010000
+#define BA1_SP_PMEM 0x00020000
+#define BA1_SP_REG 0x00030000
+#define BA1_SPCR 0x00030000
+#define BA1_DREG 0x00030004
+#define BA1_DSRWP 0x00030008
+#define BA1_TWPR 0x0003000C
+#define BA1_SPWR 0x00030010
+#define BA1_SPIR 0x00030014
+#define BA1_FGR1 0x00030020
+#define BA1_SPCS 0x00030028
+#define BA1_SDSR 0x0003002C
+#define BA1_FRMT 0x00030030
+#define BA1_FRCC 0x00030034
+#define BA1_FRSC 0x00030038
+#define BA1_OMNI_MEM 0x000E0000
+
+
+/*
+ * The following defines are for the flags in the host interrupt status
+ * register.
+ */
+#define HISR_VC_MASK 0x0000FFFF
+#define HISR_VC0 0x00000001
+#define HISR_VC1 0x00000002
+#define HISR_VC2 0x00000004
+#define HISR_VC3 0x00000008
+#define HISR_VC4 0x00000010
+#define HISR_VC5 0x00000020
+#define HISR_VC6 0x00000040
+#define HISR_VC7 0x00000080
+#define HISR_VC8 0x00000100
+#define HISR_VC9 0x00000200
+#define HISR_VC10 0x00000400
+#define HISR_VC11 0x00000800
+#define HISR_VC12 0x00001000
+#define HISR_VC13 0x00002000
+#define HISR_VC14 0x00004000
+#define HISR_VC15 0x00008000
+#define HISR_INT0 0x00010000
+#define HISR_INT1 0x00020000
+#define HISR_DMAI 0x00040000
+#define HISR_FROVR 0x00080000
+#define HISR_MIDI 0x00100000
+#ifdef NO_CS4612
+#define HISR_RESERVED 0x0FE00000
+#else
+#define HISR_SBINT 0x00200000
+#define HISR_RESERVED 0x0FC00000
+#endif
+#define HISR_H0P 0x40000000
+#define HISR_INTENA 0x80000000
+
+/*
+ * The following defines are for the flags in the host signal register 0.
+ */
+#define HSR0_VC_MASK 0xFFFFFFFF
+#define HSR0_VC16 0x00000001
+#define HSR0_VC17 0x00000002
+#define HSR0_VC18 0x00000004
+#define HSR0_VC19 0x00000008
+#define HSR0_VC20 0x00000010
+#define HSR0_VC21 0x00000020
+#define HSR0_VC22 0x00000040
+#define HSR0_VC23 0x00000080
+#define HSR0_VC24 0x00000100
+#define HSR0_VC25 0x00000200
+#define HSR0_VC26 0x00000400
+#define HSR0_VC27 0x00000800
+#define HSR0_VC28 0x00001000
+#define HSR0_VC29 0x00002000
+#define HSR0_VC30 0x00004000
+#define HSR0_VC31 0x00008000
+#define HSR0_VC32 0x00010000
+#define HSR0_VC33 0x00020000
+#define HSR0_VC34 0x00040000
+#define HSR0_VC35 0x00080000
+#define HSR0_VC36 0x00100000
+#define HSR0_VC37 0x00200000
+#define HSR0_VC38 0x00400000
+#define HSR0_VC39 0x00800000
+#define HSR0_VC40 0x01000000
+#define HSR0_VC41 0x02000000
+#define HSR0_VC42 0x04000000
+#define HSR0_VC43 0x08000000
+#define HSR0_VC44 0x10000000
+#define HSR0_VC45 0x20000000
+#define HSR0_VC46 0x40000000
+#define HSR0_VC47 0x80000000
+
+/*
+ * The following defines are for the flags in the host interrupt control
+ * register.
+ */
+#define HICR_IEV 0x00000001
+#define HICR_CHGM 0x00000002
+
+/*
+ * The following defines are for the flags in the DMA status register.
+ */
+#define DMSR_HP 0x00000001
+#define DMSR_HR 0x00000002
+#define DMSR_SP 0x00000004
+#define DMSR_SR 0x00000008
+
+/*
+ * The following defines are for the flags in the host DMA source address
+ * register.
+ */
+#define HSAR_HOST_ADDR_MASK 0xFFFFFFFF
+#define HSAR_DSP_ADDR_MASK 0x0000FFFF
+#define HSAR_MEMID_MASK 0x000F0000
+#define HSAR_MEMID_SP_DMEM0 0x00000000
+#define HSAR_MEMID_SP_DMEM1 0x00010000
+#define HSAR_MEMID_SP_PMEM 0x00020000
+#define HSAR_MEMID_SP_DEBUG 0x00030000
+#define HSAR_MEMID_OMNI_MEM 0x000E0000
+#define HSAR_END 0x40000000
+#define HSAR_ERR 0x80000000
+
+/*
+ * The following defines are for the flags in the host DMA destination address
+ * register.
+ */
+#define HDAR_HOST_ADDR_MASK 0xFFFFFFFF
+#define HDAR_DSP_ADDR_MASK 0x0000FFFF
+#define HDAR_MEMID_MASK 0x000F0000
+#define HDAR_MEMID_SP_DMEM0 0x00000000
+#define HDAR_MEMID_SP_DMEM1 0x00010000
+#define HDAR_MEMID_SP_PMEM 0x00020000
+#define HDAR_MEMID_SP_DEBUG 0x00030000
+#define HDAR_MEMID_OMNI_MEM 0x000E0000
+#define HDAR_END 0x40000000
+#define HDAR_ERR 0x80000000
+
+/*
+ * The following defines are for the flags in the host DMA control register.
+ */
+#define HDMR_AC_MASK 0x0000F000
+#define HDMR_AC_8_16 0x00001000
+#define HDMR_AC_M_S 0x00002000
+#define HDMR_AC_B_L 0x00004000
+#define HDMR_AC_S_U 0x00008000
+
+/*
+ * The following defines are for the flags in the host DMA control register.
+ */
+#define HDCR_COUNT_MASK 0x000003FF
+#define HDCR_DONE 0x00004000
+#define HDCR_OPT 0x00008000
+#define HDCR_WBD 0x00400000
+#define HDCR_WBS 0x00800000
+#define HDCR_DMS_MASK 0x07000000
+#define HDCR_DMS_LINEAR 0x00000000
+#define HDCR_DMS_16_DWORDS 0x01000000
+#define HDCR_DMS_32_DWORDS 0x02000000
+#define HDCR_DMS_64_DWORDS 0x03000000
+#define HDCR_DMS_128_DWORDS 0x04000000
+#define HDCR_DMS_256_DWORDS 0x05000000
+#define HDCR_DMS_512_DWORDS 0x06000000
+#define HDCR_DMS_1024_DWORDS 0x07000000
+#define HDCR_DH 0x08000000
+#define HDCR_SMS_MASK 0x70000000
+#define HDCR_SMS_LINEAR 0x00000000
+#define HDCR_SMS_16_DWORDS 0x10000000
+#define HDCR_SMS_32_DWORDS 0x20000000
+#define HDCR_SMS_64_DWORDS 0x30000000
+#define HDCR_SMS_128_DWORDS 0x40000000
+#define HDCR_SMS_256_DWORDS 0x50000000
+#define HDCR_SMS_512_DWORDS 0x60000000
+#define HDCR_SMS_1024_DWORDS 0x70000000
+#define HDCR_SH 0x80000000
+#define HDCR_COUNT_SHIFT 0
+
+/*
+ * The following defines are for the flags in the performance monitor control
+ * register.
+ */
+#define PFMC_C1SS_MASK 0x0000001F
+#define PFMC_C1EV 0x00000020
+#define PFMC_C1RS 0x00008000
+#define PFMC_C2SS_MASK 0x001F0000
+#define PFMC_C2EV 0x00200000
+#define PFMC_C2RS 0x80000000
+#define PFMC_C1SS_SHIFT 0
+#define PFMC_C2SS_SHIFT 16
+#define PFMC_BUS_GRANT 0
+#define PFMC_GRANT_AFTER_REQ 1
+#define PFMC_TRANSACTION 2
+#define PFMC_DWORD_TRANSFER 3
+#define PFMC_SLAVE_READ 4
+#define PFMC_SLAVE_WRITE 5
+#define PFMC_PREEMPTION 6
+#define PFMC_DISCONNECT_RETRY 7
+#define PFMC_INTERRUPT 8
+#define PFMC_BUS_OWNERSHIP 9
+#define PFMC_TRANSACTION_LAG 10
+#define PFMC_PCI_CLOCK 11
+#define PFMC_SERIAL_CLOCK 12
+#define PFMC_SP_CLOCK 13
+
+/*
+ * The following defines are for the flags in the performance counter value 1
+ * register.
+ */
+#define PFCV1_PC1V_MASK 0xFFFFFFFF
+#define PFCV1_PC1V_SHIFT 0
+
+/*
+ * The following defines are for the flags in the performance counter value 2
+ * register.
+ */
+#define PFCV2_PC2V_MASK 0xFFFFFFFF
+#define PFCV2_PC2V_SHIFT 0
+
+/*
+ * The following defines are for the flags in the clock control register 1.
+ */
+#define CLKCR1_OSCS 0x00000001
+#define CLKCR1_OSCP 0x00000002
+#define CLKCR1_PLLSS_MASK 0x0000000C
+#define CLKCR1_PLLSS_SERIAL 0x00000000
+#define CLKCR1_PLLSS_CRYSTAL 0x00000004
+#define CLKCR1_PLLSS_PCI 0x00000008
+#define CLKCR1_PLLSS_RESERVED 0x0000000C
+#define CLKCR1_PLLP 0x00000010
+#define CLKCR1_SWCE 0x00000020
+#define CLKCR1_PLLOS 0x00000040
+
+/*
+ * The following defines are for the flags in the clock control register 2.
+ */
+#define CLKCR2_PDIVS_MASK 0x0000000F
+#define CLKCR2_PDIVS_1 0x00000001
+#define CLKCR2_PDIVS_2 0x00000002
+#define CLKCR2_PDIVS_4 0x00000004
+#define CLKCR2_PDIVS_7 0x00000007
+#define CLKCR2_PDIVS_8 0x00000008
+#define CLKCR2_PDIVS_16 0x00000000
+
+/*
+ * The following defines are for the flags in the PLL multiplier register.
+ */
+#define PLLM_MASK 0x000000FF
+#define PLLM_SHIFT 0
+
+/*
+ * The following defines are for the flags in the PLL capacitor coefficient
+ * register.
+ */
+#define PLLCC_CDR_MASK 0x00000007
+#ifndef NO_CS4610
+#define PLLCC_CDR_240_350_MHZ 0x00000000
+#define PLLCC_CDR_184_265_MHZ 0x00000001
+#define PLLCC_CDR_144_205_MHZ 0x00000002
+#define PLLCC_CDR_111_160_MHZ 0x00000003
+#define PLLCC_CDR_87_123_MHZ 0x00000004
+#define PLLCC_CDR_67_96_MHZ 0x00000005
+#define PLLCC_CDR_52_74_MHZ 0x00000006
+#define PLLCC_CDR_45_58_MHZ 0x00000007
+#endif
+#ifndef NO_CS4612
+#define PLLCC_CDR_271_398_MHZ 0x00000000
+#define PLLCC_CDR_227_330_MHZ 0x00000001
+#define PLLCC_CDR_167_239_MHZ 0x00000002
+#define PLLCC_CDR_150_215_MHZ 0x00000003
+#define PLLCC_CDR_107_154_MHZ 0x00000004
+#define PLLCC_CDR_98_140_MHZ 0x00000005
+#define PLLCC_CDR_73_104_MHZ 0x00000006
+#define PLLCC_CDR_63_90_MHZ 0x00000007
+#endif
+#define PLLCC_LPF_MASK 0x000000F8
+#ifndef NO_CS4610
+#define PLLCC_LPF_23850_60000_KHZ 0x00000000
+#define PLLCC_LPF_7960_26290_KHZ 0x00000008
+#define PLLCC_LPF_4160_10980_KHZ 0x00000018
+#define PLLCC_LPF_1740_4580_KHZ 0x00000038
+#define PLLCC_LPF_724_1910_KHZ 0x00000078
+#define PLLCC_LPF_317_798_KHZ 0x000000F8
+#endif
+#ifndef NO_CS4612
+#define PLLCC_LPF_25580_64530_KHZ 0x00000000
+#define PLLCC_LPF_14360_37270_KHZ 0x00000008
+#define PLLCC_LPF_6100_16020_KHZ 0x00000018
+#define PLLCC_LPF_2540_6690_KHZ 0x00000038
+#define PLLCC_LPF_1050_2780_KHZ 0x00000078
+#define PLLCC_LPF_450_1160_KHZ 0x000000F8
+#endif
+
+/*
+ * The following defines are for the flags in the feature reporting register.
+ */
+#define FRR_FAB_MASK 0x00000003
+#define FRR_MASK_MASK 0x0000001C
+#ifdef NO_CS4612
+#define FRR_CFOP_MASK 0x000000E0
+#else
+#define FRR_CFOP_MASK 0x00000FE0
+#endif
+#define FRR_CFOP_NOT_DVD 0x00000020
+#define FRR_CFOP_A3D 0x00000040
+#define FRR_CFOP_128_PIN 0x00000080
+#ifndef NO_CS4612
+#define FRR_CFOP_CS4280 0x00000800
+#endif
+#define FRR_FAB_SHIFT 0
+#define FRR_MASK_SHIFT 2
+#define FRR_CFOP_SHIFT 5
+
+/*
+ * The following defines are for the flags in the configuration load 1
+ * register.
+ */
+#define CFL1_CLOCK_SOURCE_MASK 0x00000003
+#define CFL1_CLOCK_SOURCE_CS423X 0x00000000
+#define CFL1_CLOCK_SOURCE_AC97 0x00000001
+#define CFL1_CLOCK_SOURCE_CRYSTAL 0x00000002
+#define CFL1_CLOCK_SOURCE_DUAL_AC97 0x00000003
+#define CFL1_VALID_DATA_MASK 0x000000FF
+
+/*
+ * The following defines are for the flags in the configuration load 2
+ * register.
+ */
+#define CFL2_VALID_DATA_MASK 0x000000FF
+
+/*
+ * The following defines are for the flags in the serial port master control
+ * register 1.
+ */
+#define SERMC1_MSPE 0x00000001
+#define SERMC1_PTC_MASK 0x0000000E
+#define SERMC1_PTC_CS423X 0x00000000
+#define SERMC1_PTC_AC97 0x00000002
+#define SERMC1_PTC_DAC 0x00000004
+#define SERMC1_PLB 0x00000010
+#define SERMC1_XLB 0x00000020
+
+/*
+ * The following defines are for the flags in the serial port master control
+ * register 2.
+ */
+#define SERMC2_LROE 0x00000001
+#define SERMC2_MCOE 0x00000002
+#define SERMC2_MCDIV 0x00000004
+
+/*
+ * The following defines are for the flags in the serial port 1 configuration
+ * register.
+ */
+#define SERC1_SO1EN 0x00000001
+#define SERC1_SO1F_MASK 0x0000000E
+#define SERC1_SO1F_CS423X 0x00000000
+#define SERC1_SO1F_AC97 0x00000002
+#define SERC1_SO1F_DAC 0x00000004
+#define SERC1_SO1F_SPDIF 0x00000006
+
+/*
+ * The following defines are for the flags in the serial port 2 configuration
+ * register.
+ */
+#define SERC2_SI1EN 0x00000001
+#define SERC2_SI1F_MASK 0x0000000E
+#define SERC2_SI1F_CS423X 0x00000000
+#define SERC2_SI1F_AC97 0x00000002
+#define SERC2_SI1F_ADC 0x00000004
+#define SERC2_SI1F_SPDIF 0x00000006
+
+/*
+ * The following defines are for the flags in the serial port 3 configuration
+ * register.
+ */
+#define SERC3_SO2EN 0x00000001
+#define SERC3_SO2F_MASK 0x00000006
+#define SERC3_SO2F_DAC 0x00000000
+#define SERC3_SO2F_SPDIF 0x00000002
+
+/*
+ * The following defines are for the flags in the serial port 4 configuration
+ * register.
+ */
+#define SERC4_SO3EN 0x00000001
+#define SERC4_SO3F_MASK 0x00000006
+#define SERC4_SO3F_DAC 0x00000000
+#define SERC4_SO3F_SPDIF 0x00000002
+
+/*
+ * The following defines are for the flags in the serial port 5 configuration
+ * register.
+ */
+#define SERC5_SI2EN 0x00000001
+#define SERC5_SI2F_MASK 0x00000006
+#define SERC5_SI2F_ADC 0x00000000
+#define SERC5_SI2F_SPDIF 0x00000002
+
+/*
+ * The following defines are for the flags in the serial port backdoor sample
+ * pointer register.
+ */
+#define SERBSP_FSP_MASK 0x0000000F
+#define SERBSP_FSP_SHIFT 0
+
+/*
+ * The following defines are for the flags in the serial port backdoor status
+ * register.
+ */
+#define SERBST_RRDY 0x00000001
+#define SERBST_WBSY 0x00000002
+
+/*
+ * The following defines are for the flags in the serial port backdoor command
+ * register.
+ */
+#define SERBCM_RDC 0x00000001
+#define SERBCM_WRC 0x00000002
+
+/*
+ * The following defines are for the flags in the serial port backdoor address
+ * register.
+ */
+#ifdef NO_CS4612
+#define SERBAD_FAD_MASK 0x000000FF
+#else
+#define SERBAD_FAD_MASK 0x000001FF
+#endif
+#define SERBAD_FAD_SHIFT 0
+
+/*
+ * The following defines are for the flags in the serial port backdoor
+ * configuration register.
+ */
+#define SERBCF_HBP 0x00000001
+
+/*
+ * The following defines are for the flags in the serial port backdoor write
+ * port register.
+ */
+#define SERBWP_FWD_MASK 0x000FFFFF
+#define SERBWP_FWD_SHIFT 0
+
+/*
+ * The following defines are for the flags in the serial port backdoor read
+ * port register.
+ */
+#define SERBRP_FRD_MASK 0x000FFFFF
+#define SERBRP_FRD_SHIFT 0
+
+/*
+ * The following defines are for the flags in the async FIFO address register.
+ */
+#ifndef NO_CS4612
+#define ASER_FADDR_A1_MASK 0x000001FF
+#define ASER_FADDR_EN1 0x00008000
+#define ASER_FADDR_A2_MASK 0x01FF0000
+#define ASER_FADDR_EN2 0x80000000
+#define ASER_FADDR_A1_SHIFT 0
+#define ASER_FADDR_A2_SHIFT 16
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 control register.
+ */
+#define ACCTL_RSTN 0x00000001
+#define ACCTL_ESYN 0x00000002
+#define ACCTL_VFRM 0x00000004
+#define ACCTL_DCV 0x00000008
+#define ACCTL_CRW 0x00000010
+#define ACCTL_ASYN 0x00000020
+#ifndef NO_CS4612
+#define ACCTL_TC 0x00000040
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 status register.
+ */
+#define ACSTS_CRDY 0x00000001
+#define ACSTS_VSTS 0x00000002
+#ifndef NO_CS4612
+#define ACSTS_WKUP 0x00000004
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 output slot valid
+ * register.
+ */
+#define ACOSV_SLV3 0x00000001
+#define ACOSV_SLV4 0x00000002
+#define ACOSV_SLV5 0x00000004
+#define ACOSV_SLV6 0x00000008
+#define ACOSV_SLV7 0x00000010
+#define ACOSV_SLV8 0x00000020
+#define ACOSV_SLV9 0x00000040
+#define ACOSV_SLV10 0x00000080
+#define ACOSV_SLV11 0x00000100
+#define ACOSV_SLV12 0x00000200
+
+/*
+ * The following defines are for the flags in the AC97 command address
+ * register.
+ */
+#define ACCAD_CI_MASK 0x0000007F
+#define ACCAD_CI_SHIFT 0
+
+/*
+ * The following defines are for the flags in the AC97 command data register.
+ */
+#define ACCDA_CD_MASK 0x0000FFFF
+#define ACCDA_CD_SHIFT 0
+
+/*
+ * The following defines are for the flags in the AC97 input slot valid
+ * register.
+ */
+#define ACISV_ISV3 0x00000001
+#define ACISV_ISV4 0x00000002
+#define ACISV_ISV5 0x00000004
+#define ACISV_ISV6 0x00000008
+#define ACISV_ISV7 0x00000010
+#define ACISV_ISV8 0x00000020
+#define ACISV_ISV9 0x00000040
+#define ACISV_ISV10 0x00000080
+#define ACISV_ISV11 0x00000100
+#define ACISV_ISV12 0x00000200
+
+/*
+ * The following defines are for the flags in the AC97 status address
+ * register.
+ */
+#define ACSAD_SI_MASK 0x0000007F
+#define ACSAD_SI_SHIFT 0
+
+/*
+ * The following defines are for the flags in the AC97 status data register.
+ */
+#define ACSDA_SD_MASK 0x0000FFFF
+#define ACSDA_SD_SHIFT 0
+
+/*
+ * The following defines are for the flags in the joystick poll/trigger
+ * register.
+ */
+#define JSPT_CAX 0x00000001
+#define JSPT_CAY 0x00000002
+#define JSPT_CBX 0x00000004
+#define JSPT_CBY 0x00000008
+#define JSPT_BA1 0x00000010
+#define JSPT_BA2 0x00000020
+#define JSPT_BB1 0x00000040
+#define JSPT_BB2 0x00000080
+
+/*
+ * The following defines are for the flags in the joystick control register.
+ */
+#define JSCTL_SP_MASK 0x00000003
+#define JSCTL_SP_SLOW 0x00000000
+#define JSCTL_SP_MEDIUM_SLOW 0x00000001
+#define JSCTL_SP_MEDIUM_FAST 0x00000002
+#define JSCTL_SP_FAST 0x00000003
+#define JSCTL_ARE 0x00000004
+
+/*
+ * The following defines are for the flags in the joystick coordinate pair 1
+ * readback register.
+ */
+#define JSC1_Y1V_MASK 0x0000FFFF
+#define JSC1_X1V_MASK 0xFFFF0000
+#define JSC1_Y1V_SHIFT 0
+#define JSC1_X1V_SHIFT 16
+
+/*
+ * The following defines are for the flags in the joystick coordinate pair 2
+ * readback register.
+ */
+#define JSC2_Y2V_MASK 0x0000FFFF
+#define JSC2_X2V_MASK 0xFFFF0000
+#define JSC2_Y2V_SHIFT 0
+#define JSC2_X2V_SHIFT 16
+
+/*
+ * The following defines are for the flags in the MIDI control register.
+ */
+#define MIDCR_TXE 0x00000001 /* Enable transmitting. */
+#define MIDCR_RXE 0x00000002 /* Enable receiving. */
+#define MIDCR_RIE 0x00000004 /* Interrupt upon tx ready. */
+#define MIDCR_TIE 0x00000008 /* Interrupt upon rx ready. */
+#define MIDCR_MLB 0x00000010 /* Enable midi loopback. */
+#define MIDCR_MRST 0x00000020 /* Reset interface. */
+
+/*
+ * The following defines are for the flags in the MIDI status register.
+ */
+#define MIDSR_TBF 0x00000001 /* Tx FIFO is full. */
+#define MIDSR_RBE 0x00000002 /* Rx FIFO is empty. */
+
+/*
+ * The following defines are for the flags in the MIDI write port register.
+ */
+#define MIDWP_MWD_MASK 0x000000FF
+#define MIDWP_MWD_SHIFT 0
+
+/*
+ * The following defines are for the flags in the MIDI read port register.
+ */
+#define MIDRP_MRD_MASK 0x000000FF
+#define MIDRP_MRD_SHIFT 0
+
+/*
+ * The following defines are for the flags in the joystick GPIO register.
+ */
+#define JSIO_DAX 0x00000001
+#define JSIO_DAY 0x00000002
+#define JSIO_DBX 0x00000004
+#define JSIO_DBY 0x00000008
+#define JSIO_AXOE 0x00000010
+#define JSIO_AYOE 0x00000020
+#define JSIO_BXOE 0x00000040
+#define JSIO_BYOE 0x00000080
+
+/*
+ * The following defines are for the flags in the master async/sync serial
+ * port enable register.
+ */
+#ifndef NO_CS4612
+#define ASER_MASTER_ME 0x00000001
+#endif
+
+/*
+ * The following defines are for the flags in the configuration interface
+ * register.
+ */
+#define CFGI_CLK 0x00000001
+#define CFGI_DOUT 0x00000002
+#define CFGI_DIN_EEN 0x00000004
+#define CFGI_EELD 0x00000008
+
+/*
+ * The following defines are for the flags in the subsystem ID and vendor ID
+ * register.
+ */
+#define SSVID_VID_MASK 0x0000FFFF
+#define SSVID_SID_MASK 0xFFFF0000
+#define SSVID_VID_SHIFT 0
+#define SSVID_SID_SHIFT 16
+
+/*
+ * The following defines are for the flags in the GPIO pin interface register.
+ */
+#define GPIOR_VOLDN 0x00000001
+#define GPIOR_VOLUP 0x00000002
+#define GPIOR_SI2D 0x00000004
+#define GPIOR_SI2OE 0x00000008
+
+/*
+ * The following defines are for the flags in the extended GPIO pin direction
+ * register.
+ */
+#ifndef NO_CS4612
+#define EGPIODR_GPOE0 0x00000001
+#define EGPIODR_GPOE1 0x00000002
+#define EGPIODR_GPOE2 0x00000004
+#define EGPIODR_GPOE3 0x00000008
+#define EGPIODR_GPOE4 0x00000010
+#define EGPIODR_GPOE5 0x00000020
+#define EGPIODR_GPOE6 0x00000040
+#define EGPIODR_GPOE7 0x00000080
+#define EGPIODR_GPOE8 0x00000100
+#endif
+
+/*
+ * The following defines are for the flags in the extended GPIO pin polarity/
+ * type register.
+ */
+#ifndef NO_CS4612
+#define EGPIOPTR_GPPT0 0x00000001
+#define EGPIOPTR_GPPT1 0x00000002
+#define EGPIOPTR_GPPT2 0x00000004
+#define EGPIOPTR_GPPT3 0x00000008
+#define EGPIOPTR_GPPT4 0x00000010
+#define EGPIOPTR_GPPT5 0x00000020
+#define EGPIOPTR_GPPT6 0x00000040
+#define EGPIOPTR_GPPT7 0x00000080
+#define EGPIOPTR_GPPT8 0x00000100
+#endif
+
+/*
+ * The following defines are for the flags in the extended GPIO pin sticky
+ * register.
+ */
+#ifndef NO_CS4612
+#define EGPIOTR_GPS0 0x00000001
+#define EGPIOTR_GPS1 0x00000002
+#define EGPIOTR_GPS2 0x00000004
+#define EGPIOTR_GPS3 0x00000008
+#define EGPIOTR_GPS4 0x00000010
+#define EGPIOTR_GPS5 0x00000020
+#define EGPIOTR_GPS6 0x00000040
+#define EGPIOTR_GPS7 0x00000080
+#define EGPIOTR_GPS8 0x00000100
+#endif
+
+/*
+ * The following defines are for the flags in the extended GPIO ping wakeup
+ * register.
+ */
+#ifndef NO_CS4612
+#define EGPIOWR_GPW0 0x00000001
+#define EGPIOWR_GPW1 0x00000002
+#define EGPIOWR_GPW2 0x00000004
+#define EGPIOWR_GPW3 0x00000008
+#define EGPIOWR_GPW4 0x00000010
+#define EGPIOWR_GPW5 0x00000020
+#define EGPIOWR_GPW6 0x00000040
+#define EGPIOWR_GPW7 0x00000080
+#define EGPIOWR_GPW8 0x00000100
+#endif
+
+/*
+ * The following defines are for the flags in the extended GPIO pin status
+ * register.
+ */
+#ifndef NO_CS4612
+#define EGPIOSR_GPS0 0x00000001
+#define EGPIOSR_GPS1 0x00000002
+#define EGPIOSR_GPS2 0x00000004
+#define EGPIOSR_GPS3 0x00000008
+#define EGPIOSR_GPS4 0x00000010
+#define EGPIOSR_GPS5 0x00000020
+#define EGPIOSR_GPS6 0x00000040
+#define EGPIOSR_GPS7 0x00000080
+#define EGPIOSR_GPS8 0x00000100
+#endif
+
+/*
+ * The following defines are for the flags in the serial port 6 configuration
+ * register.
+ */
+#ifndef NO_CS4612
+#define SERC6_ASDO2EN 0x00000001
+#endif
+
+/*
+ * The following defines are for the flags in the serial port 7 configuration
+ * register.
+ */
+#ifndef NO_CS4612
+#define SERC7_ASDI2EN 0x00000001
+#define SERC7_POSILB 0x00000002
+#define SERC7_SIPOLB 0x00000004
+#define SERC7_SOSILB 0x00000008
+#define SERC7_SISOLB 0x00000010
+#endif
+
+/*
+ * The following defines are for the flags in the serial port AC link
+ * configuration register.
+ */
+#ifndef NO_CS4612
+#define SERACC_CHIP_TYPE_MASK 0x00000001
+#define SERACC_CHIP_TYPE_1_03 0x00000000
+#define SERACC_CHIP_TYPE_2_0 0x00000001
+#define SERACC_TWO_CODECS 0x00000002
+#define SERACC_MDM 0x00000004
+#define SERACC_HSP 0x00000008
+#define SERACC_ODT 0x00000010 /* only CS4630 */
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 control register 2.
+ */
+#ifndef NO_CS4612
+#define ACCTL2_RSTN 0x00000001
+#define ACCTL2_ESYN 0x00000002
+#define ACCTL2_VFRM 0x00000004
+#define ACCTL2_DCV 0x00000008
+#define ACCTL2_CRW 0x00000010
+#define ACCTL2_ASYN 0x00000020
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 status register 2.
+ */
+#ifndef NO_CS4612
+#define ACSTS2_CRDY 0x00000001
+#define ACSTS2_VSTS 0x00000002
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 output slot valid
+ * register 2.
+ */
+#ifndef NO_CS4612
+#define ACOSV2_SLV3 0x00000001
+#define ACOSV2_SLV4 0x00000002
+#define ACOSV2_SLV5 0x00000004
+#define ACOSV2_SLV6 0x00000008
+#define ACOSV2_SLV7 0x00000010
+#define ACOSV2_SLV8 0x00000020
+#define ACOSV2_SLV9 0x00000040
+#define ACOSV2_SLV10 0x00000080
+#define ACOSV2_SLV11 0x00000100
+#define ACOSV2_SLV12 0x00000200
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 command address
+ * register 2.
+ */
+#ifndef NO_CS4612
+#define ACCAD2_CI_MASK 0x0000007F
+#define ACCAD2_CI_SHIFT 0
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 command data register
+ * 2.
+ */
+#ifndef NO_CS4612
+#define ACCDA2_CD_MASK 0x0000FFFF
+#define ACCDA2_CD_SHIFT 0
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 input slot valid
+ * register 2.
+ */
+#ifndef NO_CS4612
+#define ACISV2_ISV3 0x00000001
+#define ACISV2_ISV4 0x00000002
+#define ACISV2_ISV5 0x00000004
+#define ACISV2_ISV6 0x00000008
+#define ACISV2_ISV7 0x00000010
+#define ACISV2_ISV8 0x00000020
+#define ACISV2_ISV9 0x00000040
+#define ACISV2_ISV10 0x00000080
+#define ACISV2_ISV11 0x00000100
+#define ACISV2_ISV12 0x00000200
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 status address
+ * register 2.
+ */
+#ifndef NO_CS4612
+#define ACSAD2_SI_MASK 0x0000007F
+#define ACSAD2_SI_SHIFT 0
+#endif
+
+/*
+ * The following defines are for the flags in the AC97 status data register 2.
+ */
+#ifndef NO_CS4612
+#define ACSDA2_SD_MASK 0x0000FFFF
+#define ACSDA2_SD_SHIFT 0
+#endif
+
+/*
+ * The following defines are for the flags in the I/O trap address and control
+ * registers (all 12).
+ */
+#ifndef NO_CS4612
+#define IOTAC_SA_MASK 0x0000FFFF
+#define IOTAC_MSK_MASK 0x000F0000
+#define IOTAC_IODC_MASK 0x06000000
+#define IOTAC_IODC_16_BIT 0x00000000
+#define IOTAC_IODC_10_BIT 0x02000000
+#define IOTAC_IODC_12_BIT 0x04000000
+#define IOTAC_WSPI 0x08000000
+#define IOTAC_RSPI 0x10000000
+#define IOTAC_WSE 0x20000000
+#define IOTAC_WE 0x40000000
+#define IOTAC_RE 0x80000000
+#define IOTAC_SA_SHIFT 0
+#define IOTAC_MSK_SHIFT 16
+#endif
+
+/*
+ * The following defines are for the flags in the I/O trap fast read registers
+ * (all 8).
+ */
+#ifndef NO_CS4612
+#define IOTFR_D_MASK 0x0000FFFF
+#define IOTFR_A_MASK 0x000F0000
+#define IOTFR_R_MASK 0x0F000000
+#define IOTFR_ALL 0x40000000
+#define IOTFR_VL 0x80000000
+#define IOTFR_D_SHIFT 0
+#define IOTFR_A_SHIFT 16
+#define IOTFR_R_SHIFT 24
+#endif
+
+/*
+ * The following defines are for the flags in the I/O trap FIFO register.
+ */
+#ifndef NO_CS4612
+#define IOTFIFO_BA_MASK 0x00003FFF
+#define IOTFIFO_S_MASK 0x00FF0000
+#define IOTFIFO_OF 0x40000000
+#define IOTFIFO_SPIOF 0x80000000
+#define IOTFIFO_BA_SHIFT 0
+#define IOTFIFO_S_SHIFT 16
+#endif
+
+/*
+ * The following defines are for the flags in the I/O trap retry read data
+ * register.
+ */
+#ifndef NO_CS4612
+#define IOTRRD_D_MASK 0x0000FFFF
+#define IOTRRD_RDV 0x80000000
+#define IOTRRD_D_SHIFT 0
+#endif
+
+/*
+ * The following defines are for the flags in the I/O trap FIFO pointer
+ * register.
+ */
+#ifndef NO_CS4612
+#define IOTFP_CA_MASK 0x00003FFF
+#define IOTFP_PA_MASK 0x3FFF0000
+#define IOTFP_CA_SHIFT 0
+#define IOTFP_PA_SHIFT 16
+#endif
+
+/*
+ * The following defines are for the flags in the I/O trap control register.
+ */
+#ifndef NO_CS4612
+#define IOTCR_ITD 0x00000001
+#define IOTCR_HRV 0x00000002
+#define IOTCR_SRV 0x00000004
+#define IOTCR_DTI 0x00000008
+#define IOTCR_DFI 0x00000010
+#define IOTCR_DDP 0x00000020
+#define IOTCR_JTE 0x00000040
+#define IOTCR_PPE 0x00000080
+#endif
+
+/*
+ * The following defines are for the flags in the direct PCI data register.
+ */
+#ifndef NO_CS4612
+#define DPCID_D_MASK 0xFFFFFFFF
+#define DPCID_D_SHIFT 0
+#endif
+
+/*
+ * The following defines are for the flags in the direct PCI address register.
+ */
+#ifndef NO_CS4612
+#define DPCIA_A_MASK 0xFFFFFFFF
+#define DPCIA_A_SHIFT 0
+#endif
+
+/*
+ * The following defines are for the flags in the direct PCI command register.
+ */
+#ifndef NO_CS4612
+#define DPCIC_C_MASK 0x0000000F
+#define DPCIC_C_IOREAD 0x00000002
+#define DPCIC_C_IOWRITE 0x00000003
+#define DPCIC_BE_MASK 0x000000F0
+#endif
+
+/*
+ * The following defines are for the flags in the PC/PCI request register.
+ */
+#ifndef NO_CS4612
+#define PCPCIR_RDC_MASK 0x00000007
+#define PCPCIR_C_MASK 0x00007000
+#define PCPCIR_REQ 0x00008000
+#define PCPCIR_RDC_SHIFT 0
+#define PCPCIR_C_SHIFT 12
+#endif
+
+/*
+ * The following defines are for the flags in the PC/PCI grant register.
+ */
+#ifndef NO_CS4612
+#define PCPCIG_GDC_MASK 0x00000007
+#define PCPCIG_VL 0x00008000
+#define PCPCIG_GDC_SHIFT 0
+#endif
+
+/*
+ * The following defines are for the flags in the PC/PCI master enable
+ * register.
+ */
+#ifndef NO_CS4612
+#define PCPCIEN_EN 0x00000001
+#endif
+
+/*
+ * The following defines are for the flags in the extended PCI power
+ * management control register.
+ */
+#ifndef NO_CS4612
+#define EPCIPMC_GWU 0x00000001
+#define EPCIPMC_FSPC 0x00000002
+#endif
+
+/*
+ * The following defines are for the flags in the SP control register.
+ */
+#define SPCR_RUN 0x00000001
+#define SPCR_STPFR 0x00000002
+#define SPCR_RUNFR 0x00000004
+#define SPCR_TICK 0x00000008
+#define SPCR_DRQEN 0x00000020
+#define SPCR_RSTSP 0x00000040
+#define SPCR_OREN 0x00000080
+#ifndef NO_CS4612
+#define SPCR_PCIINT 0x00000100
+#define SPCR_OINTD 0x00000200
+#define SPCR_CRE 0x00008000
+#endif
+
+/*
+ * The following defines are for the flags in the debug index register.
+ */
+#define DREG_REGID_MASK 0x0000007F
+#define DREG_DEBUG 0x00000080
+#define DREG_RGBK_MASK 0x00000700
+#define DREG_TRAP 0x00000800
+#if !defined(NO_CS4612)
+#if !defined(NO_CS4615)
+#define DREG_TRAPX 0x00001000
+#endif
+#endif
+#define DREG_REGID_SHIFT 0
+#define DREG_RGBK_SHIFT 8
+#define DREG_RGBK_REGID_MASK 0x0000077F
+#define DREG_REGID_R0 0x00000010
+#define DREG_REGID_R1 0x00000011
+#define DREG_REGID_R2 0x00000012
+#define DREG_REGID_R3 0x00000013
+#define DREG_REGID_R4 0x00000014
+#define DREG_REGID_R5 0x00000015
+#define DREG_REGID_R6 0x00000016
+#define DREG_REGID_R7 0x00000017
+#define DREG_REGID_R8 0x00000018
+#define DREG_REGID_R9 0x00000019
+#define DREG_REGID_RA 0x0000001A
+#define DREG_REGID_RB 0x0000001B
+#define DREG_REGID_RC 0x0000001C
+#define DREG_REGID_RD 0x0000001D
+#define DREG_REGID_RE 0x0000001E
+#define DREG_REGID_RF 0x0000001F
+#define DREG_REGID_RA_BUS_LOW 0x00000020
+#define DREG_REGID_RA_BUS_HIGH 0x00000038
+#define DREG_REGID_YBUS_LOW 0x00000050
+#define DREG_REGID_YBUS_HIGH 0x00000058
+#define DREG_REGID_TRAP_0 0x00000100
+#define DREG_REGID_TRAP_1 0x00000101
+#define DREG_REGID_TRAP_2 0x00000102
+#define DREG_REGID_TRAP_3 0x00000103
+#define DREG_REGID_TRAP_4 0x00000104
+#define DREG_REGID_TRAP_5 0x00000105
+#define DREG_REGID_TRAP_6 0x00000106
+#define DREG_REGID_TRAP_7 0x00000107
+#define DREG_REGID_INDIRECT_ADDRESS 0x0000010E
+#define DREG_REGID_TOP_OF_STACK 0x0000010F
+#if !defined(NO_CS4612)
+#if !defined(NO_CS4615)
+#define DREG_REGID_TRAP_8 0x00000110
+#define DREG_REGID_TRAP_9 0x00000111
+#define DREG_REGID_TRAP_10 0x00000112
+#define DREG_REGID_TRAP_11 0x00000113
+#define DREG_REGID_TRAP_12 0x00000114
+#define DREG_REGID_TRAP_13 0x00000115
+#define DREG_REGID_TRAP_14 0x00000116
+#define DREG_REGID_TRAP_15 0x00000117
+#define DREG_REGID_TRAP_16 0x00000118
+#define DREG_REGID_TRAP_17 0x00000119
+#define DREG_REGID_TRAP_18 0x0000011A
+#define DREG_REGID_TRAP_19 0x0000011B
+#define DREG_REGID_TRAP_20 0x0000011C
+#define DREG_REGID_TRAP_21 0x0000011D
+#define DREG_REGID_TRAP_22 0x0000011E
+#define DREG_REGID_TRAP_23 0x0000011F
+#endif
+#endif
+#define DREG_REGID_RSA0_LOW 0x00000200
+#define DREG_REGID_RSA0_HIGH 0x00000201
+#define DREG_REGID_RSA1_LOW 0x00000202
+#define DREG_REGID_RSA1_HIGH 0x00000203
+#define DREG_REGID_RSA2 0x00000204
+#define DREG_REGID_RSA3 0x00000205
+#define DREG_REGID_RSI0_LOW 0x00000206
+#define DREG_REGID_RSI0_HIGH 0x00000207
+#define DREG_REGID_RSI1 0x00000208
+#define DREG_REGID_RSI2 0x00000209
+#define DREG_REGID_SAGUSTATUS 0x0000020A
+#define DREG_REGID_RSCONFIG01_LOW 0x0000020B
+#define DREG_REGID_RSCONFIG01_HIGH 0x0000020C
+#define DREG_REGID_RSCONFIG23_LOW 0x0000020D
+#define DREG_REGID_RSCONFIG23_HIGH 0x0000020E
+#define DREG_REGID_RSDMA01E 0x0000020F
+#define DREG_REGID_RSDMA23E 0x00000210
+#define DREG_REGID_RSD0_LOW 0x00000211
+#define DREG_REGID_RSD0_HIGH 0x00000212
+#define DREG_REGID_RSD1_LOW 0x00000213
+#define DREG_REGID_RSD1_HIGH 0x00000214
+#define DREG_REGID_RSD2_LOW 0x00000215
+#define DREG_REGID_RSD2_HIGH 0x00000216
+#define DREG_REGID_RSD3_LOW 0x00000217
+#define DREG_REGID_RSD3_HIGH 0x00000218
+#define DREG_REGID_SRAR_HIGH 0x0000021A
+#define DREG_REGID_SRAR_LOW 0x0000021B
+#define DREG_REGID_DMA_STATE 0x0000021C
+#define DREG_REGID_CURRENT_DMA_STREAM 0x0000021D
+#define DREG_REGID_NEXT_DMA_STREAM 0x0000021E
+#define DREG_REGID_CPU_STATUS 0x00000300
+#define DREG_REGID_MAC_MODE 0x00000301
+#define DREG_REGID_STACK_AND_REPEAT 0x00000302
+#define DREG_REGID_INDEX0 0x00000304
+#define DREG_REGID_INDEX1 0x00000305
+#define DREG_REGID_DMA_STATE_0_3 0x00000400
+#define DREG_REGID_DMA_STATE_4_7 0x00000404
+#define DREG_REGID_DMA_STATE_8_11 0x00000408
+#define DREG_REGID_DMA_STATE_12_15 0x0000040C
+#define DREG_REGID_DMA_STATE_16_19 0x00000410
+#define DREG_REGID_DMA_STATE_20_23 0x00000414
+#define DREG_REGID_DMA_STATE_24_27 0x00000418
+#define DREG_REGID_DMA_STATE_28_31 0x0000041C
+#define DREG_REGID_DMA_STATE_32_35 0x00000420
+#define DREG_REGID_DMA_STATE_36_39 0x00000424
+#define DREG_REGID_DMA_STATE_40_43 0x00000428
+#define DREG_REGID_DMA_STATE_44_47 0x0000042C
+#define DREG_REGID_DMA_STATE_48_51 0x00000430
+#define DREG_REGID_DMA_STATE_52_55 0x00000434
+#define DREG_REGID_DMA_STATE_56_59 0x00000438
+#define DREG_REGID_DMA_STATE_60_63 0x0000043C
+#define DREG_REGID_DMA_STATE_64_67 0x00000440
+#define DREG_REGID_DMA_STATE_68_71 0x00000444
+#define DREG_REGID_DMA_STATE_72_75 0x00000448
+#define DREG_REGID_DMA_STATE_76_79 0x0000044C
+#define DREG_REGID_DMA_STATE_80_83 0x00000450
+#define DREG_REGID_DMA_STATE_84_87 0x00000454
+#define DREG_REGID_DMA_STATE_88_91 0x00000458
+#define DREG_REGID_DMA_STATE_92_95 0x0000045C
+#define DREG_REGID_TRAP_SELECT 0x00000500
+#define DREG_REGID_TRAP_WRITE_0 0x00000500
+#define DREG_REGID_TRAP_WRITE_1 0x00000501
+#define DREG_REGID_TRAP_WRITE_2 0x00000502
+#define DREG_REGID_TRAP_WRITE_3 0x00000503
+#define DREG_REGID_TRAP_WRITE_4 0x00000504
+#define DREG_REGID_TRAP_WRITE_5 0x00000505
+#define DREG_REGID_TRAP_WRITE_6 0x00000506
+#define DREG_REGID_TRAP_WRITE_7 0x00000507
+#if !defined(NO_CS4612)
+#if !defined(NO_CS4615)
+#define DREG_REGID_TRAP_WRITE_8 0x00000510
+#define DREG_REGID_TRAP_WRITE_9 0x00000511
+#define DREG_REGID_TRAP_WRITE_10 0x00000512
+#define DREG_REGID_TRAP_WRITE_11 0x00000513
+#define DREG_REGID_TRAP_WRITE_12 0x00000514
+#define DREG_REGID_TRAP_WRITE_13 0x00000515
+#define DREG_REGID_TRAP_WRITE_14 0x00000516
+#define DREG_REGID_TRAP_WRITE_15 0x00000517
+#define DREG_REGID_TRAP_WRITE_16 0x00000518
+#define DREG_REGID_TRAP_WRITE_17 0x00000519
+#define DREG_REGID_TRAP_WRITE_18 0x0000051A
+#define DREG_REGID_TRAP_WRITE_19 0x0000051B
+#define DREG_REGID_TRAP_WRITE_20 0x0000051C
+#define DREG_REGID_TRAP_WRITE_21 0x0000051D
+#define DREG_REGID_TRAP_WRITE_22 0x0000051E
+#define DREG_REGID_TRAP_WRITE_23 0x0000051F
+#endif
+#endif
+#define DREG_REGID_MAC0_ACC0_LOW 0x00000600
+#define DREG_REGID_MAC0_ACC1_LOW 0x00000601
+#define DREG_REGID_MAC0_ACC2_LOW 0x00000602
+#define DREG_REGID_MAC0_ACC3_LOW 0x00000603
+#define DREG_REGID_MAC1_ACC0_LOW 0x00000604
+#define DREG_REGID_MAC1_ACC1_LOW 0x00000605
+#define DREG_REGID_MAC1_ACC2_LOW 0x00000606
+#define DREG_REGID_MAC1_ACC3_LOW 0x00000607
+#define DREG_REGID_MAC0_ACC0_MID 0x00000608
+#define DREG_REGID_MAC0_ACC1_MID 0x00000609
+#define DREG_REGID_MAC0_ACC2_MID 0x0000060A
+#define DREG_REGID_MAC0_ACC3_MID 0x0000060B
+#define DREG_REGID_MAC1_ACC0_MID 0x0000060C
+#define DREG_REGID_MAC1_ACC1_MID 0x0000060D
+#define DREG_REGID_MAC1_ACC2_MID 0x0000060E
+#define DREG_REGID_MAC1_ACC3_MID 0x0000060F
+#define DREG_REGID_MAC0_ACC0_HIGH 0x00000610
+#define DREG_REGID_MAC0_ACC1_HIGH 0x00000611
+#define DREG_REGID_MAC0_ACC2_HIGH 0x00000612
+#define DREG_REGID_MAC0_ACC3_HIGH 0x00000613
+#define DREG_REGID_MAC1_ACC0_HIGH 0x00000614
+#define DREG_REGID_MAC1_ACC1_HIGH 0x00000615
+#define DREG_REGID_MAC1_ACC2_HIGH 0x00000616
+#define DREG_REGID_MAC1_ACC3_HIGH 0x00000617
+#define DREG_REGID_RSHOUT_LOW 0x00000620
+#define DREG_REGID_RSHOUT_MID 0x00000628
+#define DREG_REGID_RSHOUT_HIGH 0x00000630
+
+/*
+ * The following defines are for the flags in the DMA stream requestor write
+ */
+#define DSRWP_DSR_MASK 0x0000000F
+#define DSRWP_DSR_BG_RQ 0x00000001
+#define DSRWP_DSR_PRIORITY_MASK 0x00000006
+#define DSRWP_DSR_PRIORITY_0 0x00000000
+#define DSRWP_DSR_PRIORITY_1 0x00000002
+#define DSRWP_DSR_PRIORITY_2 0x00000004
+#define DSRWP_DSR_PRIORITY_3 0x00000006
+#define DSRWP_DSR_RQ_PENDING 0x00000008
+
+/*
+ * The following defines are for the flags in the trap write port register.
+ */
+#define TWPR_TW_MASK 0x0000FFFF
+#define TWPR_TW_SHIFT 0
+
+/*
+ * The following defines are for the flags in the stack pointer write
+ * register.
+ */
+#define SPWR_STKP_MASK 0x0000000F
+#define SPWR_STKP_SHIFT 0
+
+/*
+ * The following defines are for the flags in the SP interrupt register.
+ */
+#define SPIR_FRI 0x00000001
+#define SPIR_DOI 0x00000002
+#define SPIR_GPI2 0x00000004
+#define SPIR_GPI3 0x00000008
+#define SPIR_IP0 0x00000010
+#define SPIR_IP1 0x00000020
+#define SPIR_IP2 0x00000040
+#define SPIR_IP3 0x00000080
+
+/*
+ * The following defines are for the flags in the functional group 1 register.
+ */
+#define FGR1_F1S_MASK 0x0000FFFF
+#define FGR1_F1S_SHIFT 0
+
+/*
+ * The following defines are for the flags in the SP clock status register.
+ */
+#define SPCS_FRI 0x00000001
+#define SPCS_DOI 0x00000002
+#define SPCS_GPI2 0x00000004
+#define SPCS_GPI3 0x00000008
+#define SPCS_IP0 0x00000010
+#define SPCS_IP1 0x00000020
+#define SPCS_IP2 0x00000040
+#define SPCS_IP3 0x00000080
+#define SPCS_SPRUN 0x00000100
+#define SPCS_SLEEP 0x00000200
+#define SPCS_FG 0x00000400
+#define SPCS_ORUN 0x00000800
+#define SPCS_IRQ 0x00001000
+#define SPCS_FGN_MASK 0x0000E000
+#define SPCS_FGN_SHIFT 13
+
+/*
+ * The following defines are for the flags in the SP DMA requestor status
+ * register.
+ */
+#define SDSR_DCS_MASK 0x000000FF
+#define SDSR_DCS_SHIFT 0
+#define SDSR_DCS_NONE 0x00000007
+
+/*
+ * The following defines are for the flags in the frame timer register.
+ */
+#define FRMT_FTV_MASK 0x0000FFFF
+#define FRMT_FTV_SHIFT 0
+
+/*
+ * The following defines are for the flags in the frame timer current count
+ * register.
+ */
+#define FRCC_FCC_MASK 0x0000FFFF
+#define FRCC_FCC_SHIFT 0
+
+/*
+ * The following defines are for the flags in the frame timer save count
+ * register.
+ */
+#define FRSC_FCS_MASK 0x0000FFFF
+#define FRSC_FCS_SHIFT 0
+
+/*
+ * The following define the various flags stored in the scatter/gather
+ * descriptors.
+ */
+#define DMA_SG_NEXT_ENTRY_MASK 0x00000FF8
+#define DMA_SG_SAMPLE_END_MASK 0x0FFF0000
+#define DMA_SG_SAMPLE_END_FLAG 0x10000000
+#define DMA_SG_LOOP_END_FLAG 0x20000000
+#define DMA_SG_SIGNAL_END_FLAG 0x40000000
+#define DMA_SG_SIGNAL_PAGE_FLAG 0x80000000
+#define DMA_SG_NEXT_ENTRY_SHIFT 3
+#define DMA_SG_SAMPLE_END_SHIFT 16
+
+/*
+ * The following define the offsets of the fields within the on-chip generic
+ * DMA requestor.
+ */
+#define DMA_RQ_CONTROL1 0x00000000
+#define DMA_RQ_CONTROL2 0x00000004
+#define DMA_RQ_SOURCE_ADDR 0x00000008
+#define DMA_RQ_DESTINATION_ADDR 0x0000000C
+#define DMA_RQ_NEXT_PAGE_ADDR 0x00000010
+#define DMA_RQ_NEXT_PAGE_SGDESC 0x00000014
+#define DMA_RQ_LOOP_START_ADDR 0x00000018
+#define DMA_RQ_POST_LOOP_ADDR 0x0000001C
+#define DMA_RQ_PAGE_MAP_ADDR 0x00000020
+
+/*
+ * The following defines are for the flags in the first control word of the
+ * on-chip generic DMA requestor.
+ */
+#define DMA_RQ_C1_COUNT_MASK 0x000003FF
+#define DMA_RQ_C1_DESTINATION_SCATTER 0x00001000
+#define DMA_RQ_C1_SOURCE_GATHER 0x00002000
+#define DMA_RQ_C1_DONE_FLAG 0x00004000
+#define DMA_RQ_C1_OPTIMIZE_STATE 0x00008000
+#define DMA_RQ_C1_SAMPLE_END_STATE_MASK 0x00030000
+#define DMA_RQ_C1_FULL_PAGE 0x00000000
+#define DMA_RQ_C1_BEFORE_SAMPLE_END 0x00010000
+#define DMA_RQ_C1_PAGE_MAP_ERROR 0x00020000
+#define DMA_RQ_C1_AT_SAMPLE_END 0x00030000
+#define DMA_RQ_C1_LOOP_END_STATE_MASK 0x000C0000
+#define DMA_RQ_C1_NOT_LOOP_END 0x00000000
+#define DMA_RQ_C1_BEFORE_LOOP_END 0x00040000
+#define DMA_RQ_C1_2PAGE_LOOP_BEGIN 0x00080000
+#define DMA_RQ_C1_LOOP_BEGIN 0x000C0000
+#define DMA_RQ_C1_PAGE_MAP_MASK 0x00300000
+#define DMA_RQ_C1_PM_NONE_PENDING 0x00000000
+#define DMA_RQ_C1_PM_NEXT_PENDING 0x00100000
+#define DMA_RQ_C1_PM_RESERVED 0x00200000
+#define DMA_RQ_C1_PM_LOOP_NEXT_PENDING 0x00300000
+#define DMA_RQ_C1_WRITEBACK_DEST_FLAG 0x00400000
+#define DMA_RQ_C1_WRITEBACK_SRC_FLAG 0x00800000
+#define DMA_RQ_C1_DEST_SIZE_MASK 0x07000000
+#define DMA_RQ_C1_DEST_LINEAR 0x00000000
+#define DMA_RQ_C1_DEST_MOD16 0x01000000
+#define DMA_RQ_C1_DEST_MOD32 0x02000000
+#define DMA_RQ_C1_DEST_MOD64 0x03000000
+#define DMA_RQ_C1_DEST_MOD128 0x04000000
+#define DMA_RQ_C1_DEST_MOD256 0x05000000
+#define DMA_RQ_C1_DEST_MOD512 0x06000000
+#define DMA_RQ_C1_DEST_MOD1024 0x07000000
+#define DMA_RQ_C1_DEST_ON_HOST 0x08000000
+#define DMA_RQ_C1_SOURCE_SIZE_MASK 0x70000000
+#define DMA_RQ_C1_SOURCE_LINEAR 0x00000000
+#define DMA_RQ_C1_SOURCE_MOD16 0x10000000
+#define DMA_RQ_C1_SOURCE_MOD32 0x20000000
+#define DMA_RQ_C1_SOURCE_MOD64 0x30000000
+#define DMA_RQ_C1_SOURCE_MOD128 0x40000000
+#define DMA_RQ_C1_SOURCE_MOD256 0x50000000
+#define DMA_RQ_C1_SOURCE_MOD512 0x60000000
+#define DMA_RQ_C1_SOURCE_MOD1024 0x70000000
+#define DMA_RQ_C1_SOURCE_ON_HOST 0x80000000
+#define DMA_RQ_C1_COUNT_SHIFT 0
+
+/*
+ * The following defines are for the flags in the second control word of the
+ * on-chip generic DMA requestor.
+ */
+#define DMA_RQ_C2_VIRTUAL_CHANNEL_MASK 0x0000003F
+#define DMA_RQ_C2_VIRTUAL_SIGNAL_MASK 0x00000300
+#define DMA_RQ_C2_NO_VIRTUAL_SIGNAL 0x00000000
+#define DMA_RQ_C2_SIGNAL_EVERY_DMA 0x00000100
+#define DMA_RQ_C2_SIGNAL_SOURCE_PINGPONG 0x00000200
+#define DMA_RQ_C2_SIGNAL_DEST_PINGPONG 0x00000300
+#define DMA_RQ_C2_AUDIO_CONVERT_MASK 0x0000F000
+#define DMA_RQ_C2_AC_NONE 0x00000000
+#define DMA_RQ_C2_AC_8_TO_16_BIT 0x00001000
+#define DMA_RQ_C2_AC_MONO_TO_STEREO 0x00002000
+#define DMA_RQ_C2_AC_ENDIAN_CONVERT 0x00004000
+#define DMA_RQ_C2_AC_SIGNED_CONVERT 0x00008000
+#define DMA_RQ_C2_LOOP_END_MASK 0x0FFF0000
+#define DMA_RQ_C2_LOOP_MASK 0x30000000
+#define DMA_RQ_C2_NO_LOOP 0x00000000
+#define DMA_RQ_C2_ONE_PAGE_LOOP 0x10000000
+#define DMA_RQ_C2_TWO_PAGE_LOOP 0x20000000
+#define DMA_RQ_C2_MULTI_PAGE_LOOP 0x30000000
+#define DMA_RQ_C2_SIGNAL_LOOP_BACK 0x40000000
+#define DMA_RQ_C2_SIGNAL_POST_BEGIN_PAGE 0x80000000
+#define DMA_RQ_C2_VIRTUAL_CHANNEL_SHIFT 0
+#define DMA_RQ_C2_LOOP_END_SHIFT 16
+
+/*
+ * The following defines are for the flags in the source and destination words
+ * of the on-chip generic DMA requestor.
+ */
+#define DMA_RQ_SD_ADDRESS_MASK 0x0000FFFF
+#define DMA_RQ_SD_MEMORY_ID_MASK 0x000F0000
+#define DMA_RQ_SD_SP_PARAM_ADDR 0x00000000
+#define DMA_RQ_SD_SP_SAMPLE_ADDR 0x00010000
+#define DMA_RQ_SD_SP_PROGRAM_ADDR 0x00020000
+#define DMA_RQ_SD_SP_DEBUG_ADDR 0x00030000
+#define DMA_RQ_SD_OMNIMEM_ADDR 0x000E0000
+#define DMA_RQ_SD_END_FLAG 0x40000000
+#define DMA_RQ_SD_ERROR_FLAG 0x80000000
+#define DMA_RQ_SD_ADDRESS_SHIFT 0
+
+/*
+ * The following defines are for the flags in the page map address word of the
+ * on-chip generic DMA requestor.
+ */
+#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_MASK 0x00000FF8
+#define DMA_RQ_PMA_PAGE_TABLE_MASK 0xFFFFF000
+#define DMA_RQ_PMA_LOOP_THIRD_PAGE_ENTRY_SHIFT 3
+#define DMA_RQ_PMA_PAGE_TABLE_SHIFT 12
+
+#define BA1_VARIDEC_BUF_1 0x000
+
+#define BA1_PDTC 0x0c0 /* BA1_PLAY_DMA_TRANSACTION_COUNT_REG */
+#define BA1_PFIE 0x0c4 /* BA1_PLAY_FORMAT_&_INTERRUPT_ENABLE_REG */
+#define BA1_PBA 0x0c8 /* BA1_PLAY_BUFFER_ADDRESS */
+#define BA1_PVOL 0x0f8 /* BA1_PLAY_VOLUME_REG */
+#define BA1_PSRC 0x288 /* BA1_PLAY_SAMPLE_RATE_CORRECTION_REG */
+#define BA1_PCTL 0x2a4 /* BA1_PLAY_CONTROL_REG */
+#define BA1_PPI 0x2b4 /* BA1_PLAY_PHASE_INCREMENT_REG */
+
+#define BA1_CCTL 0x064 /* BA1_CAPTURE_CONTROL_REG */
+#define BA1_CIE 0x104 /* BA1_CAPTURE_INTERRUPT_ENABLE_REG */
+#define BA1_CBA 0x10c /* BA1_CAPTURE_BUFFER_ADDRESS */
+#define BA1_CSRC 0x2c8 /* BA1_CAPTURE_SAMPLE_RATE_CORRECTION_REG */
+#define BA1_CCI 0x2d8 /* BA1_CAPTURE_COEFFICIENT_INCREMENT_REG */
+#define BA1_CD 0x2e0 /* BA1_CAPTURE_DELAY_REG */
+#define BA1_CPI 0x2f4 /* BA1_CAPTURE_PHASE_INCREMENT_REG */
+#define BA1_CVOL 0x2f8 /* BA1_CAPTURE_VOLUME_REG */
+
+#define BA1_CFG1 0x134 /* BA1_CAPTURE_FRAME_GROUP_1_REG */
+#define BA1_CFG2 0x138 /* BA1_CAPTURE_FRAME_GROUP_2_REG */
+#define BA1_CCST 0x13c /* BA1_CAPTURE_CONSTANT_REG */
+#define BA1_CSPB 0x340 /* BA1_CAPTURE_SPB_ADDRESS */
+
+/*
+ *
+ */
+
+#define CS46XX_MODE_OUTPUT (1<<0) /* MIDI UART - output */
+#define CS46XX_MODE_INPUT (1<<1) /* MIDI UART - input */
+
+/*
+ *
+ */
+
+#define SAVE_REG_MAX 0x10
+#define POWER_DOWN_ALL 0x7f0f
+
+/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */
+#define MAX_NR_AC97 4
+#define CS46XX_PRIMARY_CODEC_INDEX 0
+#define CS46XX_SECONDARY_CODEC_INDEX 1
+#define CS46XX_SECONDARY_CODEC_OFFSET 0x80
+#define CS46XX_DSP_CAPTURE_CHANNEL 1
+
+/* capture */
+#define CS46XX_DSP_CAPTURE_CHANNEL 1
+
+/* mixer */
+#define CS46XX_MIXER_SPDIF_INPUT_ELEMENT 1
+#define CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT 2
+
+
+struct snd_cs46xx_pcm {
+ struct snd_dma_buffer hw_buf;
+
+ unsigned int ctl;
+ unsigned int shift; /* Shift count to trasform frames in bytes */
+ struct snd_pcm_indirect pcm_rec;
+ struct snd_pcm_substream *substream;
+
+ struct dsp_pcm_channel_descriptor * pcm_channel;
+
+ int pcm_channel_id; /* Fron Rear, Center Lfe ... */
+};
+
+struct snd_cs46xx_region {
+ char name[24];
+ unsigned long base;
+ void __iomem *remap_addr;
+ unsigned long size;
+};
+
+struct snd_cs46xx {
+ int irq;
+ unsigned long ba0_addr;
+ unsigned long ba1_addr;
+ union {
+ struct {
+ struct snd_cs46xx_region ba0;
+ struct snd_cs46xx_region data0;
+ struct snd_cs46xx_region data1;
+ struct snd_cs46xx_region pmem;
+ struct snd_cs46xx_region reg;
+ } name;
+ struct snd_cs46xx_region idx[5];
+ } region;
+
+ unsigned int mode;
+
+ struct {
+ struct snd_dma_buffer hw_buf;
+
+ unsigned int ctl;
+ unsigned int shift; /* Shift count to trasform frames in bytes */
+ struct snd_pcm_indirect pcm_rec;
+ struct snd_pcm_substream *substream;
+ } capt;
+
+
+ int nr_ac97_codecs;
+ struct snd_ac97_bus *ac97_bus;
+ struct snd_ac97 *ac97[MAX_NR_AC97];
+
+ struct pci_dev *pci;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_substream *midi_input;
+ struct snd_rawmidi_substream *midi_output;
+
+ spinlock_t reg_lock;
+ unsigned int midcr;
+ unsigned int uartm;
+
+ int amplifier;
+ void (*amplifier_ctrl)(struct snd_cs46xx *, int);
+ void (*active_ctrl)(struct snd_cs46xx *, int);
+ void (*mixer_init)(struct snd_cs46xx *);
+
+ int acpi_port;
+ struct snd_kcontrol *eapd_switch; /* for amplifier hack */
+ int accept_valid; /* accept mmap valid (for OSS) */
+ int in_suspend;
+
+ struct gameport *gameport;
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ struct mutex spos_mutex;
+
+ struct dsp_spos_instance * dsp_spos_instance;
+
+ struct snd_pcm *pcm_rear;
+ struct snd_pcm *pcm_center_lfe;
+ struct snd_pcm *pcm_iec958;
+
+#define CS46XX_DSP_MODULES 5
+ struct dsp_module_desc *modules[CS46XX_DSP_MODULES];
+#else /* for compatibility */
+ struct snd_cs46xx_pcm *playback_pcm;
+ unsigned int play_ctl;
+
+ struct ba1_struct *ba1;
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+ u32 *saved_regs;
+#endif
+};
+
+int snd_cs46xx_create(struct snd_card *card,
+ struct pci_dev *pci,
+ int external_amp, int thinkpad);
+extern const struct dev_pm_ops snd_cs46xx_pm;
+
+int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device);
+int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device);
+int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device);
+int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device);
+int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device);
+int snd_cs46xx_midi(struct snd_cs46xx *chip, int device);
+int snd_cs46xx_start_dsp(struct snd_cs46xx *chip);
+int snd_cs46xx_gameport(struct snd_cs46xx *chip);
+
+#endif /* __SOUND_CS46XX_H */
diff --git a/sound/pci/cs46xx/cs46xx_dsp_scb_types.h b/sound/pci/cs46xx/cs46xx_dsp_scb_types.h
new file mode 100644
index 000000000000..7339c38570be
--- /dev/null
+++ b/sound/pci/cs46xx/cs46xx_dsp_scb_types.h
@@ -0,0 +1,1198 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *
+ * NOTE: comments are copy/paste from cwcemb80.lst
+ * provided by Tom Woller at Cirrus (my only
+ * documentation about the SP OS running inside
+ * the DSP)
+ */
+
+#ifndef __CS46XX_DSP_SCB_TYPES_H__
+#define __CS46XX_DSP_SCB_TYPES_H__
+
+#include <asm/byteorder.h>
+
+#ifndef ___DSP_DUAL_16BIT_ALLOC
+#if defined(__LITTLE_ENDIAN)
+#define ___DSP_DUAL_16BIT_ALLOC(a,b) u16 a; u16 b;
+#elif defined(__BIG_ENDIAN)
+#define ___DSP_DUAL_16BIT_ALLOC(a,b) u16 b; u16 a;
+#else
+#error Not __LITTLE_ENDIAN and not __BIG_ENDIAN, then what ???
+#endif
+#endif
+
+/* This structs are used internally by the SP */
+
+struct dsp_basic_dma_req {
+ /* DMA Requestor Word 0 (DCW) fields:
+
+ 31 [30-28]27 [26:24] 23 22 21 20 [19:18] [17:16] 15 14 13 12 11 10 9 8 7 6 [5:0]
+ _______________________________________________________________________________________
+ |S| SBT |D| DBT |wb|wb| | | LS | SS |Opt|Do|SSG|DSG| | | | | | | Dword |
+ |H|_____ |H|_________|S_|D |__|__|______|_______|___|ne|__ |__ |__|__|_|_|_|_|_Count -1|
+ */
+ u32 dcw; /* DMA Control Word */
+ u32 dmw; /* DMA Mode Word */
+ u32 saw; /* Source Address Word */
+ u32 daw; /* Destination Address Word */
+};
+
+struct dsp_scatter_gather_ext {
+ u32 npaw; /* Next-Page Address Word */
+
+ /* DMA Requestor Word 5 (NPCW) fields:
+
+ 31-30 29 28 [27:16] [15:12] [11:3] [2:0]
+ _________________________________________________________________________________________
+ |SV |LE|SE| Sample-end byte offset | | Page-map entry offset for next | |
+ |page|__|__| ___________________________|_________|__page, if !sample-end___________|____|
+ */
+ u32 npcw; /* Next-Page Control Word */
+ u32 lbaw; /* Loop-Begin Address Word */
+ u32 nplbaw; /* Next-Page after Loop-Begin Address Word */
+ u32 sgaw; /* Scatter/Gather Address Word */
+};
+
+struct dsp_volume_control {
+ ___DSP_DUAL_16BIT_ALLOC(
+ rightTarg, /* Target volume for left & right channels */
+ leftTarg
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ rightVol, /* Current left & right channel volumes */
+ leftVol
+ )
+};
+
+/* Generic stream control block (SCB) structure definition */
+struct dsp_generic_scb {
+ /* For streaming I/O, the DSP should never alter any words in the DMA
+ requestor or the scatter/gather extension. Only ad hoc DMA request
+ streams are free to alter the requestor (currently only occur in the
+ DOS-based MIDI controller and in debugger-inserted code).
+
+ If an SCB does not have any associated DMA requestor, these 9 ints
+ may be freed for use by other tasks, but the pointer to the SCB must
+ still be such that the insOrd:nextSCB appear at offset 9 from the
+ SCB pointer.
+
+ Basic (non scatter/gather) DMA requestor (4 ints)
+ */
+
+ /* Initialized by the host, only modified by DMA
+ R/O for the DSP task */
+ struct dsp_basic_dma_req basic_req; /* Optional */
+
+ /* Scatter/gather DMA requestor extension (5 ints)
+ Initialized by the host, only modified by DMA
+ DSP task never needs to even read these.
+ */
+ struct dsp_scatter_gather_ext sg_ext; /* Optional */
+
+ /* Sublist pointer & next stream control block (SCB) link.
+ Initialized & modified by the host R/O for the DSP task
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb, /* REQUIRED */
+ sub_list_ptr /* REQUIRED */
+ )
+
+ /* Pointer to this tasks parameter block & stream function pointer
+ Initialized by the host R/O for the DSP task */
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point, /* REQUIRED */
+ this_spb /* REQUIRED */
+ )
+
+ /* rsConfig register for stream buffer (rsDMA reg.
+ is loaded from basicReq.daw for incoming streams, or
+ basicReq.saw, for outgoing streams)
+
+ 31 30 29 [28:24] [23:16] 15 14 13 12 11 10 9 8 7 6 5 4 [3:0]
+ ______________________________________________________________________________
+ |DMA |D|maxDMAsize| streamNum|dir|p| | | | | | |ds |shr 1|rev Cy | mod |
+ |prio |_|__________|__________|___|_|__|__|__|__|_|_|___|_____|_______|_______|
+ 31 30 29 [28:24] [23:16] 15 14 13 12 11 10 9 8 7 6 5 4 [3:0]
+
+
+ Initialized by the host R/O for the DSP task
+ */
+ u32 strm_rs_config; /* REQUIRED */
+ //
+ /* On mixer input streams: indicates mixer input stream configuration
+ On Tees, this is copied from the stream being snooped
+
+ Stream sample pointer & MAC-unit mode for this stream
+
+ Initialized by the host Updated by the DSP task
+ */
+ u32 strm_buf_ptr; /* REQUIRED */
+
+ /* On mixer input streams: points to next mixer input and is updated by the
+ mixer subroutine in the "parent" DSP task
+ (least-significant 16 bits are preserved, unused)
+
+ On Tees, the pointer is copied from the stream being snooped on
+ initialization, and, subsequently, it is copied into the
+ stream being snooped.
+
+ On wavetable/3D voices: the strmBufPtr will use all 32 bits to allow for
+ fractional phase accumulation
+
+ Fractional increment per output sample in the input sample buffer
+
+ (Not used on mixer input streams & redefined on Tees)
+ On wavetable/3D voices: this 32-bit word specifies the integer.fractional
+ increment per output sample.
+ */
+ u32 strmPhiIncr;
+
+
+ /* Standard stereo volume control
+ Initialized by the host (host updates target volumes)
+
+ Current volumes update by the DSP task
+ On mixer input streams: required & updated by the mixer subroutine in the
+ "parent" DSP task
+
+ On Tees, both current & target volumes are copied up on initialization,
+ and, subsequently, the target volume is copied up while the current
+ volume is copied down.
+
+ These two 32-bit words are redefined for wavetable & 3-D voices.
+ */
+ struct dsp_volume_control vol_ctrl_t; /* Optional */
+};
+
+
+struct dsp_spos_control_block {
+ /* WARNING: Certain items in this structure are modified by the host
+ Any dword that can be modified by the host, must not be
+ modified by the SP as the host can only do atomic dword
+ writes, and to do otherwise, even a read modify write,
+ may lead to corrupted data on the SP.
+
+ This rule does not apply to one off boot time initialisation prior to starting the SP
+ */
+
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* First element on the Hyper forground task tree */
+ hfg_tree_root_ptr, /* HOST */
+ /* First 3 dwords are written by the host and read-only on the DSP */
+ hfg_stack_base /* HOST */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* Point to this data structure to enable easy access */
+ spos_cb_ptr, /* SP */
+ prev_task_tree_ptr /* SP && HOST */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* Currently Unused */
+ xxinterval_timer_period,
+ /* Enable extension of SPOS data structure */
+ HFGSPB_ptr
+ )
+
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ xxnum_HFG_ticks_thisInterval,
+ /* Modified by the DSP */
+ xxnum_tntervals
+ )
+
+
+ /* Set by DSP upon encountering a trap (breakpoint) or a spurious
+ interrupt. The host must clear this dword after reading it
+ upon receiving spInt1. */
+ ___DSP_DUAL_16BIT_ALLOC(
+ spurious_int_flag, /* (Host & SP) Nature of the spurious interrupt */
+ trap_flag /* (Host & SP) Nature of detected Trap */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ unused2,
+ invalid_IP_flag /* (Host & SP ) Indicate detection of invalid instruction pointer */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* pointer to forground task tree header for use in next task search */
+ fg_task_tree_hdr_ptr, /* HOST */
+ /* Data structure for controlling synchronous link update */
+ hfg_sync_update_ptr /* HOST */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ begin_foreground_FCNT, /* SP */
+ /* Place holder for holding sleep timing */
+ last_FCNT_before_sleep /* SP */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ unused7, /* SP */
+ next_task_treePtr /* SP */
+ )
+
+ u32 unused5;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ active_flags, /* SP */
+ /* State flags, used to assist control of execution of Hyper Forground */
+ HFG_flags /* SP */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ unused9,
+ unused8
+ )
+
+ /* Space for saving enough context so that we can set up enough
+ to save some more context.
+ */
+ u32 rFE_save_for_invalid_IP;
+ u32 r32_save_for_spurious_int;
+ u32 r32_save_for_trap;
+ u32 r32_save_for_HFG;
+};
+
+/* SPB for MIX_TO_OSTREAM algorithm family */
+struct dsp_mix2_ostream_spb
+{
+ /* 16b.16b integer.frac approximation to the
+ number of 3 sample triplets to output each
+ frame. (approximation must be floor, to
+ insure that the fractional error is always
+ positive)
+ */
+ u32 outTripletsPerFrame;
+
+ /* 16b.16b integer.frac accumulated number of
+ output triplets since the start of group
+ */
+ u32 accumOutTriplets;
+};
+
+/* SCB for Timing master algorithm */
+struct dsp_timing_master_scb {
+ /* First 12 dwords from generic_scb_t */
+ struct dsp_basic_dma_req basic_req; /* Optional */
+ struct dsp_scatter_gather_ext sg_ext; /* Optional */
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb, /* REQUIRED */
+ sub_list_ptr /* REQUIRED */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point, /* REQUIRED */
+ this_spb /* REQUIRED */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* Initial values are 0000:xxxx */
+ reserved,
+ extra_sample_accum
+ )
+
+
+ /* Initial values are xxxx:0000
+ hi: Current CODEC output FIFO pointer
+ (0 to 0x0f)
+ lo: Flag indicating that the CODEC
+ FIFO is sync'd (host clears to
+ resynchronize the FIFO pointer
+ upon start/restart)
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ codec_FIFO_syncd,
+ codec_FIFO_ptr
+ )
+
+ /* Init. 8000:0005 for 44.1k
+ 8000:0001 for 48k
+ hi: Fractional sample accumulator 0.16b
+ lo: Number of frames remaining to be
+ processed in the current group of
+ frames
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ frac_samp_accum_qm1,
+ TM_frms_left_in_group
+ )
+
+ /* Init. 0001:0005 for 44.1k
+ 0000:0001 for 48k
+ hi: Fractional sample correction factor 0.16b
+ to be added every frameGroupLength frames
+ to correct for truncation error in
+ nsamp_per_frm_q15
+ lo: Number of frames in the group
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ frac_samp_correction_qm1,
+ TM_frm_group_length
+ )
+
+ /* Init. 44.1k*65536/8k = 0x00058333 for 44.1k
+ 48k*65536/8k = 0x00060000 for 48k
+ 16b.16b integer.frac approximation to the
+ number of samples to output each frame.
+ (approximation must be floor, to insure */
+ u32 nsamp_per_frm_q15;
+};
+
+/* SCB for CODEC output algorithm */
+struct dsp_codec_output_scb {
+ /* First 13 dwords from generic_scb_t */
+ struct dsp_basic_dma_req basic_req; /* Optional */
+ struct dsp_scatter_gather_ext sg_ext; /* Optional */
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb, /* REQUIRED */
+ sub_list_ptr /* REQUIRED */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point, /* REQUIRED */
+ this_spb /* REQUIRED */
+ )
+
+ u32 strm_rs_config; /* REQUIRED */
+
+ u32 strm_buf_ptr; /* REQUIRED */
+
+ /* NOTE: The CODEC output task reads samples from the first task on its
+ sublist at the stream buffer pointer (init. to lag DMA destination
+ address word). After the required number of samples is transferred,
+ the CODEC output task advances sub_list_ptr->strm_buf_ptr past the samples
+ consumed.
+ */
+
+ /* Init. 0000:0010 for SDout
+ 0060:0010 for SDout2
+ 0080:0010 for SDout3
+ hi: Base IO address of FIFO to which
+ the left-channel samples are to
+ be written.
+ lo: Displacement for the base IO
+ address for left-channel to obtain
+ the base IO address for the FIFO
+ to which the right-channel samples
+ are to be written.
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ left_chan_base_IO_addr,
+ right_chan_IO_disp
+ )
+
+
+ /* Init: 0x0080:0004 for non-AC-97
+ Init: 0x0080:0000 for AC-97
+ hi: Exponential volume change rate
+ for input stream
+ lo: Positive shift count to shift the
+ 16-bit input sample to obtain the
+ 32-bit output word
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ CO_scale_shift_count,
+ CO_exp_vol_change_rate
+ )
+
+ /* Pointer to SCB at end of input chain */
+ ___DSP_DUAL_16BIT_ALLOC(
+ reserved,
+ last_sub_ptr
+ )
+};
+
+/* SCB for CODEC input algorithm */
+struct dsp_codec_input_scb {
+ /* First 13 dwords from generic_scb_t */
+ struct dsp_basic_dma_req basic_req; /* Optional */
+ struct dsp_scatter_gather_ext sg_ext; /* Optional */
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb, /* REQUIRED */
+ sub_list_ptr /* REQUIRED */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point, /* REQUIRED */
+ this_spb /* REQUIRED */
+ )
+
+ u32 strm_rs_config; /* REQUIRED */
+ u32 strm_buf_ptr; /* REQUIRED */
+
+ /* NOTE: The CODEC input task reads samples from the hardware FIFO
+ sublist at the DMA source address word (sub_list_ptr->basic_req.saw).
+ After the required number of samples is transferred, the CODEC
+ output task advances sub_list_ptr->basic_req.saw past the samples
+ consumed. SPuD must initialize the sub_list_ptr->basic_req.saw
+ to point half-way around from the initial sub_list_ptr->strm_nuf_ptr
+ to allow for lag/lead.
+ */
+
+ /* Init. 0000:0010 for SDout
+ 0060:0010 for SDout2
+ 0080:0010 for SDout3
+ hi: Base IO address of FIFO to which
+ the left-channel samples are to
+ be written.
+ lo: Displacement for the base IO
+ address for left-channel to obtain
+ the base IO address for the FIFO
+ to which the right-channel samples
+ are to be written.
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ rightChanINdisp,
+ left_chan_base_IN_addr
+ )
+ /* Init. ?:fffc
+ lo: Negative shift count to shift the
+ 32-bit input dword to obtain the
+ 16-bit sample msb-aligned (count
+ is negative to shift left)
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ scaleShiftCount,
+ reserver1
+ )
+
+ u32 reserved2;
+};
+
+
+struct dsp_pcm_serial_input_scb {
+ /* First 13 dwords from generic_scb_t */
+ struct dsp_basic_dma_req basic_req; /* Optional */
+ struct dsp_scatter_gather_ext sg_ext; /* Optional */
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb, /* REQUIRED */
+ sub_list_ptr /* REQUIRED */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point, /* REQUIRED */
+ this_spb /* REQUIRED */
+ )
+
+ u32 strm_buf_ptr; /* REQUIRED */
+ u32 strm_rs_config; /* REQUIRED */
+
+ /* Init. Ptr to CODEC input SCB
+ hi: Pointer to the SCB containing the
+ input buffer to which CODEC input
+ samples are written
+ lo: Flag indicating the link to the CODEC
+ input task is to be initialized
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ init_codec_input_link,
+ codec_input_buf_scb
+ )
+
+ /* Initialized by the host (host updates target volumes) */
+ struct dsp_volume_control psi_vol_ctrl;
+
+};
+
+struct dsp_src_task_scb {
+ ___DSP_DUAL_16BIT_ALLOC(
+ frames_left_in_gof,
+ gofs_left_in_sec
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ const2_thirds,
+ num_extra_tnput_samples
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ cor_per_gof,
+ correction_per_sec
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ output_buf_producer_ptr,
+ junk_DMA_MID
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ gof_length,
+ gofs_per_sec
+ )
+
+ u32 input_buf_strm_config;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ reserved_for_SRC_use,
+ input_buf_consumer_ptr
+ )
+
+ u32 accum_phi;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ exp_src_vol_change_rate,
+ input_buf_producer_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ src_next_scb,
+ src_sub_list_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ src_entry_point,
+ src_this_sbp
+ )
+
+ u32 src_strm_rs_config;
+ u32 src_strm_buf_ptr;
+
+ u32 phiIncr6int_26frac;
+
+ struct dsp_volume_control src_vol_ctrl;
+};
+
+struct dsp_decimate_by_pow2_scb {
+ /* decimationFactor = 2, 4, or 8 (larger factors waste too much memory
+ when compared to cascading decimators)
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ dec2_coef_base_ptr,
+ dec2_coef_increment
+ )
+
+ /* coefIncrement = 128 / decimationFactor (for our ROM filter)
+ coefBasePtr = 0x8000 (for our ROM filter)
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ dec2_in_samples_per_out_triplet,
+ dec2_extra_in_samples
+ )
+ /* extraInSamples: # of accumulated, unused input samples (init. to 0)
+ inSamplesPerOutTriplet = 3 * decimationFactor
+ */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ dec2_const2_thirds,
+ dec2_half_num_taps_mp5
+ )
+ /* halfNumTapsM5: (1/2 number of taps in decimation filter) minus 5
+ const2thirds: constant 2/3 in 16Q0 format (sign.15)
+ */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ dec2_output_buf_producer_ptr,
+ dec2_junkdma_mid
+ )
+
+ u32 dec2_reserved2;
+
+ u32 dec2_input_nuf_strm_config;
+ /* inputBufStrmConfig: rsConfig for the input buffer to the decimator
+ (buffer size = decimationFactor * 32 dwords)
+ */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ dec2_phi_incr,
+ dec2_input_buf_consumer_ptr
+ )
+ /* inputBufConsumerPtr: Input buffer read pointer (into SRC filter)
+ phiIncr = decimationFactor * 4
+ */
+
+ u32 dec2_reserved3;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ dec2_exp_vol_change_rate,
+ dec2_input_buf_producer_ptr
+ )
+ /* inputBufProducerPtr: Input buffer write pointer
+ expVolChangeRate: Exponential volume change rate for possible
+ future mixer on input streams
+ */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ dec2_next_scb,
+ dec2_sub_list_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ dec2_entry_point,
+ dec2_this_spb
+ )
+
+ u32 dec2_strm_rs_config;
+ u32 dec2_strm_buf_ptr;
+
+ u32 dec2_reserved4;
+
+ struct dsp_volume_control dec2_vol_ctrl; /* Not used! */
+};
+
+struct dsp_vari_decimate_scb {
+ ___DSP_DUAL_16BIT_ALLOC(
+ vdec_frames_left_in_gof,
+ vdec_gofs_left_in_sec
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ vdec_const2_thirds,
+ vdec_extra_in_samples
+ )
+ /* extraInSamples: # of accumulated, unused input samples (init. to 0)
+ const2thirds: constant 2/3 in 16Q0 format (sign.15) */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ vdec_cor_per_gof,
+ vdec_correction_per_sec
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ vdec_output_buf_producer_ptr,
+ vdec_input_buf_consumer_ptr
+ )
+ /* inputBufConsumerPtr: Input buffer read pointer (into SRC filter) */
+ ___DSP_DUAL_16BIT_ALLOC(
+ vdec_gof_length,
+ vdec_gofs_per_sec
+ )
+
+ u32 vdec_input_buf_strm_config;
+ /* inputBufStrmConfig: rsConfig for the input buffer to the decimator
+ (buffer size = 64 dwords) */
+ u32 vdec_coef_increment;
+ /* coefIncrement = - 128.0 / decimationFactor (as a 32Q15 number) */
+
+ u32 vdec_accumphi;
+ /* accumPhi: accumulated fractional phase increment (6.26) */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ vdec_exp_vol_change_rate,
+ vdec_input_buf_producer_ptr
+ )
+ /* inputBufProducerPtr: Input buffer write pointer
+ expVolChangeRate: Exponential volume change rate for possible
+ future mixer on input streams */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ vdec_next_scb,
+ vdec_sub_list_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ vdec_entry_point,
+ vdec_this_spb
+ )
+
+ u32 vdec_strm_rs_config;
+ u32 vdec_strm_buf_ptr;
+
+ u32 vdec_phi_incr_6int_26frac;
+
+ struct dsp_volume_control vdec_vol_ctrl;
+};
+
+
+/* SCB for MIX_TO_OSTREAM algorithm family */
+struct dsp_mix2_ostream_scb {
+ /* First 13 dwords from generic_scb_t */
+ struct dsp_basic_dma_req basic_req; /* Optional */
+ struct dsp_scatter_gather_ext sg_ext; /* Optional */
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb, /* REQUIRED */
+ sub_list_ptr /* REQUIRED */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point, /* REQUIRED */
+ this_spb /* REQUIRED */
+ )
+
+ u32 strm_rs_config; /* REQUIRED */
+ u32 strm_buf_ptr; /* REQUIRED */
+
+
+ /* hi: Number of mixed-down input triplets
+ computed since start of group
+ lo: Number of frames remaining to be
+ processed in the current group of
+ frames
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ frames_left_in_group,
+ accum_input_triplets
+ )
+
+ /* hi: Exponential volume change rate
+ for mixer on input streams
+ lo: Number of frames in the group
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ frame_group_length,
+ exp_vol_change_rate
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ const_FFFF,
+ const_zero
+ )
+};
+
+
+/* SCB for S16_MIX algorithm */
+struct dsp_mix_only_scb {
+ /* First 13 dwords from generic_scb_t */
+ struct dsp_basic_dma_req basic_req; /* Optional */
+ struct dsp_scatter_gather_ext sg_ext; /* Optional */
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb, /* REQUIRED */
+ sub_list_ptr /* REQUIRED */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point, /* REQUIRED */
+ this_spb /* REQUIRED */
+ )
+
+ u32 strm_rs_config; /* REQUIRED */
+ u32 strm_buf_ptr; /* REQUIRED */
+
+ u32 reserved;
+ struct dsp_volume_control vol_ctrl;
+};
+
+/* SCB for the async. CODEC input algorithm */
+struct dsp_async_codec_input_scb {
+ u32 io_free2;
+
+ u32 io_current_total;
+ u32 io_previous_total;
+
+ u16 io_count;
+ u16 io_count_limit;
+
+ u16 o_fifo_base_addr;
+ u16 ost_mo_format;
+ /* 1 = stereo; 0 = mono
+ xxx for ASER 1 (not allowed); 118 for ASER2 */
+
+ u32 ostrm_rs_config;
+ u32 ostrm_buf_ptr;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ io_sclks_per_lr_clk,
+ io_io_enable
+ )
+
+ u32 io_free4;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ io_next_scb,
+ io_sub_list_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ io_entry_point,
+ io_this_spb
+ )
+
+ u32 istrm_rs_config;
+ u32 istrm_buf_ptr;
+
+ /* Init. 0000:8042: for ASER1
+ 0000:8044: for ASER2 */
+ ___DSP_DUAL_16BIT_ALLOC(
+ io_stat_reg_addr,
+ iofifo_pointer
+ )
+
+ /* Init 1 stero:100 ASER1
+ Init 0 mono:110 ASER2
+ */
+ ___DSP_DUAL_16BIT_ALLOC(
+ ififo_base_addr,
+ ist_mo_format
+ )
+
+ u32 i_free;
+};
+
+
+/* SCB for the SP/DIF CODEC input and output */
+struct dsp_spdifiscb {
+ ___DSP_DUAL_16BIT_ALLOC(
+ status_ptr,
+ status_start_ptr
+ )
+
+ u32 current_total;
+ u32 previous_total;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ count,
+ count_limit
+ )
+
+ u32 status_data;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ status,
+ free4
+ )
+
+ u32 free3;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ free2,
+ bit_count
+ )
+
+ u32 temp_status;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_SCB,
+ sub_list_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point,
+ this_spb
+ )
+
+ u32 strm_rs_config;
+ u32 strm_buf_ptr;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ stat_reg_addr,
+ fifo_pointer
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ fifo_base_addr,
+ st_mo_format
+ )
+
+ u32 free1;
+};
+
+
+/* SCB for the SP/DIF CODEC input and output */
+struct dsp_spdifoscb {
+
+ u32 free2;
+
+ u32 free3[4];
+
+ /* Need to be here for compatibility with AsynchFGTxCode */
+ u32 strm_rs_config;
+
+ u32 strm_buf_ptr;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ status,
+ free5
+ )
+
+ u32 free4;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb,
+ sub_list_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point,
+ this_spb
+ )
+
+ u32 free6[2];
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ stat_reg_addr,
+ fifo_pointer
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ fifo_base_addr,
+ st_mo_format
+ )
+
+ u32 free1;
+};
+
+
+struct dsp_asynch_fg_rx_scb {
+ ___DSP_DUAL_16BIT_ALLOC(
+ bot_buf_mask,
+ buf_Mask
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ max,
+ min
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ old_producer_pointer,
+ hfg_scb_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ delta,
+ adjust_count
+ )
+
+ u32 unused2[5];
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ sibling_ptr,
+ child_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ code_ptr,
+ this_ptr
+ )
+
+ u32 strm_rs_config;
+
+ u32 strm_buf_ptr;
+
+ u32 unused_phi_incr;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ right_targ,
+ left_targ
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ right_vol,
+ left_vol
+ )
+};
+
+
+struct dsp_asynch_fg_tx_scb {
+ ___DSP_DUAL_16BIT_ALLOC(
+ not_buf_mask,
+ buf_mask
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ max,
+ min
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ unused1,
+ hfg_scb_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ delta,
+ adjust_count
+ )
+
+ u32 accum_phi;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ unused2,
+ const_one_third
+ )
+
+ u32 unused3[3];
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ sibling_ptr,
+ child_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ codePtr,
+ this_ptr
+ )
+
+ u32 strm_rs_config;
+
+ u32 strm_buf_ptr;
+
+ u32 phi_incr;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ unused_right_targ,
+ unused_left_targ
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ unused_right_vol,
+ unused_left_vol
+ )
+};
+
+
+struct dsp_output_snoop_scb {
+ /* First 13 dwords from generic_scb_t */
+ struct dsp_basic_dma_req basic_req; /* Optional */
+ struct dsp_scatter_gather_ext sg_ext; /* Optional */
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb, /* REQUIRED */
+ sub_list_ptr /* REQUIRED */
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point, /* REQUIRED */
+ this_spb /* REQUIRED */
+ )
+
+ u32 strm_rs_config; /* REQUIRED */
+ u32 strm_buf_ptr; /* REQUIRED */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ init_snoop_input_link,
+ snoop_child_input_scb
+ )
+
+ u32 snoop_input_buf_ptr;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ reserved,
+ input_scb
+ )
+};
+
+struct dsp_spio_write_scb {
+ ___DSP_DUAL_16BIT_ALLOC(
+ address1,
+ address2
+ )
+
+ u32 data1;
+
+ u32 data2;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ address3,
+ address4
+ )
+
+ u32 data3;
+
+ u32 data4;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ unused1,
+ data_ptr
+ )
+
+ u32 unused2[2];
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ sibling_ptr,
+ child_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point,
+ this_ptr
+ )
+
+ u32 unused3[5];
+};
+
+struct dsp_magic_snoop_task {
+ u32 i0;
+ u32 i1;
+
+ u32 strm_buf_ptr1;
+
+ u16 i2;
+ u16 snoop_scb;
+
+ u32 i3;
+ u32 i4;
+ u32 i5;
+ u32 i6;
+
+ u32 i7;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb,
+ sub_list_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point,
+ this_ptr
+ )
+
+ u32 strm_buf_config;
+ u32 strm_buf_ptr2;
+
+ u32 i8;
+
+ struct dsp_volume_control vdec_vol_ctrl;
+};
+
+
+struct dsp_filter_scb {
+ ___DSP_DUAL_16BIT_ALLOC(
+ a0_right, /* 0x00 */
+ a0_left
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ a1_right, /* 0x01 */
+ a1_left
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ a2_right, /* 0x02 */
+ a2_left
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ output_buf_ptr, /* 0x03 */
+ init
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ filter_unused3, /* 0x04 */
+ filter_unused2
+ )
+
+ u32 prev_sample_output1; /* 0x05 */
+ u32 prev_sample_output2; /* 0x06 */
+ u32 prev_sample_input1; /* 0x07 */
+ u32 prev_sample_input2; /* 0x08 */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ next_scb_ptr, /* 0x09 */
+ sub_list_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ entry_point, /* 0x0A */
+ spb_ptr
+ )
+
+ u32 strm_rs_config; /* 0x0B */
+ u32 strm_buf_ptr; /* 0x0C */
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ b0_right, /* 0x0D */
+ b0_left
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ b1_right, /* 0x0E */
+ b1_left
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ b2_right, /* 0x0F */
+ b2_left
+ )
+};
+#endif /* __DSP_SCB_TYPES_H__ */
diff --git a/sound/pci/cs46xx/cs46xx_dsp_spos.h b/sound/pci/cs46xx/cs46xx_dsp_spos.h
new file mode 100644
index 000000000000..2fa9c7d6acc3
--- /dev/null
+++ b/sound/pci/cs46xx/cs46xx_dsp_spos.h
@@ -0,0 +1,213 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ */
+
+#ifndef __CS46XX_DSP_SPOS_H__
+#define __CS46XX_DSP_SPOS_H__
+
+#include "cs46xx_dsp_scb_types.h"
+#include "cs46xx_dsp_task_types.h"
+
+#define SYMBOL_CONSTANT 0x0
+#define SYMBOL_SAMPLE 0x1
+#define SYMBOL_PARAMETER 0x2
+#define SYMBOL_CODE 0x3
+
+#define SEGTYPE_SP_PROGRAM 0x00000001
+#define SEGTYPE_SP_PARAMETER 0x00000002
+#define SEGTYPE_SP_SAMPLE 0x00000003
+#define SEGTYPE_SP_COEFFICIENT 0x00000004
+
+#define DSP_SPOS_UU 0x0deadul /* unused */
+#define DSP_SPOS_DC 0x0badul /* don't care */
+#define DSP_SPOS_DC_DC 0x0bad0badul /* don't care */
+#define DSP_SPOS_UUUU 0xdeadc0edul /* unused */
+#define DSP_SPOS_UUHI 0xdeadul
+#define DSP_SPOS_UULO 0xc0edul
+#define DSP_SPOS_DCDC 0x0badf1d0ul /* don't care */
+#define DSP_SPOS_DCDCHI 0x0badul
+#define DSP_SPOS_DCDCLO 0xf1d0ul
+
+#define DSP_MAX_TASK_NAME 60
+#define DSP_MAX_SYMBOL_NAME 100
+#define DSP_MAX_SCB_NAME 60
+#define DSP_MAX_SCB_DESC 200
+#define DSP_MAX_TASK_DESC 50
+
+#define DSP_MAX_PCM_CHANNELS 32
+#define DSP_MAX_SRC_NR 14
+
+#define DSP_PCM_MAIN_CHANNEL 1
+#define DSP_PCM_REAR_CHANNEL 2
+#define DSP_PCM_CENTER_LFE_CHANNEL 3
+#define DSP_PCM_S71_CHANNEL 4 /* surround 7.1 */
+#define DSP_IEC958_CHANNEL 5
+
+#define DSP_SPDIF_STATUS_OUTPUT_ENABLED 1
+#define DSP_SPDIF_STATUS_PLAYBACK_OPEN 2
+#define DSP_SPDIF_STATUS_HW_ENABLED 4
+#define DSP_SPDIF_STATUS_INPUT_CTRL_ENABLED 8
+
+struct dsp_symbol_entry {
+ u32 address;
+ char symbol_name[DSP_MAX_SYMBOL_NAME];
+ int symbol_type;
+
+ /* initialized by driver */
+ struct dsp_module_desc * module;
+ int deleted;
+};
+
+struct dsp_symbol_desc {
+ int nsymbols;
+
+ struct dsp_symbol_entry *symbols;
+
+ /* initialized by driver */
+ int highest_frag_index;
+};
+
+struct dsp_segment_desc {
+ int segment_type;
+ u32 offset;
+ u32 size;
+ u32 * data;
+};
+
+struct dsp_module_desc {
+ char * module_name;
+ struct dsp_symbol_desc symbol_table;
+ int nsegments;
+ struct dsp_segment_desc * segments;
+
+ /* initialized by driver */
+ u32 overlay_begin_address;
+ u32 load_address;
+ int nfixups;
+};
+
+struct dsp_scb_descriptor {
+ char scb_name[DSP_MAX_SCB_NAME];
+ u32 address;
+ int index;
+ u32 *data;
+
+ struct dsp_scb_descriptor * sub_list_ptr;
+ struct dsp_scb_descriptor * next_scb_ptr;
+ struct dsp_scb_descriptor * parent_scb_ptr;
+
+ struct dsp_symbol_entry * task_entry;
+ struct dsp_symbol_entry * scb_symbol;
+
+ struct snd_info_entry *proc_info;
+ int ref_count;
+
+ u16 volume[2];
+ unsigned int deleted :1;
+ unsigned int updated :1;
+ unsigned int volume_set :1;
+};
+
+struct dsp_task_descriptor {
+ char task_name[DSP_MAX_TASK_NAME];
+ int size;
+ u32 address;
+ int index;
+ u32 *data;
+};
+
+struct dsp_pcm_channel_descriptor {
+ int active;
+ int src_slot;
+ int pcm_slot;
+ u32 sample_rate;
+ u32 unlinked;
+ struct dsp_scb_descriptor * pcm_reader_scb;
+ struct dsp_scb_descriptor * src_scb;
+ struct dsp_scb_descriptor * mixer_scb;
+
+ void * private_data;
+};
+
+struct dsp_spos_instance {
+ struct dsp_symbol_desc symbol_table; /* currently available loaded symbols in SP */
+
+ int nmodules;
+ struct dsp_module_desc * modules; /* modules loaded into SP */
+
+ struct dsp_segment_desc code;
+
+ /* Main PCM playback mixer */
+ struct dsp_scb_descriptor * master_mix_scb;
+ u16 dac_volume_right;
+ u16 dac_volume_left;
+
+ /* Rear/surround PCM playback mixer */
+ struct dsp_scb_descriptor * rear_mix_scb;
+
+ /* Center/LFE mixer */
+ struct dsp_scb_descriptor * center_lfe_mix_scb;
+
+ int npcm_channels;
+ int nsrc_scb;
+ struct dsp_pcm_channel_descriptor pcm_channels[DSP_MAX_PCM_CHANNELS];
+ int src_scb_slots[DSP_MAX_SRC_NR];
+
+ /* cache this symbols */
+ struct dsp_symbol_entry * null_algorithm; /* used by PCMreaderSCB's */
+ struct dsp_symbol_entry * s16_up; /* used by SRCtaskSCB's */
+
+ /* proc fs */
+ struct snd_card *snd_card;
+ struct snd_info_entry * proc_dsp_dir;
+
+ /* SCB's descriptors */
+ int nscb;
+ int scb_highest_frag_index;
+ struct dsp_scb_descriptor scbs[DSP_MAX_SCB_DESC];
+ struct dsp_scb_descriptor * the_null_scb;
+
+ /* Task's descriptors */
+ int ntask;
+ struct dsp_task_descriptor tasks[DSP_MAX_TASK_DESC];
+
+ /* SPDIF status */
+ int spdif_status_out;
+ int spdif_status_in;
+ u16 spdif_input_volume_right;
+ u16 spdif_input_volume_left;
+ /* spdif channel status,
+ left right and user validity bits */
+ unsigned int spdif_csuv_default;
+ unsigned int spdif_csuv_stream;
+
+ /* SPDIF input sample rate converter */
+ struct dsp_scb_descriptor * spdif_in_src;
+ /* SPDIF input asynch. receiver */
+ struct dsp_scb_descriptor * asynch_rx_scb;
+
+ /* Capture record mixer SCB */
+ struct dsp_scb_descriptor * record_mixer_scb;
+
+ /* CODEC input SCB */
+ struct dsp_scb_descriptor * codec_in_scb;
+
+ /* reference snooper */
+ struct dsp_scb_descriptor * ref_snoop_scb;
+
+ /* SPDIF output PCM reference */
+ struct dsp_scb_descriptor * spdif_pcm_input_scb;
+
+ /* asynch TX task */
+ struct dsp_scb_descriptor * asynch_tx_scb;
+
+ /* record sources */
+ struct dsp_scb_descriptor * pcm_input;
+ struct dsp_scb_descriptor * adc_input;
+
+ int spdif_in_sample_rate;
+};
+
+#endif /* __DSP_SPOS_H__ */
diff --git a/sound/pci/cs46xx/cs46xx_dsp_task_types.h b/sound/pci/cs46xx/cs46xx_dsp_task_types.h
new file mode 100644
index 000000000000..fcbd31e40c5a
--- /dev/null
+++ b/sound/pci/cs46xx/cs46xx_dsp_task_types.h
@@ -0,0 +1,237 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *
+ * NOTE: comments are copy/paste from cwcemb80.lst
+ * provided by Tom Woller at Cirrus (my only
+ * documentation about the SP OS running inside
+ * the DSP)
+ */
+
+#ifndef __CS46XX_DSP_TASK_TYPES_H__
+#define __CS46XX_DSP_TASK_TYPES_H__
+
+#include "cs46xx_dsp_scb_types.h"
+
+/*********************************************************************************************
+Example hierarchy of stream control blocks in the SP
+
+hfgTree
+Ptr____Call (c)
+ \
+ -------+------ ------------- ------------- ------------- -----
+| SBlaster IF |______\| Foreground |___\| Middlegr'nd |___\| Background |___\| Nul |
+| |Goto /| tree header |g /| tree header |g /| tree header |g /| SCB |r
+ -------------- (g) ------------- ------------- ------------- -----
+ |c |c |c |c
+ | | | |
+ \/ ------------- ------------- -------------
+ | Foreground |_\ | Middlegr'nd |_\ | Background |_\
+ | tree |g/ | tree |g/ | tree |g/
+ ------------- ------------- -------------
+ |c |c |c
+ | | |
+ \/ \/ \/
+
+*********************************************************************************************/
+
+#define HFG_FIRST_EXECUTE_MODE 0x0001
+#define HFG_FIRST_EXECUTE_MODE_BIT 0
+#define HFG_CONTEXT_SWITCH_MODE 0x0002
+#define HFG_CONTEXT_SWITCH_MODE_BIT 1
+
+#define MAX_FG_STACK_SIZE 32 /* THESE NEED TO BE COMPUTED PROPERLY */
+#define MAX_MG_STACK_SIZE 16
+#define MAX_BG_STACK_SIZE 9
+#define MAX_HFG_STACK_SIZE 4
+
+#define SLEEP_ACTIVE_INCREMENT 0 /* Enable task tree thread to go to sleep
+ This should only ever be used on the Background thread */
+#define STANDARD_ACTIVE_INCREMENT 1 /* Task tree thread normal operation */
+#define SUSPEND_ACTIVE_INCREMENT 2 /* Cause execution to suspend in the task tree thread
+ This should only ever be used on the Background thread */
+
+#define HOSTFLAGS_DISABLE_BG_SLEEP 0 /* Host-controlled flag that determines whether we go to sleep
+ at the end of BG */
+
+/* Minimal context save area for Hyper Forground */
+struct dsp_hf_save_area {
+ u32 r10_save;
+ u32 r54_save;
+ u32 r98_save;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ status_save,
+ ind_save
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ rci1_save,
+ rci0_save
+ )
+
+ u32 r32_save;
+ u32 r76_save;
+ u32 rsd2_save;
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ rsi2_save, /* See TaskTreeParameterBlock for
+ remainder of registers */
+ rsa2Save
+ )
+ /* saved as part of HFG context */
+};
+
+
+/* Task link data structure */
+struct dsp_tree_link {
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* Pointer to sibling task control block */
+ next_scb,
+ /* Pointer to child task control block */
+ sub_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* Pointer to code entry point */
+ entry_point,
+ /* Pointer to local data */
+ this_spb
+ )
+};
+
+
+struct dsp_task_tree_data {
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* Initial tock count; controls task tree execution rate */
+ tock_count_limit,
+ /* Tock down counter */
+ tock_count
+ )
+
+ /* Add to ActiveCount when TockCountLimit reached:
+ Subtract on task tree termination */
+ ___DSP_DUAL_16BIT_ALLOC(
+ active_tncrement,
+ /* Number of pending activations for task tree */
+ active_count
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* BitNumber to enable modification of correct bit in ActiveTaskFlags */
+ active_bit,
+ /* Pointer to OS location for indicating current activity on task level */
+ active_task_flags_ptr
+ )
+
+ /* Data structure for controlling movement of memory blocks:-
+ currently unused */
+ ___DSP_DUAL_16BIT_ALLOC(
+ mem_upd_ptr,
+ /* Data structure for controlling synchronous link update */
+ link_upd_ptr
+ )
+
+ ___DSP_DUAL_16BIT_ALLOC(
+ /* Save area for remainder of full context. */
+ save_area,
+ /* Address of start of local stack for data storage */
+ data_stack_base_ptr
+ )
+
+};
+
+
+struct dsp_interval_timer_data
+{
+ /* These data items have the same relative locations to those */
+ ___DSP_DUAL_16BIT_ALLOC(
+ interval_timer_period,
+ itd_unused
+ )
+
+ /* used for this data in the SPOS control block for SPOS 1.0 */
+ ___DSP_DUAL_16BIT_ALLOC(
+ num_FG_ticks_this_interval,
+ num_intervals
+ )
+};
+
+
+/* This structure contains extra storage for the task tree
+ Currently, this additional data is related only to a full context save */
+struct dsp_task_tree_context_block {
+ /* Up to 10 values are saved onto the stack. 8 for the task tree, 1 for
+ The access to the context switch (call or interrupt), and 1 spare that
+ users should never use. This last may be required by the system */
+ ___DSP_DUAL_16BIT_ALLOC(
+ stack1,
+ stack0
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ stack3,
+ stack2
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ stack5,
+ stack4
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ stack7,
+ stack6
+ )
+ ___DSP_DUAL_16BIT_ALLOC(
+ stack9,
+ stack8
+ )
+
+ u32 saverfe;
+
+ /* Value may be overwritten by stack save algorithm.
+ Retain the size of the stack data saved here if used */
+ ___DSP_DUAL_16BIT_ALLOC(
+ reserved1,
+ stack_size
+ )
+ u32 saverba; /* (HFG) */
+ u32 saverdc;
+ u32 savers_config_23; /* (HFG) */
+ u32 savers_DMA23; /* (HFG) */
+ u32 saversa0;
+ u32 saversi0;
+ u32 saversa1;
+ u32 saversi1;
+ u32 saversa3;
+ u32 saversd0;
+ u32 saversd1;
+ u32 saversd3;
+ u32 savers_config01;
+ u32 savers_DMA01;
+ u32 saveacc0hl;
+ u32 saveacc1hl;
+ u32 saveacc0xacc1x;
+ u32 saveacc2hl;
+ u32 saveacc3hl;
+ u32 saveacc2xacc3x;
+ u32 saveaux0hl;
+ u32 saveaux1hl;
+ u32 saveaux0xaux1x;
+ u32 saveaux2hl;
+ u32 saveaux3hl;
+ u32 saveaux2xaux3x;
+ u32 savershouthl;
+ u32 savershoutxmacmode;
+};
+
+
+struct dsp_task_tree_control_block {
+ struct dsp_hf_save_area context;
+ struct dsp_tree_link links;
+ struct dsp_task_tree_data data;
+ struct dsp_task_tree_context_block context_blk;
+ struct dsp_interval_timer_data int_timer;
+};
+
+
+#endif /* __DSP_TASK_TYPES_H__ */
diff --git a/sound/pci/cs46xx/cs46xx_image.h b/sound/pci/cs46xx/cs46xx_image.h
deleted file mode 100644
index dc93f62db2c2..000000000000
--- a/sound/pci/cs46xx/cs46xx_image.h
+++ /dev/null
@@ -1,3468 +0,0 @@
-struct BA1struct {
- struct {
- unsigned long offset;
- unsigned long size;
- } memory[BA1_MEMORY_COUNT];
- u32 map[BA1_DWORD_SIZE];
-};
-
-
-static struct BA1struct BA1Struct = {
-{{ 0x00000000, 0x00003000 },{ 0x00010000, 0x00003800 },{ 0x00020000, 0x00007000 }},
-{0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000163,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00200040,0x00008010,0x00000000,
-0x00000000,0x80000001,0x00000001,0x00060000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00900080,0x00000173,0x00000000,
-0x00000000,0x00000010,0x00800000,0x00900000,
-0xf2c0000f,0x00000200,0x00000000,0x00010600,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000163,0x330300c2,
-0x06000000,0x00000000,0x80008000,0x80008000,
-0x3fc0000f,0x00000301,0x00010400,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00b00000,0x00d0806d,0x330480c3,
-0x04800000,0x00000001,0x00800001,0x0000ffff,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x066a0600,0x06350070,0x0000929d,0x929d929d,
-0x00000000,0x0000735a,0x00000600,0x00000000,
-0x929d735a,0x8734abfe,0x00010000,0x735a735a,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x0000804f,0x000000c3,
-0x05000000,0x00a00010,0x00000000,0x80008000,
-0x00000000,0x00000000,0x00000700,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000080,0x00a00000,0x0000809a,0x000000c2,
-0x07400000,0x00000000,0x80008000,0xffffffff,
-0x00c80028,0x00005555,0x00000000,0x000107a0,
-0x00c80028,0x000000c2,0x06800000,0x00000000,
-0x06e00080,0x00300000,0x000080bb,0x000000c9,
-0x07a00000,0x04000000,0x80008000,0xffffffff,
-0x00c80028,0x00005555,0x00000000,0x00000780,
-0x00c80028,0x000000c5,0xff800000,0x00000000,
-0x00640080,0x00c00000,0x00008197,0x000000c9,
-0x07800000,0x04000000,0x80008000,0xffffffff,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x0000805e,0x000000c1,
-0x00000000,0x00800000,0x80008000,0x80008000,
-0x00020000,0x0000ffff,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x929d0600,0x929d929d,0x929d929d,0x929d0000,
-0x929d929d,0x929d929d,0x929d929d,0x929d929d,
-0x929d929d,0x00100635,0x060b013f,0x00000004,
-0x00000001,0x007a0002,0x00000000,0x066e0610,
-0x0105929d,0x929d929d,0x929d929d,0x929d929d,
-0x929d929d,0xa431ac75,0x0001735a,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0x735a0051,
-0x00000000,0x929d929d,0x929d929d,0x929d929d,
-0x929d929d,0x929d929d,0x929d929d,0x929d929d,
-0x929d929d,0x929d929d,0x00000000,0x06400136,
-0x0000270f,0x00010000,0x007a0000,0x00000000,
-0x068e0645,0x0105929d,0x929d929d,0x929d929d,
-0x929d929d,0x929d929d,0xa431ac75,0x0001735a,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0xa431ac75,0xa431ac75,0xa431ac75,0xa431ac75,
-0x735a0100,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00010004,
-0x00040730,0x00001002,0x000f619e,0x00001003,
-0x00001705,0x00001400,0x000a411e,0x00001003,
-0x00040730,0x00001002,0x000f619e,0x00001003,
-0x00009705,0x00001400,0x000a411e,0x00001003,
-0x00040730,0x00001002,0x000f619e,0x00001003,
-0x00011705,0x00001400,0x000a411e,0x00001003,
-0x00040730,0x00001002,0x000f619e,0x00001003,
-0x00019705,0x00001400,0x000a411e,0x00001003,
-0x00040730,0x00001002,0x000f619e,0x00001003,
-0x00021705,0x00001400,0x000a411e,0x00001003,
-0x00040730,0x00001002,0x000f619e,0x00001003,
-0x00029705,0x00001400,0x000a411e,0x00001003,
-0x00040730,0x00001002,0x000f619e,0x00001003,
-0x00031705,0x00001400,0x000a411e,0x00001003,
-0x00040730,0x00001002,0x000f619e,0x00001003,
-0x00039705,0x00001400,0x000a411e,0x00001003,
-0x000fe19e,0x00001003,0x0009c730,0x00001003,
-0x0008e19c,0x00001003,0x000083c1,0x00093040,
-0x00098730,0x00001002,0x000ee19e,0x00001003,
-0x00009705,0x00001400,0x000a211e,0x00001003,
-0x00098730,0x00001002,0x000ee19e,0x00001003,
-0x00011705,0x00001400,0x000a211e,0x00001003,
-0x00098730,0x00001002,0x000ee19e,0x00001003,
-0x00019705,0x00001400,0x000a211e,0x00001003,
-0x00098730,0x00001002,0x000ee19e,0x00001003,
-0x00021705,0x00001400,0x000a211e,0x00001003,
-0x00098730,0x00001002,0x000ee19e,0x00001003,
-0x00029705,0x00001400,0x000a211e,0x00001003,
-0x00098730,0x00001002,0x000ee19e,0x00001003,
-0x00031705,0x00001400,0x000a211e,0x00001003,
-0x00098730,0x00001002,0x000ee19e,0x00001003,
-0x00039705,0x00001400,0x000a211e,0x00001003,
-0x0000a730,0x00001008,0x000e2730,0x00001002,
-0x0000a731,0x00001002,0x0000a731,0x00001002,
-0x0000a731,0x00001002,0x0000a731,0x00001002,
-0x0000a731,0x00001002,0x0000a731,0x00001002,
-0x00000000,0x00000000,0x000f619c,0x00001003,
-0x0007f801,0x000c0000,0x00000037,0x00001000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x000c0000,0x00000000,0x00000000,
-0x0000373c,0x00001000,0x00000000,0x00000000,
-0x000ee19c,0x00001003,0x0007f801,0x000c0000,
-0x00000037,0x00001000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x0000273c,0x00001000,
-0x00000033,0x00001000,0x000e679e,0x00001003,
-0x00007705,0x00001400,0x000ac71e,0x00001003,
-0x00087fc1,0x000c3be0,0x0007f801,0x000c0000,
-0x00000037,0x00001000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x0000a730,0x00001003,
-0x00000033,0x00001000,0x0007f801,0x000c0000,
-0x00000037,0x00001000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x000c0000,
-0x00000032,0x00001000,0x0000273d,0x00001000,
-0x0004a730,0x00001003,0x00000f41,0x00097140,
-0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
-0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
-0x00000000,0x00000000,0x0001bf05,0x0003fc40,
-0x00002725,0x000aa400,0x00013705,0x00093a00,
-0x0000002e,0x0009d6c0,0x00038630,0x00001004,
-0x0004ef0a,0x000eb785,0x0003fc8a,0x00000000,
-0x00000000,0x000c70e0,0x0007d182,0x0002c640,
-0x00000630,0x00001004,0x000799b8,0x0002c6c0,
-0x00031705,0x00092240,0x00039f05,0x000932c0,
-0x0003520a,0x00000000,0x00040731,0x0000100b,
-0x00010705,0x000b20c0,0x00000000,0x000eba44,
-0x00032108,0x000c60c4,0x00065208,0x000c2917,
-0x000406b0,0x00001007,0x00012f05,0x00036880,
-0x0002818e,0x000c0000,0x0004410a,0x00000000,
-0x00040630,0x00001007,0x00029705,0x000c0000,
-0x00000000,0x00000000,0x00003fc1,0x0003fc40,
-0x000037c1,0x00091b40,0x00003fc1,0x000911c0,
-0x000037c1,0x000957c0,0x00003fc1,0x000951c0,
-0x000037c1,0x00000000,0x00003fc1,0x000991c0,
-0x000037c1,0x00000000,0x00003fc1,0x0009d1c0,
-0x000037c1,0x00000000,0x0001ccc1,0x000915c0,
-0x0001c441,0x0009d800,0x0009cdc1,0x00091240,
-0x0001c541,0x00091d00,0x0009cfc1,0x00095240,
-0x0001c741,0x00095c80,0x000e8ca9,0x00099240,
-0x000e85ad,0x00095640,0x00069ca9,0x00099d80,
-0x000e952d,0x00099640,0x000eaca9,0x0009d6c0,
-0x000ea5ad,0x00091a40,0x0006bca9,0x0009de80,
-0x000eb52d,0x00095a40,0x000ecca9,0x00099ac0,
-0x000ec5ad,0x0009da40,0x000edca9,0x0009d300,
-0x000a6e0a,0x00001000,0x000ed52d,0x00091e40,
-0x000eeca9,0x00095ec0,0x000ee5ad,0x00099e40,
-0x0006fca9,0x00002500,0x000fb208,0x000c59a0,
-0x000ef52d,0x0009de40,0x00068ca9,0x000912c1,
-0x000683ad,0x00095241,0x00020f05,0x000991c1,
-0x00000000,0x00000000,0x00086f88,0x00001000,
-0x0009cf81,0x000b5340,0x0009c701,0x000b92c0,
-0x0009de81,0x000bd300,0x0009d601,0x000b1700,
-0x0001fd81,0x000b9d80,0x0009f501,0x000b57c0,
-0x000a0f81,0x000bd740,0x00020701,0x000b5c80,
-0x000a1681,0x000b97c0,0x00021601,0x00002500,
-0x000a0701,0x000b9b40,0x000a0f81,0x000b1bc0,
-0x00021681,0x00002d00,0x00020f81,0x000bd800,
-0x000a0701,0x000b5bc0,0x00021601,0x00003500,
-0x000a0f81,0x000b5f40,0x000a0701,0x000bdbc0,
-0x00021681,0x00003d00,0x00020f81,0x000b1d00,
-0x000a0701,0x000b1fc0,0x00021601,0x00020500,
-0x00020f81,0x000b1341,0x000a0701,0x000b9fc0,
-0x00021681,0x00020d00,0x00020f81,0x000bde80,
-0x000a0701,0x000bdfc0,0x00021601,0x00021500,
-0x00020f81,0x000b9341,0x00020701,0x000b53c1,
-0x00021681,0x00021d00,0x000a0f81,0x000d0380,
-0x0000b601,0x000b15c0,0x00007b01,0x00000000,
-0x00007b81,0x000bd1c0,0x00007b01,0x00000000,
-0x00007b81,0x000b91c0,0x00007b01,0x000b57c0,
-0x00007b81,0x000b51c0,0x00007b01,0x000b1b40,
-0x00007b81,0x000b11c0,0x00087b01,0x000c3dc0,
-0x0007e488,0x000d7e45,0x00000000,0x000d7a44,
-0x0007e48a,0x00000000,0x00011f05,0x00084080,
-0x00000000,0x00000000,0x00001705,0x000b3540,
-0x00008a01,0x000bf040,0x00007081,0x000bb5c0,
-0x00055488,0x00000000,0x0000d482,0x0003fc40,
-0x0003fc88,0x00000000,0x0001e401,0x000b3a00,
-0x0001ec81,0x000bd6c0,0x0004ef08,0x000eb784,
-0x000c86b0,0x00001007,0x00008281,0x000bb240,
-0x0000b801,0x000b7140,0x00007888,0x00000000,
-0x0000073c,0x00001000,0x0007f188,0x000c0000,
-0x00000000,0x00000000,0x00055288,0x000c555c,
-0x0005528a,0x000c0000,0x0009fa88,0x000c5d00,
-0x0000fa88,0x00000000,0x00000032,0x00001000,
-0x0000073d,0x00001000,0x0007f188,0x000c0000,
-0x00000000,0x00000000,0x0008c01c,0x00001003,
-0x00002705,0x00001008,0x0008b201,0x000c1392,
-0x0000ba01,0x00000000,0x00008731,0x00001400,
-0x0004c108,0x000fe0c4,0x00057488,0x00000000,
-0x000a6388,0x00001001,0x0008b334,0x000bc141,
-0x0003020e,0x00000000,0x000886b0,0x00001008,
-0x00003625,0x000c5dfa,0x000a638a,0x00001001,
-0x0008020e,0x00001002,0x0008a6b0,0x00001008,
-0x0007f301,0x00000000,0x00000000,0x00000000,
-0x00002725,0x000a8c40,0x000000ae,0x00000000,
-0x000d8630,0x00001008,0x00000000,0x000c74e0,
-0x0007d182,0x0002d640,0x000a8630,0x00001008,
-0x000799b8,0x0002d6c0,0x0000748a,0x000c3ec5,
-0x0007420a,0x000c0000,0x00062208,0x000c4117,
-0x00070630,0x00001009,0x00000000,0x000c0000,
-0x0001022e,0x00000000,0x0003a630,0x00001009,
-0x00000000,0x000c0000,0x00000036,0x00001000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x0002a730,0x00001008,0x0007f801,0x000c0000,
-0x00000037,0x00001000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x0002a730,0x00001008,
-0x00000033,0x00001000,0x0002a705,0x00001008,
-0x00007a01,0x000c0000,0x000e6288,0x000d550a,
-0x0006428a,0x00000000,0x00060730,0x0000100a,
-0x00000000,0x000c0000,0x00000000,0x00000000,
-0x0007aab0,0x00034880,0x00078fb0,0x0000100b,
-0x00057488,0x00000000,0x00033b94,0x00081140,
-0x000183ae,0x00000000,0x000786b0,0x0000100b,
-0x00022f05,0x000c3545,0x0000eb8a,0x00000000,
-0x00042731,0x00001003,0x0007aab0,0x00034880,
-0x00048fb0,0x0000100a,0x00057488,0x00000000,
-0x00033b94,0x00081140,0x000183ae,0x00000000,
-0x000806b0,0x0000100b,0x00022f05,0x00000000,
-0x00007401,0x00091140,0x00048f05,0x000951c0,
-0x00042731,0x00001003,0x0000473d,0x00001000,
-0x000f19b0,0x000bbc47,0x00080000,0x000bffc7,
-0x000fe19e,0x00001003,0x00000000,0x00000000,
-0x0008e19c,0x00001003,0x000083c1,0x00093040,
-0x00000f41,0x00097140,0x0000a841,0x0009b240,
-0x0000a0c1,0x0009f040,0x0001c641,0x00093540,
-0x0001cec1,0x0009b5c0,0x00000000,0x000fdc44,
-0x00055208,0x00000000,0x00010705,0x000a2880,
-0x0000a23a,0x00093a00,0x0003fc8a,0x000df6c5,
-0x0004ef0a,0x000c0000,0x00012f05,0x00036880,
-0x00065308,0x000c2997,0x000d86b0,0x0000100a,
-0x0004410a,0x000d40c7,0x00000000,0x00000000,
-0x00080730,0x00001004,0x00056f0a,0x000ea105,
-0x00000000,0x00000000,0x0000473d,0x00001000,
-0x000f19b0,0x000bbc47,0x00080000,0x000bffc7,
-0x0000273d,0x00001000,0x00000000,0x000eba44,
-0x00048f05,0x0000f440,0x00007401,0x0000f7c0,
-0x00000734,0x00001000,0x00010705,0x000a6880,
-0x00006a88,0x000c75c4,0x00000000,0x000e5084,
-0x00000000,0x000eba44,0x00087401,0x000e4782,
-0x00000734,0x00001000,0x00010705,0x000a6880,
-0x00006a88,0x000c75c4,0x0007c108,0x000c0000,
-0x0007e721,0x000bed40,0x00005f25,0x000badc0,
-0x0003ba97,0x000beb80,0x00065590,0x000b2e00,
-0x00033217,0x00003ec0,0x00065590,0x000b8e40,
-0x0003ed80,0x000491c0,0x00073fb0,0x00074c80,
-0x000283a0,0x0000100c,0x000ee388,0x00042970,
-0x00008301,0x00021ef2,0x000b8f14,0x0000000f,
-0x000c4d8d,0x0000001b,0x000d6dc2,0x000e06c6,
-0x000032ac,0x000c3916,0x0004edc2,0x00074c80,
-0x00078898,0x00001000,0x00038894,0x00000032,
-0x000c4d8d,0x00092e1b,0x000d6dc2,0x000e06c6,
-0x0004edc2,0x000c1956,0x0000722c,0x00034a00,
-0x00041705,0x0009ed40,0x00058730,0x00001400,
-0x000d7488,0x000c3a00,0x00048f05,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000,
-0x00000000,0x00000000,0x00000000,0x00000000}
- };
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index aad37082cb6e..b96ab7fd464c 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Abramo Bagnara <abramo@alsa-project.org>
@@ -28,21 +29,6 @@
* references to be able to implement all fancy feutures
* supported by the cs46xx DSP's.
* Benny <benny@hostmobility.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
@@ -53,16 +39,18 @@
#include <linux/slab.h>
#include <linux/gameport.h>
#include <linux/mutex.h>
-
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/vmalloc.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/info.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
-#include <sound/cs46xx.h>
-
-#include <asm/io.h>
+#include "cs46xx.h"
#include "cs46xx_lib.h"
#include "dsp_spos.h"
@@ -70,18 +58,18 @@
static void amp_voyetra(struct snd_cs46xx *chip, int change);
#ifdef CONFIG_SND_CS46XX_NEW_DSP
-static struct snd_pcm_ops snd_cs46xx_playback_rear_ops;
-static struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops;
-static struct snd_pcm_ops snd_cs46xx_playback_clfe_ops;
-static struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops;
-static struct snd_pcm_ops snd_cs46xx_playback_iec958_ops;
-static struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops;
+static const struct snd_pcm_ops snd_cs46xx_playback_rear_ops;
+static const struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops;
+static const struct snd_pcm_ops snd_cs46xx_playback_clfe_ops;
+static const struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops;
+static const struct snd_pcm_ops snd_cs46xx_playback_iec958_ops;
+static const struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops;
#endif
-static struct snd_pcm_ops snd_cs46xx_playback_ops;
-static struct snd_pcm_ops snd_cs46xx_playback_indirect_ops;
-static struct snd_pcm_ops snd_cs46xx_capture_ops;
-static struct snd_pcm_ops snd_cs46xx_capture_indirect_ops;
+static const struct snd_pcm_ops snd_cs46xx_playback_ops;
+static const struct snd_pcm_ops snd_cs46xx_playback_indirect_ops;
+static const struct snd_pcm_ops snd_cs46xx_capture_ops;
+static const struct snd_pcm_ops snd_cs46xx_capture_indirect_ops;
static unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip,
unsigned short reg,
@@ -93,7 +81,7 @@ static unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip,
if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX &&
codec_index != CS46XX_SECONDARY_CODEC_INDEX))
- return -EINVAL;
+ return 0xffff;
chip->active_ctrl(chip, 1);
@@ -113,7 +101,7 @@ static unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip,
tmp = snd_cs46xx_peekBA0(chip, BA0_ACCTL);
if ((tmp & ACCTL_VFRM) == 0) {
- snd_printk(KERN_WARNING "cs46xx: ACCTL_VFRM not set 0x%x\n",tmp);
+ dev_warn(chip->card->dev, "ACCTL_VFRM not set 0x%x\n", tmp);
snd_cs46xx_pokeBA0(chip, BA0_ACCTL, (tmp & (~ACCTL_ESYN)) | ACCTL_VFRM );
msleep(50);
tmp = snd_cs46xx_peekBA0(chip, BA0_ACCTL + offset);
@@ -165,7 +153,8 @@ static unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip,
goto ok1;
}
- snd_printk(KERN_ERR "AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg);
+ dev_err(chip->card->dev,
+ "AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg);
result = 0xffff;
goto end;
@@ -184,7 +173,9 @@ static unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip,
udelay(10);
}
- snd_printk(KERN_ERR "AC'97 read problem (ACSTS_VSTS), codec_index %d, reg = 0x%x\n", codec_index, reg);
+ dev_err(chip->card->dev,
+ "AC'97 read problem (ACSTS_VSTS), codec_index %d, reg = 0x%x\n",
+ codec_index, reg);
result = 0xffff;
goto end;
@@ -194,7 +185,8 @@ static unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip,
* ACSDA = Status Data Register = 474h
*/
#if 0
- printk(KERN_DEBUG "e) reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", reg,
+ dev_dbg(chip->card->dev,
+ "e) reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", reg,
snd_cs46xx_peekBA0(chip, BA0_ACSDA),
snd_cs46xx_peekBA0(chip, BA0_ACCAD));
#endif
@@ -283,7 +275,9 @@ static void snd_cs46xx_codec_write(struct snd_cs46xx *chip,
goto end;
}
}
- snd_printk(KERN_ERR "AC'97 write problem, codec_index = %d, reg = 0x%x, val = 0x%x\n", codec_index, reg, val);
+ dev_err(chip->card->dev,
+ "AC'97 write problem, codec_index = %d, reg = 0x%x, val = 0x%x\n",
+ codec_index, reg, val);
end:
chip->active_ctrl(chip, -1);
}
@@ -329,13 +323,147 @@ int snd_cs46xx_download(struct snd_cs46xx *chip,
return 0;
}
+static inline void memcpy_le32(void *dst, const void *src, unsigned int len)
+{
+#ifdef __LITTLE_ENDIAN
+ memcpy(dst, src, len);
+#else
+ u32 *_dst = dst;
+ const __le32 *_src = src;
+ len /= 4;
+ while (len-- > 0)
+ *_dst++ = le32_to_cpu(*_src++);
+#endif
+}
+
#ifdef CONFIG_SND_CS46XX_NEW_DSP
-#include "imgs/cwc4630.h"
-#include "imgs/cwcasync.h"
-#include "imgs/cwcsnoop.h"
-#include "imgs/cwcbinhack.h"
-#include "imgs/cwcdma.h"
+static const char *module_names[CS46XX_DSP_MODULES] = {
+ "cwc4630", "cwcasync", "cwcsnoop", "cwcbinhack", "cwcdma"
+};
+
+MODULE_FIRMWARE("cs46xx/cwc4630");
+MODULE_FIRMWARE("cs46xx/cwcasync");
+MODULE_FIRMWARE("cs46xx/cwcsnoop");
+MODULE_FIRMWARE("cs46xx/cwcbinhack");
+MODULE_FIRMWARE("cs46xx/cwcdma");
+
+static void free_module_desc(struct dsp_module_desc *module)
+{
+ if (!module)
+ return;
+ kfree(module->module_name);
+ kfree(module->symbol_table.symbols);
+ if (module->segments) {
+ int i;
+ for (i = 0; i < module->nsegments; i++)
+ kfree(module->segments[i].data);
+ kfree(module->segments);
+ }
+ kfree(module);
+}
+
+/* firmware binary format:
+ * le32 nsymbols;
+ * struct {
+ * le32 address;
+ * char symbol_name[DSP_MAX_SYMBOL_NAME];
+ * le32 symbol_type;
+ * } symbols[nsymbols];
+ * le32 nsegments;
+ * struct {
+ * le32 segment_type;
+ * le32 offset;
+ * le32 size;
+ * le32 data[size];
+ * } segments[nsegments];
+ */
+
+static int load_firmware(struct snd_cs46xx *chip,
+ struct dsp_module_desc **module_ret,
+ const char *fw_name)
+{
+ int i, err;
+ unsigned int nums, fwlen, fwsize;
+ const __le32 *fwdat;
+ struct dsp_module_desc *module = NULL;
+ const struct firmware *fw;
+ char fw_path[32];
+
+ sprintf(fw_path, "cs46xx/%s", fw_name);
+ err = request_firmware(&fw, fw_path, &chip->pci->dev);
+ if (err < 0)
+ return err;
+ fwsize = fw->size / 4;
+ if (fwsize < 2) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ err = -ENOMEM;
+ module = kzalloc(sizeof(*module), GFP_KERNEL);
+ if (!module)
+ goto error;
+ module->module_name = kstrdup(fw_name, GFP_KERNEL);
+ if (!module->module_name)
+ goto error;
+
+ fwlen = 0;
+ fwdat = (const __le32 *)fw->data;
+ nums = module->symbol_table.nsymbols = le32_to_cpu(fwdat[fwlen++]);
+ if (nums >= 40)
+ goto error_inval;
+ module->symbol_table.symbols =
+ kcalloc(nums, sizeof(struct dsp_symbol_entry), GFP_KERNEL);
+ if (!module->symbol_table.symbols)
+ goto error;
+ for (i = 0; i < nums; i++) {
+ struct dsp_symbol_entry *entry =
+ &module->symbol_table.symbols[i];
+ if (fwlen + 2 + DSP_MAX_SYMBOL_NAME / 4 > fwsize)
+ goto error_inval;
+ entry->address = le32_to_cpu(fwdat[fwlen++]);
+ memcpy(entry->symbol_name, &fwdat[fwlen], DSP_MAX_SYMBOL_NAME - 1);
+ fwlen += DSP_MAX_SYMBOL_NAME / 4;
+ entry->symbol_type = le32_to_cpu(fwdat[fwlen++]);
+ }
+
+ if (fwlen >= fwsize)
+ goto error_inval;
+ nums = module->nsegments = le32_to_cpu(fwdat[fwlen++]);
+ if (nums > 10)
+ goto error_inval;
+ module->segments =
+ kcalloc(nums, sizeof(struct dsp_segment_desc), GFP_KERNEL);
+ if (!module->segments)
+ goto error;
+ for (i = 0; i < nums; i++) {
+ struct dsp_segment_desc *entry = &module->segments[i];
+ if (fwlen + 3 > fwsize)
+ goto error_inval;
+ entry->segment_type = le32_to_cpu(fwdat[fwlen++]);
+ entry->offset = le32_to_cpu(fwdat[fwlen++]);
+ entry->size = le32_to_cpu(fwdat[fwlen++]);
+ if (fwlen + entry->size > fwsize)
+ goto error_inval;
+ entry->data = kmalloc_array(entry->size, 4, GFP_KERNEL);
+ if (!entry->data)
+ goto error;
+ memcpy_le32(entry->data, &fwdat[fwlen], entry->size * 4);
+ fwlen += entry->size;
+ }
+
+ *module_ret = module;
+ release_firmware(fw);
+ return 0;
+
+ error_inval:
+ err = -EINVAL;
+ error:
+ free_module_desc(module);
+ release_firmware(fw);
+ return err;
+}
int snd_cs46xx_clear_BA1(struct snd_cs46xx *chip,
unsigned long offset,
@@ -360,20 +488,63 @@ int snd_cs46xx_clear_BA1(struct snd_cs46xx *chip,
#else /* old DSP image */
-#include "cs46xx_image.h"
+struct ba1_struct {
+ struct {
+ u32 offset;
+ u32 size;
+ } memory[BA1_MEMORY_COUNT];
+ u32 map[BA1_DWORD_SIZE];
+};
+
+MODULE_FIRMWARE("cs46xx/ba1");
+
+static int load_firmware(struct snd_cs46xx *chip)
+{
+ const struct firmware *fw;
+ int i, size, err;
+
+ err = request_firmware(&fw, "cs46xx/ba1", &chip->pci->dev);
+ if (err < 0)
+ return err;
+ if (fw->size != sizeof(*chip->ba1)) {
+ err = -EINVAL;
+ goto error;
+ }
+
+ chip->ba1 = vmalloc(sizeof(*chip->ba1));
+ if (!chip->ba1) {
+ err = -ENOMEM;
+ goto error;
+ }
+
+ memcpy_le32(chip->ba1, fw->data, sizeof(*chip->ba1));
+
+ /* sanity check */
+ size = 0;
+ for (i = 0; i < BA1_MEMORY_COUNT; i++)
+ size += chip->ba1->memory[i].size;
+ if (size > BA1_DWORD_SIZE * 4)
+ err = -EINVAL;
+
+ error:
+ release_firmware(fw);
+ return err;
+}
-int snd_cs46xx_download_image(struct snd_cs46xx *chip)
+static __maybe_unused int snd_cs46xx_download_image(struct snd_cs46xx *chip)
{
int idx, err;
- unsigned long offset = 0;
+ unsigned int offset = 0;
+ struct ba1_struct *ba1 = chip->ba1;
for (idx = 0; idx < BA1_MEMORY_COUNT; idx++) {
- if ((err = snd_cs46xx_download(chip,
- &BA1Struct.map[offset],
- BA1Struct.memory[idx].offset,
- BA1Struct.memory[idx].size)) < 0)
+ err = snd_cs46xx_download(chip,
+ &ba1->map[offset],
+ ba1->memory[idx].offset,
+ ba1->memory[idx].size);
+ if (err < 0)
return err;
- offset += BA1Struct.memory[idx].size >> 2;
+ offset += ba1->memory[idx].size >> 2;
}
return 0;
}
@@ -428,8 +599,8 @@ static int cs46xx_wait_for_fifo(struct snd_cs46xx * chip,int retry_timeout)
}
if(status & SERBST_WBSY) {
- snd_printk(KERN_ERR "cs46xx: failure waiting for "
- "FIFO command to complete\n");
+ dev_err(chip->card->dev,
+ "failure waiting for FIFO command to complete\n");
return -EINVAL;
}
@@ -466,7 +637,9 @@ static void snd_cs46xx_clear_serial_FIFOs(struct snd_cs46xx *chip)
* Make sure the previous FIFO write operation has completed.
*/
if (cs46xx_wait_for_fifo(chip,1)) {
- snd_printdd ("failed waiting for FIFO at addr (%02X)\n",idx);
+ dev_dbg(chip->card->dev,
+ "failed waiting for FIFO at addr (%02X)\n",
+ idx);
if (powerdown)
snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
@@ -514,7 +687,7 @@ static void snd_cs46xx_proc_start(struct snd_cs46xx *chip)
}
if (snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR)
- snd_printk(KERN_ERR "SPCR_RUNFR never reset\n");
+ dev_err(chip->card->dev, "SPCR_RUNFR never reset\n");
}
static void snd_cs46xx_proc_stop(struct snd_cs46xx *chip)
@@ -534,7 +707,6 @@ static void snd_cs46xx_proc_stop(struct snd_cs46xx *chip)
static void snd_cs46xx_set_play_sample_rate(struct snd_cs46xx *chip, unsigned int rate)
{
- unsigned long flags;
unsigned int tmp1, tmp2;
unsigned int phiIncr;
unsigned int correctionPerGOF, correctionPerSec;
@@ -571,16 +743,14 @@ static void snd_cs46xx_set_play_sample_rate(struct snd_cs46xx *chip, unsigned in
/*
* Fill in the SampleRateConverter control block.
*/
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_cs46xx_poke(chip, BA1_PSRC,
((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
snd_cs46xx_poke(chip, BA1_PPI, phiIncr);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static void snd_cs46xx_set_capture_sample_rate(struct snd_cs46xx *chip, unsigned int rate)
{
- unsigned long flags;
unsigned int phiIncr, coeffIncr, tmp1, tmp2;
unsigned int correctionPerGOF, correctionPerSec, initialDelay;
unsigned int frameGroupLength, cnt;
@@ -593,7 +763,7 @@ static void snd_cs46xx_set_capture_sample_rate(struct snd_cs46xx *chip, unsigned
rate = 48000 / 9;
/*
- * We can not capture at at rate greater than the Input Rate (48000).
+ * We can not capture at a rate greater than the Input Rate (48000).
* Return an error if an attempt is made to stray outside that limit.
*/
if (rate > 48000)
@@ -640,19 +810,19 @@ static void snd_cs46xx_set_capture_sample_rate(struct snd_cs46xx *chip, unsigned
correctionPerGOF = tmp1 / GOF_PER_SEC;
tmp1 -= correctionPerGOF * GOF_PER_SEC;
correctionPerSec = tmp1;
- initialDelay = ((48000 * 24) + rate - 1) / rate;
+ initialDelay = DIV_ROUND_UP(48000 * 24, rate);
/*
* Fill in the VariDecimate control block.
*/
- spin_lock_irqsave(&chip->reg_lock, flags);
- snd_cs46xx_poke(chip, BA1_CSRC,
- ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
- snd_cs46xx_poke(chip, BA1_CCI, coeffIncr);
- snd_cs46xx_poke(chip, BA1_CD,
- (((BA1_VARIDEC_BUF_1 + (initialDelay << 2)) << 16) & 0xFFFF0000) | 0x80);
- snd_cs46xx_poke(chip, BA1_CPI, phiIncr);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ snd_cs46xx_poke(chip, BA1_CSRC,
+ ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
+ snd_cs46xx_poke(chip, BA1_CCI, coeffIncr);
+ snd_cs46xx_poke(chip, BA1_CD,
+ (((BA1_VARIDEC_BUF_1 + (initialDelay << 2)) << 16) & 0xFFFF0000) | 0x80);
+ snd_cs46xx_poke(chip, BA1_CPI, phiIncr);
+ }
/*
* Figure out the frame group length for the write back task. Basically,
@@ -675,13 +845,12 @@ static void snd_cs46xx_set_capture_sample_rate(struct snd_cs46xx *chip, unsigned
/*
* Fill in the WriteBack control block.
*/
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_cs46xx_poke(chip, BA1_CFG1, frameGroupLength);
snd_cs46xx_poke(chip, BA1_CFG2, (0x00800000 | frameGroupLength));
snd_cs46xx_poke(chip, BA1_CCST, 0x0000FFFF);
snd_cs46xx_poke(chip, BA1_CSPB, ((65536 * rate) / 24000));
snd_cs46xx_poke(chip, (BA1_CSPB + 4), 0x0000FFFF);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
/*
@@ -700,8 +869,8 @@ static int snd_cs46xx_playback_transfer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_cs46xx_pcm * cpcm = runtime->private_data;
- snd_pcm_indirect_playback_transfer(substream, &cpcm->pcm_rec, snd_cs46xx_pb_trans_copy);
- return 0;
+ return snd_pcm_indirect_playback_transfer(substream, &cpcm->pcm_rec,
+ snd_cs46xx_pb_trans_copy);
}
static void snd_cs46xx_cp_trans_copy(struct snd_pcm_substream *substream,
@@ -716,8 +885,8 @@ static void snd_cs46xx_cp_trans_copy(struct snd_pcm_substream *substream,
static int snd_cs46xx_capture_transfer(struct snd_pcm_substream *substream)
{
struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
- snd_pcm_indirect_capture_transfer(substream, &chip->capt.pcm_rec, snd_cs46xx_cp_trans_copy);
- return 0;
+ return snd_pcm_indirect_capture_transfer(substream, &chip->capt.pcm_rec,
+ snd_cs46xx_cp_trans_copy);
}
static snd_pcm_uframes_t snd_cs46xx_playback_direct_pointer(struct snd_pcm_substream *substream)
@@ -796,15 +965,14 @@ static int snd_cs46xx_playback_trigger(struct snd_pcm_substream *substream,
if (substream->runtime->periods != CS46XX_FRAGS)
snd_cs46xx_playback_transfer(substream);
#else
- spin_lock(&chip->reg_lock);
- if (substream->runtime->periods != CS46XX_FRAGS)
- snd_cs46xx_playback_transfer(substream);
- { unsigned int tmp;
- tmp = snd_cs46xx_peek(chip, BA1_PCTL);
- tmp &= 0x0000ffff;
- snd_cs46xx_poke(chip, BA1_PCTL, chip->play_ctl | tmp);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ unsigned int tmp;
+ if (substream->runtime->periods != CS46XX_FRAGS)
+ snd_cs46xx_playback_transfer(substream);
+ tmp = snd_cs46xx_peek(chip, BA1_PCTL);
+ tmp &= 0x0000ffff;
+ snd_cs46xx_poke(chip, BA1_PCTL, chip->play_ctl | tmp);
}
- spin_unlock(&chip->reg_lock);
#endif
break;
case SNDRV_PCM_TRIGGER_STOP:
@@ -817,13 +985,12 @@ static int snd_cs46xx_playback_trigger(struct snd_pcm_substream *substream,
if (!cpcm->pcm_channel->unlinked)
cs46xx_dsp_pcm_unlink(chip,cpcm->pcm_channel);
#else
- spin_lock(&chip->reg_lock);
- { unsigned int tmp;
- tmp = snd_cs46xx_peek(chip, BA1_PCTL);
- tmp &= 0x0000ffff;
- snd_cs46xx_poke(chip, BA1_PCTL, tmp);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ unsigned int tmp;
+ tmp = snd_cs46xx_peek(chip, BA1_PCTL);
+ tmp &= 0x0000ffff;
+ snd_cs46xx_poke(chip, BA1_PCTL, tmp);
}
- spin_unlock(&chip->reg_lock);
#endif
break;
default:
@@ -839,9 +1006,8 @@ static int snd_cs46xx_capture_trigger(struct snd_pcm_substream *substream,
{
struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
unsigned int tmp;
- int result = 0;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -856,12 +1022,9 @@ static int snd_cs46xx_capture_trigger(struct snd_pcm_substream *substream,
snd_cs46xx_poke(chip, BA1_CCTL, tmp);
break;
default:
- result = -EINVAL;
- break;
+ return -EINVAL;
}
- spin_unlock(&chip->reg_lock);
-
- return result;
+ return 0;
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
@@ -874,7 +1037,8 @@ static int _cs46xx_adjust_sample_rate (struct snd_cs46xx *chip, struct snd_cs46x
cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, sample_rate,
cpcm, cpcm->hw_buf.addr,cpcm->pcm_channel_id);
if (cpcm->pcm_channel == NULL) {
- snd_printk(KERN_ERR "cs46xx: failed to create virtual PCM channel\n");
+ dev_err(chip->card->dev,
+ "failed to create virtual PCM channel\n");
return -ENOMEM;
}
cpcm->pcm_channel->sample_rate = sample_rate;
@@ -884,10 +1048,12 @@ static int _cs46xx_adjust_sample_rate (struct snd_cs46xx *chip, struct snd_cs46x
int unlinked = cpcm->pcm_channel->unlinked;
cs46xx_dsp_destroy_pcm_channel (chip,cpcm->pcm_channel);
- if ( (cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, sample_rate, cpcm,
- cpcm->hw_buf.addr,
- cpcm->pcm_channel_id)) == NULL) {
- snd_printk(KERN_ERR "cs46xx: failed to re-create virtual PCM channel\n");
+ cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel(chip, sample_rate, cpcm,
+ cpcm->hw_buf.addr,
+ cpcm->pcm_channel_id);
+ if (!cpcm->pcm_channel) {
+ dev_err(chip->card->dev,
+ "failed to re-create virtual PCM channel\n");
return -ENOMEM;
}
@@ -917,26 +1083,20 @@ static int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,
if (snd_BUG_ON(!sample_rate))
return -ENXIO;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
- if (_cs46xx_adjust_sample_rate (chip,cpcm,sample_rate)) {
- mutex_unlock(&chip->spos_mutex);
+ if (_cs46xx_adjust_sample_rate(chip, cpcm, sample_rate))
return -ENXIO;
- }
snd_BUG_ON(!cpcm->pcm_channel);
- if (!cpcm->pcm_channel) {
- mutex_unlock(&chip->spos_mutex);
+ if (!cpcm->pcm_channel)
return -ENXIO;
- }
-
- if (cs46xx_dsp_pcm_channel_set_period (chip,cpcm->pcm_channel,period_size)) {
- mutex_unlock(&chip->spos_mutex);
+ if (cs46xx_dsp_pcm_channel_set_period(chip, cpcm->pcm_channel, period_size))
return -EINVAL;
- }
- snd_printdd ("period_size (%d), periods (%d) buffer_size(%d)\n",
+ dev_dbg(chip->card->dev,
+ "period_size (%d), periods (%d) buffer_size(%d)\n",
period_size, params_periods(hw_params),
params_buffer_bytes(hw_params));
#endif
@@ -944,9 +1104,7 @@ static int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,
if (params_periods(hw_params) == CS46XX_FRAGS) {
if (runtime->dma_area != cpcm->hw_buf.area)
snd_pcm_lib_free_pages(substream);
- runtime->dma_area = cpcm->hw_buf.area;
- runtime->dma_addr = cpcm->hw_buf.addr;
- runtime->dma_bytes = cpcm->hw_buf.bytes;
+ snd_pcm_set_runtime_buffer(substream, &cpcm->hw_buf);
#ifdef CONFIG_SND_CS46XX_NEW_DSP
@@ -966,17 +1124,11 @@ static int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,
#endif
} else {
- if (runtime->dma_area == cpcm->hw_buf.area) {
- runtime->dma_area = NULL;
- runtime->dma_addr = 0;
- runtime->dma_bytes = 0;
- }
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) {
-#ifdef CONFIG_SND_CS46XX_NEW_DSP
- mutex_unlock(&chip->spos_mutex);
-#endif
+ if (runtime->dma_area == cpcm->hw_buf.area)
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ if (err < 0)
return err;
- }
#ifdef CONFIG_SND_CS46XX_NEW_DSP
if (cpcm->pcm_channel_id == DSP_PCM_MAIN_CHANNEL) {
@@ -996,10 +1148,6 @@ static int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,
}
-#ifdef CONFIG_SND_CS46XX_NEW_DSP
- mutex_unlock(&chip->spos_mutex);
-#endif
-
return 0;
}
@@ -1018,9 +1166,7 @@ static int snd_cs46xx_playback_hw_free(struct snd_pcm_substream *substream)
if (runtime->dma_area != cpcm->hw_buf.area)
snd_pcm_lib_free_pages(substream);
- runtime->dma_area = NULL;
- runtime->dma_addr = 0;
- runtime->dma_bytes = 0;
+ snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
@@ -1109,17 +1255,13 @@ static int snd_cs46xx_capture_hw_params(struct snd_pcm_substream *substream,
if (runtime->periods == CS46XX_FRAGS) {
if (runtime->dma_area != chip->capt.hw_buf.area)
snd_pcm_lib_free_pages(substream);
- runtime->dma_area = chip->capt.hw_buf.area;
- runtime->dma_addr = chip->capt.hw_buf.addr;
- runtime->dma_bytes = chip->capt.hw_buf.bytes;
+ snd_pcm_set_runtime_buffer(substream, &chip->capt.hw_buf);
substream->ops = &snd_cs46xx_capture_ops;
} else {
- if (runtime->dma_area == chip->capt.hw_buf.area) {
- runtime->dma_area = NULL;
- runtime->dma_addr = 0;
- runtime->dma_bytes = 0;
- }
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+ if (runtime->dma_area == chip->capt.hw_buf.area)
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ if (err < 0)
return err;
substream->ops = &snd_cs46xx_capture_indirect_ops;
}
@@ -1134,9 +1276,7 @@ static int snd_cs46xx_capture_hw_free(struct snd_pcm_substream *substream)
if (runtime->dma_area != chip->capt.hw_buf.area)
snd_pcm_lib_free_pages(substream);
- runtime->dma_area = NULL;
- runtime->dma_addr = 0;
- runtime->dma_bytes = 0;
+ snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
@@ -1221,7 +1361,7 @@ static irqreturn_t snd_cs46xx_interrupt(int irq, void *dev_id)
if ((status1 & HISR_MIDI) && chip->rmidi) {
unsigned char c;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
while ((snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_RBE) == 0) {
c = snd_cs46xx_peekBA0(chip, BA0_MIDRP);
if ((chip->midcr & MIDCR_RIE) == 0)
@@ -1238,7 +1378,6 @@ static irqreturn_t snd_cs46xx_interrupt(int irq, void *dev_id)
}
snd_cs46xx_pokeBA0(chip, BA0_MIDWP, c);
}
- spin_unlock(&chip->reg_lock);
}
/*
* EOI to the PCI part....reenables interrupts
@@ -1248,12 +1387,13 @@ static irqreturn_t snd_cs46xx_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static struct snd_pcm_hardware snd_cs46xx_playback =
+static const struct snd_pcm_hardware snd_cs46xx_playback =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER /*|*/
- /*SNDRV_PCM_INFO_RESUME*/),
+ /*SNDRV_PCM_INFO_RESUME*/ |
+ SNDRV_PCM_INFO_SYNC_APPLPTR),
.formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE),
@@ -1270,12 +1410,13 @@ static struct snd_pcm_hardware snd_cs46xx_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_cs46xx_capture =
+static const struct snd_pcm_hardware snd_cs46xx_capture =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER /*|*/
- /*SNDRV_PCM_INFO_RESUME*/),
+ /*SNDRV_PCM_INFO_RESUME*/ |
+ SNDRV_PCM_INFO_SYNC_APPLPTR),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
.rate_min = 5500,
@@ -1292,9 +1433,9 @@ static struct snd_pcm_hardware snd_cs46xx_capture =
#ifdef CONFIG_SND_CS46XX_NEW_DSP
-static unsigned int period_sizes[] = { 32, 64, 128, 256, 512, 1024, 2048 };
+static const unsigned int period_sizes[] = { 32, 64, 128, 256, 512, 1024, 2048 };
-static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = {
.count = ARRAY_SIZE(period_sizes),
.list = period_sizes,
.mask = 0
@@ -1316,7 +1457,7 @@ static int _cs46xx_playback_open_channel (struct snd_pcm_substream *substream,in
cpcm = kzalloc(sizeof(*cpcm), GFP_KERNEL);
if (cpcm == NULL)
return -ENOMEM;
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
PAGE_SIZE, &cpcm->hw_buf) < 0) {
kfree(cpcm);
return -ENOMEM;
@@ -1328,16 +1469,14 @@ static int _cs46xx_playback_open_channel (struct snd_pcm_substream *substream,in
cpcm->substream = substream;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
- mutex_lock(&chip->spos_mutex);
- cpcm->pcm_channel = NULL;
- cpcm->pcm_channel_id = pcm_channel_id;
-
+ scoped_guard(mutex, &chip->spos_mutex) {
+ cpcm->pcm_channel = NULL;
+ cpcm->pcm_channel_id = pcm_channel_id;
+ }
snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
&hw_constraints_period_sizes);
-
- mutex_unlock(&chip->spos_mutex);
#else
chip->playback_pcm = cpcm; /* HACK */
#endif
@@ -1351,22 +1490,20 @@ static int _cs46xx_playback_open_channel (struct snd_pcm_substream *substream,in
static int snd_cs46xx_playback_open(struct snd_pcm_substream *substream)
{
- snd_printdd("open front channel\n");
+ dev_dbg(substream->pcm->card->dev, "open front channel\n");
return _cs46xx_playback_open_channel(substream,DSP_PCM_MAIN_CHANNEL);
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
static int snd_cs46xx_playback_open_rear(struct snd_pcm_substream *substream)
{
- snd_printdd("open rear channel\n");
-
+ dev_dbg(substream->pcm->card->dev, "open rear channel\n");
return _cs46xx_playback_open_channel(substream,DSP_PCM_REAR_CHANNEL);
}
static int snd_cs46xx_playback_open_clfe(struct snd_pcm_substream *substream)
{
- snd_printdd("open center - LFE channel\n");
-
+ dev_dbg(substream->pcm->card->dev, "open center - LFE channel\n");
return _cs46xx_playback_open_channel(substream,DSP_PCM_CENTER_LFE_CHANNEL);
}
@@ -1374,11 +1511,11 @@ static int snd_cs46xx_playback_open_iec958(struct snd_pcm_substream *substream)
{
struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
- snd_printdd("open raw iec958 channel\n");
+ dev_dbg(chip->card->dev, "open raw iec958 channel\n");
- mutex_lock(&chip->spos_mutex);
- cs46xx_iec958_pre_open (chip);
- mutex_unlock(&chip->spos_mutex);
+ scoped_guard(mutex, &chip->spos_mutex) {
+ cs46xx_iec958_pre_open(chip);
+ }
return _cs46xx_playback_open_channel(substream,DSP_IEC958_CHANNEL);
}
@@ -1390,13 +1527,13 @@ static int snd_cs46xx_playback_close_iec958(struct snd_pcm_substream *substream)
int err;
struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
- snd_printdd("close raw iec958 channel\n");
+ dev_dbg(chip->card->dev, "close raw iec958 channel\n");
err = snd_cs46xx_playback_close(substream);
- mutex_lock(&chip->spos_mutex);
- cs46xx_iec958_post_close (chip);
- mutex_unlock(&chip->spos_mutex);
+ scoped_guard(mutex, &chip->spos_mutex) {
+ cs46xx_iec958_post_close(chip);
+ }
return err;
}
@@ -1406,7 +1543,7 @@ static int snd_cs46xx_capture_open(struct snd_pcm_substream *substream)
{
struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
PAGE_SIZE, &chip->capt.hw_buf) < 0)
return -ENOMEM;
chip->capt.substream = substream;
@@ -1437,12 +1574,12 @@ static int snd_cs46xx_playback_close(struct snd_pcm_substream *substream)
if (!cpcm) return -ENXIO;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
- mutex_lock(&chip->spos_mutex);
- if (cpcm->pcm_channel) {
- cs46xx_dsp_destroy_pcm_channel(chip,cpcm->pcm_channel);
- cpcm->pcm_channel = NULL;
+ scoped_guard(mutex, &chip->spos_mutex) {
+ if (cpcm->pcm_channel) {
+ cs46xx_dsp_destroy_pcm_channel(chip, cpcm->pcm_channel);
+ cpcm->pcm_channel = NULL;
+ }
}
- mutex_unlock(&chip->spos_mutex);
#else
chip->playback_pcm = NULL;
#endif
@@ -1466,10 +1603,9 @@ static int snd_cs46xx_capture_close(struct snd_pcm_substream *substream)
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
-static struct snd_pcm_ops snd_cs46xx_playback_rear_ops = {
+static const struct snd_pcm_ops snd_cs46xx_playback_rear_ops = {
.open = snd_cs46xx_playback_open_rear,
.close = snd_cs46xx_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
@@ -1477,10 +1613,9 @@ static struct snd_pcm_ops snd_cs46xx_playback_rear_ops = {
.pointer = snd_cs46xx_playback_direct_pointer,
};
-static struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops = {
+static const struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops = {
.open = snd_cs46xx_playback_open_rear,
.close = snd_cs46xx_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
@@ -1489,10 +1624,9 @@ static struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops = {
.ack = snd_cs46xx_playback_transfer,
};
-static struct snd_pcm_ops snd_cs46xx_playback_clfe_ops = {
+static const struct snd_pcm_ops snd_cs46xx_playback_clfe_ops = {
.open = snd_cs46xx_playback_open_clfe,
.close = snd_cs46xx_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
@@ -1500,10 +1634,9 @@ static struct snd_pcm_ops snd_cs46xx_playback_clfe_ops = {
.pointer = snd_cs46xx_playback_direct_pointer,
};
-static struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops = {
+static const struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops = {
.open = snd_cs46xx_playback_open_clfe,
.close = snd_cs46xx_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
@@ -1512,10 +1645,9 @@ static struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops = {
.ack = snd_cs46xx_playback_transfer,
};
-static struct snd_pcm_ops snd_cs46xx_playback_iec958_ops = {
+static const struct snd_pcm_ops snd_cs46xx_playback_iec958_ops = {
.open = snd_cs46xx_playback_open_iec958,
.close = snd_cs46xx_playback_close_iec958,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
@@ -1523,10 +1655,9 @@ static struct snd_pcm_ops snd_cs46xx_playback_iec958_ops = {
.pointer = snd_cs46xx_playback_direct_pointer,
};
-static struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops = {
+static const struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops = {
.open = snd_cs46xx_playback_open_iec958,
.close = snd_cs46xx_playback_close_iec958,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
@@ -1537,10 +1668,9 @@ static struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops = {
#endif
-static struct snd_pcm_ops snd_cs46xx_playback_ops = {
+static const struct snd_pcm_ops snd_cs46xx_playback_ops = {
.open = snd_cs46xx_playback_open,
.close = snd_cs46xx_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
@@ -1548,10 +1678,9 @@ static struct snd_pcm_ops snd_cs46xx_playback_ops = {
.pointer = snd_cs46xx_playback_direct_pointer,
};
-static struct snd_pcm_ops snd_cs46xx_playback_indirect_ops = {
+static const struct snd_pcm_ops snd_cs46xx_playback_indirect_ops = {
.open = snd_cs46xx_playback_open,
.close = snd_cs46xx_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_playback_hw_params,
.hw_free = snd_cs46xx_playback_hw_free,
.prepare = snd_cs46xx_playback_prepare,
@@ -1560,10 +1689,9 @@ static struct snd_pcm_ops snd_cs46xx_playback_indirect_ops = {
.ack = snd_cs46xx_playback_transfer,
};
-static struct snd_pcm_ops snd_cs46xx_capture_ops = {
+static const struct snd_pcm_ops snd_cs46xx_capture_ops = {
.open = snd_cs46xx_capture_open,
.close = snd_cs46xx_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_capture_hw_params,
.hw_free = snd_cs46xx_capture_hw_free,
.prepare = snd_cs46xx_capture_prepare,
@@ -1571,10 +1699,9 @@ static struct snd_pcm_ops snd_cs46xx_capture_ops = {
.pointer = snd_cs46xx_capture_direct_pointer,
};
-static struct snd_pcm_ops snd_cs46xx_capture_indirect_ops = {
+static const struct snd_pcm_ops snd_cs46xx_capture_indirect_ops = {
.open = snd_cs46xx_capture_open,
.close = snd_cs46xx_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs46xx_capture_hw_params,
.hw_free = snd_cs46xx_capture_hw_free,
.prepare = snd_cs46xx_capture_prepare,
@@ -1589,14 +1716,13 @@ static struct snd_pcm_ops snd_cs46xx_capture_indirect_ops = {
#define MAX_PLAYBACK_CHANNELS 1
#endif
-int __devinit snd_cs46xx_pcm(struct snd_cs46xx *chip, int device, struct snd_pcm ** rpcm)
+int snd_cs46xx_pcm(struct snd_cs46xx *chip, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
- if ((err = snd_pcm_new(chip->card, "CS46xx", device, MAX_PLAYBACK_CHANNELS, 1, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "CS46xx", device, MAX_PLAYBACK_CHANNELS, 1, &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
@@ -1606,29 +1732,25 @@ int __devinit snd_cs46xx_pcm(struct snd_cs46xx *chip, int device, struct snd_pcm
/* global setup */
pcm->info_flags = 0;
- strcpy(pcm->name, "CS46xx");
+ strscpy(pcm->name, "CS46xx");
chip->pcm = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ &chip->pci->dev,
+ 64*1024, 256*1024);
return 0;
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
-int __devinit snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device, struct snd_pcm ** rpcm)
+int snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
-
- if ((err = snd_pcm_new(chip->card, "CS46xx - Rear", device, MAX_PLAYBACK_CHANNELS, 0, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "CS46xx - Rear", device, MAX_PLAYBACK_CHANNELS, 0, &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
@@ -1637,27 +1759,23 @@ int __devinit snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device, struct sn
/* global setup */
pcm->info_flags = 0;
- strcpy(pcm->name, "CS46xx - Rear");
+ strscpy(pcm->name, "CS46xx - Rear");
chip->pcm_rear = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ &chip->pci->dev,
+ 64*1024, 256*1024);
return 0;
}
-int __devinit snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device, struct snd_pcm ** rpcm)
+int snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
-
- if ((err = snd_pcm_new(chip->card, "CS46xx - Center LFE", device, MAX_PLAYBACK_CHANNELS, 0, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "CS46xx - Center LFE", device, MAX_PLAYBACK_CHANNELS, 0, &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
@@ -1666,27 +1784,23 @@ int __devinit snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device, str
/* global setup */
pcm->info_flags = 0;
- strcpy(pcm->name, "CS46xx - Center LFE");
+ strscpy(pcm->name, "CS46xx - Center LFE");
chip->pcm_center_lfe = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ &chip->pci->dev,
+ 64*1024, 256*1024);
return 0;
}
-int __devinit snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device, struct snd_pcm ** rpcm)
+int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
-
- if ((err = snd_pcm_new(chip->card, "CS46xx - IEC958", device, 1, 0, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "CS46xx - IEC958", device, 1, 0, &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
@@ -1695,14 +1809,12 @@ int __devinit snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device, struct
/* global setup */
pcm->info_flags = 0;
- strcpy(pcm->name, "CS46xx - IEC958");
- chip->pcm_rear = pcm;
+ strscpy(pcm->name, "CS46xx - IEC958");
+ chip->pcm_iec958 = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ &chip->pci->dev,
+ 64*1024, 256*1024);
return 0;
}
@@ -1711,13 +1823,6 @@ int __devinit snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device, struct
/*
* Mixer routines
*/
-static void snd_cs46xx_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
-{
- struct snd_cs46xx *chip = bus->private_data;
-
- chip->ac97_bus = NULL;
-}
-
static void snd_cs46xx_mixer_free_ac97(struct snd_ac97 *ac97)
{
struct snd_cs46xx *chip = ac97->private_data;
@@ -1849,15 +1954,15 @@ static int snd_cs46xx_iec958_put(struct snd_kcontrol *kcontrol,
switch (kcontrol->private_value) {
case CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT:
- mutex_lock(&chip->spos_mutex);
- change = (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED);
- if (ucontrol->value.integer.value[0] && !change)
- cs46xx_dsp_enable_spdif_out(chip);
- else if (change && !ucontrol->value.integer.value[0])
- cs46xx_dsp_disable_spdif_out(chip);
-
- res = (change != (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED));
- mutex_unlock(&chip->spos_mutex);
+ scoped_guard(mutex, &chip->spos_mutex) {
+ change = (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED);
+ if (ucontrol->value.integer.value[0] && !change)
+ cs46xx_dsp_enable_spdif_out(chip);
+ else if (change && !ucontrol->value.integer.value[0])
+ cs46xx_dsp_disable_spdif_out(chip);
+
+ res = (change != (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED));
+ }
break;
case CS46XX_MIXER_SPDIF_INPUT_ELEMENT:
change = chip->dsp_spos_instance->spdif_status_in;
@@ -1998,12 +2103,11 @@ static int snd_cs46xx_spdif_default_get(struct snd_kcontrol *kcontrol,
struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
ucontrol->value.iec958.status[0] = _wrap_all_bits((ins->spdif_csuv_default >> 24) & 0xff);
ucontrol->value.iec958.status[1] = _wrap_all_bits((ins->spdif_csuv_default >> 16) & 0xff);
ucontrol->value.iec958.status[2] = 0;
ucontrol->value.iec958.status[3] = _wrap_all_bits((ins->spdif_csuv_default) & 0xff);
- mutex_unlock(&chip->spos_mutex);
return 0;
}
@@ -2016,7 +2120,7 @@ static int snd_cs46xx_spdif_default_put(struct snd_kcontrol *kcontrol,
unsigned int val;
int change;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
val = ((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[0]) << 24) |
((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[2]) << 16) |
((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[3])) |
@@ -2030,8 +2134,6 @@ static int snd_cs46xx_spdif_default_put(struct snd_kcontrol *kcontrol,
if ( !(ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN) )
cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV,val);
- mutex_unlock(&chip->spos_mutex);
-
return change;
}
@@ -2051,12 +2153,11 @@ static int snd_cs46xx_spdif_stream_get(struct snd_kcontrol *kcontrol,
struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
ucontrol->value.iec958.status[0] = _wrap_all_bits((ins->spdif_csuv_stream >> 24) & 0xff);
ucontrol->value.iec958.status[1] = _wrap_all_bits((ins->spdif_csuv_stream >> 16) & 0xff);
ucontrol->value.iec958.status[2] = 0;
ucontrol->value.iec958.status[3] = _wrap_all_bits((ins->spdif_csuv_stream) & 0xff);
- mutex_unlock(&chip->spos_mutex);
return 0;
}
@@ -2069,7 +2170,7 @@ static int snd_cs46xx_spdif_stream_put(struct snd_kcontrol *kcontrol,
unsigned int val;
int change;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
val = ((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[0]) << 24) |
((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[1]) << 16) |
((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[3])) |
@@ -2083,15 +2184,13 @@ static int snd_cs46xx_spdif_stream_put(struct snd_kcontrol *kcontrol,
if ( ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN )
cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV,val);
- mutex_unlock(&chip->spos_mutex);
-
return change;
}
#endif /* CONFIG_SND_CS46XX_NEW_DSP */
-static struct snd_kcontrol_new snd_cs46xx_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_cs46xx_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DAC Volume",
@@ -2206,7 +2305,7 @@ static int snd_cs46xx_front_dup_put(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[0] ? 0 : 0x200);
}
-static struct snd_kcontrol_new snd_cs46xx_front_dup_ctl = {
+static const struct snd_kcontrol_new snd_cs46xx_front_dup_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Duplicate Front",
.info = snd_mixer_boolean_info,
@@ -2217,7 +2316,7 @@ static struct snd_kcontrol_new snd_cs46xx_front_dup_ctl = {
#ifdef CONFIG_SND_CS46XX_NEW_DSP
/* Only available on the Hercules Game Theater XP soundcard */
-static struct snd_kcontrol_new snd_hercules_controls[] = {
+static const struct snd_kcontrol_new snd_hercules_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Optical/Coaxial SPDIF Input Switch",
@@ -2238,10 +2337,10 @@ static void snd_cs46xx_codec_reset (struct snd_ac97 * ac97)
/* set the desired CODEC mode */
if (ac97->num == CS46XX_PRIMARY_CODEC_INDEX) {
- snd_printdd("cs46xx: CODEC1 mode %04x\n", 0x0);
+ dev_dbg(ac97->bus->card->dev, "CODEC1 mode %04x\n", 0x0);
snd_cs46xx_ac97_write(ac97, AC97_CSR_ACMODE, 0x0);
} else if (ac97->num == CS46XX_SECONDARY_CODEC_INDEX) {
- snd_printdd("cs46xx: CODEC2 mode %04x\n", 0x3);
+ dev_dbg(ac97->bus->card->dev, "CODEC2 mode %04x\n", 0x3);
snd_cs46xx_ac97_write(ac97, AC97_CSR_ACMODE, 0x3);
} else {
snd_BUG(); /* should never happen ... */
@@ -2267,17 +2366,19 @@ static void snd_cs46xx_codec_reset (struct snd_ac97 * ac97)
/* test if we can write to the record gain volume register */
snd_ac97_write(ac97, AC97_REC_GAIN, 0x8a05);
- if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a05)
+ err = snd_ac97_read(ac97, AC97_REC_GAIN);
+ if (err == 0x8a05)
return;
msleep(10);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_ERR "CS46xx secondary codec doesn't respond!\n");
+ dev_err(ac97->bus->card->dev,
+ "CS46xx secondary codec doesn't respond!\n");
}
#endif
-static int __devinit cs46xx_detect_codec(struct snd_cs46xx *chip, int codec)
+static int cs46xx_detect_codec(struct snd_cs46xx *chip, int codec)
{
int idx, err;
struct snd_ac97_template ac97;
@@ -2293,7 +2394,8 @@ static int __devinit cs46xx_detect_codec(struct snd_cs46xx *chip, int codec)
snd_cs46xx_codec_write(chip, AC97_RESET, 0, codec);
udelay(10);
if (snd_cs46xx_codec_read(chip, AC97_RESET, codec) & 0x8000) {
- snd_printdd("snd_cs46xx: seconadry codec not present\n");
+ dev_dbg(chip->card->dev,
+ "secondary codec not present\n");
return -ENXIO;
}
}
@@ -2306,17 +2408,16 @@ static int __devinit cs46xx_detect_codec(struct snd_cs46xx *chip, int codec)
}
msleep(10);
}
- snd_printdd("snd_cs46xx: codec %d detection timeout\n", codec);
+ dev_dbg(chip->card->dev, "codec %d detection timeout\n", codec);
return -ENXIO;
}
-int __devinit snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
+int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
{
struct snd_card *card = chip->card;
- struct snd_ctl_elem_id id;
int err;
unsigned int idx;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
#ifdef CONFIG_SND_CS46XX_NEW_DSP
.reset = snd_cs46xx_codec_reset,
#endif
@@ -2326,17 +2427,17 @@ int __devinit snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
/* detect primary codec */
chip->nr_ac97_codecs = 0;
- snd_printdd("snd_cs46xx: detecting primary codec\n");
- if ((err = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+ dev_dbg(chip->card->dev, "detecting primary codec\n");
+ err = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus);
+ if (err < 0)
return err;
- chip->ac97_bus->private_free = snd_cs46xx_mixer_free_ac97_bus;
if (cs46xx_detect_codec(chip, CS46XX_PRIMARY_CODEC_INDEX) < 0)
return -ENXIO;
chip->nr_ac97_codecs = 1;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
- snd_printdd("snd_cs46xx: detecting seconadry codec\n");
+ dev_dbg(chip->card->dev, "detecting secondary codec\n");
/* try detect a secondary codec */
if (! cs46xx_detect_codec(chip, CS46XX_SECONDARY_CODEC_INDEX))
chip->nr_ac97_codecs = 2;
@@ -2348,20 +2449,19 @@ int __devinit snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
kctl = snd_ctl_new1(&snd_cs46xx_controls[idx], chip);
if (kctl && kctl->id.iface == SNDRV_CTL_ELEM_IFACE_PCM)
kctl->id.device = spdif_device;
- if ((err = snd_ctl_add(card, kctl)) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
}
/* get EAPD mixer switch (for voyetra hack) */
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, "External Amplifier");
- chip->eapd_switch = snd_ctl_find_id(chip->card, &id);
+ chip->eapd_switch = snd_ctl_find_id_mixer(chip->card,
+ "External Amplifier");
#ifdef CONFIG_SND_CS46XX_NEW_DSP
if (chip->nr_ac97_codecs == 1) {
unsigned int id2 = chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]->id & 0xffff;
- if (id2 == 0x592b || id2 == 0x592d) {
+ if ((id2 & 0xfff0) == 0x5920) { /* CS4294 and CS4298 */
err = snd_ctl_add(card, snd_ctl_new1(&snd_cs46xx_front_dup_ctl, chip));
if (err < 0)
return err;
@@ -2371,7 +2471,7 @@ int __devinit snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
}
/* do soundcard specific mixer setup */
if (chip->mixer_init) {
- snd_printdd ("calling chip->mixer_init(chip);\n");
+ dev_dbg(chip->card->dev, "calling chip->mixer_init(chip);\n");
chip->mixer_init(chip);
}
#endif
@@ -2398,7 +2498,7 @@ static int snd_cs46xx_midi_input_open(struct snd_rawmidi_substream *substream)
struct snd_cs46xx *chip = substream->rmidi->private_data;
chip->active_ctrl(chip, 1);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->uartm |= CS46XX_MODE_INPUT;
chip->midcr |= MIDCR_RXE;
chip->midi_input = substream;
@@ -2407,7 +2507,6 @@ static int snd_cs46xx_midi_input_open(struct snd_rawmidi_substream *substream)
} else {
snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -2415,16 +2514,16 @@ static int snd_cs46xx_midi_input_close(struct snd_rawmidi_substream *substream)
{
struct snd_cs46xx *chip = substream->rmidi->private_data;
- spin_lock_irq(&chip->reg_lock);
- chip->midcr &= ~(MIDCR_RXE | MIDCR_RIE);
- chip->midi_input = NULL;
- if (!(chip->uartm & CS46XX_MODE_OUTPUT)) {
- snd_cs46xx_midi_reset(chip);
- } else {
- snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ chip->midcr &= ~(MIDCR_RXE | MIDCR_RIE);
+ chip->midi_input = NULL;
+ if (!(chip->uartm & CS46XX_MODE_OUTPUT)) {
+ snd_cs46xx_midi_reset(chip);
+ } else {
+ snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ chip->uartm &= ~CS46XX_MODE_INPUT;
}
- chip->uartm &= ~CS46XX_MODE_INPUT;
- spin_unlock_irq(&chip->reg_lock);
chip->active_ctrl(chip, -1);
return 0;
}
@@ -2435,7 +2534,7 @@ static int snd_cs46xx_midi_output_open(struct snd_rawmidi_substream *substream)
chip->active_ctrl(chip, 1);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->uartm |= CS46XX_MODE_OUTPUT;
chip->midcr |= MIDCR_TXE;
chip->midi_output = substream;
@@ -2444,7 +2543,6 @@ static int snd_cs46xx_midi_output_open(struct snd_rawmidi_substream *substream)
} else {
snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -2452,26 +2550,25 @@ static int snd_cs46xx_midi_output_close(struct snd_rawmidi_substream *substream)
{
struct snd_cs46xx *chip = substream->rmidi->private_data;
- spin_lock_irq(&chip->reg_lock);
- chip->midcr &= ~(MIDCR_TXE | MIDCR_TIE);
- chip->midi_output = NULL;
- if (!(chip->uartm & CS46XX_MODE_INPUT)) {
- snd_cs46xx_midi_reset(chip);
- } else {
- snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ chip->midcr &= ~(MIDCR_TXE | MIDCR_TIE);
+ chip->midi_output = NULL;
+ if (!(chip->uartm & CS46XX_MODE_INPUT)) {
+ snd_cs46xx_midi_reset(chip);
+ } else {
+ snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
+ }
+ chip->uartm &= ~CS46XX_MODE_OUTPUT;
}
- chip->uartm &= ~CS46XX_MODE_OUTPUT;
- spin_unlock_irq(&chip->reg_lock);
chip->active_ctrl(chip, -1);
return 0;
}
static void snd_cs46xx_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct snd_cs46xx *chip = substream->rmidi->private_data;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (up) {
if ((chip->midcr & MIDCR_RIE) == 0) {
chip->midcr |= MIDCR_RIE;
@@ -2483,16 +2580,14 @@ static void snd_cs46xx_midi_input_trigger(struct snd_rawmidi_substream *substrea
snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static void snd_cs46xx_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct snd_cs46xx *chip = substream->rmidi->private_data;
unsigned char byte;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
if (up) {
if ((chip->midcr & MIDCR_TIE) == 0) {
chip->midcr |= MIDCR_TIE;
@@ -2513,40 +2608,36 @@ static void snd_cs46xx_midi_output_trigger(struct snd_rawmidi_substream *substre
snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
}
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
-static struct snd_rawmidi_ops snd_cs46xx_midi_output =
+static const struct snd_rawmidi_ops snd_cs46xx_midi_output =
{
.open = snd_cs46xx_midi_output_open,
.close = snd_cs46xx_midi_output_close,
.trigger = snd_cs46xx_midi_output_trigger,
};
-static struct snd_rawmidi_ops snd_cs46xx_midi_input =
+static const struct snd_rawmidi_ops snd_cs46xx_midi_input =
{
.open = snd_cs46xx_midi_input_open,
.close = snd_cs46xx_midi_input_close,
.trigger = snd_cs46xx_midi_input_trigger,
};
-int __devinit snd_cs46xx_midi(struct snd_cs46xx *chip, int device, struct snd_rawmidi **rrawmidi)
+int snd_cs46xx_midi(struct snd_cs46xx *chip, int device)
{
struct snd_rawmidi *rmidi;
int err;
- if (rrawmidi)
- *rrawmidi = NULL;
- if ((err = snd_rawmidi_new(chip->card, "CS46XX", device, 1, 1, &rmidi)) < 0)
+ err = snd_rawmidi_new(chip->card, "CS46XX", device, 1, 1, &rmidi);
+ if (err < 0)
return err;
- strcpy(rmidi->name, "CS46XX");
+ strscpy(rmidi->name, "CS46XX");
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs46xx_midi_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs46xx_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
rmidi->private_data = chip;
chip->rmidi = rmidi;
- if (rrawmidi)
- *rrawmidi = NULL;
return 0;
}
@@ -2555,7 +2646,7 @@ int __devinit snd_cs46xx_midi(struct snd_cs46xx *chip, int device, struct snd_ra
* gameport interface
*/
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
static void snd_cs46xx_gameport_trigger(struct gameport *gameport)
{
@@ -2612,13 +2703,14 @@ static int snd_cs46xx_gameport_open(struct gameport *gameport, int mode)
return 0;
}
-int __devinit snd_cs46xx_gameport(struct snd_cs46xx *chip)
+int snd_cs46xx_gameport(struct snd_cs46xx *chip)
{
struct gameport *gp;
chip->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "cs46xx: cannot allocate memory for gameport\n");
+ dev_err(chip->card->dev,
+ "cannot allocate memory for gameport\n");
return -ENOMEM;
}
@@ -2648,11 +2740,11 @@ static inline void snd_cs46xx_remove_gameport(struct snd_cs46xx *chip)
}
}
#else
-int __devinit snd_cs46xx_gameport(struct snd_cs46xx *chip) { return -ENOSYS; }
+int snd_cs46xx_gameport(struct snd_cs46xx *chip) { return -ENOSYS; }
static inline void snd_cs46xx_remove_gameport(struct snd_cs46xx *chip) { }
#endif /* CONFIG_GAMEPORT */
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
/*
* proc interface
*/
@@ -2669,11 +2761,11 @@ static ssize_t snd_cs46xx_io_read(struct snd_info_entry *entry,
return count;
}
-static struct snd_info_entry_ops snd_cs46xx_proc_io_ops = {
+static const struct snd_info_entry_ops snd_cs46xx_proc_io_ops = {
.read = snd_cs46xx_io_read,
};
-static int __devinit snd_cs46xx_proc_init(struct snd_card *card, struct snd_cs46xx *chip)
+static int snd_cs46xx_proc_init(struct snd_card *card, struct snd_cs46xx *chip)
{
struct snd_info_entry *entry;
int idx;
@@ -2685,7 +2777,7 @@ static int __devinit snd_cs46xx_proc_init(struct snd_card *card, struct snd_cs46
entry->private_data = chip;
entry->c.ops = &snd_cs46xx_proc_io_ops;
entry->size = region->size;
- entry->mode = S_IFREG | S_IRUSR;
+ entry->mode = S_IFREG | 0400;
}
}
#ifdef CONFIG_SND_CS46XX_NEW_DSP
@@ -2701,7 +2793,7 @@ static int snd_cs46xx_proc_done(struct snd_cs46xx *chip)
#endif
return 0;
}
-#else /* !CONFIG_PROC_FS */
+#else /* !CONFIG_SND_PROC_FS */
#define snd_cs46xx_proc_init(card, chip)
#define snd_cs46xx_proc_done(chip)
#endif
@@ -2756,12 +2848,12 @@ static void snd_cs46xx_hw_stop(struct snd_cs46xx *chip)
}
-static int snd_cs46xx_free(struct snd_cs46xx *chip)
+static void snd_cs46xx_free(struct snd_card *card)
{
+ struct snd_cs46xx *chip = card->private_data;
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
int idx;
-
- if (snd_BUG_ON(!chip))
- return -EINVAL;
+#endif
if (chip->active_ctrl)
chip->active_ctrl(chip, 1);
@@ -2773,42 +2865,21 @@ static int snd_cs46xx_free(struct snd_cs46xx *chip)
snd_cs46xx_proc_done(chip);
- if (chip->region.idx[0].resource)
- snd_cs46xx_hw_stop(chip);
-
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
+ snd_cs46xx_hw_stop(chip);
if (chip->active_ctrl)
chip->active_ctrl(chip, -chip->amplifier);
- for (idx = 0; idx < 5; idx++) {
- struct snd_cs46xx_region *region = &chip->region.idx[idx];
- if (region->remap_addr)
- iounmap(region->remap_addr);
- release_and_free_resource(region->resource);
- }
-
#ifdef CONFIG_SND_CS46XX_NEW_DSP
if (chip->dsp_spos_instance) {
cs46xx_dsp_spos_destroy(chip);
chip->dsp_spos_instance = NULL;
}
+ for (idx = 0; idx < CS46XX_DSP_MODULES; idx++)
+ free_module_desc(chip->modules[idx]);
+#else
+ vfree(chip->ba1);
#endif
-
-#ifdef CONFIG_PM
- kfree(chip->saved_regs);
-#endif
-
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
-}
-
-static int snd_cs46xx_dev_free(struct snd_device *device)
-{
- struct snd_cs46xx *chip = device->device_data;
- return snd_cs46xx_free(chip);
}
/*
@@ -2951,8 +3022,10 @@ static int snd_cs46xx_chip_init(struct snd_cs46xx *chip)
}
- snd_printk(KERN_ERR "create - never read codec ready from AC'97\n");
- snd_printk(KERN_ERR "it is not probably bug, try to use CS4236 driver\n");
+ dev_err(chip->card->dev,
+ "create - never read codec ready from AC'97\n");
+ dev_err(chip->card->dev,
+ "it is not probably bug, try to use CS4236 driver\n");
return -EIO;
ok1:
#ifdef CONFIG_SND_CS46XX_NEW_DSP
@@ -2970,7 +3043,8 @@ static int snd_cs46xx_chip_init(struct snd_cs46xx *chip)
* Make sure CODEC is READY.
*/
if (!(snd_cs46xx_peekBA0(chip, BA0_ACSTS2) & ACSTS_CRDY))
- snd_printdd("cs46xx: never read card ready from secondary AC'97\n");
+ dev_dbg(chip->card->dev,
+ "never read card ready from secondary AC'97\n");
}
#endif
@@ -3000,17 +3074,21 @@ static int snd_cs46xx_chip_init(struct snd_cs46xx *chip)
}
#ifndef CONFIG_SND_CS46XX_NEW_DSP
- snd_printk(KERN_ERR "create - never read ISV3 & ISV4 from AC'97\n");
+ dev_err(chip->card->dev,
+ "create - never read ISV3 & ISV4 from AC'97\n");
return -EIO;
#else
/* This may happen on a cold boot with a Terratec SiXPack 5.1.
Reloading the driver may help, if there's other soundcards
with the same problem I would like to know. (Benny) */
- snd_printk(KERN_ERR "ERROR: snd-cs46xx: never read ISV3 & ISV4 from AC'97\n");
- snd_printk(KERN_ERR " Try reloading the ALSA driver, if you find something\n");
- snd_printk(KERN_ERR " broken or not working on your soundcard upon\n");
- snd_printk(KERN_ERR " this message please report to alsa-devel@alsa-project.org\n");
+ dev_err(chip->card->dev, "never read ISV3 & ISV4 from AC'97\n");
+ dev_err(chip->card->dev,
+ "Try reloading the ALSA driver, if you find something\n");
+ dev_err(chip->card->dev,
+ "broken or not working on your soundcard upon\n");
+ dev_err(chip->card->dev,
+ "this message please report to alsa-devel@alsa-project.org\n");
return -EIO;
#endif
@@ -3060,9 +3138,14 @@ static void cs46xx_enable_stream_irqs(struct snd_cs46xx *chip)
snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt enable */
}
-int __devinit snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
+int snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
{
unsigned int tmp;
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+ int i;
+#endif
+ int err;
+
/*
* Reset the processor.
*/
@@ -3071,45 +3154,33 @@ int __devinit snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
* Download the image to the processor.
*/
#ifdef CONFIG_SND_CS46XX_NEW_DSP
-#if 0
- if (cs46xx_dsp_load_module(chip, &cwcemb80_module) < 0) {
- snd_printk(KERN_ERR "image download error\n");
- return -EIO;
- }
-#endif
-
- if (cs46xx_dsp_load_module(chip, &cwc4630_module) < 0) {
- snd_printk(KERN_ERR "image download error [cwc4630]\n");
- return -EIO;
- }
-
- if (cs46xx_dsp_load_module(chip, &cwcasync_module) < 0) {
- snd_printk(KERN_ERR "image download error [cwcasync]\n");
- return -EIO;
- }
-
- if (cs46xx_dsp_load_module(chip, &cwcsnoop_module) < 0) {
- snd_printk(KERN_ERR "image download error [cwcsnoop]\n");
- return -EIO;
- }
-
- if (cs46xx_dsp_load_module(chip, &cwcbinhack_module) < 0) {
- snd_printk(KERN_ERR "image download error [cwcbinhack]\n");
- return -EIO;
- }
-
- if (cs46xx_dsp_load_module(chip, &cwcdma_module) < 0) {
- snd_printk(KERN_ERR "image download error [cwcdma]\n");
- return -EIO;
+ for (i = 0; i < CS46XX_DSP_MODULES; i++) {
+ err = load_firmware(chip, &chip->modules[i], module_names[i]);
+ if (err < 0) {
+ dev_err(chip->card->dev, "firmware load error [%s]\n",
+ module_names[i]);
+ return err;
+ }
+ err = cs46xx_dsp_load_module(chip, chip->modules[i]);
+ if (err < 0) {
+ dev_err(chip->card->dev, "image download error [%s]\n",
+ module_names[i]);
+ return err;
+ }
}
if (cs46xx_dsp_scb_and_task_init(chip) < 0)
return -EIO;
#else
+ err = load_firmware(chip);
+ if (err < 0)
+ return err;
+
/* old image */
- if (snd_cs46xx_download_image(chip) < 0) {
- snd_printk(KERN_ERR "image download error\n");
- return -EIO;
+ err = snd_cs46xx_download_image(chip);
+ if (err < 0) {
+ dev_err(chip->card->dev, "image download error\n");
+ return err;
}
/*
@@ -3161,7 +3232,7 @@ static int voyetra_setup_eapd_slot(struct snd_cs46xx *chip)
u32 idx, valid_slots,tmp,powerdown = 0;
u16 modem_power,pin_config,logic_type;
- snd_printdd ("cs46xx: cs46xx_setup_eapd_slot()+\n");
+ dev_dbg(chip->card->dev, "cs46xx_setup_eapd_slot()+\n");
/*
* See if the devices are powered down. If so, we must power them up first
@@ -3179,7 +3250,8 @@ static int voyetra_setup_eapd_slot(struct snd_cs46xx *chip)
* stuff.
*/
if(chip->nr_ac97_codecs != 2) {
- snd_printk (KERN_ERR "cs46xx: cs46xx_setup_eapd_slot() - no secondary codec configured\n");
+ dev_err(chip->card->dev,
+ "cs46xx_setup_eapd_slot() - no secondary codec configured\n");
return -EINVAL;
}
@@ -3220,7 +3292,7 @@ static int voyetra_setup_eapd_slot(struct snd_cs46xx *chip)
snd_cs46xx_pokeBA0(chip, BA0_ACOSV, valid_slots);
if ( cs46xx_wait_for_fifo(chip,1) ) {
- snd_printdd("FIFO is busy\n");
+ dev_dbg(chip->card->dev, "FIFO is busy\n");
return -EINVAL;
}
@@ -3241,7 +3313,9 @@ static int voyetra_setup_eapd_slot(struct snd_cs46xx *chip)
* Wait for command to complete
*/
if ( cs46xx_wait_for_fifo(chip,200) ) {
- snd_printdd("failed waiting for FIFO at addr (%02X)\n",idx);
+ dev_dbg(chip->card->dev,
+ "failed waiting for FIFO at addr (%02X)\n",
+ idx);
return -EINVAL;
}
@@ -3330,14 +3404,14 @@ static void amp_hercules(struct snd_cs46xx *chip, int change)
chip->amplifier += change;
if (chip->amplifier && !old) {
- snd_printdd ("Hercules amplifier ON\n");
+ dev_dbg(chip->card->dev, "Hercules amplifier ON\n");
snd_cs46xx_pokeBA0(chip, BA0_EGPIODR,
EGPIODR_GPOE2 | val1); /* enable EGPIO2 output */
snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR,
EGPIOPTR_GPPT2 | val2); /* open-drain on output */
} else if (old && !chip->amplifier) {
- snd_printdd ("Hercules amplifier OFF\n");
+ dev_dbg(chip->card->dev, "Hercules amplifier OFF\n");
snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, val1 & ~EGPIODR_GPOE2); /* disable */
snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, val2 & ~EGPIOPTR_GPPT2); /* disable */
}
@@ -3345,7 +3419,7 @@ static void amp_hercules(struct snd_cs46xx *chip, int change)
static void voyetra_mixer_init (struct snd_cs46xx *chip)
{
- snd_printdd ("initializing Voyetra mixer\n");
+ dev_dbg(chip->card->dev, "initializing Voyetra mixer\n");
/* Enable SPDIF out */
snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, EGPIODR_GPOE0);
@@ -3363,7 +3437,7 @@ static void hercules_mixer_init (struct snd_cs46xx *chip)
/* set EGPIO to default */
hercules_init(chip);
- snd_printdd ("initializing Hercules mixer\n");
+ dev_dbg(chip->card->dev, "initializing Hercules mixer\n");
#ifdef CONFIG_SND_CS46XX_NEW_DSP
if (chip->in_suspend)
@@ -3373,8 +3447,11 @@ static void hercules_mixer_init (struct snd_cs46xx *chip)
struct snd_kcontrol *kctl;
kctl = snd_ctl_new1(&snd_hercules_controls[idx], chip);
- if ((err = snd_ctl_add(card, kctl)) < 0) {
- printk (KERN_ERR "cs46xx: failed to initialize Hercules mixer (%d)\n",err);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0) {
+ dev_err(card->dev,
+ "failed to initialize Hercules mixer (%d)\n",
+ err);
break;
}
}
@@ -3476,7 +3553,7 @@ struct cs_card_type
void (*mixer_init)(struct snd_cs46xx *);
};
-static struct cs_card_type __devinitdata cards[] = {
+static struct cs_card_type cards[] = {
{
.vendor = 0x1489,
.id = 0x7001,
@@ -3589,8 +3666,8 @@ static struct cs_card_type __devinitdata cards[] = {
/*
* APM support
*/
-#ifdef CONFIG_PM
-static unsigned int saved_regs[] = {
+#ifdef CONFIG_PM_SLEEP
+static const unsigned int saved_regs[] = {
BA0_ACOSV,
/*BA0_ASER_FADDR,*/
BA0_ASER_MASTER,
@@ -3598,15 +3675,14 @@ static unsigned int saved_regs[] = {
BA1_CVOL,
};
-int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state)
+static int snd_cs46xx_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_cs46xx *chip = card->private_data;
int i, amp_saved;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
chip->in_suspend = 1;
- snd_pcm_suspend_all(chip->pcm);
// chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL);
// chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE);
@@ -3624,16 +3700,12 @@ int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state)
/* disable CLKRUN */
chip->active_ctrl(chip, -chip->amplifier);
chip->amplifier = amp_saved; /* restore the status */
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-int snd_cs46xx_resume(struct pci_dev *pci)
+static int snd_cs46xx_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_cs46xx *chip = card->private_data;
int amp_saved;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
@@ -3641,16 +3713,6 @@ int snd_cs46xx_resume(struct pci_dev *pci)
#endif
unsigned int tmp;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "cs46xx: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
amp_saved = chip->amplifier;
chip->amplifier = 0;
chip->active_ctrl(chip, 1); /* force to on */
@@ -3706,37 +3768,29 @@ int snd_cs46xx_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
+
+SIMPLE_DEV_PM_OPS(snd_cs46xx_pm, snd_cs46xx_suspend, snd_cs46xx_resume);
+#endif /* CONFIG_PM_SLEEP */
/*
*/
-int __devinit snd_cs46xx_create(struct snd_card *card,
- struct pci_dev * pci,
- int external_amp, int thinkpad,
- struct snd_cs46xx ** rchip)
+int snd_cs46xx_create(struct snd_card *card,
+ struct pci_dev *pci,
+ int external_amp, int thinkpad)
{
- struct snd_cs46xx *chip;
+ struct snd_cs46xx *chip = card->private_data;
int err, idx;
struct snd_cs46xx_region *region;
struct cs_card_type *cp;
u16 ss_card, ss_vendor;
- static struct snd_device_ops ops = {
- .dev_free = snd_cs46xx_dev_free,
- };
- *rchip = NULL;
-
/* enable PCI device */
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
spin_lock_init(&chip->reg_lock);
#ifdef CONFIG_SND_CS46XX_NEW_DSP
mutex_init(&chip->spos_mutex);
@@ -3744,38 +3798,42 @@ int __devinit snd_cs46xx_create(struct snd_card *card,
chip->card = card;
chip->pci = pci;
chip->irq = -1;
+
+ err = pcim_request_all_regions(pci, "CS46xx");
+ if (err < 0)
+ return err;
chip->ba0_addr = pci_resource_start(pci, 0);
chip->ba1_addr = pci_resource_start(pci, 1);
if (chip->ba0_addr == 0 || chip->ba0_addr == (unsigned long)~0 ||
chip->ba1_addr == 0 || chip->ba1_addr == (unsigned long)~0) {
- snd_printk(KERN_ERR "wrong address(es) - ba0 = 0x%lx, ba1 = 0x%lx\n",
+ dev_err(chip->card->dev,
+ "wrong address(es) - ba0 = 0x%lx, ba1 = 0x%lx\n",
chip->ba0_addr, chip->ba1_addr);
- snd_cs46xx_free(chip);
return -ENOMEM;
}
region = &chip->region.name.ba0;
- strcpy(region->name, "CS46xx_BA0");
+ strscpy(region->name, "CS46xx_BA0");
region->base = chip->ba0_addr;
region->size = CS46XX_BA0_SIZE;
region = &chip->region.name.data0;
- strcpy(region->name, "CS46xx_BA1_data0");
+ strscpy(region->name, "CS46xx_BA1_data0");
region->base = chip->ba1_addr + BA1_SP_DMEM0;
region->size = CS46XX_BA1_DATA0_SIZE;
region = &chip->region.name.data1;
- strcpy(region->name, "CS46xx_BA1_data1");
+ strscpy(region->name, "CS46xx_BA1_data1");
region->base = chip->ba1_addr + BA1_SP_DMEM1;
region->size = CS46XX_BA1_DATA1_SIZE;
region = &chip->region.name.pmem;
- strcpy(region->name, "CS46xx_BA1_pmem");
+ strscpy(region->name, "CS46xx_BA1_pmem");
region->base = chip->ba1_addr + BA1_SP_PMEM;
region->size = CS46XX_BA1_PRG_SIZE;
region = &chip->region.name.reg;
- strcpy(region->name, "CS46xx_BA1_reg");
+ strscpy(region->name, "CS46xx_BA1_reg");
region->base = chip->ba1_addr + BA1_SP_REG;
region->size = CS46XX_BA1_REG_SIZE;
@@ -3785,7 +3843,8 @@ int __devinit snd_cs46xx_create(struct snd_card *card,
for (cp = &cards[0]; cp->name; cp++) {
if (cp->vendor == ss_vendor && cp->id == ss_card) {
- snd_printdd ("hack for %s enabled\n", cp->name);
+ dev_dbg(chip->card->dev, "hack for %s enabled\n",
+ cp->name);
chip->amplifier_ctrl = cp->amp;
chip->active_ctrl = cp->active;
@@ -3798,12 +3857,14 @@ int __devinit snd_cs46xx_create(struct snd_card *card,
}
if (external_amp) {
- snd_printk(KERN_INFO "Crystal EAPD support forced on.\n");
+ dev_info(chip->card->dev,
+ "Crystal EAPD support forced on.\n");
chip->amplifier_ctrl = amp_voyetra;
}
if (thinkpad) {
- snd_printk(KERN_INFO "Activating CLKRUN hack for Thinkpad.\n");
+ dev_info(chip->card->dev,
+ "Activating CLKRUN hack for Thinkpad.\n");
chip->active_ctrl = clkrun_hack;
clkrun_init(chip);
}
@@ -3819,63 +3880,45 @@ int __devinit snd_cs46xx_create(struct snd_card *card,
for (idx = 0; idx < 5; idx++) {
region = &chip->region.idx[idx];
- if ((region->resource = request_mem_region(region->base, region->size,
- region->name)) == NULL) {
- snd_printk(KERN_ERR "unable to request memory region 0x%lx-0x%lx\n",
- region->base, region->base + region->size - 1);
- snd_cs46xx_free(chip);
- return -EBUSY;
- }
- region->remap_addr = ioremap_nocache(region->base, region->size);
+ region->remap_addr = devm_ioremap(&pci->dev, region->base,
+ region->size);
if (region->remap_addr == NULL) {
- snd_printk(KERN_ERR "%s ioremap problem\n", region->name);
- snd_cs46xx_free(chip);
+ dev_err(chip->card->dev,
+ "%s ioremap problem\n", region->name);
return -ENOMEM;
}
}
- if (request_irq(pci->irq, snd_cs46xx_interrupt, IRQF_SHARED,
- "CS46XX", chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_cs46xx_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_cs46xx_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(chip->card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_cs46xx_free;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
chip->dsp_spos_instance = cs46xx_dsp_spos_create(chip);
- if (chip->dsp_spos_instance == NULL) {
- snd_cs46xx_free(chip);
+ if (!chip->dsp_spos_instance)
return -ENOMEM;
- }
#endif
err = snd_cs46xx_chip_init(chip);
- if (err < 0) {
- snd_cs46xx_free(chip);
+ if (err < 0)
return err;
- }
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_cs46xx_free(chip);
- return err;
- }
snd_cs46xx_proc_init(card, chip);
-#ifdef CONFIG_PM
- chip->saved_regs = kmalloc(sizeof(*chip->saved_regs) *
- ARRAY_SIZE(saved_regs), GFP_KERNEL);
- if (!chip->saved_regs) {
- snd_cs46xx_free(chip);
+#ifdef CONFIG_PM_SLEEP
+ chip->saved_regs = devm_kmalloc_array(&pci->dev,
+ ARRAY_SIZE(saved_regs),
+ sizeof(*chip->saved_regs),
+ GFP_KERNEL);
+ if (!chip->saved_regs)
return -ENOMEM;
- }
#endif
chip->active_ctrl(chip, -1); /* disable CLKRUN */
-
- snd_card_set_dev(card, &pci->dev);
-
- *rchip = chip;
return 0;
}
diff --git a/sound/pci/cs46xx/cs46xx_lib.h b/sound/pci/cs46xx/cs46xx_lib.h
index b5189495d58a..6bcf2b636e8f 100644
--- a/sound/pci/cs46xx/cs46xx_lib.h
+++ b/sound/pci/cs46xx/cs46xx_lib.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#ifndef __CS46XX_LIB_H__
@@ -90,12 +75,12 @@ static inline unsigned int snd_cs46xx_peekBA0(struct snd_cs46xx *chip, unsigned
struct dsp_spos_instance *cs46xx_dsp_spos_create (struct snd_cs46xx * chip);
void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip);
int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int cs46xx_dsp_resume(struct snd_cs46xx * chip);
#endif
struct dsp_symbol_entry *cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name,
int symbol_type);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip);
int cs46xx_dsp_proc_done (struct snd_cs46xx *chip);
#else
@@ -118,7 +103,7 @@ int cs46xx_dsp_disable_adc_capture (struct snd_cs46xx *chip);
int cs46xx_poke_via_dsp (struct snd_cs46xx *chip, u32 address, u32 data);
struct dsp_scb_descriptor * cs46xx_dsp_create_scb (struct snd_cs46xx *chip, char * name,
u32 * scb_data, u32 dest);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
void cs46xx_dsp_proc_free_scb_desc (struct dsp_scb_descriptor * scb);
void cs46xx_dsp_proc_register_scb_desc (struct snd_cs46xx *chip,
struct dsp_scb_descriptor * scb);
diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c
index 3e5ca8fb519f..3d34575a0e8f 100644
--- a/sound/pci/cs46xx/dsp_spos.c
+++ b/sound/pci/cs46xx/dsp_spos.c
@@ -1,18 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -20,7 +7,7 @@
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/init.h>
@@ -32,7 +19,7 @@
#include <sound/control.h>
#include <sound/info.h>
#include <sound/asoundef.h>
-#include <sound/cs46xx.h>
+#include "cs46xx.h"
#include "cs46xx_lib.h"
#include "dsp_spos.h"
@@ -40,7 +27,7 @@
static int cs46xx_dsp_async_init (struct snd_cs46xx *chip,
struct dsp_scb_descriptor * fg_entry);
-static enum wide_opcode wide_opcodes[] = {
+static const enum wide_opcode wide_opcodes[] = {
WIDE_FOR_BEGIN_LOOP,
WIDE_FOR_BEGIN_LOOP2,
WIDE_COND_GOTO_ADDR,
@@ -85,12 +72,15 @@ static int shadow_and_reallocate_code (struct snd_cs46xx * chip, u32 * data, u32
address = (hival & 0x00FFF) << 5;
address |= loval >> 15;
- snd_printdd("handle_wideop[1]: %05x:%05x addr %04x\n",hival,loval,address);
+ dev_dbg(chip->card->dev,
+ "handle_wideop[1]: %05x:%05x addr %04x\n",
+ hival, loval, address);
if ( !(address & 0x8000) ) {
address += (ins->code.offset / 2) - overlay_begin_address;
} else {
- snd_printdd("handle_wideop[1]: ROM symbol not reallocated\n");
+ dev_dbg(chip->card->dev,
+ "handle_wideop[1]: ROM symbol not reallocated\n");
}
hival &= 0xFF000;
@@ -102,8 +92,10 @@ static int shadow_and_reallocate_code (struct snd_cs46xx * chip, u32 * data, u32
address = (hival & 0x00FFF) << 5;
address |= loval >> 15;
- snd_printdd("handle_wideop:[2] %05x:%05x addr %04x\n",hival,loval,address);
- nreallocated ++;
+ dev_dbg(chip->card->dev,
+ "handle_wideop:[2] %05x:%05x addr %04x\n",
+ hival, loval, address);
+ nreallocated++;
} /* wide_opcodes[j] == wide_op */
} /* for */
} /* mod_type == 0 ... */
@@ -113,7 +105,8 @@ static int shadow_and_reallocate_code (struct snd_cs46xx * chip, u32 * data, u32
ins->code.data[ins->code.size++] = hival;
}
- snd_printdd("dsp_spos: %d instructions reallocated\n",nreallocated);
+ dev_dbg(chip->card->dev,
+ "dsp_spos: %d instructions reallocated\n", nreallocated);
return nreallocated;
}
@@ -157,7 +150,8 @@ static int add_symbols (struct snd_cs46xx * chip, struct dsp_module_desc * modul
for (i = 0;i < module->symbol_table.nsymbols; ++i) {
if (ins->symbol_table.nsymbols == (DSP_MAX_SYMBOLS - 1)) {
- snd_printk(KERN_ERR "dsp_spos: symbol table is full\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol table is full\n");
return -ENOMEM;
}
@@ -176,8 +170,11 @@ static int add_symbols (struct snd_cs46xx * chip, struct dsp_module_desc * modul
ins->symbol_table.nsymbols++;
} else {
- /* if (0) printk ("dsp_spos: symbol <%s> duplicated, probably nothing wrong with that (Cirrus?)\n",
- module->symbol_table.symbols[i].symbol_name); */
+#if 0
+ dev_dbg(chip->card->dev,
+ "dsp_spos: symbol <%s> duplicated, probably nothing wrong with that (Cirrus?)\n",
+ module->symbol_table.symbols[i].symbol_name); */
+#endif
}
}
@@ -192,20 +189,21 @@ add_symbol (struct snd_cs46xx * chip, char * symbol_name, u32 address, int type)
int index;
if (ins->symbol_table.nsymbols == (DSP_MAX_SYMBOLS - 1)) {
- snd_printk(KERN_ERR "dsp_spos: symbol table is full\n");
+ dev_err(chip->card->dev, "dsp_spos: symbol table is full\n");
return NULL;
}
if (cs46xx_dsp_lookup_symbol(chip,
symbol_name,
type) != NULL) {
- snd_printk(KERN_ERR "dsp_spos: symbol <%s> duplicated\n", symbol_name);
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol <%s> duplicated\n", symbol_name);
return NULL;
}
index = find_free_symbol_index (ins);
- strcpy (ins->symbol_table.symbols[index].symbol_name, symbol_name);
+ strscpy (ins->symbol_table.symbols[index].symbol_name, symbol_name);
ins->symbol_table.symbols[index].address = address;
ins->symbol_table.symbols[index].symbol_type = type;
ins->symbol_table.symbols[index].module = NULL;
@@ -225,39 +223,28 @@ struct dsp_spos_instance *cs46xx_dsp_spos_create (struct snd_cs46xx * chip)
{
struct dsp_spos_instance * ins = kzalloc(sizeof(struct dsp_spos_instance), GFP_KERNEL);
- if (ins == NULL)
+ if (ins == NULL)
return NULL;
/* better to use vmalloc for this big table */
- ins->symbol_table.nsymbols = 0;
- ins->symbol_table.symbols = vmalloc(sizeof(struct dsp_symbol_entry) *
- DSP_MAX_SYMBOLS);
- ins->symbol_table.highest_frag_index = 0;
-
- if (ins->symbol_table.symbols == NULL) {
+ ins->symbol_table.symbols =
+ vmalloc(array_size(DSP_MAX_SYMBOLS,
+ sizeof(struct dsp_symbol_entry)));
+ ins->code.data = kmalloc(DSP_CODE_BYTE_SIZE, GFP_KERNEL);
+ ins->modules = kmalloc_array(DSP_MAX_MODULES,
+ sizeof(struct dsp_module_desc),
+ GFP_KERNEL);
+ if (!ins->symbol_table.symbols || !ins->code.data || !ins->modules) {
cs46xx_dsp_spos_destroy(chip);
goto error;
}
-
+ ins->symbol_table.nsymbols = 0;
+ ins->symbol_table.highest_frag_index = 0;
ins->code.offset = 0;
ins->code.size = 0;
- ins->code.data = kmalloc(DSP_CODE_BYTE_SIZE, GFP_KERNEL);
-
- if (ins->code.data == NULL) {
- cs46xx_dsp_spos_destroy(chip);
- goto error;
- }
-
ins->nscb = 0;
ins->ntask = 0;
-
ins->nmodules = 0;
- ins->modules = kmalloc(sizeof(struct dsp_module_desc) * DSP_MAX_MODULES, GFP_KERNEL);
-
- if (ins->modules == NULL) {
- cs46xx_dsp_spos_destroy(chip);
- goto error;
- }
/* default SPDIF input sample rate
to 48000 khz */
@@ -271,8 +258,8 @@ struct dsp_spos_instance *cs46xx_dsp_spos_create (struct snd_cs46xx * chip)
/* set left and right validity bits and
default channel status */
- ins->spdif_csuv_default =
- ins->spdif_csuv_stream =
+ ins->spdif_csuv_default =
+ ins->spdif_csuv_stream =
/* byte 0 */ ((unsigned int)_wrap_all_bits( (SNDRV_PCM_DEFAULT_CON_SPDIF & 0xff)) << 24) |
/* byte 1 */ ((unsigned int)_wrap_all_bits( ((SNDRV_PCM_DEFAULT_CON_SPDIF >> 8) & 0xff)) << 16) |
/* byte 3 */ (unsigned int)_wrap_all_bits( (SNDRV_PCM_DEFAULT_CON_SPDIF >> 24) & 0xff) |
@@ -281,6 +268,9 @@ struct dsp_spos_instance *cs46xx_dsp_spos_create (struct snd_cs46xx * chip)
return ins;
error:
+ kfree(ins->modules);
+ kfree(ins->code.data);
+ vfree(ins->symbol_table.symbols);
kfree(ins);
return NULL;
}
@@ -293,12 +283,12 @@ void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip)
if (snd_BUG_ON(!ins))
return;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
for (i = 0; i < ins->nscb; ++i) {
if (ins->scbs[i].deleted) continue;
cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) );
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
kfree(ins->scbs[i].data);
#endif
}
@@ -307,7 +297,6 @@ void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip)
vfree(ins->symbol_table.symbols);
kfree(ins->modules);
kfree(ins);
- mutex_unlock(&chip->spos_mutex);
}
static int dsp_load_parameter(struct snd_cs46xx *chip,
@@ -316,19 +305,20 @@ static int dsp_load_parameter(struct snd_cs46xx *chip,
u32 doffset, dsize;
if (!parameter) {
- snd_printdd("dsp_spos: module got no parameter segment\n");
+ dev_dbg(chip->card->dev,
+ "dsp_spos: module got no parameter segment\n");
return 0;
}
doffset = (parameter->offset * 4 + DSP_PARAMETER_BYTE_OFFSET);
dsize = parameter->size * 4;
- snd_printdd("dsp_spos: "
- "downloading parameter data to chip (%08x-%08x)\n",
+ dev_dbg(chip->card->dev,
+ "dsp_spos: downloading parameter data to chip (%08x-%08x)\n",
doffset,doffset + dsize);
if (snd_cs46xx_download (chip, parameter->data, doffset, dsize)) {
- snd_printk(KERN_ERR "dsp_spos: "
- "failed to download parameter data to DSP\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: failed to download parameter data to DSP\n");
return -EINVAL;
}
return 0;
@@ -340,18 +330,21 @@ static int dsp_load_sample(struct snd_cs46xx *chip,
u32 doffset, dsize;
if (!sample) {
- snd_printdd("dsp_spos: module got no sample segment\n");
+ dev_dbg(chip->card->dev,
+ "dsp_spos: module got no sample segment\n");
return 0;
}
doffset = (sample->offset * 4 + DSP_SAMPLE_BYTE_OFFSET);
dsize = sample->size * 4;
- snd_printdd("dsp_spos: downloading sample data to chip (%08x-%08x)\n",
+ dev_dbg(chip->card->dev,
+ "dsp_spos: downloading sample data to chip (%08x-%08x)\n",
doffset,doffset + dsize);
if (snd_cs46xx_download (chip,sample->data,doffset,dsize)) {
- snd_printk(KERN_ERR "dsp_spos: failed to sample data to DSP\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: failed to sample data to DSP\n");
return -EINVAL;
}
return 0;
@@ -365,14 +358,16 @@ int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * m
int err;
if (ins->nmodules == DSP_MAX_MODULES - 1) {
- snd_printk(KERN_ERR "dsp_spos: to many modules loaded into DSP\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: to many modules loaded into DSP\n");
return -ENOMEM;
}
- snd_printdd("dsp_spos: loading module %s into DSP\n", module->module_name);
+ dev_dbg(chip->card->dev,
+ "dsp_spos: loading module %s into DSP\n", module->module_name);
if (ins->nmodules == 0) {
- snd_printdd("dsp_spos: clearing parameter area\n");
+ dev_dbg(chip->card->dev, "dsp_spos: clearing parameter area\n");
snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET, DSP_PARAMETER_BYTE_SIZE);
}
@@ -382,7 +377,7 @@ int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * m
return err;
if (ins->nmodules == 0) {
- snd_printdd("dsp_spos: clearing sample area\n");
+ dev_dbg(chip->card->dev, "dsp_spos: clearing sample area\n");
snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET, DSP_SAMPLE_BYTE_SIZE);
}
@@ -392,15 +387,17 @@ int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * m
return err;
if (ins->nmodules == 0) {
- snd_printdd("dsp_spos: clearing code area\n");
+ dev_dbg(chip->card->dev, "dsp_spos: clearing code area\n");
snd_cs46xx_clear_BA1(chip, DSP_CODE_BYTE_OFFSET, DSP_CODE_BYTE_SIZE);
}
if (code == NULL) {
- snd_printdd("dsp_spos: module got no code segment\n");
+ dev_dbg(chip->card->dev,
+ "dsp_spos: module got no code segment\n");
} else {
if (ins->code.offset + code->size > DSP_CODE_BYTE_SIZE) {
- snd_printk(KERN_ERR "dsp_spos: no space available in DSP\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: no space available in DSP\n");
return -ENOMEM;
}
@@ -412,19 +409,22 @@ int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * m
if (snd_BUG_ON(!module->symbol_table.symbols))
return -ENOMEM;
if (add_symbols(chip,module)) {
- snd_printk(KERN_ERR "dsp_spos: failed to load symbol table\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: failed to load symbol table\n");
return -ENOMEM;
}
doffset = (code->offset * 4 + ins->code.offset * 4 + DSP_CODE_BYTE_OFFSET);
dsize = code->size * 4;
- snd_printdd("dsp_spos: downloading code to chip (%08x-%08x)\n",
+ dev_dbg(chip->card->dev,
+ "dsp_spos: downloading code to chip (%08x-%08x)\n",
doffset,doffset + dsize);
module->nfixups = shadow_and_reallocate_code(chip,code->data,code->size,module->overlay_begin_address);
if (snd_cs46xx_download (chip,(ins->code.data + ins->code.offset),doffset,dsize)) {
- snd_printk(KERN_ERR "dsp_spos: failed to download code to DSP\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: failed to download code to DSP\n");
return -EINVAL;
}
@@ -458,7 +458,7 @@ cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name, int symb
}
#if 0
- printk ("dsp_spos: symbol <%s> type %02x not found\n",
+ dev_err(chip->card->dev, "dsp_spos: symbol <%s> type %02x not found\n",
symbol_name,symbol_type);
#endif
@@ -466,7 +466,7 @@ cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name, int symb
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static struct dsp_symbol_entry *
cs46xx_dsp_lookup_symbol_addr (struct snd_cs46xx * chip, u32 address, int symbol_type)
{
@@ -524,7 +524,7 @@ static void cs46xx_dsp_proc_modules_read (struct snd_info_entry *entry,
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
int i,j;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
snd_iprintf(buffer, "MODULES:\n");
for ( i = 0; i < ins->nmodules; ++i ) {
snd_iprintf(buffer, "\n%s:\n", ins->modules[i].module_name);
@@ -537,7 +537,6 @@ static void cs46xx_dsp_proc_modules_read (struct snd_info_entry *entry,
desc->segment_type,desc->offset, desc->size);
}
}
- mutex_unlock(&chip->spos_mutex);
}
static void cs46xx_dsp_proc_task_tree_read (struct snd_info_entry *entry,
@@ -548,7 +547,7 @@ static void cs46xx_dsp_proc_task_tree_read (struct snd_info_entry *entry,
int i, j, col;
void __iomem *dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
snd_iprintf(buffer, "TASK TREES:\n");
for ( i = 0; i < ins->ntask; ++i) {
snd_iprintf(buffer,"\n%04x %s:\n",ins->tasks[i].address,ins->tasks[i].task_name);
@@ -565,7 +564,6 @@ static void cs46xx_dsp_proc_task_tree_read (struct snd_info_entry *entry,
}
snd_iprintf(buffer,"\n");
- mutex_unlock(&chip->spos_mutex);
}
static void cs46xx_dsp_proc_scb_read (struct snd_info_entry *entry,
@@ -575,7 +573,7 @@ static void cs46xx_dsp_proc_scb_read (struct snd_info_entry *entry,
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
int i;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
snd_iprintf(buffer, "SCB's:\n");
for ( i = 0; i < ins->nscb; ++i) {
if (ins->scbs[i].deleted)
@@ -598,7 +596,6 @@ static void cs46xx_dsp_proc_scb_read (struct snd_info_entry *entry,
}
snd_iprintf(buffer,"\n");
- mutex_unlock(&chip->spos_mutex);
}
static void cs46xx_dsp_proc_parameter_dump_read (struct snd_info_entry *entry,
@@ -616,7 +613,8 @@ static void cs46xx_dsp_proc_parameter_dump_read (struct snd_info_entry *entry,
col = 0;
}
- if ( (symbol = cs46xx_dsp_lookup_symbol_addr (chip,i / sizeof(u32), SYMBOL_PARAMETER)) != NULL) {
+ symbol = cs46xx_dsp_lookup_symbol_addr(chip, i / sizeof(u32), SYMBOL_PARAMETER);
+ if (symbol) {
col = 0;
snd_iprintf (buffer,"\n%s:\n",symbol->symbol_name);
}
@@ -785,101 +783,57 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip)
ins->snd_card = card;
- if ((entry = snd_info_create_card_entry(card, "dsp", card->proc_root)) != NULL) {
- entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
-
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
-
+ entry = snd_info_create_card_entry(card, "dsp", card->proc_root);
+ if (entry)
+ entry->mode = S_IFDIR | 0555;
ins->proc_dsp_dir = entry;
if (!ins->proc_dsp_dir)
return -ENOMEM;
- if ((entry = snd_info_create_card_entry(card, "spos_symbols", ins->proc_dsp_dir)) != NULL) {
- entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->private_data = chip;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read = cs46xx_dsp_proc_symbol_table_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- ins->proc_sym_info_entry = entry;
+ entry = snd_info_create_card_entry(card, "spos_symbols",
+ ins->proc_dsp_dir);
+ if (entry)
+ snd_info_set_text_ops(entry, chip,
+ cs46xx_dsp_proc_symbol_table_read);
- if ((entry = snd_info_create_card_entry(card, "spos_modules", ins->proc_dsp_dir)) != NULL) {
- entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->private_data = chip;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read = cs46xx_dsp_proc_modules_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- ins->proc_modules_info_entry = entry;
-
- if ((entry = snd_info_create_card_entry(card, "parameter", ins->proc_dsp_dir)) != NULL) {
- entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->private_data = chip;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read = cs46xx_dsp_proc_parameter_dump_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- ins->proc_parameter_dump_info_entry = entry;
-
- if ((entry = snd_info_create_card_entry(card, "sample", ins->proc_dsp_dir)) != NULL) {
- entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->private_data = chip;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read = cs46xx_dsp_proc_sample_dump_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- ins->proc_sample_dump_info_entry = entry;
-
- if ((entry = snd_info_create_card_entry(card, "task_tree", ins->proc_dsp_dir)) != NULL) {
- entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->private_data = chip;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read = cs46xx_dsp_proc_task_tree_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- ins->proc_task_info_entry = entry;
-
- if ((entry = snd_info_create_card_entry(card, "scb_info", ins->proc_dsp_dir)) != NULL) {
- entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->private_data = chip;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
- entry->c.text.read = cs46xx_dsp_proc_scb_read;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- entry = NULL;
- }
- }
- ins->proc_scb_info_entry = entry;
-
- mutex_lock(&chip->spos_mutex);
+ entry = snd_info_create_card_entry(card, "spos_modules",
+ ins->proc_dsp_dir);
+ if (entry)
+ snd_info_set_text_ops(entry, chip,
+ cs46xx_dsp_proc_modules_read);
+
+ entry = snd_info_create_card_entry(card, "parameter",
+ ins->proc_dsp_dir);
+ if (entry)
+ snd_info_set_text_ops(entry, chip,
+ cs46xx_dsp_proc_parameter_dump_read);
+
+ entry = snd_info_create_card_entry(card, "sample",
+ ins->proc_dsp_dir);
+ if (entry)
+ snd_info_set_text_ops(entry, chip,
+ cs46xx_dsp_proc_sample_dump_read);
+
+ entry = snd_info_create_card_entry(card, "task_tree",
+ ins->proc_dsp_dir);
+ if (entry)
+ snd_info_set_text_ops(entry, chip,
+ cs46xx_dsp_proc_task_tree_read);
+
+ entry = snd_info_create_card_entry(card, "scb_info",
+ ins->proc_dsp_dir);
+ if (entry)
+ snd_info_set_text_ops(entry, chip,
+ cs46xx_dsp_proc_scb_read);
+
+ guard(mutex)(&chip->spos_mutex);
/* register/update SCB's entries on proc */
for (i = 0; i < ins->nscb; ++i) {
if (ins->scbs[i].deleted) continue;
cs46xx_dsp_proc_register_scb_desc (chip, (ins->scbs + i));
}
- mutex_unlock(&chip->spos_mutex);
return 0;
}
@@ -889,39 +843,24 @@ int cs46xx_dsp_proc_done (struct snd_cs46xx *chip)
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
int i;
- snd_info_free_entry(ins->proc_sym_info_entry);
- ins->proc_sym_info_entry = NULL;
-
- snd_info_free_entry(ins->proc_modules_info_entry);
- ins->proc_modules_info_entry = NULL;
-
- snd_info_free_entry(ins->proc_parameter_dump_info_entry);
- ins->proc_parameter_dump_info_entry = NULL;
-
- snd_info_free_entry(ins->proc_sample_dump_info_entry);
- ins->proc_sample_dump_info_entry = NULL;
-
- snd_info_free_entry(ins->proc_scb_info_entry);
- ins->proc_scb_info_entry = NULL;
-
- snd_info_free_entry(ins->proc_task_info_entry);
- ins->proc_task_info_entry = NULL;
+ if (!ins)
+ return 0;
- mutex_lock(&chip->spos_mutex);
- for (i = 0; i < ins->nscb; ++i) {
- if (ins->scbs[i].deleted) continue;
- cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) );
+ scoped_guard(mutex, &chip->spos_mutex) {
+ for (i = 0; i < ins->nscb; ++i) {
+ if (ins->scbs[i].deleted)
+ continue;
+ cs46xx_dsp_proc_free_scb_desc((ins->scbs + i));
+ }
}
- mutex_unlock(&chip->spos_mutex);
snd_info_free_entry(ins->proc_dsp_dir);
ins->proc_dsp_dir = NULL;
return 0;
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
-static int debug_tree;
static void _dsp_create_task_tree (struct snd_cs46xx *chip, u32 * task_data,
u32 dest, int size)
{
@@ -930,13 +869,13 @@ static void _dsp_create_task_tree (struct snd_cs46xx *chip, u32 * task_data,
int i;
for (i = 0; i < size; ++i) {
- if (debug_tree) printk ("addr %p, val %08x\n",spdst,task_data[i]);
+ dev_dbg(chip->card->dev, "addr %p, val %08x\n",
+ spdst, task_data[i]);
writel(task_data[i],spdst);
spdst += sizeof(u32);
}
}
-static int debug_scb;
static void _dsp_create_scb (struct snd_cs46xx *chip, u32 * scb_data, u32 dest)
{
void __iomem *spdst = chip->region.idx[1].remap_addr +
@@ -944,7 +883,8 @@ static void _dsp_create_scb (struct snd_cs46xx *chip, u32 * scb_data, u32 dest)
int i;
for (i = 0; i < 0x10; ++i) {
- if (debug_scb) printk ("addr %p, val %08x\n",spdst,scb_data[i]);
+ dev_dbg(chip->card->dev, "addr %p, val %08x\n",
+ spdst, scb_data[i]);
writel(scb_data[i],spdst);
spdst += sizeof(u32);
}
@@ -971,14 +911,15 @@ static struct dsp_scb_descriptor * _map_scb (struct snd_cs46xx *chip, char * nam
int index;
if (ins->nscb == DSP_MAX_SCB_DESC - 1) {
- snd_printk(KERN_ERR "dsp_spos: got no place for other SCB\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: got no place for other SCB\n");
return NULL;
}
index = find_free_scb_index (ins);
memset(&ins->scbs[index], 0, sizeof(ins->scbs[index]));
- strcpy(ins->scbs[index].scb_name, name);
+ strscpy(ins->scbs[index].scb_name, name);
ins->scbs[index].address = dest;
ins->scbs[index].index = index;
ins->scbs[index].ref_count = 1;
@@ -1002,14 +943,15 @@ _map_task_tree (struct snd_cs46xx *chip, char * name, u32 dest, u32 size)
struct dsp_task_descriptor * desc = NULL;
if (ins->ntask == DSP_MAX_TASK_DESC - 1) {
- snd_printk(KERN_ERR "dsp_spos: got no place for other TASK\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: got no place for other TASK\n");
return NULL;
}
if (name)
- strcpy(ins->tasks[ins->ntask].task_name, name);
+ strscpy(ins->tasks[ins->ntask].task_name, name);
else
- strcpy(ins->tasks[ins->ntask].task_name, "(NULL)");
+ strscpy(ins->tasks[ins->ntask].task_name, "(NULL)");
ins->tasks[ins->ntask].address = dest;
ins->tasks[ins->ntask].size = size;
@@ -1030,7 +972,7 @@ cs46xx_dsp_create_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32
{
struct dsp_scb_descriptor * desc;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/* copy the data for resume */
scb_data = kmemdup(scb_data, SCB_BYTES, GFP_KERNEL);
if (!scb_data)
@@ -1042,8 +984,8 @@ cs46xx_dsp_create_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32
desc->data = scb_data;
_dsp_create_scb(chip,scb_data,dest);
} else {
- snd_printk(KERN_ERR "dsp_spos: failed to map SCB\n");
-#ifdef CONFIG_PM
+ dev_err(chip->card->dev, "dsp_spos: failed to map SCB\n");
+#ifdef CONFIG_PM_SLEEP
kfree(scb_data);
#endif
}
@@ -1063,7 +1005,7 @@ cs46xx_dsp_create_task_tree (struct snd_cs46xx *chip, char * name, u32 * task_da
desc->data = task_data;
_dsp_create_task_tree(chip,task_data,dest,size);
} else {
- snd_printk(KERN_ERR "dsp_spos: failed to map TASK\n");
+ dev_err(chip->card->dev, "dsp_spos: failed to map TASK\n");
}
return desc;
@@ -1093,7 +1035,7 @@ int cs46xx_dsp_scb_and_task_init (struct snd_cs46xx *chip)
int fifo_addr, fifo_span, valid_slots;
- static struct dsp_spos_control_block sposcb = {
+ static const struct dsp_spos_control_block sposcb = {
/* 0 */ HFG_TREE_SCB,HFG_STACK,
/* 1 */ SPOSCB_ADDR,BG_TREE_SCB_ADDR,
/* 2 */ DSP_SPOS_DC,0,
@@ -1116,31 +1058,36 @@ int cs46xx_dsp_scb_and_task_init (struct snd_cs46xx *chip)
null_algorithm = cs46xx_dsp_lookup_symbol(chip, "NULLALGORITHM", SYMBOL_CODE);
if (null_algorithm == NULL) {
- snd_printk(KERN_ERR "dsp_spos: symbol NULLALGORITHM not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol NULLALGORITHM not found\n");
return -EIO;
}
fg_task_tree_header_code = cs46xx_dsp_lookup_symbol(chip, "FGTASKTREEHEADERCODE", SYMBOL_CODE);
if (fg_task_tree_header_code == NULL) {
- snd_printk(KERN_ERR "dsp_spos: symbol FGTASKTREEHEADERCODE not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol FGTASKTREEHEADERCODE not found\n");
return -EIO;
}
task_tree_header_code = cs46xx_dsp_lookup_symbol(chip, "TASKTREEHEADERCODE", SYMBOL_CODE);
if (task_tree_header_code == NULL) {
- snd_printk(KERN_ERR "dsp_spos: symbol TASKTREEHEADERCODE not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol TASKTREEHEADERCODE not found\n");
return -EIO;
}
task_tree_thread = cs46xx_dsp_lookup_symbol(chip, "TASKTREETHREAD", SYMBOL_CODE);
if (task_tree_thread == NULL) {
- snd_printk(KERN_ERR "dsp_spos: symbol TASKTREETHREAD not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol TASKTREETHREAD not found\n");
return -EIO;
}
magic_snoop_task = cs46xx_dsp_lookup_symbol(chip, "MAGICSNOOPTASK", SYMBOL_CODE);
if (magic_snoop_task == NULL) {
- snd_printk(KERN_ERR "dsp_spos: symbol MAGICSNOOPTASK not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol MAGICSNOOPTASK not found\n");
return -EIO;
}
@@ -1424,7 +1371,7 @@ int cs46xx_dsp_scb_and_task_init (struct snd_cs46xx *chip)
if (chip->nr_ac97_codecs == 2) {
/* create CODEC tasklet for rear Center/LFE output
- slot 6 and 9 on seconadry CODEC */
+ slot 6 and 9 on secondary CODEC */
clfe_codec_out_scb = cs46xx_dsp_create_codec_out_scb(chip,"CodecOutSCB_CLFE",0x0030,0x0030,
CLFE_MIXER_SCB_ADDR,
CLFE_CODEC_SCB_ADDR,
@@ -1487,7 +1434,7 @@ int cs46xx_dsp_scb_and_task_init (struct snd_cs46xx *chip)
return 0;
_fail_end:
- snd_printk(KERN_ERR "dsp_spos: failed to setup SCB's in DSP\n");
+ dev_err(chip->card->dev, "dsp_spos: failed to setup SCB's in DSP\n");
return -EINVAL;
}
@@ -1502,18 +1449,21 @@ static int cs46xx_dsp_async_init (struct snd_cs46xx *chip,
s16_async_codec_input_task = cs46xx_dsp_lookup_symbol(chip, "S16_ASYNCCODECINPUTTASK", SYMBOL_CODE);
if (s16_async_codec_input_task == NULL) {
- snd_printk(KERN_ERR "dsp_spos: symbol S16_ASYNCCODECINPUTTASK not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol S16_ASYNCCODECINPUTTASK not found\n");
return -EIO;
}
spdifo_task = cs46xx_dsp_lookup_symbol(chip, "SPDIFOTASK", SYMBOL_CODE);
if (spdifo_task == NULL) {
- snd_printk(KERN_ERR "dsp_spos: symbol SPDIFOTASK not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol SPDIFOTASK not found\n");
return -EIO;
}
spdifi_task = cs46xx_dsp_lookup_symbol(chip, "SPDIFITASK", SYMBOL_CODE);
if (spdifi_task == NULL) {
- snd_printk(KERN_ERR "dsp_spos: symbol SPDIFITASK not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol SPDIFITASK not found\n");
return -EIO;
}
@@ -1723,7 +1673,7 @@ int cs46xx_dsp_enable_spdif_in (struct snd_cs46xx *chip)
if (snd_BUG_ON(!ins->spdif_in_src))
return -EINVAL;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
if ( ! (ins->spdif_status_out & DSP_SPDIF_STATUS_INPUT_CTRL_ENABLED) ) {
/* time countdown enable */
@@ -1746,7 +1696,7 @@ int cs46xx_dsp_enable_spdif_in (struct snd_cs46xx *chip)
ins->spdif_in_src,
SCB_ON_PARENT_SUBLIST_SCB);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
/* reset SPDIF input sample buffer pointer */
/*snd_cs46xx_poke (chip, (SPDIFI_SCB_INST + 0x0c) << 2,
@@ -1759,15 +1709,12 @@ int cs46xx_dsp_enable_spdif_in (struct snd_cs46xx *chip)
/* unmute SRC volume */
cs46xx_dsp_scb_set_volume (chip,ins->spdif_in_src,0x7fff,0x7fff);
- spin_unlock_irq(&chip->reg_lock);
-
/* set SPDIF input sample rate and unmute
NOTE: only 48khz support for SPDIF input this time */
/* cs46xx_dsp_set_src_sample_rate(chip,ins->spdif_in_src,48000); */
/* monitor state */
ins->spdif_status_in = 1;
- mutex_unlock(&chip->spos_mutex);
return 0;
}
@@ -1781,17 +1728,16 @@ int cs46xx_dsp_disable_spdif_in (struct snd_cs46xx *chip)
if (snd_BUG_ON(!ins->spdif_in_src))
return -EINVAL;
- mutex_lock(&chip->spos_mutex);
+ scoped_guard(mutex, &chip->spos_mutex) {
+ /* Remove the asynchronous receiver SCB */
+ cs46xx_dsp_remove_scb(chip, ins->asynch_rx_scb);
+ ins->asynch_rx_scb = NULL;
- /* Remove the asynchronous receiver SCB */
- cs46xx_dsp_remove_scb (chip,ins->asynch_rx_scb);
- ins->asynch_rx_scb = NULL;
+ cs46xx_src_unlink(chip, ins->spdif_in_src);
- cs46xx_src_unlink(chip,ins->spdif_in_src);
-
- /* monitor state */
- ins->spdif_status_in = 0;
- mutex_unlock(&chip->spos_mutex);
+ /* monitor state */
+ ins->spdif_status_in = 0;
+ }
/* restore amplifier */
chip->active_ctrl(chip, -1);
@@ -1809,10 +1755,9 @@ int cs46xx_dsp_enable_pcm_capture (struct snd_cs46xx *chip)
if (snd_BUG_ON(!ins->ref_snoop_scb))
return -EINVAL;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
ins->pcm_input = cs46xx_add_record_source(chip,ins->ref_snoop_scb,PCMSERIALIN_PCM_SCB_ADDR,
"PCMSerialInput_Wave");
- mutex_unlock(&chip->spos_mutex);
return 0;
}
@@ -1824,10 +1769,9 @@ int cs46xx_dsp_disable_pcm_capture (struct snd_cs46xx *chip)
if (snd_BUG_ON(!ins->pcm_input))
return -EINVAL;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
cs46xx_dsp_remove_scb (chip,ins->pcm_input);
ins->pcm_input = NULL;
- mutex_unlock(&chip->spos_mutex);
return 0;
}
@@ -1841,10 +1785,9 @@ int cs46xx_dsp_enable_adc_capture (struct snd_cs46xx *chip)
if (snd_BUG_ON(!ins->codec_in_scb))
return -EINVAL;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
ins->adc_input = cs46xx_add_record_source(chip,ins->codec_in_scb,PCMSERIALIN_SCB_ADDR,
"PCMSerialInput_ADC");
- mutex_unlock(&chip->spos_mutex);
return 0;
}
@@ -1856,10 +1799,9 @@ int cs46xx_dsp_disable_adc_capture (struct snd_cs46xx *chip)
if (snd_BUG_ON(!ins->adc_input))
return -EINVAL;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
cs46xx_dsp_remove_scb (chip,ins->adc_input);
ins->adc_input = NULL;
- mutex_unlock(&chip->spos_mutex);
return 0;
}
@@ -1894,7 +1836,8 @@ int cs46xx_poke_via_dsp (struct snd_cs46xx *chip, u32 address, u32 data)
}
if (i == 25) {
- snd_printk(KERN_ERR "dsp_spos: SPIOWriteTask not responding\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: SPIOWriteTask not responding\n");
return -EBUSY;
}
@@ -1906,7 +1849,7 @@ int cs46xx_dsp_set_dac_volume (struct snd_cs46xx * chip, u16 left, u16 right)
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
struct dsp_scb_descriptor * scb;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
/* main output */
scb = ins->master_mix_scb->sub_list_ptr;
@@ -1925,8 +1868,6 @@ int cs46xx_dsp_set_dac_volume (struct snd_cs46xx * chip, u16 left, u16 right)
ins->dac_volume_left = left;
ins->dac_volume_right = right;
- mutex_unlock(&chip->spos_mutex);
-
return 0;
}
@@ -1934,7 +1875,7 @@ int cs46xx_dsp_set_iec958_volume (struct snd_cs46xx * chip, u16 left, u16 right)
{
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
if (ins->asynch_rx_scb != NULL)
cs46xx_dsp_scb_set_volume (chip,ins->asynch_rx_scb,
@@ -1943,12 +1884,10 @@ int cs46xx_dsp_set_iec958_volume (struct snd_cs46xx * chip, u16 left, u16 right)
ins->spdif_input_volume_left = left;
ins->spdif_input_volume_right = right;
- mutex_unlock(&chip->spos_mutex);
-
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int cs46xx_dsp_resume(struct snd_cs46xx * chip)
{
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
diff --git a/sound/pci/cs46xx/dsp_spos.h b/sound/pci/cs46xx/dsp_spos.h
index ca47a8114c7f..a4853c748efe 100644
--- a/sound/pci/cs46xx/dsp_spos.h
+++ b/sound/pci/cs46xx/dsp_spos.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c
index 00b148a10239..32ed415bf427 100644
--- a/sound/pci/cs46xx/dsp_spos_scb_lib.c
+++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c
@@ -1,19 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -21,7 +7,7 @@
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/init.h>
@@ -31,7 +17,7 @@
#include <sound/core.h>
#include <sound/control.h>
#include <sound/info.h>
-#include <sound/cs46xx.h>
+#include "cs46xx.h"
#include "cs46xx_lib.h"
#include "dsp_spos.h"
@@ -67,20 +53,17 @@ static void remove_symbol (struct snd_cs46xx * chip, struct dsp_symbol_entry * s
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
static void cs46xx_dsp_proc_scb_info_read (struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct proc_scb_info * scb_info = entry->private_data;
struct dsp_scb_descriptor * scb = scb_info->scb_desc;
- struct dsp_spos_instance * ins;
struct snd_cs46xx *chip = scb_info->chip;
int j,col;
void __iomem *dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET;
- ins = chip->dsp_spos_instance;
-
- mutex_lock(&chip->spos_mutex);
+ guard(mutex)(&chip->spos_mutex);
snd_iprintf(buffer,"%04x %s:\n",scb->address,scb->scb_name);
for (col = 0,j = 0;j < 0x10; j++,col++) {
@@ -108,7 +91,6 @@ static void cs46xx_dsp_proc_scb_info_read (struct snd_info_entry *entry,
scb->task_entry->address);
snd_iprintf(buffer,"index [%d] ref_count [%d]\n",scb->index,scb->ref_count);
- mutex_unlock(&chip->spos_mutex);
}
#endif
@@ -177,7 +159,6 @@ static void _dsp_clear_sample_buffer (struct snd_cs46xx *chip, u32 sample_buffer
void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor * scb)
{
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
- unsigned long flags;
/* check integrety */
if (snd_BUG_ON(scb->index < 0 ||
@@ -193,9 +174,9 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor *
goto _end;
#endif
- spin_lock_irqsave(&chip->reg_lock, flags);
- _dsp_unlink_scb (chip,scb);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ _dsp_unlink_scb(chip, scb);
+ }
cs46xx_dsp_proc_free_scb_desc(scb);
if (snd_BUG_ON(!scb->scb_symbol))
@@ -203,7 +184,7 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor *
remove_symbol (chip,scb->scb_symbol);
ins->scbs[scb->index].deleted = 1;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
kfree(ins->scbs[scb->index].data);
ins->scbs[scb->index].data = NULL;
#endif
@@ -218,23 +199,19 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor *
if (ins->scb_highest_frag_index > ins->nscb) {
ins->scb_highest_frag_index = ins->nscb;
}
-
-#if 0
- /* !!!! THIS IS A PIECE OF SHIT MADE BY ME !!! */
- for(i = scb->index + 1;i < ins->nscb; ++i) {
- ins->scbs[i - 1].index = i - 1;
- }
-#endif
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
void cs46xx_dsp_proc_free_scb_desc (struct dsp_scb_descriptor * scb)
{
if (scb->proc_info) {
struct proc_scb_info * scb_info = scb->proc_info->private_data;
+ struct snd_cs46xx *chip = scb_info->chip;
- snd_printdd("cs46xx_dsp_proc_free_scb_desc: freeing %s\n",scb->scb_name);
+ dev_dbg(chip->card->dev,
+ "cs46xx_dsp_proc_free_scb_desc: freeing %s\n",
+ scb->scb_name);
snd_info_free_entry(scb->proc_info);
scb->proc_info = NULL;
@@ -254,8 +231,9 @@ void cs46xx_dsp_proc_register_scb_desc (struct snd_cs46xx *chip,
if (ins->snd_card != NULL && ins->proc_dsp_dir != NULL &&
scb->proc_info == NULL) {
- if ((entry = snd_info_create_card_entry(ins->snd_card, scb->scb_name,
- ins->proc_dsp_dir)) != NULL) {
+ entry = snd_info_create_card_entry(ins->snd_card, scb->scb_name,
+ ins->proc_dsp_dir);
+ if (entry) {
scb_info = kmalloc(sizeof(struct proc_scb_info), GFP_KERNEL);
if (!scb_info) {
snd_info_free_entry(entry);
@@ -265,24 +243,14 @@ void cs46xx_dsp_proc_register_scb_desc (struct snd_cs46xx *chip,
scb_info->chip = chip;
scb_info->scb_desc = scb;
-
- entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->private_data = scb_info;
- entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
-
- entry->c.text.read = cs46xx_dsp_proc_scb_info_read;
-
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- kfree (scb_info);
- entry = NULL;
- }
+ snd_info_set_text_ops(entry, scb_info,
+ cs46xx_dsp_proc_scb_info_read);
}
out:
scb->proc_info = entry;
}
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_SND_PROC_FS */
static struct dsp_scb_descriptor *
_dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32 dest,
@@ -293,8 +261,6 @@ _dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
struct dsp_scb_descriptor * scb;
- unsigned long flags;
-
if (snd_BUG_ON(!ins->the_null_scb))
return NULL;
@@ -305,7 +271,7 @@ _dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u
scb_data[SCBfuncEntryPtr] &= 0xFFFF0000;
scb_data[SCBfuncEntryPtr] |= task_entry->address;
- snd_printdd("dsp_spos: creating SCB <%s>\n",name);
+ dev_dbg(chip->card->dev, "dsp_spos: creating SCB <%s>\n", name);
scb = cs46xx_dsp_create_scb(chip,name,scb_data,dest);
@@ -320,9 +286,15 @@ _dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u
/* update parent SCB */
if (scb->parent_scb_ptr) {
#if 0
- printk ("scb->parent_scb_ptr = %s\n",scb->parent_scb_ptr->scb_name);
- printk ("scb->parent_scb_ptr->next_scb_ptr = %s\n",scb->parent_scb_ptr->next_scb_ptr->scb_name);
- printk ("scb->parent_scb_ptr->sub_list_ptr = %s\n",scb->parent_scb_ptr->sub_list_ptr->scb_name);
+ dev_dbg(chip->card->dev,
+ "scb->parent_scb_ptr = %s\n",
+ scb->parent_scb_ptr->scb_name);
+ dev_dbg(chip->card->dev,
+ "scb->parent_scb_ptr->next_scb_ptr = %s\n",
+ scb->parent_scb_ptr->next_scb_ptr->scb_name);
+ dev_dbg(chip->card->dev,
+ "scb->parent_scb_ptr->sub_list_ptr = %s\n",
+ scb->parent_scb_ptr->sub_list_ptr->scb_name);
#endif
/* link to parent SCB */
if (scb_child_type == SCB_ON_PARENT_NEXT_SCB) {
@@ -342,12 +314,10 @@ _dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u
snd_BUG();
}
- spin_lock_irqsave(&chip->reg_lock, flags);
-
- /* update entry in DSP RAM */
- cs46xx_dsp_spos_update_scb(chip,scb->parent_scb_ptr);
-
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ /* update entry in DSP RAM */
+ cs46xx_dsp_spos_update_scb(chip, scb->parent_scb_ptr);
+ }
}
@@ -368,7 +338,8 @@ cs46xx_dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_d
SYMBOL_CODE);
if (task_entry == NULL) {
- snd_printk (KERN_ERR "dsp_spos: symbol %s not found\n",task_entry_name);
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol %s not found\n", task_entry_name);
return NULL;
}
@@ -582,7 +553,8 @@ cs46xx_dsp_create_pcm_reader_scb(struct snd_cs46xx * chip, char * scb_name,
SYMBOL_CODE);
if (ins->null_algorithm == NULL) {
- snd_printk (KERN_ERR "dsp_spos: symbol NULLALGORITHM not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol NULLALGORITHM not found\n");
return NULL;
}
}
@@ -612,7 +584,8 @@ cs46xx_dsp_create_src_task_scb(struct snd_cs46xx * chip, char * scb_name,
unsigned int phiIncr;
unsigned int correctionPerGOF, correctionPerSec;
- snd_printdd( "dsp_spos: setting %s rate to %u\n",scb_name,rate);
+ dev_dbg(chip->card->dev, "dsp_spos: setting %s rate to %u\n",
+ scb_name, rate);
/*
* Compute the values used to drive the actual sample rate conversion.
@@ -670,7 +643,8 @@ cs46xx_dsp_create_src_task_scb(struct snd_cs46xx * chip, char * scb_name,
SYMBOL_CODE);
if (ins->s16_up == NULL) {
- snd_printk (KERN_ERR "dsp_spos: symbol S16_UPSRC not found\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: symbol S16_UPSRC not found\n");
return NULL;
}
}
@@ -1158,7 +1132,7 @@ find_next_free_scb (struct snd_cs46xx * chip, struct dsp_scb_descriptor * from)
return scb;
}
-static u32 pcm_reader_buffer_addr[DSP_MAX_PCM_CHANNELS] = {
+static const u32 pcm_reader_buffer_addr[DSP_MAX_PCM_CHANNELS] = {
0x0600, /* 1 */
0x1500, /* 2 */
0x1580, /* 3 */
@@ -1193,7 +1167,7 @@ static u32 pcm_reader_buffer_addr[DSP_MAX_PCM_CHANNELS] = {
0x2400, /* 32 */
};
-static u32 src_output_buffer_addr[DSP_MAX_SRC_NR] = {
+static const u32 src_output_buffer_addr[DSP_MAX_SRC_NR] = {
0x2B80,
0x2BA0,
0x2BC0,
@@ -1210,7 +1184,7 @@ static u32 src_output_buffer_addr[DSP_MAX_SRC_NR] = {
0x2E20
};
-static u32 src_delay_buffer_addr[DSP_MAX_SRC_NR] = {
+static const u32 src_delay_buffer_addr[DSP_MAX_SRC_NR] = {
0x2480,
0x2500,
0x2580,
@@ -1240,7 +1214,6 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
/* struct dsp_scb_descriptor * pcm_parent_scb; */
char scb_name[DSP_MAX_SCB_NAME];
int i, pcm_index = -1, insert_point, src_index = -1, pass_through = 0;
- unsigned long flags;
switch (pcm_channel_id) {
case DSP_PCM_MAIN_CHANNEL:
@@ -1265,7 +1238,7 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
the Sample Rate Converted (which could
alter the raw data stream ...) */
if (sample_rate == 48000) {
- snd_printdd ("IEC958 pass through\n");
+ dev_dbg(chip->card->dev, "IEC958 pass through\n");
/* Hack to bypass creating a new SRC */
pass_through = 1;
}
@@ -1299,13 +1272,14 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
}
if (pcm_index == -1) {
- snd_printk (KERN_ERR "dsp_spos: no free PCM channel\n");
+ dev_err(chip->card->dev, "dsp_spos: no free PCM channel\n");
return NULL;
}
if (src_scb == NULL) {
if (ins->nsrc_scb >= DSP_MAX_SRC_NR) {
- snd_printk(KERN_ERR "dsp_spos: to many SRC instances\n!");
+ dev_err(chip->card->dev,
+ "dsp_spos: too many SRC instances\n!");
return NULL;
}
@@ -1331,7 +1305,8 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
snprintf (scb_name,DSP_MAX_SCB_NAME,"SrcTask_SCB%d",src_index);
- snd_printdd( "dsp_spos: creating SRC \"%s\"\n",scb_name);
+ dev_dbg(chip->card->dev,
+ "dsp_spos: creating SRC \"%s\"\n", scb_name);
src_scb = cs46xx_dsp_create_src_task_scb(chip,scb_name,
sample_rate,
src_output_buffer_addr[src_index],
@@ -1343,7 +1318,8 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
pass_through);
if (!src_scb) {
- snd_printk (KERN_ERR "dsp_spos: failed to create SRCtaskSCB\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: failed to create SRCtaskSCB\n");
return NULL;
}
@@ -1355,8 +1331,8 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
snprintf (scb_name,DSP_MAX_SCB_NAME,"PCMReader_SCB%d",pcm_index);
- snd_printdd( "dsp_spos: creating PCM \"%s\" (%d)\n",scb_name,
- pcm_channel_id);
+ dev_dbg(chip->card->dev, "dsp_spos: creating PCM \"%s\" (%d)\n",
+ scb_name, pcm_channel_id);
pcm_scb = cs46xx_dsp_create_pcm_reader_scb(chip,scb_name,
pcm_reader_buffer_addr[pcm_index],
@@ -1369,11 +1345,12 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
);
if (!pcm_scb) {
- snd_printk (KERN_ERR "dsp_spos: failed to create PCMreaderSCB\n");
+ dev_err(chip->card->dev,
+ "dsp_spos: failed to create PCMreaderSCB\n");
return NULL;
}
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
ins->pcm_channels[pcm_index].sample_rate = sample_rate;
ins->pcm_channels[pcm_index].pcm_reader_scb = pcm_scb;
ins->pcm_channels[pcm_index].src_scb = src_scb;
@@ -1384,7 +1361,6 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,
ins->pcm_channels[pcm_index].pcm_slot = pcm_index;
ins->pcm_channels[pcm_index].mixer_scb = mixer_scb;
ins->npcm_channels ++;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return (ins->pcm_channels + pcm_index);
}
@@ -1419,7 +1395,8 @@ int cs46xx_dsp_pcm_channel_set_period (struct snd_cs46xx * chip,
temp |= DMA_RQ_C1_SOURCE_MOD16;
break;
default:
- snd_printdd ("period size (%d) not supported by HW\n", period_size);
+ dev_dbg(chip->card->dev,
+ "period size (%d) not supported by HW\n", period_size);
return -EINVAL;
}
@@ -1457,7 +1434,8 @@ int cs46xx_dsp_pcm_ostream_set_period (struct snd_cs46xx * chip,
temp |= DMA_RQ_C1_DEST_MOD16;
break;
default:
- snd_printdd ("period size (%d) not supported by HW\n", period_size);
+ dev_dbg(chip->card->dev,
+ "period size (%d) not supported by HW\n", period_size);
return -EINVAL;
}
@@ -1470,20 +1448,19 @@ void cs46xx_dsp_destroy_pcm_channel (struct snd_cs46xx * chip,
struct dsp_pcm_channel_descriptor * pcm_channel)
{
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
- unsigned long flags;
if (snd_BUG_ON(!pcm_channel->active ||
ins->npcm_channels <= 0 ||
pcm_channel->src_scb->ref_count <= 0))
return;
- spin_lock_irqsave(&chip->reg_lock, flags);
- pcm_channel->unlinked = 1;
- pcm_channel->active = 0;
- pcm_channel->private_data = NULL;
- pcm_channel->src_scb->ref_count --;
- ins->npcm_channels --;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ pcm_channel->unlinked = 1;
+ pcm_channel->active = 0;
+ pcm_channel->private_data = NULL;
+ pcm_channel->src_scb->ref_count--;
+ ins->npcm_channels--;
+ }
cs46xx_dsp_remove_scb(chip,pcm_channel->pcm_reader_scb);
@@ -1502,22 +1479,17 @@ void cs46xx_dsp_destroy_pcm_channel (struct snd_cs46xx * chip,
int cs46xx_dsp_pcm_unlink (struct snd_cs46xx * chip,
struct dsp_pcm_channel_descriptor * pcm_channel)
{
- unsigned long flags;
-
if (snd_BUG_ON(!pcm_channel->active ||
chip->dsp_spos_instance->npcm_channels <= 0))
return -EIO;
- spin_lock_irqsave(&chip->reg_lock, flags);
- if (pcm_channel->unlinked) {
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
+ if (pcm_channel->unlinked)
return -EIO;
- }
pcm_channel->unlinked = 1;
_dsp_unlink_scb (chip,pcm_channel->pcm_reader_scb);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -1528,14 +1500,11 @@ int cs46xx_dsp_pcm_link (struct snd_cs46xx * chip,
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
struct dsp_scb_descriptor * parent_scb;
struct dsp_scb_descriptor * src_scb = pcm_channel->src_scb;
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
- if (pcm_channel->unlinked == 0) {
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ if (pcm_channel->unlinked == 0)
return -EIO;
- }
parent_scb = src_scb;
@@ -1556,7 +1525,6 @@ int cs46xx_dsp_pcm_link (struct snd_cs46xx * chip,
cs46xx_dsp_spos_update_scb(chip,parent_scb);
pcm_channel->unlinked = 0;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -1589,17 +1557,14 @@ cs46xx_add_record_source (struct snd_cs46xx *chip, struct dsp_scb_descriptor * s
int cs46xx_src_unlink(struct snd_cs46xx *chip, struct dsp_scb_descriptor * src)
{
- unsigned long flags;
-
if (snd_BUG_ON(!src->parent_scb_ptr))
return -EINVAL;
/* mute SCB */
cs46xx_dsp_scb_set_volume (chip,src,0,0);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
_dsp_unlink_scb (chip,src);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0;
}
@@ -1723,7 +1688,7 @@ int cs46xx_iec958_pre_open (struct snd_cs46xx *chip)
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
if ( ins->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED ) {
- /* remove AsynchFGTxSCB and and PCMSerialInput_II */
+ /* remove AsynchFGTxSCB and PCMSerialInput_II */
cs46xx_dsp_disable_spdif_out (chip);
/* save state */
diff --git a/sound/pci/cs46xx/imgs/cwc4630.h b/sound/pci/cs46xx/imgs/cwc4630.h
deleted file mode 100644
index 37c4f1318dc5..000000000000
--- a/sound/pci/cs46xx/imgs/cwc4630.h
+++ /dev/null
@@ -1,320 +0,0 @@
-/* generated from cwc4630.osp DO NOT MODIFY */
-
-#ifndef __HEADER_cwc4630_H__
-#define __HEADER_cwc4630_H__
-
-static struct dsp_symbol_entry cwc4630_symbols[] = {
- { 0x0000, "BEGINADDRESS",0x00 },
- { 0x8000, "EXECCHILD",0x03 },
- { 0x8001, "EXECCHILD_98",0x03 },
- { 0x8003, "EXECCHILD_PUSH1IND",0x03 },
- { 0x8008, "EXECSIBLING",0x03 },
- { 0x800a, "EXECSIBLING_298",0x03 },
- { 0x800b, "EXECSIBLING_2IND1",0x03 },
- { 0x8010, "TIMINGMASTER",0x03 },
- { 0x804f, "S16_CODECINPUTTASK",0x03 },
- { 0x805e, "PCMSERIALINPUTTASK",0x03 },
- { 0x806d, "S16_MIX_TO_OSTREAM",0x03 },
- { 0x809a, "S16_MIX",0x03 },
- { 0x80bb, "S16_UPSRC",0x03 },
- { 0x813b, "MIX3_EXP",0x03 },
- { 0x8164, "DECIMATEBYPOW2",0x03 },
- { 0x8197, "VARIDECIMATE",0x03 },
- { 0x81f2, "_3DINPUTTASK",0x03 },
- { 0x820a, "_3DPRLGCINPTASK",0x03 },
- { 0x8227, "_3DSTEREOINPUTTASK",0x03 },
- { 0x8242, "_3DOUTPUTTASK",0x03 },
- { 0x82c4, "HRTF_MORPH_TASK",0x03 },
- { 0x82c6, "WAIT4DATA",0x03 },
- { 0x82fa, "PROLOGIC",0x03 },
- { 0x8496, "DECORRELATOR",0x03 },
- { 0x84a4, "STEREO2MONO",0x03 },
- { 0x0070, "SPOSCB",0x02 },
- { 0x0107, "TASKTREETHREAD",0x03 },
- { 0x013c, "TASKTREEHEADERCODE",0x03 },
- { 0x0145, "FGTASKTREEHEADERCODE",0x03 },
- { 0x0169, "NULLALGORITHM",0x03 },
- { 0x016d, "HFGEXECCHILD",0x03 },
- { 0x016e, "HFGEXECCHILD_98",0x03 },
- { 0x0170, "HFGEXECCHILD_PUSH1IND",0x03 },
- { 0x0173, "HFGEXECSIBLING",0x03 },
- { 0x0175, "HFGEXECSIBLING_298",0x03 },
- { 0x0176, "HFGEXECSIBLING_2IND1",0x03 },
- { 0x0179, "S16_CODECOUTPUTTASK",0x03 },
- { 0x0194, "#CODE_END",0x00 },
-}; /* cwc4630 symbols */
-
-static u32 cwc4630_code[] = {
-/* BEGINADDRESS */
-/* 0000 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
-/* 0002 */ 0x00001705,0x00001400,0x000a411e,0x00001003,
-/* 0004 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
-/* 0006 */ 0x00009705,0x00001400,0x000a411e,0x00001003,
-/* 0008 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
-/* 000A */ 0x00011705,0x00001400,0x000a411e,0x00001003,
-/* 000C */ 0x00040730,0x00001002,0x000f619e,0x00001003,
-/* 000E */ 0x00019705,0x00001400,0x000a411e,0x00001003,
-/* 0010 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
-/* 0012 */ 0x00021705,0x00001400,0x000a411e,0x00001003,
-/* 0014 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
-/* 0016 */ 0x00029705,0x00001400,0x000a411e,0x00001003,
-/* 0018 */ 0x00040730,0x00001002,0x000f619e,0x00001003,
-/* 001A */ 0x00031705,0x00001400,0x000a411e,0x00001003,
-/* 001C */ 0x00040730,0x00001002,0x000f619e,0x00001003,
-/* 001E */ 0x00039705,0x00001400,0x000a411e,0x00001003,
-/* 0020 */ 0x000fe19e,0x00001003,0x0009c730,0x00001003,
-/* 0022 */ 0x0008e19c,0x00001003,0x000083c1,0x00093040,
-/* 0024 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
-/* 0026 */ 0x00009705,0x00001400,0x000a211e,0x00001003,
-/* 0028 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
-/* 002A */ 0x00011705,0x00001400,0x000a211e,0x00001003,
-/* 002C */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
-/* 002E */ 0x00019705,0x00001400,0x000a211e,0x00001003,
-/* 0030 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
-/* 0032 */ 0x00021705,0x00001400,0x000a211e,0x00001003,
-/* 0034 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
-/* 0036 */ 0x00029705,0x00001400,0x000a211e,0x00001003,
-/* 0038 */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
-/* 003A */ 0x00031705,0x00001400,0x000a211e,0x00001003,
-/* 003C */ 0x00098730,0x00001002,0x000ee19e,0x00001003,
-/* 003E */ 0x00039705,0x00001400,0x000a211e,0x00001003,
-/* 0040 */ 0x0001a730,0x00001008,0x000e2730,0x00001002,
-/* 0042 */ 0x0000a731,0x00001002,0x0000a731,0x00001002,
-/* 0044 */ 0x0000a731,0x00001002,0x0000a731,0x00001002,
-/* 0046 */ 0x0000a731,0x00001002,0x0000a731,0x00001002,
-/* 0048 */ 0x00000000,0x00000000,0x000f619c,0x00001003,
-/* 004A */ 0x0007f801,0x000c0000,0x00000037,0x00001000,
-/* 004C */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 004E */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0050 */ 0x00000000,0x000c0000,0x00000000,0x00000000,
-/* 0052 */ 0x0000373c,0x00001000,0x00000000,0x00000000,
-/* 0054 */ 0x000ee19c,0x00001003,0x0007f801,0x000c0000,
-/* 0056 */ 0x00000037,0x00001000,0x00000000,0x00000000,
-/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 005A */ 0x00000000,0x00000000,0x0000273c,0x00001000,
-/* 005C */ 0x00000033,0x00001000,0x000e679e,0x00001003,
-/* 005E */ 0x00007705,0x00001400,0x000ac71e,0x00001003,
-/* 0060 */ 0x00087fc1,0x000c3be0,0x0007f801,0x000c0000,
-/* 0062 */ 0x00000037,0x00001000,0x00000000,0x00000000,
-/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0066 */ 0x00000000,0x00000000,0x0000a730,0x00001003,
-/* 0068 */ 0x00000033,0x00001000,0x0007f801,0x000c0000,
-/* 006A */ 0x00000037,0x00001000,0x00000000,0x00000000,
-/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 006E */ 0x00000000,0x00000000,0x00000000,0x000c0000,
-/* 0070 */ 0x00000032,0x00001000,0x0000273d,0x00001000,
-/* 0072 */ 0x0004a730,0x00001003,0x00000f41,0x00097140,
-/* 0074 */ 0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
-/* 0076 */ 0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
-/* 0078 */ 0x00000000,0x00000000,0x0001bf05,0x0003fc40,
-/* 007A */ 0x00002725,0x000aa400,0x00013705,0x00093a00,
-/* 007C */ 0x0000002e,0x0009d6c0,0x0002ef8a,0x00000000,
-/* 007E */ 0x00040630,0x00001004,0x0004ef0a,0x000eb785,
-/* 0080 */ 0x0003fc8a,0x00000000,0x00000000,0x000c70e0,
-/* 0082 */ 0x0007d182,0x0002c640,0x00008630,0x00001004,
-/* 0084 */ 0x000799b8,0x0002c6c0,0x00031705,0x00092240,
-/* 0086 */ 0x00039f05,0x000932c0,0x0003520a,0x00000000,
-/* 0088 */ 0x00070731,0x0000100b,0x00010705,0x000b20c0,
-/* 008A */ 0x00000000,0x000eba44,0x00032108,0x000c60c4,
-/* 008C */ 0x00065208,0x000c2917,0x000486b0,0x00001007,
-/* 008E */ 0x00012f05,0x00036880,0x0002818e,0x000c0000,
-/* 0090 */ 0x0004410a,0x00000000,0x00048630,0x00001007,
-/* 0092 */ 0x00029705,0x000c0000,0x00000000,0x00000000,
-/* 0094 */ 0x00003fc1,0x0003fc40,0x000037c1,0x00091b40,
-/* 0096 */ 0x00003fc1,0x000911c0,0x000037c1,0x000957c0,
-/* 0098 */ 0x00003fc1,0x000951c0,0x000037c1,0x00000000,
-/* 009A */ 0x00003fc1,0x000991c0,0x000037c1,0x00000000,
-/* 009C */ 0x00003fc1,0x0009d1c0,0x000037c1,0x00000000,
-/* 009E */ 0x0001ccc1,0x000915c0,0x0001c441,0x0009d800,
-/* 00A0 */ 0x0009cdc1,0x00091240,0x0001c541,0x00091d00,
-/* 00A2 */ 0x0009cfc1,0x00095240,0x0001c741,0x00095c80,
-/* 00A4 */ 0x000e8ca9,0x00099240,0x000e85ad,0x00095640,
-/* 00A6 */ 0x00069ca9,0x00099d80,0x000e952d,0x00099640,
-/* 00A8 */ 0x000eaca9,0x0009d6c0,0x000ea5ad,0x00091a40,
-/* 00AA */ 0x0006bca9,0x0009de80,0x000eb52d,0x00095a40,
-/* 00AC */ 0x000ecca9,0x00099ac0,0x000ec5ad,0x0009da40,
-/* 00AE */ 0x000edca9,0x0009d300,0x000a6e0a,0x00001000,
-/* 00B0 */ 0x000ed52d,0x00091e40,0x000eeca9,0x00095ec0,
-/* 00B2 */ 0x000ee5ad,0x00099e40,0x0006fca9,0x00002500,
-/* 00B4 */ 0x000fb208,0x000c59a0,0x000ef52d,0x0009de40,
-/* 00B6 */ 0x00068ca9,0x000912c1,0x000683ad,0x00095241,
-/* 00B8 */ 0x00020f05,0x000991c1,0x00000000,0x00000000,
-/* 00BA */ 0x00086f88,0x00001000,0x0009cf81,0x000b5340,
-/* 00BC */ 0x0009c701,0x000b92c0,0x0009de81,0x000bd300,
-/* 00BE */ 0x0009d601,0x000b1700,0x0001fd81,0x000b9d80,
-/* 00C0 */ 0x0009f501,0x000b57c0,0x000a0f81,0x000bd740,
-/* 00C2 */ 0x00020701,0x000b5c80,0x000a1681,0x000b97c0,
-/* 00C4 */ 0x00021601,0x00002500,0x000a0701,0x000b9b40,
-/* 00C6 */ 0x000a0f81,0x000b1bc0,0x00021681,0x00002d00,
-/* 00C8 */ 0x00020f81,0x000bd800,0x000a0701,0x000b5bc0,
-/* 00CA */ 0x00021601,0x00003500,0x000a0f81,0x000b5f40,
-/* 00CC */ 0x000a0701,0x000bdbc0,0x00021681,0x00003d00,
-/* 00CE */ 0x00020f81,0x000b1d00,0x000a0701,0x000b1fc0,
-/* 00D0 */ 0x00021601,0x00020500,0x00020f81,0x000b1341,
-/* 00D2 */ 0x000a0701,0x000b9fc0,0x00021681,0x00020d00,
-/* 00D4 */ 0x00020f81,0x000bde80,0x000a0701,0x000bdfc0,
-/* 00D6 */ 0x00021601,0x00021500,0x00020f81,0x000b9341,
-/* 00D8 */ 0x00020701,0x000b53c1,0x00021681,0x00021d00,
-/* 00DA */ 0x000a0f81,0x000d0380,0x0000b601,0x000b15c0,
-/* 00DC */ 0x00007b01,0x00000000,0x00007b81,0x000bd1c0,
-/* 00DE */ 0x00007b01,0x00000000,0x00007b81,0x000b91c0,
-/* 00E0 */ 0x00007b01,0x000b57c0,0x00007b81,0x000b51c0,
-/* 00E2 */ 0x00007b01,0x000b1b40,0x00007b81,0x000b11c0,
-/* 00E4 */ 0x00087b01,0x000c3dc0,0x0007e488,0x000d7e45,
-/* 00E6 */ 0x00000000,0x000d7a44,0x0007e48a,0x00000000,
-/* 00E8 */ 0x00011f05,0x00084080,0x00000000,0x00000000,
-/* 00EA */ 0x00001705,0x000b3540,0x00008a01,0x000bf040,
-/* 00EC */ 0x00007081,0x000bb5c0,0x00055488,0x00000000,
-/* 00EE */ 0x0000d482,0x0003fc40,0x0003fc88,0x00000000,
-/* 00F0 */ 0x0001e401,0x000b3a00,0x0001ec81,0x000bd6c0,
-/* 00F2 */ 0x0002ef88,0x000e7784,0x00056f08,0x00000000,
-/* 00F4 */ 0x000d86b0,0x00001007,0x00008281,0x000bb240,
-/* 00F6 */ 0x0000b801,0x000b7140,0x00007888,0x00000000,
-/* 00F8 */ 0x0000073c,0x00001000,0x0007f188,0x000c0000,
-/* 00FA */ 0x00000000,0x00000000,0x00055288,0x000c555c,
-/* 00FC */ 0x0005528a,0x000c0000,0x0009fa88,0x000c5d00,
-/* 00FE */ 0x0000fa88,0x00000000,0x00000032,0x00001000,
-/* 0100 */ 0x0000073d,0x00001000,0x0007f188,0x000c0000,
-/* 0102 */ 0x00000000,0x00000000,0x0008c01c,0x00001003,
-/* 0104 */ 0x00002705,0x00001008,0x0008b201,0x000c1392,
-/* 0106 */ 0x0000ba01,0x00000000,
-/* TASKTREETHREAD */
-/* 0107 */ 0x00008731,0x00001400,0x0004c108,0x000fe0c4,
-/* 0109 */ 0x00057488,0x00000000,0x000a6388,0x00001001,
-/* 010B */ 0x0008b334,0x000bc141,0x0003020e,0x00000000,
-/* 010D */ 0x000986b0,0x00001008,0x00003625,0x000c5dfa,
-/* 010F */ 0x000a638a,0x00001001,0x0008020e,0x00001002,
-/* 0111 */ 0x0009a6b0,0x00001008,0x0007f301,0x00000000,
-/* 0113 */ 0x00000000,0x00000000,0x00002725,0x000a8c40,
-/* 0115 */ 0x000000ae,0x00000000,0x000e8630,0x00001008,
-/* 0117 */ 0x00000000,0x000c74e0,0x0007d182,0x0002d640,
-/* 0119 */ 0x000b8630,0x00001008,0x000799b8,0x0002d6c0,
-/* 011B */ 0x0000748a,0x000c3ec5,0x0007420a,0x000c0000,
-/* 011D */ 0x00062208,0x000c4117,0x000a0630,0x00001009,
-/* 011F */ 0x00000000,0x000c0000,0x0001022e,0x00000000,
-/* 0121 */ 0x0006a630,0x00001009,0x00000032,0x00001000,
-/* 0123 */ 0x000ca21c,0x00001003,0x00005a02,0x00000000,
-/* 0125 */ 0x0001a630,0x00001009,0x00000000,0x000c0000,
-/* 0127 */ 0x00000036,0x00001000,0x00000000,0x00000000,
-/* 0129 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 012B */ 0x00000000,0x00000000,0x0003a730,0x00001008,
-/* 012D */ 0x0007f801,0x000c0000,0x00000037,0x00001000,
-/* 012F */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0131 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0133 */ 0x0003a730,0x00001008,0x00000033,0x00001000,
-/* 0135 */ 0x0003a705,0x00001008,0x00007a01,0x000c0000,
-/* 0137 */ 0x000e6288,0x000d550a,0x0006428a,0x00000000,
-/* 0139 */ 0x00090730,0x0000100a,0x00000000,0x000c0000,
-/* 013B */ 0x00000000,0x00000000,
-/* TASKTREEHEADERCODE */
-/* 013C */ 0x0007aab0,0x00034880,0x000a8fb0,0x0000100b,
-/* 013E */ 0x00057488,0x00000000,0x00033b94,0x00081140,
-/* 0140 */ 0x000183ae,0x00000000,0x000a86b0,0x0000100b,
-/* 0142 */ 0x00022f05,0x000c3545,0x0000eb8a,0x00000000,
-/* 0144 */ 0x00042731,0x00001003,
-/* FGTASKTREEHEADERCODE */
-/* 0145 */ 0x0007aab0,0x00034880,0x00078fb0,0x0000100a,
-/* 0147 */ 0x00057488,0x00000000,0x00033b94,0x00081140,
-/* 0149 */ 0x000183ae,0x00000000,0x000b06b0,0x0000100b,
-/* 014B */ 0x00022f05,0x00000000,0x00007401,0x00091140,
-/* 014D */ 0x00048f05,0x000951c0,0x00042731,0x00001003,
-/* 014F */ 0x0000473d,0x00001000,0x000f19b0,0x000bbc47,
-/* 0151 */ 0x00080000,0x000bffc7,0x000fe19e,0x00001003,
-/* 0153 */ 0x00000000,0x00000000,0x0008e19c,0x00001003,
-/* 0155 */ 0x000083c1,0x00093040,0x00000f41,0x00097140,
-/* 0157 */ 0x0000a841,0x0009b240,0x0000a0c1,0x0009f040,
-/* 0159 */ 0x0001c641,0x00093540,0x0001cec1,0x0009b5c0,
-/* 015B */ 0x00000000,0x000fdc44,0x00055208,0x00000000,
-/* 015D */ 0x00010705,0x000a2880,0x0000a23a,0x00093a00,
-/* 015F */ 0x0003fc8a,0x000df6c5,0x0004ef0a,0x000c0000,
-/* 0161 */ 0x00012f05,0x00036880,0x00065308,0x000c2997,
-/* 0163 */ 0x000086b0,0x0000100b,0x0004410a,0x000d40c7,
-/* 0165 */ 0x00000000,0x00000000,0x00088730,0x00001004,
-/* 0167 */ 0x00056f0a,0x000ea105,0x00000000,0x00000000,
-/* NULLALGORITHM */
-/* 0169 */ 0x0000473d,0x00001000,0x000f19b0,0x000bbc47,
-/* 016B */ 0x00080000,0x000bffc7,0x0000273d,0x00001000,
-/* HFGEXECCHILD */
-/* 016D */ 0x00000000,0x000eba44,
-/* HFGEXECCHILD_98 */
-/* 016E */ 0x00048f05,0x0000f440,0x00007401,0x0000f7c0,
-/* HFGEXECCHILD_PUSH1IND */
-/* 0170 */ 0x00000734,0x00001000,0x00010705,0x000a6880,
-/* 0172 */ 0x00006a88,0x000c75c4,
-/* HFGEXECSIBLING */
-/* 0173 */ 0x00000000,0x000e5084,0x00000000,0x000eba44,
-/* HFGEXECSIBLING_298 */
-/* 0175 */ 0x00087401,0x000e4782,
-/* HFGEXECSIBLING_2IND1 */
-/* 0176 */ 0x00000734,0x00001000,0x00010705,0x000a6880,
-/* 0178 */ 0x00006a88,0x000c75c4,
-/* S16_CODECOUTPUTTASK */
-/* 0179 */ 0x0007c108,0x000c0000,0x0007e721,0x000bed40,
-/* 017B */ 0x00005f25,0x000badc0,0x0003ba97,0x000beb80,
-/* 017D */ 0x00065590,0x000b2e00,0x00033217,0x00003ec0,
-/* 017F */ 0x00065590,0x000b8e40,0x0003ed80,0x000491c0,
-/* 0181 */ 0x00073fb0,0x00074c80,0x000583a0,0x0000100c,
-/* 0183 */ 0x000ee388,0x00042970,0x00008301,0x00021ef2,
-/* 0185 */ 0x000b8f14,0x0000000f,0x000c4d8d,0x0000001b,
-/* 0187 */ 0x000d6dc2,0x000e06c6,0x000032ac,0x000c3916,
-/* 0189 */ 0x0004edc2,0x00074c80,0x00078898,0x00001000,
-/* 018B */ 0x00038894,0x00000032,0x000c4d8d,0x00092e1b,
-/* 018D */ 0x000d6dc2,0x000e06c6,0x0004edc2,0x000c1956,
-/* 018F */ 0x0000722c,0x00034a00,0x00041705,0x0009ed40,
-/* 0191 */ 0x00058730,0x00001400,0x000d7488,0x000c3a00,
-/* 0193 */ 0x00048f05,0x00000000
-};
-/* #CODE_END */
-
-static u32 cwc4630_parameter[] = {
-/* 0000 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0004 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0008 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 000C */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0010 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0014 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0018 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 001C */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0020 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0024 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0028 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 002C */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0030 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0034 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0038 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 003C */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0040 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0044 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0048 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 004C */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0050 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0054 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0058 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 005C */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0060 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0064 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0068 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 006C */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0070 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0074 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 0078 */ 0x00000000,0x00000000,0x00000000,0x00000000,
-/* 007C */ 0x00000000,0x00000000,0x00000000,0x00000000
-}; /* #PARAMETER_END */
-
-
-static struct dsp_segment_desc cwc4630_segments[] = {
- { SEGTYPE_SP_PROGRAM, 0x00000000, 0x00000328, cwc4630_code },
- { SEGTYPE_SP_PARAMETER, 0x00000000, 0x00000080, cwc4630_parameter },
-};
-
-static struct dsp_module_desc cwc4630_module = {
- "cwc4630",
- {
- 38,
- cwc4630_symbols
- },
- 2,
- cwc4630_segments,
-};
-
-#endif /* __HEADER_cwc4630_H__ */
diff --git a/sound/pci/cs46xx/imgs/cwcasync.h b/sound/pci/cs46xx/imgs/cwcasync.h
deleted file mode 100644
index 70e63e13c2b3..000000000000
--- a/sound/pci/cs46xx/imgs/cwcasync.h
+++ /dev/null
@@ -1,176 +0,0 @@
-/* generated from cwcasync.osp DO NOT MODIFY */
-
-#ifndef __HEADER_cwcasync_H__
-#define __HEADER_cwcasync_H__
-
-static struct dsp_symbol_entry cwcasync_symbols[] = {
- { 0x8000, "EXECCHILD",0x03 },
- { 0x8001, "EXECCHILD_98",0x03 },
- { 0x8003, "EXECCHILD_PUSH1IND",0x03 },
- { 0x8008, "EXECSIBLING",0x03 },
- { 0x800a, "EXECSIBLING_298",0x03 },
- { 0x800b, "EXECSIBLING_2IND1",0x03 },
- { 0x8010, "TIMINGMASTER",0x03 },
- { 0x804f, "S16_CODECINPUTTASK",0x03 },
- { 0x805e, "PCMSERIALINPUTTASK",0x03 },
- { 0x806d, "S16_MIX_TO_OSTREAM",0x03 },
- { 0x809a, "S16_MIX",0x03 },
- { 0x80bb, "S16_UPSRC",0x03 },
- { 0x813b, "MIX3_EXP",0x03 },
- { 0x8164, "DECIMATEBYPOW2",0x03 },
- { 0x8197, "VARIDECIMATE",0x03 },
- { 0x81f2, "_3DINPUTTASK",0x03 },
- { 0x820a, "_3DPRLGCINPTASK",0x03 },
- { 0x8227, "_3DSTEREOINPUTTASK",0x03 },
- { 0x8242, "_3DOUTPUTTASK",0x03 },
- { 0x82c4, "HRTF_MORPH_TASK",0x03 },
- { 0x82c6, "WAIT4DATA",0x03 },
- { 0x82fa, "PROLOGIC",0x03 },
- { 0x8496, "DECORRELATOR",0x03 },
- { 0x84a4, "STEREO2MONO",0x03 },
- { 0x0000, "OVERLAYBEGINADDRESS",0x00 },
- { 0x0000, "SPIOWRITE",0x03 },
- { 0x000d, "S16_ASYNCCODECINPUTTASK",0x03 },
- { 0x0043, "SPDIFITASK",0x03 },
- { 0x007b, "SPDIFOTASK",0x03 },
- { 0x0097, "ASYNCHFGTXCODE",0x03 },
- { 0x00be, "ASYNCHFGRXCODE",0x03 },
- { 0x00db, "#CODE_END",0x00 },
-}; /* cwcasync symbols */
-
-static u32 cwcasync_code[] = {
-/* OVERLAYBEGINADDRESS */
-/* 0000 */ 0x00002731,0x00001400,0x00003725,0x000a8440,
-/* 0002 */ 0x000000ae,0x00000000,0x00060630,0x00001000,
-/* 0004 */ 0x00000000,0x000c7560,0x00075282,0x0002d640,
-/* 0006 */ 0x00021705,0x00000000,0x00072ab8,0x0002d6c0,
-/* 0008 */ 0x00020630,0x00001000,0x000c74c2,0x000d4b82,
-/* 000A */ 0x000475c2,0x00000000,0x0003430a,0x000c0000,
-/* 000C */ 0x00042730,0x00001400,
-/* S16_ASYNCCODECINPUTTASK */
-/* 000D */ 0x0006a108,0x000cf2c4,0x0004f4c0,0x00000000,
-/* 000F */ 0x000fa418,0x0000101f,0x0005d402,0x0001c500,
-/* 0011 */ 0x000f0630,0x00001000,0x00004418,0x00001380,
-/* 0013 */ 0x000e243d,0x000d394a,0x00049705,0x00000000,
-/* 0015 */ 0x0007d530,0x000b4240,0x000e00f2,0x00001000,
-/* 0017 */ 0x00009134,0x000ca20a,0x00004c90,0x00001000,
-/* 0019 */ 0x0005d705,0x00000000,0x00004f25,0x00098240,
-/* 001B */ 0x00004725,0x00000000,0x0000e48a,0x00000000,
-/* 001D */ 0x00027295,0x0009c2c0,0x0003df25,0x00000000,
-/* 001F */ 0x000e8030,0x00001001,0x0005f718,0x000ac600,
-/* 0021 */ 0x0007cf30,0x000c2a01,0x00082630,0x00001001,
-/* 0023 */ 0x000504a0,0x00001001,0x00029314,0x000bcb80,
-/* 0025 */ 0x0003cf25,0x000b0e00,0x0004f5c0,0x00000000,
-/* 0027 */ 0x00049118,0x000d888a,0x0007dd02,0x000c6efa,
-/* 0029 */ 0x00000000,0x00000000,0x0004f5c0,0x00069c80,
-/* 002B */ 0x0000d402,0x00000000,0x000e8630,0x00001001,
-/* 002D */ 0x00079130,0x00000000,0x00049118,0x00090e00,
-/* 002F */ 0x0006c10a,0x00000000,0x00000000,0x000c0000,
-/* 0031 */ 0x0007cf30,0x00030580,0x00005725,0x00000000,
-/* 0033 */ 0x000d84a0,0x00001001,0x00029314,0x000b4780,
-/* 0035 */ 0x0003cf25,0x000b8600,0x00000000,0x00000000,
-/* 0037 */ 0x00000000,0x000c0000,0x00000000,0x00042c80,
-/* 0039 */ 0x0001dec1,0x000e488c,0x00031114,0x00000000,
-/* 003B */ 0x0004f5c2,0x00000000,0x0003640a,0x00000000,
-/* 003D */ 0x00000000,0x000e5084,0x00000000,0x000eb844,
-/* 003F */ 0x00007001,0x00000000,0x00000734,0x00001000,
-/* 0041 */ 0x00010705,0x000a6880,0x00006a88,0x000c75c4,
-/* SPDIFITASK */
-/* 0043 */ 0x0006a108,0x000cf2c4,0x0004f4c0,0x000d5384,
-/* 0045 */ 0x0007e48a,0x00000000,0x00067718,0x00001000,
-/* 0047 */ 0x0007a418,0x00001000,0x0007221a,0x00000000,
-/* 0049 */ 0x0005d402,0x00014500,0x000b8630,0x00001002,
-/* 004B */ 0x00004418,0x00001780,0x000e243d,0x000d394a,
-/* 004D */ 0x00049705,0x00000000,0x0007d530,0x000b4240,
-/* 004F */ 0x000ac0f2,0x00001002,0x00014414,0x00000000,
-/* 0051 */ 0x00004c90,0x00001000,0x0005d705,0x00000000,
-/* 0053 */ 0x00004f25,0x00098240,0x00004725,0x00000000,
-/* 0055 */ 0x0000e48a,0x00000000,0x00027295,0x0009c2c0,
-/* 0057 */ 0x0007df25,0x00000000,0x000ac030,0x00001003,
-/* 0059 */ 0x0005f718,0x000fe798,0x00029314,0x000bcb80,
-/* 005B */ 0x00000930,0x000b0e00,0x0004f5c0,0x000de204,
-/* 005D */ 0x000884a0,0x00001003,0x0007cf25,0x000e3560,
-/* 005F */ 0x00049118,0x00000000,0x00049118,0x000d888a,
-/* 0061 */ 0x0007dd02,0x000c6efa,0x0000c434,0x00030040,
-/* 0063 */ 0x000fda82,0x000c2312,0x000fdc0e,0x00001001,
-/* 0065 */ 0x00083402,0x000c2b92,0x000706b0,0x00001003,
-/* 0067 */ 0x00075a82,0x00000000,0x0000d625,0x000b0940,
-/* 0069 */ 0x0000840e,0x00001002,0x0000aabc,0x000c511e,
-/* 006B */ 0x00078730,0x00001003,0x0000aaf4,0x000e910a,
-/* 006D */ 0x0004628a,0x00000000,0x00006aca,0x00000000,
-/* 006F */ 0x00000930,0x00000000,0x0004f5c0,0x00069c80,
-/* 0071 */ 0x00046ac0,0x00000000,0x0003c40a,0x000fc898,
-/* 0073 */ 0x00049118,0x00090e00,0x0006c10a,0x00000000,
-/* 0075 */ 0x00000000,0x000e5084,0x00000000,0x000eb844,
-/* 0077 */ 0x00007001,0x00000000,0x00000734,0x00001000,
-/* 0079 */ 0x00010705,0x000a6880,0x00006a88,0x000c75c4,
-/* SPDIFOTASK */
-/* 007B */ 0x0006a108,0x000c0000,0x0004f4c0,0x000c3245,
-/* 007D */ 0x0000a418,0x00001000,0x0003a20a,0x00000000,
-/* 007F */ 0x00004418,0x00001380,0x000e243d,0x000d394a,
-/* 0081 */ 0x000c9705,0x000def92,0x0008c030,0x00001004,
-/* 0083 */ 0x0005f718,0x000fe798,0x00000000,0x000c0000,
-/* 0085 */ 0x00005725,0x00000000,0x000704a0,0x00001004,
-/* 0087 */ 0x00029314,0x000b4780,0x0003cf25,0x000b8600,
-/* 0089 */ 0x00000000,0x00000000,0x00000000,0x000c0000,
-/* 008B */ 0x00000000,0x00042c80,0x0001dec1,0x000e488c,
-/* 008D */ 0x00031114,0x00000000,0x0004f5c2,0x00000000,
-/* 008F */ 0x0004a918,0x00098600,0x0006c28a,0x00000000,
-/* 0091 */ 0x00000000,0x000e5084,0x00000000,0x000eb844,
-/* 0093 */ 0x00007001,0x00000000,0x00000734,0x00001000,
-/* 0095 */ 0x00010705,0x000a6880,0x00006a88,0x000c75c4,
-/* ASYNCHFGTXCODE */
-/* 0097 */ 0x0002a880,0x000b4e40,0x00042214,0x000e5548,
-/* 0099 */ 0x000542bf,0x00000000,0x00000000,0x000481c0,
-/* 009B */ 0x00000000,0x00000000,0x00000000,0x00000030,
-/* 009D */ 0x0000072d,0x000fbf8a,0x00077f94,0x000ea7df,
-/* 009F */ 0x0002ac95,0x000d3145,0x00002731,0x00001400,
-/* 00A1 */ 0x00006288,0x000c71c4,0x00014108,0x000e6044,
-/* 00A3 */ 0x00035408,0x00000000,0x00025418,0x000a0ec0,
-/* 00A5 */ 0x0001443d,0x000ca21e,0x00046595,0x000d730c,
-/* 00A7 */ 0x0006538e,0x00000000,0x00064630,0x00001005,
-/* 00A9 */ 0x000e7b0e,0x000df782,0x000746b0,0x00001005,
-/* 00AB */ 0x00036f05,0x000c0000,0x00043695,0x000d598c,
-/* 00AD */ 0x0005331a,0x000f2185,0x00000000,0x00000000,
-/* 00AF */ 0x000007ae,0x000bdb00,0x00040630,0x00001400,
-/* 00B1 */ 0x0005e708,0x000c0000,0x0007ef30,0x000b1c00,
-/* 00B3 */ 0x000d86a0,0x00001005,0x00066408,0x000c0000,
-/* 00B5 */ 0x00000000,0x00000000,0x00021843,0x00000000,
-/* 00B7 */ 0x00000cac,0x00062c00,0x00001dac,0x00063400,
-/* 00B9 */ 0x00002cac,0x0006cc80,0x000db943,0x000e5ca1,
-/* 00BB */ 0x00000000,0x00000000,0x0006680a,0x000f3205,
-/* 00BD */ 0x00042730,0x00001400,
-/* ASYNCHFGRXCODE */
-/* 00BE */ 0x00014108,0x000f2204,0x00025418,0x000a2ec0,
-/* 00C0 */ 0x00015dbd,0x00038100,0x00015dbc,0x00000000,
-/* 00C2 */ 0x0005e415,0x00034880,0x0001258a,0x000d730c,
-/* 00C4 */ 0x0006538e,0x000baa40,0x00060630,0x00001006,
-/* 00C6 */ 0x00067b0e,0x000ac380,0x0003ef05,0x00000000,
-/* 00C8 */ 0x0000f734,0x0001c300,0x000586b0,0x00001400,
-/* 00CA */ 0x000b6f05,0x000c3a00,0x00048f05,0x00000000,
-/* 00CC */ 0x0005b695,0x0008c380,0x0002058e,0x00000000,
-/* 00CE */ 0x000500b0,0x00001400,0x0002b318,0x000e998d,
-/* 00D0 */ 0x0006430a,0x00000000,0x00000000,0x000ef384,
-/* 00D2 */ 0x00004725,0x000c0000,0x00000000,0x000f3204,
-/* 00D4 */ 0x00004f25,0x000c0000,0x00080000,0x000e5ca1,
-/* 00D6 */ 0x000cb943,0x000e5ca1,0x0004b943,0x00000000,
-/* 00D8 */ 0x00040730,0x00001400,0x000cb943,0x000e5ca1,
-/* 00DA */ 0x0004b943,0x00000000
-};
-/* #CODE_END */
-
-static struct dsp_segment_desc cwcasync_segments[] = {
- { SEGTYPE_SP_PROGRAM, 0x00000000, 0x000001b6, cwcasync_code },
-};
-
-static struct dsp_module_desc cwcasync_module = {
- "cwcasync",
- {
- 32,
- cwcasync_symbols
- },
- 1,
- cwcasync_segments,
-};
-
-#endif /* __HEADER_cwcasync_H__ */
diff --git a/sound/pci/cs46xx/imgs/cwcbinhack.h b/sound/pci/cs46xx/imgs/cwcbinhack.h
deleted file mode 100644
index f4d93689cd49..000000000000
--- a/sound/pci/cs46xx/imgs/cwcbinhack.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* generated by Benny
- MODIFY ON YOUR OWN RISK */
-
-#ifndef __HEADER_cwcbinhack_H__
-#define __HEADER_cwcbinhack_H__
-
-static struct dsp_symbol_entry cwcbinhack_symbols[] = {
- { 0x02c8, "OVERLAYBEGINADDRESS",0x00 },
- { 0x02c8, "MAGICSNOOPTASK",0x03 },
- { 0x0308, "#CODE_END",0x00 },
-}; /* cwcbinhack symbols */
-
-static u32 cwcbinhack_code[] = {
- /* 0x02c8 */
- 0x0007bfb0,0x000bc240,0x00000c2e,0x000c6084, /* 1 */
- 0x000b8630,0x00001016,0x00006408,0x000efb84, /* 2 */
- 0x00016008,0x00000000,0x0001c088,0x000c0000, /* 3 */
- 0x000fc908,0x000e3392,0x0005f488,0x000efb84, /* 4 */
- 0x0001d402,0x000b2e00,0x0003d418,0x00001000, /* 5 */
- 0x0008d574,0x000c4293,0x00065625,0x000ea30e, /* 6 */
- 0x00096c01,0x000c6f92,0x0001a58a,0x000c6085, /* 7 */
- 0x00002f43,0x00000000,0x000e03a0,0x00001016, /* 8 */
- 0x0005e608,0x000c0000,0x00000000,0x00000000, /* 9 */
- 0x000ca108,0x000dcca1,0x00003bac,0x000c3205, /* 10 */
- 0x00073843,0x00000000,0x00010730,0x00001017, /* 11 */
- 0x0001600a,0x000c0000,0x00057488,0x00000000, /* 12 */
- 0x00000000,0x000e5084,0x00000000,0x000eba44, /* 13 */
- 0x00087401,0x000e4782,0x00000734,0x00001000, /* 14 */
- 0x00010705,0x000a6880,0x00006a88,0x000c75c4, /* 15 */
- 0x00000000,0x00000000,0x00000000,0x00000000, /* 16 */
-};
-/* #CODE_END */
-
-static struct dsp_segment_desc cwcbinhack_segments[] = {
- { SEGTYPE_SP_PROGRAM, 0x00000000, 64, cwcbinhack_code },
-};
-
-static struct dsp_module_desc cwcbinhack_module = {
- "cwcbinhack",
- {
- 3,
- cwcbinhack_symbols
- },
- 1,
- cwcbinhack_segments,
-};
-
-#endif /* __HEADER_cwcbinhack_H__ */
diff --git a/sound/pci/cs46xx/imgs/cwcdma.asp b/sound/pci/cs46xx/imgs/cwcdma.asp
deleted file mode 100644
index a65e1193c89a..000000000000
--- a/sound/pci/cs46xx/imgs/cwcdma.asp
+++ /dev/null
@@ -1,170 +0,0 @@
-//
-// Copyright(c) by Benny Sjostrand (benny@hostmobility.com)
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-//
-
-
-//
-// This code runs inside the DSP (cs4610, cs4612, cs4624, or cs4630),
-// to compile it you need a tool named SPASM 3.0 and DSP code owned by
-// Cirrus Logic(R). The SPASM program will generate a object file (cwcdma.osp),
-// the "ospparser" tool will genereate the cwcdma.h file it's included from
-// the cs46xx_lib.c file.
-//
-//
-// The purpose of this code is very simple: make it possible to tranfser
-// the samples 'as they are' with no alteration from a PCMreader
-// SCB (DMA from host) to any other SCB. This is useful for AC3 through SPDIF.
-// SRC (source rate converters) task always alters the samples in somehow,
-// however it's from 48khz -> 48khz.
-// The alterations are not audible, but AC3 wont work.
-//
-// ...
-// |
-// +---------------+
-// | AsynchFGTxSCB |
-// +---------------+
-// |
-// subListPtr
-// |
-// +--------------+
-// | DMAReader |
-// +--------------+
-// |
-// subListPtr
-// |
-// +-------------+
-// | PCMReader |
-// +-------------+
-// (DMA from host)
-//
-
-struct dmaSCB
- {
- long dma_reserved1[3];
-
- short dma_reserved2:dma_outBufPtr;
-
- short dma_unused1:dma_unused2;
-
- long dma_reserved3[4];
-
- short dma_subListPtr:dma_nextSCB;
- short dma_SPBptr:dma_entryPoint;
-
- long dma_strmRsConfig;
- long dma_strmBufPtr;
-
- long dma_reserved4;
-
- VolumeControl s2m_volume;
- };
-
-#export DMAReader
-void DMAReader()
-{
- execChild();
- r2 = r0->dma_subListPtr;
- r1 = r0->nextSCB;
-
- rsConfig01 = r2->strmRsConfig;
- // Load rsConfig for input buffer
-
- rsDMA01 = r2->basicReq.daw, , tb = Z(0 - rf);
- // Load rsDMA in case input buffer is a DMA buffer Test to see if there is any data to transfer
-
- if (tb) goto execSibling_2ind1 after {
- r5 = rf + (-1);
- r6 = r1->dma_entryPoint; // r6 = entry point of sibling task
- r1 = r1->dma_SPBptr, // r1 = pointer to sibling task's SPB
- , ind = r6; // Load entry point of sibling task
- }
-
- rsConfig23 = r0->dma_strmRsConfig;
- // Load rsConfig for output buffer (never a DMA buffer)
-
- r4 = r0->dma_outBufPtr;
-
- rsa0 = r2->strmBufPtr;
- // rsa0 = input buffer pointer
-
- for (i = r5; i >= 0; --i)
- after {
- rsa2 = r4;
- // rsa2 = output buffer pointer
-
- nop;
- nop;
- }
- //*****************************
- // TODO: cycles to this point *
- //*****************************
- {
- acc0 = (rsd0 = *rsa0++1);
- // get sample
-
- nop; // Those "nop"'s are really uggly, but there's
- nop; // something with DSP's pipelines which I don't
- nop; // understand, resulting this code to fail without
- // having those "nop"'s (Benny)
-
- rsa0?reqDMA = r2;
- // Trigger DMA transfer on input stream,
- // if needed to replenish input buffer
-
- nop;
- // Yet another magic "nop" to make stuff work
-
- ,,r98 = acc0 $+>> 0;
- // store sample in ALU
-
- nop;
- // latency on load register.
- // (this one is understandable)
-
- *rsa2++1 = r98;
- // store sample in output buffer
-
- nop; // The same story
- nop; // as above again ...
- nop;
- }
- // TODO: cycles per loop iteration
-
- r2->strmBufPtr = rsa0,, ;
- // Update the modified buffer pointers
-
- r4 = rsa2;
- // Load output pointer position into r4
-
- r2 = r0->nextSCB;
- // Sibling task
-
- goto execSibling_2ind1 // takes 6 cycles
- after {
- r98 = r2->thisSPB:entryPoint;
- // Load child routine entry and data address
-
- r1 = r9;
- // r9 is r2->thisSPB
-
- r0->dma_outBufPtr = r4,,
- // Store updated output buffer pointer
-
- ind = r8;
- // r8 is r2->entryPoint
- }
-}
diff --git a/sound/pci/cs46xx/imgs/cwcdma.h b/sound/pci/cs46xx/imgs/cwcdma.h
deleted file mode 100644
index 7ff0d4587161..000000000000
--- a/sound/pci/cs46xx/imgs/cwcdma.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/* generated from cwcdma.osp DO NOT MODIFY */
-
-#ifndef __HEADER_cwcdma_H__
-#define __HEADER_cwcdma_H__
-
-static struct dsp_symbol_entry cwcdma_symbols[] = {
- { 0x8000, "EXECCHILD",0x03 },
- { 0x8001, "EXECCHILD_98",0x03 },
- { 0x8003, "EXECCHILD_PUSH1IND",0x03 },
- { 0x8008, "EXECSIBLING",0x03 },
- { 0x800a, "EXECSIBLING_298",0x03 },
- { 0x800b, "EXECSIBLING_2IND1",0x03 },
- { 0x8010, "TIMINGMASTER",0x03 },
- { 0x804f, "S16_CODECINPUTTASK",0x03 },
- { 0x805e, "PCMSERIALINPUTTASK",0x03 },
- { 0x806d, "S16_MIX_TO_OSTREAM",0x03 },
- { 0x809a, "S16_MIX",0x03 },
- { 0x80bb, "S16_UPSRC",0x03 },
- { 0x813b, "MIX3_EXP",0x03 },
- { 0x8164, "DECIMATEBYPOW2",0x03 },
- { 0x8197, "VARIDECIMATE",0x03 },
- { 0x81f2, "_3DINPUTTASK",0x03 },
- { 0x820a, "_3DPRLGCINPTASK",0x03 },
- { 0x8227, "_3DSTEREOINPUTTASK",0x03 },
- { 0x8242, "_3DOUTPUTTASK",0x03 },
- { 0x82c4, "HRTF_MORPH_TASK",0x03 },
- { 0x82c6, "WAIT4DATA",0x03 },
- { 0x82fa, "PROLOGIC",0x03 },
- { 0x8496, "DECORRELATOR",0x03 },
- { 0x84a4, "STEREO2MONO",0x03 },
- { 0x0000, "OVERLAYBEGINADDRESS",0x00 },
- { 0x0000, "DMAREADER",0x03 },
- { 0x0018, "#CODE_END",0x00 },
-}; /* cwcdma symbols */
-
-static u32 cwcdma_code[] = {
-/* OVERLAYBEGINADDRESS */
-/* 0000 */ 0x00002731,0x00001400,0x0004c108,0x000e5044,
-/* 0002 */ 0x0005f608,0x00000000,0x000007ae,0x000be300,
-/* 0004 */ 0x00058630,0x00001400,0x0007afb0,0x000e9584,
-/* 0006 */ 0x00007301,0x000a9840,0x0005e708,0x000cd104,
-/* 0008 */ 0x00067008,0x00000000,0x000902a0,0x00001000,
-/* 000A */ 0x00012a01,0x000c0000,0x00000000,0x00000000,
-/* 000C */ 0x00021843,0x000c0000,0x00000000,0x000c0000,
-/* 000E */ 0x0000e101,0x000c0000,0x00000cac,0x00000000,
-/* 0010 */ 0x00080000,0x000e5ca1,0x00000000,0x000c0000,
-/* 0012 */ 0x00000000,0x00000000,0x00000000,0x00092c00,
-/* 0014 */ 0x000122c1,0x000e5084,0x00058730,0x00001400,
-/* 0016 */ 0x000d7488,0x000e4782,0x00007401,0x0001c100
-};
-
-/* #CODE_END */
-
-static struct dsp_segment_desc cwcdma_segments[] = {
- { SEGTYPE_SP_PROGRAM, 0x00000000, 0x00000030, cwcdma_code },
-};
-
-static struct dsp_module_desc cwcdma_module = {
- "cwcdma",
- {
- 27,
- cwcdma_symbols
- },
- 1,
- cwcdma_segments,
-};
-
-#endif /* __HEADER_cwcdma_H__ */
diff --git a/sound/pci/cs46xx/imgs/cwcsnoop.h b/sound/pci/cs46xx/imgs/cwcsnoop.h
deleted file mode 100644
index 6929d0a5a3f3..000000000000
--- a/sound/pci/cs46xx/imgs/cwcsnoop.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* generated from cwcsnoop.osp DO NOT MODIFY */
-
-#ifndef __HEADER_cwcsnoop_H__
-#define __HEADER_cwcsnoop_H__
-
-static struct dsp_symbol_entry cwcsnoop_symbols[] = {
- { 0x0500, "OVERLAYBEGINADDRESS",0x00 },
- { 0x0500, "OUTPUTSNOOP",0x03 },
- { 0x051f, "#CODE_END",0x00 },
-}; /* cwcsnoop symbols */
-
-static u32 cwcsnoop_code[] = {
-/* 0000 */ 0x0007bfb0,0x000b4e40,0x0007c088,0x000c0617,
-/* 0002 */ 0x00049705,0x00000000,0x00080630,0x00001028,
-/* 0004 */ 0x00076408,0x000efb84,0x00066008,0x00000000,
-/* 0006 */ 0x0007c908,0x000c0000,0x00046725,0x000efa44,
-/* 0008 */ 0x0005f708,0x00000000,0x0001d402,0x000b2e00,
-/* 000A */ 0x0003d418,0x00001000,0x0008d574,0x000c4293,
-/* 000C */ 0x00065625,0x000ea30e,0x00096c01,0x000c6f92,
-/* 000E */ 0x0006a58a,0x000f6085,0x00002f43,0x00000000,
-/* 0010 */ 0x000a83a0,0x00001028,0x0005e608,0x000c0000,
-/* 0012 */ 0x00000000,0x00000000,0x000ca108,0x000dcca1,
-/* 0014 */ 0x00003bac,0x000fb205,0x00073843,0x00000000,
-/* 0016 */ 0x000d8730,0x00001028,0x0006600a,0x000c0000,
-/* 0018 */ 0x00057488,0x00000000,0x00000000,0x000e5084,
-/* 001A */ 0x00000000,0x000eba44,0x00087401,0x000e4782,
-/* 001C */ 0x00000734,0x00001000,0x00010705,0x000a6880,
-/* 001E */ 0x00006a88,0x000c75c4
-};
-/* #CODE_END */
-
-static struct dsp_segment_desc cwcsnoop_segments[] = {
- { SEGTYPE_SP_PROGRAM, 0x00000000, 0x0000003e, cwcsnoop_code },
-};
-
-static struct dsp_module_desc cwcsnoop_module = {
- "cwcsnoop",
- {
- 3,
- cwcsnoop_symbols
- },
- 1,
- cwcsnoop_segments,
-};
-
-#endif /* __HEADER_cwcsnoop_H__ */
diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c
index bc07e275d4d4..292b65aa758a 100644
--- a/sound/pci/cs5530.c
+++ b/sound/pci/cs5530.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* cs5530.c - Initialisation code for Cyrix/NatSemi VSA1 softaudio
*
@@ -21,23 +22,13 @@
* Thanks to National Semiconductor for providing the needed information
* on the XpressAudio(tm) internals.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
* TO DO:
* Investigate whether we can portably support Cognac (5520) in the
* same manner.
*/
#include <linux/delay.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -50,7 +41,14 @@ MODULE_LICENSE("GPL");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for CS5530 Audio driver.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for CS5530 Audio driver.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable CS5530 Audio driver.");
struct snd_cs5530 {
struct snd_card *card;
@@ -59,7 +57,7 @@ struct snd_cs5530 {
unsigned long pci_base;
};
-static DEFINE_PCI_DEVICE_TABLE(snd_cs5530_ids) = {
+static const struct pci_device_id snd_cs5530_ids[] = {
{PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO, PCI_ANY_ID,
PCI_ANY_ID, 0, 0},
{0,}
@@ -67,27 +65,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_cs5530_ids) = {
MODULE_DEVICE_TABLE(pci, snd_cs5530_ids);
-static int snd_cs5530_free(struct snd_cs5530 *chip)
-{
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
-}
-
-static int snd_cs5530_dev_free(struct snd_device *device)
-{
- struct snd_cs5530 *chip = device->device_data;
- return snd_cs5530_free(chip);
-}
-
-static void __devexit snd_cs5530_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
-}
-
-static u8 __devinit snd_cs5530_mixer_read(unsigned long io, u8 reg)
+static u8 snd_cs5530_mixer_read(unsigned long io, u8 reg)
{
outb(reg, io + 4);
udelay(20);
@@ -96,52 +74,28 @@ static u8 __devinit snd_cs5530_mixer_read(unsigned long io, u8 reg)
return reg;
}
-static int __devinit snd_cs5530_create(struct snd_card *card,
- struct pci_dev *pci,
- struct snd_cs5530 **rchip)
+static int snd_cs5530_create(struct snd_card *card,
+ struct pci_dev *pci)
{
- struct snd_cs5530 *chip;
+ struct snd_cs5530 *chip = card->private_data;
unsigned long sb_base;
u8 irq, dma8, dma16 = 0;
u16 map;
void __iomem *mem;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_cs5530_dev_free,
- };
- *rchip = NULL;
-
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
chip->card = card;
chip->pci = pci;
- err = pci_request_regions(pci, "CS5530");
- if (err < 0) {
- kfree(chip);
- pci_disable_device(pci);
- return err;
- }
+ mem = pcim_iomap_region(pci, 0, "CS5530");
+ if (IS_ERR(mem))
+ return PTR_ERR(mem);
chip->pci_base = pci_resource_start(pci, 0);
-
- mem = pci_ioremap_bar(pci, 0);
- if (mem == NULL) {
- kfree(chip);
- pci_disable_device(pci);
- return -EBUSY;
- }
-
map = readw(mem + 0x18);
- iounmap(mem);
/* Map bits
0:1 * 0x20 + 0x200 = sb base
@@ -155,17 +109,16 @@ static int __devinit snd_cs5530_create(struct snd_card *card,
sb_base = 0x220 + 0x20 * (map & 3);
if (map & (1<<2))
- printk(KERN_INFO "CS5530: XpressAudio at 0x%lx\n", sb_base);
+ dev_info(card->dev, "XpressAudio at 0x%lx\n", sb_base);
else {
- printk(KERN_ERR "Could not find XpressAudio!\n");
- snd_cs5530_free(chip);
+ dev_err(card->dev, "Could not find XpressAudio!\n");
return -ENODEV;
}
if (map & (1<<5))
- printk(KERN_INFO "CS5530: MPU at 0x300\n");
+ dev_info(card->dev, "MPU at 0x300\n");
else if (map & (1<<6))
- printk(KERN_INFO "CS5530: MPU at 0x330\n");
+ dev_info(card->dev, "MPU at 0x330\n");
irq = snd_cs5530_mixer_read(sb_base, 0x80) & 0x0F;
dma8 = snd_cs5530_mixer_read(sb_base, 0x81);
@@ -177,8 +130,7 @@ static int __devinit snd_cs5530_create(struct snd_card *card,
else if (dma8 & 0x80)
dma16 = 7;
else {
- printk(KERN_ERR "CS5530: No 16bit DMA enabled\n");
- snd_cs5530_free(chip);
+ dev_err(card->dev, "No 16bit DMA enabled\n");
return -ENODEV;
}
@@ -189,8 +141,7 @@ static int __devinit snd_cs5530_create(struct snd_card *card,
else if (dma8 & 0x08)
dma8 = 3;
else {
- printk(KERN_ERR "CS5530: No 8bit DMA enabled\n");
- snd_cs5530_free(chip);
+ dev_err(card->dev, "No 8bit DMA enabled\n");
return -ENODEV;
}
@@ -203,53 +154,40 @@ static int __devinit snd_cs5530_create(struct snd_card *card,
else if (irq & 8)
irq = 10;
else {
- printk(KERN_ERR "CS5530: SoundBlaster IRQ not set\n");
- snd_cs5530_free(chip);
+ dev_err(card->dev, "SoundBlaster IRQ not set\n");
return -ENODEV;
}
- printk(KERN_INFO "CS5530: IRQ: %d DMA8: %d DMA16: %d\n", irq, dma8,
- dma16);
+ dev_info(card->dev, "IRQ: %d DMA8: %d DMA16: %d\n", irq, dma8, dma16);
err = snd_sbdsp_create(card, sb_base, irq, snd_sb16dsp_interrupt, dma8,
dma16, SB_HW_CS5530, &chip->sb);
if (err < 0) {
- printk(KERN_ERR "CS5530: Could not create SoundBlaster\n");
- snd_cs5530_free(chip);
+ dev_err(card->dev, "Could not create SoundBlaster\n");
return err;
}
- err = snd_sb16dsp_pcm(chip->sb, 0, &chip->sb->pcm);
+ err = snd_sb16dsp_pcm(chip->sb, 0);
if (err < 0) {
- printk(KERN_ERR "CS5530: Could not create PCM\n");
- snd_cs5530_free(chip);
+ dev_err(card->dev, "Could not create PCM\n");
return err;
}
err = snd_sbmixer_new(chip->sb);
if (err < 0) {
- printk(KERN_ERR "CS5530: Could not create Mixer\n");
- snd_cs5530_free(chip);
- return err;
- }
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0) {
- snd_cs5530_free(chip);
+ dev_err(card->dev, "Could not create Mixer\n");
return err;
}
- snd_card_set_dev(card, &pci->dev);
- *rchip = chip;
return 0;
}
-static int __devinit snd_cs5530_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_cs5530_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
- struct snd_cs5530 *chip = NULL;
+ struct snd_cs5530 *chip;
int err;
if (dev >= SNDRV_CARDS)
@@ -259,48 +197,32 @@ static int __devinit snd_cs5530_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
-
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- err = snd_cs5530_create(card, pci, &chip);
- if (err < 0) {
- snd_card_free(card);
+ err = snd_cs5530_create(card, pci);
+ if (err < 0)
return err;
- }
- strcpy(card->driver, "CS5530");
- strcpy(card->shortname, "CS5530 Audio");
+ strscpy(card->driver, "CS5530");
+ strscpy(card->shortname, "CS5530 Audio");
sprintf(card->longname, "%s at 0x%lx", card->shortname, chip->pci_base);
err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static struct pci_driver driver = {
- .name = "CS5530_Audio",
+static struct pci_driver cs5530_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_cs5530_ids,
.probe = snd_cs5530_probe,
- .remove = __devexit_p(snd_cs5530_remove),
};
-static int __init alsa_card_cs5530_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs5530_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs5530_init)
-module_exit(alsa_card_cs5530_exit)
-
+module_pci_driver(cs5530_driver);
diff --git a/sound/pci/cs5535audio/Makefile b/sound/pci/cs5535audio/Makefile
index ccc642269b9e..447e628751a2 100644
--- a/sound/pci/cs5535audio/Makefile
+++ b/sound/pci/cs5535audio/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for cs5535audio
#
snd-cs5535audio-y := cs5535audio.o cs5535audio_pcm.o
-snd-cs5535audio-$(CONFIG_PM) += cs5535audio_pm.o
+snd-cs5535audio-$(CONFIG_PM_SLEEP) += cs5535audio_pm.o
snd-cs5535audio-$(CONFIG_OLPC) += cs5535audio_olpc.o
# Toplevel Module Dependency
diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c
index afb803708416..0ebf6c02b1ef 100644
--- a/sound/pci/cs5535audio/cs5535audio.c
+++ b/sound/pci/cs5535audio/cs5535audio.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for audio on multifunction CS5535/6 companion device
* Copyright (C) Jaya Kumar
*
* Based on Jaroslav Kysela and Takashi Iwai's examples.
* This work was sponsored by CIS(M) Sdn Bhd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
@@ -26,8 +12,8 @@
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
-#include <asm/io.h>
+#include <linux/module.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -43,7 +29,7 @@ static char *ac97_quirk;
module_param(ac97_quirk, charp, 0444);
MODULE_PARM_DESC(ac97_quirk, "AC'97 board specific workarounds.");
-static struct ac97_quirk ac97_quirks[] __devinitdata = {
+static const struct ac97_quirk ac97_quirks[] = {
#if 0 /* Not yet confirmed if all 5536 boards are HP only */
{
.subvendor = PCI_VENDOR_ID_AMD,
@@ -57,7 +43,7 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " DRIVER_NAME);
@@ -66,7 +52,7 @@ MODULE_PARM_DESC(id, "ID string for " DRIVER_NAME);
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " DRIVER_NAME);
-static DEFINE_PCI_DEVICE_TABLE(snd_cs5535audio_ids) = {
+static const struct pci_device_id snd_cs5535audio_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_AUDIO) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO) },
{}
@@ -84,7 +70,8 @@ static void wait_till_cmd_acked(struct cs5535audio *cs5535au, unsigned long time
udelay(1);
} while (--timeout);
if (!timeout)
- snd_printk(KERN_ERR "Failure writing to cs5535 codec\n");
+ dev_err(cs5535au->card->dev,
+ "Failure writing to cs5535 codec\n");
}
static unsigned short snd_cs5535audio_codec_read(struct cs5535audio *cs5535au,
@@ -109,8 +96,9 @@ static unsigned short snd_cs5535audio_codec_read(struct cs5535audio *cs5535au,
udelay(1);
} while (--timeout);
if (!timeout)
- snd_printk(KERN_ERR "Failure reading codec reg 0x%x,"
- "Last value=0x%x\n", reg, val);
+ dev_err(cs5535au->card->dev,
+ "Failure reading codec reg 0x%x, Last value=0x%x\n",
+ reg, val);
return (unsigned short) val;
}
@@ -144,18 +132,19 @@ static unsigned short snd_cs5535audio_ac97_codec_read(struct snd_ac97 *ac97,
return snd_cs5535audio_codec_read(cs5535au, reg);
}
-static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
+static int snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
{
struct snd_card *card = cs5535au->card;
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_cs5535audio_ac97_codec_write,
.read = snd_cs5535audio_ac97_codec_read,
};
- if ((err = snd_ac97_bus(card, 0, &ops, NULL, &pbus)) < 0)
+ err = snd_ac97_bus(card, 0, &ops, NULL, &pbus);
+ if (err < 0)
return err;
memset(&ac97, 0, sizeof(ac97));
@@ -167,8 +156,9 @@ static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
/* set any OLPC-specific scaps */
olpc_prequirks(card, &ac97);
- if ((err = snd_ac97_mixer(pbus, &ac97, &cs5535au->ac97)) < 0) {
- snd_printk(KERN_ERR "mixer failed\n");
+ err = snd_ac97_mixer(pbus, &ac97, &cs5535au->ac97);
+ if (err < 0) {
+ dev_err(card->dev, "mixer failed\n");
return err;
}
@@ -176,7 +166,7 @@ static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
err = olpc_quirks(card, cs5535au->ac97);
if (err < 0) {
- snd_printk(KERN_ERR "olpc quirks failed\n");
+ dev_err(card->dev, "olpc quirks failed\n");
return err;
}
@@ -186,30 +176,28 @@ static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au)
static void process_bm0_irq(struct cs5535audio *cs5535au)
{
u8 bm_stat;
- spin_lock(&cs5535au->reg_lock);
- bm_stat = cs_readb(cs5535au, ACC_BM0_STATUS);
- spin_unlock(&cs5535au->reg_lock);
+
+ scoped_guard(spinlock, &cs5535au->reg_lock) {
+ bm_stat = cs_readb(cs5535au, ACC_BM0_STATUS);
+ }
if (bm_stat & EOP) {
- struct cs5535audio_dma *dma;
- dma = cs5535au->playback_substream->runtime->private_data;
snd_pcm_period_elapsed(cs5535au->playback_substream);
} else {
- snd_printk(KERN_ERR "unexpected bm0 irq src, bm_stat=%x\n",
- bm_stat);
+ dev_err(cs5535au->card->dev,
+ "unexpected bm0 irq src, bm_stat=%x\n",
+ bm_stat);
}
}
static void process_bm1_irq(struct cs5535audio *cs5535au)
{
u8 bm_stat;
- spin_lock(&cs5535au->reg_lock);
- bm_stat = cs_readb(cs5535au, ACC_BM1_STATUS);
- spin_unlock(&cs5535au->reg_lock);
- if (bm_stat & EOP) {
- struct cs5535audio_dma *dma;
- dma = cs5535au->capture_substream->runtime->private_data;
- snd_pcm_period_elapsed(cs5535au->capture_substream);
+
+ scoped_guard(spinlock, &cs5535au->reg_lock) {
+ bm_stat = cs_readb(cs5535au, ACC_BM1_STATUS);
}
+ if (bm_stat & EOP)
+ snd_pcm_period_elapsed(cs5535au->capture_substream);
}
static irqreturn_t snd_cs5535audio_interrupt(int irq, void *dev_id)
@@ -241,8 +229,9 @@ static irqreturn_t snd_cs5535audio_interrupt(int irq, void *dev_id)
process_bm1_irq(cs5535au);
break;
default:
- snd_printk(KERN_ERR "Unexpected irq src: "
- "0x%x\n", acc_irq_stat);
+ dev_err(cs5535au->card->dev,
+ "Unexpected irq src: 0x%x\n",
+ acc_irq_stat);
break;
}
}
@@ -250,52 +239,24 @@ static irqreturn_t snd_cs5535audio_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int snd_cs5535audio_free(struct cs5535audio *cs5535au)
+static void snd_cs5535audio_free(struct snd_card *card)
{
- synchronize_irq(cs5535au->irq);
- pci_set_power_state(cs5535au->pci, 3);
-
- if (cs5535au->irq >= 0)
- free_irq(cs5535au->irq, cs5535au);
-
- pci_release_regions(cs5535au->pci);
- pci_disable_device(cs5535au->pci);
- kfree(cs5535au);
- return 0;
-}
-
-static int snd_cs5535audio_dev_free(struct snd_device *device)
-{
- struct cs5535audio *cs5535au = device->device_data;
- return snd_cs5535audio_free(cs5535au);
+ olpc_quirks_cleanup();
}
-static int __devinit snd_cs5535audio_create(struct snd_card *card,
- struct pci_dev *pci,
- struct cs5535audio **rcs5535au)
+static int snd_cs5535audio_create(struct snd_card *card,
+ struct pci_dev *pci)
{
- struct cs5535audio *cs5535au;
-
+ struct cs5535audio *cs5535au = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_cs5535audio_dev_free,
- };
- *rcs5535au = NULL;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
- printk(KERN_WARNING "unable to get 32bit dma\n");
- err = -ENXIO;
- goto pcifail;
- }
-
- cs5535au = kzalloc(sizeof(*cs5535au), GFP_KERNEL);
- if (cs5535au == NULL) {
- err = -ENOMEM;
- goto pcifail;
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32))) {
+ dev_warn(card->dev, "unable to get 32bit dma\n");
+ return -ENXIO;
}
spin_lock_init(&cs5535au->reg_lock);
@@ -303,43 +264,27 @@ static int __devinit snd_cs5535audio_create(struct snd_card *card,
cs5535au->pci = pci;
cs5535au->irq = -1;
- if ((err = pci_request_regions(pci, "CS5535 Audio")) < 0) {
- kfree(cs5535au);
- goto pcifail;
- }
+ err = pcim_request_all_regions(pci, "CS5535 Audio");
+ if (err < 0)
+ return err;
cs5535au->port = pci_resource_start(pci, 0);
- if (request_irq(pci->irq, snd_cs5535audio_interrupt,
- IRQF_SHARED, "CS5535 Audio", cs5535au)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- err = -EBUSY;
- goto sndfail;
+ if (devm_request_irq(&pci->dev, pci->irq, snd_cs5535audio_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, cs5535au)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
+ return -EBUSY;
}
cs5535au->irq = pci->irq;
+ card->sync_irq = cs5535au->irq;
pci_set_master(pci);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
- cs5535au, &ops)) < 0)
- goto sndfail;
-
- snd_card_set_dev(card, &pci->dev);
-
- *rcs5535au = cs5535au;
return 0;
-
-sndfail: /* leave the device alive, just kill the snd */
- snd_cs5535audio_free(cs5535au);
- return err;
-
-pcifail:
- pci_disable_device(pci);
- return err;
}
-static int __devinit snd_cs5535audio_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_cs5535audio_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -353,72 +298,60 @@ static int __devinit snd_cs5535audio_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*cs5535au), &card);
if (err < 0)
return err;
+ cs5535au = card->private_data;
+ card->private_free = snd_cs5535audio_free;
- if ((err = snd_cs5535audio_create(card, pci, &cs5535au)) < 0)
- goto probefail_out;
-
- card->private_data = cs5535au;
+ err = snd_cs5535audio_create(card, pci);
+ if (err < 0)
+ return err;
- if ((err = snd_cs5535audio_mixer(cs5535au)) < 0)
- goto probefail_out;
+ err = snd_cs5535audio_mixer(cs5535au);
+ if (err < 0)
+ return err;
- if ((err = snd_cs5535audio_pcm(cs5535au)) < 0)
- goto probefail_out;
+ err = snd_cs5535audio_pcm(cs5535au);
+ if (err < 0)
+ return err;
- strcpy(card->driver, DRIVER_NAME);
+ strscpy(card->driver, DRIVER_NAME);
- strcpy(card->shortname, "CS5535 Audio");
+ strscpy(card->shortname, "CS5535 Audio");
sprintf(card->longname, "%s %s at 0x%lx, irq %i",
card->shortname, card->driver,
cs5535au->port, cs5535au->irq);
- if ((err = snd_card_register(card)) < 0)
- goto probefail_out;
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
pci_set_drvdata(pci, card);
dev++;
return 0;
-
-probefail_out:
- snd_card_free(card);
- return err;
}
-static void __devexit snd_cs5535audio_remove(struct pci_dev *pci)
+static int snd_cs5535audio_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- olpc_quirks_cleanup();
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_cs5535audio_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = DRIVER_NAME,
+static struct pci_driver cs5535audio_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_cs5535audio_ids,
.probe = snd_cs5535audio_probe,
- .remove = __devexit_p(snd_cs5535audio_remove),
-#ifdef CONFIG_PM
- .suspend = snd_cs5535audio_suspend,
- .resume = snd_cs5535audio_resume,
+#ifdef CONFIG_PM_SLEEP
+ .driver = {
+ .pm = &snd_cs5535audio_pm,
+ },
#endif
};
-static int __init alsa_card_cs5535audio_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs5535audio_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs5535audio_init)
-module_exit(alsa_card_cs5535audio_exit)
+module_pci_driver(cs5535audio_driver);
MODULE_AUTHOR("Jaya Kumar");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CS5535 Audio");
-MODULE_SUPPORTED_DEVICE("CS5535 Audio");
diff --git a/sound/pci/cs5535audio/cs5535audio.h b/sound/pci/cs5535audio/cs5535audio.h
index 51966d782a3c..d84620a0c26c 100644
--- a/sound/pci/cs5535audio/cs5535audio.h
+++ b/sound/pci/cs5535audio/cs5535audio.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SOUND_CS5535AUDIO_H
#define __SOUND_CS5535AUDIO_H
@@ -66,9 +67,9 @@ struct cs5535audio_dma_ops {
};
struct cs5535audio_dma_desc {
- u32 addr;
- u16 size;
- u16 ctlreserved;
+ __le32 addr;
+ __le16 size;
+ __le16 ctlreserved;
};
struct cs5535audio_dma {
@@ -94,16 +95,13 @@ struct cs5535audio {
struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS];
};
-#ifdef CONFIG_PM
-int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state);
-int snd_cs5535audio_resume(struct pci_dev *pci);
-#endif
+extern const struct dev_pm_ops snd_cs5535audio_pm;
#ifdef CONFIG_OLPC
-void __devinit olpc_prequirks(struct snd_card *card,
- struct snd_ac97_template *ac97);
-int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97);
-void __devexit olpc_quirks_cleanup(void);
+void olpc_prequirks(struct snd_card *card,
+ struct snd_ac97_template *ac97);
+int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97);
+void olpc_quirks_cleanup(void);
void olpc_analog_input(struct snd_ac97 *ac97, int on);
void olpc_mic_bias(struct snd_ac97 *ac97, int on);
@@ -136,7 +134,7 @@ static inline void olpc_capture_open(struct snd_ac97 *ac97) { }
static inline void olpc_capture_close(struct snd_ac97 *ac97) { }
#endif
-int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535audio);
+int snd_cs5535audio_pcm(struct cs5535audio *cs5535audio);
#endif /* __SOUND_CS5535AUDIO_H */
diff --git a/sound/pci/cs5535audio/cs5535audio_olpc.c b/sound/pci/cs5535audio/cs5535audio_olpc.c
index 50da49be9ae5..122170a410d9 100644
--- a/sound/pci/cs5535audio/cs5535audio_olpc.c
+++ b/sound/pci/cs5535audio/cs5535audio_olpc.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* OLPC XO-1 additional sound features
*
* Copyright © 2006 Jaya Kumar <jayakumar.lkml@gmail.com>
* Copyright © 2007-2008 Andres Salomon <dilinger@debian.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
*/
#include <sound/core.h>
#include <sound/info.h>
@@ -36,7 +32,8 @@ void olpc_analog_input(struct snd_ac97 *ac97, int on)
err = snd_ac97_update_bits(ac97, AC97_AD_TEST2,
1 << AC97_AD_HPFD_SHIFT, on << AC97_AD_HPFD_SHIFT);
if (err < 0) {
- snd_printk(KERN_ERR "setting High Pass Filter - %d\n", err);
+ dev_err(ac97->bus->card->dev,
+ "setting High Pass Filter - %d\n", err);
return;
}
@@ -58,7 +55,7 @@ void olpc_mic_bias(struct snd_ac97 *ac97, int on)
err = snd_ac97_update_bits(ac97, AC97_AD_MISC,
1 << AC97_AD_VREFD_SHIFT, on << AC97_AD_VREFD_SHIFT);
if (err < 0)
- snd_printk(KERN_ERR "setting MIC Bias - %d\n", err);
+ dev_err(ac97->bus->card->dev, "setting MIC Bias - %d\n", err);
}
static int olpc_dc_info(struct snd_kcontrol *kctl,
@@ -114,7 +111,7 @@ static int olpc_mic_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
return 1;
}
-static struct snd_kcontrol_new olpc_cs5535audio_ctls[] __devinitdata = {
+static const struct snd_kcontrol_new olpc_cs5535audio_ctls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "DC Mode Enable",
@@ -133,8 +130,8 @@ static struct snd_kcontrol_new olpc_cs5535audio_ctls[] __devinitdata = {
},
};
-void __devinit olpc_prequirks(struct snd_card *card,
- struct snd_ac97_template *ac97)
+void olpc_prequirks(struct snd_card *card,
+ struct snd_ac97_template *ac97)
{
if (!machine_is_olpc())
return;
@@ -144,7 +141,7 @@ void __devinit olpc_prequirks(struct snd_card *card,
ac97->scaps |= AC97_SCAP_INV_EAPD;
}
-int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
+int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
{
struct snd_ctl_elem_id elem;
int i, err;
@@ -153,7 +150,7 @@ int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
return 0;
if (gpio_request(OLPC_GPIO_MIC_AC, DRV_NAME)) {
- printk(KERN_ERR DRV_NAME ": unable to allocate MIC GPIO\n");
+ dev_err(card->dev, "unable to allocate MIC GPIO\n");
return -EIO;
}
gpio_direction_output(OLPC_GPIO_MIC_AC, 0);
@@ -161,23 +158,21 @@ int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
/* drop the original AD1888 HPF control */
memset(&elem, 0, sizeof(elem));
elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strncpy(elem.name, "High Pass Filter Enable", sizeof(elem.name));
+ strscpy(elem.name, "High Pass Filter Enable", sizeof(elem.name));
snd_ctl_remove_id(card, &elem);
/* drop the original V_REFOUT control */
memset(&elem, 0, sizeof(elem));
elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strncpy(elem.name, "V_REFOUT Enable", sizeof(elem.name));
+ strscpy(elem.name, "V_REFOUT Enable", sizeof(elem.name));
snd_ctl_remove_id(card, &elem);
/* add the OLPC-specific controls */
for (i = 0; i < ARRAY_SIZE(olpc_cs5535audio_ctls); i++) {
err = snd_ctl_add(card, snd_ctl_new1(&olpc_cs5535audio_ctls[i],
ac97->private_data));
- if (err < 0) {
- gpio_free(OLPC_GPIO_MIC_AC);
+ if (err < 0)
return err;
- }
}
/* turn off the mic by default */
@@ -185,7 +180,8 @@ int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
return 0;
}
-void __devexit olpc_quirks_cleanup(void)
+void olpc_quirks_cleanup(void)
{
- gpio_free(OLPC_GPIO_MIC_AC);
+ if (machine_is_olpc())
+ gpio_free(OLPC_GPIO_MIC_AC);
}
diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c
index f16bc8aad6ed..48b99a07e3bc 100644
--- a/sound/pci/cs5535audio/cs5535audio_pcm.c
+++ b/sound/pci/cs5535audio/cs5535audio_pcm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for audio on multifunction CS5535 companion device
* Copyright (C) Jaya Kumar
@@ -5,20 +6,6 @@
* Based on Jaroslav Kysela and Takashi Iwai's examples.
* This work was sponsored by CIS(M) Sdn Bhd.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
* todo: add be fmt support, spdif, pm
*/
@@ -33,7 +20,7 @@
#include <sound/ac97_codec.h>
#include "cs5535audio.h"
-static struct snd_pcm_hardware snd_cs5535audio_playback =
+static const struct snd_pcm_hardware snd_cs5535audio_playback =
{
.info = (
SNDRV_PCM_INFO_MMAP |
@@ -62,7 +49,7 @@ static struct snd_pcm_hardware snd_cs5535audio_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_cs5535audio_capture =
+static const struct snd_pcm_hardware snd_cs5535audio_capture =
{
.info = (
SNDRV_PCM_INFO_MMAP |
@@ -100,8 +87,9 @@ static int snd_cs5535audio_playback_open(struct snd_pcm_substream *substream)
snd_pcm_limit_hw_rates(runtime);
cs5535au->playback_substream = substream;
runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK]);
- if ((err = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ err = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
return err;
return 0;
@@ -122,7 +110,7 @@ static int cs5535audio_build_dma_packets(struct cs5535audio *cs5535au,
unsigned int period_bytes)
{
unsigned int i;
- u32 addr, desc_addr, jmpprd_addr;
+ u32 addr, jmpprd_addr;
struct cs5535audio_dma_desc *lastdesc;
if (periods > CS5535AUDIO_MAX_DESCRIPTORS)
@@ -130,7 +118,7 @@ static int cs5535audio_build_dma_packets(struct cs5535audio *cs5535au,
if (dma->desc_buf.area == NULL) {
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(cs5535au->pci),
+ &cs5535au->pci->dev,
CS5535AUDIO_DESC_LIST_SIZE+1,
&dma->desc_buf) < 0)
return -ENOMEM;
@@ -141,33 +129,30 @@ static int cs5535audio_build_dma_packets(struct cs5535audio *cs5535au,
return 0;
/* the u32 cast is okay because in snd*create we successfully told
- pci alloc that we're only 32 bit capable so the uppper will be 0 */
+ pci alloc that we're only 32 bit capable so the upper will be 0 */
addr = (u32) substream->runtime->dma_addr;
- desc_addr = (u32) dma->desc_buf.addr;
for (i = 0; i < periods; i++) {
struct cs5535audio_dma_desc *desc =
&((struct cs5535audio_dma_desc *) dma->desc_buf.area)[i];
desc->addr = cpu_to_le32(addr);
- desc->size = cpu_to_le32(period_bytes);
- desc->ctlreserved = cpu_to_le32(PRD_EOP);
- desc_addr += sizeof(struct cs5535audio_dma_desc);
+ desc->size = cpu_to_le16(period_bytes);
+ desc->ctlreserved = cpu_to_le16(PRD_EOP);
addr += period_bytes;
}
/* we reserved one dummy descriptor at the end to do the PRD jump */
lastdesc = &((struct cs5535audio_dma_desc *) dma->desc_buf.area)[periods];
lastdesc->addr = cpu_to_le32((u32) dma->desc_buf.addr);
lastdesc->size = 0;
- lastdesc->ctlreserved = cpu_to_le32(PRD_JMP);
- jmpprd_addr = cpu_to_le32(lastdesc->addr +
- (sizeof(struct cs5535audio_dma_desc)*periods));
+ lastdesc->ctlreserved = cpu_to_le16(PRD_JMP);
+ jmpprd_addr = (u32)dma->desc_buf.addr +
+ sizeof(struct cs5535audio_dma_desc) * periods;
dma->substream = substream;
dma->period_bytes = period_bytes;
dma->periods = periods;
- spin_lock_irq(&cs5535au->reg_lock);
+ guard(spinlock_irq)(&cs5535au->reg_lock);
dma->ops->disable_dma(cs5535au);
dma->ops->setup_prd(cs5535au, jmpprd_addr);
- spin_unlock_irq(&cs5535au->reg_lock);
return 0;
}
@@ -249,10 +234,6 @@ static int snd_cs5535audio_hw_params(struct snd_pcm_substream *substream,
struct cs5535audio_dma *dma = substream->runtime->private_data;
int err;
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
dma->buf_addr = substream->runtime->dma_addr;
dma->buf_bytes = params_buffer_bytes(hw_params);
@@ -280,7 +261,7 @@ static int snd_cs5535audio_hw_free(struct snd_pcm_substream *substream)
dma->pcm_open_flag = 0;
}
cs5535audio_clear_dma_packets(cs5535au, dma, substream);
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static int snd_cs5535audio_playback_prepare(struct snd_pcm_substream *substream)
@@ -294,9 +275,8 @@ static int snd_cs5535audio_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct cs5535audio *cs5535au = snd_pcm_substream_chip(substream);
struct cs5535audio_dma *dma = substream->runtime->private_data;
- int err = 0;
- spin_lock(&cs5535au->reg_lock);
+ guard(spinlock)(&cs5535au->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
dma->ops->pause_dma(cs5535au);
@@ -317,12 +297,10 @@ static int snd_cs5535audio_trigger(struct snd_pcm_substream *substream, int cmd)
dma->ops->disable_dma(cs5535au);
break;
default:
- snd_printk(KERN_ERR "unhandled trigger\n");
- err = -EINVAL;
- break;
+ dev_err(cs5535au->card->dev, "unhandled trigger\n");
+ return -EINVAL;
}
- spin_unlock(&cs5535au->reg_lock);
- return err;
+ return 0;
}
static snd_pcm_uframes_t snd_cs5535audio_pcm_pointer(struct snd_pcm_substream
@@ -335,13 +313,13 @@ static snd_pcm_uframes_t snd_cs5535audio_pcm_pointer(struct snd_pcm_substream
dma = substream->runtime->private_data;
curdma = dma->ops->read_dma_pntr(cs5535au);
if (curdma < dma->buf_addr) {
- snd_printk(KERN_ERR "curdma=%x < %x bufaddr.\n",
+ dev_err(cs5535au->card->dev, "curdma=%x < %x bufaddr.\n",
curdma, dma->buf_addr);
return 0;
}
curdma -= dma->buf_addr;
if (curdma >= dma->buf_bytes) {
- snd_printk(KERN_ERR "diff=%x >= %x buf_bytes.\n",
+ dev_err(cs5535au->card->dev, "diff=%x >= %x buf_bytes.\n",
curdma, dma->buf_bytes);
return 0;
}
@@ -359,8 +337,9 @@ static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream)
snd_pcm_limit_hw_rates(runtime);
cs5535au->capture_substream = substream;
runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE]);
- if ((err = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ err = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
return err;
olpc_capture_open(cs5535au->ac97);
return 0;
@@ -380,10 +359,9 @@ static int snd_cs5535audio_capture_prepare(struct snd_pcm_substream *substream)
substream->runtime->rate);
}
-static struct snd_pcm_ops snd_cs5535audio_playback_ops = {
+static const struct snd_pcm_ops snd_cs5535audio_playback_ops = {
.open = snd_cs5535audio_playback_open,
.close = snd_cs5535audio_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs5535audio_hw_params,
.hw_free = snd_cs5535audio_hw_free,
.prepare = snd_cs5535audio_playback_prepare,
@@ -391,10 +369,9 @@ static struct snd_pcm_ops snd_cs5535audio_playback_ops = {
.pointer = snd_cs5535audio_pcm_pointer,
};
-static struct snd_pcm_ops snd_cs5535audio_capture_ops = {
+static const struct snd_pcm_ops snd_cs5535audio_capture_ops = {
.open = snd_cs5535audio_capture_open,
.close = snd_cs5535audio_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_cs5535audio_hw_params,
.hw_free = snd_cs5535audio_hw_free,
.prepare = snd_cs5535audio_capture_prepare,
@@ -402,7 +379,7 @@ static struct snd_pcm_ops snd_cs5535audio_capture_ops = {
.pointer = snd_cs5535audio_pcm_pointer,
};
-static struct cs5535audio_dma_ops snd_cs5535audio_playback_dma_ops = {
+static const struct cs5535audio_dma_ops snd_cs5535audio_playback_dma_ops = {
.type = CS5535AUDIO_DMA_PLAYBACK,
.enable_dma = cs5535audio_playback_enable_dma,
.disable_dma = cs5535audio_playback_disable_dma,
@@ -412,7 +389,7 @@ static struct cs5535audio_dma_ops snd_cs5535audio_playback_dma_ops = {
.read_dma_pntr = cs5535audio_playback_read_dma_pntr,
};
-static struct cs5535audio_dma_ops snd_cs5535audio_capture_dma_ops = {
+static const struct cs5535audio_dma_ops snd_cs5535audio_capture_dma_ops = {
.type = CS5535AUDIO_DMA_CAPTURE,
.enable_dma = cs5535audio_capture_enable_dma,
.disable_dma = cs5535audio_capture_disable_dma,
@@ -422,7 +399,7 @@ static struct cs5535audio_dma_ops snd_cs5535audio_capture_dma_ops = {
.read_dma_pntr = cs5535audio_capture_read_dma_pntr,
};
-int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535au)
+int snd_cs5535audio_pcm(struct cs5535audio *cs5535au)
{
struct snd_pcm *pcm;
int err;
@@ -442,11 +419,11 @@ int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535au)
pcm->private_data = cs5535au;
pcm->info_flags = 0;
- strcpy(pcm->name, "CS5535 Audio");
+ strscpy(pcm->name, "CS5535 Audio");
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(cs5535au->pci),
- 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &cs5535au->pci->dev,
+ 64*1024, 128*1024);
cs5535au->pcm = pcm;
return 0;
diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c
index a3301cc4ab82..90fb73a9d9b2 100644
--- a/sound/pci/cs5535audio/cs5535audio_pm.c
+++ b/sound/pci/cs5535audio/cs5535audio_pm.c
@@ -1,21 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Power management for audio on multifunction CS5535 companion device
* Copyright (C) Jaya Kumar
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
@@ -55,14 +41,13 @@ static void snd_cs5535audio_stop_hardware(struct cs5535audio *cs5535au)
}
-int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state)
+static int __maybe_unused snd_cs5535audio_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct cs5535audio *cs5535au = card->private_data;
int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(cs5535au->pcm);
snd_ac97_suspend(cs5535au->ac97);
for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) {
struct cs5535audio_dma *dma = &cs5535au->dmas[i];
@@ -71,39 +56,17 @@ int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state)
}
/* save important regs, then disable aclink in hw */
snd_cs5535audio_stop_hardware(cs5535au);
-
- if (pci_save_state(pci)) {
- printk(KERN_ERR "cs5535audio: pci_save_state failed!\n");
- return -EIO;
- }
- pci_disable_device(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-int snd_cs5535audio_resume(struct pci_dev *pci)
+static int __maybe_unused snd_cs5535audio_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct cs5535audio *cs5535au = card->private_data;
u32 tmp;
int timeout;
int i;
- pci_set_power_state(pci, PCI_D0);
- if (pci_restore_state(pci) < 0) {
- printk(KERN_ERR "cs5535audio: pci_restore_state failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "cs5535audio: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
/* set LNK_WRM_RST to reset AC link */
cs_writel(cs5535au, ACC_CODEC_CNTL, ACC_CODEC_CNTL_LNK_WRM_RST);
@@ -116,7 +79,7 @@ int snd_cs5535audio_resume(struct pci_dev *pci)
} while (--timeout);
if (!timeout)
- snd_printk(KERN_ERR "Failure getting AC Link ready\n");
+ dev_err(cs5535au->card->dev, "Failure getting AC Link ready\n");
/* set up rate regs, dma. actual initiation is done in trig */
for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) {
@@ -134,3 +97,4 @@ int snd_cs5535audio_resume(struct pci_dev *pci)
return 0;
}
+SIMPLE_DEV_PM_OPS(snd_cs5535audio_pm, snd_cs5535audio_suspend, snd_cs5535audio_resume);
diff --git a/sound/pci/ctxfi/Makefile b/sound/pci/ctxfi/Makefile
index 15075f89e98a..ff2b1cba3a3c 100644
--- a/sound/pci/ctxfi/Makefile
+++ b/sound/pci/ctxfi/Makefile
@@ -1,4 +1,5 @@
-snd-ctxfi-objs := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \
+# SPDX-License-Identifier: GPL-2.0-only
+snd-ctxfi-y := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \
ctsrc.o ctamixer.o ctdaio.o ctimap.o cthardware.o cttimer.o \
cthw20k2.o cthw20k1.o
diff --git a/sound/pci/ctxfi/ct20k1reg.h b/sound/pci/ctxfi/ct20k1reg.h
index f2e34e3f27ee..05bb006c0f4c 100644
--- a/sound/pci/ctxfi/ct20k1reg.h
+++ b/sound/pci/ctxfi/ct20k1reg.h
@@ -1,13 +1,10 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
- *
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
*/
#ifndef CT20K1REG_H
-#define CT20k1REG_H
+#define CT20K1REG_H
/* 20k1 registers */
#define DSPXRAM_START 0x000000
@@ -632,5 +629,3 @@
#define I2SD_R 0x19L
#endif /* CT20K1REG_H */
-
-
diff --git a/sound/pci/ctxfi/ct20k2reg.h b/sound/pci/ctxfi/ct20k2reg.h
index e0394e3996e8..02f67828eabe 100644
--- a/sound/pci/ctxfi/ct20k2reg.h
+++ b/sound/pci/ctxfi/ct20k2reg.h
@@ -1,9 +1,6 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
- *
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
*/
#ifndef _20K2REGISTERS_H_
@@ -55,6 +52,7 @@
/* GPIO Registers */
#define GPIO_DATA 0x1B7020
#define GPIO_CTRL 0x1B7024
+#define GPIO_EXT_DATA 0x1B70A0
/* Virtual memory registers */
#define VMEM_PTPAL 0x1C6300 /* 0x1C6300 + (16 * Chn) */
diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c
index fee35cfc0c7f..bb4658592636 100644
--- a/sound/pci/ctxfi/ctamixer.c
+++ b/sound/pci/ctxfi/ctamixer.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctamixer.c
*
* @Brief
@@ -13,7 +10,6 @@
*
* @Author Liu Chun
* @Date May 21 2008
- *
*/
#include "ctamixer.h"
@@ -27,16 +23,15 @@
#define BLANK_SLOT 4094
-static int amixer_master(struct rsc *rsc)
+static void amixer_master(struct rsc *rsc)
{
rsc->conj = 0;
- return rsc->idx = container_of(rsc, struct amixer, rsc)->idx[0];
+ rsc->idx = container_of(rsc, struct amixer, rsc)->idx[0];
}
-static int amixer_next_conj(struct rsc *rsc)
+static void amixer_next_conj(struct rsc *rsc)
{
rsc->conj++;
- return container_of(rsc, struct amixer, rsc)->idx[rsc->conj];
}
static int amixer_index(const struct rsc *rsc)
@@ -49,7 +44,7 @@ static int amixer_output_slot(const struct rsc *rsc)
return (amixer_index(rsc) << 4) + 0x4;
}
-static struct rsc_ops amixer_basic_rsc_ops = {
+static const struct rsc_ops amixer_basic_rsc_ops = {
.master = amixer_master,
.next_conj = amixer_next_conj,
.index = amixer_index,
@@ -186,7 +181,7 @@ static int amixer_setup(struct amixer *amixer, struct rsc *input,
return 0;
}
-static struct amixer_rsc_ops amixer_ops = {
+static const struct amixer_rsc_ops amixer_ops = {
.set_input = amixer_set_input,
.set_invalid_squash = amixer_set_invalid_squash,
.set_scale = amixer_set_y,
@@ -236,7 +231,6 @@ static int get_amixer_rsc(struct amixer_mgr *mgr,
int err, i;
unsigned int idx;
struct amixer *amixer;
- unsigned long flags;
*ramixer = NULL;
@@ -248,17 +242,18 @@ static int get_amixer_rsc(struct amixer_mgr *mgr,
/* Check whether there are sufficient
* amixer resources to meet request. */
err = 0;
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- for (i = 0; i < desc->msr; i++) {
- err = mgr_get_resource(&mgr->mgr, 1, &idx);
- if (err)
- break;
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ for (i = 0; i < desc->msr; i++) {
+ err = mgr_get_resource(&mgr->mgr, 1, &idx);
+ if (err)
+ break;
- amixer->idx[i] = idx;
+ amixer->idx[i] = idx;
+ }
}
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
if (err) {
- printk(KERN_ERR "ctxfi: Can't meet AMIXER resource request!\n");
+ dev_err(mgr->card->dev,
+ "Can't meet AMIXER resource request!\n");
goto error;
}
@@ -271,32 +266,30 @@ static int get_amixer_rsc(struct amixer_mgr *mgr,
return 0;
error:
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- for (i--; i >= 0; i--)
- mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ for (i--; i >= 0; i--)
+ mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]);
+ }
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
kfree(amixer);
return err;
}
static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer)
{
- unsigned long flags;
int i;
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- for (i = 0; i < amixer->rsc.msr; i++)
- mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]);
-
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ for (i = 0; i < amixer->rsc.msr; i++)
+ mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]);
+ }
amixer_rsc_uninit(amixer);
kfree(amixer);
return 0;
}
-int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr)
+int amixer_mgr_create(struct hw *hw, void **ramixer_mgr)
{
int err;
struct amixer_mgr *amixer_mgr;
@@ -314,6 +307,7 @@ int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr)
amixer_mgr->get_amixer = get_amixer_rsc;
amixer_mgr->put_amixer = put_amixer_rsc;
+ amixer_mgr->card = hw->card;
*ramixer_mgr = amixer_mgr;
@@ -324,8 +318,9 @@ error:
return err;
}
-int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr)
+int amixer_mgr_destroy(void *ptr)
{
+ struct amixer_mgr *amixer_mgr = ptr;
rsc_mgr_uninit(&amixer_mgr->mgr);
kfree(amixer_mgr);
return 0;
@@ -333,16 +328,15 @@ int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr)
/* SUM resource management */
-static int sum_master(struct rsc *rsc)
+static void sum_master(struct rsc *rsc)
{
rsc->conj = 0;
- return rsc->idx = container_of(rsc, struct sum, rsc)->idx[0];
+ rsc->idx = container_of(rsc, struct sum, rsc)->idx[0];
}
-static int sum_next_conj(struct rsc *rsc)
+static void sum_next_conj(struct rsc *rsc)
{
rsc->conj++;
- return container_of(rsc, struct sum, rsc)->idx[rsc->conj];
}
static int sum_index(const struct rsc *rsc)
@@ -355,7 +349,7 @@ static int sum_output_slot(const struct rsc *rsc)
return (sum_index(rsc) << 4) + 0xc;
}
-static struct rsc_ops sum_basic_rsc_ops = {
+static const struct rsc_ops sum_basic_rsc_ops = {
.master = sum_master,
.next_conj = sum_next_conj,
.index = sum_index,
@@ -390,7 +384,6 @@ static int get_sum_rsc(struct sum_mgr *mgr,
int err, i;
unsigned int idx;
struct sum *sum;
- unsigned long flags;
*rsum = NULL;
@@ -401,17 +394,18 @@ static int get_sum_rsc(struct sum_mgr *mgr,
/* Check whether there are sufficient sum resources to meet request. */
err = 0;
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- for (i = 0; i < desc->msr; i++) {
- err = mgr_get_resource(&mgr->mgr, 1, &idx);
- if (err)
- break;
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ for (i = 0; i < desc->msr; i++) {
+ err = mgr_get_resource(&mgr->mgr, 1, &idx);
+ if (err)
+ break;
- sum->idx[i] = idx;
+ sum->idx[i] = idx;
+ }
}
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
if (err) {
- printk(KERN_ERR "ctxfi: Can't meet SUM resource request!\n");
+ dev_err(mgr->card->dev,
+ "Can't meet SUM resource request!\n");
goto error;
}
@@ -424,32 +418,29 @@ static int get_sum_rsc(struct sum_mgr *mgr,
return 0;
error:
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- for (i--; i >= 0; i--)
- mgr_put_resource(&mgr->mgr, 1, sum->idx[i]);
-
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ for (i--; i >= 0; i--)
+ mgr_put_resource(&mgr->mgr, 1, sum->idx[i]);
+ }
kfree(sum);
return err;
}
static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum)
{
- unsigned long flags;
int i;
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- for (i = 0; i < sum->rsc.msr; i++)
- mgr_put_resource(&mgr->mgr, 1, sum->idx[i]);
-
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ for (i = 0; i < sum->rsc.msr; i++)
+ mgr_put_resource(&mgr->mgr, 1, sum->idx[i]);
+ }
sum_rsc_uninit(sum);
kfree(sum);
return 0;
}
-int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr)
+int sum_mgr_create(struct hw *hw, void **rsum_mgr)
{
int err;
struct sum_mgr *sum_mgr;
@@ -467,6 +458,7 @@ int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr)
sum_mgr->get_sum = get_sum_rsc;
sum_mgr->put_sum = put_sum_rsc;
+ sum_mgr->card = hw->card;
*rsum_mgr = sum_mgr;
@@ -477,8 +469,9 @@ error:
return err;
}
-int sum_mgr_destroy(struct sum_mgr *sum_mgr)
+int sum_mgr_destroy(void *ptr)
{
+ struct sum_mgr *sum_mgr = ptr;
rsc_mgr_uninit(&sum_mgr->mgr);
kfree(sum_mgr);
return 0;
diff --git a/sound/pci/ctxfi/ctamixer.h b/sound/pci/ctxfi/ctamixer.h
index cc49e5ab4750..8fc017da6bda 100644
--- a/sound/pci/ctxfi/ctamixer.h
+++ b/sound/pci/ctxfi/ctamixer.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctamixer.h
*
* @Brief
@@ -13,7 +10,6 @@
*
* @Author Liu Chun
* @Date May 21 2008
- *
*/
#ifndef CTAMIXER_H
@@ -21,6 +17,7 @@
#include "ctresource.h"
#include <linux/spinlock.h>
+#include <sound/core.h>
/* Define the descriptor of a summation node resource */
struct sum {
@@ -35,6 +32,7 @@ struct sum_desc {
struct sum_mgr {
struct rsc_mgr mgr; /* Basic resource manager info */
+ struct snd_card *card; /* pointer to this card */
spinlock_t mgr_lock;
/* request one sum resource */
@@ -45,8 +43,8 @@ struct sum_mgr {
};
/* Constructor and destructor of daio resource manager */
-int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr);
-int sum_mgr_destroy(struct sum_mgr *sum_mgr);
+int sum_mgr_create(struct hw *hw, void **ptr);
+int sum_mgr_destroy(void *ptr);
/* Define the descriptor of a amixer resource */
struct amixer_rsc_ops;
@@ -56,7 +54,7 @@ struct amixer {
unsigned char idx[8];
struct rsc *input; /* pointer to a resource acting as source */
struct sum *sum; /* Put amixer output to this summation node */
- struct amixer_rsc_ops *ops; /* AMixer specific operations */
+ const struct amixer_rsc_ops *ops; /* AMixer specific operations */
};
struct amixer_rsc_ops {
@@ -79,6 +77,7 @@ struct amixer_desc {
struct amixer_mgr {
struct rsc_mgr mgr; /* Basic resource manager info */
+ struct snd_card *card; /* pointer to this card */
spinlock_t mgr_lock;
/* request one amixer resource */
@@ -90,7 +89,7 @@ struct amixer_mgr {
};
/* Constructor and destructor of amixer resource manager */
-int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr);
-int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr);
+int amixer_mgr_create(struct hw *hw, void **ramixer_mgr);
+int amixer_mgr_destroy(void *amixer_mgr);
#endif /* CTAMIXER_H */
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
index 1bff80cde0a2..227d8c8490e1 100644
--- a/sound/pci/ctxfi/ctatc.c
+++ b/sound/pci/ctxfi/ctatc.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctatc.c
*
* @Brief
@@ -18,7 +15,6 @@
#include "ctatc.h"
#include "ctpcm.h"
#include "ctmixer.h"
-#include "cthardware.h"
#include "ctsrc.h"
#include "ctamixer.h"
#include "ctdaio.h"
@@ -29,8 +25,10 @@
#include <sound/control.h>
#include <sound/asoundef.h>
+#define NUM_ATC_SRCS 6
+#define NUM_ATC_PCM (2 * 4)
+
#define MONO_SUM_SCALE 0x19a8 /* 2^(-0.5) in 14-bit floating format */
-#define DAIONUM 7
#define MAX_MULTI_CHN 8
#define IEC958_DEFAULT_CON ((IEC958_AES0_NONAUDIO \
@@ -40,7 +38,8 @@
| (0x10 << 16) \
| ((IEC958_AES3_CON_FS_48000) << 24))
-static struct snd_pci_quirk __devinitdata subsys_20k1_list[] = {
+static const struct snd_pci_quirk subsys_20k1_list[] = {
+ SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0021, "SB046x", CTSB046X),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0022, "SB055x", CTSB055X),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x002f, "SB055x", CTSB055X),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0029, "SB073x", CTSB073X),
@@ -50,9 +49,11 @@ static struct snd_pci_quirk __devinitdata subsys_20k1_list[] = {
{ } /* terminator */
};
-static struct snd_pci_quirk __devinitdata subsys_20k2_list[] = {
+static const struct snd_pci_quirk subsys_20k2_list[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB0760,
"SB0760", CTSB0760),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB1270,
+ "SB1270", CTSB1270),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08801,
"SB0880", CTSB0880),
SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08802,
@@ -62,11 +63,13 @@ static struct snd_pci_quirk __devinitdata subsys_20k2_list[] = {
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0xf000,
PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "HENDRIX",
CTHENDRIX),
+ SND_PCI_QUIRK(0x160b, 0x0101, "OK0010", CTOK0010),
{ } /* terminator */
};
static const char *ct_subsys_name[NUM_CTCARDS] = {
/* 20k1 models */
+ [CTSB046X] = "SB046x",
[CTSB055X] = "SB055x",
[CTSB073X] = "SB073x",
[CTUAA] = "UAA",
@@ -75,6 +78,8 @@ static const char *ct_subsys_name[NUM_CTCARDS] = {
[CTSB0760] = "SB076x",
[CTHENDRIX] = "Hendrix",
[CTSB0880] = "SB0880",
+ [CTSB1270] = "SB1270",
+ [CTOK0010] = "OK0010",
[CT20K2_UNKNOWN] = "Unknown",
};
@@ -105,23 +110,20 @@ static struct {
.public_name = "Mixer"}
};
-typedef int (*create_t)(void *, void **);
-typedef int (*destroy_t)(void *);
-
static struct {
- int (*create)(void *hw, void **rmgr);
+ int (*create)(struct hw *hw, void **rmgr);
int (*destroy)(void *mgr);
} rsc_mgr_funcs[NUM_RSCTYP] = {
- [SRC] = { .create = (create_t)src_mgr_create,
- .destroy = (destroy_t)src_mgr_destroy },
- [SRCIMP] = { .create = (create_t)srcimp_mgr_create,
- .destroy = (destroy_t)srcimp_mgr_destroy },
- [AMIXER] = { .create = (create_t)amixer_mgr_create,
- .destroy = (destroy_t)amixer_mgr_destroy },
- [SUM] = { .create = (create_t)sum_mgr_create,
- .destroy = (destroy_t)sum_mgr_destroy },
- [DAIO] = { .create = (create_t)daio_mgr_create,
- .destroy = (destroy_t)daio_mgr_destroy }
+ [SRC] = { .create = src_mgr_create,
+ .destroy = src_mgr_destroy },
+ [SRCIMP] = { .create = srcimp_mgr_create,
+ .destroy = srcimp_mgr_destroy },
+ [AMIXER] = { .create = amixer_mgr_create,
+ .destroy = amixer_mgr_destroy },
+ [SUM] = { .create = sum_mgr_create,
+ .destroy = sum_mgr_destroy },
+ [DAIO] = { .create = daio_mgr_create,
+ .destroy = daio_mgr_destroy }
};
static int
@@ -170,7 +172,8 @@ static unsigned long atc_get_ptp_phys(struct ct_atc *atc, int index)
return atc->vm->get_ptp_phys(atc->vm, index);
}
-static unsigned int convert_format(snd_pcm_format_t snd_format)
+static unsigned int convert_format(snd_pcm_format_t snd_format,
+ struct snd_card *card)
{
switch (snd_format) {
case SNDRV_PCM_FORMAT_U8:
@@ -184,7 +187,7 @@ static unsigned int convert_format(snd_pcm_format_t snd_format)
case SNDRV_PCM_FORMAT_FLOAT_LE:
return SRC_SF_F32;
default:
- printk(KERN_ERR "ctxfi: not recognized snd format is %d \n",
+ dev_err(card->dev, "not recognized snd format is %d\n",
snd_format);
return SRC_SF_S16;
}
@@ -267,12 +270,13 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
src = apcm->src;
src->ops->set_pitch(src, pitch);
src->ops->set_rom(src, select_rom(pitch));
- src->ops->set_sf(src, convert_format(apcm->substream->runtime->format));
+ src->ops->set_sf(src, convert_format(apcm->substream->runtime->format,
+ atc->card));
src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL));
/* Get AMIXER resource */
n_amixer = (n_amixer < 2) ? 2 : n_amixer;
- apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
+ apcm->amixers = kcalloc(n_amixer, sizeof(void *), GFP_KERNEL);
if (!apcm->amixers) {
err = -ENOMEM;
goto error1;
@@ -296,10 +300,10 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
src = apcm->src;
for (i = 0; i < n_amixer; i++) {
amixer = apcm->amixers[i];
- mutex_lock(&atc->atc_mutex);
- amixer->ops->setup(amixer, &src->rsc,
- INIT_VOL, atc->pcm[i+device*2]);
- mutex_unlock(&atc->atc_mutex);
+ scoped_guard(mutex, &atc->atc_mutex) {
+ amixer->ops->setup(amixer, &src->rsc,
+ INIT_VOL, atc->pcm[i+device*2]);
+ }
src = src->ops->next_interleave(src);
if (!src)
src = apcm->src;
@@ -434,6 +438,13 @@ atc_pcm_playback_position(struct ct_atc *atc, struct ct_atc_pcm *apcm)
return 0;
position = src->ops->get_ca(src);
+ if (position < apcm->vm_block->addr) {
+ dev_dbg(atc->card->dev,
+ "bad ca - ca=0x%08x, vba=0x%08x, vbs=0x%08x\n",
+ position, apcm->vm_block->addr, apcm->vm_block->size);
+ position = apcm->vm_block->addr;
+ }
+
size = apcm->vm_block->size;
max_cisz = src->multi * src->rsc.msr;
max_cisz = 128 * (max_cisz < 8 ? max_cisz : 8);
@@ -459,12 +470,12 @@ static void setup_src_node_conf(struct ct_atc *atc, struct ct_atc_pcm *apcm,
apcm->substream->runtime->rate);
*n_srcc = 0;
- if (1 == atc->msr) {
+ if (1 == atc->msr) { /* FIXME: do we really need SRC here if pitch==1 */
*n_srcc = apcm->substream->runtime->channels;
conf[0].pitch = pitch;
conf[0].mix_msr = conf[0].imp_msr = conf[0].msr = 1;
conf[0].vo = 1;
- } else if (2 == atc->msr) {
+ } else if (2 <= atc->msr) {
if (0x8000000 < pitch) {
/* Need two-stage SRCs, SRCIMPs and
* AMIXERs for converting format */
@@ -533,18 +544,18 @@ atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
}
if (n_srcc) {
- apcm->srccs = kzalloc(sizeof(void *)*n_srcc, GFP_KERNEL);
+ apcm->srccs = kcalloc(n_srcc, sizeof(void *), GFP_KERNEL);
if (!apcm->srccs)
return -ENOMEM;
}
if (n_amixer) {
- apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
+ apcm->amixers = kcalloc(n_amixer, sizeof(void *), GFP_KERNEL);
if (!apcm->amixers) {
err = -ENOMEM;
goto error1;
}
}
- apcm->srcimps = kzalloc(sizeof(void *)*n_srcimp, GFP_KERNEL);
+ apcm->srcimps = kcalloc(n_srcimp, sizeof(void *), GFP_KERNEL);
if (!apcm->srcimps) {
err = -ENOMEM;
goto error1;
@@ -732,7 +743,8 @@ static int atc_pcm_capture_start(struct ct_atc *atc, struct ct_atc_pcm *apcm)
/* Set up recording SRC */
src = apcm->src;
- src->ops->set_sf(src, convert_format(apcm->substream->runtime->format));
+ src->ops->set_sf(src, convert_format(apcm->substream->runtime->format,
+ atc->card));
src->ops->set_sa(src, apcm->vm_block->addr);
src->ops->set_la(src, apcm->vm_block->addr + apcm->vm_block->size);
src->ops->set_ca(src, apcm->vm_block->addr);
@@ -801,13 +813,14 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc,
src = apcm->src;
src->ops->set_pitch(src, pitch);
src->ops->set_rom(src, select_rom(pitch));
- src->ops->set_sf(src, convert_format(apcm->substream->runtime->format));
+ src->ops->set_sf(src, convert_format(apcm->substream->runtime->format,
+ atc->card));
src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL));
src->ops->set_bp(src, 1);
/* Get AMIXER resource */
n_amixer = (n_amixer < 2) ? 2 : n_amixer;
- apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
+ apcm->amixers = kcalloc(n_amixer, sizeof(void *), GFP_KERNEL);
if (!apcm->amixers) {
err = -ENOMEM;
goto error1;
@@ -866,17 +879,16 @@ spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm)
return -ENOENT;
}
- mutex_lock(&atc->atc_mutex);
+ guard(mutex)(&atc->atc_mutex);
dao->ops->get_spos(dao, &status);
if (((status >> 24) & IEC958_AES3_CON_FS) != iec958_con_fs) {
- status &= ((~IEC958_AES3_CON_FS) << 24);
+ status &= ~(IEC958_AES3_CON_FS << 24);
status |= (iec958_con_fs << 24);
dao->ops->set_spos(dao, status);
dao->ops->commit_write(dao);
}
if ((rate != atc->pll_rate) && (32000 != rate))
err = atc_pll_init(atc, rate);
- mutex_unlock(&atc->atc_mutex);
return err;
}
@@ -913,13 +925,13 @@ spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
src = apcm->src;
}
/* Connect to SPDIFOO */
- mutex_lock(&atc->atc_mutex);
- dao = container_of(atc->daios[SPDIFOO], struct dao, daio);
- amixer = apcm->amixers[0];
- dao->ops->set_left_input(dao, &amixer->rsc);
- amixer = apcm->amixers[1];
- dao->ops->set_right_input(dao, &amixer->rsc);
- mutex_unlock(&atc->atc_mutex);
+ scoped_guard(mutex, &atc->atc_mutex) {
+ dao = container_of(atc->daios[SPDIFOO], struct dao, daio);
+ amixer = apcm->amixers[0];
+ dao->ops->set_left_input(dao, &amixer->rsc);
+ amixer = apcm->amixers[1];
+ dao->ops->set_right_input(dao, &amixer->rsc);
+ }
ct_timer_prepare(apcm->timer);
@@ -970,11 +982,57 @@ static int atc_select_mic_in(struct ct_atc *atc)
return 0;
}
-static int atc_have_digit_io_switch(struct ct_atc *atc)
+static struct capabilities atc_capabilities(struct ct_atc *atc)
+{
+ struct hw *hw = atc->hw;
+
+ return hw->capabilities(hw);
+}
+
+static void atc_dedicated_rca_select(struct ct_atc *atc)
+{
+ struct dao *dao;
+ struct ct_mixer *mixer = atc->mixer;
+ struct rsc *rscs[2] = {NULL};
+
+ dao = container_of(atc->daios[atc->rca_state ? RCA : LINEO1],
+ struct dao, daio);
+ dao->ops->clear_left_input(dao);
+ dao->ops->clear_right_input(dao);
+
+ mixer->get_output_ports(mixer, MIX_WAVE_FRONT, &rscs[0], &rscs[1]);
+ dao = container_of(atc->daios[atc->rca_state ? LINEO1 : RCA],
+ struct dao, daio);
+ dao->ops->set_left_input(dao, rscs[0]);
+ dao->ops->set_right_input(dao, rscs[1]);
+}
+
+static int atc_output_switch_get(struct ct_atc *atc)
+{
+ struct hw *hw = atc->hw;
+
+ return hw->output_switch_get(hw);
+}
+
+static int atc_output_switch_put(struct ct_atc *atc, int position)
{
struct hw *hw = atc->hw;
- return hw->have_digit_io_switch(hw);
+ return hw->output_switch_put(hw, position);
+}
+
+static int atc_mic_source_switch_get(struct ct_atc *atc)
+{
+ struct hw *hw = atc->hw;
+
+ return hw->mic_source_switch_get(hw);
+}
+
+static int atc_mic_source_switch_put(struct ct_atc *atc, int position)
+{
+ struct hw *hw = atc->hw;
+
+ return hw->mic_source_switch_put(hw, position);
}
static int atc_select_digit_io(struct ct_atc *atc)
@@ -1045,6 +1103,16 @@ static int atc_line_in_unmute(struct ct_atc *atc, unsigned char state)
return atc_daio_unmute(atc, state, LINEIM);
}
+static int atc_mic_unmute(struct ct_atc *atc, unsigned char state)
+{
+ return atc_daio_unmute(atc, state, MIC);
+}
+
+static int atc_rca_unmute(struct ct_atc *atc, unsigned char state)
+{
+ return atc_daio_unmute(atc, state, RCA);
+}
+
static int atc_spdif_out_unmute(struct ct_atc *atc, unsigned char state)
{
return atc_daio_unmute(atc, state, SPDIFOO);
@@ -1074,7 +1142,7 @@ static int atc_spdif_out_passthru(struct ct_atc *atc, unsigned char state)
struct rsc *rscs[2] = {NULL};
unsigned int spos = 0;
- mutex_lock(&atc->atc_mutex);
+ guard(mutex)(&atc->atc_mutex);
dao = container_of(atc->daios[SPDIFOO], struct dao, daio);
da_dsc.msr = state ? 1 : atc->msr;
da_dsc.passthru = state ? 1 : 0;
@@ -1092,7 +1160,6 @@ static int atc_spdif_out_passthru(struct ct_atc *atc, unsigned char state)
}
dao->ops->set_spos(dao, spos);
dao->ops->commit_write(dao);
- mutex_unlock(&atc->atc_mutex);
return err;
}
@@ -1102,7 +1169,6 @@ static int atc_release_resources(struct ct_atc *atc)
int i;
struct daio_mgr *daio_mgr = NULL;
struct dao *dao = NULL;
- struct dai *dai = NULL;
struct daio *daio = NULL;
struct sum_mgr *sum_mgr = NULL;
struct src_mgr *src_mgr = NULL;
@@ -1123,15 +1189,14 @@ static int atc_release_resources(struct ct_atc *atc)
if (atc->daios) {
daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
- for (i = 0; i < atc->n_daio; i++) {
+ for (i = 0; i < NUM_DAIOTYP; i++) {
daio = atc->daios[i];
- if (daio->type < LINEIM) {
+ if (!daio)
+ continue;
+ if (daio->output) {
dao = container_of(daio, struct dao, daio);
dao->ops->clear_left_input(dao);
dao->ops->clear_right_input(dao);
- } else {
- dai = container_of(daio, struct dai, daio);
- /* some thing to do for dai ... */
}
daio_mgr->put_daio(daio_mgr, daio);
}
@@ -1141,8 +1206,9 @@ static int atc_release_resources(struct ct_atc *atc)
if (atc->pcm) {
sum_mgr = atc->rsc_mgrs[SUM];
- for (i = 0; i < atc->n_pcm; i++)
- sum_mgr->put_sum(sum_mgr, atc->pcm[i]);
+ for (i = 0; i < NUM_ATC_PCM; i++)
+ if (atc->pcm[i])
+ sum_mgr->put_sum(sum_mgr, atc->pcm[i]);
kfree(atc->pcm);
atc->pcm = NULL;
@@ -1150,8 +1216,9 @@ static int atc_release_resources(struct ct_atc *atc)
if (atc->srcs) {
src_mgr = atc->rsc_mgrs[SRC];
- for (i = 0; i < atc->n_src; i++)
- src_mgr->put_src(src_mgr, atc->srcs[i]);
+ for (i = 0; i < NUM_ATC_SRCS; i++)
+ if (atc->srcs[i])
+ src_mgr->put_src(src_mgr, atc->srcs[i]);
kfree(atc->srcs);
atc->srcs = NULL;
@@ -1159,7 +1226,9 @@ static int atc_release_resources(struct ct_atc *atc)
if (atc->srcimps) {
srcimp_mgr = atc->rsc_mgrs[SRCIMP];
- for (i = 0; i < atc->n_srcimp; i++) {
+ for (i = 0; i < NUM_ATC_SRCS; i++) {
+ if (!atc->srcimps[i])
+ continue;
srcimp = atc->srcimps[i];
srcimp->ops->unmap(srcimp);
srcimp_mgr->put_srcimp(srcimp_mgr, atc->srcimps[i]);
@@ -1196,7 +1265,7 @@ static int ct_atc_destroy(struct ct_atc *atc)
}
if (atc->hw)
- destroy_hw_obj((struct hw *)atc->hw);
+ destroy_hw_obj(atc->hw);
/* Destroy device virtual memory manager object */
if (atc->vm) {
@@ -1215,7 +1284,7 @@ static int atc_dev_free(struct snd_device *dev)
return ct_atc_destroy(atc);
}
-static int __devinit atc_identify_card(struct ct_atc *atc, unsigned int ssid)
+static int atc_identify_card(struct ct_atc *atc, unsigned int ssid)
{
const struct snd_pci_quirk *p;
const struct snd_pci_quirk *list;
@@ -1243,9 +1312,9 @@ static int __devinit atc_identify_card(struct ct_atc *atc, unsigned int ssid)
p = snd_pci_quirk_lookup_id(vendor_id, device_id, list);
if (p) {
if (p->value < 0) {
- printk(KERN_ERR "ctxfi: "
- "Device %04x:%04x is black-listed\n",
- vendor_id, device_id);
+ dev_err(atc->card->dev,
+ "Device %04x:%04x is on the denylist\n",
+ vendor_id, device_id);
return -ENOENT;
}
atc->model = p->value;
@@ -1256,13 +1325,14 @@ static int __devinit atc_identify_card(struct ct_atc *atc, unsigned int ssid)
atc->model = CT20K2_UNKNOWN;
}
atc->model_name = ct_subsys_name[atc->model];
- snd_printd("ctxfi: chip %s model %s (%04x:%04x) is found\n",
+ dev_info(atc->card->dev, "chip %s model %s (%04x:%04x) is found\n",
atc->chip_name, atc->model_name,
vendor_id, device_id);
+ atc->rca_state = 0;
return 0;
}
-int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc)
+int ct_atc_create_alsa_devs(struct ct_atc *atc)
{
enum CTALSADEVS i;
int err;
@@ -1276,8 +1346,8 @@ int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc)
err = alsa_dev_funcs[i].create(atc, i,
alsa_dev_funcs[i].public_name);
if (err) {
- printk(KERN_ERR "ctxfi: "
- "Creating alsa device %d failed!\n", i);
+ dev_err(atc->card->dev,
+ "Creating alsa device %d failed!\n", i);
return err;
}
}
@@ -1285,7 +1355,7 @@ int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc)
return 0;
}
-static int __devinit atc_create_hw_devs(struct ct_atc *atc)
+static int atc_create_hw_devs(struct ct_atc *atc)
{
struct hw *hw;
struct card_conf info = {0};
@@ -1293,9 +1363,10 @@ static int __devinit atc_create_hw_devs(struct ct_atc *atc)
err = create_hw_obj(atc->pci, atc->chip_type, atc->model, &hw);
if (err) {
- printk(KERN_ERR "Failed to create hw obj!!!\n");
+ dev_err(atc->card->dev, "Failed to create hw obj!!!\n");
return err;
}
+ hw->card = atc->card;
atc->hw = hw;
/* Initialize card hardware. */
@@ -1312,8 +1383,8 @@ static int __devinit atc_create_hw_devs(struct ct_atc *atc)
err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]);
if (err) {
- printk(KERN_ERR "ctxfi: "
- "Failed to create rsc_mgr %d!!!\n", i);
+ dev_err(atc->card->dev,
+ "Failed to create rsc_mgr %d!!!\n", i);
return err;
}
}
@@ -1331,91 +1402,76 @@ static int atc_get_resources(struct ct_atc *atc)
struct srcimp_mgr *srcimp_mgr;
struct sum_desc sum_dsc = {0};
struct sum_mgr *sum_mgr;
+ struct capabilities cap;
int err, i;
- atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL);
+ cap = atc->capabilities(atc);
+
+ atc->daios = kcalloc(NUM_DAIOTYP, sizeof(void *), GFP_KERNEL);
if (!atc->daios)
return -ENOMEM;
- atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
+ atc->srcs = kcalloc(NUM_ATC_SRCS, sizeof(void *), GFP_KERNEL);
if (!atc->srcs)
return -ENOMEM;
- atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
+ atc->srcimps = kcalloc(NUM_ATC_SRCS, sizeof(void *), GFP_KERNEL);
if (!atc->srcimps)
return -ENOMEM;
- atc->pcm = kzalloc(sizeof(void *)*(2*4), GFP_KERNEL);
+ atc->pcm = kcalloc(NUM_ATC_PCM, sizeof(void *), GFP_KERNEL);
if (!atc->pcm)
return -ENOMEM;
daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
da_desc.msr = atc->msr;
- for (i = 0, atc->n_daio = 0; i < DAIONUM-1; i++) {
- da_desc.type = i;
+ for (i = 0; i < NUM_DAIOTYP; i++) {
+ if (((i == MIC) && !cap.dedicated_mic) || ((i == RCA) && !cap.dedicated_rca))
+ continue;
+ da_desc.type = (atc->model != CTSB073X) ? i :
+ ((i == SPDIFIO) ? SPDIFI1 : i);
+ da_desc.output = (i < LINEIM) || (i == RCA);
err = daio_mgr->get_daio(daio_mgr, &da_desc,
(struct daio **)&atc->daios[i]);
if (err) {
- printk(KERN_ERR "ctxfi: Failed to get DAIO "
- "resource %d!!!\n", i);
+ dev_err(atc->card->dev,
+ "Failed to get DAIO resource %d!!!\n",
+ i);
return err;
}
- atc->n_daio++;
}
- if (atc->model == CTSB073X)
- da_desc.type = SPDIFI1;
- else
- da_desc.type = SPDIFIO;
- err = daio_mgr->get_daio(daio_mgr, &da_desc,
- (struct daio **)&atc->daios[i]);
- if (err) {
- printk(KERN_ERR "ctxfi: Failed to get S/PDIF-in resource!!!\n");
- return err;
- }
- atc->n_daio++;
src_mgr = atc->rsc_mgrs[SRC];
src_dsc.multi = 1;
src_dsc.msr = atc->msr;
src_dsc.mode = ARCRW;
- for (i = 0, atc->n_src = 0; i < (2*2); i++) {
+ for (i = 0; i < NUM_ATC_SRCS; i++) {
+ if (((i > 3) && !cap.dedicated_mic))
+ continue;
err = src_mgr->get_src(src_mgr, &src_dsc,
(struct src **)&atc->srcs[i]);
if (err)
return err;
-
- atc->n_src++;
}
srcimp_mgr = atc->rsc_mgrs[SRCIMP];
- srcimp_dsc.msr = 8; /* SRCIMPs for S/PDIFIn SRT */
- for (i = 0, atc->n_srcimp = 0; i < (2*1); i++) {
+ srcimp_dsc.msr = 8;
+ for (i = 0; i < NUM_ATC_SRCS; i++) {
+ if (((i > 3) && !cap.dedicated_mic))
+ continue;
err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc,
(struct srcimp **)&atc->srcimps[i]);
if (err)
return err;
-
- atc->n_srcimp++;
- }
- srcimp_dsc.msr = 8; /* SRCIMPs for LINE/MICIn SRT */
- for (i = 0; i < (2*1); i++) {
- err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc,
- (struct srcimp **)&atc->srcimps[2*1+i]);
- if (err)
- return err;
-
- atc->n_srcimp++;
}
sum_mgr = atc->rsc_mgrs[SUM];
sum_dsc.msr = atc->msr;
- for (i = 0, atc->n_pcm = 0; i < (2*4); i++) {
+ for (i = 0; i < NUM_ATC_PCM; i++) {
err = sum_mgr->get_sum(sum_mgr, &sum_dsc,
(struct sum **)&atc->pcm[i]);
if (err)
return err;
-
- atc->n_pcm++;
}
return 0;
@@ -1468,9 +1524,11 @@ static void atc_connect_resources(struct ct_atc *atc)
struct sum *sum;
struct ct_mixer *mixer;
struct rsc *rscs[2] = {NULL};
+ struct capabilities cap;
int i, j;
mixer = atc->mixer;
+ cap = atc->capabilities(atc);
for (i = MIX_WAVE_FRONT, j = LINEO1; i <= MIX_SPDIF_OUT; i++, j++) {
mixer->get_output_ports(mixer, i, &rscs[0], &rscs[1]);
@@ -1479,6 +1537,11 @@ static void atc_connect_resources(struct ct_atc *atc)
dao->ops->set_right_input(dao, rscs[1]);
}
+ if (cap.dedicated_rca) {
+ /* SE-300PCIE has a dedicated DAC for the RCA. */
+ atc_dedicated_rca_select(atc);
+ }
+
dai = container_of(atc->daios[LINEIM], struct dai, daio);
atc_connect_dai(atc->rsc_mgrs[SRC], dai,
(struct src **)&atc->srcs[2],
@@ -1488,6 +1551,19 @@ static void atc_connect_resources(struct ct_atc *atc)
src = atc->srcs[3];
mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc);
+ if (cap.dedicated_mic) {
+ /* Titanium HD has a dedicated ADC for the Mic. */
+ /* SE-300PCIE has a 4-channel ADC. */
+ dai = container_of(atc->daios[MIC], struct dai, daio);
+ atc_connect_dai(atc->rsc_mgrs[SRC], dai,
+ (struct src **)&atc->srcs[4],
+ (struct srcimp **)&atc->srcimps[4]);
+ src = atc->srcs[4];
+ mixer->set_input_left(mixer, MIX_MIC_IN, &src->rsc);
+ src = atc->srcs[5];
+ mixer->set_input_right(mixer, MIX_MIC_IN, &src->rsc);
+ }
+
dai = container_of(atc->daios[SPDIFIO], struct dai, daio);
atc_connect_dai(atc->rsc_mgrs[SRC], dai,
(struct src **)&atc->srcs[0],
@@ -1506,24 +1582,16 @@ static void atc_connect_resources(struct ct_atc *atc)
}
}
-#ifdef CONFIG_PM
-static int atc_suspend(struct ct_atc *atc, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int atc_suspend(struct ct_atc *atc)
{
- int i;
struct hw *hw = atc->hw;
snd_power_change_state(atc->card, SNDRV_CTL_POWER_D3hot);
- for (i = FRONT; i < NUM_PCMS; i++) {
- if (!atc->pcms[i])
- continue;
-
- snd_pcm_suspend_all(atc->pcms[i]);
- }
-
atc_release_resources(atc);
- hw->suspend(hw, state);
+ hw->suspend(hw);
return 0;
}
@@ -1568,8 +1636,8 @@ static int atc_resume(struct ct_atc *atc)
/* Do hardware resume. */
err = atc_hw_resume(atc);
if (err < 0) {
- printk(KERN_ERR "ctxfi: pci_enable_device failed, "
- "disabling device\n");
+ dev_err(atc->card->dev,
+ "pci_enable_device failed, disabling device\n");
snd_card_disconnect(atc->card);
return err;
}
@@ -1584,7 +1652,7 @@ static int atc_resume(struct ct_atc *atc)
}
#endif
-static struct ct_atc atc_preset __devinitdata = {
+static const struct ct_atc atc_preset = {
.map_audio_buffer = ct_map_audio_buffer,
.unmap_audio_buffer = ct_unmap_audio_buffer,
.pcm_playback_prepare = atc_pcm_playback_prepare,
@@ -1606,13 +1674,20 @@ static struct ct_atc atc_preset __devinitdata = {
.line_clfe_unmute = atc_line_clfe_unmute,
.line_rear_unmute = atc_line_rear_unmute,
.line_in_unmute = atc_line_in_unmute,
+ .mic_unmute = atc_mic_unmute,
+ .rca_unmute = atc_rca_unmute,
.spdif_out_unmute = atc_spdif_out_unmute,
.spdif_in_unmute = atc_spdif_in_unmute,
.spdif_out_get_status = atc_spdif_out_get_status,
.spdif_out_set_status = atc_spdif_out_set_status,
.spdif_out_passthru = atc_spdif_out_passthru,
- .have_digit_io_switch = atc_have_digit_io_switch,
-#ifdef CONFIG_PM
+ .capabilities = atc_capabilities,
+ .dedicated_rca_select = atc_dedicated_rca_select,
+ .output_switch_get = atc_output_switch_get,
+ .output_switch_put = atc_output_switch_put,
+ .mic_source_switch_get = atc_mic_source_switch_get,
+ .mic_source_switch_put = atc_mic_source_switch_put,
+#ifdef CONFIG_PM_SLEEP
.suspend = atc_suspend,
.resume = atc_resume,
#endif
@@ -1622,21 +1697,25 @@ static struct ct_atc atc_preset __devinitdata = {
* ct_atc_create - create and initialize a hardware manager
* @card: corresponding alsa card object
* @pci: corresponding kernel pci device object
+ * @rsr: reference sampling rate
+ * @msr: master sampling rate
+ * @chip_type: CHIPTYP enum values
+ * @ssid: vendor ID (upper 16 bits) and device ID (lower 16 bits)
* @ratc: return created object address in it
*
* Creates and initializes a hardware manager.
*
* Creates kmallocated ct_atc structure. Initializes hardware.
- * Returns 0 if suceeds, or negative error code if fails.
+ * Returns 0 if succeeds, or negative error code if fails.
*/
-int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
- unsigned int rsr, unsigned int msr,
- int chip_type, unsigned int ssid,
- struct ct_atc **ratc)
+int ct_atc_create(struct snd_card *card, struct pci_dev *pci,
+ unsigned int rsr, unsigned int msr,
+ int chip_type, unsigned int ssid,
+ struct ct_atc **ratc)
{
struct ct_atc *atc;
- static struct snd_device_ops ops = {
+ static const struct snd_device_ops ops = {
.dev_free = atc_dev_free,
};
int err;
@@ -1661,7 +1740,7 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
/* Find card model */
err = atc_identify_card(atc, ssid);
if (err < 0) {
- printk(KERN_ERR "ctatc: Card not recognised\n");
+ dev_err(card->dev, "ctatc: Card not recognised\n");
goto error1;
}
@@ -1677,7 +1756,7 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
err = ct_mixer_create(atc, (struct ct_mixer **)&atc->mixer);
if (err) {
- printk(KERN_ERR "ctxfi: Failed to create mixer obj!!!\n");
+ dev_err(card->dev, "Failed to create mixer obj!!!\n");
goto error1;
}
@@ -1690,20 +1769,20 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
atc_connect_resources(atc);
atc->timer = ct_timer_new(atc);
- if (!atc->timer)
+ if (!atc->timer) {
+ err = -ENOMEM;
goto error1;
+ }
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, atc, &ops);
if (err < 0)
goto error1;
- snd_card_set_dev(card, &pci->dev);
-
*ratc = atc;
return 0;
error1:
ct_atc_destroy(atc);
- printk(KERN_ERR "ctxfi: Something wrong!!!\n");
+ dev_err(card->dev, "Something wrong!!!\n");
return err;
}
diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h
index 7167c0185d52..ca0a9d5b86d8 100644
--- a/sound/pci/ctxfi/ctatc.h
+++ b/sound/pci/ctxfi/ctatc.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctatc.h
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date Mar 28 2008
- *
*/
#ifndef CTATC_H
@@ -25,6 +21,7 @@
#include <sound/core.h>
#include "ctvmem.h"
+#include "cthardware.h"
#include "ctresource.h"
enum CTALSADEVS { /* Types of alsa devices */
@@ -85,6 +82,8 @@ struct ct_atc {
const char *chip_name;
const char *model_name;
+ unsigned char rca_state; /* 0 = dedicated RCA, 1 = 7.1ch Front */
+
struct ct_vm *vm; /* device virtual memory manager for this card */
int (*map_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
void (*unmap_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
@@ -115,30 +114,33 @@ struct ct_atc {
int (*line_clfe_unmute)(struct ct_atc *atc, unsigned char state);
int (*line_rear_unmute)(struct ct_atc *atc, unsigned char state);
int (*line_in_unmute)(struct ct_atc *atc, unsigned char state);
+ int (*mic_unmute)(struct ct_atc *atc, unsigned char state);
+ int (*rca_unmute)(struct ct_atc *atc, unsigned char state);
int (*spdif_out_unmute)(struct ct_atc *atc, unsigned char state);
int (*spdif_in_unmute)(struct ct_atc *atc, unsigned char state);
int (*spdif_out_get_status)(struct ct_atc *atc, unsigned int *status);
int (*spdif_out_set_status)(struct ct_atc *atc, unsigned int status);
int (*spdif_out_passthru)(struct ct_atc *atc, unsigned char state);
- int (*have_digit_io_switch)(struct ct_atc *atc);
+ struct capabilities (*capabilities)(struct ct_atc *atc);
+ void (*dedicated_rca_select)(struct ct_atc *atc);
+ int (*output_switch_get)(struct ct_atc *atc);
+ int (*output_switch_put)(struct ct_atc *atc, int position);
+ int (*mic_source_switch_get)(struct ct_atc *atc);
+ int (*mic_source_switch_put)(struct ct_atc *atc, int position);
/* Don't touch! Used for internal object. */
void *rsc_mgrs[NUM_RSCTYP]; /* chip resource managers */
void *mixer; /* internal mixer object */
- void *hw; /* chip specific hardware access object */
+ struct hw *hw; /* chip specific hardware access object */
void **daios; /* digital audio io resources */
void **pcm; /* SUMs for collecting all pcm stream */
void **srcs; /* Sample Rate Converters for input signal */
void **srcimps; /* input mappers for SRCs */
- unsigned char n_daio;
- unsigned char n_src;
- unsigned char n_srcimp;
- unsigned char n_pcm;
struct ct_timer *timer;
-#ifdef CONFIG_PM
- int (*suspend)(struct ct_atc *atc, pm_message_t state);
+#ifdef CONFIG_PM_SLEEP
+ int (*suspend)(struct ct_atc *atc);
int (*resume)(struct ct_atc *atc);
#define NUM_PCMS (NUM_CTALSADEVS - 1)
struct snd_pcm *pcms[NUM_PCMS];
@@ -146,9 +148,9 @@ struct ct_atc {
};
-int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
- unsigned int rsr, unsigned int msr, int chip_type,
- unsigned int subsysid, struct ct_atc **ratc);
-int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc);
+int ct_atc_create(struct snd_card *card, struct pci_dev *pci,
+ unsigned int rsr, unsigned int msr, int chip_type,
+ unsigned int subsysid, struct ct_atc **ratc);
+int ct_atc_create_alsa_devs(struct ct_atc *atc);
#endif /* CTATC_H */
diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c
index af56eb949bde..1c8f8efd836c 100644
--- a/sound/pci/ctxfi/ctdaio.c
+++ b/sound/pci/ctxfi/ctdaio.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctdaio.c
*
* @Brief
@@ -13,7 +10,6 @@
*
* @Author Liu Chun
* @Date May 23 2008
- *
*/
#include "ctdaio.h"
@@ -22,20 +18,7 @@
#include <linux/slab.h>
#include <linux/kernel.h>
-#define DAIO_RESOURCE_NUM NUM_DAIOTYP
-#define DAIO_OUT_MAX SPDIFOO
-
-union daio_usage {
- struct {
- unsigned short lineo1:1;
- unsigned short lineo2:1;
- unsigned short lineo3:1;
- unsigned short lineo4:1;
- unsigned short spdifoo:1;
- unsigned short lineim:1;
- unsigned short spdifio:1;
- unsigned short spdifi1:1;
- } bf;
+struct daio_usage {
unsigned short data;
};
@@ -44,7 +27,7 @@ struct daio_rsc_idx {
unsigned short right;
};
-struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
+static const struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
[LINEO1] = {.left = 0x00, .right = 0x01},
[LINEO2] = {.left = 0x18, .right = 0x19},
[LINEO3] = {.left = 0x08, .right = 0x09},
@@ -55,22 +38,24 @@ struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
[SPDIFI1] = {.left = 0x95, .right = 0x9d},
};
-struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
+static const struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
[LINEO1] = {.left = 0x40, .right = 0x41},
[LINEO2] = {.left = 0x60, .right = 0x61},
[LINEO3] = {.left = 0x50, .right = 0x51},
[LINEO4] = {.left = 0x70, .right = 0x71},
[LINEIM] = {.left = 0x45, .right = 0xc5},
+ [MIC] = {.left = 0x55, .right = 0xd5},
+ [RCA] = {.left = 0x30, .right = 0x31},
[SPDIFOO] = {.left = 0x00, .right = 0x01},
[SPDIFIO] = {.left = 0x05, .right = 0x85},
};
-static int daio_master(struct rsc *rsc)
+static void daio_master(struct rsc *rsc)
{
/* Actually, this is not the resource index of DAIO.
* For DAO, it is the input mapper index. And, for DAI,
* it is the output time-slot index. */
- return rsc->conj = rsc->idx;
+ rsc->conj = rsc->idx;
}
static int daio_index(const struct rsc *rsc)
@@ -78,36 +63,36 @@ static int daio_index(const struct rsc *rsc)
return rsc->conj;
}
-static int daio_out_next_conj(struct rsc *rsc)
+static void daio_out_next_conj(struct rsc *rsc)
{
- return rsc->conj += 2;
+ rsc->conj += 2;
}
-static int daio_in_next_conj_20k1(struct rsc *rsc)
+static void daio_in_next_conj_20k1(struct rsc *rsc)
{
- return rsc->conj += 0x200;
+ rsc->conj += 0x200;
}
-static int daio_in_next_conj_20k2(struct rsc *rsc)
+static void daio_in_next_conj_20k2(struct rsc *rsc)
{
- return rsc->conj += 0x100;
+ rsc->conj += 0x100;
}
-static struct rsc_ops daio_out_rsc_ops = {
+static const struct rsc_ops daio_out_rsc_ops = {
.master = daio_master,
.next_conj = daio_out_next_conj,
.index = daio_index,
.output_slot = NULL,
};
-static struct rsc_ops daio_in_rsc_ops_20k1 = {
+static const struct rsc_ops daio_in_rsc_ops_20k1 = {
.master = daio_master,
.next_conj = daio_in_next_conj_20k1,
.index = NULL,
.output_slot = daio_index,
};
-static struct rsc_ops daio_in_rsc_ops_20k2 = {
+static const struct rsc_ops daio_in_rsc_ops_20k2 = {
.master = daio_master,
.next_conj = daio_in_next_conj_20k2,
.index = NULL,
@@ -138,6 +123,8 @@ static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw)
case LINEO3: return 5;
case LINEO4: return 6;
case LINEIM: return 4;
+ case MIC: return 5;
+ case RCA: return 3;
default: return -EINVAL;
}
default:
@@ -149,19 +136,19 @@ static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc);
static int dao_spdif_get_spos(struct dao *dao, unsigned int *spos)
{
- ((struct hw *)dao->hw)->dao_get_spos(dao->ctrl_blk, spos);
+ dao->hw->dao_get_spos(dao->ctrl_blk, spos);
return 0;
}
static int dao_spdif_set_spos(struct dao *dao, unsigned int spos)
{
- ((struct hw *)dao->hw)->dao_set_spos(dao->ctrl_blk, spos);
+ dao->hw->dao_set_spos(dao->ctrl_blk, spos);
return 0;
}
static int dao_commit_write(struct dao *dao)
{
- ((struct hw *)dao->hw)->dao_commit_write(dao->hw,
+ dao->hw->dao_commit_write(dao->hw,
daio_device_index(dao->daio.type, dao->hw), dao->ctrl_blk);
return 0;
}
@@ -172,10 +159,11 @@ static int dao_set_left_input(struct dao *dao, struct rsc *input)
struct daio *daio = &dao->daio;
int i;
- entry = kzalloc((sizeof(*entry) * daio->rscl.msr), GFP_KERNEL);
+ entry = kcalloc(daio->rscl.msr, sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
+ dao->ops->clear_left_input(dao);
/* Program master and conjugate resources */
input->ops->master(input);
daio->rscl.ops->master(&daio->rscl);
@@ -200,10 +188,11 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input)
struct daio *daio = &dao->daio;
int i;
- entry = kzalloc((sizeof(*entry) * daio->rscr.msr), GFP_KERNEL);
+ entry = kcalloc(daio->rscr.msr, sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
+ dao->ops->clear_right_input(dao);
/* Program master and conjugate resources */
input->ops->master(input);
daio->rscr.ops->master(&daio->rscr);
@@ -222,55 +211,33 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input)
return 0;
}
-static int dao_clear_left_input(struct dao *dao)
+static int dao_clear_input(struct dao *dao, unsigned int start, unsigned int end)
{
- struct imapper *entry;
- struct daio *daio = &dao->daio;
- int i;
+ unsigned int i;
- if (!dao->imappers[0])
+ if (!dao->imappers[start])
return 0;
-
- entry = dao->imappers[0];
- dao->mgr->imap_delete(dao->mgr, entry);
- /* Program conjugate resources */
- for (i = 1; i < daio->rscl.msr; i++) {
- entry = dao->imappers[i];
- dao->mgr->imap_delete(dao->mgr, entry);
+ for (i = start; i < end; i++) {
+ dao->mgr->imap_delete(dao->mgr, dao->imappers[i]);
dao->imappers[i] = NULL;
}
- kfree(dao->imappers[0]);
- dao->imappers[0] = NULL;
-
return 0;
}
-static int dao_clear_right_input(struct dao *dao)
-{
- struct imapper *entry;
- struct daio *daio = &dao->daio;
- int i;
-
- if (!dao->imappers[daio->rscl.msr])
- return 0;
-
- entry = dao->imappers[daio->rscl.msr];
- dao->mgr->imap_delete(dao->mgr, entry);
- /* Program conjugate resources */
- for (i = 1; i < daio->rscr.msr; i++) {
- entry = dao->imappers[daio->rscl.msr + i];
- dao->mgr->imap_delete(dao->mgr, entry);
- dao->imappers[daio->rscl.msr + i] = NULL;
- }
- kfree(dao->imappers[daio->rscl.msr]);
- dao->imappers[daio->rscl.msr] = NULL;
+static int dao_clear_left_input(struct dao *dao)
+{
+ return dao_clear_input(dao, 0, dao->daio.rscl.msr);
+}
- return 0;
+static int dao_clear_right_input(struct dao *dao)
+{
+ return dao_clear_input(dao, dao->daio.rscl.msr,
+ dao->daio.rscl.msr + dao->daio.rscr.msr);
}
-static struct dao_rsc_ops dao_ops = {
+static const struct dao_rsc_ops dao_ops = {
.set_spos = dao_spdif_set_spos,
.commit_write = dao_commit_write,
.get_spos = dao_spdif_get_spos,
@@ -284,16 +251,14 @@ static struct dao_rsc_ops dao_ops = {
static int dai_set_srt_srcl(struct dai *dai, struct rsc *src)
{
src->ops->master(src);
- ((struct hw *)dai->hw)->dai_srt_set_srcm(dai->ctrl_blk,
- src->ops->index(src));
+ dai->hw->dai_srt_set_srcm(dai->ctrl_blk, src->ops->index(src));
return 0;
}
static int dai_set_srt_srcr(struct dai *dai, struct rsc *src)
{
src->ops->master(src);
- ((struct hw *)dai->hw)->dai_srt_set_srco(dai->ctrl_blk,
- src->ops->index(src));
+ dai->hw->dai_srt_set_srco(dai->ctrl_blk, src->ops->index(src));
return 0;
}
@@ -304,30 +269,30 @@ static int dai_set_srt_msr(struct dai *dai, unsigned int msr)
for (rsr = 0; msr > 1; msr >>= 1)
rsr++;
- ((struct hw *)dai->hw)->dai_srt_set_rsr(dai->ctrl_blk, rsr);
+ dai->hw->dai_srt_set_rsr(dai->ctrl_blk, rsr);
return 0;
}
static int dai_set_enb_src(struct dai *dai, unsigned int enb)
{
- ((struct hw *)dai->hw)->dai_srt_set_ec(dai->ctrl_blk, enb);
+ dai->hw->dai_srt_set_ec(dai->ctrl_blk, enb);
return 0;
}
static int dai_set_enb_srt(struct dai *dai, unsigned int enb)
{
- ((struct hw *)dai->hw)->dai_srt_set_et(dai->ctrl_blk, enb);
+ dai->hw->dai_srt_set_et(dai->ctrl_blk, enb);
return 0;
}
static int dai_commit_write(struct dai *dai)
{
- ((struct hw *)dai->hw)->dai_commit_write(dai->hw,
+ dai->hw->dai_commit_write(dai->hw,
daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk);
return 0;
}
-static struct dai_rsc_ops dai_ops = {
+static const struct dai_rsc_ops dai_ops = {
.set_srt_srcl = dai_set_srt_srcl,
.set_srt_srcr = dai_set_srt_srcr,
.set_srt_msr = dai_set_srt_msr,
@@ -338,12 +303,12 @@ static struct dai_rsc_ops dai_ops = {
static int daio_rsc_init(struct daio *daio,
const struct daio_desc *desc,
- void *hw)
+ struct hw *hw)
{
int err;
unsigned int idx_l, idx_r;
- switch (((struct hw *)hw)->chip_type) {
+ switch (hw->chip_type) {
case ATC20K1:
idx_l = idx_20k1[desc->type].left;
idx_r = idx_20k1[desc->type].right;
@@ -364,10 +329,10 @@ static int daio_rsc_init(struct daio *daio,
goto error1;
/* Set daio->rscl/r->ops to daio specific ones */
- if (desc->type <= DAIO_OUT_MAX) {
+ if (desc->output) {
daio->rscl.ops = daio->rscr.ops = &daio_out_rsc_ops;
} else {
- switch (((struct hw *)hw)->chip_type) {
+ switch (hw->chip_type) {
case ATC20K1:
daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k1;
break;
@@ -379,6 +344,7 @@ static int daio_rsc_init(struct daio *daio,
}
}
daio->type = desc->type;
+ daio->output = desc->output;
return 0;
@@ -407,7 +373,8 @@ static int dao_rsc_init(struct dao *dao,
if (err)
return err;
- dao->imappers = kzalloc(sizeof(void *)*desc->msr*2, GFP_KERNEL);
+ dao->imappers = kzalloc(array3_size(sizeof(void *), desc->msr, 2),
+ GFP_KERNEL);
if (!dao->imappers) {
err = -ENOMEM;
goto error1;
@@ -424,7 +391,7 @@ static int dao_rsc_init(struct dao *dao,
hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
conf = (desc->msr & 0x7) | (desc->passthru << 3);
- hw->daio_mgr_dao_init(mgr->mgr.ctrl_blk,
+ hw->daio_mgr_dao_init(hw, mgr->mgr.ctrl_blk,
daio_device_index(dao->daio.type, hw), conf);
hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk,
daio_device_index(dao->daio.type, hw));
@@ -452,7 +419,7 @@ static int dao_rsc_uninit(struct dao *dao)
kfree(dao->imappers);
dao->imappers = NULL;
}
- ((struct hw *)dao->hw)->dao_put_ctrl_blk(dao->ctrl_blk);
+ dao->hw->dao_put_ctrl_blk(dao->ctrl_blk);
dao->hw = dao->ctrl_blk = NULL;
daio_rsc_uninit(&dao->daio);
@@ -467,6 +434,7 @@ static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc)
dsc.type = dao->daio.type;
dsc.msr = desc->msr;
dsc.passthru = desc->passthru;
+ dsc.output = dao->daio.output;
dao_rsc_uninit(dao);
return dao_rsc_init(dao, &dsc, mgr);
}
@@ -509,7 +477,7 @@ error1:
static int dai_rsc_uninit(struct dai *dai)
{
- ((struct hw *)dai->hw)->dai_put_ctrl_blk(dai->ctrl_blk);
+ dai->hw->dai_put_ctrl_blk(dai->ctrl_blk);
dai->hw = dai->ctrl_blk = NULL;
daio_rsc_uninit(&dai->daio);
return 0;
@@ -517,17 +485,17 @@ static int dai_rsc_uninit(struct dai *dai)
static int daio_mgr_get_rsc(struct rsc_mgr *mgr, enum DAIOTYP type)
{
- if (((union daio_usage *)mgr->rscs)->data & (0x1 << type))
+ if (((struct daio_usage *)mgr->rscs)->data & (0x1 << type))
return -ENOENT;
- ((union daio_usage *)mgr->rscs)->data |= (0x1 << type);
+ ((struct daio_usage *)mgr->rscs)->data |= (0x1 << type);
return 0;
}
static int daio_mgr_put_rsc(struct rsc_mgr *mgr, enum DAIOTYP type)
{
- ((union daio_usage *)mgr->rscs)->data &= ~(0x1 << type);
+ ((struct daio_usage *)mgr->rscs)->data &= ~(0x1 << type);
return 0;
}
@@ -537,42 +505,43 @@ static int get_daio_rsc(struct daio_mgr *mgr,
struct daio **rdaio)
{
int err;
- struct dai *dai = NULL;
- struct dao *dao = NULL;
- unsigned long flags;
*rdaio = NULL;
/* Check whether there are sufficient daio resources to meet request. */
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- err = daio_mgr_get_rsc(&mgr->mgr, desc->type);
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ err = daio_mgr_get_rsc(&mgr->mgr, desc->type);
+ }
if (err) {
- printk(KERN_ERR "Can't meet DAIO resource request!\n");
+ dev_err(mgr->card->dev,
+ "Can't meet DAIO resource request!\n");
return err;
}
+ err = -ENOMEM;
/* Allocate mem for daio resource */
- if (desc->type <= DAIO_OUT_MAX) {
- dao = kzalloc(sizeof(*dao), GFP_KERNEL);
- if (!dao) {
- err = -ENOMEM;
+ if (desc->output) {
+ struct dao *dao = kzalloc(sizeof(*dao), GFP_KERNEL);
+ if (!dao)
goto error;
- }
+
err = dao_rsc_init(dao, desc, mgr);
- if (err)
+ if (err) {
+ kfree(dao);
goto error;
+ }
*rdaio = &dao->daio;
} else {
- dai = kzalloc(sizeof(*dai), GFP_KERNEL);
- if (!dai) {
- err = -ENOMEM;
+ struct dai *dai = kzalloc(sizeof(*dai), GFP_KERNEL);
+ if (!dai)
goto error;
- }
+
err = dai_rsc_init(dai, desc, mgr);
- if (err)
+ if (err) {
+ kfree(dai);
goto error;
+ }
*rdaio = &dai->daio;
}
@@ -583,29 +552,22 @@ static int get_daio_rsc(struct daio_mgr *mgr,
return 0;
error:
- if (dao)
- kfree(dao);
- else if (dai)
- kfree(dai);
-
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- daio_mgr_put_rsc(&mgr->mgr, desc->type);
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ daio_mgr_put_rsc(&mgr->mgr, desc->type);
+ }
return err;
}
static int put_daio_rsc(struct daio_mgr *mgr, struct daio *daio)
{
- unsigned long flags;
-
mgr->daio_disable(mgr, daio);
mgr->commit_write(mgr);
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- daio_mgr_put_rsc(&mgr->mgr, daio->type);
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ daio_mgr_put_rsc(&mgr->mgr, daio->type);
+ }
- if (daio->type <= DAIO_OUT_MAX) {
+ if (daio->output) {
dao_rsc_uninit(container_of(daio, struct dao, daio));
kfree(container_of(daio, struct dao, daio));
} else {
@@ -620,7 +582,7 @@ static int daio_mgr_enb_daio(struct daio_mgr *mgr, struct daio *daio)
{
struct hw *hw = mgr->mgr.hw;
- if (DAIO_OUT_MAX >= daio->type) {
+ if (daio->output) {
hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk,
daio_device_index(daio->type, hw));
} else {
@@ -634,7 +596,7 @@ static int daio_mgr_dsb_daio(struct daio_mgr *mgr, struct daio *daio)
{
struct hw *hw = mgr->mgr.hw;
- if (DAIO_OUT_MAX >= daio->type) {
+ if (daio->output) {
hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk,
daio_device_index(daio->type, hw));
} else {
@@ -659,34 +621,26 @@ static int daio_map_op(void *data, struct imapper *entry)
static int daio_imap_add(struct daio_mgr *mgr, struct imapper *entry)
{
- unsigned long flags;
- int err;
-
- spin_lock_irqsave(&mgr->imap_lock, flags);
+ guard(spinlock_irqsave)(&mgr->imap_lock);
if (!entry->addr && mgr->init_imap_added) {
input_mapper_delete(&mgr->imappers, mgr->init_imap,
daio_map_op, mgr);
mgr->init_imap_added = 0;
}
- err = input_mapper_add(&mgr->imappers, entry, daio_map_op, mgr);
- spin_unlock_irqrestore(&mgr->imap_lock, flags);
-
- return err;
+ return input_mapper_add(&mgr->imappers, entry, daio_map_op, mgr);
}
static int daio_imap_delete(struct daio_mgr *mgr, struct imapper *entry)
{
- unsigned long flags;
int err;
- spin_lock_irqsave(&mgr->imap_lock, flags);
+ guard(spinlock_irqsave)(&mgr->imap_lock);
err = input_mapper_delete(&mgr->imappers, entry, daio_map_op, mgr);
if (list_empty(&mgr->imappers)) {
input_mapper_add(&mgr->imappers, mgr->init_imap,
daio_map_op, mgr);
mgr->init_imap_added = 1;
}
- spin_unlock_irqrestore(&mgr->imap_lock, flags);
return err;
}
@@ -699,7 +653,7 @@ static int daio_mgr_commit_write(struct daio_mgr *mgr)
return 0;
}
-int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
+int daio_mgr_create(struct hw *hw, void **rdaio_mgr)
{
int err, i;
struct daio_mgr *daio_mgr;
@@ -710,7 +664,7 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
if (!daio_mgr)
return -ENOMEM;
- err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw);
+ err = rsc_mgr_init(&daio_mgr->mgr, DAIO, NUM_DAIOTYP, hw);
if (err)
goto error1;
@@ -734,12 +688,13 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
daio_mgr->imap_add = daio_imap_add;
daio_mgr->imap_delete = daio_imap_delete;
daio_mgr->commit_write = daio_mgr_commit_write;
+ daio_mgr->card = hw->card;
for (i = 0; i < 8; i++) {
- ((struct hw *)hw)->daio_mgr_dsb_dao(daio_mgr->mgr.ctrl_blk, i);
- ((struct hw *)hw)->daio_mgr_dsb_dai(daio_mgr->mgr.ctrl_blk, i);
+ hw->daio_mgr_dsb_dao(daio_mgr->mgr.ctrl_blk, i);
+ hw->daio_mgr_dsb_dai(daio_mgr->mgr.ctrl_blk, i);
}
- ((struct hw *)hw)->daio_mgr_commit_write(hw, daio_mgr->mgr.ctrl_blk);
+ hw->daio_mgr_commit_write(hw, daio_mgr->mgr.ctrl_blk);
*rdaio_mgr = daio_mgr;
@@ -752,14 +707,14 @@ error1:
return err;
}
-int daio_mgr_destroy(struct daio_mgr *daio_mgr)
+int daio_mgr_destroy(void *ptr)
{
- unsigned long flags;
+ struct daio_mgr *daio_mgr = ptr;
/* free daio input mapper list */
- spin_lock_irqsave(&daio_mgr->imap_lock, flags);
- free_input_mapper_list(&daio_mgr->imappers);
- spin_unlock_irqrestore(&daio_mgr->imap_lock, flags);
+ scoped_guard(spinlock_irqsave, &daio_mgr->imap_lock) {
+ free_input_mapper_list(&daio_mgr->imappers);
+ }
rsc_mgr_uninit(&daio_mgr->mgr);
kfree(daio_mgr);
diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h
index 0f52ce571ee8..ff77d55539a5 100644
--- a/sound/pci/ctxfi/ctdaio.h
+++ b/sound/pci/ctxfi/ctdaio.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctdaio.h
*
* @Brief
@@ -13,7 +10,6 @@
*
* @Author Liu Chun
* @Date May 23 2008
- *
*/
#ifndef CTDAIO_H
@@ -23,6 +19,7 @@
#include "ctimap.h"
#include <linux/spinlock.h>
#include <linux/list.h>
+#include <sound/core.h>
/* Define the descriptor of a daio resource */
enum DAIOTYP {
@@ -33,6 +30,8 @@ enum DAIOTYP {
SPDIFOO, /* S/PDIF Out (Flexijack/Optical) */
LINEIM,
SPDIFIO, /* S/PDIF In (Flexijack/Optical) on the card */
+ MIC, /* Dedicated mic on Titanium HD */
+ RCA, /* Dedicated RCA on SE-300PCIE */
SPDIFI1, /* S/PDIF In on internal Drive Bay */
NUM_DAIOTYP
};
@@ -45,21 +44,22 @@ struct daio {
struct rsc rscl; /* Basic resource info for left TX/RX */
struct rsc rscr; /* Basic resource info for right TX/RX */
enum DAIOTYP type;
+ unsigned char output;
};
struct dao {
struct daio daio;
- struct dao_rsc_ops *ops; /* DAO specific operations */
+ const struct dao_rsc_ops *ops; /* DAO specific operations */
struct imapper **imappers;
struct daio_mgr *mgr;
- void *hw;
+ struct hw *hw;
void *ctrl_blk;
};
struct dai {
struct daio daio;
- struct dai_rsc_ops *ops; /* DAI specific operations */
- void *hw;
+ const struct dai_rsc_ops *ops; /* DAI specific operations */
+ struct hw *hw;
void *ctrl_blk;
};
@@ -93,10 +93,12 @@ struct daio_desc {
unsigned int type:4;
unsigned int msr:4;
unsigned int passthru:1;
+ unsigned int output:1;
};
struct daio_mgr {
struct rsc_mgr mgr; /* Basic resource manager info */
+ struct snd_card *card; /* pointer to this card */
spinlock_t mgr_lock;
spinlock_t imap_lock;
struct list_head imappers;
@@ -116,7 +118,7 @@ struct daio_mgr {
};
/* Constructor and destructor of daio resource manager */
-int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr);
-int daio_mgr_destroy(struct daio_mgr *daio_mgr);
+int daio_mgr_create(struct hw *hw, void **ptr);
+int daio_mgr_destroy(void *ptr);
#endif /* CTDAIO_H */
diff --git a/sound/pci/ctxfi/cthardware.c b/sound/pci/ctxfi/cthardware.c
index 8e64f4862e85..1d5064486217 100644
--- a/sound/pci/ctxfi/cthardware.c
+++ b/sound/pci/ctxfi/cthardware.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File cthardware.c
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date Jun 26 2008
- *
*/
#include "cthardware.h"
@@ -20,8 +16,8 @@
#include "cthw20k2.h"
#include <linux/bug.h>
-int __devinit create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type,
- enum CTCARDS model, struct hw **rhw)
+int create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type,
+ enum CTCARDS model, struct hw **rhw)
{
int err;
@@ -69,7 +65,8 @@ unsigned int get_field(unsigned int data, unsigned int field)
{
int i;
- BUG_ON(!field);
+ if (WARN_ON(!field))
+ return 0;
/* @field should always be greater than 0 */
for (i = 0; !(field & (1 << i)); )
i++;
@@ -81,7 +78,8 @@ void set_field(unsigned int *data, unsigned int field, unsigned int value)
{
int i;
- BUG_ON(!field);
+ if (WARN_ON(!field))
+ return;
/* @field should always be greater than 0 */
for (i = 0; !(field & (1 << i)); )
i++;
diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h
index af55405f5dec..a3051fdd31f6 100644
--- a/sound/pci/ctxfi/cthardware.h
+++ b/sound/pci/ctxfi/cthardware.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File cthardware.h
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date May 13 2008
- *
*/
#ifndef CTHARDWARE_H
@@ -20,6 +16,7 @@
#include <linux/types.h>
#include <linux/pci.h>
+#include <sound/core.h>
enum CHIPTYP {
ATC20K1,
@@ -29,8 +26,9 @@ enum CHIPTYP {
enum CTCARDS {
/* 20k1 models */
+ CTSB046X,
+ CT20K1_MODEL_FIRST = CTSB046X,
CTSB055X,
- CT20K1_MODEL_FIRST = CTSB055X,
CTSB073X,
CTUAA,
CT20K1_UNKNOWN,
@@ -39,6 +37,8 @@ enum CTCARDS {
CT20K2_MODEL_FIRST = CTSB0760,
CTHENDRIX,
CTSB0880,
+ CTSB1270,
+ CTOK0010,
CT20K2_UNKNOWN,
NUM_CTCARDS /* This should always be the last */
};
@@ -60,17 +60,29 @@ struct card_conf {
unsigned int msr; /* master sample rate in rsrs */
};
+struct capabilities {
+ unsigned int digit_io_switch:1;
+ unsigned int dedicated_mic:1;
+ unsigned int dedicated_rca:1;
+ unsigned int output_switch:1;
+ unsigned int mic_source_switch:1;
+};
+
struct hw {
int (*card_init)(struct hw *hw, struct card_conf *info);
int (*card_stop)(struct hw *hw);
int (*pll_init)(struct hw *hw, unsigned int rsr);
-#ifdef CONFIG_PM
- int (*suspend)(struct hw *hw, pm_message_t state);
+#ifdef CONFIG_PM_SLEEP
+ int (*suspend)(struct hw *hw);
int (*resume)(struct hw *hw, struct card_conf *info);
#endif
int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source);
int (*select_adc_source)(struct hw *hw, enum ADCSRC source);
- int (*have_digit_io_switch)(struct hw *hw);
+ struct capabilities (*capabilities)(struct hw *hw);
+ int (*output_switch_get)(struct hw *hw);
+ int (*output_switch_put)(struct hw *hw, int position);
+ int (*mic_source_switch_get)(struct hw *hw);
+ int (*mic_source_switch_put)(struct hw *hw, int position);
/* SRC operations */
int (*src_rsc_get_ctrl_blk)(void **rblk);
@@ -157,7 +169,7 @@ struct hw {
int (*daio_mgr_dsb_dai)(void *blk, unsigned int idx);
int (*daio_mgr_enb_dao)(void *blk, unsigned int idx);
int (*daio_mgr_dsb_dao)(void *blk, unsigned int idx);
- int (*daio_mgr_dao_init)(void *blk, unsigned int idx,
+ int (*daio_mgr_dao_init)(struct hw *hw, void *blk, unsigned int idx,
unsigned int conf);
int (*daio_mgr_set_imaparc)(void *blk, unsigned int slot);
int (*daio_mgr_set_imapnxt)(void *blk, unsigned int next);
@@ -172,9 +184,10 @@ struct hw {
void *irq_callback_data;
struct pci_dev *pci; /* the pci kernel structure of this card */
+ struct snd_card *card; /* pointer to this card */
int irq;
unsigned long io_base;
- unsigned long mem_base;
+ void __iomem *mem_base;
enum CHIPTYP chip_type;
enum CTCARDS model;
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c
index 0cf400f879f9..ea0a928937b6 100644
--- a/sound/pci/ctxfi/cthw20k1.c
+++ b/sound/pci/ctxfi/cthw20k1.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File cthw20k1.c
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date Jun 24 2008
- *
*/
#include <linux/types.h>
@@ -27,12 +23,6 @@
#include "cthw20k1.h"
#include "ct20k1reg.h"
-#if BITS_PER_LONG == 32
-#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bit PTE */
-#else
-#define CT_XFI_DMA_MASK DMA_BIT_MASK(64) /* 64 bit PTE */
-#endif
-
struct hw20k1 {
struct hw hw;
spinlock_t reg_20k1_lock;
@@ -178,7 +168,7 @@ static int src_get_rsc_ctrl_blk(void **rblk)
static int src_put_rsc_ctrl_blk(void *blk)
{
- kfree((struct src_rsc_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -504,7 +494,7 @@ static int src_mgr_get_ctrl_blk(void **rblk)
static int src_mgr_put_ctrl_blk(void *blk)
{
- kfree((struct src_mgr_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -525,7 +515,7 @@ static int srcimp_mgr_get_ctrl_blk(void **rblk)
static int srcimp_mgr_put_ctrl_blk(void *blk)
{
- kfree((struct srcimp_mgr_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -712,7 +702,7 @@ static int amixer_rsc_get_ctrl_blk(void **rblk)
static int amixer_rsc_put_ctrl_blk(void *blk)
{
- kfree((struct amixer_rsc_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -919,7 +909,7 @@ static int dai_get_ctrl_blk(void **rblk)
static int dai_put_ctrl_blk(void *blk)
{
- kfree((struct dai_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -968,7 +958,7 @@ static int dao_get_ctrl_blk(void **rblk)
static int dao_put_ctrl_blk(void *blk)
{
- kfree((struct dao_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -1041,7 +1031,7 @@ static int daio_mgr_dsb_dao(void *blk, unsigned int idx)
return 0;
}
-static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf)
+static int daio_mgr_dao_init(struct hw *hw __maybe_unused, void *blk, unsigned int idx, unsigned int conf)
{
struct daio_mgr_ctrl_blk *ctl = blk;
@@ -1166,7 +1156,7 @@ static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk)
static int daio_mgr_put_ctrl_blk(void *blk)
{
- kfree((struct daio_mgr_ctrl_blk *)blk);
+ kfree(blk);
return 0;
}
@@ -1268,7 +1258,8 @@ static int hw_trn_init(struct hw *hw, const struct trn_conf *info)
/* Set up device page table */
if ((~0UL) == info->vm_pgt_phys) {
- printk(KERN_ERR "Wrong device page table page address!\n");
+ dev_err(hw->card->dev,
+ "Wrong device page table page address!\n");
return -1;
}
@@ -1285,7 +1276,7 @@ static int hw_trn_init(struct hw *hw, const struct trn_conf *info)
hw_write_20kx(hw, PTPALX, ptp_phys_low);
hw_write_20kx(hw, PTPAHX, ptp_phys_high);
hw_write_20kx(hw, TRNCTL, trnctl);
- hw_write_20kx(hw, TRNIS, 0x200c01); /* realy needed? */
+ hw_write_20kx(hw, TRNIS, 0x200c01); /* really needed? */
return 0;
}
@@ -1324,10 +1315,10 @@ static int hw_pll_init(struct hw *hw, unsigned int rsr)
break;
hw_write_20kx(hw, PLLCTL, pllctl);
- mdelay(40);
+ msleep(40);
}
if (i >= 3) {
- printk(KERN_ALERT "PLL initialization failed!!!\n");
+ dev_alert(hw->card->dev, "PLL initialization failed!!!\n");
return -EBUSY;
}
@@ -1351,7 +1342,7 @@ static int hw_auto_init(struct hw *hw)
break;
}
if (!get_field(gctl, GCTL_AID)) {
- printk(KERN_ALERT "Card Auto-init failed!!!\n");
+ dev_alert(hw->card->dev, "Card Auto-init failed!!!\n");
return -EBUSY;
}
@@ -1412,7 +1403,7 @@ static int hw_reset_dac(struct hw *hw)
/* To be effective, need to reset the DAC twice. */
for (i = 0; i < 2; i++) {
/* set gpio */
- mdelay(100);
+ msleep(100);
gpioorg = (u16)hw_read_20kx(hw, GPIO);
gpioorg &= 0xfffd;
hw_write_20kx(hw, GPIO, gpioorg);
@@ -1777,10 +1768,18 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
return adc_init_SBx(hw, info->input, info->mic20db);
}
-static int hw_have_digit_io_switch(struct hw *hw)
+static struct capabilities hw_capabilities(struct hw *hw)
{
+ struct capabilities cap;
+
/* SB073x and Vista compatible cards have no digit IO switch */
- return !(hw->model == CTSB073X || hw->model == CTUAA);
+ cap.digit_io_switch = !(hw->model == CTSB073X || hw->model == CTUAA);
+ cap.dedicated_mic = 0;
+ cap.dedicated_rca = 0;
+ cap.output_switch = 0;
+ cap.mic_source_switch = 0;
+
+ return cap;
}
#define CTLBITS(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
@@ -1795,7 +1794,7 @@ static int uaa_to_xfi(struct pci_dev *pci)
unsigned int is_uaa;
unsigned int data[4] = {0};
unsigned int io_base;
- void *mem_base;
+ void __iomem *mem_base;
int i;
const u32 CTLX = CTLBITS('C', 'T', 'L', 'X');
const u32 CTL_ = CTLBITS('C', 'T', 'L', '-');
@@ -1896,20 +1895,15 @@ static int hw_card_start(struct hw *hw)
{
int err;
struct pci_dev *pci = hw->pci;
+ const unsigned int dma_bits = BITS_PER_LONG;
err = pci_enable_device(pci);
if (err < 0)
return err;
/* Set DMA transfer mask */
- if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 ||
- pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) {
- printk(KERN_ERR "architecture does not support PCI "
- "busmaster DMA with mask 0x%llx\n",
- CT_XFI_DMA_MASK);
- err = -ENXIO;
- goto error1;
- }
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(dma_bits)))
+ dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32));
if (!hw->io_base) {
err = pci_request_regions(pci, "XFi");
@@ -1923,7 +1917,7 @@ static int hw_card_start(struct hw *hw)
}
- /* Switch to X-Fi mode from UAA mode if neeeded */
+ /* Switch to X-Fi mode from UAA mode if needed */
if (hw->model == CTUAA) {
err = uaa_to_xfi(pci);
if (err)
@@ -1933,12 +1927,14 @@ static int hw_card_start(struct hw *hw)
if (hw->irq < 0) {
err = request_irq(pci->irq, ct_20k1_interrupt, IRQF_SHARED,
- "ctxfi", hw);
+ KBUILD_MODNAME, hw);
if (err < 0) {
- printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq);
+ dev_err(hw->card->dev,
+ "XFi: Cannot get irq %d\n", pci->irq);
goto error2;
}
hw->irq = pci->irq;
+ hw->card->sync_irq = hw->irq;
}
pci_set_master(pci);
@@ -1964,9 +1960,6 @@ static int hw_card_stop(struct hw *hw)
data = hw_read_20kx(hw, PLLCTL);
hw_write_20kx(hw, PLLCTL, (data & (~(0x0F<<12))));
- /* TODO: Disable interrupt and so on... */
- if (hw->irq >= 0)
- synchronize_irq(hw->irq);
return 0;
}
@@ -1976,11 +1969,8 @@ static int hw_card_shutdown(struct hw *hw)
free_irq(hw->irq, hw);
hw->irq = -1;
-
- if (hw->mem_base)
- iounmap((void *)hw->mem_base);
-
- hw->mem_base = (unsigned long)NULL;
+ iounmap(hw->mem_base);
+ hw->mem_base = NULL;
if (hw->io_base)
pci_release_regions(hw->pci);
@@ -2031,7 +2021,7 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
hw_write_20kx(hw, GIE, 0);
/* Reset all SRC pending interrupts */
hw_write_20kx(hw, SRCIP, 0);
- mdelay(30);
+ msleep(30);
/* Detect the card ID and configure GPIO accordingly. */
switch (hw->model) {
@@ -2078,8 +2068,8 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
return 0;
}
-#ifdef CONFIG_PM
-static int hw_suspend(struct hw *hw, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int hw_suspend(struct hw *hw)
{
struct pci_dev *pci = hw->pci;
@@ -2090,20 +2080,11 @@ static int hw_suspend(struct hw *hw, pm_message_t state)
pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x0);
}
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
-
return 0;
}
static int hw_resume(struct hw *hw, struct card_conf *info)
{
- struct pci_dev *pci = hw->pci;
-
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
-
/* Re-initialize card hardware. */
return hw_card_init(hw, info);
}
@@ -2111,60 +2092,33 @@ static int hw_resume(struct hw *hw, struct card_conf *info)
static u32 hw_read_20kx(struct hw *hw, u32 reg)
{
- u32 value;
- unsigned long flags;
-
- spin_lock_irqsave(
- &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
+ guard(spinlock_irqsave)(&container_of(hw, struct hw20k1, hw)->reg_20k1_lock);
outl(reg, hw->io_base + 0x0);
- value = inl(hw->io_base + 0x4);
- spin_unlock_irqrestore(
- &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
-
- return value;
+ return inl(hw->io_base + 0x4);
}
static void hw_write_20kx(struct hw *hw, u32 reg, u32 data)
{
- unsigned long flags;
-
- spin_lock_irqsave(
- &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
+ guard(spinlock_irqsave)(&container_of(hw, struct hw20k1, hw)->reg_20k1_lock);
outl(reg, hw->io_base + 0x0);
outl(data, hw->io_base + 0x4);
- spin_unlock_irqrestore(
- &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
-
}
static u32 hw_read_pci(struct hw *hw, u32 reg)
{
- u32 value;
- unsigned long flags;
-
- spin_lock_irqsave(
- &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
+ guard(spinlock_irqsave)(&container_of(hw, struct hw20k1, hw)->reg_pci_lock);
outl(reg, hw->io_base + 0x10);
- value = inl(hw->io_base + 0x14);
- spin_unlock_irqrestore(
- &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
-
- return value;
+ return inl(hw->io_base + 0x14);
}
static void hw_write_pci(struct hw *hw, u32 reg, u32 data)
{
- unsigned long flags;
-
- spin_lock_irqsave(
- &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
+ guard(spinlock_irqsave)(&container_of(hw, struct hw20k1, hw)->reg_pci_lock);
outl(reg, hw->io_base + 0x10);
outl(data, hw->io_base + 0x14);
- spin_unlock_irqrestore(
- &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
}
-static struct hw ct20k1_preset __devinitdata = {
+static const struct hw ct20k1_preset = {
.irq = -1,
.card_init = hw_card_init,
@@ -2172,8 +2126,8 @@ static struct hw ct20k1_preset __devinitdata = {
.pll_init = hw_pll_init,
.is_adc_source_selected = hw_is_adc_input_selected,
.select_adc_source = hw_adc_input_select,
- .have_digit_io_switch = hw_have_digit_io_switch,
-#ifdef CONFIG_PM
+ .capabilities = hw_capabilities,
+#ifdef CONFIG_PM_SLEEP
.suspend = hw_suspend,
.resume = hw_resume,
#endif
@@ -2268,7 +2222,7 @@ static struct hw ct20k1_preset __devinitdata = {
.get_wc = get_wc,
};
-int __devinit create_20k1_hw_obj(struct hw **rhw)
+int create_20k1_hw_obj(struct hw **rhw)
{
struct hw20k1 *hw20k1;
diff --git a/sound/pci/ctxfi/cthw20k1.h b/sound/pci/ctxfi/cthw20k1.h
index 02f72fb448a6..ffb019abf651 100644
--- a/sound/pci/ctxfi/cthw20k1.h
+++ b/sound/pci/ctxfi/cthw20k1.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File cthw20k1.h
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date May 13 2008
- *
*/
#ifndef CTHW20K1_H
diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c
index b6b11bfe7574..fac88f5590c9 100644
--- a/sound/pci/ctxfi/cthw20k2.c
+++ b/sound/pci/ctxfi/cthw20k2.c
@@ -1,18 +1,14 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File cthw20k2.c
*
* @Brief
- * This file contains the implementation of hardware access methord for 20k2.
+ * This file contains the implementation of hardware access method for 20k2.
*
* @Author Liu Chun
* @Date May 14 2008
- *
*/
#include <linux/types.h>
@@ -26,18 +22,14 @@
#include "cthw20k2.h"
#include "ct20k2reg.h"
-#if BITS_PER_LONG == 32
-#define CT_XFI_DMA_MASK DMA_BIT_MASK(32) /* 32 bit PTE */
-#else
-#define CT_XFI_DMA_MASK DMA_BIT_MASK(64) /* 64 bit PTE */
-#endif
-
struct hw20k2 {
struct hw hw;
/* for i2c */
unsigned char dev_id;
unsigned char addr_size;
unsigned char data_size;
+
+ int mic_source;
};
static u32 hw_read_20kx(struct hw *hw, u32 reg);
@@ -918,7 +910,7 @@ static int dao_commit_write(struct hw *hw, unsigned int idx, void *blk)
struct dao_ctrl_blk *ctl = blk;
if (ctl->dirty.bf.atxcsl) {
- if (idx < 4) {
+ if ((idx < 4) && ((hw->model != CTOK0010) || (idx < 3))) {
/* S/PDIF SPOSx */
hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_L+0x40*idx,
ctl->atxcsl);
@@ -993,13 +985,14 @@ static int daio_mgr_dsb_dao(void *blk, unsigned int idx)
return 0;
}
-static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf)
+static int daio_mgr_dao_init(struct hw *hw, void *blk, unsigned int idx, unsigned int conf)
{
struct daio_mgr_ctrl_blk *ctl = blk;
- if (idx < 4) {
+ /* Port 3 is dedicated to RCA on SE-300PCIE */
+ if ((idx < 4) && ((hw->model != CTOK0010) || (idx < 3))) {
/* S/PDIF output */
- switch ((conf & 0x7)) {
+ switch ((conf & 0xf)) {
case 1:
set_field(&ctl->txctl[idx], ATXCTL_NUC, 0);
break;
@@ -1163,7 +1156,12 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x01010101);
hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
} else if (2 == info->msr) {
- hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111);
+ if (hw->model != CTSB1270) {
+ hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111);
+ } else {
+ /* PCM4220 on Titanium HD is different. */
+ hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11011111);
+ }
/* Specify all playing 96khz
* EA [0] - Enabled
* RTA [4:5] - 96kHz
@@ -1175,13 +1173,25 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
* RTD [28:29] - 96kHz */
hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x11111111);
hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
+ } else if ((4 == info->msr) && (hw->model == CTSB1270)) {
+ hw_write_20kx(hw, AUDIO_IO_MCLK, 0x21011111);
+ hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x21212121);
+ hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
+ } else if ((4 == info->msr) && (hw->model == CTOK0010)) {
+ hw_write_20kx(hw, AUDIO_IO_MCLK, 0x21212121);
+ hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x21212121);
+ hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
} else {
- printk(KERN_ALERT "ctxfi: ERROR!!! Invalid sampling rate!!!\n");
+ dev_alert(hw->card->dev,
+ "ERROR!!! Invalid sampling rate!!!\n");
return -EINVAL;
}
for (i = 0; i < 8; i++) {
- if (i <= 3) {
+ /* Port 3 is configured as I2S on SE-300PCIE */
+ if ((i < 4) && ((hw->model != CTOK0010) || (i < 3))) {
+ /* This comment looks wrong since loop is over 4 */
+ /* channels and emu20k2 supports 4 spdif IOs. */
/* 1st 3 channels are SPDIFs (SB0960) */
if (i == 3)
data = 0x1001001;
@@ -1206,12 +1216,16 @@ static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_H+(0x40*i), 0x0B);
} else {
+ /* Again, loop is over 4 channels not 5. */
/* Next 5 channels are I2S (SB0960) */
data = 0x11;
hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), data);
if (2 == info->msr) {
/* Four channels per sample period */
data |= 0x1000;
+ } else if (4 == info->msr) {
+ /* FIXME: check this against the chip spec */
+ data |= 0x2000;
}
hw_write_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i), data);
}
@@ -1229,8 +1243,8 @@ static int hw_trn_init(struct hw *hw, const struct trn_conf *info)
/* Set up device page table */
if ((~0UL) == info->vm_pgt_phys) {
- printk(KERN_ALERT "ctxfi: "
- "Wrong device page table page address!!!\n");
+ dev_alert(hw->card->dev,
+ "Wrong device page table page address!!!\n");
return -1;
}
@@ -1299,21 +1313,18 @@ static int hw_pll_init(struct hw *hw, unsigned int rsr)
pllenb = 0xB;
hw_write_20kx(hw, PLL_ENB, pllenb);
- pllctl = 0x20D00000;
- set_field(&pllctl, PLLCTL_FD, 16 - 4);
+ pllctl = 0x20C00000;
+ set_field(&pllctl, PLLCTL_B, 0);
+ set_field(&pllctl, PLLCTL_FD, 48000 == rsr ? 16 - 4 : 147 - 4);
+ set_field(&pllctl, PLLCTL_RD, 48000 == rsr ? 1 - 1 : 10 - 1);
hw_write_20kx(hw, PLL_CTL, pllctl);
- mdelay(40);
+ msleep(40);
+
pllctl = hw_read_20kx(hw, PLL_CTL);
- set_field(&pllctl, PLLCTL_B, 0);
- if (48000 == rsr) {
- set_field(&pllctl, PLLCTL_FD, 16 - 2);
- set_field(&pllctl, PLLCTL_RD, 1 - 1);
- } else { /* 44100 */
- set_field(&pllctl, PLLCTL_FD, 147 - 2);
- set_field(&pllctl, PLLCTL_RD, 10 - 1);
- }
+ set_field(&pllctl, PLLCTL_FD, 48000 == rsr ? 16 - 2 : 147 - 2);
hw_write_20kx(hw, PLL_CTL, pllctl);
- mdelay(40);
+ msleep(40);
+
for (i = 0; i < 1000; i++) {
pllstat = hw_read_20kx(hw, PLL_STAT);
if (get_field(pllstat, PLLSTAT_PD))
@@ -1338,7 +1349,8 @@ static int hw_pll_init(struct hw *hw, unsigned int rsr)
break;
}
if (i >= 1000) {
- printk(KERN_ALERT "ctxfi: PLL initialization failed!!!\n");
+ dev_alert(hw->card->dev,
+ "PLL initialization failed!!!\n");
return -EBUSY;
}
@@ -1362,7 +1374,7 @@ static int hw_auto_init(struct hw *hw)
break;
}
if (!get_field(gctl, GCTL_AID)) {
- printk(KERN_ALERT "ctxfi: Card Auto-init failed!!!\n");
+ dev_alert(hw->card->dev, "Card Auto-init failed!!!\n");
return -EBUSY;
}
@@ -1557,7 +1569,7 @@ static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data)
hw_write_20kx(hw, I2C_IF_STATUS, i2c_status);
hw20k2_i2c_wait_data_ready(hw);
- /* Dummy write to trigger the write oprtation */
+ /* Dummy write to trigger the write operation */
hw_write_20kx(hw, I2C_IF_WDATA, 0);
hw20k2_i2c_wait_data_ready(hw);
@@ -1568,6 +1580,30 @@ static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data)
return 0;
}
+static void hw_dac_stop(struct hw *hw)
+{
+ u32 data;
+ data = hw_read_20kx(hw, GPIO_DATA);
+ data &= 0xFFFFFFFD;
+ hw_write_20kx(hw, GPIO_DATA, data);
+ usleep_range(10000, 11000);
+}
+
+static void hw_dac_start(struct hw *hw)
+{
+ u32 data;
+ data = hw_read_20kx(hw, GPIO_DATA);
+ data |= 0x2;
+ hw_write_20kx(hw, GPIO_DATA, data);
+ msleep(50);
+}
+
+static void hw_dac_reset(struct hw *hw)
+{
+ hw_dac_stop(hw);
+ hw_dac_start(hw);
+}
+
static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
{
int err;
@@ -1575,25 +1611,47 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
int i;
struct regs_cs4382 cs_read = {0};
struct regs_cs4382 cs_def = {
- 0x00000001, /* Mode Control 1 */
- 0x00000000, /* Mode Control 2 */
- 0x00000084, /* Mode Control 3 */
- 0x00000000, /* Filter Control */
- 0x00000000, /* Invert Control */
- 0x00000024, /* Mixing Control Pair 1 */
- 0x00000000, /* Vol Control A1 */
- 0x00000000, /* Vol Control B1 */
- 0x00000024, /* Mixing Control Pair 2 */
- 0x00000000, /* Vol Control A2 */
- 0x00000000, /* Vol Control B2 */
- 0x00000024, /* Mixing Control Pair 3 */
- 0x00000000, /* Vol Control A3 */
- 0x00000000, /* Vol Control B3 */
- 0x00000024, /* Mixing Control Pair 4 */
- 0x00000000, /* Vol Control A4 */
- 0x00000000 /* Vol Control B4 */
+ .mode_control_1 = 0x00000001, /* Mode Control 1 */
+ .mode_control_2 = 0x00000000, /* Mode Control 2 */
+ .mode_control_3 = 0x00000084, /* Mode Control 3 */
+ .filter_control = 0x00000000, /* Filter Control */
+ .invert_control = 0x00000000, /* Invert Control */
+ .mix_control_P1 = 0x00000024, /* Mixing Control Pair 1 */
+ .vol_control_A1 = 0x00000000, /* Vol Control A1 */
+ .vol_control_B1 = 0x00000000, /* Vol Control B1 */
+ .mix_control_P2 = 0x00000024, /* Mixing Control Pair 2 */
+ .vol_control_A2 = 0x00000000, /* Vol Control A2 */
+ .vol_control_B2 = 0x00000000, /* Vol Control B2 */
+ .mix_control_P3 = 0x00000024, /* Mixing Control Pair 3 */
+ .vol_control_A3 = 0x00000000, /* Vol Control A3 */
+ .vol_control_B3 = 0x00000000, /* Vol Control B3 */
+ .mix_control_P4 = 0x00000024, /* Mixing Control Pair 4 */
+ .vol_control_A4 = 0x00000000, /* Vol Control A4 */
+ .vol_control_B4 = 0x00000000 /* Vol Control B4 */
};
+ if (hw->model == CTSB1270) {
+ hw_dac_stop(hw);
+ data = hw_read_20kx(hw, GPIO_DATA);
+ data &= ~0x0600;
+ if (1 == info->msr)
+ data |= 0x0000; /* Single Speed Mode 0-50kHz */
+ else if (2 == info->msr)
+ data |= 0x0200; /* Double Speed Mode 50-100kHz */
+ else
+ data |= 0x0600; /* Quad Speed Mode 100-200kHz */
+ hw_write_20kx(hw, GPIO_DATA, data);
+ hw_dac_start(hw);
+ return 0;
+ } else if (hw->model == CTOK0010) {
+ hw_dac_stop(hw);
+ data = hw_read_20kx(hw, GPIO_DATA);
+ data |= 0x1000;
+ hw_write_20kx(hw, GPIO_DATA, data);
+ hw_dac_start(hw);
+ return 0;
+ }
+
/* Set DAC reset bit as output */
data = hw_read_20kx(hw, GPIO_CTRL);
data |= 0x02;
@@ -1606,22 +1664,8 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
for (i = 0; i < 2; i++) {
/* Reset DAC twice just in-case the chip
* didn't initialized properly */
- data = hw_read_20kx(hw, GPIO_DATA);
- /* GPIO data bit 1 */
- data &= 0xFFFFFFFD;
- hw_write_20kx(hw, GPIO_DATA, data);
- mdelay(10);
- data |= 0x2;
- hw_write_20kx(hw, GPIO_DATA, data);
- mdelay(50);
-
- /* Reset the 2nd time */
- data &= 0xFFFFFFFD;
- hw_write_20kx(hw, GPIO_DATA, data);
- mdelay(10);
- data |= 0x2;
- hw_write_20kx(hw, GPIO_DATA, data);
- mdelay(50);
+ hw_dac_reset(hw);
+ hw_dac_reset(hw);
if (hw20k2_i2c_read(hw, CS4382_MC1, &cs_read.mode_control_1))
continue;
@@ -1725,7 +1769,13 @@ End:
static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type)
{
u32 data;
-
+ if ((hw->model == CTSB1270) || (hw->model == CTOK0010)) {
+ /* Titanium HD has two ADC chips, one for line in and one */
+ /* for MIC. Also, SE-300PCIE has a single ADC chip that */
+ /* simultaneously supports 4-channel input. We don't need */
+ /* to switch the ADC input. */
+ return 1;
+ }
data = hw_read_20kx(hw, GPIO_DATA);
switch (type) {
case ADC_MICIN:
@@ -1740,31 +1790,49 @@ static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type)
return data;
}
+#define MIC_BOOST_0DB 0xCF
+#define MIC_BOOST_STEPS_PER_DB 2
+
+static void hw_wm8775_input_select(struct hw *hw, u8 input, s8 gain_in_db)
+{
+ u32 adcmc, gain;
+
+ if (input > 3)
+ input = 3;
+
+ adcmc = ((u32)1 << input) | 0x100; /* Link L+R gain... */
+
+ hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, adcmc),
+ MAKE_WM8775_DATA(adcmc));
+
+ if (gain_in_db < -103)
+ gain_in_db = -103;
+ if (gain_in_db > 24)
+ gain_in_db = 24;
+
+ gain = gain_in_db * MIC_BOOST_STEPS_PER_DB + MIC_BOOST_0DB;
+
+ hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, gain),
+ MAKE_WM8775_DATA(gain));
+ /* ...so there should be no need for the following. */
+ hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, gain),
+ MAKE_WM8775_DATA(gain));
+}
+
static int hw_adc_input_select(struct hw *hw, enum ADCSRC type)
{
u32 data;
-
data = hw_read_20kx(hw, GPIO_DATA);
switch (type) {
case ADC_MICIN:
data |= (0x1 << 14);
hw_write_20kx(hw, GPIO_DATA, data);
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101),
- MAKE_WM8775_DATA(0x101)); /* Mic-in */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7),
- MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7),
- MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
+ hw_wm8775_input_select(hw, 0, 20); /* Mic, 20dB */
break;
case ADC_LINEIN:
data &= ~(0x1 << 14);
hw_write_20kx(hw, GPIO_DATA, data);
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102),
- MAKE_WM8775_DATA(0x102)); /* Line-in */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF),
- MAKE_WM8775_DATA(0xCF)); /* No boost */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF),
- MAKE_WM8775_DATA(0xCF)); /* No boost */
+ hw_wm8775_input_select(hw, 1, 0); /* Line-in, 0dB */
break;
default:
break;
@@ -1773,92 +1841,202 @@ static int hw_adc_input_select(struct hw *hw, enum ADCSRC type)
return 0;
}
+static void hw_adc_stop(struct hw *hw)
+{
+ u32 data;
+ /* Reset the ADC (reset is active low). */
+ data = hw_read_20kx(hw, GPIO_DATA);
+ data &= ~(0x1 << 15);
+ hw_write_20kx(hw, GPIO_DATA, data);
+ usleep_range(10000, 11000);
+}
+
+static void hw_adc_start(struct hw *hw)
+{
+ u32 data;
+ /* Return the ADC to normal operation. */
+ data = hw_read_20kx(hw, GPIO_DATA);
+ data |= (0x1 << 15);
+ hw_write_20kx(hw, GPIO_DATA, data);
+ msleep(50);
+}
+
+static void hw_adc_reset(struct hw *hw)
+{
+ hw_adc_stop(hw);
+ hw_adc_start(hw);
+}
+
static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
{
int err;
- u32 mux = 2, data, ctl;
+ u32 data, ctl;
/* Set ADC reset bit as output */
data = hw_read_20kx(hw, GPIO_CTRL);
data |= (0x1 << 15);
hw_write_20kx(hw, GPIO_CTRL, data);
+ if (hw->model == CTOK0010) {
+ /* Manual ADC setup for SE-300PCIE is not needed. */
+ hw_adc_reset(hw);
+ return 0;
+ }
+
/* Initialize I2C */
err = hw20k2_i2c_init(hw, 0x1A, 1, 1);
if (err < 0) {
- printk(KERN_ALERT "ctxfi: Failure to acquire I2C!!!\n");
+ dev_alert(hw->card->dev, "Failure to acquire I2C!!!\n");
goto error;
}
- /* Make ADC in normal operation */
- data = hw_read_20kx(hw, GPIO_DATA);
- data &= ~(0x1 << 15);
- mdelay(10);
- data |= (0x1 << 15);
- hw_write_20kx(hw, GPIO_DATA, data);
- mdelay(50);
+ hw_adc_stop(hw);
+
+ if (hw->model == CTSB1270) {
+ /* Set up the PCM4220 ADC on Titanium HD */
+ data &= ~0x0C;
+ if (1 == info->msr)
+ data |= 0x00; /* Single Speed Mode 32-50kHz */
+ else if (2 == info->msr)
+ data |= 0x08; /* Double Speed Mode 50-108kHz */
+ else
+ data |= 0x04; /* Quad Speed Mode 108kHz-216kHz */
+ hw_write_20kx(hw, GPIO_DATA, data);
+ }
+
+ hw_adc_start(hw);
+
+ /* I2C write to register offset 0x0B to set ADC LRCLK polarity */
+ /* invert bit, interface format to I2S, word length to 24-bit, */
+ /* enable ADC high pass filter. Fixes bug 5323? */
+ hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_IC, 0x26),
+ MAKE_WM8775_DATA(0x26));
/* Set the master mode (256fs) */
if (1 == info->msr) {
+ /* slave mode, 128x oversampling 256fs */
hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02),
MAKE_WM8775_DATA(0x02));
- } else if (2 == info->msr) {
+ } else if ((2 == info->msr) || (4 == info->msr)) {
+ /* slave mode, 64x oversampling, 256fs */
hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A),
MAKE_WM8775_DATA(0x0A));
} else {
- printk(KERN_ALERT "ctxfi: Invalid master sampling "
- "rate (msr %d)!!!\n", info->msr);
+ dev_alert(hw->card->dev,
+ "Invalid master sampling rate (msr %d)!!!\n",
+ info->msr);
err = -EINVAL;
goto error;
}
- /* Configure GPIO bit 14 change to line-in/mic-in */
- ctl = hw_read_20kx(hw, GPIO_CTRL);
- ctl |= 0x1 << 14;
- hw_write_20kx(hw, GPIO_CTRL, ctl);
-
- /* Check using Mic-in or Line-in */
- data = hw_read_20kx(hw, GPIO_DATA);
-
- if (mux == 1) {
- /* Configures GPIO data to select Mic-in */
- data |= 0x1 << 14;
- hw_write_20kx(hw, GPIO_DATA, data);
-
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101),
- MAKE_WM8775_DATA(0x101)); /* Mic-in */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7),
- MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7),
- MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
- } else if (mux == 2) {
- /* Configures GPIO data to select Line-in */
- data &= ~(0x1 << 14);
- hw_write_20kx(hw, GPIO_DATA, data);
-
- /* Setup ADC */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102),
- MAKE_WM8775_DATA(0x102)); /* Line-in */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF),
- MAKE_WM8775_DATA(0xCF)); /* No boost */
- hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF),
- MAKE_WM8775_DATA(0xCF)); /* No boost */
+ if (hw->model != CTSB1270) {
+ /* Configure GPIO bit 14 change to line-in/mic-in */
+ ctl = hw_read_20kx(hw, GPIO_CTRL);
+ ctl |= 0x1 << 14;
+ hw_write_20kx(hw, GPIO_CTRL, ctl);
+ hw_adc_input_select(hw, ADC_LINEIN);
} else {
- printk(KERN_ALERT "ctxfi: ERROR!!! Invalid input mux!!!\n");
- err = -EINVAL;
- goto error;
+ hw_wm8775_input_select(hw, 0, 0);
}
return 0;
-
error:
hw20k2_i2c_uninit(hw);
return err;
}
-static int hw_have_digit_io_switch(struct hw *hw)
+static struct capabilities hw_capabilities(struct hw *hw)
{
- return 0;
+ struct capabilities cap;
+
+ cap.digit_io_switch = 0;
+ cap.dedicated_mic = (hw->model == CTSB1270) || (hw->model == CTOK0010);
+ cap.dedicated_rca = hw->model == CTOK0010;
+ cap.output_switch = hw->model == CTSB1270;
+ cap.mic_source_switch = hw->model == CTSB1270;
+
+ return cap;
+}
+
+static int hw_output_switch_get(struct hw *hw)
+{
+ u32 data = hw_read_20kx(hw, GPIO_EXT_DATA);
+
+ switch (data & 0x30) {
+ case 0x00:
+ return 0;
+ case 0x10:
+ return 1;
+ case 0x20:
+ return 2;
+ default:
+ return 3;
+ }
+}
+
+static int hw_output_switch_put(struct hw *hw, int position)
+{
+ u32 data;
+
+ if (position == hw_output_switch_get(hw))
+ return 0;
+
+ /* Mute line and headphones (intended for anti-pop). */
+ data = hw_read_20kx(hw, GPIO_DATA);
+ data |= (0x03 << 11);
+ hw_write_20kx(hw, GPIO_DATA, data);
+
+ data = hw_read_20kx(hw, GPIO_EXT_DATA) & ~0x30;
+ switch (position) {
+ case 0:
+ break;
+ case 1:
+ data |= 0x10;
+ break;
+ default:
+ data |= 0x20;
+ }
+ hw_write_20kx(hw, GPIO_EXT_DATA, data);
+
+ /* Unmute line and headphones. */
+ data = hw_read_20kx(hw, GPIO_DATA);
+ data &= ~(0x03 << 11);
+ hw_write_20kx(hw, GPIO_DATA, data);
+
+ return 1;
+}
+
+static int hw_mic_source_switch_get(struct hw *hw)
+{
+ struct hw20k2 *hw20k2 = (struct hw20k2 *)hw;
+
+ return hw20k2->mic_source;
+}
+
+static int hw_mic_source_switch_put(struct hw *hw, int position)
+{
+ struct hw20k2 *hw20k2 = (struct hw20k2 *)hw;
+
+ if (position == hw20k2->mic_source)
+ return 0;
+
+ switch (position) {
+ case 0:
+ hw_wm8775_input_select(hw, 0, 0); /* Mic, 0dB */
+ break;
+ case 1:
+ hw_wm8775_input_select(hw, 1, 0); /* FP Mic, 0dB */
+ break;
+ case 2:
+ hw_wm8775_input_select(hw, 3, 0); /* Aux Ext, 0dB */
+ break;
+ default:
+ return 0;
+ }
+
+ hw20k2->mic_source = position;
+
+ return 1;
}
static irqreturn_t ct_20k2_interrupt(int irq, void *dev_id)
@@ -1882,19 +2060,15 @@ static int hw_card_start(struct hw *hw)
int err = 0;
struct pci_dev *pci = hw->pci;
unsigned int gctl;
+ const unsigned int dma_bits = BITS_PER_LONG;
err = pci_enable_device(pci);
if (err < 0)
return err;
/* Set DMA transfer mask */
- if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 ||
- pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) {
- printk(KERN_ERR "ctxfi: architecture does not support PCI "
- "busmaster DMA with mask 0x%llx\n", CT_XFI_DMA_MASK);
- err = -ENXIO;
- goto error1;
- }
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(dma_bits)))
+ dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32));
if (!hw->io_base) {
err = pci_request_regions(pci, "XFi");
@@ -1902,8 +2076,8 @@ static int hw_card_start(struct hw *hw)
goto error1;
hw->io_base = pci_resource_start(hw->pci, 2);
- hw->mem_base = (unsigned long)ioremap(hw->io_base,
- pci_resource_len(hw->pci, 2));
+ hw->mem_base = ioremap(hw->io_base,
+ pci_resource_len(hw->pci, 2));
if (!hw->mem_base) {
err = -ENOENT;
goto error2;
@@ -1917,12 +2091,14 @@ static int hw_card_start(struct hw *hw)
if (hw->irq < 0) {
err = request_irq(pci->irq, ct_20k2_interrupt, IRQF_SHARED,
- "ctxfi", hw);
+ KBUILD_MODNAME, hw);
if (err < 0) {
- printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq);
+ dev_err(hw->card->dev,
+ "XFi: Cannot get irq %d\n", pci->irq);
goto error2;
}
hw->irq = pci->irq;
+ hw->card->sync_irq = hw->irq;
}
pci_set_master(pci);
@@ -1961,11 +2137,8 @@ static int hw_card_shutdown(struct hw *hw)
free_irq(hw->irq, hw);
hw->irq = -1;
-
- if (hw->mem_base)
- iounmap((void *)hw->mem_base);
-
- hw->mem_base = (unsigned long)NULL;
+ iounmap(hw->mem_base);
+ hw->mem_base = NULL;
if (hw->io_base)
pci_release_regions(hw->pci);
@@ -2015,13 +2188,18 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
/* Reset all SRC pending interrupts */
hw_write_20kx(hw, SRC_IP, 0);
- /* TODO: detect the card ID and configure GPIO accordingly. */
- /* Configures GPIO (0xD802 0x98028) */
- /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/
- /* Configures GPIO (SB0880) */
- /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/
- hw_write_20kx(hw, GPIO_CTRL, 0xD802);
-
+ if (hw->model == CTSB1270) {
+ hw_write_20kx(hw, GPIO_CTRL, 0x9E5F);
+ } else if (hw->model == CTOK0010) {
+ hw_write_20kx(hw, GPIO_CTRL, 0x9902);
+ } else {
+ /* TODO: detect the card ID and configure GPIO accordingly. */
+ /* Configures GPIO (0xD802 0x98028) */
+ /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/
+ /* Configures GPIO (SB0880) */
+ /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/
+ hw_write_20kx(hw, GPIO_CTRL, 0xD802);
+ }
/* Enable audio ring */
hw_write_20kx(hw, MIXER_AR_ENABLE, 0x01);
@@ -2054,27 +2232,15 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
return 0;
}
-#ifdef CONFIG_PM
-static int hw_suspend(struct hw *hw, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int hw_suspend(struct hw *hw)
{
- struct pci_dev *pci = hw->pci;
-
hw_card_stop(hw);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
-
return 0;
}
static int hw_resume(struct hw *hw, struct card_conf *info)
{
- struct pci_dev *pci = hw->pci;
-
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
-
/* Re-initialize card hardware. */
return hw_card_init(hw, info);
}
@@ -2082,15 +2248,15 @@ static int hw_resume(struct hw *hw, struct card_conf *info)
static u32 hw_read_20kx(struct hw *hw, u32 reg)
{
- return readl((void *)(hw->mem_base + reg));
+ return readl(hw->mem_base + reg);
}
static void hw_write_20kx(struct hw *hw, u32 reg, u32 data)
{
- writel(data, (void *)(hw->mem_base + reg));
+ writel(data, hw->mem_base + reg);
}
-static struct hw ct20k2_preset __devinitdata = {
+static const struct hw ct20k2_preset = {
.irq = -1,
.card_init = hw_card_init,
@@ -2098,8 +2264,12 @@ static struct hw ct20k2_preset __devinitdata = {
.pll_init = hw_pll_init,
.is_adc_source_selected = hw_is_adc_input_selected,
.select_adc_source = hw_adc_input_select,
- .have_digit_io_switch = hw_have_digit_io_switch,
-#ifdef CONFIG_PM
+ .capabilities = hw_capabilities,
+ .output_switch_get = hw_output_switch_get,
+ .output_switch_put = hw_output_switch_put,
+ .mic_source_switch_get = hw_mic_source_switch_get,
+ .mic_source_switch_put = hw_mic_source_switch_put,
+#ifdef CONFIG_PM_SLEEP
.suspend = hw_suspend,
.resume = hw_resume,
#endif
@@ -2194,7 +2364,7 @@ static struct hw ct20k2_preset __devinitdata = {
.get_wc = get_wc,
};
-int __devinit create_20k2_hw_obj(struct hw **rhw)
+int create_20k2_hw_obj(struct hw **rhw)
{
struct hw20k2 *hw20k2;
diff --git a/sound/pci/ctxfi/cthw20k2.h b/sound/pci/ctxfi/cthw20k2.h
index d2b7daab6815..6993a3d5277a 100644
--- a/sound/pci/ctxfi/cthw20k2.h
+++ b/sound/pci/ctxfi/cthw20k2.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File cthw20k2.h
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date May 13 2008
- *
*/
#ifndef CTHW20K2_H
diff --git a/sound/pci/ctxfi/ctimap.c b/sound/pci/ctxfi/ctimap.c
index 0b73368a4df6..d5a53d2f5f15 100644
--- a/sound/pci/ctxfi/ctimap.c
+++ b/sound/pci/ctxfi/ctimap.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctimap.c
*
* @Brief
@@ -13,7 +10,6 @@
*
* @Author Liu Chun
* @Date May 23 2008
- *
*/
#include "ctimap.h"
diff --git a/sound/pci/ctxfi/ctimap.h b/sound/pci/ctxfi/ctimap.h
index 53ccf9be8b68..49b1bb831410 100644
--- a/sound/pci/ctxfi/ctimap.h
+++ b/sound/pci/ctxfi/ctimap.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctimap.h
*
* @Brief
@@ -13,7 +10,6 @@
*
* @Author Liu Chun
* @Date May 23 2008
- *
*/
#ifndef CTIMAP_H
diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c
index 15c1e7271ea8..fc9fde284fb3 100644
--- a/sound/pci/ctxfi/ctmixer.c
+++ b/sound/pci/ctxfi/ctmixer.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctmixer.c
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date May 28 2008
- *
*/
@@ -86,9 +82,7 @@ enum CTALSA_MIXER_CTL {
MIXER_LINEIN_C_S,
MIXER_MIC_C_S,
MIXER_SPDIFI_C_S,
- MIXER_LINEIN_P_S,
MIXER_SPDIFO_P_S,
- MIXER_SPDIFI_P_S,
MIXER_WAVEF_P_S,
MIXER_WAVER_P_S,
MIXER_WAVEC_P_S,
@@ -137,11 +131,11 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
},
[MIXER_LINEIN_P] = {
.ctl = 1,
- .name = "Line-in Playback Volume",
+ .name = "Line Playback Volume",
},
[MIXER_LINEIN_C] = {
.ctl = 1,
- .name = "Line-in Capture Volume",
+ .name = "Line Capture Volume",
},
[MIXER_MIC_P] = {
.ctl = 1,
@@ -153,15 +147,15 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
},
[MIXER_SPDIFI_P] = {
.ctl = 1,
- .name = "S/PDIF-in Playback Volume",
+ .name = "IEC958 Playback Volume",
},
[MIXER_SPDIFI_C] = {
.ctl = 1,
- .name = "S/PDIF-in Capture Volume",
+ .name = "IEC958 Capture Volume",
},
[MIXER_SPDIFO_P] = {
.ctl = 1,
- .name = "S/PDIF-out Playback Volume",
+ .name = "Digital Playback Volume",
},
[MIXER_WAVEF_P] = {
.ctl = 1,
@@ -179,14 +173,13 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
.ctl = 1,
.name = "Surround Playback Volume",
},
-
[MIXER_PCM_C_S] = {
.ctl = 1,
.name = "PCM Capture Switch",
},
[MIXER_LINEIN_C_S] = {
.ctl = 1,
- .name = "Line-in Capture Switch",
+ .name = "Line Capture Switch",
},
[MIXER_MIC_C_S] = {
.ctl = 1,
@@ -194,19 +187,11 @@ ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
},
[MIXER_SPDIFI_C_S] = {
.ctl = 1,
- .name = "S/PDIF-in Capture Switch",
- },
- [MIXER_LINEIN_P_S] = {
- .ctl = 1,
- .name = "Line-in Playback Switch",
+ .name = "IEC958 Capture Switch",
},
[MIXER_SPDIFO_P_S] = {
.ctl = 1,
- .name = "S/PDIF-out Playback Switch",
- },
- [MIXER_SPDIFI_P_S] = {
- .ctl = 1,
- .name = "S/PDIF-in Playback Switch",
+ .name = "Digital Playback Switch",
},
[MIXER_WAVEF_P_S] = {
.ctl = 1,
@@ -236,6 +221,8 @@ ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type);
static void
ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type);
+/* FIXME: this static looks like it would fail if more than one card was */
+/* installed. */
static struct snd_kcontrol *kctls[2] = {NULL};
static enum CT_AMIXER_CTL get_amixer_index(enum CTALSA_MIXER_CTL alsa_index)
@@ -420,6 +407,77 @@ static struct snd_kcontrol_new vol_ctl = {
.tlv = { .p = ct_vol_db_scale },
};
+static int output_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[3] = {
+ "FP Headphones", "Headphones", "Speakers"
+ };
+
+ return snd_ctl_enum_info(info, 1, 3, names);
+}
+
+static int output_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.enumerated.item[0] = atc->output_switch_get(atc);
+ return 0;
+}
+
+static int output_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+ if (ucontrol->value.enumerated.item[0] > 2)
+ return -EINVAL;
+ return atc->output_switch_put(atc, ucontrol->value.enumerated.item[0]);
+}
+
+static struct snd_kcontrol_new output_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Output Playback Enum",
+ .info = output_switch_info,
+ .get = output_switch_get,
+ .put = output_switch_put,
+};
+
+static int mic_source_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[3] = {
+ "Mic", "FP Mic", "Aux"
+ };
+
+ return snd_ctl_enum_info(info, 1, 3, names);
+}
+
+static int mic_source_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.enumerated.item[0] = atc->mic_source_switch_get(atc);
+ return 0;
+}
+
+static int mic_source_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+ if (ucontrol->value.enumerated.item[0] > 2)
+ return -EINVAL;
+ return atc->mic_source_switch_put(atc,
+ ucontrol->value.enumerated.item[0]);
+}
+
+static struct snd_kcontrol_new mic_source_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Mic Source Capture Enum",
+ .info = mic_source_switch_info,
+ .get = mic_source_switch_get,
+ .put = mic_source_switch_put,
+};
+
static void
do_line_mic_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type)
{
@@ -465,6 +523,7 @@ do_digit_io_switch(struct ct_atc *atc, int state)
static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state)
{
struct ct_mixer *mixer = atc->mixer;
+ struct capabilities cap = atc->capabilities(atc);
/* Do changes in mixer. */
if ((SWH_CAPTURE_START <= type) && (SWH_CAPTURE_END >= type)) {
@@ -477,22 +536,33 @@ static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state)
}
}
/* Do changes out of mixer. */
- if (state && (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type))
- do_line_mic_switch(atc, type);
- else if (MIXER_WAVEF_P_S == type)
- atc->line_front_unmute(atc, state);
+ if (!cap.dedicated_mic &&
+ (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type)) {
+ if (state)
+ do_line_mic_switch(atc, type);
+ atc->line_in_unmute(atc, state);
+ } else if (cap.dedicated_mic && (MIXER_LINEIN_C_S == type))
+ atc->line_in_unmute(atc, state);
+ else if (cap.dedicated_mic && (MIXER_MIC_C_S == type))
+ atc->mic_unmute(atc, state);
+ else if (MIXER_SPDIFI_C_S == type)
+ atc->spdif_in_unmute(atc, state);
+ else if (MIXER_WAVEF_P_S == type) {
+ if (cap.dedicated_rca) {
+ atc->rca_unmute(atc, atc->rca_state ? 0 : state);
+ atc->line_front_unmute(atc, atc->rca_state ? state : 0);
+ } else {
+ atc->line_front_unmute(atc, state);
+ }
+ }
else if (MIXER_WAVES_P_S == type)
atc->line_surround_unmute(atc, state);
else if (MIXER_WAVEC_P_S == type)
atc->line_clfe_unmute(atc, state);
else if (MIXER_WAVER_P_S == type)
atc->line_rear_unmute(atc, state);
- else if (MIXER_LINEIN_P_S == type)
- atc->line_in_unmute(atc, state);
else if (MIXER_SPDIFO_P_S == type)
atc->spdif_out_unmute(atc, state);
- else if (MIXER_SPDIFI_P_S == type)
- atc->spdif_in_unmute(atc, state);
else if (MIXER_DIGITAL_IO_S == type)
do_digit_io_switch(atc, state);
@@ -548,6 +618,57 @@ static struct snd_kcontrol_new swh_ctl = {
.put = ct_alsa_mix_switch_put
};
+static int dedicated_rca_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *info)
+{
+ static const char *const names[2] = {
+ "RCA", "Front"
+ };
+
+ return snd_ctl_enum_info(info, 1, 2, names);
+}
+
+static int dedicated_rca_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = atc->rca_state;
+ return 0;
+}
+
+static int dedicated_rca_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+ unsigned int rca_state = ucontrol->value.enumerated.item[0];
+ unsigned char state;
+
+ if (rca_state > 1)
+ return -EINVAL;
+
+ if (rca_state == atc->rca_state)
+ return 0;
+
+ state = get_switch_state(atc->mixer, MIXER_WAVEF_P_S);
+ do_switch(atc, MIXER_WAVEF_P_S, 0);
+
+ atc->rca_state = rca_state;
+ atc->dedicated_rca_select(atc);
+
+ do_switch(atc, MIXER_WAVEF_P_S, state);
+
+ return 1;
+}
+
+static struct snd_kcontrol_new rca_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog Playback Route",
+ .info = dedicated_rca_info,
+ .get = dedicated_rca_get,
+ .put = dedicated_rca_put,
+};
+
static int ct_spdif_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -566,19 +687,6 @@ static int ct_spdif_get_mask(struct snd_kcontrol *kcontrol,
return 0;
}
-static int ct_spdif_default_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- unsigned int status = SNDRV_PCM_DEFAULT_CON_SPDIF;
-
- ucontrol->value.iec958.status[0] = (status >> 0) & 0xff;
- ucontrol->value.iec958.status[1] = (status >> 8) & 0xff;
- ucontrol->value.iec958.status[2] = (status >> 16) & 0xff;
- ucontrol->value.iec958.status[3] = (status >> 24) & 0xff;
-
- return 0;
-}
-
static int ct_spdif_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -586,6 +694,10 @@ static int ct_spdif_get(struct snd_kcontrol *kcontrol,
unsigned int status;
atc->spdif_out_get_status(atc, &status);
+
+ if (status == 0)
+ status = SNDRV_PCM_DEFAULT_CON_SPDIF;
+
ucontrol->value.iec958.status[0] = (status >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (status >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (status >> 16) & 0xff;
@@ -629,7 +741,7 @@ static struct snd_kcontrol_new iec958_default_ctl = {
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
.count = 1,
.info = ct_spdif_info,
- .get = ct_spdif_default_get,
+ .get = ct_spdif_get,
.put = ct_spdif_put,
.private_value = MIXER_IEC958_DEFAULT
};
@@ -680,6 +792,7 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
{
enum CTALSA_MIXER_CTL type;
struct ct_atc *atc = mixer->atc;
+ struct capabilities cap = atc->capabilities(atc);
int err;
/* Create snd kcontrol instances on demand */
@@ -693,8 +806,8 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
}
}
- ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl =
- atc->have_digit_io_switch(atc);
+ ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl = cap.digit_io_switch;
+
for (type = SWH_MIXER_START; type <= SWH_MIXER_END; type++) {
if (ct_kcontrol_init_table[type].ctl) {
swh_ctl.name = ct_kcontrol_init_table[type].name;
@@ -717,7 +830,28 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
if (err)
return err;
- atc->line_front_unmute(atc, 1);
+ if (cap.output_switch) {
+ err = ct_mixer_kcontrol_new(mixer, &output_ctl);
+ if (err)
+ return err;
+ }
+
+ if (cap.mic_source_switch) {
+ err = ct_mixer_kcontrol_new(mixer, &mic_source_ctl);
+ if (err)
+ return err;
+ }
+
+ if (cap.dedicated_rca) {
+ err = ct_mixer_kcontrol_new(mixer, &rca_ctl);
+ if (err)
+ return err;
+
+ atc->line_front_unmute(atc, 0);
+ atc->rca_unmute(atc, 1);
+ } else {
+ atc->line_front_unmute(atc, 1);
+ }
set_switch_state(mixer, MIXER_WAVEF_P_S, 1);
atc->line_surround_unmute(atc, 0);
set_switch_state(mixer, MIXER_WAVES_P_S, 0);
@@ -728,13 +862,12 @@ static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
atc->spdif_out_unmute(atc, 0);
set_switch_state(mixer, MIXER_SPDIFO_P_S, 0);
atc->line_in_unmute(atc, 0);
- set_switch_state(mixer, MIXER_LINEIN_P_S, 0);
+ if (cap.dedicated_mic)
+ atc->mic_unmute(atc, 0);
atc->spdif_in_unmute(atc, 0);
- set_switch_state(mixer, MIXER_SPDIFI_P_S, 0);
-
- set_switch_state(mixer, MIXER_PCM_C_S, 1);
- set_switch_state(mixer, MIXER_LINEIN_C_S, 1);
- set_switch_state(mixer, MIXER_SPDIFI_C_S, 1);
+ set_switch_state(mixer, MIXER_PCM_C_S, 0);
+ set_switch_state(mixer, MIXER_LINEIN_C_S, 0);
+ set_switch_state(mixer, MIXER_SPDIFI_C_S, 0);
return 0;
}
@@ -784,8 +917,8 @@ static int ct_mixer_get_resources(struct ct_mixer *mixer)
for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) {
err = sum_mgr->get_sum(sum_mgr, &sum_desc, &sum);
if (err) {
- printk(KERN_ERR "ctxfi:Failed to get sum resources for "
- "front output!\n");
+ dev_err(mixer->atc->card->dev,
+ "Failed to get sum resources for front output!\n");
break;
}
mixer->sums[i] = sum;
@@ -799,8 +932,8 @@ static int ct_mixer_get_resources(struct ct_mixer *mixer)
for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) {
err = amixer_mgr->get_amixer(amixer_mgr, &am_desc, &amixer);
if (err) {
- printk(KERN_ERR "ctxfi:Failed to get amixer resources "
- "for mixer obj!\n");
+ dev_err(mixer->atc->card->dev,
+ "Failed to get amixer resources for mixer obj!\n");
break;
}
mixer->amixers[i] = amixer;
@@ -840,13 +973,14 @@ static int ct_mixer_get_mem(struct ct_mixer **rmixer)
if (!mixer)
return -ENOMEM;
- mixer->amixers = kzalloc(sizeof(void *)*(NUM_CT_AMIXERS*CHN_NUM),
+ mixer->amixers = kcalloc(NUM_CT_AMIXERS * CHN_NUM, sizeof(void *),
GFP_KERNEL);
if (!mixer->amixers) {
err = -ENOMEM;
goto error1;
}
- mixer->sums = kzalloc(sizeof(void *)*(NUM_CT_SUMS*CHN_NUM), GFP_KERNEL);
+ mixer->sums = kcalloc(NUM_CT_SUMS * CHN_NUM, sizeof(void *),
+ GFP_KERNEL);
if (!mixer->sums) {
err = -ENOMEM;
goto error2;
@@ -867,17 +1001,18 @@ static int ct_mixer_topology_build(struct ct_mixer *mixer)
struct sum *sum;
struct amixer *amix_d, *amix_s;
enum CT_AMIXER_CTL i, j;
+ enum CT_SUM_CTL k;
/* Build topology from destination to source */
/* Set up Master mixer */
- for (i = AMIXER_MASTER_F, j = SUM_IN_F;
- i <= AMIXER_MASTER_S; i++, j++) {
+ for (i = AMIXER_MASTER_F, k = SUM_IN_F;
+ i <= AMIXER_MASTER_S; i++, k++) {
amix_d = mixer->amixers[i*CHN_NUM];
- sum = mixer->sums[j*CHN_NUM];
+ sum = mixer->sums[k*CHN_NUM];
amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
amix_d = mixer->amixers[i*CHN_NUM+1];
- sum = mixer->sums[j*CHN_NUM+1];
+ sum = mixer->sums[k*CHN_NUM+1];
amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
}
@@ -901,12 +1036,12 @@ static int ct_mixer_topology_build(struct ct_mixer *mixer)
amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL);
/* Set up PCM-in mixer */
- for (i = AMIXER_PCM_F, j = SUM_IN_F; i <= AMIXER_PCM_S; i++, j++) {
+ for (i = AMIXER_PCM_F, k = SUM_IN_F; i <= AMIXER_PCM_S; i++, k++) {
amix_d = mixer->amixers[i*CHN_NUM];
- sum = mixer->sums[j*CHN_NUM];
+ sum = mixer->sums[k*CHN_NUM];
amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
amix_d = mixer->amixers[i*CHN_NUM+1];
- sum = mixer->sums[j*CHN_NUM+1];
+ sum = mixer->sums[k*CHN_NUM+1];
amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
}
@@ -1048,7 +1183,7 @@ mixer_set_input_right(struct ct_mixer *mixer,
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int mixer_resume(struct ct_mixer *mixer)
{
int i, state;
@@ -1118,7 +1253,7 @@ int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer)
mixer->get_output_ports = mixer_get_output_ports;
mixer->set_input_left = mixer_set_input_left;
mixer->set_input_right = mixer_set_input_right;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
mixer->resume = mixer_resume;
#endif
@@ -1151,7 +1286,7 @@ int ct_alsa_mix_create(struct ct_atc *atc,
if (err)
return err;
- strcpy(atc->card->mixername, device_name);
+ strscpy(atc->card->mixername, device_name);
return 0;
}
diff --git a/sound/pci/ctxfi/ctmixer.h b/sound/pci/ctxfi/ctmixer.h
index b009e989e77d..e812f6c93b41 100644
--- a/sound/pci/ctxfi/ctmixer.h
+++ b/sound/pci/ctxfi/ctmixer.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctmixer.h
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date Mar 28 2008
- *
*/
#ifndef CTMIXER_H
@@ -56,7 +52,7 @@ struct ct_mixer {
enum MIXER_PORT_T type, struct rsc *rsc);
int (*set_input_right)(struct ct_mixer *mixer,
enum MIXER_PORT_T type, struct rsc *rsc);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int (*resume)(struct ct_mixer *mixer);
#endif
};
diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c
index 85ab43e89212..81dfc6a76b18 100644
--- a/sound/pci/ctxfi/ctpcm.c
+++ b/sound/pci/ctxfi/ctpcm.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctpcm.c
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date Apr 2 2008
- *
*/
#include "ctpcm.h"
@@ -21,7 +17,7 @@
#include <sound/pcm.h>
/* Hardware descriptions for playback */
-static struct snd_pcm_hardware ct_pcm_playback_hw = {
+static const struct snd_pcm_hardware ct_pcm_playback_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -46,7 +42,7 @@ static struct snd_pcm_hardware ct_pcm_playback_hw = {
.fifo_size = 0,
};
-static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = {
+static const struct snd_pcm_hardware ct_spdif_passthru_playback_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -69,7 +65,7 @@ static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = {
};
/* Hardware descriptions for capture */
-static struct snd_pcm_hardware ct_pcm_capture_hw = {
+static const struct snd_pcm_hardware ct_pcm_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -129,8 +125,6 @@ static int ct_pcm_playback_open(struct snd_pcm_substream *substream)
apcm->substream = substream;
apcm->interrupt = ct_atc_pcm_interrupt;
- runtime->private_data = apcm;
- runtime->private_free = ct_atc_pcm_free_substream;
if (IEC958 == substream->pcm->device) {
runtime->hw = ct_spdif_passthru_playback_hw;
atc->spdif_out_passthru(atc, 1);
@@ -142,23 +136,28 @@ static int ct_pcm_playback_open(struct snd_pcm_substream *substream)
err = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
- if (err < 0) {
- kfree(apcm);
- return err;
- }
+ if (err < 0)
+ goto free_pcm;
+
err = snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
1024, UINT_MAX);
- if (err < 0) {
- kfree(apcm);
- return err;
- }
+ if (err < 0)
+ goto free_pcm;
apcm->timer = ct_timer_instance_new(atc->timer, apcm);
- if (!apcm->timer)
- return -ENOMEM;
+ if (!apcm->timer) {
+ err = -ENOMEM;
+ goto free_pcm;
+ }
+ runtime->private_data = apcm;
+ runtime->private_free = ct_atc_pcm_free_substream;
return 0;
+
+free_pcm:
+ kfree(apcm);
+ return err;
}
static int ct_pcm_playback_close(struct snd_pcm_substream *substream)
@@ -179,15 +178,10 @@ static int ct_pcm_hw_params(struct snd_pcm_substream *substream,
{
struct ct_atc *atc = snd_pcm_substream_chip(substream);
struct ct_atc_pcm *apcm = substream->runtime->private_data;
- int err;
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
/* clear previous resources */
atc->pcm_release_resources(atc, apcm);
- return err;
+ return 0;
}
static int ct_pcm_hw_free(struct snd_pcm_substream *substream)
@@ -197,8 +191,7 @@ static int ct_pcm_hw_free(struct snd_pcm_substream *substream)
/* clear previous resources */
atc->pcm_release_resources(atc, apcm);
- /* Free snd-allocated pages */
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
@@ -215,7 +208,8 @@ static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream)
err = atc->pcm_playback_prepare(atc, apcm);
if (err < 0) {
- printk(KERN_ERR "ctxfi: Preparing pcm playback failed!!!\n");
+ dev_err(atc->card->dev,
+ "Preparing pcm playback failed!!!\n");
return err;
}
@@ -278,30 +272,33 @@ static int ct_pcm_capture_open(struct snd_pcm_substream *substream)
apcm->started = 0;
apcm->substream = substream;
apcm->interrupt = ct_atc_pcm_interrupt;
- runtime->private_data = apcm;
- runtime->private_free = ct_atc_pcm_free_substream;
runtime->hw = ct_pcm_capture_hw;
runtime->hw.rate_max = atc->rsr * atc->msr;
err = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
- if (err < 0) {
- kfree(apcm);
- return err;
- }
+ if (err < 0)
+ goto free_pcm;
+
err = snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
1024, UINT_MAX);
- if (err < 0) {
- kfree(apcm);
- return err;
- }
+ if (err < 0)
+ goto free_pcm;
apcm->timer = ct_timer_instance_new(atc->timer, apcm);
- if (!apcm->timer)
- return -ENOMEM;
+ if (!apcm->timer) {
+ err = -ENOMEM;
+ goto free_pcm;
+ }
+ runtime->private_data = apcm;
+ runtime->private_free = ct_atc_pcm_free_substream;
return 0;
+
+free_pcm:
+ kfree(apcm);
+ return err;
}
static int ct_pcm_capture_close(struct snd_pcm_substream *substream)
@@ -320,7 +317,8 @@ static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream)
err = atc->pcm_capture_prepare(atc, apcm);
if (err < 0) {
- printk(KERN_ERR "ctxfi: Preparing pcm capture failed!!!\n");
+ dev_err(atc->card->dev,
+ "Preparing pcm capture failed!!!\n");
return err;
}
@@ -366,29 +364,49 @@ ct_pcm_capture_pointer(struct snd_pcm_substream *substream)
}
/* PCM operators for playback */
-static struct snd_pcm_ops ct_pcm_playback_ops = {
+static const struct snd_pcm_ops ct_pcm_playback_ops = {
.open = ct_pcm_playback_open,
.close = ct_pcm_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = ct_pcm_hw_params,
.hw_free = ct_pcm_hw_free,
.prepare = ct_pcm_playback_prepare,
.trigger = ct_pcm_playback_trigger,
.pointer = ct_pcm_playback_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
/* PCM operators for capture */
-static struct snd_pcm_ops ct_pcm_capture_ops = {
+static const struct snd_pcm_ops ct_pcm_capture_ops = {
.open = ct_pcm_capture_open,
.close = ct_pcm_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = ct_pcm_hw_params,
.hw_free = ct_pcm_hw_free,
.prepare = ct_pcm_capture_prepare,
.trigger = ct_pcm_capture_trigger,
.pointer = ct_pcm_capture_pointer,
- .page = snd_pcm_sgbuf_ops_page,
+};
+
+static const struct snd_pcm_chmap_elem surround_map[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
+static const struct snd_pcm_chmap_elem clfe_map[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
+ { }
+};
+
+static const struct snd_pcm_chmap_elem side_map[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_SL, SNDRV_CHMAP_SR } },
+ { }
};
/* Create ALSA pcm device */
@@ -397,22 +415,25 @@ int ct_alsa_pcm_create(struct ct_atc *atc,
const char *device_name)
{
struct snd_pcm *pcm;
+ const struct snd_pcm_chmap_elem *map;
+ int chs;
int err;
int playback_count, capture_count;
- playback_count = (IEC958 == device) ? 1 : 8;
+ playback_count = (IEC958 == device) ? 1 : 256;
capture_count = (FRONT == device) ? 1 : 0;
err = snd_pcm_new(atc->card, "ctxfi", device,
playback_count, capture_count, &pcm);
if (err < 0) {
- printk(KERN_ERR "ctxfi: snd_pcm_new failed!! Err=%d\n", err);
+ dev_err(atc->card->dev, "snd_pcm_new failed!! Err=%d\n",
+ err);
return err;
}
pcm->private_data = atc;
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
- strlcpy(pcm->name, device_name, sizeof(pcm->name));
+ strscpy(pcm->name, device_name, sizeof(pcm->name));
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops);
@@ -420,10 +441,34 @@ int ct_alsa_pcm_create(struct ct_atc *atc,
snd_pcm_set_ops(pcm,
SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(atc->pci), 128*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &atc->pci->dev, 128*1024, 128*1024);
+
+ chs = 2;
+ switch (device) {
+ case FRONT:
+ chs = 8;
+ map = snd_pcm_std_chmaps;
+ break;
+ case SURROUND:
+ map = surround_map;
+ break;
+ case CLFE:
+ map = clfe_map;
+ break;
+ case SIDE:
+ map = side_map;
+ break;
+ default:
+ map = snd_pcm_std_chmaps;
+ break;
+ }
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, chs,
+ 0, NULL);
+ if (err < 0)
+ return err;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
atc->pcms[device] = pcm;
#endif
diff --git a/sound/pci/ctxfi/ctpcm.h b/sound/pci/ctxfi/ctpcm.h
index 178da0dca647..8b39bdd262b4 100644
--- a/sound/pci/ctxfi/ctpcm.h
+++ b/sound/pci/ctxfi/ctpcm.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctpcm.h
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date Mar 28 2008
- *
*/
#ifndef CTPCM_H
diff --git a/sound/pci/ctxfi/ctresource.c b/sound/pci/ctxfi/ctresource.c
index 7dfaf67344d4..be1d3e61309c 100644
--- a/sound/pci/ctxfi/ctresource.c
+++ b/sound/pci/ctxfi/ctresource.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctresource.c
*
* @Brief
@@ -12,7 +9,6 @@
*
* @Author Liu Chun
* @Date May 15 2008
- *
*/
#include "ctresource.h"
@@ -96,7 +92,7 @@ int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx)
return 0;
}
-static unsigned char offset_in_audio_slot_block[NUM_RSCTYP] = {
+static const unsigned char offset_in_audio_slot_block[NUM_RSCTYP] = {
/* SRC channel is at Audio Ring slot 1 every 16 slots. */
[SRC] = 0x1,
[AMIXER] = 0x4,
@@ -113,28 +109,28 @@ static int audio_ring_slot(const struct rsc *rsc)
return (rsc->conj << 4) + offset_in_audio_slot_block[rsc->type];
}
-static int rsc_next_conj(struct rsc *rsc)
+static void rsc_next_conj(struct rsc *rsc)
{
unsigned int i;
for (i = 0; (i < 8) && (!(rsc->msr & (0x1 << i))); )
i++;
rsc->conj += (AUDIO_SLOT_BLOCK_NUM >> i);
- return rsc->conj;
}
-static int rsc_master(struct rsc *rsc)
+static void rsc_master(struct rsc *rsc)
{
- return rsc->conj = rsc->idx;
+ rsc->conj = rsc->idx;
}
-static struct rsc_ops rsc_generic_ops = {
+static const struct rsc_ops rsc_generic_ops = {
.index = rsc_index,
.output_slot = audio_ring_slot,
.master = rsc_master,
.next_conj = rsc_next_conj,
};
-int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw)
+int
+rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, struct hw *hw)
{
int err = 0;
@@ -151,25 +147,24 @@ int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw)
switch (type) {
case SRC:
- err = ((struct hw *)hw)->src_rsc_get_ctrl_blk(&rsc->ctrl_blk);
+ err = hw->src_rsc_get_ctrl_blk(&rsc->ctrl_blk);
break;
case AMIXER:
- err = ((struct hw *)hw)->
- amixer_rsc_get_ctrl_blk(&rsc->ctrl_blk);
+ err = hw->amixer_rsc_get_ctrl_blk(&rsc->ctrl_blk);
break;
case SRCIMP:
case SUM:
case DAIO:
break;
default:
- printk(KERN_ERR
- "ctxfi: Invalid resource type value %d!\n", type);
+ dev_err(((struct hw *)hw)->card->dev,
+ "Invalid resource type value %d!\n", type);
return -EINVAL;
}
if (err) {
- printk(KERN_ERR
- "ctxfi: Failed to get resource control block!\n");
+ dev_err(((struct hw *)hw)->card->dev,
+ "Failed to get resource control block!\n");
return err;
}
@@ -181,19 +176,18 @@ int rsc_uninit(struct rsc *rsc)
if ((NULL != rsc->hw) && (NULL != rsc->ctrl_blk)) {
switch (rsc->type) {
case SRC:
- ((struct hw *)rsc->hw)->
- src_rsc_put_ctrl_blk(rsc->ctrl_blk);
+ rsc->hw->src_rsc_put_ctrl_blk(rsc->ctrl_blk);
break;
case AMIXER:
- ((struct hw *)rsc->hw)->
- amixer_rsc_put_ctrl_blk(rsc->ctrl_blk);
+ rsc->hw->amixer_rsc_put_ctrl_blk(rsc->ctrl_blk);
break;
case SUM:
case DAIO:
break;
default:
- printk(KERN_ERR "ctxfi: "
- "Invalid resource type value %d!\n", rsc->type);
+ dev_err(((struct hw *)rsc->hw)->card->dev,
+ "Invalid resource type value %d!\n",
+ rsc->type);
break;
}
@@ -208,14 +202,13 @@ int rsc_uninit(struct rsc *rsc)
}
int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
- unsigned int amount, void *hw_obj)
+ unsigned int amount, struct hw *hw)
{
int err = 0;
- struct hw *hw = hw_obj;
mgr->type = NUM_RSCTYP;
- mgr->rscs = kzalloc(((amount + 8 - 1) / 8), GFP_KERNEL);
+ mgr->rscs = kzalloc(DIV_ROUND_UP(amount, 8), GFP_KERNEL);
if (!mgr->rscs)
return -ENOMEM;
@@ -235,15 +228,15 @@ int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
case SUM:
break;
default:
- printk(KERN_ERR
- "ctxfi: Invalid resource type value %d!\n", type);
+ dev_err(hw->card->dev,
+ "Invalid resource type value %d!\n", type);
err = -EINVAL;
goto error;
}
if (err) {
- printk(KERN_ERR
- "ctxfi: Failed to get manager control block!\n");
+ dev_err(hw->card->dev,
+ "Failed to get manager control block!\n");
goto error;
}
@@ -260,34 +253,29 @@ error:
int rsc_mgr_uninit(struct rsc_mgr *mgr)
{
- if (NULL != mgr->rscs) {
- kfree(mgr->rscs);
- mgr->rscs = NULL;
- }
+ kfree(mgr->rscs);
+ mgr->rscs = NULL;
if ((NULL != mgr->hw) && (NULL != mgr->ctrl_blk)) {
switch (mgr->type) {
case SRC:
- ((struct hw *)mgr->hw)->
- src_mgr_put_ctrl_blk(mgr->ctrl_blk);
+ mgr->hw->src_mgr_put_ctrl_blk(mgr->ctrl_blk);
break;
case SRCIMP:
- ((struct hw *)mgr->hw)->
- srcimp_mgr_put_ctrl_blk(mgr->ctrl_blk);
+ mgr->hw->srcimp_mgr_put_ctrl_blk(mgr->ctrl_blk);
break;
case AMIXER:
- ((struct hw *)mgr->hw)->
- amixer_mgr_put_ctrl_blk(mgr->ctrl_blk);
+ mgr->hw->amixer_mgr_put_ctrl_blk(mgr->ctrl_blk);
break;
case DAIO:
- ((struct hw *)mgr->hw)->
- daio_mgr_put_ctrl_blk(mgr->ctrl_blk);
+ mgr->hw->daio_mgr_put_ctrl_blk(mgr->ctrl_blk);
break;
case SUM:
break;
default:
- printk(KERN_ERR "ctxfi: "
- "Invalid resource type value %d!\n", mgr->type);
+ dev_err(((struct hw *)mgr->hw)->card->dev,
+ "Invalid resource type value %d!\n",
+ mgr->type);
break;
}
diff --git a/sound/pci/ctxfi/ctresource.h b/sound/pci/ctxfi/ctresource.h
index 0838c2e84f8b..58553bda44f4 100644
--- a/sound/pci/ctxfi/ctresource.h
+++ b/sound/pci/ctxfi/ctresource.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctresource.h
*
* @Brief
@@ -13,7 +10,6 @@
*
* @Author Liu Chun
* @Date May 13 2008
- *
*/
#ifndef CTRESOURCE_H
@@ -38,19 +34,20 @@ struct rsc {
u32 conj:12; /* Current conjugate index */
u32 msr:4; /* The Master Sample Rate a resource working on */
void *ctrl_blk; /* Chip specific control info block for a resource */
- void *hw; /* Chip specific object for hardware access means */
- struct rsc_ops *ops; /* Generic resource operations */
+ struct hw *hw; /* Chip specific object for hardware access means */
+ const struct rsc_ops *ops; /* Generic resource operations */
};
struct rsc_ops {
- int (*master)(struct rsc *rsc); /* Move to master resource */
- int (*next_conj)(struct rsc *rsc); /* Move to next conjugate resource */
+ void (*master)(struct rsc *rsc); /* Move to master resource */
+ void (*next_conj)(struct rsc *rsc); /* Move to next conjugate resource */
int (*index)(const struct rsc *rsc); /* Return the index of resource */
/* Return the output slot number */
int (*output_slot)(const struct rsc *rsc);
};
-int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw);
+int
+rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, struct hw *hw);
int rsc_uninit(struct rsc *rsc);
struct rsc_mgr {
@@ -59,12 +56,12 @@ struct rsc_mgr {
unsigned int avail; /* The amount of currently available resources */
unsigned char *rscs; /* The bit-map for resource allocation */
void *ctrl_blk; /* Chip specific control info block */
- void *hw; /* Chip specific object for hardware access */
+ struct hw *hw; /* Chip specific object for hardware access */
};
/* Resource management is based on bit-map mechanism */
int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
- unsigned int amount, void *hw);
+ unsigned int amount, struct hw *hw);
int rsc_mgr_uninit(struct rsc_mgr *mgr);
int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx);
int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx);
diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c
index c749fa720889..46afc9604c08 100644
--- a/sound/pci/ctxfi/ctsrc.c
+++ b/sound/pci/ctxfi/ctsrc.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctsrc.c
*
* @Brief
@@ -13,14 +10,13 @@
*
* @Author Liu Chun
* @Date May 13 2008
- *
*/
#include "ctsrc.h"
#include "cthardware.h"
#include <linux/slab.h>
-#define SRC_RESOURCE_NUM 64
+#define SRC_RESOURCE_NUM 256
#define SRCIMP_RESOURCE_NUM 256
static unsigned int conj_mask;
@@ -335,7 +331,7 @@ static int src_default_config_arcrw(struct src *src)
return 0;
}
-static struct src_rsc_ops src_rsc_ops = {
+static const struct src_rsc_ops src_rsc_ops = {
.set_state = src_set_state,
.set_bm = src_set_bm,
.set_sf = src_set_sf,
@@ -418,26 +414,25 @@ get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc)
unsigned int idx = SRC_RESOURCE_NUM;
int err;
struct src *src;
- unsigned long flags;
*rsrc = NULL;
/* Check whether there are sufficient src resources to meet request. */
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- if (MEMRD == desc->mode)
- err = mgr_get_resource(&mgr->mgr, desc->multi, &idx);
- else
- err = mgr_get_resource(&mgr->mgr, 1, &idx);
-
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ if (MEMRD == desc->mode)
+ err = mgr_get_resource(&mgr->mgr, desc->multi, &idx);
+ else
+ err = mgr_get_resource(&mgr->mgr, 1, &idx);
+ }
if (err) {
- printk(KERN_ERR "ctxfi: Can't meet SRC resource request!\n");
+ dev_err(mgr->card->dev,
+ "Can't meet SRC resource request!\n");
return err;
}
/* Allocate mem for master src resource */
if (MEMRD == desc->mode)
- src = kzalloc(sizeof(*src)*desc->multi, GFP_KERNEL);
+ src = kcalloc(desc->multi, sizeof(*src), GFP_KERNEL);
else
src = kzalloc(sizeof(*src), GFP_KERNEL);
@@ -457,29 +452,25 @@ get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc)
error2:
kfree(src);
error1:
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- if (MEMRD == desc->mode)
- mgr_put_resource(&mgr->mgr, desc->multi, idx);
- else
- mgr_put_resource(&mgr->mgr, 1, idx);
-
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ if (MEMRD == desc->mode)
+ mgr_put_resource(&mgr->mgr, desc->multi, idx);
+ else
+ mgr_put_resource(&mgr->mgr, 1, idx);
+ }
return err;
}
static int put_src_rsc(struct src_mgr *mgr, struct src *src)
{
- unsigned long flags;
-
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- src->rsc.ops->master(&src->rsc);
- if (MEMRD == src->mode)
- mgr_put_resource(&mgr->mgr, src->multi,
- src->rsc.ops->index(&src->rsc));
- else
- mgr_put_resource(&mgr->mgr, 1, src->rsc.ops->index(&src->rsc));
-
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ src->rsc.ops->master(&src->rsc);
+ if (MEMRD == src->mode)
+ mgr_put_resource(&mgr->mgr, src->multi,
+ src->rsc.ops->index(&src->rsc));
+ else
+ mgr_put_resource(&mgr->mgr, 1, src->rsc.ops->index(&src->rsc));
+ }
src_rsc_uninit(src, mgr);
kfree(src);
@@ -543,7 +534,7 @@ static int src_mgr_commit_write(struct src_mgr *mgr)
return 0;
}
-int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr)
+int src_mgr_create(struct hw *hw, void **rsrc_mgr)
{
int err, i;
struct src_mgr *src_mgr;
@@ -558,7 +549,7 @@ int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr)
goto error1;
spin_lock_init(&src_mgr->mgr_lock);
- conj_mask = ((struct hw *)hw)->src_dirty_conj_mask();
+ conj_mask = hw->src_dirty_conj_mask();
src_mgr->get_src = get_src_rsc;
src_mgr->put_src = put_src_rsc;
@@ -566,12 +557,13 @@ int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr)
src_mgr->src_enable = src_enable;
src_mgr->src_disable = src_disable;
src_mgr->commit_write = src_mgr_commit_write;
+ src_mgr->card = hw->card;
/* Disable all SRC resources. */
for (i = 0; i < 256; i++)
- ((struct hw *)hw)->src_mgr_dsb_src(src_mgr->mgr.ctrl_blk, i);
+ hw->src_mgr_dsb_src(src_mgr->mgr.ctrl_blk, i);
- ((struct hw *)hw)->src_mgr_commit_write(hw, src_mgr->mgr.ctrl_blk);
+ hw->src_mgr_commit_write(hw, src_mgr->mgr.ctrl_blk);
*rsrc_mgr = src_mgr;
@@ -582,8 +574,9 @@ error1:
return err;
}
-int src_mgr_destroy(struct src_mgr *src_mgr)
+int src_mgr_destroy(void *ptr)
{
+ struct src_mgr *src_mgr = ptr;
rsc_mgr_uninit(&src_mgr->mgr);
kfree(src_mgr);
@@ -592,16 +585,15 @@ int src_mgr_destroy(struct src_mgr *src_mgr)
/* SRCIMP resource manager operations */
-static int srcimp_master(struct rsc *rsc)
+static void srcimp_master(struct rsc *rsc)
{
rsc->conj = 0;
- return rsc->idx = container_of(rsc, struct srcimp, rsc)->idx[0];
+ rsc->idx = container_of(rsc, struct srcimp, rsc)->idx[0];
}
-static int srcimp_next_conj(struct rsc *rsc)
+static void srcimp_next_conj(struct rsc *rsc)
{
rsc->conj++;
- return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj];
}
static int srcimp_index(const struct rsc *rsc)
@@ -609,7 +601,7 @@ static int srcimp_index(const struct rsc *rsc)
return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj];
}
-static struct rsc_ops srcimp_basic_rsc_ops = {
+static const struct rsc_ops srcimp_basic_rsc_ops = {
.master = srcimp_master,
.next_conj = srcimp_next_conj,
.index = srcimp_index,
@@ -660,7 +652,7 @@ static int srcimp_unmap(struct srcimp *srcimp)
return 0;
}
-static struct srcimp_rsc_ops srcimp_ops = {
+static const struct srcimp_rsc_ops srcimp_ops = {
.map = srcimp_map,
.unmap = srcimp_unmap
};
@@ -677,7 +669,7 @@ static int srcimp_rsc_init(struct srcimp *srcimp,
return err;
/* Reserve memory for imapper nodes */
- srcimp->imappers = kzalloc(sizeof(struct imapper)*desc->msr,
+ srcimp->imappers = kcalloc(desc->msr, sizeof(struct imapper),
GFP_KERNEL);
if (!srcimp->imappers) {
err = -ENOMEM;
@@ -700,10 +692,8 @@ error1:
static int srcimp_rsc_uninit(struct srcimp *srcimp)
{
- if (NULL != srcimp->imappers) {
- kfree(srcimp->imappers);
- srcimp->imappers = NULL;
- }
+ kfree(srcimp->imappers);
+ srcimp->imappers = NULL;
srcimp->ops = NULL;
srcimp->mgr = NULL;
rsc_uninit(&srcimp->rsc);
@@ -718,7 +708,6 @@ static int get_srcimp_rsc(struct srcimp_mgr *mgr,
int err, i;
unsigned int idx;
struct srcimp *srcimp;
- unsigned long flags;
*rsrcimp = NULL;
@@ -729,17 +718,18 @@ static int get_srcimp_rsc(struct srcimp_mgr *mgr,
/* Check whether there are sufficient SRCIMP resources. */
err = 0;
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- for (i = 0; i < desc->msr; i++) {
- err = mgr_get_resource(&mgr->mgr, 1, &idx);
- if (err)
- break;
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ for (i = 0; i < desc->msr; i++) {
+ err = mgr_get_resource(&mgr->mgr, 1, &idx);
+ if (err)
+ break;
- srcimp->idx[i] = idx;
+ srcimp->idx[i] = idx;
+ }
}
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
if (err) {
- printk(KERN_ERR "ctxfi: Can't meet SRCIMP resource request!\n");
+ dev_err(mgr->card->dev,
+ "Can't meet SRCIMP resource request!\n");
goto error1;
}
@@ -752,25 +742,22 @@ static int get_srcimp_rsc(struct srcimp_mgr *mgr,
return 0;
error1:
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- for (i--; i >= 0; i--)
- mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]);
-
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ for (i--; i >= 0; i--)
+ mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]);
+ }
kfree(srcimp);
return err;
}
static int put_srcimp_rsc(struct srcimp_mgr *mgr, struct srcimp *srcimp)
{
- unsigned long flags;
int i;
- spin_lock_irqsave(&mgr->mgr_lock, flags);
- for (i = 0; i < srcimp->rsc.msr; i++)
- mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]);
-
- spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+ scoped_guard(spinlock_irqsave, &mgr->mgr_lock) {
+ for (i = 0; i < srcimp->rsc.msr; i++)
+ mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]);
+ }
srcimp_rsc_uninit(srcimp);
kfree(srcimp);
@@ -793,39 +780,31 @@ static int srcimp_map_op(void *data, struct imapper *entry)
static int srcimp_imap_add(struct srcimp_mgr *mgr, struct imapper *entry)
{
- unsigned long flags;
- int err;
-
- spin_lock_irqsave(&mgr->imap_lock, flags);
+ guard(spinlock_irqsave)(&mgr->imap_lock);
if ((0 == entry->addr) && (mgr->init_imap_added)) {
input_mapper_delete(&mgr->imappers,
mgr->init_imap, srcimp_map_op, mgr);
mgr->init_imap_added = 0;
}
- err = input_mapper_add(&mgr->imappers, entry, srcimp_map_op, mgr);
- spin_unlock_irqrestore(&mgr->imap_lock, flags);
-
- return err;
+ return input_mapper_add(&mgr->imappers, entry, srcimp_map_op, mgr);
}
static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry)
{
- unsigned long flags;
int err;
- spin_lock_irqsave(&mgr->imap_lock, flags);
+ guard(spinlock_irqsave)(&mgr->imap_lock);
err = input_mapper_delete(&mgr->imappers, entry, srcimp_map_op, mgr);
if (list_empty(&mgr->imappers)) {
input_mapper_add(&mgr->imappers, mgr->init_imap,
srcimp_map_op, mgr);
mgr->init_imap_added = 1;
}
- spin_unlock_irqrestore(&mgr->imap_lock, flags);
return err;
}
-int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr)
+int srcimp_mgr_create(struct hw *hw, void **rsrcimp_mgr)
{
int err;
struct srcimp_mgr *srcimp_mgr;
@@ -857,6 +836,7 @@ int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr)
srcimp_mgr->put_srcimp = put_srcimp_rsc;
srcimp_mgr->imap_add = srcimp_imap_add;
srcimp_mgr->imap_delete = srcimp_imap_delete;
+ srcimp_mgr->card = hw->card;
*rsrcimp_mgr = srcimp_mgr;
@@ -869,14 +849,14 @@ error1:
return err;
}
-int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr)
+int srcimp_mgr_destroy(void *ptr)
{
- unsigned long flags;
+ struct srcimp_mgr *srcimp_mgr = ptr;
/* free src input mapper list */
- spin_lock_irqsave(&srcimp_mgr->imap_lock, flags);
- free_input_mapper_list(&srcimp_mgr->imappers);
- spin_unlock_irqrestore(&srcimp_mgr->imap_lock, flags);
+ scoped_guard(spinlock_irqsave, &srcimp_mgr->imap_lock) {
+ free_input_mapper_list(&srcimp_mgr->imappers);
+ }
rsc_mgr_uninit(&srcimp_mgr->mgr);
kfree(srcimp_mgr);
diff --git a/sound/pci/ctxfi/ctsrc.h b/sound/pci/ctxfi/ctsrc.h
index 259366aabcac..e6366cc6a7ae 100644
--- a/sound/pci/ctxfi/ctsrc.h
+++ b/sound/pci/ctxfi/ctsrc.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctsrc.h
*
* @Brief
@@ -13,7 +10,6 @@
*
* @Author Liu Chun
* @Date May 13 2008
- *
*/
#ifndef CTSRC_H
@@ -23,6 +19,7 @@
#include "ctimap.h"
#include <linux/spinlock.h>
#include <linux/list.h>
+#include <sound/core.h>
#define SRC_STATE_OFF 0x0
#define SRC_STATE_INIT 0x4
@@ -47,7 +44,7 @@ struct src_rsc_ops;
struct src {
struct rsc rsc; /* Basic resource info */
struct src *intlv; /* Pointer to next interleaved SRC in a series */
- struct src_rsc_ops *ops; /* SRC specific operations */
+ const struct src_rsc_ops *ops; /* SRC specific operations */
/* Number of contiguous srcs for interleaved usage */
unsigned char multi;
unsigned char mode; /* Working mode of this SRC resource */
@@ -85,6 +82,7 @@ struct src_desc {
/* Define src manager object */
struct src_mgr {
struct rsc_mgr mgr; /* Basic resource manager info */
+ struct snd_card *card; /* pointer to this card */
spinlock_t mgr_lock;
/* request src resource */
@@ -108,7 +106,7 @@ struct srcimp {
struct imapper *imappers;
unsigned int mapped; /* A bit-map indicating which conj rsc is mapped */
struct srcimp_mgr *mgr;
- struct srcimp_rsc_ops *ops;
+ const struct srcimp_rsc_ops *ops;
};
struct srcimp_rsc_ops {
@@ -123,6 +121,7 @@ struct srcimp_desc {
struct srcimp_mgr {
struct rsc_mgr mgr; /* Basic resource manager info */
+ struct snd_card *card; /* pointer to this card */
spinlock_t mgr_lock;
spinlock_t imap_lock;
struct list_head imappers;
@@ -140,10 +139,10 @@ struct srcimp_mgr {
};
/* Constructor and destructor of SRC resource manager */
-int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr);
-int src_mgr_destroy(struct src_mgr *src_mgr);
+int src_mgr_create(struct hw *hw, void **ptr);
+int src_mgr_destroy(void *ptr);
/* Constructor and destructor of SRCIMP resource manager */
-int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrc_mgr);
-int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr);
+int srcimp_mgr_create(struct hw *hw, void **ptr);
+int srcimp_mgr_destroy(void *ptr);
#endif /* CTSRC_H */
diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c
index 93b0aedc36d4..609b10320ff7 100644
--- a/sound/pci/ctxfi/cttimer.c
+++ b/sound/pci/ctxfi/cttimer.c
@@ -1,9 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* PCM timer handling on ctxfi
- *
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
*/
#include <linux/slab.h>
@@ -15,9 +12,9 @@
#include "cthardware.h"
#include "cttimer.h"
-static int use_system_timer;
-MODULE_PARM_DESC(use_system_timer, "Foce to use system-timer");
-module_param(use_system_timer, bool, S_IRUGO);
+static bool use_system_timer;
+MODULE_PARM_DESC(use_system_timer, "Force to use system-timer");
+module_param(use_system_timer, bool, 0444);
struct ct_timer_ops {
void (*init)(struct ct_timer_instance *);
@@ -49,7 +46,7 @@ struct ct_timer {
spinlock_t lock; /* global timer lock (for xfitimer) */
spinlock_t list_lock; /* lock for instance list */
struct ct_atc *atc;
- struct ct_timer_ops *ops;
+ const struct ct_timer_ops *ops;
struct list_head instance_head;
struct list_head running_head;
unsigned int wc; /* current wallclock */
@@ -63,15 +60,14 @@ struct ct_timer {
* system-timer-based updates
*/
-static void ct_systimer_callback(unsigned long data)
+static void ct_systimer_callback(struct timer_list *t)
{
- struct ct_timer_instance *ti = (struct ct_timer_instance *)data;
+ struct ct_timer_instance *ti = timer_container_of(ti, t, timer);
struct snd_pcm_substream *substream = ti->substream;
struct snd_pcm_runtime *runtime = substream->runtime;
struct ct_atc_pcm *apcm = ti->apcm;
unsigned int period_size = runtime->period_size;
unsigned int buffer_size = runtime->buffer_size;
- unsigned long flags;
unsigned int position, dist, interval;
position = substream->ops->pointer(substream);
@@ -85,50 +81,43 @@ static void ct_systimer_callback(unsigned long data)
* at 8kHz in 8-bit format or at 88kHz in 24-bit format. */
interval = ((period_size - (position % period_size))
* HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000;
- spin_lock_irqsave(&ti->lock, flags);
+ guard(spinlock_irqsave)(&ti->lock);
if (ti->running)
mod_timer(&ti->timer, jiffies + interval);
- spin_unlock_irqrestore(&ti->lock, flags);
}
static void ct_systimer_init(struct ct_timer_instance *ti)
{
- setup_timer(&ti->timer, ct_systimer_callback,
- (unsigned long)ti);
+ timer_setup(&ti->timer, ct_systimer_callback, 0);
}
static void ct_systimer_start(struct ct_timer_instance *ti)
{
struct snd_pcm_runtime *runtime = ti->substream->runtime;
- unsigned long flags;
- spin_lock_irqsave(&ti->lock, flags);
+ guard(spinlock_irqsave)(&ti->lock);
ti->running = 1;
mod_timer(&ti->timer,
jiffies + (runtime->period_size * HZ +
(runtime->rate - 1)) / runtime->rate);
- spin_unlock_irqrestore(&ti->lock, flags);
}
static void ct_systimer_stop(struct ct_timer_instance *ti)
{
- unsigned long flags;
-
- spin_lock_irqsave(&ti->lock, flags);
+ guard(spinlock_irqsave)(&ti->lock);
ti->running = 0;
- del_timer(&ti->timer);
- spin_unlock_irqrestore(&ti->lock, flags);
+ timer_delete(&ti->timer);
}
static void ct_systimer_prepare(struct ct_timer_instance *ti)
{
ct_systimer_stop(ti);
- try_to_del_timer_sync(&ti->timer);
+ timer_delete_sync_try(&ti->timer);
}
#define ct_systimer_free ct_systimer_prepare
-static struct ct_timer_ops ct_systimer_ops = {
+static const struct ct_timer_ops ct_systimer_ops = {
.init = ct_systimer_init,
.free_instance = ct_systimer_free,
.prepare = ct_systimer_prepare,
@@ -233,25 +222,22 @@ static int ct_xfitimer_reprogram(struct ct_timer *atimer, int can_update)
static void ct_xfitimer_check_period(struct ct_timer *atimer)
{
struct ct_timer_instance *ti;
- unsigned long flags;
- spin_lock_irqsave(&atimer->list_lock, flags);
+ guard(spinlock_irqsave)(&atimer->list_lock);
list_for_each_entry(ti, &atimer->instance_head, instance_list) {
if (ti->running && ti->need_update) {
ti->need_update = 0;
ti->apcm->interrupt(ti->apcm);
}
}
- spin_unlock_irqrestore(&atimer->list_lock, flags);
}
/* Handle timer-interrupt */
static void ct_xfitimer_callback(struct ct_timer *atimer)
{
int update;
- unsigned long flags;
- spin_lock_irqsave(&atimer->lock, flags);
+ guard(spinlock_irqsave)(&atimer->lock);
atimer->irq_handling = 1;
do {
update = ct_xfitimer_reprogram(atimer, 1);
@@ -261,7 +247,6 @@ static void ct_xfitimer_callback(struct ct_timer *atimer)
spin_lock(&atimer->lock);
} while (atimer->reprogram);
atimer->irq_handling = 0;
- spin_unlock_irqrestore(&atimer->lock, flags);
}
static void ct_xfitimer_prepare(struct ct_timer_instance *ti)
@@ -275,45 +260,39 @@ static void ct_xfitimer_prepare(struct ct_timer_instance *ti)
/* start/stop the timer */
static void ct_xfitimer_update(struct ct_timer *atimer)
{
- unsigned long flags;
-
- spin_lock_irqsave(&atimer->lock, flags);
+ guard(spinlock_irqsave)(&atimer->lock);
if (atimer->irq_handling) {
/* reached from IRQ handler; let it handle later */
atimer->reprogram = 1;
- spin_unlock_irqrestore(&atimer->lock, flags);
return;
}
ct_xfitimer_irq_stop(atimer);
ct_xfitimer_reprogram(atimer, 0);
- spin_unlock_irqrestore(&atimer->lock, flags);
}
static void ct_xfitimer_start(struct ct_timer_instance *ti)
{
struct ct_timer *atimer = ti->timer_base;
- unsigned long flags;
- spin_lock_irqsave(&atimer->lock, flags);
- if (list_empty(&ti->running_list))
- atimer->wc = ct_xfitimer_get_wc(atimer);
- ti->running = 1;
- ti->need_update = 0;
- list_add(&ti->running_list, &atimer->running_head);
- spin_unlock_irqrestore(&atimer->lock, flags);
+ scoped_guard(spinlock_irqsave, &atimer->lock) {
+ if (list_empty(&ti->running_list))
+ atimer->wc = ct_xfitimer_get_wc(atimer);
+ ti->running = 1;
+ ti->need_update = 0;
+ list_add(&ti->running_list, &atimer->running_head);
+ }
ct_xfitimer_update(atimer);
}
static void ct_xfitimer_stop(struct ct_timer_instance *ti)
{
struct ct_timer *atimer = ti->timer_base;
- unsigned long flags;
- spin_lock_irqsave(&atimer->lock, flags);
- list_del_init(&ti->running_list);
- ti->running = 0;
- spin_unlock_irqrestore(&atimer->lock, flags);
+ scoped_guard(spinlock_irqsave, &atimer->lock) {
+ list_del_init(&ti->running_list);
+ ti->running = 0;
+ }
ct_xfitimer_update(atimer);
}
@@ -322,7 +301,7 @@ static void ct_xfitimer_free_global(struct ct_timer *atimer)
ct_xfitimer_irq_stop(atimer);
}
-static struct ct_timer_ops ct_xfitimer_ops = {
+static const struct ct_timer_ops ct_xfitimer_ops = {
.prepare = ct_xfitimer_prepare,
.start = ct_xfitimer_start,
.stop = ct_xfitimer_stop,
@@ -351,9 +330,9 @@ ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm)
if (atimer->ops->init)
atimer->ops->init(ti);
- spin_lock_irq(&atimer->list_lock);
- list_add(&ti->instance_list, &atimer->instance_head);
- spin_unlock_irq(&atimer->list_lock);
+ scoped_guard(spinlock_irq, &atimer->list_lock) {
+ list_add(&ti->instance_list, &atimer->instance_head);
+ }
return ti;
}
@@ -386,9 +365,9 @@ void ct_timer_instance_free(struct ct_timer_instance *ti)
if (atimer->ops->free_instance)
atimer->ops->free_instance(ti);
- spin_lock_irq(&atimer->list_lock);
- list_del(&ti->instance_list);
- spin_unlock_irq(&atimer->list_lock);
+ scoped_guard(spinlock_irq, &atimer->list_lock) {
+ list_del(&ti->instance_list);
+ }
kfree(ti);
}
@@ -421,12 +400,12 @@ struct ct_timer *ct_timer_new(struct ct_atc *atc)
atimer->atc = atc;
hw = atc->hw;
if (!use_system_timer && hw->set_timer_irq) {
- snd_printd(KERN_INFO "ctxfi: Use xfi-native timer\n");
+ dev_info(atc->card->dev, "Use xfi-native timer\n");
atimer->ops = &ct_xfitimer_ops;
hw->irq_callback_data = atimer;
hw->irq_callback = ct_timer_interrupt;
} else {
- snd_printd(KERN_INFO "ctxfi: Use system timer\n");
+ dev_info(atc->card->dev, "Use system timer\n");
atimer->ops = &ct_systimer_ops;
}
return atimer;
diff --git a/sound/pci/ctxfi/cttimer.h b/sound/pci/ctxfi/cttimer.h
index 979348229291..9c5cb403b646 100644
--- a/sound/pci/ctxfi/cttimer.h
+++ b/sound/pci/ctxfi/cttimer.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Timer handling
*/
diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c
index 65da6e466f80..823d6e240a07 100644
--- a/sound/pci/ctxfi/ctvmem.c
+++ b/sound/pci/ctxfi/ctvmem.c
@@ -1,10 +1,7 @@
-/**
+// SPDX-License-Identifier: GPL-2.0-only
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctvmem.c
*
* @Brief
@@ -16,6 +13,7 @@
*/
#include "ctvmem.h"
+#include "ctatc.h"
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/io.h>
@@ -29,39 +27,37 @@
* @size must be page aligned.
* */
static struct ct_vm_block *
-get_vm_block(struct ct_vm *vm, unsigned int size)
+get_vm_block(struct ct_vm *vm, unsigned int size, struct ct_atc *atc)
{
- struct ct_vm_block *block = NULL, *entry;
+ struct ct_vm_block *block, *entry;
struct list_head *pos;
size = CT_PAGE_ALIGN(size);
if (size > vm->size) {
- printk(KERN_ERR "ctxfi: Fail! No sufficient device virtural "
- "memory space available!\n");
+ dev_err(atc->card->dev,
+ "Fail! No sufficient device virtual memory space available!\n");
return NULL;
}
- mutex_lock(&vm->lock);
+ guard(mutex)(&vm->lock);
list_for_each(pos, &vm->unused) {
entry = list_entry(pos, struct ct_vm_block, list);
if (entry->size >= size)
break; /* found a block that is big enough */
}
if (pos == &vm->unused)
- goto out;
+ return NULL;
if (entry->size == size) {
/* Move the vm node from unused list to used list directly */
- list_del(&entry->list);
- list_add(&entry->list, &vm->used);
+ list_move(&entry->list, &vm->used);
vm->size -= size;
- block = entry;
- goto out;
+ return entry;
}
block = kzalloc(sizeof(*block), GFP_KERNEL);
if (!block)
- goto out;
+ return NULL;
block->addr = entry->addr;
block->size = size;
@@ -70,8 +66,6 @@ get_vm_block(struct ct_vm *vm, unsigned int size)
entry->size -= size;
vm->size -= size;
- out:
- mutex_unlock(&vm->lock);
return block;
}
@@ -82,7 +76,7 @@ static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block)
block->size = CT_PAGE_ALIGN(block->size);
- mutex_lock(&vm->lock);
+ guard(mutex)(&vm->lock);
list_del(&block->list);
vm->size += block->size;
@@ -119,7 +113,6 @@ static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block)
pos = pre;
pre = pos->prev;
}
- mutex_unlock(&vm->lock);
}
/* Map host addr (kmalloced/vmalloced) to device logical addr. */
@@ -130,11 +123,12 @@ ct_vm_map(struct ct_vm *vm, struct snd_pcm_substream *substream, int size)
unsigned int pte_start;
unsigned i, pages;
unsigned long *ptp;
+ struct ct_atc *atc = snd_pcm_substream_chip(substream);
- block = get_vm_block(vm, size);
+ block = get_vm_block(vm, size, atc);
if (block == NULL) {
- printk(KERN_ERR "ctxfi: No virtual memory block that is big "
- "enough to allocate!\n");
+ dev_err(atc->card->dev,
+ "No virtual memory block that is big enough to allocate!\n");
return NULL;
}
@@ -165,11 +159,7 @@ static void ct_vm_unmap(struct ct_vm *vm, struct ct_vm_block *block)
static dma_addr_t
ct_get_ptp_phys(struct ct_vm *vm, int index)
{
- dma_addr_t addr;
-
- addr = (index >= CT_PTP_NUM) ? ~0UL : vm->ptp[index].addr;
-
- return addr;
+ return (index >= CT_PTP_NUM) ? ~0UL : vm->ptp[index].addr;
}
int ct_vm_create(struct ct_vm **rvm, struct pci_dev *pci)
@@ -189,7 +179,7 @@ int ct_vm_create(struct ct_vm **rvm, struct pci_dev *pci)
/* Allocate page table pages */
for (i = 0; i < CT_PTP_NUM; i++) {
err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(pci),
+ &pci->dev,
PAGE_SIZE, &vm->ptp[i]);
if (err < 0)
break;
diff --git a/sound/pci/ctxfi/ctvmem.h b/sound/pci/ctxfi/ctvmem.h
index b23adfca4de6..da54cbcdb0be 100644
--- a/sound/pci/ctxfi/ctvmem.h
+++ b/sound/pci/ctxfi/ctvmem.h
@@ -1,10 +1,7 @@
-/**
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
*
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
- *
* @File ctvmem.h
*
* @Brief
@@ -18,7 +15,7 @@
#ifndef CTVMEM_H
#define CTVMEM_H
-#define CT_PTP_NUM 1 /* num of device page table pages */
+#define CT_PTP_NUM 4 /* num of device page table pages */
#include <linux/mutex.h>
#include <linux/list.h>
diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c
index f42e7e1a1074..d8dd84d41c87 100644
--- a/sound/pci/ctxfi/xfi.c
+++ b/sound/pci/ctxfi/xfi.c
@@ -1,17 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* xfi linux driver.
*
* Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
- *
- * This source file is released under GPL v2 license (no other versions).
- * See the COPYING file included in the main directory of this source
- * distribution for the license terms and conditions.
*/
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/moduleparam.h>
#include <linux/pci_ids.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include "ctatc.h"
@@ -20,18 +18,17 @@
MODULE_AUTHOR("Creative Technology Ltd");
MODULE_DESCRIPTION("X-Fi driver version 1.03");
MODULE_LICENSE("GPL v2");
-MODULE_SUPPORTED_DEVICE("{{Creative Labs, Sound Blaster X-Fi}");
static unsigned int reference_rate = 48000;
static unsigned int multiple = 2;
MODULE_PARM_DESC(reference_rate, "Reference rate (default=48000)");
-module_param(reference_rate, uint, S_IRUGO);
+module_param(reference_rate, uint, 0444);
MODULE_PARM_DESC(multiple, "Rate multiplier (default=2)");
-module_param(multiple, uint, S_IRUGO);
+module_param(multiple, uint, 0444);
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
static unsigned int subsystem[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
@@ -43,7 +40,7 @@ MODULE_PARM_DESC(enable, "Enable Creative X-Fi driver");
module_param_array(subsystem, int, NULL, 0444);
MODULE_PARM_DESC(subsystem, "Override subsystem ID for Creative X-Fi driver");
-static DEFINE_PCI_DEVICE_TABLE(ct_pci_dev_ids) = {
+static const struct pci_device_id ct_pci_dev_ids[] = {
/* only X-Fi is supported, so... */
{ PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K1),
.driver_data = ATC20K1,
@@ -55,7 +52,7 @@ static DEFINE_PCI_DEVICE_TABLE(ct_pci_dev_ids) = {
};
MODULE_DEVICE_TABLE(pci, ct_pci_dev_ids);
-static int __devinit
+static int
ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
static int dev;
@@ -70,21 +67,23 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
dev++;
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ 0, &card);
if (err)
return err;
if ((reference_rate != 48000) && (reference_rate != 44100)) {
- printk(KERN_ERR "ctxfi: Invalid reference_rate value %u!!!\n",
- reference_rate);
- printk(KERN_ERR "ctxfi: The valid values for reference_rate "
- "are 48000 and 44100, Value 48000 is assumed.\n");
+ dev_err(card->dev,
+ "Invalid reference_rate value %u!!!\n",
+ reference_rate);
+ dev_err(card->dev,
+ "The valid values for reference_rate are 48000 and 44100, Value 48000 is assumed.\n");
reference_rate = 48000;
}
- if ((multiple != 1) && (multiple != 2)) {
- printk(KERN_ERR "ctxfi: Invalid multiple value %u!!!\n",
- multiple);
- printk(KERN_ERR "ctxfi: The valid values for multiple are "
- "1 and 2, Value 2 is assumed.\n");
+ if ((multiple != 1) && (multiple != 2) && (multiple != 4)) {
+ dev_err(card->dev, "Invalid multiple value %u!!!\n",
+ multiple);
+ dev_err(card->dev,
+ "The valid values for multiple are 1, 2 and 4, Value 2 is assumed.\n");
multiple = 2;
}
err = ct_atc_create(card, pci, reference_rate, multiple,
@@ -99,8 +98,8 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
if (err < 0)
goto error;
- strcpy(card->driver, "SB-XFi");
- strcpy(card->shortname, "Creative X-Fi");
+ strscpy(card->driver, "SB-XFi");
+ strscpy(card->shortname, "Creative X-Fi");
snprintf(card->longname, sizeof(card->longname), "%s %s %s",
card->shortname, atc->chip_name, atc->model_name);
@@ -118,50 +117,42 @@ error:
return err;
}
-static void __devexit ct_card_remove(struct pci_dev *pci)
+static void ct_card_remove(struct pci_dev *pci)
{
snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
-#ifdef CONFIG_PM
-static int ct_card_suspend(struct pci_dev *pci, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int ct_card_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct ct_atc *atc = card->private_data;
- return atc->suspend(atc, state);
+ return atc->suspend(atc);
}
-static int ct_card_resume(struct pci_dev *pci)
+static int ct_card_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct ct_atc *atc = card->private_data;
return atc->resume(atc);
}
+
+static SIMPLE_DEV_PM_OPS(ct_card_pm, ct_card_suspend, ct_card_resume);
+#define CT_CARD_PM_OPS &ct_card_pm
+#else
+#define CT_CARD_PM_OPS NULL
#endif
static struct pci_driver ct_driver = {
- .name = "SB-XFi",
+ .name = KBUILD_MODNAME,
.id_table = ct_pci_dev_ids,
.probe = ct_card_probe,
- .remove = __devexit_p(ct_card_remove),
-#ifdef CONFIG_PM
- .suspend = ct_card_suspend,
- .resume = ct_card_resume,
-#endif
+ .remove = ct_card_remove,
+ .driver = {
+ .pm = CT_CARD_PM_OPS,
+ },
};
-static int __init ct_card_init(void)
-{
- return pci_register_driver(&ct_driver);
-}
-
-static void __exit ct_card_exit(void)
-{
- pci_unregister_driver(&ct_driver);
-}
-
-module_init(ct_card_init)
-module_exit(ct_card_exit)
+module_pci_driver(ct_driver);
diff --git a/sound/pci/echoaudio/Makefile b/sound/pci/echoaudio/Makefile
index 1361de77e0cd..96667641c7cf 100644
--- a/sound/pci/echoaudio/Makefile
+++ b/sound/pci/echoaudio/Makefile
@@ -1,22 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA Echoaudio soundcard drivers
# Copyright (c) 2003 by Giuliano Pochini <pochini@shiny.it>
#
-snd-darla20-objs := darla20.o
-snd-gina20-objs := gina20.o
-snd-layla20-objs := layla20.o
-snd-darla24-objs := darla24.o
-snd-gina24-objs := gina24.o
-snd-layla24-objs := layla24.o
-snd-mona-objs := mona.o
-snd-mia-objs := mia.o
-snd-echo3g-objs := echo3g.o
-snd-indigo-objs := indigo.o
-snd-indigoio-objs := indigoio.o
-snd-indigodj-objs := indigodj.o
-snd-indigoiox-objs := indigoiox.o
-snd-indigodjx-objs := indigodjx.o
+snd-darla20-y := darla20.o
+snd-gina20-y := gina20.o
+snd-layla20-y := layla20.o
+snd-darla24-y := darla24.o
+snd-gina24-y := gina24.o
+snd-layla24-y := layla24.o
+snd-mona-y := mona.o
+snd-mia-y := mia.o
+snd-echo3g-y := echo3g.o
+snd-indigo-y := indigo.o
+snd-indigoio-y := indigoio.o
+snd-indigodj-y := indigodj.o
+snd-indigoiox-y := indigoiox.o
+snd-indigodjx-y := indigodjx.o
obj-$(CONFIG_SND_DARLA20) += snd-darla20.o
obj-$(CONFIG_SND_GINA20) += snd-gina20.o
diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c
index fe7ad64dccd7..e295c71c7a39 100644
--- a/sound/pci/echoaudio/darla20.c
+++ b/sound/pci/echoaudio/darla20.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define ECHOGALS_FAMILY
@@ -40,9 +28,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -51,8 +40,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/darla20_dsp.fw");
@@ -63,12 +51,12 @@ static const struct firmware card_fw[] = {
{0, "darla20_dsp.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x1801, 0xECC0, 0x0010, 0, 0, 0}, /* DSP 56301 Darla20 rev.0 */
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/darla20_dsp.c b/sound/pci/echoaudio/darla20_dsp.c
index 20c7cbc89bb3..0356efad7528 100644
--- a/sound/pci/echoaudio/darla20_dsp.c
+++ b/sound/pci/echoaudio/darla20_dsp.c
@@ -33,31 +33,32 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Darla20\n"));
if (snd_BUG_ON((subdevice_id & 0xfff0) != DARLA20))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
- DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw: could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_DARLA20_DSP;
chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
chip->clock_state = GD_CLOCK_UNDEF;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c
index d1fd34b1a8e3..ae816e78f599 100644
--- a/sound/pci/echoaudio/darla24.c
+++ b/sound/pci/echoaudio/darla24.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define ECHOGALS_FAMILY
@@ -44,9 +32,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -55,8 +44,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/darla24_dsp.fw");
@@ -67,13 +55,13 @@ static const struct firmware card_fw[] = {
{0, "darla24_dsp.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x1801, 0xECC0, 0x0040, 0, 0, 0}, /* DSP 56301 Darla24 rev.0 */
{0x1057, 0x1801, 0xECC0, 0x0041, 0, 0, 0}, /* DSP 56301 Darla24 rev.1 */
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/darla24_dsp.c b/sound/pci/echoaudio/darla24_dsp.c
index 6da6663e9176..b96300772aee 100644
--- a/sound/pci/echoaudio/darla24_dsp.c
+++ b/sound/pci/echoaudio/darla24_dsp.c
@@ -33,30 +33,31 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Darla24\n"));
if (snd_BUG_ON((subdevice_id & 0xfff0) != DARLA24))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
- DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw: could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_DARLA24_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
ECHO_CLOCK_BIT_ESYNC;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -128,15 +129,17 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
clock = GD24_8000;
break;
default:
- DE_ACT(("set_sample_rate: Error, invalid sample rate %d\n",
- rate));
+ dev_err(chip->card->dev,
+ "set_sample_rate: Error, invalid sample rate %d\n",
+ rate);
return -EINVAL;
}
if (wait_handshake(chip))
return -EIO;
- DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock));
+ dev_dbg(chip->card->dev,
+ "set_sample_rate: %d clock %d\n", rate, clock);
chip->sample_rate = rate;
/* Override the sample rate if this card is set to Echo sync. */
diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c
index 1dffdc54416d..3d37bb4030ec 100644
--- a/sound/pci/echoaudio/echo3g.c
+++ b/sound/pci/echoaudio/echo3g.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define ECHO3G_FAMILY
@@ -51,9 +39,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -63,8 +52,7 @@
#include <sound/asoundef.h>
#include <sound/initval.h>
#include <sound/rawmidi.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -81,12 +69,12 @@ static const struct firmware card_fw[] = {
{0, "3g_asic.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x3410, 0xECC0, 0x0100, 0, 0, 0}, /* Echo 3G */
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c
index 3cdc2ee2d1dd..9e1f2cad0909 100644
--- a/sound/pci/echoaudio/echo3g_dsp.c
+++ b/sound/pci/echoaudio/echo3g_dsp.c
@@ -46,12 +46,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
int err;
local_irq_enable();
- DE_INIT(("init_hw() - Echo3G\n"));
if (snd_BUG_ON((subdevice_id & 0xfff0) != ECHO3G))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
- DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
@@ -59,8 +60,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
cpu_to_le32((E3G_MAGIC_NUMBER / 48000) - 2);
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
- chip->has_midi = TRUE;
+ chip->bad_board = true;
+ chip->has_midi = true;
chip->dsp_code_to_load = FW_ECHO3G_DSP;
/* Load the DSP code and the ASIC on the PCI card and get
@@ -78,8 +79,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
chip->px_analog_in = chip->bx_analog_in = 14;
chip->px_digital_in = chip->bx_digital_in = 16;
chip->px_num = chip->bx_num = 24;
- chip->has_phantom_power = TRUE;
- chip->hasnt_input_nominal_level = TRUE;
+ chip->has_phantom_power = true;
+ chip->hasnt_input_nominal_level = true;
} else if (err == E3G_LAYLA3G_BOX_TYPE) {
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
ECHO_CLOCK_BIT_SPDIF |
@@ -98,7 +99,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -107,10 +107,10 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
static int set_mixer_defaults(struct echoaudio *chip)
{
chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
- chip->professional_spdif = FALSE;
- chip->non_audio_spdif = FALSE;
- chip->bad_board = FALSE;
- chip->phantom_power = FALSE;
+ chip->professional_spdif = false;
+ chip->non_audio_spdif = false;
+ chip->bad_board = false;
+ chip->phantom_power = false;
return init_line_levels(chip);
}
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 20763dd03fa0..f2c8602a1ad7 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -1,30 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Copyright (C) 2020 Mark Hills <mark@xwax.org>
*/
+#include <linux/module.h>
+#include <linux/string.h>
+
MODULE_AUTHOR("Giuliano Pochini <pochini@shiny.it>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Echoaudio " ECHOCARD_NAME " soundcards driver");
-MODULE_SUPPORTED_DEVICE("{{Echoaudio," ECHOCARD_NAME "}}");
MODULE_DEVICE_TABLE(pci, snd_echo_ids);
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " ECHOCARD_NAME " soundcard.");
@@ -33,7 +24,7 @@ MODULE_PARM_DESC(id, "ID string for " ECHOCARD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " ECHOCARD_NAME " soundcard.");
-static unsigned int channels_list[10] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 999999};
+static const unsigned int channels_list[10] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 999999};
static const DECLARE_TLV_DB_SCALE(db_scale_output_gain, -12800, 100, 1);
@@ -44,53 +35,45 @@ static int get_firmware(const struct firmware **fw_entry,
int err;
char name[30];
-#ifdef CONFIG_PM
if (chip->fw_cache[fw_index]) {
- DE_ACT(("firmware requested: %s is cached\n", card_fw[fw_index].data));
+ dev_dbg(chip->card->dev,
+ "firmware requested: %s is cached\n",
+ card_fw[fw_index].data);
*fw_entry = chip->fw_cache[fw_index];
return 0;
}
-#endif
- DE_ACT(("firmware requested: %s\n", card_fw[fw_index].data));
+ dev_dbg(chip->card->dev,
+ "firmware requested: %s\n", card_fw[fw_index].data);
snprintf(name, sizeof(name), "ea/%s", card_fw[fw_index].data);
- err = request_firmware(fw_entry, name, pci_device(chip));
+ err = request_firmware(fw_entry, name, &chip->pci->dev);
if (err < 0)
- snd_printk(KERN_ERR "get_firmware(): Firmware not available (%d)\n", err);
-#ifdef CONFIG_PM
+ dev_err(chip->card->dev,
+ "get_firmware(): Firmware not available (%d)\n", err);
else
chip->fw_cache[fw_index] = *fw_entry;
-#endif
return err;
}
-static void free_firmware(const struct firmware *fw_entry)
+static void free_firmware(const struct firmware *fw_entry,
+ struct echoaudio *chip)
{
-#ifdef CONFIG_PM
- DE_ACT(("firmware not released (kept in cache)\n"));
-#else
- release_firmware(fw_entry);
- DE_ACT(("firmware released\n"));
-#endif
+ dev_dbg(chip->card->dev, "firmware not released (kept in cache)\n");
}
static void free_firmware_cache(struct echoaudio *chip)
{
-#ifdef CONFIG_PM
int i;
for (i = 0; i < 8 ; i++)
if (chip->fw_cache[i]) {
release_firmware(chip->fw_cache[i]);
- DE_ACT(("release_firmware(%d)\n", i));
+ dev_dbg(chip->card->dev, "release_firmware(%d)\n", i);
}
-
- DE_ACT(("firmware_cache released\n"));
-#endif
}
@@ -252,13 +235,19 @@ static int hw_rule_sample_rate(struct snd_pcm_hw_params *params,
SNDRV_PCM_HW_PARAM_RATE);
struct echoaudio *chip = rule->private;
struct snd_interval fixed;
+ int err;
+
+ guard(mutex)(&chip->mode_mutex);
- if (!chip->can_set_rate) {
+ if (chip->can_set_rate) {
+ err = 0;
+ } else {
snd_interval_any(&fixed);
fixed.min = fixed.max = chip->sample_rate;
- return snd_interval_refine(rate, &fixed);
+ err = snd_interval_refine(rate, &fixed);
}
- return 0;
+
+ return err;
}
@@ -283,7 +272,7 @@ static int pcm_open(struct snd_pcm_substream *substream,
/* Set up hw capabilities and contraints */
memcpy(&pipe->hw, &pcm_hardware_skel, sizeof(struct snd_pcm_hardware));
- DE_HWP(("max_channels=%d\n", max_channels));
+ dev_dbg(chip->card->dev, "max_channels=%d\n", max_channels);
pipe->constr.list = channels_list;
pipe->constr.mask = 0;
for (i = 0; channels_list[i] <= max_channels; i++);
@@ -301,42 +290,57 @@ static int pcm_open(struct snd_pcm_substream *substream,
snd_pcm_set_sync(substream);
/* Only mono and any even number of channels are allowed */
- if ((err = snd_pcm_hw_constraint_list(runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- &pipe->constr)) < 0)
+ err = snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &pipe->constr);
+ if (err < 0)
return err;
/* All periods should have the same size */
- if ((err = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ err = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
return err;
/* The hw accesses memory in chunks 32 frames long and they should be
32-bytes-aligned. It's not a requirement, but it seems that IRQs are
generated with a resolution of 32 frames. Thus we need the following */
- if ((err = snd_pcm_hw_constraint_step(runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- 32)) < 0)
+ err = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
+ if (err < 0)
return err;
- if ((err = snd_pcm_hw_constraint_step(runtime, 0,
- SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
- 32)) < 0)
+ err = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
+ if (err < 0)
return err;
- if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- hw_rule_sample_rate, chip,
- SNDRV_PCM_HW_PARAM_RATE, -1)) < 0)
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_sample_rate, chip,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
return err;
- /* Finally allocate a page for the scatter-gather list */
- if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- PAGE_SIZE, &pipe->sgpage)) < 0) {
- DE_HWP(("s-g list allocation failed\n"));
+ /* Allocate a page for the scatter-gather list */
+ err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev,
+ PAGE_SIZE, &pipe->sgpage);
+ if (err < 0) {
+ dev_err(chip->card->dev, "s-g list allocation failed\n");
return err;
}
+ /*
+ * Sole ownership required to set the rate
+ */
+
+ dev_dbg(chip->card->dev, "pcm_open opencount=%d can_set_rate=%d, rate_set=%d",
+ chip->opencount, chip->can_set_rate, chip->rate_set);
+
+ chip->opencount++;
+ if (chip->opencount > 1 && chip->rate_set)
+ chip->can_set_rate = 0;
+
return 0;
}
@@ -347,26 +351,23 @@ static int pcm_analog_in_open(struct snd_pcm_substream *substream)
struct echoaudio *chip = snd_pcm_substream_chip(substream);
int err;
- DE_ACT(("pcm_analog_in_open\n"));
- if ((err = pcm_open(substream, num_analog_busses_in(chip) -
- substream->number)) < 0)
+ err = pcm_open(substream,
+ num_analog_busses_in(chip) - substream->number);
+ if (err < 0)
return err;
- if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- hw_rule_capture_channels_by_format, NULL,
- SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_capture_channels_by_format, NULL,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1);
+ if (err < 0)
return err;
- if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_FORMAT,
- hw_rule_capture_format_by_channels, NULL,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ hw_rule_capture_format_by_channels, NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
return err;
- atomic_inc(&chip->opencount);
- if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
- chip->can_set_rate=0;
- DE_HWP(("pcm_analog_in_open cs=%d oc=%d r=%d\n",
- chip->can_set_rate, atomic_read(&chip->opencount),
- chip->sample_rate));
+
return 0;
}
@@ -382,27 +383,24 @@ static int pcm_analog_out_open(struct snd_pcm_substream *substream)
#else
max_channels = num_analog_busses_out(chip);
#endif
- DE_ACT(("pcm_analog_out_open\n"));
- if ((err = pcm_open(substream, max_channels - substream->number)) < 0)
+ err = pcm_open(substream, max_channels - substream->number);
+ if (err < 0)
return err;
- if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- hw_rule_playback_channels_by_format,
- NULL,
- SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_playback_channels_by_format,
+ NULL,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1);
+ if (err < 0)
return err;
- if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_FORMAT,
- hw_rule_playback_format_by_channels,
- NULL,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ hw_rule_playback_format_by_channels,
+ NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
return err;
- atomic_inc(&chip->opencount);
- if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
- chip->can_set_rate=0;
- DE_HWP(("pcm_analog_out_open cs=%d oc=%d r=%d\n",
- chip->can_set_rate, atomic_read(&chip->opencount),
- chip->sample_rate));
+
return 0;
}
@@ -415,9 +413,8 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream)
struct echoaudio *chip = snd_pcm_substream_chip(substream);
int err, max_channels;
- DE_ACT(("pcm_digital_in_open\n"));
max_channels = num_digital_busses_in(chip) - substream->number;
- mutex_lock(&chip->mode_mutex);
+ guard(mutex)(&chip->mode_mutex);
if (chip->digital_mode == DIGITAL_MODE_ADAT)
err = pcm_open(substream, max_channels);
else /* If the card has ADAT, subtract the 6 channels
@@ -426,26 +423,22 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream)
err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT);
if (err < 0)
- goto din_exit;
-
- if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- hw_rule_capture_channels_by_format, NULL,
- SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)
- goto din_exit;
- if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_FORMAT,
- hw_rule_capture_format_by_channels, NULL,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)
- goto din_exit;
-
- atomic_inc(&chip->opencount);
- if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
- chip->can_set_rate=0;
-
-din_exit:
- mutex_unlock(&chip->mode_mutex);
- return err;
+ return err;
+
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_capture_channels_by_format, NULL,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ hw_rule_capture_format_by_channels, NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ return err;
+
+ return 0;
}
@@ -457,9 +450,8 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream)
struct echoaudio *chip = snd_pcm_substream_chip(substream);
int err, max_channels;
- DE_ACT(("pcm_digital_out_open\n"));
max_channels = num_digital_busses_out(chip) - substream->number;
- mutex_lock(&chip->mode_mutex);
+ guard(mutex)(&chip->mode_mutex);
if (chip->digital_mode == DIGITAL_MODE_ADAT)
err = pcm_open(substream, max_channels);
else /* If the card has ADAT, subtract the 6 channels
@@ -468,26 +460,24 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream)
err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT);
if (err < 0)
- goto dout_exit;
-
- if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- hw_rule_playback_channels_by_format,
- NULL, SNDRV_PCM_HW_PARAM_FORMAT,
- -1)) < 0)
- goto dout_exit;
- if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_FORMAT,
- hw_rule_playback_format_by_channels,
- NULL, SNDRV_PCM_HW_PARAM_CHANNELS,
- -1)) < 0)
- goto dout_exit;
- atomic_inc(&chip->opencount);
- if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
- chip->can_set_rate=0;
-dout_exit:
- mutex_unlock(&chip->mode_mutex);
- return err;
+ return err;
+
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_playback_channels_by_format,
+ NULL, SNDRV_PCM_HW_PARAM_FORMAT,
+ -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ hw_rule_playback_format_by_channels,
+ NULL, SNDRV_PCM_HW_PARAM_CHANNELS,
+ -1);
+ if (err < 0)
+ return err;
+
+ return 0;
}
#endif /* !ECHOCARD_HAS_VMIXER */
@@ -499,23 +489,27 @@ dout_exit:
static int pcm_close(struct snd_pcm_substream *substream)
{
struct echoaudio *chip = snd_pcm_substream_chip(substream);
- int oc;
/* Nothing to do here. Audio is already off and pipe will be
* freed by its callback
*/
- DE_ACT(("pcm_close\n"));
- atomic_dec(&chip->opencount);
- oc = atomic_read(&chip->opencount);
- DE_ACT(("pcm_close oc=%d cs=%d rs=%d\n", oc,
- chip->can_set_rate, chip->rate_set));
- if (oc < 2)
+ guard(mutex)(&chip->mode_mutex);
+
+ dev_dbg(chip->card->dev, "pcm_open opencount=%d can_set_rate=%d, rate_set=%d",
+ chip->opencount, chip->can_set_rate, chip->rate_set);
+
+ chip->opencount--;
+
+ switch (chip->opencount) {
+ case 1:
chip->can_set_rate = 1;
- if (oc == 0)
+ break;
+
+ case 0:
chip->rate_set = 0;
- DE_ACT(("pcm_close2 oc=%d cs=%d rs=%d\n", oc,
- chip->can_set_rate,chip->rate_set));
+ break;
+ }
return 0;
}
@@ -537,37 +531,27 @@ static int init_engine(struct snd_pcm_substream *substream,
/* Sets up che hardware. If it's already initialized, reset and
* redo with the new parameters
*/
- spin_lock_irq(&chip->lock);
- if (pipe->index >= 0) {
- DE_HWP(("hwp_ie free(%d)\n", pipe->index));
- err = free_pipes(chip, pipe);
- snd_BUG_ON(err);
- chip->substream[pipe->index] = NULL;
- }
+ scoped_guard(spinlock_irq, &chip->lock) {
+ if (pipe->index >= 0) {
+ dev_dbg(chip->card->dev, "hwp_ie free(%d)\n", pipe->index);
+ err = free_pipes(chip, pipe);
+ snd_BUG_ON(err);
+ chip->substream[pipe->index] = NULL;
+ }
- err = allocate_pipes(chip, pipe, pipe_index, interleave);
- if (err < 0) {
- spin_unlock_irq(&chip->lock);
- DE_ACT((KERN_NOTICE "allocate_pipes(%d) err=%d\n",
- pipe_index, err));
- return err;
+ err = allocate_pipes(chip, pipe, pipe_index, interleave);
+ if (err < 0) {
+ dev_err(chip->card->dev, "allocate_pipes(%d) err=%d\n",
+ pipe_index, err);
+ return err;
+ }
}
- spin_unlock_irq(&chip->lock);
- DE_ACT((KERN_NOTICE "allocate_pipes()=%d\n", pipe_index));
+ dev_dbg(chip->card->dev, "allocate_pipes()=%d\n", pipe_index);
- DE_HWP(("pcm_hw_params (bufsize=%dB periods=%d persize=%dB)\n",
+ dev_dbg(chip->card->dev,
+ "pcm_hw_params (bufsize=%dB periods=%d persize=%dB)\n",
params_buffer_bytes(hw_params), params_periods(hw_params),
- params_period_bytes(hw_params)));
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0) {
- snd_printk(KERN_ERR "malloc_pages err=%d\n", err);
- spin_lock_irq(&chip->lock);
- free_pipes(chip, pipe);
- spin_unlock_irq(&chip->lock);
- pipe->index = -1;
- return err;
- }
+ params_period_bytes(hw_params));
sglist_init(chip, pipe);
edge = PAGE_SIZE;
@@ -603,16 +587,14 @@ static int init_engine(struct snd_pcm_substream *substream,
/* This stuff is used by the irq handler, so it must be
* initialized before chip->substream
*/
- chip->last_period[pipe_index] = 0;
+ pipe->last_period = 0;
pipe->last_counter = 0;
pipe->position = 0;
smp_wmb();
chip->substream[pipe_index] = substream;
chip->rate_set = 1;
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
set_sample_rate(chip, hw_params->rate_num / hw_params->rate_den);
- spin_unlock_irq(&chip->lock);
- DE_HWP(("pcm_hw_params ok\n"));
return 0;
}
@@ -674,17 +656,14 @@ static int pcm_hw_free(struct snd_pcm_substream *substream)
chip = snd_pcm_substream_chip(substream);
pipe = (struct audiopipe *) substream->runtime->private_data;
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
if (pipe->index >= 0) {
- DE_HWP(("pcm_hw_free(%d)\n", pipe->index));
+ dev_dbg(chip->card->dev, "pcm_hw_free(%d)\n", pipe->index);
free_pipes(chip, pipe);
chip->substream[pipe->index] = NULL;
pipe->index = -1;
}
- spin_unlock_irq(&chip->lock);
- DE_HWP(("pcm_hw_freed\n"));
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -697,8 +676,8 @@ static int pcm_prepare(struct snd_pcm_substream *substream)
struct audioformat format;
int pipe_index = ((struct audiopipe *)runtime->private_data)->index;
- DE_HWP(("Prepare rate=%d format=%d channels=%d\n",
- runtime->rate, runtime->format, runtime->channels));
+ dev_dbg(chip->card->dev, "Prepare rate=%d format=%d channels=%d\n",
+ runtime->rate, runtime->format, runtime->channels);
format.interleave = runtime->channels;
format.data_are_bigendian = 0;
format.mono_to_stereo = 0;
@@ -714,20 +693,32 @@ static int pcm_prepare(struct snd_pcm_substream *substream)
break;
case SNDRV_PCM_FORMAT_S32_BE:
format.data_are_bigendian = 1;
+ fallthrough;
case SNDRV_PCM_FORMAT_S32_LE:
format.bits_per_sample = 32;
break;
default:
- DE_HWP(("Prepare error: unsupported format %d\n",
- runtime->format));
+ dev_err(chip->card->dev,
+ "Prepare error: unsupported format %d\n",
+ runtime->format);
return -EINVAL;
}
if (snd_BUG_ON(pipe_index >= px_num(chip)))
return -EINVAL;
+
+ /*
+ * We passed checks we can do independently; now take
+ * exclusive control
+ */
+
+ guard(spinlock_irq)(&chip->lock);
+
if (snd_BUG_ON(!is_pipe_allocated(chip, pipe_index)))
return -EINVAL;
+
set_audio_format(chip, pipe_index, &format);
+
return 0;
}
@@ -736,8 +727,7 @@ static int pcm_prepare(struct snd_pcm_substream *substream)
static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct echoaudio *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct audiopipe *pipe = runtime->private_data;
+ struct audiopipe *pipe;
int i, err;
u32 channelmask = 0;
struct snd_pcm_substream *s;
@@ -751,22 +741,21 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
}
}
- spin_lock(&chip->lock);
+ guard(spinlock)(&chip->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_RESUME:
- DE_ACT(("pcm_trigger resume\n"));
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- DE_ACT(("pcm_trigger start\n"));
for (i = 0; i < DSP_MAXPIPES; i++) {
if (channelmask & (1 << i)) {
pipe = chip->substream[i]->runtime->private_data;
switch (pipe->state) {
case PIPE_STATE_STOPPED:
- chip->last_period[i] = 0;
+ pipe->last_period = 0;
pipe->last_counter = 0;
pipe->position = 0;
*pipe->dma_counter = 0;
+ fallthrough;
case PIPE_STATE_PAUSED:
pipe->state = PIPE_STATE_STARTED;
break;
@@ -779,9 +768,7 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
chip->pipe_cyclic_mask);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
- DE_ACT(("pcm_trigger suspend\n"));
case SNDRV_PCM_TRIGGER_STOP:
- DE_ACT(("pcm_trigger stop\n"));
for (i = 0; i < DSP_MAXPIPES; i++) {
if (channelmask & (1 << i)) {
pipe = chip->substream[i]->runtime->private_data;
@@ -791,7 +778,6 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
err = stop_transport(chip, channelmask);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- DE_ACT(("pcm_trigger pause\n"));
for (i = 0; i < DSP_MAXPIPES; i++) {
if (channelmask & (1 << i)) {
pipe = chip->substream[i]->runtime->private_data;
@@ -803,7 +789,6 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
default:
err = -EINVAL;
}
- spin_unlock(&chip->lock);
return err;
}
@@ -813,70 +798,69 @@ static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct audiopipe *pipe = runtime->private_data;
- size_t cnt, bufsize, pos;
+ u32 counter, step;
- cnt = le32_to_cpu(*pipe->dma_counter);
- pipe->position += cnt - pipe->last_counter;
- pipe->last_counter = cnt;
- bufsize = substream->runtime->buffer_size;
- pos = bytes_to_frames(substream->runtime, pipe->position);
+ /*
+ * IRQ handling runs concurrently. Do not share tracking of
+ * counter with it, which would race or require locking
+ */
- while (pos >= bufsize) {
- pipe->position -= frames_to_bytes(substream->runtime, bufsize);
- pos -= bufsize;
- }
- return pos;
+ counter = le32_to_cpu(*pipe->dma_counter); /* presumed atomic */
+
+ step = counter - pipe->last_counter; /* handles wrapping */
+ pipe->last_counter = counter;
+
+ /* counter doesn't neccessarily wrap on a multiple of
+ * buffer_size, so can't derive the position; must
+ * accumulate */
+
+ pipe->position += step;
+ pipe->position %= frames_to_bytes(runtime, runtime->buffer_size); /* wrap */
+
+ return bytes_to_frames(runtime, pipe->position);
}
/* pcm *_ops structures */
-static struct snd_pcm_ops analog_playback_ops = {
+static const struct snd_pcm_ops analog_playback_ops = {
.open = pcm_analog_out_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = pcm_analog_out_hw_params,
.hw_free = pcm_hw_free,
.prepare = pcm_prepare,
.trigger = pcm_trigger,
.pointer = pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
-static struct snd_pcm_ops analog_capture_ops = {
+static const struct snd_pcm_ops analog_capture_ops = {
.open = pcm_analog_in_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = pcm_analog_in_hw_params,
.hw_free = pcm_hw_free,
.prepare = pcm_prepare,
.trigger = pcm_trigger,
.pointer = pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
#ifdef ECHOCARD_HAS_DIGITAL_IO
#ifndef ECHOCARD_HAS_VMIXER
-static struct snd_pcm_ops digital_playback_ops = {
+static const struct snd_pcm_ops digital_playback_ops = {
.open = pcm_digital_out_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = pcm_digital_out_hw_params,
.hw_free = pcm_hw_free,
.prepare = pcm_prepare,
.trigger = pcm_trigger,
.pointer = pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
#endif /* !ECHOCARD_HAS_VMIXER */
-static struct snd_pcm_ops digital_capture_ops = {
+static const struct snd_pcm_ops digital_capture_ops = {
.open = pcm_digital_in_open,
.close = pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = pcm_digital_in_hw_params,
.hw_free = pcm_hw_free,
.prepare = pcm_prepare,
.trigger = pcm_trigger,
.pointer = pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
#endif /* ECHOCARD_HAS_DIGITAL_IO */
@@ -885,27 +869,23 @@ static struct snd_pcm_ops digital_capture_ops = {
/* Preallocate memory only for the first substream because it's the most
* used one
*/
-static int snd_echo_preallocate_pages(struct snd_pcm *pcm, struct device *dev)
+static void snd_echo_preallocate_pages(struct snd_pcm *pcm, struct device *dev)
{
struct snd_pcm_substream *ss;
- int stream, err;
+ int stream;
for (stream = 0; stream < 2; stream++)
- for (ss = pcm->streams[stream].substream; ss; ss = ss->next) {
- err = snd_pcm_lib_preallocate_pages(ss, SNDRV_DMA_TYPE_DEV_SG,
- dev,
- ss->number ? 0 : 128<<10,
- 256<<10);
- if (err < 0)
- return err;
- }
- return 0;
+ for (ss = pcm->streams[stream].substream; ss; ss = ss->next)
+ snd_pcm_set_managed_buffer(ss, SNDRV_DMA_TYPE_DEV_SG,
+ dev,
+ ss->number ? 0 : 128<<10,
+ 256<<10);
}
/*<--snd_echo_probe() */
-static int __devinit snd_echo_new_pcm(struct echoaudio *chip)
+static int snd_echo_new_pcm(struct echoaudio *chip)
{
struct snd_pcm *pcm;
int err;
@@ -918,30 +898,28 @@ static int __devinit snd_echo_new_pcm(struct echoaudio *chip)
separated */
/* PCM#0 Virtual outputs and analog inputs */
- if ((err = snd_pcm_new(chip->card, "PCM", 0, num_pipes_out(chip),
- num_analog_busses_in(chip), &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "PCM", 0, num_pipes_out(chip),
+ num_analog_busses_in(chip), &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
chip->analog_pcm = pcm;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops);
- if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
- return err;
- DE_INIT(("Analog PCM ok\n"));
+ snd_echo_preallocate_pages(pcm, &chip->pci->dev);
#ifdef ECHOCARD_HAS_DIGITAL_IO
/* PCM#1 Digital inputs, no outputs */
- if ((err = snd_pcm_new(chip->card, "Digital PCM", 1, 0,
- num_digital_busses_in(chip), &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "Digital PCM", 1, 0,
+ num_digital_busses_in(chip), &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
chip->digital_pcm = pcm;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops);
- if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
- return err;
- DE_INIT(("Digital PCM ok\n"));
+ snd_echo_preallocate_pages(pcm, &chip->pci->dev);
#endif /* ECHOCARD_HAS_DIGITAL_IO */
#else /* ECHOCARD_HAS_VMIXER */
@@ -952,33 +930,31 @@ static int __devinit snd_echo_new_pcm(struct echoaudio *chip)
register two PCM devices: */
/* PCM#0 Analog i/o */
- if ((err = snd_pcm_new(chip->card, "Analog PCM", 0,
- num_analog_busses_out(chip),
- num_analog_busses_in(chip), &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "Analog PCM", 0,
+ num_analog_busses_out(chip),
+ num_analog_busses_in(chip), &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
chip->analog_pcm = pcm;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops);
- if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
- return err;
- DE_INIT(("Analog PCM ok\n"));
+ snd_echo_preallocate_pages(pcm, &chip->pci->dev);
#ifdef ECHOCARD_HAS_DIGITAL_IO
/* PCM#1 Digital i/o */
- if ((err = snd_pcm_new(chip->card, "Digital PCM", 1,
- num_digital_busses_out(chip),
- num_digital_busses_in(chip), &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "Digital PCM", 1,
+ num_digital_busses_out(chip),
+ num_digital_busses_in(chip), &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
chip->digital_pcm = pcm;
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &digital_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops);
- if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
- return err;
- DE_INIT(("Digital PCM ok\n"));
+ snd_echo_preallocate_pages(pcm, &chip->pci->dev);
#endif /* ECHOCARD_HAS_DIGITAL_IO */
#endif /* ECHOCARD_HAS_VMIXER */
@@ -1029,7 +1005,7 @@ static int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol,
changed = 0;
chip = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
for (c = 0; c < num_busses_out(chip); c++) {
gain = ucontrol->value.integer.value[c];
/* Ignore out of range values */
@@ -1042,13 +1018,12 @@ static int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol,
}
if (changed)
update_output_line_level(chip);
- spin_unlock_irq(&chip->lock);
return changed;
}
#ifdef ECHOCARD_HAS_LINE_OUT_GAIN
/* On the Mia this one controls the line-out volume */
-static struct snd_kcontrol_new snd_echo_line_output_gain __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_line_output_gain = {
.name = "Line Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -1059,7 +1034,7 @@ static struct snd_kcontrol_new snd_echo_line_output_gain __devinitdata = {
.tlv = {.p = db_scale_output_gain},
};
#else
-static struct snd_kcontrol_new snd_echo_pcm_output_gain __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_pcm_output_gain = {
.name = "PCM Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
@@ -1110,7 +1085,7 @@ static int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol,
changed = 0;
chip = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
for (c = 0; c < num_analog_busses_in(chip); c++) {
gain = ucontrol->value.integer.value[c];
/* Ignore out of range values */
@@ -1123,13 +1098,12 @@ static int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol,
}
if (changed)
update_input_line_level(chip);
- spin_unlock_irq(&chip->lock);
return changed;
}
static const DECLARE_TLV_DB_SCALE(db_scale_input_gain, -2500, 50, 0);
-static struct snd_kcontrol_new snd_echo_line_input_gain __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_line_input_gain = {
.name = "Line Capture Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
@@ -1179,7 +1153,7 @@ static int snd_echo_output_nominal_put(struct snd_kcontrol *kcontrol,
changed = 0;
chip = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
for (c = 0; c < num_analog_busses_out(chip); c++) {
if (chip->nominal_level[c] != ucontrol->value.integer.value[c]) {
set_nominal_level(chip, c,
@@ -1189,11 +1163,10 @@ static int snd_echo_output_nominal_put(struct snd_kcontrol *kcontrol,
}
if (changed)
update_output_line_level(chip);
- spin_unlock_irq(&chip->lock);
return changed;
}
-static struct snd_kcontrol_new snd_echo_output_nominal_level __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_output_nominal_level = {
.name = "Line Playback Switch (-10dBV)",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_echo_output_nominal_info,
@@ -1242,7 +1215,7 @@ static int snd_echo_input_nominal_put(struct snd_kcontrol *kcontrol,
changed = 0;
chip = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
for (c = 0; c < num_analog_busses_in(chip); c++) {
if (chip->nominal_level[bx_analog_in(chip) + c] !=
ucontrol->value.integer.value[c]) {
@@ -1255,11 +1228,10 @@ static int snd_echo_input_nominal_put(struct snd_kcontrol *kcontrol,
update_output_line_level(chip); /* "Output" is not a mistake
* here.
*/
- spin_unlock_irq(&chip->lock);
return changed;
}
-static struct snd_kcontrol_new snd_echo_intput_nominal_level __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_intput_nominal_level = {
.name = "Line Capture Switch (-10dBV)",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_echo_input_nominal_info,
@@ -1277,27 +1249,24 @@ static struct snd_kcontrol_new snd_echo_intput_nominal_level __devinitdata = {
static int snd_echo_mixer_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- struct echoaudio *chip;
-
- chip = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = ECHOGAIN_MINOUT;
uinfo->value.integer.max = ECHOGAIN_MAXOUT;
- uinfo->dimen.d[0] = num_busses_out(chip);
- uinfo->dimen.d[1] = num_busses_in(chip);
return 0;
}
static int snd_echo_mixer_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct echoaudio *chip;
+ struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int out = ucontrol->id.index / num_busses_in(chip);
+ unsigned int in = ucontrol->id.index % num_busses_in(chip);
- chip = snd_kcontrol_chip(kcontrol);
- ucontrol->value.integer.value[0] =
- chip->monitor_gain[ucontrol->id.index / num_busses_in(chip)]
- [ucontrol->id.index % num_busses_in(chip)];
+ if (out >= ECHO_MAXAUDIOOUTPUTS || in >= ECHO_MAXAUDIOINPUTS)
+ return -EINVAL;
+
+ ucontrol->value.integer.value[0] = chip->monitor_gain[out][in];
return 0;
}
@@ -1306,26 +1275,27 @@ static int snd_echo_mixer_put(struct snd_kcontrol *kcontrol,
{
struct echoaudio *chip;
int changed, gain;
- short out, in;
+ unsigned int out, in;
changed = 0;
chip = snd_kcontrol_chip(kcontrol);
out = ucontrol->id.index / num_busses_in(chip);
in = ucontrol->id.index % num_busses_in(chip);
+ if (out >= ECHO_MAXAUDIOOUTPUTS || in >= ECHO_MAXAUDIOINPUTS)
+ return -EINVAL;
gain = ucontrol->value.integer.value[0];
if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
return -EINVAL;
if (chip->monitor_gain[out][in] != gain) {
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
set_monitor_gain(chip, out, in, gain);
update_output_line_level(chip);
- spin_unlock_irq(&chip->lock);
changed = 1;
}
return changed;
}
-static struct snd_kcontrol_new snd_echo_monitor_mixer __devinitdata = {
+static struct snd_kcontrol_new snd_echo_monitor_mixer = {
.name = "Monitor Mixer Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
@@ -1345,15 +1315,10 @@ static struct snd_kcontrol_new snd_echo_monitor_mixer __devinitdata = {
static int snd_echo_vmixer_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- struct echoaudio *chip;
-
- chip = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = ECHOGAIN_MINOUT;
uinfo->value.integer.max = ECHOGAIN_MAXOUT;
- uinfo->dimen.d[0] = num_busses_out(chip);
- uinfo->dimen.d[1] = num_pipes_out(chip);
return 0;
}
@@ -1384,16 +1349,15 @@ static int snd_echo_vmixer_put(struct snd_kcontrol *kcontrol,
if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
return -EINVAL;
if (chip->vmixer_gain[out][vch] != ucontrol->value.integer.value[0]) {
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
set_vmixer_gain(chip, out, vch, ucontrol->value.integer.value[0]);
update_vmixer_level(chip);
- spin_unlock_irq(&chip->lock);
changed = 1;
}
return changed;
}
-static struct snd_kcontrol_new snd_echo_vmixer __devinitdata = {
+static struct snd_kcontrol_new snd_echo_vmixer = {
.name = "VMixer Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
@@ -1413,21 +1377,14 @@ static struct snd_kcontrol_new snd_echo_vmixer __devinitdata = {
static int snd_echo_digital_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *names[4] = {
+ static const char * const names[4] = {
"S/PDIF Coaxial", "S/PDIF Optical", "ADAT Optical",
"S/PDIF Cdrom"
};
struct echoaudio *chip;
chip = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->value.enumerated.items = chip->num_digital_modes;
- uinfo->count = 1;
- if (uinfo->value.enumerated.item >= chip->num_digital_modes)
- uinfo->value.enumerated.item = chip->num_digital_modes - 1;
- strcpy(uinfo->value.enumerated.name, names[
- chip->digital_mode_list[uinfo->value.enumerated.item]]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, chip->num_digital_modes, names);
}
static int snd_echo_digital_mode_get(struct snd_kcontrol *kcontrol,
@@ -1464,12 +1421,12 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
if (dmode != chip->digital_mode) {
/* mode_mutex is required to make this operation atomic wrt
pcm_digital_*_open() and set_input_clock() functions. */
- mutex_lock(&chip->mode_mutex);
+ guard(mutex)(&chip->mode_mutex);
/* Do not allow the user to change the digital mode when a pcm
device is open because it also changes the number of channels
and the allowed sample rates */
- if (atomic_read(&chip->opencount)) {
+ if (chip->opencount) {
changed = -EAGAIN;
} else {
changed = set_digital_mode(chip, dmode);
@@ -1478,17 +1435,17 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
snd_ctl_notify(chip->card,
SNDRV_CTL_EVENT_MASK_VALUE,
&chip->clock_src_ctl->id);
- DE_ACT(("SDM() =%d\n", changed));
+ dev_dbg(chip->card->dev,
+ "SDM() =%d\n", changed);
}
if (changed >= 0)
changed = 1; /* No errors */
}
- mutex_unlock(&chip->mode_mutex);
}
return changed;
}
-static struct snd_kcontrol_new snd_echo_digital_mode_switch __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_digital_mode_switch = {
.name = "Digital mode Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_digital_mode_info,
@@ -1506,16 +1463,9 @@ static struct snd_kcontrol_new snd_echo_digital_mode_switch __devinitdata = {
static int snd_echo_spdif_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *names[2] = {"Consumer", "Professional"};
+ static const char * const names[2] = {"Consumer", "Professional"};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->value.enumerated.items = 2;
- uinfo->count = 1;
- if (uinfo->value.enumerated.item)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name,
- names[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, names);
}
static int snd_echo_spdif_mode_get(struct snd_kcontrol *kcontrol,
@@ -1537,15 +1487,14 @@ static int snd_echo_spdif_mode_put(struct snd_kcontrol *kcontrol,
chip = snd_kcontrol_chip(kcontrol);
mode = !!ucontrol->value.enumerated.item[0];
if (mode != chip->professional_spdif) {
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
set_professional_spdif(chip, mode);
- spin_unlock_irq(&chip->lock);
return 1;
}
return 0;
}
-static struct snd_kcontrol_new snd_echo_spdif_mode_switch __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_spdif_mode_switch = {
.name = "S/PDIF mode Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_spdif_mode_info,
@@ -1563,21 +1512,14 @@ static struct snd_kcontrol_new snd_echo_spdif_mode_switch __devinitdata = {
static int snd_echo_clock_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *names[8] = {
+ static const char * const names[8] = {
"Internal", "Word", "Super", "S/PDIF", "ADAT", "ESync",
"ESync96", "MTC"
};
struct echoaudio *chip;
chip = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->value.enumerated.items = chip->num_clock_sources;
- uinfo->count = 1;
- if (uinfo->value.enumerated.item >= chip->num_clock_sources)
- uinfo->value.enumerated.item = chip->num_clock_sources - 1;
- strcpy(uinfo->value.enumerated.name, names[
- chip->clock_source_list[uinfo->value.enumerated.item]]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, chip->num_clock_sources, names);
}
static int snd_echo_clock_source_get(struct snd_kcontrol *kcontrol,
@@ -1610,21 +1552,21 @@ static int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol,
return -EINVAL;
dclock = chip->clock_source_list[eclock];
if (chip->input_clock != dclock) {
- mutex_lock(&chip->mode_mutex);
- spin_lock_irq(&chip->lock);
- if ((changed = set_input_clock(chip, dclock)) == 0)
+ guard(mutex)(&chip->mode_mutex);
+ guard(spinlock_irq)(&chip->lock);
+ changed = set_input_clock(chip, dclock);
+ if (!changed)
changed = 1; /* no errors */
- spin_unlock_irq(&chip->lock);
- mutex_unlock(&chip->mode_mutex);
}
if (changed < 0)
- DE_ACT(("seticlk val%d err 0x%x\n", dclock, changed));
+ dev_dbg(chip->card->dev,
+ "seticlk val%d err 0x%x\n", dclock, changed);
return changed;
}
-static struct snd_kcontrol_new snd_echo_clock_source_switch __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_clock_source_switch = {
.name = "Sample Clock Source",
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.info = snd_echo_clock_source_info,
@@ -1658,16 +1600,15 @@ static int snd_echo_phantom_power_put(struct snd_kcontrol *kcontrol,
power = !!ucontrol->value.integer.value[0];
if (chip->phantom_power != power) {
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
changed = set_phantom_power(chip, power);
- spin_unlock_irq(&chip->lock);
if (changed == 0)
changed = 1; /* no errors */
}
return changed;
}
-static struct snd_kcontrol_new snd_echo_phantom_power_switch __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_phantom_power_switch = {
.name = "Phantom power Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_phantom_power_info,
@@ -1701,16 +1642,15 @@ static int snd_echo_automute_put(struct snd_kcontrol *kcontrol,
automute = !!ucontrol->value.integer.value[0];
if (chip->digital_in_automute != automute) {
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
changed = set_input_auto_mute(chip, automute);
- spin_unlock_irq(&chip->lock);
if (changed == 0)
changed = 1; /* no errors */
}
return changed;
}
-static struct snd_kcontrol_new snd_echo_automute_switch __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_automute_switch = {
.name = "Digital Capture Switch (automute)",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_automute_info,
@@ -1731,13 +1671,12 @@ static int snd_echo_vumeters_switch_put(struct snd_kcontrol *kcontrol,
struct echoaudio *chip;
chip = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
set_meters_on(chip, ucontrol->value.integer.value[0]);
- spin_unlock_irq(&chip->lock);
return 1;
}
-static struct snd_kcontrol_new snd_echo_vumeters_switch __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_vumeters_switch = {
.name = "VU-meters Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.access = SNDRV_CTL_ELEM_ACCESS_WRITE,
@@ -1751,20 +1690,10 @@ static struct snd_kcontrol_new snd_echo_vumeters_switch __devinitdata = {
static int snd_echo_vumeters_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- struct echoaudio *chip;
-
- chip = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 96;
uinfo->value.integer.min = ECHOGAIN_MINOUT;
uinfo->value.integer.max = 0;
-#ifdef ECHOCARD_HAS_VMIXER
- uinfo->dimen.d[0] = 3; /* Out, In, Virt */
-#else
- uinfo->dimen.d[0] = 2; /* Out, In */
-#endif
- uinfo->dimen.d[1] = 16; /* 16 channels */
- uinfo->dimen.d[2] = 2; /* 0=level, 1=peak */
return 0;
}
@@ -1778,7 +1707,7 @@ static int snd_echo_vumeters_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_echo_vumeters __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_vumeters = {
.name = "VU-meters",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READ |
@@ -1795,9 +1724,6 @@ static struct snd_kcontrol_new snd_echo_vumeters __devinitdata = {
static int snd_echo_channels_info_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- struct echoaudio *chip;
-
- chip = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 6;
uinfo->value.integer.min = 0;
@@ -1834,7 +1760,7 @@ static int snd_echo_channels_info_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_echo_channels_info __devinitdata = {
+static const struct snd_kcontrol_new snd_echo_channels_info = {
.name = "Channels info",
.iface = SNDRV_CTL_ELEM_IFACE_HWDEP,
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
@@ -1846,14 +1772,43 @@ static struct snd_kcontrol_new snd_echo_channels_info __devinitdata = {
/******************************************************************************
- IRQ Handler
+ IRQ Handling
******************************************************************************/
+/* Check if a period has elapsed since last interrupt
+ *
+ * Don't make any updates to state; PCM core handles this with the
+ * correct locks.
+ *
+ * \return true if a period has elapsed, otherwise false
+ */
+static bool period_has_elapsed(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct audiopipe *pipe = runtime->private_data;
+ u32 counter, step;
+ size_t period_bytes;
+
+ if (pipe->state != PIPE_STATE_STARTED)
+ return false;
+
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+
+ counter = le32_to_cpu(*pipe->dma_counter); /* presumed atomic */
+
+ step = counter - pipe->last_period; /* handles wrapping */
+ step -= step % period_bytes; /* acknowledge whole periods only */
+
+ if (step == 0)
+ return false; /* haven't advanced a whole period yet */
+
+ pipe->last_period += step; /* used exclusively by us */
+ return true;
+}
static irqreturn_t snd_echo_interrupt(int irq, void *dev_id)
{
struct echoaudio *chip = dev_id;
- struct snd_pcm_substream *substream;
- int period, ss, st;
+ int ss, st;
spin_lock(&chip->lock);
st = service_irq(chip);
@@ -1864,17 +1819,13 @@ static irqreturn_t snd_echo_interrupt(int irq, void *dev_id)
/* The hardware doesn't tell us which substream caused the irq,
thus we have to check all running substreams. */
for (ss = 0; ss < DSP_MAXPIPES; ss++) {
+ struct snd_pcm_substream *substream;
+
substream = chip->substream[ss];
- if (substream && ((struct audiopipe *)substream->runtime->
- private_data)->state == PIPE_STATE_STARTED) {
- period = pcm_pointer(substream) /
- substream->runtime->period_size;
- if (period != chip->last_period[ss]) {
- chip->last_period[ss] = period;
- spin_unlock(&chip->lock);
- snd_pcm_period_elapsed(substream);
- spin_lock(&chip->lock);
- }
+ if (substream && period_has_elapsed(substream)) {
+ spin_unlock(&chip->lock);
+ snd_pcm_period_elapsed(substream);
+ spin_lock(&chip->lock);
}
}
spin_unlock(&chip->lock);
@@ -1882,7 +1833,7 @@ static irqreturn_t snd_echo_interrupt(int irq, void *dev_id)
#ifdef ECHOCARD_HAS_MIDI
if (st > 0 && chip->midi_in) {
snd_rawmidi_receive(chip->midi_in, chip->midi_buffer, st);
- DE_MID(("rawmidi_iread=%d\n", st));
+ dev_dbg(chip->card->dev, "rawmidi_iread=%d\n", st);
}
#endif
return IRQ_HANDLED;
@@ -1895,157 +1846,102 @@ static irqreturn_t snd_echo_interrupt(int irq, void *dev_id)
Module construction / destruction
******************************************************************************/
-static int snd_echo_free(struct echoaudio *chip)
+static void snd_echo_free(struct snd_card *card)
{
- DE_INIT(("Stop DSP...\n"));
+ struct echoaudio *chip = card->private_data;
+
if (chip->comm_page)
rest_in_peace(chip);
- DE_INIT(("Stopped.\n"));
if (chip->irq >= 0)
free_irq(chip->irq, chip);
- if (chip->comm_page)
- snd_dma_free_pages(&chip->commpage_dma_buf);
-
- if (chip->dsp_registers)
- iounmap(chip->dsp_registers);
-
- if (chip->iores)
- release_and_free_resource(chip->iores);
-
- DE_INIT(("MMIO freed.\n"));
-
- pci_disable_device(chip->pci);
-
/* release chip data */
free_firmware_cache(chip);
- kfree(chip);
- DE_INIT(("Chip freed.\n"));
- return 0;
}
-
-
-static int snd_echo_dev_free(struct snd_device *device)
-{
- struct echoaudio *chip = device->device_data;
-
- DE_INIT(("snd_echo_dev_free()...\n"));
- return snd_echo_free(chip);
-}
-
-
-
/* <--snd_echo_probe() */
-static __devinit int snd_echo_create(struct snd_card *card,
- struct pci_dev *pci,
- struct echoaudio **rchip)
+static int snd_echo_create(struct snd_card *card,
+ struct pci_dev *pci)
{
- struct echoaudio *chip;
+ struct echoaudio *chip = card->private_data;
int err;
size_t sz;
- static struct snd_device_ops ops = {
- .dev_free = snd_echo_dev_free,
- };
-
- *rchip = NULL;
pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0xC0);
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
pci_set_master(pci);
/* Allocate chip if needed */
- if (!*rchip) {
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (!chip) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
- DE_INIT(("chip=%p\n", chip));
- spin_lock_init(&chip->lock);
- chip->card = card;
- chip->pci = pci;
- chip->irq = -1;
- atomic_set(&chip->opencount, 0);
- mutex_init(&chip->mode_mutex);
- chip->can_set_rate = 1;
- } else {
- /* If this was called from the resume function, chip is
- * already allocated and it contains current card settings.
- */
- chip = *rchip;
- }
+ spin_lock_init(&chip->lock);
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+ chip->opencount = 0;
+ mutex_init(&chip->mode_mutex);
+ chip->can_set_rate = 1;
/* PCI resource allocation */
+ err = pcim_request_all_regions(pci, ECHOCARD_NAME);
+ if (err < 0)
+ return err;
+
chip->dsp_registers_phys = pci_resource_start(pci, 0);
sz = pci_resource_len(pci, 0);
if (sz > PAGE_SIZE)
sz = PAGE_SIZE; /* We map only the required part */
- if ((chip->iores = request_mem_region(chip->dsp_registers_phys, sz,
- ECHOCARD_NAME)) == NULL) {
- snd_echo_free(chip);
- snd_printk(KERN_ERR "cannot get memory region\n");
- return -EBUSY;
+ chip->dsp_registers = devm_ioremap(&pci->dev, chip->dsp_registers_phys, sz);
+ if (!chip->dsp_registers) {
+ dev_err(chip->card->dev, "ioremap failed\n");
+ return -ENOMEM;
}
- chip->dsp_registers = (volatile u32 __iomem *)
- ioremap_nocache(chip->dsp_registers_phys, sz);
if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED,
- ECHOCARD_NAME, chip)) {
- snd_echo_free(chip);
- snd_printk(KERN_ERR "cannot grab irq\n");
+ KBUILD_MODNAME, chip)) {
+ dev_err(chip->card->dev, "cannot grab irq\n");
return -EBUSY;
}
chip->irq = pci->irq;
- DE_INIT(("pci=%p irq=%d subdev=%04x Init hardware...\n",
- chip->pci, chip->irq, chip->pci->subsystem_device));
+ card->sync_irq = chip->irq;
+ dev_dbg(card->dev, "pci=%p irq=%d subdev=%04x Init hardware...\n",
+ chip->pci, chip->irq, chip->pci->subsystem_device);
+
+ card->private_free = snd_echo_free;
/* Create the DSP comm page - this is the area of memory used for most
of the communication with the DSP, which accesses it via bus mastering */
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
- sizeof(struct comm_page),
- &chip->commpage_dma_buf) < 0) {
- snd_echo_free(chip);
- snd_printk(KERN_ERR "cannot allocate the comm page\n");
+ chip->commpage_dma_buf =
+ snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV,
+ sizeof(struct comm_page));
+ if (!chip->commpage_dma_buf)
return -ENOMEM;
- }
- chip->comm_page_phys = chip->commpage_dma_buf.addr;
- chip->comm_page = (struct comm_page *)chip->commpage_dma_buf.area;
+ chip->comm_page_phys = chip->commpage_dma_buf->addr;
+ chip->comm_page = (struct comm_page *)chip->commpage_dma_buf->area;
err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
if (err >= 0)
err = set_mixer_defaults(chip);
if (err < 0) {
- DE_INIT(("init_hw err=%d\n", err));
- snd_echo_free(chip);
+ dev_err(card->dev, "init_hw err=%d\n", err);
return err;
}
- DE_INIT(("Card init OK\n"));
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_echo_free(chip);
- return err;
- }
- *rchip = chip;
- /* Init done ! */
return 0;
}
-
-
/* constructor */
-static int __devinit snd_echo_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_echo_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
struct echoaudio *chip;
char *dsp;
- int i, err;
+ int err;
if (dev >= SNDRV_CARDS)
return -ENODEV;
@@ -2054,22 +1950,18 @@ static int __devinit snd_echo_probe(struct pci_dev *pci,
return -ENOENT;
}
- DE_INIT(("Echoaudio driver starting...\n"));
- i = 0;
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- snd_card_set_dev(card, &pci->dev);
-
- chip = NULL; /* Tells snd_echo_create to allocate chip */
- if ((err = snd_echo_create(card, pci, &chip)) < 0) {
- snd_card_free(card);
+ err = snd_echo_create(card, pci);
+ if (err < 0)
return err;
- }
- strcpy(card->driver, "Echo_" ECHOCARD_NAME);
- strcpy(card->shortname, chip->card_name);
+ strscpy(card->driver, "Echo_" ECHOCARD_NAME);
+ strscpy(card->shortname, chip->card_name);
dsp = "56301";
if (pci_id->device == 0x3410)
@@ -2079,17 +1971,17 @@ static int __devinit snd_echo_probe(struct pci_dev *pci,
card->shortname, pci_id->subdevice & 0x000f, dsp,
chip->dsp_registers_phys, chip->irq);
- if ((err = snd_echo_new_pcm(chip)) < 0) {
- snd_printk(KERN_ERR "new pcm error %d\n", err);
- snd_card_free(card);
+ err = snd_echo_new_pcm(chip);
+ if (err < 0) {
+ dev_err(chip->card->dev, "new pcm error %d\n", err);
return err;
}
#ifdef ECHOCARD_HAS_MIDI
if (chip->has_midi) { /* Some Mia's do not have midi */
- if ((err = snd_echo_midi_create(card, chip)) < 0) {
- snd_printk(KERN_ERR "new midi error %d\n", err);
- snd_card_free(card);
+ err = snd_echo_midi_create(card, chip);
+ if (err < 0) {
+ dev_err(chip->card->dev, "new midi error %d\n", err);
return err;
}
}
@@ -2097,172 +1989,170 @@ static int __devinit snd_echo_probe(struct pci_dev *pci,
#ifdef ECHOCARD_HAS_VMIXER
snd_echo_vmixer.count = num_pipes_out(chip) * num_busses_out(chip);
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vmixer, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vmixer, chip));
+ if (err < 0)
+ return err;
#ifdef ECHOCARD_HAS_LINE_OUT_GAIN
err = snd_ctl_add(chip->card,
snd_ctl_new1(&snd_echo_line_output_gain, chip));
if (err < 0)
- goto ctl_error;
+ return err;
#endif
#else /* ECHOCARD_HAS_VMIXER */
err = snd_ctl_add(chip->card,
snd_ctl_new1(&snd_echo_pcm_output_gain, chip));
if (err < 0)
- goto ctl_error;
+ return err;
#endif /* ECHOCARD_HAS_VMIXER */
#ifdef ECHOCARD_HAS_INPUT_GAIN
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_input_gain, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_input_gain, chip));
+ if (err < 0)
+ return err;
#endif
#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
- if (!chip->hasnt_input_nominal_level)
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_intput_nominal_level, chip))) < 0)
- goto ctl_error;
+ if (!chip->hasnt_input_nominal_level) {
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_intput_nominal_level, chip));
+ if (err < 0)
+ return err;
+ }
#endif
#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_output_nominal_level, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_output_nominal_level, chip));
+ if (err < 0)
+ return err;
#endif
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters_switch, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters_switch, chip));
+ if (err < 0)
+ return err;
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters, chip));
+ if (err < 0)
+ return err;
#ifdef ECHOCARD_HAS_MONITOR
snd_echo_monitor_mixer.count = num_busses_in(chip) * num_busses_out(chip);
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_monitor_mixer, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_monitor_mixer, chip));
+ if (err < 0)
+ return err;
#endif
#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_automute_switch, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_automute_switch, chip));
+ if (err < 0)
+ return err;
#endif
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_channels_info, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_channels_info, chip));
+ if (err < 0)
+ return err;
#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH
/* Creates a list of available digital modes */
chip->num_digital_modes = 0;
- for (i = 0; i < 6; i++)
+ for (int i = 0; i < 6; i++)
if (chip->digital_modes & (1 << i))
chip->digital_mode_list[chip->num_digital_modes++] = i;
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_digital_mode_switch, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_digital_mode_switch, chip));
+ if (err < 0)
+ return err;
#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */
#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK
/* Creates a list of available clock sources */
chip->num_clock_sources = 0;
- for (i = 0; i < 10; i++)
+ for (int i = 0; i < 10; i++)
if (chip->input_clock_types & (1 << i))
chip->clock_source_list[chip->num_clock_sources++] = i;
if (chip->num_clock_sources > 1) {
chip->clock_src_ctl = snd_ctl_new1(&snd_echo_clock_source_switch, chip);
- if ((err = snd_ctl_add(chip->card, chip->clock_src_ctl)) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, chip->clock_src_ctl);
+ if (err < 0)
+ return err;
}
#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */
#ifdef ECHOCARD_HAS_DIGITAL_IO
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_spdif_mode_switch, chip))) < 0)
- goto ctl_error;
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_spdif_mode_switch, chip));
+ if (err < 0)
+ return err;
#endif
#ifdef ECHOCARD_HAS_PHANTOM_POWER
- if (chip->has_phantom_power)
- if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_phantom_power_switch, chip))) < 0)
- goto ctl_error;
+ if (chip->has_phantom_power) {
+ err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_phantom_power_switch, chip));
+ if (err < 0)
+ return err;
+ }
#endif
err = snd_card_register(card);
if (err < 0)
- goto ctl_error;
- snd_printk(KERN_INFO "Card registered: %s\n", card->longname);
+ return err;
+ dev_info(card->dev, "Card registered: %s\n", card->longname);
pci_set_drvdata(pci, chip);
dev++;
return 0;
-
-ctl_error:
- snd_printk(KERN_ERR "new control error %d\n", err);
- snd_card_free(card);
- return err;
}
+static int snd_echo_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ return snd_card_free_on_error(&pci->dev, __snd_echo_probe(pci, pci_id));
+}
-#if defined(CONFIG_PM)
-
-static int snd_echo_suspend(struct pci_dev *pci, pm_message_t state)
+static int snd_echo_suspend(struct device *dev)
{
- struct echoaudio *chip = pci_get_drvdata(pci);
-
- DE_INIT(("suspend start\n"));
- snd_pcm_suspend_all(chip->analog_pcm);
- snd_pcm_suspend_all(chip->digital_pcm);
+ struct echoaudio *chip = dev_get_drvdata(dev);
#ifdef ECHOCARD_HAS_MIDI
/* This call can sleep */
if (chip->midi_out)
snd_echo_midi_output_trigger(chip->midi_out, 0);
#endif
- spin_lock_irq(&chip->lock);
- if (wait_handshake(chip)) {
- spin_unlock_irq(&chip->lock);
- return -EIO;
+ scoped_guard(spinlock_irq, &chip->lock) {
+ if (wait_handshake(chip))
+ return -EIO;
+ clear_handshake(chip);
+ if (send_vector(chip, DSP_VC_GO_COMATOSE) < 0)
+ return -EIO;
}
- clear_handshake(chip);
- if (send_vector(chip, DSP_VC_GO_COMATOSE) < 0) {
- spin_unlock_irq(&chip->lock);
- return -EIO;
- }
- spin_unlock_irq(&chip->lock);
chip->dsp_code = NULL;
free_irq(chip->irq, chip);
chip->irq = -1;
- pci_save_state(pci);
- pci_disable_device(pci);
-
- DE_INIT(("suspend done\n"));
+ chip->card->sync_irq = -1;
return 0;
}
-static int snd_echo_resume(struct pci_dev *pci)
+static int snd_echo_resume(struct device *dev)
{
- struct echoaudio *chip = pci_get_drvdata(pci);
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct echoaudio *chip = dev_get_drvdata(dev);
struct comm_page *commpage, *commpage_bak;
u32 pipe_alloc_mask;
int err;
- DE_INIT(("resume start\n"));
- pci_restore_state(pci);
- commpage_bak = kmalloc(sizeof(struct echoaudio), GFP_KERNEL);
+ commpage = chip->comm_page;
+ commpage_bak = kmemdup(commpage, sizeof(*commpage), GFP_KERNEL);
if (commpage_bak == NULL)
return -ENOMEM;
- commpage = chip->comm_page;
- memcpy(commpage_bak, commpage, sizeof(struct comm_page));
err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
if (err < 0) {
kfree(commpage_bak);
- DE_INIT(("resume init_hw err=%d\n", err));
- snd_echo_free(chip);
+ dev_err(dev, "resume init_hw err=%d\n", err);
return err;
}
- DE_INIT(("resume init OK\n"));
/* Temporarily set chip->pipe_alloc_mask=0 otherwise
* restore_dsp_settings() fails.
@@ -2275,7 +2165,6 @@ static int snd_echo_resume(struct pci_dev *pci)
kfree(commpage_bak);
return err;
}
- DE_INIT(("resume restore OK\n"));
memcpy(&commpage->audio_format, &commpage_bak->audio_format,
sizeof(commpage->audio_format));
@@ -2286,73 +2175,38 @@ static int snd_echo_resume(struct pci_dev *pci)
kfree(commpage_bak);
if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED,
- ECHOCARD_NAME, chip)) {
- snd_echo_free(chip);
- snd_printk(KERN_ERR "cannot grab irq\n");
+ KBUILD_MODNAME, chip)) {
+ dev_err(chip->card->dev, "cannot grab irq\n");
return -EBUSY;
}
chip->irq = pci->irq;
- DE_INIT(("resume irq=%d\n", chip->irq));
+ chip->card->sync_irq = chip->irq;
+ dev_dbg(dev, "resume irq=%d\n", chip->irq);
#ifdef ECHOCARD_HAS_MIDI
if (chip->midi_input_enabled)
- enable_midi_input(chip, TRUE);
+ enable_midi_input(chip, true);
if (chip->midi_out)
snd_echo_midi_output_trigger(chip->midi_out, 1);
#endif
- DE_INIT(("resume done\n"));
return 0;
}
-#endif /* CONFIG_PM */
-
-
-
-static void __devexit snd_echo_remove(struct pci_dev *pci)
-{
- struct echoaudio *chip;
-
- chip = pci_get_drvdata(pci);
- if (chip)
- snd_card_free(chip->card);
- pci_set_drvdata(pci, NULL);
-}
-
-
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_echo_pm, snd_echo_suspend, snd_echo_resume);
/******************************************************************************
Everything starts and ends here
******************************************************************************/
/* pci_driver definition */
-static struct pci_driver driver = {
- .name = "Echoaudio " ECHOCARD_NAME,
+static struct pci_driver echo_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_echo_ids,
.probe = snd_echo_probe,
- .remove = __devexit_p(snd_echo_remove),
-#ifdef CONFIG_PM
- .suspend = snd_echo_suspend,
- .resume = snd_echo_resume,
-#endif /* CONFIG_PM */
+ .driver = {
+ .pm = &snd_echo_pm,
+ },
};
-
-
-/* initialization of the module */
-static int __init alsa_card_echo_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-
-
-/* clean up the module */
-static void __exit alsa_card_echo_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-
-module_init(alsa_card_echo_init)
-module_exit(alsa_card_echo_exit)
+module_pci_driver(echo_driver);
diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h
index 1df974dcb5f4..511f2fcc0fb9 100644
--- a/sound/pci/echoaudio/echoaudio.h
+++ b/sound/pci/echoaudio/echoaudio.h
@@ -153,9 +153,6 @@
#define _ECHOAUDIO_H_
-#define TRUE 1
-#define FALSE 0
-
#include "echoaudio_dsp.h"
@@ -295,41 +292,18 @@
#define PIPE_STATE_PENDING 3 /* Pipe has pending start */
-/* Debug initialization */
-#ifdef CONFIG_SND_DEBUG
-#define DE_INIT(x) snd_printk x
-#else
-#define DE_INIT(x)
-#endif
-
-/* Debug hw_params callbacks */
-#ifdef CONFIG_SND_DEBUG
-#define DE_HWP(x) snd_printk x
-#else
-#define DE_HWP(x)
-#endif
-
-/* Debug normal activity (open, start, stop...) */
-#ifdef CONFIG_SND_DEBUG
-#define DE_ACT(x) snd_printk x
-#else
-#define DE_ACT(x)
-#endif
-
-/* Debug midi activity */
-#ifdef CONFIG_SND_DEBUG
-#define DE_MID(x) snd_printk x
-#else
-#define DE_MID(x)
-#endif
-
struct audiopipe {
- volatile u32 *dma_counter; /* Commpage register that contains
+ volatile __le32 *dma_counter; /* Commpage register that contains
* the current dma position
* (lower 32 bits only)
*/
- u32 last_counter; /* The last position, which is used
+ u32 last_period; /* Counter position last time a
+ * period elapsed
+ */
+ u32 last_counter; /* Used exclusively by pcm_pointer
+ * under PCM core locks.
+ * The last position, which is used
* to compute...
*/
u32 position; /* ...the number of bytes tranferred
@@ -363,11 +337,10 @@ struct audioformat {
struct echoaudio {
spinlock_t lock;
struct snd_pcm_substream *substream[DSP_MAXPIPES];
- int last_period[DSP_MAXPIPES];
struct mutex mode_mutex;
u16 num_digital_modes, digital_mode_list[6];
u16 num_clock_sources, clock_source_list[10];
- atomic_t opencount;
+ unsigned int opencount; /* protected by mode_mutex */
struct snd_kcontrol *clock_src_ctl;
struct snd_pcm *analog_pcm, *digital_pcm;
struct snd_card *card;
@@ -375,7 +348,7 @@ struct echoaudio {
struct pci_dev *pci;
unsigned long dsp_registers_phys;
struct resource *iores;
- struct snd_dma_buffer commpage_dma_buf;
+ struct snd_dma_buffer *commpage_dma_buf;
int irq;
#ifdef ECHOCARD_HAS_MIDI
struct snd_rawmidi *rmidi;
@@ -384,8 +357,8 @@ struct echoaudio {
struct timer_list timer;
char tinuse; /* Timer in use */
char midi_full; /* MIDI output buffer is full */
- char can_set_rate;
- char rate_set;
+ char can_set_rate; /* protected by mode_mutex */
+ char rate_set; /* protected by mode_mutex */
/* This stuff is used mainly by the lowlevel code */
struct comm_page *comm_page; /* Virtual address of the memory
@@ -406,8 +379,8 @@ struct echoaudio {
*/
u8 output_clock; /* Layla20 only */
char meters_enabled; /* VU-meters status */
- char asic_loaded; /* Set TRUE when ASIC loaded */
- char bad_board; /* Set TRUE if DSP won't load */
+ char asic_loaded; /* Set true when ASIC loaded */
+ char bad_board; /* Set true if DSP won't load */
char professional_spdif; /* 0 = consumer; 1 = professional */
char non_audio_spdif; /* 3G - only */
char digital_in_automute; /* Gina24, Layla24, Mona - only */
@@ -446,12 +419,10 @@ struct echoaudio {
short asic_code; /* Current ASIC code */
u32 comm_page_phys; /* Physical address of the
* memory seen by DSP */
- volatile u32 __iomem *dsp_registers; /* DSP's register base */
+ u32 __iomem *dsp_registers; /* DSP's register base */
u32 active_mask; /* Chs. active mask or
* punks out */
-#ifdef CONFIG_PM
const struct firmware *fw_cache[8]; /* Cached firmwares */
-#endif
#ifdef ECHOCARD_HAS_MIDI
u16 mtc_state; /* State for MIDI input parsing state machine */
@@ -468,15 +439,16 @@ static int wait_handshake(struct echoaudio *chip);
static int send_vector(struct echoaudio *chip, u32 command);
static int get_firmware(const struct firmware **fw_entry,
struct echoaudio *chip, const short fw_index);
-static void free_firmware(const struct firmware *fw_entry);
+static void free_firmware(const struct firmware *fw_entry,
+ struct echoaudio *chip);
#ifdef ECHOCARD_HAS_MIDI
static int enable_midi_input(struct echoaudio *chip, char enable);
static void snd_echo_midi_output_trigger(
struct snd_rawmidi_substream *substream, int up);
static int midi_service_irq(struct echoaudio *chip);
-static int __devinit snd_echo_midi_create(struct snd_card *card,
- struct echoaudio *chip);
+static int snd_echo_midi_create(struct snd_card *card,
+ struct echoaudio *chip);
#endif
@@ -589,10 +561,4 @@ static inline int monitor_index(const struct echoaudio *chip, int out, int in)
return out * num_busses_in(chip) + in;
}
-
-#ifndef pci_device
-#define pci_device(chip) (&chip->pci->dev)
-#endif
-
-
#endif /* _ECHOAUDIO_H_ */
diff --git a/sound/pci/echoaudio/echoaudio_3g.c b/sound/pci/echoaudio/echoaudio_3g.c
index 658db44ef746..c9ee98ea3c71 100644
--- a/sound/pci/echoaudio/echoaudio_3g.c
+++ b/sound/pci/echoaudio/echoaudio_3g.c
@@ -41,7 +41,7 @@ static int check_asic_status(struct echoaudio *chip)
return -EIO;
chip->comm_page->ext_box_status = cpu_to_le32(E3G_ASIC_NOT_LOADED);
- chip->asic_loaded = FALSE;
+ chip->asic_loaded = false;
clear_handshake(chip);
send_vector(chip, DSP_VC_TEST_ASIC);
@@ -51,11 +51,11 @@ static int check_asic_status(struct echoaudio *chip)
}
box_status = le32_to_cpu(chip->comm_page->ext_box_status);
- DE_INIT(("box_status=%x\n", box_status));
+ dev_dbg(chip->card->dev, "box_status=%x\n", box_status);
if (box_status == E3G_ASIC_NOT_LOADED)
return -ENODEV;
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
return box_status & E3G_BOX_TYPE_MASK;
}
@@ -73,23 +73,26 @@ register. write_control_reg sends the new control register value to the DSP. */
static int write_control_reg(struct echoaudio *chip, u32 ctl, u32 frq,
char force)
{
+ __le32 ctl_reg, frq_reg;
+
if (wait_handshake(chip))
return -EIO;
- DE_ACT(("WriteControlReg: Setting 0x%x, 0x%x\n", ctl, frq));
+ dev_dbg(chip->card->dev,
+ "WriteControlReg: Setting 0x%x, 0x%x\n", ctl, frq);
- ctl = cpu_to_le32(ctl);
- frq = cpu_to_le32(frq);
+ ctl_reg = cpu_to_le32(ctl);
+ frq_reg = cpu_to_le32(frq);
- if (ctl != chip->comm_page->control_register ||
- frq != chip->comm_page->e3g_frq_register || force) {
- chip->comm_page->e3g_frq_register = frq;
- chip->comm_page->control_register = ctl;
+ if (ctl_reg != chip->comm_page->control_register ||
+ frq_reg != chip->comm_page->e3g_frq_register || force) {
+ chip->comm_page->e3g_frq_register = frq_reg;
+ chip->comm_page->control_register = ctl_reg;
clear_handshake(chip);
return send_vector(chip, DSP_VC_WRITE_CONTROL_REG);
}
- DE_ACT(("WriteControlReg: not written, no change\n"));
+ dev_dbg(chip->card->dev, "WriteControlReg: not written, no change\n");
return 0;
}
@@ -116,7 +119,7 @@ static int set_digital_mode(struct echoaudio *chip, u8 mode)
* updated by the DSP comm object. */
if (err >= 0 && previous_mode != mode &&
(previous_mode == DIGITAL_MODE_ADAT || mode == DIGITAL_MODE_ADAT)) {
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
for (o = 0; o < num_busses_out(chip); o++)
for (i = 0; i < num_busses_in(chip); i++)
set_monitor_gain(chip, o, i,
@@ -131,7 +134,6 @@ static int set_digital_mode(struct echoaudio *chip, u8 mode)
for (o = 0; o < num_busses_out(chip); o++)
set_output_gain(chip, o, chip->output_gain[o]);
update_output_line_level(chip);
- spin_unlock_irq(&chip->lock);
}
return err;
@@ -242,7 +244,7 @@ static int load_asic(struct echoaudio *chip)
* 48 kHz, internal clock, S/PDIF RCA mode */
if (box_type >= 0) {
err = write_control_reg(chip, E3G_48KHZ,
- E3G_FREQ_REG_DEFAULT, TRUE);
+ E3G_FREQ_REG_DEFAULT, true);
if (err < 0)
return err;
}
@@ -258,8 +260,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
/* Only set the clock for internal mode. */
if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
- DE_ACT(("set_sample_rate: Cannot set sample rate - "
- "clock not set to CLK_CLOCKININTERNAL\n"));
+ dev_warn(chip->card->dev,
+ "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n");
/* Save the rate anyhow */
chip->comm_page->sample_rate = cpu_to_le32(rate);
chip->sample_rate = rate;
@@ -271,7 +273,6 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
chip->digital_mode == DIGITAL_MODE_ADAT))
return -EINVAL;
- clock = 0;
control_reg = le32_to_cpu(chip->comm_page->control_register);
control_reg &= E3G_CLOCK_CLEAR_MASK;
@@ -313,7 +314,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */
chip->sample_rate = rate;
- DE_ACT(("SetSampleRate: %d clock %x\n", rate, control_reg));
+ dev_dbg(chip->card->dev,
+ "SetSampleRate: %d clock %x\n", rate, control_reg);
/* Tell the DSP about it - DSP reads both control reg & freq reg */
return write_control_reg(chip, control_reg, frq_reg, 0);
@@ -326,7 +328,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
{
u32 control_reg, clocks_from_dsp;
- DE_ACT(("set_input_clock:\n"));
/* Mask off the clock select bits */
control_reg = le32_to_cpu(chip->comm_page->control_register) &
@@ -335,13 +336,11 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
switch (clock) {
case ECHO_CLOCK_INTERNAL:
- DE_ACT(("Set Echo3G clock to INTERNAL\n"));
chip->input_clock = ECHO_CLOCK_INTERNAL;
return set_sample_rate(chip, chip->sample_rate);
case ECHO_CLOCK_SPDIF:
if (chip->digital_mode == DIGITAL_MODE_ADAT)
return -EAGAIN;
- DE_ACT(("Set Echo3G clock to SPDIF\n"));
control_reg |= E3G_SPDIF_CLOCK;
if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_SPDIF96)
control_reg |= E3G_DOUBLE_SPEED_MODE;
@@ -351,12 +350,10 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
case ECHO_CLOCK_ADAT:
if (chip->digital_mode != DIGITAL_MODE_ADAT)
return -EAGAIN;
- DE_ACT(("Set Echo3G clock to ADAT\n"));
control_reg |= E3G_ADAT_CLOCK;
control_reg &= ~E3G_DOUBLE_SPEED_MODE;
break;
case ECHO_CLOCK_WORD:
- DE_ACT(("Set Echo3G clock to WORD\n"));
control_reg |= E3G_WORD_CLOCK;
if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_WORD96)
control_reg |= E3G_DOUBLE_SPEED_MODE;
@@ -364,7 +361,8 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
control_reg &= ~E3G_DOUBLE_SPEED_MODE;
break;
default:
- DE_ACT(("Input clock 0x%x not supported for Echo3G\n", clock));
+ dev_err(chip->card->dev,
+ "Input clock 0x%x not supported for Echo3G\n", clock);
return -EINVAL;
}
@@ -380,23 +378,24 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
int err, incompatible_clock;
/* Set clock to "internal" if it's not compatible with the new mode */
- incompatible_clock = FALSE;
+ incompatible_clock = false;
switch (mode) {
case DIGITAL_MODE_SPDIF_OPTICAL:
case DIGITAL_MODE_SPDIF_RCA:
if (chip->input_clock == ECHO_CLOCK_ADAT)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
break;
case DIGITAL_MODE_ADAT:
if (chip->input_clock == ECHO_CLOCK_SPDIF)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
break;
default:
- DE_ACT(("Digital mode not supported: %d\n", mode));
+ dev_err(chip->card->dev,
+ "Digital mode not supported: %d\n", mode);
return -EINVAL;
}
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
if (incompatible_clock) {
chip->sample_rate = 48000;
@@ -422,11 +421,10 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
}
err = write_control_reg(chip, control_reg, get_frq_reg(chip), 1);
- spin_unlock_irq(&chip->lock);
if (err < 0)
return err;
chip->digital_mode = mode;
- DE_ACT(("set_digital_mode(%d)\n", chip->digital_mode));
+ dev_dbg(chip->card->dev, "set_digital_mode(%d)\n", chip->digital_mode);
return incompatible_clock;
}
diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c
index 64417a733220..2a40091d472c 100644
--- a/sound/pci/echoaudio/echoaudio_dsp.c
+++ b/sound/pci/echoaudio/echoaudio_dsp.c
@@ -53,7 +53,7 @@ static int wait_handshake(struct echoaudio *chip)
udelay(1);
}
- snd_printk(KERN_ERR "wait_handshake(): Timeout waiting for DSP\n");
+ dev_err(chip->card->dev, "wait_handshake(): Timeout waiting for DSP\n");
return -EBUSY;
}
@@ -80,7 +80,7 @@ static int send_vector(struct echoaudio *chip, u32 command)
udelay(1);
}
- DE_ACT((KERN_ERR "timeout on send_vector\n"));
+ dev_err(chip->card->dev, "timeout on send_vector\n");
return -EBUSY;
}
@@ -103,8 +103,8 @@ static int write_dsp(struct echoaudio *chip, u32 data)
cond_resched();
}
- chip->bad_board = TRUE; /* Set TRUE until DSP re-loaded */
- DE_ACT((KERN_ERR "write_dsp: Set bad_board to TRUE\n"));
+ chip->bad_board = true; /* Set true until DSP re-loaded */
+ dev_dbg(chip->card->dev, "write_dsp: Set bad_board to true\n");
return -EIO;
}
@@ -126,8 +126,8 @@ static int read_dsp(struct echoaudio *chip, u32 *data)
cond_resched();
}
- chip->bad_board = TRUE; /* Set TRUE until DSP re-loaded */
- DE_INIT((KERN_ERR "read_dsp: Set bad_board to TRUE\n"));
+ chip->bad_board = true; /* Set true until DSP re-loaded */
+ dev_err(chip->card->dev, "read_dsp: Set bad_board to true\n");
return -EIO;
}
@@ -149,12 +149,14 @@ static int read_sn(struct echoaudio *chip)
for (i = 0; i < 5; i++) {
if (read_dsp(chip, &sn[i])) {
- snd_printk(KERN_ERR "Failed to read serial number\n");
+ dev_err(chip->card->dev,
+ "Failed to read serial number\n");
return -EIO;
}
}
- DE_INIT(("Read serial number %08x %08x %08x %08x %08x\n",
- sn[0], sn[1], sn[2], sn[3], sn[4]));
+ dev_dbg(chip->card->dev,
+ "Read serial number %08x %08x %08x %08x %08x\n",
+ sn[0], sn[1], sn[2], sn[3], sn[4]);
return 0;
}
@@ -164,7 +166,7 @@ static int read_sn(struct echoaudio *chip)
/* This card has no ASIC, just return ok */
static inline int check_asic_status(struct echoaudio *chip)
{
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
return 0;
}
@@ -184,7 +186,7 @@ static int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic)
err = get_firmware(&fw, chip, asic);
if (err < 0) {
- snd_printk(KERN_WARNING "Firmware not found !\n");
+ dev_warn(chip->card->dev, "Firmware not found !\n");
return err;
}
@@ -204,13 +206,12 @@ static int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic)
goto la_error;
}
- DE_INIT(("ASIC loaded\n"));
- free_firmware(fw);
+ free_firmware(fw, chip);
return 0;
la_error:
- DE_INIT(("failed on write_dsp\n"));
- free_firmware(fw);
+ dev_err(chip->card->dev, "failed on write_dsp\n");
+ free_firmware(fw, chip);
return -EIO;
}
@@ -240,14 +241,15 @@ static int install_resident_loader(struct echoaudio *chip)
loader is already installed, host flag 5 will be on. */
status = get_dsp_register(chip, CHI32_STATUS_REG);
if (status & CHI32_STATUS_REG_HF5) {
- DE_INIT(("Resident loader already installed; status is 0x%x\n",
- status));
+ dev_dbg(chip->card->dev,
+ "Resident loader already installed; status is 0x%x\n",
+ status);
return 0;
}
i = get_firmware(&fw, chip, FW_361_LOADER);
if (i < 0) {
- snd_printk(KERN_WARNING "Firmware not found !\n");
+ dev_warn(chip->card->dev, "Firmware not found !\n");
return i;
}
@@ -282,12 +284,14 @@ static int install_resident_loader(struct echoaudio *chip)
/* Write the count to the DSP */
if (write_dsp(chip, words)) {
- DE_INIT(("install_resident_loader: Failed to write word count!\n"));
+ dev_err(chip->card->dev,
+ "install_resident_loader: Failed to write word count!\n");
goto irl_error;
}
/* Write the DSP address */
if (write_dsp(chip, address)) {
- DE_INIT(("install_resident_loader: Failed to write DSP address!\n"));
+ dev_err(chip->card->dev,
+ "install_resident_loader: Failed to write DSP address!\n");
goto irl_error;
}
/* Write out this block of code to the DSP */
@@ -296,7 +300,8 @@ static int install_resident_loader(struct echoaudio *chip)
data = ((u32)code[index] << 16) + code[index + 1];
if (write_dsp(chip, data)) {
- DE_INIT(("install_resident_loader: Failed to write DSP code\n"));
+ dev_err(chip->card->dev,
+ "install_resident_loader: Failed to write DSP code\n");
goto irl_error;
}
index += 2;
@@ -311,16 +316,16 @@ static int install_resident_loader(struct echoaudio *chip)
}
if (i == 200) {
- DE_INIT(("Resident loader failed to set HF5\n"));
+ dev_err(chip->card->dev, "Resident loader failed to set HF5\n");
goto irl_error;
}
- DE_INIT(("Resident loader successfully installed\n"));
- free_firmware(fw);
+ dev_dbg(chip->card->dev, "Resident loader successfully installed\n");
+ free_firmware(fw, chip);
return 0;
irl_error:
- free_firmware(fw);
+ free_firmware(fw, chip);
return -EIO;
}
@@ -333,24 +338,26 @@ static int load_dsp(struct echoaudio *chip, u16 *code)
int index, words, i;
if (chip->dsp_code == code) {
- DE_INIT(("DSP is already loaded!\n"));
+ dev_warn(chip->card->dev, "DSP is already loaded!\n");
return 0;
}
- chip->bad_board = TRUE; /* Set TRUE until DSP loaded */
+ chip->bad_board = true; /* Set true until DSP loaded */
chip->dsp_code = NULL; /* Current DSP code not loaded */
- chip->asic_loaded = FALSE; /* Loading the DSP code will reset the ASIC */
+ chip->asic_loaded = false; /* Loading the DSP code will reset the ASIC */
- DE_INIT(("load_dsp: Set bad_board to TRUE\n"));
+ dev_dbg(chip->card->dev, "load_dsp: Set bad_board to true\n");
/* If this board requires a resident loader, install it. */
#ifdef DSP_56361
- if ((i = install_resident_loader(chip)) < 0)
+ i = install_resident_loader(chip);
+ if (i < 0)
return i;
#endif
/* Send software reset command */
if (send_vector(chip, DSP_VC_RESET) < 0) {
- DE_INIT(("LoadDsp: send_vector DSP_VC_RESET failed, Critical Failure\n"));
+ dev_err(chip->card->dev,
+ "LoadDsp: send_vector DSP_VC_RESET failed, Critical Failure\n");
return -EIO;
}
/* Delay 10us */
@@ -365,7 +372,8 @@ static int load_dsp(struct echoaudio *chip, u16 *code)
}
if (i == 1000) {
- DE_INIT(("load_dsp: Timeout waiting for CHI32_STATUS_REG_HF3\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: Timeout waiting for CHI32_STATUS_REG_HF3\n");
return -EIO;
}
@@ -402,29 +410,34 @@ static int load_dsp(struct echoaudio *chip, u16 *code)
index += 2;
if (write_dsp(chip, words) < 0) {
- DE_INIT(("load_dsp: failed to write number of DSP words\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: failed to write number of DSP words\n");
return -EIO;
}
if (write_dsp(chip, address) < 0) {
- DE_INIT(("load_dsp: failed to write DSP address\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: failed to write DSP address\n");
return -EIO;
}
if (write_dsp(chip, mem_type) < 0) {
- DE_INIT(("load_dsp: failed to write DSP memory type\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: failed to write DSP memory type\n");
return -EIO;
}
/* Code */
for (i = 0; i < words; i++, index+=2) {
data = ((u32)code[index] << 16) + code[index + 1];
if (write_dsp(chip, data) < 0) {
- DE_INIT(("load_dsp: failed to write DSP data\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: failed to write DSP data\n");
return -EIO;
}
}
}
if (write_dsp(chip, 0) < 0) { /* We're done!!! */
- DE_INIT(("load_dsp: Failed to write final zero\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: Failed to write final zero\n");
return -EIO;
}
udelay(10);
@@ -437,12 +450,14 @@ static int load_dsp(struct echoaudio *chip, u16 *code)
get_dsp_register(chip, CHI32_CONTROL_REG) & ~0x1b00);
if (write_dsp(chip, DSP_FNC_SET_COMMPAGE_ADDR) < 0) {
- DE_INIT(("load_dsp: Failed to write DSP_FNC_SET_COMMPAGE_ADDR\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: Failed to write DSP_FNC_SET_COMMPAGE_ADDR\n");
return -EIO;
}
if (write_dsp(chip, chip->comm_page_phys) < 0) {
- DE_INIT(("load_dsp: Failed to write comm page address\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: Failed to write comm page address\n");
return -EIO;
}
@@ -451,19 +466,20 @@ static int load_dsp(struct echoaudio *chip, u16 *code)
We don't actually use the serial number but we have to
get it as part of the DSP init voodoo. */
if (read_sn(chip) < 0) {
- DE_INIT(("load_dsp: Failed to read serial number\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: Failed to read serial number\n");
return -EIO;
}
chip->dsp_code = code; /* Show which DSP code loaded */
- chip->bad_board = FALSE; /* DSP OK */
- DE_INIT(("load_dsp: OK!\n"));
+ chip->bad_board = false; /* DSP OK */
return 0;
}
udelay(100);
}
- DE_INIT(("load_dsp: DSP load timed out waiting for HF4\n"));
+ dev_err(chip->card->dev,
+ "load_dsp: DSP load timed out waiting for HF4\n");
return -EIO;
}
@@ -475,12 +491,13 @@ static int load_firmware(struct echoaudio *chip)
const struct firmware *fw;
int box_type, err;
- if (snd_BUG_ON(!chip->dsp_code_to_load || !chip->comm_page))
+ if (snd_BUG_ON(!chip->comm_page))
return -EPERM;
/* See if the ASIC is present and working - only if the DSP is already loaded */
if (chip->dsp_code) {
- if ((box_type = check_asic_status(chip)) >= 0)
+ box_type = check_asic_status(chip);
+ if (box_type >= 0)
return box_type;
/* ASIC check failed; force the DSP to reload */
chip->dsp_code = NULL;
@@ -490,11 +507,12 @@ static int load_firmware(struct echoaudio *chip)
if (err < 0)
return err;
err = load_dsp(chip, (u16 *)fw->data);
- free_firmware(fw);
+ free_firmware(fw, chip);
if (err < 0)
return err;
- if ((box_type = load_asic(chip)) < 0)
+ box_type = load_asic(chip);
+ if (box_type < 0)
return box_type; /* error */
return box_type;
@@ -620,36 +638,30 @@ This function assumes there are no more than 16 in/out busses or pipes
Meters is an array [3][16][2] of long. */
static void get_audio_meters(struct echoaudio *chip, long *meters)
{
- int i, m, n;
+ unsigned int i, m, n;
- m = 0;
- n = 0;
- for (i = 0; i < num_busses_out(chip); i++, m++) {
+ for (i = 0 ; i < 96; i++)
+ meters[i] = 0;
+
+ for (m = 0, n = 0, i = 0; i < num_busses_out(chip); i++, m++) {
meters[n++] = chip->comm_page->vu_meter[m];
meters[n++] = chip->comm_page->peak_meter[m];
}
- for (; n < 32; n++)
- meters[n] = 0;
#ifdef ECHOCARD_ECHO3G
m = E3G_MAX_OUTPUTS; /* Skip unused meters */
#endif
- for (i = 0; i < num_busses_in(chip); i++, m++) {
+ for (n = 32, i = 0; i < num_busses_in(chip); i++, m++) {
meters[n++] = chip->comm_page->vu_meter[m];
meters[n++] = chip->comm_page->peak_meter[m];
}
- for (; n < 64; n++)
- meters[n] = 0;
-
#ifdef ECHOCARD_HAS_VMIXER
- for (i = 0; i < num_pipes_out(chip); i++, m++) {
+ for (n = 64, i = 0; i < num_pipes_out(chip); i++, m++) {
meters[n++] = chip->comm_page->vu_meter[m];
meters[n++] = chip->comm_page->peak_meter[m];
}
#endif
- for (; n < 96; n++)
- meters[n] = 0;
}
@@ -657,15 +669,15 @@ static void get_audio_meters(struct echoaudio *chip, long *meters)
static int restore_dsp_rettings(struct echoaudio *chip)
{
int i, o, err;
- DE_INIT(("restore_dsp_settings\n"));
- if ((err = check_asic_status(chip)) < 0)
+ err = check_asic_status(chip);
+ if (err < 0)
return err;
/* Gina20/Darla20 only. Should be harmless for other cards. */
chip->comm_page->gd_clock_state = GD_CLOCK_UNDEF;
chip->comm_page->gd_spdif_status = GD_SPDIF_STATUS_UNDEF;
- chip->comm_page->handshake = 0xffffffff;
+ chip->comm_page->handshake = cpu_to_le32(0xffffffff);
/* Restore output busses */
for (i = 0; i < num_busses_out(chip); i++) {
@@ -754,7 +766,6 @@ static int restore_dsp_rettings(struct echoaudio *chip)
if (send_vector(chip, DSP_VC_UPDATE_FLAGS) < 0)
return -EIO;
- DE_INIT(("restore_dsp_rettings done\n"));
return 0;
}
@@ -834,7 +845,8 @@ static void set_audio_format(struct echoaudio *chip, u16 pipe_index,
break;
}
}
- DE_ACT(("set_audio_format[%d] = %x\n", pipe_index, dsp_format));
+ dev_dbg(chip->card->dev,
+ "set_audio_format[%d] = %x\n", pipe_index, dsp_format);
chip->comm_page->audio_format[pipe_index] = cpu_to_le16(dsp_format);
}
@@ -847,7 +859,6 @@ Same thing for pause_ and stop_ -trasport below. */
static int start_transport(struct echoaudio *chip, u32 channel_mask,
u32 cyclic_mask)
{
- DE_ACT(("start_transport %x\n", channel_mask));
if (wait_handshake(chip))
return -EIO;
@@ -865,7 +876,7 @@ static int start_transport(struct echoaudio *chip, u32 channel_mask,
return 0;
}
- DE_ACT(("start_transport: No pipes to start!\n"));
+ dev_err(chip->card->dev, "start_transport: No pipes to start!\n");
return -EINVAL;
}
@@ -873,7 +884,6 @@ static int start_transport(struct echoaudio *chip, u32 channel_mask,
static int pause_transport(struct echoaudio *chip, u32 channel_mask)
{
- DE_ACT(("pause_transport %x\n", channel_mask));
if (wait_handshake(chip))
return -EIO;
@@ -892,7 +902,7 @@ static int pause_transport(struct echoaudio *chip, u32 channel_mask)
return 0;
}
- DE_ACT(("pause_transport: No pipes to stop!\n"));
+ dev_dbg(chip->card->dev, "pause_transport: No pipes to stop!\n");
return 0;
}
@@ -900,7 +910,6 @@ static int pause_transport(struct echoaudio *chip, u32 channel_mask)
static int stop_transport(struct echoaudio *chip, u32 channel_mask)
{
- DE_ACT(("stop_transport %x\n", channel_mask));
if (wait_handshake(chip))
return -EIO;
@@ -919,7 +928,7 @@ static int stop_transport(struct echoaudio *chip, u32 channel_mask)
return 0;
}
- DE_ACT(("stop_transport: No pipes to stop!\n"));
+ dev_dbg(chip->card->dev, "stop_transport: No pipes to stop!\n");
return 0;
}
@@ -936,15 +945,14 @@ static inline int is_pipe_allocated(struct echoaudio *chip, u16 pipe_index)
stopped and unallocated. */
static int rest_in_peace(struct echoaudio *chip)
{
- DE_ACT(("rest_in_peace() open=%x\n", chip->pipe_alloc_mask));
/* Stops all active pipes (just to be sure) */
stop_transport(chip, chip->active_mask);
- set_meters_on(chip, FALSE);
+ set_meters_on(chip, false);
#ifdef ECHOCARD_HAS_MIDI
- enable_midi_input(chip, FALSE);
+ enable_midi_input(chip, false);
#endif
/* Go to sleep */
@@ -964,21 +972,22 @@ static int init_dsp_comm_page(struct echoaudio *chip)
{
/* Check if the compiler added extra padding inside the structure */
if (offsetof(struct comm_page, midi_output) != 0xbe0) {
- DE_INIT(("init_dsp_comm_page() - Invalid struct comm_page structure\n"));
+ dev_err(chip->card->dev,
+ "init_dsp_comm_page() - Invalid struct comm_page structure\n");
return -EPERM;
}
/* Init all the basic stuff */
chip->card_name = ECHOCARD_NAME;
- chip->bad_board = TRUE; /* Set TRUE until DSP loaded */
+ chip->bad_board = true; /* Set true until DSP loaded */
chip->dsp_code = NULL; /* Current DSP code not loaded */
- chip->asic_loaded = FALSE;
+ chip->asic_loaded = false;
memset(chip->comm_page, 0, sizeof(struct comm_page));
/* Init the comm page */
chip->comm_page->comm_size =
cpu_to_le32(sizeof(struct comm_page));
- chip->comm_page->handshake = 0xffffffff;
+ chip->comm_page->handshake = cpu_to_le32(0xffffffff);
chip->comm_page->midi_out_free_count =
cpu_to_le32(DSP_MIDI_OUT_FIFO_SIZE);
chip->comm_page->sample_rate = cpu_to_le32(44100);
@@ -998,7 +1007,6 @@ static int init_dsp_comm_page(struct echoaudio *chip)
*/
static int init_line_levels(struct echoaudio *chip)
{
- DE_INIT(("init_line_levels\n"));
memset(chip->output_gain, ECHOGAIN_MUTED, sizeof(chip->output_gain));
memset(chip->input_gain, ECHOGAIN_MUTED, sizeof(chip->input_gain));
memset(chip->monitor_gain, ECHOGAIN_MUTED, sizeof(chip->monitor_gain));
@@ -1048,26 +1056,25 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe,
{
int i;
u32 channel_mask;
- char is_cyclic;
- DE_ACT(("allocate_pipes: ch=%d int=%d\n", pipe_index, interleave));
+ dev_dbg(chip->card->dev,
+ "allocate_pipes: ch=%d int=%d\n", pipe_index, interleave);
if (chip->bad_board)
return -EIO;
- is_cyclic = 1; /* This driver uses cyclic buffers only */
-
for (channel_mask = i = 0; i < interleave; i++)
channel_mask |= 1 << (pipe_index + i);
if (chip->pipe_alloc_mask & channel_mask) {
- DE_ACT(("allocate_pipes: channel already open\n"));
+ dev_err(chip->card->dev,
+ "allocate_pipes: channel already open\n");
return -EAGAIN;
}
chip->comm_page->position[pipe_index] = 0;
chip->pipe_alloc_mask |= channel_mask;
- if (is_cyclic)
- chip->pipe_cyclic_mask |= channel_mask;
+ /* This driver uses cyclic buffers only */
+ chip->pipe_cyclic_mask |= channel_mask;
pipe->index = pipe_index;
pipe->interleave = interleave;
pipe->state = PIPE_STATE_STOPPED;
@@ -1075,9 +1082,8 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe,
/* The counter register is where the DSP writes the 32 bit DMA
position for a pipe. The DSP is constantly updating this value as
it moves data. The DMA counter is in units of bytes, not samples. */
- pipe->dma_counter = &chip->comm_page->position[pipe_index];
+ pipe->dma_counter = (__le32 *)&chip->comm_page->position[pipe_index];
*pipe->dma_counter = 0;
- DE_ACT(("allocate_pipes: ok\n"));
return pipe_index;
}
@@ -1088,7 +1094,6 @@ static int free_pipes(struct echoaudio *chip, struct audiopipe *pipe)
u32 channel_mask;
int i;
- DE_ACT(("free_pipes: Pipe %d\n", pipe->index));
if (snd_BUG_ON(!is_pipe_allocated(chip, pipe->index)))
return -EINVAL;
if (snd_BUG_ON(pipe->state != PIPE_STATE_STOPPED))
@@ -1130,7 +1135,7 @@ static int sglist_add_mapping(struct echoaudio *chip, struct audiopipe *pipe,
list[head].size = cpu_to_le32(length);
pipe->sglist_head++;
} else {
- DE_ACT(("SGlist: too many fragments\n"));
+ dev_err(chip->card->dev, "SGlist: too many fragments\n");
return -ENOMEM;
}
return 0;
diff --git a/sound/pci/echoaudio/echoaudio_dsp.h b/sound/pci/echoaudio/echoaudio_dsp.h
index cb7d75a0a503..aa9129519795 100644
--- a/sound/pci/echoaudio/echoaudio_dsp.h
+++ b/sound/pci/echoaudio/echoaudio_dsp.h
@@ -627,8 +627,8 @@ sg_entry struct is read by the DSP, so all values must be little-endian. */
#define MAX_SGLIST_ENTRIES 512
struct sg_entry {
- u32 addr;
- u32 size;
+ __le32 addr;
+ __le32 size;
};
@@ -643,18 +643,18 @@ struct sg_entry {
****************************************************************************/
struct comm_page { /* Base Length*/
- u32 comm_size; /* size of this object 0x000 4 */
- u32 flags; /* See Appendix A below 0x004 4 */
- u32 unused; /* Unused entry 0x008 4 */
- u32 sample_rate; /* Card sample rate in Hz 0x00c 4 */
- u32 handshake; /* DSP command handshake 0x010 4 */
- u32 cmd_start; /* Chs. to start mask 0x014 4 */
- u32 cmd_stop; /* Chs. to stop mask 0x018 4 */
- u32 cmd_reset; /* Chs. to reset mask 0x01c 4 */
- u16 audio_format[DSP_MAXPIPES]; /* Chs. audio format 0x020 32*2 */
+ __le32 comm_size; /* size of this object 0x000 4 */
+ __le32 flags; /* See Appendix A below 0x004 4 */
+ __le32 unused; /* Unused entry 0x008 4 */
+ __le32 sample_rate; /* Card sample rate in Hz 0x00c 4 */
+ __le32 handshake; /* DSP command handshake 0x010 4 */
+ __le32 cmd_start; /* Chs. to start mask 0x014 4 */
+ __le32 cmd_stop; /* Chs. to stop mask 0x018 4 */
+ __le32 cmd_reset; /* Chs. to reset mask 0x01c 4 */
+ __le16 audio_format[DSP_MAXPIPES]; /* Chs. audio format 0x020 32*2 */
struct sg_entry sglist_addr[DSP_MAXPIPES];
/* Chs. Physical sglist addrs 0x060 32*8 */
- u32 position[DSP_MAXPIPES];
+ __le32 position[DSP_MAXPIPES];
/* Positions for ea. ch. 0x160 32*4 */
s8 vu_meter[DSP_MAXPIPES];
/* VU meters 0x1e0 32*1 */
@@ -666,28 +666,28 @@ struct comm_page { /* Base Length*/
/* Input gain 0x230 16*1 */
s8 monitors[MONITOR_ARRAY_SIZE];
/* Monitor map 0x240 0x180 */
- u32 play_coeff[MAX_PLAY_TAPS];
+ __le32 play_coeff[MAX_PLAY_TAPS];
/* Gina/Darla play filters - obsolete 0x3c0 168*4 */
- u32 rec_coeff[MAX_REC_TAPS];
+ __le32 rec_coeff[MAX_REC_TAPS];
/* Gina/Darla record filters - obsolete 0x660 192*4 */
- u16 midi_input[MIDI_IN_BUFFER_SIZE];
+ __le16 midi_input[MIDI_IN_BUFFER_SIZE];
/* MIDI input data transfer buffer 0x960 256*2 */
u8 gd_clock_state; /* Chg Gina/Darla clock state 0xb60 1 */
u8 gd_spdif_status; /* Chg. Gina/Darla S/PDIF state 0xb61 1 */
u8 gd_resampler_state; /* Should always be 3 0xb62 1 */
u8 filler2; /* 0xb63 1 */
- u32 nominal_level_mask; /* -10 level enable mask 0xb64 4 */
- u16 input_clock; /* Chg. Input clock state 0xb68 2 */
- u16 output_clock; /* Chg. Output clock state 0xb6a 2 */
- u32 status_clocks; /* Current Input clock state 0xb6c 4 */
- u32 ext_box_status; /* External box status 0xb70 4 */
- u32 cmd_add_buffer; /* Pipes to add (obsolete) 0xb74 4 */
- u32 midi_out_free_count;
+ __le32 nominal_level_mask; /* -10 level enable mask 0xb64 4 */
+ __le16 input_clock; /* Chg. Input clock state 0xb68 2 */
+ __le16 output_clock; /* Chg. Output clock state 0xb6a 2 */
+ __le32 status_clocks; /* Current Input clock state 0xb6c 4 */
+ __le32 ext_box_status; /* External box status 0xb70 4 */
+ __le32 cmd_add_buffer; /* Pipes to add (obsolete) 0xb74 4 */
+ __le32 midi_out_free_count;
/* # of bytes free in MIDI output FIFO 0xb78 4 */
- u32 unused2; /* Cyclic pipes 0xb7c 4 */
- u32 control_register;
+ __le32 unused2; /* Cyclic pipes 0xb7c 4 */
+ __le32 control_register;
/* Mona, Gina24, Layla24, 3G ctrl reg 0xb80 4 */
- u32 e3g_frq_register; /* 3G frequency register 0xb84 4 */
+ __le32 e3g_frq_register; /* 3G frequency register 0xb84 4 */
u8 filler[24]; /* filler 0xb88 24*1 */
s8 vmixer[VMIXER_ARRAY_SIZE];
/* Vmixer levels 0xba0 64*1 */
diff --git a/sound/pci/echoaudio/echoaudio_gml.c b/sound/pci/echoaudio/echoaudio_gml.c
index afa273330e8a..248983fa2959 100644
--- a/sound/pci/echoaudio/echoaudio_gml.c
+++ b/sound/pci/echoaudio/echoaudio_gml.c
@@ -46,8 +46,9 @@ static int check_asic_status(struct echoaudio *chip)
/* The DSP will return a value to indicate whether or not the
ASIC is currently loaded */
if (read_dsp(chip, &asic_status) < 0) {
- DE_INIT(("check_asic_status: failed on read_dsp\n"));
- chip->asic_loaded = FALSE;
+ dev_err(chip->card->dev,
+ "check_asic_status: failed on read_dsp\n");
+ chip->asic_loaded = false;
return -EIO;
}
@@ -62,20 +63,22 @@ the control register. write_control_reg sends the new control register
value to the DSP. */
static int write_control_reg(struct echoaudio *chip, u32 value, char force)
{
+ __le32 reg_value;
+
/* Handle the digital input auto-mute */
if (chip->digital_in_automute)
value |= GML_DIGITAL_IN_AUTO_MUTE;
else
value &= ~GML_DIGITAL_IN_AUTO_MUTE;
- DE_ACT(("write_control_reg: 0x%x\n", value));
+ dev_dbg(chip->card->dev, "write_control_reg: 0x%x\n", value);
/* Write the control register */
- value = cpu_to_le32(value);
- if (value != chip->comm_page->control_register || force) {
+ reg_value = cpu_to_le32(value);
+ if (reg_value != chip->comm_page->control_register || force) {
if (wait_handshake(chip))
return -EIO;
- chip->comm_page->control_register = value;
+ chip->comm_page->control_register = reg_value;
clear_handshake(chip);
return send_vector(chip, DSP_VC_WRITE_CONTROL_REG);
}
@@ -91,7 +94,7 @@ If the auto-mute is disabled, the digital inputs are enabled regardless of
what the input clock is set or what is connected. */
static int set_input_auto_mute(struct echoaudio *chip, int automute)
{
- DE_ACT(("set_input_auto_mute %d\n", automute));
+ dev_dbg(chip->card->dev, "set_input_auto_mute %d\n", automute);
chip->digital_in_automute = automute;
@@ -191,10 +194,11 @@ static int set_professional_spdif(struct echoaudio *chip, char prof)
}
}
- if ((err = write_control_reg(chip, control_reg, FALSE)))
+ err = write_control_reg(chip, control_reg, false);
+ if (err)
return err;
chip->professional_spdif = prof;
- DE_ACT(("set_professional_spdif to %s\n",
- prof ? "Professional" : "Consumer"));
+ dev_dbg(chip->card->dev, "set_professional_spdif to %s\n",
+ prof ? "Professional" : "Consumer");
return 0;
}
diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c
index 050e54aa693f..4f864ddc9530 100644
--- a/sound/pci/echoaudio/gina20.c
+++ b/sound/pci/echoaudio/gina20.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define ECHOGALS_FAMILY
@@ -23,7 +11,7 @@
#define ECHOCARD_HAS_INPUT_GAIN
#define ECHOCARD_HAS_DIGITAL_IO
#define ECHOCARD_HAS_EXTERNAL_CLOCK
-#define ECHOCARD_HAS_ADAT FALSE
+#define ECHOCARD_HAS_ADAT false
/* Pipe indexes */
#define PX_ANALOG_OUT 0 /* 8 */
@@ -44,9 +32,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -55,8 +44,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/gina20_dsp.fw");
@@ -67,12 +55,12 @@ static const struct firmware card_fw[] = {
{0, "gina20_dsp.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x1801, 0xECC0, 0x0020, 0, 0, 0}, /* DSP 56301 Gina20 rev.0 */
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/gina20_dsp.c b/sound/pci/echoaudio/gina20_dsp.c
index d1615a0579d1..c93939850357 100644
--- a/sound/pci/echoaudio/gina20_dsp.c
+++ b/sound/pci/echoaudio/gina20_dsp.c
@@ -37,32 +37,33 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Gina20\n"));
if (snd_BUG_ON((subdevice_id & 0xfff0) != GINA20))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
- DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_GINA20_DSP;
chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
chip->clock_state = GD_CLOCK_UNDEF;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
ECHO_CLOCK_BIT_SPDIF;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -70,7 +71,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
static int set_mixer_defaults(struct echoaudio *chip)
{
- chip->professional_spdif = FALSE;
+ chip->professional_spdif = false;
return init_line_levels(chip);
}
@@ -149,7 +150,6 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
static int set_input_clock(struct echoaudio *chip, u16 clock)
{
- DE_ACT(("set_input_clock:\n"));
switch (clock) {
case ECHO_CLOCK_INTERNAL:
@@ -158,7 +158,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
set_sample_rate(chip, chip->sample_rate);
chip->input_clock = clock;
- DE_ACT(("Set Gina clock to INTERNAL\n"));
break;
case ECHO_CLOCK_SPDIF:
chip->comm_page->gd_clock_state = GD_CLOCK_SPDIFIN;
@@ -166,7 +165,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
clear_handshake(chip);
send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE);
chip->clock_state = GD_CLOCK_SPDIFIN;
- DE_ACT(("Set Gina20 clock to SPDIF\n"));
chip->input_clock = clock;
break;
default:
@@ -208,7 +206,6 @@ static int update_flags(struct echoaudio *chip)
static int set_professional_spdif(struct echoaudio *chip, char prof)
{
- DE_ACT(("set_professional_spdif %d\n", prof));
if (prof)
chip->comm_page->flags |=
cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c
index 5748fc6d29d6..eff69e83ca0a 100644
--- a/sound/pci/echoaudio/gina24.c
+++ b/sound/pci/echoaudio/gina24.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define ECHO24_FAMILY
@@ -50,9 +38,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -61,8 +50,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -85,7 +73,7 @@ static const struct firmware card_fw[] = {
{0, "gina24_361_asic.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x1801, 0xECC0, 0x0050, 0, 0, 0}, /* DSP 56301 Gina24 rev.0 */
{0x1057, 0x1801, 0xECC0, 0x0051, 0, 0, 0}, /* DSP 56301 Gina24 rev.1 */
{0x1057, 0x3410, 0xECC0, 0x0050, 0, 0, 0}, /* DSP 56361 Gina24 rev.0 */
@@ -93,7 +81,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/gina24_dsp.c b/sound/pci/echoaudio/gina24_dsp.c
index 98f7cfa81b5f..78fbac9f3eac 100644
--- a/sound/pci/echoaudio/gina24_dsp.c
+++ b/sound/pci/echoaudio/gina24_dsp.c
@@ -41,18 +41,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Gina24\n"));
if (snd_BUG_ON((subdevice_id & 0xfff0) != GINA24))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
- DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->input_clock_types =
ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
ECHO_CLOCK_BIT_ESYNC | ECHO_CLOCK_BIT_ESYNC96 |
@@ -74,11 +75,11 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_CDROM;
}
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -87,8 +88,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
static int set_mixer_defaults(struct echoaudio *chip)
{
chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
- chip->professional_spdif = FALSE;
- chip->digital_in_automute = TRUE;
+ chip->professional_spdif = false;
+ chip->digital_in_automute = true;
return init_line_levels(chip);
}
@@ -153,9 +154,8 @@ static int load_asic(struct echoaudio *chip)
48 kHz, internal clock, S/PDIF RCA mode */
if (!err) {
control_reg = GML_CONVERTER_ENABLE | GML_48KHZ;
- err = write_control_reg(chip, control_reg, TRUE);
+ err = write_control_reg(chip, control_reg, true);
}
- DE_INIT(("load_asic() done\n"));
return err;
}
@@ -171,8 +171,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
/* Only set the clock for internal mode. */
if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
- DE_ACT(("set_sample_rate: Cannot set sample rate - "
- "clock not set to CLK_CLOCKININTERNAL\n"));
+ dev_warn(chip->card->dev,
+ "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n");
/* Save the rate anyhow */
chip->comm_page->sample_rate = cpu_to_le32(rate);
chip->sample_rate = rate;
@@ -217,7 +217,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
clock = GML_8KHZ;
break;
default:
- DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+ dev_err(chip->card->dev,
+ "set_sample_rate: %d invalid!\n", rate);
return -EINVAL;
}
@@ -225,9 +226,9 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */
chip->sample_rate = rate;
- DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock));
+ dev_dbg(chip->card->dev, "set_sample_rate: %d clock %d\n", rate, clock);
- return write_control_reg(chip, control_reg, FALSE);
+ return write_control_reg(chip, control_reg, false);
}
@@ -236,7 +237,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
{
u32 control_reg, clocks_from_dsp;
- DE_ACT(("set_input_clock:\n"));
/* Mask off the clock select bits */
control_reg = le32_to_cpu(chip->comm_page->control_register) &
@@ -245,13 +245,11 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
switch (clock) {
case ECHO_CLOCK_INTERNAL:
- DE_ACT(("Set Gina24 clock to INTERNAL\n"));
chip->input_clock = ECHO_CLOCK_INTERNAL;
return set_sample_rate(chip, chip->sample_rate);
case ECHO_CLOCK_SPDIF:
if (chip->digital_mode == DIGITAL_MODE_ADAT)
return -EAGAIN;
- DE_ACT(("Set Gina24 clock to SPDIF\n"));
control_reg |= GML_SPDIF_CLOCK;
if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96)
control_reg |= GML_DOUBLE_SPEED_MODE;
@@ -261,26 +259,24 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
case ECHO_CLOCK_ADAT:
if (chip->digital_mode != DIGITAL_MODE_ADAT)
return -EAGAIN;
- DE_ACT(("Set Gina24 clock to ADAT\n"));
control_reg |= GML_ADAT_CLOCK;
control_reg &= ~GML_DOUBLE_SPEED_MODE;
break;
case ECHO_CLOCK_ESYNC:
- DE_ACT(("Set Gina24 clock to ESYNC\n"));
control_reg |= GML_ESYNC_CLOCK;
control_reg &= ~GML_DOUBLE_SPEED_MODE;
break;
case ECHO_CLOCK_ESYNC96:
- DE_ACT(("Set Gina24 clock to ESYNC96\n"));
control_reg |= GML_ESYNC_CLOCK | GML_DOUBLE_SPEED_MODE;
break;
default:
- DE_ACT(("Input clock 0x%x not supported for Gina24\n", clock));
+ dev_err(chip->card->dev,
+ "Input clock 0x%x not supported for Gina24\n", clock);
return -EINVAL;
}
chip->input_clock = clock;
- return write_control_reg(chip, control_reg, TRUE);
+ return write_control_reg(chip, control_reg, true);
}
@@ -291,24 +287,25 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
int err, incompatible_clock;
/* Set clock to "internal" if it's not compatible with the new mode */
- incompatible_clock = FALSE;
+ incompatible_clock = false;
switch (mode) {
case DIGITAL_MODE_SPDIF_OPTICAL:
case DIGITAL_MODE_SPDIF_CDROM:
case DIGITAL_MODE_SPDIF_RCA:
if (chip->input_clock == ECHO_CLOCK_ADAT)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
break;
case DIGITAL_MODE_ADAT:
if (chip->input_clock == ECHO_CLOCK_SPDIF)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
break;
default:
- DE_ACT(("Digital mode not supported: %d\n", mode));
+ dev_err(chip->card->dev,
+ "Digital mode not supported: %d\n", mode);
return -EINVAL;
}
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
if (incompatible_clock) { /* Switch to 48KHz, internal */
chip->sample_rate = 48000;
@@ -338,12 +335,12 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
break;
}
- err = write_control_reg(chip, control_reg, TRUE);
- spin_unlock_irq(&chip->lock);
+ err = write_control_reg(chip, control_reg, true);
if (err < 0)
return err;
chip->digital_mode = mode;
- DE_ACT(("set_digital_mode to %d\n", chip->digital_mode));
+ dev_dbg(chip->card->dev,
+ "set_digital_mode to %d\n", chip->digital_mode);
return incompatible_clock;
}
diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c
index 4ae5e35cb5f1..a9f2efc58f6e 100644
--- a/sound/pci/echoaudio/indigo.c
+++ b/sound/pci/echoaudio/indigo.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define INDIGO_FAMILY
@@ -42,9 +30,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -53,8 +42,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -68,12 +56,12 @@ static const struct firmware card_fw[] = {
{0, "indigo_dsp.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x3410, 0xECC0, 0x0090, 0, 0, 0}, /* Indigo */
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/indigo_dsp.c b/sound/pci/echoaudio/indigo_dsp.c
index 5e85f14fe5a8..16eb082df56a 100644
--- a/sound/pci/echoaudio/indigo_dsp.c
+++ b/sound/pci/echoaudio/indigo_dsp.c
@@ -38,29 +38,30 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Indigo\n"));
if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
- DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_INDIGO_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -109,7 +110,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
control_reg = MIA_32000;
break;
default:
- DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+ dev_err(chip->card->dev,
+ "set_sample_rate: %d invalid!\n", rate);
return -EINVAL;
}
@@ -147,7 +149,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
index = output * num_pipes_out(chip) + pipe;
chip->comm_page->vmixer[index] = gain;
- DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+ dev_dbg(chip->card->dev,
+ "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain);
return 0;
}
diff --git a/sound/pci/echoaudio/indigo_express_dsp.c b/sound/pci/echoaudio/indigo_express_dsp.c
index 2e4ab3e34a74..ceda2d7046ac 100644
--- a/sound/pci/echoaudio/indigo_express_dsp.c
+++ b/sound/pci/echoaudio/indigo_express_dsp.c
@@ -61,7 +61,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
control_reg |= clock;
if (control_reg != old_control_reg) {
- DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock));
+ dev_dbg(chip->card->dev,
+ "set_sample_rate: %d clock %d\n", rate, clock);
chip->comm_page->control_register = cpu_to_le32(control_reg);
chip->sample_rate = rate;
clear_handshake(chip);
@@ -89,7 +90,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
index = output * num_pipes_out(chip) + pipe;
chip->comm_page->vmixer[index] = gain;
- DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+ dev_dbg(chip->card->dev,
+ "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain);
return 0;
}
diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c
index 3550715bab1c..14e9769ceba1 100644
--- a/sound/pci/echoaudio/indigodj.c
+++ b/sound/pci/echoaudio/indigodj.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define INDIGO_FAMILY
@@ -42,9 +30,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -53,8 +42,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -68,12 +56,12 @@ static const struct firmware card_fw[] = {
{0, "indigo_dj_dsp.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x3410, 0xECC0, 0x00B0, 0, 0, 0}, /* Indigo DJ*/
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/indigodj_dsp.c b/sound/pci/echoaudio/indigodj_dsp.c
index 68f3c8ccc1bf..17a1d888d0b9 100644
--- a/sound/pci/echoaudio/indigodj_dsp.c
+++ b/sound/pci/echoaudio/indigodj_dsp.c
@@ -38,29 +38,30 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Indigo DJ\n"));
if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_DJ))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
- DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_INDIGO_DJ_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -109,7 +110,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
control_reg = MIA_32000;
break;
default:
- DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+ dev_err(chip->card->dev,
+ "set_sample_rate: %d invalid!\n", rate);
return -EINVAL;
}
@@ -147,7 +149,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
index = output * num_pipes_out(chip) + pipe;
chip->comm_page->vmixer[index] = gain;
- DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+ dev_dbg(chip->card->dev,
+ "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain);
return 0;
}
diff --git a/sound/pci/echoaudio/indigodjx.c b/sound/pci/echoaudio/indigodjx.c
index 19b191fd0120..a14a7dc8c87d 100644
--- a/sound/pci/echoaudio/indigodjx.c
+++ b/sound/pci/echoaudio/indigodjx.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2009 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define INDIGO_FAMILY
@@ -42,7 +30,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -54,7 +42,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -68,12 +56,12 @@ static const struct firmware card_fw[] = {
{0, "indigo_djx_dsp.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x3410, 0xECC0, 0x00E0, 0, 0, 0}, /* Indigo DJx*/
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/indigodjx_dsp.c b/sound/pci/echoaudio/indigodjx_dsp.c
index bb9632c752a9..5fbd4a3a3083 100644
--- a/sound/pci/echoaudio/indigodjx_dsp.c
+++ b/sound/pci/echoaudio/indigodjx_dsp.c
@@ -35,31 +35,30 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Indigo DJx\n"));
if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_DJX))
return -ENODEV;
err = init_dsp_comm_page(chip);
if (err < 0) {
- DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_INDIGO_DJX_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
err = load_firmware(chip);
if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c
index a9fcedf317a4..97e024450d19 100644
--- a/sound/pci/echoaudio/indigoio.c
+++ b/sound/pci/echoaudio/indigoio.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define INDIGO_FAMILY
@@ -43,9 +31,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -54,8 +43,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -69,12 +57,12 @@ static const struct firmware card_fw[] = {
{0, "indigo_io_dsp.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x3410, 0xECC0, 0x00A0, 0, 0, 0}, /* Indigo IO*/
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/indigoio_dsp.c b/sound/pci/echoaudio/indigoio_dsp.c
index beb9a5b69892..791787aa0744 100644
--- a/sound/pci/echoaudio/indigoio_dsp.c
+++ b/sound/pci/echoaudio/indigoio_dsp.c
@@ -38,29 +38,30 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Indigo IO\n"));
if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_IO))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
- DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_INDIGO_IO_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -118,7 +119,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
index = output * num_pipes_out(chip) + pipe;
chip->comm_page->vmixer[index] = gain;
- DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+ dev_dbg(chip->card->dev,
+ "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain);
return 0;
}
diff --git a/sound/pci/echoaudio/indigoiox.c b/sound/pci/echoaudio/indigoiox.c
index bcdfac63212c..a017c966b4dc 100644
--- a/sound/pci/echoaudio/indigoiox.c
+++ b/sound/pci/echoaudio/indigoiox.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2009 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define INDIGO_FAMILY
@@ -43,7 +31,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -55,7 +43,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -69,12 +57,12 @@ static const struct firmware card_fw[] = {
{0, "indigo_iox_dsp.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x3410, 0xECC0, 0x00D0, 0, 0, 0}, /* Indigo IOx */
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/indigoiox_dsp.c b/sound/pci/echoaudio/indigoiox_dsp.c
index 394c6e76bcbc..1ae394ea7c7d 100644
--- a/sound/pci/echoaudio/indigoiox_dsp.c
+++ b/sound/pci/echoaudio/indigoiox_dsp.c
@@ -35,31 +35,30 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Indigo IOx\n"));
if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_IOX))
return -ENODEV;
err = init_dsp_comm_page(chip);
if (err < 0) {
- DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_INDIGO_IOX_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
err = load_firmware(chip);
if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c
index d3a98c5dac86..7e38bc9c025d 100644
--- a/sound/pci/echoaudio/layla20.c
+++ b/sound/pci/echoaudio/layla20.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define ECHOGALS_FAMILY
@@ -26,7 +14,7 @@
#define ECHOCARD_HAS_SUPER_INTERLEAVE
#define ECHOCARD_HAS_DIGITAL_IO
#define ECHOCARD_HAS_EXTERNAL_CLOCK
-#define ECHOCARD_HAS_ADAT FALSE
+#define ECHOCARD_HAS_ADAT false
#define ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH
#define ECHOCARD_HAS_MIDI
@@ -49,9 +37,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -61,8 +50,7 @@
#include <sound/asoundef.h>
#include <sound/initval.h>
#include <sound/rawmidi.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/layla20_dsp.fw");
@@ -76,13 +64,13 @@ static const struct firmware card_fw[] = {
{0, "layla20_asic.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x1801, 0xECC0, 0x0030, 0, 0, 0}, /* DSP 56301 Layla20 rev.0 */
{0x1057, 0x1801, 0xECC0, 0x0031, 0, 0, 0}, /* DSP 56301 Layla20 rev.1 */
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/layla20_dsp.c b/sound/pci/echoaudio/layla20_dsp.c
index 53ce94605044..5fb5c4a4598b 100644
--- a/sound/pci/echoaudio/layla20_dsp.c
+++ b/sound/pci/echoaudio/layla20_dsp.c
@@ -40,19 +40,20 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Layla20\n"));
if (snd_BUG_ON((subdevice_id & 0xfff0) != LAYLA20))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
- DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
- chip->has_midi = TRUE;
+ chip->bad_board = true;
+ chip->has_midi = true;
chip->dsp_code_to_load = FW_LAYLA20_DSP;
chip->input_clock_types =
ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
@@ -60,11 +61,11 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
chip->output_clock_types =
ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_SUPER;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -72,7 +73,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
static int set_mixer_defaults(struct echoaudio *chip)
{
- chip->professional_spdif = FALSE;
+ chip->professional_spdif = false;
return init_line_levels(chip);
}
@@ -114,20 +115,21 @@ static int check_asic_status(struct echoaudio *chip)
u32 asic_status;
int goodcnt, i;
- chip->asic_loaded = FALSE;
+ chip->asic_loaded = false;
for (i = goodcnt = 0; i < 5; i++) {
send_vector(chip, DSP_VC_TEST_ASIC);
/* The DSP will return a value to indicate whether or not
the ASIC is currently loaded */
if (read_dsp(chip, &asic_status) < 0) {
- DE_ACT(("check_asic_status: failed on read_dsp\n"));
+ dev_err(chip->card->dev,
+ "check_asic_status: failed on read_dsp\n");
return -EIO;
}
if (asic_status == ASIC_ALREADY_LOADED) {
if (++goodcnt == 3) {
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
return 0;
}
}
@@ -164,8 +166,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
/* Only set the clock for internal mode. Do not return failure,
simply treat it as a non-event. */
if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
- DE_ACT(("set_sample_rate: Cannot set sample rate - "
- "clock not set to CLK_CLOCKININTERNAL\n"));
+ dev_warn(chip->card->dev,
+ "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n");
chip->comm_page->sample_rate = cpu_to_le32(rate);
chip->sample_rate = rate;
return 0;
@@ -174,7 +176,7 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
if (wait_handshake(chip))
return -EIO;
- DE_ACT(("set_sample_rate(%d)\n", rate));
+ dev_dbg(chip->card->dev, "set_sample_rate(%d)\n", rate);
chip->sample_rate = rate;
chip->comm_page->sample_rate = cpu_to_le32(rate);
clear_handshake(chip);
@@ -188,29 +190,25 @@ static int set_input_clock(struct echoaudio *chip, u16 clock_source)
u16 clock;
u32 rate;
- DE_ACT(("set_input_clock:\n"));
rate = 0;
switch (clock_source) {
case ECHO_CLOCK_INTERNAL:
- DE_ACT(("Set Layla20 clock to INTERNAL\n"));
rate = chip->sample_rate;
clock = LAYLA20_CLOCK_INTERNAL;
break;
case ECHO_CLOCK_SPDIF:
- DE_ACT(("Set Layla20 clock to SPDIF\n"));
clock = LAYLA20_CLOCK_SPDIF;
break;
case ECHO_CLOCK_WORD:
- DE_ACT(("Set Layla20 clock to WORD\n"));
clock = LAYLA20_CLOCK_WORD;
break;
case ECHO_CLOCK_SUPER:
- DE_ACT(("Set Layla20 clock to SUPER\n"));
clock = LAYLA20_CLOCK_SUPER;
break;
default:
- DE_ACT(("Input clock 0x%x not supported for Layla24\n",
- clock_source));
+ dev_err(chip->card->dev,
+ "Input clock 0x%x not supported for Layla24\n",
+ clock_source);
return -EINVAL;
}
chip->input_clock = clock_source;
@@ -229,7 +227,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock_source)
static int set_output_clock(struct echoaudio *chip, u16 clock)
{
- DE_ACT(("set_output_clock: %d\n", clock));
switch (clock) {
case ECHO_CLOCK_SUPER:
clock = LAYLA20_OUTPUT_CLOCK_SUPER;
@@ -238,7 +235,7 @@ static int set_output_clock(struct echoaudio *chip, u16 clock)
clock = LAYLA20_OUTPUT_CLOCK_WORD;
break;
default:
- DE_ACT(("set_output_clock wrong clock\n"));
+ dev_err(chip->card->dev, "set_output_clock wrong clock\n");
return -EINVAL;
}
@@ -283,7 +280,6 @@ static int update_flags(struct echoaudio *chip)
static int set_professional_spdif(struct echoaudio *chip, char prof)
{
- DE_ACT(("set_professional_spdif %d\n", prof));
if (prof)
chip->comm_page->flags |=
cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c
index 2a1dca6dce17..95c52210fb65 100644
--- a/sound/pci/echoaudio/layla24.c
+++ b/sound/pci/echoaudio/layla24.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define ECHO24_FAMILY
@@ -51,9 +39,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -63,8 +52,7 @@
#include <sound/asoundef.h>
#include <sound/initval.h>
#include <sound/rawmidi.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -87,12 +75,12 @@ static const struct firmware card_fw[] = {
{0, "layla24_2S_asic.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x3410, 0xECC0, 0x0060, 0, 0, 0}, /* DSP 56361 Layla24 rev.0 */
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/layla24_dsp.c b/sound/pci/echoaudio/layla24_dsp.c
index 8c041647f285..decfccb1e803 100644
--- a/sound/pci/echoaudio/layla24_dsp.c
+++ b/sound/pci/echoaudio/layla24_dsp.c
@@ -40,19 +40,20 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Layla24\n"));
if (snd_BUG_ON((subdevice_id & 0xfff0) != LAYLA24))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
- DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
- chip->has_midi = TRUE;
+ chip->bad_board = true;
+ chip->has_midi = true;
chip->dsp_code_to_load = FW_LAYLA24_DSP;
chip->input_clock_types =
ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
@@ -62,14 +63,15 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- if ((err = init_line_levels(chip)) < 0)
+ err = init_line_levels(chip);
+ if (err < 0)
return err;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -78,8 +80,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
static int set_mixer_defaults(struct echoaudio *chip)
{
chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
- chip->professional_spdif = FALSE;
- chip->digital_in_automute = TRUE;
+ chip->professional_spdif = false;
+ chip->digital_in_automute = true;
return init_line_levels(chip);
}
@@ -117,7 +119,6 @@ static int load_asic(struct echoaudio *chip)
if (chip->asic_loaded)
return 1;
- DE_INIT(("load_asic\n"));
/* Give the DSP a few milliseconds to settle down */
mdelay(10);
@@ -137,7 +138,7 @@ static int load_asic(struct echoaudio *chip)
err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC,
FW_LAYLA24_2S_ASIC);
if (err < 0)
- return FALSE;
+ return err;
/* Now give the external ASIC a little time to set up */
mdelay(10);
@@ -149,9 +150,8 @@ static int load_asic(struct echoaudio *chip)
48 kHz, internal clock, S/PDIF RCA mode */
if (!err)
err = write_control_reg(chip, GML_CONVERTER_ENABLE | GML_48KHZ,
- TRUE);
+ true);
- DE_INIT(("load_asic() done\n"));
return err;
}
@@ -167,8 +167,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
/* Only set the clock for internal mode. */
if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
- DE_ACT(("set_sample_rate: Cannot set sample rate - "
- "clock not set to CLK_CLOCKININTERNAL\n"));
+ dev_warn(chip->card->dev,
+ "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n");
/* Save the rate anyhow */
chip->comm_page->sample_rate = cpu_to_le32(rate);
chip->sample_rate = rate;
@@ -241,9 +241,10 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP ? */
chip->sample_rate = rate;
- DE_ACT(("set_sample_rate: %d clock %d\n", rate, control_reg));
+ dev_dbg(chip->card->dev,
+ "set_sample_rate: %d clock %d\n", rate, control_reg);
- return write_control_reg(chip, control_reg, FALSE);
+ return write_control_reg(chip, control_reg, false);
}
@@ -260,7 +261,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
/* Pick the new clock */
switch (clock) {
case ECHO_CLOCK_INTERNAL:
- DE_ACT(("Set Layla24 clock to INTERNAL\n"));
chip->input_clock = ECHO_CLOCK_INTERNAL;
return set_sample_rate(chip, chip->sample_rate);
case ECHO_CLOCK_SPDIF:
@@ -269,7 +269,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
control_reg |= GML_SPDIF_CLOCK;
/* Layla24 doesn't support 96KHz S/PDIF */
control_reg &= ~GML_DOUBLE_SPEED_MODE;
- DE_ACT(("Set Layla24 clock to SPDIF\n"));
break;
case ECHO_CLOCK_WORD:
control_reg |= GML_WORD_CLOCK;
@@ -277,22 +276,21 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
control_reg |= GML_DOUBLE_SPEED_MODE;
else
control_reg &= ~GML_DOUBLE_SPEED_MODE;
- DE_ACT(("Set Layla24 clock to WORD\n"));
break;
case ECHO_CLOCK_ADAT:
if (chip->digital_mode != DIGITAL_MODE_ADAT)
return -EAGAIN;
control_reg |= GML_ADAT_CLOCK;
control_reg &= ~GML_DOUBLE_SPEED_MODE;
- DE_ACT(("Set Layla24 clock to ADAT\n"));
break;
default:
- DE_ACT(("Input clock 0x%x not supported for Layla24\n", clock));
+ dev_err(chip->card->dev,
+ "Input clock 0x%x not supported for Layla24\n", clock);
return -EINVAL;
}
chip->input_clock = clock;
- return write_control_reg(chip, control_reg, TRUE);
+ return write_control_reg(chip, control_reg, true);
}
@@ -339,36 +337,36 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
short asic;
/* Set clock to "internal" if it's not compatible with the new mode */
- incompatible_clock = FALSE;
+ incompatible_clock = false;
switch (mode) {
case DIGITAL_MODE_SPDIF_OPTICAL:
case DIGITAL_MODE_SPDIF_RCA:
if (chip->input_clock == ECHO_CLOCK_ADAT)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
asic = FW_LAYLA24_2S_ASIC;
break;
case DIGITAL_MODE_ADAT:
if (chip->input_clock == ECHO_CLOCK_SPDIF)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
asic = FW_LAYLA24_2A_ASIC;
break;
default:
- DE_ACT(("Digital mode not supported: %d\n", mode));
+ dev_err(chip->card->dev,
+ "Digital mode not supported: %d\n", mode);
return -EINVAL;
}
if (incompatible_clock) { /* Switch to 48KHz, internal */
chip->sample_rate = 48000;
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
set_input_clock(chip, ECHO_CLOCK_INTERNAL);
- spin_unlock_irq(&chip->lock);
}
/* switch_asic() can sleep */
if (switch_asic(chip, asic) < 0)
return -EIO;
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
/* Tweak the control register */
control_reg = le32_to_cpu(chip->comm_page->control_register);
@@ -387,12 +385,11 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
break;
}
- err = write_control_reg(chip, control_reg, TRUE);
- spin_unlock_irq(&chip->lock);
+ err = write_control_reg(chip, control_reg, true);
if (err < 0)
return err;
chip->digital_mode = mode;
- DE_ACT(("set_digital_mode to %d\n", mode));
+ dev_dbg(chip->card->dev, "set_digital_mode to %d\n", mode);
return incompatible_clock;
}
diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c
index 9cdf14cfdd74..a2d4b0003b57 100644
--- a/sound/pci/echoaudio/mia.c
+++ b/sound/pci/echoaudio/mia.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define ECHO24_FAMILY
@@ -26,7 +14,7 @@
#define ECHOCARD_HAS_VMIXER
#define ECHOCARD_HAS_DIGITAL_IO
#define ECHOCARD_HAS_EXTERNAL_CLOCK
-#define ECHOCARD_HAS_ADAT FALSE
+#define ECHOCARD_HAS_ADAT false
#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
#define ECHOCARD_HAS_MIDI
#define ECHOCARD_HAS_LINE_OUT_GAIN
@@ -50,9 +38,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -62,8 +51,7 @@
#include <sound/asoundef.h>
#include <sound/initval.h>
#include <sound/rawmidi.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -77,13 +65,13 @@ static const struct firmware card_fw[] = {
{0, "mia_dsp.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x3410, 0xECC0, 0x0080, 0, 0, 0}, /* DSP 56361 Mia rev.0 */
{0x1057, 0x3410, 0xECC0, 0x0081, 0, 0, 0}, /* DSP 56361 Mia rev.1 */
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/mia_dsp.c b/sound/pci/echoaudio/mia_dsp.c
index 6ebfa6e7ab9e..8a4dffc68889 100644
--- a/sound/pci/echoaudio/mia_dsp.c
+++ b/sound/pci/echoaudio/mia_dsp.c
@@ -41,32 +41,33 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Mia\n"));
if (snd_BUG_ON((subdevice_id & 0xfff0) != MIA))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
- DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->dsp_code_to_load = FW_MIA_DSP;
/* Since this card has no ASIC, mark it as loaded so everything
works OK */
- chip->asic_loaded = TRUE;
+ chip->asic_loaded = true;
if ((subdevice_id & 0x0000f) == MIA_MIDI_REV)
- chip->has_midi = TRUE;
+ chip->has_midi = true;
chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
ECHO_CLOCK_BIT_SPDIF;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -126,7 +127,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
control_reg = MIA_32000;
break;
default:
- DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+ dev_err(chip->card->dev,
+ "set_sample_rate: %d invalid!\n", rate);
return -EINVAL;
}
@@ -153,7 +155,7 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
static int set_input_clock(struct echoaudio *chip, u16 clock)
{
- DE_ACT(("set_input_clock(%d)\n", clock));
+ dev_dbg(chip->card->dev, "set_input_clock(%d)\n", clock);
if (snd_BUG_ON(clock != ECHO_CLOCK_INTERNAL &&
clock != ECHO_CLOCK_SPDIF))
return -EINVAL;
@@ -181,7 +183,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
index = output * num_pipes_out(chip) + pipe;
chip->comm_page->vmixer[index] = gain;
- DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+ dev_dbg(chip->card->dev,
+ "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain);
return 0;
}
@@ -211,7 +214,7 @@ static int update_flags(struct echoaudio *chip)
static int set_professional_spdif(struct echoaudio *chip, char prof)
{
- DE_ACT(("set_professional_spdif %d\n", prof));
+ dev_dbg(chip->card->dev, "set_professional_spdif %d\n", prof);
if (prof)
chip->comm_page->flags |=
cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c
index a953d142cb4b..dd5212644844 100644
--- a/sound/pci/echoaudio/midi.c
+++ b/sound/pci/echoaudio/midi.c
@@ -36,7 +36,7 @@
/* Start and stop Midi input */
static int enable_midi_input(struct echoaudio *chip, char enable)
{
- DE_MID(("enable_midi_input(%d)\n", enable));
+ dev_dbg(chip->card->dev, "enable_midi_input(%d)\n", enable);
if (wait_handshake(chip))
return -EIO;
@@ -74,7 +74,7 @@ static int write_midi(struct echoaudio *chip, u8 *data, int bytes)
chip->comm_page->midi_out_free_count = 0;
clear_handshake(chip);
send_vector(chip, DSP_VC_MIDI_WRITE);
- DE_MID(("write_midi: %d\n", bytes));
+ dev_dbg(chip->card->dev, "write_midi: %d\n", bytes);
return bytes;
}
@@ -124,7 +124,6 @@ static int midi_service_irq(struct echoaudio *chip)
return 0;
/* Get the MIDI data from the comm page */
- i = 1;
received = 0;
for (i = 1; i <= count; i++) {
/* Get the MIDI byte */
@@ -157,7 +156,6 @@ static int snd_echo_midi_input_open(struct snd_rawmidi_substream *substream)
struct echoaudio *chip = substream->rmidi->private_data;
chip->midi_in = substream;
- DE_MID(("rawmidi_iopen\n"));
return 0;
}
@@ -169,9 +167,8 @@ static void snd_echo_midi_input_trigger(struct snd_rawmidi_substream *substream,
struct echoaudio *chip = substream->rmidi->private_data;
if (up != chip->midi_input_enabled) {
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
enable_midi_input(chip, up);
- spin_unlock_irq(&chip->lock);
chip->midi_input_enabled = up;
}
}
@@ -183,7 +180,6 @@ static int snd_echo_midi_input_close(struct snd_rawmidi_substream *substream)
struct echoaudio *chip = substream->rmidi->private_data;
chip->midi_in = NULL;
- DE_MID(("rawmidi_iclose\n"));
return 0;
}
@@ -196,42 +192,40 @@ static int snd_echo_midi_output_open(struct snd_rawmidi_substream *substream)
chip->tinuse = 0;
chip->midi_full = 0;
chip->midi_out = substream;
- DE_MID(("rawmidi_oopen\n"));
return 0;
}
-static void snd_echo_midi_output_write(unsigned long data)
+static void snd_echo_midi_output_write(struct timer_list *t)
{
- struct echoaudio *chip = (struct echoaudio *)data;
- unsigned long flags;
+ struct echoaudio *chip = timer_container_of(chip, t, timer);
int bytes, sent, time;
unsigned char buf[MIDI_OUT_BUFFER_SIZE - 1];
- DE_MID(("snd_echo_midi_output_write\n"));
/* No interrupts are involved: we have to check at regular intervals
if the card's output buffer has room for new data. */
- sent = bytes = 0;
- spin_lock_irqsave(&chip->lock, flags);
+ sent = 0;
+ guard(spinlock_irqsave)(&chip->lock);
chip->midi_full = 0;
if (!snd_rawmidi_transmit_empty(chip->midi_out)) {
bytes = snd_rawmidi_transmit_peek(chip->midi_out, buf,
MIDI_OUT_BUFFER_SIZE - 1);
- DE_MID(("Try to send %d bytes...\n", bytes));
+ dev_dbg(chip->card->dev, "Try to send %d bytes...\n", bytes);
sent = write_midi(chip, buf, bytes);
if (sent < 0) {
- snd_printk(KERN_ERR "write_midi() error %d\n", sent);
+ dev_err(chip->card->dev,
+ "write_midi() error %d\n", sent);
/* retry later */
sent = 9000;
chip->midi_full = 1;
} else if (sent > 0) {
- DE_MID(("%d bytes sent\n", sent));
+ dev_dbg(chip->card->dev, "%d bytes sent\n", sent);
snd_rawmidi_transmit_ack(chip->midi_out, sent);
} else {
/* Buffer is full. DSP's internal buffer is 64 (128 ?)
bytes long. Let's wait until half of them are sent */
- DE_MID(("Full\n"));
+ dev_dbg(chip->card->dev, "Full\n");
sent = 32;
chip->midi_full = 1;
}
@@ -243,9 +237,9 @@ static void snd_echo_midi_output_write(unsigned long data)
sent */
time = (sent << 3) / 25 + 1; /* 8/25=0.32ms to send a byte */
mod_timer(&chip->timer, jiffies + (time * HZ + 999) / 1000);
- DE_MID(("Timer armed(%d)\n", ((time * HZ + 999) / 1000)));
+ dev_dbg(chip->card->dev,
+ "Timer armed(%d)\n", ((time * HZ + 999) / 1000));
}
- spin_unlock_irqrestore(&chip->lock, flags);
}
@@ -254,29 +248,32 @@ static void snd_echo_midi_output_trigger(struct snd_rawmidi_substream *substream
int up)
{
struct echoaudio *chip = substream->rmidi->private_data;
-
- DE_MID(("snd_echo_midi_output_trigger(%d)\n", up));
- spin_lock_irq(&chip->lock);
- if (up) {
- if (!chip->tinuse) {
- init_timer(&chip->timer);
- chip->timer.function = snd_echo_midi_output_write;
- chip->timer.data = (unsigned long)chip;
- chip->tinuse = 1;
- }
- } else {
- if (chip->tinuse) {
- chip->tinuse = 0;
- spin_unlock_irq(&chip->lock);
- del_timer_sync(&chip->timer);
- DE_MID(("Timer removed\n"));
- return;
+ bool remove_timer = false;
+
+ dev_dbg(chip->card->dev, "snd_echo_midi_output_trigger(%d)\n", up);
+ scoped_guard(spinlock_irq, &chip->lock) {
+ if (up) {
+ if (!chip->tinuse) {
+ timer_setup(&chip->timer, snd_echo_midi_output_write,
+ 0);
+ chip->tinuse = 1;
+ }
+ } else {
+ if (chip->tinuse) {
+ chip->tinuse = 0;
+ remove_timer = true;
+ }
}
}
- spin_unlock_irq(&chip->lock);
+
+ if (remove_timer) {
+ timer_delete_sync(&chip->timer);
+ dev_dbg(chip->card->dev, "Timer removed\n");
+ return;
+ }
if (up && !chip->midi_full)
- snd_echo_midi_output_write((unsigned long)chip);
+ snd_echo_midi_output_write(&chip->timer);
}
@@ -286,19 +283,18 @@ static int snd_echo_midi_output_close(struct snd_rawmidi_substream *substream)
struct echoaudio *chip = substream->rmidi->private_data;
chip->midi_out = NULL;
- DE_MID(("rawmidi_oclose\n"));
return 0;
}
-static struct snd_rawmidi_ops snd_echo_midi_input = {
+static const struct snd_rawmidi_ops snd_echo_midi_input = {
.open = snd_echo_midi_input_open,
.close = snd_echo_midi_input_close,
.trigger = snd_echo_midi_input_trigger,
};
-static struct snd_rawmidi_ops snd_echo_midi_output = {
+static const struct snd_rawmidi_ops snd_echo_midi_output = {
.open = snd_echo_midi_output_open,
.close = snd_echo_midi_output_close,
.trigger = snd_echo_midi_output_trigger,
@@ -307,16 +303,16 @@ static struct snd_rawmidi_ops snd_echo_midi_output = {
/* <--snd_echo_probe() */
-static int __devinit snd_echo_midi_create(struct snd_card *card,
- struct echoaudio *chip)
+static int snd_echo_midi_create(struct snd_card *card,
+ struct echoaudio *chip)
{
int err;
- if ((err = snd_rawmidi_new(card, card->shortname, 0, 1, 1,
- &chip->rmidi)) < 0)
+ err = snd_rawmidi_new(card, card->shortname, 0, 1, 1, &chip->rmidi);
+ if (err < 0)
return err;
- strcpy(chip->rmidi->name, card->shortname);
+ strscpy(chip->rmidi->name, card->shortname);
chip->rmidi->private_data = chip;
snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
@@ -326,6 +322,5 @@ static int __devinit snd_echo_midi_create(struct snd_card *card,
chip->rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
- DE_INIT(("MIDI ok\n"));
return 0;
}
diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c
index 1047be405ebe..1b45a2b5066f 100644
--- a/sound/pci/echoaudio/mona.c
+++ b/sound/pci/echoaudio/mona.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA driver for Echoaudio soundcards.
* Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#define ECHO24_FAMILY
@@ -48,9 +36,10 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/control.h>
@@ -59,8 +48,7 @@
#include <sound/pcm_params.h>
#include <sound/asoundef.h>
#include <sound/initval.h>
-#include <asm/io.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include "echoaudio.h"
MODULE_FIRMWARE("ea/loader_dsp.fw");
@@ -92,7 +80,7 @@ static const struct firmware card_fw[] = {
{0, "mona_2_asic.fw"}
};
-static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
+static const struct pci_device_id snd_echo_ids[] = {
{0x1057, 0x1801, 0xECC0, 0x0070, 0, 0, 0}, /* DSP 56301 Mona rev.0 */
{0x1057, 0x1801, 0xECC0, 0x0071, 0, 0, 0}, /* DSP 56301 Mona rev.1 */
{0x1057, 0x1801, 0xECC0, 0x0072, 0, 0, 0}, /* DSP 56301 Mona rev.2 */
@@ -102,7 +90,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
{0,}
};
-static struct snd_pcm_hardware pcm_hardware_skel = {
+static const struct snd_pcm_hardware pcm_hardware_skel = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/pci/echoaudio/mona_dsp.c b/sound/pci/echoaudio/mona_dsp.c
index 6e6a7eb555b8..9bb6a174745c 100644
--- a/sound/pci/echoaudio/mona_dsp.c
+++ b/sound/pci/echoaudio/mona_dsp.c
@@ -41,18 +41,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
{
int err;
- DE_INIT(("init_hw() - Mona\n"));
if (snd_BUG_ON((subdevice_id & 0xfff0) != MONA))
return -ENODEV;
- if ((err = init_dsp_comm_page(chip))) {
- DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+ err = init_dsp_comm_page(chip);
+ if (err) {
+ dev_err(chip->card->dev,
+ "init_hw - could not initialize DSP comm page\n");
return err;
}
chip->device_id = device_id;
chip->subdevice_id = subdevice_id;
- chip->bad_board = TRUE;
+ chip->bad_board = true;
chip->input_clock_types =
ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_ADAT;
@@ -67,11 +68,11 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
else
chip->dsp_code_to_load = FW_MONA_301_DSP;
- if ((err = load_firmware(chip)) < 0)
+ err = load_firmware(chip);
+ if (err < 0)
return err;
- chip->bad_board = FALSE;
+ chip->bad_board = false;
- DE_INIT(("init_hw done\n"));
return err;
}
@@ -80,8 +81,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
static int set_mixer_defaults(struct echoaudio *chip)
{
chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
- chip->professional_spdif = FALSE;
- chip->digital_in_automute = TRUE;
+ chip->professional_spdif = false;
+ chip->digital_in_automute = true;
return init_line_levels(chip);
}
@@ -149,7 +150,7 @@ static int load_asic(struct echoaudio *chip)
48 kHz, internal clock, S/PDIF RCA mode */
if (!err) {
control_reg = GML_CONVERTER_ENABLE | GML_48KHZ;
- err = write_control_reg(chip, control_reg, TRUE);
+ err = write_control_reg(chip, control_reg, true);
}
return err;
@@ -202,8 +203,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
/* Only set the clock for internal mode. */
if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
- DE_ACT(("set_sample_rate: Cannot set sample rate - "
- "clock not set to CLK_CLOCKININTERNAL\n"));
+ dev_dbg(chip->card->dev,
+ "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n");
/* Save the rate anyhow */
chip->comm_page->sample_rate = cpu_to_le32(rate);
chip->sample_rate = rate;
@@ -279,7 +280,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
clock = GML_8KHZ;
break;
default:
- DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+ dev_err(chip->card->dev,
+ "set_sample_rate: %d invalid!\n", rate);
return -EINVAL;
}
@@ -287,7 +289,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */
chip->sample_rate = rate;
- DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock));
+ dev_dbg(chip->card->dev,
+ "set_sample_rate: %d clock %d\n", rate, clock);
return write_control_reg(chip, control_reg, force_write);
}
@@ -299,12 +302,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
u32 control_reg, clocks_from_dsp;
int err;
- DE_ACT(("set_input_clock:\n"));
-
- /* Prevent two simultaneous calls to switch_asic() */
- if (atomic_read(&chip->opencount))
- return -EAGAIN;
-
/* Mask off the clock select bits */
control_reg = le32_to_cpu(chip->comm_page->control_register) &
GML_CLOCK_CLEAR_MASK;
@@ -312,7 +309,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
switch (clock) {
case ECHO_CLOCK_INTERNAL:
- DE_ACT(("Set Mona clock to INTERNAL\n"));
chip->input_clock = ECHO_CLOCK_INTERNAL;
return set_sample_rate(chip, chip->sample_rate);
case ECHO_CLOCK_SPDIF:
@@ -324,7 +320,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
spin_lock_irq(&chip->lock);
if (err < 0)
return err;
- DE_ACT(("Set Mona clock to SPDIF\n"));
control_reg |= GML_SPDIF_CLOCK;
if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96)
control_reg |= GML_DOUBLE_SPEED_MODE;
@@ -332,7 +327,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
control_reg &= ~GML_DOUBLE_SPEED_MODE;
break;
case ECHO_CLOCK_WORD:
- DE_ACT(("Set Mona clock to WORD\n"));
spin_unlock_irq(&chip->lock);
err = switch_asic(chip, clocks_from_dsp &
GML_CLOCK_DETECT_BIT_WORD96);
@@ -346,19 +340,20 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
control_reg &= ~GML_DOUBLE_SPEED_MODE;
break;
case ECHO_CLOCK_ADAT:
- DE_ACT(("Set Mona clock to ADAT\n"));
+ dev_dbg(chip->card->dev, "Set Mona clock to ADAT\n");
if (chip->digital_mode != DIGITAL_MODE_ADAT)
return -EAGAIN;
control_reg |= GML_ADAT_CLOCK;
control_reg &= ~GML_DOUBLE_SPEED_MODE;
break;
default:
- DE_ACT(("Input clock 0x%x not supported for Mona\n", clock));
+ dev_err(chip->card->dev,
+ "Input clock 0x%x not supported for Mona\n", clock);
return -EINVAL;
}
chip->input_clock = clock;
- return write_control_reg(chip, control_reg, TRUE);
+ return write_control_reg(chip, control_reg, true);
}
@@ -369,23 +364,24 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
int err, incompatible_clock;
/* Set clock to "internal" if it's not compatible with the new mode */
- incompatible_clock = FALSE;
+ incompatible_clock = false;
switch (mode) {
case DIGITAL_MODE_SPDIF_OPTICAL:
case DIGITAL_MODE_SPDIF_RCA:
if (chip->input_clock == ECHO_CLOCK_ADAT)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
break;
case DIGITAL_MODE_ADAT:
if (chip->input_clock == ECHO_CLOCK_SPDIF)
- incompatible_clock = TRUE;
+ incompatible_clock = true;
break;
default:
- DE_ACT(("Digital mode not supported: %d\n", mode));
+ dev_err(chip->card->dev,
+ "Digital mode not supported: %d\n", mode);
return -EINVAL;
}
- spin_lock_irq(&chip->lock);
+ guard(spinlock_irq)(&chip->lock);
if (incompatible_clock) { /* Switch to 48KHz, internal */
chip->sample_rate = 48000;
@@ -416,12 +412,11 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
break;
}
- err = write_control_reg(chip, control_reg, FALSE);
- spin_unlock_irq(&chip->lock);
+ err = write_control_reg(chip, control_reg, false);
if (err < 0)
return err;
chip->digital_mode = mode;
- DE_ACT(("set_digital_mode to %d\n", mode));
+ dev_dbg(chip->card->dev, "set_digital_mode to %d\n", mode);
return incompatible_clock;
}
diff --git a/sound/pci/emu10k1/Makefile b/sound/pci/emu10k1/Makefile
index fc5591e7777e..1f325abcb3ef 100644
--- a/sound/pci/emu10k1/Makefile
+++ b/sound/pci/emu10k1/Makefile
@@ -1,13 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-emu10k1-objs := emu10k1.o emu10k1_main.o \
+snd-emu10k1-y := emu10k1.o emu10k1_main.o \
irq.o memory.o voice.o emumpu401.o emupcm.o io.o \
- emuproc.o emumixer.o emufx.o timer.o p16v.o
-snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o
-snd-emu10k1x-objs := emu10k1x.o
+ emumixer.o emufx.o timer.o p16v.o
+snd-emu10k1-$(CONFIG_SND_PROC_FS) += emuproc.o
+snd-emu10k1-synth-y := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o
+snd-emu10k1x-y := emu10k1x.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o
diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
index aff8387c45cf..548e7d049901 100644
--- a/sound/pci/emu10k1/emu10k1.c
+++ b/sound/pci/emu10k1/emu10k1.c
@@ -1,32 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* The driver for the EMU10K1 (SB Live!) based soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
- * Added support for Audigy 2 Value.
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- *
+ * James Courtier-Dutton <James@superbug.co.uk>
*/
#include <linux/init.h>
#include <linux/pci.h>
+#include <linux/string.h>
#include <linux/time.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
#include <sound/initval.h>
@@ -34,25 +17,22 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("EMU10K1");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB Live!/PCI512/E-mu APS},"
- "{Creative Labs,SB Audigy}}");
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
#define ENABLE_SYNTH
#include <sound/emu10k1_synth.h>
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
static int extin[SNDRV_CARDS];
static int extout[SNDRV_CARDS];
static int seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4};
static int max_synth_voices[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 64};
static int max_buffer_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128};
-static int enable_ir[SNDRV_CARDS];
+static bool enable_ir[SNDRV_CARDS];
static uint subsystem[SNDRV_CARDS]; /* Force card subsystem model */
-static uint delay_pcm_irq[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the EMU10K1 soundcard.");
@@ -74,33 +54,20 @@ module_param_array(enable_ir, bool, NULL, 0444);
MODULE_PARM_DESC(enable_ir, "Enable IR.");
module_param_array(subsystem, uint, NULL, 0444);
MODULE_PARM_DESC(subsystem, "Force card subsystem model.");
-module_param_array(delay_pcm_irq, uint, NULL, 0444);
-MODULE_PARM_DESC(delay_pcm_irq, "Delay PCM interrupt by specified number of samples (default 0).");
/*
* Class 0401: 1102:0008 (rev 00) Subsystem: 1102:1001 -> Audigy2 Value Model:SB0400
*/
-static DEFINE_PCI_DEVICE_TABLE(snd_emu10k1_ids) = {
+static const struct pci_device_id snd_emu10k1_ids[] = {
{ PCI_VDEVICE(CREATIVE, 0x0002), 0 }, /* EMU10K1 */
{ PCI_VDEVICE(CREATIVE, 0x0004), 1 }, /* Audigy */
{ PCI_VDEVICE(CREATIVE, 0x0008), 1 }, /* Audigy 2 Value SB0400 */
{ 0, }
};
-/*
- * Audigy 2 Value notes:
- * A_IOCFG Input (GPIO)
- * 0x400 = Front analog jack plugged in. (Green socket)
- * 0x1000 = Read analog jack plugged in. (Black socket)
- * 0x2000 = Center/LFE analog jack plugged in. (Orange socket)
- * A_IOCFG Output (GPIO)
- * 0x60 = Sound out of front Left.
- * Win sets it to 0xXX61
- */
-
MODULE_DEVICE_TABLE(pci, snd_emu10k1_ids);
-static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_card_emu10k1_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -117,63 +84,78 @@ static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*emu), &card);
if (err < 0)
return err;
+ emu = card->private_data;
+
if (max_buffer_size[dev] < 32)
max_buffer_size[dev] = 32;
else if (max_buffer_size[dev] > 1024)
max_buffer_size[dev] = 1024;
- if ((err = snd_emu10k1_create(card, pci, extin[dev], extout[dev],
- (long)max_buffer_size[dev] * 1024 * 1024,
- enable_ir[dev], subsystem[dev],
- &emu)) < 0)
- goto error;
- card->private_data = emu;
- emu->delay_pcm_irq = delay_pcm_irq[dev] & 0x1f;
- if ((err = snd_emu10k1_pcm(emu, 0, NULL)) < 0)
- goto error;
- if ((err = snd_emu10k1_pcm_mic(emu, 1, NULL)) < 0)
- goto error;
- if ((err = snd_emu10k1_pcm_efx(emu, 2, NULL)) < 0)
- goto error;
+ err = snd_emu10k1_create(card, pci, extin[dev], extout[dev],
+ (long)max_buffer_size[dev] * 1024 * 1024,
+ enable_ir[dev], subsystem[dev]);
+ if (err < 0)
+ return err;
+ err = snd_emu10k1_pcm(emu, 0);
+ if (err < 0)
+ return err;
+ if (emu->card_capabilities->ac97_chip) {
+ err = snd_emu10k1_pcm_mic(emu, 1);
+ if (err < 0)
+ return err;
+ }
+ err = snd_emu10k1_pcm_efx(emu, 2);
+ if (err < 0)
+ return err;
/* This stores the periods table. */
if (emu->card_capabilities->ca0151_chip) { /* P16V */
- if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- 1024, &emu->p16v_buffer)) < 0)
- goto error;
+ emu->p16v_buffer =
+ snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV, 1024);
+ if (!emu->p16v_buffer)
+ return -ENOMEM;
}
- if ((err = snd_emu10k1_mixer(emu, 0, 3)) < 0)
- goto error;
+ err = snd_emu10k1_mixer(emu, 0, 3);
+ if (err < 0)
+ return err;
- if ((err = snd_emu10k1_timer(emu, 0)) < 0)
- goto error;
+ err = snd_emu10k1_timer(emu, 0);
+ if (err < 0)
+ return err;
- if ((err = snd_emu10k1_pcm_multi(emu, 3, NULL)) < 0)
- goto error;
+ err = snd_emu10k1_pcm_multi(emu, 3);
+ if (err < 0)
+ return err;
if (emu->card_capabilities->ca0151_chip) { /* P16V */
- if ((err = snd_p16v_pcm(emu, 4, NULL)) < 0)
- goto error;
+ err = snd_p16v_pcm(emu, 4);
+ if (err < 0)
+ return err;
}
if (emu->audigy) {
- if ((err = snd_emu10k1_audigy_midi(emu)) < 0)
- goto error;
+ err = snd_emu10k1_audigy_midi(emu);
+ if (err < 0)
+ return err;
} else {
- if ((err = snd_emu10k1_midi(emu)) < 0)
- goto error;
+ err = snd_emu10k1_midi(emu);
+ if (err < 0)
+ return err;
}
- if ((err = snd_emu10k1_fx8010_new(emu, 0, NULL)) < 0)
- goto error;
+ err = snd_emu10k1_fx8010_new(emu, 0);
+ if (err < 0)
+ return err;
#ifdef ENABLE_SYNTH
if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH,
sizeof(struct snd_emu10k1_synth_arg), &wave) < 0 ||
wave == NULL) {
- snd_printk(KERN_WARNING "can't initialize Emu10k1 wavetable synth\n");
+ dev_warn(emu->card->dev,
+ "can't initialize Emu10k1 wavetable synth\n");
} else {
struct snd_emu10k1_synth_arg *arg;
arg = SNDRV_SEQ_DEVICE_ARGPTR(wave);
- strcpy(wave->name, "Emu-10k1 Synth");
+ strscpy(wave->name, "Emu-10k1 Synth");
arg->hwptr = emu;
arg->index = 1;
arg->seq_ports = seq_ports[dev];
@@ -181,44 +163,34 @@ static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci,
}
#endif
- strcpy(card->driver, emu->card_capabilities->driver);
- strcpy(card->shortname, emu->card_capabilities->name);
+ strscpy(card->driver, emu->card_capabilities->driver,
+ sizeof(card->driver));
+ strscpy(card->shortname, emu->card_capabilities->name,
+ sizeof(card->shortname));
snprintf(card->longname, sizeof(card->longname),
"%s (rev.%d, serial:0x%x) at 0x%lx, irq %i",
card->shortname, emu->revision, emu->serial, emu->port, emu->irq);
- if ((err = snd_card_register(card)) < 0)
- goto error;
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
pci_set_drvdata(pci, card);
dev++;
return 0;
-
- error:
- snd_card_free(card);
- return err;
}
-static void __devexit snd_card_emu10k1_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
-}
-
-
-#ifdef CONFIG_PM
-static int snd_emu10k1_suspend(struct pci_dev *pci, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int snd_emu10k1_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_emu10k1 *emu = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(emu->pcm);
- snd_pcm_suspend_all(emu->pcm_mic);
- snd_pcm_suspend_all(emu->pcm_efx);
- snd_pcm_suspend_all(emu->pcm_multi);
- snd_pcm_suspend_all(emu->pcm_p16v);
+ emu->suspend = 1;
+
+ cancel_work_sync(&emu->emu1010.work);
snd_ac97_suspend(emu->ac97);
@@ -228,28 +200,14 @@ static int snd_emu10k1_suspend(struct pci_dev *pci, pm_message_t state)
snd_p16v_suspend(emu);
snd_emu10k1_done(emu);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_emu10k1_resume(struct pci_dev *pci)
+static int snd_emu10k1_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_emu10k1 *emu = card->private_data;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "emu10k1: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
snd_emu10k1_resume_init(emu);
snd_emu10k1_efx_resume(emu);
snd_ac97_resume(emu->ac97);
@@ -258,31 +216,26 @@ static int snd_emu10k1_resume(struct pci_dev *pci)
if (emu->card_capabilities->ca0151_chip)
snd_p16v_resume(emu);
+ emu->suspend = 0;
+
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
return 0;
}
-#endif
-static struct pci_driver driver = {
- .name = "EMU10K1_Audigy",
+static SIMPLE_DEV_PM_OPS(snd_emu10k1_pm, snd_emu10k1_suspend, snd_emu10k1_resume);
+#define SND_EMU10K1_PM_OPS &snd_emu10k1_pm
+#else
+#define SND_EMU10K1_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct pci_driver emu10k1_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_emu10k1_ids,
.probe = snd_card_emu10k1_probe,
- .remove = __devexit_p(snd_card_emu10k1_remove),
-#ifdef CONFIG_PM
- .suspend = snd_emu10k1_suspend,
- .resume = snd_emu10k1_resume,
-#endif
+ .driver = {
+ .pm = SND_EMU10K1_PM_OPS,
+ },
};
-static int __init alsa_card_emu10k1_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_emu10k1_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_emu10k1_init)
-module_exit(alsa_card_emu10k1_exit)
+module_pci_driver(emu10k1_driver);
diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c
index 7ef949d99a50..ef26e4d3e2a3 100644
--- a/sound/pci/emu10k1/emu10k1_callback.c
+++ b/sound/pci/emu10k1/emu10k1_callback.c
@@ -1,23 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* synth callback routines for Emu10k1
*
* Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/export.h>
#include "emu10k1_synth_local.h"
#include <sound/asoundef.h>
@@ -45,9 +33,9 @@ static void release_voice(struct snd_emux_voice *vp);
static void update_voice(struct snd_emux_voice *vp, int update);
static void terminate_voice(struct snd_emux_voice *vp);
static void free_voice(struct snd_emux_voice *vp);
-static void set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
-static void set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
-static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
+static u32 make_fmmod(struct snd_emux_voice *vp);
+static u32 make_fm2frq2(struct snd_emux_voice *vp);
+static int get_pitch_shift(struct snd_emux *emu);
/*
* Ensure a value is between two points
@@ -60,7 +48,7 @@ static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
/*
* set up operators
*/
-static struct snd_emux_operators emu10k1_ops = {
+static const struct snd_emux_operators emu10k1_ops = {
.owner = THIS_MODULE,
.get_voice = get_voice,
.prepare = start_voice,
@@ -71,6 +59,7 @@ static struct snd_emux_operators emu10k1_ops = {
.free_voice = free_voice,
.sample_new = snd_emu10k1_sample_new,
.sample_free = snd_emu10k1_sample_free,
+ .get_pitch_shift = get_pitch_shift,
};
void
@@ -84,6 +73,8 @@ snd_emu10k1_ops_setup(struct snd_emux *emux)
* get more voice for pcm
*
* terminate most inactive voice and give it as a pcm voice.
+ *
+ * voice_lock is already held.
*/
int
snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)
@@ -91,20 +82,19 @@ snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)
struct snd_emux *emu;
struct snd_emux_voice *vp;
struct best_voice best[V_END];
- unsigned long flags;
int i;
emu = hw->synth;
- spin_lock_irqsave(&emu->voice_lock, flags);
lookup_voices(emu, hw, best, 1); /* no OFF voices */
for (i = 0; i < V_END; i++) {
if (best[i].voice >= 0) {
int ch;
vp = &emu->voices[best[i].voice];
- if ((ch = vp->ch) < 0) {
+ ch = vp->ch;
+ if (ch < 0) {
/*
- printk(KERN_WARNING
+ dev_warn(emu->card->dev,
"synth_get_voice: ch < 0 (%d) ??", i);
*/
continue;
@@ -112,11 +102,9 @@ snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)
vp->emu->num_voices--;
vp->ch = -1;
vp->state = SNDRV_EMUX_ST_OFF;
- spin_unlock_irqrestore(&emu->voice_lock, flags);
return ch;
}
}
- spin_unlock_irqrestore(&emu->voice_lock, flags);
/* not found */
return -ENOMEM;
@@ -129,14 +117,13 @@ snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)
static void
release_voice(struct snd_emux_voice *vp)
{
- int dcysusv;
struct snd_emu10k1 *hw;
hw = vp->hw;
- dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;
- snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv);
- dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease | DCYSUSV_CHANNELENABLE_MASK;
- snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, dcysusv);
+ snd_emu10k1_ptr_write_multiple(hw, vp->ch,
+ DCYSUSM, (unsigned char)vp->reg.parm.modrelease | DCYSUSM_PHASE1_MASK,
+ DCYSUSV, (unsigned char)vp->reg.parm.volrelease | DCYSUSV_PHASE1_MASK | DCYSUSV_CHANNELENABLE_MASK,
+ REGLIST_END);
}
@@ -151,7 +138,13 @@ terminate_voice(struct snd_emux_voice *vp)
if (snd_BUG_ON(!vp))
return;
hw = vp->hw;
- snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
+ snd_emu10k1_ptr_write_multiple(hw, vp->ch,
+ DCYSUSV, 0,
+ VTFT, VTFT_FILTERTARGET_MASK,
+ CVCF, CVCF_CURRENTFILTER_MASK,
+ PTRX, 0,
+ CPF, 0,
+ REGLIST_END);
if (vp->block) {
struct snd_emu10k1_memblk *emem;
emem = (struct snd_emu10k1_memblk *)vp->block;
@@ -174,11 +167,6 @@ free_voice(struct snd_emux_voice *vp)
/* Problem apparent on plug, unplug then plug */
/* on the Audigy 2 ZS Notebook. */
if (hw && (vp->ch >= 0)) {
- snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00);
- snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
- // snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0);
- snd_emu10k1_ptr_write(hw, VTFT, vp->ch, 0xffff);
- snd_emu10k1_ptr_write(hw, CVCF, vp->ch, 0xffff);
snd_emu10k1_voice_free(hw, &hw->voices[vp->ch]);
vp->emu->num_voices--;
vp->ch = -1;
@@ -204,13 +192,13 @@ update_voice(struct snd_emux_voice *vp, int update)
snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_B, vp->ch, vp->aaux);
}
if (update & SNDRV_EMUX_UPDATE_FMMOD)
- set_fmmod(hw, vp);
+ snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, make_fmmod(vp));
if (update & SNDRV_EMUX_UPDATE_TREMFREQ)
snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)
- set_fm2frq2(hw, vp);
+ snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, make_fm2frq2(vp));
if (update & SNDRV_EMUX_UPDATE_Q)
- set_filterQ(hw, vp);
+ snd_emu10k1_ptr_write(hw, CCCA_RESONANCE, vp->ch, vp->reg.parm.filterQ);
}
@@ -227,7 +215,7 @@ lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw,
int i;
for (i = 0; i < V_END; i++) {
- best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */;
+ best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */
best[i].voice = -1;
}
@@ -267,7 +255,7 @@ lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw,
/* check if sample is finished playing (non-looping only) */
if (bp != best + V_OFF && bp != best + V_FREE &&
(vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {
- val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch);
+ val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch) - 64 + 3;
if (val >= vp->reg.loopstart)
bp = best + V_OFF;
}
@@ -301,7 +289,7 @@ get_voice(struct snd_emux *emu, struct snd_emux_port *port)
if (vp->ch < 0) {
/* allocate a voice */
struct snd_emu10k1_voice *hwvoice;
- if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, &hwvoice) < 0 || hwvoice == NULL)
+ if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, 1, NULL, &hwvoice) < 0)
continue;
vp->ch = hwvoice->number;
emu->num_voices++;
@@ -322,6 +310,8 @@ start_voice(struct snd_emux_voice *vp)
{
unsigned int temp;
int ch;
+ bool w_16;
+ u32 psst, dsl, map, ccca, vtarget;
unsigned int addr, mapped_offset;
struct snd_midi_channel *chan;
struct snd_emu10k1 *hw;
@@ -332,16 +322,17 @@ start_voice(struct snd_emux_voice *vp)
if (snd_BUG_ON(ch < 0))
return -EINVAL;
chan = vp->chan;
+ w_16 = !(vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS);
emem = (struct snd_emu10k1_memblk *)vp->block;
if (emem == NULL)
return -EINVAL;
emem->map_locked++;
if (snd_emu10k1_memblk_map(hw, emem) < 0) {
- /* printk(KERN_ERR "emu: cannot map!\n"); */
+ /* dev_err(hw->card->devK, "emu: cannot map!\n"); */
return -ENOMEM;
}
- mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1;
+ mapped_offset = snd_emu10k1_memblk_offset(emem) >> w_16;
vp->reg.start += mapped_offset;
vp->reg.end += mapped_offset;
vp->reg.loopstart += mapped_offset;
@@ -359,114 +350,98 @@ start_voice(struct snd_emux_voice *vp)
snd_emu10k1_ptr_write(hw, FXRT, ch, temp);
}
- /* channel to be silent and idle */
- snd_emu10k1_ptr_write(hw, DCYSUSV, ch, 0x0000);
- snd_emu10k1_ptr_write(hw, VTFT, ch, 0x0000FFFF);
- snd_emu10k1_ptr_write(hw, CVCF, ch, 0x0000FFFF);
- snd_emu10k1_ptr_write(hw, PTRX, ch, 0);
- snd_emu10k1_ptr_write(hw, CPF, ch, 0);
-
- /* set pitch offset */
- snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);
-
- /* set envelope parameters */
- snd_emu10k1_ptr_write(hw, ENVVAL, ch, vp->reg.parm.moddelay);
- snd_emu10k1_ptr_write(hw, ATKHLDM, ch, vp->reg.parm.modatkhld);
- snd_emu10k1_ptr_write(hw, DCYSUSM, ch, vp->reg.parm.moddcysus);
- snd_emu10k1_ptr_write(hw, ENVVOL, ch, vp->reg.parm.voldelay);
- snd_emu10k1_ptr_write(hw, ATKHLDV, ch, vp->reg.parm.volatkhld);
- /* decay/sustain parameter for volume envelope is used
- for triggerg the voice */
-
- /* cutoff and volume */
- temp = (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol;
- snd_emu10k1_ptr_write(hw, IFATN, vp->ch, temp);
-
- /* modulation envelope heights */
- snd_emu10k1_ptr_write(hw, PEFE, ch, vp->reg.parm.pefe);
-
- /* lfo1/2 delay */
- snd_emu10k1_ptr_write(hw, LFOVAL1, ch, vp->reg.parm.lfo1delay);
- snd_emu10k1_ptr_write(hw, LFOVAL2, ch, vp->reg.parm.lfo2delay);
-
- /* lfo1 pitch & cutoff shift */
- set_fmmod(hw, vp);
- /* lfo1 volume & freq */
- snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
- /* lfo2 pitch & freq */
- set_fm2frq2(hw, vp);
-
- /* reverb and loop start (reverb 8bit, MSB) */
temp = vp->reg.parm.reverb;
temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;
LIMITMAX(temp, 255);
addr = vp->reg.loopstart;
- snd_emu10k1_ptr_write(hw, PSST, vp->ch, (temp << 24) | addr);
+ psst = (temp << 24) | addr;
- /* chorus & loop end (chorus 8bit, MSB) */
addr = vp->reg.loopend;
temp = vp->reg.parm.chorus;
temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;
LIMITMAX(temp, 255);
- temp = (temp <<24) | addr;
- snd_emu10k1_ptr_write(hw, DSL, ch, temp);
+ dsl = (temp << 24) | addr;
- /* clear filter delay memory */
- snd_emu10k1_ptr_write(hw, Z1, ch, 0);
- snd_emu10k1_ptr_write(hw, Z2, ch, 0);
-
- /* invalidate maps */
- temp = (hw->silent_page.addr << 1) | MAP_PTI_MASK;
- snd_emu10k1_ptr_write(hw, MAPA, ch, temp);
- snd_emu10k1_ptr_write(hw, MAPB, ch, temp);
-#if 0
- /* cache */
- {
- unsigned int val, sample;
- val = 32;
- if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
- sample = 0x80808080;
- else {
- sample = 0;
- val *= 2;
- }
-
- /* cache */
- snd_emu10k1_ptr_write(hw, CCR, ch, 0x1c << 16);
- snd_emu10k1_ptr_write(hw, CDE, ch, sample);
- snd_emu10k1_ptr_write(hw, CDF, ch, sample);
-
- /* invalidate maps */
- temp = ((unsigned int)hw->silent_page.addr << 1) | MAP_PTI_MASK;
- snd_emu10k1_ptr_write(hw, MAPA, ch, temp);
- snd_emu10k1_ptr_write(hw, MAPB, ch, temp);
-
- /* fill cache */
- val -= 4;
- val <<= 25;
- val |= 0x1c << 16;
- snd_emu10k1_ptr_write(hw, CCR, ch, val);
- }
-#endif
+ map = (hw->silent_page.addr << hw->address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
- /* Q & current address (Q 4bit value, MSB) */
- addr = vp->reg.start;
+ addr = vp->reg.start + 64 - 3;
temp = vp->reg.parm.filterQ;
- temp = (temp<<28) | addr;
+ ccca = (temp << 28) | addr;
if (vp->apitch < 0xe400)
- temp |= CCCA_INTERPROM_0;
+ ccca |= CCCA_INTERPROM_0;
else {
unsigned int shift = (vp->apitch - 0xe000) >> 10;
- temp |= shift << 25;
+ ccca |= shift << 25;
}
- if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
- temp |= CCCA_8BITSELECT;
- snd_emu10k1_ptr_write(hw, CCCA, ch, temp);
-
- /* reset volume */
- temp = (unsigned int)vp->vtarget << 16;
- snd_emu10k1_ptr_write(hw, VTFT, ch, temp | vp->ftarget);
- snd_emu10k1_ptr_write(hw, CVCF, ch, temp | 0xff00);
+ if (!w_16)
+ ccca |= CCCA_8BITSELECT;
+
+ vtarget = (unsigned int)vp->vtarget << 16;
+
+ snd_emu10k1_ptr_write_multiple(hw, ch,
+ /* channel to be silent and idle */
+ DCYSUSV, 0,
+ VTFT, VTFT_FILTERTARGET_MASK,
+ CVCF, CVCF_CURRENTFILTER_MASK,
+ PTRX, 0,
+ CPF, 0,
+
+ /* set pitch offset */
+ IP, vp->apitch,
+
+ /* set envelope parameters */
+ ENVVAL, vp->reg.parm.moddelay,
+ ATKHLDM, vp->reg.parm.modatkhld,
+ DCYSUSM, vp->reg.parm.moddcysus,
+ ENVVOL, vp->reg.parm.voldelay,
+ ATKHLDV, vp->reg.parm.volatkhld,
+ /* decay/sustain parameter for volume envelope is used
+ for triggerg the voice */
+
+ /* cutoff and volume */
+ IFATN, (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol,
+
+ /* modulation envelope heights */
+ PEFE, vp->reg.parm.pefe,
+
+ /* lfo1/2 delay */
+ LFOVAL1, vp->reg.parm.lfo1delay,
+ LFOVAL2, vp->reg.parm.lfo2delay,
+
+ /* lfo1 pitch & cutoff shift */
+ FMMOD, make_fmmod(vp),
+ /* lfo1 volume & freq */
+ TREMFRQ, vp->reg.parm.tremfrq,
+ /* lfo2 pitch & freq */
+ FM2FRQ2, make_fm2frq2(vp),
+
+ /* reverb and loop start (reverb 8bit, MSB) */
+ PSST, psst,
+
+ /* chorus & loop end (chorus 8bit, MSB) */
+ DSL, dsl,
+
+ /* clear filter delay memory */
+ Z1, 0,
+ Z2, 0,
+
+ /* invalidate maps */
+ MAPA, map,
+ MAPB, map,
+
+ /* Q & current address (Q 4bit value, MSB) */
+ CCCA, ccca,
+
+ /* cache */
+ CCR, REG_VAL_PUT(CCR_CACHEINVALIDSIZE, 64),
+
+ /* reset volume */
+ VTFT, vtarget | vp->ftarget,
+ CVCF, vtarget | CVCF_CURRENTFILTER_MASK,
+
+ REGLIST_END);
+
+ hw->voices[ch].dirty = 1;
return 0;
}
@@ -476,7 +451,7 @@ start_voice(struct snd_emux_voice *vp)
static void
trigger_voice(struct snd_emux_voice *vp)
{
- unsigned int temp, ptarget;
+ unsigned int ptarget;
struct snd_emu10k1 *hw;
struct snd_emu10k1_memblk *emem;
@@ -491,24 +466,25 @@ trigger_voice(struct snd_emux_voice *vp)
#else
ptarget = IP_TO_CP(vp->apitch);
#endif
- /* set pitch target and pan (volume) */
- temp = ptarget | (vp->apan << 8) | vp->aaux;
- snd_emu10k1_ptr_write(hw, PTRX, vp->ch, temp);
+ snd_emu10k1_ptr_write_multiple(hw, vp->ch,
+ /* set pitch target and pan (volume) */
+ PTRX, ptarget | (vp->apan << 8) | vp->aaux,
+
+ /* current pitch and fractional address */
+ CPF, ptarget,
- /* pitch target */
- snd_emu10k1_ptr_write(hw, CPF, vp->ch, ptarget);
+ /* enable envelope engine */
+ DCYSUSV, vp->reg.parm.voldcysus | DCYSUSV_CHANNELENABLE_MASK,
- /* trigger voice */
- snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, vp->reg.parm.voldcysus|DCYSUSV_CHANNELENABLE_MASK);
+ REGLIST_END);
}
#define MOD_SENSE 18
-/* set lfo1 modulation height and cutoff */
-static void
-set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
+/* calculate lfo1 modulation height and cutoff register */
+static u32
+make_fmmod(struct snd_emux_voice *vp)
{
- unsigned short fmmod;
short pitch;
unsigned char cutoff;
int modulation;
@@ -518,15 +494,13 @@ set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
pitch += (MOD_SENSE * modulation) / 1200;
LIMITVALUE(pitch, -128, 127);
- fmmod = ((unsigned char)pitch<<8) | cutoff;
- snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, fmmod);
+ return ((unsigned char)pitch << 8) | cutoff;
}
-/* set lfo2 pitch & frequency */
-static void
-set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
+/* calculate set lfo2 pitch & frequency register */
+static u32
+make_fm2frq2(struct snd_emux_voice *vp)
{
- unsigned short fm2frq2;
short pitch;
unsigned char freq;
int modulation;
@@ -536,16 +510,13 @@ set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
pitch += (MOD_SENSE * modulation) / 1200;
LIMITVALUE(pitch, -128, 127);
- fm2frq2 = ((unsigned char)pitch<<8) | freq;
- snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, fm2frq2);
+ return ((unsigned char)pitch << 8) | freq;
}
-/* set filterQ */
-static void
-set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
+static int get_pitch_shift(struct snd_emux *emu)
{
- unsigned int val;
- val = snd_emu10k1_ptr_read(hw, CCCA, vp->ch) & ~CCCA_RESONANCE;
- val |= (vp->reg.parm.filterQ << 28);
- snd_emu10k1_ptr_write(hw, CCCA, vp->ch, val);
+ struct snd_emu10k1 *hw = emu->hw;
+
+ return (hw->card_capabilities->emu_model &&
+ hw->emu1010.word_clock == 44100) ? 0 : -501;
}
diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
index 66c7fb3ced3e..b2fe2d164ba8 100644
--- a/sound/pci/emu10k1/emu10k1_main.c
+++ b/sound/pci/emu10k1/emu10k1_main.c
@@ -1,41 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * James Courtier-Dutton <James@superbug.co.uk>
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
* Creative Labs, Inc.
- * Routines for control of EMU10K1 chips
- *
- * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
- * Added support for Audigy 2 Value.
- * Added EMU 1010 support.
- * General bug fixes and enhancements.
- *
- *
- * BUGS:
- * --
- *
- * TODO:
- * --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
+ * Routines for control of EMU10K1 chips
*/
#include <linux/sched.h>
-#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/module.h>
#include <linux/interrupt.h>
+#include <linux/iommu.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
@@ -71,50 +49,53 @@ MODULE_FIRMWARE(EMU1010_NOTEBOOK_FILENAME);
void snd_emu10k1_voice_init(struct snd_emu10k1 *emu, int ch)
{
- snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0);
- snd_emu10k1_ptr_write(emu, IP, ch, 0);
- snd_emu10k1_ptr_write(emu, VTFT, ch, 0xffff);
- snd_emu10k1_ptr_write(emu, CVCF, ch, 0xffff);
- snd_emu10k1_ptr_write(emu, PTRX, ch, 0);
- snd_emu10k1_ptr_write(emu, CPF, ch, 0);
- snd_emu10k1_ptr_write(emu, CCR, ch, 0);
-
- snd_emu10k1_ptr_write(emu, PSST, ch, 0);
- snd_emu10k1_ptr_write(emu, DSL, ch, 0x10);
- snd_emu10k1_ptr_write(emu, CCCA, ch, 0);
- snd_emu10k1_ptr_write(emu, Z1, ch, 0);
- snd_emu10k1_ptr_write(emu, Z2, ch, 0);
- snd_emu10k1_ptr_write(emu, FXRT, ch, 0x32100000);
-
- snd_emu10k1_ptr_write(emu, ATKHLDM, ch, 0);
- snd_emu10k1_ptr_write(emu, DCYSUSM, ch, 0);
- snd_emu10k1_ptr_write(emu, IFATN, ch, 0xffff);
- snd_emu10k1_ptr_write(emu, PEFE, ch, 0);
- snd_emu10k1_ptr_write(emu, FMMOD, ch, 0);
- snd_emu10k1_ptr_write(emu, TREMFRQ, ch, 24); /* 1 Hz */
- snd_emu10k1_ptr_write(emu, FM2FRQ2, ch, 24); /* 1 Hz */
- snd_emu10k1_ptr_write(emu, TEMPENV, ch, 0);
-
- /*** these are last so OFF prevents writing ***/
- snd_emu10k1_ptr_write(emu, LFOVAL2, ch, 0);
- snd_emu10k1_ptr_write(emu, LFOVAL1, ch, 0);
- snd_emu10k1_ptr_write(emu, ATKHLDV, ch, 0);
- snd_emu10k1_ptr_write(emu, ENVVOL, ch, 0);
- snd_emu10k1_ptr_write(emu, ENVVAL, ch, 0);
+ snd_emu10k1_ptr_write_multiple(emu, ch,
+ DCYSUSV, 0,
+ VTFT, VTFT_FILTERTARGET_MASK,
+ CVCF, CVCF_CURRENTFILTER_MASK,
+ PTRX, 0,
+ CPF, 0,
+ CCR, 0,
+
+ PSST, 0,
+ DSL, 0x10,
+ CCCA, 0,
+ Z1, 0,
+ Z2, 0,
+ FXRT, 0x32100000,
+
+ // The rest is meaningless as long as DCYSUSV_CHANNELENABLE_MASK is zero
+ DCYSUSM, 0,
+ ATKHLDV, 0,
+ ATKHLDM, 0,
+ IP, 0,
+ IFATN, IFATN_FILTERCUTOFF_MASK | IFATN_ATTENUATION_MASK,
+ PEFE, 0,
+ FMMOD, 0,
+ TREMFRQ, 24, /* 1 Hz */
+ FM2FRQ2, 24, /* 1 Hz */
+ LFOVAL2, 0,
+ LFOVAL1, 0,
+ ENVVOL, 0,
+ ENVVAL, 0,
+
+ REGLIST_END);
/* Audigy extra stuffs */
if (emu->audigy) {
- snd_emu10k1_ptr_write(emu, 0x4c, ch, 0); /* ?? */
- snd_emu10k1_ptr_write(emu, 0x4d, ch, 0); /* ?? */
- snd_emu10k1_ptr_write(emu, 0x4e, ch, 0); /* ?? */
- snd_emu10k1_ptr_write(emu, 0x4f, ch, 0); /* ?? */
- snd_emu10k1_ptr_write(emu, A_FXRT1, ch, 0x03020100);
- snd_emu10k1_ptr_write(emu, A_FXRT2, ch, 0x3f3f3f3f);
- snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, ch, 0);
+ snd_emu10k1_ptr_write_multiple(emu, ch,
+ A_CSBA, 0,
+ A_CSDC, 0,
+ A_CSFE, 0,
+ A_CSHG, 0,
+ A_FXRT1, 0x03020100,
+ A_FXRT2, 0x07060504,
+ A_SENDAMOUNTS, 0,
+ REGLIST_END);
}
}
-static unsigned int spi_dac_init[] = {
+static const unsigned int spi_dac_init[] = {
0x00ff,
0x02ff,
0x0400,
@@ -138,7 +119,7 @@ static unsigned int spi_dac_init[] = {
0x1400,
};
-static unsigned int i2c_adc_init[][2] = {
+static const unsigned int i2c_adc_init[][2] = {
{ 0x17, 0x00 }, /* Reset */
{ 0x07, 0x00 }, /* Timeout */
{ 0x0b, 0x22 }, /* Interface control */
@@ -154,7 +135,7 @@ static unsigned int i2c_adc_init[][2] = {
{ 0x15, ADC_MUX_2 }, /* ADC Mixer control. Mic for A2ZS Notebook */
};
-static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
+static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir)
{
unsigned int silent_page;
int ch;
@@ -164,20 +145,26 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK |
HCFG_MUTEBUTTONENABLE, emu->port + HCFG);
- /* reset recording buffers */
- snd_emu10k1_ptr_write(emu, MICBS, 0, ADCBS_BUFSIZE_NONE);
- snd_emu10k1_ptr_write(emu, MICBA, 0, 0);
- snd_emu10k1_ptr_write(emu, FXBS, 0, ADCBS_BUFSIZE_NONE);
- snd_emu10k1_ptr_write(emu, FXBA, 0, 0);
- snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE);
- snd_emu10k1_ptr_write(emu, ADCBA, 0, 0);
-
- /* disable channel interrupt */
outl(0, emu->port + INTE);
- snd_emu10k1_ptr_write(emu, CLIEL, 0, 0);
- snd_emu10k1_ptr_write(emu, CLIEH, 0, 0);
- snd_emu10k1_ptr_write(emu, SOLEL, 0, 0);
- snd_emu10k1_ptr_write(emu, SOLEH, 0, 0);
+
+ snd_emu10k1_ptr_write_multiple(emu, 0,
+ /* reset recording buffers */
+ MICBS, ADCBS_BUFSIZE_NONE,
+ MICBA, 0,
+ FXBS, ADCBS_BUFSIZE_NONE,
+ FXBA, 0,
+ ADCBS, ADCBS_BUFSIZE_NONE,
+ ADCBA, 0,
+
+ /* disable channel interrupt */
+ CLIEL, 0,
+ CLIEH, 0,
+
+ /* disable stop on loop end */
+ SOLEL, 0,
+ SOLEH, 0,
+
+ REGLIST_END);
if (emu->audigy) {
/* set SPDIF bypass mode */
@@ -191,17 +178,17 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
for (ch = 0; ch < NUM_G; ch++)
snd_emu10k1_voice_init(emu, ch);
- snd_emu10k1_ptr_write(emu, SPCS0, 0, emu->spdif_bits[0]);
- snd_emu10k1_ptr_write(emu, SPCS1, 0, emu->spdif_bits[1]);
- snd_emu10k1_ptr_write(emu, SPCS2, 0, emu->spdif_bits[2]);
+ snd_emu10k1_ptr_write_multiple(emu, 0,
+ SPCS0, emu->spdif_bits[0],
+ SPCS1, emu->spdif_bits[1],
+ SPCS2, emu->spdif_bits[2],
+ REGLIST_END);
- if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
+ if (emu->card_capabilities->emu_model) {
+ } else if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
/* Hacks for Alice3 to work independent of haP16V driver */
/* Setup SRCMulti_I2S SamplingRate */
- tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
- tmp &= 0xfffff1ff;
- tmp |= (0x2<<9);
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
+ snd_emu10k1_ptr_write(emu, A_I2S_CAPTURE_RATE, 0, A_I2S_CAPTURE_96000);
/* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
snd_emu10k1_ptr20_write(emu, SRCSel, 0, 0x14);
@@ -213,32 +200,26 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
outl(0x0201, emu->port + HCFG2);
/* Set playback routing. */
snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, 0x78e4);
- }
- if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */
+ } else if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */
/* Hacks for Alice3 to work independent of haP16V driver */
- snd_printk(KERN_INFO "Audigy2 value: Special config.\n");
+ dev_info(emu->card->dev, "Audigy2 value: Special config.\n");
/* Setup SRCMulti_I2S SamplingRate */
- tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
- tmp &= 0xfffff1ff;
- tmp |= (0x2<<9);
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
+ snd_emu10k1_ptr_write(emu, A_I2S_CAPTURE_RATE, 0, A_I2S_CAPTURE_96000);
/* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
- outl(0x600000, emu->port + 0x20);
- outl(0x14, emu->port + 0x24);
+ snd_emu10k1_ptr20_write(emu, P17V_SRCSel, 0, 0x14);
/* Setup SRCMulti Input Audio Enable */
- outl(0x7b0000, emu->port + 0x20);
- outl(0xFF000000, emu->port + 0x24);
+ snd_emu10k1_ptr20_write(emu, P17V_MIXER_I2S_ENABLE, 0, 0xFF000000);
/* Setup SPDIF Out Audio Enable */
/* The Audigy 2 Value has a separate SPDIF out,
* so no need for a mixer switch
*/
- outl(0x7a0000, emu->port + 0x20);
- outl(0xFF000000, emu->port + 0x24);
- tmp = inl(emu->port + A_IOCFG) & ~0x8; /* Clear bit 3 */
- outl(tmp, emu->port + A_IOCFG);
+ snd_emu10k1_ptr20_write(emu, P17V_MIXER_SPDIF_ENABLE, 0, 0xFF000000);
+
+ tmp = inw(emu->port + A_IOCFG) & ~0x8; /* Clear bit 3 */
+ outw(tmp, emu->port + A_IOCFG);
}
if (emu->card_capabilities->spi_dac) { /* Audigy 2 ZS Notebook with DAC Wolfson WM8768/WM8568 */
int size, n;
@@ -258,15 +239,15 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
* GPIO6: Unknown
* GPIO7: Unknown
*/
- outl(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */
+ outw(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */
}
if (emu->card_capabilities->i2c_adc) { /* Audigy 2 ZS Notebook with ADC Wolfson WM8775 */
int size, n;
snd_emu10k1_ptr20_write(emu, P17V_I2S_SRC_SEL, 0, 0x2020205f);
- tmp = inl(emu->port + A_IOCFG);
- outl(tmp | 0x4, emu->port + A_IOCFG); /* Set bit 2 for mic input */
- tmp = inl(emu->port + A_IOCFG);
+ tmp = inw(emu->port + A_IOCFG);
+ outw(tmp | 0x4, emu->port + A_IOCFG); /* Set bit 2 for mic input */
+ tmp = inw(emu->port + A_IOCFG);
size = ARRAY_SIZE(i2c_adc_init);
for (n = 0; n < size; n++)
snd_emu10k1_i2c_write(emu, i2c_adc_init[n][0], i2c_adc_init[n][1]);
@@ -279,9 +260,9 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages.addr);
snd_emu10k1_ptr_write(emu, TCB, 0, 0); /* taken from original driver */
- snd_emu10k1_ptr_write(emu, TCBS, 0, 4); /* taken from original driver */
+ snd_emu10k1_ptr_write(emu, TCBS, 0, TCBS_BUFFSIZE_256K); /* taken from original driver */
- silent_page = (emu->silent_page.addr << 1) | MAP_PTI_MASK;
+ silent_page = (emu->silent_page.addr << emu->address_mode) | (emu->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
for (ch = 0; ch < NUM_G; ch++) {
snd_emu10k1_ptr_write(emu, MAPA, ch, silent_page);
snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page);
@@ -322,12 +303,12 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
} else if (emu->card_capabilities->i2c_adc) {
; /* Disable A_IOCFG for Audigy 2 ZS Notebook */
} else if (emu->audigy) {
- unsigned int reg = inl(emu->port + A_IOCFG);
- outl(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
+ u16 reg = inw(emu->port + A_IOCFG);
+ outw(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
udelay(500);
- outl(reg | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
+ outw(reg | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
udelay(100);
- outl(reg, emu->port + A_IOCFG);
+ outw(reg, emu->port + A_IOCFG);
} else {
unsigned int reg = inl(emu->port + HCFG);
outl(reg | HCFG_GPOUT2, emu->port + HCFG);
@@ -343,8 +324,13 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
} else if (emu->card_capabilities->i2c_adc) {
; /* Disable A_IOCFG for Audigy 2 ZS Notebook */
} else if (emu->audigy) { /* enable analog output */
- unsigned int reg = inl(emu->port + A_IOCFG);
- outl(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG);
+ u16 reg = inw(emu->port + A_IOCFG);
+ outw(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG);
+ }
+
+ if (emu->address_mode == 0) {
+ /* use 16M in 4G */
+ outl(inl(emu->port + HCFG) | HCFG_EXPANDED_MEM, emu->port + HCFG);
}
return 0;
@@ -363,19 +349,19 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
} else if (emu->card_capabilities->i2c_adc) {
; /* Disable A_IOCFG for Audigy 2 ZS Notebook */
} else if (emu->audigy) {
- outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG);
+ outw(inw(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG);
if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
/* Unmute Analog now. Set GPO6 to 1 for Apollo.
* This has to be done after init ALice3 I2SOut beyond 48KHz.
* So, sequence is important. */
- outl(inl(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG);
+ outw(inw(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG);
} else if (emu->card_capabilities->ca0108_chip) { /* audigy2 value */
/* Unmute Analog now. */
- outl(inl(emu->port + A_IOCFG) | 0x0060, emu->port + A_IOCFG);
+ outw(inw(emu->port + A_IOCFG) | 0x0060, emu->port + A_IOCFG);
} else {
/* Disable routing from AC97 line out to Front speakers */
- outl(inl(emu->port + A_IOCFG) | 0x0080, emu->port + A_IOCFG);
+ outw(inw(emu->port + A_IOCFG) | 0x0080, emu->port + A_IOCFG);
}
}
@@ -397,7 +383,10 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
}
#endif
- snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE);
+ if (emu->card_capabilities->emu_model)
+ snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE | INTE_A_GPIOENABLE);
+ else
+ snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE);
}
int snd_emu10k1_done(struct snd_emu10k1 *emu)
@@ -407,41 +396,48 @@ int snd_emu10k1_done(struct snd_emu10k1 *emu)
outl(0, emu->port + INTE);
/*
- * Shutdown the chip
+ * Shutdown the voices
*/
- for (ch = 0; ch < NUM_G; ch++)
- snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0);
for (ch = 0; ch < NUM_G; ch++) {
- snd_emu10k1_ptr_write(emu, VTFT, ch, 0);
- snd_emu10k1_ptr_write(emu, CVCF, ch, 0);
- snd_emu10k1_ptr_write(emu, PTRX, ch, 0);
- snd_emu10k1_ptr_write(emu, CPF, ch, 0);
+ snd_emu10k1_ptr_write_multiple(emu, ch,
+ DCYSUSV, 0,
+ VTFT, 0,
+ CVCF, 0,
+ PTRX, 0,
+ CPF, 0,
+ REGLIST_END);
}
- /* reset recording buffers */
- snd_emu10k1_ptr_write(emu, MICBS, 0, 0);
- snd_emu10k1_ptr_write(emu, MICBA, 0, 0);
- snd_emu10k1_ptr_write(emu, FXBS, 0, 0);
- snd_emu10k1_ptr_write(emu, FXBA, 0, 0);
- snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
- snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE);
- snd_emu10k1_ptr_write(emu, ADCBA, 0, 0);
- snd_emu10k1_ptr_write(emu, TCBS, 0, TCBS_BUFFSIZE_16K);
- snd_emu10k1_ptr_write(emu, TCB, 0, 0);
+ // stop the DSP
if (emu->audigy)
snd_emu10k1_ptr_write(emu, A_DBG, 0, A_DBG_SINGLE_STEP);
else
snd_emu10k1_ptr_write(emu, DBG, 0, EMU10K1_DBG_SINGLE_STEP);
- /* disable channel interrupt */
- snd_emu10k1_ptr_write(emu, CLIEL, 0, 0);
- snd_emu10k1_ptr_write(emu, CLIEH, 0, 0);
- snd_emu10k1_ptr_write(emu, SOLEL, 0, 0);
- snd_emu10k1_ptr_write(emu, SOLEH, 0, 0);
+ snd_emu10k1_ptr_write_multiple(emu, 0,
+ /* reset recording buffers */
+ MICBS, 0,
+ MICBA, 0,
+ FXBS, 0,
+ FXBA, 0,
+ FXWC, 0,
+ ADCBS, ADCBS_BUFSIZE_NONE,
+ ADCBA, 0,
+ TCBS, TCBS_BUFFSIZE_16K,
+ TCB, 0,
+
+ /* disable channel interrupt */
+ CLIEL, 0,
+ CLIEH, 0,
+ SOLEL, 0,
+ SOLEH, 0,
+
+ PTB, 0,
+
+ REGLIST_END);
/* disable audio and lock cache */
outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG);
- snd_emu10k1_ptr_write(emu, PTB, 0, 0);
return 0;
}
@@ -632,7 +628,7 @@ static int snd_emu10k1_ecard_init(struct snd_emu10k1 *emu)
static int snd_emu10k1_cardbus_init(struct snd_emu10k1 *emu)
{
unsigned long special_port;
- unsigned int value;
+ __always_unused unsigned int value;
/* Special initialisation routine
* before the rest of the IO-Ports become active.
@@ -656,144 +652,148 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 *emu)
return 0;
}
-static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu, const char *filename)
+/* firmware file names, per model, init-fw and dock-fw (optional) */
+static const char * const firmware_names[5][2] = {
+ [EMU_MODEL_EMU1010] = {
+ HANA_FILENAME, DOCK_FILENAME
+ },
+ [EMU_MODEL_EMU1010B] = {
+ EMU1010B_FILENAME, MICRO_DOCK_FILENAME
+ },
+ [EMU_MODEL_EMU1616] = {
+ EMU1010_NOTEBOOK_FILENAME, MICRO_DOCK_FILENAME
+ },
+ [EMU_MODEL_EMU0404] = {
+ EMU0404_FILENAME, NULL
+ },
+};
+
+static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu, int dock,
+ const struct firmware **fw)
{
+ const char *filename;
int err;
- int n, i;
- int reg;
- int value;
- unsigned int write_post;
- unsigned long flags;
- const struct firmware *fw_entry;
-
- err = request_firmware(&fw_entry, filename, &emu->pci->dev);
- if (err != 0) {
- snd_printk(KERN_ERR "firmware: %s not found. Err = %d\n", filename, err);
- return err;
- }
- snd_printk(KERN_INFO "firmware size = 0x%zx\n", fw_entry->size);
- /* The FPGA is a Xilinx Spartan IIE XC2S50E */
- /* GPIO7 -> FPGA PGMN
- * GPIO6 -> FPGA CCLK
- * GPIO5 -> FPGA DIN
- * FPGA CONFIG OFF -> FPGA PGMN
- */
- spin_lock_irqsave(&emu->emu_lock, flags);
- outl(0x00, emu->port + A_IOCFG); /* Set PGMN low for 1uS. */
- write_post = inl(emu->port + A_IOCFG);
- udelay(100);
- outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */
- write_post = inl(emu->port + A_IOCFG);
- udelay(100); /* Allow FPGA memory to clean */
- for (n = 0; n < fw_entry->size; n++) {
- value = fw_entry->data[n];
- for (i = 0; i < 8; i++) {
- reg = 0x80;
- if (value & 0x1)
- reg = reg | 0x20;
- value = value >> 1;
- outl(reg, emu->port + A_IOCFG);
- write_post = inl(emu->port + A_IOCFG);
- outl(reg | 0x40, emu->port + A_IOCFG);
- write_post = inl(emu->port + A_IOCFG);
- }
+ if (!*fw) {
+ filename = firmware_names[emu->card_capabilities->emu_model][dock];
+ if (!filename)
+ return 0;
+ err = request_firmware(fw, filename, &emu->pci->dev);
+ if (err)
+ return err;
}
- /* After programming, set GPIO bit 4 high again. */
- outl(0x10, emu->port + A_IOCFG);
- write_post = inl(emu->port + A_IOCFG);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- release_firmware(fw_entry);
+ snd_emu1010_load_firmware_entry(emu, dock, *fw);
return 0;
}
-static int emu1010_firmware_thread(void *data)
+static void snd_emu1010_load_dock_firmware(struct snd_emu10k1 *emu)
{
- struct snd_emu10k1 *emu = data;
- u32 tmp, tmp2, reg;
+ u32 tmp, tmp2;
int err;
- for (;;) {
- /* Delay to allow Audio Dock to settle */
- msleep_interruptible(1000);
- if (kthread_should_stop())
- break;
- snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp); /* IRQ Status */
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg); /* OPTIONS: Which cards are attached to the EMU */
- if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) {
- /* Audio Dock attached */
- /* Return to Audio Dock programming mode */
- snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n");
- snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK);
- if (emu->card_capabilities->emu_model ==
- EMU_MODEL_EMU1010) {
- err = snd_emu1010_load_firmware(emu, DOCK_FILENAME);
- if (err != 0)
- continue;
- } else if (emu->card_capabilities->emu_model ==
- EMU_MODEL_EMU1010B) {
- err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME);
- if (err != 0)
- continue;
- } else if (emu->card_capabilities->emu_model ==
- EMU_MODEL_EMU1616) {
- err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME);
- if (err != 0)
- continue;
- }
+ // The docking events clearly arrive prematurely - while the
+ // Dock's FPGA seems to be successfully programmed, the Dock
+ // fails to initialize subsequently if we don't give it some
+ // time to "warm up" here.
+ msleep(200);
- snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0);
- snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg);
- snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS = 0x%x\n", reg);
- /* ID, should read & 0x7f = 0x55 when FPGA programmed. */
- snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
- snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", reg);
- if ((reg & 0x1f) != 0x15) {
- /* FPGA failed to be programmed */
- snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n", reg);
- continue;
- }
- snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n");
- snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp);
- snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2);
- snd_printk(KERN_INFO "Audio Dock ver: %u.%u\n",
- tmp, tmp2);
- /* Sync clocking between 1010 and Dock */
- /* Allow DLL to settle */
- msleep(10);
- /* Unmute all. Default is muted after a firmware load */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
- }
+ dev_info(emu->card->dev, "emu1010: Loading Audio Dock Firmware\n");
+ err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw);
+ if (err < 0)
+ return;
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0);
+
+ snd_emu1010_fpga_read(emu, EMU_HANA_ID, &tmp);
+ dev_dbg(emu->card->dev, "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", tmp);
+ if ((tmp & 0x1f) != 0x15) {
+ /* FPGA failed to be programmed */
+ dev_err(emu->card->dev,
+ "emu1010: Loading Audio Dock Firmware failed, reg = 0x%x\n",
+ tmp);
+ return;
}
- snd_printk(KERN_INFO "emu1010: firmware thread stopping\n");
- return 0;
+ dev_info(emu->card->dev, "emu1010: Audio Dock Firmware loaded\n");
+
+ snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp);
+ snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2);
+ dev_info(emu->card->dev, "Audio Dock ver: %u.%u\n", tmp, tmp2);
+
+ /* Allow DLL to settle, to sync clocking between 1010 and Dock */
+ msleep(10);
+}
+
+static void emu1010_dock_event(struct snd_emu10k1 *emu)
+{
+ u32 reg;
+
+ snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg); /* OPTIONS: Which cards are attached to the EMU */
+ if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) {
+ /* Audio Dock attached */
+ snd_emu1010_load_dock_firmware(emu);
+ /* Unmute all. Default is muted after a firmware load */
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
+ } else if (!(reg & EMU_HANA_OPTION_DOCK_ONLINE)) {
+ /* Audio Dock removed */
+ dev_info(emu->card->dev, "emu1010: Audio Dock detached\n");
+ /* The hardware auto-mutes all, so we unmute again */
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
+ }
+}
+
+static void emu1010_clock_event(struct snd_emu10k1 *emu)
+{
+ struct snd_ctl_elem_id id;
+
+ scoped_guard(spinlock_irq, &emu->reg_lock) {
+ // This is the only thing that can actually happen.
+ emu->emu1010.clock_source = emu->emu1010.clock_fallback;
+ emu->emu1010.wclock = 1 - emu->emu1010.clock_source;
+ snd_emu1010_update_clock(emu);
+ }
+ snd_ctl_build_ioff(&id, emu->ctl_clock_source, 0);
+ snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE, &id);
+}
+
+static void emu1010_work(struct work_struct *work)
+{
+ struct snd_emu10k1 *emu;
+ u32 sts;
+
+ emu = container_of(work, struct snd_emu10k1, emu1010.work);
+ if (emu->card->shutdown)
+ return;
+#ifdef CONFIG_PM_SLEEP
+ if (emu->suspend)
+ return;
+#endif
+
+ guard(snd_emu1010_fpga_lock)(emu);
+
+ snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &sts);
+
+ // The distinction of the IRQ status bits is unreliable,
+ // so we dispatch later based on option card status.
+ if (sts & (EMU_HANA_IRQ_DOCK | EMU_HANA_IRQ_DOCK_LOST))
+ emu1010_dock_event(emu);
+
+ if (sts & EMU_HANA_IRQ_WCLK_CHANGED)
+ emu1010_clock_event(emu);
+}
+
+static void emu1010_interrupt(struct snd_emu10k1 *emu)
+{
+ // We get an interrupt on each GPIO input pin change, but we
+ // care only about the ones triggered by the dedicated pin.
+ u16 sts = inw(emu->port + A_GPIO);
+ u16 bit = emu->card_capabilities->ca0108_chip ? 0x2000 : 0x8000;
+ if (!(sts & bit))
+ return;
+
+ schedule_work(&emu->emu1010.work);
}
/*
- * EMU-1010 - details found out from this driver, official MS Win drivers,
- * testing the card:
- *
- * Audigy2 (aka Alice2):
- * ---------------------
- * * communication over PCI
- * * conversion of 32-bit data coming over EMU32 links from HANA FPGA
- * to 2 x 16-bit, using internal DSP instructions
- * * slave mode, clock supplied by HANA
- * * linked to HANA using:
- * 32 x 32-bit serial EMU32 output channels
- * 16 x EMU32 input channels
- * (?) x I2S I/O channels (?)
- *
- * FPGA (aka HANA):
- * ---------------
- * * provides all (?) physical inputs and outputs of the card
- * (ADC, DAC, SPDIF I/O, ADAT I/O, etc.)
- * * provides clock signal for the card and Alice2
- * * two crystals - for 44.1kHz and 48kHz multiples
- * * provides internal routing of signal sources to signal destinations
- * * inputs/outputs to Alice2 - see above
- *
* Current status of the driver:
* ----------------------------
* * only 44.1/48kHz supported (the MS Win driver supports up to 192 kHz)
@@ -803,75 +803,21 @@ static int emu1010_firmware_thread(void *data)
*/
static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
{
- unsigned int i;
u32 tmp, tmp2, reg;
int err;
- const char *filename = NULL;
- snd_printk(KERN_INFO "emu1010: Special config.\n");
- /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
- * Lock Sound Memory Cache, Lock Tank Memory Cache,
- * Mute all codecs.
- */
- outl(0x0005a00c, emu->port + HCFG);
- /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
- * Lock Tank Memory Cache,
- * Mute all codecs.
- */
- outl(0x0005a004, emu->port + HCFG);
- /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
- * Mute all codecs.
- */
- outl(0x0005a000, emu->port + HCFG);
- /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
- * Mute all codecs.
- */
- outl(0x0005a000, emu->port + HCFG);
+ dev_info(emu->card->dev, "emu1010: Special config.\n");
- /* Disable 48Volt power to Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
+ /* Mute, and disable audio and lock cache, just in case.
+ * Proper init follows in snd_emu10k1_init(). */
+ outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG);
- /* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */
- snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
- snd_printdd("reg1 = 0x%x\n", reg);
- if ((reg & 0x3f) == 0x15) {
- /* FPGA netlist already present so clear it */
- /* Return to programming mode */
+ guard(snd_emu1010_fpga_lock)(emu);
- snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0x02);
- }
- snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
- snd_printdd("reg2 = 0x%x\n", reg);
- if ((reg & 0x3f) == 0x15) {
- /* FPGA failed to return to programming mode */
- snd_printk(KERN_INFO "emu1010: FPGA failed to return to programming mode\n");
- return -ENODEV;
- }
- snd_printk(KERN_INFO "emu1010: EMU_HANA_ID = 0x%x\n", reg);
- switch (emu->card_capabilities->emu_model) {
- case EMU_MODEL_EMU1010:
- filename = HANA_FILENAME;
- break;
- case EMU_MODEL_EMU1010B:
- filename = EMU1010B_FILENAME;
- break;
- case EMU_MODEL_EMU1616:
- filename = EMU1010_NOTEBOOK_FILENAME;
- break;
- case EMU_MODEL_EMU0404:
- filename = EMU0404_FILENAME;
- break;
- default:
- filename = NULL;
- return -ENODEV;
- break;
- }
- snd_printk(KERN_INFO "emu1010: filename %s testing\n", filename);
- err = snd_emu1010_load_firmware(emu, filename);
- if (err != 0) {
- snd_printk(
- KERN_INFO "emu1010: Loading Firmware file %s failed\n",
- filename);
+ dev_info(emu->card->dev, "emu1010: Loading Hana Firmware\n");
+ err = snd_emu1010_load_firmware(emu, 0, &emu->firmware);
+ if (err < 0) {
+ dev_info(emu->card->dev, "emu1010: Loading Firmware failed\n");
return err;
}
@@ -879,360 +825,68 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
if ((reg & 0x3f) != 0x15) {
/* FPGA failed to be programmed */
- snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg = 0x%x\n", reg);
+ dev_info(emu->card->dev,
+ "emu1010: Loading Hana Firmware file failed, reg = 0x%x\n",
+ reg);
return -ENODEV;
}
- snd_printk(KERN_INFO "emu1010: Hana Firmware loaded\n");
+ dev_info(emu->card->dev, "emu1010: Hana Firmware loaded\n");
snd_emu1010_fpga_read(emu, EMU_HANA_MAJOR_REV, &tmp);
snd_emu1010_fpga_read(emu, EMU_HANA_MINOR_REV, &tmp2);
- snd_printk(KERN_INFO "emu1010: Hana version: %u.%u\n", tmp, tmp2);
+ dev_info(emu->card->dev, "emu1010: Hana version: %u.%u\n", tmp, tmp2);
/* Enable 48Volt power to Audio Dock */
snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, EMU_HANA_DOCK_PWR_ON);
snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
- snd_printk(KERN_INFO "emu1010: Card options = 0x%x\n", reg);
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
- snd_printk(KERN_INFO "emu1010: Card options = 0x%x\n", reg);
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTICAL_TYPE, &tmp);
- /* Optical -> ADAT I/O */
- /* 0 : SPDIF
- * 1 : ADAT
- */
- emu->emu1010.optical_in = 1; /* IN_ADAT */
- emu->emu1010.optical_out = 1; /* IN_ADAT */
- tmp = 0;
- tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) |
- (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0);
+ dev_info(emu->card->dev, "emu1010: Card options = 0x%x\n", reg);
+ if (reg & EMU_HANA_OPTION_DOCK_OFFLINE)
+ snd_emu1010_load_dock_firmware(emu);
+ if (emu->card_capabilities->no_adat) {
+ emu->emu1010.optical_in = 0; /* IN_SPDIF */
+ emu->emu1010.optical_out = 0; /* OUT_SPDIF */
+ } else {
+ /* Optical -> ADAT I/O */
+ emu->emu1010.optical_in = 1; /* IN_ADAT */
+ emu->emu1010.optical_out = 1; /* OUT_ADAT */
+ }
+ tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : EMU_HANA_OPTICAL_IN_SPDIF) |
+ (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : EMU_HANA_OPTICAL_OUT_SPDIF);
snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp);
- snd_emu1010_fpga_read(emu, EMU_HANA_ADC_PADS, &tmp);
/* Set no attenuation on Audio Dock pads. */
- snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, 0x00);
emu->emu1010.adc_pads = 0x00;
- snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp);
+ snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, emu->emu1010.adc_pads);
/* Unmute Audio dock DACs, Headphone source DAC-4. */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30);
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);
- snd_emu1010_fpga_read(emu, EMU_HANA_DAC_PADS, &tmp);
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, EMU_HANA_DOCK_PHONES_192_DAC4);
/* DAC PADs. */
- snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, 0x0f);
- emu->emu1010.dac_pads = 0x0f;
- snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp);
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30);
- snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp);
+ emu->emu1010.dac_pads = EMU_HANA_DOCK_DAC_PAD1 | EMU_HANA_DOCK_DAC_PAD2 |
+ EMU_HANA_DOCK_DAC_PAD3 | EMU_HANA_DOCK_DAC_PAD4;
+ snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, emu->emu1010.dac_pads);
/* SPDIF Format. Set Consumer mode, 24bit, copy enable */
- snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10);
+ snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, EMU_HANA_SPDIF_MODE_RX_INVALID);
/* MIDI routing */
- snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19);
- /* Unknown. */
- snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c);
- /* IRQ Enable: Alll on */
- /* snd_emu1010_fpga_write(emu, 0x09, 0x0f ); */
- /* IRQ Enable: All off */
- snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00);
+ snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, EMU_HANA_MIDI_INA_FROM_HAMOA | EMU_HANA_MIDI_INB_FROM_DOCK2);
+ snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, EMU_HANA_MIDI_OUT_DOCK2 | EMU_HANA_MIDI_OUT_SYNC2);
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
- snd_printk(KERN_INFO "emu1010: Card options3 = 0x%x\n", reg);
+ emu->gpio_interrupt = emu1010_interrupt;
+ // Note: The Audigy INTE is set later
+ snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE,
+ EMU_HANA_IRQ_DOCK | EMU_HANA_IRQ_DOCK_LOST | EMU_HANA_IRQ_WCLK_CHANGED);
+ snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg); // Clear pending IRQs
+
+ emu->emu1010.clock_source = 1; /* 48000 */
+ emu->emu1010.clock_fallback = 1; /* 48000 */
/* Default WCLK set to 48kHz. */
- snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x00);
+ snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K);
/* Word Clock source, Internal 48kHz x1 */
+ emu->emu1010.wclock = EMU_HANA_WCLOCK_INT_48K;
snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K);
/* snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X); */
- /* Audio Dock LEDs. */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);
-
-#if 0
- /* For 96kHz */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_0, EMU_SRC_HAMOA_ADC_LEFT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_1, EMU_SRC_HAMOA_ADC_RIGHT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_4, EMU_SRC_HAMOA_ADC_LEFT2);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_5, EMU_SRC_HAMOA_ADC_RIGHT2);
-#endif
-#if 0
- /* For 192kHz */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_0, EMU_SRC_HAMOA_ADC_LEFT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_1, EMU_SRC_HAMOA_ADC_RIGHT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_2, EMU_SRC_HAMOA_ADC_LEFT2);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_3, EMU_SRC_HAMOA_ADC_RIGHT2);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_4, EMU_SRC_HAMOA_ADC_LEFT3);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_5, EMU_SRC_HAMOA_ADC_RIGHT3);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_6, EMU_SRC_HAMOA_ADC_LEFT4);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_7, EMU_SRC_HAMOA_ADC_RIGHT4);
-#endif
-#if 1
- /* For 48kHz */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_0, EMU_SRC_DOCK_MIC_A1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_1, EMU_SRC_DOCK_MIC_B1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_2, EMU_SRC_HAMOA_ADC_LEFT2);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_3, EMU_SRC_HAMOA_ADC_LEFT2);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_4, EMU_SRC_DOCK_ADC1_LEFT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_5, EMU_SRC_DOCK_ADC1_RIGHT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_6, EMU_SRC_DOCK_ADC2_LEFT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_7, EMU_SRC_DOCK_ADC2_RIGHT1);
- /* Pavel Hofman - setting defaults for 8 more capture channels
- * Defaults only, users will set their own values anyways, let's
- * just copy/paste.
- */
-
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_8, EMU_SRC_DOCK_MIC_A1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_9, EMU_SRC_DOCK_MIC_B1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_A, EMU_SRC_HAMOA_ADC_LEFT2);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_B, EMU_SRC_HAMOA_ADC_LEFT2);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_C, EMU_SRC_DOCK_ADC1_LEFT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_D, EMU_SRC_DOCK_ADC1_RIGHT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_E, EMU_SRC_DOCK_ADC2_LEFT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_F, EMU_SRC_DOCK_ADC2_RIGHT1);
-#endif
-#if 0
- /* Original */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_4, EMU_SRC_HANA_ADAT);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_5, EMU_SRC_HANA_ADAT + 1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_6, EMU_SRC_HANA_ADAT + 2);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_7, EMU_SRC_HANA_ADAT + 3);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_8, EMU_SRC_HANA_ADAT + 4);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_9, EMU_SRC_HANA_ADAT + 5);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_A, EMU_SRC_HANA_ADAT + 6);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_B, EMU_SRC_HANA_ADAT + 7);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_C, EMU_SRC_DOCK_MIC_A1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_D, EMU_SRC_DOCK_MIC_B1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_E, EMU_SRC_HAMOA_ADC_LEFT2);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE2_EMU32_F, EMU_SRC_HAMOA_ADC_LEFT2);
-#endif
- for (i = 0; i < 0x20; i++) {
- /* AudioDock Elink <- Silence */
- snd_emu1010_fpga_link_dst_src_write(emu, 0x0100 + i, EMU_SRC_SILENCE);
- }
- for (i = 0; i < 4; i++) {
- /* Hana SPDIF Out <- Silence */
- snd_emu1010_fpga_link_dst_src_write(emu, 0x0200 + i, EMU_SRC_SILENCE);
- }
- for (i = 0; i < 7; i++) {
- /* Hamoa DAC <- Silence */
- snd_emu1010_fpga_link_dst_src_write(emu, 0x0300 + i, EMU_SRC_SILENCE);
- }
- for (i = 0; i < 7; i++) {
- /* Hana ADAT Out <- Silence */
- snd_emu1010_fpga_link_dst_src_write(emu, EMU_DST_HANA_ADAT + i, EMU_SRC_SILENCE);
- }
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE_I2S0_LEFT, EMU_SRC_DOCK_ADC1_LEFT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE_I2S0_RIGHT, EMU_SRC_DOCK_ADC1_RIGHT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE_I2S1_LEFT, EMU_SRC_DOCK_ADC2_LEFT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE_I2S1_RIGHT, EMU_SRC_DOCK_ADC2_RIGHT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE_I2S2_LEFT, EMU_SRC_DOCK_ADC3_LEFT1);
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_ALICE_I2S2_RIGHT, EMU_SRC_DOCK_ADC3_RIGHT1);
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x01); /* Unmute all */
-
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp);
-
- /* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave,
- * Lock Sound Memory Cache, Lock Tank Memory Cache,
- * Mute all codecs.
- */
- outl(0x0000a000, emu->port + HCFG);
- /* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave,
- * Lock Sound Memory Cache, Lock Tank Memory Cache,
- * Un-Mute all codecs.
- */
- outl(0x0000a001, emu->port + HCFG);
-
- /* Initial boot complete. Now patches */
-
- snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp);
- snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19); /* MIDI Route */
- snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c); /* Unknown */
- snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19); /* MIDI Route */
- snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c); /* Unknown */
- snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp);
- snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10); /* SPDIF Format spdif (or 0x11 for aes/ebu) */
-
- /* Start Micro/Audio Dock firmware loader thread */
- if (!emu->emu1010.firmware_thread) {
- emu->emu1010.firmware_thread =
- kthread_create(emu1010_firmware_thread, emu,
- "emu1010_firmware");
- wake_up_process(emu->emu1010.firmware_thread);
- }
+ snd_emu1010_update_clock(emu);
-#if 0
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32B + 2); /* ALICE2 bus 0xa2 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32B + 3); /* ALICE2 bus 0xa3 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 2); /* ALICE2 bus 0xb2 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); /* ALICE2 bus 0xb3 */
-#endif
- /* Default outputs */
- if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
- /* 1616(M) cardbus default outputs */
- /* ALICE2 bus 0xa0 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
- emu->emu1010.output_source[0] = 17;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[1] = 18;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2);
- emu->emu1010.output_source[2] = 19;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3);
- emu->emu1010.output_source[3] = 20;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4);
- emu->emu1010.output_source[4] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5);
- emu->emu1010.output_source[5] = 22;
- /* ALICE2 bus 0xa0 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_MANA_DAC_LEFT, EMU_SRC_ALICE_EMU32A + 0);
- emu->emu1010.output_source[16] = 17;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_MANA_DAC_RIGHT, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[17] = 18;
- } else {
- /* ALICE2 bus 0xa0 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
- emu->emu1010.output_source[0] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[1] = 22;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2);
- emu->emu1010.output_source[2] = 23;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3);
- emu->emu1010.output_source[3] = 24;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4);
- emu->emu1010.output_source[4] = 25;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5);
- emu->emu1010.output_source[5] = 26;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC4_LEFT1, EMU_SRC_ALICE_EMU32A + 6);
- emu->emu1010.output_source[6] = 27;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_DAC4_RIGHT1, EMU_SRC_ALICE_EMU32A + 7);
- emu->emu1010.output_source[7] = 28;
- /* ALICE2 bus 0xa0 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_PHONES_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
- emu->emu1010.output_source[8] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_PHONES_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[9] = 22;
- /* ALICE2 bus 0xa0 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
- emu->emu1010.output_source[10] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_DOCK_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[11] = 22;
- /* ALICE2 bus 0xa0 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
- emu->emu1010.output_source[12] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[13] = 22;
- /* ALICE2 bus 0xa0 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
- emu->emu1010.output_source[14] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[15] = 22;
- /* ALICE2 bus 0xa0 */
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT, EMU_SRC_ALICE_EMU32A + 0);
- emu->emu1010.output_source[16] = 21;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 1, EMU_SRC_ALICE_EMU32A + 1);
- emu->emu1010.output_source[17] = 22;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 2, EMU_SRC_ALICE_EMU32A + 2);
- emu->emu1010.output_source[18] = 23;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 3, EMU_SRC_ALICE_EMU32A + 3);
- emu->emu1010.output_source[19] = 24;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 4, EMU_SRC_ALICE_EMU32A + 4);
- emu->emu1010.output_source[20] = 25;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 5, EMU_SRC_ALICE_EMU32A + 5);
- emu->emu1010.output_source[21] = 26;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 6, EMU_SRC_ALICE_EMU32A + 6);
- emu->emu1010.output_source[22] = 27;
- snd_emu1010_fpga_link_dst_src_write(emu,
- EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7);
- emu->emu1010.output_source[23] = 28;
- }
- /* TEMP: Select SPDIF in/out */
- /* snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); */ /* Output spdif */
-
- /* TEMP: Select 48kHz SPDIF out */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x0); /* Mute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x0); /* Default fallback clock 48kHz */
- /* Word Clock source, Internal 48kHz x1 */
- snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K);
- /* snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X); */
- emu->emu1010.internal_clock = 1; /* 48000 */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12); /* Set LEDs on Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x1); /* Unmute all */
- /* snd_emu1010_fpga_write(emu, 0x7, 0x0); */ /* Mute all */
- /* snd_emu1010_fpga_write(emu, 0x7, 0x1); */ /* Unmute all */
- /* snd_emu1010_fpga_write(emu, 0xe, 0x12); */ /* Set LEDs on Audio Dock */
+ // The routes are all set to EMU_SRC_SILENCE due to the reset,
+ // so it is safe to simply enable the outputs.
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
return 0;
}
@@ -1240,13 +894,15 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
* Create the EMU10K1 instance
*/
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int alloc_pm_buffer(struct snd_emu10k1 *emu);
static void free_pm_buffer(struct snd_emu10k1 *emu);
#endif
-static int snd_emu10k1_free(struct snd_emu10k1 *emu)
+static void snd_emu10k1_free(struct snd_card *card)
{
+ struct snd_emu10k1 *emu = card->private_data;
+
if (emu->port) { /* avoid access to already used hardware */
snd_emu10k1_fx8010_tram_setup(emu, 0);
snd_emu10k1_done(emu);
@@ -1254,45 +910,41 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu)
}
if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010) {
/* Disable 48Volt power to Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
- }
- if (emu->emu1010.firmware_thread)
- kthread_stop(emu->emu1010.firmware_thread);
- if (emu->irq >= 0)
- free_irq(emu->irq, emu);
- /* remove reserved page */
- if (emu->reserved_page) {
- snd_emu10k1_synth_free(emu,
- (struct snd_util_memblk *)emu->reserved_page);
- emu->reserved_page = NULL;
+ snd_emu1010_fpga_write_lock(emu, EMU_HANA_DOCK_PWR, 0);
}
- if (emu->memhdr)
- snd_util_memhdr_free(emu->memhdr);
+ cancel_work_sync(&emu->emu1010.work);
+ mutex_destroy(&emu->emu1010.lock);
+ release_firmware(emu->firmware);
+ release_firmware(emu->dock_fw);
+ snd_util_memhdr_free(emu->memhdr);
if (emu->silent_page.area)
snd_dma_free_pages(&emu->silent_page);
if (emu->ptb_pages.area)
snd_dma_free_pages(&emu->ptb_pages);
vfree(emu->page_ptr_table);
vfree(emu->page_addr_table);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
free_pm_buffer(emu);
#endif
- if (emu->port)
- pci_release_regions(emu->pci);
- if (emu->card_capabilities->ca0151_chip) /* P16V */
- snd_p16v_free(emu);
- pci_disable_device(emu->pci);
- kfree(emu);
- return 0;
}
-static int snd_emu10k1_dev_free(struct snd_device *device)
-{
- struct snd_emu10k1 *emu = device->device_data;
- return snd_emu10k1_free(emu);
-}
-
-static struct snd_emu_chip_details emu_chip_details[] = {
+static const struct snd_emu_chip_details emu_chip_details[] = {
+ /* Audigy 5/Rx SB1550 */
+ /* Tested by michael@gernoth.net 28 Mar 2015 */
+ /* DSP: CA10300-IAT LF
+ * DAC: Cirrus Logic CS4382-KQZ
+ * ADC: Philips 1361T
+ * AC97: Sigmatel STAC9750
+ * CA0151: None
+ */
+ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10241102,
+ .driver = "Audigy2", .name = "SB Audigy 5/Rx [SB1550]",
+ .id = "Audigy2",
+ .emu10k2_chip = 1,
+ .ca0108_chip = 1,
+ .spk71 = 1,
+ .adc_1361t = 1, /* 24 bit capture instead of 16bit */
+ .ac97_chip = 1},
/* Audigy4 (Not PRO) SB0610 */
/* Tested by James@superbug.co.uk 4th April 2006 */
/* A_IOCFG bits
@@ -1349,6 +1001,15 @@ static struct snd_emu_chip_details emu_chip_details[] = {
* AC97: STAC9750
* CA0151: None
*/
+ /*
+ * A_IOCFG Input (GPIO)
+ * 0x400 = Front analog jack plugged in. (Green socket)
+ * 0x1000 = Rear analog jack plugged in. (Black socket)
+ * 0x2000 = Center/LFE analog jack plugged in. (Orange socket)
+ * A_IOCFG Output (GPIO)
+ * 0x60 = Sound out of front Left.
+ * Win sets it to 0xXX61
+ */
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10011102,
.driver = "Audigy2", .name = "SB Audigy 2 Value [SB0400]",
.id = "Audigy2",
@@ -1389,7 +1050,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
*
*/
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x20011102,
- .driver = "Audigy2", .name = "SB Audigy 2 ZS Notebook [SB0530]",
+ .driver = "Audigy2", .name = "Audigy 2 ZS Notebook [SB0530]",
.id = "Audigy2",
.emu10k2_chip = 1,
.ca0108_chip = 1,
@@ -1397,9 +1058,12 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.spi_dac = 1,
.i2c_adc = 1,
.spk71 = 1} ,
+ /* This is MAEM8950 "Mana" */
+ /* Attach MicroDock[M] to make it an E-MU 1616[m]. */
+ /* Does NOT support sync daughter card (obviously). */
/* Tested by James@superbug.co.uk 4th Nov 2007. */
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x42011102,
- .driver = "Audigy2", .name = "E-mu 1010 Notebook [MAEM8950]",
+ .driver = "Audigy2", .name = "E-MU 02 CardBus [MAEM8950]",
.id = "EMU1010",
.emu10k2_chip = 1,
.ca0108_chip = 1,
@@ -1407,40 +1071,75 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.spk71 = 1 ,
.emu_model = EMU_MODEL_EMU1616},
/* Tested by James@superbug.co.uk 4th Nov 2007. */
- /* This is MAEM8960, 0202 is MAEM 8980 */
+ /* This is MAEM8960 "Hana3", 0202 is MAEM8980 */
+ /* Attach 0202 daughter card to make it an E-MU 1212m, OR a
+ * MicroDock[M] to make it an E-MU 1616[m]. */
+ /* Does NOT support sync daughter card. */
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40041102,
- .driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM8960]",
+ .driver = "Audigy2", .name = "E-MU 1010b PCI [MAEM8960]",
.id = "EMU1010",
.emu10k2_chip = 1,
.ca0108_chip = 1,
.spk71 = 1,
.emu_model = EMU_MODEL_EMU1010B}, /* EMU 1010 new revision */
+ /* Tested by Maxim Kachur <mcdebugger@duganet.ru> 17th Oct 2012. */
+ /* This is MAEM8986, 0202 is MAEM8980 */
+ /* Attach 0202 daughter card to make it an E-MU 1212m, OR a
+ * MicroDockM to make it an E-MU 1616m. The non-m
+ * version was never sold with this card, but should
+ * still work. */
+ /* Does NOT support sync daughter card. */
+ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40071102,
+ .driver = "Audigy2", .name = "E-MU 1010 PCIe [MAEM8986]",
+ .id = "EMU1010",
+ .emu10k2_chip = 1,
+ .ca0108_chip = 1,
+ .spk71 = 1,
+ .emu_model = EMU_MODEL_EMU1010B}, /* EMU 1010 PCIe */
/* Tested by James@superbug.co.uk 8th July 2005. */
- /* This is MAEM8810, 0202 is MAEM8820 */
+ /* This is MAEM8810 "Hana", 0202 is MAEM8820 "Hamoa" */
+ /* Attach 0202 daughter card to make it an E-MU 1212m, OR an
+ * AudioDock[M] to make it an E-MU 1820[m]. */
+ /* Supports sync daughter card. */
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102,
- .driver = "Audigy2", .name = "E-mu 1010 [MAEM8810]",
+ .driver = "Audigy2", .name = "E-MU 1010 [MAEM8810]",
.id = "EMU1010",
.emu10k2_chip = 1,
.ca0102_chip = 1,
.spk71 = 1,
.emu_model = EMU_MODEL_EMU1010}, /* EMU 1010 old revision */
- /* EMU0404b */
+ /* This is MAEM8852 "HanaLiteLite" */
+ /* Supports sync daughter card. */
+ /* Tested by oswald.buddenhagen@gmx.de Mar 2023. */
{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40021102,
- .driver = "Audigy2", .name = "E-mu 0404b PCI [MAEM8852]",
+ .driver = "Audigy2", .name = "E-MU 0404b PCI [MAEM8852]",
.id = "EMU0404",
.emu10k2_chip = 1,
.ca0108_chip = 1,
- .spk71 = 1,
+ .spk20 = 1,
+ .no_adat = 1,
.emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 new revision */
+ /* This is MAEM8850 "HanaLite" */
+ /* Supports sync daughter card. */
/* Tested by James@superbug.co.uk 20-3-2007. */
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40021102,
- .driver = "Audigy2", .name = "E-mu 0404 [MAEM8850]",
+ .driver = "Audigy2", .name = "E-MU 0404 [MAEM8850]",
.id = "EMU0404",
.emu10k2_chip = 1,
.ca0102_chip = 1,
- .spk71 = 1,
+ .spk20 = 1,
+ .no_adat = 1,
.emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */
- /* Note that all E-mu cards require kernel 2.6 or newer. */
+ /* EMU0404 PCIe */
+ /* Does NOT support sync daughter card. */
+ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40051102,
+ .driver = "Audigy2", .name = "E-MU 0404 PCIe [MAEM8984]",
+ .id = "EMU0404",
+ .emu10k2_chip = 1,
+ .ca0108_chip = 1,
+ .spk20 = 1,
+ .no_adat = 1,
+ .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 PCIe ver_03 */
{.vendor = 0x1102, .device = 0x0008,
.driver = "Audigy2", .name = "SB Audigy 2 Value [Unknown]",
.id = "Audigy2",
@@ -1471,6 +1170,18 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.spdif_bug = 1,
.invert_shared_spdif = 1, /* digital/analog switch swapped */
.ac97_chip = 1} ,
+ /* 0x20051102 also has SB0350 written on it, treated as Audigy 2 ZS by
+ Creative's Windows driver */
+ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20051102,
+ .driver = "Audigy2", .name = "SB Audigy 2 ZS [SB0350a]",
+ .id = "Audigy2",
+ .emu10k2_chip = 1,
+ .ca0102_chip = 1,
+ .ca0151_chip = 1,
+ .spk71 = 1,
+ .spdif_bug = 1,
+ .invert_shared_spdif = 1, /* digital/analog switch swapped */
+ .ac97_chip = 1} ,
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20021102,
.driver = "Audigy2", .name = "SB Audigy 2 ZS [SB0350]",
.id = "Audigy2",
@@ -1509,8 +1220,10 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.spdif_bug = 1,
.adc_1361t = 1, /* 24 bit capture instead of 16bit */
.ac97_chip = 1} ,
+ /* Audigy 2 Platinum EX */
+ /* Win driver sets A_IOCFG output to 0x1c00 */
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10051102,
- .driver = "Audigy2", .name = "SB Audigy 2 Platinum EX [SB0280]",
+ .driver = "Audigy2", .name = "Audigy 2 Platinum EX [SB0280]",
.id = "Audigy2",
.emu10k2_chip = 1,
.ca0102_chip = 1,
@@ -1529,6 +1242,8 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.spdif_bug = 1,
.invert_shared_spdif = 1, /* digital/analog switch swapped */
.ac97_chip = 1} ,
+ /* Audigy 2 Platinum */
+ /* Win driver sets A_IOCFG output to 0xa00 */
{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10021102,
.driver = "Audigy2", .name = "SB Audigy 2 Platinum [SB0240P]",
.id = "Audigy2",
@@ -1634,6 +1349,9 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.emu10k1_chip = 1,
.ac97_chip = 1,
.sblive51 = 1} ,
+ /* SB Live! Platinum */
+ /* Win driver sets A_IOCFG output to 0 */
+ /* Tested by Jonathan Dowland <jon@dow.land> Apr 2023. */
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80401102,
.driver = "EMU10K1", .name = "SB Live! Platinum [CT4760P]",
.id = "Live",
@@ -1683,7 +1401,7 @@ static struct snd_emu_chip_details emu_chip_details[] = {
.ac97_chip = 1,
.sblive51 = 1} ,
{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x40011102,
- .driver = "EMU10K1", .name = "E-mu APS [PC545]",
+ .driver = "EMU10K1", .name = "E-MU APS [PC545]",
.id = "APS",
.emu10k1_chip = 1,
.ecard = 1} ,
@@ -1708,36 +1426,57 @@ static struct snd_emu_chip_details emu_chip_details[] = {
{ } /* terminator */
};
-int __devinit snd_emu10k1_create(struct snd_card *card,
+/*
+ * The chip (at least the Audigy 2 CA0102 chip, but most likely others, too)
+ * has a problem that from time to time it likes to do few DMA reads a bit
+ * beyond its normal allocation and gets very confused if these reads get
+ * blocked by a IOMMU.
+ *
+ * This behaviour has been observed for the first (reserved) page
+ * (for which it happens multiple times at every playback), often for various
+ * synth pages and sometimes for PCM playback buffers and the page table
+ * memory itself.
+ *
+ * As a workaround let's widen these DMA allocations by an extra page if we
+ * detect that the device is behind a non-passthrough IOMMU.
+ */
+static void snd_emu10k1_detect_iommu(struct snd_emu10k1 *emu)
+{
+ struct iommu_domain *domain;
+
+ emu->iommu_workaround = false;
+
+ domain = iommu_get_domain_for_dev(emu->card->dev);
+ if (!domain || domain->type == IOMMU_DOMAIN_IDENTITY)
+ return;
+
+ dev_notice(emu->card->dev,
+ "non-passthrough IOMMU detected, widening DMA allocations");
+ emu->iommu_workaround = true;
+}
+
+int snd_emu10k1_create(struct snd_card *card,
struct pci_dev *pci,
unsigned short extin_mask,
unsigned short extout_mask,
long max_cache_bytes,
int enable_ir,
- uint subsystem,
- struct snd_emu10k1 **remu)
+ uint subsystem)
{
- struct snd_emu10k1 *emu;
+ struct snd_emu10k1 *emu = card->private_data;
int idx, err;
int is_audigy;
+ size_t page_table_size;
+ __le32 *pgtbl;
unsigned int silent_page;
const struct snd_emu_chip_details *c;
- static struct snd_device_ops ops = {
- .dev_free = snd_emu10k1_dev_free,
- };
-
- *remu = NULL;
/* enable PCI device */
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
- emu = kzalloc(sizeof(*emu), GFP_KERNEL);
- if (emu == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
+ card->private_free = snd_emu10k1_free;
emu->card = card;
spin_lock_init(&emu->reg_lock);
spin_lock_init(&emu->emu_lock);
@@ -1753,11 +1492,15 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
emu->irq = -1;
emu->synth = NULL;
emu->get_synth_voice = NULL;
+ INIT_WORK(&emu->emu1010.work, emu1010_work);
+ mutex_init(&emu->emu1010.lock);
/* read revision & serial */
emu->revision = pci->revision;
pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial);
pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &emu->model);
- snd_printdd("vendor = 0x%x, device = 0x%x, subsystem_vendor_id = 0x%x, subsystem_id = 0x%x\n", pci->vendor, pci->device, emu->serial, emu->model);
+ dev_dbg(card->dev,
+ "vendor = 0x%x, device = 0x%x, subsystem_vendor_id = 0x%x, subsystem_id = 0x%x\n",
+ pci->vendor, pci->device, emu->serial, emu->model);
for (c = emu_chip_details; c->vendor; c++) {
if (c->vendor == pci->vendor && c->device == pci->device) {
@@ -1776,51 +1519,38 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
}
}
if (c->vendor == 0) {
- snd_printk(KERN_ERR "emu10k1: Card not recognised\n");
- kfree(emu);
- pci_disable_device(pci);
+ dev_err(card->dev, "emu10k1: Card not recognised\n");
return -ENOENT;
}
emu->card_capabilities = c;
if (c->subsystem && !subsystem)
- snd_printdd("Sound card name = %s\n", c->name);
+ dev_dbg(card->dev, "Sound card name = %s\n", c->name);
else if (subsystem)
- snd_printdd("Sound card name = %s, "
+ dev_dbg(card->dev, "Sound card name = %s, "
"vendor = 0x%x, device = 0x%x, subsystem = 0x%x. "
"Forced to subsystem = 0x%x\n", c->name,
pci->vendor, pci->device, emu->serial, c->subsystem);
else
- snd_printdd("Sound card name = %s, "
+ dev_dbg(card->dev, "Sound card name = %s, "
"vendor = 0x%x, device = 0x%x, subsystem = 0x%x.\n",
c->name, pci->vendor, pci->device,
emu->serial);
- if (!*card->id && c->id) {
- int i, n = 0;
- strlcpy(card->id, c->id, sizeof(card->id));
- for (;;) {
- for (i = 0; i < snd_ecards_limit; i++) {
- if (snd_cards[i] && !strcmp(snd_cards[i]->id, card->id))
- break;
- }
- if (i >= snd_ecards_limit)
- break;
- n++;
- if (n >= SNDRV_CARDS)
- break;
- snprintf(card->id, sizeof(card->id), "%s_%d", c->id, n);
- }
- }
+ if (!*card->id && c->id)
+ strscpy(card->id, c->id, sizeof(card->id));
is_audigy = emu->audigy = c->emu10k2_chip;
+ snd_emu10k1_detect_iommu(emu);
+
+ /* set addressing mode */
+ emu->address_mode = is_audigy ? 0 : 1;
/* set the DMA transfer mask */
- emu->dma_mask = is_audigy ? AUDIGY_DMA_MASK : EMU10K1_DMA_MASK;
- if (pci_set_dma_mask(pci, emu->dma_mask) < 0 ||
- pci_set_consistent_dma_mask(pci, emu->dma_mask) < 0) {
- snd_printk(KERN_ERR "architecture does not support PCI busmaster DMA with mask 0x%lx\n", emu->dma_mask);
- kfree(emu);
- pci_disable_device(pci);
+ emu->dma_mask = emu->address_mode ? EMU10K1_DMA_MASK : AUDIGY_DMA_MASK;
+ if (dma_set_mask_and_coherent(&pci->dev, emu->dma_mask) < 0) {
+ dev_err(card->dev,
+ "architecture does not support PCI busmaster DMA with mask 0x%lx\n",
+ emu->dma_mask);
return -ENXIO;
}
if (is_audigy)
@@ -1828,49 +1558,51 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
else
emu->gpr_base = FXGPREGBASE;
- err = pci_request_regions(pci, "EMU10K1");
- if (err < 0) {
- kfree(emu);
- pci_disable_device(pci);
+ err = pcim_request_all_regions(pci, "EMU10K1");
+ if (err < 0)
return err;
- }
emu->port = pci_resource_start(pci, 0);
emu->max_cache_pages = max_cache_bytes >> PAGE_SHIFT;
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- 32 * 1024, &emu->ptb_pages) < 0) {
- err = -ENOMEM;
- goto error;
- }
- emu->page_ptr_table = vmalloc(emu->max_cache_pages * sizeof(void *));
- emu->page_addr_table = vmalloc(emu->max_cache_pages *
- sizeof(unsigned long));
- if (emu->page_ptr_table == NULL || emu->page_addr_table == NULL) {
- err = -ENOMEM;
- goto error;
- }
+ page_table_size = sizeof(u32) * (emu->address_mode ? MAXPAGES1 :
+ MAXPAGES0);
+ if (snd_emu10k1_alloc_pages_maybe_wider(emu, page_table_size,
+ &emu->ptb_pages) < 0)
+ return -ENOMEM;
+ dev_dbg(card->dev, "page table address range is %.8lx:%.8lx\n",
+ (unsigned long)emu->ptb_pages.addr,
+ (unsigned long)(emu->ptb_pages.addr + emu->ptb_pages.bytes));
+
+ emu->page_ptr_table = vmalloc(array_size(sizeof(void *),
+ emu->max_cache_pages));
+ emu->page_addr_table = vmalloc(array_size(sizeof(unsigned long),
+ emu->max_cache_pages));
+ if (!emu->page_ptr_table || !emu->page_addr_table)
+ return -ENOMEM;
+
+ if (snd_emu10k1_alloc_pages_maybe_wider(emu, EMUPAGESIZE,
+ &emu->silent_page) < 0)
+ return -ENOMEM;
+ dev_dbg(card->dev, "silent page range is %.8lx:%.8lx\n",
+ (unsigned long)emu->silent_page.addr,
+ (unsigned long)(emu->silent_page.addr +
+ emu->silent_page.bytes));
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- EMUPAGESIZE, &emu->silent_page) < 0) {
- err = -ENOMEM;
- goto error;
- }
emu->memhdr = snd_util_memhdr_new(emu->max_cache_pages * PAGE_SIZE);
- if (emu->memhdr == NULL) {
- err = -ENOMEM;
- goto error;
- }
+ if (!emu->memhdr)
+ return -ENOMEM;
emu->memhdr->block_extra_size = sizeof(struct snd_emu10k1_memblk) -
sizeof(struct snd_util_memblk);
pci_set_master(pci);
- emu->fx8010.fxbus_mask = 0x303f;
+ // The masks are not used for Audigy.
+ // FIXME: these should come from the card_capabilites table.
if (extin_mask == 0)
- extin_mask = 0x3fcf;
+ extin_mask = 0x3fcf; // EXTIN_*
if (extout_mask == 0)
- extout_mask = 0x7fff;
+ extout_mask = 0x7fff; // EXTOUT_*
emu->fx8010.extin_mask = extin_mask;
emu->fx8010.extout_mask = extout_mask;
emu->enable_ir = enable_ir;
@@ -1878,18 +1610,16 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
if (emu->card_capabilities->ca_cardbus_chip) {
err = snd_emu10k1_cardbus_init(emu);
if (err < 0)
- goto error;
+ return err;
}
if (emu->card_capabilities->ecard) {
err = snd_emu10k1_ecard_init(emu);
if (err < 0)
- goto error;
+ return err;
} else if (emu->card_capabilities->emu_model) {
err = snd_emu10k1_emu1010_init(emu);
- if (err < 0) {
- snd_emu10k1_free(emu);
+ if (err < 0)
return err;
- }
} else {
/* 5.1: Enable the additional AC97 Slots. If the emu10k1 version
does not support this, it shouldn't do any harm */
@@ -1903,12 +1633,11 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
emu->fx8010.etram_pages.bytes = 0;
/* irq handler must be registered after I/O ports are activated */
- if (request_irq(pci->irq, snd_emu10k1_interrupt, IRQF_SHARED,
- "EMU10K1", emu)) {
- err = -EBUSY;
- goto error;
- }
+ if (devm_request_irq(&pci->dev, pci->irq, snd_emu10k1_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, emu))
+ return -EBUSY;
emu->irq = pci->irq;
+ card->sync_irq = emu->irq;
/*
* Init to 0x02109204 :
@@ -1930,57 +1659,40 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
SPCS_GENERATIONSTATUS | 0x00001200 |
0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT;
- emu->reserved_page = (struct snd_emu10k1_memblk *)
- snd_emu10k1_synth_alloc(emu, 4096);
- if (emu->reserved_page)
- emu->reserved_page->map_locked = 1;
-
/* Clear silent pages and set up pointers */
- memset(emu->silent_page.area, 0, PAGE_SIZE);
- silent_page = emu->silent_page.addr << 1;
- for (idx = 0; idx < MAXPAGES; idx++)
- ((u32 *)emu->ptb_pages.area)[idx] = cpu_to_le32(silent_page | idx);
+ memset(emu->silent_page.area, 0, emu->silent_page.bytes);
+ silent_page = emu->silent_page.addr << emu->address_mode;
+ pgtbl = (__le32 *)emu->ptb_pages.area;
+ for (idx = 0; idx < (emu->address_mode ? MAXPAGES1 : MAXPAGES0); idx++)
+ pgtbl[idx] = cpu_to_le32(silent_page | idx);
/* set up voice indices */
- for (idx = 0; idx < NUM_G; idx++) {
- emu->voices[idx].emu = emu;
+ for (idx = 0; idx < NUM_G; idx++)
emu->voices[idx].number = idx;
- }
- err = snd_emu10k1_init(emu, enable_ir, 0);
+ err = snd_emu10k1_init(emu, enable_ir);
if (err < 0)
- goto error;
-#ifdef CONFIG_PM
+ return err;
+#ifdef CONFIG_PM_SLEEP
err = alloc_pm_buffer(emu);
if (err < 0)
- goto error;
+ return err;
#endif
/* Initialize the effect engine */
err = snd_emu10k1_init_efx(emu);
if (err < 0)
- goto error;
+ return err;
snd_emu10k1_audio_enable(emu);
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, emu, &ops);
- if (err < 0)
- goto error;
-
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_SND_PROC_FS
snd_emu10k1_proc_init(emu);
#endif
-
- snd_card_set_dev(card, &pci->dev);
- *remu = emu;
return 0;
-
- error:
- snd_emu10k1_free(emu);
- return err;
}
-#ifdef CONFIG_PM
-static unsigned char saved_regs[] = {
+#ifdef CONFIG_PM_SLEEP
+static const unsigned char saved_regs[] = {
CPF, PTRX, CVCF, VTFT, Z1, Z2, PSST, DSL, CCCA, CCR, CLP,
FXRT, MAPA, MAPB, ENVVOL, ATKHLDV, DCYSUSV, LFOVAL1, ENVVAL,
ATKHLDM, DCYSUSM, LFOVAL2, IP, IFATN, PEFE, FMMOD, TREMFRQ, FM2FRQ2,
@@ -1989,20 +1701,20 @@ static unsigned char saved_regs[] = {
SPBYPASS, AC97SLOT, CDSRCS, GPSRCS, ZVSRCS, MICIDX, ADCIDX, FXIDX,
0xff /* end */
};
-static unsigned char saved_regs_audigy[] = {
- A_ADCIDX, A_MICIDX, A_FXWC1, A_FXWC2, A_SAMPLE_RATE,
+static const unsigned char saved_regs_audigy[] = {
+ A_ADCIDX, A_MICIDX, A_FXWC1, A_FXWC2, A_EHC,
A_FXRT2, A_SENDAMOUNTS, A_FXRT1,
0xff /* end */
};
-static int __devinit alloc_pm_buffer(struct snd_emu10k1 *emu)
+static int alloc_pm_buffer(struct snd_emu10k1 *emu)
{
int size;
size = ARRAY_SIZE(saved_regs);
if (emu->audigy)
size += ARRAY_SIZE(saved_regs_audigy);
- emu->saved_ptr = vmalloc(4 * NUM_G * size);
+ emu->saved_ptr = vmalloc(array3_size(4, NUM_G, size));
if (!emu->saved_ptr)
return -ENOMEM;
if (snd_emu10k1_efx_alloc_pm_buffer(emu) < 0)
@@ -2024,7 +1736,7 @@ static void free_pm_buffer(struct snd_emu10k1 *emu)
void snd_emu10k1_suspend_regs(struct snd_emu10k1 *emu)
{
int i;
- unsigned char *reg;
+ const unsigned char *reg;
unsigned int *val;
val = emu->saved_ptr;
@@ -2037,7 +1749,7 @@ void snd_emu10k1_suspend_regs(struct snd_emu10k1 *emu)
*val = snd_emu10k1_ptr_read(emu, *reg, i);
}
if (emu->audigy)
- emu->saved_a_iocfg = inl(emu->port + A_IOCFG);
+ emu->saved_a_iocfg = inw(emu->port + A_IOCFG);
emu->saved_hcfg = inl(emu->port + HCFG);
}
@@ -2051,20 +1763,20 @@ void snd_emu10k1_resume_init(struct snd_emu10k1 *emu)
snd_emu10k1_emu1010_init(emu);
else
snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
- snd_emu10k1_init(emu, emu->enable_ir, 1);
+ snd_emu10k1_init(emu, emu->enable_ir);
}
void snd_emu10k1_resume_regs(struct snd_emu10k1 *emu)
{
int i;
- unsigned char *reg;
+ const unsigned char *reg;
unsigned int *val;
snd_emu10k1_audio_enable(emu);
/* resore for spdif */
if (emu->audigy)
- outl(emu->saved_a_iocfg, emu->port + A_IOCFG);
+ outw(emu->saved_a_iocfg, emu->port + A_IOCFG);
outl(emu->saved_hcfg, emu->port + HCFG);
val = emu->saved_ptr;
diff --git a/sound/pci/emu10k1/emu10k1_patch.c b/sound/pci/emu10k1/emu10k1_patch.c
index e10f027bde03..806b4f95cad1 100644
--- a/sound/pci/emu10k1/emu10k1_patch.c
+++ b/sound/pci/emu10k1/emu10k1_patch.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Patch transfer callback for Emu10k1
*
* Copyright (C) 2000 Takashi iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* All the code for loading in a patch. There is very little that is
@@ -29,7 +16,7 @@
#define BLANK_LOOP_START 4
#define BLANK_LOOP_END 8
#define BLANK_LOOP_SIZE 12
-#define BLANK_HEAD_SIZE 32
+#define BLANK_HEAD_SIZE 3
/*
* allocate a sample block and copy data from userspace
@@ -39,60 +26,81 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
struct snd_util_memhdr *hdr,
const void __user *data, long count)
{
+ u8 fill;
+ u32 xor;
+ int shift;
int offset;
- int truesize, size, loopsize, blocksize;
- int loopend, sampleend;
- unsigned int start_addr;
+ int truesize, size, blocksize;
+ int loop_start, loop_end, loop_size, data_end, unroll;
struct snd_emu10k1 *emu;
emu = rec->hw;
if (snd_BUG_ON(!sp || !hdr))
return -EINVAL;
- if (sp->v.size == 0) {
- snd_printd("emu: rom font for sample %d\n", sp->v.sample);
- return 0;
+ if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP | SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
+ /* should instead return -ENOTSUPP; but compatibility */
+ dev_warn(emu->card->dev,
+ "Emu10k1 wavetable patch %d with unsupported loop feature\n",
+ sp->v.sample);
}
- /* recalculate address offset */
- sp->v.end -= sp->v.start;
- sp->v.loopstart -= sp->v.start;
- sp->v.loopend -= sp->v.start;
- sp->v.start = 0;
-
- /* some samples have invalid data. the addresses are corrected in voice info */
- sampleend = sp->v.end;
- if (sampleend > sp->v.size)
- sampleend = sp->v.size;
- loopend = sp->v.loopend;
- if (loopend > sampleend)
- loopend = sampleend;
-
- /* be sure loop points start < end */
- if (sp->v.loopstart >= sp->v.loopend) {
- int tmp = sp->v.loopstart;
- sp->v.loopstart = sp->v.loopend;
- sp->v.loopend = tmp;
+ if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) {
+ shift = 0;
+ fill = 0x80;
+ xor = (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) ? 0 : 0x80808080;
+ } else {
+ shift = 1;
+ fill = 0;
+ xor = (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) ? 0x80008000 : 0;
}
/* compute true data size to be loaded */
truesize = sp->v.size + BLANK_HEAD_SIZE;
- loopsize = 0;
-#if 0 /* not supported */
- if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
- loopsize = sp->v.loopend - sp->v.loopstart;
- truesize += loopsize;
-#endif
- if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
+ if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
truesize += BLANK_LOOP_SIZE;
+ /* if no blank loop is attached in the sample, add it */
+ if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
+ sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
+ sp->v.loopend = sp->v.end + BLANK_LOOP_END;
+ }
+ }
+
+ loop_start = sp->v.loopstart;
+ loop_end = sp->v.loopend;
+ loop_size = loop_end - loop_start;
+ if (!loop_size)
+ return -EINVAL;
+ data_end = sp->v.end;
+
+ /* recalculate offset */
+ sp->v.start += BLANK_HEAD_SIZE;
+ sp->v.end += BLANK_HEAD_SIZE;
+ sp->v.loopstart += BLANK_HEAD_SIZE;
+ sp->v.loopend += BLANK_HEAD_SIZE;
+
+ // Automatic pre-filling of the cache does not work in the presence
+ // of loops (*), and we don't want to fill it manually, as that is
+ // fiddly and slow. So we unroll the loop until the loop end is
+ // beyond the cache size.
+ // (*) Strictly speaking, a single iteration is supported (that's
+ // how it works when the playback engine runs), but handling this
+ // special case is not worth it.
+ unroll = 0;
+ while (sp->v.loopend < 64) {
+ truesize += loop_size;
+ sp->v.loopstart += loop_size;
+ sp->v.loopend += loop_size;
+ sp->v.end += loop_size;
+ unroll++;
+ }
/* try to allocate a memory block */
- blocksize = truesize;
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
- blocksize *= 2;
+ blocksize = truesize << shift;
sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
if (sp->block == NULL) {
- snd_printd("emu10k1: synth malloc failed (size=%d)\n", blocksize);
+ dev_dbg(emu->card->dev,
+ "synth malloc failed (size=%d)\n", blocksize);
/* not ENOMEM (for compatibility with OSS) */
return -ENOSPC;
}
@@ -101,110 +109,43 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
/* write blank samples at head */
offset = 0;
- size = BLANK_HEAD_SIZE;
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
- size *= 2;
- if (offset + size > blocksize)
- return -EINVAL;
- snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
+ size = BLANK_HEAD_SIZE << shift;
+ snd_emu10k1_synth_memset(emu, sp->block, offset, size, fill);
offset += size;
- /* copy start->loopend */
- size = loopend;
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
- size *= 2;
- if (offset + size > blocksize)
- return -EINVAL;
- if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
- snd_emu10k1_synth_free(emu, sp->block);
- sp->block = NULL;
- return -EFAULT;
- }
- offset += size;
- data += size;
-
-#if 0 /* not suppported yet */
- /* handle reverse (or bidirectional) loop */
- if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
- /* copy loop in reverse */
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
- int woffset;
- unsigned short *wblock = (unsigned short*)block;
- woffset = offset / 2;
- if (offset + loopsize * 2 > blocksize)
- return -EINVAL;
- for (i = 0; i < loopsize; i++)
- wblock[woffset + i] = wblock[woffset - i -1];
- offset += loopsize * 2;
- } else {
- if (offset + loopsize > blocksize)
- return -EINVAL;
- for (i = 0; i < loopsize; i++)
- block[offset + i] = block[offset - i -1];
- offset += loopsize;
+ /* copy provided samples */
+ if (unroll && loop_end <= data_end) {
+ size = loop_end << shift;
+ if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor))
+ goto faulty;
+ offset += size;
+
+ data += loop_start << shift;
+ while (--unroll > 0) {
+ size = loop_size << shift;
+ if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor))
+ goto faulty;
+ offset += size;
}
- /* modify loop pointers */
- if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
- sp->v.loopend += loopsize;
- } else {
- sp->v.loopstart += loopsize;
- sp->v.loopend += loopsize;
- }
- /* add sample pointer */
- sp->v.end += loopsize;
- }
-#endif
-
- /* loopend -> sample end */
- size = sp->v.size - loopend;
- if (size < 0)
- return -EINVAL;
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
- size *= 2;
- if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
- snd_emu10k1_synth_free(emu, sp->block);
- sp->block = NULL;
- return -EFAULT;
+ size = (data_end - loop_start) << shift;
+ } else {
+ size = data_end << shift;
}
+ if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor))
+ goto faulty;
offset += size;
/* clear rest of samples (if any) */
if (offset < blocksize)
- snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset);
-
- if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
- /* if no blank loop is attached in the sample, add it */
- if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
- sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
- sp->v.loopend = sp->v.end + BLANK_LOOP_END;
- }
- }
-
-#if 0 /* not supported yet */
- if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) {
- /* unsigned -> signed */
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
- unsigned short *wblock = (unsigned short*)block;
- for (i = 0; i < truesize; i++)
- wblock[i] ^= 0x8000;
- } else {
- for (i = 0; i < truesize; i++)
- block[i] ^= 0x80;
- }
- }
-#endif
-
- /* recalculate offset */
- start_addr = BLANK_HEAD_SIZE * 2;
- if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
- start_addr >>= 1;
- sp->v.start += start_addr;
- sp->v.end += start_addr;
- sp->v.loopstart += start_addr;
- sp->v.loopend += start_addr;
+ snd_emu10k1_synth_memset(emu, sp->block, offset, blocksize - offset, fill);
return 0;
+
+faulty:
+ snd_emu10k1_synth_free(emu, sp->block);
+ sp->block = NULL;
+ return -EFAULT;
}
/*
diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c
index ad7b71491fc4..662d20eb9689 100644
--- a/sound/pci/emu10k1/emu10k1_synth.c
+++ b/sound/pci/emu10k1/emu10k1_synth.c
@@ -1,25 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
*
* Routines for control of EMU10K1 WaveTable synth
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "emu10k1_synth_local.h"
#include <linux/init.h>
+#include <linux/module.h>
MODULE_AUTHOR("Takashi Iwai");
MODULE_DESCRIPTION("Routines for control of EMU10K1 WaveTable synth");
@@ -28,12 +16,12 @@ 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;
- unsigned long flags;
arg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
if (arg == NULL)
@@ -54,7 +42,6 @@ static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev)
emux->hw = hw;
emux->max_voices = arg->max_voices;
emux->num_ports = arg->seq_ports;
- emux->pitch_shift = -501;
emux->memhdr = hw->memhdr;
/* maximum two ports */
emux->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2;
@@ -68,21 +55,20 @@ static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev)
return -ENOMEM;
}
- spin_lock_irqsave(&hw->voice_lock, flags);
+ guard(spinlock_irq)(&hw->voice_lock);
hw->synth = emux;
hw->get_synth_voice = snd_emu10k1_synth_get_voice;
- spin_unlock_irqrestore(&hw->voice_lock, flags);
dev->driver_data = emux;
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;
if (dev->driver_data == NULL)
return 0; /* not registered actually */
@@ -90,10 +76,10 @@ static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev)
emux = dev->driver_data;
hw = emux->hw;
- spin_lock_irqsave(&hw->voice_lock, flags);
- hw->synth = NULL;
- hw->get_synth_voice = NULL;
- spin_unlock_irqrestore(&hw->voice_lock, flags);
+ scoped_guard(spinlock_irq, &hw->voice_lock) {
+ hw->synth = NULL;
+ hw->get_synth_voice = NULL;
+ }
snd_emux_free(emux);
return 0;
@@ -103,21 +89,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);
diff --git a/sound/pci/emu10k1/emu10k1_synth_local.h b/sound/pci/emu10k1/emu10k1_synth_local.h
index 25f328ff639f..11373695344b 100644
--- a/sound/pci/emu10k1/emu10k1_synth_local.h
+++ b/sound/pci/emu10k1/emu10k1_synth_local.h
@@ -1,23 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __EMU10K1_SYNTH_LOCAL_H
#define __EMU10K1_SYNTH_LOCAL_H
/*
* Local defininitons for Emu10k1 wavetable
*
* Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/time.h>
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index df47f738098d..9607a0f7174b 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
* Driver EMU10K1X chips
@@ -13,28 +14,14 @@
* Chips (SB0200 model):
* - EMU10K1X-DBQ
* - STAC 9708T
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
@@ -45,12 +32,11 @@
MODULE_AUTHOR("Francisco Moraes <fmoraes@nc.rr.com>");
MODULE_DESCRIPTION("EMU10K1X");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Dell Creative Labs,SB Live!}");
// module parameters (see "Module Parameters")
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the EMU10K1X soundcard.");
@@ -114,7 +100,7 @@ MODULE_PARM_DESC(enable, "Enable the EMU10K1X soundcard.");
*/
#define PLAYBACK_LIST_SIZE 0x01 /* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000 */
#define PLAYBACK_LIST_PTR 0x02 /* Pointer to the current period being played */
-#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA addresss */
+#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA address */
#define PLAYBACK_PERIOD_SIZE 0x05 /* Playback period size */
#define PLAYBACK_POINTER 0x06 /* Playback period pointer. Sample currently in DAC */
#define PLAYBACK_UNKNOWN1 0x07
@@ -180,7 +166,7 @@ MODULE_PARM_DESC(enable, "Enable the EMU10K1X soundcard.");
/* From 0x50 - 0x5f, last samples captured */
-/**
+/*
* The hardware has 3 channels for playback and 1 for capture.
* - channel 0 is the front channel
* - channel 1 is the rear channel
@@ -231,7 +217,6 @@ struct emu10k1x {
struct pci_dev *pci;
unsigned long port;
- struct resource *res_port;
int irq;
unsigned char revision; /* chip revision */
@@ -248,13 +233,13 @@ struct emu10k1x {
struct emu10k1x_voice capture_voice;
u32 spdif_bits[3]; // SPDIF out setup
- struct snd_dma_buffer dma_buffer;
+ struct snd_dma_buffer *dma_buffer;
struct emu10k1x_midi midi;
};
/* hardware definition */
-static struct snd_pcm_hardware snd_emu10k1x_playback_hw = {
+static const struct snd_pcm_hardware snd_emu10k1x_playback_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -273,7 +258,7 @@ static struct snd_pcm_hardware snd_emu10k1x_playback_hw = {
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_emu10k1x_capture_hw = {
+static const struct snd_pcm_hardware snd_emu10k1x_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -296,16 +281,13 @@ static unsigned int snd_emu10k1x_ptr_read(struct emu10k1x * emu,
unsigned int reg,
unsigned int chn)
{
- unsigned long flags;
- unsigned int regptr, val;
+ unsigned int regptr;
regptr = (reg << 16) | chn;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
outl(regptr, emu->port + PTR);
- val = inl(emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- return val;
+ return inl(emu->port + DATA);
}
static void snd_emu10k1x_ptr_write(struct emu10k1x *emu,
@@ -314,45 +296,36 @@ static void snd_emu10k1x_ptr_write(struct emu10k1x *emu,
unsigned int data)
{
unsigned int regptr;
- unsigned long flags;
regptr = (reg << 16) | chn;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
outl(regptr, emu->port + PTR);
outl(data, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
static void snd_emu10k1x_intr_enable(struct emu10k1x *emu, unsigned int intrenb)
{
- unsigned long flags;
unsigned int intr_enable;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
intr_enable = inl(emu->port + INTE) | intrenb;
outl(intr_enable, emu->port + INTE);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
static void snd_emu10k1x_intr_disable(struct emu10k1x *emu, unsigned int intrenb)
{
- unsigned long flags;
unsigned int intr_enable;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
intr_enable = inl(emu->port + INTE) & ~intrenb;
outl(intr_enable, emu->port + INTE);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
static void snd_emu10k1x_gpio_write(struct emu10k1x *emu, unsigned int value)
{
- unsigned long flags;
-
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
outl(value, emu->port + GPIO);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
static void snd_emu10k1x_pcm_free_substream(struct snd_pcm_runtime *runtime)
@@ -364,12 +337,14 @@ static void snd_emu10k1x_pcm_interrupt(struct emu10k1x *emu, struct emu10k1x_voi
{
struct emu10k1x_pcm *epcm;
- if ((epcm = voice->epcm) == NULL)
+ epcm = voice->epcm;
+ if (!epcm)
return;
if (epcm->substream == NULL)
return;
#if 0
- snd_printk(KERN_INFO "IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
+ dev_info(emu->card->dev,
+ "IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
epcm->substream->ops->pointer(epcm->substream),
snd_pcm_lib_period_bytes(epcm->substream),
snd_pcm_lib_buffer_bytes(epcm->substream));
@@ -385,10 +360,11 @@ static int snd_emu10k1x_playback_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) {
+ err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
return err;
- }
- if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+ err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64);
+ if (err < 0)
return err;
epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
@@ -424,8 +400,7 @@ static int snd_emu10k1x_pcm_hw_params(struct snd_pcm_substream *substream,
epcm->voice->epcm = epcm;
}
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
+ return 0;
}
/* hw_free callback */
@@ -445,7 +420,7 @@ static int snd_emu10k1x_pcm_hw_free(struct snd_pcm_substream *substream)
epcm->voice = NULL;
}
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
/* prepare callback */
@@ -455,7 +430,7 @@ static int snd_emu10k1x_pcm_prepare(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct emu10k1x_pcm *epcm = runtime->private_data;
int voice = epcm->voice->number;
- u32 *table_base = (u32 *)(emu->dma_buffer.area+1024*voice);
+ u32 *table_base = (u32 *)(emu->dma_buffer->area+1024*voice);
u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
int i;
@@ -464,7 +439,7 @@ static int snd_emu10k1x_pcm_prepare(struct snd_pcm_substream *substream)
*table_base++=period_size_bytes<<16;
}
- snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_ADDR, voice, emu->dma_buffer.addr+1024*voice);
+ snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_ADDR, voice, emu->dma_buffer->addr+1024*voice);
snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_SIZE, voice, (runtime->periods - 1) << 19);
snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_PTR, voice, 0);
snd_emu10k1x_ptr_write(emu, PLAYBACK_POINTER, voice, 0);
@@ -487,7 +462,11 @@ static int snd_emu10k1x_pcm_trigger(struct snd_pcm_substream *substream,
int channel = epcm->voice->number;
int result = 0;
-// snd_printk(KERN_INFO "trigger - emu10k1x = 0x%x, cmd = %i, pointer = %d\n", (int)emu, cmd, (int)substream->ops->pointer(substream));
+ /*
+ dev_dbg(emu->card->dev,
+ "trigger - emu10k1x = 0x%x, cmd = %i, pointer = %d\n",
+ (int)emu, cmd, (int)substream->ops->pointer(substream));
+ */
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -543,10 +522,9 @@ snd_emu10k1x_pcm_pointer(struct snd_pcm_substream *substream)
}
/* operators */
-static struct snd_pcm_ops snd_emu10k1x_playback_ops = {
+static const struct snd_pcm_ops snd_emu10k1x_playback_ops = {
.open = snd_emu10k1x_playback_open,
.close = snd_emu10k1x_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_emu10k1x_pcm_hw_params,
.hw_free = snd_emu10k1x_pcm_hw_free,
.prepare = snd_emu10k1x_pcm_prepare,
@@ -562,10 +540,12 @@ static int snd_emu10k1x_pcm_open_capture(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
- return err;
- if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
- return err;
+ err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64);
+ if (err < 0)
+ return err;
epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
if (epcm == NULL)
@@ -603,8 +583,7 @@ static int snd_emu10k1x_pcm_hw_params_capture(struct snd_pcm_substream *substrea
epcm->voice->use = 1;
}
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
+ return 0;
}
/* hw_free callback */
@@ -624,7 +603,7 @@ static int snd_emu10k1x_pcm_hw_free_capture(struct snd_pcm_substream *substream)
epcm->voice = NULL;
}
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
/* prepare capture callback */
@@ -689,10 +668,9 @@ snd_emu10k1x_pcm_pointer_capture(struct snd_pcm_substream *substream)
return ptr;
}
-static struct snd_pcm_ops snd_emu10k1x_capture_ops = {
+static const struct snd_pcm_ops snd_emu10k1x_capture_ops = {
.open = snd_emu10k1x_pcm_open_capture,
.close = snd_emu10k1x_pcm_close_capture,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_emu10k1x_pcm_hw_params_capture,
.hw_free = snd_emu10k1x_pcm_hw_free_capture,
.prepare = snd_emu10k1x_pcm_prepare_capture,
@@ -704,26 +682,20 @@ static unsigned short snd_emu10k1x_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
struct emu10k1x *emu = ac97->private_data;
- unsigned long flags;
- unsigned short val;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
outb(reg, emu->port + AC97ADDRESS);
- val = inw(emu->port + AC97DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- return val;
+ return inw(emu->port + AC97DATA);
}
static void snd_emu10k1x_ac97_write(struct snd_ac97 *ac97,
unsigned short reg, unsigned short val)
{
struct emu10k1x *emu = ac97->private_data;
- unsigned long flags;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
outb(reg, emu->port + AC97ADDRESS);
outw(val, emu->port + AC97DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
static int snd_emu10k1x_ac97(struct emu10k1x *chip)
@@ -731,12 +703,13 @@ static int snd_emu10k1x_ac97(struct emu10k1x *chip)
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_emu10k1x_ac97_write,
.read = snd_emu10k1x_ac97_read,
};
- if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus);
+ if (err < 0)
return err;
pbus->no_vra = 1; /* we don't need VRA */
@@ -746,37 +719,15 @@ static int snd_emu10k1x_ac97(struct emu10k1x *chip)
return snd_ac97_mixer(pbus, &ac97, &chip->ac97);
}
-static int snd_emu10k1x_free(struct emu10k1x *chip)
+static void snd_emu10k1x_free(struct snd_card *card)
{
+ struct emu10k1x *chip = card->private_data;
+
snd_emu10k1x_ptr_write(chip, TRIGGER_CHANNEL, 0, 0);
// disable interrupts
outl(0, chip->port + INTE);
// disable audio
outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG);
-
- /* release the irq */
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
-
- // release the i/o port
- release_and_free_resource(chip->res_port);
-
- // release the DMA
- if (chip->dma_buffer.area) {
- snd_dma_free_pages(&chip->dma_buffer);
- }
-
- pci_disable_device(chip->pci);
-
- // release the data
- kfree(chip);
- return 0;
-}
-
-static int snd_emu10k1x_dev_free(struct snd_device *device)
-{
- struct emu10k1x *chip = device->device_data;
- return snd_emu10k1x_free(chip);
}
static irqreturn_t snd_emu10k1x_interrupt(int irq, void *dev_id)
@@ -826,22 +777,34 @@ static irqreturn_t snd_emu10k1x_interrupt(int irq, void *dev_id)
// acknowledge the interrupt if necessary
outl(status, chip->port + IPR);
- // snd_printk(KERN_INFO "interrupt %08x\n", status);
+ /* dev_dbg(chip->card->dev, "interrupt %08x\n", status); */
return IRQ_HANDLED;
}
-static int __devinit snd_emu10k1x_pcm(struct emu10k1x *emu, int device, struct snd_pcm **rpcm)
+static const struct snd_pcm_chmap_elem surround_map[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
+static const struct snd_pcm_chmap_elem clfe_map[] = {
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
+ { }
+};
+
+static int snd_emu10k1x_pcm(struct emu10k1x *emu, int device)
{
struct snd_pcm *pcm;
+ const struct snd_pcm_chmap_elem *map = NULL;
int err;
int capture = 0;
- if (rpcm)
- *rpcm = NULL;
if (device == 0)
capture = 1;
- if ((err = snd_pcm_new(emu->card, "emu10k1x", device, 1, capture, &pcm)) < 0)
+ err = snd_pcm_new(emu->card, "emu10k1x", device, 1, capture, &pcm);
+ if (err < 0)
return err;
pcm->private_data = emu;
@@ -860,53 +823,41 @@ static int __devinit snd_emu10k1x_pcm(struct emu10k1x *emu, int device, struct s
pcm->info_flags = 0;
switch(device) {
case 0:
- strcpy(pcm->name, "EMU10K1X Front");
+ strscpy(pcm->name, "EMU10K1X Front");
+ map = snd_pcm_std_chmaps;
break;
case 1:
- strcpy(pcm->name, "EMU10K1X Rear");
+ strscpy(pcm->name, "EMU10K1X Rear");
+ map = surround_map;
break;
case 2:
- strcpy(pcm->name, "EMU10K1X Center/LFE");
+ strscpy(pcm->name, "EMU10K1X Center/LFE");
+ map = clfe_map;
break;
}
emu->pcm = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(emu->pci),
- 32*1024, 32*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &emu->pci->dev, 32*1024, 32*1024);
- if (rpcm)
- *rpcm = pcm;
-
- return 0;
+ return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, 2,
+ 1 << 2, NULL);
}
-static int __devinit snd_emu10k1x_create(struct snd_card *card,
- struct pci_dev *pci,
- struct emu10k1x **rchip)
+static int snd_emu10k1x_create(struct snd_card *card,
+ struct pci_dev *pci)
{
- struct emu10k1x *chip;
+ struct emu10k1x *chip = card->private_data;
int err;
int ch;
- static struct snd_device_ops ops = {
- .dev_free = snd_emu10k1x_dev_free,
- };
-
- *rchip = NULL;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
- snd_printk(KERN_ERR "error to set 28bit mask DMA\n");
- pci_disable_device(pci);
- return -ENXIO;
- }
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(28)) < 0) {
+ dev_err(card->dev, "error to set 28bit mask DMA\n");
+ return -ENXIO;
}
chip->card = card;
@@ -916,34 +867,31 @@ static int __devinit snd_emu10k1x_create(struct snd_card *card,
spin_lock_init(&chip->emu_lock);
spin_lock_init(&chip->voice_lock);
+ err = pcim_request_all_regions(pci, "EMU10K1X");
+ if (err < 0)
+ return err;
chip->port = pci_resource_start(pci, 0);
- if ((chip->res_port = request_region(chip->port, 8,
- "EMU10K1X")) == NULL) {
- snd_printk(KERN_ERR "emu10k1x: cannot allocate the port 0x%lx\n", chip->port);
- snd_emu10k1x_free(chip);
- return -EBUSY;
- }
- if (request_irq(pci->irq, snd_emu10k1x_interrupt,
- IRQF_SHARED, "EMU10K1X", chip)) {
- snd_printk(KERN_ERR "emu10k1x: cannot grab irq %d\n", pci->irq);
- snd_emu10k1x_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_emu10k1x_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "cannot grab irq %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_emu10k1x_free;
- if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- 4 * 1024, &chip->dma_buffer) < 0) {
- snd_emu10k1x_free(chip);
+ chip->dma_buffer = snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV,
+ 4 * 1024);
+ if (!chip->dma_buffer)
return -ENOMEM;
- }
pci_set_master(pci);
/* read revision & serial */
chip->revision = pci->revision;
pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
- snd_printk(KERN_INFO "Model %04x Rev %08x Serial %08x\n", chip->model,
+ dev_info(card->dev, "Model %04x Rev %08x Serial %08x\n", chip->model,
chip->revision, chip->serial);
outl(0, chip->port + INTE);
@@ -992,12 +940,6 @@ static int __devinit snd_emu10k1x_create(struct snd_card *card,
outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
- chip, &ops)) < 0) {
- snd_emu10k1x_free(chip);
- return err;
- }
- *rchip = chip;
return 0;
}
@@ -1006,14 +948,12 @@ static void snd_emu10k1x_proc_reg_read(struct snd_info_entry *entry,
{
struct emu10k1x *emu = entry->private_data;
unsigned long value,value1,value2;
- unsigned long flags;
int i;
snd_iprintf(buffer, "Registers:\n\n");
for(i = 0; i < 0x20; i+=4) {
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
value = inl(emu->port + i);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
snd_iprintf(buffer, "Register %02X: %08lX\n", i, value);
}
snd_iprintf(buffer, "\nRegisters\n\n");
@@ -1040,22 +980,16 @@ static void snd_emu10k1x_proc_reg_write(struct snd_info_entry *entry,
if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
continue;
- if (reg < 0x49 && val <= 0xffffffff && channel_id <= 2)
+ if (reg < 0x49 && channel_id <= 2)
snd_emu10k1x_ptr_write(emu, reg, channel_id, val);
}
}
-static int __devinit snd_emu10k1x_proc_init(struct emu10k1x * emu)
+static int snd_emu10k1x_proc_init(struct emu10k1x *emu)
{
- struct snd_info_entry *entry;
-
- if(! snd_card_proc_new(emu->card, "emu10k1x_regs", &entry)) {
- snd_info_set_text_ops(entry, emu, snd_emu10k1x_proc_reg_read);
- entry->c.text.write = snd_emu10k1x_proc_reg_write;
- entry->mode |= S_IWUSR;
- entry->private_data = emu;
- }
-
+ snd_card_rw_proc_new(emu->card, "emu10k1x_regs", emu,
+ snd_emu10k1x_proc_reg_read,
+ snd_emu10k1x_proc_reg_write);
return 0;
}
@@ -1076,7 +1010,6 @@ static int snd_emu10k1x_shared_spdif_put(struct snd_kcontrol *kcontrol,
{
struct emu10k1x *emu = snd_kcontrol_chip(kcontrol);
unsigned int val;
- int change = 0;
val = ucontrol->value.integer.value[0] ;
@@ -1091,10 +1024,10 @@ static int snd_emu10k1x_shared_spdif_put(struct snd_kcontrol *kcontrol,
snd_emu10k1x_ptr_write(emu, ROUTING, 0, 0x1003F);
snd_emu10k1x_gpio_write(emu, 0x1080);
}
- return change;
+ return 0;
}
-static struct snd_kcontrol_new snd_emu10k1x_shared_spdif __devinitdata =
+static const struct snd_kcontrol_new snd_emu10k1x_shared_spdif =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog/Digital Output Jack",
@@ -1153,7 +1086,7 @@ static int snd_emu10k1x_spdif_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu10k1x_spdif_mask_control =
+static const struct snd_kcontrol_new snd_emu10k1x_spdif_mask_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1163,7 +1096,7 @@ static struct snd_kcontrol_new snd_emu10k1x_spdif_mask_control =
.get = snd_emu10k1x_spdif_get_mask
};
-static struct snd_kcontrol_new snd_emu10k1x_spdif_control =
+static const struct snd_kcontrol_new snd_emu10k1x_spdif_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -1173,23 +1106,29 @@ static struct snd_kcontrol_new snd_emu10k1x_spdif_control =
.put = snd_emu10k1x_spdif_put
};
-static int __devinit snd_emu10k1x_mixer(struct emu10k1x *emu)
+static int snd_emu10k1x_mixer(struct emu10k1x *emu)
{
int err;
struct snd_kcontrol *kctl;
struct snd_card *card = emu->card;
- if ((kctl = snd_ctl_new1(&snd_emu10k1x_spdif_mask_control, emu)) == NULL)
+ kctl = snd_ctl_new1(&snd_emu10k1x_spdif_mask_control, emu);
+ if (!kctl)
return -ENOMEM;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
- if ((kctl = snd_ctl_new1(&snd_emu10k1x_shared_spdif, emu)) == NULL)
+ kctl = snd_ctl_new1(&snd_emu10k1x_shared_spdif, emu);
+ if (!kctl)
return -ENOMEM;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
- if ((kctl = snd_ctl_new1(&snd_emu10k1x_spdif_control, emu)) == NULL)
+ kctl = snd_ctl_new1(&snd_emu10k1x_spdif_control, emu);
+ if (!kctl)
return -ENOMEM;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
return 0;
@@ -1227,7 +1166,9 @@ static void mpu401_clear_rx(struct emu10k1x *emu, struct emu10k1x_midi *mpu)
mpu401_read_data(emu, mpu);
#ifdef CONFIG_SND_DEBUG
if (timeout <= 0)
- snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n", mpu401_read_stat(emu, mpu));
+ dev_err(emu->card->dev,
+ "cmd: clear rx timeout (status = 0x%x)\n",
+ mpu401_read_stat(emu, mpu));
#endif
}
@@ -1245,28 +1186,28 @@ static void do_emu10k1x_midi_interrupt(struct emu10k1x *emu,
return;
}
- spin_lock(&midi->input_lock);
- if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
- if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
- mpu401_clear_rx(emu, midi);
- } else {
- byte = mpu401_read_data(emu, midi);
- if (midi->substream_input)
- snd_rawmidi_receive(midi->substream_input, &byte, 1);
+ scoped_guard(spinlock, &midi->input_lock) {
+ if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
+ if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
+ mpu401_clear_rx(emu, midi);
+ } else {
+ byte = mpu401_read_data(emu, midi);
+ if (midi->substream_input)
+ snd_rawmidi_receive(midi->substream_input, &byte, 1);
+ }
}
}
- spin_unlock(&midi->input_lock);
- spin_lock(&midi->output_lock);
- if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
- if (midi->substream_output &&
- snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
- mpu401_write_data(emu, midi, byte);
- } else {
- snd_emu10k1x_intr_disable(emu, midi->tx_enable);
+ scoped_guard(spinlock, &midi->output_lock) {
+ if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
+ if (midi->substream_output &&
+ snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
+ mpu401_write_data(emu, midi, byte);
+ } else {
+ snd_emu10k1x_intr_disable(emu, midi->tx_enable);
+ }
}
}
- spin_unlock(&midi->output_lock);
}
static void snd_emu10k1x_midi_interrupt(struct emu10k1x *emu, unsigned int status)
@@ -1277,31 +1218,31 @@ static void snd_emu10k1x_midi_interrupt(struct emu10k1x *emu, unsigned int statu
static int snd_emu10k1x_midi_cmd(struct emu10k1x * emu,
struct emu10k1x_midi *midi, unsigned char cmd, int ack)
{
- unsigned long flags;
int timeout, ok;
- spin_lock_irqsave(&midi->input_lock, flags);
- mpu401_write_data(emu, midi, 0x00);
- /* mpu401_clear_rx(emu, midi); */
-
- mpu401_write_cmd(emu, midi, cmd);
- if (ack) {
- ok = 0;
- timeout = 10000;
- while (!ok && timeout-- > 0) {
- if (mpu401_input_avail(emu, midi)) {
- if (mpu401_read_data(emu, midi) == MPU401_ACK)
- ok = 1;
+ scoped_guard(spinlock_irqsave, &midi->input_lock) {
+ mpu401_write_data(emu, midi, 0x00);
+ /* mpu401_clear_rx(emu, midi); */
+
+ mpu401_write_cmd(emu, midi, cmd);
+ if (ack) {
+ ok = 0;
+ timeout = 10000;
+ while (!ok && timeout-- > 0) {
+ if (mpu401_input_avail(emu, midi)) {
+ if (mpu401_read_data(emu, midi) == MPU401_ACK)
+ ok = 1;
+ }
}
- }
- if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
+ if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
+ ok = 1;
+ } else {
ok = 1;
- } else {
- ok = 1;
+ }
}
- spin_unlock_irqrestore(&midi->input_lock, flags);
if (!ok) {
- snd_printk(KERN_ERR "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
+ dev_err(emu->card->dev,
+ "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
cmd, emu->port,
mpu401_read_stat(emu, midi),
mpu401_read_data(emu, midi));
@@ -1314,100 +1255,78 @@ static int snd_emu10k1x_midi_input_open(struct snd_rawmidi_substream *substream)
{
struct emu10k1x *emu;
struct emu10k1x_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- midi->midi_mode |= EMU10K1X_MIDI_MODE_INPUT;
- midi->substream_input = substream;
- if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1))
- goto error_out;
- if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
- goto error_out;
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &midi->open_lock) {
+ midi->midi_mode |= EMU10K1X_MIDI_MODE_INPUT;
+ midi->substream_input = substream;
+ if (midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)
+ return 0;
}
+ if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1))
+ return -EIO;
+ if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+ return -EIO;
return 0;
-
-error_out:
- return -EIO;
}
static int snd_emu10k1x_midi_output_open(struct snd_rawmidi_substream *substream)
{
struct emu10k1x *emu;
struct emu10k1x_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- midi->midi_mode |= EMU10K1X_MIDI_MODE_OUTPUT;
- midi->substream_output = substream;
- if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1))
- goto error_out;
- if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
- goto error_out;
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &midi->open_lock) {
+ midi->midi_mode |= EMU10K1X_MIDI_MODE_OUTPUT;
+ midi->substream_output = substream;
+ if (midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)
+ return 0;
}
+ if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1))
+ return -EIO;
+ if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+ return -EIO;
return 0;
-
-error_out:
- return -EIO;
}
static int snd_emu10k1x_midi_input_close(struct snd_rawmidi_substream *substream)
{
struct emu10k1x *emu;
struct emu10k1x_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
- int err = 0;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- snd_emu10k1x_intr_disable(emu, midi->rx_enable);
- midi->midi_mode &= ~EMU10K1X_MIDI_MODE_INPUT;
- midi->substream_input = NULL;
- if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- err = snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &midi->open_lock) {
+ snd_emu10k1x_intr_disable(emu, midi->rx_enable);
+ midi->midi_mode &= ~EMU10K1X_MIDI_MODE_INPUT;
+ midi->substream_input = NULL;
+ if (midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)
+ return 0;
}
- return err;
+ return snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
}
static int snd_emu10k1x_midi_output_close(struct snd_rawmidi_substream *substream)
{
struct emu10k1x *emu;
struct emu10k1x_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
- int err = 0;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- snd_emu10k1x_intr_disable(emu, midi->tx_enable);
- midi->midi_mode &= ~EMU10K1X_MIDI_MODE_OUTPUT;
- midi->substream_output = NULL;
- if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- err = snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irqsave, &midi->open_lock) {
+ snd_emu10k1x_intr_disable(emu, midi->tx_enable);
+ midi->midi_mode &= ~EMU10K1X_MIDI_MODE_OUTPUT;
+ midi->substream_output = NULL;
+ if (midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)
+ return 0;
}
- return err;
+ return snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
}
static void snd_emu10k1x_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
@@ -1428,7 +1347,6 @@ static void snd_emu10k1x_midi_output_trigger(struct snd_rawmidi_substream *subst
{
struct emu10k1x *emu;
struct emu10k1x_midi *midi = substream->rmidi->private_data;
- unsigned long flags;
emu = midi->emu;
if (snd_BUG_ON(!emu))
@@ -1439,22 +1357,21 @@ static void snd_emu10k1x_midi_output_trigger(struct snd_rawmidi_substream *subst
unsigned char byte;
/* try to send some amount of bytes here before interrupts */
- spin_lock_irqsave(&midi->output_lock, flags);
- while (max > 0) {
- if (mpu401_output_ready(emu, midi)) {
- if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT) ||
- snd_rawmidi_transmit(substream, &byte, 1) != 1) {
- /* no more data */
- spin_unlock_irqrestore(&midi->output_lock, flags);
- return;
+ scoped_guard(spinlock_irqsave, &midi->output_lock) {
+ while (max > 0) {
+ if (mpu401_output_ready(emu, midi)) {
+ if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT) ||
+ snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+ /* no more data */
+ return;
+ }
+ mpu401_write_data(emu, midi, byte);
+ max--;
+ } else {
+ break;
}
- mpu401_write_data(emu, midi, byte);
- max--;
- } else {
- break;
}
}
- spin_unlock_irqrestore(&midi->output_lock, flags);
snd_emu10k1x_intr_enable(emu, midi->tx_enable);
} else {
snd_emu10k1x_intr_disable(emu, midi->tx_enable);
@@ -1465,14 +1382,14 @@ static void snd_emu10k1x_midi_output_trigger(struct snd_rawmidi_substream *subst
*/
-static struct snd_rawmidi_ops snd_emu10k1x_midi_output =
+static const struct snd_rawmidi_ops snd_emu10k1x_midi_output =
{
.open = snd_emu10k1x_midi_output_open,
.close = snd_emu10k1x_midi_output_close,
.trigger = snd_emu10k1x_midi_output_trigger,
};
-static struct snd_rawmidi_ops snd_emu10k1x_midi_input =
+static const struct snd_rawmidi_ops snd_emu10k1x_midi_input =
{
.open = snd_emu10k1x_midi_input_open,
.close = snd_emu10k1x_midi_input_close,
@@ -1486,19 +1403,21 @@ static void snd_emu10k1x_midi_free(struct snd_rawmidi *rmidi)
midi->rmidi = NULL;
}
-static int __devinit emu10k1x_midi_init(struct emu10k1x *emu,
- struct emu10k1x_midi *midi, int device, char *name)
+static int emu10k1x_midi_init(struct emu10k1x *emu,
+ struct emu10k1x_midi *midi, int device,
+ char *name)
{
struct snd_rawmidi *rmidi;
int err;
- if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0)
+ err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi);
+ if (err < 0)
return err;
midi->emu = emu;
spin_lock_init(&midi->open_lock);
spin_lock_init(&midi->input_lock);
spin_lock_init(&midi->output_lock);
- strcpy(rmidi->name, name);
+ strscpy(rmidi->name, name);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1x_midi_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1x_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
@@ -1510,12 +1429,13 @@ static int __devinit emu10k1x_midi_init(struct emu10k1x *emu,
return 0;
}
-static int __devinit snd_emu10k1x_midi(struct emu10k1x *emu)
+static int snd_emu10k1x_midi(struct emu10k1x *emu)
{
struct emu10k1x_midi *midi = &emu->midi;
int err;
- if ((err = emu10k1x_midi_init(emu, midi, 0, "EMU10K1X MPU-401 (UART)")) < 0)
+ err = emu10k1x_midi_init(emu, midi, 0, "EMU10K1X MPU-401 (UART)");
+ if (err < 0)
return err;
midi->tx_enable = INTE_MIDITXENABLE;
@@ -1527,8 +1447,8 @@ static int __devinit snd_emu10k1x_midi(struct emu10k1x *emu)
return 0;
}
-static int __devinit snd_emu10k1x_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_emu10k1x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -1542,94 +1462,72 @@ static int __devinit snd_emu10k1x_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- if ((err = snd_emu10k1x_create(card, pci, &chip)) < 0) {
- snd_card_free(card);
+ err = snd_emu10k1x_create(card, pci);
+ if (err < 0)
return err;
- }
- if ((err = snd_emu10k1x_pcm(chip, 0, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_emu10k1x_pcm(chip, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_emu10k1x_pcm(chip, 1, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_emu10k1x_pcm(chip, 1);
+ if (err < 0)
return err;
- }
- if ((err = snd_emu10k1x_pcm(chip, 2, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_emu10k1x_pcm(chip, 2);
+ if (err < 0)
return err;
- }
- if ((err = snd_emu10k1x_ac97(chip)) < 0) {
- snd_card_free(card);
+ err = snd_emu10k1x_ac97(chip);
+ if (err < 0)
return err;
- }
- if ((err = snd_emu10k1x_mixer(chip)) < 0) {
- snd_card_free(card);
+ err = snd_emu10k1x_mixer(chip);
+ if (err < 0)
return err;
- }
- if ((err = snd_emu10k1x_midi(chip)) < 0) {
- snd_card_free(card);
+ err = snd_emu10k1x_midi(chip);
+ if (err < 0)
return err;
- }
snd_emu10k1x_proc_init(chip);
- strcpy(card->driver, "EMU10K1X");
- strcpy(card->shortname, "Dell Sound Blaster Live!");
+ strscpy(card->driver, "EMU10K1X");
+ strscpy(card->shortname, "Dell Sound Blaster Live!");
sprintf(card->longname, "%s at 0x%lx irq %i",
card->shortname, chip->port, chip->irq);
- snd_card_set_dev(card, &pci->dev);
-
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_emu10k1x_remove(struct pci_dev *pci)
+static int snd_emu10k1x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_emu10k1x_probe(pci, pci_id));
}
// PCI IDs
-static DEFINE_PCI_DEVICE_TABLE(snd_emu10k1x_ids) = {
+static const struct pci_device_id snd_emu10k1x_ids[] = {
{ PCI_VDEVICE(CREATIVE, 0x0006), 0 }, /* Dell OEM version (EMU10K1) */
{ 0, }
};
MODULE_DEVICE_TABLE(pci, snd_emu10k1x_ids);
// pci_driver definition
-static struct pci_driver driver = {
- .name = "EMU10K1X",
+static struct pci_driver emu10k1x_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_emu10k1x_ids,
.probe = snd_emu10k1x_probe,
- .remove = __devexit_p(snd_emu10k1x_remove),
};
-// initialization of the module
-static int __init alsa_card_emu10k1x_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-// clean up the module
-static void __exit alsa_card_emu10k1x_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_emu10k1x_init)
-module_exit(alsa_card_emu10k1x_exit)
+module_pci_driver(emu10k1x_driver);
diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
index 7a9401462c1c..37af7bf76347 100644
--- a/sound/pci/emu10k1/emufx.c
+++ b/sound/pci/emu10k1/emufx.c
@@ -1,41 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * James Courtier-Dutton <James@superbug.co.uk>
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
* Creative Labs, Inc.
- * Routines for effect processor FX8010
- *
- * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
- * Added EMU 1010 support.
- *
- * BUGS:
- * --
- *
- * TODO:
- * --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
+ * Routines for effect processor FX8010
*/
#include <linux/pci.h>
#include <linux/capability.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/moduleparam.h>
+#include <linux/nospec.h>
#include <sound/core.h>
#include <sound/tlv.h>
@@ -59,26 +41,45 @@ MODULE_PARM_DESC(high_res_gpr_volume, "GPR mixer controls use 31-bit range.");
* Tables
*/
-static char *fxbuses[16] = {
+// Playback channel labels; corresponds with the public FXBUS_* defines.
+// Unlike the tables below, this is not determined by the hardware.
+const char * const snd_emu10k1_fxbus[32] = {
/* 0x00 */ "PCM Left",
/* 0x01 */ "PCM Right",
- /* 0x02 */ "PCM Surround Left",
- /* 0x03 */ "PCM Surround Right",
+ /* 0x02 */ "PCM Rear Left",
+ /* 0x03 */ "PCM Rear Right",
/* 0x04 */ "MIDI Left",
/* 0x05 */ "MIDI Right",
- /* 0x06 */ "Center",
- /* 0x07 */ "LFE",
- /* 0x08 */ NULL,
- /* 0x09 */ NULL,
+ /* 0x06 */ "PCM Center",
+ /* 0x07 */ "PCM LFE",
+ /* 0x08 */ "PCM Front Left",
+ /* 0x09 */ "PCM Front Right",
/* 0x0a */ NULL,
/* 0x0b */ NULL,
/* 0x0c */ "MIDI Reverb",
/* 0x0d */ "MIDI Chorus",
- /* 0x0e */ NULL,
- /* 0x0f */ NULL
+ /* 0x0e */ "PCM Side Left",
+ /* 0x0f */ "PCM Side Right",
+ /* 0x10 */ NULL,
+ /* 0x11 */ NULL,
+ /* 0x12 */ NULL,
+ /* 0x13 */ NULL,
+ /* 0x14 */ "Passthrough Left",
+ /* 0x15 */ "Passthrough Right",
+ /* 0x16 */ NULL,
+ /* 0x17 */ NULL,
+ /* 0x18 */ NULL,
+ /* 0x19 */ NULL,
+ /* 0x1a */ NULL,
+ /* 0x1b */ NULL,
+ /* 0x1c */ NULL,
+ /* 0x1d */ NULL,
+ /* 0x1e */ NULL,
+ /* 0x1f */ NULL
};
-static char *creative_ins[16] = {
+// Physical inputs; corresponds with the public EXTIN_* defines.
+const char * const snd_emu10k1_sblive_ins[16] = {
/* 0x00 */ "AC97 Left",
/* 0x01 */ "AC97 Right",
/* 0x02 */ "TTL IEC958 Left",
@@ -97,7 +98,8 @@ static char *creative_ins[16] = {
/* 0x0f */ NULL
};
-static char *audigy_ins[16] = {
+// Physical inputs; corresponds with the public A_EXTIN_* defines.
+const char * const snd_emu10k1_audigy_ins[16] = {
/* 0x00 */ "AC97 Left",
/* 0x01 */ "AC97 Right",
/* 0x02 */ "Audigy CD Left",
@@ -116,7 +118,8 @@ static char *audigy_ins[16] = {
/* 0x0f */ NULL
};
-static char *creative_outs[32] = {
+// Physical outputs; corresponds with the public EXTOUT_* defines.
+const char * const snd_emu10k1_sblive_outs[32] = {
/* 0x00 */ "AC97 Left",
/* 0x01 */ "AC97 Right",
/* 0x02 */ "Optical IEC958 Left",
@@ -133,6 +136,7 @@ static char *creative_outs[32] = {
/* 0x0d */ "AC97 Surround Left",
/* 0x0e */ "AC97 Surround Right",
/* 0x0f */ NULL,
+ // This is actually the FXBUS2 range; SB Live! 5.1 only.
/* 0x10 */ NULL,
/* 0x11 */ "Analog Center",
/* 0x12 */ "Analog LFE",
@@ -151,7 +155,8 @@ static char *creative_outs[32] = {
/* 0x1f */ NULL,
};
-static char *audigy_outs[32] = {
+// Physical outputs; corresponds with the public A_EXTOUT_* defines.
+const char * const snd_emu10k1_audigy_outs[32] = {
/* 0x00 */ "Digital Front Left",
/* 0x01 */ "Digital Front Right",
/* 0x02 */ "Digital Center",
@@ -170,7 +175,7 @@ static char *audigy_outs[32] = {
/* 0x0f */ "Rear Right",
/* 0x10 */ "AC97 Front Left",
/* 0x11 */ "AC97 Front Right",
- /* 0x12 */ "ADC Caputre Left",
+ /* 0x12 */ "ADC Capture Left",
/* 0x13 */ "ADC Capture Right",
/* 0x14 */ NULL,
/* 0x15 */ NULL,
@@ -186,6 +191,18 @@ static char *audigy_outs[32] = {
/* 0x1f */ NULL,
};
+// On the SB Live! 5.1, FXBUS2[1] and FXBUS2[2] are occupied by EXTOUT_ACENTER
+// and EXTOUT_ALFE, so we can't connect inputs to them for multitrack recording.
+//
+// Since only 14 of the 16 EXTINs are used, this is not a big problem.
+// We route AC97 to FX capture 14 and 15, SPDIF_CD to FX capture 0 and 3,
+// and the rest of the EXTINs to the corresponding FX capture channel.
+// Multitrack recorders will still see the center/LFE output signal
+// on the second and third "input" channel.
+const s8 snd_emu10k1_sblive51_fxbus2_map[16] = {
+ 2, -1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 1
+};
+
static const u32 bass_table[41][5] = {
{ 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 },
{ 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d },
@@ -303,26 +320,14 @@ static const u32 db_table[101] = {
static const DECLARE_TLV_DB_SCALE(snd_emu10k1_db_scale1, -4000, 40, 1);
static const DECLARE_TLV_DB_LINEAR(snd_emu10k1_db_linear, TLV_DB_GAIN_MUTE, 0);
+/* EMU10K1 bass/treble db gain */
+static const DECLARE_TLV_DB_SCALE(snd_emu10k1_bass_treble_db_scale, -1200, 60, 0);
+
static const u32 onoff_table[2] = {
0x00000000, 0x00000001
};
/*
- */
-
-static inline mm_segment_t snd_enter_user(void)
-{
- mm_segment_t fs = get_fs();
- set_fs(get_ds());
- return fs;
-}
-
-static inline void snd_leave_user(mm_segment_t fs)
-{
- set_fs(fs);
-}
-
-/*
* controls
*/
@@ -343,16 +348,12 @@ static int snd_emu10k1_gpr_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ct
static int snd_emu10k1_gpr_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_fx8010_ctl *ctl =
(struct snd_emu10k1_fx8010_ctl *) kcontrol->private_value;
- unsigned long flags;
unsigned int i;
- spin_lock_irqsave(&emu->reg_lock, flags);
for (i = 0; i < ctl->vcount; i++)
ucontrol->value.integer.value[i] = ctl->value[i];
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
@@ -361,12 +362,10 @@ static int snd_emu10k1_gpr_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_fx8010_ctl *ctl =
(struct snd_emu10k1_fx8010_ctl *) kcontrol->private_value;
- unsigned long flags;
- unsigned int nval, val;
+ int nval, val;
unsigned int i, j;
int change = 0;
- spin_lock_irqsave(&emu->reg_lock, flags);
for (i = 0; i < ctl->vcount; i++) {
nval = ucontrol->value.integer.value[i];
if (nval < ctl->min)
@@ -380,9 +379,16 @@ static int snd_emu10k1_gpr_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl
case EMU10K1_GPR_TRANSLATION_NONE:
snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, val);
break;
+ case EMU10K1_GPR_TRANSLATION_NEGATE:
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, ~val);
+ break;
case EMU10K1_GPR_TRANSLATION_TABLE100:
snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, db_table[val]);
break;
+ case EMU10K1_GPR_TRANSLATION_NEG_TABLE100:
+ snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0,
+ val == 100 ? 0x80000000 : -(int)db_table[val]);
+ break;
case EMU10K1_GPR_TRANSLATION_BASS:
if ((ctl->count % 5) != 0 || (ctl->count / 5) != ctl->vcount) {
change = -EIO;
@@ -405,7 +411,6 @@ static int snd_emu10k1_gpr_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl
}
}
__error:
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
@@ -433,19 +438,13 @@ int snd_emu10k1_fx8010_register_irq_handler(struct snd_emu10k1 *emu,
snd_fx8010_irq_handler_t *handler,
unsigned char gpr_running,
void *private_data,
- struct snd_emu10k1_fx8010_irq **r_irq)
+ struct snd_emu10k1_fx8010_irq *irq)
{
- struct snd_emu10k1_fx8010_irq *irq;
- unsigned long flags;
-
- irq = kmalloc(sizeof(*irq), GFP_ATOMIC);
- if (irq == NULL)
- return -ENOMEM;
irq->handler = handler;
irq->gpr_running = gpr_running;
irq->private_data = private_data;
irq->next = NULL;
- spin_lock_irqsave(&emu->fx8010.irq_lock, flags);
+ guard(spinlock_irqsave)(&emu->fx8010.irq_lock);
if (emu->fx8010.irq_handlers == NULL) {
emu->fx8010.irq_handlers = irq;
emu->dsp_interrupt = snd_emu10k1_fx8010_interrupt;
@@ -454,9 +453,6 @@ int snd_emu10k1_fx8010_register_irq_handler(struct snd_emu10k1 *emu,
irq->next = emu->fx8010.irq_handlers;
emu->fx8010.irq_handlers = irq;
}
- spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
- if (r_irq)
- *r_irq = irq;
return 0;
}
@@ -464,10 +460,10 @@ int snd_emu10k1_fx8010_unregister_irq_handler(struct snd_emu10k1 *emu,
struct snd_emu10k1_fx8010_irq *irq)
{
struct snd_emu10k1_fx8010_irq *tmp;
- unsigned long flags;
- spin_lock_irqsave(&emu->fx8010.irq_lock, flags);
- if ((tmp = emu->fx8010.irq_handlers) == irq) {
+ guard(spinlock_irqsave)(&emu->fx8010.irq_lock);
+ tmp = emu->fx8010.irq_handlers;
+ if (tmp == irq) {
emu->fx8010.irq_handlers = tmp->next;
if (emu->fx8010.irq_handlers == NULL) {
snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
@@ -479,8 +475,6 @@ int snd_emu10k1_fx8010_unregister_irq_handler(struct snd_emu10k1 *emu,
if (tmp)
tmp->next = tmp->next->next;
}
- spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
- kfree(irq);
return 0;
}
@@ -495,7 +489,7 @@ static void snd_emu10k1_write_op(struct snd_emu10k1_fx8010_code *icode,
u_int32_t *code;
if (snd_BUG_ON(*ptr >= 512))
return;
- code = (u_int32_t __force *)icode->code + (*ptr) * 2;
+ code = icode->code + (*ptr) * 2;
set_bit(*ptr, icode->code_valid);
code[0] = ((x & 0x3ff) << 10) | (y & 0x3ff);
code[1] = ((op & 0x0f) << 20) | ((r & 0x3ff) << 10) | (a & 0x3ff);
@@ -512,7 +506,7 @@ static void snd_emu10k1_audigy_write_op(struct snd_emu10k1_fx8010_code *icode,
u_int32_t *code;
if (snd_BUG_ON(*ptr >= 1024))
return;
- code = (u_int32_t __force *)icode->code + (*ptr) * 2;
+ code = icode->code + (*ptr) * 2;
set_bit(*ptr, icode->code_valid);
code[0] = ((x & 0x7ff) << 12) | (y & 0x7ff);
code[1] = ((op & 0x0f) << 24) | ((r & 0x7ff) << 12) | (a & 0x7ff);
@@ -535,7 +529,8 @@ unsigned int snd_emu10k1_efx_read(struct snd_emu10k1 *emu, unsigned int pc)
}
static int snd_emu10k1_gpr_poke(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_code *icode)
+ struct snd_emu10k1_fx8010_code *icode,
+ bool in_kernel)
{
int gpr;
u32 val;
@@ -543,7 +538,9 @@ static int snd_emu10k1_gpr_poke(struct snd_emu10k1 *emu,
for (gpr = 0; gpr < (emu->audigy ? 0x200 : 0x100); gpr++) {
if (!test_bit(gpr, icode->gpr_valid))
continue;
- if (get_user(val, &icode->gpr_map[gpr]))
+ if (in_kernel)
+ val = icode->gpr_map[gpr];
+ else if (get_user(val, (__user u32 *)&icode->gpr_map[gpr]))
return -EFAULT;
snd_emu10k1_ptr_write(emu, emu->gpr_base + gpr, 0, val);
}
@@ -559,14 +556,15 @@ static int snd_emu10k1_gpr_peek(struct snd_emu10k1 *emu,
for (gpr = 0; gpr < (emu->audigy ? 0x200 : 0x100); gpr++) {
set_bit(gpr, icode->gpr_valid);
val = snd_emu10k1_ptr_read(emu, emu->gpr_base + gpr, 0);
- if (put_user(val, &icode->gpr_map[gpr]))
+ if (put_user(val, (__user u32 *)&icode->gpr_map[gpr]))
return -EFAULT;
}
return 0;
}
static int snd_emu10k1_tram_poke(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_code *icode)
+ struct snd_emu10k1_fx8010_code *icode,
+ bool in_kernel)
{
int tram;
u32 addr, val;
@@ -574,9 +572,14 @@ static int snd_emu10k1_tram_poke(struct snd_emu10k1 *emu,
for (tram = 0; tram < (emu->audigy ? 0x100 : 0xa0); tram++) {
if (!test_bit(tram, icode->tram_valid))
continue;
- if (get_user(val, &icode->tram_data_map[tram]) ||
- get_user(addr, &icode->tram_addr_map[tram]))
- return -EFAULT;
+ if (in_kernel) {
+ val = icode->tram_data_map[tram];
+ addr = icode->tram_addr_map[tram];
+ } else {
+ if (get_user(val, (__user __u32 *)&icode->tram_data_map[tram]) ||
+ get_user(addr, (__user __u32 *)&icode->tram_addr_map[tram]))
+ return -EFAULT;
+ }
snd_emu10k1_ptr_write(emu, TANKMEMDATAREGBASE + tram, 0, val);
if (!emu->audigy) {
snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + tram, 0, addr);
@@ -604,24 +607,30 @@ static int snd_emu10k1_tram_peek(struct snd_emu10k1 *emu,
addr = snd_emu10k1_ptr_read(emu, TANKMEMADDRREGBASE + tram, 0) >> 12;
addr |= snd_emu10k1_ptr_read(emu, A_TANKMEMCTLREGBASE + tram, 0) << 20;
}
- if (put_user(val, &icode->tram_data_map[tram]) ||
- put_user(addr, &icode->tram_addr_map[tram]))
+ if (put_user(val, (__user u32 *)&icode->tram_data_map[tram]) ||
+ put_user(addr, (__user u32 *)&icode->tram_addr_map[tram]))
return -EFAULT;
}
return 0;
}
static int snd_emu10k1_code_poke(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_code *icode)
+ struct snd_emu10k1_fx8010_code *icode,
+ bool in_kernel)
{
u32 pc, lo, hi;
for (pc = 0; pc < (emu->audigy ? 2*1024 : 2*512); pc += 2) {
if (!test_bit(pc / 2, icode->code_valid))
continue;
- if (get_user(lo, &icode->code[pc + 0]) ||
- get_user(hi, &icode->code[pc + 1]))
- return -EFAULT;
+ if (in_kernel) {
+ lo = icode->code[pc + 0];
+ hi = icode->code[pc + 1];
+ } else {
+ if (get_user(lo, (__user u32 *)&icode->code[pc + 0]) ||
+ get_user(hi, (__user u32 *)&icode->code[pc + 1]))
+ return -EFAULT;
+ }
snd_emu10k1_efx_write(emu, pc + 0, lo);
snd_emu10k1_efx_write(emu, pc + 1, hi);
}
@@ -636,25 +645,29 @@ static int snd_emu10k1_code_peek(struct snd_emu10k1 *emu,
memset(icode->code_valid, 0, sizeof(icode->code_valid));
for (pc = 0; pc < (emu->audigy ? 2*1024 : 2*512); pc += 2) {
set_bit(pc / 2, icode->code_valid);
- if (put_user(snd_emu10k1_efx_read(emu, pc + 0), &icode->code[pc + 0]))
+ if (put_user(snd_emu10k1_efx_read(emu, pc + 0),
+ (__user u32 *)&icode->code[pc + 0]))
return -EFAULT;
- if (put_user(snd_emu10k1_efx_read(emu, pc + 1), &icode->code[pc + 1]))
+ if (put_user(snd_emu10k1_efx_read(emu, pc + 1),
+ (__user u32 *)&icode->code[pc + 1]))
return -EFAULT;
}
return 0;
}
static struct snd_emu10k1_fx8010_ctl *
-snd_emu10k1_look_for_ctl(struct snd_emu10k1 *emu, struct snd_ctl_elem_id *id)
+snd_emu10k1_look_for_ctl(struct snd_emu10k1 *emu,
+ struct emu10k1_ctl_elem_id *_id)
{
+ struct snd_ctl_elem_id *id = (struct snd_ctl_elem_id *)_id;
struct snd_emu10k1_fx8010_ctl *ctl;
struct snd_kcontrol *kcontrol;
list_for_each_entry(ctl, &emu->fx8010.gpr_ctl, list) {
kcontrol = ctl->kcontrol;
if (kcontrol->id.iface == id->iface &&
- !strcmp(kcontrol->id.name, id->name) &&
- kcontrol->id.index == id->index)
+ kcontrol->id.index == id->index &&
+ !strcmp(kcontrol->id.name, id->name))
return ctl;
}
return NULL;
@@ -662,14 +675,16 @@ snd_emu10k1_look_for_ctl(struct snd_emu10k1 *emu, struct snd_ctl_elem_id *id)
#define MAX_TLV_SIZE 256
-static unsigned int *copy_tlv(const unsigned int __user *_tlv)
+static unsigned int *copy_tlv(const unsigned int __user *_tlv, bool in_kernel)
{
unsigned int data[2];
unsigned int *tlv;
if (!_tlv)
return NULL;
- if (copy_from_user(data, _tlv, sizeof(data)))
+ if (in_kernel)
+ memcpy(data, (__force void *)_tlv, sizeof(data));
+ else if (copy_from_user(data, _tlv, sizeof(data)))
return NULL;
if (data[1] >= MAX_TLV_SIZE)
return NULL;
@@ -677,7 +692,9 @@ static unsigned int *copy_tlv(const unsigned int __user *_tlv)
if (!tlv)
return NULL;
memcpy(tlv, data, sizeof(data));
- if (copy_from_user(tlv + 2, _tlv + 2, data[1])) {
+ if (in_kernel) {
+ memcpy(tlv + 2, (__force void *)(_tlv + 2), data[1]);
+ } else if (copy_from_user(tlv + 2, _tlv + 2, data[1])) {
kfree(tlv);
return NULL;
}
@@ -685,48 +702,77 @@ static unsigned int *copy_tlv(const unsigned int __user *_tlv)
}
static int copy_gctl(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_control_gpr *gctl,
- struct snd_emu10k1_fx8010_control_gpr __user *_gctl,
- int idx)
+ struct snd_emu10k1_fx8010_control_gpr *dst,
+ struct snd_emu10k1_fx8010_control_gpr *src,
+ int idx, bool in_kernel)
{
- struct snd_emu10k1_fx8010_control_old_gpr __user *octl;
+ struct snd_emu10k1_fx8010_control_gpr __user *_src;
+ struct snd_emu10k1_fx8010_control_old_gpr *octl;
+ struct snd_emu10k1_fx8010_control_old_gpr __user *_octl;
+
+ _src = (struct snd_emu10k1_fx8010_control_gpr __user *)src;
+ if (emu->support_tlv) {
+ if (in_kernel)
+ *dst = src[idx];
+ else if (copy_from_user(dst, &_src[idx], sizeof(*src)))
+ return -EFAULT;
+ return 0;
+ }
- if (emu->support_tlv)
- return copy_from_user(gctl, &_gctl[idx], sizeof(*gctl));
- octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)_gctl;
- if (copy_from_user(gctl, &octl[idx], sizeof(*octl)))
+ octl = (struct snd_emu10k1_fx8010_control_old_gpr *)src;
+ _octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)octl;
+ if (in_kernel)
+ memcpy(dst, &octl[idx], sizeof(*octl));
+ else if (copy_from_user(dst, &_octl[idx], sizeof(*octl)))
return -EFAULT;
- gctl->tlv = NULL;
+ dst->tlv = NULL;
return 0;
}
static int copy_gctl_to_user(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_control_gpr __user *_gctl,
- struct snd_emu10k1_fx8010_control_gpr *gctl,
+ struct snd_emu10k1_fx8010_control_gpr *dst,
+ struct snd_emu10k1_fx8010_control_gpr *src,
int idx)
{
+ struct snd_emu10k1_fx8010_control_gpr __user *_dst;
struct snd_emu10k1_fx8010_control_old_gpr __user *octl;
+ _dst = (struct snd_emu10k1_fx8010_control_gpr __user *)dst;
if (emu->support_tlv)
- return copy_to_user(&_gctl[idx], gctl, sizeof(*gctl));
+ return copy_to_user(&_dst[idx], src, sizeof(*src));
- octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)_gctl;
- return copy_to_user(&octl[idx], gctl, sizeof(*octl));
+ octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)dst;
+ return copy_to_user(&octl[idx], src, sizeof(*octl));
+}
+
+static int copy_ctl_elem_id(const struct emu10k1_ctl_elem_id *list, int i,
+ struct emu10k1_ctl_elem_id *ret, bool in_kernel)
+{
+ struct emu10k1_ctl_elem_id __user *_id =
+ (struct emu10k1_ctl_elem_id __user *)&list[i];
+
+ if (in_kernel)
+ *ret = list[i];
+ else if (copy_from_user(ret, _id, sizeof(*ret)))
+ return -EFAULT;
+ return 0;
}
static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_code *icode)
+ struct snd_emu10k1_fx8010_code *icode,
+ bool in_kernel)
{
unsigned int i;
- struct snd_ctl_elem_id __user *_id;
- struct snd_ctl_elem_id id;
+ struct emu10k1_ctl_elem_id id;
struct snd_emu10k1_fx8010_control_gpr *gctl;
+ struct snd_ctl_elem_id *gctl_id;
int err;
- for (i = 0, _id = icode->gpr_del_controls;
- i < icode->gpr_del_control_count; i++, _id++) {
- if (copy_from_user(&id, _id, sizeof(id)))
- return -EFAULT;
+ for (i = 0; i < icode->gpr_del_control_count; i++) {
+ err = copy_ctl_elem_id(icode->gpr_del_controls, i, &id,
+ in_kernel);
+ if (err < 0)
+ return err;
if (snd_emu10k1_look_for_ctl(emu, &id) == NULL)
return -ENOENT;
}
@@ -735,28 +781,56 @@ static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
return -ENOMEM;
err = 0;
for (i = 0; i < icode->gpr_add_control_count; i++) {
- if (copy_gctl(emu, gctl, icode->gpr_add_controls, i)) {
+ if (copy_gctl(emu, gctl, icode->gpr_add_controls, i,
+ in_kernel)) {
err = -EFAULT;
goto __error;
}
if (snd_emu10k1_look_for_ctl(emu, &gctl->id))
continue;
- down_read(&emu->card->controls_rwsem);
- if (snd_ctl_find_id(emu->card, &gctl->id) != NULL) {
- up_read(&emu->card->controls_rwsem);
+ gctl_id = (struct snd_ctl_elem_id *)&gctl->id;
+ if (snd_ctl_find_id(emu->card, gctl_id)) {
err = -EEXIST;
goto __error;
}
- up_read(&emu->card->controls_rwsem);
- if (gctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER &&
- gctl->id.iface != SNDRV_CTL_ELEM_IFACE_PCM) {
+ if (gctl_id->iface != SNDRV_CTL_ELEM_IFACE_MIXER &&
+ gctl_id->iface != SNDRV_CTL_ELEM_IFACE_PCM) {
+ err = -EINVAL;
+ goto __error;
+ }
+ switch (gctl->translation) {
+ case EMU10K1_GPR_TRANSLATION_NONE:
+ case EMU10K1_GPR_TRANSLATION_NEGATE:
+ break;
+ case EMU10K1_GPR_TRANSLATION_TABLE100:
+ case EMU10K1_GPR_TRANSLATION_NEG_TABLE100:
+ if (gctl->min != 0 || gctl->max != 100) {
+ err = -EINVAL;
+ goto __error;
+ }
+ break;
+ case EMU10K1_GPR_TRANSLATION_BASS:
+ case EMU10K1_GPR_TRANSLATION_TREBLE:
+ if (gctl->min != 0 || gctl->max != 40) {
+ err = -EINVAL;
+ goto __error;
+ }
+ break;
+ case EMU10K1_GPR_TRANSLATION_ONOFF:
+ if (gctl->min != 0 || gctl->max != 1) {
+ err = -EINVAL;
+ goto __error;
+ }
+ break;
+ default:
err = -EINVAL;
goto __error;
}
}
for (i = 0; i < icode->gpr_list_control_count; i++) {
/* FIXME: we need to check the WRITE access */
- if (copy_gctl(emu, gctl, icode->gpr_list_controls, i)) {
+ if (copy_gctl(emu, gctl, icode->gpr_list_controls, i,
+ in_kernel)) {
err = -EFAULT;
goto __error;
}
@@ -774,15 +848,16 @@ static void snd_emu10k1_ctl_private_free(struct snd_kcontrol *kctl)
kctl->private_value = 0;
list_del(&ctl->list);
kfree(ctl);
- if (kctl->tlv.p)
- kfree(kctl->tlv.p);
+ kfree(kctl->tlv.p);
}
static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_code *icode)
+ struct snd_emu10k1_fx8010_code *icode,
+ bool in_kernel)
{
unsigned int i, j;
struct snd_emu10k1_fx8010_control_gpr *gctl;
+ struct snd_ctl_elem_id *gctl_id;
struct snd_emu10k1_fx8010_ctl *ctl, *nctl;
struct snd_kcontrol_new knew;
struct snd_kcontrol *kctl;
@@ -798,28 +873,30 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
}
for (i = 0; i < icode->gpr_add_control_count; i++) {
- if (copy_gctl(emu, gctl, icode->gpr_add_controls, i)) {
+ if (copy_gctl(emu, gctl, icode->gpr_add_controls, i,
+ in_kernel)) {
err = -EFAULT;
goto __error;
}
- if (gctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER &&
- gctl->id.iface != SNDRV_CTL_ELEM_IFACE_PCM) {
+ gctl_id = (struct snd_ctl_elem_id *)&gctl->id;
+ if (gctl_id->iface != SNDRV_CTL_ELEM_IFACE_MIXER &&
+ gctl_id->iface != SNDRV_CTL_ELEM_IFACE_PCM) {
err = -EINVAL;
goto __error;
}
- if (! gctl->id.name[0]) {
+ if (!*gctl_id->name) {
err = -EINVAL;
goto __error;
}
ctl = snd_emu10k1_look_for_ctl(emu, &gctl->id);
memset(&knew, 0, sizeof(knew));
- knew.iface = gctl->id.iface;
- knew.name = gctl->id.name;
- knew.index = gctl->id.index;
- knew.device = gctl->id.device;
- knew.subdevice = gctl->id.subdevice;
+ knew.iface = gctl_id->iface;
+ knew.name = gctl_id->name;
+ knew.index = gctl_id->index;
+ knew.device = gctl_id->device;
+ knew.subdevice = gctl_id->subdevice;
knew.info = snd_emu10k1_gpr_ctl_info;
- knew.tlv.p = copy_tlv(gctl->tlv);
+ knew.tlv.p = copy_tlv((const unsigned int __user *)gctl->tlv, in_kernel);
if (knew.tlv.p)
knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ;
@@ -845,7 +922,9 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
}
knew.private_value = (unsigned long)ctl;
*ctl = *nctl;
- if ((err = snd_ctl_add(emu->card, kctl = snd_ctl_new1(&knew, emu))) < 0) {
+ kctl = snd_ctl_new1(&knew, emu);
+ err = snd_ctl_add(emu->card, kctl);
+ if (err < 0) {
kfree(ctl);
kfree(knew.tlv.p);
goto __error;
@@ -871,23 +950,23 @@ static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
}
static int snd_emu10k1_del_controls(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_code *icode)
+ struct snd_emu10k1_fx8010_code *icode,
+ bool in_kernel)
{
unsigned int i;
- struct snd_ctl_elem_id id;
- struct snd_ctl_elem_id __user *_id;
+ struct emu10k1_ctl_elem_id id;
struct snd_emu10k1_fx8010_ctl *ctl;
struct snd_card *card = emu->card;
+ int err;
- for (i = 0, _id = icode->gpr_del_controls;
- i < icode->gpr_del_control_count; i++, _id++) {
- if (copy_from_user(&id, _id, sizeof(id)))
- return -EFAULT;
- down_write(&card->controls_rwsem);
+ for (i = 0; i < icode->gpr_del_control_count; i++) {
+ err = copy_ctl_elem_id(icode->gpr_del_controls, i, &id,
+ in_kernel);
+ if (err < 0)
+ return err;
ctl = snd_emu10k1_look_for_ctl(emu, &id);
if (ctl)
snd_ctl_remove(card, ctl->kcontrol);
- up_write(&card->controls_rwsem);
}
return 0;
}
@@ -911,8 +990,8 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu,
i < icode->gpr_list_control_count) {
memset(gctl, 0, sizeof(*gctl));
id = &ctl->kcontrol->id;
- gctl->id.iface = id->iface;
- strlcpy(gctl->id.name, id->name, sizeof(gctl->id.name));
+ gctl->id.iface = (__force int)id->iface;
+ strscpy(gctl->id.name, id->name, sizeof(gctl->id.name));
gctl->id.index = id->index;
gctl->id.device = id->device;
gctl->id.subdevice = id->subdevice;
@@ -939,14 +1018,16 @@ static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu,
}
static int snd_emu10k1_icode_poke(struct snd_emu10k1 *emu,
- struct snd_emu10k1_fx8010_code *icode)
+ struct snd_emu10k1_fx8010_code *icode,
+ bool in_kernel)
{
- int err = 0;
+ int err;
- mutex_lock(&emu->fx8010.lock);
- if ((err = snd_emu10k1_verify_controls(emu, icode)) < 0)
- goto __error;
- strlcpy(emu->fx8010.name, icode->name, sizeof(emu->fx8010.name));
+ guard(mutex)(&emu->fx8010.lock);
+ err = snd_emu10k1_verify_controls(emu, icode, in_kernel);
+ if (err < 0)
+ return err;
+ strscpy(emu->fx8010.name, icode->name, sizeof(emu->fx8010.name));
/* stop FX processor - this may be dangerous, but it's better to miss
some samples than generate wrong ones - [jk] */
if (emu->audigy)
@@ -954,20 +1035,27 @@ static int snd_emu10k1_icode_poke(struct snd_emu10k1 *emu,
else
snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_SINGLE_STEP);
/* ok, do the main job */
- if ((err = snd_emu10k1_del_controls(emu, icode)) < 0 ||
- (err = snd_emu10k1_gpr_poke(emu, icode)) < 0 ||
- (err = snd_emu10k1_tram_poke(emu, icode)) < 0 ||
- (err = snd_emu10k1_code_poke(emu, icode)) < 0 ||
- (err = snd_emu10k1_add_controls(emu, icode)) < 0)
- goto __error;
+ err = snd_emu10k1_del_controls(emu, icode, in_kernel);
+ if (err < 0)
+ return err;
+ err = snd_emu10k1_gpr_poke(emu, icode, in_kernel);
+ if (err < 0)
+ return err;
+ err = snd_emu10k1_tram_poke(emu, icode, in_kernel);
+ if (err < 0)
+ return err;
+ err = snd_emu10k1_code_poke(emu, icode, in_kernel);
+ if (err < 0)
+ return err;
+ err = snd_emu10k1_add_controls(emu, icode, in_kernel);
+ if (err < 0)
+ return err;
/* start FX processor when the DSP code is updated */
if (emu->audigy)
snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg);
else
snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg);
- __error:
- mutex_unlock(&emu->fx8010.lock);
- return err;
+ return 0;
}
static int snd_emu10k1_icode_peek(struct snd_emu10k1 *emu,
@@ -975,8 +1063,8 @@ static int snd_emu10k1_icode_peek(struct snd_emu10k1 *emu,
{
int err;
- mutex_lock(&emu->fx8010.lock);
- strlcpy(icode->name, emu->fx8010.name, sizeof(icode->name));
+ guard(mutex)(&emu->fx8010.lock);
+ strscpy(icode->name, emu->fx8010.name, sizeof(icode->name));
/* ok, do the main job */
err = snd_emu10k1_gpr_peek(emu, icode);
if (err >= 0)
@@ -985,7 +1073,6 @@ static int snd_emu10k1_icode_peek(struct snd_emu10k1 *emu,
err = snd_emu10k1_code_peek(emu, icode);
if (err >= 0)
err = snd_emu10k1_list_controls(emu, icode);
- mutex_unlock(&emu->fx8010.lock);
return err;
}
@@ -993,28 +1080,25 @@ static int snd_emu10k1_ipcm_poke(struct snd_emu10k1 *emu,
struct snd_emu10k1_fx8010_pcm_rec *ipcm)
{
unsigned int i;
- int err = 0;
struct snd_emu10k1_fx8010_pcm *pcm;
if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT)
return -EINVAL;
+ ipcm->substream = array_index_nospec(ipcm->substream,
+ EMU10K1_FX8010_PCM_COUNT);
if (ipcm->channels > 32)
return -EINVAL;
pcm = &emu->fx8010.pcm[ipcm->substream];
- mutex_lock(&emu->fx8010.lock);
- spin_lock_irq(&emu->reg_lock);
- if (pcm->opened) {
- err = -EBUSY;
- goto __error;
- }
+ guard(mutex)(&emu->fx8010.lock);
+ guard(spinlock_irq)(&emu->reg_lock);
+ if (pcm->opened)
+ return -EBUSY;
if (ipcm->channels == 0) { /* remove */
pcm->valid = 0;
} else {
/* FIXME: we need to add universal code to the PCM transfer routine */
- if (ipcm->channels != 2) {
- err = -EINVAL;
- goto __error;
- }
+ if (ipcm->channels != 2)
+ return -EINVAL;
pcm->valid = 1;
pcm->opened = 0;
pcm->channels = ipcm->channels;
@@ -1029,24 +1113,22 @@ static int snd_emu10k1_ipcm_poke(struct snd_emu10k1 *emu,
for (i = 0; i < pcm->channels; i++)
pcm->etram[i] = ipcm->etram[i];
}
- __error:
- spin_unlock_irq(&emu->reg_lock);
- mutex_unlock(&emu->fx8010.lock);
- return err;
+ return 0;
}
static int snd_emu10k1_ipcm_peek(struct snd_emu10k1 *emu,
struct snd_emu10k1_fx8010_pcm_rec *ipcm)
{
unsigned int i;
- int err = 0;
struct snd_emu10k1_fx8010_pcm *pcm;
if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT)
return -EINVAL;
+ ipcm->substream = array_index_nospec(ipcm->substream,
+ EMU10K1_FX8010_PCM_COUNT);
pcm = &emu->fx8010.pcm[ipcm->substream];
- mutex_lock(&emu->fx8010.lock);
- spin_lock_irq(&emu->reg_lock);
+ guard(mutex)(&emu->fx8010.lock);
+ guard(spinlock_irq)(&emu->reg_lock);
ipcm->channels = pcm->channels;
ipcm->tram_start = pcm->tram_start;
ipcm->buffer_size = pcm->buffer_size;
@@ -1060,9 +1142,7 @@ static int snd_emu10k1_ipcm_peek(struct snd_emu10k1 *emu,
ipcm->etram[i] = pcm->etram[i];
ipcm->res1 = ipcm->res2 = 0;
ipcm->pad = 0;
- spin_unlock_irq(&emu->reg_lock);
- mutex_unlock(&emu->fx8010.lock);
- return err;
+ return 0;
}
#define SND_EMU10K1_GPR_CONTROLS 44
@@ -1070,55 +1150,63 @@ static int snd_emu10k1_ipcm_peek(struct snd_emu10k1 *emu,
#define SND_EMU10K1_PLAYBACK_CHANNELS 8
#define SND_EMU10K1_CAPTURE_CHANNELS 4
-static void __devinit
-snd_emu10k1_init_mono_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
- const char *name, int gpr, int defval)
+#define HR_VAL(v) ((v) * 0x80000000LL / 100 - 1)
+
+static void
+snd_emu10k1_init_mono_control2(struct snd_emu10k1_fx8010_control_gpr *ctl,
+ const char *name, int gpr, int defval, int defval_hr)
{
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(ctl->id.name, name);
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
+ strscpy(ctl->id.name, name);
ctl->vcount = ctl->count = 1;
- ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
if (high_res_gpr_volume) {
- ctl->min = 0;
+ ctl->min = -1;
ctl->max = 0x7fffffff;
ctl->tlv = snd_emu10k1_db_linear;
- ctl->translation = EMU10K1_GPR_TRANSLATION_NONE;
+ ctl->translation = EMU10K1_GPR_TRANSLATION_NEGATE;
+ defval = defval_hr;
} else {
ctl->min = 0;
ctl->max = 100;
ctl->tlv = snd_emu10k1_db_scale1;
- ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
+ ctl->translation = EMU10K1_GPR_TRANSLATION_NEG_TABLE100;
}
+ ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
}
+#define snd_emu10k1_init_mono_control(ctl, name, gpr, defval) \
+ snd_emu10k1_init_mono_control2(ctl, name, gpr, defval, HR_VAL(defval))
-static void __devinit
-snd_emu10k1_init_stereo_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
- const char *name, int gpr, int defval)
+static void
+snd_emu10k1_init_stereo_control2(struct snd_emu10k1_fx8010_control_gpr *ctl,
+ const char *name, int gpr, int defval, int defval_hr)
{
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(ctl->id.name, name);
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
+ strscpy(ctl->id.name, name);
ctl->vcount = ctl->count = 2;
- ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
- ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
if (high_res_gpr_volume) {
- ctl->min = 0;
+ ctl->min = -1;
ctl->max = 0x7fffffff;
ctl->tlv = snd_emu10k1_db_linear;
- ctl->translation = EMU10K1_GPR_TRANSLATION_NONE;
+ ctl->translation = EMU10K1_GPR_TRANSLATION_NEGATE;
+ defval = defval_hr;
} else {
ctl->min = 0;
ctl->max = 100;
ctl->tlv = snd_emu10k1_db_scale1;
- ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
+ ctl->translation = EMU10K1_GPR_TRANSLATION_NEG_TABLE100;
}
+ ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
+ ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
}
+#define snd_emu10k1_init_stereo_control(ctl, name, gpr, defval) \
+ snd_emu10k1_init_stereo_control2(ctl, name, gpr, defval, HR_VAL(defval))
-static void __devinit
+static void
snd_emu10k1_init_mono_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
const char *name, int gpr, int defval)
{
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(ctl->id.name, name);
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
+ strscpy(ctl->id.name, name);
ctl->vcount = ctl->count = 1;
ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
ctl->min = 0;
@@ -1126,12 +1214,12 @@ snd_emu10k1_init_mono_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF;
}
-static void __devinit
+static void
snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
const char *name, int gpr, int defval)
{
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(ctl->id.name, name);
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
+ strscpy(ctl->id.name, name);
ctl->vcount = ctl->count = 2;
ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
@@ -1141,111 +1229,121 @@ snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl
}
/*
- * Used for emu1010 - conversion from 32-bit capture inputs from HANA
- * to 2 x 16-bit registers in audigy - their values are read via DMA.
+ * Used for emu1010 - conversion from 32-bit capture inputs from the FPGA
+ * to 2 x 16-bit registers in Audigy - their values are read via DMA.
* Conversion is performed by Audigy DSP instructions of FX8010.
*/
-static int snd_emu10k1_audigy_dsp_convert_32_to_2x16(
+static void snd_emu10k1_audigy_dsp_convert_32_to_2x16(
struct snd_emu10k1_fx8010_code *icode,
u32 *ptr, int tmp, int bit_shifter16,
int reg_in, int reg_out)
{
- A_OP(icode, ptr, iACC3, A_GPR(tmp + 1), reg_in, A_C_00000000, A_C_00000000);
- A_OP(icode, ptr, iANDXOR, A_GPR(tmp), A_GPR(tmp + 1), A_GPR(bit_shifter16 - 1), A_C_00000000);
- A_OP(icode, ptr, iTSTNEG, A_GPR(tmp + 2), A_GPR(tmp), A_C_80000000, A_GPR(bit_shifter16 - 2));
- A_OP(icode, ptr, iANDXOR, A_GPR(tmp + 2), A_GPR(tmp + 2), A_C_80000000, A_C_00000000);
- A_OP(icode, ptr, iANDXOR, A_GPR(tmp), A_GPR(tmp), A_GPR(bit_shifter16 - 3), A_C_00000000);
- A_OP(icode, ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A_GPR(tmp), A_C_00010000);
- A_OP(icode, ptr, iANDXOR, reg_out, A_GPR(tmp), A_C_ffffffff, A_GPR(tmp + 2));
- A_OP(icode, ptr, iACC3, reg_out + 1, A_GPR(tmp + 1), A_C_00000000, A_C_00000000);
- return 1;
+ // This leaves the low word in place, which is fine,
+ // as the low bits are completely ignored subsequently.
+ // reg_out[1] = reg_in
+ A_OP(icode, ptr, iACC3, reg_out + 1, reg_in, A_C_00000000, A_C_00000000);
+ // It is fine to read reg_in multiple times.
+ // tmp = reg_in << 15
+ A_OP(icode, ptr, iMACINT1, A_GPR(tmp), A_C_00000000, reg_in, A_GPR(bit_shifter16));
+ // Left-shift once more. This is a separate step, as the
+ // signed multiplication would clobber the MSB.
+ // reg_out[0] = tmp + ((tmp << 31) >> 31)
+ A_OP(icode, ptr, iMAC3, reg_out, A_GPR(tmp), A_GPR(tmp), A_C_80000000);
}
+#define ENUM_GPR(name, size) name, name ## _dummy = name + (size) - 1
+
/*
* initial DSP configuration for Audigy
*/
-static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
+static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
{
- int err, i, z, gpr, nctl;
- int bit_shifter16;
- const int playback = 10;
- const int capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2); /* we reserve 10 voices */
- const int stereo_mix = capture + 2;
- const int tmp = 0x88;
- u32 ptr;
+ int err, z, nctl;
+ enum {
+ ENUM_GPR(playback, SND_EMU10K1_PLAYBACK_CHANNELS),
+ ENUM_GPR(stereo_mix, 2),
+ ENUM_GPR(capture, 2),
+ ENUM_GPR(bit_shifter16, 1),
+ // The fixed allocation of these breaks the pattern, but why not.
+ // Splitting these into left/right is questionable, as it will break
+ // down for center/lfe. But it works for stereo/quadro, so whatever.
+ ENUM_GPR(bass_gpr, 2 * 5), // two sides, five coefficients
+ ENUM_GPR(treble_gpr, 2 * 5),
+ ENUM_GPR(bass_tmp, SND_EMU10K1_PLAYBACK_CHANNELS * 4), // four delay stages
+ ENUM_GPR(treble_tmp, SND_EMU10K1_PLAYBACK_CHANNELS * 4),
+ ENUM_GPR(tmp, 3),
+ num_static_gprs
+ };
+ int gpr = num_static_gprs;
+ u32 ptr, ptr_skip;
struct snd_emu10k1_fx8010_code *icode = NULL;
struct snd_emu10k1_fx8010_control_gpr *controls = NULL, *ctl;
u32 *gpr_map;
- mm_segment_t seg;
-
- if ((icode = kzalloc(sizeof(*icode), GFP_KERNEL)) == NULL ||
- (icode->gpr_map = (u_int32_t __user *)
- kcalloc(512 + 256 + 256 + 2 * 1024, sizeof(u_int32_t),
- GFP_KERNEL)) == NULL ||
- (controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
- sizeof(*controls), GFP_KERNEL)) == NULL) {
- err = -ENOMEM;
- goto __err;
- }
- gpr_map = (u32 __force *)icode->gpr_map;
+
+ err = -ENOMEM;
+ icode = kzalloc(sizeof(*icode), GFP_KERNEL);
+ if (!icode)
+ return err;
+
+ icode->gpr_map = kcalloc(512 + 256 + 256 + 2 * 1024,
+ sizeof(u_int32_t), GFP_KERNEL);
+ if (!icode->gpr_map)
+ goto __err_gpr;
+ controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
+ sizeof(*controls), GFP_KERNEL);
+ if (!controls)
+ goto __err_ctrls;
+
+ gpr_map = icode->gpr_map;
icode->tram_data_map = icode->gpr_map + 512;
icode->tram_addr_map = icode->tram_data_map + 256;
icode->code = icode->tram_addr_map + 256;
/* clear free GPRs */
- for (i = 0; i < 512; i++)
- set_bit(i, icode->gpr_valid);
+ memset(icode->gpr_valid, 0xff, 512 / 8);
/* clear TRAM data & address lines */
- for (i = 0; i < 256; i++)
- set_bit(i, icode->tram_valid);
+ memset(icode->tram_valid, 0xff, 256 / 8);
- strcpy(icode->name, "Audigy DSP code for ALSA");
+ strscpy(icode->name, "Audigy DSP code for ALSA");
ptr = 0;
nctl = 0;
- gpr = stereo_mix + 10;
- gpr_map[gpr++] = 0x00007fff;
- gpr_map[gpr++] = 0x00008000;
- gpr_map[gpr++] = 0x0000ffff;
- bit_shifter16 = gpr;
-
- /* stop FX processor */
- snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP);
+ gpr_map[bit_shifter16] = 0x00008000;
#if 1
/* PCM front Playback Volume (independent from stereo mix)
- * playback = 0 + ( gpr * FXBUS_PCM_LEFT_FRONT >> 31)
- * where gpr contains attenuation from corresponding mixer control
+ * playback = -gpr * FXBUS_PCM_LEFT_FRONT >> 31
+ * where gpr contains negated attenuation from corresponding mixer control
* (snd_emu10k1_init_stereo_control)
*/
- A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT));
snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Front Playback Volume", gpr, 100);
gpr += 2;
/* PCM Surround Playback (independent from stereo mix) */
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_REAR));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_REAR));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR));
snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Surround Playback Volume", gpr, 100);
gpr += 2;
/* PCM Side Playback (independent from stereo mix) */
if (emu->card_capabilities->spk71) {
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_SIDE));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_SIDE));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+6), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_SIDE));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+7), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_SIDE));
snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Side Playback Volume", gpr, 100);
gpr += 2;
}
/* PCM Center Playback (independent from stereo mix) */
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_CENTER));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+4), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_CENTER));
snd_emu10k1_init_mono_control(&controls[nctl++], "PCM Center Playback Volume", gpr, 100);
gpr++;
/* PCM LFE Playback (independent from stereo mix) */
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LFE));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+5), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LFE));
snd_emu10k1_init_mono_control(&controls[nctl++], "PCM LFE Playback Volume", gpr, 100);
gpr++;
@@ -1253,159 +1351,174 @@ static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
* Stereo Mix
*/
/* Wave (PCM) Playback Volume (will be renamed later) */
- A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
- A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(stereo_mix), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(stereo_mix+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Playback Volume", gpr, 100);
gpr += 2;
/* Synth Playback */
- A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix+0), A_GPR(stereo_mix+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
- A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix+1), A_GPR(stereo_mix+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(stereo_mix+0), A_GPR(stereo_mix+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(stereo_mix+1), A_GPR(stereo_mix+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
snd_emu10k1_init_stereo_control(&controls[nctl++], "Synth Playback Volume", gpr, 100);
gpr += 2;
/* Wave (PCM) Capture */
- A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
- A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(capture+0), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(capture+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Capture Volume", gpr, 0);
gpr += 2;
/* Synth Capture */
- A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
- A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
+ A_OP(icode, &ptr, iMAC1, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
snd_emu10k1_init_stereo_control(&controls[nctl++], "Synth Capture Volume", gpr, 0);
gpr += 2;
-
+
+ // We need to double the volume, as we configure the voices for half volume,
+ // which is necessary for bit-identical reproduction.
+ { static_assert(stereo_mix == playback + SND_EMU10K1_PLAYBACK_CHANNELS); }
+ for (z = 0; z < SND_EMU10K1_PLAYBACK_CHANNELS + 2; z++)
+ A_OP(icode, &ptr, iACC3, A_GPR(playback + z), A_GPR(playback + z), A_GPR(playback + z), A_C_00000000);
+
/*
* inputs
*/
#define A_ADD_VOLUME_IN(var,vol,input) \
-A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
+ A_OP(icode, &ptr, iMAC1, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
- /* emu1212 DSP 0 and DSP 1 Capture */
if (emu->card_capabilities->emu_model) {
+ /* EMU1010 DSP 0 and DSP 1 Capture */
+ // The 24 MSB hold the actual value. We implicitly discard the 16 LSB.
if (emu->card_capabilities->ca0108_chip) {
- /* Note:JCD:No longer bit shift lower 16bits to upper 16bits of 32bit value. */
- A_OP(icode, &ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A3_EMU32IN(0x0), A_C_00000001);
- A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_GPR(tmp));
- A_OP(icode, &ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A3_EMU32IN(0x1), A_C_00000001);
- A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr), A_GPR(tmp));
+ // For unclear reasons, the EMU32IN cannot be the Y operand!
+ A_OP(icode, &ptr, iMAC1, A_GPR(capture+0), A_GPR(capture+0), A3_EMU32IN(0x0), A_GPR(gpr));
+ // A3_EMU32IN(0) is delayed by one sample, so all other A3_EMU32IN channels
+ // need to be delayed as well; we use an auxiliary register for that.
+ A_OP(icode, &ptr, iMAC1, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+2), A_GPR(gpr+1));
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr+2), A3_EMU32IN(0x1), A_C_00000000, A_C_00000000);
} else {
- A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_P16VIN(0x0));
- A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_P16VIN(0x1));
+ A_OP(icode, &ptr, iMAC1, A_GPR(capture+0), A_GPR(capture+0), A_P16VIN(0x0), A_GPR(gpr));
+ // A_P16VIN(0) is delayed by one sample, so all other A_P16VIN channels
+ // need to be delayed as well; we use an auxiliary register for that.
+ A_OP(icode, &ptr, iMAC1, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+2), A_GPR(gpr+1));
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr+2), A_P16VIN(0x1), A_C_00000000, A_C_00000000);
}
snd_emu10k1_init_stereo_control(&controls[nctl++], "EMU Capture Volume", gpr, 0);
- gpr += 2;
- }
- /* AC'97 Playback Volume - used only for mic (renamed later) */
- A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AC97_L);
- A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AC97_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++], "AMic Playback Volume", gpr, 0);
- gpr += 2;
- /* AC'97 Capture Volume - used only for mic */
- A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AC97_L);
- A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AC97_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++], "Mic Capture Volume", gpr, 0);
- gpr += 2;
+ gpr_map[gpr + 2] = 0x00000000;
+ gpr += 3;
+ } else {
+ if (emu->card_capabilities->ac97_chip) {
+ /* AC'97 Playback Volume - used only for mic (renamed later) */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AC97_L);
+ A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AC97_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "AMic Playback Volume", gpr, 0);
+ gpr += 2;
+ /* AC'97 Capture Volume - used only for mic */
+ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AC97_L);
+ A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AC97_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "Mic Capture Volume", gpr, 0);
+ gpr += 2;
+
+ /* mic capture buffer */
+ A_OP(icode, &ptr, iINTERP, A_EXTOUT(A_EXTOUT_MIC_CAP), A_EXTIN(A_EXTIN_AC97_L), A_C_40000000, A_EXTIN(A_EXTIN_AC97_R));
+ }
- /* mic capture buffer */
- A_OP(icode, &ptr, iINTERP, A_EXTOUT(A_EXTOUT_MIC_CAP), A_EXTIN(A_EXTIN_AC97_L), 0xcd, A_EXTIN(A_EXTIN_AC97_R));
+ /* Audigy CD Playback Volume */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_SPDIF_CD_L);
+ A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_SPDIF_CD_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++],
+ emu->card_capabilities->ac97_chip ? "Audigy CD Playback Volume" : "CD Playback Volume",
+ gpr, 0);
+ gpr += 2;
+ /* Audigy CD Capture Volume */
+ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_SPDIF_CD_L);
+ A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_SPDIF_CD_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++],
+ emu->card_capabilities->ac97_chip ? "Audigy CD Capture Volume" : "CD Capture Volume",
+ gpr, 0);
+ gpr += 2;
- /* Audigy CD Playback Volume */
- A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_SPDIF_CD_L);
- A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_SPDIF_CD_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++],
- emu->card_capabilities->ac97_chip ? "Audigy CD Playback Volume" : "CD Playback Volume",
- gpr, 0);
- gpr += 2;
- /* Audigy CD Capture Volume */
- A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_SPDIF_CD_L);
- A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_SPDIF_CD_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++],
- emu->card_capabilities->ac97_chip ? "Audigy CD Capture Volume" : "CD Capture Volume",
- gpr, 0);
- gpr += 2;
+ /* Optical SPDIF Playback Volume */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_OPT_SPDIF_L);
+ A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_OPT_SPDIF_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], SNDRV_CTL_NAME_IEC958("Optical ",PLAYBACK,VOLUME), gpr, 0);
+ gpr += 2;
+ /* Optical SPDIF Capture Volume */
+ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_OPT_SPDIF_L);
+ A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_OPT_SPDIF_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], SNDRV_CTL_NAME_IEC958("Optical ",CAPTURE,VOLUME), gpr, 0);
+ gpr += 2;
- /* Optical SPDIF Playback Volume */
- A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_OPT_SPDIF_L);
- A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_OPT_SPDIF_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++], SNDRV_CTL_NAME_IEC958("Optical ",PLAYBACK,VOLUME), gpr, 0);
- gpr += 2;
- /* Optical SPDIF Capture Volume */
- A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_OPT_SPDIF_L);
- A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_OPT_SPDIF_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++], SNDRV_CTL_NAME_IEC958("Optical ",CAPTURE,VOLUME), gpr, 0);
- gpr += 2;
+ /* Line2 Playback Volume */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_LINE2_L);
+ A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_LINE2_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++],
+ emu->card_capabilities->ac97_chip ? "Line2 Playback Volume" : "Line Playback Volume",
+ gpr, 0);
+ gpr += 2;
+ /* Line2 Capture Volume */
+ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_LINE2_L);
+ A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_LINE2_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++],
+ emu->card_capabilities->ac97_chip ? "Line2 Capture Volume" : "Line Capture Volume",
+ gpr, 0);
+ gpr += 2;
- /* Line2 Playback Volume */
- A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_LINE2_L);
- A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_LINE2_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++],
- emu->card_capabilities->ac97_chip ? "Line2 Playback Volume" : "Line Playback Volume",
- gpr, 0);
- gpr += 2;
- /* Line2 Capture Volume */
- A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_LINE2_L);
- A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_LINE2_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++],
- emu->card_capabilities->ac97_chip ? "Line2 Capture Volume" : "Line Capture Volume",
- gpr, 0);
- gpr += 2;
-
- /* Philips ADC Playback Volume */
- A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_ADC_L);
- A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_ADC_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++], "Analog Mix Playback Volume", gpr, 0);
- gpr += 2;
- /* Philips ADC Capture Volume */
- A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_ADC_L);
- A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_ADC_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++], "Analog Mix Capture Volume", gpr, 0);
- gpr += 2;
+ /* Philips ADC Playback Volume */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_ADC_L);
+ A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_ADC_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "Analog Mix Playback Volume", gpr, 0);
+ gpr += 2;
+ /* Philips ADC Capture Volume */
+ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_ADC_L);
+ A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_ADC_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "Analog Mix Capture Volume", gpr, 0);
+ gpr += 2;
- /* Aux2 Playback Volume */
- A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AUX2_L);
- A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AUX2_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++],
- emu->card_capabilities->ac97_chip ? "Aux2 Playback Volume" : "Aux Playback Volume",
- gpr, 0);
- gpr += 2;
- /* Aux2 Capture Volume */
- A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AUX2_L);
- A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AUX2_R);
- snd_emu10k1_init_stereo_control(&controls[nctl++],
- emu->card_capabilities->ac97_chip ? "Aux2 Capture Volume" : "Aux Capture Volume",
- gpr, 0);
- gpr += 2;
+ /* Aux2 Playback Volume */
+ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AUX2_L);
+ A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AUX2_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++],
+ emu->card_capabilities->ac97_chip ? "Aux2 Playback Volume" : "Aux Playback Volume",
+ gpr, 0);
+ gpr += 2;
+ /* Aux2 Capture Volume */
+ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AUX2_L);
+ A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AUX2_R);
+ snd_emu10k1_init_stereo_control(&controls[nctl++],
+ emu->card_capabilities->ac97_chip ? "Aux2 Capture Volume" : "Aux Capture Volume",
+ gpr, 0);
+ gpr += 2;
+ }
/* Stereo Mix Front Playback Volume */
- A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_GPR(playback), A_GPR(gpr), A_GPR(stereo_mix));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_GPR(playback+1), A_GPR(gpr+1), A_GPR(stereo_mix+1));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback), A_GPR(playback), A_GPR(gpr), A_GPR(stereo_mix));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+1), A_GPR(playback+1), A_GPR(gpr+1), A_GPR(stereo_mix+1));
snd_emu10k1_init_stereo_control(&controls[nctl++], "Front Playback Volume", gpr, 100);
gpr += 2;
/* Stereo Mix Surround Playback */
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_GPR(playback+2), A_GPR(gpr), A_GPR(stereo_mix));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_GPR(playback+3), A_GPR(gpr+1), A_GPR(stereo_mix+1));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+2), A_GPR(playback+2), A_GPR(gpr), A_GPR(stereo_mix));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+3), A_GPR(playback+3), A_GPR(gpr+1), A_GPR(stereo_mix+1));
snd_emu10k1_init_stereo_control(&controls[nctl++], "Surround Playback Volume", gpr, 0);
gpr += 2;
/* Stereo Mix Center Playback */
/* Center = sub = Left/2 + Right/2 */
- A_OP(icode, &ptr, iINTERP, A_GPR(tmp), A_GPR(stereo_mix), 0xcd, A_GPR(stereo_mix+1));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_GPR(playback+4), A_GPR(gpr), A_GPR(tmp));
+ A_OP(icode, &ptr, iINTERP, A_GPR(tmp), A_GPR(stereo_mix), A_C_40000000, A_GPR(stereo_mix+1));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+4), A_GPR(playback+4), A_GPR(gpr), A_GPR(tmp));
snd_emu10k1_init_mono_control(&controls[nctl++], "Center Playback Volume", gpr, 0);
gpr++;
/* Stereo Mix LFE Playback */
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_GPR(playback+5), A_GPR(gpr), A_GPR(tmp));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+5), A_GPR(playback+5), A_GPR(gpr), A_GPR(tmp));
snd_emu10k1_init_mono_control(&controls[nctl++], "LFE Playback Volume", gpr, 0);
gpr++;
if (emu->card_capabilities->spk71) {
/* Stereo Mix Side Playback */
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_GPR(playback+6), A_GPR(gpr), A_GPR(stereo_mix));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_GPR(playback+7), A_GPR(gpr+1), A_GPR(stereo_mix+1));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+6), A_GPR(playback+6), A_GPR(gpr), A_GPR(stereo_mix));
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+7), A_GPR(playback+7), A_GPR(gpr+1), A_GPR(stereo_mix+1));
snd_emu10k1_init_stereo_control(&controls[nctl++], "Side Playback Volume", gpr, 0);
gpr += 2;
}
@@ -1430,21 +1543,9 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
/*
* Process tone control
*/
- A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), A_GPR(playback + 0), A_C_00000000, A_C_00000000); /* left */
- A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), A_GPR(playback + 1), A_C_00000000, A_C_00000000); /* right */
- A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2), A_GPR(playback + 2), A_C_00000000, A_C_00000000); /* rear left */
- A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 3), A_GPR(playback + 3), A_C_00000000, A_C_00000000); /* rear right */
- A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), A_GPR(playback + 4), A_C_00000000, A_C_00000000); /* center */
- A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), A_GPR(playback + 5), A_C_00000000, A_C_00000000); /* LFE */
- if (emu->card_capabilities->spk71) {
- A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 6), A_GPR(playback + 6), A_C_00000000, A_C_00000000); /* side left */
- A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 7), A_GPR(playback + 7), A_C_00000000, A_C_00000000); /* side right */
- }
-
-
ctl = &controls[nctl + 0];
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(ctl->id.name, "Tone Control - Bass");
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
+ strscpy(ctl->id.name, "Tone Control - Bass");
ctl->vcount = 2;
ctl->count = 10;
ctl->min = 0;
@@ -1452,44 +1553,47 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
ctl->value[0] = ctl->value[1] = 20;
ctl->translation = EMU10K1_GPR_TRANSLATION_BASS;
ctl = &controls[nctl + 1];
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(ctl->id.name, "Tone Control - Treble");
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
+ strscpy(ctl->id.name, "Tone Control - Treble");
ctl->vcount = 2;
ctl->count = 10;
ctl->min = 0;
ctl->max = 40;
ctl->value[0] = ctl->value[1] = 20;
ctl->translation = EMU10K1_GPR_TRANSLATION_TREBLE;
-
-#define BASS_GPR 0x8c
-#define TREBLE_GPR 0x96
-
for (z = 0; z < 5; z++) {
int j;
for (j = 0; j < 2; j++) {
- controls[nctl + 0].gpr[z * 2 + j] = BASS_GPR + z * 2 + j;
- controls[nctl + 1].gpr[z * 2 + j] = TREBLE_GPR + z * 2 + j;
+ controls[nctl + 0].gpr[z * 2 + j] = bass_gpr + z * 2 + j;
+ controls[nctl + 1].gpr[z * 2 + j] = treble_gpr + z * 2 + j;
}
}
+ nctl += 2;
+
+ A_OP(icode, &ptr, iACC3, A_C_00000000, A_GPR(gpr), A_C_00000000, A_C_00000000);
+ snd_emu10k1_init_mono_onoff_control(controls + nctl++, "Tone Control - Switch", gpr, 0);
+ gpr++;
+ A_OP(icode, &ptr, iSKIP, A_GPR_COND, A_GPR_COND, A_CC_REG_ZERO, A_GPR(gpr));
+ ptr_skip = ptr;
for (z = 0; z < 4; z++) { /* front/rear/center-lfe/side */
int j, k, l, d;
for (j = 0; j < 2; j++) { /* left/right */
- k = 0xb0 + (z * 8) + (j * 4);
- l = 0xe0 + (z * 8) + (j * 4);
- d = playback + SND_EMU10K1_PLAYBACK_CHANNELS + z * 2 + j;
-
- A_OP(icode, &ptr, iMAC0, A_C_00000000, A_C_00000000, A_GPR(d), A_GPR(BASS_GPR + 0 + j));
- A_OP(icode, &ptr, iMACMV, A_GPR(k+1), A_GPR(k), A_GPR(k+1), A_GPR(BASS_GPR + 4 + j));
- A_OP(icode, &ptr, iMACMV, A_GPR(k), A_GPR(d), A_GPR(k), A_GPR(BASS_GPR + 2 + j));
- A_OP(icode, &ptr, iMACMV, A_GPR(k+3), A_GPR(k+2), A_GPR(k+3), A_GPR(BASS_GPR + 8 + j));
- A_OP(icode, &ptr, iMAC0, A_GPR(k+2), A_GPR_ACCU, A_GPR(k+2), A_GPR(BASS_GPR + 6 + j));
+ k = bass_tmp + (z * 8) + (j * 4);
+ l = treble_tmp + (z * 8) + (j * 4);
+ d = playback + z * 2 + j;
+
+ A_OP(icode, &ptr, iMAC0, A_C_00000000, A_C_00000000, A_GPR(d), A_GPR(bass_gpr + 0 + j));
+ A_OP(icode, &ptr, iMACMV, A_GPR(k+1), A_GPR(k), A_GPR(k+1), A_GPR(bass_gpr + 4 + j));
+ A_OP(icode, &ptr, iMACMV, A_GPR(k), A_GPR(d), A_GPR(k), A_GPR(bass_gpr + 2 + j));
+ A_OP(icode, &ptr, iMACMV, A_GPR(k+3), A_GPR(k+2), A_GPR(k+3), A_GPR(bass_gpr + 8 + j));
+ A_OP(icode, &ptr, iMAC0, A_GPR(k+2), A_GPR_ACCU, A_GPR(k+2), A_GPR(bass_gpr + 6 + j));
A_OP(icode, &ptr, iACC3, A_GPR(k+2), A_GPR(k+2), A_GPR(k+2), A_C_00000000);
- A_OP(icode, &ptr, iMAC0, A_C_00000000, A_C_00000000, A_GPR(k+2), A_GPR(TREBLE_GPR + 0 + j));
- A_OP(icode, &ptr, iMACMV, A_GPR(l+1), A_GPR(l), A_GPR(l+1), A_GPR(TREBLE_GPR + 4 + j));
- A_OP(icode, &ptr, iMACMV, A_GPR(l), A_GPR(k+2), A_GPR(l), A_GPR(TREBLE_GPR + 2 + j));
- A_OP(icode, &ptr, iMACMV, A_GPR(l+3), A_GPR(l+2), A_GPR(l+3), A_GPR(TREBLE_GPR + 8 + j));
- A_OP(icode, &ptr, iMAC0, A_GPR(l+2), A_GPR_ACCU, A_GPR(l+2), A_GPR(TREBLE_GPR + 6 + j));
+ A_OP(icode, &ptr, iMAC0, A_C_00000000, A_C_00000000, A_GPR(k+2), A_GPR(treble_gpr + 0 + j));
+ A_OP(icode, &ptr, iMACMV, A_GPR(l+1), A_GPR(l), A_GPR(l+1), A_GPR(treble_gpr + 4 + j));
+ A_OP(icode, &ptr, iMACMV, A_GPR(l), A_GPR(k+2), A_GPR(l), A_GPR(treble_gpr + 2 + j));
+ A_OP(icode, &ptr, iMACMV, A_GPR(l+3), A_GPR(l+2), A_GPR(l+3), A_GPR(treble_gpr + 8 + j));
+ A_OP(icode, &ptr, iMAC0, A_GPR(l+2), A_GPR_ACCU, A_GPR(l+2), A_GPR(treble_gpr + 6 + j));
A_OP(icode, &ptr, iMACINT0, A_GPR(l+2), A_C_00000000, A_GPR(l+2), A_C_00000010);
A_OP(icode, &ptr, iACC3, A_GPR(d), A_GPR(l+2), A_C_00000000, A_C_00000000);
@@ -1498,206 +1602,109 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
break;
}
}
- nctl += 2;
-
-#undef BASS_GPR
-#undef TREBLE_GPR
-
- for (z = 0; z < 8; z++) {
- A_SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, gpr + 0);
- A_SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 0);
- A_SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1);
- A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
- }
- snd_emu10k1_init_stereo_onoff_control(controls + nctl++, "Tone Control - Switch", gpr, 0);
- gpr += 2;
+ gpr_map[gpr++] = ptr - ptr_skip;
/* Master volume (will be renamed later) */
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+0+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+0+SND_EMU10K1_PLAYBACK_CHANNELS));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+1+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+1+SND_EMU10K1_PLAYBACK_CHANNELS));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+2+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+2+SND_EMU10K1_PLAYBACK_CHANNELS));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+3+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+3+SND_EMU10K1_PLAYBACK_CHANNELS));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+4+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+4+SND_EMU10K1_PLAYBACK_CHANNELS));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+5+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+5+SND_EMU10K1_PLAYBACK_CHANNELS));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+6+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+6+SND_EMU10K1_PLAYBACK_CHANNELS));
- A_OP(icode, &ptr, iMAC0, A_GPR(playback+7+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+7+SND_EMU10K1_PLAYBACK_CHANNELS));
+ for (z = 0; z < 8; z++)
+ A_OP(icode, &ptr, iMAC1, A_GPR(playback+z), A_C_00000000, A_GPR(gpr), A_GPR(playback+z));
snd_emu10k1_init_mono_control(&controls[nctl++], "Wave Master Playback Volume", gpr, 0);
- gpr += 2;
-
- /* analog speakers */
- A_PUT_STEREO_OUTPUT(A_EXTOUT_AFRONT_L, A_EXTOUT_AFRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS);
- A_PUT_STEREO_OUTPUT(A_EXTOUT_AREAR_L, A_EXTOUT_AREAR_R, playback+2 + SND_EMU10K1_PLAYBACK_CHANNELS);
- A_PUT_OUTPUT(A_EXTOUT_ACENTER, playback+4 + SND_EMU10K1_PLAYBACK_CHANNELS);
- A_PUT_OUTPUT(A_EXTOUT_ALFE, playback+5 + SND_EMU10K1_PLAYBACK_CHANNELS);
- if (emu->card_capabilities->spk71)
- A_PUT_STEREO_OUTPUT(A_EXTOUT_ASIDE_L, A_EXTOUT_ASIDE_R, playback+6 + SND_EMU10K1_PLAYBACK_CHANNELS);
-
- /* headphone */
- A_PUT_STEREO_OUTPUT(A_EXTOUT_HEADPHONE_L, A_EXTOUT_HEADPHONE_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS);
+ gpr++;
- /* digital outputs */
- /* A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS); */
if (emu->card_capabilities->emu_model) {
/* EMU1010 Outputs from PCM Front, Rear, Center, LFE, Side */
- snd_printk(KERN_INFO "EMU outputs on\n");
+ dev_info(emu->card->dev, "EMU outputs on\n");
for (z = 0; z < 8; z++) {
if (emu->card_capabilities->ca0108_chip) {
- A_OP(icode, &ptr, iACC3, A3_EMU32OUT(z), A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_C_00000000, A_C_00000000);
+ A_OP(icode, &ptr, iACC3, A3_EMU32OUT(z), A_GPR(playback + z), A_C_00000000, A_C_00000000);
} else {
- A_OP(icode, &ptr, iACC3, A_EMU32OUTL(z), A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_C_00000000, A_C_00000000);
+ A_OP(icode, &ptr, iACC3, A_EMU32OUTL(z), A_GPR(playback + z), A_C_00000000, A_C_00000000);
}
}
- }
+ } else {
+ /* analog speakers */
+ A_PUT_STEREO_OUTPUT(A_EXTOUT_AFRONT_L, A_EXTOUT_AFRONT_R, playback);
+ A_PUT_STEREO_OUTPUT(A_EXTOUT_AREAR_L, A_EXTOUT_AREAR_R, playback+2);
+ A_PUT_OUTPUT(A_EXTOUT_ACENTER, playback+4);
+ A_PUT_OUTPUT(A_EXTOUT_ALFE, playback+5);
+ if (emu->card_capabilities->spk71)
+ A_PUT_STEREO_OUTPUT(A_EXTOUT_ASIDE_L, A_EXTOUT_ASIDE_R, playback+6);
- /* IEC958 Optical Raw Playback Switch */
- gpr_map[gpr++] = 0;
- gpr_map[gpr++] = 0x1008;
- gpr_map[gpr++] = 0xffff0000;
- for (z = 0; z < 2; z++) {
- A_OP(icode, &ptr, iMAC0, A_GPR(tmp + 2), A_FXBUS(FXBUS_PT_LEFT + z), A_C_00000000, A_C_00000000);
- A_OP(icode, &ptr, iSKIP, A_GPR_COND, A_GPR_COND, A_GPR(gpr - 2), A_C_00000001);
- A_OP(icode, &ptr, iACC3, A_GPR(tmp + 2), A_C_00000000, A_C_00010000, A_GPR(tmp + 2));
- A_OP(icode, &ptr, iANDXOR, A_GPR(tmp + 2), A_GPR(tmp + 2), A_GPR(gpr - 1), A_C_00000000);
- A_SWITCH(icode, &ptr, tmp + 0, tmp + 2, gpr + z);
- A_SWITCH_NEG(icode, &ptr, tmp + 1, gpr + z);
- A_SWITCH(icode, &ptr, tmp + 1, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, tmp + 1);
- if ((z==1) && (emu->card_capabilities->spdif_bug)) {
- /* Due to a SPDIF output bug on some Audigy cards, this code delays the Right channel by 1 sample */
- snd_printk(KERN_INFO "Installing spdif_bug patch: %s\n", emu->card_capabilities->name);
- A_OP(icode, &ptr, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L + z), A_GPR(gpr - 3), A_C_00000000, A_C_00000000);
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 3), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
- } else {
- A_OP(icode, &ptr, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L + z), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
+ /* headphone */
+ A_PUT_STEREO_OUTPUT(A_EXTOUT_HEADPHONE_L, A_EXTOUT_HEADPHONE_R, playback);
+
+ /* IEC958 Optical Raw Playback Switch */
+ gpr_map[gpr++] = 0;
+ gpr_map[gpr++] = 0x1008;
+ gpr_map[gpr++] = 0xffff0000;
+ for (z = 0; z < 2; z++) {
+ A_OP(icode, &ptr, iMAC0, A_GPR(tmp + 2), A_FXBUS(FXBUS_PT_LEFT + z), A_C_00000000, A_C_00000000);
+ A_OP(icode, &ptr, iSKIP, A_GPR_COND, A_GPR_COND, A_GPR(gpr - 2), A_C_00000001);
+ A_OP(icode, &ptr, iACC3, A_GPR(tmp + 2), A_C_00000000, A_C_00010000, A_GPR(tmp + 2));
+ A_OP(icode, &ptr, iANDXOR, A_GPR(tmp + 2), A_GPR(tmp + 2), A_GPR(gpr - 1), A_C_00000000);
+ A_SWITCH(icode, &ptr, tmp + 0, tmp + 2, gpr + z);
+ A_SWITCH_NEG(icode, &ptr, tmp + 1, gpr + z);
+ A_SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1);
+ if ((z==1) && (emu->card_capabilities->spdif_bug)) {
+ /* Due to a SPDIF output bug on some Audigy cards, this code delays the Right channel by 1 sample */
+ dev_info(emu->card->dev,
+ "Installing spdif_bug patch: %s\n",
+ emu->card_capabilities->name);
+ A_OP(icode, &ptr, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L + z), A_GPR(gpr - 3), A_C_00000000, A_C_00000000);
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 3), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
+ } else {
+ A_OP(icode, &ptr, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L + z), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
+ }
}
+ snd_emu10k1_init_stereo_onoff_control(controls + nctl++, SNDRV_CTL_NAME_IEC958("Optical Raw ",PLAYBACK,SWITCH), gpr, 0);
+ gpr += 2;
+
+ A_PUT_STEREO_OUTPUT(A_EXTOUT_REAR_L, A_EXTOUT_REAR_R, playback+2);
+ A_PUT_OUTPUT(A_EXTOUT_CENTER, playback+4);
+ A_PUT_OUTPUT(A_EXTOUT_LFE, playback+5);
}
- snd_emu10k1_init_stereo_onoff_control(controls + nctl++, SNDRV_CTL_NAME_IEC958("Optical Raw ",PLAYBACK,SWITCH), gpr, 0);
- gpr += 2;
-
- A_PUT_STEREO_OUTPUT(A_EXTOUT_REAR_L, A_EXTOUT_REAR_R, playback+2 + SND_EMU10K1_PLAYBACK_CHANNELS);
- A_PUT_OUTPUT(A_EXTOUT_CENTER, playback+4 + SND_EMU10K1_PLAYBACK_CHANNELS);
- A_PUT_OUTPUT(A_EXTOUT_LFE, playback+5 + SND_EMU10K1_PLAYBACK_CHANNELS);
/* ADC buffer */
#ifdef EMU10K1_CAPTURE_DIGITAL_OUT
- A_PUT_STEREO_OUTPUT(A_EXTOUT_ADC_CAP_L, A_EXTOUT_ADC_CAP_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS);
+ A_PUT_STEREO_OUTPUT(A_EXTOUT_ADC_CAP_L, A_EXTOUT_ADC_CAP_R, playback);
#else
A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_L, capture);
A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_R, capture+1);
#endif
if (emu->card_capabilities->emu_model) {
+ /* Capture 16 channels of S32_LE sound. */
if (emu->card_capabilities->ca0108_chip) {
- snd_printk(KERN_INFO "EMU2 inputs on\n");
- for (z = 0; z < 0x10; z++) {
+ dev_info(emu->card->dev, "EMU2 inputs on\n");
+ /* Note that the Tina[2] DSPs have 16 more EMU32 inputs which we don't use. */
+
+ snd_emu10k1_audigy_dsp_convert_32_to_2x16(
+ icode, &ptr, tmp, bit_shifter16, A3_EMU32IN(0), A_FXBUS2(0));
+ // A3_EMU32IN(0) is delayed by one sample, so all other A3_EMU32IN channels
+ // need to be delayed as well; we use an auxiliary register for that.
+ for (z = 1; z < 0x10; z++) {
snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp,
bit_shifter16,
- A3_EMU32IN(z),
+ A_GPR(gpr),
A_FXBUS2(z*2) );
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr), A3_EMU32IN(z), A_C_00000000, A_C_00000000);
+ gpr_map[gpr++] = 0x00000000;
}
} else {
- snd_printk(KERN_INFO "EMU inputs on\n");
- /* Capture 16 (originally 8) channels of S32_LE sound */
+ dev_info(emu->card->dev, "EMU inputs on\n");
+ /* Note that the Alice2 DSPs have 6 I2S inputs which we don't use. */
/*
- printk(KERN_DEBUG "emufx.c: gpr=0x%x, tmp=0x%x\n",
+ dev_dbg(emu->card->dev, "emufx.c: gpr=0x%x, tmp=0x%x\n",
gpr, tmp);
*/
- /* For the EMU1010: How to get 32bit values from the DSP. High 16bits into L, low 16bits into R. */
- /* A_P16VIN(0) is delayed by one sample,
- * so all other A_P16VIN channels will need to also be delayed
- */
- /* Left ADC in. 1 of 2 */
snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_P16VIN(0x0), A_FXBUS2(0) );
- /* Right ADC in 1 of 2 */
- gpr_map[gpr++] = 0x00000000;
- /* Delaying by one sample: instead of copying the input
- * value A_P16VIN to output A_FXBUS2 as in the first channel,
- * we use an auxiliary register, delaying the value by one
- * sample
- */
- snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(2) );
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x1), A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(4) );
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x2), A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(6) );
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x3), A_C_00000000, A_C_00000000);
- /* For 96kHz mode */
- /* Left ADC in. 2 of 2 */
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0x8) );
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x4), A_C_00000000, A_C_00000000);
- /* Right ADC in 2 of 2 */
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xa) );
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x5), A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xc) );
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x6), A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xe) );
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x7), A_C_00000000, A_C_00000000);
- /* Pavel Hofman - we still have voices, A_FXBUS2s, and
- * A_P16VINs available -
- * let's add 8 more capture channels - total of 16
- */
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
- bit_shifter16,
- A_GPR(gpr - 1),
- A_FXBUS2(0x10));
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x8),
- A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
- bit_shifter16,
- A_GPR(gpr - 1),
- A_FXBUS2(0x12));
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x9),
- A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
- bit_shifter16,
- A_GPR(gpr - 1),
- A_FXBUS2(0x14));
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xa),
- A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
- bit_shifter16,
- A_GPR(gpr - 1),
- A_FXBUS2(0x16));
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xb),
- A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
- bit_shifter16,
- A_GPR(gpr - 1),
- A_FXBUS2(0x18));
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xc),
- A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
- bit_shifter16,
- A_GPR(gpr - 1),
- A_FXBUS2(0x1a));
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xd),
- A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
- bit_shifter16,
- A_GPR(gpr - 1),
- A_FXBUS2(0x1c));
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xe),
- A_C_00000000, A_C_00000000);
- gpr_map[gpr++] = 0x00000000;
- snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
- bit_shifter16,
- A_GPR(gpr - 1),
- A_FXBUS2(0x1e));
- A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xf),
- A_C_00000000, A_C_00000000);
+ /* A_P16VIN(0) is delayed by one sample, so all other A_P16VIN channels
+ * will need to also be delayed; we use an auxiliary register for that. */
+ for (z = 1; z < 0x10; z++) {
+ snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr), A_FXBUS2(z * 2) );
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr), A_P16VIN(z), A_C_00000000, A_C_00000000);
+ gpr_map[gpr++] = 0x00000000;
+ }
}
#if 0
@@ -1721,29 +1728,28 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
* ok, set up done..
*/
- if (gpr > tmp) {
+ if (gpr > 512) {
snd_BUG();
err = -EIO;
goto __err;
}
+
/* clear remaining instruction memory */
while (ptr < 0x400)
A_OP(icode, &ptr, 0x0f, 0xc0, 0xc0, 0xcf, 0xc0);
- seg = snd_enter_user();
icode->gpr_add_control_count = nctl;
- icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls;
+ icode->gpr_add_controls = controls;
emu->support_tlv = 1; /* support TLV */
- err = snd_emu10k1_icode_poke(emu, icode);
+ err = snd_emu10k1_icode_poke(emu, icode, true);
emu->support_tlv = 0; /* clear again */
- snd_leave_user(seg);
- __err:
+__err:
kfree(controls);
- if (icode != NULL) {
- kfree((void __force *)icode->gpr_map);
- kfree(icode);
- }
+__err_ctrls:
+ kfree(icode->gpr_map);
+__err_gpr:
+ kfree(icode);
return err;
}
@@ -1752,30 +1758,14 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
* initial DSP configuration for Emu10k1
*/
-/* when volume = max, then copy only to avoid volume modification */
-/* with iMAC0 (negative values) */
-static void __devinit _volume(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
-{
- OP(icode, ptr, iMAC0, dst, C_00000000, src, vol);
- OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
- OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000001);
- OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000);
-}
-static void __devinit _volume_add(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
+/* Volumes are in the [-2^31, 0] range, zero being mute. */
+static void _volume(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
{
- OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
- OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002);
- OP(icode, ptr, iMACINT0, dst, dst, src, C_00000001);
- OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001);
- OP(icode, ptr, iMAC0, dst, dst, src, vol);
+ OP(icode, ptr, iMAC1, dst, C_00000000, src, vol);
}
-static void __devinit _volume_out(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
+static void _volume_add(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
{
- OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
- OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002);
- OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000);
- OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001);
- OP(icode, ptr, iMAC0, dst, C_00000000, src, vol);
+ OP(icode, ptr, iMAC1, dst, dst, src, vol);
}
#define VOLUME(icode, ptr, dst, src, vol) \
@@ -1787,7 +1777,7 @@ static void __devinit _volume_out(struct snd_emu10k1_fx8010_code *icode, u32 *pt
#define VOLUME_ADDIN(icode, ptr, dst, src, vol) \
_volume_add(icode, ptr, GPR(dst), EXTIN(src), GPR(vol))
#define VOLUME_OUT(icode, ptr, dst, src, vol) \
- _volume_out(icode, ptr, EXTOUT(dst), GPR(src), GPR(vol))
+ _volume(icode, ptr, EXTOUT(dst), GPR(src), GPR(vol))
#define _SWITCH(icode, ptr, dst, src, sw) \
OP((icode), ptr, iMACINT0, dst, C_00000000, src, sw);
#define SWITCH(icode, ptr, dst, src, sw) \
@@ -1800,70 +1790,72 @@ static void __devinit _volume_out(struct snd_emu10k1_fx8010_code *icode, u32 *pt
_SWITCH_NEG(icode, ptr, GPR(dst), GPR(src))
-static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
+static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
{
int err, i, z, gpr, tmp, playback, capture;
- u32 ptr;
+ u32 ptr, ptr_skip;
struct snd_emu10k1_fx8010_code *icode;
struct snd_emu10k1_fx8010_pcm_rec *ipcm = NULL;
struct snd_emu10k1_fx8010_control_gpr *controls = NULL, *ctl;
u32 *gpr_map;
- mm_segment_t seg;
- if ((icode = kzalloc(sizeof(*icode), GFP_KERNEL)) == NULL)
- return -ENOMEM;
- if ((icode->gpr_map = (u_int32_t __user *)
- kcalloc(256 + 160 + 160 + 2 * 512, sizeof(u_int32_t),
- GFP_KERNEL)) == NULL ||
- (controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
- sizeof(struct snd_emu10k1_fx8010_control_gpr),
- GFP_KERNEL)) == NULL ||
- (ipcm = kzalloc(sizeof(*ipcm), GFP_KERNEL)) == NULL) {
- err = -ENOMEM;
- goto __err;
- }
- gpr_map = (u32 __force *)icode->gpr_map;
+ err = -ENOMEM;
+ icode = kzalloc(sizeof(*icode), GFP_KERNEL);
+ if (!icode)
+ return err;
+
+ icode->gpr_map = kcalloc(256 + 160 + 160 + 2 * 512,
+ sizeof(u_int32_t), GFP_KERNEL);
+ if (!icode->gpr_map)
+ goto __err_gpr;
+
+ controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
+ sizeof(struct snd_emu10k1_fx8010_control_gpr),
+ GFP_KERNEL);
+ if (!controls)
+ goto __err_ctrls;
+
+ ipcm = kzalloc(sizeof(*ipcm), GFP_KERNEL);
+ if (!ipcm)
+ goto __err_ipcm;
+
+ gpr_map = icode->gpr_map;
icode->tram_data_map = icode->gpr_map + 256;
icode->tram_addr_map = icode->tram_data_map + 160;
icode->code = icode->tram_addr_map + 160;
/* clear free GPRs */
- for (i = 0; i < 256; i++)
- set_bit(i, icode->gpr_valid);
+ memset(icode->gpr_valid, 0xff, 256 / 8);
/* clear TRAM data & address lines */
- for (i = 0; i < 160; i++)
- set_bit(i, icode->tram_valid);
+ memset(icode->tram_valid, 0xff, 160 / 8);
- strcpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela");
+ strscpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela");
ptr = 0; i = 0;
/* we have 12 inputs */
playback = SND_EMU10K1_INPUTS;
/* we have 6 playback channels and tone control doubles */
- capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2);
+ capture = playback + SND_EMU10K1_PLAYBACK_CHANNELS;
gpr = capture + SND_EMU10K1_CAPTURE_CHANNELS;
tmp = 0x88; /* we need 4 temporary GPR */
/* from 0x8c to 0xff is the area for tone control */
- /* stop FX processor */
- snd_emu10k1_ptr_write(emu, DBG, 0, (emu->fx8010.dbg = 0) | EMU10K1_DBG_SINGLE_STEP);
-
/*
* Process FX Buses
*/
- OP(icode, &ptr, iMACINT0, GPR(0), C_00000000, FXBUS(FXBUS_PCM_LEFT), C_00000004);
- OP(icode, &ptr, iMACINT0, GPR(1), C_00000000, FXBUS(FXBUS_PCM_RIGHT), C_00000004);
- OP(icode, &ptr, iMACINT0, GPR(2), C_00000000, FXBUS(FXBUS_MIDI_LEFT), C_00000004);
- OP(icode, &ptr, iMACINT0, GPR(3), C_00000000, FXBUS(FXBUS_MIDI_RIGHT), C_00000004);
- OP(icode, &ptr, iMACINT0, GPR(4), C_00000000, FXBUS(FXBUS_PCM_LEFT_REAR), C_00000004);
- OP(icode, &ptr, iMACINT0, GPR(5), C_00000000, FXBUS(FXBUS_PCM_RIGHT_REAR), C_00000004);
- OP(icode, &ptr, iMACINT0, GPR(6), C_00000000, FXBUS(FXBUS_PCM_CENTER), C_00000004);
- OP(icode, &ptr, iMACINT0, GPR(7), C_00000000, FXBUS(FXBUS_PCM_LFE), C_00000004);
+ OP(icode, &ptr, iMACINT0, GPR(0), C_00000000, FXBUS(FXBUS_PCM_LEFT), C_00000008);
+ OP(icode, &ptr, iMACINT0, GPR(1), C_00000000, FXBUS(FXBUS_PCM_RIGHT), C_00000008);
+ OP(icode, &ptr, iMACINT0, GPR(2), C_00000000, FXBUS(FXBUS_MIDI_LEFT), C_00000008);
+ OP(icode, &ptr, iMACINT0, GPR(3), C_00000000, FXBUS(FXBUS_MIDI_RIGHT), C_00000008);
+ OP(icode, &ptr, iMACINT0, GPR(4), C_00000000, FXBUS(FXBUS_PCM_LEFT_REAR), C_00000008);
+ OP(icode, &ptr, iMACINT0, GPR(5), C_00000000, FXBUS(FXBUS_PCM_RIGHT_REAR), C_00000008);
+ OP(icode, &ptr, iMACINT0, GPR(6), C_00000000, FXBUS(FXBUS_PCM_CENTER), C_00000008);
+ OP(icode, &ptr, iMACINT0, GPR(7), C_00000000, FXBUS(FXBUS_PCM_LFE), C_00000008);
OP(icode, &ptr, iMACINT0, GPR(8), C_00000000, C_00000000, C_00000000); /* S/PDIF left */
OP(icode, &ptr, iMACINT0, GPR(9), C_00000000, C_00000000, C_00000000); /* S/PDIF right */
- OP(icode, &ptr, iMACINT0, GPR(10), C_00000000, FXBUS(FXBUS_PCM_LEFT_FRONT), C_00000004);
- OP(icode, &ptr, iMACINT0, GPR(11), C_00000000, FXBUS(FXBUS_PCM_RIGHT_FRONT), C_00000004);
+ OP(icode, &ptr, iMACINT0, GPR(10), C_00000000, FXBUS(FXBUS_PCM_LEFT_FRONT), C_00000008);
+ OP(icode, &ptr, iMACINT0, GPR(11), C_00000000, FXBUS(FXBUS_PCM_RIGHT_FRONT), C_00000008);
/* Raw S/PDIF PCM */
ipcm->substream = 0;
@@ -1957,7 +1949,7 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
/* Wave Center/LFE Playback Volume */
OP(icode, &ptr, iACC3, GPR(tmp + 0), FXBUS(FXBUS_PCM_LEFT), FXBUS(FXBUS_PCM_RIGHT), C_00000000);
- OP(icode, &ptr, iMACINT0, GPR(tmp + 0), C_00000000, GPR(tmp + 0), C_00000002);
+ OP(icode, &ptr, iMACINT0, GPR(tmp + 0), C_00000000, GPR(tmp + 0), C_00000004);
VOLUME(icode, &ptr, playback + 4, tmp + 0, gpr);
snd_emu10k1_init_mono_control(controls + i++, "Wave Center Playback Volume", gpr++, 0);
VOLUME(icode, &ptr, playback + 5, tmp + 0, gpr);
@@ -2148,30 +2140,25 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
/*
* Process tone control
*/
- OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), GPR(playback + 0), C_00000000, C_00000000); /* left */
- OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), GPR(playback + 1), C_00000000, C_00000000); /* right */
- OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2), GPR(playback + 2), C_00000000, C_00000000); /* rear left */
- OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 3), GPR(playback + 3), C_00000000, C_00000000); /* rear right */
- OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), GPR(playback + 4), C_00000000, C_00000000); /* center */
- OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), GPR(playback + 5), C_00000000, C_00000000); /* LFE */
-
ctl = &controls[i + 0];
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(ctl->id.name, "Tone Control - Bass");
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
+ strscpy(ctl->id.name, "Tone Control - Bass");
ctl->vcount = 2;
ctl->count = 10;
ctl->min = 0;
ctl->max = 40;
ctl->value[0] = ctl->value[1] = 20;
+ ctl->tlv = snd_emu10k1_bass_treble_db_scale;
ctl->translation = EMU10K1_GPR_TRANSLATION_BASS;
ctl = &controls[i + 1];
- ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(ctl->id.name, "Tone Control - Treble");
+ ctl->id.iface = (__force int)SNDRV_CTL_ELEM_IFACE_MIXER;
+ strscpy(ctl->id.name, "Tone Control - Treble");
ctl->vcount = 2;
ctl->count = 10;
ctl->min = 0;
ctl->max = 40;
ctl->value[0] = ctl->value[1] = 20;
+ ctl->tlv = snd_emu10k1_bass_treble_db_scale;
ctl->translation = EMU10K1_GPR_TRANSLATION_TREBLE;
#define BASS_GPR 0x8c
@@ -2184,12 +2171,19 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
controls[i + 1].gpr[z * 2 + j] = TREBLE_GPR + z * 2 + j;
}
}
+ i += 2;
+
+ OP(icode, &ptr, iACC3, C_00000000, GPR(gpr), C_00000000, C_00000000);
+ snd_emu10k1_init_mono_onoff_control(controls + i++, "Tone Control - Switch", gpr, 0);
+ gpr++;
+ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_ZERO, GPR(gpr));
+ ptr_skip = ptr;
for (z = 0; z < 3; z++) { /* front/rear/center-lfe */
int j, k, l, d;
for (j = 0; j < 2; j++) { /* left/right */
k = 0xa0 + (z * 8) + (j * 4);
l = 0xd0 + (z * 8) + (j * 4);
- d = playback + SND_EMU10K1_PLAYBACK_CHANNELS + z * 2 + j;
+ d = playback + z * 2 + j;
OP(icode, &ptr, iMAC0, C_00000000, C_00000000, GPR(d), GPR(BASS_GPR + 0 + j));
OP(icode, &ptr, iMACMV, GPR(k+1), GPR(k), GPR(k+1), GPR(BASS_GPR + 4 + j));
@@ -2211,20 +2205,11 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
break;
}
}
- i += 2;
+ gpr_map[gpr++] = ptr - ptr_skip;
#undef BASS_GPR
#undef TREBLE_GPR
- for (z = 0; z < 6; z++) {
- SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, gpr + 0);
- SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 0);
- SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1);
- OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
- }
- snd_emu10k1_init_stereo_onoff_control(controls + i++, "Tone Control - Switch", gpr, 0);
- gpr += 2;
-
/*
* Process outputs
*/
@@ -2232,7 +2217,7 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
/* AC'97 Playback Volume */
for (z = 0; z < 2; z++)
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_L + z), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_L + z), GPR(playback + z), C_00000000, C_00000000);
}
if (emu->fx8010.extout_mask & ((1<<EXTOUT_TOSLINK_L)|(1<<EXTOUT_TOSLINK_R))) {
@@ -2241,7 +2226,7 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
for (z = 0; z < 2; z++) {
SWITCH(icode, &ptr, tmp + 0, 8 + z, gpr + z);
SWITCH_NEG(icode, &ptr, tmp + 1, gpr + z);
- SWITCH(icode, &ptr, tmp + 1, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, tmp + 1);
+ SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1);
OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_TOSLINK_L + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
#ifdef EMU10K1_CAPTURE_DIGITAL_OUT
OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ADC_CAP_L + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
@@ -2256,9 +2241,9 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
/* Headphone Playback Volume */
for (z = 0; z < 2; z++) {
- SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4 + z, gpr + 2 + z);
+ SWITCH(icode, &ptr, tmp + 0, playback + 4 + z, gpr + 2 + z);
SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 2 + z);
- SWITCH(icode, &ptr, tmp + 1, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, tmp + 1);
+ SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1);
OP(icode, &ptr, iACC3, GPR(tmp + 0), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
VOLUME_OUT(icode, &ptr, EXTOUT_HEADPHONE_L + z, tmp + 0, gpr + z);
}
@@ -2275,29 +2260,29 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
if (emu->fx8010.extout_mask & ((1<<EXTOUT_REAR_L)|(1<<EXTOUT_REAR_R)))
for (z = 0; z < 2; z++)
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_REAR_L + z), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2 + z), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_REAR_L + z), GPR(playback + 2 + z), C_00000000, C_00000000);
if (emu->fx8010.extout_mask & ((1<<EXTOUT_AC97_REAR_L)|(1<<EXTOUT_AC97_REAR_R)))
for (z = 0; z < 2; z++)
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_REAR_L + z), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2 + z), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_REAR_L + z), GPR(playback + 2 + z), C_00000000, C_00000000);
if (emu->fx8010.extout_mask & (1<<EXTOUT_AC97_CENTER)) {
#ifndef EMU10K1_CENTER_LFE_FROM_FRONT
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_CENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), C_00000000, C_00000000);
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ACENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_CENTER), GPR(playback + 4), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ACENTER), GPR(playback + 4), C_00000000, C_00000000);
#else
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_CENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), C_00000000, C_00000000);
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ACENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_CENTER), GPR(playback + 0), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ACENTER), GPR(playback + 0), C_00000000, C_00000000);
#endif
}
if (emu->fx8010.extout_mask & (1<<EXTOUT_AC97_LFE)) {
#ifndef EMU10K1_CENTER_LFE_FROM_FRONT
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_LFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), C_00000000, C_00000000);
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ALFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_LFE), GPR(playback + 5), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ALFE), GPR(playback + 5), C_00000000, C_00000000);
#else
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_LFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), C_00000000, C_00000000);
- OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ALFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_LFE), GPR(playback + 1), C_00000000, C_00000000);
+ OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ALFE), GPR(playback + 1), C_00000000, C_00000000);
#endif
}
@@ -2311,21 +2296,11 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
/* EFX capture - capture the 16 EXTINS */
if (emu->card_capabilities->sblive51) {
- /* On the Live! 5.1, FXBUS2(1) and FXBUS(2) are shared with EXTOUT_ACENTER
- * and EXTOUT_ALFE, so we can't connect inputs to them for multitrack recording.
- *
- * Since only 14 of the 16 EXTINs are used, this is not a big problem.
- * We route AC97L and R to FX capture 14 and 15, SPDIF CD in to FX capture
- * 0 and 3, then the rest of the EXTINs to the corresponding FX capture
- * channel. Multitrack recorders will still see the center/lfe output signal
- * on the second and third channels.
- */
- OP(icode, &ptr, iACC3, FXBUS2(14), C_00000000, C_00000000, EXTIN(0));
- OP(icode, &ptr, iACC3, FXBUS2(15), C_00000000, C_00000000, EXTIN(1));
- OP(icode, &ptr, iACC3, FXBUS2(0), C_00000000, C_00000000, EXTIN(2));
- OP(icode, &ptr, iACC3, FXBUS2(3), C_00000000, C_00000000, EXTIN(3));
- for (z = 4; z < 14; z++)
- OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(z));
+ for (z = 0; z < 16; z++) {
+ s8 c = snd_emu10k1_sblive51_fxbus2_map[z];
+ if (c != -1)
+ OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(c));
+ }
} else {
for (z = 0; z < 16; z++)
OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(z));
@@ -2347,28 +2322,28 @@ static int __devinit _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
while (ptr < 0x200)
OP(icode, &ptr, iACC3, C_00000000, C_00000000, C_00000000, C_00000000);
- if ((err = snd_emu10k1_fx8010_tram_setup(emu, ipcm->buffer_size)) < 0)
+ err = snd_emu10k1_fx8010_tram_setup(emu, ipcm->buffer_size);
+ if (err < 0)
goto __err;
- seg = snd_enter_user();
icode->gpr_add_control_count = i;
- icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls;
+ icode->gpr_add_controls = controls;
emu->support_tlv = 1; /* support TLV */
- err = snd_emu10k1_icode_poke(emu, icode);
+ err = snd_emu10k1_icode_poke(emu, icode, true);
emu->support_tlv = 0; /* clear again */
- snd_leave_user(seg);
if (err >= 0)
err = snd_emu10k1_ipcm_poke(emu, ipcm);
- __err:
+__err:
kfree(ipcm);
+__err_ipcm:
kfree(controls);
- if (icode != NULL) {
- kfree((void __force *)icode->gpr_map);
- kfree(icode);
- }
+__err_ctrls:
+ kfree(icode->gpr_map);
+__err_gpr:
+ kfree(icode);
return err;
}
-int __devinit snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
+int snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
{
spin_lock_init(&emu->fx8010.irq_lock);
INIT_LIST_HEAD(&emu->fx8010.gpr_ctl);
@@ -2421,11 +2396,11 @@ int snd_emu10k1_fx8010_tram_setup(struct snd_emu10k1 *emu, u32 size)
}
if ((emu->fx8010.etram_pages.bytes / 2) == size)
return 0;
- spin_lock_irq(&emu->emu_lock);
- outl(HCFG_LOCKTANKCACHE_MASK | inl(emu->port + HCFG), emu->port + HCFG);
- spin_unlock_irq(&emu->emu_lock);
+ scoped_guard(spinlock_irq, &emu->emu_lock) {
+ outl(HCFG_LOCKTANKCACHE_MASK | inl(emu->port + HCFG), emu->port + HCFG);
+ }
snd_emu10k1_ptr_write(emu, TCB, 0, 0);
- snd_emu10k1_ptr_write(emu, TCBS, 0, 0);
+ snd_emu10k1_ptr_write(emu, TCBS, 0, TCBS_BUFFSIZE_16K);
if (emu->fx8010.etram_pages.area != NULL) {
snd_dma_free_pages(&emu->fx8010.etram_pages);
emu->fx8010.etram_pages.area = NULL;
@@ -2433,15 +2408,15 @@ int snd_emu10k1_fx8010_tram_setup(struct snd_emu10k1 *emu, u32 size)
}
if (size > 0) {
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci),
+ if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &emu->pci->dev,
size * 2, &emu->fx8010.etram_pages) < 0)
return -ENOMEM;
memset(emu->fx8010.etram_pages.area, 0, size * 2);
snd_emu10k1_ptr_write(emu, TCB, 0, emu->fx8010.etram_pages.addr);
snd_emu10k1_ptr_write(emu, TCBS, 0, size_reg);
- spin_lock_irq(&emu->emu_lock);
- outl(inl(emu->port + HCFG) & ~HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG);
- spin_unlock_irq(&emu->emu_lock);
+ scoped_guard(spinlock_irq, &emu->emu_lock) {
+ outl(inl(emu->port + HCFG) & ~HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG);
+ }
}
return 0;
@@ -2452,7 +2427,7 @@ static int snd_emu10k1_fx8010_open(struct snd_hwdep * hw, struct file *file)
return 0;
}
-static void copy_string(char *dst, char *src, char *null, int idx)
+static void copy_string(char *dst, const char *src, const char *null, int idx)
{
if (src == NULL)
sprintf(dst, "%s %02X", null, idx);
@@ -2463,20 +2438,19 @@ static void copy_string(char *dst, char *src, char *null, int idx)
static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu,
struct snd_emu10k1_fx8010_info *info)
{
- char **fxbus, **extin, **extout;
- unsigned short fxbus_mask, extin_mask, extout_mask;
+ const char * const *fxbus, * const *extin, * const *extout;
+ unsigned short extin_mask, extout_mask;
int res;
info->internal_tram_size = emu->fx8010.itram_size;
info->external_tram_size = emu->fx8010.etram_pages.bytes / 2;
- fxbus = fxbuses;
- extin = emu->audigy ? audigy_ins : creative_ins;
- extout = emu->audigy ? audigy_outs : creative_outs;
- fxbus_mask = emu->fx8010.fxbus_mask;
- extin_mask = emu->fx8010.extin_mask;
- extout_mask = emu->fx8010.extout_mask;
+ fxbus = snd_emu10k1_fxbus;
+ extin = emu->audigy ? snd_emu10k1_audigy_ins : snd_emu10k1_sblive_ins;
+ extout = emu->audigy ? snd_emu10k1_audigy_outs : snd_emu10k1_sblive_outs;
+ extin_mask = emu->audigy ? ~0 : emu->fx8010.extin_mask;
+ extout_mask = emu->audigy ? ~0 : emu->fx8010.extout_mask;
for (res = 0; res < 16; res++, fxbus++, extin++, extout++) {
- copy_string(info->fxbus_names[res], fxbus_mask & (1 << res) ? *fxbus : NULL, "FXBUS", res);
+ copy_string(info->fxbus_names[res], *fxbus, "FXBUS", res);
copy_string(info->extin_names[res], extin_mask & (1 << res) ? *extin : NULL, "Unused", res);
copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res);
}
@@ -2500,7 +2474,7 @@ static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, un
emu->support_tlv = 1;
return put_user(SNDRV_EMU10K1_VERSION, (int __user *)argp);
case SNDRV_EMU10K1_IOCTL_INFO:
- info = kmalloc(sizeof(*info), GFP_KERNEL);
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
snd_emu10k1_fx8010_info(emu, info);
@@ -2517,7 +2491,7 @@ static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, un
icode = memdup_user(argp, sizeof(*icode));
if (IS_ERR(icode))
return PTR_ERR(icode);
- res = snd_emu10k1_icode_poke(emu, icode);
+ res = snd_emu10k1_icode_poke(emu, icode, false);
kfree(icode);
return res;
case SNDRV_EMU10K1_IOCTL_CODE_PEEK:
@@ -2554,9 +2528,9 @@ static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, un
return -EPERM;
if (get_user(addr, (unsigned int __user *)argp))
return -EFAULT;
- mutex_lock(&emu->fx8010.lock);
- res = snd_emu10k1_fx8010_tram_setup(emu, addr);
- mutex_unlock(&emu->fx8010.lock);
+ scoped_guard(mutex, &emu->fx8010.lock) {
+ res = snd_emu10k1_fx8010_tram_setup(emu, addr);
+ }
return res;
case SNDRV_EMU10K1_IOCTL_STOP:
if (!capable(CAP_SYS_ADMIN))
@@ -2592,17 +2566,19 @@ static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, un
return -EPERM;
if (get_user(addr, (unsigned int __user *)argp))
return -EFAULT;
- if (addr > 0x1ff)
- return -EINVAL;
- if (emu->audigy)
- snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | addr);
- else
- snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | addr);
- udelay(10);
- if (emu->audigy)
- snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | A_DBG_STEP_ADDR | addr);
- else
- snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | EMU10K1_DBG_STEP | addr);
+ if (emu->audigy) {
+ if (addr > A_DBG_STEP_ADDR)
+ return -EINVAL;
+ snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP);
+ udelay(10);
+ snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_STEP | addr);
+ } else {
+ if (addr > EMU10K1_DBG_SINGLE_STEP_ADDR)
+ return -EINVAL;
+ snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP);
+ udelay(10);
+ snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_STEP | addr);
+ }
return 0;
case SNDRV_EMU10K1_IOCTL_DBG_READ:
if (emu->audigy)
@@ -2621,42 +2597,39 @@ static int snd_emu10k1_fx8010_release(struct snd_hwdep * hw, struct file *file)
return 0;
}
-int __devinit snd_emu10k1_fx8010_new(struct snd_emu10k1 *emu, int device, struct snd_hwdep ** rhwdep)
+int snd_emu10k1_fx8010_new(struct snd_emu10k1 *emu, int device)
{
struct snd_hwdep *hw;
int err;
- if (rhwdep)
- *rhwdep = NULL;
- if ((err = snd_hwdep_new(emu->card, "FX8010", device, &hw)) < 0)
+ err = snd_hwdep_new(emu->card, "FX8010", device, &hw);
+ if (err < 0)
return err;
- strcpy(hw->name, "EMU10K1 (FX8010)");
+ strscpy(hw->name, "EMU10K1 (FX8010)");
hw->iface = SNDRV_HWDEP_IFACE_EMU10K1;
hw->ops.open = snd_emu10k1_fx8010_open;
hw->ops.ioctl = snd_emu10k1_fx8010_ioctl;
hw->ops.release = snd_emu10k1_fx8010_release;
hw->private_data = emu;
- if (rhwdep)
- *rhwdep = hw;
return 0;
}
-#ifdef CONFIG_PM
-int __devinit snd_emu10k1_efx_alloc_pm_buffer(struct snd_emu10k1 *emu)
+#ifdef CONFIG_PM_SLEEP
+int snd_emu10k1_efx_alloc_pm_buffer(struct snd_emu10k1 *emu)
{
int len;
len = emu->audigy ? 0x200 : 0x100;
- emu->saved_gpr = kmalloc(len * 4, GFP_KERNEL);
+ emu->saved_gpr = kmalloc_array(len, 4, GFP_KERNEL);
if (! emu->saved_gpr)
return -ENOMEM;
len = emu->audigy ? 0x100 : 0xa0;
- emu->tram_val_saved = kmalloc(len * 4, GFP_KERNEL);
- emu->tram_addr_saved = kmalloc(len * 4, GFP_KERNEL);
+ emu->tram_val_saved = kmalloc_array(len, 4, GFP_KERNEL);
+ emu->tram_addr_saved = kmalloc_array(len, 4, GFP_KERNEL);
if (! emu->tram_val_saved || ! emu->tram_addr_saved)
return -ENOMEM;
len = emu->audigy ? 2 * 1024 : 2 * 512;
- emu->saved_icode = vmalloc(len * 4);
+ emu->saved_icode = vmalloc(array_size(len, 4));
if (! emu->saved_icode)
return -ENOMEM;
return 0;
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index 05afe06e353a..f4906ab30c02 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -1,37 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
* Takashi Iwai <tiwai@suse.de>
+ * Lee Revell <rlrevell@joe-job.com>
+ * James Courtier-Dutton <James@superbug.co.uk>
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
* Creative Labs, Inc.
- * Routines for control of EMU10K1 chips / mixer routines
- * Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com>
- *
- * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
- * Added EMU 1010 support.
- *
- * BUGS:
- * --
- *
- * TODO:
- * --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
+ * Routines for control of EMU10K1 chips / mixer routines
*/
#include <linux/time.h>
#include <linux/init.h>
+#include <linux/string.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
#include <linux/delay.h>
@@ -43,6 +24,24 @@
static const DECLARE_TLV_DB_SCALE(snd_audigy_db_scale2, -10350, 50, 1); /* WM8775 gain scale */
+
+static int add_ctls(struct snd_emu10k1 *emu, const struct snd_kcontrol_new *tpl,
+ const char * const *ctls, unsigned nctls)
+{
+ struct snd_kcontrol_new kctl = *tpl;
+ int err;
+
+ for (unsigned i = 0; i < nctls; i++) {
+ kctl.name = ctls[i];
+ kctl.private_value = i;
+ err = snd_ctl_add(emu->card, snd_ctl_new1(&kctl, emu));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+
static int snd_emu10k1_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
@@ -55,17 +54,14 @@ static int snd_emu10k1_spdif_get(struct snd_kcontrol *kcontrol,
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- unsigned long flags;
/* Limit: emu->spdif_bits */
if (idx >= 3)
return -EINVAL;
- spin_lock_irqsave(&emu->reg_lock, flags);
ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
@@ -79,292 +75,354 @@ static int snd_emu10k1_spdif_get_mask(struct snd_kcontrol *kcontrol,
return 0;
}
+#define PAIR_PS(base, one, two, sfx) base " " one sfx, base " " two sfx
+#define LR_PS(base, sfx) PAIR_PS(base, "Left", "Right", sfx)
+
+#define ADAT_PS(pfx, sfx) \
+ pfx "ADAT 0" sfx, pfx "ADAT 1" sfx, pfx "ADAT 2" sfx, pfx "ADAT 3" sfx, \
+ pfx "ADAT 4" sfx, pfx "ADAT 5" sfx, pfx "ADAT 6" sfx, pfx "ADAT 7" sfx
+
+#define PAIR_REGS(base, one, two) \
+ base ## one ## 1, \
+ base ## two ## 1
+
+#define LR_REGS(base) PAIR_REGS(base, _LEFT, _RIGHT)
+
+#define ADAT_REGS(base) \
+ base+0, base+1, base+2, base+3, base+4, base+5, base+6, base+7
+
/*
- * Items labels in enum mixer controls assigning source data to
- * each destination
+ * List of data sources available for each destination
*/
-static char *emu1010_src_texts[] = {
- "Silence",
- "Dock Mic A",
- "Dock Mic B",
- "Dock ADC1 Left",
- "Dock ADC1 Right",
- "Dock ADC2 Left",
- "Dock ADC2 Right",
- "Dock ADC3 Left",
- "Dock ADC3 Right",
- "0202 ADC Left",
- "0202 ADC Right",
- "0202 SPDIF Left",
- "0202 SPDIF Right",
- "ADAT 0",
- "ADAT 1",
- "ADAT 2",
- "ADAT 3",
- "ADAT 4",
- "ADAT 5",
- "ADAT 6",
- "ADAT 7",
- "DSP 0",
- "DSP 1",
- "DSP 2",
- "DSP 3",
- "DSP 4",
- "DSP 5",
- "DSP 6",
- "DSP 7",
- "DSP 8",
- "DSP 9",
- "DSP 10",
- "DSP 11",
- "DSP 12",
- "DSP 13",
- "DSP 14",
- "DSP 15",
- "DSP 16",
- "DSP 17",
- "DSP 18",
- "DSP 19",
- "DSP 20",
- "DSP 21",
- "DSP 22",
- "DSP 23",
- "DSP 24",
- "DSP 25",
- "DSP 26",
- "DSP 27",
- "DSP 28",
- "DSP 29",
- "DSP 30",
- "DSP 31",
+
+#define DSP_TEXTS \
+ "DSP 0", "DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", \
+ "DSP 8", "DSP 9", "DSP 10", "DSP 11", "DSP 12", "DSP 13", "DSP 14", "DSP 15", \
+ "DSP 16", "DSP 17", "DSP 18", "DSP 19", "DSP 20", "DSP 21", "DSP 22", "DSP 23", \
+ "DSP 24", "DSP 25", "DSP 26", "DSP 27", "DSP 28", "DSP 29", "DSP 30", "DSP 31"
+
+#define PAIR_TEXTS(base, one, two) PAIR_PS(base, one, two, "")
+#define LR_TEXTS(base) LR_PS(base, "")
+#define ADAT_TEXTS(pfx) ADAT_PS(pfx, "")
+
+#define EMU32_SRC_REGS \
+ EMU_SRC_ALICE_EMU32A, \
+ EMU_SRC_ALICE_EMU32A+1, \
+ EMU_SRC_ALICE_EMU32A+2, \
+ EMU_SRC_ALICE_EMU32A+3, \
+ EMU_SRC_ALICE_EMU32A+4, \
+ EMU_SRC_ALICE_EMU32A+5, \
+ EMU_SRC_ALICE_EMU32A+6, \
+ EMU_SRC_ALICE_EMU32A+7, \
+ EMU_SRC_ALICE_EMU32A+8, \
+ EMU_SRC_ALICE_EMU32A+9, \
+ EMU_SRC_ALICE_EMU32A+0xa, \
+ EMU_SRC_ALICE_EMU32A+0xb, \
+ EMU_SRC_ALICE_EMU32A+0xc, \
+ EMU_SRC_ALICE_EMU32A+0xd, \
+ EMU_SRC_ALICE_EMU32A+0xe, \
+ EMU_SRC_ALICE_EMU32A+0xf, \
+ EMU_SRC_ALICE_EMU32B, \
+ EMU_SRC_ALICE_EMU32B+1, \
+ EMU_SRC_ALICE_EMU32B+2, \
+ EMU_SRC_ALICE_EMU32B+3, \
+ EMU_SRC_ALICE_EMU32B+4, \
+ EMU_SRC_ALICE_EMU32B+5, \
+ EMU_SRC_ALICE_EMU32B+6, \
+ EMU_SRC_ALICE_EMU32B+7, \
+ EMU_SRC_ALICE_EMU32B+8, \
+ EMU_SRC_ALICE_EMU32B+9, \
+ EMU_SRC_ALICE_EMU32B+0xa, \
+ EMU_SRC_ALICE_EMU32B+0xb, \
+ EMU_SRC_ALICE_EMU32B+0xc, \
+ EMU_SRC_ALICE_EMU32B+0xd, \
+ EMU_SRC_ALICE_EMU32B+0xe, \
+ EMU_SRC_ALICE_EMU32B+0xf
+
+/* 1010 rev1 */
+
+#define EMU1010_COMMON_TEXTS \
+ "Silence", \
+ PAIR_TEXTS("Dock Mic", "A", "B"), \
+ LR_TEXTS("Dock ADC1"), \
+ LR_TEXTS("Dock ADC2"), \
+ LR_TEXTS("Dock ADC3"), \
+ LR_TEXTS("0202 ADC"), \
+ LR_TEXTS("1010 SPDIF"), \
+ ADAT_TEXTS("1010 ")
+
+static const char * const emu1010_src_texts[] = {
+ EMU1010_COMMON_TEXTS,
+ DSP_TEXTS,
+};
+
+static const unsigned short emu1010_src_regs[] = {
+ EMU_SRC_SILENCE,
+ PAIR_REGS(EMU_SRC_DOCK_MIC, _A, _B),
+ LR_REGS(EMU_SRC_DOCK_ADC1),
+ LR_REGS(EMU_SRC_DOCK_ADC2),
+ LR_REGS(EMU_SRC_DOCK_ADC3),
+ LR_REGS(EMU_SRC_HAMOA_ADC),
+ LR_REGS(EMU_SRC_HANA_SPDIF),
+ ADAT_REGS(EMU_SRC_HANA_ADAT),
+ EMU32_SRC_REGS,
+};
+static_assert(ARRAY_SIZE(emu1010_src_regs) == ARRAY_SIZE(emu1010_src_texts));
+
+/* 1010 rev2 */
+
+#define EMU1010b_COMMON_TEXTS \
+ "Silence", \
+ PAIR_TEXTS("Dock Mic", "A", "B"), \
+ LR_TEXTS("Dock ADC1"), \
+ LR_TEXTS("Dock ADC2"), \
+ LR_TEXTS("0202 ADC"), \
+ LR_TEXTS("Dock SPDIF"), \
+ LR_TEXTS("1010 SPDIF"), \
+ ADAT_TEXTS("Dock "), \
+ ADAT_TEXTS("1010 ")
+
+static const char * const emu1010b_src_texts[] = {
+ EMU1010b_COMMON_TEXTS,
+ DSP_TEXTS,
};
+static const unsigned short emu1010b_src_regs[] = {
+ EMU_SRC_SILENCE,
+ PAIR_REGS(EMU_SRC_DOCK_MIC, _A, _B),
+ LR_REGS(EMU_SRC_DOCK_ADC1),
+ LR_REGS(EMU_SRC_DOCK_ADC2),
+ LR_REGS(EMU_SRC_HAMOA_ADC),
+ LR_REGS(EMU_SRC_MDOCK_SPDIF),
+ LR_REGS(EMU_SRC_HANA_SPDIF),
+ ADAT_REGS(EMU_SRC_MDOCK_ADAT),
+ ADAT_REGS(EMU_SRC_HANA_ADAT),
+ EMU32_SRC_REGS,
+};
+static_assert(ARRAY_SIZE(emu1010b_src_regs) == ARRAY_SIZE(emu1010b_src_texts));
+
/* 1616(m) cardbus */
-static char *emu1616_src_texts[] = {
- "Silence",
- "Dock Mic A",
- "Dock Mic B",
- "Dock ADC1 Left",
- "Dock ADC1 Right",
- "Dock ADC2 Left",
- "Dock ADC2 Right",
- "Dock SPDIF Left",
- "Dock SPDIF Right",
- "ADAT 0",
- "ADAT 1",
- "ADAT 2",
- "ADAT 3",
- "ADAT 4",
- "ADAT 5",
- "ADAT 6",
- "ADAT 7",
- "DSP 0",
- "DSP 1",
- "DSP 2",
- "DSP 3",
- "DSP 4",
- "DSP 5",
- "DSP 6",
- "DSP 7",
- "DSP 8",
- "DSP 9",
- "DSP 10",
- "DSP 11",
- "DSP 12",
- "DSP 13",
- "DSP 14",
- "DSP 15",
- "DSP 16",
- "DSP 17",
- "DSP 18",
- "DSP 19",
- "DSP 20",
- "DSP 21",
- "DSP 22",
- "DSP 23",
- "DSP 24",
- "DSP 25",
- "DSP 26",
- "DSP 27",
- "DSP 28",
- "DSP 29",
- "DSP 30",
- "DSP 31",
+#define EMU1616_COMMON_TEXTS \
+ "Silence", \
+ PAIR_TEXTS("Mic", "A", "B"), \
+ LR_TEXTS("ADC1"), \
+ LR_TEXTS("ADC2"), \
+ LR_TEXTS("SPDIF"), \
+ ADAT_TEXTS("")
+
+static const char * const emu1616_src_texts[] = {
+ EMU1616_COMMON_TEXTS,
+ DSP_TEXTS,
};
+static const unsigned short emu1616_src_regs[] = {
+ EMU_SRC_SILENCE,
+ PAIR_REGS(EMU_SRC_DOCK_MIC, _A, _B),
+ LR_REGS(EMU_SRC_DOCK_ADC1),
+ LR_REGS(EMU_SRC_DOCK_ADC2),
+ LR_REGS(EMU_SRC_MDOCK_SPDIF),
+ ADAT_REGS(EMU_SRC_MDOCK_ADAT),
+ EMU32_SRC_REGS,
+};
+static_assert(ARRAY_SIZE(emu1616_src_regs) == ARRAY_SIZE(emu1616_src_texts));
-/*
- * List of data sources available for each destination
- */
-static unsigned int emu1010_src_regs[] = {
- EMU_SRC_SILENCE,/* 0 */
- EMU_SRC_DOCK_MIC_A1, /* 1 */
- EMU_SRC_DOCK_MIC_B1, /* 2 */
- EMU_SRC_DOCK_ADC1_LEFT1, /* 3 */
- EMU_SRC_DOCK_ADC1_RIGHT1, /* 4 */
- EMU_SRC_DOCK_ADC2_LEFT1, /* 5 */
- EMU_SRC_DOCK_ADC2_RIGHT1, /* 6 */
- EMU_SRC_DOCK_ADC3_LEFT1, /* 7 */
- EMU_SRC_DOCK_ADC3_RIGHT1, /* 8 */
- EMU_SRC_HAMOA_ADC_LEFT1, /* 9 */
- EMU_SRC_HAMOA_ADC_RIGHT1, /* 10 */
- EMU_SRC_HANA_SPDIF_LEFT1, /* 11 */
- EMU_SRC_HANA_SPDIF_RIGHT1, /* 12 */
- EMU_SRC_HANA_ADAT, /* 13 */
- EMU_SRC_HANA_ADAT+1, /* 14 */
- EMU_SRC_HANA_ADAT+2, /* 15 */
- EMU_SRC_HANA_ADAT+3, /* 16 */
- EMU_SRC_HANA_ADAT+4, /* 17 */
- EMU_SRC_HANA_ADAT+5, /* 18 */
- EMU_SRC_HANA_ADAT+6, /* 19 */
- EMU_SRC_HANA_ADAT+7, /* 20 */
- EMU_SRC_ALICE_EMU32A, /* 21 */
- EMU_SRC_ALICE_EMU32A+1, /* 22 */
- EMU_SRC_ALICE_EMU32A+2, /* 23 */
- EMU_SRC_ALICE_EMU32A+3, /* 24 */
- EMU_SRC_ALICE_EMU32A+4, /* 25 */
- EMU_SRC_ALICE_EMU32A+5, /* 26 */
- EMU_SRC_ALICE_EMU32A+6, /* 27 */
- EMU_SRC_ALICE_EMU32A+7, /* 28 */
- EMU_SRC_ALICE_EMU32A+8, /* 29 */
- EMU_SRC_ALICE_EMU32A+9, /* 30 */
- EMU_SRC_ALICE_EMU32A+0xa, /* 31 */
- EMU_SRC_ALICE_EMU32A+0xb, /* 32 */
- EMU_SRC_ALICE_EMU32A+0xc, /* 33 */
- EMU_SRC_ALICE_EMU32A+0xd, /* 34 */
- EMU_SRC_ALICE_EMU32A+0xe, /* 35 */
- EMU_SRC_ALICE_EMU32A+0xf, /* 36 */
- EMU_SRC_ALICE_EMU32B, /* 37 */
- EMU_SRC_ALICE_EMU32B+1, /* 38 */
- EMU_SRC_ALICE_EMU32B+2, /* 39 */
- EMU_SRC_ALICE_EMU32B+3, /* 40 */
- EMU_SRC_ALICE_EMU32B+4, /* 41 */
- EMU_SRC_ALICE_EMU32B+5, /* 42 */
- EMU_SRC_ALICE_EMU32B+6, /* 43 */
- EMU_SRC_ALICE_EMU32B+7, /* 44 */
- EMU_SRC_ALICE_EMU32B+8, /* 45 */
- EMU_SRC_ALICE_EMU32B+9, /* 46 */
- EMU_SRC_ALICE_EMU32B+0xa, /* 47 */
- EMU_SRC_ALICE_EMU32B+0xb, /* 48 */
- EMU_SRC_ALICE_EMU32B+0xc, /* 49 */
- EMU_SRC_ALICE_EMU32B+0xd, /* 50 */
- EMU_SRC_ALICE_EMU32B+0xe, /* 51 */
- EMU_SRC_ALICE_EMU32B+0xf, /* 52 */
+/* 0404 rev1 & rev2 */
+
+#define EMU0404_COMMON_TEXTS \
+ "Silence", \
+ LR_TEXTS("ADC"), \
+ LR_TEXTS("SPDIF")
+
+static const char * const emu0404_src_texts[] = {
+ EMU0404_COMMON_TEXTS,
+ DSP_TEXTS,
};
-/* 1616(m) cardbus */
-static unsigned int emu1616_src_regs[] = {
+static const unsigned short emu0404_src_regs[] = {
EMU_SRC_SILENCE,
- EMU_SRC_DOCK_MIC_A1,
- EMU_SRC_DOCK_MIC_B1,
- EMU_SRC_DOCK_ADC1_LEFT1,
- EMU_SRC_DOCK_ADC1_RIGHT1,
- EMU_SRC_DOCK_ADC2_LEFT1,
- EMU_SRC_DOCK_ADC2_RIGHT1,
- EMU_SRC_MDOCK_SPDIF_LEFT1,
- EMU_SRC_MDOCK_SPDIF_RIGHT1,
- EMU_SRC_MDOCK_ADAT,
- EMU_SRC_MDOCK_ADAT+1,
- EMU_SRC_MDOCK_ADAT+2,
- EMU_SRC_MDOCK_ADAT+3,
- EMU_SRC_MDOCK_ADAT+4,
- EMU_SRC_MDOCK_ADAT+5,
- EMU_SRC_MDOCK_ADAT+6,
- EMU_SRC_MDOCK_ADAT+7,
- EMU_SRC_ALICE_EMU32A,
- EMU_SRC_ALICE_EMU32A+1,
- EMU_SRC_ALICE_EMU32A+2,
- EMU_SRC_ALICE_EMU32A+3,
- EMU_SRC_ALICE_EMU32A+4,
- EMU_SRC_ALICE_EMU32A+5,
- EMU_SRC_ALICE_EMU32A+6,
- EMU_SRC_ALICE_EMU32A+7,
- EMU_SRC_ALICE_EMU32A+8,
- EMU_SRC_ALICE_EMU32A+9,
- EMU_SRC_ALICE_EMU32A+0xa,
- EMU_SRC_ALICE_EMU32A+0xb,
- EMU_SRC_ALICE_EMU32A+0xc,
- EMU_SRC_ALICE_EMU32A+0xd,
- EMU_SRC_ALICE_EMU32A+0xe,
- EMU_SRC_ALICE_EMU32A+0xf,
- EMU_SRC_ALICE_EMU32B,
- EMU_SRC_ALICE_EMU32B+1,
- EMU_SRC_ALICE_EMU32B+2,
- EMU_SRC_ALICE_EMU32B+3,
- EMU_SRC_ALICE_EMU32B+4,
- EMU_SRC_ALICE_EMU32B+5,
- EMU_SRC_ALICE_EMU32B+6,
- EMU_SRC_ALICE_EMU32B+7,
- EMU_SRC_ALICE_EMU32B+8,
- EMU_SRC_ALICE_EMU32B+9,
- EMU_SRC_ALICE_EMU32B+0xa,
- EMU_SRC_ALICE_EMU32B+0xb,
- EMU_SRC_ALICE_EMU32B+0xc,
- EMU_SRC_ALICE_EMU32B+0xd,
- EMU_SRC_ALICE_EMU32B+0xe,
- EMU_SRC_ALICE_EMU32B+0xf,
+ LR_REGS(EMU_SRC_HAMOA_ADC),
+ LR_REGS(EMU_SRC_HANA_SPDIF),
+ EMU32_SRC_REGS,
};
+static_assert(ARRAY_SIZE(emu0404_src_regs) == ARRAY_SIZE(emu0404_src_texts));
/*
* Data destinations - physical EMU outputs.
* Each destination has an enum mixer control to choose a data source
*/
-static unsigned int emu1010_output_dst[] = {
- EMU_DST_DOCK_DAC1_LEFT1, /* 0 */
- EMU_DST_DOCK_DAC1_RIGHT1, /* 1 */
- EMU_DST_DOCK_DAC2_LEFT1, /* 2 */
- EMU_DST_DOCK_DAC2_RIGHT1, /* 3 */
- EMU_DST_DOCK_DAC3_LEFT1, /* 4 */
- EMU_DST_DOCK_DAC3_RIGHT1, /* 5 */
- EMU_DST_DOCK_DAC4_LEFT1, /* 6 */
- EMU_DST_DOCK_DAC4_RIGHT1, /* 7 */
- EMU_DST_DOCK_PHONES_LEFT1, /* 8 */
- EMU_DST_DOCK_PHONES_RIGHT1, /* 9 */
- EMU_DST_DOCK_SPDIF_LEFT1, /* 10 */
- EMU_DST_DOCK_SPDIF_RIGHT1, /* 11 */
- EMU_DST_HANA_SPDIF_LEFT1, /* 12 */
- EMU_DST_HANA_SPDIF_RIGHT1, /* 13 */
- EMU_DST_HAMOA_DAC_LEFT1, /* 14 */
- EMU_DST_HAMOA_DAC_RIGHT1, /* 15 */
- EMU_DST_HANA_ADAT, /* 16 */
- EMU_DST_HANA_ADAT+1, /* 17 */
- EMU_DST_HANA_ADAT+2, /* 18 */
- EMU_DST_HANA_ADAT+3, /* 19 */
- EMU_DST_HANA_ADAT+4, /* 20 */
- EMU_DST_HANA_ADAT+5, /* 21 */
- EMU_DST_HANA_ADAT+6, /* 22 */
- EMU_DST_HANA_ADAT+7, /* 23 */
+
+#define LR_CTLS(base) LR_PS(base, " Playback Enum")
+#define ADAT_CTLS(pfx) ADAT_PS(pfx, " Playback Enum")
+
+/* 1010 rev1 */
+
+static const char * const emu1010_output_texts[] = {
+ LR_CTLS("Dock DAC1"),
+ LR_CTLS("Dock DAC2"),
+ LR_CTLS("Dock DAC3"),
+ LR_CTLS("Dock DAC4"),
+ LR_CTLS("Dock Phones"),
+ LR_CTLS("Dock SPDIF"),
+ LR_CTLS("0202 DAC"),
+ LR_CTLS("1010 SPDIF"),
+ ADAT_CTLS("1010 "),
+};
+static_assert(ARRAY_SIZE(emu1010_output_texts) <= NUM_OUTPUT_DESTS);
+
+static const unsigned short emu1010_output_dst[] = {
+ LR_REGS(EMU_DST_DOCK_DAC1),
+ LR_REGS(EMU_DST_DOCK_DAC2),
+ LR_REGS(EMU_DST_DOCK_DAC3),
+ LR_REGS(EMU_DST_DOCK_DAC4),
+ LR_REGS(EMU_DST_DOCK_PHONES),
+ LR_REGS(EMU_DST_DOCK_SPDIF),
+ LR_REGS(EMU_DST_HAMOA_DAC),
+ LR_REGS(EMU_DST_HANA_SPDIF),
+ ADAT_REGS(EMU_DST_HANA_ADAT),
+};
+static_assert(ARRAY_SIZE(emu1010_output_dst) == ARRAY_SIZE(emu1010_output_texts));
+
+static const unsigned short emu1010_output_dflt[] = {
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
+ EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5,
+ EMU_SRC_ALICE_EMU32A+6, EMU_SRC_ALICE_EMU32A+7,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1, EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
+ EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5, EMU_SRC_ALICE_EMU32A+6, EMU_SRC_ALICE_EMU32A+7,
+};
+static_assert(ARRAY_SIZE(emu1010_output_dflt) == ARRAY_SIZE(emu1010_output_dst));
+
+/* 1010 rev2 */
+
+static const char * const snd_emu1010b_output_texts[] = {
+ LR_CTLS("Dock DAC1"),
+ LR_CTLS("Dock DAC2"),
+ LR_CTLS("Dock DAC3"),
+ LR_CTLS("Dock SPDIF"),
+ ADAT_CTLS("Dock "),
+ LR_CTLS("0202 DAC"),
+ LR_CTLS("1010 SPDIF"),
+ ADAT_CTLS("1010 "),
+};
+static_assert(ARRAY_SIZE(snd_emu1010b_output_texts) <= NUM_OUTPUT_DESTS);
+
+static const unsigned short emu1010b_output_dst[] = {
+ LR_REGS(EMU_DST_DOCK_DAC1),
+ LR_REGS(EMU_DST_DOCK_DAC2),
+ LR_REGS(EMU_DST_DOCK_DAC3),
+ LR_REGS(EMU_DST_MDOCK_SPDIF),
+ ADAT_REGS(EMU_DST_MDOCK_ADAT),
+ LR_REGS(EMU_DST_HAMOA_DAC),
+ LR_REGS(EMU_DST_HANA_SPDIF),
+ ADAT_REGS(EMU_DST_HANA_ADAT),
+};
+static_assert(ARRAY_SIZE(emu1010b_output_dst) == ARRAY_SIZE(snd_emu1010b_output_texts));
+
+static const unsigned short emu1010b_output_dflt[] = {
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
+ EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1, EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
+ EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5, EMU_SRC_ALICE_EMU32A+6, EMU_SRC_ALICE_EMU32A+7,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1, EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
+ EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5, EMU_SRC_ALICE_EMU32A+6, EMU_SRC_ALICE_EMU32A+7,
};
/* 1616(m) cardbus */
-static unsigned int emu1616_output_dst[] = {
- EMU_DST_DOCK_DAC1_LEFT1,
- EMU_DST_DOCK_DAC1_RIGHT1,
- EMU_DST_DOCK_DAC2_LEFT1,
- EMU_DST_DOCK_DAC2_RIGHT1,
- EMU_DST_DOCK_DAC3_LEFT1,
- EMU_DST_DOCK_DAC3_RIGHT1,
- EMU_DST_MDOCK_SPDIF_LEFT1,
- EMU_DST_MDOCK_SPDIF_RIGHT1,
- EMU_DST_MDOCK_ADAT,
- EMU_DST_MDOCK_ADAT+1,
- EMU_DST_MDOCK_ADAT+2,
- EMU_DST_MDOCK_ADAT+3,
- EMU_DST_MDOCK_ADAT+4,
- EMU_DST_MDOCK_ADAT+5,
- EMU_DST_MDOCK_ADAT+6,
- EMU_DST_MDOCK_ADAT+7,
- EMU_DST_MANA_DAC_LEFT,
- EMU_DST_MANA_DAC_RIGHT,
+
+static const char * const snd_emu1616_output_texts[] = {
+ LR_CTLS("Dock DAC1"),
+ LR_CTLS("Dock DAC2"),
+ LR_CTLS("Dock DAC3"),
+ LR_CTLS("Dock SPDIF"),
+ ADAT_CTLS("Dock "),
+ LR_CTLS("Mana DAC"),
+};
+static_assert(ARRAY_SIZE(snd_emu1616_output_texts) <= NUM_OUTPUT_DESTS);
+
+static const unsigned short emu1616_output_dst[] = {
+ LR_REGS(EMU_DST_DOCK_DAC1),
+ LR_REGS(EMU_DST_DOCK_DAC2),
+ LR_REGS(EMU_DST_DOCK_DAC3),
+ LR_REGS(EMU_DST_MDOCK_SPDIF),
+ ADAT_REGS(EMU_DST_MDOCK_ADAT),
+ EMU_DST_MANA_DAC_LEFT, EMU_DST_MANA_DAC_RIGHT,
+};
+static_assert(ARRAY_SIZE(emu1616_output_dst) == ARRAY_SIZE(snd_emu1616_output_texts));
+
+static const unsigned short emu1616_output_dflt[] = {
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
+ EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1, EMU_SRC_ALICE_EMU32A+2, EMU_SRC_ALICE_EMU32A+3,
+ EMU_SRC_ALICE_EMU32A+4, EMU_SRC_ALICE_EMU32A+5, EMU_SRC_ALICE_EMU32A+6, EMU_SRC_ALICE_EMU32A+7,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+};
+static_assert(ARRAY_SIZE(emu1616_output_dflt) == ARRAY_SIZE(emu1616_output_dst));
+
+/* 0404 rev1 & rev2 */
+
+static const char * const snd_emu0404_output_texts[] = {
+ LR_CTLS("DAC"),
+ LR_CTLS("SPDIF"),
};
+static_assert(ARRAY_SIZE(snd_emu0404_output_texts) <= NUM_OUTPUT_DESTS);
+
+static const unsigned short emu0404_output_dst[] = {
+ LR_REGS(EMU_DST_HAMOA_DAC),
+ LR_REGS(EMU_DST_HANA_SPDIF),
+};
+static_assert(ARRAY_SIZE(emu0404_output_dst) == ARRAY_SIZE(snd_emu0404_output_texts));
+
+static const unsigned short emu0404_output_dflt[] = {
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+ EMU_SRC_ALICE_EMU32A+0, EMU_SRC_ALICE_EMU32A+1,
+};
+static_assert(ARRAY_SIZE(emu0404_output_dflt) == ARRAY_SIZE(emu0404_output_dst));
/*
- * Data destinations - HANA outputs going to Alice2 (audigy) for
+ * Data destinations - FPGA outputs going to Alice2 (Audigy) for
* capture (EMU32 + I2S links)
* Each destination has an enum mixer control to choose a data source
*/
-static unsigned int emu1010_input_dst[] = {
+
+static const char * const emu1010_input_texts[] = {
+ "DSP 0 Capture Enum",
+ "DSP 1 Capture Enum",
+ "DSP 2 Capture Enum",
+ "DSP 3 Capture Enum",
+ "DSP 4 Capture Enum",
+ "DSP 5 Capture Enum",
+ "DSP 6 Capture Enum",
+ "DSP 7 Capture Enum",
+ "DSP 8 Capture Enum",
+ "DSP 9 Capture Enum",
+ "DSP A Capture Enum",
+ "DSP B Capture Enum",
+ "DSP C Capture Enum",
+ "DSP D Capture Enum",
+ "DSP E Capture Enum",
+ "DSP F Capture Enum",
+ /* These exist only on rev1 EMU1010 cards. */
+ "DSP 10 Capture Enum",
+ "DSP 11 Capture Enum",
+ "DSP 12 Capture Enum",
+ "DSP 13 Capture Enum",
+ "DSP 14 Capture Enum",
+ "DSP 15 Capture Enum",
+};
+static_assert(ARRAY_SIZE(emu1010_input_texts) <= NUM_INPUT_DESTS);
+
+static const unsigned short emu1010_input_dst[] = {
EMU_DST_ALICE2_EMU32_0,
EMU_DST_ALICE2_EMU32_1,
EMU_DST_ALICE2_EMU32_2,
@@ -381,6 +439,7 @@ static unsigned int emu1010_input_dst[] = {
EMU_DST_ALICE2_EMU32_D,
EMU_DST_ALICE2_EMU32_E,
EMU_DST_ALICE2_EMU32_F,
+ /* These exist only on rev1 EMU1010 cards. */
EMU_DST_ALICE_I2S0_LEFT,
EMU_DST_ALICE_I2S0_RIGHT,
EMU_DST_ALICE_I2S1_LEFT,
@@ -388,41 +447,199 @@ static unsigned int emu1010_input_dst[] = {
EMU_DST_ALICE_I2S2_LEFT,
EMU_DST_ALICE_I2S2_RIGHT,
};
+static_assert(ARRAY_SIZE(emu1010_input_dst) == ARRAY_SIZE(emu1010_input_texts));
+
+static const unsigned short emu1010_input_dflt[] = {
+ EMU_SRC_DOCK_MIC_A1,
+ EMU_SRC_DOCK_MIC_B1,
+ EMU_SRC_HAMOA_ADC_LEFT1,
+ EMU_SRC_HAMOA_ADC_RIGHT1,
+ EMU_SRC_DOCK_ADC1_LEFT1,
+ EMU_SRC_DOCK_ADC1_RIGHT1,
+ EMU_SRC_DOCK_ADC2_LEFT1,
+ EMU_SRC_DOCK_ADC2_RIGHT1,
+ /* Pavel Hofman - setting defaults for all capture channels.
+ * Defaults only, users will set their own values anyways, let's
+ * just copy/paste. */
+ EMU_SRC_DOCK_MIC_A1,
+ EMU_SRC_DOCK_MIC_B1,
+ EMU_SRC_HAMOA_ADC_LEFT1,
+ EMU_SRC_HAMOA_ADC_RIGHT1,
+ EMU_SRC_DOCK_ADC1_LEFT1,
+ EMU_SRC_DOCK_ADC1_RIGHT1,
+ EMU_SRC_DOCK_ADC2_LEFT1,
+ EMU_SRC_DOCK_ADC2_RIGHT1,
+
+ EMU_SRC_DOCK_ADC1_LEFT1,
+ EMU_SRC_DOCK_ADC1_RIGHT1,
+ EMU_SRC_DOCK_ADC2_LEFT1,
+ EMU_SRC_DOCK_ADC2_RIGHT1,
+ EMU_SRC_DOCK_ADC3_LEFT1,
+ EMU_SRC_DOCK_ADC3_RIGHT1,
+};
+static_assert(ARRAY_SIZE(emu1010_input_dflt) == ARRAY_SIZE(emu1010_input_dst));
+
+static const unsigned short emu0404_input_dflt[] = {
+ EMU_SRC_HAMOA_ADC_LEFT1,
+ EMU_SRC_HAMOA_ADC_RIGHT1,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+ EMU_SRC_HANA_SPDIF_LEFT1,
+ EMU_SRC_HANA_SPDIF_RIGHT1,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+ EMU_SRC_SILENCE,
+};
+
+struct snd_emu1010_routing_info {
+ const char * const *src_texts;
+ const char * const *out_texts;
+ const unsigned short *src_regs;
+ const unsigned short *out_regs;
+ const unsigned short *in_regs;
+ const unsigned short *out_dflts;
+ const unsigned short *in_dflts;
+ unsigned n_srcs;
+ unsigned n_outs;
+ unsigned n_ins;
+};
+
+static const struct snd_emu1010_routing_info emu1010_routing_info[] = {
+ {
+ /* rev1 1010 */
+ .src_regs = emu1010_src_regs,
+ .src_texts = emu1010_src_texts,
+ .n_srcs = ARRAY_SIZE(emu1010_src_texts),
+
+ .out_dflts = emu1010_output_dflt,
+ .out_regs = emu1010_output_dst,
+ .out_texts = emu1010_output_texts,
+ .n_outs = ARRAY_SIZE(emu1010_output_dst),
+
+ .in_dflts = emu1010_input_dflt,
+ .in_regs = emu1010_input_dst,
+ .n_ins = ARRAY_SIZE(emu1010_input_dst),
+ },
+ {
+ /* rev2 1010 */
+ .src_regs = emu1010b_src_regs,
+ .src_texts = emu1010b_src_texts,
+ .n_srcs = ARRAY_SIZE(emu1010b_src_texts),
+
+ .out_dflts = emu1010b_output_dflt,
+ .out_regs = emu1010b_output_dst,
+ .out_texts = snd_emu1010b_output_texts,
+ .n_outs = ARRAY_SIZE(emu1010b_output_dst),
+
+ .in_dflts = emu1010_input_dflt,
+ .in_regs = emu1010_input_dst,
+ .n_ins = ARRAY_SIZE(emu1010_input_dst) - 6,
+ },
+ {
+ /* 1616(m) cardbus */
+ .src_regs = emu1616_src_regs,
+ .src_texts = emu1616_src_texts,
+ .n_srcs = ARRAY_SIZE(emu1616_src_texts),
+
+ .out_dflts = emu1616_output_dflt,
+ .out_regs = emu1616_output_dst,
+ .out_texts = snd_emu1616_output_texts,
+ .n_outs = ARRAY_SIZE(emu1616_output_dst),
+
+ .in_dflts = emu1010_input_dflt,
+ .in_regs = emu1010_input_dst,
+ .n_ins = ARRAY_SIZE(emu1010_input_dst) - 6,
+ },
+ {
+ /* 0404 */
+ .src_regs = emu0404_src_regs,
+ .src_texts = emu0404_src_texts,
+ .n_srcs = ARRAY_SIZE(emu0404_src_texts),
+
+ .out_dflts = emu0404_output_dflt,
+ .out_regs = emu0404_output_dst,
+ .out_texts = snd_emu0404_output_texts,
+ .n_outs = ARRAY_SIZE(emu0404_output_dflt),
+
+ .in_dflts = emu0404_input_dflt,
+ .in_regs = emu1010_input_dst,
+ .n_ins = ARRAY_SIZE(emu1010_input_dst) - 6,
+ },
+};
+
+static unsigned emu1010_idx(struct snd_emu10k1 *emu)
+{
+ return emu->card_capabilities->emu_model - 1;
+}
+
+static void snd_emu1010_output_source_apply(struct snd_emu10k1 *emu,
+ int channel, int src)
+{
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu1010_idx(emu)];
+
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ emu_ri->out_regs[channel], emu_ri->src_regs[src]);
+}
+
+static void snd_emu1010_input_source_apply(struct snd_emu10k1 *emu,
+ int channel, int src)
+{
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu1010_idx(emu)];
+
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ emu_ri->in_regs[channel], emu_ri->src_regs[src]);
+}
+
+static void snd_emu1010_apply_sources(struct snd_emu10k1 *emu)
+{
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu1010_idx(emu)];
+
+ for (unsigned i = 0; i < emu_ri->n_outs; i++)
+ snd_emu1010_output_source_apply(
+ emu, i, emu->emu1010.output_source[i]);
+ for (unsigned i = 0; i < emu_ri->n_ins; i++)
+ snd_emu1010_input_source_apply(
+ emu, i, emu->emu1010.input_source[i]);
+}
+
+static u8 emu1010_map_source(const struct snd_emu1010_routing_info *emu_ri,
+ unsigned val)
+{
+ for (unsigned i = 0; i < emu_ri->n_srcs; i++)
+ if (val == emu_ri->src_regs[i])
+ return i;
+ return 0;
+}
static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- char **items;
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu1010_idx(emu)];
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
- uinfo->value.enumerated.items = 49;
- items = emu1616_src_texts;
- } else {
- uinfo->value.enumerated.items = 53;
- items = emu1010_src_texts;
- }
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- items[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, emu_ri->n_srcs, emu_ri->src_texts);
}
static int snd_emu1010_output_source_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- unsigned int channel;
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu1010_idx(emu)];
+ unsigned channel = kcontrol->private_value;
- channel = (kcontrol->private_value) & 0xff;
- /* Limit: emu1010_output_dst, emu->emu1010.output_source */
- if (channel >= 24 ||
- (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
- channel >= 18))
+ if (channel >= emu_ri->n_outs)
return -EINVAL;
ucontrol->value.enumerated.item[0] = emu->emu1010.output_source[channel];
return 0;
@@ -432,41 +649,42 @@ static int snd_emu1010_output_source_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- unsigned int val;
- unsigned int channel;
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu1010_idx(emu)];
+ unsigned val = ucontrol->value.enumerated.item[0];
+ unsigned channel = kcontrol->private_value;
+ int change;
- val = ucontrol->value.enumerated.item[0];
- if (val >= 53 ||
- (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
- val >= 49))
+ if (val >= emu_ri->n_srcs)
return -EINVAL;
- channel = (kcontrol->private_value) & 0xff;
- /* Limit: emu1010_output_dst, emu->emu1010.output_source */
- if (channel >= 24 ||
- (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
- channel >= 18))
+ if (channel >= emu_ri->n_outs)
return -EINVAL;
- if (emu->emu1010.output_source[channel] == val)
- return 0;
- emu->emu1010.output_source[channel] = val;
- if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616)
- snd_emu1010_fpga_link_dst_src_write(emu,
- emu1616_output_dst[channel], emu1616_src_regs[val]);
- else
- snd_emu1010_fpga_link_dst_src_write(emu,
- emu1010_output_dst[channel], emu1010_src_regs[val]);
- return 1;
+ change = (emu->emu1010.output_source[channel] != val);
+ if (change) {
+ emu->emu1010.output_source[channel] = val;
+ guard(snd_emu1010_fpga_lock)(emu);
+ snd_emu1010_output_source_apply(emu, channel, val);
+ }
+ return change;
}
+static const struct snd_kcontrol_new emu1010_output_source_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_emu1010_input_output_source_info,
+ .get = snd_emu1010_output_source_get,
+ .put = snd_emu1010_output_source_put
+};
+
static int snd_emu1010_input_source_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- unsigned int channel;
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu1010_idx(emu)];
+ unsigned channel = kcontrol->private_value;
- channel = (kcontrol->private_value) & 0xff;
- /* Limit: emu1010_input_dst, emu->emu1010.input_source */
- if (channel >= 22)
+ if (channel >= emu_ri->n_ins)
return -EINVAL;
ucontrol->value.enumerated.item[0] = emu->emu1010.input_source[channel];
return 0;
@@ -476,134 +694,70 @@ static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- unsigned int val;
- unsigned int channel;
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu1010_idx(emu)];
+ unsigned val = ucontrol->value.enumerated.item[0];
+ unsigned channel = kcontrol->private_value;
+ int change;
- val = ucontrol->value.enumerated.item[0];
- if (val >= 53 ||
- (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
- val >= 49))
+ if (val >= emu_ri->n_srcs)
return -EINVAL;
- channel = (kcontrol->private_value) & 0xff;
- /* Limit: emu1010_input_dst, emu->emu1010.input_source */
- if (channel >= 22)
+ if (channel >= emu_ri->n_ins)
return -EINVAL;
- if (emu->emu1010.input_source[channel] == val)
- return 0;
- emu->emu1010.input_source[channel] = val;
- if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616)
- snd_emu1010_fpga_link_dst_src_write(emu,
- emu1010_input_dst[channel], emu1616_src_regs[val]);
- else
- snd_emu1010_fpga_link_dst_src_write(emu,
- emu1010_input_dst[channel], emu1010_src_regs[val]);
- return 1;
+ change = (emu->emu1010.input_source[channel] != val);
+ if (change) {
+ emu->emu1010.input_source[channel] = val;
+ guard(snd_emu1010_fpga_lock)(emu);
+ snd_emu1010_input_source_apply(emu, channel, val);
+ }
+ return change;
}
-#define EMU1010_SOURCE_OUTPUT(xname,chid) \
-{ \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
- .info = snd_emu1010_input_output_source_info, \
- .get = snd_emu1010_output_source_get, \
- .put = snd_emu1010_output_source_put, \
- .private_value = chid \
-}
-
-static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] __devinitdata = {
- EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Enum", 0),
- EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Enum", 1),
- EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Enum", 2),
- EMU1010_SOURCE_OUTPUT("Dock DAC2 Right Playback Enum", 3),
- EMU1010_SOURCE_OUTPUT("Dock DAC3 Left Playback Enum", 4),
- EMU1010_SOURCE_OUTPUT("Dock DAC3 Right Playback Enum", 5),
- EMU1010_SOURCE_OUTPUT("Dock DAC4 Left Playback Enum", 6),
- EMU1010_SOURCE_OUTPUT("Dock DAC4 Right Playback Enum", 7),
- EMU1010_SOURCE_OUTPUT("Dock Phones Left Playback Enum", 8),
- EMU1010_SOURCE_OUTPUT("Dock Phones Right Playback Enum", 9),
- EMU1010_SOURCE_OUTPUT("Dock SPDIF Left Playback Enum", 0xa),
- EMU1010_SOURCE_OUTPUT("Dock SPDIF Right Playback Enum", 0xb),
- EMU1010_SOURCE_OUTPUT("1010 SPDIF Left Playback Enum", 0xc),
- EMU1010_SOURCE_OUTPUT("1010 SPDIF Right Playback Enum", 0xd),
- EMU1010_SOURCE_OUTPUT("0202 DAC Left Playback Enum", 0xe),
- EMU1010_SOURCE_OUTPUT("0202 DAC Right Playback Enum", 0xf),
- EMU1010_SOURCE_OUTPUT("1010 ADAT 0 Playback Enum", 0x10),
- EMU1010_SOURCE_OUTPUT("1010 ADAT 1 Playback Enum", 0x11),
- EMU1010_SOURCE_OUTPUT("1010 ADAT 2 Playback Enum", 0x12),
- EMU1010_SOURCE_OUTPUT("1010 ADAT 3 Playback Enum", 0x13),
- EMU1010_SOURCE_OUTPUT("1010 ADAT 4 Playback Enum", 0x14),
- EMU1010_SOURCE_OUTPUT("1010 ADAT 5 Playback Enum", 0x15),
- EMU1010_SOURCE_OUTPUT("1010 ADAT 6 Playback Enum", 0x16),
- EMU1010_SOURCE_OUTPUT("1010 ADAT 7 Playback Enum", 0x17),
+static const struct snd_kcontrol_new emu1010_input_source_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_emu1010_input_output_source_info,
+ .get = snd_emu1010_input_source_get,
+ .put = snd_emu1010_input_source_put
};
+static int add_emu1010_source_mixers(struct snd_emu10k1 *emu)
+{
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu1010_idx(emu)];
+ int err;
-/* 1616(m) cardbus */
-static struct snd_kcontrol_new snd_emu1616_output_enum_ctls[] __devinitdata = {
- EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Enum", 0),
- EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Enum", 1),
- EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Enum", 2),
- EMU1010_SOURCE_OUTPUT("Dock DAC2 Right Playback Enum", 3),
- EMU1010_SOURCE_OUTPUT("Dock DAC3 Left Playback Enum", 4),
- EMU1010_SOURCE_OUTPUT("Dock DAC3 Right Playback Enum", 5),
- EMU1010_SOURCE_OUTPUT("Dock SPDIF Left Playback Enum", 6),
- EMU1010_SOURCE_OUTPUT("Dock SPDIF Right Playback Enum", 7),
- EMU1010_SOURCE_OUTPUT("Dock ADAT 0 Playback Enum", 8),
- EMU1010_SOURCE_OUTPUT("Dock ADAT 1 Playback Enum", 9),
- EMU1010_SOURCE_OUTPUT("Dock ADAT 2 Playback Enum", 0xa),
- EMU1010_SOURCE_OUTPUT("Dock ADAT 3 Playback Enum", 0xb),
- EMU1010_SOURCE_OUTPUT("Dock ADAT 4 Playback Enum", 0xc),
- EMU1010_SOURCE_OUTPUT("Dock ADAT 5 Playback Enum", 0xd),
- EMU1010_SOURCE_OUTPUT("Dock ADAT 6 Playback Enum", 0xe),
- EMU1010_SOURCE_OUTPUT("Dock ADAT 7 Playback Enum", 0xf),
- EMU1010_SOURCE_OUTPUT("Mana DAC Left Playback Enum", 0x10),
- EMU1010_SOURCE_OUTPUT("Mana DAC Right Playback Enum", 0x11),
-};
+ err = add_ctls(emu, &emu1010_output_source_ctl,
+ emu_ri->out_texts, emu_ri->n_outs);
+ if (err < 0)
+ return err;
+ err = add_ctls(emu, &emu1010_input_source_ctl,
+ emu1010_input_texts, emu_ri->n_ins);
+ return err;
+}
-#define EMU1010_SOURCE_INPUT(xname,chid) \
-{ \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
- .info = snd_emu1010_input_output_source_info, \
- .get = snd_emu1010_input_source_get, \
- .put = snd_emu1010_input_source_put, \
- .private_value = chid \
-}
-
-static struct snd_kcontrol_new snd_emu1010_input_enum_ctls[] __devinitdata = {
- EMU1010_SOURCE_INPUT("DSP 0 Capture Enum", 0),
- EMU1010_SOURCE_INPUT("DSP 1 Capture Enum", 1),
- EMU1010_SOURCE_INPUT("DSP 2 Capture Enum", 2),
- EMU1010_SOURCE_INPUT("DSP 3 Capture Enum", 3),
- EMU1010_SOURCE_INPUT("DSP 4 Capture Enum", 4),
- EMU1010_SOURCE_INPUT("DSP 5 Capture Enum", 5),
- EMU1010_SOURCE_INPUT("DSP 6 Capture Enum", 6),
- EMU1010_SOURCE_INPUT("DSP 7 Capture Enum", 7),
- EMU1010_SOURCE_INPUT("DSP 8 Capture Enum", 8),
- EMU1010_SOURCE_INPUT("DSP 9 Capture Enum", 9),
- EMU1010_SOURCE_INPUT("DSP A Capture Enum", 0xa),
- EMU1010_SOURCE_INPUT("DSP B Capture Enum", 0xb),
- EMU1010_SOURCE_INPUT("DSP C Capture Enum", 0xc),
- EMU1010_SOURCE_INPUT("DSP D Capture Enum", 0xd),
- EMU1010_SOURCE_INPUT("DSP E Capture Enum", 0xe),
- EMU1010_SOURCE_INPUT("DSP F Capture Enum", 0xf),
- EMU1010_SOURCE_INPUT("DSP 10 Capture Enum", 0x10),
- EMU1010_SOURCE_INPUT("DSP 11 Capture Enum", 0x11),
- EMU1010_SOURCE_INPUT("DSP 12 Capture Enum", 0x12),
- EMU1010_SOURCE_INPUT("DSP 13 Capture Enum", 0x13),
- EMU1010_SOURCE_INPUT("DSP 14 Capture Enum", 0x14),
- EMU1010_SOURCE_INPUT("DSP 15 Capture Enum", 0x15),
+static const char * const snd_emu1010_adc_pads[] = {
+ "ADC1 14dB PAD 0202 Capture Switch",
+ "ADC1 14dB PAD Audio Dock Capture Switch",
+ "ADC2 14dB PAD Audio Dock Capture Switch",
+ "ADC3 14dB PAD Audio Dock Capture Switch",
};
-
+static const unsigned short snd_emu1010_adc_pad_regs[] = {
+ EMU_HANA_0202_ADC_PAD1,
+ EMU_HANA_DOCK_ADC_PAD1,
+ EMU_HANA_DOCK_ADC_PAD2,
+ EMU_HANA_DOCK_ADC_PAD3,
+};
#define snd_emu1010_adc_pads_info snd_ctl_boolean_mono_info
static int snd_emu1010_adc_pads_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- unsigned int mask = kcontrol->private_value & 0xff;
+ unsigned int mask = snd_emu1010_adc_pad_regs[kcontrol->private_value];
+
ucontrol->value.integer.value[0] = (emu->emu1010.adc_pads & mask) ? 1 : 0;
return 0;
}
@@ -611,39 +765,48 @@ static int snd_emu1010_adc_pads_get(struct snd_kcontrol *kcontrol, struct snd_ct
static int snd_emu1010_adc_pads_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- unsigned int mask = kcontrol->private_value & 0xff;
+ unsigned int mask = snd_emu1010_adc_pad_regs[kcontrol->private_value];
unsigned int val, cache;
+ int change;
+
val = ucontrol->value.integer.value[0];
cache = emu->emu1010.adc_pads;
if (val == 1)
cache = cache | mask;
else
cache = cache & ~mask;
- if (cache != emu->emu1010.adc_pads) {
- snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, cache );
+ change = (cache != emu->emu1010.adc_pads);
+ if (change) {
+ snd_emu1010_fpga_write_lock(emu, EMU_HANA_ADC_PADS, cache );
emu->emu1010.adc_pads = cache;
}
- return 0;
+ return change;
}
+static const struct snd_kcontrol_new emu1010_adc_pads_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_emu1010_adc_pads_info,
+ .get = snd_emu1010_adc_pads_get,
+ .put = snd_emu1010_adc_pads_put
+};
-#define EMU1010_ADC_PADS(xname,chid) \
-{ \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
- .info = snd_emu1010_adc_pads_info, \
- .get = snd_emu1010_adc_pads_get, \
- .put = snd_emu1010_adc_pads_put, \
- .private_value = chid \
-}
+static const char * const snd_emu1010_dac_pads[] = {
+ "DAC1 0202 14dB PAD Playback Switch",
+ "DAC1 Audio Dock 14dB PAD Playback Switch",
+ "DAC2 Audio Dock 14dB PAD Playback Switch",
+ "DAC3 Audio Dock 14dB PAD Playback Switch",
+ "DAC4 Audio Dock 14dB PAD Playback Switch",
+};
-static struct snd_kcontrol_new snd_emu1010_adc_pads[] __devinitdata = {
- EMU1010_ADC_PADS("ADC1 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD1),
- EMU1010_ADC_PADS("ADC2 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD2),
- EMU1010_ADC_PADS("ADC3 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD3),
- EMU1010_ADC_PADS("ADC1 14dB PAD 0202 Capture Switch", EMU_HANA_0202_ADC_PAD1),
+static const unsigned short snd_emu1010_dac_regs[] = {
+ EMU_HANA_0202_DAC_PAD1,
+ EMU_HANA_DOCK_DAC_PAD1,
+ EMU_HANA_DOCK_DAC_PAD2,
+ EMU_HANA_DOCK_DAC_PAD3,
+ EMU_HANA_DOCK_DAC_PAD4,
};
#define snd_emu1010_dac_pads_info snd_ctl_boolean_mono_info
@@ -651,7 +814,8 @@ static struct snd_kcontrol_new snd_emu1010_adc_pads[] __devinitdata = {
static int snd_emu1010_dac_pads_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- unsigned int mask = kcontrol->private_value & 0xff;
+ unsigned int mask = snd_emu1010_dac_regs[kcontrol->private_value];
+
ucontrol->value.integer.value[0] = (emu->emu1010.dac_pads & mask) ? 1 : 0;
return 0;
}
@@ -659,192 +823,355 @@ static int snd_emu1010_dac_pads_get(struct snd_kcontrol *kcontrol, struct snd_ct
static int snd_emu1010_dac_pads_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- unsigned int mask = kcontrol->private_value & 0xff;
+ unsigned int mask = snd_emu1010_dac_regs[kcontrol->private_value];
unsigned int val, cache;
+ int change;
+
val = ucontrol->value.integer.value[0];
cache = emu->emu1010.dac_pads;
if (val == 1)
cache = cache | mask;
else
cache = cache & ~mask;
- if (cache != emu->emu1010.dac_pads) {
- snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, cache );
+ change = (cache != emu->emu1010.dac_pads);
+ if (change) {
+ snd_emu1010_fpga_write_lock(emu, EMU_HANA_DAC_PADS, cache );
emu->emu1010.dac_pads = cache;
}
+ return change;
+}
+
+static const struct snd_kcontrol_new emu1010_dac_pads_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = snd_emu1010_dac_pads_info,
+ .get = snd_emu1010_dac_pads_get,
+ .put = snd_emu1010_dac_pads_put
+};
+
+
+struct snd_emu1010_pads_info {
+ const char * const *adc_ctls, * const *dac_ctls;
+ unsigned n_adc_ctls, n_dac_ctls;
+};
+
+static const struct snd_emu1010_pads_info emu1010_pads_info[] = {
+ {
+ /* rev1 1010 */
+ .adc_ctls = snd_emu1010_adc_pads,
+ .n_adc_ctls = ARRAY_SIZE(snd_emu1010_adc_pads),
+ .dac_ctls = snd_emu1010_dac_pads,
+ .n_dac_ctls = ARRAY_SIZE(snd_emu1010_dac_pads),
+ },
+ {
+ /* rev2 1010 */
+ .adc_ctls = snd_emu1010_adc_pads,
+ .n_adc_ctls = ARRAY_SIZE(snd_emu1010_adc_pads) - 1,
+ .dac_ctls = snd_emu1010_dac_pads,
+ .n_dac_ctls = ARRAY_SIZE(snd_emu1010_dac_pads) - 1,
+ },
+ {
+ /* 1616(m) cardbus */
+ .adc_ctls = snd_emu1010_adc_pads + 1,
+ .n_adc_ctls = ARRAY_SIZE(snd_emu1010_adc_pads) - 2,
+ .dac_ctls = snd_emu1010_dac_pads + 1,
+ .n_dac_ctls = ARRAY_SIZE(snd_emu1010_dac_pads) - 2,
+ },
+ {
+ /* 0404 */
+ .adc_ctls = NULL,
+ .n_adc_ctls = 0,
+ .dac_ctls = NULL,
+ .n_dac_ctls = 0,
+ },
+};
+
+static const char * const emu1010_clock_texts[] = {
+ "44100", "48000", "SPDIF", "ADAT", "Dock", "BNC"
+};
+
+static const u8 emu1010_clock_vals[] = {
+ EMU_HANA_WCLOCK_INT_44_1K,
+ EMU_HANA_WCLOCK_INT_48K,
+ EMU_HANA_WCLOCK_HANA_SPDIF_IN,
+ EMU_HANA_WCLOCK_HANA_ADAT_IN,
+ EMU_HANA_WCLOCK_2ND_HANA,
+ EMU_HANA_WCLOCK_SYNC_BNC,
+};
+
+static const char * const emu0404_clock_texts[] = {
+ "44100", "48000", "SPDIF", "BNC"
+};
+
+static const u8 emu0404_clock_vals[] = {
+ EMU_HANA_WCLOCK_INT_44_1K,
+ EMU_HANA_WCLOCK_INT_48K,
+ EMU_HANA_WCLOCK_HANA_SPDIF_IN,
+ EMU_HANA_WCLOCK_SYNC_BNC,
+};
+
+struct snd_emu1010_clock_info {
+ const char * const *texts;
+ const u8 *vals;
+ unsigned num;
+};
+
+static const struct snd_emu1010_clock_info emu1010_clock_info[] = {
+ {
+ // rev1 1010
+ .texts = emu1010_clock_texts,
+ .vals = emu1010_clock_vals,
+ .num = ARRAY_SIZE(emu1010_clock_vals),
+ },
+ {
+ // rev2 1010
+ .texts = emu1010_clock_texts,
+ .vals = emu1010_clock_vals,
+ .num = ARRAY_SIZE(emu1010_clock_vals) - 1,
+ },
+ {
+ // 1616(m) CardBus
+ .texts = emu1010_clock_texts,
+ // TODO: determine what is actually available.
+ // Pedantically, *every* source comes from the 2nd FPGA, as the
+ // card itself has no own (digital) audio ports. The user manual
+ // claims that ADAT and S/PDIF clock sources are separate, which
+ // can mean two things: either E-MU mapped the dock's sources to
+ // the primary ones, or they determine the meaning of the "Dock"
+ // source depending on how the ports are actually configured
+ // (which the 2nd FPGA must be doing anyway).
+ .vals = emu1010_clock_vals,
+ .num = ARRAY_SIZE(emu1010_clock_vals),
+ },
+ {
+ // 0404
+ .texts = emu0404_clock_texts,
+ .vals = emu0404_clock_vals,
+ .num = ARRAY_SIZE(emu0404_clock_vals),
+ },
+};
+
+static int snd_emu1010_clock_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ const struct snd_emu1010_clock_info *emu_ci =
+ &emu1010_clock_info[emu1010_idx(emu)];
+
+ return snd_ctl_enum_info(uinfo, 1, emu_ci->num, emu_ci->texts);
+}
+
+static int snd_emu1010_clock_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = emu->emu1010.clock_source;
return 0;
}
+static int snd_emu1010_clock_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ const struct snd_emu1010_clock_info *emu_ci =
+ &emu1010_clock_info[emu1010_idx(emu)];
+ unsigned int val;
+ val = ucontrol->value.enumerated.item[0] ;
+ if (val >= emu_ci->num)
+ return -EINVAL;
+ guard(snd_emu1010_fpga_lock)(emu);
+ scoped_guard(spinlock_irq, &emu->reg_lock) {
+ if (emu->emu1010.clock_source == val)
+ return 0;
+ emu->emu1010.clock_source = val;
+ emu->emu1010.wclock = emu_ci->vals[val];
+ snd_emu1010_update_clock(emu);
+
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE);
+ snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, emu->emu1010.wclock);
+ }
-#define EMU1010_DAC_PADS(xname,chid) \
-{ \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
- .info = snd_emu1010_dac_pads_info, \
- .get = snd_emu1010_dac_pads_get, \
- .put = snd_emu1010_dac_pads_put, \
- .private_value = chid \
+ msleep(10); // Allow DLL to settle
+ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
+ return 1;
}
-static struct snd_kcontrol_new snd_emu1010_dac_pads[] __devinitdata = {
- EMU1010_DAC_PADS("DAC1 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD1),
- EMU1010_DAC_PADS("DAC2 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD2),
- EMU1010_DAC_PADS("DAC3 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD3),
- EMU1010_DAC_PADS("DAC4 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD4),
- EMU1010_DAC_PADS("DAC1 0202 14dB PAD Playback Switch", EMU_HANA_0202_DAC_PAD1),
+static const struct snd_kcontrol_new snd_emu1010_clock_source =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Clock Source",
+ .count = 1,
+ .info = snd_emu1010_clock_source_info,
+ .get = snd_emu1010_clock_source_get,
+ .put = snd_emu1010_clock_source_put
};
-
-static int snd_emu1010_internal_clock_info(struct snd_kcontrol *kcontrol,
+static int snd_emu1010_clock_fallback_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = {
- "44100", "48000", "SPDIF", "ADAT"
+ static const char * const texts[2] = {
+ "44100", "48000"
};
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
+}
+
+static int snd_emu1010_clock_fallback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = emu->emu1010.clock_fallback;
return 0;
-
-
}
-static int snd_emu1010_internal_clock_get(struct snd_kcontrol *kcontrol,
+static int snd_emu1010_clock_fallback_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int val = ucontrol->value.enumerated.item[0];
+ int change;
+
+ if (val >= 2)
+ return -EINVAL;
+ change = (emu->emu1010.clock_fallback != val);
+ if (change) {
+ emu->emu1010.clock_fallback = val;
+ snd_emu1010_fpga_write_lock(emu, EMU_HANA_DEFCLOCK, 1 - val);
+ }
+ return change;
+}
+
+static const struct snd_kcontrol_new snd_emu1010_clock_fallback =
+{
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Clock Fallback",
+ .count = 1,
+ .info = snd_emu1010_clock_fallback_info,
+ .get = snd_emu1010_clock_fallback_get,
+ .put = snd_emu1010_clock_fallback_put
+};
+
+static int snd_emu1010_optical_out_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char * const texts[2] = {
+ "SPDIF", "ADAT"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
+}
+
+static int snd_emu1010_optical_out_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
- ucontrol->value.enumerated.item[0] = emu->emu1010.internal_clock;
+ ucontrol->value.enumerated.item[0] = emu->emu1010.optical_out;
return 0;
}
-static int snd_emu1010_internal_clock_put(struct snd_kcontrol *kcontrol,
+static int snd_emu1010_optical_out_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
unsigned int val;
+ u32 tmp;
int change = 0;
- val = ucontrol->value.enumerated.item[0] ;
- /* Limit: uinfo->value.enumerated.items = 4; */
- if (val >= 4)
+ val = ucontrol->value.enumerated.item[0];
+ /* Limit: uinfo->value.enumerated.items = 2; */
+ if (val >= 2)
return -EINVAL;
- change = (emu->emu1010.internal_clock != val);
+ change = (emu->emu1010.optical_out != val);
if (change) {
- emu->emu1010.internal_clock = val;
- switch (val) {
- case 0:
- /* 44100 */
- /* Mute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
- /* Default fallback clock 48kHz */
- snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_44_1K );
- /* Word Clock source, Internal 44.1kHz x1 */
- snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
- EMU_HANA_WCLOCK_INT_44_1K | EMU_HANA_WCLOCK_1X );
- /* Set LEDs on Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
- EMU_HANA_DOCK_LEDS_2_44K | EMU_HANA_DOCK_LEDS_2_LOCK );
- /* Allow DLL to settle */
- msleep(10);
- /* Unmute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
- break;
- case 1:
- /* 48000 */
- /* Mute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
- /* Default fallback clock 48kHz */
- snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K );
- /* Word Clock source, Internal 48kHz x1 */
- snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
- EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_1X );
- /* Set LEDs on Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
- EMU_HANA_DOCK_LEDS_2_48K | EMU_HANA_DOCK_LEDS_2_LOCK );
- /* Allow DLL to settle */
- msleep(10);
- /* Unmute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
- break;
-
- case 2: /* Take clock from S/PDIF IN */
- /* Mute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
- /* Default fallback clock 48kHz */
- snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K );
- /* Word Clock source, sync to S/PDIF input */
- snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
- EMU_HANA_WCLOCK_HANA_SPDIF_IN | EMU_HANA_WCLOCK_1X );
- /* Set LEDs on Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
- EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_LOCK );
- /* FIXME: We should set EMU_HANA_DOCK_LEDS_2_LOCK only when clock signal is present and valid */
- /* Allow DLL to settle */
- msleep(10);
- /* Unmute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
- break;
-
- case 3:
- /* Take clock from ADAT IN */
- /* Mute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
- /* Default fallback clock 48kHz */
- snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K );
- /* Word Clock source, sync to ADAT input */
- snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
- EMU_HANA_WCLOCK_HANA_ADAT_IN | EMU_HANA_WCLOCK_1X );
- /* Set LEDs on Audio Dock */
- snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_LOCK );
- /* FIXME: We should set EMU_HANA_DOCK_LEDS_2_LOCK only when clock signal is present and valid */
- /* Allow DLL to settle */
- msleep(10);
- /* Unmute all */
- snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
-
-
- break;
- }
+ emu->emu1010.optical_out = val;
+ tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : EMU_HANA_OPTICAL_IN_SPDIF) |
+ (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : EMU_HANA_OPTICAL_OUT_SPDIF);
+ snd_emu1010_fpga_write_lock(emu, EMU_HANA_OPTICAL_TYPE, tmp);
}
- return change;
+ return change;
}
-static struct snd_kcontrol_new snd_emu1010_internal_clock =
+static const struct snd_kcontrol_new snd_emu1010_optical_out = {
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Optical Output Mode",
+ .count = 1,
+ .info = snd_emu1010_optical_out_info,
+ .get = snd_emu1010_optical_out_get,
+ .put = snd_emu1010_optical_out_put
+};
+
+static int snd_emu1010_optical_in_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
+ static const char * const texts[2] = {
+ "SPDIF", "ADAT"
+ };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
+}
+
+static int snd_emu1010_optical_in_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = emu->emu1010.optical_in;
+ return 0;
+}
+
+static int snd_emu1010_optical_in_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+ unsigned int val;
+ u32 tmp;
+ int change = 0;
+
+ val = ucontrol->value.enumerated.item[0];
+ /* Limit: uinfo->value.enumerated.items = 2; */
+ if (val >= 2)
+ return -EINVAL;
+ change = (emu->emu1010.optical_in != val);
+ if (change) {
+ emu->emu1010.optical_in = val;
+ tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : EMU_HANA_OPTICAL_IN_SPDIF) |
+ (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : EMU_HANA_OPTICAL_OUT_SPDIF);
+ snd_emu1010_fpga_write_lock(emu, EMU_HANA_OPTICAL_TYPE, tmp);
+ }
+ return change;
+}
+
+static const struct snd_kcontrol_new snd_emu1010_optical_in = {
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Clock Internal Rate",
+ .name = "Optical Input Mode",
.count = 1,
- .info = snd_emu1010_internal_clock_info,
- .get = snd_emu1010_internal_clock_get,
- .put = snd_emu1010_internal_clock_put
+ .info = snd_emu1010_optical_in_info,
+ .get = snd_emu1010_optical_in_get,
+ .put = snd_emu1010_optical_in_put
};
static int snd_audigy_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
#if 0
- static char *texts[4] = {
+ static const char * const texts[4] = {
"Unknown1", "Unknown2", "Mic", "Line"
};
#endif
- static char *texts[2] = {
+ static const char * const texts[2] = {
"Mic", "Line"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_audigy_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -862,9 +1189,8 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
unsigned int source_id;
unsigned int ngain, ogain;
- u32 gpio;
+ u16 gpio;
int change = 0;
- unsigned long flags;
u32 source;
/* If the capture source has changed,
* update the capture volume from the cached value
@@ -878,13 +1204,13 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
change = (emu->i2c_capture_source != source_id);
if (change) {
snd_emu10k1_i2c_write(emu, ADC_MUX, 0); /* Mute input */
- spin_lock_irqsave(&emu->emu_lock, flags);
- gpio = inl(emu->port + A_IOCFG);
- if (source_id==0)
- outl(gpio | 0x4, emu->port + A_IOCFG);
- else
- outl(gpio & ~0x4, emu->port + A_IOCFG);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ scoped_guard(spinlock_irq, &emu->emu_lock) {
+ gpio = inw(emu->port + A_IOCFG);
+ if (source_id == 0)
+ outw(gpio | 0x4, emu->port + A_IOCFG);
+ else
+ outw(gpio & ~0x4, emu->port + A_IOCFG);
+ }
ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
@@ -902,7 +1228,7 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_audigy_i2c_capture_source =
+static const struct snd_kcontrol_new snd_audigy_i2c_capture_source =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
@@ -943,7 +1269,7 @@ static int snd_audigy_i2c_volume_put(struct snd_kcontrol *kcontrol,
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
unsigned int ogain;
- unsigned int ngain;
+ unsigned int ngain0, ngain1;
unsigned int source_id;
int change = 0;
@@ -952,60 +1278,51 @@ static int snd_audigy_i2c_volume_put(struct snd_kcontrol *kcontrol,
/* capture_source: uinfo->value.enumerated.items = 2 */
if (source_id >= 2)
return -EINVAL;
+ ngain0 = ucontrol->value.integer.value[0];
+ ngain1 = ucontrol->value.integer.value[1];
+ if (ngain0 > 0xff)
+ return -EINVAL;
+ if (ngain1 > 0xff)
+ return -EINVAL;
ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
- ngain = ucontrol->value.integer.value[0];
- if (ngain > 0xff)
- return 0;
- if (ogain != ngain) {
+ if (ogain != ngain0) {
if (emu->i2c_capture_source == source_id)
- snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
- emu->i2c_capture_volume[source_id][0] = ngain;
+ snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCL, ngain0);
+ emu->i2c_capture_volume[source_id][0] = ngain0;
change = 1;
}
ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
- ngain = ucontrol->value.integer.value[1];
- if (ngain > 0xff)
- return 0;
- if (ogain != ngain) {
+ if (ogain != ngain1) {
if (emu->i2c_capture_source == source_id)
- snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
- emu->i2c_capture_volume[source_id][1] = ngain;
+ snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCR, ngain1);
+ emu->i2c_capture_volume[source_id][1] = ngain1;
change = 1;
}
return change;
}
-#define I2C_VOLUME(xname,chid) \
-{ \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
- SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
- .info = snd_audigy_i2c_volume_info, \
- .get = snd_audigy_i2c_volume_get, \
- .put = snd_audigy_i2c_volume_put, \
- .tlv = { .p = snd_audigy_db_scale2 }, \
- .private_value = chid \
-}
-
+static const struct snd_kcontrol_new i2c_volume_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = snd_audigy_i2c_volume_info,
+ .get = snd_audigy_i2c_volume_get,
+ .put = snd_audigy_i2c_volume_put,
+ .tlv = { .p = snd_audigy_db_scale2 }
+};
-static struct snd_kcontrol_new snd_audigy_i2c_volume_ctls[] __devinitdata = {
- I2C_VOLUME("Mic Capture Volume", 0),
- I2C_VOLUME("Line Capture Volume", 0)
+static const char * const snd_audigy_i2c_volume_ctls[] = {
+ "Mic Capture Volume",
+ "Line Capture Volume",
};
#if 0
static int snd_audigy_spdif_output_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {"44100", "48000", "96000"};
+ static const char * const texts[] = {"44100", "48000", "96000"};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int snd_audigy_spdif_output_rate_get(struct snd_kcontrol *kcontrol,
@@ -1013,10 +1330,7 @@ static int snd_audigy_spdif_output_rate_get(struct snd_kcontrol *kcontrol,
{
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
unsigned int tmp;
- unsigned long flags;
-
- spin_lock_irqsave(&emu->reg_lock, flags);
tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
switch (tmp & A_SPDIF_RATE_MASK) {
case A_SPDIF_44100:
@@ -1031,7 +1345,6 @@ static int snd_audigy_spdif_output_rate_get(struct snd_kcontrol *kcontrol,
default:
ucontrol->value.enumerated.item[0] = 1;
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
@@ -1041,7 +1354,6 @@ static int snd_audigy_spdif_output_rate_put(struct snd_kcontrol *kcontrol,
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
int change;
unsigned int reg, val, tmp;
- unsigned long flags;
switch(ucontrol->value.enumerated.item[0]) {
case 0:
@@ -1059,17 +1371,17 @@ static int snd_audigy_spdif_output_rate_put(struct snd_kcontrol *kcontrol,
}
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irq)(&emu->reg_lock);
reg = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
tmp = reg & ~A_SPDIF_RATE_MASK;
tmp |= val;
- if ((change = (tmp != reg)))
+ change = (tmp != reg);
+ if (change)
snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
-static struct snd_kcontrol_new snd_audigy_spdif_output_rate =
+static const struct snd_kcontrol_new snd_audigy_spdif_output_rate =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1088,7 +1400,6 @@ static int snd_emu10k1_spdif_put(struct snd_kcontrol *kcontrol,
unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
int change;
unsigned int val;
- unsigned long flags;
/* Limit: emu->spdif_bits */
if (idx >= 3)
@@ -1097,17 +1408,15 @@ static int snd_emu10k1_spdif_put(struct snd_kcontrol *kcontrol,
(ucontrol->value.iec958.status[1] << 8) |
(ucontrol->value.iec958.status[2] << 16) |
(ucontrol->value.iec958.status[3] << 24);
- spin_lock_irqsave(&emu->reg_lock, flags);
change = val != emu->spdif_bits[idx];
if (change) {
snd_emu10k1_ptr_write(emu, SPCS0 + idx, 0, val);
emu->spdif_bits[idx] = val;
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_spdif_mask_control =
+static const struct snd_kcontrol_new snd_emu10k1_spdif_mask_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1117,7 +1426,7 @@ static struct snd_kcontrol_new snd_emu10k1_spdif_mask_control =
.get = snd_emu10k1_spdif_get_mask
};
-static struct snd_kcontrol_new snd_emu10k1_spdif_control =
+static const struct snd_kcontrol_new snd_emu10k1_spdif_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -1131,10 +1440,10 @@ static struct snd_kcontrol_new snd_emu10k1_spdif_control =
static void update_emu10k1_fxrt(struct snd_emu10k1 *emu, int voice, unsigned char *route)
{
if (emu->audigy) {
- snd_emu10k1_ptr_write(emu, A_FXRT1, voice,
- snd_emu10k1_compose_audigy_fxrt1(route));
- snd_emu10k1_ptr_write(emu, A_FXRT2, voice,
- snd_emu10k1_compose_audigy_fxrt2(route));
+ snd_emu10k1_ptr_write_multiple(emu, voice,
+ A_FXRT1, snd_emu10k1_compose_audigy_fxrt1(route),
+ A_FXRT2, snd_emu10k1_compose_audigy_fxrt2(route),
+ REGLIST_END);
} else {
snd_emu10k1_ptr_write(emu, FXRT, voice,
snd_emu10k1_compose_send_routing(route));
@@ -1148,11 +1457,8 @@ static void update_emu10k1_send_volume(struct snd_emu10k1 *emu, int voice, unsig
snd_emu10k1_ptr_write(emu, PSST_FXSENDAMOUNT_C, voice, volume[2]);
snd_emu10k1_ptr_write(emu, DSL_FXSENDAMOUNT_D, voice, volume[3]);
if (emu->audigy) {
- unsigned int val = ((unsigned int)volume[4] << 24) |
- ((unsigned int)volume[5] << 16) |
- ((unsigned int)volume[6] << 8) |
- (unsigned int)volume[7];
- snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice, val);
+ snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice,
+ snd_emu10k1_compose_audigy_sendamounts(volume));
}
}
@@ -1171,7 +1477,6 @@ static int snd_emu10k1_send_routing_info(struct snd_kcontrol *kcontrol, struct s
static int snd_emu10k1_send_routing_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
@@ -1179,19 +1484,16 @@ static int snd_emu10k1_send_routing_get(struct snd_kcontrol *kcontrol,
int num_efx = emu->audigy ? 8 : 4;
int mask = emu->audigy ? 0x3f : 0x0f;
- spin_lock_irqsave(&emu->reg_lock, flags);
for (voice = 0; voice < 3; voice++)
for (idx = 0; idx < num_efx; idx++)
ucontrol->value.integer.value[(voice * num_efx) + idx] =
mix->send_routing[voice][idx] & mask;
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
@@ -1199,7 +1501,7 @@ static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol,
int num_efx = emu->audigy ? 8 : 4;
int mask = emu->audigy ? 0x3f : 0x0f;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irq)(&emu->reg_lock);
for (voice = 0; voice < 3; voice++)
for (idx = 0; idx < num_efx; idx++) {
val = ucontrol->value.integer.value[(voice * num_efx) + idx] & mask;
@@ -1208,22 +1510,21 @@ static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol,
change = 1;
}
}
- if (change && mix->epcm) {
- if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+ if (change && mix->epcm && mix->epcm->voices[0]) {
+ if (!mix->epcm->voices[0]->last) {
update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
&mix->send_routing[1][0]);
- update_emu10k1_fxrt(emu, mix->epcm->voices[1]->number,
+ update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number + 1,
&mix->send_routing[2][0]);
- } else if (mix->epcm->voices[0]) {
+ } else {
update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
&mix->send_routing[0][0]);
}
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_send_routing_control =
+static const struct snd_kcontrol_new snd_emu10k1_send_routing_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1247,31 +1548,27 @@ static int snd_emu10k1_send_volume_info(struct snd_kcontrol *kcontrol, struct sn
static int snd_emu10k1_send_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
int idx;
int num_efx = emu->audigy ? 8 : 4;
- spin_lock_irqsave(&emu->reg_lock, flags);
for (idx = 0; idx < 3*num_efx; idx++)
ucontrol->value.integer.value[idx] = mix->send_volume[idx/num_efx][idx%num_efx];
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
static int snd_emu10k1_send_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
int change = 0, idx, val;
int num_efx = emu->audigy ? 8 : 4;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irq)(&emu->reg_lock);
for (idx = 0; idx < 3*num_efx; idx++) {
val = ucontrol->value.integer.value[idx] & 255;
if (mix->send_volume[idx/num_efx][idx%num_efx] != val) {
@@ -1279,22 +1576,21 @@ static int snd_emu10k1_send_volume_put(struct snd_kcontrol *kcontrol,
change = 1;
}
}
- if (change && mix->epcm) {
- if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+ if (change && mix->epcm && mix->epcm->voices[0]) {
+ if (!mix->epcm->voices[0]->last) {
update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
&mix->send_volume[1][0]);
- update_emu10k1_send_volume(emu, mix->epcm->voices[1]->number,
+ update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number + 1,
&mix->send_volume[2][0]);
- } else if (mix->epcm->voices[0]) {
+ } else {
update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
&mix->send_volume[0][0]);
}
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_send_volume_control =
+static const struct snd_kcontrol_new snd_emu10k1_send_volume_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1310,7 +1606,7 @@ static int snd_emu10k1_attn_info(struct snd_kcontrol *kcontrol, struct snd_ctl_e
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 3;
uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 0xffff;
+ uinfo->value.integer.max = 0x1fffd;
return 0;
}
@@ -1320,46 +1616,42 @@ static int snd_emu10k1_attn_get(struct snd_kcontrol *kcontrol,
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
- unsigned long flags;
int idx;
- spin_lock_irqsave(&emu->reg_lock, flags);
for (idx = 0; idx < 3; idx++)
- ucontrol->value.integer.value[idx] = mix->attn[idx];
- spin_unlock_irqrestore(&emu->reg_lock, flags);
+ ucontrol->value.integer.value[idx] = mix->attn[idx] * 0xffffU / 0x8000U;
return 0;
}
static int snd_emu10k1_attn_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
int change = 0, idx, val;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irq)(&emu->reg_lock);
for (idx = 0; idx < 3; idx++) {
- val = ucontrol->value.integer.value[idx] & 0xffff;
+ unsigned uval = ucontrol->value.integer.value[idx] & 0x1ffff;
+ val = uval * 0x8000U / 0xffffU;
if (mix->attn[idx] != val) {
mix->attn[idx] = val;
change = 1;
}
}
- if (change && mix->epcm) {
- if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+ if (change && mix->epcm && mix->epcm->voices[0]) {
+ if (!mix->epcm->voices[0]->last) {
snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[1]);
- snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[1]->number, mix->attn[2]);
- } else if (mix->epcm->voices[0]) {
+ snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number + 1, mix->attn[2]);
+ } else {
snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[0]);
}
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_attn_control =
+static const struct snd_kcontrol_new snd_emu10k1_attn_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1385,7 +1677,6 @@ static int snd_emu10k1_efx_send_routing_info(struct snd_kcontrol *kcontrol, stru
static int snd_emu10k1_efx_send_routing_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
@@ -1393,18 +1684,15 @@ static int snd_emu10k1_efx_send_routing_get(struct snd_kcontrol *kcontrol,
int num_efx = emu->audigy ? 8 : 4;
int mask = emu->audigy ? 0x3f : 0x0f;
- spin_lock_irqsave(&emu->reg_lock, flags);
for (idx = 0; idx < num_efx; idx++)
ucontrol->value.integer.value[idx] =
mix->send_routing[0][idx] & mask;
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
@@ -1412,7 +1700,7 @@ static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol,
int num_efx = emu->audigy ? 8 : 4;
int mask = emu->audigy ? 0x3f : 0x0f;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irq)(&emu->reg_lock);
for (idx = 0; idx < num_efx; idx++) {
val = ucontrol->value.integer.value[idx] & mask;
if (mix->send_routing[0][idx] != val) {
@@ -1427,11 +1715,10 @@ static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol,
&mix->send_routing[0][0]);
}
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_efx_send_routing_control =
+static const struct snd_kcontrol_new snd_emu10k1_efx_send_routing_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1455,31 +1742,27 @@ static int snd_emu10k1_efx_send_volume_info(struct snd_kcontrol *kcontrol, struc
static int snd_emu10k1_efx_send_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
int idx;
int num_efx = emu->audigy ? 8 : 4;
- spin_lock_irqsave(&emu->reg_lock, flags);
for (idx = 0; idx < num_efx; idx++)
ucontrol->value.integer.value[idx] = mix->send_volume[0][idx];
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
static int snd_emu10k1_efx_send_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
int change = 0, idx, val;
int num_efx = emu->audigy ? 8 : 4;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irq)(&emu->reg_lock);
for (idx = 0; idx < num_efx; idx++) {
val = ucontrol->value.integer.value[idx] & 255;
if (mix->send_volume[0][idx] != val) {
@@ -1493,12 +1776,11 @@ static int snd_emu10k1_efx_send_volume_put(struct snd_kcontrol *kcontrol,
&mix->send_volume[0][0]);
}
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_efx_send_volume_control =
+static const struct snd_kcontrol_new snd_emu10k1_efx_send_volume_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1514,7 +1796,7 @@ static int snd_emu10k1_efx_attn_info(struct snd_kcontrol *kcontrol, struct snd_c
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 0xffff;
+ uinfo->value.integer.max = 0x1fffd;
return 0;
}
@@ -1524,25 +1806,23 @@ static int snd_emu10k1_efx_attn_get(struct snd_kcontrol *kcontrol,
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
struct snd_emu10k1_pcm_mixer *mix =
&emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
- unsigned long flags;
- spin_lock_irqsave(&emu->reg_lock, flags);
- ucontrol->value.integer.value[0] = mix->attn[0];
- spin_unlock_irqrestore(&emu->reg_lock, flags);
+ ucontrol->value.integer.value[0] = mix->attn[0] * 0xffffU / 0x8000U;
return 0;
}
static int snd_emu10k1_efx_attn_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
int change = 0, val;
+ unsigned uval;
- spin_lock_irqsave(&emu->reg_lock, flags);
- val = ucontrol->value.integer.value[0] & 0xffff;
+ guard(spinlock_irq)(&emu->reg_lock);
+ uval = ucontrol->value.integer.value[0] & 0x1ffff;
+ val = uval * 0x8000U / 0xffffU;
if (mix->attn[0] != val) {
mix->attn[0] = val;
change = 1;
@@ -1552,11 +1832,10 @@ static int snd_emu10k1_efx_attn_put(struct snd_kcontrol *kcontrol,
snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[ch]->number, mix->attn[0]);
}
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_efx_attn_control =
+static const struct snd_kcontrol_new snd_emu10k1_efx_attn_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1575,7 +1854,7 @@ static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol,
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
if (emu->audigy)
- ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0;
+ ucontrol->value.integer.value[0] = inw(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0;
else
ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 1 : 0;
if (emu->card_capabilities->invert_shared_spdif)
@@ -1588,7 +1867,6 @@ static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol,
static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- unsigned long flags;
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
unsigned int reg, val, sw;
int change = 0;
@@ -1596,17 +1874,17 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
sw = ucontrol->value.integer.value[0];
if (emu->card_capabilities->invert_shared_spdif)
sw = !sw;
- spin_lock_irqsave(&emu->reg_lock, flags);
+ guard(spinlock_irq)(&emu->emu_lock);
if ( emu->card_capabilities->i2c_adc) {
/* Do nothing for Audigy 2 ZS Notebook */
} else if (emu->audigy) {
- reg = inl(emu->port + A_IOCFG);
+ reg = inw(emu->port + A_IOCFG);
val = sw ? A_IOCFG_GPOUT0 : 0;
change = (reg & A_IOCFG_GPOUT0) != val;
if (change) {
reg &= ~A_IOCFG_GPOUT0;
reg |= val;
- outl(reg | val, emu->port + A_IOCFG);
+ outw(reg | val, emu->port + A_IOCFG);
}
}
reg = inl(emu->port + HCFG);
@@ -1617,11 +1895,10 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
reg |= val;
outl(reg | val, emu->port + HCFG);
}
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_shared_spdif __devinitdata =
+static const struct snd_kcontrol_new snd_emu10k1_shared_spdif =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "SB Live Analog/Digital Output Jack",
@@ -1630,7 +1907,7 @@ static struct snd_kcontrol_new snd_emu10k1_shared_spdif __devinitdata =
.put = snd_emu10k1_shared_spdif_put
};
-static struct snd_kcontrol_new snd_audigy_shared_spdif __devinitdata =
+static const struct snd_kcontrol_new snd_audigy_shared_spdif =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Audigy Analog/Digital Output Jack",
@@ -1668,10 +1945,10 @@ static int snd_audigy_capture_boost_put(struct snd_kcontrol *kcontrol,
return snd_ac97_update(emu->ac97, AC97_REC_GAIN, val);
}
-static struct snd_kcontrol_new snd_audigy_capture_boost __devinitdata =
+static const struct snd_kcontrol_new snd_audigy_capture_boost =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Analog Capture Boost",
+ .name = "Mic Extra Boost",
.info = snd_audigy_capture_boost_info,
.get = snd_audigy_capture_boost_get,
.put = snd_audigy_capture_boost_put
@@ -1692,45 +1969,34 @@ static int remove_ctl(struct snd_card *card, const char *name)
{
struct snd_ctl_elem_id id;
memset(&id, 0, sizeof(id));
- strcpy(id.name, name);
+ strscpy(id.name, name);
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
return snd_ctl_remove_id(card, &id);
}
-static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name)
-{
- struct snd_ctl_elem_id sid;
- memset(&sid, 0, sizeof(sid));
- strcpy(sid.name, name);
- sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- return snd_ctl_find_id(card, &sid);
-}
-
static int rename_ctl(struct snd_card *card, const char *src, const char *dst)
{
- struct snd_kcontrol *kctl = ctl_find(card, src);
+ struct snd_kcontrol *kctl = snd_ctl_find_id_mixer(card, src);
if (kctl) {
- strcpy(kctl->id.name, dst);
+ snd_ctl_rename(card, kctl, dst);
return 0;
}
return -ENOENT;
}
-int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
- int pcm_device, int multi_device)
+int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
+ int pcm_device, int multi_device)
{
- int err, pcm;
+ int err;
struct snd_kcontrol *kctl;
struct snd_card *card = emu->card;
- char **c;
- static char *emu10k1_remove_ctls[] = {
+ const char * const *c;
+ static const char * const emu10k1_remove_ctls[] = {
/* no AC97 mono, surround, center/lfe */
"Master Mono Playback Switch",
"Master Mono Playback Volume",
"PCM Out Path & Mute",
"Mono Output Select",
- "Front Playback Switch",
- "Front Playback Volume",
"Surround Playback Switch",
"Surround Playback Volume",
"Center Playback Switch",
@@ -1739,20 +2005,18 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"LFE Playback Volume",
NULL
};
- static char *emu10k1_rename_ctls[] = {
+ static const char * const emu10k1_rename_ctls[] = {
"Surround Digital Playback Volume", "Surround Playback Volume",
"Center Digital Playback Volume", "Center Playback Volume",
"LFE Digital Playback Volume", "LFE Playback Volume",
NULL
};
- static char *audigy_remove_ctls[] = {
+ static const char * const audigy_remove_ctls[] = {
/* Master/PCM controls on ac97 of Audigy has no effect */
/* On the Audigy2 the AC97 playback is piped into
* the Philips ADC for 24bit capture */
"PCM Playback Switch",
"PCM Playback Volume",
- "Master Mono Playback Switch",
- "Master Mono Playback Volume",
"Master Playback Switch",
"Master Playback Volume",
"PCM Out Path & Mute",
@@ -1762,21 +2026,29 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"Capture Switch",
"Capture Volume",
"Mic Select",
+ "Headphone Playback Switch",
+ "Headphone Playback Volume",
+ "3D Control - Center",
+ "3D Control - Depth",
+ "3D Control - Switch",
"Video Playback Switch",
"Video Playback Volume",
"Mic Playback Switch",
"Mic Playback Volume",
+ "External Amplifier",
NULL
};
- static char *audigy_rename_ctls[] = {
+ static const char * const audigy_rename_ctls[] = {
/* use conventional names */
"Wave Playback Volume", "PCM Playback Volume",
/* "Wave Capture Volume", "PCM Capture Volume", */
"Wave Master Playback Volume", "Master Playback Volume",
"AMic Playback Volume", "Mic Playback Volume",
+ "Master Mono Playback Switch", "Phone Output Playback Switch",
+ "Master Mono Playback Volume", "Phone Output Playback Volume",
NULL
};
- static char *audigy_rename_ctls_i2c_adc[] = {
+ static const char * const audigy_rename_ctls_i2c_adc[] = {
//"Analog Mix Capture Volume","OLD Analog Mix Capture Volume",
"Line Capture Volume", "Analog Mix Capture Volume",
"Wave Playback Volume", "OLD PCM Playback Volume",
@@ -1785,7 +2057,7 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"CD Capture Volume", "IEC958 Optical Capture Volume",
NULL
};
- static char *audigy_remove_ctls_i2c_adc[] = {
+ static const char * const audigy_remove_ctls_i2c_adc[] = {
/* On the Audigy2 ZS Notebook
* Capture via WM8775 */
"Mic Capture Volume",
@@ -1794,13 +2066,11 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"IEC958 Optical Capture Volume",
NULL
};
- static char *audigy_remove_ctls_1361t_adc[] = {
+ static const char * const audigy_remove_ctls_1361t_adc[] = {
/* On the Audigy2 the AC97 playback is piped into
* the Philips ADC for 24bit capture */
"PCM Playback Switch",
"PCM Playback Volume",
- "Master Mono Playback Switch",
- "Master Mono Playback Volume",
"Capture Source",
"Capture Switch",
"Capture Volume",
@@ -1814,7 +2084,7 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"Line2 Capture Volume",
NULL
};
- static char *audigy_rename_ctls_1361t_adc[] = {
+ static const char * const audigy_rename_ctls_1361t_adc[] = {
"Master Playback Switch", "Master Capture Switch",
"Master Playback Volume", "Master Capture Volume",
"Wave Master Playback Volume", "Master Playback Volume",
@@ -1832,19 +2102,21 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
"Aux Playback Volume", "Aux Capture Volume",
"Video Playback Switch", "Video Capture Switch",
"Video Playback Volume", "Video Capture Volume",
-
+ "Master Mono Playback Switch", "Phone Output Playback Switch",
+ "Master Mono Playback Volume", "Phone Output Playback Volume",
NULL
};
if (emu->card_capabilities->ac97_chip) {
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_emu10k1_ac97_write,
.read = snd_emu10k1_ac97_read,
};
- if ((err = snd_ac97_bus(emu->card, 0, &ops, NULL, &pbus)) < 0)
+ err = snd_ac97_bus(emu->card, 0, &ops, NULL, &pbus);
+ if (err < 0)
return err;
pbus->no_vra = 1; /* we don't need VRA */
@@ -1852,11 +2124,14 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
ac97.private_data = emu;
ac97.private_free = snd_emu10k1_mixer_free_ac97;
ac97.scaps = AC97_SCAP_NO_SPDIF;
- if ((err = snd_ac97_mixer(pbus, &ac97, &emu->ac97)) < 0) {
+ err = snd_ac97_mixer(pbus, &ac97, &emu->ac97);
+ if (err < 0) {
if (emu->card_capabilities->ac97_chip == 1)
return err;
- snd_printd(KERN_INFO "emu10k1: AC97 is optional on this board\n");
- snd_printd(KERN_INFO" Proceeding without ac97 mixers...\n");
+ dev_info(emu->card->dev,
+ "AC97 is optional on this board\n");
+ dev_info(emu->card->dev,
+ "Proceeding without ac97 mixers...\n");
snd_device_free(emu->card, pbus);
goto no_ac97; /* FIXME: get rid of ugly gotos.. */
}
@@ -1865,6 +2140,9 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
snd_ac97_write_cache(emu->ac97, AC97_MASTER, 0x0000);
/* set capture source to mic */
snd_ac97_write_cache(emu->ac97, AC97_REC_SEL, 0x0000);
+ /* set mono output (TAD) to mic */
+ snd_ac97_update_bits(emu->ac97, AC97_GENERAL_PURPOSE,
+ 0x0200, 0x0200);
if (emu->card_capabilities->adc_1361t)
c = audigy_remove_ctls_1361t_adc;
else
@@ -1879,6 +2157,8 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
emu->rear_ac97 = 1;
snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE|AC97SLOT_REAR_LEFT|AC97SLOT_REAR_RIGHT);
snd_ac97_write_cache(emu->ac97, AC97_HEADPHONE, 0x0202);
+ remove_ctl(card,"Front Playback Volume");
+ remove_ctl(card,"Front Playback Switch");
}
/* remove unused AC97 controls */
snd_ac97_write_cache(emu->ac97, AC97_SURROUND_MASTER, 0x0202);
@@ -1894,11 +2174,11 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
} else {
no_ac97:
if (emu->card_capabilities->ecard)
- strcpy(emu->card->mixername, "EMU APS");
+ strscpy(emu->card->mixername, "EMU APS");
else if (emu->audigy)
- strcpy(emu->card->mixername, "SB Audigy");
+ strscpy(emu->card->mixername, "SB Audigy");
else
- strcpy(emu->card->mixername, "Emu10k1");
+ strscpy(emu->card->mixername, "Emu10k1");
}
if (emu->audigy)
@@ -1913,217 +2193,176 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu,
for (; *c; c += 2)
rename_ctl(card, c[0], c[1]);
+ if (emu->card_capabilities->subsystem == 0x80401102) { /* SB Live! Platinum CT4760P */
+ remove_ctl(card, "Center Playback Volume");
+ remove_ctl(card, "LFE Playback Volume");
+ remove_ctl(card, "Wave Center Playback Volume");
+ remove_ctl(card, "Wave LFE Playback Volume");
+ }
if (emu->card_capabilities->subsystem == 0x20071102) { /* Audigy 4 Pro */
rename_ctl(card, "Line2 Capture Volume", "Line1/Mic Capture Volume");
rename_ctl(card, "Analog Mix Capture Volume", "Line2 Capture Volume");
rename_ctl(card, "Aux2 Capture Volume", "Line3 Capture Volume");
rename_ctl(card, "Mic Capture Volume", "Unknown1 Capture Volume");
- remove_ctl(card, "Headphone Playback Switch");
- remove_ctl(card, "Headphone Playback Volume");
- remove_ctl(card, "3D Control - Center");
- remove_ctl(card, "3D Control - Depth");
- remove_ctl(card, "3D Control - Switch");
}
- if ((kctl = emu->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu)) == NULL)
+ kctl = emu->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu);
+ if (!kctl)
return -ENOMEM;
kctl->id.device = pcm_device;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
- if ((kctl = emu->ctl_send_volume = snd_ctl_new1(&snd_emu10k1_send_volume_control, emu)) == NULL)
+ kctl = emu->ctl_send_volume = snd_ctl_new1(&snd_emu10k1_send_volume_control, emu);
+ if (!kctl)
return -ENOMEM;
kctl->id.device = pcm_device;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
- if ((kctl = emu->ctl_attn = snd_ctl_new1(&snd_emu10k1_attn_control, emu)) == NULL)
+ kctl = emu->ctl_attn = snd_ctl_new1(&snd_emu10k1_attn_control, emu);
+ if (!kctl)
return -ENOMEM;
kctl->id.device = pcm_device;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
- if ((kctl = emu->ctl_efx_send_routing = snd_ctl_new1(&snd_emu10k1_efx_send_routing_control, emu)) == NULL)
+ kctl = emu->ctl_efx_send_routing = snd_ctl_new1(&snd_emu10k1_efx_send_routing_control, emu);
+ if (!kctl)
return -ENOMEM;
kctl->id.device = multi_device;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
- if ((kctl = emu->ctl_efx_send_volume = snd_ctl_new1(&snd_emu10k1_efx_send_volume_control, emu)) == NULL)
+ kctl = emu->ctl_efx_send_volume = snd_ctl_new1(&snd_emu10k1_efx_send_volume_control, emu);
+ if (!kctl)
return -ENOMEM;
kctl->id.device = multi_device;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
- if ((kctl = emu->ctl_efx_attn = snd_ctl_new1(&snd_emu10k1_efx_attn_control, emu)) == NULL)
+ kctl = emu->ctl_efx_attn = snd_ctl_new1(&snd_emu10k1_efx_attn_control, emu);
+ if (!kctl)
return -ENOMEM;
kctl->id.device = multi_device;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
- /* initialize the routing and volume table for each pcm playback stream */
- for (pcm = 0; pcm < 32; pcm++) {
- struct snd_emu10k1_pcm_mixer *mix;
- int v;
-
- mix = &emu->pcm_mixer[pcm];
- mix->epcm = NULL;
-
- for (v = 0; v < 4; v++)
- mix->send_routing[0][v] =
- mix->send_routing[1][v] =
- mix->send_routing[2][v] = v;
-
- memset(&mix->send_volume, 0, sizeof(mix->send_volume));
- mix->send_volume[0][0] = mix->send_volume[0][1] =
- mix->send_volume[1][0] = mix->send_volume[2][1] = 255;
-
- mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
- }
-
- /* initialize the routing and volume table for the multichannel playback stream */
- for (pcm = 0; pcm < NUM_EFX_PLAYBACK; pcm++) {
- struct snd_emu10k1_pcm_mixer *mix;
- int v;
-
- mix = &emu->efx_pcm_mixer[pcm];
- mix->epcm = NULL;
-
- mix->send_routing[0][0] = pcm;
- mix->send_routing[0][1] = (pcm == 0) ? 1 : 0;
- for (v = 0; v < 2; v++)
- mix->send_routing[0][2+v] = 13+v;
- if (emu->audigy)
- for (v = 0; v < 4; v++)
- mix->send_routing[0][4+v] = 60+v;
-
- memset(&mix->send_volume, 0, sizeof(mix->send_volume));
- mix->send_volume[0][0] = 255;
-
- mix->attn[0] = 0xffff;
- }
-
- if (! emu->card_capabilities->ecard) { /* FIXME: APS has these controls? */
+ if (!emu->card_capabilities->ecard && !emu->card_capabilities->emu_model) {
/* sb live! and audigy */
- if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL)
+ kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu);
+ if (!kctl)
return -ENOMEM;
if (!emu->audigy)
kctl->id.device = emu->pcm_efx->device;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
- if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_control, emu)) == NULL)
+ kctl = snd_ctl_new1(&snd_emu10k1_spdif_control, emu);
+ if (!kctl)
return -ENOMEM;
if (!emu->audigy)
kctl->id.device = emu->pcm_efx->device;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
}
if (emu->card_capabilities->emu_model) {
; /* Disable the snd_audigy_spdif_shared_spdif */
} else if (emu->audigy) {
- if ((kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu)) == NULL)
+ kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu);
+ if (!kctl)
return -ENOMEM;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
#if 0
- if ((kctl = snd_ctl_new1(&snd_audigy_spdif_output_rate, emu)) == NULL)
+ kctl = snd_ctl_new1(&snd_audigy_spdif_output_rate, emu);
+ if (!kctl)
return -ENOMEM;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
#endif
} else if (! emu->card_capabilities->ecard) {
/* sb live! */
- if ((kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu)) == NULL)
+ kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu);
+ if (!kctl)
return -ENOMEM;
- if ((err = snd_ctl_add(card, kctl)))
+ err = snd_ctl_add(card, kctl);
+ if (err)
return err;
}
if (emu->card_capabilities->ca0151_chip) { /* P16V */
- if ((err = snd_p16v_mixer(emu)))
+ err = snd_p16v_mixer(emu);
+ if (err)
return err;
}
- if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
- /* 1616(m) cardbus */
- int i;
-
- for (i = 0; i < ARRAY_SIZE(snd_emu1616_output_enum_ctls); i++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1616_output_enum_ctls[i],
- emu));
- if (err < 0)
- return err;
- }
- for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_input_enum_ctls[i],
- emu));
- if (err < 0)
- return err;
- }
- for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads) - 2; i++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_adc_pads[i], emu));
- if (err < 0)
- return err;
- }
- for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads) - 2; i++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
- if (err < 0)
- return err;
+ if (emu->card_capabilities->emu_model) {
+ unsigned i, emu_idx = emu1010_idx(emu);
+ const struct snd_emu1010_routing_info *emu_ri =
+ &emu1010_routing_info[emu_idx];
+ const struct snd_emu1010_pads_info *emu_pi = &emu1010_pads_info[emu_idx];
+
+ for (i = 0; i < emu_ri->n_ins; i++)
+ emu->emu1010.input_source[i] =
+ emu1010_map_source(emu_ri, emu_ri->in_dflts[i]);
+ for (i = 0; i < emu_ri->n_outs; i++)
+ emu->emu1010.output_source[i] =
+ emu1010_map_source(emu_ri, emu_ri->out_dflts[i]);
+ scoped_guard(snd_emu1010_fpga_lock, emu) {
+ snd_emu1010_apply_sources(emu);
}
+
+ kctl = emu->ctl_clock_source = snd_ctl_new1(&snd_emu1010_clock_source, emu);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_internal_clock, emu));
+ snd_ctl_new1(&snd_emu1010_clock_fallback, emu));
if (err < 0)
return err;
- } else if (emu->card_capabilities->emu_model) {
- /* all other e-mu cards for now */
- int i;
+ err = add_ctls(emu, &emu1010_adc_pads_ctl,
+ emu_pi->adc_ctls, emu_pi->n_adc_ctls);
+ if (err < 0)
+ return err;
+ err = add_ctls(emu, &emu1010_dac_pads_ctl,
+ emu_pi->dac_ctls, emu_pi->n_dac_ctls);
+ if (err < 0)
+ return err;
- for (i = 0; i < ARRAY_SIZE(snd_emu1010_output_enum_ctls); i++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_output_enum_ctls[i],
- emu));
- if (err < 0)
- return err;
- }
- for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) {
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_input_enum_ctls[i],
- emu));
- if (err < 0)
- return err;
- }
- for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads); i++) {
+ if (!emu->card_capabilities->no_adat) {
err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_adc_pads[i], emu));
+ snd_ctl_new1(&snd_emu1010_optical_out, emu));
if (err < 0)
return err;
- }
- for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads); i++) {
err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
+ snd_ctl_new1(&snd_emu1010_optical_in, emu));
if (err < 0)
return err;
}
- err = snd_ctl_add(card,
- snd_ctl_new1(&snd_emu1010_internal_clock, emu));
+
+ err = add_emu1010_source_mixers(emu);
if (err < 0)
return err;
}
if ( emu->card_capabilities->i2c_adc) {
- int i;
-
err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_i2c_capture_source, emu));
if (err < 0)
return err;
- for (i = 0; i < ARRAY_SIZE(snd_audigy_i2c_volume_ctls); i++) {
- err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_i2c_volume_ctls[i], emu));
- if (err < 0)
- return err;
- }
+ err = add_ctls(emu, &i2c_volume_ctl,
+ snd_audigy_i2c_volume_ctls,
+ ARRAY_SIZE(snd_audigy_i2c_volume_ctls));
+ if (err < 0)
+ return err;
}
if (emu->card_capabilities->ac97_chip && emu->audigy) {
diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c
index bab564824efe..c102a3599225 100644
--- a/sound/pci/emu10k1/emumpu401.c
+++ b/sound/pci/emu10k1/emumpu401.c
@@ -1,22 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of EMU10K1 MPU-401 in UART mode
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/time.h>
@@ -64,7 +49,9 @@ static void mpu401_clear_rx(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *mp
mpu401_read_data(emu, mpu);
#ifdef CONFIG_SND_DEBUG
if (timeout <= 0)
- snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n", mpu401_read_stat(emu, mpu));
+ dev_err(emu->card->dev,
+ "cmd: clear rx timeout (status = 0x%x)\n",
+ mpu401_read_stat(emu, mpu));
#endif
}
@@ -81,28 +68,28 @@ static void do_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, struct snd_emu10k
return;
}
- spin_lock(&midi->input_lock);
- if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
- if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
- mpu401_clear_rx(emu, midi);
- } else {
- byte = mpu401_read_data(emu, midi);
- if (midi->substream_input)
- snd_rawmidi_receive(midi->substream_input, &byte, 1);
+ scoped_guard(spinlock, &midi->input_lock) {
+ if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
+ if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
+ mpu401_clear_rx(emu, midi);
+ } else {
+ byte = mpu401_read_data(emu, midi);
+ if (midi->substream_input)
+ snd_rawmidi_receive(midi->substream_input, &byte, 1);
+ }
}
}
- spin_unlock(&midi->input_lock);
- spin_lock(&midi->output_lock);
- if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
- if (midi->substream_output &&
- snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
- mpu401_write_data(emu, midi, byte);
- } else {
- snd_emu10k1_intr_disable(emu, midi->tx_enable);
+ scoped_guard(spinlock, &midi->output_lock) {
+ if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
+ if (midi->substream_output &&
+ snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
+ mpu401_write_data(emu, midi, byte);
+ } else {
+ snd_emu10k1_intr_disable(emu, midi->tx_enable);
+ }
}
}
- spin_unlock(&midi->output_lock);
}
static void snd_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, unsigned int status)
@@ -117,31 +104,31 @@ static void snd_emu10k1_midi_interrupt2(struct snd_emu10k1 *emu, unsigned int st
static int snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_midi *midi, unsigned char cmd, int ack)
{
- unsigned long flags;
int timeout, ok;
- spin_lock_irqsave(&midi->input_lock, flags);
- mpu401_write_data(emu, midi, 0x00);
- /* mpu401_clear_rx(emu, midi); */
-
- mpu401_write_cmd(emu, midi, cmd);
- if (ack) {
- ok = 0;
- timeout = 10000;
- while (!ok && timeout-- > 0) {
- if (mpu401_input_avail(emu, midi)) {
- if (mpu401_read_data(emu, midi) == MPU401_ACK)
- ok = 1;
+ scoped_guard(spinlock_irq, &midi->input_lock) {
+ mpu401_write_data(emu, midi, 0x00);
+ /* mpu401_clear_rx(emu, midi); */
+
+ mpu401_write_cmd(emu, midi, cmd);
+ if (ack) {
+ ok = 0;
+ timeout = 10000;
+ while (!ok && timeout-- > 0) {
+ if (mpu401_input_avail(emu, midi)) {
+ if (mpu401_read_data(emu, midi) == MPU401_ACK)
+ ok = 1;
+ }
}
- }
- if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
+ if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
+ ok = 1;
+ } else {
ok = 1;
- } else {
- ok = 1;
+ }
}
- spin_unlock_irqrestore(&midi->input_lock, flags);
if (!ok) {
- snd_printk(KERN_ERR "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
+ dev_err(emu->card->dev,
+ "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
cmd, emu->port,
mpu401_read_stat(emu, midi),
mpu401_read_data(emu, midi));
@@ -154,100 +141,78 @@ static int snd_emu10k1_midi_input_open(struct snd_rawmidi_substream *substream)
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
- unsigned long flags;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT;
- midi->substream_input = substream;
- if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
- goto error_out;
- if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
- goto error_out;
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irq, &midi->open_lock) {
+ midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT;
+ midi->substream_input = substream;
+ if (midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)
+ return 0;
}
+ if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
+ return -EIO;
+ if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+ return -EIO;
return 0;
-
-error_out:
- return -EIO;
}
static int snd_emu10k1_midi_output_open(struct snd_rawmidi_substream *substream)
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
- unsigned long flags;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT;
- midi->substream_output = substream;
- if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
- goto error_out;
- if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
- goto error_out;
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irq, &midi->open_lock) {
+ midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT;
+ midi->substream_output = substream;
+ if (midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)
+ return 0;
}
+ if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
+ return -EIO;
+ if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+ return -EIO;
return 0;
-
-error_out:
- return -EIO;
}
static int snd_emu10k1_midi_input_close(struct snd_rawmidi_substream *substream)
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
- unsigned long flags;
- int err = 0;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- snd_emu10k1_intr_disable(emu, midi->rx_enable);
- midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT;
- midi->substream_input = NULL;
- if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irq, &midi->open_lock) {
+ snd_emu10k1_intr_disable(emu, midi->rx_enable);
+ midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT;
+ midi->substream_input = NULL;
+ if (midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)
+ return 0;
}
- return err;
+ return snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
}
static int snd_emu10k1_midi_output_close(struct snd_rawmidi_substream *substream)
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
- unsigned long flags;
- int err = 0;
emu = midi->emu;
if (snd_BUG_ON(!emu))
return -ENXIO;
- spin_lock_irqsave(&midi->open_lock, flags);
- snd_emu10k1_intr_disable(emu, midi->tx_enable);
- midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT;
- midi->substream_output = NULL;
- if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
- spin_unlock_irqrestore(&midi->open_lock, flags);
- err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
- } else {
- spin_unlock_irqrestore(&midi->open_lock, flags);
+ scoped_guard(spinlock_irq, &midi->open_lock) {
+ snd_emu10k1_intr_disable(emu, midi->tx_enable);
+ midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT;
+ midi->substream_output = NULL;
+ if (midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)
+ return 0;
}
- return err;
+ return snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
}
static void snd_emu10k1_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
@@ -268,7 +233,6 @@ static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substr
{
struct snd_emu10k1 *emu;
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
- unsigned long flags;
emu = midi->emu;
if (snd_BUG_ON(!emu))
@@ -279,22 +243,21 @@ static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substr
unsigned char byte;
/* try to send some amount of bytes here before interrupts */
- spin_lock_irqsave(&midi->output_lock, flags);
- while (max > 0) {
- if (mpu401_output_ready(emu, midi)) {
- if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) ||
- snd_rawmidi_transmit(substream, &byte, 1) != 1) {
- /* no more data */
- spin_unlock_irqrestore(&midi->output_lock, flags);
- return;
+ scoped_guard(spinlock_irq, &midi->output_lock) {
+ while (max > 0) {
+ if (mpu401_output_ready(emu, midi)) {
+ if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) ||
+ snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+ /* no more data */
+ return;
+ }
+ mpu401_write_data(emu, midi, byte);
+ max--;
+ } else {
+ break;
}
- mpu401_write_data(emu, midi, byte);
- max--;
- } else {
- break;
}
}
- spin_unlock_irqrestore(&midi->output_lock, flags);
snd_emu10k1_intr_enable(emu, midi->tx_enable);
} else {
snd_emu10k1_intr_disable(emu, midi->tx_enable);
@@ -305,14 +268,14 @@ static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substr
*/
-static struct snd_rawmidi_ops snd_emu10k1_midi_output =
+static const struct snd_rawmidi_ops snd_emu10k1_midi_output =
{
.open = snd_emu10k1_midi_output_open,
.close = snd_emu10k1_midi_output_close,
.trigger = snd_emu10k1_midi_output_trigger,
};
-static struct snd_rawmidi_ops snd_emu10k1_midi_input =
+static const struct snd_rawmidi_ops snd_emu10k1_midi_input =
{
.open = snd_emu10k1_midi_input_open,
.close = snd_emu10k1_midi_input_close,
@@ -326,18 +289,19 @@ static void snd_emu10k1_midi_free(struct snd_rawmidi *rmidi)
midi->rmidi = NULL;
}
-static int __devinit emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, int device, char *name)
+static int emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, int device, char *name)
{
struct snd_rawmidi *rmidi;
int err;
- if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0)
+ err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi);
+ if (err < 0)
return err;
midi->emu = emu;
spin_lock_init(&midi->open_lock);
spin_lock_init(&midi->input_lock);
spin_lock_init(&midi->output_lock);
- strcpy(rmidi->name, name);
+ strscpy(rmidi->name, name);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
@@ -349,12 +313,13 @@ static int __devinit emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10
return 0;
}
-int __devinit snd_emu10k1_midi(struct snd_emu10k1 *emu)
+int snd_emu10k1_midi(struct snd_emu10k1 *emu)
{
struct snd_emu10k1_midi *midi = &emu->midi;
int err;
- if ((err = emu10k1_midi_init(emu, midi, 0, "EMU10K1 MPU-401 (UART)")) < 0)
+ err = emu10k1_midi_init(emu, midi, 0, "EMU10K1 MPU-401 (UART)");
+ if (err < 0)
return err;
midi->tx_enable = INTE_MIDITXENABLE;
@@ -366,13 +331,14 @@ int __devinit snd_emu10k1_midi(struct snd_emu10k1 *emu)
return 0;
}
-int __devinit snd_emu10k1_audigy_midi(struct snd_emu10k1 *emu)
+int snd_emu10k1_audigy_midi(struct snd_emu10k1 *emu)
{
struct snd_emu10k1_midi *midi;
int err;
midi = &emu->midi;
- if ((err = emu10k1_midi_init(emu, midi, 0, "Audigy MPU-401 (UART)")) < 0)
+ err = emu10k1_midi_init(emu, midi, 0, "Audigy MPU-401 (UART)");
+ if (err < 0)
return err;
midi->tx_enable = INTE_MIDITXENABLE;
@@ -383,7 +349,8 @@ int __devinit snd_emu10k1_audigy_midi(struct snd_emu10k1 *emu)
midi->interrupt = snd_emu10k1_midi_interrupt;
midi = &emu->midi2;
- if ((err = emu10k1_midi_init(emu, midi, 1, "Audigy MPU-401 #2")) < 0)
+ err = emu10k1_midi_init(emu, midi, 1, "Audigy MPU-401 #2");
+ if (err < 0)
return err;
midi->tx_enable = INTE_A_MIDITXENABLE2;
diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
index 622bace148e3..071c75ba81fd 100644
--- a/sound/pci/emu10k1/emupcm.c
+++ b/sound/pci/emu10k1/emupcm.c
@@ -1,29 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * Lee Revell <rlrevell@joe-job.com>
+ * James Courtier-Dutton <James@superbug.co.uk>
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
* Creative Labs, Inc.
- * Routines for control of EMU10K1 chips / PCM routines
- * Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com>
- *
- * BUGS:
- * --
- *
- * TODO:
- * --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
+ * Routines for control of EMU10K1 chips / PCM routines
*/
#include <linux/pci.h>
@@ -39,12 +22,14 @@ static void snd_emu10k1_pcm_interrupt(struct snd_emu10k1 *emu,
{
struct snd_emu10k1_pcm *epcm;
- if ((epcm = voice->epcm) == NULL)
+ epcm = voice->epcm;
+ if (!epcm)
return;
if (epcm->substream == NULL)
return;
#if 0
- printk(KERN_DEBUG "IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
+ dev_dbg(emu->card->dev,
+ "IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
epcm->substream->runtime->hw->pointer(emu, epcm->substream),
snd_pcm_lib_period_bytes(epcm->substream),
snd_pcm_lib_buffer_bytes(epcm->substream));
@@ -88,82 +73,64 @@ static void snd_emu10k1_pcm_efx_interrupt(struct snd_emu10k1 *emu,
snd_pcm_period_elapsed(emu->pcm_capture_efx_substream);
}
-static snd_pcm_uframes_t snd_emu10k1_efx_playback_pointer(struct snd_pcm_substream *substream)
+static void snd_emu10k1_pcm_free_voices(struct snd_emu10k1_pcm *epcm)
{
- struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_emu10k1_pcm *epcm = runtime->private_data;
- unsigned int ptr;
-
- if (!epcm->running)
- return 0;
- ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff;
- ptr += runtime->buffer_size;
- ptr -= epcm->ccca_start_addr;
- ptr %= runtime->buffer_size;
-
- return ptr;
-}
-
-static int snd_emu10k1_pcm_channel_alloc(struct snd_emu10k1_pcm * epcm, int voices)
-{
- int err, i;
-
- if (epcm->voices[1] != NULL && voices < 2) {
- snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]);
- epcm->voices[1] = NULL;
- }
- for (i = 0; i < voices; i++) {
- if (epcm->voices[i] == NULL)
- break;
- }
- if (i == voices)
- return 0; /* already allocated */
-
- for (i = 0; i < ARRAY_SIZE(epcm->voices); i++) {
+ for (unsigned i = 0; i < ARRAY_SIZE(epcm->voices); i++) {
if (epcm->voices[i]) {
snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
epcm->voices[i] = NULL;
}
}
+}
+
+static int snd_emu10k1_pcm_channel_alloc(struct snd_emu10k1_pcm *epcm,
+ int type, int count, int channels)
+{
+ int err;
+
+ snd_emu10k1_pcm_free_voices(epcm);
+
err = snd_emu10k1_voice_alloc(epcm->emu,
- epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX,
- voices,
- &epcm->voices[0]);
-
+ type, count, channels,
+ epcm, &epcm->voices[0]);
if (err < 0)
return err;
- epcm->voices[0]->epcm = epcm;
- if (voices > 1) {
- for (i = 1; i < voices; i++) {
- epcm->voices[i] = &epcm->emu->voices[epcm->voices[0]->number + i];
- epcm->voices[i]->epcm = epcm;
- }
- }
+
if (epcm->extra == NULL) {
+ // The hardware supports only (half-)loop interrupts, so to support an
+ // arbitrary number of periods per buffer, we use an extra voice with a
+ // period-sized loop as the interrupt source. Additionally, the interrupt
+ // timing of the hardware is "suboptimal" and needs some compensation.
err = snd_emu10k1_voice_alloc(epcm->emu,
- epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX,
- 1,
- &epcm->extra);
+ type + 1, 1, 1,
+ epcm, &epcm->extra);
if (err < 0) {
/*
- printk(KERN_DEBUG "pcm_channel_alloc: "
+ dev_dbg(emu->card->dev, "pcm_channel_alloc: "
"failed extra: voices=%d, frame=%d\n",
voices, frame);
*/
- for (i = 0; i < voices; i++) {
- snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
- epcm->voices[i] = NULL;
- }
+ snd_emu10k1_pcm_free_voices(epcm);
return err;
}
- epcm->extra->epcm = epcm;
epcm->extra->interrupt = snd_emu10k1_pcm_interrupt;
}
+
return 0;
}
-static unsigned int capture_period_sizes[31] = {
+// Primes 2-7 and 2^n multiples thereof, up to 16.
+static const unsigned int efx_capture_channels[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16
+};
+
+static const struct snd_pcm_hw_constraint_list hw_constraints_efx_capture_channels = {
+ .count = ARRAY_SIZE(efx_capture_channels),
+ .list = efx_capture_channels,
+ .mask = 0
+};
+
+static const unsigned int capture_buffer_sizes[31] = {
384, 448, 512, 640,
384*2, 448*2, 512*2, 640*2,
384*4, 448*4, 512*4, 640*4,
@@ -174,19 +141,9 @@ static unsigned int capture_period_sizes[31] = {
384*128,448*128,512*128
};
-static struct snd_pcm_hw_constraint_list hw_constraints_capture_period_sizes = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_capture_buffer_sizes = {
.count = 31,
- .list = capture_period_sizes,
- .mask = 0
-};
-
-static unsigned int capture_rates[8] = {
- 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000
-};
-
-static struct snd_pcm_hw_constraint_list hw_constraints_capture_rates = {
- .count = 8,
- .list = capture_rates,
+ .list = capture_buffer_sizes,
.mask = 0
};
@@ -212,7 +169,7 @@ static unsigned int snd_emu10k1_audigy_capture_rate_reg(unsigned int rate)
switch (rate) {
case 8000: return A_ADCCR_SAMPLERATE_8;
case 11025: return A_ADCCR_SAMPLERATE_11;
- case 12000: return A_ADCCR_SAMPLERATE_12; /* really supported? */
+ case 12000: return A_ADCCR_SAMPLERATE_12;
case 16000: return ADCCR_SAMPLERATE_16;
case 22050: return ADCCR_SAMPLERATE_22;
case 24000: return ADCCR_SAMPLERATE_24;
@@ -225,6 +182,33 @@ static unsigned int snd_emu10k1_audigy_capture_rate_reg(unsigned int rate)
}
}
+static void snd_emu10k1_constrain_capture_rates(struct snd_emu10k1 *emu,
+ struct snd_pcm_runtime *runtime)
+{
+ if (emu->card_capabilities->emu_model &&
+ emu->emu1010.word_clock == 44100) {
+ runtime->hw.rates = SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_44100;
+ runtime->hw.rate_min = 11025;
+ runtime->hw.rate_max = 44100;
+ } else if (emu->audigy) {
+ runtime->hw.rates = SNDRV_PCM_RATE_8000_48000 |
+ SNDRV_PCM_RATE_12000 |
+ SNDRV_PCM_RATE_24000;
+ }
+}
+
+static void snd_emu1010_constrain_efx_rate(struct snd_emu10k1 *emu,
+ struct snd_pcm_runtime *runtime)
+{
+ int rate;
+
+ rate = emu->emu1010.word_clock;
+ runtime->hw.rate_min = runtime->hw.rate_max = rate;
+ runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate);
+}
+
static unsigned int emu10k1_calc_pitch_target(unsigned int rate)
{
unsigned int pitch_target;
@@ -261,147 +245,104 @@ static unsigned int emu10k1_select_interprom(unsigned int pitch_target)
return CCCA_INTERPROM_2;
}
-/*
- * calculate cache invalidate size
- *
- * stereo: channel is stereo
- * w_16: using 16bit samples
- *
- * returns: cache invalidate size in samples
- */
-static inline int emu10k1_ccis(int stereo, int w_16)
+static u16 emu10k1_send_target_from_amount(u8 amount)
{
- if (w_16) {
- return stereo ? 24 : 26;
- } else {
- return stereo ? 24*2 : 26*2;
- }
+ static const u8 shifts[8] = { 4, 4, 5, 6, 7, 8, 9, 10 };
+ static const u16 offsets[8] = { 0, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000 };
+ u8 exp;
+
+ if (amount == 0xff)
+ return 0xffff;
+ exp = amount >> 5;
+ return ((amount & 0x1f) << shifts[exp]) + offsets[exp];
}
static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
- int master, int extra,
struct snd_emu10k1_voice *evoice,
+ bool w_16, bool stereo,
unsigned int start_addr,
unsigned int end_addr,
- struct snd_emu10k1_pcm_mixer *mix)
+ const unsigned char *send_routing,
+ const unsigned char *send_amount)
{
- struct snd_pcm_substream *substream = evoice->epcm->substream;
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int silent_page, tmp;
- int voice, stereo, w_16;
- unsigned char attn, send_amount[8];
- unsigned char send_routing[8];
- unsigned long flags;
- unsigned int pitch_target;
- unsigned int ccis;
+ unsigned int silent_page;
+ int voice;
voice = evoice->number;
- stereo = runtime->channels == 2;
- w_16 = snd_pcm_format_width(runtime->format) == 16;
- if (!extra && stereo) {
- start_addr >>= 1;
- end_addr >>= 1;
- }
- if (w_16) {
- start_addr >>= 1;
- end_addr >>= 1;
- }
-
- spin_lock_irqsave(&emu->reg_lock, flags);
-
- /* volume parameters */
- if (extra) {
- attn = 0;
- memset(send_routing, 0, sizeof(send_routing));
- send_routing[0] = 0;
- send_routing[1] = 1;
- send_routing[2] = 2;
- send_routing[3] = 3;
- memset(send_amount, 0, sizeof(send_amount));
+ silent_page = ((unsigned int)emu->silent_page.addr << emu->address_mode) |
+ (emu->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
+ snd_emu10k1_ptr_write_multiple(emu, voice,
+ // Not really necessary for the slave, but it doesn't hurt
+ CPF, stereo ? CPF_STEREO_MASK : 0,
+ // Assumption that PT is already 0 so no harm overwriting
+ PTRX, (send_amount[0] << 8) | send_amount[1],
+ // Stereo slaves don't need to have the addresses set, but it doesn't hurt
+ DSL, end_addr | (send_amount[3] << 24),
+ PSST, start_addr | (send_amount[2] << 24),
+ CCCA, emu10k1_select_interprom(evoice->epcm->pitch_target) |
+ (w_16 ? 0 : CCCA_8BITSELECT),
+ // Clear filter delay memory
+ Z1, 0,
+ Z2, 0,
+ // Invalidate maps
+ MAPA, silent_page,
+ MAPB, silent_page,
+ // Disable filter (in conjunction with CCCA_RESONANCE == 0)
+ VTFT, VTFT_FILTERTARGET_MASK,
+ CVCF, CVCF_CURRENTFILTER_MASK,
+ REGLIST_END);
+ // Setup routing
+ if (emu->audigy) {
+ snd_emu10k1_ptr_write_multiple(emu, voice,
+ A_FXRT1, snd_emu10k1_compose_audigy_fxrt1(send_routing),
+ A_FXRT2, snd_emu10k1_compose_audigy_fxrt2(send_routing),
+ A_SENDAMOUNTS, snd_emu10k1_compose_audigy_sendamounts(send_amount),
+ REGLIST_END);
+ for (int i = 0; i < 4; i++) {
+ u32 aml = emu10k1_send_target_from_amount(send_amount[2 * i]);
+ u32 amh = emu10k1_send_target_from_amount(send_amount[2 * i + 1]);
+ snd_emu10k1_ptr_write(emu, A_CSBA + i, voice, (amh << 16) | aml);
+ }
} else {
- /* mono, left, right (master voice = left) */
- tmp = stereo ? (master ? 1 : 2) : 0;
- memcpy(send_routing, &mix->send_routing[tmp][0], 8);
- memcpy(send_amount, &mix->send_volume[tmp][0], 8);
+ snd_emu10k1_ptr_write(emu, FXRT, voice,
+ snd_emu10k1_compose_send_routing(send_routing));
}
- ccis = emu10k1_ccis(stereo, w_16);
-
- if (master) {
- evoice->epcm->ccca_start_addr = start_addr + ccis;
- if (extra) {
- start_addr += ccis;
- end_addr += ccis + emu->delay_pcm_irq;
- }
- if (stereo && !extra) {
- snd_emu10k1_ptr_write(emu, CPF, voice, CPF_STEREO_MASK);
- snd_emu10k1_ptr_write(emu, CPF, (voice + 1), CPF_STEREO_MASK);
- } else {
- snd_emu10k1_ptr_write(emu, CPF, voice, 0);
- }
- }
+ emu->voices[voice].dirty = 1;
+}
- /* setup routing */
- if (emu->audigy) {
- snd_emu10k1_ptr_write(emu, A_FXRT1, voice,
- snd_emu10k1_compose_audigy_fxrt1(send_routing));
- snd_emu10k1_ptr_write(emu, A_FXRT2, voice,
- snd_emu10k1_compose_audigy_fxrt2(send_routing));
- snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice,
- ((unsigned int)send_amount[4] << 24) |
- ((unsigned int)send_amount[5] << 16) |
- ((unsigned int)send_amount[6] << 8) |
- (unsigned int)send_amount[7]);
- } else
- snd_emu10k1_ptr_write(emu, FXRT, voice,
- snd_emu10k1_compose_send_routing(send_routing));
- /* Stop CA */
- /* Assumption that PT is already 0 so no harm overwriting */
- snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]);
- snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24));
- snd_emu10k1_ptr_write(emu, PSST, voice,
- (start_addr + (extra ? emu->delay_pcm_irq : 0)) |
- (send_amount[2] << 24));
- if (emu->card_capabilities->emu_model)
- pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */
- else
- pitch_target = emu10k1_calc_pitch_target(runtime->rate);
- if (extra)
- snd_emu10k1_ptr_write(emu, CCCA, voice, start_addr |
- emu10k1_select_interprom(pitch_target) |
- (w_16 ? 0 : CCCA_8BITSELECT));
- else
- snd_emu10k1_ptr_write(emu, CCCA, voice, (start_addr + ccis) |
- emu10k1_select_interprom(pitch_target) |
- (w_16 ? 0 : CCCA_8BITSELECT));
- /* Clear filter delay memory */
- snd_emu10k1_ptr_write(emu, Z1, voice, 0);
- snd_emu10k1_ptr_write(emu, Z2, voice, 0);
- /* invalidate maps */
- silent_page = ((unsigned int)emu->silent_page.addr << 1) | MAP_PTI_MASK;
- snd_emu10k1_ptr_write(emu, MAPA, voice, silent_page);
- snd_emu10k1_ptr_write(emu, MAPB, voice, silent_page);
- /* modulation envelope */
- snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff);
- snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff);
- snd_emu10k1_ptr_write(emu, ATKHLDM, voice, 0);
- snd_emu10k1_ptr_write(emu, DCYSUSM, voice, 0x007f);
- snd_emu10k1_ptr_write(emu, LFOVAL1, voice, 0x8000);
- snd_emu10k1_ptr_write(emu, LFOVAL2, voice, 0x8000);
- snd_emu10k1_ptr_write(emu, FMMOD, voice, 0);
- snd_emu10k1_ptr_write(emu, TREMFRQ, voice, 0);
- snd_emu10k1_ptr_write(emu, FM2FRQ2, voice, 0);
- snd_emu10k1_ptr_write(emu, ENVVAL, voice, 0x8000);
- /* volume envelope */
- snd_emu10k1_ptr_write(emu, ATKHLDV, voice, 0x7f7f);
- snd_emu10k1_ptr_write(emu, ENVVOL, voice, 0x0000);
- /* filter envelope */
- snd_emu10k1_ptr_write(emu, PEFE_FILTERAMOUNT, voice, 0x7f);
- /* pitch envelope */
- snd_emu10k1_ptr_write(emu, PEFE_PITCHAMOUNT, voice, 0);
-
- spin_unlock_irqrestore(&emu->reg_lock, flags);
+static void snd_emu10k1_pcm_init_voices(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *evoice,
+ bool w_16, bool stereo,
+ unsigned int start_addr,
+ unsigned int end_addr,
+ struct snd_emu10k1_pcm_mixer *mix)
+{
+ guard(spinlock_irq)(&emu->reg_lock);
+ snd_emu10k1_pcm_init_voice(emu, evoice, w_16, stereo,
+ start_addr, end_addr,
+ &mix->send_routing[stereo][0],
+ &mix->send_volume[stereo][0]);
+ if (stereo)
+ snd_emu10k1_pcm_init_voice(emu, evoice + 1, w_16, true,
+ start_addr, end_addr,
+ &mix->send_routing[2][0],
+ &mix->send_volume[2][0]);
+}
+
+static void snd_emu10k1_pcm_init_extra_voice(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *evoice,
+ bool w_16,
+ unsigned int start_addr,
+ unsigned int end_addr)
+{
+ static const unsigned char send_routing[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
+ static const unsigned char send_amount[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ snd_emu10k1_pcm_init_voice(emu, evoice, w_16, false,
+ start_addr, end_addr,
+ send_routing, send_amount);
}
static int snd_emu10k1_playback_hw_params(struct snd_pcm_substream *substream,
@@ -410,12 +351,31 @@ static int snd_emu10k1_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_emu10k1_pcm *epcm = runtime->private_data;
+ size_t alloc_size;
+ int type, channels, count;
int err;
- if ((err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params))) < 0)
+ if (epcm->type == PLAYBACK_EMUVOICE) {
+ type = EMU10K1_PCM;
+ channels = 1;
+ count = params_channels(hw_params);
+ } else {
+ type = EMU10K1_EFX;
+ channels = params_channels(hw_params);
+ count = 1;
+ }
+ err = snd_emu10k1_pcm_channel_alloc(epcm, type, count, channels);
+ if (err < 0)
return err;
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+
+ alloc_size = params_buffer_bytes(hw_params);
+ if (emu->iommu_workaround)
+ alloc_size += EMUPAGESIZE;
+ err = snd_pcm_lib_malloc_pages(substream, alloc_size);
+ if (err < 0)
return err;
+ if (emu->iommu_workaround && runtime->dma_bytes >= EMUPAGESIZE)
+ runtime->dma_bytes -= EMUPAGESIZE;
if (err > 0) { /* change */
int mapped;
if (epcm->memblk != NULL)
@@ -445,43 +405,7 @@ static int snd_emu10k1_playback_hw_free(struct snd_pcm_substream *substream)
snd_emu10k1_voice_free(epcm->emu, epcm->extra);
epcm->extra = NULL;
}
- if (epcm->voices[1]) {
- snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]);
- epcm->voices[1] = NULL;
- }
- if (epcm->voices[0]) {
- snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]);
- epcm->voices[0] = NULL;
- }
- if (epcm->memblk) {
- snd_emu10k1_free_pages(emu, epcm->memblk);
- epcm->memblk = NULL;
- epcm->start_addr = 0;
- }
- snd_pcm_lib_free_pages(substream);
- return 0;
-}
-
-static int snd_emu10k1_efx_playback_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_emu10k1_pcm *epcm;
- int i;
-
- if (runtime->private_data == NULL)
- return 0;
- epcm = runtime->private_data;
- if (epcm->extra) {
- snd_emu10k1_voice_free(epcm->emu, epcm->extra);
- epcm->extra = NULL;
- }
- for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
- if (epcm->voices[i]) {
- snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
- epcm->voices[i] = NULL;
- }
- }
+ snd_emu10k1_pcm_free_voices(epcm);
if (epcm->memblk) {
snd_emu10k1_free_pages(emu, epcm->memblk);
epcm->memblk = NULL;
@@ -496,26 +420,28 @@ static int snd_emu10k1_playback_prepare(struct snd_pcm_substream *substream)
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_emu10k1_pcm *epcm = runtime->private_data;
+ bool w_16 = snd_pcm_format_width(runtime->format) == 16;
+ bool stereo = runtime->channels == 2;
unsigned int start_addr, end_addr;
+ unsigned int rate;
+
+ rate = runtime->rate;
+ if (emu->card_capabilities->emu_model &&
+ emu->emu1010.word_clock == 44100)
+ rate = rate * 480 / 441;
+ epcm->pitch_target = emu10k1_calc_pitch_target(rate);
+
+ start_addr = epcm->start_addr >> w_16;
+ end_addr = start_addr + runtime->period_size;
+ snd_emu10k1_pcm_init_extra_voice(emu, epcm->extra, w_16,
+ start_addr, end_addr);
+ start_addr >>= stereo;
+ epcm->ccca_start_addr = start_addr;
+ end_addr = start_addr + runtime->buffer_size;
+ snd_emu10k1_pcm_init_voices(emu, epcm->voices[0], w_16, stereo,
+ start_addr, end_addr,
+ &emu->pcm_mixer[substream->number]);
- start_addr = epcm->start_addr;
- end_addr = snd_pcm_lib_period_bytes(substream);
- if (runtime->channels == 2) {
- start_addr >>= 1;
- end_addr >>= 1;
- }
- end_addr += start_addr;
- snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra,
- start_addr, end_addr, NULL);
- start_addr = epcm->start_addr;
- end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream);
- snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0],
- start_addr, end_addr,
- &emu->pcm_mixer[substream->number]);
- if (epcm->voices[1])
- snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[1],
- start_addr, end_addr,
- &emu->pcm_mixer[substream->number]);
return 0;
}
@@ -524,38 +450,32 @@ static int snd_emu10k1_efx_playback_prepare(struct snd_pcm_substream *substream)
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_emu10k1_pcm *epcm = runtime->private_data;
- unsigned int start_addr, end_addr;
- unsigned int channel_size;
- int i;
+ unsigned int start_addr;
+ unsigned int extra_size, channel_size;
+ unsigned int i;
- start_addr = epcm->start_addr;
- end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream);
+ epcm->pitch_target = PITCH_48000;
- /*
- * the kX driver leaves some space between voices
- */
- channel_size = ( end_addr - start_addr ) / NUM_EFX_PLAYBACK;
-
- snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra,
- start_addr, start_addr + (channel_size / 2), NULL);
-
- /* only difference with the master voice is we use it for the pointer */
- snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0],
- start_addr, start_addr + channel_size,
- &emu->efx_pcm_mixer[0]);
-
- start_addr += channel_size;
- for (i = 1; i < NUM_EFX_PLAYBACK; i++) {
- snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[i],
- start_addr, start_addr + channel_size,
- &emu->efx_pcm_mixer[i]);
+ start_addr = epcm->start_addr >> 1; // 16-bit voices
+
+ extra_size = runtime->period_size;
+ channel_size = runtime->buffer_size;
+
+ snd_emu10k1_pcm_init_extra_voice(emu, epcm->extra, true,
+ start_addr, start_addr + extra_size);
+
+ epcm->ccca_start_addr = start_addr;
+ for (i = 0; i < runtime->channels; i++) {
+ snd_emu10k1_pcm_init_voices(emu, epcm->voices[i], true, false,
+ start_addr, start_addr + channel_size,
+ &emu->efx_pcm_mixer[i]);
start_addr += channel_size;
}
return 0;
}
-static struct snd_pcm_hardware snd_emu10k1_efx_playback =
+static const struct snd_pcm_hardware snd_emu10k1_efx_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_NONINTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -565,27 +485,15 @@ static struct snd_pcm_hardware snd_emu10k1_efx_playback =
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
.rate_max = 48000,
- .channels_min = NUM_EFX_PLAYBACK,
+ .channels_min = 1,
.channels_max = NUM_EFX_PLAYBACK,
- .buffer_bytes_max = (64*1024),
- .period_bytes_min = 64,
- .period_bytes_max = (64*1024),
+ .buffer_bytes_max = (128*1024),
+ .period_bytes_max = (128*1024),
.periods_min = 2,
- .periods_max = 2,
+ .periods_max = 1024,
.fifo_size = 0,
};
-static int snd_emu10k1_capture_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_emu10k1_capture_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_emu10k1_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
@@ -600,9 +508,17 @@ static int snd_emu10k1_capture_prepare(struct snd_pcm_substream *substream)
snd_emu10k1_ptr_write(emu, ADCCR, 0, 0);
break;
case CAPTURE_EFX:
+ if (emu->card_capabilities->emu_model) {
+ // The upper 32 16-bit capture voices, two for each of the 16 32-bit channels.
+ // The lower voices are occupied by A_EXTOUT_*_CAP*.
+ epcm->capture_cr_val = 0;
+ epcm->capture_cr_val2 = 0xffffffff >> (32 - runtime->channels * 2);
+ }
if (emu->audigy) {
- snd_emu10k1_ptr_write(emu, A_FXWC1, 0, 0);
- snd_emu10k1_ptr_write(emu, A_FXWC2, 0, 0);
+ snd_emu10k1_ptr_write_multiple(emu, 0,
+ A_FXWC1, 0,
+ A_FXWC2, 0,
+ REGLIST_END);
} else
snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
break;
@@ -613,7 +529,7 @@ static int snd_emu10k1_capture_prepare(struct snd_pcm_substream *substream)
epcm->capture_bufsize = snd_pcm_lib_buffer_bytes(substream);
epcm->capture_bs_val = 0;
for (idx = 0; idx < 31; idx++) {
- if (capture_period_sizes[idx] == epcm->capture_bufsize) {
+ if (capture_buffer_sizes[idx] == epcm->capture_bufsize) {
epcm->capture_bs_val = idx + 1;
break;
}
@@ -623,132 +539,181 @@ static int snd_emu10k1_capture_prepare(struct snd_pcm_substream *substream)
epcm->capture_bs_val++;
}
if (epcm->type == CAPTURE_AC97ADC) {
+ unsigned rate = runtime->rate;
+ if (!(runtime->hw.rates & SNDRV_PCM_RATE_48000))
+ rate = rate * 480 / 441;
+
epcm->capture_cr_val = emu->audigy ? A_ADCCR_LCHANENABLE : ADCCR_LCHANENABLE;
if (runtime->channels > 1)
epcm->capture_cr_val |= emu->audigy ? A_ADCCR_RCHANENABLE : ADCCR_RCHANENABLE;
epcm->capture_cr_val |= emu->audigy ?
- snd_emu10k1_audigy_capture_rate_reg(runtime->rate) :
- snd_emu10k1_capture_rate_reg(runtime->rate);
+ snd_emu10k1_audigy_capture_rate_reg(rate) :
+ snd_emu10k1_capture_rate_reg(rate);
}
return 0;
}
-static void snd_emu10k1_playback_invalidate_cache(struct snd_emu10k1 *emu, int extra, struct snd_emu10k1_voice *evoice)
+static void snd_emu10k1_playback_fill_cache(struct snd_emu10k1 *emu,
+ unsigned voice,
+ u32 sample, bool stereo)
{
- struct snd_pcm_runtime *runtime;
- unsigned int voice, stereo, i, ccis, cra = 64, cs, sample;
+ u32 ccr;
- if (evoice == NULL)
- return;
- runtime = evoice->epcm->substream->runtime;
- voice = evoice->number;
- stereo = (!extra && runtime->channels == 2);
- sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080;
- ccis = emu10k1_ccis(stereo, sample == 0);
- /* set cs to 2 * number of cache registers beside the invalidated */
- cs = (sample == 0) ? (32-ccis) : (64-ccis+1) >> 1;
- if (cs > 16) cs = 16;
- for (i = 0; i < cs; i++) {
+ // We assume that the cache is resting at this point (i.e.,
+ // CCR_CACHEINVALIDSIZE is very small).
+
+ // Clear leading frames. For simplicitly, this does too much,
+ // except for 16-bit stereo. And the interpolator will actually
+ // access them at all only when we're pitch-shifting.
+ for (int i = 0; i < 3; i++)
snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample);
- if (stereo) {
- snd_emu10k1_ptr_write(emu, CD0 + i, voice + 1, sample);
- }
- }
- /* reset cache */
- snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, 0);
- snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice, cra);
+
+ // Fill cache
+ ccr = (64 - 3) << REG_SHIFT(CCR_CACHEINVALIDSIZE);
if (stereo) {
- snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice + 1, 0);
- snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice + 1, cra);
+ // The engine goes haywire if CCR_READADDRESS is out of sync
+ snd_emu10k1_ptr_write(emu, CCR, voice + 1, ccr);
}
- /* fill cache */
- snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, ccis);
- if (stereo) {
- snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice+1, ccis);
+ snd_emu10k1_ptr_write(emu, CCR, voice, ccr);
+}
+
+static void snd_emu10k1_playback_prepare_voices(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_pcm *epcm,
+ bool w_16, bool stereo,
+ int channels)
+{
+ struct snd_pcm_substream *substream = epcm->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned eloop_start = epcm->start_addr >> w_16;
+ unsigned loop_start = eloop_start >> stereo;
+ unsigned eloop_size = runtime->period_size;
+ unsigned loop_size = runtime->buffer_size;
+ u32 sample = w_16 ? 0 : 0x80808080;
+
+ // To make the playback actually start at the 1st frame,
+ // we need to compensate for two circumstances:
+ // - The actual position is delayed by the cache size (64 frames)
+ // - The interpolator is centered around the 4th frame
+ loop_start += (epcm->resume_pos + 64 - 3) % loop_size;
+ for (int i = 0; i < channels; i++) {
+ unsigned voice = epcm->voices[i]->number;
+ snd_emu10k1_ptr_write(emu, CCCA_CURRADDR, voice, loop_start);
+ loop_start += loop_size;
+ snd_emu10k1_playback_fill_cache(emu, voice, sample, stereo);
}
+
+ // The interrupt is triggered when CCCA_CURRADDR (CA) wraps around,
+ // which is ahead of the actual playback position, so the interrupt
+ // source needs to be delayed.
+ //
+ // In principle, this wouldn't need to be the cache's entire size - in
+ // practice, CCR_CACHEINVALIDSIZE (CIS) > `fetch threshold` has never
+ // been observed, and assuming 40 _bytes_ should be safe.
+ //
+ // The cache fills are somewhat random, which makes it impossible to
+ // align them with the interrupts. This makes a non-delayed interrupt
+ // source not practical, as the interrupt handler would have to wait
+ // for (CA - CIS) >= period_boundary for every channel in the stream.
+ //
+ // This is why all other (open) drivers for these chips use timer-based
+ // interrupts.
+ //
+ eloop_start += (epcm->resume_pos + eloop_size - 3) % eloop_size;
+ snd_emu10k1_ptr_write(emu, CCCA_CURRADDR, epcm->extra->number, eloop_start);
+
+ // It takes a moment until the cache fills complete,
+ // but the unmuting takes long enough for that.
}
-static void snd_emu10k1_playback_prepare_voice(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *evoice,
- int master, int extra,
- struct snd_emu10k1_pcm_mixer *mix)
+static void snd_emu10k1_playback_commit_volume(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *evoice,
+ unsigned int vattn)
{
- struct snd_pcm_substream *substream;
- struct snd_pcm_runtime *runtime;
- unsigned int attn, vattn;
- unsigned int voice, tmp;
+ snd_emu10k1_ptr_write_multiple(emu, evoice->number,
+ VTFT, vattn | VTFT_FILTERTARGET_MASK,
+ CVCF, vattn | CVCF_CURRENTFILTER_MASK,
+ REGLIST_END);
+}
- if (evoice == NULL) /* skip second voice for mono */
- return;
- substream = evoice->epcm->substream;
- runtime = substream->runtime;
- voice = evoice->number;
+static void snd_emu10k1_playback_unmute_voice(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *evoice,
+ bool stereo, bool master,
+ struct snd_emu10k1_pcm_mixer *mix)
+{
+ unsigned int vattn;
+ unsigned int tmp;
- attn = extra ? 0 : 0x00ff;
- tmp = runtime->channels == 2 ? (master ? 1 : 2) : 0;
- vattn = mix != NULL ? (mix->attn[tmp] << 16) : 0;
- snd_emu10k1_ptr_write(emu, IFATN, voice, attn);
- snd_emu10k1_ptr_write(emu, VTFT, voice, vattn | 0xffff);
- snd_emu10k1_ptr_write(emu, CVCF, voice, vattn | 0xffff);
- snd_emu10k1_ptr_write(emu, DCYSUSV, voice, 0x7f7f);
- snd_emu10k1_voice_clear_loop_stop(emu, voice);
+ tmp = stereo ? (master ? 1 : 2) : 0;
+ vattn = mix->attn[tmp] << 16;
+ snd_emu10k1_playback_commit_volume(emu, evoice, vattn);
}
-static void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *evoice, int master, int extra)
+static void snd_emu10k1_playback_unmute_voices(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *evoice,
+ bool stereo,
+ struct snd_emu10k1_pcm_mixer *mix)
{
- struct snd_pcm_substream *substream;
- struct snd_pcm_runtime *runtime;
- unsigned int voice, pitch, pitch_target;
+ snd_emu10k1_playback_unmute_voice(emu, evoice, stereo, true, mix);
+ if (stereo)
+ snd_emu10k1_playback_unmute_voice(emu, evoice + 1, true, false, mix);
+}
- if (evoice == NULL) /* skip second voice for mono */
- return;
- substream = evoice->epcm->substream;
- runtime = substream->runtime;
- voice = evoice->number;
+static void snd_emu10k1_playback_mute_voice(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *evoice)
+{
+ snd_emu10k1_playback_commit_volume(emu, evoice, 0);
+}
- pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8;
- if (emu->card_capabilities->emu_model)
- pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */
- else
- pitch_target = emu10k1_calc_pitch_target(runtime->rate);
- snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, pitch_target);
- if (master || evoice->epcm->type == PLAYBACK_EFX)
- snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, pitch_target);
- snd_emu10k1_ptr_write(emu, IP, voice, pitch);
- if (extra)
- snd_emu10k1_voice_intr_enable(emu, voice);
+static void snd_emu10k1_playback_mute_voices(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *evoice,
+ bool stereo)
+{
+ snd_emu10k1_playback_mute_voice(emu, evoice);
+ if (stereo)
+ snd_emu10k1_playback_mute_voice(emu, evoice + 1);
+}
+
+static void snd_emu10k1_playback_commit_pitch(struct snd_emu10k1 *emu,
+ u32 voice, u32 pitch_target)
+{
+ u32 ptrx = snd_emu10k1_ptr_read(emu, PTRX, voice);
+ u32 cpf = snd_emu10k1_ptr_read(emu, CPF, voice);
+ snd_emu10k1_ptr_write_multiple(emu, voice,
+ PTRX, (ptrx & ~PTRX_PITCHTARGET_MASK) | pitch_target,
+ CPF, (cpf & ~(CPF_CURRENTPITCH_MASK | CPF_FRACADDRESS_MASK)) | pitch_target,
+ REGLIST_END);
}
-static void snd_emu10k1_playback_stop_voice(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *evoice)
+static void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *evoice)
{
unsigned int voice;
- if (evoice == NULL)
- return;
voice = evoice->number;
- snd_emu10k1_voice_intr_disable(emu, voice);
- snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, 0);
- snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, 0);
- snd_emu10k1_ptr_write(emu, IFATN, voice, 0xffff);
- snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff);
- snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff);
- snd_emu10k1_ptr_write(emu, IP, voice, 0);
+ snd_emu10k1_playback_commit_pitch(emu, voice, evoice->epcm->pitch_target << 16);
}
-static inline void snd_emu10k1_playback_mangle_extra(struct snd_emu10k1 *emu,
- struct snd_emu10k1_pcm *epcm,
- struct snd_pcm_substream *substream,
- struct snd_pcm_runtime *runtime)
+static void snd_emu10k1_playback_stop_voice(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *evoice)
{
- unsigned int ptr, period_pos;
+ unsigned int voice;
- /* try to sychronize the current position for the interrupt
- source voice */
- period_pos = runtime->status->hw_ptr - runtime->hw_ptr_interrupt;
- period_pos %= runtime->period_size;
- ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->extra->number);
- ptr &= ~0x00ffffff;
- ptr |= epcm->ccca_start_addr + period_pos;
- snd_emu10k1_ptr_write(emu, CCCA, epcm->extra->number, ptr);
+ voice = evoice->number;
+ snd_emu10k1_playback_commit_pitch(emu, voice, 0);
+}
+
+static void snd_emu10k1_playback_set_running(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_pcm *epcm)
+{
+ epcm->running = 1;
+ snd_emu10k1_voice_intr_enable(emu, epcm->extra->number);
+}
+
+static void snd_emu10k1_playback_set_stopped(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_pcm *epcm)
+{
+ snd_emu10k1_voice_intr_disable(emu, epcm->extra->number);
+ epcm->running = 0;
}
static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream,
@@ -758,45 +723,39 @@ static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_emu10k1_pcm *epcm = runtime->private_data;
struct snd_emu10k1_pcm_mixer *mix;
- int result = 0;
+ bool w_16 = snd_pcm_format_width(runtime->format) == 16;
+ bool stereo = runtime->channels == 2;
/*
- printk(KERN_DEBUG "trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n",
+ dev_dbg(emu->card->dev,
+ "trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n",
(int)emu, cmd, substream->ops->pointer(substream))
*/
- spin_lock(&emu->reg_lock);
+ guard(spinlock)(&emu->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra); /* do we need this? */
- snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[0]);
- /* follow thru */
+ snd_emu10k1_playback_prepare_voices(emu, epcm, w_16, stereo, 1);
+ fallthrough;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
- if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE)
- snd_emu10k1_playback_mangle_extra(emu, epcm, substream, runtime);
mix = &emu->pcm_mixer[substream->number];
- snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 1, 0, mix);
- snd_emu10k1_playback_prepare_voice(emu, epcm->voices[1], 0, 0, mix);
- snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1, NULL);
- snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 1, 0);
- snd_emu10k1_playback_trigger_voice(emu, epcm->voices[1], 0, 0);
- snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1);
- epcm->running = 1;
+ snd_emu10k1_playback_unmute_voices(emu, epcm->voices[0], stereo, mix);
+ snd_emu10k1_playback_set_running(emu, epcm);
+ snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0]);
+ snd_emu10k1_playback_trigger_voice(emu, epcm->extra);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
- epcm->running = 0;
snd_emu10k1_playback_stop_voice(emu, epcm->voices[0]);
- snd_emu10k1_playback_stop_voice(emu, epcm->voices[1]);
snd_emu10k1_playback_stop_voice(emu, epcm->extra);
+ snd_emu10k1_playback_set_stopped(emu, epcm);
+ snd_emu10k1_playback_mute_voices(emu, epcm->voices[0], stereo);
break;
default:
- result = -EINVAL;
- break;
+ return -EINVAL;
}
- spin_unlock(&emu->reg_lock);
- return result;
+ return 0;
}
static int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream,
@@ -805,9 +764,8 @@ static int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream,
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_emu10k1_pcm *epcm = runtime->private_data;
- int result = 0;
- spin_lock(&emu->reg_lock);
+ guard(spinlock)(&emu->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -815,7 +773,7 @@ static int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream,
outl(epcm->capture_ipr, emu->port + IPR);
snd_emu10k1_intr_enable(emu, epcm->capture_inte);
/*
- printk(KERN_DEBUG "adccr = 0x%x, adcbs = 0x%x\n",
+ dev_dbg(emu->card->dev, "adccr = 0x%x, adcbs = 0x%x\n",
epcm->adccr, epcm->adcbs);
*/
switch (epcm->type) {
@@ -824,9 +782,14 @@ static int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream,
break;
case CAPTURE_EFX:
if (emu->audigy) {
- snd_emu10k1_ptr_write(emu, A_FXWC1, 0, epcm->capture_cr_val);
- snd_emu10k1_ptr_write(emu, A_FXWC2, 0, epcm->capture_cr_val2);
- snd_printdd("cr_val=0x%x, cr_val2=0x%x\n", epcm->capture_cr_val, epcm->capture_cr_val2);
+ snd_emu10k1_ptr_write_multiple(emu, 0,
+ A_FXWC1, epcm->capture_cr_val,
+ A_FXWC2, epcm->capture_cr_val2,
+ REGLIST_END);
+ dev_dbg(emu->card->dev,
+ "cr_val=0x%x, cr_val2=0x%x\n",
+ epcm->capture_cr_val,
+ epcm->capture_cr_val2);
} else
snd_emu10k1_ptr_write(emu, FXWC, 0, epcm->capture_cr_val);
break;
@@ -849,8 +812,10 @@ static int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream,
break;
case CAPTURE_EFX:
if (emu->audigy) {
- snd_emu10k1_ptr_write(emu, A_FXWC1, 0, 0);
- snd_emu10k1_ptr_write(emu, A_FXWC2, 0, 0);
+ snd_emu10k1_ptr_write_multiple(emu, 0,
+ A_FXWC1, 0,
+ A_FXWC2, 0,
+ REGLIST_END);
} else
snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
break;
@@ -859,10 +824,9 @@ static int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream,
}
break;
default:
- result = -EINVAL;
+ return -EINVAL;
}
- spin_unlock(&emu->reg_lock);
- return result;
+ return 0;
}
static snd_pcm_uframes_t snd_emu10k1_playback_pointer(struct snd_pcm_substream *substream)
@@ -870,26 +834,29 @@ static snd_pcm_uframes_t snd_emu10k1_playback_pointer(struct snd_pcm_substream *
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_emu10k1_pcm *epcm = runtime->private_data;
- unsigned int ptr;
+ int ptr;
if (!epcm->running)
return 0;
+
ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff;
-#if 0 /* Perex's code */
- ptr += runtime->buffer_size;
ptr -= epcm->ccca_start_addr;
- ptr %= runtime->buffer_size;
-#else /* EMU10K1 Open Source code from Creative */
- if (ptr < epcm->ccca_start_addr)
- ptr += runtime->buffer_size - epcm->ccca_start_addr;
- else {
- ptr -= epcm->ccca_start_addr;
- if (ptr >= runtime->buffer_size)
- ptr -= runtime->buffer_size;
- }
-#endif
+
+ // This is the size of the whole cache minus the interpolator read-ahead,
+ // which leads us to the actual playback position.
+ //
+ // The cache is constantly kept mostly filled, so in principle we could
+ // return a more advanced position representing how far the hardware has
+ // already read the buffer, and set runtime->delay accordingly. However,
+ // this would be slightly different for every channel (and remarkably slow
+ // to obtain), so only a fixed worst-case value would be practical.
+ //
+ ptr -= 64 - 3;
+ if (ptr < 0)
+ ptr += runtime->buffer_size;
+
/*
- printk(KERN_DEBUG
+ dev_dbg(emu->card->dev,
"ptr = 0x%lx, buffer_size = 0x%lx, period_size = 0x%lx\n",
(long)ptr, (long)runtime->buffer_size,
(long)runtime->period_size);
@@ -897,6 +864,49 @@ static snd_pcm_uframes_t snd_emu10k1_playback_pointer(struct snd_pcm_substream *
return ptr;
}
+static u64 snd_emu10k1_efx_playback_voice_mask(struct snd_emu10k1_pcm *epcm,
+ int channels)
+{
+ u64 mask = 0;
+
+ for (int i = 0; i < channels; i++) {
+ int voice = epcm->voices[i]->number;
+ mask |= 1ULL << voice;
+ }
+ return mask;
+}
+
+static void snd_emu10k1_efx_playback_freeze_voices(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_pcm *epcm,
+ int channels)
+{
+ for (int i = 0; i < channels; i++) {
+ int voice = epcm->voices[i]->number;
+ snd_emu10k1_ptr_write(emu, CPF_STOP, voice, 1);
+ snd_emu10k1_playback_commit_pitch(emu, voice, PITCH_48000 << 16);
+ }
+}
+
+static void snd_emu10k1_efx_playback_unmute_voices(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_pcm *epcm,
+ int channels)
+{
+ for (int i = 0; i < channels; i++)
+ snd_emu10k1_playback_unmute_voice(emu, epcm->voices[i], false, true,
+ &emu->efx_pcm_mixer[i]);
+}
+
+static void snd_emu10k1_efx_playback_stop_voices(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_pcm *epcm,
+ int channels)
+{
+ for (int i = 0; i < channels; i++)
+ snd_emu10k1_playback_stop_voice(emu, epcm->voices[i]);
+ snd_emu10k1_playback_set_stopped(emu, epcm);
+
+ for (int i = 0; i < channels; i++)
+ snd_emu10k1_playback_mute_voice(emu, epcm->voices[i]);
+}
static int snd_emu10k1_efx_playback_trigger(struct snd_pcm_substream *substream,
int cmd)
@@ -904,47 +914,60 @@ static int snd_emu10k1_efx_playback_trigger(struct snd_pcm_substream *substream,
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_emu10k1_pcm *epcm = runtime->private_data;
- int i;
+ u64 mask;
int result = 0;
- spin_lock(&emu->reg_lock);
+ guard(spinlock)(&emu->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- /* prepare voices */
- for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
- snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[i]);
- }
- snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra);
-
- /* follow thru */
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
- snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1, NULL);
- snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 0, 0,
- &emu->efx_pcm_mixer[0]);
- for (i = 1; i < NUM_EFX_PLAYBACK; i++)
- snd_emu10k1_playback_prepare_voice(emu, epcm->voices[i], 0, 0,
- &emu->efx_pcm_mixer[i]);
- snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 0, 0);
- snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1);
- for (i = 1; i < NUM_EFX_PLAYBACK; i++)
- snd_emu10k1_playback_trigger_voice(emu, epcm->voices[i], 0, 0);
- epcm->running = 1;
+ mask = snd_emu10k1_efx_playback_voice_mask(
+ epcm, runtime->channels);
+ for (int i = 0; i < 10; i++) {
+ // Note that the freeze is not interruptible, so we make no
+ // effort to reset the bits outside the error handling here.
+ snd_emu10k1_voice_set_loop_stop_multiple(emu, mask);
+ snd_emu10k1_efx_playback_freeze_voices(
+ emu, epcm, runtime->channels);
+ snd_emu10k1_playback_prepare_voices(
+ emu, epcm, true, false, runtime->channels);
+
+ // It might seem to make more sense to unmute the voices only after
+ // they have been started, to potentially avoid torturing the speakers
+ // if something goes wrong. However, we cannot unmute atomically,
+ // which means that we'd get some mild artifacts in the regular case.
+ snd_emu10k1_efx_playback_unmute_voices(emu, epcm, runtime->channels);
+
+ snd_emu10k1_playback_set_running(emu, epcm);
+ result = snd_emu10k1_voice_clear_loop_stop_multiple_atomic(emu, mask);
+ if (result == 0) {
+ // The extra voice is allowed to lag a bit
+ snd_emu10k1_playback_trigger_voice(emu, epcm->extra);
+ return 0;
+ }
+
+ snd_emu10k1_efx_playback_stop_voices(
+ emu, epcm, runtime->channels);
+
+ if (result != -EAGAIN)
+ break;
+ // The sync start can legitimately fail due to NMIs, etc.
+ }
+ snd_emu10k1_voice_clear_loop_stop_multiple(emu, mask);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- epcm->running = 0;
- for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
- snd_emu10k1_playback_stop_voice(emu, epcm->voices[i]);
- }
snd_emu10k1_playback_stop_voice(emu, epcm->extra);
+ snd_emu10k1_efx_playback_stop_voices(
+ emu, epcm, runtime->channels);
+
+ epcm->resume_pos = snd_emu10k1_playback_pointer(substream);
break;
default:
- result = -EINVAL;
- break;
+ return -EINVAL;
}
- spin_unlock(&emu->reg_lock);
return result;
}
@@ -970,7 +993,7 @@ static snd_pcm_uframes_t snd_emu10k1_capture_pointer(struct snd_pcm_substream *s
* Playback support device description
*/
-static struct snd_pcm_hardware snd_emu10k1_playback =
+static const struct snd_pcm_hardware snd_emu10k1_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -983,9 +1006,8 @@ static struct snd_pcm_hardware snd_emu10k1_playback =
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = (128*1024),
- .period_bytes_min = 64,
.period_bytes_max = (128*1024),
- .periods_min = 1,
+ .periods_min = 2,
.periods_max = 1024,
.fifo_size = 0,
};
@@ -994,14 +1016,14 @@ static struct snd_pcm_hardware snd_emu10k1_playback =
* Capture support device description
*/
-static struct snd_pcm_hardware snd_emu10k1_capture =
+static const struct snd_pcm_hardware snd_emu10k1_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = SNDRV_PCM_RATE_8000_48000,
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_24000,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 1,
@@ -1014,20 +1036,18 @@ static struct snd_pcm_hardware snd_emu10k1_capture =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_emu10k1_capture_efx =
+static const struct snd_pcm_hardware snd_emu10k1_capture_efx =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
- .rate_min = 44100,
- .rate_max = 192000,
- .channels_min = 8,
- .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 16,
.buffer_bytes_max = (64*1024),
.period_bytes_min = 384,
.period_bytes_max = (64*1024),
@@ -1088,13 +1108,29 @@ static int snd_emu10k1_efx_playback_close(struct snd_pcm_substream *substream)
return 0;
}
+static int snd_emu10k1_playback_set_constraints(struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ // The buffer size must be a multiple of the period size, to avoid a
+ // mismatch between the extra voice and the regular voices.
+ err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ return err;
+ // The hardware is typically the cache's size of 64 frames ahead.
+ // Leave enough time for actually filling up the buffer.
+ err = snd_pcm_hw_constraint_minmax(
+ runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 128, UINT_MAX);
+ return err;
+}
+
static int snd_emu10k1_efx_playback_open(struct snd_pcm_substream *substream)
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_emu10k1_pcm *epcm;
struct snd_emu10k1_pcm_mixer *mix;
struct snd_pcm_runtime *runtime = substream->runtime;
- int i;
+ int i, j, err;
epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
if (epcm == NULL)
@@ -1103,18 +1139,24 @@ static int snd_emu10k1_efx_playback_open(struct snd_pcm_substream *substream)
epcm->type = PLAYBACK_EFX;
epcm->substream = substream;
- emu->pcm_playback_efx_substream = substream;
-
runtime->private_data = epcm;
runtime->private_free = snd_emu10k1_pcm_free_substream;
runtime->hw = snd_emu10k1_efx_playback;
-
+ if (emu->card_capabilities->emu_model)
+ snd_emu1010_constrain_efx_rate(emu, runtime);
+ err = snd_emu10k1_playback_set_constraints(runtime);
+ if (err < 0) {
+ kfree(epcm);
+ return err;
+ }
+
for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
mix = &emu->efx_pcm_mixer[i];
- mix->send_routing[0][0] = i;
+ for (j = 0; j < 8; j++)
+ mix->send_routing[0][j] = i + j;
memset(&mix->send_volume, 0, sizeof(mix->send_volume));
mix->send_volume[0][0] = 255;
- mix->attn[0] = 0xffff;
+ mix->attn[0] = 0x8000;
mix->epcm = epcm;
snd_emu10k1_pcm_efx_mixer_notify(emu, i, 1);
}
@@ -1127,7 +1169,7 @@ static int snd_emu10k1_playback_open(struct snd_pcm_substream *substream)
struct snd_emu10k1_pcm *epcm;
struct snd_emu10k1_pcm_mixer *mix;
struct snd_pcm_runtime *runtime = substream->runtime;
- int i, err;
+ int i, err, sample_rate;
epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
if (epcm == NULL)
@@ -1138,21 +1180,27 @@ static int snd_emu10k1_playback_open(struct snd_pcm_substream *substream)
runtime->private_data = epcm;
runtime->private_free = snd_emu10k1_pcm_free_substream;
runtime->hw = snd_emu10k1_playback;
- if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) {
+ err = snd_emu10k1_playback_set_constraints(runtime);
+ if (err < 0) {
kfree(epcm);
return err;
}
- if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0) {
+ if (emu->card_capabilities->emu_model)
+ sample_rate = emu->emu1010.word_clock;
+ else
+ sample_rate = 48000;
+ err = snd_pcm_hw_rule_noresample(runtime, sample_rate);
+ if (err < 0) {
kfree(epcm);
return err;
}
mix = &emu->pcm_mixer[substream->number];
- for (i = 0; i < 4; i++)
+ for (i = 0; i < 8; i++)
mix->send_routing[0][i] = mix->send_routing[1][i] = mix->send_routing[2][i] = i;
memset(&mix->send_volume, 0, sizeof(mix->send_volume));
mix->send_volume[0][0] = mix->send_volume[0][1] =
mix->send_volume[1][0] = mix->send_volume[2][1] = 255;
- mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
+ mix->attn[0] = mix->attn[1] = mix->attn[2] = 0x8000;
mix->epcm = epcm;
snd_emu10k1_pcm_mixer_notify(emu, substream->number, 1);
return 0;
@@ -1188,10 +1236,11 @@ static int snd_emu10k1_capture_open(struct snd_pcm_substream *substream)
runtime->private_data = epcm;
runtime->private_free = snd_emu10k1_pcm_free_substream;
runtime->hw = snd_emu10k1_capture;
+ snd_emu10k1_constrain_capture_rates(emu, runtime);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ &hw_constraints_capture_buffer_sizes);
emu->capture_interrupt = snd_emu10k1_pcm_ac97adc_interrupt;
emu->pcm_capture_substream = substream;
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes);
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_capture_rates);
return 0;
}
@@ -1226,10 +1275,10 @@ static int snd_emu10k1_capture_mic_open(struct snd_pcm_substream *substream)
runtime->hw = snd_emu10k1_capture;
runtime->hw.rates = SNDRV_PCM_RATE_8000;
runtime->hw.rate_min = runtime->hw.rate_max = 8000;
- runtime->hw.channels_min = 1;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ &hw_constraints_capture_buffer_sizes);
emu->capture_mic_interrupt = snd_emu10k1_pcm_ac97mic_interrupt;
emu->pcm_capture_mic_substream = substream;
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes);
return 0;
}
@@ -1237,7 +1286,7 @@ static int snd_emu10k1_capture_mic_close(struct snd_pcm_substream *substream)
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
- emu->capture_interrupt = NULL;
+ emu->capture_mic_interrupt = NULL;
emu->pcm_capture_mic_substream = NULL;
return 0;
}
@@ -1248,7 +1297,7 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
struct snd_emu10k1_pcm *epcm;
struct snd_pcm_runtime *runtime = substream->runtime;
int nefx = emu->audigy ? 64 : 32;
- int idx;
+ int idx, err;
epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
if (epcm == NULL)
@@ -1264,66 +1313,30 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
substream->runtime->private_data = epcm;
substream->runtime->private_free = snd_emu10k1_pcm_free_substream;
runtime->hw = snd_emu10k1_capture_efx;
- runtime->hw.rates = SNDRV_PCM_RATE_48000;
- runtime->hw.rate_min = runtime->hw.rate_max = 48000;
- spin_lock_irq(&emu->reg_lock);
if (emu->card_capabilities->emu_model) {
- /* Nb. of channels has been increased to 16 */
- /* TODO
- * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE
- * SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
- * SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
- * SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000
- * rate_min = 44100,
- * rate_max = 192000,
- * channels_min = 16,
- * channels_max = 16,
- * Need to add mixer control to fix sample rate
- *
+ snd_emu1010_constrain_efx_rate(emu, runtime);
+ /*
* There are 32 mono channels of 16bits each.
- * 24bit Audio uses 2x channels over 16bit
- * 96kHz uses 2x channels over 48kHz
- * 192kHz uses 4x channels over 48kHz
- * So, for 48kHz 24bit, one has 16 channels
- * for 96kHz 24bit, one has 8 channels
- * for 192kHz 24bit, one has 4 channels
- *
+ * 24bit Audio uses 2x channels over 16bit,
+ * 96kHz uses 2x channels over 48kHz,
+ * 192kHz uses 4x channels over 48kHz.
+ * So, for 48kHz 24bit, one has 16 channels,
+ * for 96kHz 24bit, one has 8 channels,
+ * for 192kHz 24bit, one has 4 channels.
+ * 1010rev2 and 1616(m) cards have double that,
+ * but we don't exceed 16 channels anyway.
*/
-#if 1
- switch (emu->emu1010.internal_clock) {
- case 0:
- /* For 44.1kHz */
- runtime->hw.rates = SNDRV_PCM_RATE_44100;
- runtime->hw.rate_min = runtime->hw.rate_max = 44100;
- runtime->hw.channels_min =
- runtime->hw.channels_max = 16;
- break;
- case 1:
- /* For 48kHz */
- runtime->hw.rates = SNDRV_PCM_RATE_48000;
- runtime->hw.rate_min = runtime->hw.rate_max = 48000;
- runtime->hw.channels_min =
- runtime->hw.channels_max = 16;
- break;
- };
-#endif
#if 0
/* For 96kHz */
- runtime->hw.rates = SNDRV_PCM_RATE_96000;
- runtime->hw.rate_min = runtime->hw.rate_max = 96000;
runtime->hw.channels_min = runtime->hw.channels_max = 4;
#endif
#if 0
/* For 192kHz */
- runtime->hw.rates = SNDRV_PCM_RATE_192000;
- runtime->hw.rate_min = runtime->hw.rate_max = 192000;
runtime->hw.channels_min = runtime->hw.channels_max = 2;
#endif
runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
- /* efx_voices_mask[0] is expected to be zero
- * efx_voices_mask[1] is expected to have 32bits set
- */
} else {
+ guard(spinlock_irq)(&emu->reg_lock);
runtime->hw.channels_min = runtime->hw.channels_max = 0;
for (idx = 0; idx < nefx; idx++) {
if (emu->efx_voices_mask[idx/32] & (1 << (idx%32))) {
@@ -1331,13 +1344,19 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
runtime->hw.channels_max++;
}
}
+ epcm->capture_cr_val = emu->efx_voices_mask[0];
+ epcm->capture_cr_val2 = emu->efx_voices_mask[1];
+ }
+ err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &hw_constraints_efx_capture_channels);
+ if (err < 0) {
+ kfree(epcm);
+ return err;
}
- epcm->capture_cr_val = emu->efx_voices_mask[0];
- epcm->capture_cr_val2 = emu->efx_voices_mask[1];
- spin_unlock_irq(&emu->reg_lock);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ &hw_constraints_capture_buffer_sizes);
emu->capture_efx_interrupt = snd_emu10k1_pcm_efx_interrupt;
emu->pcm_capture_efx_substream = substream;
- snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes);
return 0;
}
@@ -1345,57 +1364,48 @@ static int snd_emu10k1_capture_efx_close(struct snd_pcm_substream *substream)
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
- emu->capture_interrupt = NULL;
+ emu->capture_efx_interrupt = NULL;
emu->pcm_capture_efx_substream = NULL;
return 0;
}
-static struct snd_pcm_ops snd_emu10k1_playback_ops = {
+static const struct snd_pcm_ops snd_emu10k1_playback_ops = {
.open = snd_emu10k1_playback_open,
.close = snd_emu10k1_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_emu10k1_playback_hw_params,
.hw_free = snd_emu10k1_playback_hw_free,
.prepare = snd_emu10k1_playback_prepare,
.trigger = snd_emu10k1_playback_trigger,
.pointer = snd_emu10k1_playback_pointer,
- .page = snd_pcm_sgbuf_ops_page,
};
-static struct snd_pcm_ops snd_emu10k1_capture_ops = {
+static const struct snd_pcm_ops snd_emu10k1_capture_ops = {
.open = snd_emu10k1_capture_open,
.close = snd_emu10k1_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_emu10k1_capture_hw_params,
- .hw_free = snd_emu10k1_capture_hw_free,
.prepare = snd_emu10k1_capture_prepare,
.trigger = snd_emu10k1_capture_trigger,
.pointer = snd_emu10k1_capture_pointer,
};
/* EFX playback */
-static struct snd_pcm_ops snd_emu10k1_efx_playback_ops = {
+static const struct snd_pcm_ops snd_emu10k1_efx_playback_ops = {
.open = snd_emu10k1_efx_playback_open,
.close = snd_emu10k1_efx_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_emu10k1_playback_hw_params,
- .hw_free = snd_emu10k1_efx_playback_hw_free,
+ .hw_free = snd_emu10k1_playback_hw_free,
.prepare = snd_emu10k1_efx_playback_prepare,
.trigger = snd_emu10k1_efx_playback_trigger,
- .pointer = snd_emu10k1_efx_playback_pointer,
- .page = snd_pcm_sgbuf_ops_page,
+ .pointer = snd_emu10k1_playback_pointer,
};
-int __devinit snd_emu10k1_pcm(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm)
+int snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device)
{
struct snd_pcm *pcm;
struct snd_pcm_substream *substream;
int err;
- if (rpcm)
- *rpcm = NULL;
-
- if ((err = snd_pcm_new(emu->card, "emu10k1", device, 32, 1, &pcm)) < 0)
+ err = snd_pcm_new(emu->card, "emu10k1", device, 32, 1, &pcm);
+ if (err < 0)
return err;
pcm->private_data = emu;
@@ -1405,32 +1415,30 @@ int __devinit snd_emu10k1_pcm(struct snd_emu10k1 * emu, int device, struct snd_p
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
- strcpy(pcm->name, "ADC Capture/Standard PCM Playback");
+ strscpy(pcm->name, "ADC Capture/Standard PCM Playback");
emu->pcm = pcm;
+ /* playback substream can't use managed buffers due to alignment */
for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
- if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0)
- return err;
+ snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG,
+ &emu->pci->dev,
+ 64*1024, 64*1024);
for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next)
- snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
+ &emu->pci->dev, 64*1024, 64*1024);
return 0;
}
-int __devinit snd_emu10k1_pcm_multi(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm)
+int snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device)
{
struct snd_pcm *pcm;
struct snd_pcm_substream *substream;
int err;
- if (rpcm)
- *rpcm = NULL;
-
- if ((err = snd_pcm_new(emu->card, "emu10k1", device, 1, 0, &pcm)) < 0)
+ err = snd_pcm_new(emu->card, "emu10k1", device, 1, 0, &pcm);
+ if (err < 0)
return err;
pcm->private_data = emu;
@@ -1439,40 +1447,33 @@ int __devinit snd_emu10k1_pcm_multi(struct snd_emu10k1 * emu, int device, struct
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
- strcpy(pcm->name, "Multichannel Playback");
+ strscpy(pcm->name, "Multichannel Playback");
emu->pcm_multi = pcm;
for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
- if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0)
- return err;
-
- if (rpcm)
- *rpcm = pcm;
+ snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG,
+ &emu->pci->dev,
+ 64*1024, 64*1024);
return 0;
}
-static struct snd_pcm_ops snd_emu10k1_capture_mic_ops = {
+static const struct snd_pcm_ops snd_emu10k1_capture_mic_ops = {
.open = snd_emu10k1_capture_mic_open,
.close = snd_emu10k1_capture_mic_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_emu10k1_capture_hw_params,
- .hw_free = snd_emu10k1_capture_hw_free,
.prepare = snd_emu10k1_capture_prepare,
.trigger = snd_emu10k1_capture_trigger,
.pointer = snd_emu10k1_capture_pointer,
};
-int __devinit snd_emu10k1_pcm_mic(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm)
+int snd_emu10k1_pcm_mic(struct snd_emu10k1 *emu, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
-
- if ((err = snd_pcm_new(emu->card, "emu10k1 mic", device, 0, 1, &pcm)) < 0)
+ err = snd_pcm_new(emu->card, "emu10k1 mic", device, 0, 1, &pcm);
+ if (err < 0)
return err;
pcm->private_data = emu;
@@ -1480,13 +1481,12 @@ int __devinit snd_emu10k1_pcm_mic(struct snd_emu10k1 * emu, int device, struct s
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_mic_ops);
pcm->info_flags = 0;
- strcpy(pcm->name, "Mic Capture");
+ strscpy(pcm->name, "Mic Capture");
emu->pcm_mic = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &emu->pci->dev,
+ 64*1024, 64*1024);
- if (rpcm)
- *rpcm = pcm;
return 0;
}
@@ -1507,10 +1507,8 @@ static int snd_emu10k1_pcm_efx_voices_mask_get(struct snd_kcontrol *kcontrol, st
int nefx = emu->audigy ? 64 : 32;
int idx;
- spin_lock_irq(&emu->reg_lock);
for (idx = 0; idx < nefx; idx++)
ucontrol->value.integer.value[idx] = (emu->efx_voices_mask[idx / 32] & (1 << (idx % 32))) ? 1 : 0;
- spin_unlock_irq(&emu->reg_lock);
return 0;
}
@@ -1519,7 +1517,6 @@ static int snd_emu10k1_pcm_efx_voices_mask_put(struct snd_kcontrol *kcontrol, st
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
unsigned int nval[2], bits;
int nefx = emu->audigy ? 64 : 32;
- int nefxb = emu->audigy ? 7 : 6;
int change, idx;
nval[0] = nval[1] = 0;
@@ -1528,24 +1525,19 @@ static int snd_emu10k1_pcm_efx_voices_mask_put(struct snd_kcontrol *kcontrol, st
nval[idx / 32] |= 1 << (idx % 32);
bits++;
}
-
- for (idx = 0; idx < nefxb; idx++)
- if (1 << idx == bits)
- break;
-
- if (idx >= nefxb)
+
+ if (bits == 9 || bits == 11 || bits == 13 || bits == 15 || bits > 16)
return -EINVAL;
- spin_lock_irq(&emu->reg_lock);
+ guard(spinlock_irq)(&emu->reg_lock);
change = (nval[0] != emu->efx_voices_mask[0]) ||
(nval[1] != emu->efx_voices_mask[1]);
emu->efx_voices_mask[0] = nval[0];
emu->efx_voices_mask[1] = nval[1];
- spin_unlock_irq(&emu->reg_lock);
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_pcm_efx_voices_mask = {
+static const struct snd_kcontrol_new snd_emu10k1_pcm_efx_voices_mask = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Captured FX8010 Outputs",
.info = snd_emu10k1_pcm_efx_voices_mask_info,
@@ -1553,12 +1545,9 @@ static struct snd_kcontrol_new snd_emu10k1_pcm_efx_voices_mask = {
.put = snd_emu10k1_pcm_efx_voices_mask_put
};
-static struct snd_pcm_ops snd_emu10k1_capture_efx_ops = {
+static const struct snd_pcm_ops snd_emu10k1_capture_efx_ops = {
.open = snd_emu10k1_capture_efx_open,
.close = snd_emu10k1_capture_efx_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_emu10k1_capture_hw_params,
- .hw_free = snd_emu10k1_capture_hw_free,
.prepare = snd_emu10k1_capture_prepare,
.trigger = snd_emu10k1_capture_trigger,
.pointer = snd_emu10k1_capture_pointer,
@@ -1583,7 +1572,8 @@ static void snd_emu10k1_fx8010_playback_tram_poke1(unsigned short *dst_left,
unsigned int tram_shift)
{
/*
- printk(KERN_DEBUG "tram_poke1: dst_left = 0x%p, dst_right = 0x%p, "
+ dev_dbg(emu->card->dev,
+ "tram_poke1: dst_left = 0x%p, dst_right = 0x%p, "
"src = 0x%p, count = 0x%x\n",
dst_left, dst_right, src, count);
*/
@@ -1634,14 +1624,8 @@ static int snd_emu10k1_fx8010_playback_transfer(struct snd_pcm_substream *substr
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
- snd_pcm_indirect_playback_transfer(substream, &pcm->pcm_rec, fx8010_pb_trans_copy);
- return 0;
-}
-
-static int snd_emu10k1_fx8010_playback_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ return snd_pcm_indirect_playback_transfer(substream, &pcm->pcm_rec,
+ fx8010_pb_trans_copy);
}
static int snd_emu10k1_fx8010_playback_hw_free(struct snd_pcm_substream *substream)
@@ -1652,7 +1636,6 @@ static int snd_emu10k1_fx8010_playback_hw_free(struct snd_pcm_substream *substre
for (i = 0; i < pcm->channels; i++)
snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, 0);
- snd_pcm_lib_free_pages(substream);
return 0;
}
@@ -1664,7 +1647,7 @@ static int snd_emu10k1_fx8010_playback_prepare(struct snd_pcm_substream *substre
unsigned int i;
/*
- printk(KERN_DEBUG "prepare: etram_pages = 0x%p, dma_area = 0x%x, "
+ dev_dbg(emu->card->dev, "prepare: etram_pages = 0x%p, dma_area = 0x%x, "
"buffer_size = 0x%x (0x%x)\n",
emu->fx8010.etram_pages, runtime->dma_area,
runtime->buffer_size, runtime->buffer_size << 2);
@@ -1674,12 +1657,14 @@ static int snd_emu10k1_fx8010_playback_prepare(struct snd_pcm_substream *substre
pcm->pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size);
pcm->tram_shift = 0;
- snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_running, 0, 0); /* reset */
- snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); /* reset */
- snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_size, 0, runtime->buffer_size);
- snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_ptr, 0, 0); /* reset ptr number */
- snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_count, 0, runtime->period_size);
- snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_tmpcount, 0, runtime->period_size);
+ snd_emu10k1_ptr_write_multiple(emu, 0,
+ emu->gpr_base + pcm->gpr_running, 0, /* reset */
+ emu->gpr_base + pcm->gpr_trigger, 0, /* reset */
+ emu->gpr_base + pcm->gpr_size, runtime->buffer_size,
+ emu->gpr_base + pcm->gpr_ptr, 0, /* reset ptr number */
+ emu->gpr_base + pcm->gpr_count, runtime->period_size,
+ emu->gpr_base + pcm->gpr_tmpcount, runtime->period_size,
+ REGLIST_END);
for (i = 0; i < pcm->channels; i++)
snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, (TANKMEMADDRREG_READ|TANKMEMADDRREG_ALIGN) + i * (runtime->buffer_size / pcm->channels));
return 0;
@@ -1689,9 +1674,9 @@ static int snd_emu10k1_fx8010_playback_trigger(struct snd_pcm_substream *substre
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
- int result = 0;
+ int result;
- spin_lock(&emu->reg_lock);
+ guard(spinlock)(&emu->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
/* follow thru */
@@ -1711,25 +1696,22 @@ static int snd_emu10k1_fx8010_playback_trigger(struct snd_pcm_substream *substre
#endif
result = snd_emu10k1_fx8010_register_irq_handler(emu, snd_emu10k1_fx8010_playback_irq, pcm->gpr_running, substream, &pcm->irq);
if (result < 0)
- goto __err;
+ return result;
snd_emu10k1_fx8010_playback_transfer(substream); /* roll the ball */
snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 1);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
- snd_emu10k1_fx8010_unregister_irq_handler(emu, pcm->irq); pcm->irq = NULL;
+ snd_emu10k1_fx8010_unregister_irq_handler(emu, &pcm->irq);
snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0);
pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size);
pcm->tram_shift = 0;
break;
default:
- result = -EINVAL;
- break;
+ return -EINVAL;
}
- __err:
- spin_unlock(&emu->reg_lock);
- return result;
+ return 0;
}
static snd_pcm_uframes_t snd_emu10k1_fx8010_playback_pointer(struct snd_pcm_substream *substream)
@@ -1744,11 +1726,12 @@ static snd_pcm_uframes_t snd_emu10k1_fx8010_playback_pointer(struct snd_pcm_subs
return snd_pcm_indirect_playback_pointer(substream, &pcm->pcm_rec, ptr);
}
-static struct snd_pcm_hardware snd_emu10k1_fx8010_playback =
+static const struct snd_pcm_hardware snd_emu10k1_fx8010_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
- /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE),
+ /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_SYNC_APPLPTR),
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
@@ -1772,13 +1755,10 @@ static int snd_emu10k1_fx8010_playback_open(struct snd_pcm_substream *substream)
runtime->hw = snd_emu10k1_fx8010_playback;
runtime->hw.channels_min = runtime->hw.channels_max = pcm->channels;
runtime->hw.period_bytes_max = (pcm->buffer_size * 2) / 2;
- spin_lock_irq(&emu->reg_lock);
- if (pcm->valid == 0) {
- spin_unlock_irq(&emu->reg_lock);
+ guard(spinlock_irq)(&emu->reg_lock);
+ if (pcm->valid == 0)
return -ENODEV;
- }
pcm->opened = 1;
- spin_unlock_irq(&emu->reg_lock);
return 0;
}
@@ -1787,17 +1767,14 @@ static int snd_emu10k1_fx8010_playback_close(struct snd_pcm_substream *substream
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
- spin_lock_irq(&emu->reg_lock);
+ guard(spinlock_irq)(&emu->reg_lock);
pcm->opened = 0;
- spin_unlock_irq(&emu->reg_lock);
return 0;
}
-static struct snd_pcm_ops snd_emu10k1_fx8010_playback_ops = {
+static const struct snd_pcm_ops snd_emu10k1_fx8010_playback_ops = {
.open = snd_emu10k1_fx8010_playback_open,
.close = snd_emu10k1_fx8010_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_emu10k1_fx8010_playback_hw_params,
.hw_free = snd_emu10k1_fx8010_playback_hw_free,
.prepare = snd_emu10k1_fx8010_playback_prepare,
.trigger = snd_emu10k1_fx8010_playback_trigger,
@@ -1805,61 +1782,55 @@ static struct snd_pcm_ops snd_emu10k1_fx8010_playback_ops = {
.ack = snd_emu10k1_fx8010_playback_transfer,
};
-int __devinit snd_emu10k1_pcm_efx(struct snd_emu10k1 * emu, int device, struct snd_pcm ** rpcm)
+int snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device)
{
struct snd_pcm *pcm;
struct snd_kcontrol *kctl;
int err;
- if (rpcm)
- *rpcm = NULL;
-
- if ((err = snd_pcm_new(emu->card, "emu10k1 efx", device, 8, 1, &pcm)) < 0)
+ err = snd_pcm_new(emu->card, "emu10k1 efx", device, emu->audigy ? 0 : 8, 1, &pcm);
+ if (err < 0)
return err;
pcm->private_data = emu;
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_fx8010_playback_ops);
+ if (!emu->audigy)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_fx8010_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_efx_ops);
pcm->info_flags = 0;
- strcpy(pcm->name, "Multichannel Capture/PT Playback");
+ if (emu->audigy)
+ strscpy(pcm->name, "Multichannel Capture");
+ else
+ strscpy(pcm->name, "Multichannel Capture/PT Playback");
emu->pcm_efx = pcm;
- if (rpcm)
- *rpcm = pcm;
- /* EFX capture - record the "FXBUS2" channels, by default we connect the EXTINs
- * to these
- */
-
- /* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */
- if (emu->audigy) {
- emu->efx_voices_mask[0] = 0;
- if (emu->card_capabilities->emu_model)
- /* Pavel Hofman - 32 voices will be used for
- * capture (write mode) -
- * each bit = corresponding voice
- */
- emu->efx_voices_mask[1] = 0xffffffff;
- else
+ if (!emu->card_capabilities->emu_model) {
+ // On Sound Blasters, the DSP code copies the EXTINs to FXBUS2.
+ // The mask determines which of these and the EXTOUTs the multi-
+ // channel capture actually records (the channel order is fixed).
+ if (emu->audigy) {
+ emu->efx_voices_mask[0] = 0;
emu->efx_voices_mask[1] = 0xffff;
+ } else {
+ emu->efx_voices_mask[0] = 0xffff0000;
+ emu->efx_voices_mask[1] = 0;
+ }
+ kctl = snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->id.device = device;
+ err = snd_ctl_add(emu->card, kctl);
+ if (err < 0)
+ return err;
} else {
- emu->efx_voices_mask[0] = 0xffff0000;
- emu->efx_voices_mask[1] = 0;
+ // On E-MU cards, the DSP code copies the P16VINs/EMU32INs to
+ // FXBUS2. These are already selected & routed by the FPGA,
+ // so there is no need to apply additional masking.
}
- /* For emu1010, the control has to set 32 upper bits (voices)
- * out of the 64 bits (voices) to true for the 16-channels capture
- * to work correctly. Correct A_FXWC2 initial value (0xffffffff)
- * is already defined but the snd_emu10k1_pcm_efx_voices_mask
- * control can override this register's value.
- */
- kctl = snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu);
- if (!kctl)
- return -ENOMEM;
- kctl->id.device = device;
- snd_ctl_add(emu->card, kctl);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &emu->pci->dev,
+ 64*1024, 64*1024);
return 0;
}
diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
index bc38dd4d071f..f6186b5be049 100644
--- a/sound/pci/emu10k1/emuproc.c
+++ b/sound/pci/emu10k1/emuproc.c
@@ -1,50 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * Lee Revell <rlrevell@joe-job.com>
+ * James Courtier-Dutton <James@superbug.co.uk>
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
* Creative Labs, Inc.
- * Routines for control of EMU10K1 chips / proc interface routines
- *
- * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
- * Added EMU 1010 support.
- *
- * BUGS:
- * --
- *
- * TODO:
- * --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
+ * Routines for control of EMU10K1 chips / proc interface routines
*/
#include <linux/slab.h>
#include <linux/init.h>
+#include <linux/string_choices.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
#include "p16v.h"
-#ifdef CONFIG_PROC_FS
static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu,
struct snd_info_buffer *buffer,
char *title,
int status_reg,
int rate_reg)
{
- static char *clkaccy[4] = { "1000ppm", "50ppm", "variable", "unknown" };
- static int samplerate[16] = { 44100, 1, 48000, 32000, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
- static char *channel[16] = { "unspec", "left", "right", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" };
- static char *emphasis[8] = { "none", "50/15 usec 2 channel", "2", "3", "4", "5", "6", "7" };
+ static const char * const clkaccy[4] = { "1000ppm", "50ppm", "variable", "unknown" };
+ static const int samplerate[16] = { 44100, 1, 48000, 32000, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+ static const char * const channel[16] = { "unspec", "left", "right", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" };
+ static const char * const emphasis[8] = { "none", "50/15 usec 2 channel", "2", "3", "4", "5", "6", "7" };
unsigned int status, rate = 0;
status = snd_emu10k1_ptr_read(emu, status_reg, 0);
@@ -52,9 +33,9 @@ static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu,
snd_iprintf(buffer, "\n%s\n", title);
if (status != 0xffffffff) {
- snd_iprintf(buffer, "Professional Mode : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no");
- snd_iprintf(buffer, "Not Audio Data : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no");
- snd_iprintf(buffer, "Copyright : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no");
+ snd_iprintf(buffer, "Professional Mode : %s\n", str_yes_no(status & SPCS_PROFESSIONAL));
+ snd_iprintf(buffer, "Not Audio Data : %s\n", str_yes_no(status & SPCS_NOTAUDIODATA));
+ snd_iprintf(buffer, "Copyright : %s\n", str_yes_no(status & SPCS_COPYRIGHT));
snd_iprintf(buffer, "Emphasis : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]);
snd_iprintf(buffer, "Mode : %i\n", (status & SPCS_MODEMASK) >> 6);
snd_iprintf(buffer, "Category Code : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8);
@@ -66,9 +47,9 @@ static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu,
if (rate_reg > 0) {
rate = snd_emu10k1_ptr_read(emu, rate_reg, 0);
- snd_iprintf(buffer, "S/PDIF Valid : %s\n", rate & SRCS_SPDIFVALID ? "on" : "off");
- snd_iprintf(buffer, "S/PDIF Locked : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off");
- snd_iprintf(buffer, "Rate Locked : %s\n", rate & SRCS_RATELOCKED ? "on" : "off");
+ snd_iprintf(buffer, "S/PDIF Valid : %s\n", str_on_off(rate & SRCS_SPDIFVALID));
+ snd_iprintf(buffer, "S/PDIF Locked : %s\n", str_on_off(rate & SRCS_SPDIFLOCKED));
+ snd_iprintf(buffer, "Rate Locked : %s\n", str_on_off(rate & SRCS_RATELOCKED));
/* From ((Rate * 48000 ) / 262144); */
snd_iprintf(buffer, "Estimated Sample Rate : %d\n", ((rate & 0xFFFFF ) * 375) >> 11);
}
@@ -81,158 +62,100 @@ static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu,
static void snd_emu10k1_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- /* FIXME - output names are in emufx.c too */
- static char *creative_outs[32] = {
- /* 00 */ "AC97 Left",
- /* 01 */ "AC97 Right",
- /* 02 */ "Optical IEC958 Left",
- /* 03 */ "Optical IEC958 Right",
- /* 04 */ "Center",
- /* 05 */ "LFE",
- /* 06 */ "Headphone Left",
- /* 07 */ "Headphone Right",
- /* 08 */ "Surround Left",
- /* 09 */ "Surround Right",
- /* 10 */ "PCM Capture Left",
- /* 11 */ "PCM Capture Right",
- /* 12 */ "MIC Capture",
- /* 13 */ "AC97 Surround Left",
- /* 14 */ "AC97 Surround Right",
- /* 15 */ "???",
- /* 16 */ "???",
- /* 17 */ "Analog Center",
- /* 18 */ "Analog LFE",
- /* 19 */ "???",
- /* 20 */ "???",
- /* 21 */ "???",
- /* 22 */ "???",
- /* 23 */ "???",
- /* 24 */ "???",
- /* 25 */ "???",
- /* 26 */ "???",
- /* 27 */ "???",
- /* 28 */ "???",
- /* 29 */ "???",
- /* 30 */ "???",
- /* 31 */ "???"
- };
-
- static char *audigy_outs[64] = {
- /* 00 */ "Digital Front Left",
- /* 01 */ "Digital Front Right",
- /* 02 */ "Digital Center",
- /* 03 */ "Digital LEF",
- /* 04 */ "Headphone Left",
- /* 05 */ "Headphone Right",
- /* 06 */ "Digital Rear Left",
- /* 07 */ "Digital Rear Right",
- /* 08 */ "Front Left",
- /* 09 */ "Front Right",
- /* 10 */ "Center",
- /* 11 */ "LFE",
- /* 12 */ "???",
- /* 13 */ "???",
- /* 14 */ "Rear Left",
- /* 15 */ "Rear Right",
- /* 16 */ "AC97 Front Left",
- /* 17 */ "AC97 Front Right",
- /* 18 */ "ADC Caputre Left",
- /* 19 */ "ADC Capture Right",
- /* 20 */ "???",
- /* 21 */ "???",
- /* 22 */ "???",
- /* 23 */ "???",
- /* 24 */ "???",
- /* 25 */ "???",
- /* 26 */ "???",
- /* 27 */ "???",
- /* 28 */ "???",
- /* 29 */ "???",
- /* 30 */ "???",
- /* 31 */ "???",
- /* 32 */ "FXBUS2_0",
- /* 33 */ "FXBUS2_1",
- /* 34 */ "FXBUS2_2",
- /* 35 */ "FXBUS2_3",
- /* 36 */ "FXBUS2_4",
- /* 37 */ "FXBUS2_5",
- /* 38 */ "FXBUS2_6",
- /* 39 */ "FXBUS2_7",
- /* 40 */ "FXBUS2_8",
- /* 41 */ "FXBUS2_9",
- /* 42 */ "FXBUS2_10",
- /* 43 */ "FXBUS2_11",
- /* 44 */ "FXBUS2_12",
- /* 45 */ "FXBUS2_13",
- /* 46 */ "FXBUS2_14",
- /* 47 */ "FXBUS2_15",
- /* 48 */ "FXBUS2_16",
- /* 49 */ "FXBUS2_17",
- /* 50 */ "FXBUS2_18",
- /* 51 */ "FXBUS2_19",
- /* 52 */ "FXBUS2_20",
- /* 53 */ "FXBUS2_21",
- /* 54 */ "FXBUS2_22",
- /* 55 */ "FXBUS2_23",
- /* 56 */ "FXBUS2_24",
- /* 57 */ "FXBUS2_25",
- /* 58 */ "FXBUS2_26",
- /* 59 */ "FXBUS2_27",
- /* 60 */ "FXBUS2_28",
- /* 61 */ "FXBUS2_29",
- /* 62 */ "FXBUS2_30",
- /* 63 */ "FXBUS2_31"
- };
-
struct snd_emu10k1 *emu = entry->private_data;
- unsigned int val, val1;
- int nefx = emu->audigy ? 64 : 32;
- char **outputs = emu->audigy ? audigy_outs : creative_outs;
+ const char * const *inputs = emu->audigy ?
+ snd_emu10k1_audigy_ins : snd_emu10k1_sblive_ins;
+ const char * const *outputs = emu->audigy ?
+ snd_emu10k1_audigy_outs : snd_emu10k1_sblive_outs;
+ unsigned short extin_mask = emu->audigy ? ~0 : emu->fx8010.extin_mask;
+ unsigned short extout_mask = emu->audigy ? ~0 : emu->fx8010.extout_mask;
+ unsigned int val, val1, ptrx, psst, dsl, snda;
+ int nefx = emu->audigy ? 32 : 16;
int idx;
snd_iprintf(buffer, "EMU10K1\n\n");
snd_iprintf(buffer, "Card : %s\n",
- emu->audigy ? "Audigy" : (emu->card_capabilities->ecard ? "EMU APS" : "Creative"));
+ emu->card_capabilities->emu_model ? "E-MU D.A.S." :
+ emu->card_capabilities->ecard ? "E-MU A.P.S." :
+ emu->audigy ? "SB Audigy" : "SB Live!");
snd_iprintf(buffer, "Internal TRAM (words) : 0x%x\n", emu->fx8010.itram_size);
snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", (int)emu->fx8010.etram_pages.bytes / 2);
- snd_iprintf(buffer, "\n");
- snd_iprintf(buffer, "Effect Send Routing :\n");
+
+ snd_iprintf(buffer, "\nEffect Send Routing & Amounts:\n");
for (idx = 0; idx < NUM_G; idx++) {
- val = emu->audigy ?
- snd_emu10k1_ptr_read(emu, A_FXRT1, idx) :
- snd_emu10k1_ptr_read(emu, FXRT, idx);
- val1 = emu->audigy ?
- snd_emu10k1_ptr_read(emu, A_FXRT2, idx) :
- 0;
+ ptrx = snd_emu10k1_ptr_read(emu, PTRX, idx);
+ psst = snd_emu10k1_ptr_read(emu, PSST, idx);
+ dsl = snd_emu10k1_ptr_read(emu, DSL, idx);
if (emu->audigy) {
- snd_iprintf(buffer, "Ch%i: A=%i, B=%i, C=%i, D=%i, ",
+ val = snd_emu10k1_ptr_read(emu, A_FXRT1, idx);
+ val1 = snd_emu10k1_ptr_read(emu, A_FXRT2, idx);
+ snda = snd_emu10k1_ptr_read(emu, A_SENDAMOUNTS, idx);
+ snd_iprintf(buffer, "Ch%-2i: A=%2i:%02x, B=%2i:%02x, C=%2i:%02x, D=%2i:%02x, ",
idx,
- val & 0x3f,
- (val >> 8) & 0x3f,
- (val >> 16) & 0x3f,
- (val >> 24) & 0x3f);
- snd_iprintf(buffer, "E=%i, F=%i, G=%i, H=%i\n",
- val1 & 0x3f,
- (val1 >> 8) & 0x3f,
- (val1 >> 16) & 0x3f,
- (val1 >> 24) & 0x3f);
+ val & 0x3f, REG_VAL_GET(PTRX_FXSENDAMOUNT_A, ptrx),
+ (val >> 8) & 0x3f, REG_VAL_GET(PTRX_FXSENDAMOUNT_B, ptrx),
+ (val >> 16) & 0x3f, REG_VAL_GET(PSST_FXSENDAMOUNT_C, psst),
+ (val >> 24) & 0x3f, REG_VAL_GET(DSL_FXSENDAMOUNT_D, dsl));
+ snd_iprintf(buffer, "E=%2i:%02x, F=%2i:%02x, G=%2i:%02x, H=%2i:%02x\n",
+ val1 & 0x3f, (snda >> 24) & 0xff,
+ (val1 >> 8) & 0x3f, (snda >> 16) & 0xff,
+ (val1 >> 16) & 0x3f, (snda >> 8) & 0xff,
+ (val1 >> 24) & 0x3f, snda & 0xff);
} else {
- snd_iprintf(buffer, "Ch%i: A=%i, B=%i, C=%i, D=%i\n",
+ val = snd_emu10k1_ptr_read(emu, FXRT, idx);
+ snd_iprintf(buffer, "Ch%-2i: A=%2i:%02x, B=%2i:%02x, C=%2i:%02x, D=%2i:%02x\n",
idx,
- (val >> 16) & 0x0f,
- (val >> 20) & 0x0f,
- (val >> 24) & 0x0f,
- (val >> 28) & 0x0f);
+ (val >> 16) & 0x0f, REG_VAL_GET(PTRX_FXSENDAMOUNT_A, ptrx),
+ (val >> 20) & 0x0f, REG_VAL_GET(PTRX_FXSENDAMOUNT_B, ptrx),
+ (val >> 24) & 0x0f, REG_VAL_GET(PSST_FXSENDAMOUNT_C, psst),
+ (val >> 28) & 0x0f, REG_VAL_GET(DSL_FXSENDAMOUNT_D, dsl));
}
}
- snd_iprintf(buffer, "\nCaptured FX Outputs :\n");
- for (idx = 0; idx < nefx; idx++) {
- if (emu->efx_voices_mask[idx/32] & (1 << (idx%32)))
- snd_iprintf(buffer, " Output %02i [%s]\n", idx, outputs[idx]);
+ snd_iprintf(buffer, "\nEffect Send Targets:\n");
+ // Audigy actually has 64, but we don't use them all.
+ for (idx = 0; idx < 32; idx++) {
+ const char *c = snd_emu10k1_fxbus[idx];
+ if (c)
+ snd_iprintf(buffer, " Channel %02i [%s]\n", idx, c);
+ }
+ if (!emu->card_capabilities->emu_model) {
+ snd_iprintf(buffer, "\nOutput Channels:\n");
+ for (idx = 0; idx < 32; idx++)
+ if (outputs[idx] && (extout_mask & (1 << idx)))
+ snd_iprintf(buffer, " Channel %02i [%s]\n", idx, outputs[idx]);
+ snd_iprintf(buffer, "\nInput Channels:\n");
+ for (idx = 0; idx < 16; idx++)
+ if (inputs[idx] && (extin_mask & (1 << idx)))
+ snd_iprintf(buffer, " Channel %02i [%s]\n", idx, inputs[idx]);
+ snd_iprintf(buffer, "\nMultichannel Capture Sources:\n");
+ for (idx = 0; idx < nefx; idx++)
+ if (emu->efx_voices_mask[0] & (1 << idx))
+ snd_iprintf(buffer, " Channel %02i [Output: %s]\n",
+ idx, outputs[idx] ? outputs[idx] : "???");
+ if (emu->audigy) {
+ for (idx = 0; idx < 32; idx++)
+ if (emu->efx_voices_mask[1] & (1 << idx))
+ snd_iprintf(buffer, " Channel %02i [Input: %s]\n",
+ idx + 32, inputs[idx] ? inputs[idx] : "???");
+ } else {
+ for (idx = 0; idx < 16; idx++) {
+ if (emu->efx_voices_mask[0] & ((1 << 16) << idx)) {
+ if (emu->card_capabilities->sblive51) {
+ s8 c = snd_emu10k1_sblive51_fxbus2_map[idx];
+ if (c == -1)
+ snd_iprintf(buffer, " Channel %02i [Output: %s]\n",
+ idx + 16, outputs[idx + 16]);
+ else
+ snd_iprintf(buffer, " Channel %02i [Input: %s]\n",
+ idx + 16, inputs[c]);
+ } else {
+ snd_iprintf(buffer, " Channel %02i [Input: %s]\n",
+ idx + 16, inputs[idx] ? inputs[idx] : "???");
+ }
+ }
+ }
+ }
}
- snd_iprintf(buffer, "\nAll FX Outputs :\n");
- for (idx = 0; idx < (emu->audigy ? 64 : 32); idx++)
- snd_iprintf(buffer, " Output %02i [%s]\n", idx, outputs[idx]);
}
static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry,
@@ -241,36 +164,42 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry,
struct snd_emu10k1 *emu = entry->private_data;
u32 value;
u32 value2;
- unsigned long flags;
- u32 rate;
if (emu->card_capabilities->emu_model) {
- spin_lock_irqsave(&emu->emu_lock, flags);
- snd_emu1010_fpga_read(emu, 0x38, &value);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- if ((value & 0x1) == 0) {
- spin_lock_irqsave(&emu->emu_lock, flags);
- snd_emu1010_fpga_read(emu, 0x2a, &value);
- snd_emu1010_fpga_read(emu, 0x2b, &value2);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- rate = 0x1770000 / (((value << 5) | value2)+1);
- snd_iprintf(buffer, "ADAT Locked : %u\n", rate);
- } else {
- snd_iprintf(buffer, "ADAT Unlocked\n");
- }
- spin_lock_irqsave(&emu->emu_lock, flags);
- snd_emu1010_fpga_read(emu, 0x20, &value);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- if ((value & 0x4) == 0) {
- spin_lock_irqsave(&emu->emu_lock, flags);
- snd_emu1010_fpga_read(emu, 0x28, &value);
- snd_emu1010_fpga_read(emu, 0x29, &value2);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- rate = 0x1770000 / (((value << 5) | value2)+1);
- snd_iprintf(buffer, "SPDIF Locked : %d\n", rate);
- } else {
- snd_iprintf(buffer, "SPDIF Unlocked\n");
+ guard(snd_emu1010_fpga_lock)(emu);
+
+ // This represents the S/PDIF lock status on 0404b, which is
+ // kinda weird and unhelpful, because monitoring it via IRQ is
+ // impractical (one gets an IRQ flood as long as it is desynced).
+ snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &value);
+ snd_iprintf(buffer, "Lock status 1: %#x\n", value & 0x10);
+
+ // Bit 0x1 in LO being 0 is supposedly for ADAT lock.
+ // The registers are always all zero on 0404b.
+ snd_emu1010_fpga_read(emu, EMU_HANA_LOCK_STS_LO, &value);
+ snd_emu1010_fpga_read(emu, EMU_HANA_LOCK_STS_HI, &value2);
+ snd_iprintf(buffer, "Lock status 2: %#x %#x\n", value, value2);
+
+ snd_iprintf(buffer, "S/PDIF rate: %dHz\n",
+ snd_emu1010_get_raw_rate(emu, EMU_HANA_WCLOCK_HANA_SPDIF_IN));
+ if (emu->card_capabilities->emu_model != EMU_MODEL_EMU0404) {
+ snd_iprintf(buffer, "ADAT rate: %dHz\n",
+ snd_emu1010_get_raw_rate(emu, EMU_HANA_WCLOCK_HANA_ADAT_IN));
+ snd_iprintf(buffer, "Dock rate: %dHz\n",
+ snd_emu1010_get_raw_rate(emu, EMU_HANA_WCLOCK_2ND_HANA));
}
+ if (emu->card_capabilities->emu_model == EMU_MODEL_EMU0404 ||
+ emu->card_capabilities->emu_model == EMU_MODEL_EMU1010)
+ snd_iprintf(buffer, "BNC rate: %dHz\n",
+ snd_emu1010_get_raw_rate(emu, EMU_HANA_WCLOCK_SYNC_BNC));
+
+ snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &value);
+ if (value & EMU_HANA_SPDIF_MODE_RX_INVALID)
+ snd_iprintf(buffer, "\nS/PDIF input invalid\n");
+ else
+ snd_iprintf(buffer, "\nS/PDIF mode: %s%s\n",
+ value & EMU_HANA_SPDIF_MODE_RX_PRO ? "professional" : "consumer",
+ value & EMU_HANA_SPDIF_MODE_RX_NOCOPY ? ", no copy" : "");
} else {
snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF In", CDCS, CDSRCS);
snd_emu10k1_proc_spdif_status(emu, buffer, "Optical or Coax S/PDIF In", GPSCS, GPSRCS);
@@ -278,7 +207,7 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry,
#if 0
val = snd_emu10k1_ptr_read(emu, ZVSRCS, 0);
snd_iprintf(buffer, "\nZoomed Video\n");
- snd_iprintf(buffer, "Rate Locked : %s\n", val & SRCS_RATELOCKED ? "on" : "off");
+ snd_iprintf(buffer, "Rate Locked : %s\n", str_on_off(val & SRCS_RATELOCKED));
snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", val & SRCS_ESTSAMPLERATE);
#endif
}
@@ -286,11 +215,10 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry,
static void snd_emu10k1_proc_rates_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- static int samplerate[8] = { 44100, 48000, 96000, 192000, 4, 5, 6, 7 };
+ static const int samplerate[8] = { 44100, 48000, 96000, 192000, 4, 5, 6, 7 };
struct snd_emu10k1 *emu = entry->private_data;
unsigned int val, tmp, n;
val = snd_emu10k1_ptr20_read(emu, CAPTURE_RATE_STATUS, 0);
- tmp = (val >> 16) & 0x8;
for (n = 0; n < 4; n++) {
tmp = val >> (16 + (n*4));
if (tmp & 0x8) snd_iprintf(buffer, "Channel %d: Rate=%d\n", n, samplerate[tmp & 0x7]);
@@ -298,37 +226,148 @@ static void snd_emu10k1_proc_rates_read(struct snd_info_entry *entry,
}
}
-static void snd_emu10k1_proc_acode_read(struct snd_info_entry *entry,
+struct emu10k1_reg_entry {
+ unsigned short base, size;
+ const char *name;
+};
+
+static const struct emu10k1_reg_entry sblive_reg_entries[] = {
+ { 0, 0x10, "FXBUS" },
+ { 0x10, 0x10, "EXTIN" },
+ { 0x20, 0x10, "EXTOUT" },
+ { 0x30, 0x10, "FXBUS2" },
+ { 0x40, 0x20, NULL }, // Constants
+ { 0x100, 0x100, "GPR" },
+ { 0x200, 0x80, "ITRAM_DATA" },
+ { 0x280, 0x20, "ETRAM_DATA" },
+ { 0x300, 0x80, "ITRAM_ADDR" },
+ { 0x380, 0x20, "ETRAM_ADDR" },
+ { 0x400, 0, NULL }
+};
+
+static const struct emu10k1_reg_entry audigy_reg_entries[] = {
+ { 0, 0x40, "FXBUS" },
+ { 0x40, 0x10, "EXTIN" },
+ { 0x50, 0x10, "P16VIN" },
+ { 0x60, 0x20, "EXTOUT" },
+ { 0x80, 0x20, "FXBUS2" },
+ { 0xa0, 0x10, "EMU32OUTH" },
+ { 0xb0, 0x10, "EMU32OUTL" },
+ { 0xc0, 0x20, NULL }, // Constants
+ // This can't be quite right - overlap.
+ //{ 0x100, 0xc0, "ITRAM_CTL" },
+ //{ 0x1c0, 0x40, "ETRAM_CTL" },
+ { 0x160, 0x20, "A3_EMU32IN" },
+ { 0x1e0, 0x20, "A3_EMU32OUT" },
+ { 0x200, 0xc0, "ITRAM_DATA" },
+ { 0x2c0, 0x40, "ETRAM_DATA" },
+ { 0x300, 0xc0, "ITRAM_ADDR" },
+ { 0x3c0, 0x40, "ETRAM_ADDR" },
+ { 0x400, 0x200, "GPR" },
+ { 0x600, 0, NULL }
+};
+
+static const char * const emu10k1_const_entries[] = {
+ "C_00000000",
+ "C_00000001",
+ "C_00000002",
+ "C_00000003",
+ "C_00000004",
+ "C_00000008",
+ "C_00000010",
+ "C_00000020",
+ "C_00000100",
+ "C_00010000",
+ "C_00000800",
+ "C_10000000",
+ "C_20000000",
+ "C_40000000",
+ "C_80000000",
+ "C_7fffffff",
+ "C_ffffffff",
+ "C_fffffffe",
+ "C_c0000000",
+ "C_4f1bbcdc",
+ "C_5a7ef9db",
+ "C_00100000",
+ "GPR_ACCU",
+ "GPR_COND",
+ "GPR_NOISE0",
+ "GPR_NOISE1",
+ "GPR_IRQ",
+ "GPR_DBAC",
+ "GPR_DBACE",
+ "???",
+};
+
+static int disasm_emu10k1_reg(char *buffer,
+ const struct emu10k1_reg_entry *entries,
+ unsigned reg, const char *pfx)
+{
+ for (int i = 0; ; i++) {
+ unsigned base = entries[i].base;
+ unsigned size = entries[i].size;
+ if (!size)
+ return sprintf(buffer, "%s0x%03x", pfx, reg);
+ if (reg >= base && reg < base + size) {
+ const char *name = entries[i].name;
+ reg -= base;
+ if (name)
+ return sprintf(buffer, "%s%s(%u)", pfx, name, reg);
+ return sprintf(buffer, "%s%s", pfx, emu10k1_const_entries[reg]);
+ }
+ }
+}
+
+static int disasm_sblive_reg(char *buffer, unsigned reg, const char *pfx)
+{
+ return disasm_emu10k1_reg(buffer, sblive_reg_entries, reg, pfx);
+}
+
+static int disasm_audigy_reg(char *buffer, unsigned reg, const char *pfx)
+{
+ return disasm_emu10k1_reg(buffer, audigy_reg_entries, reg, pfx);
+}
+
+static void snd_emu10k1_proc_acode_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
u32 pc;
struct snd_emu10k1 *emu = entry->private_data;
+ static const char * const insns[16] = {
+ "MAC0", "MAC1", "MAC2", "MAC3", "MACINT0", "MACINT1", "ACC3", "MACMV",
+ "ANDXOR", "TSTNEG", "LIMITGE", "LIMITLT", "LOG", "EXP", "INTERP", "SKIP",
+ };
+ static const char spaces[] = " ";
+ const int nspaces = sizeof(spaces) - 1;
snd_iprintf(buffer, "FX8010 Instruction List '%s'\n", emu->fx8010.name);
snd_iprintf(buffer, " Code dump :\n");
for (pc = 0; pc < (emu->audigy ? 1024 : 512); pc++) {
u32 low, high;
+ int len;
+ char buf[100];
+ char *bufp = buf;
low = snd_emu10k1_efx_read(emu, pc * 2);
high = snd_emu10k1_efx_read(emu, pc * 2 + 1);
- if (emu->audigy)
- snd_iprintf(buffer, " OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n",
- (high >> 24) & 0x0f,
- (high >> 12) & 0x7ff,
- (high >> 0) & 0x7ff,
- (low >> 12) & 0x7ff,
- (low >> 0) & 0x7ff,
- pc,
- high, low);
- else
- snd_iprintf(buffer, " OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n",
- (high >> 20) & 0x0f,
- (high >> 10) & 0x3ff,
- (high >> 0) & 0x3ff,
- (low >> 10) & 0x3ff,
- (low >> 0) & 0x3ff,
- pc,
- high, low);
+ if (emu->audigy) {
+ bufp += sprintf(bufp, " %-7s ", insns[(high >> 24) & 0x0f]);
+ bufp += disasm_audigy_reg(bufp, (high >> 12) & 0x7ff, "");
+ bufp += disasm_audigy_reg(bufp, (high >> 0) & 0x7ff, ", ");
+ bufp += disasm_audigy_reg(bufp, (low >> 12) & 0x7ff, ", ");
+ bufp += disasm_audigy_reg(bufp, (low >> 0) & 0x7ff, ", ");
+ } else {
+ bufp += sprintf(bufp, " %-7s ", insns[(high >> 20) & 0x0f]);
+ bufp += disasm_sblive_reg(bufp, (high >> 10) & 0x3ff, "");
+ bufp += disasm_sblive_reg(bufp, (high >> 0) & 0x3ff, ", ");
+ bufp += disasm_sblive_reg(bufp, (low >> 10) & 0x3ff, ", ");
+ bufp += disasm_sblive_reg(bufp, (low >> 0) & 0x3ff, ", ");
+ }
+ len = (int)(ptrdiff_t)(bufp - buf);
+ snd_iprintf(buffer, "%s %s /* 0x%04x: 0x%08x%08x */\n",
+ buf, &spaces[nspaces - clamp(65 - len, 0, nspaces)],
+ pc, high, low);
}
}
@@ -390,35 +429,78 @@ static void snd_emu10k1_proc_voices_read(struct snd_info_entry *entry,
struct snd_emu10k1 *emu = entry->private_data;
struct snd_emu10k1_voice *voice;
int idx;
-
- snd_iprintf(buffer, "ch\tuse\tpcm\tefx\tsynth\tmidi\n");
+ static const char * const types[] = {
+ "Unused", "EFX", "EFX IRQ", "PCM", "PCM IRQ", "Synth"
+ };
+ static_assert(ARRAY_SIZE(types) == EMU10K1_NUM_TYPES);
+
+ snd_iprintf(buffer, "ch\tdirty\tlast\tuse\n");
for (idx = 0; idx < NUM_G; idx++) {
voice = &emu->voices[idx];
- snd_iprintf(buffer, "%i\t%i\t%i\t%i\t%i\t%i\n",
+ snd_iprintf(buffer, "%i\t%u\t%u\t%s\n",
idx,
- voice->use,
- voice->pcm,
- voice->efx,
- voice->synth,
- voice->midi);
+ voice->dirty,
+ voice->last,
+ types[voice->use]);
}
}
#ifdef CONFIG_SND_DEBUG
+
+static void snd_emu_proc_emu1010_link_read(struct snd_emu10k1 *emu,
+ struct snd_info_buffer *buffer,
+ u32 dst)
+{
+ u32 src = snd_emu1010_fpga_link_dst_src_read(emu, dst);
+ snd_iprintf(buffer, "%04x: %04x\n", dst, src);
+}
+
static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_emu10k1 *emu = entry->private_data;
u32 value;
- unsigned long flags;
int i;
+
+ guard(snd_emu1010_fpga_lock)(emu);
+
snd_iprintf(buffer, "EMU1010 Registers:\n\n");
for(i = 0; i < 0x40; i+=1) {
- spin_lock_irqsave(&emu->emu_lock, flags);
snd_emu1010_fpga_read(emu, i, &value);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- snd_iprintf(buffer, "%02X: %08X, %02X\n", i, value, (value >> 8) & 0x7f);
+ snd_iprintf(buffer, "%02x: %02x\n", i, value);
+ }
+
+ snd_iprintf(buffer, "\nEMU1010 Routes:\n\n");
+
+ for (i = 0; i < 16; i++) // To Alice2/Tina[2] via EMU32
+ snd_emu_proc_emu1010_link_read(emu, buffer, i);
+ if (emu->card_capabilities->emu_model != EMU_MODEL_EMU0404)
+ for (i = 0; i < 32; i++) // To Dock via EDI
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x100 + i);
+ if (emu->card_capabilities->emu_model != EMU_MODEL_EMU1616)
+ for (i = 0; i < 8; i++) // To Hamoa/local
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x200 + i);
+ for (i = 0; i < 8; i++) // To Hamoa/Mana/local
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x300 + i);
+ if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
+ for (i = 0; i < 16; i++) // To Tina2 via EMU32
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x400 + i);
+ } else if (emu->card_capabilities->emu_model != EMU_MODEL_EMU0404) {
+ for (i = 0; i < 8; i++) // To Hana ADAT
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x400 + i);
+ if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010B) {
+ for (i = 0; i < 16; i++) // To Tina via EMU32
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x500 + i);
+ } else {
+ // To Alice2 via I2S
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x500);
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x501);
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x600);
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x601);
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x700);
+ snd_emu_proc_emu1010_link_read(emu, buffer, 0x701);
+ }
}
}
@@ -427,13 +509,10 @@ static void snd_emu_proc_io_reg_read(struct snd_info_entry *entry,
{
struct snd_emu10k1 *emu = entry->private_data;
unsigned long value;
- unsigned long flags;
int i;
snd_iprintf(buffer, "IO Registers:\n\n");
for(i = 0; i < 0x40; i+=4) {
- spin_lock_irqsave(&emu->emu_lock, flags);
value = inl(emu->port + i);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
snd_iprintf(buffer, "%02X: %08lX\n", i, value);
}
}
@@ -442,16 +521,13 @@ static void snd_emu_proc_io_reg_write(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_emu10k1 *emu = entry->private_data;
- unsigned long flags;
char line[64];
u32 reg, val;
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x", &reg, &val) != 2)
continue;
if (reg < 0x40 && val <= 0xffffffff) {
- spin_lock_irqsave(&emu->emu_lock, flags);
outl(val, emu->port + (reg & 0xfffffffc));
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
}
}
@@ -461,16 +537,13 @@ static unsigned int snd_ptr_read(struct snd_emu10k1 * emu,
unsigned int reg,
unsigned int chn)
{
- unsigned long flags;
- unsigned int regptr, val;
+ unsigned int regptr;
regptr = (reg << 16) | chn;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irq)(&emu->emu_lock);
outl(regptr, emu->port + iobase + PTR);
- val = inl(emu->port + iobase + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- return val;
+ return inl(emu->port + iobase + DATA);
}
static void snd_ptr_write(struct snd_emu10k1 *emu,
@@ -480,14 +553,12 @@ static void snd_ptr_write(struct snd_emu10k1 *emu,
unsigned int data)
{
unsigned int regptr;
- unsigned long flags;
regptr = (reg << 16) | chn;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irq)(&emu->emu_lock);
outl(regptr, emu->port + iobase + PTR);
outl(data, emu->port + iobase + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
@@ -505,10 +576,7 @@ static void snd_emu_proc_ptr_reg_read(struct snd_info_entry *entry,
for(i = offset; i < offset+length; i++) {
snd_iprintf(buffer, "%02X: ",i);
for (j = 0; j < voices; j++) {
- if(iobase == 0)
- value = snd_ptr_read(emu, 0, i, j);
- else
- value = snd_ptr_read(emu, 0x20, i, j);
+ value = snd_ptr_read(emu, iobase, i, j);
snd_iprintf(buffer, "%08lX ", value);
}
snd_iprintf(buffer, "\n");
@@ -516,7 +584,8 @@ static void snd_emu_proc_ptr_reg_read(struct snd_info_entry *entry,
}
static void snd_emu_proc_ptr_reg_write(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer, int iobase)
+ struct snd_info_buffer *buffer,
+ int iobase, int length, int voices)
{
struct snd_emu10k1 *emu = entry->private_data;
char line[64];
@@ -524,7 +593,7 @@ static void snd_emu_proc_ptr_reg_write(struct snd_info_entry *entry,
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
continue;
- if (reg < 0xa0 && val <= 0xffffffff && channel_id <= 3)
+ if (reg < length && channel_id < voices)
snd_ptr_write(emu, iobase, reg, channel_id, val);
}
}
@@ -532,13 +601,15 @@ static void snd_emu_proc_ptr_reg_write(struct snd_info_entry *entry,
static void snd_emu_proc_ptr_reg_write00(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- snd_emu_proc_ptr_reg_write(entry, buffer, 0);
+ snd_emu_proc_ptr_reg_write(entry, buffer, 0, 0x80, 64);
}
static void snd_emu_proc_ptr_reg_write20(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- snd_emu_proc_ptr_reg_write(entry, buffer, 0x20);
+ struct snd_emu10k1 *emu = entry->private_data;
+ snd_emu_proc_ptr_reg_write(entry, buffer, 0x20,
+ emu->card_capabilities->ca0108_chip ? 0xa0 : 0x80, 4);
}
@@ -573,99 +644,83 @@ static void snd_emu_proc_ptr_reg_read20c(struct snd_info_entry *entry,
}
#endif
-static struct snd_info_entry_ops snd_emu10k1_proc_ops_fx8010 = {
+static const struct snd_info_entry_ops snd_emu10k1_proc_ops_fx8010 = {
.read = snd_emu10k1_fx8010_read,
};
-int __devinit snd_emu10k1_proc_init(struct snd_emu10k1 * emu)
+int snd_emu10k1_proc_init(struct snd_emu10k1 *emu)
{
struct snd_info_entry *entry;
#ifdef CONFIG_SND_DEBUG
if (emu->card_capabilities->emu_model) {
- if (! snd_card_proc_new(emu->card, "emu1010_regs", &entry))
- snd_info_set_text_ops(entry, emu, snd_emu_proc_emu1010_reg_read);
- }
- if (! snd_card_proc_new(emu->card, "io_regs", &entry)) {
- snd_info_set_text_ops(entry, emu, snd_emu_proc_io_reg_read);
- entry->c.text.write = snd_emu_proc_io_reg_write;
- entry->mode |= S_IWUSR;
+ snd_card_ro_proc_new(emu->card, "emu1010_regs",
+ emu, snd_emu_proc_emu1010_reg_read);
}
- if (! snd_card_proc_new(emu->card, "ptr_regs00a", &entry)) {
- snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00a);
- entry->c.text.write = snd_emu_proc_ptr_reg_write00;
- entry->mode |= S_IWUSR;
- }
- if (! snd_card_proc_new(emu->card, "ptr_regs00b", &entry)) {
- snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00b);
- entry->c.text.write = snd_emu_proc_ptr_reg_write00;
- entry->mode |= S_IWUSR;
- }
- if (! snd_card_proc_new(emu->card, "ptr_regs20a", &entry)) {
- snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20a);
- entry->c.text.write = snd_emu_proc_ptr_reg_write20;
- entry->mode |= S_IWUSR;
- }
- if (! snd_card_proc_new(emu->card, "ptr_regs20b", &entry)) {
- snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20b);
- entry->c.text.write = snd_emu_proc_ptr_reg_write20;
- entry->mode |= S_IWUSR;
- }
- if (! snd_card_proc_new(emu->card, "ptr_regs20c", &entry)) {
- snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20c);
- entry->c.text.write = snd_emu_proc_ptr_reg_write20;
- entry->mode |= S_IWUSR;
+ snd_card_rw_proc_new(emu->card, "io_regs", emu,
+ snd_emu_proc_io_reg_read,
+ snd_emu_proc_io_reg_write);
+ snd_card_rw_proc_new(emu->card, "ptr_regs00a", emu,
+ snd_emu_proc_ptr_reg_read00a,
+ snd_emu_proc_ptr_reg_write00);
+ snd_card_rw_proc_new(emu->card, "ptr_regs00b", emu,
+ snd_emu_proc_ptr_reg_read00b,
+ snd_emu_proc_ptr_reg_write00);
+ if (!emu->card_capabilities->emu_model &&
+ (emu->card_capabilities->ca0151_chip || emu->card_capabilities->ca0108_chip)) {
+ snd_card_rw_proc_new(emu->card, "ptr_regs20a", emu,
+ snd_emu_proc_ptr_reg_read20a,
+ snd_emu_proc_ptr_reg_write20);
+ snd_card_rw_proc_new(emu->card, "ptr_regs20b", emu,
+ snd_emu_proc_ptr_reg_read20b,
+ snd_emu_proc_ptr_reg_write20);
+ if (emu->card_capabilities->ca0108_chip)
+ snd_card_rw_proc_new(emu->card, "ptr_regs20c", emu,
+ snd_emu_proc_ptr_reg_read20c,
+ snd_emu_proc_ptr_reg_write20);
}
#endif
- if (! snd_card_proc_new(emu->card, "emu10k1", &entry))
- snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_read);
+ snd_card_ro_proc_new(emu->card, "emu10k1", emu, snd_emu10k1_proc_read);
- if (emu->card_capabilities->emu10k2_chip) {
- if (! snd_card_proc_new(emu->card, "spdif-in", &entry))
- snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_spdif_read);
- }
- if (emu->card_capabilities->ca0151_chip) {
- if (! snd_card_proc_new(emu->card, "capture-rates", &entry))
- snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_rates_read);
- }
+ if (emu->card_capabilities->emu10k2_chip)
+ snd_card_ro_proc_new(emu->card, "spdif-in", emu,
+ snd_emu10k1_proc_spdif_read);
+ if (emu->card_capabilities->ca0151_chip)
+ snd_card_ro_proc_new(emu->card, "capture-rates", emu,
+ snd_emu10k1_proc_rates_read);
- if (! snd_card_proc_new(emu->card, "voices", &entry))
- snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_voices_read);
+ snd_card_ro_proc_new(emu->card, "voices", emu,
+ snd_emu10k1_proc_voices_read);
if (! snd_card_proc_new(emu->card, "fx8010_gpr", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = emu;
- entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+ entry->mode = S_IFREG | 0444 /*| S_IWUSR*/;
entry->size = emu->audigy ? A_TOTAL_SIZE_GPR : TOTAL_SIZE_GPR;
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
}
if (! snd_card_proc_new(emu->card, "fx8010_tram_data", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = emu;
- entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+ entry->mode = S_IFREG | 0444 /*| S_IWUSR*/;
entry->size = emu->audigy ? A_TOTAL_SIZE_TANKMEM_DATA : TOTAL_SIZE_TANKMEM_DATA ;
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
}
if (! snd_card_proc_new(emu->card, "fx8010_tram_addr", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = emu;
- entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+ entry->mode = S_IFREG | 0444 /*| S_IWUSR*/;
entry->size = emu->audigy ? A_TOTAL_SIZE_TANKMEM_ADDR : TOTAL_SIZE_TANKMEM_ADDR ;
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
}
if (! snd_card_proc_new(emu->card, "fx8010_code", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = emu;
- entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+ entry->mode = S_IFREG | 0444 /*| S_IWUSR*/;
entry->size = emu->audigy ? A_TOTAL_SIZE_CODE : TOTAL_SIZE_CODE;
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
}
- if (! snd_card_proc_new(emu->card, "fx8010_acode", &entry)) {
- entry->content = SNDRV_INFO_CONTENT_TEXT;
- entry->private_data = emu;
- entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
- entry->c.text.read = snd_emu10k1_proc_acode_read;
- }
+ snd_card_ro_proc_new(emu->card, "fx8010_acode", emu,
+ snd_emu10k1_proc_acode_read);
return 0;
}
-#endif /* CONFIG_PROC_FS */
diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c
index 5ef7080e14d0..9c897c3e8c28 100644
--- a/sound/pci/emu10k1/io.c
+++ b/sound/pci/emu10k1/io.c
@@ -1,63 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * Lee Revell <rlrevell@joe-job.com>
+ * James Courtier-Dutton <James@superbug.co.uk>
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
* Creative Labs, Inc.
- * Routines for control of EMU10K1 chips
- *
- * BUGS:
- * --
- *
- * TODO:
- * --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
+ * Routines for control of EMU10K1 chips
*/
#include <linux/time.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
#include <linux/delay.h>
+#include <linux/export.h>
#include "p17v.h"
+static inline bool check_ptr_reg(struct snd_emu10k1 *emu, unsigned int reg)
+{
+ if (snd_BUG_ON(!emu))
+ return false;
+ if (snd_BUG_ON(reg & (emu->audigy ? (0xffff0000 & ~A_PTR_ADDRESS_MASK)
+ : (0xffff0000 & ~PTR_ADDRESS_MASK))))
+ return false;
+ if (snd_BUG_ON(reg & 0x0000ffff & ~PTR_CHANNELNUM_MASK))
+ return false;
+ return true;
+}
+
unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
{
- unsigned long flags;
unsigned int regptr, val;
unsigned int mask;
- mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
- regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
+ regptr = (reg << 16) | chn;
+ if (!check_ptr_reg(emu, regptr))
+ return 0;
+
+ scoped_guard(spinlock_irqsave, &emu->emu_lock) {
+ outl(regptr, emu->port + PTR);
+ val = inl(emu->port + DATA);
+ }
if (reg & 0xff000000) {
unsigned char size, offset;
size = (reg >> 24) & 0x3f;
offset = (reg >> 16) & 0x1f;
- mask = ((1 << size) - 1) << offset;
+ mask = (1 << size) - 1;
- spin_lock_irqsave(&emu->emu_lock, flags);
- outl(regptr, emu->port + PTR);
- val = inl(emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
-
- return (val & mask) >> offset;
+ return (val >> offset) & mask;
} else {
- spin_lock_irqsave(&emu->emu_lock, flags);
- outl(regptr, emu->port + PTR);
- val = inl(emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
return val;
}
}
@@ -67,54 +60,74 @@ EXPORT_SYMBOL(snd_emu10k1_ptr_read);
void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
{
unsigned int regptr;
- unsigned long flags;
unsigned int mask;
- if (!emu) {
- snd_printk(KERN_ERR "ptr_write: emu is null!\n");
- dump_stack();
+ regptr = (reg << 16) | chn;
+ if (!check_ptr_reg(emu, regptr))
return;
- }
- mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
- regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
+ guard(spinlock_irqsave)(&emu->emu_lock);
if (reg & 0xff000000) {
unsigned char size, offset;
size = (reg >> 24) & 0x3f;
offset = (reg >> 16) & 0x1f;
- mask = ((1 << size) - 1) << offset;
- data = (data << offset) & mask;
+ mask = (1 << size) - 1;
+ if (snd_BUG_ON(data & ~mask))
+ return;
+ mask <<= offset;
+ data <<= offset;
- spin_lock_irqsave(&emu->emu_lock, flags);
outl(regptr, emu->port + PTR);
data |= inl(emu->port + DATA) & ~mask;
- outl(data, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
} else {
- spin_lock_irqsave(&emu->emu_lock, flags);
outl(regptr, emu->port + PTR);
- outl(data, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
+ outl(data, emu->port + DATA);
}
EXPORT_SYMBOL(snd_emu10k1_ptr_write);
+void snd_emu10k1_ptr_write_multiple(struct snd_emu10k1 *emu, unsigned int chn, ...)
+{
+ va_list va;
+ u32 addr_mask;
+
+ if (snd_BUG_ON(!emu))
+ return;
+ if (snd_BUG_ON(chn & ~PTR_CHANNELNUM_MASK))
+ return;
+ addr_mask = ~((emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK) >> 16);
+
+ va_start(va, chn);
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ for (;;) {
+ u32 data;
+ u32 reg = va_arg(va, u32);
+ if (reg == REGLIST_END)
+ break;
+ data = va_arg(va, u32);
+ if (snd_BUG_ON(reg & addr_mask)) // Only raw registers supported here
+ continue;
+ outl((reg << 16) | chn, emu->port + PTR);
+ outl(data, emu->port + DATA);
+ }
+ va_end(va);
+}
+
+EXPORT_SYMBOL(snd_emu10k1_ptr_write_multiple);
+
unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,
unsigned int reg,
unsigned int chn)
{
- unsigned long flags;
- unsigned int regptr, val;
+ unsigned int regptr;
regptr = (reg << 16) | chn;
- spin_lock_irqsave(&emu->emu_lock, flags);
- outl(regptr, emu->port + 0x20 + PTR);
- val = inl(emu->port + 0x20 + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- return val;
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ outl(regptr, emu->port + PTR2);
+ return inl(emu->port + DATA2);
}
void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu,
@@ -123,14 +136,12 @@ void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu,
unsigned int data)
{
unsigned int regptr;
- unsigned long flags;
regptr = (reg << 16) | chn;
- spin_lock_irqsave(&emu->emu_lock, flags);
- outl(regptr, emu->port + 0x20 + PTR);
- outl(data, emu->port + 0x20 + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ outl(regptr, emu->port + PTR2);
+ outl(data, emu->port + DATA2);
}
int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
@@ -139,22 +150,19 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
unsigned int reset, set;
unsigned int reg, tmp;
int n, result;
- int err = 0;
/* This function is not re-entrant, so protect against it. */
- spin_lock(&emu->spi_lock);
+ guard(spinlock)(&emu->spi_lock);
if (emu->card_capabilities->ca0108_chip)
- reg = 0x3c; /* PTR20, reg 0x3c */
+ reg = P17V_SPI;
else {
/* For other chip types the SPI register
* is currently unknown. */
- err = 1;
- goto spi_write_exit;
+ return 1;
}
if (data > 0xffff) {
/* Only 16bit values allowed */
- err = 1;
- goto spi_write_exit;
+ return 1;
}
tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
@@ -175,15 +183,11 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
}
if (result) {
/* Timed out */
- err = 1;
- goto spi_write_exit;
+ return 1;
}
snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
- err = 0;
-spi_write_exit:
- spin_unlock(&emu->spi_lock);
- return err;
+ return 0;
}
/* The ADC does not support i2c read, so only write is implemented */
@@ -195,15 +199,14 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
int timeout = 0;
int status;
int retry;
- int err = 0;
if ((reg > 0x7f) || (value > 0x1ff)) {
- snd_printk(KERN_ERR "i2c_write: invalid values.\n");
+ dev_err(emu->card->dev, "i2c_write: invalid values.\n");
return -EINVAL;
}
/* This function is not re-entrant, so protect against it. */
- spin_lock(&emu->i2c_lock);
+ guard(spinlock)(&emu->i2c_lock);
tmp = reg << 25 | value << 16;
@@ -226,7 +229,7 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
break;
if (timeout > 1000) {
- snd_printk(KERN_WARNING
+ dev_warn(emu->card->dev,
"emu10k1:I2C:timeout status=0x%x\n",
status);
break;
@@ -238,98 +241,242 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
}
if (retry == 10) {
- snd_printk(KERN_ERR "Writing to ADC failed!\n");
- snd_printk(KERN_ERR "status=0x%x, reg=%d, value=%d\n",
+ dev_err(emu->card->dev, "Writing to ADC failed!\n");
+ dev_err(emu->card->dev, "status=0x%x, reg=%d, value=%d\n",
status, reg, value);
/* dump_stack(); */
- err = -EINVAL;
+ return -EINVAL;
}
- spin_unlock(&emu->i2c_lock);
- return err;
+ return 0;
}
-int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
+static void snd_emu1010_fpga_write_locked(struct snd_emu10k1 *emu, u32 reg, u32 value)
{
- unsigned long flags;
-
- if (reg > 0x3f)
- return 1;
+ if (snd_BUG_ON(reg > 0x3f))
+ return;
reg += 0x40; /* 0x40 upwards are registers. */
- if (value > 0x3f) /* 0 to 0x3f are values */
- return 1;
- spin_lock_irqsave(&emu->emu_lock, flags);
- outl(reg, emu->port + A_IOCFG);
+ if (snd_BUG_ON(value > 0x3f)) /* 0 to 0x3f are values */
+ return;
+ outw(reg, emu->port + A_GPIO);
udelay(10);
- outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
+ outw(reg | 0x80, emu->port + A_GPIO); /* High bit clocks the value into the fpga. */
udelay(10);
- outl(value, emu->port + A_IOCFG);
+ outw(value, emu->port + A_GPIO);
udelay(10);
- outl(value | 0x80 , emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+ outw(value | 0x80 , emu->port + A_GPIO); /* High bit clocks the value into the fpga. */
+ udelay(10);
+}
- return 0;
+void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value)
+{
+ if (snd_BUG_ON(!mutex_is_locked(&emu->emu1010.lock)))
+ return;
+ snd_emu1010_fpga_write_locked(emu, reg, value);
}
-int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value)
+void snd_emu1010_fpga_write_lock(struct snd_emu10k1 *emu, u32 reg, u32 value)
{
- unsigned long flags;
- if (reg > 0x3f)
- return 1;
+ guard(snd_emu1010_fpga_lock)(emu);
+ snd_emu1010_fpga_write_locked(emu, reg, value);
+}
+
+void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value)
+{
+ // The higest input pin is used as the designated interrupt trigger,
+ // so it needs to be masked out.
+ // But note that any other input pin change will also cause an IRQ,
+ // so using this function often causes an IRQ as a side effect.
+ u32 mask = emu->card_capabilities->ca0108_chip ? 0x1f : 0x7f;
+
+ if (snd_BUG_ON(!mutex_is_locked(&emu->emu1010.lock)))
+ return;
+ if (snd_BUG_ON(reg > 0x3f))
+ return;
reg += 0x40; /* 0x40 upwards are registers. */
- spin_lock_irqsave(&emu->emu_lock, flags);
- outl(reg, emu->port + A_IOCFG);
+ outw(reg, emu->port + A_GPIO);
udelay(10);
- outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
+ outw(reg | 0x80, emu->port + A_GPIO); /* High bit clocks the value into the fpga. */
udelay(10);
- *value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
-
- return 0;
+ *value = ((inw(emu->port + A_GPIO) >> 8) & mask);
}
/* Each Destination has one and only one Source,
* but one Source can feed any number of Destinations simultaneously.
*/
-int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, u32 dst, u32 src)
+void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src)
{
- snd_emu1010_fpga_write(emu, 0x00, ((dst >> 8) & 0x3f) );
- snd_emu1010_fpga_write(emu, 0x01, (dst & 0x3f) );
- snd_emu1010_fpga_write(emu, 0x02, ((src >> 8) & 0x3f) );
- snd_emu1010_fpga_write(emu, 0x03, (src & 0x3f) );
+ if (snd_BUG_ON(dst & ~0x71f))
+ return;
+ if (snd_BUG_ON(src & ~0x71f))
+ return;
+ snd_emu1010_fpga_write(emu, EMU_HANA_DESTHI, dst >> 8);
+ snd_emu1010_fpga_write(emu, EMU_HANA_DESTLO, dst & 0x1f);
+ snd_emu1010_fpga_write(emu, EMU_HANA_SRCHI, src >> 8);
+ snd_emu1010_fpga_write(emu, EMU_HANA_SRCLO, src & 0x1f);
+}
- return 0;
+u32 snd_emu1010_fpga_link_dst_src_read(struct snd_emu10k1 *emu, u32 dst)
+{
+ u32 hi, lo;
+
+ if (snd_BUG_ON(dst & ~0x71f))
+ return 0;
+ snd_emu1010_fpga_write(emu, EMU_HANA_DESTHI, dst >> 8);
+ snd_emu1010_fpga_write(emu, EMU_HANA_DESTLO, dst & 0x1f);
+ snd_emu1010_fpga_read(emu, EMU_HANA_SRCHI, &hi);
+ snd_emu1010_fpga_read(emu, EMU_HANA_SRCLO, &lo);
+ return (hi << 8) | lo;
+}
+
+int snd_emu1010_get_raw_rate(struct snd_emu10k1 *emu, u8 src)
+{
+ u32 reg_lo, reg_hi, value, value2;
+
+ switch (src) {
+ case EMU_HANA_WCLOCK_HANA_SPDIF_IN:
+ snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &value);
+ if (value & EMU_HANA_SPDIF_MODE_RX_INVALID)
+ return 0;
+ reg_lo = EMU_HANA_WC_SPDIF_LO;
+ reg_hi = EMU_HANA_WC_SPDIF_HI;
+ break;
+ case EMU_HANA_WCLOCK_HANA_ADAT_IN:
+ reg_lo = EMU_HANA_WC_ADAT_LO;
+ reg_hi = EMU_HANA_WC_ADAT_HI;
+ break;
+ case EMU_HANA_WCLOCK_SYNC_BNC:
+ reg_lo = EMU_HANA_WC_BNC_LO;
+ reg_hi = EMU_HANA_WC_BNC_HI;
+ break;
+ case EMU_HANA_WCLOCK_2ND_HANA:
+ reg_lo = EMU_HANA2_WC_SPDIF_LO;
+ reg_hi = EMU_HANA2_WC_SPDIF_HI;
+ break;
+ default:
+ return 0;
+ }
+ snd_emu1010_fpga_read(emu, reg_hi, &value);
+ snd_emu1010_fpga_read(emu, reg_lo, &value2);
+ // FIXME: The /4 is valid for 0404b, but contradicts all other info.
+ return 0x1770000 / 4 / (((value << 5) | value2) + 1);
+}
+
+void snd_emu1010_update_clock(struct snd_emu10k1 *emu)
+{
+ int clock;
+ u32 leds;
+
+ switch (emu->emu1010.wclock) {
+ case EMU_HANA_WCLOCK_INT_44_1K | EMU_HANA_WCLOCK_1X:
+ clock = 44100;
+ leds = EMU_HANA_DOCK_LEDS_2_44K;
+ break;
+ case EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_1X:
+ clock = 48000;
+ leds = EMU_HANA_DOCK_LEDS_2_48K;
+ break;
+ default:
+ clock = snd_emu1010_get_raw_rate(
+ emu, emu->emu1010.wclock & EMU_HANA_WCLOCK_SRC_MASK);
+ // The raw rate reading is rather coarse (it cannot accurately
+ // represent 44.1 kHz) and fluctuates slightly. Luckily, the
+ // clock comes from digital inputs, which use standardized rates.
+ // So we round to the closest standard rate and ignore discrepancies.
+ if (clock < 46000) {
+ clock = 44100;
+ leds = EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_44K;
+ } else {
+ clock = 48000;
+ leds = EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_48K;
+ }
+ break;
+ }
+ emu->emu1010.word_clock = clock;
+
+ // FIXME: this should probably represent the AND of all currently
+ // used sources' lock status. But we don't know how to get that ...
+ leds |= EMU_HANA_DOCK_LEDS_2_LOCK;
+
+ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, leds);
+}
+
+void snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, int dock,
+ const struct firmware *fw_entry)
+{
+ __always_unused u16 write_post;
+
+ // On E-MU 1010 rev1 the FPGA is a Xilinx Spartan IIE XC2S50E.
+ // On E-MU 0404b it is a Xilinx Spartan III XC3S50.
+ // The wiring is as follows:
+ // GPO7 -> FPGA input & 1K resistor -> FPGA /PGMN <- FPGA output
+ // In normal operation, the active low reset line is held up by
+ // an FPGA output, while the GPO pin performs its duty as control
+ // register access strobe signal. Writing the respective bit to
+ // EMU_HANA_FPGA_CONFIG puts the FPGA output into high-Z mode, at
+ // which point the GPO pin can control the reset line through the
+ // resistor.
+ // GPO6 -> FPGA CCLK & FPGA input
+ // GPO5 -> FPGA DIN (dual function)
+
+ // If the FPGA is already programmed, return it to programming mode
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG,
+ dock ? EMU_HANA_FPGA_CONFIG_AUDIODOCK :
+ EMU_HANA_FPGA_CONFIG_HANA);
+
+ // Assert reset line for 100uS
+ outw(0x00, emu->port + A_GPIO);
+ write_post = inw(emu->port + A_GPIO);
+ udelay(100);
+ outw(0x80, emu->port + A_GPIO);
+ write_post = inw(emu->port + A_GPIO);
+ udelay(100); // Allow FPGA memory to clean
+
+ // Upload the netlist. Keep reset line high!
+ for (int n = 0; n < fw_entry->size; n++) {
+ u8 value = fw_entry->data[n];
+ for (int i = 0; i < 8; i++) {
+ u16 reg = 0x80;
+ if (value & 1)
+ reg |= 0x20;
+ value >>= 1;
+ outw(reg, emu->port + A_GPIO);
+ write_post = inw(emu->port + A_GPIO);
+ outw(reg | 0x40, emu->port + A_GPIO);
+ write_post = inw(emu->port + A_GPIO);
+ }
+ }
+
+ // After programming, set GPIO bit 4 high again.
+ // This appears to be a config word that the rev1 Hana
+ // firmware reads; weird things happen without this.
+ outw(0x10, emu->port + A_GPIO);
+ write_post = inw(emu->port + A_GPIO);
}
void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
{
- unsigned long flags;
unsigned int enable;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
enable = inl(emu->port + INTE) | intrenb;
outl(enable, emu->port + INTE);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
{
- unsigned long flags;
unsigned int enable;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
enable = inl(emu->port + INTE) & ~intrenb;
outl(enable, emu->port + INTE);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
{
- unsigned long flags;
unsigned int val;
- spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
+ guard(spinlock_irqsave)(&emu->emu_lock);
if (voicenum >= 32) {
outl(CLIEH << 16, emu->port + PTR);
val = inl(emu->port + DATA);
@@ -340,16 +487,13 @@ void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenu
val |= 1 << voicenum;
}
outl(val, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
{
- unsigned long flags;
unsigned int val;
- spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
+ guard(spinlock_irqsave)(&emu->emu_lock);
if (voicenum >= 32) {
outl(CLIEH << 16, emu->port + PTR);
val = inl(emu->port + DATA);
@@ -360,15 +504,11 @@ void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicen
val &= ~(1 << voicenum);
}
outl(val, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
{
- unsigned long flags;
-
- spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
+ guard(spinlock_irqsave)(&emu->emu_lock);
if (voicenum >= 32) {
outl(CLIPH << 16, emu->port + PTR);
voicenum = 1 << (voicenum - 32);
@@ -377,16 +517,13 @@ void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
voicenum = 1 << voicenum;
}
outl(voicenum, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
{
- unsigned long flags;
unsigned int val;
- spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
+ guard(spinlock_irqsave)(&emu->emu_lock);
if (voicenum >= 32) {
outl(HLIEH << 16, emu->port + PTR);
val = inl(emu->port + DATA);
@@ -397,16 +534,13 @@ void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned i
val |= 1 << voicenum;
}
outl(val, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
{
- unsigned long flags;
unsigned int val;
- spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
+ guard(spinlock_irqsave)(&emu->emu_lock);
if (voicenum >= 32) {
outl(HLIEH << 16, emu->port + PTR);
val = inl(emu->port + DATA);
@@ -417,15 +551,11 @@ void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned
val &= ~(1 << voicenum);
}
outl(val, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
{
- unsigned long flags;
-
- spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
+ guard(spinlock_irqsave)(&emu->emu_lock);
if (voicenum >= 32) {
outl(HLIPH << 16, emu->port + PTR);
voicenum = 1 << (voicenum - 32);
@@ -434,16 +564,14 @@ void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int
voicenum = 1 << voicenum;
}
outl(voicenum, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
+#if 0
void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
{
- unsigned long flags;
unsigned int sol;
- spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
+ guard(spinlock_irqsave)(&emu->emu_lock);
if (voicenum >= 32) {
outl(SOLEH << 16, emu->port + PTR);
sol = inl(emu->port + DATA);
@@ -454,16 +582,13 @@ void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voice
sol |= 1 << voicenum;
}
outl(sol, emu->port + DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
{
- unsigned long flags;
unsigned int sol;
- spin_lock_irqsave(&emu->emu_lock, flags);
- /* voice interrupt */
+ guard(spinlock_irqsave)(&emu->emu_lock);
if (voicenum >= 32) {
outl(SOLEH << 16, emu->port + PTR);
sol = inl(emu->port + DATA);
@@ -474,7 +599,83 @@ void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voi
sol &= ~(1 << voicenum);
}
outl(sol, emu->port + DATA);
+}
+#endif
+
+void snd_emu10k1_voice_set_loop_stop_multiple(struct snd_emu10k1 *emu, u64 voices)
+{
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ outl(SOLEL << 16, emu->port + PTR);
+ outl(inl(emu->port + DATA) | (u32)voices, emu->port + DATA);
+ outl(SOLEH << 16, emu->port + PTR);
+ outl(inl(emu->port + DATA) | (u32)(voices >> 32), emu->port + DATA);
+}
+
+void snd_emu10k1_voice_clear_loop_stop_multiple(struct snd_emu10k1 *emu, u64 voices)
+{
+ guard(spinlock_irqsave)(&emu->emu_lock);
+ outl(SOLEL << 16, emu->port + PTR);
+ outl(inl(emu->port + DATA) & (u32)~voices, emu->port + DATA);
+ outl(SOLEH << 16, emu->port + PTR);
+ outl(inl(emu->port + DATA) & (u32)(~voices >> 32), emu->port + DATA);
+}
+
+int snd_emu10k1_voice_clear_loop_stop_multiple_atomic(struct snd_emu10k1 *emu, u64 voices)
+{
+ unsigned long flags;
+ u32 soll, solh;
+ int ret = -EIO;
+
+ spin_lock_irqsave(&emu->emu_lock, flags);
+
+ outl(SOLEL << 16, emu->port + PTR);
+ soll = inl(emu->port + DATA);
+ outl(SOLEH << 16, emu->port + PTR);
+ solh = inl(emu->port + DATA);
+
+ soll &= (u32)~voices;
+ solh &= (u32)(~voices >> 32);
+
+ for (int tries = 0; tries < 1000; tries++) {
+ const u32 quart = 1U << (REG_SIZE(WC_CURRENTCHANNEL) - 2);
+ // First we wait for the third quarter of the sample cycle ...
+ u32 wc = inl(emu->port + WC);
+ u32 cc = REG_VAL_GET(WC_CURRENTCHANNEL, wc);
+ if (cc >= quart * 2 && cc < quart * 3) {
+ // ... and release the low voices, while the high ones are serviced.
+ outl(SOLEL << 16, emu->port + PTR);
+ outl(soll, emu->port + DATA);
+ // Then we wait for the first quarter of the next sample cycle ...
+ for (; tries < 1000; tries++) {
+ cc = REG_VAL_GET(WC_CURRENTCHANNEL, inl(emu->port + WC));
+ if (cc < quart)
+ goto good;
+ // We will block for 10+ us with interrupts disabled. This is
+ // not nice at all, but necessary for reasonable reliability.
+ udelay(1);
+ }
+ break;
+ good:
+ // ... and release the high voices, while the low ones are serviced.
+ outl(SOLEH << 16, emu->port + PTR);
+ outl(solh, emu->port + DATA);
+ // Finally we verify that nothing interfered in fact.
+ if (REG_VAL_GET(WC_SAMPLECOUNTER, inl(emu->port + WC)) ==
+ ((REG_VAL_GET(WC_SAMPLECOUNTER, wc) + 1) & REG_MASK0(WC_SAMPLECOUNTER))) {
+ ret = 0;
+ } else {
+ ret = -EAGAIN;
+ }
+ break;
+ }
+ // Don't block for too long
+ spin_unlock_irqrestore(&emu->emu_lock, flags);
+ udelay(1);
+ spin_lock_irqsave(&emu->emu_lock, flags);
+ }
+
spin_unlock_irqrestore(&emu->emu_lock, flags);
+ return ret;
}
void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
@@ -499,84 +700,17 @@ void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
struct snd_emu10k1 *emu = ac97->private_data;
- unsigned long flags;
- unsigned short val;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
outb(reg, emu->port + AC97ADDRESS);
- val = inw(emu->port + AC97DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
- return val;
+ return inw(emu->port + AC97DATA);
}
void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
{
struct snd_emu10k1 *emu = ac97->private_data;
- unsigned long flags;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
outb(reg, emu->port + AC97ADDRESS);
outw(data, emu->port + AC97DATA);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
-}
-
-/*
- * convert rate to pitch
- */
-
-unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
-{
- static u32 logMagTable[128] = {
- 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
- 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
- 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
- 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
- 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
- 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
- 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
- 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
- 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
- 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
- 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
- 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
- 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
- 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
- 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
- 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
- };
- static char logSlopeTable[128] = {
- 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
- 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
- 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
- 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
- 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
- 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
- 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
- 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
- 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
- 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
- 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
- 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
- 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
- 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
- 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
- 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
- };
- int i;
-
- if (rate == 0)
- return 0; /* Bail out if no leading "1" */
- rate *= 11185; /* Scale 48000 to 0x20002380 */
- for (i = 31; i > 0; i--) {
- if (rate & 0x80000000) { /* Detect leading "1" */
- return (((unsigned int) (i - 15) << 20) +
- logMagTable[0x7f & (rate >> 24)] +
- (0x7f & (rate >> 17)) *
- logSlopeTable[0x7f & (rate >> 24)]);
- }
- rate <<= 1;
- }
-
- return 0; /* Should never reach this point */
}
-
diff --git a/sound/pci/emu10k1/irq.c b/sound/pci/emu10k1/irq.c
index 30bfed6f8339..71aa90b9cc88 100644
--- a/sound/pci/emu10k1/irq.c
+++ b/sound/pci/emu10k1/irq.c
@@ -1,28 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Creative Labs, Inc.
* Routines for IRQ control of EMU10K1 chips
- *
- * BUGS:
- * --
- *
- * TODO:
- * --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/time.h>
@@ -32,20 +12,24 @@
irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
{
struct snd_emu10k1 *emu = dev_id;
- unsigned int status, status2, orig_status, orig_status2;
+ unsigned int status, orig_status;
int handled = 0;
int timeout = 0;
- while (((status = inl(emu->port + IPR)) != 0) && (timeout < 1000)) {
- timeout++;
- orig_status = status;
+ while ((status = inl(emu->port + IPR)) != 0) {
handled = 1;
if ((status & 0xffffffff) == 0xffffffff) {
- snd_printk(KERN_INFO "snd-emu10k1: Suspected sound card removal\n");
+ dev_info(emu->card->dev,
+ "Suspected sound card removal\n");
+ break;
+ }
+ if (++timeout == 1000) {
+ dev_info(emu->card->dev, "emu10k1 irq routine failure\n");
break;
}
+ orig_status = status;
if (status & IPR_PCIERROR) {
- snd_printk(KERN_ERR "interrupt: PCI error\n");
+ dev_err(emu->card->dev, "interrupt: PCI error\n");
snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE);
status &= ~IPR_PCIERROR;
}
@@ -57,12 +41,13 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
status &= ~(IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE);
}
if (status & IPR_CHANNELLOOP) {
+ struct snd_emu10k1_voice *pvoice;
int voice;
int voice_max = status & IPR_CHANNELNUMBERMASK;
u32 val;
- struct snd_emu10k1_voice *pvoice = emu->voices;
val = snd_emu10k1_ptr_read(emu, CLIPL, 0);
+ pvoice = emu->voices;
for (voice = 0; voice <= voice_max; voice++) {
if (voice == 0x20)
val = snd_emu10k1_ptr_read(emu, CLIPH, 0);
@@ -78,6 +63,7 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
pvoice++;
}
val = snd_emu10k1_ptr_read(emu, HLIPL, 0);
+ pvoice = emu->voices;
for (voice = 0; voice <= voice_max; voice++) {
if (voice == 0x20)
val = snd_emu10k1_ptr_read(emu, HLIPH, 0);
@@ -92,9 +78,8 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
val >>= 1;
pvoice++;
}
- status &= ~IPR_CHANNELLOOP;
+ status &= ~(IPR_CHANNELLOOP | IPR_CHANNELNUMBERMASK);
}
- status &= ~IPR_CHANNELNUMBERMASK;
if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) {
if (emu->capture_interrupt)
emu->capture_interrupt(emu, status);
@@ -152,57 +137,26 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
status &= ~IPR_FXDSP;
}
if (status & IPR_P16V) {
- while ((status2 = inl(emu->port + IPR2)) != 0) {
- u32 mask = INTE2_PLAYBACK_CH_0_LOOP; /* Full Loop */
- struct snd_emu10k1_voice *pvoice = &(emu->p16v_voices[0]);
- struct snd_emu10k1_voice *cvoice = &(emu->p16v_capture_voice);
-
- //printk(KERN_INFO "status2=0x%x\n", status2);
- orig_status2 = status2;
- if(status2 & mask) {
- if(pvoice->use) {
- snd_pcm_period_elapsed(pvoice->epcm->substream);
- } else {
- snd_printk(KERN_ERR "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n", status2, mask, pvoice, pvoice->use);
- }
- }
- if(status2 & 0x110000) {
- //printk(KERN_INFO "capture int found\n");
- if(cvoice->use) {
- //printk(KERN_INFO "capture period_elapsed\n");
- snd_pcm_period_elapsed(cvoice->epcm->substream);
- }
- }
- outl(orig_status2, emu->port + IPR2); /* ack all */
- }
+ if (emu->p16v_interrupt)
+ emu->p16v_interrupt(emu);
+ else
+ outl(0, emu->port + INTE2);
status &= ~IPR_P16V;
}
+ if (status & IPR_A_GPIO) {
+ if (emu->gpio_interrupt)
+ emu->gpio_interrupt(emu);
+ else
+ snd_emu10k1_intr_disable(emu, INTE_A_GPIOENABLE);
+ status &= ~IPR_A_GPIO;
+ }
if (status) {
- unsigned int bits;
- snd_printk(KERN_ERR "emu10k1: unhandled interrupt: 0x%08x\n", status);
- //make sure any interrupts we don't handle are disabled:
- bits = INTE_FXDSPENABLE |
- INTE_PCIERRORENABLE |
- INTE_VOLINCRENABLE |
- INTE_VOLDECRENABLE |
- INTE_MUTEENABLE |
- INTE_MICBUFENABLE |
- INTE_ADCBUFENABLE |
- INTE_EFXBUFENABLE |
- INTE_GPSPDIFENABLE |
- INTE_CDSPDIFENABLE |
- INTE_INTERVALTIMERENB |
- INTE_MIDITXENABLE |
- INTE_MIDIRXENABLE;
- if (emu->audigy)
- bits |= INTE_A_MIDITXENABLE2 | INTE_A_MIDIRXENABLE2;
- snd_emu10k1_intr_disable(emu, bits);
+ dev_err(emu->card->dev,
+ "unhandled interrupt: 0x%08x\n", status);
}
outl(orig_status, emu->port + IPR); /* ack all */
}
- if (timeout == 1000)
- snd_printk(KERN_INFO "emu10k1 irq routine failure\n");
return IRQ_RETVAL(handled);
}
diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c
index 957a311514c8..be889a4ccf9a 100644
--- a/sound/pci/emu10k1/memory.c
+++ b/sound/pci/emu10k1/memory.c
@@ -1,30 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
*
* EMU10K1 memory page allocation (PTB area)
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/pci.h>
#include <linux/gfp.h>
#include <linux/time.h>
#include <linux/mutex.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
@@ -33,17 +19,20 @@
* aligned pages in others
*/
#define __set_ptb_entry(emu,page,addr) \
- (((u32 *)(emu)->ptb_pages.area)[page] = cpu_to_le32(((addr) << 1) | (page)))
+ (((__le32 *)(emu)->ptb_pages.area)[page] = \
+ cpu_to_le32(((addr) << (emu->address_mode)) | (page)))
+#define __get_ptb_entry(emu, page) \
+ (le32_to_cpu(((__le32 *)(emu)->ptb_pages.area)[page]))
#define UNIT_PAGES (PAGE_SIZE / EMUPAGESIZE)
-#define MAX_ALIGN_PAGES (MAXPAGES / UNIT_PAGES)
+#define MAX_ALIGN_PAGES0 (MAXPAGES0 / UNIT_PAGES)
+#define MAX_ALIGN_PAGES1 (MAXPAGES1 / UNIT_PAGES)
/* get aligned page from offset address */
#define get_aligned_page(offset) ((offset) >> PAGE_SHIFT)
/* get offset address from aligned page */
#define aligned_page_offset(page) ((page) << PAGE_SHIFT)
-#if PAGE_SIZE == 4096
-/* page size == EMUPAGESIZE */
+#if PAGE_SIZE == EMUPAGESIZE && !IS_ENABLED(CONFIG_DYNAMIC_DEBUG)
/* fill PTB entrie(s) corresponding to page with addr */
#define set_ptb_entry(emu,page,addr) __set_ptb_entry(emu,page,addr)
/* fill PTB entrie(s) corresponding to page with silence pointer */
@@ -56,6 +45,8 @@ static inline void set_ptb_entry(struct snd_emu10k1 *emu, int page, dma_addr_t a
page *= UNIT_PAGES;
for (i = 0; i < UNIT_PAGES; i++, page++) {
__set_ptb_entry(emu, page, addr);
+ dev_dbg(emu->card->dev, "mapped page %d to entry %.8x\n", page,
+ (unsigned int)__get_ptb_entry(emu, page));
addr += EMUPAGESIZE;
}
}
@@ -63,9 +54,12 @@ static inline void set_silent_ptb(struct snd_emu10k1 *emu, int page)
{
int i;
page *= UNIT_PAGES;
- for (i = 0; i < UNIT_PAGES; i++, page++)
+ for (i = 0; i < UNIT_PAGES; i++, page++) {
/* do not increment ptr */
__set_ptb_entry(emu, page, emu->silent_page.addr);
+ dev_dbg(emu->card->dev, "mapped silent page %d to entry %.8x\n",
+ page, (unsigned int)__get_ptb_entry(emu, page));
+ }
}
#endif /* PAGE_SIZE */
@@ -100,7 +94,7 @@ static void emu10k1_memblk_init(struct snd_emu10k1_memblk *blk)
*/
static int search_empty_map_area(struct snd_emu10k1 *emu, int npages, struct list_head **nextp)
{
- int page = 0, found_page = -ENOMEM;
+ int page = 1, found_page = -ENOMEM;
int max_size = npages;
int size;
struct list_head *candidate = &emu->mapped_link_head;
@@ -123,7 +117,7 @@ static int search_empty_map_area(struct snd_emu10k1 *emu, int npages, struct lis
}
page = blk->mapped_page + blk->pages;
}
- size = MAX_ALIGN_PAGES - page;
+ size = (emu->address_mode ? MAX_ALIGN_PAGES1 : MAX_ALIGN_PAGES0) - page;
if (size >= max_size) {
*nextp = pos;
return page;
@@ -145,6 +139,10 @@ static int map_memblk(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
page = search_empty_map_area(emu, blk->pages, &next);
if (page < 0) /* not found */
return page;
+ if (page == 0) {
+ dev_err(emu->card->dev, "trying to map zero (reserved) page\n");
+ return -EINVAL;
+ }
/* insert this block in the proper position of mapped list */
list_add_tail(&blk->mapped_link, next);
/* append this as a newest block in order list */
@@ -171,16 +169,20 @@ static int unmap_memblk(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
struct snd_emu10k1_memblk *q;
/* calculate the expected size of empty region */
- if ((p = blk->mapped_link.prev) != &emu->mapped_link_head) {
+ p = blk->mapped_link.prev;
+ if (p != &emu->mapped_link_head) {
q = get_emu10k1_memblk(p, mapped_link);
start_page = q->mapped_page + q->pages;
- } else
- start_page = 0;
- if ((p = blk->mapped_link.next) != &emu->mapped_link_head) {
+ } else {
+ start_page = 1;
+ }
+ p = blk->mapped_link.next;
+ if (p != &emu->mapped_link_head) {
q = get_emu10k1_memblk(p, mapped_link);
end_page = q->mapped_page;
- } else
- end_page = MAX_ALIGN_PAGES;
+ } else {
+ end_page = (emu->address_mode ? MAX_ALIGN_PAGES1 : MAX_ALIGN_PAGES0);
+ }
/* remove links */
list_del(&blk->mapped_link);
@@ -235,11 +237,13 @@ __found_pages:
static int is_valid_page(struct snd_emu10k1 *emu, dma_addr_t addr)
{
if (addr & ~emu->dma_mask) {
- snd_printk(KERN_ERR "max memory size is 0x%lx (addr = 0x%lx)!!\n", emu->dma_mask, (unsigned long)addr);
+ dev_err_ratelimited(emu->card->dev,
+ "max memory size is 0x%lx (addr = 0x%lx)!!\n",
+ emu->dma_mask, (unsigned long)addr);
return 0;
}
if (addr & (EMUPAGESIZE-1)) {
- snd_printk(KERN_ERR "page is not aligned\n");
+ dev_err_ratelimited(emu->card->dev, "page is not aligned\n");
return 0;
}
return 1;
@@ -248,7 +252,7 @@ static int is_valid_page(struct snd_emu10k1 *emu, dma_addr_t addr)
/*
* map the given memory block on PTB.
* if the block is already mapped, update the link order.
- * if no empty pages are found, tries to release unsed memory blocks
+ * if no empty pages are found, tries to release unused memory blocks
* and retry the mapping.
*/
int snd_emu10k1_memblk_map(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
@@ -257,17 +261,16 @@ int snd_emu10k1_memblk_map(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *b
int size;
struct list_head *p, *nextp;
struct snd_emu10k1_memblk *deleted;
- unsigned long flags;
- spin_lock_irqsave(&emu->memblk_lock, flags);
+ guard(spinlock_irqsave)(&emu->memblk_lock);
if (blk->mapped_page >= 0) {
/* update order link */
- list_del(&blk->mapped_order_link);
- list_add_tail(&blk->mapped_order_link, &emu->mapped_order_link_head);
- spin_unlock_irqrestore(&emu->memblk_lock, flags);
+ list_move_tail(&blk->mapped_order_link,
+ &emu->mapped_order_link_head);
return 0;
}
- if ((err = map_memblk(emu, blk)) < 0) {
+ err = map_memblk(emu, blk);
+ if (err < 0) {
/* no enough page - try to unmap some blocks */
/* starting from the oldest block */
p = emu->mapped_order_link_head.next;
@@ -284,7 +287,6 @@ int snd_emu10k1_memblk_map(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *b
}
}
}
- spin_unlock_irqrestore(&emu->memblk_lock, flags);
return err;
}
@@ -304,31 +306,30 @@ snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *subst
if (snd_BUG_ON(!emu))
return NULL;
if (snd_BUG_ON(runtime->dma_bytes <= 0 ||
- runtime->dma_bytes >= MAXPAGES * EMUPAGESIZE))
+ runtime->dma_bytes >= (emu->address_mode ? MAXPAGES1 : MAXPAGES0) * EMUPAGESIZE))
return NULL;
hdr = emu->memhdr;
if (snd_BUG_ON(!hdr))
return NULL;
- idx = runtime->period_size >= runtime->buffer_size ?
- (emu->delay_pcm_irq * 2) : 0;
- mutex_lock(&hdr->block_mutex);
- blk = search_empty(emu, runtime->dma_bytes + idx);
- if (blk == NULL) {
- mutex_unlock(&hdr->block_mutex);
+ guard(mutex)(&hdr->block_mutex);
+ blk = search_empty(emu, runtime->dma_bytes);
+ if (blk == NULL)
return NULL;
- }
/* fill buffer addresses but pointers are not stored so that
- * snd_free_pci_page() is not called in in synth_free()
+ * snd_free_pci_page() is not called in synth_free()
*/
idx = 0;
for (page = blk->first_page; page <= blk->last_page; page++, idx++) {
unsigned long ofs = idx << PAGE_SHIFT;
dma_addr_t addr;
- addr = snd_pcm_sgbuf_get_addr(substream, ofs);
+ if (ofs >= runtime->dma_bytes)
+ addr = emu->silent_page.addr;
+ else
+ addr = snd_pcm_sgbuf_get_addr(substream, ofs);
if (! is_valid_page(emu, addr)) {
- printk(KERN_ERR "emu: failure page = %d\n", idx);
- mutex_unlock(&hdr->block_mutex);
+ dev_err_ratelimited(emu->card->dev,
+ "emu: failure page = %d\n", idx);
return NULL;
}
emu->page_addr_table[page] = addr;
@@ -340,10 +341,8 @@ snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *subst
err = snd_emu10k1_memblk_map(emu, blk);
if (err < 0) {
__snd_util_mem_free(hdr, (struct snd_util_memblk *)blk);
- mutex_unlock(&hdr->block_mutex);
return NULL;
}
- mutex_unlock(&hdr->block_mutex);
return (struct snd_util_memblk *)blk;
}
@@ -358,6 +357,33 @@ int snd_emu10k1_free_pages(struct snd_emu10k1 *emu, struct snd_util_memblk *blk)
return snd_emu10k1_synth_free(emu, blk);
}
+/*
+ * allocate DMA pages, widening the allocation if necessary
+ *
+ * See the comment above snd_emu10k1_detect_iommu() in emu10k1_main.c why
+ * this might be needed.
+ *
+ * If you modify this function check whether __synth_free_pages() also needs
+ * changes.
+ */
+int snd_emu10k1_alloc_pages_maybe_wider(struct snd_emu10k1 *emu, size_t size,
+ struct snd_dma_buffer *dmab)
+{
+ if (emu->iommu_workaround) {
+ size_t npages = DIV_ROUND_UP(size, PAGE_SIZE);
+ size_t size_real = npages * PAGE_SIZE;
+
+ /*
+ * The device has been observed to accesses up to 256 extra
+ * bytes, but use 1k to be safe.
+ */
+ if (size_real < size + 1024)
+ size += PAGE_SIZE;
+ }
+
+ return snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ &emu->pci->dev, size, dmab);
+}
/*
* memory allocation using multiple pages (for synth)
@@ -373,19 +399,15 @@ snd_emu10k1_synth_alloc(struct snd_emu10k1 *hw, unsigned int size)
struct snd_emu10k1_memblk *blk;
struct snd_util_memhdr *hdr = hw->memhdr;
- mutex_lock(&hdr->block_mutex);
+ guard(mutex)(&hdr->block_mutex);
blk = (struct snd_emu10k1_memblk *)__snd_util_mem_alloc(hdr, size);
- if (blk == NULL) {
- mutex_unlock(&hdr->block_mutex);
+ if (blk == NULL)
return NULL;
- }
if (synth_alloc_pages(hw, blk)) {
__snd_util_mem_free(hdr, (struct snd_util_memblk *)blk);
- mutex_unlock(&hdr->block_mutex);
return NULL;
}
snd_emu10k1_memblk_map(hw, blk);
- mutex_unlock(&hdr->block_mutex);
return (struct snd_util_memblk *)blk;
}
@@ -399,16 +421,14 @@ snd_emu10k1_synth_free(struct snd_emu10k1 *emu, struct snd_util_memblk *memblk)
{
struct snd_util_memhdr *hdr = emu->memhdr;
struct snd_emu10k1_memblk *blk = (struct snd_emu10k1_memblk *)memblk;
- unsigned long flags;
- mutex_lock(&hdr->block_mutex);
- spin_lock_irqsave(&emu->memblk_lock, flags);
- if (blk->mapped_page >= 0)
- unmap_memblk(emu, blk);
- spin_unlock_irqrestore(&emu->memblk_lock, flags);
+ guard(mutex)(&hdr->block_mutex);
+ scoped_guard(spinlock_irqsave, &emu->memblk_lock) {
+ if (blk->mapped_page >= 0)
+ unmap_memblk(emu, blk);
+ }
synth_free_pages(emu, blk);
- __snd_util_mem_free(hdr, memblk);
- mutex_unlock(&hdr->block_mutex);
+ __snd_util_mem_free(hdr, memblk);
return 0;
}
@@ -423,13 +443,15 @@ static void get_single_page_range(struct snd_util_memhdr *hdr,
struct snd_emu10k1_memblk *q;
int first_page, last_page;
first_page = blk->first_page;
- if ((p = blk->mem.list.prev) != &hdr->block) {
+ p = blk->mem.list.prev;
+ if (p != &hdr->block) {
q = get_emu10k1_memblk(p, mem.list);
if (q->last_page == first_page)
first_page++; /* first page was already allocated */
}
last_page = blk->last_page;
- if ((p = blk->mem.list.next) != &hdr->block) {
+ p = blk->mem.list.next;
+ if (p != &hdr->block) {
q = get_emu10k1_memblk(p, mem.list);
if (q->first_page == last_page)
last_page--; /* last page was already allocated */
@@ -442,10 +464,27 @@ static void get_single_page_range(struct snd_util_memhdr *hdr,
static void __synth_free_pages(struct snd_emu10k1 *emu, int first_page,
int last_page)
{
+ struct snd_dma_buffer dmab;
int page;
+ dmab.dev.type = SNDRV_DMA_TYPE_DEV;
+ dmab.dev.dev = &emu->pci->dev;
+
for (page = first_page; page <= last_page; page++) {
- free_page((unsigned long)emu->page_ptr_table[page]);
+ if (emu->page_ptr_table[page] == NULL)
+ continue;
+ dmab.area = emu->page_ptr_table[page];
+ dmab.addr = emu->page_addr_table[page];
+
+ /*
+ * please keep me in sync with logic in
+ * snd_emu10k1_alloc_pages_maybe_wider()
+ */
+ dmab.bytes = PAGE_SIZE;
+ if (emu->iommu_workaround)
+ dmab.bytes *= 2;
+
+ snd_dma_free_pages(&dmab);
emu->page_addr_table[page] = 0;
emu->page_ptr_table[page] = NULL;
}
@@ -457,30 +496,30 @@ static void __synth_free_pages(struct snd_emu10k1 *emu, int first_page,
static int synth_alloc_pages(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
{
int page, first_page, last_page;
+ struct snd_dma_buffer dmab;
emu10k1_memblk_init(blk);
get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
/* allocate kernel pages */
for (page = first_page; page <= last_page; page++) {
- /* first try to allocate from <4GB zone */
- struct page *p = alloc_page(GFP_KERNEL | GFP_DMA32 |
- __GFP_NOWARN);
- if (!p || (page_to_pfn(p) & ~(emu->dma_mask >> PAGE_SHIFT))) {
- if (p)
- __free_page(p);
- /* try to allocate from <16MB zone */
- p = alloc_page(GFP_ATOMIC | GFP_DMA |
- __GFP_NORETRY | /* no OOM-killer */
- __GFP_NOWARN);
- }
- if (!p) {
- __synth_free_pages(emu, first_page, page - 1);
- return -ENOMEM;
+ if (snd_emu10k1_alloc_pages_maybe_wider(emu, PAGE_SIZE,
+ &dmab) < 0)
+ goto __fail;
+ if (!is_valid_page(emu, dmab.addr)) {
+ snd_dma_free_pages(&dmab);
+ goto __fail;
}
- emu->page_addr_table[page] = page_to_phys(p);
- emu->page_ptr_table[page] = page_address(p);
+ emu->page_addr_table[page] = dmab.addr;
+ emu->page_ptr_table[page] = dmab.area;
}
return 0;
+
+__fail:
+ /* release allocated pages */
+ last_page = page - 1;
+ __synth_free_pages(emu, first_page, last_page);
+
+ return -ENOMEM;
}
/*
@@ -503,7 +542,8 @@ static inline void *offset_ptr(struct snd_emu10k1 *emu, int page, int offset)
return NULL;
ptr = emu->page_ptr_table[page];
if (! ptr) {
- printk(KERN_ERR "emu10k1: access to NULL ptr: page = %d\n", page);
+ dev_err(emu->card->dev,
+ "access to NULL ptr: page = %d\n", page);
return NULL;
}
ptr += offset & (PAGE_SIZE - 1);
@@ -511,15 +551,18 @@ static inline void *offset_ptr(struct snd_emu10k1 *emu, int page, int offset)
}
/*
- * bzero(blk + offset, size)
+ * memset(blk + offset, value, size)
*/
-int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk,
- int offset, int size)
+int snd_emu10k1_synth_memset(struct snd_emu10k1 *emu, struct snd_util_memblk *blk,
+ int offset, int size, u8 value)
{
int page, nextofs, end_offset, temp, temp1;
void *ptr;
struct snd_emu10k1_memblk *p = (struct snd_emu10k1_memblk *)blk;
+ if (snd_BUG_ON(offset + size > p->mem.size))
+ return -EFAULT;
+
offset += blk->offset & (PAGE_SIZE - 1);
end_offset = offset + size;
page = get_aligned_page(offset);
@@ -531,25 +574,55 @@ int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk
temp = temp1;
ptr = offset_ptr(emu, page + p->first_page, offset);
if (ptr)
- memset(ptr, 0, temp);
+ memset(ptr, value, temp);
offset = nextofs;
page++;
} while (offset < end_offset);
return 0;
}
-EXPORT_SYMBOL(snd_emu10k1_synth_bzero);
+EXPORT_SYMBOL(snd_emu10k1_synth_memset);
+
+// Note that the value is assumed to be suitably repetitive.
+static void xor_range(void *ptr, int size, u32 value)
+{
+ if ((long)ptr & 1) {
+ *(u8 *)ptr ^= (u8)value;
+ ptr++;
+ size--;
+ }
+ if (size > 1 && ((long)ptr & 2)) {
+ *(u16 *)ptr ^= (u16)value;
+ ptr += 2;
+ size -= 2;
+ }
+ while (size > 3) {
+ *(u32 *)ptr ^= value;
+ ptr += 4;
+ size -= 4;
+ }
+ if (size > 1) {
+ *(u16 *)ptr ^= (u16)value;
+ ptr += 2;
+ size -= 2;
+ }
+ if (size > 0)
+ *(u8 *)ptr ^= (u8)value;
+}
/*
- * copy_from_user(blk + offset, data, size)
+ * copy_from_user(blk + offset, data, size) ^ xor
*/
int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_memblk *blk,
- int offset, const char __user *data, int size)
+ int offset, const char __user *data, int size, u32 xor)
{
int page, nextofs, end_offset, temp, temp1;
void *ptr;
struct snd_emu10k1_memblk *p = (struct snd_emu10k1_memblk *)blk;
+ if (snd_BUG_ON(offset + size > p->mem.size))
+ return -EFAULT;
+
offset += blk->offset & (PAGE_SIZE - 1);
end_offset = offset + size;
page = get_aligned_page(offset);
@@ -560,8 +633,12 @@ int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_me
if (temp1 < temp)
temp = temp1;
ptr = offset_ptr(emu, page + p->first_page, offset);
- if (ptr && copy_from_user(ptr, data, temp))
- return -EFAULT;
+ if (ptr) {
+ if (copy_from_user(ptr, data, temp))
+ return -EFAULT;
+ if (xor)
+ xor_range(ptr, temp, xor);
+ }
offset = nextofs;
data += temp;
page++;
diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c
index 61b8ab39800f..b74128e61254 100644
--- a/sound/pci/emu10k1/p16v.c
+++ b/sound/pci/emu10k1/p16v.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver p16v chips
@@ -69,23 +70,8 @@
* ADC: Philips 1361T (Stereo 24bit)
* DAC: CS4382-K (8-channel, 24bit, 192Khz)
*
- * This code was initally based on code from ALSA's emu10k1x.c which is:
+ * This code was initially based on code from ALSA's emu10k1x.c which is:
* Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
#include <linux/init.h>
@@ -122,7 +108,7 @@
*/
/* hardware definition */
-static struct snd_pcm_hardware snd_p16v_playback_hw = {
+static const struct snd_pcm_hardware snd_p16v_playback_hw = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -143,7 +129,7 @@ static struct snd_pcm_hardware snd_p16v_playback_hw = {
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_p16v_capture_hw = {
+static const struct snd_pcm_hardware snd_p16v_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -163,103 +149,49 @@ static struct snd_pcm_hardware snd_p16v_capture_hw = {
.fifo_size = 0,
};
-static void snd_p16v_pcm_free_substream(struct snd_pcm_runtime *runtime)
-{
- struct snd_emu10k1_pcm *epcm = runtime->private_data;
-
- if (epcm) {
- /* snd_printk(KERN_DEBUG "epcm free: %p\n", epcm); */
- kfree(epcm);
- }
-}
-
/* open_playback callback */
static int snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream *substream, int channel_id)
{
- struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
- struct snd_emu10k1_voice *channel = &(emu->p16v_voices[channel_id]);
- struct snd_emu10k1_pcm *epcm;
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
- /* snd_printk(KERN_DEBUG "epcm kcalloc: %p\n", epcm); */
-
- if (epcm == NULL)
- return -ENOMEM;
- epcm->emu = emu;
- epcm->substream = substream;
/*
- snd_printk(KERN_DEBUG "epcm device=%d, channel_id=%d\n",
+ dev_dbg(emu->card->dev, "epcm device=%d, channel_id=%d\n",
substream->pcm->device, channel_id);
*/
- runtime->private_data = epcm;
- runtime->private_free = snd_p16v_pcm_free_substream;
runtime->hw = snd_p16v_playback_hw;
- channel->emu = emu;
- channel->number = channel_id;
-
- channel->use=1;
#if 0 /* debug */
- snd_printk(KERN_DEBUG
+ dev_dbg(emu->card->dev,
"p16v: open channel_id=%d, channel=%p, use=0x%x\n",
channel_id, channel, channel->use);
- printk(KERN_DEBUG "open:channel_id=%d, chip=%p, channel=%p\n",
+ dev_dbg(emu->card->dev, "open:channel_id=%d, chip=%p, channel=%p\n",
channel_id, chip, channel);
#endif /* debug */
/* channel->interrupt = snd_p16v_pcm_channel_interrupt; */
- channel->epcm = epcm;
- if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
return err;
- runtime->sync.id32[0] = substream->pcm->card->number;
- runtime->sync.id32[1] = 'P';
- runtime->sync.id32[2] = 16;
- runtime->sync.id32[3] = 'V';
-
return 0;
}
+
/* open_capture callback */
static int snd_p16v_pcm_open_capture_channel(struct snd_pcm_substream *substream, int channel_id)
{
- struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
- struct snd_emu10k1_voice *channel = &(emu->p16v_capture_voice);
- struct snd_emu10k1_pcm *epcm;
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
- epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
- /* snd_printk(KERN_DEBUG "epcm kcalloc: %p\n", epcm); */
-
- if (epcm == NULL)
- return -ENOMEM;
- epcm->emu = emu;
- epcm->substream = substream;
/*
- snd_printk(KERN_DEBUG "epcm device=%d, channel_id=%d\n",
+ dev_dbg(emu->card->dev, "epcm device=%d, channel_id=%d\n",
substream->pcm->device, channel_id);
*/
- runtime->private_data = epcm;
- runtime->private_free = snd_p16v_pcm_free_substream;
runtime->hw = snd_p16v_capture_hw;
- channel->emu = emu;
- channel->number = channel_id;
-
- channel->use=1;
-#if 0 /* debug */
- snd_printk(KERN_DEBUG
- "p16v: open channel_id=%d, channel=%p, use=0x%x\n",
- channel_id, channel, channel->use);
- printk(KERN_DEBUG "open:channel_id=%d, chip=%p, channel=%p\n",
- channel_id, chip, channel);
-#endif /* debug */
- /* channel->interrupt = snd_p16v_pcm_channel_interrupt; */
- channel->epcm = epcm;
- if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
return err;
return 0;
@@ -269,22 +201,12 @@ static int snd_p16v_pcm_open_capture_channel(struct snd_pcm_substream *substream
/* close callback */
static int snd_p16v_pcm_close_playback(struct snd_pcm_substream *substream)
{
- struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
- //struct snd_pcm_runtime *runtime = substream->runtime;
- //struct snd_emu10k1_pcm *epcm = runtime->private_data;
- emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use = 0;
- /* FIXME: maybe zero others */
return 0;
}
/* close callback */
static int snd_p16v_pcm_close_capture(struct snd_pcm_substream *substream)
{
- struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
- //struct snd_pcm_runtime *runtime = substream->runtime;
- //struct snd_emu10k1_pcm *epcm = runtime->private_data;
- emu->p16v_capture_voice.use = 0;
- /* FIXME: maybe zero others */
return 0;
}
@@ -299,82 +221,63 @@ static int snd_p16v_pcm_open_capture(struct snd_pcm_substream *substream)
return snd_p16v_pcm_open_capture_channel(substream, 0);
}
-/* hw_params callback */
-static int snd_p16v_pcm_hw_params_playback(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- int result;
- result = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- return result;
-}
-
-/* hw_params callback */
-static int snd_p16v_pcm_hw_params_capture(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- int result;
- result = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- return result;
-}
-
-
-/* hw_free callback */
-static int snd_p16v_pcm_hw_free_playback(struct snd_pcm_substream *substream)
-{
- int result;
- result = snd_pcm_lib_free_pages(substream);
- return result;
-}
-
-/* hw_free callback */
-static int snd_p16v_pcm_hw_free_capture(struct snd_pcm_substream *substream)
+static int snd_p16v_pcm_ioctl_playback(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
{
- int result;
- result = snd_pcm_lib_free_pages(substream);
- return result;
+ if (cmd == SNDRV_PCM_IOCTL1_SYNC_ID) {
+ static const unsigned char id[4] = { 'P', '1', '6', 'V' };
+ snd_pcm_set_sync_per_card(substream, arg, id, 4);
+ return 0;
+ }
+ return snd_pcm_lib_ioctl(substream, cmd, arg);
}
-
/* prepare playback callback */
static int snd_p16v_pcm_prepare_playback(struct snd_pcm_substream *substream)
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int channel = substream->pcm->device - emu->p16v_device_offset;
- u32 *table_base = (u32 *)(emu->p16v_buffer.area+(8*16*channel));
+ u32 *table_base = (u32 *)(emu->p16v_buffer->area+(8*16*channel));
u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
int i;
u32 tmp;
#if 0 /* debug */
- snd_printk(KERN_DEBUG "prepare:channel_number=%d, rate=%d, "
+ dev_dbg(emu->card->dev,
+ "prepare:channel_number=%d, rate=%d, "
"format=0x%x, channels=%d, buffer_size=%ld, "
"period_size=%ld, periods=%u, frames_to_bytes=%d\n",
channel, runtime->rate, runtime->format, runtime->channels,
runtime->buffer_size, runtime->period_size,
runtime->periods, frames_to_bytes(runtime, 1));
- snd_printk(KERN_DEBUG "dma_addr=%x, dma_area=%p, table_base=%p\n",
+ dev_dbg(emu->card->dev,
+ "dma_addr=%x, dma_area=%p, table_base=%p\n",
runtime->dma_addr, runtime->dma_area, table_base);
- snd_printk(KERN_DEBUG "dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
- emu->p16v_buffer.addr, emu->p16v_buffer.area,
- emu->p16v_buffer.bytes);
+ dev_dbg(emu->card->dev,
+ "dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
+ emu->p16v_buffer->addr, emu->p16v_buffer->area,
+ emu->p16v_buffer->bytes);
#endif /* debug */
tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
+ tmp &= ~(A_SPDIF_RATE_MASK | A_EHC_SRC48_MASK);
switch (runtime->rate) {
case 44100:
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x8080);
+ snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel,
+ tmp | A_SPDIF_44100 | A_EHC_SRC48_44);
break;
case 96000:
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x4040);
+ snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel,
+ tmp | A_SPDIF_96000 | A_EHC_SRC48_96);
break;
case 192000:
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x2020);
+ snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel,
+ tmp | A_SPDIF_192000 | A_EHC_SRC48_192);
break;
case 48000:
default:
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x0000);
+ snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel,
+ tmp | A_SPDIF_48000 | A_EHC_SRC48_BYPASS);
break;
}
/* FIXME: Check emu->buffer.size before actually writing to it. */
@@ -383,15 +286,15 @@ static int snd_p16v_pcm_prepare_playback(struct snd_pcm_substream *substream)
table_base[(i*2)+1]=period_size_bytes<<16;
}
- snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_ADDR, channel, emu->p16v_buffer.addr+(8*16*channel));
+ snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_ADDR, channel, emu->p16v_buffer->addr+(8*16*channel));
snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19);
snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_PTR, channel, 0);
snd_emu10k1_ptr20_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr);
//snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes
snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, 0); // buffer size in bytes
snd_emu10k1_ptr20_write(emu, PLAYBACK_POINTER, channel, 0);
- snd_emu10k1_ptr20_write(emu, 0x07, channel, 0x0);
- snd_emu10k1_ptr20_write(emu, 0x08, channel, 0);
+ snd_emu10k1_ptr20_write(emu, PLAYBACK_FIFO_END_ADDRESS, channel, 0);
+ snd_emu10k1_ptr20_write(emu, PLAYBACK_FIFO_POINTER, channel, 0);
return 0;
}
@@ -402,34 +305,32 @@ static int snd_p16v_pcm_prepare_capture(struct snd_pcm_substream *substream)
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int channel = substream->pcm->device - emu->p16v_device_offset;
- u32 tmp;
/*
- printk(KERN_DEBUG "prepare capture:channel_number=%d, rate=%d, "
+ dev_dbg(emu->card->dev, "prepare capture:channel_number=%d, rate=%d, "
"format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, "
"frames_to_bytes=%d\n",
channel, runtime->rate, runtime->format, runtime->channels,
runtime->buffer_size, runtime->period_size,
frames_to_bytes(runtime, 1));
*/
- tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
switch (runtime->rate) {
case 44100:
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0800);
+ snd_emu10k1_ptr_write(emu, A_I2S_CAPTURE_RATE, channel, A_I2S_CAPTURE_44100);
break;
case 96000:
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0400);
+ snd_emu10k1_ptr_write(emu, A_I2S_CAPTURE_RATE, channel, A_I2S_CAPTURE_96000);
break;
case 192000:
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0200);
+ snd_emu10k1_ptr_write(emu, A_I2S_CAPTURE_RATE, channel, A_I2S_CAPTURE_192000);
break;
case 48000:
default:
- snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0000);
+ snd_emu10k1_ptr_write(emu, A_I2S_CAPTURE_RATE, channel, A_I2S_CAPTURE_48000);
break;
}
/* FIXME: Check emu->buffer.size before actually writing to it. */
- snd_emu10k1_ptr20_write(emu, 0x13, channel, 0);
+ snd_emu10k1_ptr20_write(emu, CAPTURE_FIFO_POINTER, channel, 0);
snd_emu10k1_ptr20_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr);
snd_emu10k1_ptr20_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size) << 16); // buffer size in bytes
snd_emu10k1_ptr20_write(emu, CAPTURE_POINTER, channel, 0);
@@ -441,24 +342,56 @@ static int snd_p16v_pcm_prepare_capture(struct snd_pcm_substream *substream)
static void snd_p16v_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
{
- unsigned long flags;
unsigned int enable;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
enable = inl(emu->port + INTE2) | intrenb;
outl(enable, emu->port + INTE2);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
}
static void snd_p16v_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
{
- unsigned long flags;
unsigned int disable;
- spin_lock_irqsave(&emu->emu_lock, flags);
+ guard(spinlock_irqsave)(&emu->emu_lock);
disable = inl(emu->port + INTE2) & (~intrenb);
outl(disable, emu->port + INTE2);
- spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_p16v_interrupt(struct snd_emu10k1 *emu)
+{
+ unsigned int status;
+
+ while ((status = inl(emu->port + IPR2)) != 0) {
+ u32 mask = INTE2_PLAYBACK_CH_0_LOOP; /* Full Loop */
+
+ /* dev_dbg(emu->card->dev, "p16v status=0x%x\n", status); */
+ if (status & mask) {
+ struct snd_pcm_substream *substream =
+ emu->pcm_p16v->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (runtime && runtime->private_data) {
+ snd_pcm_period_elapsed(substream);
+ } else {
+ dev_err(emu->card->dev,
+ "p16v: status: 0x%08x, mask=0x%08x\n",
+ status, mask);
+ }
+ }
+ if (status & 0x110000) {
+ struct snd_pcm_substream *substream =
+ emu->pcm_p16v->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ /* dev_info(emu->card->dev, "capture int found\n"); */
+ if (runtime && runtime->private_data) {
+ /* dev_info(emu->card->dev, "capture period_elapsed\n"); */
+ snd_pcm_period_elapsed(substream);
+ }
+ }
+ outl(status, emu->port + IPR2); /* ack all */
+ }
}
/* trigger_playback callback */
@@ -467,7 +400,6 @@ static int snd_p16v_pcm_trigger_playback(struct snd_pcm_substream *substream,
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime;
- struct snd_emu10k1_pcm *epcm;
int channel;
int result = 0;
struct snd_pcm_substream *s;
@@ -489,15 +421,14 @@ static int snd_p16v_pcm_trigger_playback(struct snd_pcm_substream *substream,
s->stream != SNDRV_PCM_STREAM_PLAYBACK)
continue;
runtime = s->runtime;
- epcm = runtime->private_data;
channel = substream->pcm->device-emu->p16v_device_offset;
- /* snd_printk(KERN_DEBUG "p16v channel=%d\n", channel); */
- epcm->running = running;
+ /* dev_dbg(emu->card->dev, "p16v channel=%d\n", channel); */
+ runtime->private_data = (void *)(ptrdiff_t)running;
basic |= (0x1<<channel);
inte |= (INTE2_PLAYBACK_CH_0_LOOP<<channel);
snd_pcm_trigger_done(s, substream);
}
- /* snd_printk(KERN_DEBUG "basic=0x%x, inte=0x%x\n", basic, inte); */
+ /* dev_dbg(emu->card->dev, "basic=0x%x, inte=0x%x\n", basic, inte); */
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -521,7 +452,6 @@ static int snd_p16v_pcm_trigger_capture(struct snd_pcm_substream *substream,
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_emu10k1_pcm *epcm = runtime->private_data;
int channel = 0;
int result = 0;
u32 inte = INTE2_CAPTURE_CH_0_LOOP | INTE2_CAPTURE_CH_0_HALF_LOOP;
@@ -530,13 +460,13 @@ static int snd_p16v_pcm_trigger_capture(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_START:
snd_p16v_intr_enable(emu, inte);
snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0)|(0x100<<channel));
- epcm->running = 1;
+ runtime->private_data = (void *)1;
break;
case SNDRV_PCM_TRIGGER_STOP:
snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & ~(0x100<<channel));
snd_p16v_intr_disable(emu, inte);
//snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) & ~(0x110000<<channel));
- epcm->running = 0;
+ runtime->private_data = NULL;
break;
default:
result = -EINVAL;
@@ -551,10 +481,10 @@ snd_p16v_pcm_pointer_playback(struct snd_pcm_substream *substream)
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_emu10k1_pcm *epcm = runtime->private_data;
snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0;
int channel = substream->pcm->device - emu->p16v_device_offset;
- if (!epcm->running)
+
+ if (!runtime->private_data)
return 0;
ptr3 = snd_emu10k1_ptr20_read(emu, PLAYBACK_LIST_PTR, channel);
@@ -576,11 +506,10 @@ snd_p16v_pcm_pointer_capture(struct snd_pcm_substream *substream)
{
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_emu10k1_pcm *epcm = runtime->private_data;
snd_pcm_uframes_t ptr, ptr1, ptr2 = 0;
int channel = 0;
- if (!epcm->running)
+ if (!runtime->private_data)
return 0;
ptr1 = snd_emu10k1_ptr20_read(emu, CAPTURE_POINTER, channel);
@@ -588,10 +517,10 @@ snd_p16v_pcm_pointer_capture(struct snd_pcm_substream *substream)
ptr=ptr2;
if (ptr >= runtime->buffer_size) {
ptr -= runtime->buffer_size;
- printk(KERN_WARNING "buffer capture limited!\n");
+ dev_warn(emu->card->dev, "buffer capture limited!\n");
}
/*
- printk(KERN_DEBUG "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, "
+ dev_dbg(emu->card->dev, "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, "
"buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n",
ptr1, ptr2, ptr, (int)runtime->buffer_size,
(int)runtime->period_size, (int)runtime->frame_bits,
@@ -601,55 +530,35 @@ snd_p16v_pcm_pointer_capture(struct snd_pcm_substream *substream)
}
/* operators */
-static struct snd_pcm_ops snd_p16v_playback_front_ops = {
+static const struct snd_pcm_ops snd_p16v_playback_front_ops = {
.open = snd_p16v_pcm_open_playback_front,
.close = snd_p16v_pcm_close_playback,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_p16v_pcm_hw_params_playback,
- .hw_free = snd_p16v_pcm_hw_free_playback,
+ .ioctl = snd_p16v_pcm_ioctl_playback,
.prepare = snd_p16v_pcm_prepare_playback,
.trigger = snd_p16v_pcm_trigger_playback,
.pointer = snd_p16v_pcm_pointer_playback,
};
-static struct snd_pcm_ops snd_p16v_capture_ops = {
+static const struct snd_pcm_ops snd_p16v_capture_ops = {
.open = snd_p16v_pcm_open_capture,
.close = snd_p16v_pcm_close_capture,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_p16v_pcm_hw_params_capture,
- .hw_free = snd_p16v_pcm_hw_free_capture,
.prepare = snd_p16v_pcm_prepare_capture,
.trigger = snd_p16v_pcm_trigger_capture,
.pointer = snd_p16v_pcm_pointer_capture,
};
-
-int snd_p16v_free(struct snd_emu10k1 *chip)
-{
- // release the data
- if (chip->p16v_buffer.area) {
- snd_dma_free_pages(&chip->p16v_buffer);
- /*
- snd_printk(KERN_DEBUG "period lables free: %p\n",
- &chip->p16v_buffer);
- */
- }
- return 0;
-}
-
-int __devinit snd_p16v_pcm(struct snd_emu10k1 *emu, int device, struct snd_pcm **rpcm)
+int snd_p16v_pcm(struct snd_emu10k1 *emu, int device)
{
struct snd_pcm *pcm;
struct snd_pcm_substream *substream;
int err;
int capture=1;
- /* snd_printk(KERN_DEBUG "snd_p16v_pcm called. device=%d\n", device); */
+ /* dev_dbg(emu->card->dev, "snd_p16v_pcm called. device=%d\n", device); */
emu->p16v_device_offset = device;
- if (rpcm)
- *rpcm = NULL;
- if ((err = snd_pcm_new(emu->card, "p16v", device, 1, capture, &pcm)) < 0)
+ err = snd_pcm_new(emu->card, "p16v", device, 1, capture, &pcm);
+ if (err < 0)
return err;
pcm->private_data = emu;
@@ -660,19 +569,19 @@ int __devinit snd_p16v_pcm(struct snd_emu10k1 *emu, int device, struct snd_pcm *
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
- strcpy(pcm->name, "p16v");
+ strscpy(pcm->name, "p16v");
emu->pcm_p16v = pcm;
+ emu->p16v_interrupt = snd_p16v_interrupt;
for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
substream;
substream = substream->next) {
- if ((err = snd_pcm_lib_preallocate_pages(substream,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(emu->pci),
- ((65536 - 64) * 8), ((65536 - 64) * 8))) < 0)
- return err;
+ snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
+ &emu->pci->dev,
+ (65536 - 64) * 8,
+ (65536 - 64) * 8);
/*
- snd_printk(KERN_DEBUG
+ dev_dbg(emu->card->dev,
"preallocate playback substream: err=%d\n", err);
*/
}
@@ -680,20 +589,15 @@ int __devinit snd_p16v_pcm(struct snd_emu10k1 *emu, int device, struct snd_pcm *
for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
substream;
substream = substream->next) {
- if ((err = snd_pcm_lib_preallocate_pages(substream,
- SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(emu->pci),
- 65536 - 64, 65536 - 64)) < 0)
- return err;
+ snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
+ &emu->pci->dev,
+ 65536 - 64, 65536 - 64);
/*
- snd_printk(KERN_DEBUG
+ dev_dbg(emu->card->dev,
"preallocate capture substream: err=%d\n", err);
*/
}
- if (rpcm)
- *rpcm = pcm;
-
return 0;
}
@@ -754,18 +658,12 @@ static int snd_p16v_volume_put(struct snd_kcontrol *kcontrol,
static int snd_p16v_capture_source_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[8] = {
+ static const char * const texts[8] = {
"SPDIF", "I2S", "SRC48", "SRCMulti_SPDIF", "SRCMulti_I2S",
"CDIF", "FX", "AC97"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 8;
- if (uinfo->value.enumerated.item > 7)
- uinfo->value.enumerated.item = 7;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 8, texts);
}
static int snd_p16v_capture_source_get(struct snd_kcontrol *kcontrol,
@@ -802,15 +700,9 @@ static int snd_p16v_capture_source_put(struct snd_kcontrol *kcontrol,
static int snd_p16v_capture_channel_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = { "0", "1", "2", "3", };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item > 3)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ static const char * const texts[4] = { "0", "1", "2", "3", };
+
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
}
static int snd_p16v_capture_channel_get(struct snd_kcontrol *kcontrol,
@@ -854,7 +746,7 @@ static const DECLARE_TLV_DB_SCALE(snd_p16v_db_scale1, -5175, 25, 1);
.private_value = ((xreg) | ((xhl) << 8)) \
}
-static struct snd_kcontrol_new p16v_mixer_controls[] __devinitdata = {
+static const struct snd_kcontrol_new p16v_mixer_controls[] = {
P16V_VOL("HD Analog Front Playback Volume", PLAYBACK_VOLUME_MIXER9, 0),
P16V_VOL("HD Analog Rear Playback Volume", PLAYBACK_VOLUME_MIXER10, 1),
P16V_VOL("HD Analog Center/LFE Playback Volume", PLAYBACK_VOLUME_MIXER9, 1),
@@ -880,26 +772,26 @@ static struct snd_kcontrol_new p16v_mixer_controls[] __devinitdata = {
};
-int __devinit snd_p16v_mixer(struct snd_emu10k1 *emu)
+int snd_p16v_mixer(struct snd_emu10k1 *emu)
{
int i, err;
struct snd_card *card = emu->card;
for (i = 0; i < ARRAY_SIZE(p16v_mixer_controls); i++) {
- if ((err = snd_ctl_add(card, snd_ctl_new1(&p16v_mixer_controls[i],
- emu))) < 0)
+ err = snd_ctl_add(card, snd_ctl_new1(&p16v_mixer_controls[i], emu));
+ if (err < 0)
return err;
}
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
#define NUM_CHS 1 /* up to 4, but only first channel is used */
-int __devinit snd_p16v_alloc_pm_buffer(struct snd_emu10k1 *emu)
+int snd_p16v_alloc_pm_buffer(struct snd_emu10k1 *emu)
{
- emu->p16v_saved = vmalloc(NUM_CHS * 4 * 0x80);
+ emu->p16v_saved = vmalloc(array_size(NUM_CHS * 4, 0x80));
if (! emu->p16v_saved)
return -ENOMEM;
return 0;
diff --git a/sound/pci/emu10k1/p16v.h b/sound/pci/emu10k1/p16v.h
index 153214940336..95ab8071751b 100644
--- a/sound/pci/emu10k1/p16v.h
+++ b/sound/pci/emu10k1/p16v.h
@@ -1,92 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver p16v chips
- * Version: 0.21
*
- * FEATURES currently supported:
- * Output fixed at S32_LE, 2 channel to hw:0,0
- * Rates: 44.1, 48, 96, 192.
- *
- * Changelog:
- * 0.8
- * Use separate card based buffer for periods table.
- * 0.9
- * Use 2 channel output streams instead of 8 channel.
- * (8 channel output streams might be good for ASIO type output)
- * Corrected speaker output, so Front -> Front etc.
- * 0.10
- * Fixed missed interrupts.
- * 0.11
- * Add Sound card model number and names.
- * Add Analog volume controls.
- * 0.12
- * Corrected playback interrupts. Now interrupt per period, instead of half period.
- * 0.13
- * Use single trigger for multichannel.
- * 0.14
- * Mic capture now works at fixed: S32_LE, 96000Hz, Stereo.
- * 0.15
- * Force buffer_size / period_size == INTEGER.
- * 0.16
- * Update p16v.c to work with changed alsa api.
- * 0.17
- * Update p16v.c to work with changed alsa api. Removed boot_devs.
- * 0.18
- * Merging with snd-emu10k1 driver.
- * 0.19
- * One stereo channel at 24bit now works.
- * 0.20
- * Added better register defines.
- * 0.21
- * Split from p16v.c
- *
- *
- * BUGS:
- * Some stability problems when unloading the snd-p16v kernel module.
- * --
- *
- * TODO:
- * SPDIF out.
- * Find out how to change capture sample rates. E.g. To record SPDIF at 48000Hz.
- * Currently capture fixed at 48000Hz.
- *
- * --
- * GENERAL INFO:
- * Model: SB0240
- * P16V Chip: CA0151-DBS
- * Audigy 2 Chip: CA0102-IAT
- * AC97 Codec: STAC 9721
- * ADC: Philips 1361T (Stereo 24bit)
- * DAC: CS4382-K (8-channel, 24bit, 192Khz)
- *
- * This code was initally based on code from ALSA's emu10k1x.c which is:
+ * This code was initially based on code from ALSA's emu10k1x.c which is:
* Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/********************************************************************************************************/
-/* Audigy2 P16V pointer-offset register set, accessed through the PTR2 and DATA2 registers */
+/* Audigy2 P16V pointer-offset register set, accessed through the PTR2 and DATA2 registers */
/********************************************************************************************************/
/* The sample rate of the SPDIF outputs is set by modifying a register in the EMU10K2 PTR register A_SPDIF_SAMPLERATE.
* The sample rate is also controlled by the same registers that control the rate of the EMU10K2 sample rate converters.
*/
-/* Initally all registers from 0x00 to 0x3f have zero contents. */
+/* Initially all registers from 0x00 to 0x3f have zero contents. */
#define PLAYBACK_LIST_ADDR 0x00 /* Base DMA address of a list of pointers to each period/size */
/* One list entry: 4 bytes for DMA address,
* 4 bytes for period_size << 16.
@@ -96,7 +25,7 @@
#define PLAYBACK_LIST_SIZE 0x01 /* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000 */
#define PLAYBACK_LIST_PTR 0x02 /* Pointer to the current period being played */
#define PLAYBACK_UNKNOWN3 0x03 /* Not used */
-#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA addresss */
+#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA address */
#define PLAYBACK_PERIOD_SIZE 0x05 /* Playback period size. win2000 uses 0x04000000 */
#define PLAYBACK_POINTER 0x06 /* Playback period pointer. Used with PLAYBACK_LIST_PTR to determine buffer position currently in DAC */
#define PLAYBACK_FIFO_END_ADDRESS 0x07 /* Playback FIFO end address */
diff --git a/sound/pci/emu10k1/p17v.h b/sound/pci/emu10k1/p17v.h
index 4ef5f68a9cd0..ee4f4ab4b79c 100644
--- a/sound/pci/emu10k1/p17v.h
+++ b/sound/pci/emu10k1/p17v.h
@@ -1,27 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver p17v chips
- * Version: 0.01
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/******************************************************************************/
-/* Audigy2Value Tina (P17V) pointer-offset register set,
- * accessed through the PTR20 and DATA24 registers */
+/* Audigy2Value Tina (P17V) pointer-offset register set, */
+/* accessed through the PTR2 and DATA2 registers */
/******************************************************************************/
/* 00 - 07: Not used */
diff --git a/sound/pci/emu10k1/timer.c b/sound/pci/emu10k1/timer.c
index 72321e946ccc..1231ae2bf931 100644
--- a/sound/pci/emu10k1/timer.c
+++ b/sound/pci/emu10k1/timer.c
@@ -1,28 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Lee Revell <rlrevell@joe-job.com>
* Clemens Ladisch <clemens@ladisch.de>
- * Routines for control of EMU10K1 chips
- *
- * BUGS:
- * --
- *
- * TODO:
- * --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
*
+ * Routines for control of EMU10K1 chips
*/
#include <linux/time.h>
@@ -32,50 +14,60 @@
static int snd_emu10k1_timer_start(struct snd_timer *timer)
{
struct snd_emu10k1 *emu;
- unsigned long flags;
unsigned int delay;
emu = snd_timer_chip(timer);
delay = timer->sticks - 1;
if (delay < 5 ) /* minimum time is 5 ticks */
delay = 5;
- spin_lock_irqsave(&emu->reg_lock, flags);
snd_emu10k1_intr_enable(emu, INTE_INTERVALTIMERENB);
outw(delay & TIMER_RATE_MASK, emu->port + TIMER);
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
static int snd_emu10k1_timer_stop(struct snd_timer *timer)
{
struct snd_emu10k1 *emu;
- unsigned long flags;
emu = snd_timer_chip(timer);
- spin_lock_irqsave(&emu->reg_lock, flags);
snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB);
- spin_unlock_irqrestore(&emu->reg_lock, flags);
return 0;
}
+static unsigned long snd_emu10k1_timer_c_resolution(struct snd_timer *timer)
+{
+ struct snd_emu10k1 *emu = snd_timer_chip(timer);
+
+ if (emu->card_capabilities->emu_model &&
+ emu->emu1010.word_clock == 44100)
+ return 22676; // 1 sample @ 44.1 kHz = 22.675736...us
+ else
+ return 20833; // 1 sample @ 48 kHz = 20.833...us
+}
+
static int snd_emu10k1_timer_precise_resolution(struct snd_timer *timer,
unsigned long *num, unsigned long *den)
{
+ struct snd_emu10k1 *emu = snd_timer_chip(timer);
+
*num = 1;
- *den = 48000;
+ if (emu->card_capabilities->emu_model)
+ *den = emu->emu1010.word_clock;
+ else
+ *den = 48000;
return 0;
}
-static struct snd_timer_hardware snd_emu10k1_timer_hw = {
+static const struct snd_timer_hardware snd_emu10k1_timer_hw = {
.flags = SNDRV_TIMER_HW_AUTO,
- .resolution = 20833, /* 1 sample @ 48KHZ = 20.833...us */
.ticks = 1024,
.start = snd_emu10k1_timer_start,
.stop = snd_emu10k1_timer_stop,
+ .c_resolution = snd_emu10k1_timer_c_resolution,
.precise_resolution = snd_emu10k1_timer_precise_resolution,
};
-int __devinit snd_emu10k1_timer(struct snd_emu10k1 *emu, int device)
+int snd_emu10k1_timer(struct snd_emu10k1 *emu, int device)
{
struct snd_timer *timer = NULL;
struct snd_timer_id tid;
@@ -86,8 +78,9 @@ int __devinit snd_emu10k1_timer(struct snd_emu10k1 *emu, int device)
tid.card = emu->card->number;
tid.device = device;
tid.subdevice = 0;
- if ((err = snd_timer_new(emu->card, "EMU10K1", &tid, &timer)) >= 0) {
- strcpy(timer->name, "EMU10K1 timer");
+ err = snd_timer_new(emu->card, "EMU10K1", &tid, &timer);
+ if (err >= 0) {
+ strscpy(timer->name, "EMU10K1 timer");
timer->private_data = emu;
timer->hw = snd_emu10k1_timer_hw;
}
diff --git a/sound/pci/emu10k1/tina2.h b/sound/pci/emu10k1/tina2.h
index f2d8eb6c89e1..e3fcb290271c 100644
--- a/sound/pci/emu10k1/tina2.h
+++ b/sound/pci/emu10k1/tina2.h
@@ -1,22 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
* Driver tina2 chips
- * Version: 0.1
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/********************************************************************************************************/
diff --git a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c
index 20b8da250bd0..7fe1d1727768 100644
--- a/sound/pci/emu10k1/voice.c
+++ b/sound/pci/emu10k1/voice.c
@@ -1,34 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- * Creative Labs, Inc.
* Lee Revell <rlrevell@joe-job.com>
- * Routines for control of EMU10K1 chips - voice manager
- *
- * Rewrote voice allocator for multichannel support - rlrevell 12/2004
- *
- * BUGS:
- * --
- *
- * TODO:
- * --
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
+ * Creative Labs, Inc.
*
+ * Routines for control of EMU10K1 chips - voice manager
*/
#include <linux/time.h>
+#include <linux/export.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
@@ -36,112 +17,101 @@
* allocator uses a round robin scheme. The next free voice is tracked in
* the card record and each allocation begins where the last left off. The
* hardware requires stereo interleaved voices be aligned to an even/odd
- * boundary. For multichannel voice allocation we ensure than the block of
- * voices does not cross the 32 voice boundary. This simplifies the
- * multichannel support and ensures we can use a single write to the
- * (set|clear)_loop_stop registers. Otherwise (for example) the voices would
- * get out of sync when pausing/resuming a stream.
+ * boundary.
* --rlrevell
*/
static int voice_alloc(struct snd_emu10k1 *emu, int type, int number,
- struct snd_emu10k1_voice **rvoice)
+ struct snd_emu10k1_pcm *epcm, struct snd_emu10k1_voice **rvoice)
{
struct snd_emu10k1_voice *voice;
- int i, j, k, first_voice, last_voice, skip;
+ int i, j, k, skip;
- *rvoice = NULL;
- first_voice = last_voice = 0;
- for (i = emu->next_free_voice, j = 0; j < NUM_G ; i += number, j += number) {
+ for (i = emu->next_free_voice, j = 0; j < NUM_G; i = (i + skip) % NUM_G, j += skip) {
/*
- printk(KERN_DEBUG "i %d j %d next free %d!\n",
+ dev_dbg(emu->card->dev, "i %d j %d next free %d!\n",
i, j, emu->next_free_voice);
*/
- i %= NUM_G;
/* stereo voices must be even/odd */
- if ((number == 2) && (i % 2)) {
- i++;
+ if ((number > 1) && (i % 2)) {
+ skip = 1;
continue;
}
-
- skip = 0;
+
for (k = 0; k < number; k++) {
- voice = &emu->voices[(i+k) % NUM_G];
+ voice = &emu->voices[i + k];
if (voice->use) {
- skip = 1;
- break;
+ skip = k + 1;
+ goto next;
}
}
- if (!skip) {
- /* printk(KERN_DEBUG "allocated voice %d\n", i); */
- first_voice = i;
- last_voice = (i + number) % NUM_G;
- emu->next_free_voice = last_voice;
- break;
- }
- }
-
- if (first_voice == last_voice)
- return -ENOMEM;
-
- for (i = 0; i < number; i++) {
- voice = &emu->voices[(first_voice + i) % NUM_G];
- /*
- printk(kERN_DEBUG "voice alloc - %i, %i of %i\n",
- voice->number, idx-first_voice+1, number);
- */
- voice->use = 1;
- switch (type) {
- case EMU10K1_PCM:
- voice->pcm = 1;
- break;
- case EMU10K1_SYNTH:
- voice->synth = 1;
- break;
- case EMU10K1_MIDI:
- voice->midi = 1;
- break;
- case EMU10K1_EFX:
- voice->efx = 1;
- break;
+
+ for (k = 0; k < number; k++) {
+ voice = &emu->voices[i + k];
+ voice->use = type;
+ voice->epcm = epcm;
+ /* dev_dbg(emu->card->dev, "allocated voice %d\n", i + k); */
}
+ voice->last = 1;
+
+ *rvoice = &emu->voices[i];
+ emu->next_free_voice = (i + number) % NUM_G;
+ return 0;
+
+ next: ;
}
- *rvoice = &emu->voices[first_voice];
- return 0;
+ return -ENOMEM; // -EBUSY would have been better
+}
+
+static void voice_free(struct snd_emu10k1 *emu,
+ struct snd_emu10k1_voice *pvoice)
+{
+ if (pvoice->dirty)
+ snd_emu10k1_voice_init(emu, pvoice->number);
+ pvoice->interrupt = NULL;
+ pvoice->use = pvoice->dirty = pvoice->last = 0;
+ pvoice->epcm = NULL;
}
-int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int number,
- struct snd_emu10k1_voice **rvoice)
+int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int count, int channels,
+ struct snd_emu10k1_pcm *epcm, struct snd_emu10k1_voice **rvoice)
{
- unsigned long flags;
int result;
if (snd_BUG_ON(!rvoice))
return -EINVAL;
- if (snd_BUG_ON(!number))
+ if (snd_BUG_ON(!count))
+ return -EINVAL;
+ if (snd_BUG_ON(!channels))
return -EINVAL;
- spin_lock_irqsave(&emu->voice_lock, flags);
- for (;;) {
- result = voice_alloc(emu, type, number, rvoice);
- if (result == 0 || type == EMU10K1_SYNTH || type == EMU10K1_MIDI)
- break;
-
- /* free a voice from synth */
- if (emu->get_synth_voice) {
+ guard(spinlock_irqsave)(&emu->voice_lock);
+ for (int got = 0; got < channels; ) {
+ result = voice_alloc(emu, type, count, epcm, &rvoice[got]);
+ if (result == 0) {
+ got++;
+ /*
+ dev_dbg(emu->card->dev, "voice alloc - %i, %i of %i\n",
+ rvoice[got - 1]->number, got, want);
+ */
+ continue;
+ }
+ if (type != EMU10K1_SYNTH && emu->get_synth_voice) {
+ /* free a voice from synth */
result = emu->get_synth_voice(emu);
if (result >= 0) {
- struct snd_emu10k1_voice *pvoice = &emu->voices[result];
- pvoice->interrupt = NULL;
- pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
- pvoice->epcm = NULL;
+ voice_free(emu, &emu->voices[result]);
+ continue;
}
}
- if (result < 0)
- break;
+ for (int i = 0; i < got; i++) {
+ for (int j = 0; j < count; j++)
+ voice_free(emu, rvoice[i] + j);
+ rvoice[i] = NULL;
+ }
+ break;
}
- spin_unlock_irqrestore(&emu->voice_lock, flags);
return result;
}
@@ -151,16 +121,15 @@ EXPORT_SYMBOL(snd_emu10k1_voice_alloc);
int snd_emu10k1_voice_free(struct snd_emu10k1 *emu,
struct snd_emu10k1_voice *pvoice)
{
- unsigned long flags;
+ int last;
if (snd_BUG_ON(!pvoice))
return -EINVAL;
- spin_lock_irqsave(&emu->voice_lock, flags);
- pvoice->interrupt = NULL;
- pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
- pvoice->epcm = NULL;
- snd_emu10k1_voice_init(emu, pvoice->number);
- spin_unlock_irqrestore(&emu->voice_lock, flags);
+ guard(spinlock_irqsave)(&emu->voice_lock);
+ do {
+ last = pvoice->last;
+ voice_free(emu, pvoice++);
+ } while (!last);
return 0;
}
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index 537cfba829a5..657056a59175 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -1,39 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Ensoniq ES1370/ES1371 AudioPCI soundcard
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
* Thomas Sailer <sailer@ife.ee.ethz.ch>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/* Power-Management-Code ( CONFIG_PM )
* for ens1371 only ( FIXME )
* derived from cs4281.c, atiixp.c and via82xx.c
- * using http://www.alsa-project.org/~tiwai/writing-an-alsa-driver/
+ * using https://www.kernel.org/doc/html/latest/sound/kernel-api/writing-an-alsa-driver.html
* by Kurt J. Bosch
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/gameport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
@@ -55,8 +41,10 @@
#ifdef CHIP1370
#define DRIVER_NAME "ENS1370"
+#define CHIP_NAME "ES1370" /* it can be ENS but just to keep compatibility... */
#else
#define DRIVER_NAME "ENS1371"
+#define CHIP_NAME "ES1371"
#endif
@@ -64,31 +52,23 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Thomas Sailer <sailer@ife.ee.et
MODULE_LICENSE("GPL");
#ifdef CHIP1370
MODULE_DESCRIPTION("Ensoniq AudioPCI ES1370");
-MODULE_SUPPORTED_DEVICE("{{Ensoniq,AudioPCI-97 ES1370},"
- "{Creative Labs,SB PCI64/128 (ES1370)}}");
#endif
#ifdef CHIP1371
MODULE_DESCRIPTION("Ensoniq/Creative AudioPCI ES1371+");
-MODULE_SUPPORTED_DEVICE("{{Ensoniq,AudioPCI ES1371/73},"
- "{Ensoniq,AudioPCI ES1373},"
- "{Creative Labs,Ectiva EV1938},"
- "{Creative Labs,SB PCI64/128 (ES1371/73)},"
- "{Creative Labs,Vibra PCI128},"
- "{Ectiva,EV1938}}");
#endif
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */
#ifdef SUPPORT_JOYSTICK
#ifdef CHIP1371
static int joystick_port[SNDRV_CARDS];
#else
-static int joystick[SNDRV_CARDS];
+static bool joystick[SNDRV_CARDS];
#endif
#endif
#ifdef CHIP1371
@@ -104,7 +84,7 @@ module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Ensoniq AudioPCI soundcard.");
#ifdef SUPPORT_JOYSTICK
#ifdef CHIP1371
-module_param_array(joystick_port, int, NULL, 0444);
+module_param_hw_array(joystick_port, int, ioport, NULL, 0444);
MODULE_PARM_DESC(joystick_port, "Joystick port address.");
#else
module_param_array(joystick, bool, NULL, 0444);
@@ -229,6 +209,7 @@ MODULE_PARM_DESC(lineio, "Line In to Rear Out (0 = auto, 1 = force).");
#define ES_REG_1371_CODEC 0x14 /* W/R: Codec Read/Write register address */
#define ES_1371_CODEC_RDY (1<<31) /* codec ready */
#define ES_1371_CODEC_WIP (1<<30) /* codec register access in progress */
+#define EV_1938_CODEC_MAGIC (1<<26)
#define ES_1371_CODEC_PIRD (1<<23) /* codec read/write select register */
#define ES_1371_CODEC_WRITE(a,d) ((((a)&0x7f)<<16)|(((d)&0xffff)<<0))
#define ES_1371_CODEC_READS(a) ((((a)&0x7f)<<16)|ES_1371_CODEC_PIRD)
@@ -433,7 +414,7 @@ struct ensoniq {
unsigned int spdif_stream;
#ifdef CHIP1370
- struct snd_dma_buffer dma_bug;
+ struct snd_dma_buffer *dma_bug;
#endif
#ifdef SUPPORT_JOYSTICK
@@ -443,7 +424,7 @@ struct ensoniq {
static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id);
-static DEFINE_PCI_DEVICE_TABLE(snd_audiopci_ids) = {
+static const struct pci_device_id snd_audiopci_ids[] = {
#ifdef CHIP1370
{ PCI_VDEVICE(ENSONIQ, 0x5000), 0, }, /* ES1370 */
#endif
@@ -464,41 +445,41 @@ MODULE_DEVICE_TABLE(pci, snd_audiopci_ids);
#define POLL_COUNT 0xa000
#ifdef CHIP1370
-static unsigned int snd_es1370_fixed_rates[] =
+static const unsigned int snd_es1370_fixed_rates[] =
{5512, 11025, 22050, 44100};
-static struct snd_pcm_hw_constraint_list snd_es1370_hw_constraints_rates = {
+static const struct snd_pcm_hw_constraint_list snd_es1370_hw_constraints_rates = {
.count = 4,
.list = snd_es1370_fixed_rates,
.mask = 0,
};
-static struct snd_ratnum es1370_clock = {
+static const struct snd_ratnum es1370_clock = {
.num = ES_1370_SRCLOCK,
.den_min = 29,
.den_max = 353,
.den_step = 1,
};
-static struct snd_pcm_hw_constraint_ratnums snd_es1370_hw_constraints_clock = {
+static const struct snd_pcm_hw_constraint_ratnums snd_es1370_hw_constraints_clock = {
.nrats = 1,
.rats = &es1370_clock,
};
#else
-static struct snd_ratden es1371_dac_clock = {
+static const struct snd_ratden es1371_dac_clock = {
.num_min = 3000 * (1 << 15),
.num_max = 48000 * (1 << 15),
.num_step = 3000,
.den = 1 << 15,
};
-static struct snd_pcm_hw_constraint_ratdens snd_es1371_hw_constraints_dac_clock = {
+static const struct snd_pcm_hw_constraint_ratdens snd_es1371_hw_constraints_dac_clock = {
.nrats = 1,
.rats = &es1371_dac_clock,
};
-static struct snd_ratnum es1371_adc_clock = {
+static const struct snd_ratnum es1371_adc_clock = {
.num = 48000 << 15,
.den_min = 32768,
.den_max = 393216,
.den_step = 1,
};
-static struct snd_pcm_hw_constraint_ratnums snd_es1371_hw_constraints_adc_clock = {
+static const struct snd_pcm_hw_constraint_ratnums snd_es1371_hw_constraints_adc_clock = {
.nrats = 1,
.rats = &es1371_adc_clock,
};
@@ -522,7 +503,7 @@ static unsigned int snd_es1371_wait_src_ready(struct ensoniq * ensoniq)
return r;
cond_resched();
}
- snd_printk(KERN_ERR "wait src ready timeout 0x%lx [0x%x]\n",
+ dev_err(ensoniq->card->dev, "wait src ready timeout 0x%lx [0x%x]\n",
ES_REG(ensoniq, 1371_SMPRATE), r);
return 0;
}
@@ -584,7 +565,7 @@ static void snd_es1370_codec_write(struct snd_ak4531 *ak4531,
unsigned long end_time = jiffies + HZ / 10;
#if 0
- printk(KERN_DEBUG
+ dev_dbg(ensoniq->card->dev,
"CODEC WRITE: reg = 0x%x, val = 0x%x (0x%x), creg = 0x%x\n",
reg, val, ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC));
#endif
@@ -595,7 +576,7 @@ static void snd_es1370_codec_write(struct snd_ak4531 *ak4531,
}
schedule_timeout_uninterruptible(1);
} while (time_after(end_time, jiffies));
- snd_printk(KERN_ERR "codec write timeout, status = 0x%x\n",
+ dev_err(ensoniq->card->dev, "codec write timeout, status = 0x%x\n",
inl(ES_REG(ensoniq, STATUS)));
}
@@ -603,13 +584,19 @@ static void snd_es1370_codec_write(struct snd_ak4531 *ak4531,
#ifdef CHIP1371
+static inline bool is_ev1938(struct ensoniq *ensoniq)
+{
+ return ensoniq->pci->device == 0x8938;
+}
+
static void snd_es1371_codec_write(struct snd_ac97 *ac97,
unsigned short reg, unsigned short val)
{
struct ensoniq *ensoniq = ac97->private_data;
- unsigned int t, x;
+ unsigned int t, x, flag;
- mutex_lock(&ensoniq->src_mutex);
+ flag = is_ev1938(ensoniq) ? EV_1938_CODEC_MAGIC : 0;
+ guard(mutex)(&ensoniq->src_mutex);
for (t = 0; t < POLL_COUNT; t++) {
if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) {
/* save the current state for latter */
@@ -630,16 +617,15 @@ static void snd_es1371_codec_write(struct snd_ac97 *ac97,
0x00010000)
break;
}
- outl(ES_1371_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1371_CODEC));
+ outl(ES_1371_CODEC_WRITE(reg, val) | flag,
+ ES_REG(ensoniq, 1371_CODEC));
/* restore SRC reg */
snd_es1371_wait_src_ready(ensoniq);
outl(x, ES_REG(ensoniq, 1371_SMPRATE));
- mutex_unlock(&ensoniq->src_mutex);
return;
}
}
- mutex_unlock(&ensoniq->src_mutex);
- snd_printk(KERN_ERR "codec write timeout at 0x%lx [0x%x]\n",
+ dev_err(ensoniq->card->dev, "codec write timeout at 0x%lx [0x%x]\n",
ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC)));
}
@@ -647,8 +633,9 @@ static unsigned short snd_es1371_codec_read(struct snd_ac97 *ac97,
unsigned short reg)
{
struct ensoniq *ensoniq = ac97->private_data;
- unsigned int t, x, fail = 0;
+ unsigned int t, x, flag, fail = 0;
+ flag = is_ev1938(ensoniq) ? EV_1938_CODEC_MAGIC : 0;
__again:
mutex_lock(&ensoniq->src_mutex);
for (t = 0; t < POLL_COUNT; t++) {
@@ -671,7 +658,8 @@ static unsigned short snd_es1371_codec_read(struct snd_ac97 *ac97,
0x00010000)
break;
}
- outl(ES_1371_CODEC_READS(reg), ES_REG(ensoniq, 1371_CODEC));
+ outl(ES_1371_CODEC_READS(reg) | flag,
+ ES_REG(ensoniq, 1371_CODEC));
/* restore SRC reg */
snd_es1371_wait_src_ready(ensoniq);
outl(x, ES_REG(ensoniq, 1371_SMPRATE));
@@ -682,15 +670,21 @@ static unsigned short snd_es1371_codec_read(struct snd_ac97 *ac97,
}
/* now wait for the stinkin' data (RDY) */
for (t = 0; t < POLL_COUNT; t++) {
- if ((x = inl(ES_REG(ensoniq, 1371_CODEC))) & ES_1371_CODEC_RDY) {
+ x = inl(ES_REG(ensoniq, 1371_CODEC));
+ if (x & ES_1371_CODEC_RDY) {
+ if (is_ev1938(ensoniq)) {
+ for (t = 0; t < 100; t++)
+ inl(ES_REG(ensoniq, CONTROL));
+ x = inl(ES_REG(ensoniq, 1371_CODEC));
+ }
mutex_unlock(&ensoniq->src_mutex);
return ES_1371_CODEC_READ(x);
}
}
mutex_unlock(&ensoniq->src_mutex);
if (++fail > 10) {
- snd_printk(KERN_ERR "codec read timeout (final) "
- "at 0x%lx, reg = 0x%x [0x%x]\n",
+ dev_err(ensoniq->card->dev,
+ "codec read timeout (final) at 0x%lx, reg = 0x%x [0x%x]\n",
ES_REG(ensoniq, 1371_CODEC), reg,
inl(ES_REG(ensoniq, 1371_CODEC)));
return 0;
@@ -699,7 +693,7 @@ static unsigned short snd_es1371_codec_read(struct snd_ac97 *ac97,
}
}
mutex_unlock(&ensoniq->src_mutex);
- snd_printk(KERN_ERR "es1371: codec read timeout at 0x%lx [0x%x]\n",
+ dev_err(ensoniq->card->dev, "codec read timeout at 0x%lx [0x%x]\n",
ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC)));
return 0;
}
@@ -715,15 +709,14 @@ static void snd_es1371_codec_wait(struct snd_ac97 *ac97)
static void snd_es1371_adc_rate(struct ensoniq * ensoniq, unsigned int rate)
{
- unsigned int n, truncm, freq, result;
+ unsigned int n, truncm, freq;
- mutex_lock(&ensoniq->src_mutex);
+ guard(mutex)(&ensoniq->src_mutex);
n = rate / 3000;
if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9)))
n--;
truncm = (21 * n - 1) | 1;
freq = ((48000UL << 15) / rate) * n;
- result = (48000UL << 15) / (freq / n);
if (rate >= 24000) {
if (truncm > 239)
truncm = 239;
@@ -742,15 +735,14 @@ static void snd_es1371_adc_rate(struct ensoniq * ensoniq, unsigned int rate)
snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, n << 8);
snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, n << 8);
- mutex_unlock(&ensoniq->src_mutex);
}
static void snd_es1371_dac1_rate(struct ensoniq * ensoniq, unsigned int rate)
{
unsigned int freq, r;
- mutex_lock(&ensoniq->src_mutex);
- freq = ((rate << 15) + 1500) / 3000;
+ guard(mutex)(&ensoniq->src_mutex);
+ freq = DIV_ROUND_CLOSEST(rate << 15, 3000);
r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE |
ES_1371_DIS_P2 | ES_1371_DIS_R1)) |
ES_1371_DIS_P1;
@@ -763,15 +755,14 @@ static void snd_es1371_dac1_rate(struct ensoniq * ensoniq, unsigned int rate)
r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE |
ES_1371_DIS_P2 | ES_1371_DIS_R1));
outl(r, ES_REG(ensoniq, 1371_SMPRATE));
- mutex_unlock(&ensoniq->src_mutex);
}
static void snd_es1371_dac2_rate(struct ensoniq * ensoniq, unsigned int rate)
{
unsigned int freq, r;
- mutex_lock(&ensoniq->src_mutex);
- freq = ((rate << 15) + 1500) / 3000;
+ guard(mutex)(&ensoniq->src_mutex);
+ freq = DIV_ROUND_CLOSEST(rate << 15, 3000);
r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE |
ES_1371_DIS_P1 | ES_1371_DIS_R1)) |
ES_1371_DIS_P2;
@@ -785,7 +776,6 @@ static void snd_es1371_dac2_rate(struct ensoniq * ensoniq, unsigned int rate)
r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE |
ES_1371_DIS_P1 | ES_1371_DIS_R1));
outl(r, ES_REG(ensoniq, 1371_SMPRATE));
- mutex_unlock(&ensoniq->src_mutex);
}
#endif /* CHIP1371 */
@@ -809,13 +799,13 @@ static int snd_ensoniq_trigger(struct snd_pcm_substream *substream, int cmd)
} else if (s == ensoniq->capture_substream)
return -EINVAL;
}
- spin_lock(&ensoniq->reg_lock);
- if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
- ensoniq->sctrl |= what;
- else
- ensoniq->sctrl &= ~what;
- outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
- spin_unlock(&ensoniq->reg_lock);
+ scoped_guard(spinlock, &ensoniq->reg_lock) {
+ if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
+ ensoniq->sctrl |= what;
+ else
+ ensoniq->sctrl &= ~what;
+ outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+ }
break;
}
case SNDRV_PCM_TRIGGER_START:
@@ -835,13 +825,13 @@ static int snd_ensoniq_trigger(struct snd_pcm_substream *substream, int cmd)
snd_pcm_trigger_done(s, substream);
}
}
- spin_lock(&ensoniq->reg_lock);
- if (cmd == SNDRV_PCM_TRIGGER_START)
- ensoniq->ctrl |= what;
- else
- ensoniq->ctrl &= ~what;
- outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
- spin_unlock(&ensoniq->reg_lock);
+ scoped_guard(spinlock, &ensoniq->reg_lock) {
+ if (cmd == SNDRV_PCM_TRIGGER_START)
+ ensoniq->ctrl |= what;
+ else
+ ensoniq->ctrl &= ~what;
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ }
break;
}
default:
@@ -854,17 +844,6 @@ static int snd_ensoniq_trigger(struct snd_pcm_substream *substream, int cmd)
* PCM part
*/
-static int snd_ensoniq_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_ensoniq_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_ensoniq_playback1_prepare(struct snd_pcm_substream *substream)
{
struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
@@ -877,36 +856,36 @@ static int snd_ensoniq_playback1_prepare(struct snd_pcm_substream *substream)
mode |= 0x02;
if (runtime->channels > 1)
mode |= 0x01;
- spin_lock_irq(&ensoniq->reg_lock);
- ensoniq->ctrl &= ~ES_DAC1_EN;
+ scoped_guard(spinlock_irq, &ensoniq->reg_lock) {
+ ensoniq->ctrl &= ~ES_DAC1_EN;
#ifdef CHIP1371
- /* 48k doesn't need SRC (it breaks AC3-passthru) */
- if (runtime->rate == 48000)
- ensoniq->ctrl |= ES_1373_BYPASS_P1;
- else
- ensoniq->ctrl &= ~ES_1373_BYPASS_P1;
+ /* 48k doesn't need SRC (it breaks AC3-passthru) */
+ if (runtime->rate == 48000)
+ ensoniq->ctrl |= ES_1373_BYPASS_P1;
+ else
+ ensoniq->ctrl &= ~ES_1373_BYPASS_P1;
#endif
- outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
- outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
- outl(runtime->dma_addr, ES_REG(ensoniq, DAC1_FRAME));
- outl((ensoniq->p1_dma_size >> 2) - 1, ES_REG(ensoniq, DAC1_SIZE));
- ensoniq->sctrl &= ~(ES_P1_LOOP_SEL | ES_P1_PAUSE | ES_P1_SCT_RLD | ES_P1_MODEM);
- ensoniq->sctrl |= ES_P1_INT_EN | ES_P1_MODEO(mode);
- outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
- outl((ensoniq->p1_period_size >> snd_ensoniq_sample_shift[mode]) - 1,
- ES_REG(ensoniq, DAC1_COUNT));
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
+ outl(runtime->dma_addr, ES_REG(ensoniq, DAC1_FRAME));
+ outl((ensoniq->p1_dma_size >> 2) - 1, ES_REG(ensoniq, DAC1_SIZE));
+ ensoniq->sctrl &= ~(ES_P1_LOOP_SEL | ES_P1_PAUSE | ES_P1_SCT_RLD | ES_P1_MODEM);
+ ensoniq->sctrl |= ES_P1_INT_EN | ES_P1_MODEO(mode);
+ outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+ outl((ensoniq->p1_period_size >> snd_ensoniq_sample_shift[mode]) - 1,
+ ES_REG(ensoniq, DAC1_COUNT));
#ifdef CHIP1370
- ensoniq->ctrl &= ~ES_1370_WTSRSELM;
- switch (runtime->rate) {
- case 5512: ensoniq->ctrl |= ES_1370_WTSRSEL(0); break;
- case 11025: ensoniq->ctrl |= ES_1370_WTSRSEL(1); break;
- case 22050: ensoniq->ctrl |= ES_1370_WTSRSEL(2); break;
- case 44100: ensoniq->ctrl |= ES_1370_WTSRSEL(3); break;
- default: snd_BUG();
- }
+ ensoniq->ctrl &= ~ES_1370_WTSRSELM;
+ switch (runtime->rate) {
+ case 5512: ensoniq->ctrl |= ES_1370_WTSRSEL(0); break;
+ case 11025: ensoniq->ctrl |= ES_1370_WTSRSEL(1); break;
+ case 22050: ensoniq->ctrl |= ES_1370_WTSRSEL(2); break;
+ case 44100: ensoniq->ctrl |= ES_1370_WTSRSEL(3); break;
+ default: snd_BUG();
+ }
#endif
- outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
- spin_unlock_irq(&ensoniq->reg_lock);
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ }
#ifndef CHIP1370
snd_es1371_dac1_rate(ensoniq, runtime->rate);
#endif
@@ -925,28 +904,28 @@ static int snd_ensoniq_playback2_prepare(struct snd_pcm_substream *substream)
mode |= 0x02;
if (runtime->channels > 1)
mode |= 0x01;
- spin_lock_irq(&ensoniq->reg_lock);
- ensoniq->ctrl &= ~ES_DAC2_EN;
- outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
- outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
- outl(runtime->dma_addr, ES_REG(ensoniq, DAC2_FRAME));
- outl((ensoniq->p2_dma_size >> 2) - 1, ES_REG(ensoniq, DAC2_SIZE));
- ensoniq->sctrl &= ~(ES_P2_LOOP_SEL | ES_P2_PAUSE | ES_P2_DAC_SEN |
- ES_P2_END_INCM | ES_P2_ST_INCM | ES_P2_MODEM);
- ensoniq->sctrl |= ES_P2_INT_EN | ES_P2_MODEO(mode) |
- ES_P2_END_INCO(mode & 2 ? 2 : 1) | ES_P2_ST_INCO(0);
- outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
- outl((ensoniq->p2_period_size >> snd_ensoniq_sample_shift[mode]) - 1,
- ES_REG(ensoniq, DAC2_COUNT));
+ scoped_guard(spinlock_irq, &ensoniq->reg_lock) {
+ ensoniq->ctrl &= ~ES_DAC2_EN;
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
+ outl(runtime->dma_addr, ES_REG(ensoniq, DAC2_FRAME));
+ outl((ensoniq->p2_dma_size >> 2) - 1, ES_REG(ensoniq, DAC2_SIZE));
+ ensoniq->sctrl &= ~(ES_P2_LOOP_SEL | ES_P2_PAUSE | ES_P2_DAC_SEN |
+ ES_P2_END_INCM | ES_P2_ST_INCM | ES_P2_MODEM);
+ ensoniq->sctrl |= ES_P2_INT_EN | ES_P2_MODEO(mode) |
+ ES_P2_END_INCO(mode & 2 ? 2 : 1) | ES_P2_ST_INCO(0);
+ outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+ outl((ensoniq->p2_period_size >> snd_ensoniq_sample_shift[mode]) - 1,
+ ES_REG(ensoniq, DAC2_COUNT));
#ifdef CHIP1370
- if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_CAPTURE)) {
- ensoniq->ctrl &= ~ES_1370_PCLKDIVM;
- ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate));
- ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_PLAY2;
- }
+ if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_CAPTURE)) {
+ ensoniq->ctrl &= ~ES_1370_PCLKDIVM;
+ ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate));
+ ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_PLAY2;
+ }
#endif
- outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
- spin_unlock_irq(&ensoniq->reg_lock);
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ }
#ifndef CHIP1370
snd_es1371_dac2_rate(ensoniq, runtime->rate);
#endif
@@ -965,26 +944,26 @@ static int snd_ensoniq_capture_prepare(struct snd_pcm_substream *substream)
mode |= 0x02;
if (runtime->channels > 1)
mode |= 0x01;
- spin_lock_irq(&ensoniq->reg_lock);
- ensoniq->ctrl &= ~ES_ADC_EN;
- outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
- outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
- outl(runtime->dma_addr, ES_REG(ensoniq, ADC_FRAME));
- outl((ensoniq->c_dma_size >> 2) - 1, ES_REG(ensoniq, ADC_SIZE));
- ensoniq->sctrl &= ~(ES_R1_LOOP_SEL | ES_R1_MODEM);
- ensoniq->sctrl |= ES_R1_INT_EN | ES_R1_MODEO(mode);
- outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
- outl((ensoniq->c_period_size >> snd_ensoniq_sample_shift[mode]) - 1,
- ES_REG(ensoniq, ADC_COUNT));
+ scoped_guard(spinlock_irq, &ensoniq->reg_lock) {
+ ensoniq->ctrl &= ~ES_ADC_EN;
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
+ outl(runtime->dma_addr, ES_REG(ensoniq, ADC_FRAME));
+ outl((ensoniq->c_dma_size >> 2) - 1, ES_REG(ensoniq, ADC_SIZE));
+ ensoniq->sctrl &= ~(ES_R1_LOOP_SEL | ES_R1_MODEM);
+ ensoniq->sctrl |= ES_R1_INT_EN | ES_R1_MODEO(mode);
+ outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+ outl((ensoniq->c_period_size >> snd_ensoniq_sample_shift[mode]) - 1,
+ ES_REG(ensoniq, ADC_COUNT));
#ifdef CHIP1370
- if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_PLAY2)) {
- ensoniq->ctrl &= ~ES_1370_PCLKDIVM;
- ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate));
- ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_CAPTURE;
- }
+ if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_PLAY2)) {
+ ensoniq->ctrl &= ~ES_1370_PCLKDIVM;
+ ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate));
+ ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_CAPTURE;
+ }
#endif
- outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
- spin_unlock_irq(&ensoniq->reg_lock);
+ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
+ }
#ifndef CHIP1370
snd_es1371_adc_rate(ensoniq, runtime->rate);
#endif
@@ -996,16 +975,14 @@ static snd_pcm_uframes_t snd_ensoniq_playback1_pointer(struct snd_pcm_substream
struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
size_t ptr;
- spin_lock(&ensoniq->reg_lock);
+ guard(spinlock)(&ensoniq->reg_lock);
if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC1_EN) {
outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC1_SIZE)));
- ptr = bytes_to_frames(substream->runtime, ptr);
+ return bytes_to_frames(substream->runtime, ptr);
} else {
- ptr = 0;
+ return 0;
}
- spin_unlock(&ensoniq->reg_lock);
- return ptr;
}
static snd_pcm_uframes_t snd_ensoniq_playback2_pointer(struct snd_pcm_substream *substream)
@@ -1013,16 +990,14 @@ static snd_pcm_uframes_t snd_ensoniq_playback2_pointer(struct snd_pcm_substream
struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
size_t ptr;
- spin_lock(&ensoniq->reg_lock);
+ guard(spinlock)(&ensoniq->reg_lock);
if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC2_EN) {
outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC2_SIZE)));
- ptr = bytes_to_frames(substream->runtime, ptr);
+ return bytes_to_frames(substream->runtime, ptr);
} else {
- ptr = 0;
+ return 0;
}
- spin_unlock(&ensoniq->reg_lock);
- return ptr;
}
static snd_pcm_uframes_t snd_ensoniq_capture_pointer(struct snd_pcm_substream *substream)
@@ -1030,19 +1005,17 @@ static snd_pcm_uframes_t snd_ensoniq_capture_pointer(struct snd_pcm_substream *s
struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
size_t ptr;
- spin_lock(&ensoniq->reg_lock);
+ guard(spinlock)(&ensoniq->reg_lock);
if (inl(ES_REG(ensoniq, CONTROL)) & ES_ADC_EN) {
outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, ADC_SIZE)));
- ptr = bytes_to_frames(substream->runtime, ptr);
+ return bytes_to_frames(substream->runtime, ptr);
} else {
- ptr = 0;
+ return 0;
}
- spin_unlock(&ensoniq->reg_lock);
- return ptr;
}
-static struct snd_pcm_hardware snd_ensoniq_playback1 =
+static const struct snd_pcm_hardware snd_ensoniq_playback1 =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1069,7 +1042,7 @@ static struct snd_pcm_hardware snd_ensoniq_playback1 =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_ensoniq_playback2 =
+static const struct snd_pcm_hardware snd_ensoniq_playback2 =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1089,7 +1062,7 @@ static struct snd_pcm_hardware snd_ensoniq_playback2 =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_ensoniq_capture =
+static const struct snd_pcm_hardware snd_ensoniq_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1117,10 +1090,10 @@ static int snd_ensoniq_playback1_open(struct snd_pcm_substream *substream)
ensoniq->playback1_substream = substream;
runtime->hw = snd_ensoniq_playback1;
snd_pcm_set_sync(substream);
- spin_lock_irq(&ensoniq->reg_lock);
- if (ensoniq->spdif && ensoniq->playback2_substream == NULL)
- ensoniq->spdif_stream = ensoniq->spdif_default;
- spin_unlock_irq(&ensoniq->reg_lock);
+ scoped_guard(spinlock_irq, &ensoniq->reg_lock) {
+ if (ensoniq->spdif && ensoniq->playback2_substream == NULL)
+ ensoniq->spdif_stream = ensoniq->spdif_default;
+ }
#ifdef CHIP1370
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&snd_es1370_hw_constraints_rates);
@@ -1140,10 +1113,10 @@ static int snd_ensoniq_playback2_open(struct snd_pcm_substream *substream)
ensoniq->playback2_substream = substream;
runtime->hw = snd_ensoniq_playback2;
snd_pcm_set_sync(substream);
- spin_lock_irq(&ensoniq->reg_lock);
- if (ensoniq->spdif && ensoniq->playback1_substream == NULL)
- ensoniq->spdif_stream = ensoniq->spdif_default;
- spin_unlock_irq(&ensoniq->reg_lock);
+ scoped_guard(spinlock_irq, &ensoniq->reg_lock) {
+ if (ensoniq->spdif && ensoniq->playback1_substream == NULL)
+ ensoniq->spdif_stream = ensoniq->spdif_default;
+ }
#ifdef CHIP1370
snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&snd_es1370_hw_constraints_clock);
@@ -1187,12 +1160,11 @@ static int snd_ensoniq_playback2_close(struct snd_pcm_substream *substream)
struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
ensoniq->playback2_substream = NULL;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
#ifdef CHIP1370
ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_PLAY2;
#endif
ensoniq->mode &= ~ES_MODE_PLAY2;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -1201,61 +1173,52 @@ static int snd_ensoniq_capture_close(struct snd_pcm_substream *substream)
struct ensoniq *ensoniq = snd_pcm_substream_chip(substream);
ensoniq->capture_substream = NULL;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
#ifdef CHIP1370
ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_CAPTURE;
#endif
ensoniq->mode &= ~ES_MODE_CAPTURE;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
-static struct snd_pcm_ops snd_ensoniq_playback1_ops = {
+static const struct snd_pcm_ops snd_ensoniq_playback1_ops = {
.open = snd_ensoniq_playback1_open,
.close = snd_ensoniq_playback1_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ensoniq_hw_params,
- .hw_free = snd_ensoniq_hw_free,
.prepare = snd_ensoniq_playback1_prepare,
.trigger = snd_ensoniq_trigger,
.pointer = snd_ensoniq_playback1_pointer,
};
-static struct snd_pcm_ops snd_ensoniq_playback2_ops = {
+static const struct snd_pcm_ops snd_ensoniq_playback2_ops = {
.open = snd_ensoniq_playback2_open,
.close = snd_ensoniq_playback2_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ensoniq_hw_params,
- .hw_free = snd_ensoniq_hw_free,
.prepare = snd_ensoniq_playback2_prepare,
.trigger = snd_ensoniq_trigger,
.pointer = snd_ensoniq_playback2_pointer,
};
-static struct snd_pcm_ops snd_ensoniq_capture_ops = {
+static const struct snd_pcm_ops snd_ensoniq_capture_ops = {
.open = snd_ensoniq_capture_open,
.close = snd_ensoniq_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ensoniq_hw_params,
- .hw_free = snd_ensoniq_hw_free,
.prepare = snd_ensoniq_capture_prepare,
.trigger = snd_ensoniq_trigger,
.pointer = snd_ensoniq_capture_pointer,
};
-static int __devinit snd_ensoniq_pcm(struct ensoniq * ensoniq, int device,
- struct snd_pcm ** rpcm)
+static const struct snd_pcm_chmap_elem surround_map[] = {
+ { .channels = 1,
+ .map = { SNDRV_CHMAP_MONO } },
+ { .channels = 2,
+ .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+ { }
+};
+
+static int snd_ensoniq_pcm(struct ensoniq *ensoniq, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
-#ifdef CHIP1370
- err = snd_pcm_new(ensoniq->card, "ES1370/1", device, 1, 1, &pcm);
-#else
- err = snd_pcm_new(ensoniq->card, "ES1371/1", device, 1, 1, &pcm);
-#endif
+ err = snd_pcm_new(ensoniq->card, CHIP_NAME "/1", device, 1, 1, &pcm);
if (err < 0)
return err;
@@ -1268,34 +1231,28 @@ static int __devinit snd_ensoniq_pcm(struct ensoniq * ensoniq, int device,
pcm->private_data = ensoniq;
pcm->info_flags = 0;
-#ifdef CHIP1370
- strcpy(pcm->name, "ES1370 DAC2/ADC");
-#else
- strcpy(pcm->name, "ES1371 DAC2/ADC");
-#endif
+ strscpy(pcm->name, CHIP_NAME " DAC2/ADC");
ensoniq->pcm1 = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &ensoniq->pci->dev, 64*1024, 128*1024);
- if (rpcm)
- *rpcm = pcm;
- return 0;
+#ifdef CHIP1370
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ surround_map, 2, 0, NULL);
+#else
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_std_chmaps, 2, 0, NULL);
+#endif
+ return err;
}
-static int __devinit snd_ensoniq_pcm2(struct ensoniq * ensoniq, int device,
- struct snd_pcm ** rpcm)
+static int snd_ensoniq_pcm2(struct ensoniq *ensoniq, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
-#ifdef CHIP1370
- err = snd_pcm_new(ensoniq->card, "ES1370/2", device, 1, 0, &pcm);
-#else
- err = snd_pcm_new(ensoniq->card, "ES1371/2", device, 1, 0, &pcm);
-#endif
+ err = snd_pcm_new(ensoniq->card, CHIP_NAME "/2", device, 1, 0, &pcm);
if (err < 0)
return err;
@@ -1306,19 +1263,20 @@ static int __devinit snd_ensoniq_pcm2(struct ensoniq * ensoniq, int device,
#endif
pcm->private_data = ensoniq;
pcm->info_flags = 0;
-#ifdef CHIP1370
- strcpy(pcm->name, "ES1370 DAC1");
-#else
- strcpy(pcm->name, "ES1371 DAC1");
-#endif
+ strscpy(pcm->name, CHIP_NAME " DAC1");
ensoniq->pcm2 = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &ensoniq->pci->dev, 64*1024, 128*1024);
- if (rpcm)
- *rpcm = pcm;
- return 0;
+#ifdef CHIP1370
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_std_chmaps, 2, 0, NULL);
+#else
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ surround_map, 2, 0, NULL);
+#endif
+ return err;
}
/*
@@ -1341,12 +1299,12 @@ static int snd_ens1373_spdif_default_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&ensoniq->reg_lock);
+
+ guard(spinlock_irq)(&ensoniq->reg_lock);
ucontrol->value.iec958.status[0] = (ensoniq->spdif_default >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (ensoniq->spdif_default >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (ensoniq->spdif_default >> 16) & 0xff;
ucontrol->value.iec958.status[3] = (ensoniq->spdif_default >> 24) & 0xff;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -1361,13 +1319,12 @@ static int snd_ens1373_spdif_default_put(struct snd_kcontrol *kcontrol,
((u32)ucontrol->value.iec958.status[1] << 8) |
((u32)ucontrol->value.iec958.status[2] << 16) |
((u32)ucontrol->value.iec958.status[3] << 24);
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
change = ensoniq->spdif_default != val;
ensoniq->spdif_default = val;
if (change && ensoniq->playback1_substream == NULL &&
ensoniq->playback2_substream == NULL)
outl(val, ES_REG(ensoniq, CHANNEL_STATUS));
- spin_unlock_irq(&ensoniq->reg_lock);
return change;
}
@@ -1385,12 +1342,12 @@ static int snd_ens1373_spdif_stream_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&ensoniq->reg_lock);
+
+ guard(spinlock_irq)(&ensoniq->reg_lock);
ucontrol->value.iec958.status[0] = (ensoniq->spdif_stream >> 0) & 0xff;
ucontrol->value.iec958.status[1] = (ensoniq->spdif_stream >> 8) & 0xff;
ucontrol->value.iec958.status[2] = (ensoniq->spdif_stream >> 16) & 0xff;
ucontrol->value.iec958.status[3] = (ensoniq->spdif_stream >> 24) & 0xff;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -1405,13 +1362,12 @@ static int snd_ens1373_spdif_stream_put(struct snd_kcontrol *kcontrol,
((u32)ucontrol->value.iec958.status[1] << 8) |
((u32)ucontrol->value.iec958.status[2] << 16) |
((u32)ucontrol->value.iec958.status[3] << 24);
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
change = ensoniq->spdif_stream != val;
ensoniq->spdif_stream = val;
if (change && (ensoniq->playback1_substream != NULL ||
ensoniq->playback2_substream != NULL))
outl(val, ES_REG(ensoniq, CHANNEL_STATUS));
- spin_unlock_irq(&ensoniq->reg_lock);
return change;
}
@@ -1426,9 +1382,8 @@ static int snd_es1371_spdif_get(struct snd_kcontrol *kcontrol,
{
struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
ucontrol->value.integer.value[0] = ensoniq->ctrl & ES_1373_SPDIF_THRU ? 1 : 0;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -1441,7 +1396,7 @@ static int snd_es1371_spdif_put(struct snd_kcontrol *kcontrol,
nval1 = ucontrol->value.integer.value[0] ? ES_1373_SPDIF_THRU : 0;
nval2 = ucontrol->value.integer.value[0] ? ES_1373_SPDIF_EN : 0;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
change = (ensoniq->ctrl & ES_1373_SPDIF_THRU) != nval1;
ensoniq->ctrl &= ~ES_1373_SPDIF_THRU;
ensoniq->ctrl |= nval1;
@@ -1449,13 +1404,12 @@ static int snd_es1371_spdif_put(struct snd_kcontrol *kcontrol,
ensoniq->cssr |= nval2;
outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
- spin_unlock_irq(&ensoniq->reg_lock);
return change;
}
/* spdif controls */
-static struct snd_kcontrol_new snd_es1371_mixer_spdif[] __devinitdata = {
+static const struct snd_kcontrol_new snd_es1371_mixer_spdif[] = {
ES1371_SPDIF(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH)),
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1489,12 +1443,11 @@ static int snd_es1373_rear_get(struct snd_kcontrol *kcontrol,
struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
int val = 0;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
if ((ensoniq->cssr & (ES_1373_REAR_BIT27|ES_1373_REAR_BIT26|
ES_1373_REAR_BIT24)) == ES_1373_REAR_BIT26)
val = 1;
ucontrol->value.integer.value[0] = val;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -1507,17 +1460,16 @@ static int snd_es1373_rear_put(struct snd_kcontrol *kcontrol,
nval1 = ucontrol->value.integer.value[0] ?
ES_1373_REAR_BIT26 : (ES_1373_REAR_BIT27|ES_1373_REAR_BIT24);
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
change = (ensoniq->cssr & (ES_1373_REAR_BIT27|
ES_1373_REAR_BIT26|ES_1373_REAR_BIT24)) != nval1;
ensoniq->cssr &= ~(ES_1373_REAR_BIT27|ES_1373_REAR_BIT26|ES_1373_REAR_BIT24);
ensoniq->cssr |= nval1;
outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
- spin_unlock_irq(&ensoniq->reg_lock);
return change;
}
-static struct snd_kcontrol_new snd_ens1373_rear __devinitdata =
+static const struct snd_kcontrol_new snd_ens1373_rear =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "AC97 2ch->4ch Copy Switch",
@@ -1534,11 +1486,10 @@ static int snd_es1373_line_get(struct snd_kcontrol *kcontrol,
struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
int val = 0;
- spin_lock_irq(&ensoniq->reg_lock);
- if ((ensoniq->ctrl & ES_1371_GPIO_OUTM) >= 4)
+ guard(spinlock_irq)(&ensoniq->reg_lock);
+ if (ensoniq->ctrl & ES_1371_GPIO_OUT(4))
val = 1;
ucontrol->value.integer.value[0] = val;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -1549,7 +1500,7 @@ static int snd_es1373_line_put(struct snd_kcontrol *kcontrol,
int changed;
unsigned int ctrl;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
ctrl = ensoniq->ctrl;
if (ucontrol->value.integer.value[0])
ensoniq->ctrl |= ES_1371_GPIO_OUT(4); /* switch line-in -> rear out */
@@ -1558,11 +1509,10 @@ static int snd_es1373_line_put(struct snd_kcontrol *kcontrol,
changed = (ctrl != ensoniq->ctrl);
if (changed)
outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
- spin_unlock_irq(&ensoniq->reg_lock);
return changed;
}
-static struct snd_kcontrol_new snd_ens1373_line __devinitdata =
+static const struct snd_kcontrol_new snd_ens1373_line =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line In->Rear Out Switch",
@@ -1584,7 +1534,7 @@ struct es1371_quirk {
};
static int es1371_quirk_lookup(struct ensoniq *ensoniq,
- struct es1371_quirk *list)
+ const struct es1371_quirk *list)
{
while (list->vid != (unsigned short)PCI_ANY_ID) {
if (ensoniq->pci->vendor == list->vid &&
@@ -1596,7 +1546,7 @@ static int es1371_quirk_lookup(struct ensoniq *ensoniq,
return 0;
}
-static struct es1371_quirk es1371_spdif_present[] __devinitdata = {
+static const struct es1371_quirk es1371_spdif_present[] = {
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_C },
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_D },
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_E },
@@ -1605,26 +1555,27 @@ static struct es1371_quirk es1371_spdif_present[] __devinitdata = {
{ .vid = PCI_ANY_ID, .did = PCI_ANY_ID }
};
-static struct snd_pci_quirk ens1373_line_quirk[] __devinitdata = {
+static const struct snd_pci_quirk ens1373_line_quirk[] = {
SND_PCI_QUIRK_ID(0x1274, 0x2000), /* GA-7DXR */
SND_PCI_QUIRK_ID(0x1458, 0xa000), /* GA-8IEXP */
{ } /* end */
};
-static int __devinit snd_ensoniq_1371_mixer(struct ensoniq *ensoniq,
- int has_spdif, int has_line)
+static int snd_ensoniq_1371_mixer(struct ensoniq *ensoniq,
+ int has_spdif, int has_line)
{
struct snd_card *card = ensoniq->card;
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_es1371_codec_write,
.read = snd_es1371_codec_read,
.wait = snd_es1371_codec_wait,
};
- if ((err = snd_ac97_bus(card, 0, &ops, NULL, &pbus)) < 0)
+ err = snd_ac97_bus(card, 0, &ops, NULL, &pbus);
+ if (err < 0)
return err;
memset(&ac97, 0, sizeof(ac97));
@@ -1632,7 +1583,8 @@ static int __devinit snd_ensoniq_1371_mixer(struct ensoniq *ensoniq,
ac97.private_free = snd_ensoniq_mixer_free_ac97;
ac97.pci = ensoniq->pci;
ac97.scaps = AC97_SCAP_AUDIO;
- if ((err = snd_ac97_mixer(pbus, &ac97, &ensoniq->u.es1371.ac97)) < 0)
+ err = snd_ac97_mixer(pbus, &ac97, &ensoniq->u.es1371.ac97);
+ if (err < 0)
return err;
if (has_spdif > 0 ||
(!has_spdif && es1371_quirk_lookup(ensoniq, es1371_spdif_present))) {
@@ -1692,9 +1644,8 @@ static int snd_ensoniq_control_get(struct snd_kcontrol *kcontrol,
struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol);
int mask = kcontrol->private_value;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
ucontrol->value.integer.value[0] = ensoniq->ctrl & mask ? 1 : 0;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -1707,12 +1658,11 @@ static int snd_ensoniq_control_put(struct snd_kcontrol *kcontrol,
int change;
nval = ucontrol->value.integer.value[0] ? mask : 0;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
change = (ensoniq->ctrl & mask) != nval;
ensoniq->ctrl &= ~mask;
ensoniq->ctrl |= nval;
outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
- spin_unlock_irq(&ensoniq->reg_lock);
return change;
}
@@ -1720,7 +1670,7 @@ static int snd_ensoniq_control_put(struct snd_kcontrol *kcontrol,
* ENS1370 mixer
*/
-static struct snd_kcontrol_new snd_es1370_controls[2] __devinitdata = {
+static const struct snd_kcontrol_new snd_es1370_controls[2] = {
ENSONIQ_CONTROL("PCM 0 Output also on Line-In Jack", ES_1370_XCTL0),
ENSONIQ_CONTROL("Mic +5V bias", ES_1370_XCTL1)
};
@@ -1733,7 +1683,7 @@ static void snd_ensoniq_mixer_free_ak4531(struct snd_ak4531 *ak4531)
ensoniq->u.es1370.ak4531 = NULL;
}
-static int __devinit snd_ensoniq_1370_mixer(struct ensoniq * ensoniq)
+static int snd_ensoniq_1370_mixer(struct ensoniq *ensoniq)
{
struct snd_card *card = ensoniq->card;
struct snd_ak4531 ak4531;
@@ -1752,7 +1702,8 @@ static int __devinit snd_ensoniq_1370_mixer(struct ensoniq * ensoniq)
ak4531.write = snd_es1370_codec_write;
ak4531.private_data = ensoniq;
ak4531.private_free = snd_ensoniq_mixer_free_ak4531;
- if ((err = snd_ak4531_mixer(card, &ak4531, &ensoniq->u.es1370.ak4531)) < 0)
+ err = snd_ak4531_mixer(card, &ak4531, &ensoniq->u.es1370.ak4531);
+ if (err < 0)
return err;
for (idx = 0; idx < ES1370_CONTROLS; idx++) {
err = snd_ctl_add(card, snd_ctl_new1(&snd_es1370_controls[idx], ensoniq));
@@ -1767,7 +1718,7 @@ static int __devinit snd_ensoniq_1370_mixer(struct ensoniq * ensoniq)
#ifdef SUPPORT_JOYSTICK
#ifdef CHIP1371
-static int __devinit snd_ensoniq_get_joystick_port(int dev)
+static int snd_ensoniq_get_joystick_port(struct ensoniq *ensoniq, int dev)
{
switch (joystick_port[dev]) {
case 0: /* disabled */
@@ -1779,23 +1730,24 @@ static int __devinit snd_ensoniq_get_joystick_port(int dev)
return joystick_port[dev];
default:
- printk(KERN_ERR "ens1371: invalid joystick port %#x", joystick_port[dev]);
+ dev_err(ensoniq->card->dev,
+ "invalid joystick port %#x", joystick_port[dev]);
return 0;
}
}
#else
-static inline int snd_ensoniq_get_joystick_port(int dev)
+static int snd_ensoniq_get_joystick_port(struct ensoniq *ensoniq, int dev)
{
return joystick[dev] ? 0x200 : 0;
}
#endif
-static int __devinit snd_ensoniq_create_gameport(struct ensoniq *ensoniq, int dev)
+static int snd_ensoniq_create_gameport(struct ensoniq *ensoniq, int dev)
{
struct gameport *gp;
int io_port;
- io_port = snd_ensoniq_get_joystick_port(dev);
+ io_port = snd_ensoniq_get_joystick_port(ensoniq, dev);
switch (io_port) {
case 0:
@@ -1806,14 +1758,16 @@ static int __devinit snd_ensoniq_create_gameport(struct ensoniq *ensoniq, int de
if (request_region(io_port, 8, "ens137x: gameport"))
break;
if (io_port > 0x218) {
- printk(KERN_WARNING "ens137x: no gameport ports available\n");
+ dev_warn(ensoniq->card->dev,
+ "no gameport ports available\n");
return -EBUSY;
}
break;
default:
if (!request_region(io_port, 8, "ens137x: gameport")) {
- printk(KERN_WARNING "ens137x: gameport io port 0x%#x in use\n",
+ dev_warn(ensoniq->card->dev,
+ "gameport io port %#x in use\n",
io_port);
return -EBUSY;
}
@@ -1822,7 +1776,8 @@ static int __devinit snd_ensoniq_create_gameport(struct ensoniq *ensoniq, int de
ensoniq->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "ens137x: cannot allocate memory for gameport\n");
+ dev_err(ensoniq->card->dev,
+ "cannot allocate memory for gameport\n");
release_region(io_port, 8);
return -ENOMEM;
}
@@ -1870,41 +1825,35 @@ static void snd_ensoniq_proc_read(struct snd_info_entry *entry,
{
struct ensoniq *ensoniq = entry->private_data;
-#ifdef CHIP1370
- snd_iprintf(buffer, "Ensoniq AudioPCI ES1370\n\n");
-#else
- snd_iprintf(buffer, "Ensoniq AudioPCI ES1371\n\n");
-#endif
+ snd_iprintf(buffer, "Ensoniq AudioPCI " CHIP_NAME "\n\n");
snd_iprintf(buffer, "Joystick enable : %s\n",
- ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off");
+ str_on_off(ensoniq->ctrl & ES_JYSTK_EN));
#ifdef CHIP1370
snd_iprintf(buffer, "MIC +5V bias : %s\n",
- ensoniq->ctrl & ES_1370_XCTL1 ? "on" : "off");
+ str_on_off(ensoniq->ctrl & ES_1370_XCTL1));
snd_iprintf(buffer, "Line In to AOUT : %s\n",
- ensoniq->ctrl & ES_1370_XCTL0 ? "on" : "off");
+ str_on_off(ensoniq->ctrl & ES_1370_XCTL0));
#else
snd_iprintf(buffer, "Joystick port : 0x%x\n",
(ES_1371_JOY_ASELI(ensoniq->ctrl) * 8) + 0x200);
#endif
}
-static void __devinit snd_ensoniq_proc_init(struct ensoniq * ensoniq)
+static void snd_ensoniq_proc_init(struct ensoniq *ensoniq)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(ensoniq->card, "audiopci", &entry))
- snd_info_set_text_ops(entry, ensoniq, snd_ensoniq_proc_read);
+ snd_card_ro_proc_new(ensoniq->card, "audiopci", ensoniq,
+ snd_ensoniq_proc_read);
}
/*
*/
-static int snd_ensoniq_free(struct ensoniq *ensoniq)
+static void snd_ensoniq_free(struct snd_card *card)
{
+ struct ensoniq *ensoniq = card->private_data;
+
snd_ensoniq_free_gameport(ensoniq);
- if (ensoniq->irq < 0)
- goto __hw_end;
#ifdef CHIP1370
outl(ES_1370_SERR_DISABLE, ES_REG(ensoniq, CONTROL)); /* switch everything off */
outl(0, ES_REG(ensoniq, SERIAL)); /* clear serial interface */
@@ -1912,30 +1861,10 @@ static int snd_ensoniq_free(struct ensoniq *ensoniq)
outl(0, ES_REG(ensoniq, CONTROL)); /* switch everything off */
outl(0, ES_REG(ensoniq, SERIAL)); /* clear serial interface */
#endif
- if (ensoniq->irq >= 0)
- synchronize_irq(ensoniq->irq);
- pci_set_power_state(ensoniq->pci, 3);
- __hw_end:
-#ifdef CHIP1370
- if (ensoniq->dma_bug.area)
- snd_dma_free_pages(&ensoniq->dma_bug);
-#endif
- if (ensoniq->irq >= 0)
- free_irq(ensoniq->irq, ensoniq);
- pci_release_regions(ensoniq->pci);
- pci_disable_device(ensoniq->pci);
- kfree(ensoniq);
- return 0;
-}
-
-static int snd_ensoniq_dev_free(struct snd_device *device)
-{
- struct ensoniq *ensoniq = device->device_data;
- return snd_ensoniq_free(ensoniq);
}
#ifdef CHIP1371
-static struct snd_pci_quirk es1371_amplifier_hack[] __devinitdata = {
+static const struct snd_pci_quirk es1371_amplifier_hack[] = {
SND_PCI_QUIRK_ID(0x107b, 0x2150), /* Gateway Solo 2150 */
SND_PCI_QUIRK_ID(0x13bd, 0x100c), /* EV1938 on Mebius PC-MJ100V */
SND_PCI_QUIRK_ID(0x1102, 0x5938), /* Targa Xtender300 */
@@ -1943,7 +1872,7 @@ static struct snd_pci_quirk es1371_amplifier_hack[] __devinitdata = {
{ } /* end */
};
-static struct es1371_quirk es1371_ac97_reset_hack[] = {
+static const struct es1371_quirk es1371_ac97_reset_hack[] = {
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_C },
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_D },
{ .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_E },
@@ -1965,7 +1894,7 @@ static void snd_ensoniq_chip_init(struct ensoniq *ensoniq)
outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
- outl(ensoniq->dma_bug.addr, ES_REG(ensoniq, PHANTOM_FRAME));
+ outl(ensoniq->dma_bug->addr, ES_REG(ensoniq, PHANTOM_FRAME));
outl(0, ES_REG(ensoniq, PHANTOM_COUNT));
#else
outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
@@ -2014,20 +1943,15 @@ static void snd_ensoniq_chip_init(struct ensoniq *ensoniq)
outb(ensoniq->uartc = 0x00, ES_REG(ensoniq, UART_CONTROL));
outb(0x00, ES_REG(ensoniq, UART_RES));
outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
- synchronize_irq(ensoniq->irq);
}
-#ifdef CONFIG_PM
-static int snd_ensoniq_suspend(struct pci_dev *pci, pm_message_t state)
+static int snd_ensoniq_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct ensoniq *ensoniq = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(ensoniq->pcm1);
- snd_pcm_suspend_all(ensoniq->pcm2);
-
#ifdef CHIP1371
snd_ac97_suspend(ensoniq->u.es1371.ac97);
#else
@@ -2040,28 +1964,14 @@ static int snd_ensoniq_suspend(struct pci_dev *pci, pm_message_t state)
udelay(100);
snd_ak4531_suspend(ensoniq->u.es1370.ak4531);
#endif
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_ensoniq_resume(struct pci_dev *pci)
+static int snd_ensoniq_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct ensoniq *ensoniq = card->private_data;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR DRIVER_NAME ": pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
snd_ensoniq_chip_init(ensoniq);
#ifdef CHIP1371
@@ -2072,52 +1982,39 @@ static int snd_ensoniq_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_ensoniq_pm, snd_ensoniq_suspend, snd_ensoniq_resume);
-static int __devinit snd_ensoniq_create(struct snd_card *card,
- struct pci_dev *pci,
- struct ensoniq ** rensoniq)
+static int snd_ensoniq_create(struct snd_card *card,
+ struct pci_dev *pci)
{
- struct ensoniq *ensoniq;
+ struct ensoniq *ensoniq = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_ensoniq_dev_free,
- };
- *rensoniq = NULL;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- ensoniq = kzalloc(sizeof(*ensoniq), GFP_KERNEL);
- if (ensoniq == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
spin_lock_init(&ensoniq->reg_lock);
mutex_init(&ensoniq->src_mutex);
ensoniq->card = card;
ensoniq->pci = pci;
ensoniq->irq = -1;
- if ((err = pci_request_regions(pci, "Ensoniq AudioPCI")) < 0) {
- kfree(ensoniq);
- pci_disable_device(pci);
+ err = pcim_request_all_regions(pci, "Ensoniq AudioPCI");
+ if (err < 0)
return err;
- }
ensoniq->port = pci_resource_start(pci, 0);
- if (request_irq(pci->irq, snd_audiopci_interrupt, IRQF_SHARED,
- "Ensoniq AudioPCI", ensoniq)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_ensoniq_free(ensoniq);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_audiopci_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, ensoniq)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
ensoniq->irq = pci->irq;
+ card->sync_irq = ensoniq->irq;
#ifdef CHIP1370
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- 16, &ensoniq->dma_bug) < 0) {
- snd_printk(KERN_ERR "unable to allocate space for phantom area - dma_bug\n");
- snd_ensoniq_free(ensoniq);
- return -EBUSY;
- }
+ ensoniq->dma_bug =
+ snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV, 16);
+ if (!ensoniq->dma_bug)
+ return -ENOMEM;
#endif
pci_set_master(pci);
ensoniq->rev = pci->revision;
@@ -2140,18 +2037,10 @@ static int __devinit snd_ensoniq_create(struct snd_card *card,
ensoniq->cssr |= ES_1371_ST_AC97_RST;
#endif
+ card->private_free = snd_ensoniq_free;
snd_ensoniq_chip_init(ensoniq);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ensoniq, &ops)) < 0) {
- snd_ensoniq_free(ensoniq);
- return err;
- }
-
snd_ensoniq_proc_init(ensoniq);
-
- snd_card_set_dev(card, &pci->dev);
-
- *rensoniq = ensoniq;
return 0;
}
@@ -2167,19 +2056,19 @@ static void snd_ensoniq_midi_interrupt(struct ensoniq * ensoniq)
if (rmidi == NULL)
return;
/* do Rx at first */
- spin_lock(&ensoniq->reg_lock);
- mask = ensoniq->uartm & ES_MODE_INPUT ? ES_RXRDY : 0;
- while (mask) {
- status = inb(ES_REG(ensoniq, UART_STATUS));
- if ((status & mask) == 0)
- break;
- byte = inb(ES_REG(ensoniq, UART_DATA));
- snd_rawmidi_receive(ensoniq->midi_input, &byte, 1);
+ scoped_guard(spinlock, &ensoniq->reg_lock) {
+ mask = ensoniq->uartm & ES_MODE_INPUT ? ES_RXRDY : 0;
+ while (mask) {
+ status = inb(ES_REG(ensoniq, UART_STATUS));
+ if ((status & mask) == 0)
+ break;
+ byte = inb(ES_REG(ensoniq, UART_DATA));
+ snd_rawmidi_receive(ensoniq->midi_input, &byte, 1);
+ }
}
- spin_unlock(&ensoniq->reg_lock);
/* do Tx at second */
- spin_lock(&ensoniq->reg_lock);
+ guard(spinlock)(&ensoniq->reg_lock);
mask = ensoniq->uartm & ES_MODE_OUTPUT ? ES_TXRDY : 0;
while (mask) {
status = inb(ES_REG(ensoniq, UART_STATUS));
@@ -2193,14 +2082,13 @@ static void snd_ensoniq_midi_interrupt(struct ensoniq * ensoniq)
outb(byte, ES_REG(ensoniq, UART_DATA));
}
}
- spin_unlock(&ensoniq->reg_lock);
}
static int snd_ensoniq_midi_input_open(struct snd_rawmidi_substream *substream)
{
struct ensoniq *ensoniq = substream->rmidi->private_data;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
ensoniq->uartm |= ES_MODE_INPUT;
ensoniq->midi_input = substream;
if (!(ensoniq->uartm & ES_MODE_OUTPUT)) {
@@ -2208,7 +2096,6 @@ static int snd_ensoniq_midi_input_open(struct snd_rawmidi_substream *substream)
outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL));
}
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -2216,7 +2103,7 @@ static int snd_ensoniq_midi_input_close(struct snd_rawmidi_substream *substream)
{
struct ensoniq *ensoniq = substream->rmidi->private_data;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
if (!(ensoniq->uartm & ES_MODE_OUTPUT)) {
outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL));
@@ -2225,7 +2112,6 @@ static int snd_ensoniq_midi_input_close(struct snd_rawmidi_substream *substream)
}
ensoniq->midi_input = NULL;
ensoniq->uartm &= ~ES_MODE_INPUT;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -2233,7 +2119,7 @@ static int snd_ensoniq_midi_output_open(struct snd_rawmidi_substream *substream)
{
struct ensoniq *ensoniq = substream->rmidi->private_data;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
ensoniq->uartm |= ES_MODE_OUTPUT;
ensoniq->midi_output = substream;
if (!(ensoniq->uartm & ES_MODE_INPUT)) {
@@ -2241,7 +2127,6 @@ static int snd_ensoniq_midi_output_open(struct snd_rawmidi_substream *substream)
outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL));
}
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
@@ -2249,7 +2134,7 @@ static int snd_ensoniq_midi_output_close(struct snd_rawmidi_substream *substream
{
struct ensoniq *ensoniq = substream->rmidi->private_data;
- spin_lock_irq(&ensoniq->reg_lock);
+ guard(spinlock_irq)(&ensoniq->reg_lock);
if (!(ensoniq->uartm & ES_MODE_INPUT)) {
outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL));
@@ -2258,17 +2143,15 @@ static int snd_ensoniq_midi_output_close(struct snd_rawmidi_substream *substream
}
ensoniq->midi_output = NULL;
ensoniq->uartm &= ~ES_MODE_OUTPUT;
- spin_unlock_irq(&ensoniq->reg_lock);
return 0;
}
static void snd_ensoniq_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct ensoniq *ensoniq = substream->rmidi->private_data;
int idx;
- spin_lock_irqsave(&ensoniq->reg_lock, flags);
+ guard(spinlock_irqsave)(&ensoniq->reg_lock);
if (up) {
if ((ensoniq->uartc & ES_RXINTEN) == 0) {
/* empty input FIFO */
@@ -2283,16 +2166,14 @@ static void snd_ensoniq_midi_input_trigger(struct snd_rawmidi_substream *substre
outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
}
}
- spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
}
static void snd_ensoniq_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
- unsigned long flags;
struct ensoniq *ensoniq = substream->rmidi->private_data;
unsigned char byte;
- spin_lock_irqsave(&ensoniq->reg_lock, flags);
+ guard(spinlock_irqsave)(&ensoniq->reg_lock);
if (up) {
if (ES_TXINTENI(ensoniq->uartc) == 0) {
ensoniq->uartc |= ES_TXINTENO(1);
@@ -2313,46 +2194,37 @@ static void snd_ensoniq_midi_output_trigger(struct snd_rawmidi_substream *substr
outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
}
}
- spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
}
-static struct snd_rawmidi_ops snd_ensoniq_midi_output =
+static const struct snd_rawmidi_ops snd_ensoniq_midi_output =
{
.open = snd_ensoniq_midi_output_open,
.close = snd_ensoniq_midi_output_close,
.trigger = snd_ensoniq_midi_output_trigger,
};
-static struct snd_rawmidi_ops snd_ensoniq_midi_input =
+static const struct snd_rawmidi_ops snd_ensoniq_midi_input =
{
.open = snd_ensoniq_midi_input_open,
.close = snd_ensoniq_midi_input_close,
.trigger = snd_ensoniq_midi_input_trigger,
};
-static int __devinit snd_ensoniq_midi(struct ensoniq * ensoniq, int device,
- struct snd_rawmidi **rrawmidi)
+static int snd_ensoniq_midi(struct ensoniq *ensoniq, int device)
{
struct snd_rawmidi *rmidi;
int err;
- if (rrawmidi)
- *rrawmidi = NULL;
- if ((err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi)) < 0)
+ err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi);
+ if (err < 0)
return err;
-#ifdef CHIP1370
- strcpy(rmidi->name, "ES1370");
-#else
- strcpy(rmidi->name, "ES1371");
-#endif
+ strscpy(rmidi->name, CHIP_NAME);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_ensoniq_midi_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_ensoniq_midi_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_DUPLEX;
rmidi->private_data = ensoniq;
ensoniq->rmidi = rmidi;
- if (rrawmidi)
- *rrawmidi = rmidi;
return 0;
}
@@ -2372,17 +2244,17 @@ static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id)
if (!(status & ES_INTR))
return IRQ_NONE;
- spin_lock(&ensoniq->reg_lock);
- sctrl = ensoniq->sctrl;
- if (status & ES_DAC1)
- sctrl &= ~ES_P1_INT_EN;
- if (status & ES_DAC2)
- sctrl &= ~ES_P2_INT_EN;
- if (status & ES_ADC)
- sctrl &= ~ES_R1_INT_EN;
- outl(sctrl, ES_REG(ensoniq, SERIAL));
- outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
- spin_unlock(&ensoniq->reg_lock);
+ scoped_guard(spinlock, &ensoniq->reg_lock) {
+ sctrl = ensoniq->sctrl;
+ if (status & ES_DAC1)
+ sctrl &= ~ES_P1_INT_EN;
+ if (status & ES_DAC2)
+ sctrl &= ~ES_P2_INT_EN;
+ if (status & ES_ADC)
+ sctrl &= ~ES_R1_INT_EN;
+ outl(sctrl, ES_REG(ensoniq, SERIAL));
+ outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
+ }
if (status & ES_UART)
snd_ensoniq_midi_interrupt(ensoniq);
@@ -2395,13 +2267,13 @@ static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int __devinit snd_audiopci_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_audiopci_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
struct ensoniq *ensoniq;
- int err, pcm_devs[2];
+ int err;
if (dev >= SNDRV_CARDS)
return -ENODEV;
@@ -2410,89 +2282,69 @@ static int __devinit snd_audiopci_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*ensoniq), &card);
if (err < 0)
return err;
+ ensoniq = card->private_data;
- if ((err = snd_ensoniq_create(card, pci, &ensoniq)) < 0) {
- snd_card_free(card);
+ err = snd_ensoniq_create(card, pci);
+ if (err < 0)
return err;
- }
- card->private_data = ensoniq;
- pcm_devs[0] = 0; pcm_devs[1] = 1;
#ifdef CHIP1370
- if ((err = snd_ensoniq_1370_mixer(ensoniq)) < 0) {
- snd_card_free(card);
+ err = snd_ensoniq_1370_mixer(ensoniq);
+ if (err < 0)
return err;
- }
#endif
#ifdef CHIP1371
- if ((err = snd_ensoniq_1371_mixer(ensoniq, spdif[dev], lineio[dev])) < 0) {
- snd_card_free(card);
+ err = snd_ensoniq_1371_mixer(ensoniq, spdif[dev], lineio[dev]);
+ if (err < 0)
return err;
- }
#endif
- if ((err = snd_ensoniq_pcm(ensoniq, 0, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_ensoniq_pcm(ensoniq, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_ensoniq_pcm2(ensoniq, 1, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_ensoniq_pcm2(ensoniq, 1);
+ if (err < 0)
return err;
- }
- if ((err = snd_ensoniq_midi(ensoniq, 0, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_ensoniq_midi(ensoniq, 0);
+ if (err < 0)
return err;
- }
snd_ensoniq_create_gameport(ensoniq, dev);
- strcpy(card->driver, DRIVER_NAME);
+ strscpy(card->driver, DRIVER_NAME);
- strcpy(card->shortname, "Ensoniq AudioPCI");
+ strscpy(card->shortname, "Ensoniq AudioPCI");
sprintf(card->longname, "%s %s at 0x%lx, irq %i",
card->shortname,
card->driver,
ensoniq->port,
ensoniq->irq);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_audiopci_remove(struct pci_dev *pci)
+static int snd_audiopci_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_audiopci_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = DRIVER_NAME,
+static struct pci_driver ens137x_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_audiopci_ids,
.probe = snd_audiopci_probe,
- .remove = __devexit_p(snd_audiopci_remove),
-#ifdef CONFIG_PM
- .suspend = snd_ensoniq_suspend,
- .resume = snd_ensoniq_resume,
-#endif
+ .driver = {
+ .pm = &snd_ensoniq_pm,
+ },
};
-static int __init alsa_card_ens137x_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ens137x_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ens137x_init)
-module_exit(alsa_card_ens137x_exit)
+module_pci_driver(ens137x_driver);
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index 553b75217259..280125eff362 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for ESS Solo-1 (ES1938, ES1946, ES1969) soundcard
* Copyright (c) by Jaromir Koutek <miri@punknet.cz>,
@@ -10,22 +11,6 @@
*
* TODO:
* Rewrite better spinlocks
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -52,9 +37,10 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/gameport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -63,23 +49,17 @@
#include <sound/initval.h>
#include <sound/tlv.h>
-#include <asm/io.h>
-
MODULE_AUTHOR("Jaromir Koutek <miri@punknet.cz>");
MODULE_DESCRIPTION("ESS Solo-1");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ESS,ES1938},"
- "{ESS,ES1946},"
- "{ESS,ES1969},"
- "{TerraTec,128i PCI}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for ESS Solo-1 soundcard.");
@@ -236,14 +216,12 @@ struct es1938 {
#ifdef SUPPORT_JOYSTICK
struct gameport *gameport;
#endif
-#ifdef CONFIG_PM
unsigned char saved_regs[SAVED_REG_SIZE];
-#endif
};
static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id);
-static DEFINE_PCI_DEVICE_TABLE(snd_es1938_ids) = {
+static const struct pci_device_id snd_es1938_ids[] = {
{ PCI_VDEVICE(ESS, 0x1969), 0, }, /* Solo-1 */
{ 0, }
};
@@ -254,20 +232,15 @@ MODULE_DEVICE_TABLE(pci, snd_es1938_ids);
#define WRITE_LOOP_TIMEOUT 0x10000
#define GET_LOOP_TIMEOUT 0x01000
-#undef REG_DEBUG
/* -----------------------------------------------------------------
* Write to a mixer register
* -----------------------------------------------------------------*/
static void snd_es1938_mixer_write(struct es1938 *chip, unsigned char reg, unsigned char val)
{
- unsigned long flags;
- spin_lock_irqsave(&chip->mixer_lock, flags);
+ guard(spinlock_irqsave)(&chip->mixer_lock);
outb(reg, SLSB_REG(chip, MIXERADDR));
outb(val, SLSB_REG(chip, MIXERDATA));
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
-#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Mixer reg %02x set to %02x\n", reg, val);
-#endif
+ dev_dbg(chip->card->dev, "Mixer reg %02x set to %02x\n", reg, val);
}
/* -----------------------------------------------------------------
@@ -276,14 +249,11 @@ static void snd_es1938_mixer_write(struct es1938 *chip, unsigned char reg, unsig
static int snd_es1938_mixer_read(struct es1938 *chip, unsigned char reg)
{
int data;
- unsigned long flags;
- spin_lock_irqsave(&chip->mixer_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->mixer_lock);
outb(reg, SLSB_REG(chip, MIXERADDR));
data = inb(SLSB_REG(chip, MIXERDATA));
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
-#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Mixer reg %02x now is %02x\n", reg, data);
-#endif
+ dev_dbg(chip->card->dev, "Mixer reg %02x now is %02x\n", reg, data);
return data;
}
@@ -293,21 +263,19 @@ static int snd_es1938_mixer_read(struct es1938 *chip, unsigned char reg)
static int snd_es1938_mixer_bits(struct es1938 *chip, unsigned char reg,
unsigned char mask, unsigned char val)
{
- unsigned long flags;
unsigned char old, new, oval;
- spin_lock_irqsave(&chip->mixer_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->mixer_lock);
outb(reg, SLSB_REG(chip, MIXERADDR));
old = inb(SLSB_REG(chip, MIXERDATA));
oval = old & mask;
if (val != oval) {
new = (old & ~mask) | (val & mask);
outb(new, SLSB_REG(chip, MIXERDATA));
-#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Mixer reg %02x was %02x, set to %02x\n",
+ dev_dbg(chip->card->dev,
+ "Mixer reg %02x was %02x, set to %02x\n",
reg, old, new);
-#endif
}
- spin_unlock_irqrestore(&chip->mixer_lock, flags);
return oval;
}
@@ -319,12 +287,14 @@ static void snd_es1938_write_cmd(struct es1938 *chip, unsigned char cmd)
int i;
unsigned char v;
for (i = 0; i < WRITE_LOOP_TIMEOUT; i++) {
- if (!(v = inb(SLSB_REG(chip, READSTATUS)) & 0x80)) {
+ v = inb(SLSB_REG(chip, READSTATUS));
+ if (!(v & 0x80)) {
outb(cmd, SLSB_REG(chip, WRITEDATA));
return;
}
}
- printk(KERN_ERR "snd_es1938_write_cmd timeout (0x02%x/0x02%x)\n", cmd, v);
+ dev_err(chip->card->dev,
+ "snd_es1938_write_cmd timeout (0x02%x/0x02%x)\n", cmd, v);
}
/* -----------------------------------------------------------------
@@ -334,10 +304,12 @@ static int snd_es1938_get_byte(struct es1938 *chip)
{
int i;
unsigned char v;
- for (i = GET_LOOP_TIMEOUT; i; i--)
- if ((v = inb(SLSB_REG(chip, STATUS))) & 0x80)
+ for (i = GET_LOOP_TIMEOUT; i; i--) {
+ v = inb(SLSB_REG(chip, STATUS));
+ if (v & 0x80)
return inb(SLSB_REG(chip, READDATA));
- snd_printk(KERN_ERR "get_byte timeout: status 0x02%x\n", v);
+ }
+ dev_err(chip->card->dev, "get_byte timeout: status 0x02%x\n", v);
return -ENODEV;
}
@@ -346,14 +318,10 @@ static int snd_es1938_get_byte(struct es1938 *chip)
* -----------------------------------------------------------------*/
static void snd_es1938_write(struct es1938 *chip, unsigned char reg, unsigned char val)
{
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_es1938_write_cmd(chip, reg);
snd_es1938_write_cmd(chip, val);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Reg %02x set to %02x\n", reg, val);
-#endif
+ dev_dbg(chip->card->dev, "Reg %02x set to %02x\n", reg, val);
}
/* -----------------------------------------------------------------
@@ -362,15 +330,12 @@ static void snd_es1938_write(struct es1938 *chip, unsigned char reg, unsigned ch
static unsigned char snd_es1938_read(struct es1938 *chip, unsigned char reg)
{
unsigned char val;
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_es1938_write_cmd(chip, ESS_CMD_READREG);
snd_es1938_write_cmd(chip, reg);
val = snd_es1938_get_byte(chip);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Reg %02x now is %02x\n", reg, val);
-#endif
+ dev_dbg(chip->card->dev, "Reg %02x now is %02x\n", reg, val);
return val;
}
@@ -380,9 +345,9 @@ static unsigned char snd_es1938_read(struct es1938 *chip, unsigned char reg)
static int snd_es1938_bits(struct es1938 *chip, unsigned char reg, unsigned char mask,
unsigned char val)
{
- unsigned long flags;
unsigned char old, new, oval;
- spin_lock_irqsave(&chip->reg_lock, flags);
+
+ guard(spinlock_irqsave)(&chip->reg_lock);
snd_es1938_write_cmd(chip, ESS_CMD_READREG);
snd_es1938_write_cmd(chip, reg);
old = snd_es1938_get_byte(chip);
@@ -391,12 +356,9 @@ static int snd_es1938_bits(struct es1938 *chip, unsigned char reg, unsigned char
snd_es1938_write_cmd(chip, reg);
new = (old & ~mask) | (val & mask);
snd_es1938_write_cmd(chip, new);
-#ifdef REG_DEBUG
- snd_printk(KERN_DEBUG "Reg %02x was %02x, set to %02x\n",
+ dev_dbg(chip->card->dev, "Reg %02x was %02x, set to %02x\n",
reg, old, new);
-#endif
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
return oval;
}
@@ -416,7 +378,7 @@ static void snd_es1938_reset(struct es1938 *chip)
goto __next;
}
}
- snd_printk(KERN_ERR "ESS Solo-1 reset failed\n");
+ dev_err(chip->card->dev, "ESS Solo-1 reset failed\n");
__next:
snd_es1938_write_cmd(chip, ESS_CMD_ENABLEEXT);
@@ -448,7 +410,7 @@ static void snd_es1938_reset_fifo(struct es1938 *chip)
outb(0, SLSB_REG(chip, RESET));
}
-static struct snd_ratnum clocks[2] = {
+static const struct snd_ratnum clocks[2] = {
{
.num = 793800,
.den_min = 1,
@@ -463,7 +425,7 @@ static struct snd_ratnum clocks[2] = {
}
};
-static struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
+static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
.nrats = 2,
.rats = clocks,
};
@@ -851,52 +813,30 @@ static snd_pcm_uframes_t snd_es1938_playback_pointer(struct snd_pcm_substream *s
}
static int snd_es1938_capture_copy(struct snd_pcm_substream *substream,
- int channel,
- snd_pcm_uframes_t pos,
- void __user *dst,
- snd_pcm_uframes_t count)
+ int channel, unsigned long pos,
+ struct iov_iter *dst, unsigned long count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct es1938 *chip = snd_pcm_substream_chip(substream);
- pos <<= chip->dma1_shift;
- count <<= chip->dma1_shift;
+
if (snd_BUG_ON(pos + count > chip->dma1_size))
return -EINVAL;
if (pos + count < chip->dma1_size) {
- if (copy_to_user(dst, runtime->dma_area + pos + 1, count))
+ if (copy_to_iter(runtime->dma_area + pos + 1, count, dst) != count)
return -EFAULT;
} else {
- if (copy_to_user(dst, runtime->dma_area + pos + 1, count - 1))
+ if (copy_to_iter(runtime->dma_area + pos + 1, count - 1, dst) != count - 1)
return -EFAULT;
- if (put_user(runtime->dma_area[0], ((unsigned char __user *)dst) + count - 1))
+ if (copy_to_iter(runtime->dma_area, 1, dst) != 1)
return -EFAULT;
}
return 0;
}
-/*
- * buffer management
- */
-static int snd_es1938_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-
-{
- int err;
-
- if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
- return err;
- return 0;
-}
-
-static int snd_es1938_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
/* ----------------------------------------------------------------------
* Audio1 Capture (ADC)
* ----------------------------------------------------------------------*/
-static struct snd_pcm_hardware snd_es1938_capture =
+static const struct snd_pcm_hardware snd_es1938_capture =
{
.info = (SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER),
@@ -918,7 +858,7 @@ static struct snd_pcm_hardware snd_es1938_capture =
/* -----------------------------------------------------------------------
* Audio2 Playback (DAC)
* -----------------------------------------------------------------------*/
-static struct snd_pcm_hardware snd_es1938_playback =
+static const struct snd_pcm_hardware snd_es1938_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1004,45 +944,40 @@ static int snd_es1938_playback_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_es1938_playback_ops = {
+static const struct snd_pcm_ops snd_es1938_playback_ops = {
.open = snd_es1938_playback_open,
.close = snd_es1938_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_es1938_pcm_hw_params,
- .hw_free = snd_es1938_pcm_hw_free,
.prepare = snd_es1938_playback_prepare,
.trigger = snd_es1938_playback_trigger,
.pointer = snd_es1938_playback_pointer,
};
-static struct snd_pcm_ops snd_es1938_capture_ops = {
+static const struct snd_pcm_ops snd_es1938_capture_ops = {
.open = snd_es1938_capture_open,
.close = snd_es1938_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_es1938_pcm_hw_params,
- .hw_free = snd_es1938_pcm_hw_free,
.prepare = snd_es1938_capture_prepare,
.trigger = snd_es1938_capture_trigger,
.pointer = snd_es1938_capture_pointer,
.copy = snd_es1938_capture_copy,
};
-static int __devinit snd_es1938_new_pcm(struct es1938 *chip, int device)
+static int snd_es1938_new_pcm(struct es1938 *chip, int device)
{
struct snd_pcm *pcm;
int err;
- if ((err = snd_pcm_new(chip->card, "es-1938-1946", device, 2, 1, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "es-1938-1946", device, 2, 1, &pcm);
+ if (err < 0)
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1938_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es1938_capture_ops);
pcm->private_data = chip;
pcm->info_flags = 0;
- strcpy(pcm->name, "ESS Solo-1");
+ strscpy(pcm->name, "ESS Solo-1");
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci), 64*1024, 64*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev, 64*1024, 64*1024);
chip->pcm = pcm;
return 0;
@@ -1056,18 +991,12 @@ static int __devinit snd_es1938_new_pcm(struct es1938 *chip, int device)
static int snd_es1938_info_mux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[8] = {
+ static const char * const texts[8] = {
"Mic", "Mic Master", "CD", "AOUT",
"Mic1", "Mix", "Line", "Master"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 8;
- if (uinfo->value.enumerated.item > 7)
- uinfo->value.enumerated.item = 7;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 8, texts);
}
static int snd_es1938_get_mux(struct snd_kcontrol *kcontrol,
@@ -1321,39 +1250,34 @@ static int snd_es1938_put_double(struct snd_kcontrol *kcontrol,
return change;
}
-static unsigned int db_scale_master[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(db_scale_master,
0, 54, TLV_DB_SCALE_ITEM(-3600, 50, 1),
54, 63, TLV_DB_SCALE_ITEM(-900, 100, 0),
-};
+);
-static unsigned int db_scale_audio1[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(db_scale_audio1,
0, 8, TLV_DB_SCALE_ITEM(-3300, 300, 1),
8, 15, TLV_DB_SCALE_ITEM(-900, 150, 0),
-};
+);
-static unsigned int db_scale_audio2[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(db_scale_audio2,
0, 8, TLV_DB_SCALE_ITEM(-3450, 300, 1),
8, 15, TLV_DB_SCALE_ITEM(-1050, 150, 0),
-};
+);
-static unsigned int db_scale_mic[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(db_scale_mic,
0, 8, TLV_DB_SCALE_ITEM(-2400, 300, 1),
8, 15, TLV_DB_SCALE_ITEM(0, 150, 0),
-};
+);
-static unsigned int db_scale_line[] = {
- TLV_DB_RANGE_HEAD(2),
+static const DECLARE_TLV_DB_RANGE(db_scale_line,
0, 8, TLV_DB_SCALE_ITEM(-3150, 300, 1),
8, 15, TLV_DB_SCALE_ITEM(-750, 150, 0),
-};
+);
static const DECLARE_TLV_DB_SCALE(db_scale_capture, 0, 150, 0);
-static struct snd_kcontrol_new snd_es1938_controls[] = {
+static const struct snd_kcontrol_new snd_es1938_controls[] = {
ES1938_DOUBLE_TLV("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0,
db_scale_master),
ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
@@ -1461,12 +1385,11 @@ static void snd_es1938_chip_init(struct es1938 *chip)
outb(0, SLDM_REG(chip, DMACLEAR));
}
-#ifdef CONFIG_PM
/*
* PM support
*/
-static unsigned char saved_regs[SAVED_REG_SIZE+1] = {
+static const unsigned char saved_regs[SAVED_REG_SIZE+1] = {
0x14, 0x1a, 0x1c, 0x3a, 0x3c, 0x3e, 0x36, 0x38,
0x50, 0x52, 0x60, 0x61, 0x62, 0x63, 0x64, 0x68,
0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x7c, 0x7d,
@@ -1474,14 +1397,14 @@ static unsigned char saved_regs[SAVED_REG_SIZE+1] = {
};
-static int es1938_suspend(struct pci_dev *pci, pm_message_t state)
+static int es1938_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct es1938 *chip = card->private_data;
- unsigned char *s, *d;
+ const unsigned char *s;
+ unsigned char *d;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
/* save mixer-related registers */
for (s = saved_regs, d = chip->saved_regs; *s; s++, d++)
@@ -1491,36 +1414,28 @@ static int es1938_suspend(struct pci_dev *pci, pm_message_t state)
if (chip->irq >= 0) {
free_irq(chip->irq, chip);
chip->irq = -1;
+ card->sync_irq = -1;
}
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int es1938_resume(struct pci_dev *pci)
+static int es1938_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct snd_card *card = dev_get_drvdata(dev);
struct es1938 *chip = card->private_data;
- unsigned char *s, *d;
-
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "es1938: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
+ const unsigned char *s;
+ unsigned char *d;
if (request_irq(pci->irq, snd_es1938_interrupt,
- IRQF_SHARED, "ES1938", chip)) {
- printk(KERN_ERR "es1938: unable to grab IRQ %d, "
- "disabling device\n", pci->irq);
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(dev, "unable to grab IRQ %d, disabling device\n",
+ pci->irq);
snd_card_disconnect(card);
return -EIO;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
snd_es1938_chip_init(chip);
/* restore mixer-related registers */
@@ -1534,16 +1449,18 @@ static int es1938_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
+
+static DEFINE_SIMPLE_DEV_PM_OPS(es1938_pm, es1938_suspend, es1938_resume);
#ifdef SUPPORT_JOYSTICK
-static int __devinit snd_es1938_create_gameport(struct es1938 *chip)
+static int snd_es1938_create_gameport(struct es1938 *chip)
{
struct gameport *gp;
chip->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "es1938: cannot allocate memory for gameport\n");
+ dev_err(chip->card->dev,
+ "cannot allocate memory for gameport\n");
return -ENOMEM;
}
@@ -1569,8 +1486,10 @@ static inline int snd_es1938_create_gameport(struct es1938 *chip) { return -ENOS
static inline void snd_es1938_free_gameport(struct es1938 *chip) { }
#endif /* SUPPORT_JOYSTICK */
-static int snd_es1938_free(struct es1938 *chip)
+static void snd_es1938_free(struct snd_card *card)
{
+ struct es1938 *chip = card->private_data;
+
/* disable irqs */
outb(0x00, SLIO_REG(chip, IRQCONTROL));
if (chip->rmidi)
@@ -1580,85 +1499,54 @@ static int snd_es1938_free(struct es1938 *chip)
if (chip->irq >= 0)
free_irq(chip->irq, chip);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
}
-static int snd_es1938_dev_free(struct snd_device *device)
+static int snd_es1938_create(struct snd_card *card,
+ struct pci_dev *pci)
{
- struct es1938 *chip = device->device_data;
- return snd_es1938_free(chip);
-}
-
-static int __devinit snd_es1938_create(struct snd_card *card,
- struct pci_dev * pci,
- struct es1938 ** rchip)
-{
- struct es1938 *chip;
+ struct es1938 *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_es1938_dev_free,
- };
-
- *rchip = NULL;
/* enable PCI device */
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
/* check, if we can restrict PCI DMA transfers to 24 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(24)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(24)) < 0) {
- snd_printk(KERN_ERR "architecture does not support 24bit PCI busmaster DMA\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(24))) {
+ dev_err(card->dev,
+ "architecture does not support 24bit PCI busmaster DMA\n");
return -ENXIO;
}
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
spin_lock_init(&chip->reg_lock);
spin_lock_init(&chip->mixer_lock);
chip->card = card;
chip->pci = pci;
chip->irq = -1;
- if ((err = pci_request_regions(pci, "ESS Solo-1")) < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pcim_request_all_regions(pci, "ESS Solo-1");
+ if (err < 0)
return err;
- }
chip->io_port = pci_resource_start(pci, 0);
chip->sb_port = pci_resource_start(pci, 1);
chip->vc_port = pci_resource_start(pci, 2);
chip->mpu_port = pci_resource_start(pci, 3);
chip->game_port = pci_resource_start(pci, 4);
+ /* still use non-managed irq handler as it's re-acquired at PM resume */
if (request_irq(pci->irq, snd_es1938_interrupt, IRQF_SHARED,
- "ES1938", chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_es1938_free(chip);
+ KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
-#ifdef ES1938_DDEBUG
- snd_printk(KERN_DEBUG "create: io: 0x%lx, sb: 0x%lx, vc: 0x%lx, mpu: 0x%lx, game: 0x%lx\n",
+ card->sync_irq = chip->irq;
+ card->private_free = snd_es1938_free;
+ dev_dbg(card->dev,
+ "create: io: 0x%lx, sb: 0x%lx, vc: 0x%lx, mpu: 0x%lx, game: 0x%lx\n",
chip->io_port, chip->sb_port, chip->vc_port, chip->mpu_port, chip->game_port);
-#endif
chip->ddma_port = chip->vc_port + 0x00; /* fix from Thomas Sailer */
snd_es1938_chip_init(chip);
-
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_es1938_free(chip);
- return err;
- }
-
- snd_card_set_dev(card, &pci->dev);
-
- *rchip = chip;
return 0;
}
@@ -1668,26 +1556,28 @@ static int __devinit snd_es1938_create(struct snd_card *card,
static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id)
{
struct es1938 *chip = dev_id;
- unsigned char status, audiostatus;
+ unsigned char status;
+ __always_unused unsigned char audiostatus;
int handled = 0;
status = inb(SLIO_REG(chip, IRQCONTROL));
#if 0
- printk(KERN_DEBUG "Es1938debug - interrupt status: =0x%x\n", status);
+ dev_dbg(chip->card->dev,
+ "Es1938debug - interrupt status: =0x%x\n", status);
#endif
/* AUDIO 1 */
if (status & 0x10) {
#if 0
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
"Es1938debug - AUDIO channel 1 interrupt\n");
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
"Es1938debug - AUDIO channel 1 DMAC DMA count: %u\n",
inw(SLDM_REG(chip, DMACOUNT)));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
"Es1938debug - AUDIO channel 1 DMAC DMA base: %u\n",
inl(SLDM_REG(chip, DMAADDR)));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
"Es1938debug - AUDIO channel 1 DMAC DMA status: 0x%x\n",
inl(SLDM_REG(chip, DMASTATUS)));
#endif
@@ -1703,12 +1593,12 @@ static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id)
/* AUDIO 2 */
if (status & 0x20) {
#if 0
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
"Es1938debug - AUDIO channel 2 interrupt\n");
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
"Es1938debug - AUDIO channel 2 DMAC DMA count: %u\n",
inw(SLIO_REG(chip, AUDIO2DMACOUNT)));
- printk(KERN_DEBUG
+ dev_dbg(chip->card->dev,
"Es1938debug - AUDIO channel 2 DMAC DMA base: %u\n",
inl(SLIO_REG(chip, AUDIO2DMAADDR)));
@@ -1752,7 +1642,7 @@ static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id)
#define ES1938_DMA_SIZE 64
-static int __devinit snd_es1938_mixer(struct es1938 *chip)
+static int snd_es1938_mixer(struct es1938 *chip)
{
struct snd_card *card;
unsigned int idx;
@@ -1760,7 +1650,7 @@ static int __devinit snd_es1938_mixer(struct es1938 *chip)
card = chip->card;
- strcpy(card->mixername, "ESS Solo-1");
+ strscpy(card->mixername, "ESS Solo-1");
for (idx = 0; idx < ARRAY_SIZE(snd_es1938_controls); idx++) {
struct snd_kcontrol *kctl;
@@ -1783,15 +1673,16 @@ static int __devinit snd_es1938_mixer(struct es1938 *chip)
kctl->private_free = snd_es1938_hwv_free;
break;
}
- if ((err = snd_ctl_add(card, kctl)) < 0)
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
return err;
}
return 0;
}
-static int __devinit snd_es1938_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_es1938_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -1806,57 +1697,53 @@ static int __devinit snd_es1938_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
- for (idx = 0; idx < 5; idx++) {
+ chip = card->private_data;
+
+ for (idx = 0; idx < 5; idx++)
if (pci_resource_start(pci, idx) == 0 ||
- !(pci_resource_flags(pci, idx) & IORESOURCE_IO)) {
- snd_card_free(card);
- return -ENODEV;
- }
- }
- if ((err = snd_es1938_create(card, pci, &chip)) < 0) {
- snd_card_free(card);
+ !(pci_resource_flags(pci, idx) & IORESOURCE_IO))
+ return -ENODEV;
+
+ err = snd_es1938_create(card, pci);
+ if (err < 0)
return err;
- }
- card->private_data = chip;
- strcpy(card->driver, "ES1938");
- strcpy(card->shortname, "ESS ES1938 (Solo-1)");
+ strscpy(card->driver, "ES1938");
+ strscpy(card->shortname, "ESS ES1938 (Solo-1)");
sprintf(card->longname, "%s rev %i, irq %i",
card->shortname,
chip->revision,
chip->irq);
- if ((err = snd_es1938_new_pcm(chip, 0)) < 0) {
- snd_card_free(card);
+ err = snd_es1938_new_pcm(chip, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_es1938_mixer(chip)) < 0) {
- snd_card_free(card);
+ err = snd_es1938_mixer(chip);
+ if (err < 0)
return err;
- }
if (snd_opl3_create(card,
SLSB_REG(chip, FMLOWADDR),
SLSB_REG(chip, FMHIGHADDR),
OPL3_HW_OPL3, 1, &opl3) < 0) {
- printk(KERN_ERR "es1938: OPL3 not detected at 0x%lx\n",
+ dev_err(card->dev, "OPL3 not detected at 0x%lx\n",
SLSB_REG(chip, FMLOWADDR));
} else {
- if ((err = snd_opl3_timer_new(opl3, 0, 1)) < 0) {
- snd_card_free(card);
+ err = snd_opl3_timer_new(opl3, 0, 1);
+ if (err < 0)
return err;
- }
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
return err;
- }
}
if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- chip->mpu_port, MPU401_INFO_INTEGRATED,
- chip->irq, 0, &chip->rmidi) < 0) {
- printk(KERN_ERR "es1938: unable to initialize MPU-401\n");
+ chip->mpu_port,
+ MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
+ -1, &chip->rmidi) < 0) {
+ dev_err(card->dev, "unable to initialize MPU-401\n");
} else {
// this line is vital for MIDI interrupt handling on ess-solo1
// andreas@flying-snail.de
@@ -1865,42 +1752,28 @@ static int __devinit snd_es1938_probe(struct pci_dev *pci,
snd_es1938_create_gameport(chip);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_es1938_remove(struct pci_dev *pci)
+static int snd_es1938_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_es1938_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "ESS ES1938 (Solo-1)",
+static struct pci_driver es1938_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_es1938_ids,
.probe = snd_es1938_probe,
- .remove = __devexit_p(snd_es1938_remove),
-#ifdef CONFIG_PM
- .suspend = es1938_suspend,
- .resume = es1938_resume,
-#endif
+ .driver = {
+ .pm = &es1938_pm,
+ },
};
-static int __init alsa_card_es1938_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_es1938_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_es1938_init)
-module_exit(alsa_card_es1938_exit)
+module_pci_driver(es1938_driver);
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index 23a58f0d6cb9..51aee2c4d461 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for ESS Maestro 1/2/2E Sound Card (started 21.8.99)
* Copyright (c) by Matze Braun <MatzeBraun@gmx.de>.
@@ -10,21 +11,6 @@
* TODO:
* Perhaps Synth
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- *
* Notes from Zach Brown about the driver code
*
* Hardware Description
@@ -94,7 +80,7 @@
* places.
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -102,7 +88,7 @@
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/gameport.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/input.h>
@@ -112,23 +98,23 @@
#include <sound/ac97_codec.h>
#include <sound/initval.h>
+#ifdef CONFIG_SND_ES1968_RADIO
+#include <media/drv-intf/tea575x.h>
+#endif
+
#define CARD_NAME "ESS Maestro1/2"
#define DRIVER_NAME "ES1968"
MODULE_DESCRIPTION("ESS Maestro");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ESS,Maestro 2e},"
- "{ESS,Maestro 2},"
- "{ESS,Maestro 1},"
- "{TerraTec,DMX}}");
-#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
+#if IS_REACHABLE(CONFIG_GAMEPORT)
#define SUPPORT_JOYSTICK 1
#endif
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 1-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
static int total_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1024 };
static int pcm_substreams_p[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4 };
static int pcm_substreams_c[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1 };
@@ -136,8 +122,9 @@ static int clock[SNDRV_CARDS];
static int use_pm[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
static int enable_mpu[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
#ifdef SUPPORT_JOYSTICK
-static int joystick[SNDRV_CARDS];
+static bool joystick[SNDRV_CARDS];
#endif
+static int radio_nr[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
@@ -161,6 +148,9 @@ MODULE_PARM_DESC(enable_mpu, "Enable MPU401. (0 = off, 1 = on, 2 = auto)");
module_param_array(joystick, bool, NULL, 0444);
MODULE_PARM_DESC(joystick, "Enable joystick.");
#endif
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
+
#define NR_APUS 64
@@ -220,7 +210,7 @@ MODULE_PARM_DESC(joystick, "Enable joystick.");
#define RINGB_EN_2CODEC 0x0020
#define RINGB_SING_BIT_DUAL 0x0040
-/* ****Port Adresses**** */
+/* ****Port Addresses**** */
/* Write & Read */
#define ESM_INDEX 0x02
@@ -483,9 +473,7 @@ struct esschan {
/* linked list */
struct list_head list;
-#ifdef CONFIG_PM
u16 wc_map[4];
-#endif
};
struct es1968 {
@@ -536,9 +524,7 @@ struct es1968 {
struct list_head substream_list;
spinlock_t substream_lock;
-#ifdef CONFIG_PM
u16 apu_map[NR_APUS][NR_APU_REGS];
-#endif
#ifdef SUPPORT_JOYSTICK
struct gameport *gameport;
@@ -550,14 +536,19 @@ struct es1968 {
#else
struct snd_kcontrol *master_switch; /* for h/w volume control */
struct snd_kcontrol *master_volume;
- spinlock_t ac97_lock;
- struct tasklet_struct hwvol_tq;
+#endif
+ struct work_struct hwvol_work;
+
+#ifdef CONFIG_SND_ES1968_RADIO
+ struct v4l2_device v4l2_dev;
+ struct snd_tea575x tea;
+ unsigned int tea575x_tuner;
#endif
};
static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id);
-static DEFINE_PCI_DEVICE_TABLE(snd_es1968_ids) = {
+static const struct pci_device_id snd_es1968_ids[] = {
/* Maestro 1 */
{ 0x1285, 0x0100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO },
/* Maestro 2 */
@@ -583,10 +574,8 @@ static void __maestro_write(struct es1968 *chip, u16 reg, u16 data)
static inline void maestro_write(struct es1968 *chip, u16 reg, u16 data)
{
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
__maestro_write(chip, reg, data);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
/* no spinlock */
@@ -601,12 +590,8 @@ static u16 __maestro_read(struct es1968 *chip, u16 reg)
static inline u16 maestro_read(struct es1968 *chip, u16 reg)
{
- unsigned long flags;
- u16 result;
- spin_lock_irqsave(&chip->reg_lock, flags);
- result = __maestro_read(chip, reg);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return result;
+ guard(spinlock_irqsave)(&chip->reg_lock);
+ return __maestro_read(chip, reg);
}
/* Wait for the codec bus to be free */
@@ -619,7 +604,7 @@ static int snd_es1968_ac97_wait(struct es1968 *chip)
return 0;
cond_resched();
}
- snd_printd("es1968: ac97 timeout\n");
+ dev_dbg(chip->card->dev, "ac97 timeout\n");
return 1; /* timeout */
}
@@ -631,45 +616,30 @@ static int snd_es1968_ac97_wait_poll(struct es1968 *chip)
if (!(inb(chip->io_port + ESM_AC97_INDEX) & 1))
return 0;
}
- snd_printd("es1968: ac97 timeout\n");
+ dev_dbg(chip->card->dev, "ac97 timeout\n");
return 1; /* timeout */
}
static void snd_es1968_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
{
struct es1968 *chip = ac97->private_data;
-#ifndef CONFIG_SND_ES1968_INPUT
- unsigned long flags;
-#endif
snd_es1968_ac97_wait(chip);
/* Write the bus */
-#ifndef CONFIG_SND_ES1968_INPUT
- spin_lock_irqsave(&chip->ac97_lock, flags);
-#endif
outw(val, chip->io_port + ESM_AC97_DATA);
/*msleep(1);*/
outb(reg, chip->io_port + ESM_AC97_INDEX);
/*msleep(1);*/
-#ifndef CONFIG_SND_ES1968_INPUT
- spin_unlock_irqrestore(&chip->ac97_lock, flags);
-#endif
}
static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
u16 data = 0;
struct es1968 *chip = ac97->private_data;
-#ifndef CONFIG_SND_ES1968_INPUT
- unsigned long flags;
-#endif
snd_es1968_ac97_wait(chip);
-#ifndef CONFIG_SND_ES1968_INPUT
- spin_lock_irqsave(&chip->ac97_lock, flags);
-#endif
outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX);
/*msleep(1);*/
@@ -677,9 +647,6 @@ static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short
data = inw(chip->io_port + ESM_AC97_DATA);
/*msleep(1);*/
}
-#ifndef CONFIG_SND_ES1968_INPUT
- spin_unlock_irqrestore(&chip->ac97_lock, flags);
-#endif
return data;
}
@@ -692,7 +659,7 @@ static void apu_index_set(struct es1968 *chip, u16 index)
for (i = 0; i < 1000; i++)
if (__maestro_read(chip, IDR1_CRAM_POINTER) == index)
return;
- snd_printd("es1968: APU register select failed. (Timeout)\n");
+ dev_dbg(chip->card->dev, "APU register select failed. (Timeout)\n");
}
/* no spinlock */
@@ -704,7 +671,7 @@ static void apu_data_set(struct es1968 *chip, u16 data)
return;
__maestro_write(chip, IDR0_DATA_PORT, data);
}
- snd_printd("es1968: APU register set probably failed (Timeout)!\n");
+ dev_dbg(chip->card->dev, "APU register set probably failed (Timeout)!\n");
}
/* no spinlock */
@@ -712,9 +679,7 @@ static void __apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 dat
{
if (snd_BUG_ON(channel >= NR_APUS))
return;
-#ifdef CONFIG_PM
chip->apu_map[channel][reg] = data;
-#endif
reg |= (channel << 4);
apu_index_set(chip, reg);
apu_data_set(chip, data);
@@ -722,10 +687,8 @@ static void __apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 dat
static void apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 data)
{
- unsigned long flags;
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
__apu_set_register(chip, channel, reg, data);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static u16 __apu_get_register(struct es1968 *chip, u16 channel, u8 reg)
@@ -739,62 +702,40 @@ static u16 __apu_get_register(struct es1968 *chip, u16 channel, u8 reg)
static u16 apu_get_register(struct es1968 *chip, u16 channel, u8 reg)
{
- unsigned long flags;
- u16 v;
- spin_lock_irqsave(&chip->reg_lock, flags);
- v = __apu_get_register(chip, channel, reg);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- return v;
+ guard(spinlock_irqsave)(&chip->reg_lock);
+ return __apu_get_register(chip, channel, reg);
}
#if 0 /* ASSP is not supported */
static void assp_set_register(struct es1968 *chip, u32 reg, u32 value)
{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave),(&chip->reg_lock);
outl(reg, chip->io_port + ASSP_INDEX);
outl(value, chip->io_port + ASSP_DATA);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static u32 assp_get_register(struct es1968 *chip, u32 reg)
{
- unsigned long flags;
- u32 value;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
outl(reg, chip->io_port + ASSP_INDEX);
- value = inl(chip->io_port + ASSP_DATA);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
- return value;
+ return inl(chip->io_port + ASSP_DATA);
}
#endif
static void wave_set_register(struct es1968 *chip, u16 reg, u16 value)
{
- unsigned long flags;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
outw(reg, chip->io_port + WC_INDEX);
outw(value, chip->io_port + WC_DATA);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
static u16 wave_get_register(struct es1968 *chip, u16 reg)
{
- unsigned long flags;
- u16 value;
-
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
outw(reg, chip->io_port + WC_INDEX);
- value = inw(chip->io_port + WC_DATA);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
- return value;
+ return inw(chip->io_port + WC_DATA);
}
/* *******************
@@ -953,7 +894,7 @@ static inline void snd_es1968_trigger_apu(struct es1968 *esm, int apu, int mode)
static void snd_es1968_pcm_start(struct es1968 *chip, struct esschan *es)
{
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
__apu_set_register(chip, es->apu[0], 5, es->base[0]);
snd_es1968_trigger_apu(chip, es->apu[0], es->apu_mode[0]);
if (es->mode == ESM_MODE_CAPTURE) {
@@ -968,19 +909,17 @@ static void snd_es1968_pcm_start(struct es1968 *chip, struct esschan *es)
snd_es1968_trigger_apu(chip, es->apu[3], es->apu_mode[3]);
}
}
- spin_unlock(&chip->reg_lock);
}
static void snd_es1968_pcm_stop(struct es1968 *chip, struct esschan *es)
{
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
snd_es1968_trigger_apu(chip, es->apu[0], 0);
snd_es1968_trigger_apu(chip, es->apu[1], 0);
if (es->mode == ESM_MODE_CAPTURE) {
snd_es1968_trigger_apu(chip, es->apu[2], 0);
snd_es1968_trigger_apu(chip, es->apu[3], 0);
}
- spin_unlock(&chip->reg_lock);
}
/* set the wavecache control reg */
@@ -999,9 +938,7 @@ static void snd_es1968_program_wavecache(struct es1968 *chip, struct esschan *es
/* set the wavecache control reg */
wave_set_register(chip, es->apu[channel] << 3, tmpval);
-#ifdef CONFIG_PM
es->wc_map[channel] = tmpval;
-#endif
}
@@ -1012,7 +949,6 @@ static void snd_es1968_playback_setup(struct es1968 *chip, struct esschan *es,
int high_apu = 0;
int channel, apu;
int i, size;
- unsigned long flags;
u32 freq;
size = es->dma_size >> es->wav_shift;
@@ -1082,12 +1018,12 @@ static void snd_es1968_playback_setup(struct es1968 *chip, struct esschan *es,
apu_set_register(chip, apu, 10, 0x8F08);
}
- spin_lock_irqsave(&chip->reg_lock, flags);
- /* clear WP interrupts */
- outw(1, chip->io_port + 0x04);
- /* enable WP ints */
- outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ /* clear WP interrupts */
+ outw(1, chip->io_port + 0x04);
+ /* enable WP ints */
+ outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ);
+ }
freq = runtime->rate;
/* set frequency */
@@ -1158,7 +1094,6 @@ static void snd_es1968_capture_setup(struct es1968 *chip, struct esschan *es,
{
int size;
u32 freq;
- unsigned long flags;
size = es->dma_size >> es->wav_shift;
@@ -1210,12 +1145,11 @@ static void snd_es1968_capture_setup(struct es1968 *chip, struct esschan *es,
snd_es1968_apu_set_freq(chip, es->apu[2], freq);
snd_es1968_apu_set_freq(chip, es->apu[3], freq);
- spin_lock_irqsave(&chip->reg_lock, flags);
+ guard(spinlock_irqsave)(&chip->reg_lock);
/* clear WP interrupts */
outw(1, chip->io_port + 0x04);
/* enable WP ints */
outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
}
/*******************
@@ -1259,7 +1193,7 @@ static int snd_es1968_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
struct es1968 *chip = snd_pcm_substream_chip(substream);
struct esschan *es = substream->runtime->private_data;
- spin_lock(&chip->substream_lock);
+ guard(spinlock)(&chip->substream_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -1280,7 +1214,6 @@ static int snd_es1968_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
snd_es1968_bob_dec(chip);
break;
}
- spin_unlock(&chip->substream_lock);
return 0;
}
@@ -1295,7 +1228,7 @@ static snd_pcm_uframes_t snd_es1968_pcm_pointer(struct snd_pcm_substream *substr
return bytes_to_frames(substream->runtime, ptr % es->dma_size);
}
-static struct snd_pcm_hardware snd_es1968_playback = {
+static const struct snd_pcm_hardware snd_es1968_playback = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -1316,7 +1249,7 @@ static struct snd_pcm_hardware snd_es1968_playback = {
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_es1968_capture = {
+static const struct snd_pcm_hardware snd_es1968_capture = {
.info = (SNDRV_PCM_INFO_NONINTERLEAVED |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -1349,12 +1282,11 @@ static int calc_available_memory_size(struct es1968 *chip)
int max_size = 0;
struct esm_memory *buf;
- mutex_lock(&chip->memory_mutex);
+ guard(mutex)(&chip->memory_mutex);
list_for_each_entry(buf, &chip->buf_list, list) {
if (buf->empty && buf->buf.bytes > max_size)
max_size = buf->buf.bytes;
}
- mutex_unlock(&chip->memory_mutex);
if (max_size >= 128*1024)
max_size = 127*1024;
return max_size;
@@ -1366,21 +1298,18 @@ static struct esm_memory *snd_es1968_new_memory(struct es1968 *chip, int size)
struct esm_memory *buf;
size = ALIGN(size, ESM_MEM_ALIGN);
- mutex_lock(&chip->memory_mutex);
+ guard(mutex)(&chip->memory_mutex);
list_for_each_entry(buf, &chip->buf_list, list) {
if (buf->empty && buf->buf.bytes >= size)
goto __found;
}
- mutex_unlock(&chip->memory_mutex);
return NULL;
__found:
if (buf->buf.bytes > size) {
struct esm_memory *chunk = kmalloc(sizeof(*chunk), GFP_KERNEL);
- if (chunk == NULL) {
- mutex_unlock(&chip->memory_mutex);
+ if (chunk == NULL)
return NULL;
- }
chunk->buf = buf->buf;
chunk->buf.bytes -= size;
chunk->buf.area += size;
@@ -1390,7 +1319,6 @@ __found:
list_add(&chunk->list, &buf->list);
}
buf->empty = 0;
- mutex_unlock(&chip->memory_mutex);
return buf;
}
@@ -1399,7 +1327,7 @@ static void snd_es1968_free_memory(struct es1968 *chip, struct esm_memory *buf)
{
struct esm_memory *chunk;
- mutex_lock(&chip->memory_mutex);
+ guard(mutex)(&chip->memory_mutex);
buf->empty = 1;
if (buf->list.prev != &chip->buf_list) {
chunk = list_entry(buf->list.prev, struct esm_memory, list);
@@ -1418,7 +1346,6 @@ static void snd_es1968_free_memory(struct es1968 *chip, struct esm_memory *buf)
kfree(chunk);
}
}
- mutex_unlock(&chip->memory_mutex);
}
static void snd_es1968_free_dmabuf(struct es1968 *chip)
@@ -1427,7 +1354,7 @@ static void snd_es1968_free_dmabuf(struct es1968 *chip)
if (! chip->dma.area)
return;
- snd_dma_reserve_buf(&chip->dma, snd_dma_pci_buf_id(chip->pci));
+ snd_dma_free_pages(&chip->dma);
while ((p = chip->buf_list.next) != &chip->buf_list) {
struct esm_memory *chunk = list_entry(p, struct esm_memory, list);
list_del(p);
@@ -1435,28 +1362,25 @@ static void snd_es1968_free_dmabuf(struct es1968 *chip)
}
}
-static int __devinit
+static int
snd_es1968_init_dmabuf(struct es1968 *chip)
{
int err;
struct esm_memory *chunk;
- chip->dma.dev.type = SNDRV_DMA_TYPE_DEV;
- chip->dma.dev.dev = snd_dma_pci_data(chip->pci);
- if (! snd_dma_get_reserved_buf(&chip->dma, snd_dma_pci_buf_id(chip->pci))) {
- err = snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- chip->total_bufsize, &chip->dma);
- if (err < 0 || ! chip->dma.area) {
- snd_printk(KERN_ERR "es1968: can't allocate dma pages for size %d\n",
- chip->total_bufsize);
- return -ENOMEM;
- }
- if ((chip->dma.addr + chip->dma.bytes - 1) & ~((1 << 28) - 1)) {
- snd_dma_free_pages(&chip->dma);
- snd_printk(KERN_ERR "es1968: DMA buffer beyond 256MB.\n");
- return -ENOMEM;
- }
+ err = snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev,
+ chip->total_bufsize, &chip->dma);
+ if (err < 0 || ! chip->dma.area) {
+ dev_err(chip->card->dev,
+ "can't allocate dma pages for size %d\n",
+ chip->total_bufsize);
+ return -ENOMEM;
+ }
+ if ((chip->dma.addr + chip->dma.bytes - 1) & ~((1 << 28) - 1)) {
+ snd_dma_free_pages(&chip->dma);
+ dev_err(chip->card->dev, "DMA buffer beyond 256MB.\n");
+ return -ENOMEM;
}
INIT_LIST_HEAD(&chip->buf_list);
@@ -1496,7 +1420,8 @@ static int snd_es1968_hw_params(struct snd_pcm_substream *substream,
}
chan->memory = snd_es1968_new_memory(chip, size);
if (chan->memory == NULL) {
- // snd_printd("cannot allocate dma buffer: size = %d\n", size);
+ dev_dbg(chip->card->dev,
+ "cannot allocate dma buffer: size = %d\n", size);
return -ENOMEM;
}
snd_pcm_set_runtime_buffer(substream, &chan->memory->buf);
@@ -1582,9 +1507,8 @@ static int snd_es1968_playback_open(struct snd_pcm_substream *substream)
runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max =
calc_available_memory_size(chip);
- spin_lock_irq(&chip->substream_lock);
+ guard(spinlock_irq)(&chip->substream_lock);
list_add(&es->list, &chip->substream_list);
- spin_unlock_irq(&chip->substream_lock);
return 0;
}
@@ -1594,7 +1518,7 @@ static int snd_es1968_capture_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct es1968 *chip = snd_pcm_substream_chip(substream);
struct esschan *es;
- int apu1, apu2;
+ int err, apu1, apu2;
apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_CAPTURE);
if (apu1 < 0)
@@ -1625,7 +1549,8 @@ static int snd_es1968_capture_open(struct snd_pcm_substream *substream)
es->mode = ESM_MODE_CAPTURE;
/* get mixbuffer */
- if ((es->mixbuf = snd_es1968_new_memory(chip, ESM_MIXBUF_SIZE)) == NULL) {
+ es->mixbuf = snd_es1968_new_memory(chip, ESM_MIXBUF_SIZE);
+ if (!es->mixbuf) {
snd_es1968_free_apu_pair(chip, apu1);
snd_es1968_free_apu_pair(chip, apu2);
kfree(es);
@@ -1637,11 +1562,12 @@ static int snd_es1968_capture_open(struct snd_pcm_substream *substream)
runtime->hw = snd_es1968_capture;
runtime->hw.buffer_bytes_max = runtime->hw.period_bytes_max =
calc_available_memory_size(chip) - 1024; /* keep MIXBUF size */
- snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
+ err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES);
+ if (err < 0)
+ return err;
- spin_lock_irq(&chip->substream_lock);
+ guard(spinlock_irq)(&chip->substream_lock);
list_add(&es->list, &chip->substream_list);
- spin_unlock_irq(&chip->substream_lock);
return 0;
}
@@ -1654,9 +1580,9 @@ static int snd_es1968_playback_close(struct snd_pcm_substream *substream)
if (substream->runtime->private_data == NULL)
return 0;
es = substream->runtime->private_data;
- spin_lock_irq(&chip->substream_lock);
- list_del(&es->list);
- spin_unlock_irq(&chip->substream_lock);
+ scoped_guard(spinlock_irq, &chip->substream_lock) {
+ list_del(&es->list);
+ }
snd_es1968_free_apu_pair(chip, es->apu[0]);
kfree(es);
@@ -1671,9 +1597,9 @@ static int snd_es1968_capture_close(struct snd_pcm_substream *substream)
if (substream->runtime->private_data == NULL)
return 0;
es = substream->runtime->private_data;
- spin_lock_irq(&chip->substream_lock);
- list_del(&es->list);
- spin_unlock_irq(&chip->substream_lock);
+ scoped_guard(spinlock_irq, &chip->substream_lock) {
+ list_del(&es->list);
+ }
snd_es1968_free_memory(chip, es->mixbuf);
snd_es1968_free_apu_pair(chip, es->apu[0]);
snd_es1968_free_apu_pair(chip, es->apu[2]);
@@ -1682,10 +1608,9 @@ static int snd_es1968_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_es1968_playback_ops = {
+static const struct snd_pcm_ops snd_es1968_playback_ops = {
.open = snd_es1968_playback_open,
.close = snd_es1968_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_es1968_hw_params,
.hw_free = snd_es1968_hw_free,
.prepare = snd_es1968_pcm_prepare,
@@ -1693,10 +1618,9 @@ static struct snd_pcm_ops snd_es1968_playback_ops = {
.pointer = snd_es1968_pcm_pointer,
};
-static struct snd_pcm_ops snd_es1968_capture_ops = {
+static const struct snd_pcm_ops snd_es1968_capture_ops = {
.open = snd_es1968_capture_open,
.close = snd_es1968_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_es1968_hw_params,
.hw_free = snd_es1968_hw_free,
.prepare = snd_es1968_pcm_prepare,
@@ -1710,23 +1634,28 @@ static struct snd_pcm_ops snd_es1968_capture_ops = {
*/
#define CLOCK_MEASURE_BUFSIZE 16768 /* enough large for a single shot */
-static void __devinit es1968_measure_clock(struct es1968 *chip)
+static void es1968_measure_clock(struct es1968 *chip)
{
int i, apu;
unsigned int pa, offset, t;
struct esm_memory *memory;
- struct timeval start_time, stop_time;
+ ktime_t start_time, stop_time;
+ ktime_t diff;
if (chip->clock == 0)
chip->clock = 48000; /* default clock value */
/* search 2 APUs (although one apu is enough) */
- if ((apu = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY)) < 0) {
- snd_printk(KERN_ERR "Hmm, cannot find empty APU pair!?\n");
+ apu = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY);
+ if (apu < 0) {
+ dev_err(chip->card->dev, "Hmm, cannot find empty APU pair!?\n");
return;
}
- if ((memory = snd_es1968_new_memory(chip, CLOCK_MEASURE_BUFSIZE)) == NULL) {
- snd_printk(KERN_ERR "cannot allocate dma buffer - using default clock %d\n", chip->clock);
+ memory = snd_es1968_new_memory(chip, CLOCK_MEASURE_BUFSIZE);
+ if (!memory) {
+ dev_warn(chip->card->dev,
+ "cannot allocate dma buffer - using default clock %d\n",
+ chip->clock);
snd_es1968_free_apu_pair(chip, apu);
return;
}
@@ -1751,43 +1680,39 @@ static void __devinit es1968_measure_clock(struct es1968 *chip)
apu_set_register(chip, apu, 9, 0xD000);
apu_set_register(chip, apu, 10, 0x8F08);
apu_set_register(chip, apu, 11, 0x0000);
- spin_lock_irq(&chip->reg_lock);
- outw(1, chip->io_port + 0x04); /* clear WP interrupts */
- outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ); /* enable WP ints */
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ outw(1, chip->io_port + 0x04); /* clear WP interrupts */
+ outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ); /* enable WP ints */
+ }
snd_es1968_apu_set_freq(chip, apu, ((unsigned int)48000 << 16) / chip->clock); /* 48000 Hz */
chip->in_measurement = 1;
chip->measure_apu = apu;
- spin_lock_irq(&chip->reg_lock);
- snd_es1968_bob_inc(chip, ESM_BOB_FREQ);
- __apu_set_register(chip, apu, 5, pa & 0xffff);
- snd_es1968_trigger_apu(chip, apu, ESM_APU_16BITLINEAR);
- do_gettimeofday(&start_time);
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ snd_es1968_bob_inc(chip, ESM_BOB_FREQ);
+ __apu_set_register(chip, apu, 5, pa & 0xffff);
+ snd_es1968_trigger_apu(chip, apu, ESM_APU_16BITLINEAR);
+ start_time = ktime_get();
+ }
msleep(50);
- spin_lock_irq(&chip->reg_lock);
- offset = __apu_get_register(chip, apu, 5);
- do_gettimeofday(&stop_time);
- snd_es1968_trigger_apu(chip, apu, 0); /* stop */
- snd_es1968_bob_dec(chip);
- chip->in_measurement = 0;
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ offset = __apu_get_register(chip, apu, 5);
+ stop_time = ktime_get();
+ snd_es1968_trigger_apu(chip, apu, 0); /* stop */
+ snd_es1968_bob_dec(chip);
+ chip->in_measurement = 0;
+ }
/* check the current position */
offset -= (pa & 0xffff);
offset &= 0xfffe;
offset += chip->measure_count * (CLOCK_MEASURE_BUFSIZE/2);
- t = stop_time.tv_sec - start_time.tv_sec;
- t *= 1000000;
- if (stop_time.tv_usec < start_time.tv_usec)
- t -= start_time.tv_usec - stop_time.tv_usec;
- else
- t += stop_time.tv_usec - start_time.tv_usec;
+ diff = ktime_sub(stop_time, start_time);
+ t = ktime_to_us(diff);
if (t == 0) {
- snd_printk(KERN_ERR "?? calculation error..\n");
+ dev_err(chip->card->dev, "?? calculation error..\n");
} else {
offset *= 1000;
offset = (offset / t) * 1000 + ((offset % t) * 1000) / t;
@@ -1795,7 +1720,7 @@ static void __devinit es1968_measure_clock(struct es1968 *chip)
if (offset >= 40000 && offset <= 50000)
chip->clock = (chip->clock * offset) / 48000;
}
- printk(KERN_INFO "es1968: clocking to %d\n", chip->clock);
+ dev_info(chip->card->dev, "clocking to %d\n", chip->clock);
}
snd_es1968_free_memory(chip, memory);
snd_es1968_free_apu_pair(chip, apu);
@@ -1812,14 +1737,15 @@ static void snd_es1968_pcm_free(struct snd_pcm *pcm)
esm->pcm = NULL;
}
-static int __devinit
+static int
snd_es1968_pcm(struct es1968 *chip, int device)
{
struct snd_pcm *pcm;
int err;
/* get DMA buffer */
- if ((err = snd_es1968_init_dmabuf(chip)) < 0)
+ err = snd_es1968_init_dmabuf(chip);
+ if (err < 0)
return err;
/* set PCMBAR */
@@ -1828,9 +1754,10 @@ snd_es1968_pcm(struct es1968 *chip, int device)
wave_set_register(chip, 0x01FE, chip->dma.addr >> 12);
wave_set_register(chip, 0x01FF, chip->dma.addr >> 12);
- if ((err = snd_pcm_new(chip->card, "ESS Maestro", device,
- chip->playback_streams,
- chip->capture_streams, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "ESS Maestro", device,
+ chip->playback_streams,
+ chip->capture_streams, &pcm);
+ if (err < 0)
return err;
pcm->private_data = chip;
@@ -1841,7 +1768,7 @@ snd_es1968_pcm(struct es1968 *chip, int device)
pcm->info_flags = 0;
- strcpy(pcm->name, "ESS Maestro");
+ strscpy(pcm->name, "ESS Maestro");
chip->pcm = pcm;
@@ -1896,13 +1823,10 @@ static void snd_es1968_update_pcm(struct es1968 *chip, struct esschan *es)
(without wrap around) in response to volume button presses and then
generating an interrupt. The pair of counters is stored in bits 1-3 and 5-7
of a byte wide register. The meaning of bits 0 and 4 is unknown. */
-static void es1968_update_hw_volume(unsigned long private_data)
+static void es1968_update_hw_volume(struct work_struct *work)
{
- struct es1968 *chip = (struct es1968 *) private_data;
+ struct es1968 *chip = container_of(work, struct es1968, hwvol_work);
int x, val;
-#ifndef CONFIG_SND_ES1968_INPUT
- unsigned long flags;
-#endif
/* Figure out which volume control button was pushed,
based on differences from the default register
@@ -1921,18 +1845,11 @@ static void es1968_update_hw_volume(unsigned long private_data)
if (! chip->master_switch || ! chip->master_volume)
return;
- /* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */
- spin_lock_irqsave(&chip->ac97_lock, flags);
- val = chip->ac97->regs[AC97_MASTER];
+ val = snd_ac97_read(chip->ac97, AC97_MASTER);
switch (x) {
case 0x88:
/* mute */
val ^= 0x8000;
- chip->ac97->regs[AC97_MASTER] = val;
- outw(val, chip->io_port + ESM_AC97_DATA);
- outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &chip->master_switch->id);
break;
case 0xaa:
/* volume up */
@@ -1940,11 +1857,6 @@ static void es1968_update_hw_volume(unsigned long private_data)
val--;
if ((val & 0x7f00) > 0)
val -= 0x0100;
- chip->ac97->regs[AC97_MASTER] = val;
- outw(val, chip->io_port + ESM_AC97_DATA);
- outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &chip->master_volume->id);
break;
case 0x66:
/* volume down */
@@ -1952,14 +1864,11 @@ static void es1968_update_hw_volume(unsigned long private_data)
val++;
if ((val & 0x7f00) < 0x1f00)
val += 0x0100;
- chip->ac97->regs[AC97_MASTER] = val;
- outw(val, chip->io_port + ESM_AC97_DATA);
- outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
- snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &chip->master_volume->id);
break;
}
- spin_unlock_irqrestore(&chip->ac97_lock, flags);
+ if (snd_ac97_update(chip->ac97, AC97_MASTER, val))
+ snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &chip->master_volume->id);
#else
if (!chip->input_dev)
return;
@@ -1999,17 +1908,14 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id)
struct es1968 *chip = dev_id;
u32 event;
- if (!(event = inb(chip->io_port + 0x1A)))
+ event = inb(chip->io_port + 0x1A);
+ if (!event)
return IRQ_NONE;
outw(inw(chip->io_port + 4) & 1, chip->io_port + 4);
if (event & ESM_HWVOL_IRQ)
-#ifdef CONFIG_SND_ES1968_INPUT
- es1968_update_hw_volume((unsigned long)chip);
-#else
- tasklet_schedule(&chip->hwvol_tq); /* we'll do this later */
-#endif
+ schedule_work(&chip->hwvol_work);
/* else ack 'em all, i imagine */
outb(0xFF, chip->io_port + 0x1A);
@@ -2020,15 +1926,15 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id)
if (event & ESM_SOUND_IRQ) {
struct esschan *es;
- spin_lock(&chip->substream_lock);
- list_for_each_entry(es, &chip->substream_list, list) {
- if (es->running) {
- snd_es1968_update_pcm(chip, es);
- if (es->fmt & ESS_FMT_STEREO)
- snd_es1968_suppress_jitter(chip, es);
+ scoped_guard(spinlock, &chip->substream_lock) {
+ list_for_each_entry(es, &chip->substream_list, list) {
+ if (es->running) {
+ snd_es1968_update_pcm(chip, es);
+ if (es->fmt & ESS_FMT_STEREO)
+ snd_es1968_suppress_jitter(chip, es);
+ }
}
}
- spin_unlock(&chip->substream_lock);
if (chip->in_measurement) {
unsigned int curp = __apu_get_register(chip, chip->measure_apu, 5);
if (curp < chip->measure_lastpos)
@@ -2044,39 +1950,34 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id)
* Mixer stuff
*/
-static int __devinit
+static int
snd_es1968_mixer(struct es1968 *chip)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
-#ifndef CONFIG_SND_ES1968_INPUT
- struct snd_ctl_elem_id elem_id;
-#endif
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_es1968_ac97_write,
.read = snd_es1968_ac97_read,
};
- if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus);
+ if (err < 0)
return err;
pbus->no_vra = 1; /* ES1968 doesn't need VRA */
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
- if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97)) < 0)
+ err = snd_ac97_mixer(pbus, &ac97, &chip->ac97);
+ if (err < 0)
return err;
#ifndef CONFIG_SND_ES1968_INPUT
/* attach master switch / volumes for h/w volume control */
- memset(&elem_id, 0, sizeof(elem_id));
- elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(elem_id.name, "Master Playback Switch");
- chip->master_switch = snd_ctl_find_id(chip->card, &elem_id);
- memset(&elem_id, 0, sizeof(elem_id));
- elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(elem_id.name, "Master Playback Volume");
- chip->master_volume = snd_ctl_find_id(chip->card, &elem_id);
+ chip->master_switch = snd_ctl_find_id_mixer(chip->card,
+ "Master Playback Switch");
+ chip->master_volume = snd_ctl_find_id_mixer(chip->card,
+ "Master Playback Volume");
#endif
return 0;
@@ -2137,7 +2038,7 @@ static void snd_es1968_ac97_reset(struct es1968 *chip)
outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c);
#if 0 /* the loop here needs to be much better if we want it.. */
- snd_printk(KERN_INFO "trying software reset\n");
+ dev_info(chip->card->dev, "trying software reset\n");
/* try and do a software reset */
outb(0x80 | 0x7c, ioaddr + 0x30);
for (w = 0;; w++) {
@@ -2319,7 +2220,7 @@ static void snd_es1968_chip_init(struct es1968 *chip)
outb(0x88, iobase+0x1f);
/* it appears some maestros (dell 7500) only work if these are set,
- regardless of wether we use the assp or not. */
+ regardless of whether we use the assp or not. */
outb(0, iobase + ASSP_CONTROL_B);
outb(3, iobase + ASSP_CONTROL_A); /* M: Reserved bits... */
@@ -2405,50 +2306,34 @@ static void snd_es1968_start_irq(struct es1968 *chip)
outw(w, chip->io_port + ESM_PORT_HOST_IRQ);
}
-#ifdef CONFIG_PM
/*
* PM support
*/
-static int es1968_suspend(struct pci_dev *pci, pm_message_t state)
+static int es1968_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct es1968 *chip = card->private_data;
if (! chip->do_pm)
return 0;
chip->in_suspend = 1;
+ cancel_work_sync(&chip->hwvol_work);
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
snd_ac97_suspend(chip->ac97);
snd_es1968_bob_stop(chip);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int es1968_resume(struct pci_dev *pci)
+static int es1968_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct es1968 *chip = card->private_data;
struct esschan *es;
if (! chip->do_pm)
return 0;
- /* restore all our config */
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "es1968: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
-
snd_es1968_chip_init(chip);
/* need to restore the base pointers.. */
@@ -2481,11 +2366,12 @@ static int es1968_resume(struct pci_dev *pci)
chip->in_suspend = 0;
return 0;
}
-#endif /* CONFIG_PM */
+
+static DEFINE_SIMPLE_DEV_PM_OPS(es1968_pm, es1968_suspend, es1968_resume);
#ifdef SUPPORT_JOYSTICK
#define JOYSTICK_ADDR 0x200
-static int __devinit snd_es1968_create_gameport(struct es1968 *chip, int dev)
+static int snd_es1968_create_gameport(struct es1968 *chip, int dev)
{
struct gameport *gp;
struct resource *r;
@@ -2494,14 +2380,15 @@ static int __devinit snd_es1968_create_gameport(struct es1968 *chip, int dev)
if (!joystick[dev])
return -ENODEV;
- r = request_region(JOYSTICK_ADDR, 8, "ES1968 gameport");
+ r = devm_request_region(&chip->pci->dev, JOYSTICK_ADDR, 8,
+ "ES1968 gameport");
if (!r)
return -EBUSY;
chip->gameport = gp = gameport_allocate_port();
if (!gp) {
- printk(KERN_ERR "es1968: cannot allocate memory for gameport\n");
- release_and_free_resource(r);
+ dev_err(chip->card->dev,
+ "cannot allocate memory for gameport\n");
return -ENOMEM;
}
@@ -2512,7 +2399,6 @@ static int __devinit snd_es1968_create_gameport(struct es1968 *chip, int dev)
gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
gameport_set_dev_parent(gp, &chip->pci->dev);
gp->io = JOYSTICK_ADDR;
- gameport_set_port_data(gp, r);
gameport_register_port(gp);
@@ -2522,12 +2408,8 @@ static int __devinit snd_es1968_create_gameport(struct es1968 *chip, int dev)
static void snd_es1968_free_gameport(struct es1968 *chip)
{
if (chip->gameport) {
- struct resource *r = gameport_get_port_data(chip->gameport);
-
gameport_unregister_port(chip->gameport);
chip->gameport = NULL;
-
- release_and_free_resource(r);
}
}
#else
@@ -2536,12 +2418,12 @@ static inline void snd_es1968_free_gameport(struct es1968 *chip) { }
#endif
#ifdef CONFIG_SND_ES1968_INPUT
-static int __devinit snd_es1968_input_register(struct es1968 *chip)
+static int snd_es1968_input_register(struct es1968 *chip)
{
struct input_dev *input_dev;
int err;
- input_dev = input_allocate_device();
+ input_dev = devm_input_allocate_device(&chip->pci->dev);
if (!input_dev)
return -ENOMEM;
@@ -2561,43 +2443,108 @@ static int __devinit snd_es1968_input_register(struct es1968 *chip)
__set_bit(KEY_VOLUMEUP, input_dev->keybit);
err = input_register_device(input_dev);
- if (err) {
- input_free_device(input_dev);
+ if (err)
return err;
- }
chip->input_dev = input_dev;
return 0;
}
#endif /* CONFIG_SND_ES1968_INPUT */
-static int snd_es1968_free(struct es1968 *chip)
+#ifdef CONFIG_SND_ES1968_RADIO
+#define GPIO_DATA 0x60
+#define IO_MASK 4 /* mask register offset from GPIO_DATA
+ bits 1=unmask write to given bit */
+#define IO_DIR 8 /* direction register offset from GPIO_DATA
+ bits 0/1=read/write direction */
+
+/* GPIO to TEA575x maps */
+struct snd_es1968_tea575x_gpio {
+ u8 data, clk, wren, most;
+ char *name;
+};
+
+static const struct snd_es1968_tea575x_gpio snd_es1968_tea575x_gpios[] = {
+ { .data = 6, .clk = 7, .wren = 8, .most = 9, .name = "SF64-PCE2" },
+ { .data = 7, .clk = 8, .wren = 6, .most = 10, .name = "M56VAP" },
+};
+
+#define get_tea575x_gpio(chip) \
+ (&snd_es1968_tea575x_gpios[(chip)->tea575x_tuner])
+
+
+static void snd_es1968_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
{
-#ifdef CONFIG_SND_ES1968_INPUT
- if (chip->input_dev)
- input_unregister_device(chip->input_dev);
+ struct es1968 *chip = tea->private_data;
+ struct snd_es1968_tea575x_gpio gpio = *get_tea575x_gpio(chip);
+ u16 val = 0;
+
+ val |= (pins & TEA575X_DATA) ? (1 << gpio.data) : 0;
+ val |= (pins & TEA575X_CLK) ? (1 << gpio.clk) : 0;
+ val |= (pins & TEA575X_WREN) ? (1 << gpio.wren) : 0;
+
+ outw(val, chip->io_port + GPIO_DATA);
+}
+
+static u8 snd_es1968_tea575x_get_pins(struct snd_tea575x *tea)
+{
+ struct es1968 *chip = tea->private_data;
+ struct snd_es1968_tea575x_gpio gpio = *get_tea575x_gpio(chip);
+ u16 val = inw(chip->io_port + GPIO_DATA);
+ u8 ret = 0;
+
+ if (val & (1 << gpio.data))
+ ret |= TEA575X_DATA;
+ if (val & (1 << gpio.most))
+ ret |= TEA575X_MOST;
+
+ return ret;
+}
+
+static void snd_es1968_tea575x_set_direction(struct snd_tea575x *tea, bool output)
+{
+ struct es1968 *chip = tea->private_data;
+ unsigned long io = chip->io_port + GPIO_DATA;
+ u16 odir = inw(io + IO_DIR);
+ struct snd_es1968_tea575x_gpio gpio = *get_tea575x_gpio(chip);
+
+ if (output) {
+ outw(~((1 << gpio.data) | (1 << gpio.clk) | (1 << gpio.wren)),
+ io + IO_MASK);
+ outw(odir | (1 << gpio.data) | (1 << gpio.clk) | (1 << gpio.wren),
+ io + IO_DIR);
+ } else {
+ outw(~((1 << gpio.clk) | (1 << gpio.wren) | (1 << gpio.data) | (1 << gpio.most)),
+ io + IO_MASK);
+ outw((odir & ~((1 << gpio.data) | (1 << gpio.most)))
+ | (1 << gpio.clk) | (1 << gpio.wren), io + IO_DIR);
+ }
+}
+
+static const struct snd_tea575x_ops snd_es1968_tea_ops = {
+ .set_pins = snd_es1968_tea575x_set_pins,
+ .get_pins = snd_es1968_tea575x_get_pins,
+ .set_direction = snd_es1968_tea575x_set_direction,
+};
#endif
+static void snd_es1968_free(struct snd_card *card)
+{
+ struct es1968 *chip = card->private_data;
+
+ cancel_work_sync(&chip->hwvol_work);
+
if (chip->io_port) {
- if (chip->irq >= 0)
- synchronize_irq(chip->irq);
outw(1, chip->io_port + 0x04); /* clear WP interrupts */
outw(0, chip->io_port + ESM_PORT_HOST_IRQ); /* disable IRQ */
}
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- snd_es1968_free_gameport(chip);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
-}
+#ifdef CONFIG_SND_ES1968_RADIO
+ snd_tea575x_exit(&chip->tea);
+ v4l2_device_unregister(&chip->v4l2_dev);
+#endif
-static int snd_es1968_dev_free(struct snd_device *device)
-{
- struct es1968 *chip = device->device_data;
- return snd_es1968_free(chip);
+ snd_es1968_free_gameport(chip);
}
struct ess_device_list {
@@ -2605,53 +2552,44 @@ struct ess_device_list {
unsigned short vendor; /* subsystem vendor id */
};
-static struct ess_device_list pm_whitelist[] __devinitdata = {
+static const struct ess_device_list pm_allowlist[] = {
{ TYPE_MAESTRO2E, 0x0e11 }, /* Compaq Armada */
{ TYPE_MAESTRO2E, 0x1028 },
{ TYPE_MAESTRO2E, 0x103c },
{ TYPE_MAESTRO2E, 0x1179 },
{ TYPE_MAESTRO2E, 0x14c0 }, /* HP omnibook 4150 */
{ TYPE_MAESTRO2E, 0x1558 },
+ { TYPE_MAESTRO2E, 0x125d }, /* a PCI card, e.g. Terratec DMX */
+ { TYPE_MAESTRO2, 0x125d }, /* a PCI card, e.g. SF64-PCE2 */
};
-static struct ess_device_list mpu_blacklist[] __devinitdata = {
+static const struct ess_device_list mpu_denylist[] = {
{ TYPE_MAESTRO2, 0x125d },
};
-static int __devinit snd_es1968_create(struct snd_card *card,
- struct pci_dev *pci,
- int total_bufsize,
- int play_streams,
- int capt_streams,
- int chip_type,
- int do_pm,
- struct es1968 **chip_ret)
-{
- static struct snd_device_ops ops = {
- .dev_free = snd_es1968_dev_free,
- };
- struct es1968 *chip;
+static int snd_es1968_create(struct snd_card *card,
+ struct pci_dev *pci,
+ int total_bufsize,
+ int play_streams,
+ int capt_streams,
+ int chip_type,
+ int do_pm,
+ int radio_nr)
+{
+ struct es1968 *chip = card->private_data;
int i, err;
- *chip_ret = NULL;
-
/* enable PCI device */
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
/* check, if we can restrict PCI DMA transfers to 28 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
- snd_printk(KERN_ERR "architecture does not support 28bit PCI busmaster DMA\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(28))) {
+ dev_err(card->dev,
+ "architecture does not support 28bit PCI busmaster DMA\n");
return -ENXIO;
}
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (! chip) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
/* Set Vars */
chip->type = chip_type;
spin_lock_init(&chip->reg_lock);
@@ -2659,10 +2597,7 @@ static int __devinit snd_es1968_create(struct snd_card *card,
INIT_LIST_HEAD(&chip->buf_list);
INIT_LIST_HEAD(&chip->substream_list);
mutex_init(&chip->memory_mutex);
-#ifndef CONFIG_SND_ES1968_INPUT
- spin_lock_init(&chip->ac97_lock);
- tasklet_init(&chip->hwvol_tq, es1968_update_hw_volume, (unsigned long)chip);
-#endif
+ INIT_WORK(&chip->hwvol_work, es1968_update_hw_volume);
chip->card = card;
chip->pci = pci;
chip->irq = -1;
@@ -2670,19 +2605,18 @@ static int __devinit snd_es1968_create(struct snd_card *card,
chip->playback_streams = play_streams;
chip->capture_streams = capt_streams;
- if ((err = pci_request_regions(pci, "ESS Maestro")) < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pcim_request_all_regions(pci, "ESS Maestro");
+ if (err < 0)
return err;
- }
chip->io_port = pci_resource_start(pci, 0);
- if (request_irq(pci->irq, snd_es1968_interrupt, IRQF_SHARED,
- "ESS Maestro", chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_es1968_free(chip);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_es1968_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_es1968_free;
/* Clear Maestro_map */
for (i = 0; i < 32; i++)
@@ -2696,19 +2630,19 @@ static int __devinit snd_es1968_create(struct snd_card *card,
pci_set_master(pci);
if (do_pm > 1) {
- /* disable power-management if not on the whitelist */
+ /* disable power-management if not on the allowlist */
unsigned short vend;
pci_read_config_word(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend);
- for (i = 0; i < (int)ARRAY_SIZE(pm_whitelist); i++) {
- if (chip->type == pm_whitelist[i].type &&
- vend == pm_whitelist[i].vendor) {
+ for (i = 0; i < (int)ARRAY_SIZE(pm_allowlist); i++) {
+ if (chip->type == pm_allowlist[i].type &&
+ vend == pm_allowlist[i].vendor) {
do_pm = 1;
break;
}
}
if (do_pm > 1) {
/* not matched; disabling pm */
- printk(KERN_INFO "es1968: not attempting power management.\n");
+ dev_info(card->dev, "not attempting power management.\n");
do_pm = 0;
}
}
@@ -2716,23 +2650,37 @@ static int __devinit snd_es1968_create(struct snd_card *card,
snd_es1968_chip_init(chip);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_es1968_free(chip);
+#ifdef CONFIG_SND_ES1968_RADIO
+ /* don't play with GPIOs on laptops */
+ if (chip->pci->subsystem_vendor != 0x125d)
+ return 0;
+ err = v4l2_device_register(&pci->dev, &chip->v4l2_dev);
+ if (err < 0)
return err;
+ chip->tea.v4l2_dev = &chip->v4l2_dev;
+ chip->tea.private_data = chip;
+ chip->tea.radio_nr = radio_nr;
+ chip->tea.ops = &snd_es1968_tea_ops;
+ sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
+ for (i = 0; i < ARRAY_SIZE(snd_es1968_tea575x_gpios); i++) {
+ chip->tea575x_tuner = i;
+ if (!snd_tea575x_init(&chip->tea, THIS_MODULE)) {
+ dev_info(card->dev, "detected TEA575x radio type %s\n",
+ get_tea575x_gpio(chip)->name);
+ strscpy(chip->tea.card, get_tea575x_gpio(chip)->name,
+ sizeof(chip->tea.card));
+ break;
+ }
}
-
- snd_card_set_dev(card, &pci->dev);
-
- *chip_ret = chip;
-
+#endif
return 0;
}
/*
*/
-static int __devinit snd_es1968_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_es1968_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -2747,70 +2695,69 @@ static int __devinit snd_es1968_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
if (total_bufsize[dev] < 128)
total_bufsize[dev] = 128;
if (total_bufsize[dev] > 4096)
total_bufsize[dev] = 4096;
- if ((err = snd_es1968_create(card, pci,
- total_bufsize[dev] * 1024, /* in bytes */
- pcm_substreams_p[dev],
- pcm_substreams_c[dev],
- pci_id->driver_data,
- use_pm[dev],
- &chip)) < 0) {
- snd_card_free(card);
+ err = snd_es1968_create(card, pci,
+ total_bufsize[dev] * 1024, /* in bytes */
+ pcm_substreams_p[dev],
+ pcm_substreams_c[dev],
+ pci_id->driver_data,
+ use_pm[dev],
+ radio_nr[dev]);
+ if (err < 0)
return err;
- }
- card->private_data = chip;
switch (chip->type) {
case TYPE_MAESTRO2E:
- strcpy(card->driver, "ES1978");
- strcpy(card->shortname, "ESS ES1978 (Maestro 2E)");
+ strscpy(card->driver, "ES1978");
+ strscpy(card->shortname, "ESS ES1978 (Maestro 2E)");
break;
case TYPE_MAESTRO2:
- strcpy(card->driver, "ES1968");
- strcpy(card->shortname, "ESS ES1968 (Maestro 2)");
+ strscpy(card->driver, "ES1968");
+ strscpy(card->shortname, "ESS ES1968 (Maestro 2)");
break;
case TYPE_MAESTRO:
- strcpy(card->driver, "ESM1");
- strcpy(card->shortname, "ESS Maestro 1");
+ strscpy(card->driver, "ESM1");
+ strscpy(card->shortname, "ESS Maestro 1");
break;
}
- if ((err = snd_es1968_pcm(chip, 0)) < 0) {
- snd_card_free(card);
+ err = snd_es1968_pcm(chip, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_es1968_mixer(chip)) < 0) {
- snd_card_free(card);
+ err = snd_es1968_mixer(chip);
+ if (err < 0)
return err;
- }
if (enable_mpu[dev] == 2) {
- /* check the black list */
+ /* check the deny list */
unsigned short vend;
pci_read_config_word(chip->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend);
- for (i = 0; i < ARRAY_SIZE(mpu_blacklist); i++) {
- if (chip->type == mpu_blacklist[i].type &&
- vend == mpu_blacklist[i].vendor) {
+ for (i = 0; i < ARRAY_SIZE(mpu_denylist); i++) {
+ if (chip->type == mpu_denylist[i].type &&
+ vend == mpu_denylist[i].vendor) {
enable_mpu[dev] = 0;
break;
}
}
}
if (enable_mpu[dev]) {
- if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
- chip->io_port + ESM_MPU401_PORT,
- MPU401_INFO_INTEGRATED,
- chip->irq, 0, &chip->rmidi)) < 0) {
- printk(KERN_WARNING "es1968: skipping MPU-401 MIDI support..\n");
- }
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+ chip->io_port + ESM_MPU401_PORT,
+ MPU401_INFO_INTEGRATED |
+ MPU401_INFO_IRQ_HOOK,
+ -1, &chip->rmidi);
+ if (err < 0)
+ dev_warn(card->dev, "skipping MPU-401 MIDI support..\n");
}
snd_es1968_create_gameport(chip, dev);
@@ -2818,8 +2765,8 @@ static int __devinit snd_es1968_probe(struct pci_dev *pci,
#ifdef CONFIG_SND_ES1968_INPUT
err = snd_es1968_input_register(chip);
if (err)
- snd_printk(KERN_WARNING "Input device registration "
- "failed with error %i", err);
+ dev_warn(card->dev,
+ "Input device registration failed with error %i", err);
#endif
snd_es1968_start_irq(chip);
@@ -2831,41 +2778,27 @@ static int __devinit snd_es1968_probe(struct pci_dev *pci,
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, chip->io_port, chip->irq);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_es1968_remove(struct pci_dev *pci)
+static int snd_es1968_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_es1968_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "ES1968 (ESS Maestro)",
+static struct pci_driver es1968_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_es1968_ids,
.probe = snd_es1968_probe,
- .remove = __devexit_p(snd_es1968_remove),
-#ifdef CONFIG_PM
- .suspend = es1968_suspend,
- .resume = es1968_resume,
-#endif
+ .driver = {
+ .pm = &es1968_pm,
+ },
};
-static int __init alsa_card_es1968_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_es1968_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_es1968_init)
-module_exit(alsa_card_es1968_exit)
+module_pci_driver(es1968_driver);
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index e1baad74ea4b..4ca992449ea3 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -1,31 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* The driver for the ForteMedia FM801 based soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- *
- * Support FM only card by Andy Shevchenko <andy@smile.org.ua>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/io.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/tlv.h>
@@ -34,31 +19,27 @@
#include <sound/opl3.h>
#include <sound/initval.h>
-#include <asm/io.h>
-
#ifdef CONFIG_SND_FM801_TEA575X_BOOL
-#include <sound/tea575x-tuner.h>
-#define TEA575X_RADIO 1
+#include <media/drv-intf/tea575x.h>
#endif
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("ForteMedia FM801");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{ForteMedia,FM801},"
- "{Genius,SoundMaker Live 5.1}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
/*
* Enable TEA575x tuner
* 1 = MediaForte 256-PCS
- * 2 = MediaForte 256-PCPR
+ * 2 = MediaForte 256-PCP
* 3 = MediaForte 64-PCR
* 16 = setup tuner only (this is additional bit), i.e. SF64-PCR FM card
* High 16-bits are video (radio) device number + 1
*/
static int tea575x_tuner[SNDRV_CARDS];
+static int radio_nr[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the FM801 soundcard.");
@@ -67,8 +48,12 @@ MODULE_PARM_DESC(id, "ID string for the FM801 soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable FM801 soundcard.");
module_param_array(tea575x_tuner, int, NULL, 0444);
-MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (1 = SF256-PCS, 2=SF256-PCPR, 3=SF64-PCR, +16=tuner-only).");
+MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (0 = auto, 1 = SF256-PCS, 2=SF256-PCP, 3=SF64-PCR, 8=disable, +16=tuner-only).");
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
+
+#define TUNER_DISABLED (1<<3)
#define TUNER_ONLY (1<<4)
#define TUNER_TYPE_MASK (~TUNER_ONLY & 0xFFFF)
@@ -76,7 +61,10 @@ MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (1 = SF256-PCS, 2=S
* Direct registers
*/
-#define FM801_REG(chip, reg) (chip->port + FM801_##reg)
+#define fm801_writew(chip,reg,value) outw((value), chip->port + FM801_##reg)
+#define fm801_readw(chip,reg) inw(chip->port + FM801_##reg)
+
+#define fm801_writel(chip,reg,value) outl((value), chip->port + FM801_##reg)
#define FM801_PCM_VOL 0x00 /* PCM Output Volume */
#define FM801_FM_VOL 0x02 /* FM Output Volume */
@@ -152,21 +140,55 @@ MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (1 = SF256-PCS, 2=S
#define FM801_GPIO_GS3 (1<<15)
#define FM801_GPIO_GS(x) (1<<(12+(x)))
-/*
-
+/**
+ * struct fm801 - describes FM801 chip
+ * @dev: device for this chio
+ * @irq: irq number
+ * @port: I/O port number
+ * @multichannel: multichannel support
+ * @secondary: secondary codec
+ * @secondary_addr: address of the secondary codec
+ * @tea575x_tuner: tuner access method & flags
+ * @ply_ctrl: playback control
+ * @cap_ctrl: capture control
+ * @ply_buffer: playback buffer
+ * @ply_buf: playback buffer index
+ * @ply_count: playback buffer count
+ * @ply_size: playback buffer size
+ * @ply_pos: playback position
+ * @cap_buffer: capture buffer
+ * @cap_buf: capture buffer index
+ * @cap_count: capture buffer count
+ * @cap_size: capture buffer size
+ * @cap_pos: capture position
+ * @ac97_bus: ac97 bus handle
+ * @ac97: ac97 handle
+ * @ac97_sec: ac97 secondary handle
+ * @card: ALSA card
+ * @pcm: PCM devices
+ * @rmidi: rmidi device
+ * @playback_substream: substream for playback
+ * @capture_substream: substream for capture
+ * @p_dma_size: playback DMA size
+ * @c_dma_size: capture DMA size
+ * @reg_lock: lock
+ * @proc_entry: /proc entry
+ * @v4l2_dev: v4l2 device
+ * @tea: tea575a structure
+ * @saved_regs: context saved during suspend
*/
-
struct fm801 {
+ struct device *dev;
int irq;
- unsigned long port; /* I/O port number */
- unsigned int multichannel: 1, /* multichannel support */
- secondary: 1; /* secondary codec */
- unsigned char secondary_addr; /* address of the secondary codec */
- unsigned int tea575x_tuner; /* tuner access method & flags */
+ unsigned long port;
+ unsigned int multichannel: 1,
+ secondary: 1;
+ unsigned char secondary_addr;
+ unsigned int tea575x_tuner;
- unsigned short ply_ctrl; /* playback control */
- unsigned short cap_ctrl; /* capture control */
+ unsigned short ply_ctrl;
+ unsigned short cap_ctrl;
unsigned long ply_buffer;
unsigned int ply_buf;
@@ -184,7 +206,6 @@ struct fm801 {
struct snd_ac97 *ac97;
struct snd_ac97 *ac97_sec;
- struct pci_dev *pci;
struct snd_card *card;
struct snd_pcm *pcm;
struct snd_rawmidi *rmidi;
@@ -196,16 +217,29 @@ struct fm801 {
spinlock_t reg_lock;
struct snd_info_entry *proc_entry;
-#ifdef TEA575X_RADIO
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
+ struct v4l2_device v4l2_dev;
struct snd_tea575x tea;
#endif
-#ifdef CONFIG_PM
u16 saved_regs[0x20];
-#endif
};
-static DEFINE_PCI_DEVICE_TABLE(snd_fm801_ids) = {
+/*
+ * IO accessors
+ */
+
+static inline void fm801_iowrite16(struct fm801 *chip, unsigned short offset, u16 value)
+{
+ outw(value, chip->port + offset);
+}
+
+static inline u16 fm801_ioread16(struct fm801 *chip, unsigned short offset)
+{
+ return inw(chip->port + offset);
+}
+
+static const struct pci_device_id snd_fm801_ids[] = {
{ 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, /* FM801 */
{ 0x5213, 0x0510, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, }, /* Gallant Odyssey Sound 4 */
{ 0, }
@@ -217,20 +251,42 @@ MODULE_DEVICE_TABLE(pci, snd_fm801_ids);
* common I/O routines
*/
+static bool fm801_ac97_is_ready(struct fm801 *chip, unsigned int iterations)
+{
+ unsigned int idx;
+
+ for (idx = 0; idx < iterations; idx++) {
+ if (!(fm801_readw(chip, AC97_CMD) & FM801_AC97_BUSY))
+ return true;
+ udelay(10);
+ }
+ return false;
+}
+
+static bool fm801_ac97_is_valid(struct fm801 *chip, unsigned int iterations)
+{
+ unsigned int idx;
+
+ for (idx = 0; idx < iterations; idx++) {
+ if (fm801_readw(chip, AC97_CMD) & FM801_AC97_VALID)
+ return true;
+ udelay(10);
+ }
+ return false;
+}
+
static int snd_fm801_update_bits(struct fm801 *chip, unsigned short reg,
unsigned short mask, unsigned short value)
{
int change;
- unsigned long flags;
unsigned short old, new;
- spin_lock_irqsave(&chip->reg_lock, flags);
- old = inw(chip->port + reg);
+ guard(spinlock_irqsave)(&chip->reg_lock);
+ old = fm801_ioread16(chip, reg);
new = (old & ~mask) | value;
change = old != new;
if (change)
- outw(new, chip->port + reg);
- spin_unlock_irqrestore(&chip->reg_lock, flags);
+ fm801_iowrite16(chip, reg, new);
return change;
}
@@ -239,92 +295,73 @@ static void snd_fm801_codec_write(struct snd_ac97 *ac97,
unsigned short val)
{
struct fm801 *chip = ac97->private_data;
- int idx;
/*
* Wait until the codec interface is not ready..
*/
- for (idx = 0; idx < 100; idx++) {
- if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
- goto ok1;
- udelay(10);
+ if (!fm801_ac97_is_ready(chip, 100)) {
+ dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
+ return;
}
- snd_printk(KERN_ERR "AC'97 interface is busy (1)\n");
- return;
- ok1:
/* write data and address */
- outw(val, FM801_REG(chip, AC97_DATA));
- outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD));
+ fm801_writew(chip, AC97_DATA, val);
+ fm801_writew(chip, AC97_CMD, reg | (ac97->addr << FM801_AC97_ADDR_SHIFT));
/*
* Wait until the write command is not completed..
- */
- for (idx = 0; idx < 1000; idx++) {
- if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
- return;
- udelay(10);
- }
- snd_printk(KERN_ERR "AC'97 interface #%d is busy (2)\n", ac97->num);
+ */
+ if (!fm801_ac97_is_ready(chip, 1000))
+ dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n",
+ ac97->num);
}
static unsigned short snd_fm801_codec_read(struct snd_ac97 *ac97, unsigned short reg)
{
struct fm801 *chip = ac97->private_data;
- int idx;
/*
* Wait until the codec interface is not ready..
*/
- for (idx = 0; idx < 100; idx++) {
- if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
- goto ok1;
- udelay(10);
+ if (!fm801_ac97_is_ready(chip, 100)) {
+ dev_err(chip->card->dev, "AC'97 interface is busy (1)\n");
+ return 0;
}
- snd_printk(KERN_ERR "AC'97 interface is busy (1)\n");
- return 0;
- ok1:
/* read command */
- outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ,
- FM801_REG(chip, AC97_CMD));
- for (idx = 0; idx < 100; idx++) {
- if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY))
- goto ok2;
- udelay(10);
+ fm801_writew(chip, AC97_CMD,
+ reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ);
+ if (!fm801_ac97_is_ready(chip, 100)) {
+ dev_err(chip->card->dev, "AC'97 interface #%d is busy (2)\n",
+ ac97->num);
+ return 0;
}
- snd_printk(KERN_ERR "AC'97 interface #%d is busy (2)\n", ac97->num);
- return 0;
- ok2:
- for (idx = 0; idx < 1000; idx++) {
- if (inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_VALID)
- goto ok3;
- udelay(10);
+ if (!fm801_ac97_is_valid(chip, 1000)) {
+ dev_err(chip->card->dev,
+ "AC'97 interface #%d is not valid (2)\n", ac97->num);
+ return 0;
}
- snd_printk(KERN_ERR "AC'97 interface #%d is not valid (2)\n", ac97->num);
- return 0;
- ok3:
- return inw(FM801_REG(chip, AC97_DATA));
+ return fm801_readw(chip, AC97_DATA);
}
-static unsigned int rates[] = {
+static const unsigned int rates[] = {
5500, 8000, 9600, 11025,
16000, 19200, 22050, 32000,
38400, 44100, 48000
};
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
};
-static unsigned int channels[] = {
+static const unsigned int channels[] = {
2, 4, 6
};
-static struct snd_pcm_hw_constraint_list hw_constraints_channels = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_channels = {
.count = ARRAY_SIZE(channels),
.list = channels,
.mask = 0,
@@ -354,7 +391,7 @@ static int snd_fm801_playback_trigger(struct snd_pcm_substream *substream,
{
struct fm801 *chip = snd_pcm_substream_chip(substream);
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
chip->ply_ctrl &= ~(FM801_BUF1_LAST |
@@ -375,12 +412,10 @@ static int snd_fm801_playback_trigger(struct snd_pcm_substream *substream,
chip->ply_ctrl &= ~FM801_PAUSE;
break;
default:
- spin_unlock(&chip->reg_lock);
snd_BUG();
return -EINVAL;
}
- outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
- spin_unlock(&chip->reg_lock);
+ fm801_writew(chip, PLY_CTRL, chip->ply_ctrl);
return 0;
}
@@ -389,7 +424,7 @@ static int snd_fm801_capture_trigger(struct snd_pcm_substream *substream,
{
struct fm801 *chip = snd_pcm_substream_chip(substream);
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
chip->cap_ctrl &= ~(FM801_BUF1_LAST |
@@ -410,26 +445,13 @@ static int snd_fm801_capture_trigger(struct snd_pcm_substream *substream,
chip->cap_ctrl &= ~FM801_PAUSE;
break;
default:
- spin_unlock(&chip->reg_lock);
snd_BUG();
return -EINVAL;
}
- outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
- spin_unlock(&chip->reg_lock);
+ fm801_writew(chip, CAP_CTRL, chip->cap_ctrl);
return 0;
}
-static int snd_fm801_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_fm801_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
static int snd_fm801_playback_prepare(struct snd_pcm_substream *substream)
{
struct fm801 *chip = snd_pcm_substream_chip(substream);
@@ -437,7 +459,7 @@ static int snd_fm801_playback_prepare(struct snd_pcm_substream *substream)
chip->ply_size = snd_pcm_lib_buffer_bytes(substream);
chip->ply_count = snd_pcm_lib_period_bytes(substream);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->ply_ctrl &= ~(FM801_START | FM801_16BIT |
FM801_STEREO | FM801_RATE_MASK |
FM801_CHANNELS_MASK);
@@ -452,13 +474,13 @@ static int snd_fm801_playback_prepare(struct snd_pcm_substream *substream)
}
chip->ply_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
chip->ply_buf = 0;
- outw(chip->ply_ctrl, FM801_REG(chip, PLY_CTRL));
- outw(chip->ply_count - 1, FM801_REG(chip, PLY_COUNT));
+ fm801_writew(chip, PLY_CTRL, chip->ply_ctrl);
+ fm801_writew(chip, PLY_COUNT, chip->ply_count - 1);
chip->ply_buffer = runtime->dma_addr;
chip->ply_pos = 0;
- outl(chip->ply_buffer, FM801_REG(chip, PLY_BUF1));
- outl(chip->ply_buffer + (chip->ply_count % chip->ply_size), FM801_REG(chip, PLY_BUF2));
- spin_unlock_irq(&chip->reg_lock);
+ fm801_writel(chip, PLY_BUF1, chip->ply_buffer);
+ fm801_writel(chip, PLY_BUF2,
+ chip->ply_buffer + (chip->ply_count % chip->ply_size));
return 0;
}
@@ -469,7 +491,7 @@ static int snd_fm801_capture_prepare(struct snd_pcm_substream *substream)
chip->cap_size = snd_pcm_lib_buffer_bytes(substream);
chip->cap_count = snd_pcm_lib_period_bytes(substream);
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
chip->cap_ctrl &= ~(FM801_START | FM801_16BIT |
FM801_STEREO | FM801_RATE_MASK);
if (snd_pcm_format_width(runtime->format) == 16)
@@ -478,13 +500,13 @@ static int snd_fm801_capture_prepare(struct snd_pcm_substream *substream)
chip->cap_ctrl |= FM801_STEREO;
chip->cap_ctrl |= snd_fm801_rate_bits(runtime->rate) << FM801_RATE_SHIFT;
chip->cap_buf = 0;
- outw(chip->cap_ctrl, FM801_REG(chip, CAP_CTRL));
- outw(chip->cap_count - 1, FM801_REG(chip, CAP_COUNT));
+ fm801_writew(chip, CAP_CTRL, chip->cap_ctrl);
+ fm801_writew(chip, CAP_COUNT, chip->cap_count - 1);
chip->cap_buffer = runtime->dma_addr;
chip->cap_pos = 0;
- outl(chip->cap_buffer, FM801_REG(chip, CAP_BUF1));
- outl(chip->cap_buffer + (chip->cap_count % chip->cap_size), FM801_REG(chip, CAP_BUF2));
- spin_unlock_irq(&chip->reg_lock);
+ fm801_writel(chip, CAP_BUF1, chip->cap_buffer);
+ fm801_writel(chip, CAP_BUF2,
+ chip->cap_buffer + (chip->cap_count % chip->cap_size));
return 0;
}
@@ -495,13 +517,12 @@ static snd_pcm_uframes_t snd_fm801_playback_pointer(struct snd_pcm_substream *su
if (!(chip->ply_ctrl & FM801_START))
return 0;
- spin_lock(&chip->reg_lock);
- ptr = chip->ply_pos + (chip->ply_count - 1) - inw(FM801_REG(chip, PLY_COUNT));
- if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_PLAYBACK) {
+ guard(spinlock)(&chip->reg_lock);
+ ptr = chip->ply_pos + (chip->ply_count - 1) - fm801_readw(chip, PLY_COUNT);
+ if (fm801_readw(chip, IRQ_STATUS) & FM801_IRQ_PLAYBACK) {
ptr += chip->ply_count;
ptr %= chip->ply_size;
}
- spin_unlock(&chip->reg_lock);
return bytes_to_frames(substream->runtime, ptr);
}
@@ -512,13 +533,12 @@ static snd_pcm_uframes_t snd_fm801_capture_pointer(struct snd_pcm_substream *sub
if (!(chip->cap_ctrl & FM801_START))
return 0;
- spin_lock(&chip->reg_lock);
- ptr = chip->cap_pos + (chip->cap_count - 1) - inw(FM801_REG(chip, CAP_COUNT));
- if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_CAPTURE) {
+ guard(spinlock)(&chip->reg_lock);
+ ptr = chip->cap_pos + (chip->cap_count - 1) - fm801_readw(chip, CAP_COUNT);
+ if (fm801_readw(chip, IRQ_STATUS) & FM801_IRQ_CAPTURE) {
ptr += chip->cap_count;
ptr %= chip->cap_size;
}
- spin_unlock(&chip->reg_lock);
return bytes_to_frames(substream->runtime, ptr);
}
@@ -528,49 +548,50 @@ static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id)
unsigned short status;
unsigned int tmp;
- status = inw(FM801_REG(chip, IRQ_STATUS));
+ status = fm801_readw(chip, IRQ_STATUS);
status &= FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU|FM801_IRQ_VOLUME;
if (! status)
return IRQ_NONE;
/* ack first */
- outw(status, FM801_REG(chip, IRQ_STATUS));
+ fm801_writew(chip, IRQ_STATUS, status);
if (chip->pcm && (status & FM801_IRQ_PLAYBACK) && chip->playback_substream) {
- spin_lock(&chip->reg_lock);
- chip->ply_buf++;
- chip->ply_pos += chip->ply_count;
- chip->ply_pos %= chip->ply_size;
- tmp = chip->ply_pos + chip->ply_count;
- tmp %= chip->ply_size;
- outl(chip->ply_buffer + tmp,
- (chip->ply_buf & 1) ?
- FM801_REG(chip, PLY_BUF1) :
- FM801_REG(chip, PLY_BUF2));
- spin_unlock(&chip->reg_lock);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ chip->ply_buf++;
+ chip->ply_pos += chip->ply_count;
+ chip->ply_pos %= chip->ply_size;
+ tmp = chip->ply_pos + chip->ply_count;
+ tmp %= chip->ply_size;
+ if (chip->ply_buf & 1)
+ fm801_writel(chip, PLY_BUF1, chip->ply_buffer + tmp);
+ else
+ fm801_writel(chip, PLY_BUF2, chip->ply_buffer + tmp);
+ }
snd_pcm_period_elapsed(chip->playback_substream);
}
if (chip->pcm && (status & FM801_IRQ_CAPTURE) && chip->capture_substream) {
- spin_lock(&chip->reg_lock);
- chip->cap_buf++;
- chip->cap_pos += chip->cap_count;
- chip->cap_pos %= chip->cap_size;
- tmp = chip->cap_pos + chip->cap_count;
- tmp %= chip->cap_size;
- outl(chip->cap_buffer + tmp,
- (chip->cap_buf & 1) ?
- FM801_REG(chip, CAP_BUF1) :
- FM801_REG(chip, CAP_BUF2));
- spin_unlock(&chip->reg_lock);
+ scoped_guard(spinlock, &chip->reg_lock) {
+ chip->cap_buf++;
+ chip->cap_pos += chip->cap_count;
+ chip->cap_pos %= chip->cap_size;
+ tmp = chip->cap_pos + chip->cap_count;
+ tmp %= chip->cap_size;
+ if (chip->cap_buf & 1)
+ fm801_writel(chip, CAP_BUF1, chip->cap_buffer + tmp);
+ else
+ fm801_writel(chip, CAP_BUF2, chip->cap_buffer + tmp);
+ }
snd_pcm_period_elapsed(chip->capture_substream);
}
if (chip->rmidi && (status & FM801_IRQ_MPU))
snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
- if (status & FM801_IRQ_VOLUME)
- ;/* TODO */
+ if (status & FM801_IRQ_VOLUME) {
+ /* TODO */
+ }
return IRQ_HANDLED;
}
-static struct snd_pcm_hardware snd_fm801_playback =
+static const struct snd_pcm_hardware snd_fm801_playback =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -590,7 +611,7 @@ static struct snd_pcm_hardware snd_fm801_playback =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_fm801_capture =
+static const struct snd_pcm_hardware snd_fm801_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -626,7 +647,8 @@ static int snd_fm801_playback_open(struct snd_pcm_substream *substream)
SNDRV_PCM_HW_PARAM_CHANNELS,
&hw_constraints_channels);
}
- if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
return err;
return 0;
}
@@ -641,7 +663,8 @@ static int snd_fm801_capture_open(struct snd_pcm_substream *substream)
runtime->hw = snd_fm801_capture;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&hw_constraints_rates);
- if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
return err;
return 0;
}
@@ -662,36 +685,30 @@ static int snd_fm801_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_fm801_playback_ops = {
+static const struct snd_pcm_ops snd_fm801_playback_ops = {
.open = snd_fm801_playback_open,
.close = snd_fm801_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_fm801_hw_params,
- .hw_free = snd_fm801_hw_free,
.prepare = snd_fm801_playback_prepare,
.trigger = snd_fm801_playback_trigger,
.pointer = snd_fm801_playback_pointer,
};
-static struct snd_pcm_ops snd_fm801_capture_ops = {
+static const struct snd_pcm_ops snd_fm801_capture_ops = {
.open = snd_fm801_capture_open,
.close = snd_fm801_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_fm801_hw_params,
- .hw_free = snd_fm801_hw_free,
.prepare = snd_fm801_capture_prepare,
.trigger = snd_fm801_capture_trigger,
.pointer = snd_fm801_capture_pointer,
};
-static int __devinit snd_fm801_pcm(struct fm801 *chip, int device, struct snd_pcm ** rpcm)
+static int snd_fm801_pcm(struct fm801 *chip, int device)
{
+ struct pci_dev *pdev = to_pci_dev(chip->dev);
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
- if ((err = snd_pcm_new(chip->card, "FM801", device, 1, 1, &pcm)) < 0)
+ err = snd_pcm_new(chip->card, "FM801", device, 1, 1, &pcm);
+ if (err < 0)
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_fm801_playback_ops);
@@ -699,326 +716,113 @@ static int __devinit snd_fm801_pcm(struct fm801 *chip, int device, struct snd_pc
pcm->private_data = chip;
pcm->info_flags = 0;
- strcpy(pcm->name, "FM801");
+ strscpy(pcm->name, "FM801");
chip->pcm = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- chip->multichannel ? 128*1024 : 64*1024, 128*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &pdev->dev,
+ chip->multichannel ? 128*1024 : 64*1024, 128*1024);
- if (rpcm)
- *rpcm = pcm;
- return 0;
+ return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps,
+ chip->multichannel ? 6 : 2, 0,
+ NULL);
}
/*
* TEA5757 radio
*/
-#ifdef TEA575X_RADIO
-
-/* 256PCS GPIO numbers */
-#define TEA_256PCS_DATA 1
-#define TEA_256PCS_WRITE_ENABLE 2 /* inverted */
-#define TEA_256PCS_BUS_CLOCK 3
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
-static void snd_fm801_tea575x_256pcs_write(struct snd_tea575x *tea, unsigned int val)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
- int i = 25;
+/* GPIO to TEA575x maps */
+struct snd_fm801_tea575x_gpio {
+ u8 data, clk, wren, most;
+ char *name;
+};
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines and set write enable bit */
- reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
- FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK);
- /* all of lines are in the write direction */
- /* clear data and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCS_DATA) |
- FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCS_DATA) |
- FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE));
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- while (i--) {
- if (val & (1 << i))
- reg |= FM801_GPIO_GP(TEA_256PCS_DATA);
- else
- reg &= ~FM801_GPIO_GP(TEA_256PCS_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- }
+static const struct snd_fm801_tea575x_gpio snd_fm801_tea575x_gpios[] = {
+ { .data = 1, .clk = 3, .wren = 2, .most = 0, .name = "SF256-PCS" },
+ { .data = 1, .clk = 0, .wren = 2, .most = 3, .name = "SF256-PCP" },
+ { .data = 2, .clk = 0, .wren = 1, .most = 3, .name = "SF64-PCR" },
+};
- /* and reset the write enable bit */
- reg |= FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GP(TEA_256PCS_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- spin_unlock_irq(&chip->reg_lock);
-}
+#define get_tea575x_gpio(chip) \
+ (&snd_fm801_tea575x_gpios[((chip)->tea575x_tuner & TUNER_TYPE_MASK) - 1])
-static unsigned int snd_fm801_tea575x_256pcs_read(struct snd_tea575x *tea)
+static void snd_fm801_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
{
struct fm801 *chip = tea->private_data;
- unsigned short reg;
- unsigned int val = 0;
- int i;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines, set data direction to input */
- reg |= FM801_GPIO_GS(TEA_256PCS_DATA) |
- FM801_GPIO_GS(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GD(TEA_256PCS_DATA) |
- FM801_GPIO_GP(TEA_256PCS_DATA) |
- FM801_GPIO_GP(TEA_256PCS_WRITE_ENABLE);
- /* all of lines are in the write direction, except data */
- /* clear data, write enable and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCS_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCS_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK));
-
- for (i = 0; i < 24; i++) {
- reg &= ~FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCS_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- val <<= 1;
- if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCS_DATA))
- val |= 1;
- }
+ unsigned short reg = fm801_readw(chip, GPIO_CTRL);
+ struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
- spin_unlock_irq(&chip->reg_lock);
+ reg &= ~(FM801_GPIO_GP(gpio.data) |
+ FM801_GPIO_GP(gpio.clk) |
+ FM801_GPIO_GP(gpio.wren));
- return val;
-}
+ reg |= (pins & TEA575X_DATA) ? FM801_GPIO_GP(gpio.data) : 0;
+ reg |= (pins & TEA575X_CLK) ? FM801_GPIO_GP(gpio.clk) : 0;
+ /* WRITE_ENABLE is inverted */
+ reg |= (pins & TEA575X_WREN) ? 0 : FM801_GPIO_GP(gpio.wren);
-/* 256PCPR GPIO numbers */
-#define TEA_256PCPR_BUS_CLOCK 0
-#define TEA_256PCPR_DATA 1
-#define TEA_256PCPR_WRITE_ENABLE 2 /* inverted */
-
-static void snd_fm801_tea575x_256pcpr_write(struct snd_tea575x *tea, unsigned int val)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
- int i = 25;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines and set write enable bit */
- reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) |
- FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK);
- /* all of lines are in the write direction */
- /* clear data and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCPR_DATA) |
- FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCPR_DATA) |
- FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE));
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- while (i--) {
- if (val & (1 << i))
- reg |= FM801_GPIO_GP(TEA_256PCPR_DATA);
- else
- reg &= ~FM801_GPIO_GP(TEA_256PCPR_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- }
-
- /* and reset the write enable bit */
- reg |= FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GP(TEA_256PCPR_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- spin_unlock_irq(&chip->reg_lock);
+ fm801_writew(chip, GPIO_CTRL, reg);
}
-static unsigned int snd_fm801_tea575x_256pcpr_read(struct snd_tea575x *tea)
+static u8 snd_fm801_tea575x_get_pins(struct snd_tea575x *tea)
{
struct fm801 *chip = tea->private_data;
- unsigned short reg;
- unsigned int val = 0;
- int i;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines, set data direction to input */
- reg |= FM801_GPIO_GS(TEA_256PCPR_DATA) |
- FM801_GPIO_GS(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GD(TEA_256PCPR_DATA) |
- FM801_GPIO_GP(TEA_256PCPR_DATA) |
- FM801_GPIO_GP(TEA_256PCPR_WRITE_ENABLE);
- /* all of lines are in the write direction, except data */
- /* clear data, write enable and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_256PCPR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_256PCPR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK));
-
- for (i = 0; i < 24; i++) {
- reg &= ~FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_256PCPR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- val <<= 1;
- if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_256PCPR_DATA))
- val |= 1;
- }
-
- spin_unlock_irq(&chip->reg_lock);
-
- return val;
+ unsigned short reg = fm801_readw(chip, GPIO_CTRL);
+ struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
+ u8 ret;
+
+ ret = 0;
+ if (reg & FM801_GPIO_GP(gpio.data))
+ ret |= TEA575X_DATA;
+ if (reg & FM801_GPIO_GP(gpio.most))
+ ret |= TEA575X_MOST;
+ return ret;
}
-/* 64PCR GPIO numbers */
-#define TEA_64PCR_BUS_CLOCK 0
-#define TEA_64PCR_WRITE_ENABLE 1 /* inverted */
-#define TEA_64PCR_DATA 2
-
-static void snd_fm801_tea575x_64pcr_write(struct snd_tea575x *tea, unsigned int val)
+static void snd_fm801_tea575x_set_direction(struct snd_tea575x *tea, bool output)
{
struct fm801 *chip = tea->private_data;
- unsigned short reg;
- int i = 25;
+ unsigned short reg = fm801_readw(chip, GPIO_CTRL);
+ struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
/* use GPIO lines and set write enable bit */
- reg |= FM801_GPIO_GS(TEA_64PCR_DATA) |
- FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK);
- /* all of lines are in the write direction */
- /* clear data and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_64PCR_DATA) |
- FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_64PCR_DATA) |
- FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE));
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- while (i--) {
- if (val & (1 << i))
- reg |= FM801_GPIO_GP(TEA_64PCR_DATA);
- else
- reg &= ~FM801_GPIO_GP(TEA_64PCR_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- }
-
- /* and reset the write enable bit */
- reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GP(TEA_64PCR_DATA);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- spin_unlock_irq(&chip->reg_lock);
-}
-
-static unsigned int snd_fm801_tea575x_64pcr_read(struct snd_tea575x *tea)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
- unsigned int val = 0;
- int i;
-
- spin_lock_irq(&chip->reg_lock);
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- /* use GPIO lines, set data direction to input */
- reg |= FM801_GPIO_GS(TEA_64PCR_DATA) |
- FM801_GPIO_GS(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GS(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GD(TEA_64PCR_DATA) |
- FM801_GPIO_GP(TEA_64PCR_DATA) |
- FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
- /* all of lines are in the write direction, except data */
- /* clear data, write enable and clock lines */
- reg &= ~(FM801_GPIO_GD(TEA_64PCR_WRITE_ENABLE) |
- FM801_GPIO_GD(TEA_64PCR_BUS_CLOCK) |
- FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK));
-
- for (i = 0; i < 24; i++) {
- reg &= ~FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- reg |= FM801_GPIO_GP(TEA_64PCR_BUS_CLOCK);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
- val <<= 1;
- if (inw(FM801_REG(chip, GPIO_CTRL)) & FM801_GPIO_GP(TEA_64PCR_DATA))
- val |= 1;
+ reg |= FM801_GPIO_GS(gpio.data) |
+ FM801_GPIO_GS(gpio.wren) |
+ FM801_GPIO_GS(gpio.clk) |
+ FM801_GPIO_GS(gpio.most);
+ if (output) {
+ /* all of lines are in the write direction */
+ /* clear data and clock lines */
+ reg &= ~(FM801_GPIO_GD(gpio.data) |
+ FM801_GPIO_GD(gpio.wren) |
+ FM801_GPIO_GD(gpio.clk) |
+ FM801_GPIO_GP(gpio.data) |
+ FM801_GPIO_GP(gpio.clk) |
+ FM801_GPIO_GP(gpio.wren));
+ } else {
+ /* use GPIO lines, set data direction to input */
+ reg |= FM801_GPIO_GD(gpio.data) |
+ FM801_GPIO_GD(gpio.most) |
+ FM801_GPIO_GP(gpio.data) |
+ FM801_GPIO_GP(gpio.most) |
+ FM801_GPIO_GP(gpio.wren);
+ /* all of lines are in the write direction, except data */
+ /* clear data, write enable and clock lines */
+ reg &= ~(FM801_GPIO_GD(gpio.wren) |
+ FM801_GPIO_GD(gpio.clk) |
+ FM801_GPIO_GP(gpio.clk));
}
- spin_unlock_irq(&chip->reg_lock);
-
- return val;
+ fm801_writew(chip, GPIO_CTRL, reg);
}
-static void snd_fm801_tea575x_64pcr_mute(struct snd_tea575x *tea,
- unsigned int mute)
-{
- struct fm801 *chip = tea->private_data;
- unsigned short reg;
-
- spin_lock_irq(&chip->reg_lock);
-
- reg = inw(FM801_REG(chip, GPIO_CTRL));
- if (mute)
- /* 0xf800 (mute) */
- reg &= ~FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
- else
- /* 0xf802 (unmute) */
- reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
- outw(reg, FM801_REG(chip, GPIO_CTRL));
- udelay(1);
-
- spin_unlock_irq(&chip->reg_lock);
-}
-
-static struct snd_tea575x_ops snd_fm801_tea_ops[3] = {
- {
- /* 1 = MediaForte 256-PCS */
- .write = snd_fm801_tea575x_256pcs_write,
- .read = snd_fm801_tea575x_256pcs_read,
- },
- {
- /* 2 = MediaForte 256-PCPR */
- .write = snd_fm801_tea575x_256pcpr_write,
- .read = snd_fm801_tea575x_256pcpr_read,
- },
- {
- /* 3 = MediaForte 64-PCR */
- .write = snd_fm801_tea575x_64pcr_write,
- .read = snd_fm801_tea575x_64pcr_read,
- .mute = snd_fm801_tea575x_64pcr_mute,
- }
+static const struct snd_tea575x_ops snd_fm801_tea_ops = {
+ .set_pins = snd_fm801_tea575x_set_pins,
+ .get_pins = snd_fm801_tea575x_get_pins,
+ .set_direction = snd_fm801_tea575x_set_direction,
};
#endif
@@ -1051,10 +855,11 @@ static int snd_fm801_get_single(struct snd_kcontrol *kcontrol,
int shift = (kcontrol->private_value >> 8) & 0xff;
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
+ long *value = ucontrol->value.integer.value;
- ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift) & mask;
+ value[0] = (fm801_ioread16(chip, reg) >> shift) & mask;
if (invert)
- ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+ value[0] = mask - value[0];
return 0;
}
@@ -1107,14 +912,14 @@ static int snd_fm801_get_double(struct snd_kcontrol *kcontrol,
int shift_right = (kcontrol->private_value >> 12) & 0x0f;
int mask = (kcontrol->private_value >> 16) & 0xff;
int invert = (kcontrol->private_value >> 24) & 0xff;
+ long *value = ucontrol->value.integer.value;
- spin_lock_irq(&chip->reg_lock);
- ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift_left) & mask;
- ucontrol->value.integer.value[1] = (inw(chip->port + reg) >> shift_right) & mask;
- spin_unlock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
+ value[0] = (fm801_ioread16(chip, reg) >> shift_left) & mask;
+ value[1] = (fm801_ioread16(chip, reg) >> shift_right) & mask;
if (invert) {
- ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
- ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+ value[0] = mask - value[0];
+ value[1] = mask - value[1];
}
return 0;
}
@@ -1144,17 +949,11 @@ static int snd_fm801_put_double(struct snd_kcontrol *kcontrol,
static int snd_fm801_info_mux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[5] = {
+ static const char * const texts[5] = {
"AC97 Primary", "FM", "I2S", "PCM", "AC97 Secondary"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 5;
- if (uinfo->value.enumerated.item > 4)
- uinfo->value.enumerated.item = 4;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 5, texts);
}
static int snd_fm801_get_mux(struct snd_kcontrol *kcontrol,
@@ -1163,7 +962,7 @@ static int snd_fm801_get_mux(struct snd_kcontrol *kcontrol,
struct fm801 *chip = snd_kcontrol_chip(kcontrol);
unsigned short val;
- val = inw(FM801_REG(chip, REC_SRC)) & 7;
+ val = fm801_readw(chip, REC_SRC) & 7;
if (val > 4)
val = 4;
ucontrol->value.enumerated.item[0] = val;
@@ -1176,7 +975,8 @@ static int snd_fm801_put_mux(struct snd_kcontrol *kcontrol,
struct fm801 *chip = snd_kcontrol_chip(kcontrol);
unsigned short val;
- if ((val = ucontrol->value.enumerated.item[0]) > 4)
+ val = ucontrol->value.enumerated.item[0];
+ if (val > 4)
return -EINVAL;
return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val);
}
@@ -1185,7 +985,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_dsp, -3450, 150, 0);
#define FM801_CONTROLS ARRAY_SIZE(snd_fm801_controls)
-static struct snd_kcontrol_new snd_fm801_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_fm801_controls[] = {
FM801_DOUBLE_TLV("Wave Playback Volume", FM801_PCM_VOL, 0, 8, 31, 1,
db_scale_dsp),
FM801_SINGLE("Wave Playback Switch", FM801_PCM_VOL, 15, 1, 1),
@@ -1206,7 +1006,7 @@ FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1),
#define FM801_CONTROLS_MULTI ARRAY_SIZE(snd_fm801_controls_multi)
-static struct snd_kcontrol_new snd_fm801_controls_multi[] __devinitdata = {
+static const struct snd_kcontrol_new snd_fm801_controls_multi[] = {
FM801_SINGLE("AC97 2ch->4ch Copy Switch", FM801_CODEC_CTRL, 7, 1, 0),
FM801_SINGLE("AC97 18-bit Switch", FM801_CODEC_CTRL, 10, 1, 0),
FM801_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), FM801_I2S_MODE, 8, 1, 0),
@@ -1215,52 +1015,45 @@ FM801_SINGLE(SNDRV_CTL_NAME_IEC958("Raw Data ",CAPTURE,SWITCH), FM801_I2S_MODE,
FM801_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), FM801_GEN_CTRL, 2, 1, 0),
};
-static void snd_fm801_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
-{
- struct fm801 *chip = bus->private_data;
- chip->ac97_bus = NULL;
-}
-
-static void snd_fm801_mixer_free_ac97(struct snd_ac97 *ac97)
-{
- struct fm801 *chip = ac97->private_data;
- if (ac97->num == 0) {
- chip->ac97 = NULL;
- } else {
- chip->ac97_sec = NULL;
- }
-}
-
-static int __devinit snd_fm801_mixer(struct fm801 *chip)
+static int snd_fm801_mixer(struct fm801 *chip)
{
struct snd_ac97_template ac97;
unsigned int i;
int err;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_fm801_codec_write,
.read = snd_fm801_codec_read,
};
- if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus);
+ if (err < 0)
return err;
- chip->ac97_bus->private_free = snd_fm801_mixer_free_ac97_bus;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
- ac97.private_free = snd_fm801_mixer_free_ac97;
- if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
+ err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97);
+ if (err < 0)
return err;
if (chip->secondary) {
ac97.num = 1;
ac97.addr = chip->secondary_addr;
- if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_sec)) < 0)
+ err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_sec);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0; i < FM801_CONTROLS; i++) {
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&snd_fm801_controls[i], chip));
+ if (err < 0)
return err;
}
- for (i = 0; i < FM801_CONTROLS; i++)
- snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls[i], chip));
if (chip->multichannel) {
- for (i = 0; i < FM801_CONTROLS_MULTI; i++)
- snd_ctl_add(chip->card, snd_ctl_new1(&snd_fm801_controls_multi[i], chip));
+ for (i = 0; i < FM801_CONTROLS_MULTI; i++) {
+ err = snd_ctl_add(chip->card,
+ snd_ctl_new1(&snd_fm801_controls_multi[i], chip));
+ if (err < 0)
+ return err;
+ }
}
return 0;
}
@@ -1274,38 +1067,32 @@ static int wait_for_codec(struct fm801 *chip, unsigned int codec_id,
{
unsigned long timeout = jiffies + waits;
- outw(FM801_AC97_READ | (codec_id << FM801_AC97_ADDR_SHIFT) | reg,
- FM801_REG(chip, AC97_CMD));
+ fm801_writew(chip, AC97_CMD,
+ reg | (codec_id << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ);
udelay(5);
do {
- if ((inw(FM801_REG(chip, AC97_CMD)) & (FM801_AC97_VALID|FM801_AC97_BUSY))
- == FM801_AC97_VALID)
+ if ((fm801_readw(chip, AC97_CMD) &
+ (FM801_AC97_VALID | FM801_AC97_BUSY)) == FM801_AC97_VALID)
return 0;
schedule_timeout_uninterruptible(1);
} while (time_after(timeout, jiffies));
return -EIO;
}
-static int snd_fm801_chip_init(struct fm801 *chip, int resume)
+static int reset_codec(struct fm801 *chip)
{
- unsigned short cmdw;
-
- if (chip->tea575x_tuner & TUNER_ONLY)
- goto __ac97_ok;
-
/* codec cold reset + AC'97 warm reset */
- outw((1<<5) | (1<<6), FM801_REG(chip, CODEC_CTRL));
- inw(FM801_REG(chip, CODEC_CTRL)); /* flush posting data */
+ fm801_writew(chip, CODEC_CTRL, (1 << 5) | (1 << 6));
+ fm801_readw(chip, CODEC_CTRL); /* flush posting data */
udelay(100);
- outw(0, FM801_REG(chip, CODEC_CTRL));
+ fm801_writew(chip, CODEC_CTRL, 0);
- if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0)
- if (!resume) {
- snd_printk(KERN_INFO "Primary AC'97 codec not found, "
- "assume SF64-PCR (tuner-only)\n");
- chip->tea575x_tuner = 3 | TUNER_ONLY;
- goto __ac97_ok;
- }
+ return wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750));
+}
+
+static void snd_fm801_chip_multichannel_init(struct fm801 *chip)
+{
+ unsigned short cmdw;
if (chip->multichannel) {
if (chip->secondary_addr) {
@@ -1318,7 +1105,7 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
for (i = 3; i > 0; i--) {
if (!wait_for_codec(chip, i, AC97_VENDOR_ID1,
msecs_to_jiffies(50))) {
- cmdw = inw(FM801_REG(chip, AC97_DATA));
+ cmdw = fm801_readw(chip, AC97_DATA);
if (cmdw != 0xffff && cmdw != 0) {
chip->secondary = 1;
chip->secondary_addr = i;
@@ -1332,142 +1119,144 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
/* cause timeout problems */
wait_for_codec(chip, 0, AC97_VENDOR_ID1, msecs_to_jiffies(750));
}
+}
- __ac97_ok:
+static void snd_fm801_chip_init(struct fm801 *chip)
+{
+ unsigned short cmdw;
/* init volume */
- outw(0x0808, FM801_REG(chip, PCM_VOL));
- outw(0x9f1f, FM801_REG(chip, FM_VOL));
- outw(0x8808, FM801_REG(chip, I2S_VOL));
+ fm801_writew(chip, PCM_VOL, 0x0808);
+ fm801_writew(chip, FM_VOL, 0x9f1f);
+ fm801_writew(chip, I2S_VOL, 0x8808);
/* I2S control - I2S mode */
- outw(0x0003, FM801_REG(chip, I2S_MODE));
+ fm801_writew(chip, I2S_MODE, 0x0003);
/* interrupt setup */
- cmdw = inw(FM801_REG(chip, IRQ_MASK));
+ cmdw = fm801_readw(chip, IRQ_MASK);
if (chip->irq < 0)
cmdw |= 0x00c3; /* mask everything, no PCM nor MPU */
else
cmdw &= ~0x0083; /* unmask MPU, PLAYBACK & CAPTURE */
- outw(cmdw, FM801_REG(chip, IRQ_MASK));
+ fm801_writew(chip, IRQ_MASK, cmdw);
/* interrupt clear */
- outw(FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS));
-
- return 0;
+ fm801_writew(chip, IRQ_STATUS,
+ FM801_IRQ_PLAYBACK | FM801_IRQ_CAPTURE | FM801_IRQ_MPU);
}
-
-static int snd_fm801_free(struct fm801 *chip)
+static void snd_fm801_free(struct snd_card *card)
{
+ struct fm801 *chip = card->private_data;
unsigned short cmdw;
- if (chip->irq < 0)
- goto __end_hw;
-
/* interrupt setup - mask everything */
- cmdw = inw(FM801_REG(chip, IRQ_MASK));
+ cmdw = fm801_readw(chip, IRQ_MASK);
cmdw |= 0x00c3;
- outw(cmdw, FM801_REG(chip, IRQ_MASK));
+ fm801_writew(chip, IRQ_MASK, cmdw);
- __end_hw:
-#ifdef TEA575X_RADIO
- snd_tea575x_exit(&chip->tea);
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
+ if (!(chip->tea575x_tuner & TUNER_DISABLED)) {
+ snd_tea575x_exit(&chip->tea);
+ v4l2_device_unregister(&chip->v4l2_dev);
+ }
#endif
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
-
- kfree(chip);
- return 0;
}
-static int snd_fm801_dev_free(struct snd_device *device)
+static int snd_fm801_create(struct snd_card *card,
+ struct pci_dev *pci,
+ int tea575x_tuner,
+ int radio_nr)
{
- struct fm801 *chip = device->device_data;
- return snd_fm801_free(chip);
-}
-
-static int __devinit snd_fm801_create(struct snd_card *card,
- struct pci_dev * pci,
- int tea575x_tuner,
- struct fm801 ** rchip)
-{
- struct fm801 *chip;
+ struct fm801 *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_fm801_dev_free,
- };
- *rchip = NULL;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
spin_lock_init(&chip->reg_lock);
chip->card = card;
- chip->pci = pci;
+ chip->dev = &pci->dev;
chip->irq = -1;
chip->tea575x_tuner = tea575x_tuner;
- if ((err = pci_request_regions(pci, "FM801")) < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pcim_request_all_regions(pci, "FM801");
+ if (err < 0)
return err;
- }
chip->port = pci_resource_start(pci, 0);
- if ((tea575x_tuner & TUNER_ONLY) == 0) {
- if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_SHARED,
- "FM801", chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq);
- snd_fm801_free(chip);
- return -EBUSY;
- }
- chip->irq = pci->irq;
- pci_set_master(pci);
- }
if (pci->revision >= 0xb1) /* FM801-AU */
chip->multichannel = 1;
- snd_fm801_chip_init(chip, 0);
- /* init might set tuner access method */
- tea575x_tuner = chip->tea575x_tuner;
+ if (!(chip->tea575x_tuner & TUNER_ONLY)) {
+ if (reset_codec(chip) < 0) {
+ dev_info(chip->card->dev,
+ "Primary AC'97 codec not found, assume SF64-PCR (tuner-only)\n");
+ chip->tea575x_tuner = 3 | TUNER_ONLY;
+ } else {
+ snd_fm801_chip_multichannel_init(chip);
+ }
+ }
- if (chip->irq >= 0 && (tea575x_tuner & TUNER_ONLY)) {
- pci_clear_master(pci);
- free_irq(chip->irq, chip);
- chip->irq = -1;
+ if ((chip->tea575x_tuner & TUNER_ONLY) == 0) {
+ if (devm_request_irq(&pci->dev, pci->irq, snd_fm801_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+ pci_set_master(pci);
}
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_fm801_free(chip);
+ card->private_free = snd_fm801_free;
+ snd_fm801_chip_init(chip);
+
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
+ err = v4l2_device_register(&pci->dev, &chip->v4l2_dev);
+ if (err < 0)
return err;
- }
+ chip->tea.v4l2_dev = &chip->v4l2_dev;
+ chip->tea.radio_nr = radio_nr;
+ chip->tea.private_data = chip;
+ chip->tea.ops = &snd_fm801_tea_ops;
+ sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
+ if ((chip->tea575x_tuner & TUNER_TYPE_MASK) > 0 &&
+ (chip->tea575x_tuner & TUNER_TYPE_MASK) < 4) {
+ if (snd_tea575x_init(&chip->tea, THIS_MODULE)) {
+ dev_err(card->dev, "TEA575x radio not found\n");
+ return -ENODEV;
+ }
+ } else if ((chip->tea575x_tuner & TUNER_TYPE_MASK) == 0) {
+ unsigned int tuner_only = chip->tea575x_tuner & TUNER_ONLY;
+
+ /* autodetect tuner connection */
+ for (tea575x_tuner = 1; tea575x_tuner <= 3; tea575x_tuner++) {
+ chip->tea575x_tuner = tea575x_tuner;
+ if (!snd_tea575x_init(&chip->tea, THIS_MODULE)) {
+ dev_info(card->dev,
+ "detected TEA575x radio type %s\n",
+ get_tea575x_gpio(chip)->name);
+ break;
+ }
+ }
+ if (tea575x_tuner == 4) {
+ dev_err(card->dev, "TEA575x radio not found\n");
+ chip->tea575x_tuner = TUNER_DISABLED;
+ }
- snd_card_set_dev(card, &pci->dev);
-
-#ifdef TEA575X_RADIO
- if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 &&
- (tea575x_tuner & TUNER_TYPE_MASK) < 4) {
- chip->tea.dev_nr = tea575x_tuner >> 16;
- chip->tea.card = card;
- chip->tea.freq_fixup = 10700;
- chip->tea.private_data = chip;
- chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & TUNER_TYPE_MASK) - 1];
- snd_tea575x_init(&chip->tea);
+ chip->tea575x_tuner |= tuner_only;
+ }
+ if (!(chip->tea575x_tuner & TUNER_DISABLED)) {
+ strscpy(chip->tea.card, get_tea575x_gpio(chip)->name,
+ sizeof(chip->tea.card));
}
#endif
-
- *rchip = chip;
return 0;
}
-static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_card_fm801_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
@@ -1482,17 +1271,17 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
- if ((err = snd_fm801_create(card, pci, tea575x_tuner[dev], &chip)) < 0) {
- snd_card_free(card);
+ chip = card->private_data;
+ err = snd_fm801_create(card, pci, tea575x_tuner[dev], radio_nr[dev]);
+ if (err < 0)
return err;
- }
- card->private_data = chip;
- strcpy(card->driver, "FM801");
- strcpy(card->shortname, "ForteMedia FM801-");
+ strscpy(card->driver, "FM801");
+ strscpy(card->shortname, "ForteMedia FM801-");
strcat(card->shortname, chip->multichannel ? "AU" : "AS");
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, chip->port, chip->irq);
@@ -1500,123 +1289,108 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
if (chip->tea575x_tuner & TUNER_ONLY)
goto __fm801_tuner_only;
- if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_fm801_pcm(chip, 0);
+ if (err < 0)
return err;
- }
- if ((err = snd_fm801_mixer(chip)) < 0) {
- snd_card_free(card);
+ err = snd_fm801_mixer(chip);
+ if (err < 0)
return err;
- }
- if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801,
- FM801_REG(chip, MPU401_DATA),
- MPU401_INFO_INTEGRATED,
- chip->irq, 0, &chip->rmidi)) < 0) {
- snd_card_free(card);
+ err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801,
+ chip->port + FM801_MPU401_DATA,
+ MPU401_INFO_INTEGRATED |
+ MPU401_INFO_IRQ_HOOK,
+ -1, &chip->rmidi);
+ if (err < 0)
return err;
- }
- if ((err = snd_opl3_create(card, FM801_REG(chip, OPL3_BANK0),
- FM801_REG(chip, OPL3_BANK1),
- OPL3_HW_OPL3_FM801, 1, &opl3)) < 0) {
- snd_card_free(card);
+ err = snd_opl3_create(card, chip->port + FM801_OPL3_BANK0,
+ chip->port + FM801_OPL3_BANK1,
+ OPL3_HW_OPL3_FM801, 1, &opl3);
+ if (err < 0)
return err;
- }
- if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
- snd_card_free(card);
+ err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+ if (err < 0)
return err;
- }
__fm801_tuner_only:
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_card_fm801_remove(struct pci_dev *pci)
+static int snd_card_fm801_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_card_fm801_probe(pci, pci_id));
}
-#ifdef CONFIG_PM
-static unsigned char saved_regs[] = {
+static const unsigned char saved_regs[] = {
FM801_PCM_VOL, FM801_I2S_VOL, FM801_FM_VOL, FM801_REC_SRC,
FM801_PLY_CTRL, FM801_PLY_COUNT, FM801_PLY_BUF1, FM801_PLY_BUF2,
FM801_CAP_CTRL, FM801_CAP_COUNT, FM801_CAP_BUF1, FM801_CAP_BUF2,
FM801_CODEC_CTRL, FM801_I2S_MODE, FM801_VOLUME, FM801_GEN_CTRL,
};
-static int snd_fm801_suspend(struct pci_dev *pci, pm_message_t state)
+static int snd_fm801_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct fm801 *chip = card->private_data;
int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(chip->pcm);
- snd_ac97_suspend(chip->ac97);
- snd_ac97_suspend(chip->ac97_sec);
+
for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
- chip->saved_regs[i] = inw(chip->port + saved_regs[i]);
- /* FIXME: tea575x suspend */
+ chip->saved_regs[i] = fm801_ioread16(chip, saved_regs[i]);
+
+ if (chip->tea575x_tuner & TUNER_ONLY) {
+ /* FIXME: tea575x suspend */
+ } else {
+ snd_ac97_suspend(chip->ac97);
+ snd_ac97_suspend(chip->ac97_sec);
+ }
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_fm801_resume(struct pci_dev *pci)
+static int snd_fm801_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct fm801 *chip = card->private_data;
int i;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "fm801: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
+ if (chip->tea575x_tuner & TUNER_ONLY) {
+ snd_fm801_chip_init(chip);
+ } else {
+ reset_codec(chip);
+ snd_fm801_chip_multichannel_init(chip);
+ snd_fm801_chip_init(chip);
+ snd_ac97_resume(chip->ac97);
+ snd_ac97_resume(chip->ac97_sec);
}
- pci_set_master(pci);
- snd_fm801_chip_init(chip, 1);
- snd_ac97_resume(chip->ac97);
- snd_ac97_resume(chip->ac97_sec);
for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
- outw(chip->saved_regs[i], chip->port + saved_regs[i]);
+ fm801_iowrite16(chip, saved_regs[i], chip->saved_regs[i]);
+
+#ifdef CONFIG_SND_FM801_TEA575X_BOOL
+ if (!(chip->tea575x_tuner & TUNER_DISABLED))
+ snd_tea575x_set_freq(&chip->tea);
+#endif
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif
-static struct pci_driver driver = {
- .name = "FM801",
+static DEFINE_SIMPLE_DEV_PM_OPS(snd_fm801_pm, snd_fm801_suspend, snd_fm801_resume);
+
+static struct pci_driver fm801_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_fm801_ids,
.probe = snd_card_fm801_probe,
- .remove = __devexit_p(snd_card_fm801_remove),
-#ifdef CONFIG_PM
- .suspend = snd_fm801_suspend,
- .resume = snd_fm801_resume,
-#endif
+ .driver = {
+ .pm = &snd_fm801_pm,
+ },
};
-static int __init alsa_card_fm801_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_fm801_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_fm801_init)
-module_exit(alsa_card_fm801_exit)
+module_pci_driver(fm801_driver);
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
deleted file mode 100644
index 0ea5cc60ac78..000000000000
--- a/sound/pci/hda/Kconfig
+++ /dev/null
@@ -1,220 +0,0 @@
-menuconfig SND_HDA_INTEL
- tristate "Intel HD Audio"
- select SND_PCM
- select SND_VMASTER
- help
- Say Y here to include support for Intel "High Definition
- Audio" (Azalia) and its compatible devices.
-
- This option enables the HD-audio controller. Don't forget
- to choose the appropriate codec options below.
-
- To compile this driver as a module, choose M here: the module
- will be called snd-hda-intel.
-
-if SND_HDA_INTEL
-
-config SND_HDA_HWDEP
- bool "Build hwdep interface for HD-audio driver"
- select SND_HWDEP
- help
- Say Y here to build a hwdep interface for HD-audio driver.
- This interface can be used for out-of-band communication
- with codecs for debugging purposes.
-
-config SND_HDA_RECONFIG
- bool "Allow dynamic codec reconfiguration (EXPERIMENTAL)"
- depends on SND_HDA_HWDEP && EXPERIMENTAL
- help
- Say Y here to enable the HD-audio codec re-configuration feature.
- This adds the sysfs interfaces to allow user to clear the whole
- codec configuration, change the codec setup, add extra verbs,
- and re-configure the codec dynamically.
-
-config SND_HDA_INPUT_BEEP
- bool "Support digital beep via input layer"
- depends on INPUT=y || INPUT=SND_HDA_INTEL
- help
- Say Y here to build a digital beep interface for HD-audio
- driver. This interface is used to generate digital beeps.
-
-config SND_HDA_INPUT_BEEP_MODE
- int "Digital beep registration mode (0=off, 1=on, 2=mute sw on/off)"
- depends on SND_HDA_INPUT_BEEP=y
- default "1"
- range 0 2
- help
- Set 0 to disable the digital beep interface for HD-audio by default.
- Set 1 to always enable the digital beep interface for HD-audio by
- default. Set 2 to control the beep device registration to input
- layer using a "Beep Switch" in mixer applications.
-
-config SND_HDA_INPUT_JACK
- bool "Support jack plugging notification via input layer"
- depends on INPUT=y || INPUT=SND
- select SND_JACK
- help
- Say Y here to enable the jack plugging notification via
- input layer.
-
-config SND_HDA_PATCH_LOADER
- bool "Support initialization patch loading for HD-audio"
- depends on EXPERIMENTAL
- select FW_LOADER
- select SND_HDA_HWDEP
- select SND_HDA_RECONFIG
- help
- Say Y here to allow the HD-audio driver to load a pseudo
- firmware file ("patch") for overriding the BIOS setup at
- start up. The "patch" file can be specified via patch module
- option, such as patch=hda-init.
-
- This option turns on hwdep and reconfig features automatically.
-
-config SND_HDA_CODEC_REALTEK
- bool "Build Realtek HD-audio codec support"
- default y
- help
- Say Y here to include Realtek HD-audio codec support in
- snd-hda-intel driver, such as ALC880.
-
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-realtek.
- This module is automatically loaded at probing.
-
-config SND_HDA_CODEC_ANALOG
- bool "Build Analog Device HD-audio codec support"
- default y
- help
- Say Y here to include Analog Device HD-audio codec support in
- snd-hda-intel driver, such as AD1986A.
-
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-analog.
- This module is automatically loaded at probing.
-
-config SND_HDA_CODEC_SIGMATEL
- bool "Build IDT/Sigmatel HD-audio codec support"
- default y
- help
- Say Y here to include IDT (Sigmatel) HD-audio codec support in
- snd-hda-intel driver, such as STAC9200.
-
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-idt.
- This module is automatically loaded at probing.
-
-config SND_HDA_CODEC_VIA
- bool "Build VIA HD-audio codec support"
- default y
- help
- Say Y here to include VIA HD-audio codec support in
- snd-hda-intel driver, such as VT1708.
-
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-via.
- This module is automatically loaded at probing.
-
-config SND_HDA_CODEC_HDMI
- bool "Build HDMI/DisplayPort HD-audio codec support"
- select SND_DYNAMIC_MINORS
- default y
- help
- Say Y here to include HDMI and DisplayPort HD-audio codec
- support in snd-hda-intel driver. This includes all AMD/ATI,
- Intel and Nvidia HDMI/DisplayPort codecs.
-
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-hdmi.
- This module is automatically loaded at probing.
-
-config SND_HDA_CODEC_CIRRUS
- bool "Build Cirrus Logic codec support"
- depends on SND_HDA_INTEL
- default y
- help
- Say Y here to include Cirrus Logic codec support in
- snd-hda-intel driver, such as CS4206.
-
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-cirrus.
- This module is automatically loaded at probing.
-
-config SND_HDA_CODEC_CONEXANT
- bool "Build Conexant HD-audio codec support"
- default y
- help
- Say Y here to include Conexant HD-audio codec support in
- snd-hda-intel driver, such as CX20549.
-
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-conexant.
- This module is automatically loaded at probing.
-
-config SND_HDA_CODEC_CA0110
- bool "Build Creative CA0110-IBG codec support"
- depends on SND_HDA_INTEL
- default y
- help
- Say Y here to include Creative CA0110-IBG codec support in
- snd-hda-intel driver, found on some Creative X-Fi cards.
-
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-ca0110.
- This module is automatically loaded at probing.
-
-config SND_HDA_CODEC_CMEDIA
- bool "Build C-Media HD-audio codec support"
- default y
- help
- Say Y here to include C-Media HD-audio codec support in
- snd-hda-intel driver, such as CMI9880.
-
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-cmedia.
- This module is automatically loaded at probing.
-
-config SND_HDA_CODEC_SI3054
- bool "Build Silicon Labs 3054 HD-modem codec support"
- default y
- help
- Say Y here to include Silicon Labs 3054 HD-modem codec
- (and compatibles) support in snd-hda-intel driver.
-
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-si3054.
- This module is automatically loaded at probing.
-
-config SND_HDA_GENERIC
- bool "Enable generic HD-audio codec parser"
- default y
- help
- Say Y here to enable the generic HD-audio codec parser
- in snd-hda-intel driver.
-
-config SND_HDA_POWER_SAVE
- bool "Aggressive power-saving on HD-audio"
- help
- Say Y here to enable more aggressive power-saving mode on
- HD-audio driver. The power-saving timeout can be configured
- via power_save option or over sysfs on-the-fly.
-
-config SND_HDA_POWER_SAVE_DEFAULT
- int "Default time-out for HD-audio power-save mode"
- depends on SND_HDA_POWER_SAVE
- default 0
- help
- The default time-out value in seconds for HD-audio automatic
- power-save mode. 0 means to disable the power-save mode.
-
-endif
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
deleted file mode 100644
index 17ef3658f34b..000000000000
--- a/sound/pci/hda/Makefile
+++ /dev/null
@@ -1,58 +0,0 @@
-snd-hda-intel-objs := hda_intel.o
-
-snd-hda-codec-y := hda_codec.o
-snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
-snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
-snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
-snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
-
-snd-hda-codec-realtek-objs := patch_realtek.o
-snd-hda-codec-cmedia-objs := patch_cmedia.o
-snd-hda-codec-analog-objs := patch_analog.o
-snd-hda-codec-idt-objs := patch_sigmatel.o
-snd-hda-codec-si3054-objs := patch_si3054.o
-snd-hda-codec-cirrus-objs := patch_cirrus.o
-snd-hda-codec-ca0110-objs := patch_ca0110.o
-snd-hda-codec-conexant-objs := patch_conexant.o
-snd-hda-codec-via-objs := patch_via.o
-snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o
-
-# common driver
-obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o
-
-# codec drivers (note: CONFIG_SND_HDA_CODEC_XXX are booleans)
-ifdef CONFIG_SND_HDA_CODEC_REALTEK
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-realtek.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_CMEDIA
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cmedia.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_ANALOG
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-analog.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_SIGMATEL
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-idt.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_SI3054
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-si3054.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_CIRRUS
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_CA0110
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_CONEXANT
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_VIA
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-via.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_HDMI
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-hdmi.o
-endif
-
-# this must be the last entry after codec drivers;
-# otherwise the codec patches won't be hooked before the PCI probe
-# when built in kernel
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
deleted file mode 100644
index 29714c818b53..000000000000
--- a/sound/pci/hda/hda_beep.c
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Digital Beep Input Interface for HD-audio codec
- *
- * Author: Matthew Ranostay <mranostay@embeddedalley.com>
- * Copyright (c) 2008 Embedded Alley Solutions Inc
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/input.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-#include <linux/workqueue.h>
-#include <sound/core.h>
-#include "hda_beep.h"
-#include "hda_local.h"
-
-enum {
- DIGBEEP_HZ_STEP = 46875, /* 46.875 Hz */
- DIGBEEP_HZ_MIN = 93750, /* 93.750 Hz */
- DIGBEEP_HZ_MAX = 12000000, /* 12 KHz */
-};
-
-static void snd_hda_generate_beep(struct work_struct *work)
-{
- struct hda_beep *beep =
- container_of(work, struct hda_beep, beep_work);
- struct hda_codec *codec = beep->codec;
-
- if (!beep->enabled)
- return;
-
- /* generate tone */
- snd_hda_codec_write(codec, beep->nid, 0,
- AC_VERB_SET_BEEP_CONTROL, beep->tone);
-}
-
-/* (non-standard) Linear beep tone calculation for IDT/STAC codecs
- *
- * The tone frequency of beep generator on IDT/STAC codecs is
- * defined from the 8bit tone parameter, in Hz,
- * freq = 48000 * (257 - tone) / 1024
- * that is from 12kHz to 93.75Hz in steps of 46.875 Hz
- */
-static int beep_linear_tone(struct hda_beep *beep, int hz)
-{
- if (hz <= 0)
- return 0;
- hz *= 1000; /* fixed point */
- hz = hz - DIGBEEP_HZ_MIN
- + DIGBEEP_HZ_STEP / 2; /* round to nearest step */
- if (hz < 0)
- hz = 0; /* turn off PC beep*/
- else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
- hz = 1; /* max frequency */
- else {
- hz /= DIGBEEP_HZ_STEP;
- hz = 255 - hz;
- }
- return hz;
-}
-
-/* HD-audio standard beep tone parameter calculation
- *
- * The tone frequency in Hz is calculated as
- * freq = 48000 / (tone * 4)
- * from 47Hz to 12kHz
- */
-static int beep_standard_tone(struct hda_beep *beep, int hz)
-{
- if (hz <= 0)
- return 0; /* disabled */
- hz = 12000 / hz;
- if (hz > 0xff)
- return 0xff;
- if (hz <= 0)
- return 1;
- return hz;
-}
-
-static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
- unsigned int code, int hz)
-{
- struct hda_beep *beep = input_get_drvdata(dev);
-
- switch (code) {
- case SND_BELL:
- if (hz)
- hz = 1000;
- case SND_TONE:
- if (beep->linear_tone)
- beep->tone = beep_linear_tone(beep, hz);
- else
- beep->tone = beep_standard_tone(beep, hz);
- break;
- default:
- return -1;
- }
-
- /* schedule beep event */
- schedule_work(&beep->beep_work);
- return 0;
-}
-
-static void snd_hda_do_detach(struct hda_beep *beep)
-{
- input_unregister_device(beep->dev);
- beep->dev = NULL;
- cancel_work_sync(&beep->beep_work);
- /* turn off beep for sure */
- snd_hda_codec_write(beep->codec, beep->nid, 0,
- AC_VERB_SET_BEEP_CONTROL, 0);
-}
-
-static int snd_hda_do_attach(struct hda_beep *beep)
-{
- struct input_dev *input_dev;
- struct hda_codec *codec = beep->codec;
- int err;
-
- input_dev = input_allocate_device();
- if (!input_dev) {
- printk(KERN_INFO "hda_beep: unable to allocate input device\n");
- return -ENOMEM;
- }
-
- /* setup digital beep device */
- input_dev->name = "HDA Digital PCBeep";
- input_dev->phys = beep->phys;
- input_dev->id.bustype = BUS_PCI;
-
- input_dev->id.vendor = codec->vendor_id >> 16;
- input_dev->id.product = codec->vendor_id & 0xffff;
- input_dev->id.version = 0x01;
-
- input_dev->evbit[0] = BIT_MASK(EV_SND);
- input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
- input_dev->event = snd_hda_beep_event;
- input_dev->dev.parent = &codec->bus->pci->dev;
- input_set_drvdata(input_dev, beep);
-
- err = input_register_device(input_dev);
- if (err < 0) {
- input_free_device(input_dev);
- printk(KERN_INFO "hda_beep: unable to register input device\n");
- return err;
- }
- beep->dev = input_dev;
- return 0;
-}
-
-static void snd_hda_do_register(struct work_struct *work)
-{
- struct hda_beep *beep =
- container_of(work, struct hda_beep, register_work);
-
- mutex_lock(&beep->mutex);
- if (beep->enabled && !beep->dev)
- snd_hda_do_attach(beep);
- mutex_unlock(&beep->mutex);
-}
-
-static void snd_hda_do_unregister(struct work_struct *work)
-{
- struct hda_beep *beep =
- container_of(work, struct hda_beep, unregister_work.work);
-
- mutex_lock(&beep->mutex);
- if (!beep->enabled && beep->dev)
- snd_hda_do_detach(beep);
- mutex_unlock(&beep->mutex);
-}
-
-int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
-{
- struct hda_beep *beep = codec->beep;
- enable = !!enable;
- if (beep == NULL)
- return 0;
- if (beep->enabled != enable) {
- beep->enabled = enable;
- if (!enable) {
- /* turn off beep */
- snd_hda_codec_write(beep->codec, beep->nid, 0,
- AC_VERB_SET_BEEP_CONTROL, 0);
- }
- if (beep->mode == HDA_BEEP_MODE_SWREG) {
- if (enable) {
- cancel_delayed_work(&beep->unregister_work);
- schedule_work(&beep->register_work);
- } else {
- schedule_delayed_work(&beep->unregister_work,
- HZ);
- }
- }
- return 1;
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device);
-
-int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
-{
- struct hda_beep *beep;
-
- if (!snd_hda_get_bool_hint(codec, "beep"))
- return 0; /* disabled explicitly by hints */
- if (codec->beep_mode == HDA_BEEP_MODE_OFF)
- return 0; /* disabled by module option */
-
- beep = kzalloc(sizeof(*beep), GFP_KERNEL);
- if (beep == NULL)
- return -ENOMEM;
- snprintf(beep->phys, sizeof(beep->phys),
- "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
- /* enable linear scale */
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_DIGI_CONVERT_2, 0x01);
-
- beep->nid = nid;
- beep->codec = codec;
- beep->mode = codec->beep_mode;
- codec->beep = beep;
-
- INIT_WORK(&beep->register_work, &snd_hda_do_register);
- INIT_DELAYED_WORK(&beep->unregister_work, &snd_hda_do_unregister);
- INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
- mutex_init(&beep->mutex);
-
- if (beep->mode == HDA_BEEP_MODE_ON) {
- int err = snd_hda_do_attach(beep);
- if (err < 0) {
- kfree(beep);
- codec->beep = NULL;
- return err;
- }
- }
-
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device);
-
-void snd_hda_detach_beep_device(struct hda_codec *codec)
-{
- struct hda_beep *beep = codec->beep;
- if (beep) {
- cancel_work_sync(&beep->register_work);
- cancel_delayed_work(&beep->unregister_work);
- if (beep->dev)
- snd_hda_do_detach(beep);
- codec->beep = NULL;
- kfree(beep);
- }
-}
-EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h
deleted file mode 100644
index f1de1bac042c..000000000000
--- a/sound/pci/hda/hda_beep.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Digital Beep Input Interface for HD-audio codec
- *
- * Author: Matthew Ranostay <mranostay@embeddedalley.com>
- * Copyright (c) 2008 Embedded Alley Solutions Inc
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifndef __SOUND_HDA_BEEP_H
-#define __SOUND_HDA_BEEP_H
-
-#include "hda_codec.h"
-
-#define HDA_BEEP_MODE_OFF 0
-#define HDA_BEEP_MODE_ON 1
-#define HDA_BEEP_MODE_SWREG 2
-
-/* beep information */
-struct hda_beep {
- struct input_dev *dev;
- struct hda_codec *codec;
- unsigned int mode;
- char phys[32];
- int tone;
- hda_nid_t nid;
- unsigned int enabled:1;
- unsigned int request_enable:1;
- unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */
- struct work_struct register_work; /* registration work */
- struct delayed_work unregister_work; /* unregistration work */
- struct work_struct beep_work; /* scheduled task for beep event */
- struct mutex mutex;
-};
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-int snd_hda_enable_beep_device(struct hda_codec *codec, int enable);
-int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
-void snd_hda_detach_beep_device(struct hda_codec *codec);
-#else
-#define snd_hda_attach_beep_device(...) 0
-#define snd_hda_detach_beep_device(...)
-#endif
-#endif
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
deleted file mode 100644
index 644e3f14f8ca..000000000000
--- a/sound/pci/hda/hda_codec.c
+++ /dev/null
@@ -1,4949 +0,0 @@
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
-#include <linux/mutex.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include <sound/asoundef.h>
-#include <sound/tlv.h>
-#include <sound/initval.h>
-#include "hda_local.h"
-#include "hda_beep.h"
-#include <sound/hda_hwdep.h>
-
-/*
- * vendor / preset table
- */
-
-struct hda_vendor_id {
- unsigned int id;
- const char *name;
-};
-
-/* codec vendor labels */
-static struct hda_vendor_id hda_vendor_ids[] = {
- { 0x1002, "ATI" },
- { 0x1013, "Cirrus Logic" },
- { 0x1057, "Motorola" },
- { 0x1095, "Silicon Image" },
- { 0x10de, "Nvidia" },
- { 0x10ec, "Realtek" },
- { 0x1102, "Creative" },
- { 0x1106, "VIA" },
- { 0x111d, "IDT" },
- { 0x11c1, "LSI" },
- { 0x11d4, "Analog Devices" },
- { 0x13f6, "C-Media" },
- { 0x14f1, "Conexant" },
- { 0x17e8, "Chrontel" },
- { 0x1854, "LG" },
- { 0x1aec, "Wolfson Microelectronics" },
- { 0x434d, "C-Media" },
- { 0x8086, "Intel" },
- { 0x8384, "SigmaTel" },
- {} /* terminator */
-};
-
-static DEFINE_MUTEX(preset_mutex);
-static LIST_HEAD(hda_preset_tables);
-
-int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset)
-{
- mutex_lock(&preset_mutex);
- list_add_tail(&preset->list, &hda_preset_tables);
- mutex_unlock(&preset_mutex);
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_add_codec_preset);
-
-int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset)
-{
- mutex_lock(&preset_mutex);
- list_del(&preset->list);
- mutex_unlock(&preset_mutex);
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_delete_codec_preset);
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static void hda_power_work(struct work_struct *work);
-static void hda_keep_power_on(struct hda_codec *codec);
-#else
-static inline void hda_keep_power_on(struct hda_codec *codec) {}
-#endif
-
-/**
- * snd_hda_get_jack_location - Give a location string of the jack
- * @cfg: pin default config value
- *
- * Parse the pin default config value and returns the string of the
- * jack location, e.g. "Rear", "Front", etc.
- */
-const char *snd_hda_get_jack_location(u32 cfg)
-{
- static char *bases[7] = {
- "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
- };
- static unsigned char specials_idx[] = {
- 0x07, 0x08,
- 0x17, 0x18, 0x19,
- 0x37, 0x38
- };
- static char *specials[] = {
- "Rear Panel", "Drive Bar",
- "Riser", "HDMI", "ATAPI",
- "Mobile-In", "Mobile-Out"
- };
- int i;
- cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
- if ((cfg & 0x0f) < 7)
- return bases[cfg & 0x0f];
- for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
- if (cfg == specials_idx[i])
- return specials[i];
- }
- return "UNKNOWN";
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_jack_location);
-
-/**
- * snd_hda_get_jack_connectivity - Give a connectivity string of the jack
- * @cfg: pin default config value
- *
- * Parse the pin default config value and returns the string of the
- * jack connectivity, i.e. external or internal connection.
- */
-const char *snd_hda_get_jack_connectivity(u32 cfg)
-{
- static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
-
- return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3];
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity);
-
-/**
- * snd_hda_get_jack_type - Give a type string of the jack
- * @cfg: pin default config value
- *
- * Parse the pin default config value and returns the string of the
- * jack type, i.e. the purpose of the jack, such as Line-Out or CD.
- */
-const char *snd_hda_get_jack_type(u32 cfg)
-{
- static char *jack_types[16] = {
- "Line Out", "Speaker", "HP Out", "CD",
- "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
- "Line In", "Aux", "Mic", "Telephony",
- "SPDIF In", "Digitial In", "Reserved", "Other"
- };
-
- return jack_types[(cfg & AC_DEFCFG_DEVICE)
- >> AC_DEFCFG_DEVICE_SHIFT];
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_jack_type);
-
-/*
- * Compose a 32bit command word to be sent to the HD-audio controller
- */
-static inline unsigned int
-make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
- unsigned int verb, unsigned int parm)
-{
- u32 val;
-
- if ((codec->addr & ~0xf) || (direct & ~1) || (nid & ~0x7f) ||
- (verb & ~0xfff) || (parm & ~0xffff)) {
- printk(KERN_ERR "hda-codec: out of range cmd %x:%x:%x:%x:%x\n",
- codec->addr, direct, nid, verb, parm);
- return ~0;
- }
-
- val = (u32)codec->addr << 28;
- val |= (u32)direct << 27;
- val |= (u32)nid << 20;
- val |= verb << 8;
- val |= parm;
- return val;
-}
-
-/*
- * Send and receive a verb
- */
-static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
- unsigned int *res)
-{
- struct hda_bus *bus = codec->bus;
- int err;
-
- if (cmd == ~0)
- return -1;
-
- if (res)
- *res = -1;
- again:
- snd_hda_power_up(codec);
- mutex_lock(&bus->cmd_mutex);
- err = bus->ops.command(bus, cmd);
- if (!err && res)
- *res = bus->ops.get_response(bus, codec->addr);
- mutex_unlock(&bus->cmd_mutex);
- snd_hda_power_down(codec);
- if (res && *res == -1 && bus->rirb_error) {
- if (bus->response_reset) {
- snd_printd("hda_codec: resetting BUS due to "
- "fatal communication error\n");
- bus->ops.bus_reset(bus);
- }
- goto again;
- }
- /* clear reset-flag when the communication gets recovered */
- if (!err)
- bus->response_reset = 0;
- return err;
-}
-
-/**
- * snd_hda_codec_read - send a command and get the response
- * @codec: the HDA codec
- * @nid: NID to send the command
- * @direct: direct flag
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command and read the corresponding response.
- *
- * Returns the obtained response value, or -1 for an error.
- */
-unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
- int direct,
- unsigned int verb, unsigned int parm)
-{
- unsigned cmd = make_codec_cmd(codec, nid, direct, verb, parm);
- unsigned int res;
- codec_exec_verb(codec, cmd, &res);
- return res;
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_read);
-
-/**
- * snd_hda_codec_write - send a single command without waiting for response
- * @codec: the HDA codec
- * @nid: NID to send the command
- * @direct: direct flag
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command without waiting for response.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
- unsigned int verb, unsigned int parm)
-{
- unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm);
- unsigned int res;
- return codec_exec_verb(codec, cmd,
- codec->bus->sync_write ? &res : NULL);
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_write);
-
-/**
- * snd_hda_sequence_write - sequence writes
- * @codec: the HDA codec
- * @seq: VERB array to send
- *
- * Send the commands sequentially from the given array.
- * The array must be terminated with NID=0.
- */
-void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq)
-{
- for (; seq->nid; seq++)
- snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param);
-}
-EXPORT_SYMBOL_HDA(snd_hda_sequence_write);
-
-/**
- * snd_hda_get_sub_nodes - get the range of sub nodes
- * @codec: the HDA codec
- * @nid: NID to parse
- * @start_id: the pointer to store the start NID
- *
- * Parse the NID and store the start NID of its sub-nodes.
- * Returns the number of sub-nodes.
- */
-int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t *start_id)
-{
- unsigned int parm;
-
- parm = snd_hda_param_read(codec, nid, AC_PAR_NODE_COUNT);
- if (parm == -1)
- return 0;
- *start_id = (parm >> 16) & 0x7fff;
- return (int)(parm & 0x7fff);
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
-
-/**
- * snd_hda_get_connections - get connection list
- * @codec: the HDA codec
- * @nid: NID to parse
- * @conn_list: connection list array
- * @max_conns: max. number of connections to store
- *
- * Parses the connection list of the given widget and stores the list
- * of NIDs.
- *
- * Returns the number of connections, or a negative error code.
- */
-int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t *conn_list, int max_conns)
-{
- unsigned int parm;
- int i, conn_len, conns;
- unsigned int shift, num_elems, mask;
- unsigned int wcaps;
- hda_nid_t prev_nid;
-
- if (snd_BUG_ON(!conn_list || max_conns <= 0))
- return -EINVAL;
-
- wcaps = get_wcaps(codec, nid);
- if (!(wcaps & AC_WCAP_CONN_LIST) &&
- get_wcaps_type(wcaps) != AC_WID_VOL_KNB) {
- snd_printk(KERN_WARNING "hda_codec: "
- "connection list not available for 0x%x\n", nid);
- return -EINVAL;
- }
-
- parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);
- if (parm & AC_CLIST_LONG) {
- /* long form */
- shift = 16;
- num_elems = 2;
- } else {
- /* short form */
- shift = 8;
- num_elems = 4;
- }
- conn_len = parm & AC_CLIST_LENGTH;
- mask = (1 << (shift-1)) - 1;
-
- if (!conn_len)
- return 0; /* no connection */
-
- if (conn_len == 1) {
- /* single connection */
- parm = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONNECT_LIST, 0);
- if (parm == -1 && codec->bus->rirb_error)
- return -EIO;
- conn_list[0] = parm & mask;
- return 1;
- }
-
- /* multi connection */
- conns = 0;
- prev_nid = 0;
- for (i = 0; i < conn_len; i++) {
- int range_val;
- hda_nid_t val, n;
-
- if (i % num_elems == 0) {
- parm = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONNECT_LIST, i);
- if (parm == -1 && codec->bus->rirb_error)
- return -EIO;
- }
- range_val = !!(parm & (1 << (shift-1))); /* ranges */
- val = parm & mask;
- if (val == 0) {
- snd_printk(KERN_WARNING "hda_codec: "
- "invalid CONNECT_LIST verb %x[%i]:%x\n",
- nid, i, parm);
- return 0;
- }
- parm >>= shift;
- if (range_val) {
- /* ranges between the previous and this one */
- if (!prev_nid || prev_nid >= val) {
- snd_printk(KERN_WARNING "hda_codec: "
- "invalid dep_range_val %x:%x\n",
- prev_nid, val);
- continue;
- }
- for (n = prev_nid + 1; n <= val; n++) {
- if (conns >= max_conns) {
- snd_printk(KERN_ERR "hda_codec: "
- "Too many connections %d for NID 0x%x\n",
- conns, nid);
- return -EINVAL;
- }
- conn_list[conns++] = n;
- }
- } else {
- if (conns >= max_conns) {
- snd_printk(KERN_ERR "hda_codec: "
- "Too many connections %d for NID 0x%x\n",
- conns, nid);
- return -EINVAL;
- }
- conn_list[conns++] = val;
- }
- prev_nid = val;
- }
- return conns;
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_connections);
-
-
-/**
- * snd_hda_queue_unsol_event - add an unsolicited event to queue
- * @bus: the BUS
- * @res: unsolicited event (lower 32bit of RIRB entry)
- * @res_ex: codec addr and flags (upper 32bit or RIRB entry)
- *
- * Adds the given event to the queue. The events are processed in
- * the workqueue asynchronously. Call this function in the interrupt
- * hanlder when RIRB receives an unsolicited event.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
-{
- struct hda_bus_unsolicited *unsol;
- unsigned int wp;
-
- unsol = bus->unsol;
- if (!unsol)
- return 0;
-
- wp = (unsol->wp + 1) % HDA_UNSOL_QUEUE_SIZE;
- unsol->wp = wp;
-
- wp <<= 1;
- unsol->queue[wp] = res;
- unsol->queue[wp + 1] = res_ex;
-
- queue_work(bus->workq, &unsol->work);
-
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_queue_unsol_event);
-
-/*
- * process queued unsolicited events
- */
-static void process_unsol_events(struct work_struct *work)
-{
- struct hda_bus_unsolicited *unsol =
- container_of(work, struct hda_bus_unsolicited, work);
- struct hda_bus *bus = unsol->bus;
- struct hda_codec *codec;
- unsigned int rp, caddr, res;
-
- while (unsol->rp != unsol->wp) {
- rp = (unsol->rp + 1) % HDA_UNSOL_QUEUE_SIZE;
- unsol->rp = rp;
- rp <<= 1;
- res = unsol->queue[rp];
- caddr = unsol->queue[rp + 1];
- if (!(caddr & (1 << 4))) /* no unsolicited event? */
- continue;
- codec = bus->caddr_tbl[caddr & 0x0f];
- if (codec && codec->patch_ops.unsol_event)
- codec->patch_ops.unsol_event(codec, res);
- }
-}
-
-/*
- * initialize unsolicited queue
- */
-static int init_unsol_queue(struct hda_bus *bus)
-{
- struct hda_bus_unsolicited *unsol;
-
- if (bus->unsol) /* already initialized */
- return 0;
-
- unsol = kzalloc(sizeof(*unsol), GFP_KERNEL);
- if (!unsol) {
- snd_printk(KERN_ERR "hda_codec: "
- "can't allocate unsolicited queue\n");
- return -ENOMEM;
- }
- INIT_WORK(&unsol->work, process_unsol_events);
- unsol->bus = bus;
- bus->unsol = unsol;
- return 0;
-}
-
-/*
- * destructor
- */
-static void snd_hda_codec_free(struct hda_codec *codec);
-
-static int snd_hda_bus_free(struct hda_bus *bus)
-{
- struct hda_codec *codec, *n;
-
- if (!bus)
- return 0;
- if (bus->workq)
- flush_workqueue(bus->workq);
- if (bus->unsol)
- kfree(bus->unsol);
- list_for_each_entry_safe(codec, n, &bus->codec_list, list) {
- snd_hda_codec_free(codec);
- }
- if (bus->ops.private_free)
- bus->ops.private_free(bus);
- if (bus->workq)
- destroy_workqueue(bus->workq);
- kfree(bus);
- return 0;
-}
-
-static int snd_hda_bus_dev_free(struct snd_device *device)
-{
- struct hda_bus *bus = device->device_data;
- bus->shutdown = 1;
- return snd_hda_bus_free(bus);
-}
-
-#ifdef CONFIG_SND_HDA_HWDEP
-static int snd_hda_bus_dev_register(struct snd_device *device)
-{
- struct hda_bus *bus = device->device_data;
- struct hda_codec *codec;
- list_for_each_entry(codec, &bus->codec_list, list) {
- snd_hda_hwdep_add_sysfs(codec);
- snd_hda_hwdep_add_power_sysfs(codec);
- }
- return 0;
-}
-#else
-#define snd_hda_bus_dev_register NULL
-#endif
-
-/**
- * snd_hda_bus_new - create a HDA bus
- * @card: the card entry
- * @temp: the template for hda_bus information
- * @busp: the pointer to store the created bus instance
- *
- * Returns 0 if successful, or a negative error code.
- */
-int /*__devinit*/ snd_hda_bus_new(struct snd_card *card,
- const struct hda_bus_template *temp,
- struct hda_bus **busp)
-{
- struct hda_bus *bus;
- int err;
- static struct snd_device_ops dev_ops = {
- .dev_register = snd_hda_bus_dev_register,
- .dev_free = snd_hda_bus_dev_free,
- };
-
- if (snd_BUG_ON(!temp))
- return -EINVAL;
- if (snd_BUG_ON(!temp->ops.command || !temp->ops.get_response))
- return -EINVAL;
-
- if (busp)
- *busp = NULL;
-
- bus = kzalloc(sizeof(*bus), GFP_KERNEL);
- if (bus == NULL) {
- snd_printk(KERN_ERR "can't allocate struct hda_bus\n");
- return -ENOMEM;
- }
-
- bus->card = card;
- bus->private_data = temp->private_data;
- bus->pci = temp->pci;
- bus->modelname = temp->modelname;
- bus->power_save = temp->power_save;
- bus->ops = temp->ops;
-
- mutex_init(&bus->cmd_mutex);
- mutex_init(&bus->prepare_mutex);
- INIT_LIST_HEAD(&bus->codec_list);
-
- snprintf(bus->workq_name, sizeof(bus->workq_name),
- "hd-audio%d", card->number);
- bus->workq = create_singlethread_workqueue(bus->workq_name);
- if (!bus->workq) {
- snd_printk(KERN_ERR "cannot create workqueue %s\n",
- bus->workq_name);
- kfree(bus);
- return -ENOMEM;
- }
-
- err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops);
- if (err < 0) {
- snd_hda_bus_free(bus);
- return err;
- }
- if (busp)
- *busp = bus;
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_bus_new);
-
-#ifdef CONFIG_SND_HDA_GENERIC
-#define is_generic_config(codec) \
- (codec->modelname && !strcmp(codec->modelname, "generic"))
-#else
-#define is_generic_config(codec) 0
-#endif
-
-#ifdef MODULE
-#define HDA_MODREQ_MAX_COUNT 2 /* two request_modules()'s */
-#else
-#define HDA_MODREQ_MAX_COUNT 0 /* all presets are statically linked */
-#endif
-
-/*
- * find a matching codec preset
- */
-static const struct hda_codec_preset *
-find_codec_preset(struct hda_codec *codec)
-{
- struct hda_codec_preset_list *tbl;
- const struct hda_codec_preset *preset;
- int mod_requested = 0;
-
- if (is_generic_config(codec))
- return NULL; /* use the generic parser */
-
- again:
- mutex_lock(&preset_mutex);
- list_for_each_entry(tbl, &hda_preset_tables, list) {
- if (!try_module_get(tbl->owner)) {
- snd_printk(KERN_ERR "hda_codec: cannot module_get\n");
- continue;
- }
- for (preset = tbl->preset; preset->id; preset++) {
- u32 mask = preset->mask;
- if (preset->afg && preset->afg != codec->afg)
- continue;
- if (preset->mfg && preset->mfg != codec->mfg)
- continue;
- if (!mask)
- mask = ~0;
- if (preset->id == (codec->vendor_id & mask) &&
- (!preset->rev ||
- preset->rev == codec->revision_id)) {
- mutex_unlock(&preset_mutex);
- codec->owner = tbl->owner;
- return preset;
- }
- }
- module_put(tbl->owner);
- }
- mutex_unlock(&preset_mutex);
-
- if (mod_requested < HDA_MODREQ_MAX_COUNT) {
- char name[32];
- if (!mod_requested)
- snprintf(name, sizeof(name), "snd-hda-codec-id:%08x",
- codec->vendor_id);
- else
- snprintf(name, sizeof(name), "snd-hda-codec-id:%04x*",
- (codec->vendor_id >> 16) & 0xffff);
- request_module(name);
- mod_requested++;
- goto again;
- }
- return NULL;
-}
-
-/*
- * get_codec_name - store the codec name
- */
-static int get_codec_name(struct hda_codec *codec)
-{
- const struct hda_vendor_id *c;
- const char *vendor = NULL;
- u16 vendor_id = codec->vendor_id >> 16;
- char tmp[16];
-
- if (codec->vendor_name)
- goto get_chip_name;
-
- for (c = hda_vendor_ids; c->id; c++) {
- if (c->id == vendor_id) {
- vendor = c->name;
- break;
- }
- }
- if (!vendor) {
- sprintf(tmp, "Generic %04x", vendor_id);
- vendor = tmp;
- }
- codec->vendor_name = kstrdup(vendor, GFP_KERNEL);
- if (!codec->vendor_name)
- return -ENOMEM;
-
- get_chip_name:
- if (codec->chip_name)
- return 0;
-
- if (codec->preset && codec->preset->name)
- codec->chip_name = kstrdup(codec->preset->name, GFP_KERNEL);
- else {
- sprintf(tmp, "ID %x", codec->vendor_id & 0xffff);
- codec->chip_name = kstrdup(tmp, GFP_KERNEL);
- }
- if (!codec->chip_name)
- return -ENOMEM;
- return 0;
-}
-
-/*
- * look for an AFG and MFG nodes
- */
-static void /*__devinit*/ setup_fg_nodes(struct hda_codec *codec)
-{
- int i, total_nodes, function_id;
- hda_nid_t nid;
-
- total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
- for (i = 0; i < total_nodes; i++, nid++) {
- function_id = snd_hda_param_read(codec, nid,
- AC_PAR_FUNCTION_TYPE);
- switch (function_id & 0xff) {
- case AC_GRP_AUDIO_FUNCTION:
- codec->afg = nid;
- codec->afg_function_id = function_id & 0xff;
- codec->afg_unsol = (function_id >> 8) & 1;
- break;
- case AC_GRP_MODEM_FUNCTION:
- codec->mfg = nid;
- codec->mfg_function_id = function_id & 0xff;
- codec->mfg_unsol = (function_id >> 8) & 1;
- break;
- default:
- break;
- }
- }
-}
-
-/*
- * read widget caps for each widget and store in cache
- */
-static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node)
-{
- int i;
- hda_nid_t nid;
-
- codec->num_nodes = snd_hda_get_sub_nodes(codec, fg_node,
- &codec->start_nid);
- codec->wcaps = kmalloc(codec->num_nodes * 4, GFP_KERNEL);
- if (!codec->wcaps)
- return -ENOMEM;
- nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, nid++)
- codec->wcaps[i] = snd_hda_param_read(codec, nid,
- AC_PAR_AUDIO_WIDGET_CAP);
- return 0;
-}
-
-/* read all pin default configurations and save codec->init_pins */
-static int read_pin_defaults(struct hda_codec *codec)
-{
- int i;
- hda_nid_t nid = codec->start_nid;
-
- for (i = 0; i < codec->num_nodes; i++, nid++) {
- struct hda_pincfg *pin;
- unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int wid_type = get_wcaps_type(wcaps);
- if (wid_type != AC_WID_PIN)
- continue;
- pin = snd_array_new(&codec->init_pins);
- if (!pin)
- return -ENOMEM;
- pin->nid = nid;
- pin->cfg = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT, 0);
- pin->ctrl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL,
- 0);
- }
- return 0;
-}
-
-/* look up the given pin config list and return the item matching with NID */
-static struct hda_pincfg *look_up_pincfg(struct hda_codec *codec,
- struct snd_array *array,
- hda_nid_t nid)
-{
- int i;
- for (i = 0; i < array->used; i++) {
- struct hda_pincfg *pin = snd_array_elem(array, i);
- if (pin->nid == nid)
- return pin;
- }
- return NULL;
-}
-
-/* write a config value for the given NID */
-static void set_pincfg(struct hda_codec *codec, hda_nid_t nid,
- unsigned int cfg)
-{
- int i;
- for (i = 0; i < 4; i++) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i,
- cfg & 0xff);
- cfg >>= 8;
- }
-}
-
-/* set the current pin config value for the given NID.
- * the value is cached, and read via snd_hda_codec_get_pincfg()
- */
-int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
- hda_nid_t nid, unsigned int cfg)
-{
- struct hda_pincfg *pin;
- unsigned int oldcfg;
-
- if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
- return -EINVAL;
-
- oldcfg = snd_hda_codec_get_pincfg(codec, nid);
- pin = look_up_pincfg(codec, list, nid);
- if (!pin) {
- pin = snd_array_new(list);
- if (!pin)
- return -ENOMEM;
- pin->nid = nid;
- }
- pin->cfg = cfg;
-
- /* change only when needed; e.g. if the pincfg is already present
- * in user_pins[], don't write it
- */
- cfg = snd_hda_codec_get_pincfg(codec, nid);
- if (oldcfg != cfg)
- set_pincfg(codec, nid, cfg);
- return 0;
-}
-
-/**
- * snd_hda_codec_set_pincfg - Override a pin default configuration
- * @codec: the HDA codec
- * @nid: NID to set the pin config
- * @cfg: the pin default config value
- *
- * Override a pin default configuration value in the cache.
- * This value can be read by snd_hda_codec_get_pincfg() in a higher
- * priority than the real hardware value.
- */
-int snd_hda_codec_set_pincfg(struct hda_codec *codec,
- hda_nid_t nid, unsigned int cfg)
-{
- return snd_hda_add_pincfg(codec, &codec->driver_pins, nid, cfg);
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg);
-
-/**
- * snd_hda_codec_get_pincfg - Obtain a pin-default configuration
- * @codec: the HDA codec
- * @nid: NID to get the pin config
- *
- * Get the current pin config value of the given pin NID.
- * If the pincfg value is cached or overridden via sysfs or driver,
- * returns the cached value.
- */
-unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
-{
- struct hda_pincfg *pin;
-
-#ifdef CONFIG_SND_HDA_HWDEP
- pin = look_up_pincfg(codec, &codec->user_pins, nid);
- if (pin)
- return pin->cfg;
-#endif
- pin = look_up_pincfg(codec, &codec->driver_pins, nid);
- if (pin)
- return pin->cfg;
- pin = look_up_pincfg(codec, &codec->init_pins, nid);
- if (pin)
- return pin->cfg;
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_get_pincfg);
-
-/* restore all current pin configs */
-static void restore_pincfgs(struct hda_codec *codec)
-{
- int i;
- for (i = 0; i < codec->init_pins.used; i++) {
- struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
- set_pincfg(codec, pin->nid,
- snd_hda_codec_get_pincfg(codec, pin->nid));
- }
-}
-
-/**
- * snd_hda_shutup_pins - Shut up all pins
- * @codec: the HDA codec
- *
- * Clear all pin controls to shup up before suspend for avoiding click noise.
- * The controls aren't cached so that they can be resumed properly.
- */
-void snd_hda_shutup_pins(struct hda_codec *codec)
-{
- int i;
- /* don't shut up pins when unloading the driver; otherwise it breaks
- * the default pin setup at the next load of the driver
- */
- if (codec->bus->shutdown)
- return;
- for (i = 0; i < codec->init_pins.used; i++) {
- struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
- /* use read here for syncing after issuing each verb */
- snd_hda_codec_read(codec, pin->nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
- }
- codec->pins_shutup = 1;
-}
-EXPORT_SYMBOL_HDA(snd_hda_shutup_pins);
-
-/* Restore the pin controls cleared previously via snd_hda_shutup_pins() */
-static void restore_shutup_pins(struct hda_codec *codec)
-{
- int i;
- if (!codec->pins_shutup)
- return;
- if (codec->bus->shutdown)
- return;
- for (i = 0; i < codec->init_pins.used; i++) {
- struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
- snd_hda_codec_write(codec, pin->nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin->ctrl);
- }
- codec->pins_shutup = 0;
-}
-
-static void init_hda_cache(struct hda_cache_rec *cache,
- unsigned int record_size);
-static void free_hda_cache(struct hda_cache_rec *cache);
-
-/* restore the initial pin cfgs and release all pincfg lists */
-static void restore_init_pincfgs(struct hda_codec *codec)
-{
- /* first free driver_pins and user_pins, then call restore_pincfg
- * so that only the values in init_pins are restored
- */
- snd_array_free(&codec->driver_pins);
-#ifdef CONFIG_SND_HDA_HWDEP
- snd_array_free(&codec->user_pins);
-#endif
- restore_pincfgs(codec);
- snd_array_free(&codec->init_pins);
-}
-
-/*
- * audio-converter setup caches
- */
-struct hda_cvt_setup {
- hda_nid_t nid;
- u8 stream_tag;
- u8 channel_id;
- u16 format_id;
- unsigned char active; /* cvt is currently used */
- unsigned char dirty; /* setups should be cleared */
-};
-
-/* get or create a cache entry for the given audio converter NID */
-static struct hda_cvt_setup *
-get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid)
-{
- struct hda_cvt_setup *p;
- int i;
-
- for (i = 0; i < codec->cvt_setups.used; i++) {
- p = snd_array_elem(&codec->cvt_setups, i);
- if (p->nid == nid)
- return p;
- }
- p = snd_array_new(&codec->cvt_setups);
- if (p)
- p->nid = nid;
- return p;
-}
-
-/*
- * codec destructor
- */
-static void snd_hda_codec_free(struct hda_codec *codec)
-{
- if (!codec)
- return;
- restore_init_pincfgs(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- cancel_delayed_work(&codec->power_work);
- flush_workqueue(codec->bus->workq);
-#endif
- list_del(&codec->list);
- snd_array_free(&codec->mixers);
- snd_array_free(&codec->nids);
- codec->bus->caddr_tbl[codec->addr] = NULL;
- if (codec->patch_ops.free)
- codec->patch_ops.free(codec);
- module_put(codec->owner);
- free_hda_cache(&codec->amp_cache);
- free_hda_cache(&codec->cmd_cache);
- kfree(codec->vendor_name);
- kfree(codec->chip_name);
- kfree(codec->modelname);
- kfree(codec->wcaps);
- kfree(codec);
-}
-
-static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
- unsigned int power_state);
-
-/**
- * snd_hda_codec_new - create a HDA codec
- * @bus: the bus to assign
- * @codec_addr: the codec address
- * @codecp: the pointer to store the generated codec
- *
- * Returns 0 if successful, or a negative error code.
- */
-int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
- unsigned int codec_addr,
- struct hda_codec **codecp)
-{
- struct hda_codec *codec;
- char component[31];
- int err;
-
- if (snd_BUG_ON(!bus))
- return -EINVAL;
- if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS))
- return -EINVAL;
-
- if (bus->caddr_tbl[codec_addr]) {
- snd_printk(KERN_ERR "hda_codec: "
- "address 0x%x is already occupied\n", codec_addr);
- return -EBUSY;
- }
-
- codec = kzalloc(sizeof(*codec), GFP_KERNEL);
- if (codec == NULL) {
- snd_printk(KERN_ERR "can't allocate struct hda_codec\n");
- return -ENOMEM;
- }
-
- codec->bus = bus;
- codec->addr = codec_addr;
- mutex_init(&codec->spdif_mutex);
- mutex_init(&codec->control_mutex);
- init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
- init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
- snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
- snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32);
- snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
- snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
- snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
- if (codec->bus->modelname) {
- codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
- if (!codec->modelname) {
- snd_hda_codec_free(codec);
- return -ENODEV;
- }
- }
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
- /* snd_hda_codec_new() marks the codec as power-up, and leave it as is.
- * the caller has to power down appropriatley after initialization
- * phase.
- */
- hda_keep_power_on(codec);
-#endif
-
- list_add_tail(&codec->list, &bus->codec_list);
- bus->caddr_tbl[codec_addr] = codec;
-
- codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT,
- AC_PAR_VENDOR_ID);
- if (codec->vendor_id == -1)
- /* read again, hopefully the access method was corrected
- * in the last read...
- */
- codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT,
- AC_PAR_VENDOR_ID);
- codec->subsystem_id = snd_hda_param_read(codec, AC_NODE_ROOT,
- AC_PAR_SUBSYSTEM_ID);
- codec->revision_id = snd_hda_param_read(codec, AC_NODE_ROOT,
- AC_PAR_REV_ID);
-
- setup_fg_nodes(codec);
- if (!codec->afg && !codec->mfg) {
- snd_printdd("hda_codec: no AFG or MFG node found\n");
- err = -ENODEV;
- goto error;
- }
-
- err = read_widget_caps(codec, codec->afg ? codec->afg : codec->mfg);
- if (err < 0) {
- snd_printk(KERN_ERR "hda_codec: cannot malloc\n");
- goto error;
- }
- err = read_pin_defaults(codec);
- if (err < 0)
- goto error;
-
- if (!codec->subsystem_id) {
- hda_nid_t nid = codec->afg ? codec->afg : codec->mfg;
- codec->subsystem_id =
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_SUBSYSTEM_ID, 0);
- }
-
- /* power-up all before initialization */
- hda_set_power_state(codec,
- codec->afg ? codec->afg : codec->mfg,
- AC_PWRST_D0);
-
- snd_hda_codec_proc_new(codec);
-
- snd_hda_create_hwdep(codec);
-
- sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id,
- codec->subsystem_id, codec->revision_id);
- snd_component_add(codec->bus->card, component);
-
- if (codecp)
- *codecp = codec;
- return 0;
-
- error:
- snd_hda_codec_free(codec);
- return err;
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_new);
-
-/**
- * snd_hda_codec_configure - (Re-)configure the HD-audio codec
- * @codec: the HDA codec
- *
- * Start parsing of the given codec tree and (re-)initialize the whole
- * patch instance.
- *
- * Returns 0 if successful or a negative error code.
- */
-int snd_hda_codec_configure(struct hda_codec *codec)
-{
- int err;
-
- codec->preset = find_codec_preset(codec);
- if (!codec->vendor_name || !codec->chip_name) {
- err = get_codec_name(codec);
- if (err < 0)
- return err;
- }
-
- if (is_generic_config(codec)) {
- err = snd_hda_parse_generic_codec(codec);
- goto patched;
- }
- if (codec->preset && codec->preset->patch) {
- err = codec->preset->patch(codec);
- goto patched;
- }
-
- /* call the default parser */
- err = snd_hda_parse_generic_codec(codec);
- if (err < 0)
- printk(KERN_ERR "hda-codec: No codec parser is available\n");
-
- patched:
- if (!err && codec->patch_ops.unsol_event)
- err = init_unsol_queue(codec->bus);
- /* audio codec should override the mixer name */
- if (!err && (codec->afg || !*codec->bus->card->mixername))
- snprintf(codec->bus->card->mixername,
- sizeof(codec->bus->card->mixername),
- "%s %s", codec->vendor_name, codec->chip_name);
- return err;
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_configure);
-
-/**
- * snd_hda_codec_setup_stream - set up the codec for streaming
- * @codec: the CODEC to set up
- * @nid: the NID to set up
- * @stream_tag: stream tag to pass, it's between 0x1 and 0xf.
- * @channel_id: channel id to pass, zero based.
- * @format: stream format.
- */
-void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
- u32 stream_tag,
- int channel_id, int format)
-{
- struct hda_codec *c;
- struct hda_cvt_setup *p;
- unsigned int oldval, newval;
- int type;
- int i;
-
- if (!nid)
- return;
-
- snd_printdd("hda_codec_setup_stream: "
- "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
- nid, stream_tag, channel_id, format);
- p = get_hda_cvt_setup(codec, nid);
- if (!p)
- return;
- /* update the stream-id if changed */
- if (p->stream_tag != stream_tag || p->channel_id != channel_id) {
- oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
- newval = (stream_tag << 4) | channel_id;
- if (oldval != newval)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CHANNEL_STREAMID,
- newval);
- p->stream_tag = stream_tag;
- p->channel_id = channel_id;
- }
- /* update the format-id if changed */
- if (p->format_id != format) {
- oldval = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_STREAM_FORMAT, 0);
- if (oldval != format) {
- msleep(1);
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_STREAM_FORMAT,
- format);
- }
- p->format_id = format;
- }
- p->active = 1;
- p->dirty = 0;
-
- /* make other inactive cvts with the same stream-tag dirty */
- type = get_wcaps_type(get_wcaps(codec, nid));
- list_for_each_entry(c, &codec->bus->codec_list, list) {
- for (i = 0; i < c->cvt_setups.used; i++) {
- p = snd_array_elem(&c->cvt_setups, i);
- if (!p->active && p->stream_tag == stream_tag &&
- get_wcaps_type(get_wcaps(codec, p->nid)) == type)
- p->dirty = 1;
- }
- }
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream);
-
-static void really_cleanup_stream(struct hda_codec *codec,
- struct hda_cvt_setup *q);
-
-/**
- * __snd_hda_codec_cleanup_stream - clean up the codec for closing
- * @codec: the CODEC to clean up
- * @nid: the NID to clean up
- * @do_now: really clean up the stream instead of clearing the active flag
- */
-void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
- int do_now)
-{
- struct hda_cvt_setup *p;
-
- if (!nid)
- return;
-
- if (codec->no_sticky_stream)
- do_now = 1;
-
- snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid);
- p = get_hda_cvt_setup(codec, nid);
- if (p) {
- /* here we just clear the active flag when do_now isn't set;
- * actual clean-ups will be done later in
- * purify_inactive_streams() called from snd_hda_codec_prpapre()
- */
- if (do_now)
- really_cleanup_stream(codec, p);
- else
- p->active = 0;
- }
-}
-EXPORT_SYMBOL_HDA(__snd_hda_codec_cleanup_stream);
-
-static void really_cleanup_stream(struct hda_codec *codec,
- struct hda_cvt_setup *q)
-{
- hda_nid_t nid = q->nid;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
- memset(q, 0, sizeof(*q));
- q->nid = nid;
-}
-
-/* clean up the all conflicting obsolete streams */
-static void purify_inactive_streams(struct hda_codec *codec)
-{
- struct hda_codec *c;
- int i;
-
- list_for_each_entry(c, &codec->bus->codec_list, list) {
- for (i = 0; i < c->cvt_setups.used; i++) {
- struct hda_cvt_setup *p;
- p = snd_array_elem(&c->cvt_setups, i);
- if (p->dirty)
- really_cleanup_stream(c, p);
- }
- }
-}
-
-/* clean up all streams; called from suspend */
-static void hda_cleanup_all_streams(struct hda_codec *codec)
-{
- int i;
-
- for (i = 0; i < codec->cvt_setups.used; i++) {
- struct hda_cvt_setup *p = snd_array_elem(&codec->cvt_setups, i);
- if (p->stream_tag)
- really_cleanup_stream(codec, p);
- }
-}
-
-/*
- * amp access functions
- */
-
-/* FIXME: more better hash key? */
-#define HDA_HASH_KEY(nid, dir, idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
-#define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24))
-#define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24))
-#define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24))
-#define INFO_AMP_CAPS (1<<0)
-#define INFO_AMP_VOL(ch) (1 << (1 + (ch)))
-
-/* initialize the hash table */
-static void /*__devinit*/ init_hda_cache(struct hda_cache_rec *cache,
- unsigned int record_size)
-{
- memset(cache, 0, sizeof(*cache));
- memset(cache->hash, 0xff, sizeof(cache->hash));
- snd_array_init(&cache->buf, record_size, 64);
-}
-
-static void free_hda_cache(struct hda_cache_rec *cache)
-{
- snd_array_free(&cache->buf);
-}
-
-/* query the hash. allocate an entry if not found. */
-static struct hda_cache_head *get_hash(struct hda_cache_rec *cache, u32 key)
-{
- u16 idx = key % (u16)ARRAY_SIZE(cache->hash);
- u16 cur = cache->hash[idx];
- struct hda_cache_head *info;
-
- while (cur != 0xffff) {
- info = snd_array_elem(&cache->buf, cur);
- if (info->key == key)
- return info;
- cur = info->next;
- }
- return NULL;
-}
-
-/* query the hash. allocate an entry if not found. */
-static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
- u32 key)
-{
- struct hda_cache_head *info = get_hash(cache, key);
- if (!info) {
- u16 idx, cur;
- /* add a new hash entry */
- info = snd_array_new(&cache->buf);
- if (!info)
- return NULL;
- cur = snd_array_index(&cache->buf, info);
- info->key = key;
- info->val = 0;
- idx = key % (u16)ARRAY_SIZE(cache->hash);
- info->next = cache->hash[idx];
- cache->hash[idx] = cur;
- }
- return info;
-}
-
-/* query and allocate an amp hash entry */
-static inline struct hda_amp_info *
-get_alloc_amp_hash(struct hda_codec *codec, u32 key)
-{
- return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key);
-}
-
-/**
- * query_amp_caps - query AMP capabilities
- * @codec: the HD-auio codec
- * @nid: the NID to query
- * @direction: either #HDA_INPUT or #HDA_OUTPUT
- *
- * Query AMP capabilities for the given widget and direction.
- * Returns the obtained capability bits.
- *
- * When cap bits have been already read, this doesn't read again but
- * returns the cached value.
- */
-u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
-{
- struct hda_amp_info *info;
-
- info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0));
- if (!info)
- return 0;
- if (!(info->head.val & INFO_AMP_CAPS)) {
- if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
- nid = codec->afg;
- info->amp_caps = snd_hda_param_read(codec, nid,
- direction == HDA_OUTPUT ?
- AC_PAR_AMP_OUT_CAP :
- AC_PAR_AMP_IN_CAP);
- if (info->amp_caps)
- info->head.val |= INFO_AMP_CAPS;
- }
- return info->amp_caps;
-}
-EXPORT_SYMBOL_HDA(query_amp_caps);
-
-/**
- * snd_hda_override_amp_caps - Override the AMP capabilities
- * @codec: the CODEC to clean up
- * @nid: the NID to clean up
- * @direction: either #HDA_INPUT or #HDA_OUTPUT
- * @caps: the capability bits to set
- *
- * Override the cached AMP caps bits value by the given one.
- * This function is useful if the driver needs to adjust the AMP ranges,
- * e.g. limit to 0dB, etc.
- *
- * Returns zero if successful or a negative error code.
- */
-int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
- unsigned int caps)
-{
- struct hda_amp_info *info;
-
- info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, dir, 0));
- if (!info)
- return -EINVAL;
- info->amp_caps = caps;
- info->head.val |= INFO_AMP_CAPS;
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
-
-static unsigned int
-query_caps_hash(struct hda_codec *codec, hda_nid_t nid, u32 key,
- unsigned int (*func)(struct hda_codec *, hda_nid_t))
-{
- struct hda_amp_info *info;
-
- info = get_alloc_amp_hash(codec, key);
- if (!info)
- return 0;
- if (!info->head.val) {
- info->head.val |= INFO_AMP_CAPS;
- info->amp_caps = func(codec, nid);
- }
- return info->amp_caps;
-}
-
-static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
-{
- return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
-}
-
-/**
- * snd_hda_query_pin_caps - Query PIN capabilities
- * @codec: the HD-auio codec
- * @nid: the NID to query
- *
- * Query PIN capabilities for the given widget.
- * Returns the obtained capability bits.
- *
- * When cap bits have been already read, this doesn't read again but
- * returns the cached value.
- */
-u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
-{
- return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid),
- read_pin_cap);
-}
-EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
-
-/**
- * snd_hda_pin_sense - execute pin sense measurement
- * @codec: the CODEC to sense
- * @nid: the pin NID to sense
- *
- * Execute necessary pin sense measurement and return its Presence Detect,
- * Impedance, ELD Valid etc. status bits.
- */
-u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
-{
- u32 pincap;
-
- if (!codec->no_trigger_sense) {
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_SET_PIN_SENSE, 0);
- }
- return snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_SENSE, 0);
-}
-EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
-
-/**
- * snd_hda_jack_detect - query pin Presence Detect status
- * @codec: the CODEC to sense
- * @nid: the pin NID to sense
- *
- * Query and return the pin's Presence Detect status.
- */
-int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
-{
- u32 sense = snd_hda_pin_sense(codec, nid);
- return !!(sense & AC_PINSENSE_PRESENCE);
-}
-EXPORT_SYMBOL_HDA(snd_hda_jack_detect);
-
-/*
- * read the current volume to info
- * if the cache exists, read the cache value.
- */
-static unsigned int get_vol_mute(struct hda_codec *codec,
- struct hda_amp_info *info, hda_nid_t nid,
- int ch, int direction, int index)
-{
- u32 val, parm;
-
- if (info->head.val & INFO_AMP_VOL(ch))
- return info->vol[ch];
-
- parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
- parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
- parm |= index;
- val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_AMP_GAIN_MUTE, parm);
- info->vol[ch] = val & 0xff;
- info->head.val |= INFO_AMP_VOL(ch);
- return info->vol[ch];
-}
-
-/*
- * write the current volume in info to the h/w and update the cache
- */
-static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
- hda_nid_t nid, int ch, int direction, int index,
- int val)
-{
- u32 parm;
-
- parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT;
- parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT;
- parm |= index << AC_AMP_SET_INDEX_SHIFT;
- parm |= val;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm);
- info->vol[ch] = val;
-}
-
-/**
- * snd_hda_codec_amp_read - Read AMP value
- * @codec: HD-audio codec
- * @nid: NID to read the AMP value
- * @ch: channel (left=0 or right=1)
- * @direction: #HDA_INPUT or #HDA_OUTPUT
- * @index: the index value (only for input direction)
- *
- * Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit.
- */
-int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
- int direction, int index)
-{
- struct hda_amp_info *info;
- info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
- if (!info)
- return 0;
- return get_vol_mute(codec, info, nid, ch, direction, index);
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
-
-/**
- * snd_hda_codec_amp_update - update the AMP value
- * @codec: HD-audio codec
- * @nid: NID to read the AMP value
- * @ch: channel (left=0 or right=1)
- * @direction: #HDA_INPUT or #HDA_OUTPUT
- * @idx: the index value (only for input direction)
- * @mask: bit mask to set
- * @val: the bits value to set
- *
- * Update the AMP value with a bit mask.
- * Returns 0 if the value is unchanged, 1 if changed.
- */
-int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
- int direction, int idx, int mask, int val)
-{
- struct hda_amp_info *info;
-
- info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx));
- if (!info)
- return 0;
- if (snd_BUG_ON(mask & ~0xff))
- mask &= 0xff;
- val &= mask;
- val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask;
- if (info->vol[ch] == val)
- return 0;
- put_vol_mute(codec, info, nid, ch, direction, idx, val);
- return 1;
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
-
-/**
- * snd_hda_codec_amp_stereo - update the AMP stereo values
- * @codec: HD-audio codec
- * @nid: NID to read the AMP value
- * @direction: #HDA_INPUT or #HDA_OUTPUT
- * @idx: the index value (only for input direction)
- * @mask: bit mask to set
- * @val: the bits value to set
- *
- * Update the AMP values like snd_hda_codec_amp_update(), but for a
- * stereo widget with the same mask and value.
- */
-int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
- int direction, int idx, int mask, int val)
-{
- int ch, ret = 0;
-
- if (snd_BUG_ON(mask & ~0xff))
- mask &= 0xff;
- for (ch = 0; ch < 2; ch++)
- ret |= snd_hda_codec_amp_update(codec, nid, ch, direction,
- idx, mask, val);
- return ret;
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
-
-#ifdef SND_HDA_NEEDS_RESUME
-/**
- * snd_hda_codec_resume_amp - Resume all AMP commands from the cache
- * @codec: HD-audio codec
- *
- * Resume the all amp commands from the cache.
- */
-void snd_hda_codec_resume_amp(struct hda_codec *codec)
-{
- struct hda_amp_info *buffer = codec->amp_cache.buf.list;
- int i;
-
- for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) {
- u32 key = buffer->head.key;
- hda_nid_t nid;
- unsigned int idx, dir, ch;
- if (!key)
- continue;
- nid = key & 0xff;
- idx = (key >> 16) & 0xff;
- dir = (key >> 24) & 0xff;
- for (ch = 0; ch < 2; ch++) {
- if (!(buffer->head.val & INFO_AMP_VOL(ch)))
- continue;
- put_vol_mute(codec, buffer, nid, ch, dir, idx,
- buffer->vol[ch]);
- }
- }
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
-#endif /* SND_HDA_NEEDS_RESUME */
-
-static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
- unsigned int ofs)
-{
- u32 caps = query_amp_caps(codec, nid, dir);
- /* get num steps */
- caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
- if (ofs < caps)
- caps -= ofs;
- return caps;
-}
-
-/**
- * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
- */
-int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- u16 nid = get_amp_nid(kcontrol);
- u8 chs = get_amp_channels(kcontrol);
- int dir = get_amp_direction(kcontrol);
- unsigned int ofs = get_amp_offset(kcontrol);
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = chs == 3 ? 2 : 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = get_amp_max_value(codec, nid, dir, ofs);
- if (!uinfo->value.integer.max) {
- printk(KERN_WARNING "hda_codec: "
- "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid,
- kcontrol->id.name);
- return -EINVAL;
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info);
-
-
-static inline unsigned int
-read_amp_value(struct hda_codec *codec, hda_nid_t nid,
- int ch, int dir, int idx, unsigned int ofs)
-{
- unsigned int val;
- val = snd_hda_codec_amp_read(codec, nid, ch, dir, idx);
- val &= HDA_AMP_VOLMASK;
- if (val >= ofs)
- val -= ofs;
- else
- val = 0;
- return val;
-}
-
-static inline int
-update_amp_value(struct hda_codec *codec, hda_nid_t nid,
- int ch, int dir, int idx, unsigned int ofs,
- unsigned int val)
-{
- unsigned int maxval;
-
- if (val > 0)
- val += ofs;
- /* ofs = 0: raw max value */
- maxval = get_amp_max_value(codec, nid, dir, 0);
- if (val > maxval)
- val = maxval;
- return snd_hda_codec_amp_update(codec, nid, ch, dir, idx,
- HDA_AMP_VOLMASK, val);
-}
-
-/**
- * snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
- */
-int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = get_amp_nid(kcontrol);
- int chs = get_amp_channels(kcontrol);
- int dir = get_amp_direction(kcontrol);
- int idx = get_amp_index(kcontrol);
- unsigned int ofs = get_amp_offset(kcontrol);
- long *valp = ucontrol->value.integer.value;
-
- if (chs & 1)
- *valp++ = read_amp_value(codec, nid, 0, dir, idx, ofs);
- if (chs & 2)
- *valp = read_amp_value(codec, nid, 1, dir, idx, ofs);
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get);
-
-/**
- * snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
- */
-int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = get_amp_nid(kcontrol);
- int chs = get_amp_channels(kcontrol);
- int dir = get_amp_direction(kcontrol);
- int idx = get_amp_index(kcontrol);
- unsigned int ofs = get_amp_offset(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int change = 0;
-
- snd_hda_power_up(codec);
- if (chs & 1) {
- change = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp);
- valp++;
- }
- if (chs & 2)
- change |= update_amp_value(codec, nid, 1, dir, idx, ofs, *valp);
- snd_hda_power_down(codec);
- return change;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put);
-
-/**
- * snd_hda_mixer_amp_volume_put - TLV callback for a standard AMP mixer volume
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
- */
-int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
- unsigned int size, unsigned int __user *_tlv)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = get_amp_nid(kcontrol);
- int dir = get_amp_direction(kcontrol);
- unsigned int ofs = get_amp_offset(kcontrol);
- bool min_mute = get_amp_min_mute(kcontrol);
- u32 caps, val1, val2;
-
- if (size < 4 * sizeof(unsigned int))
- return -ENOMEM;
- caps = query_amp_caps(codec, nid, dir);
- val2 = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
- val2 = (val2 + 1) * 25;
- val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
- val1 += ofs;
- val1 = ((int)val1) * ((int)val2);
- if (min_mute)
- val2 |= TLV_DB_SCALE_MUTE;
- if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
- return -EFAULT;
- if (put_user(2 * sizeof(unsigned int), _tlv + 1))
- return -EFAULT;
- if (put_user(val1, _tlv + 2))
- return -EFAULT;
- if (put_user(val2, _tlv + 3))
- return -EFAULT;
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv);
-
-/**
- * snd_hda_set_vmaster_tlv - Set TLV for a virtual master control
- * @codec: HD-audio codec
- * @nid: NID of a reference widget
- * @dir: #HDA_INPUT or #HDA_OUTPUT
- * @tlv: TLV data to be stored, at least 4 elements
- *
- * Set (static) TLV data for a virtual master volume using the AMP caps
- * obtained from the reference NID.
- * The volume range is recalculated as if the max volume is 0dB.
- */
-void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
- unsigned int *tlv)
-{
- u32 caps;
- int nums, step;
-
- caps = query_amp_caps(codec, nid, dir);
- nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
- step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
- step = (step + 1) * 25;
- tlv[0] = SNDRV_CTL_TLVT_DB_SCALE;
- tlv[1] = 2 * sizeof(unsigned int);
- tlv[2] = -nums * step;
- tlv[3] = step;
-}
-EXPORT_SYMBOL_HDA(snd_hda_set_vmaster_tlv);
-
-/* find a mixer control element with the given name */
-static struct snd_kcontrol *
-_snd_hda_find_mixer_ctl(struct hda_codec *codec,
- const char *name, int idx)
-{
- struct snd_ctl_elem_id id;
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- id.index = idx;
- if (snd_BUG_ON(strlen(name) >= sizeof(id.name)))
- return NULL;
- strcpy(id.name, name);
- return snd_ctl_find_id(codec->bus->card, &id);
-}
-
-/**
- * snd_hda_find_mixer_ctl - Find a mixer control element with the given name
- * @codec: HD-audio codec
- * @name: ctl id name string
- *
- * Get the control element with the given id string and IFACE_MIXER.
- */
-struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
- const char *name)
-{
- return _snd_hda_find_mixer_ctl(codec, name, 0);
-}
-EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
-
-/**
- * snd_hda_ctl_add - Add a control element and assign to the codec
- * @codec: HD-audio codec
- * @nid: corresponding NID (optional)
- * @kctl: the control element to assign
- *
- * Add the given control element to an array inside the codec instance.
- * All control elements belonging to a codec are supposed to be added
- * by this function so that a proper clean-up works at the free or
- * reconfiguration time.
- *
- * If non-zero @nid is passed, the NID is assigned to the control element.
- * The assignment is shown in the codec proc file.
- *
- * snd_hda_ctl_add() checks the control subdev id field whether
- * #HDA_SUBDEV_NID_FLAG bit is set. If set (and @nid is zero), the lower
- * bits value is taken as the NID to assign. The #HDA_NID_ITEM_AMP bit
- * specifies if kctl->private_value is a HDA amplifier value.
- */
-int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
- struct snd_kcontrol *kctl)
-{
- int err;
- unsigned short flags = 0;
- struct hda_nid_item *item;
-
- if (kctl->id.subdevice & HDA_SUBDEV_AMP_FLAG) {
- flags |= HDA_NID_ITEM_AMP;
- if (nid == 0)
- nid = get_amp_nid_(kctl->private_value);
- }
- if ((kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) != 0 && nid == 0)
- nid = kctl->id.subdevice & 0xffff;
- if (kctl->id.subdevice & (HDA_SUBDEV_NID_FLAG|HDA_SUBDEV_AMP_FLAG))
- kctl->id.subdevice = 0;
- err = snd_ctl_add(codec->bus->card, kctl);
- if (err < 0)
- return err;
- item = snd_array_new(&codec->mixers);
- if (!item)
- return -ENOMEM;
- item->kctl = kctl;
- item->nid = nid;
- item->flags = flags;
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
-
-/**
- * snd_hda_add_nid - Assign a NID to a control element
- * @codec: HD-audio codec
- * @nid: corresponding NID (optional)
- * @kctl: the control element to assign
- * @index: index to kctl
- *
- * Add the given control element to an array inside the codec instance.
- * This function is used when #snd_hda_ctl_add cannot be used for 1:1
- * NID:KCTL mapping - for example "Capture Source" selector.
- */
-int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl,
- unsigned int index, hda_nid_t nid)
-{
- struct hda_nid_item *item;
-
- if (nid > 0) {
- item = snd_array_new(&codec->nids);
- if (!item)
- return -ENOMEM;
- item->kctl = kctl;
- item->index = index;
- item->nid = nid;
- return 0;
- }
- printk(KERN_ERR "hda-codec: no NID for mapping control %s:%d:%d\n",
- kctl->id.name, kctl->id.index, index);
- return -EINVAL;
-}
-EXPORT_SYMBOL_HDA(snd_hda_add_nid);
-
-/**
- * snd_hda_ctls_clear - Clear all controls assigned to the given codec
- * @codec: HD-audio codec
- */
-void snd_hda_ctls_clear(struct hda_codec *codec)
-{
- int i;
- struct hda_nid_item *items = codec->mixers.list;
- for (i = 0; i < codec->mixers.used; i++)
- snd_ctl_remove(codec->bus->card, items[i].kctl);
- snd_array_free(&codec->mixers);
- snd_array_free(&codec->nids);
-}
-
-/* pseudo device locking
- * toggle card->shutdown to allow/disallow the device access (as a hack)
- */
-static int hda_lock_devices(struct snd_card *card)
-{
- spin_lock(&card->files_lock);
- if (card->shutdown) {
- spin_unlock(&card->files_lock);
- return -EINVAL;
- }
- card->shutdown = 1;
- spin_unlock(&card->files_lock);
- return 0;
-}
-
-static void hda_unlock_devices(struct snd_card *card)
-{
- spin_lock(&card->files_lock);
- card->shutdown = 0;
- spin_unlock(&card->files_lock);
-}
-
-/**
- * snd_hda_codec_reset - Clear all objects assigned to the codec
- * @codec: HD-audio codec
- *
- * This frees the all PCM and control elements assigned to the codec, and
- * clears the caches and restores the pin default configurations.
- *
- * When a device is being used, it returns -EBSY. If successfully freed,
- * returns zero.
- */
-int snd_hda_codec_reset(struct hda_codec *codec)
-{
- struct snd_card *card = codec->bus->card;
- int i, pcm;
-
- if (hda_lock_devices(card) < 0)
- return -EBUSY;
- /* check whether the codec isn't used by any mixer or PCM streams */
- if (!list_empty(&card->ctl_files)) {
- hda_unlock_devices(card);
- return -EBUSY;
- }
- for (pcm = 0; pcm < codec->num_pcms; pcm++) {
- struct hda_pcm *cpcm = &codec->pcm_info[pcm];
- if (!cpcm->pcm)
- continue;
- if (cpcm->pcm->streams[0].substream_opened ||
- cpcm->pcm->streams[1].substream_opened) {
- hda_unlock_devices(card);
- return -EBUSY;
- }
- }
-
- /* OK, let it free */
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- cancel_delayed_work(&codec->power_work);
- flush_workqueue(codec->bus->workq);
-#endif
- snd_hda_ctls_clear(codec);
- /* relase PCMs */
- for (i = 0; i < codec->num_pcms; i++) {
- if (codec->pcm_info[i].pcm) {
- snd_device_free(card, codec->pcm_info[i].pcm);
- clear_bit(codec->pcm_info[i].device,
- codec->bus->pcm_dev_bits);
- }
- }
- if (codec->patch_ops.free)
- codec->patch_ops.free(codec);
- codec->proc_widget_hook = NULL;
- codec->spec = NULL;
- free_hda_cache(&codec->amp_cache);
- free_hda_cache(&codec->cmd_cache);
- init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
- init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
- /* free only driver_pins so that init_pins + user_pins are restored */
- snd_array_free(&codec->driver_pins);
- restore_pincfgs(codec);
- codec->num_pcms = 0;
- codec->pcm_info = NULL;
- codec->preset = NULL;
- memset(&codec->patch_ops, 0, sizeof(codec->patch_ops));
- codec->slave_dig_outs = NULL;
- codec->spdif_status_reset = 0;
- module_put(codec->owner);
- codec->owner = NULL;
-
- /* allow device access again */
- hda_unlock_devices(card);
- return 0;
-}
-
-/**
- * snd_hda_add_vmaster - create a virtual master control and add slaves
- * @codec: HD-audio codec
- * @name: vmaster control name
- * @tlv: TLV data (optional)
- * @slaves: slave control names (optional)
- *
- * Create a virtual master control with the given name. The TLV data
- * must be either NULL or a valid data.
- *
- * @slaves is a NULL-terminated array of strings, each of which is a
- * slave control name. All controls with these names are assigned to
- * the new virtual master control.
- *
- * This function returns zero if successful or a negative error code.
- */
-int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
- unsigned int *tlv, const char **slaves)
-{
- struct snd_kcontrol *kctl;
- const char **s;
- int err;
-
- for (s = slaves; *s && !snd_hda_find_mixer_ctl(codec, *s); s++)
- ;
- if (!*s) {
- snd_printdd("No slave found for %s\n", name);
- return 0;
- }
- kctl = snd_ctl_make_virtual_master(name, tlv);
- if (!kctl)
- return -ENOMEM;
- err = snd_hda_ctl_add(codec, 0, kctl);
- if (err < 0)
- return err;
-
- for (s = slaves; *s; s++) {
- struct snd_kcontrol *sctl;
- int i = 0;
- for (;;) {
- sctl = _snd_hda_find_mixer_ctl(codec, *s, i);
- if (!sctl) {
- if (!i)
- snd_printdd("Cannot find slave %s, "
- "skipped\n", *s);
- break;
- }
- err = snd_ctl_add_slave(kctl, sctl);
- if (err < 0)
- return err;
- i++;
- }
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_add_vmaster);
-
-/**
- * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
- */
-int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- int chs = get_amp_channels(kcontrol);
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- uinfo->count = chs == 3 ? 2 : 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 1;
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info);
-
-/**
- * snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
- */
-int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = get_amp_nid(kcontrol);
- int chs = get_amp_channels(kcontrol);
- int dir = get_amp_direction(kcontrol);
- int idx = get_amp_index(kcontrol);
- long *valp = ucontrol->value.integer.value;
-
- if (chs & 1)
- *valp++ = (snd_hda_codec_amp_read(codec, nid, 0, dir, idx) &
- HDA_AMP_MUTE) ? 0 : 1;
- if (chs & 2)
- *valp = (snd_hda_codec_amp_read(codec, nid, 1, dir, idx) &
- HDA_AMP_MUTE) ? 0 : 1;
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get);
-
-/**
- * snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
- */
-int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = get_amp_nid(kcontrol);
- int chs = get_amp_channels(kcontrol);
- int dir = get_amp_direction(kcontrol);
- int idx = get_amp_index(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int change = 0;
-
- snd_hda_power_up(codec);
- if (chs & 1) {
- change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
- HDA_AMP_MUTE,
- *valp ? 0 : HDA_AMP_MUTE);
- valp++;
- }
- if (chs & 2)
- change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
- HDA_AMP_MUTE,
- *valp ? 0 : HDA_AMP_MUTE);
- hda_call_check_power_status(codec, nid);
- snd_hda_power_down(codec);
- return change;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-/**
- * snd_hda_mixer_amp_switch_put_beep - Put callback for a beep AMP switch
- *
- * This function calls snd_hda_enable_beep_device(), which behaves differently
- * depending on beep_mode option.
- */
-int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- long *valp = ucontrol->value.integer.value;
-
- snd_hda_enable_beep_device(codec, *valp);
- return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep);
-#endif /* CONFIG_SND_HDA_INPUT_BEEP */
-
-/*
- * bound volume controls
- *
- * bind multiple volumes (# indices, from 0)
- */
-
-#define AMP_VAL_IDX_SHIFT 19
-#define AMP_VAL_IDX_MASK (0x0f<<19)
-
-/**
- * snd_hda_mixer_bind_switch_get - Get callback for a bound volume control
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_BIND_MUTE*() macros.
- */
-int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned long pval;
- int err;
-
- mutex_lock(&codec->control_mutex);
- pval = kcontrol->private_value;
- kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */
- err = snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
- kcontrol->private_value = pval;
- mutex_unlock(&codec->control_mutex);
- return err;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get);
-
-/**
- * snd_hda_mixer_bind_switch_put - Put callback for a bound volume control
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_BIND_MUTE*() macros.
- */
-int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned long pval;
- int i, indices, err = 0, change = 0;
-
- mutex_lock(&codec->control_mutex);
- pval = kcontrol->private_value;
- indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT;
- for (i = 0; i < indices; i++) {
- kcontrol->private_value = (pval & ~AMP_VAL_IDX_MASK) |
- (i << AMP_VAL_IDX_SHIFT);
- err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
- if (err < 0)
- break;
- change |= err;
- }
- kcontrol->private_value = pval;
- mutex_unlock(&codec->control_mutex);
- return err < 0 ? err : change;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put);
-
-/**
- * snd_hda_mixer_bind_ctls_info - Info callback for a generic bound control
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
- */
-int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_bind_ctls *c;
- int err;
-
- mutex_lock(&codec->control_mutex);
- c = (struct hda_bind_ctls *)kcontrol->private_value;
- kcontrol->private_value = *c->values;
- err = c->ops->info(kcontrol, uinfo);
- kcontrol->private_value = (long)c;
- mutex_unlock(&codec->control_mutex);
- return err;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info);
-
-/**
- * snd_hda_mixer_bind_ctls_get - Get callback for a generic bound control
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
- */
-int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_bind_ctls *c;
- int err;
-
- mutex_lock(&codec->control_mutex);
- c = (struct hda_bind_ctls *)kcontrol->private_value;
- kcontrol->private_value = *c->values;
- err = c->ops->get(kcontrol, ucontrol);
- kcontrol->private_value = (long)c;
- mutex_unlock(&codec->control_mutex);
- return err;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get);
-
-/**
- * snd_hda_mixer_bind_ctls_put - Put callback for a generic bound control
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
- */
-int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_bind_ctls *c;
- unsigned long *vals;
- int err = 0, change = 0;
-
- mutex_lock(&codec->control_mutex);
- c = (struct hda_bind_ctls *)kcontrol->private_value;
- for (vals = c->values; *vals; vals++) {
- kcontrol->private_value = *vals;
- err = c->ops->put(kcontrol, ucontrol);
- if (err < 0)
- break;
- change |= err;
- }
- kcontrol->private_value = (long)c;
- mutex_unlock(&codec->control_mutex);
- return err < 0 ? err : change;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put);
-
-/**
- * snd_hda_mixer_bind_tlv - TLV callback for a generic bound control
- *
- * The control element is supposed to have the private_value field
- * set up via HDA_BIND_VOL() macro.
- */
-int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
- unsigned int size, unsigned int __user *tlv)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_bind_ctls *c;
- int err;
-
- mutex_lock(&codec->control_mutex);
- c = (struct hda_bind_ctls *)kcontrol->private_value;
- kcontrol->private_value = *c->values;
- err = c->ops->tlv(kcontrol, op_flag, size, tlv);
- kcontrol->private_value = (long)c;
- mutex_unlock(&codec->control_mutex);
- return err;
-}
-EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_tlv);
-
-struct hda_ctl_ops snd_hda_bind_vol = {
- .info = snd_hda_mixer_amp_volume_info,
- .get = snd_hda_mixer_amp_volume_get,
- .put = snd_hda_mixer_amp_volume_put,
- .tlv = snd_hda_mixer_amp_tlv
-};
-EXPORT_SYMBOL_HDA(snd_hda_bind_vol);
-
-struct hda_ctl_ops snd_hda_bind_sw = {
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = snd_hda_mixer_amp_switch_put,
- .tlv = snd_hda_mixer_amp_tlv
-};
-EXPORT_SYMBOL_HDA(snd_hda_bind_sw);
-
-/*
- * SPDIF out controls
- */
-
-static int snd_hda_spdif_mask_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
- uinfo->count = 1;
- return 0;
-}
-
-static int snd_hda_spdif_cmask_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
- IEC958_AES0_NONAUDIO |
- IEC958_AES0_CON_EMPHASIS_5015 |
- IEC958_AES0_CON_NOT_COPYRIGHT;
- ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY |
- IEC958_AES1_CON_ORIGINAL;
- return 0;
-}
-
-static int snd_hda_spdif_pmask_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
- IEC958_AES0_NONAUDIO |
- IEC958_AES0_PRO_EMPHASIS_5015;
- return 0;
-}
-
-static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-
- ucontrol->value.iec958.status[0] = codec->spdif_status & 0xff;
- ucontrol->value.iec958.status[1] = (codec->spdif_status >> 8) & 0xff;
- ucontrol->value.iec958.status[2] = (codec->spdif_status >> 16) & 0xff;
- ucontrol->value.iec958.status[3] = (codec->spdif_status >> 24) & 0xff;
-
- return 0;
-}
-
-/* convert from SPDIF status bits to HDA SPDIF bits
- * bit 0 (DigEn) is always set zero (to be filled later)
- */
-static unsigned short convert_from_spdif_status(unsigned int sbits)
-{
- unsigned short val = 0;
-
- if (sbits & IEC958_AES0_PROFESSIONAL)
- val |= AC_DIG1_PROFESSIONAL;
- if (sbits & IEC958_AES0_NONAUDIO)
- val |= AC_DIG1_NONAUDIO;
- if (sbits & IEC958_AES0_PROFESSIONAL) {
- if ((sbits & IEC958_AES0_PRO_EMPHASIS) ==
- IEC958_AES0_PRO_EMPHASIS_5015)
- val |= AC_DIG1_EMPHASIS;
- } else {
- if ((sbits & IEC958_AES0_CON_EMPHASIS) ==
- IEC958_AES0_CON_EMPHASIS_5015)
- val |= AC_DIG1_EMPHASIS;
- if (!(sbits & IEC958_AES0_CON_NOT_COPYRIGHT))
- val |= AC_DIG1_COPYRIGHT;
- if (sbits & (IEC958_AES1_CON_ORIGINAL << 8))
- val |= AC_DIG1_LEVEL;
- val |= sbits & (IEC958_AES1_CON_CATEGORY << 8);
- }
- return val;
-}
-
-/* convert to SPDIF status bits from HDA SPDIF bits
- */
-static unsigned int convert_to_spdif_status(unsigned short val)
-{
- unsigned int sbits = 0;
-
- if (val & AC_DIG1_NONAUDIO)
- sbits |= IEC958_AES0_NONAUDIO;
- if (val & AC_DIG1_PROFESSIONAL)
- sbits |= IEC958_AES0_PROFESSIONAL;
- if (sbits & IEC958_AES0_PROFESSIONAL) {
- if (sbits & AC_DIG1_EMPHASIS)
- sbits |= IEC958_AES0_PRO_EMPHASIS_5015;
- } else {
- if (val & AC_DIG1_EMPHASIS)
- sbits |= IEC958_AES0_CON_EMPHASIS_5015;
- if (!(val & AC_DIG1_COPYRIGHT))
- sbits |= IEC958_AES0_CON_NOT_COPYRIGHT;
- if (val & AC_DIG1_LEVEL)
- sbits |= (IEC958_AES1_CON_ORIGINAL << 8);
- sbits |= val & (0x7f << 8);
- }
- return sbits;
-}
-
-/* set digital convert verbs both for the given NID and its slaves */
-static void set_dig_out(struct hda_codec *codec, hda_nid_t nid,
- int verb, int val)
-{
- hda_nid_t *d;
-
- snd_hda_codec_write_cache(codec, nid, 0, verb, val);
- d = codec->slave_dig_outs;
- if (!d)
- return;
- for (; *d; d++)
- snd_hda_codec_write_cache(codec, *d, 0, verb, val);
-}
-
-static inline void set_dig_out_convert(struct hda_codec *codec, hda_nid_t nid,
- int dig1, int dig2)
-{
- if (dig1 != -1)
- set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_1, dig1);
- if (dig2 != -1)
- set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_2, dig2);
-}
-
-static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
- unsigned short val;
- int change;
-
- mutex_lock(&codec->spdif_mutex);
- codec->spdif_status = ucontrol->value.iec958.status[0] |
- ((unsigned int)ucontrol->value.iec958.status[1] << 8) |
- ((unsigned int)ucontrol->value.iec958.status[2] << 16) |
- ((unsigned int)ucontrol->value.iec958.status[3] << 24);
- val = convert_from_spdif_status(codec->spdif_status);
- val |= codec->spdif_ctls & 1;
- change = codec->spdif_ctls != val;
- codec->spdif_ctls = val;
-
- if (change)
- set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff);
-
- mutex_unlock(&codec->spdif_mutex);
- return change;
-}
-
-#define snd_hda_spdif_out_switch_info snd_ctl_boolean_mono_info
-
-static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-
- ucontrol->value.integer.value[0] = codec->spdif_ctls & AC_DIG1_ENABLE;
- return 0;
-}
-
-static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
- unsigned short val;
- int change;
-
- mutex_lock(&codec->spdif_mutex);
- val = codec->spdif_ctls & ~AC_DIG1_ENABLE;
- if (ucontrol->value.integer.value[0])
- val |= AC_DIG1_ENABLE;
- change = codec->spdif_ctls != val;
- if (change) {
- codec->spdif_ctls = val;
- set_dig_out_convert(codec, nid, val & 0xff, -1);
- /* unmute amp switch (if any) */
- if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
- (val & AC_DIG1_ENABLE))
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, 0);
- }
- mutex_unlock(&codec->spdif_mutex);
- return change;
-}
-
-static struct snd_kcontrol_new dig_mixes[] = {
- {
- .access = SNDRV_CTL_ELEM_ACCESS_READ,
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
- .info = snd_hda_spdif_mask_info,
- .get = snd_hda_spdif_cmask_get,
- },
- {
- .access = SNDRV_CTL_ELEM_ACCESS_READ,
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),
- .info = snd_hda_spdif_mask_info,
- .get = snd_hda_spdif_pmask_get,
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
- .info = snd_hda_spdif_mask_info,
- .get = snd_hda_spdif_default_get,
- .put = snd_hda_spdif_default_put,
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
- .info = snd_hda_spdif_out_switch_info,
- .get = snd_hda_spdif_out_switch_get,
- .put = snd_hda_spdif_out_switch_put,
- },
- { } /* end */
-};
-
-#define SPDIF_MAX_IDX 4 /* 4 instances should be enough to probe */
-
-/**
- * snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls
- * @codec: the HDA codec
- * @nid: audio out widget NID
- *
- * Creates controls related with the SPDIF output.
- * Called from each patch supporting the SPDIF out.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
-{
- int err;
- struct snd_kcontrol *kctl;
- struct snd_kcontrol_new *dig_mix;
- int idx;
-
- for (idx = 0; idx < SPDIF_MAX_IDX; idx++) {
- if (!_snd_hda_find_mixer_ctl(codec, "IEC958 Playback Switch",
- idx))
- break;
- }
- if (idx >= SPDIF_MAX_IDX) {
- printk(KERN_ERR "hda_codec: too many IEC958 outputs\n");
- return -EBUSY;
- }
- for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
- kctl = snd_ctl_new1(dig_mix, codec);
- if (!kctl)
- return -ENOMEM;
- kctl->id.index = idx;
- kctl->private_value = nid;
- err = snd_hda_ctl_add(codec, nid, kctl);
- if (err < 0)
- return err;
- }
- codec->spdif_ctls =
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_DIGI_CONVERT_1, 0);
- codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls);
-
-/*
- * SPDIF sharing with analog output
- */
-static int spdif_share_sw_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
- ucontrol->value.integer.value[0] = mout->share_spdif;
- return 0;
-}
-
-static int spdif_share_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_multi_out *mout = snd_kcontrol_chip(kcontrol);
- mout->share_spdif = !!ucontrol->value.integer.value[0];
- return 0;
-}
-
-static struct snd_kcontrol_new spdif_share_sw = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "IEC958 Default PCM Playback Switch",
- .info = snd_ctl_boolean_mono_info,
- .get = spdif_share_sw_get,
- .put = spdif_share_sw_put,
-};
-
-/**
- * snd_hda_create_spdif_share_sw - create Default PCM switch
- * @codec: the HDA codec
- * @mout: multi-out instance
- */
-int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
- struct hda_multi_out *mout)
-{
- if (!mout->dig_out_nid)
- return 0;
- /* ATTENTION: here mout is passed as private_data, instead of codec */
- return snd_hda_ctl_add(codec, mout->dig_out_nid,
- snd_ctl_new1(&spdif_share_sw, mout));
-}
-EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw);
-
-/*
- * SPDIF input
- */
-
-#define snd_hda_spdif_in_switch_info snd_hda_spdif_out_switch_info
-
-static int snd_hda_spdif_in_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-
- ucontrol->value.integer.value[0] = codec->spdif_in_enable;
- return 0;
-}
-
-static int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
- unsigned int val = !!ucontrol->value.integer.value[0];
- int change;
-
- mutex_lock(&codec->spdif_mutex);
- change = codec->spdif_in_enable != val;
- if (change) {
- codec->spdif_in_enable = val;
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_DIGI_CONVERT_1, val);
- }
- mutex_unlock(&codec->spdif_mutex);
- return change;
-}
-
-static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
- unsigned short val;
- unsigned int sbits;
-
- val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0);
- sbits = convert_to_spdif_status(val);
- ucontrol->value.iec958.status[0] = sbits;
- ucontrol->value.iec958.status[1] = sbits >> 8;
- ucontrol->value.iec958.status[2] = sbits >> 16;
- ucontrol->value.iec958.status[3] = sbits >> 24;
- return 0;
-}
-
-static struct snd_kcontrol_new dig_in_ctls[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH),
- .info = snd_hda_spdif_in_switch_info,
- .get = snd_hda_spdif_in_switch_get,
- .put = snd_hda_spdif_in_switch_put,
- },
- {
- .access = SNDRV_CTL_ELEM_ACCESS_READ,
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
- .info = snd_hda_spdif_mask_info,
- .get = snd_hda_spdif_in_status_get,
- },
- { } /* end */
-};
-
-/**
- * snd_hda_create_spdif_in_ctls - create Input SPDIF-related controls
- * @codec: the HDA codec
- * @nid: audio in widget NID
- *
- * Creates controls related with the SPDIF input.
- * Called from each patch supporting the SPDIF in.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
-{
- int err;
- struct snd_kcontrol *kctl;
- struct snd_kcontrol_new *dig_mix;
- int idx;
-
- for (idx = 0; idx < SPDIF_MAX_IDX; idx++) {
- if (!_snd_hda_find_mixer_ctl(codec, "IEC958 Capture Switch",
- idx))
- break;
- }
- if (idx >= SPDIF_MAX_IDX) {
- printk(KERN_ERR "hda_codec: too many IEC958 inputs\n");
- return -EBUSY;
- }
- for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
- kctl = snd_ctl_new1(dig_mix, codec);
- if (!kctl)
- return -ENOMEM;
- kctl->private_value = nid;
- err = snd_hda_ctl_add(codec, nid, kctl);
- if (err < 0)
- return err;
- }
- codec->spdif_in_enable =
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_DIGI_CONVERT_1, 0) &
- AC_DIG1_ENABLE;
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
-
-#ifdef SND_HDA_NEEDS_RESUME
-/*
- * command cache
- */
-
-/* build a 32bit cache key with the widget id and the command parameter */
-#define build_cmd_cache_key(nid, verb) ((verb << 8) | nid)
-#define get_cmd_cache_nid(key) ((key) & 0xff)
-#define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff)
-
-/**
- * snd_hda_codec_write_cache - send a single command with caching
- * @codec: the HDA codec
- * @nid: NID to send the command
- * @direct: direct flag
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command without waiting for response.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb, unsigned int parm)
-{
- int err = snd_hda_codec_write(codec, nid, direct, verb, parm);
- struct hda_cache_head *c;
- u32 key;
-
- if (err < 0)
- return err;
- /* parm may contain the verb stuff for get/set amp */
- verb = verb | (parm >> 8);
- parm &= 0xff;
- key = build_cmd_cache_key(nid, verb);
- mutex_lock(&codec->bus->cmd_mutex);
- c = get_alloc_hash(&codec->cmd_cache, key);
- if (c)
- c->val = parm;
- mutex_unlock(&codec->bus->cmd_mutex);
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
-
-/**
- * snd_hda_codec_update_cache - check cache and write the cmd only when needed
- * @codec: the HDA codec
- * @nid: NID to send the command
- * @direct: direct flag
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * This function works like snd_hda_codec_write_cache(), but it doesn't send
- * command if the parameter is already identical with the cached value.
- * If not, it sends the command and refreshes the cache.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb, unsigned int parm)
-{
- struct hda_cache_head *c;
- u32 key;
-
- /* parm may contain the verb stuff for get/set amp */
- verb = verb | (parm >> 8);
- parm &= 0xff;
- key = build_cmd_cache_key(nid, verb);
- mutex_lock(&codec->bus->cmd_mutex);
- c = get_hash(&codec->cmd_cache, key);
- if (c && c->val == parm) {
- mutex_unlock(&codec->bus->cmd_mutex);
- return 0;
- }
- mutex_unlock(&codec->bus->cmd_mutex);
- return snd_hda_codec_write_cache(codec, nid, direct, verb, parm);
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache);
-
-/**
- * snd_hda_codec_resume_cache - Resume the all commands from the cache
- * @codec: HD-audio codec
- *
- * Execute all verbs recorded in the command caches to resume.
- */
-void snd_hda_codec_resume_cache(struct hda_codec *codec)
-{
- struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
- int i;
-
- for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) {
- u32 key = buffer->key;
- if (!key)
- continue;
- snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0,
- get_cmd_cache_cmd(key), buffer->val);
- }
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache);
-
-/**
- * snd_hda_sequence_write_cache - sequence writes with caching
- * @codec: the HDA codec
- * @seq: VERB array to send
- *
- * Send the commands sequentially from the given array.
- * Thte commands are recorded on cache for power-save and resume.
- * The array must be terminated with NID=0.
- */
-void snd_hda_sequence_write_cache(struct hda_codec *codec,
- const struct hda_verb *seq)
-{
- for (; seq->nid; seq++)
- snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb,
- seq->param);
-}
-EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
-#endif /* SND_HDA_NEEDS_RESUME */
-
-/*
- * set power state of the codec
- */
-static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
- unsigned int power_state)
-{
- hda_nid_t nid;
- int i;
-
- /* this delay seems necessary to avoid click noise at power-down */
- if (power_state == AC_PWRST_D3)
- msleep(100);
- snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
- power_state);
- /* partial workaround for "azx_get_response timeout" */
- if (power_state == AC_PWRST_D0 &&
- (codec->vendor_id & 0xffff0000) == 0x14f10000)
- msleep(10);
-
- nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, nid++) {
- unsigned int wcaps = get_wcaps(codec, nid);
- if (wcaps & AC_WCAP_POWER) {
- unsigned int wid_type = get_wcaps_type(wcaps);
- if (power_state == AC_PWRST_D3 &&
- wid_type == AC_WID_PIN) {
- unsigned int pincap;
- /*
- * don't power down the widget if it controls
- * eapd and EAPD_BTLENABLE is set.
- */
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_EAPD) {
- int eapd = snd_hda_codec_read(codec,
- nid, 0,
- AC_VERB_GET_EAPD_BTLENABLE, 0);
- eapd &= 0x02;
- if (eapd)
- continue;
- }
- }
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_POWER_STATE,
- power_state);
- }
- }
-
- if (power_state == AC_PWRST_D0) {
- unsigned long end_time;
- int state;
- /* wait until the codec reachs to D0 */
- end_time = jiffies + msecs_to_jiffies(500);
- do {
- state = snd_hda_codec_read(codec, fg, 0,
- AC_VERB_GET_POWER_STATE, 0);
- if (state == power_state)
- break;
- msleep(1);
- } while (time_after_eq(end_time, jiffies));
- }
-}
-
-#ifdef CONFIG_SND_HDA_HWDEP
-/* execute additional init verbs */
-static void hda_exec_init_verbs(struct hda_codec *codec)
-{
- if (codec->init_verbs.list)
- snd_hda_sequence_write(codec, codec->init_verbs.list);
-}
-#else
-static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
-#endif
-
-#ifdef SND_HDA_NEEDS_RESUME
-/*
- * call suspend and power-down; used both from PM and power-save
- */
-static void hda_call_codec_suspend(struct hda_codec *codec)
-{
- if (codec->patch_ops.suspend)
- codec->patch_ops.suspend(codec, PMSG_SUSPEND);
- hda_cleanup_all_streams(codec);
- hda_set_power_state(codec,
- codec->afg ? codec->afg : codec->mfg,
- AC_PWRST_D3);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- snd_hda_update_power_acct(codec);
- cancel_delayed_work(&codec->power_work);
- codec->power_on = 0;
- codec->power_transition = 0;
- codec->power_jiffies = jiffies;
-#endif
-}
-
-/*
- * kick up codec; used both from PM and power-save
- */
-static void hda_call_codec_resume(struct hda_codec *codec)
-{
- hda_set_power_state(codec,
- codec->afg ? codec->afg : codec->mfg,
- AC_PWRST_D0);
- restore_pincfgs(codec); /* restore all current pin configs */
- restore_shutup_pins(codec);
- hda_exec_init_verbs(codec);
- if (codec->patch_ops.resume)
- codec->patch_ops.resume(codec);
- else {
- if (codec->patch_ops.init)
- codec->patch_ops.init(codec);
- snd_hda_codec_resume_amp(codec);
- snd_hda_codec_resume_cache(codec);
- }
-}
-#endif /* SND_HDA_NEEDS_RESUME */
-
-
-/**
- * snd_hda_build_controls - build mixer controls
- * @bus: the BUS
- *
- * Creates mixer controls for each codec included in the bus.
- *
- * Returns 0 if successful, otherwise a negative error code.
- */
-int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus)
-{
- struct hda_codec *codec;
-
- list_for_each_entry(codec, &bus->codec_list, list) {
- int err = snd_hda_codec_build_controls(codec);
- if (err < 0) {
- printk(KERN_ERR "hda_codec: cannot build controls "
- "for #%d (error %d)\n", codec->addr, err);
- err = snd_hda_codec_reset(codec);
- if (err < 0) {
- printk(KERN_ERR
- "hda_codec: cannot revert codec\n");
- return err;
- }
- }
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_build_controls);
-
-int snd_hda_codec_build_controls(struct hda_codec *codec)
-{
- int err = 0;
- hda_exec_init_verbs(codec);
- /* continue to initialize... */
- if (codec->patch_ops.init)
- err = codec->patch_ops.init(codec);
- if (!err && codec->patch_ops.build_controls)
- err = codec->patch_ops.build_controls(codec);
- if (err < 0)
- return err;
- return 0;
-}
-
-/*
- * stream formats
- */
-struct hda_rate_tbl {
- unsigned int hz;
- unsigned int alsa_bits;
- unsigned int hda_fmt;
-};
-
-/* rate = base * mult / div */
-#define HDA_RATE(base, mult, div) \
- (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \
- (((div) - 1) << AC_FMT_DIV_SHIFT))
-
-static struct hda_rate_tbl rate_bits[] = {
- /* rate in Hz, ALSA rate bitmask, HDA format value */
-
- /* autodetected value used in snd_hda_query_supported_pcm */
- { 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) },
- { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) },
- { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) },
- { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) },
- { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) },
- { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) },
- { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) },
- { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) },
- { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) },
- { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) },
- { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) },
-#define AC_PAR_PCM_RATE_BITS 11
- /* up to bits 10, 384kHZ isn't supported properly */
-
- /* not autodetected value */
- { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) },
-
- { 0 } /* terminator */
-};
-
-/**
- * snd_hda_calc_stream_format - calculate format bitset
- * @rate: the sample rate
- * @channels: the number of channels
- * @format: the PCM format (SNDRV_PCM_FORMAT_XXX)
- * @maxbps: the max. bps
- *
- * Calculate the format bitset from the given rate, channels and th PCM format.
- *
- * Return zero if invalid.
- */
-unsigned int snd_hda_calc_stream_format(unsigned int rate,
- unsigned int channels,
- unsigned int format,
- unsigned int maxbps,
- unsigned short spdif_ctls)
-{
- int i;
- unsigned int val = 0;
-
- for (i = 0; rate_bits[i].hz; i++)
- if (rate_bits[i].hz == rate) {
- val = rate_bits[i].hda_fmt;
- break;
- }
- if (!rate_bits[i].hz) {
- snd_printdd("invalid rate %d\n", rate);
- return 0;
- }
-
- if (channels == 0 || channels > 8) {
- snd_printdd("invalid channels %d\n", channels);
- return 0;
- }
- val |= channels - 1;
-
- switch (snd_pcm_format_width(format)) {
- case 8:
- val |= AC_FMT_BITS_8;
- break;
- case 16:
- val |= AC_FMT_BITS_16;
- break;
- case 20:
- case 24:
- case 32:
- if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE)
- val |= AC_FMT_BITS_32;
- else if (maxbps >= 24)
- val |= AC_FMT_BITS_24;
- else
- val |= AC_FMT_BITS_20;
- break;
- default:
- snd_printdd("invalid format width %d\n",
- snd_pcm_format_width(format));
- return 0;
- }
-
- if (spdif_ctls & AC_DIG1_NONAUDIO)
- val |= AC_FMT_TYPE_NON_PCM;
-
- return val;
-}
-EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
-
-static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int val = 0;
- if (nid != codec->afg &&
- (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD))
- val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
- if (!val || val == -1)
- val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
- if (!val || val == -1)
- return 0;
- return val;
-}
-
-static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
-{
- return query_caps_hash(codec, nid, HDA_HASH_PARPCM_KEY(nid),
- get_pcm_param);
-}
-
-static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
- if (!streams || streams == -1)
- streams = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
- if (!streams || streams == -1)
- return 0;
- return streams;
-}
-
-static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
-{
- return query_caps_hash(codec, nid, HDA_HASH_PARSTR_KEY(nid),
- get_stream_param);
-}
-
-/**
- * snd_hda_query_supported_pcm - query the supported PCM rates and formats
- * @codec: the HDA codec
- * @nid: NID to query
- * @ratesp: the pointer to store the detected rate bitflags
- * @formatsp: the pointer to store the detected formats
- * @bpsp: the pointer to store the detected format widths
- *
- * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp
- * or @bsps argument is ignored.
- *
- * Returns 0 if successful, otherwise a negative error code.
- */
-static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
- u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
-{
- unsigned int i, val, wcaps;
-
- wcaps = get_wcaps(codec, nid);
- val = query_pcm_param(codec, nid);
-
- if (ratesp) {
- u32 rates = 0;
- for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) {
- if (val & (1 << i))
- rates |= rate_bits[i].alsa_bits;
- }
- if (rates == 0) {
- snd_printk(KERN_ERR "hda_codec: rates == 0 "
- "(nid=0x%x, val=0x%x, ovrd=%i)\n",
- nid, val,
- (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0);
- return -EIO;
- }
- *ratesp = rates;
- }
-
- if (formatsp || bpsp) {
- u64 formats = 0;
- unsigned int streams, bps;
-
- streams = query_stream_param(codec, nid);
- if (!streams)
- return -EIO;
-
- bps = 0;
- if (streams & AC_SUPFMT_PCM) {
- if (val & AC_SUPPCM_BITS_8) {
- formats |= SNDRV_PCM_FMTBIT_U8;
- bps = 8;
- }
- if (val & AC_SUPPCM_BITS_16) {
- formats |= SNDRV_PCM_FMTBIT_S16_LE;
- bps = 16;
- }
- if (wcaps & AC_WCAP_DIGITAL) {
- if (val & AC_SUPPCM_BITS_32)
- formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
- if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24))
- formats |= SNDRV_PCM_FMTBIT_S32_LE;
- if (val & AC_SUPPCM_BITS_24)
- bps = 24;
- else if (val & AC_SUPPCM_BITS_20)
- bps = 20;
- } else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24|
- AC_SUPPCM_BITS_32)) {
- formats |= SNDRV_PCM_FMTBIT_S32_LE;
- if (val & AC_SUPPCM_BITS_32)
- bps = 32;
- else if (val & AC_SUPPCM_BITS_24)
- bps = 24;
- else if (val & AC_SUPPCM_BITS_20)
- bps = 20;
- }
- }
- if (streams & AC_SUPFMT_FLOAT32) {
- formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
- if (!bps)
- bps = 32;
- }
- if (streams == AC_SUPFMT_AC3) {
- /* should be exclusive */
- /* temporary hack: we have still no proper support
- * for the direct AC3 stream...
- */
- formats |= SNDRV_PCM_FMTBIT_U8;
- bps = 8;
- }
- if (formats == 0) {
- snd_printk(KERN_ERR "hda_codec: formats == 0 "
- "(nid=0x%x, val=0x%x, ovrd=%i, "
- "streams=0x%x)\n",
- nid, val,
- (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0,
- streams);
- return -EIO;
- }
- if (formatsp)
- *formatsp = formats;
- if (bpsp)
- *bpsp = bps;
- }
-
- return 0;
-}
-
-/**
- * snd_hda_is_supported_format - Check the validity of the format
- * @codec: HD-audio codec
- * @nid: NID to check
- * @format: the HD-audio format value to check
- *
- * Check whether the given node supports the format value.
- *
- * Returns 1 if supported, 0 if not.
- */
-int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
- unsigned int format)
-{
- int i;
- unsigned int val = 0, rate, stream;
-
- val = query_pcm_param(codec, nid);
- if (!val)
- return 0;
-
- rate = format & 0xff00;
- for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++)
- if (rate_bits[i].hda_fmt == rate) {
- if (val & (1 << i))
- break;
- return 0;
- }
- if (i >= AC_PAR_PCM_RATE_BITS)
- return 0;
-
- stream = query_stream_param(codec, nid);
- if (!stream)
- return 0;
-
- if (stream & AC_SUPFMT_PCM) {
- switch (format & 0xf0) {
- case 0x00:
- if (!(val & AC_SUPPCM_BITS_8))
- return 0;
- break;
- case 0x10:
- if (!(val & AC_SUPPCM_BITS_16))
- return 0;
- break;
- case 0x20:
- if (!(val & AC_SUPPCM_BITS_20))
- return 0;
- break;
- case 0x30:
- if (!(val & AC_SUPPCM_BITS_24))
- return 0;
- break;
- case 0x40:
- if (!(val & AC_SUPPCM_BITS_32))
- return 0;
- break;
- default:
- return 0;
- }
- } else {
- /* FIXME: check for float32 and AC3? */
- }
-
- return 1;
-}
-EXPORT_SYMBOL_HDA(snd_hda_is_supported_format);
-
-/*
- * PCM stuff
- */
-static int hda_pcm_default_open_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- return 0;
-}
-
-static int hda_pcm_default_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
- return 0;
-}
-
-static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- snd_hda_codec_cleanup_stream(codec, hinfo->nid);
- return 0;
-}
-
-static int set_pcm_default_values(struct hda_codec *codec,
- struct hda_pcm_stream *info)
-{
- int err;
-
- /* query support PCM information from the given NID */
- if (info->nid && (!info->rates || !info->formats)) {
- err = snd_hda_query_supported_pcm(codec, info->nid,
- info->rates ? NULL : &info->rates,
- info->formats ? NULL : &info->formats,
- info->maxbps ? NULL : &info->maxbps);
- if (err < 0)
- return err;
- }
- if (info->ops.open == NULL)
- info->ops.open = hda_pcm_default_open_close;
- if (info->ops.close == NULL)
- info->ops.close = hda_pcm_default_open_close;
- if (info->ops.prepare == NULL) {
- if (snd_BUG_ON(!info->nid))
- return -EINVAL;
- info->ops.prepare = hda_pcm_default_prepare;
- }
- if (info->ops.cleanup == NULL) {
- if (snd_BUG_ON(!info->nid))
- return -EINVAL;
- info->ops.cleanup = hda_pcm_default_cleanup;
- }
- return 0;
-}
-
-/*
- * codec prepare/cleanup entries
- */
-int snd_hda_codec_prepare(struct hda_codec *codec,
- struct hda_pcm_stream *hinfo,
- unsigned int stream,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- int ret;
- mutex_lock(&codec->bus->prepare_mutex);
- ret = hinfo->ops.prepare(hinfo, codec, stream, format, substream);
- if (ret >= 0)
- purify_inactive_streams(codec);
- mutex_unlock(&codec->bus->prepare_mutex);
- return ret;
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_prepare);
-
-void snd_hda_codec_cleanup(struct hda_codec *codec,
- struct hda_pcm_stream *hinfo,
- struct snd_pcm_substream *substream)
-{
- mutex_lock(&codec->bus->prepare_mutex);
- hinfo->ops.cleanup(hinfo, codec, substream);
- mutex_unlock(&codec->bus->prepare_mutex);
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup);
-
-/* global */
-const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
- "Audio", "SPDIF", "HDMI", "Modem"
-};
-
-/*
- * get the empty PCM device number to assign
- *
- * note the max device number is limited by HDA_MAX_PCMS, currently 10
- */
-static int get_empty_pcm_device(struct hda_bus *bus, int type)
-{
- /* audio device indices; not linear to keep compatibility */
- static int audio_idx[HDA_PCM_NTYPES][5] = {
- [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 },
- [HDA_PCM_TYPE_SPDIF] = { 1, -1 },
- [HDA_PCM_TYPE_HDMI] = { 3, 7, 8, 9, -1 },
- [HDA_PCM_TYPE_MODEM] = { 6, -1 },
- };
- int i;
-
- if (type >= HDA_PCM_NTYPES) {
- snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
- return -EINVAL;
- }
-
- for (i = 0; audio_idx[type][i] >= 0 ; i++)
- if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits))
- return audio_idx[type][i];
-
- snd_printk(KERN_WARNING "Too many %s devices\n",
- snd_hda_pcm_type_name[type]);
- return -EAGAIN;
-}
-
-/*
- * attach a new PCM stream
- */
-static int snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm)
-{
- struct hda_bus *bus = codec->bus;
- struct hda_pcm_stream *info;
- int stream, err;
-
- if (snd_BUG_ON(!pcm->name))
- return -EINVAL;
- for (stream = 0; stream < 2; stream++) {
- info = &pcm->stream[stream];
- if (info->substreams) {
- err = set_pcm_default_values(codec, info);
- if (err < 0)
- return err;
- }
- }
- return bus->ops.attach_pcm(bus, codec, pcm);
-}
-
-/* assign all PCMs of the given codec */
-int snd_hda_codec_build_pcms(struct hda_codec *codec)
-{
- unsigned int pcm;
- int err;
-
- if (!codec->num_pcms) {
- if (!codec->patch_ops.build_pcms)
- return 0;
- err = codec->patch_ops.build_pcms(codec);
- if (err < 0) {
- printk(KERN_ERR "hda_codec: cannot build PCMs"
- "for #%d (error %d)\n", codec->addr, err);
- err = snd_hda_codec_reset(codec);
- if (err < 0) {
- printk(KERN_ERR
- "hda_codec: cannot revert codec\n");
- return err;
- }
- }
- }
- for (pcm = 0; pcm < codec->num_pcms; pcm++) {
- struct hda_pcm *cpcm = &codec->pcm_info[pcm];
- int dev;
-
- if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
- continue; /* no substreams assigned */
-
- if (!cpcm->pcm) {
- dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type);
- if (dev < 0)
- continue; /* no fatal error */
- cpcm->device = dev;
- err = snd_hda_attach_pcm(codec, cpcm);
- if (err < 0) {
- printk(KERN_ERR "hda_codec: cannot attach "
- "PCM stream %d for codec #%d\n",
- dev, codec->addr);
- continue; /* no fatal error */
- }
- }
- }
- return 0;
-}
-
-/**
- * snd_hda_build_pcms - build PCM information
- * @bus: the BUS
- *
- * Create PCM information for each codec included in the bus.
- *
- * The build_pcms codec patch is requested to set up codec->num_pcms and
- * codec->pcm_info properly. The array is referred by the top-level driver
- * to create its PCM instances.
- * The allocated codec->pcm_info should be released in codec->patch_ops.free
- * callback.
- *
- * At least, substreams, channels_min and channels_max must be filled for
- * each stream. substreams = 0 indicates that the stream doesn't exist.
- * When rates and/or formats are zero, the supported values are queried
- * from the given nid. The nid is used also by the default ops.prepare
- * and ops.cleanup callbacks.
- *
- * The driver needs to call ops.open in its open callback. Similarly,
- * ops.close is supposed to be called in the close callback.
- * ops.prepare should be called in the prepare or hw_params callback
- * with the proper parameters for set up.
- * ops.cleanup should be called in hw_free for clean up of streams.
- *
- * This function returns 0 if successfull, or a negative error code.
- */
-int __devinit snd_hda_build_pcms(struct hda_bus *bus)
-{
- struct hda_codec *codec;
-
- list_for_each_entry(codec, &bus->codec_list, list) {
- int err = snd_hda_codec_build_pcms(codec);
- if (err < 0)
- return err;
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_build_pcms);
-
-/**
- * snd_hda_check_board_config - compare the current codec with the config table
- * @codec: the HDA codec
- * @num_configs: number of config enums
- * @models: array of model name strings
- * @tbl: configuration table, terminated by null entries
- *
- * Compares the modelname or PCI subsystem id of the current codec with the
- * given configuration table. If a matching entry is found, returns its
- * config value (supposed to be 0 or positive).
- *
- * If no entries are matching, the function returns a negative value.
- */
-int snd_hda_check_board_config(struct hda_codec *codec,
- int num_configs, const char **models,
- const struct snd_pci_quirk *tbl)
-{
- if (codec->modelname && models) {
- int i;
- for (i = 0; i < num_configs; i++) {
- if (models[i] &&
- !strcmp(codec->modelname, models[i])) {
- snd_printd(KERN_INFO "hda_codec: model '%s' is "
- "selected\n", models[i]);
- return i;
- }
- }
- }
-
- if (!codec->bus->pci || !tbl)
- return -1;
-
- tbl = snd_pci_quirk_lookup(codec->bus->pci, tbl);
- if (!tbl)
- return -1;
- if (tbl->value >= 0 && tbl->value < num_configs) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- char tmp[10];
- const char *model = NULL;
- if (models)
- model = models[tbl->value];
- if (!model) {
- sprintf(tmp, "#%d", tbl->value);
- model = tmp;
- }
- snd_printdd(KERN_INFO "hda_codec: model '%s' is selected "
- "for config %x:%x (%s)\n",
- model, tbl->subvendor, tbl->subdevice,
- (tbl->name ? tbl->name : "Unknown device"));
-#endif
- return tbl->value;
- }
- return -1;
-}
-EXPORT_SYMBOL_HDA(snd_hda_check_board_config);
-
-/**
- * snd_hda_check_board_codec_sid_config - compare the current codec
- subsystem ID with the
- config table
-
- This is important for Gateway notebooks with SB450 HDA Audio
- where the vendor ID of the PCI device is:
- ATI Technologies Inc SB450 HDA Audio [1002:437b]
- and the vendor/subvendor are found only at the codec.
-
- * @codec: the HDA codec
- * @num_configs: number of config enums
- * @models: array of model name strings
- * @tbl: configuration table, terminated by null entries
- *
- * Compares the modelname or PCI subsystem id of the current codec with the
- * given configuration table. If a matching entry is found, returns its
- * config value (supposed to be 0 or positive).
- *
- * If no entries are matching, the function returns a negative value.
- */
-int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
- int num_configs, const char **models,
- const struct snd_pci_quirk *tbl)
-{
- const struct snd_pci_quirk *q;
-
- /* Search for codec ID */
- for (q = tbl; q->subvendor; q++) {
- unsigned long vendorid = (q->subdevice) | (q->subvendor << 16);
-
- if (vendorid == codec->subsystem_id)
- break;
- }
-
- if (!q->subvendor)
- return -1;
-
- tbl = q;
-
- if (tbl->value >= 0 && tbl->value < num_configs) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- char tmp[10];
- const char *model = NULL;
- if (models)
- model = models[tbl->value];
- if (!model) {
- sprintf(tmp, "#%d", tbl->value);
- model = tmp;
- }
- snd_printdd(KERN_INFO "hda_codec: model '%s' is selected "
- "for config %x:%x (%s)\n",
- model, tbl->subvendor, tbl->subdevice,
- (tbl->name ? tbl->name : "Unknown device"));
-#endif
- return tbl->value;
- }
- return -1;
-}
-EXPORT_SYMBOL_HDA(snd_hda_check_board_codec_sid_config);
-
-/**
- * snd_hda_add_new_ctls - create controls from the array
- * @codec: the HDA codec
- * @knew: the array of struct snd_kcontrol_new
- *
- * This helper function creates and add new controls in the given array.
- * The array must be terminated with an empty entry as terminator.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
-{
- int err;
-
- for (; knew->name; knew++) {
- struct snd_kcontrol *kctl;
- if (knew->iface == -1) /* skip this codec private value */
- continue;
- kctl = snd_ctl_new1(knew, codec);
- if (!kctl)
- return -ENOMEM;
- err = snd_hda_ctl_add(codec, 0, kctl);
- if (err < 0) {
- if (!codec->addr)
- return err;
- kctl = snd_ctl_new1(knew, codec);
- if (!kctl)
- return -ENOMEM;
- kctl->id.device = codec->addr;
- err = snd_hda_ctl_add(codec, 0, kctl);
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls);
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
- unsigned int power_state);
-
-static void hda_power_work(struct work_struct *work)
-{
- struct hda_codec *codec =
- container_of(work, struct hda_codec, power_work.work);
- struct hda_bus *bus = codec->bus;
-
- if (!codec->power_on || codec->power_count) {
- codec->power_transition = 0;
- return;
- }
-
- hda_call_codec_suspend(codec);
- if (bus->ops.pm_notify)
- bus->ops.pm_notify(bus);
-}
-
-static void hda_keep_power_on(struct hda_codec *codec)
-{
- codec->power_count++;
- codec->power_on = 1;
- codec->power_jiffies = jiffies;
-}
-
-/* update the power on/off account with the current jiffies */
-void snd_hda_update_power_acct(struct hda_codec *codec)
-{
- unsigned long delta = jiffies - codec->power_jiffies;
- if (codec->power_on)
- codec->power_on_acct += delta;
- else
- codec->power_off_acct += delta;
- codec->power_jiffies += delta;
-}
-
-/**
- * snd_hda_power_up - Power-up the codec
- * @codec: HD-audio codec
- *
- * Increment the power-up counter and power up the hardware really when
- * not turned on yet.
- */
-void snd_hda_power_up(struct hda_codec *codec)
-{
- struct hda_bus *bus = codec->bus;
-
- codec->power_count++;
- if (codec->power_on || codec->power_transition)
- return;
-
- snd_hda_update_power_acct(codec);
- codec->power_on = 1;
- codec->power_jiffies = jiffies;
- if (bus->ops.pm_notify)
- bus->ops.pm_notify(bus);
- hda_call_codec_resume(codec);
- cancel_delayed_work(&codec->power_work);
- codec->power_transition = 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_power_up);
-
-#define power_save(codec) \
- ((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
-
-/**
- * snd_hda_power_down - Power-down the codec
- * @codec: HD-audio codec
- *
- * Decrement the power-up counter and schedules the power-off work if
- * the counter rearches to zero.
- */
-void snd_hda_power_down(struct hda_codec *codec)
-{
- --codec->power_count;
- if (!codec->power_on || codec->power_count || codec->power_transition)
- return;
- if (power_save(codec)) {
- codec->power_transition = 1; /* avoid reentrance */
- queue_delayed_work(codec->bus->workq, &codec->power_work,
- msecs_to_jiffies(power_save(codec) * 1000));
- }
-}
-EXPORT_SYMBOL_HDA(snd_hda_power_down);
-
-/**
- * snd_hda_check_amp_list_power - Check the amp list and update the power
- * @codec: HD-audio codec
- * @check: the object containing an AMP list and the status
- * @nid: NID to check / update
- *
- * Check whether the given NID is in the amp list. If it's in the list,
- * check the current AMP status, and update the the power-status according
- * to the mute status.
- *
- * This function is supposed to be set or called from the check_power_status
- * patch ops.
- */
-int snd_hda_check_amp_list_power(struct hda_codec *codec,
- struct hda_loopback_check *check,
- hda_nid_t nid)
-{
- struct hda_amp_list *p;
- int ch, v;
-
- if (!check->amplist)
- return 0;
- for (p = check->amplist; p->nid; p++) {
- if (p->nid == nid)
- break;
- }
- if (!p->nid)
- return 0; /* nothing changed */
-
- for (p = check->amplist; p->nid; p++) {
- for (ch = 0; ch < 2; ch++) {
- v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
- p->idx);
- if (!(v & HDA_AMP_MUTE) && v > 0) {
- if (!check->power_on) {
- check->power_on = 1;
- snd_hda_power_up(codec);
- }
- return 1;
- }
- }
- }
- if (check->power_on) {
- check->power_on = 0;
- snd_hda_power_down(codec);
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power);
-#endif
-
-/*
- * Channel mode helper
- */
-
-/**
- * snd_hda_ch_mode_info - Info callback helper for the channel mode enum
- */
-int snd_hda_ch_mode_info(struct hda_codec *codec,
- struct snd_ctl_elem_info *uinfo,
- const struct hda_channel_mode *chmode,
- int num_chmodes)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = num_chmodes;
- if (uinfo->value.enumerated.item >= num_chmodes)
- uinfo->value.enumerated.item = num_chmodes - 1;
- sprintf(uinfo->value.enumerated.name, "%dch",
- chmode[uinfo->value.enumerated.item].channels);
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info);
-
-/**
- * snd_hda_ch_mode_get - Get callback helper for the channel mode enum
- */
-int snd_hda_ch_mode_get(struct hda_codec *codec,
- struct snd_ctl_elem_value *ucontrol,
- const struct hda_channel_mode *chmode,
- int num_chmodes,
- int max_channels)
-{
- int i;
-
- for (i = 0; i < num_chmodes; i++) {
- if (max_channels == chmode[i].channels) {
- ucontrol->value.enumerated.item[0] = i;
- break;
- }
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get);
-
-/**
- * snd_hda_ch_mode_put - Put callback helper for the channel mode enum
- */
-int snd_hda_ch_mode_put(struct hda_codec *codec,
- struct snd_ctl_elem_value *ucontrol,
- const struct hda_channel_mode *chmode,
- int num_chmodes,
- int *max_channelsp)
-{
- unsigned int mode;
-
- mode = ucontrol->value.enumerated.item[0];
- if (mode >= num_chmodes)
- return -EINVAL;
- if (*max_channelsp == chmode[mode].channels)
- return 0;
- /* change the current channel setting */
- *max_channelsp = chmode[mode].channels;
- if (chmode[mode].sequence)
- snd_hda_sequence_write_cache(codec, chmode[mode].sequence);
- return 1;
-}
-EXPORT_SYMBOL_HDA(snd_hda_ch_mode_put);
-
-/*
- * input MUX helper
- */
-
-/**
- * snd_hda_input_mux_info_info - Info callback helper for the input-mux enum
- */
-int snd_hda_input_mux_info(const struct hda_input_mux *imux,
- struct snd_ctl_elem_info *uinfo)
-{
- unsigned int index;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = imux->num_items;
- if (!imux->num_items)
- return 0;
- index = uinfo->value.enumerated.item;
- if (index >= imux->num_items)
- index = imux->num_items - 1;
- strcpy(uinfo->value.enumerated.name, imux->items[index].label);
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_input_mux_info);
-
-/**
- * snd_hda_input_mux_info_put - Put callback helper for the input-mux enum
- */
-int snd_hda_input_mux_put(struct hda_codec *codec,
- const struct hda_input_mux *imux,
- struct snd_ctl_elem_value *ucontrol,
- hda_nid_t nid,
- unsigned int *cur_val)
-{
- unsigned int idx;
-
- if (!imux->num_items)
- return 0;
- idx = ucontrol->value.enumerated.item[0];
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
- if (*cur_val == idx)
- return 0;
- snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
- imux->items[idx].index);
- *cur_val = idx;
- return 1;
-}
-EXPORT_SYMBOL_HDA(snd_hda_input_mux_put);
-
-
-/*
- * Multi-channel / digital-out PCM helper functions
- */
-
-/* setup SPDIF output stream */
-static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
- unsigned int stream_tag, unsigned int format)
-{
- /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
- if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
- set_dig_out_convert(codec, nid,
- codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff,
- -1);
- snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
- if (codec->slave_dig_outs) {
- hda_nid_t *d;
- for (d = codec->slave_dig_outs; *d; d++)
- snd_hda_codec_setup_stream(codec, *d, stream_tag, 0,
- format);
- }
- /* turn on again (if needed) */
- if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
- set_dig_out_convert(codec, nid,
- codec->spdif_ctls & 0xff, -1);
-}
-
-static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
-{
- snd_hda_codec_cleanup_stream(codec, nid);
- if (codec->slave_dig_outs) {
- hda_nid_t *d;
- for (d = codec->slave_dig_outs; *d; d++)
- snd_hda_codec_cleanup_stream(codec, *d);
- }
-}
-
-/**
- * snd_hda_bus_reboot_notify - call the reboot notifier of each codec
- * @bus: HD-audio bus
- */
-void snd_hda_bus_reboot_notify(struct hda_bus *bus)
-{
- struct hda_codec *codec;
-
- if (!bus)
- return;
- list_for_each_entry(codec, &bus->codec_list, list) {
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!codec->power_on)
- continue;
-#endif
- if (codec->patch_ops.reboot_notify)
- codec->patch_ops.reboot_notify(codec);
- }
-}
-EXPORT_SYMBOL_HDA(snd_hda_bus_reboot_notify);
-
-/**
- * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode
- */
-int snd_hda_multi_out_dig_open(struct hda_codec *codec,
- struct hda_multi_out *mout)
-{
- mutex_lock(&codec->spdif_mutex);
- if (mout->dig_out_used == HDA_DIG_ANALOG_DUP)
- /* already opened as analog dup; reset it once */
- cleanup_dig_out_stream(codec, mout->dig_out_nid);
- mout->dig_out_used = HDA_DIG_EXCLUSIVE;
- mutex_unlock(&codec->spdif_mutex);
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open);
-
-/**
- * snd_hda_multi_out_dig_prepare - prepare the digital out stream
- */
-int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
- struct hda_multi_out *mout,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- mutex_lock(&codec->spdif_mutex);
- setup_dig_out_stream(codec, mout->dig_out_nid, stream_tag, format);
- mutex_unlock(&codec->spdif_mutex);
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare);
-
-/**
- * snd_hda_multi_out_dig_cleanup - clean-up the digital out stream
- */
-int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
- struct hda_multi_out *mout)
-{
- mutex_lock(&codec->spdif_mutex);
- cleanup_dig_out_stream(codec, mout->dig_out_nid);
- mutex_unlock(&codec->spdif_mutex);
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_cleanup);
-
-/**
- * snd_hda_multi_out_dig_close - release the digital out stream
- */
-int snd_hda_multi_out_dig_close(struct hda_codec *codec,
- struct hda_multi_out *mout)
-{
- mutex_lock(&codec->spdif_mutex);
- mout->dig_out_used = 0;
- mutex_unlock(&codec->spdif_mutex);
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close);
-
-/**
- * snd_hda_multi_out_analog_open - open analog outputs
- *
- * Open analog outputs and set up the hw-constraints.
- * If the digital outputs can be opened as slave, open the digital
- * outputs, too.
- */
-int snd_hda_multi_out_analog_open(struct hda_codec *codec,
- struct hda_multi_out *mout,
- struct snd_pcm_substream *substream,
- struct hda_pcm_stream *hinfo)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- runtime->hw.channels_max = mout->max_channels;
- if (mout->dig_out_nid) {
- if (!mout->analog_rates) {
- mout->analog_rates = hinfo->rates;
- mout->analog_formats = hinfo->formats;
- mout->analog_maxbps = hinfo->maxbps;
- } else {
- runtime->hw.rates = mout->analog_rates;
- runtime->hw.formats = mout->analog_formats;
- hinfo->maxbps = mout->analog_maxbps;
- }
- if (!mout->spdif_rates) {
- snd_hda_query_supported_pcm(codec, mout->dig_out_nid,
- &mout->spdif_rates,
- &mout->spdif_formats,
- &mout->spdif_maxbps);
- }
- mutex_lock(&codec->spdif_mutex);
- if (mout->share_spdif) {
- if ((runtime->hw.rates & mout->spdif_rates) &&
- (runtime->hw.formats & mout->spdif_formats)) {
- runtime->hw.rates &= mout->spdif_rates;
- runtime->hw.formats &= mout->spdif_formats;
- if (mout->spdif_maxbps < hinfo->maxbps)
- hinfo->maxbps = mout->spdif_maxbps;
- } else {
- mout->share_spdif = 0;
- /* FIXME: need notify? */
- }
- }
- mutex_unlock(&codec->spdif_mutex);
- }
- return snd_pcm_hw_constraint_step(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS, 2);
-}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open);
-
-/**
- * snd_hda_multi_out_analog_prepare - Preapre the analog outputs.
- *
- * Set up the i/o for analog out.
- * When the digital out is available, copy the front out to digital out, too.
- */
-int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
- struct hda_multi_out *mout,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- hda_nid_t *nids = mout->dac_nids;
- int chs = substream->runtime->channels;
- int i;
-
- mutex_lock(&codec->spdif_mutex);
- if (mout->dig_out_nid && mout->share_spdif &&
- mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
- if (chs == 2 &&
- snd_hda_is_supported_format(codec, mout->dig_out_nid,
- format) &&
- !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
- mout->dig_out_used = HDA_DIG_ANALOG_DUP;
- setup_dig_out_stream(codec, mout->dig_out_nid,
- stream_tag, format);
- } else {
- mout->dig_out_used = 0;
- cleanup_dig_out_stream(codec, mout->dig_out_nid);
- }
- }
- mutex_unlock(&codec->spdif_mutex);
-
- /* front */
- snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
- 0, format);
- if (!mout->no_share_stream &&
- mout->hp_nid && mout->hp_nid != nids[HDA_FRONT])
- /* headphone out will just decode front left/right (stereo) */
- snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
- 0, format);
- /* extra outputs copied from front */
- for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
- if (!mout->no_share_stream && mout->extra_out_nid[i])
- snd_hda_codec_setup_stream(codec,
- mout->extra_out_nid[i],
- stream_tag, 0, format);
-
- /* surrounds */
- for (i = 1; i < mout->num_dacs; i++) {
- if (chs >= (i + 1) * 2) /* independent out */
- snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
- i * 2, format);
- else if (!mout->no_share_stream) /* copy front */
- snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
- 0, format);
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare);
-
-/**
- * snd_hda_multi_out_analog_cleanup - clean up the setting for analog out
- */
-int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
- struct hda_multi_out *mout)
-{
- hda_nid_t *nids = mout->dac_nids;
- int i;
-
- for (i = 0; i < mout->num_dacs; i++)
- snd_hda_codec_cleanup_stream(codec, nids[i]);
- if (mout->hp_nid)
- snd_hda_codec_cleanup_stream(codec, mout->hp_nid);
- for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
- if (mout->extra_out_nid[i])
- snd_hda_codec_cleanup_stream(codec,
- mout->extra_out_nid[i]);
- mutex_lock(&codec->spdif_mutex);
- if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
- cleanup_dig_out_stream(codec, mout->dig_out_nid);
- mout->dig_out_used = 0;
- }
- mutex_unlock(&codec->spdif_mutex);
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup);
-
-/*
- * Helper for automatic pin configuration
- */
-
-static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
-{
- for (; *list; list++)
- if (*list == nid)
- return 1;
- return 0;
-}
-
-
-/*
- * Sort an associated group of pins according to their sequence numbers.
- */
-static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
- int num_pins)
-{
- int i, j;
- short seq;
- hda_nid_t nid;
-
- for (i = 0; i < num_pins; i++) {
- for (j = i + 1; j < num_pins; j++) {
- if (sequences[i] > sequences[j]) {
- seq = sequences[i];
- sequences[i] = sequences[j];
- sequences[j] = seq;
- nid = pins[i];
- pins[i] = pins[j];
- pins[j] = nid;
- }
- }
- }
-}
-
-
-/* add the found input-pin to the cfg->inputs[] table */
-static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid,
- int type)
-{
- if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
- cfg->inputs[cfg->num_inputs].pin = nid;
- cfg->inputs[cfg->num_inputs].type = type;
- cfg->num_inputs++;
- }
-}
-
-/* sort inputs in the order of AUTO_PIN_* type */
-static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg)
-{
- int i, j;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- for (j = i + 1; j < cfg->num_inputs; j++) {
- if (cfg->inputs[i].type > cfg->inputs[j].type) {
- struct auto_pin_cfg_item tmp;
- tmp = cfg->inputs[i];
- cfg->inputs[i] = cfg->inputs[j];
- cfg->inputs[j] = tmp;
- }
- }
- }
-}
-
-/*
- * Parse all pin widgets and store the useful pin nids to cfg
- *
- * The number of line-outs or any primary output is stored in line_outs,
- * and the corresponding output pins are assigned to line_out_pins[],
- * in the order of front, rear, CLFE, side, ...
- *
- * If more extra outputs (speaker and headphone) are found, the pins are
- * assisnged to hp_pins[] and speaker_pins[], respectively. If no line-out jack
- * is detected, one of speaker of HP pins is assigned as the primary
- * output, i.e. to line_out_pins[0]. So, line_outs is always positive
- * if any analog output exists.
- *
- * The analog input pins are assigned to inputs array.
- * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
- * respectively.
- */
-int snd_hda_parse_pin_def_config(struct hda_codec *codec,
- struct auto_pin_cfg *cfg,
- hda_nid_t *ignore_nids)
-{
- hda_nid_t nid, end_nid;
- short seq, assoc_line_out, assoc_speaker;
- short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
- short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
- short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
- int i;
-
- memset(cfg, 0, sizeof(*cfg));
-
- memset(sequences_line_out, 0, sizeof(sequences_line_out));
- memset(sequences_speaker, 0, sizeof(sequences_speaker));
- memset(sequences_hp, 0, sizeof(sequences_hp));
- assoc_line_out = assoc_speaker = 0;
-
- end_nid = codec->start_nid + codec->num_nodes;
- for (nid = codec->start_nid; nid < end_nid; nid++) {
- unsigned int wid_caps = get_wcaps(codec, nid);
- unsigned int wid_type = get_wcaps_type(wid_caps);
- unsigned int def_conf;
- short assoc, loc;
-
- /* read all default configuration for pin complex */
- if (wid_type != AC_WID_PIN)
- continue;
- /* ignore the given nids (e.g. pc-beep returns error) */
- if (ignore_nids && is_in_nid_list(nid, ignore_nids))
- continue;
-
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
- continue;
- loc = get_defcfg_location(def_conf);
- switch (get_defcfg_device(def_conf)) {
- case AC_JACK_LINE_OUT:
- seq = get_defcfg_sequence(def_conf);
- assoc = get_defcfg_association(def_conf);
-
- if (!(wid_caps & AC_WCAP_STEREO))
- if (!cfg->mono_out_pin)
- cfg->mono_out_pin = nid;
- if (!assoc)
- continue;
- if (!assoc_line_out)
- assoc_line_out = assoc;
- else if (assoc_line_out != assoc)
- continue;
- if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins))
- continue;
- cfg->line_out_pins[cfg->line_outs] = nid;
- sequences_line_out[cfg->line_outs] = seq;
- cfg->line_outs++;
- break;
- case AC_JACK_SPEAKER:
- seq = get_defcfg_sequence(def_conf);
- assoc = get_defcfg_association(def_conf);
- if (!assoc)
- continue;
- if (!assoc_speaker)
- assoc_speaker = assoc;
- else if (assoc_speaker != assoc)
- continue;
- if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins))
- continue;
- cfg->speaker_pins[cfg->speaker_outs] = nid;
- sequences_speaker[cfg->speaker_outs] = seq;
- cfg->speaker_outs++;
- break;
- case AC_JACK_HP_OUT:
- seq = get_defcfg_sequence(def_conf);
- assoc = get_defcfg_association(def_conf);
- if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
- continue;
- cfg->hp_pins[cfg->hp_outs] = nid;
- sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
- cfg->hp_outs++;
- break;
- case AC_JACK_MIC_IN:
- add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
- break;
- case AC_JACK_LINE_IN:
- add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
- break;
- case AC_JACK_CD:
- add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
- break;
- case AC_JACK_AUX:
- add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
- break;
- case AC_JACK_SPDIF_OUT:
- case AC_JACK_DIG_OTHER_OUT:
- if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins))
- continue;
- cfg->dig_out_pins[cfg->dig_outs] = nid;
- cfg->dig_out_type[cfg->dig_outs] =
- (loc == AC_JACK_LOC_HDMI) ?
- HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
- cfg->dig_outs++;
- break;
- case AC_JACK_SPDIF_IN:
- case AC_JACK_DIG_OTHER_IN:
- cfg->dig_in_pin = nid;
- if (loc == AC_JACK_LOC_HDMI)
- cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
- else
- cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
- break;
- }
- }
-
- /* FIX-UP:
- * If no line-out is defined but multiple HPs are found,
- * some of them might be the real line-outs.
- */
- if (!cfg->line_outs && cfg->hp_outs > 1) {
- int i = 0;
- while (i < cfg->hp_outs) {
- /* The real HPs should have the sequence 0x0f */
- if ((sequences_hp[i] & 0x0f) == 0x0f) {
- i++;
- continue;
- }
- /* Move it to the line-out table */
- cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i];
- sequences_line_out[cfg->line_outs] = sequences_hp[i];
- cfg->line_outs++;
- cfg->hp_outs--;
- memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1,
- sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i));
- memmove(sequences_hp + i, sequences_hp + i + 1,
- sizeof(sequences_hp[0]) * (cfg->hp_outs - i));
- }
- memset(cfg->hp_pins + cfg->hp_outs, 0,
- sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
- }
-
- /* sort by sequence */
- sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out,
- cfg->line_outs);
- sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker,
- cfg->speaker_outs);
- sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
- cfg->hp_outs);
-
- /*
- * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
- * as a primary output
- */
- if (!cfg->line_outs) {
- if (cfg->speaker_outs) {
- cfg->line_outs = cfg->speaker_outs;
- memcpy(cfg->line_out_pins, cfg->speaker_pins,
- sizeof(cfg->speaker_pins));
- cfg->speaker_outs = 0;
- memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
- cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
- } else if (cfg->hp_outs) {
- cfg->line_outs = cfg->hp_outs;
- memcpy(cfg->line_out_pins, cfg->hp_pins,
- sizeof(cfg->hp_pins));
- cfg->hp_outs = 0;
- memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
- cfg->line_out_type = AUTO_PIN_HP_OUT;
- }
- }
-
- /* Reorder the surround channels
- * ALSA sequence is front/surr/clfe/side
- * HDA sequence is:
- * 4-ch: front/surr => OK as it is
- * 6-ch: front/clfe/surr
- * 8-ch: front/clfe/rear/side|fc
- */
- switch (cfg->line_outs) {
- case 3:
- case 4:
- nid = cfg->line_out_pins[1];
- cfg->line_out_pins[1] = cfg->line_out_pins[2];
- cfg->line_out_pins[2] = nid;
- break;
- }
-
- sort_autocfg_input_pins(cfg);
-
- /*
- * debug prints of the parsed results
- */
- snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
- cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1],
- cfg->line_out_pins[2], cfg->line_out_pins[3],
- cfg->line_out_pins[4]);
- snd_printd(" speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
- cfg->speaker_outs, cfg->speaker_pins[0],
- cfg->speaker_pins[1], cfg->speaker_pins[2],
- cfg->speaker_pins[3], cfg->speaker_pins[4]);
- snd_printd(" hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
- cfg->hp_outs, cfg->hp_pins[0],
- cfg->hp_pins[1], cfg->hp_pins[2],
- cfg->hp_pins[3], cfg->hp_pins[4]);
- snd_printd(" mono: mono_out=0x%x\n", cfg->mono_out_pin);
- if (cfg->dig_outs)
- snd_printd(" dig-out=0x%x/0x%x\n",
- cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
- snd_printd(" inputs:");
- for (i = 0; i < cfg->num_inputs; i++) {
- snd_printdd(" %s=0x%x",
- hda_get_autocfg_input_label(codec, cfg, i),
- cfg->inputs[i].pin);
- }
- snd_printd("\n");
- if (cfg->dig_in_pin)
- snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin);
-
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_parse_pin_def_config);
-
-int snd_hda_get_input_pin_attr(unsigned int def_conf)
-{
- unsigned int loc = get_defcfg_location(def_conf);
- unsigned int conn = get_defcfg_connect(def_conf);
- if (conn == AC_JACK_PORT_NONE)
- return INPUT_PIN_ATTR_UNUSED;
- /* Windows may claim the internal mic to be BOTH, too */
- if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH)
- return INPUT_PIN_ATTR_INT;
- if ((loc & 0x30) == AC_JACK_LOC_INTERNAL)
- return INPUT_PIN_ATTR_INT;
- if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
- return INPUT_PIN_ATTR_DOCK;
- if (loc == AC_JACK_LOC_REAR)
- return INPUT_PIN_ATTR_REAR;
- if (loc == AC_JACK_LOC_FRONT)
- return INPUT_PIN_ATTR_FRONT;
- return INPUT_PIN_ATTR_NORMAL;
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr);
-
-/**
- * hda_get_input_pin_label - Give a label for the given input pin
- *
- * When check_location is true, the function checks the pin location
- * for mic and line-in pins, and set an appropriate prefix like "Front",
- * "Rear", "Internal".
- */
-
-const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
- int check_location)
-{
- unsigned int def_conf;
- static const char *mic_names[] = {
- "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic",
- };
- int attr;
-
- def_conf = snd_hda_codec_get_pincfg(codec, pin);
-
- switch (get_defcfg_device(def_conf)) {
- case AC_JACK_MIC_IN:
- if (!check_location)
- return "Mic";
- attr = snd_hda_get_input_pin_attr(def_conf);
- if (!attr)
- return "None";
- return mic_names[attr - 1];
- case AC_JACK_LINE_IN:
- if (!check_location)
- return "Line";
- attr = snd_hda_get_input_pin_attr(def_conf);
- if (!attr)
- return "None";
- if (attr == INPUT_PIN_ATTR_DOCK)
- return "Dock Line";
- return "Line";
- case AC_JACK_AUX:
- return "Aux";
- case AC_JACK_CD:
- return "CD";
- case AC_JACK_SPDIF_IN:
- return "SPDIF In";
- case AC_JACK_DIG_OTHER_IN:
- return "Digital In";
- default:
- return "Misc";
- }
-}
-EXPORT_SYMBOL_HDA(hda_get_input_pin_label);
-
-/* Check whether the location prefix needs to be added to the label.
- * If all mic-jacks are in the same location (e.g. rear panel), we don't
- * have to put "Front" prefix to each label. In such a case, returns false.
- */
-static int check_mic_location_need(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg,
- int input)
-{
- unsigned int defc;
- int i, attr, attr2;
-
- defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
- attr = snd_hda_get_input_pin_attr(defc);
- /* for internal or docking mics, we need locations */
- if (attr <= INPUT_PIN_ATTR_NORMAL)
- return 1;
-
- attr = 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
- attr2 = snd_hda_get_input_pin_attr(defc);
- if (attr2 >= INPUT_PIN_ATTR_NORMAL) {
- if (attr && attr != attr2)
- return 1; /* different locations found */
- attr = attr2;
- }
- }
- return 0;
-}
-
-/**
- * hda_get_autocfg_input_label - Get a label for the given input
- *
- * Get a label for the given input pin defined by the autocfg item.
- * Unlike hda_get_input_pin_label(), this function checks all inputs
- * defined in autocfg and avoids the redundant mic/line prefix as much as
- * possible.
- */
-const char *hda_get_autocfg_input_label(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg,
- int input)
-{
- int type = cfg->inputs[input].type;
- int has_multiple_pins = 0;
-
- if ((input > 0 && cfg->inputs[input - 1].type == type) ||
- (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
- has_multiple_pins = 1;
- if (has_multiple_pins && type == AUTO_PIN_MIC)
- has_multiple_pins &= check_mic_location_need(codec, cfg, input);
- return hda_get_input_pin_label(codec, cfg->inputs[input].pin,
- has_multiple_pins);
-}
-EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label);
-
-/**
- * snd_hda_add_imux_item - Add an item to input_mux
- *
- * When the same label is used already in the existing items, the number
- * suffix is appended to the label. This label index number is stored
- * to type_idx when non-NULL pointer is given.
- */
-int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
- int index, int *type_idx)
-{
- int i, label_idx = 0;
- if (imux->num_items >= HDA_MAX_NUM_INPUTS) {
- snd_printd(KERN_ERR "hda_codec: Too many imux items!\n");
- return -EINVAL;
- }
- for (i = 0; i < imux->num_items; i++) {
- if (!strncmp(label, imux->items[i].label, strlen(label)))
- label_idx++;
- }
- if (type_idx)
- *type_idx = label_idx;
- if (label_idx > 0)
- snprintf(imux->items[imux->num_items].label,
- sizeof(imux->items[imux->num_items].label),
- "%s %d", label, label_idx);
- else
- strlcpy(imux->items[imux->num_items].label, label,
- sizeof(imux->items[imux->num_items].label));
- imux->items[imux->num_items].index = index;
- imux->num_items++;
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_add_imux_item);
-
-
-#ifdef CONFIG_PM
-/*
- * power management
- */
-
-/**
- * snd_hda_suspend - suspend the codecs
- * @bus: the HDA bus
- *
- * Returns 0 if successful.
- */
-int snd_hda_suspend(struct hda_bus *bus)
-{
- struct hda_codec *codec;
-
- list_for_each_entry(codec, &bus->codec_list, list) {
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!codec->power_on)
- continue;
-#endif
- hda_call_codec_suspend(codec);
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_suspend);
-
-/**
- * snd_hda_resume - resume the codecs
- * @bus: the HDA bus
- *
- * Returns 0 if successful.
- *
- * This fucntion is defined only when POWER_SAVE isn't set.
- * In the power-save mode, the codec is resumed dynamically.
- */
-int snd_hda_resume(struct hda_bus *bus)
-{
- struct hda_codec *codec;
-
- list_for_each_entry(codec, &bus->codec_list, list) {
- if (snd_hda_codec_needs_resume(codec))
- hda_call_codec_resume(codec);
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_resume);
-#endif /* CONFIG_PM */
-
-/*
- * generic arrays
- */
-
-/**
- * snd_array_new - get a new element from the given array
- * @array: the array object
- *
- * Get a new element from the given array. If it exceeds the
- * pre-allocated array size, re-allocate the array.
- *
- * Returns NULL if allocation failed.
- */
-void *snd_array_new(struct snd_array *array)
-{
- if (array->used >= array->alloced) {
- int num = array->alloced + array->alloc_align;
- void *nlist;
- if (snd_BUG_ON(num >= 4096))
- return NULL;
- nlist = kcalloc(num + 1, array->elem_size, GFP_KERNEL);
- if (!nlist)
- return NULL;
- if (array->list) {
- memcpy(nlist, array->list,
- array->elem_size * array->alloced);
- kfree(array->list);
- }
- array->list = nlist;
- array->alloced = num;
- }
- return snd_array_elem(array, array->used++);
-}
-EXPORT_SYMBOL_HDA(snd_array_new);
-
-/**
- * snd_array_free - free the given array elements
- * @array: the array object
- */
-void snd_array_free(struct snd_array *array)
-{
- kfree(array->list);
- array->used = 0;
- array->alloced = 0;
- array->list = NULL;
-}
-EXPORT_SYMBOL_HDA(snd_array_free);
-
-/**
- * snd_print_pcm_rates - Print the supported PCM rates to the string buffer
- * @pcm: PCM caps bits
- * @buf: the string buffer to write
- * @buflen: the max buffer length
- *
- * used by hda_proc.c and hda_eld.c
- */
-void snd_print_pcm_rates(int pcm, char *buf, int buflen)
-{
- static unsigned int rates[] = {
- 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
- 96000, 176400, 192000, 384000
- };
- int i, j;
-
- for (i = 0, j = 0; i < ARRAY_SIZE(rates); i++)
- if (pcm & (1 << i))
- j += snprintf(buf + j, buflen - j, " %d", rates[i]);
-
- buf[j] = '\0'; /* necessary when j == 0 */
-}
-EXPORT_SYMBOL_HDA(snd_print_pcm_rates);
-
-/**
- * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
- * @pcm: PCM caps bits
- * @buf: the string buffer to write
- * @buflen: the max buffer length
- *
- * used by hda_proc.c and hda_eld.c
- */
-void snd_print_pcm_bits(int pcm, char *buf, int buflen)
-{
- static unsigned int bits[] = { 8, 16, 20, 24, 32 };
- int i, j;
-
- for (i = 0, j = 0; i < ARRAY_SIZE(bits); i++)
- if (pcm & (AC_SUPPCM_BITS_8 << i))
- j += snprintf(buf + j, buflen - j, " %d", bits[i]);
-
- buf[j] = '\0'; /* necessary when j == 0 */
-}
-EXPORT_SYMBOL_HDA(snd_print_pcm_bits);
-
-MODULE_DESCRIPTION("HDA codec core");
-MODULE_LICENSE("GPL");
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
deleted file mode 100644
index fdf8d44f8b6b..000000000000
--- a/sound/pci/hda/hda_codec.h
+++ /dev/null
@@ -1,1048 +0,0 @@
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 59
- * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef __SOUND_HDA_CODEC_H
-#define __SOUND_HDA_CODEC_H
-
-#include <sound/info.h>
-#include <sound/control.h>
-#include <sound/pcm.h>
-#include <sound/hwdep.h>
-
-#if defined(CONFIG_PM) || defined(CONFIG_SND_HDA_POWER_SAVE)
-#define SND_HDA_NEEDS_RESUME /* resume control code is required */
-#endif
-
-/*
- * nodes
- */
-#define AC_NODE_ROOT 0x00
-
-/*
- * function group types
- */
-enum {
- AC_GRP_AUDIO_FUNCTION = 0x01,
- AC_GRP_MODEM_FUNCTION = 0x02,
-};
-
-/*
- * widget types
- */
-enum {
- AC_WID_AUD_OUT, /* Audio Out */
- AC_WID_AUD_IN, /* Audio In */
- AC_WID_AUD_MIX, /* Audio Mixer */
- AC_WID_AUD_SEL, /* Audio Selector */
- AC_WID_PIN, /* Pin Complex */
- AC_WID_POWER, /* Power */
- AC_WID_VOL_KNB, /* Volume Knob */
- AC_WID_BEEP, /* Beep Generator */
- AC_WID_VENDOR = 0x0f /* Vendor specific */
-};
-
-/*
- * GET verbs
- */
-#define AC_VERB_GET_STREAM_FORMAT 0x0a00
-#define AC_VERB_GET_AMP_GAIN_MUTE 0x0b00
-#define AC_VERB_GET_PROC_COEF 0x0c00
-#define AC_VERB_GET_COEF_INDEX 0x0d00
-#define AC_VERB_PARAMETERS 0x0f00
-#define AC_VERB_GET_CONNECT_SEL 0x0f01
-#define AC_VERB_GET_CONNECT_LIST 0x0f02
-#define AC_VERB_GET_PROC_STATE 0x0f03
-#define AC_VERB_GET_SDI_SELECT 0x0f04
-#define AC_VERB_GET_POWER_STATE 0x0f05
-#define AC_VERB_GET_CONV 0x0f06
-#define AC_VERB_GET_PIN_WIDGET_CONTROL 0x0f07
-#define AC_VERB_GET_UNSOLICITED_RESPONSE 0x0f08
-#define AC_VERB_GET_PIN_SENSE 0x0f09
-#define AC_VERB_GET_BEEP_CONTROL 0x0f0a
-#define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c
-#define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d
-#define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e /* unused */
-#define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f
-/* f10-f1a: GPIO */
-#define AC_VERB_GET_GPIO_DATA 0x0f15
-#define AC_VERB_GET_GPIO_MASK 0x0f16
-#define AC_VERB_GET_GPIO_DIRECTION 0x0f17
-#define AC_VERB_GET_GPIO_WAKE_MASK 0x0f18
-#define AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK 0x0f19
-#define AC_VERB_GET_GPIO_STICKY_MASK 0x0f1a
-#define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c
-/* f20: AFG/MFG */
-#define AC_VERB_GET_SUBSYSTEM_ID 0x0f20
-#define AC_VERB_GET_CVT_CHAN_COUNT 0x0f2d
-#define AC_VERB_GET_HDMI_DIP_SIZE 0x0f2e
-#define AC_VERB_GET_HDMI_ELDD 0x0f2f
-#define AC_VERB_GET_HDMI_DIP_INDEX 0x0f30
-#define AC_VERB_GET_HDMI_DIP_DATA 0x0f31
-#define AC_VERB_GET_HDMI_DIP_XMIT 0x0f32
-#define AC_VERB_GET_HDMI_CP_CTRL 0x0f33
-#define AC_VERB_GET_HDMI_CHAN_SLOT 0x0f34
-
-/*
- * SET verbs
- */
-#define AC_VERB_SET_STREAM_FORMAT 0x200
-#define AC_VERB_SET_AMP_GAIN_MUTE 0x300
-#define AC_VERB_SET_PROC_COEF 0x400
-#define AC_VERB_SET_COEF_INDEX 0x500
-#define AC_VERB_SET_CONNECT_SEL 0x701
-#define AC_VERB_SET_PROC_STATE 0x703
-#define AC_VERB_SET_SDI_SELECT 0x704
-#define AC_VERB_SET_POWER_STATE 0x705
-#define AC_VERB_SET_CHANNEL_STREAMID 0x706
-#define AC_VERB_SET_PIN_WIDGET_CONTROL 0x707
-#define AC_VERB_SET_UNSOLICITED_ENABLE 0x708
-#define AC_VERB_SET_PIN_SENSE 0x709
-#define AC_VERB_SET_BEEP_CONTROL 0x70a
-#define AC_VERB_SET_EAPD_BTLENABLE 0x70c
-#define AC_VERB_SET_DIGI_CONVERT_1 0x70d
-#define AC_VERB_SET_DIGI_CONVERT_2 0x70e
-#define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f
-#define AC_VERB_SET_GPIO_DATA 0x715
-#define AC_VERB_SET_GPIO_MASK 0x716
-#define AC_VERB_SET_GPIO_DIRECTION 0x717
-#define AC_VERB_SET_GPIO_WAKE_MASK 0x718
-#define AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK 0x719
-#define AC_VERB_SET_GPIO_STICKY_MASK 0x71a
-#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c
-#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d
-#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e
-#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3 0x71f
-#define AC_VERB_SET_EAPD 0x788
-#define AC_VERB_SET_CODEC_RESET 0x7ff
-#define AC_VERB_SET_CVT_CHAN_COUNT 0x72d
-#define AC_VERB_SET_HDMI_DIP_INDEX 0x730
-#define AC_VERB_SET_HDMI_DIP_DATA 0x731
-#define AC_VERB_SET_HDMI_DIP_XMIT 0x732
-#define AC_VERB_SET_HDMI_CP_CTRL 0x733
-#define AC_VERB_SET_HDMI_CHAN_SLOT 0x734
-
-/*
- * Parameter IDs
- */
-#define AC_PAR_VENDOR_ID 0x00
-#define AC_PAR_SUBSYSTEM_ID 0x01
-#define AC_PAR_REV_ID 0x02
-#define AC_PAR_NODE_COUNT 0x04
-#define AC_PAR_FUNCTION_TYPE 0x05
-#define AC_PAR_AUDIO_FG_CAP 0x08
-#define AC_PAR_AUDIO_WIDGET_CAP 0x09
-#define AC_PAR_PCM 0x0a
-#define AC_PAR_STREAM 0x0b
-#define AC_PAR_PIN_CAP 0x0c
-#define AC_PAR_AMP_IN_CAP 0x0d
-#define AC_PAR_CONNLIST_LEN 0x0e
-#define AC_PAR_POWER_STATE 0x0f
-#define AC_PAR_PROC_CAP 0x10
-#define AC_PAR_GPIO_CAP 0x11
-#define AC_PAR_AMP_OUT_CAP 0x12
-#define AC_PAR_VOL_KNB_CAP 0x13
-#define AC_PAR_HDMI_LPCM_CAP 0x20
-
-/*
- * AC_VERB_PARAMETERS results (32bit)
- */
-
-/* Function Group Type */
-#define AC_FGT_TYPE (0xff<<0)
-#define AC_FGT_TYPE_SHIFT 0
-#define AC_FGT_UNSOL_CAP (1<<8)
-
-/* Audio Function Group Capabilities */
-#define AC_AFG_OUT_DELAY (0xf<<0)
-#define AC_AFG_IN_DELAY (0xf<<8)
-#define AC_AFG_BEEP_GEN (1<<16)
-
-/* Audio Widget Capabilities */
-#define AC_WCAP_STEREO (1<<0) /* stereo I/O */
-#define AC_WCAP_IN_AMP (1<<1) /* AMP-in present */
-#define AC_WCAP_OUT_AMP (1<<2) /* AMP-out present */
-#define AC_WCAP_AMP_OVRD (1<<3) /* AMP-parameter override */
-#define AC_WCAP_FORMAT_OVRD (1<<4) /* format override */
-#define AC_WCAP_STRIPE (1<<5) /* stripe */
-#define AC_WCAP_PROC_WID (1<<6) /* Proc Widget */
-#define AC_WCAP_UNSOL_CAP (1<<7) /* Unsol capable */
-#define AC_WCAP_CONN_LIST (1<<8) /* connection list */
-#define AC_WCAP_DIGITAL (1<<9) /* digital I/O */
-#define AC_WCAP_POWER (1<<10) /* power control */
-#define AC_WCAP_LR_SWAP (1<<11) /* L/R swap */
-#define AC_WCAP_CP_CAPS (1<<12) /* content protection */
-#define AC_WCAP_CHAN_CNT_EXT (7<<13) /* channel count ext */
-#define AC_WCAP_DELAY (0xf<<16)
-#define AC_WCAP_DELAY_SHIFT 16
-#define AC_WCAP_TYPE (0xf<<20)
-#define AC_WCAP_TYPE_SHIFT 20
-
-/* supported PCM rates and bits */
-#define AC_SUPPCM_RATES (0xfff << 0)
-#define AC_SUPPCM_BITS_8 (1<<16)
-#define AC_SUPPCM_BITS_16 (1<<17)
-#define AC_SUPPCM_BITS_20 (1<<18)
-#define AC_SUPPCM_BITS_24 (1<<19)
-#define AC_SUPPCM_BITS_32 (1<<20)
-
-/* supported PCM stream format */
-#define AC_SUPFMT_PCM (1<<0)
-#define AC_SUPFMT_FLOAT32 (1<<1)
-#define AC_SUPFMT_AC3 (1<<2)
-
-/* GP I/O count */
-#define AC_GPIO_IO_COUNT (0xff<<0)
-#define AC_GPIO_O_COUNT (0xff<<8)
-#define AC_GPIO_O_COUNT_SHIFT 8
-#define AC_GPIO_I_COUNT (0xff<<16)
-#define AC_GPIO_I_COUNT_SHIFT 16
-#define AC_GPIO_UNSOLICITED (1<<30)
-#define AC_GPIO_WAKE (1<<31)
-
-/* Converter stream, channel */
-#define AC_CONV_CHANNEL (0xf<<0)
-#define AC_CONV_STREAM (0xf<<4)
-#define AC_CONV_STREAM_SHIFT 4
-
-/* Input converter SDI select */
-#define AC_SDI_SELECT (0xf<<0)
-
-/* stream format id */
-#define AC_FMT_CHAN_SHIFT 0
-#define AC_FMT_CHAN_MASK (0x0f << 0)
-#define AC_FMT_BITS_SHIFT 4
-#define AC_FMT_BITS_MASK (7 << 4)
-#define AC_FMT_BITS_8 (0 << 4)
-#define AC_FMT_BITS_16 (1 << 4)
-#define AC_FMT_BITS_20 (2 << 4)
-#define AC_FMT_BITS_24 (3 << 4)
-#define AC_FMT_BITS_32 (4 << 4)
-#define AC_FMT_DIV_SHIFT 8
-#define AC_FMT_DIV_MASK (7 << 8)
-#define AC_FMT_MULT_SHIFT 11
-#define AC_FMT_MULT_MASK (7 << 11)
-#define AC_FMT_BASE_SHIFT 14
-#define AC_FMT_BASE_48K (0 << 14)
-#define AC_FMT_BASE_44K (1 << 14)
-#define AC_FMT_TYPE_SHIFT 15
-#define AC_FMT_TYPE_PCM (0 << 15)
-#define AC_FMT_TYPE_NON_PCM (1 << 15)
-
-/* Unsolicited response control */
-#define AC_UNSOL_TAG (0x3f<<0)
-#define AC_UNSOL_ENABLED (1<<7)
-#define AC_USRSP_EN AC_UNSOL_ENABLED
-
-/* Unsolicited responses */
-#define AC_UNSOL_RES_TAG (0x3f<<26)
-#define AC_UNSOL_RES_TAG_SHIFT 26
-#define AC_UNSOL_RES_SUBTAG (0x1f<<21)
-#define AC_UNSOL_RES_SUBTAG_SHIFT 21
-#define AC_UNSOL_RES_ELDV (1<<1) /* ELD Data valid (for HDMI) */
-#define AC_UNSOL_RES_PD (1<<0) /* pinsense detect */
-#define AC_UNSOL_RES_CP_STATE (1<<1) /* content protection */
-#define AC_UNSOL_RES_CP_READY (1<<0) /* content protection */
-
-/* Pin widget capabilies */
-#define AC_PINCAP_IMP_SENSE (1<<0) /* impedance sense capable */
-#define AC_PINCAP_TRIG_REQ (1<<1) /* trigger required */
-#define AC_PINCAP_PRES_DETECT (1<<2) /* presence detect capable */
-#define AC_PINCAP_HP_DRV (1<<3) /* headphone drive capable */
-#define AC_PINCAP_OUT (1<<4) /* output capable */
-#define AC_PINCAP_IN (1<<5) /* input capable */
-#define AC_PINCAP_BALANCE (1<<6) /* balanced I/O capable */
-/* Note: This LR_SWAP pincap is defined in the Realtek ALC883 specification,
- * but is marked reserved in the Intel HDA specification.
- */
-#define AC_PINCAP_LR_SWAP (1<<7) /* L/R swap */
-/* Note: The same bit as LR_SWAP is newly defined as HDMI capability
- * in HD-audio specification
- */
-#define AC_PINCAP_HDMI (1<<7) /* HDMI pin */
-#define AC_PINCAP_DP (1<<24) /* DisplayPort pin, can
- * coexist with AC_PINCAP_HDMI
- */
-#define AC_PINCAP_VREF (0x37<<8)
-#define AC_PINCAP_VREF_SHIFT 8
-#define AC_PINCAP_EAPD (1<<16) /* EAPD capable */
-#define AC_PINCAP_HBR (1<<27) /* High Bit Rate */
-/* Vref status (used in pin cap) */
-#define AC_PINCAP_VREF_HIZ (1<<0) /* Hi-Z */
-#define AC_PINCAP_VREF_50 (1<<1) /* 50% */
-#define AC_PINCAP_VREF_GRD (1<<2) /* ground */
-#define AC_PINCAP_VREF_80 (1<<4) /* 80% */
-#define AC_PINCAP_VREF_100 (1<<5) /* 100% */
-
-/* Amplifier capabilities */
-#define AC_AMPCAP_OFFSET (0x7f<<0) /* 0dB offset */
-#define AC_AMPCAP_OFFSET_SHIFT 0
-#define AC_AMPCAP_NUM_STEPS (0x7f<<8) /* number of steps */
-#define AC_AMPCAP_NUM_STEPS_SHIFT 8
-#define AC_AMPCAP_STEP_SIZE (0x7f<<16) /* step size 0-32dB
- * in 0.25dB
- */
-#define AC_AMPCAP_STEP_SIZE_SHIFT 16
-#define AC_AMPCAP_MUTE (1<<31) /* mute capable */
-#define AC_AMPCAP_MUTE_SHIFT 31
-
-/* Connection list */
-#define AC_CLIST_LENGTH (0x7f<<0)
-#define AC_CLIST_LONG (1<<7)
-
-/* Supported power status */
-#define AC_PWRST_D0SUP (1<<0)
-#define AC_PWRST_D1SUP (1<<1)
-#define AC_PWRST_D2SUP (1<<2)
-#define AC_PWRST_D3SUP (1<<3)
-#define AC_PWRST_D3COLDSUP (1<<4)
-#define AC_PWRST_S3D3COLDSUP (1<<29)
-#define AC_PWRST_CLKSTOP (1<<30)
-#define AC_PWRST_EPSS (1U<<31)
-
-/* Power state values */
-#define AC_PWRST_SETTING (0xf<<0)
-#define AC_PWRST_ACTUAL (0xf<<4)
-#define AC_PWRST_ACTUAL_SHIFT 4
-#define AC_PWRST_D0 0x00
-#define AC_PWRST_D1 0x01
-#define AC_PWRST_D2 0x02
-#define AC_PWRST_D3 0x03
-
-/* Processing capabilies */
-#define AC_PCAP_BENIGN (1<<0)
-#define AC_PCAP_NUM_COEF (0xff<<8)
-#define AC_PCAP_NUM_COEF_SHIFT 8
-
-/* Volume knobs capabilities */
-#define AC_KNBCAP_NUM_STEPS (0x7f<<0)
-#define AC_KNBCAP_DELTA (1<<7)
-
-/* HDMI LPCM capabilities */
-#define AC_LPCMCAP_48K_CP_CHNS (0x0f<<0) /* max channels w/ CP-on */
-#define AC_LPCMCAP_48K_NO_CHNS (0x0f<<4) /* max channels w/o CP-on */
-#define AC_LPCMCAP_48K_20BIT (1<<8) /* 20b bitrate supported */
-#define AC_LPCMCAP_48K_24BIT (1<<9) /* 24b bitrate supported */
-#define AC_LPCMCAP_96K_CP_CHNS (0x0f<<10) /* max channels w/ CP-on */
-#define AC_LPCMCAP_96K_NO_CHNS (0x0f<<14) /* max channels w/o CP-on */
-#define AC_LPCMCAP_96K_20BIT (1<<18) /* 20b bitrate supported */
-#define AC_LPCMCAP_96K_24BIT (1<<19) /* 24b bitrate supported */
-#define AC_LPCMCAP_192K_CP_CHNS (0x0f<<20) /* max channels w/ CP-on */
-#define AC_LPCMCAP_192K_NO_CHNS (0x0f<<24) /* max channels w/o CP-on */
-#define AC_LPCMCAP_192K_20BIT (1<<28) /* 20b bitrate supported */
-#define AC_LPCMCAP_192K_24BIT (1<<29) /* 24b bitrate supported */
-#define AC_LPCMCAP_44K (1<<30) /* 44.1kHz support */
-#define AC_LPCMCAP_44K_MS (1<<31) /* 44.1kHz-multiplies support */
-
-/*
- * Control Parameters
- */
-
-/* Amp gain/mute */
-#define AC_AMP_MUTE (1<<7)
-#define AC_AMP_GAIN (0x7f)
-#define AC_AMP_GET_INDEX (0xf<<0)
-
-#define AC_AMP_GET_LEFT (1<<13)
-#define AC_AMP_GET_RIGHT (0<<13)
-#define AC_AMP_GET_OUTPUT (1<<15)
-#define AC_AMP_GET_INPUT (0<<15)
-
-#define AC_AMP_SET_INDEX (0xf<<8)
-#define AC_AMP_SET_INDEX_SHIFT 8
-#define AC_AMP_SET_RIGHT (1<<12)
-#define AC_AMP_SET_LEFT (1<<13)
-#define AC_AMP_SET_INPUT (1<<14)
-#define AC_AMP_SET_OUTPUT (1<<15)
-
-/* DIGITAL1 bits */
-#define AC_DIG1_ENABLE (1<<0)
-#define AC_DIG1_V (1<<1)
-#define AC_DIG1_VCFG (1<<2)
-#define AC_DIG1_EMPHASIS (1<<3)
-#define AC_DIG1_COPYRIGHT (1<<4)
-#define AC_DIG1_NONAUDIO (1<<5)
-#define AC_DIG1_PROFESSIONAL (1<<6)
-#define AC_DIG1_LEVEL (1<<7)
-
-/* DIGITAL2 bits */
-#define AC_DIG2_CC (0x7f<<0)
-
-/* Pin widget control - 8bit */
-#define AC_PINCTL_EPT (0x3<<0)
-#define AC_PINCTL_EPT_NATIVE 0
-#define AC_PINCTL_EPT_HBR 3
-#define AC_PINCTL_VREFEN (0x7<<0)
-#define AC_PINCTL_VREF_HIZ 0 /* Hi-Z */
-#define AC_PINCTL_VREF_50 1 /* 50% */
-#define AC_PINCTL_VREF_GRD 2 /* ground */
-#define AC_PINCTL_VREF_80 4 /* 80% */
-#define AC_PINCTL_VREF_100 5 /* 100% */
-#define AC_PINCTL_IN_EN (1<<5)
-#define AC_PINCTL_OUT_EN (1<<6)
-#define AC_PINCTL_HP_EN (1<<7)
-
-/* Pin sense - 32bit */
-#define AC_PINSENSE_IMPEDANCE_MASK (0x7fffffff)
-#define AC_PINSENSE_PRESENCE (1<<31)
-#define AC_PINSENSE_ELDV (1<<30) /* ELD valid (HDMI) */
-
-/* EAPD/BTL enable - 32bit */
-#define AC_EAPDBTL_BALANCED (1<<0)
-#define AC_EAPDBTL_EAPD (1<<1)
-#define AC_EAPDBTL_LR_SWAP (1<<2)
-
-/* HDMI ELD data */
-#define AC_ELDD_ELD_VALID (1<<31)
-#define AC_ELDD_ELD_DATA 0xff
-
-/* HDMI DIP size */
-#define AC_DIPSIZE_ELD_BUF (1<<3) /* ELD buf size of packet size */
-#define AC_DIPSIZE_PACK_IDX (0x07<<0) /* packet index */
-
-/* HDMI DIP index */
-#define AC_DIPIDX_PACK_IDX (0x07<<5) /* packet idnex */
-#define AC_DIPIDX_BYTE_IDX (0x1f<<0) /* byte index */
-
-/* HDMI DIP xmit (transmit) control */
-#define AC_DIPXMIT_MASK (0x3<<6)
-#define AC_DIPXMIT_DISABLE (0x0<<6) /* disable xmit */
-#define AC_DIPXMIT_ONCE (0x2<<6) /* xmit once then disable */
-#define AC_DIPXMIT_BEST (0x3<<6) /* best effort */
-
-/* HDMI content protection (CP) control */
-#define AC_CPCTRL_CES (1<<9) /* current encryption state */
-#define AC_CPCTRL_READY (1<<8) /* ready bit */
-#define AC_CPCTRL_SUBTAG (0x1f<<3) /* subtag for unsol-resp */
-#define AC_CPCTRL_STATE (3<<0) /* current CP request state */
-
-/* Converter channel <-> HDMI slot mapping */
-#define AC_CVTMAP_HDMI_SLOT (0xf<<0) /* HDMI slot number */
-#define AC_CVTMAP_CHAN (0xf<<4) /* converter channel number */
-
-/* configuration default - 32bit */
-#define AC_DEFCFG_SEQUENCE (0xf<<0)
-#define AC_DEFCFG_DEF_ASSOC (0xf<<4)
-#define AC_DEFCFG_ASSOC_SHIFT 4
-#define AC_DEFCFG_MISC (0xf<<8)
-#define AC_DEFCFG_MISC_SHIFT 8
-#define AC_DEFCFG_MISC_NO_PRESENCE (1<<0)
-#define AC_DEFCFG_COLOR (0xf<<12)
-#define AC_DEFCFG_COLOR_SHIFT 12
-#define AC_DEFCFG_CONN_TYPE (0xf<<16)
-#define AC_DEFCFG_CONN_TYPE_SHIFT 16
-#define AC_DEFCFG_DEVICE (0xf<<20)
-#define AC_DEFCFG_DEVICE_SHIFT 20
-#define AC_DEFCFG_LOCATION (0x3f<<24)
-#define AC_DEFCFG_LOCATION_SHIFT 24
-#define AC_DEFCFG_PORT_CONN (0x3<<30)
-#define AC_DEFCFG_PORT_CONN_SHIFT 30
-
-/* device device types (0x0-0xf) */
-enum {
- AC_JACK_LINE_OUT,
- AC_JACK_SPEAKER,
- AC_JACK_HP_OUT,
- AC_JACK_CD,
- AC_JACK_SPDIF_OUT,
- AC_JACK_DIG_OTHER_OUT,
- AC_JACK_MODEM_LINE_SIDE,
- AC_JACK_MODEM_HAND_SIDE,
- AC_JACK_LINE_IN,
- AC_JACK_AUX,
- AC_JACK_MIC_IN,
- AC_JACK_TELEPHONY,
- AC_JACK_SPDIF_IN,
- AC_JACK_DIG_OTHER_IN,
- AC_JACK_OTHER = 0xf,
-};
-
-/* jack connection types (0x0-0xf) */
-enum {
- AC_JACK_CONN_UNKNOWN,
- AC_JACK_CONN_1_8,
- AC_JACK_CONN_1_4,
- AC_JACK_CONN_ATAPI,
- AC_JACK_CONN_RCA,
- AC_JACK_CONN_OPTICAL,
- AC_JACK_CONN_OTHER_DIGITAL,
- AC_JACK_CONN_OTHER_ANALOG,
- AC_JACK_CONN_DIN,
- AC_JACK_CONN_XLR,
- AC_JACK_CONN_RJ11,
- AC_JACK_CONN_COMB,
- AC_JACK_CONN_OTHER = 0xf,
-};
-
-/* jack colors (0x0-0xf) */
-enum {
- AC_JACK_COLOR_UNKNOWN,
- AC_JACK_COLOR_BLACK,
- AC_JACK_COLOR_GREY,
- AC_JACK_COLOR_BLUE,
- AC_JACK_COLOR_GREEN,
- AC_JACK_COLOR_RED,
- AC_JACK_COLOR_ORANGE,
- AC_JACK_COLOR_YELLOW,
- AC_JACK_COLOR_PURPLE,
- AC_JACK_COLOR_PINK,
- AC_JACK_COLOR_WHITE = 0xe,
- AC_JACK_COLOR_OTHER,
-};
-
-/* Jack location (0x0-0x3f) */
-/* common case */
-enum {
- AC_JACK_LOC_NONE,
- AC_JACK_LOC_REAR,
- AC_JACK_LOC_FRONT,
- AC_JACK_LOC_LEFT,
- AC_JACK_LOC_RIGHT,
- AC_JACK_LOC_TOP,
- AC_JACK_LOC_BOTTOM,
-};
-/* bits 4-5 */
-enum {
- AC_JACK_LOC_EXTERNAL = 0x00,
- AC_JACK_LOC_INTERNAL = 0x10,
- AC_JACK_LOC_SEPARATE = 0x20,
- AC_JACK_LOC_OTHER = 0x30,
-};
-enum {
- /* external on primary chasis */
- AC_JACK_LOC_REAR_PANEL = 0x07,
- AC_JACK_LOC_DRIVE_BAY,
- /* internal */
- AC_JACK_LOC_RISER = 0x17,
- AC_JACK_LOC_HDMI,
- AC_JACK_LOC_ATAPI,
- /* others */
- AC_JACK_LOC_MOBILE_IN = 0x37,
- AC_JACK_LOC_MOBILE_OUT,
-};
-
-/* Port connectivity (0-3) */
-enum {
- AC_JACK_PORT_COMPLEX,
- AC_JACK_PORT_NONE,
- AC_JACK_PORT_FIXED,
- AC_JACK_PORT_BOTH,
-};
-
-/* max. connections to a widget */
-#define HDA_MAX_CONNECTIONS 32
-
-/* max. codec address */
-#define HDA_MAX_CODEC_ADDRESS 0x0f
-
-/* max number of PCM devics per card */
-#define HDA_MAX_PCMS 10
-
-/*
- * generic arrays
- */
-struct snd_array {
- unsigned int used;
- unsigned int alloced;
- unsigned int elem_size;
- unsigned int alloc_align;
- void *list;
-};
-
-void *snd_array_new(struct snd_array *array);
-void snd_array_free(struct snd_array *array);
-static inline void snd_array_init(struct snd_array *array, unsigned int size,
- unsigned int align)
-{
- array->elem_size = size;
- array->alloc_align = align;
-}
-
-static inline void *snd_array_elem(struct snd_array *array, unsigned int idx)
-{
- return array->list + idx * array->elem_size;
-}
-
-static inline unsigned int snd_array_index(struct snd_array *array, void *ptr)
-{
- return (unsigned long)(ptr - array->list) / array->elem_size;
-}
-
-/*
- * Structures
- */
-
-struct hda_bus;
-struct hda_beep;
-struct hda_codec;
-struct hda_pcm;
-struct hda_pcm_stream;
-struct hda_bus_unsolicited;
-
-/* NID type */
-typedef u16 hda_nid_t;
-
-/* bus operators */
-struct hda_bus_ops {
- /* send a single command */
- int (*command)(struct hda_bus *bus, unsigned int cmd);
- /* get a response from the last command */
- unsigned int (*get_response)(struct hda_bus *bus, unsigned int addr);
- /* free the private data */
- void (*private_free)(struct hda_bus *);
- /* attach a PCM stream */
- int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec,
- struct hda_pcm *pcm);
- /* reset bus for retry verb */
- void (*bus_reset)(struct hda_bus *bus);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- /* notify power-up/down from codec to controller */
- void (*pm_notify)(struct hda_bus *bus);
-#endif
-};
-
-/* template to pass to the bus constructor */
-struct hda_bus_template {
- void *private_data;
- struct pci_dev *pci;
- const char *modelname;
- int *power_save;
- struct hda_bus_ops ops;
-};
-
-/*
- * codec bus
- *
- * each controller needs to creata a hda_bus to assign the accessor.
- * A hda_bus contains several codecs in the list codec_list.
- */
-struct hda_bus {
- struct snd_card *card;
-
- /* copied from template */
- void *private_data;
- struct pci_dev *pci;
- const char *modelname;
- int *power_save;
- struct hda_bus_ops ops;
-
- /* codec linked list */
- struct list_head codec_list;
- /* link caddr -> codec */
- struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1];
-
- struct mutex cmd_mutex;
- struct mutex prepare_mutex;
-
- /* unsolicited event queue */
- struct hda_bus_unsolicited *unsol;
- char workq_name[16];
- struct workqueue_struct *workq; /* common workqueue for codecs */
-
- /* assigned PCMs */
- DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES);
-
- /* misc op flags */
- unsigned int needs_damn_long_delay :1;
- unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */
- unsigned int sync_write:1; /* sync after verb write */
- /* status for codec/controller */
- unsigned int shutdown :1; /* being unloaded */
- unsigned int rirb_error:1; /* error in codec communication */
- unsigned int response_reset:1; /* controller was reset */
- unsigned int in_reset:1; /* during reset operation */
- unsigned int power_keep_link_on:1; /* don't power off HDA link */
-};
-
-/*
- * codec preset
- *
- * Known codecs have the patch to build and set up the controls/PCMs
- * better than the generic parser.
- */
-struct hda_codec_preset {
- unsigned int id;
- unsigned int mask;
- unsigned int subs;
- unsigned int subs_mask;
- unsigned int rev;
- hda_nid_t afg, mfg;
- const char *name;
- int (*patch)(struct hda_codec *codec);
-};
-
-struct hda_codec_preset_list {
- const struct hda_codec_preset *preset;
- struct module *owner;
- struct list_head list;
-};
-
-/* initial hook */
-int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset);
-int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset);
-
-/* ops set by the preset patch */
-struct hda_codec_ops {
- int (*build_controls)(struct hda_codec *codec);
- int (*build_pcms)(struct hda_codec *codec);
- int (*init)(struct hda_codec *codec);
- void (*free)(struct hda_codec *codec);
- void (*unsol_event)(struct hda_codec *codec, unsigned int res);
-#ifdef SND_HDA_NEEDS_RESUME
- int (*suspend)(struct hda_codec *codec, pm_message_t state);
- int (*resume)(struct hda_codec *codec);
-#endif
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid);
-#endif
- void (*reboot_notify)(struct hda_codec *codec);
-};
-
-/* record for amp information cache */
-struct hda_cache_head {
- u32 key; /* hash key */
- u16 val; /* assigned value */
- u16 next; /* next link; -1 = terminal */
-};
-
-struct hda_amp_info {
- struct hda_cache_head head;
- u32 amp_caps; /* amp capabilities */
- u16 vol[2]; /* current volume & mute */
-};
-
-struct hda_cache_rec {
- u16 hash[64]; /* hash table for index */
- struct snd_array buf; /* record entries */
-};
-
-/* PCM callbacks */
-struct hda_pcm_ops {
- int (*open)(struct hda_pcm_stream *info, struct hda_codec *codec,
- struct snd_pcm_substream *substream);
- int (*close)(struct hda_pcm_stream *info, struct hda_codec *codec,
- struct snd_pcm_substream *substream);
- int (*prepare)(struct hda_pcm_stream *info, struct hda_codec *codec,
- unsigned int stream_tag, unsigned int format,
- struct snd_pcm_substream *substream);
- int (*cleanup)(struct hda_pcm_stream *info, struct hda_codec *codec,
- struct snd_pcm_substream *substream);
-};
-
-/* PCM information for each substream */
-struct hda_pcm_stream {
- unsigned int substreams; /* number of substreams, 0 = not exist*/
- unsigned int channels_min; /* min. number of channels */
- unsigned int channels_max; /* max. number of channels */
- hda_nid_t nid; /* default NID to query rates/formats/bps, or set up */
- u32 rates; /* supported rates */
- u64 formats; /* supported formats (SNDRV_PCM_FMTBIT_) */
- unsigned int maxbps; /* supported max. bit per sample */
- struct hda_pcm_ops ops;
-};
-
-/* PCM types */
-enum {
- HDA_PCM_TYPE_AUDIO,
- HDA_PCM_TYPE_SPDIF,
- HDA_PCM_TYPE_HDMI,
- HDA_PCM_TYPE_MODEM,
- HDA_PCM_NTYPES
-};
-
-/* for PCM creation */
-struct hda_pcm {
- char *name;
- struct hda_pcm_stream stream[2];
- unsigned int pcm_type; /* HDA_PCM_TYPE_XXX */
- int device; /* device number to assign */
- struct snd_pcm *pcm; /* assigned PCM instance */
-};
-
-/* codec information */
-struct hda_codec {
- struct hda_bus *bus;
- unsigned int addr; /* codec addr*/
- struct list_head list; /* list point */
-
- hda_nid_t afg; /* AFG node id */
- hda_nid_t mfg; /* MFG node id */
-
- /* ids */
- u8 afg_function_id;
- u8 mfg_function_id;
- u8 afg_unsol;
- u8 mfg_unsol;
- u32 vendor_id;
- u32 subsystem_id;
- u32 revision_id;
-
- /* detected preset */
- const struct hda_codec_preset *preset;
- struct module *owner;
- const char *vendor_name; /* codec vendor name */
- const char *chip_name; /* codec chip name */
- const char *modelname; /* model name for preset */
-
- /* set by patch */
- struct hda_codec_ops patch_ops;
-
- /* PCM to create, set by patch_ops.build_pcms callback */
- unsigned int num_pcms;
- struct hda_pcm *pcm_info;
-
- /* codec specific info */
- void *spec;
-
- /* beep device */
- struct hda_beep *beep;
- unsigned int beep_mode;
-
- /* widget capabilities cache */
- unsigned int num_nodes;
- hda_nid_t start_nid;
- u32 *wcaps;
-
- struct snd_array mixers; /* list of assigned mixer elements */
- struct snd_array nids; /* list of mapped mixer elements */
-
- struct hda_cache_rec amp_cache; /* cache for amp access */
- struct hda_cache_rec cmd_cache; /* cache for other commands */
-
- struct mutex spdif_mutex;
- struct mutex control_mutex;
- unsigned int spdif_status; /* IEC958 status bits */
- unsigned short spdif_ctls; /* SPDIF control bits */
- unsigned int spdif_in_enable; /* SPDIF input enable? */
- hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
- struct snd_array init_pins; /* initial (BIOS) pin configurations */
- struct snd_array driver_pins; /* pin configs set by codec parser */
- struct snd_array cvt_setups; /* audio convert setups */
-
-#ifdef CONFIG_SND_HDA_HWDEP
- struct snd_hwdep *hwdep; /* assigned hwdep device */
- struct snd_array init_verbs; /* additional init verbs */
- struct snd_array hints; /* additional hints */
- struct snd_array user_pins; /* default pin configs to override */
-#endif
-
- /* misc flags */
- unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each
- * status change
- * (e.g. Realtek codecs)
- */
- unsigned int pin_amp_workaround:1; /* pin out-amp takes index
- * (e.g. Conexant codecs)
- */
- unsigned int no_sticky_stream:1; /* no sticky-PCM stream assignment */
- unsigned int pins_shutup:1; /* pins are shut up */
- unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- unsigned int power_on :1; /* current (global) power-state */
- unsigned int power_transition :1; /* power-state in transition */
- int power_count; /* current (global) power refcount */
- struct delayed_work power_work; /* delayed task for powerdown */
- unsigned long power_on_acct;
- unsigned long power_off_acct;
- unsigned long power_jiffies;
-#endif
-
- /* codec-specific additional proc output */
- void (*proc_widget_hook)(struct snd_info_buffer *buffer,
- struct hda_codec *codec, hda_nid_t nid);
-};
-
-/* direction */
-enum {
- HDA_INPUT, HDA_OUTPUT
-};
-
-
-/*
- * constructors
- */
-int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
- struct hda_bus **busp);
-int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
- struct hda_codec **codecp);
-int snd_hda_codec_configure(struct hda_codec *codec);
-
-/*
- * low level functions
- */
-unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
- int direct,
- unsigned int verb, unsigned int parm);
-int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
- unsigned int verb, unsigned int parm);
-#define snd_hda_param_read(codec, nid, param) \
- snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param)
-int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t *start_id);
-int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t *conn_list, int max_conns);
-
-struct hda_verb {
- hda_nid_t nid;
- u32 verb;
- u32 param;
-};
-
-void snd_hda_sequence_write(struct hda_codec *codec,
- const struct hda_verb *seq);
-
-/* unsolicited event */
-int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex);
-
-/* cached write */
-#ifdef SND_HDA_NEEDS_RESUME
-int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb, unsigned int parm);
-void snd_hda_sequence_write_cache(struct hda_codec *codec,
- const struct hda_verb *seq);
-int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
- int direct, unsigned int verb, unsigned int parm);
-void snd_hda_codec_resume_cache(struct hda_codec *codec);
-#else
-#define snd_hda_codec_write_cache snd_hda_codec_write
-#define snd_hda_codec_update_cache snd_hda_codec_write
-#define snd_hda_sequence_write_cache snd_hda_sequence_write
-#endif
-
-/* the struct for codec->pin_configs */
-struct hda_pincfg {
- hda_nid_t nid;
- unsigned char ctrl; /* current pin control value */
- unsigned char pad; /* reserved */
- unsigned int cfg; /* default configuration */
-};
-
-unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid);
-int snd_hda_codec_set_pincfg(struct hda_codec *codec, hda_nid_t nid,
- unsigned int cfg);
-int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
- hda_nid_t nid, unsigned int cfg); /* for hwdep */
-void snd_hda_shutup_pins(struct hda_codec *codec);
-
-/*
- * Mixer
- */
-int snd_hda_build_controls(struct hda_bus *bus);
-int snd_hda_codec_build_controls(struct hda_codec *codec);
-
-/*
- * PCM
- */
-int snd_hda_build_pcms(struct hda_bus *bus);
-int snd_hda_codec_build_pcms(struct hda_codec *codec);
-
-int snd_hda_codec_prepare(struct hda_codec *codec,
- struct hda_pcm_stream *hinfo,
- unsigned int stream,
- unsigned int format,
- struct snd_pcm_substream *substream);
-void snd_hda_codec_cleanup(struct hda_codec *codec,
- struct hda_pcm_stream *hinfo,
- struct snd_pcm_substream *substream);
-
-void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
- u32 stream_tag,
- int channel_id, int format);
-void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
- int do_now);
-#define snd_hda_codec_cleanup_stream(codec, nid) \
- __snd_hda_codec_cleanup_stream(codec, nid, 0)
-unsigned int snd_hda_calc_stream_format(unsigned int rate,
- unsigned int channels,
- unsigned int format,
- unsigned int maxbps,
- unsigned short spdif_ctls);
-int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
- unsigned int format);
-
-/*
- * Misc
- */
-void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
-void snd_hda_bus_reboot_notify(struct hda_bus *bus);
-
-/*
- * power management
- */
-#ifdef CONFIG_PM
-int snd_hda_suspend(struct hda_bus *bus);
-int snd_hda_resume(struct hda_bus *bus);
-#endif
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static inline
-int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
-{
- if (codec->patch_ops.check_power_status)
- return codec->patch_ops.check_power_status(codec, nid);
- return 0;
-}
-#else
-#define hda_call_check_power_status(codec, nid) 0
-#endif
-
-/*
- * get widget information
- */
-const char *snd_hda_get_jack_connectivity(u32 cfg);
-const char *snd_hda_get_jack_type(u32 cfg);
-const char *snd_hda_get_jack_location(u32 cfg);
-
-/*
- * power saving
- */
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-void snd_hda_power_up(struct hda_codec *codec);
-void snd_hda_power_down(struct hda_codec *codec);
-#define snd_hda_codec_needs_resume(codec) codec->power_count
-void snd_hda_update_power_acct(struct hda_codec *codec);
-#else
-static inline void snd_hda_power_up(struct hda_codec *codec) {}
-static inline void snd_hda_power_down(struct hda_codec *codec) {}
-#define snd_hda_codec_needs_resume(codec) 1
-#endif
-
-#ifdef CONFIG_SND_HDA_PATCH_LOADER
-/*
- * patch firmware
- */
-int snd_hda_load_patch(struct hda_bus *bus, const char *patch);
-#endif
-
-/*
- * Codec modularization
- */
-
-/* Export symbols only for communication with codec drivers;
- * When built in kernel, all HD-audio drivers are supposed to be statically
- * linked to the kernel. Thus, the symbols don't have to (or shouldn't) be
- * exported unless it's built as a module.
- */
-#ifdef MODULE
-#define EXPORT_SYMBOL_HDA(sym) EXPORT_SYMBOL_GPL(sym)
-#else
-#define EXPORT_SYMBOL_HDA(sym)
-#endif
-
-#endif /* __SOUND_HDA_CODEC_H */
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
deleted file mode 100644
index cb0c23a6b473..000000000000
--- a/sound/pci/hda/hda_eld.c
+++ /dev/null
@@ -1,641 +0,0 @@
-/*
- * Generic routines and proc interface for ELD(EDID Like Data) information
- *
- * Copyright(c) 2008 Intel Corporation.
- *
- * Authors:
- * Wu Fengguang <wfg@linux.intel.com>
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <asm/unaligned.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-enum eld_versions {
- ELD_VER_CEA_861D = 2,
- ELD_VER_PARTIAL = 31,
-};
-
-enum cea_edid_versions {
- CEA_EDID_VER_NONE = 0,
- CEA_EDID_VER_CEA861 = 1,
- CEA_EDID_VER_CEA861A = 2,
- CEA_EDID_VER_CEA861BCD = 3,
- CEA_EDID_VER_RESERVED = 4,
-};
-
-static char *cea_speaker_allocation_names[] = {
- /* 0 */ "FL/FR",
- /* 1 */ "LFE",
- /* 2 */ "FC",
- /* 3 */ "RL/RR",
- /* 4 */ "RC",
- /* 5 */ "FLC/FRC",
- /* 6 */ "RLC/RRC",
- /* 7 */ "FLW/FRW",
- /* 8 */ "FLH/FRH",
- /* 9 */ "TC",
- /* 10 */ "FCH",
-};
-
-static char *eld_connection_type_names[4] = {
- "HDMI",
- "DisplayPort",
- "2-reserved",
- "3-reserved"
-};
-
-enum cea_audio_coding_types {
- AUDIO_CODING_TYPE_REF_STREAM_HEADER = 0,
- AUDIO_CODING_TYPE_LPCM = 1,
- AUDIO_CODING_TYPE_AC3 = 2,
- AUDIO_CODING_TYPE_MPEG1 = 3,
- AUDIO_CODING_TYPE_MP3 = 4,
- AUDIO_CODING_TYPE_MPEG2 = 5,
- AUDIO_CODING_TYPE_AACLC = 6,
- AUDIO_CODING_TYPE_DTS = 7,
- AUDIO_CODING_TYPE_ATRAC = 8,
- AUDIO_CODING_TYPE_SACD = 9,
- AUDIO_CODING_TYPE_EAC3 = 10,
- AUDIO_CODING_TYPE_DTS_HD = 11,
- AUDIO_CODING_TYPE_MLP = 12,
- AUDIO_CODING_TYPE_DST = 13,
- AUDIO_CODING_TYPE_WMAPRO = 14,
- AUDIO_CODING_TYPE_REF_CXT = 15,
- /* also include valid xtypes below */
- AUDIO_CODING_TYPE_HE_AAC = 15,
- AUDIO_CODING_TYPE_HE_AAC2 = 16,
- AUDIO_CODING_TYPE_MPEG_SURROUND = 17,
-};
-
-enum cea_audio_coding_xtypes {
- AUDIO_CODING_XTYPE_HE_REF_CT = 0,
- AUDIO_CODING_XTYPE_HE_AAC = 1,
- AUDIO_CODING_XTYPE_HE_AAC2 = 2,
- AUDIO_CODING_XTYPE_MPEG_SURROUND = 3,
- AUDIO_CODING_XTYPE_FIRST_RESERVED = 4,
-};
-
-static char *cea_audio_coding_type_names[] = {
- /* 0 */ "undefined",
- /* 1 */ "LPCM",
- /* 2 */ "AC-3",
- /* 3 */ "MPEG1",
- /* 4 */ "MP3",
- /* 5 */ "MPEG2",
- /* 6 */ "AAC-LC",
- /* 7 */ "DTS",
- /* 8 */ "ATRAC",
- /* 9 */ "DSD (One Bit Audio)",
- /* 10 */ "E-AC-3/DD+ (Dolby Digital Plus)",
- /* 11 */ "DTS-HD",
- /* 12 */ "MLP (Dolby TrueHD)",
- /* 13 */ "DST",
- /* 14 */ "WMAPro",
- /* 15 */ "HE-AAC",
- /* 16 */ "HE-AACv2",
- /* 17 */ "MPEG Surround",
-};
-
-/*
- * The following two lists are shared between
- * - HDMI audio InfoFrame (source to sink)
- * - CEA E-EDID Extension (sink to source)
- */
-
-/*
- * SS1:SS0 index => sample size
- */
-static int cea_sample_sizes[4] = {
- 0, /* 0: Refer to Stream Header */
- AC_SUPPCM_BITS_16, /* 1: 16 bits */
- AC_SUPPCM_BITS_20, /* 2: 20 bits */
- AC_SUPPCM_BITS_24, /* 3: 24 bits */
-};
-
-/*
- * SF2:SF1:SF0 index => sampling frequency
- */
-static int cea_sampling_frequencies[8] = {
- 0, /* 0: Refer to Stream Header */
- SNDRV_PCM_RATE_32000, /* 1: 32000Hz */
- SNDRV_PCM_RATE_44100, /* 2: 44100Hz */
- SNDRV_PCM_RATE_48000, /* 3: 48000Hz */
- SNDRV_PCM_RATE_88200, /* 4: 88200Hz */
- SNDRV_PCM_RATE_96000, /* 5: 96000Hz */
- SNDRV_PCM_RATE_176400, /* 6: 176400Hz */
- SNDRV_PCM_RATE_192000, /* 7: 192000Hz */
-};
-
-static unsigned char hdmi_get_eld_byte(struct hda_codec *codec, hda_nid_t nid,
- int byte_index)
-{
- unsigned int val;
-
- val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_HDMI_ELDD, byte_index);
-
-#ifdef BE_PARANOID
- printk(KERN_INFO "HDMI: ELD data byte %d: 0x%x\n", byte_index, val);
-#endif
-
- if ((val & AC_ELDD_ELD_VALID) == 0) {
- snd_printd(KERN_INFO "HDMI: invalid ELD data byte %d\n",
- byte_index);
- val = 0;
- }
-
- return val & AC_ELDD_ELD_DATA;
-}
-
-#define GRAB_BITS(buf, byte, lowbit, bits) \
-({ \
- BUILD_BUG_ON(lowbit > 7); \
- BUILD_BUG_ON(bits > 8); \
- BUILD_BUG_ON(bits <= 0); \
- \
- (buf[byte] >> (lowbit)) & ((1 << (bits)) - 1); \
-})
-
-static void hdmi_update_short_audio_desc(struct cea_sad *a,
- const unsigned char *buf)
-{
- int i;
- int val;
-
- val = GRAB_BITS(buf, 1, 0, 7);
- a->rates = 0;
- for (i = 0; i < 7; i++)
- if (val & (1 << i))
- a->rates |= cea_sampling_frequencies[i + 1];
-
- a->channels = GRAB_BITS(buf, 0, 0, 3);
- a->channels++;
-
- a->format = GRAB_BITS(buf, 0, 3, 4);
- switch (a->format) {
- case AUDIO_CODING_TYPE_REF_STREAM_HEADER:
- snd_printd(KERN_INFO
- "HDMI: audio coding type 0 not expected\n");
- break;
-
- case AUDIO_CODING_TYPE_LPCM:
- val = GRAB_BITS(buf, 2, 0, 3);
- a->sample_bits = 0;
- for (i = 0; i < 3; i++)
- if (val & (1 << i))
- a->sample_bits |= cea_sample_sizes[i + 1];
- break;
-
- case AUDIO_CODING_TYPE_AC3:
- case AUDIO_CODING_TYPE_MPEG1:
- case AUDIO_CODING_TYPE_MP3:
- case AUDIO_CODING_TYPE_MPEG2:
- case AUDIO_CODING_TYPE_AACLC:
- case AUDIO_CODING_TYPE_DTS:
- case AUDIO_CODING_TYPE_ATRAC:
- a->max_bitrate = GRAB_BITS(buf, 2, 0, 8);
- a->max_bitrate *= 8000;
- break;
-
- case AUDIO_CODING_TYPE_SACD:
- break;
-
- case AUDIO_CODING_TYPE_EAC3:
- break;
-
- case AUDIO_CODING_TYPE_DTS_HD:
- break;
-
- case AUDIO_CODING_TYPE_MLP:
- break;
-
- case AUDIO_CODING_TYPE_DST:
- break;
-
- case AUDIO_CODING_TYPE_WMAPRO:
- a->profile = GRAB_BITS(buf, 2, 0, 3);
- break;
-
- case AUDIO_CODING_TYPE_REF_CXT:
- a->format = GRAB_BITS(buf, 2, 3, 5);
- if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT ||
- a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) {
- snd_printd(KERN_INFO
- "HDMI: audio coding xtype %d not expected\n",
- a->format);
- a->format = 0;
- } else
- a->format += AUDIO_CODING_TYPE_HE_AAC -
- AUDIO_CODING_XTYPE_HE_AAC;
- break;
- }
-}
-
-/*
- * Be careful, ELD buf could be totally rubbish!
- */
-static int hdmi_update_eld(struct hdmi_eld *e,
- const unsigned char *buf, int size)
-{
- int mnl;
- int i;
-
- e->eld_ver = GRAB_BITS(buf, 0, 3, 5);
- if (e->eld_ver != ELD_VER_CEA_861D &&
- e->eld_ver != ELD_VER_PARTIAL) {
- snd_printd(KERN_INFO "HDMI: Unknown ELD version %d\n",
- e->eld_ver);
- goto out_fail;
- }
-
- e->eld_size = size;
- e->baseline_len = GRAB_BITS(buf, 2, 0, 8);
- mnl = GRAB_BITS(buf, 4, 0, 5);
- e->cea_edid_ver = GRAB_BITS(buf, 4, 5, 3);
-
- e->support_hdcp = GRAB_BITS(buf, 5, 0, 1);
- e->support_ai = GRAB_BITS(buf, 5, 1, 1);
- e->conn_type = GRAB_BITS(buf, 5, 2, 2);
- e->sad_count = GRAB_BITS(buf, 5, 4, 4);
-
- e->aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2;
- e->spk_alloc = GRAB_BITS(buf, 7, 0, 7);
-
- e->port_id = get_unaligned_le64(buf + 8);
-
- /* not specified, but the spec's tendency is little endian */
- e->manufacture_id = get_unaligned_le16(buf + 16);
- e->product_id = get_unaligned_le16(buf + 18);
-
- if (mnl > ELD_MAX_MNL) {
- snd_printd(KERN_INFO "HDMI: MNL is reserved value %d\n", mnl);
- goto out_fail;
- } else if (ELD_FIXED_BYTES + mnl > size) {
- snd_printd(KERN_INFO "HDMI: out of range MNL %d\n", mnl);
- goto out_fail;
- } else
- strlcpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl);
-
- for (i = 0; i < e->sad_count; i++) {
- if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) {
- snd_printd(KERN_INFO "HDMI: out of range SAD %d\n", i);
- goto out_fail;
- }
- hdmi_update_short_audio_desc(e->sad + i,
- buf + ELD_FIXED_BYTES + mnl + 3 * i);
- }
-
- return 0;
-
-out_fail:
- e->eld_ver = 0;
- return -EINVAL;
-}
-
-static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid)
-{
- int eldv;
- int present;
-
- present = snd_hda_pin_sense(codec, nid);
- eldv = (present & AC_PINSENSE_ELDV);
- present = (present & AC_PINSENSE_PRESENCE);
-
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- printk(KERN_INFO "HDMI: sink_present = %d, eld_valid = %d\n",
- !!present, !!eldv);
-#endif
-
- return eldv && present;
-}
-
-int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
-{
- return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
- AC_DIPSIZE_ELD_BUF);
-}
-
-int snd_hdmi_get_eld(struct hdmi_eld *eld,
- struct hda_codec *codec, hda_nid_t nid)
-{
- int i;
- int ret;
- int size;
- unsigned char *buf;
-
- if (!hdmi_eld_valid(codec, nid))
- return -ENOENT;
-
- size = snd_hdmi_get_eld_size(codec, nid);
- if (size == 0) {
- /* wfg: workaround for ASUS P5E-VM HDMI board */
- snd_printd(KERN_INFO "HDMI: ELD buf size is 0, force 128\n");
- size = 128;
- }
- if (size < ELD_FIXED_BYTES || size > PAGE_SIZE) {
- snd_printd(KERN_INFO "HDMI: invalid ELD buf size %d\n", size);
- return -ERANGE;
- }
-
- buf = kmalloc(size, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- for (i = 0; i < size; i++)
- buf[i] = hdmi_get_eld_byte(codec, nid, i);
-
- ret = hdmi_update_eld(eld, buf, size);
-
- kfree(buf);
- return ret;
-}
-
-static void hdmi_show_short_audio_desc(struct cea_sad *a)
-{
- char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
- char buf2[8 + SND_PRINT_BITS_ADVISED_BUFSIZE] = ", bits =";
-
- if (!a->format)
- return;
-
- snd_print_pcm_rates(a->rates, buf, sizeof(buf));
-
- if (a->format == AUDIO_CODING_TYPE_LPCM)
- snd_print_pcm_bits(a->sample_bits, buf2 + 8, sizeof(buf2 - 8));
- else if (a->max_bitrate)
- snprintf(buf2, sizeof(buf2),
- ", max bitrate = %d", a->max_bitrate);
- else
- buf2[0] = '\0';
-
- printk(KERN_INFO "HDMI: supports coding type %s:"
- " channels = %d, rates =%s%s\n",
- cea_audio_coding_type_names[a->format],
- a->channels,
- buf,
- buf2);
-}
-
-void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
-{
- int i, j;
-
- for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
- if (spk_alloc & (1 << i))
- j += snprintf(buf + j, buflen - j, " %s",
- cea_speaker_allocation_names[i]);
- }
- buf[j] = '\0'; /* necessary when j == 0 */
-}
-
-void snd_hdmi_show_eld(struct hdmi_eld *e)
-{
- int i;
-
- printk(KERN_INFO "HDMI: detected monitor %s at connection type %s\n",
- e->monitor_name,
- eld_connection_type_names[e->conn_type]);
-
- if (e->spk_alloc) {
- char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
- snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
- printk(KERN_INFO "HDMI: available speakers:%s\n", buf);
- }
-
- for (i = 0; i < e->sad_count; i++)
- hdmi_show_short_audio_desc(e->sad + i);
-}
-
-#ifdef CONFIG_PROC_FS
-
-static void hdmi_print_sad_info(int i, struct cea_sad *a,
- struct snd_info_buffer *buffer)
-{
- char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
-
- snd_iprintf(buffer, "sad%d_coding_type\t[0x%x] %s\n",
- i, a->format, cea_audio_coding_type_names[a->format]);
- snd_iprintf(buffer, "sad%d_channels\t\t%d\n", i, a->channels);
-
- snd_print_pcm_rates(a->rates, buf, sizeof(buf));
- snd_iprintf(buffer, "sad%d_rates\t\t[0x%x]%s\n", i, a->rates, buf);
-
- if (a->format == AUDIO_CODING_TYPE_LPCM) {
- snd_print_pcm_bits(a->sample_bits, buf, sizeof(buf));
- snd_iprintf(buffer, "sad%d_bits\t\t[0x%x]%s\n",
- i, a->sample_bits, buf);
- }
-
- if (a->max_bitrate)
- snd_iprintf(buffer, "sad%d_max_bitrate\t%d\n",
- i, a->max_bitrate);
-
- if (a->profile)
- snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile);
-}
-
-static void hdmi_print_eld_info(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
-{
- struct hdmi_eld *e = entry->private_data;
- char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
- int i;
- static char *eld_versoin_names[32] = {
- "reserved",
- "reserved",
- "CEA-861D or below",
- [3 ... 30] = "reserved",
- [31] = "partial"
- };
- static char *cea_edid_version_names[8] = {
- "no CEA EDID Timing Extension block present",
- "CEA-861",
- "CEA-861-A",
- "CEA-861-B, C or D",
- [4 ... 7] = "reserved"
- };
-
- snd_iprintf(buffer, "monitor_present\t\t%d\n", e->monitor_present);
- snd_iprintf(buffer, "eld_valid\t\t%d\n", e->eld_valid);
- snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name);
- snd_iprintf(buffer, "connection_type\t\t%s\n",
- eld_connection_type_names[e->conn_type]);
- snd_iprintf(buffer, "eld_version\t\t[0x%x] %s\n", e->eld_ver,
- eld_versoin_names[e->eld_ver]);
- snd_iprintf(buffer, "edid_version\t\t[0x%x] %s\n", e->cea_edid_ver,
- cea_edid_version_names[e->cea_edid_ver]);
- snd_iprintf(buffer, "manufacture_id\t\t0x%x\n", e->manufacture_id);
- snd_iprintf(buffer, "product_id\t\t0x%x\n", e->product_id);
- snd_iprintf(buffer, "port_id\t\t\t0x%llx\n", (long long)e->port_id);
- snd_iprintf(buffer, "support_hdcp\t\t%d\n", e->support_hdcp);
- snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai);
- snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay);
-
- snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
- snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf);
-
- snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count);
-
- for (i = 0; i < e->sad_count; i++)
- hdmi_print_sad_info(i, e->sad + i, buffer);
-}
-
-static void hdmi_write_eld_info(struct snd_info_entry *entry,
- struct snd_info_buffer *buffer)
-{
- struct hdmi_eld *e = entry->private_data;
- char line[64];
- char name[64];
- char *sname;
- long long val;
- unsigned int n;
-
- while (!snd_info_get_line(buffer, line, sizeof(line))) {
- if (sscanf(line, "%s %llx", name, &val) != 2)
- continue;
- /*
- * We don't allow modification to these fields:
- * monitor_name manufacture_id product_id
- * eld_version edid_version
- */
- if (!strcmp(name, "monitor_present"))
- e->monitor_present = val;
- else if (!strcmp(name, "eld_valid"))
- e->eld_valid = val;
- else if (!strcmp(name, "connection_type"))
- e->conn_type = val;
- else if (!strcmp(name, "port_id"))
- e->port_id = val;
- else if (!strcmp(name, "support_hdcp"))
- e->support_hdcp = val;
- else if (!strcmp(name, "support_ai"))
- e->support_ai = val;
- else if (!strcmp(name, "audio_sync_delay"))
- e->aud_synch_delay = val;
- else if (!strcmp(name, "speakers"))
- e->spk_alloc = val;
- else if (!strcmp(name, "sad_count"))
- e->sad_count = val;
- else if (!strncmp(name, "sad", 3)) {
- sname = name + 4;
- n = name[3] - '0';
- if (name[4] >= '0' && name[4] <= '9') {
- sname++;
- n = 10 * n + name[4] - '0';
- }
- if (n >= ELD_MAX_SAD)
- continue;
- if (!strcmp(sname, "_coding_type"))
- e->sad[n].format = val;
- else if (!strcmp(sname, "_channels"))
- e->sad[n].channels = val;
- else if (!strcmp(sname, "_rates"))
- e->sad[n].rates = val;
- else if (!strcmp(sname, "_bits"))
- e->sad[n].sample_bits = val;
- else if (!strcmp(sname, "_max_bitrate"))
- e->sad[n].max_bitrate = val;
- else if (!strcmp(sname, "_profile"))
- e->sad[n].profile = val;
- if (n >= e->sad_count)
- e->sad_count = n + 1;
- }
- }
-}
-
-
-int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
- int index)
-{
- char name[32];
- struct snd_info_entry *entry;
- int err;
-
- snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
- err = snd_card_proc_new(codec->bus->card, name, &entry);
- if (err < 0)
- return err;
-
- snd_info_set_text_ops(entry, eld, hdmi_print_eld_info);
- entry->c.text.write = hdmi_write_eld_info;
- entry->mode |= S_IWUSR;
- eld->proc_entry = entry;
-
- return 0;
-}
-
-void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
-{
- if (!codec->bus->shutdown && eld->proc_entry) {
- snd_device_free(codec->bus->card, eld->proc_entry);
- eld->proc_entry = NULL;
- }
-}
-
-#endif /* CONFIG_PROC_FS */
-
-/* update PCM info based on ELD */
-void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm,
- struct hda_pcm_stream *codec_pars)
-{
- int i;
-
- pcm->rates = 0;
- pcm->formats = 0;
- pcm->maxbps = 0;
- pcm->channels_min = -1;
- pcm->channels_max = 0;
- for (i = 0; i < eld->sad_count; i++) {
- struct cea_sad *a = &eld->sad[i];
- pcm->rates |= a->rates;
- if (a->channels < pcm->channels_min)
- pcm->channels_min = a->channels;
- if (a->channels > pcm->channels_max)
- pcm->channels_max = a->channels;
- if (a->format == AUDIO_CODING_TYPE_LPCM) {
- if (a->sample_bits & AC_SUPPCM_BITS_16) {
- pcm->formats |= SNDRV_PCM_FMTBIT_S16_LE;
- if (pcm->maxbps < 16)
- pcm->maxbps = 16;
- }
- if (a->sample_bits & AC_SUPPCM_BITS_20) {
- pcm->formats |= SNDRV_PCM_FMTBIT_S32_LE;
- if (pcm->maxbps < 20)
- pcm->maxbps = 20;
- }
- if (a->sample_bits & AC_SUPPCM_BITS_24) {
- pcm->formats |= SNDRV_PCM_FMTBIT_S32_LE;
- if (pcm->maxbps < 24)
- pcm->maxbps = 24;
- }
- }
- }
-
- if (!codec_pars)
- return;
-
- /* restrict the parameters by the values the codec provides */
- pcm->rates &= codec_pars->rates;
- pcm->formats &= codec_pars->formats;
- pcm->channels_min = max(pcm->channels_min, codec_pars->channels_min);
- pcm->channels_max = min(pcm->channels_max, codec_pars->channels_max);
- pcm->maxbps = min(pcm->maxbps, codec_pars->maxbps);
-}
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
deleted file mode 100644
index fb0582f8d725..000000000000
--- a/sound/pci/hda/hda_generic.c
+++ /dev/null
@@ -1,1083 +0,0 @@
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * Generic widget tree parser
- *
- * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-/* widget node for parsing */
-struct hda_gnode {
- hda_nid_t nid; /* NID of this widget */
- unsigned short nconns; /* number of input connections */
- hda_nid_t *conn_list;
- hda_nid_t slist[2]; /* temporay list */
- unsigned int wid_caps; /* widget capabilities */
- unsigned char type; /* widget type */
- unsigned char pin_ctl; /* pin controls */
- unsigned char checked; /* the flag indicates that the node is already parsed */
- unsigned int pin_caps; /* pin widget capabilities */
- unsigned int def_cfg; /* default configuration */
- unsigned int amp_out_caps; /* AMP out capabilities */
- unsigned int amp_in_caps; /* AMP in capabilities */
- struct list_head list;
-};
-
-/* patch-specific record */
-
-#define MAX_PCM_VOLS 2
-struct pcm_vol {
- struct hda_gnode *node; /* Node for PCM volume */
- unsigned int index; /* connection of PCM volume */
-};
-
-struct hda_gspec {
- struct hda_gnode *dac_node[2]; /* DAC node */
- struct hda_gnode *out_pin_node[2]; /* Output pin (Line-Out) node */
- struct pcm_vol pcm_vol[MAX_PCM_VOLS]; /* PCM volumes */
- unsigned int pcm_vol_nodes; /* number of PCM volumes */
-
- struct hda_gnode *adc_node; /* ADC node */
- struct hda_gnode *cap_vol_node; /* Node for capture volume */
- unsigned int cur_cap_src; /* current capture source */
- struct hda_input_mux input_mux;
-
- unsigned int def_amp_in_caps;
- unsigned int def_amp_out_caps;
-
- struct hda_pcm pcm_rec; /* PCM information */
-
- struct list_head nid_list; /* list of widgets */
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define MAX_LOOPBACK_AMPS 7
- struct hda_loopback_check loopback;
- int num_loopbacks;
- struct hda_amp_list loopback_list[MAX_LOOPBACK_AMPS + 1];
-#endif
-};
-
-/*
- * retrieve the default device type from the default config value
- */
-#define defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> \
- AC_DEFCFG_DEVICE_SHIFT)
-#define defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> \
- AC_DEFCFG_LOCATION_SHIFT)
-#define defcfg_port_conn(node) (((node)->def_cfg & AC_DEFCFG_PORT_CONN) >> \
- AC_DEFCFG_PORT_CONN_SHIFT)
-
-/*
- * destructor
- */
-static void snd_hda_generic_free(struct hda_codec *codec)
-{
- struct hda_gspec *spec = codec->spec;
- struct hda_gnode *node, *n;
-
- if (! spec)
- return;
- /* free all widgets */
- list_for_each_entry_safe(node, n, &spec->nid_list, list) {
- if (node->conn_list != node->slist)
- kfree(node->conn_list);
- kfree(node);
- }
- kfree(spec);
-}
-
-
-/*
- * add a new widget node and read its attributes
- */
-static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid_t nid)
-{
- struct hda_gnode *node;
- int nconns;
- hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
-
- node = kzalloc(sizeof(*node), GFP_KERNEL);
- if (node == NULL)
- return -ENOMEM;
- node->nid = nid;
- node->wid_caps = get_wcaps(codec, nid);
- node->type = get_wcaps_type(node->wid_caps);
- if (node->wid_caps & AC_WCAP_CONN_LIST) {
- nconns = snd_hda_get_connections(codec, nid, conn_list,
- HDA_MAX_CONNECTIONS);
- if (nconns < 0) {
- kfree(node);
- return nconns;
- }
- } else {
- nconns = 0;
- }
- if (nconns <= ARRAY_SIZE(node->slist))
- node->conn_list = node->slist;
- else {
- node->conn_list = kmalloc(sizeof(hda_nid_t) * nconns,
- GFP_KERNEL);
- if (! node->conn_list) {
- snd_printk(KERN_ERR "hda-generic: cannot malloc\n");
- kfree(node);
- return -ENOMEM;
- }
- }
- memcpy(node->conn_list, conn_list, nconns * sizeof(hda_nid_t));
- node->nconns = nconns;
-
- if (node->type == AC_WID_PIN) {
- node->pin_caps = snd_hda_query_pin_caps(codec, node->nid);
- node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- node->def_cfg = snd_hda_codec_get_pincfg(codec, node->nid);
- }
-
- if (node->wid_caps & AC_WCAP_OUT_AMP) {
- if (node->wid_caps & AC_WCAP_AMP_OVRD)
- node->amp_out_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_OUT_CAP);
- if (! node->amp_out_caps)
- node->amp_out_caps = spec->def_amp_out_caps;
- }
- if (node->wid_caps & AC_WCAP_IN_AMP) {
- if (node->wid_caps & AC_WCAP_AMP_OVRD)
- node->amp_in_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_IN_CAP);
- if (! node->amp_in_caps)
- node->amp_in_caps = spec->def_amp_in_caps;
- }
- list_add_tail(&node->list, &spec->nid_list);
- return 0;
-}
-
-/*
- * build the AFG subtree
- */
-static int build_afg_tree(struct hda_codec *codec)
-{
- struct hda_gspec *spec = codec->spec;
- int i, nodes, err;
- hda_nid_t nid;
-
- if (snd_BUG_ON(!spec))
- return -EINVAL;
-
- spec->def_amp_out_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_OUT_CAP);
- spec->def_amp_in_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_IN_CAP);
-
- nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
- if (! nid || nodes < 0) {
- printk(KERN_ERR "Invalid AFG subtree\n");
- return -EINVAL;
- }
-
- /* parse all nodes belonging to the AFG */
- for (i = 0; i < nodes; i++, nid++) {
- if ((err = add_new_node(codec, spec, nid)) < 0)
- return err;
- }
-
- return 0;
-}
-
-
-/*
- * look for the node record for the given NID
- */
-/* FIXME: should avoid the braindead linear search */
-static struct hda_gnode *hda_get_node(struct hda_gspec *spec, hda_nid_t nid)
-{
- struct hda_gnode *node;
-
- list_for_each_entry(node, &spec->nid_list, list) {
- if (node->nid == nid)
- return node;
- }
- return NULL;
-}
-
-/*
- * unmute (and set max vol) the output amplifier
- */
-static int unmute_output(struct hda_codec *codec, struct hda_gnode *node)
-{
- unsigned int val, ofs;
- snd_printdd("UNMUTE OUT: NID=0x%x\n", node->nid);
- val = (node->amp_out_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
- ofs = (node->amp_out_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
- if (val >= ofs)
- val -= ofs;
- snd_hda_codec_amp_stereo(codec, node->nid, HDA_OUTPUT, 0, 0xff, val);
- return 0;
-}
-
-/*
- * unmute (and set max vol) the input amplifier
- */
-static int unmute_input(struct hda_codec *codec, struct hda_gnode *node, unsigned int index)
-{
- unsigned int val, ofs;
- snd_printdd("UNMUTE IN: NID=0x%x IDX=0x%x\n", node->nid, index);
- val = (node->amp_in_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
- ofs = (node->amp_in_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
- if (val >= ofs)
- val -= ofs;
- snd_hda_codec_amp_stereo(codec, node->nid, HDA_INPUT, index, 0xff, val);
- return 0;
-}
-
-/*
- * select the input connection of the given node.
- */
-static int select_input_connection(struct hda_codec *codec, struct hda_gnode *node,
- unsigned int index)
-{
- snd_printdd("CONNECT: NID=0x%x IDX=0x%x\n", node->nid, index);
- return snd_hda_codec_write_cache(codec, node->nid, 0,
- AC_VERB_SET_CONNECT_SEL, index);
-}
-
-/*
- * clear checked flag of each node in the node list
- */
-static void clear_check_flags(struct hda_gspec *spec)
-{
- struct hda_gnode *node;
-
- list_for_each_entry(node, &spec->nid_list, list) {
- node->checked = 0;
- }
-}
-
-/*
- * parse the output path recursively until reach to an audio output widget
- *
- * returns 0 if not found, 1 if found, or a negative error code.
- */
-static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec,
- struct hda_gnode *node, int dac_idx)
-{
- int i, err;
- struct hda_gnode *child;
-
- if (node->checked)
- return 0;
-
- node->checked = 1;
- if (node->type == AC_WID_AUD_OUT) {
- if (node->wid_caps & AC_WCAP_DIGITAL) {
- snd_printdd("Skip Digital OUT node %x\n", node->nid);
- return 0;
- }
- snd_printdd("AUD_OUT found %x\n", node->nid);
- if (spec->dac_node[dac_idx]) {
- /* already DAC node is assigned, just unmute & connect */
- return node == spec->dac_node[dac_idx];
- }
- spec->dac_node[dac_idx] = node;
- if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
- spec->pcm_vol_nodes < MAX_PCM_VOLS) {
- spec->pcm_vol[spec->pcm_vol_nodes].node = node;
- spec->pcm_vol[spec->pcm_vol_nodes].index = 0;
- spec->pcm_vol_nodes++;
- }
- return 1; /* found */
- }
-
- for (i = 0; i < node->nconns; i++) {
- child = hda_get_node(spec, node->conn_list[i]);
- if (! child)
- continue;
- err = parse_output_path(codec, spec, child, dac_idx);
- if (err < 0)
- return err;
- else if (err > 0) {
- /* found one,
- * select the path, unmute both input and output
- */
- if (node->nconns > 1)
- select_input_connection(codec, node, i);
- unmute_input(codec, node, i);
- unmute_output(codec, node);
- if (spec->dac_node[dac_idx] &&
- spec->pcm_vol_nodes < MAX_PCM_VOLS &&
- !(spec->dac_node[dac_idx]->wid_caps &
- AC_WCAP_OUT_AMP)) {
- if ((node->wid_caps & AC_WCAP_IN_AMP) ||
- (node->wid_caps & AC_WCAP_OUT_AMP)) {
- int n = spec->pcm_vol_nodes;
- spec->pcm_vol[n].node = node;
- spec->pcm_vol[n].index = i;
- spec->pcm_vol_nodes++;
- }
- }
- return 1;
- }
- }
- return 0;
-}
-
-/*
- * Look for the output PIN widget with the given jack type
- * and parse the output path to that PIN.
- *
- * Returns the PIN node when the path to DAC is established.
- */
-static struct hda_gnode *parse_output_jack(struct hda_codec *codec,
- struct hda_gspec *spec,
- int jack_type)
-{
- struct hda_gnode *node;
- int err;
-
- list_for_each_entry(node, &spec->nid_list, list) {
- if (node->type != AC_WID_PIN)
- continue;
- /* output capable? */
- if (! (node->pin_caps & AC_PINCAP_OUT))
- continue;
- if (defcfg_port_conn(node) == AC_JACK_PORT_NONE)
- continue; /* unconnected */
- if (jack_type >= 0) {
- if (jack_type != defcfg_type(node))
- continue;
- if (node->wid_caps & AC_WCAP_DIGITAL)
- continue; /* skip SPDIF */
- } else {
- /* output as default? */
- if (! (node->pin_ctl & AC_PINCTL_OUT_EN))
- continue;
- }
- clear_check_flags(spec);
- err = parse_output_path(codec, spec, node, 0);
- if (err < 0)
- return NULL;
- if (! err && spec->out_pin_node[0]) {
- err = parse_output_path(codec, spec, node, 1);
- if (err < 0)
- return NULL;
- }
- if (err > 0) {
- /* unmute the PIN output */
- unmute_output(codec, node);
- /* set PIN-Out enable */
- snd_hda_codec_write_cache(codec, node->nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- AC_PINCTL_OUT_EN |
- ((node->pin_caps & AC_PINCAP_HP_DRV) ?
- AC_PINCTL_HP_EN : 0));
- return node;
- }
- }
- return NULL;
-}
-
-
-/*
- * parse outputs
- */
-static int parse_output(struct hda_codec *codec)
-{
- struct hda_gspec *spec = codec->spec;
- struct hda_gnode *node;
-
- /*
- * Look for the output PIN widget
- */
- /* first, look for the line-out pin */
- node = parse_output_jack(codec, spec, AC_JACK_LINE_OUT);
- if (node) /* found, remember the PIN node */
- spec->out_pin_node[0] = node;
- else {
- /* if no line-out is found, try speaker out */
- node = parse_output_jack(codec, spec, AC_JACK_SPEAKER);
- if (node)
- spec->out_pin_node[0] = node;
- }
- /* look for the HP-out pin */
- node = parse_output_jack(codec, spec, AC_JACK_HP_OUT);
- if (node) {
- if (! spec->out_pin_node[0])
- spec->out_pin_node[0] = node;
- else
- spec->out_pin_node[1] = node;
- }
-
- if (! spec->out_pin_node[0]) {
- /* no line-out or HP pins found,
- * then choose for the first output pin
- */
- spec->out_pin_node[0] = parse_output_jack(codec, spec, -1);
- if (! spec->out_pin_node[0])
- snd_printd("hda_generic: no proper output path found\n");
- }
-
- return 0;
-}
-
-/*
- * input MUX
- */
-
-/* control callbacks */
-static int capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gspec *spec = codec->spec;
- return snd_hda_input_mux_info(&spec->input_mux, uinfo);
-}
-
-static int capture_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gspec *spec = codec->spec;
-
- ucontrol->value.enumerated.item[0] = spec->cur_cap_src;
- return 0;
-}
-
-static int capture_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gspec *spec = codec->spec;
- return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol,
- spec->adc_node->nid, &spec->cur_cap_src);
-}
-
-/*
- * return the string name of the given input PIN widget
- */
-static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl)
-{
- unsigned int location = defcfg_location(node);
- switch (defcfg_type(node)) {
- case AC_JACK_LINE_IN:
- if ((location & 0x0f) == AC_JACK_LOC_FRONT)
- return "Front Line";
- return "Line";
- case AC_JACK_CD:
-#if 0
- if (pinctl)
- *pinctl |= AC_PINCTL_VREF_GRD;
-#endif
- return "CD";
- case AC_JACK_AUX:
- if ((location & 0x0f) == AC_JACK_LOC_FRONT)
- return "Front Aux";
- return "Aux";
- case AC_JACK_MIC_IN:
- if (pinctl &&
- (node->pin_caps &
- (AC_PINCAP_VREF_80 << AC_PINCAP_VREF_SHIFT)))
- *pinctl |= AC_PINCTL_VREF_80;
- if ((location & 0x0f) == AC_JACK_LOC_FRONT)
- return "Front Mic";
- return "Mic";
- case AC_JACK_SPDIF_IN:
- return "SPDIF";
- case AC_JACK_DIG_OTHER_IN:
- return "Digital";
- }
- return NULL;
-}
-
-/*
- * parse the nodes recursively until reach to the input PIN
- *
- * returns 0 if not found, 1 if found, or a negative error code.
- */
-static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
- struct hda_gnode *node, int idx)
-{
- int i, err;
- unsigned int pinctl;
- const char *type;
-
- if (node->checked)
- return 0;
-
- node->checked = 1;
- if (node->type != AC_WID_PIN) {
- for (i = 0; i < node->nconns; i++) {
- struct hda_gnode *child;
- child = hda_get_node(spec, node->conn_list[i]);
- if (! child)
- continue;
- err = parse_adc_sub_nodes(codec, spec, child, idx);
- if (err < 0)
- return err;
- if (err > 0) {
- /* found one,
- * select the path, unmute both input and output
- */
- if (node->nconns > 1)
- select_input_connection(codec, node, i);
- unmute_input(codec, node, i);
- unmute_output(codec, node);
- return err;
- }
- }
- return 0;
- }
-
- /* input capable? */
- if (! (node->pin_caps & AC_PINCAP_IN))
- return 0;
-
- if (defcfg_port_conn(node) == AC_JACK_PORT_NONE)
- return 0; /* unconnected */
-
- if (node->wid_caps & AC_WCAP_DIGITAL)
- return 0; /* skip SPDIF */
-
- if (spec->input_mux.num_items >= HDA_MAX_NUM_INPUTS) {
- snd_printk(KERN_ERR "hda_generic: Too many items for capture\n");
- return -EINVAL;
- }
-
- pinctl = AC_PINCTL_IN_EN;
- /* create a proper capture source label */
- type = get_input_type(node, &pinctl);
- if (! type) {
- /* input as default? */
- if (! (node->pin_ctl & AC_PINCTL_IN_EN))
- return 0;
- type = "Input";
- }
- snd_hda_add_imux_item(&spec->input_mux, type, idx, NULL);
-
- /* unmute the PIN external input */
- unmute_input(codec, node, 0); /* index = 0? */
- /* set PIN-In enable */
- snd_hda_codec_write_cache(codec, node->nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
-
- return 1; /* found */
-}
-
-/*
- * parse input
- */
-static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node)
-{
- struct hda_gspec *spec = codec->spec;
- struct hda_gnode *node;
- int i, err;
-
- snd_printdd("AUD_IN = %x\n", adc_node->nid);
- clear_check_flags(spec);
-
- // awk added - fixed no recording due to muted widget
- unmute_input(codec, adc_node, 0);
-
- /*
- * check each connection of the ADC
- * if it reaches to a proper input PIN, add the path as the
- * input path.
- */
- /* first, check the direct connections to PIN widgets */
- for (i = 0; i < adc_node->nconns; i++) {
- node = hda_get_node(spec, adc_node->conn_list[i]);
- if (node && node->type == AC_WID_PIN) {
- err = parse_adc_sub_nodes(codec, spec, node, i);
- if (err < 0)
- return err;
- }
- }
- /* ... then check the rests, more complicated connections */
- for (i = 0; i < adc_node->nconns; i++) {
- node = hda_get_node(spec, adc_node->conn_list[i]);
- if (node && node->type != AC_WID_PIN) {
- err = parse_adc_sub_nodes(codec, spec, node, i);
- if (err < 0)
- return err;
- }
- }
-
- if (! spec->input_mux.num_items)
- return 0; /* no input path found... */
-
- snd_printdd("[Capture Source] NID=0x%x, #SRC=%d\n", adc_node->nid, spec->input_mux.num_items);
- for (i = 0; i < spec->input_mux.num_items; i++)
- snd_printdd(" [%s] IDX=0x%x\n", spec->input_mux.items[i].label,
- spec->input_mux.items[i].index);
-
- spec->adc_node = adc_node;
- return 1;
-}
-
-/*
- * parse input
- */
-static int parse_input(struct hda_codec *codec)
-{
- struct hda_gspec *spec = codec->spec;
- struct hda_gnode *node;
- int err;
-
- /*
- * At first we look for an audio input widget.
- * If it reaches to certain input PINs, we take it as the
- * input path.
- */
- list_for_each_entry(node, &spec->nid_list, list) {
- if (node->wid_caps & AC_WCAP_DIGITAL)
- continue; /* skip SPDIF */
- if (node->type == AC_WID_AUD_IN) {
- err = parse_input_path(codec, node);
- if (err < 0)
- return err;
- else if (err > 0)
- return 0;
- }
- }
- snd_printd("hda_generic: no proper input path found\n");
- return 0;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static void add_input_loopback(struct hda_codec *codec, hda_nid_t nid,
- int dir, int idx)
-{
- struct hda_gspec *spec = codec->spec;
- struct hda_amp_list *p;
-
- if (spec->num_loopbacks >= MAX_LOOPBACK_AMPS) {
- snd_printk(KERN_ERR "hda_generic: Too many loopback ctls\n");
- return;
- }
- p = &spec->loopback_list[spec->num_loopbacks++];
- p->nid = nid;
- p->dir = dir;
- p->idx = idx;
- spec->loopback.amplist = spec->loopback_list;
-}
-#else
-#define add_input_loopback(codec,nid,dir,idx)
-#endif
-
-/*
- * create mixer controls if possible
- */
-static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
- unsigned int index, const char *type,
- const char *dir_sfx, int is_loopback)
-{
- char name[32];
- int err;
- int created = 0;
- struct snd_kcontrol_new knew;
-
- if (type)
- sprintf(name, "%s %s Switch", type, dir_sfx);
- else
- sprintf(name, "%s Switch", dir_sfx);
- if ((node->wid_caps & AC_WCAP_IN_AMP) &&
- (node->amp_in_caps & AC_AMPCAP_MUTE)) {
- knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT);
- if (is_loopback)
- add_input_loopback(codec, node->nid, HDA_INPUT, index);
- snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
- err = snd_hda_ctl_add(codec, node->nid,
- snd_ctl_new1(&knew, codec));
- if (err < 0)
- return err;
- created = 1;
- } else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
- (node->amp_out_caps & AC_AMPCAP_MUTE)) {
- knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT);
- if (is_loopback)
- add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);
- snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
- err = snd_hda_ctl_add(codec, node->nid,
- snd_ctl_new1(&knew, codec));
- if (err < 0)
- return err;
- created = 1;
- }
-
- if (type)
- sprintf(name, "%s %s Volume", type, dir_sfx);
- else
- sprintf(name, "%s Volume", dir_sfx);
- if ((node->wid_caps & AC_WCAP_IN_AMP) &&
- (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
- knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
- snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
- err = snd_hda_ctl_add(codec, node->nid,
- snd_ctl_new1(&knew, codec));
- if (err < 0)
- return err;
- created = 1;
- } else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
- (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
- knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
- snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
- err = snd_hda_ctl_add(codec, node->nid,
- snd_ctl_new1(&knew, codec));
- if (err < 0)
- return err;
- created = 1;
- }
-
- return created;
-}
-
-/*
- * check whether the controls with the given name and direction suffix already exist
- */
-static int check_existing_control(struct hda_codec *codec, const char *type, const char *dir)
-{
- struct snd_ctl_elem_id id;
- memset(&id, 0, sizeof(id));
- sprintf(id.name, "%s %s Volume", type, dir);
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- if (snd_ctl_find_id(codec->bus->card, &id))
- return 1;
- sprintf(id.name, "%s %s Switch", type, dir);
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- if (snd_ctl_find_id(codec->bus->card, &id))
- return 1;
- return 0;
-}
-
-/*
- * build output mixer controls
- */
-static int create_output_mixers(struct hda_codec *codec, const char **names)
-{
- struct hda_gspec *spec = codec->spec;
- int i, err;
-
- for (i = 0; i < spec->pcm_vol_nodes; i++) {
- err = create_mixer(codec, spec->pcm_vol[i].node,
- spec->pcm_vol[i].index,
- names[i], "Playback", 0);
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-static int build_output_controls(struct hda_codec *codec)
-{
- struct hda_gspec *spec = codec->spec;
- static const char *types_speaker[] = { "Speaker", "Headphone" };
- static const char *types_line[] = { "Front", "Headphone" };
-
- switch (spec->pcm_vol_nodes) {
- case 1:
- return create_mixer(codec, spec->pcm_vol[0].node,
- spec->pcm_vol[0].index,
- "Master", "Playback", 0);
- case 2:
- if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER)
- return create_output_mixers(codec, types_speaker);
- else
- return create_output_mixers(codec, types_line);
- }
- return 0;
-}
-
-/* create capture volume/switch */
-static int build_input_controls(struct hda_codec *codec)
-{
- struct hda_gspec *spec = codec->spec;
- struct hda_gnode *adc_node = spec->adc_node;
- int i, err;
- static struct snd_kcontrol_new cap_sel = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = capture_source_info,
- .get = capture_source_get,
- .put = capture_source_put,
- };
-
- if (! adc_node || ! spec->input_mux.num_items)
- return 0; /* not found */
-
- spec->cur_cap_src = 0;
- select_input_connection(codec, adc_node,
- spec->input_mux.items[0].index);
-
- /* create capture volume and switch controls if the ADC has an amp */
- /* do we have only a single item? */
- if (spec->input_mux.num_items == 1) {
- err = create_mixer(codec, adc_node,
- spec->input_mux.items[0].index,
- NULL, "Capture", 0);
- if (err < 0)
- return err;
- return 0;
- }
-
- /* create input MUX if multiple sources are available */
- err = snd_hda_ctl_add(codec, spec->adc_node->nid,
- snd_ctl_new1(&cap_sel, codec));
- if (err < 0)
- return err;
-
- /* no volume control? */
- if (! (adc_node->wid_caps & AC_WCAP_IN_AMP) ||
- ! (adc_node->amp_in_caps & AC_AMPCAP_NUM_STEPS))
- return 0;
-
- for (i = 0; i < spec->input_mux.num_items; i++) {
- struct snd_kcontrol_new knew;
- char name[32];
- sprintf(name, "%s Capture Volume",
- spec->input_mux.items[i].label);
- knew = (struct snd_kcontrol_new)
- HDA_CODEC_VOLUME(name, adc_node->nid,
- spec->input_mux.items[i].index,
- HDA_INPUT);
- err = snd_hda_ctl_add(codec, adc_node->nid,
- snd_ctl_new1(&knew, codec));
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-
-/*
- * parse the nodes recursively until reach to the output PIN.
- *
- * returns 0 - if not found,
- * 1 - if found, but no mixer is created
- * 2 - if found and mixer was already created, (just skip)
- * a negative error code
- */
-static int parse_loopback_path(struct hda_codec *codec, struct hda_gspec *spec,
- struct hda_gnode *node, struct hda_gnode *dest_node,
- const char *type)
-{
- int i, err;
-
- if (node->checked)
- return 0;
-
- node->checked = 1;
- if (node == dest_node) {
- /* loopback connection found */
- return 1;
- }
-
- for (i = 0; i < node->nconns; i++) {
- struct hda_gnode *child = hda_get_node(spec, node->conn_list[i]);
- if (! child)
- continue;
- err = parse_loopback_path(codec, spec, child, dest_node, type);
- if (err < 0)
- return err;
- else if (err >= 1) {
- if (err == 1) {
- err = create_mixer(codec, node, i, type,
- "Playback", 1);
- if (err < 0)
- return err;
- if (err > 0)
- return 2; /* ok, created */
- /* not created, maybe in the lower path */
- err = 1;
- }
- /* connect and unmute */
- if (node->nconns > 1)
- select_input_connection(codec, node, i);
- unmute_input(codec, node, i);
- unmute_output(codec, node);
- return err;
- }
- }
- return 0;
-}
-
-/*
- * parse the tree and build the loopback controls
- */
-static int build_loopback_controls(struct hda_codec *codec)
-{
- struct hda_gspec *spec = codec->spec;
- struct hda_gnode *node;
- int err;
- const char *type;
-
- if (! spec->out_pin_node[0])
- return 0;
-
- list_for_each_entry(node, &spec->nid_list, list) {
- if (node->type != AC_WID_PIN)
- continue;
- /* input capable? */
- if (! (node->pin_caps & AC_PINCAP_IN))
- return 0;
- type = get_input_type(node, NULL);
- if (type) {
- if (check_existing_control(codec, type, "Playback"))
- continue;
- clear_check_flags(spec);
- err = parse_loopback_path(codec, spec,
- spec->out_pin_node[0],
- node, type);
- if (err < 0)
- return err;
- if (! err)
- continue;
- }
- }
- return 0;
-}
-
-/*
- * build mixer controls
- */
-static int build_generic_controls(struct hda_codec *codec)
-{
- int err;
-
- if ((err = build_input_controls(codec)) < 0 ||
- (err = build_output_controls(codec)) < 0 ||
- (err = build_loopback_controls(codec)) < 0)
- return err;
-
- return 0;
-}
-
-/*
- * PCM
- */
-static struct hda_pcm_stream generic_pcm_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-static int generic_pcm2_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct hda_gspec *spec = codec->spec;
-
- snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
- snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid,
- stream_tag, 0, format);
- return 0;
-}
-
-static int generic_pcm2_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hda_gspec *spec = codec->spec;
-
- snd_hda_codec_cleanup_stream(codec, hinfo->nid);
- snd_hda_codec_cleanup_stream(codec, spec->dac_node[1]->nid);
- return 0;
-}
-
-static int build_generic_pcms(struct hda_codec *codec)
-{
- struct hda_gspec *spec = codec->spec;
- struct hda_pcm *info = &spec->pcm_rec;
-
- if (! spec->dac_node[0] && ! spec->adc_node) {
- snd_printd("hda_generic: no PCM found\n");
- return 0;
- }
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = "HDA Generic";
- if (spec->dac_node[0]) {
- info->stream[0] = generic_pcm_playback;
- info->stream[0].nid = spec->dac_node[0]->nid;
- if (spec->dac_node[1]) {
- info->stream[0].ops.prepare = generic_pcm2_prepare;
- info->stream[0].ops.cleanup = generic_pcm2_cleanup;
- }
- }
- if (spec->adc_node) {
- info->stream[1] = generic_pcm_playback;
- info->stream[1].nid = spec->adc_node->nid;
- }
-
- return 0;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid)
-{
- struct hda_gspec *spec = codec->spec;
- return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
-}
-#endif
-
-
-/*
- */
-static struct hda_codec_ops generic_patch_ops = {
- .build_controls = build_generic_controls,
- .build_pcms = build_generic_pcms,
- .free = snd_hda_generic_free,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .check_power_status = generic_check_power_status,
-#endif
-};
-
-/*
- * the generic parser
- */
-int snd_hda_parse_generic_codec(struct hda_codec *codec)
-{
- struct hda_gspec *spec;
- int err;
-
- if(!codec->afg)
- return 0;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL) {
- printk(KERN_ERR "hda_generic: can't allocate spec\n");
- return -ENOMEM;
- }
- codec->spec = spec;
- INIT_LIST_HEAD(&spec->nid_list);
-
- if ((err = build_afg_tree(codec)) < 0)
- goto error;
-
- if ((err = parse_input(codec)) < 0 ||
- (err = parse_output(codec)) < 0)
- goto error;
-
- codec->patch_ops = generic_patch_ops;
-
- return 0;
-
- error:
- snd_hda_generic_free(codec);
- return err;
-}
-EXPORT_SYMBOL(snd_hda_parse_generic_codec);
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
deleted file mode 100644
index bf3ced51e0f8..000000000000
--- a/sound/pci/hda/hda_hwdep.c
+++ /dev/null
@@ -1,818 +0,0 @@
-/*
- * HWDEP Interface for HD-audio codec
- *
- * Copyright (c) 2007 Takashi Iwai <tiwai@suse.de>
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
-#include <linux/compat.h>
-#include <linux/mutex.h>
-#include <linux/ctype.h>
-#include <linux/string.h>
-#include <linux/firmware.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-#include <sound/hda_hwdep.h>
-#include <sound/minors.h>
-
-/* hint string pair */
-struct hda_hint {
- const char *key;
- const char *val; /* contained in the same alloc as key */
-};
-
-/*
- * write/read an out-of-bound verb
- */
-static int verb_write_ioctl(struct hda_codec *codec,
- struct hda_verb_ioctl __user *arg)
-{
- u32 verb, res;
-
- if (get_user(verb, &arg->verb))
- return -EFAULT;
- res = snd_hda_codec_read(codec, verb >> 24, 0,
- (verb >> 8) & 0xffff, verb & 0xff);
- if (put_user(res, &arg->res))
- return -EFAULT;
- return 0;
-}
-
-static int get_wcap_ioctl(struct hda_codec *codec,
- struct hda_verb_ioctl __user *arg)
-{
- u32 verb, res;
-
- if (get_user(verb, &arg->verb))
- return -EFAULT;
- res = get_wcaps(codec, verb >> 24);
- if (put_user(res, &arg->res))
- return -EFAULT;
- return 0;
-}
-
-
-/*
- */
-static int hda_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct hda_codec *codec = hw->private_data;
- void __user *argp = (void __user *)arg;
-
- switch (cmd) {
- case HDA_IOCTL_PVERSION:
- return put_user(HDA_HWDEP_VERSION, (int __user *)argp);
- case HDA_IOCTL_VERB_WRITE:
- return verb_write_ioctl(codec, argp);
- case HDA_IOCTL_GET_WCAP:
- return get_wcap_ioctl(codec, argp);
- }
- return -ENOIOCTLCMD;
-}
-
-#ifdef CONFIG_COMPAT
-static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- return hda_hwdep_ioctl(hw, file, cmd, (unsigned long)compat_ptr(arg));
-}
-#endif
-
-static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
-{
-#ifndef CONFIG_SND_DEBUG_VERBOSE
- if (!capable(CAP_SYS_RAWIO))
- return -EACCES;
-#endif
- return 0;
-}
-
-static void clear_hwdep_elements(struct hda_codec *codec)
-{
- int i;
-
- /* clear init verbs */
- snd_array_free(&codec->init_verbs);
- /* clear hints */
- for (i = 0; i < codec->hints.used; i++) {
- struct hda_hint *hint = snd_array_elem(&codec->hints, i);
- kfree(hint->key); /* we don't need to free hint->val */
- }
- snd_array_free(&codec->hints);
- snd_array_free(&codec->user_pins);
-}
-
-static void hwdep_free(struct snd_hwdep *hwdep)
-{
- clear_hwdep_elements(hwdep->private_data);
-}
-
-int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
-{
- char hwname[16];
- struct snd_hwdep *hwdep;
- int err;
-
- sprintf(hwname, "HDA Codec %d", codec->addr);
- err = snd_hwdep_new(codec->bus->card, hwname, codec->addr, &hwdep);
- if (err < 0)
- return err;
- codec->hwdep = hwdep;
- sprintf(hwdep->name, "HDA Codec %d", codec->addr);
- hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
- hwdep->private_data = codec;
- hwdep->private_free = hwdep_free;
- hwdep->exclusive = 1;
-
- hwdep->ops.open = hda_hwdep_open;
- hwdep->ops.ioctl = hda_hwdep_ioctl;
-#ifdef CONFIG_COMPAT
- hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
-#endif
-
- snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
- snd_array_init(&codec->hints, sizeof(struct hda_hint), 32);
- snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16);
-
- return 0;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static ssize_t power_on_acct_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- snd_hda_update_power_acct(codec);
- return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct));
-}
-
-static ssize_t power_off_acct_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- snd_hda_update_power_acct(codec);
- return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct));
-}
-
-static struct device_attribute power_attrs[] = {
- __ATTR_RO(power_on_acct),
- __ATTR_RO(power_off_acct),
-};
-
-int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec)
-{
- struct snd_hwdep *hwdep = codec->hwdep;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(power_attrs); i++)
- snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
- hwdep->device, &power_attrs[i]);
- return 0;
-}
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
-
-#ifdef CONFIG_SND_HDA_RECONFIG
-
-/*
- * sysfs interface
- */
-
-static int clear_codec(struct hda_codec *codec)
-{
- int err;
-
- err = snd_hda_codec_reset(codec);
- if (err < 0) {
- snd_printk(KERN_ERR "The codec is being used, can't free.\n");
- return err;
- }
- clear_hwdep_elements(codec);
- return 0;
-}
-
-static int reconfig_codec(struct hda_codec *codec)
-{
- int err;
-
- snd_hda_power_up(codec);
- snd_printk(KERN_INFO "hda-codec: reconfiguring\n");
- err = snd_hda_codec_reset(codec);
- if (err < 0) {
- snd_printk(KERN_ERR
- "The codec is being used, can't reconfigure.\n");
- goto error;
- }
- err = snd_hda_codec_configure(codec);
- if (err < 0)
- goto error;
- /* rebuild PCMs */
- err = snd_hda_codec_build_pcms(codec);
- if (err < 0)
- goto error;
- /* rebuild mixers */
- err = snd_hda_codec_build_controls(codec);
- if (err < 0)
- goto error;
- err = snd_card_register(codec->bus->card);
- error:
- snd_hda_power_down(codec);
- return err;
-}
-
-/*
- * allocate a string at most len chars, and remove the trailing EOL
- */
-static char *kstrndup_noeol(const char *src, size_t len)
-{
- char *s = kstrndup(src, len, GFP_KERNEL);
- char *p;
- if (!s)
- return NULL;
- p = strchr(s, '\n');
- if (p)
- *p = 0;
- return s;
-}
-
-#define CODEC_INFO_SHOW(type) \
-static ssize_t type##_show(struct device *dev, \
- struct device_attribute *attr, \
- char *buf) \
-{ \
- struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
- struct hda_codec *codec = hwdep->private_data; \
- return sprintf(buf, "0x%x\n", codec->type); \
-}
-
-#define CODEC_INFO_STR_SHOW(type) \
-static ssize_t type##_show(struct device *dev, \
- struct device_attribute *attr, \
- char *buf) \
-{ \
- struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
- struct hda_codec *codec = hwdep->private_data; \
- return sprintf(buf, "%s\n", \
- codec->type ? codec->type : ""); \
-}
-
-CODEC_INFO_SHOW(vendor_id);
-CODEC_INFO_SHOW(subsystem_id);
-CODEC_INFO_SHOW(revision_id);
-CODEC_INFO_SHOW(afg);
-CODEC_INFO_SHOW(mfg);
-CODEC_INFO_STR_SHOW(vendor_name);
-CODEC_INFO_STR_SHOW(chip_name);
-CODEC_INFO_STR_SHOW(modelname);
-
-#define CODEC_INFO_STORE(type) \
-static ssize_t type##_store(struct device *dev, \
- struct device_attribute *attr, \
- const char *buf, size_t count) \
-{ \
- struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
- struct hda_codec *codec = hwdep->private_data; \
- unsigned long val; \
- int err = strict_strtoul(buf, 0, &val); \
- if (err < 0) \
- return err; \
- codec->type = val; \
- return count; \
-}
-
-#define CODEC_INFO_STR_STORE(type) \
-static ssize_t type##_store(struct device *dev, \
- struct device_attribute *attr, \
- const char *buf, size_t count) \
-{ \
- struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
- struct hda_codec *codec = hwdep->private_data; \
- char *s = kstrndup_noeol(buf, 64); \
- if (!s) \
- return -ENOMEM; \
- kfree(codec->type); \
- codec->type = s; \
- return count; \
-}
-
-CODEC_INFO_STORE(vendor_id);
-CODEC_INFO_STORE(subsystem_id);
-CODEC_INFO_STORE(revision_id);
-CODEC_INFO_STR_STORE(vendor_name);
-CODEC_INFO_STR_STORE(chip_name);
-CODEC_INFO_STR_STORE(modelname);
-
-#define CODEC_ACTION_STORE(type) \
-static ssize_t type##_store(struct device *dev, \
- struct device_attribute *attr, \
- const char *buf, size_t count) \
-{ \
- struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
- struct hda_codec *codec = hwdep->private_data; \
- int err = 0; \
- if (*buf) \
- err = type##_codec(codec); \
- return err < 0 ? err : count; \
-}
-
-CODEC_ACTION_STORE(reconfig);
-CODEC_ACTION_STORE(clear);
-
-static ssize_t init_verbs_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- int i, len = 0;
- for (i = 0; i < codec->init_verbs.used; i++) {
- struct hda_verb *v = snd_array_elem(&codec->init_verbs, i);
- len += snprintf(buf + len, PAGE_SIZE - len,
- "0x%02x 0x%03x 0x%04x\n",
- v->nid, v->verb, v->param);
- }
- return len;
-}
-
-static int parse_init_verbs(struct hda_codec *codec, const char *buf)
-{
- struct hda_verb *v;
- int nid, verb, param;
-
- if (sscanf(buf, "%i %i %i", &nid, &verb, &param) != 3)
- return -EINVAL;
- if (!nid || !verb)
- return -EINVAL;
- v = snd_array_new(&codec->init_verbs);
- if (!v)
- return -ENOMEM;
- v->nid = nid;
- v->verb = verb;
- v->param = param;
- return 0;
-}
-
-static ssize_t init_verbs_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- int err = parse_init_verbs(codec, buf);
- if (err < 0)
- return err;
- return count;
-}
-
-static ssize_t hints_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- int i, len = 0;
- for (i = 0; i < codec->hints.used; i++) {
- struct hda_hint *hint = snd_array_elem(&codec->hints, i);
- len += snprintf(buf + len, PAGE_SIZE - len,
- "%s = %s\n", hint->key, hint->val);
- }
- return len;
-}
-
-static struct hda_hint *get_hint(struct hda_codec *codec, const char *key)
-{
- int i;
-
- for (i = 0; i < codec->hints.used; i++) {
- struct hda_hint *hint = snd_array_elem(&codec->hints, i);
- if (!strcmp(hint->key, key))
- return hint;
- }
- return NULL;
-}
-
-static void remove_trail_spaces(char *str)
-{
- char *p;
- if (!*str)
- return;
- p = str + strlen(str) - 1;
- for (; isspace(*p); p--) {
- *p = 0;
- if (p == str)
- return;
- }
-}
-
-#define MAX_HINTS 1024
-
-static int parse_hints(struct hda_codec *codec, const char *buf)
-{
- char *key, *val;
- struct hda_hint *hint;
-
- buf = skip_spaces(buf);
- if (!*buf || *buf == '#' || *buf == '\n')
- return 0;
- if (*buf == '=')
- return -EINVAL;
- key = kstrndup_noeol(buf, 1024);
- if (!key)
- return -ENOMEM;
- /* extract key and val */
- val = strchr(key, '=');
- if (!val) {
- kfree(key);
- return -EINVAL;
- }
- *val++ = 0;
- val = skip_spaces(val);
- remove_trail_spaces(key);
- remove_trail_spaces(val);
- hint = get_hint(codec, key);
- if (hint) {
- /* replace */
- kfree(hint->key);
- hint->key = key;
- hint->val = val;
- return 0;
- }
- /* allocate a new hint entry */
- if (codec->hints.used >= MAX_HINTS)
- hint = NULL;
- else
- hint = snd_array_new(&codec->hints);
- if (!hint) {
- kfree(key);
- return -ENOMEM;
- }
- hint->key = key;
- hint->val = val;
- return 0;
-}
-
-static ssize_t hints_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- int err = parse_hints(codec, buf);
- if (err < 0)
- return err;
- return count;
-}
-
-static ssize_t pin_configs_show(struct hda_codec *codec,
- struct snd_array *list,
- char *buf)
-{
- int i, len = 0;
- for (i = 0; i < list->used; i++) {
- struct hda_pincfg *pin = snd_array_elem(list, i);
- len += sprintf(buf + len, "0x%02x 0x%08x\n",
- pin->nid, pin->cfg);
- }
- return len;
-}
-
-static ssize_t init_pin_configs_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- return pin_configs_show(codec, &codec->init_pins, buf);
-}
-
-static ssize_t user_pin_configs_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- return pin_configs_show(codec, &codec->user_pins, buf);
-}
-
-static ssize_t driver_pin_configs_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- return pin_configs_show(codec, &codec->driver_pins, buf);
-}
-
-#define MAX_PIN_CONFIGS 32
-
-static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
-{
- int nid, cfg;
-
- if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
- return -EINVAL;
- if (!nid)
- return -EINVAL;
- return snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
-}
-
-static ssize_t user_pin_configs_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct snd_hwdep *hwdep = dev_get_drvdata(dev);
- struct hda_codec *codec = hwdep->private_data;
- int err = parse_user_pin_configs(codec, buf);
- if (err < 0)
- return err;
- return count;
-}
-
-#define CODEC_ATTR_RW(type) \
- __ATTR(type, 0644, type##_show, type##_store)
-#define CODEC_ATTR_RO(type) \
- __ATTR_RO(type)
-#define CODEC_ATTR_WO(type) \
- __ATTR(type, 0200, NULL, type##_store)
-
-static struct device_attribute codec_attrs[] = {
- CODEC_ATTR_RW(vendor_id),
- CODEC_ATTR_RW(subsystem_id),
- CODEC_ATTR_RW(revision_id),
- CODEC_ATTR_RO(afg),
- CODEC_ATTR_RO(mfg),
- CODEC_ATTR_RW(vendor_name),
- CODEC_ATTR_RW(chip_name),
- CODEC_ATTR_RW(modelname),
- CODEC_ATTR_RW(init_verbs),
- CODEC_ATTR_RW(hints),
- CODEC_ATTR_RO(init_pin_configs),
- CODEC_ATTR_RW(user_pin_configs),
- CODEC_ATTR_RO(driver_pin_configs),
- CODEC_ATTR_WO(reconfig),
- CODEC_ATTR_WO(clear),
-};
-
-/*
- * create sysfs files on hwdep directory
- */
-int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
-{
- struct snd_hwdep *hwdep = codec->hwdep;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(codec_attrs); i++)
- snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
- hwdep->device, &codec_attrs[i]);
- return 0;
-}
-
-/*
- * Look for hint string
- */
-const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
-{
- struct hda_hint *hint = get_hint(codec, key);
- return hint ? hint->val : NULL;
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_hint);
-
-int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
-{
- const char *p = snd_hda_get_hint(codec, key);
- if (!p || !*p)
- return -ENOENT;
- switch (toupper(*p)) {
- case 'T': /* true */
- case 'Y': /* yes */
- case '1':
- return 1;
- }
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint);
-
-#endif /* CONFIG_SND_HDA_RECONFIG */
-
-#ifdef CONFIG_SND_HDA_PATCH_LOADER
-
-/* parser mode */
-enum {
- LINE_MODE_NONE,
- LINE_MODE_CODEC,
- LINE_MODE_MODEL,
- LINE_MODE_PINCFG,
- LINE_MODE_VERB,
- LINE_MODE_HINT,
- LINE_MODE_VENDOR_ID,
- LINE_MODE_SUBSYSTEM_ID,
- LINE_MODE_REVISION_ID,
- LINE_MODE_CHIP_NAME,
- NUM_LINE_MODES,
-};
-
-static inline int strmatch(const char *a, const char *b)
-{
- return strnicmp(a, b, strlen(b)) == 0;
-}
-
-/* parse the contents after the line "[codec]"
- * accept only the line with three numbers, and assign the current codec
- */
-static void parse_codec_mode(char *buf, struct hda_bus *bus,
- struct hda_codec **codecp)
-{
- unsigned int vendorid, subid, caddr;
- struct hda_codec *codec;
-
- *codecp = NULL;
- if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) {
- list_for_each_entry(codec, &bus->codec_list, list) {
- if (codec->vendor_id == vendorid &&
- codec->subsystem_id == subid &&
- codec->addr == caddr) {
- *codecp = codec;
- break;
- }
- }
- }
-}
-
-/* parse the contents after the other command tags, [pincfg], [verb],
- * [vendor_id], [subsystem_id], [revision_id], [chip_name], [hint] and [model]
- * just pass to the sysfs helper (only when any codec was specified)
- */
-static void parse_pincfg_mode(char *buf, struct hda_bus *bus,
- struct hda_codec **codecp)
-{
- parse_user_pin_configs(*codecp, buf);
-}
-
-static void parse_verb_mode(char *buf, struct hda_bus *bus,
- struct hda_codec **codecp)
-{
- parse_init_verbs(*codecp, buf);
-}
-
-static void parse_hint_mode(char *buf, struct hda_bus *bus,
- struct hda_codec **codecp)
-{
- parse_hints(*codecp, buf);
-}
-
-static void parse_model_mode(char *buf, struct hda_bus *bus,
- struct hda_codec **codecp)
-{
- kfree((*codecp)->modelname);
- (*codecp)->modelname = kstrdup(buf, GFP_KERNEL);
-}
-
-static void parse_chip_name_mode(char *buf, struct hda_bus *bus,
- struct hda_codec **codecp)
-{
- kfree((*codecp)->chip_name);
- (*codecp)->chip_name = kstrdup(buf, GFP_KERNEL);
-}
-
-#define DEFINE_PARSE_ID_MODE(name) \
-static void parse_##name##_mode(char *buf, struct hda_bus *bus, \
- struct hda_codec **codecp) \
-{ \
- unsigned long val; \
- if (!strict_strtoul(buf, 0, &val)) \
- (*codecp)->name = val; \
-}
-
-DEFINE_PARSE_ID_MODE(vendor_id);
-DEFINE_PARSE_ID_MODE(subsystem_id);
-DEFINE_PARSE_ID_MODE(revision_id);
-
-
-struct hda_patch_item {
- const char *tag;
- void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc);
- int need_codec;
-};
-
-static struct hda_patch_item patch_items[NUM_LINE_MODES] = {
- [LINE_MODE_CODEC] = { "[codec]", parse_codec_mode, 0 },
- [LINE_MODE_MODEL] = { "[model]", parse_model_mode, 1 },
- [LINE_MODE_VERB] = { "[verb]", parse_verb_mode, 1 },
- [LINE_MODE_PINCFG] = { "[pincfg]", parse_pincfg_mode, 1 },
- [LINE_MODE_HINT] = { "[hint]", parse_hint_mode, 1 },
- [LINE_MODE_VENDOR_ID] = { "[vendor_id]", parse_vendor_id_mode, 1 },
- [LINE_MODE_SUBSYSTEM_ID] = { "[subsystem_id]", parse_subsystem_id_mode, 1 },
- [LINE_MODE_REVISION_ID] = { "[revision_id]", parse_revision_id_mode, 1 },
- [LINE_MODE_CHIP_NAME] = { "[chip_name]", parse_chip_name_mode, 1 },
-};
-
-/* check the line starting with '[' -- change the parser mode accodingly */
-static int parse_line_mode(char *buf, struct hda_bus *bus)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE(patch_items); i++) {
- if (!patch_items[i].tag)
- continue;
- if (strmatch(buf, patch_items[i].tag))
- return i;
- }
- return LINE_MODE_NONE;
-}
-
-/* copy one line from the buffer in fw, and update the fields in fw
- * return zero if it reaches to the end of the buffer, or non-zero
- * if successfully copied a line
- *
- * the spaces at the beginning and the end of the line are stripped
- */
-static int get_line_from_fw(char *buf, int size, struct firmware *fw)
-{
- int len;
- const char *p = fw->data;
- while (isspace(*p) && fw->size) {
- p++;
- fw->size--;
- }
- if (!fw->size)
- return 0;
- if (size < fw->size)
- size = fw->size;
-
- for (len = 0; len < fw->size; len++) {
- if (!*p)
- break;
- if (*p == '\n') {
- p++;
- len++;
- break;
- }
- if (len < size)
- *buf++ = *p++;
- }
- *buf = 0;
- fw->size -= len;
- fw->data = p;
- remove_trail_spaces(buf);
- return 1;
-}
-
-/*
- * load a "patch" firmware file and parse it
- */
-int snd_hda_load_patch(struct hda_bus *bus, const char *patch)
-{
- int err;
- const struct firmware *fw;
- struct firmware tmp;
- char buf[128];
- struct hda_codec *codec;
- int line_mode;
- struct device *dev = bus->card->dev;
-
- if (snd_BUG_ON(!dev))
- return -ENODEV;
- err = request_firmware(&fw, patch, dev);
- if (err < 0) {
- printk(KERN_ERR "hda-codec: Cannot load the patch '%s'\n",
- patch);
- return err;
- }
-
- tmp = *fw;
- line_mode = LINE_MODE_NONE;
- codec = NULL;
- while (get_line_from_fw(buf, sizeof(buf) - 1, &tmp)) {
- if (!*buf || *buf == '#' || *buf == '\n')
- continue;
- if (*buf == '[')
- line_mode = parse_line_mode(buf, bus);
- else if (patch_items[line_mode].parser &&
- (codec || !patch_items[line_mode].need_codec))
- patch_items[line_mode].parser(buf, bus, &codec);
- }
- release_firmware(fw);
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_load_patch);
-#endif /* CONFIG_SND_HDA_PATCH_LOADER */
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
deleted file mode 100644
index 21aa9b0e28f6..000000000000
--- a/sound/pci/hda/hda_intel.c
+++ /dev/null
@@ -1,2843 +0,0 @@
-/*
- *
- * hda_intel.c - Implementation of primary alsa driver code base
- * for Intel HD Audio.
- *
- * Copyright(c) 2004 Intel Corporation. All rights reserved.
- *
- * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- * PeiSen Hou <pshou@realtek.com.tw>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 59
- * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * CONTACTS:
- *
- * Matt Jared matt.jared@intel.com
- * Andy Kopp andy.kopp@intel.com
- * Dan Kogan dan.d.kogan@intel.com
- *
- * CHANGES:
- *
- * 2004.12.01 Major rewrite by tiwai, merged the work of pshou
- *
- */
-
-#include <asm/io.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/dma-mapping.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
-#include <linux/mutex.h>
-#include <linux/reboot.h>
-#include <sound/core.h>
-#include <sound/initval.h>
-#include "hda_codec.h"
-
-
-static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
-static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
-static char *model[SNDRV_CARDS];
-static int position_fix[SNDRV_CARDS];
-static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
-static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
-static int probe_only[SNDRV_CARDS];
-static int single_cmd;
-static int enable_msi = -1;
-#ifdef CONFIG_SND_HDA_PATCH_LOADER
-static char *patch[SNDRV_CARDS];
-#endif
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-static int beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
- CONFIG_SND_HDA_INPUT_BEEP_MODE};
-#endif
-
-module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
-module_param_array(id, charp, NULL, 0444);
-MODULE_PARM_DESC(id, "ID string for Intel HD audio interface.");
-module_param_array(enable, bool, NULL, 0444);
-MODULE_PARM_DESC(enable, "Enable Intel HD audio interface.");
-module_param_array(model, charp, NULL, 0444);
-MODULE_PARM_DESC(model, "Use the given board model.");
-module_param_array(position_fix, int, NULL, 0444);
-MODULE_PARM_DESC(position_fix, "DMA pointer read method."
- "(0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO).");
-module_param_array(bdl_pos_adj, int, NULL, 0644);
-MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
-module_param_array(probe_mask, int, NULL, 0444);
-MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
-module_param_array(probe_only, int, NULL, 0444);
-MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization.");
-module_param(single_cmd, bool, 0444);
-MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
- "(for debugging only).");
-module_param(enable_msi, int, 0444);
-MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
-#ifdef CONFIG_SND_HDA_PATCH_LOADER
-module_param_array(patch, charp, NULL, 0444);
-MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface.");
-#endif
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-module_param_array(beep_mode, int, NULL, 0444);
-MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode "
- "(0=off, 1=on, 2=mute switch on/off) (default=1).");
-#endif
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
-module_param(power_save, int, 0644);
-MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
- "(in second, 0 = disable).");
-
-/* reset the HD-audio controller in power save mode.
- * this may give more power-saving, but will take longer time to
- * wake up.
- */
-static int power_save_controller = 1;
-module_param(power_save_controller, bool, 0644);
-MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode.");
-#endif
-
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
- "{Intel, ICH6M},"
- "{Intel, ICH7},"
- "{Intel, ESB2},"
- "{Intel, ICH8},"
- "{Intel, ICH9},"
- "{Intel, ICH10},"
- "{Intel, PCH},"
- "{Intel, CPT},"
- "{Intel, PBG},"
- "{Intel, SCH},"
- "{ATI, SB450},"
- "{ATI, SB600},"
- "{ATI, RS600},"
- "{ATI, RS690},"
- "{ATI, RS780},"
- "{ATI, R600},"
- "{ATI, RV630},"
- "{ATI, RV610},"
- "{ATI, RV670},"
- "{ATI, RV635},"
- "{ATI, RV620},"
- "{ATI, RV770},"
- "{VIA, VT8251},"
- "{VIA, VT8237A},"
- "{SiS, SIS966},"
- "{ULI, M5461}}");
-MODULE_DESCRIPTION("Intel HDA driver");
-
-#ifdef CONFIG_SND_VERBOSE_PRINTK
-#define SFX /* nop */
-#else
-#define SFX "hda-intel: "
-#endif
-
-/*
- * registers
- */
-#define ICH6_REG_GCAP 0x00
-#define ICH6_GCAP_64OK (1 << 0) /* 64bit address support */
-#define ICH6_GCAP_NSDO (3 << 1) /* # of serial data out signals */
-#define ICH6_GCAP_BSS (31 << 3) /* # of bidirectional streams */
-#define ICH6_GCAP_ISS (15 << 8) /* # of input streams */
-#define ICH6_GCAP_OSS (15 << 12) /* # of output streams */
-#define ICH6_REG_VMIN 0x02
-#define ICH6_REG_VMAJ 0x03
-#define ICH6_REG_OUTPAY 0x04
-#define ICH6_REG_INPAY 0x06
-#define ICH6_REG_GCTL 0x08
-#define ICH6_GCTL_RESET (1 << 0) /* controller reset */
-#define ICH6_GCTL_FCNTRL (1 << 1) /* flush control */
-#define ICH6_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */
-#define ICH6_REG_WAKEEN 0x0c
-#define ICH6_REG_STATESTS 0x0e
-#define ICH6_REG_GSTS 0x10
-#define ICH6_GSTS_FSTS (1 << 1) /* flush status */
-#define ICH6_REG_INTCTL 0x20
-#define ICH6_REG_INTSTS 0x24
-#define ICH6_REG_WALLCLK 0x30 /* 24Mhz source */
-#define ICH6_REG_SYNC 0x34
-#define ICH6_REG_CORBLBASE 0x40
-#define ICH6_REG_CORBUBASE 0x44
-#define ICH6_REG_CORBWP 0x48
-#define ICH6_REG_CORBRP 0x4a
-#define ICH6_CORBRP_RST (1 << 15) /* read pointer reset */
-#define ICH6_REG_CORBCTL 0x4c
-#define ICH6_CORBCTL_RUN (1 << 1) /* enable DMA */
-#define ICH6_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */
-#define ICH6_REG_CORBSTS 0x4d
-#define ICH6_CORBSTS_CMEI (1 << 0) /* memory error indication */
-#define ICH6_REG_CORBSIZE 0x4e
-
-#define ICH6_REG_RIRBLBASE 0x50
-#define ICH6_REG_RIRBUBASE 0x54
-#define ICH6_REG_RIRBWP 0x58
-#define ICH6_RIRBWP_RST (1 << 15) /* write pointer reset */
-#define ICH6_REG_RINTCNT 0x5a
-#define ICH6_REG_RIRBCTL 0x5c
-#define ICH6_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */
-#define ICH6_RBCTL_DMA_EN (1 << 1) /* enable DMA */
-#define ICH6_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */
-#define ICH6_REG_RIRBSTS 0x5d
-#define ICH6_RBSTS_IRQ (1 << 0) /* response irq */
-#define ICH6_RBSTS_OVERRUN (1 << 2) /* overrun irq */
-#define ICH6_REG_RIRBSIZE 0x5e
-
-#define ICH6_REG_IC 0x60
-#define ICH6_REG_IR 0x64
-#define ICH6_REG_IRS 0x68
-#define ICH6_IRS_VALID (1<<1)
-#define ICH6_IRS_BUSY (1<<0)
-
-#define ICH6_REG_DPLBASE 0x70
-#define ICH6_REG_DPUBASE 0x74
-#define ICH6_DPLBASE_ENABLE 0x1 /* Enable position buffer */
-
-/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
-enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
-
-/* stream register offsets from stream base */
-#define ICH6_REG_SD_CTL 0x00
-#define ICH6_REG_SD_STS 0x03
-#define ICH6_REG_SD_LPIB 0x04
-#define ICH6_REG_SD_CBL 0x08
-#define ICH6_REG_SD_LVI 0x0c
-#define ICH6_REG_SD_FIFOW 0x0e
-#define ICH6_REG_SD_FIFOSIZE 0x10
-#define ICH6_REG_SD_FORMAT 0x12
-#define ICH6_REG_SD_BDLPL 0x18
-#define ICH6_REG_SD_BDLPU 0x1c
-
-/* PCI space */
-#define ICH6_PCIREG_TCSEL 0x44
-
-/*
- * other constants
- */
-
-/* max number of SDs */
-/* ICH, ATI and VIA have 4 playback and 4 capture */
-#define ICH6_NUM_CAPTURE 4
-#define ICH6_NUM_PLAYBACK 4
-
-/* ULI has 6 playback and 5 capture */
-#define ULI_NUM_CAPTURE 5
-#define ULI_NUM_PLAYBACK 6
-
-/* ATI HDMI has 1 playback and 0 capture */
-#define ATIHDMI_NUM_CAPTURE 0
-#define ATIHDMI_NUM_PLAYBACK 1
-
-/* TERA has 4 playback and 3 capture */
-#define TERA_NUM_CAPTURE 3
-#define TERA_NUM_PLAYBACK 4
-
-/* this number is statically defined for simplicity */
-#define MAX_AZX_DEV 16
-
-/* max number of fragments - we may use more if allocating more pages for BDL */
-#define BDL_SIZE 4096
-#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16)
-#define AZX_MAX_FRAG 32
-/* max buffer size - no h/w limit, you can increase as you like */
-#define AZX_MAX_BUF_SIZE (1024*1024*1024)
-
-/* RIRB int mask: overrun[2], response[0] */
-#define RIRB_INT_RESPONSE 0x01
-#define RIRB_INT_OVERRUN 0x04
-#define RIRB_INT_MASK 0x05
-
-/* STATESTS int mask: S3,SD2,SD1,SD0 */
-#define AZX_MAX_CODECS 8
-#define AZX_DEFAULT_CODECS 4
-#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1)
-
-/* SD_CTL bits */
-#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */
-#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */
-#define SD_CTL_STRIPE (3 << 16) /* stripe control */
-#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */
-#define SD_CTL_DIR (1 << 19) /* bi-directional stream */
-#define SD_CTL_STREAM_TAG_MASK (0xf << 20)
-#define SD_CTL_STREAM_TAG_SHIFT 20
-
-/* SD_CTL and SD_STS */
-#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */
-#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */
-#define SD_INT_COMPLETE 0x04 /* completion interrupt */
-#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\
- SD_INT_COMPLETE)
-
-/* SD_STS */
-#define SD_STS_FIFO_READY 0x20 /* FIFO ready */
-
-/* INTCTL and INTSTS */
-#define ICH6_INT_ALL_STREAM 0xff /* all stream interrupts */
-#define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */
-#define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */
-
-/* below are so far hardcoded - should read registers in future */
-#define ICH6_MAX_CORB_ENTRIES 256
-#define ICH6_MAX_RIRB_ENTRIES 256
-
-/* position fix mode */
-enum {
- POS_FIX_AUTO,
- POS_FIX_LPIB,
- POS_FIX_POSBUF,
- POS_FIX_VIACOMBO,
-};
-
-/* Defines for ATI HD Audio support in SB450 south bridge */
-#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR 0x42
-#define ATI_SB450_HDAUDIO_ENABLE_SNOOP 0x02
-
-/* Defines for Nvidia HDA support */
-#define NVIDIA_HDA_TRANSREG_ADDR 0x4e
-#define NVIDIA_HDA_ENABLE_COHBITS 0x0f
-#define NVIDIA_HDA_ISTRM_COH 0x4d
-#define NVIDIA_HDA_OSTRM_COH 0x4c
-#define NVIDIA_HDA_ENABLE_COHBIT 0x01
-
-/* Defines for Intel SCH HDA snoop control */
-#define INTEL_SCH_HDA_DEVC 0x78
-#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11)
-
-/* Define IN stream 0 FIFO size offset in VIA controller */
-#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET 0x90
-/* Define VIA HD Audio Device ID*/
-#define VIA_HDAC_DEVICE_ID 0x3288
-
-/* HD Audio class code */
-#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
-
-/*
- */
-
-struct azx_dev {
- struct snd_dma_buffer bdl; /* BDL buffer */
- u32 *posbuf; /* position buffer pointer */
-
- unsigned int bufsize; /* size of the play buffer in bytes */
- unsigned int period_bytes; /* size of the period in bytes */
- unsigned int frags; /* number for period in the play buffer */
- unsigned int fifo_size; /* FIFO size */
- unsigned long start_wallclk; /* start + minimum wallclk */
- unsigned long period_wallclk; /* wallclk for period */
-
- void __iomem *sd_addr; /* stream descriptor pointer */
-
- u32 sd_int_sta_mask; /* stream int status mask */
-
- /* pcm support */
- struct snd_pcm_substream *substream; /* assigned substream,
- * set in PCM open
- */
- unsigned int format_val; /* format value to be set in the
- * controller and the codec
- */
- unsigned char stream_tag; /* assigned stream */
- unsigned char index; /* stream index */
- int device; /* last device number assigned to */
-
- unsigned int opened :1;
- unsigned int running :1;
- unsigned int irq_pending :1;
- /*
- * For VIA:
- * A flag to ensure DMA position is 0
- * when link position is not greater than FIFO size
- */
- unsigned int insufficient :1;
-};
-
-/* CORB/RIRB */
-struct azx_rb {
- u32 *buf; /* CORB/RIRB buffer
- * Each CORB entry is 4byte, RIRB is 8byte
- */
- dma_addr_t addr; /* physical address of CORB/RIRB buffer */
- /* for RIRB */
- unsigned short rp, wp; /* read/write pointers */
- int cmds[AZX_MAX_CODECS]; /* number of pending requests */
- u32 res[AZX_MAX_CODECS]; /* last read value */
-};
-
-struct azx {
- struct snd_card *card;
- struct pci_dev *pci;
- int dev_index;
-
- /* chip type specific */
- int driver_type;
- int playback_streams;
- int playback_index_offset;
- int capture_streams;
- int capture_index_offset;
- int num_streams;
-
- /* pci resources */
- unsigned long addr;
- void __iomem *remap_addr;
- int irq;
-
- /* locks */
- spinlock_t reg_lock;
- struct mutex open_mutex;
-
- /* streams (x num_streams) */
- struct azx_dev *azx_dev;
-
- /* PCM */
- struct snd_pcm *pcm[HDA_MAX_PCMS];
-
- /* HD codec */
- unsigned short codec_mask;
- int codec_probe_mask; /* copied from probe_mask option */
- struct hda_bus *bus;
- unsigned int beep_mode;
-
- /* CORB/RIRB */
- struct azx_rb corb;
- struct azx_rb rirb;
-
- /* CORB/RIRB and position buffers */
- struct snd_dma_buffer rb;
- struct snd_dma_buffer posbuf;
-
- /* flags */
- int position_fix[2]; /* for both playback/capture streams */
- int poll_count;
- unsigned int running :1;
- unsigned int initialized :1;
- unsigned int single_cmd :1;
- unsigned int polling_mode :1;
- unsigned int msi :1;
- unsigned int irq_pending_warned :1;
- unsigned int probing :1; /* codec probing phase */
-
- /* for debugging */
- unsigned int last_cmd[AZX_MAX_CODECS];
-
- /* for pending irqs */
- struct work_struct irq_pending_work;
-
- /* reboot notifier (for mysterious hangup problem at power-down) */
- struct notifier_block reboot_notifier;
-};
-
-/* driver types */
-enum {
- AZX_DRIVER_ICH,
- AZX_DRIVER_PCH,
- AZX_DRIVER_SCH,
- AZX_DRIVER_ATI,
- AZX_DRIVER_ATIHDMI,
- AZX_DRIVER_VIA,
- AZX_DRIVER_SIS,
- AZX_DRIVER_ULI,
- AZX_DRIVER_NVIDIA,
- AZX_DRIVER_TERA,
- AZX_DRIVER_CTX,
- AZX_DRIVER_GENERIC,
- AZX_NUM_DRIVERS, /* keep this as last entry */
-};
-
-static char *driver_short_names[] __devinitdata = {
- [AZX_DRIVER_ICH] = "HDA Intel",
- [AZX_DRIVER_PCH] = "HDA Intel PCH",
- [AZX_DRIVER_SCH] = "HDA Intel MID",
- [AZX_DRIVER_ATI] = "HDA ATI SB",
- [AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI",
- [AZX_DRIVER_VIA] = "HDA VIA VT82xx",
- [AZX_DRIVER_SIS] = "HDA SIS966",
- [AZX_DRIVER_ULI] = "HDA ULI M5461",
- [AZX_DRIVER_NVIDIA] = "HDA NVidia",
- [AZX_DRIVER_TERA] = "HDA Teradici",
- [AZX_DRIVER_CTX] = "HDA Creative",
- [AZX_DRIVER_GENERIC] = "HD-Audio Generic",
-};
-
-/*
- * macros for easy use
- */
-#define azx_writel(chip,reg,value) \
- writel(value, (chip)->remap_addr + ICH6_REG_##reg)
-#define azx_readl(chip,reg) \
- readl((chip)->remap_addr + ICH6_REG_##reg)
-#define azx_writew(chip,reg,value) \
- writew(value, (chip)->remap_addr + ICH6_REG_##reg)
-#define azx_readw(chip,reg) \
- readw((chip)->remap_addr + ICH6_REG_##reg)
-#define azx_writeb(chip,reg,value) \
- writeb(value, (chip)->remap_addr + ICH6_REG_##reg)
-#define azx_readb(chip,reg) \
- readb((chip)->remap_addr + ICH6_REG_##reg)
-
-#define azx_sd_writel(dev,reg,value) \
- writel(value, (dev)->sd_addr + ICH6_REG_##reg)
-#define azx_sd_readl(dev,reg) \
- readl((dev)->sd_addr + ICH6_REG_##reg)
-#define azx_sd_writew(dev,reg,value) \
- writew(value, (dev)->sd_addr + ICH6_REG_##reg)
-#define azx_sd_readw(dev,reg) \
- readw((dev)->sd_addr + ICH6_REG_##reg)
-#define azx_sd_writeb(dev,reg,value) \
- writeb(value, (dev)->sd_addr + ICH6_REG_##reg)
-#define azx_sd_readb(dev,reg) \
- readb((dev)->sd_addr + ICH6_REG_##reg)
-
-/* for pcm support */
-#define get_azx_dev(substream) (substream->runtime->private_data)
-
-static int azx_acquire_irq(struct azx *chip, int do_disconnect);
-static int azx_send_cmd(struct hda_bus *bus, unsigned int val);
-/*
- * Interface for HD codec
- */
-
-/*
- * CORB / RIRB interface
- */
-static int azx_alloc_cmd_io(struct azx *chip)
-{
- int err;
-
- /* single page (at least 4096 bytes) must suffice for both ringbuffes */
- err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- PAGE_SIZE, &chip->rb);
- if (err < 0) {
- snd_printk(KERN_ERR SFX "cannot allocate CORB/RIRB\n");
- return err;
- }
- return 0;
-}
-
-static void azx_init_cmd_io(struct azx *chip)
-{
- spin_lock_irq(&chip->reg_lock);
- /* CORB set up */
- chip->corb.addr = chip->rb.addr;
- chip->corb.buf = (u32 *)chip->rb.area;
- azx_writel(chip, CORBLBASE, (u32)chip->corb.addr);
- azx_writel(chip, CORBUBASE, upper_32_bits(chip->corb.addr));
-
- /* set the corb size to 256 entries (ULI requires explicitly) */
- azx_writeb(chip, CORBSIZE, 0x02);
- /* set the corb write pointer to 0 */
- azx_writew(chip, CORBWP, 0);
- /* reset the corb hw read pointer */
- azx_writew(chip, CORBRP, ICH6_CORBRP_RST);
- /* enable corb dma */
- azx_writeb(chip, CORBCTL, ICH6_CORBCTL_RUN);
-
- /* RIRB set up */
- chip->rirb.addr = chip->rb.addr + 2048;
- chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
- chip->rirb.wp = chip->rirb.rp = 0;
- memset(chip->rirb.cmds, 0, sizeof(chip->rirb.cmds));
- azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
- azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr));
-
- /* set the rirb size to 256 entries (ULI requires explicitly) */
- azx_writeb(chip, RIRBSIZE, 0x02);
- /* reset the rirb hw write pointer */
- azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST);
- /* set N=1, get RIRB response interrupt for new entry */
- if (chip->driver_type == AZX_DRIVER_CTX)
- azx_writew(chip, RINTCNT, 0xc0);
- else
- azx_writew(chip, RINTCNT, 1);
- /* enable rirb dma and response irq */
- azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);
- spin_unlock_irq(&chip->reg_lock);
-}
-
-static void azx_free_cmd_io(struct azx *chip)
-{
- spin_lock_irq(&chip->reg_lock);
- /* disable ringbuffer DMAs */
- azx_writeb(chip, RIRBCTL, 0);
- azx_writeb(chip, CORBCTL, 0);
- spin_unlock_irq(&chip->reg_lock);
-}
-
-static unsigned int azx_command_addr(u32 cmd)
-{
- unsigned int addr = cmd >> 28;
-
- if (addr >= AZX_MAX_CODECS) {
- snd_BUG();
- addr = 0;
- }
-
- return addr;
-}
-
-static unsigned int azx_response_addr(u32 res)
-{
- unsigned int addr = res & 0xf;
-
- if (addr >= AZX_MAX_CODECS) {
- snd_BUG();
- addr = 0;
- }
-
- return addr;
-}
-
-/* send a command */
-static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
-{
- struct azx *chip = bus->private_data;
- unsigned int addr = azx_command_addr(val);
- unsigned int wp;
-
- spin_lock_irq(&chip->reg_lock);
-
- /* add command to corb */
- wp = azx_readb(chip, CORBWP);
- wp++;
- wp %= ICH6_MAX_CORB_ENTRIES;
-
- chip->rirb.cmds[addr]++;
- chip->corb.buf[wp] = cpu_to_le32(val);
- azx_writel(chip, CORBWP, wp);
-
- spin_unlock_irq(&chip->reg_lock);
-
- return 0;
-}
-
-#define ICH6_RIRB_EX_UNSOL_EV (1<<4)
-
-/* retrieve RIRB entry - called from interrupt handler */
-static void azx_update_rirb(struct azx *chip)
-{
- unsigned int rp, wp;
- unsigned int addr;
- u32 res, res_ex;
-
- wp = azx_readb(chip, RIRBWP);
- if (wp == chip->rirb.wp)
- return;
- chip->rirb.wp = wp;
-
- while (chip->rirb.rp != wp) {
- chip->rirb.rp++;
- chip->rirb.rp %= ICH6_MAX_RIRB_ENTRIES;
-
- rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
- res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
- res = le32_to_cpu(chip->rirb.buf[rp]);
- addr = azx_response_addr(res_ex);
- if (res_ex & ICH6_RIRB_EX_UNSOL_EV)
- snd_hda_queue_unsol_event(chip->bus, res, res_ex);
- else if (chip->rirb.cmds[addr]) {
- chip->rirb.res[addr] = res;
- smp_wmb();
- chip->rirb.cmds[addr]--;
- } else
- snd_printk(KERN_ERR SFX "spurious response %#x:%#x, "
- "last cmd=%#08x\n",
- res, res_ex,
- chip->last_cmd[addr]);
- }
-}
-
-/* receive a response */
-static unsigned int azx_rirb_get_response(struct hda_bus *bus,
- unsigned int addr)
-{
- struct azx *chip = bus->private_data;
- unsigned long timeout;
- int do_poll = 0;
-
- again:
- timeout = jiffies + msecs_to_jiffies(1000);
- for (;;) {
- if (chip->polling_mode || do_poll) {
- spin_lock_irq(&chip->reg_lock);
- azx_update_rirb(chip);
- spin_unlock_irq(&chip->reg_lock);
- }
- if (!chip->rirb.cmds[addr]) {
- smp_rmb();
- bus->rirb_error = 0;
-
- if (!do_poll)
- chip->poll_count = 0;
- return chip->rirb.res[addr]; /* the last value */
- }
- if (time_after(jiffies, timeout))
- break;
- if (bus->needs_damn_long_delay)
- msleep(2); /* temporary workaround */
- else {
- udelay(10);
- cond_resched();
- }
- }
-
- if (!chip->polling_mode && chip->poll_count < 2) {
- snd_printdd(SFX "azx_get_response timeout, "
- "polling the codec once: last cmd=0x%08x\n",
- chip->last_cmd[addr]);
- do_poll = 1;
- chip->poll_count++;
- goto again;
- }
-
-
- if (!chip->polling_mode) {
- snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
- "switching to polling mode: last cmd=0x%08x\n",
- chip->last_cmd[addr]);
- chip->polling_mode = 1;
- goto again;
- }
-
- if (chip->msi) {
- snd_printk(KERN_WARNING SFX "No response from codec, "
- "disabling MSI: last cmd=0x%08x\n",
- chip->last_cmd[addr]);
- free_irq(chip->irq, chip);
- chip->irq = -1;
- pci_disable_msi(chip->pci);
- chip->msi = 0;
- if (azx_acquire_irq(chip, 1) < 0) {
- bus->rirb_error = 1;
- return -1;
- }
- goto again;
- }
-
- if (chip->probing) {
- /* If this critical timeout happens during the codec probing
- * phase, this is likely an access to a non-existing codec
- * slot. Better to return an error and reset the system.
- */
- return -1;
- }
-
- /* a fatal communication error; need either to reset or to fallback
- * to the single_cmd mode
- */
- bus->rirb_error = 1;
- if (bus->allow_bus_reset && !bus->response_reset && !bus->in_reset) {
- bus->response_reset = 1;
- return -1; /* give a chance to retry */
- }
-
- snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
- "switching to single_cmd mode: last cmd=0x%08x\n",
- chip->last_cmd[addr]);
- chip->single_cmd = 1;
- bus->response_reset = 0;
- /* release CORB/RIRB */
- azx_free_cmd_io(chip);
- /* disable unsolicited responses */
- azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_UNSOL);
- return -1;
-}
-
-/*
- * Use the single immediate command instead of CORB/RIRB for simplicity
- *
- * Note: according to Intel, this is not preferred use. The command was
- * intended for the BIOS only, and may get confused with unsolicited
- * responses. So, we shouldn't use it for normal operation from the
- * driver.
- * I left the codes, however, for debugging/testing purposes.
- */
-
-/* receive a response */
-static int azx_single_wait_for_response(struct azx *chip, unsigned int addr)
-{
- int timeout = 50;
-
- while (timeout--) {
- /* check IRV busy bit */
- if (azx_readw(chip, IRS) & ICH6_IRS_VALID) {
- /* reuse rirb.res as the response return value */
- chip->rirb.res[addr] = azx_readl(chip, IR);
- return 0;
- }
- udelay(1);
- }
- if (printk_ratelimit())
- snd_printd(SFX "get_response timeout: IRS=0x%x\n",
- azx_readw(chip, IRS));
- chip->rirb.res[addr] = -1;
- return -EIO;
-}
-
-/* send a command */
-static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
-{
- struct azx *chip = bus->private_data;
- unsigned int addr = azx_command_addr(val);
- int timeout = 50;
-
- bus->rirb_error = 0;
- while (timeout--) {
- /* check ICB busy bit */
- if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) {
- /* Clear IRV valid bit */
- azx_writew(chip, IRS, azx_readw(chip, IRS) |
- ICH6_IRS_VALID);
- azx_writel(chip, IC, val);
- azx_writew(chip, IRS, azx_readw(chip, IRS) |
- ICH6_IRS_BUSY);
- return azx_single_wait_for_response(chip, addr);
- }
- udelay(1);
- }
- if (printk_ratelimit())
- snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n",
- azx_readw(chip, IRS), val);
- return -EIO;
-}
-
-/* receive a response */
-static unsigned int azx_single_get_response(struct hda_bus *bus,
- unsigned int addr)
-{
- struct azx *chip = bus->private_data;
- return chip->rirb.res[addr];
-}
-
-/*
- * The below are the main callbacks from hda_codec.
- *
- * They are just the skeleton to call sub-callbacks according to the
- * current setting of chip->single_cmd.
- */
-
-/* send a command */
-static int azx_send_cmd(struct hda_bus *bus, unsigned int val)
-{
- struct azx *chip = bus->private_data;
-
- chip->last_cmd[azx_command_addr(val)] = val;
- if (chip->single_cmd)
- return azx_single_send_cmd(bus, val);
- else
- return azx_corb_send_cmd(bus, val);
-}
-
-/* get a response */
-static unsigned int azx_get_response(struct hda_bus *bus,
- unsigned int addr)
-{
- struct azx *chip = bus->private_data;
- if (chip->single_cmd)
- return azx_single_get_response(bus, addr);
- else
- return azx_rirb_get_response(bus, addr);
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static void azx_power_notify(struct hda_bus *bus);
-#endif
-
-/* reset codec link */
-static int azx_reset(struct azx *chip, int full_reset)
-{
- int count;
-
- if (!full_reset)
- goto __skip;
-
- /* clear STATESTS */
- azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
-
- /* reset controller */
- azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_RESET);
-
- count = 50;
- while (azx_readb(chip, GCTL) && --count)
- msleep(1);
-
- /* delay for >= 100us for codec PLL to settle per spec
- * Rev 0.9 section 5.5.1
- */
- msleep(1);
-
- /* Bring controller out of reset */
- azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | ICH6_GCTL_RESET);
-
- count = 50;
- while (!azx_readb(chip, GCTL) && --count)
- msleep(1);
-
- /* Brent Chartrand said to wait >= 540us for codecs to initialize */
- msleep(1);
-
- __skip:
- /* check to see if controller is ready */
- if (!azx_readb(chip, GCTL)) {
- snd_printd(SFX "azx_reset: controller not ready!\n");
- return -EBUSY;
- }
-
- /* Accept unsolicited responses */
- if (!chip->single_cmd)
- azx_writel(chip, GCTL, azx_readl(chip, GCTL) |
- ICH6_GCTL_UNSOL);
-
- /* detect codecs */
- if (!chip->codec_mask) {
- chip->codec_mask = azx_readw(chip, STATESTS);
- snd_printdd(SFX "codec_mask = 0x%x\n", chip->codec_mask);
- }
-
- return 0;
-}
-
-
-/*
- * Lowlevel interface
- */
-
-/* enable interrupts */
-static void azx_int_enable(struct azx *chip)
-{
- /* enable controller CIE and GIE */
- azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) |
- ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN);
-}
-
-/* disable interrupts */
-static void azx_int_disable(struct azx *chip)
-{
- int i;
-
- /* disable interrupts in stream descriptor */
- for (i = 0; i < chip->num_streams; i++) {
- struct azx_dev *azx_dev = &chip->azx_dev[i];
- azx_sd_writeb(azx_dev, SD_CTL,
- azx_sd_readb(azx_dev, SD_CTL) & ~SD_INT_MASK);
- }
-
- /* disable SIE for all streams */
- azx_writeb(chip, INTCTL, 0);
-
- /* disable controller CIE and GIE */
- azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) &
- ~(ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN));
-}
-
-/* clear interrupts */
-static void azx_int_clear(struct azx *chip)
-{
- int i;
-
- /* clear stream status */
- for (i = 0; i < chip->num_streams; i++) {
- struct azx_dev *azx_dev = &chip->azx_dev[i];
- azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
- }
-
- /* clear STATESTS */
- azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
-
- /* clear rirb status */
- azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
-
- /* clear int status */
- azx_writel(chip, INTSTS, ICH6_INT_CTRL_EN | ICH6_INT_ALL_STREAM);
-}
-
-/* start a stream */
-static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev)
-{
- /*
- * Before stream start, initialize parameter
- */
- azx_dev->insufficient = 1;
-
- /* enable SIE */
- azx_writel(chip, INTCTL,
- azx_readl(chip, INTCTL) | (1 << azx_dev->index));
- /* set DMA start and interrupt mask */
- azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) |
- SD_CTL_DMA_START | SD_INT_MASK);
-}
-
-/* stop DMA */
-static void azx_stream_clear(struct azx *chip, struct azx_dev *azx_dev)
-{
- azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) &
- ~(SD_CTL_DMA_START | SD_INT_MASK));
- azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
-}
-
-/* stop a stream */
-static void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev)
-{
- azx_stream_clear(chip, azx_dev);
- /* disable SIE */
- azx_writel(chip, INTCTL,
- azx_readl(chip, INTCTL) & ~(1 << azx_dev->index));
-}
-
-
-/*
- * reset and start the controller registers
- */
-static void azx_init_chip(struct azx *chip, int full_reset)
-{
- if (chip->initialized)
- return;
-
- /* reset controller */
- azx_reset(chip, full_reset);
-
- /* initialize interrupts */
- azx_int_clear(chip);
- azx_int_enable(chip);
-
- /* initialize the codec command I/O */
- if (!chip->single_cmd)
- azx_init_cmd_io(chip);
-
- /* program the position buffer */
- azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
- azx_writel(chip, DPUBASE, upper_32_bits(chip->posbuf.addr));
-
- chip->initialized = 1;
-}
-
-/*
- * initialize the PCI registers
- */
-/* update bits in a PCI register byte */
-static void update_pci_byte(struct pci_dev *pci, unsigned int reg,
- unsigned char mask, unsigned char val)
-{
- unsigned char data;
-
- pci_read_config_byte(pci, reg, &data);
- data &= ~mask;
- data |= (val & mask);
- pci_write_config_byte(pci, reg, data);
-}
-
-static void azx_init_pci(struct azx *chip)
-{
- unsigned short snoop;
-
- /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44)
- * TCSEL == Traffic Class Select Register, which sets PCI express QOS
- * Ensuring these bits are 0 clears playback static on some HD Audio
- * codecs
- */
- update_pci_byte(chip->pci, ICH6_PCIREG_TCSEL, 0x07, 0);
-
- switch (chip->driver_type) {
- case AZX_DRIVER_ATI:
- /* For ATI SB450 azalia HD audio, we need to enable snoop */
- update_pci_byte(chip->pci,
- ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR,
- 0x07, ATI_SB450_HDAUDIO_ENABLE_SNOOP);
- break;
- case AZX_DRIVER_NVIDIA:
- /* For NVIDIA HDA, enable snoop */
- update_pci_byte(chip->pci,
- NVIDIA_HDA_TRANSREG_ADDR,
- 0x0f, NVIDIA_HDA_ENABLE_COHBITS);
- update_pci_byte(chip->pci,
- NVIDIA_HDA_ISTRM_COH,
- 0x01, NVIDIA_HDA_ENABLE_COHBIT);
- update_pci_byte(chip->pci,
- NVIDIA_HDA_OSTRM_COH,
- 0x01, NVIDIA_HDA_ENABLE_COHBIT);
- break;
- case AZX_DRIVER_SCH:
- case AZX_DRIVER_PCH:
- pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop);
- if (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) {
- pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC,
- snoop & (~INTEL_SCH_HDA_DEVC_NOSNOOP));
- pci_read_config_word(chip->pci,
- INTEL_SCH_HDA_DEVC, &snoop);
- snd_printdd(SFX "HDA snoop disabled, enabling ... %s\n",
- (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP)
- ? "Failed" : "OK");
- }
- break;
-
- }
-}
-
-
-static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev);
-
-/*
- * interrupt handler
- */
-static irqreturn_t azx_interrupt(int irq, void *dev_id)
-{
- struct azx *chip = dev_id;
- struct azx_dev *azx_dev;
- u32 status;
- u8 sd_status;
- int i, ok;
-
- spin_lock(&chip->reg_lock);
-
- status = azx_readl(chip, INTSTS);
- if (status == 0) {
- spin_unlock(&chip->reg_lock);
- return IRQ_NONE;
- }
-
- for (i = 0; i < chip->num_streams; i++) {
- azx_dev = &chip->azx_dev[i];
- if (status & azx_dev->sd_int_sta_mask) {
- sd_status = azx_sd_readb(azx_dev, SD_STS);
- azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
- if (!azx_dev->substream || !azx_dev->running ||
- !(sd_status & SD_INT_COMPLETE))
- continue;
- /* check whether this IRQ is really acceptable */
- ok = azx_position_ok(chip, azx_dev);
- if (ok == 1) {
- azx_dev->irq_pending = 0;
- spin_unlock(&chip->reg_lock);
- snd_pcm_period_elapsed(azx_dev->substream);
- spin_lock(&chip->reg_lock);
- } else if (ok == 0 && chip->bus && chip->bus->workq) {
- /* bogus IRQ, process it later */
- azx_dev->irq_pending = 1;
- queue_work(chip->bus->workq,
- &chip->irq_pending_work);
- }
- }
- }
-
- /* clear rirb int */
- status = azx_readb(chip, RIRBSTS);
- if (status & RIRB_INT_MASK) {
- if (status & RIRB_INT_RESPONSE) {
- if (chip->driver_type == AZX_DRIVER_CTX)
- udelay(80);
- azx_update_rirb(chip);
- }
- azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
- }
-
-#if 0
- /* clear state status int */
- if (azx_readb(chip, STATESTS) & 0x04)
- azx_writeb(chip, STATESTS, 0x04);
-#endif
- spin_unlock(&chip->reg_lock);
-
- return IRQ_HANDLED;
-}
-
-
-/*
- * set up a BDL entry
- */
-static int setup_bdle(struct snd_pcm_substream *substream,
- struct azx_dev *azx_dev, u32 **bdlp,
- int ofs, int size, int with_ioc)
-{
- u32 *bdl = *bdlp;
-
- while (size > 0) {
- dma_addr_t addr;
- int chunk;
-
- if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
- return -EINVAL;
-
- addr = snd_pcm_sgbuf_get_addr(substream, ofs);
- /* program the address field of the BDL entry */
- bdl[0] = cpu_to_le32((u32)addr);
- bdl[1] = cpu_to_le32(upper_32_bits(addr));
- /* program the size field of the BDL entry */
- chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size);
- bdl[2] = cpu_to_le32(chunk);
- /* program the IOC to enable interrupt
- * only when the whole fragment is processed
- */
- size -= chunk;
- bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01);
- bdl += 4;
- azx_dev->frags++;
- ofs += chunk;
- }
- *bdlp = bdl;
- return ofs;
-}
-
-/*
- * set up BDL entries
- */
-static int azx_setup_periods(struct azx *chip,
- struct snd_pcm_substream *substream,
- struct azx_dev *azx_dev)
-{
- u32 *bdl;
- int i, ofs, periods, period_bytes;
- int pos_adj;
-
- /* reset BDL address */
- azx_sd_writel(azx_dev, SD_BDLPL, 0);
- azx_sd_writel(azx_dev, SD_BDLPU, 0);
-
- period_bytes = azx_dev->period_bytes;
- periods = azx_dev->bufsize / period_bytes;
-
- /* program the initial BDL entries */
- bdl = (u32 *)azx_dev->bdl.area;
- ofs = 0;
- azx_dev->frags = 0;
- pos_adj = bdl_pos_adj[chip->dev_index];
- if (pos_adj > 0) {
- struct snd_pcm_runtime *runtime = substream->runtime;
- int pos_align = pos_adj;
- pos_adj = (pos_adj * runtime->rate + 47999) / 48000;
- if (!pos_adj)
- pos_adj = pos_align;
- else
- pos_adj = ((pos_adj + pos_align - 1) / pos_align) *
- pos_align;
- pos_adj = frames_to_bytes(runtime, pos_adj);
- if (pos_adj >= period_bytes) {
- snd_printk(KERN_WARNING SFX "Too big adjustment %d\n",
- bdl_pos_adj[chip->dev_index]);
- pos_adj = 0;
- } else {
- ofs = setup_bdle(substream, azx_dev,
- &bdl, ofs, pos_adj, 1);
- if (ofs < 0)
- goto error;
- }
- } else
- pos_adj = 0;
- for (i = 0; i < periods; i++) {
- if (i == periods - 1 && pos_adj)
- ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
- period_bytes - pos_adj, 0);
- else
- ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
- period_bytes, 1);
- if (ofs < 0)
- goto error;
- }
- return 0;
-
- error:
- snd_printk(KERN_ERR SFX "Too many BDL entries: buffer=%d, period=%d\n",
- azx_dev->bufsize, period_bytes);
- return -EINVAL;
-}
-
-/* reset stream */
-static void azx_stream_reset(struct azx *chip, struct azx_dev *azx_dev)
-{
- unsigned char val;
- int timeout;
-
- azx_stream_clear(chip, azx_dev);
-
- azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) |
- SD_CTL_STREAM_RESET);
- udelay(3);
- timeout = 300;
- while (!((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
- --timeout)
- ;
- val &= ~SD_CTL_STREAM_RESET;
- azx_sd_writeb(azx_dev, SD_CTL, val);
- udelay(3);
-
- timeout = 300;
- /* waiting for hardware to report that the stream is out of reset */
- while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
- --timeout)
- ;
-
- /* reset first position - may not be synced with hw at this time */
- *azx_dev->posbuf = 0;
-}
-
-/*
- * set up the SD for streaming
- */
-static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
-{
- /* make sure the run bit is zero for SD */
- azx_stream_clear(chip, azx_dev);
- /* program the stream_tag */
- azx_sd_writel(azx_dev, SD_CTL,
- (azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK)|
- (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT));
-
- /* program the length of samples in cyclic buffer */
- azx_sd_writel(azx_dev, SD_CBL, azx_dev->bufsize);
-
- /* program the stream format */
- /* this value needs to be the same as the one programmed */
- azx_sd_writew(azx_dev, SD_FORMAT, azx_dev->format_val);
-
- /* program the stream LVI (last valid index) of the BDL */
- azx_sd_writew(azx_dev, SD_LVI, azx_dev->frags - 1);
-
- /* program the BDL address */
- /* lower BDL address */
- azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr);
- /* upper BDL address */
- azx_sd_writel(azx_dev, SD_BDLPU, upper_32_bits(azx_dev->bdl.addr));
-
- /* enable the position buffer */
- if (chip->position_fix[0] != POS_FIX_LPIB ||
- chip->position_fix[1] != POS_FIX_LPIB) {
- if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
- azx_writel(chip, DPLBASE,
- (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
- }
-
- /* set the interrupt enable bits in the descriptor control register */
- azx_sd_writel(azx_dev, SD_CTL,
- azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK);
-
- return 0;
-}
-
-/*
- * Probe the given codec address
- */
-static int probe_codec(struct azx *chip, int addr)
-{
- unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
- (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
- unsigned int res;
-
- mutex_lock(&chip->bus->cmd_mutex);
- chip->probing = 1;
- azx_send_cmd(chip->bus, cmd);
- res = azx_get_response(chip->bus, addr);
- chip->probing = 0;
- mutex_unlock(&chip->bus->cmd_mutex);
- if (res == -1)
- return -EIO;
- snd_printdd(SFX "codec #%d probed OK\n", addr);
- return 0;
-}
-
-static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
- struct hda_pcm *cpcm);
-static void azx_stop_chip(struct azx *chip);
-
-static void azx_bus_reset(struct hda_bus *bus)
-{
- struct azx *chip = bus->private_data;
-
- bus->in_reset = 1;
- azx_stop_chip(chip);
- azx_init_chip(chip, 1);
-#ifdef CONFIG_PM
- if (chip->initialized) {
- int i;
-
- for (i = 0; i < HDA_MAX_PCMS; i++)
- snd_pcm_suspend_all(chip->pcm[i]);
- snd_hda_suspend(chip->bus);
- snd_hda_resume(chip->bus);
- }
-#endif
- bus->in_reset = 0;
-}
-
-/*
- * Codec initialization
- */
-
-/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */
-static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
- [AZX_DRIVER_NVIDIA] = 8,
- [AZX_DRIVER_TERA] = 1,
-};
-
-static int __devinit azx_codec_create(struct azx *chip, const char *model)
-{
- struct hda_bus_template bus_temp;
- int c, codecs, err;
- int max_slots;
-
- memset(&bus_temp, 0, sizeof(bus_temp));
- bus_temp.private_data = chip;
- bus_temp.modelname = model;
- bus_temp.pci = chip->pci;
- bus_temp.ops.command = azx_send_cmd;
- bus_temp.ops.get_response = azx_get_response;
- bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
- bus_temp.ops.bus_reset = azx_bus_reset;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- bus_temp.power_save = &power_save;
- bus_temp.ops.pm_notify = azx_power_notify;
-#endif
-
- err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus);
- if (err < 0)
- return err;
-
- if (chip->driver_type == AZX_DRIVER_NVIDIA)
- chip->bus->needs_damn_long_delay = 1;
-
- codecs = 0;
- max_slots = azx_max_codecs[chip->driver_type];
- if (!max_slots)
- max_slots = AZX_DEFAULT_CODECS;
-
- /* First try to probe all given codec slots */
- for (c = 0; c < max_slots; c++) {
- if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
- if (probe_codec(chip, c) < 0) {
- /* Some BIOSen give you wrong codec addresses
- * that don't exist
- */
- snd_printk(KERN_WARNING SFX
- "Codec #%d probe error; "
- "disabling it...\n", c);
- chip->codec_mask &= ~(1 << c);
- /* More badly, accessing to a non-existing
- * codec often screws up the controller chip,
- * and disturbs the further communications.
- * Thus if an error occurs during probing,
- * better to reset the controller chip to
- * get back to the sanity state.
- */
- azx_stop_chip(chip);
- azx_init_chip(chip, 1);
- }
- }
- }
-
- /* Then create codec instances */
- for (c = 0; c < max_slots; c++) {
- if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
- struct hda_codec *codec;
- err = snd_hda_codec_new(chip->bus, c, &codec);
- if (err < 0)
- continue;
- codec->beep_mode = chip->beep_mode;
- codecs++;
- }
- }
- if (!codecs) {
- snd_printk(KERN_ERR SFX "no codecs initialized\n");
- return -ENXIO;
- }
- return 0;
-}
-
-/* configure each codec instance */
-static int __devinit azx_codec_configure(struct azx *chip)
-{
- struct hda_codec *codec;
- list_for_each_entry(codec, &chip->bus->codec_list, list) {
- snd_hda_codec_configure(codec);
- }
- return 0;
-}
-
-
-/*
- * PCM support
- */
-
-/* assign a stream for the PCM */
-static inline struct azx_dev *
-azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream)
-{
- int dev, i, nums;
- struct azx_dev *res = NULL;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dev = chip->playback_index_offset;
- nums = chip->playback_streams;
- } else {
- dev = chip->capture_index_offset;
- nums = chip->capture_streams;
- }
- for (i = 0; i < nums; i++, dev++)
- if (!chip->azx_dev[dev].opened) {
- res = &chip->azx_dev[dev];
- if (res->device == substream->pcm->device)
- break;
- }
- if (res) {
- res->opened = 1;
- res->device = substream->pcm->device;
- }
- return res;
-}
-
-/* release the assigned stream */
-static inline void azx_release_device(struct azx_dev *azx_dev)
-{
- azx_dev->opened = 0;
-}
-
-static struct snd_pcm_hardware azx_pcm_hw = {
- .info = (SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP_VALID |
- /* No full-resume yet implemented */
- /* SNDRV_PCM_INFO_RESUME |*/
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_SYNC_START),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = SNDRV_PCM_RATE_48000,
- .rate_min = 48000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = AZX_MAX_BUF_SIZE,
- .period_bytes_min = 128,
- .period_bytes_max = AZX_MAX_BUF_SIZE / 2,
- .periods_min = 2,
- .periods_max = AZX_MAX_FRAG,
- .fifo_size = 0,
-};
-
-struct azx_pcm {
- struct azx *chip;
- struct hda_codec *codec;
- struct hda_pcm_stream *hinfo[2];
-};
-
-static int azx_pcm_open(struct snd_pcm_substream *substream)
-{
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
- struct azx *chip = apcm->chip;
- struct azx_dev *azx_dev;
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned long flags;
- int err;
-
- mutex_lock(&chip->open_mutex);
- azx_dev = azx_assign_device(chip, substream);
- if (azx_dev == NULL) {
- mutex_unlock(&chip->open_mutex);
- return -EBUSY;
- }
- runtime->hw = azx_pcm_hw;
- runtime->hw.channels_min = hinfo->channels_min;
- runtime->hw.channels_max = hinfo->channels_max;
- runtime->hw.formats = hinfo->formats;
- runtime->hw.rates = hinfo->rates;
- snd_pcm_limit_hw_rates(runtime);
- snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
- snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
- 128);
- snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
- 128);
- snd_hda_power_up(apcm->codec);
- err = hinfo->ops.open(hinfo, apcm->codec, substream);
- if (err < 0) {
- azx_release_device(azx_dev);
- snd_hda_power_down(apcm->codec);
- mutex_unlock(&chip->open_mutex);
- return err;
- }
- snd_pcm_limit_hw_rates(runtime);
- /* sanity check */
- if (snd_BUG_ON(!runtime->hw.channels_min) ||
- snd_BUG_ON(!runtime->hw.channels_max) ||
- snd_BUG_ON(!runtime->hw.formats) ||
- snd_BUG_ON(!runtime->hw.rates)) {
- azx_release_device(azx_dev);
- hinfo->ops.close(hinfo, apcm->codec, substream);
- snd_hda_power_down(apcm->codec);
- mutex_unlock(&chip->open_mutex);
- return -EINVAL;
- }
- spin_lock_irqsave(&chip->reg_lock, flags);
- azx_dev->substream = substream;
- azx_dev->running = 0;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
-
- runtime->private_data = azx_dev;
- snd_pcm_set_sync(substream);
- mutex_unlock(&chip->open_mutex);
- return 0;
-}
-
-static int azx_pcm_close(struct snd_pcm_substream *substream)
-{
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
- struct azx *chip = apcm->chip;
- struct azx_dev *azx_dev = get_azx_dev(substream);
- unsigned long flags;
-
- mutex_lock(&chip->open_mutex);
- spin_lock_irqsave(&chip->reg_lock, flags);
- azx_dev->substream = NULL;
- azx_dev->running = 0;
- spin_unlock_irqrestore(&chip->reg_lock, flags);
- azx_release_device(azx_dev);
- hinfo->ops.close(hinfo, apcm->codec, substream);
- snd_hda_power_down(apcm->codec);
- mutex_unlock(&chip->open_mutex);
- return 0;
-}
-
-static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct azx_dev *azx_dev = get_azx_dev(substream);
-
- azx_dev->bufsize = 0;
- azx_dev->period_bytes = 0;
- azx_dev->format_val = 0;
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct azx_dev *azx_dev = get_azx_dev(substream);
- struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
-
- /* reset BDL address */
- azx_sd_writel(azx_dev, SD_BDLPL, 0);
- azx_sd_writel(azx_dev, SD_BDLPU, 0);
- azx_sd_writel(azx_dev, SD_CTL, 0);
- azx_dev->bufsize = 0;
- azx_dev->period_bytes = 0;
- azx_dev->format_val = 0;
-
- snd_hda_codec_cleanup(apcm->codec, hinfo, substream);
-
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int azx_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct azx *chip = apcm->chip;
- struct azx_dev *azx_dev = get_azx_dev(substream);
- struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int bufsize, period_bytes, format_val, stream_tag;
- int err;
-
- azx_stream_reset(chip, azx_dev);
- format_val = snd_hda_calc_stream_format(runtime->rate,
- runtime->channels,
- runtime->format,
- hinfo->maxbps,
- apcm->codec->spdif_ctls);
- if (!format_val) {
- snd_printk(KERN_ERR SFX
- "invalid format_val, rate=%d, ch=%d, format=%d\n",
- runtime->rate, runtime->channels, runtime->format);
- return -EINVAL;
- }
-
- bufsize = snd_pcm_lib_buffer_bytes(substream);
- period_bytes = snd_pcm_lib_period_bytes(substream);
-
- snd_printdd(SFX "azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
- bufsize, format_val);
-
- if (bufsize != azx_dev->bufsize ||
- period_bytes != azx_dev->period_bytes ||
- format_val != azx_dev->format_val) {
- azx_dev->bufsize = bufsize;
- azx_dev->period_bytes = period_bytes;
- azx_dev->format_val = format_val;
- err = azx_setup_periods(chip, substream, azx_dev);
- if (err < 0)
- return err;
- }
-
- /* wallclk has 24Mhz clock source */
- azx_dev->period_wallclk = (((runtime->period_size * 24000) /
- runtime->rate) * 1000);
- azx_setup_controller(chip, azx_dev);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
- else
- azx_dev->fifo_size = 0;
-
- stream_tag = azx_dev->stream_tag;
- /* CA-IBG chips need the playback stream starting from 1 */
- if (chip->driver_type == AZX_DRIVER_CTX &&
- stream_tag > chip->capture_streams)
- stream_tag -= chip->capture_streams;
- return snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag,
- azx_dev->format_val, substream);
-}
-
-static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct azx *chip = apcm->chip;
- struct azx_dev *azx_dev;
- struct snd_pcm_substream *s;
- int rstart = 0, start, nsync = 0, sbits = 0;
- int nwait, timeout;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- rstart = 1;
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- case SNDRV_PCM_TRIGGER_RESUME:
- start = 1;
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- start = 0;
- break;
- default:
- return -EINVAL;
- }
-
- snd_pcm_group_for_each_entry(s, substream) {
- if (s->pcm->card != substream->pcm->card)
- continue;
- azx_dev = get_azx_dev(s);
- sbits |= 1 << azx_dev->index;
- nsync++;
- snd_pcm_trigger_done(s, substream);
- }
-
- spin_lock(&chip->reg_lock);
- if (nsync > 1) {
- /* first, set SYNC bits of corresponding streams */
- azx_writel(chip, SYNC, azx_readl(chip, SYNC) | sbits);
- }
- snd_pcm_group_for_each_entry(s, substream) {
- if (s->pcm->card != substream->pcm->card)
- continue;
- azx_dev = get_azx_dev(s);
- if (start) {
- azx_dev->start_wallclk = azx_readl(chip, WALLCLK);
- if (!rstart)
- azx_dev->start_wallclk -=
- azx_dev->period_wallclk;
- azx_stream_start(chip, azx_dev);
- } else {
- azx_stream_stop(chip, azx_dev);
- }
- azx_dev->running = start;
- }
- spin_unlock(&chip->reg_lock);
- if (start) {
- if (nsync == 1)
- return 0;
- /* wait until all FIFOs get ready */
- for (timeout = 5000; timeout; timeout--) {
- nwait = 0;
- snd_pcm_group_for_each_entry(s, substream) {
- if (s->pcm->card != substream->pcm->card)
- continue;
- azx_dev = get_azx_dev(s);
- if (!(azx_sd_readb(azx_dev, SD_STS) &
- SD_STS_FIFO_READY))
- nwait++;
- }
- if (!nwait)
- break;
- cpu_relax();
- }
- } else {
- /* wait until all RUN bits are cleared */
- for (timeout = 5000; timeout; timeout--) {
- nwait = 0;
- snd_pcm_group_for_each_entry(s, substream) {
- if (s->pcm->card != substream->pcm->card)
- continue;
- azx_dev = get_azx_dev(s);
- if (azx_sd_readb(azx_dev, SD_CTL) &
- SD_CTL_DMA_START)
- nwait++;
- }
- if (!nwait)
- break;
- cpu_relax();
- }
- }
- if (nsync > 1) {
- spin_lock(&chip->reg_lock);
- /* reset SYNC bits */
- azx_writel(chip, SYNC, azx_readl(chip, SYNC) & ~sbits);
- spin_unlock(&chip->reg_lock);
- }
- return 0;
-}
-
-/* get the current DMA position with correction on VIA chips */
-static unsigned int azx_via_get_position(struct azx *chip,
- struct azx_dev *azx_dev)
-{
- unsigned int link_pos, mini_pos, bound_pos;
- unsigned int mod_link_pos, mod_dma_pos, mod_mini_pos;
- unsigned int fifo_size;
-
- link_pos = azx_sd_readl(azx_dev, SD_LPIB);
- if (azx_dev->index >= 4) {
- /* Playback, no problem using link position */
- return link_pos;
- }
-
- /* Capture */
- /* For new chipset,
- * use mod to get the DMA position just like old chipset
- */
- mod_dma_pos = le32_to_cpu(*azx_dev->posbuf);
- mod_dma_pos %= azx_dev->period_bytes;
-
- /* azx_dev->fifo_size can't get FIFO size of in stream.
- * Get from base address + offset.
- */
- fifo_size = readw(chip->remap_addr + VIA_IN_STREAM0_FIFO_SIZE_OFFSET);
-
- if (azx_dev->insufficient) {
- /* Link position never gather than FIFO size */
- if (link_pos <= fifo_size)
- return 0;
-
- azx_dev->insufficient = 0;
- }
-
- if (link_pos <= fifo_size)
- mini_pos = azx_dev->bufsize + link_pos - fifo_size;
- else
- mini_pos = link_pos - fifo_size;
-
- /* Find nearest previous boudary */
- mod_mini_pos = mini_pos % azx_dev->period_bytes;
- mod_link_pos = link_pos % azx_dev->period_bytes;
- if (mod_link_pos >= fifo_size)
- bound_pos = link_pos - mod_link_pos;
- else if (mod_dma_pos >= mod_mini_pos)
- bound_pos = mini_pos - mod_mini_pos;
- else {
- bound_pos = mini_pos - mod_mini_pos + azx_dev->period_bytes;
- if (bound_pos >= azx_dev->bufsize)
- bound_pos = 0;
- }
-
- /* Calculate real DMA position we want */
- return bound_pos + mod_dma_pos;
-}
-
-static unsigned int azx_get_position(struct azx *chip,
- struct azx_dev *azx_dev)
-{
- unsigned int pos;
- int stream = azx_dev->substream->stream;
-
- switch (chip->position_fix[stream]) {
- case POS_FIX_LPIB:
- /* read LPIB */
- pos = azx_sd_readl(azx_dev, SD_LPIB);
- break;
- case POS_FIX_VIACOMBO:
- pos = azx_via_get_position(chip, azx_dev);
- break;
- default:
- /* use the position buffer */
- pos = le32_to_cpu(*azx_dev->posbuf);
- }
-
- if (pos >= azx_dev->bufsize)
- pos = 0;
- return pos;
-}
-
-static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
- struct azx *chip = apcm->chip;
- struct azx_dev *azx_dev = get_azx_dev(substream);
- return bytes_to_frames(substream->runtime,
- azx_get_position(chip, azx_dev));
-}
-
-/*
- * Check whether the current DMA position is acceptable for updating
- * periods. Returns non-zero if it's OK.
- *
- * Many HD-audio controllers appear pretty inaccurate about
- * the update-IRQ timing. The IRQ is issued before actually the
- * data is processed. So, we need to process it afterwords in a
- * workqueue.
- */
-static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
-{
- u32 wallclk;
- unsigned int pos;
- int stream;
-
- wallclk = azx_readl(chip, WALLCLK) - azx_dev->start_wallclk;
- if (wallclk < (azx_dev->period_wallclk * 2) / 3)
- return -1; /* bogus (too early) interrupt */
-
- stream = azx_dev->substream->stream;
- pos = azx_get_position(chip, azx_dev);
- if (chip->position_fix[stream] == POS_FIX_AUTO) {
- if (!pos) {
- printk(KERN_WARNING
- "hda-intel: Invalid position buffer, "
- "using LPIB read method instead.\n");
- chip->position_fix[stream] = POS_FIX_LPIB;
- pos = azx_get_position(chip, azx_dev);
- } else
- chip->position_fix[stream] = POS_FIX_POSBUF;
- }
-
- if (WARN_ONCE(!azx_dev->period_bytes,
- "hda-intel: zero azx_dev->period_bytes"))
- return -1; /* this shouldn't happen! */
- if (wallclk < (azx_dev->period_wallclk * 5) / 4 &&
- pos % azx_dev->period_bytes > azx_dev->period_bytes / 2)
- /* NG - it's below the first next period boundary */
- return bdl_pos_adj[chip->dev_index] ? 0 : -1;
- azx_dev->start_wallclk += wallclk;
- return 1; /* OK, it's fine */
-}
-
-/*
- * The work for pending PCM period updates.
- */
-static void azx_irq_pending_work(struct work_struct *work)
-{
- struct azx *chip = container_of(work, struct azx, irq_pending_work);
- int i, pending, ok;
-
- if (!chip->irq_pending_warned) {
- printk(KERN_WARNING
- "hda-intel: IRQ timing workaround is activated "
- "for card #%d. Suggest a bigger bdl_pos_adj.\n",
- chip->card->number);
- chip->irq_pending_warned = 1;
- }
-
- for (;;) {
- pending = 0;
- spin_lock_irq(&chip->reg_lock);
- for (i = 0; i < chip->num_streams; i++) {
- struct azx_dev *azx_dev = &chip->azx_dev[i];
- if (!azx_dev->irq_pending ||
- !azx_dev->substream ||
- !azx_dev->running)
- continue;
- ok = azx_position_ok(chip, azx_dev);
- if (ok > 0) {
- azx_dev->irq_pending = 0;
- spin_unlock(&chip->reg_lock);
- snd_pcm_period_elapsed(azx_dev->substream);
- spin_lock(&chip->reg_lock);
- } else if (ok < 0) {
- pending = 0; /* too early */
- } else
- pending++;
- }
- spin_unlock_irq(&chip->reg_lock);
- if (!pending)
- return;
- msleep(1);
- }
-}
-
-/* clear irq_pending flags and assure no on-going workq */
-static void azx_clear_irq_pending(struct azx *chip)
-{
- int i;
-
- spin_lock_irq(&chip->reg_lock);
- for (i = 0; i < chip->num_streams; i++)
- chip->azx_dev[i].irq_pending = 0;
- spin_unlock_irq(&chip->reg_lock);
-}
-
-static struct snd_pcm_ops azx_pcm_ops = {
- .open = azx_pcm_open,
- .close = azx_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = azx_pcm_hw_params,
- .hw_free = azx_pcm_hw_free,
- .prepare = azx_pcm_prepare,
- .trigger = azx_pcm_trigger,
- .pointer = azx_pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
-};
-
-static void azx_pcm_free(struct snd_pcm *pcm)
-{
- struct azx_pcm *apcm = pcm->private_data;
- if (apcm) {
- apcm->chip->pcm[pcm->device] = NULL;
- kfree(apcm);
- }
-}
-
-static int
-azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
- struct hda_pcm *cpcm)
-{
- struct azx *chip = bus->private_data;
- struct snd_pcm *pcm;
- struct azx_pcm *apcm;
- int pcm_dev = cpcm->device;
- int s, err;
-
- if (pcm_dev >= HDA_MAX_PCMS) {
- snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n",
- pcm_dev);
- return -EINVAL;
- }
- if (chip->pcm[pcm_dev]) {
- snd_printk(KERN_ERR SFX "PCM %d already exists\n", pcm_dev);
- return -EBUSY;
- }
- err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
- cpcm->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams,
- cpcm->stream[SNDRV_PCM_STREAM_CAPTURE].substreams,
- &pcm);
- if (err < 0)
- return err;
- strlcpy(pcm->name, cpcm->name, sizeof(pcm->name));
- apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
- if (apcm == NULL)
- return -ENOMEM;
- apcm->chip = chip;
- apcm->codec = codec;
- pcm->private_data = apcm;
- pcm->private_free = azx_pcm_free;
- if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM)
- pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
- chip->pcm[pcm_dev] = pcm;
- cpcm->pcm = pcm;
- for (s = 0; s < 2; s++) {
- apcm->hinfo[s] = &cpcm->stream[s];
- if (cpcm->stream[s].substreams)
- snd_pcm_set_ops(pcm, s, &azx_pcm_ops);
- }
- /* buffer pre-allocation */
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci),
- 1024 * 64, 32 * 1024 * 1024);
- return 0;
-}
-
-/*
- * mixer creation - all stuff is implemented in hda module
- */
-static int __devinit azx_mixer_create(struct azx *chip)
-{
- return snd_hda_build_controls(chip->bus);
-}
-
-
-/*
- * initialize SD streams
- */
-static int __devinit azx_init_stream(struct azx *chip)
-{
- int i;
-
- /* initialize each stream (aka device)
- * assign the starting bdl address to each stream (device)
- * and initialize
- */
- for (i = 0; i < chip->num_streams; i++) {
- struct azx_dev *azx_dev = &chip->azx_dev[i];
- azx_dev->posbuf = (u32 __iomem *)(chip->posbuf.area + i * 8);
- /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
- azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80);
- /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
- azx_dev->sd_int_sta_mask = 1 << i;
- /* stream tag: must be non-zero and unique */
- azx_dev->index = i;
- azx_dev->stream_tag = i + 1;
- }
-
- return 0;
-}
-
-static int azx_acquire_irq(struct azx *chip, int do_disconnect)
-{
- if (request_irq(chip->pci->irq, azx_interrupt,
- chip->msi ? 0 : IRQF_SHARED,
- "hda_intel", chip)) {
- printk(KERN_ERR "hda-intel: unable to grab IRQ %d, "
- "disabling device\n", chip->pci->irq);
- if (do_disconnect)
- snd_card_disconnect(chip->card);
- return -1;
- }
- chip->irq = chip->pci->irq;
- pci_intx(chip->pci, !chip->msi);
- return 0;
-}
-
-
-static void azx_stop_chip(struct azx *chip)
-{
- if (!chip->initialized)
- return;
-
- /* disable interrupts */
- azx_int_disable(chip);
- azx_int_clear(chip);
-
- /* disable CORB/RIRB */
- azx_free_cmd_io(chip);
-
- /* disable position buffer */
- azx_writel(chip, DPLBASE, 0);
- azx_writel(chip, DPUBASE, 0);
-
- chip->initialized = 0;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-/* power-up/down the controller */
-static void azx_power_notify(struct hda_bus *bus)
-{
- struct azx *chip = bus->private_data;
- struct hda_codec *c;
- int power_on = 0;
-
- list_for_each_entry(c, &bus->codec_list, list) {
- if (c->power_on) {
- power_on = 1;
- break;
- }
- }
- if (power_on)
- azx_init_chip(chip, 1);
- else if (chip->running && power_save_controller &&
- !bus->power_keep_link_on)
- azx_stop_chip(chip);
-}
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
-
-#ifdef CONFIG_PM
-/*
- * power management
- */
-
-static int snd_hda_codecs_inuse(struct hda_bus *bus)
-{
- struct hda_codec *codec;
-
- list_for_each_entry(codec, &bus->codec_list, list) {
- if (snd_hda_codec_needs_resume(codec))
- return 1;
- }
- return 0;
-}
-
-static int azx_suspend(struct pci_dev *pci, pm_message_t state)
-{
- struct snd_card *card = pci_get_drvdata(pci);
- struct azx *chip = card->private_data;
- int i;
-
- snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- azx_clear_irq_pending(chip);
- for (i = 0; i < HDA_MAX_PCMS; i++)
- snd_pcm_suspend_all(chip->pcm[i]);
- if (chip->initialized)
- snd_hda_suspend(chip->bus);
- azx_stop_chip(chip);
- if (chip->irq >= 0) {
- free_irq(chip->irq, chip);
- chip->irq = -1;
- }
- if (chip->msi)
- pci_disable_msi(chip->pci);
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
- return 0;
-}
-
-static int azx_resume(struct pci_dev *pci)
-{
- struct snd_card *card = pci_get_drvdata(pci);
- struct azx *chip = card->private_data;
-
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "hda-intel: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
- if (chip->msi)
- if (pci_enable_msi(pci) < 0)
- chip->msi = 0;
- if (azx_acquire_irq(chip, 1) < 0)
- return -EIO;
- azx_init_pci(chip);
-
- if (snd_hda_codecs_inuse(chip->bus))
- azx_init_chip(chip, 1);
-
- snd_hda_resume(chip->bus);
- snd_power_change_state(card, SNDRV_CTL_POWER_D0);
- return 0;
-}
-#endif /* CONFIG_PM */
-
-
-/*
- * reboot notifier for hang-up problem at power-down
- */
-static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf)
-{
- struct azx *chip = container_of(nb, struct azx, reboot_notifier);
- snd_hda_bus_reboot_notify(chip->bus);
- azx_stop_chip(chip);
- return NOTIFY_OK;
-}
-
-static void azx_notifier_register(struct azx *chip)
-{
- chip->reboot_notifier.notifier_call = azx_halt;
- register_reboot_notifier(&chip->reboot_notifier);
-}
-
-static void azx_notifier_unregister(struct azx *chip)
-{
- if (chip->reboot_notifier.notifier_call)
- unregister_reboot_notifier(&chip->reboot_notifier);
-}
-
-/*
- * destructor
- */
-static int azx_free(struct azx *chip)
-{
- int i;
-
- azx_notifier_unregister(chip);
-
- if (chip->initialized) {
- azx_clear_irq_pending(chip);
- for (i = 0; i < chip->num_streams; i++)
- azx_stream_stop(chip, &chip->azx_dev[i]);
- azx_stop_chip(chip);
- }
-
- if (chip->irq >= 0)
- free_irq(chip->irq, (void*)chip);
- if (chip->msi)
- pci_disable_msi(chip->pci);
- if (chip->remap_addr)
- iounmap(chip->remap_addr);
-
- if (chip->azx_dev) {
- for (i = 0; i < chip->num_streams; i++)
- if (chip->azx_dev[i].bdl.area)
- snd_dma_free_pages(&chip->azx_dev[i].bdl);
- }
- if (chip->rb.area)
- snd_dma_free_pages(&chip->rb);
- if (chip->posbuf.area)
- snd_dma_free_pages(&chip->posbuf);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip->azx_dev);
- kfree(chip);
-
- return 0;
-}
-
-static int azx_dev_free(struct snd_device *device)
-{
- return azx_free(device->device_data);
-}
-
-/*
- * white/black-listing for position_fix
- */
-static struct snd_pci_quirk position_fix_list[] __devinitdata = {
- SND_PCI_QUIRK(0x1025, 0x009f, "Acer Aspire 5110", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1028, 0x01f6, "Dell Latitude 131L", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS M2V", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x104d, 0x9069, "Sony VPCS11V9E", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba A100-259", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1297, 0x3166, "Shuttle", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1458, 0xa022, "ga-ma770-ud3", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1565, 0x820f, "Biostar Microtech", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1565, 0x8218, "Biostar Microtech", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x1849, 0x0888, "775Dual-VSTA", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x8086, 0x2503, "DG965OT AAD63733-203", POS_FIX_LPIB),
- SND_PCI_QUIRK(0x8086, 0xd601, "eMachines T5212", POS_FIX_LPIB),
- {}
-};
-
-static int __devinit check_position_fix(struct azx *chip, int fix)
-{
- const struct snd_pci_quirk *q;
-
- switch (fix) {
- case POS_FIX_LPIB:
- case POS_FIX_POSBUF:
- case POS_FIX_VIACOMBO:
- return fix;
- }
-
- q = snd_pci_quirk_lookup(chip->pci, position_fix_list);
- if (q) {
- printk(KERN_INFO
- "hda_intel: position_fix set to %d "
- "for device %04x:%04x\n",
- q->value, q->subvendor, q->subdevice);
- return q->value;
- }
-
- /* Check VIA/ATI HD Audio Controller exist */
- switch (chip->driver_type) {
- case AZX_DRIVER_VIA:
- case AZX_DRIVER_ATI:
- /* Use link position directly, avoid any transfer problem. */
- return POS_FIX_VIACOMBO;
- }
-
- return POS_FIX_AUTO;
-}
-
-/*
- * black-lists for probe_mask
- */
-static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
- /* Thinkpad often breaks the controller communication when accessing
- * to the non-working (or non-existing) modem codec slot.
- */
- SND_PCI_QUIRK(0x1014, 0x05b7, "Thinkpad Z60", 0x01),
- SND_PCI_QUIRK(0x17aa, 0x2010, "Thinkpad X/T/R60", 0x01),
- SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X/T/R61", 0x01),
- /* broken BIOS */
- SND_PCI_QUIRK(0x1028, 0x20ac, "Dell Studio Desktop", 0x01),
- /* including bogus ALC268 in slot#2 that conflicts with ALC888 */
- SND_PCI_QUIRK(0x17c0, 0x4085, "Medion MD96630", 0x01),
- /* forced codec slots */
- SND_PCI_QUIRK(0x1043, 0x1262, "ASUS W5Fm", 0x103),
- SND_PCI_QUIRK(0x1046, 0x1262, "ASUS W5F", 0x103),
- {}
-};
-
-#define AZX_FORCE_CODEC_MASK 0x100
-
-static void __devinit check_probe_mask(struct azx *chip, int dev)
-{
- const struct snd_pci_quirk *q;
-
- chip->codec_probe_mask = probe_mask[dev];
- if (chip->codec_probe_mask == -1) {
- q = snd_pci_quirk_lookup(chip->pci, probe_mask_list);
- if (q) {
- printk(KERN_INFO
- "hda_intel: probe_mask set to 0x%x "
- "for device %04x:%04x\n",
- q->value, q->subvendor, q->subdevice);
- chip->codec_probe_mask = q->value;
- }
- }
-
- /* check forced option */
- if (chip->codec_probe_mask != -1 &&
- (chip->codec_probe_mask & AZX_FORCE_CODEC_MASK)) {
- chip->codec_mask = chip->codec_probe_mask & 0xff;
- printk(KERN_INFO "hda_intel: codec_mask forced to 0x%x\n",
- chip->codec_mask);
- }
-}
-
-/*
- * white/black-list for enable_msi
- */
-static struct snd_pci_quirk msi_black_list[] __devinitdata = {
- SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */
- SND_PCI_QUIRK(0x1043, 0x81f6, "ASUS", 0), /* nvidia */
- SND_PCI_QUIRK(0x1043, 0x822d, "ASUS", 0), /* Athlon64 X2 + nvidia MCP55 */
- SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */
- SND_PCI_QUIRK(0xa0a0, 0x0575, "Aopen MZ915-M", 0), /* ICH6 */
- {}
-};
-
-static void __devinit check_msi(struct azx *chip)
-{
- const struct snd_pci_quirk *q;
-
- if (enable_msi >= 0) {
- chip->msi = !!enable_msi;
- return;
- }
- chip->msi = 1; /* enable MSI as default */
- q = snd_pci_quirk_lookup(chip->pci, msi_black_list);
- if (q) {
- printk(KERN_INFO
- "hda_intel: msi for device %04x:%04x set to %d\n",
- q->subvendor, q->subdevice, q->value);
- chip->msi = q->value;
- return;
- }
-
- /* NVidia chipsets seem to cause troubles with MSI */
- if (chip->driver_type == AZX_DRIVER_NVIDIA) {
- printk(KERN_INFO "hda_intel: Disable MSI for Nvidia chipset\n");
- chip->msi = 0;
- }
-}
-
-
-/*
- * constructor
- */
-static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
- int dev, int driver_type,
- struct azx **rchip)
-{
- struct azx *chip;
- int i, err;
- unsigned short gcap;
- static struct snd_device_ops ops = {
- .dev_free = azx_dev_free,
- };
-
- *rchip = NULL;
-
- err = pci_enable_device(pci);
- if (err < 0)
- return err;
-
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (!chip) {
- snd_printk(KERN_ERR SFX "cannot allocate chip\n");
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
- spin_lock_init(&chip->reg_lock);
- mutex_init(&chip->open_mutex);
- chip->card = card;
- chip->pci = pci;
- chip->irq = -1;
- chip->driver_type = driver_type;
- check_msi(chip);
- chip->dev_index = dev;
- INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work);
-
- chip->position_fix[0] = chip->position_fix[1] =
- check_position_fix(chip, position_fix[dev]);
- check_probe_mask(chip, dev);
-
- chip->single_cmd = single_cmd;
-
- if (bdl_pos_adj[dev] < 0) {
- switch (chip->driver_type) {
- case AZX_DRIVER_ICH:
- case AZX_DRIVER_PCH:
- bdl_pos_adj[dev] = 1;
- break;
- default:
- bdl_pos_adj[dev] = 32;
- break;
- }
- }
-
-#if BITS_PER_LONG != 64
- /* Fix up base address on ULI M5461 */
- if (chip->driver_type == AZX_DRIVER_ULI) {
- u16 tmp3;
- pci_read_config_word(pci, 0x40, &tmp3);
- pci_write_config_word(pci, 0x40, tmp3 | 0x10);
- pci_write_config_dword(pci, PCI_BASE_ADDRESS_1, 0);
- }
-#endif
-
- err = pci_request_regions(pci, "ICH HD audio");
- if (err < 0) {
- kfree(chip);
- pci_disable_device(pci);
- return err;
- }
-
- chip->addr = pci_resource_start(pci, 0);
- chip->remap_addr = pci_ioremap_bar(pci, 0);
- if (chip->remap_addr == NULL) {
- snd_printk(KERN_ERR SFX "ioremap error\n");
- err = -ENXIO;
- goto errout;
- }
-
- if (chip->msi)
- if (pci_enable_msi(pci) < 0)
- chip->msi = 0;
-
- if (azx_acquire_irq(chip, 0) < 0) {
- err = -EBUSY;
- goto errout;
- }
-
- pci_set_master(pci);
- synchronize_irq(chip->irq);
-
- gcap = azx_readw(chip, GCAP);
- snd_printdd(SFX "chipset global capabilities = 0x%x\n", gcap);
-
- /* disable SB600 64bit support for safety */
- if ((chip->driver_type == AZX_DRIVER_ATI) ||
- (chip->driver_type == AZX_DRIVER_ATIHDMI)) {
- struct pci_dev *p_smbus;
- p_smbus = pci_get_device(PCI_VENDOR_ID_ATI,
- PCI_DEVICE_ID_ATI_SBX00_SMBUS,
- NULL);
- if (p_smbus) {
- if (p_smbus->revision < 0x30)
- gcap &= ~ICH6_GCAP_64OK;
- pci_dev_put(p_smbus);
- }
- }
-
- /* disable 64bit DMA address for Teradici */
- /* it does not work with device 6549:1200 subsys e4a2:040b */
- if (chip->driver_type == AZX_DRIVER_TERA)
- gcap &= ~ICH6_GCAP_64OK;
-
- /* allow 64bit DMA address if supported by H/W */
- if ((gcap & ICH6_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64));
- else {
- pci_set_dma_mask(pci, DMA_BIT_MASK(32));
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32));
- }
-
- /* read number of streams from GCAP register instead of using
- * hardcoded value
- */
- chip->capture_streams = (gcap >> 8) & 0x0f;
- chip->playback_streams = (gcap >> 12) & 0x0f;
- if (!chip->playback_streams && !chip->capture_streams) {
- /* gcap didn't give any info, switching to old method */
-
- switch (chip->driver_type) {
- case AZX_DRIVER_ULI:
- chip->playback_streams = ULI_NUM_PLAYBACK;
- chip->capture_streams = ULI_NUM_CAPTURE;
- break;
- case AZX_DRIVER_ATIHDMI:
- chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
- chip->capture_streams = ATIHDMI_NUM_CAPTURE;
- break;
- case AZX_DRIVER_GENERIC:
- default:
- chip->playback_streams = ICH6_NUM_PLAYBACK;
- chip->capture_streams = ICH6_NUM_CAPTURE;
- break;
- }
- }
- chip->capture_index_offset = 0;
- chip->playback_index_offset = chip->capture_streams;
- chip->num_streams = chip->playback_streams + chip->capture_streams;
- chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev),
- GFP_KERNEL);
- if (!chip->azx_dev) {
- snd_printk(KERN_ERR SFX "cannot malloc azx_dev\n");
- goto errout;
- }
-
- for (i = 0; i < chip->num_streams; i++) {
- /* allocate memory for the BDL for each stream */
- err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- BDL_SIZE, &chip->azx_dev[i].bdl);
- if (err < 0) {
- snd_printk(KERN_ERR SFX "cannot allocate BDL\n");
- goto errout;
- }
- }
- /* allocate memory for the position buffer */
- err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- chip->num_streams * 8, &chip->posbuf);
- if (err < 0) {
- snd_printk(KERN_ERR SFX "cannot allocate posbuf\n");
- goto errout;
- }
- /* allocate CORB/RIRB */
- err = azx_alloc_cmd_io(chip);
- if (err < 0)
- goto errout;
-
- /* initialize streams */
- azx_init_stream(chip);
-
- /* initialize chip */
- azx_init_pci(chip);
- azx_init_chip(chip, (probe_only[dev] & 2) == 0);
-
- /* codec detection */
- if (!chip->codec_mask) {
- snd_printk(KERN_ERR SFX "no codecs found!\n");
- err = -ENODEV;
- goto errout;
- }
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err <0) {
- snd_printk(KERN_ERR SFX "Error creating device [card]!\n");
- goto errout;
- }
-
- strcpy(card->driver, "HDA-Intel");
- strlcpy(card->shortname, driver_short_names[chip->driver_type],
- sizeof(card->shortname));
- snprintf(card->longname, sizeof(card->longname),
- "%s at 0x%lx irq %i",
- card->shortname, chip->addr, chip->irq);
-
- *rchip = chip;
- return 0;
-
- errout:
- azx_free(chip);
- return err;
-}
-
-static void power_down_all_codecs(struct azx *chip)
-{
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- /* The codecs were powered up in snd_hda_codec_new().
- * Now all initialization done, so turn them down if possible
- */
- struct hda_codec *codec;
- list_for_each_entry(codec, &chip->bus->codec_list, list) {
- snd_hda_power_down(codec);
- }
-#endif
-}
-
-static int __devinit azx_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
-{
- static int dev;
- struct snd_card *card;
- struct azx *chip;
- int err;
-
- if (dev >= SNDRV_CARDS)
- return -ENODEV;
- if (!enable[dev]) {
- dev++;
- return -ENOENT;
- }
-
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
- if (err < 0) {
- snd_printk(KERN_ERR SFX "Error creating card!\n");
- return err;
- }
-
- /* set this here since it's referred in snd_hda_load_patch() */
- snd_card_set_dev(card, &pci->dev);
-
- err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
- if (err < 0)
- goto out_free;
- card->private_data = chip;
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
- chip->beep_mode = beep_mode[dev];
-#endif
-
- /* create codec instances */
- err = azx_codec_create(chip, model[dev]);
- if (err < 0)
- goto out_free;
-#ifdef CONFIG_SND_HDA_PATCH_LOADER
- if (patch[dev]) {
- snd_printk(KERN_ERR SFX "Applying patch firmware '%s'\n",
- patch[dev]);
- err = snd_hda_load_patch(chip->bus, patch[dev]);
- if (err < 0)
- goto out_free;
- }
-#endif
- if ((probe_only[dev] & 1) == 0) {
- err = azx_codec_configure(chip);
- if (err < 0)
- goto out_free;
- }
-
- /* create PCM streams */
- err = snd_hda_build_pcms(chip->bus);
- if (err < 0)
- goto out_free;
-
- /* create mixer controls */
- err = azx_mixer_create(chip);
- if (err < 0)
- goto out_free;
-
- err = snd_card_register(card);
- if (err < 0)
- goto out_free;
-
- pci_set_drvdata(pci, card);
- chip->running = 1;
- power_down_all_codecs(chip);
- azx_notifier_register(chip);
-
- dev++;
- return err;
-out_free:
- snd_card_free(card);
- return err;
-}
-
-static void __devexit azx_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
-}
-
-/* PCI IDs */
-static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
- /* CPT */
- { PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_PCH },
- /* PBG */
- { PCI_DEVICE(0x8086, 0x1d20), .driver_data = AZX_DRIVER_PCH },
- /* SCH */
- { PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH },
- /* Generic Intel */
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID),
- .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
- .class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_ICH },
- /* ATI SB 450/600 */
- { PCI_DEVICE(0x1002, 0x437b), .driver_data = AZX_DRIVER_ATI },
- { PCI_DEVICE(0x1002, 0x4383), .driver_data = AZX_DRIVER_ATI },
- /* ATI HDMI */
- { PCI_DEVICE(0x1002, 0x793b), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0x7919), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0x960f), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0x970f), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa00), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa08), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa10), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa18), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa20), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa28), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa30), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa38), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa40), .driver_data = AZX_DRIVER_ATIHDMI },
- { PCI_DEVICE(0x1002, 0xaa48), .driver_data = AZX_DRIVER_ATIHDMI },
- /* VIA VT8251/VT8237A */
- { PCI_DEVICE(0x1106, 0x3288), .driver_data = AZX_DRIVER_VIA },
- /* SIS966 */
- { PCI_DEVICE(0x1039, 0x7502), .driver_data = AZX_DRIVER_SIS },
- /* ULI M5461 */
- { PCI_DEVICE(0x10b9, 0x5461), .driver_data = AZX_DRIVER_ULI },
- /* NVIDIA MCP */
- { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),
- .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
- .class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_NVIDIA },
- /* Teradici */
- { PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
- /* Creative X-Fi (CA0110-IBG) */
-#if !defined(CONFIG_SND_CTXFI) && !defined(CONFIG_SND_CTXFI_MODULE)
- /* the following entry conflicts with snd-ctxfi driver,
- * as ctxfi driver mutates from HD-audio to native mode with
- * a special command sequence.
- */
- { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID),
- .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
- .class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_CTX },
-#else
- /* this entry seems still valid -- i.e. without emu20kx chip */
- { PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_CTX },
-#endif
- /* Vortex86MX */
- { PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC },
- /* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */
- { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
- .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
- .class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_GENERIC },
- { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_ANY_ID),
- .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
- .class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_GENERIC },
- { 0, }
-};
-MODULE_DEVICE_TABLE(pci, azx_ids);
-
-/* pci_driver definition */
-static struct pci_driver driver = {
- .name = "HDA Intel",
- .id_table = azx_ids,
- .probe = azx_probe,
- .remove = __devexit_p(azx_remove),
-#ifdef CONFIG_PM
- .suspend = azx_suspend,
- .resume = azx_resume,
-#endif
-};
-
-static int __init alsa_card_azx_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_azx_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_azx_init)
-module_exit(alsa_card_azx_exit)
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
deleted file mode 100644
index f7ff3f7ccb8e..000000000000
--- a/sound/pci/hda/patch_analog.c
+++ /dev/null
@@ -1,4820 +0,0 @@
-/*
- * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984,
- * AD1986A, AD1988
- *
- * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de>
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
-
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-#include "hda_beep.h"
-
-struct ad198x_spec {
- struct snd_kcontrol_new *mixers[5];
- int num_mixers;
- unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
- const struct hda_verb *init_verbs[5]; /* initialization verbs
- * don't forget NULL termination!
- */
- unsigned int num_init_verbs;
-
- /* playback */
- struct hda_multi_out multiout; /* playback set-up
- * max_channels, dacs must be set
- * dig_out_nid and hp_nid are optional
- */
- unsigned int cur_eapd;
- unsigned int need_dac_fix;
-
- /* capture */
- unsigned int num_adc_nids;
- hda_nid_t *adc_nids;
- hda_nid_t dig_in_nid; /* digital-in NID; optional */
-
- /* capture source */
- const struct hda_input_mux *input_mux;
- hda_nid_t *capsrc_nids;
- unsigned int cur_mux[3];
-
- /* channel model */
- const struct hda_channel_mode *channel_mode;
- int num_channel_mode;
-
- /* PCM information */
- struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
-
- unsigned int spdif_route;
-
- /* dynamic controls, init_verbs and input_mux */
- struct auto_pin_cfg autocfg;
- struct snd_array kctls;
- struct hda_input_mux private_imux;
- hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
-
- unsigned int jack_present: 1;
- unsigned int inv_jack_detect: 1;/* inverted jack-detection */
- unsigned int inv_eapd: 1; /* inverted EAPD implementation */
- unsigned int analog_beep: 1; /* analog beep input present */
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- struct hda_loopback_check loopback;
-#endif
- /* for virtual master */
- hda_nid_t vmaster_nid;
- const char **slave_vols;
- const char **slave_sws;
-};
-
-/*
- * input MUX handling (common part)
- */
-static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
-
- return snd_hda_input_mux_info(spec->input_mux, uinfo);
-}
-
-static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
- return 0;
-}
-
-static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
- spec->capsrc_nids[adc_idx],
- &spec->cur_mux[adc_idx]);
-}
-
-/*
- * initialization (common callbacks)
- */
-static int ad198x_init(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_init_verbs; i++)
- snd_hda_sequence_write(codec, spec->init_verbs[i]);
- return 0;
-}
-
-static const char *ad_slave_vols[] = {
- "Front Playback Volume",
- "Surround Playback Volume",
- "Center Playback Volume",
- "LFE Playback Volume",
- "Side Playback Volume",
- "Headphone Playback Volume",
- "Mono Playback Volume",
- "Speaker Playback Volume",
- "IEC958 Playback Volume",
- NULL
-};
-
-static const char *ad_slave_sws[] = {
- "Front Playback Switch",
- "Surround Playback Switch",
- "Center Playback Switch",
- "LFE Playback Switch",
- "Side Playback Switch",
- "Headphone Playback Switch",
- "Mono Playback Switch",
- "Speaker Playback Switch",
- "IEC958 Playback Switch",
- NULL
-};
-
-static void ad198x_free_kctls(struct hda_codec *codec);
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-/* additional beep mixers; the actual parameters are overwritten at build */
-static struct snd_kcontrol_new ad_beep_mixer[] = {
- HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad_beep2_mixer[] = {
- HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT),
- { } /* end */
-};
-
-#define set_beep_amp(spec, nid, idx, dir) \
- ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
-#else
-#define set_beep_amp(spec, nid, idx, dir) /* NOP */
-#endif
-
-static int ad198x_build_controls(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- struct snd_kcontrol *kctl;
- unsigned int i;
- int err;
-
- for (i = 0; i < spec->num_mixers; i++) {
- err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
- if (err < 0)
- return err;
- }
- if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- if (spec->dig_in_nid) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
- if (err < 0)
- return err;
- }
-
- /* create beep controls if needed */
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
- if (spec->beep_amp) {
- struct snd_kcontrol_new *knew;
- knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer;
- for ( ; knew->name; knew++) {
- struct snd_kcontrol *kctl;
- kctl = snd_ctl_new1(knew, codec);
- if (!kctl)
- return -ENOMEM;
- kctl->private_value = spec->beep_amp;
- err = snd_hda_ctl_add(codec, 0, kctl);
- if (err < 0)
- return err;
- }
- }
-#endif
-
- /* if we have no master control, let's create it */
- if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
- unsigned int vmaster_tlv[4];
- snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
- HDA_OUTPUT, vmaster_tlv);
- err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- vmaster_tlv,
- (spec->slave_vols ?
- spec->slave_vols : ad_slave_vols));
- if (err < 0)
- return err;
- }
- if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
- err = snd_hda_add_vmaster(codec, "Master Playback Switch",
- NULL,
- (spec->slave_sws ?
- spec->slave_sws : ad_slave_sws));
- if (err < 0)
- return err;
- }
-
- ad198x_free_kctls(codec); /* no longer needed */
-
- /* assign Capture Source enums to NID */
- kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
- if (!kctl)
- kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
- for (i = 0; kctl && i < kctl->count; i++) {
- err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]);
- if (err < 0)
- return err;
- }
-
- /* assign IEC958 enums to NID */
- kctl = snd_hda_find_mixer_ctl(codec,
- SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
- if (kctl) {
- err = snd_hda_add_nid(codec, kctl, 0,
- spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
-}
-#endif
-
-/*
- * Analog playback callbacks
- */
-static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Digital out
- */
-static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Analog capture
- */
-static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
- stream_tag, 0, format);
- return 0;
-}
-
-static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ad198x_spec *spec = codec->spec;
- snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
- return 0;
-}
-
-
-/*
- */
-static struct hda_pcm_stream ad198x_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 6, /* changed later */
- .nid = 0, /* fill later */
- .ops = {
- .open = ad198x_playback_pcm_open,
- .prepare = ad198x_playback_pcm_prepare,
- .cleanup = ad198x_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream ad198x_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .prepare = ad198x_capture_pcm_prepare,
- .cleanup = ad198x_capture_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream ad198x_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .open = ad198x_dig_playback_pcm_open,
- .close = ad198x_dig_playback_pcm_close,
- .prepare = ad198x_dig_playback_pcm_prepare,
- .cleanup = ad198x_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream ad198x_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
-};
-
-static int ad198x_build_pcms(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = "AD198x Analog";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
-
- if (spec->multiout.dig_out_nid) {
- info++;
- codec->num_pcms++;
- info->name = "AD198x Digital";
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
- if (spec->dig_in_nid) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
- }
- }
-
- return 0;
-}
-
-static inline void ad198x_shutup(struct hda_codec *codec)
-{
- snd_hda_shutup_pins(codec);
-}
-
-static void ad198x_free_kctls(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
-
- if (spec->kctls.list) {
- struct snd_kcontrol_new *kctl = spec->kctls.list;
- int i;
- for (i = 0; i < spec->kctls.used; i++)
- kfree(kctl[i].name);
- }
- snd_array_free(&spec->kctls);
-}
-
-static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
- hda_nid_t hp)
-{
- struct ad198x_spec *spec = codec->spec;
- snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
- !spec->inv_eapd ? 0x00 : 0x02);
- snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
- !spec->inv_eapd ? 0x00 : 0x02);
-}
-
-static void ad198x_power_eapd(struct hda_codec *codec)
-{
- /* We currently only handle front, HP */
- switch (codec->vendor_id) {
- case 0x11d41882:
- case 0x11d4882a:
- case 0x11d41884:
- case 0x11d41984:
- case 0x11d41883:
- case 0x11d4184a:
- case 0x11d4194a:
- case 0x11d4194b:
- ad198x_power_eapd_write(codec, 0x12, 0x11);
- break;
- case 0x11d41981:
- case 0x11d41983:
- ad198x_power_eapd_write(codec, 0x05, 0x06);
- break;
- case 0x11d41986:
- ad198x_power_eapd_write(codec, 0x1b, 0x1a);
- break;
- case 0x11d41988:
- case 0x11d4198b:
- case 0x11d4989a:
- case 0x11d4989b:
- ad198x_power_eapd_write(codec, 0x29, 0x22);
- break;
- }
-}
-
-static void ad198x_free(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
-
- if (!spec)
- return;
-
- ad198x_shutup(codec);
- ad198x_free_kctls(codec);
- kfree(spec);
- snd_hda_detach_beep_device(codec);
-}
-
-#ifdef SND_HDA_NEEDS_RESUME
-static int ad198x_suspend(struct hda_codec *codec, pm_message_t state)
-{
- ad198x_shutup(codec);
- ad198x_power_eapd(codec);
- return 0;
-}
-#endif
-
-static struct hda_codec_ops ad198x_patch_ops = {
- .build_controls = ad198x_build_controls,
- .build_pcms = ad198x_build_pcms,
- .init = ad198x_init,
- .free = ad198x_free,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .check_power_status = ad198x_check_power_status,
-#endif
-#ifdef SND_HDA_NEEDS_RESUME
- .suspend = ad198x_suspend,
-#endif
- .reboot_notify = ad198x_shutup,
-};
-
-
-/*
- * EAPD control
- * the private value = nid
- */
-#define ad198x_eapd_info snd_ctl_boolean_mono_info
-
-static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- if (spec->inv_eapd)
- ucontrol->value.integer.value[0] = ! spec->cur_eapd;
- else
- ucontrol->value.integer.value[0] = spec->cur_eapd;
- return 0;
-}
-
-static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- hda_nid_t nid = kcontrol->private_value & 0xff;
- unsigned int eapd;
- eapd = !!ucontrol->value.integer.value[0];
- if (spec->inv_eapd)
- eapd = !eapd;
- if (eapd == spec->cur_eapd)
- return 0;
- spec->cur_eapd = eapd;
- snd_hda_codec_write_cache(codec, nid,
- 0, AC_VERB_SET_EAPD_BTLENABLE,
- eapd ? 0x02 : 0x00);
- return 1;
-}
-
-static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo);
-static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-
-
-/*
- * AD1986A specific
- */
-
-#define AD1986A_SPDIF_OUT 0x02
-#define AD1986A_FRONT_DAC 0x03
-#define AD1986A_SURR_DAC 0x04
-#define AD1986A_CLFE_DAC 0x05
-#define AD1986A_ADC 0x06
-
-static hda_nid_t ad1986a_dac_nids[3] = {
- AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
-};
-static hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
-static hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
-
-static struct hda_input_mux ad1986a_capture_source = {
- .num_items = 7,
- .items = {
- { "Mic", 0x0 },
- { "CD", 0x1 },
- { "Aux", 0x3 },
- { "Line", 0x4 },
- { "Mix", 0x5 },
- { "Mono", 0x6 },
- { "Phone", 0x7 },
- },
-};
-
-
-static struct hda_bind_ctls ad1986a_bind_pcm_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct hda_bind_ctls ad1986a_bind_pcm_sw = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-/*
- * mixers
- */
-static struct snd_kcontrol_new ad1986a_mixers[] = {
- /*
- * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
- */
- HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
- HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
- HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-/* additional mixers for 3stack mode */
-static struct snd_kcontrol_new ad1986a_3st_mixers[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = ad198x_ch_mode_info,
- .get = ad198x_ch_mode_get,
- .put = ad198x_ch_mode_put,
- },
- { } /* end */
-};
-
-/* laptop model - 2ch only */
-static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
-
-/* master controls both pins 0x1a and 0x1b */
-static struct hda_bind_ctls ad1986a_laptop_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
- 0,
- },
-};
-
-static struct hda_bind_ctls ad1986a_laptop_master_sw = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
- 0,
- },
-};
-
-static struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
- HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
- /*
- HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
-};
-
-/* laptop-eapd model - 2ch only */
-
-static struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x4 },
- { "Mix", 0x5 },
- },
-};
-
-static struct hda_input_mux ad1986a_automic_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- { "Mix", 0x5 },
- },
-};
-
-static struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
- HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
- HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "External Amplifier",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
- .info = ad198x_eapd_info,
- .get = ad198x_eapd_get,
- .put = ad198x_eapd_put,
- .private_value = 0x1b, /* port-D */
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
- { } /* end */
-};
-
-/* re-connect the mic boost input according to the jack sensing */
-static void ad1986a_automic(struct hda_codec *codec)
-{
- unsigned int present;
- present = snd_hda_jack_detect(codec, 0x1f);
- /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
- snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
- present ? 0 : 2);
-}
-
-#define AD1986A_MIC_EVENT 0x36
-
-static void ad1986a_automic_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) != AD1986A_MIC_EVENT)
- return;
- ad1986a_automic(codec);
-}
-
-static int ad1986a_automic_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1986a_automic(codec);
- return 0;
-}
-
-/* laptop-automute - 2ch only */
-
-static void ad1986a_update_hp(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- unsigned int mute;
-
- if (spec->jack_present)
- mute = HDA_AMP_MUTE; /* mute internal speaker */
- else
- /* unmute internal speaker if necessary */
- mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
- snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
-}
-
-static void ad1986a_hp_automute(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
-
- spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
- if (spec->inv_jack_detect)
- spec->jack_present = !spec->jack_present;
- ad1986a_update_hp(codec);
-}
-
-#define AD1986A_HP_EVENT 0x37
-
-static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) != AD1986A_HP_EVENT)
- return;
- ad1986a_hp_automute(codec);
-}
-
-static int ad1986a_hp_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1986a_hp_automute(codec);
- return 0;
-}
-
-/* bind hp and internal speaker mute (with plug check) */
-static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int change;
-
- change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
- HDA_AMP_MUTE,
- valp[0] ? 0 : HDA_AMP_MUTE);
- change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
- HDA_AMP_MUTE,
- valp[1] ? 0 : HDA_AMP_MUTE);
- if (change)
- ad1986a_update_hp(codec);
- return change;
-}
-
-static struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
- HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = ad1986a_hp_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
- },
- { } /* end */
-};
-
-
-/*
- * initialization verbs
- */
-static struct hda_verb ad1986a_init_verbs[] = {
- /* Front, Surround, CLFE DAC; mute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Downmix - off */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* HP, Line-Out, Surround, CLFE selectors */
- {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mono selector */
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mic selector: Mic 1/2 pin */
- {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Line-in selector: Line-in */
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mic 1/2 swap */
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Record selector: mic */
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* PC beep */
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* HP Pin */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
- /* Front, Surround, CLFE Pins */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* Mono Pin */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* Mic Pin */
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* Line, Aux, CD, Beep-In Pin */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- { } /* end */
-};
-
-static struct hda_verb ad1986a_ch2_init[] = {
- /* Surround out -> Line In */
- { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- /* Line-in selectors */
- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
- /* CLFE -> Mic in */
- { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
- { } /* end */
-};
-
-static struct hda_verb ad1986a_ch4_init[] = {
- /* Surround out -> Surround */
- { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
- /* CLFE -> Mic in */
- { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
- { } /* end */
-};
-
-static struct hda_verb ad1986a_ch6_init[] = {
- /* Surround out -> Surround out */
- { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
- /* CLFE -> CLFE */
- { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
- { } /* end */
-};
-
-static struct hda_channel_mode ad1986a_modes[3] = {
- { 2, ad1986a_ch2_init },
- { 4, ad1986a_ch4_init },
- { 6, ad1986a_ch6_init },
-};
-
-/* eapd initialization */
-static struct hda_verb ad1986a_eapd_init_verbs[] = {
- {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
- {}
-};
-
-static struct hda_verb ad1986a_automic_verbs[] = {
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
- {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
- {}
-};
-
-/* Ultra initialization */
-static struct hda_verb ad1986a_ultra_init[] = {
- /* eapd initialization */
- { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
- /* CLFE -> Mic in */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
- { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
- { } /* end */
-};
-
-/* pin sensing on HP jack */
-static struct hda_verb ad1986a_hp_init_verbs[] = {
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
- {}
-};
-
-static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case AD1986A_HP_EVENT:
- ad1986a_hp_automute(codec);
- break;
- case AD1986A_MIC_EVENT:
- ad1986a_automic(codec);
- break;
- }
-}
-
-static int ad1986a_samsung_p50_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1986a_hp_automute(codec);
- ad1986a_automic(codec);
- return 0;
-}
-
-
-/* models */
-enum {
- AD1986A_6STACK,
- AD1986A_3STACK,
- AD1986A_LAPTOP,
- AD1986A_LAPTOP_EAPD,
- AD1986A_LAPTOP_AUTOMUTE,
- AD1986A_ULTRA,
- AD1986A_SAMSUNG,
- AD1986A_SAMSUNG_P50,
- AD1986A_MODELS
-};
-
-static const char *ad1986a_models[AD1986A_MODELS] = {
- [AD1986A_6STACK] = "6stack",
- [AD1986A_3STACK] = "3stack",
- [AD1986A_LAPTOP] = "laptop",
- [AD1986A_LAPTOP_EAPD] = "laptop-eapd",
- [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
- [AD1986A_ULTRA] = "ultra",
- [AD1986A_SAMSUNG] = "samsung",
- [AD1986A_SAMSUNG_P50] = "samsung-p50",
-};
-
-static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
- SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
- SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK),
- SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
- SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
- SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
- SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
- SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
- SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
- SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
- SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
- SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
- SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
- {}
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1986a_loopbacks[] = {
- { 0x13, HDA_OUTPUT, 0 }, /* Mic */
- { 0x14, HDA_OUTPUT, 0 }, /* Phone */
- { 0x15, HDA_OUTPUT, 0 }, /* CD */
- { 0x16, HDA_OUTPUT, 0 }, /* Aux */
- { 0x17, HDA_OUTPUT, 0 }, /* Line */
- { } /* end */
-};
-#endif
-
-static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
- return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
-}
-
-static int patch_ad1986a(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- err = snd_hda_attach_beep_device(codec, 0x19);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
- set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
-
- spec->multiout.max_channels = 6;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
- spec->multiout.dac_nids = ad1986a_dac_nids;
- spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
- spec->num_adc_nids = 1;
- spec->adc_nids = ad1986a_adc_nids;
- spec->capsrc_nids = ad1986a_capsrc_nids;
- spec->input_mux = &ad1986a_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1986a_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1986a_init_verbs;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1986a_loopbacks;
-#endif
- spec->vmaster_nid = 0x1b;
- spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
-
- codec->patch_ops = ad198x_patch_ops;
-
- /* override some parameters */
- board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
- ad1986a_models,
- ad1986a_cfg_tbl);
- switch (board_config) {
- case AD1986A_3STACK:
- spec->num_mixers = 2;
- spec->mixers[1] = ad1986a_3st_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1986a_ch2_init;
- spec->channel_mode = ad1986a_modes;
- spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
- spec->need_dac_fix = 1;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- break;
- case AD1986A_LAPTOP:
- spec->mixers[0] = ad1986a_laptop_mixers;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- break;
- case AD1986A_LAPTOP_EAPD:
- spec->num_mixers = 3;
- spec->mixers[0] = ad1986a_laptop_master_mixers;
- spec->mixers[1] = ad1986a_laptop_eapd_mixers;
- spec->mixers[2] = ad1986a_laptop_intmic_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1986a_eapd_init_verbs;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- if (!is_jack_available(codec, 0x25))
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1986a_laptop_eapd_capture_source;
- break;
- case AD1986A_SAMSUNG:
- spec->num_mixers = 2;
- spec->mixers[0] = ad1986a_laptop_master_mixers;
- spec->mixers[1] = ad1986a_laptop_eapd_mixers;
- spec->num_init_verbs = 3;
- spec->init_verbs[1] = ad1986a_eapd_init_verbs;
- spec->init_verbs[2] = ad1986a_automic_verbs;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- if (!is_jack_available(codec, 0x25))
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1986a_automic_capture_source;
- codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
- codec->patch_ops.init = ad1986a_automic_init;
- break;
- case AD1986A_SAMSUNG_P50:
- spec->num_mixers = 2;
- spec->mixers[0] = ad1986a_automute_master_mixers;
- spec->mixers[1] = ad1986a_laptop_eapd_mixers;
- spec->num_init_verbs = 4;
- spec->init_verbs[1] = ad1986a_eapd_init_verbs;
- spec->init_verbs[2] = ad1986a_automic_verbs;
- spec->init_verbs[3] = ad1986a_hp_init_verbs;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- if (!is_jack_available(codec, 0x25))
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1986a_automic_capture_source;
- codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
- codec->patch_ops.init = ad1986a_samsung_p50_init;
- break;
- case AD1986A_LAPTOP_AUTOMUTE:
- spec->num_mixers = 3;
- spec->mixers[0] = ad1986a_automute_master_mixers;
- spec->mixers[1] = ad1986a_laptop_eapd_mixers;
- spec->mixers[2] = ad1986a_laptop_intmic_mixers;
- spec->num_init_verbs = 3;
- spec->init_verbs[1] = ad1986a_eapd_init_verbs;
- spec->init_verbs[2] = ad1986a_hp_init_verbs;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- if (!is_jack_available(codec, 0x25))
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1986a_laptop_eapd_capture_source;
- codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
- codec->patch_ops.init = ad1986a_hp_init;
- /* Lenovo N100 seems to report the reversed bit
- * for HP jack-sensing
- */
- spec->inv_jack_detect = 1;
- break;
- case AD1986A_ULTRA:
- spec->mixers[0] = ad1986a_laptop_eapd_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1986a_ultra_init;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- spec->multiout.dig_out_nid = 0;
- break;
- }
-
- /* AD1986A has a hardware problem that it can't share a stream
- * with multiple output pins. The copy of front to surrounds
- * causes noisy or silent outputs at a certain timing, e.g.
- * changing the volume.
- * So, let's disable the shared stream.
- */
- spec->multiout.no_share_stream = 1;
-
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
-
- return 0;
-}
-
-/*
- * AD1983 specific
- */
-
-#define AD1983_SPDIF_OUT 0x02
-#define AD1983_DAC 0x03
-#define AD1983_ADC 0x04
-
-static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
-static hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
-static hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
-
-static struct hda_input_mux ad1983_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x1 },
- { "Mix", 0x2 },
- { "Mix Mono", 0x3 },
- },
-};
-
-/*
- * SPDIF playback route
- */
-static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[] = { "PCM", "ADC" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item > 1)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
-
-static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
-
- ucontrol->value.enumerated.item[0] = spec->spdif_route;
- return 0;
-}
-
-static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
-
- if (ucontrol->value.enumerated.item[0] > 1)
- return -EINVAL;
- if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
- spec->spdif_route = ucontrol->value.enumerated.item[0];
- snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
- AC_VERB_SET_CONNECT_SEL,
- spec->spdif_route);
- return 1;
- }
- return 0;
-}
-
-static struct snd_kcontrol_new ad1983_mixers[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-static struct hda_verb ad1983_init_verbs[] = {
- /* Front, HP, Mono; mute as default */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Beep, PCM, Mic, Line-In: mute */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Front, HP selectors; from Mix */
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* Mono selector; from Mix */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
- /* Mic selector; Mic */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Line-in selector: Line-in */
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Mic boost: 0dB */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* Record selector: mic */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* SPDIF route: PCM */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Front Pin */
- {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* HP Pin */
- {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
- /* Mono Pin */
- {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* Mic Pin */
- {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* Line Pin */
- {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- { } /* end */
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1983_loopbacks[] = {
- { 0x12, HDA_OUTPUT, 0 }, /* Mic */
- { 0x13, HDA_OUTPUT, 0 }, /* Line */
- { } /* end */
-};
-#endif
-
-static int patch_ad1983(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
- spec->multiout.dac_nids = ad1983_dac_nids;
- spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
- spec->num_adc_nids = 1;
- spec->adc_nids = ad1983_adc_nids;
- spec->capsrc_nids = ad1983_capsrc_nids;
- spec->input_mux = &ad1983_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1983_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1983_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1983_loopbacks;
-#endif
- spec->vmaster_nid = 0x05;
-
- codec->patch_ops = ad198x_patch_ops;
-
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
-
- return 0;
-}
-
-
-/*
- * AD1981 HD specific
- */
-
-#define AD1981_SPDIF_OUT 0x02
-#define AD1981_DAC 0x03
-#define AD1981_ADC 0x04
-
-static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
-static hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
-static hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
-
-/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
-static struct hda_input_mux ad1981_capture_source = {
- .num_items = 7,
- .items = {
- { "Front Mic", 0x0 },
- { "Line", 0x1 },
- { "Mix", 0x2 },
- { "Mix Mono", 0x3 },
- { "CD", 0x4 },
- { "Mic", 0x6 },
- { "Aux", 0x7 },
- },
-};
-
-static struct snd_kcontrol_new ad1981_mixers[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* identical with AD1983 */
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-static struct hda_verb ad1981_init_verbs[] = {
- /* Front, HP, Mono; mute as default */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Front, HP selectors; from Mix */
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* Mono selector; from Mix */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
- /* Mic Mixer; select Front Mic */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* Mic boost: 0dB */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Record selector: Front mic */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- /* SPDIF route: PCM */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Front Pin */
- {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* HP Pin */
- {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
- /* Mono Pin */
- {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* Front & Rear Mic Pins */
- {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* Line Pin */
- {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* Digital Beep */
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Line-Out as Input: disabled */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- { } /* end */
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1981_loopbacks[] = {
- { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
- { 0x13, HDA_OUTPUT, 0 }, /* Line */
- { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
- { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
- { 0x1d, HDA_OUTPUT, 0 }, /* CD */
- { } /* end */
-};
-#endif
-
-/*
- * Patch for HP nx6320
- *
- * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
- * speaker output enabled _and_ mute-LED off.
- */
-
-#define AD1981_HP_EVENT 0x37
-#define AD1981_MIC_EVENT 0x38
-
-static struct hda_verb ad1981_hp_init_verbs[] = {
- {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
- /* pin sensing on HP and Mic jacks */
- {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
- {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
- {}
-};
-
-/* turn on/off EAPD (+ mute HP) as a master switch */
-static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
-
- if (! ad198x_eapd_put(kcontrol, ucontrol))
- return 0;
- /* change speaker pin appropriately */
- snd_hda_codec_write(codec, 0x05, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->cur_eapd ? PIN_OUT : 0);
- /* toggle HP mute appropriately */
- snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
- HDA_AMP_MUTE,
- spec->cur_eapd ? 0 : HDA_AMP_MUTE);
- return 1;
-}
-
-/* bind volumes of both NID 0x05 and 0x06 */
-static struct hda_bind_ctls ad1981_hp_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-/* mute internal speaker if HP is plugged */
-static void ad1981_hp_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x06);
- snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-/* toggle input of built-in and mic jack appropriately */
-static void ad1981_hp_automic(struct hda_codec *codec)
-{
- static struct hda_verb mic_jack_on[] = {
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- {}
- };
- static struct hda_verb mic_jack_off[] = {
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- {}
- };
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x08);
- if (present)
- snd_hda_sequence_write(codec, mic_jack_on);
- else
- snd_hda_sequence_write(codec, mic_jack_off);
-}
-
-/* unsolicited event for HP jack sensing */
-static void ad1981_hp_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- res >>= 26;
- switch (res) {
- case AD1981_HP_EVENT:
- ad1981_hp_automute(codec);
- break;
- case AD1981_MIC_EVENT:
- ad1981_hp_automic(codec);
- break;
- }
-}
-
-static struct hda_input_mux ad1981_hp_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Docking-Station", 0x1 },
- { "Mix", 0x2 },
- },
-};
-
-static struct snd_kcontrol_new ad1981_hp_mixers[] = {
- HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
- .name = "Master Playback Switch",
- .info = ad198x_eapd_info,
- .get = ad198x_eapd_get,
- .put = ad1981_hp_master_sw_put,
- .private_value = 0x05,
- },
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
-#if 0
- /* FIXME: analog mic/line loopback doesn't work with my tests...
- * (although recording is OK)
- */
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Docking-Station Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Docking-Station Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
- /* FIXME: does this laptop have analog CD connection? */
- HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
-#endif
- HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost", 0x18, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
-};
-
-/* initialize jack-sensing, too */
-static int ad1981_hp_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1981_hp_automute(codec);
- ad1981_hp_automic(codec);
- return 0;
-}
-
-/* configuration for Toshiba Laptops */
-static struct hda_verb ad1981_toshiba_init_verbs[] = {
- {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
- /* pin sensing on HP and Mic jacks */
- {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
- {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
- {}
-};
-
-static struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
- HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
- { }
-};
-
-/* configuration for Lenovo Thinkpad T60 */
-static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* identical with AD1983 */
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-static struct hda_input_mux ad1981_thinkpad_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Mix", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-/* models */
-enum {
- AD1981_BASIC,
- AD1981_HP,
- AD1981_THINKPAD,
- AD1981_TOSHIBA,
- AD1981_MODELS
-};
-
-static const char *ad1981_models[AD1981_MODELS] = {
- [AD1981_HP] = "hp",
- [AD1981_THINKPAD] = "thinkpad",
- [AD1981_BASIC] = "basic",
- [AD1981_TOSHIBA] = "toshiba"
-};
-
-static struct snd_pci_quirk ad1981_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
- SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
- /* All HP models */
- SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
- SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
- /* Lenovo Thinkpad T60/X60/Z6xx */
- SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
- /* HP nx6320 (reversed SSID, H/W bug) */
- SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
- {}
-};
-
-static int patch_ad1981(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
- set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
- spec->multiout.dac_nids = ad1981_dac_nids;
- spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
- spec->num_adc_nids = 1;
- spec->adc_nids = ad1981_adc_nids;
- spec->capsrc_nids = ad1981_capsrc_nids;
- spec->input_mux = &ad1981_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1981_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1981_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1981_loopbacks;
-#endif
- spec->vmaster_nid = 0x05;
-
- codec->patch_ops = ad198x_patch_ops;
-
- /* override some parameters */
- board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
- ad1981_models,
- ad1981_cfg_tbl);
- switch (board_config) {
- case AD1981_HP:
- spec->mixers[0] = ad1981_hp_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1981_hp_init_verbs;
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1981_hp_capture_source;
-
- codec->patch_ops.init = ad1981_hp_init;
- codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- case AD1981_THINKPAD:
- spec->mixers[0] = ad1981_thinkpad_mixers;
- spec->input_mux = &ad1981_thinkpad_capture_source;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- case AD1981_TOSHIBA:
- spec->mixers[0] = ad1981_hp_mixers;
- spec->mixers[1] = ad1981_toshiba_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = ad1981_toshiba_init_verbs;
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1981_hp_capture_source;
- codec->patch_ops.init = ad1981_hp_init;
- codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
- break;
- }
-
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
-
- return 0;
-}
-
-
-/*
- * AD1988
- *
- * Output pins and routes
- *
- * Pin Mix Sel DAC (*)
- * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
- * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
- * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a
- * port-D 0x12 (mute/hp) <- 0x29 <- 04
- * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
- * port-F 0x16 (mute) <- 0x2a <- 06
- * port-G 0x24 (mute) <- 0x27 <- 05
- * port-H 0x25 (mute) <- 0x28 <- 0a
- * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
- *
- * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
- * (*) DAC2/3/4 are swapped to DAC3/4/2 on AD198A rev.2 due to a h/w bug.
- *
- * Input pins and routes
- *
- * pin boost mix input # / adc input #
- * port-A 0x11 -> 0x38 -> mix 2, ADC 0
- * port-B 0x14 -> 0x39 -> mix 0, ADC 1
- * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
- * port-D 0x12 -> 0x3d -> mix 3, ADC 8
- * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
- * port-F 0x16 -> 0x3b -> mix 5, ADC 3
- * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
- * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
- *
- *
- * DAC assignment
- * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
- * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
- *
- * Inputs of Analog Mix (0x20)
- * 0:Port-B (front mic)
- * 1:Port-C/G/H (line-in)
- * 2:Port-A
- * 3:Port-D (line-in/2)
- * 4:Port-E/G/H (mic-in)
- * 5:Port-F (mic2-in)
- * 6:CD
- * 7:Beep
- *
- * ADC selection
- * 0:Port-A
- * 1:Port-B (front mic-in)
- * 2:Port-C (line-in)
- * 3:Port-F (mic2-in)
- * 4:Port-E (mic-in)
- * 5:CD
- * 6:Port-G
- * 7:Port-H
- * 8:Port-D (line-in/2)
- * 9:Mix
- *
- * Proposed pin assignments by the datasheet
- *
- * 6-stack
- * Port-A front headphone
- * B front mic-in
- * C rear line-in
- * D rear front-out
- * E rear mic-in
- * F rear surround
- * G rear CLFE
- * H rear side
- *
- * 3-stack
- * Port-A front headphone
- * B front mic
- * C rear line-in/surround
- * D rear front-out
- * E rear mic-in/CLFE
- *
- * laptop
- * Port-A headphone
- * B mic-in
- * C docking station
- * D internal speaker (with EAPD)
- * E/F quad mic array
- */
-
-
-/* models */
-enum {
- AD1988_6STACK,
- AD1988_6STACK_DIG,
- AD1988_3STACK,
- AD1988_3STACK_DIG,
- AD1988_LAPTOP,
- AD1988_LAPTOP_DIG,
- AD1988_AUTO,
- AD1988_MODEL_LAST,
-};
-
-/* reivision id to check workarounds */
-#define AD1988A_REV2 0x100200
-
-#define is_rev2(codec) \
- ((codec)->vendor_id == 0x11d41988 && \
- (codec)->revision_id == AD1988A_REV2)
-
-/*
- * mixers
- */
-
-static hda_nid_t ad1988_6stack_dac_nids[4] = {
- 0x04, 0x06, 0x05, 0x0a
-};
-
-static hda_nid_t ad1988_3stack_dac_nids[3] = {
- 0x04, 0x05, 0x0a
-};
-
-/* for AD1988A revision-2, DAC2-4 are swapped */
-static hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
- 0x04, 0x05, 0x0a, 0x06
-};
-
-static hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
- 0x04, 0x0a, 0x06
-};
-
-static hda_nid_t ad1988_adc_nids[3] = {
- 0x08, 0x09, 0x0f
-};
-
-static hda_nid_t ad1988_capsrc_nids[3] = {
- 0x0c, 0x0d, 0x0e
-};
-
-#define AD1988_SPDIF_OUT 0x02
-#define AD1988_SPDIF_OUT_HDMI 0x0b
-#define AD1988_SPDIF_IN 0x07
-
-static hda_nid_t ad1989b_slave_dig_outs[] = {
- AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
-};
-
-static struct hda_input_mux ad1988_6stack_capture_source = {
- .num_items = 5,
- .items = {
- { "Front Mic", 0x1 }, /* port-B */
- { "Line", 0x2 }, /* port-C */
- { "Mic", 0x4 }, /* port-E */
- { "CD", 0x5 },
- { "Mix", 0x9 },
- },
-};
-
-static struct hda_input_mux ad1988_laptop_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic/Line", 0x1 }, /* port-B */
- { "CD", 0x5 },
- { "Mix", 0x9 },
- },
-};
-
-/*
- */
-static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
- spec->num_channel_mode);
-}
-
-static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
- spec->num_channel_mode, spec->multiout.max_channels);
-}
-
-static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
- spec->num_channel_mode,
- &spec->multiout.max_channels);
- if (err >= 0 && spec->need_dac_fix)
- spec->multiout.num_dacs = spec->multiout.max_channels / 2;
- return err;
-}
-
-/* 6-stack mode */
-static struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
- HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
- HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
-
- { } /* end */
-};
-
-/* 3-stack mode */
-static struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
- HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
- HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = ad198x_ch_mode_info,
- .get = ad198x_ch_mode_get,
- .put = ad198x_ch_mode_put,
- },
-
- { } /* end */
-};
-
-/* laptop mode */
-static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
- HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Boost", 0x39, 0x0, HDA_OUTPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "External Amplifier",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
- .info = ad198x_eapd_info,
- .get = ad198x_eapd_get,
- .put = ad198x_eapd_put,
- .private_value = 0x12, /* port-D */
- },
-
- { } /* end */
-};
-
-/* capture */
-static struct snd_kcontrol_new ad1988_capture_mixers[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 3,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
-};
-
-static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[] = {
- "PCM", "ADC1", "ADC2", "ADC3"
- };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item >= 4)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
-
-static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int sel;
-
- sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_INPUT);
- if (!(sel & 0x80))
- ucontrol->value.enumerated.item[0] = 0;
- else {
- sel = snd_hda_codec_read(codec, 0x0b, 0,
- AC_VERB_GET_CONNECT_SEL, 0);
- if (sel < 3)
- sel++;
- else
- sel = 0;
- ucontrol->value.enumerated.item[0] = sel;
- }
- return 0;
-}
-
-static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int val, sel;
- int change;
-
- val = ucontrol->value.enumerated.item[0];
- if (val > 3)
- return -EINVAL;
- if (!val) {
- sel = snd_hda_codec_read(codec, 0x1d, 0,
- AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_INPUT);
- change = sel & 0x80;
- if (change) {
- snd_hda_codec_write_cache(codec, 0x1d, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(0));
- snd_hda_codec_write_cache(codec, 0x1d, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_MUTE(1));
- }
- } else {
- sel = snd_hda_codec_read(codec, 0x1d, 0,
- AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_INPUT | 0x01);
- change = sel & 0x80;
- if (change) {
- snd_hda_codec_write_cache(codec, 0x1d, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_MUTE(0));
- snd_hda_codec_write_cache(codec, 0x1d, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(1));
- }
- sel = snd_hda_codec_read(codec, 0x0b, 0,
- AC_VERB_GET_CONNECT_SEL, 0) + 1;
- change |= sel != val;
- if (change)
- snd_hda_codec_write_cache(codec, 0x0b, 0,
- AC_VERB_SET_CONNECT_SEL,
- val - 1);
- }
- return change;
-}
-
-static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "IEC958 Playback Source",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
- .info = ad1988_spdif_playback_source_info,
- .get = ad1988_spdif_playback_source_get,
- .put = ad1988_spdif_playback_source_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
- HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-/*
- * initialization verbs
- */
-
-/*
- * for 6-stack (+dig)
- */
-static struct hda_verb ad1988_6stack_init_verbs[] = {
- /* Front, Surround, CLFE, side DAC; unmute as default */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-A front headphon path */
- {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Port-D line-out path */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Port-F surround path */
- {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Port-G CLFE path */
- {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Port-H side path */
- {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Mono out path */
- {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
- /* Port-B front mic-in path */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-C line-in path */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Port-E mic-in path */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Analog CD Input */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
-
- { }
-};
-
-static struct hda_verb ad1988_capture_init_verbs[] = {
- /* mute analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* select ADCs - front-mic */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
-
- { }
-};
-
-static struct hda_verb ad1988_spdif_init_verbs[] = {
- /* SPDIF out sel */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* SPDIF out pin */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
-
- { }
-};
-
-static struct hda_verb ad1988_spdif_in_init_verbs[] = {
- /* unmute SPDIF input pin */
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { }
-};
-
-/* AD1989 has no ADC -> SPDIF route */
-static struct hda_verb ad1989_spdif_init_verbs[] = {
- /* SPDIF-1 out pin */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- /* SPDIF-2/HDMI out pin */
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- { }
-};
-
-/*
- * verbs for 3stack (+dig)
- */
-static struct hda_verb ad1988_3stack_ch2_init[] = {
- /* set port-C to line-in */
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- /* set port-E to mic-in */
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { } /* end */
-};
-
-static struct hda_verb ad1988_3stack_ch6_init[] = {
- /* set port-C to surround out */
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- /* set port-E to CLFE out */
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { } /* end */
-};
-
-static struct hda_channel_mode ad1988_3stack_modes[2] = {
- { 2, ad1988_3stack_ch2_init },
- { 6, ad1988_3stack_ch6_init },
-};
-
-static struct hda_verb ad1988_3stack_init_verbs[] = {
- /* Front, Surround, CLFE, side DAC; unmute as default */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-A front headphon path */
- {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Port-D line-out path */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Mono out path */
- {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
- /* Port-B front mic-in path */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-C line-in/surround path - 6ch mode as default */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
- {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* Port-E mic-in/CLFE path - 6ch mode as default */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
- {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* mute analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* select ADCs - front-mic */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
- { }
-};
-
-/*
- * verbs for laptop mode (+dig)
- */
-static struct hda_verb ad1988_laptop_hp_on[] = {
- /* unmute port-A and mute port-D */
- { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-static struct hda_verb ad1988_laptop_hp_off[] = {
- /* mute port-A and unmute port-D */
- { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { } /* end */
-};
-
-#define AD1988_HP_EVENT 0x01
-
-static struct hda_verb ad1988_laptop_init_verbs[] = {
- /* Front, Surround, CLFE, side DAC; unmute as default */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-A front headphon path */
- {0x37, AC_VERB_SET_CONNECT_SEL, 0x01}, /* DAC1:04h */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
- /* Port-D line-out path + EAPD */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
- /* Mono out path */
- {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
- /* Port-B mic-in path */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-C docking station - try to output */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* mute analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* select ADCs - mic */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
- { }
-};
-
-static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) != AD1988_HP_EVENT)
- return;
- if (snd_hda_jack_detect(codec, 0x11))
- snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
- else
- snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1988_loopbacks[] = {
- { 0x20, HDA_INPUT, 0 }, /* Front Mic */
- { 0x20, HDA_INPUT, 1 }, /* Line */
- { 0x20, HDA_INPUT, 4 }, /* Mic */
- { 0x20, HDA_INPUT, 6 }, /* CD */
- { } /* end */
-};
-#endif
-
-/*
- * Automatic parse of I/O pins from the BIOS configuration
- */
-
-enum {
- AD_CTL_WIDGET_VOL,
- AD_CTL_WIDGET_MUTE,
- AD_CTL_BIND_MUTE,
-};
-static struct snd_kcontrol_new ad1988_control_templates[] = {
- HDA_CODEC_VOLUME(NULL, 0, 0, 0),
- HDA_CODEC_MUTE(NULL, 0, 0, 0),
- HDA_BIND_MUTE(NULL, 0, 0, 0),
-};
-
-/* add dynamic controls */
-static int add_control(struct ad198x_spec *spec, int type, const char *name,
- unsigned long val)
-{
- struct snd_kcontrol_new *knew;
-
- snd_array_init(&spec->kctls, sizeof(*knew), 32);
- knew = snd_array_new(&spec->kctls);
- if (!knew)
- return -ENOMEM;
- *knew = ad1988_control_templates[type];
- knew->name = kstrdup(name, GFP_KERNEL);
- if (! knew->name)
- return -ENOMEM;
- if (get_amp_nid_(val))
- knew->subdevice = HDA_SUBDEV_AMP_FLAG;
- knew->private_value = val;
- return 0;
-}
-
-#define AD1988_PIN_CD_NID 0x18
-#define AD1988_PIN_BEEP_NID 0x10
-
-static hda_nid_t ad1988_mixer_nids[8] = {
- /* A B C D E F G H */
- 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
-};
-
-static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
-{
- static hda_nid_t idx_to_dac[8] = {
- /* A B C D E F G H */
- 0x04, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
- };
- static hda_nid_t idx_to_dac_rev2[8] = {
- /* A B C D E F G H */
- 0x04, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
- };
- if (is_rev2(codec))
- return idx_to_dac_rev2[idx];
- else
- return idx_to_dac[idx];
-}
-
-static hda_nid_t ad1988_boost_nids[8] = {
- 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
-};
-
-static int ad1988_pin_idx(hda_nid_t nid)
-{
- static hda_nid_t ad1988_io_pins[8] = {
- 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
- };
- int i;
- for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
- if (ad1988_io_pins[i] == nid)
- return i;
- return 0; /* should be -1 */
-}
-
-static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
-{
- static int loopback_idx[8] = {
- 2, 0, 1, 3, 4, 5, 1, 4
- };
- switch (nid) {
- case AD1988_PIN_CD_NID:
- return 6;
- default:
- return loopback_idx[ad1988_pin_idx(nid)];
- }
-}
-
-static int ad1988_pin_to_adc_idx(hda_nid_t nid)
-{
- static int adc_idx[8] = {
- 0, 1, 2, 8, 4, 3, 6, 7
- };
- switch (nid) {
- case AD1988_PIN_CD_NID:
- return 5;
- default:
- return adc_idx[ad1988_pin_idx(nid)];
- }
-}
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct ad198x_spec *spec = codec->spec;
- int i, idx;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- /* check the pins hardwired to audio widget */
- for (i = 0; i < cfg->line_outs; i++) {
- idx = ad1988_pin_idx(cfg->line_out_pins[i]);
- spec->multiout.dac_nids[i] = ad1988_idx_to_dac(codec, idx);
- }
- spec->multiout.num_dacs = cfg->line_outs;
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- char name[32];
- static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
- hda_nid_t nid;
- int i, err;
-
- for (i = 0; i < cfg->line_outs; i++) {
- hda_nid_t dac = spec->multiout.dac_nids[i];
- if (! dac)
- continue;
- nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
- if (i == 2) {
- /* Center/LFE */
- err = add_control(spec, AD_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_control(spec, AD_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_control(spec, AD_CTL_BIND_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
- if (err < 0)
- return err;
- err = add_control(spec, AD_CTL_BIND_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
- if (err < 0)
- return err;
- } else {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = add_control(spec, AD_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = add_control(spec, AD_CTL_BIND_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-/* add playback controls for speaker and HP outputs */
-static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
- const char *pfx)
-{
- struct ad198x_spec *spec = codec->spec;
- hda_nid_t nid;
- int i, idx, err;
- char name[32];
-
- if (! pin)
- return 0;
-
- idx = ad1988_pin_idx(pin);
- nid = ad1988_idx_to_dac(codec, idx);
- /* check whether the corresponding DAC was already taken */
- for (i = 0; i < spec->autocfg.line_outs; i++) {
- hda_nid_t pin = spec->autocfg.line_out_pins[i];
- hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
- if (dac == nid)
- break;
- }
- if (i >= spec->autocfg.line_outs) {
- /* specify the DAC as the extra output */
- if (!spec->multiout.hp_nid)
- spec->multiout.hp_nid = nid;
- else
- spec->multiout.extra_out_nid[0] = nid;
- /* control HP volume/switch on the output mixer amp */
- sprintf(name, "%s Playback Volume", pfx);
- err = add_control(spec, AD_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- nid = ad1988_mixer_nids[idx];
- sprintf(name, "%s Playback Switch", pfx);
- if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
- return err;
- return 0;
-}
-
-/* create input playback/capture controls for the given pin */
-static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
- const char *ctlname, int ctlidx, int boost)
-{
- char name[32];
- int err, idx;
-
- sprintf(name, "%s Playback Volume", ctlname);
- idx = ad1988_pin_to_loopback_idx(pin);
- if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
- return err;
- sprintf(name, "%s Playback Switch", ctlname);
- if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
- return err;
- if (boost) {
- hda_nid_t bnid;
- idx = ad1988_pin_idx(pin);
- bnid = ad1988_boost_nids[idx];
- if (bnid) {
- sprintf(name, "%s Boost", ctlname);
- return add_control(spec, AD_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
-
- }
- }
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct ad198x_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux;
- int i, err, type, type_idx;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- const char *label;
- type = cfg->inputs[i].type;
- label = hda_get_autocfg_input_label(codec, cfg, i);
- snd_hda_add_imux_item(imux, label,
- ad1988_pin_to_adc_idx(cfg->inputs[i].pin),
- &type_idx);
- err = new_analog_input(spec, cfg->inputs[i].pin,
- label, type_idx,
- type == AUTO_PIN_MIC);
- if (err < 0)
- return err;
- }
- snd_hda_add_imux_item(imux, "Mix", 9, NULL);
-
- if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
- "Analog Mix Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
- return err;
- if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
- "Analog Mix Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
- return err;
-
- return 0;
-}
-
-static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- int dac_idx)
-{
- /* set as output */
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
- switch (nid) {
- case 0x11: /* port-A - DAC 04 */
- snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
- break;
- case 0x14: /* port-B - DAC 06 */
- snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
- break;
- case 0x15: /* port-C - DAC 05 */
- snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
- break;
- case 0x17: /* port-E - DAC 0a */
- snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
- break;
- case 0x13: /* mono - DAC 04 */
- snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
- break;
- }
-}
-
-static void ad1988_auto_init_multi_out(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.line_outs; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
- }
-}
-
-static void ad1988_auto_init_extra_out(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- hda_nid_t pin;
-
- pin = spec->autocfg.speaker_pins[0];
- if (pin) /* connect to front */
- ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
- pin = spec->autocfg.hp_pins[0];
- if (pin) /* connect to front */
- ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
-}
-
-static void ad1988_auto_init_analog_input(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, idx;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- switch (nid) {
- case 0x15: /* port-C */
- snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
- break;
- case 0x17: /* port-E */
- snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
- break;
- }
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- i == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN);
- if (nid != AD1988_PIN_CD_NID)
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- idx = ad1988_pin_idx(nid);
- if (ad1988_boost_nids[idx])
- snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_ZERO);
- }
-}
-
-/* parse the BIOS configuration and set up the alc_spec */
-/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
-static int ad1988_parse_auto_config(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- int err;
-
- if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
- return err;
- if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
- return err;
- if (! spec->autocfg.line_outs)
- return 0; /* can't find valid BIOS pin config */
- if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
- (err = ad1988_auto_create_extra_out(codec,
- spec->autocfg.speaker_pins[0],
- "Speaker")) < 0 ||
- (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
- "Headphone")) < 0 ||
- (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
- if (spec->autocfg.dig_in_pin)
- spec->dig_in_nid = AD1988_SPDIF_IN;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
-
- spec->input_mux = &spec->private_imux;
-
- return 1;
-}
-
-/* init callback for auto-configuration model -- overriding the default init */
-static int ad1988_auto_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1988_auto_init_multi_out(codec);
- ad1988_auto_init_extra_out(codec);
- ad1988_auto_init_analog_input(codec);
- return 0;
-}
-
-
-/*
- */
-
-static const char *ad1988_models[AD1988_MODEL_LAST] = {
- [AD1988_6STACK] = "6stack",
- [AD1988_6STACK_DIG] = "6stack-dig",
- [AD1988_3STACK] = "3stack",
- [AD1988_3STACK_DIG] = "3stack-dig",
- [AD1988_LAPTOP] = "laptop",
- [AD1988_LAPTOP_DIG] = "laptop-dig",
- [AD1988_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk ad1988_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
- SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
- SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
- SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
- {}
-};
-
-static int patch_ad1988(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- if (is_rev2(codec))
- snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
-
- board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
- ad1988_models, ad1988_cfg_tbl);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = AD1988_AUTO;
- }
-
- if (board_config == AD1988_AUTO) {
- /* automatic parse from the BIOS config */
- err = ad1988_parse_auto_config(codec);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- } else if (! err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
- board_config = AD1988_6STACK;
- }
- }
-
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
- switch (board_config) {
- case AD1988_6STACK:
- case AD1988_6STACK_DIG:
- spec->multiout.max_channels = 8;
- spec->multiout.num_dacs = 4;
- if (is_rev2(codec))
- spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
- else
- spec->multiout.dac_nids = ad1988_6stack_dac_nids;
- spec->input_mux = &ad1988_6stack_capture_source;
- spec->num_mixers = 2;
- if (is_rev2(codec))
- spec->mixers[0] = ad1988_6stack_mixers1_rev2;
- else
- spec->mixers[0] = ad1988_6stack_mixers1;
- spec->mixers[1] = ad1988_6stack_mixers2;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1988_6stack_init_verbs;
- if (board_config == AD1988_6STACK_DIG) {
- spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
- spec->dig_in_nid = AD1988_SPDIF_IN;
- }
- break;
- case AD1988_3STACK:
- case AD1988_3STACK_DIG:
- spec->multiout.max_channels = 6;
- spec->multiout.num_dacs = 3;
- if (is_rev2(codec))
- spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
- else
- spec->multiout.dac_nids = ad1988_3stack_dac_nids;
- spec->input_mux = &ad1988_6stack_capture_source;
- spec->channel_mode = ad1988_3stack_modes;
- spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
- spec->num_mixers = 2;
- if (is_rev2(codec))
- spec->mixers[0] = ad1988_3stack_mixers1_rev2;
- else
- spec->mixers[0] = ad1988_3stack_mixers1;
- spec->mixers[1] = ad1988_3stack_mixers2;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1988_3stack_init_verbs;
- if (board_config == AD1988_3STACK_DIG)
- spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
- break;
- case AD1988_LAPTOP:
- case AD1988_LAPTOP_DIG:
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = ad1988_3stack_dac_nids;
- spec->input_mux = &ad1988_laptop_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1988_laptop_mixers;
- spec->inv_eapd = 1; /* inverted EAPD */
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1988_laptop_init_verbs;
- if (board_config == AD1988_LAPTOP_DIG)
- spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
- break;
- }
-
- spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
- spec->adc_nids = ad1988_adc_nids;
- spec->capsrc_nids = ad1988_capsrc_nids;
- spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
- spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
- if (spec->multiout.dig_out_nid) {
- if (codec->vendor_id >= 0x11d4989a) {
- spec->mixers[spec->num_mixers++] =
- ad1989_spdif_out_mixers;
- spec->init_verbs[spec->num_init_verbs++] =
- ad1989_spdif_init_verbs;
- codec->slave_dig_outs = ad1989b_slave_dig_outs;
- } else {
- spec->mixers[spec->num_mixers++] =
- ad1988_spdif_out_mixers;
- spec->init_verbs[spec->num_init_verbs++] =
- ad1988_spdif_init_verbs;
- }
- }
- if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
- spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
- spec->init_verbs[spec->num_init_verbs++] =
- ad1988_spdif_in_init_verbs;
- }
-
- codec->patch_ops = ad198x_patch_ops;
- switch (board_config) {
- case AD1988_AUTO:
- codec->patch_ops.init = ad1988_auto_init;
- break;
- case AD1988_LAPTOP:
- case AD1988_LAPTOP_DIG:
- codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
- break;
- }
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1988_loopbacks;
-#endif
- spec->vmaster_nid = 0x04;
-
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
-
- return 0;
-}
-
-
-/*
- * AD1884 / AD1984
- *
- * port-B - front line/mic-in
- * port-E - aux in/out
- * port-F - aux in/out
- * port-C - rear line/mic-in
- * port-D - rear line/hp-out
- * port-A - front line/hp-out
- *
- * AD1984 = AD1884 + two digital mic-ins
- *
- * FIXME:
- * For simplicity, we share the single DAC for both HP and line-outs
- * right now. The inidividual playbacks could be easily implemented,
- * but no build-up framework is given, so far.
- */
-
-static hda_nid_t ad1884_dac_nids[1] = {
- 0x04,
-};
-
-static hda_nid_t ad1884_adc_nids[2] = {
- 0x08, 0x09,
-};
-
-static hda_nid_t ad1884_capsrc_nids[2] = {
- 0x0c, 0x0d,
-};
-
-#define AD1884_SPDIF_OUT 0x02
-
-static struct hda_input_mux ad1884_capture_source = {
- .num_items = 4,
- .items = {
- { "Front Mic", 0x0 },
- { "Mic", 0x1 },
- { "CD", 0x2 },
- { "Mix", 0x3 },
- },
-};
-
-static struct snd_kcontrol_new ad1884_base_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* SPDIF controls */
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- /* identical with ad1983 */
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
- HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
- HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
- HDA_INPUT),
- { } /* end */
-};
-
-/*
- * initialization verbs
- */
-static struct hda_verb ad1884_init_verbs[] = {
- /* DACs; mute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-A (HP) mixer */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* HP selector - select DAC2 */
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Port-D (Line-out) mixer */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-D pin */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mono-out mixer */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Mono-out pin */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mono selector */
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Port-B (front mic) pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Port-C (rear mic) pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Analog mixer; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
- /* SPDIF output selector */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- { } /* end */
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1884_loopbacks[] = {
- { 0x20, HDA_INPUT, 0 }, /* Front Mic */
- { 0x20, HDA_INPUT, 1 }, /* Mic */
- { 0x20, HDA_INPUT, 2 }, /* CD */
- { 0x20, HDA_INPUT, 4 }, /* Docking */
- { } /* end */
-};
-#endif
-
-static const char *ad1884_slave_vols[] = {
- "PCM Playback Volume",
- "Mic Playback Volume",
- "Mono Playback Volume",
- "Front Mic Playback Volume",
- "Mic Playback Volume",
- "CD Playback Volume",
- "Internal Mic Playback Volume",
- "Docking Mic Playback Volume",
- /* "Beep Playback Volume", */
- "IEC958 Playback Volume",
- NULL
-};
-
-static int patch_ad1884(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
- spec->multiout.dac_nids = ad1884_dac_nids;
- spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
- spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
- spec->adc_nids = ad1884_adc_nids;
- spec->capsrc_nids = ad1884_capsrc_nids;
- spec->input_mux = &ad1884_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1884_base_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1884_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1884_loopbacks;
-#endif
- spec->vmaster_nid = 0x04;
- /* we need to cover all playback volumes */
- spec->slave_vols = ad1884_slave_vols;
-
- codec->patch_ops = ad198x_patch_ops;
-
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
-
- return 0;
-}
-
-/*
- * Lenovo Thinkpad T61/X61
- */
-static struct hda_input_mux ad1984_thinkpad_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x1 },
- { "Mix", 0x3 },
- { "Docking-Station", 0x4 },
- },
-};
-
-
-/*
- * Dell Precision T3400
- */
-static struct hda_input_mux ad1984_dell_desktop_capture_source = {
- .num_items = 3,
- .items = {
- { "Front Mic", 0x0 },
- { "Line-In", 0x1 },
- { "Mix", 0x3 },
- },
-};
-
-
-static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* SPDIF controls */
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- /* identical with ad1983 */
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-/* additional verbs */
-static struct hda_verb ad1984_thinkpad_init_verbs[] = {
- /* Port-E (docking station mic) pin */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* docking mic boost */
- {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Analog PC Beeper - allow firmware/ACPI beeps */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a},
- /* Analog mixer - docking mic; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* enable EAPD bit */
- {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- { } /* end */
-};
-
-/*
- * Dell Precision T3400
- */
-static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
-};
-
-/* Digial MIC ADC NID 0x05 + 0x06 */
-static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
- stream_tag, 0, format);
- return 0;
-}
-
-static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
- return 0;
-}
-
-static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x05,
- .ops = {
- .prepare = ad1984_pcm_dmic_prepare,
- .cleanup = ad1984_pcm_dmic_cleanup
- },
-};
-
-static int ad1984_build_pcms(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- struct hda_pcm *info;
- int err;
-
- err = ad198x_build_pcms(codec);
- if (err < 0)
- return err;
-
- info = spec->pcm_rec + codec->num_pcms;
- codec->num_pcms++;
- info->name = "AD1984 Digital Mic";
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
- return 0;
-}
-
-/* models */
-enum {
- AD1984_BASIC,
- AD1984_THINKPAD,
- AD1984_DELL_DESKTOP,
- AD1984_MODELS
-};
-
-static const char *ad1984_models[AD1984_MODELS] = {
- [AD1984_BASIC] = "basic",
- [AD1984_THINKPAD] = "thinkpad",
- [AD1984_DELL_DESKTOP] = "dell_desktop",
-};
-
-static struct snd_pci_quirk ad1984_cfg_tbl[] = {
- /* Lenovo Thinkpad T61/X61 */
- SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
- SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
- SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", AD1984_DELL_DESKTOP),
- {}
-};
-
-static int patch_ad1984(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int board_config, err;
-
- err = patch_ad1884(codec);
- if (err < 0)
- return err;
- spec = codec->spec;
- board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
- ad1984_models, ad1984_cfg_tbl);
- switch (board_config) {
- case AD1984_BASIC:
- /* additional digital mics */
- spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
- codec->patch_ops.build_pcms = ad1984_build_pcms;
- break;
- case AD1984_THINKPAD:
- if (codec->subsystem_id == 0x17aa20fb) {
- /* Thinpad X300 does not have the ability to do SPDIF,
- or attach to docking station to use SPDIF */
- spec->multiout.dig_out_nid = 0;
- } else
- spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
- spec->input_mux = &ad1984_thinkpad_capture_source;
- spec->mixers[0] = ad1984_thinkpad_mixers;
- spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
- spec->analog_beep = 1;
- break;
- case AD1984_DELL_DESKTOP:
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1984_dell_desktop_capture_source;
- spec->mixers[0] = ad1984_dell_desktop_mixers;
- break;
- }
- return 0;
-}
-
-
-/*
- * AD1883 / AD1884A / AD1984A / AD1984B
- *
- * port-B (0x14) - front mic-in
- * port-E (0x1c) - rear mic-in
- * port-F (0x16) - CD / ext out
- * port-C (0x15) - rear line-in
- * port-D (0x12) - rear line-out
- * port-A (0x11) - front hp-out
- *
- * AD1984A = AD1884A + digital-mic
- * AD1883 = equivalent with AD1984A
- * AD1984B = AD1984A + extra SPDIF-out
- *
- * FIXME:
- * We share the single DAC for both HP and line-outs (see AD1884/1984).
- */
-
-static hda_nid_t ad1884a_dac_nids[1] = {
- 0x03,
-};
-
-#define ad1884a_adc_nids ad1884_adc_nids
-#define ad1884a_capsrc_nids ad1884_capsrc_nids
-
-#define AD1884A_SPDIF_OUT 0x02
-
-static struct hda_input_mux ad1884a_capture_source = {
- .num_items = 5,
- .items = {
- { "Front Mic", 0x0 },
- { "Mic", 0x4 },
- { "Line", 0x1 },
- { "CD", 0x2 },
- { "Mix", 0x3 },
- },
-};
-
-static struct snd_kcontrol_new ad1884a_base_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Boost", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* SPDIF controls */
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- /* identical with ad1983 */
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-/*
- * initialization verbs
- */
-static struct hda_verb ad1884a_init_verbs[] = {
- /* DACs; unmute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- /* Port-A (HP) mixer - route only from analog mixer */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Port-D (Line-out) mixer - route only from analog mixer */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-D pin */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mono-out mixer - route only from analog mixer */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Mono-out pin */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Port-B (front mic) pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Port-C (rear line-in) pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Port-E (rear mic) pin */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
- /* Port-F (CD) pin */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Analog mixer; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* capture sources */
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* SPDIF output amp */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- { } /* end */
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1884a_loopbacks[] = {
- { 0x20, HDA_INPUT, 0 }, /* Front Mic */
- { 0x20, HDA_INPUT, 1 }, /* Mic */
- { 0x20, HDA_INPUT, 2 }, /* CD */
- { 0x20, HDA_INPUT, 4 }, /* Docking */
- { } /* end */
-};
-#endif
-
-/*
- * Laptop model
- *
- * Port A: Headphone jack
- * Port B: MIC jack
- * Port C: Internal MIC
- * Port D: Dock Line Out (if enabled)
- * Port E: Dock Line In (if enabled)
- * Port F: Internal speakers
- */
-
-static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
- int mute = (!ucontrol->value.integer.value[0] &&
- !ucontrol->value.integer.value[1]);
- /* toggle GPIO1 according to the mute state */
- snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
- mute ? 0x02 : 0x0);
- return ret;
-}
-
-static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = ad1884a_mobile_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = ad1884a_mobile_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-/* mute internal speaker if HP is plugged */
-static void ad1884a_hp_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x11);
- snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
- present ? 0x00 : 0x02);
-}
-
-/* switch to external mic if plugged */
-static void ad1884a_hp_automic(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x14);
- snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
- present ? 0 : 1);
-}
-
-#define AD1884A_HP_EVENT 0x37
-#define AD1884A_MIC_EVENT 0x36
-
-/* unsolicited event for HP jack sensing */
-static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- switch (res >> 26) {
- case AD1884A_HP_EVENT:
- ad1884a_hp_automute(codec);
- break;
- case AD1884A_MIC_EVENT:
- ad1884a_hp_automic(codec);
- break;
- }
-}
-
-/* initialize jack-sensing, too */
-static int ad1884a_hp_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1884a_hp_automute(codec);
- ad1884a_hp_automic(codec);
- return 0;
-}
-
-/* mute internal speaker if HP or docking HP is plugged */
-static void ad1884a_laptop_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x11);
- if (!present)
- present = snd_hda_jack_detect(codec, 0x12);
- snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
- present ? 0x00 : 0x02);
-}
-
-/* switch to external mic if plugged */
-static void ad1884a_laptop_automic(struct hda_codec *codec)
-{
- unsigned int idx;
-
- if (snd_hda_jack_detect(codec, 0x14))
- idx = 0;
- else if (snd_hda_jack_detect(codec, 0x1c))
- idx = 4;
- else
- idx = 1;
- snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
-}
-
-/* unsolicited event for HP jack sensing */
-static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case AD1884A_HP_EVENT:
- ad1884a_laptop_automute(codec);
- break;
- case AD1884A_MIC_EVENT:
- ad1884a_laptop_automic(codec);
- break;
- }
-}
-
-/* initialize jack-sensing, too */
-static int ad1884a_laptop_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1884a_laptop_automute(codec);
- ad1884a_laptop_automic(codec);
- return 0;
-}
-
-/* additional verbs for laptop model */
-static struct hda_verb ad1884a_laptop_verbs[] = {
- /* Port-A (HP) pin - always unmuted */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-F (int speaker) mixer - route only from analog mixer */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-F (int speaker) pin */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* required for compaq 6530s/6531s speaker output */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Port-C pin - internal mic-in */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
- /* Port-D (docking line-out) pin - default unmuted */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
- /* allow to touch GPIO1 (for mute control) */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
- { } /* end */
-};
-
-static struct hda_verb ad1884a_mobile_verbs[] = {
- /* DACs; unmute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- /* Port-A (HP) mixer - route only from analog mixer */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Port-A (HP) pin - always unmuted */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-B (mic jack) pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
- /* Port-C (int mic) pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
- /* Port-F (int speaker) mixer - route only from analog mixer */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-F pin */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Analog mixer; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* capture sources */
- /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
- /* allow to touch GPIO1 (for mute control) */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
- { } /* end */
-};
-
-/*
- * Thinkpad X300
- * 0x11 - HP
- * 0x12 - speaker
- * 0x14 - mic-in
- * 0x17 - built-in mic
- */
-
-static struct hda_verb ad1984a_thinkpad_verbs[] = {
- /* HP unmute */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* analog mix */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* turn on EAPD */
- {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- /* internal mic - dmic */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* set magic COEFs for dmic */
- {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
- {0x01, AC_VERB_SET_PROC_COEF, 0x08},
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct hda_input_mux ad1984a_thinkpad_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x5 },
- { "Mix", 0x3 },
- },
-};
-
-/* mute internal speaker if HP is plugged */
-static void ad1984a_thinkpad_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x11);
- snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-/* unsolicited event for HP jack sensing */
-static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) != AD1884A_HP_EVENT)
- return;
- ad1984a_thinkpad_automute(codec);
-}
-
-/* initialize jack-sensing, too */
-static int ad1984a_thinkpad_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1984a_thinkpad_automute(codec);
- return 0;
-}
-
-/*
- * HP Touchsmart
- * port-A (0x11) - front hp-out
- * port-B (0x14) - unused
- * port-C (0x15) - unused
- * port-D (0x12) - rear line out
- * port-E (0x1c) - front mic-in
- * port-F (0x16) - Internal speakers
- * digital-mic (0x17) - Internal mic
- */
-
-static struct hda_verb ad1984a_touchsmart_verbs[] = {
- /* DACs; unmute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
- /* Port-A (HP) mixer - route only from analog mixer */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Port-A (HP) pin - always unmuted */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Port-E (int speaker) mixer - route only from analog mixer */
- {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
- /* Port-E pin */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- /* Port-F (int speaker) mixer - route only from analog mixer */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-F pin */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Analog mixer; mute as default */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* capture sources */
- /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* unsolicited event for pin-sense */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
- /* allow to touch GPIO1 (for mute control) */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
- /* internal mic - dmic */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* set magic COEFs for dmic */
- {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
- {0x01, AC_VERB_SET_PROC_COEF, 0x08},
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
-/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .name = "Master Playback Switch",
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = ad1884a_mobile_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-/* switch to external mic if plugged */
-static void ad1984a_touchsmart_automic(struct hda_codec *codec)
-{
- if (snd_hda_jack_detect(codec, 0x1c))
- snd_hda_codec_write(codec, 0x0c, 0,
- AC_VERB_SET_CONNECT_SEL, 0x4);
- else
- snd_hda_codec_write(codec, 0x0c, 0,
- AC_VERB_SET_CONNECT_SEL, 0x5);
-}
-
-
-/* unsolicited event for HP jack sensing */
-static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case AD1884A_HP_EVENT:
- ad1884a_hp_automute(codec);
- break;
- case AD1884A_MIC_EVENT:
- ad1984a_touchsmart_automic(codec);
- break;
- }
-}
-
-/* initialize jack-sensing, too */
-static int ad1984a_touchsmart_init(struct hda_codec *codec)
-{
- ad198x_init(codec);
- ad1884a_hp_automute(codec);
- ad1984a_touchsmart_automic(codec);
- return 0;
-}
-
-
-/*
- */
-
-enum {
- AD1884A_DESKTOP,
- AD1884A_LAPTOP,
- AD1884A_MOBILE,
- AD1884A_THINKPAD,
- AD1984A_TOUCHSMART,
- AD1884A_MODELS
-};
-
-static const char *ad1884a_models[AD1884A_MODELS] = {
- [AD1884A_DESKTOP] = "desktop",
- [AD1884A_LAPTOP] = "laptop",
- [AD1884A_MOBILE] = "mobile",
- [AD1884A_THINKPAD] = "thinkpad",
- [AD1984A_TOUCHSMART] = "touchsmart",
-};
-
-static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
- SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
- SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
- SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
- SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
- SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
- SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
- SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
- {}
-};
-
-static int patch_ad1884a(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
- spec->multiout.dac_nids = ad1884a_dac_nids;
- spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
- spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
- spec->adc_nids = ad1884a_adc_nids;
- spec->capsrc_nids = ad1884a_capsrc_nids;
- spec->input_mux = &ad1884a_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = ad1884a_base_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1884a_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1884a_loopbacks;
-#endif
- codec->patch_ops = ad198x_patch_ops;
-
- /* override some parameters */
- board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
- ad1884a_models,
- ad1884a_cfg_tbl);
- switch (board_config) {
- case AD1884A_LAPTOP:
- spec->mixers[0] = ad1884a_laptop_mixers;
- spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
- spec->multiout.dig_out_nid = 0;
- codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
- codec->patch_ops.init = ad1884a_laptop_init;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- case AD1884A_MOBILE:
- spec->mixers[0] = ad1884a_mobile_mixers;
- spec->init_verbs[0] = ad1884a_mobile_verbs;
- spec->multiout.dig_out_nid = 0;
- codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
- codec->patch_ops.init = ad1884a_hp_init;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- case AD1884A_THINKPAD:
- spec->mixers[0] = ad1984a_thinkpad_mixers;
- spec->init_verbs[spec->num_init_verbs++] =
- ad1984a_thinkpad_verbs;
- spec->multiout.dig_out_nid = 0;
- spec->input_mux = &ad1984a_thinkpad_capture_source;
- codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
- codec->patch_ops.init = ad1984a_thinkpad_init;
- break;
- case AD1984A_TOUCHSMART:
- spec->mixers[0] = ad1984a_touchsmart_mixers;
- spec->init_verbs[0] = ad1984a_touchsmart_verbs;
- spec->multiout.dig_out_nid = 0;
- codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
- codec->patch_ops.init = ad1984a_touchsmart_init;
- /* set the upper-limit for mixer amp to 0dB for avoiding the
- * possible damage by overloading
- */
- snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- }
-
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
-
- return 0;
-}
-
-
-/*
- * AD1882 / AD1882A
- *
- * port-A - front hp-out
- * port-B - front mic-in
- * port-C - rear line-in, shared surr-out (3stack)
- * port-D - rear line-out
- * port-E - rear mic-in, shared clfe-out (3stack)
- * port-F - rear surr-out (6stack)
- * port-G - rear clfe-out (6stack)
- */
-
-static hda_nid_t ad1882_dac_nids[3] = {
- 0x04, 0x03, 0x05
-};
-
-static hda_nid_t ad1882_adc_nids[2] = {
- 0x08, 0x09,
-};
-
-static hda_nid_t ad1882_capsrc_nids[2] = {
- 0x0c, 0x0d,
-};
-
-#define AD1882_SPDIF_OUT 0x02
-
-/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
-static struct hda_input_mux ad1882_capture_source = {
- .num_items = 5,
- .items = {
- { "Front Mic", 0x1 },
- { "Mic", 0x4 },
- { "Line", 0x2 },
- { "CD", 0x3 },
- { "Mix", 0x7 },
- },
-};
-
-/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
-static struct hda_input_mux ad1882a_capture_source = {
- .num_items = 5,
- .items = {
- { "Front Mic", 0x1 },
- { "Mic", 0x4},
- { "Line", 0x2 },
- { "Digital Mic", 0x06 },
- { "Mix", 0x7 },
- },
-};
-
-static struct snd_kcontrol_new ad1882_base_mixers[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = ad198x_mux_enum_info,
- .get = ad198x_mux_enum_get,
- .put = ad198x_mux_enum_put,
- },
- /* SPDIF controls */
- HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
- /* identical with ad1983 */
- .info = ad1983_spdif_route_info,
- .get = ad1983_spdif_route_get,
- .put = ad1983_spdif_route_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1882_loopback_mixers[] = {
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
- HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
- HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = ad198x_ch_mode_info,
- .get = ad198x_ch_mode_get,
- .put = ad198x_ch_mode_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
- HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct hda_verb ad1882_ch2_init[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- { } /* end */
-};
-
-static struct hda_verb ad1882_ch4_init[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- { } /* end */
-};
-
-static struct hda_verb ad1882_ch6_init[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- { } /* end */
-};
-
-static struct hda_channel_mode ad1882_modes[3] = {
- { 2, ad1882_ch2_init },
- { 4, ad1882_ch4_init },
- { 6, ad1882_ch6_init },
-};
-
-/*
- * initialization verbs
- */
-static struct hda_verb ad1882_init_verbs[] = {
- /* DACs; mute as default */
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Port-A (HP) mixer */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-A pin */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* HP selector - select DAC2 */
- {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Port-D (Line-out) mixer */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Port-D pin */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mono-out mixer */
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Mono-out pin */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Port-B (front mic) pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
- /* Port-C (line-in) pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
- /* Port-C mixer - mute as input */
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Port-E (mic-in) pin */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
- /* Port-E mixer - mute as input */
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Port-F (surround) */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Port-G (CLFE) */
- {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Analog mixer; mute as default */
- /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* Analog Mix output amp */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
- /* SPDIF output selector */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
- { } /* end */
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list ad1882_loopbacks[] = {
- { 0x20, HDA_INPUT, 0 }, /* Front Mic */
- { 0x20, HDA_INPUT, 1 }, /* Mic */
- { 0x20, HDA_INPUT, 4 }, /* Line */
- { 0x20, HDA_INPUT, 6 }, /* CD */
- { } /* end */
-};
-#endif
-
-/* models */
-enum {
- AD1882_3STACK,
- AD1882_6STACK,
- AD1882_MODELS
-};
-
-static const char *ad1882_models[AD1986A_MODELS] = {
- [AD1882_3STACK] = "3stack",
- [AD1882_6STACK] = "6stack",
-};
-
-
-static int patch_ad1882(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- err = snd_hda_attach_beep_device(codec, 0x10);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
- spec->multiout.max_channels = 6;
- spec->multiout.num_dacs = 3;
- spec->multiout.dac_nids = ad1882_dac_nids;
- spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
- spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
- spec->adc_nids = ad1882_adc_nids;
- spec->capsrc_nids = ad1882_capsrc_nids;
- if (codec->vendor_id == 0x11d41882)
- spec->input_mux = &ad1882_capture_source;
- else
- spec->input_mux = &ad1882a_capture_source;
- spec->num_mixers = 2;
- spec->mixers[0] = ad1882_base_mixers;
- if (codec->vendor_id == 0x11d41882)
- spec->mixers[1] = ad1882_loopback_mixers;
- else
- spec->mixers[1] = ad1882a_loopback_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = ad1882_init_verbs;
- spec->spdif_route = 0;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = ad1882_loopbacks;
-#endif
- spec->vmaster_nid = 0x04;
-
- codec->patch_ops = ad198x_patch_ops;
-
- /* override some parameters */
- board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
- ad1882_models, NULL);
- switch (board_config) {
- default:
- case AD1882_3STACK:
- spec->num_mixers = 3;
- spec->mixers[2] = ad1882_3stack_mixers;
- spec->channel_mode = ad1882_modes;
- spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
- spec->need_dac_fix = 1;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- break;
- case AD1882_6STACK:
- spec->num_mixers = 3;
- spec->mixers[2] = ad1882_6stack_mixers;
- break;
- }
-
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
-
- return 0;
-}
-
-
-/*
- * patch entries
- */
-static struct hda_codec_preset snd_hda_preset_analog[] = {
- { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
- { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
- { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
- { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
- { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
- { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
- { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
- { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
- { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
- { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
- { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
- { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
- { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
- { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
- { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
- {} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:11d4*");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Analog Devices HD-audio codec");
-
-static struct hda_codec_preset_list analog_list = {
- .preset = snd_hda_preset_analog,
- .owner = THIS_MODULE,
-};
-
-static int __init patch_analog_init(void)
-{
- return snd_hda_add_codec_preset(&analog_list);
-}
-
-static void __exit patch_analog_exit(void)
-{
- snd_hda_delete_codec_preset(&analog_list);
-}
-
-module_init(patch_analog_init)
-module_exit(patch_analog_exit)
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c
deleted file mode 100644
index 46c8bf48c31f..000000000000
--- a/sound/pci/hda/patch_ca0110.c
+++ /dev/null
@@ -1,572 +0,0 @@
-/*
- * HD audio interface patch for Creative X-Fi CA0110-IBG chip
- *
- * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de>
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-/*
- */
-
-struct ca0110_spec {
- struct auto_pin_cfg autocfg;
- struct hda_multi_out multiout;
- hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
- hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
- hda_nid_t hp_dac;
- hda_nid_t input_pins[AUTO_PIN_LAST];
- hda_nid_t adcs[AUTO_PIN_LAST];
- hda_nid_t dig_out;
- hda_nid_t dig_in;
- unsigned int num_inputs;
- const char *input_labels[AUTO_PIN_LAST];
- struct hda_pcm pcm_rec[2]; /* PCM information */
-};
-
-/*
- * PCM callbacks
- */
-static int ca0110_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static int ca0110_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int ca0110_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Digital out
- */
-static int ca0110_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int ca0110_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int ca0110_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-/*
- * Analog capture
- */
-static int ca0110_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
-
- snd_hda_codec_setup_stream(codec, spec->adcs[substream->number],
- stream_tag, 0, format);
- return 0;
-}
-
-static int ca0110_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0110_spec *spec = codec->spec;
-
- snd_hda_codec_cleanup_stream(codec, spec->adcs[substream->number]);
- return 0;
-}
-
-/*
- */
-
-static char *dirstr[2] = { "Playback", "Capture" };
-
-static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
- int chan, int dir)
-{
- char namestr[44];
- int type = dir ? HDA_INPUT : HDA_OUTPUT;
- struct snd_kcontrol_new knew =
- HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
- sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
- return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
-}
-
-static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
- int chan, int dir)
-{
- char namestr[44];
- int type = dir ? HDA_INPUT : HDA_OUTPUT;
- struct snd_kcontrol_new knew =
- HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
- sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
- return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
-}
-
-#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0)
-#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0)
-#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1)
-#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1)
-#define add_mono_switch(codec, nid, pfx, chan) \
- _add_switch(codec, nid, pfx, chan, 0)
-#define add_mono_volume(codec, nid, pfx, chan) \
- _add_volume(codec, nid, pfx, chan, 0)
-
-static int ca0110_build_controls(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- static char *prefix[AUTO_CFG_MAX_OUTS] = {
- "Front", "Surround", NULL, "Side", "Multi"
- };
- hda_nid_t mutenid;
- int i, err;
-
- for (i = 0; i < spec->multiout.num_dacs; i++) {
- if (get_wcaps(codec, spec->out_pins[i]) & AC_WCAP_OUT_AMP)
- mutenid = spec->out_pins[i];
- else
- mutenid = spec->multiout.dac_nids[i];
- if (!prefix[i]) {
- err = add_mono_switch(codec, mutenid,
- "Center", 1);
- if (err < 0)
- return err;
- err = add_mono_switch(codec, mutenid,
- "LFE", 1);
- if (err < 0)
- return err;
- err = add_mono_volume(codec, spec->multiout.dac_nids[i],
- "Center", 1);
- if (err < 0)
- return err;
- err = add_mono_volume(codec, spec->multiout.dac_nids[i],
- "LFE", 1);
- if (err < 0)
- return err;
- } else {
- err = add_out_switch(codec, mutenid,
- prefix[i]);
- if (err < 0)
- return err;
- err = add_out_volume(codec, spec->multiout.dac_nids[i],
- prefix[i]);
- if (err < 0)
- return err;
- }
- }
- if (cfg->hp_outs) {
- if (get_wcaps(codec, cfg->hp_pins[0]) & AC_WCAP_OUT_AMP)
- mutenid = cfg->hp_pins[0];
- else
- mutenid = spec->multiout.dac_nids[i];
-
- err = add_out_switch(codec, mutenid, "Headphone");
- if (err < 0)
- return err;
- if (spec->hp_dac) {
- err = add_out_volume(codec, spec->hp_dac, "Headphone");
- if (err < 0)
- return err;
- }
- }
- for (i = 0; i < spec->num_inputs; i++) {
- const char *label = spec->input_labels[i];
- if (get_wcaps(codec, spec->input_pins[i]) & AC_WCAP_IN_AMP)
- mutenid = spec->input_pins[i];
- else
- mutenid = spec->adcs[i];
- err = add_in_switch(codec, mutenid, label);
- if (err < 0)
- return err;
- err = add_in_volume(codec, spec->adcs[i], label);
- if (err < 0)
- return err;
- }
-
- if (spec->dig_out) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- if (spec->dig_in) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
- if (err < 0)
- return err;
- err = add_in_volume(codec, spec->dig_in, "IEC958");
- }
- return 0;
-}
-
-/*
- */
-static struct hda_pcm_stream ca0110_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- .ops = {
- .open = ca0110_playback_pcm_open,
- .prepare = ca0110_playback_pcm_prepare,
- .cleanup = ca0110_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream ca0110_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .prepare = ca0110_capture_pcm_prepare,
- .cleanup = ca0110_capture_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream ca0110_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .open = ca0110_dig_playback_pcm_open,
- .close = ca0110_dig_playback_pcm_close,
- .prepare = ca0110_dig_playback_pcm_prepare
- },
-};
-
-static struct hda_pcm_stream ca0110_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-static int ca0110_build_pcms(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->pcm_info = info;
- codec->num_pcms = 0;
-
- info->name = "CA0110 Analog";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0110_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
- spec->multiout.max_channels;
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0110_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
- codec->num_pcms++;
-
- if (!spec->dig_out && !spec->dig_in)
- return 0;
-
- info++;
- info->name = "CA0110 Digital";
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- if (spec->dig_out) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- ca0110_pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
- }
- if (spec->dig_in) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- ca0110_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
- }
- codec->num_pcms++;
-
- return 0;
-}
-
-static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
-{
- if (pin) {
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
- if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
- }
- if (dac)
- snd_hda_codec_write(codec, dac, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
-}
-
-static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
-{
- if (pin) {
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80);
- if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(0));
- }
- if (adc)
- snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(0));
-}
-
-static int ca0110_init(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < spec->multiout.num_dacs; i++)
- init_output(codec, spec->out_pins[i],
- spec->multiout.dac_nids[i]);
- init_output(codec, cfg->hp_pins[0], spec->hp_dac);
- init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
-
- for (i = 0; i < spec->num_inputs; i++)
- init_input(codec, spec->input_pins[i], spec->adcs[i]);
- init_input(codec, cfg->dig_in_pin, spec->dig_in);
- return 0;
-}
-
-static void ca0110_free(struct hda_codec *codec)
-{
- kfree(codec->spec);
-}
-
-static struct hda_codec_ops ca0110_patch_ops = {
- .build_controls = ca0110_build_controls,
- .build_pcms = ca0110_build_pcms,
- .init = ca0110_init,
- .free = ca0110_free,
-};
-
-
-static void parse_line_outs(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, n;
- unsigned int def_conf;
- hda_nid_t nid;
-
- n = 0;
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- if (!def_conf)
- continue; /* invalid pin */
- if (snd_hda_get_connections(codec, nid, &spec->dacs[i], 1) != 1)
- continue;
- spec->out_pins[n++] = nid;
- }
- spec->multiout.dac_nids = spec->dacs;
- spec->multiout.num_dacs = n;
- spec->multiout.max_channels = n * 2;
-}
-
-static void parse_hp_out(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
- unsigned int def_conf;
- hda_nid_t nid, dac;
-
- if (!cfg->hp_outs)
- return;
- nid = cfg->hp_pins[0];
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- if (!def_conf) {
- cfg->hp_outs = 0;
- return;
- }
- if (snd_hda_get_connections(codec, nid, &dac, 1) != 1)
- return;
-
- for (i = 0; i < cfg->line_outs; i++)
- if (dac == spec->dacs[i])
- break;
- if (i >= cfg->line_outs) {
- spec->hp_dac = dac;
- spec->multiout.hp_nid = dac;
- }
-}
-
-static void parse_input(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid, pin;
- int n, i, j;
-
- n = 0;
- nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, nid++) {
- unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int type = get_wcaps_type(wcaps);
- if (type != AC_WID_AUD_IN)
- continue;
- if (snd_hda_get_connections(codec, nid, &pin, 1) != 1)
- continue;
- if (pin == cfg->dig_in_pin) {
- spec->dig_in = nid;
- continue;
- }
- for (j = 0; j < cfg->num_inputs; j++)
- if (cfg->inputs[j].pin == pin)
- break;
- if (j >= cfg->num_inputs)
- continue;
- spec->input_pins[n] = pin;
- spec->input_labels[n] = hda_get_input_pin_label(codec, pin, 1);
- spec->adcs[n] = nid;
- n++;
- }
- spec->num_inputs = n;
-}
-
-static void parse_digital(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
-
- if (cfg->dig_outs &&
- snd_hda_get_connections(codec, cfg->dig_out_pins[0],
- &spec->dig_out, 1) == 1)
- spec->multiout.dig_out_nid = spec->dig_out;
-}
-
-static int ca0110_parse_auto_config(struct hda_codec *codec)
-{
- struct ca0110_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
-
- parse_line_outs(codec);
- parse_hp_out(codec);
- parse_digital(codec);
- parse_input(codec);
- return 0;
-}
-
-
-static int patch_ca0110(struct hda_codec *codec)
-{
- struct ca0110_spec *spec;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
- codec->spec = spec;
-
- codec->bus->needs_damn_long_delay = 1;
-
- err = ca0110_parse_auto_config(codec);
- if (err < 0)
- goto error;
-
- codec->patch_ops = ca0110_patch_ops;
-
- return 0;
-
- error:
- kfree(codec->spec);
- codec->spec = NULL;
- return err;
-}
-
-
-/*
- * patch entries
- */
-static struct hda_codec_preset snd_hda_preset_ca0110[] = {
- { .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 },
- { .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 },
- { .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 },
- {} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:1102000a");
-MODULE_ALIAS("snd-hda-codec-id:1102000b");
-MODULE_ALIAS("snd-hda-codec-id:1102000d");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
-
-static struct hda_codec_preset_list ca0110_list = {
- .preset = snd_hda_preset_ca0110,
- .owner = THIS_MODULE,
-};
-
-static int __init patch_ca0110_init(void)
-{
- return snd_hda_add_codec_preset(&ca0110_list);
-}
-
-static void __exit patch_ca0110_exit(void)
-{
- snd_hda_delete_codec_preset(&ca0110_list);
-}
-
-module_init(patch_ca0110_init)
-module_exit(patch_ca0110_exit)
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
deleted file mode 100644
index 460fb2ef7e39..000000000000
--- a/sound/pci/hda/patch_cirrus.c
+++ /dev/null
@@ -1,1311 +0,0 @@
-/*
- * HD audio interface patch for Cirrus Logic CS420x chip
- *
- * Copyright (c) 2009 Takashi Iwai <tiwai@suse.de>
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-/*
- */
-
-struct cs_spec {
- int board_config;
- struct auto_pin_cfg autocfg;
- struct hda_multi_out multiout;
- struct snd_kcontrol *vmaster_sw;
- struct snd_kcontrol *vmaster_vol;
-
- hda_nid_t dac_nid[AUTO_CFG_MAX_OUTS];
- hda_nid_t slave_dig_outs[2];
-
- unsigned int input_idx[AUTO_PIN_LAST];
- unsigned int capsrc_idx[AUTO_PIN_LAST];
- hda_nid_t adc_nid[AUTO_PIN_LAST];
- unsigned int adc_idx[AUTO_PIN_LAST];
- unsigned int num_inputs;
- unsigned int cur_input;
- unsigned int automic_idx;
- hda_nid_t cur_adc;
- unsigned int cur_adc_stream_tag;
- unsigned int cur_adc_format;
- hda_nid_t dig_in;
-
- struct hda_bind_ctls *capture_bind[2];
-
- unsigned int gpio_mask;
- unsigned int gpio_dir;
- unsigned int gpio_data;
-
- struct hda_pcm pcm_rec[2]; /* PCM information */
-
- unsigned int hp_detect:1;
- unsigned int mic_detect:1;
-};
-
-/* available models */
-enum {
- CS420X_MBP53,
- CS420X_MBP55,
- CS420X_IMAC27,
- CS420X_AUTO,
- CS420X_MODELS
-};
-
-/* Vendor-specific processing widget */
-#define CS420X_VENDOR_NID 0x11
-#define CS_DIG_OUT1_PIN_NID 0x10
-#define CS_DIG_OUT2_PIN_NID 0x15
-#define CS_DMIC1_PIN_NID 0x12
-#define CS_DMIC2_PIN_NID 0x0e
-
-/* coef indices */
-#define IDX_SPDIF_STAT 0x0000
-#define IDX_SPDIF_CTL 0x0001
-#define IDX_ADC_CFG 0x0002
-/* SZC bitmask, 4 modes below:
- * 0 = immediate,
- * 1 = digital immediate, analog zero-cross
- * 2 = digtail & analog soft-ramp
- * 3 = digital soft-ramp, analog zero-cross
- */
-#define CS_COEF_ADC_SZC_MASK (3 << 0)
-#define CS_COEF_ADC_MIC_SZC_MODE (3 << 0) /* SZC setup for mic */
-#define CS_COEF_ADC_LI_SZC_MODE (3 << 0) /* SZC setup for line-in */
-/* PGA mode: 0 = differential, 1 = signle-ended */
-#define CS_COEF_ADC_MIC_PGA_MODE (1 << 5) /* PGA setup for mic */
-#define CS_COEF_ADC_LI_PGA_MODE (1 << 6) /* PGA setup for line-in */
-#define IDX_DAC_CFG 0x0003
-/* SZC bitmask, 4 modes below:
- * 0 = Immediate
- * 1 = zero-cross
- * 2 = soft-ramp
- * 3 = soft-ramp on zero-cross
- */
-#define CS_COEF_DAC_HP_SZC_MODE (3 << 0) /* nid 0x02 */
-#define CS_COEF_DAC_LO_SZC_MODE (3 << 2) /* nid 0x03 */
-#define CS_COEF_DAC_SPK_SZC_MODE (3 << 4) /* nid 0x04 */
-
-#define IDX_BEEP_CFG 0x0004
-/* 0x0008 - test reg key */
-/* 0x0009 - 0x0014 -> 12 test regs */
-/* 0x0015 - visibility reg */
-
-
-static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
-{
- snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
- AC_VERB_SET_COEF_INDEX, idx);
- return snd_hda_codec_read(codec, CS420X_VENDOR_NID, 0,
- AC_VERB_GET_PROC_COEF, 0);
-}
-
-static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
- unsigned int coef)
-{
- snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
- AC_VERB_SET_COEF_INDEX, idx);
- snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
- AC_VERB_SET_PROC_COEF, coef);
-}
-
-
-#define HP_EVENT 1
-#define MIC_EVENT 2
-
-/*
- * PCM callbacks
- */
-static int cs_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static int cs_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int cs_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Digital out
- */
-static int cs_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int cs_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int cs_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-static int cs_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Analog capture
- */
-static int cs_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- spec->cur_adc = spec->adc_nid[spec->cur_input];
- spec->cur_adc_stream_tag = stream_tag;
- spec->cur_adc_format = format;
- snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
- return 0;
-}
-
-static int cs_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cs_spec *spec = codec->spec;
- snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
- spec->cur_adc = 0;
- return 0;
-}
-
-/*
- */
-static struct hda_pcm_stream cs_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .open = cs_playback_pcm_open,
- .prepare = cs_playback_pcm_prepare,
- .cleanup = cs_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream cs_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .prepare = cs_capture_pcm_prepare,
- .cleanup = cs_capture_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream cs_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .ops = {
- .open = cs_dig_playback_pcm_open,
- .close = cs_dig_playback_pcm_close,
- .prepare = cs_dig_playback_pcm_prepare,
- .cleanup = cs_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream cs_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-static int cs_build_pcms(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->pcm_info = info;
- codec->num_pcms = 0;
-
- info->name = "Cirrus Analog";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cs_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dac_nid[0];
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
- spec->multiout.max_channels;
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = cs_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
- spec->adc_nid[spec->cur_input];
- codec->num_pcms++;
-
- if (!spec->multiout.dig_out_nid && !spec->dig_in)
- return 0;
-
- info++;
- info->name = "Cirrus Digital";
- info->pcm_type = spec->autocfg.dig_out_type[0];
- if (!info->pcm_type)
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- if (spec->multiout.dig_out_nid) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- cs_pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->multiout.dig_out_nid;
- }
- if (spec->dig_in) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- cs_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
- }
- codec->num_pcms++;
-
- return 0;
-}
-
-/*
- * parse codec topology
- */
-
-static hda_nid_t get_dac(struct hda_codec *codec, hda_nid_t pin)
-{
- hda_nid_t dac;
- if (!pin)
- return 0;
- if (snd_hda_get_connections(codec, pin, &dac, 1) != 1)
- return 0;
- return dac;
-}
-
-static int is_ext_mic(struct hda_codec *codec, unsigned int idx)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t pin = cfg->inputs[idx].pin;
- unsigned int val = snd_hda_query_pin_caps(codec, pin);
- if (!(val & AC_PINCAP_PRES_DETECT))
- return 0;
- val = snd_hda_codec_get_pincfg(codec, pin);
- return (snd_hda_get_input_pin_attr(val) != INPUT_PIN_ATTR_INT);
-}
-
-static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin,
- unsigned int *idxp)
-{
- int i;
- hda_nid_t nid;
-
- nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, nid++) {
- hda_nid_t pins[2];
- unsigned int type;
- int j, nums;
- type = (get_wcaps(codec, nid) & AC_WCAP_TYPE)
- >> AC_WCAP_TYPE_SHIFT;
- if (type != AC_WID_AUD_IN)
- continue;
- nums = snd_hda_get_connections(codec, nid, pins,
- ARRAY_SIZE(pins));
- if (nums <= 0)
- continue;
- for (j = 0; j < nums; j++) {
- if (pins[j] == pin) {
- *idxp = j;
- return nid;
- }
- }
- }
- return 0;
-}
-
-static int is_active_pin(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int val;
- val = snd_hda_codec_get_pincfg(codec, nid);
- return (get_defcfg_connect(val) != AC_JACK_PORT_NONE);
-}
-
-static int parse_output(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, extra_nids;
- hda_nid_t dac;
-
- for (i = 0; i < cfg->line_outs; i++) {
- dac = get_dac(codec, cfg->line_out_pins[i]);
- if (!dac)
- break;
- spec->dac_nid[i] = dac;
- }
- spec->multiout.num_dacs = i;
- spec->multiout.dac_nids = spec->dac_nid;
- spec->multiout.max_channels = i * 2;
-
- /* add HP and speakers */
- extra_nids = 0;
- for (i = 0; i < cfg->hp_outs; i++) {
- dac = get_dac(codec, cfg->hp_pins[i]);
- if (!dac)
- break;
- if (!i)
- spec->multiout.hp_nid = dac;
- else
- spec->multiout.extra_out_nid[extra_nids++] = dac;
- }
- for (i = 0; i < cfg->speaker_outs; i++) {
- dac = get_dac(codec, cfg->speaker_pins[i]);
- if (!dac)
- break;
- spec->multiout.extra_out_nid[extra_nids++] = dac;
- }
-
- if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
- cfg->speaker_outs = cfg->line_outs;
- memcpy(cfg->speaker_pins, cfg->line_out_pins,
- sizeof(cfg->speaker_pins));
- cfg->line_outs = 0;
- }
-
- return 0;
-}
-
-static int parse_input(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t pin = cfg->inputs[i].pin;
- spec->input_idx[spec->num_inputs] = i;
- spec->capsrc_idx[i] = spec->num_inputs++;
- spec->cur_input = i;
- spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]);
- }
- if (!spec->num_inputs)
- return 0;
-
- /* check whether the automatic mic switch is available */
- if (spec->num_inputs == 2 &&
- cfg->inputs[0].type == AUTO_PIN_MIC &&
- cfg->inputs[1].type == AUTO_PIN_MIC) {
- if (is_ext_mic(codec, cfg->inputs[0].pin)) {
- if (!is_ext_mic(codec, cfg->inputs[1].pin)) {
- spec->mic_detect = 1;
- spec->automic_idx = 0;
- }
- } else {
- if (is_ext_mic(codec, cfg->inputs[1].pin)) {
- spec->mic_detect = 1;
- spec->automic_idx = 1;
- }
- }
- }
- return 0;
-}
-
-
-static int parse_digital_output(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
-
- if (!cfg->dig_outs)
- return 0;
- if (snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) < 1)
- return 0;
- spec->multiout.dig_out_nid = nid;
- spec->multiout.share_spdif = 1;
- if (cfg->dig_outs > 1 &&
- snd_hda_get_connections(codec, cfg->dig_out_pins[1], &nid, 1) > 0) {
- spec->slave_dig_outs[0] = nid;
- codec->slave_dig_outs = spec->slave_dig_outs;
- }
- return 0;
-}
-
-static int parse_digital_input(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int idx;
-
- if (cfg->dig_in_pin)
- spec->dig_in = get_adc(codec, cfg->dig_in_pin, &idx);
- return 0;
-}
-
-/*
- * create mixer controls
- */
-
-static const char *dir_sfx[2] = { "Playback", "Capture" };
-
-static int add_mute(struct hda_codec *codec, const char *name, int index,
- unsigned int pval, int dir, struct snd_kcontrol **kctlp)
-{
- char tmp[44];
- struct snd_kcontrol_new knew =
- HDA_CODEC_MUTE_IDX(tmp, index, 0, 0, HDA_OUTPUT);
- knew.private_value = pval;
- snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]);
- *kctlp = snd_ctl_new1(&knew, codec);
- (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG;
- return snd_hda_ctl_add(codec, 0, *kctlp);
-}
-
-static int add_volume(struct hda_codec *codec, const char *name,
- int index, unsigned int pval, int dir,
- struct snd_kcontrol **kctlp)
-{
- char tmp[32];
- struct snd_kcontrol_new knew =
- HDA_CODEC_VOLUME_IDX(tmp, index, 0, 0, HDA_OUTPUT);
- knew.private_value = pval;
- snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]);
- *kctlp = snd_ctl_new1(&knew, codec);
- (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG;
- return snd_hda_ctl_add(codec, 0, *kctlp);
-}
-
-static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
-{
- unsigned int caps;
-
- /* set the upper-limit for mixer amp to 0dB */
- caps = query_amp_caps(codec, dac, HDA_OUTPUT);
- caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT);
- caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f)
- << AC_AMPCAP_NUM_STEPS_SHIFT;
- snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps);
-}
-
-static int add_vmaster(struct hda_codec *codec, hda_nid_t dac)
-{
- struct cs_spec *spec = codec->spec;
- unsigned int tlv[4];
- int err;
-
- spec->vmaster_sw =
- snd_ctl_make_virtual_master("Master Playback Switch", NULL);
- err = snd_hda_ctl_add(codec, dac, spec->vmaster_sw);
- if (err < 0)
- return err;
-
- snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv);
- spec->vmaster_vol =
- snd_ctl_make_virtual_master("Master Playback Volume", tlv);
- err = snd_hda_ctl_add(codec, dac, spec->vmaster_vol);
- if (err < 0)
- return err;
- return 0;
-}
-
-static int add_output(struct hda_codec *codec, hda_nid_t dac, int idx,
- int num_ctls, int type)
-{
- struct cs_spec *spec = codec->spec;
- const char *name;
- int err, index;
- struct snd_kcontrol *kctl;
- static char *speakers[] = {
- "Front Speaker", "Surround Speaker", "Bass Speaker"
- };
- static char *line_outs[] = {
- "Front Line-Out", "Surround Line-Out", "Bass Line-Out"
- };
-
- fix_volume_caps(codec, dac);
- if (!spec->vmaster_sw) {
- err = add_vmaster(codec, dac);
- if (err < 0)
- return err;
- }
-
- index = 0;
- switch (type) {
- case AUTO_PIN_HP_OUT:
- name = "Headphone";
- index = idx;
- break;
- case AUTO_PIN_SPEAKER_OUT:
- if (num_ctls > 1)
- name = speakers[idx];
- else
- name = "Speaker";
- break;
- default:
- if (num_ctls > 1)
- name = line_outs[idx];
- else
- name = "Line-Out";
- break;
- }
-
- err = add_mute(codec, name, index,
- HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
- if (err < 0)
- return err;
- err = snd_ctl_add_slave(spec->vmaster_sw, kctl);
- if (err < 0)
- return err;
-
- err = add_volume(codec, name, index,
- HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
- if (err < 0)
- return err;
- err = snd_ctl_add_slave(spec->vmaster_vol, kctl);
- if (err < 0)
- return err;
-
- return 0;
-}
-
-static int build_output(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, err;
-
- for (i = 0; i < cfg->line_outs; i++) {
- err = add_output(codec, get_dac(codec, cfg->line_out_pins[i]),
- i, cfg->line_outs, cfg->line_out_type);
- if (err < 0)
- return err;
- }
- for (i = 0; i < cfg->hp_outs; i++) {
- err = add_output(codec, get_dac(codec, cfg->hp_pins[i]),
- i, cfg->hp_outs, AUTO_PIN_HP_OUT);
- if (err < 0)
- return err;
- }
- for (i = 0; i < cfg->speaker_outs; i++) {
- err = add_output(codec, get_dac(codec, cfg->speaker_pins[i]),
- i, cfg->speaker_outs, AUTO_PIN_SPEAKER_OUT);
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-/*
- */
-
-static struct snd_kcontrol_new cs_capture_ctls[] = {
- HDA_BIND_SW("Capture Switch", 0),
- HDA_BIND_VOL("Capture Volume", 0),
-};
-
-static int change_cur_input(struct hda_codec *codec, unsigned int idx,
- int force)
-{
- struct cs_spec *spec = codec->spec;
-
- if (spec->cur_input == idx && !force)
- return 0;
- if (spec->cur_adc && spec->cur_adc != spec->adc_nid[idx]) {
- /* stream is running, let's swap the current ADC */
- __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
- spec->cur_adc = spec->adc_nid[idx];
- snd_hda_codec_setup_stream(codec, spec->cur_adc,
- spec->cur_adc_stream_tag, 0,
- spec->cur_adc_format);
- }
- snd_hda_codec_write(codec, spec->cur_adc, 0,
- AC_VERB_SET_CONNECT_SEL,
- spec->adc_idx[idx]);
- spec->cur_input = idx;
- return 1;
-}
-
-static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int idx;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = spec->num_inputs;
- if (uinfo->value.enumerated.item >= spec->num_inputs)
- uinfo->value.enumerated.item = spec->num_inputs - 1;
- idx = spec->input_idx[uinfo->value.enumerated.item];
- strcpy(uinfo->value.enumerated.name,
- hda_get_input_pin_label(codec, cfg->inputs[idx].pin, 1));
- return 0;
-}
-
-static int cs_capture_source_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cs_spec *spec = codec->spec;
- ucontrol->value.enumerated.item[0] = spec->capsrc_idx[spec->cur_input];
- return 0;
-}
-
-static int cs_capture_source_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cs_spec *spec = codec->spec;
- unsigned int idx = ucontrol->value.enumerated.item[0];
-
- if (idx >= spec->num_inputs)
- return -EINVAL;
- idx = spec->input_idx[idx];
- return change_cur_input(codec, idx, 0);
-}
-
-static struct snd_kcontrol_new cs_capture_source = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .info = cs_capture_source_info,
- .get = cs_capture_source_get,
- .put = cs_capture_source_put,
-};
-
-static struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec,
- struct hda_ctl_ops *ops)
-{
- struct cs_spec *spec = codec->spec;
- struct hda_bind_ctls *bind;
- int i, n;
-
- bind = kzalloc(sizeof(*bind) + sizeof(long) * (spec->num_inputs + 1),
- GFP_KERNEL);
- if (!bind)
- return NULL;
- bind->ops = ops;
- n = 0;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!spec->adc_nid[i])
- continue;
- bind->values[n++] =
- HDA_COMPOSE_AMP_VAL(spec->adc_nid[i], 3,
- spec->adc_idx[i], HDA_INPUT);
- }
- return bind;
-}
-
-/* add a (input-boost) volume control to the given input pin */
-static int add_input_volume_control(struct hda_codec *codec,
- struct auto_pin_cfg *cfg,
- int item)
-{
- hda_nid_t pin = cfg->inputs[item].pin;
- u32 caps;
- const char *label;
- struct snd_kcontrol *kctl;
-
- if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP))
- return 0;
- caps = query_amp_caps(codec, pin, HDA_INPUT);
- caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
- if (caps <= 1)
- return 0;
- label = hda_get_autocfg_input_label(codec, cfg, item);
- return add_volume(codec, label, 0,
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl);
-}
-
-static int build_input(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- int i, err;
-
- if (!spec->num_inputs)
- return 0;
-
- /* make bind-capture */
- spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw);
- spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol);
- for (i = 0; i < 2; i++) {
- struct snd_kcontrol *kctl;
- int n;
- if (!spec->capture_bind[i])
- return -ENOMEM;
- kctl = snd_ctl_new1(&cs_capture_ctls[i], codec);
- if (!kctl)
- return -ENOMEM;
- kctl->private_value = (long)spec->capture_bind[i];
- err = snd_hda_ctl_add(codec, 0, kctl);
- if (err < 0)
- return err;
- for (n = 0; n < AUTO_PIN_LAST; n++) {
- if (!spec->adc_nid[n])
- continue;
- err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[n]);
- if (err < 0)
- return err;
- }
- }
-
- if (spec->num_inputs > 1 && !spec->mic_detect) {
- err = snd_hda_ctl_add(codec, 0,
- snd_ctl_new1(&cs_capture_source, codec));
- if (err < 0)
- return err;
- }
-
- for (i = 0; i < spec->num_inputs; i++) {
- err = add_input_volume_control(codec, &spec->autocfg, i);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-/*
- */
-
-static int build_digital_output(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- int err;
-
- if (!spec->multiout.dig_out_nid)
- return 0;
-
- err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
- if (err < 0)
- return err;
- return 0;
-}
-
-static int build_digital_input(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- if (spec->dig_in)
- return snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
- return 0;
-}
-
-/*
- * auto-mute and auto-mic switching
- */
-
-static void cs_automute(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int caps, hp_present;
- hda_nid_t nid;
- int i;
-
- hp_present = 0;
- for (i = 0; i < cfg->hp_outs; i++) {
- nid = cfg->hp_pins[i];
- caps = snd_hda_query_pin_caps(codec, nid);
- if (!(caps & AC_PINCAP_PRES_DETECT))
- continue;
- hp_present = snd_hda_jack_detect(codec, nid);
- if (hp_present)
- break;
- }
- for (i = 0; i < cfg->speaker_outs; i++) {
- nid = cfg->speaker_pins[i];
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- hp_present ? 0 : PIN_OUT);
- }
- if (spec->board_config == CS420X_MBP53 ||
- spec->board_config == CS420X_MBP55 ||
- spec->board_config == CS420X_IMAC27) {
- unsigned int gpio = hp_present ? 0x02 : 0x08;
- snd_hda_codec_write(codec, 0x01, 0,
- AC_VERB_SET_GPIO_DATA, gpio);
- }
-}
-
-static void cs_automic(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
- unsigned int present;
-
- nid = cfg->inputs[spec->automic_idx].pin;
- present = snd_hda_jack_detect(codec, nid);
- if (present)
- change_cur_input(codec, spec->automic_idx, 0);
- else
- change_cur_input(codec, !spec->automic_idx, 0);
-}
-
-/*
- */
-
-static void init_output(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- /* mute first */
- for (i = 0; i < spec->multiout.num_dacs; i++)
- snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
- if (spec->multiout.hp_nid)
- snd_hda_codec_write(codec, spec->multiout.hp_nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
- for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) {
- if (!spec->multiout.extra_out_nid[i])
- break;
- snd_hda_codec_write(codec, spec->multiout.extra_out_nid[i], 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
- }
-
- /* set appropriate pin controls */
- for (i = 0; i < cfg->line_outs; i++)
- snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- for (i = 0; i < cfg->hp_outs; i++) {
- hda_nid_t nid = cfg->hp_pins[i];
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
- if (!cfg->speaker_outs)
- continue;
- if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | HP_EVENT);
- spec->hp_detect = 1;
- }
- }
- for (i = 0; i < cfg->speaker_outs; i++)
- snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- if (spec->hp_detect)
- cs_automute(codec);
-}
-
-static void init_input(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int coef;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- unsigned int ctl;
- hda_nid_t pin = cfg->inputs[i].pin;
- if (!spec->adc_nid[i])
- continue;
- /* set appropriate pin control and mute first */
- ctl = PIN_IN;
- if (cfg->inputs[i].type == AUTO_PIN_MIC) {
- unsigned int caps = snd_hda_query_pin_caps(codec, pin);
- caps >>= AC_PINCAP_VREF_SHIFT;
- if (caps & AC_PINCAP_VREF_80)
- ctl = PIN_VREF80;
- }
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
- snd_hda_codec_write(codec, spec->adc_nid[i], 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_MUTE(spec->adc_idx[i]));
- if (spec->mic_detect && spec->automic_idx == i)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | MIC_EVENT);
- }
- change_cur_input(codec, spec->cur_input, 1);
- if (spec->mic_detect)
- cs_automic(codec);
-
- coef = 0x000a; /* ADC1/2 - Digital and Analog Soft Ramp */
- if (is_active_pin(codec, CS_DMIC2_PIN_NID))
- coef |= 0x0500; /* DMIC2 enable 2 channels, disable GPIO1 */
- if (is_active_pin(codec, CS_DMIC1_PIN_NID))
- coef |= 0x1800; /* DMIC1 enable 2 channels, disable GPIO0
- * No effect if SPDIF_OUT2 is selected in
- * IDX_SPDIF_CTL.
- */
- cs_vendor_coef_set(codec, IDX_ADC_CFG, coef);
-}
-
-static struct hda_verb cs_coef_init_verbs[] = {
- {0x11, AC_VERB_SET_PROC_STATE, 1},
- {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG},
- {0x11, AC_VERB_SET_PROC_COEF,
- (0x002a /* DAC1/2/3 SZCMode Soft Ramp */
- | 0x0040 /* Mute DACs on FIFO error */
- | 0x1000 /* Enable DACs High Pass Filter */
- | 0x0400 /* Disable Coefficient Auto increment */
- )},
- /* Beep */
- {0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG},
- {0x11, AC_VERB_SET_PROC_COEF, 0x0007}, /* Enable Beep thru DAC1/2/3 */
-
- {} /* terminator */
-};
-
-/* Errata: CS4207 rev C0/C1/C2 Silicon
- *
- * http://www.cirrus.com/en/pubs/errata/ER880C3.pdf
- *
- * 6. At high temperature (TA > +85°C), the digital supply current (IVD)
- * may be excessive (up to an additional 200 μA), which is most easily
- * observed while the part is being held in reset (RESET# active low).
- *
- * Root Cause: At initial powerup of the device, the logic that drives
- * the clock and write enable to the S/PDIF SRC RAMs is not properly
- * initialized.
- * Certain random patterns will cause a steady leakage current in those
- * RAM cells. The issue will resolve once the SRCs are used (turned on).
- *
- * Workaround: The following verb sequence briefly turns on the S/PDIF SRC
- * blocks, which will alleviate the issue.
- */
-
-static struct hda_verb cs_errata_init_verbs[] = {
- {0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */
- {0x11, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */
-
- {0x11, AC_VERB_SET_COEF_INDEX, 0x0008},
- {0x11, AC_VERB_SET_PROC_COEF, 0x9999},
- {0x11, AC_VERB_SET_COEF_INDEX, 0x0017},
- {0x11, AC_VERB_SET_PROC_COEF, 0xa412},
- {0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
- {0x11, AC_VERB_SET_PROC_COEF, 0x0009},
-
- {0x07, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Rx: D0 */
- {0x08, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Tx: D0 */
-
- {0x11, AC_VERB_SET_COEF_INDEX, 0x0017},
- {0x11, AC_VERB_SET_PROC_COEF, 0x2412},
- {0x11, AC_VERB_SET_COEF_INDEX, 0x0008},
- {0x11, AC_VERB_SET_PROC_COEF, 0x0000},
- {0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
- {0x11, AC_VERB_SET_PROC_COEF, 0x0008},
- {0x11, AC_VERB_SET_PROC_STATE, 0x00},
-
- {0x07, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Rx: D3 */
- {0x08, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Tx: D3 */
- /*{0x01, AC_VERB_SET_POWER_STATE, 0x03},*/ /* AFG: D3 This is already handled */
-
- {} /* terminator */
-};
-
-/* SPDIF setup */
-static void init_digital(struct hda_codec *codec)
-{
- unsigned int coef;
-
- coef = 0x0002; /* SRC_MUTE soft-mute on SPDIF (if no lock) */
- coef |= 0x0008; /* Replace with mute on error */
- if (is_active_pin(codec, CS_DIG_OUT2_PIN_NID))
- coef |= 0x4000; /* RX to TX1 or TX2 Loopthru / SPDIF2
- * SPDIF_OUT2 is shared with GPIO1 and
- * DMIC_SDA2.
- */
- cs_vendor_coef_set(codec, IDX_SPDIF_CTL, coef);
-}
-
-static int cs_init(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
-
- /* init_verb sequence for C0/C1/C2 errata*/
- snd_hda_sequence_write(codec, cs_errata_init_verbs);
-
- snd_hda_sequence_write(codec, cs_coef_init_verbs);
-
- if (spec->gpio_mask) {
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
- spec->gpio_mask);
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
- spec->gpio_dir);
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
- spec->gpio_data);
- }
-
- init_output(codec);
- init_input(codec);
- init_digital(codec);
- return 0;
-}
-
-static int cs_build_controls(struct hda_codec *codec)
-{
- int err;
-
- err = build_output(codec);
- if (err < 0)
- return err;
- err = build_input(codec);
- if (err < 0)
- return err;
- err = build_digital_output(codec);
- if (err < 0)
- return err;
- err = build_digital_input(codec);
- if (err < 0)
- return err;
- return cs_init(codec);
-}
-
-static void cs_free(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- kfree(spec->capture_bind[0]);
- kfree(spec->capture_bind[1]);
- kfree(codec->spec);
-}
-
-static void cs_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- switch ((res >> 26) & 0x7f) {
- case HP_EVENT:
- cs_automute(codec);
- break;
- case MIC_EVENT:
- cs_automic(codec);
- break;
- }
-}
-
-static struct hda_codec_ops cs_patch_ops = {
- .build_controls = cs_build_controls,
- .build_pcms = cs_build_pcms,
- .init = cs_init,
- .free = cs_free,
- .unsol_event = cs_unsol_event,
-};
-
-static int cs_parse_auto_config(struct hda_codec *codec)
-{
- struct cs_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
-
- err = parse_output(codec);
- if (err < 0)
- return err;
- err = parse_input(codec);
- if (err < 0)
- return err;
- err = parse_digital_output(codec);
- if (err < 0)
- return err;
- err = parse_digital_input(codec);
- if (err < 0)
- return err;
- return 0;
-}
-
-static const char *cs420x_models[CS420X_MODELS] = {
- [CS420X_MBP53] = "mbp53",
- [CS420X_MBP55] = "mbp55",
- [CS420X_IMAC27] = "imac27",
- [CS420X_AUTO] = "auto",
-};
-
-
-static struct snd_pci_quirk cs420x_cfg_tbl[] = {
- SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53),
- SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
- SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55),
- SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),
- {} /* terminator */
-};
-
-struct cs_pincfg {
- hda_nid_t nid;
- u32 val;
-};
-
-static struct cs_pincfg mbp53_pincfgs[] = {
- { 0x09, 0x012b4050 },
- { 0x0a, 0x90100141 },
- { 0x0b, 0x90100140 },
- { 0x0c, 0x018b3020 },
- { 0x0d, 0x90a00110 },
- { 0x0e, 0x400000f0 },
- { 0x0f, 0x01cbe030 },
- { 0x10, 0x014be060 },
- { 0x12, 0x400000f0 },
- { 0x15, 0x400000f0 },
- {} /* terminator */
-};
-
-static struct cs_pincfg mbp55_pincfgs[] = {
- { 0x09, 0x012b4030 },
- { 0x0a, 0x90100121 },
- { 0x0b, 0x90100120 },
- { 0x0c, 0x400000f0 },
- { 0x0d, 0x90a00110 },
- { 0x0e, 0x400000f0 },
- { 0x0f, 0x400000f0 },
- { 0x10, 0x014be040 },
- { 0x12, 0x400000f0 },
- { 0x15, 0x400000f0 },
- {} /* terminator */
-};
-
-static struct cs_pincfg imac27_pincfgs[] = {
- { 0x09, 0x012b4050 },
- { 0x0a, 0x90100140 },
- { 0x0b, 0x90100142 },
- { 0x0c, 0x018b3020 },
- { 0x0d, 0x90a00110 },
- { 0x0e, 0x400000f0 },
- { 0x0f, 0x01cbe030 },
- { 0x10, 0x014be060 },
- { 0x12, 0x01ab9070 },
- { 0x15, 0x400000f0 },
- {} /* terminator */
-};
-
-static struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = {
- [CS420X_MBP53] = mbp53_pincfgs,
- [CS420X_MBP55] = mbp55_pincfgs,
- [CS420X_IMAC27] = imac27_pincfgs,
-};
-
-static void fix_pincfg(struct hda_codec *codec, int model)
-{
- const struct cs_pincfg *cfg = cs_pincfgs[model];
- if (!cfg)
- return;
- for (; cfg->nid; cfg++)
- snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
-}
-
-
-static int patch_cs420x(struct hda_codec *codec)
-{
- struct cs_spec *spec;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
- codec->spec = spec;
-
- spec->board_config =
- snd_hda_check_board_config(codec, CS420X_MODELS,
- cs420x_models, cs420x_cfg_tbl);
- if (spec->board_config >= 0)
- fix_pincfg(codec, spec->board_config);
-
- switch (spec->board_config) {
- case CS420X_IMAC27:
- case CS420X_MBP53:
- case CS420X_MBP55:
- /* GPIO1 = headphones */
- /* GPIO3 = speakers */
- spec->gpio_mask = 0x0a;
- spec->gpio_dir = 0x0a;
- break;
- }
-
- err = cs_parse_auto_config(codec);
- if (err < 0)
- goto error;
-
- codec->patch_ops = cs_patch_ops;
-
- return 0;
-
- error:
- kfree(codec->spec);
- codec->spec = NULL;
- return err;
-}
-
-
-/*
- * patch entries
- */
-static struct hda_codec_preset snd_hda_preset_cirrus[] = {
- { .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x },
- { .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x },
- {} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:10134206");
-MODULE_ALIAS("snd-hda-codec-id:10134207");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
-
-static struct hda_codec_preset_list cirrus_list = {
- .preset = snd_hda_preset_cirrus,
- .owner = THIS_MODULE,
-};
-
-static int __init patch_cirrus_init(void)
-{
- return snd_hda_add_codec_preset(&cirrus_list);
-}
-
-static void __exit patch_cirrus_exit(void)
-{
- snd_hda_delete_codec_preset(&cirrus_list);
-}
-
-module_init(patch_cirrus_init)
-module_exit(patch_cirrus_exit)
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
deleted file mode 100644
index ff60908f4554..000000000000
--- a/sound/pci/hda/patch_cmedia.c
+++ /dev/null
@@ -1,776 +0,0 @@
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * HD audio interface patch for C-Media CMI9880
- *
- * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-#define NUM_PINS 11
-
-
-/* board config type */
-enum {
- CMI_MINIMAL, /* back 3-jack */
- CMI_MIN_FP, /* back 3-jack + front-panel 2-jack */
- CMI_FULL, /* back 6-jack + front-panel 2-jack */
- CMI_FULL_DIG, /* back 6-jack + front-panel 2-jack + digital I/O */
- CMI_ALLOUT, /* back 5-jack + front-panel 2-jack + digital out */
- CMI_AUTO, /* let driver guess it */
- CMI_MODELS
-};
-
-struct cmi_spec {
- int board_config;
- unsigned int no_line_in: 1; /* no line-in (5-jack) */
- unsigned int front_panel: 1; /* has front-panel 2-jack */
-
- /* playback */
- struct hda_multi_out multiout;
- hda_nid_t dac_nids[AUTO_CFG_MAX_OUTS]; /* NID for each DAC */
- int num_dacs;
-
- /* capture */
- hda_nid_t *adc_nids;
- hda_nid_t dig_in_nid;
-
- /* capture source */
- const struct hda_input_mux *input_mux;
- unsigned int cur_mux[2];
-
- /* channel mode */
- int num_channel_modes;
- const struct hda_channel_mode *channel_modes;
-
- struct hda_pcm pcm_rec[2]; /* PCM information */
-
- /* pin default configuration */
- hda_nid_t pin_nid[NUM_PINS];
- unsigned int def_conf[NUM_PINS];
- unsigned int pin_def_confs;
-
- /* multichannel pins */
- struct hda_verb multi_init[9]; /* 2 verbs for each pin + terminator */
-};
-
-/*
- * input MUX
- */
-static int cmi_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cmi_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->input_mux, uinfo);
-}
-
-static int cmi_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cmi_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
- return 0;
-}
-
-static int cmi_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cmi_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
- spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]);
-}
-
-/*
- * shared line-in, mic for surrounds
- */
-
-/* 3-stack / 2 channel */
-static struct hda_verb cmi9880_ch2_init[] = {
- /* set line-in PIN for input */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- /* set mic PIN for input, also enable vref */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- /* route front PCM (DAC1) to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- {}
-};
-
-/* 3-stack / 6 channel */
-static struct hda_verb cmi9880_ch6_init[] = {
- /* set line-in PIN for output */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- /* set mic PIN for output */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- /* route front PCM (DAC1) to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- {}
-};
-
-/* 3-stack+front / 8 channel */
-static struct hda_verb cmi9880_ch8_init[] = {
- /* set line-in PIN for output */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- /* set mic PIN for output */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- /* route rear-surround PCM (DAC4) to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x03 },
- {}
-};
-
-static struct hda_channel_mode cmi9880_channel_modes[3] = {
- { 2, cmi9880_ch2_init },
- { 6, cmi9880_ch6_init },
- { 8, cmi9880_ch8_init },
-};
-
-static int cmi_ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cmi_spec *spec = codec->spec;
- return snd_hda_ch_mode_info(codec, uinfo, spec->channel_modes,
- spec->num_channel_modes);
-}
-
-static int cmi_ch_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cmi_spec *spec = codec->spec;
- return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_modes,
- spec->num_channel_modes, spec->multiout.max_channels);
-}
-
-static int cmi_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct cmi_spec *spec = codec->spec;
- return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_modes,
- spec->num_channel_modes, &spec->multiout.max_channels);
-}
-
-/*
- */
-static struct snd_kcontrol_new cmi9880_basic_mixer[] = {
- /* CMI9880 has no playback volumes! */
- HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), /* front */
- HDA_CODEC_MUTE("Surround Playback Switch", 0x04, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Side Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = cmi_mux_enum_info,
- .get = cmi_mux_enum_get,
- .put = cmi_mux_enum_put,
- },
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x23, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x23, 0, HDA_OUTPUT),
- { } /* end */
-};
-
-/*
- * shared I/O pins
- */
-static struct snd_kcontrol_new cmi9880_ch_mode_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = cmi_ch_mode_info,
- .get = cmi_ch_mode_get,
- .put = cmi_ch_mode_put,
- },
- { } /* end */
-};
-
-/* AUD-in selections:
- * 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x1f 0x20
- */
-static struct hda_input_mux cmi9880_basic_mux = {
- .num_items = 4,
- .items = {
- { "Front Mic", 0x5 },
- { "Rear Mic", 0x2 },
- { "Line", 0x1 },
- { "CD", 0x7 },
- }
-};
-
-static struct hda_input_mux cmi9880_no_line_mux = {
- .num_items = 3,
- .items = {
- { "Front Mic", 0x5 },
- { "Rear Mic", 0x2 },
- { "CD", 0x7 },
- }
-};
-
-/* front, rear, clfe, rear_surr */
-static hda_nid_t cmi9880_dac_nids[4] = {
- 0x03, 0x04, 0x05, 0x06
-};
-/* ADC0, ADC1 */
-static hda_nid_t cmi9880_adc_nids[2] = {
- 0x08, 0x09
-};
-
-#define CMI_DIG_OUT_NID 0x07
-#define CMI_DIG_IN_NID 0x0a
-
-/*
- */
-static struct hda_verb cmi9880_basic_init[] = {
- /* port-D for line out (rear panel) */
- { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- /* port-E for HP out (front panel) */
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- /* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-A for surround (rear panel) */
- { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- /* port-G for CLFE (rear panel) */
- { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x02 },
- /* port-H for side (rear panel) */
- { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { 0x20, AC_VERB_SET_CONNECT_SEL, 0x01 },
- /* port-C for line-in (rear panel) */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- /* port-B for mic-in (rear panel) with vref */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- /* port-F for mic-in (front panel) with vref */
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- /* CD-in */
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- /* route front mic to ADC1/2 */
- { 0x08, AC_VERB_SET_CONNECT_SEL, 0x05 },
- { 0x09, AC_VERB_SET_CONNECT_SEL, 0x05 },
- {} /* terminator */
-};
-
-static struct hda_verb cmi9880_allout_init[] = {
- /* port-D for line out (rear panel) */
- { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- /* port-E for HP out (front panel) */
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- /* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-A for side (rear panel) */
- { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- /* port-G for CLFE (rear panel) */
- { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x02 },
- /* port-H for side (rear panel) */
- { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { 0x20, AC_VERB_SET_CONNECT_SEL, 0x01 },
- /* port-C for surround (rear panel) */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- /* port-B for mic-in (rear panel) with vref */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- /* port-F for mic-in (front panel) with vref */
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- /* CD-in */
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- /* route front mic to ADC1/2 */
- { 0x08, AC_VERB_SET_CONNECT_SEL, 0x05 },
- { 0x09, AC_VERB_SET_CONNECT_SEL, 0x05 },
- {} /* terminator */
-};
-
-/*
- */
-static int cmi9880_build_controls(struct hda_codec *codec)
-{
- struct cmi_spec *spec = codec->spec;
- struct snd_kcontrol *kctl;
- int i, err;
-
- err = snd_hda_add_new_ctls(codec, cmi9880_basic_mixer);
- if (err < 0)
- return err;
- if (spec->channel_modes) {
- err = snd_hda_add_new_ctls(codec, cmi9880_ch_mode_mixer);
- if (err < 0)
- return err;
- }
- if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- if (spec->dig_in_nid) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
- if (err < 0)
- return err;
- }
-
- /* assign Capture Source enums to NID */
- kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
- for (i = 0; kctl && i < kctl->count; i++) {
- err = snd_hda_add_nid(codec, kctl, i, spec->adc_nids[i]);
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-/* fill in the multi_dac_nids table, which will decide
- which audio widget to use for each channel */
-static int cmi9880_fill_multi_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
-{
- struct cmi_spec *spec = codec->spec;
- hda_nid_t nid;
- int assigned[4];
- int i, j;
-
- /* clear the table, only one c-media dac assumed here */
- memset(spec->dac_nids, 0, sizeof(spec->dac_nids));
- memset(assigned, 0, sizeof(assigned));
- /* check the pins we found */
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- /* nid 0x0b~0x0e is hardwired to audio widget 0x3~0x6 */
- if (nid >= 0x0b && nid <= 0x0e) {
- spec->dac_nids[i] = (nid - 0x0b) + 0x03;
- assigned[nid - 0x0b] = 1;
- }
- }
- /* left pin can be connect to any audio widget */
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- if (nid <= 0x0e)
- continue;
- /* search for an empty channel */
- for (j = 0; j < cfg->line_outs; j++) {
- if (! assigned[j]) {
- spec->dac_nids[i] = j + 0x03;
- assigned[j] = 1;
- break;
- }
- }
- }
- spec->num_dacs = cfg->line_outs;
- return 0;
-}
-
-/* create multi_init table, which is used for multichannel initialization */
-static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
-{
- struct cmi_spec *spec = codec->spec;
- hda_nid_t nid;
- int i, j, k, len;
-
- /* clear the table, only one c-media dac assumed here */
- memset(spec->multi_init, 0, sizeof(spec->multi_init));
- for (j = 0, i = 0; i < cfg->line_outs; i++) {
- hda_nid_t conn[4];
- nid = cfg->line_out_pins[i];
- /* set as output */
- spec->multi_init[j].nid = nid;
- spec->multi_init[j].verb = AC_VERB_SET_PIN_WIDGET_CONTROL;
- spec->multi_init[j].param = PIN_OUT;
- j++;
- if (nid > 0x0e) {
- /* set connection */
- spec->multi_init[j].nid = nid;
- spec->multi_init[j].verb = AC_VERB_SET_CONNECT_SEL;
- spec->multi_init[j].param = 0;
- /* find the index in connect list */
- len = snd_hda_get_connections(codec, nid, conn, 4);
- for (k = 0; k < len; k++)
- if (conn[k] == spec->dac_nids[i]) {
- spec->multi_init[j].param = k;
- break;
- }
- j++;
- }
- }
- return 0;
-}
-
-static int cmi9880_init(struct hda_codec *codec)
-{
- struct cmi_spec *spec = codec->spec;
- if (spec->board_config == CMI_ALLOUT)
- snd_hda_sequence_write(codec, cmi9880_allout_init);
- else
- snd_hda_sequence_write(codec, cmi9880_basic_init);
- if (spec->board_config == CMI_AUTO)
- snd_hda_sequence_write(codec, spec->multi_init);
- return 0;
-}
-
-/*
- * Analog playback callbacks
- */
-static int cmi9880_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cmi_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static int cmi9880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct cmi_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-static int cmi9880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cmi_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Digital out
- */
-static int cmi9880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int cmi9880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int cmi9880_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct cmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-/*
- * Analog capture
- */
-static int cmi9880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct cmi_spec *spec = codec->spec;
-
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
- stream_tag, 0, format);
- return 0;
-}
-
-static int cmi9880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct cmi_spec *spec = codec->spec;
-
- snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
- return 0;
-}
-
-
-/*
- */
-static struct hda_pcm_stream cmi9880_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- .nid = 0x03, /* NID to query formats and rates */
- .ops = {
- .open = cmi9880_playback_pcm_open,
- .prepare = cmi9880_playback_pcm_prepare,
- .cleanup = cmi9880_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream cmi9880_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x08, /* NID to query formats and rates */
- .ops = {
- .prepare = cmi9880_capture_pcm_prepare,
- .cleanup = cmi9880_capture_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream cmi9880_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in cmi9880_build_pcms */
- .ops = {
- .open = cmi9880_dig_playback_pcm_open,
- .close = cmi9880_dig_playback_pcm_close,
- .prepare = cmi9880_dig_playback_pcm_prepare
- },
-};
-
-static struct hda_pcm_stream cmi9880_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in cmi9880_build_pcms */
-};
-
-static int cmi9880_build_pcms(struct hda_codec *codec)
-{
- struct cmi_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = "CMI9880";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cmi9880_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = cmi9880_pcm_analog_capture;
-
- if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
- codec->num_pcms++;
- info++;
- info->name = "CMI9880 Digital";
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- if (spec->multiout.dig_out_nid) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cmi9880_pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
- }
- if (spec->dig_in_nid) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = cmi9880_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
- }
- }
-
- return 0;
-}
-
-static void cmi9880_free(struct hda_codec *codec)
-{
- kfree(codec->spec);
-}
-
-/*
- */
-
-static const char *cmi9880_models[CMI_MODELS] = {
- [CMI_MINIMAL] = "minimal",
- [CMI_MIN_FP] = "min_fp",
- [CMI_FULL] = "full",
- [CMI_FULL_DIG] = "full_dig",
- [CMI_ALLOUT] = "allout",
- [CMI_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk cmi9880_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", CMI_FULL_DIG),
- SND_PCI_QUIRK(0x1854, 0x002b, "LG LS75", CMI_MINIMAL),
- SND_PCI_QUIRK(0x1854, 0x0032, "LG", CMI_FULL_DIG),
- {} /* terminator */
-};
-
-static struct hda_codec_ops cmi9880_patch_ops = {
- .build_controls = cmi9880_build_controls,
- .build_pcms = cmi9880_build_pcms,
- .init = cmi9880_init,
- .free = cmi9880_free,
-};
-
-static int patch_cmi9880(struct hda_codec *codec)
-{
- struct cmi_spec *spec;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
- spec->board_config = snd_hda_check_board_config(codec, CMI_MODELS,
- cmi9880_models,
- cmi9880_cfg_tbl);
- if (spec->board_config < 0) {
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- spec->board_config = CMI_AUTO; /* try everything */
- }
-
- /* copy default DAC NIDs */
- memcpy(spec->dac_nids, cmi9880_dac_nids, sizeof(spec->dac_nids));
- spec->num_dacs = 4;
-
- switch (spec->board_config) {
- case CMI_MINIMAL:
- case CMI_MIN_FP:
- spec->channel_modes = cmi9880_channel_modes;
- if (spec->board_config == CMI_MINIMAL)
- spec->num_channel_modes = 2;
- else {
- spec->front_panel = 1;
- spec->num_channel_modes = 3;
- }
- spec->multiout.max_channels = cmi9880_channel_modes[0].channels;
- spec->input_mux = &cmi9880_basic_mux;
- break;
- case CMI_FULL:
- case CMI_FULL_DIG:
- spec->front_panel = 1;
- spec->multiout.max_channels = 8;
- spec->input_mux = &cmi9880_basic_mux;
- if (spec->board_config == CMI_FULL_DIG) {
- spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
- spec->dig_in_nid = CMI_DIG_IN_NID;
- }
- break;
- case CMI_ALLOUT:
- spec->front_panel = 1;
- spec->multiout.max_channels = 8;
- spec->no_line_in = 1;
- spec->input_mux = &cmi9880_no_line_mux;
- spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
- break;
- case CMI_AUTO:
- {
- unsigned int port_e, port_f, port_g, port_h;
- unsigned int port_spdifi, port_spdifo;
- struct auto_pin_cfg cfg;
-
- /* collect pin default configuration */
- port_e = snd_hda_codec_get_pincfg(codec, 0x0f);
- port_f = snd_hda_codec_get_pincfg(codec, 0x10);
- spec->front_panel = 1;
- if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE ||
- get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) {
- port_g = snd_hda_codec_get_pincfg(codec, 0x1f);
- port_h = snd_hda_codec_get_pincfg(codec, 0x20);
- spec->channel_modes = cmi9880_channel_modes;
- /* no front panel */
- if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE ||
- get_defcfg_connect(port_h) == AC_JACK_PORT_NONE) {
- /* no optional rear panel */
- spec->board_config = CMI_MINIMAL;
- spec->front_panel = 0;
- spec->num_channel_modes = 2;
- } else {
- spec->board_config = CMI_MIN_FP;
- spec->num_channel_modes = 3;
- }
- spec->input_mux = &cmi9880_basic_mux;
- spec->multiout.max_channels = cmi9880_channel_modes[0].channels;
- } else {
- spec->input_mux = &cmi9880_basic_mux;
- port_spdifi = snd_hda_codec_get_pincfg(codec, 0x13);
- port_spdifo = snd_hda_codec_get_pincfg(codec, 0x12);
- if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE)
- spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
- if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE)
- spec->dig_in_nid = CMI_DIG_IN_NID;
- spec->multiout.max_channels = 8;
- }
- snd_hda_parse_pin_def_config(codec, &cfg, NULL);
- if (cfg.line_outs) {
- spec->multiout.max_channels = cfg.line_outs * 2;
- cmi9880_fill_multi_dac_nids(codec, &cfg);
- cmi9880_fill_multi_init(codec, &cfg);
- } else
- snd_printd("patch_cmedia: cannot detect association in defcfg\n");
- break;
- }
- }
-
- spec->multiout.num_dacs = spec->num_dacs;
- spec->multiout.dac_nids = spec->dac_nids;
-
- spec->adc_nids = cmi9880_adc_nids;
-
- codec->patch_ops = cmi9880_patch_ops;
-
- return 0;
-}
-
-/*
- * patch entries
- */
-static struct hda_codec_preset snd_hda_preset_cmedia[] = {
- { .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 },
- { .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 },
- {} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:13f69880");
-MODULE_ALIAS("snd-hda-codec-id:434d4980");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("C-Media HD-audio codec");
-
-static struct hda_codec_preset_list cmedia_list = {
- .preset = snd_hda_preset_cmedia,
- .owner = THIS_MODULE,
-};
-
-static int __init patch_cmedia_init(void)
-{
- return snd_hda_add_codec_preset(&cmedia_list);
-}
-
-static void __exit patch_cmedia_exit(void)
-{
- snd_hda_delete_codec_preset(&cmedia_list);
-}
-
-module_init(patch_cmedia_init)
-module_exit(patch_cmedia_exit)
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
deleted file mode 100644
index 6361f752b5f3..000000000000
--- a/sound/pci/hda/patch_conexant.c
+++ /dev/null
@@ -1,3937 +0,0 @@
-/*
- * HD audio interface patch for Conexant HDA audio codec
- *
- * Copyright (c) 2006 Pototskiy Akex <alex.pototskiy@gmail.com>
- * Takashi Iwai <tiwai@suse.de>
- * Tobin Davis <tdavis@dsl-only.net>
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
-#include <sound/core.h>
-#include <sound/jack.h>
-
-#include "hda_codec.h"
-#include "hda_local.h"
-#include "hda_beep.h"
-
-#define CXT_PIN_DIR_IN 0x00
-#define CXT_PIN_DIR_OUT 0x01
-#define CXT_PIN_DIR_INOUT 0x02
-#define CXT_PIN_DIR_IN_NOMICBIAS 0x03
-#define CXT_PIN_DIR_INOUT_NOMICBIAS 0x04
-
-#define CONEXANT_HP_EVENT 0x37
-#define CONEXANT_MIC_EVENT 0x38
-
-/* Conexant 5051 specific */
-
-#define CXT5051_SPDIF_OUT 0x12
-#define CXT5051_PORTB_EVENT 0x38
-#define CXT5051_PORTC_EVENT 0x39
-
-#define AUTO_MIC_PORTB (1 << 1)
-#define AUTO_MIC_PORTC (1 << 2)
-
-struct conexant_jack {
-
- hda_nid_t nid;
- int type;
- struct snd_jack *jack;
-
-};
-
-struct pin_dac_pair {
- hda_nid_t pin;
- hda_nid_t dac;
- int type;
-};
-
-struct conexant_spec {
-
- struct snd_kcontrol_new *mixers[5];
- int num_mixers;
- hda_nid_t vmaster_nid;
-
- const struct hda_verb *init_verbs[5]; /* initialization verbs
- * don't forget NULL
- * termination!
- */
- unsigned int num_init_verbs;
-
- /* playback */
- struct hda_multi_out multiout; /* playback set-up
- * max_channels, dacs must be set
- * dig_out_nid and hp_nid are optional
- */
- unsigned int cur_eapd;
- unsigned int hp_present;
- unsigned int auto_mic;
- int auto_mic_ext; /* autocfg.inputs[] index for ext mic */
- unsigned int need_dac_fix;
-
- /* capture */
- unsigned int num_adc_nids;
- hda_nid_t *adc_nids;
- hda_nid_t dig_in_nid; /* digital-in NID; optional */
-
- unsigned int cur_adc_idx;
- hda_nid_t cur_adc;
- unsigned int cur_adc_stream_tag;
- unsigned int cur_adc_format;
-
- /* capture source */
- const struct hda_input_mux *input_mux;
- hda_nid_t *capsrc_nids;
- unsigned int cur_mux[3];
-
- /* channel model */
- const struct hda_channel_mode *channel_mode;
- int num_channel_mode;
-
- /* PCM information */
- struct hda_pcm pcm_rec[2]; /* used in build_pcms() */
-
- unsigned int spdif_route;
-
- /* jack detection */
- struct snd_array jacks;
-
- /* dynamic controls, init_verbs and input_mux */
- struct auto_pin_cfg autocfg;
- struct hda_input_mux private_imux;
- hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
- struct pin_dac_pair dac_info[8];
- int dac_info_filled;
-
- unsigned int port_d_mode;
- unsigned int auto_mute:1; /* used in auto-parser */
- unsigned int dell_automute:1;
- unsigned int dell_vostro:1;
- unsigned int ideapad:1;
- unsigned int thinkpad:1;
- unsigned int hp_laptop:1;
-
- unsigned int ext_mic_present;
- unsigned int recording;
- void (*capture_prepare)(struct hda_codec *codec);
- void (*capture_cleanup)(struct hda_codec *codec);
-
- /* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors)
- * through the microphone jack.
- * When the user enables this through a mixer switch, both internal and
- * external microphones are disabled. Gain is fixed at 0dB. In this mode,
- * we also allow the bias to be configured through a separate mixer
- * control. */
- unsigned int dc_enable;
- unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */
- unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */
-
- unsigned int beep_amp;
-};
-
-static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static int conexant_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
- stream_tag,
- format, substream);
-}
-
-static int conexant_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Digital out
- */
-static int conexant_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int conexant_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int conexant_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
- stream_tag,
- format, substream);
-}
-
-/*
- * Analog capture
- */
-static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- if (spec->capture_prepare)
- spec->capture_prepare(codec);
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
- stream_tag, 0, format);
- return 0;
-}
-
-static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
- if (spec->capture_cleanup)
- spec->capture_cleanup(codec);
- return 0;
-}
-
-
-
-static struct hda_pcm_stream conexant_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .open = conexant_playback_pcm_open,
- .prepare = conexant_playback_pcm_prepare,
- .cleanup = conexant_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream conexant_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .prepare = conexant_capture_pcm_prepare,
- .cleanup = conexant_capture_pcm_cleanup
- },
-};
-
-
-static struct hda_pcm_stream conexant_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .open = conexant_dig_playback_pcm_open,
- .close = conexant_dig_playback_pcm_close,
- .prepare = conexant_dig_playback_pcm_prepare
- },
-};
-
-static struct hda_pcm_stream conexant_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
-};
-
-static int cx5051_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- spec->cur_adc = spec->adc_nids[spec->cur_adc_idx];
- spec->cur_adc_stream_tag = stream_tag;
- spec->cur_adc_format = format;
- snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
- return 0;
-}
-
-static int cx5051_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct conexant_spec *spec = codec->spec;
- snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
- spec->cur_adc = 0;
- return 0;
-}
-
-static struct hda_pcm_stream cx5051_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .prepare = cx5051_capture_pcm_prepare,
- .cleanup = cx5051_capture_pcm_cleanup
- },
-};
-
-static int conexant_build_pcms(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = "CONEXANT Analog";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = conexant_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
- spec->multiout.max_channels;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->multiout.dac_nids[0];
- if (codec->vendor_id == 0x14f15051)
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- cx5051_pcm_analog_capture;
- else
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- conexant_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
-
- if (spec->multiout.dig_out_nid) {
- info++;
- codec->num_pcms++;
- info->name = "Conexant Digital";
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- conexant_pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->multiout.dig_out_nid;
- if (spec->dig_in_nid) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- conexant_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
- spec->dig_in_nid;
- }
- }
-
- return 0;
-}
-
-static int conexant_mux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
-
- return snd_hda_input_mux_info(spec->input_mux, uinfo);
-}
-
-static int conexant_mux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
- return 0;
-}
-
-static int conexant_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
- spec->capsrc_nids[adc_idx],
- &spec->cur_mux[adc_idx]);
-}
-
-#ifdef CONFIG_SND_HDA_INPUT_JACK
-static void conexant_free_jack_priv(struct snd_jack *jack)
-{
- struct conexant_jack *jacks = jack->private_data;
- jacks->nid = 0;
- jacks->jack = NULL;
-}
-
-static int conexant_add_jack(struct hda_codec *codec,
- hda_nid_t nid, int type)
-{
- struct conexant_spec *spec;
- struct conexant_jack *jack;
- const char *name;
- int err;
-
- spec = codec->spec;
- snd_array_init(&spec->jacks, sizeof(*jack), 32);
- jack = snd_array_new(&spec->jacks);
- name = (type == SND_JACK_HEADPHONE) ? "Headphone" : "Mic" ;
-
- if (!jack)
- return -ENOMEM;
-
- jack->nid = nid;
- jack->type = type;
-
- err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
- if (err < 0)
- return err;
- jack->jack->private_data = jack;
- jack->jack->private_free = conexant_free_jack_priv;
- return 0;
-}
-
-static void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid)
-{
- struct conexant_spec *spec = codec->spec;
- struct conexant_jack *jacks = spec->jacks.list;
-
- if (jacks) {
- int i;
- for (i = 0; i < spec->jacks.used; i++) {
- if (jacks->nid == nid) {
- unsigned int present;
- present = snd_hda_jack_detect(codec, nid);
-
- present = (present) ? jacks->type : 0 ;
-
- snd_jack_report(jacks->jack,
- present);
- }
- jacks++;
- }
- }
-}
-
-static int conexant_init_jacks(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_init_verbs; i++) {
- const struct hda_verb *hv;
-
- hv = spec->init_verbs[i];
- while (hv->nid) {
- int err = 0;
- switch (hv->param ^ AC_USRSP_EN) {
- case CONEXANT_HP_EVENT:
- err = conexant_add_jack(codec, hv->nid,
- SND_JACK_HEADPHONE);
- conexant_report_jack(codec, hv->nid);
- break;
- case CXT5051_PORTC_EVENT:
- case CONEXANT_MIC_EVENT:
- err = conexant_add_jack(codec, hv->nid,
- SND_JACK_MICROPHONE);
- conexant_report_jack(codec, hv->nid);
- break;
- }
- if (err < 0)
- return err;
- ++hv;
- }
- }
- return 0;
-
-}
-#else
-static inline void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid)
-{
-}
-
-static inline int conexant_init_jacks(struct hda_codec *codec)
-{
- return 0;
-}
-#endif
-
-static int conexant_init(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_init_verbs; i++)
- snd_hda_sequence_write(codec, spec->init_verbs[i]);
- return 0;
-}
-
-static void conexant_free(struct hda_codec *codec)
-{
-#ifdef CONFIG_SND_HDA_INPUT_JACK
- struct conexant_spec *spec = codec->spec;
- if (spec->jacks.list) {
- struct conexant_jack *jacks = spec->jacks.list;
- int i;
- for (i = 0; i < spec->jacks.used; i++, jacks++) {
- if (jacks->jack)
- snd_device_free(codec->bus->card, jacks->jack);
- }
- snd_array_free(&spec->jacks);
- }
-#endif
- snd_hda_detach_beep_device(codec);
- kfree(codec->spec);
-}
-
-static struct snd_kcontrol_new cxt_capture_mixers[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = conexant_mux_enum_info,
- .get = conexant_mux_enum_get,
- .put = conexant_mux_enum_put
- },
- {}
-};
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-/* additional beep mixers; the actual parameters are overwritten at build */
-static struct snd_kcontrol_new cxt_beep_mixer[] = {
- HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
- { } /* end */
-};
-#endif
-
-static const char *slave_vols[] = {
- "Headphone Playback Volume",
- "Speaker Playback Volume",
- NULL
-};
-
-static const char *slave_sws[] = {
- "Headphone Playback Switch",
- "Speaker Playback Switch",
- NULL
-};
-
-static int conexant_build_controls(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- unsigned int i;
- int err;
-
- for (i = 0; i < spec->num_mixers; i++) {
- err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
- if (err < 0)
- return err;
- }
- if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- if (spec->dig_in_nid) {
- err = snd_hda_create_spdif_in_ctls(codec,spec->dig_in_nid);
- if (err < 0)
- return err;
- }
-
- /* if we have no master control, let's create it */
- if (spec->vmaster_nid &&
- !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
- unsigned int vmaster_tlv[4];
- snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
- HDA_OUTPUT, vmaster_tlv);
- err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- vmaster_tlv, slave_vols);
- if (err < 0)
- return err;
- }
- if (spec->vmaster_nid &&
- !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
- err = snd_hda_add_vmaster(codec, "Master Playback Switch",
- NULL, slave_sws);
- if (err < 0)
- return err;
- }
-
- if (spec->input_mux) {
- err = snd_hda_add_new_ctls(codec, cxt_capture_mixers);
- if (err < 0)
- return err;
- }
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
- /* create beep controls if needed */
- if (spec->beep_amp) {
- struct snd_kcontrol_new *knew;
- for (knew = cxt_beep_mixer; knew->name; knew++) {
- struct snd_kcontrol *kctl;
- kctl = snd_ctl_new1(knew, codec);
- if (!kctl)
- return -ENOMEM;
- kctl->private_value = spec->beep_amp;
- err = snd_hda_ctl_add(codec, 0, kctl);
- if (err < 0)
- return err;
- }
- }
-#endif
-
- return 0;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int conexant_suspend(struct hda_codec *codec, pm_message_t state)
-{
- snd_hda_shutup_pins(codec);
- return 0;
-}
-#endif
-
-static struct hda_codec_ops conexant_patch_ops = {
- .build_controls = conexant_build_controls,
- .build_pcms = conexant_build_pcms,
- .init = conexant_init,
- .free = conexant_free,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .suspend = conexant_suspend,
-#endif
- .reboot_notify = snd_hda_shutup_pins,
-};
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-#define set_beep_amp(spec, nid, idx, dir) \
- ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir))
-#else
-#define set_beep_amp(spec, nid, idx, dir) /* NOP */
-#endif
-
-/*
- * EAPD control
- * the private value = nid | (invert << 8)
- */
-
-#define cxt_eapd_info snd_ctl_boolean_mono_info
-
-static int cxt_eapd_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- int invert = (kcontrol->private_value >> 8) & 1;
- if (invert)
- ucontrol->value.integer.value[0] = !spec->cur_eapd;
- else
- ucontrol->value.integer.value[0] = spec->cur_eapd;
- return 0;
-
-}
-
-static int cxt_eapd_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- int invert = (kcontrol->private_value >> 8) & 1;
- hda_nid_t nid = kcontrol->private_value & 0xff;
- unsigned int eapd;
-
- eapd = !!ucontrol->value.integer.value[0];
- if (invert)
- eapd = !eapd;
- if (eapd == spec->cur_eapd)
- return 0;
-
- spec->cur_eapd = eapd;
- snd_hda_codec_write_cache(codec, nid,
- 0, AC_VERB_SET_EAPD_BTLENABLE,
- eapd ? 0x02 : 0x00);
- return 1;
-}
-
-/* controls for test mode */
-#ifdef CONFIG_SND_DEBUG
-
-#define CXT_EAPD_SWITCH(xname, nid, mask) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
- .info = cxt_eapd_info, \
- .get = cxt_eapd_get, \
- .put = cxt_eapd_put, \
- .private_value = nid | (mask<<16) }
-
-
-
-static int conexant_ch_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
- spec->num_channel_mode);
-}
-
-static int conexant_ch_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
- spec->num_channel_mode,
- spec->multiout.max_channels);
-}
-
-static int conexant_ch_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
- spec->num_channel_mode,
- &spec->multiout.max_channels);
- if (err >= 0 && spec->need_dac_fix)
- spec->multiout.num_dacs = spec->multiout.max_channels / 2;
- return err;
-}
-
-#define CXT_PIN_MODE(xname, nid, dir) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
- .info = conexant_ch_mode_info, \
- .get = conexant_ch_mode_get, \
- .put = conexant_ch_mode_put, \
- .private_value = nid | (dir<<16) }
-
-#endif /* CONFIG_SND_DEBUG */
-
-/* Conexant 5045 specific */
-
-static hda_nid_t cxt5045_dac_nids[1] = { 0x19 };
-static hda_nid_t cxt5045_adc_nids[1] = { 0x1a };
-static hda_nid_t cxt5045_capsrc_nids[1] = { 0x1a };
-#define CXT5045_SPDIF_OUT 0x18
-
-static struct hda_channel_mode cxt5045_modes[1] = {
- { 2, NULL },
-};
-
-static struct hda_input_mux cxt5045_capture_source = {
- .num_items = 2,
- .items = {
- { "IntMic", 0x1 },
- { "ExtMic", 0x2 },
- }
-};
-
-static struct hda_input_mux cxt5045_capture_source_benq = {
- .num_items = 5,
- .items = {
- { "IntMic", 0x1 },
- { "ExtMic", 0x2 },
- { "LineIn", 0x3 },
- { "CD", 0x4 },
- { "Mixer", 0x0 },
- }
-};
-
-static struct hda_input_mux cxt5045_capture_source_hp530 = {
- .num_items = 2,
- .items = {
- { "ExtMic", 0x1 },
- { "IntMic", 0x2 },
- }
-};
-
-/* turn on/off EAPD (+ mute HP) as a master switch */
-static int cxt5045_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- unsigned int bits;
-
- if (!cxt_eapd_put(kcontrol, ucontrol))
- return 0;
-
- /* toggle internal speakers mute depending of presence of
- * the headphone jack
- */
- bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
-
- bits = spec->cur_eapd ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, 0x11, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
- return 1;
-}
-
-/* bind volumes of both NID 0x10 and 0x11 */
-static struct hda_bind_ctls cxt5045_hp_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x11, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-/* toggle input of built-in and mic jack appropriately */
-static void cxt5045_hp_automic(struct hda_codec *codec)
-{
- static struct hda_verb mic_jack_on[] = {
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- {}
- };
- static struct hda_verb mic_jack_off[] = {
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- {}
- };
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x12);
- if (present)
- snd_hda_sequence_write(codec, mic_jack_on);
- else
- snd_hda_sequence_write(codec, mic_jack_off);
-}
-
-
-/* mute internal speaker if HP is plugged */
-static void cxt5045_hp_automute(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- unsigned int bits;
-
- spec->hp_present = snd_hda_jack_detect(codec, 0x11);
-
- bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
-}
-
-/* unsolicited event for HP jack sensing */
-static void cxt5045_hp_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- res >>= 26;
- switch (res) {
- case CONEXANT_HP_EVENT:
- cxt5045_hp_automute(codec);
- break;
- case CONEXANT_MIC_EVENT:
- cxt5045_hp_automic(codec);
- break;
-
- }
-}
-
-static struct snd_kcontrol_new cxt5045_mixers[] = {
- HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Ext Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x17, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x17, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x17, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x17, 0x2, HDA_INPUT),
- HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .info = cxt_eapd_info,
- .get = cxt_eapd_get,
- .put = cxt5045_hp_master_sw_put,
- .private_value = 0x10,
- },
-
- {}
-};
-
-static struct snd_kcontrol_new cxt5045_benq_mixers[] = {
- HDA_CODEC_VOLUME("CD Capture Volume", 0x1a, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Capture Switch", 0x1a, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x17, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x17, 0x4, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Line In Capture Volume", 0x1a, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Line In Capture Switch", 0x1a, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Playback Volume", 0x17, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Line In Playback Switch", 0x17, 0x3, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Mixer Capture Volume", 0x1a, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer Capture Switch", 0x1a, 0x0, HDA_INPUT),
-
- {}
-};
-
-static struct snd_kcontrol_new cxt5045_mixers_hp530[] = {
- HDA_CODEC_VOLUME("Int Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Ext Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Ext Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x17, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x17, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x17, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x17, 0x1, HDA_INPUT),
- HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .info = cxt_eapd_info,
- .get = cxt_eapd_get,
- .put = cxt5045_hp_master_sw_put,
- .private_value = 0x10,
- },
-
- {}
-};
-
-static struct hda_verb cxt5045_init_verbs[] = {
- /* Line in, Mic */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
- /* HP, Amp */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Record selector: Int mic */
- {0x1a, AC_VERB_SET_CONNECT_SEL,0x1},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE,
- AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
- /* SPDIF route: PCM */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- { 0x13, AC_VERB_SET_CONNECT_SEL, 0x0 },
- /* EAPD */
- {0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x2 }, /* default on */
- { } /* end */
-};
-
-static struct hda_verb cxt5045_benq_init_verbs[] = {
- /* Int Mic, Mic */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
- /* Line In,HP, Amp */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Record selector: Int mic */
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x1},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE,
- AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
- /* SPDIF route: PCM */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* EAPD */
- {0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
- { } /* end */
-};
-
-static struct hda_verb cxt5045_hp_sense_init_verbs[] = {
- /* pin sensing on HP jack */
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- { } /* end */
-};
-
-static struct hda_verb cxt5045_mic_sense_init_verbs[] = {
- /* pin sensing on HP jack */
- {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
- { } /* end */
-};
-
-#ifdef CONFIG_SND_DEBUG
-/* Test configuration for debugging, modelled after the ALC260 test
- * configuration.
- */
-static struct hda_input_mux cxt5045_test_capture_source = {
- .num_items = 5,
- .items = {
- { "MIXER", 0x0 },
- { "MIC1 pin", 0x1 },
- { "LINE1 pin", 0x2 },
- { "HP-OUT pin", 0x3 },
- { "CD pin", 0x4 },
- },
-};
-
-static struct snd_kcontrol_new cxt5045_test_mixer[] = {
-
- /* Output controls */
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x10, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x10, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Node 11 Playback Volume", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Node 11 Playback Switch", 0x11, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Node 12 Playback Volume", 0x12, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Node 12 Playback Switch", 0x12, 0x0, HDA_OUTPUT),
-
- /* Modes for retasking pin widgets */
- CXT_PIN_MODE("HP-OUT pin mode", 0x11, CXT_PIN_DIR_INOUT),
- CXT_PIN_MODE("LINE1 pin mode", 0x12, CXT_PIN_DIR_INOUT),
-
- /* EAPD Switch Control */
- CXT_EAPD_SWITCH("External Amplifier", 0x10, 0x0),
-
- /* Loopback mixer controls */
-
- HDA_CODEC_VOLUME("Mixer-1 Volume", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer-1 Switch", 0x17, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mixer-2 Volume", 0x17, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer-2 Switch", 0x17, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Mixer-3 Volume", 0x17, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer-3 Switch", 0x17, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mixer-4 Volume", 0x17, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer-4 Switch", 0x17, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("Mixer-5 Volume", 0x17, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer-5 Switch", 0x17, 0x4, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Input Source",
- .info = conexant_mux_enum_info,
- .get = conexant_mux_enum_get,
- .put = conexant_mux_enum_put,
- },
- /* Audio input controls */
- HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_verb cxt5045_test_init_verbs[] = {
- /* Set connections */
- { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
- { 0x11, AC_VERB_SET_CONNECT_SEL, 0x0 },
- { 0x12, AC_VERB_SET_CONNECT_SEL, 0x0 },
- /* Enable retasking pins as output, initially without power amp */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* Disable digital (SPDIF) pins initially, but users can enable
- * them via a mixer switch. In the case of SPDIF-out, this initverb
- * payload also sets the generation to 0, output to be in "consumer"
- * PCM format, copyright asserted, no pre-emphasis and no validity
- * control.
- */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_DIGI_CONVERT_1, 0},
-
- /* Start with output sum widgets muted and their output gains at min */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- /* Unmute retasking pin widget output buffers since the default
- * state appears to be output. As the pin mode is changed by the
- * user the pin mode control will take care of enabling the pin's
- * input/output buffers as needed.
- */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Mute capture amp left and right */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-
- /* Set ADC connection select to match default mixer setting (mic1
- * pin)
- */
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mute all inputs to mixer widget (even unconnected ones) */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* Mixer pin */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* Mic1 pin */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* Line pin */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* HP pin */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
-
- { }
-};
-#endif
-
-
-/* initialize jack-sensing, too */
-static int cxt5045_init(struct hda_codec *codec)
-{
- conexant_init(codec);
- cxt5045_hp_automute(codec);
- return 0;
-}
-
-
-enum {
- CXT5045_LAPTOP_HPSENSE,
- CXT5045_LAPTOP_MICSENSE,
- CXT5045_LAPTOP_HPMICSENSE,
- CXT5045_BENQ,
- CXT5045_LAPTOP_HP530,
-#ifdef CONFIG_SND_DEBUG
- CXT5045_TEST,
-#endif
- CXT5045_MODELS
-};
-
-static const char *cxt5045_models[CXT5045_MODELS] = {
- [CXT5045_LAPTOP_HPSENSE] = "laptop-hpsense",
- [CXT5045_LAPTOP_MICSENSE] = "laptop-micsense",
- [CXT5045_LAPTOP_HPMICSENSE] = "laptop-hpmicsense",
- [CXT5045_BENQ] = "benq",
- [CXT5045_LAPTOP_HP530] = "laptop-hp530",
-#ifdef CONFIG_SND_DEBUG
- [CXT5045_TEST] = "test",
-#endif
-};
-
-static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series",
- CXT5045_LAPTOP_HPSENSE),
- SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE),
- SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ),
- SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE),
- SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP_HPMICSENSE),
- SND_PCI_QUIRK(0x1734, 0x110e, "Fujitsu V5505",
- CXT5045_LAPTOP_HPMICSENSE),
- SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE),
- SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE),
- SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE),
- SND_PCI_QUIRK_MASK(0x1631, 0xff00, 0xc100, "Packard Bell",
- CXT5045_LAPTOP_HPMICSENSE),
- SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE),
- {}
-};
-
-static int patch_cxt5045(struct hda_codec *codec)
-{
- struct conexant_spec *spec;
- int board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
- codec->spec = spec;
- codec->pin_amp_workaround = 1;
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(cxt5045_dac_nids);
- spec->multiout.dac_nids = cxt5045_dac_nids;
- spec->multiout.dig_out_nid = CXT5045_SPDIF_OUT;
- spec->num_adc_nids = 1;
- spec->adc_nids = cxt5045_adc_nids;
- spec->capsrc_nids = cxt5045_capsrc_nids;
- spec->input_mux = &cxt5045_capture_source;
- spec->num_mixers = 1;
- spec->mixers[0] = cxt5045_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = cxt5045_init_verbs;
- spec->spdif_route = 0;
- spec->num_channel_mode = ARRAY_SIZE(cxt5045_modes);
- spec->channel_mode = cxt5045_modes;
-
- set_beep_amp(spec, 0x16, 0, 1);
-
- codec->patch_ops = conexant_patch_ops;
-
- board_config = snd_hda_check_board_config(codec, CXT5045_MODELS,
- cxt5045_models,
- cxt5045_cfg_tbl);
- switch (board_config) {
- case CXT5045_LAPTOP_HPSENSE:
- codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
- spec->input_mux = &cxt5045_capture_source;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = cxt5045_hp_sense_init_verbs;
- spec->mixers[0] = cxt5045_mixers;
- codec->patch_ops.init = cxt5045_init;
- break;
- case CXT5045_LAPTOP_MICSENSE:
- codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
- spec->input_mux = &cxt5045_capture_source;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = cxt5045_mic_sense_init_verbs;
- spec->mixers[0] = cxt5045_mixers;
- codec->patch_ops.init = cxt5045_init;
- break;
- default:
- case CXT5045_LAPTOP_HPMICSENSE:
- codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
- spec->input_mux = &cxt5045_capture_source;
- spec->num_init_verbs = 3;
- spec->init_verbs[1] = cxt5045_hp_sense_init_verbs;
- spec->init_verbs[2] = cxt5045_mic_sense_init_verbs;
- spec->mixers[0] = cxt5045_mixers;
- codec->patch_ops.init = cxt5045_init;
- break;
- case CXT5045_BENQ:
- codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
- spec->input_mux = &cxt5045_capture_source_benq;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = cxt5045_benq_init_verbs;
- spec->mixers[0] = cxt5045_mixers;
- spec->mixers[1] = cxt5045_benq_mixers;
- spec->num_mixers = 2;
- codec->patch_ops.init = cxt5045_init;
- break;
- case CXT5045_LAPTOP_HP530:
- codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
- spec->input_mux = &cxt5045_capture_source_hp530;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = cxt5045_hp_sense_init_verbs;
- spec->mixers[0] = cxt5045_mixers_hp530;
- codec->patch_ops.init = cxt5045_init;
- break;
-#ifdef CONFIG_SND_DEBUG
- case CXT5045_TEST:
- spec->input_mux = &cxt5045_test_capture_source;
- spec->mixers[0] = cxt5045_test_mixer;
- spec->init_verbs[0] = cxt5045_test_init_verbs;
- break;
-
-#endif
- }
-
- switch (codec->subsystem_id >> 16) {
- case 0x103c:
- case 0x1631:
- case 0x1734:
- case 0x17aa:
- /* HP, Packard Bell, Fujitsu-Siemens & Lenovo laptops have
- * really bad sound over 0dB on NID 0x17. Fix max PCM level to
- * 0 dB (originally it has 0x2b steps with 0dB offset 0x14)
- */
- snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
- (0x14 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- }
-
- if (spec->beep_amp)
- snd_hda_attach_beep_device(codec, spec->beep_amp);
-
- return 0;
-}
-
-
-/* Conexant 5047 specific */
-#define CXT5047_SPDIF_OUT 0x11
-
-static hda_nid_t cxt5047_dac_nids[1] = { 0x10 }; /* 0x1c */
-static hda_nid_t cxt5047_adc_nids[1] = { 0x12 };
-static hda_nid_t cxt5047_capsrc_nids[1] = { 0x1a };
-
-static struct hda_channel_mode cxt5047_modes[1] = {
- { 2, NULL },
-};
-
-static struct hda_input_mux cxt5047_toshiba_capture_source = {
- .num_items = 2,
- .items = {
- { "ExtMic", 0x2 },
- { "Line-In", 0x1 },
- }
-};
-
-/* turn on/off EAPD (+ mute HP) as a master switch */
-static int cxt5047_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- unsigned int bits;
-
- if (!cxt_eapd_put(kcontrol, ucontrol))
- return 0;
-
- /* toggle internal speakers mute depending of presence of
- * the headphone jack
- */
- bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE;
- /* NOTE: Conexat codec needs the index for *OUTPUT* amp of
- * pin widgets unlike other codecs. In this case, we need to
- * set index 0x01 for the volume from the mixer amp 0x19.
- */
- snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0x01,
- HDA_AMP_MUTE, bits);
- bits = spec->cur_eapd ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
- return 1;
-}
-
-/* mute internal speaker if HP is plugged */
-static void cxt5047_hp_automute(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- unsigned int bits;
-
- spec->hp_present = snd_hda_jack_detect(codec, 0x13);
-
- bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0;
- /* See the note in cxt5047_hp_master_sw_put */
- snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0x01,
- HDA_AMP_MUTE, bits);
-}
-
-/* toggle input of built-in and mic jack appropriately */
-static void cxt5047_hp_automic(struct hda_codec *codec)
-{
- static struct hda_verb mic_jack_on[] = {
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {}
- };
- static struct hda_verb mic_jack_off[] = {
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {}
- };
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x15);
- if (present)
- snd_hda_sequence_write(codec, mic_jack_on);
- else
- snd_hda_sequence_write(codec, mic_jack_off);
-}
-
-/* unsolicited event for HP jack sensing */
-static void cxt5047_hp_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case CONEXANT_HP_EVENT:
- cxt5047_hp_automute(codec);
- break;
- case CONEXANT_MIC_EVENT:
- cxt5047_hp_automic(codec);
- break;
- }
-}
-
-static struct snd_kcontrol_new cxt5047_base_mixers[] = {
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x19, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x19, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT),
- HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .info = cxt_eapd_info,
- .get = cxt_eapd_get,
- .put = cxt5047_hp_master_sw_put,
- .private_value = 0x13,
- },
-
- {}
-};
-
-static struct snd_kcontrol_new cxt5047_hp_spk_mixers[] = {
- /* See the note in cxt5047_hp_master_sw_put */
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x01, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x13, 0x00, HDA_OUTPUT),
- {}
-};
-
-static struct snd_kcontrol_new cxt5047_hp_only_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x13, 0x00, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct hda_verb cxt5047_init_verbs[] = {
- /* Line in, Mic, Built-in Mic */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 },
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_50 },
- /* HP, Speaker */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, /* mixer(0x19) */
- {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mixer(0x19) */
- /* Record selector: Mic */
- {0x12, AC_VERB_SET_CONNECT_SEL,0x03},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE,
- AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
- {0x1A, AC_VERB_SET_CONNECT_SEL,0x02},
- {0x1A, AC_VERB_SET_AMP_GAIN_MUTE,
- AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x00},
- {0x1A, AC_VERB_SET_AMP_GAIN_MUTE,
- AC_AMP_SET_OUTPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x03},
- /* SPDIF route: PCM */
- { 0x18, AC_VERB_SET_CONNECT_SEL, 0x0 },
- /* Enable unsolicited events */
- {0x13, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
- { } /* end */
-};
-
-/* configuration for Toshiba Laptops */
-static struct hda_verb cxt5047_toshiba_init_verbs[] = {
- {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x0}, /* default off */
- {}
-};
-
-/* Test configuration for debugging, modelled after the ALC260 test
- * configuration.
- */
-#ifdef CONFIG_SND_DEBUG
-static struct hda_input_mux cxt5047_test_capture_source = {
- .num_items = 4,
- .items = {
- { "LINE1 pin", 0x0 },
- { "MIC1 pin", 0x1 },
- { "MIC2 pin", 0x2 },
- { "CD pin", 0x3 },
- },
-};
-
-static struct snd_kcontrol_new cxt5047_test_mixer[] = {
-
- /* Output only controls */
- HDA_CODEC_VOLUME("OutAmp-1 Volume", 0x10, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("OutAmp-1 Switch", 0x10,0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("OutAmp-2 Volume", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("OutAmp-2 Switch", 0x1c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("HeadPhone Playback Volume", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("HeadPhone Playback Switch", 0x13, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line1-Out Playback Volume", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line1-Out Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line2-Out Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Line2-Out Playback Switch", 0x15, 0x0, HDA_OUTPUT),
-
- /* Modes for retasking pin widgets */
- CXT_PIN_MODE("LINE1 pin mode", 0x14, CXT_PIN_DIR_INOUT),
- CXT_PIN_MODE("MIC1 pin mode", 0x15, CXT_PIN_DIR_INOUT),
-
- /* EAPD Switch Control */
- CXT_EAPD_SWITCH("External Amplifier", 0x13, 0x0),
-
- /* Loopback mixer controls */
- HDA_CODEC_VOLUME("MIC1 Playback Volume", 0x12, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("MIC1 Playback Switch", 0x12, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("MIC2 Playback Volume", 0x12, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("MIC2 Playback Switch", 0x12, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("LINE Playback Volume", 0x12, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("LINE Playback Switch", 0x12, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x12, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x12, 0x04, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Capture-1 Volume", 0x19, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture-1 Switch", 0x19, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture-2 Volume", 0x19, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Capture-2 Switch", 0x19, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture-3 Volume", 0x19, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Capture-3 Switch", 0x19, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture-4 Volume", 0x19, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Capture-4 Switch", 0x19, 0x3, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Input Source",
- .info = conexant_mux_enum_info,
- .get = conexant_mux_enum_get,
- .put = conexant_mux_enum_put,
- },
- HDA_CODEC_VOLUME("Mic Boost Volume", 0x1a, 0x0, HDA_OUTPUT),
-
- { } /* end */
-};
-
-static struct hda_verb cxt5047_test_init_verbs[] = {
- /* Enable retasking pins as output, initially without power amp */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* Disable digital (SPDIF) pins initially, but users can enable
- * them via a mixer switch. In the case of SPDIF-out, this initverb
- * payload also sets the generation to 0, output to be in "consumer"
- * PCM format, copyright asserted, no pre-emphasis and no validity
- * control.
- */
- {0x18, AC_VERB_SET_DIGI_CONVERT_1, 0},
-
- /* Ensure mic1, mic2, line1 pin widgets take input from the
- * OUT1 sum bus when acting as an output.
- */
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* Start with output sum widgets muted and their output gains at min */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- /* Unmute retasking pin widget output buffers since the default
- * state appears to be output. As the pin mode is changed by the
- * user the pin mode control will take care of enabling the pin's
- * input/output buffers as needed.
- */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Mute capture amp left and right */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-
- /* Set ADC connection select to match default mixer setting (mic1
- * pin)
- */
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mute all inputs to mixer widget (even unconnected ones) */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
-
- { }
-};
-#endif
-
-
-/* initialize jack-sensing, too */
-static int cxt5047_hp_init(struct hda_codec *codec)
-{
- conexant_init(codec);
- cxt5047_hp_automute(codec);
- return 0;
-}
-
-
-enum {
- CXT5047_LAPTOP, /* Laptops w/o EAPD support */
- CXT5047_LAPTOP_HP, /* Some HP laptops */
- CXT5047_LAPTOP_EAPD, /* Laptops with EAPD support */
-#ifdef CONFIG_SND_DEBUG
- CXT5047_TEST,
-#endif
- CXT5047_MODELS
-};
-
-static const char *cxt5047_models[CXT5047_MODELS] = {
- [CXT5047_LAPTOP] = "laptop",
- [CXT5047_LAPTOP_HP] = "laptop-hp",
- [CXT5047_LAPTOP_EAPD] = "laptop-eapd",
-#ifdef CONFIG_SND_DEBUG
- [CXT5047_TEST] = "test",
-#endif
-};
-
-static struct snd_pci_quirk cxt5047_cfg_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series",
- CXT5047_LAPTOP),
- SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD),
- {}
-};
-
-static int patch_cxt5047(struct hda_codec *codec)
-{
- struct conexant_spec *spec;
- int board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
- codec->spec = spec;
- codec->pin_amp_workaround = 1;
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(cxt5047_dac_nids);
- spec->multiout.dac_nids = cxt5047_dac_nids;
- spec->multiout.dig_out_nid = CXT5047_SPDIF_OUT;
- spec->num_adc_nids = 1;
- spec->adc_nids = cxt5047_adc_nids;
- spec->capsrc_nids = cxt5047_capsrc_nids;
- spec->num_mixers = 1;
- spec->mixers[0] = cxt5047_base_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = cxt5047_init_verbs;
- spec->spdif_route = 0;
- spec->num_channel_mode = ARRAY_SIZE(cxt5047_modes),
- spec->channel_mode = cxt5047_modes,
-
- codec->patch_ops = conexant_patch_ops;
-
- board_config = snd_hda_check_board_config(codec, CXT5047_MODELS,
- cxt5047_models,
- cxt5047_cfg_tbl);
- switch (board_config) {
- case CXT5047_LAPTOP:
- spec->num_mixers = 2;
- spec->mixers[1] = cxt5047_hp_spk_mixers;
- codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
- break;
- case CXT5047_LAPTOP_HP:
- spec->num_mixers = 2;
- spec->mixers[1] = cxt5047_hp_only_mixers;
- codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
- codec->patch_ops.init = cxt5047_hp_init;
- break;
- case CXT5047_LAPTOP_EAPD:
- spec->input_mux = &cxt5047_toshiba_capture_source;
- spec->num_mixers = 2;
- spec->mixers[1] = cxt5047_hp_spk_mixers;
- spec->num_init_verbs = 2;
- spec->init_verbs[1] = cxt5047_toshiba_init_verbs;
- codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
- break;
-#ifdef CONFIG_SND_DEBUG
- case CXT5047_TEST:
- spec->input_mux = &cxt5047_test_capture_source;
- spec->mixers[0] = cxt5047_test_mixer;
- spec->init_verbs[0] = cxt5047_test_init_verbs;
- codec->patch_ops.unsol_event = cxt5047_hp_unsol_event;
-#endif
- }
- spec->vmaster_nid = 0x13;
-
- switch (codec->subsystem_id >> 16) {
- case 0x103c:
- /* HP laptops have really bad sound over 0 dB on NID 0x10.
- * Fix max PCM level to 0 dB (originally it has 0x1e steps
- * with 0 dB offset 0x17)
- */
- snd_hda_override_amp_caps(codec, 0x10, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- break;
- }
-
- return 0;
-}
-
-/* Conexant 5051 specific */
-static hda_nid_t cxt5051_dac_nids[1] = { 0x10 };
-static hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 };
-
-static struct hda_channel_mode cxt5051_modes[1] = {
- { 2, NULL },
-};
-
-static void cxt5051_update_speaker(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- unsigned int pinctl;
- /* headphone pin */
- pinctl = (spec->hp_present && spec->cur_eapd) ? PIN_HP : 0;
- snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
- /* speaker pin */
- pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
- /* on ideapad there is an aditional speaker (subwoofer) to mute */
- if (spec->ideapad)
- snd_hda_codec_write(codec, 0x1b, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
-}
-
-/* turn on/off EAPD (+ mute HP) as a master switch */
-static int cxt5051_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-
- if (!cxt_eapd_put(kcontrol, ucontrol))
- return 0;
- cxt5051_update_speaker(codec);
- return 1;
-}
-
-/* toggle input of built-in and mic jack appropriately */
-static void cxt5051_portb_automic(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- unsigned int present;
-
- if (!(spec->auto_mic & AUTO_MIC_PORTB))
- return;
- present = snd_hda_jack_detect(codec, 0x17);
- snd_hda_codec_write(codec, 0x14, 0,
- AC_VERB_SET_CONNECT_SEL,
- present ? 0x01 : 0x00);
-}
-
-/* switch the current ADC according to the jack state */
-static void cxt5051_portc_automic(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- unsigned int present;
- hda_nid_t new_adc;
-
- if (!(spec->auto_mic & AUTO_MIC_PORTC))
- return;
- present = snd_hda_jack_detect(codec, 0x18);
- if (present)
- spec->cur_adc_idx = 1;
- else
- spec->cur_adc_idx = 0;
- new_adc = spec->adc_nids[spec->cur_adc_idx];
- if (spec->cur_adc && spec->cur_adc != new_adc) {
- /* stream is running, let's swap the current ADC */
- __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
- spec->cur_adc = new_adc;
- snd_hda_codec_setup_stream(codec, new_adc,
- spec->cur_adc_stream_tag, 0,
- spec->cur_adc_format);
- }
-}
-
-/* mute internal speaker if HP is plugged */
-static void cxt5051_hp_automute(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
-
- spec->hp_present = snd_hda_jack_detect(codec, 0x16);
- cxt5051_update_speaker(codec);
-}
-
-/* unsolicited event for HP jack sensing */
-static void cxt5051_hp_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20;
- switch (res >> 26) {
- case CONEXANT_HP_EVENT:
- cxt5051_hp_automute(codec);
- break;
- case CXT5051_PORTB_EVENT:
- cxt5051_portb_automic(codec);
- break;
- case CXT5051_PORTC_EVENT:
- cxt5051_portc_automic(codec);
- break;
- }
- conexant_report_jack(codec, nid);
-}
-
-static struct snd_kcontrol_new cxt5051_playback_mixers[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .info = cxt_eapd_info,
- .get = cxt_eapd_get,
- .put = cxt5051_hp_master_sw_put,
- .private_value = 0x1a,
- },
- {}
-};
-
-static struct snd_kcontrol_new cxt5051_capture_mixers[] = {
- HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT),
- {}
-};
-
-static struct snd_kcontrol_new cxt5051_hp_mixers[] = {
- HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("External Mic Volume", 0x15, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("External Mic Switch", 0x15, 0x00, HDA_INPUT),
- {}
-};
-
-static struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x14, 0x00, HDA_INPUT),
- {}
-};
-
-static struct snd_kcontrol_new cxt5051_f700_mixers[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x14, 0x01, HDA_INPUT),
- {}
-};
-
-static struct snd_kcontrol_new cxt5051_toshiba_mixers[] = {
- HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT),
- {}
-};
-
-static struct hda_verb cxt5051_init_verbs[] = {
- /* Line in, Mic */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- /* SPK */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP, Amp */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Record selector: Int mic */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
- /* SPDIF route: PCM */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* EAPD */
- {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
- {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
- { } /* end */
-};
-
-static struct hda_verb cxt5051_hp_dv6736_init_verbs[] = {
- /* Line in, Mic */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
- /* SPK */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP, Amp */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Record selector: Int mic */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* SPDIF route: PCM */
- {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* EAPD */
- {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
- {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
- { } /* end */
-};
-
-static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = {
- /* Line in, Mic */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- /* SPK */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP, Amp */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Docking HP */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Record selector: Int mic */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
- /* SPDIF route: PCM */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* needed for W500 Advanced Mini Dock 250410 */
- {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* EAPD */
- {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
- {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
- { } /* end */
-};
-
-static struct hda_verb cxt5051_f700_init_verbs[] = {
- /* Line in, Mic */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
- /* SPK */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP, Amp */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Record selector: Int mic */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* SPDIF route: PCM */
- {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* EAPD */
- {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
- {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
- { } /* end */
-};
-
-static void cxt5051_init_mic_port(struct hda_codec *codec, hda_nid_t nid,
- unsigned int event)
-{
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | event);
-#ifdef CONFIG_SND_HDA_INPUT_JACK
- conexant_add_jack(codec, nid, SND_JACK_MICROPHONE);
- conexant_report_jack(codec, nid);
-#endif
-}
-
-static struct hda_verb cxt5051_ideapad_init_verbs[] = {
- /* Subwoofer */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- { } /* end */
-};
-
-/* initialize jack-sensing, too */
-static int cxt5051_init(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
-
- conexant_init(codec);
- conexant_init_jacks(codec);
-
- if (spec->auto_mic & AUTO_MIC_PORTB)
- cxt5051_init_mic_port(codec, 0x17, CXT5051_PORTB_EVENT);
- if (spec->auto_mic & AUTO_MIC_PORTC)
- cxt5051_init_mic_port(codec, 0x18, CXT5051_PORTC_EVENT);
-
- if (codec->patch_ops.unsol_event) {
- cxt5051_hp_automute(codec);
- cxt5051_portb_automic(codec);
- cxt5051_portc_automic(codec);
- }
- return 0;
-}
-
-
-enum {
- CXT5051_LAPTOP, /* Laptops w/ EAPD support */
- CXT5051_HP, /* no docking */
- CXT5051_HP_DV6736, /* HP without mic switch */
- CXT5051_LENOVO_X200, /* Lenovo X200 laptop, also used for Advanced Mini Dock 250410 */
- CXT5051_F700, /* HP Compaq Presario F700 */
- CXT5051_TOSHIBA, /* Toshiba M300 & co */
- CXT5051_IDEAPAD, /* Lenovo IdeaPad Y430 */
- CXT5051_MODELS
-};
-
-static const char *cxt5051_models[CXT5051_MODELS] = {
- [CXT5051_LAPTOP] = "laptop",
- [CXT5051_HP] = "hp",
- [CXT5051_HP_DV6736] = "hp-dv6736",
- [CXT5051_LENOVO_X200] = "lenovo-x200",
- [CXT5051_F700] = "hp-700",
- [CXT5051_TOSHIBA] = "toshiba",
- [CXT5051_IDEAPAD] = "ideapad",
-};
-
-static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6736", CXT5051_HP_DV6736),
- SND_PCI_QUIRK(0x103c, 0x360b, "Compaq Presario CQ60", CXT5051_HP),
- SND_PCI_QUIRK(0x103c, 0x30ea, "Compaq Presario F700", CXT5051_F700),
- SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba M30x", CXT5051_TOSHIBA),
- SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
- CXT5051_LAPTOP),
- SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
- SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200),
- SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo IdeaPad", CXT5051_IDEAPAD),
- {}
-};
-
-static int patch_cxt5051(struct hda_codec *codec)
-{
- struct conexant_spec *spec;
- int board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
- codec->spec = spec;
- codec->pin_amp_workaround = 1;
-
- codec->patch_ops = conexant_patch_ops;
- codec->patch_ops.init = cxt5051_init;
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(cxt5051_dac_nids);
- spec->multiout.dac_nids = cxt5051_dac_nids;
- spec->multiout.dig_out_nid = CXT5051_SPDIF_OUT;
- spec->num_adc_nids = 1; /* not 2; via auto-mic switch */
- spec->adc_nids = cxt5051_adc_nids;
- spec->num_mixers = 2;
- spec->mixers[0] = cxt5051_capture_mixers;
- spec->mixers[1] = cxt5051_playback_mixers;
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = cxt5051_init_verbs;
- spec->spdif_route = 0;
- spec->num_channel_mode = ARRAY_SIZE(cxt5051_modes);
- spec->channel_mode = cxt5051_modes;
- spec->cur_adc = 0;
- spec->cur_adc_idx = 0;
-
- set_beep_amp(spec, 0x13, 0, HDA_OUTPUT);
-
- codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
-
- board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
- cxt5051_models,
- cxt5051_cfg_tbl);
- spec->auto_mic = AUTO_MIC_PORTB | AUTO_MIC_PORTC;
- switch (board_config) {
- case CXT5051_HP:
- spec->mixers[0] = cxt5051_hp_mixers;
- break;
- case CXT5051_HP_DV6736:
- spec->init_verbs[0] = cxt5051_hp_dv6736_init_verbs;
- spec->mixers[0] = cxt5051_hp_dv6736_mixers;
- spec->auto_mic = 0;
- break;
- case CXT5051_LENOVO_X200:
- spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs;
- /* Thinkpad X301 does not have S/PDIF wired and no ability
- to use a docking station. */
- if (codec->subsystem_id == 0x17aa211f)
- spec->multiout.dig_out_nid = 0;
- break;
- case CXT5051_F700:
- spec->init_verbs[0] = cxt5051_f700_init_verbs;
- spec->mixers[0] = cxt5051_f700_mixers;
- spec->auto_mic = 0;
- break;
- case CXT5051_TOSHIBA:
- spec->mixers[0] = cxt5051_toshiba_mixers;
- spec->auto_mic = AUTO_MIC_PORTB;
- break;
- case CXT5051_IDEAPAD:
- spec->init_verbs[spec->num_init_verbs++] =
- cxt5051_ideapad_init_verbs;
- spec->ideapad = 1;
- break;
- }
-
- if (spec->beep_amp)
- snd_hda_attach_beep_device(codec, spec->beep_amp);
-
- return 0;
-}
-
-/* Conexant 5066 specific */
-
-static hda_nid_t cxt5066_dac_nids[1] = { 0x10 };
-static hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 };
-static hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 };
-#define CXT5066_SPDIF_OUT 0x21
-
-/* OLPC's microphone port is DC coupled for use with external sensors,
- * therefore we use a 50% mic bias in order to center the input signal with
- * the DC input range of the codec. */
-#define CXT5066_OLPC_EXT_MIC_BIAS PIN_VREF50
-
-static struct hda_channel_mode cxt5066_modes[1] = {
- { 2, NULL },
-};
-
-static void cxt5066_update_speaker(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- unsigned int pinctl;
-
- snd_printdd("CXT5066: update speaker, hp_present=%d\n",
- spec->hp_present);
-
- /* Port A (HP) */
- pinctl = ((spec->hp_present & 1) && spec->cur_eapd) ? PIN_HP : 0;
- snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
-
- /* Port D (HP/LO) */
- pinctl = ((spec->hp_present & 2) && spec->cur_eapd)
- ? spec->port_d_mode : 0;
- /* Mute if Port A is connected on Thinkpad */
- if (spec->thinkpad && (spec->hp_present & 1))
- pinctl = 0;
- snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
-
- /* CLASS_D AMP */
- pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
- snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinctl);
-
- if (spec->dell_automute) {
- /* DELL AIO Port Rule: PortA > PortD > IntSpk */
- pinctl = (!(spec->hp_present & 1) && spec->cur_eapd)
- ? PIN_OUT : 0;
- snd_hda_codec_write(codec, 0x1c, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
- }
-}
-
-/* turn on/off EAPD (+ mute HP) as a master switch */
-static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-
- if (!cxt_eapd_put(kcontrol, ucontrol))
- return 0;
-
- cxt5066_update_speaker(codec);
- return 1;
-}
-
-static const struct hda_input_mux cxt5066_olpc_dc_bias = {
- .num_items = 3,
- .items = {
- { "Off", PIN_IN },
- { "50%", PIN_VREF50 },
- { "80%", PIN_VREF80 },
- },
-};
-
-static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- /* Even though port F is the DC input, the bias is controlled on port B.
- * we also leave that port as an active input (but unselected) in DC mode
- * just in case that is necessary to make the bias setting take effect. */
- return snd_hda_codec_write_cache(codec, 0x1a, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index);
-}
-
-/* OLPC defers mic widget control until when capture is started because the
- * microphone LED comes on as soon as these settings are put in place. if we
- * did this before recording, it would give the false indication that recording
- * is happening when it is not. */
-static void cxt5066_olpc_select_mic(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- if (!spec->recording)
- return;
-
- if (spec->dc_enable) {
- /* in DC mode we ignore presence detection and just use the jack
- * through our special DC port */
- const struct hda_verb enable_dc_mode[] = {
- /* disble internal mic, port C */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* enable DC capture, port F */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {},
- };
-
- snd_hda_sequence_write(codec, enable_dc_mode);
- /* port B input disabled (and bias set) through the following call */
- cxt5066_set_olpc_dc_bias(codec);
- return;
- }
-
- /* disable DC (port F) */
- snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
-
- /* external mic, port B */
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0);
-
- /* internal mic, port C */
- snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->ext_mic_present ? 0 : PIN_VREF80);
-}
-
-/* toggle input of built-in and mic jack appropriately */
-static void cxt5066_olpc_automic(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- unsigned int present;
-
- if (spec->dc_enable) /* don't do presence detection in DC mode */
- return;
-
- present = snd_hda_codec_read(codec, 0x1a, 0,
- AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
- if (present)
- snd_printdd("CXT5066: external microphone detected\n");
- else
- snd_printdd("CXT5066: external microphone absent\n");
-
- snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL,
- present ? 0 : 1);
- spec->ext_mic_present = !!present;
-
- cxt5066_olpc_select_mic(codec);
-}
-
-/* toggle input of built-in digital mic and mic jack appropriately */
-static void cxt5066_vostro_automic(struct hda_codec *codec)
-{
- unsigned int present;
-
- struct hda_verb ext_mic_present[] = {
- /* enable external mic, port B */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-
- /* switch to external mic input */
- {0x17, AC_VERB_SET_CONNECT_SEL, 0},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* disable internal digital mic */
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {}
- };
- static struct hda_verb ext_mic_absent[] = {
- /* enable internal mic, port C */
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- /* switch to internal mic input */
- {0x14, AC_VERB_SET_CONNECT_SEL, 2},
-
- /* disable external mic, port B */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {}
- };
-
- present = snd_hda_jack_detect(codec, 0x1a);
- if (present) {
- snd_printdd("CXT5066: external microphone detected\n");
- snd_hda_sequence_write(codec, ext_mic_present);
- } else {
- snd_printdd("CXT5066: external microphone absent\n");
- snd_hda_sequence_write(codec, ext_mic_absent);
- }
-}
-
-/* toggle input of built-in digital mic and mic jack appropriately */
-static void cxt5066_ideapad_automic(struct hda_codec *codec)
-{
- unsigned int present;
-
- struct hda_verb ext_mic_present[] = {
- {0x14, AC_VERB_SET_CONNECT_SEL, 0},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {}
- };
- static struct hda_verb ext_mic_absent[] = {
- {0x14, AC_VERB_SET_CONNECT_SEL, 2},
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {}
- };
-
- present = snd_hda_jack_detect(codec, 0x1b);
- if (present) {
- snd_printdd("CXT5066: external microphone detected\n");
- snd_hda_sequence_write(codec, ext_mic_present);
- } else {
- snd_printdd("CXT5066: external microphone absent\n");
- snd_hda_sequence_write(codec, ext_mic_absent);
- }
-}
-
-/* toggle input of built-in digital mic and mic jack appropriately */
-static void cxt5066_hp_laptop_automic(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x1b);
- snd_printdd("CXT5066: external microphone present=%d\n", present);
- snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL,
- present ? 1 : 3);
-}
-
-
-/* toggle input of built-in digital mic and mic jack appropriately
- order is: external mic -> dock mic -> interal mic */
-static void cxt5066_thinkpad_automic(struct hda_codec *codec)
-{
- unsigned int ext_present, dock_present;
-
- static struct hda_verb ext_mic_present[] = {
- {0x14, AC_VERB_SET_CONNECT_SEL, 0},
- {0x17, AC_VERB_SET_CONNECT_SEL, 1},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {}
- };
- static struct hda_verb dock_mic_present[] = {
- {0x14, AC_VERB_SET_CONNECT_SEL, 0},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {}
- };
- static struct hda_verb ext_mic_absent[] = {
- {0x14, AC_VERB_SET_CONNECT_SEL, 2},
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {}
- };
-
- ext_present = snd_hda_jack_detect(codec, 0x1b);
- dock_present = snd_hda_jack_detect(codec, 0x1a);
- if (ext_present) {
- snd_printdd("CXT5066: external microphone detected\n");
- snd_hda_sequence_write(codec, ext_mic_present);
- } else if (dock_present) {
- snd_printdd("CXT5066: dock microphone detected\n");
- snd_hda_sequence_write(codec, dock_mic_present);
- } else {
- snd_printdd("CXT5066: external microphone absent\n");
- snd_hda_sequence_write(codec, ext_mic_absent);
- }
-}
-
-/* mute internal speaker if HP is plugged */
-static void cxt5066_hp_automute(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- unsigned int portA, portD;
-
- /* Port A */
- portA = snd_hda_jack_detect(codec, 0x19);
-
- /* Port D */
- portD = snd_hda_jack_detect(codec, 0x1c);
-
- spec->hp_present = !!(portA);
- spec->hp_present |= portD ? 2 : 0;
- snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n",
- portA, portD, spec->hp_present);
- cxt5066_update_speaker(codec);
-}
-
-/* unsolicited event for jack sensing */
-static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- struct conexant_spec *spec = codec->spec;
- snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26);
- switch (res >> 26) {
- case CONEXANT_HP_EVENT:
- cxt5066_hp_automute(codec);
- break;
- case CONEXANT_MIC_EVENT:
- /* ignore mic events in DC mode; we're always using the jack */
- if (!spec->dc_enable)
- cxt5066_olpc_automic(codec);
- break;
- }
-}
-
-/* unsolicited event for jack sensing */
-static void cxt5066_vostro_event(struct hda_codec *codec, unsigned int res)
-{
- snd_printdd("CXT5066_vostro: unsol event %x (%x)\n", res, res >> 26);
- switch (res >> 26) {
- case CONEXANT_HP_EVENT:
- cxt5066_hp_automute(codec);
- break;
- case CONEXANT_MIC_EVENT:
- cxt5066_vostro_automic(codec);
- break;
- }
-}
-
-/* unsolicited event for jack sensing */
-static void cxt5066_ideapad_event(struct hda_codec *codec, unsigned int res)
-{
- snd_printdd("CXT5066_ideapad: unsol event %x (%x)\n", res, res >> 26);
- switch (res >> 26) {
- case CONEXANT_HP_EVENT:
- cxt5066_hp_automute(codec);
- break;
- case CONEXANT_MIC_EVENT:
- cxt5066_ideapad_automic(codec);
- break;
- }
-}
-
-/* unsolicited event for jack sensing */
-static void cxt5066_hp_laptop_event(struct hda_codec *codec, unsigned int res)
-{
- snd_printdd("CXT5066_hp_laptop: unsol event %x (%x)\n", res, res >> 26);
- switch (res >> 26) {
- case CONEXANT_HP_EVENT:
- cxt5066_hp_automute(codec);
- break;
- case CONEXANT_MIC_EVENT:
- cxt5066_hp_laptop_automic(codec);
- break;
- }
-}
-
-/* unsolicited event for jack sensing */
-static void cxt5066_thinkpad_event(struct hda_codec *codec, unsigned int res)
-{
- snd_printdd("CXT5066_thinkpad: unsol event %x (%x)\n", res, res >> 26);
- switch (res >> 26) {
- case CONEXANT_HP_EVENT:
- cxt5066_hp_automute(codec);
- break;
- case CONEXANT_MIC_EVENT:
- cxt5066_thinkpad_automic(codec);
- break;
- }
-}
-
-static const struct hda_input_mux cxt5066_analog_mic_boost = {
- .num_items = 5,
- .items = {
- { "0dB", 0 },
- { "10dB", 1 },
- { "20dB", 2 },
- { "30dB", 3 },
- { "40dB", 4 },
- },
-};
-
-static void cxt5066_set_mic_boost(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- snd_hda_codec_write_cache(codec, 0x17, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT |
- cxt5066_analog_mic_boost.items[spec->mic_boost].index);
- if (spec->ideapad || spec->thinkpad) {
- /* adjust the internal mic as well...it is not through 0x17 */
- snd_hda_codec_write_cache(codec, 0x23, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_INPUT |
- cxt5066_analog_mic_boost.
- items[spec->mic_boost].index);
- }
-}
-
-static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- return snd_hda_input_mux_info(&cxt5066_analog_mic_boost, uinfo);
-}
-
-static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- ucontrol->value.enumerated.item[0] = spec->mic_boost;
- return 0;
-}
-
-static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
- unsigned int idx;
- idx = ucontrol->value.enumerated.item[0];
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
-
- spec->mic_boost = idx;
- if (!spec->dc_enable)
- cxt5066_set_mic_boost(codec);
- return 1;
-}
-
-static void cxt5066_enable_dc(struct hda_codec *codec)
-{
- const struct hda_verb enable_dc_mode[] = {
- /* disable gain */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* switch to DC input */
- {0x17, AC_VERB_SET_CONNECT_SEL, 3},
- {}
- };
-
- /* configure as input source */
- snd_hda_sequence_write(codec, enable_dc_mode);
- cxt5066_olpc_select_mic(codec); /* also sets configured bias */
-}
-
-static void cxt5066_disable_dc(struct hda_codec *codec)
-{
- /* reconfigure input source */
- cxt5066_set_mic_boost(codec);
- /* automic also selects the right mic if we're recording */
- cxt5066_olpc_automic(codec);
-}
-
-static int cxt5066_olpc_dc_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- ucontrol->value.integer.value[0] = spec->dc_enable;
- return 0;
-}
-
-static int cxt5066_olpc_dc_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- int dc_enable = !!ucontrol->value.integer.value[0];
-
- if (dc_enable == spec->dc_enable)
- return 0;
-
- spec->dc_enable = dc_enable;
- if (dc_enable)
- cxt5066_enable_dc(codec);
- else
- cxt5066_disable_dc(codec);
-
- return 1;
-}
-
-static int cxt5066_olpc_dc_bias_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- return snd_hda_input_mux_info(&cxt5066_olpc_dc_bias, uinfo);
-}
-
-static int cxt5066_olpc_dc_bias_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- ucontrol->value.enumerated.item[0] = spec->dc_input_bias;
- return 0;
-}
-
-static int cxt5066_olpc_dc_bias_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct conexant_spec *spec = codec->spec;
- const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
- unsigned int idx;
-
- idx = ucontrol->value.enumerated.item[0];
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
-
- spec->dc_input_bias = idx;
- if (spec->dc_enable)
- cxt5066_set_olpc_dc_bias(codec);
- return 1;
-}
-
-static void cxt5066_olpc_capture_prepare(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- /* mark as recording and configure the microphone widget so that the
- * recording LED comes on. */
- spec->recording = 1;
- cxt5066_olpc_select_mic(codec);
-}
-
-static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- const struct hda_verb disable_mics[] = {
- /* disable external mic, port B */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* disble internal mic, port C */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* disable DC capture, port F */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {},
- };
-
- snd_hda_sequence_write(codec, disable_mics);
- spec->recording = 0;
-}
-
-static struct hda_input_mux cxt5066_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic B", 0 },
- { "Mic C", 1 },
- { "Mic E", 2 },
- { "Mic F", 3 },
- },
-};
-
-static struct hda_bind_ctls cxt5066_bind_capture_vol_others = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT),
- 0
- },
-};
-
-static struct hda_bind_ctls cxt5066_bind_capture_sw_others = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new cxt5066_mixer_master[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
- {}
-};
-
-static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Volume",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ |
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_volume_info,
- .get = snd_hda_mixer_amp_volume_get,
- .put = snd_hda_mixer_amp_volume_put,
- .tlv = { .c = snd_hda_mixer_amp_tlv },
- /* offset by 28 volume steps to limit minimum gain to -46dB */
- .private_value =
- HDA_COMPOSE_AMP_VAL_OFS(0x10, 3, 0, HDA_OUTPUT, 28),
- },
- {}
-};
-
-static struct snd_kcontrol_new cxt5066_mixer_olpc_dc[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "DC Mode Enable Switch",
- .info = snd_ctl_boolean_mono_info,
- .get = cxt5066_olpc_dc_get,
- .put = cxt5066_olpc_dc_put,
- },
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "DC Input Bias Enum",
- .info = cxt5066_olpc_dc_bias_enum_info,
- .get = cxt5066_olpc_dc_bias_enum_get,
- .put = cxt5066_olpc_dc_bias_enum_put,
- },
- {}
-};
-
-static struct snd_kcontrol_new cxt5066_mixers[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .info = cxt_eapd_info,
- .get = cxt_eapd_get,
- .put = cxt5066_hp_master_sw_put,
- .private_value = 0x1d,
- },
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Analog Mic Boost Capture Enum",
- .info = cxt5066_mic_boost_mux_enum_info,
- .get = cxt5066_mic_boost_mux_enum_get,
- .put = cxt5066_mic_boost_mux_enum_put,
- },
-
- HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others),
- HDA_BIND_SW("Capture Switch", &cxt5066_bind_capture_sw_others),
- {}
-};
-
-static struct snd_kcontrol_new cxt5066_vostro_mixers[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Int Mic Boost Capture Enum",
- .info = cxt5066_mic_boost_mux_enum_info,
- .get = cxt5066_mic_boost_mux_enum_get,
- .put = cxt5066_mic_boost_mux_enum_put,
- .private_value = 0x23 | 0x100,
- },
- {}
-};
-
-static struct hda_verb cxt5066_init_verbs[] = {
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */
-
- /* Speakers */
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* HP, Amp */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /* no digital microphone support yet */
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Audio input selector */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
-
- /* SPDIF route: PCM */
- {0x20, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x0},
-
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* EAPD */
- {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
-
- /* not handling these yet */
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
- {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
- {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
- {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
- {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
- { } /* end */
-};
-
-static struct hda_verb cxt5066_init_verbs_olpc[] = {
- /* Port A: headphones */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* Port B: external microphone */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port C: internal microphone */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port D: unused */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port E: unused, but has primary EAPD */
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
-
- /* Port F: external DC input through microphone port */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port G: internal speakers */
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* DAC2: unused */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-
- /* Disable digital microphone port */
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Audio input selectors */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-
- /* Disable SPDIF */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* enable unsolicited events for Port A and B */
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
- { } /* end */
-};
-
-static struct hda_verb cxt5066_init_verbs_vostro[] = {
- /* Port A: headphones */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* Port B: external microphone */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port C: unused */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port D: unused */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port E: unused, but has primary EAPD */
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
-
- /* Port F: unused */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port G: internal speakers */
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* DAC2: unused */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-
- /* Digital microphone port */
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- /* Audio input selectors */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-
- /* Disable SPDIF */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* enable unsolicited events for Port A and B */
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
- { } /* end */
-};
-
-static struct hda_verb cxt5066_init_verbs_ideapad[] = {
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */
-
- /* Speakers */
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* HP, Amp */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x14, AC_VERB_SET_CONNECT_SEL, 2}, /* default to internal mic */
-
- /* Audio input selector */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x2},
- {0x17, AC_VERB_SET_CONNECT_SEL, 1}, /* route ext mic */
-
- /* SPDIF route: PCM */
- {0x20, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x0},
-
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* internal microphone */
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable int mic */
-
- /* EAPD */
- {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
-
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
- { } /* end */
-};
-
-static struct hda_verb cxt5066_init_verbs_thinkpad[] = {
- {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */
-
- /* Port G: internal speakers */
- {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* Port A: HP, Amp */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* Port B: Mic Dock */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port C: Mic */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-
- /* Port D: HP Dock, Amp */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
-
- /* DAC1 */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x14, AC_VERB_SET_CONNECT_SEL, 2}, /* default to internal mic */
-
- /* Audio input selector */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x2},
- {0x17, AC_VERB_SET_CONNECT_SEL, 1}, /* route ext mic */
-
- /* SPDIF route: PCM */
- {0x20, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x0},
-
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* internal microphone */
- {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable int mic */
-
- /* EAPD */
- {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
-
- /* enable unsolicited events for Port A, B, C and D */
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
- { } /* end */
-};
-
-static struct hda_verb cxt5066_init_verbs_portd_lo[] = {
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- { } /* end */
-};
-
-
-static struct hda_verb cxt5066_init_verbs_hp_laptop[] = {
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x0},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
- { } /* end */
-};
-
-/* initialize jack-sensing, too */
-static int cxt5066_init(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
-
- snd_printdd("CXT5066: init\n");
- conexant_init(codec);
- if (codec->patch_ops.unsol_event) {
- cxt5066_hp_automute(codec);
- if (spec->dell_vostro)
- cxt5066_vostro_automic(codec);
- else if (spec->ideapad)
- cxt5066_ideapad_automic(codec);
- else if (spec->thinkpad)
- cxt5066_thinkpad_automic(codec);
- else if (spec->hp_laptop)
- cxt5066_hp_laptop_automic(codec);
- }
- cxt5066_set_mic_boost(codec);
- return 0;
-}
-
-static int cxt5066_olpc_init(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- snd_printdd("CXT5066: init\n");
- conexant_init(codec);
- cxt5066_hp_automute(codec);
- if (!spec->dc_enable) {
- cxt5066_set_mic_boost(codec);
- cxt5066_olpc_automic(codec);
- } else {
- cxt5066_enable_dc(codec);
- }
- return 0;
-}
-
-enum {
- CXT5066_LAPTOP, /* Laptops w/ EAPD support */
- CXT5066_DELL_LAPTOP, /* Dell Laptop */
- CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */
- CXT5066_DELL_VOSTRO, /* Dell Vostro 1015i */
- CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */
- CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */
- CXT5066_HP_LAPTOP, /* HP Laptop */
- CXT5066_MODELS
-};
-
-static const char *cxt5066_models[CXT5066_MODELS] = {
- [CXT5066_LAPTOP] = "laptop",
- [CXT5066_DELL_LAPTOP] = "dell-laptop",
- [CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5",
- [CXT5066_DELL_VOSTRO] = "dell-vostro",
- [CXT5066_IDEAPAD] = "ideapad",
- [CXT5066_THINKPAD] = "thinkpad",
- [CXT5066_HP_LAPTOP] = "hp-laptop",
-};
-
-static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
- SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO),
- SND_PCI_QUIRK(0x1028, 0x02f5, "Dell",
- CXT5066_DELL_LAPTOP),
- SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO),
- SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP),
- SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5),
- SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5),
- SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
- CXT5066_LAPTOP),
- SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
- SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD),
- SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x17aa, 0x21b3, "Thinkpad Edge 13 (197)", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x17aa, 0x21b4, "Thinkpad Edge", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD),
- SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G series", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x17aa, 0x390a, "Lenovo S10-3t", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G series (AMD)", CXT5066_IDEAPAD),
- SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD),
- {}
-};
-
-static int patch_cxt5066(struct hda_codec *codec)
-{
- struct conexant_spec *spec;
- int board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
- codec->spec = spec;
-
- codec->patch_ops = conexant_patch_ops;
- codec->patch_ops.init = conexant_init;
-
- spec->dell_automute = 0;
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = ARRAY_SIZE(cxt5066_dac_nids);
- spec->multiout.dac_nids = cxt5066_dac_nids;
- spec->multiout.dig_out_nid = CXT5066_SPDIF_OUT;
- spec->num_adc_nids = 1;
- spec->adc_nids = cxt5066_adc_nids;
- spec->capsrc_nids = cxt5066_capsrc_nids;
- spec->input_mux = &cxt5066_capture_source;
-
- spec->port_d_mode = PIN_HP;
-
- spec->num_init_verbs = 1;
- spec->init_verbs[0] = cxt5066_init_verbs;
- spec->num_channel_mode = ARRAY_SIZE(cxt5066_modes);
- spec->channel_mode = cxt5066_modes;
- spec->cur_adc = 0;
- spec->cur_adc_idx = 0;
-
- set_beep_amp(spec, 0x13, 0, HDA_OUTPUT);
-
- board_config = snd_hda_check_board_config(codec, CXT5066_MODELS,
- cxt5066_models, cxt5066_cfg_tbl);
- switch (board_config) {
- default:
- case CXT5066_LAPTOP:
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
- spec->mixers[spec->num_mixers++] = cxt5066_mixers;
- break;
- case CXT5066_DELL_LAPTOP:
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
- spec->mixers[spec->num_mixers++] = cxt5066_mixers;
-
- spec->port_d_mode = PIN_OUT;
- spec->init_verbs[spec->num_init_verbs] = cxt5066_init_verbs_portd_lo;
- spec->num_init_verbs++;
- spec->dell_automute = 1;
- break;
- case CXT5066_HP_LAPTOP:
- codec->patch_ops.init = cxt5066_init;
- codec->patch_ops.unsol_event = cxt5066_hp_laptop_event;
- spec->init_verbs[spec->num_init_verbs] =
- cxt5066_init_verbs_hp_laptop;
- spec->num_init_verbs++;
- spec->hp_laptop = 1;
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
- spec->mixers[spec->num_mixers++] = cxt5066_mixers;
- /* no S/PDIF out */
- spec->multiout.dig_out_nid = 0;
- /* input source automatically selected */
- spec->input_mux = NULL;
- spec->port_d_mode = 0;
- spec->mic_boost = 3; /* default 30dB gain */
- break;
-
- case CXT5066_OLPC_XO_1_5:
- codec->patch_ops.init = cxt5066_olpc_init;
- codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event;
- spec->init_verbs[0] = cxt5066_init_verbs_olpc;
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_olpc_dc;
- spec->mixers[spec->num_mixers++] = cxt5066_mixers;
- spec->port_d_mode = 0;
- spec->mic_boost = 3; /* default 30dB gain */
-
- /* no S/PDIF out */
- spec->multiout.dig_out_nid = 0;
-
- /* input source automatically selected */
- spec->input_mux = NULL;
-
- /* our capture hooks which allow us to turn on the microphone LED
- * at the right time */
- spec->capture_prepare = cxt5066_olpc_capture_prepare;
- spec->capture_cleanup = cxt5066_olpc_capture_cleanup;
- break;
- case CXT5066_DELL_VOSTRO:
- codec->patch_ops.init = cxt5066_init;
- codec->patch_ops.unsol_event = cxt5066_vostro_event;
- spec->init_verbs[0] = cxt5066_init_verbs_vostro;
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
- spec->mixers[spec->num_mixers++] = cxt5066_mixers;
- spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers;
- spec->port_d_mode = 0;
- spec->dell_vostro = 1;
- spec->mic_boost = 3; /* default 30dB gain */
-
- /* no S/PDIF out */
- spec->multiout.dig_out_nid = 0;
-
- /* input source automatically selected */
- spec->input_mux = NULL;
- break;
- case CXT5066_IDEAPAD:
- codec->patch_ops.init = cxt5066_init;
- codec->patch_ops.unsol_event = cxt5066_ideapad_event;
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
- spec->mixers[spec->num_mixers++] = cxt5066_mixers;
- spec->init_verbs[0] = cxt5066_init_verbs_ideapad;
- spec->port_d_mode = 0;
- spec->ideapad = 1;
- spec->mic_boost = 2; /* default 20dB gain */
-
- /* no S/PDIF out */
- spec->multiout.dig_out_nid = 0;
-
- /* input source automatically selected */
- spec->input_mux = NULL;
- break;
- case CXT5066_THINKPAD:
- codec->patch_ops.init = cxt5066_init;
- codec->patch_ops.unsol_event = cxt5066_thinkpad_event;
- spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
- spec->mixers[spec->num_mixers++] = cxt5066_mixers;
- spec->init_verbs[0] = cxt5066_init_verbs_thinkpad;
- spec->thinkpad = 1;
- spec->port_d_mode = PIN_OUT;
- spec->mic_boost = 2; /* default 20dB gain */
-
- /* no S/PDIF out */
- spec->multiout.dig_out_nid = 0;
-
- /* input source automatically selected */
- spec->input_mux = NULL;
- break;
- }
-
- if (spec->beep_amp)
- snd_hda_attach_beep_device(codec, spec->beep_amp);
-
- return 0;
-}
-
-/*
- * Automatic parser for CX20641 & co
- */
-
-static hda_nid_t cx_auto_adc_nids[] = { 0x14 };
-
-/* get the connection index of @nid in the widget @mux */
-static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
- hda_nid_t nid)
-{
- hda_nid_t conn[HDA_MAX_NUM_INPUTS];
- int i, nums;
-
- nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
- for (i = 0; i < nums; i++)
- if (conn[i] == nid)
- return i;
- return -1;
-}
-
-/* get an unassigned DAC from the given list.
- * Return the nid if found and reduce the DAC list, or return zero if
- * not found
- */
-static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t pin,
- hda_nid_t *dacs, int *num_dacs)
-{
- int i, nums = *num_dacs;
- hda_nid_t ret = 0;
-
- for (i = 0; i < nums; i++) {
- if (get_connection_index(codec, pin, dacs[i]) >= 0) {
- ret = dacs[i];
- break;
- }
- }
- if (!ret)
- return 0;
- if (--nums > 0)
- memmove(dacs, dacs + 1, nums * sizeof(hda_nid_t));
- *num_dacs = nums;
- return ret;
-}
-
-#define MAX_AUTO_DACS 5
-
-/* fill analog DAC list from the widget tree */
-static int fill_cx_auto_dacs(struct hda_codec *codec, hda_nid_t *dacs)
-{
- hda_nid_t nid, end_nid;
- int nums = 0;
-
- end_nid = codec->start_nid + codec->num_nodes;
- for (nid = codec->start_nid; nid < end_nid; nid++) {
- unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int type = get_wcaps_type(wcaps);
- if (type == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) {
- dacs[nums++] = nid;
- if (nums >= MAX_AUTO_DACS)
- break;
- }
- }
- return nums;
-}
-
-/* fill pin_dac_pair list from the pin and dac list */
-static int fill_dacs_for_pins(struct hda_codec *codec, hda_nid_t *pins,
- int num_pins, hda_nid_t *dacs, int *rest,
- struct pin_dac_pair *filled, int type)
-{
- int i, nums;
-
- nums = 0;
- for (i = 0; i < num_pins; i++) {
- filled[nums].pin = pins[i];
- filled[nums].type = type;
- filled[nums].dac = get_unassigned_dac(codec, pins[i], dacs, rest);
- nums++;
- }
- return nums;
-}
-
-/* parse analog output paths */
-static void cx_auto_parse_output(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t dacs[MAX_AUTO_DACS];
- int i, j, nums, rest;
-
- rest = fill_cx_auto_dacs(codec, dacs);
- /* parse all analog output pins */
- nums = fill_dacs_for_pins(codec, cfg->line_out_pins, cfg->line_outs,
- dacs, &rest, spec->dac_info,
- AUTO_PIN_LINE_OUT);
- nums += fill_dacs_for_pins(codec, cfg->hp_pins, cfg->hp_outs,
- dacs, &rest, spec->dac_info + nums,
- AUTO_PIN_HP_OUT);
- nums += fill_dacs_for_pins(codec, cfg->speaker_pins, cfg->speaker_outs,
- dacs, &rest, spec->dac_info + nums,
- AUTO_PIN_SPEAKER_OUT);
- spec->dac_info_filled = nums;
- /* fill multiout struct */
- for (i = 0; i < nums; i++) {
- hda_nid_t dac = spec->dac_info[i].dac;
- if (!dac)
- continue;
- switch (spec->dac_info[i].type) {
- case AUTO_PIN_LINE_OUT:
- spec->private_dac_nids[spec->multiout.num_dacs] = dac;
- spec->multiout.num_dacs++;
- break;
- case AUTO_PIN_HP_OUT:
- case AUTO_PIN_SPEAKER_OUT:
- if (!spec->multiout.hp_nid) {
- spec->multiout.hp_nid = dac;
- break;
- }
- for (j = 0; j < ARRAY_SIZE(spec->multiout.extra_out_nid); j++)
- if (!spec->multiout.extra_out_nid[j]) {
- spec->multiout.extra_out_nid[j] = dac;
- break;
- }
- break;
- }
- }
- spec->multiout.dac_nids = spec->private_dac_nids;
- spec->multiout.max_channels = nums * 2;
-
- if (cfg->hp_outs > 0)
- spec->auto_mute = 1;
- spec->vmaster_nid = spec->private_dac_nids[0];
-}
-
-/* auto-mute/unmute speaker and line outs according to headphone jack */
-static void cx_auto_hp_automute(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, present;
-
- if (!spec->auto_mute)
- return;
- present = 0;
- for (i = 0; i < cfg->hp_outs; i++) {
- if (snd_hda_jack_detect(codec, cfg->hp_pins[i])) {
- present = 1;
- break;
- }
- }
- for (i = 0; i < cfg->line_outs; i++) {
- snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- present ? 0 : PIN_OUT);
- }
- for (i = 0; i < cfg->speaker_outs; i++) {
- snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- present ? 0 : PIN_OUT);
- }
-}
-
-/* automatic switch internal and external mic */
-static void cx_auto_automic(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- struct hda_input_mux *imux = &spec->private_imux;
- int ext_idx = spec->auto_mic_ext;
-
- if (!spec->auto_mic)
- return;
- if (snd_hda_jack_detect(codec, cfg->inputs[ext_idx].pin)) {
- snd_hda_codec_write(codec, spec->adc_nids[0], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[ext_idx].index);
- } else {
- snd_hda_codec_write(codec, spec->adc_nids[0], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[!ext_idx].index);
- }
-}
-
-static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20;
- switch (res >> 26) {
- case CONEXANT_HP_EVENT:
- cx_auto_hp_automute(codec);
- conexant_report_jack(codec, nid);
- break;
- case CONEXANT_MIC_EVENT:
- cx_auto_automic(codec);
- conexant_report_jack(codec, nid);
- break;
- }
-}
-
-/* return true if it's an internal-mic pin */
-static int is_int_mic(struct hda_codec *codec, hda_nid_t pin)
-{
- unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
- return get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
- snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT;
-}
-
-/* return true if it's an external-mic pin */
-static int is_ext_mic(struct hda_codec *codec, hda_nid_t pin)
-{
- unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
- return get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
- snd_hda_get_input_pin_attr(def_conf) >= INPUT_PIN_ATTR_NORMAL &&
- (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_PRES_DETECT);
-}
-
-/* check whether the pin config is suitable for auto-mic switching;
- * auto-mic is enabled only when one int-mic and one-ext mic exist
- */
-static void cx_auto_check_auto_mic(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
-
- if (is_ext_mic(codec, cfg->inputs[0].pin) &&
- is_int_mic(codec, cfg->inputs[1].pin)) {
- spec->auto_mic = 1;
- spec->auto_mic_ext = 1;
- return;
- }
- if (is_int_mic(codec, cfg->inputs[1].pin) &&
- is_ext_mic(codec, cfg->inputs[0].pin)) {
- spec->auto_mic = 1;
- spec->auto_mic_ext = 0;
- return;
- }
-}
-
-static void cx_auto_parse_input(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- struct hda_input_mux *imux;
- int i;
-
- imux = &spec->private_imux;
- for (i = 0; i < cfg->num_inputs; i++) {
- int idx = get_connection_index(codec, spec->adc_nids[0],
- cfg->inputs[i].pin);
- if (idx >= 0) {
- const char *label;
- label = hda_get_autocfg_input_label(codec, cfg, i);
- snd_hda_add_imux_item(imux, label, idx, NULL);
- }
- }
- if (imux->num_items == 2 && cfg->num_inputs == 2)
- cx_auto_check_auto_mic(codec);
- if (imux->num_items > 1 && !spec->auto_mic)
- spec->input_mux = imux;
-}
-
-/* get digital-input audio widget corresponding to the given pin */
-static hda_nid_t cx_auto_get_dig_in(struct hda_codec *codec, hda_nid_t pin)
-{
- hda_nid_t nid, end_nid;
-
- end_nid = codec->start_nid + codec->num_nodes;
- for (nid = codec->start_nid; nid < end_nid; nid++) {
- unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int type = get_wcaps_type(wcaps);
- if (type == AC_WID_AUD_IN && (wcaps & AC_WCAP_DIGITAL)) {
- if (get_connection_index(codec, nid, pin) >= 0)
- return nid;
- }
- }
- return 0;
-}
-
-static void cx_auto_parse_digital(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
-
- if (cfg->dig_outs &&
- snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) == 1)
- spec->multiout.dig_out_nid = nid;
- if (cfg->dig_in_pin)
- spec->dig_in_nid = cx_auto_get_dig_in(codec, cfg->dig_in_pin);
-}
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-static void cx_auto_parse_beep(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- hda_nid_t nid, end_nid;
-
- end_nid = codec->start_nid + codec->num_nodes;
- for (nid = codec->start_nid; nid < end_nid; nid++)
- if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) {
- set_beep_amp(spec, nid, 0, HDA_OUTPUT);
- break;
- }
-}
-#else
-#define cx_auto_parse_beep(codec)
-#endif
-
-static int cx_auto_parse_auto_config(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
-
- cx_auto_parse_output(codec);
- cx_auto_parse_input(codec);
- cx_auto_parse_digital(codec);
- cx_auto_parse_beep(codec);
- return 0;
-}
-
-static void cx_auto_turn_on_eapd(struct hda_codec *codec, int num_pins,
- hda_nid_t *pins)
-{
- int i;
- for (i = 0; i < num_pins; i++) {
- if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD)
- snd_hda_codec_write(codec, pins[i], 0,
- AC_VERB_SET_EAPD_BTLENABLE, 0x02);
- }
-}
-
-static void select_connection(struct hda_codec *codec, hda_nid_t pin,
- hda_nid_t src)
-{
- int idx = get_connection_index(codec, pin, src);
- if (idx >= 0)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_CONNECT_SEL, idx);
-}
-
-static void cx_auto_init_output(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
- int i;
-
- for (i = 0; i < spec->multiout.num_dacs; i++)
- snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
-
- for (i = 0; i < cfg->hp_outs; i++)
- snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
- if (spec->auto_mute) {
- for (i = 0; i < cfg->hp_outs; i++) {
- snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | CONEXANT_HP_EVENT);
- }
- cx_auto_hp_automute(codec);
- } else {
- for (i = 0; i < cfg->line_outs; i++)
- snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- for (i = 0; i < cfg->speaker_outs; i++)
- snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- }
-
- for (i = 0; i < spec->dac_info_filled; i++) {
- nid = spec->dac_info[i].dac;
- if (!nid)
- nid = spec->multiout.dac_nids[0];
- select_connection(codec, spec->dac_info[i].pin, nid);
- }
-
- /* turn on EAPD */
- cx_auto_turn_on_eapd(codec, cfg->line_outs, cfg->line_out_pins);
- cx_auto_turn_on_eapd(codec, cfg->hp_outs, cfg->hp_pins);
- cx_auto_turn_on_eapd(codec, cfg->speaker_outs, cfg->speaker_pins);
-}
-
-static void cx_auto_init_input(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < spec->num_adc_nids; i++)
- snd_hda_codec_write(codec, spec->adc_nids[i], 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0));
-
- for (i = 0; i < cfg->num_inputs; i++) {
- unsigned int type;
- if (cfg->inputs[i].type == AUTO_PIN_MIC)
- type = PIN_VREF80;
- else
- type = PIN_IN;
- snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, type);
- }
-
- if (spec->auto_mic) {
- int ext_idx = spec->auto_mic_ext;
- snd_hda_codec_write(codec, cfg->inputs[ext_idx].pin, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | CONEXANT_MIC_EVENT);
- cx_auto_automic(codec);
- } else {
- for (i = 0; i < spec->num_adc_nids; i++) {
- snd_hda_codec_write(codec, spec->adc_nids[i], 0,
- AC_VERB_SET_CONNECT_SEL,
- spec->private_imux.items[0].index);
- }
- }
-}
-
-static void cx_auto_init_digital(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
-
- if (spec->multiout.dig_out_nid)
- snd_hda_codec_write(codec, cfg->dig_out_pins[0], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- if (spec->dig_in_nid)
- snd_hda_codec_write(codec, cfg->dig_in_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
-}
-
-static int cx_auto_init(struct hda_codec *codec)
-{
- /*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/
- cx_auto_init_output(codec);
- cx_auto_init_input(codec);
- cx_auto_init_digital(codec);
- return 0;
-}
-
-static int cx_auto_add_volume(struct hda_codec *codec, const char *basename,
- const char *dir, int cidx,
- hda_nid_t nid, int hda_dir)
-{
- static char name[32];
- static struct snd_kcontrol_new knew[] = {
- HDA_CODEC_VOLUME(name, 0, 0, 0),
- HDA_CODEC_MUTE(name, 0, 0, 0),
- };
- static char *sfx[2] = { "Volume", "Switch" };
- int i, err;
-
- for (i = 0; i < 2; i++) {
- struct snd_kcontrol *kctl;
- knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, hda_dir);
- knew[i].subdevice = HDA_SUBDEV_AMP_FLAG;
- knew[i].index = cidx;
- snprintf(name, sizeof(name), "%s%s %s", basename, dir, sfx[i]);
- kctl = snd_ctl_new1(&knew[i], codec);
- if (!kctl)
- return -ENOMEM;
- err = snd_hda_ctl_add(codec, nid, kctl);
- if (err < 0)
- return err;
- if (!(query_amp_caps(codec, nid, hda_dir) & AC_AMPCAP_MUTE))
- break;
- }
- return 0;
-}
-
-#define cx_auto_add_pb_volume(codec, nid, str, idx) \
- cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT)
-
-static int cx_auto_build_output_controls(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- int i, err;
- int num_line = 0, num_hp = 0, num_spk = 0;
- static const char *texts[3] = { "Front", "Surround", "CLFE" };
-
- if (spec->dac_info_filled == 1)
- return cx_auto_add_pb_volume(codec, spec->dac_info[0].dac,
- "Master", 0);
- for (i = 0; i < spec->dac_info_filled; i++) {
- const char *label;
- int idx, type;
- if (!spec->dac_info[i].dac)
- continue;
- type = spec->dac_info[i].type;
- if (type == AUTO_PIN_LINE_OUT)
- type = spec->autocfg.line_out_type;
- switch (type) {
- case AUTO_PIN_LINE_OUT:
- default:
- label = texts[num_line++];
- idx = 0;
- break;
- case AUTO_PIN_HP_OUT:
- label = "Headphone";
- idx = num_hp++;
- break;
- case AUTO_PIN_SPEAKER_OUT:
- label = "Speaker";
- idx = num_spk++;
- break;
- }
- err = cx_auto_add_pb_volume(codec, spec->dac_info[i].dac,
- label, idx);
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-static int cx_auto_build_input_controls(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- static const char *prev_label;
- int i, err, cidx;
-
- err = cx_auto_add_volume(codec, "Capture", "", 0, spec->adc_nids[0],
- HDA_INPUT);
- if (err < 0)
- return err;
- prev_label = NULL;
- cidx = 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- const char *label;
- if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
- continue;
- label = hda_get_autocfg_input_label(codec, cfg, i);
- if (label == prev_label)
- cidx++;
- else
- cidx = 0;
- prev_label = label;
- err = cx_auto_add_volume(codec, label, " Capture", cidx,
- nid, HDA_INPUT);
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-static int cx_auto_build_controls(struct hda_codec *codec)
-{
- int err;
-
- err = cx_auto_build_output_controls(codec);
- if (err < 0)
- return err;
- err = cx_auto_build_input_controls(codec);
- if (err < 0)
- return err;
- return conexant_build_controls(codec);
-}
-
-static struct hda_codec_ops cx_auto_patch_ops = {
- .build_controls = cx_auto_build_controls,
- .build_pcms = conexant_build_pcms,
- .init = cx_auto_init,
- .free = conexant_free,
- .unsol_event = cx_auto_unsol_event,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .suspend = conexant_suspend,
-#endif
- .reboot_notify = snd_hda_shutup_pins,
-};
-
-static int patch_conexant_auto(struct hda_codec *codec)
-{
- struct conexant_spec *spec;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
- codec->spec = spec;
- spec->adc_nids = cx_auto_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(cx_auto_adc_nids);
- spec->capsrc_nids = spec->adc_nids;
- err = cx_auto_parse_auto_config(codec);
- if (err < 0) {
- kfree(codec->spec);
- codec->spec = NULL;
- return err;
- }
- codec->patch_ops = cx_auto_patch_ops;
- if (spec->beep_amp)
- snd_hda_attach_beep_device(codec, spec->beep_amp);
- return 0;
-}
-
-/*
- */
-
-static struct hda_codec_preset snd_hda_preset_conexant[] = {
- { .id = 0x14f15045, .name = "CX20549 (Venice)",
- .patch = patch_cxt5045 },
- { .id = 0x14f15047, .name = "CX20551 (Waikiki)",
- .patch = patch_cxt5047 },
- { .id = 0x14f15051, .name = "CX20561 (Hermosa)",
- .patch = patch_cxt5051 },
- { .id = 0x14f15066, .name = "CX20582 (Pebble)",
- .patch = patch_cxt5066 },
- { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)",
- .patch = patch_cxt5066 },
- { .id = 0x14f15068, .name = "CX20584",
- .patch = patch_cxt5066 },
- { .id = 0x14f15069, .name = "CX20585",
- .patch = patch_cxt5066 },
- { .id = 0x14f15097, .name = "CX20631",
- .patch = patch_conexant_auto },
- { .id = 0x14f15098, .name = "CX20632",
- .patch = patch_conexant_auto },
- { .id = 0x14f150a1, .name = "CX20641",
- .patch = patch_conexant_auto },
- { .id = 0x14f150a2, .name = "CX20642",
- .patch = patch_conexant_auto },
- { .id = 0x14f150ab, .name = "CX20651",
- .patch = patch_conexant_auto },
- { .id = 0x14f150ac, .name = "CX20652",
- .patch = patch_conexant_auto },
- { .id = 0x14f150b8, .name = "CX20664",
- .patch = patch_conexant_auto },
- { .id = 0x14f150b9, .name = "CX20665",
- .patch = patch_conexant_auto },
- {} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:14f15045");
-MODULE_ALIAS("snd-hda-codec-id:14f15047");
-MODULE_ALIAS("snd-hda-codec-id:14f15051");
-MODULE_ALIAS("snd-hda-codec-id:14f15066");
-MODULE_ALIAS("snd-hda-codec-id:14f15067");
-MODULE_ALIAS("snd-hda-codec-id:14f15068");
-MODULE_ALIAS("snd-hda-codec-id:14f15069");
-MODULE_ALIAS("snd-hda-codec-id:14f15097");
-MODULE_ALIAS("snd-hda-codec-id:14f15098");
-MODULE_ALIAS("snd-hda-codec-id:14f150a1");
-MODULE_ALIAS("snd-hda-codec-id:14f150a2");
-MODULE_ALIAS("snd-hda-codec-id:14f150ab");
-MODULE_ALIAS("snd-hda-codec-id:14f150ac");
-MODULE_ALIAS("snd-hda-codec-id:14f150b8");
-MODULE_ALIAS("snd-hda-codec-id:14f150b9");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Conexant HD-audio codec");
-
-static struct hda_codec_preset_list conexant_list = {
- .preset = snd_hda_preset_conexant,
- .owner = THIS_MODULE,
-};
-
-static int __init patch_conexant_init(void)
-{
- return snd_hda_add_codec_preset(&conexant_list);
-}
-
-static void __exit patch_conexant_exit(void)
-{
- snd_hda_delete_codec_preset(&conexant_list);
-}
-
-module_init(patch_conexant_init)
-module_exit(patch_conexant_exit)
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
deleted file mode 100644
index d3e49aa5b9ec..000000000000
--- a/sound/pci/hda/patch_hdmi.c
+++ /dev/null
@@ -1,1639 +0,0 @@
-/*
- *
- * patch_hdmi.c - routines for HDMI/DisplayPort codecs
- *
- * Copyright(c) 2008-2010 Intel Corporation. All rights reserved.
- * Copyright (c) 2006 ATI Technologies Inc.
- * Copyright (c) 2008 NVIDIA Corp. All rights reserved.
- * Copyright (c) 2008 Wei Ni <wni@nvidia.com>
- *
- * Authors:
- * Wu Fengguang <wfg@linux.intel.com>
- *
- * Maintained by:
- * Wu Fengguang <wfg@linux.intel.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-/*
- * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
- * could support two independent pipes, each of them can be connected to one or
- * more ports (DVI, HDMI or DisplayPort).
- *
- * The HDA correspondence of pipes/ports are converter/pin nodes.
- */
-#define MAX_HDMI_CVTS 3
-#define MAX_HDMI_PINS 3
-
-struct hdmi_spec {
- int num_cvts;
- int num_pins;
- hda_nid_t cvt[MAX_HDMI_CVTS+1]; /* audio sources */
- hda_nid_t pin[MAX_HDMI_PINS+1]; /* audio sinks */
-
- /*
- * source connection for each pin
- */
- hda_nid_t pin_cvt[MAX_HDMI_PINS+1];
-
- /*
- * HDMI sink attached to each pin
- */
- struct hdmi_eld sink_eld[MAX_HDMI_PINS];
-
- /*
- * export one pcm per pipe
- */
- struct hda_pcm pcm_rec[MAX_HDMI_CVTS];
- struct hda_pcm_stream codec_pcm_pars[MAX_HDMI_CVTS];
-
- /*
- * ati/nvhdmi specific
- */
- struct hda_multi_out multiout;
- struct hda_pcm_stream *pcm_playback;
-
- /* misc flags */
- /* PD bit indicates only the update, not the current state */
- unsigned int old_pin_detect:1;
-};
-
-
-struct hdmi_audio_infoframe {
- u8 type; /* 0x84 */
- u8 ver; /* 0x01 */
- u8 len; /* 0x0a */
-
- u8 checksum;
-
- u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */
- u8 SS01_SF24;
- u8 CXT04;
- u8 CA;
- u8 LFEPBL01_LSV36_DM_INH7;
-};
-
-struct dp_audio_infoframe {
- u8 type; /* 0x84 */
- u8 len; /* 0x1b */
- u8 ver; /* 0x11 << 2 */
-
- u8 CC02_CT47; /* match with HDMI infoframe from this on */
- u8 SS01_SF24;
- u8 CXT04;
- u8 CA;
- u8 LFEPBL01_LSV36_DM_INH7;
-};
-
-/*
- * CEA speaker placement:
- *
- * FLH FCH FRH
- * FLW FL FLC FC FRC FR FRW
- *
- * LFE
- * TC
- *
- * RL RLC RC RRC RR
- *
- * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
- * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
- */
-enum cea_speaker_placement {
- FL = (1 << 0), /* Front Left */
- FC = (1 << 1), /* Front Center */
- FR = (1 << 2), /* Front Right */
- FLC = (1 << 3), /* Front Left Center */
- FRC = (1 << 4), /* Front Right Center */
- RL = (1 << 5), /* Rear Left */
- RC = (1 << 6), /* Rear Center */
- RR = (1 << 7), /* Rear Right */
- RLC = (1 << 8), /* Rear Left Center */
- RRC = (1 << 9), /* Rear Right Center */
- LFE = (1 << 10), /* Low Frequency Effect */
- FLW = (1 << 11), /* Front Left Wide */
- FRW = (1 << 12), /* Front Right Wide */
- FLH = (1 << 13), /* Front Left High */
- FCH = (1 << 14), /* Front Center High */
- FRH = (1 << 15), /* Front Right High */
- TC = (1 << 16), /* Top Center */
-};
-
-/*
- * ELD SA bits in the CEA Speaker Allocation data block
- */
-static int eld_speaker_allocation_bits[] = {
- [0] = FL | FR,
- [1] = LFE,
- [2] = FC,
- [3] = RL | RR,
- [4] = RC,
- [5] = FLC | FRC,
- [6] = RLC | RRC,
- /* the following are not defined in ELD yet */
- [7] = FLW | FRW,
- [8] = FLH | FRH,
- [9] = TC,
- [10] = FCH,
-};
-
-struct cea_channel_speaker_allocation {
- int ca_index;
- int speakers[8];
-
- /* derived values, just for convenience */
- int channels;
- int spk_mask;
-};
-
-/*
- * ALSA sequence is:
- *
- * surround40 surround41 surround50 surround51 surround71
- * ch0 front left = = = =
- * ch1 front right = = = =
- * ch2 rear left = = = =
- * ch3 rear right = = = =
- * ch4 LFE center center center
- * ch5 LFE LFE
- * ch6 side left
- * ch7 side right
- *
- * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
- */
-static int hdmi_channel_mapping[0x32][8] = {
- /* stereo */
- [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
- /* 2.1 */
- [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
- /* Dolby Surround */
- [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
- /* surround40 */
- [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
- /* 4ch */
- [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
- /* surround41 */
- [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
- /* surround50 */
- [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
- /* surround51 */
- [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
- /* 7.1 */
- [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
-};
-
-/*
- * This is an ordered list!
- *
- * The preceding ones have better chances to be selected by
- * hdmi_channel_allocation().
- */
-static struct cea_channel_speaker_allocation channel_allocations[] = {
-/* channel: 7 6 5 4 3 2 1 0 */
-{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } },
- /* 2.1 */
-{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } },
- /* Dolby Surround */
-{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } },
- /* surround40 */
-{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } },
- /* surround41 */
-{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } },
- /* surround50 */
-{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } },
- /* surround51 */
-{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } },
- /* 6.1 */
-{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } },
- /* surround71 */
-{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } },
-
-{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } },
-{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } },
-{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } },
-{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } },
-{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } },
-{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } },
-{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } },
-{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } },
-{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } },
-{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } },
-{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } },
-{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } },
-{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } },
-{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } },
-{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } },
-{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } },
-{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } },
-{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } },
-{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } },
-{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } },
-{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } },
-{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } },
-{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } },
-{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } },
-};
-
-
-/*
- * HDMI routines
- */
-
-static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
-{
- int i;
-
- for (i = 0; nids[i]; i++)
- if (nids[i] == nid)
- return i;
-
- snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid);
- return -EINVAL;
-}
-
-static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid,
- struct hdmi_eld *eld)
-{
- if (!snd_hdmi_get_eld(eld, codec, pin_nid))
- snd_hdmi_show_eld(eld);
-}
-
-#ifdef BE_PARANOID
-static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
- int *packet_index, int *byte_index)
-{
- int val;
-
- val = snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_HDMI_DIP_INDEX, 0);
-
- *packet_index = val >> 5;
- *byte_index = val & 0x1f;
-}
-#endif
-
-static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
- int packet_index, int byte_index)
-{
- int val;
-
- val = (packet_index << 5) | (byte_index & 0x1f);
-
- snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
-}
-
-static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
- unsigned char val)
-{
- snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
-}
-
-static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid)
-{
- /* Unmute */
- if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
- /* Enable pin out */
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-}
-
-static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid)
-{
- return 1 + snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CVT_CHAN_COUNT, 0);
-}
-
-static void hdmi_set_channel_count(struct hda_codec *codec,
- hda_nid_t nid, int chs)
-{
- if (chs != hdmi_get_channel_count(codec, nid))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
-}
-
-
-/*
- * Channel mapping routines
- */
-
-/*
- * Compute derived values in channel_allocations[].
- */
-static void init_channel_allocations(void)
-{
- int i, j;
- struct cea_channel_speaker_allocation *p;
-
- for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
- p = channel_allocations + i;
- p->channels = 0;
- p->spk_mask = 0;
- for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
- if (p->speakers[j]) {
- p->channels++;
- p->spk_mask |= p->speakers[j];
- }
- }
-}
-
-/*
- * The transformation takes two steps:
- *
- * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
- * spk_mask => (channel_allocations[]) => ai->CA
- *
- * TODO: it could select the wrong CA from multiple candidates.
-*/
-static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
- int channels)
-{
- struct hdmi_spec *spec = codec->spec;
- struct hdmi_eld *eld;
- int i;
- int ca = 0;
- int spk_mask = 0;
- char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
-
- /*
- * CA defaults to 0 for basic stereo audio
- */
- if (channels <= 2)
- return 0;
-
- i = hda_node_index(spec->pin_cvt, nid);
- if (i < 0)
- return 0;
- eld = &spec->sink_eld[i];
-
- /*
- * HDMI sink's ELD info cannot always be retrieved for now, e.g.
- * in console or for audio devices. Assume the highest speakers
- * configuration, to _not_ prohibit multi-channel audio playback.
- */
- if (!eld->spk_alloc)
- eld->spk_alloc = 0xffff;
-
- /*
- * expand ELD's speaker allocation mask
- *
- * ELD tells the speaker mask in a compact(paired) form,
- * expand ELD's notions to match the ones used by Audio InfoFrame.
- */
- for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
- if (eld->spk_alloc & (1 << i))
- spk_mask |= eld_speaker_allocation_bits[i];
- }
-
- /* search for the first working match in the CA table */
- for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
- if (channels == channel_allocations[i].channels &&
- (spk_mask & channel_allocations[i].spk_mask) ==
- channel_allocations[i].spk_mask) {
- ca = channel_allocations[i].ca_index;
- break;
- }
- }
-
- snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
- snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n",
- ca, channels, buf);
-
- return ca;
-}
-
-static void hdmi_debug_channel_mapping(struct hda_codec *codec,
- hda_nid_t pin_nid)
-{
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- int i;
- int slot;
-
- for (i = 0; i < 8; i++) {
- slot = snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_HDMI_CHAN_SLOT, i);
- printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
- slot >> 4, slot & 0xf);
- }
-#endif
-}
-
-
-static void hdmi_setup_channel_mapping(struct hda_codec *codec,
- hda_nid_t pin_nid,
- int ca)
-{
- int i;
- int err;
-
- if (hdmi_channel_mapping[ca][1] == 0) {
- for (i = 0; i < channel_allocations[ca].channels; i++)
- hdmi_channel_mapping[ca][i] = i | (i << 4);
- for (; i < 8; i++)
- hdmi_channel_mapping[ca][i] = 0xf | (i << 4);
- }
-
- for (i = 0; i < 8; i++) {
- err = snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_HDMI_CHAN_SLOT,
- hdmi_channel_mapping[ca][i]);
- if (err) {
- snd_printdd(KERN_NOTICE
- "HDMI: channel mapping failed\n");
- break;
- }
- }
-
- hdmi_debug_channel_mapping(codec, pin_nid);
-}
-
-
-/*
- * Audio InfoFrame routines
- */
-
-/*
- * Enable Audio InfoFrame Transmission
- */
-static void hdmi_start_infoframe_trans(struct hda_codec *codec,
- hda_nid_t pin_nid)
-{
- hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
- snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
- AC_DIPXMIT_BEST);
-}
-
-/*
- * Disable Audio InfoFrame Transmission
- */
-static void hdmi_stop_infoframe_trans(struct hda_codec *codec,
- hda_nid_t pin_nid)
-{
- hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
- snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
- AC_DIPXMIT_DISABLE);
-}
-
-static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid)
-{
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- int i;
- int size;
-
- size = snd_hdmi_get_eld_size(codec, pin_nid);
- printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size);
-
- for (i = 0; i < 8; i++) {
- size = snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_HDMI_DIP_SIZE, i);
- printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size);
- }
-#endif
-}
-
-static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
-{
-#ifdef BE_PARANOID
- int i, j;
- int size;
- int pi, bi;
- for (i = 0; i < 8; i++) {
- size = snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_HDMI_DIP_SIZE, i);
- if (size == 0)
- continue;
-
- hdmi_set_dip_index(codec, pin_nid, i, 0x0);
- for (j = 1; j < 1000; j++) {
- hdmi_write_dip_byte(codec, pin_nid, 0x0);
- hdmi_get_dip_index(codec, pin_nid, &pi, &bi);
- if (pi != i)
- snd_printd(KERN_INFO "dip index %d: %d != %d\n",
- bi, pi, i);
- if (bi == 0) /* byte index wrapped around */
- break;
- }
- snd_printd(KERN_INFO
- "HDMI: DIP GP[%d] buf reported size=%d, written=%d\n",
- i, size, j);
- }
-#endif
-}
-
-static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *hdmi_ai)
-{
- u8 *bytes = (u8 *)hdmi_ai;
- u8 sum = 0;
- int i;
-
- hdmi_ai->checksum = 0;
-
- for (i = 0; i < sizeof(*hdmi_ai); i++)
- sum += bytes[i];
-
- hdmi_ai->checksum = -sum;
-}
-
-static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
- hda_nid_t pin_nid,
- u8 *dip, int size)
-{
- int i;
-
- hdmi_debug_dip_size(codec, pin_nid);
- hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
-
- hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
- for (i = 0; i < size; i++)
- hdmi_write_dip_byte(codec, pin_nid, dip[i]);
-}
-
-static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
- u8 *dip, int size)
-{
- u8 val;
- int i;
-
- if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0)
- != AC_DIPXMIT_BEST)
- return false;
-
- hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
- for (i = 0; i < size; i++) {
- val = snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_HDMI_DIP_DATA, 0);
- if (val != dip[i])
- return false;
- }
-
- return true;
-}
-
-static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
- struct snd_pcm_substream *substream)
-{
- struct hdmi_spec *spec = codec->spec;
- hda_nid_t pin_nid;
- int channels = substream->runtime->channels;
- int ca;
- int i;
- u8 ai[max(sizeof(struct hdmi_audio_infoframe),
- sizeof(struct dp_audio_infoframe))];
-
- ca = hdmi_channel_allocation(codec, nid, channels);
-
- for (i = 0; i < spec->num_pins; i++) {
- if (spec->pin_cvt[i] != nid)
- continue;
- if (!spec->sink_eld[i].monitor_present)
- continue;
-
- pin_nid = spec->pin[i];
-
- memset(ai, 0, sizeof(ai));
- if (spec->sink_eld[i].conn_type == 0) { /* HDMI */
- struct hdmi_audio_infoframe *hdmi_ai;
-
- hdmi_ai = (struct hdmi_audio_infoframe *)ai;
- hdmi_ai->type = 0x84;
- hdmi_ai->ver = 0x01;
- hdmi_ai->len = 0x0a;
- hdmi_ai->CC02_CT47 = channels - 1;
- hdmi_checksum_audio_infoframe(hdmi_ai);
- } else if (spec->sink_eld[i].conn_type == 1) { /* DisplayPort */
- struct dp_audio_infoframe *dp_ai;
-
- dp_ai = (struct dp_audio_infoframe *)ai;
- dp_ai->type = 0x84;
- dp_ai->len = 0x1b;
- dp_ai->ver = 0x11 << 2;
- dp_ai->CC02_CT47 = channels - 1;
- } else {
- snd_printd("HDMI: unknown connection type at pin %d\n",
- pin_nid);
- continue;
- }
-
- /*
- * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
- * sizeof(*dp_ai) to avoid partial match/update problems when
- * the user switches between HDMI/DP monitors.
- */
- if (!hdmi_infoframe_uptodate(codec, pin_nid, ai, sizeof(ai))) {
- snd_printdd("hdmi_setup_audio_infoframe: "
- "cvt=%d pin=%d channels=%d\n",
- nid, pin_nid,
- channels);
- hdmi_setup_channel_mapping(codec, pin_nid, ca);
- hdmi_stop_infoframe_trans(codec, pin_nid);
- hdmi_fill_audio_infoframe(codec, pin_nid,
- ai, sizeof(ai));
- hdmi_start_infoframe_trans(codec, pin_nid);
- }
- }
-}
-
-
-/*
- * Unsolicited events
- */
-
-static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
- struct hdmi_eld *eld);
-
-static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
-{
- struct hdmi_spec *spec = codec->spec;
- int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
- int pind = !!(res & AC_UNSOL_RES_PD);
- int eldv = !!(res & AC_UNSOL_RES_ELDV);
- int index;
-
- printk(KERN_INFO
- "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
- tag, pind, eldv);
-
- index = hda_node_index(spec->pin, tag);
- if (index < 0)
- return;
-
- if (spec->old_pin_detect) {
- if (pind)
- hdmi_present_sense(codec, tag, &spec->sink_eld[index]);
- pind = spec->sink_eld[index].monitor_present;
- }
-
- spec->sink_eld[index].monitor_present = pind;
- spec->sink_eld[index].eld_valid = eldv;
-
- if (pind && eldv) {
- hdmi_get_show_eld(codec, spec->pin[index],
- &spec->sink_eld[index]);
- /* TODO: do real things about ELD */
- }
-}
-
-static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
-{
- int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
- int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
- int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
- int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
-
- printk(KERN_INFO
- "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
- tag,
- subtag,
- cp_state,
- cp_ready);
-
- /* TODO */
- if (cp_state)
- ;
- if (cp_ready)
- ;
-}
-
-
-static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- struct hdmi_spec *spec = codec->spec;
- int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
- int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
-
- if (hda_node_index(spec->pin, tag) < 0) {
- snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
- return;
- }
-
- if (subtag == 0)
- hdmi_intrinsic_event(codec, res);
- else
- hdmi_non_intrinsic_event(codec, res);
-}
-
-/*
- * Callbacks
- */
-
-/* HBR should be Non-PCM, 8 channels */
-#define is_hbr_format(format) \
- ((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7)
-
-static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
- u32 stream_tag, int format)
-{
- struct hdmi_spec *spec = codec->spec;
- int pinctl;
- int new_pinctl = 0;
- int i;
-
- for (i = 0; i < spec->num_pins; i++) {
- if (spec->pin_cvt[i] != nid)
- continue;
- if (!(snd_hda_query_pin_caps(codec, spec->pin[i]) & AC_PINCAP_HBR))
- continue;
-
- pinctl = snd_hda_codec_read(codec, spec->pin[i], 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-
- new_pinctl = pinctl & ~AC_PINCTL_EPT;
- if (is_hbr_format(format))
- new_pinctl |= AC_PINCTL_EPT_HBR;
- else
- new_pinctl |= AC_PINCTL_EPT_NATIVE;
-
- snd_printdd("hdmi_setup_stream: "
- "NID=0x%x, %spinctl=0x%x\n",
- spec->pin[i],
- pinctl == new_pinctl ? "" : "new-",
- new_pinctl);
-
- if (pinctl != new_pinctl)
- snd_hda_codec_write(codec, spec->pin[i], 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- new_pinctl);
- }
-
- if (is_hbr_format(format) && !new_pinctl) {
- snd_printdd("hdmi_setup_stream: HBR is not supported\n");
- return -EINVAL;
- }
-
- snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
- return 0;
-}
-
-/*
- * HDA PCM callbacks
- */
-static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hdmi_spec *spec = codec->spec;
- struct hdmi_eld *eld;
- struct hda_pcm_stream *codec_pars;
- unsigned int idx;
-
- for (idx = 0; idx < spec->num_cvts; idx++)
- if (hinfo->nid == spec->cvt[idx])
- break;
- if (snd_BUG_ON(idx >= spec->num_cvts) ||
- snd_BUG_ON(idx >= spec->num_pins))
- return -EINVAL;
-
- /* save the PCM info the codec provides */
- codec_pars = &spec->codec_pcm_pars[idx];
- if (!codec_pars->rates)
- *codec_pars = *hinfo;
-
- eld = &spec->sink_eld[idx];
- if (eld->sad_count > 0) {
- hdmi_eld_update_pcm_info(eld, hinfo, codec_pars);
- if (hinfo->channels_min > hinfo->channels_max ||
- !hinfo->rates || !hinfo->formats)
- return -ENODEV;
- } else {
- /* fallback to the codec default */
- hinfo->channels_min = codec_pars->channels_min;
- hinfo->channels_max = codec_pars->channels_max;
- hinfo->rates = codec_pars->rates;
- hinfo->formats = codec_pars->formats;
- hinfo->maxbps = codec_pars->maxbps;
- }
- return 0;
-}
-
-/*
- * HDA/HDMI auto parsing
- */
-static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
-{
- struct hdmi_spec *spec = codec->spec;
- hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
- int conn_len, curr;
- int index;
-
- if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
- snd_printk(KERN_WARNING
- "HDMI: pin %d wcaps %#x "
- "does not support connection list\n",
- pin_nid, get_wcaps(codec, pin_nid));
- return -EINVAL;
- }
-
- conn_len = snd_hda_get_connections(codec, pin_nid, conn_list,
- HDA_MAX_CONNECTIONS);
- if (conn_len > 1)
- curr = snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_CONNECT_SEL, 0);
- else
- curr = 0;
-
- index = hda_node_index(spec->pin, pin_nid);
- if (index < 0)
- return -EINVAL;
-
- spec->pin_cvt[index] = conn_list[curr];
-
- return 0;
-}
-
-static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
- struct hdmi_eld *eld)
-{
- int present = snd_hda_pin_sense(codec, pin_nid);
-
- eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
- eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
-
- if (present & AC_PINSENSE_ELDV)
- hdmi_get_show_eld(codec, pin_nid, eld);
-}
-
-static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
-{
- struct hdmi_spec *spec = codec->spec;
-
- if (spec->num_pins >= MAX_HDMI_PINS) {
- snd_printk(KERN_WARNING
- "HDMI: no space for pin %d\n", pin_nid);
- return -E2BIG;
- }
-
- hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
-
- spec->pin[spec->num_pins] = pin_nid;
- spec->num_pins++;
-
- /*
- * It is assumed that converter nodes come first in the node list and
- * hence have been registered and usable now.
- */
- return hdmi_read_pin_conn(codec, pin_nid);
-}
-
-static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
-{
- struct hdmi_spec *spec = codec->spec;
-
- if (spec->num_cvts >= MAX_HDMI_CVTS) {
- snd_printk(KERN_WARNING
- "HDMI: no space for converter %d\n", nid);
- return -E2BIG;
- }
-
- spec->cvt[spec->num_cvts] = nid;
- spec->num_cvts++;
-
- return 0;
-}
-
-static int hdmi_parse_codec(struct hda_codec *codec)
-{
- hda_nid_t nid;
- int i, nodes;
-
- nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
- if (!nid || nodes < 0) {
- snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n");
- return -EINVAL;
- }
-
- for (i = 0; i < nodes; i++, nid++) {
- unsigned int caps;
- unsigned int type;
-
- caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
- type = get_wcaps_type(caps);
-
- if (!(caps & AC_WCAP_DIGITAL))
- continue;
-
- switch (type) {
- case AC_WID_AUD_OUT:
- hdmi_add_cvt(codec, nid);
- break;
- case AC_WID_PIN:
- caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
- if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
- continue;
- hdmi_add_pin(codec, nid);
- break;
- }
- }
-
- /*
- * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event
- * can be lost and presence sense verb will become inaccurate if the
- * HDA link is powered off at hot plug or hw initialization time.
- */
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) &
- AC_PWRST_EPSS))
- codec->bus->power_keep_link_on = 1;
-#endif
-
- return 0;
-}
-
-/*
- */
-static char *generic_hdmi_pcm_names[MAX_HDMI_CVTS] = {
- "HDMI 0",
- "HDMI 1",
- "HDMI 2",
-};
-
-/*
- * HDMI callbacks
- */
-
-static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- hdmi_set_channel_count(codec, hinfo->nid,
- substream->runtime->channels);
-
- hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
-
- return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
-}
-
-static struct hda_pcm_stream generic_hdmi_pcm_playback = {
- .substreams = 1,
- .channels_min = 2,
- .ops = {
- .open = hdmi_pcm_open,
- .prepare = generic_hdmi_playback_pcm_prepare,
- },
-};
-
-static int generic_hdmi_build_pcms(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
- int i;
-
- codec->num_pcms = spec->num_cvts;
- codec->pcm_info = info;
-
- for (i = 0; i < codec->num_pcms; i++, info++) {
- unsigned int chans;
- struct hda_pcm_stream *pstr;
-
- chans = get_wcaps(codec, spec->cvt[i]);
- chans = get_wcaps_channels(chans);
-
- info->name = generic_hdmi_pcm_names[i];
- info->pcm_type = HDA_PCM_TYPE_HDMI;
- pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
- if (spec->pcm_playback)
- *pstr = *spec->pcm_playback;
- else
- *pstr = generic_hdmi_pcm_playback;
- pstr->nid = spec->cvt[i];
- if (pstr->channels_max <= 2 && chans && chans <= 16)
- pstr->channels_max = chans;
- }
-
- return 0;
-}
-
-static int generic_hdmi_build_controls(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- int err;
- int i;
-
- for (i = 0; i < codec->num_pcms; i++) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static int generic_hdmi_init(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- int i;
-
- for (i = 0; spec->pin[i]; i++) {
- hdmi_enable_output(codec, spec->pin[i]);
- snd_hda_codec_write(codec, spec->pin[i], 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | spec->pin[i]);
- }
- return 0;
-}
-
-static void generic_hdmi_free(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_pins; i++)
- snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
-
- kfree(spec);
-}
-
-static struct hda_codec_ops generic_hdmi_patch_ops = {
- .init = generic_hdmi_init,
- .free = generic_hdmi_free,
- .build_pcms = generic_hdmi_build_pcms,
- .build_controls = generic_hdmi_build_controls,
- .unsol_event = hdmi_unsol_event,
-};
-
-static int patch_generic_hdmi(struct hda_codec *codec)
-{
- struct hdmi_spec *spec;
- int i;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
- if (hdmi_parse_codec(codec) < 0) {
- codec->spec = NULL;
- kfree(spec);
- return -EINVAL;
- }
- codec->patch_ops = generic_hdmi_patch_ops;
-
- for (i = 0; i < spec->num_pins; i++)
- snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
-
- init_channel_allocations();
-
- return 0;
-}
-
-/*
- * Nvidia specific implementations
- */
-
-#define Nv_VERB_SET_Channel_Allocation 0xF79
-#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A
-#define Nv_VERB_SET_Audio_Protection_On 0xF98
-#define Nv_VERB_SET_Audio_Protection_Off 0xF99
-
-#define nvhdmi_master_con_nid_7x 0x04
-#define nvhdmi_master_pin_nid_7x 0x05
-
-static hda_nid_t nvhdmi_con_nids_7x[4] = {
- /*front, rear, clfe, rear_surr */
- 0x6, 0x8, 0xa, 0xc,
-};
-
-static struct hda_verb nvhdmi_basic_init_7x[] = {
- /* set audio protect on */
- { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
- /* enable digital output on pin widget */
- { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- {} /* terminator */
-};
-
-#ifdef LIMITED_RATE_FMT_SUPPORT
-/* support only the safe format and rate */
-#define SUPPORTED_RATES SNDRV_PCM_RATE_48000
-#define SUPPORTED_MAXBPS 16
-#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE
-#else
-/* support all rates and formats */
-#define SUPPORTED_RATES \
- (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
- SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
- SNDRV_PCM_RATE_192000)
-#define SUPPORTED_MAXBPS 24
-#define SUPPORTED_FORMATS \
- (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
-#endif
-
-static int nvhdmi_7x_init(struct hda_codec *codec)
-{
- snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
- return 0;
-}
-
-static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hdmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int simple_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hdmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct hdmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hdmi_spec *spec = codec->spec;
- int i;
-
- snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
- 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
- for (i = 0; i < 4; i++) {
- /* set the stream id */
- snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
- AC_VERB_SET_CHANNEL_STREAMID, 0);
- /* set the stream format */
- snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
- AC_VERB_SET_STREAM_FORMAT, 0);
- }
-
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- int chs;
- unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
- int i;
-
- mutex_lock(&codec->spdif_mutex);
-
- chs = substream->runtime->channels;
- chan = chs ? (chs - 1) : 1;
-
- switch (chs) {
- default:
- case 0:
- case 2:
- chanmask = 0x00;
- break;
- case 4:
- chanmask = 0x08;
- break;
- case 6:
- chanmask = 0x0b;
- break;
- case 8:
- chanmask = 0x13;
- break;
- }
- dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
- dataDCC2 = 0x2;
-
- /* set the Audio InforFrame Channel Allocation */
- snd_hda_codec_write(codec, 0x1, 0,
- Nv_VERB_SET_Channel_Allocation, chanmask);
-
- /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
- if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
- snd_hda_codec_write(codec,
- nvhdmi_master_con_nid_7x,
- 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
-
- /* set the stream id */
- snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
- AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
-
- /* set the stream format */
- snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
- AC_VERB_SET_STREAM_FORMAT, format);
-
- /* turn on again (if needed) */
- /* enable and set the channel status audio/data flag */
- if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
- snd_hda_codec_write(codec,
- nvhdmi_master_con_nid_7x,
- 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & 0xff);
- snd_hda_codec_write(codec,
- nvhdmi_master_con_nid_7x,
- 0,
- AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
- }
-
- for (i = 0; i < 4; i++) {
- if (chs == 2)
- channel_id = 0;
- else
- channel_id = i * 2;
-
- /* turn off SPDIF once;
- *otherwise the IEC958 bits won't be updated
- */
- if (codec->spdif_status_reset &&
- (codec->spdif_ctls & AC_DIG1_ENABLE))
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
- /* set the stream id */
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_CHANNEL_STREAMID,
- (stream_tag << 4) | channel_id);
- /* set the stream format */
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_STREAM_FORMAT,
- format);
- /* turn on again (if needed) */
- /* enable and set the channel status audio/data flag */
- if (codec->spdif_status_reset &&
- (codec->spdif_ctls & AC_DIG1_ENABLE)) {
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & 0xff);
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
- }
- }
-
- /* set the Audio Info Frame Checksum */
- snd_hda_codec_write(codec, 0x1, 0,
- Nv_VERB_SET_Info_Frame_Checksum,
- (0x71 - chan - chanmask));
-
- mutex_unlock(&codec->spdif_mutex);
- return 0;
-}
-
-static struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- .nid = nvhdmi_master_con_nid_7x,
- .rates = SUPPORTED_RATES,
- .maxbps = SUPPORTED_MAXBPS,
- .formats = SUPPORTED_FORMATS,
- .ops = {
- .open = simple_playback_pcm_open,
- .close = nvhdmi_8ch_7x_pcm_close,
- .prepare = nvhdmi_8ch_7x_pcm_prepare
- },
-};
-
-static struct hda_pcm_stream nvhdmi_pcm_playback_2ch = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = nvhdmi_master_con_nid_7x,
- .rates = SUPPORTED_RATES,
- .maxbps = SUPPORTED_MAXBPS,
- .formats = SUPPORTED_FORMATS,
- .ops = {
- .open = simple_playback_pcm_open,
- .close = simple_playback_pcm_close,
- .prepare = simple_playback_pcm_prepare
- },
-};
-
-static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
- .build_controls = generic_hdmi_build_controls,
- .build_pcms = generic_hdmi_build_pcms,
- .init = nvhdmi_7x_init,
- .free = generic_hdmi_free,
-};
-
-static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
- .build_controls = generic_hdmi_build_controls,
- .build_pcms = generic_hdmi_build_pcms,
- .init = nvhdmi_7x_init,
- .free = generic_hdmi_free,
-};
-
-static int patch_nvhdmi_8ch_89(struct hda_codec *codec)
-{
- struct hdmi_spec *spec;
- int err = patch_generic_hdmi(codec);
-
- if (err < 0)
- return err;
- spec = codec->spec;
- spec->old_pin_detect = 1;
- return 0;
-}
-
-static int patch_nvhdmi_2ch(struct hda_codec *codec)
-{
- struct hdmi_spec *spec;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- spec->multiout.num_dacs = 0; /* no analog */
- spec->multiout.max_channels = 2;
- spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
- spec->old_pin_detect = 1;
- spec->num_cvts = 1;
- spec->cvt[0] = nvhdmi_master_con_nid_7x;
- spec->pcm_playback = &nvhdmi_pcm_playback_2ch;
-
- codec->patch_ops = nvhdmi_patch_ops_2ch;
-
- return 0;
-}
-
-static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
-{
- struct hdmi_spec *spec;
- int err = patch_nvhdmi_2ch(codec);
-
- if (err < 0)
- return err;
- spec = codec->spec;
- spec->multiout.max_channels = 8;
- spec->pcm_playback = &nvhdmi_pcm_playback_8ch_7x;
- codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
- return 0;
-}
-
-/*
- * ATI-specific implementations
- *
- * FIXME: we may omit the whole this and use the generic code once after
- * it's confirmed to work.
- */
-
-#define ATIHDMI_CVT_NID 0x02 /* audio converter */
-#define ATIHDMI_PIN_NID 0x03 /* HDMI output pin */
-
-static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct hdmi_spec *spec = codec->spec;
- int chans = substream->runtime->channels;
- int i, err;
-
- err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format,
- substream);
- if (err < 0)
- return err;
- snd_hda_codec_write(codec, spec->cvt[0], 0, AC_VERB_SET_CVT_CHAN_COUNT,
- chans - 1);
- /* FIXME: XXX */
- for (i = 0; i < chans; i++) {
- snd_hda_codec_write(codec, spec->cvt[0], 0,
- AC_VERB_SET_HDMI_CHAN_SLOT,
- (i << 4) | i);
- }
- return 0;
-}
-
-static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = ATIHDMI_CVT_NID,
- .ops = {
- .open = simple_playback_pcm_open,
- .close = simple_playback_pcm_close,
- .prepare = atihdmi_playback_pcm_prepare
- },
-};
-
-static struct hda_verb atihdmi_basic_init[] = {
- /* enable digital output on pin widget */
- { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- {} /* terminator */
-};
-
-static int atihdmi_init(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
-
- snd_hda_sequence_write(codec, atihdmi_basic_init);
- /* SI codec requires to unmute the pin */
- if (get_wcaps(codec, spec->pin[0]) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, spec->pin[0], 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
- return 0;
-}
-
-static struct hda_codec_ops atihdmi_patch_ops = {
- .build_controls = generic_hdmi_build_controls,
- .build_pcms = generic_hdmi_build_pcms,
- .init = atihdmi_init,
- .free = generic_hdmi_free,
-};
-
-
-static int patch_atihdmi(struct hda_codec *codec)
-{
- struct hdmi_spec *spec;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- spec->multiout.num_dacs = 0; /* no analog */
- spec->multiout.max_channels = 2;
- spec->multiout.dig_out_nid = ATIHDMI_CVT_NID;
- spec->num_cvts = 1;
- spec->cvt[0] = ATIHDMI_CVT_NID;
- spec->pin[0] = ATIHDMI_PIN_NID;
- spec->pcm_playback = &atihdmi_pcm_digital_playback;
-
- codec->patch_ops = atihdmi_patch_ops;
-
- return 0;
-}
-
-
-/*
- * patch entries
- */
-static struct hda_codec_preset snd_hda_preset_hdmi[] = {
-{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
-{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
-{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
-{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
-{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x10de0002, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0003, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
-{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
-{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
-{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi },
-{} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:1002793c");
-MODULE_ALIAS("snd-hda-codec-id:10027919");
-MODULE_ALIAS("snd-hda-codec-id:1002791a");
-MODULE_ALIAS("snd-hda-codec-id:1002aa01");
-MODULE_ALIAS("snd-hda-codec-id:10951390");
-MODULE_ALIAS("snd-hda-codec-id:10951392");
-MODULE_ALIAS("snd-hda-codec-id:10de0002");
-MODULE_ALIAS("snd-hda-codec-id:10de0003");
-MODULE_ALIAS("snd-hda-codec-id:10de0005");
-MODULE_ALIAS("snd-hda-codec-id:10de0006");
-MODULE_ALIAS("snd-hda-codec-id:10de0007");
-MODULE_ALIAS("snd-hda-codec-id:10de000a");
-MODULE_ALIAS("snd-hda-codec-id:10de000b");
-MODULE_ALIAS("snd-hda-codec-id:10de000c");
-MODULE_ALIAS("snd-hda-codec-id:10de000d");
-MODULE_ALIAS("snd-hda-codec-id:10de0010");
-MODULE_ALIAS("snd-hda-codec-id:10de0011");
-MODULE_ALIAS("snd-hda-codec-id:10de0012");
-MODULE_ALIAS("snd-hda-codec-id:10de0013");
-MODULE_ALIAS("snd-hda-codec-id:10de0014");
-MODULE_ALIAS("snd-hda-codec-id:10de0018");
-MODULE_ALIAS("snd-hda-codec-id:10de0019");
-MODULE_ALIAS("snd-hda-codec-id:10de001a");
-MODULE_ALIAS("snd-hda-codec-id:10de001b");
-MODULE_ALIAS("snd-hda-codec-id:10de001c");
-MODULE_ALIAS("snd-hda-codec-id:10de0040");
-MODULE_ALIAS("snd-hda-codec-id:10de0041");
-MODULE_ALIAS("snd-hda-codec-id:10de0042");
-MODULE_ALIAS("snd-hda-codec-id:10de0043");
-MODULE_ALIAS("snd-hda-codec-id:10de0044");
-MODULE_ALIAS("snd-hda-codec-id:10de0067");
-MODULE_ALIAS("snd-hda-codec-id:10de8001");
-MODULE_ALIAS("snd-hda-codec-id:17e80047");
-MODULE_ALIAS("snd-hda-codec-id:80860054");
-MODULE_ALIAS("snd-hda-codec-id:80862801");
-MODULE_ALIAS("snd-hda-codec-id:80862802");
-MODULE_ALIAS("snd-hda-codec-id:80862803");
-MODULE_ALIAS("snd-hda-codec-id:80862804");
-MODULE_ALIAS("snd-hda-codec-id:80862805");
-MODULE_ALIAS("snd-hda-codec-id:808629fb");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("HDMI HD-audio codec");
-MODULE_ALIAS("snd-hda-codec-intelhdmi");
-MODULE_ALIAS("snd-hda-codec-nvhdmi");
-MODULE_ALIAS("snd-hda-codec-atihdmi");
-
-static struct hda_codec_preset_list intel_list = {
- .preset = snd_hda_preset_hdmi,
- .owner = THIS_MODULE,
-};
-
-static int __init patch_hdmi_init(void)
-{
- return snd_hda_add_codec_preset(&intel_list);
-}
-
-static void __exit patch_hdmi_exit(void)
-{
- snd_hda_delete_codec_preset(&intel_list);
-}
-
-module_init(patch_hdmi_init)
-module_exit(patch_hdmi_exit)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
deleted file mode 100644
index 5f00589cb791..000000000000
--- a/sound/pci/hda/patch_realtek.c
+++ /dev/null
@@ -1,19942 +0,0 @@
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * HD audio interface patch for ALC 260/880/882 codecs
- *
- * Copyright (c) 2004 Kailang Yang <kailang@realtek.com.tw>
- * PeiSen Hou <pshou@realtek.com.tw>
- * Takashi Iwai <tiwai@suse.de>
- * Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
-#include <sound/core.h>
-#include <sound/jack.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-#include "hda_beep.h"
-
-#define ALC880_FRONT_EVENT 0x01
-#define ALC880_DCVOL_EVENT 0x02
-#define ALC880_HP_EVENT 0x04
-#define ALC880_MIC_EVENT 0x08
-
-/* ALC880 board config type */
-enum {
- ALC880_3ST,
- ALC880_3ST_DIG,
- ALC880_5ST,
- ALC880_5ST_DIG,
- ALC880_W810,
- ALC880_Z71V,
- ALC880_6ST,
- ALC880_6ST_DIG,
- ALC880_F1734,
- ALC880_ASUS,
- ALC880_ASUS_DIG,
- ALC880_ASUS_W1V,
- ALC880_ASUS_DIG2,
- ALC880_FUJITSU,
- ALC880_UNIWILL_DIG,
- ALC880_UNIWILL,
- ALC880_UNIWILL_P53,
- ALC880_CLEVO,
- ALC880_TCL_S700,
- ALC880_LG,
- ALC880_LG_LW,
- ALC880_MEDION_RIM,
-#ifdef CONFIG_SND_DEBUG
- ALC880_TEST,
-#endif
- ALC880_AUTO,
- ALC880_MODEL_LAST /* last tag */
-};
-
-/* ALC260 models */
-enum {
- ALC260_BASIC,
- ALC260_HP,
- ALC260_HP_DC7600,
- ALC260_HP_3013,
- ALC260_FUJITSU_S702X,
- ALC260_ACER,
- ALC260_WILL,
- ALC260_REPLACER_672V,
- ALC260_FAVORIT100,
-#ifdef CONFIG_SND_DEBUG
- ALC260_TEST,
-#endif
- ALC260_AUTO,
- ALC260_MODEL_LAST /* last tag */
-};
-
-/* ALC262 models */
-enum {
- ALC262_BASIC,
- ALC262_HIPPO,
- ALC262_HIPPO_1,
- ALC262_FUJITSU,
- ALC262_HP_BPC,
- ALC262_HP_BPC_D7000_WL,
- ALC262_HP_BPC_D7000_WF,
- ALC262_HP_TC_T5735,
- ALC262_HP_RP5700,
- ALC262_BENQ_ED8,
- ALC262_SONY_ASSAMD,
- ALC262_BENQ_T31,
- ALC262_ULTRA,
- ALC262_LENOVO_3000,
- ALC262_NEC,
- ALC262_TOSHIBA_S06,
- ALC262_TOSHIBA_RX1,
- ALC262_TYAN,
- ALC262_AUTO,
- ALC262_MODEL_LAST /* last tag */
-};
-
-/* ALC268 models */
-enum {
- ALC267_QUANTA_IL1,
- ALC268_3ST,
- ALC268_TOSHIBA,
- ALC268_ACER,
- ALC268_ACER_DMIC,
- ALC268_ACER_ASPIRE_ONE,
- ALC268_DELL,
- ALC268_ZEPTO,
-#ifdef CONFIG_SND_DEBUG
- ALC268_TEST,
-#endif
- ALC268_AUTO,
- ALC268_MODEL_LAST /* last tag */
-};
-
-/* ALC269 models */
-enum {
- ALC269_BASIC,
- ALC269_QUANTA_FL1,
- ALC269_AMIC,
- ALC269_DMIC,
- ALC269VB_AMIC,
- ALC269VB_DMIC,
- ALC269_FUJITSU,
- ALC269_LIFEBOOK,
- ALC271_ACER,
- ALC269_AUTO,
- ALC269_MODEL_LAST /* last tag */
-};
-
-/* ALC861 models */
-enum {
- ALC861_3ST,
- ALC660_3ST,
- ALC861_3ST_DIG,
- ALC861_6ST_DIG,
- ALC861_UNIWILL_M31,
- ALC861_TOSHIBA,
- ALC861_ASUS,
- ALC861_ASUS_LAPTOP,
- ALC861_AUTO,
- ALC861_MODEL_LAST,
-};
-
-/* ALC861-VD models */
-enum {
- ALC660VD_3ST,
- ALC660VD_3ST_DIG,
- ALC660VD_ASUS_V1S,
- ALC861VD_3ST,
- ALC861VD_3ST_DIG,
- ALC861VD_6ST_DIG,
- ALC861VD_LENOVO,
- ALC861VD_DALLAS,
- ALC861VD_HP,
- ALC861VD_AUTO,
- ALC861VD_MODEL_LAST,
-};
-
-/* ALC662 models */
-enum {
- ALC662_3ST_2ch_DIG,
- ALC662_3ST_6ch_DIG,
- ALC662_3ST_6ch,
- ALC662_5ST_DIG,
- ALC662_LENOVO_101E,
- ALC662_ASUS_EEEPC_P701,
- ALC662_ASUS_EEEPC_EP20,
- ALC663_ASUS_M51VA,
- ALC663_ASUS_G71V,
- ALC663_ASUS_H13,
- ALC663_ASUS_G50V,
- ALC662_ECS,
- ALC663_ASUS_MODE1,
- ALC662_ASUS_MODE2,
- ALC663_ASUS_MODE3,
- ALC663_ASUS_MODE4,
- ALC663_ASUS_MODE5,
- ALC663_ASUS_MODE6,
- ALC663_ASUS_MODE7,
- ALC663_ASUS_MODE8,
- ALC272_DELL,
- ALC272_DELL_ZM1,
- ALC272_SAMSUNG_NC10,
- ALC662_AUTO,
- ALC662_MODEL_LAST,
-};
-
-/* ALC882 models */
-enum {
- ALC882_3ST_DIG,
- ALC882_6ST_DIG,
- ALC882_ARIMA,
- ALC882_W2JC,
- ALC882_TARGA,
- ALC882_ASUS_A7J,
- ALC882_ASUS_A7M,
- ALC885_MACPRO,
- ALC885_MBA21,
- ALC885_MBP3,
- ALC885_MB5,
- ALC885_MACMINI3,
- ALC885_IMAC24,
- ALC885_IMAC91,
- ALC883_3ST_2ch_DIG,
- ALC883_3ST_6ch_DIG,
- ALC883_3ST_6ch,
- ALC883_6ST_DIG,
- ALC883_TARGA_DIG,
- ALC883_TARGA_2ch_DIG,
- ALC883_TARGA_8ch_DIG,
- ALC883_ACER,
- ALC883_ACER_ASPIRE,
- ALC888_ACER_ASPIRE_4930G,
- ALC888_ACER_ASPIRE_6530G,
- ALC888_ACER_ASPIRE_8930G,
- ALC888_ACER_ASPIRE_7730G,
- ALC883_MEDION,
- ALC883_MEDION_MD2,
- ALC883_MEDION_WIM2160,
- ALC883_LAPTOP_EAPD,
- ALC883_LENOVO_101E_2ch,
- ALC883_LENOVO_NB0763,
- ALC888_LENOVO_MS7195_DIG,
- ALC888_LENOVO_SKY,
- ALC883_HAIER_W66,
- ALC888_3ST_HP,
- ALC888_6ST_DELL,
- ALC883_MITAC,
- ALC883_CLEVO_M540R,
- ALC883_CLEVO_M720,
- ALC883_FUJITSU_PI2515,
- ALC888_FUJITSU_XA3530,
- ALC883_3ST_6ch_INTEL,
- ALC889A_INTEL,
- ALC889_INTEL,
- ALC888_ASUS_M90V,
- ALC888_ASUS_EEE1601,
- ALC889A_MB31,
- ALC1200_ASUS_P5Q,
- ALC883_SONY_VAIO_TT,
- ALC882_AUTO,
- ALC882_MODEL_LAST,
-};
-
-/* ALC680 models */
-enum {
- ALC680_BASE,
- ALC680_AUTO,
- ALC680_MODEL_LAST,
-};
-
-/* for GPIO Poll */
-#define GPIO_MASK 0x03
-
-/* extra amp-initialization sequence types */
-enum {
- ALC_INIT_NONE,
- ALC_INIT_DEFAULT,
- ALC_INIT_GPIO1,
- ALC_INIT_GPIO2,
- ALC_INIT_GPIO3,
-};
-
-struct alc_mic_route {
- hda_nid_t pin;
- unsigned char mux_idx;
- unsigned char amix_idx;
-};
-
-struct alc_jack {
- hda_nid_t nid;
- int type;
- struct snd_jack *jack;
-};
-
-#define MUX_IDX_UNDEF ((unsigned char)-1)
-
-struct alc_customize_define {
- unsigned int sku_cfg;
- unsigned char port_connectivity;
- unsigned char check_sum;
- unsigned char customization;
- unsigned char external_amp;
- unsigned int enable_pcbeep:1;
- unsigned int platform_type:1;
- unsigned int swap:1;
- unsigned int override:1;
- unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */
-};
-
-struct alc_spec {
- /* codec parameterization */
- struct snd_kcontrol_new *mixers[5]; /* mixer arrays */
- unsigned int num_mixers;
- struct snd_kcontrol_new *cap_mixer; /* capture mixer */
- unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
-
- const struct hda_verb *init_verbs[10]; /* initialization verbs
- * don't forget NULL
- * termination!
- */
- unsigned int num_init_verbs;
-
- char stream_name_analog[32]; /* analog PCM stream */
- struct hda_pcm_stream *stream_analog_playback;
- struct hda_pcm_stream *stream_analog_capture;
- struct hda_pcm_stream *stream_analog_alt_playback;
- struct hda_pcm_stream *stream_analog_alt_capture;
-
- char stream_name_digital[32]; /* digital PCM stream */
- struct hda_pcm_stream *stream_digital_playback;
- struct hda_pcm_stream *stream_digital_capture;
-
- /* playback */
- struct hda_multi_out multiout; /* playback set-up
- * max_channels, dacs must be set
- * dig_out_nid and hp_nid are optional
- */
- hda_nid_t alt_dac_nid;
- hda_nid_t slave_dig_outs[3]; /* optional - for auto-parsing */
- int dig_out_type;
-
- /* capture */
- unsigned int num_adc_nids;
- hda_nid_t *adc_nids;
- hda_nid_t *capsrc_nids;
- hda_nid_t dig_in_nid; /* digital-in NID; optional */
-
- /* capture setup for dynamic dual-adc switch */
- unsigned int cur_adc_idx;
- hda_nid_t cur_adc;
- unsigned int cur_adc_stream_tag;
- unsigned int cur_adc_format;
-
- /* capture source */
- unsigned int num_mux_defs;
- const struct hda_input_mux *input_mux;
- unsigned int cur_mux[3];
- struct alc_mic_route ext_mic;
- struct alc_mic_route int_mic;
-
- /* channel model */
- const struct hda_channel_mode *channel_mode;
- int num_channel_mode;
- int need_dac_fix;
- int const_channel_count;
- int ext_channel_count;
-
- /* PCM information */
- struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
-
- /* jack detection */
- struct snd_array jacks;
-
- /* dynamic controls, init_verbs and input_mux */
- struct auto_pin_cfg autocfg;
- struct alc_customize_define cdefine;
- struct snd_array kctls;
- struct hda_input_mux private_imux[3];
- hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
- hda_nid_t private_adc_nids[AUTO_CFG_MAX_OUTS];
- hda_nid_t private_capsrc_nids[AUTO_CFG_MAX_OUTS];
-
- /* hooks */
- void (*init_hook)(struct hda_codec *codec);
- void (*unsol_event)(struct hda_codec *codec, unsigned int res);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- void (*power_hook)(struct hda_codec *codec);
-#endif
-
- /* for pin sensing */
- unsigned int sense_updated: 1;
- unsigned int jack_present: 1;
- unsigned int master_sw: 1;
- unsigned int auto_mic:1;
-
- /* other flags */
- unsigned int no_analog :1; /* digital I/O only */
- unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */
- int init_amp;
- int codec_variant; /* flag for other variants */
-
- /* for virtual master */
- hda_nid_t vmaster_nid;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- struct hda_loopback_check loopback;
-#endif
-
- /* for PLL fix */
- hda_nid_t pll_nid;
- unsigned int pll_coef_idx, pll_coef_bit;
-};
-
-/*
- * configuration template - to be copied to the spec instance
- */
-struct alc_config_preset {
- struct snd_kcontrol_new *mixers[5]; /* should be identical size
- * with spec
- */
- struct snd_kcontrol_new *cap_mixer; /* capture mixer */
- const struct hda_verb *init_verbs[5];
- unsigned int num_dacs;
- hda_nid_t *dac_nids;
- hda_nid_t dig_out_nid; /* optional */
- hda_nid_t hp_nid; /* optional */
- hda_nid_t *slave_dig_outs;
- unsigned int num_adc_nids;
- hda_nid_t *adc_nids;
- hda_nid_t *capsrc_nids;
- hda_nid_t dig_in_nid;
- unsigned int num_channel_mode;
- const struct hda_channel_mode *channel_mode;
- int need_dac_fix;
- int const_channel_count;
- unsigned int num_mux_defs;
- const struct hda_input_mux *input_mux;
- void (*unsol_event)(struct hda_codec *, unsigned int);
- void (*setup)(struct hda_codec *);
- void (*init_hook)(struct hda_codec *);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- struct hda_amp_list *loopbacks;
- void (*power_hook)(struct hda_codec *codec);
-#endif
-};
-
-
-/*
- * input MUX handling
- */
-static int alc_mux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- unsigned int mux_idx = snd_ctl_get_ioffidx(kcontrol, &uinfo->id);
- if (mux_idx >= spec->num_mux_defs)
- mux_idx = 0;
- if (!spec->input_mux[mux_idx].num_items && mux_idx > 0)
- mux_idx = 0;
- return snd_hda_input_mux_info(&spec->input_mux[mux_idx], uinfo);
-}
-
-static int alc_mux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
- return 0;
-}
-
-static int alc_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- const struct hda_input_mux *imux;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- unsigned int mux_idx;
- hda_nid_t nid = spec->capsrc_nids ?
- spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx];
- unsigned int type;
-
- mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
- imux = &spec->input_mux[mux_idx];
- if (!imux->num_items && mux_idx > 0)
- imux = &spec->input_mux[0];
-
- type = get_wcaps_type(get_wcaps(codec, nid));
- if (type == AC_WID_AUD_MIX) {
- /* Matrix-mixer style (e.g. ALC882) */
- unsigned int *cur_val = &spec->cur_mux[adc_idx];
- unsigned int i, idx;
-
- idx = ucontrol->value.enumerated.item[0];
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
- if (*cur_val == idx)
- return 0;
- for (i = 0; i < imux->num_items; i++) {
- unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
- imux->items[i].index,
- HDA_AMP_MUTE, v);
- }
- *cur_val = idx;
- return 1;
- } else {
- /* MUX style (e.g. ALC880) */
- return snd_hda_input_mux_put(codec, imux, ucontrol, nid,
- &spec->cur_mux[adc_idx]);
- }
-}
-
-/*
- * channel mode setting
- */
-static int alc_ch_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- return snd_hda_ch_mode_info(codec, uinfo, spec->channel_mode,
- spec->num_channel_mode);
-}
-
-static int alc_ch_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
- spec->num_channel_mode,
- spec->ext_channel_count);
-}
-
-static int alc_ch_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
- spec->num_channel_mode,
- &spec->ext_channel_count);
- if (err >= 0 && !spec->const_channel_count) {
- spec->multiout.max_channels = spec->ext_channel_count;
- if (spec->need_dac_fix)
- spec->multiout.num_dacs = spec->multiout.max_channels / 2;
- }
- return err;
-}
-
-/*
- * Control the mode of pin widget settings via the mixer. "pc" is used
- * instead of "%" to avoid consequences of accidently treating the % as
- * being part of a format specifier. Maximum allowed length of a value is
- * 63 characters plus NULL terminator.
- *
- * Note: some retasking pin complexes seem to ignore requests for input
- * states other than HiZ (eg: PIN_VREFxx) and revert to HiZ if any of these
- * are requested. Therefore order this list so that this behaviour will not
- * cause problems when mixer clients move through the enum sequentially.
- * NIDs 0x0f and 0x10 have been observed to have this behaviour as of
- * March 2006.
- */
-static char *alc_pin_mode_names[] = {
- "Mic 50pc bias", "Mic 80pc bias",
- "Line in", "Line out", "Headphone out",
-};
-static unsigned char alc_pin_mode_values[] = {
- PIN_VREF50, PIN_VREF80, PIN_IN, PIN_OUT, PIN_HP,
-};
-/* The control can present all 5 options, or it can limit the options based
- * in the pin being assumed to be exclusively an input or an output pin. In
- * addition, "input" pins may or may not process the mic bias option
- * depending on actual widget capability (NIDs 0x0f and 0x10 don't seem to
- * accept requests for bias as of chip versions up to March 2006) and/or
- * wiring in the computer.
- */
-#define ALC_PIN_DIR_IN 0x00
-#define ALC_PIN_DIR_OUT 0x01
-#define ALC_PIN_DIR_INOUT 0x02
-#define ALC_PIN_DIR_IN_NOMICBIAS 0x03
-#define ALC_PIN_DIR_INOUT_NOMICBIAS 0x04
-
-/* Info about the pin modes supported by the different pin direction modes.
- * For each direction the minimum and maximum values are given.
- */
-static signed char alc_pin_mode_dir_info[5][2] = {
- { 0, 2 }, /* ALC_PIN_DIR_IN */
- { 3, 4 }, /* ALC_PIN_DIR_OUT */
- { 0, 4 }, /* ALC_PIN_DIR_INOUT */
- { 2, 2 }, /* ALC_PIN_DIR_IN_NOMICBIAS */
- { 2, 4 }, /* ALC_PIN_DIR_INOUT_NOMICBIAS */
-};
-#define alc_pin_mode_min(_dir) (alc_pin_mode_dir_info[_dir][0])
-#define alc_pin_mode_max(_dir) (alc_pin_mode_dir_info[_dir][1])
-#define alc_pin_mode_n_items(_dir) \
- (alc_pin_mode_max(_dir)-alc_pin_mode_min(_dir)+1)
-
-static int alc_pin_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- unsigned int item_num = uinfo->value.enumerated.item;
- unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = alc_pin_mode_n_items(dir);
-
- if (item_num<alc_pin_mode_min(dir) || item_num>alc_pin_mode_max(dir))
- item_num = alc_pin_mode_min(dir);
- strcpy(uinfo->value.enumerated.name, alc_pin_mode_names[item_num]);
- return 0;
-}
-
-static int alc_pin_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- unsigned int i;
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
- long *valp = ucontrol->value.integer.value;
- unsigned int pinctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL,
- 0x00);
-
- /* Find enumerated value for current pinctl setting */
- i = alc_pin_mode_min(dir);
- while (i <= alc_pin_mode_max(dir) && alc_pin_mode_values[i] != pinctl)
- i++;
- *valp = i <= alc_pin_mode_max(dir) ? i: alc_pin_mode_min(dir);
- return 0;
-}
-
-static int alc_pin_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- signed int change;
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char dir = (kcontrol->private_value >> 16) & 0xff;
- long val = *ucontrol->value.integer.value;
- unsigned int pinctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL,
- 0x00);
-
- if (val < alc_pin_mode_min(dir) || val > alc_pin_mode_max(dir))
- val = alc_pin_mode_min(dir);
-
- change = pinctl != alc_pin_mode_values[val];
- if (change) {
- /* Set pin mode to that requested */
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- alc_pin_mode_values[val]);
-
- /* Also enable the retasking pin's input/output as required
- * for the requested pin mode. Enum values of 2 or less are
- * input modes.
- *
- * Dynamically switching the input/output buffers probably
- * reduces noise slightly (particularly on input) so we'll
- * do it. However, having both input and output buffers
- * enabled simultaneously doesn't seem to be problematic if
- * this turns out to be necessary in the future.
- */
- if (val <= 2) {
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0,
- HDA_AMP_MUTE, 0);
- } else {
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, 0);
- }
- }
- return change;
-}
-
-#define ALC_PIN_MODE(xname, nid, dir) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
- .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
- .info = alc_pin_mode_info, \
- .get = alc_pin_mode_get, \
- .put = alc_pin_mode_put, \
- .private_value = nid | (dir<<16) }
-
-/* A switch control for ALC260 GPIO pins. Multiple GPIOs can be ganged
- * together using a mask with more than one bit set. This control is
- * currently used only by the ALC260 test model. At this stage they are not
- * needed for any "production" models.
- */
-#ifdef CONFIG_SND_DEBUG
-#define alc_gpio_data_info snd_ctl_boolean_mono_info
-
-static int alc_gpio_data_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
- long *valp = ucontrol->value.integer.value;
- unsigned int val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_GPIO_DATA, 0x00);
-
- *valp = (val & mask) != 0;
- return 0;
-}
-static int alc_gpio_data_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- signed int change;
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
- long val = *ucontrol->value.integer.value;
- unsigned int gpio_data = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_GPIO_DATA,
- 0x00);
-
- /* Set/unset the masked GPIO bit(s) as needed */
- change = (val == 0 ? 0 : mask) != (gpio_data & mask);
- if (val == 0)
- gpio_data &= ~mask;
- else
- gpio_data |= mask;
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_GPIO_DATA, gpio_data);
-
- return change;
-}
-#define ALC_GPIO_DATA_SWITCH(xname, nid, mask) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
- .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
- .info = alc_gpio_data_info, \
- .get = alc_gpio_data_get, \
- .put = alc_gpio_data_put, \
- .private_value = nid | (mask<<16) }
-#endif /* CONFIG_SND_DEBUG */
-
-/* A switch control to allow the enabling of the digital IO pins on the
- * ALC260. This is incredibly simplistic; the intention of this control is
- * to provide something in the test model allowing digital outputs to be
- * identified if present. If models are found which can utilise these
- * outputs a more complete mixer control can be devised for those models if
- * necessary.
- */
-#ifdef CONFIG_SND_DEBUG
-#define alc_spdif_ctrl_info snd_ctl_boolean_mono_info
-
-static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
- long *valp = ucontrol->value.integer.value;
- unsigned int val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_DIGI_CONVERT_1, 0x00);
-
- *valp = (val & mask) != 0;
- return 0;
-}
-static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- signed int change;
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
- long val = *ucontrol->value.integer.value;
- unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_DIGI_CONVERT_1,
- 0x00);
-
- /* Set/unset the masked control bit(s) as needed */
- change = (val == 0 ? 0 : mask) != (ctrl_data & mask);
- if (val==0)
- ctrl_data &= ~mask;
- else
- ctrl_data |= mask;
- snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
- ctrl_data);
-
- return change;
-}
-#define ALC_SPDIF_CTRL_SWITCH(xname, nid, mask) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
- .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
- .info = alc_spdif_ctrl_info, \
- .get = alc_spdif_ctrl_get, \
- .put = alc_spdif_ctrl_put, \
- .private_value = nid | (mask<<16) }
-#endif /* CONFIG_SND_DEBUG */
-
-/* A switch control to allow the enabling EAPD digital outputs on the ALC26x.
- * Again, this is only used in the ALC26x test models to help identify when
- * the EAPD line must be asserted for features to work.
- */
-#ifdef CONFIG_SND_DEBUG
-#define alc_eapd_ctrl_info snd_ctl_boolean_mono_info
-
-static int alc_eapd_ctrl_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
- long *valp = ucontrol->value.integer.value;
- unsigned int val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_EAPD_BTLENABLE, 0x00);
-
- *valp = (val & mask) != 0;
- return 0;
-}
-
-static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int change;
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value & 0xffff;
- unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
- long val = *ucontrol->value.integer.value;
- unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_EAPD_BTLENABLE,
- 0x00);
-
- /* Set/unset the masked control bit(s) as needed */
- change = (!val ? 0 : mask) != (ctrl_data & mask);
- if (!val)
- ctrl_data &= ~mask;
- else
- ctrl_data |= mask;
- snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
- ctrl_data);
-
- return change;
-}
-
-#define ALC_EAPD_CTRL_SWITCH(xname, nid, mask) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
- .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
- .info = alc_eapd_ctrl_info, \
- .get = alc_eapd_ctrl_get, \
- .put = alc_eapd_ctrl_put, \
- .private_value = nid | (mask<<16) }
-#endif /* CONFIG_SND_DEBUG */
-
-/*
- * set up the input pin config (depending on the given auto-pin type)
- */
-static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid,
- int auto_pin_type)
-{
- unsigned int val = PIN_IN;
-
- if (auto_pin_type == AUTO_PIN_MIC) {
- unsigned int pincap;
- unsigned int oldval;
- oldval = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- pincap = snd_hda_query_pin_caps(codec, nid);
- pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
- /* if the default pin setup is vref50, we give it priority */
- if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50)
- val = PIN_VREF80;
- else if (pincap & AC_PINCAP_VREF_50)
- val = PIN_VREF50;
- else if (pincap & AC_PINCAP_VREF_100)
- val = PIN_VREF100;
- else if (pincap & AC_PINCAP_VREF_GRD)
- val = PIN_VREFGRD;
- }
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val);
-}
-
-static void alc_fixup_autocfg_pin_nums(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
-
- if (!cfg->line_outs) {
- while (cfg->line_outs < AUTO_CFG_MAX_OUTS &&
- cfg->line_out_pins[cfg->line_outs])
- cfg->line_outs++;
- }
- if (!cfg->speaker_outs) {
- while (cfg->speaker_outs < AUTO_CFG_MAX_OUTS &&
- cfg->speaker_pins[cfg->speaker_outs])
- cfg->speaker_outs++;
- }
- if (!cfg->hp_outs) {
- while (cfg->hp_outs < AUTO_CFG_MAX_OUTS &&
- cfg->hp_pins[cfg->hp_outs])
- cfg->hp_outs++;
- }
-}
-
-/*
- */
-static void add_mixer(struct alc_spec *spec, struct snd_kcontrol_new *mix)
-{
- if (snd_BUG_ON(spec->num_mixers >= ARRAY_SIZE(spec->mixers)))
- return;
- spec->mixers[spec->num_mixers++] = mix;
-}
-
-static void add_verb(struct alc_spec *spec, const struct hda_verb *verb)
-{
- if (snd_BUG_ON(spec->num_init_verbs >= ARRAY_SIZE(spec->init_verbs)))
- return;
- spec->init_verbs[spec->num_init_verbs++] = verb;
-}
-
-/*
- * set up from the preset table
- */
-static void setup_preset(struct hda_codec *codec,
- const struct alc_config_preset *preset)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++)
- add_mixer(spec, preset->mixers[i]);
- spec->cap_mixer = preset->cap_mixer;
- for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i];
- i++)
- add_verb(spec, preset->init_verbs[i]);
-
- spec->channel_mode = preset->channel_mode;
- spec->num_channel_mode = preset->num_channel_mode;
- spec->need_dac_fix = preset->need_dac_fix;
- spec->const_channel_count = preset->const_channel_count;
-
- if (preset->const_channel_count)
- spec->multiout.max_channels = preset->const_channel_count;
- else
- spec->multiout.max_channels = spec->channel_mode[0].channels;
- spec->ext_channel_count = spec->channel_mode[0].channels;
-
- spec->multiout.num_dacs = preset->num_dacs;
- spec->multiout.dac_nids = preset->dac_nids;
- spec->multiout.dig_out_nid = preset->dig_out_nid;
- spec->multiout.slave_dig_outs = preset->slave_dig_outs;
- spec->multiout.hp_nid = preset->hp_nid;
-
- spec->num_mux_defs = preset->num_mux_defs;
- if (!spec->num_mux_defs)
- spec->num_mux_defs = 1;
- spec->input_mux = preset->input_mux;
-
- spec->num_adc_nids = preset->num_adc_nids;
- spec->adc_nids = preset->adc_nids;
- spec->capsrc_nids = preset->capsrc_nids;
- spec->dig_in_nid = preset->dig_in_nid;
-
- spec->unsol_event = preset->unsol_event;
- spec->init_hook = preset->init_hook;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->power_hook = preset->power_hook;
- spec->loopback.amplist = preset->loopbacks;
-#endif
-
- if (preset->setup)
- preset->setup(codec);
-
- alc_fixup_autocfg_pin_nums(codec);
-}
-
-/* Enable GPIO mask and set output */
-static struct hda_verb alc_gpio1_init_verbs[] = {
- {0x01, AC_VERB_SET_GPIO_MASK, 0x01},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
- { }
-};
-
-static struct hda_verb alc_gpio2_init_verbs[] = {
- {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x02},
- { }
-};
-
-static struct hda_verb alc_gpio3_init_verbs[] = {
- {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x03},
- { }
-};
-
-/*
- * Fix hardware PLL issue
- * On some codecs, the analog PLL gating control must be off while
- * the default value is 1.
- */
-static void alc_fix_pll(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int val;
-
- if (!spec->pll_nid)
- return;
- snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_COEF_INDEX,
- spec->pll_coef_idx);
- val = snd_hda_codec_read(codec, spec->pll_nid, 0,
- AC_VERB_GET_PROC_COEF, 0);
- snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_COEF_INDEX,
- spec->pll_coef_idx);
- snd_hda_codec_write(codec, spec->pll_nid, 0, AC_VERB_SET_PROC_COEF,
- val & ~(1 << spec->pll_coef_bit));
-}
-
-static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
- unsigned int coef_idx, unsigned int coef_bit)
-{
- struct alc_spec *spec = codec->spec;
- spec->pll_nid = nid;
- spec->pll_coef_idx = coef_idx;
- spec->pll_coef_bit = coef_bit;
- alc_fix_pll(codec);
-}
-
-#ifdef CONFIG_SND_HDA_INPUT_JACK
-static void alc_free_jack_priv(struct snd_jack *jack)
-{
- struct alc_jack *jacks = jack->private_data;
- jacks->nid = 0;
- jacks->jack = NULL;
-}
-
-static int alc_add_jack(struct hda_codec *codec,
- hda_nid_t nid, int type)
-{
- struct alc_spec *spec;
- struct alc_jack *jack;
- const char *name;
- int err;
-
- spec = codec->spec;
- snd_array_init(&spec->jacks, sizeof(*jack), 32);
- jack = snd_array_new(&spec->jacks);
- if (!jack)
- return -ENOMEM;
-
- jack->nid = nid;
- jack->type = type;
- name = (type == SND_JACK_HEADPHONE) ? "Headphone" : "Mic" ;
-
- err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
- if (err < 0)
- return err;
- jack->jack->private_data = jack;
- jack->jack->private_free = alc_free_jack_priv;
- return 0;
-}
-
-static void alc_report_jack(struct hda_codec *codec, hda_nid_t nid)
-{
- struct alc_spec *spec = codec->spec;
- struct alc_jack *jacks = spec->jacks.list;
-
- if (jacks) {
- int i;
- for (i = 0; i < spec->jacks.used; i++) {
- if (jacks->nid == nid) {
- unsigned int present;
- present = snd_hda_jack_detect(codec, nid);
-
- present = (present) ? jacks->type : 0;
-
- snd_jack_report(jacks->jack, present);
- }
- jacks++;
- }
- }
-}
-
-static int alc_init_jacks(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int err;
- unsigned int hp_nid = spec->autocfg.hp_pins[0];
- unsigned int mic_nid = spec->ext_mic.pin;
-
- if (hp_nid) {
- err = alc_add_jack(codec, hp_nid, SND_JACK_HEADPHONE);
- if (err < 0)
- return err;
- alc_report_jack(codec, hp_nid);
- }
-
- if (mic_nid) {
- err = alc_add_jack(codec, mic_nid, SND_JACK_MICROPHONE);
- if (err < 0)
- return err;
- alc_report_jack(codec, mic_nid);
- }
-
- return 0;
-}
-#else
-static inline void alc_report_jack(struct hda_codec *codec, hda_nid_t nid)
-{
-}
-
-static inline int alc_init_jacks(struct hda_codec *codec)
-{
- return 0;
-}
-#endif
-
-static void alc_automute_speaker(struct hda_codec *codec, int pinctl)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int mute;
- hda_nid_t nid;
- int i;
-
- spec->jack_present = 0;
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
- nid = spec->autocfg.hp_pins[i];
- if (!nid)
- break;
- if (snd_hda_jack_detect(codec, nid)) {
- spec->jack_present = 1;
- break;
- }
- alc_report_jack(codec, spec->autocfg.hp_pins[i]);
- }
-
- mute = spec->jack_present ? HDA_AMP_MUTE : 0;
- /* Toggle internal speakers muting */
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
- nid = spec->autocfg.speaker_pins[i];
- if (!nid)
- break;
- if (pinctl) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->jack_present ? 0 : PIN_OUT);
- } else {
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- }
- }
-}
-
-static void alc_automute_pin(struct hda_codec *codec)
-{
- alc_automute_speaker(codec, 1);
-}
-
-static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
- hda_nid_t nid)
-{
- hda_nid_t conn[HDA_MAX_NUM_INPUTS];
- int i, nums;
-
- nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
- for (i = 0; i < nums; i++)
- if (conn[i] == nid)
- return i;
- return -1;
-}
-
-/* switch the current ADC according to the jack state */
-static void alc_dual_mic_adc_auto_switch(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int present;
- hda_nid_t new_adc;
-
- present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
- if (present)
- spec->cur_adc_idx = 1;
- else
- spec->cur_adc_idx = 0;
- new_adc = spec->adc_nids[spec->cur_adc_idx];
- if (spec->cur_adc && spec->cur_adc != new_adc) {
- /* stream is running, let's swap the current ADC */
- __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
- spec->cur_adc = new_adc;
- snd_hda_codec_setup_stream(codec, new_adc,
- spec->cur_adc_stream_tag, 0,
- spec->cur_adc_format);
- }
-}
-
-static void alc_mic_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct alc_mic_route *dead, *alive;
- unsigned int present, type;
- hda_nid_t cap_nid;
-
- if (!spec->auto_mic)
- return;
- if (!spec->int_mic.pin || !spec->ext_mic.pin)
- return;
- if (snd_BUG_ON(!spec->adc_nids))
- return;
-
- if (spec->dual_adc_switch) {
- alc_dual_mic_adc_auto_switch(codec);
- return;
- }
-
- cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0];
-
- present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
- if (present) {
- alive = &spec->ext_mic;
- dead = &spec->int_mic;
- } else {
- alive = &spec->int_mic;
- dead = &spec->ext_mic;
- }
-
- type = get_wcaps_type(get_wcaps(codec, cap_nid));
- if (type == AC_WID_AUD_MIX) {
- /* Matrix-mixer style (e.g. ALC882) */
- snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
- alive->mux_idx,
- HDA_AMP_MUTE, 0);
- snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
- dead->mux_idx,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- } else {
- /* MUX style (e.g. ALC880) */
- snd_hda_codec_write_cache(codec, cap_nid, 0,
- AC_VERB_SET_CONNECT_SEL,
- alive->mux_idx);
- }
- alc_report_jack(codec, spec->ext_mic.pin);
-
- /* FIXME: analog mixer */
-}
-
-/* unsolicited event for HP jack sensing */
-static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if (codec->vendor_id == 0x10ec0880)
- res >>= 28;
- else
- res >>= 26;
- switch (res) {
- case ALC880_HP_EVENT:
- alc_automute_pin(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-static void alc_inithook(struct hda_codec *codec)
-{
- alc_automute_pin(codec);
- alc_mic_automute(codec);
-}
-
-/* additional initialization for ALC888 variants */
-static void alc888_coef_init(struct hda_codec *codec)
-{
- unsigned int tmp;
-
- snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0);
- tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0);
- snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7);
- if ((tmp & 0xf0) == 0x20)
- /* alc888S-VC */
- snd_hda_codec_read(codec, 0x20, 0,
- AC_VERB_SET_PROC_COEF, 0x830);
- else
- /* alc888-VB */
- snd_hda_codec_read(codec, 0x20, 0,
- AC_VERB_SET_PROC_COEF, 0x3030);
-}
-
-static void alc889_coef_init(struct hda_codec *codec)
-{
- unsigned int tmp;
-
- snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7);
- tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0);
- snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7);
- snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, tmp|0x2010);
-}
-
-/* turn on/off EAPD control (only if available) */
-static void set_eapd(struct hda_codec *codec, hda_nid_t nid, int on)
-{
- if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
- return;
- if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
- on ? 2 : 0);
-}
-
-static void alc_auto_init_amp(struct hda_codec *codec, int type)
-{
- unsigned int tmp;
-
- switch (type) {
- case ALC_INIT_GPIO1:
- snd_hda_sequence_write(codec, alc_gpio1_init_verbs);
- break;
- case ALC_INIT_GPIO2:
- snd_hda_sequence_write(codec, alc_gpio2_init_verbs);
- break;
- case ALC_INIT_GPIO3:
- snd_hda_sequence_write(codec, alc_gpio3_init_verbs);
- break;
- case ALC_INIT_DEFAULT:
- switch (codec->vendor_id) {
- case 0x10ec0260:
- set_eapd(codec, 0x0f, 1);
- set_eapd(codec, 0x10, 1);
- break;
- case 0x10ec0262:
- case 0x10ec0267:
- case 0x10ec0268:
- case 0x10ec0269:
- case 0x10ec0270:
- case 0x10ec0272:
- case 0x10ec0660:
- case 0x10ec0662:
- case 0x10ec0663:
- case 0x10ec0862:
- case 0x10ec0889:
- set_eapd(codec, 0x14, 1);
- set_eapd(codec, 0x15, 1);
- break;
- }
- switch (codec->vendor_id) {
- case 0x10ec0260:
- snd_hda_codec_write(codec, 0x1a, 0,
- AC_VERB_SET_COEF_INDEX, 7);
- tmp = snd_hda_codec_read(codec, 0x1a, 0,
- AC_VERB_GET_PROC_COEF, 0);
- snd_hda_codec_write(codec, 0x1a, 0,
- AC_VERB_SET_COEF_INDEX, 7);
- snd_hda_codec_write(codec, 0x1a, 0,
- AC_VERB_SET_PROC_COEF,
- tmp | 0x2010);
- break;
- case 0x10ec0262:
- case 0x10ec0880:
- case 0x10ec0882:
- case 0x10ec0883:
- case 0x10ec0885:
- case 0x10ec0887:
- case 0x10ec0889:
- alc889_coef_init(codec);
- break;
- case 0x10ec0888:
- alc888_coef_init(codec);
- break;
-#if 0 /* XXX: This may cause the silent output on speaker on some machines */
- case 0x10ec0267:
- case 0x10ec0268:
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_COEF_INDEX, 7);
- tmp = snd_hda_codec_read(codec, 0x20, 0,
- AC_VERB_GET_PROC_COEF, 0);
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_COEF_INDEX, 7);
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_PROC_COEF,
- tmp | 0x3000);
- break;
-#endif /* XXX */
- }
- break;
- }
-}
-
-static void alc_init_auto_hp(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- if (!cfg->hp_pins[0]) {
- if (cfg->line_out_type != AUTO_PIN_HP_OUT)
- return;
- }
-
- if (!cfg->speaker_pins[0]) {
- if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
- return;
- memcpy(cfg->speaker_pins, cfg->line_out_pins,
- sizeof(cfg->speaker_pins));
- cfg->speaker_outs = cfg->line_outs;
- }
-
- if (!cfg->hp_pins[0]) {
- memcpy(cfg->hp_pins, cfg->line_out_pins,
- sizeof(cfg->hp_pins));
- cfg->hp_outs = cfg->line_outs;
- }
-
- for (i = 0; i < cfg->hp_outs; i++) {
- snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
- cfg->hp_pins[i]);
- snd_hda_codec_write_cache(codec, cfg->hp_pins[i], 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | ALC880_HP_EVENT);
- }
- spec->unsol_event = alc_sku_unsol_event;
-}
-
-static void alc_init_auto_mic(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t fixed, ext;
- int i;
-
- /* there must be only two mic inputs exclusively */
- for (i = 0; i < cfg->num_inputs; i++)
- if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
- return;
-
- fixed = ext = 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- unsigned int defcfg;
- defcfg = snd_hda_codec_get_pincfg(codec, nid);
- switch (snd_hda_get_input_pin_attr(defcfg)) {
- case INPUT_PIN_ATTR_INT:
- if (fixed)
- return; /* already occupied */
- fixed = nid;
- break;
- case INPUT_PIN_ATTR_UNUSED:
- return; /* invalid entry */
- default:
- if (ext)
- return; /* already occupied */
- ext = nid;
- break;
- }
- }
- if (!ext || !fixed)
- return;
- if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
- return; /* no unsol support */
- snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x\n",
- ext, fixed);
- spec->ext_mic.pin = ext;
- spec->int_mic.pin = fixed;
- spec->ext_mic.mux_idx = MUX_IDX_UNDEF; /* set later */
- spec->int_mic.mux_idx = MUX_IDX_UNDEF; /* set later */
- spec->auto_mic = 1;
- snd_hda_codec_write_cache(codec, spec->ext_mic.pin, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | ALC880_MIC_EVENT);
- spec->unsol_event = alc_sku_unsol_event;
-}
-
-/* Could be any non-zero and even value. When used as fixup, tells
- * the driver to ignore any present sku defines.
- */
-#define ALC_FIXUP_SKU_IGNORE (2)
-
-static int alc_auto_parse_customize_define(struct hda_codec *codec)
-{
- unsigned int ass, tmp, i;
- unsigned nid = 0;
- struct alc_spec *spec = codec->spec;
-
- spec->cdefine.enable_pcbeep = 1; /* assume always enabled */
-
- if (spec->cdefine.fixup) {
- ass = spec->cdefine.sku_cfg;
- if (ass == ALC_FIXUP_SKU_IGNORE)
- return -1;
- goto do_sku;
- }
-
- ass = codec->subsystem_id & 0xffff;
- if (ass != codec->bus->pci->subsystem_device && (ass & 1))
- goto do_sku;
-
- nid = 0x1d;
- if (codec->vendor_id == 0x10ec0260)
- nid = 0x17;
- ass = snd_hda_codec_get_pincfg(codec, nid);
-
- if (!(ass & 1)) {
- printk(KERN_INFO "hda_codec: %s: SKU not ready 0x%08x\n",
- codec->chip_name, ass);
- return -1;
- }
-
- /* check sum */
- tmp = 0;
- for (i = 1; i < 16; i++) {
- if ((ass >> i) & 1)
- tmp++;
- }
- if (((ass >> 16) & 0xf) != tmp)
- return -1;
-
- spec->cdefine.port_connectivity = ass >> 30;
- spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20;
- spec->cdefine.check_sum = (ass >> 16) & 0xf;
- spec->cdefine.customization = ass >> 8;
-do_sku:
- spec->cdefine.sku_cfg = ass;
- spec->cdefine.external_amp = (ass & 0x38) >> 3;
- spec->cdefine.platform_type = (ass & 0x4) >> 2;
- spec->cdefine.swap = (ass & 0x2) >> 1;
- spec->cdefine.override = ass & 0x1;
-
- snd_printd("SKU: Nid=0x%x sku_cfg=0x%08x\n",
- nid, spec->cdefine.sku_cfg);
- snd_printd("SKU: port_connectivity=0x%x\n",
- spec->cdefine.port_connectivity);
- snd_printd("SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep);
- snd_printd("SKU: check_sum=0x%08x\n", spec->cdefine.check_sum);
- snd_printd("SKU: customization=0x%08x\n", spec->cdefine.customization);
- snd_printd("SKU: external_amp=0x%x\n", spec->cdefine.external_amp);
- snd_printd("SKU: platform_type=0x%x\n", spec->cdefine.platform_type);
- snd_printd("SKU: swap=0x%x\n", spec->cdefine.swap);
- snd_printd("SKU: override=0x%x\n", spec->cdefine.override);
-
- return 0;
-}
-
-/* check subsystem ID and set up device-specific initialization;
- * return 1 if initialized, 0 if invalid SSID
- */
-/* 32-bit subsystem ID for BIOS loading in HD Audio codec.
- * 31 ~ 16 : Manufacture ID
- * 15 ~ 8 : SKU ID
- * 7 ~ 0 : Assembly ID
- * port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36
- */
-static int alc_subsystem_id(struct hda_codec *codec,
- hda_nid_t porta, hda_nid_t porte,
- hda_nid_t portd, hda_nid_t porti)
-{
- unsigned int ass, tmp, i;
- unsigned nid;
- struct alc_spec *spec = codec->spec;
-
- if (spec->cdefine.fixup) {
- ass = spec->cdefine.sku_cfg;
- if (ass == ALC_FIXUP_SKU_IGNORE)
- return 0;
- goto do_sku;
- }
-
- ass = codec->subsystem_id & 0xffff;
- if ((ass != codec->bus->pci->subsystem_device) && (ass & 1))
- goto do_sku;
-
- /* invalid SSID, check the special NID pin defcfg instead */
- /*
- * 31~30 : port connectivity
- * 29~21 : reserve
- * 20 : PCBEEP input
- * 19~16 : Check sum (15:1)
- * 15~1 : Custom
- * 0 : override
- */
- nid = 0x1d;
- if (codec->vendor_id == 0x10ec0260)
- nid = 0x17;
- ass = snd_hda_codec_get_pincfg(codec, nid);
- snd_printd("realtek: No valid SSID, "
- "checking pincfg 0x%08x for NID 0x%x\n",
- ass, nid);
- if (!(ass & 1))
- return 0;
- if ((ass >> 30) != 1) /* no physical connection */
- return 0;
-
- /* check sum */
- tmp = 0;
- for (i = 1; i < 16; i++) {
- if ((ass >> i) & 1)
- tmp++;
- }
- if (((ass >> 16) & 0xf) != tmp)
- return 0;
-do_sku:
- snd_printd("realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n",
- ass & 0xffff, codec->vendor_id);
- /*
- * 0 : override
- * 1 : Swap Jack
- * 2 : 0 --> Desktop, 1 --> Laptop
- * 3~5 : External Amplifier control
- * 7~6 : Reserved
- */
- tmp = (ass & 0x38) >> 3; /* external Amp control */
- switch (tmp) {
- case 1:
- spec->init_amp = ALC_INIT_GPIO1;
- break;
- case 3:
- spec->init_amp = ALC_INIT_GPIO2;
- break;
- case 7:
- spec->init_amp = ALC_INIT_GPIO3;
- break;
- case 5:
- spec->init_amp = ALC_INIT_DEFAULT;
- break;
- }
-
- /* is laptop or Desktop and enable the function "Mute internal speaker
- * when the external headphone out jack is plugged"
- */
- if (!(ass & 0x8000))
- return 1;
- /*
- * 10~8 : Jack location
- * 12~11: Headphone out -> 00: PortA, 01: PortE, 02: PortD, 03: Resvered
- * 14~13: Resvered
- * 15 : 1 --> enable the function "Mute internal speaker
- * when the external headphone out jack is plugged"
- */
- if (!spec->autocfg.hp_pins[0]) {
- hda_nid_t nid;
- tmp = (ass >> 11) & 0x3; /* HP to chassis */
- if (tmp == 0)
- nid = porta;
- else if (tmp == 1)
- nid = porte;
- else if (tmp == 2)
- nid = portd;
- else if (tmp == 3)
- nid = porti;
- else
- return 1;
- for (i = 0; i < spec->autocfg.line_outs; i++)
- if (spec->autocfg.line_out_pins[i] == nid)
- return 1;
- spec->autocfg.hp_pins[0] = nid;
- }
-
- alc_init_auto_hp(codec);
- alc_init_auto_mic(codec);
- return 1;
-}
-
-static void alc_ssid_check(struct hda_codec *codec,
- hda_nid_t porta, hda_nid_t porte,
- hda_nid_t portd, hda_nid_t porti)
-{
- if (!alc_subsystem_id(codec, porta, porte, portd, porti)) {
- struct alc_spec *spec = codec->spec;
- snd_printd("realtek: "
- "Enable default setup for auto mode as fallback\n");
- spec->init_amp = ALC_INIT_DEFAULT;
- alc_init_auto_hp(codec);
- alc_init_auto_mic(codec);
- }
-}
-
-/*
- * Fix-up pin default configurations and add default verbs
- */
-
-struct alc_pincfg {
- hda_nid_t nid;
- u32 val;
-};
-
-struct alc_fixup {
- unsigned int sku;
- const struct alc_pincfg *pins;
- const struct hda_verb *verbs;
-};
-
-static void alc_pick_fixup(struct hda_codec *codec,
- const struct snd_pci_quirk *quirk,
- const struct alc_fixup *fix,
- int pre_init)
-{
- const struct alc_pincfg *cfg;
- struct alc_spec *spec;
-
- quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk);
- if (!quirk)
- return;
- fix += quirk->value;
- cfg = fix->pins;
- if (pre_init && fix->sku) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- snd_printdd(KERN_INFO "hda_codec: %s: Apply sku override for %s\n",
- codec->chip_name, quirk->name);
-#endif
- spec = codec->spec;
- spec->cdefine.sku_cfg = fix->sku;
- spec->cdefine.fixup = 1;
- }
- if (pre_init && cfg) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- snd_printdd(KERN_INFO "hda_codec: %s: Apply pincfg for %s\n",
- codec->chip_name, quirk->name);
-#endif
- for (; cfg->nid; cfg++)
- snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
- }
- if (!pre_init && fix->verbs) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- snd_printdd(KERN_INFO "hda_codec: %s: Apply fix-verbs for %s\n",
- codec->chip_name, quirk->name);
-#endif
- add_verb(codec->spec, fix->verbs);
- }
-}
-
-static int alc_read_coef_idx(struct hda_codec *codec,
- unsigned int coef_idx)
-{
- unsigned int val;
- snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX,
- coef_idx);
- val = snd_hda_codec_read(codec, 0x20, 0,
- AC_VERB_GET_PROC_COEF, 0);
- return val;
-}
-
-static void alc_write_coef_idx(struct hda_codec *codec, unsigned int coef_idx,
- unsigned int coef_val)
-{
- snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX,
- coef_idx);
- snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF,
- coef_val);
-}
-
-/* set right pin controls for digital I/O */
-static void alc_auto_init_digital(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
- hda_nid_t pin;
-
- for (i = 0; i < spec->autocfg.dig_outs; i++) {
- pin = spec->autocfg.dig_out_pins[i];
- if (pin) {
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_OUT);
- }
- }
- pin = spec->autocfg.dig_in_pin;
- if (pin)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_IN);
-}
-
-/* parse digital I/Os and set up NIDs in BIOS auto-parse mode */
-static void alc_auto_parse_digital(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i, err;
- hda_nid_t dig_nid;
-
- /* support multiple SPDIFs; the secondary is set up as a slave */
- for (i = 0; i < spec->autocfg.dig_outs; i++) {
- err = snd_hda_get_connections(codec,
- spec->autocfg.dig_out_pins[i],
- &dig_nid, 1);
- if (err < 0)
- continue;
- if (!i) {
- spec->multiout.dig_out_nid = dig_nid;
- spec->dig_out_type = spec->autocfg.dig_out_type[0];
- } else {
- spec->multiout.slave_dig_outs = spec->slave_dig_outs;
- if (i >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
- break;
- spec->slave_dig_outs[i - 1] = dig_nid;
- }
- }
-
- if (spec->autocfg.dig_in_pin) {
- dig_nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
- unsigned int wcaps = get_wcaps(codec, dig_nid);
- if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
- continue;
- if (!(wcaps & AC_WCAP_DIGITAL))
- continue;
- if (!(wcaps & AC_WCAP_CONN_LIST))
- continue;
- err = get_connection_index(codec, dig_nid,
- spec->autocfg.dig_in_pin);
- if (err >= 0) {
- spec->dig_in_nid = dig_nid;
- break;
- }
- }
- }
-}
-
-/*
- * ALC888
- */
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc888_4ST_ch2_intel_init[] = {
-/* Mic-in jack as mic in */
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-/* Line-in jack as Line in */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-/* Line-Out as Front */
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00},
- { } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc888_4ST_ch4_intel_init[] = {
-/* Mic-in jack as mic in */
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-/* Line-in jack as Surround */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-/* Line-Out as Front */
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00},
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc888_4ST_ch6_intel_init[] = {
-/* Mic-in jack as CLFE */
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-/* Line-in jack as Surround */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-/* Line-Out as CLFE (workaround because Mic-in is not loud enough) */
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
- { } /* end */
-};
-
-/*
- * 8ch mode
- */
-static struct hda_verb alc888_4ST_ch8_intel_init[] = {
-/* Mic-in jack as CLFE */
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-/* Line-in jack as Surround */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-/* Line-Out as Side */
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
- { } /* end */
-};
-
-static struct hda_channel_mode alc888_4ST_8ch_intel_modes[4] = {
- { 2, alc888_4ST_ch2_intel_init },
- { 4, alc888_4ST_ch4_intel_init },
- { 6, alc888_4ST_ch6_intel_init },
- { 8, alc888_4ST_ch8_intel_init },
-};
-
-/*
- * ALC888 Fujitsu Siemens Amillo xa3530
- */
-
-static struct hda_verb alc888_fujitsu_xa3530_verbs[] = {
-/* Front Mic: set to PIN_IN (empty by default) */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-/* Connect Internal HP to Front */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-/* Connect Bass HP to Front */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
-/* Connect Line-Out side jack (SPDIF) to Side */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
-/* Connect Mic jack to CLFE */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x02},
-/* Connect Line-in jack to Surround */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
-/* Connect HP out jack to Front */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
-/* Enable unsolicited event for HP jack and Line-out jack */
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {}
-};
-
-static void alc_automute_amp(struct hda_codec *codec)
-{
- alc_automute_speaker(codec, 0);
-}
-
-static void alc_automute_amp_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if (codec->vendor_id == 0x10ec0880)
- res >>= 28;
- else
- res >>= 26;
- if (res == ALC880_HP_EVENT)
- alc_automute_amp(codec);
-}
-
-static void alc889_automute_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x16;
- spec->autocfg.speaker_pins[2] = 0x17;
- spec->autocfg.speaker_pins[3] = 0x19;
- spec->autocfg.speaker_pins[4] = 0x1a;
-}
-
-static void alc889_intel_init_hook(struct hda_codec *codec)
-{
- alc889_coef_init(codec);
- alc_automute_amp(codec);
-}
-
-static void alc888_fujitsu_xa3530_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x17; /* line-out */
- spec->autocfg.hp_pins[1] = 0x1b; /* hp */
- spec->autocfg.speaker_pins[0] = 0x14; /* speaker */
- spec->autocfg.speaker_pins[1] = 0x15; /* bass */
-}
-
-/*
- * ALC888 Acer Aspire 4930G model
- */
-
-static struct hda_verb alc888_acer_aspire_4930g_verbs[] = {
-/* Front Mic: set to PIN_IN (empty by default) */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-/* Unselect Front Mic by default in input mixer 3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)},
-/* Enable unsolicited event for HP jack */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-/* Connect Internal HP to front */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-/* Connect HP out to front */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- { }
-};
-
-/*
- * ALC888 Acer Aspire 6530G model
- */
-
-static struct hda_verb alc888_acer_aspire_6530g_verbs[] = {
-/* Route to built-in subwoofer as well as speakers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-/* Bias voltage on for external mic port */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN | PIN_VREF80},
-/* Front Mic: set to PIN_IN (empty by default) */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-/* Unselect Front Mic by default in input mixer 3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)},
-/* Enable unsolicited event for HP jack */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-/* Enable speaker output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
-/* Enable headphone output */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-/*
- * ALC889 Acer Aspire 8930G model
- */
-
-static struct hda_verb alc889_acer_aspire_8930g_verbs[] = {
-/* Front Mic: set to PIN_IN (empty by default) */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-/* Unselect Front Mic by default in input mixer 3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)},
-/* Enable unsolicited event for HP jack */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-/* Connect Internal Front to Front */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-/* Connect Internal Rear to Rear */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01},
-/* Connect Internal CLFE to CLFE */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
-/* Connect HP out to Front */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
-/* Enable all DACs */
-/* DAC DISABLE/MUTE 1? */
-/* setting bits 1-5 disables DAC nids 0x02-0x06 apparently. Init=0x38 */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x03},
- {0x20, AC_VERB_SET_PROC_COEF, 0x0000},
-/* DAC DISABLE/MUTE 2? */
-/* some bit here disables the other DACs. Init=0x4900 */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x08},
- {0x20, AC_VERB_SET_PROC_COEF, 0x0000},
-/* DMIC fix
- * This laptop has a stereo digital microphone. The mics are only 1cm apart
- * which makes the stereo useless. However, either the mic or the ALC889
- * makes the signal become a difference/sum signal instead of standard
- * stereo, which is annoying. So instead we flip this bit which makes the
- * codec replicate the sum signal to both channels, turning it into a
- * normal mono mic.
- */
-/* DMIC_CONTROL? Init value = 0x0001 */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x0b},
- {0x20, AC_VERB_SET_PROC_COEF, 0x0003},
- { }
-};
-
-static struct hda_input_mux alc888_2_capture_sources[2] = {
- /* Front mic only available on one ADC */
- {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Front Mic", 0xb },
- },
- },
- {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
- }
-};
-
-static struct hda_input_mux alc888_acer_aspire_6530_sources[2] = {
- /* Interal mic only available on one ADC */
- {
- .num_items = 5,
- .items = {
- { "Ext Mic", 0x0 },
- { "Line In", 0x2 },
- { "CD", 0x4 },
- { "Input Mix", 0xa },
- { "Int Mic", 0xb },
- },
- },
- {
- .num_items = 4,
- .items = {
- { "Ext Mic", 0x0 },
- { "Line In", 0x2 },
- { "CD", 0x4 },
- { "Input Mix", 0xa },
- },
- }
-};
-
-static struct hda_input_mux alc889_capture_sources[3] = {
- /* Digital mic only available on first "ADC" */
- {
- .num_items = 5,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Front Mic", 0xb },
- { "Input Mix", 0xa },
- },
- },
- {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Input Mix", 0xa },
- },
- },
- {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Input Mix", 0xa },
- },
- }
-};
-
-static struct snd_kcontrol_new alc888_base_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc889_acer_aspire_8930g_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-
-static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x16;
- spec->autocfg.speaker_pins[2] = 0x17;
-}
-
-static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x16;
- spec->autocfg.speaker_pins[2] = 0x17;
-}
-
-static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x16;
- spec->autocfg.speaker_pins[2] = 0x1b;
-}
-
-/*
- * ALC880 3-stack model
- *
- * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e)
- * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18,
- * F-Mic = 0x1b, HP = 0x19
- */
-
-static hda_nid_t alc880_dac_nids[4] = {
- /* front, rear, clfe, rear_surr */
- 0x02, 0x05, 0x04, 0x03
-};
-
-static hda_nid_t alc880_adc_nids[3] = {
- /* ADC0-2 */
- 0x07, 0x08, 0x09,
-};
-
-/* The datasheet says the node 0x07 is connected from inputs,
- * but it shows zero connection in the real implementation on some devices.
- * Note: this is a 915GAV bug, fixed on 915GLV
- */
-static hda_nid_t alc880_adc_nids_alt[2] = {
- /* ADC1-2 */
- 0x08, 0x09,
-};
-
-#define ALC880_DIGOUT_NID 0x06
-#define ALC880_DIGIN_NID 0x0a
-
-static struct hda_input_mux alc880_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x3 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-/* channel source setting (2/6 channel selection for 3-stack) */
-/* 2ch mode */
-static struct hda_verb alc880_threestack_ch2_init[] = {
- /* set line-in to input, mute it */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- /* set mic-in to input vref 80%, mute it */
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/* 6ch mode */
-static struct hda_verb alc880_threestack_ch6_init[] = {
- /* set line-in to output, unmute it */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- /* set mic-in to output, unmute it */
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { } /* end */
-};
-
-static struct hda_channel_mode alc880_threestack_modes[2] = {
- { 2, alc880_threestack_ch2_init },
- { 6, alc880_threestack_ch6_init },
-};
-
-static struct snd_kcontrol_new alc880_three_stack_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-/* capture mixer elements */
-static int alc_cap_vol_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- int err;
-
- mutex_lock(&codec->control_mutex);
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0,
- HDA_INPUT);
- err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
- mutex_unlock(&codec->control_mutex);
- return err;
-}
-
-static int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
- unsigned int size, unsigned int __user *tlv)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- int err;
-
- mutex_lock(&codec->control_mutex);
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0,
- HDA_INPUT);
- err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
- mutex_unlock(&codec->control_mutex);
- return err;
-}
-
-typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-
-static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol,
- getput_call_t func)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- int err;
-
- mutex_lock(&codec->control_mutex);
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[adc_idx],
- 3, 0, HDA_INPUT);
- err = func(kcontrol, ucontrol);
- mutex_unlock(&codec->control_mutex);
- return err;
-}
-
-static int alc_cap_vol_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- return alc_cap_getput_caller(kcontrol, ucontrol,
- snd_hda_mixer_amp_volume_get);
-}
-
-static int alc_cap_vol_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- return alc_cap_getput_caller(kcontrol, ucontrol,
- snd_hda_mixer_amp_volume_put);
-}
-
-/* capture mixer elements */
-#define alc_cap_sw_info snd_ctl_boolean_stereo_info
-
-static int alc_cap_sw_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- return alc_cap_getput_caller(kcontrol, ucontrol,
- snd_hda_mixer_amp_switch_get);
-}
-
-static int alc_cap_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- return alc_cap_getput_caller(kcontrol, ucontrol,
- snd_hda_mixer_amp_switch_put);
-}
-
-#define _DEFINE_CAPMIX(num) \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Capture Switch", \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
- .count = num, \
- .info = alc_cap_sw_info, \
- .get = alc_cap_sw_get, \
- .put = alc_cap_sw_put, \
- }, \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Capture Volume", \
- .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | \
- SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), \
- .count = num, \
- .info = alc_cap_vol_info, \
- .get = alc_cap_vol_get, \
- .put = alc_cap_vol_put, \
- .tlv = { .c = alc_cap_vol_tlv }, \
- }
-
-#define _DEFINE_CAPSRC(num) \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- /* .name = "Capture Source", */ \
- .name = "Input Source", \
- .count = num, \
- .info = alc_mux_enum_info, \
- .get = alc_mux_enum_get, \
- .put = alc_mux_enum_put, \
- }
-
-#define DEFINE_CAPMIX(num) \
-static struct snd_kcontrol_new alc_capture_mixer ## num[] = { \
- _DEFINE_CAPMIX(num), \
- _DEFINE_CAPSRC(num), \
- { } /* end */ \
-}
-
-#define DEFINE_CAPMIX_NOSRC(num) \
-static struct snd_kcontrol_new alc_capture_mixer_nosrc ## num[] = { \
- _DEFINE_CAPMIX(num), \
- { } /* end */ \
-}
-
-/* up to three ADCs */
-DEFINE_CAPMIX(1);
-DEFINE_CAPMIX(2);
-DEFINE_CAPMIX(3);
-DEFINE_CAPMIX_NOSRC(1);
-DEFINE_CAPMIX_NOSRC(2);
-DEFINE_CAPMIX_NOSRC(3);
-
-/*
- * ALC880 5-stack model
- *
- * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d),
- * Side = 0x02 (0xd)
- * Pin assignment: Front = 0x14, Surr = 0x17, CLFE = 0x16
- * Line-In/Side = 0x1a, Mic = 0x18, F-Mic = 0x1b, HP = 0x19
- */
-
-/* additional mixers to alc880_three_stack_mixer */
-static struct snd_kcontrol_new alc880_five_stack_mixer[] = {
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0d, 2, HDA_INPUT),
- { } /* end */
-};
-
-/* channel source setting (6/8 channel selection for 5-stack) */
-/* 6ch mode */
-static struct hda_verb alc880_fivestack_ch6_init[] = {
- /* set line-in to input, mute it */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/* 8ch mode */
-static struct hda_verb alc880_fivestack_ch8_init[] = {
- /* set line-in to output, unmute it */
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { } /* end */
-};
-
-static struct hda_channel_mode alc880_fivestack_modes[2] = {
- { 6, alc880_fivestack_ch6_init },
- { 8, alc880_fivestack_ch8_init },
-};
-
-
-/*
- * ALC880 6-stack model
- *
- * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e),
- * Side = 0x05 (0x0f)
- * Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, Side = 0x17,
- * Mic = 0x18, F-Mic = 0x19, Line = 0x1a, HP = 0x1b
- */
-
-static hda_nid_t alc880_6st_dac_nids[4] = {
- /* front, rear, clfe, rear_surr */
- 0x02, 0x03, 0x04, 0x05
-};
-
-static struct hda_input_mux alc880_6stack_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-/* fixed 8-channels */
-static struct hda_channel_mode alc880_sixstack_modes[1] = {
- { 8, NULL },
-};
-
-static struct snd_kcontrol_new alc880_six_stack_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-
-/*
- * ALC880 W810 model
- *
- * W810 has rear IO for:
- * Front (DAC 02)
- * Surround (DAC 03)
- * Center/LFE (DAC 04)
- * Digital out (06)
- *
- * The system also has a pair of internal speakers, and a headphone jack.
- * These are both connected to Line2 on the codec, hence to DAC 02.
- *
- * There is a variable resistor to control the speaker or headphone
- * volume. This is a hardware-only device without a software API.
- *
- * Plugging headphones in will disable the internal speakers. This is
- * implemented in hardware, not via the driver using jack sense. In
- * a similar fashion, plugging into the rear socket marked "front" will
- * disable both the speakers and headphones.
- *
- * For input, there's a microphone jack, and an "audio in" jack.
- * These may not do anything useful with this driver yet, because I
- * haven't setup any initialization verbs for these yet...
- */
-
-static hda_nid_t alc880_w810_dac_nids[3] = {
- /* front, rear/surround, clfe */
- 0x02, 0x03, 0x04
-};
-
-/* fixed 6 channels */
-static struct hda_channel_mode alc880_w810_modes[1] = {
- { 6, NULL }
-};
-
-/* Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, HP = 0x1b */
-static struct snd_kcontrol_new alc880_w810_base_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-
-/*
- * Z710V model
- *
- * DAC: Front = 0x02 (0x0c), HP = 0x03 (0x0d)
- * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?),
- * Line = 0x1a
- */
-
-static hda_nid_t alc880_z71v_dac_nids[1] = {
- 0x02
-};
-#define ALC880_Z71V_HP_DAC 0x03
-
-/* fixed 2 channels */
-static struct hda_channel_mode alc880_2_jack_modes[1] = {
- { 2, NULL }
-};
-
-static struct snd_kcontrol_new alc880_z71v_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-
-/*
- * ALC880 F1734 model
- *
- * DAC: HP = 0x02 (0x0c), Front = 0x03 (0x0d)
- * Pin assignment: HP = 0x14, Front = 0x15, Mic = 0x18
- */
-
-static hda_nid_t alc880_f1734_dac_nids[1] = {
- 0x03
-};
-#define ALC880_F1734_HP_DAC 0x02
-
-static struct snd_kcontrol_new alc880_f1734_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_input_mux alc880_f1734_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x1 },
- { "CD", 0x4 },
- },
-};
-
-
-/*
- * ALC880 ASUS model
- *
- * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e)
- * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16,
- * Mic = 0x18, Line = 0x1a
- */
-
-#define alc880_asus_dac_nids alc880_w810_dac_nids /* identical with w810 */
-#define alc880_asus_modes alc880_threestack_modes /* 2/6 channel mode */
-
-static struct snd_kcontrol_new alc880_asus_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-/*
- * ALC880 ASUS W1V model
- *
- * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e)
- * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16,
- * Mic = 0x18, Line = 0x1a, Line2 = 0x1b
- */
-
-/* additional mixers to alc880_asus_mixer */
-static struct snd_kcontrol_new alc880_asus_w1v_mixer[] = {
- HDA_CODEC_VOLUME("Line2 Playback Volume", 0x0b, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Line2 Playback Switch", 0x0b, 0x03, HDA_INPUT),
- { } /* end */
-};
-
-/* TCL S700 */
-static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0B, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0B, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0B, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0B, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-/* Uniwill */
-static struct snd_kcontrol_new alc880_uniwill_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc880_fujitsu_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc880_uniwill_p53_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-/*
- * virtual master controls
- */
-
-/*
- * slave controls for virtual master
- */
-static const char *alc_slave_vols[] = {
- "Front Playback Volume",
- "Surround Playback Volume",
- "Center Playback Volume",
- "LFE Playback Volume",
- "Side Playback Volume",
- "Headphone Playback Volume",
- "Speaker Playback Volume",
- "Mono Playback Volume",
- "Line-Out Playback Volume",
- "PCM Playback Volume",
- NULL,
-};
-
-static const char *alc_slave_sws[] = {
- "Front Playback Switch",
- "Surround Playback Switch",
- "Center Playback Switch",
- "LFE Playback Switch",
- "Side Playback Switch",
- "Headphone Playback Switch",
- "Speaker Playback Switch",
- "Mono Playback Switch",
- "IEC958 Playback Switch",
- "Line-Out Playback Switch",
- "PCM Playback Switch",
- NULL,
-};
-
-/*
- * build control elements
- */
-
-#define NID_MAPPING (-1)
-
-#define SUBDEV_SPEAKER_ (0 << 6)
-#define SUBDEV_HP_ (1 << 6)
-#define SUBDEV_LINE_ (2 << 6)
-#define SUBDEV_SPEAKER(x) (SUBDEV_SPEAKER_ | ((x) & 0x3f))
-#define SUBDEV_HP(x) (SUBDEV_HP_ | ((x) & 0x3f))
-#define SUBDEV_LINE(x) (SUBDEV_LINE_ | ((x) & 0x3f))
-
-static void alc_free_kctls(struct hda_codec *codec);
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-/* additional beep mixers; the actual parameters are overwritten at build */
-static struct snd_kcontrol_new alc_beep_mixer[] = {
- HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT),
- HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT),
- { } /* end */
-};
-#endif
-
-static int alc_build_controls(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct snd_kcontrol *kctl = NULL;
- struct snd_kcontrol_new *knew;
- int i, j, err;
- unsigned int u;
- hda_nid_t nid;
-
- for (i = 0; i < spec->num_mixers; i++) {
- err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
- if (err < 0)
- return err;
- }
- if (spec->cap_mixer) {
- err = snd_hda_add_new_ctls(codec, spec->cap_mixer);
- if (err < 0)
- return err;
- }
- if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- if (!spec->no_analog) {
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- }
- if (spec->dig_in_nid) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
- if (err < 0)
- return err;
- }
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
- /* create beep controls if needed */
- if (spec->beep_amp) {
- struct snd_kcontrol_new *knew;
- for (knew = alc_beep_mixer; knew->name; knew++) {
- struct snd_kcontrol *kctl;
- kctl = snd_ctl_new1(knew, codec);
- if (!kctl)
- return -ENOMEM;
- kctl->private_value = spec->beep_amp;
- err = snd_hda_ctl_add(codec, 0, kctl);
- if (err < 0)
- return err;
- }
- }
-#endif
-
- /* if we have no master control, let's create it */
- if (!spec->no_analog &&
- !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
- unsigned int vmaster_tlv[4];
- snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
- HDA_OUTPUT, vmaster_tlv);
- err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- vmaster_tlv, alc_slave_vols);
- if (err < 0)
- return err;
- }
- if (!spec->no_analog &&
- !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
- err = snd_hda_add_vmaster(codec, "Master Playback Switch",
- NULL, alc_slave_sws);
- if (err < 0)
- return err;
- }
-
- /* assign Capture Source enums to NID */
- if (spec->capsrc_nids || spec->adc_nids) {
- kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
- if (!kctl)
- kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
- for (i = 0; kctl && i < kctl->count; i++) {
- hda_nid_t *nids = spec->capsrc_nids;
- if (!nids)
- nids = spec->adc_nids;
- err = snd_hda_add_nid(codec, kctl, i, nids[i]);
- if (err < 0)
- return err;
- }
- }
- if (spec->cap_mixer) {
- const char *kname = kctl ? kctl->id.name : NULL;
- for (knew = spec->cap_mixer; knew->name; knew++) {
- if (kname && strcmp(knew->name, kname) == 0)
- continue;
- kctl = snd_hda_find_mixer_ctl(codec, knew->name);
- for (i = 0; kctl && i < kctl->count; i++) {
- err = snd_hda_add_nid(codec, kctl, i,
- spec->adc_nids[i]);
- if (err < 0)
- return err;
- }
- }
- }
-
- /* other nid->control mapping */
- for (i = 0; i < spec->num_mixers; i++) {
- for (knew = spec->mixers[i]; knew->name; knew++) {
- if (knew->iface != NID_MAPPING)
- continue;
- kctl = snd_hda_find_mixer_ctl(codec, knew->name);
- if (kctl == NULL)
- continue;
- u = knew->subdevice;
- for (j = 0; j < 4; j++, u >>= 8) {
- nid = u & 0x3f;
- if (nid == 0)
- continue;
- switch (u & 0xc0) {
- case SUBDEV_SPEAKER_:
- nid = spec->autocfg.speaker_pins[nid];
- break;
- case SUBDEV_LINE_:
- nid = spec->autocfg.line_out_pins[nid];
- break;
- case SUBDEV_HP_:
- nid = spec->autocfg.hp_pins[nid];
- break;
- default:
- continue;
- }
- err = snd_hda_add_nid(codec, kctl, 0, nid);
- if (err < 0)
- return err;
- }
- u = knew->private_value;
- for (j = 0; j < 4; j++, u >>= 8) {
- nid = u & 0xff;
- if (nid == 0)
- continue;
- err = snd_hda_add_nid(codec, kctl, 0, nid);
- if (err < 0)
- return err;
- }
- }
- }
-
- alc_free_kctls(codec); /* no longer needed */
-
- return 0;
-}
-
-
-/*
- * initialize the codec volumes, etc
- */
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc880_volume_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for front
- * panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
-
- /*
- * Set up output mixers (0x0c - 0x0f)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- { }
-};
-
-/*
- * 3-stack pin configuration:
- * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b
- */
-static struct hda_verb alc880_pin_3stack_init_verbs[] = {
- /*
- * preset connection lists of input pins
- * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround
- */
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */
-
- /*
- * Set pin mode and muting
- */
- /* set front pin widgets 0x14 for output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mic2 (as headphone out) for HP output */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Line In pin widget for input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line2 (as front mic) pin widget for input and vref at 80% */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-/*
- * 5-stack pin configuration:
- * front = 0x14, surround = 0x17, clfe = 0x16, mic = 0x18, HP = 0x19,
- * line-in/side = 0x1a, f-mic = 0x1b
- */
-static struct hda_verb alc880_pin_5stack_init_verbs[] = {
- /*
- * preset connection lists of input pins
- * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround
- */
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/side */
-
- /*
- * Set pin mode and muting
- */
- /* set pin widgets 0x14-0x17 for output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* unmute pins for output (no gain on this amp) */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mic2 (as headphone out) for HP output */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Line In pin widget for input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line2 (as front mic) pin widget for input and vref at 80% */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-/*
- * W810 pin configuration:
- * front = 0x14, surround = 0x15, clfe = 0x16, HP = 0x1b
- */
-static struct hda_verb alc880_pin_w810_init_verbs[] = {
- /* hphone/speaker input selector: front DAC */
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- { }
-};
-
-/*
- * Z71V pin configuration:
- * Speaker-out = 0x14, HP = 0x15, Mic = 0x18, Line-in = 0x1a, Mic2 = 0x1b (?)
- */
-static struct hda_verb alc880_pin_z71v_init_verbs[] = {
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-/*
- * 6-stack pin configuration:
- * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18,
- * f-mic = 0x19, line = 0x1a, HP = 0x1b
- */
-static struct hda_verb alc880_pin_6stack_init_verbs[] = {
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-/*
- * Uniwill pin configuration:
- * HP = 0x14, InternalSpeaker = 0x15, mic = 0x18, internal mic = 0x19,
- * line = 0x1a
- */
-static struct hda_verb alc880_uniwill_init_verbs[] = {
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, */
- /* {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
-
- { }
-};
-
-/*
-* Uniwill P53
-* HP = 0x14, InternalSpeaker = 0x15, mic = 0x19,
- */
-static struct hda_verb alc880_uniwill_p53_init_verbs[] = {
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_DCVOL_EVENT},
-
- { }
-};
-
-static struct hda_verb alc880_beep_init_verbs[] = {
- { 0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) },
- { }
-};
-
-/* auto-toggle front mic */
-static void alc880_uniwill_mic_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x18);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
-}
-
-static void alc880_uniwill_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x16;
-}
-
-static void alc880_uniwill_init_hook(struct hda_codec *codec)
-{
- alc_automute_amp(codec);
- alc880_uniwill_mic_automute(codec);
-}
-
-static void alc880_uniwill_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- /* Looks like the unsol event is incompatible with the standard
- * definition. 4bit tag is placed at 28 bit!
- */
- switch (res >> 28) {
- case ALC880_MIC_EVENT:
- alc880_uniwill_mic_automute(codec);
- break;
- default:
- alc_automute_amp_unsol_event(codec, res);
- break;
- }
-}
-
-static void alc880_uniwill_p53_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x15;
-}
-
-static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_codec_read(codec, 0x21, 0,
- AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
- present &= HDA_AMP_VOLMASK;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0,
- HDA_AMP_VOLMASK, present);
- snd_hda_codec_amp_stereo(codec, 0x0d, HDA_OUTPUT, 0,
- HDA_AMP_VOLMASK, present);
-}
-
-static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- /* Looks like the unsol event is incompatible with the standard
- * definition. 4bit tag is placed at 28 bit!
- */
- if ((res >> 28) == ALC880_DCVOL_EVENT)
- alc880_uniwill_p53_dcvol_automute(codec);
- else
- alc_automute_amp_unsol_event(codec, res);
-}
-
-/*
- * F1734 pin configuration:
- * HP = 0x14, speaker-out = 0x15, mic = 0x18
- */
-static struct hda_verb alc880_pin_f1734_init_verbs[] = {
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x02},
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_DCVOL_EVENT},
-
- { }
-};
-
-/*
- * ASUS pin configuration:
- * HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a
- */
-static struct hda_verb alc880_pin_asus_init_verbs[] = {
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x02},
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-/* Enable GPIO mask and set output */
-#define alc880_gpio1_init_verbs alc_gpio1_init_verbs
-#define alc880_gpio2_init_verbs alc_gpio2_init_verbs
-#define alc880_gpio3_init_verbs alc_gpio3_init_verbs
-
-/* Clevo m520g init */
-static struct hda_verb alc880_pin_clevo_init_verbs[] = {
- /* headphone output */
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* line-out */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Line-in */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* CD */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Mic1 (rear panel) */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Mic2 (front panel) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* headphone */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* change to EAPD mode */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3060},
-
- { }
-};
-
-static struct hda_verb alc880_pin_tcl_S700_init_verbs[] = {
- /* change to EAPD mode */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3060},
-
- /* Headphone output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Front output*/
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Line In pin widget for input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-
- /* change to EAPD mode */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3070},
-
- { }
-};
-
-/*
- * LG m1 express dual
- *
- * Pin assignment:
- * Rear Line-In/Out (blue): 0x14
- * Build-in Mic-In: 0x15
- * Speaker-out: 0x17
- * HP-Out (green): 0x1b
- * Mic-In/Out (red): 0x19
- * SPDIF-Out: 0x1e
- */
-
-/* To make 5.1 output working (green=Front, blue=Surr, red=CLFE) */
-static hda_nid_t alc880_lg_dac_nids[3] = {
- 0x05, 0x02, 0x03
-};
-
-/* seems analog CD is not working */
-static struct hda_input_mux alc880_lg_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x1 },
- { "Line", 0x5 },
- { "Internal Mic", 0x6 },
- },
-};
-
-/* 2,4,6 channel modes */
-static struct hda_verb alc880_lg_ch2_init[] = {
- /* set line-in and mic-in to input */
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { }
-};
-
-static struct hda_verb alc880_lg_ch4_init[] = {
- /* set line-in to out and mic-in to input */
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { }
-};
-
-static struct hda_verb alc880_lg_ch6_init[] = {
- /* set line-in and mic-in to output */
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { }
-};
-
-static struct hda_channel_mode alc880_lg_ch_modes[3] = {
- { 2, alc880_lg_ch2_init },
- { 4, alc880_lg_ch4_init },
- { 6, alc880_lg_ch6_init },
-};
-
-static struct snd_kcontrol_new alc880_lg_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x06, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x07, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x07, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-static struct hda_verb alc880_lg_init_verbs[] = {
- /* set capture source to mic-in */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* mute all amp mixer inputs */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* line-in to input */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* built-in mic */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* speaker-out */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* mic-in to input */
- {0x11, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* HP-out */
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x03},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* jack sense */
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- { }
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc880_lg_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x17;
-}
-
-/*
- * LG LW20
- *
- * Pin assignment:
- * Speaker-out: 0x14
- * Mic-In: 0x18
- * Built-in Mic-In: 0x19
- * Line-In: 0x1b
- * HP-Out: 0x1a
- * SPDIF-Out: 0x1e
- */
-
-static struct hda_input_mux alc880_lg_lw_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x1 },
- { "Line In", 0x2 },
- },
-};
-
-#define alc880_lg_lw_modes alc880_threestack_modes
-
-static struct snd_kcontrol_new alc880_lg_lw_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-static struct hda_verb alc880_lg_lw_init_verbs[] = {
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
- {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */
-
- /* set capture source to mic-in */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /* speaker-out */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* HP-out */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* mic-in to input */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* built-in mic */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* jack sense */
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- { }
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc880_lg_lw_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-static struct snd_kcontrol_new alc880_medion_rim_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_input_mux alc880_medion_rim_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x1 },
- },
-};
-
-static struct hda_verb alc880_medion_rim_init_verbs[] = {
- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Mic2 (as headphone out) for HP output */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Internal Speaker */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3060},
-
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- { }
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc880_medion_rim_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc_automute_amp(codec);
- /* toggle EAPD */
- if (spec->jack_present)
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0);
- else
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 2);
-}
-
-static void alc880_medion_rim_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- /* Looks like the unsol event is incompatible with the standard
- * definition. 4bit tag is placed at 28 bit!
- */
- if ((res >> 28) == ALC880_HP_EVENT)
- alc880_medion_rim_automute(codec);
-}
-
-static void alc880_medion_rim_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x1b;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list alc880_loopbacks[] = {
- { 0x0b, HDA_INPUT, 0 },
- { 0x0b, HDA_INPUT, 1 },
- { 0x0b, HDA_INPUT, 2 },
- { 0x0b, HDA_INPUT, 3 },
- { 0x0b, HDA_INPUT, 4 },
- { } /* end */
-};
-
-static struct hda_amp_list alc880_lg_loopbacks[] = {
- { 0x0b, HDA_INPUT, 1 },
- { 0x0b, HDA_INPUT, 6 },
- { 0x0b, HDA_INPUT, 7 },
- { } /* end */
-};
-#endif
-
-/*
- * Common callbacks
- */
-
-static int alc_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int i;
-
- alc_fix_pll(codec);
- alc_auto_init_amp(codec, spec->init_amp);
-
- for (i = 0; i < spec->num_init_verbs; i++)
- snd_hda_sequence_write(codec, spec->init_verbs[i]);
-
- if (spec->init_hook)
- spec->init_hook(codec);
-
- hda_call_check_power_status(codec, 0x01);
- return 0;
-}
-
-static void alc_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- struct alc_spec *spec = codec->spec;
-
- if (spec->unsol_event)
- spec->unsol_event(codec, res);
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
-}
-#endif
-
-/*
- * Analog playback callbacks
- */
-static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Digital out
- */
-static int alc880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int alc880_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int alc880_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
-}
-
-static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-/*
- * Analog capture
- */
-static int alc880_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
-
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
- stream_tag, 0, format);
- return 0;
-}
-
-static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
-
- snd_hda_codec_cleanup_stream(codec,
- spec->adc_nids[substream->number + 1]);
- return 0;
-}
-
-/* analog capture with dynamic dual-adc changes */
-static int dualmic_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- spec->cur_adc = spec->adc_nids[spec->cur_adc_idx];
- spec->cur_adc_stream_tag = stream_tag;
- spec->cur_adc_format = format;
- snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
- return 0;
-}
-
-static int dualmic_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
- snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
- spec->cur_adc = 0;
- return 0;
-}
-
-static struct hda_pcm_stream dualmic_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .prepare = dualmic_capture_pcm_prepare,
- .cleanup = dualmic_capture_pcm_cleanup
- },
-};
-
-/*
- */
-static struct hda_pcm_stream alc880_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- /* NID is set in alc_build_pcms */
- .ops = {
- .open = alc880_playback_pcm_open,
- .prepare = alc880_playback_pcm_prepare,
- .cleanup = alc880_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream alc880_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
-};
-
-static struct hda_pcm_stream alc880_pcm_analog_alt_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
-};
-
-static struct hda_pcm_stream alc880_pcm_analog_alt_capture = {
- .substreams = 2, /* can be overridden */
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
- .ops = {
- .prepare = alc880_alt_capture_pcm_prepare,
- .cleanup = alc880_alt_capture_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream alc880_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
- .ops = {
- .open = alc880_dig_playback_pcm_open,
- .close = alc880_dig_playback_pcm_close,
- .prepare = alc880_dig_playback_pcm_prepare,
- .cleanup = alc880_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream alc880_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
-};
-
-/* Used by alc_build_pcms to flag that a PCM has no playback stream */
-static struct hda_pcm_stream alc_pcm_null_stream = {
- .substreams = 0,
- .channels_min = 0,
- .channels_max = 0,
-};
-
-static int alc_build_pcms(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
- int i;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- if (spec->no_analog)
- goto skip_analog;
-
- snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
- "%s Analog", codec->chip_name);
- info->name = spec->stream_name_analog;
-
- if (spec->stream_analog_playback) {
- if (snd_BUG_ON(!spec->multiout.dac_nids))
- return -EINVAL;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
- }
- if (spec->stream_analog_capture) {
- if (snd_BUG_ON(!spec->adc_nids))
- return -EINVAL;
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
- }
-
- if (spec->channel_mode) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0;
- for (i = 0; i < spec->num_channel_mode; i++) {
- if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels;
- }
- }
- }
-
- skip_analog:
- /* SPDIF for stream index #1 */
- if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
- snprintf(spec->stream_name_digital,
- sizeof(spec->stream_name_digital),
- "%s Digital", codec->chip_name);
- codec->num_pcms = 2;
- codec->slave_dig_outs = spec->multiout.slave_dig_outs;
- info = spec->pcm_rec + 1;
- info->name = spec->stream_name_digital;
- if (spec->dig_out_type)
- info->pcm_type = spec->dig_out_type;
- else
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- if (spec->multiout.dig_out_nid &&
- spec->stream_digital_playback) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback);
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
- }
- if (spec->dig_in_nid &&
- spec->stream_digital_capture) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_digital_capture);
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
- }
- /* FIXME: do we need this for all Realtek codec models? */
- codec->spdif_status_reset = 1;
- }
-
- if (spec->no_analog)
- return 0;
-
- /* If the use of more than one ADC is requested for the current
- * model, configure a second analog capture-only PCM.
- */
- /* Additional Analaog capture for index #2 */
- if ((spec->alt_dac_nid && spec->stream_analog_alt_playback) ||
- (spec->num_adc_nids > 1 && spec->stream_analog_alt_capture)) {
- codec->num_pcms = 3;
- info = spec->pcm_rec + 2;
- info->name = spec->stream_name_analog;
- if (spec->alt_dac_nid) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- *spec->stream_analog_alt_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->alt_dac_nid;
- } else {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- alc_pcm_null_stream;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
- }
- if (spec->num_adc_nids > 1) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- *spec->stream_analog_alt_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
- spec->adc_nids[1];
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
- spec->num_adc_nids - 1;
- } else {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- alc_pcm_null_stream;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0;
- }
- }
-
- return 0;
-}
-
-static inline void alc_shutup(struct hda_codec *codec)
-{
- snd_hda_shutup_pins(codec);
-}
-
-static void alc_free_kctls(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- if (spec->kctls.list) {
- struct snd_kcontrol_new *kctl = spec->kctls.list;
- int i;
- for (i = 0; i < spec->kctls.used; i++)
- kfree(kctl[i].name);
- }
- snd_array_free(&spec->kctls);
-}
-
-static void alc_free(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- if (!spec)
- return;
-
- alc_shutup(codec);
- alc_free_kctls(codec);
- kfree(spec);
- snd_hda_detach_beep_device(codec);
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static void alc_power_eapd(struct hda_codec *codec)
-{
- /* We currently only handle front, HP */
- switch (codec->vendor_id) {
- case 0x10ec0260:
- set_eapd(codec, 0x0f, 0);
- set_eapd(codec, 0x10, 0);
- break;
- case 0x10ec0262:
- case 0x10ec0267:
- case 0x10ec0268:
- case 0x10ec0269:
- case 0x10ec0270:
- case 0x10ec0272:
- case 0x10ec0660:
- case 0x10ec0662:
- case 0x10ec0663:
- case 0x10ec0862:
- case 0x10ec0889:
- set_eapd(codec, 0x14, 0);
- set_eapd(codec, 0x15, 0);
- break;
- }
-}
-
-static int alc_suspend(struct hda_codec *codec, pm_message_t state)
-{
- struct alc_spec *spec = codec->spec;
- alc_shutup(codec);
- if (spec && spec->power_hook)
- spec->power_hook(codec);
- return 0;
-}
-#endif
-
-#ifdef SND_HDA_NEEDS_RESUME
-static int alc_resume(struct hda_codec *codec)
-{
- codec->patch_ops.init(codec);
- snd_hda_codec_resume_amp(codec);
- snd_hda_codec_resume_cache(codec);
- hda_call_check_power_status(codec, 0x01);
- return 0;
-}
-#endif
-
-/*
- */
-static struct hda_codec_ops alc_patch_ops = {
- .build_controls = alc_build_controls,
- .build_pcms = alc_build_pcms,
- .init = alc_init,
- .free = alc_free,
- .unsol_event = alc_unsol_event,
-#ifdef SND_HDA_NEEDS_RESUME
- .resume = alc_resume,
-#endif
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .suspend = alc_suspend,
- .check_power_status = alc_check_power_status,
-#endif
- .reboot_notify = alc_shutup,
-};
-
-/* replace the codec chip_name with the given string */
-static int alc_codec_rename(struct hda_codec *codec, const char *name)
-{
- kfree(codec->chip_name);
- codec->chip_name = kstrdup(name, GFP_KERNEL);
- if (!codec->chip_name) {
- alc_free(codec);
- return -ENOMEM;
- }
- return 0;
-}
-
-/*
- * Test configuration for debugging
- *
- * Almost all inputs/outputs are enabled. I/O pins can be configured via
- * enum controls.
- */
-#ifdef CONFIG_SND_DEBUG
-static hda_nid_t alc880_test_dac_nids[4] = {
- 0x02, 0x03, 0x04, 0x05
-};
-
-static struct hda_input_mux alc880_test_capture_source = {
- .num_items = 7,
- .items = {
- { "In-1", 0x0 },
- { "In-2", 0x1 },
- { "In-3", 0x2 },
- { "In-4", 0x3 },
- { "CD", 0x4 },
- { "Front", 0x5 },
- { "Surround", 0x6 },
- },
-};
-
-static struct hda_channel_mode alc880_test_modes[4] = {
- { 2, NULL },
- { 4, NULL },
- { 6, NULL },
- { 8, NULL },
-};
-
-static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[] = {
- "N/A", "Line Out", "HP Out",
- "In Hi-Z", "In 50%", "In Grd", "In 80%", "In 100%"
- };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 8;
- if (uinfo->value.enumerated.item >= 8)
- uinfo->value.enumerated.item = 7;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
-
-static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
- unsigned int pin_ctl, item = 0;
-
- pin_ctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- if (pin_ctl & AC_PINCTL_OUT_EN) {
- if (pin_ctl & AC_PINCTL_HP_EN)
- item = 2;
- else
- item = 1;
- } else if (pin_ctl & AC_PINCTL_IN_EN) {
- switch (pin_ctl & AC_PINCTL_VREFEN) {
- case AC_PINCTL_VREF_HIZ: item = 3; break;
- case AC_PINCTL_VREF_50: item = 4; break;
- case AC_PINCTL_VREF_GRD: item = 5; break;
- case AC_PINCTL_VREF_80: item = 6; break;
- case AC_PINCTL_VREF_100: item = 7; break;
- }
- }
- ucontrol->value.enumerated.item[0] = item;
- return 0;
-}
-
-static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
- static unsigned int ctls[] = {
- 0, AC_PINCTL_OUT_EN, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN,
- AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ,
- AC_PINCTL_IN_EN | AC_PINCTL_VREF_50,
- AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD,
- AC_PINCTL_IN_EN | AC_PINCTL_VREF_80,
- AC_PINCTL_IN_EN | AC_PINCTL_VREF_100,
- };
- unsigned int old_ctl, new_ctl;
-
- old_ctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- new_ctl = ctls[ucontrol->value.enumerated.item[0]];
- if (old_ctl != new_ctl) {
- int val;
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- new_ctl);
- val = ucontrol->value.enumerated.item[0] >= 3 ?
- HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, val);
- return 1;
- }
- return 0;
-}
-
-static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[] = {
- "Front", "Surround", "CLFE", "Side"
- };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item >= 4)
- uinfo->value.enumerated.item = 3;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
-}
-
-static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
- unsigned int sel;
-
- sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0);
- ucontrol->value.enumerated.item[0] = sel & 3;
- return 0;
-}
-
-static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
- unsigned int sel;
-
- sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0) & 3;
- if (ucontrol->value.enumerated.item[0] != sel) {
- sel = ucontrol->value.enumerated.item[0] & 3;
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, sel);
- return 1;
- }
- return 0;
-}
-
-#define PIN_CTL_TEST(xname,nid) { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
- .info = alc_test_pin_ctl_info, \
- .get = alc_test_pin_ctl_get, \
- .put = alc_test_pin_ctl_put, \
- .private_value = nid \
- }
-
-#define PIN_SRC_TEST(xname,nid) { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
- .info = alc_test_pin_src_info, \
- .get = alc_test_pin_src_get, \
- .put = alc_test_pin_src_put, \
- .private_value = nid \
- }
-
-static struct snd_kcontrol_new alc880_test_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CLFE Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_BIND_MUTE("CLFE Playback Switch", 0x0e, 2, HDA_INPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- PIN_CTL_TEST("Front Pin Mode", 0x14),
- PIN_CTL_TEST("Surround Pin Mode", 0x15),
- PIN_CTL_TEST("CLFE Pin Mode", 0x16),
- PIN_CTL_TEST("Side Pin Mode", 0x17),
- PIN_CTL_TEST("In-1 Pin Mode", 0x18),
- PIN_CTL_TEST("In-2 Pin Mode", 0x19),
- PIN_CTL_TEST("In-3 Pin Mode", 0x1a),
- PIN_CTL_TEST("In-4 Pin Mode", 0x1b),
- PIN_SRC_TEST("In-1 Pin Source", 0x18),
- PIN_SRC_TEST("In-2 Pin Source", 0x19),
- PIN_SRC_TEST("In-3 Pin Source", 0x1a),
- PIN_SRC_TEST("In-4 Pin Source", 0x1b),
- HDA_CODEC_VOLUME("In-1 Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("In-1 Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("In-2 Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("In-2 Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("In-3 Playback Volume", 0x0b, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("In-3 Playback Switch", 0x0b, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("In-4 Playback Volume", 0x0b, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("In-4 Playback Switch", 0x0b, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x4, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-static struct hda_verb alc880_test_init_verbs[] = {
- /* Unmute inputs of 0x0c - 0x0f */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Vol output for 0x0c-0x0f */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* Set output pins 0x14-0x17 */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Unmute output pins 0x14-0x17 */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Set input pins 0x18-0x1c */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Mute input pins 0x18-0x1b */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* ADC set up */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Analog input/passthru */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- { }
-};
-#endif
-
-/*
- */
-
-static const char *alc880_models[ALC880_MODEL_LAST] = {
- [ALC880_3ST] = "3stack",
- [ALC880_TCL_S700] = "tcl",
- [ALC880_3ST_DIG] = "3stack-digout",
- [ALC880_CLEVO] = "clevo",
- [ALC880_5ST] = "5stack",
- [ALC880_5ST_DIG] = "5stack-digout",
- [ALC880_W810] = "w810",
- [ALC880_Z71V] = "z71v",
- [ALC880_6ST] = "6stack",
- [ALC880_6ST_DIG] = "6stack-digout",
- [ALC880_ASUS] = "asus",
- [ALC880_ASUS_W1V] = "asus-w1v",
- [ALC880_ASUS_DIG] = "asus-dig",
- [ALC880_ASUS_DIG2] = "asus-dig2",
- [ALC880_UNIWILL_DIG] = "uniwill",
- [ALC880_UNIWILL_P53] = "uniwill-p53",
- [ALC880_FUJITSU] = "fujitsu",
- [ALC880_F1734] = "F1734",
- [ALC880_LG] = "lg",
- [ALC880_LG_LW] = "lg-lw",
- [ALC880_MEDION_RIM] = "medion",
-#ifdef CONFIG_SND_DEBUG
- [ALC880_TEST] = "test",
-#endif
- [ALC880_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc880_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810),
- SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_6ST),
- SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_3ST),
- SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x103c, 0x2a09, "HP", ALC880_5ST),
- SND_PCI_QUIRK(0x1043, 0x10b3, "ASUS W1V", ALC880_ASUS_W1V),
- SND_PCI_QUIRK(0x1043, 0x10c2, "ASUS W6A", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS Wxx", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x1113, "ASUS", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x1123, "ASUS", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x1173, "ASUS", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x1964, "ASUS Z71V", ALC880_Z71V),
- /* SND_PCI_QUIRK(0x1043, 0x1964, "ASUS", ALC880_ASUS_DIG), */
- SND_PCI_QUIRK(0x1043, 0x1973, "ASUS", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x814e, "ASUS P5GD1 w/SPDIF", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG),
- SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST),
- SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST),
- SND_PCI_QUIRK_VENDOR(0x1043, "ASUS", ALC880_ASUS), /* default ASUS */
- SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_3ST),
- SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST),
- SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST),
- SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_5ST),
- SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_5ST),
- SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO),
- SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO),
- SND_PCI_QUIRK(0x1558, 0x5401, "ASUS", ALC880_ASUS_DIG2),
- SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_UNIWILL_DIG),
- SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734),
- SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL),
- SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53),
- SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810),
- SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_MEDION_RIM),
- SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734),
- SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FUJITSU),
- SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_F1734),
- SND_PCI_QUIRK(0x1734, 0x10b0, "Fujitsu", ALC880_FUJITSU),
- SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW),
- SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_LG),
- SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_LG),
- SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_LG_LW),
- SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700),
- SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG), /* broken BIOS */
- SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_5ST_DIG),
- /* default Intel */
- SND_PCI_QUIRK_VENDOR(0x8086, "Intel mobo", ALC880_3ST),
- SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG),
- {}
-};
-
-/*
- * ALC880 codec presets
- */
-static struct alc_config_preset alc880_presets[] = {
- [ALC880_3ST] = {
- .mixers = { alc880_three_stack_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_3stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_3ST_DIG] = {
- .mixers = { alc880_three_stack_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_3stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_TCL_S700] = {
- .mixers = { alc880_tcl_s700_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_tcl_S700_init_verbs,
- alc880_gpio2_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .adc_nids = alc880_adc_nids_alt, /* FIXME: correct? */
- .num_adc_nids = 1, /* single ADC */
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
- .channel_mode = alc880_2_jack_modes,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_5ST] = {
- .mixers = { alc880_three_stack_mixer,
- alc880_five_stack_mixer},
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_5stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes),
- .channel_mode = alc880_fivestack_modes,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_5ST_DIG] = {
- .mixers = { alc880_three_stack_mixer,
- alc880_five_stack_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_5stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes),
- .channel_mode = alc880_fivestack_modes,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_6ST] = {
- .mixers = { alc880_six_stack_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_6stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids),
- .dac_nids = alc880_6st_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes),
- .channel_mode = alc880_sixstack_modes,
- .input_mux = &alc880_6stack_capture_source,
- },
- [ALC880_6ST_DIG] = {
- .mixers = { alc880_six_stack_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_6stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids),
- .dac_nids = alc880_6st_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes),
- .channel_mode = alc880_sixstack_modes,
- .input_mux = &alc880_6stack_capture_source,
- },
- [ALC880_W810] = {
- .mixers = { alc880_w810_base_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_w810_init_verbs,
- alc880_gpio2_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_w810_dac_nids),
- .dac_nids = alc880_w810_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_w810_modes),
- .channel_mode = alc880_w810_modes,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_Z71V] = {
- .mixers = { alc880_z71v_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_z71v_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_z71v_dac_nids),
- .dac_nids = alc880_z71v_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
- .channel_mode = alc880_2_jack_modes,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_F1734] = {
- .mixers = { alc880_f1734_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_f1734_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_f1734_dac_nids),
- .dac_nids = alc880_f1734_dac_nids,
- .hp_nid = 0x02,
- .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
- .channel_mode = alc880_2_jack_modes,
- .input_mux = &alc880_f1734_capture_source,
- .unsol_event = alc880_uniwill_p53_unsol_event,
- .setup = alc880_uniwill_p53_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC880_ASUS] = {
- .mixers = { alc880_asus_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_asus_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
- .channel_mode = alc880_asus_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_ASUS_DIG] = {
- .mixers = { alc880_asus_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_asus_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
- .channel_mode = alc880_asus_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_ASUS_DIG2] = {
- .mixers = { alc880_asus_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_asus_init_verbs,
- alc880_gpio2_init_verbs }, /* use GPIO2 */
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
- .channel_mode = alc880_asus_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_ASUS_W1V] = {
- .mixers = { alc880_asus_mixer, alc880_asus_w1v_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_asus_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
- .channel_mode = alc880_asus_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_UNIWILL_DIG] = {
- .mixers = { alc880_asus_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_asus_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
- .channel_mode = alc880_asus_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_UNIWILL] = {
- .mixers = { alc880_uniwill_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_uniwill_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- .unsol_event = alc880_uniwill_unsol_event,
- .setup = alc880_uniwill_setup,
- .init_hook = alc880_uniwill_init_hook,
- },
- [ALC880_UNIWILL_P53] = {
- .mixers = { alc880_uniwill_p53_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_uniwill_p53_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
- .dac_nids = alc880_asus_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_w810_modes),
- .channel_mode = alc880_threestack_modes,
- .input_mux = &alc880_capture_source,
- .unsol_event = alc880_uniwill_p53_unsol_event,
- .setup = alc880_uniwill_p53_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC880_FUJITSU] = {
- .mixers = { alc880_fujitsu_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_uniwill_p53_init_verbs,
- alc880_beep_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
- .channel_mode = alc880_2_jack_modes,
- .input_mux = &alc880_capture_source,
- .unsol_event = alc880_uniwill_p53_unsol_event,
- .setup = alc880_uniwill_p53_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC880_CLEVO] = {
- .mixers = { alc880_three_stack_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_pin_clevo_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_capture_source,
- },
- [ALC880_LG] = {
- .mixers = { alc880_lg_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_lg_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_lg_dac_nids),
- .dac_nids = alc880_lg_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_lg_ch_modes),
- .channel_mode = alc880_lg_ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc880_lg_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc880_lg_setup,
- .init_hook = alc_automute_amp,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .loopbacks = alc880_lg_loopbacks,
-#endif
- },
- [ALC880_LG_LW] = {
- .mixers = { alc880_lg_lw_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_lg_lw_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_lg_lw_modes),
- .channel_mode = alc880_lg_lw_modes,
- .input_mux = &alc880_lg_lw_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc880_lg_lw_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC880_MEDION_RIM] = {
- .mixers = { alc880_medion_rim_mixer },
- .init_verbs = { alc880_volume_init_verbs,
- alc880_medion_rim_init_verbs,
- alc_gpio2_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_dac_nids),
- .dac_nids = alc880_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
- .channel_mode = alc880_2_jack_modes,
- .input_mux = &alc880_medion_rim_capture_source,
- .unsol_event = alc880_medion_rim_unsol_event,
- .setup = alc880_medion_rim_setup,
- .init_hook = alc880_medion_rim_automute,
- },
-#ifdef CONFIG_SND_DEBUG
- [ALC880_TEST] = {
- .mixers = { alc880_test_mixer },
- .init_verbs = { alc880_test_init_verbs },
- .num_dacs = ARRAY_SIZE(alc880_test_dac_nids),
- .dac_nids = alc880_test_dac_nids,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_test_modes),
- .channel_mode = alc880_test_modes,
- .input_mux = &alc880_test_capture_source,
- },
-#endif
-};
-
-/*
- * Automatic parse of I/O pins from the BIOS configuration
- */
-
-enum {
- ALC_CTL_WIDGET_VOL,
- ALC_CTL_WIDGET_MUTE,
- ALC_CTL_BIND_MUTE,
-};
-static struct snd_kcontrol_new alc880_control_templates[] = {
- HDA_CODEC_VOLUME(NULL, 0, 0, 0),
- HDA_CODEC_MUTE(NULL, 0, 0, 0),
- HDA_BIND_MUTE(NULL, 0, 0, 0),
-};
-
-/* add dynamic controls */
-static int add_control(struct alc_spec *spec, int type, const char *name,
- int cidx, unsigned long val)
-{
- struct snd_kcontrol_new *knew;
-
- snd_array_init(&spec->kctls, sizeof(*knew), 32);
- knew = snd_array_new(&spec->kctls);
- if (!knew)
- return -ENOMEM;
- *knew = alc880_control_templates[type];
- knew->name = kstrdup(name, GFP_KERNEL);
- if (!knew->name)
- return -ENOMEM;
- knew->index = cidx;
- if (get_amp_nid_(val))
- knew->subdevice = HDA_SUBDEV_AMP_FLAG;
- knew->private_value = val;
- return 0;
-}
-
-static int add_control_with_pfx(struct alc_spec *spec, int type,
- const char *pfx, const char *dir,
- const char *sfx, int cidx, unsigned long val)
-{
- char name[32];
- snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
- return add_control(spec, type, name, cidx, val);
-}
-
-#define add_pb_vol_ctrl(spec, type, pfx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val)
-#define add_pb_sw_ctrl(spec, type, pfx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val)
-#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val)
-#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val)
-
-#define alc880_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17)
-#define alc880_fixed_pin_idx(nid) ((nid) - 0x14)
-#define alc880_is_multi_pin(nid) ((nid) >= 0x18)
-#define alc880_multi_pin_idx(nid) ((nid) - 0x18)
-#define alc880_idx_to_dac(nid) ((nid) + 0x02)
-#define alc880_dac_to_idx(nid) ((nid) - 0x02)
-#define alc880_idx_to_mixer(nid) ((nid) + 0x0c)
-#define alc880_idx_to_selector(nid) ((nid) + 0x10)
-#define ALC880_PIN_CD_NID 0x1c
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int alc880_auto_fill_dac_nids(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- hda_nid_t nid;
- int assigned[4];
- int i, j;
-
- memset(assigned, 0, sizeof(assigned));
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- /* check the pins hardwired to audio widget */
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- if (alc880_is_fixed_pin(nid)) {
- int idx = alc880_fixed_pin_idx(nid);
- spec->multiout.dac_nids[i] = alc880_idx_to_dac(idx);
- assigned[idx] = 1;
- }
- }
- /* left pins can be connect to any audio widget */
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- if (alc880_is_fixed_pin(nid))
- continue;
- /* search for an empty channel */
- for (j = 0; j < cfg->line_outs; j++) {
- if (!assigned[j]) {
- spec->multiout.dac_nids[i] =
- alc880_idx_to_dac(j);
- assigned[j] = 1;
- break;
- }
- }
- }
- spec->multiout.num_dacs = cfg->line_outs;
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- static const char *chname[4] = {
- "Front", "Surround", NULL /*CLFE*/, "Side"
- };
- hda_nid_t nid;
- int i, err;
-
- for (i = 0; i < cfg->line_outs; i++) {
- if (!spec->multiout.dac_nids[i])
- continue;
- nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i]));
- if (i == 2) {
- /* Center/LFE */
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
- "Center",
- HDA_COMPOSE_AMP_VAL(nid, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
- "LFE",
- HDA_COMPOSE_AMP_VAL(nid, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
- "Center",
- HDA_COMPOSE_AMP_VAL(nid, 1, 2,
- HDA_INPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
- "LFE",
- HDA_COMPOSE_AMP_VAL(nid, 2, 2,
- HDA_INPUT));
- if (err < 0)
- return err;
- } else {
- const char *pfx;
- if (cfg->line_outs == 1 &&
- cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- pfx = "Speaker";
- else
- pfx = chname[i];
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(nid, 3, 2,
- HDA_INPUT));
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-/* add playback controls for speaker and HP outputs */
-static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
- const char *pfx)
-{
- hda_nid_t nid;
- int err;
-
- if (!pin)
- return 0;
-
- if (alc880_is_fixed_pin(pin)) {
- nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin));
- /* specify the DAC as the extra output */
- if (!spec->multiout.hp_nid)
- spec->multiout.hp_nid = nid;
- else
- spec->multiout.extra_out_nid[0] = nid;
- /* control HP volume/switch on the output mixer amp */
- nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin));
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
- if (err < 0)
- return err;
- } else if (alc880_is_multi_pin(pin)) {
- /* set manual connection */
- /* we have only a switch on HP-out PIN */
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-/* create input playback/capture controls for the given pin */
-static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
- const char *ctlname, int ctlidx,
- int idx, hda_nid_t mix_nid)
-{
- int err;
-
- err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx,
- HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
- if (err < 0)
- return err;
- err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx,
- HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
- if (err < 0)
- return err;
- return 0;
-}
-
-static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
- return (pincap & AC_PINCAP_IN) != 0;
-}
-
-/* create playback/capture controls for input pins */
-static int alc_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg,
- hda_nid_t mixer,
- hda_nid_t cap1, hda_nid_t cap2)
-{
- struct alc_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx, type, type_idx = 0;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t pin;
- const char *label;
-
- pin = cfg->inputs[i].pin;
- if (!alc_is_input_pin(codec, pin))
- continue;
-
- type = cfg->inputs[i].type;
- if (i > 0 && type == cfg->inputs[i - 1].type)
- type_idx++;
- else
- type_idx = 0;
- label = hda_get_autocfg_input_label(codec, cfg, i);
- if (mixer) {
- idx = get_connection_index(codec, mixer, pin);
- if (idx >= 0) {
- err = new_analog_input(spec, pin,
- label, type_idx,
- idx, mixer);
- if (err < 0)
- return err;
- }
- }
-
- if (!cap1)
- continue;
- idx = get_connection_index(codec, cap1, pin);
- if (idx < 0 && cap2)
- idx = get_connection_index(codec, cap2, pin);
- if (idx >= 0)
- snd_hda_add_imux_item(imux, label, idx, NULL);
- }
- return 0;
-}
-
-static int alc880_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x08, 0x09);
-}
-
-static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid,
- unsigned int pin_type)
-{
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_type);
- /* unmute pin */
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
-}
-
-static void alc880_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- int dac_idx)
-{
- alc_set_pin_output(codec, nid, pin_type);
- /* need the manual connection? */
- if (alc880_is_multi_pin(nid)) {
- struct alc_spec *spec = codec->spec;
- int idx = alc880_multi_pin_idx(nid);
- snd_hda_codec_write(codec, alc880_idx_to_selector(idx), 0,
- AC_VERB_SET_CONNECT_SEL,
- alc880_dac_to_idx(spec->multiout.dac_nids[dac_idx]));
- }
-}
-
-static int get_pin_type(int line_out_type)
-{
- if (line_out_type == AUTO_PIN_HP_OUT)
- return PIN_HP;
- else
- return PIN_OUT;
-}
-
-static void alc880_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.line_outs; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- alc880_auto_set_output_and_unmute(codec, nid, pin_type, i);
- }
-}
-
-static void alc880_auto_init_extra_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t pin;
-
- pin = spec->autocfg.speaker_pins[0];
- if (pin) /* connect to front */
- alc880_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
- pin = spec->autocfg.hp_pins[0];
- if (pin) /* connect to front */
- alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
-}
-
-static void alc880_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (alc_is_input_pin(codec, nid)) {
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (nid != ALC880_PIN_CD_NID &&
- (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
- }
-}
-
-static void alc880_auto_init_input_src(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int c;
-
- for (c = 0; c < spec->num_adc_nids; c++) {
- unsigned int mux_idx;
- const struct hda_input_mux *imux;
- mux_idx = c >= spec->num_mux_defs ? 0 : c;
- imux = &spec->input_mux[mux_idx];
- if (!imux->num_items && mux_idx > 0)
- imux = &spec->input_mux[0];
- if (imux)
- snd_hda_codec_write(codec, spec->adc_nids[c], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[0].index);
- }
-}
-
-/* parse the BIOS configuration and set up the alc_spec */
-/* return 1 if successful, 0 if the proper config is not found,
- * or a negative error code
- */
-static int alc880_parse_auto_config(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc880_ignore[] = { 0x1d, 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc880_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs)
- return 0; /* can't find valid BIOS pin config */
-
- err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc880_auto_create_extra_out(spec,
- spec->autocfg.speaker_pins[0],
- "Speaker");
- if (err < 0)
- return err;
- err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0],
- "Headphone");
- if (err < 0)
- return err;
- err = alc880_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc880_volume_init_verbs);
-
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
- return 1;
-}
-
-/* additional initialization for auto-configuration model */
-static void alc880_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc880_auto_init_multi_out(codec);
- alc880_auto_init_extra_out(codec);
- alc880_auto_init_analog_input(codec);
- alc880_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-/* check the ADC/MUX contains all input pins; some ADC/MUX contains only
- * one of two digital mic pins, e.g. on ALC272
- */
-static void fixup_automic_adc(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_adc_nids; i++) {
- hda_nid_t cap = spec->capsrc_nids ?
- spec->capsrc_nids[i] : spec->adc_nids[i];
- int iidx, eidx;
-
- iidx = get_connection_index(codec, cap, spec->int_mic.pin);
- if (iidx < 0)
- continue;
- eidx = get_connection_index(codec, cap, spec->ext_mic.pin);
- if (eidx < 0)
- continue;
- spec->int_mic.mux_idx = iidx;
- spec->ext_mic.mux_idx = eidx;
- if (spec->capsrc_nids)
- spec->capsrc_nids += i;
- spec->adc_nids += i;
- spec->num_adc_nids = 1;
- return;
- }
- snd_printd(KERN_INFO "hda_codec: %s: "
- "No ADC/MUX containing both 0x%x and 0x%x pins\n",
- codec->chip_name, spec->int_mic.pin, spec->ext_mic.pin);
- spec->auto_mic = 0; /* disable auto-mic to be sure */
-}
-
-/* select or unmute the given capsrc route */
-static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap,
- int idx)
-{
- if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) {
- snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx,
- HDA_AMP_MUTE, 0);
- } else {
- snd_hda_codec_write_cache(codec, cap, 0,
- AC_VERB_SET_CONNECT_SEL, idx);
- }
-}
-
-/* set the default connection to that pin */
-static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_adc_nids; i++) {
- hda_nid_t cap = spec->capsrc_nids ?
- spec->capsrc_nids[i] : spec->adc_nids[i];
- int idx;
-
- idx = get_connection_index(codec, cap, pin);
- if (idx < 0)
- continue;
- select_or_unmute_capsrc(codec, cap, idx);
- return i; /* return the found index */
- }
- return -1; /* not found */
-}
-
-/* choose the ADC/MUX containing the input pin and initialize the setup */
-static void fixup_single_adc(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- /* search for the input pin; there must be only one */
- if (cfg->num_inputs != 1)
- return;
- i = init_capsrc_for_pin(codec, cfg->inputs[0].pin);
- if (i >= 0) {
- /* use only this ADC */
- if (spec->capsrc_nids)
- spec->capsrc_nids += i;
- spec->adc_nids += i;
- spec->num_adc_nids = 1;
- }
-}
-
-/* initialize dual adcs */
-static void fixup_dual_adc_switch(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- init_capsrc_for_pin(codec, spec->ext_mic.pin);
- init_capsrc_for_pin(codec, spec->int_mic.pin);
-}
-
-static void set_capture_mixer(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- static struct snd_kcontrol_new *caps[2][3] = {
- { alc_capture_mixer_nosrc1,
- alc_capture_mixer_nosrc2,
- alc_capture_mixer_nosrc3 },
- { alc_capture_mixer1,
- alc_capture_mixer2,
- alc_capture_mixer3 },
- };
- if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) {
- int mux = 0;
- int num_adcs = spec->num_adc_nids;
- if (spec->dual_adc_switch)
- fixup_dual_adc_switch(codec);
- else if (spec->auto_mic)
- fixup_automic_adc(codec);
- else if (spec->input_mux) {
- if (spec->input_mux->num_items > 1)
- mux = 1;
- else if (spec->input_mux->num_items == 1)
- fixup_single_adc(codec);
- }
- if (spec->dual_adc_switch)
- num_adcs = 1;
- spec->cap_mixer = caps[mux][num_adcs - 1];
- }
-}
-
-/* fill adc_nids (and capsrc_nids) containing all active input pins */
-static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
- int num_nids)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int n;
- hda_nid_t fallback_adc = 0, fallback_cap = 0;
-
- for (n = 0; n < num_nids; n++) {
- hda_nid_t adc, cap;
- hda_nid_t conn[HDA_MAX_NUM_INPUTS];
- int nconns, i, j;
-
- adc = nids[n];
- if (get_wcaps_type(get_wcaps(codec, adc)) != AC_WID_AUD_IN)
- continue;
- cap = adc;
- nconns = snd_hda_get_connections(codec, cap, conn,
- ARRAY_SIZE(conn));
- if (nconns == 1) {
- cap = conn[0];
- nconns = snd_hda_get_connections(codec, cap, conn,
- ARRAY_SIZE(conn));
- }
- if (nconns <= 0)
- continue;
- if (!fallback_adc) {
- fallback_adc = adc;
- fallback_cap = cap;
- }
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- for (j = 0; j < nconns; j++) {
- if (conn[j] == nid)
- break;
- }
- if (j >= nconns)
- break;
- }
- if (i >= cfg->num_inputs) {
- int num_adcs = spec->num_adc_nids;
- spec->private_adc_nids[num_adcs] = adc;
- spec->private_capsrc_nids[num_adcs] = cap;
- spec->num_adc_nids++;
- spec->adc_nids = spec->private_adc_nids;
- if (adc != cap)
- spec->capsrc_nids = spec->private_capsrc_nids;
- }
- }
- if (!spec->num_adc_nids) {
- printk(KERN_WARNING "hda_codec: %s: no valid ADC found;"
- " using fallback 0x%x\n",
- codec->chip_name, fallback_adc);
- spec->private_adc_nids[0] = fallback_adc;
- spec->adc_nids = spec->private_adc_nids;
- if (fallback_adc != fallback_cap) {
- spec->private_capsrc_nids[0] = fallback_cap;
- spec->capsrc_nids = spec->private_adc_nids;
- }
- }
-}
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-#define set_beep_amp(spec, nid, idx, dir) \
- ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir))
-
-static struct snd_pci_quirk beep_white_list[] = {
- SND_PCI_QUIRK(0x1043, 0x829f, "ASUS", 1),
- SND_PCI_QUIRK(0x1043, 0x83ce, "EeePC", 1),
- SND_PCI_QUIRK(0x8086, 0xd613, "Intel", 1),
- {}
-};
-
-static inline int has_cdefine_beep(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- const struct snd_pci_quirk *q;
- q = snd_pci_quirk_lookup(codec->bus->pci, beep_white_list);
- if (q)
- return q->value;
- return spec->cdefine.enable_pcbeep;
-}
-#else
-#define set_beep_amp(spec, nid, idx, dir) /* NOP */
-#define has_cdefine_beep(codec) 0
-#endif
-
-/*
- * OK, here we have finally the patch for ALC880
- */
-
-static int patch_alc880(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int board_config;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- board_config = snd_hda_check_board_config(codec, ALC880_MODEL_LAST,
- alc880_models,
- alc880_cfg_tbl);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC880_AUTO;
- }
-
- if (board_config == ALC880_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc880_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using 3-stack mode...\n");
- board_config = ALC880_3ST;
- }
- }
-
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
-
- if (board_config != ALC880_AUTO)
- setup_preset(codec, &alc880_presets[board_config]);
-
- spec->stream_analog_playback = &alc880_pcm_analog_playback;
- spec->stream_analog_capture = &alc880_pcm_analog_capture;
- spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
-
- spec->stream_digital_playback = &alc880_pcm_digital_playback;
- spec->stream_digital_capture = &alc880_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- /* check whether NID 0x07 is valid */
- unsigned int wcap = get_wcaps(codec, alc880_adc_nids[0]);
- /* get type */
- wcap = get_wcaps_type(wcap);
- if (wcap != AC_WID_AUD_IN) {
- spec->adc_nids = alc880_adc_nids_alt;
- spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt);
- } else {
- spec->adc_nids = alc880_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
- }
- }
- set_capture_mixer(codec);
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
-
- spec->vmaster_nid = 0x0c;
-
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC880_AUTO)
- spec->init_hook = alc880_auto_init;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc880_loopbacks;
-#endif
-
- return 0;
-}
-
-
-/*
- * ALC260 support
- */
-
-static hda_nid_t alc260_dac_nids[1] = {
- /* front */
- 0x02,
-};
-
-static hda_nid_t alc260_adc_nids[1] = {
- /* ADC0 */
- 0x04,
-};
-
-static hda_nid_t alc260_adc_nids_alt[1] = {
- /* ADC1 */
- 0x05,
-};
-
-/* NIDs used when simultaneous access to both ADCs makes sense. Note that
- * alc260_capture_mixer assumes ADC0 (nid 0x04) is the first ADC.
- */
-static hda_nid_t alc260_dual_adc_nids[2] = {
- /* ADC0, ADC1 */
- 0x04, 0x05
-};
-
-#define ALC260_DIGOUT_NID 0x03
-#define ALC260_DIGIN_NID 0x06
-
-static struct hda_input_mux alc260_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-/* On Fujitsu S702x laptops capture only makes sense from Mic/LineIn jack,
- * headphone jack and the internal CD lines since these are the only pins at
- * which audio can appear. For flexibility, also allow the option of
- * recording the mixer output on the second ADC (ADC0 doesn't have a
- * connection to the mixer output).
- */
-static struct hda_input_mux alc260_fujitsu_capture_sources[2] = {
- {
- .num_items = 3,
- .items = {
- { "Mic/Line", 0x0 },
- { "CD", 0x4 },
- { "Headphone", 0x2 },
- },
- },
- {
- .num_items = 4,
- .items = {
- { "Mic/Line", 0x0 },
- { "CD", 0x4 },
- { "Headphone", 0x2 },
- { "Mixer", 0x5 },
- },
- },
-
-};
-
-/* Acer TravelMate(/Extensa/Aspire) notebooks have similar configuration to
- * the Fujitsu S702x, but jacks are marked differently.
- */
-static struct hda_input_mux alc260_acer_capture_sources[2] = {
- {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Headphone", 0x5 },
- },
- },
- {
- .num_items = 5,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "Headphone", 0x6 },
- { "Mixer", 0x5 },
- },
- },
-};
-
-/* Maxdata Favorit 100XS */
-static struct hda_input_mux alc260_favorit100_capture_sources[2] = {
- {
- .num_items = 2,
- .items = {
- { "Line/Mic", 0x0 },
- { "CD", 0x4 },
- },
- },
- {
- .num_items = 3,
- .items = {
- { "Line/Mic", 0x0 },
- { "CD", 0x4 },
- { "Mixer", 0x5 },
- },
- },
-};
-
-/*
- * This is just place-holder, so there's something for alc_build_pcms to look
- * at when it calculates the maximum number of channels. ALC260 has no mixer
- * element which allows changing the channel mode, so the verb list is
- * never used.
- */
-static struct hda_channel_mode alc260_modes[1] = {
- { 2, NULL },
-};
-
-
-/* Mixer combinations
- *
- * basic: base_output + input + pc_beep + capture
- * HP: base_output + input + capture_alt
- * HP_3013: hp_3013 + input + capture
- * fujitsu: fujitsu + capture
- * acer: acer + capture
- */
-
-static struct snd_kcontrol_new alc260_base_output_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc260_input_mixer[] = {
- HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x07, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x07, 0x01, HDA_INPUT),
- { } /* end */
-};
-
-/* update HP, line and mono out pins according to the master switch */
-static void alc260_hp_master_update(struct hda_codec *codec,
- hda_nid_t hp, hda_nid_t line,
- hda_nid_t mono)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int val = spec->master_sw ? PIN_HP : 0;
- /* change HP and line-out pins */
- snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- val);
- snd_hda_codec_write(codec, line, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- val);
- /* mono (speaker) depending on the HP jack sense */
- val = (val && !spec->jack_present) ? PIN_OUT : 0;
- snd_hda_codec_write(codec, mono, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- val);
-}
-
-static int alc260_hp_master_sw_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- *ucontrol->value.integer.value = spec->master_sw;
- return 0;
-}
-
-static int alc260_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- int val = !!*ucontrol->value.integer.value;
- hda_nid_t hp, line, mono;
-
- if (val == spec->master_sw)
- return 0;
- spec->master_sw = val;
- hp = (kcontrol->private_value >> 16) & 0xff;
- line = (kcontrol->private_value >> 8) & 0xff;
- mono = kcontrol->private_value & 0xff;
- alc260_hp_master_update(codec, hp, line, mono);
- return 1;
-}
-
-static struct snd_kcontrol_new alc260_hp_output_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x11,
- .info = snd_ctl_boolean_mono_info,
- .get = alc260_hp_master_sw_get,
- .put = alc260_hp_master_sw_put,
- .private_value = (0x0f << 16) | (0x10 << 8) | 0x11
- },
- HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0,
- HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_verb alc260_hp_unsol_verbs[] = {
- {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {},
-};
-
-static void alc260_hp_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->jack_present = snd_hda_jack_detect(codec, 0x10);
- alc260_hp_master_update(codec, 0x0f, 0x10, 0x11);
-}
-
-static void alc260_hp_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc260_hp_automute(codec);
-}
-
-static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x11,
- .info = snd_ctl_boolean_mono_info,
- .get = alc260_hp_master_sw_get,
- .put = alc260_hp_master_sw_put,
- .private_value = (0x15 << 16) | (0x10 << 8) | 0x11
- },
- HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("Aux-In Playback Switch", 0x07, 0x06, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct hda_bind_ctls alc260_dc7600_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x0a, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct hda_bind_ctls alc260_dc7600_bind_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x11, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc260_hp_dc7600_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc260_dc7600_bind_master_vol),
- HDA_BIND_SW("LineOut Playback Switch", &alc260_dc7600_bind_switch),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x0f, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x10, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct hda_verb alc260_hp_3013_unsol_verbs[] = {
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {},
-};
-
-static void alc260_hp_3013_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->jack_present = snd_hda_jack_detect(codec, 0x15);
- alc260_hp_master_update(codec, 0x15, 0x10, 0x11);
-}
-
-static void alc260_hp_3013_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc260_hp_3013_automute(codec);
-}
-
-static void alc260_hp_3012_automute(struct hda_codec *codec)
-{
- unsigned int bits = snd_hda_jack_detect(codec, 0x10) ? 0 : PIN_OUT;
-
- snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- bits);
- snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- bits);
- snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- bits);
-}
-
-static void alc260_hp_3012_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc260_hp_3012_automute(codec);
-}
-
-/* Fujitsu S702x series laptops. ALC260 pin usage: Mic/Line jack = 0x12,
- * HP jack = 0x14, CD audio = 0x16, internal speaker = 0x10.
- */
-static struct snd_kcontrol_new alc260_fujitsu_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x08, 2, HDA_INPUT),
- ALC_PIN_MODE("Headphone Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic/Line Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic/Line Playback Switch", 0x07, 0x0, HDA_INPUT),
- ALC_PIN_MODE("Mic/Line Jack Mode", 0x12, ALC_PIN_DIR_IN),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x09, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x09, 2, HDA_INPUT),
- { } /* end */
-};
-
-/* Mixer for Acer TravelMate(/Extensa/Aspire) notebooks. Note that current
- * versions of the ALC260 don't act on requests to enable mic bias from NID
- * 0x0f (used to drive the headphone jack in these laptops). The ALC260
- * datasheet doesn't mention this restriction. At this stage it's not clear
- * whether this behaviour is intentional or is a hardware bug in chip
- * revisions available in early 2006. Therefore for now allow the
- * "Headphone Jack Mode" control to span all choices, but if it turns out
- * that the lack of mic bias for this NID is intentional we could change the
- * mode from ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS.
- *
- * In addition, Acer TravelMate(/Extensa/Aspire) notebooks in early 2006
- * don't appear to make the mic bias available from the "line" jack, even
- * though the NID used for this jack (0x14) can supply it. The theory is
- * that perhaps Acer have included blocking capacitors between the ALC260
- * and the output jack. If this turns out to be the case for all such
- * models the "Line Jack Mode" mode could be changed from ALC_PIN_DIR_INOUT
- * to ALC_PIN_DIR_INOUT_NOMICBIAS.
- *
- * The C20x Tablet series have a mono internal speaker which is controlled
- * via the chip's Mono sum widget and pin complex, so include the necessary
- * controls for such models. On models without a "mono speaker" the control
- * won't do anything.
- */
-static struct snd_kcontrol_new alc260_acer_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT),
- ALC_PIN_MODE("Headphone Jack Mode", 0x0f, ALC_PIN_DIR_INOUT),
- HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0,
- HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2,
- HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
- ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
- ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
- { } /* end */
-};
-
-/* Maxdata Favorit 100XS: one output and one input (0x12) jack
- */
-static struct snd_kcontrol_new alc260_favorit100_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT),
- ALC_PIN_MODE("Output Jack Mode", 0x0f, ALC_PIN_DIR_INOUT),
- HDA_CODEC_VOLUME("Line/Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Line/Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
- ALC_PIN_MODE("Line/Mic Jack Mode", 0x12, ALC_PIN_DIR_IN),
- { } /* end */
-};
-
-/* Packard bell V7900 ALC260 pin usage: HP = 0x0f, Mic jack = 0x12,
- * Line In jack = 0x14, CD audio = 0x16, pc beep = 0x17.
- */
-static struct snd_kcontrol_new alc260_will_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x08, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
- ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
- ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
- { } /* end */
-};
-
-/* Replacer 672V ALC260 pin usage: Mic jack = 0x12,
- * Line In jack = 0x14, ATAPI Mic = 0x13, speaker = 0x0f.
- */
-static struct snd_kcontrol_new alc260_replacer_672v_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x08, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
- ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN),
- HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x07, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("ATATI Mic Playback Switch", 0x07, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
- ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT),
- { } /* end */
-};
-
-/*
- * initialization verbs
- */
-static struct hda_verb alc260_init_verbs[] = {
- /* Line In pin widget for input */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* CD pin widget for input */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- /* Mic2 (front panel) pin widget for input and vref at 80% */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- /* LINE-2 is used for line-out in rear */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* select line-out */
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* LINE-OUT pin */
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* enable HP */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* enable Mono */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* mute capture amp left and right */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* set connection select to line in (default select for this ADC) */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* mute capture amp left and right */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* set connection select to line in (default select for this ADC) */
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* set vol=0 Line-Out mixer amp left and right */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* unmute pin widget amp left and right (no gain on this amp) */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* set vol=0 HP mixer amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* unmute pin widget amp left and right (no gain on this amp) */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* set vol=0 Mono mixer amp left and right */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* unmute pin widget amp left and right (no gain on this amp) */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* unmute LINE-2 out pin */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 &
- * Line In 2 = 0x03
- */
- /* mute analog inputs */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
- /* mute Front out path */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* mute Headphone out path */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* mute Mono out path */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- { }
-};
-
-#if 0 /* should be identical with alc260_init_verbs? */
-static struct hda_verb alc260_hp_init_verbs[] = {
- /* Headphone and output */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- /* mono output */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- /* Mic2 (front panel) pin widget for input and vref at 80% */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- /* Line In pin widget for input */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- /* Line-2 pin widget for output */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* CD pin widget for input */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- /* unmute amp left and right */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
- /* set connection select to line in (default select for this ADC) */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* unmute Line-Out mixer amp left and right (volume = 0) */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* mute pin widget amp left and right (no gain on this amp) */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- /* unmute HP mixer amp left and right (volume = 0) */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* mute pin widget amp left and right (no gain on this amp) */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 &
- * Line In 2 = 0x03
- */
- /* mute analog inputs */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
- /* Unmute Front out path */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- /* Unmute Headphone out path */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- /* Unmute Mono out path */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- { }
-};
-#endif
-
-static struct hda_verb alc260_hp_3013_init_verbs[] = {
- /* Line out and output */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* mono output */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* Mic1 (rear panel) pin widget for input and vref at 80% */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- /* Mic2 (front panel) pin widget for input and vref at 80% */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- /* Line In pin widget for input */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- /* Headphone pin widget for output */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- /* CD pin widget for input */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- /* unmute amp left and right */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
- /* set connection select to line in (default select for this ADC) */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* unmute Line-Out mixer amp left and right (volume = 0) */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* mute pin widget amp left and right (no gain on this amp) */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- /* unmute HP mixer amp left and right (volume = 0) */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* mute pin widget amp left and right (no gain on this amp) */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 &
- * Line In 2 = 0x03
- */
- /* mute analog inputs */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
- /* Unmute Front out path */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- /* Unmute Headphone out path */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- /* Unmute Mono out path */
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- { }
-};
-
-/* Initialisation sequence for ALC260 as configured in Fujitsu S702x
- * laptops. ALC260 pin usage: Mic/Line jack = 0x12, HP jack = 0x14, CD
- * audio = 0x16, internal speaker = 0x10.
- */
-static struct hda_verb alc260_fujitsu_init_verbs[] = {
- /* Disable all GPIOs */
- {0x01, AC_VERB_SET_GPIO_MASK, 0},
- /* Internal speaker is connected to headphone pin */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Headphone/Line-out jack connects to Line1 pin; make it an output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- /* Mic/Line-in jack is connected to mic1 pin, so make it an input */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Ensure all other unused pins are disabled and muted. */
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-
- /* Disable digital (SPDIF) pins */
- {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
- {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
-
- /* Ensure Line1 pin widget takes its input from the OUT1 sum bus
- * when acting as an output.
- */
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* Start with output sum widgets muted and their output gains at min */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Unmute HP pin widget amp left and right (no equiv mixer ctrl) */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Unmute Line1 pin widget output buffer since it starts as an output.
- * If the pin mode is changed by the user the pin mode control will
- * take care of enabling the pin's input/output buffers as needed.
- * Therefore there's no need to enable the input buffer at this
- * stage.
- */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Unmute input buffer of pin widget used for Line-in (no equiv
- * mixer ctrl)
- */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute capture amp left and right */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- /* Set ADC connection select to match default mixer setting - line
- * in (on mic1 pin)
- */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Do the same for the second ADC: mute capture input amp and
- * set ADC connection to line in (on mic1 pin)
- */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mute all inputs to mixer widget (even unconnected ones) */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
-
- { }
-};
-
-/* Initialisation sequence for ALC260 as configured in Acer TravelMate and
- * similar laptops (adapted from Fujitsu init verbs).
- */
-static struct hda_verb alc260_acer_init_verbs[] = {
- /* On TravelMate laptops, GPIO 0 enables the internal speaker and
- * the headphone jack. Turn this on and rely on the standard mute
- * methods whenever the user wants to turn these outputs off.
- */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x01},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
- /* Internal speaker/Headphone jack is connected to Line-out pin */
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Internal microphone/Mic jack is connected to Mic1 pin */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
- /* Line In jack is connected to Line1 pin */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- /* Some Acers (eg: C20x Tablets) use Mono pin for internal speaker */
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Ensure all other unused pins are disabled and muted. */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- /* Disable digital (SPDIF) pins */
- {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
- {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
-
- /* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum
- * bus when acting as outputs.
- */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* Start with output sum widgets muted and their output gains at min */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Unmute Line-out pin widget amp left and right
- * (no equiv mixer ctrl)
- */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Unmute mono pin widget amp output (no equiv mixer ctrl) */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Unmute Mic1 and Line1 pin widget input buffers since they start as
- * inputs. If the pin mode is changed by the user the pin mode control
- * will take care of enabling the pin's input/output buffers as needed.
- * Therefore there's no need to enable the input buffer at this
- * stage.
- */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute capture amp left and right */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- /* Set ADC connection select to match default mixer setting - mic
- * (on mic1 pin)
- */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Do similar with the second ADC: mute capture input amp and
- * set ADC connection to mic to match ALSA's default state.
- */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mute all inputs to mixer widget (even unconnected ones) */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
-
- { }
-};
-
-/* Initialisation sequence for Maxdata Favorit 100XS
- * (adapted from Acer init verbs).
- */
-static struct hda_verb alc260_favorit100_init_verbs[] = {
- /* GPIO 0 enables the output jack.
- * Turn this on and rely on the standard mute
- * methods whenever the user wants to turn these outputs off.
- */
- {0x01, AC_VERB_SET_GPIO_MASK, 0x01},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
- /* Line/Mic input jack is connected to Mic1 pin */
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
- /* Ensure all other unused pins are disabled and muted. */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- /* Disable digital (SPDIF) pins */
- {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
- {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
-
- /* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum
- * bus when acting as outputs.
- */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* Start with output sum widgets muted and their output gains at min */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Unmute Line-out pin widget amp left and right
- * (no equiv mixer ctrl)
- */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Unmute Mic1 and Line1 pin widget input buffers since they start as
- * inputs. If the pin mode is changed by the user the pin mode control
- * will take care of enabling the pin's input/output buffers as needed.
- * Therefore there's no need to enable the input buffer at this
- * stage.
- */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute capture amp left and right */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- /* Set ADC connection select to match default mixer setting - mic
- * (on mic1 pin)
- */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Do similar with the second ADC: mute capture input amp and
- * set ADC connection to mic to match ALSA's default state.
- */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mute all inputs to mixer widget (even unconnected ones) */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
-
- { }
-};
-
-static struct hda_verb alc260_will_verbs[] = {
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x0f, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- {0x1a, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x1a, AC_VERB_SET_PROC_COEF, 0x3040},
- {}
-};
-
-static struct hda_verb alc260_replacer_672v_verbs[] = {
- {0x0f, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- {0x1a, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x1a, AC_VERB_SET_PROC_COEF, 0x3050},
-
- {0x01, AC_VERB_SET_GPIO_MASK, 0x01},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x00},
-
- {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc260_replacer_672v_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- /* speaker --> GPIO Data 0, hp or spdif --> GPIO data 1 */
- present = snd_hda_jack_detect(codec, 0x0f);
- if (present) {
- snd_hda_codec_write_cache(codec, 0x01, 0,
- AC_VERB_SET_GPIO_DATA, 1);
- snd_hda_codec_write_cache(codec, 0x0f, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_HP);
- } else {
- snd_hda_codec_write_cache(codec, 0x01, 0,
- AC_VERB_SET_GPIO_DATA, 0);
- snd_hda_codec_write_cache(codec, 0x0f, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_OUT);
- }
-}
-
-static void alc260_replacer_672v_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc260_replacer_672v_automute(codec);
-}
-
-static struct hda_verb alc260_hp_dc7600_verbs[] = {
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-/* Test configuration for debugging, modelled after the ALC880 test
- * configuration.
- */
-#ifdef CONFIG_SND_DEBUG
-static hda_nid_t alc260_test_dac_nids[1] = {
- 0x02,
-};
-static hda_nid_t alc260_test_adc_nids[2] = {
- 0x04, 0x05,
-};
-/* For testing the ALC260, each input MUX needs its own definition since
- * the signal assignments are different. This assumes that the first ADC
- * is NID 0x04.
- */
-static struct hda_input_mux alc260_test_capture_sources[2] = {
- {
- .num_items = 7,
- .items = {
- { "MIC1 pin", 0x0 },
- { "MIC2 pin", 0x1 },
- { "LINE1 pin", 0x2 },
- { "LINE2 pin", 0x3 },
- { "CD pin", 0x4 },
- { "LINE-OUT pin", 0x5 },
- { "HP-OUT pin", 0x6 },
- },
- },
- {
- .num_items = 8,
- .items = {
- { "MIC1 pin", 0x0 },
- { "MIC2 pin", 0x1 },
- { "LINE1 pin", 0x2 },
- { "LINE2 pin", 0x3 },
- { "CD pin", 0x4 },
- { "Mixer", 0x5 },
- { "LINE-OUT pin", 0x6 },
- { "HP-OUT pin", 0x7 },
- },
- },
-};
-static struct snd_kcontrol_new alc260_test_mixer[] = {
- /* Output driver widgets */
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x09, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("LOUT2 Playback Switch", 0x09, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x08, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("LOUT1 Playback Switch", 0x08, 2, HDA_INPUT),
-
- /* Modes for retasking pin widgets
- * Note: the ALC260 doesn't seem to act on requests to enable mic
- * bias from NIDs 0x0f and 0x10. The ALC260 datasheet doesn't
- * mention this restriction. At this stage it's not clear whether
- * this behaviour is intentional or is a hardware bug in chip
- * revisions available at least up until early 2006. Therefore for
- * now allow the "HP-OUT" and "LINE-OUT" Mode controls to span all
- * choices, but if it turns out that the lack of mic bias for these
- * NIDs is intentional we could change their modes from
- * ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS.
- */
- ALC_PIN_MODE("HP-OUT pin mode", 0x10, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("LINE-OUT pin mode", 0x0f, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("LINE2 pin mode", 0x15, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("LINE1 pin mode", 0x14, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("MIC2 pin mode", 0x13, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("MIC1 pin mode", 0x12, ALC_PIN_DIR_INOUT),
-
- /* Loopback mixer controls */
- HDA_CODEC_VOLUME("MIC1 Playback Volume", 0x07, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("MIC1 Playback Switch", 0x07, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("MIC2 Playback Volume", 0x07, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("MIC2 Playback Switch", 0x07, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("LINE1 Playback Volume", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("LINE1 Playback Switch", 0x07, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("LINE2 Playback Volume", 0x07, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("LINE2 Playback Switch", 0x07, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("LINE-OUT loopback Playback Volume", 0x07, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("LINE-OUT loopback Playback Switch", 0x07, 0x06, HDA_INPUT),
- HDA_CODEC_VOLUME("HP-OUT loopback Playback Volume", 0x07, 0x7, HDA_INPUT),
- HDA_CODEC_MUTE("HP-OUT loopback Playback Switch", 0x07, 0x7, HDA_INPUT),
-
- /* Controls for GPIO pins, assuming they are configured as outputs */
- ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01),
- ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02),
- ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04),
- ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08),
-
- /* Switches to allow the digital IO pins to be enabled. The datasheet
- * is ambigious as to which NID is which; testing on laptops which
- * make this output available should provide clarification.
- */
- ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x03, 0x01),
- ALC_SPDIF_CTRL_SWITCH("SPDIF Capture Switch", 0x06, 0x01),
-
- /* A switch allowing EAPD to be enabled. Some laptops seem to use
- * this output to turn on an external amplifier.
- */
- ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02),
- ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02),
-
- { } /* end */
-};
-static struct hda_verb alc260_test_init_verbs[] = {
- /* Enable all GPIOs as outputs with an initial value of 0 */
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x0f},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x00},
- {0x01, AC_VERB_SET_GPIO_MASK, 0x0f},
-
- /* Enable retasking pins as output, initially without power amp */
- {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* Disable digital (SPDIF) pins initially, but users can enable
- * them via a mixer switch. In the case of SPDIF-out, this initverb
- * payload also sets the generation to 0, output to be in "consumer"
- * PCM format, copyright asserted, no pre-emphasis and no validity
- * control.
- */
- {0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},
- {0x06, AC_VERB_SET_DIGI_CONVERT_1, 0},
-
- /* Ensure mic1, mic2, line1 and line2 pin widgets take input from the
- * OUT1 sum bus when acting as an output.
- */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0},
- {0x0c, AC_VERB_SET_CONNECT_SEL, 0},
- {0x0d, AC_VERB_SET_CONNECT_SEL, 0},
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* Start with output sum widgets muted and their output gains at min */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Unmute retasking pin widget output buffers since the default
- * state appears to be output. As the pin mode is changed by the
- * user the pin mode control will take care of enabling the pin's
- * input/output buffers as needed.
- */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Also unmute the mono-out pin widget */
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Mute capture amp left and right */
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- /* Set ADC connection select to match default mixer setting (mic1
- * pin)
- */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Do the same for the second ADC: mute capture input amp and
- * set ADC connection to mic1 pin
- */
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mute all inputs to mixer widget (even unconnected ones) */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* mic2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, /* line1 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, /* line2 pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* CD pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, /* Beep-gen pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* Line-out pin */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* HP-pin pin */
-
- { }
-};
-#endif
-
-#define alc260_pcm_analog_playback alc880_pcm_analog_alt_playback
-#define alc260_pcm_analog_capture alc880_pcm_analog_capture
-
-#define alc260_pcm_digital_playback alc880_pcm_digital_playback
-#define alc260_pcm_digital_capture alc880_pcm_digital_capture
-
-/*
- * for BIOS auto-configuration
- */
-
-static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid,
- const char *pfx, int *vol_bits)
-{
- hda_nid_t nid_vol;
- unsigned long vol_val, sw_val;
- int err;
-
- if (nid >= 0x0f && nid < 0x11) {
- nid_vol = nid - 0x7;
- vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT);
- sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- } else if (nid == 0x11) {
- nid_vol = nid - 0x7;
- vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT);
- sw_val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
- } else if (nid >= 0x12 && nid <= 0x15) {
- nid_vol = 0x08;
- vol_val = HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT);
- sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- } else
- return 0; /* N/A */
-
- if (!(*vol_bits & (1 << nid_vol))) {
- /* first control for the volume widget */
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, vol_val);
- if (err < 0)
- return err;
- *vol_bits |= (1 << nid_vol);
- }
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, sw_val);
- if (err < 0)
- return err;
- return 1;
-}
-
-/* add playback controls from the parsed DAC table */
-static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- hda_nid_t nid;
- int err;
- int vols = 0;
-
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = spec->private_dac_nids;
- spec->multiout.dac_nids[0] = 0x02;
-
- nid = cfg->line_out_pins[0];
- if (nid) {
- const char *pfx;
- if (!cfg->speaker_pins[0] && !cfg->hp_pins[0])
- pfx = "Master";
- else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- pfx = "Speaker";
- else
- pfx = "Front";
- err = alc260_add_playback_controls(spec, nid, pfx, &vols);
- if (err < 0)
- return err;
- }
-
- nid = cfg->speaker_pins[0];
- if (nid) {
- err = alc260_add_playback_controls(spec, nid, "Speaker", &vols);
- if (err < 0)
- return err;
- }
-
- nid = cfg->hp_pins[0];
- if (nid) {
- err = alc260_add_playback_controls(spec, nid, "Headphone",
- &vols);
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int alc260_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- return alc_auto_create_input_ctls(codec, cfg, 0x07, 0x04, 0x05);
-}
-
-static void alc260_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- int sel_idx)
-{
- alc_set_pin_output(codec, nid, pin_type);
- /* need the manual connection? */
- if (nid >= 0x12) {
- int idx = nid - 0x12;
- snd_hda_codec_write(codec, idx + 0x0b, 0,
- AC_VERB_SET_CONNECT_SEL, sel_idx);
- }
-}
-
-static void alc260_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t nid;
-
- nid = spec->autocfg.line_out_pins[0];
- if (nid) {
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- alc260_auto_set_output_and_unmute(codec, nid, pin_type, 0);
- }
-
- nid = spec->autocfg.speaker_pins[0];
- if (nid)
- alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
-
- nid = spec->autocfg.hp_pins[0];
- if (nid)
- alc260_auto_set_output_and_unmute(codec, nid, PIN_HP, 0);
-}
-
-#define ALC260_PIN_CD_NID 0x16
-static void alc260_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (nid >= 0x12) {
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (nid != ALC260_PIN_CD_NID &&
- (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
- }
-}
-
-#define alc260_auto_init_input_src alc880_auto_init_input_src
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc260_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for
- * front panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- /* mute analog inputs */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /*
- * Set up output mixers (0x08 - 0x0a)
- */
- /* set vol=0 to output mixers */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- { }
-};
-
-static int alc260_parse_auto_config(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc260_ignore[] = { 0x17, 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc260_ignore);
- if (err < 0)
- return err;
- err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->kctls.list)
- return 0; /* can't find valid BIOS pin config */
- err = alc260_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = 2;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = ALC260_DIGOUT_NID;
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc260_volume_init_verbs);
-
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- alc_ssid_check(codec, 0x10, 0x15, 0x0f, 0);
-
- return 1;
-}
-
-/* additional initialization for auto-configuration model */
-static void alc260_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc260_auto_init_multi_out(codec);
- alc260_auto_init_analog_input(codec);
- alc260_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list alc260_loopbacks[] = {
- { 0x07, HDA_INPUT, 0 },
- { 0x07, HDA_INPUT, 1 },
- { 0x07, HDA_INPUT, 2 },
- { 0x07, HDA_INPUT, 3 },
- { 0x07, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
-
-/*
- * Pin config fixes
- */
-enum {
- PINFIX_HP_DC5750,
-};
-
-static const struct alc_fixup alc260_fixups[] = {
- [PINFIX_HP_DC5750] = {
- .pins = (const struct alc_pincfg[]) {
- { 0x11, 0x90130110 }, /* speaker */
- { }
- }
- },
-};
-
-static struct snd_pci_quirk alc260_fixup_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", PINFIX_HP_DC5750),
- {}
-};
-
-/*
- * ALC260 configurations
- */
-static const char *alc260_models[ALC260_MODEL_LAST] = {
- [ALC260_BASIC] = "basic",
- [ALC260_HP] = "hp",
- [ALC260_HP_3013] = "hp-3013",
- [ALC260_HP_DC7600] = "hp-dc7600",
- [ALC260_FUJITSU_S702X] = "fujitsu",
- [ALC260_ACER] = "acer",
- [ALC260_WILL] = "will",
- [ALC260_REPLACER_672V] = "replacer",
- [ALC260_FAVORIT100] = "favorit100",
-#ifdef CONFIG_SND_DEBUG
- [ALC260_TEST] = "test",
-#endif
- [ALC260_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc260_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1025, 0x007b, "Acer C20x", ALC260_ACER),
- SND_PCI_QUIRK(0x1025, 0x007f, "Acer", ALC260_WILL),
- SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_ACER),
- SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FAVORIT100),
- SND_PCI_QUIRK(0x103c, 0x2808, "HP d5700", ALC260_HP_3013),
- SND_PCI_QUIRK(0x103c, 0x280a, "HP d5750", ALC260_AUTO), /* no quirk */
- SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013),
- SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP_3013),
- SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_DC7600),
- SND_PCI_QUIRK(0x103c, 0x3013, "HP", ALC260_HP_3013),
- SND_PCI_QUIRK(0x103c, 0x3014, "HP", ALC260_HP),
- SND_PCI_QUIRK(0x103c, 0x3015, "HP", ALC260_HP),
- SND_PCI_QUIRK(0x103c, 0x3016, "HP", ALC260_HP),
- SND_PCI_QUIRK(0x104d, 0x81bb, "Sony VAIO", ALC260_BASIC),
- SND_PCI_QUIRK(0x104d, 0x81cc, "Sony VAIO", ALC260_BASIC),
- SND_PCI_QUIRK(0x104d, 0x81cd, "Sony VAIO", ALC260_BASIC),
- SND_PCI_QUIRK(0x10cf, 0x1326, "Fujitsu S702X", ALC260_FUJITSU_S702X),
- SND_PCI_QUIRK(0x152d, 0x0729, "CTL U553W", ALC260_BASIC),
- SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_REPLACER_672V),
- SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL),
- {}
-};
-
-static struct alc_config_preset alc260_presets[] = {
- [ALC260_BASIC] = {
- .mixers = { alc260_base_output_mixer,
- alc260_input_mixer },
- .init_verbs = { alc260_init_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
- .adc_nids = alc260_dual_adc_nids,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .input_mux = &alc260_capture_source,
- },
- [ALC260_HP] = {
- .mixers = { alc260_hp_output_mixer,
- alc260_input_mixer },
- .init_verbs = { alc260_init_verbs,
- alc260_hp_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
- .adc_nids = alc260_adc_nids_alt,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .input_mux = &alc260_capture_source,
- .unsol_event = alc260_hp_unsol_event,
- .init_hook = alc260_hp_automute,
- },
- [ALC260_HP_DC7600] = {
- .mixers = { alc260_hp_dc7600_mixer,
- alc260_input_mixer },
- .init_verbs = { alc260_init_verbs,
- alc260_hp_dc7600_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
- .adc_nids = alc260_adc_nids_alt,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .input_mux = &alc260_capture_source,
- .unsol_event = alc260_hp_3012_unsol_event,
- .init_hook = alc260_hp_3012_automute,
- },
- [ALC260_HP_3013] = {
- .mixers = { alc260_hp_3013_mixer,
- alc260_input_mixer },
- .init_verbs = { alc260_hp_3013_init_verbs,
- alc260_hp_3013_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
- .adc_nids = alc260_adc_nids_alt,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .input_mux = &alc260_capture_source,
- .unsol_event = alc260_hp_3013_unsol_event,
- .init_hook = alc260_hp_3013_automute,
- },
- [ALC260_FUJITSU_S702X] = {
- .mixers = { alc260_fujitsu_mixer },
- .init_verbs = { alc260_fujitsu_init_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
- .adc_nids = alc260_dual_adc_nids,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .num_mux_defs = ARRAY_SIZE(alc260_fujitsu_capture_sources),
- .input_mux = alc260_fujitsu_capture_sources,
- },
- [ALC260_ACER] = {
- .mixers = { alc260_acer_mixer },
- .init_verbs = { alc260_acer_init_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
- .adc_nids = alc260_dual_adc_nids,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .num_mux_defs = ARRAY_SIZE(alc260_acer_capture_sources),
- .input_mux = alc260_acer_capture_sources,
- },
- [ALC260_FAVORIT100] = {
- .mixers = { alc260_favorit100_mixer },
- .init_verbs = { alc260_favorit100_init_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
- .adc_nids = alc260_dual_adc_nids,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .num_mux_defs = ARRAY_SIZE(alc260_favorit100_capture_sources),
- .input_mux = alc260_favorit100_capture_sources,
- },
- [ALC260_WILL] = {
- .mixers = { alc260_will_mixer },
- .init_verbs = { alc260_init_verbs, alc260_will_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_adc_nids),
- .adc_nids = alc260_adc_nids,
- .dig_out_nid = ALC260_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .input_mux = &alc260_capture_source,
- },
- [ALC260_REPLACER_672V] = {
- .mixers = { alc260_replacer_672v_mixer },
- .init_verbs = { alc260_init_verbs, alc260_replacer_672v_verbs },
- .num_dacs = ARRAY_SIZE(alc260_dac_nids),
- .dac_nids = alc260_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_adc_nids),
- .adc_nids = alc260_adc_nids,
- .dig_out_nid = ALC260_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .input_mux = &alc260_capture_source,
- .unsol_event = alc260_replacer_672v_unsol_event,
- .init_hook = alc260_replacer_672v_automute,
- },
-#ifdef CONFIG_SND_DEBUG
- [ALC260_TEST] = {
- .mixers = { alc260_test_mixer },
- .init_verbs = { alc260_test_init_verbs },
- .num_dacs = ARRAY_SIZE(alc260_test_dac_nids),
- .dac_nids = alc260_test_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc260_test_adc_nids),
- .adc_nids = alc260_test_adc_nids,
- .num_channel_mode = ARRAY_SIZE(alc260_modes),
- .channel_mode = alc260_modes,
- .num_mux_defs = ARRAY_SIZE(alc260_test_capture_sources),
- .input_mux = alc260_test_capture_sources,
- },
-#endif
-};
-
-static int patch_alc260(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- board_config = snd_hda_check_board_config(codec, ALC260_MODEL_LAST,
- alc260_models,
- alc260_cfg_tbl);
- if (board_config < 0) {
- snd_printd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC260_AUTO;
- }
-
- if (board_config == ALC260_AUTO)
- alc_pick_fixup(codec, alc260_fixup_tbl, alc260_fixups, 1);
-
- if (board_config == ALC260_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc260_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC260_BASIC;
- }
- }
-
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
-
- if (board_config != ALC260_AUTO)
- setup_preset(codec, &alc260_presets[board_config]);
-
- spec->stream_analog_playback = &alc260_pcm_analog_playback;
- spec->stream_analog_capture = &alc260_pcm_analog_capture;
- spec->stream_analog_alt_capture = &alc260_pcm_analog_capture;
-
- spec->stream_digital_playback = &alc260_pcm_digital_playback;
- spec->stream_digital_capture = &alc260_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- /* check whether NID 0x04 is valid */
- unsigned int wcap = get_wcaps(codec, 0x04);
- wcap = get_wcaps_type(wcap);
- /* get type */
- if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
- spec->adc_nids = alc260_adc_nids_alt;
- spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt);
- } else {
- spec->adc_nids = alc260_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
- }
- }
- set_capture_mixer(codec);
- set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
-
- if (board_config == ALC260_AUTO)
- alc_pick_fixup(codec, alc260_fixup_tbl, alc260_fixups, 0);
-
- spec->vmaster_nid = 0x08;
-
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC260_AUTO)
- spec->init_hook = alc260_auto_init;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc260_loopbacks;
-#endif
-
- return 0;
-}
-
-
-/*
- * ALC882/883/885/888/889 support
- *
- * ALC882 is almost identical with ALC880 but has cleaner and more flexible
- * configuration. Each pin widget can choose any input DACs and a mixer.
- * Each ADC is connected from a mixer of all inputs. This makes possible
- * 6-channel independent captures.
- *
- * In addition, an independent DAC for the multi-playback (not used in this
- * driver yet).
- */
-#define ALC882_DIGOUT_NID 0x06
-#define ALC882_DIGIN_NID 0x0a
-#define ALC883_DIGOUT_NID ALC882_DIGOUT_NID
-#define ALC883_DIGIN_NID ALC882_DIGIN_NID
-#define ALC1200_DIGOUT_NID 0x10
-
-
-static struct hda_channel_mode alc882_ch_modes[1] = {
- { 8, NULL }
-};
-
-/* DACs */
-static hda_nid_t alc882_dac_nids[4] = {
- /* front, rear, clfe, rear_surr */
- 0x02, 0x03, 0x04, 0x05
-};
-#define alc883_dac_nids alc882_dac_nids
-
-/* ADCs */
-#define alc882_adc_nids alc880_adc_nids
-#define alc882_adc_nids_alt alc880_adc_nids_alt
-#define alc883_adc_nids alc882_adc_nids_alt
-static hda_nid_t alc883_adc_nids_alt[1] = { 0x08 };
-static hda_nid_t alc883_adc_nids_rev[2] = { 0x09, 0x08 };
-#define alc889_adc_nids alc880_adc_nids
-
-static hda_nid_t alc882_capsrc_nids[3] = { 0x24, 0x23, 0x22 };
-static hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 };
-#define alc883_capsrc_nids alc882_capsrc_nids_alt
-static hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 };
-#define alc889_capsrc_nids alc882_capsrc_nids
-
-/* input MUX */
-/* FIXME: should be a matrix-type input source selection */
-
-static struct hda_input_mux alc882_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-#define alc883_capture_source alc882_capture_source
-
-static struct hda_input_mux alc889_capture_source = {
- .num_items = 3,
- .items = {
- { "Front Mic", 0x0 },
- { "Mic", 0x3 },
- { "Line", 0x2 },
- },
-};
-
-static struct hda_input_mux mb5_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x1 },
- { "Line", 0x7 },
- { "CD", 0x4 },
- },
-};
-
-static struct hda_input_mux macmini3_capture_source = {
- .num_items = 2,
- .items = {
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-static struct hda_input_mux alc883_3stack_6ch_intel = {
- .num_items = 4,
- .items = {
- { "Mic", 0x1 },
- { "Front Mic", 0x0 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-static struct hda_input_mux alc883_lenovo_101e_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x1 },
- { "Line", 0x2 },
- },
-};
-
-static struct hda_input_mux alc883_lenovo_nb0763_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Int Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-static struct hda_input_mux alc883_fujitsu_pi2515_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- { "Int Mic", 0x1 },
- },
-};
-
-static struct hda_input_mux alc883_lenovo_sky_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x4 },
- },
-};
-
-static struct hda_input_mux alc883_asus_eee1601_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- { "Line", 0x2 },
- },
-};
-
-static struct hda_input_mux alc889A_mb31_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x0 },
- /* Front Mic (0x01) unused */
- { "Line", 0x2 },
- /* Line 2 (0x03) unused */
- /* CD (0x04) unused? */
- },
-};
-
-static struct hda_input_mux alc889A_imac91_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x01 },
- { "Line", 0x2 }, /* Not sure! */
- },
-};
-
-/*
- * 2ch mode
- */
-static struct hda_channel_mode alc883_3ST_2ch_modes[1] = {
- { 2, NULL }
-};
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc882_3ST_ch2_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc882_3ST_ch4_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc882_3ST_ch6_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-static struct hda_channel_mode alc882_3ST_6ch_modes[3] = {
- { 2, alc882_3ST_ch2_init },
- { 4, alc882_3ST_ch4_init },
- { 6, alc882_3ST_ch6_init },
-};
-
-#define alc883_3ST_6ch_modes alc882_3ST_6ch_modes
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc883_3ST_ch2_clevo_init[] = {
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc883_3ST_ch4_clevo_init[] = {
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc883_3ST_ch6_clevo_init[] = {
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-static struct hda_channel_mode alc883_3ST_6ch_clevo_modes[3] = {
- { 2, alc883_3ST_ch2_clevo_init },
- { 4, alc883_3ST_ch4_clevo_init },
- { 6, alc883_3ST_ch6_clevo_init },
-};
-
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc882_sixstack_ch6_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-/*
- * 8ch mode
- */
-static struct hda_verb alc882_sixstack_ch8_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-static struct hda_channel_mode alc882_sixstack_modes[2] = {
- { 6, alc882_sixstack_ch6_init },
- { 8, alc882_sixstack_ch8_init },
-};
-
-
-/* Macbook Air 2,1 */
-
-static struct hda_channel_mode alc885_mba21_ch_modes[1] = {
- { 2, NULL },
-};
-
-/*
- * macbook pro ALC885 can switch LineIn to LineOut without losing Mic
- */
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc885_mbp_ch2_init[] = {
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- { } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc885_mbp_ch4_init[] = {
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- { } /* end */
-};
-
-static struct hda_channel_mode alc885_mbp_4ch_modes[2] = {
- { 2, alc885_mbp_ch2_init },
- { 4, alc885_mbp_ch4_init },
-};
-
-/*
- * 2ch
- * Speakers/Woofer/HP = Front
- * LineIn = Input
- */
-static struct hda_verb alc885_mb5_ch2_init[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- { } /* end */
-};
-
-/*
- * 6ch mode
- * Speakers/HP = Front
- * Woofer = LFE
- * LineIn = Surround
- */
-static struct hda_verb alc885_mb5_ch6_init[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- { } /* end */
-};
-
-static struct hda_channel_mode alc885_mb5_6ch_modes[2] = {
- { 2, alc885_mb5_ch2_init },
- { 6, alc885_mb5_ch6_init },
-};
-
-#define alc885_macmini3_6ch_modes alc885_mb5_6ch_modes
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc883_4ST_ch2_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc883_4ST_ch4_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc883_4ST_ch6_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-/*
- * 8ch mode
- */
-static struct hda_verb alc883_4ST_ch8_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-static struct hda_channel_mode alc883_4ST_8ch_modes[4] = {
- { 2, alc883_4ST_ch2_init },
- { 4, alc883_4ST_ch4_init },
- { 6, alc883_4ST_ch6_init },
- { 8, alc883_4ST_ch8_init },
-};
-
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc883_3ST_ch2_intel_init[] = {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc883_3ST_ch4_intel_init[] = {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc883_3ST_ch6_intel_init[] = {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x19, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-static struct hda_channel_mode alc883_3ST_6ch_intel_modes[3] = {
- { 2, alc883_3ST_ch2_intel_init },
- { 4, alc883_3ST_ch4_intel_init },
- { 6, alc883_3ST_ch6_intel_init },
-};
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc889_ch2_intel_init[] = {
- { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x19, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x16, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc889_ch6_intel_init[] = {
- { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 8ch mode
- */
-static struct hda_verb alc889_ch8_intel_init[] = {
- { 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x03 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { } /* end */
-};
-
-static struct hda_channel_mode alc889_8ch_intel_modes[3] = {
- { 2, alc889_ch2_intel_init },
- { 6, alc889_ch6_intel_init },
- { 8, alc889_ch8_intel_init },
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc883_sixstack_ch6_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-/*
- * 8ch mode
- */
-static struct hda_verb alc883_sixstack_ch8_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-static struct hda_channel_mode alc883_sixstack_modes[2] = {
- { 6, alc883_sixstack_ch6_init },
- { 8, alc883_sixstack_ch8_init },
-};
-
-
-/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
- * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
- */
-static struct snd_kcontrol_new alc882_base_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-/* Macbook Air 2,1 same control for HP and internal Speaker */
-
-static struct snd_kcontrol_new alc885_mba21_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 0x02, HDA_OUTPUT),
- { }
-};
-
-
-static struct snd_kcontrol_new alc885_mbp3_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Speaker Playback Switch", 0x0c, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0e, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Headphone Playback Switch", 0x0e, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Boost", 0x1a, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc885_mb5_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("LFE Playback Volume", 0x0e, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("LFE Playback Switch", 0x0e, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0f, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Headphone Playback Switch", 0x0f, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x07, HDA_INPUT),
- HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x07, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Boost", 0x15, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x19, 0x00, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc885_macmini3_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("LFE Playback Volume", 0x0e, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("LFE Playback Switch", 0x0e, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0f, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Headphone Playback Switch", 0x0f, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x07, HDA_INPUT),
- HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x07, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Boost", 0x15, 0x00, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc885_imac91_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 0x02, HDA_INPUT),
- { } /* end */
-};
-
-
-static struct snd_kcontrol_new alc882_w2jc_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc882_targa_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- { } /* end */
-};
-
-/* Pin assignment: Front=0x14, HP = 0x15, Front = 0x16, ???
- * Front Mic=0x18, Line In = 0x1a, Line In = 0x1b, CD = 0x1c
- */
-static struct snd_kcontrol_new alc882_asus_a7j_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Mobile Front Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mobile Line Playback Volume", 0x0b, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Mobile Line Playback Switch", 0x0b, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc882_asus_a7m_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc882_chmode_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-static struct hda_verb alc882_base_init_verbs[] = {
- /* Front mixer: unmute input/output amp left and right (volume = 0) */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Rear mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* CLFE mixer */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Side mixer */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- /* Front Pin: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Rear Pin: output 1 (0x0d) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* CLFE Pin: output 2 (0x0e) */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* Side Pin: output 3 (0x0f) */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
- /* Mic (rear) pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line-2 In: Headphone output (output 0 - 0x0c) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* ADC2: mute amp left and right */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* ADC3: mute amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- { }
-};
-
-static struct hda_verb alc882_adc1_init_verbs[] = {
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* ADC1: mute amp left and right */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- { }
-};
-
-static struct hda_verb alc882_eapd_verbs[] = {
- /* change to EAPD mode */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3060},
- { }
-};
-
-static struct hda_verb alc889_eapd_verbs[] = {
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-static struct hda_verb alc_hp15_unsol_verbs[] = {
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {}
-};
-
-static struct hda_verb alc885_init_verbs[] = {
- /* Front mixer: unmute input/output amp left and right (volume = 0) */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Rear mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* CLFE mixer */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* Side mixer */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Front HP Pin: output 0 (0x0c) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Front Pin: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Rear Pin: output 1 (0x0d) */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* CLFE Pin: output 2 (0x0e) */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* Side Pin: output 3 (0x0f) */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
- /* Mic (rear) pin: input vref at 80% */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- /* Mixer elements: 0x18, , 0x1a, 0x1b */
- /* Input mixer1 */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* ADC2: mute amp left and right */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- /* ADC3: mute amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-
- { }
-};
-
-static struct hda_verb alc885_init_input_verbs[] = {
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- { }
-};
-
-
-/* Unmute Selector 24h and set the default input to front mic */
-static struct hda_verb alc889_init_input_verbs[] = {
- {0x24, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- { }
-};
-
-
-#define alc883_init_verbs alc882_base_init_verbs
-
-/* Mac Pro test */
-static struct snd_kcontrol_new alc882_macpro_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x18, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
- /* FIXME: this looks suspicious...
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x02, HDA_INPUT),
- */
- { } /* end */
-};
-
-static struct hda_verb alc882_macpro_init_verbs[] = {
- /* Front mixer: unmute input/output amp left and right (volume = 0) */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Front Pin: output 0 (0x0c) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Speaker: output */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x04},
- /* Headphone output (output 0 - 0x0c) */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* ADC1: mute amp left and right */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* ADC2: mute amp left and right */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* ADC3: mute amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- { }
-};
-
-/* Macbook 5,1 */
-static struct hda_verb alc885_mb5_init_verbs[] = {
- /* DACs */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Front mixer */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Surround mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* LFE mixer */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* HP mixer */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Front Pin (0x0c) */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* LFE Pin (0x0e) */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* HP Pin (0x0f) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x03},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0x1)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0x7)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0x4)},
- { }
-};
-
-/* Macmini 3,1 */
-static struct hda_verb alc885_macmini3_init_verbs[] = {
- /* DACs */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Front mixer */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Surround mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* LFE mixer */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* HP mixer */
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Front Pin (0x0c) */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* LFE Pin (0x0e) */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* HP Pin (0x0f) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x03},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- /* Line In pin */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- { }
-};
-
-
-static struct hda_verb alc885_mba21_init_verbs[] = {
- /*Internal and HP Speaker Mixer*/
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /*Internal Speaker Pin (0x0c)*/
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) },
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP Pin: output 0 (0x0e) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, (ALC880_HP_EVENT | AC_USRSP_EN)},
- /* Line in (is hp when jack connected)*/
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_VREF_50},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- { }
- };
-
-
-/* Macbook Pro rev3 */
-static struct hda_verb alc885_mbp3_init_verbs[] = {
- /* Front mixer: unmute input/output amp left and right (volume = 0) */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Rear mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* HP mixer */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Front Pin: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP Pin: output 0 (0x0e) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x02},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- /* Mic (rear) pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: use output 1 when in LineOut mode */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* ADC1: mute amp left and right */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* ADC2: mute amp left and right */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* ADC3: mute amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- { }
-};
-
-/* iMac 9,1 */
-static struct hda_verb alc885_imac91_init_verbs[] = {
- /* Internal Speaker Pin (0x0c) */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) },
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) },
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP Pin: Rear */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, (ALC880_HP_EVENT | AC_USRSP_EN)},
- /* Line in Rear */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_VREF_50},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Rear mixer */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* Line-Out mixer: unmute input/output amp left and right (volume = 0) */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* 0x24 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* 0x23 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* 0x22 [Audio Mixer] wcaps 0x20010b: Stereo Amp-In */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- /* 0x07 [Audio Input] wcaps 0x10011b: Stereo Amp-In */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* 0x08 [Audio Input] wcaps 0x10011b: Stereo Amp-In */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* 0x09 [Audio Input] wcaps 0x10011b: Stereo Amp-In */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- { }
-};
-
-/* iMac 24 mixer. */
-static struct snd_kcontrol_new alc885_imac24_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x00, HDA_INPUT),
- { } /* end */
-};
-
-/* iMac 24 init verbs. */
-static struct hda_verb alc885_imac24_init_verbs[] = {
- /* Internal speakers: output 0 (0x0c) */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Internal speakers: output 0 (0x0c) */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Headphone: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- /* Front Mic: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- { }
-};
-
-/* Toggle speaker-output according to the hp-jack state */
-static void alc885_imac24_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x18;
- spec->autocfg.speaker_pins[1] = 0x1a;
-}
-
-#define alc885_mb5_setup alc885_imac24_setup
-#define alc885_macmini3_setup alc885_imac24_setup
-
-/* Macbook Air 2,1 */
-static void alc885_mba21_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x18;
-}
-
-
-
-static void alc885_mbp3_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-static void alc885_imac91_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x18;
- spec->autocfg.speaker_pins[1] = 0x1a;
-}
-
-static struct hda_verb alc882_targa_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
-
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc882_targa_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc_automute_amp(codec);
- snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA,
- spec->jack_present ? 1 : 3);
-}
-
-static void alc882_targa_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x1b;
-}
-
-static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc882_targa_automute(codec);
-}
-
-static struct hda_verb alc882_asus_a7j_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
-
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- { } /* end */
-};
-
-static struct hda_verb alc882_asus_a7m_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */
-
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
- { } /* end */
-};
-
-static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted)
-{
- unsigned int gpiostate, gpiomask, gpiodir;
-
- gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DATA, 0);
-
- if (!muted)
- gpiostate |= (1 << pin);
- else
- gpiostate &= ~(1 << pin);
-
- gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_MASK, 0);
- gpiomask |= (1 << pin);
-
- gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DIRECTION, 0);
- gpiodir |= (1 << pin);
-
-
- snd_hda_codec_write(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_MASK, gpiomask);
- snd_hda_codec_write(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_DIRECTION, gpiodir);
-
- msleep(1);
-
- snd_hda_codec_write(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_DATA, gpiostate);
-}
-
-/* set up GPIO at initialization */
-static void alc885_macpro_init_hook(struct hda_codec *codec)
-{
- alc882_gpio_mute(codec, 0, 0);
- alc882_gpio_mute(codec, 1, 0);
-}
-
-/* set up GPIO and update auto-muting at initialization */
-static void alc885_imac24_init_hook(struct hda_codec *codec)
-{
- alc885_macpro_init_hook(codec);
- alc_automute_amp(codec);
-}
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc883_auto_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /*
- * Set up output mixers (0x0c - 0x0f)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { }
-};
-
-/* 2ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:front) */
-static struct hda_verb alc889A_mb31_ch2_init[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP as front */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Line as input */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Line off */
- { } /* end */
-};
-
-/* 4ch mode (Speaker:front, Subwoofer:CLFE, Line:CLFE, Headphones:front) */
-static struct hda_verb alc889A_mb31_ch4_init[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP as front */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Line as output */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Line on */
- { } /* end */
-};
-
-/* 5ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:rear) */
-static struct hda_verb alc889A_mb31_ch5_init[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* HP as rear */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Line as input */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Line off */
- { } /* end */
-};
-
-/* 6ch mode (Speaker:front, Subwoofer:off, Line:CLFE, Headphones:Rear) */
-static struct hda_verb alc889A_mb31_ch6_init[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* HP as front */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Subwoofer off */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Line as output */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Line on */
- { } /* end */
-};
-
-static struct hda_channel_mode alc889A_mb31_6ch_modes[4] = {
- { 2, alc889A_mb31_ch2_init },
- { 4, alc889A_mb31_ch4_init },
- { 5, alc889A_mb31_ch5_init },
- { 6, alc889A_mb31_ch6_init },
-};
-
-static struct hda_verb alc883_medion_eapd_verbs[] = {
- /* eanable EAPD on medion laptop */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3070},
- { }
-};
-
-#define alc883_base_mixer alc882_base_mixer
-
-static struct snd_kcontrol_new alc883_mitac_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_clevo_m720_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_3ST_6ch_intel_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc885_8ch_intel_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x1b, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_fivestack_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_targa_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_targa_2ch_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_targa_8ch_mixer[] = {
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_lenovo_nb0763_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_medion_md2_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_medion_wim2160_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x08, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_verb alc883_medion_wim2160_verbs[] = {
- /* Unmute front mixer */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Set speaker pin to front mixer */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Init headphone pin */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-
- { } /* end */
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc883_medion_wim2160_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1a;
- spec->autocfg.speaker_pins[0] = 0x15;
-}
-
-static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc888_acer_aspire_6530_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("LFE Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc888_lenovo_sky_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume",
- 0x0d, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc889A_mb31_mixer[] = {
- /* Output mixers */
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x00,
- HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x02, HDA_INPUT),
- /* Output switches */
- HDA_CODEC_MUTE("Enable Speaker", 0x14, 0x00, HDA_OUTPUT),
- HDA_CODEC_MUTE("Enable Headphones", 0x15, 0x00, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Enable LFE", 0x16, 2, 0x00, HDA_OUTPUT),
- /* Boost mixers */
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Boost", 0x1a, 0x00, HDA_INPUT),
- /* Input mixers */
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_vaiott_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_bind_ctls alc883_bind_cap_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
- 0
- },
-};
-
-static struct hda_bind_ctls alc883_bind_cap_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_asus_eee1601_cap_mixer[] = {
- HDA_BIND_VOL("Capture Volume", &alc883_bind_cap_vol),
- HDA_BIND_SW("Capture Switch", &alc883_bind_cap_switch),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc883_chmode_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc883_mitac_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x17;
-}
-
-/* auto-toggle front mic */
-/*
-static void alc883_mitac_mic_automute(struct hda_codec *codec)
-{
- unsigned char bits = snd_hda_jack_detect(codec, 0x18) ? HDA_AMP_MUTE : 0;
-
- snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
-}
-*/
-
-static struct hda_verb alc883_mitac_verbs[] = {
- /* HP */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Subwoofer */
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x02},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* enable unsolicited event */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- /* {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, */
-
- { } /* end */
-};
-
-static struct hda_verb alc883_clevo_m540r_verbs[] = {
- /* HP */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Int speaker */
- /*{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},*/
-
- /* enable unsolicited event */
- /*
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
- */
-
- { } /* end */
-};
-
-static struct hda_verb alc883_clevo_m720_verbs[] = {
- /* HP */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Int speaker */
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* enable unsolicited event */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
-
- { } /* end */
-};
-
-static struct hda_verb alc883_2ch_fujitsu_pi2515_verbs[] = {
- /* HP */
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* Subwoofer */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* enable unsolicited event */
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-
- { } /* end */
-};
-
-static struct hda_verb alc883_targa_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
-/* Connect Line-Out side jack (SPDIF) to Side */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
-/* Connect Mic jack to CLFE */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x02},
-/* Connect Line-in jack to Surround */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
-/* Connect HP out jack to Front */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-
- { } /* end */
-};
-
-static struct hda_verb alc883_lenovo_101e_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_FRONT_EVENT|AC_USRSP_EN},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT|AC_USRSP_EN},
- { } /* end */
-};
-
-static struct hda_verb alc883_lenovo_nb0763_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- { } /* end */
-};
-
-static struct hda_verb alc888_lenovo_ms7195_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_FRONT_EVENT | AC_USRSP_EN},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
-
-static struct hda_verb alc883_haier_w66_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- { } /* end */
-};
-
-static struct hda_verb alc888_lenovo_sky_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
-
-static struct hda_verb alc888_6st_dell_verbs[] = {
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { }
-};
-
-static struct hda_verb alc883_vaiott_verbs[] = {
- /* HP */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-
- /* enable unsolicited event */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-
- { } /* end */
-};
-
-static void alc888_3st_hp_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x16;
- spec->autocfg.speaker_pins[2] = 0x18;
-}
-
-static struct hda_verb alc888_3st_hp_verbs[] = {
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc888_3st_hp_2ch_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc888_3st_hp_4ch_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x16, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc888_3st_hp_6ch_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x16, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-static struct hda_channel_mode alc888_3st_hp_modes[3] = {
- { 2, alc888_3st_hp_2ch_init },
- { 4, alc888_3st_hp_4ch_init },
- { 6, alc888_3st_hp_6ch_init },
-};
-
-/* toggle front-jack and RCA according to the hp-jack state */
-static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)
-{
- unsigned int present = snd_hda_jack_detect(codec, 0x1b);
-
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-/* toggle RCA according to the front-jack state */
-static void alc888_lenovo_ms7195_rca_automute(struct hda_codec *codec)
-{
- unsigned int present = snd_hda_jack_detect(codec, 0x14);
-
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-static void alc883_lenovo_ms7195_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc888_lenovo_ms7195_front_automute(codec);
- if ((res >> 26) == ALC880_FRONT_EVENT)
- alc888_lenovo_ms7195_rca_automute(codec);
-}
-
-static struct hda_verb alc883_medion_md2_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc883_medion_md2_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x15;
-}
-
-/* toggle speaker-output according to the hp-jack state */
-#define alc883_targa_init_hook alc882_targa_init_hook
-#define alc883_targa_unsol_event alc882_targa_unsol_event
-
-static void alc883_clevo_m720_mic_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- present = snd_hda_jack_detect(codec, 0x18);
- snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-static void alc883_clevo_m720_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-static void alc883_clevo_m720_init_hook(struct hda_codec *codec)
-{
- alc_automute_amp(codec);
- alc883_clevo_m720_mic_automute(codec);
-}
-
-static void alc883_clevo_m720_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_MIC_EVENT:
- alc883_clevo_m720_mic_automute(codec);
- break;
- default:
- alc_automute_amp_unsol_event(codec, res);
- break;
- }
-}
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc883_2ch_fujitsu_pi2515_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x15;
-}
-
-static void alc883_haier_w66_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
-{
- int bits = snd_hda_jack_detect(codec, 0x14) ? HDA_AMP_MUTE : 0;
-
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc883_lenovo_101e_all_automute(struct hda_codec *codec)
-{
- int bits = snd_hda_jack_detect(codec, 0x1b) ? HDA_AMP_MUTE : 0;
-
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc883_lenovo_101e_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc883_lenovo_101e_all_automute(codec);
- if ((res >> 26) == ALC880_FRONT_EVENT)
- alc883_lenovo_101e_ispeaker_automute(codec);
-}
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc883_acer_aspire_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x15;
- spec->autocfg.speaker_pins[1] = 0x16;
-}
-
-static struct hda_verb alc883_acer_eapd_verbs[] = {
- /* HP Pin: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* Front Pin: output 0 (0x0c) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* eanable EAPD on medion laptop */
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3050},
- /* enable unsolicited event */
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { }
-};
-
-static struct hda_verb alc888_acer_aspire_7730G_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x02},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
-
-static void alc888_6st_dell_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x15;
- spec->autocfg.speaker_pins[2] = 0x16;
- spec->autocfg.speaker_pins[3] = 0x17;
-}
-
-static void alc888_lenovo_sky_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x15;
- spec->autocfg.speaker_pins[2] = 0x16;
- spec->autocfg.speaker_pins[3] = 0x17;
- spec->autocfg.speaker_pins[4] = 0x1a;
-}
-
-static void alc883_vaiott_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x17;
-}
-
-static struct hda_verb alc888_asus_m90v_verbs[] = {
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* enable unsolicited event */
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
- { } /* end */
-};
-
-static void alc883_mode2_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x15;
- spec->autocfg.speaker_pins[2] = 0x16;
- spec->ext_mic.pin = 0x18;
- spec->int_mic.pin = 0x19;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
-}
-
-static struct hda_verb alc888_asus_eee1601_verbs[] = {
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x20, AC_VERB_SET_COEF_INDEX, 0x0b},
- {0x20, AC_VERB_SET_PROC_COEF, 0x0838},
- /* enable unsolicited event */
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
-
-static void alc883_eee1601_inithook(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x1b;
- alc_automute_pin(codec);
-}
-
-static struct hda_verb alc889A_mb31_verbs[] = {
- /* Init rear pin (used as headphone output) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4}, /* Apple Headphones */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Connect to front */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- /* Init line pin (used as output in 4ch and 6ch mode) */
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Connect to CLFE */
- /* Init line 2 pin (used as headphone out by default) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Use as input */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Mute output */
- { } /* end */
-};
-
-/* Mute speakers according to the headphone jack state */
-static void alc889A_mb31_automute(struct hda_codec *codec)
-{
- unsigned int present;
-
- /* Mute only in 2ch or 4ch mode */
- if (snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_CONNECT_SEL, 0)
- == 0x00) {
- present = snd_hda_jack_detect(codec, 0x15);
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- }
-}
-
-static void alc889A_mb31_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc889A_mb31_automute(codec);
-}
-
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc882_loopbacks alc880_loopbacks
-#endif
-
-/* pcm configuration: identical with ALC880 */
-#define alc882_pcm_analog_playback alc880_pcm_analog_playback
-#define alc882_pcm_analog_capture alc880_pcm_analog_capture
-#define alc882_pcm_digital_playback alc880_pcm_digital_playback
-#define alc882_pcm_digital_capture alc880_pcm_digital_capture
-
-static hda_nid_t alc883_slave_dig_outs[] = {
- ALC1200_DIGOUT_NID, 0,
-};
-
-static hda_nid_t alc1200_slave_dig_outs[] = {
- ALC883_DIGOUT_NID, 0,
-};
-
-/*
- * configuration and preset
- */
-static const char *alc882_models[ALC882_MODEL_LAST] = {
- [ALC882_3ST_DIG] = "3stack-dig",
- [ALC882_6ST_DIG] = "6stack-dig",
- [ALC882_ARIMA] = "arima",
- [ALC882_W2JC] = "w2jc",
- [ALC882_TARGA] = "targa",
- [ALC882_ASUS_A7J] = "asus-a7j",
- [ALC882_ASUS_A7M] = "asus-a7m",
- [ALC885_MACPRO] = "macpro",
- [ALC885_MB5] = "mb5",
- [ALC885_MACMINI3] = "macmini3",
- [ALC885_MBA21] = "mba21",
- [ALC885_MBP3] = "mbp3",
- [ALC885_IMAC24] = "imac24",
- [ALC885_IMAC91] = "imac91",
- [ALC883_3ST_2ch_DIG] = "3stack-2ch-dig",
- [ALC883_3ST_6ch_DIG] = "3stack-6ch-dig",
- [ALC883_3ST_6ch] = "3stack-6ch",
- [ALC883_6ST_DIG] = "alc883-6stack-dig",
- [ALC883_TARGA_DIG] = "targa-dig",
- [ALC883_TARGA_2ch_DIG] = "targa-2ch-dig",
- [ALC883_TARGA_8ch_DIG] = "targa-8ch-dig",
- [ALC883_ACER] = "acer",
- [ALC883_ACER_ASPIRE] = "acer-aspire",
- [ALC888_ACER_ASPIRE_4930G] = "acer-aspire-4930g",
- [ALC888_ACER_ASPIRE_6530G] = "acer-aspire-6530g",
- [ALC888_ACER_ASPIRE_8930G] = "acer-aspire-8930g",
- [ALC888_ACER_ASPIRE_7730G] = "acer-aspire-7730g",
- [ALC883_MEDION] = "medion",
- [ALC883_MEDION_MD2] = "medion-md2",
- [ALC883_MEDION_WIM2160] = "medion-wim2160",
- [ALC883_LAPTOP_EAPD] = "laptop-eapd",
- [ALC883_LENOVO_101E_2ch] = "lenovo-101e",
- [ALC883_LENOVO_NB0763] = "lenovo-nb0763",
- [ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig",
- [ALC888_LENOVO_SKY] = "lenovo-sky",
- [ALC883_HAIER_W66] = "haier-w66",
- [ALC888_3ST_HP] = "3stack-hp",
- [ALC888_6ST_DELL] = "6stack-dell",
- [ALC883_MITAC] = "mitac",
- [ALC883_CLEVO_M540R] = "clevo-m540r",
- [ALC883_CLEVO_M720] = "clevo-m720",
- [ALC883_FUJITSU_PI2515] = "fujitsu-pi2515",
- [ALC888_FUJITSU_XA3530] = "fujitsu-xa3530",
- [ALC883_3ST_6ch_INTEL] = "3stack-6ch-intel",
- [ALC889A_INTEL] = "intel-alc889a",
- [ALC889_INTEL] = "intel-x58",
- [ALC1200_ASUS_P5Q] = "asus-p5q",
- [ALC889A_MB31] = "mb31",
- [ALC883_SONY_VAIO_TT] = "sony-vaio-tt",
- [ALC882_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc882_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG),
-
- SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G",
- ALC888_ACER_ASPIRE_4930G),
- SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G",
- ALC888_ACER_ASPIRE_4930G),
- SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G",
- ALC888_ACER_ASPIRE_8930G),
- SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G",
- ALC888_ACER_ASPIRE_8930G),
- SND_PCI_QUIRK(0x1025, 0x0157, "Acer X3200", ALC882_AUTO),
- SND_PCI_QUIRK(0x1025, 0x0158, "Acer AX1700-U3700A", ALC882_AUTO),
- SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G",
- ALC888_ACER_ASPIRE_6530G),
- SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G",
- ALC888_ACER_ASPIRE_6530G),
- SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G",
- ALC888_ACER_ASPIRE_7730G),
- /* default Acer -- disabled as it causes more problems.
- * model=auto should work fine now
- */
- /* SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_ACER), */
-
- SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL),
-
- SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
- SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
- SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x103c, 0x2a66, "HP Acacia", ALC888_3ST_HP),
- SND_PCI_QUIRK(0x103c, 0x2a72, "HP Educ.ar", ALC888_3ST_HP),
-
- SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J),
- SND_PCI_QUIRK(0x1043, 0x1243, "Asus A7J", ALC882_ASUS_A7J),
- SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_ASUS_A7M),
- SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V),
- SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
- SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x1043, 0x8284, "Asus Z37E", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1043, 0x82fe, "Asus P5Q-EM HDMI", ALC1200_ASUS_P5Q),
- SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601),
-
- SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC883_SONY_VAIO_TT),
- SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1071, 0x8227, "Mitac 82801H", ALC883_MITAC),
- SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC),
- SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL),
- SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
- SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG),
-
- SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */
- SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC882_AUTO),
- SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3783, "NEC S970", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3fdf, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x42cd, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x4570, "MSI Wind Top AE2220", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x6510, "MSI GX620", ALC883_TARGA_8ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7260, "MSI 7260", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7350, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7437, "MSI NetOn AP1900", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1462, 0xaa08, "MSI", ALC883_TARGA_2ch_DIG),
-
- SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1558, 0x0571, "Clevo laptop M570U", ALC883_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720),
- SND_PCI_QUIRK(0x1558, 0x0722, "Clevo laptop M720SR", ALC883_CLEVO_M720),
- SND_PCI_QUIRK(0x1558, 0x5409, "Clevo laptop M540R", ALC883_CLEVO_M540R),
- SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC883_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch),
- /* SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA), */
- SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION),
- SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1100, "FSC AMILO Xi/Pi25xx",
- ALC883_FUJITSU_PI2515),
- SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1130, "Fujitsu AMILO Xa35xx",
- ALC888_FUJITSU_XA3530),
- SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch),
- SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763),
- SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763),
- SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763),
- SND_PCI_QUIRK(0x17aa, 0x101d, "Lenovo Sky", ALC888_LENOVO_SKY),
- SND_PCI_QUIRK(0x17c0, 0x4071, "MEDION MD2", ALC883_MEDION_MD2),
- SND_PCI_QUIRK(0x17c0, 0x4085, "MEDION MD96630", ALC888_LENOVO_MS7195_DIG),
- SND_PCI_QUIRK(0x17f2, 0x5000, "Albatron KI690-AM2", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66),
-
- SND_PCI_QUIRK(0x8086, 0x0001, "DG33BUC", ALC883_3ST_6ch_INTEL),
- SND_PCI_QUIRK(0x8086, 0x0002, "DG33FBC", ALC883_3ST_6ch_INTEL),
- SND_PCI_QUIRK(0x8086, 0x2503, "82801H", ALC883_MITAC),
- SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_INTEL),
- SND_PCI_QUIRK(0x8086, 0x0021, "Intel IbexPeak", ALC889A_INTEL),
- SND_PCI_QUIRK(0x8086, 0x3b56, "Intel IbexPeak", ALC889A_INTEL),
- SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC882_6ST_DIG),
-
- {}
-};
-
-/* codec SSID table for Intel Mac */
-static struct snd_pci_quirk alc882_ssid_cfg_tbl[] = {
- SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC885_MACPRO),
- SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_IMAC24),
- SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_IMAC24),
- SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x3000, "iMac", ALC889A_MB31),
- SND_PCI_QUIRK(0x106b, 0x3200, "iMac 7,1 Aluminum", ALC882_ASUS_A7M),
- SND_PCI_QUIRK(0x106b, 0x3400, "MacBookAir 1,1", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x3500, "MacBookAir 2,1", ALC885_MBA21),
- SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889A_MB31),
- SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC885_MBP3),
- SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_IMAC24),
- SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC885_IMAC91),
- SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC885_MB5),
- SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC885_MB5),
- /* FIXME: HP jack sense seems not working for MBP 5,1 or 5,2,
- * so apparently no perfect solution yet
- */
- SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC885_MB5),
- SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC885_MB5),
- SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC885_MACMINI3),
- {} /* terminator */
-};
-
-static struct alc_config_preset alc882_presets[] = {
- [ALC882_3ST_DIG] = {
- .mixers = { alc882_base_mixer },
- .init_verbs = { alc882_base_init_verbs,
- alc882_adc1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
- .channel_mode = alc882_ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- },
- [ALC882_6ST_DIG] = {
- .mixers = { alc882_base_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_base_init_verbs,
- alc882_adc1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
- .channel_mode = alc882_sixstack_modes,
- .input_mux = &alc882_capture_source,
- },
- [ALC882_ARIMA] = {
- .mixers = { alc882_base_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
- alc882_eapd_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
- .channel_mode = alc882_sixstack_modes,
- .input_mux = &alc882_capture_source,
- },
- [ALC882_W2JC] = {
- .mixers = { alc882_w2jc_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
- alc882_eapd_verbs, alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- .dig_out_nid = ALC882_DIGOUT_NID,
- },
- [ALC885_MBA21] = {
- .mixers = { alc885_mba21_mixer },
- .init_verbs = { alc885_mba21_init_verbs, alc880_gpio1_init_verbs },
- .num_dacs = 2,
- .dac_nids = alc882_dac_nids,
- .channel_mode = alc885_mba21_ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc885_mba21_ch_modes),
- .input_mux = &alc882_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc885_mba21_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC885_MBP3] = {
- .mixers = { alc885_mbp3_mixer, alc882_chmode_mixer },
- .init_verbs = { alc885_mbp3_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = 2,
- .dac_nids = alc882_dac_nids,
- .hp_nid = 0x04,
- .channel_mode = alc885_mbp_4ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc885_mbp_4ch_modes),
- .input_mux = &alc882_capture_source,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc885_mbp3_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC885_MB5] = {
- .mixers = { alc885_mb5_mixer, alc882_chmode_mixer },
- .init_verbs = { alc885_mb5_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .channel_mode = alc885_mb5_6ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc885_mb5_6ch_modes),
- .input_mux = &mb5_capture_source,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc885_mb5_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC885_MACMINI3] = {
- .mixers = { alc885_macmini3_mixer, alc882_chmode_mixer },
- .init_verbs = { alc885_macmini3_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .channel_mode = alc885_macmini3_6ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc885_macmini3_6ch_modes),
- .input_mux = &macmini3_capture_source,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc885_macmini3_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC885_MACPRO] = {
- .mixers = { alc882_macpro_mixer },
- .init_verbs = { alc882_macpro_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
- .channel_mode = alc882_ch_modes,
- .input_mux = &alc882_capture_source,
- .init_hook = alc885_macpro_init_hook,
- },
- [ALC885_IMAC24] = {
- .mixers = { alc885_imac24_mixer },
- .init_verbs = { alc885_imac24_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
- .channel_mode = alc882_ch_modes,
- .input_mux = &alc882_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc885_imac24_setup,
- .init_hook = alc885_imac24_init_hook,
- },
- [ALC885_IMAC91] = {
- .mixers = {alc885_imac91_mixer},
- .init_verbs = { alc885_imac91_init_verbs,
- alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .channel_mode = alc885_mba21_ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc885_mba21_ch_modes),
- .input_mux = &alc889A_imac91_capture_source,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .dig_in_nid = ALC882_DIGIN_NID,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc885_imac91_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC882_TARGA] = {
- .mixers = { alc882_targa_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
- alc880_gpio3_init_verbs, alc882_targa_verbs},
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
- .adc_nids = alc882_adc_nids,
- .capsrc_nids = alc882_capsrc_nids,
- .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes),
- .channel_mode = alc882_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- .unsol_event = alc882_targa_unsol_event,
- .setup = alc882_targa_setup,
- .init_hook = alc882_targa_automute,
- },
- [ALC882_ASUS_A7J] = {
- .mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
- alc882_asus_a7j_verbs},
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
- .adc_nids = alc882_adc_nids,
- .capsrc_nids = alc882_capsrc_nids,
- .num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes),
- .channel_mode = alc882_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- },
- [ALC882_ASUS_A7M] = {
- .mixers = { alc882_asus_a7m_mixer, alc882_chmode_mixer },
- .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
- alc882_eapd_verbs, alc880_gpio1_init_verbs,
- alc882_asus_a7m_verbs },
- .num_dacs = ARRAY_SIZE(alc882_dac_nids),
- .dac_nids = alc882_dac_nids,
- .dig_out_nid = ALC882_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
- .channel_mode = alc880_threestack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc882_capture_source,
- },
- [ALC883_3ST_2ch_DIG] = {
- .mixers = { alc883_3ST_2ch_mixer },
- .init_verbs = { alc883_init_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- },
- [ALC883_3ST_6ch_DIG] = {
- .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- },
- [ALC883_3ST_6ch] = {
- .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- },
- [ALC883_3ST_6ch_INTEL] = {
- .mixers = { alc883_3ST_6ch_intel_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .slave_dig_outs = alc883_slave_dig_outs,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_intel_modes),
- .channel_mode = alc883_3ST_6ch_intel_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_3stack_6ch_intel,
- },
- [ALC889A_INTEL] = {
- .mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer },
- .init_verbs = { alc885_init_verbs, alc885_init_input_verbs,
- alc_hp15_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc889_adc_nids),
- .adc_nids = alc889_adc_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .slave_dig_outs = alc883_slave_dig_outs,
- .num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes),
- .channel_mode = alc889_8ch_intel_modes,
- .capsrc_nids = alc889_capsrc_nids,
- .input_mux = &alc889_capture_source,
- .setup = alc889_automute_setup,
- .init_hook = alc_automute_amp,
- .unsol_event = alc_automute_amp_unsol_event,
- .need_dac_fix = 1,
- },
- [ALC889_INTEL] = {
- .mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer },
- .init_verbs = { alc885_init_verbs, alc889_init_input_verbs,
- alc889_eapd_verbs, alc_hp15_unsol_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc889_adc_nids),
- .adc_nids = alc889_adc_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .slave_dig_outs = alc883_slave_dig_outs,
- .num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes),
- .channel_mode = alc889_8ch_intel_modes,
- .capsrc_nids = alc889_capsrc_nids,
- .input_mux = &alc889_capture_source,
- .setup = alc889_automute_setup,
- .init_hook = alc889_intel_init_hook,
- .unsol_event = alc_automute_amp_unsol_event,
- .need_dac_fix = 1,
- },
- [ALC883_6ST_DIG] = {
- .mixers = { alc883_base_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
- .channel_mode = alc883_sixstack_modes,
- .input_mux = &alc883_capture_source,
- },
- [ALC883_TARGA_DIG] = {
- .mixers = { alc883_targa_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs,
- alc883_targa_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc883_targa_unsol_event,
- .setup = alc882_targa_setup,
- .init_hook = alc882_targa_automute,
- },
- [ALC883_TARGA_2ch_DIG] = {
- .mixers = { alc883_targa_2ch_mixer},
- .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs,
- alc883_targa_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .adc_nids = alc883_adc_nids_alt,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
- .capsrc_nids = alc883_capsrc_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc883_targa_unsol_event,
- .setup = alc882_targa_setup,
- .init_hook = alc882_targa_automute,
- },
- [ALC883_TARGA_8ch_DIG] = {
- .mixers = { alc883_targa_mixer, alc883_targa_8ch_mixer,
- alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs,
- alc883_targa_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
- .adc_nids = alc883_adc_nids_rev,
- .capsrc_nids = alc883_capsrc_nids_rev,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_4ST_8ch_modes),
- .channel_mode = alc883_4ST_8ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc883_targa_unsol_event,
- .setup = alc882_targa_setup,
- .init_hook = alc882_targa_automute,
- },
- [ALC883_ACER] = {
- .mixers = { alc883_base_mixer },
- /* On TravelMate laptops, GPIO 0 enables the internal speaker
- * and the headphone jack. Turn this on and rely on the
- * standard mute methods whenever the user wants to turn
- * these outputs off.
- */
- .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- },
- [ALC883_ACER_ASPIRE] = {
- .mixers = { alc883_acer_aspire_mixer },
- .init_verbs = { alc883_init_verbs, alc883_acer_eapd_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_acer_aspire_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC888_ACER_ASPIRE_4930G] = {
- .mixers = { alc888_base_mixer,
- alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
- alc888_acer_aspire_4930g_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
- .adc_nids = alc883_adc_nids_rev,
- .capsrc_nids = alc883_capsrc_nids_rev,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .const_channel_count = 6,
- .num_mux_defs =
- ARRAY_SIZE(alc888_2_capture_sources),
- .input_mux = alc888_2_capture_sources,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_acer_aspire_4930g_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC888_ACER_ASPIRE_6530G] = {
- .mixers = { alc888_acer_aspire_6530_mixer },
- .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
- alc888_acer_aspire_6530g_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
- .adc_nids = alc883_adc_nids_rev,
- .capsrc_nids = alc883_capsrc_nids_rev,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .num_mux_defs =
- ARRAY_SIZE(alc888_2_capture_sources),
- .input_mux = alc888_acer_aspire_6530_sources,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_acer_aspire_6530g_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC888_ACER_ASPIRE_8930G] = {
- .mixers = { alc889_acer_aspire_8930g_mixer,
- alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
- alc889_acer_aspire_8930g_verbs,
- alc889_eapd_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc889_adc_nids),
- .adc_nids = alc889_adc_nids,
- .capsrc_nids = alc889_capsrc_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .const_channel_count = 6,
- .num_mux_defs =
- ARRAY_SIZE(alc889_capture_sources),
- .input_mux = alc889_capture_sources,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc889_acer_aspire_8930g_setup,
- .init_hook = alc_automute_amp,
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .power_hook = alc_power_eapd,
-#endif
- },
- [ALC888_ACER_ASPIRE_7730G] = {
- .mixers = { alc883_3ST_6ch_mixer,
- alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
- alc888_acer_aspire_7730G_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
- .adc_nids = alc883_adc_nids_rev,
- .capsrc_nids = alc883_capsrc_nids_rev,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .const_channel_count = 6,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_acer_aspire_6530g_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC883_MEDION] = {
- .mixers = { alc883_fivestack_mixer,
- alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs,
- alc883_medion_eapd_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .adc_nids = alc883_adc_nids_alt,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
- .capsrc_nids = alc883_capsrc_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
- .channel_mode = alc883_sixstack_modes,
- .input_mux = &alc883_capture_source,
- },
- [ALC883_MEDION_MD2] = {
- .mixers = { alc883_medion_md2_mixer},
- .init_verbs = { alc883_init_verbs, alc883_medion_md2_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_medion_md2_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC883_MEDION_WIM2160] = {
- .mixers = { alc883_medion_wim2160_mixer },
- .init_verbs = { alc883_init_verbs, alc883_medion_wim2160_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
- .adc_nids = alc883_adc_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_medion_wim2160_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC883_LAPTOP_EAPD] = {
- .mixers = { alc883_base_mixer },
- .init_verbs = { alc883_init_verbs, alc882_eapd_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- },
- [ALC883_CLEVO_M540R] = {
- .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc883_clevo_m540r_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_clevo_modes),
- .channel_mode = alc883_3ST_6ch_clevo_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- /* This machine has the hardware HP auto-muting, thus
- * we need no software mute via unsol event
- */
- },
- [ALC883_CLEVO_M720] = {
- .mixers = { alc883_clevo_m720_mixer },
- .init_verbs = { alc883_init_verbs, alc883_clevo_m720_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc883_clevo_m720_unsol_event,
- .setup = alc883_clevo_m720_setup,
- .init_hook = alc883_clevo_m720_init_hook,
- },
- [ALC883_LENOVO_101E_2ch] = {
- .mixers = { alc883_lenovo_101e_2ch_mixer},
- .init_verbs = { alc883_init_verbs, alc883_lenovo_101e_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .adc_nids = alc883_adc_nids_alt,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
- .capsrc_nids = alc883_capsrc_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_lenovo_101e_capture_source,
- .unsol_event = alc883_lenovo_101e_unsol_event,
- .init_hook = alc883_lenovo_101e_all_automute,
- },
- [ALC883_LENOVO_NB0763] = {
- .mixers = { alc883_lenovo_nb0763_mixer },
- .init_verbs = { alc883_init_verbs, alc883_lenovo_nb0763_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_lenovo_nb0763_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_medion_md2_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC888_LENOVO_MS7195_DIG] = {
- .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc888_lenovo_ms7195_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc883_lenovo_ms7195_unsol_event,
- .init_hook = alc888_lenovo_ms7195_front_automute,
- },
- [ALC883_HAIER_W66] = {
- .mixers = { alc883_targa_2ch_mixer},
- .init_verbs = { alc883_init_verbs, alc883_haier_w66_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_haier_w66_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC888_3ST_HP] = {
- .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc888_3st_hp_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc888_3st_hp_modes),
- .channel_mode = alc888_3st_hp_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_3st_hp_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC888_6ST_DELL] = {
- .mixers = { alc883_base_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc888_6st_dell_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
- .channel_mode = alc883_sixstack_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_6st_dell_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC883_MITAC] = {
- .mixers = { alc883_mitac_mixer },
- .init_verbs = { alc883_init_verbs, alc883_mitac_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_mitac_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC883_FUJITSU_PI2515] = {
- .mixers = { alc883_2ch_fujitsu_pi2515_mixer },
- .init_verbs = { alc883_init_verbs,
- alc883_2ch_fujitsu_pi2515_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_fujitsu_pi2515_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_2ch_fujitsu_pi2515_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC888_FUJITSU_XA3530] = {
- .mixers = { alc888_base_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs,
- alc888_fujitsu_xa3530_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
- .adc_nids = alc883_adc_nids_rev,
- .capsrc_nids = alc883_capsrc_nids_rev,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc888_4ST_8ch_intel_modes),
- .channel_mode = alc888_4ST_8ch_intel_modes,
- .num_mux_defs =
- ARRAY_SIZE(alc888_2_capture_sources),
- .input_mux = alc888_2_capture_sources,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_fujitsu_xa3530_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC888_LENOVO_SKY] = {
- .mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc888_lenovo_sky_verbs},
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
- .channel_mode = alc883_sixstack_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_lenovo_sky_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc888_lenovo_sky_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC888_ASUS_M90V] = {
- .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs, alc888_asus_m90v_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
- .channel_mode = alc883_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_fujitsu_pi2515_capture_source,
- .unsol_event = alc_sku_unsol_event,
- .setup = alc883_mode2_setup,
- .init_hook = alc_inithook,
- },
- [ALC888_ASUS_EEE1601] = {
- .mixers = { alc883_asus_eee1601_mixer },
- .cap_mixer = alc883_asus_eee1601_cap_mixer,
- .init_verbs = { alc883_init_verbs, alc888_asus_eee1601_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc883_asus_eee1601_capture_source,
- .unsol_event = alc_sku_unsol_event,
- .init_hook = alc883_eee1601_inithook,
- },
- [ALC1200_ASUS_P5Q] = {
- .mixers = { alc883_base_mixer, alc883_chmode_mixer },
- .init_verbs = { alc883_init_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .dig_out_nid = ALC1200_DIGOUT_NID,
- .dig_in_nid = ALC883_DIGIN_NID,
- .slave_dig_outs = alc1200_slave_dig_outs,
- .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
- .channel_mode = alc883_sixstack_modes,
- .input_mux = &alc883_capture_source,
- },
- [ALC889A_MB31] = {
- .mixers = { alc889A_mb31_mixer, alc883_chmode_mixer},
- .init_verbs = { alc883_init_verbs, alc889A_mb31_verbs,
- alc880_gpio1_init_verbs },
- .adc_nids = alc883_adc_nids,
- .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
- .capsrc_nids = alc883_capsrc_nids,
- .dac_nids = alc883_dac_nids,
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .channel_mode = alc889A_mb31_6ch_modes,
- .num_channel_mode = ARRAY_SIZE(alc889A_mb31_6ch_modes),
- .input_mux = &alc889A_mb31_capture_source,
- .dig_out_nid = ALC883_DIGOUT_NID,
- .unsol_event = alc889A_mb31_unsol_event,
- .init_hook = alc889A_mb31_automute,
- },
- [ALC883_SONY_VAIO_TT] = {
- .mixers = { alc883_vaiott_mixer },
- .init_verbs = { alc883_init_verbs, alc883_vaiott_verbs },
- .num_dacs = ARRAY_SIZE(alc883_dac_nids),
- .dac_nids = alc883_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .input_mux = &alc883_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc883_vaiott_setup,
- .init_hook = alc_automute_amp,
- },
-};
-
-
-/*
- * Pin config fixes
- */
-enum {
- PINFIX_ABIT_AW9D_MAX,
- PINFIX_PB_M5210,
- PINFIX_ACER_ASPIRE_7736,
-};
-
-static const struct alc_fixup alc882_fixups[] = {
- [PINFIX_ABIT_AW9D_MAX] = {
- .pins = (const struct alc_pincfg[]) {
- { 0x15, 0x01080104 }, /* side */
- { 0x16, 0x01011012 }, /* rear */
- { 0x17, 0x01016011 }, /* clfe */
- { }
- }
- },
- [PINFIX_PB_M5210] = {
- .verbs = (const struct hda_verb[]) {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
- {}
- }
- },
- [PINFIX_ACER_ASPIRE_7736] = {
- .sku = ALC_FIXUP_SKU_IGNORE,
- },
-};
-
-static struct snd_pci_quirk alc882_fixup_tbl[] = {
- SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", PINFIX_PB_M5210),
- SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX),
- SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736),
- {}
-};
-
-/*
- * BIOS auto configuration
- */
-static int alc882_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x23, 0x22);
-}
-
-static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- hda_nid_t dac)
-{
- int idx;
-
- /* set as output */
- alc_set_pin_output(codec, nid, pin_type);
-
- if (dac == 0x25)
- idx = 4;
- else if (dac >= 0x02 && dac <= 0x05)
- idx = dac - 2;
- else
- return;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
-}
-
-static void alc882_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i <= HDA_SIDE; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- if (nid)
- alc882_auto_set_output_and_unmute(codec, nid, pin_type,
- spec->multiout.dac_nids[i]);
- }
-}
-
-static void alc882_auto_init_hp_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t pin, dac;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
- pin = spec->autocfg.hp_pins[i];
- if (!pin)
- break;
- dac = spec->multiout.hp_nid;
- if (!dac)
- dac = spec->multiout.dac_nids[0]; /* to front */
- alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac);
- }
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
- pin = spec->autocfg.speaker_pins[i];
- if (!pin)
- break;
- dac = spec->multiout.extra_out_nid[0];
- if (!dac)
- dac = spec->multiout.dac_nids[0]; /* to front */
- alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac);
- }
-}
-
-static void alc882_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
-}
-
-static void alc882_auto_init_input_src(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int c;
-
- for (c = 0; c < spec->num_adc_nids; c++) {
- hda_nid_t conn_list[HDA_MAX_NUM_INPUTS];
- hda_nid_t nid = spec->capsrc_nids[c];
- unsigned int mux_idx;
- const struct hda_input_mux *imux;
- int conns, mute, idx, item;
-
- conns = snd_hda_get_connections(codec, nid, conn_list,
- ARRAY_SIZE(conn_list));
- if (conns < 0)
- continue;
- mux_idx = c >= spec->num_mux_defs ? 0 : c;
- imux = &spec->input_mux[mux_idx];
- if (!imux->num_items && mux_idx > 0)
- imux = &spec->input_mux[0];
- for (idx = 0; idx < conns; idx++) {
- /* if the current connection is the selected one,
- * unmute it as default - otherwise mute it
- */
- mute = AMP_IN_MUTE(idx);
- for (item = 0; item < imux->num_items; item++) {
- if (imux->items[item].index == idx) {
- if (spec->cur_mux[c] == item)
- mute = AMP_IN_UNMUTE(idx);
- break;
- }
- }
- /* check if we have a selector or mixer
- * we could check for the widget type instead, but
- * just check for Amp-In presence (in case of mixer
- * without amp-in there is something wrong, this
- * function shouldn't be used or capsrc nid is wrong)
- */
- if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- mute);
- else if (mute != AMP_IN_MUTE(idx))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL,
- idx);
- }
- }
-}
-
-/* add mic boosts if needed */
-static int alc_auto_add_mic_boost(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, err;
- hda_nid_t nid;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- if (cfg->inputs[i].type > AUTO_PIN_MIC)
- break;
- nid = cfg->inputs[i].pin;
- if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
- char label[32];
- snprintf(label, sizeof(label), "%s Boost",
- hda_get_autocfg_input_label(codec, cfg, i));
- err = add_control(spec, ALC_CTL_WIDGET_VOL, label, 0,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-/* almost identical with ALC880 parser... */
-static int alc882_parse_auto_config(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- static hda_nid_t alc882_ignore[] = { 0x1d, 0 };
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc882_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs)
- return 0; /* can't find valid BIOS pin config */
-
- err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0],
- "Headphone");
- if (err < 0)
- return err;
- err = alc880_auto_create_extra_out(spec,
- spec->autocfg.speaker_pins[0],
- "Speaker");
- if (err < 0)
- return err;
- err = alc882_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc883_auto_init_verbs);
- /* if ADC 0x07 is available, initialize it, too */
- if (get_wcaps_type(get_wcaps(codec, 0x07)) == AC_WID_AUD_IN)
- add_verb(spec, alc882_adc1_init_verbs);
-
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
-
- return 1; /* config found */
-}
-
-/* additional initialization for auto-configuration model */
-static void alc882_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc882_auto_init_multi_out(codec);
- alc882_auto_init_hp_out(codec);
- alc882_auto_init_analog_input(codec);
- alc882_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-static int patch_alc882(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- switch (codec->vendor_id) {
- case 0x10ec0882:
- case 0x10ec0885:
- break;
- default:
- /* ALC883 and variants */
- alc_fix_pll_init(codec, 0x20, 0x0a, 10);
- break;
- }
-
- board_config = snd_hda_check_board_config(codec, ALC882_MODEL_LAST,
- alc882_models,
- alc882_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC882_MODEL_LAST)
- board_config = snd_hda_check_board_codec_sid_config(codec,
- ALC882_MODEL_LAST, alc882_models, alc882_ssid_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC882_MODEL_LAST) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC882_AUTO;
- }
-
- if (board_config == ALC882_AUTO)
- alc_pick_fixup(codec, alc882_fixup_tbl, alc882_fixups, 1);
-
- alc_auto_parse_customize_define(codec);
-
- if (board_config == ALC882_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc882_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC882_3ST_DIG;
- }
- }
-
- if (has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
- }
-
- if (board_config != ALC882_AUTO)
- setup_preset(codec, &alc882_presets[board_config]);
-
- spec->stream_analog_playback = &alc882_pcm_analog_playback;
- spec->stream_analog_capture = &alc882_pcm_analog_capture;
- /* FIXME: setup DAC5 */
- /*spec->stream_analog_alt_playback = &alc880_pcm_analog_alt_playback;*/
- spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
-
- spec->stream_digital_playback = &alc882_pcm_digital_playback;
- spec->stream_digital_capture = &alc882_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- int i, j;
- spec->num_adc_nids = 0;
- for (i = 0; i < ARRAY_SIZE(alc882_adc_nids); i++) {
- const struct hda_input_mux *imux = spec->input_mux;
- hda_nid_t cap;
- hda_nid_t items[16];
- hda_nid_t nid = alc882_adc_nids[i];
- unsigned int wcap = get_wcaps(codec, nid);
- /* get type */
- wcap = get_wcaps_type(wcap);
- if (wcap != AC_WID_AUD_IN)
- continue;
- spec->private_adc_nids[spec->num_adc_nids] = nid;
- err = snd_hda_get_connections(codec, nid, &cap, 1);
- if (err < 0)
- continue;
- err = snd_hda_get_connections(codec, cap, items,
- ARRAY_SIZE(items));
- if (err < 0)
- continue;
- for (j = 0; j < imux->num_items; j++)
- if (imux->items[j].index >= err)
- break;
- if (j < imux->num_items)
- continue;
- spec->private_capsrc_nids[spec->num_adc_nids] = cap;
- spec->num_adc_nids++;
- }
- spec->adc_nids = spec->private_adc_nids;
- spec->capsrc_nids = spec->private_capsrc_nids;
- }
-
- set_capture_mixer(codec);
-
- if (has_cdefine_beep(codec))
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
-
- if (board_config == ALC882_AUTO)
- alc_pick_fixup(codec, alc882_fixup_tbl, alc882_fixups, 0);
-
- spec->vmaster_nid = 0x0c;
-
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC882_AUTO)
- spec->init_hook = alc882_auto_init;
-
- alc_init_jacks(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc882_loopbacks;
-#endif
-
- return 0;
-}
-
-
-/*
- * ALC262 support
- */
-
-#define ALC262_DIGOUT_NID ALC880_DIGOUT_NID
-#define ALC262_DIGIN_NID ALC880_DIGIN_NID
-
-#define alc262_dac_nids alc260_dac_nids
-#define alc262_adc_nids alc882_adc_nids
-#define alc262_adc_nids_alt alc882_adc_nids_alt
-#define alc262_capsrc_nids alc882_capsrc_nids
-#define alc262_capsrc_nids_alt alc882_capsrc_nids_alt
-
-#define alc262_modes alc260_modes
-#define alc262_capture_source alc882_capture_source
-
-static hda_nid_t alc262_dmic_adc_nids[1] = {
- /* ADC0 */
- 0x09
-};
-
-static hda_nid_t alc262_dmic_capsrc_nids[1] = { 0x22 };
-
-static struct snd_kcontrol_new alc262_base_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-/* update HP, line and mono-out pins according to the master switch */
-static void alc262_hp_master_update(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int val = spec->master_sw;
-
- /* HP & line-out */
- snd_hda_codec_write_cache(codec, 0x1b, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- val ? PIN_HP : 0);
- snd_hda_codec_write_cache(codec, 0x15, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- val ? PIN_HP : 0);
- /* mono (speaker) depending on the HP jack sense */
- val = val && !spec->jack_present;
- snd_hda_codec_write_cache(codec, 0x16, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- val ? PIN_OUT : 0);
-}
-
-static void alc262_hp_bpc_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->jack_present = snd_hda_jack_detect(codec, 0x1b);
- alc262_hp_master_update(codec);
-}
-
-static void alc262_hp_bpc_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) != ALC880_HP_EVENT)
- return;
- alc262_hp_bpc_automute(codec);
-}
-
-static void alc262_hp_wildwest_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->jack_present = snd_hda_jack_detect(codec, 0x15);
- alc262_hp_master_update(codec);
-}
-
-static void alc262_hp_wildwest_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) != ALC880_HP_EVENT)
- return;
- alc262_hp_wildwest_automute(codec);
-}
-
-#define alc262_hp_master_sw_get alc260_hp_master_sw_get
-
-static int alc262_hp_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- int val = !!*ucontrol->value.integer.value;
-
- if (val == spec->master_sw)
- return 0;
- spec->master_sw = val;
- alc262_hp_master_update(codec);
- return 1;
-}
-
-#define ALC262_HP_MASTER_SWITCH \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Master Playback Switch", \
- .info = snd_ctl_boolean_mono_info, \
- .get = alc262_hp_master_sw_get, \
- .put = alc262_hp_master_sw_put, \
- }, \
- { \
- .iface = NID_MAPPING, \
- .name = "Master Playback Switch", \
- .private_value = 0x15 | (0x16 << 8) | (0x1b << 16), \
- }
-
-
-static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
- ALC262_HP_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("AUX IN Playback Volume", 0x0b, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("AUX IN Playback Switch", 0x0b, 0x06, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = {
- ALC262_HP_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x1a, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = {
- HDA_CODEC_VOLUME("Rear Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Rear Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Rear Mic Boost", 0x18, 0, HDA_INPUT),
- { } /* end */
-};
-
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_hp_t5735_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_verb alc262_hp_t5735_verbs[] = {
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { }
-};
-
-static struct snd_kcontrol_new alc262_hp_rp5700_mixer[] = {
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_verb alc262_hp_rp5700_verbs[] = {
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))},
- {}
-};
-
-static struct hda_input_mux alc262_hp_rp5700_capture_source = {
- .num_items = 1,
- .items = {
- { "Line", 0x1 },
- },
-};
-
-/* bind hp and internal speaker mute (with plug check) as master switch */
-static void alc262_hippo_master_update(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
- hda_nid_t line_nid = spec->autocfg.line_out_pins[0];
- hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0];
- unsigned int mute;
-
- /* HP */
- mute = spec->master_sw ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, hp_nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- /* mute internal speaker per jack sense */
- if (spec->jack_present)
- mute = HDA_AMP_MUTE;
- if (line_nid)
- snd_hda_codec_amp_stereo(codec, line_nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- if (speaker_nid && speaker_nid != line_nid)
- snd_hda_codec_amp_stereo(codec, speaker_nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
-}
-
-#define alc262_hippo_master_sw_get alc262_hp_master_sw_get
-
-static int alc262_hippo_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- int val = !!*ucontrol->value.integer.value;
-
- if (val == spec->master_sw)
- return 0;
- spec->master_sw = val;
- alc262_hippo_master_update(codec);
- return 1;
-}
-
-#define ALC262_HIPPO_MASTER_SWITCH \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Master Playback Switch", \
- .info = snd_ctl_boolean_mono_info, \
- .get = alc262_hippo_master_sw_get, \
- .put = alc262_hippo_master_sw_put, \
- }, \
- { \
- .iface = NID_MAPPING, \
- .name = "Master Playback Switch", \
- .subdevice = SUBDEV_HP(0) | (SUBDEV_LINE(0) << 8) | \
- (SUBDEV_SPEAKER(0) << 16), \
- }
-
-static struct snd_kcontrol_new alc262_hippo_mixer[] = {
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc262_hippo1_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- { } /* end */
-};
-
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_hippo_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
-
- spec->jack_present = snd_hda_jack_detect(codec, hp_nid);
- alc262_hippo_master_update(codec);
-}
-
-static void alc262_hippo_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- if ((res >> 26) != ALC880_HP_EVENT)
- return;
- alc262_hippo_automute(codec);
-}
-
-static void alc262_hippo_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-static void alc262_hippo1_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-
-static struct snd_kcontrol_new alc262_sony_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc262_benq_t31_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc262_tyan_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Aux Playback Volume", 0x0b, 0x06, HDA_INPUT),
- HDA_CODEC_MUTE("Aux Playback Switch", 0x0b, 0x06, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_verb alc262_tyan_verbs[] = {
- /* Headphone automute */
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* P11 AUX_IN, white 4-pin connector */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, 0xe1},
- {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, 0x93},
- {0x14, AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, 0x19},
-
- {}
-};
-
-/* unsolicited event for HP jack sensing */
-static void alc262_tyan_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x15;
-}
-
-
-#define alc262_capture_mixer alc882_capture_mixer
-#define alc262_capture_alt_mixer alc882_capture_alt_mixer
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc262_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for
- * front panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /*
- * Set up output mixers (0x0c - 0x0e)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
-
- { }
-};
-
-static struct hda_verb alc262_eapd_verbs[] = {
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-static struct hda_verb alc262_hippo1_unsol_verbs[] = {
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {}
-};
-
-static struct hda_verb alc262_sony_unsol_verbs[] = {
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, // Front Mic
-
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {}
-};
-
-static struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_verb alc262_toshiba_s06_verbs[] = {
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x09},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static void alc262_toshiba_s06_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x12;
- spec->int_mic.mux_idx = 9;
- spec->auto_mic = 1;
-}
-
-/*
- * nec model
- * 0x15 = headphone
- * 0x16 = internal speaker
- * 0x18 = external mic
- */
-
-static struct snd_kcontrol_new alc262_nec_mixer[] = {
- HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 0, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct hda_verb alc262_nec_verbs[] = {
- /* Unmute Speaker */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Headphone */
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-
- /* External mic to headphone */
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* External mic to speaker */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {}
-};
-
-/*
- * fujitsu model
- * 0x14 = headphone/spdif-out, 0x15 = internal speaker,
- * 0x1b = port replicator headphone out
- */
-
-#define ALC_HP_EVENT 0x37
-
-static struct hda_verb alc262_fujitsu_unsol_verbs[] = {
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {}
-};
-
-static struct hda_verb alc262_lenovo_3000_unsol_verbs[] = {
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {}
-};
-
-static struct hda_verb alc262_lenovo_3000_init_verbs[] = {
- /* Front Mic pin: input vref at 50% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {}
-};
-
-static struct hda_input_mux alc262_fujitsu_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Int Mic", 0x1 },
- { "CD", 0x4 },
- },
-};
-
-static struct hda_input_mux alc262_HP_capture_source = {
- .num_items = 5,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- { "AUX IN", 0x6 },
- },
-};
-
-static struct hda_input_mux alc262_HP_D7000_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x2 },
- { "Line", 0x1 },
- { "CD", 0x4 },
- },
-};
-
-/* mute/unmute internal speaker according to the hp jacks and mute state */
-static void alc262_fujitsu_automute(struct hda_codec *codec, int force)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int mute;
-
- if (force || !spec->sense_updated) {
- spec->jack_present = snd_hda_jack_detect(codec, 0x14) ||
- snd_hda_jack_detect(codec, 0x1b);
- spec->sense_updated = 1;
- }
- /* unmute internal speaker only if both HPs are unplugged and
- * master switch is on
- */
- if (spec->jack_present)
- mute = HDA_AMP_MUTE;
- else
- mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
-}
-
-/* unsolicited event for HP jack sensing */
-static void alc262_fujitsu_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) != ALC_HP_EVENT)
- return;
- alc262_fujitsu_automute(codec, 1);
-}
-
-static void alc262_fujitsu_init_hook(struct hda_codec *codec)
-{
- alc262_fujitsu_automute(codec, 1);
-}
-
-/* bind volumes of both NID 0x0c and 0x0d */
-static struct hda_bind_ctls alc262_fujitsu_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x0d, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_lenovo_3000_automute(struct hda_codec *codec, int force)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int mute;
-
- if (force || !spec->sense_updated) {
- spec->jack_present = snd_hda_jack_detect(codec, 0x1b);
- spec->sense_updated = 1;
- }
- if (spec->jack_present) {
- /* mute internal speaker */
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- } else {
- /* unmute internal speaker if necessary */
- mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0);
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- }
-}
-
-/* unsolicited event for HP jack sensing */
-static void alc262_lenovo_3000_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) != ALC_HP_EVENT)
- return;
- alc262_lenovo_3000_automute(codec, 1);
-}
-
-static int amp_stereo_mute_update(struct hda_codec *codec, hda_nid_t nid,
- int dir, int idx, long *valp)
-{
- int i, change = 0;
-
- for (i = 0; i < 2; i++, valp++)
- change |= snd_hda_codec_amp_update(codec, nid, i, dir, idx,
- HDA_AMP_MUTE,
- *valp ? 0 : HDA_AMP_MUTE);
- return change;
-}
-
-/* bind hp and internal speaker mute (with plug check) */
-static int alc262_fujitsu_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int change;
-
- change = amp_stereo_mute_update(codec, 0x14, HDA_OUTPUT, 0, valp);
- change |= amp_stereo_mute_update(codec, 0x1b, HDA_OUTPUT, 0, valp);
- if (change)
- alc262_fujitsu_automute(codec, 0);
- return change;
-}
-
-static struct snd_kcontrol_new alc262_fujitsu_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = alc262_fujitsu_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- },
- {
- .iface = NID_MAPPING,
- .name = "Master Playback Switch",
- .private_value = 0x1b,
- },
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-/* bind hp and internal speaker mute (with plug check) */
-static int alc262_lenovo_3000_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int change;
-
- change = amp_stereo_mute_update(codec, 0x1b, HDA_OUTPUT, 0, valp);
- if (change)
- alc262_lenovo_3000_automute(codec, 0);
- return change;
-}
-
-static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = alc262_lenovo_3000_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc262_toshiba_rx1_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol),
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- { } /* end */
-};
-
-/* additional init verbs for Benq laptops */
-static struct hda_verb alc262_EAPD_verbs[] = {
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3070},
- {}
-};
-
-static struct hda_verb alc262_benq_t31_EAPD_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
-
- {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
- {0x20, AC_VERB_SET_PROC_COEF, 0x3050},
- {}
-};
-
-/* Samsung Q1 Ultra Vista model setup */
-static struct snd_kcontrol_new alc262_ultra_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Mic Boost", 0x15, 0, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_verb alc262_ultra_verbs[] = {
- /* output mixer */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* speaker */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* HP */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- /* internal mic */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* ADC, choose mic */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(8)},
- {}
-};
-
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_ultra_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int mute;
-
- mute = 0;
- /* auto-mute only when HP is used as HP */
- if (!spec->cur_mux[0]) {
- spec->jack_present = snd_hda_jack_detect(codec, 0x15);
- if (spec->jack_present)
- mute = HDA_AMP_MUTE;
- }
- /* mute/unmute internal speaker */
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- /* mute/unmute HP */
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute ? 0 : HDA_AMP_MUTE);
-}
-
-/* unsolicited event for HP jack sensing */
-static void alc262_ultra_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) != ALC880_HP_EVENT)
- return;
- alc262_ultra_automute(codec);
-}
-
-static struct hda_input_mux alc262_ultra_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x1 },
- { "Headphone", 0x7 },
- },
-};
-
-static int alc262_ultra_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct alc_spec *spec = codec->spec;
- int ret;
-
- ret = alc_mux_enum_put(kcontrol, ucontrol);
- if (!ret)
- return 0;
- /* reprogram the HP pin as mic or HP according to the input source */
- snd_hda_codec_write_cache(codec, 0x15, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- spec->cur_mux[0] ? PIN_VREF80 : PIN_HP);
- alc262_ultra_automute(codec); /* mute/unmute HP */
- return ret;
-}
-
-static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc262_ultra_mux_enum_put,
- },
- {
- .iface = NID_MAPPING,
- .name = "Capture Source",
- .private_value = 0x15,
- },
- { } /* end */
-};
-
-/* We use two mixers depending on the output pin; 0x16 is a mono output
- * and thus it's bound with a different mixer.
- * This function returns which mixer amp should be used.
- */
-static int alc262_check_volbit(hda_nid_t nid)
-{
- if (!nid)
- return 0;
- else if (nid == 0x16)
- return 2;
- else
- return 1;
-}
-
-static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
- const char *pfx, int *vbits, int idx)
-{
- unsigned long val;
- int vbit;
-
- vbit = alc262_check_volbit(nid);
- if (!vbit)
- return 0;
- if (*vbits & vbit) /* a volume control for this mixer already there */
- return 0;
- *vbits |= vbit;
- if (vbit == 2)
- val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT);
- else
- val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT);
- return __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, idx, val);
-}
-
-static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
- const char *pfx, int idx)
-{
- unsigned long val;
-
- if (!nid)
- return 0;
- if (nid == 0x16)
- val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
- else
- val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, idx, val);
-}
-
-/* add playback controls from the parsed DAC table */
-static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- const char *pfx;
- int vbits;
- int i, err;
-
- spec->multiout.num_dacs = 1; /* only use one dac */
- spec->multiout.dac_nids = spec->private_dac_nids;
- spec->multiout.dac_nids[0] = 2;
-
- if (!cfg->speaker_pins[0] && !cfg->hp_pins[0])
- pfx = "Master";
- else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- pfx = "Speaker";
- else if (cfg->line_out_type == AUTO_PIN_HP_OUT)
- pfx = "Headphone";
- else
- pfx = "Front";
- for (i = 0; i < 2; i++) {
- err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx, i);
- if (err < 0)
- return err;
- if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
- err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[i],
- "Speaker", i);
- if (err < 0)
- return err;
- }
- if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
- err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[i],
- "Headphone", i);
- if (err < 0)
- return err;
- }
- }
-
- vbits = alc262_check_volbit(cfg->line_out_pins[0]) |
- alc262_check_volbit(cfg->speaker_pins[0]) |
- alc262_check_volbit(cfg->hp_pins[0]);
- if (vbits == 1 || vbits == 2)
- pfx = "Master"; /* only one mixer is used */
- vbits = 0;
- for (i = 0; i < 2; i++) {
- err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[i], pfx,
- &vbits, i);
- if (err < 0)
- return err;
- if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
- err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[i],
- "Speaker", &vbits, i);
- if (err < 0)
- return err;
- }
- if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
- err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[i],
- "Headphone", &vbits, i);
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-#define alc262_auto_create_input_ctls \
- alc882_auto_create_input_ctls
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc262_volume_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for
- * front panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /*
- * Set up output mixers (0x0c - 0x0f)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
-
- { }
-};
-
-static struct hda_verb alc262_HP_BPC_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for
- * front panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
-
- /*
- * Set up output mixers (0x0c - 0x0e)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
-
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 0b, 12 */
- /* Input mixer1: only unmute Mic */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))},
-
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-
- { }
-};
-
-static struct hda_verb alc262_HP_BPC_WildWest_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- * Note: PASD motherboards uses the Line In 2 as the input for front
- * panel mic (mic 2)
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
- /*
- * Set up output mixers (0x0c - 0x0e)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
-
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Mono */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* rear MIC */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* Line in */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Front MIC */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Line out */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD in */
-
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
-
- /* {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 },
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, /*rear MIC*/
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, /*Line in*/
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, /*F MIC*/
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, /*Front*/
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, /*CD*/
- /* {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, /*HP*/
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
- /* {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
- /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))},
-
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-
- { }
-};
-
-static struct hda_verb alc262_toshiba_rx1_unsol_verbs[] = {
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Front Speaker */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x01},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* MIC jack */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Front MIC */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) },
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) },
-
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP jack */
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-/*
- * Pin config fixes
- */
-enum {
- PINFIX_FSC_H270,
-};
-
-static const struct alc_fixup alc262_fixups[] = {
- [PINFIX_FSC_H270] = {
- .pins = (const struct alc_pincfg[]) {
- { 0x14, 0x99130110 }, /* speaker */
- { 0x15, 0x0221142f }, /* front HP */
- { 0x1b, 0x0121141f }, /* rear HP */
- { }
- }
- },
- [PINFIX_PB_M5210] = {
- .verbs = (const struct hda_verb[]) {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
- {}
- }
- },
-};
-
-static struct snd_pci_quirk alc262_fixup_tbl[] = {
- SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", PINFIX_FSC_H270),
- {}
-};
-
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc262_loopbacks alc880_loopbacks
-#endif
-
-/* pcm configuration: identical with ALC880 */
-#define alc262_pcm_analog_playback alc880_pcm_analog_playback
-#define alc262_pcm_analog_capture alc880_pcm_analog_capture
-#define alc262_pcm_digital_playback alc880_pcm_digital_playback
-#define alc262_pcm_digital_capture alc880_pcm_digital_capture
-
-/*
- * BIOS auto configuration
- */
-static int alc262_parse_auto_config(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc262_ignore[] = { 0x1d, 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc262_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs) {
- if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) {
- spec->multiout.max_channels = 2;
- spec->no_analog = 1;
- goto dig_only;
- }
- return 0; /* can't find valid BIOS pin config */
- }
- err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc262_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- dig_only:
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc262_volume_init_verbs);
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
-
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
- return 1;
-}
-
-#define alc262_auto_init_multi_out alc882_auto_init_multi_out
-#define alc262_auto_init_hp_out alc882_auto_init_hp_out
-#define alc262_auto_init_analog_input alc882_auto_init_analog_input
-#define alc262_auto_init_input_src alc882_auto_init_input_src
-
-
-/* init callback for auto-configuration model -- overriding the default init */
-static void alc262_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc262_auto_init_multi_out(codec);
- alc262_auto_init_hp_out(codec);
- alc262_auto_init_analog_input(codec);
- alc262_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-/*
- * configuration and preset
- */
-static const char *alc262_models[ALC262_MODEL_LAST] = {
- [ALC262_BASIC] = "basic",
- [ALC262_HIPPO] = "hippo",
- [ALC262_HIPPO_1] = "hippo_1",
- [ALC262_FUJITSU] = "fujitsu",
- [ALC262_HP_BPC] = "hp-bpc",
- [ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000",
- [ALC262_HP_TC_T5735] = "hp-tc-t5735",
- [ALC262_HP_RP5700] = "hp-rp5700",
- [ALC262_BENQ_ED8] = "benq",
- [ALC262_BENQ_T31] = "benq-t31",
- [ALC262_SONY_ASSAMD] = "sony-assamd",
- [ALC262_TOSHIBA_S06] = "toshiba-s06",
- [ALC262_TOSHIBA_RX1] = "toshiba-rx1",
- [ALC262_ULTRA] = "ultra",
- [ALC262_LENOVO_3000] = "lenovo-3000",
- [ALC262_NEC] = "nec",
- [ALC262_TYAN] = "tyan",
- [ALC262_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc262_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO),
- SND_PCI_QUIRK(0x1033, 0x8895, "NEC Versa S9100", ALC262_NEC),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1200, "HP xw series",
- ALC262_HP_BPC),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1300, "HP xw series",
- ALC262_HP_BPC),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1700, "HP xw series",
- ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL),
- SND_PCI_QUIRK(0x103c, 0x2801, "HP D7000", ALC262_HP_BPC_D7000_WF),
- SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL),
- SND_PCI_QUIRK(0x103c, 0x2803, "HP D7000", ALC262_HP_BPC_D7000_WF),
- SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL),
- SND_PCI_QUIRK(0x103c, 0x2805, "HP D7000", ALC262_HP_BPC_D7000_WF),
- SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL),
- SND_PCI_QUIRK(0x103c, 0x2807, "HP D7000", ALC262_HP_BPC_D7000_WF),
- SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x302f, "HP Thin Client T5735",
- ALC262_HP_TC_T5735),
- SND_PCI_QUIRK(0x103c, 0x2817, "HP RP5700", ALC262_HP_RP5700),
- SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD),
- SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO),
- SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
- SND_PCI_QUIRK(0x104d, 0x9016, "Sony VAIO", ALC262_AUTO), /* dig-only */
- SND_PCI_QUIRK(0x104d, 0x9025, "Sony VAIO Z21MN", ALC262_TOSHIBA_S06),
- SND_PCI_QUIRK(0x104d, 0x9035, "Sony VAIO VGN-FW170J", ALC262_AUTO),
- SND_PCI_QUIRK(0x104d, 0x9047, "Sony VAIO Type G", ALC262_AUTO),
-#if 0 /* disable the quirk since model=auto works better in recent versions */
- SND_PCI_QUIRK_MASK(0x104d, 0xff00, 0x9000, "Sony VAIO",
- ALC262_SONY_ASSAMD),
-#endif
- SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1",
- ALC262_TOSHIBA_RX1),
- SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06),
- SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),
- SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU),
- SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_TYAN),
- SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc032, "Samsung Q1",
- ALC262_ULTRA),
- SND_PCI_QUIRK(0x144d, 0xc510, "Samsung Q45", ALC262_HIPPO),
- SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000 y410", ALC262_LENOVO_3000),
- SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8),
- SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31),
- SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1),
- {}
-};
-
-static struct alc_config_preset alc262_presets[] = {
- [ALC262_BASIC] = {
- .mixers = { alc262_base_mixer },
- .init_verbs = { alc262_init_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- },
- [ALC262_HIPPO] = {
- .mixers = { alc262_hippo_mixer },
- .init_verbs = { alc262_init_verbs, alc_hp15_unsol_verbs},
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .dig_out_nid = ALC262_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc262_hippo_unsol_event,
- .setup = alc262_hippo_setup,
- .init_hook = alc262_hippo_automute,
- },
- [ALC262_HIPPO_1] = {
- .mixers = { alc262_hippo1_mixer },
- .init_verbs = { alc262_init_verbs, alc262_hippo1_unsol_verbs},
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x02,
- .dig_out_nid = ALC262_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc262_hippo_unsol_event,
- .setup = alc262_hippo1_setup,
- .init_hook = alc262_hippo_automute,
- },
- [ALC262_FUJITSU] = {
- .mixers = { alc262_fujitsu_mixer },
- .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs,
- alc262_fujitsu_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .dig_out_nid = ALC262_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_fujitsu_capture_source,
- .unsol_event = alc262_fujitsu_unsol_event,
- .init_hook = alc262_fujitsu_init_hook,
- },
- [ALC262_HP_BPC] = {
- .mixers = { alc262_HP_BPC_mixer },
- .init_verbs = { alc262_HP_BPC_init_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_HP_capture_source,
- .unsol_event = alc262_hp_bpc_unsol_event,
- .init_hook = alc262_hp_bpc_automute,
- },
- [ALC262_HP_BPC_D7000_WF] = {
- .mixers = { alc262_HP_BPC_WildWest_mixer },
- .init_verbs = { alc262_HP_BPC_WildWest_init_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_HP_D7000_capture_source,
- .unsol_event = alc262_hp_wildwest_unsol_event,
- .init_hook = alc262_hp_wildwest_automute,
- },
- [ALC262_HP_BPC_D7000_WL] = {
- .mixers = { alc262_HP_BPC_WildWest_mixer,
- alc262_HP_BPC_WildWest_option_mixer },
- .init_verbs = { alc262_HP_BPC_WildWest_init_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_HP_D7000_capture_source,
- .unsol_event = alc262_hp_wildwest_unsol_event,
- .init_hook = alc262_hp_wildwest_automute,
- },
- [ALC262_HP_TC_T5735] = {
- .mixers = { alc262_hp_t5735_mixer },
- .init_verbs = { alc262_init_verbs, alc262_hp_t5735_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc_sku_unsol_event,
- .setup = alc262_hp_t5735_setup,
- .init_hook = alc_inithook,
- },
- [ALC262_HP_RP5700] = {
- .mixers = { alc262_hp_rp5700_mixer },
- .init_verbs = { alc262_init_verbs, alc262_hp_rp5700_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_hp_rp5700_capture_source,
- },
- [ALC262_BENQ_ED8] = {
- .mixers = { alc262_base_mixer },
- .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- },
- [ALC262_SONY_ASSAMD] = {
- .mixers = { alc262_sony_mixer },
- .init_verbs = { alc262_init_verbs, alc262_sony_unsol_verbs},
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x02,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc262_hippo_unsol_event,
- .setup = alc262_hippo_setup,
- .init_hook = alc262_hippo_automute,
- },
- [ALC262_BENQ_T31] = {
- .mixers = { alc262_benq_t31_mixer },
- .init_verbs = { alc262_init_verbs, alc262_benq_t31_EAPD_verbs,
- alc_hp15_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc262_hippo_unsol_event,
- .setup = alc262_hippo_setup,
- .init_hook = alc262_hippo_automute,
- },
- [ALC262_ULTRA] = {
- .mixers = { alc262_ultra_mixer },
- .cap_mixer = alc262_ultra_capture_mixer,
- .init_verbs = { alc262_ultra_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_ultra_capture_source,
- .adc_nids = alc262_adc_nids, /* ADC0 */
- .capsrc_nids = alc262_capsrc_nids,
- .num_adc_nids = 1, /* single ADC */
- .unsol_event = alc262_ultra_unsol_event,
- .init_hook = alc262_ultra_automute,
- },
- [ALC262_LENOVO_3000] = {
- .mixers = { alc262_lenovo_3000_mixer },
- .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs,
- alc262_lenovo_3000_unsol_verbs,
- alc262_lenovo_3000_init_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .dig_out_nid = ALC262_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_fujitsu_capture_source,
- .unsol_event = alc262_lenovo_3000_unsol_event,
- },
- [ALC262_NEC] = {
- .mixers = { alc262_nec_mixer },
- .init_verbs = { alc262_nec_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- },
- [ALC262_TOSHIBA_S06] = {
- .mixers = { alc262_toshiba_s06_mixer },
- .init_verbs = { alc262_init_verbs, alc262_toshiba_s06_verbs,
- alc262_eapd_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .capsrc_nids = alc262_dmic_capsrc_nids,
- .dac_nids = alc262_dac_nids,
- .adc_nids = alc262_dmic_adc_nids, /* ADC0 */
- .num_adc_nids = 1, /* single ADC */
- .dig_out_nid = ALC262_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .unsol_event = alc_sku_unsol_event,
- .setup = alc262_toshiba_s06_setup,
- .init_hook = alc_inithook,
- },
- [ALC262_TOSHIBA_RX1] = {
- .mixers = { alc262_toshiba_rx1_mixer },
- .init_verbs = { alc262_init_verbs, alc262_toshiba_rx1_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc262_hippo_unsol_event,
- .setup = alc262_hippo_setup,
- .init_hook = alc262_hippo_automute,
- },
- [ALC262_TYAN] = {
- .mixers = { alc262_tyan_mixer },
- .init_verbs = { alc262_init_verbs, alc262_tyan_verbs},
- .num_dacs = ARRAY_SIZE(alc262_dac_nids),
- .dac_nids = alc262_dac_nids,
- .hp_nid = 0x02,
- .dig_out_nid = ALC262_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc262_modes),
- .channel_mode = alc262_modes,
- .input_mux = &alc262_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc262_tyan_setup,
- .init_hook = alc_automute_amp,
- },
-};
-
-static int patch_alc262(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int board_config;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-#if 0
- /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is
- * under-run
- */
- {
- int tmp;
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7);
- tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0);
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7);
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80);
- }
-#endif
- alc_auto_parse_customize_define(codec);
-
- alc_fix_pll_init(codec, 0x20, 0x0a, 10);
-
- board_config = snd_hda_check_board_config(codec, ALC262_MODEL_LAST,
- alc262_models,
- alc262_cfg_tbl);
-
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC262_AUTO;
- }
-
- if (board_config == ALC262_AUTO)
- alc_pick_fixup(codec, alc262_fixup_tbl, alc262_fixups, 1);
-
- if (board_config == ALC262_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc262_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC262_BASIC;
- }
- }
-
- if (!spec->no_analog && has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
- }
-
- if (board_config != ALC262_AUTO)
- setup_preset(codec, &alc262_presets[board_config]);
-
- spec->stream_analog_playback = &alc262_pcm_analog_playback;
- spec->stream_analog_capture = &alc262_pcm_analog_capture;
-
- spec->stream_digital_playback = &alc262_pcm_digital_playback;
- spec->stream_digital_capture = &alc262_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- int i;
- /* check whether the digital-mic has to be supported */
- for (i = 0; i < spec->input_mux->num_items; i++) {
- if (spec->input_mux->items[i].index >= 9)
- break;
- }
- if (i < spec->input_mux->num_items) {
- /* use only ADC0 */
- spec->adc_nids = alc262_dmic_adc_nids;
- spec->num_adc_nids = 1;
- spec->capsrc_nids = alc262_dmic_capsrc_nids;
- } else {
- /* all analog inputs */
- /* check whether NID 0x07 is valid */
- unsigned int wcap = get_wcaps(codec, 0x07);
-
- /* get type */
- wcap = get_wcaps_type(wcap);
- if (wcap != AC_WID_AUD_IN) {
- spec->adc_nids = alc262_adc_nids_alt;
- spec->num_adc_nids =
- ARRAY_SIZE(alc262_adc_nids_alt);
- spec->capsrc_nids = alc262_capsrc_nids_alt;
- } else {
- spec->adc_nids = alc262_adc_nids;
- spec->num_adc_nids =
- ARRAY_SIZE(alc262_adc_nids);
- spec->capsrc_nids = alc262_capsrc_nids;
- }
- }
- }
- if (!spec->cap_mixer && !spec->no_analog)
- set_capture_mixer(codec);
- if (!spec->no_analog && has_cdefine_beep(codec))
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
-
- if (board_config == ALC262_AUTO)
- alc_pick_fixup(codec, alc262_fixup_tbl, alc262_fixups, 0);
-
- spec->vmaster_nid = 0x0c;
-
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC262_AUTO)
- spec->init_hook = alc262_auto_init;
-
- alc_init_jacks(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc262_loopbacks;
-#endif
-
- return 0;
-}
-
-/*
- * ALC268 channel source setting (2 channel)
- */
-#define ALC268_DIGOUT_NID ALC880_DIGOUT_NID
-#define alc268_modes alc260_modes
-
-static hda_nid_t alc268_dac_nids[2] = {
- /* front, hp */
- 0x02, 0x03
-};
-
-static hda_nid_t alc268_adc_nids[2] = {
- /* ADC0-1 */
- 0x08, 0x07
-};
-
-static hda_nid_t alc268_adc_nids_alt[1] = {
- /* ADC0 */
- 0x08
-};
-
-static hda_nid_t alc268_capsrc_nids[2] = { 0x23, 0x24 };
-
-static struct snd_kcontrol_new alc268_base_mixer[] = {
- /* output mixer control */
- HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0, HDA_INPUT),
- { }
-};
-
-static struct snd_kcontrol_new alc268_toshiba_mixer[] = {
- /* output mixer control */
- HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0, HDA_INPUT),
- { }
-};
-
-/* bind Beep switches of both NID 0x0f and 0x10 */
-static struct hda_bind_ctls alc268_bind_beep_sw = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x0f, 3, 1, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x10, 3, 1, HDA_INPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc268_beep_mixer[] = {
- HDA_CODEC_VOLUME("Beep Playback Volume", 0x1d, 0x0, HDA_INPUT),
- HDA_BIND_SW("Beep Playback Switch", &alc268_bind_beep_sw),
- { }
-};
-
-static struct hda_verb alc268_eapd_verbs[] = {
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-/* Toshiba specific */
-static struct hda_verb alc268_toshiba_verbs[] = {
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { } /* end */
-};
-
-/* Acer specific */
-/* bind volumes of both NID 0x02 and 0x03 */
-static struct hda_bind_ctls alc268_acer_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc268_acer_automute(struct hda_codec *codec, int force)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int mute;
-
- if (force || !spec->sense_updated) {
- spec->jack_present = snd_hda_jack_detect(codec, 0x14);
- spec->sense_updated = 1;
- }
- if (spec->jack_present)
- mute = HDA_AMP_MUTE; /* mute internal speaker */
- else /* unmute internal speaker if necessary */
- mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
-}
-
-
-/* bind hp and internal speaker mute (with plug check) */
-static int alc268_acer_master_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int change;
-
- change = amp_stereo_mute_update(codec, 0x14, HDA_OUTPUT, 0, valp);
- if (change)
- alc268_acer_automute(codec, 0);
- return change;
-}
-
-static struct snd_kcontrol_new alc268_acer_aspire_one_mixer[] = {
- /* output mixer control */
- HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = alc268_acer_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x18, 0, HDA_INPUT),
- { }
-};
-
-static struct snd_kcontrol_new alc268_acer_mixer[] = {
- /* output mixer control */
- HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = alc268_acer_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0, HDA_INPUT),
- { }
-};
-
-static struct snd_kcontrol_new alc268_acer_dmic_mixer[] = {
- /* output mixer control */
- HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = alc268_acer_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0, HDA_INPUT),
- { }
-};
-
-static struct hda_verb alc268_acer_aspire_one_verbs[] = {
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x23, AC_VERB_SET_CONNECT_SEL, 0x06},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, 0xa017},
- { }
-};
-
-static struct hda_verb alc268_acer_verbs[] = {
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* internal dmic? */
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- { }
-};
-
-/* unsolicited event for HP jack sensing */
-#define alc268_toshiba_unsol_event alc262_hippo_unsol_event
-#define alc268_toshiba_setup alc262_hippo_setup
-#define alc268_toshiba_automute alc262_hippo_automute
-
-static void alc268_acer_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) != ALC880_HP_EVENT)
- return;
- alc268_acer_automute(codec, 1);
-}
-
-static void alc268_acer_init_hook(struct hda_codec *codec)
-{
- alc268_acer_automute(codec, 1);
-}
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc268_aspire_one_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x15);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc268_acer_lc_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc268_aspire_one_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-static void alc268_acer_lc_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x12;
- spec->int_mic.mux_idx = 6;
- spec->auto_mic = 1;
-}
-
-static void alc268_acer_lc_init_hook(struct hda_codec *codec)
-{
- alc268_aspire_one_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-
-static struct snd_kcontrol_new alc268_dell_mixer[] = {
- /* output mixer control */
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT),
- { }
-};
-
-static struct hda_verb alc268_dell_verbs[] = {
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
- { }
-};
-
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc268_dell_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
-}
-
-static struct snd_kcontrol_new alc267_quanta_il1_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Capture Volume", 0x23, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Mic Capture Switch", 0x23, 2, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Ext Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
- { }
-};
-
-static struct hda_verb alc267_quanta_il1_verbs[] = {
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
- { }
-};
-
-static void alc267_quanta_il1_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
-}
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc268_base_init_verbs[] = {
- /* Unmute DAC0-1 and set vol = 0 */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /*
- * Set up output mixers (0x0c - 0x0e)
- */
- /* set vol=0 to output mixers */
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- /* set PCBEEP vol = 0, mute connections */
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- /* Unmute Selector 23h,24h and set the default input to mic-in */
-
- {0x23, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x24, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- { }
-};
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc268_volume_init_verbs[] = {
- /* set output DAC */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
-
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- /* set PCBEEP vol = 0, mute connections */
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- { }
-};
-
-static struct snd_kcontrol_new alc268_capture_nosrc_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
- _DEFINE_CAPSRC(1),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc268_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x24, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x24, 0x0, HDA_OUTPUT),
- _DEFINE_CAPSRC(2),
- { } /* end */
-};
-
-static struct hda_input_mux alc268_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x3 },
- },
-};
-
-static struct hda_input_mux alc268_acer_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x1 },
- { "Line", 0x2 },
- },
-};
-
-static struct hda_input_mux alc268_acer_dmic_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Internal Mic", 0x6 },
- { "Line", 0x2 },
- },
-};
-
-#ifdef CONFIG_SND_DEBUG
-static struct snd_kcontrol_new alc268_test_mixer[] = {
- /* Volume widgets */
- HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Mono sum Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE("LINE-OUT sum Playback Switch", 0x0f, 2, HDA_INPUT),
- HDA_BIND_MUTE("HP-OUT sum Playback Switch", 0x10, 2, HDA_INPUT),
- HDA_BIND_MUTE("LINE-OUT Playback Switch", 0x14, 2, HDA_OUTPUT),
- HDA_BIND_MUTE("HP-OUT Playback Switch", 0x15, 2, HDA_OUTPUT),
- HDA_BIND_MUTE("Mono Playback Switch", 0x16, 2, HDA_OUTPUT),
- HDA_CODEC_VOLUME("MIC1 Capture Volume", 0x18, 0x0, HDA_INPUT),
- HDA_BIND_MUTE("MIC1 Capture Switch", 0x18, 2, HDA_OUTPUT),
- HDA_CODEC_VOLUME("MIC2 Capture Volume", 0x19, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("LINE1 Capture Volume", 0x1a, 0x0, HDA_INPUT),
- HDA_BIND_MUTE("LINE1 Capture Switch", 0x1a, 2, HDA_OUTPUT),
- /* The below appears problematic on some hardwares */
- /*HDA_CODEC_VOLUME("PCBEEP Playback Volume", 0x1d, 0x0, HDA_INPUT),*/
- HDA_CODEC_VOLUME("PCM-IN1 Capture Volume", 0x23, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("PCM-IN1 Capture Switch", 0x23, 2, HDA_OUTPUT),
- HDA_CODEC_VOLUME("PCM-IN2 Capture Volume", 0x24, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("PCM-IN2 Capture Switch", 0x24, 2, HDA_OUTPUT),
-
- /* Modes for retasking pin widgets */
- ALC_PIN_MODE("LINE-OUT pin mode", 0x14, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("HP-OUT pin mode", 0x15, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("MIC1 pin mode", 0x18, ALC_PIN_DIR_INOUT),
- ALC_PIN_MODE("LINE1 pin mode", 0x1a, ALC_PIN_DIR_INOUT),
-
- /* Controls for GPIO pins, assuming they are configured as outputs */
- ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01),
- ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02),
- ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04),
- ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08),
-
- /* Switches to allow the digital SPDIF output pin to be enabled.
- * The ALC268 does not have an SPDIF input.
- */
- ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x06, 0x01),
-
- /* A switch allowing EAPD to be enabled. Some laptops seem to use
- * this output to turn on an external amplifier.
- */
- ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02),
- ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02),
-
- { } /* end */
-};
-#endif
-
-/* create input playback/capture controls for the given pin */
-static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
- const char *ctlname, int idx)
-{
- hda_nid_t dac;
- int err;
-
- switch (nid) {
- case 0x14:
- case 0x16:
- dac = 0x02;
- break;
- case 0x15:
- case 0x1a: /* ALC259/269 only */
- case 0x1b: /* ALC259/269 only */
- case 0x21: /* ALC269vb has this pin, too */
- dac = 0x03;
- break;
- default:
- snd_printd(KERN_WARNING "hda_codec: "
- "ignoring pin 0x%x as unknown\n", nid);
- return 0;
- }
- if (spec->multiout.dac_nids[0] != dac &&
- spec->multiout.dac_nids[1] != dac) {
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
- HDA_COMPOSE_AMP_VAL(dac, 3, idx,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
- }
-
- if (nid != 0x16)
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
- HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
- else /* mono */
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
- HDA_COMPOSE_AMP_VAL(nid, 2, idx, HDA_OUTPUT));
- if (err < 0)
- return err;
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- hda_nid_t nid;
- int err;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- nid = cfg->line_out_pins[0];
- if (nid) {
- const char *name;
- if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- name = "Speaker";
- else
- name = "Front";
- err = alc268_new_analog_output(spec, nid, name, 0);
- if (err < 0)
- return err;
- }
-
- nid = cfg->speaker_pins[0];
- if (nid == 0x1d) {
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, "Speaker",
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
- } else if (nid) {
- err = alc268_new_analog_output(spec, nid, "Speaker", 0);
- if (err < 0)
- return err;
- }
- nid = cfg->hp_pins[0];
- if (nid) {
- err = alc268_new_analog_output(spec, nid, "Headphone", 0);
- if (err < 0)
- return err;
- }
-
- nid = cfg->line_out_pins[1] | cfg->line_out_pins[2];
- if (nid == 0x16) {
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, "Mono",
- HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int alc268_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- return alc_auto_create_input_ctls(codec, cfg, 0, 0x23, 0x24);
-}
-
-static void alc268_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type)
-{
- int idx;
-
- alc_set_pin_output(codec, nid, pin_type);
- if (nid == 0x14 || nid == 0x16)
- idx = 0;
- else
- idx = 1;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
-}
-
-static void alc268_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.line_outs; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- alc268_auto_set_output_and_unmute(codec, nid, pin_type);
- }
-}
-
-static void alc268_auto_init_hp_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t pin;
- int i;
-
- for (i = 0; i < spec->autocfg.hp_outs; i++) {
- pin = spec->autocfg.hp_pins[i];
- alc268_auto_set_output_and_unmute(codec, pin, PIN_HP);
- }
- for (i = 0; i < spec->autocfg.speaker_outs; i++) {
- pin = spec->autocfg.speaker_pins[i];
- alc268_auto_set_output_and_unmute(codec, pin, PIN_OUT);
- }
- if (spec->autocfg.mono_out_pin)
- snd_hda_codec_write(codec, spec->autocfg.mono_out_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-}
-
-static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0];
- hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
- hda_nid_t line_nid = spec->autocfg.line_out_pins[0];
- unsigned int dac_vol1, dac_vol2;
-
- if (line_nid == 0x1d || speaker_nid == 0x1d) {
- snd_hda_codec_write(codec, speaker_nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- /* mute mixer inputs from 0x1d */
- snd_hda_codec_write(codec, 0x0f, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(1));
- snd_hda_codec_write(codec, 0x10, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(1));
- } else {
- /* unmute mixer inputs from 0x1d */
- snd_hda_codec_write(codec, 0x0f, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1));
- snd_hda_codec_write(codec, 0x10, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1));
- }
-
- dac_vol1 = dac_vol2 = 0xb000 | 0x40; /* set max volume */
- if (line_nid == 0x14)
- dac_vol2 = AMP_OUT_ZERO;
- else if (line_nid == 0x15)
- dac_vol1 = AMP_OUT_ZERO;
- if (hp_nid == 0x14)
- dac_vol2 = AMP_OUT_ZERO;
- else if (hp_nid == 0x15)
- dac_vol1 = AMP_OUT_ZERO;
- if (line_nid != 0x16 || hp_nid != 0x16 ||
- spec->autocfg.line_out_pins[1] != 0x16 ||
- spec->autocfg.line_out_pins[2] != 0x16)
- dac_vol1 = dac_vol2 = AMP_OUT_ZERO;
-
- snd_hda_codec_write(codec, 0x02, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, dac_vol1);
- snd_hda_codec_write(codec, 0x03, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, dac_vol2);
-}
-
-/* pcm configuration: identical with ALC880 */
-#define alc268_pcm_analog_playback alc880_pcm_analog_playback
-#define alc268_pcm_analog_capture alc880_pcm_analog_capture
-#define alc268_pcm_analog_alt_capture alc880_pcm_analog_alt_capture
-#define alc268_pcm_digital_playback alc880_pcm_digital_playback
-
-/*
- * BIOS auto configuration
- */
-static int alc268_parse_auto_config(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc268_ignore[] = { 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc268_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs) {
- if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) {
- spec->multiout.max_channels = 2;
- spec->no_analog = 1;
- goto dig_only;
- }
- return 0; /* can't find valid BIOS pin config */
- }
- err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc268_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = 2;
-
- dig_only:
- /* digital only support output */
- alc_auto_parse_digital(codec);
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d)
- add_mixer(spec, alc268_beep_mixer);
-
- add_verb(spec, alc268_volume_init_verbs);
- spec->num_mux_defs = 2;
- spec->input_mux = &spec->private_imux[0];
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
-
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
- return 1;
-}
-
-#define alc268_auto_init_analog_input alc882_auto_init_analog_input
-
-/* init callback for auto-configuration model -- overriding the default init */
-static void alc268_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc268_auto_init_multi_out(codec);
- alc268_auto_init_hp_out(codec);
- alc268_auto_init_mono_speaker_out(codec);
- alc268_auto_init_analog_input(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-/*
- * configuration and preset
- */
-static const char *alc268_models[ALC268_MODEL_LAST] = {
- [ALC267_QUANTA_IL1] = "quanta-il1",
- [ALC268_3ST] = "3stack",
- [ALC268_TOSHIBA] = "toshiba",
- [ALC268_ACER] = "acer",
- [ALC268_ACER_DMIC] = "acer-dmic",
- [ALC268_ACER_ASPIRE_ONE] = "acer-aspire",
- [ALC268_DELL] = "dell",
- [ALC268_ZEPTO] = "zepto",
-#ifdef CONFIG_SND_DEBUG
- [ALC268_TEST] = "test",
-#endif
- [ALC268_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc268_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1025, 0x011e, "Acer Aspire 5720z", ALC268_ACER),
- SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER),
- SND_PCI_QUIRK(0x1025, 0x012e, "Acer Aspire 5310", ALC268_ACER),
- SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER),
- SND_PCI_QUIRK(0x1025, 0x0136, "Acer Aspire 5315", ALC268_ACER),
- SND_PCI_QUIRK(0x1025, 0x015b, "Acer Aspire One",
- ALC268_ACER_ASPIRE_ONE),
- SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL),
- SND_PCI_QUIRK_MASK(0x1028, 0xfff0, 0x02b0,
- "Dell Inspiron Mini9/Vostro A90", ALC268_DELL),
- /* almost compatible with toshiba but with optional digital outs;
- * auto-probing seems working fine
- */
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP TX25xx series",
- ALC268_AUTO),
- SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),
- SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO),
- SND_PCI_QUIRK(0x14c0, 0x0025, "COMPAL IFL90/JFL-92", ALC268_TOSHIBA),
- SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER),
- SND_PCI_QUIRK(0x152d, 0x0771, "Quanta IL1", ALC267_QUANTA_IL1),
- {}
-};
-
-/* Toshiba laptops have no unique PCI SSID but only codec SSID */
-static struct snd_pci_quirk alc268_ssid_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1179, 0xff0a, "TOSHIBA X-200", ALC268_AUTO),
- SND_PCI_QUIRK(0x1179, 0xff0e, "TOSHIBA X-200 HDMI", ALC268_AUTO),
- SND_PCI_QUIRK_MASK(0x1179, 0xff00, 0xff00, "TOSHIBA A/Lx05",
- ALC268_TOSHIBA),
- {}
-};
-
-static struct alc_config_preset alc268_presets[] = {
- [ALC267_QUANTA_IL1] = {
- .mixers = { alc267_quanta_il1_mixer, alc268_beep_mixer,
- alc268_capture_nosrc_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc267_quanta_il1_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .unsol_event = alc_sku_unsol_event,
- .setup = alc267_quanta_il1_setup,
- .init_hook = alc_inithook,
- },
- [ALC268_3ST] = {
- .mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
- alc268_beep_mixer },
- .init_verbs = { alc268_base_init_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x03,
- .dig_out_nid = ALC268_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .input_mux = &alc268_capture_source,
- },
- [ALC268_TOSHIBA] = {
- .mixers = { alc268_toshiba_mixer, alc268_capture_alt_mixer,
- alc268_beep_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_toshiba_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .input_mux = &alc268_capture_source,
- .unsol_event = alc268_toshiba_unsol_event,
- .setup = alc268_toshiba_setup,
- .init_hook = alc268_toshiba_automute,
- },
- [ALC268_ACER] = {
- .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer,
- alc268_beep_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_acer_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x02,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .input_mux = &alc268_acer_capture_source,
- .unsol_event = alc268_acer_unsol_event,
- .init_hook = alc268_acer_init_hook,
- },
- [ALC268_ACER_DMIC] = {
- .mixers = { alc268_acer_dmic_mixer, alc268_capture_alt_mixer,
- alc268_beep_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_acer_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x02,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .input_mux = &alc268_acer_dmic_capture_source,
- .unsol_event = alc268_acer_unsol_event,
- .init_hook = alc268_acer_init_hook,
- },
- [ALC268_ACER_ASPIRE_ONE] = {
- .mixers = { alc268_acer_aspire_one_mixer,
- alc268_beep_mixer,
- alc268_capture_nosrc_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_acer_aspire_one_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .unsol_event = alc268_acer_lc_unsol_event,
- .setup = alc268_acer_lc_setup,
- .init_hook = alc268_acer_lc_init_hook,
- },
- [ALC268_DELL] = {
- .mixers = { alc268_dell_mixer, alc268_beep_mixer,
- alc268_capture_nosrc_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_dell_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x02,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .unsol_event = alc_sku_unsol_event,
- .setup = alc268_dell_setup,
- .init_hook = alc_inithook,
- },
- [ALC268_ZEPTO] = {
- .mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
- alc268_beep_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_toshiba_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x03,
- .dig_out_nid = ALC268_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .input_mux = &alc268_capture_source,
- .setup = alc268_toshiba_setup,
- .init_hook = alc268_toshiba_automute,
- },
-#ifdef CONFIG_SND_DEBUG
- [ALC268_TEST] = {
- .mixers = { alc268_test_mixer, alc268_capture_mixer },
- .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_volume_init_verbs },
- .num_dacs = ARRAY_SIZE(alc268_dac_nids),
- .dac_nids = alc268_dac_nids,
- .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
- .adc_nids = alc268_adc_nids_alt,
- .capsrc_nids = alc268_capsrc_nids,
- .hp_nid = 0x03,
- .dig_out_nid = ALC268_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc268_modes),
- .channel_mode = alc268_modes,
- .input_mux = &alc268_capture_source,
- },
-#endif
-};
-
-static int patch_alc268(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int board_config;
- int i, has_beep, err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- board_config = snd_hda_check_board_config(codec, ALC268_MODEL_LAST,
- alc268_models,
- alc268_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC268_MODEL_LAST)
- board_config = snd_hda_check_board_codec_sid_config(codec,
- ALC268_MODEL_LAST, alc268_models, alc268_ssid_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC268_MODEL_LAST) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC268_AUTO;
- }
-
- if (board_config == ALC268_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc268_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC268_3ST;
- }
- }
-
- if (board_config != ALC268_AUTO)
- setup_preset(codec, &alc268_presets[board_config]);
-
- spec->stream_analog_playback = &alc268_pcm_analog_playback;
- spec->stream_analog_capture = &alc268_pcm_analog_capture;
- spec->stream_analog_alt_capture = &alc268_pcm_analog_alt_capture;
-
- spec->stream_digital_playback = &alc268_pcm_digital_playback;
-
- has_beep = 0;
- for (i = 0; i < spec->num_mixers; i++) {
- if (spec->mixers[i] == alc268_beep_mixer) {
- has_beep = 1;
- break;
- }
- }
-
- if (has_beep) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
- if (!query_amp_caps(codec, 0x1d, HDA_INPUT))
- /* override the amp caps for beep generator */
- snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT,
- (0x0c << AC_AMPCAP_OFFSET_SHIFT) |
- (0x0c << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x07 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (0 << AC_AMPCAP_MUTE_SHIFT));
- }
-
- if (!spec->no_analog && !spec->adc_nids && spec->input_mux) {
- /* check whether NID 0x07 is valid */
- unsigned int wcap = get_wcaps(codec, 0x07);
- int i;
-
- spec->capsrc_nids = alc268_capsrc_nids;
- /* get type */
- wcap = get_wcaps_type(wcap);
- if (spec->auto_mic ||
- wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
- spec->adc_nids = alc268_adc_nids_alt;
- spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt);
- if (spec->auto_mic)
- fixup_automic_adc(codec);
- if (spec->auto_mic || spec->input_mux->num_items == 1)
- add_mixer(spec, alc268_capture_nosrc_mixer);
- else
- add_mixer(spec, alc268_capture_alt_mixer);
- } else {
- spec->adc_nids = alc268_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids);
- add_mixer(spec, alc268_capture_mixer);
- }
- /* set default input source */
- for (i = 0; i < spec->num_adc_nids; i++)
- snd_hda_codec_write_cache(codec, alc268_capsrc_nids[i],
- 0, AC_VERB_SET_CONNECT_SEL,
- i < spec->num_mux_defs ?
- spec->input_mux[i].items[0].index :
- spec->input_mux->items[0].index);
- }
-
- spec->vmaster_nid = 0x02;
-
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC268_AUTO)
- spec->init_hook = alc268_auto_init;
-
- alc_init_jacks(codec);
-
- return 0;
-}
-
-/*
- * ALC269 channel source setting (2 channel)
- */
-#define ALC269_DIGOUT_NID ALC880_DIGOUT_NID
-
-#define alc269_dac_nids alc260_dac_nids
-
-static hda_nid_t alc269_adc_nids[1] = {
- /* ADC1 */
- 0x08,
-};
-
-static hda_nid_t alc269_capsrc_nids[1] = {
- 0x23,
-};
-
-static hda_nid_t alc269vb_adc_nids[1] = {
- /* ADC1 */
- 0x09,
-};
-
-static hda_nid_t alc269vb_capsrc_nids[1] = {
- 0x22,
-};
-
-static hda_nid_t alc269_adc_candidates[] = {
- 0x08, 0x09, 0x07,
-};
-
-#define alc269_modes alc260_modes
-#define alc269_capture_source alc880_lg_lw_capture_source
-
-static struct snd_kcontrol_new alc269_base_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = {
- /* output mixer control */
- HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = alc268_acer_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT),
- { }
-};
-
-static struct snd_kcontrol_new alc269_lifebook_mixer[] = {
- /* output mixer control */
- HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Playback Switch",
- .subdevice = HDA_SUBDEV_AMP_FLAG,
- .info = snd_hda_mixer_amp_switch_info,
- .get = snd_hda_mixer_amp_switch_get,
- .put = alc268_acer_master_sw_put,
- .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- },
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x0b, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x0b, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("Dock Mic Boost", 0x1b, 0, HDA_INPUT),
- { }
-};
-
-static struct snd_kcontrol_new alc269_laptop_mixer[] = {
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc269vb_laptop_mixer[] = {
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc269_asus_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-/* capture mixer elements */
-static struct snd_kcontrol_new alc269_laptop_analog_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("IntMic Boost", 0x19, 0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc269_laptop_digital_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc269vb_laptop_analog_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("IntMic Boost", 0x19, 0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc269vb_laptop_digital_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- { } /* end */
-};
-
-/* FSC amilo */
-#define alc269_fujitsu_mixer alc269_laptop_mixer
-
-static struct hda_verb alc269_quanta_fl1_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- { }
-};
-
-static struct hda_verb alc269_lifebook_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- { }
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x15);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
-
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_COEF_INDEX, 0x0c);
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_PROC_COEF, 0x680);
-
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_COEF_INDEX, 0x0c);
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_PROC_COEF, 0x480);
-}
-
-/* toggle speaker-output according to the hp-jacks state */
-static void alc269_lifebook_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- /* Check laptop headphone socket */
- present = snd_hda_jack_detect(codec, 0x15);
-
- /* Check port replicator headphone socket */
- present |= snd_hda_jack_detect(codec, 0x1a);
-
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
-
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_COEF_INDEX, 0x0c);
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_PROC_COEF, 0x680);
-
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_COEF_INDEX, 0x0c);
- snd_hda_codec_write(codec, 0x20, 0,
- AC_VERB_SET_PROC_COEF, 0x480);
-}
-
-static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec)
-{
- unsigned int present_laptop;
- unsigned int present_dock;
-
- present_laptop = snd_hda_jack_detect(codec, 0x18);
- present_dock = snd_hda_jack_detect(codec, 0x1b);
-
- /* Laptop mic port overrides dock mic port, design decision */
- if (present_dock)
- snd_hda_codec_write(codec, 0x23, 0,
- AC_VERB_SET_CONNECT_SEL, 0x3);
- if (present_laptop)
- snd_hda_codec_write(codec, 0x23, 0,
- AC_VERB_SET_CONNECT_SEL, 0x0);
- if (!present_dock && !present_laptop)
- snd_hda_codec_write(codec, 0x23, 0,
- AC_VERB_SET_CONNECT_SEL, 0x1);
-}
-
-static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc269_quanta_fl1_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-static void alc269_lifebook_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc269_lifebook_speaker_automute(codec);
- if ((res >> 26) == ALC880_MIC_EVENT)
- alc269_lifebook_mic_autoswitch(codec);
-}
-
-static void alc269_quanta_fl1_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
-}
-
-static void alc269_quanta_fl1_init_hook(struct hda_codec *codec)
-{
- alc269_quanta_fl1_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-
-static void alc269_lifebook_init_hook(struct hda_codec *codec)
-{
- alc269_lifebook_speaker_automute(codec);
- alc269_lifebook_mic_autoswitch(codec);
-}
-
-static struct hda_verb alc269_laptop_dmic_init_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x23, AC_VERB_SET_CONNECT_SEL, 0x05},
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc269_laptop_amic_init_verbs[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x23, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x701b | (0x00 << 8))},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc269vb_laptop_dmic_init_verbs[] = {
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x06},
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc269vb_laptop_amic_init_verbs[] = {
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc271_acer_dmic_verbs[] = {
- {0x20, AC_VERB_SET_COEF_INDEX, 0x0d},
- {0x20, AC_VERB_SET_PROC_COEF, 0x4000},
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x22, AC_VERB_SET_CONNECT_SEL, 6},
- { }
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc269_speaker_automute(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- unsigned int nid = spec->autocfg.hp_pins[0];
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, nid);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
- alc_report_jack(codec, nid);
-}
-
-/* unsolicited event for HP jack sensing */
-static void alc269_laptop_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc269_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-static void alc269_laptop_amic_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
-}
-
-static void alc269_laptop_dmic_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x12;
- spec->int_mic.mux_idx = 5;
- spec->auto_mic = 1;
-}
-
-static void alc269vb_laptop_amic_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x21;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
-}
-
-static void alc269vb_laptop_dmic_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x21;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x12;
- spec->int_mic.mux_idx = 6;
- spec->auto_mic = 1;
-}
-
-static void alc269_laptop_inithook(struct hda_codec *codec)
-{
- alc269_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc269_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /*
- * Set up output mixers (0x02 - 0x03)
- */
- /* set vol=0 to output mixers */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* FIXME: use Mux-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x23, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* set EAPD */
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-static struct hda_verb alc269vb_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /*
- * Set up output mixers (0x02 - 0x03)
- */
- /* set vol=0 to output mixers */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* FIXME: use Mux-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x22, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* set EAPD */
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-#define alc269_auto_create_multi_out_ctls \
- alc268_auto_create_multi_out_ctls
-#define alc269_auto_create_input_ctls \
- alc268_auto_create_input_ctls
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc269_loopbacks alc880_loopbacks
-#endif
-
-/* pcm configuration: identical with ALC880 */
-#define alc269_pcm_analog_playback alc880_pcm_analog_playback
-#define alc269_pcm_analog_capture alc880_pcm_analog_capture
-#define alc269_pcm_digital_playback alc880_pcm_digital_playback
-#define alc269_pcm_digital_capture alc880_pcm_digital_capture
-
-static struct hda_pcm_stream alc269_44k_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
- /* NID is set in alc_build_pcms */
- .ops = {
- .open = alc880_playback_pcm_open,
- .prepare = alc880_playback_pcm_prepare,
- .cleanup = alc880_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream alc269_44k_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_44100, /* fixed rate */
- /* NID is set in alc_build_pcms */
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int alc269_mic2_for_mute_led(struct hda_codec *codec)
-{
- switch (codec->subsystem_id) {
- case 0x103c1586:
- return 1;
- }
- return 0;
-}
-
-static int alc269_mic2_mute_check_ps(struct hda_codec *codec, hda_nid_t nid)
-{
- /* update mute-LED according to the speaker mute state */
- if (nid == 0x01 || nid == 0x14) {
- int pinval;
- if (snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0) &
- HDA_AMP_MUTE)
- pinval = 0x24;
- else
- pinval = 0x20;
- /* mic2 vref pin is used for mute LED control */
- snd_hda_codec_update_cache(codec, 0x19, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pinval);
- }
- return alc_check_power_status(codec, nid);
-}
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
-
-static int alc275_setup_dual_adc(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- if (codec->vendor_id != 0x10ec0275 || !spec->auto_mic)
- return 0;
- if ((spec->ext_mic.pin >= 0x18 && spec->int_mic.pin <= 0x13) ||
- (spec->ext_mic.pin <= 0x12 && spec->int_mic.pin >= 0x18)) {
- if (spec->ext_mic.pin <= 0x12) {
- spec->private_adc_nids[0] = 0x08;
- spec->private_adc_nids[1] = 0x11;
- spec->private_capsrc_nids[0] = 0x23;
- spec->private_capsrc_nids[1] = 0x22;
- } else {
- spec->private_adc_nids[0] = 0x11;
- spec->private_adc_nids[1] = 0x08;
- spec->private_capsrc_nids[0] = 0x22;
- spec->private_capsrc_nids[1] = 0x23;
- }
- spec->adc_nids = spec->private_adc_nids;
- spec->capsrc_nids = spec->private_capsrc_nids;
- spec->num_adc_nids = 2;
- spec->dual_adc_switch = 1;
- snd_printdd("realtek: enabling dual ADC switchg (%02x:%02x)\n",
- spec->adc_nids[0], spec->adc_nids[1]);
- return 1;
- }
- return 0;
-}
-
-/* different alc269-variants */
-enum {
- ALC269_TYPE_NORMAL,
- ALC269_TYPE_ALC259,
- ALC269_TYPE_ALC271X,
-};
-
-/*
- * BIOS auto configuration
- */
-static int alc269_parse_auto_config(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc269_ignore[] = { 0x1d, 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc269_ignore);
- if (err < 0)
- return err;
-
- err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (spec->codec_variant == ALC269_TYPE_NORMAL)
- err = alc269_auto_create_input_ctls(codec, &spec->autocfg);
- else
- err = alc_auto_create_input_ctls(codec, &spec->autocfg, 0,
- 0x22, 0);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- if (spec->codec_variant != ALC269_TYPE_NORMAL) {
- add_verb(spec, alc269vb_init_verbs);
- alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21);
- } else {
- add_verb(spec, alc269_init_verbs);
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
- }
-
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- if (!alc275_setup_dual_adc(codec))
- fillup_priv_adc_nids(codec, alc269_adc_candidates,
- sizeof(alc269_adc_candidates));
-
- /* set default input source */
- if (!spec->dual_adc_switch)
- select_or_unmute_capsrc(codec, spec->capsrc_nids[0],
- spec->input_mux->items[0].index);
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
-
- if (!spec->cap_mixer && !spec->no_analog)
- set_capture_mixer(codec);
-
- return 1;
-}
-
-#define alc269_auto_init_multi_out alc268_auto_init_multi_out
-#define alc269_auto_init_hp_out alc268_auto_init_hp_out
-#define alc269_auto_init_analog_input alc882_auto_init_analog_input
-
-
-/* init callback for auto-configuration model -- overriding the default init */
-static void alc269_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc269_auto_init_multi_out(codec);
- alc269_auto_init_hp_out(codec);
- alc269_auto_init_analog_input(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-#ifdef SND_HDA_NEEDS_RESUME
-static void alc269_toggle_power_output(struct hda_codec *codec, int power_up)
-{
- int val = alc_read_coef_idx(codec, 0x04);
- if (power_up)
- val |= 1 << 11;
- else
- val &= ~(1 << 11);
- alc_write_coef_idx(codec, 0x04, val);
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int alc269_suspend(struct hda_codec *codec, pm_message_t state)
-{
- struct alc_spec *spec = codec->spec;
-
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017)
- alc269_toggle_power_output(codec, 0);
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
- alc269_toggle_power_output(codec, 0);
- msleep(150);
- }
-
- alc_shutup(codec);
- if (spec && spec->power_hook)
- spec->power_hook(codec);
- return 0;
-}
-#endif /* CONFIG_SND_HDA_POWER_SAVE */
-
-static int alc269_resume(struct hda_codec *codec)
-{
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
- alc269_toggle_power_output(codec, 0);
- msleep(150);
- }
-
- codec->patch_ops.init(codec);
-
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) {
- alc269_toggle_power_output(codec, 1);
- msleep(200);
- }
-
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018)
- alc269_toggle_power_output(codec, 1);
-
- snd_hda_codec_resume_amp(codec);
- snd_hda_codec_resume_cache(codec);
- hda_call_check_power_status(codec, 0x01);
- return 0;
-}
-#endif /* SND_HDA_NEEDS_RESUME */
-
-enum {
- ALC269_FIXUP_SONY_VAIO,
- ALC269_FIXUP_DELL_M101Z,
-};
-
-static const struct alc_fixup alc269_fixups[] = {
- [ALC269_FIXUP_SONY_VAIO] = {
- .verbs = (const struct hda_verb[]) {
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD},
- {}
- }
- },
- [ALC269_FIXUP_DELL_M101Z] = {
- .verbs = (const struct hda_verb[]) {
- /* Enables internal speaker */
- {0x20, AC_VERB_SET_COEF_INDEX, 13},
- {0x20, AC_VERB_SET_PROC_COEF, 0x4040},
- {}
- }
- },
-};
-
-static struct snd_pci_quirk alc269_fixup_tbl[] = {
- SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
- SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
- {}
-};
-
-
-/*
- * configuration and preset
- */
-static const char *alc269_models[ALC269_MODEL_LAST] = {
- [ALC269_BASIC] = "basic",
- [ALC269_QUANTA_FL1] = "quanta",
- [ALC269_AMIC] = "laptop-amic",
- [ALC269_DMIC] = "laptop-dmic",
- [ALC269_FUJITSU] = "fujitsu",
- [ALC269_LIFEBOOK] = "lifebook",
- [ALC269_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc269_cfg_tbl[] = {
- SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_QUANTA_FL1),
- SND_PCI_QUIRK(0x1025, 0x047c, "ACER ZGA", ALC271_ACER),
- SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A",
- ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1113, "ASUS N63Jn", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x11b3, "ASUS K52DR", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x11e3, "ASUS U33Jc", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80Jt", ALC269VB_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82Jv", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x12d3, "ASUS N61Jv", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1593, "ASUS N51Vn", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_DMIC),
- SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_AMIC),
- SND_PCI_QUIRK(0x1043, 0x831a, "ASUS Eeepc P901",
- ALC269_DMIC),
- SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101",
- ALC269_DMIC),
- SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_DMIC),
- SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_DMIC),
- SND_PCI_QUIRK(0x104d, 0x9071, "Sony VAIO", ALC269_AUTO),
- SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK),
- SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_DMIC),
- SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU),
- SND_PCI_QUIRK(0x17aa, 0x3be9, "Quanta Wistron", ALC269_AMIC),
- SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_AMIC),
- SND_PCI_QUIRK(0x17ff, 0x059a, "Quanta EL3", ALC269_DMIC),
- SND_PCI_QUIRK(0x17ff, 0x059b, "Quanta JR1", ALC269_DMIC),
- {}
-};
-
-static struct alc_config_preset alc269_presets[] = {
- [ALC269_BASIC] = {
- .mixers = { alc269_base_mixer },
- .init_verbs = { alc269_init_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .input_mux = &alc269_capture_source,
- },
- [ALC269_QUANTA_FL1] = {
- .mixers = { alc269_quanta_fl1_mixer },
- .init_verbs = { alc269_init_verbs, alc269_quanta_fl1_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .input_mux = &alc269_capture_source,
- .unsol_event = alc269_quanta_fl1_unsol_event,
- .setup = alc269_quanta_fl1_setup,
- .init_hook = alc269_quanta_fl1_init_hook,
- },
- [ALC269_AMIC] = {
- .mixers = { alc269_laptop_mixer },
- .cap_mixer = alc269_laptop_analog_capture_mixer,
- .init_verbs = { alc269_init_verbs,
- alc269_laptop_amic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .unsol_event = alc269_laptop_unsol_event,
- .setup = alc269_laptop_amic_setup,
- .init_hook = alc269_laptop_inithook,
- },
- [ALC269_DMIC] = {
- .mixers = { alc269_laptop_mixer },
- .cap_mixer = alc269_laptop_digital_capture_mixer,
- .init_verbs = { alc269_init_verbs,
- alc269_laptop_dmic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .unsol_event = alc269_laptop_unsol_event,
- .setup = alc269_laptop_dmic_setup,
- .init_hook = alc269_laptop_inithook,
- },
- [ALC269VB_AMIC] = {
- .mixers = { alc269vb_laptop_mixer },
- .cap_mixer = alc269vb_laptop_analog_capture_mixer,
- .init_verbs = { alc269vb_init_verbs,
- alc269vb_laptop_amic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .unsol_event = alc269_laptop_unsol_event,
- .setup = alc269vb_laptop_amic_setup,
- .init_hook = alc269_laptop_inithook,
- },
- [ALC269VB_DMIC] = {
- .mixers = { alc269vb_laptop_mixer },
- .cap_mixer = alc269vb_laptop_digital_capture_mixer,
- .init_verbs = { alc269vb_init_verbs,
- alc269vb_laptop_dmic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .unsol_event = alc269_laptop_unsol_event,
- .setup = alc269vb_laptop_dmic_setup,
- .init_hook = alc269_laptop_inithook,
- },
- [ALC269_FUJITSU] = {
- .mixers = { alc269_fujitsu_mixer },
- .cap_mixer = alc269_laptop_digital_capture_mixer,
- .init_verbs = { alc269_init_verbs,
- alc269_laptop_dmic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .unsol_event = alc269_laptop_unsol_event,
- .setup = alc269_laptop_dmic_setup,
- .init_hook = alc269_laptop_inithook,
- },
- [ALC269_LIFEBOOK] = {
- .mixers = { alc269_lifebook_mixer },
- .init_verbs = { alc269_init_verbs, alc269_lifebook_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .hp_nid = 0x03,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .input_mux = &alc269_capture_source,
- .unsol_event = alc269_lifebook_unsol_event,
- .init_hook = alc269_lifebook_init_hook,
- },
- [ALC271_ACER] = {
- .mixers = { alc269_asus_mixer },
- .cap_mixer = alc269vb_laptop_digital_capture_mixer,
- .init_verbs = { alc269_init_verbs, alc271_acer_dmic_verbs },
- .num_dacs = ARRAY_SIZE(alc269_dac_nids),
- .dac_nids = alc269_dac_nids,
- .adc_nids = alc262_dmic_adc_nids,
- .num_adc_nids = ARRAY_SIZE(alc262_dmic_adc_nids),
- .capsrc_nids = alc262_dmic_capsrc_nids,
- .num_channel_mode = ARRAY_SIZE(alc269_modes),
- .channel_mode = alc269_modes,
- .input_mux = &alc269_capture_source,
- .dig_out_nid = ALC880_DIGOUT_NID,
- .unsol_event = alc_sku_unsol_event,
- .setup = alc269vb_laptop_dmic_setup,
- .init_hook = alc_inithook,
- },
-};
-
-static int alc269_fill_coef(struct hda_codec *codec)
-{
- int val;
-
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) < 0x015) {
- alc_write_coef_idx(codec, 0xf, 0x960b);
- alc_write_coef_idx(codec, 0xe, 0x8817);
- }
-
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x016) {
- alc_write_coef_idx(codec, 0xf, 0x960b);
- alc_write_coef_idx(codec, 0xe, 0x8814);
- }
-
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) {
- val = alc_read_coef_idx(codec, 0x04);
- /* Power up output pin */
- alc_write_coef_idx(codec, 0x04, val | (1<<11));
- }
-
- if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
- val = alc_read_coef_idx(codec, 0xd);
- if ((val & 0x0c00) >> 10 != 0x1) {
- /* Capless ramp up clock control */
- alc_write_coef_idx(codec, 0xd, val | 1<<10);
- }
- val = alc_read_coef_idx(codec, 0x17);
- if ((val & 0x01c0) >> 6 != 0x4) {
- /* Class D power on reset */
- alc_write_coef_idx(codec, 0x17, val | 1<<7);
- }
- }
- return 0;
-}
-
-static int patch_alc269(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int board_config;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- alc_auto_parse_customize_define(codec);
-
- if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010){
- if (codec->bus->pci->subsystem_vendor == 0x1025 &&
- spec->cdefine.platform_type == 1) {
- alc_codec_rename(codec, "ALC271X");
- spec->codec_variant = ALC269_TYPE_ALC271X;
- } else {
- alc_codec_rename(codec, "ALC259");
- spec->codec_variant = ALC269_TYPE_ALC259;
- }
- } else
- alc_fix_pll_init(codec, 0x20, 0x04, 15);
-
- alc269_fill_coef(codec);
-
- board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
- alc269_models,
- alc269_cfg_tbl);
-
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC269_AUTO;
- }
-
- if (board_config == ALC269_AUTO)
- alc_pick_fixup(codec, alc269_fixup_tbl, alc269_fixups, 1);
-
- if (board_config == ALC269_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc269_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC269_BASIC;
- }
- }
-
- if (has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
- }
-
- if (board_config != ALC269_AUTO)
- setup_preset(codec, &alc269_presets[board_config]);
-
- if (board_config == ALC269_QUANTA_FL1) {
- /* Due to a hardware problem on Lenovo Ideadpad, we need to
- * fix the sample rate of analog I/O to 44.1kHz
- */
- spec->stream_analog_playback = &alc269_44k_pcm_analog_playback;
- spec->stream_analog_capture = &alc269_44k_pcm_analog_capture;
- } else if (spec->dual_adc_switch) {
- spec->stream_analog_playback = &alc269_pcm_analog_playback;
- /* switch ADC dynamically */
- spec->stream_analog_capture = &dualmic_pcm_analog_capture;
- } else {
- spec->stream_analog_playback = &alc269_pcm_analog_playback;
- spec->stream_analog_capture = &alc269_pcm_analog_capture;
- }
- spec->stream_digital_playback = &alc269_pcm_digital_playback;
- spec->stream_digital_capture = &alc269_pcm_digital_capture;
-
- if (!spec->adc_nids) { /* wasn't filled automatically? use default */
- if (spec->codec_variant != ALC269_TYPE_NORMAL) {
- spec->adc_nids = alc269_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
- spec->capsrc_nids = alc269_capsrc_nids;
- } else {
- spec->adc_nids = alc269vb_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc269vb_adc_nids);
- spec->capsrc_nids = alc269vb_capsrc_nids;
- }
- }
-
- if (!spec->cap_mixer)
- set_capture_mixer(codec);
- if (has_cdefine_beep(codec))
- set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
-
- if (board_config == ALC269_AUTO)
- alc_pick_fixup(codec, alc269_fixup_tbl, alc269_fixups, 0);
-
- spec->vmaster_nid = 0x02;
-
- codec->patch_ops = alc_patch_ops;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- codec->patch_ops.suspend = alc269_suspend;
-#endif
-#ifdef SND_HDA_NEEDS_RESUME
- codec->patch_ops.resume = alc269_resume;
-#endif
- if (board_config == ALC269_AUTO)
- spec->init_hook = alc269_auto_init;
-
- alc_init_jacks(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc269_loopbacks;
- if (alc269_mic2_for_mute_led(codec))
- codec->patch_ops.check_power_status = alc269_mic2_mute_check_ps;
-#endif
-
- return 0;
-}
-
-/*
- * ALC861 channel source setting (2/6 channel selection for 3-stack)
- */
-
-/*
- * set the path ways for 2 channel output
- * need to set the codec line out and mic 1 pin widgets to inputs
- */
-static struct hda_verb alc861_threestack_ch2_init[] = {
- /* set pin widget 1Ah (line in) for input */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* set pin widget 18h (mic1/2) for input, for mic also enable
- * the vref
- */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
-
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
-#if 0
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/
-#endif
- { } /* end */
-};
-/*
- * 6ch mode
- * need to set the codec line out and mic 1 pin widgets to outputs
- */
-static struct hda_verb alc861_threestack_ch6_init[] = {
- /* set pin widget 1Ah (line in) for output (Back Surround)*/
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* set pin widget 18h (mic1) for output (CLFE)*/
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
-
- { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 },
-
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
-#if 0
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/
-#endif
- { } /* end */
-};
-
-static struct hda_channel_mode alc861_threestack_modes[2] = {
- { 2, alc861_threestack_ch2_init },
- { 6, alc861_threestack_ch6_init },
-};
-/* Set mic1 as input and unmute the mixer */
-static struct hda_verb alc861_uniwill_m31_ch2_init[] = {
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/
- { } /* end */
-};
-/* Set mic1 as output and mute mixer */
-static struct hda_verb alc861_uniwill_m31_ch4_init[] = {
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
- { } /* end */
-};
-
-static struct hda_channel_mode alc861_uniwill_m31_modes[2] = {
- { 2, alc861_uniwill_m31_ch2_init },
- { 4, alc861_uniwill_m31_ch4_init },
-};
-
-/* Set mic1 and line-in as input and unmute the mixer */
-static struct hda_verb alc861_asus_ch2_init[] = {
- /* set pin widget 1Ah (line in) for input */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* set pin widget 18h (mic1/2) for input, for mic also enable
- * the vref
- */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
-
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
-#if 0
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/
-#endif
- { } /* end */
-};
-/* Set mic1 nad line-in as output and mute mixer */
-static struct hda_verb alc861_asus_ch6_init[] = {
- /* set pin widget 1Ah (line in) for output (Back Surround)*/
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* { 0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */
- /* set pin widget 18h (mic1) for output (CLFE)*/
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, */
- { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 },
- { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 },
-
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
-#if 0
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/
-#endif
- { } /* end */
-};
-
-static struct hda_channel_mode alc861_asus_modes[2] = {
- { 2, alc861_asus_ch2_init },
- { 6, alc861_asus_ch6_init },
-};
-
-/* patch-ALC861 */
-
-static struct snd_kcontrol_new alc861_base_mixer[] = {
- /* output mixer control */
- HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT),
-
- /*Input mixer control */
- /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
-
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc861_3ST_mixer[] = {
- /* output mixer control */
- HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
- /*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */
-
- /* Input mixer control */
- /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- .private_value = ARRAY_SIZE(alc861_threestack_modes),
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc861_toshiba_mixer[] = {
- /* output mixer control */
- HDA_CODEC_MUTE("Master Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
-
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = {
- /* output mixer control */
- HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
- /*HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT), */
-
- /* Input mixer control */
- /* HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT), */
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- .private_value = ARRAY_SIZE(alc861_uniwill_m31_modes),
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc861_asus_mixer[] = {
- /* output mixer control */
- HDA_CODEC_MUTE("Front Playback Switch", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x06, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x05, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x05, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Side Playback Switch", 0x04, 0x0, HDA_OUTPUT),
-
- /* Input mixer control */
- HDA_CODEC_VOLUME("Input Playback Volume", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Input Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x15, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_OUTPUT),
-
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- .private_value = ARRAY_SIZE(alc861_asus_modes),
- },
- { }
-};
-
-/* additional mixer */
-static struct snd_kcontrol_new alc861_asus_laptop_mixer[] = {
- HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_INPUT),
- { }
-};
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc861_base_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- /* port-A for surround (rear panel) */
- { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-B for mic-in (rear panel) with vref */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-C for line-in (rear panel) */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* port-D for Front */
- { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-E for HP out (front panel) */
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
- /* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-F for mic-in (front panel) with vref */
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-G for CLFE (rear panel) */
- { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-H for side (rear panel) */
- { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x20, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* CD-in */
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* route front mic to ADC1*/
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Unmute DAC0~3 & spdif out*/
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Unmute Mixer 14 (mic) 1c (Line in)*/
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Unmute Stereo Mixer 15 */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */
-
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* hp used DAC 3 (Front) */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-
- { }
-};
-
-static struct hda_verb alc861_threestack_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- /* port-A for surround (rear panel) */
- { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- /* port-B for mic-in (rear panel) with vref */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-C for line-in (rear panel) */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* port-D for Front */
- { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-E for HP out (front panel) */
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
- /* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-F for mic-in (front panel) with vref */
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-G for CLFE (rear panel) */
- { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- /* port-H for side (rear panel) */
- { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- /* CD-in */
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* route front mic to ADC1*/
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Unmute DAC0~3 & spdif out*/
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Unmute Mixer 14 (mic) 1c (Line in)*/
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Unmute Stereo Mixer 15 */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */
-
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* hp used DAC 3 (Front) */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- { }
-};
-
-static struct hda_verb alc861_uniwill_m31_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- /* port-A for surround (rear panel) */
- { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- /* port-B for mic-in (rear panel) with vref */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-C for line-in (rear panel) */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* port-D for Front */
- { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-E for HP out (front panel) */
- /* this has to be set to VREF80 */
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-F for mic-in (front panel) with vref */
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-G for CLFE (rear panel) */
- { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- /* port-H for side (rear panel) */
- { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- /* CD-in */
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* route front mic to ADC1*/
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Unmute DAC0~3 & spdif out*/
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Unmute Mixer 14 (mic) 1c (Line in)*/
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Unmute Stereo Mixer 15 */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */
-
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* hp used DAC 3 (Front) */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- { }
-};
-
-static struct hda_verb alc861_asus_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- /* port-A for surround (rear panel)
- * according to codec#0 this is the HP jack
- */
- { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, /* was 0x00 */
- /* route front PCM to HP */
- { 0x0e, AC_VERB_SET_CONNECT_SEL, 0x01 },
- /* port-B for mic-in (rear panel) with vref */
- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-C for line-in (rear panel) */
- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* port-D for Front */
- { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-E for HP out (front panel) */
- /* this has to be set to VREF80 */
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* route front PCM to HP */
- { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
- /* port-F for mic-in (front panel) with vref */
- { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
- /* port-G for CLFE (rear panel) */
- { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* port-H for side (rear panel) */
- { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
- /* CD-in */
- { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
- /* route front mic to ADC1*/
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Unmute DAC0~3 & spdif out*/
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* Unmute Mixer 14 (mic) 1c (Line in)*/
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Unmute Stereo Mixer 15 */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c}, /* Output 0~12 step */
-
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- /* hp used DAC 3 (Front) */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- { }
-};
-
-/* additional init verbs for ASUS laptops */
-static struct hda_verb alc861_asus_laptop_init_verbs[] = {
- { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x45 }, /* HP-out */
- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2) }, /* mute line-in */
- { }
-};
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc861_auto_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- /* {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Unmute DAC0~3 & spdif out*/
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Unmute Mixer 14 (mic) 1c (Line in)*/
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Unmute Stereo Mixer 15 */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c},
-
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, /* set Mic 1 */
-
- { }
-};
-
-static struct hda_verb alc861_toshiba_init_verbs[] = {
- {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
-
- { }
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc861_toshiba_automute(struct hda_codec *codec)
-{
- unsigned int present = snd_hda_jack_detect(codec, 0x0f);
-
- snd_hda_codec_amp_stereo(codec, 0x16, HDA_INPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- snd_hda_codec_amp_stereo(codec, 0x1a, HDA_INPUT, 3,
- HDA_AMP_MUTE, present ? 0 : HDA_AMP_MUTE);
-}
-
-static void alc861_toshiba_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc861_toshiba_automute(codec);
-}
-
-/* pcm configuration: identical with ALC880 */
-#define alc861_pcm_analog_playback alc880_pcm_analog_playback
-#define alc861_pcm_analog_capture alc880_pcm_analog_capture
-#define alc861_pcm_digital_playback alc880_pcm_digital_playback
-#define alc861_pcm_digital_capture alc880_pcm_digital_capture
-
-
-#define ALC861_DIGOUT_NID 0x07
-
-static struct hda_channel_mode alc861_8ch_modes[1] = {
- { 8, NULL }
-};
-
-static hda_nid_t alc861_dac_nids[4] = {
- /* front, surround, clfe, side */
- 0x03, 0x06, 0x05, 0x04
-};
-
-static hda_nid_t alc660_dac_nids[3] = {
- /* front, clfe, surround */
- 0x03, 0x05, 0x06
-};
-
-static hda_nid_t alc861_adc_nids[1] = {
- /* ADC0-2 */
- 0x08,
-};
-
-static struct hda_input_mux alc861_capture_source = {
- .num_items = 5,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x3 },
- { "Line", 0x1 },
- { "CD", 0x4 },
- { "Mixer", 0x5 },
- },
-};
-
-static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t mix, srcs[5];
- int i, j, num;
-
- if (snd_hda_get_connections(codec, pin, &mix, 1) != 1)
- return 0;
- num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs));
- if (num < 0)
- return 0;
- for (i = 0; i < num; i++) {
- unsigned int type;
- type = get_wcaps_type(get_wcaps(codec, srcs[i]));
- if (type != AC_WID_AUD_OUT)
- continue;
- for (j = 0; j < spec->multiout.num_dacs; j++)
- if (spec->multiout.dac_nids[j] == srcs[i])
- break;
- if (j >= spec->multiout.num_dacs)
- return srcs[i];
- }
- return 0;
-}
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int alc861_auto_fill_dac_nids(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct alc_spec *spec = codec->spec;
- int i;
- hda_nid_t nid, dac;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- dac = alc861_look_for_dac(codec, nid);
- if (!dac)
- continue;
- spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
- }
- return 0;
-}
-
-static int alc861_create_out_sw(struct hda_codec *codec, const char *pfx,
- hda_nid_t nid, unsigned int chs)
-{
- return add_pb_sw_ctrl(codec->spec, ALC_CTL_WIDGET_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
-}
-
-/* add playback controls from the parsed DAC table */
-static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct alc_spec *spec = codec->spec;
- static const char *chname[4] = {
- "Front", "Surround", NULL /*CLFE*/, "Side"
- };
- hda_nid_t nid;
- int i, err;
-
- if (cfg->line_outs == 1) {
- const char *pfx = NULL;
- if (!cfg->hp_outs)
- pfx = "Master";
- else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- pfx = "Speaker";
- if (pfx) {
- nid = spec->multiout.dac_nids[0];
- return alc861_create_out_sw(codec, pfx, nid, 3);
- }
- }
-
- for (i = 0; i < cfg->line_outs; i++) {
- nid = spec->multiout.dac_nids[i];
- if (!nid)
- continue;
- if (i == 2) {
- /* Center/LFE */
- err = alc861_create_out_sw(codec, "Center", nid, 1);
- if (err < 0)
- return err;
- err = alc861_create_out_sw(codec, "LFE", nid, 2);
- if (err < 0)
- return err;
- } else {
- err = alc861_create_out_sw(codec, chname[i], nid, 3);
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-static int alc861_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
-{
- struct alc_spec *spec = codec->spec;
- int err;
- hda_nid_t nid;
-
- if (!pin)
- return 0;
-
- if ((pin >= 0x0b && pin <= 0x10) || pin == 0x1f || pin == 0x20) {
- nid = alc861_look_for_dac(codec, pin);
- if (nid) {
- err = alc861_create_out_sw(codec, "Headphone", nid, 3);
- if (err < 0)
- return err;
- spec->multiout.hp_nid = nid;
- }
- }
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int alc861_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- return alc_auto_create_input_ctls(codec, cfg, 0x15, 0x08, 0);
-}
-
-static void alc861_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid,
- int pin_type, hda_nid_t dac)
-{
- hda_nid_t mix, srcs[5];
- int i, num;
-
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_type);
- snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
- if (snd_hda_get_connections(codec, nid, &mix, 1) != 1)
- return;
- num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs));
- if (num < 0)
- return;
- for (i = 0; i < num; i++) {
- unsigned int mute;
- if (srcs[i] == dac || srcs[i] == 0x15)
- mute = AMP_IN_UNMUTE(i);
- else
- mute = AMP_IN_MUTE(i);
- snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- mute);
- }
-}
-
-static void alc861_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.line_outs; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- if (nid)
- alc861_auto_set_output_and_unmute(codec, nid, pin_type,
- spec->multiout.dac_nids[i]);
- }
-}
-
-static void alc861_auto_init_hp_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- if (spec->autocfg.hp_outs)
- alc861_auto_set_output_and_unmute(codec,
- spec->autocfg.hp_pins[0],
- PIN_HP,
- spec->multiout.hp_nid);
- if (spec->autocfg.speaker_outs)
- alc861_auto_set_output_and_unmute(codec,
- spec->autocfg.speaker_pins[0],
- PIN_OUT,
- spec->multiout.dac_nids[0]);
-}
-
-static void alc861_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (nid >= 0x0c && nid <= 0x11)
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- }
-}
-
-/* parse the BIOS configuration and set up the alc_spec */
-/* return 1 if successful, 0 if the proper config is not found,
- * or a negative error code
- */
-static int alc861_parse_auto_config(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc861_ignore[] = { 0x1d, 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc861_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs)
- return 0; /* can't find valid BIOS pin config */
-
- err = alc861_auto_fill_dac_nids(codec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc861_auto_create_multi_out_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc861_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = alc861_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc861_auto_init_verbs);
-
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- spec->adc_nids = alc861_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
- set_capture_mixer(codec);
-
- alc_ssid_check(codec, 0x0e, 0x0f, 0x0b, 0);
-
- return 1;
-}
-
-/* additional initialization for auto-configuration model */
-static void alc861_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc861_auto_init_multi_out(codec);
- alc861_auto_init_hp_out(codec);
- alc861_auto_init_analog_input(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list alc861_loopbacks[] = {
- { 0x15, HDA_INPUT, 0 },
- { 0x15, HDA_INPUT, 1 },
- { 0x15, HDA_INPUT, 2 },
- { 0x15, HDA_INPUT, 3 },
- { } /* end */
-};
-#endif
-
-
-/*
- * configuration and preset
- */
-static const char *alc861_models[ALC861_MODEL_LAST] = {
- [ALC861_3ST] = "3stack",
- [ALC660_3ST] = "3stack-660",
- [ALC861_3ST_DIG] = "3stack-dig",
- [ALC861_6ST_DIG] = "6stack-dig",
- [ALC861_UNIWILL_M31] = "uniwill-m31",
- [ALC861_TOSHIBA] = "toshiba",
- [ALC861_ASUS] = "asus",
- [ALC861_ASUS_LAPTOP] = "asus-laptop",
- [ALC861_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc861_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST),
- SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS),
- SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS P1-AH2", ALC861_3ST_DIG),
- SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA),
- /* FIXME: the entry below breaks Toshiba A100 (model=auto works!)
- * Any other models that need this preset?
- */
- /* SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), */
- SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST),
- SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST),
- SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31),
- SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31),
- SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP),
- /* FIXME: the below seems conflict */
- /* SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31), */
- SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST),
- SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST),
- {}
-};
-
-static struct alc_config_preset alc861_presets[] = {
- [ALC861_3ST] = {
- .mixers = { alc861_3ST_mixer },
- .init_verbs = { alc861_threestack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
- .channel_mode = alc861_threestack_modes,
- .need_dac_fix = 1,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
- [ALC861_3ST_DIG] = {
- .mixers = { alc861_base_mixer },
- .init_verbs = { alc861_threestack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .dig_out_nid = ALC861_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
- .channel_mode = alc861_threestack_modes,
- .need_dac_fix = 1,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
- [ALC861_6ST_DIG] = {
- .mixers = { alc861_base_mixer },
- .init_verbs = { alc861_base_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .dig_out_nid = ALC861_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861_8ch_modes),
- .channel_mode = alc861_8ch_modes,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
- [ALC660_3ST] = {
- .mixers = { alc861_3ST_mixer },
- .init_verbs = { alc861_threestack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc660_dac_nids),
- .dac_nids = alc660_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes),
- .channel_mode = alc861_threestack_modes,
- .need_dac_fix = 1,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
- [ALC861_UNIWILL_M31] = {
- .mixers = { alc861_uniwill_m31_mixer },
- .init_verbs = { alc861_uniwill_m31_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .dig_out_nid = ALC861_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861_uniwill_m31_modes),
- .channel_mode = alc861_uniwill_m31_modes,
- .need_dac_fix = 1,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
- [ALC861_TOSHIBA] = {
- .mixers = { alc861_toshiba_mixer },
- .init_verbs = { alc861_base_init_verbs,
- alc861_toshiba_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- .unsol_event = alc861_toshiba_unsol_event,
- .init_hook = alc861_toshiba_automute,
- },
- [ALC861_ASUS] = {
- .mixers = { alc861_asus_mixer },
- .init_verbs = { alc861_asus_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .dig_out_nid = ALC861_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861_asus_modes),
- .channel_mode = alc861_asus_modes,
- .need_dac_fix = 1,
- .hp_nid = 0x06,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
- [ALC861_ASUS_LAPTOP] = {
- .mixers = { alc861_toshiba_mixer, alc861_asus_laptop_mixer },
- .init_verbs = { alc861_asus_init_verbs,
- alc861_asus_laptop_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861_dac_nids),
- .dac_nids = alc861_dac_nids,
- .dig_out_nid = ALC861_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
- .channel_mode = alc883_3ST_2ch_modes,
- .need_dac_fix = 1,
- .num_adc_nids = ARRAY_SIZE(alc861_adc_nids),
- .adc_nids = alc861_adc_nids,
- .input_mux = &alc861_capture_source,
- },
-};
-
-/* Pin config fixes */
-enum {
- PINFIX_FSC_AMILO_PI1505,
-};
-
-static const struct alc_fixup alc861_fixups[] = {
- [PINFIX_FSC_AMILO_PI1505] = {
- .pins = (const struct alc_pincfg[]) {
- { 0x0b, 0x0221101f }, /* HP */
- { 0x0f, 0x90170310 }, /* speaker */
- { }
- }
- },
-};
-
-static struct snd_pci_quirk alc861_fixup_tbl[] = {
- SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505),
- {}
-};
-
-static int patch_alc861(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int board_config;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- board_config = snd_hda_check_board_config(codec, ALC861_MODEL_LAST,
- alc861_models,
- alc861_cfg_tbl);
-
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC861_AUTO;
- }
-
- if (board_config == ALC861_AUTO)
- alc_pick_fixup(codec, alc861_fixup_tbl, alc861_fixups, 1);
-
- if (board_config == ALC861_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc861_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC861_3ST_DIG;
- }
- }
-
- err = snd_hda_attach_beep_device(codec, 0x23);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
-
- if (board_config != ALC861_AUTO)
- setup_preset(codec, &alc861_presets[board_config]);
-
- spec->stream_analog_playback = &alc861_pcm_analog_playback;
- spec->stream_analog_capture = &alc861_pcm_analog_capture;
-
- spec->stream_digital_playback = &alc861_pcm_digital_playback;
- spec->stream_digital_capture = &alc861_pcm_digital_capture;
-
- if (!spec->cap_mixer)
- set_capture_mixer(codec);
- set_beep_amp(spec, 0x23, 0, HDA_OUTPUT);
-
- spec->vmaster_nid = 0x03;
-
- if (board_config == ALC861_AUTO)
- alc_pick_fixup(codec, alc861_fixup_tbl, alc861_fixups, 0);
-
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC861_AUTO) {
- spec->init_hook = alc861_auto_init;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->power_hook = alc_power_eapd;
-#endif
- }
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc861_loopbacks;
-#endif
-
- return 0;
-}
-
-/*
- * ALC861-VD support
- *
- * Based on ALC882
- *
- * In addition, an independent DAC
- */
-#define ALC861VD_DIGOUT_NID 0x06
-
-static hda_nid_t alc861vd_dac_nids[4] = {
- /* front, surr, clfe, side surr */
- 0x02, 0x03, 0x04, 0x05
-};
-
-/* dac_nids for ALC660vd are in a different order - according to
- * Realtek's driver.
- * This should probably result in a different mixer for 6stack models
- * of ALC660vd codecs, but for now there is only 3stack mixer
- * - and it is the same as in 861vd.
- * adc_nids in ALC660vd are (is) the same as in 861vd
- */
-static hda_nid_t alc660vd_dac_nids[3] = {
- /* front, rear, clfe, rear_surr */
- 0x02, 0x04, 0x03
-};
-
-static hda_nid_t alc861vd_adc_nids[1] = {
- /* ADC0 */
- 0x09,
-};
-
-static hda_nid_t alc861vd_capsrc_nids[1] = { 0x22 };
-
-/* input MUX */
-/* FIXME: should be a matrix-type input source selection */
-static struct hda_input_mux alc861vd_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-static struct hda_input_mux alc861vd_dallas_capture_source = {
- .num_items = 2,
- .items = {
- { "Ext Mic", 0x0 },
- { "Int Mic", 0x1 },
- },
-};
-
-static struct hda_input_mux alc861vd_hp_capture_source = {
- .num_items = 2,
- .items = {
- { "Front Mic", 0x0 },
- { "ATAPI Mic", 0x1 },
- },
-};
-
-/*
- * 2ch mode
- */
-static struct hda_channel_mode alc861vd_3stack_2ch_modes[1] = {
- { 2, NULL }
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc861vd_6stack_ch6_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-/*
- * 8ch mode
- */
-static struct hda_verb alc861vd_6stack_ch8_init[] = {
- { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-static struct hda_channel_mode alc861vd_6stack_modes[2] = {
- { 6, alc861vd_6stack_ch6_init },
- { 8, alc861vd_6stack_ch8_init },
-};
-
-static struct snd_kcontrol_new alc861vd_chmode_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
- * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
- */
-static struct snd_kcontrol_new alc861vd_6st_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0,
- HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0,
- HDA_OUTPUT),
- HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
- HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Side Playback Volume", 0x05, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
-
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
-
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc861vd_3st_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
-
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
-
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc861vd_lenovo_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- /*HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),*/
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
-
- { } /* end */
-};
-
-/* Pin assignment: Speaker=0x14, HP = 0x15,
- * Ext Mic=0x18, Int Mic = 0x19, CD = 0x1c, PC Beep = 0x1d
- */
-static struct snd_kcontrol_new alc861vd_dallas_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Ext Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-/* Pin assignment: Speaker=0x14, Line-out = 0x15,
- * Front Mic=0x18, ATAPI Mic = 0x19,
- */
-static struct snd_kcontrol_new alc861vd_hp_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-
- { } /* end */
-};
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc861vd_volume_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of
- * the analog-loopback mixer widget
- */
- /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /* Capture mixer: unmute Mic, F-Mic, Line, CD inputs */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /*
- * Set up output mixers (0x02 - 0x05)
- */
- /* set vol=0 to output mixers */
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- { }
-};
-
-/*
- * 3-stack pin configuration:
- * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b
- */
-static struct hda_verb alc861vd_3stack_init_verbs[] = {
- /*
- * Set pin mode and muting
- */
- /* set front pin widgets 0x14 for output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Mic (rear) pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line-2 In: Headphone output (output 0 - 0x0c) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-/*
- * 6-stack pin configuration:
- */
-static struct hda_verb alc861vd_6stack_init_verbs[] = {
- /*
- * Set pin mode and muting
- */
- /* set front pin widgets 0x14 for output */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- /* Rear Pin: output 1 (0x0d) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- /* CLFE Pin: output 2 (0x0e) */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
- /* Side Pin: output 3 (0x0f) */
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
-
- /* Mic (rear) pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line-2 In: Headphone output (output 0 - 0x0c) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- { }
-};
-
-static struct hda_verb alc861vd_eapd_verbs[] = {
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-static struct hda_verb alc660vd_eapd_verbs[] = {
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
- { }
-};
-
-static struct hda_verb alc861vd_lenovo_unsol_verbs[] = {
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {}
-};
-
-static void alc861vd_lenovo_mic_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x18);
- bits = present ? HDA_AMP_MUTE : 0;
-
- snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc861vd_lenovo_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- spec->autocfg.hp_pins[0] = 0x1b;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-static void alc861vd_lenovo_init_hook(struct hda_codec *codec)
-{
- alc_automute_amp(codec);
- alc861vd_lenovo_mic_automute(codec);
-}
-
-static void alc861vd_lenovo_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_MIC_EVENT:
- alc861vd_lenovo_mic_automute(codec);
- break;
- default:
- alc_automute_amp_unsol_event(codec, res);
- break;
- }
-}
-
-static struct hda_verb alc861vd_dallas_verbs[] = {
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
-
- { } /* end */
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc861vd_dallas_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x15;
- spec->autocfg.speaker_pins[0] = 0x14;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc861vd_loopbacks alc880_loopbacks
-#endif
-
-/* pcm configuration: identical with ALC880 */
-#define alc861vd_pcm_analog_playback alc880_pcm_analog_playback
-#define alc861vd_pcm_analog_capture alc880_pcm_analog_capture
-#define alc861vd_pcm_digital_playback alc880_pcm_digital_playback
-#define alc861vd_pcm_digital_capture alc880_pcm_digital_capture
-
-/*
- * configuration and preset
- */
-static const char *alc861vd_models[ALC861VD_MODEL_LAST] = {
- [ALC660VD_3ST] = "3stack-660",
- [ALC660VD_3ST_DIG] = "3stack-660-digout",
- [ALC660VD_ASUS_V1S] = "asus-v1s",
- [ALC861VD_3ST] = "3stack",
- [ALC861VD_3ST_DIG] = "3stack-digout",
- [ALC861VD_6ST_DIG] = "6stack-digout",
- [ALC861VD_LENOVO] = "lenovo",
- [ALC861VD_DALLAS] = "dallas",
- [ALC861VD_HP] = "hp",
- [ALC861VD_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST),
- SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP),
- SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST),
- /*SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),*/ /* auto */
- SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC660VD_ASUS_V1S),
- SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG),
- SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST),
- SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
- /*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/
- SND_PCI_QUIRK(0x1179, 0xff01, "Toshiba A135", ALC861VD_LENOVO),
- SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO),
- SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba L30-149", ALC861VD_DALLAS),
- SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG),
- SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", ALC861VD_LENOVO),
- SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG),
- {}
-};
-
-static struct alc_config_preset alc861vd_presets[] = {
- [ALC660VD_3ST] = {
- .mixers = { alc861vd_3st_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_3stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
- .dac_nids = alc660vd_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_capture_source,
- },
- [ALC660VD_3ST_DIG] = {
- .mixers = { alc861vd_3st_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_3stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
- .dac_nids = alc660vd_dac_nids,
- .dig_out_nid = ALC861VD_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_capture_source,
- },
- [ALC861VD_3ST] = {
- .mixers = { alc861vd_3st_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_3stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
- .dac_nids = alc861vd_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_capture_source,
- },
- [ALC861VD_3ST_DIG] = {
- .mixers = { alc861vd_3st_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_3stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
- .dac_nids = alc861vd_dac_nids,
- .dig_out_nid = ALC861VD_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_capture_source,
- },
- [ALC861VD_6ST_DIG] = {
- .mixers = { alc861vd_6st_mixer, alc861vd_chmode_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_6stack_init_verbs },
- .num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
- .dac_nids = alc861vd_dac_nids,
- .dig_out_nid = ALC861VD_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861vd_6stack_modes),
- .channel_mode = alc861vd_6stack_modes,
- .input_mux = &alc861vd_capture_source,
- },
- [ALC861VD_LENOVO] = {
- .mixers = { alc861vd_lenovo_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_3stack_init_verbs,
- alc861vd_eapd_verbs,
- alc861vd_lenovo_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
- .dac_nids = alc660vd_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_capture_source,
- .unsol_event = alc861vd_lenovo_unsol_event,
- .setup = alc861vd_lenovo_setup,
- .init_hook = alc861vd_lenovo_init_hook,
- },
- [ALC861VD_DALLAS] = {
- .mixers = { alc861vd_dallas_mixer },
- .init_verbs = { alc861vd_dallas_verbs },
- .num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
- .dac_nids = alc861vd_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_dallas_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc861vd_dallas_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC861VD_HP] = {
- .mixers = { alc861vd_hp_mixer },
- .init_verbs = { alc861vd_dallas_verbs, alc861vd_eapd_verbs },
- .num_dacs = ARRAY_SIZE(alc861vd_dac_nids),
- .dac_nids = alc861vd_dac_nids,
- .dig_out_nid = ALC861VD_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_hp_capture_source,
- .unsol_event = alc_automute_amp_unsol_event,
- .setup = alc861vd_dallas_setup,
- .init_hook = alc_automute_amp,
- },
- [ALC660VD_ASUS_V1S] = {
- .mixers = { alc861vd_lenovo_mixer },
- .init_verbs = { alc861vd_volume_init_verbs,
- alc861vd_3stack_init_verbs,
- alc861vd_eapd_verbs,
- alc861vd_lenovo_unsol_verbs },
- .num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
- .dac_nids = alc660vd_dac_nids,
- .dig_out_nid = ALC861VD_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
- .channel_mode = alc861vd_3stack_2ch_modes,
- .input_mux = &alc861vd_capture_source,
- .unsol_event = alc861vd_lenovo_unsol_event,
- .setup = alc861vd_lenovo_setup,
- .init_hook = alc861vd_lenovo_init_hook,
- },
-};
-
-/*
- * BIOS auto configuration
- */
-static int alc861vd_auto_create_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- return alc_auto_create_input_ctls(codec, cfg, 0x15, 0x09, 0);
-}
-
-
-static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type, int dac_idx)
-{
- alc_set_pin_output(codec, nid, pin_type);
-}
-
-static void alc861vd_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i <= HDA_SIDE; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- if (nid)
- alc861vd_auto_set_output_and_unmute(codec, nid,
- pin_type, i);
- }
-}
-
-
-static void alc861vd_auto_init_hp_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t pin;
-
- pin = spec->autocfg.hp_pins[0];
- if (pin) /* connect to front and use dac 0 */
- alc861vd_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
- pin = spec->autocfg.speaker_pins[0];
- if (pin)
- alc861vd_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
-}
-
-#define ALC861VD_PIN_CD_NID ALC880_PIN_CD_NID
-
-static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (alc_is_input_pin(codec, nid)) {
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (nid != ALC861VD_PIN_CD_NID &&
- (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
- }
-}
-
-#define alc861vd_auto_init_input_src alc882_auto_init_input_src
-
-#define alc861vd_idx_to_mixer_vol(nid) ((nid) + 0x02)
-#define alc861vd_idx_to_mixer_switch(nid) ((nid) + 0x0c)
-
-/* add playback controls from the parsed DAC table */
-/* Based on ALC880 version. But ALC861VD has separate,
- * different NIDs for mute/unmute switch and volume control */
-static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- static const char *chname[4] = {"Front", "Surround", "CLFE", "Side"};
- hda_nid_t nid_v, nid_s;
- int i, err;
-
- for (i = 0; i < cfg->line_outs; i++) {
- if (!spec->multiout.dac_nids[i])
- continue;
- nid_v = alc861vd_idx_to_mixer_vol(
- alc880_dac_to_idx(
- spec->multiout.dac_nids[i]));
- nid_s = alc861vd_idx_to_mixer_switch(
- alc880_dac_to_idx(
- spec->multiout.dac_nids[i]));
-
- if (i == 2) {
- /* Center/LFE */
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
- "Center",
- HDA_COMPOSE_AMP_VAL(nid_v, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
- "LFE",
- HDA_COMPOSE_AMP_VAL(nid_v, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
- "Center",
- HDA_COMPOSE_AMP_VAL(nid_s, 1, 2,
- HDA_INPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
- "LFE",
- HDA_COMPOSE_AMP_VAL(nid_s, 2, 2,
- HDA_INPUT));
- if (err < 0)
- return err;
- } else {
- const char *pfx;
- if (cfg->line_outs == 1 &&
- cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
- if (!cfg->hp_pins)
- pfx = "Speaker";
- else
- pfx = "PCM";
- } else
- pfx = chname[i];
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
- HDA_COMPOSE_AMP_VAL(nid_v, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- if (cfg->line_outs == 1 &&
- cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- pfx = "Speaker";
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(nid_s, 3, 2,
- HDA_INPUT));
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-/* add playback controls for speaker and HP outputs */
-/* Based on ALC880 version. But ALC861VD has separate,
- * different NIDs for mute/unmute switch and volume control */
-static int alc861vd_auto_create_extra_out(struct alc_spec *spec,
- hda_nid_t pin, const char *pfx)
-{
- hda_nid_t nid_v, nid_s;
- int err;
-
- if (!pin)
- return 0;
-
- if (alc880_is_fixed_pin(pin)) {
- nid_v = alc880_idx_to_dac(alc880_fixed_pin_idx(pin));
- /* specify the DAC as the extra output */
- if (!spec->multiout.hp_nid)
- spec->multiout.hp_nid = nid_v;
- else
- spec->multiout.extra_out_nid[0] = nid_v;
- /* control HP volume/switch on the output mixer amp */
- nid_v = alc861vd_idx_to_mixer_vol(
- alc880_fixed_pin_idx(pin));
- nid_s = alc861vd_idx_to_mixer_switch(
- alc880_fixed_pin_idx(pin));
-
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
- HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT));
- if (err < 0)
- return err;
- } else if (alc880_is_multi_pin(pin)) {
- /* set manual connection */
- /* we have only a switch on HP-out PIN */
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-/* parse the BIOS configuration and set up the alc_spec
- * return 1 if successful, 0 if the proper config is not found,
- * or a negative error code
- * Based on ALC880 version - had to change it to override
- * alc880_auto_create_extra_out and alc880_auto_create_multi_out_ctls */
-static int alc861vd_parse_auto_config(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc861vd_ignore[] = { 0x1d, 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc861vd_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs)
- return 0; /* can't find valid BIOS pin config */
-
- err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc861vd_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc861vd_auto_create_extra_out(spec,
- spec->autocfg.speaker_pins[0],
- "Speaker");
- if (err < 0)
- return err;
- err = alc861vd_auto_create_extra_out(spec,
- spec->autocfg.hp_pins[0],
- "Headphone");
- if (err < 0)
- return err;
- err = alc861vd_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc861vd_volume_init_verbs);
-
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
-
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
- return 1;
-}
-
-/* additional initialization for auto-configuration model */
-static void alc861vd_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc861vd_auto_init_multi_out(codec);
- alc861vd_auto_init_hp_out(codec);
- alc861vd_auto_init_analog_input(codec);
- alc861vd_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-enum {
- ALC660VD_FIX_ASUS_GPIO1
-};
-
-/* reset GPIO1 */
-static const struct alc_fixup alc861vd_fixups[] = {
- [ALC660VD_FIX_ASUS_GPIO1] = {
- .verbs = (const struct hda_verb[]) {
- {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
- { }
- }
- },
-};
-
-static struct snd_pci_quirk alc861vd_fixup_tbl[] = {
- SND_PCI_QUIRK(0x1043, 0x1339, "ASUS A7-K", ALC660VD_FIX_ASUS_GPIO1),
- {}
-};
-
-static int patch_alc861vd(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int err, board_config;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- board_config = snd_hda_check_board_config(codec, ALC861VD_MODEL_LAST,
- alc861vd_models,
- alc861vd_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC861VD_MODEL_LAST) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC861VD_AUTO;
- }
-
- if (board_config == ALC861VD_AUTO)
- alc_pick_fixup(codec, alc861vd_fixup_tbl, alc861vd_fixups, 1);
-
- if (board_config == ALC861VD_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc861vd_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC861VD_3ST;
- }
- }
-
- err = snd_hda_attach_beep_device(codec, 0x23);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
-
- if (board_config != ALC861VD_AUTO)
- setup_preset(codec, &alc861vd_presets[board_config]);
-
- if (codec->vendor_id == 0x10ec0660) {
- /* always turn on EAPD */
- add_verb(spec, alc660vd_eapd_verbs);
- }
-
- spec->stream_analog_playback = &alc861vd_pcm_analog_playback;
- spec->stream_analog_capture = &alc861vd_pcm_analog_capture;
-
- spec->stream_digital_playback = &alc861vd_pcm_digital_playback;
- spec->stream_digital_capture = &alc861vd_pcm_digital_capture;
-
- if (!spec->adc_nids) {
- spec->adc_nids = alc861vd_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids);
- }
- if (!spec->capsrc_nids)
- spec->capsrc_nids = alc861vd_capsrc_nids;
-
- set_capture_mixer(codec);
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
-
- spec->vmaster_nid = 0x02;
-
- if (board_config == ALC861VD_AUTO)
- alc_pick_fixup(codec, alc861vd_fixup_tbl, alc861vd_fixups, 0);
-
- codec->patch_ops = alc_patch_ops;
-
- if (board_config == ALC861VD_AUTO)
- spec->init_hook = alc861vd_auto_init;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc861vd_loopbacks;
-#endif
-
- return 0;
-}
-
-/*
- * ALC662 support
- *
- * ALC662 is almost identical with ALC880 but has cleaner and more flexible
- * configuration. Each pin widget can choose any input DACs and a mixer.
- * Each ADC is connected from a mixer of all inputs. This makes possible
- * 6-channel independent captures.
- *
- * In addition, an independent DAC for the multi-playback (not used in this
- * driver yet).
- */
-#define ALC662_DIGOUT_NID 0x06
-#define ALC662_DIGIN_NID 0x0a
-
-static hda_nid_t alc662_dac_nids[4] = {
- /* front, rear, clfe, rear_surr */
- 0x02, 0x03, 0x04
-};
-
-static hda_nid_t alc272_dac_nids[2] = {
- 0x02, 0x03
-};
-
-static hda_nid_t alc662_adc_nids[2] = {
- /* ADC1-2 */
- 0x09, 0x08
-};
-
-static hda_nid_t alc272_adc_nids[1] = {
- /* ADC1-2 */
- 0x08,
-};
-
-static hda_nid_t alc662_capsrc_nids[2] = { 0x22, 0x23 };
-static hda_nid_t alc272_capsrc_nids[1] = { 0x23 };
-
-
-/* input MUX */
-/* FIXME: should be a matrix-type input source selection */
-static struct hda_input_mux alc662_capture_source = {
- .num_items = 4,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- { "CD", 0x4 },
- },
-};
-
-static struct hda_input_mux alc662_lenovo_101e_capture_source = {
- .num_items = 2,
- .items = {
- { "Mic", 0x1 },
- { "Line", 0x2 },
- },
-};
-
-static struct hda_input_mux alc663_capture_source = {
- .num_items = 3,
- .items = {
- { "Mic", 0x0 },
- { "Front Mic", 0x1 },
- { "Line", 0x2 },
- },
-};
-
-#if 0 /* set to 1 for testing other input sources below */
-static struct hda_input_mux alc272_nc10_capture_source = {
- .num_items = 16,
- .items = {
- { "Autoselect Mic", 0x0 },
- { "Internal Mic", 0x1 },
- { "In-0x02", 0x2 },
- { "In-0x03", 0x3 },
- { "In-0x04", 0x4 },
- { "In-0x05", 0x5 },
- { "In-0x06", 0x6 },
- { "In-0x07", 0x7 },
- { "In-0x08", 0x8 },
- { "In-0x09", 0x9 },
- { "In-0x0a", 0x0a },
- { "In-0x0b", 0x0b },
- { "In-0x0c", 0x0c },
- { "In-0x0d", 0x0d },
- { "In-0x0e", 0x0e },
- { "In-0x0f", 0x0f },
- },
-};
-#endif
-
-/*
- * 2ch mode
- */
-static struct hda_channel_mode alc662_3ST_2ch_modes[1] = {
- { 2, NULL }
-};
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc662_3ST_ch2_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc662_3ST_ch6_init[] = {
- { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
- { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
- { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
- { } /* end */
-};
-
-static struct hda_channel_mode alc662_3ST_6ch_modes[2] = {
- { 2, alc662_3ST_ch2_init },
- { 6, alc662_3ST_ch6_init },
-};
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc662_sixstack_ch6_init[] = {
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc662_sixstack_ch8_init[] = {
- { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- { } /* end */
-};
-
-static struct hda_channel_mode alc662_5stack_modes[2] = {
- { 2, alc662_sixstack_ch6_init },
- { 6, alc662_sixstack_ch8_init },
-};
-
-/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
- * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
- */
-
-static struct snd_kcontrol_new alc662_base_mixer[] = {
- /* output mixer control */
- HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x3, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-
- /*Input mixer control */
- HDA_CODEC_VOLUME("CD Playback Volume", 0xb, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0xb, 0x4, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0xb, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0xb, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0xb, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0xb, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0xb, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0xb, 0x01, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x0c, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Surround Playback Switch", 0x0d, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc662_lenovo_101e_mixer[] = {
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Speaker Playback Switch", 0x03, 2, HDA_INPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- ALC262_HIPPO_MASTER_SWITCH,
-
- HDA_CODEC_VOLUME("e-Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("e-Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("e-Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("i-Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("i-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("i-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc662_eeepc_ep20_mixer[] = {
- ALC262_HIPPO_MASTER_SWITCH,
- HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("MuteCtrl Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_bind_ctls alc663_asus_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct hda_bind_ctls alc663_asus_one_bind_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc663_m51va_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol),
- HDA_BIND_SW("Master Playback Switch", &alc663_asus_one_bind_switch),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_bind_ctls alc663_asus_tree_bind_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc663_two_hp_m1_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol),
- HDA_BIND_SW("Master Playback Switch", &alc663_asus_tree_bind_switch),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-
- { } /* end */
-};
-
-static struct hda_bind_ctls alc663_asus_four_bind_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc663_two_hp_m2_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol),
- HDA_BIND_SW("Master Playback Switch", &alc663_asus_four_bind_switch),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc662_1bjd_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_bind_ctls alc663_asus_two_bind_master_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x04, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct hda_bind_ctls alc663_asus_two_bind_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc663_asus_21jd_clfe_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume",
- &alc663_asus_two_bind_master_vol),
- HDA_BIND_SW("Master Playback Switch", &alc663_asus_two_bind_switch),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc663_asus_15jd_clfe_mixer[] = {
- HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol),
- HDA_BIND_SW("Master Playback Switch", &alc663_asus_two_bind_switch),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc663_g71v_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Front Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("i-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("i-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc663_g50v_mixer[] = {
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("i-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("i-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
- { } /* end */
-};
-
-static struct hda_bind_ctls alc663_asus_mode7_8_all_bind_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct hda_bind_ctls alc663_asus_mode7_8_sp_bind_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc663_mode7_mixer[] = {
- HDA_BIND_SW("Master Playback Switch", &alc663_asus_mode7_8_all_bind_switch),
- HDA_BIND_VOL("Speaker Playback Volume", &alc663_asus_bind_master_vol),
- HDA_BIND_SW("Speaker Playback Switch", &alc663_asus_mode7_8_sp_bind_switch),
- HDA_CODEC_MUTE("Headphone1 Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone2 Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("IntMic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("IntMic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc663_mode8_mixer[] = {
- HDA_BIND_SW("Master Playback Switch", &alc663_asus_mode7_8_all_bind_switch),
- HDA_BIND_VOL("Speaker Playback Volume", &alc663_asus_bind_master_vol),
- HDA_BIND_SW("Speaker Playback Switch", &alc663_asus_mode7_8_sp_bind_switch),
- HDA_CODEC_MUTE("Headphone1 Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone2 Playback Switch", 0x21, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-
-static struct snd_kcontrol_new alc662_chmode_mixer[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = alc_ch_mode_info,
- .get = alc_ch_mode_get,
- .put = alc_ch_mode_put,
- },
- { } /* end */
-};
-
-static struct hda_verb alc662_init_verbs[] = {
- /* ADC: mute amp left and right */
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
-
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Front Pin: output 0 (0x0c) */
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Rear Pin: output 1 (0x0d) */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* CLFE Pin: output 2 (0x0e) */
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Mic (rear) pin: input vref at 80% */
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Front Mic pin: input vref at 80% */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line In pin: input */
- {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- /* Line-2 In: Headphone output (output 0 - 0x0c) */
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* CD pin widget for input */
- {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* always trun on EAPD */
- {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
- {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
-
- { }
-};
-
-static struct hda_verb alc663_init_verbs[] = {
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- { }
-};
-
-static struct hda_verb alc272_init_verbs[] = {
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- { }
-};
-
-static struct hda_verb alc662_sue_init_verbs[] = {
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_FRONT_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc662_eeepc_sue_init_verbs[] = {
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-/* Set Unsolicited Event*/
-static struct hda_verb alc662_eeepc_ep20_sue_init_verbs[] = {
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_m51va_init_verbs[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_21jd_amic_init_verbs[] = {
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc662_1bjd_amic_init_verbs[] = {
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_15jd_amic_init_verbs[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_two_hp_amic_m1_init_verbs[] = {
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x0}, /* Headphone */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x0}, /* Headphone */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_two_hp_amic_m2_init_verbs[] = {
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_g71v_init_verbs[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- /* {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, */
- /* {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, */ /* Headphone */
-
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */
-
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_FRONT_EVENT},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_g50v_init_verbs[] = {
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Headphone */
-
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc662_ecs_init_verbs[] = {
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc272_dell_zm1_init_verbs[] = {
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc272_dell_init_verbs[] = {
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_mode7_init_verbs[] = {
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct hda_verb alc663_mode8_init_verbs[] = {
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x21, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Headphone */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
- {0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
- {}
-};
-
-static struct snd_kcontrol_new alc662_auto_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc272_auto_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
- { } /* end */
-};
-
-static void alc662_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x14);
- bits = present ? HDA_AMP_MUTE : 0;
-
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc662_lenovo_101e_all_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x1b);
- bits = present ? HDA_AMP_MUTE : 0;
-
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc662_lenovo_101e_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc662_lenovo_101e_all_automute(codec);
- if ((res >> 26) == ALC880_FRONT_EVENT)
- alc662_lenovo_101e_ispeaker_automute(codec);
-}
-
-/* unsolicited event for HP jack sensing */
-static void alc662_eeepc_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_MIC_EVENT)
- alc_mic_automute(codec);
- else
- alc262_hippo_unsol_event(codec, res);
-}
-
-static void alc662_eeepc_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- alc262_hippo1_setup(codec);
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
-}
-
-static void alc662_eeepc_inithook(struct hda_codec *codec)
-{
- alc262_hippo_automute(codec);
- alc_mic_automute(codec);
-}
-
-static void alc662_eeepc_ep20_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x14;
- spec->autocfg.speaker_pins[0] = 0x1b;
-}
-
-#define alc662_eeepc_ep20_inithook alc262_hippo_master_update
-
-static void alc663_m51va_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x21);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc663_21jd_two_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x21);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc663_15jd_two_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x15);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 1,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc662_f5z_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x1b);
- bits = present ? 0 : PIN_OUT;
- snd_hda_codec_write(codec, 0x14, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, bits);
-}
-
-static void alc663_two_hp_m1_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present1, present2;
-
- present1 = snd_hda_jack_detect(codec, 0x21);
- present2 = snd_hda_jack_detect(codec, 0x15);
-
- if (present1 || present2) {
- snd_hda_codec_write_cache(codec, 0x14, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
- } else {
- snd_hda_codec_write_cache(codec, 0x14, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- }
-}
-
-static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present1, present2;
-
- present1 = snd_hda_jack_detect(codec, 0x1b);
- present2 = snd_hda_jack_detect(codec, 0x15);
-
- if (present1 || present2) {
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, HDA_AMP_MUTE);
- } else {
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
- HDA_AMP_MUTE, 0);
- snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
- HDA_AMP_MUTE, 0);
- }
-}
-
-static void alc663_two_hp_m7_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present1, present2;
-
- present1 = snd_hda_codec_read(codec, 0x1b, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
- present2 = snd_hda_codec_read(codec, 0x21, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
-
- if (present1 || present2) {
- snd_hda_codec_write_cache(codec, 0x14, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
- snd_hda_codec_write_cache(codec, 0x17, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
- } else {
- snd_hda_codec_write_cache(codec, 0x14, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- snd_hda_codec_write_cache(codec, 0x17, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- }
-}
-
-static void alc663_two_hp_m8_speaker_automute(struct hda_codec *codec)
-{
- unsigned int present1, present2;
-
- present1 = snd_hda_codec_read(codec, 0x21, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
- present2 = snd_hda_codec_read(codec, 0x15, 0,
- AC_VERB_GET_PIN_SENSE, 0)
- & AC_PINSENSE_PRESENCE;
-
- if (present1 || present2) {
- snd_hda_codec_write_cache(codec, 0x14, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
- snd_hda_codec_write_cache(codec, 0x17, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
- } else {
- snd_hda_codec_write_cache(codec, 0x14, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- snd_hda_codec_write_cache(codec, 0x17, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- }
-}
-
-static void alc663_m51va_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_m51va_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-static void alc663_m51va_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x12;
- spec->int_mic.mux_idx = 9;
- spec->auto_mic = 1;
-}
-
-static void alc663_m51va_inithook(struct hda_codec *codec)
-{
- alc663_m51va_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-
-/* ***************** Mode1 ******************************/
-#define alc663_mode1_unsol_event alc663_m51va_unsol_event
-
-static void alc663_mode1_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- spec->ext_mic.pin = 0x18;
- spec->ext_mic.mux_idx = 0;
- spec->int_mic.pin = 0x19;
- spec->int_mic.mux_idx = 1;
- spec->auto_mic = 1;
-}
-
-#define alc663_mode1_inithook alc663_m51va_inithook
-
-/* ***************** Mode2 ******************************/
-static void alc662_mode2_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc662_f5z_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc662_mode2_setup alc663_mode1_setup
-
-static void alc662_mode2_inithook(struct hda_codec *codec)
-{
- alc662_f5z_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-/* ***************** Mode3 ******************************/
-static void alc663_mode3_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_two_hp_m1_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_mode3_setup alc663_mode1_setup
-
-static void alc663_mode3_inithook(struct hda_codec *codec)
-{
- alc663_two_hp_m1_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-/* ***************** Mode4 ******************************/
-static void alc663_mode4_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_21jd_two_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_mode4_setup alc663_mode1_setup
-
-static void alc663_mode4_inithook(struct hda_codec *codec)
-{
- alc663_21jd_two_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-/* ***************** Mode5 ******************************/
-static void alc663_mode5_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_15jd_two_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_mode5_setup alc663_mode1_setup
-
-static void alc663_mode5_inithook(struct hda_codec *codec)
-{
- alc663_15jd_two_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-/* ***************** Mode6 ******************************/
-static void alc663_mode6_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_two_hp_m2_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_mode6_setup alc663_mode1_setup
-
-static void alc663_mode6_inithook(struct hda_codec *codec)
-{
- alc663_two_hp_m2_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-
-/* ***************** Mode7 ******************************/
-static void alc663_mode7_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_two_hp_m7_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_mode7_setup alc663_mode1_setup
-
-static void alc663_mode7_inithook(struct hda_codec *codec)
-{
- alc663_two_hp_m7_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-
-/* ***************** Mode8 ******************************/
-static void alc663_mode8_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_two_hp_m8_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_mode8_setup alc663_m51va_setup
-
-static void alc663_mode8_inithook(struct hda_codec *codec)
-{
- alc663_two_hp_m8_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-
-static void alc663_g71v_hp_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x21);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc663_g71v_front_automute(struct hda_codec *codec)
-{
- unsigned int present;
- unsigned char bits;
-
- present = snd_hda_jack_detect(codec, 0x15);
- bits = present ? HDA_AMP_MUTE : 0;
- snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, bits);
-}
-
-static void alc663_g71v_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_g71v_hp_automute(codec);
- break;
- case ALC880_FRONT_EVENT:
- alc663_g71v_front_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_g71v_setup alc663_m51va_setup
-
-static void alc663_g71v_inithook(struct hda_codec *codec)
-{
- alc663_g71v_front_automute(codec);
- alc663_g71v_hp_automute(codec);
- alc_mic_automute(codec);
-}
-
-static void alc663_g50v_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- switch (res >> 26) {
- case ALC880_HP_EVENT:
- alc663_m51va_speaker_automute(codec);
- break;
- case ALC880_MIC_EVENT:
- alc_mic_automute(codec);
- break;
- }
-}
-
-#define alc663_g50v_setup alc663_m51va_setup
-
-static void alc663_g50v_inithook(struct hda_codec *codec)
-{
- alc663_m51va_speaker_automute(codec);
- alc_mic_automute(codec);
-}
-
-static struct snd_kcontrol_new alc662_ecs_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- ALC262_HIPPO_MASTER_SWITCH,
-
- HDA_CODEC_VOLUME("e-Mic/LineIn Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("e-Mic/LineIn Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("e-Mic/LineIn Playback Switch", 0x0b, 0x0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("i-Mic Boost", 0x19, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("i-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("i-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new alc272_nc10_mixer[] = {
- /* Master Playback automatically created from Speaker and Headphone */
- HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
- HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Ext Mic Boost", 0x18, 0, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
- { } /* end */
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc662_loopbacks alc880_loopbacks
-#endif
-
-
-/* pcm configuration: identical with ALC880 */
-#define alc662_pcm_analog_playback alc880_pcm_analog_playback
-#define alc662_pcm_analog_capture alc880_pcm_analog_capture
-#define alc662_pcm_digital_playback alc880_pcm_digital_playback
-#define alc662_pcm_digital_capture alc880_pcm_digital_capture
-
-/*
- * configuration and preset
- */
-static const char *alc662_models[ALC662_MODEL_LAST] = {
- [ALC662_3ST_2ch_DIG] = "3stack-dig",
- [ALC662_3ST_6ch_DIG] = "3stack-6ch-dig",
- [ALC662_3ST_6ch] = "3stack-6ch",
- [ALC662_5ST_DIG] = "6stack-dig",
- [ALC662_LENOVO_101E] = "lenovo-101e",
- [ALC662_ASUS_EEEPC_P701] = "eeepc-p701",
- [ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20",
- [ALC662_ECS] = "ecs",
- [ALC663_ASUS_M51VA] = "m51va",
- [ALC663_ASUS_G71V] = "g71v",
- [ALC663_ASUS_H13] = "h13",
- [ALC663_ASUS_G50V] = "g50v",
- [ALC663_ASUS_MODE1] = "asus-mode1",
- [ALC662_ASUS_MODE2] = "asus-mode2",
- [ALC663_ASUS_MODE3] = "asus-mode3",
- [ALC663_ASUS_MODE4] = "asus-mode4",
- [ALC663_ASUS_MODE5] = "asus-mode5",
- [ALC663_ASUS_MODE6] = "asus-mode6",
- [ALC663_ASUS_MODE7] = "asus-mode7",
- [ALC663_ASUS_MODE8] = "asus-mode8",
- [ALC272_DELL] = "dell",
- [ALC272_DELL_ZM1] = "dell-zm1",
- [ALC272_SAMSUNG_NC10] = "samsung-nc10",
- [ALC662_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc662_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_ECS),
- SND_PCI_QUIRK(0x1028, 0x02d6, "DELL", ALC272_DELL),
- SND_PCI_QUIRK(0x1028, 0x02f4, "DELL ZM1", ALC272_DELL_ZM1),
- SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x1173, "ASUS K73Jn", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1303, "ASUS G60J", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1333, "ASUS G60Jx", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x13e3, "ASUS N71JA", ALC663_ASUS_MODE7),
- SND_PCI_QUIRK(0x1043, 0x1463, "ASUS N71", ALC663_ASUS_MODE7),
- SND_PCI_QUIRK(0x1043, 0x14d3, "ASUS G72", ALC663_ASUS_MODE8),
- SND_PCI_QUIRK(0x1043, 0x1563, "ASUS N90", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x15d3, "ASUS N50SF F50SF", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x16f3, "ASUS K40C K50C", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1733, "ASUS N81De", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC663_ASUS_MODE6),
- SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC663_ASUS_MODE6),
- SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1793, "ASUS F50GX", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x17b3, "ASUS F70SL", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x17c3, "ASUS UX20", ALC663_ASUS_M51VA),
- SND_PCI_QUIRK(0x1043, 0x17f3, "ASUS X58LE", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC663_ASUS_MODE5),
- SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC663_ASUS_MODE6),
- SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1853, "ASUS F50Z", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M51VA", ALC663_ASUS_M51VA),
- /*SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M50Vr", ALC663_ASUS_MODE1),*/
- SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x18c3, "ASUS VX5", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x1943, "ASUS Vx3V", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC663_ASUS_MODE3),
- SND_PCI_QUIRK(0x1043, 0x1983, "ASUS N5051A", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x1993, "ASUS N20", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS G50V", ALC663_ASUS_G50V),
- /*SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS NB", ALC663_ASUS_MODE1),*/
- SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_ASUS_MODE2),
- SND_PCI_QUIRK(0x1043, 0x19d3, "ASUS NB", ALC663_ASUS_M51VA),
- SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC663_ASUS_MODE1),
- SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC663_ASUS_MODE4),
- SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701),
- SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20),
- SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS),
- SND_PCI_QUIRK(0x105b, 0x0d47, "Foxconn 45CMX/45GMX/45CMX-K",
- ALC662_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x1179, 0xff6e, "Toshiba NB20x", ALC662_AUTO),
- SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10),
- SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L",
- ALC662_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x152d, 0x2304, "Quanta WH1", ALC663_ASUS_H13),
- SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x1631, 0xc10c, "PB RS65", ALC663_ASUS_M51VA),
- SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
- SND_PCI_QUIRK(0x1849, 0x3662, "ASROCK K10N78FullHD-hSLI R3.0",
- ALC662_3ST_6ch_DIG),
- SND_PCI_QUIRK_MASK(0x1854, 0xf000, 0x2000, "ASUS H13-200x",
- ALC663_ASUS_H13),
- {}
-};
-
-static struct alc_config_preset alc662_presets[] = {
- [ALC662_3ST_2ch_DIG] = {
- .mixers = { alc662_3ST_2ch_mixer },
- .init_verbs = { alc662_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .dig_in_nid = ALC662_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc662_capture_source,
- },
- [ALC662_3ST_6ch_DIG] = {
- .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer },
- .init_verbs = { alc662_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .dig_in_nid = ALC662_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
- .channel_mode = alc662_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc662_capture_source,
- },
- [ALC662_3ST_6ch] = {
- .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer },
- .init_verbs = { alc662_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
- .channel_mode = alc662_3ST_6ch_modes,
- .need_dac_fix = 1,
- .input_mux = &alc662_capture_source,
- },
- [ALC662_5ST_DIG] = {
- .mixers = { alc662_base_mixer, alc662_chmode_mixer },
- .init_verbs = { alc662_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .dig_in_nid = ALC662_DIGIN_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_5stack_modes),
- .channel_mode = alc662_5stack_modes,
- .input_mux = &alc662_capture_source,
- },
- [ALC662_LENOVO_101E] = {
- .mixers = { alc662_lenovo_101e_mixer },
- .init_verbs = { alc662_init_verbs, alc662_sue_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .input_mux = &alc662_lenovo_101e_capture_source,
- .unsol_event = alc662_lenovo_101e_unsol_event,
- .init_hook = alc662_lenovo_101e_all_automute,
- },
- [ALC662_ASUS_EEEPC_P701] = {
- .mixers = { alc662_eeepc_p701_mixer },
- .init_verbs = { alc662_init_verbs,
- alc662_eeepc_sue_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc662_eeepc_unsol_event,
- .setup = alc662_eeepc_setup,
- .init_hook = alc662_eeepc_inithook,
- },
- [ALC662_ASUS_EEEPC_EP20] = {
- .mixers = { alc662_eeepc_ep20_mixer,
- alc662_chmode_mixer },
- .init_verbs = { alc662_init_verbs,
- alc662_eeepc_ep20_sue_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
- .channel_mode = alc662_3ST_6ch_modes,
- .input_mux = &alc662_lenovo_101e_capture_source,
- .unsol_event = alc662_eeepc_unsol_event,
- .setup = alc662_eeepc_ep20_setup,
- .init_hook = alc662_eeepc_ep20_inithook,
- },
- [ALC662_ECS] = {
- .mixers = { alc662_ecs_mixer },
- .init_verbs = { alc662_init_verbs,
- alc662_ecs_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc662_eeepc_unsol_event,
- .setup = alc662_eeepc_setup,
- .init_hook = alc662_eeepc_inithook,
- },
- [ALC663_ASUS_M51VA] = {
- .mixers = { alc663_m51va_mixer },
- .init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_m51va_unsol_event,
- .setup = alc663_m51va_setup,
- .init_hook = alc663_m51va_inithook,
- },
- [ALC663_ASUS_G71V] = {
- .mixers = { alc663_g71v_mixer },
- .init_verbs = { alc662_init_verbs, alc663_g71v_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_g71v_unsol_event,
- .setup = alc663_g71v_setup,
- .init_hook = alc663_g71v_inithook,
- },
- [ALC663_ASUS_H13] = {
- .mixers = { alc663_m51va_mixer },
- .init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_m51va_unsol_event,
- .init_hook = alc663_m51va_inithook,
- },
- [ALC663_ASUS_G50V] = {
- .mixers = { alc663_g50v_mixer },
- .init_verbs = { alc662_init_verbs, alc663_g50v_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
- .channel_mode = alc662_3ST_6ch_modes,
- .input_mux = &alc663_capture_source,
- .unsol_event = alc663_g50v_unsol_event,
- .setup = alc663_g50v_setup,
- .init_hook = alc663_g50v_inithook,
- },
- [ALC663_ASUS_MODE1] = {
- .mixers = { alc663_m51va_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs,
- alc663_21jd_amic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .hp_nid = 0x03,
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_mode1_unsol_event,
- .setup = alc663_mode1_setup,
- .init_hook = alc663_mode1_inithook,
- },
- [ALC662_ASUS_MODE2] = {
- .mixers = { alc662_1bjd_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs,
- alc662_1bjd_amic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc662_mode2_unsol_event,
- .setup = alc662_mode2_setup,
- .init_hook = alc662_mode2_inithook,
- },
- [ALC663_ASUS_MODE3] = {
- .mixers = { alc663_two_hp_m1_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs,
- alc663_two_hp_amic_m1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .hp_nid = 0x03,
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_mode3_unsol_event,
- .setup = alc663_mode3_setup,
- .init_hook = alc663_mode3_inithook,
- },
- [ALC663_ASUS_MODE4] = {
- .mixers = { alc663_asus_21jd_clfe_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs,
- alc663_21jd_amic_init_verbs},
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .hp_nid = 0x03,
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_mode4_unsol_event,
- .setup = alc663_mode4_setup,
- .init_hook = alc663_mode4_inithook,
- },
- [ALC663_ASUS_MODE5] = {
- .mixers = { alc663_asus_15jd_clfe_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs,
- alc663_15jd_amic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .hp_nid = 0x03,
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_mode5_unsol_event,
- .setup = alc663_mode5_setup,
- .init_hook = alc663_mode5_inithook,
- },
- [ALC663_ASUS_MODE6] = {
- .mixers = { alc663_two_hp_m2_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs,
- alc663_two_hp_amic_m2_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .hp_nid = 0x03,
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_mode6_unsol_event,
- .setup = alc663_mode6_setup,
- .init_hook = alc663_mode6_inithook,
- },
- [ALC663_ASUS_MODE7] = {
- .mixers = { alc663_mode7_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs,
- alc663_mode7_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .hp_nid = 0x03,
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_mode7_unsol_event,
- .setup = alc663_mode7_setup,
- .init_hook = alc663_mode7_inithook,
- },
- [ALC663_ASUS_MODE8] = {
- .mixers = { alc663_mode8_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs,
- alc663_mode8_init_verbs },
- .num_dacs = ARRAY_SIZE(alc662_dac_nids),
- .hp_nid = 0x03,
- .dac_nids = alc662_dac_nids,
- .dig_out_nid = ALC662_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_mode8_unsol_event,
- .setup = alc663_mode8_setup,
- .init_hook = alc663_mode8_inithook,
- },
- [ALC272_DELL] = {
- .mixers = { alc663_m51va_mixer },
- .cap_mixer = alc272_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs, alc272_dell_init_verbs },
- .num_dacs = ARRAY_SIZE(alc272_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .adc_nids = alc272_adc_nids,
- .num_adc_nids = ARRAY_SIZE(alc272_adc_nids),
- .capsrc_nids = alc272_capsrc_nids,
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_m51va_unsol_event,
- .setup = alc663_m51va_setup,
- .init_hook = alc663_m51va_inithook,
- },
- [ALC272_DELL_ZM1] = {
- .mixers = { alc663_m51va_mixer },
- .cap_mixer = alc662_auto_capture_mixer,
- .init_verbs = { alc662_init_verbs, alc272_dell_zm1_init_verbs },
- .num_dacs = ARRAY_SIZE(alc272_dac_nids),
- .dac_nids = alc662_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .adc_nids = alc662_adc_nids,
- .num_adc_nids = 1,
- .capsrc_nids = alc662_capsrc_nids,
- .channel_mode = alc662_3ST_2ch_modes,
- .unsol_event = alc663_m51va_unsol_event,
- .setup = alc663_m51va_setup,
- .init_hook = alc663_m51va_inithook,
- },
- [ALC272_SAMSUNG_NC10] = {
- .mixers = { alc272_nc10_mixer },
- .init_verbs = { alc662_init_verbs,
- alc663_21jd_amic_init_verbs },
- .num_dacs = ARRAY_SIZE(alc272_dac_nids),
- .dac_nids = alc272_dac_nids,
- .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
- .channel_mode = alc662_3ST_2ch_modes,
- /*.input_mux = &alc272_nc10_capture_source,*/
- .unsol_event = alc663_mode4_unsol_event,
- .setup = alc663_mode4_setup,
- .init_hook = alc663_mode4_inithook,
- },
-};
-
-
-/*
- * BIOS auto configuration
- */
-
-/* convert from MIX nid to DAC */
-static inline hda_nid_t alc662_mix_to_dac(hda_nid_t nid)
-{
- if (nid == 0x0f)
- return 0x02;
- else if (nid >= 0x0c && nid <= 0x0e)
- return nid - 0x0c + 0x02;
- else
- return 0;
-}
-
-/* get MIX nid connected to the given pin targeted to DAC */
-static hda_nid_t alc662_dac_to_mix(struct hda_codec *codec, hda_nid_t pin,
- hda_nid_t dac)
-{
- hda_nid_t mix[4];
- int i, num;
-
- num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix));
- for (i = 0; i < num; i++) {
- if (alc662_mix_to_dac(mix[i]) == dac)
- return mix[i];
- }
- return 0;
-}
-
-/* look for an empty DAC slot */
-static hda_nid_t alc662_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t srcs[5];
- int i, j, num;
-
- num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs));
- if (num < 0)
- return 0;
- for (i = 0; i < num; i++) {
- hda_nid_t nid = alc662_mix_to_dac(srcs[i]);
- if (!nid)
- continue;
- for (j = 0; j < spec->multiout.num_dacs; j++)
- if (spec->multiout.dac_nids[j] == nid)
- break;
- if (j >= spec->multiout.num_dacs)
- return nid;
- }
- return 0;
-}
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int alc662_auto_fill_dac_nids(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct alc_spec *spec = codec->spec;
- int i;
- hda_nid_t dac;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
- for (i = 0; i < cfg->line_outs; i++) {
- dac = alc662_look_for_dac(codec, cfg->line_out_pins[i]);
- if (!dac)
- continue;
- spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
- }
- return 0;
-}
-
-static inline int alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx,
- hda_nid_t nid, unsigned int chs)
-{
- return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
- HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
-}
-
-static inline int alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx,
- hda_nid_t nid, unsigned int chs)
-{
- return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT));
-}
-
-#define alc662_add_stereo_vol(spec, pfx, nid) \
- alc662_add_vol_ctl(spec, pfx, nid, 3)
-#define alc662_add_stereo_sw(spec, pfx, nid) \
- alc662_add_sw_ctl(spec, pfx, nid, 3)
-
-/* add playback controls from the parsed DAC table */
-static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct alc_spec *spec = codec->spec;
- static const char *chname[4] = {
- "Front", "Surround", NULL /*CLFE*/, "Side"
- };
- hda_nid_t nid, mix;
- int i, err;
-
- for (i = 0; i < cfg->line_outs; i++) {
- nid = spec->multiout.dac_nids[i];
- if (!nid)
- continue;
- mix = alc662_dac_to_mix(codec, cfg->line_out_pins[i], nid);
- if (!mix)
- continue;
- if (i == 2) {
- /* Center/LFE */
- err = alc662_add_vol_ctl(spec, "Center", nid, 1);
- if (err < 0)
- return err;
- err = alc662_add_vol_ctl(spec, "LFE", nid, 2);
- if (err < 0)
- return err;
- err = alc662_add_sw_ctl(spec, "Center", mix, 1);
- if (err < 0)
- return err;
- err = alc662_add_sw_ctl(spec, "LFE", mix, 2);
- if (err < 0)
- return err;
- } else {
- const char *pfx;
- if (cfg->line_outs == 1 &&
- cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
- if (cfg->hp_outs)
- pfx = "Speaker";
- else
- pfx = "PCM";
- } else
- pfx = chname[i];
- err = alc662_add_vol_ctl(spec, pfx, nid, 3);
- if (err < 0)
- return err;
- if (cfg->line_outs == 1 &&
- cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- pfx = "Speaker";
- err = alc662_add_sw_ctl(spec, pfx, mix, 3);
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-/* add playback controls for speaker and HP outputs */
-/* return DAC nid if any new DAC is assigned */
-static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
- const char *pfx)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t nid, mix;
- int err;
-
- if (!pin)
- return 0;
- nid = alc662_look_for_dac(codec, pin);
- if (!nid) {
- /* the corresponding DAC is already occupied */
- if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP))
- return 0; /* no way */
- /* create a switch only */
- return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- }
-
- mix = alc662_dac_to_mix(codec, pin, nid);
- if (!mix)
- return 0;
- err = alc662_add_vol_ctl(spec, pfx, nid, 3);
- if (err < 0)
- return err;
- err = alc662_add_sw_ctl(spec, pfx, mix, 3);
- if (err < 0)
- return err;
- return nid;
-}
-
-/* create playback/capture controls for input pins */
-#define alc662_auto_create_input_ctls \
- alc882_auto_create_input_ctls
-
-static void alc662_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- hda_nid_t dac)
-{
- int i, num;
- hda_nid_t srcs[HDA_MAX_CONNECTIONS];
-
- alc_set_pin_output(codec, nid, pin_type);
- /* need the manual connection? */
- num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs));
- if (num <= 1)
- return;
- for (i = 0; i < num; i++) {
- if (alc662_mix_to_dac(srcs[i]) != dac)
- continue;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i);
- return;
- }
-}
-
-static void alc662_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- int i;
-
- for (i = 0; i <= HDA_SIDE; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- if (nid)
- alc662_auto_set_output_and_unmute(codec, nid, pin_type,
- spec->multiout.dac_nids[i]);
- }
-}
-
-static void alc662_auto_init_hp_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t pin;
-
- pin = spec->autocfg.hp_pins[0];
- if (pin)
- alc662_auto_set_output_and_unmute(codec, pin, PIN_HP,
- spec->multiout.hp_nid);
- pin = spec->autocfg.speaker_pins[0];
- if (pin)
- alc662_auto_set_output_and_unmute(codec, pin, PIN_OUT,
- spec->multiout.extra_out_nid[0]);
-}
-
-#define ALC662_PIN_CD_NID ALC880_PIN_CD_NID
-
-static void alc662_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (alc_is_input_pin(codec, nid)) {
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (nid != ALC662_PIN_CD_NID &&
- (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
- }
-}
-
-#define alc662_auto_init_input_src alc882_auto_init_input_src
-
-static int alc662_parse_auto_config(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc662_ignore[] = { 0x1d, 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc662_ignore);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs)
- return 0; /* can't find valid BIOS pin config */
-
- err = alc662_auto_fill_dac_nids(codec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc662_auto_create_multi_out_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
- err = alc662_auto_create_extra_out(codec,
- spec->autocfg.speaker_pins[0],
- "Speaker");
- if (err < 0)
- return err;
- if (err)
- spec->multiout.extra_out_nid[0] = err;
- err = alc662_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
- "Headphone");
- if (err < 0)
- return err;
- if (err)
- spec->multiout.hp_nid = err;
- err = alc662_auto_create_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- alc_auto_parse_digital(codec);
-
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- spec->num_mux_defs = 1;
- spec->input_mux = &spec->private_imux[0];
-
- add_verb(spec, alc662_init_verbs);
- if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 ||
- codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670)
- add_verb(spec, alc663_init_verbs);
-
- if (codec->vendor_id == 0x10ec0272)
- add_verb(spec, alc272_init_verbs);
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
-
- if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 ||
- codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670)
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0x21);
- else
- alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
-
- return 1;
-}
-
-/* additional initialization for auto-configuration model */
-static void alc662_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc662_auto_init_multi_out(codec);
- alc662_auto_init_hp_out(codec);
- alc662_auto_init_analog_input(codec);
- alc662_auto_init_input_src(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-enum {
- ALC662_FIXUP_ASPIRE,
- ALC662_FIXUP_IDEAPAD,
-};
-
-static const struct alc_fixup alc662_fixups[] = {
- [ALC662_FIXUP_ASPIRE] = {
- .pins = (const struct alc_pincfg[]) {
- { 0x15, 0x99130112 }, /* subwoofer */
- { }
- }
- },
- [ALC662_FIXUP_IDEAPAD] = {
- .pins = (const struct alc_pincfg[]) {
- { 0x17, 0x99130112 }, /* subwoofer */
- { }
- }
- },
-};
-
-static struct snd_pci_quirk alc662_fixup_tbl[] = {
- SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
- SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD),
- SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD),
- {}
-};
-
-
-
-static int patch_alc662(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int err, board_config;
- int coef;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return -ENOMEM;
-
- codec->spec = spec;
-
- alc_auto_parse_customize_define(codec);
-
- alc_fix_pll_init(codec, 0x20, 0x04, 15);
-
- coef = alc_read_coef_idx(codec, 0);
- if (coef == 0x8020 || coef == 0x8011)
- alc_codec_rename(codec, "ALC661");
- else if (coef & (1 << 14) &&
- codec->bus->pci->subsystem_vendor == 0x1025 &&
- spec->cdefine.platform_type == 1)
- alc_codec_rename(codec, "ALC272X");
- else if (coef == 0x4011)
- alc_codec_rename(codec, "ALC656");
-
- board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST,
- alc662_models,
- alc662_cfg_tbl);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC662_AUTO;
- }
-
- if (board_config == ALC662_AUTO) {
- alc_pick_fixup(codec, alc662_fixup_tbl, alc662_fixups, 1);
- /* automatic parse from the BIOS config */
- err = alc662_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC662_3ST_2ch_DIG;
- }
- }
-
- if (has_cdefine_beep(codec)) {
- err = snd_hda_attach_beep_device(codec, 0x1);
- if (err < 0) {
- alc_free(codec);
- return err;
- }
- }
-
- if (board_config != ALC662_AUTO)
- setup_preset(codec, &alc662_presets[board_config]);
-
- spec->stream_analog_playback = &alc662_pcm_analog_playback;
- spec->stream_analog_capture = &alc662_pcm_analog_capture;
-
- spec->stream_digital_playback = &alc662_pcm_digital_playback;
- spec->stream_digital_capture = &alc662_pcm_digital_capture;
-
- if (!spec->adc_nids) {
- spec->adc_nids = alc662_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
- }
- if (!spec->capsrc_nids)
- spec->capsrc_nids = alc662_capsrc_nids;
-
- if (!spec->cap_mixer)
- set_capture_mixer(codec);
-
- if (has_cdefine_beep(codec)) {
- switch (codec->vendor_id) {
- case 0x10ec0662:
- set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
- break;
- case 0x10ec0272:
- case 0x10ec0663:
- case 0x10ec0665:
- set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
- break;
- case 0x10ec0273:
- set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT);
- break;
- }
- }
- spec->vmaster_nid = 0x02;
-
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC662_AUTO) {
- spec->init_hook = alc662_auto_init;
- alc_pick_fixup(codec, alc662_fixup_tbl, alc662_fixups, 0);
- }
-
- alc_init_jacks(codec);
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (!spec->loopback.amplist)
- spec->loopback.amplist = alc662_loopbacks;
-#endif
-
- return 0;
-}
-
-static int patch_alc888(struct hda_codec *codec)
-{
- if ((alc_read_coef_idx(codec, 0) & 0x00f0)==0x0030){
- kfree(codec->chip_name);
- codec->chip_name = kstrdup("ALC888-VD", GFP_KERNEL);
- if (!codec->chip_name) {
- alc_free(codec);
- return -ENOMEM;
- }
- return patch_alc662(codec);
- }
- return patch_alc882(codec);
-}
-
-/*
- * ALC680 support
- */
-#define ALC680_DIGIN_NID ALC880_DIGIN_NID
-#define ALC680_DIGOUT_NID ALC880_DIGOUT_NID
-#define alc680_modes alc260_modes
-
-static hda_nid_t alc680_dac_nids[3] = {
- /* Lout1, Lout2, hp */
- 0x02, 0x03, 0x04
-};
-
-static hda_nid_t alc680_adc_nids[3] = {
- /* ADC0-2 */
- /* DMIC, MIC, Line-in*/
- 0x07, 0x08, 0x09
-};
-
-/*
- * Analog capture ADC cgange
- */
-static void alc680_rec_autoswitch(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int pin_found = 0;
- int type_found = AUTO_PIN_LAST;
- hda_nid_t nid;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- nid = cfg->inputs[i].pin;
- if (!(snd_hda_query_pin_caps(codec, nid) &
- AC_PINCAP_PRES_DETECT))
- continue;
- if (snd_hda_jack_detect(codec, nid)) {
- if (cfg->inputs[i].type < type_found) {
- type_found = cfg->inputs[i].type;
- pin_found = nid;
- }
- }
- }
-
- nid = 0x07;
- if (pin_found)
- snd_hda_get_connections(codec, pin_found, &nid, 1);
-
- if (nid != spec->cur_adc)
- __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
- spec->cur_adc = nid;
- snd_hda_codec_setup_stream(codec, nid, spec->cur_adc_stream_tag, 0,
- spec->cur_adc_format);
-}
-
-static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->cur_adc = 0x07;
- spec->cur_adc_stream_tag = stream_tag;
- spec->cur_adc_format = format;
-
- alc680_rec_autoswitch(codec);
- return 0;
-}
-
-static int alc680_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- snd_hda_codec_cleanup_stream(codec, 0x07);
- snd_hda_codec_cleanup_stream(codec, 0x08);
- snd_hda_codec_cleanup_stream(codec, 0x09);
- return 0;
-}
-
-static struct hda_pcm_stream alc680_pcm_analog_auto_capture = {
- .substreams = 1, /* can be overridden */
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in alc_build_pcms */
- .ops = {
- .prepare = alc680_capture_pcm_prepare,
- .cleanup = alc680_capture_pcm_cleanup
- },
-};
-
-static struct snd_kcontrol_new alc680_base_mixer[] = {
- /* output mixer control */
- HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Headphone Playback Volume", 0x4, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Headphone Playback Switch", 0x16, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Int Mic Boost", 0x12, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Boost", 0x19, 0, HDA_INPUT),
- { }
-};
-
-static struct hda_bind_ctls alc680_bind_cap_vol = {
- .ops = &snd_hda_bind_vol,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
- 0
- },
-};
-
-static struct hda_bind_ctls alc680_bind_cap_switch = {
- .ops = &snd_hda_bind_sw,
- .values = {
- HDA_COMPOSE_AMP_VAL(0x07, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
- 0
- },
-};
-
-static struct snd_kcontrol_new alc680_master_capture_mixer[] = {
- HDA_BIND_VOL("Capture Volume", &alc680_bind_cap_vol),
- HDA_BIND_SW("Capture Switch", &alc680_bind_cap_switch),
- { } /* end */
-};
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc680_init_verbs[] = {
- {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
- {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
- {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
- {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
- {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
-
- { }
-};
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc680_base_setup(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
-
- spec->autocfg.hp_pins[0] = 0x16;
- spec->autocfg.speaker_pins[0] = 0x14;
- spec->autocfg.speaker_pins[1] = 0x15;
- spec->autocfg.num_inputs = 2;
- spec->autocfg.inputs[0].pin = 0x18;
- spec->autocfg.inputs[0].type = AUTO_PIN_MIC;
- spec->autocfg.inputs[1].pin = 0x19;
- spec->autocfg.inputs[1].type = AUTO_PIN_LINE_IN;
-}
-
-static void alc680_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- if ((res >> 26) == ALC880_HP_EVENT)
- alc_automute_amp(codec);
- if ((res >> 26) == ALC880_MIC_EVENT)
- alc680_rec_autoswitch(codec);
-}
-
-static void alc680_inithook(struct hda_codec *codec)
-{
- alc_automute_amp(codec);
- alc680_rec_autoswitch(codec);
-}
-
-/* create input playback/capture controls for the given pin */
-static int alc680_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
- const char *ctlname, int idx)
-{
- hda_nid_t dac;
- int err;
-
- switch (nid) {
- case 0x14:
- dac = 0x02;
- break;
- case 0x15:
- dac = 0x03;
- break;
- case 0x16:
- dac = 0x04;
- break;
- default:
- return 0;
- }
- if (spec->multiout.dac_nids[0] != dac &&
- spec->multiout.dac_nids[1] != dac) {
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
- HDA_COMPOSE_AMP_VAL(dac, 3, idx,
- HDA_OUTPUT));
- if (err < 0)
- return err;
-
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
- HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
-
- if (err < 0)
- return err;
- spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int alc680_auto_create_multi_out_ctls(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- hda_nid_t nid;
- int err;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- nid = cfg->line_out_pins[0];
- if (nid) {
- const char *name;
- if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- name = "Speaker";
- else
- name = "Front";
- err = alc680_new_analog_output(spec, nid, name, 0);
- if (err < 0)
- return err;
- }
-
- nid = cfg->speaker_pins[0];
- if (nid) {
- err = alc680_new_analog_output(spec, nid, "Speaker", 0);
- if (err < 0)
- return err;
- }
- nid = cfg->hp_pins[0];
- if (nid) {
- err = alc680_new_analog_output(spec, nid, "Headphone", 0);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static void alc680_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type)
-{
- alc_set_pin_output(codec, nid, pin_type);
-}
-
-static void alc680_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t nid = spec->autocfg.line_out_pins[0];
- if (nid) {
- int pin_type = get_pin_type(spec->autocfg.line_out_type);
- alc680_auto_set_output_and_unmute(codec, nid, pin_type);
- }
-}
-
-static void alc680_auto_init_hp_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t pin;
-
- pin = spec->autocfg.hp_pins[0];
- if (pin)
- alc680_auto_set_output_and_unmute(codec, pin, PIN_HP);
- pin = spec->autocfg.speaker_pins[0];
- if (pin)
- alc680_auto_set_output_and_unmute(codec, pin, PIN_OUT);
-}
-
-/* pcm configuration: identical with ALC880 */
-#define alc680_pcm_analog_playback alc880_pcm_analog_playback
-#define alc680_pcm_analog_capture alc880_pcm_analog_capture
-#define alc680_pcm_analog_alt_capture alc880_pcm_analog_alt_capture
-#define alc680_pcm_digital_playback alc880_pcm_digital_playback
-#define alc680_pcm_digital_capture alc880_pcm_digital_capture
-
-/*
- * BIOS auto configuration
- */
-static int alc680_parse_auto_config(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int err;
- static hda_nid_t alc680_ignore[] = { 0 };
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
- alc680_ignore);
- if (err < 0)
- return err;
-
- if (!spec->autocfg.line_outs) {
- if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) {
- spec->multiout.max_channels = 2;
- spec->no_analog = 1;
- goto dig_only;
- }
- return 0; /* can't find valid BIOS pin config */
- }
- err = alc680_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = 2;
-
- dig_only:
- /* digital only support output */
- alc_auto_parse_digital(codec);
- if (spec->kctls.list)
- add_mixer(spec, spec->kctls.list);
-
- add_verb(spec, alc680_init_verbs);
-
- err = alc_auto_add_mic_boost(codec);
- if (err < 0)
- return err;
-
- return 1;
-}
-
-#define alc680_auto_init_analog_input alc882_auto_init_analog_input
-
-/* init callback for auto-configuration model -- overriding the default init */
-static void alc680_auto_init(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- alc680_auto_init_multi_out(codec);
- alc680_auto_init_hp_out(codec);
- alc680_auto_init_analog_input(codec);
- alc_auto_init_digital(codec);
- if (spec->unsol_event)
- alc_inithook(codec);
-}
-
-/*
- * configuration and preset
- */
-static const char *alc680_models[ALC680_MODEL_LAST] = {
- [ALC680_BASE] = "base",
- [ALC680_AUTO] = "auto",
-};
-
-static struct snd_pci_quirk alc680_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1043, 0x12f3, "ASUS NX90", ALC680_BASE),
- {}
-};
-
-static struct alc_config_preset alc680_presets[] = {
- [ALC680_BASE] = {
- .mixers = { alc680_base_mixer },
- .cap_mixer = alc680_master_capture_mixer,
- .init_verbs = { alc680_init_verbs },
- .num_dacs = ARRAY_SIZE(alc680_dac_nids),
- .dac_nids = alc680_dac_nids,
- .dig_out_nid = ALC680_DIGOUT_NID,
- .num_channel_mode = ARRAY_SIZE(alc680_modes),
- .channel_mode = alc680_modes,
- .unsol_event = alc680_unsol_event,
- .setup = alc680_base_setup,
- .init_hook = alc680_inithook,
-
- },
-};
-
-static int patch_alc680(struct hda_codec *codec)
-{
- struct alc_spec *spec;
- int board_config;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- board_config = snd_hda_check_board_config(codec, ALC680_MODEL_LAST,
- alc680_models,
- alc680_cfg_tbl);
-
- if (board_config < 0 || board_config >= ALC680_MODEL_LAST) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = ALC680_AUTO;
- }
-
- if (board_config == ALC680_AUTO) {
- /* automatic parse from the BIOS config */
- err = alc680_parse_auto_config(codec);
- if (err < 0) {
- alc_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO
- "hda_codec: Cannot set up configuration "
- "from BIOS. Using base mode...\n");
- board_config = ALC680_BASE;
- }
- }
-
- if (board_config != ALC680_AUTO)
- setup_preset(codec, &alc680_presets[board_config]);
-
- spec->stream_analog_playback = &alc680_pcm_analog_playback;
- spec->stream_analog_capture = &alc680_pcm_analog_auto_capture;
- spec->stream_digital_playback = &alc680_pcm_digital_playback;
- spec->stream_digital_capture = &alc680_pcm_digital_capture;
-
- if (!spec->adc_nids) {
- spec->adc_nids = alc680_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(alc680_adc_nids);
- }
-
- if (!spec->cap_mixer)
- set_capture_mixer(codec);
-
- spec->vmaster_nid = 0x02;
-
- codec->patch_ops = alc_patch_ops;
- if (board_config == ALC680_AUTO)
- spec->init_hook = alc680_auto_init;
-
- return 0;
-}
-
-/*
- * patch entries
- */
-static struct hda_codec_preset snd_hda_preset_realtek[] = {
- { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
- { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
- { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
- { .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 },
- { .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 },
- { .id = 0x10ec0270, .name = "ALC270", .patch = patch_alc269 },
- { .id = 0x10ec0272, .name = "ALC272", .patch = patch_alc662 },
- { .id = 0x10ec0275, .name = "ALC275", .patch = patch_alc269 },
- { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
- .patch = patch_alc861 },
- { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
- { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 },
- { .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd },
- { .id = 0x10ec0662, .rev = 0x100002, .name = "ALC662 rev2",
- .patch = patch_alc882 },
- { .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1",
- .patch = patch_alc662 },
- { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
- { .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 },
- { .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 },
- { .id = 0x10ec0680, .name = "ALC680", .patch = patch_alc680 },
- { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
- { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
- { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
- { .id = 0x10ec0885, .rev = 0x100101, .name = "ALC889A",
- .patch = patch_alc882 },
- { .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A",
- .patch = patch_alc882 },
- { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
- { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc882 },
- { .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200",
- .patch = patch_alc882 },
- { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc888 },
- { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 },
- { .id = 0x10ec0892, .name = "ALC892", .patch = patch_alc662 },
- {} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:10ec*");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Realtek HD-audio codec");
-
-static struct hda_codec_preset_list realtek_list = {
- .preset = snd_hda_preset_realtek,
- .owner = THIS_MODULE,
-};
-
-static int __init patch_realtek_init(void)
-{
- return snd_hda_add_codec_preset(&realtek_list);
-}
-
-static void __exit patch_realtek_exit(void)
-{
- snd_hda_delete_codec_preset(&realtek_list);
-}
-
-module_init(patch_realtek_init)
-module_exit(patch_realtek_exit)
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
deleted file mode 100644
index 93fa59cc60ef..000000000000
--- a/sound/pci/hda/patch_sigmatel.c
+++ /dev/null
@@ -1,6431 +0,0 @@
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * HD audio interface patch for SigmaTel STAC92xx
- *
- * Copyright (c) 2005 Embedded Alley Solutions, Inc.
- * Matt Porter <mporter@embeddedalley.com>
- *
- * Based on patch_cmedia.c and patch_realtek.c
- * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
-#include <linux/dmi.h>
-#include <sound/core.h>
-#include <sound/asoundef.h>
-#include <sound/jack.h>
-#include <sound/tlv.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-#include "hda_beep.h"
-
-enum {
- STAC_VREF_EVENT = 1,
- STAC_INSERT_EVENT,
- STAC_PWR_EVENT,
- STAC_HP_EVENT,
- STAC_LO_EVENT,
- STAC_MIC_EVENT,
-};
-
-enum {
- STAC_AUTO,
- STAC_REF,
- STAC_9200_OQO,
- STAC_9200_DELL_D21,
- STAC_9200_DELL_D22,
- STAC_9200_DELL_D23,
- STAC_9200_DELL_M21,
- STAC_9200_DELL_M22,
- STAC_9200_DELL_M23,
- STAC_9200_DELL_M24,
- STAC_9200_DELL_M25,
- STAC_9200_DELL_M26,
- STAC_9200_DELL_M27,
- STAC_9200_M4,
- STAC_9200_M4_2,
- STAC_9200_PANASONIC,
- STAC_9200_MODELS
-};
-
-enum {
- STAC_9205_AUTO,
- STAC_9205_REF,
- STAC_9205_DELL_M42,
- STAC_9205_DELL_M43,
- STAC_9205_DELL_M44,
- STAC_9205_EAPD,
- STAC_9205_MODELS
-};
-
-enum {
- STAC_92HD73XX_AUTO,
- STAC_92HD73XX_NO_JD, /* no jack-detection */
- STAC_92HD73XX_REF,
- STAC_92HD73XX_INTEL,
- STAC_DELL_M6_AMIC,
- STAC_DELL_M6_DMIC,
- STAC_DELL_M6_BOTH,
- STAC_DELL_EQ,
- STAC_ALIENWARE_M17X,
- STAC_92HD73XX_MODELS
-};
-
-enum {
- STAC_92HD83XXX_AUTO,
- STAC_92HD83XXX_REF,
- STAC_92HD83XXX_PWR_REF,
- STAC_DELL_S14,
- STAC_92HD83XXX_HP,
- STAC_HP_DV7_4000,
- STAC_92HD83XXX_MODELS
-};
-
-enum {
- STAC_92HD71BXX_AUTO,
- STAC_92HD71BXX_REF,
- STAC_DELL_M4_1,
- STAC_DELL_M4_2,
- STAC_DELL_M4_3,
- STAC_HP_M4,
- STAC_HP_DV4,
- STAC_HP_DV5,
- STAC_HP_HDX,
- STAC_HP_DV4_1222NR,
- STAC_92HD71BXX_MODELS
-};
-
-enum {
- STAC_925x_AUTO,
- STAC_925x_REF,
- STAC_M1,
- STAC_M1_2,
- STAC_M2,
- STAC_M2_2,
- STAC_M3,
- STAC_M5,
- STAC_M6,
- STAC_925x_MODELS
-};
-
-enum {
- STAC_922X_AUTO,
- STAC_D945_REF,
- STAC_D945GTP3,
- STAC_D945GTP5,
- STAC_INTEL_MAC_V1,
- STAC_INTEL_MAC_V2,
- STAC_INTEL_MAC_V3,
- STAC_INTEL_MAC_V4,
- STAC_INTEL_MAC_V5,
- STAC_INTEL_MAC_AUTO, /* This model is selected if no module parameter
- * is given, one of the above models will be
- * chosen according to the subsystem id. */
- /* for backward compatibility */
- STAC_MACMINI,
- STAC_MACBOOK,
- STAC_MACBOOK_PRO_V1,
- STAC_MACBOOK_PRO_V2,
- STAC_IMAC_INTEL,
- STAC_IMAC_INTEL_20,
- STAC_ECS_202,
- STAC_922X_DELL_D81,
- STAC_922X_DELL_D82,
- STAC_922X_DELL_M81,
- STAC_922X_DELL_M82,
- STAC_922X_MODELS
-};
-
-enum {
- STAC_927X_AUTO,
- STAC_D965_REF_NO_JD, /* no jack-detection */
- STAC_D965_REF,
- STAC_D965_3ST,
- STAC_D965_5ST,
- STAC_D965_5ST_NO_FP,
- STAC_DELL_3ST,
- STAC_DELL_BIOS,
- STAC_927X_VOLKNOB,
- STAC_927X_MODELS
-};
-
-enum {
- STAC_9872_AUTO,
- STAC_9872_VAIO,
- STAC_9872_MODELS
-};
-
-struct sigmatel_event {
- hda_nid_t nid;
- unsigned char type;
- unsigned char tag;
- int data;
-};
-
-struct sigmatel_jack {
- hda_nid_t nid;
- int type;
- struct snd_jack *jack;
-};
-
-struct sigmatel_mic_route {
- hda_nid_t pin;
- signed char mux_idx;
- signed char dmux_idx;
-};
-
-struct sigmatel_spec {
- struct snd_kcontrol_new *mixers[4];
- unsigned int num_mixers;
-
- int board_config;
- unsigned int eapd_switch: 1;
- unsigned int surr_switch: 1;
- unsigned int alt_switch: 1;
- unsigned int hp_detect: 1;
- unsigned int spdif_mute: 1;
- unsigned int check_volume_offset:1;
- unsigned int auto_mic:1;
- unsigned int linear_tone_beep:1;
-
- /* gpio lines */
- unsigned int eapd_mask;
- unsigned int gpio_mask;
- unsigned int gpio_dir;
- unsigned int gpio_data;
- unsigned int gpio_mute;
- unsigned int gpio_led;
- unsigned int gpio_led_polarity;
-
- /* stream */
- unsigned int stream_delay;
-
- /* analog loopback */
- struct snd_kcontrol_new *aloopback_ctl;
- unsigned char aloopback_mask;
- unsigned char aloopback_shift;
-
- /* power management */
- unsigned int num_pwrs;
- unsigned int *pwr_mapping;
- hda_nid_t *pwr_nids;
- hda_nid_t *dac_list;
-
- /* jack detection */
- struct snd_array jacks;
-
- /* events */
- struct snd_array events;
-
- /* playback */
- struct hda_input_mux *mono_mux;
- unsigned int cur_mmux;
- struct hda_multi_out multiout;
- hda_nid_t dac_nids[5];
- hda_nid_t hp_dacs[5];
- hda_nid_t speaker_dacs[5];
-
- int volume_offset;
-
- /* capture */
- hda_nid_t *adc_nids;
- unsigned int num_adcs;
- hda_nid_t *mux_nids;
- unsigned int num_muxes;
- hda_nid_t *dmic_nids;
- unsigned int num_dmics;
- hda_nid_t *dmux_nids;
- unsigned int num_dmuxes;
- hda_nid_t *smux_nids;
- unsigned int num_smuxes;
- unsigned int num_analog_muxes;
-
- unsigned long *capvols; /* amp-volume attr: HDA_COMPOSE_AMP_VAL() */
- unsigned long *capsws; /* amp-mute attr: HDA_COMPOSE_AMP_VAL() */
- unsigned int num_caps; /* number of capture volume/switch elements */
-
- struct sigmatel_mic_route ext_mic;
- struct sigmatel_mic_route int_mic;
- struct sigmatel_mic_route dock_mic;
-
- const char **spdif_labels;
-
- hda_nid_t dig_in_nid;
- hda_nid_t mono_nid;
- hda_nid_t anabeep_nid;
- hda_nid_t digbeep_nid;
-
- /* pin widgets */
- hda_nid_t *pin_nids;
- unsigned int num_pins;
-
- /* codec specific stuff */
- struct hda_verb *init;
- struct snd_kcontrol_new *mixer;
-
- /* capture source */
- struct hda_input_mux *dinput_mux;
- unsigned int cur_dmux[2];
- struct hda_input_mux *input_mux;
- unsigned int cur_mux[3];
- struct hda_input_mux *sinput_mux;
- unsigned int cur_smux[2];
- unsigned int cur_amux;
- hda_nid_t *amp_nids;
- unsigned int powerdown_adcs;
-
- /* i/o switches */
- unsigned int io_switch[2];
- unsigned int clfe_swap;
- hda_nid_t line_switch; /* shared line-in for input and output */
- hda_nid_t mic_switch; /* shared mic-in for input and output */
- hda_nid_t hp_switch; /* NID of HP as line-out */
- unsigned int aloopback;
-
- struct hda_pcm pcm_rec[2]; /* PCM information */
-
- /* dynamic controls and input_mux */
- struct auto_pin_cfg autocfg;
- struct snd_array kctls;
- struct hda_input_mux private_dimux;
- struct hda_input_mux private_imux;
- struct hda_input_mux private_smux;
- struct hda_input_mux private_mono_mux;
-};
-
-static hda_nid_t stac9200_adc_nids[1] = {
- 0x03,
-};
-
-static hda_nid_t stac9200_mux_nids[1] = {
- 0x0c,
-};
-
-static hda_nid_t stac9200_dac_nids[1] = {
- 0x02,
-};
-
-static hda_nid_t stac92hd73xx_pwr_nids[8] = {
- 0x0a, 0x0b, 0x0c, 0xd, 0x0e,
- 0x0f, 0x10, 0x11
-};
-
-static hda_nid_t stac92hd73xx_slave_dig_outs[2] = {
- 0x26, 0,
-};
-
-static hda_nid_t stac92hd73xx_adc_nids[2] = {
- 0x1a, 0x1b
-};
-
-#define STAC92HD73XX_NUM_DMICS 2
-static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
- 0x13, 0x14, 0
-};
-
-#define STAC92HD73_DAC_COUNT 5
-
-static hda_nid_t stac92hd73xx_mux_nids[2] = {
- 0x20, 0x21,
-};
-
-static hda_nid_t stac92hd73xx_dmux_nids[2] = {
- 0x20, 0x21,
-};
-
-static hda_nid_t stac92hd73xx_smux_nids[2] = {
- 0x22, 0x23,
-};
-
-#define STAC92HD73XX_NUM_CAPS 2
-static unsigned long stac92hd73xx_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x20, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
-};
-#define stac92hd73xx_capsws stac92hd73xx_capvols
-
-#define STAC92HD83_DAC_COUNT 3
-
-static hda_nid_t stac92hd83xxx_mux_nids[2] = {
- 0x17, 0x18,
-};
-
-static hda_nid_t stac92hd83xxx_adc_nids[2] = {
- 0x15, 0x16,
-};
-
-static hda_nid_t stac92hd83xxx_pwr_nids[4] = {
- 0xa, 0xb, 0xd, 0xe,
-};
-
-static hda_nid_t stac92hd83xxx_slave_dig_outs[2] = {
- 0x1e, 0,
-};
-
-static unsigned int stac92hd83xxx_pwr_mapping[4] = {
- 0x03, 0x0c, 0x20, 0x40,
-};
-
-#define STAC92HD83XXX_NUM_DMICS 2
-static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = {
- 0x11, 0x20, 0
-};
-
-#define STAC92HD83XXX_NUM_CAPS 2
-static unsigned long stac92hd83xxx_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_OUTPUT),
-};
-#define stac92hd83xxx_capsws stac92hd83xxx_capvols
-
-static hda_nid_t stac92hd71bxx_pwr_nids[3] = {
- 0x0a, 0x0d, 0x0f
-};
-
-static hda_nid_t stac92hd71bxx_adc_nids[2] = {
- 0x12, 0x13,
-};
-
-static hda_nid_t stac92hd71bxx_mux_nids[2] = {
- 0x1a, 0x1b
-};
-
-static hda_nid_t stac92hd71bxx_dmux_nids[2] = {
- 0x1c, 0x1d,
-};
-
-static hda_nid_t stac92hd71bxx_smux_nids[2] = {
- 0x24, 0x25,
-};
-
-#define STAC92HD71BXX_NUM_DMICS 2
-static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = {
- 0x18, 0x19, 0
-};
-
-static hda_nid_t stac92hd71bxx_slave_dig_outs[2] = {
- 0x22, 0
-};
-
-#define STAC92HD71BXX_NUM_CAPS 2
-static unsigned long stac92hd71bxx_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
-};
-#define stac92hd71bxx_capsws stac92hd71bxx_capvols
-
-static hda_nid_t stac925x_adc_nids[1] = {
- 0x03,
-};
-
-static hda_nid_t stac925x_mux_nids[1] = {
- 0x0f,
-};
-
-static hda_nid_t stac925x_dac_nids[1] = {
- 0x02,
-};
-
-#define STAC925X_NUM_DMICS 1
-static hda_nid_t stac925x_dmic_nids[STAC925X_NUM_DMICS + 1] = {
- 0x15, 0
-};
-
-static hda_nid_t stac925x_dmux_nids[1] = {
- 0x14,
-};
-
-static unsigned long stac925x_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT),
-};
-static unsigned long stac925x_capsws[] = {
- HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
-};
-
-static hda_nid_t stac922x_adc_nids[2] = {
- 0x06, 0x07,
-};
-
-static hda_nid_t stac922x_mux_nids[2] = {
- 0x12, 0x13,
-};
-
-#define STAC922X_NUM_CAPS 2
-static unsigned long stac922x_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT),
-};
-#define stac922x_capsws stac922x_capvols
-
-static hda_nid_t stac927x_slave_dig_outs[2] = {
- 0x1f, 0,
-};
-
-static hda_nid_t stac927x_adc_nids[3] = {
- 0x07, 0x08, 0x09
-};
-
-static hda_nid_t stac927x_mux_nids[3] = {
- 0x15, 0x16, 0x17
-};
-
-static hda_nid_t stac927x_smux_nids[1] = {
- 0x21,
-};
-
-static hda_nid_t stac927x_dac_nids[6] = {
- 0x02, 0x03, 0x04, 0x05, 0x06, 0
-};
-
-static hda_nid_t stac927x_dmux_nids[1] = {
- 0x1b,
-};
-
-#define STAC927X_NUM_DMICS 2
-static hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = {
- 0x13, 0x14, 0
-};
-
-#define STAC927X_NUM_CAPS 3
-static unsigned long stac927x_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x19, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_INPUT),
-};
-static unsigned long stac927x_capsws[] = {
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
-};
-
-static const char *stac927x_spdif_labels[5] = {
- "Digital Playback", "ADAT", "Analog Mux 1",
- "Analog Mux 2", "Analog Mux 3"
-};
-
-static hda_nid_t stac9205_adc_nids[2] = {
- 0x12, 0x13
-};
-
-static hda_nid_t stac9205_mux_nids[2] = {
- 0x19, 0x1a
-};
-
-static hda_nid_t stac9205_dmux_nids[1] = {
- 0x1d,
-};
-
-static hda_nid_t stac9205_smux_nids[1] = {
- 0x21,
-};
-
-#define STAC9205_NUM_DMICS 2
-static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = {
- 0x17, 0x18, 0
-};
-
-#define STAC9205_NUM_CAPS 2
-static unsigned long stac9205_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_INPUT),
- HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_INPUT),
-};
-static unsigned long stac9205_capsws[] = {
- HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
- HDA_COMPOSE_AMP_VAL(0x1e, 3, 0, HDA_OUTPUT),
-};
-
-static hda_nid_t stac9200_pin_nids[8] = {
- 0x08, 0x09, 0x0d, 0x0e,
- 0x0f, 0x10, 0x11, 0x12,
-};
-
-static hda_nid_t stac925x_pin_nids[8] = {
- 0x07, 0x08, 0x0a, 0x0b,
- 0x0c, 0x0d, 0x10, 0x11,
-};
-
-static hda_nid_t stac922x_pin_nids[10] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x10, 0x11, 0x15, 0x1b,
-};
-
-static hda_nid_t stac92hd73xx_pin_nids[13] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x10, 0x11, 0x12, 0x13,
- 0x14, 0x22, 0x23
-};
-
-static hda_nid_t stac92hd83xxx_pin_nids[10] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x10, 0x11, 0x1f, 0x20,
-};
-
-static hda_nid_t stac92hd88xxx_pin_nids[10] = {
- 0x0a, 0x0b, 0x0c, 0x0d,
- 0x0f, 0x11, 0x1f, 0x20,
-};
-
-#define STAC92HD71BXX_NUM_PINS 13
-static hda_nid_t stac92hd71bxx_pin_nids_4port[STAC92HD71BXX_NUM_PINS] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x00,
- 0x00, 0x14, 0x18, 0x19, 0x1e,
- 0x1f, 0x20, 0x27
-};
-static hda_nid_t stac92hd71bxx_pin_nids_6port[STAC92HD71BXX_NUM_PINS] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x14, 0x18, 0x19, 0x1e,
- 0x1f, 0x20, 0x27
-};
-
-static hda_nid_t stac927x_pin_nids[14] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x10, 0x11, 0x12, 0x13,
- 0x14, 0x21, 0x22, 0x23,
-};
-
-static hda_nid_t stac9205_pin_nids[12] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x14, 0x16, 0x17, 0x18,
- 0x21, 0x22,
-};
-
-static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->dinput_mux, uinfo);
-}
-
-static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- ucontrol->value.enumerated.item[0] = spec->cur_dmux[dmux_idx];
- return 0;
-}
-
-static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol,
- spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]);
-}
-
-static int stac92xx_smux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->sinput_mux, uinfo);
-}
-
-static int stac92xx_smux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- ucontrol->value.enumerated.item[0] = spec->cur_smux[smux_idx];
- return 0;
-}
-
-static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- struct hda_input_mux *smux = &spec->private_smux;
- unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- int err, val;
- hda_nid_t nid;
-
- err = snd_hda_input_mux_put(codec, spec->sinput_mux, ucontrol,
- spec->smux_nids[smux_idx], &spec->cur_smux[smux_idx]);
- if (err < 0)
- return err;
-
- if (spec->spdif_mute) {
- if (smux_idx == 0)
- nid = spec->multiout.dig_out_nid;
- else
- nid = codec->slave_dig_outs[smux_idx - 1];
- if (spec->cur_smux[smux_idx] == smux->num_items - 1)
- val = HDA_AMP_MUTE;
- else
- val = 0;
- /* un/mute SPDIF out */
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, val);
- }
- return 0;
-}
-
-static unsigned int stac92xx_vref_set(struct hda_codec *codec,
- hda_nid_t nid, unsigned int new_vref)
-{
- int error;
- unsigned int pincfg;
- pincfg = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-
- pincfg &= 0xff;
- pincfg &= ~(AC_PINCTL_VREFEN | AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
- pincfg |= new_vref;
-
- if (new_vref == AC_PINCTL_VREF_HIZ)
- pincfg |= AC_PINCTL_OUT_EN;
- else
- pincfg |= AC_PINCTL_IN_EN;
-
- error = snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pincfg);
- if (error < 0)
- return error;
- else
- return 1;
-}
-
-static unsigned int stac92xx_vref_get(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int vref;
- vref = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- vref &= AC_PINCTL_VREFEN;
- return vref;
-}
-
-static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->input_mux, uinfo);
-}
-
-static int stac92xx_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
- return 0;
-}
-
-static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- const struct hda_input_mux *imux = spec->input_mux;
- unsigned int idx, prev_idx;
-
- idx = ucontrol->value.enumerated.item[0];
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
- prev_idx = spec->cur_mux[adc_idx];
- if (prev_idx == idx)
- return 0;
- if (idx < spec->num_analog_muxes) {
- snd_hda_codec_write_cache(codec, spec->mux_nids[adc_idx], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[idx].index);
- if (prev_idx >= spec->num_analog_muxes) {
- imux = spec->dinput_mux;
- /* 0 = analog */
- snd_hda_codec_write_cache(codec,
- spec->dmux_nids[adc_idx], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[0].index);
- }
- } else {
- imux = spec->dinput_mux;
- snd_hda_codec_write_cache(codec, spec->dmux_nids[adc_idx], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[idx - 1].index);
- }
- spec->cur_mux[adc_idx] = idx;
- return 1;
-}
-
-static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->mono_mux, uinfo);
-}
-
-static int stac92xx_mono_mux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
-
- ucontrol->value.enumerated.item[0] = spec->cur_mmux;
- return 0;
-}
-
-static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
-
- return snd_hda_input_mux_put(codec, spec->mono_mux, ucontrol,
- spec->mono_nid, &spec->cur_mmux);
-}
-
-#define stac92xx_aloopback_info snd_ctl_boolean_mono_info
-
-static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- struct sigmatel_spec *spec = codec->spec;
-
- ucontrol->value.integer.value[0] = !!(spec->aloopback &
- (spec->aloopback_mask << idx));
- return 0;
-}
-
-static int stac92xx_aloopback_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- unsigned int dac_mode;
- unsigned int val, idx_val;
-
- idx_val = spec->aloopback_mask << idx;
- if (ucontrol->value.integer.value[0])
- val = spec->aloopback | idx_val;
- else
- val = spec->aloopback & ~idx_val;
- if (spec->aloopback == val)
- return 0;
-
- spec->aloopback = val;
-
- /* Only return the bits defined by the shift value of the
- * first two bytes of the mask
- */
- dac_mode = snd_hda_codec_read(codec, codec->afg, 0,
- kcontrol->private_value & 0xFFFF, 0x0);
- dac_mode >>= spec->aloopback_shift;
-
- if (spec->aloopback & idx_val) {
- snd_hda_power_up(codec);
- dac_mode |= idx_val;
- } else {
- snd_hda_power_down(codec);
- dac_mode &= ~idx_val;
- }
-
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- kcontrol->private_value >> 16, dac_mode);
-
- return 1;
-}
-
-static struct hda_verb stac9200_core_init[] = {
- /* set dac0mux for dac converter */
- { 0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {}
-};
-
-static struct hda_verb stac9200_eapd_init[] = {
- /* set dac0mux for dac converter */
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- {}
-};
-
-static struct hda_verb dell_eq_core_init[] = {
- /* set master volume to max value without distortion
- * and direct control */
- { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec},
- {}
-};
-
-static struct hda_verb stac92hd73xx_core_init[] = {
- /* set master volume and direct control */
- { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- {}
-};
-
-static struct hda_verb stac92hd83xxx_core_init[] = {
- /* power state controls amps */
- { 0x01, AC_VERB_SET_EAPD, 1 << 2},
- {}
-};
-
-static struct hda_verb stac92hd71bxx_core_init[] = {
- /* set master volume and direct control */
- { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- {}
-};
-
-static struct hda_verb stac92hd71bxx_unmute_core_init[] = {
- /* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */
- { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {}
-};
-
-static struct hda_verb stac925x_core_init[] = {
- /* set dac0mux for dac converter */
- { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* mute the master volume */
- { 0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- {}
-};
-
-static struct hda_verb stac922x_core_init[] = {
- /* set master volume and direct control */
- { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- {}
-};
-
-static struct hda_verb d965_core_init[] = {
- /* set master volume and direct control */
- { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- /* unmute node 0x1b */
- { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* select node 0x03 as DAC */
- { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01},
- {}
-};
-
-static struct hda_verb dell_3st_core_init[] = {
- /* don't set delta bit */
- {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f},
- /* unmute node 0x1b */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* select node 0x03 as DAC */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x01},
- {}
-};
-
-static struct hda_verb stac927x_core_init[] = {
- /* set master volume and direct control */
- { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- /* enable analog pc beep path */
- { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
- {}
-};
-
-static struct hda_verb stac927x_volknob_core_init[] = {
- /* don't set delta bit */
- {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f},
- /* enable analog pc beep path */
- {0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
- {}
-};
-
-static struct hda_verb stac9205_core_init[] = {
- /* set master volume and direct control */
- { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- /* enable analog pc beep path */
- { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
- {}
-};
-
-#define STAC_MONO_MUX \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Mono Mux", \
- .count = 1, \
- .info = stac92xx_mono_mux_enum_info, \
- .get = stac92xx_mono_mux_enum_get, \
- .put = stac92xx_mono_mux_enum_put, \
- }
-
-#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = "Analog Loopback", \
- .count = cnt, \
- .info = stac92xx_aloopback_info, \
- .get = stac92xx_aloopback_get, \
- .put = stac92xx_aloopback_put, \
- .private_value = verb_read | (verb_write << 16), \
- }
-
-#define DC_BIAS(xname, idx, nid) \
- { \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = idx, \
- .info = stac92xx_dc_bias_info, \
- .get = stac92xx_dc_bias_get, \
- .put = stac92xx_dc_bias_put, \
- .private_value = nid, \
- }
-
-static struct snd_kcontrol_new stac9200_mixer[] = {
- HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new stac92hd73xx_6ch_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
- {}
-};
-
-static struct snd_kcontrol_new stac92hd73xx_8ch_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4),
- {}
-};
-
-static struct snd_kcontrol_new stac92hd73xx_10ch_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5),
- {}
-};
-
-
-static struct snd_kcontrol_new stac92hd71bxx_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2)
-};
-
-static struct snd_kcontrol_new stac925x_mixer[] = {
- HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xe, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct snd_kcontrol_new stac9205_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1),
- {}
-};
-
-static struct snd_kcontrol_new stac927x_loopback[] = {
- STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1),
- {}
-};
-
-static struct snd_kcontrol_new stac_dmux_mixer = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Digital Input Source",
- /* count set later */
- .info = stac92xx_dmux_enum_info,
- .get = stac92xx_dmux_enum_get,
- .put = stac92xx_dmux_enum_put,
-};
-
-static struct snd_kcontrol_new stac_smux_mixer = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "IEC958 Playback Source",
- /* count set later */
- .info = stac92xx_smux_enum_info,
- .get = stac92xx_smux_enum_get,
- .put = stac92xx_smux_enum_put,
-};
-
-static const char *slave_vols[] = {
- "Front Playback Volume",
- "Surround Playback Volume",
- "Center Playback Volume",
- "LFE Playback Volume",
- "Side Playback Volume",
- "Headphone Playback Volume",
- "Speaker Playback Volume",
- NULL
-};
-
-static const char *slave_sws[] = {
- "Front Playback Switch",
- "Surround Playback Switch",
- "Center Playback Switch",
- "LFE Playback Switch",
- "Side Playback Switch",
- "Headphone Playback Switch",
- "Speaker Playback Switch",
- "IEC958 Playback Switch",
- NULL
-};
-
-static void stac92xx_free_kctls(struct hda_codec *codec);
-static int stac92xx_add_jack(struct hda_codec *codec, hda_nid_t nid, int type);
-
-static int stac92xx_build_controls(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
- int err;
- int i;
-
- if (spec->mixer) {
- err = snd_hda_add_new_ctls(codec, spec->mixer);
- if (err < 0)
- return err;
- }
-
- for (i = 0; i < spec->num_mixers; i++) {
- err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
- if (err < 0)
- return err;
- }
- if (!spec->auto_mic && spec->num_dmuxes > 0 &&
- snd_hda_get_bool_hint(codec, "separate_dmux") == 1) {
- stac_dmux_mixer.count = spec->num_dmuxes;
- err = snd_hda_ctl_add(codec, 0,
- snd_ctl_new1(&stac_dmux_mixer, codec));
- if (err < 0)
- return err;
- }
- if (spec->num_smuxes > 0) {
- int wcaps = get_wcaps(codec, spec->multiout.dig_out_nid);
- struct hda_input_mux *smux = &spec->private_smux;
- /* check for mute support on SPDIF out */
- if (wcaps & AC_WCAP_OUT_AMP) {
- snd_hda_add_imux_item(smux, "Off", 0, NULL);
- spec->spdif_mute = 1;
- }
- stac_smux_mixer.count = spec->num_smuxes;
- err = snd_hda_ctl_add(codec, 0,
- snd_ctl_new1(&stac_smux_mixer, codec));
- if (err < 0)
- return err;
- }
-
- if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- if (spec->dig_in_nid && !(spec->gpio_dir & 0x01)) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
- if (err < 0)
- return err;
- }
-
- /* if we have no master control, let's create it */
- if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
- unsigned int vmaster_tlv[4];
- snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
- HDA_OUTPUT, vmaster_tlv);
- /* correct volume offset */
- vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset;
- /* minimum value is actually mute */
- vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
- err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- vmaster_tlv, slave_vols);
- if (err < 0)
- return err;
- }
- if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
- err = snd_hda_add_vmaster(codec, "Master Playback Switch",
- NULL, slave_sws);
- if (err < 0)
- return err;
- }
-
- if (spec->aloopback_ctl &&
- snd_hda_get_bool_hint(codec, "loopback") == 1) {
- err = snd_hda_add_new_ctls(codec, spec->aloopback_ctl);
- if (err < 0)
- return err;
- }
-
- stac92xx_free_kctls(codec); /* no longer needed */
-
- /* create jack input elements */
- if (spec->hp_detect) {
- for (i = 0; i < cfg->hp_outs; i++) {
- int type = SND_JACK_HEADPHONE;
- nid = cfg->hp_pins[i];
- /* jack detection */
- if (cfg->hp_outs == i)
- type |= SND_JACK_LINEOUT;
- err = stac92xx_add_jack(codec, nid, type);
- if (err < 0)
- return err;
- }
- }
- for (i = 0; i < cfg->line_outs; i++) {
- err = stac92xx_add_jack(codec, cfg->line_out_pins[i],
- SND_JACK_LINEOUT);
- if (err < 0)
- return err;
- }
- for (i = 0; i < cfg->num_inputs; i++) {
- nid = cfg->inputs[i].pin;
- err = stac92xx_add_jack(codec, nid, SND_JACK_MICROPHONE);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static unsigned int ref9200_pin_configs[8] = {
- 0x01c47010, 0x01447010, 0x0221401f, 0x01114010,
- 0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
-};
-
-static unsigned int gateway9200_m4_pin_configs[8] = {
- 0x400000fe, 0x404500f4, 0x400100f0, 0x90110010,
- 0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3,
-};
-static unsigned int gateway9200_m4_2_pin_configs[8] = {
- 0x400000fe, 0x404500f4, 0x400100f0, 0x90110010,
- 0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3,
-};
-
-/*
- STAC 9200 pin configs for
- 102801A8
- 102801DE
- 102801E8
-*/
-static unsigned int dell9200_d21_pin_configs[8] = {
- 0x400001f0, 0x400001f1, 0x02214030, 0x01014010,
- 0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
-};
-
-/*
- STAC 9200 pin configs for
- 102801C0
- 102801C1
-*/
-static unsigned int dell9200_d22_pin_configs[8] = {
- 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010,
- 0x01813020, 0x02a19021, 0x90100140, 0x400001f2,
-};
-
-/*
- STAC 9200 pin configs for
- 102801C4 (Dell Dimension E310)
- 102801C5
- 102801C7
- 102801D9
- 102801DA
- 102801E3
-*/
-static unsigned int dell9200_d23_pin_configs[8] = {
- 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010,
- 0x01813020, 0x01a19021, 0x90100140, 0x400001f2,
-};
-
-
-/*
- STAC 9200-32 pin configs for
- 102801B5 (Dell Inspiron 630m)
- 102801D8 (Dell Inspiron 640m)
-*/
-static unsigned int dell9200_m21_pin_configs[8] = {
- 0x40c003fa, 0x03441340, 0x0321121f, 0x90170310,
- 0x408003fb, 0x03a11020, 0x401003fc, 0x403003fd,
-};
-
-/*
- STAC 9200-32 pin configs for
- 102801C2 (Dell Latitude D620)
- 102801C8
- 102801CC (Dell Latitude D820)
- 102801D4
- 102801D6
-*/
-static unsigned int dell9200_m22_pin_configs[8] = {
- 0x40c003fa, 0x0144131f, 0x0321121f, 0x90170310,
- 0x90a70321, 0x03a11020, 0x401003fb, 0x40f000fc,
-};
-
-/*
- STAC 9200-32 pin configs for
- 102801CE (Dell XPS M1710)
- 102801CF (Dell Precision M90)
-*/
-static unsigned int dell9200_m23_pin_configs[8] = {
- 0x40c003fa, 0x01441340, 0x0421421f, 0x90170310,
- 0x408003fb, 0x04a1102e, 0x90170311, 0x403003fc,
-};
-
-/*
- STAC 9200-32 pin configs for
- 102801C9
- 102801CA
- 102801CB (Dell Latitude 120L)
- 102801D3
-*/
-static unsigned int dell9200_m24_pin_configs[8] = {
- 0x40c003fa, 0x404003fb, 0x0321121f, 0x90170310,
- 0x408003fc, 0x03a11020, 0x401003fd, 0x403003fe,
-};
-
-/*
- STAC 9200-32 pin configs for
- 102801BD (Dell Inspiron E1505n)
- 102801EE
- 102801EF
-*/
-static unsigned int dell9200_m25_pin_configs[8] = {
- 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310,
- 0x408003fb, 0x04a11020, 0x401003fc, 0x403003fd,
-};
-
-/*
- STAC 9200-32 pin configs for
- 102801F5 (Dell Inspiron 1501)
- 102801F6
-*/
-static unsigned int dell9200_m26_pin_configs[8] = {
- 0x40c003fa, 0x404003fb, 0x0421121f, 0x90170310,
- 0x408003fc, 0x04a11020, 0x401003fd, 0x403003fe,
-};
-
-/*
- STAC 9200-32
- 102801CD (Dell Inspiron E1705/9400)
-*/
-static unsigned int dell9200_m27_pin_configs[8] = {
- 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310,
- 0x90170310, 0x04a11020, 0x90170310, 0x40f003fc,
-};
-
-static unsigned int oqo9200_pin_configs[8] = {
- 0x40c000f0, 0x404000f1, 0x0221121f, 0x02211210,
- 0x90170111, 0x90a70120, 0x400000f2, 0x400000f3,
-};
-
-
-static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = {
- [STAC_REF] = ref9200_pin_configs,
- [STAC_9200_OQO] = oqo9200_pin_configs,
- [STAC_9200_DELL_D21] = dell9200_d21_pin_configs,
- [STAC_9200_DELL_D22] = dell9200_d22_pin_configs,
- [STAC_9200_DELL_D23] = dell9200_d23_pin_configs,
- [STAC_9200_DELL_M21] = dell9200_m21_pin_configs,
- [STAC_9200_DELL_M22] = dell9200_m22_pin_configs,
- [STAC_9200_DELL_M23] = dell9200_m23_pin_configs,
- [STAC_9200_DELL_M24] = dell9200_m24_pin_configs,
- [STAC_9200_DELL_M25] = dell9200_m25_pin_configs,
- [STAC_9200_DELL_M26] = dell9200_m26_pin_configs,
- [STAC_9200_DELL_M27] = dell9200_m27_pin_configs,
- [STAC_9200_M4] = gateway9200_m4_pin_configs,
- [STAC_9200_M4_2] = gateway9200_m4_2_pin_configs,
- [STAC_9200_PANASONIC] = ref9200_pin_configs,
-};
-
-static const char *stac9200_models[STAC_9200_MODELS] = {
- [STAC_AUTO] = "auto",
- [STAC_REF] = "ref",
- [STAC_9200_OQO] = "oqo",
- [STAC_9200_DELL_D21] = "dell-d21",
- [STAC_9200_DELL_D22] = "dell-d22",
- [STAC_9200_DELL_D23] = "dell-d23",
- [STAC_9200_DELL_M21] = "dell-m21",
- [STAC_9200_DELL_M22] = "dell-m22",
- [STAC_9200_DELL_M23] = "dell-m23",
- [STAC_9200_DELL_M24] = "dell-m24",
- [STAC_9200_DELL_M25] = "dell-m25",
- [STAC_9200_DELL_M26] = "dell-m26",
- [STAC_9200_DELL_M27] = "dell-m27",
- [STAC_9200_M4] = "gateway-m4",
- [STAC_9200_M4_2] = "gateway-m4-2",
- [STAC_9200_PANASONIC] = "panasonic",
-};
-
-static struct snd_pci_quirk stac9200_cfg_tbl[] = {
- /* SigmaTel reference board */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
- "DFI LanParty", STAC_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
- "DFI LanParty", STAC_REF),
- /* Dell laptops have BIOS problem */
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a8,
- "unknown Dell", STAC_9200_DELL_D21),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01b5,
- "Dell Inspiron 630m", STAC_9200_DELL_M21),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01bd,
- "Dell Inspiron E1505n", STAC_9200_DELL_M25),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c0,
- "unknown Dell", STAC_9200_DELL_D22),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c1,
- "unknown Dell", STAC_9200_DELL_D22),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c2,
- "Dell Latitude D620", STAC_9200_DELL_M22),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c5,
- "unknown Dell", STAC_9200_DELL_D23),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c7,
- "unknown Dell", STAC_9200_DELL_D23),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c8,
- "unknown Dell", STAC_9200_DELL_M22),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c9,
- "unknown Dell", STAC_9200_DELL_M24),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ca,
- "unknown Dell", STAC_9200_DELL_M24),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cb,
- "Dell Latitude 120L", STAC_9200_DELL_M24),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cc,
- "Dell Latitude D820", STAC_9200_DELL_M22),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cd,
- "Dell Inspiron E1705/9400", STAC_9200_DELL_M27),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ce,
- "Dell XPS M1710", STAC_9200_DELL_M23),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cf,
- "Dell Precision M90", STAC_9200_DELL_M23),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d3,
- "unknown Dell", STAC_9200_DELL_M22),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d4,
- "unknown Dell", STAC_9200_DELL_M22),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d6,
- "unknown Dell", STAC_9200_DELL_M22),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d8,
- "Dell Inspiron 640m", STAC_9200_DELL_M21),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d9,
- "unknown Dell", STAC_9200_DELL_D23),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01da,
- "unknown Dell", STAC_9200_DELL_D23),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01de,
- "unknown Dell", STAC_9200_DELL_D21),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01e3,
- "unknown Dell", STAC_9200_DELL_D23),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01e8,
- "unknown Dell", STAC_9200_DELL_D21),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ee,
- "unknown Dell", STAC_9200_DELL_M25),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ef,
- "unknown Dell", STAC_9200_DELL_M25),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f5,
- "Dell Inspiron 1501", STAC_9200_DELL_M26),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f6,
- "unknown Dell", STAC_9200_DELL_M26),
- /* Panasonic */
- SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_9200_PANASONIC),
- /* Gateway machines needs EAPD to be set on resume */
- SND_PCI_QUIRK(0x107b, 0x0205, "Gateway S-7110M", STAC_9200_M4),
- SND_PCI_QUIRK(0x107b, 0x0317, "Gateway MT3423, MX341*", STAC_9200_M4_2),
- SND_PCI_QUIRK(0x107b, 0x0318, "Gateway ML3019, MT3707", STAC_9200_M4_2),
- /* OQO Mobile */
- SND_PCI_QUIRK(0x1106, 0x3288, "OQO Model 2", STAC_9200_OQO),
- {} /* terminator */
-};
-
-static unsigned int ref925x_pin_configs[8] = {
- 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
- 0x90a70320, 0x02214210, 0x01019020, 0x9033032e,
-};
-
-static unsigned int stac925xM1_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
-};
-
-static unsigned int stac925xM1_2_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
-};
-
-static unsigned int stac925xM2_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
-};
-
-static unsigned int stac925xM2_2_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
-};
-
-static unsigned int stac925xM3_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x503303f3,
-};
-
-static unsigned int stac925xM5_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
-};
-
-static unsigned int stac925xM6_pin_configs[8] = {
- 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
- 0x40a000f0, 0x90100210, 0x400003f1, 0x90330320,
-};
-
-static unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = {
- [STAC_REF] = ref925x_pin_configs,
- [STAC_M1] = stac925xM1_pin_configs,
- [STAC_M1_2] = stac925xM1_2_pin_configs,
- [STAC_M2] = stac925xM2_pin_configs,
- [STAC_M2_2] = stac925xM2_2_pin_configs,
- [STAC_M3] = stac925xM3_pin_configs,
- [STAC_M5] = stac925xM5_pin_configs,
- [STAC_M6] = stac925xM6_pin_configs,
-};
-
-static const char *stac925x_models[STAC_925x_MODELS] = {
- [STAC_925x_AUTO] = "auto",
- [STAC_REF] = "ref",
- [STAC_M1] = "m1",
- [STAC_M1_2] = "m1-2",
- [STAC_M2] = "m2",
- [STAC_M2_2] = "m2-2",
- [STAC_M3] = "m3",
- [STAC_M5] = "m5",
- [STAC_M6] = "m6",
-};
-
-static struct snd_pci_quirk stac925x_codec_id_cfg_tbl[] = {
- SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_M2),
- SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_M5),
- SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_M1),
- SND_PCI_QUIRK(0x107b, 0x0681, "Gateway NX860", STAC_M2),
- SND_PCI_QUIRK(0x107b, 0x0367, "Gateway MX6453", STAC_M1_2),
- /* Not sure about the brand name for those */
- SND_PCI_QUIRK(0x107b, 0x0281, "Gateway mobile", STAC_M1),
- SND_PCI_QUIRK(0x107b, 0x0507, "Gateway mobile", STAC_M3),
- SND_PCI_QUIRK(0x107b, 0x0281, "Gateway mobile", STAC_M6),
- SND_PCI_QUIRK(0x107b, 0x0685, "Gateway mobile", STAC_M2_2),
- {} /* terminator */
-};
-
-static struct snd_pci_quirk stac925x_cfg_tbl[] = {
- /* SigmaTel reference board */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF),
- SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF),
-
- /* Default table for unknown ID */
- SND_PCI_QUIRK(0x1002, 0x437b, "Gateway mobile", STAC_M2_2),
-
- {} /* terminator */
-};
-
-static unsigned int ref92hd73xx_pin_configs[13] = {
- 0x02214030, 0x02a19040, 0x01a19020, 0x02214030,
- 0x0181302e, 0x01014010, 0x01014020, 0x01014030,
- 0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050,
- 0x01452050,
-};
-
-static unsigned int dell_m6_pin_configs[13] = {
- 0x0321101f, 0x4f00000f, 0x4f0000f0, 0x90170110,
- 0x03a11020, 0x0321101f, 0x4f0000f0, 0x4f0000f0,
- 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0,
- 0x4f0000f0,
-};
-
-static unsigned int alienware_m17x_pin_configs[13] = {
- 0x0321101f, 0x0321101f, 0x03a11020, 0x03014020,
- 0x90170110, 0x4f0000f0, 0x4f0000f0, 0x4f0000f0,
- 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0,
- 0x904601b0,
-};
-
-static unsigned int intel_dg45id_pin_configs[13] = {
- 0x02214230, 0x02A19240, 0x01013214, 0x01014210,
- 0x01A19250, 0x01011212, 0x01016211
-};
-
-static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
- [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs,
- [STAC_DELL_M6_AMIC] = dell_m6_pin_configs,
- [STAC_DELL_M6_DMIC] = dell_m6_pin_configs,
- [STAC_DELL_M6_BOTH] = dell_m6_pin_configs,
- [STAC_DELL_EQ] = dell_m6_pin_configs,
- [STAC_ALIENWARE_M17X] = alienware_m17x_pin_configs,
- [STAC_92HD73XX_INTEL] = intel_dg45id_pin_configs,
-};
-
-static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
- [STAC_92HD73XX_AUTO] = "auto",
- [STAC_92HD73XX_NO_JD] = "no-jd",
- [STAC_92HD73XX_REF] = "ref",
- [STAC_92HD73XX_INTEL] = "intel",
- [STAC_DELL_M6_AMIC] = "dell-m6-amic",
- [STAC_DELL_M6_DMIC] = "dell-m6-dmic",
- [STAC_DELL_M6_BOTH] = "dell-m6",
- [STAC_DELL_EQ] = "dell-eq",
- [STAC_ALIENWARE_M17X] = "alienware",
-};
-
-static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
- /* SigmaTel reference board */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
- "DFI LanParty", STAC_92HD73XX_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
- "DFI LanParty", STAC_92HD73XX_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5002,
- "Intel DG45ID", STAC_92HD73XX_INTEL),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5003,
- "Intel DG45FC", STAC_92HD73XX_INTEL),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0254,
- "Dell Studio 1535", STAC_DELL_M6_DMIC),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0255,
- "unknown Dell", STAC_DELL_M6_DMIC),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0256,
- "unknown Dell", STAC_DELL_M6_BOTH),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0257,
- "unknown Dell", STAC_DELL_M6_BOTH),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025e,
- "unknown Dell", STAC_DELL_M6_AMIC),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x025f,
- "unknown Dell", STAC_DELL_M6_AMIC),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0271,
- "unknown Dell", STAC_DELL_M6_DMIC),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0272,
- "unknown Dell", STAC_DELL_M6_DMIC),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x029f,
- "Dell Studio 1537", STAC_DELL_M6_DMIC),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a0,
- "Dell Studio 17", STAC_DELL_M6_DMIC),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02be,
- "Dell Studio 1555", STAC_DELL_M6_DMIC),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02bd,
- "Dell Studio 1557", STAC_DELL_M6_DMIC),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02fe,
- "Dell Studio XPS 1645", STAC_DELL_M6_BOTH),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0413,
- "Dell Studio 1558", STAC_DELL_M6_BOTH),
- {} /* terminator */
-};
-
-static struct snd_pci_quirk stac92hd73xx_codec_id_cfg_tbl[] = {
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a1,
- "Alienware M17x", STAC_ALIENWARE_M17X),
- {} /* terminator */
-};
-
-static unsigned int ref92hd83xxx_pin_configs[10] = {
- 0x02214030, 0x02211010, 0x02a19020, 0x02170130,
- 0x01014050, 0x01819040, 0x01014020, 0x90a3014e,
- 0x01451160, 0x98560170,
-};
-
-static unsigned int dell_s14_pin_configs[10] = {
- 0x0221403f, 0x0221101f, 0x02a19020, 0x90170110,
- 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a60160,
- 0x40f000f0, 0x40f000f0,
-};
-
-static unsigned int hp_dv7_4000_pin_configs[10] = {
- 0x03a12050, 0x0321201f, 0x40f000f0, 0x90170110,
- 0x40f000f0, 0x40f000f0, 0x90170110, 0xd5a30140,
- 0x40f000f0, 0x40f000f0,
-};
-
-static unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = {
- [STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs,
- [STAC_92HD83XXX_PWR_REF] = ref92hd83xxx_pin_configs,
- [STAC_DELL_S14] = dell_s14_pin_configs,
- [STAC_HP_DV7_4000] = hp_dv7_4000_pin_configs,
-};
-
-static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
- [STAC_92HD83XXX_AUTO] = "auto",
- [STAC_92HD83XXX_REF] = "ref",
- [STAC_92HD83XXX_PWR_REF] = "mic-ref",
- [STAC_DELL_S14] = "dell-s14",
- [STAC_92HD83XXX_HP] = "hp",
- [STAC_HP_DV7_4000] = "hp-dv7-4000",
-};
-
-static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
- /* SigmaTel reference board */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
- "DFI LanParty", STAC_92HD83XXX_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
- "DFI LanParty", STAC_92HD83XXX_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba,
- "unknown Dell", STAC_DELL_S14),
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x3600,
- "HP", STAC_92HD83XXX_HP),
- {} /* terminator */
-};
-
-static unsigned int ref92hd71bxx_pin_configs[STAC92HD71BXX_NUM_PINS] = {
- 0x02214030, 0x02a19040, 0x01a19020, 0x01014010,
- 0x0181302e, 0x01014010, 0x01019020, 0x90a000f0,
- 0x90a000f0, 0x01452050, 0x01452050, 0x00000000,
- 0x00000000
-};
-
-static unsigned int dell_m4_1_pin_configs[STAC92HD71BXX_NUM_PINS] = {
- 0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110,
- 0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0,
- 0x40f000f0, 0x4f0000f0, 0x4f0000f0, 0x00000000,
- 0x00000000
-};
-
-static unsigned int dell_m4_2_pin_configs[STAC92HD71BXX_NUM_PINS] = {
- 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
- 0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0,
- 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000,
- 0x00000000
-};
-
-static unsigned int dell_m4_3_pin_configs[STAC92HD71BXX_NUM_PINS] = {
- 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
- 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a000f0,
- 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000,
- 0x00000000
-};
-
-static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
- [STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs,
- [STAC_DELL_M4_1] = dell_m4_1_pin_configs,
- [STAC_DELL_M4_2] = dell_m4_2_pin_configs,
- [STAC_DELL_M4_3] = dell_m4_3_pin_configs,
- [STAC_HP_M4] = NULL,
- [STAC_HP_DV4] = NULL,
- [STAC_HP_DV5] = NULL,
- [STAC_HP_HDX] = NULL,
- [STAC_HP_DV4_1222NR] = NULL,
-};
-
-static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
- [STAC_92HD71BXX_AUTO] = "auto",
- [STAC_92HD71BXX_REF] = "ref",
- [STAC_DELL_M4_1] = "dell-m4-1",
- [STAC_DELL_M4_2] = "dell-m4-2",
- [STAC_DELL_M4_3] = "dell-m4-3",
- [STAC_HP_M4] = "hp-m4",
- [STAC_HP_DV4] = "hp-dv4",
- [STAC_HP_DV5] = "hp-dv5",
- [STAC_HP_HDX] = "hp-hdx",
- [STAC_HP_DV4_1222NR] = "hp-dv4-1222nr",
-};
-
-static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
- /* SigmaTel reference board */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
- "DFI LanParty", STAC_92HD71BXX_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
- "DFI LanParty", STAC_92HD71BXX_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fb,
- "HP dv4-1222nr", STAC_HP_DV4_1222NR),
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x1720,
- "HP", STAC_HP_DV5),
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080,
- "HP", STAC_HP_DV5),
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0,
- "HP dv4-7", STAC_HP_DV4),
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3600,
- "HP dv4-7", STAC_HP_DV5),
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3610,
- "HP HDX", STAC_HP_HDX), /* HDX18 */
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a,
- "HP mini 1000", STAC_HP_M4),
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361b,
- "HP HDX", STAC_HP_HDX), /* HDX16 */
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3620,
- "HP dv6", STAC_HP_DV5),
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3061,
- "HP dv6", STAC_HP_DV5), /* HP dv6-1110ax */
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x363e,
- "HP DV6", STAC_HP_DV5),
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010,
- "HP", STAC_HP_DV5),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
- "unknown Dell", STAC_DELL_M4_1),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
- "unknown Dell", STAC_DELL_M4_1),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0250,
- "unknown Dell", STAC_DELL_M4_1),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024f,
- "unknown Dell", STAC_DELL_M4_1),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024d,
- "unknown Dell", STAC_DELL_M4_1),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0251,
- "unknown Dell", STAC_DELL_M4_1),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0277,
- "unknown Dell", STAC_DELL_M4_1),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0263,
- "unknown Dell", STAC_DELL_M4_2),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0265,
- "unknown Dell", STAC_DELL_M4_2),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0262,
- "unknown Dell", STAC_DELL_M4_2),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0264,
- "unknown Dell", STAC_DELL_M4_2),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02aa,
- "unknown Dell", STAC_DELL_M4_3),
- {} /* terminator */
-};
-
-static unsigned int ref922x_pin_configs[10] = {
- 0x01014010, 0x01016011, 0x01012012, 0x0221401f,
- 0x01813122, 0x01011014, 0x01441030, 0x01c41030,
- 0x40000100, 0x40000100,
-};
-
-/*
- STAC 922X pin configs for
- 102801A7
- 102801AB
- 102801A9
- 102801D1
- 102801D2
-*/
-static unsigned int dell_922x_d81_pin_configs[10] = {
- 0x02214030, 0x01a19021, 0x01111012, 0x01114010,
- 0x02a19020, 0x01117011, 0x400001f0, 0x400001f1,
- 0x01813122, 0x400001f2,
-};
-
-/*
- STAC 922X pin configs for
- 102801AC
- 102801D0
-*/
-static unsigned int dell_922x_d82_pin_configs[10] = {
- 0x02214030, 0x01a19021, 0x01111012, 0x01114010,
- 0x02a19020, 0x01117011, 0x01451140, 0x400001f0,
- 0x01813122, 0x400001f1,
-};
-
-/*
- STAC 922X pin configs for
- 102801BF
-*/
-static unsigned int dell_922x_m81_pin_configs[10] = {
- 0x0321101f, 0x01112024, 0x01111222, 0x91174220,
- 0x03a11050, 0x01116221, 0x90a70330, 0x01452340,
- 0x40C003f1, 0x405003f0,
-};
-
-/*
- STAC 9221 A1 pin configs for
- 102801D7 (Dell XPS M1210)
-*/
-static unsigned int dell_922x_m82_pin_configs[10] = {
- 0x02211211, 0x408103ff, 0x02a1123e, 0x90100310,
- 0x408003f1, 0x0221121f, 0x03451340, 0x40c003f2,
- 0x508003f3, 0x405003f4,
-};
-
-static unsigned int d945gtp3_pin_configs[10] = {
- 0x0221401f, 0x01a19022, 0x01813021, 0x01014010,
- 0x40000100, 0x40000100, 0x40000100, 0x40000100,
- 0x02a19120, 0x40000100,
-};
-
-static unsigned int d945gtp5_pin_configs[10] = {
- 0x0221401f, 0x01011012, 0x01813024, 0x01014010,
- 0x01a19021, 0x01016011, 0x01452130, 0x40000100,
- 0x02a19320, 0x40000100,
-};
-
-static unsigned int intel_mac_v1_pin_configs[10] = {
- 0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd,
- 0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240,
- 0x400000fc, 0x400000fb,
-};
-
-static unsigned int intel_mac_v2_pin_configs[10] = {
- 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
- 0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa,
- 0x400000fc, 0x400000fb,
-};
-
-static unsigned int intel_mac_v3_pin_configs[10] = {
- 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
- 0x400000fe, 0x0181e020, 0x1145e230, 0x11c5e240,
- 0x400000fc, 0x400000fb,
-};
-
-static unsigned int intel_mac_v4_pin_configs[10] = {
- 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
- 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
- 0x400000fc, 0x400000fb,
-};
-
-static unsigned int intel_mac_v5_pin_configs[10] = {
- 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
- 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
- 0x400000fc, 0x400000fb,
-};
-
-static unsigned int ecs202_pin_configs[10] = {
- 0x0221401f, 0x02a19020, 0x01a19020, 0x01114010,
- 0x408000f0, 0x01813022, 0x074510a0, 0x40c400f1,
- 0x9037012e, 0x40e000f2,
-};
-
-static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
- [STAC_D945_REF] = ref922x_pin_configs,
- [STAC_D945GTP3] = d945gtp3_pin_configs,
- [STAC_D945GTP5] = d945gtp5_pin_configs,
- [STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs,
- [STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs,
- [STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs,
- [STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs,
- [STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs,
- [STAC_INTEL_MAC_AUTO] = intel_mac_v3_pin_configs,
- /* for backward compatibility */
- [STAC_MACMINI] = intel_mac_v3_pin_configs,
- [STAC_MACBOOK] = intel_mac_v5_pin_configs,
- [STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs,
- [STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs,
- [STAC_IMAC_INTEL] = intel_mac_v2_pin_configs,
- [STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs,
- [STAC_ECS_202] = ecs202_pin_configs,
- [STAC_922X_DELL_D81] = dell_922x_d81_pin_configs,
- [STAC_922X_DELL_D82] = dell_922x_d82_pin_configs,
- [STAC_922X_DELL_M81] = dell_922x_m81_pin_configs,
- [STAC_922X_DELL_M82] = dell_922x_m82_pin_configs,
-};
-
-static const char *stac922x_models[STAC_922X_MODELS] = {
- [STAC_922X_AUTO] = "auto",
- [STAC_D945_REF] = "ref",
- [STAC_D945GTP5] = "5stack",
- [STAC_D945GTP3] = "3stack",
- [STAC_INTEL_MAC_V1] = "intel-mac-v1",
- [STAC_INTEL_MAC_V2] = "intel-mac-v2",
- [STAC_INTEL_MAC_V3] = "intel-mac-v3",
- [STAC_INTEL_MAC_V4] = "intel-mac-v4",
- [STAC_INTEL_MAC_V5] = "intel-mac-v5",
- [STAC_INTEL_MAC_AUTO] = "intel-mac-auto",
- /* for backward compatibility */
- [STAC_MACMINI] = "macmini",
- [STAC_MACBOOK] = "macbook",
- [STAC_MACBOOK_PRO_V1] = "macbook-pro-v1",
- [STAC_MACBOOK_PRO_V2] = "macbook-pro",
- [STAC_IMAC_INTEL] = "imac-intel",
- [STAC_IMAC_INTEL_20] = "imac-intel-20",
- [STAC_ECS_202] = "ecs202",
- [STAC_922X_DELL_D81] = "dell-d81",
- [STAC_922X_DELL_D82] = "dell-d82",
- [STAC_922X_DELL_M81] = "dell-m81",
- [STAC_922X_DELL_M82] = "dell-m82",
-};
-
-static struct snd_pci_quirk stac922x_cfg_tbl[] = {
- /* SigmaTel reference board */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
- "DFI LanParty", STAC_D945_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
- "DFI LanParty", STAC_D945_REF),
- /* Intel 945G based systems */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0101,
- "Intel D945G", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0202,
- "Intel D945G", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0606,
- "Intel D945G", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0601,
- "Intel D945G", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0111,
- "Intel D945G", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1115,
- "Intel D945G", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1116,
- "Intel D945G", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1117,
- "Intel D945G", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1118,
- "Intel D945G", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x1119,
- "Intel D945G", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x8826,
- "Intel D945G", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5049,
- "Intel D945G", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5055,
- "Intel D945G", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5048,
- "Intel D945G", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0110,
- "Intel D945G", STAC_D945GTP3),
- /* Intel D945G 5-stack systems */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0404,
- "Intel D945G", STAC_D945GTP5),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0303,
- "Intel D945G", STAC_D945GTP5),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0013,
- "Intel D945G", STAC_D945GTP5),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0417,
- "Intel D945G", STAC_D945GTP5),
- /* Intel 945P based systems */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0b0b,
- "Intel D945P", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0112,
- "Intel D945P", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0d0d,
- "Intel D945P", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0909,
- "Intel D945P", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0505,
- "Intel D945P", STAC_D945GTP3),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0707,
- "Intel D945P", STAC_D945GTP5),
- /* other intel */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0204,
- "Intel D945", STAC_D945_REF),
- /* other systems */
- /* Apple Intel Mac (Mac Mini, MacBook, MacBook Pro...) */
- SND_PCI_QUIRK(0x8384, 0x7680,
- "Mac", STAC_INTEL_MAC_AUTO),
- /* Dell systems */
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a7,
- "unknown Dell", STAC_922X_DELL_D81),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a9,
- "unknown Dell", STAC_922X_DELL_D81),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ab,
- "unknown Dell", STAC_922X_DELL_D81),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ac,
- "unknown Dell", STAC_922X_DELL_D82),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01bf,
- "unknown Dell", STAC_922X_DELL_M81),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d0,
- "unknown Dell", STAC_922X_DELL_D82),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d1,
- "unknown Dell", STAC_922X_DELL_D81),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d2,
- "unknown Dell", STAC_922X_DELL_D81),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d7,
- "Dell XPS M1210", STAC_922X_DELL_M82),
- /* ECS/PC Chips boards */
- SND_PCI_QUIRK_MASK(0x1019, 0xf000, 0x2000,
- "ECS/PC chips", STAC_ECS_202),
- {} /* terminator */
-};
-
-static unsigned int ref927x_pin_configs[14] = {
- 0x02214020, 0x02a19080, 0x0181304e, 0x01014010,
- 0x01a19040, 0x01011012, 0x01016011, 0x0101201f,
- 0x183301f0, 0x18a001f0, 0x18a001f0, 0x01442070,
- 0x01c42190, 0x40000100,
-};
-
-static unsigned int d965_3st_pin_configs[14] = {
- 0x0221401f, 0x02a19120, 0x40000100, 0x01014011,
- 0x01a19021, 0x01813024, 0x40000100, 0x40000100,
- 0x40000100, 0x40000100, 0x40000100, 0x40000100,
- 0x40000100, 0x40000100
-};
-
-static unsigned int d965_5st_pin_configs[14] = {
- 0x02214020, 0x02a19080, 0x0181304e, 0x01014010,
- 0x01a19040, 0x01011012, 0x01016011, 0x40000100,
- 0x40000100, 0x40000100, 0x40000100, 0x01442070,
- 0x40000100, 0x40000100
-};
-
-static unsigned int d965_5st_no_fp_pin_configs[14] = {
- 0x40000100, 0x40000100, 0x0181304e, 0x01014010,
- 0x01a19040, 0x01011012, 0x01016011, 0x40000100,
- 0x40000100, 0x40000100, 0x40000100, 0x01442070,
- 0x40000100, 0x40000100
-};
-
-static unsigned int dell_3st_pin_configs[14] = {
- 0x02211230, 0x02a11220, 0x01a19040, 0x01114210,
- 0x01111212, 0x01116211, 0x01813050, 0x01112214,
- 0x403003fa, 0x90a60040, 0x90a60040, 0x404003fb,
- 0x40c003fc, 0x40000100
-};
-
-static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
- [STAC_D965_REF_NO_JD] = ref927x_pin_configs,
- [STAC_D965_REF] = ref927x_pin_configs,
- [STAC_D965_3ST] = d965_3st_pin_configs,
- [STAC_D965_5ST] = d965_5st_pin_configs,
- [STAC_D965_5ST_NO_FP] = d965_5st_no_fp_pin_configs,
- [STAC_DELL_3ST] = dell_3st_pin_configs,
- [STAC_DELL_BIOS] = NULL,
- [STAC_927X_VOLKNOB] = NULL,
-};
-
-static const char *stac927x_models[STAC_927X_MODELS] = {
- [STAC_927X_AUTO] = "auto",
- [STAC_D965_REF_NO_JD] = "ref-no-jd",
- [STAC_D965_REF] = "ref",
- [STAC_D965_3ST] = "3stack",
- [STAC_D965_5ST] = "5stack",
- [STAC_D965_5ST_NO_FP] = "5stack-no-fp",
- [STAC_DELL_3ST] = "dell-3stack",
- [STAC_DELL_BIOS] = "dell-bios",
- [STAC_927X_VOLKNOB] = "volknob",
-};
-
-static struct snd_pci_quirk stac927x_cfg_tbl[] = {
- /* SigmaTel reference board */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
- "DFI LanParty", STAC_D965_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
- "DFI LanParty", STAC_D965_REF),
- /* Intel 946 based systems */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x3d01, "Intel D946", STAC_D965_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xa301, "Intel D946", STAC_D965_3ST),
- /* 965 based 3 stack systems */
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2100,
- "Intel D965", STAC_D965_3ST),
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2000,
- "Intel D965", STAC_D965_3ST),
- /* Dell 3 stack systems */
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01dd, "Dell Dimension E520", STAC_DELL_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ed, "Dell ", STAC_DELL_3ST),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f4, "Dell ", STAC_DELL_3ST),
- /* Dell 3 stack systems with verb table in BIOS */
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_BIOS),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0227, "Dell Vostro 1400 ", STAC_DELL_BIOS),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell Inspiron 1525", STAC_DELL_BIOS),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0242, "Dell ", STAC_DELL_BIOS),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0243, "Dell ", STAC_DELL_BIOS),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS),
- /* 965 based 5 stack systems */
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2300,
- "Intel D965", STAC_D965_5ST),
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2500,
- "Intel D965", STAC_D965_5ST),
- /* volume-knob fixes */
- SND_PCI_QUIRK_VENDOR(0x10cf, "FSC", STAC_927X_VOLKNOB),
- {} /* terminator */
-};
-
-static unsigned int ref9205_pin_configs[12] = {
- 0x40000100, 0x40000100, 0x01016011, 0x01014010,
- 0x01813122, 0x01a19021, 0x01019020, 0x40000100,
- 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030
-};
-
-/*
- STAC 9205 pin configs for
- 102801F1
- 102801F2
- 102801FC
- 102801FD
- 10280204
- 1028021F
- 10280228 (Dell Vostro 1500)
- 10280229 (Dell Vostro 1700)
-*/
-static unsigned int dell_9205_m42_pin_configs[12] = {
- 0x0321101F, 0x03A11020, 0x400003FA, 0x90170310,
- 0x400003FB, 0x400003FC, 0x400003FD, 0x40F000F9,
- 0x90A60330, 0x400003FF, 0x0144131F, 0x40C003FE,
-};
-
-/*
- STAC 9205 pin configs for
- 102801F9
- 102801FA
- 102801FE
- 102801FF (Dell Precision M4300)
- 10280206
- 10280200
- 10280201
-*/
-static unsigned int dell_9205_m43_pin_configs[12] = {
- 0x0321101f, 0x03a11020, 0x90a70330, 0x90170310,
- 0x400000fe, 0x400000ff, 0x400000fd, 0x40f000f9,
- 0x400000fa, 0x400000fc, 0x0144131f, 0x40c003f8,
-};
-
-static unsigned int dell_9205_m44_pin_configs[12] = {
- 0x0421101f, 0x04a11020, 0x400003fa, 0x90170310,
- 0x400003fb, 0x400003fc, 0x400003fd, 0x400003f9,
- 0x90a60330, 0x400003ff, 0x01441340, 0x40c003fe,
-};
-
-static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = {
- [STAC_9205_REF] = ref9205_pin_configs,
- [STAC_9205_DELL_M42] = dell_9205_m42_pin_configs,
- [STAC_9205_DELL_M43] = dell_9205_m43_pin_configs,
- [STAC_9205_DELL_M44] = dell_9205_m44_pin_configs,
- [STAC_9205_EAPD] = NULL,
-};
-
-static const char *stac9205_models[STAC_9205_MODELS] = {
- [STAC_9205_AUTO] = "auto",
- [STAC_9205_REF] = "ref",
- [STAC_9205_DELL_M42] = "dell-m42",
- [STAC_9205_DELL_M43] = "dell-m43",
- [STAC_9205_DELL_M44] = "dell-m44",
- [STAC_9205_EAPD] = "eapd",
-};
-
-static struct snd_pci_quirk stac9205_cfg_tbl[] = {
- /* SigmaTel reference board */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
- "DFI LanParty", STAC_9205_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xfb30,
- "SigmaTel", STAC_9205_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
- "DFI LanParty", STAC_9205_REF),
- /* Dell */
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1,
- "unknown Dell", STAC_9205_DELL_M42),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2,
- "unknown Dell", STAC_9205_DELL_M42),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f8,
- "Dell Precision", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f9,
- "Dell Precision", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fa,
- "Dell Precision", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc,
- "unknown Dell", STAC_9205_DELL_M42),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd,
- "unknown Dell", STAC_9205_DELL_M42),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fe,
- "Dell Precision", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ff,
- "Dell Precision M4300", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0204,
- "unknown Dell", STAC_9205_DELL_M42),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206,
- "Dell Precision", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021b,
- "Dell Precision", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021c,
- "Dell Precision", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f,
- "Dell Inspiron", STAC_9205_DELL_M44),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228,
- "Dell Vostro 1500", STAC_9205_DELL_M42),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0229,
- "Dell Vostro 1700", STAC_9205_DELL_M42),
- /* Gateway */
- SND_PCI_QUIRK(0x107b, 0x0560, "Gateway T6834c", STAC_9205_EAPD),
- SND_PCI_QUIRK(0x107b, 0x0565, "Gateway T1616", STAC_9205_EAPD),
- {} /* terminator */
-};
-
-static void stac92xx_set_config_regs(struct hda_codec *codec,
- unsigned int *pincfgs)
-{
- int i;
- struct sigmatel_spec *spec = codec->spec;
-
- if (!pincfgs)
- return;
-
- for (i = 0; i < spec->num_pins; i++)
- if (spec->pin_nids[i] && pincfgs[i])
- snd_hda_codec_set_pincfg(codec, spec->pin_nids[i],
- pincfgs[i]);
-}
-
-/*
- * Analog playback callbacks
- */
-static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- if (spec->stream_delay)
- msleep(spec->stream_delay);
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream);
-}
-
-static int stac92xx_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Digital playback callbacks
- */
-static int stac92xx_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int stac92xx_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int stac92xx_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int stac92xx_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
-}
-
-
-/*
- * Analog capture callbacks
- */
-static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid = spec->adc_nids[substream->number];
-
- if (spec->powerdown_adcs) {
- msleep(40);
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- }
- snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
- return 0;
-}
-
-static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid = spec->adc_nids[substream->number];
-
- snd_hda_codec_cleanup_stream(codec, nid);
- if (spec->powerdown_adcs)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- return 0;
-}
-
-static struct hda_pcm_stream stac92xx_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in stac92xx_build_pcms */
- .ops = {
- .open = stac92xx_dig_playback_pcm_open,
- .close = stac92xx_dig_playback_pcm_close,
- .prepare = stac92xx_dig_playback_pcm_prepare,
- .cleanup = stac92xx_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream stac92xx_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in stac92xx_build_pcms */
-};
-
-static struct hda_pcm_stream stac92xx_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- .nid = 0x02, /* NID to query formats and rates */
- .ops = {
- .open = stac92xx_playback_pcm_open,
- .prepare = stac92xx_playback_pcm_prepare,
- .cleanup = stac92xx_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream stac92xx_pcm_analog_alt_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x06, /* NID to query formats and rates */
- .ops = {
- .open = stac92xx_playback_pcm_open,
- .prepare = stac92xx_playback_pcm_prepare,
- .cleanup = stac92xx_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream stac92xx_pcm_analog_capture = {
- .channels_min = 2,
- .channels_max = 2,
- /* NID + .substreams is set in stac92xx_build_pcms */
- .ops = {
- .prepare = stac92xx_capture_pcm_prepare,
- .cleanup = stac92xx_capture_pcm_cleanup
- },
-};
-
-static int stac92xx_build_pcms(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = "STAC92xx Analog";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->multiout.dac_nids[0];
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adcs;
-
- if (spec->alt_switch) {
- codec->num_pcms++;
- info++;
- info->name = "STAC92xx Analog Alt";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_alt_playback;
- }
-
- if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
- codec->num_pcms++;
- info++;
- info->name = "STAC92xx Digital";
- info->pcm_type = spec->autocfg.dig_out_type[0];
- if (spec->multiout.dig_out_nid) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
- }
- if (spec->dig_in_nid) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
- }
- }
-
- return 0;
-}
-
-static unsigned int stac92xx_get_default_vref(struct hda_codec *codec,
- hda_nid_t nid)
-{
- unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
- pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
- if (pincap & AC_PINCAP_VREF_100)
- return AC_PINCTL_VREF_100;
- if (pincap & AC_PINCAP_VREF_80)
- return AC_PINCTL_VREF_80;
- if (pincap & AC_PINCAP_VREF_50)
- return AC_PINCTL_VREF_50;
- if (pincap & AC_PINCAP_VREF_GRD)
- return AC_PINCTL_VREF_GRD;
- return 0;
-}
-
-static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
-
-{
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
-}
-
-#define stac92xx_hp_switch_info snd_ctl_boolean_mono_info
-
-static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
-
- ucontrol->value.integer.value[0] = !!spec->hp_switch;
- return 0;
-}
-
-static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid);
-
-static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- int nid = kcontrol->private_value;
-
- spec->hp_switch = ucontrol->value.integer.value[0] ? nid : 0;
-
- /* check to be sure that the ports are upto date with
- * switch changes
- */
- stac_issue_unsol_event(codec, nid);
-
- return 1;
-}
-
-static int stac92xx_dc_bias_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- int i;
- static char *texts[] = {
- "Mic In", "Line In", "Line Out"
- };
-
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid = kcontrol->private_value;
-
- if (nid == spec->mic_switch || nid == spec->line_switch)
- i = 3;
- else
- i = 2;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->value.enumerated.items = i;
- uinfo->count = 1;
- if (uinfo->value.enumerated.item >= i)
- uinfo->value.enumerated.item = i-1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
- return 0;
-}
-
-static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
- unsigned int vref = stac92xx_vref_get(codec, nid);
-
- if (vref == stac92xx_get_default_vref(codec, nid))
- ucontrol->value.enumerated.item[0] = 0;
- else if (vref == AC_PINCTL_VREF_GRD)
- ucontrol->value.enumerated.item[0] = 1;
- else if (vref == AC_PINCTL_VREF_HIZ)
- ucontrol->value.enumerated.item[0] = 2;
-
- return 0;
-}
-
-static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int new_vref = 0;
- int error;
- hda_nid_t nid = kcontrol->private_value;
-
- if (ucontrol->value.enumerated.item[0] == 0)
- new_vref = stac92xx_get_default_vref(codec, nid);
- else if (ucontrol->value.enumerated.item[0] == 1)
- new_vref = AC_PINCTL_VREF_GRD;
- else if (ucontrol->value.enumerated.item[0] == 2)
- new_vref = AC_PINCTL_VREF_HIZ;
- else
- return 0;
-
- if (new_vref != stac92xx_vref_get(codec, nid)) {
- error = stac92xx_vref_set(codec, nid, new_vref);
- return error;
- }
-
- return 0;
-}
-
-static int stac92xx_io_switch_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static char *texts[2];
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
-
- if (kcontrol->private_value == spec->line_switch)
- texts[0] = "Line In";
- else
- texts[0] = "Mic In";
- texts[1] = "Line Out";
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->value.enumerated.items = 2;
- uinfo->count = 1;
-
- if (uinfo->value.enumerated.item >= 2)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
- return 0;
-}
-
-static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid = kcontrol->private_value;
- int io_idx = (nid == spec->mic_switch) ? 1 : 0;
-
- ucontrol->value.enumerated.item[0] = spec->io_switch[io_idx];
- return 0;
-}
-
-static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid = kcontrol->private_value;
- int io_idx = (nid == spec->mic_switch) ? 1 : 0;
- unsigned short val = !!ucontrol->value.enumerated.item[0];
-
- spec->io_switch[io_idx] = val;
-
- if (val)
- stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
- else {
- unsigned int pinctl = AC_PINCTL_IN_EN;
- if (io_idx) /* set VREF for mic */
- pinctl |= stac92xx_get_default_vref(codec, nid);
- stac92xx_auto_set_pinctl(codec, nid, pinctl);
- }
-
- /* check the auto-mute again: we need to mute/unmute the speaker
- * appropriately according to the pin direction
- */
- if (spec->hp_detect)
- stac_issue_unsol_event(codec, nid);
-
- return 1;
-}
-
-#define stac92xx_clfe_switch_info snd_ctl_boolean_mono_info
-
-static int stac92xx_clfe_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
-
- ucontrol->value.integer.value[0] = spec->clfe_swap;
- return 0;
-}
-
-static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid = kcontrol->private_value & 0xff;
- unsigned int val = !!ucontrol->value.integer.value[0];
-
- if (spec->clfe_swap == val)
- return 0;
-
- spec->clfe_swap = val;
-
- snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
- spec->clfe_swap ? 0x4 : 0x0);
-
- return 1;
-}
-
-#define STAC_CODEC_HP_SWITCH(xname) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = 0, \
- .info = stac92xx_hp_switch_info, \
- .get = stac92xx_hp_switch_get, \
- .put = stac92xx_hp_switch_put, \
- }
-
-#define STAC_CODEC_IO_SWITCH(xname, xpval) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = 0, \
- .info = stac92xx_io_switch_info, \
- .get = stac92xx_io_switch_get, \
- .put = stac92xx_io_switch_put, \
- .private_value = xpval, \
- }
-
-#define STAC_CODEC_CLFE_SWITCH(xname, xpval) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .index = 0, \
- .info = stac92xx_clfe_switch_info, \
- .get = stac92xx_clfe_switch_get, \
- .put = stac92xx_clfe_switch_put, \
- .private_value = xpval, \
- }
-
-enum {
- STAC_CTL_WIDGET_VOL,
- STAC_CTL_WIDGET_MUTE,
- STAC_CTL_WIDGET_MUTE_BEEP,
- STAC_CTL_WIDGET_MONO_MUX,
- STAC_CTL_WIDGET_HP_SWITCH,
- STAC_CTL_WIDGET_IO_SWITCH,
- STAC_CTL_WIDGET_CLFE_SWITCH,
- STAC_CTL_WIDGET_DC_BIAS
-};
-
-static struct snd_kcontrol_new stac92xx_control_templates[] = {
- HDA_CODEC_VOLUME(NULL, 0, 0, 0),
- HDA_CODEC_MUTE(NULL, 0, 0, 0),
- HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0),
- STAC_MONO_MUX,
- STAC_CODEC_HP_SWITCH(NULL),
- STAC_CODEC_IO_SWITCH(NULL, 0),
- STAC_CODEC_CLFE_SWITCH(NULL, 0),
- DC_BIAS(NULL, 0, 0),
-};
-
-/* add dynamic controls */
-static struct snd_kcontrol_new *
-stac_control_new(struct sigmatel_spec *spec,
- struct snd_kcontrol_new *ktemp,
- const char *name,
- unsigned int subdev)
-{
- struct snd_kcontrol_new *knew;
-
- snd_array_init(&spec->kctls, sizeof(*knew), 32);
- knew = snd_array_new(&spec->kctls);
- if (!knew)
- return NULL;
- *knew = *ktemp;
- knew->name = kstrdup(name, GFP_KERNEL);
- if (!knew->name) {
- /* roolback */
- memset(knew, 0, sizeof(*knew));
- spec->kctls.alloced--;
- return NULL;
- }
- knew->subdevice = subdev;
- return knew;
-}
-
-static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
- struct snd_kcontrol_new *ktemp,
- int idx, const char *name,
- unsigned long val)
-{
- struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name,
- HDA_SUBDEV_AMP_FLAG);
- if (!knew)
- return -ENOMEM;
- knew->index = idx;
- knew->private_value = val;
- return 0;
-}
-
-static inline int stac92xx_add_control_idx(struct sigmatel_spec *spec,
- int type, int idx, const char *name,
- unsigned long val)
-{
- return stac92xx_add_control_temp(spec,
- &stac92xx_control_templates[type],
- idx, name, val);
-}
-
-
-/* add dynamic controls */
-static inline int stac92xx_add_control(struct sigmatel_spec *spec, int type,
- const char *name, unsigned long val)
-{
- return stac92xx_add_control_idx(spec, type, 0, name, val);
-}
-
-static struct snd_kcontrol_new stac_input_src_temp = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Input Source",
- .info = stac92xx_mux_enum_info,
- .get = stac92xx_mux_enum_get,
- .put = stac92xx_mux_enum_put,
-};
-
-static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
- hda_nid_t nid, int idx)
-{
- int def_conf = snd_hda_codec_get_pincfg(codec, nid);
- int control = 0;
- struct sigmatel_spec *spec = codec->spec;
- char name[22];
-
- if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
- if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
- && nid == spec->line_switch)
- control = STAC_CTL_WIDGET_IO_SWITCH;
- else if (snd_hda_query_pin_caps(codec, nid)
- & (AC_PINCAP_VREF_GRD << AC_PINCAP_VREF_SHIFT))
- control = STAC_CTL_WIDGET_DC_BIAS;
- else if (nid == spec->mic_switch)
- control = STAC_CTL_WIDGET_IO_SWITCH;
- }
-
- if (control) {
- strcpy(name, hda_get_input_pin_label(codec, nid, 1));
- return stac92xx_add_control(codec->spec, control,
- strcat(name, " Jack Mode"), nid);
- }
-
- return 0;
-}
-
-static int stac92xx_add_input_source(struct sigmatel_spec *spec)
-{
- struct snd_kcontrol_new *knew;
- struct hda_input_mux *imux = &spec->private_imux;
-
- if (spec->auto_mic)
- return 0; /* no need for input source */
- if (!spec->num_adcs || imux->num_items <= 1)
- return 0; /* no need for input source control */
- knew = stac_control_new(spec, &stac_input_src_temp,
- stac_input_src_temp.name, 0);
- if (!knew)
- return -ENOMEM;
- knew->count = spec->num_adcs;
- return 0;
-}
-
-/* check whether the line-input can be used as line-out */
-static hda_nid_t check_line_out_switch(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
- unsigned int pincap;
- int i;
-
- if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
- return 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- if (cfg->inputs[i].type == AUTO_PIN_LINE_IN) {
- nid = cfg->inputs[i].pin;
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_OUT)
- return nid;
- }
- }
- return 0;
-}
-
-static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid);
-
-/* check whether the mic-input can be used as line-out */
-static hda_nid_t check_mic_out_switch(struct hda_codec *codec, hda_nid_t *dac)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int def_conf, pincap;
- int i;
-
- *dac = 0;
- if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
- return 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (cfg->inputs[i].type != AUTO_PIN_MIC)
- continue;
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- /* some laptops have an internal analog microphone
- * which can't be used as a output */
- if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_OUT) {
- *dac = get_unassigned_dac(codec, nid);
- if (*dac)
- return nid;
- }
- }
- }
- return 0;
-}
-
-static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
-{
- int i;
-
- for (i = 0; i < spec->multiout.num_dacs; i++) {
- if (spec->multiout.dac_nids[i] == nid)
- return 1;
- }
-
- return 0;
-}
-
-static int check_all_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
-{
- int i;
- if (is_in_dac_nids(spec, nid))
- return 1;
- for (i = 0; i < spec->autocfg.hp_outs; i++)
- if (spec->hp_dacs[i] == nid)
- return 1;
- for (i = 0; i < spec->autocfg.speaker_outs; i++)
- if (spec->speaker_dacs[i] == nid)
- return 1;
- return 0;
-}
-
-static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid)
-{
- struct sigmatel_spec *spec = codec->spec;
- int j, conn_len;
- hda_nid_t conn[HDA_MAX_CONNECTIONS];
- unsigned int wcaps, wtype;
-
- conn_len = snd_hda_get_connections(codec, nid, conn,
- HDA_MAX_CONNECTIONS);
- /* 92HD88: trace back up the link of nids to find the DAC */
- while (conn_len == 1 && (get_wcaps_type(get_wcaps(codec, conn[0]))
- != AC_WID_AUD_OUT)) {
- nid = conn[0];
- conn_len = snd_hda_get_connections(codec, nid, conn,
- HDA_MAX_CONNECTIONS);
- }
- for (j = 0; j < conn_len; j++) {
- wcaps = get_wcaps(codec, conn[j]);
- wtype = get_wcaps_type(wcaps);
- /* we check only analog outputs */
- if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL))
- continue;
- /* if this route has a free DAC, assign it */
- if (!check_all_dac_nids(spec, conn[j])) {
- if (conn_len > 1) {
- /* select this DAC in the pin's input mux */
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, j);
- }
- return conn[j];
- }
- }
- /* if all DACs are already assigned, connect to the primary DAC */
- if (conn_len > 1) {
- for (j = 0; j < conn_len; j++) {
- if (conn[j] == spec->multiout.dac_nids[0]) {
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, j);
- break;
- }
- }
- }
- return 0;
-}
-
-static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid);
-static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid);
-
-/*
- * Fill in the dac_nids table from the parsed pin configuration
- * This function only works when every pin in line_out_pins[]
- * contains atleast one DAC in its connection list. Some 92xx
- * codecs are not connected directly to a DAC, such as the 9200
- * and 9202/925x. For those, dac_nids[] must be hard-coded.
- */
-static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
- hda_nid_t nid, dac;
-
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- dac = get_unassigned_dac(codec, nid);
- if (!dac) {
- if (spec->multiout.num_dacs > 0) {
- /* we have already working output pins,
- * so let's drop the broken ones again
- */
- cfg->line_outs = spec->multiout.num_dacs;
- break;
- }
- /* error out, no available DAC found */
- snd_printk(KERN_ERR
- "%s: No available DAC for pin 0x%x\n",
- __func__, nid);
- return -ENODEV;
- }
- add_spec_dacs(spec, dac);
- }
-
- for (i = 0; i < cfg->hp_outs; i++) {
- nid = cfg->hp_pins[i];
- dac = get_unassigned_dac(codec, nid);
- if (dac) {
- if (!spec->multiout.hp_nid)
- spec->multiout.hp_nid = dac;
- else
- add_spec_extra_dacs(spec, dac);
- }
- spec->hp_dacs[i] = dac;
- }
-
- for (i = 0; i < cfg->speaker_outs; i++) {
- nid = cfg->speaker_pins[i];
- dac = get_unassigned_dac(codec, nid);
- if (dac)
- add_spec_extra_dacs(spec, dac);
- spec->speaker_dacs[i] = dac;
- }
-
- /* add line-in as output */
- nid = check_line_out_switch(codec);
- if (nid) {
- dac = get_unassigned_dac(codec, nid);
- if (dac) {
- snd_printdd("STAC: Add line-in 0x%x as output %d\n",
- nid, cfg->line_outs);
- cfg->line_out_pins[cfg->line_outs] = nid;
- cfg->line_outs++;
- spec->line_switch = nid;
- add_spec_dacs(spec, dac);
- }
- }
- /* add mic as output */
- nid = check_mic_out_switch(codec, &dac);
- if (nid && dac) {
- snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
- nid, cfg->line_outs);
- cfg->line_out_pins[cfg->line_outs] = nid;
- cfg->line_outs++;
- spec->mic_switch = nid;
- add_spec_dacs(spec, dac);
- }
-
- snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
- spec->multiout.num_dacs,
- spec->multiout.dac_nids[0],
- spec->multiout.dac_nids[1],
- spec->multiout.dac_nids[2],
- spec->multiout.dac_nids[3],
- spec->multiout.dac_nids[4]);
-
- return 0;
-}
-
-/* create volume control/switch for the given prefx type */
-static int create_controls_idx(struct hda_codec *codec, const char *pfx,
- int idx, hda_nid_t nid, int chs)
-{
- struct sigmatel_spec *spec = codec->spec;
- char name[32];
- int err;
-
- if (!spec->check_volume_offset) {
- unsigned int caps, step, nums, db_scale;
- caps = query_amp_caps(codec, nid, HDA_OUTPUT);
- step = (caps & AC_AMPCAP_STEP_SIZE) >>
- AC_AMPCAP_STEP_SIZE_SHIFT;
- step = (step + 1) * 25; /* in .01dB unit */
- nums = (caps & AC_AMPCAP_NUM_STEPS) >>
- AC_AMPCAP_NUM_STEPS_SHIFT;
- db_scale = nums * step;
- /* if dB scale is over -64dB, and finer enough,
- * let's reduce it to half
- */
- if (db_scale > 6400 && nums >= 0x1f)
- spec->volume_offset = nums / 2;
- spec->check_volume_offset = 1;
- }
-
- sprintf(name, "%s Playback Volume", pfx);
- err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, idx, name,
- HDA_COMPOSE_AMP_VAL_OFS(nid, chs, 0, HDA_OUTPUT,
- spec->volume_offset));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", pfx);
- err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_MUTE, idx, name,
- HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- return 0;
-}
-
-#define create_controls(codec, pfx, nid, chs) \
- create_controls_idx(codec, pfx, 0, nid, chs)
-
-static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
-{
- if (spec->multiout.num_dacs > 4) {
- printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
- return 1;
- } else {
- spec->multiout.dac_nids[spec->multiout.num_dacs] = nid;
- spec->multiout.num_dacs++;
- }
- return 0;
-}
-
-static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) {
- if (!spec->multiout.extra_out_nid[i]) {
- spec->multiout.extra_out_nid[i] = nid;
- return 0;
- }
- }
- printk(KERN_WARNING "stac92xx: No space for extra DAC 0x%x\n", nid);
- return 1;
-}
-
-/* Create output controls
- * The mixer elements are named depending on the given type (AUTO_PIN_XXX_OUT)
- */
-static int create_multi_out_ctls(struct hda_codec *codec, int num_outs,
- const hda_nid_t *pins,
- const hda_nid_t *dac_nids,
- int type)
-{
- struct sigmatel_spec *spec = codec->spec;
- static const char *chname[4] = {
- "Front", "Surround", NULL /*CLFE*/, "Side"
- };
- hda_nid_t nid;
- int i, err;
- unsigned int wid_caps;
-
- for (i = 0; i < num_outs && i < ARRAY_SIZE(chname); i++) {
- if (type == AUTO_PIN_HP_OUT && !spec->hp_detect) {
- wid_caps = get_wcaps(codec, pins[i]);
- if (wid_caps & AC_WCAP_UNSOL_CAP)
- spec->hp_detect = 1;
- }
- nid = dac_nids[i];
- if (!nid)
- continue;
- if (type != AUTO_PIN_HP_OUT && i == 2) {
- /* Center/LFE */
- err = create_controls(codec, "Center", nid, 1);
- if (err < 0)
- return err;
- err = create_controls(codec, "LFE", nid, 2);
- if (err < 0)
- return err;
-
- wid_caps = get_wcaps(codec, nid);
-
- if (wid_caps & AC_WCAP_LR_SWAP) {
- err = stac92xx_add_control(spec,
- STAC_CTL_WIDGET_CLFE_SWITCH,
- "Swap Center/LFE Playback Switch", nid);
-
- if (err < 0)
- return err;
- }
-
- } else {
- const char *name;
- int idx;
- switch (type) {
- case AUTO_PIN_HP_OUT:
- name = "Headphone";
- idx = i;
- break;
- case AUTO_PIN_SPEAKER_OUT:
- name = "Speaker";
- idx = i;
- break;
- default:
- name = chname[i];
- idx = 0;
- break;
- }
- err = create_controls_idx(codec, name, idx, nid, 3);
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-static int stac92xx_add_capvol_ctls(struct hda_codec *codec, unsigned long vol,
- unsigned long sw, int idx)
-{
- int err;
- err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx,
- "Capture Volume", vol);
- if (err < 0)
- return err;
- err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_MUTE, idx,
- "Capture Switch", sw);
- if (err < 0)
- return err;
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid;
- int err;
- int idx;
-
- err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins,
- spec->multiout.dac_nids,
- cfg->line_out_type);
- if (err < 0)
- return err;
-
- if (cfg->hp_outs > 1 && cfg->line_out_type == AUTO_PIN_LINE_OUT) {
- err = stac92xx_add_control(spec,
- STAC_CTL_WIDGET_HP_SWITCH,
- "Headphone as Line Out Switch",
- cfg->hp_pins[cfg->hp_outs - 1]);
- if (err < 0)
- return err;
- }
-
- for (idx = 0; idx < cfg->num_inputs; idx++) {
- if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN)
- break;
- nid = cfg->inputs[idx].pin;
- err = stac92xx_add_jack_mode_control(codec, nid, idx);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-/* add playback controls for Speaker and HP outputs */
-static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
- struct auto_pin_cfg *cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- int err;
-
- err = create_multi_out_ctls(codec, cfg->hp_outs, cfg->hp_pins,
- spec->hp_dacs, AUTO_PIN_HP_OUT);
- if (err < 0)
- return err;
-
- err = create_multi_out_ctls(codec, cfg->speaker_outs, cfg->speaker_pins,
- spec->speaker_dacs, AUTO_PIN_SPEAKER_OUT);
- if (err < 0)
- return err;
-
- return 0;
-}
-
-/* labels for mono mux outputs */
-static const char *stac92xx_mono_labels[4] = {
- "DAC0", "DAC1", "Mixer", "DAC2"
-};
-
-/* create mono mux for mono out on capable codecs */
-static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct hda_input_mux *mono_mux = &spec->private_mono_mux;
- int i, num_cons;
- hda_nid_t con_lst[ARRAY_SIZE(stac92xx_mono_labels)];
-
- num_cons = snd_hda_get_connections(codec,
- spec->mono_nid,
- con_lst,
- HDA_MAX_NUM_INPUTS);
- if (num_cons <= 0 || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
- return -EINVAL;
-
- for (i = 0; i < num_cons; i++)
- snd_hda_add_imux_item(mono_mux, stac92xx_mono_labels[i], i,
- NULL);
-
- return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX,
- "Mono Mux", spec->mono_nid);
-}
-
-/* create PC beep volume controls */
-static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec,
- hda_nid_t nid)
-{
- struct sigmatel_spec *spec = codec->spec;
- u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT);
- int err, type = STAC_CTL_WIDGET_MUTE_BEEP;
-
- if (spec->anabeep_nid == nid)
- type = STAC_CTL_WIDGET_MUTE;
-
- /* check for mute support for the the amp */
- if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) {
- err = stac92xx_add_control(spec, type,
- "Beep Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- }
-
- /* check to see if there is volume support for the amp */
- if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
- err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL,
- "Beep Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-#define stac92xx_dig_beep_switch_info snd_ctl_boolean_mono_info
-
-static int stac92xx_dig_beep_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- ucontrol->value.integer.value[0] = codec->beep->enabled;
- return 0;
-}
-
-static int stac92xx_dig_beep_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]);
-}
-
-static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .info = stac92xx_dig_beep_switch_info,
- .get = stac92xx_dig_beep_switch_get,
- .put = stac92xx_dig_beep_switch_put,
-};
-
-static int stac92xx_beep_switch_ctl(struct hda_codec *codec)
-{
- return stac92xx_add_control_temp(codec->spec, &stac92xx_dig_beep_ctrl,
- 0, "Beep Playback Switch", 0);
-}
-#endif
-
-static int stac92xx_auto_create_mux_input_ctls(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i, j, err = 0;
-
- for (i = 0; i < spec->num_muxes; i++) {
- hda_nid_t nid;
- unsigned int wcaps;
- unsigned long val;
-
- nid = spec->mux_nids[i];
- wcaps = get_wcaps(codec, nid);
- if (!(wcaps & AC_WCAP_OUT_AMP))
- continue;
-
- /* check whether already the same control was created as
- * normal Capture Volume.
- */
- val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- for (j = 0; j < spec->num_caps; j++) {
- if (spec->capvols[j] == val)
- break;
- }
- if (j < spec->num_caps)
- continue;
-
- err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, i,
- "Mux Capture Volume", val);
- if (err < 0)
- return err;
- }
- return 0;
-};
-
-static const char *stac92xx_spdif_labels[3] = {
- "Digital Playback", "Analog Mux 1", "Analog Mux 2",
-};
-
-static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct hda_input_mux *spdif_mux = &spec->private_smux;
- const char **labels = spec->spdif_labels;
- int i, num_cons;
- hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
-
- num_cons = snd_hda_get_connections(codec,
- spec->smux_nids[0],
- con_lst,
- HDA_MAX_NUM_INPUTS);
- if (num_cons <= 0)
- return -EINVAL;
-
- if (!labels)
- labels = stac92xx_spdif_labels;
-
- for (i = 0; i < num_cons; i++)
- snd_hda_add_imux_item(spdif_mux, labels[i], i, NULL);
-
- return 0;
-}
-
-/* labels for dmic mux inputs */
-static const char *stac92xx_dmic_labels[5] = {
- "Analog Inputs", "Digital Mic 1", "Digital Mic 2",
- "Digital Mic 3", "Digital Mic 4"
-};
-
-static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
- hda_nid_t nid)
-{
- hda_nid_t conn[HDA_MAX_NUM_INPUTS];
- int i, nums;
-
- nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
- for (i = 0; i < nums; i++)
- if (conn[i] == nid)
- return i;
- return -1;
-}
-
-/* create a volume assigned to the given pin (only if supported) */
-/* return 1 if the volume control is created */
-static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
- const char *label, int idx, int direction)
-{
- unsigned int caps, nums;
- char name[32];
- int err;
-
- if (direction == HDA_OUTPUT)
- caps = AC_WCAP_OUT_AMP;
- else
- caps = AC_WCAP_IN_AMP;
- if (!(get_wcaps(codec, nid) & caps))
- return 0;
- caps = query_amp_caps(codec, nid, direction);
- nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
- if (!nums)
- return 0;
- snprintf(name, sizeof(name), "%s Capture Volume", label);
- err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
- if (err < 0)
- return err;
- return 1;
-}
-
-/* create playback/capture controls for input pins on dmic capable codecs */
-static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux;
- struct hda_input_mux *dimux = &spec->private_dimux;
- int err, i;
- unsigned int def_conf;
-
- snd_hda_add_imux_item(dimux, stac92xx_dmic_labels[0], 0, NULL);
-
- for (i = 0; i < spec->num_dmics; i++) {
- hda_nid_t nid;
- int index, type_idx;
- const char *label;
-
- nid = spec->dmic_nids[i];
- if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
- continue;
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
- continue;
-
- index = get_connection_index(codec, spec->dmux_nids[0], nid);
- if (index < 0)
- continue;
-
- label = hda_get_input_pin_label(codec, nid, 1);
- snd_hda_add_imux_item(dimux, label, index, &type_idx);
-
- err = create_elem_capture_vol(codec, nid, label, type_idx,
- HDA_INPUT);
- if (err < 0)
- return err;
- if (!err) {
- err = create_elem_capture_vol(codec, nid, label,
- type_idx, HDA_OUTPUT);
- if (err < 0)
- return err;
- }
-
- if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1) {
- snd_hda_add_imux_item(imux, label, index, NULL);
- spec->num_analog_muxes++;
- }
- }
-
- return 0;
-}
-
-static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t *fixed, hda_nid_t *ext, hda_nid_t *dock)
-{
- unsigned int cfg;
-
- if (!nid)
- return 0;
- cfg = snd_hda_codec_get_pincfg(codec, nid);
- switch (snd_hda_get_input_pin_attr(cfg)) {
- case INPUT_PIN_ATTR_INT:
- if (*fixed)
- return 1; /* already occupied */
- *fixed = nid;
- break;
- case INPUT_PIN_ATTR_UNUSED:
- break;
- case INPUT_PIN_ATTR_DOCK:
- if (*dock)
- return 1; /* already occupied */
- *dock = nid;
- break;
- default:
- if (*ext)
- return 1; /* already occupied */
- *ext = nid;
- break;
- }
- return 0;
-}
-
-static int set_mic_route(struct hda_codec *codec,
- struct sigmatel_mic_route *mic,
- hda_nid_t pin)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- mic->pin = pin;
- if (pin == 0)
- return 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- if (pin == cfg->inputs[i].pin)
- break;
- }
- if (i < cfg->num_inputs && cfg->inputs[i].type == AUTO_PIN_MIC) {
- /* analog pin */
- i = get_connection_index(codec, spec->mux_nids[0], pin);
- if (i < 0)
- return -1;
- mic->mux_idx = i;
- mic->dmux_idx = -1;
- if (spec->dmux_nids)
- mic->dmux_idx = get_connection_index(codec,
- spec->dmux_nids[0],
- spec->mux_nids[0]);
- } else if (spec->dmux_nids) {
- /* digital pin */
- i = get_connection_index(codec, spec->dmux_nids[0], pin);
- if (i < 0)
- return -1;
- mic->dmux_idx = i;
- mic->mux_idx = -1;
- if (spec->mux_nids)
- mic->mux_idx = get_connection_index(codec,
- spec->mux_nids[0],
- spec->dmux_nids[0]);
- }
- return 0;
-}
-
-/* return non-zero if the device is for automatic mic switch */
-static int stac_check_auto_mic(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t fixed, ext, dock;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
- return 0; /* must be exclusively mics */
- }
- fixed = ext = dock = 0;
- for (i = 0; i < cfg->num_inputs; i++)
- if (check_mic_pin(codec, cfg->inputs[i].pin,
- &fixed, &ext, &dock))
- return 0;
- for (i = 0; i < spec->num_dmics; i++)
- if (check_mic_pin(codec, spec->dmic_nids[i],
- &fixed, &ext, &dock))
- return 0;
- if (!fixed && !ext && !dock)
- return 0; /* no input to switch */
- if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
- return 0; /* no unsol support */
- if (set_mic_route(codec, &spec->ext_mic, ext) ||
- set_mic_route(codec, &spec->int_mic, fixed) ||
- set_mic_route(codec, &spec->dock_mic, dock))
- return 0; /* something is wrong */
- return 1;
-}
-
-/* create playback/capture controls for input pins */
-static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux;
- int i, j;
- const char *label;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- int index, err, type_idx;
-
- index = -1;
- for (j = 0; j < spec->num_muxes; j++) {
- index = get_connection_index(codec, spec->mux_nids[j],
- nid);
- if (index >= 0)
- break;
- }
- if (index < 0)
- continue;
-
- label = hda_get_autocfg_input_label(codec, cfg, i);
- snd_hda_add_imux_item(imux, label, index, &type_idx);
-
- err = create_elem_capture_vol(codec, nid,
- label, type_idx,
- HDA_INPUT);
- if (err < 0)
- return err;
- }
- spec->num_analog_muxes = imux->num_items;
-
- if (imux->num_items) {
- /*
- * Set the current input for the muxes.
- * The STAC9221 has two input muxes with identical source
- * NID lists. Hopefully this won't get confused.
- */
- for (i = 0; i < spec->num_muxes; i++) {
- snd_hda_codec_write_cache(codec, spec->mux_nids[i], 0,
- AC_VERB_SET_CONNECT_SEL,
- imux->items[0].index);
- }
- }
-
- return 0;
-}
-
-static void stac92xx_auto_init_multi_out(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.line_outs; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
- }
-}
-
-static void stac92xx_auto_init_hp_out(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.hp_outs; i++) {
- hda_nid_t pin;
- pin = spec->autocfg.hp_pins[i];
- if (pin) /* connect to front */
- stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
- }
- for (i = 0; i < spec->autocfg.speaker_outs; i++) {
- hda_nid_t pin;
- pin = spec->autocfg.speaker_pins[i];
- if (pin) /* connect to front */
- stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN);
- }
-}
-
-static int is_dual_headphones(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i, valid_hps;
-
- if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT ||
- spec->autocfg.hp_outs <= 1)
- return 0;
- valid_hps = 0;
- for (i = 0; i < spec->autocfg.hp_outs; i++) {
- hda_nid_t nid = spec->autocfg.hp_pins[i];
- unsigned int cfg = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_location(cfg) & AC_JACK_LOC_SEPARATE)
- continue;
- valid_hps++;
- }
- return (valid_hps > 1);
-}
-
-
-static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in)
-{
- struct sigmatel_spec *spec = codec->spec;
- int hp_swap = 0;
- int i, err;
-
- if ((err = snd_hda_parse_pin_def_config(codec,
- &spec->autocfg,
- spec->dmic_nids)) < 0)
- return err;
- if (! spec->autocfg.line_outs)
- return 0; /* can't find valid pin config */
-
- /* If we have no real line-out pin and multiple hp-outs, HPs should
- * be set up as multi-channel outputs.
- */
- if (is_dual_headphones(codec)) {
- /* Copy hp_outs to line_outs, backup line_outs in
- * speaker_outs so that the following routines can handle
- * HP pins as primary outputs.
- */
- snd_printdd("stac92xx: Enabling multi-HPs workaround\n");
- memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins,
- sizeof(spec->autocfg.line_out_pins));
- spec->autocfg.speaker_outs = spec->autocfg.line_outs;
- memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins,
- sizeof(spec->autocfg.hp_pins));
- spec->autocfg.line_outs = spec->autocfg.hp_outs;
- spec->autocfg.line_out_type = AUTO_PIN_HP_OUT;
- spec->autocfg.hp_outs = 0;
- hp_swap = 1;
- }
- if (spec->autocfg.mono_out_pin) {
- int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) &
- (AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP);
- u32 caps = query_amp_caps(codec,
- spec->autocfg.mono_out_pin, dir);
- hda_nid_t conn_list[1];
-
- /* get the mixer node and then the mono mux if it exists */
- if (snd_hda_get_connections(codec,
- spec->autocfg.mono_out_pin, conn_list, 1) &&
- snd_hda_get_connections(codec, conn_list[0],
- conn_list, 1) > 0) {
-
- int wcaps = get_wcaps(codec, conn_list[0]);
- int wid_type = get_wcaps_type(wcaps);
- /* LR swap check, some stac925x have a mux that
- * changes the DACs output path instead of the
- * mono-mux path.
- */
- if (wid_type == AC_WID_AUD_SEL &&
- !(wcaps & AC_WCAP_LR_SWAP))
- spec->mono_nid = conn_list[0];
- }
- if (dir) {
- hda_nid_t nid = spec->autocfg.mono_out_pin;
-
- /* most mono outs have a least a mute/unmute switch */
- dir = (dir & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT;
- err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
- "Mono Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir));
- if (err < 0)
- return err;
- /* check for volume support for the amp */
- if ((caps & AC_AMPCAP_NUM_STEPS)
- >> AC_AMPCAP_NUM_STEPS_SHIFT) {
- err = stac92xx_add_control(spec,
- STAC_CTL_WIDGET_VOL,
- "Mono Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir));
- if (err < 0)
- return err;
- }
- }
-
- stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin,
- AC_PINCTL_OUT_EN);
- }
-
- if (!spec->multiout.num_dacs) {
- err = stac92xx_auto_fill_dac_nids(codec);
- if (err < 0)
- return err;
- err = stac92xx_auto_create_multi_out_ctls(codec,
- &spec->autocfg);
- if (err < 0)
- return err;
- }
-
- /* setup analog beep controls */
- if (spec->anabeep_nid > 0) {
- err = stac92xx_auto_create_beep_ctls(codec,
- spec->anabeep_nid);
- if (err < 0)
- return err;
- }
-
- /* setup digital beep controls and input device */
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
- if (spec->digbeep_nid > 0) {
- hda_nid_t nid = spec->digbeep_nid;
- unsigned int caps;
-
- err = stac92xx_auto_create_beep_ctls(codec, nid);
- if (err < 0)
- return err;
- err = snd_hda_attach_beep_device(codec, nid);
- if (err < 0)
- return err;
- if (codec->beep) {
- /* IDT/STAC codecs have linear beep tone parameter */
- codec->beep->linear_tone = spec->linear_tone_beep;
- /* if no beep switch is available, make its own one */
- caps = query_amp_caps(codec, nid, HDA_OUTPUT);
- if (!(caps & AC_AMPCAP_MUTE)) {
- err = stac92xx_beep_switch_ctl(codec);
- if (err < 0)
- return err;
- }
- }
- }
-#endif
-
- err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- /* All output parsing done, now restore the swapped hp pins */
- if (hp_swap) {
- memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
- sizeof(spec->autocfg.hp_pins));
- spec->autocfg.hp_outs = spec->autocfg.line_outs;
- spec->autocfg.line_out_type = AUTO_PIN_HP_OUT;
- spec->autocfg.line_outs = 0;
- }
-
- if (stac_check_auto_mic(codec)) {
- spec->auto_mic = 1;
- /* only one capture for auto-mic */
- spec->num_adcs = 1;
- spec->num_caps = 1;
- spec->num_muxes = 1;
- }
-
- for (i = 0; i < spec->num_caps; i++) {
- err = stac92xx_add_capvol_ctls(codec, spec->capvols[i],
- spec->capsws[i], i);
- if (err < 0)
- return err;
- }
-
- err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- if (spec->mono_nid > 0) {
- err = stac92xx_auto_create_mono_output_ctls(codec);
- if (err < 0)
- return err;
- }
- if (spec->num_dmics > 0 && !spec->dinput_mux)
- if ((err = stac92xx_auto_create_dmic_input_ctls(codec,
- &spec->autocfg)) < 0)
- return err;
- if (spec->num_muxes > 0) {
- err = stac92xx_auto_create_mux_input_ctls(codec);
- if (err < 0)
- return err;
- }
- if (spec->num_smuxes > 0) {
- err = stac92xx_auto_create_spdif_mux_ctls(codec);
- if (err < 0)
- return err;
- }
-
- err = stac92xx_add_input_source(spec);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- if (spec->multiout.max_channels > 2)
- spec->surr_switch = 1;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = dig_out;
- if (dig_in && spec->autocfg.dig_in_pin)
- spec->dig_in_nid = dig_in;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux;
- if (!spec->dinput_mux)
- spec->dinput_mux = &spec->private_dimux;
- spec->sinput_mux = &spec->private_smux;
- spec->mono_mux = &spec->private_mono_mux;
- return 1;
-}
-
-/* add playback controls for HP output */
-static int stac9200_auto_create_hp_ctls(struct hda_codec *codec,
- struct auto_pin_cfg *cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- hda_nid_t pin = cfg->hp_pins[0];
- unsigned int wid_caps;
-
- if (! pin)
- return 0;
-
- wid_caps = get_wcaps(codec, pin);
- if (wid_caps & AC_WCAP_UNSOL_CAP)
- spec->hp_detect = 1;
-
- return 0;
-}
-
-/* add playback controls for LFE output */
-static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec,
- struct auto_pin_cfg *cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- int err;
- hda_nid_t lfe_pin = 0x0;
- int i;
-
- /*
- * search speaker outs and line outs for a mono speaker pin
- * with an amp. If one is found, add LFE controls
- * for it.
- */
- for (i = 0; i < spec->autocfg.speaker_outs && lfe_pin == 0x0; i++) {
- hda_nid_t pin = spec->autocfg.speaker_pins[i];
- unsigned int wcaps = get_wcaps(codec, pin);
- wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
- if (wcaps == AC_WCAP_OUT_AMP)
- /* found a mono speaker with an amp, must be lfe */
- lfe_pin = pin;
- }
-
- /* if speaker_outs is 0, then speakers may be in line_outs */
- if (lfe_pin == 0 && spec->autocfg.speaker_outs == 0) {
- for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) {
- hda_nid_t pin = spec->autocfg.line_out_pins[i];
- unsigned int defcfg;
- defcfg = snd_hda_codec_get_pincfg(codec, pin);
- if (get_defcfg_device(defcfg) == AC_JACK_SPEAKER) {
- unsigned int wcaps = get_wcaps(codec, pin);
- wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
- if (wcaps == AC_WCAP_OUT_AMP)
- /* found a mono speaker with an amp,
- must be lfe */
- lfe_pin = pin;
- }
- }
- }
-
- if (lfe_pin) {
- err = create_controls(codec, "LFE", lfe_pin, 1);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static int stac9200_parse_auto_config(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- int err;
-
- if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
- return err;
-
- if ((err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
- return err;
-
- if ((err = stac9200_auto_create_hp_ctls(codec, &spec->autocfg)) < 0)
- return err;
-
- if ((err = stac9200_auto_create_lfe_ctls(codec, &spec->autocfg)) < 0)
- return err;
-
- if (spec->num_muxes > 0) {
- err = stac92xx_auto_create_mux_input_ctls(codec);
- if (err < 0)
- return err;
- }
-
- err = stac92xx_add_input_source(spec);
- if (err < 0)
- return err;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = 0x05;
- if (spec->autocfg.dig_in_pin)
- spec->dig_in_nid = 0x04;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux;
- spec->dinput_mux = &spec->private_dimux;
-
- return 1;
-}
-
-/*
- * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a
- * funky external mute control using GPIO pins.
- */
-
-static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
- unsigned int dir_mask, unsigned int data)
-{
- unsigned int gpiostate, gpiomask, gpiodir;
-
- gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DATA, 0);
- gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask);
-
- gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_MASK, 0);
- gpiomask |= mask;
-
- gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DIRECTION, 0);
- gpiodir |= dir_mask;
-
- /* Configure GPIOx as CMOS */
- snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0);
-
- snd_hda_codec_write(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_MASK, gpiomask);
- snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */
-
- msleep(1);
-
- snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
-}
-
-#ifdef CONFIG_SND_HDA_INPUT_JACK
-static void stac92xx_free_jack_priv(struct snd_jack *jack)
-{
- struct sigmatel_jack *jacks = jack->private_data;
- jacks->nid = 0;
- jacks->jack = NULL;
-}
-#endif
-
-static int stac92xx_add_jack(struct hda_codec *codec,
- hda_nid_t nid, int type)
-{
-#ifdef CONFIG_SND_HDA_INPUT_JACK
- struct sigmatel_spec *spec = codec->spec;
- struct sigmatel_jack *jack;
- int def_conf = snd_hda_codec_get_pincfg(codec, nid);
- int connectivity = get_defcfg_connect(def_conf);
- char name[32];
- int err;
-
- if (connectivity && connectivity != AC_JACK_PORT_FIXED)
- return 0;
-
- snd_array_init(&spec->jacks, sizeof(*jack), 32);
- jack = snd_array_new(&spec->jacks);
- if (!jack)
- return -ENOMEM;
- jack->nid = nid;
- jack->type = type;
-
- snprintf(name, sizeof(name), "%s at %s %s Jack",
- snd_hda_get_jack_type(def_conf),
- snd_hda_get_jack_connectivity(def_conf),
- snd_hda_get_jack_location(def_conf));
-
- err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
- if (err < 0) {
- jack->nid = 0;
- return err;
- }
- jack->jack->private_data = jack;
- jack->jack->private_free = stac92xx_free_jack_priv;
-#endif
- return 0;
-}
-
-static int stac_add_event(struct sigmatel_spec *spec, hda_nid_t nid,
- unsigned char type, int data)
-{
- struct sigmatel_event *event;
-
- snd_array_init(&spec->events, sizeof(*event), 32);
- event = snd_array_new(&spec->events);
- if (!event)
- return -ENOMEM;
- event->nid = nid;
- event->type = type;
- event->tag = spec->events.used;
- event->data = data;
-
- return event->tag;
-}
-
-static struct sigmatel_event *stac_get_event(struct hda_codec *codec,
- hda_nid_t nid)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct sigmatel_event *event = spec->events.list;
- int i;
-
- for (i = 0; i < spec->events.used; i++, event++) {
- if (event->nid == nid)
- return event;
- }
- return NULL;
-}
-
-static struct sigmatel_event *stac_get_event_from_tag(struct hda_codec *codec,
- unsigned char tag)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct sigmatel_event *event = spec->events.list;
- int i;
-
- for (i = 0; i < spec->events.used; i++, event++) {
- if (event->tag == tag)
- return event;
- }
- return NULL;
-}
-
-/* check if given nid is a valid pin and no other events are assigned
- * to it. If OK, assign the event, set the unsol flag, and returns 1.
- * Otherwise, returns zero.
- */
-static int enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
- unsigned int type)
-{
- struct sigmatel_event *event;
- int tag;
-
- if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP))
- return 0;
- event = stac_get_event(codec, nid);
- if (event) {
- if (event->type != type)
- return 0;
- tag = event->tag;
- } else {
- tag = stac_add_event(codec->spec, nid, type, 0);
- if (tag < 0)
- return 0;
- }
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | tag);
- return 1;
-}
-
-static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
-{
- int i;
- for (i = 0; i < cfg->hp_outs; i++)
- if (cfg->hp_pins[i] == nid)
- return 1; /* nid is a HP-Out */
-
- return 0; /* nid is not a HP-Out */
-};
-
-static void stac92xx_power_down(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- /* power down inactive DACs */
- hda_nid_t *dac;
- for (dac = spec->dac_list; *dac; dac++)
- if (!check_all_dac_nids(spec, *dac))
- snd_hda_codec_write(codec, *dac, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
-}
-
-static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
- int enable);
-
-static inline int get_int_hint(struct hda_codec *codec, const char *key,
- int *valp)
-{
- const char *p;
- p = snd_hda_get_hint(codec, key);
- if (p) {
- unsigned long val;
- if (!strict_strtoul(p, 0, &val)) {
- *valp = val;
- return 1;
- }
- }
- return 0;
-}
-
-/* override some hints from the hwdep entry */
-static void stac_store_hints(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- int val;
-
- val = snd_hda_get_bool_hint(codec, "hp_detect");
- if (val >= 0)
- spec->hp_detect = val;
- if (get_int_hint(codec, "gpio_mask", &spec->gpio_mask)) {
- spec->eapd_mask = spec->gpio_dir = spec->gpio_data =
- spec->gpio_mask;
- }
- if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir))
- spec->gpio_mask &= spec->gpio_mask;
- if (get_int_hint(codec, "gpio_data", &spec->gpio_data))
- spec->gpio_dir &= spec->gpio_mask;
- if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask))
- spec->eapd_mask &= spec->gpio_mask;
- if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute))
- spec->gpio_mute &= spec->gpio_mask;
- val = snd_hda_get_bool_hint(codec, "eapd_switch");
- if (val >= 0)
- spec->eapd_switch = val;
- get_int_hint(codec, "gpio_led_polarity", &spec->gpio_led_polarity);
- if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
- spec->gpio_mask |= spec->gpio_led;
- spec->gpio_dir |= spec->gpio_led;
- if (spec->gpio_led_polarity)
- spec->gpio_data |= spec->gpio_led;
- }
-}
-
-static int stac92xx_init(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int gpio;
- int i;
-
- snd_hda_sequence_write(codec, spec->init);
-
- /* power down adcs initially */
- if (spec->powerdown_adcs)
- for (i = 0; i < spec->num_adcs; i++)
- snd_hda_codec_write(codec,
- spec->adc_nids[i], 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
-
- /* override some hints */
- stac_store_hints(codec);
-
- /* set up GPIO */
- gpio = spec->gpio_data;
- /* turn on EAPD statically when spec->eapd_switch isn't set.
- * otherwise, unsol event will turn it on/off dynamically
- */
- if (!spec->eapd_switch)
- gpio |= spec->eapd_mask;
- stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, gpio);
-
- /* set up pins */
- if (spec->hp_detect) {
- /* Enable unsolicited responses on the HP widget */
- for (i = 0; i < cfg->hp_outs; i++) {
- hda_nid_t nid = cfg->hp_pins[i];
- enable_pin_detect(codec, nid, STAC_HP_EVENT);
- }
- if (cfg->line_out_type == AUTO_PIN_LINE_OUT &&
- cfg->speaker_outs > 0) {
- /* enable pin-detect for line-outs as well */
- for (i = 0; i < cfg->line_outs; i++) {
- hda_nid_t nid = cfg->line_out_pins[i];
- enable_pin_detect(codec, nid, STAC_LO_EVENT);
- }
- }
-
- /* force to enable the first line-out; the others are set up
- * in unsol_event
- */
- stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0],
- AC_PINCTL_OUT_EN);
- /* fake event to set up pins */
- if (cfg->hp_pins[0])
- stac_issue_unsol_event(codec, cfg->hp_pins[0]);
- else if (cfg->line_out_pins[0])
- stac_issue_unsol_event(codec, cfg->line_out_pins[0]);
- } else {
- stac92xx_auto_init_multi_out(codec);
- stac92xx_auto_init_hp_out(codec);
- for (i = 0; i < cfg->hp_outs; i++)
- stac_toggle_power_map(codec, cfg->hp_pins[i], 1);
- }
- if (spec->auto_mic) {
- /* initialize connection to analog input */
- if (spec->dmux_nids)
- snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0,
- AC_VERB_SET_CONNECT_SEL, 0);
- if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT))
- stac_issue_unsol_event(codec, spec->ext_mic.pin);
- if (enable_pin_detect(codec, spec->dock_mic.pin,
- STAC_MIC_EVENT))
- stac_issue_unsol_event(codec, spec->dock_mic.pin);
- }
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- int type = cfg->inputs[i].type;
- unsigned int pinctl, conf;
- if (type == AUTO_PIN_MIC) {
- /* for mic pins, force to initialize */
- pinctl = stac92xx_get_default_vref(codec, nid);
- pinctl |= AC_PINCTL_IN_EN;
- stac92xx_auto_set_pinctl(codec, nid, pinctl);
- } else {
- pinctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- /* if PINCTL already set then skip */
- /* Also, if both INPUT and OUTPUT are set,
- * it must be a BIOS bug; need to override, too
- */
- if (!(pinctl & AC_PINCTL_IN_EN) ||
- (pinctl & AC_PINCTL_OUT_EN)) {
- pinctl &= ~AC_PINCTL_OUT_EN;
- pinctl |= AC_PINCTL_IN_EN;
- stac92xx_auto_set_pinctl(codec, nid, pinctl);
- }
- }
- conf = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
- if (enable_pin_detect(codec, nid, STAC_INSERT_EVENT))
- stac_issue_unsol_event(codec, nid);
- }
- }
- for (i = 0; i < spec->num_dmics; i++)
- stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
- AC_PINCTL_IN_EN);
- if (cfg->dig_out_pins[0])
- stac92xx_auto_set_pinctl(codec, cfg->dig_out_pins[0],
- AC_PINCTL_OUT_EN);
- if (cfg->dig_in_pin)
- stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin,
- AC_PINCTL_IN_EN);
- for (i = 0; i < spec->num_pwrs; i++) {
- hda_nid_t nid = spec->pwr_nids[i];
- int pinctl, def_conf;
-
- /* power on when no jack detection is available */
- if (!spec->hp_detect) {
- stac_toggle_power_map(codec, nid, 1);
- continue;
- }
-
- if (is_nid_hp_pin(cfg, nid))
- continue; /* already has an unsol event */
-
- pinctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- /* outputs are only ports capable of power management
- * any attempts on powering down a input port cause the
- * referenced VREF to act quirky.
- */
- if (pinctl & AC_PINCTL_IN_EN) {
- stac_toggle_power_map(codec, nid, 1);
- continue;
- }
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- def_conf = get_defcfg_connect(def_conf);
- /* skip any ports that don't have jacks since presence
- * detection is useless */
- if (def_conf != AC_JACK_PORT_COMPLEX) {
- if (def_conf != AC_JACK_PORT_NONE)
- stac_toggle_power_map(codec, nid, 1);
- continue;
- }
- if (enable_pin_detect(codec, nid, STAC_PWR_EVENT))
- stac_issue_unsol_event(codec, nid);
- }
-
- /* sync mute LED */
- if (spec->gpio_led)
- hda_call_check_power_status(codec, 0x01);
- if (spec->dac_list)
- stac92xx_power_down(codec);
- return 0;
-}
-
-static void stac92xx_free_jacks(struct hda_codec *codec)
-{
-#ifdef CONFIG_SND_HDA_INPUT_JACK
- /* free jack instances manually when clearing/reconfiguring */
- struct sigmatel_spec *spec = codec->spec;
- if (!codec->bus->shutdown && spec->jacks.list) {
- struct sigmatel_jack *jacks = spec->jacks.list;
- int i;
- for (i = 0; i < spec->jacks.used; i++, jacks++) {
- if (jacks->jack)
- snd_device_free(codec->bus->card, jacks->jack);
- }
- }
- snd_array_free(&spec->jacks);
-#endif
-}
-
-static void stac92xx_free_kctls(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (spec->kctls.list) {
- struct snd_kcontrol_new *kctl = spec->kctls.list;
- int i;
- for (i = 0; i < spec->kctls.used; i++)
- kfree(kctl[i].name);
- }
- snd_array_free(&spec->kctls);
-}
-
-static void stac92xx_shutup(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- snd_hda_shutup_pins(codec);
-
- if (spec->eapd_mask)
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data &
- ~spec->eapd_mask);
-}
-
-static void stac92xx_free(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (! spec)
- return;
-
- stac92xx_shutup(codec);
- stac92xx_free_jacks(codec);
- snd_array_free(&spec->events);
-
- kfree(spec);
- snd_hda_detach_beep_device(codec);
-}
-
-static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
- unsigned int flag)
-{
- unsigned int old_ctl, pin_ctl;
-
- pin_ctl = snd_hda_codec_read(codec, nid,
- 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
-
- if (pin_ctl & AC_PINCTL_IN_EN) {
- /*
- * we need to check the current set-up direction of
- * shared input pins since they can be switched via
- * "xxx as Output" mixer switch
- */
- struct sigmatel_spec *spec = codec->spec;
- if (nid == spec->line_switch || nid == spec->mic_switch)
- return;
- }
-
- old_ctl = pin_ctl;
- /* if setting pin direction bits, clear the current
- direction bits first */
- if (flag & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))
- pin_ctl &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
-
- pin_ctl |= flag;
- if (old_ctl != pin_ctl)
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_ctl);
-}
-
-static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
- unsigned int flag)
-{
- unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
- 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
- if (pin_ctl & flag)
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_ctl & ~flag);
-}
-
-static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
-{
- if (!nid)
- return 0;
- return snd_hda_jack_detect(codec, nid);
-}
-
-static void stac92xx_line_out_detect(struct hda_codec *codec,
- int presence)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->line_outs; i++) {
- if (presence)
- break;
- presence = get_pin_presence(codec, cfg->line_out_pins[i]);
- if (presence) {
- unsigned int pinctl;
- pinctl = snd_hda_codec_read(codec,
- cfg->line_out_pins[i], 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- if (pinctl & AC_PINCTL_IN_EN)
- presence = 0; /* mic- or line-input */
- }
- }
-
- if (presence) {
- /* disable speakers */
- for (i = 0; i < cfg->speaker_outs; i++)
- stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
- AC_PINCTL_OUT_EN);
- if (spec->eapd_mask && spec->eapd_switch)
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data &
- ~spec->eapd_mask);
- } else {
- /* enable speakers */
- for (i = 0; i < cfg->speaker_outs; i++)
- stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
- AC_PINCTL_OUT_EN);
- if (spec->eapd_mask && spec->eapd_switch)
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data |
- spec->eapd_mask);
- }
-}
-
-/* return non-zero if the hp-pin of the given array index isn't
- * a jack-detection target
- */
-static int no_hp_sensing(struct sigmatel_spec *spec, int i)
-{
- struct auto_pin_cfg *cfg = &spec->autocfg;
-
- /* ignore sensing of shared line and mic jacks */
- if (cfg->hp_pins[i] == spec->line_switch)
- return 1;
- if (cfg->hp_pins[i] == spec->mic_switch)
- return 1;
- /* ignore if the pin is set as line-out */
- if (cfg->hp_pins[i] == spec->hp_switch)
- return 1;
- return 0;
-}
-
-static void stac92xx_hp_detect(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, presence;
-
- presence = 0;
- if (spec->gpio_mute)
- presence = !(snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute);
-
- for (i = 0; i < cfg->hp_outs; i++) {
- if (presence)
- break;
- if (no_hp_sensing(spec, i))
- continue;
- presence = get_pin_presence(codec, cfg->hp_pins[i]);
- if (presence) {
- unsigned int pinctl;
- pinctl = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- if (pinctl & AC_PINCTL_IN_EN)
- presence = 0; /* mic- or line-input */
- }
- }
-
- if (presence) {
- /* disable lineouts */
- if (spec->hp_switch)
- stac92xx_reset_pinctl(codec, spec->hp_switch,
- AC_PINCTL_OUT_EN);
- for (i = 0; i < cfg->line_outs; i++)
- stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
- AC_PINCTL_OUT_EN);
- } else {
- /* enable lineouts */
- if (spec->hp_switch)
- stac92xx_set_pinctl(codec, spec->hp_switch,
- AC_PINCTL_OUT_EN);
- for (i = 0; i < cfg->line_outs; i++)
- stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
- AC_PINCTL_OUT_EN);
- }
- stac92xx_line_out_detect(codec, presence);
- /* toggle hp outs */
- for (i = 0; i < cfg->hp_outs; i++) {
- unsigned int val = AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN;
- if (no_hp_sensing(spec, i))
- continue;
- if (presence)
- stac92xx_set_pinctl(codec, cfg->hp_pins[i], val);
-#if 0 /* FIXME */
-/* Resetting the pinctl like below may lead to (a sort of) regressions
- * on some devices since they use the HP pin actually for line/speaker
- * outs although the default pin config shows a different pin (that is
- * wrong and useless).
- *
- * So, it's basically a problem of default pin configs, likely a BIOS issue.
- * But, disabling the code below just works around it, and I'm too tired of
- * bug reports with such devices...
- */
- else
- stac92xx_reset_pinctl(codec, cfg->hp_pins[i], val);
-#endif /* FIXME */
- }
-}
-
-static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
- int enable)
-{
- struct sigmatel_spec *spec = codec->spec;
- unsigned int idx, val;
-
- for (idx = 0; idx < spec->num_pwrs; idx++) {
- if (spec->pwr_nids[idx] == nid)
- break;
- }
- if (idx >= spec->num_pwrs)
- return;
-
- /* several codecs have two power down bits */
- if (spec->pwr_mapping)
- idx = spec->pwr_mapping[idx];
- else
- idx = 1 << idx;
-
- val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0) & 0xff;
- if (enable)
- val &= ~idx;
- else
- val |= idx;
-
- /* power down unused output ports */
- snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
-}
-
-static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid)
-{
- stac_toggle_power_map(codec, nid, get_pin_presence(codec, nid));
-}
-
-static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct sigmatel_jack *jacks = spec->jacks.list;
-
- if (jacks) {
- int i;
- for (i = 0; i < spec->jacks.used; i++) {
- if (jacks->nid == nid) {
- unsigned int pin_ctl =
- snd_hda_codec_read(codec, nid,
- 0, AC_VERB_GET_PIN_WIDGET_CONTROL,
- 0x00);
- int type = jacks->type;
- if (type == (SND_JACK_LINEOUT
- | SND_JACK_HEADPHONE))
- type = (pin_ctl & AC_PINCTL_HP_EN)
- ? SND_JACK_HEADPHONE : SND_JACK_LINEOUT;
- snd_jack_report(jacks->jack,
- get_pin_presence(codec, nid)
- ? type : 0);
- }
- jacks++;
- }
- }
-}
-
-/* get the pin connection (fixed, none, etc) */
-static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
-{
- struct sigmatel_spec *spec = codec->spec;
- unsigned int cfg;
-
- cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
- return get_defcfg_connect(cfg);
-}
-
-static int stac92xx_connected_ports(struct hda_codec *codec,
- hda_nid_t *nids, int num_nids)
-{
- struct sigmatel_spec *spec = codec->spec;
- int idx, num;
- unsigned int def_conf;
-
- for (num = 0; num < num_nids; num++) {
- for (idx = 0; idx < spec->num_pins; idx++)
- if (spec->pin_nids[idx] == nids[num])
- break;
- if (idx >= spec->num_pins)
- break;
- def_conf = stac_get_defcfg_connect(codec, idx);
- if (def_conf == AC_JACK_PORT_NONE)
- break;
- }
- return num;
-}
-
-static void stac92xx_mic_detect(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct sigmatel_mic_route *mic;
-
- if (get_pin_presence(codec, spec->ext_mic.pin))
- mic = &spec->ext_mic;
- else if (get_pin_presence(codec, spec->dock_mic.pin))
- mic = &spec->dock_mic;
- else
- mic = &spec->int_mic;
- if (mic->dmux_idx >= 0)
- snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0,
- AC_VERB_SET_CONNECT_SEL,
- mic->dmux_idx);
- if (mic->mux_idx >= 0)
- snd_hda_codec_write_cache(codec, spec->mux_nids[0], 0,
- AC_VERB_SET_CONNECT_SEL,
- mic->mux_idx);
-}
-
-static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid)
-{
- struct sigmatel_event *event = stac_get_event(codec, nid);
- if (!event)
- return;
- codec->patch_ops.unsol_event(codec, (unsigned)event->tag << 26);
-}
-
-static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct sigmatel_event *event;
- int tag, data;
-
- tag = (res >> 26) & 0x7f;
- event = stac_get_event_from_tag(codec, tag);
- if (!event)
- return;
-
- switch (event->type) {
- case STAC_HP_EVENT:
- case STAC_LO_EVENT:
- stac92xx_hp_detect(codec);
- break;
- case STAC_MIC_EVENT:
- stac92xx_mic_detect(codec);
- break;
- }
-
- switch (event->type) {
- case STAC_HP_EVENT:
- case STAC_LO_EVENT:
- case STAC_MIC_EVENT:
- case STAC_INSERT_EVENT:
- case STAC_PWR_EVENT:
- if (spec->num_pwrs > 0)
- stac92xx_pin_sense(codec, event->nid);
- stac92xx_report_jack(codec, event->nid);
-
- switch (codec->subsystem_id) {
- case 0x103c308f:
- if (event->nid == 0xb) {
- int pin = AC_PINCTL_IN_EN;
-
- if (get_pin_presence(codec, 0xa)
- && get_pin_presence(codec, 0xb))
- pin |= AC_PINCTL_VREF_80;
- if (!get_pin_presence(codec, 0xb))
- pin |= AC_PINCTL_VREF_80;
-
- /* toggle VREF state based on mic + hp pin
- * status
- */
- stac92xx_auto_set_pinctl(codec, 0x0a, pin);
- }
- }
- break;
- case STAC_VREF_EVENT:
- data = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DATA, 0);
- /* toggle VREF state based on GPIOx status */
- snd_hda_codec_write(codec, codec->afg, 0, 0x7e0,
- !!(data & (1 << event->data)));
- break;
- }
-}
-
-static int hp_blike_system(u32 subsystem_id);
-
-static void set_hp_led_gpio(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- unsigned int gpio;
-
- if (spec->gpio_led)
- return;
-
- gpio = snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP);
- gpio &= AC_GPIO_IO_COUNT;
- if (gpio > 3)
- spec->gpio_led = 0x08; /* GPIO 3 */
- else
- spec->gpio_led = 0x01; /* GPIO 0 */
-}
-
-/*
- * This method searches for the mute LED GPIO configuration
- * provided as OEM string in SMBIOS. The format of that string
- * is HP_Mute_LED_P_G or HP_Mute_LED_P
- * where P can be 0 or 1 and defines mute LED GPIO control state (low/high)
- * that corresponds to the NOT muted state of the master volume
- * and G is the index of the GPIO to use as the mute LED control (0..9)
- * If _G portion is missing it is assigned based on the codec ID
- *
- * So, HP B-series like systems may have HP_Mute_LED_0 (current models)
- * or HP_Mute_LED_0_3 (future models) OEM SMBIOS strings
- *
- *
- * The dv-series laptops don't seem to have the HP_Mute_LED* strings in
- * SMBIOS - at least the ones I have seen do not have them - which include
- * my own system (HP Pavilion dv6-1110ax) and my cousin's
- * HP Pavilion dv9500t CTO.
- * Need more information on whether it is true across the entire series.
- * -- kunal
- */
-static int find_mute_led_gpio(struct hda_codec *codec, int default_polarity)
-{
- struct sigmatel_spec *spec = codec->spec;
- const struct dmi_device *dev = NULL;
-
- if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) {
- while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,
- NULL, dev))) {
- if (sscanf(dev->name, "HP_Mute_LED_%d_%d",
- &spec->gpio_led_polarity,
- &spec->gpio_led) == 2) {
- spec->gpio_led = 1 << spec->gpio_led;
- return 1;
- }
- if (sscanf(dev->name, "HP_Mute_LED_%d",
- &spec->gpio_led_polarity) == 1) {
- set_hp_led_gpio(codec);
- return 1;
- }
- }
-
- /*
- * Fallback case - if we don't find the DMI strings,
- * we statically set the GPIO - if not a B-series system.
- */
- if (!hp_blike_system(codec->subsystem_id)) {
- set_hp_led_gpio(codec);
- spec->gpio_led_polarity = default_polarity;
- return 1;
- }
- }
- return 0;
-}
-
-static int hp_blike_system(u32 subsystem_id)
-{
- switch (subsystem_id) {
- case 0x103c1520:
- case 0x103c1521:
- case 0x103c1523:
- case 0x103c1524:
- case 0x103c1525:
- case 0x103c1722:
- case 0x103c1723:
- case 0x103c1724:
- case 0x103c1725:
- case 0x103c1726:
- case 0x103c1727:
- case 0x103c1728:
- case 0x103c1729:
- case 0x103c172a:
- case 0x103c172b:
- case 0x103c307e:
- case 0x103c307f:
- case 0x103c3080:
- case 0x103c3081:
- case 0x103c7007:
- case 0x103c7008:
- return 1;
- }
- return 0;
-}
-
-#ifdef CONFIG_PROC_FS
-static void stac92hd_proc_hook(struct snd_info_buffer *buffer,
- struct hda_codec *codec, hda_nid_t nid)
-{
- if (nid == codec->afg)
- snd_iprintf(buffer, "Power-Map: 0x%02x\n",
- snd_hda_codec_read(codec, nid, 0, 0x0fec, 0x0));
-}
-
-static void analog_loop_proc_hook(struct snd_info_buffer *buffer,
- struct hda_codec *codec,
- unsigned int verb)
-{
- snd_iprintf(buffer, "Analog Loopback: 0x%02x\n",
- snd_hda_codec_read(codec, codec->afg, 0, verb, 0));
-}
-
-/* stac92hd71bxx, stac92hd73xx */
-static void stac92hd7x_proc_hook(struct snd_info_buffer *buffer,
- struct hda_codec *codec, hda_nid_t nid)
-{
- stac92hd_proc_hook(buffer, codec, nid);
- if (nid == codec->afg)
- analog_loop_proc_hook(buffer, codec, 0xfa0);
-}
-
-static void stac9205_proc_hook(struct snd_info_buffer *buffer,
- struct hda_codec *codec, hda_nid_t nid)
-{
- if (nid == codec->afg)
- analog_loop_proc_hook(buffer, codec, 0xfe0);
-}
-
-static void stac927x_proc_hook(struct snd_info_buffer *buffer,
- struct hda_codec *codec, hda_nid_t nid)
-{
- if (nid == codec->afg)
- analog_loop_proc_hook(buffer, codec, 0xfeb);
-}
-#else
-#define stac92hd_proc_hook NULL
-#define stac92hd7x_proc_hook NULL
-#define stac9205_proc_hook NULL
-#define stac927x_proc_hook NULL
-#endif
-
-#ifdef SND_HDA_NEEDS_RESUME
-static int stac92xx_resume(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- stac92xx_init(codec);
- snd_hda_codec_resume_amp(codec);
- snd_hda_codec_resume_cache(codec);
- /* fake event to set up pins again to override cached values */
- if (spec->hp_detect) {
- if (spec->autocfg.hp_pins[0])
- stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0]);
- else if (spec->autocfg.line_out_pins[0])
- stac_issue_unsol_event(codec,
- spec->autocfg.line_out_pins[0]);
- }
- /* sync mute LED */
- if (spec->gpio_led)
- hda_call_check_power_status(codec, 0x01);
- return 0;
-}
-
-/*
- * using power check for controlling mute led of HP notebooks
- * check for mute state only on Speakers (nid = 0x10)
- *
- * For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise
- * the LED is NOT working properly !
- *
- * Changed name to reflect that it now works for any designated
- * model, not just HP HDX.
- */
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int stac92xx_hp_check_power_status(struct hda_codec *codec,
- hda_nid_t nid)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i, muted = 1;
-
- for (i = 0; i < spec->multiout.num_dacs; i++) {
- nid = spec->multiout.dac_nids[i];
- if (!(snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
- HDA_AMP_MUTE)) {
- muted = 0; /* something heard */
- break;
- }
- }
- if (muted)
- spec->gpio_data &= ~spec->gpio_led; /* orange */
- else
- spec->gpio_data |= spec->gpio_led; /* white */
-
- if (!spec->gpio_led_polarity) {
- /* LED state is inverted on these systems */
- spec->gpio_data ^= spec->gpio_led;
- }
-
- stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
- return 0;
-}
-#endif
-
-static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
-{
- stac92xx_shutup(codec);
- return 0;
-}
-#endif
-
-static struct hda_codec_ops stac92xx_patch_ops = {
- .build_controls = stac92xx_build_controls,
- .build_pcms = stac92xx_build_pcms,
- .init = stac92xx_init,
- .free = stac92xx_free,
- .unsol_event = stac92xx_unsol_event,
-#ifdef SND_HDA_NEEDS_RESUME
- .suspend = stac92xx_suspend,
- .resume = stac92xx_resume,
-#endif
- .reboot_notify = stac92xx_shutup,
-};
-
-static int patch_stac9200(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->no_trigger_sense = 1;
- codec->spec = spec;
- spec->linear_tone_beep = 1;
- spec->num_pins = ARRAY_SIZE(stac9200_pin_nids);
- spec->pin_nids = stac9200_pin_nids;
- spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
- stac9200_models,
- stac9200_cfg_tbl);
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac9200_brd_tbl[spec->board_config]);
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = stac9200_dac_nids;
- spec->adc_nids = stac9200_adc_nids;
- spec->mux_nids = stac9200_mux_nids;
- spec->num_muxes = 1;
- spec->num_dmics = 0;
- spec->num_adcs = 1;
- spec->num_pwrs = 0;
-
- if (spec->board_config == STAC_9200_M4 ||
- spec->board_config == STAC_9200_M4_2 ||
- spec->board_config == STAC_9200_OQO)
- spec->init = stac9200_eapd_init;
- else
- spec->init = stac9200_core_init;
- spec->mixer = stac9200_mixer;
-
- if (spec->board_config == STAC_9200_PANASONIC) {
- spec->gpio_mask = spec->gpio_dir = 0x09;
- spec->gpio_data = 0x00;
- }
-
- err = stac9200_parse_auto_config(codec);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
-
- /* CF-74 has no headphone detection, and the driver should *NOT*
- * do detection and HP/speaker toggle because the hardware does it.
- */
- if (spec->board_config == STAC_9200_PANASONIC)
- spec->hp_detect = 0;
-
- codec->patch_ops = stac92xx_patch_ops;
-
- return 0;
-}
-
-static int patch_stac925x(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->no_trigger_sense = 1;
- codec->spec = spec;
- spec->linear_tone_beep = 1;
- spec->num_pins = ARRAY_SIZE(stac925x_pin_nids);
- spec->pin_nids = stac925x_pin_nids;
-
- /* Check first for codec ID */
- spec->board_config = snd_hda_check_board_codec_sid_config(codec,
- STAC_925x_MODELS,
- stac925x_models,
- stac925x_codec_id_cfg_tbl);
-
- /* Now checks for PCI ID, if codec ID is not found */
- if (spec->board_config < 0)
- spec->board_config = snd_hda_check_board_config(codec,
- STAC_925x_MODELS,
- stac925x_models,
- stac925x_cfg_tbl);
- again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac925x_brd_tbl[spec->board_config]);
-
- spec->multiout.max_channels = 2;
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = stac925x_dac_nids;
- spec->adc_nids = stac925x_adc_nids;
- spec->mux_nids = stac925x_mux_nids;
- spec->num_muxes = 1;
- spec->num_adcs = 1;
- spec->num_pwrs = 0;
- switch (codec->vendor_id) {
- case 0x83847632: /* STAC9202 */
- case 0x83847633: /* STAC9202D */
- case 0x83847636: /* STAC9251 */
- case 0x83847637: /* STAC9251D */
- spec->num_dmics = STAC925X_NUM_DMICS;
- spec->dmic_nids = stac925x_dmic_nids;
- spec->num_dmuxes = ARRAY_SIZE(stac925x_dmux_nids);
- spec->dmux_nids = stac925x_dmux_nids;
- break;
- default:
- spec->num_dmics = 0;
- break;
- }
-
- spec->init = stac925x_core_init;
- spec->mixer = stac925x_mixer;
- spec->num_caps = 1;
- spec->capvols = stac925x_capvols;
- spec->capsws = stac925x_capsws;
-
- err = stac92xx_parse_auto_config(codec, 0x8, 0x7);
- if (!err) {
- if (spec->board_config < 0) {
- printk(KERN_WARNING "hda_codec: No auto-config is "
- "available, default to model=ref\n");
- spec->board_config = STAC_925x_REF;
- goto again;
- }
- err = -EINVAL;
- }
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
-
- codec->patch_ops = stac92xx_patch_ops;
-
- return 0;
-}
-
-static int patch_stac92hd73xx(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec;
- hda_nid_t conn[STAC92HD73_DAC_COUNT + 2];
- int err = 0;
- int num_dacs;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->no_trigger_sense = 1;
- codec->spec = spec;
- spec->linear_tone_beep = 0;
- codec->slave_dig_outs = stac92hd73xx_slave_dig_outs;
- spec->num_pins = ARRAY_SIZE(stac92hd73xx_pin_nids);
- spec->pin_nids = stac92hd73xx_pin_nids;
- spec->board_config = snd_hda_check_board_config(codec,
- STAC_92HD73XX_MODELS,
- stac92hd73xx_models,
- stac92hd73xx_cfg_tbl);
- /* check codec subsystem id if not found */
- if (spec->board_config < 0)
- spec->board_config =
- snd_hda_check_board_codec_sid_config(codec,
- STAC_92HD73XX_MODELS, stac92hd73xx_models,
- stac92hd73xx_codec_id_cfg_tbl);
-again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac92hd73xx_brd_tbl[spec->board_config]);
-
- num_dacs = snd_hda_get_connections(codec, 0x0a,
- conn, STAC92HD73_DAC_COUNT + 2) - 1;
-
- if (num_dacs < 3 || num_dacs > 5) {
- printk(KERN_WARNING "hda_codec: Could not determine "
- "number of channels defaulting to DAC count\n");
- num_dacs = STAC92HD73_DAC_COUNT;
- }
- spec->init = stac92hd73xx_core_init;
- switch (num_dacs) {
- case 0x3: /* 6 Channel */
- spec->aloopback_ctl = stac92hd73xx_6ch_loopback;
- break;
- case 0x4: /* 8 Channel */
- spec->aloopback_ctl = stac92hd73xx_8ch_loopback;
- break;
- case 0x5: /* 10 Channel */
- spec->aloopback_ctl = stac92hd73xx_10ch_loopback;
- break;
- }
- spec->multiout.dac_nids = spec->dac_nids;
-
- spec->aloopback_mask = 0x01;
- spec->aloopback_shift = 8;
-
- spec->digbeep_nid = 0x1c;
- spec->mux_nids = stac92hd73xx_mux_nids;
- spec->adc_nids = stac92hd73xx_adc_nids;
- spec->dmic_nids = stac92hd73xx_dmic_nids;
- spec->dmux_nids = stac92hd73xx_dmux_nids;
- spec->smux_nids = stac92hd73xx_smux_nids;
-
- spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids);
- spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids);
- spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids);
-
- spec->num_caps = STAC92HD73XX_NUM_CAPS;
- spec->capvols = stac92hd73xx_capvols;
- spec->capsws = stac92hd73xx_capsws;
-
- switch (spec->board_config) {
- case STAC_DELL_EQ:
- spec->init = dell_eq_core_init;
- /* fallthru */
- case STAC_DELL_M6_AMIC:
- case STAC_DELL_M6_DMIC:
- case STAC_DELL_M6_BOTH:
- spec->num_smuxes = 0;
- spec->eapd_switch = 0;
-
- switch (spec->board_config) {
- case STAC_DELL_M6_AMIC: /* Analog Mics */
- snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
- spec->num_dmics = 0;
- break;
- case STAC_DELL_M6_DMIC: /* Digital Mics */
- snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
- spec->num_dmics = 1;
- break;
- case STAC_DELL_M6_BOTH: /* Both */
- snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
- snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
- spec->num_dmics = 1;
- break;
- }
- break;
- case STAC_ALIENWARE_M17X:
- spec->num_dmics = STAC92HD73XX_NUM_DMICS;
- spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);
- spec->eapd_switch = 0;
- break;
- default:
- spec->num_dmics = STAC92HD73XX_NUM_DMICS;
- spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);
- spec->eapd_switch = 1;
- break;
- }
- if (spec->board_config != STAC_92HD73XX_REF) {
- /* GPIO0 High = Enable EAPD */
- spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
- spec->gpio_data = 0x01;
- }
-
- spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
- spec->pwr_nids = stac92hd73xx_pwr_nids;
-
- err = stac92xx_parse_auto_config(codec, 0x25, 0x27);
-
- if (!err) {
- if (spec->board_config < 0) {
- printk(KERN_WARNING "hda_codec: No auto-config is "
- "available, default to model=ref\n");
- spec->board_config = STAC_92HD73XX_REF;
- goto again;
- }
- err = -EINVAL;
- }
-
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
-
- if (spec->board_config == STAC_92HD73XX_NO_JD)
- spec->hp_detect = 0;
-
- codec->patch_ops = stac92xx_patch_ops;
-
- codec->proc_widget_hook = stac92hd7x_proc_hook;
-
- return 0;
-}
-
-static int stac92hd83xxx_set_system_btl_amp(struct hda_codec *codec)
-{
- if (codec->vendor_id != 0x111d7605 &&
- codec->vendor_id != 0x111d76d1)
- return 0;
-
- switch (codec->subsystem_id) {
- case 0x103c1618:
- case 0x103c1619:
- case 0x103c161a:
- case 0x103c161b:
- case 0x103c161c:
- case 0x103c161d:
- case 0x103c161e:
- case 0x103c161f:
- case 0x103c1620:
- case 0x103c1621:
- case 0x103c1622:
- case 0x103c1623:
-
- case 0x103c162a:
- case 0x103c162b:
-
- case 0x103c1630:
- case 0x103c1631:
-
- case 0x103c1633:
-
- case 0x103c1635:
-
- case 0x103c164f:
-
- case 0x103c1676:
- case 0x103c1677:
- case 0x103c1678:
- case 0x103c1679:
- case 0x103c167a:
- case 0x103c167b:
- case 0x103c167c:
- case 0x103c167d:
- case 0x103c167e:
- case 0x103c167f:
- case 0x103c1680:
- case 0x103c1681:
- case 0x103c1682:
- case 0x103c1683:
- case 0x103c1684:
- case 0x103c1685:
- case 0x103c1686:
- case 0x103c1687:
- case 0x103c1688:
- case 0x103c1689:
- case 0x103c168a:
- case 0x103c168b:
- case 0x103c168c:
- case 0x103c168d:
- case 0x103c168e:
- case 0x103c168f:
- case 0x103c1690:
- case 0x103c1691:
- case 0x103c1692:
-
- case 0x103c3587:
- case 0x103c3588:
- case 0x103c3589:
- case 0x103c358a:
-
- case 0x103c3667:
- case 0x103c3668:
- /* set BTL amp level to 13.43dB for louder speaker output */
- return snd_hda_codec_write_cache(codec, codec->afg, 0,
- 0x7F4, 0x14);
- }
- return 0;
-}
-
-static int patch_stac92hd83xxx(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec;
- hda_nid_t conn[STAC92HD83_DAC_COUNT + 1];
- int err;
- int num_dacs;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- /* reset pin power-down; Windows may leave these bits after reboot */
- snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7EC, 0);
- snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7ED, 0);
- codec->no_trigger_sense = 1;
- codec->spec = spec;
- spec->linear_tone_beep = 1;
- codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
- spec->digbeep_nid = 0x21;
- spec->dmic_nids = stac92hd83xxx_dmic_nids;
- spec->dmux_nids = stac92hd83xxx_mux_nids;
- spec->mux_nids = stac92hd83xxx_mux_nids;
- spec->num_muxes = ARRAY_SIZE(stac92hd83xxx_mux_nids);
- spec->adc_nids = stac92hd83xxx_adc_nids;
- spec->num_adcs = ARRAY_SIZE(stac92hd83xxx_adc_nids);
- spec->pwr_nids = stac92hd83xxx_pwr_nids;
- spec->pwr_mapping = stac92hd83xxx_pwr_mapping;
- spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
- spec->multiout.dac_nids = spec->dac_nids;
-
- spec->init = stac92hd83xxx_core_init;
- spec->num_pins = ARRAY_SIZE(stac92hd83xxx_pin_nids);
- spec->pin_nids = stac92hd83xxx_pin_nids;
- spec->num_caps = STAC92HD83XXX_NUM_CAPS;
- spec->capvols = stac92hd83xxx_capvols;
- spec->capsws = stac92hd83xxx_capsws;
-
- spec->board_config = snd_hda_check_board_config(codec,
- STAC_92HD83XXX_MODELS,
- stac92hd83xxx_models,
- stac92hd83xxx_cfg_tbl);
-again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac92hd83xxx_brd_tbl[spec->board_config]);
-
- switch (codec->vendor_id) {
- case 0x111d7666:
- case 0x111d7667:
- case 0x111d7668:
- case 0x111d7669:
- case 0x111d76d1:
- case 0x111d76d9:
- spec->num_pins = ARRAY_SIZE(stac92hd88xxx_pin_nids);
- spec->pin_nids = stac92hd88xxx_pin_nids;
- spec->mono_nid = 0;
- spec->digbeep_nid = 0;
- spec->num_pwrs = 0;
- break;
- case 0x111d7604:
- case 0x111d76d4:
- case 0x111d7605:
- case 0x111d76d5:
- case 0x111d76e7:
- if (spec->board_config == STAC_92HD83XXX_PWR_REF)
- break;
- spec->num_pwrs = 0;
- spec->num_dmics = stac92xx_connected_ports(codec,
- stac92hd83xxx_dmic_nids,
- STAC92HD83XXX_NUM_DMICS);
- break;
- }
-
- codec->patch_ops = stac92xx_patch_ops;
-
- if (find_mute_led_gpio(codec, 0))
- snd_printd("mute LED gpio %d polarity %d\n",
- spec->gpio_led,
- spec->gpio_led_polarity);
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (spec->gpio_led) {
- spec->gpio_mask |= spec->gpio_led;
- spec->gpio_dir |= spec->gpio_led;
- spec->gpio_data |= spec->gpio_led;
- /* register check_power_status callback. */
- codec->patch_ops.check_power_status =
- stac92xx_hp_check_power_status;
- }
-#endif
-
- err = stac92xx_parse_auto_config(codec, 0x1d, 0);
- if (!err) {
- if (spec->board_config < 0) {
- printk(KERN_WARNING "hda_codec: No auto-config is "
- "available, default to model=ref\n");
- spec->board_config = STAC_92HD83XXX_REF;
- goto again;
- }
- err = -EINVAL;
- }
-
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
-
- /* docking output support */
- num_dacs = snd_hda_get_connections(codec, 0xF,
- conn, STAC92HD83_DAC_COUNT + 1) - 1;
- /* skip non-DAC connections */
- while (num_dacs >= 0 &&
- (get_wcaps_type(get_wcaps(codec, conn[num_dacs]))
- != AC_WID_AUD_OUT))
- num_dacs--;
- /* set port E and F to select the last DAC */
- if (num_dacs >= 0) {
- snd_hda_codec_write_cache(codec, 0xE, 0,
- AC_VERB_SET_CONNECT_SEL, num_dacs);
- snd_hda_codec_write_cache(codec, 0xF, 0,
- AC_VERB_SET_CONNECT_SEL, num_dacs);
- }
-
- stac92hd83xxx_set_system_btl_amp(codec);
-
- codec->proc_widget_hook = stac92hd_proc_hook;
-
- return 0;
-}
-
-static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec,
- hda_nid_t dig0pin)
-{
- struct sigmatel_spec *spec = codec->spec;
- int idx;
-
- for (idx = 0; idx < spec->num_pins; idx++)
- if (spec->pin_nids[idx] == dig0pin)
- break;
- if ((idx + 2) >= spec->num_pins)
- return 0;
-
- /* dig1pin case */
- if (stac_get_defcfg_connect(codec, idx + 1) != AC_JACK_PORT_NONE)
- return 2;
-
- /* dig0pin + dig2pin case */
- if (stac_get_defcfg_connect(codec, idx + 2) != AC_JACK_PORT_NONE)
- return 2;
- if (stac_get_defcfg_connect(codec, idx) != AC_JACK_PORT_NONE)
- return 1;
- else
- return 0;
-}
-
-/* HP dv7 bass switch - GPIO5 */
-#define stac_hp_bass_gpio_info snd_ctl_boolean_mono_info
-static int stac_hp_bass_gpio_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- ucontrol->value.integer.value[0] = !!(spec->gpio_data & 0x20);
- return 0;
-}
-
-static int stac_hp_bass_gpio_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- unsigned int gpio_data;
-
- gpio_data = (spec->gpio_data & ~0x20) |
- (ucontrol->value.integer.value[0] ? 0x20 : 0);
- if (gpio_data == spec->gpio_data)
- return 0;
- spec->gpio_data = gpio_data;
- stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
- return 1;
-}
-
-static struct snd_kcontrol_new stac_hp_bass_sw_ctrl = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .info = stac_hp_bass_gpio_info,
- .get = stac_hp_bass_gpio_get,
- .put = stac_hp_bass_gpio_put,
-};
-
-static int stac_add_hp_bass_switch(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (!stac_control_new(spec, &stac_hp_bass_sw_ctrl,
- "Bass Speaker Playback Switch", 0))
- return -ENOMEM;
-
- spec->gpio_mask |= 0x20;
- spec->gpio_dir |= 0x20;
- spec->gpio_data |= 0x20;
- return 0;
-}
-
-static int patch_stac92hd71bxx(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec;
- struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init;
- unsigned int pin_cfg;
- int err = 0;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->no_trigger_sense = 1;
- codec->spec = spec;
- spec->linear_tone_beep = 0;
- codec->patch_ops = stac92xx_patch_ops;
- spec->num_pins = STAC92HD71BXX_NUM_PINS;
- switch (codec->vendor_id) {
- case 0x111d76b6:
- case 0x111d76b7:
- spec->pin_nids = stac92hd71bxx_pin_nids_4port;
- break;
- case 0x111d7603:
- case 0x111d7608:
- /* On 92HD75Bx 0x27 isn't a pin nid */
- spec->num_pins--;
- /* fallthrough */
- default:
- spec->pin_nids = stac92hd71bxx_pin_nids_6port;
- }
- spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
- spec->board_config = snd_hda_check_board_config(codec,
- STAC_92HD71BXX_MODELS,
- stac92hd71bxx_models,
- stac92hd71bxx_cfg_tbl);
-again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac92hd71bxx_brd_tbl[spec->board_config]);
-
- if (spec->board_config != STAC_92HD71BXX_REF) {
- /* GPIO0 = EAPD */
- spec->gpio_mask = 0x01;
- spec->gpio_dir = 0x01;
- spec->gpio_data = 0x01;
- }
-
- spec->dmic_nids = stac92hd71bxx_dmic_nids;
- spec->dmux_nids = stac92hd71bxx_dmux_nids;
-
- spec->num_caps = STAC92HD71BXX_NUM_CAPS;
- spec->capvols = stac92hd71bxx_capvols;
- spec->capsws = stac92hd71bxx_capsws;
-
- switch (codec->vendor_id) {
- case 0x111d76b6: /* 4 Port without Analog Mixer */
- case 0x111d76b7:
- unmute_init++;
- /* fallthru */
- case 0x111d76b4: /* 6 Port without Analog Mixer */
- case 0x111d76b5:
- spec->init = stac92hd71bxx_core_init;
- codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
- spec->num_dmics = stac92xx_connected_ports(codec,
- stac92hd71bxx_dmic_nids,
- STAC92HD71BXX_NUM_DMICS);
- break;
- case 0x111d7608: /* 5 Port with Analog Mixer */
- switch (spec->board_config) {
- case STAC_HP_M4:
- /* Enable VREF power saving on GPIO1 detect */
- err = stac_add_event(spec, codec->afg,
- STAC_VREF_EVENT, 0x02);
- if (err < 0)
- return err;
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02);
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | err);
- spec->gpio_mask |= 0x02;
- break;
- }
- if ((codec->revision_id & 0xf) == 0 ||
- (codec->revision_id & 0xf) == 1)
- spec->stream_delay = 40; /* 40 milliseconds */
-
- /* no output amps */
- spec->num_pwrs = 0;
- /* disable VSW */
- spec->init = stac92hd71bxx_core_init;
- unmute_init++;
- snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
- snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
- stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS - 1] = 0;
- spec->num_dmics = stac92xx_connected_ports(codec,
- stac92hd71bxx_dmic_nids,
- STAC92HD71BXX_NUM_DMICS - 1);
- break;
- case 0x111d7603: /* 6 Port with Analog Mixer */
- if ((codec->revision_id & 0xf) == 1)
- spec->stream_delay = 40; /* 40 milliseconds */
-
- /* no output amps */
- spec->num_pwrs = 0;
- /* fallthru */
- default:
- spec->init = stac92hd71bxx_core_init;
- codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
- spec->num_dmics = stac92xx_connected_ports(codec,
- stac92hd71bxx_dmic_nids,
- STAC92HD71BXX_NUM_DMICS);
- break;
- }
-
- if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
- snd_hda_sequence_write_cache(codec, unmute_init);
-
- /* Some HP machines seem to have unstable codec communications
- * especially with ATI fglrx driver. For recovering from the
- * CORB/RIRB stall, allow the BUS reset and keep always sync
- */
- if (spec->board_config == STAC_HP_DV5) {
- codec->bus->sync_write = 1;
- codec->bus->allow_bus_reset = 1;
- }
-
- spec->aloopback_ctl = stac92hd71bxx_loopback;
- spec->aloopback_mask = 0x50;
- spec->aloopback_shift = 0;
-
- spec->powerdown_adcs = 1;
- spec->digbeep_nid = 0x26;
- spec->mux_nids = stac92hd71bxx_mux_nids;
- spec->adc_nids = stac92hd71bxx_adc_nids;
- spec->smux_nids = stac92hd71bxx_smux_nids;
- spec->pwr_nids = stac92hd71bxx_pwr_nids;
-
- spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids);
- spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids);
- spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
- spec->num_smuxes = stac92hd71bxx_connected_smuxes(codec, 0x1e);
-
- snd_printdd("Found board config: %d\n", spec->board_config);
-
- switch (spec->board_config) {
- case STAC_HP_M4:
- /* enable internal microphone */
- snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040);
- stac92xx_auto_set_pinctl(codec, 0x0e,
- AC_PINCTL_IN_EN | AC_PINCTL_VREF_80);
- /* fallthru */
- case STAC_DELL_M4_2:
- spec->num_dmics = 0;
- spec->num_smuxes = 0;
- spec->num_dmuxes = 0;
- break;
- case STAC_DELL_M4_1:
- case STAC_DELL_M4_3:
- spec->num_dmics = 1;
- spec->num_smuxes = 0;
- spec->num_dmuxes = 1;
- break;
- case STAC_HP_DV4_1222NR:
- spec->num_dmics = 1;
- /* I don't know if it needs 1 or 2 smuxes - will wait for
- * bug reports to fix if needed
- */
- spec->num_smuxes = 1;
- spec->num_dmuxes = 1;
- /* fallthrough */
- case STAC_HP_DV4:
- spec->gpio_led = 0x01;
- /* fallthrough */
- case STAC_HP_DV5:
- snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010);
- stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN);
- /* HP dv6 gives the headphone pin as a line-out. Thus we
- * need to set hp_detect flag here to force to enable HP
- * detection.
- */
- spec->hp_detect = 1;
- break;
- case STAC_HP_HDX:
- spec->num_dmics = 1;
- spec->num_dmuxes = 1;
- spec->num_smuxes = 1;
- spec->gpio_led = 0x08;
- break;
- }
-
- if (hp_blike_system(codec->subsystem_id)) {
- pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f);
- if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT ||
- get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER ||
- get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT) {
- /* It was changed in the BIOS to just satisfy MS DTM.
- * Lets turn it back into slaved HP
- */
- pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE))
- | (AC_JACK_HP_OUT <<
- AC_DEFCFG_DEVICE_SHIFT);
- pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC
- | AC_DEFCFG_SEQUENCE)))
- | 0x1f;
- snd_hda_codec_set_pincfg(codec, 0x0f, pin_cfg);
- }
- }
-
- if (find_mute_led_gpio(codec, 1))
- snd_printd("mute LED gpio %d polarity %d\n",
- spec->gpio_led,
- spec->gpio_led_polarity);
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (spec->gpio_led) {
- spec->gpio_mask |= spec->gpio_led;
- spec->gpio_dir |= spec->gpio_led;
- spec->gpio_data |= spec->gpio_led;
- /* register check_power_status callback. */
- codec->patch_ops.check_power_status =
- stac92xx_hp_check_power_status;
- }
-#endif
-
- spec->multiout.dac_nids = spec->dac_nids;
-
- err = stac92xx_parse_auto_config(codec, 0x21, 0);
- if (!err) {
- if (spec->board_config < 0) {
- printk(KERN_WARNING "hda_codec: No auto-config is "
- "available, default to model=ref\n");
- spec->board_config = STAC_92HD71BXX_REF;
- goto again;
- }
- err = -EINVAL;
- }
-
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
-
- /* enable bass on HP dv7 */
- if (spec->board_config == STAC_HP_DV4 ||
- spec->board_config == STAC_HP_DV5) {
- unsigned int cap;
- cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP);
- cap &= AC_GPIO_IO_COUNT;
- if (cap >= 6)
- stac_add_hp_bass_switch(codec);
- }
-
- codec->proc_widget_hook = stac92hd7x_proc_hook;
-
- return 0;
-}
-
-static int patch_stac922x(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->no_trigger_sense = 1;
- codec->spec = spec;
- spec->linear_tone_beep = 1;
- spec->num_pins = ARRAY_SIZE(stac922x_pin_nids);
- spec->pin_nids = stac922x_pin_nids;
- spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
- stac922x_models,
- stac922x_cfg_tbl);
- if (spec->board_config == STAC_INTEL_MAC_AUTO) {
- spec->gpio_mask = spec->gpio_dir = 0x03;
- spec->gpio_data = 0x03;
- /* Intel Macs have all same PCI SSID, so we need to check
- * codec SSID to distinguish the exact models
- */
- printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id);
- switch (codec->subsystem_id) {
-
- case 0x106b0800:
- spec->board_config = STAC_INTEL_MAC_V1;
- break;
- case 0x106b0600:
- case 0x106b0700:
- spec->board_config = STAC_INTEL_MAC_V2;
- break;
- case 0x106b0e00:
- case 0x106b0f00:
- case 0x106b1600:
- case 0x106b1700:
- case 0x106b0200:
- case 0x106b1e00:
- spec->board_config = STAC_INTEL_MAC_V3;
- break;
- case 0x106b1a00:
- case 0x00000100:
- spec->board_config = STAC_INTEL_MAC_V4;
- break;
- case 0x106b0a00:
- case 0x106b2200:
- spec->board_config = STAC_INTEL_MAC_V5;
- break;
- default:
- spec->board_config = STAC_INTEL_MAC_V3;
- break;
- }
- }
-
- again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac922x_brd_tbl[spec->board_config]);
-
- spec->adc_nids = stac922x_adc_nids;
- spec->mux_nids = stac922x_mux_nids;
- spec->num_muxes = ARRAY_SIZE(stac922x_mux_nids);
- spec->num_adcs = ARRAY_SIZE(stac922x_adc_nids);
- spec->num_dmics = 0;
- spec->num_pwrs = 0;
-
- spec->init = stac922x_core_init;
-
- spec->num_caps = STAC922X_NUM_CAPS;
- spec->capvols = stac922x_capvols;
- spec->capsws = stac922x_capsws;
-
- spec->multiout.dac_nids = spec->dac_nids;
-
- err = stac92xx_parse_auto_config(codec, 0x08, 0x09);
- if (!err) {
- if (spec->board_config < 0) {
- printk(KERN_WARNING "hda_codec: No auto-config is "
- "available, default to model=ref\n");
- spec->board_config = STAC_D945_REF;
- goto again;
- }
- err = -EINVAL;
- }
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
-
- codec->patch_ops = stac92xx_patch_ops;
-
- /* Fix Mux capture level; max to 2 */
- snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT,
- (0 << AC_AMPCAP_OFFSET_SHIFT) |
- (2 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (0 << AC_AMPCAP_MUTE_SHIFT));
-
- return 0;
-}
-
-static int patch_stac927x(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->no_trigger_sense = 1;
- codec->spec = spec;
- spec->linear_tone_beep = 1;
- codec->slave_dig_outs = stac927x_slave_dig_outs;
- spec->num_pins = ARRAY_SIZE(stac927x_pin_nids);
- spec->pin_nids = stac927x_pin_nids;
- spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS,
- stac927x_models,
- stac927x_cfg_tbl);
- again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac927x_brd_tbl[spec->board_config]);
-
- spec->digbeep_nid = 0x23;
- spec->adc_nids = stac927x_adc_nids;
- spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
- spec->mux_nids = stac927x_mux_nids;
- spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
- spec->smux_nids = stac927x_smux_nids;
- spec->num_smuxes = ARRAY_SIZE(stac927x_smux_nids);
- spec->spdif_labels = stac927x_spdif_labels;
- spec->dac_list = stac927x_dac_nids;
- spec->multiout.dac_nids = spec->dac_nids;
-
- if (spec->board_config != STAC_D965_REF) {
- /* GPIO0 High = Enable EAPD */
- spec->eapd_mask = spec->gpio_mask = 0x01;
- spec->gpio_dir = spec->gpio_data = 0x01;
- }
-
- switch (spec->board_config) {
- case STAC_D965_3ST:
- case STAC_D965_5ST:
- /* GPIO0 High = Enable EAPD */
- spec->num_dmics = 0;
- spec->init = d965_core_init;
- break;
- case STAC_DELL_BIOS:
- switch (codec->subsystem_id) {
- case 0x10280209:
- case 0x1028022e:
- /* correct the device field to SPDIF out */
- snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070);
- break;
- }
- /* configure the analog microphone on some laptops */
- snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130);
- /* correct the front output jack as a hp out */
- snd_hda_codec_set_pincfg(codec, 0x0f, 0x0227011f);
- /* correct the front input jack as a mic */
- snd_hda_codec_set_pincfg(codec, 0x0e, 0x02a79130);
- /* fallthru */
- case STAC_DELL_3ST:
- if (codec->subsystem_id != 0x1028022f) {
- /* GPIO2 High = Enable EAPD */
- spec->eapd_mask = spec->gpio_mask = 0x04;
- spec->gpio_dir = spec->gpio_data = 0x04;
- }
- spec->dmic_nids = stac927x_dmic_nids;
- spec->num_dmics = STAC927X_NUM_DMICS;
-
- spec->init = dell_3st_core_init;
- spec->dmux_nids = stac927x_dmux_nids;
- spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids);
- break;
- case STAC_927X_VOLKNOB:
- spec->num_dmics = 0;
- spec->init = stac927x_volknob_core_init;
- break;
- default:
- spec->num_dmics = 0;
- spec->init = stac927x_core_init;
- break;
- }
-
- spec->num_caps = STAC927X_NUM_CAPS;
- spec->capvols = stac927x_capvols;
- spec->capsws = stac927x_capsws;
-
- spec->num_pwrs = 0;
- spec->aloopback_ctl = stac927x_loopback;
- spec->aloopback_mask = 0x40;
- spec->aloopback_shift = 0;
- spec->eapd_switch = 1;
-
- err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
- if (!err) {
- if (spec->board_config < 0) {
- printk(KERN_WARNING "hda_codec: No auto-config is "
- "available, default to model=ref\n");
- spec->board_config = STAC_D965_REF;
- goto again;
- }
- err = -EINVAL;
- }
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
-
- codec->patch_ops = stac92xx_patch_ops;
-
- codec->proc_widget_hook = stac927x_proc_hook;
-
- /*
- * !!FIXME!!
- * The STAC927x seem to require fairly long delays for certain
- * command sequences. With too short delays (even if the answer
- * is set to RIRB properly), it results in the silence output
- * on some hardwares like Dell.
- *
- * The below flag enables the longer delay (see get_response
- * in hda_intel.c).
- */
- codec->bus->needs_damn_long_delay = 1;
-
- /* no jack detecion for ref-no-jd model */
- if (spec->board_config == STAC_D965_REF_NO_JD)
- spec->hp_detect = 0;
-
- return 0;
-}
-
-static int patch_stac9205(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->no_trigger_sense = 1;
- codec->spec = spec;
- spec->linear_tone_beep = 1;
- spec->num_pins = ARRAY_SIZE(stac9205_pin_nids);
- spec->pin_nids = stac9205_pin_nids;
- spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS,
- stac9205_models,
- stac9205_cfg_tbl);
- again:
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac9205_brd_tbl[spec->board_config]);
-
- spec->digbeep_nid = 0x23;
- spec->adc_nids = stac9205_adc_nids;
- spec->num_adcs = ARRAY_SIZE(stac9205_adc_nids);
- spec->mux_nids = stac9205_mux_nids;
- spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids);
- spec->smux_nids = stac9205_smux_nids;
- spec->num_smuxes = ARRAY_SIZE(stac9205_smux_nids);
- spec->dmic_nids = stac9205_dmic_nids;
- spec->num_dmics = STAC9205_NUM_DMICS;
- spec->dmux_nids = stac9205_dmux_nids;
- spec->num_dmuxes = ARRAY_SIZE(stac9205_dmux_nids);
- spec->num_pwrs = 0;
-
- spec->init = stac9205_core_init;
- spec->aloopback_ctl = stac9205_loopback;
-
- spec->num_caps = STAC9205_NUM_CAPS;
- spec->capvols = stac9205_capvols;
- spec->capsws = stac9205_capsws;
-
- spec->aloopback_mask = 0x40;
- spec->aloopback_shift = 0;
- /* Turn on/off EAPD per HP plugging */
- if (spec->board_config != STAC_9205_EAPD)
- spec->eapd_switch = 1;
- spec->multiout.dac_nids = spec->dac_nids;
-
- switch (spec->board_config){
- case STAC_9205_DELL_M43:
- /* Enable SPDIF in/out */
- snd_hda_codec_set_pincfg(codec, 0x1f, 0x01441030);
- snd_hda_codec_set_pincfg(codec, 0x20, 0x1c410030);
-
- /* Enable unsol response for GPIO4/Dock HP connection */
- err = stac_add_event(spec, codec->afg, STAC_VREF_EVENT, 0x01);
- if (err < 0)
- return err;
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | err);
-
- spec->gpio_dir = 0x0b;
- spec->eapd_mask = 0x01;
- spec->gpio_mask = 0x1b;
- spec->gpio_mute = 0x10;
- /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute,
- * GPIO3 Low = DRM
- */
- spec->gpio_data = 0x01;
- break;
- case STAC_9205_REF:
- /* SPDIF-In enabled */
- break;
- default:
- /* GPIO0 High = EAPD */
- spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
- spec->gpio_data = 0x01;
- break;
- }
-
- err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
- if (!err) {
- if (spec->board_config < 0) {
- printk(KERN_WARNING "hda_codec: No auto-config is "
- "available, default to model=ref\n");
- spec->board_config = STAC_9205_REF;
- goto again;
- }
- err = -EINVAL;
- }
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
-
- codec->patch_ops = stac92xx_patch_ops;
-
- codec->proc_widget_hook = stac9205_proc_hook;
-
- return 0;
-}
-
-/*
- * STAC9872 hack
- */
-
-static struct hda_verb stac9872_core_init[] = {
- {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Mic-in -> 0x9 */
- {}
-};
-
-static hda_nid_t stac9872_pin_nids[] = {
- 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x11, 0x13, 0x14,
-};
-
-static hda_nid_t stac9872_adc_nids[] = {
- 0x8 /*,0x6*/
-};
-
-static hda_nid_t stac9872_mux_nids[] = {
- 0x15
-};
-
-static unsigned long stac9872_capvols[] = {
- HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
-};
-#define stac9872_capsws stac9872_capvols
-
-static unsigned int stac9872_vaio_pin_configs[9] = {
- 0x03211020, 0x411111f0, 0x411111f0, 0x03a15030,
- 0x411111f0, 0x90170110, 0x411111f0, 0x411111f0,
- 0x90a7013e
-};
-
-static const char *stac9872_models[STAC_9872_MODELS] = {
- [STAC_9872_AUTO] = "auto",
- [STAC_9872_VAIO] = "vaio",
-};
-
-static unsigned int *stac9872_brd_tbl[STAC_9872_MODELS] = {
- [STAC_9872_VAIO] = stac9872_vaio_pin_configs,
-};
-
-static struct snd_pci_quirk stac9872_cfg_tbl[] = {
- SND_PCI_QUIRK_MASK(0x104d, 0xfff0, 0x81e0,
- "Sony VAIO F/S", STAC_9872_VAIO),
- {} /* terminator */
-};
-
-static int patch_stac9872(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec;
- int err;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
- codec->no_trigger_sense = 1;
- codec->spec = spec;
- spec->linear_tone_beep = 1;
- spec->num_pins = ARRAY_SIZE(stac9872_pin_nids);
- spec->pin_nids = stac9872_pin_nids;
-
- spec->board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS,
- stac9872_models,
- stac9872_cfg_tbl);
- if (spec->board_config < 0)
- snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- else
- stac92xx_set_config_regs(codec,
- stac9872_brd_tbl[spec->board_config]);
-
- spec->multiout.dac_nids = spec->dac_nids;
- spec->num_adcs = ARRAY_SIZE(stac9872_adc_nids);
- spec->adc_nids = stac9872_adc_nids;
- spec->num_muxes = ARRAY_SIZE(stac9872_mux_nids);
- spec->mux_nids = stac9872_mux_nids;
- spec->init = stac9872_core_init;
- spec->num_caps = 1;
- spec->capvols = stac9872_capvols;
- spec->capsws = stac9872_capsws;
-
- err = stac92xx_parse_auto_config(codec, 0x10, 0x12);
- if (err < 0) {
- stac92xx_free(codec);
- return -EINVAL;
- }
- spec->input_mux = &spec->private_imux;
- codec->patch_ops = stac92xx_patch_ops;
- return 0;
-}
-
-
-/*
- * patch entries
- */
-static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
- { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
- { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
- { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
- { .id = 0x83847880, .name = "STAC9220 A2", .patch = patch_stac922x },
- { .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x },
- { .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x },
- { .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x },
- { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac927x },
- { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac927x },
- { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac927x },
- { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac927x },
- { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac927x },
- { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac927x },
- { .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x },
- { .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x },
- { .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x },
- { .id = 0x83847623, .name = "STAC9273D", .patch = patch_stac927x },
- { .id = 0x83847624, .name = "STAC9272X", .patch = patch_stac927x },
- { .id = 0x83847625, .name = "STAC9272D", .patch = patch_stac927x },
- { .id = 0x83847626, .name = "STAC9271X", .patch = patch_stac927x },
- { .id = 0x83847627, .name = "STAC9271D", .patch = patch_stac927x },
- { .id = 0x83847628, .name = "STAC9274X5NH", .patch = patch_stac927x },
- { .id = 0x83847629, .name = "STAC9274D5NH", .patch = patch_stac927x },
- { .id = 0x83847632, .name = "STAC9202", .patch = patch_stac925x },
- { .id = 0x83847633, .name = "STAC9202D", .patch = patch_stac925x },
- { .id = 0x83847634, .name = "STAC9250", .patch = patch_stac925x },
- { .id = 0x83847635, .name = "STAC9250D", .patch = patch_stac925x },
- { .id = 0x83847636, .name = "STAC9251", .patch = patch_stac925x },
- { .id = 0x83847637, .name = "STAC9250D", .patch = patch_stac925x },
- { .id = 0x83847645, .name = "92HD206X", .patch = patch_stac927x },
- { .id = 0x83847646, .name = "92HD206D", .patch = patch_stac927x },
- /* The following does not take into account .id=0x83847661 when subsys =
- * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
- * currently not fully supported.
- */
- { .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 },
- { .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 },
- { .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 },
- { .id = 0x83847698, .name = "STAC9205", .patch = patch_stac9205 },
- { .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 },
- { .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 },
- { .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 },
- { .id = 0x838476a3, .name = "STAC9204D", .patch = patch_stac9205 },
- { .id = 0x838476a4, .name = "STAC9255", .patch = patch_stac9205 },
- { .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 },
- { .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
- { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
- { .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx},
- { .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d4, .name = "92HD83C1C5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d5, .name = "92HD81B1C5", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d1, .name = "92HD87B1/3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76d9, .name = "92HD87B2/4", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7666, .name = "92HD88B3", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7667, .name = "92HD88B1", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7668, .name = "92HD88B2", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7669, .name = "92HD88B4", .patch = patch_stac92hd83xxx},
- { .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx},
- { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
- { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
- { .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx },
- { .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b3, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b4, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
- { .id = 0x111d76c0, .name = "92HD89C3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c1, .name = "92HD89C2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c2, .name = "92HD89C1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c3, .name = "92HD89B3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c4, .name = "92HD89B2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c5, .name = "92HD89B1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c6, .name = "92HD89E3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c7, .name = "92HD89E2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c8, .name = "92HD89E1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76c9, .name = "92HD89D3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76ca, .name = "92HD89D2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76cb, .name = "92HD89D1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx },
- { .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx },
- { .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx },
- { .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx},
- { .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx},
- {} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:8384*");
-MODULE_ALIAS("snd-hda-codec-id:111d*");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec");
-
-static struct hda_codec_preset_list sigmatel_list = {
- .preset = snd_hda_preset_sigmatel,
- .owner = THIS_MODULE,
-};
-
-static int __init patch_sigmatel_init(void)
-{
- return snd_hda_add_codec_preset(&sigmatel_list);
-}
-
-static void __exit patch_sigmatel_exit(void)
-{
- snd_hda_delete_codec_preset(&sigmatel_list);
-}
-
-module_init(patch_sigmatel_init)
-module_exit(patch_sigmatel_exit)
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
deleted file mode 100644
index d1c3f8defc48..000000000000
--- a/sound/pci/hda/patch_via.c
+++ /dev/null
@@ -1,6058 +0,0 @@
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
- *
- * (C) 2006-2009 VIA Technology, Inc.
- * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
-/* */
-/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
-/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
-/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
-/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
-/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
-/* 2007-09-17 Lydia Wang Add VT1708B codec support */
-/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
-/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
-/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
-/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
-/* 2008-04-09 Lydia Wang Add Independent HP feature */
-/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
-/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
-/* 2009-02-16 Logan Li Add support for VT1718S */
-/* 2009-03-13 Logan Li Add support for VT1716S */
-/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
-/* 2009-07-08 Lydia Wang Add support for VT2002P */
-/* 2009-07-21 Lydia Wang Add support for VT1812 */
-/* 2009-09-19 Lydia Wang Add support for VT1818S */
-/* */
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/asoundef.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-#define NID_MAPPING (-1)
-
-/* amp values */
-#define AMP_VAL_IDX_SHIFT 19
-#define AMP_VAL_IDX_MASK (0x0f<<19)
-
-/* Pin Widget NID */
-#define VT1708_HP_NID 0x13
-#define VT1708_DIGOUT_NID 0x14
-#define VT1708_DIGIN_NID 0x16
-#define VT1708_DIGIN_PIN 0x26
-#define VT1708_HP_PIN_NID 0x20
-#define VT1708_CD_PIN_NID 0x24
-
-#define VT1709_HP_DAC_NID 0x28
-#define VT1709_DIGOUT_NID 0x13
-#define VT1709_DIGIN_NID 0x17
-#define VT1709_DIGIN_PIN 0x25
-
-#define VT1708B_HP_NID 0x25
-#define VT1708B_DIGOUT_NID 0x12
-#define VT1708B_DIGIN_NID 0x15
-#define VT1708B_DIGIN_PIN 0x21
-
-#define VT1708S_HP_NID 0x25
-#define VT1708S_DIGOUT_NID 0x12
-
-#define VT1702_HP_NID 0x17
-#define VT1702_DIGOUT_NID 0x11
-
-enum VIA_HDA_CODEC {
- UNKNOWN = -1,
- VT1708,
- VT1709_10CH,
- VT1709_6CH,
- VT1708B_8CH,
- VT1708B_4CH,
- VT1708S,
- VT1708BCE,
- VT1702,
- VT1718S,
- VT1716S,
- VT2002P,
- VT1812,
- CODEC_TYPES,
-};
-
-struct via_spec {
- /* codec parameterization */
- struct snd_kcontrol_new *mixers[6];
- unsigned int num_mixers;
-
- struct hda_verb *init_verbs[5];
- unsigned int num_iverbs;
-
- char *stream_name_analog;
- struct hda_pcm_stream *stream_analog_playback;
- struct hda_pcm_stream *stream_analog_capture;
-
- char *stream_name_digital;
- struct hda_pcm_stream *stream_digital_playback;
- struct hda_pcm_stream *stream_digital_capture;
-
- /* playback */
- struct hda_multi_out multiout;
- hda_nid_t slave_dig_outs[2];
-
- /* capture */
- unsigned int num_adc_nids;
- hda_nid_t *adc_nids;
- hda_nid_t mux_nids[3];
- hda_nid_t dig_in_nid;
- hda_nid_t dig_in_pin;
-
- /* capture source */
- const struct hda_input_mux *input_mux;
- unsigned int cur_mux[3];
-
- /* PCM information */
- struct hda_pcm pcm_rec[3];
-
- /* dynamic controls, init_verbs and input_mux */
- struct auto_pin_cfg autocfg;
- struct snd_array kctls;
- struct hda_input_mux private_imux[2];
- hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
-
- /* HP mode source */
- const struct hda_input_mux *hp_mux;
- unsigned int hp_independent_mode;
- unsigned int hp_independent_mode_index;
- unsigned int smart51_enabled;
- unsigned int dmic_enabled;
- enum VIA_HDA_CODEC codec_type;
-
- /* work to check hp jack state */
- struct hda_codec *codec;
- struct delayed_work vt1708_hp_work;
- int vt1708_jack_detectect;
- int vt1708_hp_present;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- struct hda_loopback_check loopback;
-#endif
-};
-
-static struct via_spec * via_new_spec(struct hda_codec *codec)
-{
- struct via_spec *spec;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return NULL;
-
- codec->spec = spec;
- spec->codec = codec;
- return spec;
-}
-
-static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
-{
- u32 vendor_id = codec->vendor_id;
- u16 ven_id = vendor_id >> 16;
- u16 dev_id = vendor_id & 0xffff;
- enum VIA_HDA_CODEC codec_type;
-
- /* get codec type */
- if (ven_id != 0x1106)
- codec_type = UNKNOWN;
- else if (dev_id >= 0x1708 && dev_id <= 0x170b)
- codec_type = VT1708;
- else if (dev_id >= 0xe710 && dev_id <= 0xe713)
- codec_type = VT1709_10CH;
- else if (dev_id >= 0xe714 && dev_id <= 0xe717)
- codec_type = VT1709_6CH;
- else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
- codec_type = VT1708B_8CH;
- if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
- codec_type = VT1708BCE;
- } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
- codec_type = VT1708B_4CH;
- else if ((dev_id & 0xfff) == 0x397
- && (dev_id >> 12) < 8)
- codec_type = VT1708S;
- else if ((dev_id & 0xfff) == 0x398
- && (dev_id >> 12) < 8)
- codec_type = VT1702;
- else if ((dev_id & 0xfff) == 0x428
- && (dev_id >> 12) < 8)
- codec_type = VT1718S;
- else if (dev_id == 0x0433 || dev_id == 0xa721)
- codec_type = VT1716S;
- else if (dev_id == 0x0441 || dev_id == 0x4441)
- codec_type = VT1718S;
- else if (dev_id == 0x0438 || dev_id == 0x4438)
- codec_type = VT2002P;
- else if (dev_id == 0x0448)
- codec_type = VT1812;
- else if (dev_id == 0x0440)
- codec_type = VT1708S;
- else
- codec_type = UNKNOWN;
- return codec_type;
-};
-
-#define VIA_HP_EVENT 0x01
-#define VIA_GPIO_EVENT 0x02
-#define VIA_JACK_EVENT 0x04
-#define VIA_MONO_EVENT 0x08
-#define VIA_SPEAKER_EVENT 0x10
-#define VIA_BIND_HP_EVENT 0x20
-
-enum {
- VIA_CTL_WIDGET_VOL,
- VIA_CTL_WIDGET_MUTE,
- VIA_CTL_WIDGET_ANALOG_MUTE,
- VIA_CTL_WIDGET_BIND_PIN_MUTE,
-};
-
-enum {
- AUTO_SEQ_FRONT = 0,
- AUTO_SEQ_SURROUND,
- AUTO_SEQ_CENLFE,
- AUTO_SEQ_SIDE
-};
-
-static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
-static void set_jack_power_state(struct hda_codec *codec);
-static int is_aa_path_mute(struct hda_codec *codec);
-
-static void vt1708_start_hp_work(struct via_spec *spec)
-{
- if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
- return;
- snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
- !spec->vt1708_jack_detectect);
- if (!delayed_work_pending(&spec->vt1708_hp_work))
- schedule_delayed_work(&spec->vt1708_hp_work,
- msecs_to_jiffies(100));
-}
-
-static void vt1708_stop_hp_work(struct via_spec *spec)
-{
- if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
- return;
- if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
- && !is_aa_path_mute(spec->codec))
- return;
- snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
- !spec->vt1708_jack_detectect);
- cancel_delayed_work(&spec->vt1708_hp_work);
- flush_scheduled_work();
-}
-
-
-static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-
- set_jack_power_state(codec);
- analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
- if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
- if (is_aa_path_mute(codec))
- vt1708_start_hp_work(codec->spec);
- else
- vt1708_stop_hp_work(codec->spec);
- }
- return change;
-}
-
-/* modify .put = snd_hda_mixer_amp_switch_put */
-#define ANALOG_INPUT_MUTE \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = NULL, \
- .index = 0, \
- .info = snd_hda_mixer_amp_switch_info, \
- .get = snd_hda_mixer_amp_switch_get, \
- .put = analog_input_switch_put, \
- .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
-
-static void via_hp_bind_automute(struct hda_codec *codec);
-
-static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- int i;
- int change = 0;
-
- long *valp = ucontrol->value.integer.value;
- int lmute, rmute;
- if (strstr(kcontrol->id.name, "Switch") == NULL) {
- snd_printd("Invalid control!\n");
- return change;
- }
- change = snd_hda_mixer_amp_switch_put(kcontrol,
- ucontrol);
- /* Get mute value */
- lmute = *valp ? 0 : HDA_AMP_MUTE;
- valp++;
- rmute = *valp ? 0 : HDA_AMP_MUTE;
-
- /* Set hp pins */
- if (!spec->hp_independent_mode) {
- for (i = 0; i < spec->autocfg.hp_outs; i++) {
- snd_hda_codec_amp_update(
- codec, spec->autocfg.hp_pins[i],
- 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- lmute);
- snd_hda_codec_amp_update(
- codec, spec->autocfg.hp_pins[i],
- 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- rmute);
- }
- }
-
- if (!lmute && !rmute) {
- /* Line Outs */
- for (i = 0; i < spec->autocfg.line_outs; i++)
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.line_out_pins[i],
- HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
- /* Speakers */
- for (i = 0; i < spec->autocfg.speaker_outs; i++)
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.speaker_pins[i],
- HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
- /* unmute */
- via_hp_bind_automute(codec);
-
- } else {
- if (lmute) {
- /* Mute all left channels */
- for (i = 1; i < spec->autocfg.line_outs; i++)
- snd_hda_codec_amp_update(
- codec,
- spec->autocfg.line_out_pins[i],
- 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- lmute);
- for (i = 0; i < spec->autocfg.speaker_outs; i++)
- snd_hda_codec_amp_update(
- codec,
- spec->autocfg.speaker_pins[i],
- 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- lmute);
- }
- if (rmute) {
- /* mute all right channels */
- for (i = 1; i < spec->autocfg.line_outs; i++)
- snd_hda_codec_amp_update(
- codec,
- spec->autocfg.line_out_pins[i],
- 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- rmute);
- for (i = 0; i < spec->autocfg.speaker_outs; i++)
- snd_hda_codec_amp_update(
- codec,
- spec->autocfg.speaker_pins[i],
- 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- rmute);
- }
- }
- return change;
-}
-
-#define BIND_PIN_MUTE \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = NULL, \
- .index = 0, \
- .info = snd_hda_mixer_amp_switch_info, \
- .get = snd_hda_mixer_amp_switch_get, \
- .put = bind_pin_switch_put, \
- .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
-
-static struct snd_kcontrol_new via_control_templates[] = {
- HDA_CODEC_VOLUME(NULL, 0, 0, 0),
- HDA_CODEC_MUTE(NULL, 0, 0, 0),
- ANALOG_INPUT_MUTE,
- BIND_PIN_MUTE,
-};
-
-static hda_nid_t vt1708_adc_nids[2] = {
- /* ADC1-2 */
- 0x15, 0x27
-};
-
-static hda_nid_t vt1709_adc_nids[3] = {
- /* ADC1-2 */
- 0x14, 0x15, 0x16
-};
-
-static hda_nid_t vt1708B_adc_nids[2] = {
- /* ADC1-2 */
- 0x13, 0x14
-};
-
-static hda_nid_t vt1708S_adc_nids[2] = {
- /* ADC1-2 */
- 0x13, 0x14
-};
-
-static hda_nid_t vt1702_adc_nids[3] = {
- /* ADC1-2 */
- 0x12, 0x20, 0x1F
-};
-
-static hda_nid_t vt1718S_adc_nids[2] = {
- /* ADC1-2 */
- 0x10, 0x11
-};
-
-static hda_nid_t vt1716S_adc_nids[2] = {
- /* ADC1-2 */
- 0x13, 0x14
-};
-
-static hda_nid_t vt2002P_adc_nids[2] = {
- /* ADC1-2 */
- 0x10, 0x11
-};
-
-static hda_nid_t vt1812_adc_nids[2] = {
- /* ADC1-2 */
- 0x10, 0x11
-};
-
-
-/* add dynamic controls */
-static int __via_add_control(struct via_spec *spec, int type, const char *name,
- int idx, unsigned long val)
-{
- struct snd_kcontrol_new *knew;
-
- snd_array_init(&spec->kctls, sizeof(*knew), 32);
- knew = snd_array_new(&spec->kctls);
- if (!knew)
- return -ENOMEM;
- *knew = via_control_templates[type];
- knew->name = kstrdup(name, GFP_KERNEL);
- if (!knew->name)
- return -ENOMEM;
- if (get_amp_nid_(val))
- knew->subdevice = HDA_SUBDEV_AMP_FLAG;
- knew->private_value = val;
- return 0;
-}
-
-#define via_add_control(spec, type, name, val) \
- __via_add_control(spec, type, name, 0, val)
-
-static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
- struct snd_kcontrol_new *tmpl)
-{
- struct snd_kcontrol_new *knew;
-
- snd_array_init(&spec->kctls, sizeof(*knew), 32);
- knew = snd_array_new(&spec->kctls);
- if (!knew)
- return NULL;
- *knew = *tmpl;
- knew->name = kstrdup(tmpl->name, GFP_KERNEL);
- if (!knew->name)
- return NULL;
- return knew;
-}
-
-static void via_free_kctls(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
-
- if (spec->kctls.list) {
- struct snd_kcontrol_new *kctl = spec->kctls.list;
- int i;
- for (i = 0; i < spec->kctls.used; i++)
- kfree(kctl[i].name);
- }
- snd_array_free(&spec->kctls);
-}
-
-/* create input playback/capture controls for the given pin */
-static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
- int type_idx, int idx, int mix_nid)
-{
- char name[32];
- int err;
-
- sprintf(name, "%s Playback Volume", ctlname);
- err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
- HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", ctlname);
- err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
- HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
- if (err < 0)
- return err;
- return 0;
-}
-
-static void via_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- int dac_idx)
-{
- /* set as output */
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- pin_type);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
- if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_EAPD_BTLENABLE, 0x02);
-}
-
-
-static void via_auto_init_multi_out(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- if (nid)
- via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
- }
-}
-
-static void via_auto_init_hp_out(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- hda_nid_t pin;
- int i;
-
- for (i = 0; i < spec->autocfg.hp_outs; i++) {
- pin = spec->autocfg.hp_pins[i];
- if (pin) /* connect to front */
- via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
- }
-}
-
-static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
-
-static void via_auto_init_analog_input(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int ctl;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (spec->smart51_enabled && is_smart51_pins(spec, nid))
- ctl = PIN_OUT;
- else if (i == AUTO_PIN_MIC)
- ctl = PIN_VREF50;
- else
- ctl = PIN_IN;
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
- }
-}
-
-static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
- unsigned int *affected_parm)
-{
- unsigned parm;
- unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
- unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
- >> AC_DEFCFG_MISC_SHIFT
- & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
- unsigned present = snd_hda_jack_detect(codec, nid);
- struct via_spec *spec = codec->spec;
- if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
- || ((no_presence || present)
- && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
- *affected_parm = AC_PWRST_D0; /* if it's connected */
- parm = AC_PWRST_D0;
- } else
- parm = AC_PWRST_D3;
-
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
-}
-
-static void set_jack_power_state(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int imux_is_smixer;
- unsigned int parm;
-
- if (spec->codec_type == VT1702) {
- imux_is_smixer = snd_hda_codec_read(
- codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
- /* inputs */
- /* PW 1/2/5 (14h/15h/18h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x14, &parm);
- set_pin_power_state(codec, 0x15, &parm);
- set_pin_power_state(codec, 0x18, &parm);
- if (imux_is_smixer)
- parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */
- /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
- snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* outputs */
- /* PW 3/4 (16h/17h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x16, &parm);
- set_pin_power_state(codec, 0x17, &parm);
- /* MW0 (1ah), AOW 0/1 (10h/1dh) */
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
- imux_is_smixer ? AC_PWRST_D0 : parm);
- snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE,
- parm);
- } else if (spec->codec_type == VT1708B_8CH
- || spec->codec_type == VT1708B_4CH
- || spec->codec_type == VT1708S) {
- /* SW0 (17h) = stereo mixer */
- int is_8ch = spec->codec_type != VT1708B_4CH;
- imux_is_smixer = snd_hda_codec_read(
- codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
- == ((spec->codec_type == VT1708S) ? 5 : 0);
- /* inputs */
- /* PW 1/2/5 (1ah/1bh/1eh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x1a, &parm);
- set_pin_power_state(codec, 0x1b, &parm);
- set_pin_power_state(codec, 0x1e, &parm);
- if (imux_is_smixer)
- parm = AC_PWRST_D0;
- /* SW0 (17h), AIW 0/1 (13h/14h) */
- snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* outputs */
- /* PW0 (19h), SW1 (18h), AOW1 (11h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x19, &parm);
- if (spec->smart51_enabled)
- parm = AC_PWRST_D0;
- snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* PW6 (22h), SW2 (26h), AOW2 (24h) */
- if (is_8ch) {
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x22, &parm);
- if (spec->smart51_enabled)
- parm = AC_PWRST_D0;
- snd_hda_codec_write(codec, 0x26, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x24, 0,
- AC_VERB_SET_POWER_STATE, parm);
- }
-
- /* PW 3/4/7 (1ch/1dh/23h) */
- parm = AC_PWRST_D3;
- /* force to D0 for internal Speaker */
- set_pin_power_state(codec, 0x1c, &parm);
- set_pin_power_state(codec, 0x1d, &parm);
- if (is_8ch)
- set_pin_power_state(codec, 0x23, &parm);
- /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
- snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
- imux_is_smixer ? AC_PWRST_D0 : parm);
- snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
- parm);
- if (is_8ch) {
- snd_hda_codec_write(codec, 0x25, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x27, 0,
- AC_VERB_SET_POWER_STATE, parm);
- }
- } else if (spec->codec_type == VT1718S) {
- /* MUX6 (1eh) = stereo mixer */
- imux_is_smixer = snd_hda_codec_read(
- codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
- /* inputs */
- /* PW 5/6/7 (29h/2ah/2bh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x29, &parm);
- set_pin_power_state(codec, 0x2a, &parm);
- set_pin_power_state(codec, 0x2b, &parm);
- if (imux_is_smixer)
- parm = AC_PWRST_D0;
- /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
- snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* outputs */
- /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x27, &parm);
- snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* PW2 (26h), AOW2 (ah) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x26, &parm);
- snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* PW0/1 (24h/25h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x24, &parm);
- set_pin_power_state(codec, 0x25, &parm);
- if (!spec->hp_independent_mode) /* check for redirected HP */
- set_pin_power_state(codec, 0x28, &parm);
- snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE,
- parm);
- /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
- snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
- imux_is_smixer ? AC_PWRST_D0 : parm);
- if (spec->hp_independent_mode) {
- /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x28, &parm);
- snd_hda_codec_write(codec, 0x1b, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x34, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0xc, 0,
- AC_VERB_SET_POWER_STATE, parm);
- }
- } else if (spec->codec_type == VT1716S) {
- unsigned int mono_out, present;
- /* SW0 (17h) = stereo mixer */
- imux_is_smixer = snd_hda_codec_read(
- codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
- /* inputs */
- /* PW 1/2/5 (1ah/1bh/1eh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x1a, &parm);
- set_pin_power_state(codec, 0x1b, &parm);
- set_pin_power_state(codec, 0x1e, &parm);
- if (imux_is_smixer)
- parm = AC_PWRST_D0;
- /* SW0 (17h), AIW0(13h) */
- snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x1e, &parm);
- /* PW11 (22h) */
- if (spec->dmic_enabled)
- set_pin_power_state(codec, 0x22, &parm);
- else
- snd_hda_codec_write(
- codec, 0x22, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
-
- /* SW2(26h), AIW1(14h) */
- snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* outputs */
- /* PW0 (19h), SW1 (18h), AOW1 (11h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x19, &parm);
- /* Smart 5.1 PW2(1bh) */
- if (spec->smart51_enabled)
- set_pin_power_state(codec, 0x1b, &parm);
- snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* PW7 (23h), SW3 (27h), AOW3 (25h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x23, &parm);
- /* Smart 5.1 PW1(1ah) */
- if (spec->smart51_enabled)
- set_pin_power_state(codec, 0x1a, &parm);
- snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* Smart 5.1 PW5(1eh) */
- if (spec->smart51_enabled)
- set_pin_power_state(codec, 0x1e, &parm);
- snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* Mono out */
- /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
- present = snd_hda_jack_detect(codec, 0x1c);
- if (present)
- mono_out = 0;
- else {
- present = snd_hda_jack_detect(codec, 0x1d);
- if (!spec->hp_independent_mode && present)
- mono_out = 0;
- else
- mono_out = 1;
- }
- parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
- snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE,
- parm);
- snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE,
- parm);
-
- /* PW 3/4 (1ch/1dh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x1c, &parm);
- set_pin_power_state(codec, 0x1d, &parm);
- /* HP Independent Mode, power on AOW3 */
- if (spec->hp_independent_mode)
- snd_hda_codec_write(codec, 0x25, 0,
- AC_VERB_SET_POWER_STATE, parm);
-
- /* force to D0 for internal Speaker */
- /* MW0 (16h), AOW0 (10h) */
- snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
- imux_is_smixer ? AC_PWRST_D0 : parm);
- snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
- mono_out ? AC_PWRST_D0 : parm);
- } else if (spec->codec_type == VT2002P) {
- unsigned int present;
- /* MUX9 (1eh) = stereo mixer */
- imux_is_smixer = snd_hda_codec_read(
- codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
- /* inputs */
- /* PW 5/6/7 (29h/2ah/2bh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x29, &parm);
- set_pin_power_state(codec, 0x2a, &parm);
- set_pin_power_state(codec, 0x2b, &parm);
- if (imux_is_smixer)
- parm = AC_PWRST_D0;
- /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
- snd_hda_codec_write(codec, 0x1e, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x1f, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x10, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x11, 0,
- AC_VERB_SET_POWER_STATE, parm);
-
- /* outputs */
- /* AOW0 (8h)*/
- snd_hda_codec_write(codec, 0x8, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
-
- /* PW4 (26h), MW4 (1ch), MUX4(37h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x26, &parm);
- snd_hda_codec_write(codec, 0x1c, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x37,
- 0, AC_VERB_SET_POWER_STATE, parm);
-
- /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x25, &parm);
- snd_hda_codec_write(codec, 0x19, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x35, 0,
- AC_VERB_SET_POWER_STATE, parm);
- if (spec->hp_independent_mode) {
- snd_hda_codec_write(codec, 0x9, 0,
- AC_VERB_SET_POWER_STATE, parm);
- }
-
- /* Class-D */
- /* PW0 (24h), MW0(18h), MUX0(34h) */
- present = snd_hda_jack_detect(codec, 0x25);
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x24, &parm);
- if (present) {
- snd_hda_codec_write(
- codec, 0x18, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- snd_hda_codec_write(
- codec, 0x34, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- } else {
- snd_hda_codec_write(
- codec, 0x18, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- snd_hda_codec_write(
- codec, 0x34, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- }
-
- /* Mono Out */
- /* PW15 (31h), MW8(17h), MUX8(3bh) */
- present = snd_hda_jack_detect(codec, 0x26);
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x31, &parm);
- if (present) {
- snd_hda_codec_write(
- codec, 0x17, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- snd_hda_codec_write(
- codec, 0x3b, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- } else {
- snd_hda_codec_write(
- codec, 0x17, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- snd_hda_codec_write(
- codec, 0x3b, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- }
-
- /* MW9 (21h) */
- if (imux_is_smixer || !is_aa_path_mute(codec))
- snd_hda_codec_write(
- codec, 0x21, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- else
- snd_hda_codec_write(
- codec, 0x21, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- } else if (spec->codec_type == VT1812) {
- unsigned int present;
- /* MUX10 (1eh) = stereo mixer */
- imux_is_smixer = snd_hda_codec_read(
- codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
- /* inputs */
- /* PW 5/6/7 (29h/2ah/2bh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x29, &parm);
- set_pin_power_state(codec, 0x2a, &parm);
- set_pin_power_state(codec, 0x2b, &parm);
- if (imux_is_smixer)
- parm = AC_PWRST_D0;
- /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
- snd_hda_codec_write(codec, 0x1e, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x1f, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x10, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x11, 0,
- AC_VERB_SET_POWER_STATE, parm);
-
- /* outputs */
- /* AOW0 (8h)*/
- snd_hda_codec_write(codec, 0x8, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
-
- /* PW4 (28h), MW4 (18h), MUX4(38h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x28, &parm);
- snd_hda_codec_write(codec, 0x18, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x38, 0,
- AC_VERB_SET_POWER_STATE, parm);
-
- /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x25, &parm);
- snd_hda_codec_write(codec, 0x15, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x35, 0,
- AC_VERB_SET_POWER_STATE, parm);
- if (spec->hp_independent_mode) {
- snd_hda_codec_write(codec, 0x9, 0,
- AC_VERB_SET_POWER_STATE, parm);
- }
-
- /* Internal Speaker */
- /* PW0 (24h), MW0(14h), MUX0(34h) */
- present = snd_hda_jack_detect(codec, 0x25);
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x24, &parm);
- if (present) {
- snd_hda_codec_write(codec, 0x14, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D3);
- snd_hda_codec_write(codec, 0x34, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D3);
- } else {
- snd_hda_codec_write(codec, 0x14, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D0);
- snd_hda_codec_write(codec, 0x34, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D0);
- }
- /* Mono Out */
- /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
- present = snd_hda_jack_detect(codec, 0x28);
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x31, &parm);
- if (present) {
- snd_hda_codec_write(codec, 0x1c, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D3);
- snd_hda_codec_write(codec, 0x3c, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D3);
- snd_hda_codec_write(codec, 0x3e, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D3);
- } else {
- snd_hda_codec_write(codec, 0x1c, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D0);
- snd_hda_codec_write(codec, 0x3c, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D0);
- snd_hda_codec_write(codec, 0x3e, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D0);
- }
-
- /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
- parm = AC_PWRST_D3;
- set_pin_power_state(codec, 0x33, &parm);
- snd_hda_codec_write(codec, 0x1d, 0,
- AC_VERB_SET_POWER_STATE, parm);
- snd_hda_codec_write(codec, 0x3d, 0,
- AC_VERB_SET_POWER_STATE, parm);
-
- /* MW9 (21h) */
- if (imux_is_smixer || !is_aa_path_mute(codec))
- snd_hda_codec_write(
- codec, 0x21, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- else
- snd_hda_codec_write(
- codec, 0x21, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- }
-}
-
-/*
- * input MUX handling
- */
-static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->input_mux, uinfo);
-}
-
-static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
- return 0;
-}
-
-static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- if (!spec->mux_nids[adc_idx])
- return -EINVAL;
- /* switch to D0 beofre change index */
- if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
- AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
- snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- /* update jack power state */
- set_jack_power_state(codec);
-
- return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
- spec->mux_nids[adc_idx],
- &spec->cur_mux[adc_idx]);
-}
-
-static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->hp_mux, uinfo);
-}
-
-static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
- unsigned int pinsel;
-
- /* use !! to translate conn sel 2 for VT1718S */
- pinsel = !!snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONNECT_SEL,
- 0x00);
- ucontrol->value.enumerated.item[0] = pinsel;
-
- return 0;
-}
-
-static void activate_ctl(struct hda_codec *codec, const char *name, int active)
-{
- struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
- if (ctl) {
- ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- ctl->vd[0].access |= active
- ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- snd_ctl_notify(codec->bus->card,
- SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
- }
-}
-
-static hda_nid_t side_mute_channel(struct via_spec *spec)
-{
- switch (spec->codec_type) {
- case VT1708: return 0x1b;
- case VT1709_10CH: return 0x29;
- case VT1708B_8CH: /* fall thru */
- case VT1708S: return 0x27;
- default: return 0;
- }
-}
-
-static int update_side_mute_status(struct hda_codec *codec)
-{
- /* mute side channel */
- struct via_spec *spec = codec->spec;
- unsigned int parm = spec->hp_independent_mode
- ? AMP_OUT_MUTE : AMP_OUT_UNMUTE;
- hda_nid_t sw3 = side_mute_channel(spec);
-
- if (sw3)
- snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- parm);
- return 0;
-}
-
-static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- hda_nid_t nid = kcontrol->private_value;
- unsigned int pinsel = ucontrol->value.enumerated.item[0];
- /* Get Independent Mode index of headphone pin widget */
- spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
- ? 1 : 0;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
-
- if (spec->multiout.hp_nid && spec->multiout.hp_nid
- != spec->multiout.dac_nids[HDA_FRONT])
- snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
- 0, 0, 0);
-
- update_side_mute_status(codec);
- /* update HP volume/swtich active state */
- if (spec->codec_type == VT1708S
- || spec->codec_type == VT1702
- || spec->codec_type == VT1718S
- || spec->codec_type == VT1716S
- || spec->codec_type == VT2002P
- || spec->codec_type == VT1812) {
- activate_ctl(codec, "Headphone Playback Volume",
- spec->hp_independent_mode);
- activate_ctl(codec, "Headphone Playback Switch",
- spec->hp_independent_mode);
- }
- return 0;
-}
-
-static struct snd_kcontrol_new via_hp_mixer[2] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Independent HP",
- .info = via_independent_hp_info,
- .get = via_independent_hp_get,
- .put = via_independent_hp_put,
- },
- {
- .iface = NID_MAPPING,
- .name = "Independent HP",
- },
-};
-
-static int via_hp_build(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- struct snd_kcontrol_new *knew;
- hda_nid_t nid;
- int nums;
- hda_nid_t conn[HDA_MAX_CONNECTIONS];
-
- switch (spec->codec_type) {
- case VT1718S:
- nid = 0x34;
- break;
- case VT2002P:
- nid = 0x35;
- break;
- case VT1812:
- nid = 0x3d;
- break;
- default:
- nid = spec->autocfg.hp_pins[0];
- break;
- }
-
- nums = snd_hda_get_connections(codec, nid, conn, HDA_MAX_CONNECTIONS);
- if (nums <= 1)
- return 0;
-
- knew = via_clone_control(spec, &via_hp_mixer[0]);
- if (knew == NULL)
- return -ENOMEM;
-
- knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
- knew->private_value = nid;
-
- knew = via_clone_control(spec, &via_hp_mixer[1]);
- if (knew == NULL)
- return -ENOMEM;
- knew->subdevice = side_mute_channel(spec);
-
- return 0;
-}
-
-static void notify_aa_path_ctls(struct hda_codec *codec)
-{
- int i;
- struct snd_ctl_elem_id id;
- const char *labels[] = {"Mic", "Front Mic", "Line"};
-
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- for (i = 0; i < ARRAY_SIZE(labels); i++) {
- sprintf(id.name, "%s Playback Volume", labels[i]);
- snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &id);
- }
-}
-
-static void mute_aa_path(struct hda_codec *codec, int mute)
-{
- struct via_spec *spec = codec->spec;
- hda_nid_t nid_mixer;
- int start_idx;
- int end_idx;
- int i;
- /* get nid of MW0 and start & end index */
- switch (spec->codec_type) {
- case VT1708:
- nid_mixer = 0x17;
- start_idx = 2;
- end_idx = 4;
- break;
- case VT1709_10CH:
- case VT1709_6CH:
- nid_mixer = 0x18;
- start_idx = 2;
- end_idx = 4;
- break;
- case VT1708B_8CH:
- case VT1708B_4CH:
- case VT1708S:
- case VT1716S:
- nid_mixer = 0x16;
- start_idx = 2;
- end_idx = 4;
- break;
- default:
- return;
- }
- /* check AA path's mute status */
- for (i = start_idx; i <= end_idx; i++) {
- int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
- snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i,
- HDA_AMP_MUTE, val);
- }
-}
-static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
-{
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- if (pin == cfg->inputs[i].pin)
- return cfg->inputs[i].type <= AUTO_PIN_LINE_IN;
- }
- return 0;
-}
-
-static int via_smart51_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 1;
- return 0;
-}
-
-static int via_smart51_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- int on = 1;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- int ctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
- continue;
- if (cfg->inputs[i].type == AUTO_PIN_MIC &&
- spec->hp_independent_mode && spec->codec_type != VT1718S)
- continue; /* ignore FMic for independent HP */
- if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
- on = 0;
- }
- *ucontrol->value.integer.value = on;
- return 0;
-}
-
-static int via_smart51_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- int out_in = *ucontrol->value.integer.value
- ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- unsigned int parm;
-
- if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
- continue;
- if (cfg->inputs[i].type == AUTO_PIN_MIC &&
- spec->hp_independent_mode && spec->codec_type != VT1718S)
- continue; /* don't retask FMic for independent HP */
-
- parm = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
- parm |= out_in;
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- parm);
- if (out_in == AC_PINCTL_OUT_EN) {
- mute_aa_path(codec, 1);
- notify_aa_path_ctls(codec);
- }
- if (spec->codec_type == VT1718S) {
- snd_hda_codec_amp_stereo(
- codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- HDA_AMP_UNMUTE);
- }
- if (cfg->inputs[i].type == AUTO_PIN_MIC) {
- if (spec->codec_type == VT1708S
- || spec->codec_type == VT1716S) {
- /* input = index 1 (AOW3) */
- snd_hda_codec_write(
- codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, 1);
- snd_hda_codec_amp_stereo(
- codec, nid, HDA_OUTPUT,
- 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE);
- }
- }
- }
- spec->smart51_enabled = *ucontrol->value.integer.value;
- set_jack_power_state(codec);
- return 1;
-}
-
-static struct snd_kcontrol_new via_smart51_mixer[2] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Smart 5.1",
- .count = 1,
- .info = via_smart51_info,
- .get = via_smart51_get,
- .put = via_smart51_put,
- },
- {
- .iface = NID_MAPPING,
- .name = "Smart 5.1",
- }
-};
-
-static int via_smart51_build(struct via_spec *spec)
-{
- struct snd_kcontrol_new *knew;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t nid;
- int i;
-
- knew = via_clone_control(spec, &via_smart51_mixer[0]);
- if (knew == NULL)
- return -ENOMEM;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- nid = cfg->inputs[i].pin;
- if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) {
- knew = via_clone_control(spec, &via_smart51_mixer[1]);
- if (knew == NULL)
- return -ENOMEM;
- knew->subdevice = nid;
- break;
- }
- }
-
- return 0;
-}
-
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1708_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-/* check AA path's mute statue */
-static int is_aa_path_mute(struct hda_codec *codec)
-{
- int mute = 1;
- hda_nid_t nid_mixer;
- int start_idx;
- int end_idx;
- int i;
- struct via_spec *spec = codec->spec;
- /* get nid of MW0 and start & end index */
- switch (spec->codec_type) {
- case VT1708B_8CH:
- case VT1708B_4CH:
- case VT1708S:
- case VT1716S:
- nid_mixer = 0x16;
- start_idx = 2;
- end_idx = 4;
- break;
- case VT1702:
- nid_mixer = 0x1a;
- start_idx = 1;
- end_idx = 3;
- break;
- case VT1718S:
- nid_mixer = 0x21;
- start_idx = 1;
- end_idx = 3;
- break;
- case VT2002P:
- case VT1812:
- nid_mixer = 0x21;
- start_idx = 0;
- end_idx = 2;
- break;
- default:
- return 0;
- }
- /* check AA path's mute status */
- for (i = start_idx; i <= end_idx; i++) {
- unsigned int con_list = snd_hda_codec_read(
- codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
- int shift = 8 * (i % 4);
- hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
- unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
- if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
- /* check mute status while the pin is connected */
- int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0,
- HDA_INPUT, i) >> 7;
- int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1,
- HDA_INPUT, i) >> 7;
- if (!mute_l || !mute_r) {
- mute = 0;
- break;
- }
- }
- }
- return mute;
-}
-
-/* enter/exit analog low-current mode */
-static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
-{
- struct via_spec *spec = codec->spec;
- static int saved_stream_idle = 1; /* saved stream idle status */
- int enable = is_aa_path_mute(codec);
- unsigned int verb = 0;
- unsigned int parm = 0;
-
- if (stream_idle == -1) /* stream status did not change */
- enable = enable && saved_stream_idle;
- else {
- enable = enable && stream_idle;
- saved_stream_idle = stream_idle;
- }
-
- /* decide low current mode's verb & parameter */
- switch (spec->codec_type) {
- case VT1708B_8CH:
- case VT1708B_4CH:
- verb = 0xf70;
- parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
- break;
- case VT1708S:
- case VT1718S:
- case VT1716S:
- verb = 0xf73;
- parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
- break;
- case VT1702:
- verb = 0xf73;
- parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
- break;
- case VT2002P:
- case VT1812:
- verb = 0xf93;
- parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
- break;
- default:
- return; /* other codecs are not supported */
- }
- /* send verb */
- snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
-}
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb vt1708_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /*
- * Set up output mixers (0x19 - 0x1b)
- */
- /* set vol=0 to output mixers */
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Setup default input MW0 to PW4 */
- {0x20, AC_VERB_SET_CONNECT_SEL, 0},
- /* PW9 Output enable */
- {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- { }
-};
-
-static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- int idle = substream->pstr->substream_opened == 1
- && substream->ref_count == 0;
- analog_low_current_mode(codec, idle);
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static void playback_multi_pcm_prep_0(struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- struct hda_multi_out *mout = &spec->multiout;
- hda_nid_t *nids = mout->dac_nids;
- int chs = substream->runtime->channels;
- int i;
-
- mutex_lock(&codec->spdif_mutex);
- if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
- if (chs == 2 &&
- snd_hda_is_supported_format(codec, mout->dig_out_nid,
- format) &&
- !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
- mout->dig_out_used = HDA_DIG_ANALOG_DUP;
- /* turn off SPDIF once; otherwise the IEC958 bits won't
- * be updated */
- if (codec->spdif_ctls & AC_DIG1_ENABLE)
- snd_hda_codec_write(codec, mout->dig_out_nid, 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls &
- ~AC_DIG1_ENABLE & 0xff);
- snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
- stream_tag, 0, format);
- /* turn on again (if needed) */
- if (codec->spdif_ctls & AC_DIG1_ENABLE)
- snd_hda_codec_write(codec, mout->dig_out_nid, 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & 0xff);
- } else {
- mout->dig_out_used = 0;
- snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
- 0, 0, 0);
- }
- }
- mutex_unlock(&codec->spdif_mutex);
-
- /* front */
- snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
- 0, format);
-
- if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]
- && !spec->hp_independent_mode)
- /* headphone out will just decode front left/right (stereo) */
- snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
- 0, format);
-
- /* extra outputs copied from front */
- for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
- if (mout->extra_out_nid[i])
- snd_hda_codec_setup_stream(codec,
- mout->extra_out_nid[i],
- stream_tag, 0, format);
-
- /* surrounds */
- for (i = 1; i < mout->num_dacs; i++) {
- if (chs >= (i + 1) * 2) /* independent out */
- snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
- i * 2, format);
- else /* copy front */
- snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
- 0, format);
- }
-}
-
-static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- struct hda_multi_out *mout = &spec->multiout;
- hda_nid_t *nids = mout->dac_nids;
-
- if (substream->number == 0)
- playback_multi_pcm_prep_0(codec, stream_tag, format,
- substream);
- else {
- if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
- spec->hp_independent_mode)
- snd_hda_codec_setup_stream(codec, mout->hp_nid,
- stream_tag, 0, format);
- }
- vt1708_start_hp_work(spec);
- return 0;
-}
-
-static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- struct hda_multi_out *mout = &spec->multiout;
- hda_nid_t *nids = mout->dac_nids;
- int i;
-
- if (substream->number == 0) {
- for (i = 0; i < mout->num_dacs; i++)
- snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
-
- if (mout->hp_nid && !spec->hp_independent_mode)
- snd_hda_codec_setup_stream(codec, mout->hp_nid,
- 0, 0, 0);
-
- for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
- if (mout->extra_out_nid[i])
- snd_hda_codec_setup_stream(codec,
- mout->extra_out_nid[i],
- 0, 0, 0);
- mutex_lock(&codec->spdif_mutex);
- if (mout->dig_out_nid &&
- mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
- snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
- 0, 0, 0);
- mout->dig_out_used = 0;
- }
- mutex_unlock(&codec->spdif_mutex);
- } else {
- if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
- spec->hp_independent_mode)
- snd_hda_codec_setup_stream(codec, mout->hp_nid,
- 0, 0, 0);
- }
- vt1708_stop_hp_work(spec);
- return 0;
-}
-
-/*
- * Digital out
- */
-static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
- return 0;
-}
-
-/*
- * Analog capture
- */
-static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
-
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
- stream_tag, 0, format);
- return 0;
-}
-
-static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct via_spec *spec = codec->spec;
- snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
- return 0;
-}
-
-static struct hda_pcm_stream vt1708_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 8,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 8,
- .nid = 0x10, /* NID to query formats and rates */
- /* We got noisy outputs on the right channel on VT1708 when
- * 24bit samples are used. Until any workaround is found,
- * disable the 24bit format, so far.
- */
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1708_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x15, /* NID to query formats and rates */
- .ops = {
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1708_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1708_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-static int via_build_controls(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- struct snd_kcontrol *kctl;
- struct snd_kcontrol_new *knew;
- int err, i;
-
- for (i = 0; i < spec->num_mixers; i++) {
- err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
- if (err < 0)
- return err;
- }
-
- if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- if (spec->dig_in_nid) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
- if (err < 0)
- return err;
- }
-
- /* assign Capture Source enums to NID */
- kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
- for (i = 0; kctl && i < kctl->count; i++) {
- err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
- if (err < 0)
- return err;
- }
-
- /* other nid->control mapping */
- for (i = 0; i < spec->num_mixers; i++) {
- for (knew = spec->mixers[i]; knew->name; knew++) {
- if (knew->iface != NID_MAPPING)
- continue;
- kctl = snd_hda_find_mixer_ctl(codec, knew->name);
- if (kctl == NULL)
- continue;
- err = snd_hda_add_nid(codec, kctl, 0,
- knew->subdevice);
- }
- }
-
- /* init power states */
- set_jack_power_state(codec);
- analog_low_current_mode(codec, 1);
-
- via_free_kctls(codec); /* no longer needed */
- return 0;
-}
-
-static int via_build_pcms(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = spec->stream_name_analog;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- *(spec->stream_analog_playback);
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->multiout.dac_nids[0];
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
-
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
- spec->multiout.max_channels;
-
- if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
- codec->num_pcms++;
- info++;
- info->name = spec->stream_name_digital;
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- if (spec->multiout.dig_out_nid) {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- *(spec->stream_digital_playback);
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->multiout.dig_out_nid;
- }
- if (spec->dig_in_nid) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- *(spec->stream_digital_capture);
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
- spec->dig_in_nid;
- }
- }
-
- return 0;
-}
-
-static void via_free(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
-
- if (!spec)
- return;
-
- via_free_kctls(codec);
- vt1708_stop_hp_work(spec);
- kfree(codec->spec);
-}
-
-/* mute internal speaker if HP is plugged */
-static void via_hp_automute(struct hda_codec *codec)
-{
- unsigned int present = 0;
- struct via_spec *spec = codec->spec;
-
- present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
-
- if (!spec->hp_independent_mode) {
- struct snd_ctl_elem_id id;
- /* auto mute */
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
- /* notify change */
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, "Front Playback Switch");
- snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &id);
- }
-}
-
-/* mute mono out if HP or Line out is plugged */
-static void via_mono_automute(struct hda_codec *codec)
-{
- unsigned int hp_present, lineout_present;
- struct via_spec *spec = codec->spec;
-
- if (spec->codec_type != VT1716S)
- return;
-
- lineout_present = snd_hda_jack_detect(codec,
- spec->autocfg.line_out_pins[0]);
-
- /* Mute Mono Out if Line Out is plugged */
- if (lineout_present) {
- snd_hda_codec_amp_stereo(
- codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE);
- return;
- }
-
- hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
-
- if (!spec->hp_independent_mode)
- snd_hda_codec_amp_stereo(
- codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- hp_present ? HDA_AMP_MUTE : 0);
-}
-
-static void via_gpio_control(struct hda_codec *codec)
-{
- unsigned int gpio_data;
- unsigned int vol_counter;
- unsigned int vol;
- unsigned int master_vol;
-
- struct via_spec *spec = codec->spec;
-
- gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DATA, 0) & 0x03;
-
- vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
- 0xF84, 0) & 0x3F0000) >> 16;
-
- vol = vol_counter & 0x1F;
- master_vol = snd_hda_codec_read(codec, 0x1A, 0,
- AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_INPUT);
-
- if (gpio_data == 0x02) {
- /* unmute line out */
- snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
- HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
-
- if (vol_counter & 0x20) {
- /* decrease volume */
- if (vol > master_vol)
- vol = master_vol;
- snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
- 0, HDA_AMP_VOLMASK,
- master_vol-vol);
- } else {
- /* increase volume */
- snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
- HDA_AMP_VOLMASK,
- ((master_vol+vol) > 0x2A) ? 0x2A :
- (master_vol+vol));
- }
- } else if (!(gpio_data & 0x02)) {
- /* mute line out */
- snd_hda_codec_amp_stereo(codec,
- spec->autocfg.line_out_pins[0],
- HDA_OUTPUT, 0, HDA_AMP_MUTE,
- HDA_AMP_MUTE);
- }
-}
-
-/* mute Internal-Speaker if HP is plugged */
-static void via_speaker_automute(struct hda_codec *codec)
-{
- unsigned int hp_present;
- struct via_spec *spec = codec->spec;
-
- if (spec->codec_type != VT2002P && spec->codec_type != VT1812)
- return;
-
- hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
-
- if (!spec->hp_independent_mode) {
- struct snd_ctl_elem_id id;
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0,
- HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
- /* notify change */
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- strcpy(id.name, "Speaker Playback Switch");
- snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
- &id);
- }
-}
-
-/* mute line-out and internal speaker if HP is plugged */
-static void via_hp_bind_automute(struct hda_codec *codec)
-{
- /* use long instead of int below just to avoid an internal compiler
- * error with gcc 4.0.x
- */
- unsigned long hp_present, present = 0;
- struct via_spec *spec = codec->spec;
- int i;
-
- if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0])
- return;
-
- hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
-
- present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]);
-
- if (!spec->hp_independent_mode) {
- /* Mute Line-Outs */
- for (i = 0; i < spec->autocfg.line_outs; i++)
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.line_out_pins[i],
- HDA_OUTPUT, 0,
- HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
- if (hp_present)
- present = hp_present;
- }
- /* Speakers */
- for (i = 0; i < spec->autocfg.speaker_outs; i++)
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0,
- HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-
-/* unsolicited event for jack sensing */
-static void via_unsol_event(struct hda_codec *codec,
- unsigned int res)
-{
- res >>= 26;
- if (res & VIA_HP_EVENT)
- via_hp_automute(codec);
- if (res & VIA_GPIO_EVENT)
- via_gpio_control(codec);
- if (res & VIA_JACK_EVENT)
- set_jack_power_state(codec);
- if (res & VIA_MONO_EVENT)
- via_mono_automute(codec);
- if (res & VIA_SPEAKER_EVENT)
- via_speaker_automute(codec);
- if (res & VIA_BIND_HP_EVENT)
- via_hp_bind_automute(codec);
-}
-
-static int via_init(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int i;
- for (i = 0; i < spec->num_iverbs; i++)
- snd_hda_sequence_write(codec, spec->init_verbs[i]);
-
- spec->codec_type = get_codec_type(codec);
- if (spec->codec_type == VT1708BCE)
- spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost
- same */
- /* Lydia Add for EAPD enable */
- if (!spec->dig_in_nid) { /* No Digital In connection */
- if (spec->dig_in_pin) {
- snd_hda_codec_write(codec, spec->dig_in_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_OUT);
- snd_hda_codec_write(codec, spec->dig_in_pin, 0,
- AC_VERB_SET_EAPD_BTLENABLE, 0x02);
- }
- } else /* enable SPDIF-input pin */
- snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
-
- /* assign slave outs */
- if (spec->slave_dig_outs[0])
- codec->slave_dig_outs = spec->slave_dig_outs;
-
- return 0;
-}
-
-#ifdef SND_HDA_NEEDS_RESUME
-static int via_suspend(struct hda_codec *codec, pm_message_t state)
-{
- struct via_spec *spec = codec->spec;
- vt1708_stop_hp_work(spec);
- return 0;
-}
-#endif
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
-{
- struct via_spec *spec = codec->spec;
- return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
-}
-#endif
-
-/*
- */
-static struct hda_codec_ops via_patch_ops = {
- .build_controls = via_build_controls,
- .build_pcms = via_build_pcms,
- .init = via_init,
- .free = via_free,
-#ifdef SND_HDA_NEEDS_RESUME
- .suspend = via_suspend,
-#endif
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- .check_power_status = via_check_power_status,
-#endif
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- int i;
- hda_nid_t nid;
-
- spec->multiout.num_dacs = cfg->line_outs;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- for (i = 0; i < 4; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- spec->multiout.dac_nids[i] = 0x10;
- break;
- case AUTO_SEQ_CENLFE:
- spec->multiout.dac_nids[i] = 0x12;
- break;
- case AUTO_SEQ_SURROUND:
- spec->multiout.dac_nids[i] = 0x11;
- break;
- case AUTO_SEQ_SIDE:
- spec->multiout.dac_nids[i] = 0x13;
- break;
- }
- }
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- char name[32];
- static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
- hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b};
- int i, err;
-
- for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
- nid = cfg->line_out_pins[i];
-
- if (!nid)
- continue;
-
- nid_vol = nid_vols[i];
-
- if (i == AUTO_SEQ_CENLFE) {
- /* Center/LFE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_FRONT) {
- /* add control to mixer index 0 */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
-
- /* add control to PW3 */
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
-
- return 0;
-}
-
-static void create_hp_imux(struct via_spec *spec)
-{
- int i;
- struct hda_input_mux *imux = &spec->private_imux[1];
- static const char *texts[] = { "OFF", "ON", NULL};
-
- /* for hp mode select */
- for (i = 0; texts[i]; i++)
- snd_hda_add_imux_item(imux, texts[i], i, NULL);
-
- spec->hp_mux = &spec->private_imux[1];
-}
-
-static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err;
-
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- create_hp_imux(spec);
-
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg,
- hda_nid_t cap_nid,
- hda_nid_t pin_idxs[], int num_idxs)
-{
- struct via_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx, type, type_idx = 0;
-
- /* for internal loopback recording select */
- for (idx = 0; idx < num_idxs; idx++) {
- if (pin_idxs[idx] == 0xff) {
- snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
- break;
- }
- }
-
- for (i = 0; i < cfg->num_inputs; i++) {
- const char *label;
- type = cfg->inputs[i].type;
- for (idx = 0; idx < num_idxs; idx++)
- if (pin_idxs[idx] == cfg->inputs[i].pin)
- break;
- if (idx >= num_idxs)
- continue;
- if (i > 0 && type == cfg->inputs[i - 1].type)
- type_idx++;
- else
- type_idx = 0;
- label = hda_get_autocfg_input_label(codec, cfg, i);
- err = via_new_analog_input(spec, label, type_idx, idx, cap_nid);
- if (err < 0)
- return err;
- snd_hda_add_imux_item(imux, label, idx, NULL);
- }
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1708_loopbacks[] = {
- { 0x17, HDA_INPUT, 1 },
- { 0x17, HDA_INPUT, 2 },
- { 0x17, HDA_INPUT, 3 },
- { 0x17, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
-
-static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int def_conf;
- unsigned char seqassoc;
-
- def_conf = snd_hda_codec_get_pincfg(codec, nid);
- seqassoc = (unsigned char) get_defcfg_association(def_conf);
- seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
- if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
- && (seqassoc == 0xf0 || seqassoc == 0xff)) {
- def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
- snd_hda_codec_set_pincfg(codec, nid, def_conf);
- }
-
- return;
-}
-
-static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
-
- if (spec->codec_type != VT1708)
- return 0;
- spec->vt1708_jack_detectect =
- !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
- ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect;
- return 0;
-}
-
-static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- int change;
-
- if (spec->codec_type != VT1708)
- return 0;
- spec->vt1708_jack_detectect = ucontrol->value.integer.value[0];
- change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
- == !spec->vt1708_jack_detectect;
- if (spec->vt1708_jack_detectect) {
- mute_aa_path(codec, 1);
- notify_aa_path_ctls(codec);
- }
- return change;
-}
-
-static struct snd_kcontrol_new vt1708_jack_detectect[] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Jack Detect",
- .count = 1,
- .info = snd_ctl_boolean_mono_info,
- .get = vt1708_jack_detectect_get,
- .put = vt1708_jack_detectect_put,
- },
- {} /* end */
-};
-
-static int vt1708_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
- /* Add HP and CD pin config connect bit re-config action */
- vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
- vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
-
- err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
- /* add jack detect on/off control */
- err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
- spec->dig_in_pin = VT1708_DIGIN_PIN;
- if (spec->autocfg.dig_in_pin)
- spec->dig_in_nid = VT1708_DIGIN_NID;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- via_smart51_build(spec);
- return 1;
-}
-
-/* init callback for auto-configuration model -- overriding the default init */
-static int via_auto_init(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
-
- via_init(codec);
- via_auto_init_multi_out(codec);
- via_auto_init_hp_out(codec);
- via_auto_init_analog_input(codec);
- if (spec->codec_type == VT2002P || spec->codec_type == VT1812) {
- via_hp_bind_automute(codec);
- } else {
- via_hp_automute(codec);
- via_speaker_automute(codec);
- }
-
- return 0;
-}
-
-static void vt1708_update_hp_jack_state(struct work_struct *work)
-{
- struct via_spec *spec = container_of(work, struct via_spec,
- vt1708_hp_work.work);
- if (spec->codec_type != VT1708)
- return;
- /* if jack state toggled */
- if (spec->vt1708_hp_present
- != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
- spec->vt1708_hp_present ^= 1;
- via_hp_automute(spec->codec);
- }
- vt1708_start_hp_work(spec);
-}
-
-static int get_mux_nids(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- hda_nid_t nid, conn[8];
- unsigned int type;
- int i, n;
-
- for (i = 0; i < spec->num_adc_nids; i++) {
- nid = spec->adc_nids[i];
- while (nid) {
- type = get_wcaps_type(get_wcaps(codec, nid));
- if (type == AC_WID_PIN)
- break;
- n = snd_hda_get_connections(codec, nid, conn,
- ARRAY_SIZE(conn));
- if (n <= 0)
- break;
- if (n > 1) {
- spec->mux_nids[i] = nid;
- break;
- }
- nid = conn[0];
- }
- }
- return 0;
-}
-
-static int patch_vt1708(struct hda_codec *codec)
-{
- struct via_spec *spec;
- int err;
-
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
-
- /* automatic parse from the BIOS config */
- err = vt1708_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
-
- spec->stream_name_analog = "VT1708 Analog";
- spec->stream_analog_playback = &vt1708_pcm_analog_playback;
- /* disable 32bit format on VT1708 */
- if (codec->vendor_id == 0x11061708)
- spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
- spec->stream_analog_capture = &vt1708_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1708 Digital";
- spec->stream_digital_playback = &vt1708_pcm_digital_playback;
- spec->stream_digital_capture = &vt1708_pcm_digital_capture;
-
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1708_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
- get_mux_nids(codec);
- spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
- spec->num_mixers++;
- }
-
- codec->patch_ops = via_patch_ops;
-
- codec->patch_ops.init = via_auto_init;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1708_loopbacks;
-#endif
- INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
- return 0;
-}
-
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1709_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct hda_verb vt1709_uniwill_init_verbs[] = {
- {0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
- { }
-};
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /*
- * Set up output selector (0x1a, 0x1b, 0x29)
- */
- /* set vol=0 to output mixers */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /*
- * Unmute PW3 and PW4
- */
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Set input of PW4 as MW0 */
- {0x20, AC_VERB_SET_CONNECT_SEL, 0},
- /* PW9 Output enable */
- {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- { }
-};
-
-static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 10,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- },
-};
-
-static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 6,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- },
-};
-
-static struct hda_pcm_stream vt1709_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x14, /* NID to query formats and rates */
- .ops = {
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1709_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close
- },
-};
-
-static struct hda_pcm_stream vt1709_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- int i;
- hda_nid_t nid;
-
- if (cfg->line_outs == 4) /* 10 channels */
- spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */
- else if (cfg->line_outs == 3) /* 6 channels */
- spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- if (cfg->line_outs == 4) { /* 10 channels */
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- /* AOW0 */
- spec->multiout.dac_nids[i] = 0x10;
- break;
- case AUTO_SEQ_CENLFE:
- /* AOW2 */
- spec->multiout.dac_nids[i] = 0x12;
- break;
- case AUTO_SEQ_SURROUND:
- /* AOW3 */
- spec->multiout.dac_nids[i] = 0x11;
- break;
- case AUTO_SEQ_SIDE:
- /* AOW1 */
- spec->multiout.dac_nids[i] = 0x27;
- break;
- default:
- break;
- }
- }
- }
- spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
-
- } else if (cfg->line_outs == 3) { /* 6 channels */
- for (i = 0; i < cfg->line_outs; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- /* AOW0 */
- spec->multiout.dac_nids[i] = 0x10;
- break;
- case AUTO_SEQ_CENLFE:
- /* AOW2 */
- spec->multiout.dac_nids[i] = 0x12;
- break;
- case AUTO_SEQ_SURROUND:
- /* AOW1 */
- spec->multiout.dac_nids[i] = 0x11;
- break;
- default:
- break;
- }
- }
- }
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- char name[32];
- static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
- hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29};
- int i, err;
-
- for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
- nid = cfg->line_out_pins[i];
-
- if (!nid)
- continue;
-
- nid_vol = nid_vols[i];
-
- if (i == AUTO_SEQ_CENLFE) {
- /* Center/LFE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_FRONT) {
- /* ADD control to mixer index 0 */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
-
- /* add control to PW3 */
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_SURROUND) {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_SIDE) {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
-
- return 0;
-}
-
-static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err;
-
- if (!pin)
- return 0;
-
- if (spec->multiout.num_dacs == 5) /* 10 channels */
- spec->multiout.hp_nid = VT1709_HP_DAC_NID;
- else if (spec->multiout.num_dacs == 3) /* 6 channels */
- spec->multiout.hp_nid = 0;
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-static int vt1709_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
-
- err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
- spec->dig_in_pin = VT1709_DIGIN_PIN;
- if (spec->autocfg.dig_in_pin)
- spec->dig_in_nid = VT1709_DIGIN_NID;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- via_smart51_build(spec);
- return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1709_loopbacks[] = {
- { 0x18, HDA_INPUT, 1 },
- { 0x18, HDA_INPUT, 2 },
- { 0x18, HDA_INPUT, 3 },
- { 0x18, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
-
-static int patch_vt1709_10ch(struct hda_codec *codec)
-{
- struct via_spec *spec;
- int err;
-
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
-
- err = vt1709_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration. "
- "Using genenic mode...\n");
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
-
- spec->stream_name_analog = "VT1709 Analog";
- spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
- spec->stream_analog_capture = &vt1709_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1709 Digital";
- spec->stream_digital_playback = &vt1709_pcm_digital_playback;
- spec->stream_digital_capture = &vt1709_pcm_digital_capture;
-
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1709_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
- get_mux_nids(codec);
- spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
- spec->num_mixers++;
- }
-
- codec->patch_ops = via_patch_ops;
-
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1709_loopbacks;
-#endif
-
- return 0;
-}
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /*
- * Set up output selector (0x1a, 0x1b, 0x29)
- */
- /* set vol=0 to output mixers */
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /*
- * Unmute PW3 and PW4
- */
- {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Set input of PW4 as MW0 */
- {0x20, AC_VERB_SET_CONNECT_SEL, 0},
- /* PW9 Output enable */
- {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- { }
-};
-
-static int patch_vt1709_6ch(struct hda_codec *codec)
-{
- struct via_spec *spec;
- int err;
-
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
-
- err = vt1709_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration. "
- "Using genenic mode...\n");
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
-
- spec->stream_name_analog = "VT1709 Analog";
- spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
- spec->stream_analog_capture = &vt1709_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1709 Digital";
- spec->stream_digital_playback = &vt1709_pcm_digital_playback;
- spec->stream_digital_capture = &vt1709_pcm_digital_capture;
-
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1709_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
- get_mux_nids(codec);
- spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
- spec->num_mixers++;
- }
-
- codec->patch_ops = via_patch_ops;
-
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1709_loopbacks;
-#endif
- return 0;
-}
-
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1708B_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /*
- * Set up output mixers
- */
- /* set vol=0 to output mixers */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Setup default input to PW4 */
- {0x1d, AC_VERB_SET_CONNECT_SEL, 0},
- /* PW9 Output enable */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* PW10 Input enable */
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- { }
-};
-
-static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /*
- * Set up output mixers
- */
- /* set vol=0 to output mixers */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
- /* Setup default input of PW4 to MW0 */
- {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* PW9 Output enable */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* PW10 Input enable */
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
- { }
-};
-
-static struct hda_verb vt1708B_uniwill_init_verbs[] = {
- {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- { }
-};
-
-static int via_pcm_open_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- int idle = substream->pstr->substream_opened == 1
- && substream->ref_count == 0;
-
- analog_low_current_mode(codec, idle);
- return 0;
-}
-
-static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 8,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close
- },
-};
-
-static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 4,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x13, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close
- },
-};
-
-static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1708B_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- int i;
- hda_nid_t nid;
-
- spec->multiout.num_dacs = cfg->line_outs;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- for (i = 0; i < 4; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- spec->multiout.dac_nids[i] = 0x10;
- break;
- case AUTO_SEQ_CENLFE:
- spec->multiout.dac_nids[i] = 0x24;
- break;
- case AUTO_SEQ_SURROUND:
- spec->multiout.dac_nids[i] = 0x11;
- break;
- case AUTO_SEQ_SIDE:
- spec->multiout.dac_nids[i] = 0x25;
- break;
- }
- }
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- char name[32];
- static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
- hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
- hda_nid_t nid, nid_vol = 0;
- int i, err;
-
- for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
- nid = cfg->line_out_pins[i];
-
- if (!nid)
- continue;
-
- nid_vol = nid_vols[i];
-
- if (i == AUTO_SEQ_CENLFE) {
- /* Center/LFE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_FRONT) {
- /* add control to mixer index 0 */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
-
- /* add control to PW3 */
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
-
- return 0;
-}
-
-static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err;
-
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- create_hp_imux(spec);
-
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-static int vt1708B_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
-
- err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- if (spec->autocfg.dig_outs)
- spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
- spec->dig_in_pin = VT1708B_DIGIN_PIN;
- if (spec->autocfg.dig_in_pin)
- spec->dig_in_nid = VT1708B_DIGIN_NID;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- via_smart51_build(spec);
- return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1708B_loopbacks[] = {
- { 0x16, HDA_INPUT, 1 },
- { 0x16, HDA_INPUT, 2 },
- { 0x16, HDA_INPUT, 3 },
- { 0x16, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
-static int patch_vt1708S(struct hda_codec *codec);
-static int patch_vt1708B_8ch(struct hda_codec *codec)
-{
- struct via_spec *spec;
- int err;
-
- if (get_codec_type(codec) == VT1708BCE)
- return patch_vt1708S(codec);
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
-
- /* automatic parse from the BIOS config */
- err = vt1708B_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
-
- spec->stream_name_analog = "VT1708B Analog";
- spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
- spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1708B Digital";
- spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
- spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1708B_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
- get_mux_nids(codec);
- spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
- spec->num_mixers++;
- }
-
- codec->patch_ops = via_patch_ops;
-
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1708B_loopbacks;
-#endif
-
- return 0;
-}
-
-static int patch_vt1708B_4ch(struct hda_codec *codec)
-{
- struct via_spec *spec;
- int err;
-
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
-
- /* automatic parse from the BIOS config */
- err = vt1708B_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
-
- spec->stream_name_analog = "VT1708B Analog";
- spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
- spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1708B Digital";
- spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
- spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1708B_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
- get_mux_nids(codec);
- spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
- spec->num_mixers++;
- }
-
- codec->patch_ops = via_patch_ops;
-
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1708B_loopbacks;
-#endif
-
- return 0;
-}
-
-/* Patch for VT1708S */
-
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1708S_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
- HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct hda_verb vt1708S_volume_init_verbs[] = {
- /* Unmute ADC0-1 and set the default input to mic-in */
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
- * analog-loopback mixer widget */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
- /* Setup default input of PW4 to MW0 */
- {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
- /* PW9, PW10 Output enable */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* Enable Mic Boost Volume backdoor */
- {0x1, 0xf98, 0x1},
- /* don't bybass mixer */
- {0x1, 0xf88, 0xc0},
- { }
-};
-
-static struct hda_verb vt1708S_uniwill_init_verbs[] = {
- {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- { }
-};
-
-static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 8,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close
- },
-};
-
-static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x13, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close
- },
-};
-
-static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- int i;
- hda_nid_t nid;
-
- spec->multiout.num_dacs = cfg->line_outs;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- for (i = 0; i < 4; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- spec->multiout.dac_nids[i] = 0x10;
- break;
- case AUTO_SEQ_CENLFE:
- spec->multiout.dac_nids[i] = 0x24;
- break;
- case AUTO_SEQ_SURROUND:
- spec->multiout.dac_nids[i] = 0x11;
- break;
- case AUTO_SEQ_SIDE:
- spec->multiout.dac_nids[i] = 0x25;
- break;
- }
- }
- }
-
- /* for Smart 5.1, line/mic inputs double as output pins */
- if (cfg->line_outs == 1) {
- spec->multiout.num_dacs = 3;
- spec->multiout.dac_nids[AUTO_SEQ_SURROUND] = 0x11;
- spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x24;
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- char name[32];
- static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
- hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25};
- hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27};
- hda_nid_t nid, nid_vol, nid_mute;
- int i, err;
-
- for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
- nid = cfg->line_out_pins[i];
-
- /* for Smart 5.1, there are always at least six channels */
- if (!nid && i > AUTO_SEQ_CENLFE)
- continue;
-
- nid_vol = nid_vols[i];
- nid_mute = nid_mutes[i];
-
- if (i == AUTO_SEQ_CENLFE) {
- /* Center/LFE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_mute,
- 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_mute,
- 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_FRONT) {
- /* add control to mixer index 0 */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
- HDA_INPUT));
- if (err < 0)
- return err;
-
- /* Front */
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_mute,
- 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_mute,
- 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
-
- return 0;
-}
-
-static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err;
-
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- create_hp_imux(spec);
-
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-/* fill out digital output widgets; one for master and one for slave outputs */
-static void fill_dig_outs(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.dig_outs; i++) {
- hda_nid_t nid;
- int conn;
-
- nid = spec->autocfg.dig_out_pins[i];
- if (!nid)
- continue;
- conn = snd_hda_get_connections(codec, nid, &nid, 1);
- if (conn < 1)
- continue;
- if (!spec->multiout.dig_out_nid)
- spec->multiout.dig_out_nid = nid;
- else {
- spec->slave_dig_outs[0] = nid;
- break; /* at most two dig outs */
- }
- }
-}
-
-static int vt1708S_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
-
- err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- fill_dig_outs(codec);
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- via_smart51_build(spec);
- return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1708S_loopbacks[] = {
- { 0x16, HDA_INPUT, 1 },
- { 0x16, HDA_INPUT, 2 },
- { 0x16, HDA_INPUT, 3 },
- { 0x16, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
-
-static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
- int offset, int num_steps, int step_size)
-{
- snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
- (offset << AC_AMPCAP_OFFSET_SHIFT) |
- (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (0 << AC_AMPCAP_MUTE_SHIFT));
-}
-
-static int patch_vt1708S(struct hda_codec *codec)
-{
- struct via_spec *spec;
- int err;
-
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
-
- /* automatic parse from the BIOS config */
- err = vt1708S_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs;
-
- if (codec->vendor_id == 0x11060440)
- spec->stream_name_analog = "VT1818S Analog";
- else
- spec->stream_name_analog = "VT1708S Analog";
- spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
- spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
-
- if (codec->vendor_id == 0x11060440)
- spec->stream_name_digital = "VT1818S Digital";
- else
- spec->stream_name_digital = "VT1708S Digital";
- spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1708S_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids);
- get_mux_nids(codec);
- override_mic_boost(codec, 0x1a, 0, 3, 40);
- override_mic_boost(codec, 0x1e, 0, 3, 40);
- spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
- spec->num_mixers++;
- }
-
- codec->patch_ops = via_patch_ops;
-
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1708S_loopbacks;
-#endif
-
- /* correct names for VT1708BCE */
- if (get_codec_type(codec) == VT1708BCE) {
- kfree(codec->chip_name);
- codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
- snprintf(codec->bus->card->mixername,
- sizeof(codec->bus->card->mixername),
- "%s %s", codec->vendor_name, codec->chip_name);
- spec->stream_name_analog = "VT1708BCE Analog";
- spec->stream_name_digital = "VT1708BCE Digital";
- }
- return 0;
-}
-
-/* Patch for VT1702 */
-
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1702_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0,
- HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct hda_verb vt1702_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
- {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
- {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /* Setup default input of PW4 to MW0 */
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* PW6 PW7 Output enable */
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* mixer enable */
- {0x1, 0xF88, 0x3},
- /* GPIO 0~2 */
- {0x1, 0xF82, 0x3F},
- { }
-};
-
-static struct hda_verb vt1702_uniwill_init_verbs[] = {
- {0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
- {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- { }
-};
-
-static struct hda_pcm_stream vt1702_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close
- },
-};
-
-static struct hda_pcm_stream vt1702_pcm_analog_capture = {
- .substreams = 3,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x12, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close
- },
-};
-
-static struct hda_pcm_stream vt1702_pcm_digital_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1702_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- if (cfg->line_out_pins[0]) {
- /* config dac list */
- spec->multiout.dac_nids[0] = 0x10;
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- int err;
-
- if (!cfg->line_out_pins[0])
- return -1;
-
- /* add control to mixer index 0 */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
-
- /* Front */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- return 0;
-}
-
-static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err, i;
- struct hda_input_mux *imux;
- static const char *texts[] = { "ON", "OFF", NULL};
- if (!pin)
- return 0;
- spec->multiout.hp_nid = 0x1D;
- spec->hp_independent_mode_index = 0;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- imux = &spec->private_imux[1];
-
- /* for hp mode select */
- for (i = 0; texts[i]; i++)
- snd_hda_add_imux_item(imux, texts[i], i, NULL);
-
- spec->hp_mux = &spec->private_imux[1];
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-static int vt1702_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
-
- err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- /* limit AA path volume to 0 dB */
- snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
- err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- fill_dig_outs(codec);
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1702_loopbacks[] = {
- { 0x1A, HDA_INPUT, 1 },
- { 0x1A, HDA_INPUT, 2 },
- { 0x1A, HDA_INPUT, 3 },
- { 0x1A, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
-
-static int patch_vt1702(struct hda_codec *codec)
-{
- struct via_spec *spec;
- int err;
-
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
-
- /* automatic parse from the BIOS config */
- err = vt1702_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
-
- spec->stream_name_analog = "VT1702 Analog";
- spec->stream_analog_playback = &vt1702_pcm_analog_playback;
- spec->stream_analog_capture = &vt1702_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1702 Digital";
- spec->stream_digital_playback = &vt1702_pcm_digital_playback;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1702_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids);
- get_mux_nids(codec);
- spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
- spec->num_mixers++;
- }
-
- codec->patch_ops = via_patch_ops;
-
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1702_loopbacks;
-#endif
-
- return 0;
-}
-
-/* Patch for VT1718S */
-
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1718S_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
- HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- .name = "Input Source",
- .count = 2,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct hda_verb vt1718S_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
-
- /* Setup default input of Front HP to MW9 */
- {0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* PW9 PW10 Output enable */
- {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
- {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
- /* PW11 Input enable */
- {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN},
- /* Enable Boost Volume backdoor */
- {0x1, 0xf88, 0x8},
- /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */
- {0x34, AC_VERB_SET_CONNECT_SEL, 0x2},
- {0x35, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Unmute MW4's index 0 */
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { }
-};
-
-
-static struct hda_verb vt1718S_uniwill_init_verbs[] = {
- {0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
- {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- { }
-};
-
-static struct hda_pcm_stream vt1718S_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 10,
- .nid = 0x8, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt1718S_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt1718S_pcm_digital_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-static struct hda_pcm_stream vt1718S_pcm_digital_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1718S_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- int i;
- hda_nid_t nid;
-
- spec->multiout.num_dacs = cfg->line_outs;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- for (i = 0; i < 4; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- spec->multiout.dac_nids[i] = 0x8;
- break;
- case AUTO_SEQ_CENLFE:
- spec->multiout.dac_nids[i] = 0xa;
- break;
- case AUTO_SEQ_SURROUND:
- spec->multiout.dac_nids[i] = 0x9;
- break;
- case AUTO_SEQ_SIDE:
- spec->multiout.dac_nids[i] = 0xb;
- break;
- }
- }
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- char name[32];
- static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
- hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb};
- hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27};
- hda_nid_t nid, nid_vol, nid_mute = 0;
- int i, err;
-
- for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
- nid = cfg->line_out_pins[i];
-
- if (!nid)
- continue;
- nid_vol = nid_vols[i];
- nid_mute = nid_mutes[i];
-
- if (i == AUTO_SEQ_CENLFE) {
- /* Center/LFE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_FRONT) {
- /* Front */
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err;
-
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = 0xc; /* AOW4 */
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- create_hp_imux(spec);
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-static int vt1718S_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
-
- if (err < 0)
- return err;
- err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
-
- err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- fill_dig_outs(codec);
-
- if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428)
- spec->dig_in_nid = 0x13;
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- via_smart51_build(spec);
-
- return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1718S_loopbacks[] = {
- { 0x21, HDA_INPUT, 1 },
- { 0x21, HDA_INPUT, 2 },
- { 0x21, HDA_INPUT, 3 },
- { 0x21, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
-
-static int patch_vt1718S(struct hda_codec *codec)
-{
- struct via_spec *spec;
- int err;
-
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
-
- /* automatic parse from the BIOS config */
- err = vt1718S_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs;
-
- if (codec->vendor_id == 0x11060441)
- spec->stream_name_analog = "VT2020 Analog";
- else if (codec->vendor_id == 0x11064441)
- spec->stream_name_analog = "VT1828S Analog";
- else
- spec->stream_name_analog = "VT1718S Analog";
- spec->stream_analog_playback = &vt1718S_pcm_analog_playback;
- spec->stream_analog_capture = &vt1718S_pcm_analog_capture;
-
- if (codec->vendor_id == 0x11060441)
- spec->stream_name_digital = "VT2020 Digital";
- else if (codec->vendor_id == 0x11064441)
- spec->stream_name_digital = "VT1828S Digital";
- else
- spec->stream_name_digital = "VT1718S Digital";
- spec->stream_digital_playback = &vt1718S_pcm_digital_playback;
- if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441)
- spec->stream_digital_capture = &vt1718S_pcm_digital_capture;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1718S_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids);
- get_mux_nids(codec);
- override_mic_boost(codec, 0x2b, 0, 3, 40);
- override_mic_boost(codec, 0x29, 0, 3, 40);
- spec->mixers[spec->num_mixers] = vt1718S_capture_mixer;
- spec->num_mixers++;
- }
-
- codec->patch_ops = via_patch_ops;
-
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1718S_loopbacks;
-#endif
-
- return 0;
-}
-
-/* Patch for VT1716S */
-
-static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- uinfo->count = 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 1;
- return 0;
-}
-
-static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- int index = 0;
-
- index = snd_hda_codec_read(codec, 0x26, 0,
- AC_VERB_GET_CONNECT_SEL, 0);
- if (index != -1)
- *ucontrol->value.integer.value = index;
-
- return 0;
-}
-
-static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- int index = *ucontrol->value.integer.value;
-
- snd_hda_codec_write(codec, 0x26, 0,
- AC_VERB_SET_CONNECT_SEL, index);
- spec->dmic_enabled = index;
- set_jack_power_state(codec);
-
- return 1;
-}
-
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1716S_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
- HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Input Source",
- .count = 1,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
- HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Digital Mic Capture Switch",
- .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
- .count = 1,
- .info = vt1716s_dmic_info,
- .get = vt1716s_dmic_get,
- .put = vt1716s_dmic_put,
- },
- {} /* end */
-};
-
-
-/* mono-out mixer elements */
-static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
- HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
- { } /* end */
-};
-
-static struct hda_verb vt1716S_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /* MUX Indices: Stereo Mixer = 5 */
- {0x17, AC_VERB_SET_CONNECT_SEL, 0x5},
-
- /* Setup default input of PW4 to MW0 */
- {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
-
- /* Setup default input of SW1 as MW0 */
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x1},
-
- /* Setup default input of SW4 as AOW0 */
- {0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
-
- /* PW9 PW10 Output enable */
- {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-
- /* Unmute SW1, PW12 */
- {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- /* PW12 Output enable */
- {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
- /* Enable Boost Volume backdoor */
- {0x1, 0xf8a, 0x80},
- /* don't bybass mixer */
- {0x1, 0xf88, 0xc0},
- /* Enable mono output */
- {0x1, 0xf90, 0x08},
- { }
-};
-
-
-static struct hda_verb vt1716S_uniwill_init_verbs[] = {
- {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
- {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT},
- {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- { }
-};
-
-static struct hda_pcm_stream vt1716S_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 6,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt1716S_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x13, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt1716S_pcm_digital_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1716S_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{ int i;
- hda_nid_t nid;
-
- spec->multiout.num_dacs = cfg->line_outs;
-
- spec->multiout.dac_nids = spec->private_dac_nids;
-
- for (i = 0; i < 3; i++) {
- nid = cfg->line_out_pins[i];
- if (nid) {
- /* config dac list */
- switch (i) {
- case AUTO_SEQ_FRONT:
- spec->multiout.dac_nids[i] = 0x10;
- break;
- case AUTO_SEQ_CENLFE:
- spec->multiout.dac_nids[i] = 0x25;
- break;
- case AUTO_SEQ_SURROUND:
- spec->multiout.dac_nids[i] = 0x11;
- break;
- }
- }
- }
-
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- char name[32];
- static const char *chname[3] = { "Front", "Surround", "C/LFE" };
- hda_nid_t nid_vols[] = {0x10, 0x11, 0x25};
- hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27};
- hda_nid_t nid, nid_vol, nid_mute;
- int i, err;
-
- for (i = 0; i <= AUTO_SEQ_CENLFE; i++) {
- nid = cfg->line_out_pins[i];
-
- if (!nid)
- continue;
-
- nid_vol = nid_vols[i];
- nid_mute = nid_mutes[i];
-
- if (i == AUTO_SEQ_CENLFE) {
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL,
- "LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE,
- "Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE,
- "LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else if (i == AUTO_SEQ_FRONT) {
-
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
-
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- } else {
- sprintf(name, "%s Playback Volume", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- sprintf(name, "%s Playback Switch", chname[i]);
- err = via_add_control(
- spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
- HDA_OUTPUT));
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err;
-
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = 0x25; /* AOW3 */
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- create_hp_imux(spec);
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
- return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
- ARRAY_SIZE(pin_idxs));
-}
-
-static int vt1716S_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
-
- err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- fill_dig_outs(codec);
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- via_smart51_build(spec);
-
- return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1716S_loopbacks[] = {
- { 0x16, HDA_INPUT, 1 },
- { 0x16, HDA_INPUT, 2 },
- { 0x16, HDA_INPUT, 3 },
- { 0x16, HDA_INPUT, 4 },
- { } /* end */
-};
-#endif
-
-static int patch_vt1716S(struct hda_codec *codec)
-{
- struct via_spec *spec;
- int err;
-
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
-
- /* automatic parse from the BIOS config */
- err = vt1716S_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs;
-
- spec->stream_name_analog = "VT1716S Analog";
- spec->stream_analog_playback = &vt1716S_pcm_analog_playback;
- spec->stream_analog_capture = &vt1716S_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1716S Digital";
- spec->stream_digital_playback = &vt1716S_pcm_digital_playback;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1716S_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids);
- get_mux_nids(codec);
- override_mic_boost(codec, 0x1a, 0, 3, 40);
- override_mic_boost(codec, 0x1e, 0, 3, 40);
- spec->mixers[spec->num_mixers] = vt1716S_capture_mixer;
- spec->num_mixers++;
- }
-
- spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
- spec->num_mixers++;
-
- spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
-
- codec->patch_ops = via_patch_ops;
-
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1716S_loopbacks;
-#endif
-
- return 0;
-}
-
-/* for vt2002P */
-
-/* capture mixer elements */
-static struct snd_kcontrol_new vt2002P_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
- HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 2,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct hda_verb vt2002P_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /* MUX Indices: Mic = 0 */
- {0x1e, AC_VERB_SET_CONNECT_SEL, 0},
- {0x1f, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* PW9 Output enable */
- {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
-
- /* Enable Boost Volume backdoor */
- {0x1, 0xfb9, 0x24},
-
- /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* set MUX0/1/4/8 = 0 (AOW0) */
- {0x34, AC_VERB_SET_CONNECT_SEL, 0},
- {0x35, AC_VERB_SET_CONNECT_SEL, 0},
- {0x37, AC_VERB_SET_CONNECT_SEL, 0},
- {0x3b, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* set PW0 index=0 (MW0) */
- {0x24, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* Enable AOW0 to MW9 */
- {0x1, 0xfb8, 0x88},
- { }
-};
-
-
-static struct hda_verb vt2002P_uniwill_init_verbs[] = {
- {0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
- {0x26, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
- {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- { }
-};
-
-static struct hda_pcm_stream vt2002P_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x8, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt2002P_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt2002P_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt2002P_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = spec->private_dac_nids;
- if (cfg->line_out_pins[0])
- spec->multiout.dac_nids[0] = 0x8;
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- int err;
-
- if (!cfg->line_out_pins[0])
- return -1;
-
-
- /* Line-Out: PortE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
- "Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- return 0;
-}
-
-static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err;
-
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = 0x9;
- spec->hp_independent_mode_index = 1;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(
- spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- create_hp_imux(spec);
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct via_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux[0];
- static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff };
- int err;
-
- err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
- ARRAY_SIZE(pin_idxs));
- if (err < 0)
- return err;
- /* build volume/mute control of loopback */
- err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21);
- if (err < 0)
- return err;
-
- /* for digital mic select */
- snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL);
-
- return 0;
-}
-
-static int vt2002P_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
-
- err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
-
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
- return 0; /* can't find valid BIOS pin config */
-
- err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- fill_dig_outs(codec);
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt2002P_loopbacks[] = {
- { 0x21, HDA_INPUT, 0 },
- { 0x21, HDA_INPUT, 1 },
- { 0x21, HDA_INPUT, 2 },
- { } /* end */
-};
-#endif
-
-
-/* patch for vt2002P */
-static int patch_vt2002P(struct hda_codec *codec)
-{
- struct via_spec *spec;
- int err;
-
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
-
- /* automatic parse from the BIOS config */
- err = vt2002P_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt2002P_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs;
-
- spec->stream_name_analog = "VT2002P Analog";
- spec->stream_analog_playback = &vt2002P_pcm_analog_playback;
- spec->stream_analog_capture = &vt2002P_pcm_analog_capture;
-
- spec->stream_name_digital = "VT2002P Digital";
- spec->stream_digital_playback = &vt2002P_pcm_digital_playback;
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt2002P_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids);
- get_mux_nids(codec);
- override_mic_boost(codec, 0x2b, 0, 3, 40);
- override_mic_boost(codec, 0x29, 0, 3, 40);
- spec->mixers[spec->num_mixers] = vt2002P_capture_mixer;
- spec->num_mixers++;
- }
-
- codec->patch_ops = via_patch_ops;
-
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt2002P_loopbacks;
-#endif
-
- return 0;
-}
-
-/* for vt1812 */
-
-/* capture mixer elements */
-static struct snd_kcontrol_new vt1812_capture_mixer[] = {
- HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0,
- HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* The multiple "Capture Source" controls confuse alsamixer
- * So call somewhat different..
- */
- .name = "Input Source",
- .count = 2,
- .info = via_mux_enum_info,
- .get = via_mux_enum_get,
- .put = via_mux_enum_put,
- },
- { } /* end */
-};
-
-static struct hda_verb vt1812_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
- * mixer widget
- */
- /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /* MUX Indices: Mic = 0 */
- {0x1e, AC_VERB_SET_CONNECT_SEL, 0},
- {0x1f, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* PW9 Output enable */
- {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
-
- /* Enable Boost Volume backdoor */
- {0x1, 0xfb9, 0x24},
-
- /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* set MUX0/1/4/13/15 = 0 (AOW0) */
- {0x34, AC_VERB_SET_CONNECT_SEL, 0},
- {0x35, AC_VERB_SET_CONNECT_SEL, 0},
- {0x38, AC_VERB_SET_CONNECT_SEL, 0},
- {0x3c, AC_VERB_SET_CONNECT_SEL, 0},
- {0x3d, AC_VERB_SET_CONNECT_SEL, 0},
-
- /* Enable AOW0 to MW9 */
- {0x1, 0xfb8, 0xa8},
- { }
-};
-
-
-static struct hda_verb vt1812_uniwill_init_verbs[] = {
- {0x33, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
- {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT },
- {0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
- {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
- { }
-};
-
-static struct hda_pcm_stream vt1812_pcm_analog_playback = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x8, /* NID to query formats and rates */
- .ops = {
- .open = via_playback_pcm_open,
- .prepare = via_playback_multi_pcm_prepare,
- .cleanup = via_playback_multi_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt1812_pcm_analog_capture = {
- .substreams = 2,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0x10, /* NID to query formats and rates */
- .ops = {
- .open = via_pcm_open_close,
- .prepare = via_capture_pcm_prepare,
- .cleanup = via_capture_pcm_cleanup,
- .close = via_pcm_open_close,
- },
-};
-
-static struct hda_pcm_stream vt1812_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in via_build_pcms */
- .ops = {
- .open = via_dig_playback_pcm_open,
- .close = via_dig_playback_pcm_close,
- .prepare = via_dig_playback_pcm_prepare,
- .cleanup = via_dig_playback_pcm_cleanup
- },
-};
-/* fill in the dac_nids table from the parsed pin configuration */
-static int vt1812_auto_fill_dac_nids(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- spec->multiout.num_dacs = 1;
- spec->multiout.dac_nids = spec->private_dac_nids;
- if (cfg->line_out_pins[0])
- spec->multiout.dac_nids[0] = 0x8;
- return 0;
-}
-
-
-/* add playback controls from the parsed DAC table */
-static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
-{
- int err;
-
- if (!cfg->line_out_pins[0])
- return -1;
-
- /* Line-Out: PortE */
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
- err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
- "Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- return 0;
-}
-
-static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
-{
- int err;
-
- if (!pin)
- return 0;
-
- spec->multiout.hp_nid = 0x9;
- spec->hp_independent_mode_index = 1;
-
-
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Headphone Playback Volume",
- HDA_COMPOSE_AMP_VAL(
- spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
- "Headphone Playback Switch",
- HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
- if (err < 0)
- return err;
-
- create_hp_imux(spec);
- return 0;
-}
-
-/* create playback/capture controls for input pins */
-static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct via_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->private_imux[0];
- static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff };
- int err;
-
- err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
- ARRAY_SIZE(pin_idxs));
- if (err < 0)
- return err;
-
- /* build volume/mute control of loopback */
- err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21);
- if (err < 0)
- return err;
-
- /* for digital mic select */
- snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL);
-
- return 0;
-}
-
-static int vt1812_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
-
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
- fill_dig_outs(codec);
- err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg);
- if (err < 0)
- return err;
-
- if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs)
- return 0; /* can't find valid BIOS pin config */
-
- err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg);
- if (err < 0)
- return err;
- err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
- if (err < 0)
- return err;
- err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- spec->multiout.max_channels = spec->multiout.num_dacs * 2;
-
- fill_dig_outs(codec);
-
- if (spec->kctls.list)
- spec->mixers[spec->num_mixers++] = spec->kctls.list;
-
- spec->input_mux = &spec->private_imux[0];
-
- if (spec->hp_mux)
- via_hp_build(codec);
-
- return 1;
-}
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static struct hda_amp_list vt1812_loopbacks[] = {
- { 0x21, HDA_INPUT, 0 },
- { 0x21, HDA_INPUT, 1 },
- { 0x21, HDA_INPUT, 2 },
- { } /* end */
-};
-#endif
-
-
-/* patch for vt1812 */
-static int patch_vt1812(struct hda_codec *codec)
-{
- struct via_spec *spec;
- int err;
-
- /* create a codec specific record */
- spec = via_new_spec(codec);
- if (spec == NULL)
- return -ENOMEM;
-
- /* automatic parse from the BIOS config */
- err = vt1812_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- } else if (!err) {
- printk(KERN_INFO "hda_codec: Cannot set up configuration "
- "from BIOS. Using genenic mode...\n");
- }
-
-
- spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs;
- spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs;
-
- spec->stream_name_analog = "VT1812 Analog";
- spec->stream_analog_playback = &vt1812_pcm_analog_playback;
- spec->stream_analog_capture = &vt1812_pcm_analog_capture;
-
- spec->stream_name_digital = "VT1812 Digital";
- spec->stream_digital_playback = &vt1812_pcm_digital_playback;
-
-
- if (!spec->adc_nids && spec->input_mux) {
- spec->adc_nids = vt1812_adc_nids;
- spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids);
- get_mux_nids(codec);
- override_mic_boost(codec, 0x2b, 0, 3, 40);
- override_mic_boost(codec, 0x29, 0, 3, 40);
- spec->mixers[spec->num_mixers] = vt1812_capture_mixer;
- spec->num_mixers++;
- }
-
- codec->patch_ops = via_patch_ops;
-
- codec->patch_ops.init = via_auto_init;
- codec->patch_ops.unsol_event = via_unsol_event;
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- spec->loopback.amplist = vt1812_loopbacks;
-#endif
-
- return 0;
-}
-
-/*
- * patch entries
- */
-static struct hda_codec_preset snd_hda_preset_via[] = {
- { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
- { .id = 0x1106e710, .name = "VT1709 10-Ch",
- .patch = patch_vt1709_10ch},
- { .id = 0x1106e711, .name = "VT1709 10-Ch",
- .patch = patch_vt1709_10ch},
- { .id = 0x1106e712, .name = "VT1709 10-Ch",
- .patch = patch_vt1709_10ch},
- { .id = 0x1106e713, .name = "VT1709 10-Ch",
- .patch = patch_vt1709_10ch},
- { .id = 0x1106e714, .name = "VT1709 6-Ch",
- .patch = patch_vt1709_6ch},
- { .id = 0x1106e715, .name = "VT1709 6-Ch",
- .patch = patch_vt1709_6ch},
- { .id = 0x1106e716, .name = "VT1709 6-Ch",
- .patch = patch_vt1709_6ch},
- { .id = 0x1106e717, .name = "VT1709 6-Ch",
- .patch = patch_vt1709_6ch},
- { .id = 0x1106e720, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B_8ch},
- { .id = 0x1106e721, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B_8ch},
- { .id = 0x1106e722, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B_8ch},
- { .id = 0x1106e723, .name = "VT1708B 8-Ch",
- .patch = patch_vt1708B_8ch},
- { .id = 0x1106e724, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B_4ch},
- { .id = 0x1106e725, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B_4ch},
- { .id = 0x1106e726, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B_4ch},
- { .id = 0x1106e727, .name = "VT1708B 4-Ch",
- .patch = patch_vt1708B_4ch},
- { .id = 0x11060397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11061397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11062397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11063397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11064397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11065397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11066397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11067397, .name = "VT1708S",
- .patch = patch_vt1708S},
- { .id = 0x11060398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11061398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11062398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11063398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11064398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11065398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11066398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11067398, .name = "VT1702",
- .patch = patch_vt1702},
- { .id = 0x11060428, .name = "VT1718S",
- .patch = patch_vt1718S},
- { .id = 0x11064428, .name = "VT1718S",
- .patch = patch_vt1718S},
- { .id = 0x11060441, .name = "VT2020",
- .patch = patch_vt1718S},
- { .id = 0x11064441, .name = "VT1828S",
- .patch = patch_vt1718S},
- { .id = 0x11060433, .name = "VT1716S",
- .patch = patch_vt1716S},
- { .id = 0x1106a721, .name = "VT1716S",
- .patch = patch_vt1716S},
- { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
- { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
- { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
- { .id = 0x11060440, .name = "VT1818S",
- .patch = patch_vt1708S},
- {} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:1106*");
-
-static struct hda_codec_preset_list via_list = {
- .preset = snd_hda_preset_via,
- .owner = THIS_MODULE,
-};
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("VIA HD-audio codec");
-
-static int __init patch_via_init(void)
-{
- return snd_hda_add_codec_preset(&via_list);
-}
-
-static void __exit patch_via_exit(void)
-{
- snd_hda_delete_codec_preset(&via_list);
-}
-
-module_init(patch_via_init)
-module_exit(patch_via_exit)
diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile
index f7ce33f00ea5..f406a048374c 100644
--- a/sound/pci/ice1712/Makefile
+++ b/sound/pci/ice1712/Makefile
@@ -1,11 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-ice17xx-ak4xxx-objs := ak4xxx.o
-snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o
-snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o quartet.o
+snd-ice17xx-ak4xxx-y := ak4xxx.o
+snd-ice1712-y := ice1712.o delta.o hoontech.o ews.o
+snd-ice1724-y := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o quartet.o psc724.o wm8766.o wm8776.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o
diff --git a/sound/pci/ice1712/ak4xxx.c b/sound/pci/ice1712/ak4xxx.c
index 90d560c3df13..cad33a2f26bc 100644
--- a/sound/pci/ice1712/ak4xxx.c
+++ b/sound/pci/ice1712/ak4xxx.c
@@ -1,31 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* AK4524 / AK4528 / AK4529 / AK4355 / AK4381 interface
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/init.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include "ice1712.h"
@@ -178,18 +165,6 @@ int snd_ice1712_akm4xxx_build_controls(struct snd_ice1712 *ice)
return 0;
}
-static int __init alsa_ice1712_akm4xxx_module_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_ice1712_akm4xxx_module_exit(void)
-{
-}
-
-module_init(alsa_ice1712_akm4xxx_module_init)
-module_exit(alsa_ice1712_akm4xxx_module_exit)
-
EXPORT_SYMBOL(snd_ice1712_akm4xxx_init);
EXPORT_SYMBOL(snd_ice1712_akm4xxx_free);
EXPORT_SYMBOL(snd_ice1712_akm4xxx_build_controls);
diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c
index e328cfb7620c..a7b496de6ce2 100644
--- a/sound/pci/ice1712/amp.c
+++ b/sound/pci/ice1712/amp.c
@@ -1,27 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
* Lowlevel functions for Advanced Micro Peripherals Ltd AUDIO2000
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -38,7 +23,7 @@ static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val)
snd_vt1724_write_i2c(ice, WM_DEV, cval >> 8, cval & 0xff);
}
-static int __devinit snd_vt1724_amp_init(struct snd_ice1712 *ice)
+static int snd_vt1724_amp_init(struct snd_ice1712 *ice)
{
static const unsigned short wm_inits[] = {
WM_ATTEN_L, 0x0000, /* 0 db */
@@ -66,16 +51,19 @@ static int __devinit snd_vt1724_amp_init(struct snd_ice1712 *ice)
return 0;
}
-static int __devinit snd_vt1724_amp_add_controls(struct snd_ice1712 *ice)
+static int snd_vt1724_amp_add_controls(struct snd_ice1712 *ice)
{
- /* we use pins 39 and 41 of the VT1616 for left and right read outputs */
- snd_ac97_write_cache(ice->ac97, 0x5a, snd_ac97_read(ice->ac97, 0x5a) & ~0x8000);
+ if (ice->ac97)
+ /* we use pins 39 and 41 of the VT1616 for left and right
+ read outputs */
+ snd_ac97_write_cache(ice->ac97, 0x5a,
+ snd_ac97_read(ice->ac97, 0x5a) & ~0x8000);
return 0;
}
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_amp_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_amp_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_AV710,
.name = "Chaintech AV-710",
diff --git a/sound/pci/ice1712/amp.h b/sound/pci/ice1712/amp.h
index bf81d30d9150..bd6323fe38e8 100644
--- a/sound/pci/ice1712/amp.h
+++ b/sound/pci/ice1712/amp.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_AMP_H
#define __SOUND_AMP_H
@@ -7,21 +8,6 @@
* Lowlevel functions for Advanced Micro Peripherals Ltd AUDIO2000
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define AMP_AUDIO2000_DEVICE_DESC "{AMP Ltd,AUDIO2000},"\
diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c
index 2f6252266a02..b4c9e7d11609 100644
--- a/sound/pci/ice1712/aureon.c
+++ b/sound/pci/ice1712/aureon.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
@@ -5,21 +6,6 @@
*
* Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- *
* NOTES:
*
* - we reuse the struct snd_akm4xxx record for storing the wm8770 codec data.
@@ -46,7 +32,6 @@
* on mixer switch and other coll stuff.
*/
-#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -148,7 +133,7 @@ static void aureon_pca9554_write(struct snd_ice1712 *ice, unsigned char reg,
udelay(100);
/*
* send device address, command and value,
- * skipping ack cycles inbetween
+ * skipping ack cycles in between
*/
for (j = 0; j < 3; j++) {
switch (j) {
@@ -203,15 +188,10 @@ static void aureon_pca9554_write(struct snd_ice1712 *ice, unsigned char reg,
static int aureon_universe_inmux_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- char *texts[3] = {"Internal Aux", "Wavetable", "Rear Line-In"};
+ static const char * const texts[3] =
+ {"Internal Aux", "Wavetable", "Rear Line-In"};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int aureon_universe_inmux_get(struct snd_kcontrol *kcontrol,
@@ -378,14 +358,13 @@ static int aureon_ac97_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short vol;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
vol = aureon_ac97_read(ice, kcontrol->private_value & 0x7F);
ucontrol->value.integer.value[0] = 0x1F - (vol & 0x1F);
if (kcontrol->private_value & AUREON_AC97_STEREO)
ucontrol->value.integer.value[1] = 0x1F - ((vol >> 8) & 0x1F);
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -421,12 +400,11 @@ static int aureon_ac97_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_el
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = aureon_ac97_read(ice,
kcontrol->private_value & 0x7F) & 0x8000 ? 0 : 1;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -459,11 +437,10 @@ static int aureon_ac97_micboost_get(struct snd_kcontrol *kcontrol, struct snd_ct
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = aureon_ac97_read(ice, AC97_MIC) & 0x0020 ? 0 : 1;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -662,11 +639,10 @@ static int aureon_ac97_mmute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = (wm_get(ice, WM_OUT_MUX1) >> 1) & 0x01;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -724,9 +700,8 @@ static int wm_pcm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ? 0 : 1;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -964,11 +939,10 @@ static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short val;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
val = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
val = val > PCM_MIN ? (val - PCM_MIN) : 0;
ucontrol->value.integer.value[0] = val;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -1004,12 +978,11 @@ static int wm_adc_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
unsigned short val;
int i;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
val = wm_get(ice, WM_ADC_GAIN + i);
ucontrol->value.integer.value[i] = ~val>>5 & 0x1;
}
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -1051,13 +1024,12 @@ static int wm_adc_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
int i, idx;
unsigned short vol;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
idx = WM_ADC_GAIN + i;
vol = wm_get(ice, idx) & 0x1f;
ucontrol->value.integer.value[i] = vol;
}
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -1106,20 +1078,10 @@ static int wm_adc_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_in
};
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 2;
- if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON71_UNIVERSE) {
- uinfo->value.enumerated.items = 8;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, universe_texts[uinfo->value.enumerated.item]);
- } else {
- uinfo->value.enumerated.items = 5;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- }
- return 0;
+ if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON71_UNIVERSE)
+ return snd_ctl_enum_info(uinfo, 2, 8, universe_texts);
+ else
+ return snd_ctl_enum_info(uinfo, 2, 5, texts);
}
static int wm_adc_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1127,11 +1089,10 @@ static int wm_adc_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short val;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
val = wm_get(ice, WM_ADC_MUX);
ucontrol->value.enumerated.item[0] = val & 7;
ucontrol->value.enumerated.item[1] = (val >> 4) & 7;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -1167,16 +1128,10 @@ static int aureon_cs8415_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_
"CD",
"Coax"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71)
- strcpy(uinfo->value.enumerated.name, prodigy_texts[uinfo->value.enumerated.item]);
+ return snd_ctl_enum_info(uinfo, 1, 2, prodigy_texts);
else
- strcpy(uinfo->value.enumerated.name, aureon_texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, aureon_texts);
}
static int aureon_cs8415_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1392,15 +1347,7 @@ static int aureon_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_
{
static const char * const texts[2] = { "128x", "64x" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
-
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int aureon_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -1433,7 +1380,7 @@ static int aureon_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl
* mixers
*/
-static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
+static const struct snd_kcontrol_new aureon_dac_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -1548,7 +1495,7 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {
}
};
-static struct snd_kcontrol_new wm_controls[] __devinitdata = {
+static const struct snd_kcontrol_new wm_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
@@ -1614,7 +1561,7 @@ static struct snd_kcontrol_new wm_controls[] __devinitdata = {
}
};
-static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
+static const struct snd_kcontrol_new ac97_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "AC97 Playback Switch",
@@ -1719,7 +1666,7 @@ static struct snd_kcontrol_new ac97_controls[] __devinitdata = {
}
};
-static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
+static const struct snd_kcontrol_new universe_ac97_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "AC97 Playback Switch",
@@ -1851,7 +1798,7 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
};
-static struct snd_kcontrol_new cs8415_controls[] __devinitdata = {
+static const struct snd_kcontrol_new cs8415_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH),
@@ -1896,7 +1843,7 @@ static struct snd_kcontrol_new cs8415_controls[] __devinitdata = {
}
};
-static int __devinit aureon_add_controls(struct snd_ice1712 *ice)
+static int aureon_add_controls(struct snd_ice1712 *ice)
{
unsigned int i, counts;
int err;
@@ -1936,21 +1883,21 @@ static int __devinit aureon_add_controls(struct snd_ice1712 *ice)
unsigned char id;
snd_ice1712_save_gpio_status(ice);
id = aureon_cs8415_get(ice, CS8415_ID);
+ snd_ice1712_restore_gpio_status(ice);
if (id != 0x41)
- snd_printk(KERN_INFO "No CS8415 chip. Skipping CS8415 controls.\n");
- else if ((id & 0x0F) != 0x01)
- snd_printk(KERN_INFO "Detected unsupported CS8415 rev. (%c)\n", (char)((id & 0x0F) + 'A' - 1));
+ dev_info(ice->card->dev,
+ "No CS8415 chip. Skipping CS8415 controls.\n");
else {
for (i = 0; i < ARRAY_SIZE(cs8415_controls); i++) {
struct snd_kcontrol *kctl;
- err = snd_ctl_add(ice->card, (kctl = snd_ctl_new1(&cs8415_controls[i], ice)));
- if (err < 0)
- return err;
+ kctl = snd_ctl_new1(&cs8415_controls[i], ice);
if (i > 1)
kctl->id.device = ice->pcm->device;
+ err = snd_ctl_add(ice->card, kctl);
+ if (err < 0)
+ return err;
}
}
- snd_ice1712_restore_gpio_status(ice);
}
return 0;
@@ -2103,7 +2050,7 @@ static int aureon_reset(struct snd_ice1712 *ice)
/*
* suspend/resume
*/
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int aureon_resume(struct snd_ice1712 *ice)
{
struct aureon_spec *spec = ice->spec;
@@ -2124,7 +2071,7 @@ static int aureon_resume(struct snd_ice1712 *ice)
/*
* initialize the chip
*/
-static int __devinit aureon_init(struct snd_ice1712 *ice)
+static int aureon_init(struct snd_ice1712 *ice)
{
struct aureon_spec *spec;
int i, err;
@@ -2143,7 +2090,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
ice->num_total_adcs = 2;
}
- /* to remeber the register values of CS8415 */
+ /* to remember the register values of CS8415 */
ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
if (!ice->akm)
return -ENOMEM;
@@ -2160,7 +2107,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
ice->pm_resume = aureon_resume;
ice->pm_suspend_enabled = 1;
#endif
@@ -2174,7 +2121,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char aureon51_eeprom[] __devinitdata = {
+static const unsigned char aureon51_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x0a, /* clock 512, spdif-in/ADC, 3DACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
@@ -2190,7 +2137,7 @@ static unsigned char aureon51_eeprom[] __devinitdata = {
[ICE_EEP2_GPIO_STATE2] = 0x00,
};
-static unsigned char aureon71_eeprom[] __devinitdata = {
+static const unsigned char aureon71_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x0b, /* clock 512, spdif-in/ADC, 4DACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
@@ -2207,7 +2154,7 @@ static unsigned char aureon71_eeprom[] __devinitdata = {
};
#define prodigy71_eeprom aureon71_eeprom
-static unsigned char aureon71_universe_eeprom[] __devinitdata = {
+static const unsigned char aureon71_universe_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401, spdif-in/ADC,
* 4DACs
*/
@@ -2225,7 +2172,7 @@ static unsigned char aureon71_universe_eeprom[] __devinitdata = {
[ICE_EEP2_GPIO_STATE2] = 0x00,
};
-static unsigned char prodigy71lt_eeprom[] __devinitdata = {
+static const unsigned char prodigy71lt_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x4b, /* clock 384, spdif-in/ADC, 4DACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0xfc, /* vol, 96k, 24bit, 192k */
@@ -2243,7 +2190,7 @@ static unsigned char prodigy71lt_eeprom[] __devinitdata = {
#define prodigy71xt_eeprom prodigy71lt_eeprom
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_aureon_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_aureon_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_AUREON51_SKY,
.name = "Terratec Aureon 5.1-Sky",
diff --git a/sound/pci/ice1712/aureon.h b/sound/pci/ice1712/aureon.h
index c253b8e2c789..de011495e27a 100644
--- a/sound/pci/ice1712/aureon.h
+++ b/sound/pci/ice1712/aureon.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_AUREON_H
#define __SOUND_AUREON_H
@@ -7,21 +8,6 @@
* Lowlevel functions for Terratec Aureon cards
*
* Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define AUREON_DEVICE_DESC "{Terratec,Aureon 5.1 Sky},"\
diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c
index 712c1710f9a2..e5a9585cba4c 100644
--- a/sound/pci/ice1712/delta.c
+++ b/sound/pci/ice1712/delta.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
@@ -5,24 +6,8 @@
* Audiophile, Digigram VX442
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -96,6 +81,11 @@ static unsigned char ap_cs8427_codec_select(struct snd_ice1712 *ice)
tmp |= ICE1712_DELTA_AP_CCLK | ICE1712_DELTA_AP_CS_CODEC;
tmp &= ~ICE1712_DELTA_AP_CS_DIGITAL;
break;
+ case ICE1712_SUBDEVICE_DELTA66E:
+ tmp |= ICE1712_DELTA_66E_CCLK | ICE1712_DELTA_66E_CS_CHIP_A |
+ ICE1712_DELTA_66E_CS_CHIP_B;
+ tmp &= ~ICE1712_DELTA_66E_CS_CS8427;
+ break;
case ICE1712_SUBDEVICE_VX442:
tmp |= ICE1712_VX442_CCLK | ICE1712_VX442_CODEC_CHIP_A | ICE1712_VX442_CODEC_CHIP_B;
tmp &= ~ICE1712_VX442_CS_DIGITAL;
@@ -119,6 +109,9 @@ static void ap_cs8427_codec_deassert(struct snd_ice1712 *ice, unsigned char tmp)
case ICE1712_SUBDEVICE_DELTA410:
tmp |= ICE1712_DELTA_AP_CS_DIGITAL;
break;
+ case ICE1712_SUBDEVICE_DELTA66E:
+ tmp |= ICE1712_DELTA_66E_CS_CS8427;
+ break;
case ICE1712_SUBDEVICE_VX442:
tmp |= ICE1712_VX442_CS_DIGITAL;
break;
@@ -133,13 +126,12 @@ static int ap_cs8427_sendbytes(struct snd_i2c_device *device, unsigned char *byt
int res = count;
unsigned char tmp;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
tmp = ap_cs8427_codec_select(ice);
ap_cs8427_write_byte(ice, (device->addr << 1) | 0, tmp); /* address + write mode */
while (count-- > 0)
ap_cs8427_write_byte(ice, *bytes++, tmp);
ap_cs8427_codec_deassert(ice, tmp);
- mutex_unlock(&ice->gpio_mutex);
return res;
}
@@ -150,13 +142,12 @@ static int ap_cs8427_readbytes(struct snd_i2c_device *device, unsigned char *byt
int res = count;
unsigned char tmp;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
tmp = ap_cs8427_codec_select(ice);
ap_cs8427_write_byte(ice, (device->addr << 1) | 1, tmp); /* address + read mode */
while (count-- > 0)
*bytes++ = ap_cs8427_read_byte(ice, tmp);
ap_cs8427_codec_deassert(ice, tmp);
- mutex_unlock(&ice->gpio_mutex);
return res;
}
@@ -167,7 +158,7 @@ static int ap_cs8427_probeaddr(struct snd_i2c_bus *bus, unsigned short addr)
return -ENOENT;
}
-static struct snd_i2c_ops ap_cs8427_i2c_ops = {
+static const struct snd_i2c_ops ap_cs8427_i2c_ops = {
.sendbytes = ap_cs8427_sendbytes,
.readbytes = ap_cs8427_readbytes,
.probeaddr = ap_cs8427_probeaddr,
@@ -183,7 +174,7 @@ static void snd_ice1712_delta_cs8403_spdif_write(struct snd_ice1712 *ice, unsign
/* send byte to transmitter */
mask1 = ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK;
mask2 = ICE1712_DELTA_SPDIF_OUT_STAT_DATA;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
for (idx = 7; idx >= 0; idx--) {
tmp &= ~(mask1 | mask2);
@@ -197,7 +188,6 @@ static void snd_ice1712_delta_cs8403_spdif_write(struct snd_ice1712 *ice, unsign
}
tmp &= ~mask1;
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
- mutex_unlock(&ice->gpio_mutex);
}
@@ -212,15 +202,13 @@ static int delta_spdif_default_put(struct snd_ice1712 *ice, struct snd_ctl_elem_
int change;
val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958);
- spin_lock_irq(&ice->reg_lock);
- change = ice->spdif.cs8403_bits != val;
- ice->spdif.cs8403_bits = val;
- if (change && ice->playback_pro_substream == NULL) {
- spin_unlock_irq(&ice->reg_lock);
- snd_ice1712_delta_cs8403_spdif_write(ice, val);
- } else {
- spin_unlock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ change = ice->spdif.cs8403_bits != val;
+ ice->spdif.cs8403_bits = val;
+ if (!change || ice->playback_pro_substream)
+ return change;
}
+ snd_ice1712_delta_cs8403_spdif_write(ice, val);
return change;
}
@@ -235,15 +223,13 @@ static int delta_spdif_stream_put(struct snd_ice1712 *ice, struct snd_ctl_elem_v
int change;
val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958);
- spin_lock_irq(&ice->reg_lock);
- change = ice->spdif.cs8403_stream_bits != val;
- ice->spdif.cs8403_stream_bits = val;
- if (change && ice->playback_pro_substream != NULL) {
- spin_unlock_irq(&ice->reg_lock);
- snd_ice1712_delta_cs8403_spdif_write(ice, val);
- } else {
- spin_unlock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ change = ice->spdif.cs8403_stream_bits != val;
+ ice->spdif.cs8403_stream_bits = val;
+ if (!change || ice->playback_pro_substream)
+ return change;
}
+ snd_ice1712_delta_cs8403_spdif_write(ice, val);
return change;
}
@@ -276,6 +262,20 @@ static void delta1010lt_ak4524_lock(struct snd_akm4xxx *ak, int chip)
}
/*
+ * AK4524 on Delta66 rev E to choose the chip address
+ */
+static void delta66e_ak4524_lock(struct snd_akm4xxx *ak, int chip)
+{
+ struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];
+ struct snd_ice1712 *ice = ak->private_data[0];
+
+ snd_ice1712_save_gpio_status(ice);
+ priv->cs_mask =
+ priv->cs_addr = chip == 0 ? ICE1712_DELTA_66E_CS_CHIP_A :
+ ICE1712_DELTA_66E_CS_CHIP_B;
+}
+
+/*
* AK4528 on VX442 to choose the chip mask
*/
static void vx442_ak4524_lock(struct snd_akm4xxx *ak, int chip)
@@ -299,14 +299,13 @@ static void delta_1010_set_rate_val(struct snd_ice1712 *ice, unsigned int rate)
if (rate == 0) /* no hint - S/PDIF input is master, simply return */
return;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
tmp2 = tmp & ~ICE1712_DELTA_DFS;
if (rate > 48000)
tmp2 |= ICE1712_DELTA_DFS;
if (tmp != tmp2)
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp2);
- mutex_unlock(&ice->gpio_mutex);
}
/*
@@ -321,9 +320,9 @@ static void delta_ak4524_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
return;
/* check before reset ak4524 to avoid unnecessary clicks */
- mutex_lock(&ice->gpio_mutex);
- tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
- mutex_unlock(&ice->gpio_mutex);
+ scoped_guard(mutex, &ice->gpio_mutex) {
+ tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+ }
tmp2 = tmp & ~ICE1712_DELTA_DFS;
if (rate > 48000)
tmp2 |= ICE1712_DELTA_DFS;
@@ -332,12 +331,12 @@ static void delta_ak4524_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
/* do it again */
snd_akm4xxx_reset(ak, 1);
- mutex_lock(&ice->gpio_mutex);
- tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ~ICE1712_DELTA_DFS;
- if (rate > 48000)
- tmp |= ICE1712_DELTA_DFS;
- snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
- mutex_unlock(&ice->gpio_mutex);
+ scoped_guard(mutex, &ice->gpio_mutex) {
+ tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ~ICE1712_DELTA_DFS;
+ if (rate > 48000)
+ tmp |= ICE1712_DELTA_DFS;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+ }
snd_akm4xxx_reset(ak, 0);
}
@@ -372,23 +371,22 @@ static void delta_open_spdif(struct snd_ice1712 *ice, struct snd_pcm_substream *
/* set up */
static void delta_setup_spdif(struct snd_ice1712 *ice, int rate)
{
- unsigned long flags;
unsigned int tmp;
int change;
- spin_lock_irqsave(&ice->reg_lock, flags);
- tmp = ice->spdif.cs8403_stream_bits;
- if (tmp & 0x01) /* consumer */
- tmp &= (tmp & 0x01) ? ~0x06 : ~0x18;
- switch (rate) {
- case 32000: tmp |= (tmp & 0x01) ? 0x04 : 0x00; break;
- case 44100: tmp |= (tmp & 0x01) ? 0x00 : 0x10; break;
- case 48000: tmp |= (tmp & 0x01) ? 0x02 : 0x08; break;
- default: tmp |= (tmp & 0x01) ? 0x00 : 0x18; break;
+ scoped_guard(spinlock_irqsave, &ice->reg_lock) {
+ tmp = ice->spdif.cs8403_stream_bits;
+ if (tmp & 0x01) /* consumer */
+ tmp &= (tmp & 0x01) ? ~0x06 : ~0x18;
+ switch (rate) {
+ case 32000: tmp |= (tmp & 0x01) ? 0x04 : 0x00; break;
+ case 44100: tmp |= (tmp & 0x01) ? 0x00 : 0x10; break;
+ case 48000: tmp |= (tmp & 0x01) ? 0x02 : 0x08; break;
+ default: tmp |= (tmp & 0x01) ? 0x00 : 0x18; break;
+ }
+ change = ice->spdif.cs8403_stream_bits != tmp;
+ ice->spdif.cs8403_stream_bits = tmp;
}
- change = ice->spdif.cs8403_stream_bits != tmp;
- ice->spdif.cs8403_stream_bits = tmp;
- spin_unlock_irqrestore(&ice->reg_lock, flags);
if (change)
snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif.stream_ctl->id);
snd_ice1712_delta_cs8403_spdif_write(ice, tmp);
@@ -404,13 +402,14 @@ static int snd_ice1712_delta1010lt_wordclock_status_get(struct snd_kcontrol *kco
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
if (snd_i2c_sendbytes(ice->cs8427, &reg, 1) != 1)
- snd_printk(KERN_ERR "unable to send register 0x%x byte to CS8427\n", reg);
+ dev_err(ice->card->dev,
+ "unable to send register 0x%x byte to CS8427\n", reg);
snd_i2c_readbytes(ice->cs8427, &reg, 1);
ucontrol->value.integer.value[0] = (reg & CS8427_UNLOCK) ? 1 : 0;
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_status __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_status =
{
.access = (SNDRV_CTL_ELEM_ACCESS_READ),
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -423,7 +422,7 @@ static struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_status __devini
* initialize the chips on M-Audio cards
*/
-static struct snd_akm4xxx akm_audiophile __devinitdata = {
+static const struct snd_akm4xxx akm_audiophile = {
.type = SND_AK4528,
.num_adcs = 2,
.num_dacs = 2,
@@ -432,7 +431,7 @@ static struct snd_akm4xxx akm_audiophile __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_audiophile_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_audiophile_priv = {
.caddr = 2,
.cif = 0,
.data_mask = ICE1712_DELTA_AP_DOUT,
@@ -444,7 +443,7 @@ static struct snd_ak4xxx_private akm_audiophile_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_delta410 __devinitdata = {
+static const struct snd_akm4xxx akm_delta410 = {
.type = SND_AK4529,
.num_adcs = 2,
.num_dacs = 8,
@@ -453,7 +452,7 @@ static struct snd_akm4xxx akm_delta410 __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_delta410_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_delta410_priv = {
.caddr = 0,
.cif = 0,
.data_mask = ICE1712_DELTA_AP_DOUT,
@@ -465,7 +464,7 @@ static struct snd_ak4xxx_private akm_delta410_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_delta1010lt __devinitdata = {
+static const struct snd_akm4xxx akm_delta1010lt = {
.type = SND_AK4524,
.num_adcs = 8,
.num_dacs = 8,
@@ -475,7 +474,7 @@ static struct snd_akm4xxx akm_delta1010lt __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_delta1010lt_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_delta1010lt_priv = {
.caddr = 2,
.cif = 0, /* the default level of the CIF pin from AK4524 */
.data_mask = ICE1712_DELTA_1010LT_DOUT,
@@ -487,7 +486,30 @@ static struct snd_ak4xxx_private akm_delta1010lt_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_delta44 __devinitdata = {
+static const struct snd_akm4xxx akm_delta66e = {
+ .type = SND_AK4524,
+ .num_adcs = 4,
+ .num_dacs = 4,
+ .ops = {
+ .lock = delta66e_ak4524_lock,
+ .set_rate_val = delta_ak4524_set_rate_val
+ }
+};
+
+static const struct snd_ak4xxx_private akm_delta66e_priv = {
+ .caddr = 2,
+ .cif = 0, /* the default level of the CIF pin from AK4524 */
+ .data_mask = ICE1712_DELTA_66E_DOUT,
+ .clk_mask = ICE1712_DELTA_66E_CCLK,
+ .cs_mask = 0,
+ .cs_addr = 0, /* set later */
+ .cs_none = 0,
+ .add_flags = 0,
+ .mask_flags = 0,
+};
+
+
+static const struct snd_akm4xxx akm_delta44 = {
.type = SND_AK4524,
.num_adcs = 4,
.num_dacs = 4,
@@ -497,7 +519,7 @@ static struct snd_akm4xxx akm_delta44 __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_delta44_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_delta44_priv = {
.caddr = 2,
.cif = 0, /* the default level of the CIF pin from AK4524 */
.data_mask = ICE1712_DELTA_CODEC_SERIAL_DATA,
@@ -509,7 +531,7 @@ static struct snd_ak4xxx_private akm_delta44_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_vx442 __devinitdata = {
+static const struct snd_akm4xxx akm_vx442 = {
.type = SND_AK4524,
.num_adcs = 4,
.num_dacs = 4,
@@ -519,7 +541,7 @@ static struct snd_akm4xxx akm_vx442 __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_vx442_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_vx442_priv = {
.caddr = 2,
.cif = 0,
.data_mask = ICE1712_VX442_DOUT,
@@ -531,10 +553,60 @@ static struct snd_ak4xxx_private akm_vx442_priv __devinitdata = {
.mask_flags = 0,
};
-static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
+#ifdef CONFIG_PM_SLEEP
+static int snd_ice1712_delta_resume(struct snd_ice1712 *ice)
+{
+ unsigned char akm_img_bak[AK4XXX_IMAGE_SIZE];
+ unsigned char akm_vol_bak[AK4XXX_IMAGE_SIZE];
+
+ /* init spdif */
+ switch (ice->eeprom.subvendor) {
+ case ICE1712_SUBDEVICE_AUDIOPHILE:
+ case ICE1712_SUBDEVICE_DELTA410:
+ case ICE1712_SUBDEVICE_DELTA1010E:
+ case ICE1712_SUBDEVICE_DELTA1010LT:
+ case ICE1712_SUBDEVICE_VX442:
+ case ICE1712_SUBDEVICE_DELTA66E:
+ snd_cs8427_init(ice->i2c, ice->cs8427);
+ break;
+ case ICE1712_SUBDEVICE_DELTA1010:
+ case ICE1712_SUBDEVICE_MEDIASTATION:
+ /* nothing */
+ break;
+ case ICE1712_SUBDEVICE_DELTADIO2496:
+ case ICE1712_SUBDEVICE_DELTA66:
+ /* Set spdif defaults */
+ snd_ice1712_delta_cs8403_spdif_write(ice, ice->spdif.cs8403_bits);
+ break;
+ }
+
+ /* init codec and restore registers */
+ if (ice->akm_codecs) {
+ memcpy(akm_img_bak, ice->akm->images, sizeof(akm_img_bak));
+ memcpy(akm_vol_bak, ice->akm->volumes, sizeof(akm_vol_bak));
+ snd_akm4xxx_init(ice->akm);
+ memcpy(ice->akm->images, akm_img_bak, sizeof(akm_img_bak));
+ memcpy(ice->akm->volumes, akm_vol_bak, sizeof(akm_vol_bak));
+ snd_akm4xxx_reset(ice->akm, 0);
+ }
+
+ return 0;
+}
+
+static int snd_ice1712_delta_suspend(struct snd_ice1712 *ice)
+{
+ if (ice->akm_codecs) /* reset & mute codec */
+ snd_akm4xxx_reset(ice->akm, 1);
+
+ return 0;
+}
+#endif
+
+static int snd_ice1712_delta_init(struct snd_ice1712 *ice)
{
int err;
struct snd_akm4xxx *ak;
+ unsigned char tmp;
if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DELTA1010 &&
ice->eeprom.gpiodir == 0x7b)
@@ -571,11 +643,21 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
ice->num_total_dacs = 4; /* two AK4324 codecs */
break;
case ICE1712_SUBDEVICE_VX442:
- case ICE1712_SUBDEVICE_DELTA66E: /* omni not suported yet */
+ case ICE1712_SUBDEVICE_DELTA66E: /* omni not supported yet */
ice->num_total_dacs = 4;
ice->num_total_adcs = 4;
break;
}
+#ifdef CONFIG_PM_SLEEP
+ ice->pm_resume = snd_ice1712_delta_resume;
+ ice->pm_suspend = snd_ice1712_delta_suspend;
+ ice->pm_suspend_enabled = 1;
+#endif
+ /* initialize the SPI clock to high */
+ tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+ tmp |= ICE1712_DELTA_AP_CCLK;
+ snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+ udelay(5);
/* initialize spdif */
switch (ice->eeprom.subvendor) {
@@ -585,13 +667,15 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
case ICE1712_SUBDEVICE_DELTA1010LT:
case ICE1712_SUBDEVICE_VX442:
case ICE1712_SUBDEVICE_DELTA66E:
- if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) {
- snd_printk(KERN_ERR "unable to create I2C bus\n");
+ err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c);
+ if (err < 0) {
+ dev_err(ice->card->dev, "unable to create I2C bus\n");
return err;
}
ice->i2c->private_data = ice;
ice->i2c->ops = &ap_cs8427_i2c_ops;
- if ((err = snd_ice1712_init_cs8427(ice, CS8427_BASE_ADDR)) < 0)
+ err = snd_ice1712_init_cs8427(ice, CS8427_BASE_ADDR);
+ if (err < 0)
return err;
break;
case ICE1712_SUBDEVICE_DELTA1010:
@@ -600,7 +684,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
break;
case ICE1712_SUBDEVICE_DELTADIO2496:
ice->gpio.set_pro_rate = delta_1010_set_rate_val;
- /* fall thru */
+ fallthrough;
case ICE1712_SUBDEVICE_DELTA66:
ice->spdif.ops.open = delta_open_spdif;
ice->spdif.ops.setup_rate = delta_setup_spdif;
@@ -644,9 +728,11 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
err = snd_ice1712_akm4xxx_init(ak, &akm_delta44, &akm_delta44_priv, ice);
break;
case ICE1712_SUBDEVICE_VX442:
- case ICE1712_SUBDEVICE_DELTA66E:
err = snd_ice1712_akm4xxx_init(ak, &akm_vx442, &akm_vx442_priv, ice);
break;
+ case ICE1712_SUBDEVICE_DELTA66E:
+ err = snd_ice1712_akm4xxx_init(ak, &akm_delta66e, &akm_delta66e_priv, ice);
+ break;
default:
snd_BUG();
return -EINVAL;
@@ -660,19 +746,19 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice)
* additional controls for M-Audio cards
*/
-static struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_select __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_select =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Word Clock Sync", 0, ICE1712_DELTA_WORD_CLOCK_SELECT, 1, 0);
-static struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_select __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_select =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Word Clock Sync", 0, ICE1712_DELTA_1010LT_WORDCLOCK, 0, 0);
-static struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_status __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_delta1010_wordclock_status =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Word Clock Status", 0, ICE1712_DELTA_WORD_CLOCK_STATUS, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE);
-static struct snd_kcontrol_new snd_ice1712_deltadio2496_spdif_in_select __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_deltadio2496_spdif_in_select =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "IEC958 Input Optical", 0, ICE1712_DELTA_SPDIF_INPUT_SELECT, 0, 0);
-static struct snd_kcontrol_new snd_ice1712_delta_spdif_in_status __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_delta_spdif_in_status =
ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Delta IEC958 Input Status", 0, ICE1712_DELTA_SPDIF_IN_STAT, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE);
-static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice)
+static int snd_ice1712_delta_add_controls(struct snd_ice1712 *ice)
{
int err;
@@ -748,7 +834,7 @@ static int __devinit snd_ice1712_delta_add_controls(struct snd_ice1712 *ice)
/* entry point */
-struct snd_ice1712_card_info snd_ice1712_delta_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_ice1712_delta_cards[] = {
{
.subvendor = ICE1712_SUBDEVICE_DELTA1010,
.name = "M Audio Delta 1010",
diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h
index 1a0ac6cd6501..01fcaf7e85b9 100644
--- a/sound/pci/ice1712/delta.h
+++ b/sound/pci/ice1712/delta.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_DELTA_H
#define __SOUND_DELTA_H
@@ -8,21 +9,6 @@
* Digigram VX442
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define DELTA_DEVICE_DESC \
@@ -144,6 +130,17 @@ extern struct snd_ice1712_card_info snd_ice1712_delta_cards[];
#define ICE1712_DELTA_1010LT_CS_NONE 0x50 /* nothing */
#define ICE1712_DELTA_1010LT_WORDCLOCK 0x80 /* sample clock source: 0 = Word Clock Input, 1 = S/PDIF Input ??? */
+/* M-Audio Delta 66 rev. E definitions.
+ * Newer revisions of Delta 66 have CS8427 over SPI for
+ * S/PDIF transceiver instead of CS8404/CS8414. */
+/* 0x01 = DFS */
+#define ICE1712_DELTA_66E_CCLK 0x02 /* SPI clock */
+#define ICE1712_DELTA_66E_DIN 0x04 /* data input */
+#define ICE1712_DELTA_66E_DOUT 0x08 /* data output */
+#define ICE1712_DELTA_66E_CS_CS8427 0x10 /* chip select, low = CS8427 */
+#define ICE1712_DELTA_66E_CS_CHIP_A 0x20 /* AK4524 #0 */
+#define ICE1712_DELTA_66E_CS_CHIP_B 0x40 /* AK4524 #1 */
+
/* Digigram VX442 definitions */
#define ICE1712_VX442_CCLK 0x02 /* SPI clock */
#define ICE1712_VX442_DIN 0x04 /* data input */
diff --git a/sound/pci/ice1712/envy24ht.h b/sound/pci/ice1712/envy24ht.h
index a0c5e009bb4a..10e79c82f86f 100644
--- a/sound/pci/ice1712/envy24ht.h
+++ b/sound/pci/ice1712/envy24ht.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_VT1724_H
#define __SOUND_VT1724_H
@@ -5,21 +6,6 @@
* ALSA driver for ICEnsemble VT1724 (Envy24)
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <sound/control.h>
@@ -66,6 +52,7 @@ enum {
#define VT1724_CFG_CLOCK384 0x40 /* 16.9344Mhz, 44.1kHz*384 */
#define VT1724_CFG_MPU401 0x20 /* MPU401 UARTs */
#define VT1724_CFG_ADC_MASK 0x0c /* one, two or one and S/PDIF, stereo ADCs */
+#define VT1724_CFG_ADC_NONE 0x0c /* no ADCs */
#define VT1724_CFG_DAC_MASK 0x03 /* one, two, three, four stereo DACs */
#define VT1724_REG_AC97_CFG 0x05 /* byte */
diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c
index 6fe35b812040..1dffcb011deb 100644
--- a/sound/pci/ice1712/ews.c
+++ b/sound/pci/ice1712/ews.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
@@ -5,24 +6,8 @@
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
* 2002 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -164,7 +149,8 @@ static int snd_ice1712_ews88mt_chip_select(struct snd_ice1712 *ice, int chip_mas
__error:
snd_i2c_unlock(ice->i2c);
- snd_printk(KERN_ERR "AK4524 chip select failed, check cable to the front module\n");
+ dev_err(ice->card->dev,
+ "AK4524 chip select failed, check cable to the front module\n");
return -EIO;
}
@@ -175,7 +161,7 @@ static void ews88mt_ak4524_lock(struct snd_akm4xxx *ak, int chip)
unsigned char tmp;
/* assert AK4524 CS */
if (snd_ice1712_ews88mt_chip_select(ice, ~(1 << chip) & 0x0f) < 0)
- snd_printk(KERN_ERR "fatal error (ews88mt chip select)\n");
+ dev_err(ice->card->dev, "fatal error (ews88mt chip select)\n");
snd_ice1712_save_gpio_status(ice);
tmp = ICE1712_EWS88_SERIAL_DATA |
ICE1712_EWS88_SERIAL_CLOCK |
@@ -274,15 +260,13 @@ static int ews88_spdif_default_put(struct snd_ice1712 *ice, struct snd_ctl_elem_
int change;
val = snd_cs8404_encode_spdif_bits(&ucontrol->value.iec958);
- spin_lock_irq(&ice->reg_lock);
- change = ice->spdif.cs8403_bits != val;
- ice->spdif.cs8403_bits = val;
- if (change && ice->playback_pro_substream == NULL) {
- spin_unlock_irq(&ice->reg_lock);
- snd_ice1712_ews_cs8404_spdif_write(ice, val);
- } else {
- spin_unlock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ change = ice->spdif.cs8403_bits != val;
+ ice->spdif.cs8403_bits = val;
+ if (!change || ice->playback_pro_substream)
+ return change;
}
+ snd_ice1712_ews_cs8404_spdif_write(ice, val);
return change;
}
@@ -297,15 +281,13 @@ static int ews88_spdif_stream_put(struct snd_ice1712 *ice, struct snd_ctl_elem_v
int change;
val = snd_cs8404_encode_spdif_bits(&ucontrol->value.iec958);
- spin_lock_irq(&ice->reg_lock);
- change = ice->spdif.cs8403_stream_bits != val;
- ice->spdif.cs8403_stream_bits = val;
- if (change && ice->playback_pro_substream != NULL) {
- spin_unlock_irq(&ice->reg_lock);
- snd_ice1712_ews_cs8404_spdif_write(ice, val);
- } else {
- spin_unlock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ change = ice->spdif.cs8403_stream_bits != val;
+ ice->spdif.cs8403_stream_bits = val;
+ if (!change || ice->playback_pro_substream)
+ return change;
}
+ snd_ice1712_ews_cs8404_spdif_write(ice, val);
return change;
}
@@ -319,23 +301,22 @@ static void ews88_open_spdif(struct snd_ice1712 *ice, struct snd_pcm_substream *
/* set up SPDIF for EWS88MT / EWS88D */
static void ews88_setup_spdif(struct snd_ice1712 *ice, int rate)
{
- unsigned long flags;
unsigned char tmp;
int change;
- spin_lock_irqsave(&ice->reg_lock, flags);
- tmp = ice->spdif.cs8403_stream_bits;
- if (tmp & 0x10) /* consumer */
- tmp &= (tmp & 0x01) ? ~0x06 : ~0x60;
- switch (rate) {
- case 32000: tmp |= (tmp & 0x01) ? 0x02 : 0x00; break;
- case 44100: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break;
- case 48000: tmp |= (tmp & 0x01) ? 0x04 : 0x20; break;
- default: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break;
+ scoped_guard(spinlock_irqsave, &ice->reg_lock) {
+ tmp = ice->spdif.cs8403_stream_bits;
+ if (tmp & 0x10) /* consumer */
+ tmp &= (tmp & 0x01) ? ~0x06 : ~0x60;
+ switch (rate) {
+ case 32000: tmp |= (tmp & 0x01) ? 0x02 : 0x00; break;
+ case 44100: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break;
+ case 48000: tmp |= (tmp & 0x01) ? 0x04 : 0x20; break;
+ default: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break;
+ }
+ change = ice->spdif.cs8403_stream_bits != tmp;
+ ice->spdif.cs8403_stream_bits = tmp;
}
- change = ice->spdif.cs8403_stream_bits != tmp;
- ice->spdif.cs8403_stream_bits = tmp;
- spin_unlock_irqrestore(&ice->reg_lock, flags);
if (change)
snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif.stream_ctl->id);
snd_ice1712_ews_cs8404_spdif_write(ice, tmp);
@@ -344,7 +325,7 @@ static void ews88_setup_spdif(struct snd_ice1712 *ice, int rate)
/*
*/
-static struct snd_akm4xxx akm_ews88mt __devinitdata = {
+static const struct snd_akm4xxx akm_ews88mt = {
.num_adcs = 8,
.num_dacs = 8,
.type = SND_AK4524,
@@ -354,7 +335,7 @@ static struct snd_akm4xxx akm_ews88mt __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_ews88mt_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_ews88mt_priv = {
.caddr = 2,
.cif = 1, /* CIF high */
.data_mask = ICE1712_EWS88_SERIAL_DATA,
@@ -366,7 +347,7 @@ static struct snd_ak4xxx_private akm_ews88mt_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_ewx2496 __devinitdata = {
+static const struct snd_akm4xxx akm_ewx2496 = {
.num_adcs = 2,
.num_dacs = 2,
.type = SND_AK4524,
@@ -375,7 +356,7 @@ static struct snd_akm4xxx akm_ewx2496 __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_ewx2496_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_ewx2496_priv = {
.caddr = 2,
.cif = 1, /* CIF high */
.data_mask = ICE1712_EWS88_SERIAL_DATA,
@@ -387,7 +368,7 @@ static struct snd_ak4xxx_private akm_ewx2496_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_6fire __devinitdata = {
+static const struct snd_akm4xxx akm_6fire = {
.num_adcs = 6,
.num_dacs = 6,
.type = SND_AK4524,
@@ -396,7 +377,7 @@ static struct snd_akm4xxx akm_6fire __devinitdata = {
}
};
-static struct snd_ak4xxx_private akm_6fire_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_6fire_priv = {
.caddr = 2,
.cif = 1, /* CIF high */
.data_mask = ICE1712_6FIRE_SERIAL_DATA,
@@ -420,7 +401,7 @@ static struct snd_ak4xxx_private akm_6fire_priv __devinitdata = {
static int snd_ice1712_6fire_write_pca(struct snd_ice1712 *ice, unsigned char reg, unsigned char data);
-static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
+static int snd_ice1712_ews_init(struct snd_ice1712 *ice)
{
int err;
struct snd_akm4xxx *ak;
@@ -456,8 +437,9 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
ice->spec = spec;
/* create i2c */
- if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) {
- snd_printk(KERN_ERR "unable to create I2C bus\n");
+ err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c);
+ if (err < 0) {
+ dev_err(ice->card->dev, "unable to create I2C bus\n");
return err;
}
ice->i2c->private_data = ice;
@@ -470,7 +452,8 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
ICE1712_6FIRE_PCF9554_ADDR,
&spec->i2cdevs[EWS_I2C_6FIRE]);
if (err < 0) {
- snd_printk(KERN_ERR "PCF9554 initialization failed\n");
+ dev_err(ice->card->dev,
+ "PCF9554 initialization failed\n");
return err;
}
snd_ice1712_6fire_write_pca(ice, PCF9554_REG_CONFIG, 0x80);
@@ -496,7 +479,8 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
if (err < 0)
return err;
/* Check if the front module is connected */
- if ((err = snd_ice1712_ews88mt_chip_select(ice, 0x0f)) < 0)
+ err = snd_ice1712_ews88mt_chip_select(ice, 0x0f);
+ if (err < 0)
return err;
break;
case ICE1712_SUBDEVICE_EWS88D:
@@ -511,12 +495,14 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
/* set up SPDIF interface */
switch (ice->eeprom.subvendor) {
case ICE1712_SUBDEVICE_EWX2496:
- if ((err = snd_ice1712_init_cs8427(ice, CS8427_BASE_ADDR)) < 0)
+ err = snd_ice1712_init_cs8427(ice, CS8427_BASE_ADDR);
+ if (err < 0)
return err;
snd_cs8427_reg_write(ice->cs8427, CS8427_REG_RECVERRMASK, CS8427_UNLOCK | CS8427_CONF | CS8427_BIP | CS8427_PAR);
break;
case ICE1712_SUBDEVICE_DMX6FIRE:
- if ((err = snd_ice1712_init_cs8427(ice, ICE1712_6FIRE_CS8427_ADDR)) < 0)
+ err = snd_ice1712_init_cs8427(ice, ICE1712_6FIRE_CS8427_ADDR);
+ if (err < 0)
return err;
snd_cs8427_reg_write(ice->cs8427, CS8427_REG_RECVERRMASK, CS8427_UNLOCK | CS8427_CONF | CS8427_BIP | CS8427_PAR);
break;
@@ -576,16 +562,10 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice)
/* i/o sensitivity - this callback is shared among other devices, too */
static int snd_ice1712_ewx_io_sense_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){
- static char *texts[2] = {
+ static const char * const texts[2] = {
"+4dBu", "-10dBV",
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item >= 2)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int snd_ice1712_ewx_io_sense_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -616,7 +596,7 @@ static int snd_ice1712_ewx_io_sense_put(struct snd_kcontrol *kcontrol, struct sn
return val != nval;
}
-static struct snd_kcontrol_new snd_ice1712_ewx2496_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_ewx2496_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Sensitivity Switch",
@@ -724,7 +704,7 @@ static int snd_ice1712_ews88mt_input_sense_put(struct snd_kcontrol *kcontrol, st
return ndata != data;
}
-static struct snd_kcontrol_new snd_ice1712_ews88mt_input_sense __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_ews88mt_input_sense = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Sensitivity Switch",
.info = snd_ice1712_ewx_io_sense_info,
@@ -733,7 +713,7 @@ static struct snd_kcontrol_new snd_ice1712_ews88mt_input_sense __devinitdata = {
.count = 8,
};
-static struct snd_kcontrol_new snd_ice1712_ews88mt_output_sense __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_ews88mt_output_sense = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Output Sensitivity Switch",
.info = snd_ice1712_ewx_io_sense_info,
@@ -811,7 +791,7 @@ static int snd_ice1712_ews88d_control_put(struct snd_kcontrol *kcontrol, struct
.private_value = xshift | (xinvert << 8),\
}
-static struct snd_kcontrol_new snd_ice1712_ews88d_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_ews88d_controls[] = {
EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "IEC958 Input Optical", 0, 1, 0), /* inverted */
EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Output Optical", 1, 0, 0),
EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT External Master Clock", 2, 0, 0),
@@ -831,11 +811,16 @@ static int snd_ice1712_6fire_read_pca(struct snd_ice1712 *ice, unsigned char reg
snd_i2c_lock(ice->i2c);
byte = reg;
- snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1);
+ if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1) != 1) {
+ snd_i2c_unlock(ice->i2c);
+ dev_err(ice->card->dev, "cannot send pca\n");
+ return -EIO;
+ }
+
byte = 0;
if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1) != 1) {
snd_i2c_unlock(ice->i2c);
- printk(KERN_ERR "cannot read pca\n");
+ dev_err(ice->card->dev, "cannot read pca\n");
return -EIO;
}
snd_i2c_unlock(ice->i2c);
@@ -867,7 +852,8 @@ static int snd_ice1712_6fire_control_get(struct snd_kcontrol *kcontrol, struct s
int invert = (kcontrol->private_value >> 8) & 1;
int data;
- if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
+ data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT);
+ if (data < 0)
return data;
data = (data >> shift) & 1;
if (invert)
@@ -883,7 +869,8 @@ static int snd_ice1712_6fire_control_put(struct snd_kcontrol *kcontrol, struct s
int invert = (kcontrol->private_value >> 8) & 1;
int data, ndata;
- if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
+ data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT);
+ if (data < 0)
return data;
ndata = data & ~(1 << shift);
if (ucontrol->value.integer.value[0])
@@ -899,16 +886,10 @@ static int snd_ice1712_6fire_control_put(struct snd_kcontrol *kcontrol, struct s
static int snd_ice1712_6fire_select_input_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- static char *texts[4] = {
+ static const char * const texts[4] = {
"Internal", "Front Input", "Rear Input", "Wave Table"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item >= 4)
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 4, texts);
}
static int snd_ice1712_6fire_select_input_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@@ -916,7 +897,8 @@ static int snd_ice1712_6fire_select_input_get(struct snd_kcontrol *kcontrol, str
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int data;
- if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
+ data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT);
+ if (data < 0)
return data;
ucontrol->value.integer.value[0] = data & 3;
return 0;
@@ -927,7 +909,8 @@ static int snd_ice1712_6fire_select_input_put(struct snd_kcontrol *kcontrol, str
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int data, ndata;
- if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
+ data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT);
+ if (data < 0)
return data;
ndata = data & ~3;
ndata |= (ucontrol->value.integer.value[0] & 3);
@@ -948,7 +931,7 @@ static int snd_ice1712_6fire_select_input_put(struct snd_kcontrol *kcontrol, str
.private_value = xshift | (xinvert << 8),\
}
-static struct snd_kcontrol_new snd_ice1712_6fire_controls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_6fire_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog Input Select",
@@ -964,7 +947,7 @@ static struct snd_kcontrol_new snd_ice1712_6fire_controls[] __devinitdata = {
};
-static int __devinit snd_ice1712_ews_add_controls(struct snd_ice1712 *ice)
+static int snd_ice1712_ews_add_controls(struct snd_ice1712 *ice)
{
unsigned int idx;
int err;
@@ -1030,7 +1013,7 @@ static int __devinit snd_ice1712_ews_add_controls(struct snd_ice1712 *ice)
/* entry point */
-struct snd_ice1712_card_info snd_ice1712_ews_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_ice1712_ews_cards[] = {
{
.subvendor = ICE1712_SUBDEVICE_EWX2496,
.name = "TerraTec EWX24/96",
diff --git a/sound/pci/ice1712/ews.h b/sound/pci/ice1712/ews.h
index 1c443718af03..aec8dc69a9ea 100644
--- a/sound/pci/ice1712/ews.h
+++ b/sound/pci/ice1712/ews.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_EWS_H
#define __SOUND_EWS_H
@@ -8,21 +9,6 @@
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
* 2002 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define EWS_DEVICE_DESC \
diff --git a/sound/pci/ice1712/hoontech.c b/sound/pci/ice1712/hoontech.c
index 6914189073a4..071f94dc7390 100644
--- a/sound/pci/ice1712/hoontech.c
+++ b/sound/pci/ice1712/hoontech.c
@@ -1,27 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* Lowlevel functions for Hoontech STDSP24
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -40,7 +25,7 @@ struct hoontech_spec {
unsigned short boxconfig[4];
};
-static void __devinit snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, unsigned char byte)
+static void snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, unsigned char byte)
{
byte |= ICE1712_STDSP24_CLOCK_BIT;
udelay(100);
@@ -53,38 +38,38 @@ static void __devinit snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, un
snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
}
-static void __devinit snd_ice1712_stdsp24_darear(struct snd_ice1712 *ice, int activate)
+static void snd_ice1712_stdsp24_darear(struct snd_ice1712 *ice, int activate)
{
struct hoontech_spec *spec = ice->spec;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
ICE1712_STDSP24_0_DAREAR(spec->boxbits, activate);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]);
- mutex_unlock(&ice->gpio_mutex);
}
-static void __devinit snd_ice1712_stdsp24_mute(struct snd_ice1712 *ice, int activate)
+static void snd_ice1712_stdsp24_mute(struct snd_ice1712 *ice, int activate)
{
struct hoontech_spec *spec = ice->spec;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
ICE1712_STDSP24_3_MUTE(spec->boxbits, activate);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
- mutex_unlock(&ice->gpio_mutex);
}
-static void __devinit snd_ice1712_stdsp24_insel(struct snd_ice1712 *ice, int activate)
+static void snd_ice1712_stdsp24_insel(struct snd_ice1712 *ice, int activate)
{
struct hoontech_spec *spec = ice->spec;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
ICE1712_STDSP24_3_INSEL(spec->boxbits, activate);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
- mutex_unlock(&ice->gpio_mutex);
}
-static void __devinit snd_ice1712_stdsp24_box_channel(struct snd_ice1712 *ice, int box, int chn, int activate)
+static void snd_ice1712_stdsp24_box_channel(struct snd_ice1712 *ice, int box, int chn, int activate)
{
struct hoontech_spec *spec = ice->spec;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
/* select box */
ICE1712_STDSP24_0_BOX(spec->boxbits, box);
@@ -126,15 +111,13 @@ static void __devinit snd_ice1712_stdsp24_box_channel(struct snd_ice1712 *ice, i
ICE1712_STDSP24_2_MIDI1(spec->boxbits, 0);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
-
- mutex_unlock(&ice->gpio_mutex);
}
-static void __devinit snd_ice1712_stdsp24_box_midi(struct snd_ice1712 *ice, int box, int master)
+static void snd_ice1712_stdsp24_box_midi(struct snd_ice1712 *ice, int box, int master)
{
struct hoontech_spec *spec = ice->spec;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
/* select box */
ICE1712_STDSP24_0_BOX(spec->boxbits, box);
@@ -154,20 +137,18 @@ static void __devinit snd_ice1712_stdsp24_box_midi(struct snd_ice1712 *ice, int
ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]);
-
- mutex_unlock(&ice->gpio_mutex);
}
-static void __devinit snd_ice1712_stdsp24_midi2(struct snd_ice1712 *ice, int activate)
+static void snd_ice1712_stdsp24_midi2(struct snd_ice1712 *ice, int activate)
{
struct hoontech_spec *spec = ice->spec;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
ICE1712_STDSP24_3_MIDI2(spec->boxbits, activate);
snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]);
- mutex_unlock(&ice->gpio_mutex);
}
-static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice)
+static int hoontech_init(struct snd_ice1712 *ice, bool staudio)
{
struct hoontech_spec *spec;
int box, chn;
@@ -204,7 +185,10 @@ static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice)
ICE1712_STDSP24_3_INSEL(spec->boxbits, 0);
/* let's go - activate only functions in first box */
- spec->config = 0;
+ if (staudio)
+ spec->config = ICE1712_STDSP24_MUTE;
+ else
+ spec->config = 0;
/* ICE1712_STDSP24_MUTE |
ICE1712_STDSP24_INSEL |
ICE1712_STDSP24_DAREAR; */
@@ -227,9 +211,16 @@ static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice)
ICE1712_STDSP24_BOX_CHN4 |
ICE1712_STDSP24_BOX_MIDI1 |
ICE1712_STDSP24_BOX_MIDI2;
- spec->boxconfig[1] =
- spec->boxconfig[2] =
- spec->boxconfig[3] = 0;
+ if (staudio) {
+ spec->boxconfig[1] =
+ spec->boxconfig[2] =
+ spec->boxconfig[3] = spec->boxconfig[0];
+ } else {
+ spec->boxconfig[1] =
+ spec->boxconfig[2] =
+ spec->boxconfig[3] = 0;
+ }
+
snd_ice1712_stdsp24_darear(ice,
(spec->config & ICE1712_STDSP24_DAREAR) ? 1 : 0);
snd_ice1712_stdsp24_mute(ice,
@@ -249,6 +240,16 @@ static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice)
return 0;
}
+static int snd_ice1712_hoontech_init(struct snd_ice1712 *ice)
+{
+ return hoontech_init(ice, false);
+}
+
+static int snd_ice1712_staudio_init(struct snd_ice1712 *ice)
+{
+ return hoontech_init(ice, true);
+}
+
/*
* AK4524 access
*/
@@ -267,10 +268,10 @@ static void stdsp24_ak4524_lock(struct snd_akm4xxx *ak, int chip)
snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~tmp);
}
-static int __devinit snd_ice1712_value_init(struct snd_ice1712 *ice)
+static int snd_ice1712_value_init(struct snd_ice1712 *ice)
{
/* Hoontech STDSP24 with modified hardware */
- static struct snd_akm4xxx akm_stdsp24_mv __devinitdata = {
+ static const struct snd_akm4xxx akm_stdsp24_mv = {
.num_adcs = 2,
.num_dacs = 2,
.type = SND_AK4524,
@@ -279,7 +280,7 @@ static int __devinit snd_ice1712_value_init(struct snd_ice1712 *ice)
}
};
- static struct snd_ak4xxx_private akm_stdsp24_mv_priv __devinitdata = {
+ static const struct snd_ak4xxx_private akm_stdsp24_mv_priv = {
.caddr = 2,
.cif = 1, /* CIF high */
.data_mask = ICE1712_STDSP24_SERIAL_DATA,
@@ -310,14 +311,10 @@ static int __devinit snd_ice1712_value_init(struct snd_ice1712 *ice)
return err;
/* ak4524 controls */
- err = snd_ice1712_akm4xxx_build_controls(ice);
- if (err < 0)
- return err;
-
- return 0;
+ return snd_ice1712_akm4xxx_build_controls(ice);
}
-static int __devinit snd_ice1712_ez8_init(struct snd_ice1712 *ice)
+static int snd_ice1712_ez8_init(struct snd_ice1712 *ice)
{
ice->gpio.write_mask = ice->eeprom.gpiomask;
ice->gpio.direction = ice->eeprom.gpiodir;
@@ -329,7 +326,7 @@ static int __devinit snd_ice1712_ez8_init(struct snd_ice1712 *ice)
/* entry point */
-struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] = {
{
.subvendor = ICE1712_SUBDEVICE_STDSP24,
.name = "Hoontech SoundTrack Audio DSP24",
@@ -356,5 +353,14 @@ struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] __devinitdata = {
.model = "ez8",
.chip_init = snd_ice1712_ez8_init,
},
+ {
+ /* STAudio ADCIII has the same SSID as Hoontech StA DSP24,
+ * thus identified only via the explicit model option
+ */
+ .subvendor = ICE1712_SUBDEVICE_STAUDIO_ADCIII, /* a dummy id */
+ .name = "STAudio ADCIII",
+ .model = "staudio",
+ .chip_init = snd_ice1712_staudio_init,
+ },
{ } /* terminator */
};
diff --git a/sound/pci/ice1712/hoontech.h b/sound/pci/ice1712/hoontech.h
index cc1da1e69ad1..89404ceecbe7 100644
--- a/sound/pci/ice1712/hoontech.h
+++ b/sound/pci/ice1712/hoontech.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_HOONTECH_H
#define __SOUND_HOONTECH_H
@@ -7,21 +8,6 @@
* Lowlevel functions for Hoontech STDSP24
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define HOONTECH_DEVICE_DESC \
@@ -34,6 +20,7 @@
#define ICE1712_SUBDEVICE_STDSP24_VALUE 0x00010010 /* A dummy id for Hoontech SoundTrack Audio DSP 24 Value */
#define ICE1712_SUBDEVICE_STDSP24_MEDIA7_1 0x16141217 /* Hoontech ST Audio DSP24 Media 7.1 */
#define ICE1712_SUBDEVICE_EVENT_EZ8 0x00010001 /* A dummy id for EZ8 */
+#define ICE1712_SUBDEVICE_STAUDIO_ADCIII 0x00010002 /* A dummy id for STAudio ADCIII */
extern struct snd_ice1712_card_info snd_ice1712_hoontech_cards[];
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index 4fc6d8bc637e..1e39b985bef2 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/*
@@ -47,14 +33,13 @@
*/
-#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
@@ -75,19 +60,13 @@
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("ICEnsemble ICE1712 (Envy24)");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{"
- HOONTECH_DEVICE_DESC
- DELTA_DEVICE_DESC
- EWS_DEVICE_DESC
- "{ICEnsemble,Generic ICE1712},"
- "{ICEnsemble,Generic Envy24}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
static char *model[SNDRV_CARDS];
-static int omni[SNDRV_CARDS]; /* Delta44 & 66 Omni I/O support */
-static int cs8427_timeout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 500}; /* CS8427 S/PDIF transciever reset timeout value in msec */
+static bool omni[SNDRV_CARDS]; /* Delta44 & 66 Omni I/O support */
+static int cs8427_timeout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 500}; /* CS8427 S/PDIF transceiver reset timeout value in msec */
static int dxr_enable[SNDRV_CARDS]; /* DXR enable for DMX6FIRE */
module_param_array(index, int, NULL, 0444);
@@ -106,7 +85,7 @@ module_param_array(dxr_enable, int, NULL, 0444);
MODULE_PARM_DESC(dxr_enable, "Enable DXR support for Terratec DMX6FIRE.");
-static DEFINE_PCI_DEVICE_TABLE(snd_ice1712_ids) = {
+static const struct pci_device_id snd_ice1712_ids[] = {
{ PCI_VDEVICE(ICE, PCI_DEVICE_ID_ICE_1712), 0 }, /* ICE1712 */
{ 0, }
};
@@ -270,17 +249,16 @@ static int snd_ice1712_digmix_route_ac97_put(struct snd_kcontrol *kcontrol, stru
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char val, nval;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
val = inb(ICEMT(ice, MONITOR_ROUTECTRL));
nval = val & ~ICE1712_ROUTE_AC97;
if (ucontrol->value.integer.value[0])
nval |= ICE1712_ROUTE_AC97;
outb(nval, ICEMT(ice, MONITOR_ROUTECTRL));
- spin_unlock_irq(&ice->reg_lock);
return val != nval;
}
-static struct snd_kcontrol_new snd_ice1712_mixer_digmix_route_ac97 __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_digmix_route_ac97 = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Digital Mixer To AC97",
.info = snd_ice1712_digmix_route_ac97_info,
@@ -388,14 +366,14 @@ static void setup_cs8427(struct snd_ice1712 *ice, int rate)
/*
* create and initialize callbacks for cs8427 interface
*/
-int __devinit snd_ice1712_init_cs8427(struct snd_ice1712 *ice, int addr)
+int snd_ice1712_init_cs8427(struct snd_ice1712 *ice, int addr)
{
int err;
err = snd_cs8427_create(ice->i2c, addr,
(ice->cs8427_timeout * HZ) / 1000, &ice->cs8427);
if (err < 0) {
- snd_printk(KERN_ERR "CS8427 initialization failed\n");
+ dev_err(ice->card->dev, "CS8427 initialization failed\n");
return err;
}
ice->spdif.ops.open = open_cs8427;
@@ -468,7 +446,7 @@ static irqreturn_t snd_ice1712_interrupt(int irq, void *dev_id)
u16 pbkstatus;
struct snd_pcm_substream *substream;
pbkstatus = inw(ICEDS(ice, INTSTAT));
- /* printk(KERN_DEBUG "pbkstatus = 0x%x\n", pbkstatus); */
+ /* dev_dbg(ice->card->dev, "pbkstatus = 0x%x\n", pbkstatus); */
for (idx = 0; idx < 6; idx++) {
if ((pbkstatus & (3 << (idx * 2))) == 0)
continue;
@@ -495,21 +473,6 @@ static irqreturn_t snd_ice1712_interrupt(int irq, void *dev_id)
/*
- * PCM part - misc
- */
-
-static int snd_ice1712_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_ice1712_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-/*
* PCM part - consumer I/O
*/
@@ -520,7 +483,7 @@ static int snd_ice1712_playback_trigger(struct snd_pcm_substream *substream,
int result = 0;
u32 tmp;
- spin_lock(&ice->reg_lock);
+ guard(spinlock)(&ice->reg_lock);
tmp = snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL);
if (cmd == SNDRV_PCM_TRIGGER_START) {
tmp |= 1;
@@ -534,7 +497,6 @@ static int snd_ice1712_playback_trigger(struct snd_pcm_substream *substream,
result = -EINVAL;
}
snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp);
- spin_unlock(&ice->reg_lock);
return result;
}
@@ -545,7 +507,7 @@ static int snd_ice1712_playback_ds_trigger(struct snd_pcm_substream *substream,
int result = 0;
u32 tmp;
- spin_lock(&ice->reg_lock);
+ guard(spinlock)(&ice->reg_lock);
tmp = snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL);
if (cmd == SNDRV_PCM_TRIGGER_START) {
tmp |= 1;
@@ -559,7 +521,6 @@ static int snd_ice1712_playback_ds_trigger(struct snd_pcm_substream *substream,
result = -EINVAL;
}
snd_ice1712_ds_write(ice, substream->number * 2, ICE1712_DSC_CONTROL, tmp);
- spin_unlock(&ice->reg_lock);
return result;
}
@@ -570,7 +531,7 @@ static int snd_ice1712_capture_trigger(struct snd_pcm_substream *substream,
int result = 0;
u8 tmp;
- spin_lock(&ice->reg_lock);
+ guard(spinlock)(&ice->reg_lock);
tmp = snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL);
if (cmd == SNDRV_PCM_TRIGGER_START) {
tmp |= 1;
@@ -580,7 +541,6 @@ static int snd_ice1712_capture_trigger(struct snd_pcm_substream *substream,
result = -EINVAL;
}
snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
- spin_unlock(&ice->reg_lock);
return result;
}
@@ -600,7 +560,7 @@ static int snd_ice1712_playback_prepare(struct snd_pcm_substream *substream)
rate = (runtime->rate * 8192) / 375;
if (rate > 0x000fffff)
rate = 0x000fffff;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
outb(0, ice->ddma_port + 15);
outb(ICE1712_DMA_MODE_WRITE | ICE1712_DMA_AUTOINIT, ice->ddma_port + 0x0b);
outl(runtime->dma_addr, ice->ddma_port + 0);
@@ -613,7 +573,6 @@ static int snd_ice1712_playback_prepare(struct snd_pcm_substream *substream)
snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_HI, period_size >> 8);
snd_ice1712_write(ice, ICE1712_IREG_PBK_LEFT, 0);
snd_ice1712_write(ice, ICE1712_IREG_PBK_RIGHT, 0);
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -621,10 +580,9 @@ static int snd_ice1712_playback_ds_prepare(struct snd_pcm_substream *substream)
{
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- u32 period_size, buf_size, rate, tmp, chn;
+ u32 period_size, rate, tmp, chn;
period_size = snd_pcm_lib_period_bytes(substream) - 1;
- buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
tmp = 0x0064;
if (snd_pcm_format_width(runtime->format) == 16)
tmp &= ~0x04;
@@ -636,7 +594,7 @@ static int snd_ice1712_playback_ds_prepare(struct snd_pcm_substream *substream)
ice->playback_con_active_buf[substream->number] = 0;
ice->playback_con_virt_addr[substream->number] = runtime->dma_addr;
chn = substream->number * 2;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR0, runtime->dma_addr);
snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT0, period_size);
snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR1, runtime->dma_addr + (runtime->periods > 1 ? period_size + 1 : 0));
@@ -648,7 +606,6 @@ static int snd_ice1712_playback_ds_prepare(struct snd_pcm_substream *substream)
snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_RATE, rate);
snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_VOLUME, 0);
}
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -666,13 +623,13 @@ static int snd_ice1712_capture_prepare(struct snd_pcm_substream *substream)
tmp &= ~0x04;
if (runtime->channels == 2)
tmp &= ~0x02;
- spin_lock_irq(&ice->reg_lock);
- outl(ice->capture_con_virt_addr = runtime->dma_addr, ICEREG(ice, CONCAP_ADDR));
- outw(buf_size, ICEREG(ice, CONCAP_COUNT));
- snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_HI, period_size >> 8);
- snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_LO, period_size & 0xff);
- snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
- spin_unlock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ outl(ice->capture_con_virt_addr = runtime->dma_addr, ICEREG(ice, CONCAP_ADDR));
+ outw(buf_size, ICEREG(ice, CONCAP_COUNT));
+ snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_HI, period_size >> 8);
+ snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_LO, period_size & 0xff);
+ snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
+ }
snd_ac97_set_rate(ice->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
return 0;
}
@@ -686,9 +643,10 @@ static snd_pcm_uframes_t snd_ice1712_playback_pointer(struct snd_pcm_substream *
if (!(snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL) & 1))
return 0;
ptr = runtime->buffer_size - inw(ice->ddma_port + 4);
+ ptr = bytes_to_frames(substream->runtime, ptr);
if (ptr == runtime->buffer_size)
ptr = 0;
- return bytes_to_frames(substream->runtime, ptr);
+ return ptr;
}
static snd_pcm_uframes_t snd_ice1712_playback_ds_pointer(struct snd_pcm_substream *substream)
@@ -705,9 +663,10 @@ static snd_pcm_uframes_t snd_ice1712_playback_ds_pointer(struct snd_pcm_substrea
addr = ICE1712_DSC_ADDR0;
ptr = snd_ice1712_ds_read(ice, substream->number * 2, addr) -
ice->playback_con_virt_addr[substream->number];
+ ptr = bytes_to_frames(substream->runtime, ptr);
if (ptr == substream->runtime->buffer_size)
ptr = 0;
- return bytes_to_frames(substream->runtime, ptr);
+ return ptr;
}
static snd_pcm_uframes_t snd_ice1712_capture_pointer(struct snd_pcm_substream *substream)
@@ -718,9 +677,10 @@ static snd_pcm_uframes_t snd_ice1712_capture_pointer(struct snd_pcm_substream *s
if (!(snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL) & 1))
return 0;
ptr = inl(ICEREG(ice, CONCAP_ADDR)) - ice->capture_con_virt_addr;
+ ptr = bytes_to_frames(substream->runtime, ptr);
if (ptr == substream->runtime->buffer_size)
ptr = 0;
- return bytes_to_frames(substream->runtime, ptr);
+ return ptr;
}
static const struct snd_pcm_hardware snd_ice1712_playback = {
@@ -797,10 +757,9 @@ static int snd_ice1712_playback_ds_open(struct snd_pcm_substream *substream)
ice->playback_con_substream_ds[substream->number] = substream;
runtime->hw = snd_ice1712_playback_ds;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
tmp = inw(ICEDS(ice, INTMASK)) & ~(1 << (substream->number * 2));
outw(tmp, ICEDS(ice, INTMASK));
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -830,10 +789,9 @@ static int snd_ice1712_playback_ds_close(struct snd_pcm_substream *substream)
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
u32 tmp;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
tmp = inw(ICEDS(ice, INTMASK)) | (3 << (substream->number * 2));
outw(tmp, ICEDS(ice, INTMASK));
- spin_unlock_irq(&ice->reg_lock);
ice->playback_con_substream_ds[substream->number] = NULL;
return 0;
}
@@ -846,46 +804,35 @@ static int snd_ice1712_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_ice1712_playback_ops = {
+static const struct snd_pcm_ops snd_ice1712_playback_ops = {
.open = snd_ice1712_playback_open,
.close = snd_ice1712_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ice1712_hw_params,
- .hw_free = snd_ice1712_hw_free,
.prepare = snd_ice1712_playback_prepare,
.trigger = snd_ice1712_playback_trigger,
.pointer = snd_ice1712_playback_pointer,
};
-static struct snd_pcm_ops snd_ice1712_playback_ds_ops = {
+static const struct snd_pcm_ops snd_ice1712_playback_ds_ops = {
.open = snd_ice1712_playback_ds_open,
.close = snd_ice1712_playback_ds_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ice1712_hw_params,
- .hw_free = snd_ice1712_hw_free,
.prepare = snd_ice1712_playback_ds_prepare,
.trigger = snd_ice1712_playback_ds_trigger,
.pointer = snd_ice1712_playback_ds_pointer,
};
-static struct snd_pcm_ops snd_ice1712_capture_ops = {
+static const struct snd_pcm_ops snd_ice1712_capture_ops = {
.open = snd_ice1712_capture_open,
.close = snd_ice1712_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_ice1712_hw_params,
- .hw_free = snd_ice1712_hw_free,
.prepare = snd_ice1712_capture_prepare,
.trigger = snd_ice1712_capture_trigger,
.pointer = snd_ice1712_capture_pointer,
};
-static int __devinit snd_ice1712_pcm(struct snd_ice1712 *ice, int device, struct snd_pcm **rpcm)
+static int snd_ice1712_pcm(struct snd_ice1712 *ice, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
err = snd_pcm_new(ice->card, "ICE1712 consumer", device, 1, 1, &pcm);
if (err < 0)
return err;
@@ -895,27 +842,23 @@ static int __devinit snd_ice1712_pcm(struct snd_ice1712 *ice, int device, struct
pcm->private_data = ice;
pcm->info_flags = 0;
- strcpy(pcm->name, "ICE1712 consumer");
+ strscpy(pcm->name, "ICE1712 consumer");
ice->pcm = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(ice->pci), 64*1024, 64*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &ice->pci->dev, 64*1024, 64*1024);
- printk(KERN_WARNING "Consumer PCM code does not work well at the moment --jk\n");
+ dev_warn(ice->card->dev,
+ "Consumer PCM code does not work well at the moment --jk\n");
return 0;
}
-static int __devinit snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device, struct snd_pcm **rpcm)
+static int snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
err = snd_pcm_new(ice->card, "ICE1712 consumer (DS)", device, 6, 0, &pcm);
if (err < 0)
return err;
@@ -924,14 +867,11 @@ static int __devinit snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device, str
pcm->private_data = ice;
pcm->info_flags = 0;
- strcpy(pcm->name, "ICE1712 consumer (DS)");
+ strscpy(pcm->name, "ICE1712 consumer (DS)");
ice->pcm_ds = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(ice->pci), 64*1024, 128*1024);
-
- if (rpcm)
- *rpcm = pcm;
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &ice->pci->dev, 64*1024, 128*1024);
return 0;
}
@@ -940,10 +880,10 @@ static int __devinit snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device, str
* PCM code - professional part (multitrack)
*/
-static unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000,
+static const unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000 };
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
@@ -963,14 +903,13 @@ static int snd_ice1712_pro_trigger(struct snd_pcm_substream *substream,
return -EINVAL;
what = ICE1712_PLAYBACK_PAUSE;
snd_pcm_trigger_done(substream, substream);
- spin_lock(&ice->reg_lock);
+ guard(spinlock)(&ice->reg_lock);
old = inl(ICEMT(ice, PLAYBACK_CONTROL));
if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
old |= what;
else
old &= ~what;
outl(old, ICEMT(ice, PLAYBACK_CONTROL));
- spin_unlock(&ice->reg_lock);
break;
}
case SNDRV_PCM_TRIGGER_START:
@@ -989,14 +928,13 @@ static int snd_ice1712_pro_trigger(struct snd_pcm_substream *substream,
snd_pcm_trigger_done(s, substream);
}
}
- spin_lock(&ice->reg_lock);
+ guard(spinlock)(&ice->reg_lock);
old = inl(ICEMT(ice, PLAYBACK_CONTROL));
if (cmd == SNDRV_PCM_TRIGGER_START)
old |= what;
else
old &= ~what;
outl(old, ICEMT(ice, PLAYBACK_CONTROL));
- spin_unlock(&ice->reg_lock);
break;
}
default:
@@ -1009,7 +947,6 @@ static int snd_ice1712_pro_trigger(struct snd_pcm_substream *substream,
*/
static void snd_ice1712_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate, int force)
{
- unsigned long flags;
unsigned char val, old;
unsigned int i;
@@ -1034,22 +971,21 @@ static void snd_ice1712_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
break;
}
- spin_lock_irqsave(&ice->reg_lock, flags);
- if (inb(ICEMT(ice, PLAYBACK_CONTROL)) & (ICE1712_CAPTURE_START_SHADOW|
- ICE1712_PLAYBACK_PAUSE|
- ICE1712_PLAYBACK_START)) {
-__out:
- spin_unlock_irqrestore(&ice->reg_lock, flags);
- return;
- }
- if (!force && is_pro_rate_locked(ice))
- goto __out;
+ scoped_guard(spinlock_irqsave, &ice->reg_lock) {
+ if (inb(ICEMT(ice, PLAYBACK_CONTROL)) & (ICE1712_CAPTURE_START_SHADOW|
+ ICE1712_PLAYBACK_PAUSE|
+ ICE1712_PLAYBACK_START))
+ return;
+ if (!force && is_pro_rate_locked(ice))
+ return;
- old = inb(ICEMT(ice, RATE));
- if (!force && old == val)
- goto __out;
- outb(val, ICEMT(ice, RATE));
- spin_unlock_irqrestore(&ice->reg_lock, flags);
+ old = inb(ICEMT(ice, RATE));
+ if (!force && old == val)
+ return;
+
+ ice->cur_rate = rate;
+ outb(val, ICEMT(ice, RATE));
+ }
if (ice->gpio.set_pro_rate)
ice->gpio.set_pro_rate(ice, rate);
@@ -1066,11 +1002,10 @@ static int snd_ice1712_playback_pro_prepare(struct snd_pcm_substream *substream)
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
ice->playback_pro_size = snd_pcm_lib_buffer_bytes(substream);
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
outl(substream->runtime->dma_addr, ICEMT(ice, PLAYBACK_ADDR));
outw((ice->playback_pro_size >> 2) - 1, ICEMT(ice, PLAYBACK_SIZE));
outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, PLAYBACK_COUNT));
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -1081,7 +1016,7 @@ static int snd_ice1712_playback_pro_hw_params(struct snd_pcm_substream *substrea
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
snd_ice1712_set_pro_rate(ice, params_rate(hw_params), 0);
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ return 0;
}
static int snd_ice1712_capture_pro_prepare(struct snd_pcm_substream *substream)
@@ -1089,11 +1024,10 @@ static int snd_ice1712_capture_pro_prepare(struct snd_pcm_substream *substream)
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
ice->capture_pro_size = snd_pcm_lib_buffer_bytes(substream);
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
outl(substream->runtime->dma_addr, ICEMT(ice, CAPTURE_ADDR));
outw((ice->capture_pro_size >> 2) - 1, ICEMT(ice, CAPTURE_SIZE));
outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, CAPTURE_COUNT));
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -1103,7 +1037,7 @@ static int snd_ice1712_capture_pro_hw_params(struct snd_pcm_substream *substream
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
snd_ice1712_set_pro_rate(ice, params_rate(hw_params), 0);
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ return 0;
}
static snd_pcm_uframes_t snd_ice1712_playback_pro_pointer(struct snd_pcm_substream *substream)
@@ -1114,9 +1048,10 @@ static snd_pcm_uframes_t snd_ice1712_playback_pro_pointer(struct snd_pcm_substre
if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_PLAYBACK_START))
return 0;
ptr = ice->playback_pro_size - (inw(ICEMT(ice, PLAYBACK_SIZE)) << 2);
+ ptr = bytes_to_frames(substream->runtime, ptr);
if (ptr == substream->runtime->buffer_size)
ptr = 0;
- return bytes_to_frames(substream->runtime, ptr);
+ return ptr;
}
static snd_pcm_uframes_t snd_ice1712_capture_pro_pointer(struct snd_pcm_substream *substream)
@@ -1127,9 +1062,10 @@ static snd_pcm_uframes_t snd_ice1712_capture_pro_pointer(struct snd_pcm_substrea
if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_CAPTURE_START_SHADOW))
return 0;
ptr = ice->capture_pro_size - (inw(ICEMT(ice, CAPTURE_SIZE)) << 2);
+ ptr = bytes_to_frames(substream->runtime, ptr);
if (ptr == substream->runtime->buffer_size)
ptr = 0;
- return bytes_to_frames(substream->runtime, ptr);
+ return ptr;
}
static const struct snd_pcm_hardware snd_ice1712_playback_pro = {
@@ -1232,35 +1168,29 @@ static int snd_ice1712_capture_pro_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_ice1712_playback_pro_ops = {
+static const struct snd_pcm_ops snd_ice1712_playback_pro_ops = {
.open = snd_ice1712_playback_pro_open,
.close = snd_ice1712_playback_pro_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_ice1712_playback_pro_hw_params,
- .hw_free = snd_ice1712_hw_free,
.prepare = snd_ice1712_playback_pro_prepare,
.trigger = snd_ice1712_pro_trigger,
.pointer = snd_ice1712_playback_pro_pointer,
};
-static struct snd_pcm_ops snd_ice1712_capture_pro_ops = {
+static const struct snd_pcm_ops snd_ice1712_capture_pro_ops = {
.open = snd_ice1712_capture_pro_open,
.close = snd_ice1712_capture_pro_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_ice1712_capture_pro_hw_params,
- .hw_free = snd_ice1712_hw_free,
.prepare = snd_ice1712_capture_pro_prepare,
.trigger = snd_ice1712_pro_trigger,
.pointer = snd_ice1712_capture_pro_pointer,
};
-static int __devinit snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device, struct snd_pcm **rpcm)
+static int snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device)
{
struct snd_pcm *pcm;
int err;
- if (rpcm)
- *rpcm = NULL;
err = snd_pcm_new(ice->card, "ICE1712 multi", device, 1, 1, &pcm);
if (err < 0)
return err;
@@ -1270,14 +1200,12 @@ static int __devinit snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device,
pcm->private_data = ice;
pcm->info_flags = 0;
- strcpy(pcm->name, "ICE1712 multi");
+ strscpy(pcm->name, "ICE1712 multi");
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(ice->pci), 256*1024, 256*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &ice->pci->dev, 256*1024, 256*1024);
ice->pcm_pro = pcm;
- if (rpcm)
- *rpcm = pcm;
if (ice->cs8427) {
/* assign channels to iec958 */
@@ -1288,10 +1216,7 @@ static int __devinit snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device,
return err;
}
- err = snd_ice1712_build_pro_mixer(ice);
- if (err < 0)
- return err;
- return 0;
+ return snd_ice1712_build_pro_mixer(ice);
}
/*
@@ -1317,12 +1242,11 @@ static int snd_ice1712_pro_mixer_switch_get(struct snd_kcontrol *kcontrol, struc
int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
kcontrol->private_value;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
ucontrol->value.integer.value[0] =
!((ice->pro_volumes[priv_idx] >> 15) & 1);
ucontrol->value.integer.value[1] =
!((ice->pro_volumes[priv_idx] >> 31) & 1);
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -1335,12 +1259,11 @@ static int snd_ice1712_pro_mixer_switch_put(struct snd_kcontrol *kcontrol, struc
nval = (ucontrol->value.integer.value[0] ? 0 : 0x00008000) |
(ucontrol->value.integer.value[1] ? 0 : 0x80000000);
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
nval |= ice->pro_volumes[priv_idx] & ~0x80008000;
change = nval != ice->pro_volumes[priv_idx];
ice->pro_volumes[priv_idx] = nval;
snd_ice1712_update_volume(ice, priv_idx);
- spin_unlock_irq(&ice->reg_lock);
return change;
}
@@ -1359,12 +1282,11 @@ static int snd_ice1712_pro_mixer_volume_get(struct snd_kcontrol *kcontrol, struc
int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +
kcontrol->private_value;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
ucontrol->value.integer.value[0] =
(ice->pro_volumes[priv_idx] >> 0) & 127;
ucontrol->value.integer.value[1] =
(ice->pro_volumes[priv_idx] >> 16) & 127;
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -1377,18 +1299,17 @@ static int snd_ice1712_pro_mixer_volume_put(struct snd_kcontrol *kcontrol, struc
nval = (ucontrol->value.integer.value[0] & 127) |
((ucontrol->value.integer.value[1] & 127) << 16);
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
nval |= ice->pro_volumes[priv_idx] & ~0x007f007f;
change = nval != ice->pro_volumes[priv_idx];
ice->pro_volumes[priv_idx] = nval;
snd_ice1712_update_volume(ice, priv_idx);
- spin_unlock_irq(&ice->reg_lock);
return change;
}
static const DECLARE_TLV_DB_SCALE(db_scale_playback, -14400, 150, 0);
-static struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Playback Switch",
@@ -1412,7 +1333,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_playback_ctrls[] __devinitdata
},
};
-static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "H/W Multi Capture Switch",
.info = snd_ice1712_pro_mixer_switch_info,
@@ -1421,7 +1342,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch __devinit
.private_value = 10,
};
-static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("Multi ", CAPTURE, SWITCH),
.info = snd_ice1712_pro_mixer_switch_info,
@@ -1431,7 +1352,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch __devinitd
.count = 2,
};
-static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -1443,7 +1364,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume __devinit
.tlv = { .p = db_scale_playback }
};
-static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("Multi ", CAPTURE, VOLUME),
.info = snd_ice1712_pro_mixer_volume_info,
@@ -1453,7 +1374,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume __devinitd
.count = 2,
};
-static int __devinit snd_ice1712_build_pro_mixer(struct snd_ice1712 *ice)
+static int snd_ice1712_build_pro_mixer(struct snd_ice1712 *ice)
{
struct snd_card *card = ice->card;
unsigned int idx;
@@ -1512,16 +1433,16 @@ static void snd_ice1712_mixer_free_ac97(struct snd_ac97 *ac97)
ice->ac97 = NULL;
}
-static int __devinit snd_ice1712_ac97_mixer(struct snd_ice1712 *ice)
+static int snd_ice1712_ac97_mixer(struct snd_ice1712 *ice)
{
int err, bus_num = 0;
struct snd_ac97_template ac97;
struct snd_ac97_bus *pbus;
- static struct snd_ac97_bus_ops con_ops = {
+ static const struct snd_ac97_bus_ops con_ops = {
.write = snd_ice1712_ac97_write,
.read = snd_ice1712_ac97_read,
};
- static struct snd_ac97_bus_ops pro_ops = {
+ static const struct snd_ac97_bus_ops pro_ops = {
.write = snd_ice1712_pro_ac97_write,
.read = snd_ice1712_pro_ac97_read,
};
@@ -1535,12 +1456,12 @@ static int __devinit snd_ice1712_ac97_mixer(struct snd_ice1712 *ice)
ac97.private_free = snd_ice1712_mixer_free_ac97;
err = snd_ac97_mixer(pbus, &ac97, &ice->ac97);
if (err < 0)
- printk(KERN_WARNING "ice1712: cannot initialize ac97 for consumer, skipped\n");
+ dev_warn(ice->card->dev,
+ "cannot initialize ac97 for consumer, skipped\n");
else {
- err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice));
- if (err < 0)
- return err;
- return 0;
+ return snd_ctl_add(ice->card,
+ snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97,
+ ice));
}
}
@@ -1553,7 +1474,8 @@ static int __devinit snd_ice1712_ac97_mixer(struct snd_ice1712 *ice)
ac97.private_free = snd_ice1712_mixer_free_ac97;
err = snd_ac97_mixer(pbus, &ac97, &ice->ac97);
if (err < 0)
- printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n");
+ dev_warn(ice->card->dev,
+ "cannot initialize pro ac97, skipped\n");
else
return 0;
}
@@ -1611,12 +1533,9 @@ static void snd_ice1712_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, " GPIO_DIRECTION : 0x%02x\n", (unsigned)snd_ice1712_read(ice, ICE1712_IREG_GPIO_DIRECTION));
}
-static void __devinit snd_ice1712_proc_init(struct snd_ice1712 *ice)
+static void snd_ice1712_proc_init(struct snd_ice1712 *ice)
{
- struct snd_info_entry *entry;
-
- if (!snd_card_proc_new(ice->card, "ice1712", &entry))
- snd_info_set_text_ops(entry, ice, snd_ice1712_proc_read);
+ snd_card_ro_proc_new(ice->card, "ice1712", ice, snd_ice1712_proc_read);
}
/*
@@ -1640,7 +1559,7 @@ static int snd_ice1712_eeprom_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_eeprom __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_eeprom = {
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "ICE1712 EEPROM",
.access = SNDRV_CTL_ELEM_ACCESS_READ,
@@ -1676,7 +1595,7 @@ static int snd_ice1712_spdif_default_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_spdif_default __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_spdif_default =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
@@ -1727,7 +1646,7 @@ static int snd_ice1712_spdif_maskp_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_spdif_maskc __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_spdif_maskc =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1736,7 +1655,7 @@ static struct snd_kcontrol_new snd_ice1712_spdif_maskc __devinitdata =
.get = snd_ice1712_spdif_maskc_get,
};
-static struct snd_kcontrol_new snd_ice1712_spdif_maskp __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_spdif_maskp =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1763,7 +1682,7 @@ static int snd_ice1712_spdif_stream_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_spdif_stream __devinitdata =
+static const struct snd_kcontrol_new snd_ice1712_spdif_stream =
{
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_INACTIVE),
@@ -1830,13 +1749,7 @@ static int snd_ice1712_pro_internal_clock_info(struct snd_kcontrol *kcontrol,
"96000", /* 12: 7 */
"IEC958 Input", /* 13: -- */
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 14;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 14, texts);
}
static int snd_ice1712_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
@@ -1848,7 +1761,7 @@ static int snd_ice1712_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
};
unsigned char val;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
if (is_spdif_master(ice)) {
ucontrol->value.enumerated.item[0] = 13;
} else {
@@ -1859,7 +1772,6 @@ static int snd_ice1712_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
}
ucontrol->value.enumerated.item[0] = val;
}
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -1894,7 +1806,7 @@ static int snd_ice1712_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ice1712_pro_internal_clock __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_pro_internal_clock = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Internal Clock",
.info = snd_ice1712_pro_internal_clock_info,
@@ -1921,13 +1833,7 @@ static int snd_ice1712_pro_internal_clock_default_info(struct snd_kcontrol *kcon
"96000", /* 12: 7 */
/* "IEC958 Input", 13: -- */
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 13;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 13, texts);
}
static int snd_ice1712_pro_internal_clock_default_get(struct snd_kcontrol *kcontrol,
@@ -1965,7 +1871,7 @@ static int snd_ice1712_pro_internal_clock_default_put(struct snd_kcontrol *kcont
return change;
}
-static struct snd_kcontrol_new snd_ice1712_pro_internal_clock_default __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_pro_internal_clock_default = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Internal Clock Default",
.info = snd_ice1712_pro_internal_clock_default_info,
@@ -1989,14 +1895,13 @@ static int snd_ice1712_pro_rate_locking_put(struct snd_kcontrol *kcontrol,
int change = 0, nval;
nval = ucontrol->value.integer.value[0] ? 1 : 0;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
change = PRO_RATE_LOCKED != nval;
PRO_RATE_LOCKED = nval;
- spin_unlock_irq(&ice->reg_lock);
return change;
}
-static struct snd_kcontrol_new snd_ice1712_pro_rate_locking __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_pro_rate_locking = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Locking",
.info = snd_ice1712_pro_rate_locking_info,
@@ -2020,14 +1925,13 @@ static int snd_ice1712_pro_rate_reset_put(struct snd_kcontrol *kcontrol,
int change = 0, nval;
nval = ucontrol->value.integer.value[0] ? 1 : 0;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
change = PRO_RATE_RESET != nval;
PRO_RATE_RESET = nval;
- spin_unlock_irq(&ice->reg_lock);
return change;
}
-static struct snd_kcontrol_new snd_ice1712_pro_rate_reset __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_pro_rate_reset = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Reset",
.info = snd_ice1712_pro_rate_reset_info,
@@ -2048,15 +1952,8 @@ static int snd_ice1712_pro_route_info(struct snd_kcontrol *kcontrol,
"IEC958 In L", "IEC958 In R", /* 9-10 */
"Digital Mixer", /* 11 - optional */
};
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items =
- snd_ctl_get_ioffidx(kcontrol, &uinfo->id) < 2 ? 12 : 11;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ int num_items = snd_ctl_get_ioffidx(kcontrol, &uinfo->id) < 2 ? 12 : 11;
+ return snd_ctl_enum_info(uinfo, 1, num_items, texts);
}
static int snd_ice1712_pro_route_analog_get(struct snd_kcontrol *kcontrol,
@@ -2066,10 +1963,10 @@ static int snd_ice1712_pro_route_analog_get(struct snd_kcontrol *kcontrol,
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
unsigned int val, cval;
- spin_lock_irq(&ice->reg_lock);
- val = inw(ICEMT(ice, ROUTE_PSDOUT03));
- cval = inl(ICEMT(ice, ROUTE_CAPTURE));
- spin_unlock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ val = inw(ICEMT(ice, ROUTE_PSDOUT03));
+ cval = inl(ICEMT(ice, ROUTE_CAPTURE));
+ }
val >>= ((idx % 2) * 8) + ((idx / 2) * 2);
val &= 3;
@@ -2103,35 +2000,35 @@ static int snd_ice1712_pro_route_analog_put(struct snd_kcontrol *kcontrol,
else
nval = 0; /* pcm */
shift = ((idx % 2) * 8) + ((idx / 2) * 2);
- spin_lock_irq(&ice->reg_lock);
- val = old_val = inw(ICEMT(ice, ROUTE_PSDOUT03));
- val &= ~(0x03 << shift);
- val |= nval << shift;
- change = val != old_val;
- if (change)
- outw(val, ICEMT(ice, ROUTE_PSDOUT03));
- spin_unlock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ val = old_val = inw(ICEMT(ice, ROUTE_PSDOUT03));
+ val &= ~(0x03 << shift);
+ val |= nval << shift;
+ change = val != old_val;
+ if (change)
+ outw(val, ICEMT(ice, ROUTE_PSDOUT03));
+ }
if (nval < 2) /* dig mixer of pcm */
return change;
/* update CAPTURE */
- spin_lock_irq(&ice->reg_lock);
- val = old_val = inl(ICEMT(ice, ROUTE_CAPTURE));
- shift = ((idx / 2) * 8) + ((idx % 2) * 4);
- if (nval == 2) { /* analog in */
- nval = ucontrol->value.enumerated.item[0] - 1;
- val &= ~(0x07 << shift);
- val |= nval << shift;
- } else { /* spdif in */
- nval = (ucontrol->value.enumerated.item[0] - 9) << 3;
- val &= ~(0x08 << shift);
- val |= nval << shift;
- }
- if (val != old_val) {
- change = 1;
- outl(val, ICEMT(ice, ROUTE_CAPTURE));
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ val = old_val = inl(ICEMT(ice, ROUTE_CAPTURE));
+ shift = ((idx / 2) * 8) + ((idx % 2) * 4);
+ if (nval == 2) { /* analog in */
+ nval = ucontrol->value.enumerated.item[0] - 1;
+ val &= ~(0x07 << shift);
+ val |= nval << shift;
+ } else { /* spdif in */
+ nval = (ucontrol->value.enumerated.item[0] - 9) << 3;
+ val &= ~(0x08 << shift);
+ val |= nval << shift;
+ }
+ if (val != old_val) {
+ change = 1;
+ outl(val, ICEMT(ice, ROUTE_CAPTURE));
+ }
}
- spin_unlock_irq(&ice->reg_lock);
return change;
}
@@ -2164,7 +2061,7 @@ static int snd_ice1712_pro_route_spdif_put(struct snd_kcontrol *kcontrol,
unsigned int val, old_val, nval;
/* update SPDOUT */
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
val = old_val = inw(ICEMT(ice, ROUTE_SPDOUT));
if (ucontrol->value.enumerated.item[0] >= 11)
nval = 1;
@@ -2190,11 +2087,10 @@ static int snd_ice1712_pro_route_spdif_put(struct snd_kcontrol *kcontrol,
change = val != old_val;
if (change)
outw(val, ICEMT(ice, ROUTE_SPDOUT));
- spin_unlock_irq(&ice->reg_lock);
return change;
}
-static struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "H/W Playback Route",
.info = snd_ice1712_pro_route_info,
@@ -2202,7 +2098,7 @@ static struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route __devinitdata
.put = snd_ice1712_pro_route_analog_put,
};
-static struct snd_kcontrol_new snd_ice1712_mixer_pro_spdif_route __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_pro_spdif_route = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, NONE) "Route",
.info = snd_ice1712_pro_route_info,
@@ -2237,14 +2133,13 @@ static int snd_ice1712_pro_volume_rate_put(struct snd_kcontrol *kcontrol,
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int change;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
change = inb(ICEMT(ice, MONITOR_RATE)) != ucontrol->value.integer.value[0];
outb(ucontrol->value.integer.value[0], ICEMT(ice, MONITOR_RATE));
- spin_unlock_irq(&ice->reg_lock);
return change;
}
-static struct snd_kcontrol_new snd_ice1712_mixer_pro_volume_rate __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_pro_volume_rate = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Volume Rate",
.info = snd_ice1712_pro_volume_rate_info,
@@ -2268,16 +2163,15 @@ static int snd_ice1712_pro_peak_get(struct snd_kcontrol *kcontrol,
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int idx;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
for (idx = 0; idx < 22; idx++) {
outb(idx, ICEMT(ice, MONITOR_PEAKINDEX));
ucontrol->value.integer.value[idx] = inb(ICEMT(ice, MONITOR_PEAKDATA));
}
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_mixer_pro_peak __devinitdata = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_pro_peak = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Multi Track Peak",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
@@ -2292,16 +2186,16 @@ static struct snd_kcontrol_new snd_ice1712_mixer_pro_peak __devinitdata = {
/*
* list of available boards
*/
-static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
+static const struct snd_ice1712_card_info *card_tables[] = {
snd_ice1712_hoontech_cards,
snd_ice1712_delta_cards,
snd_ice1712_ews_cards,
NULL,
};
-static unsigned char __devinit snd_ice1712_read_i2c(struct snd_ice1712 *ice,
- unsigned char dev,
- unsigned char addr)
+static unsigned char snd_ice1712_read_i2c(struct snd_ice1712 *ice,
+ unsigned char dev,
+ unsigned char addr)
{
long t = 0x10000;
@@ -2311,12 +2205,12 @@ static unsigned char __devinit snd_ice1712_read_i2c(struct snd_ice1712 *ice,
return inb(ICEREG(ice, I2C_DATA));
}
-static int __devinit snd_ice1712_read_eeprom(struct snd_ice1712 *ice,
- const char *modelname)
+static int snd_ice1712_read_eeprom(struct snd_ice1712 *ice,
+ const char *modelname)
{
- int dev = 0xa0; /* EEPROM device address */
+ int dev = ICE_I2C_EEPROM_ADDR; /* I2C EEPROM device address */
unsigned int i, size;
- struct snd_ice1712_card_info * const *tbl, *c;
+ const struct snd_ice1712_card_info * const *tbl, *c;
if (!modelname || !*modelname) {
ice->eeprom.subvendor = 0;
@@ -2333,7 +2227,8 @@ static int __devinit snd_ice1712_read_eeprom(struct snd_ice1712 *ice,
pci_read_config_word(ice->pci, PCI_SUBSYSTEM_ID, &device);
ice->eeprom.subvendor = ((unsigned int)swab16(vendor) << 16) | swab16(device);
if (ice->eeprom.subvendor == 0 || ice->eeprom.subvendor == (unsigned int)-1) {
- printk(KERN_ERR "ice1712: No valid ID is found\n");
+ dev_err(ice->card->dev,
+ "No valid ID is found\n");
return -ENXIO;
}
}
@@ -2341,21 +2236,22 @@ static int __devinit snd_ice1712_read_eeprom(struct snd_ice1712 *ice,
for (tbl = card_tables; *tbl; tbl++) {
for (c = *tbl; c->subvendor; c++) {
if (modelname && c->model && !strcmp(modelname, c->model)) {
- printk(KERN_INFO "ice1712: Using board model %s\n", c->name);
+ dev_info(ice->card->dev,
+ "Using board model %s\n", c->name);
ice->eeprom.subvendor = c->subvendor;
} else if (c->subvendor != ice->eeprom.subvendor)
continue;
if (!c->eeprom_size || !c->eeprom_data)
goto found;
/* if the EEPROM is given by the driver, use it */
- snd_printdd("using the defined eeprom..\n");
+ dev_dbg(ice->card->dev, "using the defined eeprom..\n");
ice->eeprom.version = 1;
ice->eeprom.size = c->eeprom_size + 6;
memcpy(ice->eeprom.data, c->eeprom_data, c->eeprom_size);
goto read_skipped;
}
}
- printk(KERN_WARNING "ice1712: No matching model found for ID 0x%x\n",
+ dev_warn(ice->card->dev, "No matching model found for ID 0x%x\n",
ice->eeprom.subvendor);
found:
@@ -2363,12 +2259,13 @@ static int __devinit snd_ice1712_read_eeprom(struct snd_ice1712 *ice,
if (ice->eeprom.size < 6)
ice->eeprom.size = 32; /* FIXME: any cards without the correct size? */
else if (ice->eeprom.size > 32) {
- snd_printk(KERN_ERR "invalid EEPROM (size = %i)\n", ice->eeprom.size);
+ dev_err(ice->card->dev,
+ "invalid EEPROM (size = %i)\n", ice->eeprom.size);
return -EIO;
}
ice->eeprom.version = snd_ice1712_read_i2c(ice, dev, 0x05);
if (ice->eeprom.version != 1) {
- snd_printk(KERN_ERR "invalid EEPROM version %i\n",
+ dev_err(ice->card->dev, "invalid EEPROM version %i\n",
ice->eeprom.version);
/* return -EIO; */
}
@@ -2386,7 +2283,7 @@ static int __devinit snd_ice1712_read_eeprom(struct snd_ice1712 *ice,
-static int __devinit snd_ice1712_chip_init(struct snd_ice1712 *ice)
+static int snd_ice1712_chip_init(struct snd_ice1712 *ice)
{
outb(ICE1712_RESET | ICE1712_NATIVE, ICEREG(ice, CONTROL));
udelay(200);
@@ -2403,7 +2300,8 @@ static int __devinit snd_ice1712_chip_init(struct snd_ice1712 *ice)
pci_write_config_byte(ice->pci, 0x61, ice->eeprom.data[ICE_EEP1_ACLINK]);
pci_write_config_byte(ice->pci, 0x62, ice->eeprom.data[ICE_EEP1_I2SID]);
pci_write_config_byte(ice->pci, 0x63, ice->eeprom.data[ICE_EEP1_SPDIF]);
- if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24) {
+ if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24 &&
+ ice->eeprom.subvendor != ICE1712_SUBDEVICE_STAUDIO_ADCIII) {
ice->gpio.write_mask = ice->eeprom.gpiomask;
ice->gpio.direction = ice->eeprom.gpiodir;
snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK,
@@ -2429,39 +2327,50 @@ static int __devinit snd_ice1712_chip_init(struct snd_ice1712 *ice)
snd_ice1712_write(ice, ICE1712_IREG_CONSUMER_POWERDOWN, 0);
}
snd_ice1712_set_pro_rate(ice, 48000, 1);
+ /* unmask used interrupts */
+ outb(((ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) == 0 ?
+ ICE1712_IRQ_MPU2 : 0) |
+ ((ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_NO_CON_AC97) ?
+ ICE1712_IRQ_PBKDS | ICE1712_IRQ_CONCAP | ICE1712_IRQ_CONPBK : 0),
+ ICEREG(ice, IRQMASK));
+ outb(0x00, ICEMT(ice, IRQ));
return 0;
}
-int __devinit snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice)
+int snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice)
{
int err;
struct snd_kcontrol *kctl;
if (snd_BUG_ON(!ice->pcm_pro))
return -EIO;
- err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice));
+ kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice);
+ kctl->id.device = ice->pcm_pro->device;
+ err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
+ kctl = snd_ctl_new1(&snd_ice1712_spdif_maskc, ice);
kctl->id.device = ice->pcm_pro->device;
- err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskc, ice));
+ err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
+ kctl = snd_ctl_new1(&snd_ice1712_spdif_maskp, ice);
kctl->id.device = ice->pcm_pro->device;
- err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskp, ice));
+ err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
+ kctl = snd_ctl_new1(&snd_ice1712_spdif_stream, ice);
kctl->id.device = ice->pcm_pro->device;
- err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_stream, ice));
+ err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
- kctl->id.device = ice->pcm_pro->device;
ice->spdif.stream_ctl = kctl;
return 0;
}
-static int __devinit snd_ice1712_build_controls(struct snd_ice1712 *ice)
+static int snd_ice1712_build_controls(struct snd_ice1712 *ice)
{
int err;
@@ -2497,73 +2406,45 @@ static int __devinit snd_ice1712_build_controls(struct snd_ice1712 *ice)
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_volume_rate, ice));
if (err < 0)
return err;
- err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_peak, ice));
- if (err < 0)
- return err;
-
- return 0;
+ return snd_ctl_add(ice->card,
+ snd_ctl_new1(&snd_ice1712_mixer_pro_peak, ice));
}
-static int snd_ice1712_free(struct snd_ice1712 *ice)
+static void snd_ice1712_free(struct snd_card *card)
{
- if (!ice->port)
- goto __hw_end;
+ struct snd_ice1712 *ice = card->private_data;
+
+ if (ice->card_info && ice->card_info->chip_exit)
+ ice->card_info->chip_exit(ice);
+
/* mask all interrupts */
- outb(0xc0, ICEMT(ice, IRQ));
+ outb(ICE1712_MULTI_CAPTURE | ICE1712_MULTI_PLAYBACK, ICEMT(ice, IRQ));
outb(0xff, ICEREG(ice, IRQMASK));
- /* --- */
-__hw_end:
- if (ice->irq >= 0)
- free_irq(ice->irq, ice);
- if (ice->port)
- pci_release_regions(ice->pci);
snd_ice1712_akm4xxx_free(ice);
- pci_disable_device(ice->pci);
- kfree(ice->spec);
- kfree(ice);
- return 0;
}
-static int snd_ice1712_dev_free(struct snd_device *device)
+static int snd_ice1712_create(struct snd_card *card,
+ struct pci_dev *pci,
+ const char *modelname,
+ int omni,
+ int cs8427_timeout,
+ int dxr_enable)
{
- struct snd_ice1712 *ice = device->device_data;
- return snd_ice1712_free(ice);
-}
-
-static int __devinit snd_ice1712_create(struct snd_card *card,
- struct pci_dev *pci,
- const char *modelname,
- int omni,
- int cs8427_timeout,
- int dxr_enable,
- struct snd_ice1712 **r_ice1712)
-{
- struct snd_ice1712 *ice;
+ struct snd_ice1712 *ice = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_ice1712_dev_free,
- };
-
- *r_ice1712 = NULL;
/* enable PCI device */
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
/* check, if we can restrict PCI DMA transfers to 28 bits */
- if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 ||
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
- snd_printk(KERN_ERR "architecture does not support 28bit PCI busmaster DMA\n");
- pci_disable_device(pci);
+ if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(28))) {
+ dev_err(card->dev,
+ "architecture does not support 28bit PCI busmaster DMA\n");
return -ENXIO;
}
- ice = kzalloc(sizeof(*ice), GFP_KERNEL);
- if (ice == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
ice->omni = omni ? 1 : 0;
if (cs8427_timeout < 1)
cs8427_timeout = 1;
@@ -2590,57 +2471,34 @@ static int __devinit snd_ice1712_create(struct snd_card *card,
ice->pci = pci;
ice->irq = -1;
pci_set_master(pci);
+ /* disable legacy emulation */
pci_write_config_word(ice->pci, 0x40, 0x807f);
pci_write_config_word(ice->pci, 0x42, 0x0006);
snd_ice1712_proc_init(ice);
- synchronize_irq(pci->irq);
- err = pci_request_regions(pci, "ICE1712");
- if (err < 0) {
- kfree(ice);
- pci_disable_device(pci);
+ err = pcim_request_all_regions(pci, "ICE1712");
+ if (err < 0)
return err;
- }
ice->port = pci_resource_start(pci, 0);
ice->ddma_port = pci_resource_start(pci, 1);
ice->dmapath_port = pci_resource_start(pci, 2);
ice->profi_port = pci_resource_start(pci, 3);
- if (request_irq(pci->irq, snd_ice1712_interrupt, IRQF_SHARED,
- "ICE1712", ice)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_ice1712_free(ice);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_ice1712_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, ice)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EIO;
}
ice->irq = pci->irq;
+ card->sync_irq = ice->irq;
+ card->private_free = snd_ice1712_free;
- if (snd_ice1712_read_eeprom(ice, modelname) < 0) {
- snd_ice1712_free(ice);
+ if (snd_ice1712_read_eeprom(ice, modelname) < 0)
return -EIO;
- }
- if (snd_ice1712_chip_init(ice) < 0) {
- snd_ice1712_free(ice);
+ if (snd_ice1712_chip_init(ice) < 0)
return -EIO;
- }
- /* unmask used interrupts */
- outb(((ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) == 0 ?
- ICE1712_IRQ_MPU2 : 0) |
- ((ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_NO_CON_AC97) ?
- ICE1712_IRQ_PBKDS | ICE1712_IRQ_CONCAP | ICE1712_IRQ_CONPBK : 0),
- ICEREG(ice, IRQMASK));
- outb(0x00, ICEMT(ice, IRQ));
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops);
- if (err < 0) {
- snd_ice1712_free(ice);
- return err;
- }
-
- snd_card_set_dev(card, &pci->dev);
-
- *r_ice1712 = ice;
return 0;
}
@@ -2651,16 +2509,16 @@ static int __devinit snd_ice1712_create(struct snd_card *card,
*
*/
-static struct snd_ice1712_card_info no_matched __devinitdata;
+static struct snd_ice1712_card_info no_matched;
-static int __devinit snd_ice1712_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_ice1712_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
struct snd_ice1712 *ice;
int pcm_dev = 0, err;
- struct snd_ice1712_card_info * const *tbl, *c;
+ const struct snd_ice1712_card_info * const *tbl, *c;
if (dev >= SNDRV_CARDS)
return -ENODEV;
@@ -2669,33 +2527,32 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*ice), &card);
if (err < 0)
return err;
+ ice = card->private_data;
- strcpy(card->driver, "ICE1712");
- strcpy(card->shortname, "ICEnsemble ICE1712");
+ strscpy(card->driver, "ICE1712");
+ strscpy(card->shortname, "ICEnsemble ICE1712");
err = snd_ice1712_create(card, pci, model[dev], omni[dev],
- cs8427_timeout[dev], dxr_enable[dev], &ice);
- if (err < 0) {
- snd_card_free(card);
+ cs8427_timeout[dev], dxr_enable[dev]);
+ if (err < 0)
return err;
- }
for (tbl = card_tables; *tbl; tbl++) {
for (c = *tbl; c->subvendor; c++) {
if (c->subvendor == ice->eeprom.subvendor) {
- strcpy(card->shortname, c->name);
+ strscpy(card->shortname, c->name);
if (c->driver) /* specific driver? */
- strcpy(card->driver, c->driver);
+ strscpy(card->driver, c->driver);
if (c->chip_init) {
err = c->chip_init(ice);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
}
+ ice->card_info = c;
goto __found;
}
}
@@ -2703,59 +2560,46 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
c = &no_matched;
__found:
- err = snd_ice1712_pcm_profi(ice, pcm_dev++, NULL);
- if (err < 0) {
- snd_card_free(card);
+ err = snd_ice1712_pcm_profi(ice, pcm_dev++);
+ if (err < 0)
return err;
- }
if (ice_has_con_ac97(ice)) {
- err = snd_ice1712_pcm(ice, pcm_dev++, NULL);
- if (err < 0) {
- snd_card_free(card);
+ err = snd_ice1712_pcm(ice, pcm_dev++);
+ if (err < 0)
return err;
- }
}
err = snd_ice1712_ac97_mixer(ice);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
err = snd_ice1712_build_controls(ice);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
if (c->build_controls) {
err = c->build_controls(ice);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
}
if (ice_has_con_ac97(ice)) {
- err = snd_ice1712_pcm_ds(ice, pcm_dev++, NULL);
- if (err < 0) {
- snd_card_free(card);
+ err = snd_ice1712_pcm_ds(ice, pcm_dev++);
+ if (err < 0)
return err;
- }
}
if (!c->no_mpu401) {
err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
ICEREG(ice, MPU1_CTRL),
- (c->mpu401_1_info_flags | MPU401_INFO_INTEGRATED),
- ice->irq, 0, &ice->rmidi[0]);
- if (err < 0) {
- snd_card_free(card);
+ c->mpu401_1_info_flags |
+ MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
+ -1, &ice->rmidi[0]);
+ if (err < 0)
return err;
- }
if (c->mpu401_1_name)
- /* Prefered name available in card_info */
+ /* Preferred name available in card_info */
snprintf(ice->rmidi[0]->name,
sizeof(ice->rmidi[0]->name),
"%s %d", c->mpu401_1_name, card->number);
@@ -2764,15 +2608,14 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
/* 2nd port used */
err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712,
ICEREG(ice, MPU2_CTRL),
- (c->mpu401_2_info_flags | MPU401_INFO_INTEGRATED),
- ice->irq, 0, &ice->rmidi[1]);
+ c->mpu401_2_info_flags |
+ MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
+ -1, &ice->rmidi[1]);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
if (c->mpu401_2_name)
- /* Prefered name available in card_info */
+ /* Preferred name available in card_info */
snprintf(ice->rmidi[1]->name,
sizeof(ice->rmidi[1]->name),
"%s %d", c->mpu401_2_name,
@@ -2786,37 +2629,96 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
card->shortname, ice->port, ice->irq);
err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_ice1712_remove(struct pci_dev *pci)
+#ifdef CONFIG_PM_SLEEP
+static int snd_ice1712_suspend(struct device *dev)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
-}
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct snd_ice1712 *ice = card->private_data;
-static struct pci_driver driver = {
- .name = "ICE1712",
- .id_table = snd_ice1712_ids,
- .probe = snd_ice1712_probe,
- .remove = __devexit_p(snd_ice1712_remove),
-};
+ if (!ice->pm_suspend_enabled)
+ return 0;
-static int __init alsa_card_ice1712_init(void)
-{
- return pci_register_driver(&driver);
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+ snd_ac97_suspend(ice->ac97);
+
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ ice->pm_saved_is_spdif_master = is_spdif_master(ice);
+ ice->pm_saved_spdif_ctrl = inw(ICEMT(ice, ROUTE_SPDOUT));
+ ice->pm_saved_route = inw(ICEMT(ice, ROUTE_PSDOUT03));
+ }
+
+ if (ice->pm_suspend)
+ ice->pm_suspend(ice);
+ return 0;
}
-static void __exit alsa_card_ice1712_exit(void)
+static int snd_ice1712_resume(struct device *dev)
{
- pci_unregister_driver(&driver);
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct snd_ice1712 *ice = card->private_data;
+ int rate;
+
+ if (!ice->pm_suspend_enabled)
+ return 0;
+
+ if (ice->cur_rate)
+ rate = ice->cur_rate;
+ else
+ rate = PRO_RATE_DEFAULT;
+
+ if (snd_ice1712_chip_init(ice) < 0) {
+ snd_card_disconnect(card);
+ return -EIO;
+ }
+
+ ice->cur_rate = rate;
+
+ if (ice->pm_resume)
+ ice->pm_resume(ice);
+
+ if (ice->pm_saved_is_spdif_master) {
+ /* switching to external clock via SPDIF */
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ outb(inb(ICEMT(ice, RATE)) | ICE1712_SPDIF_MASTER,
+ ICEMT(ice, RATE));
+ }
+ snd_ice1712_set_input_clock_source(ice, 1);
+ } else {
+ /* internal on-card clock */
+ snd_ice1712_set_pro_rate(ice, rate, 1);
+ snd_ice1712_set_input_clock_source(ice, 0);
+ }
+
+ outw(ice->pm_saved_spdif_ctrl, ICEMT(ice, ROUTE_SPDOUT));
+ outw(ice->pm_saved_route, ICEMT(ice, ROUTE_PSDOUT03));
+
+ snd_ac97_resume(ice->ac97);
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ return 0;
}
-module_init(alsa_card_ice1712_init)
-module_exit(alsa_card_ice1712_exit)
+static SIMPLE_DEV_PM_OPS(snd_ice1712_pm, snd_ice1712_suspend, snd_ice1712_resume);
+#define SND_VT1712_PM_OPS &snd_ice1712_pm
+#else
+#define SND_VT1712_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct pci_driver ice1712_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_ice1712_ids,
+ .probe = snd_ice1712_probe,
+ .driver = {
+ .pm = SND_VT1712_PM_OPS,
+ },
+};
+
+module_pci_driver(ice1712_driver);
diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
index 0da778a69ef8..cd02710d8271 100644
--- a/sound/pci/ice1712/ice1712.h
+++ b/sound/pci/ice1712/ice1712.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_ICE1712_H
#define __SOUND_ICE1712_H
@@ -5,23 +6,9 @@
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
+#include <linux/io.h>
#include <sound/control.h>
#include <sound/ac97_codec.h>
#include <sound/rawmidi.h>
@@ -40,14 +27,17 @@
#define ICEREG(ice, x) ((ice)->port + ICE1712_REG_##x)
#define ICE1712_REG_CONTROL 0x00 /* byte */
-#define ICE1712_RESET 0x80 /* reset whole chip */
-#define ICE1712_SERR_LEVEL 0x04 /* SERR# level otherwise edge */
+#define ICE1712_RESET 0x80 /* soft reset whole chip */
+#define ICE1712_SERR_ASSERT_DS_DMA 0x40 /* disabled SERR# assertion for the DS DMA Ch-C irq otherwise enabled */
+#define ICE1712_DOS_VOL 0x10 /* DOS WT/FM volume control */
+#define ICE1712_SERR_LEVEL 0x08 /* SERR# level otherwise edge */
+#define ICE1712_SERR_ASSERT_SB 0x02 /* disabled SERR# assertion for SB irq otherwise enabled */
#define ICE1712_NATIVE 0x01 /* native mode otherwise SB */
#define ICE1712_REG_IRQMASK 0x01 /* byte */
-#define ICE1712_IRQ_MPU1 0x80
-#define ICE1712_IRQ_TIMER 0x40
-#define ICE1712_IRQ_MPU2 0x20
-#define ICE1712_IRQ_PROPCM 0x10
+#define ICE1712_IRQ_MPU1 0x80 /* MIDI irq mask */
+#define ICE1712_IRQ_TIMER 0x40 /* Timer mask */
+#define ICE1712_IRQ_MPU2 0x20 /* Secondary MIDI irq mask */
+#define ICE1712_IRQ_PROPCM 0x10 /* professional multi-track */
#define ICE1712_IRQ_FM 0x08 /* FM/MIDI - legacy */
#define ICE1712_IRQ_PBKDS 0x04 /* playback DS channels */
#define ICE1712_IRQ_CONCAP 0x02 /* consumer capture */
@@ -214,8 +204,9 @@
/*
- *
+ * I2C EEPROM Address
*/
+#define ICE_I2C_EEPROM_ADDR 0xA0
struct snd_ice1712;
@@ -288,6 +279,7 @@ struct snd_ice1712_spdif {
} ops;
};
+struct snd_ice1712_card_info;
struct snd_ice1712 {
unsigned long conp_dma_size;
@@ -324,6 +316,7 @@ struct snd_ice1712 {
struct snd_info_entry *proc_entry;
struct snd_ice1712_eeprom eeprom;
+ const struct snd_ice1712_card_info *card_info;
unsigned int pro_volumes[20];
unsigned int omni:1; /* Delta Omni I/O */
@@ -342,7 +335,7 @@ struct snd_ice1712 {
struct mutex open_mutex;
struct snd_pcm_substream *pcm_reserved[4];
- struct snd_pcm_hw_constraint_list *hw_rates; /* card-specific rate constraints */
+ const struct snd_pcm_hw_constraint_list *hw_rates; /* card-specific rate constraints */
unsigned int akm_codecs;
struct snd_akm4xxx *akm;
@@ -381,10 +374,10 @@ struct snd_ice1712 {
unsigned char (*set_mclk)(struct snd_ice1712 *ice, unsigned int rate);
int (*set_spdif_clock)(struct snd_ice1712 *ice, int type);
int (*get_spdif_master_type)(struct snd_ice1712 *ice);
- char **ext_clock_names;
+ const char * const *ext_clock_names;
int ext_clock_count;
void (*pro_open)(struct snd_ice1712 *, struct snd_pcm_substream *);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int (*pm_suspend)(struct snd_ice1712 *);
int (*pm_resume)(struct snd_ice1712 *);
unsigned int pm_suspend_enabled:1;
@@ -513,10 +506,11 @@ static inline u8 snd_ice1712_read(struct snd_ice1712 *ice, u8 addr)
struct snd_ice1712_card_info {
unsigned int subvendor;
- char *name;
- char *model;
- char *driver;
+ const char *name;
+ const char *model;
+ const char *driver;
int (*chip_init)(struct snd_ice1712 *);
+ void (*chip_exit)(struct snd_ice1712 *);
int (*build_controls)(struct snd_ice1712 *);
unsigned int no_mpu401:1;
unsigned int mpu401_1_info_flags;
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index c1498fa5545f..e2dbbbfbca9f 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for VT1724 ICEnsemble ICE1724 / VIA VT1724 (Envy24HT)
* VIA VT1720 (Envy24PT)
@@ -5,30 +6,14 @@
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
* 2002 James Stafford <jstafford@ampltd.com>
* 2003 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/info.h>
@@ -54,33 +39,15 @@
#include "wtm.h"
#include "se.h"
#include "quartet.h"
+#include "psc724.h"
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{"
- REVO_DEVICE_DESC
- AMP_AUDIO2000_DEVICE_DESC
- AUREON_DEVICE_DESC
- VT1720_MOBO_DEVICE_DESC
- PONTIS_DEVICE_DESC
- PRODIGY192_DEVICE_DESC
- PRODIGY_HIFI_DEVICE_DESC
- JULI_DEVICE_DESC
- MAYA44_DEVICE_DESC
- PHASE_DEVICE_DESC
- WTM_DEVICE_DESC
- SE_DEVICE_DESC
- QTET_DEVICE_DESC
- "{VIA,VT1720},"
- "{VIA,VT1724},"
- "{ICEnsemble,Generic ICE1724},"
- "{ICEnsemble,Generic Envy24HT}"
- "{ICEnsemble,Generic Envy24PT}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
static char *model[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
@@ -94,7 +61,7 @@ MODULE_PARM_DESC(model, "Use the given board model.");
/* Both VT1720 and VT1724 have the same PCI IDs */
-static DEFINE_PCI_DEVICE_TABLE(snd_vt1724_ids) = {
+static const struct pci_device_id snd_vt1724_ids[] = {
{ PCI_VDEVICE(ICE, PCI_DEVICE_ID_VT1724), 0 },
{ 0, }
};
@@ -106,7 +73,7 @@ static int PRO_RATE_LOCKED;
static int PRO_RATE_RESET = 1;
static unsigned int PRO_RATE_DEFAULT = 44100;
-static char *ext_clock_names[1] = { "IEC958 In" };
+static const char * const ext_clock_names[1] = { "IEC958 In" };
/*
* Basic I/O
@@ -146,7 +113,7 @@ static unsigned char snd_vt1724_ac97_ready(struct snd_ice1712 *ice)
continue;
return old_cmd;
}
- snd_printd(KERN_ERR "snd_vt1724_ac97_ready: timeout\n");
+ dev_dbg(ice->card->dev, "snd_vt1724_ac97_ready: timeout\n");
return old_cmd;
}
@@ -156,7 +123,7 @@ static int snd_vt1724_ac97_wait_bit(struct snd_ice1712 *ice, unsigned char bit)
for (tm = 0; tm < 0x10000; tm++)
if ((inb(ICEMT1724(ice, AC97_CMD)) & bit) == 0)
return 0;
- snd_printd(KERN_ERR "snd_vt1724_ac97_wait_bit: timeout\n");
+ dev_dbg(ice->card->dev, "snd_vt1724_ac97_wait_bit: timeout\n");
return -EIO;
}
@@ -321,9 +288,8 @@ static void vt1724_enable_midi_irq(struct snd_rawmidi_substream *substream,
{
struct snd_ice1712 *ice = substream->rmidi->private_data;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
enable_midi_irq(ice, flag, enable);
- spin_unlock_irq(&ice->reg_lock);
}
static int vt1724_midi_output_open(struct snd_rawmidi_substream *s)
@@ -339,9 +305,8 @@ static int vt1724_midi_output_close(struct snd_rawmidi_substream *s)
static void vt1724_midi_output_trigger(struct snd_rawmidi_substream *s, int up)
{
struct snd_ice1712 *ice = s->rmidi->private_data;
- unsigned long flags;
- spin_lock_irqsave(&ice->reg_lock, flags);
+ guard(spinlock_irqsave)(&ice->reg_lock);
if (up) {
ice->midi_output = 1;
vt1724_midi_write(ice);
@@ -349,7 +314,6 @@ static void vt1724_midi_output_trigger(struct snd_rawmidi_substream *s, int up)
ice->midi_output = 0;
enable_midi_irq(ice, VT1724_IRQ_MPU_TX, 0);
}
- spin_unlock_irqrestore(&ice->reg_lock, flags);
}
static void vt1724_midi_output_drain(struct snd_rawmidi_substream *s)
@@ -367,7 +331,7 @@ static void vt1724_midi_output_drain(struct snd_rawmidi_substream *s)
} while (time_after(timeout, jiffies));
}
-static struct snd_rawmidi_ops vt1724_midi_output_ops = {
+static const struct snd_rawmidi_ops vt1724_midi_output_ops = {
.open = vt1724_midi_output_open,
.close = vt1724_midi_output_close,
.trigger = vt1724_midi_output_trigger,
@@ -390,19 +354,17 @@ static int vt1724_midi_input_close(struct snd_rawmidi_substream *s)
static void vt1724_midi_input_trigger(struct snd_rawmidi_substream *s, int up)
{
struct snd_ice1712 *ice = s->rmidi->private_data;
- unsigned long flags;
- spin_lock_irqsave(&ice->reg_lock, flags);
+ guard(spinlock_irqsave)(&ice->reg_lock);
if (up) {
ice->midi_input = 1;
vt1724_midi_read(ice);
} else {
ice->midi_input = 0;
}
- spin_unlock_irqrestore(&ice->reg_lock, flags);
}
-static struct snd_rawmidi_ops vt1724_midi_input_ops = {
+static const struct snd_rawmidi_ops vt1724_midi_input_ops = {
.open = vt1724_midi_input_open,
.close = vt1724_midi_input_close,
.trigger = vt1724_midi_input_trigger,
@@ -427,40 +389,39 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
status &= status_mask;
if (status == 0)
break;
- spin_lock(&ice->reg_lock);
- if (++timeout > 10) {
- status = inb(ICEREG1724(ice, IRQSTAT));
- printk(KERN_ERR "ice1724: Too long irq loop, "
- "status = 0x%x\n", status);
+ scoped_guard(spinlock, &ice->reg_lock) {
+ if (++timeout > 10) {
+ status = inb(ICEREG1724(ice, IRQSTAT));
+ dev_err(ice->card->dev,
+ "Too long irq loop, status = 0x%x\n", status);
+ if (status & VT1724_IRQ_MPU_TX) {
+ dev_err(ice->card->dev, "Disabling MPU_TX\n");
+ enable_midi_irq(ice, VT1724_IRQ_MPU_TX, 0);
+ }
+ goto out;
+ }
+ handled = 1;
if (status & VT1724_IRQ_MPU_TX) {
- printk(KERN_ERR "ice1724: Disabling MPU_TX\n");
- enable_midi_irq(ice, VT1724_IRQ_MPU_TX, 0);
+ if (ice->midi_output)
+ vt1724_midi_write(ice);
+ else
+ enable_midi_irq(ice, VT1724_IRQ_MPU_TX, 0);
+ /* Due to mysterical reasons, MPU_TX is always
+ * generated (and can't be cleared) when a PCM
+ * playback is going. So let's ignore at the
+ * next loop.
+ */
+ status_mask &= ~VT1724_IRQ_MPU_TX;
}
- spin_unlock(&ice->reg_lock);
- break;
- }
- handled = 1;
- if (status & VT1724_IRQ_MPU_TX) {
- if (ice->midi_output)
- vt1724_midi_write(ice);
- else
- enable_midi_irq(ice, VT1724_IRQ_MPU_TX, 0);
- /* Due to mysterical reasons, MPU_TX is always
- * generated (and can't be cleared) when a PCM
- * playback is going. So let's ignore at the
- * next loop.
- */
- status_mask &= ~VT1724_IRQ_MPU_TX;
- }
- if (status & VT1724_IRQ_MPU_RX) {
- if (ice->midi_input)
- vt1724_midi_read(ice);
- else
- vt1724_midi_clear_rx(ice);
+ if (status & VT1724_IRQ_MPU_RX) {
+ if (ice->midi_input)
+ vt1724_midi_read(ice);
+ else
+ vt1724_midi_clear_rx(ice);
+ }
+ /* ack MPU irq */
+ outb(status, ICEREG1724(ice, IRQSTAT));
}
- /* ack MPU irq */
- outb(status, ICEREG1724(ice, IRQSTAT));
- spin_unlock(&ice->reg_lock);
if (status & VT1724_IRQ_MTPCM) {
/*
* Multi-track PCM
@@ -514,6 +475,7 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
}
}
+ out:
return IRQ_RETVAL(handled);
}
@@ -521,25 +483,25 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
* PCM code - professional part (multitrack)
*/
-static unsigned int rates[] = {
+static const unsigned int rates[] = {
8000, 9600, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000,
176400, 192000,
};
-static struct snd_pcm_hw_constraint_list hw_constraints_rates_96 = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_rates_96 = {
.count = ARRAY_SIZE(rates) - 2, /* up to 96000 */
.list = rates,
.mask = 0,
};
-static struct snd_pcm_hw_constraint_list hw_constraints_rates_48 = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_rates_48 = {
.count = ARRAY_SIZE(rates) - 5, /* up to 48000 */
.list = rates,
.mask = 0,
};
-static struct snd_pcm_hw_constraint_list hw_constraints_rates_192 = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_rates_192 = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
@@ -572,27 +534,27 @@ static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- spin_lock(&ice->reg_lock);
- old = inb(ICEMT1724(ice, DMA_PAUSE));
- if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
- old |= what;
- else
- old &= ~what;
- outb(old, ICEMT1724(ice, DMA_PAUSE));
- spin_unlock(&ice->reg_lock);
+ scoped_guard(spinlock, &ice->reg_lock) {
+ old = inb(ICEMT1724(ice, DMA_PAUSE));
+ if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
+ old |= what;
+ else
+ old &= ~what;
+ outb(old, ICEMT1724(ice, DMA_PAUSE));
+ }
break;
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
- spin_lock(&ice->reg_lock);
- old = inb(ICEMT1724(ice, DMA_CONTROL));
- if (cmd == SNDRV_PCM_TRIGGER_START)
- old |= what;
- else
- old &= ~what;
- outb(old, ICEMT1724(ice, DMA_CONTROL));
- spin_unlock(&ice->reg_lock);
+ scoped_guard(spinlock, &ice->reg_lock) {
+ old = inb(ICEMT1724(ice, DMA_CONTROL));
+ if (cmd == SNDRV_PCM_TRIGGER_START)
+ old |= what;
+ else
+ old &= ~what;
+ outb(old, ICEMT1724(ice, DMA_CONTROL));
+ }
break;
case SNDRV_PCM_TRIGGER_RESUME:
@@ -620,9 +582,7 @@ static const unsigned int stdclock_rate_list[16] = {
static unsigned int stdclock_get_rate(struct snd_ice1712 *ice)
{
- unsigned int rate;
- rate = stdclock_rate_list[inb(ICEMT1724(ice, RATE)) & 15];
- return rate;
+ return stdclock_rate_list[inb(ICEMT1724(ice, RATE)) & 15];
}
static void stdclock_set_rate(struct snd_ice1712 *ice, unsigned int rate)
@@ -660,46 +620,45 @@ static unsigned char stdclock_set_mclk(struct snd_ice1712 *ice,
static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
int force)
{
- unsigned long flags;
unsigned char mclk_change;
unsigned int i, old_rate;
+ bool call_set_rate = false;
if (rate > ice->hw_rates->list[ice->hw_rates->count - 1])
return -EINVAL;
- spin_lock_irqsave(&ice->reg_lock, flags);
- if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) ||
- (inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) {
- /* running? we cannot change the rate now... */
- spin_unlock_irqrestore(&ice->reg_lock, flags);
- return ((rate == ice->cur_rate) && !force) ? 0 : -EBUSY;
- }
- if (!force && is_pro_rate_locked(ice)) {
- /* comparing required and current rate - makes sense for
- * internal clock only */
- spin_unlock_irqrestore(&ice->reg_lock, flags);
- return (rate == ice->cur_rate) ? 0 : -EBUSY;
- }
+ scoped_guard(spinlock_irqsave, &ice->reg_lock) {
+ if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) ||
+ (inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) {
+ /* running? we cannot change the rate now... */
+ return ((rate == ice->cur_rate) && !force) ? 0 : -EBUSY;
+ }
+ if (!force && is_pro_rate_locked(ice)) {
+ /* comparing required and current rate - makes sense for
+ * internal clock only */
+ return (rate == ice->cur_rate) ? 0 : -EBUSY;
+ }
- if (force || !ice->is_spdif_master(ice)) {
- /* force means the rate was switched by ucontrol, otherwise
- * setting clock rate for internal clock mode */
- old_rate = ice->get_rate(ice);
- if (force || (old_rate != rate))
- ice->set_rate(ice, rate);
- else if (rate == ice->cur_rate) {
- spin_unlock_irqrestore(&ice->reg_lock, flags);
- return 0;
+ if (force || !ice->is_spdif_master(ice)) {
+ /* force means the rate was switched by ucontrol, otherwise
+ * setting clock rate for internal clock mode */
+ old_rate = ice->get_rate(ice);
+ if (force || (old_rate != rate))
+ call_set_rate = true;
+ else if (rate == ice->cur_rate) {
+ return 0;
+ }
}
+
+ ice->cur_rate = rate;
}
- ice->cur_rate = rate;
+ if (call_set_rate)
+ ice->set_rate(ice, rate);
/* setting master clock */
mclk_change = ice->set_mclk(ice, rate);
- spin_unlock_irqrestore(&ice->reg_lock, flags);
-
if (mclk_change && ice->gpio.i2s_mclk_changed)
ice->gpio.i2s_mclk_changed(ice);
if (ice->gpio.set_pro_rate)
@@ -716,24 +675,21 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
return 0;
}
-static int snd_vt1724_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
+static int __snd_vt1724_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
{
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
- int i, chs, err;
+ int i, chs;
chs = params_channels(hw_params);
- mutex_lock(&ice->open_mutex);
/* mark surround channels */
if (substream == ice->playback_pro_substream) {
/* PDMA0 can be multi-channel up to 8 */
chs = chs / 2 - 1;
for (i = 0; i < chs; i++) {
if (ice->pcm_reserved[i] &&
- ice->pcm_reserved[i] != substream) {
- mutex_unlock(&ice->open_mutex);
+ ice->pcm_reserved[i] != substream)
return -EBUSY;
- }
ice->pcm_reserved[i] = substream;
}
for (; i < 3; i++) {
@@ -745,22 +701,30 @@ static int snd_vt1724_pcm_hw_params(struct snd_pcm_substream *substream,
/* check individual playback stream */
if (ice->playback_con_substream_ds[i] == substream) {
if (ice->pcm_reserved[i] &&
- ice->pcm_reserved[i] != substream) {
- mutex_unlock(&ice->open_mutex);
+ ice->pcm_reserved[i] != substream)
return -EBUSY;
- }
ice->pcm_reserved[i] = substream;
break;
}
}
}
- mutex_unlock(&ice->open_mutex);
- err = snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0);
- if (err < 0)
- return err;
+ return 0;
+}
+
+static int snd_vt1724_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
+ int err;
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+ scoped_guard(mutex, &ice->open_mutex) {
+ err = __snd_vt1724_pcm_hw_params(substream, hw_params);
+ if (err < 0)
+ return err;
+ }
+
+ return snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0);
}
static int snd_vt1724_pcm_hw_free(struct snd_pcm_substream *substream)
@@ -768,13 +732,12 @@ static int snd_vt1724_pcm_hw_free(struct snd_pcm_substream *substream)
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
int i;
- mutex_lock(&ice->open_mutex);
+ guard(mutex)(&ice->open_mutex);
/* unmark surround channels */
for (i = 0; i < 3; i++)
if (ice->pcm_reserved[i] == substream)
ice->pcm_reserved[i] = NULL;
- mutex_unlock(&ice->open_mutex);
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static int snd_vt1724_playback_pro_prepare(struct snd_pcm_substream *substream)
@@ -783,7 +746,7 @@ static int snd_vt1724_playback_pro_prepare(struct snd_pcm_substream *substream)
unsigned char val;
unsigned int size;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
val = (8 - substream->runtime->channels) >> 1;
outb(val, ICEMT1724(ice, BURST));
@@ -798,10 +761,8 @@ static int snd_vt1724_playback_pro_prepare(struct snd_pcm_substream *substream)
outw(size, ICEMT1724(ice, PLAYBACK_COUNT));
outb(size >> 16, ICEMT1724(ice, PLAYBACK_COUNT) + 2);
- spin_unlock_irq(&ice->reg_lock);
-
/*
- printk(KERN_DEBUG "pro prepare: ch = %d, addr = 0x%x, "
+ dev_dbg(ice->card->dev, "pro prepare: ch = %d, addr = 0x%x, "
"buffer = 0x%x, period = 0x%x\n",
substream->runtime->channels,
(unsigned int)substream->runtime->dma_addr,
@@ -821,13 +782,13 @@ static snd_pcm_uframes_t snd_vt1724_playback_pro_pointer(struct snd_pcm_substrea
#if 0 /* read PLAYBACK_ADDR */
ptr = inl(ICEMT1724(ice, PLAYBACK_ADDR));
if (ptr < substream->runtime->dma_addr) {
- snd_printd("ice1724: invalid negative ptr\n");
+ dev_dbg(ice->card->dev, "invalid negative ptr\n");
return 0;
}
ptr -= substream->runtime->dma_addr;
ptr = bytes_to_frames(substream->runtime, ptr);
if (ptr >= substream->runtime->buffer_size) {
- snd_printd("ice1724: invalid ptr %d (size=%d)\n",
+ dev_dbg(ice->card->dev, "invalid ptr %d (size=%d)\n",
(int)ptr, (int)substream->runtime->period_size);
return 0;
}
@@ -840,7 +801,7 @@ static snd_pcm_uframes_t snd_vt1724_playback_pro_pointer(struct snd_pcm_substrea
else if (ptr <= substream->runtime->buffer_size)
ptr = substream->runtime->buffer_size - ptr;
else {
- snd_printd("ice1724: invalid ptr %d (size=%d)\n",
+ dev_dbg(ice->card->dev, "invalid ptr %d (size=%d)\n",
(int)ptr, (int)substream->runtime->buffer_size);
ptr = 0;
}
@@ -853,13 +814,12 @@ static int snd_vt1724_pcm_prepare(struct snd_pcm_substream *substream)
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
const struct vt1724_pcm_reg *reg = substream->runtime->private_data;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
outl(substream->runtime->dma_addr, ice->profi_port + reg->addr);
outw((snd_pcm_lib_buffer_bytes(substream) >> 2) - 1,
ice->profi_port + reg->size);
outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1,
ice->profi_port + reg->count);
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -884,7 +844,7 @@ static snd_pcm_uframes_t snd_vt1724_pcm_pointer(struct snd_pcm_substream *substr
else if (ptr <= substream->runtime->buffer_size)
ptr = substream->runtime->buffer_size - ptr;
else {
- snd_printd("ice1724: invalid ptr %d (size=%d)\n",
+ dev_dbg(ice->card->dev, "invalid ptr %d (size=%d)\n",
(int)ptr, (int)substream->runtime->buffer_size);
ptr = 0;
}
@@ -1013,6 +973,25 @@ static int set_rate_constraints(struct snd_ice1712 *ice,
ice->hw_rates);
}
+/* if the card has the internal rate locked (is_pro_locked), limit runtime
+ hw rates to the current internal rate only.
+*/
+static void constrain_rate_if_locked(struct snd_pcm_substream *substream)
+{
+ struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int rate;
+ if (is_pro_rate_locked(ice)) {
+ rate = ice->get_rate(ice);
+ if (rate >= runtime->hw.rate_min
+ && rate <= runtime->hw.rate_max) {
+ runtime->hw.rate_min = rate;
+ runtime->hw.rate_max = rate;
+ }
+ }
+}
+
+
/* multi-channel playback needs alignment 8x32bit regardless of the channels
* actually used
*/
@@ -1030,22 +1009,23 @@ static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream)
snd_pcm_set_sync(substream);
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
set_rate_constraints(ice, substream);
- mutex_lock(&ice->open_mutex);
- /* calculate the currently available channels */
- num_indeps = ice->num_total_dacs / 2 - 1;
- for (chs = 0; chs < num_indeps; chs++) {
- if (ice->pcm_reserved[chs])
- break;
+ scoped_guard(mutex, &ice->open_mutex) {
+ /* calculate the currently available channels */
+ num_indeps = ice->num_total_dacs / 2 - 1;
+ for (chs = 0; chs < num_indeps; chs++) {
+ if (ice->pcm_reserved[chs])
+ break;
+ }
+ chs = (chs + 1) * 2;
+ runtime->hw.channels_max = chs;
+ if (chs > 2) /* channels must be even */
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
}
- chs = (chs + 1) * 2;
- runtime->hw.channels_max = chs;
- if (chs > 2) /* channels must be even */
- snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
- mutex_unlock(&ice->open_mutex);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
VT1724_BUFFER_ALIGN);
+ constrain_rate_if_locked(substream);
if (ice->pro_open)
ice->pro_open(ice, substream);
return 0;
@@ -1066,6 +1046,7 @@ static int snd_vt1724_capture_pro_open(struct snd_pcm_substream *substream)
VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
VT1724_BUFFER_ALIGN);
+ constrain_rate_if_locked(substream);
if (ice->pro_open)
ice->pro_open(ice, substream);
return 0;
@@ -1092,10 +1073,9 @@ static int snd_vt1724_capture_pro_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_vt1724_playback_pro_ops = {
+static const struct snd_pcm_ops snd_vt1724_playback_pro_ops = {
.open = snd_vt1724_playback_pro_open,
.close = snd_vt1724_playback_pro_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_vt1724_pcm_hw_params,
.hw_free = snd_vt1724_pcm_hw_free,
.prepare = snd_vt1724_playback_pro_prepare,
@@ -1103,10 +1083,9 @@ static struct snd_pcm_ops snd_vt1724_playback_pro_ops = {
.pointer = snd_vt1724_playback_pro_pointer,
};
-static struct snd_pcm_ops snd_vt1724_capture_pro_ops = {
+static const struct snd_pcm_ops snd_vt1724_capture_pro_ops = {
.open = snd_vt1724_capture_pro_open,
.close = snd_vt1724_capture_pro_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_vt1724_pcm_hw_params,
.hw_free = snd_vt1724_pcm_hw_free,
.prepare = snd_vt1724_pcm_prepare,
@@ -1114,25 +1093,31 @@ static struct snd_pcm_ops snd_vt1724_capture_pro_ops = {
.pointer = snd_vt1724_pcm_pointer,
};
-static int __devinit snd_vt1724_pcm_profi(struct snd_ice1712 *ice, int device)
+static int snd_vt1724_pcm_profi(struct snd_ice1712 *ice, int device)
{
struct snd_pcm *pcm;
- int err;
+ int capt, err;
- err = snd_pcm_new(ice->card, "ICE1724", device, 1, 1, &pcm);
+ if ((ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_ADC_MASK) ==
+ VT1724_CFG_ADC_NONE)
+ capt = 0;
+ else
+ capt = 1;
+ err = snd_pcm_new(ice->card, "ICE1724", device, 1, capt, &pcm);
if (err < 0)
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_vt1724_playback_pro_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_vt1724_capture_pro_ops);
+ if (capt)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_vt1724_capture_pro_ops);
pcm->private_data = ice;
pcm->info_flags = 0;
- strcpy(pcm->name, "ICE1724");
+ strscpy(pcm->name, "ICE1724");
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(ice->pci),
- 256*1024, 256*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &ice->pci->dev, 256*1024, 256*1024);
ice->pcm_pro = pcm;
@@ -1163,9 +1148,8 @@ static void update_spdif_bits(struct snd_ice1712 *ice, unsigned int val)
static void update_spdif_rate(struct snd_ice1712 *ice, unsigned int rate)
{
unsigned int val, nval;
- unsigned long flags;
- spin_lock_irqsave(&ice->reg_lock, flags);
+ guard(spinlock_irqsave)(&ice->reg_lock);
nval = val = inw(ICEMT1724(ice, SPDIF_CTRL));
nval &= ~(7 << 12);
switch (rate) {
@@ -1179,7 +1163,6 @@ static void update_spdif_rate(struct snd_ice1712 *ice, unsigned int rate)
}
if (val != nval)
update_spdif_bits(ice, nval);
- spin_unlock_irqrestore(&ice->reg_lock, flags);
}
static int snd_vt1724_playback_spdif_prepare(struct snd_pcm_substream *substream)
@@ -1208,6 +1191,7 @@ static int snd_vt1724_playback_spdif_open(struct snd_pcm_substream *substream)
VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
VT1724_BUFFER_ALIGN);
+ constrain_rate_if_locked(substream);
if (ice->spdif.ops.open)
ice->spdif.ops.open(ice, substream);
return 0;
@@ -1244,6 +1228,7 @@ static int snd_vt1724_capture_spdif_open(struct snd_pcm_substream *substream)
VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
VT1724_BUFFER_ALIGN);
+ constrain_rate_if_locked(substream);
if (ice->spdif.ops.open)
ice->spdif.ops.open(ice, substream);
return 0;
@@ -1262,10 +1247,9 @@ static int snd_vt1724_capture_spdif_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_vt1724_playback_spdif_ops = {
+static const struct snd_pcm_ops snd_vt1724_playback_spdif_ops = {
.open = snd_vt1724_playback_spdif_open,
.close = snd_vt1724_playback_spdif_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_vt1724_pcm_hw_params,
.hw_free = snd_vt1724_pcm_hw_free,
.prepare = snd_vt1724_playback_spdif_prepare,
@@ -1273,10 +1257,9 @@ static struct snd_pcm_ops snd_vt1724_playback_spdif_ops = {
.pointer = snd_vt1724_pcm_pointer,
};
-static struct snd_pcm_ops snd_vt1724_capture_spdif_ops = {
+static const struct snd_pcm_ops snd_vt1724_capture_spdif_ops = {
.open = snd_vt1724_capture_spdif_open,
.close = snd_vt1724_capture_spdif_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_vt1724_pcm_hw_params,
.hw_free = snd_vt1724_pcm_hw_free,
.prepare = snd_vt1724_pcm_prepare,
@@ -1285,7 +1268,7 @@ static struct snd_pcm_ops snd_vt1724_capture_spdif_ops = {
};
-static int __devinit snd_vt1724_pcm_spdif(struct snd_ice1712 *ice, int device)
+static int snd_vt1724_pcm_spdif(struct snd_ice1712 *ice, int device)
{
char *name;
struct snd_pcm *pcm;
@@ -1324,11 +1307,10 @@ static int __devinit snd_vt1724_pcm_spdif(struct snd_ice1712 *ice, int device)
pcm->private_data = ice;
pcm->info_flags = 0;
- strcpy(pcm->name, name);
+ strscpy(pcm->name, name);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(ice->pci),
- 256*1024, 256*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &ice->pci->dev, 256*1024, 256*1024);
ice->pcm = pcm;
@@ -1366,11 +1348,11 @@ static int snd_vt1724_playback_indep_prepare(struct snd_pcm_substream *substream
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
unsigned char val;
- spin_lock_irq(&ice->reg_lock);
- val = 3 - substream->number;
- if (inb(ICEMT1724(ice, BURST)) < val)
- outb(val, ICEMT1724(ice, BURST));
- spin_unlock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ val = 3 - substream->number;
+ if (inb(ICEMT1724(ice, BURST)) < val)
+ outb(val, ICEMT1724(ice, BURST));
+ }
return snd_vt1724_pcm_prepare(substream);
}
@@ -1379,13 +1361,11 @@ static int snd_vt1724_playback_indep_open(struct snd_pcm_substream *substream)
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
- mutex_lock(&ice->open_mutex);
- /* already used by PDMA0? */
- if (ice->pcm_reserved[substream->number]) {
- mutex_unlock(&ice->open_mutex);
- return -EBUSY; /* FIXME: should handle blocking mode properly */
+ scoped_guard(mutex, &ice->open_mutex) {
+ /* already used by PDMA0? */
+ if (ice->pcm_reserved[substream->number])
+ return -EBUSY; /* FIXME: should handle blocking mode properly */
}
- mutex_unlock(&ice->open_mutex);
runtime->private_data = (void *)&vt1724_playback_dma_regs[substream->number];
ice->playback_con_substream_ds[substream->number] = substream;
runtime->hw = snd_vt1724_2ch_stereo;
@@ -1407,10 +1387,9 @@ static int snd_vt1724_playback_indep_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_vt1724_playback_indep_ops = {
+static const struct snd_pcm_ops snd_vt1724_playback_indep_ops = {
.open = snd_vt1724_playback_indep_open,
.close = snd_vt1724_playback_indep_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_vt1724_pcm_hw_params,
.hw_free = snd_vt1724_pcm_hw_free,
.prepare = snd_vt1724_playback_indep_prepare,
@@ -1419,7 +1398,7 @@ static struct snd_pcm_ops snd_vt1724_playback_indep_ops = {
};
-static int __devinit snd_vt1724_pcm_indep(struct snd_ice1712 *ice, int device)
+static int snd_vt1724_pcm_indep(struct snd_ice1712 *ice, int device)
{
struct snd_pcm *pcm;
int play;
@@ -1438,11 +1417,10 @@ static int __devinit snd_vt1724_pcm_indep(struct snd_ice1712 *ice, int device)
pcm->private_data = ice;
pcm->info_flags = 0;
- strcpy(pcm->name, "ICE1724 Surround PCM");
+ strscpy(pcm->name, "ICE1724 Surround PCM");
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(ice->pci),
- 256*1024, 256*1024);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &ice->pci->dev, 256*1024, 256*1024);
ice->pcm_ds = pcm;
@@ -1454,14 +1432,14 @@ static int __devinit snd_vt1724_pcm_indep(struct snd_ice1712 *ice, int device)
* Mixer section
*/
-static int __devinit snd_vt1724_ac97_mixer(struct snd_ice1712 *ice)
+static int snd_vt1724_ac97_mixer(struct snd_ice1712 *ice)
{
int err;
if (!(ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S)) {
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
- static struct snd_ac97_bus_ops ops = {
+ static const struct snd_ac97_bus_ops ops = {
.write = snd_vt1724_ac97_write,
.read = snd_vt1724_ac97_read,
};
@@ -1478,7 +1456,8 @@ static int __devinit snd_vt1724_ac97_mixer(struct snd_ice1712 *ice)
ac97.private_data = ice;
err = snd_ac97_mixer(pbus, &ac97, &ice->ac97);
if (err < 0)
- printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n");
+ dev_warn(ice->card->dev,
+ "cannot initialize pro ac97, skipped\n");
else
return 0;
}
@@ -1540,12 +1519,9 @@ static void snd_vt1724_proc_read(struct snd_info_entry *entry,
idx, inb(ice->profi_port+idx));
}
-static void __devinit snd_vt1724_proc_init(struct snd_ice1712 *ice)
+static void snd_vt1724_proc_init(struct snd_ice1712 *ice)
{
- struct snd_info_entry *entry;
-
- if (!snd_card_proc_new(ice->card, "ice1724", &entry))
- snd_info_set_text_ops(entry, ice, snd_vt1724_proc_read);
+ snd_card_ro_proc_new(ice->card, "ice1724", ice, snd_vt1724_proc_read);
}
/*
@@ -1569,7 +1545,7 @@ static int snd_vt1724_eeprom_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_vt1724_eeprom __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_eeprom = {
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "ICE1724 EEPROM",
.access = SNDRV_CTL_ELEM_ACCESS_READ,
@@ -1674,15 +1650,14 @@ static int snd_vt1724_spdif_default_put(struct snd_kcontrol *kcontrol,
unsigned int val, old;
val = encode_spdif_bits(&ucontrol->value.iec958);
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
old = inw(ICEMT1724(ice, SPDIF_CTRL));
if (val != old)
update_spdif_bits(ice, val);
- spin_unlock_irq(&ice->reg_lock);
return val != old;
}
-static struct snd_kcontrol_new snd_vt1724_spdif_default __devinitdata =
+static const struct snd_kcontrol_new snd_vt1724_spdif_default =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
@@ -1714,7 +1689,7 @@ static int snd_vt1724_spdif_maskp_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_vt1724_spdif_maskc __devinitdata =
+static const struct snd_kcontrol_new snd_vt1724_spdif_maskc =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1723,7 +1698,7 @@ static struct snd_kcontrol_new snd_vt1724_spdif_maskc __devinitdata =
.get = snd_vt1724_spdif_maskc_get,
};
-static struct snd_kcontrol_new snd_vt1724_spdif_maskp __devinitdata =
+static const struct snd_kcontrol_new snd_vt1724_spdif_maskp =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1749,18 +1724,17 @@ static int snd_vt1724_spdif_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char old, val;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
old = val = inb(ICEREG1724(ice, SPDIF_CFG));
val &= ~VT1724_CFG_SPDIF_OUT_EN;
if (ucontrol->value.integer.value[0])
val |= VT1724_CFG_SPDIF_OUT_EN;
if (old != val)
outb(val, ICEREG1724(ice, SPDIF_CFG));
- spin_unlock_irq(&ice->reg_lock);
return old != val;
}
-static struct snd_kcontrol_new snd_vt1724_spdif_switch __devinitdata =
+static const struct snd_kcontrol_new snd_vt1724_spdif_switch =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* FIXME: the following conflict with IEC958 Playback Route */
@@ -1825,13 +1799,18 @@ static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol,
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
- uinfo->value.enumerated.items = hw_rates_count + ice->ext_clock_count;
+ /* internal clocks */
+ uinfo->value.enumerated.items = hw_rates_count;
+ /* external clocks */
+ if (ice->force_rdma1 ||
+ (ice->eeprom.data[ICE_EEP2_SPDIF] & VT1724_CFG_SPDIF_IN))
+ uinfo->value.enumerated.items += ice->ext_clock_count;
/* upper limit - keep at top */
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
if (uinfo->value.enumerated.item >= hw_rates_count)
/* ext_clock items */
- strcpy(uinfo->value.enumerated.name,
+ strscpy(uinfo->value.enumerated.name,
ice->ext_clock_names[
uinfo->value.enumerated.item - hw_rates_count]);
else
@@ -1847,7 +1826,7 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned int i, rate;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
if (ice->is_spdif_master(ice)) {
ucontrol->value.enumerated.item[0] = ice->hw_rates->count +
ice->get_spdif_master_type(ice);
@@ -1861,7 +1840,6 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
}
}
}
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
@@ -1892,29 +1870,31 @@ static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
unsigned int old_rate, new_rate;
unsigned int item = ucontrol->value.enumerated.item[0];
unsigned int first_ext_clock = ice->hw_rates->count;
+ bool set_pro_rate = false;
if (item > first_ext_clock + ice->ext_clock_count - 1)
return -EINVAL;
/* if rate = 0 => external clock */
- spin_lock_irq(&ice->reg_lock);
- if (ice->is_spdif_master(ice))
- old_rate = 0;
- else
- old_rate = ice->get_rate(ice);
- if (item >= first_ext_clock) {
- /* switching to external clock */
- ice->set_spdif_clock(ice, item - first_ext_clock);
- new_rate = 0;
- } else {
- /* internal on-card clock */
- new_rate = ice->hw_rates->list[item];
- ice->pro_rate_default = new_rate;
- spin_unlock_irq(&ice->reg_lock);
- snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1);
- spin_lock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ if (ice->is_spdif_master(ice))
+ old_rate = 0;
+ else
+ old_rate = ice->get_rate(ice);
+ if (item >= first_ext_clock) {
+ /* switching to external clock */
+ ice->set_spdif_clock(ice, item - first_ext_clock);
+ new_rate = 0;
+ } else {
+ /* internal on-card clock */
+ new_rate = ice->hw_rates->list[item];
+ ice->pro_rate_default = new_rate;
+ set_pro_rate = true;
+ }
}
- spin_unlock_irq(&ice->reg_lock);
+
+ if (set_pro_rate)
+ snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1);
/* the first switch to the ext. clock mode? */
if (old_rate != new_rate && !new_rate) {
@@ -1930,7 +1910,7 @@ static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
return old_rate != new_rate;
}
-static struct snd_kcontrol_new snd_vt1724_pro_internal_clock __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_pro_internal_clock = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Internal Clock",
.info = snd_vt1724_pro_internal_clock_info,
@@ -1954,14 +1934,13 @@ static int snd_vt1724_pro_rate_locking_put(struct snd_kcontrol *kcontrol,
int change = 0, nval;
nval = ucontrol->value.integer.value[0] ? 1 : 0;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
change = PRO_RATE_LOCKED != nval;
PRO_RATE_LOCKED = nval;
- spin_unlock_irq(&ice->reg_lock);
return change;
}
-static struct snd_kcontrol_new snd_vt1724_pro_rate_locking __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_pro_rate_locking = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Locking",
.info = snd_vt1724_pro_rate_locking_info,
@@ -1985,14 +1964,13 @@ static int snd_vt1724_pro_rate_reset_put(struct snd_kcontrol *kcontrol,
int change = 0, nval;
nval = ucontrol->value.integer.value[0] ? 1 : 0;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
change = PRO_RATE_RESET != nval;
PRO_RATE_RESET = nval;
- spin_unlock_irq(&ice->reg_lock);
return change;
}
-static struct snd_kcontrol_new snd_vt1724_pro_rate_reset __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_pro_rate_reset = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Reset",
.info = snd_vt1724_pro_rate_reset_info,
@@ -2007,19 +1985,13 @@ static struct snd_kcontrol_new snd_vt1724_pro_rate_reset __devinitdata = {
static int snd_vt1724_pro_route_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {
+ static const char * const texts[] = {
"PCM Out", /* 0 */
"H/W In 0", "H/W In 1", /* 1-2 */
"IEC958 In L", "IEC958 In R", /* 3-4 */
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 5;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 5, texts);
}
static inline int analog_route_shift(int idx)
@@ -2114,7 +2086,7 @@ static int snd_vt1724_pro_route_spdif_put(struct snd_kcontrol *kcontrol,
digital_route_shift(idx));
}
-static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata =
+static const struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "H/W Playback Route",
@@ -2123,7 +2095,7 @@ static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata =
.put = snd_vt1724_pro_route_analog_put,
};
-static struct snd_kcontrol_new snd_vt1724_mixer_pro_spdif_route __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_mixer_pro_spdif_route = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, NONE) "Route",
.info = snd_vt1724_pro_route_info,
@@ -2149,17 +2121,16 @@ static int snd_vt1724_pro_peak_get(struct snd_kcontrol *kcontrol,
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int idx;
- spin_lock_irq(&ice->reg_lock);
+ guard(spinlock_irq)(&ice->reg_lock);
for (idx = 0; idx < 22; idx++) {
outb(idx, ICEMT1724(ice, MONITOR_PEAKINDEX));
ucontrol->value.integer.value[idx] =
inb(ICEMT1724(ice, MONITOR_PEAKDATA));
}
- spin_unlock_irq(&ice->reg_lock);
return 0;
}
-static struct snd_kcontrol_new snd_vt1724_mixer_pro_peak __devinitdata = {
+static const struct snd_kcontrol_new snd_vt1724_mixer_pro_peak = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Multi Track Peak",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
@@ -2168,12 +2139,39 @@ static struct snd_kcontrol_new snd_vt1724_mixer_pro_peak __devinitdata = {
};
/*
- *
- */
+ ooAoo cards with no controls
+*/
+static const unsigned char ooaoo_sq210_eeprom[] = {
+ [ICE_EEP2_SYSCONF] = 0x4c, /* 49MHz crystal, no mpu401, no ADC,
+ 1xDACs */
+ [ICE_EEP2_ACLINK] = 0x80, /* I2S */
+ [ICE_EEP2_I2S] = 0x78, /* no volume, 96k, 24bit, 192k */
+ [ICE_EEP2_SPDIF] = 0xc1, /* out-en, out-int, out-ext */
+ [ICE_EEP2_GPIO_DIR] = 0x00, /* no GPIOs are used */
+ [ICE_EEP2_GPIO_DIR1] = 0x00,
+ [ICE_EEP2_GPIO_DIR2] = 0x00,
+ [ICE_EEP2_GPIO_MASK] = 0xff,
+ [ICE_EEP2_GPIO_MASK1] = 0xff,
+ [ICE_EEP2_GPIO_MASK2] = 0xff,
+
+ [ICE_EEP2_GPIO_STATE] = 0x00, /* inputs */
+ [ICE_EEP2_GPIO_STATE1] = 0x00, /* all 1, but GPIO_CPLD_RW
+ and GPIO15 always zero */
+ [ICE_EEP2_GPIO_STATE2] = 0x00, /* inputs */
+};
-static struct snd_ice1712_card_info no_matched __devinitdata;
-static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
+static const struct snd_ice1712_card_info snd_vt1724_ooaoo_cards[] = {
+ {
+ .name = "ooAoo SQ210a",
+ .model = "sq210a",
+ .eeprom_size = sizeof(ooaoo_sq210_eeprom),
+ .eeprom_data = ooaoo_sq210_eeprom,
+ },
+ { } /* terminator */
+};
+
+static const struct snd_ice1712_card_info *card_tables[] = {
snd_vt1724_revo_cards,
snd_vt1724_amp_cards,
snd_vt1724_aureon_cards,
@@ -2187,6 +2185,8 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
snd_vt1724_wtm_cards,
snd_vt1724_se_cards,
snd_vt1724_qtet_cards,
+ snd_vt1724_ooaoo_cards,
+ snd_vt1724_psc724_cards,
NULL,
};
@@ -2200,7 +2200,7 @@ static void wait_i2c_busy(struct snd_ice1712 *ice)
while ((inb(ICEREG1724(ice, I2C_CTRL)) & VT1724_I2C_BUSY) && t--)
;
if (t == -1)
- printk(KERN_ERR "ice1724: i2c busy timeout\n");
+ dev_err(ice->card->dev, "i2c busy timeout\n");
}
unsigned char snd_vt1724_read_i2c(struct snd_ice1712 *ice,
@@ -2208,15 +2208,14 @@ unsigned char snd_vt1724_read_i2c(struct snd_ice1712 *ice,
{
unsigned char val;
- mutex_lock(&ice->i2c_mutex);
+ guard(mutex)(&ice->i2c_mutex);
wait_i2c_busy(ice);
outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR));
outb(dev & ~VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR));
wait_i2c_busy(ice);
val = inb(ICEREG1724(ice, I2C_DATA));
- mutex_unlock(&ice->i2c_mutex);
/*
- printk(KERN_DEBUG "i2c_read: [0x%x,0x%x] = 0x%x\n", dev, addr, val);
+ dev_dbg(ice->card->dev, "i2c_read: [0x%x,0x%x] = 0x%x\n", dev, addr, val);
*/
return val;
}
@@ -2224,24 +2223,23 @@ unsigned char snd_vt1724_read_i2c(struct snd_ice1712 *ice,
void snd_vt1724_write_i2c(struct snd_ice1712 *ice,
unsigned char dev, unsigned char addr, unsigned char data)
{
- mutex_lock(&ice->i2c_mutex);
+ guard(mutex)(&ice->i2c_mutex);
wait_i2c_busy(ice);
/*
- printk(KERN_DEBUG "i2c_write: [0x%x,0x%x] = 0x%x\n", dev, addr, data);
+ dev_dbg(ice->card->dev, "i2c_write: [0x%x,0x%x] = 0x%x\n", dev, addr, data);
*/
outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR));
outb(data, ICEREG1724(ice, I2C_DATA));
outb(dev | VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR));
wait_i2c_busy(ice);
- mutex_unlock(&ice->i2c_mutex);
}
-static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
- const char *modelname)
+static int snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
+ const char *modelname)
{
const int dev = 0xa0; /* EEPROM device address */
unsigned int i, size;
- struct snd_ice1712_card_info * const *tbl, *c;
+ const struct snd_ice1712_card_info * const *tbl, *c;
if (!modelname || !*modelname) {
ice->eeprom.subvendor = 0;
@@ -2264,45 +2262,52 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
((unsigned int)swab16(vendor) << 16) | swab16(device);
if (ice->eeprom.subvendor == 0 ||
ice->eeprom.subvendor == (unsigned int)-1) {
- printk(KERN_ERR "ice1724: No valid ID is found\n");
+ dev_err(ice->card->dev,
+ "No valid ID is found\n");
return -ENXIO;
}
}
}
for (tbl = card_tables; *tbl; tbl++) {
- for (c = *tbl; c->subvendor; c++) {
+ for (c = *tbl; c->name; c++) {
if (modelname && c->model &&
!strcmp(modelname, c->model)) {
- printk(KERN_INFO "ice1724: Using board model %s\n",
+ dev_info(ice->card->dev,
+ "Using board model %s\n",
c->name);
ice->eeprom.subvendor = c->subvendor;
} else if (c->subvendor != ice->eeprom.subvendor)
continue;
+ ice->card_info = c;
if (!c->eeprom_size || !c->eeprom_data)
goto found;
/* if the EEPROM is given by the driver, use it */
- snd_printdd("using the defined eeprom..\n");
+ dev_dbg(ice->card->dev, "using the defined eeprom..\n");
ice->eeprom.version = 2;
ice->eeprom.size = c->eeprom_size + 6;
memcpy(ice->eeprom.data, c->eeprom_data, c->eeprom_size);
goto read_skipped;
}
}
- printk(KERN_WARNING "ice1724: No matching model found for ID 0x%x\n",
+ dev_warn(ice->card->dev, "No matching model found for ID 0x%x\n",
ice->eeprom.subvendor);
+#ifdef CONFIG_PM_SLEEP
+ /* assume AC97-only card which can suspend without additional code */
+ ice->pm_suspend_enabled = 1;
+#endif
found:
ice->eeprom.size = snd_vt1724_read_i2c(ice, dev, 0x04);
if (ice->eeprom.size < 6)
ice->eeprom.size = 32;
else if (ice->eeprom.size > 32) {
- printk(KERN_ERR "ice1724: Invalid EEPROM (size = %i)\n",
+ dev_err(ice->card->dev, "Invalid EEPROM (size = %i)\n",
ice->eeprom.size);
return -EIO;
}
ice->eeprom.version = snd_vt1724_read_i2c(ice, dev, 0x05);
- if (ice->eeprom.version != 2)
- printk(KERN_WARNING "ice1724: Invalid EEPROM version %i\n",
+ if (ice->eeprom.version != 1 && ice->eeprom.version != 2)
+ dev_warn(ice->card->dev, "Invalid EEPROM version %i\n",
ice->eeprom.version);
size = ice->eeprom.size - 6;
for (i = 0; i < size; i++)
@@ -2354,7 +2359,7 @@ static int snd_vt1724_chip_init(struct snd_ice1712 *ice)
return 0;
}
-static int __devinit snd_vt1724_spdif_build_controls(struct snd_ice1712 *ice)
+static int snd_vt1724_spdif_build_controls(struct snd_ice1712 *ice)
{
int err;
struct snd_kcontrol *kctl;
@@ -2373,30 +2378,34 @@ static int __devinit snd_vt1724_spdif_build_controls(struct snd_ice1712 *ice)
if (err < 0)
return err;
- err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_default, ice));
+ kctl = snd_ctl_new1(&snd_vt1724_spdif_default, ice);
+ kctl->id.device = ice->pcm->device;
+ err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
+ kctl = snd_ctl_new1(&snd_vt1724_spdif_maskc, ice);
kctl->id.device = ice->pcm->device;
- err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_maskc, ice));
+ err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
+ kctl = snd_ctl_new1(&snd_vt1724_spdif_maskp, ice);
kctl->id.device = ice->pcm->device;
- err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_maskp, ice));
+ err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
- kctl->id.device = ice->pcm->device;
#if 0 /* use default only */
- err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_stream, ice));
+ kctl = snd_ctl_new1(&snd_vt1724_spdif_stream, ice);
+ kctl->id.device = ice->pcm->device;
+ err = snd_ctl_add(ice->card, kctl);
if (err < 0)
return err;
- kctl->id.device = ice->pcm->device;
ice->spdif.stream_ctl = kctl;
#endif
return 0;
}
-static int __devinit snd_vt1724_build_controls(struct snd_ice1712 *ice)
+static int snd_vt1724_build_controls(struct snd_ice1712 *ice)
{
int err;
@@ -2424,61 +2433,33 @@ static int __devinit snd_vt1724_build_controls(struct snd_ice1712 *ice)
return err;
}
- err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_mixer_pro_peak, ice));
- if (err < 0)
- return err;
-
- return 0;
+ return snd_ctl_add(ice->card,
+ snd_ctl_new1(&snd_vt1724_mixer_pro_peak, ice));
}
-static int snd_vt1724_free(struct snd_ice1712 *ice)
+static void snd_vt1724_free(struct snd_card *card)
{
- if (!ice->port)
- goto __hw_end;
+ struct snd_ice1712 *ice = card->private_data;
+
/* mask all interrupts */
outb(0xff, ICEMT1724(ice, DMA_INT_MASK));
outb(0xff, ICEREG1724(ice, IRQMASK));
- /* --- */
-__hw_end:
- if (ice->irq >= 0)
- free_irq(ice->irq, ice);
- pci_release_regions(ice->pci);
- snd_ice1712_akm4xxx_free(ice);
- pci_disable_device(ice->pci);
- kfree(ice->spec);
- kfree(ice);
- return 0;
-}
-static int snd_vt1724_dev_free(struct snd_device *device)
-{
- struct snd_ice1712 *ice = device->device_data;
- return snd_vt1724_free(ice);
+ snd_ice1712_akm4xxx_free(ice);
}
-static int __devinit snd_vt1724_create(struct snd_card *card,
- struct pci_dev *pci,
- const char *modelname,
- struct snd_ice1712 **r_ice1712)
+static int snd_vt1724_create(struct snd_card *card,
+ struct pci_dev *pci,
+ const char *modelname)
{
- struct snd_ice1712 *ice;
+ struct snd_ice1712 *ice = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_vt1724_dev_free,
- };
-
- *r_ice1712 = NULL;
/* enable PCI device */
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
- ice = kzalloc(sizeof(*ice), GFP_KERNEL);
- if (ice == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
ice->vt1724 = 1;
spin_lock_init(&ice->reg_lock);
mutex_init(&ice->gpio_mutex);
@@ -2495,47 +2476,29 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
ice->irq = -1;
pci_set_master(pci);
snd_vt1724_proc_init(ice);
- synchronize_irq(pci->irq);
-
- card->private_data = ice;
- err = pci_request_regions(pci, "ICE1724");
- if (err < 0) {
- kfree(ice);
- pci_disable_device(pci);
+ err = pcim_request_all_regions(pci, "ICE1724");
+ if (err < 0)
return err;
- }
ice->port = pci_resource_start(pci, 0);
ice->profi_port = pci_resource_start(pci, 1);
- if (request_irq(pci->irq, snd_vt1724_interrupt,
- IRQF_SHARED, "ICE1724", ice)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_vt1724_free(ice);
+ if (devm_request_irq(&pci->dev, pci->irq, snd_vt1724_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, ice)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EIO;
}
ice->irq = pci->irq;
+ card->sync_irq = ice->irq;
+ card->private_free = snd_vt1724_free;
snd_vt1724_chip_reset(ice);
- if (snd_vt1724_read_eeprom(ice, modelname) < 0) {
- snd_vt1724_free(ice);
+ if (snd_vt1724_read_eeprom(ice, modelname) < 0)
return -EIO;
- }
- if (snd_vt1724_chip_init(ice) < 0) {
- snd_vt1724_free(ice);
+ if (snd_vt1724_chip_init(ice) < 0)
return -EIO;
- }
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops);
- if (err < 0) {
- snd_vt1724_free(ice);
- return err;
- }
- snd_card_set_dev(card, &pci->dev);
-
- *r_ice1712 = ice;
return 0;
}
@@ -2546,14 +2509,14 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
*
*/
-static int __devinit snd_vt1724_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_vt1724_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
struct snd_ice1712 *ice;
int pcm_dev = 0, err;
- struct snd_ice1712_card_info * const *tbl, *c;
+ const struct snd_ice1712_card_info *c;
if (dev >= SNDRV_CARDS)
return -ENODEV;
@@ -2562,41 +2525,34 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*ice), &card);
if (err < 0)
return err;
+ ice = card->private_data;
- strcpy(card->driver, "ICE1724");
- strcpy(card->shortname, "ICEnsemble ICE1724");
+ strscpy(card->driver, "ICE1724");
+ strscpy(card->shortname, "ICEnsemble ICE1724");
- err = snd_vt1724_create(card, pci, model[dev], &ice);
- if (err < 0) {
- snd_card_free(card);
+ err = snd_vt1724_create(card, pci, model[dev]);
+ if (err < 0)
return err;
- }
/* field init before calling chip_init */
ice->ext_clock_count = 0;
- for (tbl = card_tables; *tbl; tbl++) {
- for (c = *tbl; c->subvendor; c++) {
- if (c->subvendor == ice->eeprom.subvendor) {
- strcpy(card->shortname, c->name);
- if (c->driver) /* specific driver? */
- strcpy(card->driver, c->driver);
- if (c->chip_init) {
- err = c->chip_init(ice);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
- }
- goto __found;
- }
+ c = ice->card_info;
+ if (c) {
+ strscpy(card->shortname, c->name);
+ if (c->driver) /* specific driver? */
+ strscpy(card->driver, c->driver);
+ if (c->chip_init) {
+ err = c->chip_init(ice);
+ if (err < 0)
+ return err;
}
}
- c = &no_matched;
-__found:
+
/*
* VT1724 has separate DMAs for the analog and the SPDIF streams while
* ICE1712 has only one for both (mixed up).
@@ -2627,63 +2583,47 @@ __found:
set_std_hw_rates(ice);
err = snd_vt1724_pcm_profi(ice, pcm_dev++);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
err = snd_vt1724_pcm_spdif(ice, pcm_dev++);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
err = snd_vt1724_pcm_indep(ice, pcm_dev++);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
err = snd_vt1724_ac97_mixer(ice);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
err = snd_vt1724_build_controls(ice);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
if (ice->pcm && ice->has_spdif) { /* has SPDIF I/O */
err = snd_vt1724_spdif_build_controls(ice);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
}
- if (c->build_controls) {
+ if (c && c->build_controls) {
err = c->build_controls(ice);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
}
- if (!c->no_mpu401) {
+ if (!c || !c->no_mpu401) {
if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) {
struct snd_rawmidi *rmidi;
err = snd_rawmidi_new(card, "MIDI", 0, 1, 1, &rmidi);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
ice->rmidi[0] = rmidi;
rmidi->private_data = ice;
- strcpy(rmidi->name, "ICE1724 MIDI");
+ strscpy(rmidi->name, "ICE1724 MIDI");
rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_DUPLEX;
@@ -2705,25 +2645,23 @@ __found:
card->shortname, ice->port, ice->irq);
err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
dev++;
return 0;
}
-static void __devexit snd_vt1724_remove(struct pci_dev *pci)
+static int snd_vt1724_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_vt1724_probe(pci, pci_id));
}
-#ifdef CONFIG_PM
-static int snd_vt1724_suspend(struct pci_dev *pci, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int snd_vt1724_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_ice1712 *ice = card->private_data;
if (!ice->pm_suspend_enabled)
@@ -2731,45 +2669,28 @@ static int snd_vt1724_suspend(struct pci_dev *pci, pm_message_t state)
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- snd_pcm_suspend_all(ice->pcm);
- snd_pcm_suspend_all(ice->pcm_pro);
- snd_pcm_suspend_all(ice->pcm_ds);
snd_ac97_suspend(ice->ac97);
- spin_lock_irq(&ice->reg_lock);
- ice->pm_saved_is_spdif_master = ice->is_spdif_master(ice);
- ice->pm_saved_spdif_ctrl = inw(ICEMT1724(ice, SPDIF_CTRL));
- ice->pm_saved_spdif_cfg = inb(ICEREG1724(ice, SPDIF_CFG));
- ice->pm_saved_route = inl(ICEMT1724(ice, ROUTE_PLAYBACK));
- spin_unlock_irq(&ice->reg_lock);
+ scoped_guard(spinlock_irq, &ice->reg_lock) {
+ ice->pm_saved_is_spdif_master = ice->is_spdif_master(ice);
+ ice->pm_saved_spdif_ctrl = inw(ICEMT1724(ice, SPDIF_CTRL));
+ ice->pm_saved_spdif_cfg = inb(ICEREG1724(ice, SPDIF_CFG));
+ ice->pm_saved_route = inl(ICEMT1724(ice, ROUTE_PLAYBACK));
+ }
if (ice->pm_suspend)
ice->pm_suspend(ice);
-
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int snd_vt1724_resume(struct pci_dev *pci)
+static int snd_vt1724_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct snd_ice1712 *ice = card->private_data;
if (!ice->pm_suspend_enabled)
return 0;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
-
- if (pci_enable_device(pci) < 0) {
- snd_card_disconnect(card);
- return -EIO;
- }
-
- pci_set_master(pci);
-
snd_vt1724_chip_reset(ice);
if (snd_vt1724_chip_init(ice) < 0) {
@@ -2785,7 +2706,12 @@ static int snd_vt1724_resume(struct pci_dev *pci)
ice->set_spdif_clock(ice, 0);
} else {
/* internal on-card clock */
- snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1);
+ int rate;
+ if (ice->cur_rate)
+ rate = ice->cur_rate;
+ else
+ rate = ice->pro_rate_default;
+ snd_vt1724_set_pro_rate(ice, rate, 1);
}
update_spdif_bits(ice, ice->pm_saved_spdif_ctrl);
@@ -2793,34 +2719,25 @@ static int snd_vt1724_resume(struct pci_dev *pci)
outb(ice->pm_saved_spdif_cfg, ICEREG1724(ice, SPDIF_CFG));
outl(ice->pm_saved_route, ICEMT1724(ice, ROUTE_PLAYBACK));
- if (ice->ac97)
- snd_ac97_resume(ice->ac97);
+ snd_ac97_resume(ice->ac97);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif
-static struct pci_driver driver = {
- .name = "ICE1724",
+static SIMPLE_DEV_PM_OPS(snd_vt1724_pm, snd_vt1724_suspend, snd_vt1724_resume);
+#define SND_VT1724_PM_OPS &snd_vt1724_pm
+#else
+#define SND_VT1724_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct pci_driver vt1724_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_vt1724_ids,
.probe = snd_vt1724_probe,
- .remove = __devexit_p(snd_vt1724_remove),
-#ifdef CONFIG_PM
- .suspend = snd_vt1724_suspend,
- .resume = snd_vt1724_resume,
-#endif
+ .driver = {
+ .pm = SND_VT1724_PM_OPS,
+ },
};
-static int __init alsa_card_ice1724_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ice1724_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ice1724_init)
-module_exit(alsa_card_ice1724_exit)
+module_pci_driver(vt1724_driver);
diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c
index 98bc3b7681b5..d679842ae1bd 100644
--- a/sound/pci/ice1712/juli.c
+++ b/sound/pci/ice1712/juli.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
@@ -5,29 +6,13 @@
*
* Copyright (c) 2004 Jaroslav Kysela <perex@perex.cz>
* 2008 Pavel Hofman <dustin@seznam.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <sound/core.h>
#include <sound/tlv.h>
@@ -134,19 +119,19 @@ struct juli_spec {
/*
* Initial setup of the conversion array GPIO <-> rate
*/
-static unsigned int juli_rates[] = {
+static const unsigned int juli_rates[] = {
16000, 22050, 24000, 32000,
44100, 48000, 64000, 88200,
96000, 176400, 192000,
};
-static unsigned int gpio_vals[] = {
+static const unsigned int gpio_vals[] = {
GPIO_RATE_16000, GPIO_RATE_22050, GPIO_RATE_24000, GPIO_RATE_32000,
GPIO_RATE_44100, GPIO_RATE_48000, GPIO_RATE_64000, GPIO_RATE_88200,
GPIO_RATE_96000, GPIO_RATE_176400, GPIO_RATE_192000,
};
-static struct snd_pcm_hw_constraint_list juli_rates_info = {
+static const struct snd_pcm_hw_constraint_list juli_rates_info = {
.count = ARRAY_SIZE(juli_rates),
.list = juli_rates,
.mask = 0,
@@ -245,7 +230,7 @@ static void juli_akm_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
/* AK5385 first, since it requires cold reset affecting both codecs */
old_gpio = ice->gpio.get_data(ice);
new_gpio = (old_gpio & ~GPIO_AK5385A_MASK) | ak5385_pins;
- /* printk(KERN_DEBUG "JULI - ak5385 set_rate_val: new gpio 0x%x\n",
+ /* dev_dbg(ice->card->dev, "JULI - ak5385 set_rate_val: new gpio 0x%x\n",
new_gpio); */
ice->gpio.set_data(ice, new_gpio);
@@ -283,7 +268,7 @@ static const struct snd_akm4xxx_dac_channel juli_dac[] = {
};
-static struct snd_akm4xxx akm_juli_dac __devinitdata = {
+static const struct snd_akm4xxx akm_juli_dac = {
.type = SND_AK4358,
.num_dacs = 8, /* DAC1 - analog out
DAC2 - analog in monitor
@@ -345,7 +330,7 @@ static int juli_mute_put(struct snd_kcontrol *kcontrol,
new_gpio = old_gpio &
~((unsigned int) kcontrol->private_value);
}
- /* printk(KERN_DEBUG
+ /* dev_dbg(ice->card->dev,
"JULI - mute/unmute: control_value: 0x%x, old_gpio: 0x%x, "
"new_gpio 0x%x\n",
(unsigned int)ucontrol->value.integer.value[0], old_gpio,
@@ -358,7 +343,7 @@ static int juli_mute_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new juli_mute_controls[] __devinitdata = {
+static const struct snd_kcontrol_new juli_mute_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -412,7 +397,7 @@ static struct snd_kcontrol_new juli_mute_controls[] __devinitdata = {
},
};
-static char *slave_vols[] __devinitdata = {
+static const char * const follower_vols[] = {
PCM_VOLUME,
MONITOR_AN_IN_VOLUME,
MONITOR_DIG_IN_VOLUME,
@@ -420,34 +405,10 @@ static char *slave_vols[] __devinitdata = {
NULL
};
-static __devinitdata
+static
DECLARE_TLV_DB_SCALE(juli_master_db_scale, -6350, 50, 1);
-static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card,
- const char *name)
-{
- struct snd_ctl_elem_id sid;
- memset(&sid, 0, sizeof(sid));
- /* FIXME: strcpy is bad. */
- strcpy(sid.name, name);
- sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- return snd_ctl_find_id(card, &sid);
-}
-
-static void __devinit add_slaves(struct snd_card *card,
- struct snd_kcontrol *master, char **list)
-{
- for (; *list; list++) {
- struct snd_kcontrol *slave = ctl_find(card, *list);
- /* printk(KERN_DEBUG "add_slaves - %s\n", *list); */
- if (slave) {
- /* printk(KERN_DEBUG "slave %s found\n", *list); */
- snd_ctl_add_slave(master, slave);
- }
- }
-}
-
-static int __devinit juli_add_controls(struct snd_ice1712 *ice)
+static int juli_add_controls(struct snd_ice1712 *ice)
{
struct juli_spec *spec = ice->spec;
int err;
@@ -469,24 +430,23 @@ static int __devinit juli_add_controls(struct snd_ice1712 *ice)
juli_master_db_scale);
if (!vmaster)
return -ENOMEM;
- add_slaves(ice->card, vmaster, slave_vols);
err = snd_ctl_add(ice->card, vmaster);
if (err < 0)
return err;
+ err = snd_ctl_add_followers(ice->card, vmaster, follower_vols);
+ if (err < 0)
+ return err;
/* only capture SPDIF over AK4114 */
- err = snd_ak4114_build(spec->ak4114, NULL,
+ return snd_ak4114_build(spec->ak4114, NULL,
ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
- if (err < 0)
- return err;
- return 0;
}
/*
* suspend/resume
* */
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int juli_resume(struct snd_ice1712 *ice)
{
struct snd_akm4xxx *ak = ice->akm;
@@ -494,15 +454,17 @@ static int juli_resume(struct snd_ice1712 *ice)
/* akm4358 un-reset, un-mute */
snd_akm4xxx_reset(ak, 0);
/* reinit ak4114 */
- snd_ak4114_reinit(spec->ak4114);
+ snd_ak4114_resume(spec->ak4114);
return 0;
}
static int juli_suspend(struct snd_ice1712 *ice)
{
struct snd_akm4xxx *ak = ice->akm;
+ struct juli_spec *spec = ice->spec;
/* akm4358 reset and soft-mute */
snd_akm4xxx_reset(ak, 1);
+ snd_ak4114_suspend(spec->ak4114);
return 0;
}
#endif
@@ -536,7 +498,7 @@ static void juli_set_rate(struct snd_ice1712 *ice, unsigned int rate)
old = ice->gpio.get_data(ice);
new = (old & ~GPIO_RATE_MASK) | get_gpio_val(rate);
- /* printk(KERN_DEBUG "JULI - set_rate: old %x, new %x\n",
+ /* dev_dbg(ice->card->dev, "JULI - set_rate: old %x, new %x\n",
old & GPIO_RATE_MASK,
new & GPIO_RATE_MASK); */
@@ -573,13 +535,13 @@ static void juli_ak4114_change(struct ak4114 *ak4114, unsigned char c0,
if (ice->is_spdif_master(ice) && c1) {
/* only for SPDIF master mode, rate was changed */
rate = snd_ak4114_external_rate(ak4114);
- /* printk(KERN_DEBUG "ak4114 - input rate changed to %d\n",
+ /* dev_dbg(ice->card->dev, "ak4114 - input rate changed to %d\n",
rate); */
juli_akm_set_rate_val(ice->akm, rate);
}
}
-static int __devinit juli_init(struct snd_ice1712 *ice)
+static int juli_init(struct snd_ice1712 *ice)
{
static const unsigned char ak4114_init_vals[] = {
/* AK4117_REG_PWRDN */ AK4114_RST | AK4114_PWN |
@@ -628,7 +590,7 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
#endif
if (spec->analog) {
- printk(KERN_INFO "juli@: analog I/O detected\n");
+ dev_info(ice->card->dev, "juli@: analog I/O detected\n");
ice->num_total_dacs = 2;
ice->num_total_adcs = 2;
@@ -652,7 +614,7 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
ice->spdif.ops.open = juli_spdif_in_open;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
ice->pm_resume = juli_resume;
ice->pm_suspend = juli_suspend;
ice->pm_suspend_enabled = 1;
@@ -667,7 +629,7 @@ static int __devinit juli_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char juli_eeprom[] __devinitdata = {
+static const unsigned char juli_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401, 1xADC, 1xDACs,
SPDIF in */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
@@ -686,7 +648,7 @@ static unsigned char juli_eeprom[] __devinitdata = {
};
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_juli_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_juli_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_JULI,
.name = "ESI Juli@",
diff --git a/sound/pci/ice1712/juli.h b/sound/pci/ice1712/juli.h
index d9f8534fd92e..9c22d4e73ee3 100644
--- a/sound/pci/ice1712/juli.h
+++ b/sound/pci/ice1712/juli.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SOUND_JULI_H
#define __SOUND_JULI_H
diff --git a/sound/pci/ice1712/maya44.c b/sound/pci/ice1712/maya44.c
index 726fd4b92e19..551f478c59c4 100644
--- a/sound/pci/ice1712/maya44.c
+++ b/sound/pci/ice1712/maya44.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
@@ -5,26 +6,10 @@
*
* Copyright (c) 2009 Takashi Iwai <tiwai@suse.de>
* Based on the patches by Rainer Zimmermann <mail@lightshed.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/init.h>
#include <linux/slab.h>
-#include <linux/io.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
@@ -131,7 +116,7 @@ struct maya_vol_info {
unsigned char mux_bits[2]; /* extra bits for ADC mute */
};
-static struct maya_vol_info vol_info[WM_NUM_VOLS] = {
+static const struct maya_vol_info vol_info[WM_NUM_VOLS] = {
[WM_VOL_HP] = {
.maxval = 80,
.regs = { WM8776_REG_HEADPHONE_L, WM8776_REG_HEADPHONE_R },
@@ -173,7 +158,7 @@ static int maya_vol_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
unsigned int idx = kcontrol->private_value;
- struct maya_vol_info *vol = &vol_info[idx];
+ const struct maya_vol_info *vol = &vol_info[idx];
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
@@ -190,10 +175,9 @@ static int maya_vol_get(struct snd_kcontrol *kcontrol,
&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
unsigned int idx = kcontrol->private_value;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
ucontrol->value.integer.value[0] = wm->volumes[idx][0];
ucontrol->value.integer.value[1] = wm->volumes[idx][1];
- mutex_unlock(&chip->mutex);
return 0;
}
@@ -204,11 +188,11 @@ static int maya_vol_put(struct snd_kcontrol *kcontrol,
struct snd_wm8776 *wm =
&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
unsigned int idx = kcontrol->private_value;
- struct maya_vol_info *vol = &vol_info[idx];
+ const struct maya_vol_info *vol = &vol_info[idx];
unsigned int val, data;
int ch, changed = 0;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
for (ch = 0; ch < 2; ch++) {
val = ucontrol->value.integer.value[ch];
if (val > vol->maxval)
@@ -228,7 +212,6 @@ static int maya_vol_put(struct snd_kcontrol *kcontrol,
val ? 0 : vol->mux_bits[ch]);
wm->volumes[idx][ch] = val;
}
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -265,7 +248,7 @@ static int maya_sw_put(struct snd_kcontrol *kcontrol,
unsigned int mask, val;
int changed;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
mask = 1 << idx;
wm->switch_bits &= ~mask;
val = ucontrol->value.integer.value[0];
@@ -275,7 +258,6 @@ static int maya_sw_put(struct snd_kcontrol *kcontrol,
changed = wm8776_write_bits(chip->ice, wm,
GET_SW_VAL_REG(kcontrol->private_value),
mask, val ? mask : 0);
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -330,14 +312,13 @@ static int maya_gpio_sw_put(struct snd_kcontrol *kcontrol,
unsigned int val, mask;
int changed;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
mask = 1 << shift;
val = ucontrol->value.integer.value[0];
if (GET_GPIO_VAL_INV(kcontrol->private_value))
val = !val;
val = val ? mask : 0;
changed = maya_set_gpio_bits(chip->ice, mask, val);
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -358,17 +339,9 @@ static void wm8776_select_input(struct snd_maya44 *chip, int idx, int line)
static int maya_rec_src_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = { "Line", "Mic" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = ARRAY_SIZE(texts);
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ static const char * const texts[] = { "Line", "Mic" };
+
+ return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
}
static int maya_rec_src_get(struct snd_kcontrol *kcontrol,
@@ -392,11 +365,10 @@ static int maya_rec_src_put(struct snd_kcontrol *kcontrol,
int sel = ucontrol->value.enumerated.item[0];
int changed;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
changed = maya_set_gpio_bits(chip->ice, 1 << GPIO_MIC_RELAY,
sel ? (1 << GPIO_MIC_RELAY) : 0);
wm8776_select_input(chip, 0, sel ? MAYA_MIC_IN : MAYA_LINE_IN);
- mutex_unlock(&chip->mutex);
return changed;
}
@@ -407,20 +379,12 @@ static int maya_rec_src_put(struct snd_kcontrol *kcontrol,
static int maya_pb_route_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[] = {
+ static const char * const texts[] = {
"PCM Out", /* 0 */
"Input 1", "Input 2", "Input 3", "Input 4"
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = ARRAY_SIZE(texts);
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
}
static int maya_pb_route_shift(int idx)
@@ -455,7 +419,7 @@ static int maya_pb_route_put(struct snd_kcontrol *kcontrol,
* controls to be added
*/
-static struct snd_kcontrol_new maya_controls[] __devinitdata = {
+static const struct snd_kcontrol_new maya_controls[] = {
{
.name = "Crossmix Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -545,7 +509,7 @@ static struct snd_kcontrol_new maya_controls[] __devinitdata = {
},
};
-static int __devinit maya44_add_controls(struct snd_ice1712 *ice)
+static int maya44_add_controls(struct snd_ice1712 *ice)
{
int err, i;
@@ -562,8 +526,8 @@ static int __devinit maya44_add_controls(struct snd_ice1712 *ice)
/*
* initialize a wm8776 chip
*/
-static void __devinit wm8776_init(struct snd_ice1712 *ice,
- struct snd_wm8776 *wm, unsigned int addr)
+static void wm8776_init(struct snd_ice1712 *ice,
+ struct snd_wm8776 *wm, unsigned int addr)
{
static const unsigned short inits_wm8776[] = {
0x02, 0x100, /* R2: headphone L+R muted + update */
@@ -666,24 +630,23 @@ static void set_rate(struct snd_ice1712 *ice, unsigned int rate)
val |= 8;
val |= ratio << 4;
- mutex_lock(&chip->mutex);
+ guard(mutex)(&chip->mutex);
for (i = 0; i < 2; i++)
wm8776_write_bits(ice, &chip->wm[i],
WM8776_REG_MASTER_MODE_CONTROL,
0x180, val);
- mutex_unlock(&chip->mutex);
}
/*
* supported sample rates (to override the default one)
*/
-static unsigned int rates[] = {
+static const unsigned int rates[] = {
32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000
};
/* playback rates: 32..192 kHz */
-static struct snd_pcm_hw_constraint_list dac_rates = {
+static const struct snd_pcm_hw_constraint_list dac_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0
@@ -693,14 +656,14 @@ static struct snd_pcm_hw_constraint_list dac_rates = {
/*
* chip addresses on I2C bus
*/
-static unsigned char wm8776_addr[2] __devinitdata = {
+static const unsigned char wm8776_addr[2] = {
0x34, 0x36, /* codec 0 & 1 */
};
/*
* initialize the chip
*/
-static int __devinit maya44_init(struct snd_ice1712 *ice)
+static int maya44_init(struct snd_ice1712 *ice)
{
int i;
struct snd_maya44 *chip;
@@ -743,7 +706,7 @@ static int __devinit maya44_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char maya44_eeprom[] __devinitdata = {
+static const unsigned char maya44_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x45,
/* clock xin1=49.152MHz, mpu401, 2 stereo ADCs+DACs */
[ICE_EEP2_ACLINK] = 0x80,
@@ -765,7 +728,7 @@ static unsigned char maya44_eeprom[] __devinitdata = {
};
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_maya44_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_maya44_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_MAYA44,
.name = "ESI Maya44",
diff --git a/sound/pci/ice1712/maya44.h b/sound/pci/ice1712/maya44.h
index eafd03a8f4b5..f5a97d987a6f 100644
--- a/sound/pci/ice1712/maya44.h
+++ b/sound/pci/ice1712/maya44.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SOUND_MAYA44_H
#define __SOUND_MAYA44_H
diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c
index de29be8c9657..151b740ce66d 100644
--- a/sound/pci/ice1712/phase.c
+++ b/sound/pci/ice1712/phase.c
@@ -1,24 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble ICE1724 (Envy24)
*
* Lowlevel functions for Terratec PHASE 22
*
* Copyright (c) 2005 Misha Zhilin <misha@epiphan.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
/* PHASE 22 overview:
@@ -42,7 +28,6 @@
* Digital receiver: CS8414-CS (supported in this release)
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -103,13 +88,13 @@ static const unsigned char wm_vol[256] = {
#define WM_VOL_MAX (sizeof(wm_vol) - 1)
#define WM_VOL_MUTE 0x8000
-static struct snd_akm4xxx akm_phase22 __devinitdata = {
+static const struct snd_akm4xxx akm_phase22 = {
.type = SND_AK4524,
.num_dacs = 2,
.num_adcs = 2,
};
-static struct snd_ak4xxx_private akm_phase22_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_phase22_priv = {
.caddr = 2,
.cif = 1,
.data_mask = 1 << 4,
@@ -121,7 +106,7 @@ static struct snd_ak4xxx_private akm_phase22_priv __devinitdata = {
.mask_flags = 0,
};
-static int __devinit phase22_init(struct snd_ice1712 *ice)
+static int phase22_init(struct snd_ice1712 *ice)
{
struct snd_akm4xxx *ak;
int err;
@@ -158,7 +143,7 @@ static int __devinit phase22_init(struct snd_ice1712 *ice)
return 0;
}
-static int __devinit phase22_add_controls(struct snd_ice1712 *ice)
+static int phase22_add_controls(struct snd_ice1712 *ice)
{
int err = 0;
@@ -172,7 +157,7 @@ static int __devinit phase22_add_controls(struct snd_ice1712 *ice)
return 0;
}
-static unsigned char phase22_eeprom[] __devinitdata = {
+static const unsigned char phase22_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x28, /* clock 512, mpu 401,
spdif-in/1xADC, 1xDACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
@@ -189,7 +174,7 @@ static unsigned char phase22_eeprom[] __devinitdata = {
[ICE_EEP2_GPIO_STATE2] = 0x00,
};
-static unsigned char phase28_eeprom[] __devinitdata = {
+static const unsigned char phase28_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x2b, /* clock 512, mpu401,
spdif-in/1xADC, 4xDACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
@@ -302,10 +287,9 @@ static int wm_pcm_mute_get(struct snd_kcontrol *kcontrol,
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ?
0 : 1;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -379,7 +363,7 @@ static int wm_master_vol_put(struct snd_kcontrol *kcontrol,
return change;
}
-static int __devinit phase28_init(struct snd_ice1712 *ice)
+static int phase28_init(struct snd_ice1712 *ice)
{
static const unsigned short wm_inits_phase28[] = {
/* These come first to reduce init pop noise */
@@ -653,11 +637,10 @@ static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned short val;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
val = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
val = val > PCM_MIN ? (val - PCM_MIN) : 0;
ucontrol->value.integer.value[0] = val;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -722,19 +705,9 @@ static int phase28_deemp_put(struct snd_kcontrol *kcontrol,
static int phase28_oversampling_info(struct snd_kcontrol *k,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "128x", "64x" };
+ static const char * const texts[2] = { "128x", "64x" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items -
- 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
static int phase28_oversampling_get(struct snd_kcontrol *kcontrol,
@@ -770,7 +743,7 @@ static int phase28_oversampling_put(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
static const DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
-static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
+static const struct snd_kcontrol_new phase28_dac_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -885,7 +858,7 @@ static struct snd_kcontrol_new phase28_dac_controls[] __devinitdata = {
}
};
-static struct snd_kcontrol_new wm_controls[] __devinitdata = {
+static const struct snd_kcontrol_new wm_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
@@ -919,7 +892,7 @@ static struct snd_kcontrol_new wm_controls[] __devinitdata = {
}
};
-static int __devinit phase28_add_controls(struct snd_ice1712 *ice)
+static int phase28_add_controls(struct snd_ice1712 *ice)
{
unsigned int i, counts;
int err;
@@ -943,7 +916,7 @@ static int __devinit phase28_add_controls(struct snd_ice1712 *ice)
return 0;
}
-struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_phase_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_PHASE22,
.name = "Terratec PHASE 22",
diff --git a/sound/pci/ice1712/phase.h b/sound/pci/ice1712/phase.h
index 7fc22d9d442f..c019018e2e19 100644
--- a/sound/pci/ice1712/phase.h
+++ b/sound/pci/ice1712/phase.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_PHASE_H
#define __SOUND_PHASE_H
@@ -7,21 +8,6 @@
* Lowlevel functions for Terratec PHASE 22
*
* Copyright (c) 2005 Misha Zhilin <misha@epiphan.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define PHASE_DEVICE_DESC "{Terratec,Phase 22},"\
diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c
index cdb873f5da50..557473f0d59e 100644
--- a/sound/pci/ice1712/pontis.c
+++ b/sound/pci/ice1712/pontis.c
@@ -1,27 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
* Lowlevel functions for Pontis MS300
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -127,13 +112,12 @@ static int wm_dac_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
unsigned short val;
int i;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
val = wm_get(ice, WM_DAC_ATTEN_L + i) & 0xff;
val = val > DAC_MIN ? (val - DAC_MIN) : 0;
ucontrol->value.integer.value[i] = val;
}
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -143,7 +127,7 @@ static int wm_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
unsigned short oval, nval;
int i, idx, change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
nval = ucontrol->value.integer.value[i];
nval = (nval ? (nval + DAC_MIN) : 0) & 0xff;
@@ -155,7 +139,6 @@ static int wm_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
change = 1;
}
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -182,13 +165,12 @@ static int wm_adc_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
unsigned short val;
int i;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
val = wm_get(ice, WM_ADC_ATTEN_L + i) & 0xff;
val = val > ADC_MIN ? (val - ADC_MIN) : 0;
ucontrol->value.integer.value[i] = val;
}
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -198,7 +180,7 @@ static int wm_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
unsigned short ovol, nvol;
int i, idx, change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
nvol = ucontrol->value.integer.value[i];
nvol = nvol ? (nvol + ADC_MIN) : 0;
@@ -209,7 +191,6 @@ static int wm_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
change = 1;
}
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -223,9 +204,8 @@ static int wm_adc_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int bit = kcontrol->private_value;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = (wm_get(ice, WM_ADC_MUX) & (1 << bit)) ? 1 : 0;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -236,7 +216,7 @@ static int wm_adc_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
unsigned short oval, nval;
int change;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
nval = oval = wm_get(ice, WM_ADC_MUX);
if (ucontrol->value.integer.value[0])
nval |= (1 << bit);
@@ -246,7 +226,6 @@ static int wm_adc_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
if (change) {
wm_put(ice, WM_ADC_MUX, nval);
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -259,9 +238,8 @@ static int wm_bypass_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = (wm_get(ice, WM_OUT_MUX) & 0x04) ? 1 : 0;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -271,7 +249,7 @@ static int wm_bypass_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
unsigned short val, oval;
int change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
val = oval = wm_get(ice, WM_OUT_MUX);
if (ucontrol->value.integer.value[0])
val |= 0x04;
@@ -281,7 +259,6 @@ static int wm_bypass_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
wm_put(ice, WM_OUT_MUX, val);
change = 1;
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -294,9 +271,8 @@ static int wm_chswap_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL1) & 0xf0) != 0x90;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -306,7 +282,7 @@ static int wm_chswap_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
unsigned short val, oval;
int change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
oval = wm_get(ice, WM_DAC_CTRL1);
val = oval & 0x0f;
if (ucontrol->value.integer.value[0])
@@ -318,7 +294,6 @@ static int wm_chswap_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
wm_put_nocache(ice, WM_DAC_CTRL1, val);
change = 1;
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -418,22 +393,15 @@ static int cs_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_inf
"Optical", /* RXP1 */
"CD", /* RXP2 */
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, texts);
}
static int cs_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.enumerated.item[0] = ice->gpio.saved[0];
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -443,14 +411,13 @@ static int cs_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu
unsigned char val;
int change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
if (ucontrol->value.enumerated.item[0] != ice->gpio.saved[0]) {
ice->gpio.saved[0] = ucontrol->value.enumerated.item[0] & 3;
val = 0x80 | (ice->gpio.saved[0] << 3);
spi_write(ice, CS_DEV, 0x04, val);
change = 1;
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -470,10 +437,10 @@ static int pontis_gpio_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_e
static int pontis_gpio_mask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
/* 4-7 reserved */
ucontrol->value.integer.value[0] = (~ice->gpio.write_mask & 0xffff) | 0x00f0;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -482,22 +449,22 @@ static int pontis_gpio_mask_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned int val;
int changed;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
/* 4-7 reserved */
val = (~ucontrol->value.integer.value[0] & 0xffff) | 0x00f0;
changed = val != ice->gpio.write_mask;
ice->gpio.write_mask = val;
- mutex_unlock(&ice->gpio_mutex);
return changed;
}
static int pontis_gpio_dir_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
/* 4-7 reserved */
ucontrol->value.integer.value[0] = ice->gpio.direction & 0xff0f;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -506,23 +473,23 @@ static int pontis_gpio_dir_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned int val;
int changed;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
/* 4-7 reserved */
val = ucontrol->value.integer.value[0] & 0xff0f;
changed = (val != ice->gpio.direction);
ice->gpio.direction = val;
- mutex_unlock(&ice->gpio_mutex);
return changed;
}
static int pontis_gpio_data_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
ucontrol->value.integer.value[0] = snd_ice1712_gpio_read(ice) & 0xffff;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -531,7 +498,8 @@ static int pontis_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned int val, nval;
int changed = 0;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
snd_ice1712_gpio_set_dir(ice, ice->gpio.direction);
snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask);
val = snd_ice1712_gpio_read(ice) & 0xffff;
@@ -540,7 +508,6 @@ static int pontis_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
snd_ice1712_gpio_write(ice, nval);
changed = 1;
}
- mutex_unlock(&ice->gpio_mutex);
return changed;
}
@@ -550,7 +517,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_volume, -6400, 50, 1);
* mixers
*/
-static struct snd_kcontrol_new pontis_controls[] __devinitdata = {
+static const struct snd_kcontrol_new pontis_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -641,14 +608,14 @@ static void wm_proc_regs_write(struct snd_info_entry *entry, struct snd_info_buf
struct snd_ice1712 *ice = entry->private_data;
char line[64];
unsigned int reg, val;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x", &reg, &val) != 2)
continue;
if (reg <= 0x17 && val <= 0xffff)
wm_put(ice, reg, val);
}
- mutex_unlock(&ice->gpio_mutex);
}
static void wm_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
@@ -656,22 +623,17 @@ static void wm_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buff
struct snd_ice1712 *ice = entry->private_data;
int reg, val;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (reg = 0; reg <= 0x17; reg++) {
val = wm_get(ice, reg);
snd_iprintf(buffer, "%02x = %04x\n", reg, val);
}
- mutex_unlock(&ice->gpio_mutex);
}
static void wm_proc_init(struct snd_ice1712 *ice)
{
- struct snd_info_entry *entry;
- if (! snd_card_proc_new(ice->card, "wm_codec", &entry)) {
- snd_info_set_text_ops(entry, ice, wm_proc_regs_read);
- entry->mode |= S_IWUSR;
- entry->c.text.write = wm_proc_regs_write;
- }
+ snd_card_rw_proc_new(ice->card, "wm_codec", ice, wm_proc_regs_read,
+ wm_proc_regs_write);
}
static void cs_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
@@ -679,25 +641,22 @@ static void cs_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buff
struct snd_ice1712 *ice = entry->private_data;
int reg, val;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (reg = 0; reg <= 0x26; reg++) {
val = spi_read(ice, CS_DEV, reg);
snd_iprintf(buffer, "%02x = %02x\n", reg, val);
}
val = spi_read(ice, CS_DEV, 0x7f);
snd_iprintf(buffer, "%02x = %02x\n", 0x7f, val);
- mutex_unlock(&ice->gpio_mutex);
}
static void cs_proc_init(struct snd_ice1712 *ice)
{
- struct snd_info_entry *entry;
- if (! snd_card_proc_new(ice->card, "cs_codec", &entry))
- snd_info_set_text_ops(entry, ice, cs_proc_regs_read);
+ snd_card_ro_proc_new(ice->card, "cs_codec", ice, cs_proc_regs_read);
}
-static int __devinit pontis_add_controls(struct snd_ice1712 *ice)
+static int pontis_add_controls(struct snd_ice1712 *ice)
{
unsigned int i;
int err;
@@ -718,7 +677,7 @@ static int __devinit pontis_add_controls(struct snd_ice1712 *ice)
/*
* initialize the chip
*/
-static int __devinit pontis_init(struct snd_ice1712 *ice)
+static int pontis_init(struct snd_ice1712 *ice)
{
static const unsigned short wm_inits[] = {
/* These come first to reduce init pop noise */
@@ -768,7 +727,7 @@ static int __devinit pontis_init(struct snd_ice1712 *ice)
ice->num_total_dacs = 2;
ice->num_total_adcs = 2;
- /* to remeber the register values */
+ /* to remember the register values */
ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
if (! ice->akm)
return -ENOMEM;
@@ -805,7 +764,7 @@ static int __devinit pontis_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char pontis_eeprom[] __devinitdata = {
+static const unsigned char pontis_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x08, /* clock 256, mpu401, spdif-in/ADC, 1DAC */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0xf8, /* vol, 96k, 24bit, 192k */
@@ -822,7 +781,7 @@ static unsigned char pontis_eeprom[] __devinitdata = {
};
/* entry point */
-struct snd_ice1712_card_info snd_vt1720_pontis_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1720_pontis_cards[] = {
{
.subvendor = VT1720_SUBDEVICE_PONTIS_MS300,
.name = "Pontis MS300",
diff --git a/sound/pci/ice1712/pontis.h b/sound/pci/ice1712/pontis.h
index d0d1378b935c..bba01aeac7f0 100644
--- a/sound/pci/ice1712/pontis.h
+++ b/sound/pci/ice1712/pontis.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_PONTIS_H
#define __SOUND_PONTIS_H
@@ -7,21 +8,6 @@
* Lowlevel functions for Pontis MS300 boards
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define PONTIS_DEVICE_DESC "{Pontis,MS300},"
diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c
index e36ddb94c382..cd7db2b65b51 100644
--- a/sound/pci/ice1712/prodigy192.c
+++ b/sound/pci/ice1712/prodigy192.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
@@ -31,30 +32,14 @@
* Experimentally I found out that only a combination of
* OCKS0=1, OCKS1=1 (128fs, 64fs output) and ice1724 -
* VT1724_MT_I2S_MCLK_128X=0 (256fs input) yields correct
- * sampling rate. That means the the FPGA doubles the
+ * sampling rate. That means that the FPGA doubles the
* MCK01 rate.
*
* Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
* Copyright (c) 2003 Dimitromanolakis Apostolos <apostol@cs.utoronto.ca>
* Copyright (c) 2004 Kouichi ONO <co2b@ceres.dti.ne.jp>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -99,7 +84,7 @@ static int stac9460_dac_mute(struct snd_ice1712 *ice, int idx,
new = (~mute << 7 & 0x80) | (old & ~0x80);
change = (new != old);
if (change)
- /*printk ("Volume register 0x%02x: 0x%02x\n", idx, new);*/
+ /* dev_dbg(ice->card->dev, "Volume register 0x%02x: 0x%02x\n", idx, new);*/
stac9460_put(ice, idx, new);
return change;
}
@@ -125,21 +110,19 @@ static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
struct prodigy192_spec *spec = ice->spec;
- int idx, change;
+ int idx;
if (kcontrol->private_value)
idx = STAC946X_MASTER_VOLUME;
else
idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME;
/* due to possible conflicts with stac9460_set_rate_val, mutexing */
- mutex_lock(&spec->mute_mutex);
+ guard(mutex)(&spec->mute_mutex);
/*
- printk(KERN_DEBUG "Mute put: reg 0x%02x, ctrl value: 0x%02x\n", idx,
+ dev_dbg(ice->card->dev, "Mute put: reg 0x%02x, ctrl value: 0x%02x\n", idx,
ucontrol->value.integer.value[0]);
*/
- change = stac9460_dac_mute(ice, idx, ucontrol->value.integer.value[0]);
- mutex_unlock(&spec->mute_mutex);
- return change;
+ return stac9460_dac_mute(ice, idx, ucontrol->value.integer.value[0]);
}
/*
@@ -185,14 +168,9 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
tmp = stac9460_get(ice, idx);
ovol = 0x7f - (tmp & 0x7f);
change = (ovol != nvol);
- if (change) {
- ovol = (0x7f - nvol) | (tmp & 0x80);
- /*
- printk(KERN_DEBUG "DAC Volume: reg 0x%02x: 0x%02x\n",
- idx, ovol);
- */
+ if (change)
stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));
- }
+
return change;
}
@@ -283,17 +261,9 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "Line In", "Mic" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ static const char * const texts[2] = { "Line In", "Mic" };
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
@@ -344,12 +314,12 @@ static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate)
return;
/* change detected, setting master clock, muting first */
/* due to possible conflicts with mute controls - mutexing */
- mutex_lock(&spec->mute_mutex);
+ guard(mutex)(&spec->mute_mutex);
/* we have to remember current mute status for each DAC */
for (idx = 0; idx < 7 ; ++idx)
changed[idx] = stac9460_dac_mute(ice,
STAC946X_MASTER_VOLUME + idx, 0);
- /*printk(KERN_DEBUG "Rate change: %d, new MC: 0x%02x\n", rate, new);*/
+ /*dev_dbg(ice->card->dev, "Rate change: %d, new MC: 0x%02x\n", rate, new);*/
stac9460_put(ice, STAC946X_MASTER_CLOCKING, new);
udelay(10);
/* unmuting - only originally unmuted dacs -
@@ -358,7 +328,6 @@ static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate)
if (changed[idx])
stac9460_dac_mute(ice, STAC946X_MASTER_VOLUME + idx, 1);
}
- mutex_unlock(&spec->mute_mutex);
}
@@ -369,7 +338,7 @@ static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
* mixers
*/
-static struct snd_kcontrol_new stac_controls[] __devinitdata = {
+static const struct snd_kcontrol_new stac_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -562,15 +531,9 @@ static unsigned char prodigy192_ak4114_read(void *private_data,
static int ak4114_input_sw_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[2] = { "Toslink", "Coax" };
+ static const char * const texts[2] = { "Toslink", "Coax" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
}
@@ -607,7 +570,7 @@ static int ak4114_input_sw_put(struct snd_kcontrol *kcontrol,
}
-static struct snd_kcontrol_new ak4114_controls[] __devinitdata = {
+static const struct snd_kcontrol_new ak4114_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "MIODIO IEC958 Capture Input",
@@ -666,13 +629,12 @@ static void stac9460_proc_regs_read(struct snd_info_entry *entry,
static void stac9460_proc_init(struct snd_ice1712 *ice)
{
- struct snd_info_entry *entry;
- if (!snd_card_proc_new(ice->card, "stac9460_codec", &entry))
- snd_info_set_text_ops(entry, ice, stac9460_proc_regs_read);
+ snd_card_ro_proc_new(ice->card, "stac9460_codec", ice,
+ stac9460_proc_regs_read);
}
-static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice)
+static int prodigy192_add_controls(struct snd_ice1712 *ice)
{
struct prodigy192_spec *spec = ice->spec;
unsigned int i;
@@ -728,7 +690,7 @@ static int prodigy192_miodio_exists(struct snd_ice1712 *ice)
/*
* initialize the chip
*/
-static int __devinit prodigy192_init(struct snd_ice1712 *ice)
+static int prodigy192_init(struct snd_ice1712 *ice)
{
static const unsigned short stac_inits_prodigy[] = {
STAC946X_RESET, 0,
@@ -769,13 +731,12 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice)
/* from this moment if err = 0 then
* spec->ak4114 should not be null
*/
- snd_printdd("AK4114 initialized with status %d\n", err);
+ dev_dbg(ice->card->dev,
+ "AK4114 initialized with status %d\n", err);
} else
- snd_printdd("AK4114 not found\n");
- if (err < 0)
- return err;
+ dev_dbg(ice->card->dev, "AK4114 not found\n");
- return 0;
+ return err;
}
@@ -784,7 +745,7 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice)
* hence the driver needs to sets up it properly.
*/
-static unsigned char prodigy71_eeprom[] __devinitdata = {
+static const unsigned char prodigy71_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x6a, /* 49MHz crystal, mpu401,
* spdif-in+ 1 stereo ADC,
* 3 stereo DACs
@@ -808,7 +769,7 @@ static unsigned char prodigy71_eeprom[] __devinitdata = {
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_PRODIGY192VE,
.name = "Audiotrak Prodigy 192",
diff --git a/sound/pci/ice1712/prodigy192.h b/sound/pci/ice1712/prodigy192.h
index 16a53b459c72..7bfd769ba982 100644
--- a/sound/pci/ice1712/prodigy192.h
+++ b/sound/pci/ice1712/prodigy192.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SOUND_PRODIGY192_H
#define __SOUND_PRODIGY192_H
diff --git a/sound/pci/ice1712/prodigy_hifi.c b/sound/pci/ice1712/prodigy_hifi.c
index 6a9fee3ee78f..eac233093865 100644
--- a/sound/pci/ice1712/prodigy_hifi.c
+++ b/sound/pci/ice1712/prodigy_hifi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
@@ -7,25 +8,9 @@
* Copyright (c) 2007 Julian Scheel <julian@jusst.de>
* Copyright (c) 2007 allank
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -283,7 +268,7 @@ static int ak4396_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
int i;
int change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
if (ucontrol->value.integer.value[i] != spec->vol[i]) {
spec->vol[i] = ucontrol->value.integer.value[i];
@@ -292,13 +277,13 @@ static int ak4396_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
change = 1;
}
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
+static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
-static struct snd_kcontrol_new prodigy_hd2_controls[] __devinitdata = {
+static const struct snd_kcontrol_new prodigy_hd2_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -307,33 +292,14 @@ static struct snd_kcontrol_new prodigy_hd2_controls[] __devinitdata = {
.info = ak4396_dac_vol_info,
.get = ak4396_dac_vol_get,
.put = ak4396_dac_vol_put,
- .tlv = { .p = db_scale_wm_dac },
+ .tlv = { .p = ak4396_db_scale },
},
};
/* --------------- */
-/*
- * Logarithmic volume values for WM87*6
- * Computed as 20 * Log10(255 / x)
- */
-static const unsigned char wm_vol[256] = {
- 127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23,
- 23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17,
- 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13,
- 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11,
- 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0
-};
-
-#define WM_VOL_MAX (sizeof(wm_vol) - 1)
+#define WM_VOL_MAX 255
#define WM_VOL_MUTE 0x8000
@@ -409,7 +375,7 @@ static int wm_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
struct prodigy_hifi_spec *spec = ice->spec;
int i, idx, change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
if (ucontrol->value.integer.value[i] != spec->vol[2 + i]) {
idx = WM_DAC_ATTEN_L + i;
@@ -419,7 +385,6 @@ static int wm_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
change = 1;
}
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -461,7 +426,7 @@ static int wm8766_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
voices = kcontrol->private_value >> 8;
ofs = kcontrol->private_value & 0xff;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < voices; i++) {
if (ucontrol->value.integer.value[i] != spec->vol[ofs + i]) {
idx = WM8766_LDA1 + ofs + i;
@@ -472,7 +437,6 @@ static int wm8766_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val
change = 1;
}
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -507,7 +471,7 @@ static int wm_master_vol_put(struct snd_kcontrol *kcontrol,
struct prodigy_hifi_spec *spec = ice->spec;
int ch, change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (ch = 0; ch < 2; ch++) {
if (ucontrol->value.integer.value[ch] != spec->master[ch]) {
spec->master[ch] = ucontrol->value.integer.value[ch];
@@ -527,7 +491,6 @@ static int wm_master_vol_put(struct snd_kcontrol *kcontrol,
change = 1;
}
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -537,7 +500,7 @@ static int wm_master_vol_put(struct snd_kcontrol *kcontrol,
static int wm_adc_mux_enum_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char* texts[32] = {
+ static const char * const texts[32] = {
"NULL", WM_AIN1, WM_AIN2, WM_AIN1 "+" WM_AIN2,
WM_AIN3, WM_AIN1 "+" WM_AIN3, WM_AIN2 "+" WM_AIN3,
WM_AIN1 "+" WM_AIN2 "+" WM_AIN3,
@@ -560,14 +523,7 @@ static int wm_adc_mux_enum_info(struct snd_kcontrol *kcontrol,
WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5
};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 32;
- if (uinfo->value.enumerated.item > 31)
- uinfo->value.enumerated.item = 31;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 32, texts);
}
static int wm_adc_mux_enum_get(struct snd_kcontrol *kcontrol,
@@ -575,9 +531,8 @@ static int wm_adc_mux_enum_get(struct snd_kcontrol *kcontrol,
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
- ucontrol->value.integer.value[0] = wm_get(ice, WM_ADC_MUX) & 0x1f;
- mutex_unlock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
+ ucontrol->value.enumerated.item[0] = wm_get(ice, WM_ADC_MUX) & 0x1f;
return 0;
}
@@ -588,14 +543,13 @@ static int wm_adc_mux_enum_put(struct snd_kcontrol *kcontrol,
unsigned short oval, nval;
int change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
oval = wm_get(ice, WM_ADC_MUX);
- nval = (oval & 0xe0) | ucontrol->value.integer.value[0];
+ nval = (oval & 0xe0) | ucontrol->value.enumerated.item[0];
if (nval != oval) {
wm_put(ice, WM_ADC_MUX, nval);
change = 1;
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -626,13 +580,12 @@ static int wm_adc_vol_get(struct snd_kcontrol *kcontrol,
unsigned short val;
int i;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
val = wm_get(ice, WM_ADC_ATTEN_L + i) & 0xff;
val = val > ADC_MIN ? (val - ADC_MIN) : 0;
ucontrol->value.integer.value[i] = val;
}
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -643,7 +596,7 @@ static int wm_adc_vol_put(struct snd_kcontrol *kcontrol,
unsigned short ovol, nvol;
int i, idx, change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (i = 0; i < 2; i++) {
nvol = ucontrol->value.integer.value[i];
nvol = nvol ? (nvol + ADC_MIN) : 0;
@@ -654,7 +607,6 @@ static int wm_adc_vol_put(struct snd_kcontrol *kcontrol,
change = 1;
}
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -669,10 +621,9 @@ static int wm_adc_mux_get(struct snd_kcontrol *kcontrol,
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
int bit = kcontrol->private_value;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] =
(wm_get(ice, WM_ADC_MUX) & (1 << bit)) ? 1 : 0;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -684,7 +635,7 @@ static int wm_adc_mux_put(struct snd_kcontrol *kcontrol,
unsigned short oval, nval;
int change;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
nval = oval = wm_get(ice, WM_ADC_MUX);
if (ucontrol->value.integer.value[0])
nval |= (1 << bit);
@@ -694,7 +645,6 @@ static int wm_adc_mux_put(struct snd_kcontrol *kcontrol,
if (change) {
wm_put(ice, WM_ADC_MUX, nval);
}
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -708,10 +658,9 @@ static int wm_bypass_get(struct snd_kcontrol *kcontrol,
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] =
(wm_get(ice, WM_OUT_MUX) & 0x04) ? 1 : 0;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -722,7 +671,7 @@ static int wm_bypass_put(struct snd_kcontrol *kcontrol,
unsigned short val, oval;
int change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
val = oval = wm_get(ice, WM_OUT_MUX);
if (ucontrol->value.integer.value[0])
val |= 0x04;
@@ -732,7 +681,6 @@ static int wm_bypass_put(struct snd_kcontrol *kcontrol,
wm_put(ice, WM_OUT_MUX, val);
change = 1;
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -746,10 +694,9 @@ static int wm_chswap_get(struct snd_kcontrol *kcontrol,
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
ucontrol->value.integer.value[0] =
(wm_get(ice, WM_DAC_CTRL1) & 0xf0) != 0x90;
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
@@ -760,7 +707,7 @@ static int wm_chswap_put(struct snd_kcontrol *kcontrol,
unsigned short val, oval;
int change = 0;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
oval = wm_get(ice, WM_DAC_CTRL1);
val = oval & 0x0f;
if (ucontrol->value.integer.value[0])
@@ -772,7 +719,6 @@ static int wm_chswap_put(struct snd_kcontrol *kcontrol,
wm_put_nocache(ice, WM_DAC_CTRL1, val);
change = 1;
}
- mutex_unlock(&ice->gpio_mutex);
return change;
}
@@ -781,7 +727,7 @@ static int wm_chswap_put(struct snd_kcontrol *kcontrol,
* mixers
*/
-static struct snd_kcontrol_new prodigy_hifi_controls[] __devinitdata = {
+static const struct snd_kcontrol_new prodigy_hifi_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -904,14 +850,14 @@ static void wm_proc_regs_write(struct snd_info_entry *entry,
struct snd_ice1712 *ice = entry->private_data;
char line[64];
unsigned int reg, val;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
while (!snd_info_get_line(buffer, line, sizeof(line))) {
if (sscanf(line, "%x %x", &reg, &val) != 2)
continue;
if (reg <= 0x17 && val <= 0xffff)
wm_put(ice, reg, val);
}
- mutex_unlock(&ice->gpio_mutex);
}
static void wm_proc_regs_read(struct snd_info_entry *entry,
@@ -920,25 +866,20 @@ static void wm_proc_regs_read(struct snd_info_entry *entry,
struct snd_ice1712 *ice = entry->private_data;
int reg, val;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
for (reg = 0; reg <= 0x17; reg++) {
val = wm_get(ice, reg);
snd_iprintf(buffer, "%02x = %04x\n", reg, val);
}
- mutex_unlock(&ice->gpio_mutex);
}
static void wm_proc_init(struct snd_ice1712 *ice)
{
- struct snd_info_entry *entry;
- if (!snd_card_proc_new(ice->card, "wm_codec", &entry)) {
- snd_info_set_text_ops(entry, ice, wm_proc_regs_read);
- entry->mode |= S_IWUSR;
- entry->c.text.write = wm_proc_regs_write;
- }
+ snd_card_rw_proc_new(ice->card, "wm_codec", ice, wm_proc_regs_read,
+ wm_proc_regs_write);
}
-static int __devinit prodigy_hifi_add_controls(struct snd_ice1712 *ice)
+static int prodigy_hifi_add_controls(struct snd_ice1712 *ice)
{
unsigned int i;
int err;
@@ -955,7 +896,7 @@ static int __devinit prodigy_hifi_add_controls(struct snd_ice1712 *ice)
return 0;
}
-static int __devinit prodigy_hd2_add_controls(struct snd_ice1712 *ice)
+static int prodigy_hd2_add_controls(struct snd_ice1712 *ice)
{
unsigned int i;
int err;
@@ -972,13 +913,32 @@ static int __devinit prodigy_hd2_add_controls(struct snd_ice1712 *ice)
return 0;
}
+static void wm8766_init(struct snd_ice1712 *ice)
+{
+ static const unsigned short wm8766_inits[] = {
+ WM8766_RESET, 0x0000,
+ WM8766_DAC_CTRL, 0x0120,
+ WM8766_INT_CTRL, 0x0022, /* I2S Normal Mode, 24 bit */
+ WM8766_DAC_CTRL2, 0x0001,
+ WM8766_DAC_CTRL3, 0x0080,
+ WM8766_LDA1, 0x0100,
+ WM8766_LDA2, 0x0100,
+ WM8766_LDA3, 0x0100,
+ WM8766_RDA1, 0x0100,
+ WM8766_RDA2, 0x0100,
+ WM8766_RDA3, 0x0100,
+ WM8766_MUTE1, 0x0000,
+ WM8766_MUTE2, 0x0000,
+ };
+ unsigned int i;
-/*
- * initialize the chip
- */
-static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
+ for (i = 0; i < ARRAY_SIZE(wm8766_inits); i += 2)
+ wm8766_spi_write(ice, wm8766_inits[i], wm8766_inits[i + 1]);
+}
+
+static void wm8776_init(struct snd_ice1712 *ice)
{
- static unsigned short wm_inits[] = {
+ static const unsigned short wm8776_inits[] = {
/* These come first to reduce init pop noise */
WM_ADC_MUX, 0x0003, /* ADC mute */
/* 0x00c0 replaced by 0x0003 */
@@ -989,7 +949,75 @@ static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
WM_POWERDOWN, 0x0008, /* All power-up except HP */
WM_RESET, 0x0000, /* reset */
};
- static unsigned short wm_inits2[] = {
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm8776_inits); i += 2)
+ wm_put(ice, wm8776_inits[i], wm8776_inits[i + 1]);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int prodigy_hifi_resume(struct snd_ice1712 *ice)
+{
+ static const unsigned short wm8776_reinit_registers[] = {
+ WM_MASTER_CTRL,
+ WM_DAC_INT,
+ WM_ADC_INT,
+ WM_OUT_MUX,
+ WM_HP_ATTEN_L,
+ WM_HP_ATTEN_R,
+ WM_PHASE_SWAP,
+ WM_DAC_CTRL2,
+ WM_ADC_ATTEN_L,
+ WM_ADC_ATTEN_R,
+ WM_ALC_CTRL1,
+ WM_ALC_CTRL2,
+ WM_ALC_CTRL3,
+ WM_NOISE_GATE,
+ WM_ADC_MUX,
+ /* no DAC attenuation here */
+ };
+ struct prodigy_hifi_spec *spec = ice->spec;
+ int i, ch;
+
+ guard(mutex)(&ice->gpio_mutex);
+
+ /* reinitialize WM8776 and re-apply old register values */
+ wm8776_init(ice);
+ schedule_timeout_uninterruptible(1);
+ for (i = 0; i < ARRAY_SIZE(wm8776_reinit_registers); i++)
+ wm_put(ice, wm8776_reinit_registers[i],
+ wm_get(ice, wm8776_reinit_registers[i]));
+
+ /* reinitialize WM8766 and re-apply volumes for all DACs */
+ wm8766_init(ice);
+ for (ch = 0; ch < 2; ch++) {
+ wm_set_vol(ice, WM_DAC_ATTEN_L + ch,
+ spec->vol[2 + ch], spec->master[ch]);
+
+ wm8766_set_vol(ice, WM8766_LDA1 + ch,
+ spec->vol[0 + ch], spec->master[ch]);
+
+ wm8766_set_vol(ice, WM8766_LDA2 + ch,
+ spec->vol[4 + ch], spec->master[ch]);
+
+ wm8766_set_vol(ice, WM8766_LDA3 + ch,
+ spec->vol[6 + ch], spec->master[ch]);
+ }
+
+ /* unmute WM8776 DAC */
+ wm_put(ice, WM_DAC_MUTE, 0x00);
+ wm_put(ice, WM_DAC_CTRL1, 0x90);
+
+ return 0;
+}
+#endif
+
+/*
+ * initialize the chip
+ */
+static int prodigy_hifi_init(struct snd_ice1712 *ice)
+{
+ static const unsigned short wm8776_defaults[] = {
WM_MASTER_CTRL, 0x0022, /* 256fs, slave mode */
WM_DAC_INT, 0x0022, /* I2S, normal polarity, 24bit */
WM_ADC_INT, 0x0022, /* I2S, normal polarity, 24bit */
@@ -1017,22 +1045,6 @@ static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
WM_DAC_MUTE, 0x0000, /* DAC unmute */
WM_ADC_MUX, 0x0003, /* ADC unmute, both CD/Line On */
};
- static unsigned short wm8766_inits[] = {
- WM8766_RESET, 0x0000,
- WM8766_DAC_CTRL, 0x0120,
- WM8766_INT_CTRL, 0x0022, /* I2S Normal Mode, 24 bit */
- WM8766_DAC_CTRL2, 0x0001,
- WM8766_DAC_CTRL3, 0x0080,
- WM8766_LDA1, 0x0100,
- WM8766_LDA2, 0x0100,
- WM8766_LDA3, 0x0100,
- WM8766_RDA1, 0x0100,
- WM8766_RDA2, 0x0100,
- WM8766_RDA3, 0x0100,
- WM8766_MUTE1, 0x0000,
- WM8766_MUTE2, 0x0000,
- };
-
struct prodigy_hifi_spec *spec;
unsigned int i;
@@ -1046,7 +1058,7 @@ static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
* don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten
*/
ice->gpio.saved[0] = 0;
- /* to remeber the register values */
+ /* to remember the register values */
ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
if (! ice->akm)
@@ -1059,16 +1071,17 @@ static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
ice->spec = spec;
/* initialize WM8776 codec */
- for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2)
- wm_put(ice, wm_inits[i], wm_inits[i+1]);
+ wm8776_init(ice);
schedule_timeout_uninterruptible(1);
- for (i = 0; i < ARRAY_SIZE(wm_inits2); i += 2)
- wm_put(ice, wm_inits2[i], wm_inits2[i+1]);
+ for (i = 0; i < ARRAY_SIZE(wm8776_defaults); i += 2)
+ wm_put(ice, wm8776_defaults[i], wm8776_defaults[i + 1]);
- /* initialize WM8766 codec */
- for (i = 0; i < ARRAY_SIZE(wm8766_inits); i += 2)
- wm8766_spi_write(ice, wm8766_inits[i], wm8766_inits[i+1]);
+ wm8766_init(ice);
+#ifdef CONFIG_PM_SLEEP
+ ice->pm_resume = &prodigy_hifi_resume;
+ ice->pm_suspend_enabled = 1;
+#endif
return 0;
}
@@ -1079,7 +1092,7 @@ static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
*/
static void ak4396_init(struct snd_ice1712 *ice)
{
- static unsigned short ak4396_inits[] = {
+ static const unsigned short ak4396_inits[] = {
AK4396_CTRL1, 0x87, /* I2S Normal Mode, 24 bit */
AK4396_CTRL2, 0x02,
AK4396_CTRL3, 0x00,
@@ -1099,22 +1112,22 @@ static void ak4396_init(struct snd_ice1712 *ice)
ak4396_write(ice, ak4396_inits[i], ak4396_inits[i+1]);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int prodigy_hd2_resume(struct snd_ice1712 *ice)
{
/* initialize ak4396 codec and restore previous mixer volumes */
struct prodigy_hifi_spec *spec = ice->spec;
int i;
- mutex_lock(&ice->gpio_mutex);
+
+ guard(mutex)(&ice->gpio_mutex);
ak4396_init(ice);
for (i = 0; i < 2; i++)
ak4396_write(ice, AK4396_LCH_ATT + i, spec->vol[i] & 0xff);
- mutex_unlock(&ice->gpio_mutex);
return 0;
}
#endif
-static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
+static int prodigy_hd2_init(struct snd_ice1712 *ice)
{
struct prodigy_hifi_spec *spec;
@@ -1128,7 +1141,7 @@ static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
* don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten
*/
ice->gpio.saved[0] = 0;
- /* to remeber the register values */
+ /* to remember the register values */
ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL);
if (! ice->akm)
@@ -1140,7 +1153,7 @@ static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
return -ENOMEM;
ice->spec = spec;
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
ice->pm_resume = &prodigy_hd2_resume;
ice->pm_suspend_enabled = 1;
#endif
@@ -1151,7 +1164,7 @@ static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
}
-static unsigned char prodigy71hifi_eeprom[] __devinitdata = {
+static const unsigned char prodigy71hifi_eeprom[] = {
0x4b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
0x80, /* ACLINK: I2S */
0xfc, /* I2S: vol, 96k, 24bit, 192k */
@@ -1167,7 +1180,7 @@ static unsigned char prodigy71hifi_eeprom[] __devinitdata = {
0x00, /* GPIO_STATE2 */
};
-static unsigned char prodigyhd2_eeprom[] __devinitdata = {
+static const unsigned char prodigyhd2_eeprom[] = {
0x4b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
0x80, /* ACLINK: I2S */
0xfc, /* I2S: vol, 96k, 24bit, 192k */
@@ -1183,7 +1196,7 @@ static unsigned char prodigyhd2_eeprom[] __devinitdata = {
0x00, /* GPIO_STATE2 */
};
-static unsigned char fortissimo4_eeprom[] __devinitdata = {
+static const unsigned char fortissimo4_eeprom[] = {
0x43, /* SYSCONF: clock 512, ADC, 4DACs */
0x80, /* ACLINK: I2S */
0xfc, /* I2S: vol, 96k, 24bit, 192k */
@@ -1200,7 +1213,7 @@ static unsigned char fortissimo4_eeprom[] __devinitdata = {
};
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_prodigy_hifi_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_prodigy_hifi_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_PRODIGY_HIFI,
.name = "Audiotrak Prodigy 7.1 HiFi",
diff --git a/sound/pci/ice1712/prodigy_hifi.h b/sound/pci/ice1712/prodigy_hifi.h
index a4415d455d9e..f0e88396de79 100644
--- a/sound/pci/ice1712/prodigy_hifi.h
+++ b/sound/pci/ice1712/prodigy_hifi.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_PRODIGY_HIFI_H
#define __SOUND_PRODIGY_HIFI_H
@@ -7,21 +8,6 @@
* Lowlevel functions for Audiotrak Prodigy Hifi
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define PRODIGY_HIFI_DEVICE_DESC "{Audiotrak,Prodigy 7.1 HIFI},"\
diff --git a/sound/pci/ice1712/psc724.c b/sound/pci/ice1712/psc724.c
new file mode 100644
index 000000000000..0818e42c94ca
--- /dev/null
+++ b/sound/pci/ice1712/psc724.c
@@ -0,0 +1,447 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ALSA driver for ICEnsemble VT1724 (Envy24HT)
+ *
+ * Lowlevel functions for Philips PSC724 Ultimate Edge
+ *
+ * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+#include "psc724.h"
+#include "wm8766.h"
+#include "wm8776.h"
+
+struct psc724_spec {
+ struct snd_wm8766 wm8766;
+ struct snd_wm8776 wm8776;
+ bool mute_all, jack_detect;
+ struct snd_ice1712 *ice;
+ struct delayed_work hp_work;
+ bool hp_connected;
+};
+
+/****************************************************************************/
+/* PHILIPS PSC724 ULTIMATE EDGE */
+/****************************************************************************/
+/*
+ * VT1722 (Envy24GT) - 6 outputs, 4 inputs (only 2 used), 24-bit/96kHz
+ *
+ * system configuration ICE_EEP2_SYSCONF=0x42
+ * XIN1 49.152MHz
+ * no MPU401
+ * one stereo ADC, no S/PDIF receiver
+ * three stereo DACs (FRONT, REAR, CENTER+LFE)
+ *
+ * AC-Link configuration ICE_EEP2_ACLINK=0x80
+ * use I2S, not AC97
+ *
+ * I2S converters feature ICE_EEP2_I2S=0x30
+ * I2S codec has no volume/mute control feature (bug!)
+ * I2S codec does not support 96KHz or 192KHz (bug!)
+ * I2S codec 24bits
+ *
+ * S/PDIF configuration ICE_EEP2_SPDIF=0xc1
+ * Enable integrated S/PDIF transmitter
+ * internal S/PDIF out implemented
+ * No S/PDIF input
+ * External S/PDIF out implemented
+ *
+ *
+ * ** connected chips **
+ *
+ * WM8776
+ * 2-channel DAC used for main output and stereo ADC (with 10-channel MUX)
+ * AIN1: LINE IN, AIN2: CD/VIDEO, AIN3: AUX, AIN4: Front MIC, AIN5: Rear MIC
+ * Controlled by I2C using VT1722 I2C interface:
+ * MODE (pin16) -- GND
+ * CE (pin17) -- GND I2C mode (address=0x34)
+ * DI (pin18) -- SDA (VT1722 pin70)
+ * CL (pin19) -- SCLK (VT1722 pin71)
+ *
+ * WM8766
+ * 6-channel DAC used for rear & center/LFE outputs (only 4 channels used)
+ * Controlled by SPI using VT1722 GPIO pins:
+ * MODE (pin 1) -- GPIO19 (VT1722 pin99)
+ * ML/I2S (pin11) -- GPIO18 (VT1722 pin98)
+ * MC/IWL (pin12) -- GPIO17 (VT1722 pin97)
+ * MD/DM (pin13) -- GPIO16 (VT1722 pin96)
+ * MUTE (pin14) -- GPIO20 (VT1722 pin101)
+ *
+ * GPIO14 is used as input for headphone jack detection (1 = connected)
+ * GPIO22 is used as MUTE ALL output, grounding all 6 channels
+ *
+ * ** output pins and device names **
+ *
+ * 5.1ch name -- output connector color -- device (-D option)
+ *
+ * FRONT 2ch -- green -- plughw:0,0
+ * CENTER(Lch) SUBWOOFER(Rch) -- orange -- plughw:0,2,0
+ * REAR 2ch -- black -- plughw:0,2,1
+ */
+
+/* codec access low-level functions */
+
+#define GPIO_HP_JACK (1 << 14)
+#define GPIO_MUTE_SUR (1 << 20)
+#define GPIO_MUTE_ALL (1 << 22)
+
+#define JACK_INTERVAL 1000
+
+#define PSC724_SPI_DELAY 1
+
+#define PSC724_SPI_DATA (1 << 16)
+#define PSC724_SPI_CLK (1 << 17)
+#define PSC724_SPI_LOAD (1 << 18)
+#define PSC724_SPI_MASK (PSC724_SPI_DATA | PSC724_SPI_CLK | PSC724_SPI_LOAD)
+
+static void psc724_wm8766_write(struct snd_wm8766 *wm, u16 addr, u16 data)
+{
+ struct psc724_spec *spec = container_of(wm, struct psc724_spec, wm8766);
+ struct snd_ice1712 *ice = spec->ice;
+ u32 st, bits;
+ int i;
+
+ snd_ice1712_save_gpio_status(ice);
+
+ st = ((addr & 0x7f) << 9) | (data & 0x1ff);
+ snd_ice1712_gpio_set_dir(ice, ice->gpio.direction | PSC724_SPI_MASK);
+ snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask & ~PSC724_SPI_MASK);
+ bits = snd_ice1712_gpio_read(ice) & ~PSC724_SPI_MASK;
+ snd_ice1712_gpio_write(ice, bits);
+
+ for (i = 0; i < 16; i++) {
+ udelay(PSC724_SPI_DELAY);
+ bits &= ~PSC724_SPI_CLK;
+ /* MSB first */
+ st <<= 1;
+ if (st & 0x10000)
+ bits |= PSC724_SPI_DATA;
+ else
+ bits &= ~PSC724_SPI_DATA;
+ snd_ice1712_gpio_write(ice, bits);
+ /* CLOCK high */
+ udelay(PSC724_SPI_DELAY);
+ bits |= PSC724_SPI_CLK;
+ snd_ice1712_gpio_write(ice, bits);
+ }
+ /* LOAD high */
+ udelay(PSC724_SPI_DELAY);
+ bits |= PSC724_SPI_LOAD;
+ snd_ice1712_gpio_write(ice, bits);
+ /* LOAD low, DATA and CLOCK high */
+ udelay(PSC724_SPI_DELAY);
+ bits |= (PSC724_SPI_DATA | PSC724_SPI_CLK);
+ snd_ice1712_gpio_write(ice, bits);
+
+ snd_ice1712_restore_gpio_status(ice);
+}
+
+static void psc724_wm8776_write(struct snd_wm8776 *wm, u8 addr, u8 data)
+{
+ struct psc724_spec *spec = container_of(wm, struct psc724_spec, wm8776);
+
+ snd_vt1724_write_i2c(spec->ice, 0x34, addr, data);
+}
+
+/* mute all */
+
+static void psc724_set_master_switch(struct snd_ice1712 *ice, bool on)
+{
+ unsigned int bits = snd_ice1712_gpio_read(ice);
+ struct psc724_spec *spec = ice->spec;
+
+ spec->mute_all = !on;
+ if (on)
+ bits &= ~(GPIO_MUTE_ALL | GPIO_MUTE_SUR);
+ else
+ bits |= GPIO_MUTE_ALL | GPIO_MUTE_SUR;
+ snd_ice1712_gpio_write(ice, bits);
+}
+
+static bool psc724_get_master_switch(struct snd_ice1712 *ice)
+{
+ struct psc724_spec *spec = ice->spec;
+
+ return !spec->mute_all;
+}
+
+/* jack detection */
+
+static void psc724_set_jack_state(struct snd_ice1712 *ice, bool hp_connected)
+{
+ struct psc724_spec *spec = ice->spec;
+ struct snd_kcontrol *kctl;
+ u16 power = spec->wm8776.regs[WM8776_REG_PWRDOWN] & ~WM8776_PWR_HPPD;
+
+ psc724_set_master_switch(ice, !hp_connected);
+ if (!hp_connected)
+ power |= WM8776_PWR_HPPD;
+ snd_wm8776_set_power(&spec->wm8776, power);
+ spec->hp_connected = hp_connected;
+ /* notify about master speaker mute change */
+ kctl = snd_ctl_find_id_mixer(ice->card,
+ "Master Speakers Playback Switch");
+ if (kctl)
+ snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+ /* and headphone mute change */
+ kctl = snd_ctl_find_id_mixer(ice->card,
+ spec->wm8776.ctl[WM8776_CTL_HP_SW].name);
+ if (kctl)
+ snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+}
+
+static void psc724_update_hp_jack_state(struct work_struct *work)
+{
+ struct psc724_spec *spec = container_of(work, struct psc724_spec,
+ hp_work.work);
+ struct snd_ice1712 *ice = spec->ice;
+ bool hp_connected = snd_ice1712_gpio_read(ice) & GPIO_HP_JACK;
+
+ schedule_delayed_work(&spec->hp_work, msecs_to_jiffies(JACK_INTERVAL));
+ if (hp_connected == spec->hp_connected)
+ return;
+ psc724_set_jack_state(ice, hp_connected);
+}
+
+static void psc724_set_jack_detection(struct snd_ice1712 *ice, bool on)
+{
+ struct psc724_spec *spec = ice->spec;
+
+ if (spec->jack_detect == on)
+ return;
+
+ spec->jack_detect = on;
+ if (on) {
+ bool hp_connected = snd_ice1712_gpio_read(ice) & GPIO_HP_JACK;
+ psc724_set_jack_state(ice, hp_connected);
+ schedule_delayed_work(&spec->hp_work,
+ msecs_to_jiffies(JACK_INTERVAL));
+ } else
+ cancel_delayed_work_sync(&spec->hp_work);
+}
+
+static bool psc724_get_jack_detection(struct snd_ice1712 *ice)
+{
+ struct psc724_spec *spec = ice->spec;
+
+ return spec->jack_detect;
+}
+
+/* mixer controls */
+
+struct psc724_control {
+ const char *name;
+ void (*set)(struct snd_ice1712 *ice, bool on);
+ bool (*get)(struct snd_ice1712 *ice);
+};
+
+static const struct psc724_control psc724_cont[] = {
+ {
+ .name = "Master Speakers Playback Switch",
+ .set = psc724_set_master_switch,
+ .get = psc724_get_master_switch,
+ },
+ {
+ .name = "Headphone Jack Detection Playback Switch",
+ .set = psc724_set_jack_detection,
+ .get = psc724_get_jack_detection,
+ },
+};
+
+static int psc724_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+
+ ucontrol->value.integer.value[0] = psc724_cont[n].get(ice);
+
+ return 0;
+}
+
+static int psc724_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+
+ psc724_cont[n].set(ice, ucontrol->value.integer.value[0]);
+
+ return 0;
+}
+
+static const char *front_volume = "Front Playback Volume";
+static const char *front_switch = "Front Playback Switch";
+static const char *front_zc = "Front Zero Cross Detect Playback Switch";
+static const char *front_izd = "Front Infinite Zero Detect Playback Switch";
+static const char *front_phase = "Front Phase Invert Playback Switch";
+static const char *front_deemph = "Front Deemphasis Playback Switch";
+static const char *ain1_switch = "Line Capture Switch";
+static const char *ain2_switch = "CD Capture Switch";
+static const char *ain3_switch = "AUX Capture Switch";
+static const char *ain4_switch = "Front Mic Capture Switch";
+static const char *ain5_switch = "Rear Mic Capture Switch";
+static const char *rear_volume = "Surround Playback Volume";
+static const char *clfe_volume = "CLFE Playback Volume";
+static const char *rear_switch = "Surround Playback Switch";
+static const char *clfe_switch = "CLFE Playback Switch";
+static const char *rear_phase = "Surround Phase Invert Playback Switch";
+static const char *clfe_phase = "CLFE Phase Invert Playback Switch";
+static const char *rear_deemph = "Surround Deemphasis Playback Switch";
+static const char *clfe_deemph = "CLFE Deemphasis Playback Switch";
+static const char *rear_clfe_izd = "Rear Infinite Zero Detect Playback Switch";
+static const char *rear_clfe_zc = "Rear Zero Cross Detect Playback Switch";
+
+static int psc724_add_controls(struct snd_ice1712 *ice)
+{
+ struct snd_kcontrol_new cont;
+ struct snd_kcontrol *ctl;
+ int err, i;
+ struct psc724_spec *spec = ice->spec;
+
+ spec->wm8776.ctl[WM8776_CTL_DAC_VOL].name = front_volume;
+ spec->wm8776.ctl[WM8776_CTL_DAC_SW].name = front_switch;
+ spec->wm8776.ctl[WM8776_CTL_DAC_ZC_SW].name = front_zc;
+ spec->wm8776.ctl[WM8776_CTL_AUX_SW].name = NULL;
+ spec->wm8776.ctl[WM8776_CTL_DAC_IZD_SW].name = front_izd;
+ spec->wm8776.ctl[WM8776_CTL_PHASE_SW].name = front_phase;
+ spec->wm8776.ctl[WM8776_CTL_DEEMPH_SW].name = front_deemph;
+ spec->wm8776.ctl[WM8776_CTL_INPUT1_SW].name = ain1_switch;
+ spec->wm8776.ctl[WM8776_CTL_INPUT2_SW].name = ain2_switch;
+ spec->wm8776.ctl[WM8776_CTL_INPUT3_SW].name = ain3_switch;
+ spec->wm8776.ctl[WM8776_CTL_INPUT4_SW].name = ain4_switch;
+ spec->wm8776.ctl[WM8776_CTL_INPUT5_SW].name = ain5_switch;
+ snd_wm8776_build_controls(&spec->wm8776);
+ spec->wm8766.ctl[WM8766_CTL_CH1_VOL].name = rear_volume;
+ spec->wm8766.ctl[WM8766_CTL_CH2_VOL].name = clfe_volume;
+ spec->wm8766.ctl[WM8766_CTL_CH3_VOL].name = NULL;
+ spec->wm8766.ctl[WM8766_CTL_CH1_SW].name = rear_switch;
+ spec->wm8766.ctl[WM8766_CTL_CH2_SW].name = clfe_switch;
+ spec->wm8766.ctl[WM8766_CTL_CH3_SW].name = NULL;
+ spec->wm8766.ctl[WM8766_CTL_PHASE1_SW].name = rear_phase;
+ spec->wm8766.ctl[WM8766_CTL_PHASE2_SW].name = clfe_phase;
+ spec->wm8766.ctl[WM8766_CTL_PHASE3_SW].name = NULL;
+ spec->wm8766.ctl[WM8766_CTL_DEEMPH1_SW].name = rear_deemph;
+ spec->wm8766.ctl[WM8766_CTL_DEEMPH2_SW].name = clfe_deemph;
+ spec->wm8766.ctl[WM8766_CTL_DEEMPH3_SW].name = NULL;
+ spec->wm8766.ctl[WM8766_CTL_IZD_SW].name = rear_clfe_izd;
+ spec->wm8766.ctl[WM8766_CTL_ZC_SW].name = rear_clfe_zc;
+ snd_wm8766_build_controls(&spec->wm8766);
+
+ memset(&cont, 0, sizeof(cont));
+ cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ for (i = 0; i < ARRAY_SIZE(psc724_cont); i++) {
+ cont.private_value = i;
+ cont.name = psc724_cont[i].name;
+ cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ cont.info = snd_ctl_boolean_mono_info;
+ cont.get = psc724_ctl_get;
+ cont.put = psc724_ctl_put;
+ ctl = snd_ctl_new1(&cont, ice);
+ if (!ctl)
+ return -ENOMEM;
+ err = snd_ctl_add(ice->card, ctl);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void psc724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate)
+{
+ struct psc724_spec *spec = ice->spec;
+ /* restore codec volume settings after rate change (PMCLK stop) */
+ snd_wm8776_volume_restore(&spec->wm8776);
+ snd_wm8766_volume_restore(&spec->wm8766);
+}
+
+/* power management */
+
+#ifdef CONFIG_PM_SLEEP
+static int psc724_resume(struct snd_ice1712 *ice)
+{
+ struct psc724_spec *spec = ice->spec;
+
+ snd_wm8776_resume(&spec->wm8776);
+ snd_wm8766_resume(&spec->wm8766);
+
+ return 0;
+}
+#endif
+
+/* init */
+
+static int psc724_init(struct snd_ice1712 *ice)
+{
+ struct psc724_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ ice->spec = spec;
+ spec->ice = ice;
+
+ ice->num_total_dacs = 6;
+ ice->num_total_adcs = 2;
+ spec->wm8776.ops.write = psc724_wm8776_write;
+ spec->wm8776.card = ice->card;
+ snd_wm8776_init(&spec->wm8776);
+ spec->wm8766.ops.write = psc724_wm8766_write;
+ spec->wm8766.card = ice->card;
+#ifdef CONFIG_PM_SLEEP
+ ice->pm_resume = psc724_resume;
+ ice->pm_suspend_enabled = 1;
+#endif
+ snd_wm8766_init(&spec->wm8766);
+ snd_wm8766_set_if(&spec->wm8766,
+ WM8766_IF_FMT_I2S | WM8766_IF_IWL_24BIT);
+ ice->gpio.set_pro_rate = psc724_set_pro_rate;
+ INIT_DELAYED_WORK(&spec->hp_work, psc724_update_hp_jack_state);
+ psc724_set_jack_detection(ice, true);
+ return 0;
+}
+
+static void psc724_exit(struct snd_ice1712 *ice)
+{
+ struct psc724_spec *spec = ice->spec;
+
+ cancel_delayed_work_sync(&spec->hp_work);
+}
+
+/* PSC724 has buggy EEPROM (no 96&192kHz, all FFh GPIOs), so override it here */
+static const unsigned char psc724_eeprom[] = {
+ [ICE_EEP2_SYSCONF] = 0x42, /* 49.152MHz, 1 ADC, 3 DACs */
+ [ICE_EEP2_ACLINK] = 0x80, /* I2S */
+ [ICE_EEP2_I2S] = 0xf0, /* I2S volume, 96kHz, 24bit */
+ [ICE_EEP2_SPDIF] = 0xc1, /* spdif out-en, out-int, no input */
+ /* GPIO outputs */
+ [ICE_EEP2_GPIO_DIR2] = 0x5f, /* MUTE_ALL,WM8766 MUTE/MODE/ML/MC/MD */
+ /* GPIO write enable */
+ [ICE_EEP2_GPIO_MASK] = 0xff, /* read-only */
+ [ICE_EEP2_GPIO_MASK1] = 0xff, /* read-only */
+ [ICE_EEP2_GPIO_MASK2] = 0xa0, /* MUTE_ALL,WM8766 MUTE/MODE/ML/MC/MD */
+ /* GPIO initial state */
+ [ICE_EEP2_GPIO_STATE2] = 0x20, /* unmuted, all WM8766 pins low */
+};
+
+struct snd_ice1712_card_info snd_vt1724_psc724_cards[] = {
+ {
+ .subvendor = VT1724_SUBDEVICE_PSC724,
+ .name = "Philips PSC724 Ultimate Edge",
+ .model = "psc724",
+ .chip_init = psc724_init,
+ .chip_exit = psc724_exit,
+ .build_controls = psc724_add_controls,
+ .eeprom_size = sizeof(psc724_eeprom),
+ .eeprom_data = psc724_eeprom,
+ },
+ {} /*terminator*/
+};
diff --git a/sound/pci/ice1712/psc724.h b/sound/pci/ice1712/psc724.h
new file mode 100644
index 000000000000..e6ce335ae87e
--- /dev/null
+++ b/sound/pci/ice1712/psc724.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __SOUND_PSC724_H
+#define __SOUND_PSC724_H
+
+/* ID */
+#define PSC724_DEVICE_DESC \
+ "{Philips,PSC724 Ultimate Edge},"
+
+#define VT1724_SUBDEVICE_PSC724 0xab170619
+
+/* entry struct */
+extern struct snd_ice1712_card_info snd_vt1724_psc724_cards[];
+
+#endif /* __SOUND_PSC724_H */
diff --git a/sound/pci/ice1712/quartet.c b/sound/pci/ice1712/quartet.c
index 1948632787e6..099601edf1d0 100644
--- a/sound/pci/ice1712/quartet.c
+++ b/sound/pci/ice1712/quartet.c
@@ -1,32 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
* Lowlevel functions for Infrasonic Quartet
*
* Copyright (c) 2009 Pavel Hofman <pavel.hofman@ivitera.com>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <sound/core.h>
#include <sound/tlv.h>
#include <sound/info.h>
@@ -47,7 +32,7 @@ struct qtet_kcontrol_private {
unsigned int bit;
void (*set_register)(struct snd_ice1712 *ice, unsigned int val);
unsigned int (*get_register)(struct snd_ice1712 *ice);
- unsigned char *texts[2];
+ const char * const texts[2];
};
enum {
@@ -63,7 +48,7 @@ enum {
OUT34_MON12,
};
-static char *ext_clock_names[3] = {"IEC958 In", "Word Clock 1xFS",
+static const char * const ext_clock_names[3] = {"IEC958 In", "Word Clock 1xFS",
"Word Clock 256xFS"};
/* chip address on I2C bus */
@@ -232,17 +217,17 @@ static char *get_binary(char *buffer, int value)
/*
* Initial setup of the conversion array GPIO <-> rate
*/
-static unsigned int qtet_rates[] = {
+static const unsigned int qtet_rates[] = {
44100, 48000, 88200,
96000, 176400, 192000,
};
-static unsigned int cks_vals[] = {
+static const unsigned int cks_vals[] = {
CPLD_CKS_44100HZ, CPLD_CKS_48000HZ, CPLD_CKS_88200HZ,
CPLD_CKS_96000HZ, CPLD_CKS_176400HZ, CPLD_CKS_192000HZ,
};
-static struct snd_pcm_hw_constraint_list qtet_rates_info = {
+static const struct snd_pcm_hw_constraint_list qtet_rates_info = {
.count = ARRAY_SIZE(qtet_rates),
.list = qtet_rates,
.mask = 0,
@@ -279,7 +264,7 @@ static void qtet_akm_write(struct snd_akm4xxx *ak, int chip,
if (snd_BUG_ON(chip < 0 || chip >= 4))
return;
- /*printk(KERN_DEBUG "Writing to AK4620: chip=%d, addr=0x%x,
+ /*dev_dbg(ice->card->dev, "Writing to AK4620: chip=%d, addr=0x%x,
data=0x%x\n", chip, addr, data);*/
orig_dir = ice->gpio.get_dir(ice);
ice->gpio.set_dir(ice, orig_dir | GPIO_SPI_ALL);
@@ -387,7 +372,7 @@ static const struct snd_akm4xxx_adc_channel qtet_adc[] = {
AK_CONTROL(PCM_34_CAPTURE_VOLUME, 2),
};
-static struct snd_akm4xxx akm_qtet_dac __devinitdata = {
+static const struct snd_akm4xxx akm_qtet_dac = {
.type = SND_AK4620,
.num_dacs = 4, /* DAC1 - Output 12
*/
@@ -411,7 +396,7 @@ static void reg_write(struct snd_ice1712 *ice, unsigned int reg,
{
unsigned int tmp;
- mutex_lock(&ice->gpio_mutex);
+ guard(mutex)(&ice->gpio_mutex);
/* set direction of used GPIOs*/
/* all outputs */
tmp = 0x00ffff;
@@ -444,7 +429,6 @@ static void reg_write(struct snd_ice1712 *ice, unsigned int reg,
ice->gpio.set_mask(ice, 0xffffff);
/* outputs only 8-15 */
ice->gpio.set_dir(ice, 0x00ff00);
- mutex_unlock(&ice->gpio_mutex);
}
static unsigned int get_scr(struct snd_ice1712 *ice)
@@ -485,7 +469,7 @@ static void set_cpld(struct snd_ice1712 *ice, unsigned int val)
reg_write(ice, GPIO_CPLD_CSN, val);
spec->cpld = val;
}
-#ifdef CONFIG_PROC_FS
+
static void proc_regs_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
@@ -502,13 +486,8 @@ static void proc_regs_read(struct snd_info_entry *entry,
static void proc_init(struct snd_ice1712 *ice)
{
- struct snd_info_entry *entry;
- if (!snd_card_proc_new(ice->card, "quartet", &entry))
- snd_info_set_text_ops(entry, ice, proc_regs_read);
+ snd_card_ro_proc_new(ice->card, "quartet", ice, proc_regs_read);
}
-#else /* !CONFIG_PROC_FS */
-static void proc_init(struct snd_ice1712 *ice) {}
-#endif
static int qtet_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -551,18 +530,9 @@ static int qtet_mute_put(struct snd_kcontrol *kcontrol,
static int qtet_ain12_enum_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static char *texts[3] = {"Line In 1/2", "Mic", "Mic + Low-cut"};
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = ARRAY_SIZE(texts);
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
- return 0;
+ static const char * const texts[3] =
+ {"Line In 1/2", "Mic", "Mic + Low-cut"};
+ return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts);
}
static int qtet_ain12_sw_get(struct snd_kcontrol *kcontrol,
@@ -595,7 +565,7 @@ static int qtet_ain12_sw_put(struct snd_kcontrol *kcontrol,
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned int old, new, tmp, masked_old;
- old = new = get_scr(ice);
+ old = get_scr(ice);
masked_old = old & (SCR_AIN12_SEL1 | SCR_AIN12_SEL0);
tmp = ucontrol->value.integer.value[0];
if (tmp == 2)
@@ -686,7 +656,7 @@ static int qtet_php_put(struct snd_kcontrol *kcontrol,
.get_register = get_##xreg,\
.texts = {xtext1, xtext2} }
-static struct qtet_kcontrol_private qtet_privates[] = {
+static const struct qtet_kcontrol_private qtet_privates[] = {
PRIV_ENUM2(IN12_SEL, CPLD_IN12_SEL, cpld, "An In 1/2", "An In 3/4"),
PRIV_ENUM2(IN34_SEL, CPLD_IN34_SEL, cpld, "An In 3/4", "IEC958 In"),
PRIV_ENUM2(AIN34_SEL, SCR_AIN34_SEL, scr, "Line In 3/4", "Hi-Z"),
@@ -704,17 +674,8 @@ static int qtet_enum_info(struct snd_kcontrol *kcontrol,
{
struct qtet_kcontrol_private private =
qtet_privates[kcontrol->private_value];
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = ARRAY_SIZE(private.texts);
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- private.texts[uinfo->value.enumerated.item]);
-
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(private.texts),
+ private.texts);
}
static int qtet_sw_get(struct snd_kcontrol *kcontrol,
@@ -758,7 +719,7 @@ static int qtet_sw_put(struct snd_kcontrol *kcontrol,
.put = qtet_sw_put,\
.private_value = xpriv }
-static struct snd_kcontrol_new qtet_controls[] __devinitdata = {
+static const struct snd_kcontrol_new qtet_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
@@ -795,37 +756,16 @@ static struct snd_kcontrol_new qtet_controls[] __devinitdata = {
QTET_CONTROL("Output 3/4 to Monitor 1/2", sw, OUT34_MON12),
};
-static char *slave_vols[] __devinitdata = {
+static const char * const follower_vols[] = {
PCM_12_PLAYBACK_VOLUME,
PCM_34_PLAYBACK_VOLUME,
NULL
};
-static __devinitdata
+static
DECLARE_TLV_DB_SCALE(qtet_master_db_scale, -6350, 50, 1);
-static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card,
- const char *name)
-{
- struct snd_ctl_elem_id sid;
- memset(&sid, 0, sizeof(sid));
- /* FIXME: strcpy is bad. */
- strcpy(sid.name, name);
- sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- return snd_ctl_find_id(card, &sid);
-}
-
-static void __devinit add_slaves(struct snd_card *card,
- struct snd_kcontrol *master, char **list)
-{
- for (; *list; list++) {
- struct snd_kcontrol *slave = ctl_find(card, *list);
- if (slave)
- snd_ctl_add_slave(master, slave);
- }
-}
-
-static int __devinit qtet_add_controls(struct snd_ice1712 *ice)
+static int qtet_add_controls(struct snd_ice1712 *ice)
{
struct qtet_spec *spec = ice->spec;
int err, i;
@@ -845,16 +785,15 @@ static int __devinit qtet_add_controls(struct snd_ice1712 *ice)
qtet_master_db_scale);
if (!vmaster)
return -ENOMEM;
- add_slaves(ice->card, vmaster, slave_vols);
err = snd_ctl_add(ice->card, vmaster);
if (err < 0)
return err;
- /* only capture SPDIF over AK4113 */
- err = snd_ak4113_build(spec->ak4113,
- ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
+ err = snd_ctl_add_followers(ice->card, vmaster, follower_vols);
if (err < 0)
return err;
- return 0;
+ /* only capture SPDIF over AK4113 */
+ return snd_ak4113_build(spec->ak4113,
+ ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
}
static inline int qtet_is_spdif_master(struct snd_ice1712 *ice)
@@ -896,7 +835,7 @@ static void qtet_set_rate(struct snd_ice1712 *ice, unsigned int rate)
new = (get_cpld(ice) & ~CPLD_CKS_MASK) | get_cks_val(rate);
/* switch to internal clock, drop CPLD_SYNC_SEL */
new &= ~CPLD_SYNC_SEL;
- /* printk(KERN_DEBUG "QT - set_rate: old %x, new %x\n",
+ /* dev_dbg(ice->card->dev, "QT - set_rate: old %x, new %x\n",
get_cpld(ice), new); */
set_cpld(ice, new);
}
@@ -976,7 +915,7 @@ static void qtet_ak4113_change(struct ak4113 *ak4113, unsigned char c0,
c1) {
/* only for SPDIF master mode, rate was changed */
rate = snd_ak4113_external_rate(ak4113);
- /* printk(KERN_DEBUG "ak4113 - input rate changed to %d\n",
+ /* dev_dbg(ice->card->dev, "ak4113 - input rate changed to %d\n",
rate); */
qtet_akm_set_rate_val(ice->akm, rate);
}
@@ -1007,7 +946,7 @@ static void qtet_spdif_in_open(struct snd_ice1712 *ice,
/*
* initialize the chip
*/
-static int __devinit qtet_init(struct snd_ice1712 *ice)
+static int qtet_init(struct snd_ice1712 *ice)
{
static const unsigned char ak4113_init_vals[] = {
/* AK4113_REG_PWRDN */ AK4113_RST | AK4113_PWN |
@@ -1095,7 +1034,7 @@ static int __devinit qtet_init(struct snd_ice1712 *ice)
return 0;
}
-static unsigned char qtet_eeprom[] __devinitdata = {
+static const unsigned char qtet_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x28, /* clock 256(24MHz), mpu401, 1xADC,
1xDACs, SPDIF in */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
@@ -1116,7 +1055,7 @@ static unsigned char qtet_eeprom[] __devinitdata = {
};
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_qtet_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_qtet_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_QTET,
.name = "Infrasonic Quartet",
diff --git a/sound/pci/ice1712/quartet.h b/sound/pci/ice1712/quartet.h
index 80809b72439a..a1c2fe27185d 100644
--- a/sound/pci/ice1712/quartet.h
+++ b/sound/pci/ice1712/quartet.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SOUND_QTET_H
#define __SOUND_QTET_H
diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c
index b508bb360b97..bcf114152dfd 100644
--- a/sound/pci/ice1712/revo.c
+++ b/sound/pci/ice1712/revo.c
@@ -1,27 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble ICE1712 (Envy24)
*
* Lowlevel functions for M-Audio Audiophile 192, Revolution 7.1 and 5.1
*
* Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -36,6 +21,7 @@
struct revo51_spec {
struct snd_i2c_device *dev;
struct snd_pt2258 *pt2258;
+ struct ak4114 *ak4114;
};
static void revo_i2s_mclk_changed(struct snd_ice1712 *ice)
@@ -235,7 +221,7 @@ static const struct snd_akm4xxx_adc_channel revo51_adc[] = {
},
};
-static struct snd_akm4xxx akm_revo_front __devinitdata = {
+static const struct snd_akm4xxx akm_revo_front = {
.type = SND_AK4381,
.num_dacs = 2,
.ops = {
@@ -244,7 +230,7 @@ static struct snd_akm4xxx akm_revo_front __devinitdata = {
.dac_info = revo71_front,
};
-static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_revo_front_priv = {
.caddr = 1,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
@@ -256,7 +242,7 @@ static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_revo_surround __devinitdata = {
+static const struct snd_akm4xxx akm_revo_surround = {
.type = SND_AK4355,
.idx_offset = 1,
.num_dacs = 6,
@@ -266,7 +252,7 @@ static struct snd_akm4xxx akm_revo_surround __devinitdata = {
.dac_info = revo71_surround,
};
-static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_revo_surround_priv = {
.caddr = 3,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
@@ -278,7 +264,7 @@ static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_revo51 __devinitdata = {
+static const struct snd_akm4xxx akm_revo51 = {
.type = SND_AK4358,
.num_dacs = 8,
.ops = {
@@ -287,7 +273,7 @@ static struct snd_akm4xxx akm_revo51 __devinitdata = {
.dac_info = revo51_dac,
};
-static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_revo51_priv = {
.caddr = 2,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
@@ -299,13 +285,13 @@ static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = {
.mask_flags = 0,
};
-static struct snd_akm4xxx akm_revo51_adc __devinitdata = {
+static const struct snd_akm4xxx akm_revo51_adc = {
.type = SND_AK5365,
.num_adcs = 2,
.adc_info = revo51_adc,
};
-static struct snd_ak4xxx_private akm_revo51_adc_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_revo51_adc_priv = {
.caddr = 2,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
@@ -346,7 +332,7 @@ static const struct snd_akm4xxx_dac_channel ap192_dac[] = {
AK_DAC("PCM Playback Volume", 2)
};
-static struct snd_akm4xxx akm_ap192 __devinitdata = {
+static const struct snd_akm4xxx akm_ap192 = {
.type = SND_AK4358,
.num_dacs = 2,
.ops = {
@@ -355,14 +341,14 @@ static struct snd_akm4xxx akm_ap192 __devinitdata = {
.dac_info = ap192_dac,
};
-static struct snd_ak4xxx_private akm_ap192_priv __devinitdata = {
+static const struct snd_ak4xxx_private akm_ap192_priv = {
.caddr = 2,
.cif = 0,
.data_mask = VT1724_REVO_CDOUT,
.clk_mask = VT1724_REVO_CCLK,
- .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1,
- .cs_addr = VT1724_REVO_CS1,
- .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1,
+ .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS3,
+ .cs_addr = VT1724_REVO_CS3,
+ .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS3,
.add_flags = VT1724_REVO_CCLK, /* high at init */
.mask_flags = 0,
};
@@ -373,7 +359,7 @@ static struct snd_ak4xxx_private akm_ap192_priv __devinitdata = {
* CCLK (pin 34) -- GPIO1 pin 51 (shared with AK4358)
* CSN (pin 35) -- GPIO7 pin 59
*/
-#define AK4114_ADDR 0x02
+#define AK4114_ADDR 0x00
static void write_data(struct snd_ice1712 *ice, unsigned int gpio,
unsigned int data, int idx)
@@ -427,7 +413,7 @@ static unsigned int ap192_4wire_start(struct snd_ice1712 *ice)
tmp = snd_ice1712_gpio_read(ice);
tmp |= VT1724_REVO_CCLK; /* high at init */
tmp |= VT1724_REVO_CS0;
- tmp &= ~VT1724_REVO_CS1;
+ tmp &= ~VT1724_REVO_CS3;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
return tmp;
@@ -435,7 +421,7 @@ static unsigned int ap192_4wire_start(struct snd_ice1712 *ice)
static void ap192_4wire_finish(struct snd_ice1712 *ice, unsigned int tmp)
{
- tmp |= VT1724_REVO_CS1;
+ tmp |= VT1724_REVO_CS3;
tmp |= VT1724_REVO_CS0;
snd_ice1712_gpio_write(ice, tmp);
udelay(1);
@@ -468,35 +454,42 @@ static unsigned char ap192_ak4114_read(void *private_data, unsigned char addr)
return data;
}
-static int __devinit ap192_ak4114_init(struct snd_ice1712 *ice)
+static int ap192_ak4114_init(struct snd_ice1712 *ice)
{
static const unsigned char ak4114_init_vals[] = {
- AK4114_RST | AK4114_PWN | AK4114_OCKS0 | AK4114_OCKS1,
+ AK4114_RST | AK4114_PWN | AK4114_OCKS0,
AK4114_DIF_I24I2S,
AK4114_TX1E,
- AK4114_EFH_1024 | AK4114_DIT | AK4114_IPS(1),
+ AK4114_EFH_1024 | AK4114_DIT | AK4114_IPS(0),
0,
0
};
static const unsigned char ak4114_init_txcsb[] = {
0x41, 0x02, 0x2c, 0x00, 0x00
};
- struct ak4114 *ak;
int err;
+ struct revo51_spec *spec;
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ ice->spec = spec;
+
err = snd_ak4114_create(ice->card,
ap192_ak4114_read,
ap192_ak4114_write,
ak4114_init_vals, ak4114_init_txcsb,
- ice, &ak);
+ ice, &spec->ak4114);
+ if (err < 0)
+ return err;
/* AK4114 in Revo cannot detect external rate correctly.
* No reason to stop capture stream due to incorrect checks */
- ak->check_flags = AK4114_CHECK_NO_RATE;
+ spec->ak4114->check_flags = AK4114_CHECK_NO_RATE;
- return 0; /* error ignored; it's no fatal error */
+ return 0;
}
-static int __devinit revo_init(struct snd_ice1712 *ice)
+static int revo_init(struct snd_ice1712 *ice)
{
struct snd_akm4xxx *ak;
int err;
@@ -563,6 +556,9 @@ static int __devinit revo_init(struct snd_ice1712 *ice)
ice);
if (err < 0)
return err;
+ err = ap192_ak4114_init(ice);
+ if (err < 0)
+ return err;
/* unmute all codecs */
snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE,
@@ -574,9 +570,9 @@ static int __devinit revo_init(struct snd_ice1712 *ice)
}
-static int __devinit revo_add_controls(struct snd_ice1712 *ice)
+static int revo_add_controls(struct snd_ice1712 *ice)
{
- struct revo51_spec *spec;
+ struct revo51_spec *spec = ice->spec;
int err;
switch (ice->eeprom.subvendor) {
@@ -598,7 +594,9 @@ static int __devinit revo_add_controls(struct snd_ice1712 *ice)
err = snd_ice1712_akm4xxx_build_controls(ice);
if (err < 0)
return err;
- err = ap192_ak4114_init(ice);
+ /* only capture SPDIF over AK4114 */
+ err = snd_ak4114_build(spec->ak4114, NULL,
+ ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
if (err < 0)
return err;
break;
@@ -607,7 +605,7 @@ static int __devinit revo_add_controls(struct snd_ice1712 *ice)
}
/* entry point */
-struct snd_ice1712_card_info snd_vt1724_revo_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_revo_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_REVOLUTION71,
.name = "M Audio Revolution-7.1",
diff --git a/sound/pci/ice1712/revo.h b/sound/pci/ice1712/revo.h
index a3ba425911cc..573ae25067e0 100644
--- a/sound/pci/ice1712/revo.h
+++ b/sound/pci/ice1712/revo.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_REVO_H
#define __SOUND_REVO_H
@@ -7,21 +8,6 @@
* Lowlevel functions for M-Audio Revolution 7.1
*
* Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define REVO_DEVICE_DESC \
diff --git a/sound/pci/ice1712/se.c b/sound/pci/ice1712/se.c
index 69673b95869d..ffa9d8860a5a 100644
--- a/sound/pci/ice1712/se.c
+++ b/sound/pci/ice1712/se.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
@@ -5,24 +6,8 @@
*
* Copyright (c) 2007 Shin-ya Okada sh_okada(at)d4.dion.ne.jp
* (at) -> @
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -114,7 +99,7 @@ struct se_spec {
/* WM8740 interface */
/****************************************************************************/
-static void __devinit se200pci_WM8740_init(struct snd_ice1712 *ice)
+static void se200pci_WM8740_init(struct snd_ice1712 *ice)
{
/* nothing to do */
}
@@ -196,7 +181,7 @@ static void se200pci_WM8766_set_volume(struct snd_ice1712 *ice, int ch,
}
}
-static void __devinit se200pci_WM8766_init(struct snd_ice1712 *ice)
+static void se200pci_WM8766_init(struct snd_ice1712 *ice)
{
se200pci_WM8766_write(ice, 0x1f, 0x000); /* RESET ALL */
udelay(10);
@@ -253,14 +238,14 @@ static void se200pci_WM8776_set_input_volume(struct snd_ice1712 *ice,
se200pci_WM8776_write(ice, 0x0f, vol2 | 0x100);
}
-static const char *se200pci_sel[] = {
+static const char * const se200pci_sel[] = {
"LINE-IN", "CD-IN", "MIC-IN", "ALL-MIX", NULL
};
static void se200pci_WM8776_set_input_selector(struct snd_ice1712 *ice,
unsigned int sel)
{
- static unsigned char vals[] = {
+ static const unsigned char vals[] = {
/* LINE, CD, MIC, ALL, GND */
0x10, 0x04, 0x08, 0x1c, 0x03
};
@@ -278,7 +263,7 @@ static void se200pci_WM8776_set_afl(struct snd_ice1712 *ice, unsigned int afl)
se200pci_WM8776_write(ice, 0x16, 0x001);
}
-static const char *se200pci_agc[] = {
+static const char * const se200pci_agc[] = {
"Off", "LimiterMode", "ALCMode", NULL
};
@@ -300,10 +285,10 @@ static void se200pci_WM8776_set_agc(struct snd_ice1712 *ice, unsigned int agc)
}
}
-static void __devinit se200pci_WM8776_init(struct snd_ice1712 *ice)
+static void se200pci_WM8776_init(struct snd_ice1712 *ice)
{
int i;
- static unsigned short __devinitdata default_values[] = {
+ static const unsigned short default_values[] = {
0x100, 0x100, 0x100,
0x100, 0x100, 0x100,
0x000, 0x090, 0x000, 0x000,
@@ -352,7 +337,7 @@ static void se200pci_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate)
}
struct se200pci_control {
- char *name;
+ const char *name;
enum {
WM8766,
WM8776in,
@@ -363,7 +348,7 @@ struct se200pci_control {
} target;
enum { VOLUME1, VOLUME2, BOOLEAN, ENUM } type;
int ch;
- const char **member;
+ const char * const *member;
const char *comment;
};
@@ -421,7 +406,7 @@ static const struct se200pci_control se200pci_cont[] = {
static int se200pci_get_enum_count(int n)
{
- const char **member;
+ const char * const *member;
int c;
member = se200pci_cont[n].member;
@@ -453,14 +438,7 @@ static int se200pci_cont_enum_info(struct snd_kcontrol *kc,
c = se200pci_get_enum_count(n);
if (!c)
return -EINVAL;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = c;
- if (uinfo->value.enumerated.item >= c)
- uinfo->value.enumerated.item = c - 1;
- strcpy(uinfo->value.enumerated.name,
- se200pci_cont[n].member[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, c, se200pci_cont[n].member);
}
static int se200pci_cont_volume_get(struct snd_kcontrol *kc,
@@ -600,7 +578,7 @@ static int se200pci_cont_enum_put(struct snd_kcontrol *kc,
static const DECLARE_TLV_DB_SCALE(db_scale_gain1, -12750, 50, 1);
static const DECLARE_TLV_DB_SCALE(db_scale_gain2, -10350, 50, 1);
-static int __devinit se200pci_add_controls(struct snd_ice1712 *ice)
+static int se200pci_add_controls(struct snd_ice1712 *ice)
{
int i;
struct snd_kcontrol_new cont;
@@ -678,7 +656,7 @@ static int __devinit se200pci_add_controls(struct snd_ice1712 *ice)
/* probe/initialize/setup */
/****************************************************************************/
-static int __devinit se_init(struct snd_ice1712 *ice)
+static int se_init(struct snd_ice1712 *ice)
{
struct se_spec *spec;
@@ -706,7 +684,7 @@ static int __devinit se_init(struct snd_ice1712 *ice)
return -ENOENT;
}
-static int __devinit se_add_controls(struct snd_ice1712 *ice)
+static int se_add_controls(struct snd_ice1712 *ice)
{
int err;
@@ -723,7 +701,7 @@ static int __devinit se_add_controls(struct snd_ice1712 *ice)
/* entry point */
/****************************************************************************/
-static unsigned char se200pci_eeprom[] __devinitdata = {
+static const unsigned char se200pci_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x4b, /* 49.152Hz, spdif-in/ADC, 4DACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0x78, /* 96k-ok, 24bit, 192k-ok */
@@ -742,7 +720,7 @@ static unsigned char se200pci_eeprom[] __devinitdata = {
[ICE_EEP2_GPIO_STATE2] = 0x07, /* WM8766 ML/MC/MD */
};
-static unsigned char se90pci_eeprom[] __devinitdata = {
+static const unsigned char se90pci_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x4b, /* 49.152Hz, spdif-in/ADC, 4DACs */
[ICE_EEP2_ACLINK] = 0x80, /* I2S */
[ICE_EEP2_I2S] = 0x78, /* 96k-ok, 24bit, 192k-ok */
@@ -751,7 +729,7 @@ static unsigned char se90pci_eeprom[] __devinitdata = {
/* ALL GPIO bits are in input mode */
};
-struct snd_ice1712_card_info snd_vt1724_se_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_se_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_SE200PCI,
.name = "ONKYO SE200PCI",
diff --git a/sound/pci/ice1712/se.h b/sound/pci/ice1712/se.h
index 0b0a9dabdcfb..61348ecef1e0 100644
--- a/sound/pci/ice1712/se.h
+++ b/sound/pci/ice1712/se.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SOUND_SE_H
#define __SOUND_SE_H
diff --git a/sound/pci/ice1712/stac946x.h b/sound/pci/ice1712/stac946x.h
index 5b390952d0e4..58f9f17a258a 100644
--- a/sound/pci/ice1712/stac946x.h
+++ b/sound/pci/ice1712/stac946x.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SOUND_STAC946X_H
#define __SOUND_STAC946X_H
diff --git a/sound/pci/ice1712/vt1720_mobo.c b/sound/pci/ice1712/vt1720_mobo.c
index 4c551e147c08..47470b07197c 100644
--- a/sound/pci/ice1712/vt1720_mobo.c
+++ b/sound/pci/ice1712/vt1720_mobo.c
@@ -1,27 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for VT1720/VT1724 (Envy24PT/Envy24HT)
*
* Lowlevel functions for VT1720-based motherboards
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
@@ -32,7 +17,7 @@
#include "vt1720_mobo.h"
-static int __devinit k8x800_init(struct snd_ice1712 *ice)
+static int k8x800_init(struct snd_ice1712 *ice)
{
ice->vt1720 = 1;
@@ -46,7 +31,7 @@ static int __devinit k8x800_init(struct snd_ice1712 *ice)
return 0;
}
-static int __devinit k8x800_add_controls(struct snd_ice1712 *ice)
+static int k8x800_add_controls(struct snd_ice1712 *ice)
{
/* FIXME: needs some quirks for VT1616? */
return 0;
@@ -54,7 +39,7 @@ static int __devinit k8x800_add_controls(struct snd_ice1712 *ice)
/* EEPROM image */
-static unsigned char k8x800_eeprom[] __devinitdata = {
+static const unsigned char k8x800_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x01, /* clock 256, 1ADC, 2DACs */
[ICE_EEP2_ACLINK] = 0x02, /* ACLINK, packed */
[ICE_EEP2_I2S] = 0x00, /* - */
@@ -70,7 +55,7 @@ static unsigned char k8x800_eeprom[] __devinitdata = {
[ICE_EEP2_GPIO_STATE2] = 0x00, /* - */
};
-static unsigned char sn25p_eeprom[] __devinitdata = {
+static const unsigned char sn25p_eeprom[] = {
[ICE_EEP2_SYSCONF] = 0x01, /* clock 256, 1ADC, 2DACs */
[ICE_EEP2_ACLINK] = 0x02, /* ACLINK, packed */
[ICE_EEP2_I2S] = 0x00, /* - */
@@ -88,7 +73,7 @@ static unsigned char sn25p_eeprom[] __devinitdata = {
/* entry point */
-struct snd_ice1712_card_info snd_vt1720_mobo_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1720_mobo_cards[] = {
{
.subvendor = VT1720_SUBDEVICE_K8X800,
.name = "Albatron K8X800 Pro II",
diff --git a/sound/pci/ice1712/vt1720_mobo.h b/sound/pci/ice1712/vt1720_mobo.h
index 0b1b0ee1bea7..90c77d8270d0 100644
--- a/sound/pci/ice1712/vt1720_mobo.h
+++ b/sound/pci/ice1712/vt1720_mobo.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __SOUND_VT1720_MOBO_H
#define __SOUND_VT1720_MOBO_H
@@ -7,21 +8,6 @@
* Lowlevel functions for VT1720-based motherboards
*
* Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#define VT1720_MOBO_DEVICE_DESC "{Albatron,K8X800 Pro II},"\
diff --git a/sound/pci/ice1712/wm8766.c b/sound/pci/ice1712/wm8766.c
new file mode 100644
index 000000000000..fe3e243b3854
--- /dev/null
+++ b/sound/pci/ice1712/wm8766.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ALSA driver for ICEnsemble VT17xx
+ *
+ * Lowlevel functions for WM8766 codec
+ *
+ * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
+ */
+
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include "wm8766.h"
+
+/* low-level access */
+
+static void snd_wm8766_write(struct snd_wm8766 *wm, u16 addr, u16 data)
+{
+ if (addr < WM8766_REG_COUNT)
+ wm->regs[addr] = data;
+ wm->ops.write(wm, addr, data);
+}
+
+/* mixer controls */
+
+static const DECLARE_TLV_DB_SCALE(wm8766_tlv, -12750, 50, 1);
+
+static const struct snd_wm8766_ctl snd_wm8766_default_ctl[WM8766_CTL_COUNT] = {
+ [WM8766_CTL_CH1_VOL] = {
+ .name = "Channel 1 Playback Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8766_tlv,
+ .reg1 = WM8766_REG_DACL1,
+ .reg2 = WM8766_REG_DACR1,
+ .mask1 = WM8766_VOL_MASK,
+ .mask2 = WM8766_VOL_MASK,
+ .max = 0xff,
+ .flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE,
+ },
+ [WM8766_CTL_CH2_VOL] = {
+ .name = "Channel 2 Playback Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8766_tlv,
+ .reg1 = WM8766_REG_DACL2,
+ .reg2 = WM8766_REG_DACR2,
+ .mask1 = WM8766_VOL_MASK,
+ .mask2 = WM8766_VOL_MASK,
+ .max = 0xff,
+ .flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE,
+ },
+ [WM8766_CTL_CH3_VOL] = {
+ .name = "Channel 3 Playback Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8766_tlv,
+ .reg1 = WM8766_REG_DACL3,
+ .reg2 = WM8766_REG_DACR3,
+ .mask1 = WM8766_VOL_MASK,
+ .mask2 = WM8766_VOL_MASK,
+ .max = 0xff,
+ .flags = WM8766_FLAG_STEREO | WM8766_FLAG_VOL_UPDATE,
+ },
+ [WM8766_CTL_CH1_SW] = {
+ .name = "Channel 1 Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_DACCTRL2,
+ .mask1 = WM8766_DAC2_MUTE1,
+ .flags = WM8766_FLAG_INVERT,
+ },
+ [WM8766_CTL_CH2_SW] = {
+ .name = "Channel 2 Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_DACCTRL2,
+ .mask1 = WM8766_DAC2_MUTE2,
+ .flags = WM8766_FLAG_INVERT,
+ },
+ [WM8766_CTL_CH3_SW] = {
+ .name = "Channel 3 Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_DACCTRL2,
+ .mask1 = WM8766_DAC2_MUTE3,
+ .flags = WM8766_FLAG_INVERT,
+ },
+ [WM8766_CTL_PHASE1_SW] = {
+ .name = "Channel 1 Phase Invert Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_IFCTRL,
+ .mask1 = WM8766_PHASE_INVERT1,
+ },
+ [WM8766_CTL_PHASE2_SW] = {
+ .name = "Channel 2 Phase Invert Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_IFCTRL,
+ .mask1 = WM8766_PHASE_INVERT2,
+ },
+ [WM8766_CTL_PHASE3_SW] = {
+ .name = "Channel 3 Phase Invert Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_IFCTRL,
+ .mask1 = WM8766_PHASE_INVERT3,
+ },
+ [WM8766_CTL_DEEMPH1_SW] = {
+ .name = "Channel 1 Deemphasis Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_DACCTRL2,
+ .mask1 = WM8766_DAC2_DEEMP1,
+ },
+ [WM8766_CTL_DEEMPH2_SW] = {
+ .name = "Channel 2 Deemphasis Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_DACCTRL2,
+ .mask1 = WM8766_DAC2_DEEMP2,
+ },
+ [WM8766_CTL_DEEMPH3_SW] = {
+ .name = "Channel 3 Deemphasis Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_DACCTRL2,
+ .mask1 = WM8766_DAC2_DEEMP3,
+ },
+ [WM8766_CTL_IZD_SW] = {
+ .name = "Infinite Zero Detect Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_DACCTRL1,
+ .mask1 = WM8766_DAC_IZD,
+ },
+ [WM8766_CTL_ZC_SW] = {
+ .name = "Zero Cross Detect Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8766_REG_DACCTRL2,
+ .mask1 = WM8766_DAC2_ZCD,
+ .flags = WM8766_FLAG_INVERT,
+ },
+};
+
+/* exported functions */
+
+void snd_wm8766_init(struct snd_wm8766 *wm)
+{
+ int i;
+ static const u16 default_values[] = {
+ 0x000, 0x100,
+ 0x120, 0x000,
+ 0x000, 0x100, 0x000, 0x100, 0x000,
+ 0x000, 0x080,
+ };
+
+ memcpy(wm->ctl, snd_wm8766_default_ctl, sizeof(wm->ctl));
+
+ snd_wm8766_write(wm, WM8766_REG_RESET, 0x00); /* reset */
+ udelay(10);
+ /* load defaults */
+ for (i = 0; i < ARRAY_SIZE(default_values); i++)
+ snd_wm8766_write(wm, i, default_values[i]);
+}
+
+void snd_wm8766_resume(struct snd_wm8766 *wm)
+{
+ int i;
+
+ for (i = 0; i < WM8766_REG_COUNT; i++)
+ snd_wm8766_write(wm, i, wm->regs[i]);
+}
+
+void snd_wm8766_set_if(struct snd_wm8766 *wm, u16 dac)
+{
+ u16 val = wm->regs[WM8766_REG_IFCTRL] & ~WM8766_IF_MASK;
+
+ dac &= WM8766_IF_MASK;
+ snd_wm8766_write(wm, WM8766_REG_IFCTRL, val | dac);
+}
+
+void snd_wm8766_volume_restore(struct snd_wm8766 *wm)
+{
+ u16 val = wm->regs[WM8766_REG_DACR1];
+ /* restore volume after MCLK stopped */
+ snd_wm8766_write(wm, WM8766_REG_DACR1, val | WM8766_VOL_UPDATE);
+}
+
+/* mixer callbacks */
+
+static int snd_wm8766_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = (wm->ctl[n].flags & WM8766_FLAG_STEREO) ? 2 : 1;
+ uinfo->value.integer.min = wm->ctl[n].min;
+ uinfo->value.integer.max = wm->ctl[n].max;
+
+ return 0;
+}
+
+static int snd_wm8766_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+
+ return snd_ctl_enum_info(uinfo, 1, wm->ctl[n].max,
+ wm->ctl[n].enum_names);
+}
+
+static int snd_wm8766_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+ u16 val1, val2;
+
+ if (wm->ctl[n].get)
+ wm->ctl[n].get(wm, &val1, &val2);
+ else {
+ val1 = wm->regs[wm->ctl[n].reg1] & wm->ctl[n].mask1;
+ val1 >>= __ffs(wm->ctl[n].mask1);
+ if (wm->ctl[n].flags & WM8766_FLAG_STEREO) {
+ val2 = wm->regs[wm->ctl[n].reg2] & wm->ctl[n].mask2;
+ val2 >>= __ffs(wm->ctl[n].mask2);
+ if (wm->ctl[n].flags & WM8766_FLAG_VOL_UPDATE)
+ val2 &= ~WM8766_VOL_UPDATE;
+ }
+ }
+ if (wm->ctl[n].flags & WM8766_FLAG_INVERT) {
+ val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min);
+ if (wm->ctl[n].flags & WM8766_FLAG_STEREO)
+ val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min);
+ }
+ ucontrol->value.integer.value[0] = val1;
+ if (wm->ctl[n].flags & WM8766_FLAG_STEREO)
+ ucontrol->value.integer.value[1] = val2;
+
+ return 0;
+}
+
+static int snd_wm8766_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_wm8766 *wm = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+ u16 val, regval1, regval2;
+
+ /* this also works for enum because value is a union */
+ regval1 = ucontrol->value.integer.value[0];
+ regval2 = ucontrol->value.integer.value[1];
+ if (wm->ctl[n].flags & WM8766_FLAG_INVERT) {
+ regval1 = wm->ctl[n].max - (regval1 - wm->ctl[n].min);
+ regval2 = wm->ctl[n].max - (regval2 - wm->ctl[n].min);
+ }
+ if (wm->ctl[n].set)
+ wm->ctl[n].set(wm, regval1, regval2);
+ else {
+ val = wm->regs[wm->ctl[n].reg1] & ~wm->ctl[n].mask1;
+ val |= regval1 << __ffs(wm->ctl[n].mask1);
+ /* both stereo controls in one register */
+ if (wm->ctl[n].flags & WM8766_FLAG_STEREO &&
+ wm->ctl[n].reg1 == wm->ctl[n].reg2) {
+ val &= ~wm->ctl[n].mask2;
+ val |= regval2 << __ffs(wm->ctl[n].mask2);
+ }
+ snd_wm8766_write(wm, wm->ctl[n].reg1, val);
+ /* stereo controls in different registers */
+ if (wm->ctl[n].flags & WM8766_FLAG_STEREO &&
+ wm->ctl[n].reg1 != wm->ctl[n].reg2) {
+ val = wm->regs[wm->ctl[n].reg2] & ~wm->ctl[n].mask2;
+ val |= regval2 << __ffs(wm->ctl[n].mask2);
+ if (wm->ctl[n].flags & WM8766_FLAG_VOL_UPDATE)
+ val |= WM8766_VOL_UPDATE;
+ snd_wm8766_write(wm, wm->ctl[n].reg2, val);
+ }
+ }
+
+ return 0;
+}
+
+static int snd_wm8766_add_control(struct snd_wm8766 *wm, int num)
+{
+ struct snd_kcontrol_new cont;
+ struct snd_kcontrol *ctl;
+
+ memset(&cont, 0, sizeof(cont));
+ cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ cont.private_value = num;
+ cont.name = wm->ctl[num].name;
+ cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ if (wm->ctl[num].flags & WM8766_FLAG_LIM ||
+ wm->ctl[num].flags & WM8766_FLAG_ALC)
+ cont.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ cont.tlv.p = NULL;
+ cont.get = snd_wm8766_ctl_get;
+ cont.put = snd_wm8766_ctl_put;
+
+ switch (wm->ctl[num].type) {
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ cont.info = snd_wm8766_volume_info;
+ cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ cont.tlv.p = wm->ctl[num].tlv;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ wm->ctl[num].max = 1;
+ if (wm->ctl[num].flags & WM8766_FLAG_STEREO)
+ cont.info = snd_ctl_boolean_stereo_info;
+ else
+ cont.info = snd_ctl_boolean_mono_info;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+ cont.info = snd_wm8766_enum_info;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ctl = snd_ctl_new1(&cont, wm);
+ if (!ctl)
+ return -ENOMEM;
+ wm->ctl[num].kctl = ctl;
+
+ return snd_ctl_add(wm->card, ctl);
+}
+
+int snd_wm8766_build_controls(struct snd_wm8766 *wm)
+{
+ int err, i;
+
+ for (i = 0; i < WM8766_CTL_COUNT; i++)
+ if (wm->ctl[i].name) {
+ err = snd_wm8766_add_control(wm, i);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
diff --git a/sound/pci/ice1712/wm8766.h b/sound/pci/ice1712/wm8766.h
new file mode 100644
index 000000000000..44ab7c72a3d1
--- /dev/null
+++ b/sound/pci/ice1712/wm8766.h
@@ -0,0 +1,147 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __SOUND_WM8766_H
+#define __SOUND_WM8766_H
+
+/*
+ * ALSA driver for ICEnsemble VT17xx
+ *
+ * Lowlevel functions for WM8766 codec
+ *
+ * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
+ */
+
+#define WM8766_REG_DACL1 0x00
+#define WM8766_REG_DACR1 0x01
+#define WM8766_VOL_MASK 0x1ff /* incl. update bit */
+#define WM8766_VOL_UPDATE (1 << 8) /* update volume */
+#define WM8766_REG_DACCTRL1 0x02
+#define WM8766_DAC_MUTEALL (1 << 0)
+#define WM8766_DAC_DEEMPALL (1 << 1)
+#define WM8766_DAC_PDWN (1 << 2)
+#define WM8766_DAC_ATC (1 << 3)
+#define WM8766_DAC_IZD (1 << 4)
+#define WM8766_DAC_PL_MASK 0x1e0
+#define WM8766_DAC_PL_LL (1 << 5) /* L chan: L signal */
+#define WM8766_DAC_PL_LR (2 << 5) /* L chan: R signal */
+#define WM8766_DAC_PL_LB (3 << 5) /* L chan: both */
+#define WM8766_DAC_PL_RL (1 << 7) /* R chan: L signal */
+#define WM8766_DAC_PL_RR (2 << 7) /* R chan: R signal */
+#define WM8766_DAC_PL_RB (3 << 7) /* R chan: both */
+#define WM8766_REG_IFCTRL 0x03
+#define WM8766_IF_FMT_RIGHTJ (0 << 0)
+#define WM8766_IF_FMT_LEFTJ (1 << 0)
+#define WM8766_IF_FMT_I2S (2 << 0)
+#define WM8766_IF_FMT_DSP (3 << 0)
+#define WM8766_IF_DSP_LATE (1 << 2) /* in DSP mode */
+#define WM8766_IF_LRC_INVERTED (1 << 2) /* in other modes */
+#define WM8766_IF_BCLK_INVERTED (1 << 3)
+#define WM8766_IF_IWL_16BIT (0 << 4)
+#define WM8766_IF_IWL_20BIT (1 << 4)
+#define WM8766_IF_IWL_24BIT (2 << 4)
+#define WM8766_IF_IWL_32BIT (3 << 4)
+#define WM8766_IF_MASK 0x3f
+#define WM8766_PHASE_INVERT1 (1 << 6)
+#define WM8766_PHASE_INVERT2 (1 << 7)
+#define WM8766_PHASE_INVERT3 (1 << 8)
+#define WM8766_REG_DACL2 0x04
+#define WM8766_REG_DACR2 0x05
+#define WM8766_REG_DACL3 0x06
+#define WM8766_REG_DACR3 0x07
+#define WM8766_REG_MASTDA 0x08
+#define WM8766_REG_DACCTRL2 0x09
+#define WM8766_DAC2_ZCD (1 << 0)
+#define WM8766_DAC2_ZFLAG_ALL (0 << 1)
+#define WM8766_DAC2_ZFLAG_1 (1 << 1)
+#define WM8766_DAC2_ZFLAG_2 (2 << 1)
+#define WM8766_DAC2_ZFLAG_3 (3 << 1)
+#define WM8766_DAC2_MUTE1 (1 << 3)
+#define WM8766_DAC2_MUTE2 (1 << 4)
+#define WM8766_DAC2_MUTE3 (1 << 5)
+#define WM8766_DAC2_DEEMP1 (1 << 6)
+#define WM8766_DAC2_DEEMP2 (1 << 7)
+#define WM8766_DAC2_DEEMP3 (1 << 8)
+#define WM8766_REG_DACCTRL3 0x0a
+#define WM8766_DAC3_DACPD1 (1 << 1)
+#define WM8766_DAC3_DACPD2 (1 << 2)
+#define WM8766_DAC3_DACPD3 (1 << 3)
+#define WM8766_DAC3_PWRDNALL (1 << 4)
+#define WM8766_DAC3_POWER_MASK 0x1e
+#define WM8766_DAC3_MASTER (1 << 5)
+#define WM8766_DAC3_DAC128FS (0 << 6)
+#define WM8766_DAC3_DAC192FS (1 << 6)
+#define WM8766_DAC3_DAC256FS (2 << 6)
+#define WM8766_DAC3_DAC384FS (3 << 6)
+#define WM8766_DAC3_DAC512FS (4 << 6)
+#define WM8766_DAC3_DAC768FS (5 << 6)
+#define WM8766_DAC3_MSTR_MASK 0x1e0
+#define WM8766_REG_MUTE1 0x0c
+#define WM8766_MUTE1_MPD (1 << 6)
+#define WM8766_REG_MUTE2 0x0f
+#define WM8766_MUTE2_MPD (1 << 5)
+#define WM8766_REG_RESET 0x1f
+
+#define WM8766_REG_COUNT 0x10 /* don't cache the RESET register */
+
+struct snd_wm8766;
+
+struct snd_wm8766_ops {
+ void (*write)(struct snd_wm8766 *wm, u16 addr, u16 data);
+};
+
+enum snd_wm8766_ctl_id {
+ WM8766_CTL_CH1_VOL,
+ WM8766_CTL_CH2_VOL,
+ WM8766_CTL_CH3_VOL,
+ WM8766_CTL_CH1_SW,
+ WM8766_CTL_CH2_SW,
+ WM8766_CTL_CH3_SW,
+ WM8766_CTL_PHASE1_SW,
+ WM8766_CTL_PHASE2_SW,
+ WM8766_CTL_PHASE3_SW,
+ WM8766_CTL_DEEMPH1_SW,
+ WM8766_CTL_DEEMPH2_SW,
+ WM8766_CTL_DEEMPH3_SW,
+ WM8766_CTL_IZD_SW,
+ WM8766_CTL_ZC_SW,
+
+ WM8766_CTL_COUNT,
+};
+
+#define WM8766_ENUM_MAX 16
+
+#define WM8766_FLAG_STEREO (1 << 0)
+#define WM8766_FLAG_VOL_UPDATE (1 << 1)
+#define WM8766_FLAG_INVERT (1 << 2)
+#define WM8766_FLAG_LIM (1 << 3)
+#define WM8766_FLAG_ALC (1 << 4)
+
+struct snd_wm8766_ctl {
+ struct snd_kcontrol *kctl;
+ const char *name;
+ snd_ctl_elem_type_t type;
+ const char *const enum_names[WM8766_ENUM_MAX];
+ const unsigned int *tlv;
+ u16 reg1, reg2, mask1, mask2, min, max, flags;
+ void (*set)(struct snd_wm8766 *wm, u16 ch1, u16 ch2);
+ void (*get)(struct snd_wm8766 *wm, u16 *ch1, u16 *ch2);
+};
+
+enum snd_wm8766_agc_mode { WM8766_AGC_OFF, WM8766_AGC_LIM, WM8766_AGC_ALC };
+
+struct snd_wm8766 {
+ struct snd_card *card;
+ struct snd_wm8766_ctl ctl[WM8766_CTL_COUNT];
+ enum snd_wm8766_agc_mode agc_mode;
+ struct snd_wm8766_ops ops;
+ u16 regs[WM8766_REG_COUNT]; /* 9-bit registers */
+};
+
+
+
+void snd_wm8766_init(struct snd_wm8766 *wm);
+void snd_wm8766_resume(struct snd_wm8766 *wm);
+void snd_wm8766_set_if(struct snd_wm8766 *wm, u16 dac);
+void snd_wm8766_volume_restore(struct snd_wm8766 *wm);
+int snd_wm8766_build_controls(struct snd_wm8766 *wm);
+
+#endif /* __SOUND_WM8766_H */
diff --git a/sound/pci/ice1712/wm8776.c b/sound/pci/ice1712/wm8776.c
new file mode 100644
index 000000000000..493425697bb4
--- /dev/null
+++ b/sound/pci/ice1712/wm8776.c
@@ -0,0 +1,601 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ALSA driver for ICEnsemble VT17xx
+ *
+ * Lowlevel functions for WM8776 codec
+ *
+ * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
+ */
+
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include "wm8776.h"
+
+/* low-level access */
+
+static void snd_wm8776_write(struct snd_wm8776 *wm, u16 addr, u16 data)
+{
+ u8 bus_addr = addr << 1 | data >> 8; /* addr + 9th data bit */
+ u8 bus_data = data & 0xff; /* remaining 8 data bits */
+
+ if (addr < WM8776_REG_RESET)
+ wm->regs[addr] = data;
+ wm->ops.write(wm, bus_addr, bus_data);
+}
+
+/* register-level functions */
+
+static void snd_wm8776_activate_ctl(struct snd_wm8776 *wm,
+ const char *ctl_name,
+ bool active)
+{
+ struct snd_card *card = wm->card;
+ struct snd_kcontrol *kctl;
+ struct snd_kcontrol_volatile *vd;
+ unsigned int index_offset;
+
+ kctl = snd_ctl_find_id_mixer(card, ctl_name);
+ if (!kctl)
+ return;
+ index_offset = snd_ctl_get_ioff(kctl, &kctl->id);
+ vd = &kctl->vd[index_offset];
+ if (active)
+ vd->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ else
+ vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id);
+}
+
+static void snd_wm8776_update_agc_ctl(struct snd_wm8776 *wm)
+{
+ int i, flags_on = 0, flags_off = 0;
+
+ switch (wm->agc_mode) {
+ case WM8776_AGC_OFF:
+ flags_off = WM8776_FLAG_LIM | WM8776_FLAG_ALC;
+ break;
+ case WM8776_AGC_LIM:
+ flags_off = WM8776_FLAG_ALC;
+ flags_on = WM8776_FLAG_LIM;
+ break;
+ case WM8776_AGC_ALC_R:
+ case WM8776_AGC_ALC_L:
+ case WM8776_AGC_ALC_STEREO:
+ flags_off = WM8776_FLAG_LIM;
+ flags_on = WM8776_FLAG_ALC;
+ break;
+ }
+
+ for (i = 0; i < WM8776_CTL_COUNT; i++)
+ if (wm->ctl[i].flags & flags_off)
+ snd_wm8776_activate_ctl(wm, wm->ctl[i].name, false);
+ else if (wm->ctl[i].flags & flags_on)
+ snd_wm8776_activate_ctl(wm, wm->ctl[i].name, true);
+}
+
+static void snd_wm8776_set_agc(struct snd_wm8776 *wm, u16 agc, u16 nothing)
+{
+ u16 alc1 = wm->regs[WM8776_REG_ALCCTRL1] & ~WM8776_ALC1_LCT_MASK;
+ u16 alc2 = wm->regs[WM8776_REG_ALCCTRL2] & ~WM8776_ALC2_LCEN;
+
+ switch (agc) {
+ case 0: /* Off */
+ wm->agc_mode = WM8776_AGC_OFF;
+ break;
+ case 1: /* Limiter */
+ alc2 |= WM8776_ALC2_LCEN;
+ wm->agc_mode = WM8776_AGC_LIM;
+ break;
+ case 2: /* ALC Right */
+ alc1 |= WM8776_ALC1_LCSEL_ALCR;
+ alc2 |= WM8776_ALC2_LCEN;
+ wm->agc_mode = WM8776_AGC_ALC_R;
+ break;
+ case 3: /* ALC Left */
+ alc1 |= WM8776_ALC1_LCSEL_ALCL;
+ alc2 |= WM8776_ALC2_LCEN;
+ wm->agc_mode = WM8776_AGC_ALC_L;
+ break;
+ case 4: /* ALC Stereo */
+ alc1 |= WM8776_ALC1_LCSEL_ALCSTEREO;
+ alc2 |= WM8776_ALC2_LCEN;
+ wm->agc_mode = WM8776_AGC_ALC_STEREO;
+ break;
+ }
+ snd_wm8776_write(wm, WM8776_REG_ALCCTRL1, alc1);
+ snd_wm8776_write(wm, WM8776_REG_ALCCTRL2, alc2);
+ snd_wm8776_update_agc_ctl(wm);
+}
+
+static void snd_wm8776_get_agc(struct snd_wm8776 *wm, u16 *mode, u16 *nothing)
+{
+ *mode = wm->agc_mode;
+}
+
+/* mixer controls */
+
+static const DECLARE_TLV_DB_SCALE(wm8776_hp_tlv, -7400, 100, 1);
+static const DECLARE_TLV_DB_SCALE(wm8776_dac_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(wm8776_adc_tlv, -10350, 50, 1);
+static const DECLARE_TLV_DB_SCALE(wm8776_lct_tlv, -1600, 100, 0);
+static const DECLARE_TLV_DB_SCALE(wm8776_maxgain_tlv, 0, 400, 0);
+static const DECLARE_TLV_DB_SCALE(wm8776_ngth_tlv, -7800, 600, 0);
+static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_lim_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_alc_tlv, -2100, 400, 0);
+
+static const struct snd_wm8776_ctl snd_wm8776_default_ctl[WM8776_CTL_COUNT] = {
+ [WM8776_CTL_DAC_VOL] = {
+ .name = "Master Playback Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8776_dac_tlv,
+ .reg1 = WM8776_REG_DACLVOL,
+ .reg2 = WM8776_REG_DACRVOL,
+ .mask1 = WM8776_DACVOL_MASK,
+ .mask2 = WM8776_DACVOL_MASK,
+ .max = 0xff,
+ .flags = WM8776_FLAG_STEREO | WM8776_FLAG_VOL_UPDATE,
+ },
+ [WM8776_CTL_DAC_SW] = {
+ .name = "Master Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_DACCTRL1,
+ .reg2 = WM8776_REG_DACCTRL1,
+ .mask1 = WM8776_DAC_PL_LL,
+ .mask2 = WM8776_DAC_PL_RR,
+ .flags = WM8776_FLAG_STEREO,
+ },
+ [WM8776_CTL_DAC_ZC_SW] = {
+ .name = "Master Zero Cross Detect Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_DACCTRL1,
+ .mask1 = WM8776_DAC_DZCEN,
+ },
+ [WM8776_CTL_HP_VOL] = {
+ .name = "Headphone Playback Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8776_hp_tlv,
+ .reg1 = WM8776_REG_HPLVOL,
+ .reg2 = WM8776_REG_HPRVOL,
+ .mask1 = WM8776_HPVOL_MASK,
+ .mask2 = WM8776_HPVOL_MASK,
+ .min = 0x2f,
+ .max = 0x7f,
+ .flags = WM8776_FLAG_STEREO | WM8776_FLAG_VOL_UPDATE,
+ },
+ [WM8776_CTL_HP_SW] = {
+ .name = "Headphone Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_PWRDOWN,
+ .mask1 = WM8776_PWR_HPPD,
+ .flags = WM8776_FLAG_INVERT,
+ },
+ [WM8776_CTL_HP_ZC_SW] = {
+ .name = "Headphone Zero Cross Detect Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_HPLVOL,
+ .reg2 = WM8776_REG_HPRVOL,
+ .mask1 = WM8776_VOL_HPZCEN,
+ .mask2 = WM8776_VOL_HPZCEN,
+ .flags = WM8776_FLAG_STEREO,
+ },
+ [WM8776_CTL_AUX_SW] = {
+ .name = "AUX Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_OUTMUX,
+ .mask1 = WM8776_OUTMUX_AUX,
+ },
+ [WM8776_CTL_BYPASS_SW] = {
+ .name = "Bypass Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_OUTMUX,
+ .mask1 = WM8776_OUTMUX_BYPASS,
+ },
+ [WM8776_CTL_DAC_IZD_SW] = {
+ .name = "Infinite Zero Detect Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_DACCTRL1,
+ .mask1 = WM8776_DAC_IZD,
+ },
+ [WM8776_CTL_PHASE_SW] = {
+ .name = "Phase Invert Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_PHASESWAP,
+ .reg2 = WM8776_REG_PHASESWAP,
+ .mask1 = WM8776_PHASE_INVERTL,
+ .mask2 = WM8776_PHASE_INVERTR,
+ .flags = WM8776_FLAG_STEREO,
+ },
+ [WM8776_CTL_DEEMPH_SW] = {
+ .name = "Deemphasis Playback Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_DACCTRL2,
+ .mask1 = WM8776_DAC2_DEEMPH,
+ },
+ [WM8776_CTL_ADC_VOL] = {
+ .name = "Input Capture Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8776_adc_tlv,
+ .reg1 = WM8776_REG_ADCLVOL,
+ .reg2 = WM8776_REG_ADCRVOL,
+ .mask1 = WM8776_ADC_GAIN_MASK,
+ .mask2 = WM8776_ADC_GAIN_MASK,
+ .max = 0xff,
+ .flags = WM8776_FLAG_STEREO | WM8776_FLAG_VOL_UPDATE,
+ },
+ [WM8776_CTL_ADC_SW] = {
+ .name = "Input Capture Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_ADCMUX,
+ .reg2 = WM8776_REG_ADCMUX,
+ .mask1 = WM8776_ADC_MUTEL,
+ .mask2 = WM8776_ADC_MUTER,
+ .flags = WM8776_FLAG_STEREO | WM8776_FLAG_INVERT,
+ },
+ [WM8776_CTL_INPUT1_SW] = {
+ .name = "AIN1 Capture Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_ADCMUX,
+ .mask1 = WM8776_ADC_MUX_AIN1,
+ },
+ [WM8776_CTL_INPUT2_SW] = {
+ .name = "AIN2 Capture Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_ADCMUX,
+ .mask1 = WM8776_ADC_MUX_AIN2,
+ },
+ [WM8776_CTL_INPUT3_SW] = {
+ .name = "AIN3 Capture Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_ADCMUX,
+ .mask1 = WM8776_ADC_MUX_AIN3,
+ },
+ [WM8776_CTL_INPUT4_SW] = {
+ .name = "AIN4 Capture Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_ADCMUX,
+ .mask1 = WM8776_ADC_MUX_AIN4,
+ },
+ [WM8776_CTL_INPUT5_SW] = {
+ .name = "AIN5 Capture Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_ADCMUX,
+ .mask1 = WM8776_ADC_MUX_AIN5,
+ },
+ [WM8776_CTL_AGC_SEL] = {
+ .name = "AGC Select Capture Enum",
+ .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
+ .enum_names = { "Off", "Limiter", "ALC Right", "ALC Left",
+ "ALC Stereo" },
+ .max = 5, /* .enum_names item count */
+ .set = snd_wm8776_set_agc,
+ .get = snd_wm8776_get_agc,
+ },
+ [WM8776_CTL_LIM_THR] = {
+ .name = "Limiter Threshold Capture Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8776_lct_tlv,
+ .reg1 = WM8776_REG_ALCCTRL1,
+ .mask1 = WM8776_ALC1_LCT_MASK,
+ .max = 15,
+ .flags = WM8776_FLAG_LIM,
+ },
+ [WM8776_CTL_LIM_ATK] = {
+ .name = "Limiter Attack Time Capture Enum",
+ .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
+ .enum_names = { "0.25 ms", "0.5 ms", "1 ms", "2 ms", "4 ms",
+ "8 ms", "16 ms", "32 ms", "64 ms", "128 ms", "256 ms" },
+ .max = 11, /* .enum_names item count */
+ .reg1 = WM8776_REG_ALCCTRL3,
+ .mask1 = WM8776_ALC3_ATK_MASK,
+ .flags = WM8776_FLAG_LIM,
+ },
+ [WM8776_CTL_LIM_DCY] = {
+ .name = "Limiter Decay Time Capture Enum",
+ .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
+ .enum_names = { "1.2 ms", "2.4 ms", "4.8 ms", "9.6 ms",
+ "19.2 ms", "38.4 ms", "76.8 ms", "154 ms", "307 ms",
+ "614 ms", "1.23 s" },
+ .max = 11, /* .enum_names item count */
+ .reg1 = WM8776_REG_ALCCTRL3,
+ .mask1 = WM8776_ALC3_DCY_MASK,
+ .flags = WM8776_FLAG_LIM,
+ },
+ [WM8776_CTL_LIM_TRANWIN] = {
+ .name = "Limiter Transient Window Capture Enum",
+ .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
+ .enum_names = { "0 us", "62.5 us", "125 us", "250 us", "500 us",
+ "1 ms", "2 ms", "4 ms" },
+ .max = 8, /* .enum_names item count */
+ .reg1 = WM8776_REG_LIMITER,
+ .mask1 = WM8776_LIM_TRANWIN_MASK,
+ .flags = WM8776_FLAG_LIM,
+ },
+ [WM8776_CTL_LIM_MAXATTN] = {
+ .name = "Limiter Maximum Attenuation Capture Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8776_maxatten_lim_tlv,
+ .reg1 = WM8776_REG_LIMITER,
+ .mask1 = WM8776_LIM_MAXATTEN_MASK,
+ .min = 3,
+ .max = 12,
+ .flags = WM8776_FLAG_LIM | WM8776_FLAG_INVERT,
+ },
+ [WM8776_CTL_ALC_TGT] = {
+ .name = "ALC Target Level Capture Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8776_lct_tlv,
+ .reg1 = WM8776_REG_ALCCTRL1,
+ .mask1 = WM8776_ALC1_LCT_MASK,
+ .max = 15,
+ .flags = WM8776_FLAG_ALC,
+ },
+ [WM8776_CTL_ALC_ATK] = {
+ .name = "ALC Attack Time Capture Enum",
+ .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
+ .enum_names = { "8.40 ms", "16.8 ms", "33.6 ms", "67.2 ms",
+ "134 ms", "269 ms", "538 ms", "1.08 s", "2.15 s",
+ "4.3 s", "8.6 s" },
+ .max = 11, /* .enum_names item count */
+ .reg1 = WM8776_REG_ALCCTRL3,
+ .mask1 = WM8776_ALC3_ATK_MASK,
+ .flags = WM8776_FLAG_ALC,
+ },
+ [WM8776_CTL_ALC_DCY] = {
+ .name = "ALC Decay Time Capture Enum",
+ .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
+ .enum_names = { "33.5 ms", "67.0 ms", "134 ms", "268 ms",
+ "536 ms", "1.07 s", "2.14 s", "4.29 s", "8.58 s",
+ "17.2 s", "34.3 s" },
+ .max = 11, /* .enum_names item count */
+ .reg1 = WM8776_REG_ALCCTRL3,
+ .mask1 = WM8776_ALC3_DCY_MASK,
+ .flags = WM8776_FLAG_ALC,
+ },
+ [WM8776_CTL_ALC_MAXGAIN] = {
+ .name = "ALC Maximum Gain Capture Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8776_maxgain_tlv,
+ .reg1 = WM8776_REG_ALCCTRL1,
+ .mask1 = WM8776_ALC1_MAXGAIN_MASK,
+ .min = 1,
+ .max = 7,
+ .flags = WM8776_FLAG_ALC,
+ },
+ [WM8776_CTL_ALC_MAXATTN] = {
+ .name = "ALC Maximum Attenuation Capture Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8776_maxatten_alc_tlv,
+ .reg1 = WM8776_REG_LIMITER,
+ .mask1 = WM8776_LIM_MAXATTEN_MASK,
+ .min = 10,
+ .max = 15,
+ .flags = WM8776_FLAG_ALC | WM8776_FLAG_INVERT,
+ },
+ [WM8776_CTL_ALC_HLD] = {
+ .name = "ALC Hold Time Capture Enum",
+ .type = SNDRV_CTL_ELEM_TYPE_ENUMERATED,
+ .enum_names = { "0 ms", "2.67 ms", "5.33 ms", "10.6 ms",
+ "21.3 ms", "42.7 ms", "85.3 ms", "171 ms", "341 ms",
+ "683 ms", "1.37 s", "2.73 s", "5.46 s", "10.9 s",
+ "21.8 s", "43.7 s" },
+ .max = 16, /* .enum_names item count */
+ .reg1 = WM8776_REG_ALCCTRL2,
+ .mask1 = WM8776_ALC2_HOLD_MASK,
+ .flags = WM8776_FLAG_ALC,
+ },
+ [WM8776_CTL_NGT_SW] = {
+ .name = "Noise Gate Capture Switch",
+ .type = SNDRV_CTL_ELEM_TYPE_BOOLEAN,
+ .reg1 = WM8776_REG_NOISEGATE,
+ .mask1 = WM8776_NGAT_ENABLE,
+ .flags = WM8776_FLAG_ALC,
+ },
+ [WM8776_CTL_NGT_THR] = {
+ .name = "Noise Gate Threshold Capture Volume",
+ .type = SNDRV_CTL_ELEM_TYPE_INTEGER,
+ .tlv = wm8776_ngth_tlv,
+ .reg1 = WM8776_REG_NOISEGATE,
+ .mask1 = WM8776_NGAT_THR_MASK,
+ .max = 7,
+ .flags = WM8776_FLAG_ALC,
+ },
+};
+
+/* exported functions */
+
+void snd_wm8776_init(struct snd_wm8776 *wm)
+{
+ int i;
+ static const u16 default_values[] = {
+ 0x000, 0x100, 0x000,
+ 0x000, 0x100, 0x000,
+ 0x000, 0x090, 0x000, 0x000,
+ 0x022, 0x022, 0x022,
+ 0x008, 0x0cf, 0x0cf, 0x07b, 0x000,
+ 0x032, 0x000, 0x0a6, 0x001, 0x001
+ };
+
+ memcpy(wm->ctl, snd_wm8776_default_ctl, sizeof(wm->ctl));
+
+ snd_wm8776_write(wm, WM8776_REG_RESET, 0x00); /* reset */
+ udelay(10);
+ /* load defaults */
+ for (i = 0; i < ARRAY_SIZE(default_values); i++)
+ snd_wm8776_write(wm, i, default_values[i]);
+}
+
+void snd_wm8776_resume(struct snd_wm8776 *wm)
+{
+ int i;
+
+ for (i = 0; i < WM8776_REG_COUNT; i++)
+ snd_wm8776_write(wm, i, wm->regs[i]);
+}
+
+void snd_wm8776_set_power(struct snd_wm8776 *wm, u16 power)
+{
+ snd_wm8776_write(wm, WM8776_REG_PWRDOWN, power);
+}
+
+void snd_wm8776_volume_restore(struct snd_wm8776 *wm)
+{
+ u16 val = wm->regs[WM8776_REG_DACRVOL];
+ /* restore volume after MCLK stopped */
+ snd_wm8776_write(wm, WM8776_REG_DACRVOL, val | WM8776_VOL_UPDATE);
+}
+
+/* mixer callbacks */
+
+static int snd_wm8776_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = (wm->ctl[n].flags & WM8776_FLAG_STEREO) ? 2 : 1;
+ uinfo->value.integer.min = wm->ctl[n].min;
+ uinfo->value.integer.max = wm->ctl[n].max;
+
+ return 0;
+}
+
+static int snd_wm8776_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+
+ return snd_ctl_enum_info(uinfo, 1, wm->ctl[n].max,
+ wm->ctl[n].enum_names);
+}
+
+static int snd_wm8776_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+ u16 val1, val2;
+
+ if (wm->ctl[n].get)
+ wm->ctl[n].get(wm, &val1, &val2);
+ else {
+ val1 = wm->regs[wm->ctl[n].reg1] & wm->ctl[n].mask1;
+ val1 >>= __ffs(wm->ctl[n].mask1);
+ if (wm->ctl[n].flags & WM8776_FLAG_STEREO) {
+ val2 = wm->regs[wm->ctl[n].reg2] & wm->ctl[n].mask2;
+ val2 >>= __ffs(wm->ctl[n].mask2);
+ if (wm->ctl[n].flags & WM8776_FLAG_VOL_UPDATE)
+ val2 &= ~WM8776_VOL_UPDATE;
+ }
+ }
+ if (wm->ctl[n].flags & WM8776_FLAG_INVERT) {
+ val1 = wm->ctl[n].max - (val1 - wm->ctl[n].min);
+ if (wm->ctl[n].flags & WM8776_FLAG_STEREO)
+ val2 = wm->ctl[n].max - (val2 - wm->ctl[n].min);
+ }
+ ucontrol->value.integer.value[0] = val1;
+ if (wm->ctl[n].flags & WM8776_FLAG_STEREO)
+ ucontrol->value.integer.value[1] = val2;
+
+ return 0;
+}
+
+static int snd_wm8776_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_wm8776 *wm = snd_kcontrol_chip(kcontrol);
+ int n = kcontrol->private_value;
+ u16 val, regval1, regval2;
+
+ /* this also works for enum because value is a union */
+ regval1 = ucontrol->value.integer.value[0];
+ regval2 = ucontrol->value.integer.value[1];
+ if (wm->ctl[n].flags & WM8776_FLAG_INVERT) {
+ regval1 = wm->ctl[n].max - (regval1 - wm->ctl[n].min);
+ regval2 = wm->ctl[n].max - (regval2 - wm->ctl[n].min);
+ }
+ if (wm->ctl[n].set)
+ wm->ctl[n].set(wm, regval1, regval2);
+ else {
+ val = wm->regs[wm->ctl[n].reg1] & ~wm->ctl[n].mask1;
+ val |= regval1 << __ffs(wm->ctl[n].mask1);
+ /* both stereo controls in one register */
+ if (wm->ctl[n].flags & WM8776_FLAG_STEREO &&
+ wm->ctl[n].reg1 == wm->ctl[n].reg2) {
+ val &= ~wm->ctl[n].mask2;
+ val |= regval2 << __ffs(wm->ctl[n].mask2);
+ }
+ snd_wm8776_write(wm, wm->ctl[n].reg1, val);
+ /* stereo controls in different registers */
+ if (wm->ctl[n].flags & WM8776_FLAG_STEREO &&
+ wm->ctl[n].reg1 != wm->ctl[n].reg2) {
+ val = wm->regs[wm->ctl[n].reg2] & ~wm->ctl[n].mask2;
+ val |= regval2 << __ffs(wm->ctl[n].mask2);
+ if (wm->ctl[n].flags & WM8776_FLAG_VOL_UPDATE)
+ val |= WM8776_VOL_UPDATE;
+ snd_wm8776_write(wm, wm->ctl[n].reg2, val);
+ }
+ }
+
+ return 0;
+}
+
+static int snd_wm8776_add_control(struct snd_wm8776 *wm, int num)
+{
+ struct snd_kcontrol_new cont;
+ struct snd_kcontrol *ctl;
+
+ memset(&cont, 0, sizeof(cont));
+ cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ cont.private_value = num;
+ cont.name = wm->ctl[num].name;
+ cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ if (wm->ctl[num].flags & WM8776_FLAG_LIM ||
+ wm->ctl[num].flags & WM8776_FLAG_ALC)
+ cont.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ cont.tlv.p = NULL;
+ cont.get = snd_wm8776_ctl_get;
+ cont.put = snd_wm8776_ctl_put;
+
+ switch (wm->ctl[num].type) {
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ cont.info = snd_wm8776_volume_info;
+ cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ cont.tlv.p = wm->ctl[num].tlv;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ wm->ctl[num].max = 1;
+ if (wm->ctl[num].flags & WM8776_FLAG_STEREO)
+ cont.info = snd_ctl_boolean_stereo_info;
+ else
+ cont.info = snd_ctl_boolean_mono_info;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+ cont.info = snd_wm8776_enum_info;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ctl = snd_ctl_new1(&cont, wm);
+ if (!ctl)
+ return -ENOMEM;
+
+ return snd_ctl_add(wm->card, ctl);
+}
+
+int snd_wm8776_build_controls(struct snd_wm8776 *wm)
+{
+ int err, i;
+
+ for (i = 0; i < WM8776_CTL_COUNT; i++)
+ if (wm->ctl[i].name) {
+ err = snd_wm8776_add_control(wm, i);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
diff --git a/sound/pci/ice1712/wm8776.h b/sound/pci/ice1712/wm8776.h
new file mode 100644
index 000000000000..0d49d0c7488a
--- /dev/null
+++ b/sound/pci/ice1712/wm8776.h
@@ -0,0 +1,209 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __SOUND_WM8776_H
+#define __SOUND_WM8776_H
+
+/*
+ * ALSA driver for ICEnsemble VT17xx
+ *
+ * Lowlevel functions for WM8776 codec
+ *
+ * Copyright (c) 2012 Ondrej Zary <linux@rainbow-software.org>
+ */
+
+#define WM8776_REG_HPLVOL 0x00
+#define WM8776_REG_HPRVOL 0x01
+#define WM8776_REG_HPMASTER 0x02
+#define WM8776_HPVOL_MASK 0x17f /* incl. update bit */
+#define WM8776_VOL_HPZCEN (1 << 7) /* zero cross detect */
+#define WM8776_VOL_UPDATE (1 << 8) /* update volume */
+#define WM8776_REG_DACLVOL 0x03
+#define WM8776_REG_DACRVOL 0x04
+#define WM8776_REG_DACMASTER 0x05
+#define WM8776_DACVOL_MASK 0x1ff /* incl. update bit */
+#define WM8776_REG_PHASESWAP 0x06
+#define WM8776_PHASE_INVERTL (1 << 0)
+#define WM8776_PHASE_INVERTR (1 << 1)
+#define WM8776_REG_DACCTRL1 0x07
+#define WM8776_DAC_DZCEN (1 << 0)
+#define WM8776_DAC_ATC (1 << 1)
+#define WM8776_DAC_IZD (1 << 2)
+#define WM8776_DAC_TOD (1 << 3)
+#define WM8776_DAC_PL_MASK 0xf0
+#define WM8776_DAC_PL_LL (1 << 4) /* L chan: L signal */
+#define WM8776_DAC_PL_LR (2 << 4) /* L chan: R signal */
+#define WM8776_DAC_PL_LB (3 << 4) /* L chan: both */
+#define WM8776_DAC_PL_RL (1 << 6) /* R chan: L signal */
+#define WM8776_DAC_PL_RR (2 << 6) /* R chan: R signal */
+#define WM8776_DAC_PL_RB (3 << 6) /* R chan: both */
+#define WM8776_REG_DACMUTE 0x08
+#define WM8776_DACMUTE (1 << 0)
+#define WM8776_REG_DACCTRL2 0x09
+#define WM8776_DAC2_DEEMPH (1 << 0)
+#define WM8776_DAC2_ZFLAG_DISABLE (0 << 1)
+#define WM8776_DAC2_ZFLAG_OWN (1 << 1)
+#define WM8776_DAC2_ZFLAG_BOTH (2 << 1)
+#define WM8776_DAC2_ZFLAG_EITHER (3 << 1)
+#define WM8776_REG_DACIFCTRL 0x0a
+#define WM8776_FMT_RIGHTJ (0 << 0)
+#define WM8776_FMT_LEFTJ (1 << 0)
+#define WM8776_FMT_I2S (2 << 0)
+#define WM8776_FMT_DSP (3 << 0)
+#define WM8776_FMT_DSP_LATE (1 << 2) /* in DSP mode */
+#define WM8776_FMT_LRC_INVERTED (1 << 2) /* in other modes */
+#define WM8776_FMT_BCLK_INVERTED (1 << 3)
+#define WM8776_FMT_16BIT (0 << 4)
+#define WM8776_FMT_20BIT (1 << 4)
+#define WM8776_FMT_24BIT (2 << 4)
+#define WM8776_FMT_32BIT (3 << 4)
+#define WM8776_REG_ADCIFCTRL 0x0b
+#define WM8776_FMT_ADCMCLK_INVERTED (1 << 6)
+#define WM8776_FMT_ADCHPD (1 << 8)
+#define WM8776_REG_MSTRCTRL 0x0c
+#define WM8776_IF_ADC256FS (2 << 0)
+#define WM8776_IF_ADC384FS (3 << 0)
+#define WM8776_IF_ADC512FS (4 << 0)
+#define WM8776_IF_ADC768FS (5 << 0)
+#define WM8776_IF_OVERSAMP64 (1 << 3)
+#define WM8776_IF_DAC128FS (0 << 4)
+#define WM8776_IF_DAC192FS (1 << 4)
+#define WM8776_IF_DAC256FS (2 << 4)
+#define WM8776_IF_DAC384FS (3 << 4)
+#define WM8776_IF_DAC512FS (4 << 4)
+#define WM8776_IF_DAC768FS (5 << 4)
+#define WM8776_IF_DAC_MASTER (1 << 7)
+#define WM8776_IF_ADC_MASTER (1 << 8)
+#define WM8776_REG_PWRDOWN 0x0d
+#define WM8776_PWR_PDWN (1 << 0)
+#define WM8776_PWR_ADCPD (1 << 1)
+#define WM8776_PWR_DACPD (1 << 2)
+#define WM8776_PWR_HPPD (1 << 3)
+#define WM8776_PWR_AINPD (1 << 6)
+#define WM8776_REG_ADCLVOL 0x0e
+#define WM8776_REG_ADCRVOL 0x0f
+#define WM8776_ADC_GAIN_MASK 0xff
+#define WM8776_ADC_ZCEN (1 << 8)
+#define WM8776_REG_ALCCTRL1 0x10
+#define WM8776_ALC1_LCT_MASK 0x0f /* 0=-16dB, 1=-15dB..15=-1dB */
+#define WM8776_ALC1_MAXGAIN_MASK 0x70 /* 0,1=0dB, 2=+4dB...7=+24dB */
+#define WM8776_ALC1_LCSEL_MASK 0x180
+#define WM8776_ALC1_LCSEL_LIMITER (0 << 7)
+#define WM8776_ALC1_LCSEL_ALCR (1 << 7)
+#define WM8776_ALC1_LCSEL_ALCL (2 << 7)
+#define WM8776_ALC1_LCSEL_ALCSTEREO (3 << 7)
+#define WM8776_REG_ALCCTRL2 0x11
+#define WM8776_ALC2_HOLD_MASK 0x0f /*0=0ms, 1=2.67ms, 2=5.33ms.. */
+#define WM8776_ALC2_ZCEN (1 << 7)
+#define WM8776_ALC2_LCEN (1 << 8)
+#define WM8776_REG_ALCCTRL3 0x12
+#define WM8776_ALC3_ATK_MASK 0x0f
+#define WM8776_ALC3_DCY_MASK 0xf0
+#define WM8776_ALC3_FDECAY (1 << 8)
+#define WM8776_REG_NOISEGATE 0x13
+#define WM8776_NGAT_ENABLE (1 << 0)
+#define WM8776_NGAT_THR_MASK 0x1c /*0=-78dB, 1=-72dB...7=-36dB */
+#define WM8776_REG_LIMITER 0x14
+#define WM8776_LIM_MAXATTEN_MASK 0x0f
+#define WM8776_LIM_TRANWIN_MASK 0x70 /*0=0us, 1=62.5us, 2=125us.. */
+#define WM8776_REG_ADCMUX 0x15
+#define WM8776_ADC_MUX_AIN1 (1 << 0)
+#define WM8776_ADC_MUX_AIN2 (1 << 1)
+#define WM8776_ADC_MUX_AIN3 (1 << 2)
+#define WM8776_ADC_MUX_AIN4 (1 << 3)
+#define WM8776_ADC_MUX_AIN5 (1 << 4)
+#define WM8776_ADC_MUTER (1 << 6)
+#define WM8776_ADC_MUTEL (1 << 7)
+#define WM8776_ADC_LRBOTH (1 << 8)
+#define WM8776_REG_OUTMUX 0x16
+#define WM8776_OUTMUX_DAC (1 << 0)
+#define WM8776_OUTMUX_AUX (1 << 1)
+#define WM8776_OUTMUX_BYPASS (1 << 2)
+#define WM8776_REG_RESET 0x17
+
+#define WM8776_REG_COUNT 0x17 /* don't cache the RESET register */
+
+struct snd_wm8776;
+
+struct snd_wm8776_ops {
+ void (*write)(struct snd_wm8776 *wm, u8 addr, u8 data);
+};
+
+enum snd_wm8776_ctl_id {
+ WM8776_CTL_DAC_VOL,
+ WM8776_CTL_DAC_SW,
+ WM8776_CTL_DAC_ZC_SW,
+ WM8776_CTL_HP_VOL,
+ WM8776_CTL_HP_SW,
+ WM8776_CTL_HP_ZC_SW,
+ WM8776_CTL_AUX_SW,
+ WM8776_CTL_BYPASS_SW,
+ WM8776_CTL_DAC_IZD_SW,
+ WM8776_CTL_PHASE_SW,
+ WM8776_CTL_DEEMPH_SW,
+ WM8776_CTL_ADC_VOL,
+ WM8776_CTL_ADC_SW,
+ WM8776_CTL_INPUT1_SW,
+ WM8776_CTL_INPUT2_SW,
+ WM8776_CTL_INPUT3_SW,
+ WM8776_CTL_INPUT4_SW,
+ WM8776_CTL_INPUT5_SW,
+ WM8776_CTL_AGC_SEL,
+ WM8776_CTL_LIM_THR,
+ WM8776_CTL_LIM_ATK,
+ WM8776_CTL_LIM_DCY,
+ WM8776_CTL_LIM_TRANWIN,
+ WM8776_CTL_LIM_MAXATTN,
+ WM8776_CTL_ALC_TGT,
+ WM8776_CTL_ALC_ATK,
+ WM8776_CTL_ALC_DCY,
+ WM8776_CTL_ALC_MAXGAIN,
+ WM8776_CTL_ALC_MAXATTN,
+ WM8776_CTL_ALC_HLD,
+ WM8776_CTL_NGT_SW,
+ WM8776_CTL_NGT_THR,
+
+ WM8776_CTL_COUNT,
+};
+
+#define WM8776_ENUM_MAX 16
+
+#define WM8776_FLAG_STEREO (1 << 0)
+#define WM8776_FLAG_VOL_UPDATE (1 << 1)
+#define WM8776_FLAG_INVERT (1 << 2)
+#define WM8776_FLAG_LIM (1 << 3)
+#define WM8776_FLAG_ALC (1 << 4)
+
+struct snd_wm8776_ctl {
+ const char *name;
+ snd_ctl_elem_type_t type;
+ const char *const enum_names[WM8776_ENUM_MAX];
+ const unsigned int *tlv;
+ u16 reg1, reg2, mask1, mask2, min, max, flags;
+ void (*set)(struct snd_wm8776 *wm, u16 ch1, u16 ch2);
+ void (*get)(struct snd_wm8776 *wm, u16 *ch1, u16 *ch2);
+};
+
+enum snd_wm8776_agc_mode {
+ WM8776_AGC_OFF,
+ WM8776_AGC_LIM,
+ WM8776_AGC_ALC_R,
+ WM8776_AGC_ALC_L,
+ WM8776_AGC_ALC_STEREO
+};
+
+struct snd_wm8776 {
+ struct snd_card *card;
+ struct snd_wm8776_ctl ctl[WM8776_CTL_COUNT];
+ enum snd_wm8776_agc_mode agc_mode;
+ struct snd_wm8776_ops ops;
+ u16 regs[WM8776_REG_COUNT]; /* 9-bit registers */
+};
+
+
+
+void snd_wm8776_init(struct snd_wm8776 *wm);
+void snd_wm8776_resume(struct snd_wm8776 *wm);
+void snd_wm8776_set_power(struct snd_wm8776 *wm, u16 power);
+void snd_wm8776_volume_restore(struct snd_wm8776 *wm);
+int snd_wm8776_build_controls(struct snd_wm8776 *wm);
+
+#endif /* __SOUND_WM8776_H */
diff --git a/sound/pci/ice1712/wtm.c b/sound/pci/ice1712/wtm.c
index e618f789026e..57a79536e7ba 100644
--- a/sound/pci/ice1712/wtm.c
+++ b/sound/pci/ice1712/wtm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for ICEnsemble VT1724 (Envy24HT)
*
@@ -6,36 +7,27 @@
* Copyright (c) 2006 Guedez Clement <klem.dev@gmail.com>
* Some functions are taken from the Prodigy192 driver
* source
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <sound/core.h>
+#include <sound/tlv.h>
+#include <linux/slab.h>
#include "ice1712.h"
#include "envy24ht.h"
#include "wtm.h"
#include "stac946x.h"
+struct wtm_spec {
+ /* rate change needs atomic mute/unmute of all dacs*/
+ struct mutex mute_mutex;
+};
+
/*
* 2*ADC 6*DAC no1 ringbuffer r/w on i2c bus
@@ -69,15 +61,65 @@ static inline unsigned char stac9460_2_get(struct snd_ice1712 *ice, int reg)
/*
* DAC mute control
*/
+static void stac9460_dac_mute_all(struct snd_ice1712 *ice, unsigned char mute,
+ unsigned short int *change_mask)
+{
+ unsigned char new, old;
+ int id, idx, change;
+
+ /*stac9460 1*/
+ for (id = 0; id < 7; id++) {
+ if (*change_mask & (0x01 << id)) {
+ if (id == 0)
+ idx = STAC946X_MASTER_VOLUME;
+ else
+ idx = STAC946X_LF_VOLUME - 1 + id;
+ old = stac9460_get(ice, idx);
+ new = (~mute << 7 & 0x80) | (old & ~0x80);
+ change = (new != old);
+ if (change) {
+ stac9460_put(ice, idx, new);
+ *change_mask = *change_mask | (0x01 << id);
+ } else {
+ *change_mask = *change_mask & ~(0x01 << id);
+ }
+ }
+ }
+
+ /*stac9460 2*/
+ for (id = 0; id < 3; id++) {
+ if (*change_mask & (0x01 << (id + 7))) {
+ if (id == 0)
+ idx = STAC946X_MASTER_VOLUME;
+ else
+ idx = STAC946X_LF_VOLUME - 1 + id;
+ old = stac9460_2_get(ice, idx);
+ new = (~mute << 7 & 0x80) | (old & ~0x80);
+ change = (new != old);
+ if (change) {
+ stac9460_2_put(ice, idx, new);
+ *change_mask = *change_mask | (0x01 << id);
+ } else {
+ *change_mask = *change_mask & ~(0x01 << id);
+ }
+ }
+ }
+}
+
+
+
#define stac9460_dac_mute_info snd_ctl_boolean_mono_info
static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+ struct wtm_spec *spec = ice->spec;
unsigned char val;
int idx, id;
+ guard(mutex)(&spec->mute_mutex);
+
if (kcontrol->private_value) {
idx = STAC946X_MASTER_VOLUME;
id = 0;
@@ -90,6 +132,7 @@ static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol,
else
val = stac9460_2_get(ice, idx - 6);
ucontrol->value.integer.value[0] = (~val >> 7) & 0x1;
+
return 0;
}
@@ -339,8 +382,14 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,
/*
* MIC / LINE switch fonction
*/
+static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char * const texts[2] = { "Line In", "Mic" };
+
+ return snd_ctl_enum_info(uinfo, 1, 2, texts);
+}
-#define stac9460_mic_sw_info snd_ctl_boolean_mono_info
static int stac9460_mic_sw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -354,7 +403,7 @@ static int stac9460_mic_sw_get(struct snd_kcontrol *kcontrol,
val = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
else
val = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
- ucontrol->value.integer.value[0] = ~val>>7 & 0x1;
+ ucontrol->value.enumerated.item[0] = (val >> 7) & 0x1;
return 0;
}
@@ -370,7 +419,7 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
old = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);
else
old = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);
- new = (~ucontrol->value.integer.value[0] << 7 & 0x80) | (old & ~0x80);
+ new = (ucontrol->value.enumerated.item[0] << 7 & 0x80) | (old & ~0x80);
change = (new != old);
if (change) {
if (id == 0)
@@ -381,17 +430,62 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,
return change;
}
+
+/*
+ * Handler for setting correct codec rate - called when rate change is detected
+ */
+static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate)
+{
+ unsigned char old, new;
+ unsigned short int changed;
+ struct wtm_spec *spec = ice->spec;
+
+ if (rate == 0) /* no hint - S/PDIF input is master, simply return */
+ return;
+ else if (rate <= 48000)
+ new = 0x08; /* 256x, base rate mode */
+ else if (rate <= 96000)
+ new = 0x11; /* 256x, mid rate mode */
+ else
+ new = 0x12; /* 128x, high rate mode */
+
+ old = stac9460_get(ice, STAC946X_MASTER_CLOCKING);
+ if (old == new)
+ return;
+ /* change detected, setting master clock, muting first */
+ /* due to possible conflicts with mute controls - mutexing */
+ guard(mutex)(&spec->mute_mutex);
+ /* we have to remember current mute status for each DAC */
+ changed = 0xFFFF;
+ stac9460_dac_mute_all(ice, 0, &changed);
+ /*printk(KERN_DEBUG "Rate change: %d, new MC: 0x%02x\n", rate, new);*/
+ stac9460_put(ice, STAC946X_MASTER_CLOCKING, new);
+ stac9460_2_put(ice, STAC946X_MASTER_CLOCKING, new);
+ udelay(10);
+ /* unmuting - only originally unmuted dacs -
+ * i.e. those changed when muting */
+ stac9460_dac_mute_all(ice, 1, &changed);
+}
+
+
+/*Limits value in dB for fader*/
+static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0);
+
/*
* Control tabs
*/
-static struct snd_kcontrol_new stac9640_controls[] __devinitdata = {
+static const struct snd_kcontrol_new stac9640_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
.name = "Master Playback Switch",
.info = stac9460_dac_mute_info,
.get = stac9460_dac_mute_get,
.put = stac9460_dac_mute_put,
- .private_value = 1
+ .private_value = 1,
+ .tlv = { .p = db_scale_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -403,7 +497,7 @@ static struct snd_kcontrol_new stac9640_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "MIC/Line switch",
+ .name = "MIC/Line Input Enum",
.count = 2,
.info = stac9460_mic_sw_info,
.get = stac9460_mic_sw_get,
@@ -420,11 +514,15 @@ static struct snd_kcontrol_new stac9640_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+
.name = "DAC Volume",
.count = 8,
.info = stac9460_dac_vol_info,
.get = stac9460_dac_vol_get,
.put = stac9460_dac_vol_put,
+ .tlv = { .p = db_scale_dac }
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -436,19 +534,22 @@ static struct snd_kcontrol_new stac9640_controls[] __devinitdata = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+
.name = "ADC Volume",
.count = 2,
.info = stac9460_adc_vol_info,
.get = stac9460_adc_vol_get,
.put = stac9460_adc_vol_put,
-
+ .tlv = { .p = db_scale_adc }
}
};
/*INIT*/
-static int __devinit wtm_add_controls(struct snd_ice1712 *ice)
+static int wtm_add_controls(struct snd_ice1712 *ice)
{
unsigned int i;
int err;
@@ -462,48 +563,60 @@ static int __devinit wtm_add_controls(struct snd_ice1712 *ice)
return 0;
}
-static int __devinit wtm_init(struct snd_ice1712 *ice)
+static int wtm_init(struct snd_ice1712 *ice)
{
- static unsigned short stac_inits_prodigy[] = {
+ static const unsigned short stac_inits_wtm[] = {
STAC946X_RESET, 0,
+ STAC946X_MASTER_CLOCKING, 0x11,
(unsigned short)-1
};
- unsigned short *p;
+ const unsigned short *p;
+ struct wtm_spec *spec;
/*WTM 192M*/
ice->num_total_dacs = 8;
ice->num_total_adcs = 4;
ice->force_rdma1 = 1;
+ /*init mutex for dac mute conflict*/
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ ice->spec = spec;
+ mutex_init(&spec->mute_mutex);
+
+
/*initialize codec*/
- p = stac_inits_prodigy;
+ p = stac_inits_wtm;
for (; *p != (unsigned short)-1; p += 2) {
stac9460_put(ice, p[0], p[1]);
stac9460_2_put(ice, p[0], p[1]);
}
+ ice->gpio.set_pro_rate = stac9460_set_rate_val;
return 0;
}
-static unsigned char wtm_eeprom[] __devinitdata = {
- 0x47, /*SYSCONF: clock 192KHz, 4ADC, 8DAC */
- 0x80, /* ACLINK : I2S */
- 0xf8, /* I2S: vol; 96k, 24bit, 192k */
- 0xc1 /*SPDIF: out-en, spidf ext out*/,
- 0x9f, /* GPIO_DIR */
- 0xff, /* GPIO_DIR1 */
- 0x7f, /* GPIO_DIR2 */
- 0x9f, /* GPIO_MASK */
- 0xff, /* GPIO_MASK1 */
- 0x7f, /* GPIO_MASK2 */
- 0x16, /* GPIO_STATE */
- 0x80, /* GPIO_STATE1 */
- 0x00, /* GPIO_STATE2 */
+static const unsigned char wtm_eeprom[] = {
+ [ICE_EEP2_SYSCONF] = 0x67, /*SYSCONF: clock 192KHz, mpu401,
+ 4ADC, 8DAC */
+ [ICE_EEP2_ACLINK] = 0x80, /* ACLINK : I2S */
+ [ICE_EEP2_I2S] = 0xf8, /* I2S: vol; 96k, 24bit, 192k */
+ [ICE_EEP2_SPDIF] = 0xc1, /*SPDIF: out-en, spidf ext out*/
+ [ICE_EEP2_GPIO_DIR] = 0x9f,
+ [ICE_EEP2_GPIO_DIR1] = 0xff,
+ [ICE_EEP2_GPIO_DIR2] = 0x7f,
+ [ICE_EEP2_GPIO_MASK] = 0x9f,
+ [ICE_EEP2_GPIO_MASK1] = 0xff,
+ [ICE_EEP2_GPIO_MASK2] = 0x7f,
+ [ICE_EEP2_GPIO_STATE] = 0x16,
+ [ICE_EEP2_GPIO_STATE1] = 0x80,
+ [ICE_EEP2_GPIO_STATE2] = 0x00,
};
/*entry point*/
-struct snd_ice1712_card_info snd_vt1724_wtm_cards[] __devinitdata = {
+struct snd_ice1712_card_info snd_vt1724_wtm_cards[] = {
{
.subvendor = VT1724_SUBDEVICE_WTM,
.name = "ESI Waveterminal 192M",
diff --git a/sound/pci/ice1712/wtm.h b/sound/pci/ice1712/wtm.h
index 423c1a204c0b..1cfcbde15f42 100644
--- a/sound/pci/ice1712/wtm.h
+++ b/sound/pci/ice1712/wtm.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __SOUND_WTM_H
#define __SOUND_WTM_H
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 400f9ebd243e..3b53c5e63c29 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -1,102 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA driver for Intel ICH (i8x0) chipsets
*
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
*
- *
* This code also contains alpha support for SiS 735 chipsets provided
* by Mike Pieper <mptei@users.sourceforge.net>. We have no datasheet
* for SiS735, so the code is not fully functional.
*
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/info.h>
#include <sound/initval.h>
-/* for 440MX workaround */
-#include <asm/pgtable.h>
-#include <asm/cacheflush.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Intel,82801AA-ICH},"
- "{Intel,82901AB-ICH0},"
- "{Intel,82801BA-ICH2},"
- "{Intel,82801CA-ICH3},"
- "{Intel,82801DB-ICH4},"
- "{Intel,ICH5},"
- "{Intel,ICH6},"
- "{Intel,ICH7},"
- "{Intel,6300ESB},"
- "{Intel,ESB2},"
- "{Intel,MX440},"
- "{SiS,SI7012},"
- "{NVidia,nForce Audio},"
- "{NVidia,nForce2 Audio},"
- "{NVidia,nForce3 Audio},"
- "{NVidia,MCP04},"
- "{NVidia,MCP501},"
- "{NVidia,CK804},"
- "{NVidia,CK8},"
- "{NVidia,CK8S},"
- "{AMD,AMD768},"
- "{AMD,AMD8111},"
- "{ALI,M5455}}");
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
static int ac97_clock;
static char *ac97_quirk;
-static int buggy_semaphore;
+static bool buggy_semaphore;
static int buggy_irq = -1; /* auto-check */
-static int xbox;
+static bool xbox;
static int spdif_aclink = -1;
+static int inside_vm = -1;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for Intel i8x0 soundcard.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for Intel i8x0 soundcard.");
module_param(ac97_clock, int, 0444);
-MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = whitelist + auto-detect, 1 = force autodetect).");
+MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = allowlist + auto-detect, 1 = force autodetect).");
module_param(ac97_quirk, charp, 0444);
MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");
module_param(buggy_semaphore, bool, 0444);
MODULE_PARM_DESC(buggy_semaphore, "Enable workaround for hardwares with problematic codec semaphores.");
-module_param(buggy_irq, bool, 0444);
+module_param(buggy_irq, bint, 0444);
MODULE_PARM_DESC(buggy_irq, "Enable workaround for buggy interrupts on some motherboards.");
module_param(xbox, bool, 0444);
MODULE_PARM_DESC(xbox, "Set to 1 for Xbox, if you have problems with the AC'97 codec detection.");
module_param(spdif_aclink, int, 0444);
MODULE_PARM_DESC(spdif_aclink, "S/PDIF over AC-link.");
+module_param(inside_vm, bint, 0444);
+MODULE_PARM_DESC(inside_vm, "KVM/Parallels optimization.");
/* just for backward compatibility */
-static int enable;
+static bool enable;
module_param(enable, bool, 0444);
static int joystick;
module_param(joystick, int, 0444);
@@ -117,7 +79,7 @@ enum { \
ICH_REG_##name##_PICB = base + 0x08, /* word - position in current buffer */ \
ICH_REG_##name##_PIV = base + 0x0a, /* byte - prefetched index value */ \
ICH_REG_##name##_CR = base + 0x0b, /* byte - control register */ \
-};
+}
/* busmaster blocks */
DEFINE_REGSET(OFF, 0); /* offset */
@@ -346,7 +308,7 @@ enum {
struct ichdev {
unsigned int ichd; /* ich device number */
unsigned long reg_offset; /* offset to bmaddr */
- u32 *bdbar; /* CPU address (32bit) */
+ __le32 *bdbar; /* CPU address (32bit) */
unsigned int bdbar_addr; /* PCI bus address (32bit) */
struct snd_pcm_substream *substream;
unsigned int physbuf; /* physical address (32bit) */
@@ -369,7 +331,7 @@ struct ichdev {
unsigned int ali_slot; /* ALI DMA slot */
struct ac97_pcm *pcm;
int pcm_open_flag;
- unsigned int page_attr_changed: 1;
+ unsigned int prepared:1;
unsigned int suspended: 1;
};
@@ -400,6 +362,7 @@ struct intel8x0 {
unsigned buggy_irq: 1; /* workaround for buggy mobos */
unsigned xbox: 1; /* workaround for Xbox AC'97 detection */
unsigned buggy_semaphore: 1; /* workaround for buggy codec semaphore */
+ unsigned inside_vm: 1; /* enable VM optimization */
int spdif_idx; /* SPDIF BAR index; *_SPBAR or -1 if use PCMOUT */
unsigned int sdm_saved; /* SDM reg value */
@@ -408,19 +371,19 @@ struct intel8x0 {
struct snd_ac97 *ac97[3];
unsigned int ac97_sdin[3];
unsigned int max_codecs, ncodecs;
- unsigned int *codec_bit;
+ const unsigned int *codec_bit;
unsigned int codec_isr_bits;
unsigned int codec_ready_bits;
spinlock_t reg_lock;
u32 bdbars_count;
- struct snd_dma_buffer bdbars;
+ struct snd_dma_buffer *bdbars;
u32 int_sta_reg; /* interrupt status register */
u32 int_sta_mask; /* interrupt status mask */
};
-static DEFINE_PCI_DEVICE_TABLE(snd_intel8x0_ids) = {
+static const struct pci_device_id snd_intel8x0_ids[] = {
{ PCI_VDEVICE(INTEL, 0x2415), DEVICE_INTEL }, /* 82801AA */
{ PCI_VDEVICE(INTEL, 0x2425), DEVICE_INTEL }, /* 82901AB */
{ PCI_VDEVICE(INTEL, 0x2445), DEVICE_INTEL }, /* 82801BA */
@@ -534,10 +497,11 @@ static int snd_intel8x0_codec_semaphore(struct intel8x0 *chip, unsigned int code
udelay(10);
} while (time--);
- /* access to some forbidden (non existant) ac97 registers will not
+ /* access to some forbidden (non existent) ac97 registers will not
* reset the semaphore. So even if you don't get the semaphore, still
* continue the access. We don't need the semaphore anyway. */
- snd_printk(KERN_ERR "codec_semaphore: semaphore is not ready [0x%x][0x%x]\n",
+ dev_err(chip->card->dev,
+ "codec_semaphore: semaphore is not ready [0x%x][0x%x]\n",
igetbyte(chip, ICHREG(ACC_SEMA)), igetdword(chip, ICHREG(GLOB_STA)));
iagetword(chip, 0); /* clear semaphore flag */
/* I don't care about the semaphore */
@@ -552,7 +516,9 @@ static void snd_intel8x0_codec_write(struct snd_ac97 *ac97,
if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) {
if (! chip->in_ac97_init)
- snd_printk(KERN_ERR "codec_write %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
+ dev_err(chip->card->dev,
+ "codec_write %d: semaphore is not ready for register 0x%x\n",
+ ac97->num, reg);
}
iaputword(chip, reg + ac97->num * 0x80, val);
}
@@ -566,30 +532,36 @@ static unsigned short snd_intel8x0_codec_read(struct snd_ac97 *ac97,
if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) {
if (! chip->in_ac97_init)
- snd_printk(KERN_ERR "codec_read %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
+ dev_err(chip->card->dev,
+ "codec_read %d: semaphore is not ready for register 0x%x\n",
+ ac97->num, reg);
res = 0xffff;
} else {
res = iagetword(chip, reg + ac97->num * 0x80);
- if ((tmp = igetdword(chip, ICHREG(GLOB_STA))) & ICH_RCS) {
+ tmp = igetdword(chip, ICHREG(GLOB_STA));
+ if (tmp & ICH_RCS) {
/* reset RCS and preserve other R/WC bits */
iputdword(chip, ICHREG(GLOB_STA), tmp &
~(chip->codec_ready_bits | ICH_GSCI));
if (! chip->in_ac97_init)
- snd_printk(KERN_ERR "codec_read %d: read timeout for register 0x%x\n", ac97->num, reg);
+ dev_err(chip->card->dev,
+ "codec_read %d: read timeout for register 0x%x\n",
+ ac97->num, reg);
res = 0xffff;
}
}
return res;
}
-static void __devinit snd_intel8x0_codec_read_test(struct intel8x0 *chip,
- unsigned int codec)
+static void snd_intel8x0_codec_read_test(struct intel8x0 *chip,
+ unsigned int codec)
{
unsigned int tmp;
if (snd_intel8x0_codec_semaphore(chip, codec) >= 0) {
iagetword(chip, codec * 0x80);
- if ((tmp = igetdword(chip, ICHREG(GLOB_STA))) & ICH_RCS) {
+ tmp = igetdword(chip, ICHREG(GLOB_STA));
+ if (tmp & ICH_RCS) {
/* reset RCS and preserve other R/WC bits */
iputdword(chip, ICHREG(GLOB_STA), tmp &
~(chip->codec_ready_bits | ICH_GSCI));
@@ -609,7 +581,7 @@ static int snd_intel8x0_ali_codec_ready(struct intel8x0 *chip, int mask)
return 0;
}
if (! chip->in_ac97_init)
- snd_printd(KERN_WARNING "intel8x0: AC97 codec ready timeout.\n");
+ dev_warn(chip->card->dev, "AC97 codec ready timeout.\n");
return -EBUSY;
}
@@ -621,7 +593,7 @@ static int snd_intel8x0_ali_codec_semaphore(struct intel8x0 *chip)
while (--time && (igetdword(chip, ICHREG(ALI_CAS)) & ALI_CAS_SEM_BUSY))
udelay(1);
if (! time && ! chip->in_ac97_init)
- snd_printk(KERN_WARNING "ali_codec_semaphore timeout\n");
+ dev_warn(chip->card->dev, "ali_codec_semaphore timeout\n");
return snd_intel8x0_ali_codec_ready(chip, ALI_CSPSR_CODEC_READY);
}
@@ -664,7 +636,7 @@ static void snd_intel8x0_ali_codec_write(struct snd_ac97 *ac97, unsigned short r
static void snd_intel8x0_setup_periods(struct intel8x0 *chip, struct ichdev *ichdev)
{
int idx;
- u32 *bdbar = ichdev->bdbar;
+ __le32 *bdbar = ichdev->bdbar;
unsigned long port = ichdev->reg_offset;
iputdword(chip, port + ICH_REG_OFF_BDBAR, ichdev->bdbar_addr);
@@ -690,7 +662,7 @@ static void snd_intel8x0_setup_periods(struct intel8x0 *chip, struct ichdev *ich
bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */
ichdev->fragsize >> ichdev->pos_shift);
#if 0
- printk(KERN_DEBUG "bdbar[%i] = 0x%x [0x%x]\n",
+ dev_dbg(chip->card->dev, "bdbar[%i] = 0x%x [0x%x]\n",
idx + 0, bdbar[idx + 0], bdbar[idx + 1]);
#endif
}
@@ -702,8 +674,8 @@ static void snd_intel8x0_setup_periods(struct intel8x0 *chip, struct ichdev *ich
ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags;
ichdev->position = 0;
#if 0
- printk(KERN_DEBUG "lvi_frag = %i, frags = %i, period_size = 0x%x, "
- "period_size1 = 0x%x\n",
+ dev_dbg(chip->card->dev,
+ "lvi_frag = %i, frags = %i, period_size = 0x%x, period_size1 = 0x%x\n",
ichdev->lvi_frag, ichdev->frags, ichdev->fragsize,
ichdev->fragsize1);
#endif
@@ -711,25 +683,6 @@ static void snd_intel8x0_setup_periods(struct intel8x0 *chip, struct ichdev *ich
iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
}
-#ifdef __i386__
-/*
- * Intel 82443MX running a 100MHz processor system bus has a hardware bug,
- * which aborts PCI busmaster for audio transfer. A workaround is to set
- * the pages as non-cached. For details, see the errata in
- * http://download.intel.com/design/chipsets/specupdt/24505108.pdf
- */
-static void fill_nocache(void *buf, int size, int nocache)
-{
- size = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
- if (nocache)
- set_pages_uc(virt_to_page(buf), size);
- else
- set_pages_wb(virt_to_page(buf), size);
-}
-#else
-#define fill_nocache(buf, size, nocache) do { ; } while (0)
-#endif
-
/*
* Interrupt handler
*/
@@ -737,52 +690,51 @@ static void fill_nocache(void *buf, int size, int nocache)
static inline void snd_intel8x0_update(struct intel8x0 *chip, struct ichdev *ichdev)
{
unsigned long port = ichdev->reg_offset;
- unsigned long flags;
int status, civ, i, step;
int ack = 0;
- spin_lock_irqsave(&chip->reg_lock, flags);
- status = igetbyte(chip, port + ichdev->roff_sr);
- civ = igetbyte(chip, port + ICH_REG_OFF_CIV);
- if (!(status & ICH_BCIS)) {
- step = 0;
- } else if (civ == ichdev->civ) {
- // snd_printd("civ same %d\n", civ);
- step = 1;
- ichdev->civ++;
- ichdev->civ &= ICH_REG_LVI_MASK;
- } else {
- step = civ - ichdev->civ;
- if (step < 0)
- step += ICH_REG_LVI_MASK + 1;
- // if (step != 1)
- // snd_printd("step = %d, %d -> %d\n", step, ichdev->civ, civ);
- ichdev->civ = civ;
- }
-
- ichdev->position += step * ichdev->fragsize1;
- if (! chip->in_measurement)
- ichdev->position %= ichdev->size;
- ichdev->lvi += step;
- ichdev->lvi &= ICH_REG_LVI_MASK;
- iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi);
- for (i = 0; i < step; i++) {
- ichdev->lvi_frag++;
- ichdev->lvi_frag %= ichdev->frags;
- ichdev->bdbar[ichdev->lvi * 2] = cpu_to_le32(ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1);
+ if (!(ichdev->prepared || chip->in_measurement) || ichdev->suspended)
+ return;
+
+ scoped_guard(spinlock_irqsave, &chip->reg_lock) {
+ status = igetbyte(chip, port + ichdev->roff_sr);
+ civ = igetbyte(chip, port + ICH_REG_OFF_CIV);
+ if (!(status & ICH_BCIS)) {
+ step = 0;
+ } else if (civ == ichdev->civ) {
+ step = 1;
+ ichdev->civ++;
+ ichdev->civ &= ICH_REG_LVI_MASK;
+ } else {
+ step = civ - ichdev->civ;
+ if (step < 0)
+ step += ICH_REG_LVI_MASK + 1;
+ ichdev->civ = civ;
+ }
+
+ ichdev->position += step * ichdev->fragsize1;
+ if (! chip->in_measurement)
+ ichdev->position %= ichdev->size;
+ ichdev->lvi += step;
+ ichdev->lvi &= ICH_REG_LVI_MASK;
+ iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi);
+ for (i = 0; i < step; i++) {
+ ichdev->lvi_frag++;
+ ichdev->lvi_frag %= ichdev->frags;
+ ichdev->bdbar[ichdev->lvi * 2] = cpu_to_le32(ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1);
#if 0
- printk(KERN_DEBUG "new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, "
- "all = 0x%x, 0x%x\n",
- ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2],
- ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_OFF_PIV + port),
- inl(port + 4), inb(port + ICH_REG_OFF_CR));
+ dev_dbg(chip->card->dev,
+ "new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n",
+ ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2],
+ ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_OFF_PIV + port),
+ inl(port + 4), inb(port + ICH_REG_OFF_CR));
#endif
- if (--ichdev->ack == 0) {
- ichdev->ack = ichdev->ack_reload;
- ack = 1;
+ if (--ichdev->ack == 0) {
+ ichdev->ack = ichdev->ack_reload;
+ ack = 1;
+ }
}
}
- spin_unlock_irqrestore(&chip->reg_lock, flags);
if (ack && ichdev->substream) {
snd_pcm_period_elapsed(ichdev->substream);
}
@@ -837,7 +789,7 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd
switch (cmd) {
case SNDRV_PCM_TRIGGER_RESUME:
ichdev->suspended = 0;
- /* fallthru */
+ fallthrough;
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
val = ICH_IOCE | ICH_STARTBM;
@@ -845,7 +797,7 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
ichdev->suspended = 1;
- /* fallthru */
+ fallthrough;
case SNDRV_PCM_TRIGGER_STOP:
val = 0;
break;
@@ -870,7 +822,7 @@ static int snd_intel8x0_ali_trigger(struct snd_pcm_substream *substream, int cmd
struct intel8x0 *chip = snd_pcm_substream_chip(substream);
struct ichdev *ichdev = get_ichdev(substream);
unsigned long port = ichdev->reg_offset;
- static int fiforeg[] = {
+ static const int fiforeg[] = {
ICHREG(ALI_FIFOCR1), ICHREG(ALI_FIFOCR2), ICHREG(ALI_FIFOCR3)
};
unsigned int val, fifo;
@@ -879,7 +831,7 @@ static int snd_intel8x0_ali_trigger(struct snd_pcm_substream *substream, int cmd
switch (cmd) {
case SNDRV_PCM_TRIGGER_RESUME:
ichdev->suspended = 0;
- /* fallthru */
+ fallthrough;
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -896,7 +848,7 @@ static int snd_intel8x0_ali_trigger(struct snd_pcm_substream *substream, int cmd
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
ichdev->suspended = 1;
- /* fallthru */
+ fallthrough;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
/* pause */
@@ -925,26 +877,13 @@ static int snd_intel8x0_hw_params(struct snd_pcm_substream *substream,
{
struct intel8x0 *chip = snd_pcm_substream_chip(substream);
struct ichdev *ichdev = get_ichdev(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
int dbl = params_rate(hw_params) > 48000;
int err;
- if (chip->fix_nocache && ichdev->page_attr_changed) {
- fill_nocache(runtime->dma_area, runtime->dma_bytes, 0); /* clear */
- ichdev->page_attr_changed = 0;
- }
- err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
- if (chip->fix_nocache) {
- if (runtime->dma_area && ! ichdev->page_attr_changed) {
- fill_nocache(runtime->dma_area, runtime->dma_bytes, 1);
- ichdev->page_attr_changed = 1;
- }
- }
if (ichdev->pcm_open_flag) {
snd_ac97_pcm_close(ichdev->pcm);
ichdev->pcm_open_flag = 0;
+ ichdev->prepared = 0;
}
err = snd_ac97_pcm_open(ichdev->pcm, params_rate(hw_params),
params_channels(hw_params),
@@ -961,18 +900,14 @@ static int snd_intel8x0_hw_params(struct snd_pcm_substream *substream,
static int snd_intel8x0_hw_free(struct snd_pcm_substream *substream)
{
- struct intel8x0 *chip = snd_pcm_substream_chip(substream);
struct ichdev *ichdev = get_ichdev(substream);
if (ichdev->pcm_open_flag) {
snd_ac97_pcm_close(ichdev->pcm);
ichdev->pcm_open_flag = 0;
+ ichdev->prepared = 0;
}
- if (chip->fix_nocache && ichdev->page_attr_changed) {
- fill_nocache(substream->runtime->dma_area, substream->runtime->dma_bytes, 0);
- ichdev->page_attr_changed = 0;
- }
- return snd_pcm_lib_free_pages(substream);
+ return 0;
}
static void snd_intel8x0_setup_pcm_out(struct intel8x0 *chip,
@@ -981,7 +916,7 @@ static void snd_intel8x0_setup_pcm_out(struct intel8x0 *chip,
unsigned int cnt;
int dbl = runtime->rate > 48000;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
switch (chip->device_type) {
case DEVICE_ALI:
cnt = igetdword(chip, ICHREG(ALI_SCR));
@@ -1027,7 +962,6 @@ static void snd_intel8x0_setup_pcm_out(struct intel8x0 *chip,
iputdword(chip, ICHREG(GLOB_CNT), cnt);
break;
}
- spin_unlock_irq(&chip->reg_lock);
}
static int snd_intel8x0_pcm_prepare(struct snd_pcm_substream *substream)
@@ -1045,6 +979,7 @@ static int snd_intel8x0_pcm_prepare(struct snd_pcm_substream *substream)
ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1;
}
snd_intel8x0_setup_periods(chip, ichdev);
+ ichdev->prepared = 1;
return 0;
}
@@ -1056,7 +991,7 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *subs
int civ, timeout = 10;
unsigned int position;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
do {
civ = igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV);
ptr1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
@@ -1065,8 +1000,18 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *subs
udelay(10);
continue;
}
- if (civ == igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV) &&
- ptr1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))
+ if (civ != igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV))
+ continue;
+
+ /* IO read operation is very expensive inside virtual machine
+ * as it is emulated. The probability that subsequent PICB read
+ * will return different result is high enough to loop till
+ * timeout here.
+ * Same CIV is strict enough condition to be sure that PICB
+ * is valid inside VM on emulated card. */
+ if (chip->inside_vm)
+ break;
+ if (ptr1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))
break;
} while (timeout--);
ptr = ichdev->last_pos;
@@ -1086,13 +1031,12 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *subs
}
}
ichdev->last_pos = ptr;
- spin_unlock(&chip->reg_lock);
if (ptr >= ichdev->size)
return 0;
return bytes_to_frames(substream->runtime, ptr);
}
-static struct snd_pcm_hardware snd_intel8x0_stream =
+static const struct snd_pcm_hardware snd_intel8x0_stream =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1113,31 +1057,31 @@ static struct snd_pcm_hardware snd_intel8x0_stream =
.fifo_size = 0,
};
-static unsigned int channels4[] = {
+static const unsigned int channels4[] = {
2, 4,
};
-static struct snd_pcm_hw_constraint_list hw_constraints_channels4 = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_channels4 = {
.count = ARRAY_SIZE(channels4),
.list = channels4,
.mask = 0,
};
-static unsigned int channels6[] = {
+static const unsigned int channels6[] = {
2, 4, 6,
};
-static struct snd_pcm_hw_constraint_list hw_constraints_channels6 = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_channels6 = {
.count = ARRAY_SIZE(channels6),
.list = channels6,
.mask = 0,
};
-static unsigned int channels8[] = {
+static const unsigned int channels8[] = {
2, 4, 6, 8,
};
-static struct snd_pcm_hw_constraint_list hw_constraints_channels8 = {
+static const struct snd_pcm_hw_constraint_list hw_constraints_channels8 = {
.count = ARRAY_SIZE(channels8),
.list = channels8,
.mask = 0,
@@ -1157,7 +1101,8 @@ static int snd_intel8x0_pcm_open(struct snd_pcm_substream *substream, struct ich
runtime->hw.buffer_bytes_max = 64*1024;
runtime->hw.period_bytes_max = 64*1024;
}
- if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+ err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
return err;
runtime->private_data = ichdev;
return 0;
@@ -1287,12 +1232,12 @@ static int snd_intel8x0_ali_ac97spdifout_open(struct snd_pcm_substream *substrea
struct intel8x0 *chip = snd_pcm_substream_chip(substream);
unsigned int val;
- spin_lock_irq(&chip->reg_lock);
- val = igetdword(chip, ICHREG(ALI_INTERFACECR));
- val |= ICH_ALI_IF_AC97SP;
- iputdword(chip, ICHREG(ALI_INTERFACECR), val);
- /* also needs to set ALI_SC_CODEC_SPDF correctly */
- spin_unlock_irq(&chip->reg_lock);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ val = igetdword(chip, ICHREG(ALI_INTERFACECR));
+ val |= ICH_ALI_IF_AC97SP;
+ iputdword(chip, ICHREG(ALI_INTERFACECR), val);
+ /* also needs to set ALI_SC_CODEC_SPDF correctly */
+ }
return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_AC97SPDIFOUT]);
}
@@ -1303,11 +1248,10 @@ static int snd_intel8x0_ali_ac97spdifout_close(struct snd_pcm_substream *substre
unsigned int val;
chip->ichd[ALID_AC97SPDIFOUT].substream = NULL;
- spin_lock_irq(&chip->reg_lock);
+ guard(spinlock_irq)(&chip->reg_lock);
val = igetdword(chip, ICHREG(ALI_INTERFACECR));
val &= ~ICH_ALI_IF_AC97SP;
iputdword(chip, ICHREG(ALI_INTERFACECR), val);
- spin_unlock_irq(&chip->reg_lock);
return 0;
}
@@ -1344,10 +1288,9 @@ static int snd_intel8x0_ali_spdifout_close(struct snd_pcm_substream *substream)
}
#endif
-static struct snd_pcm_ops snd_intel8x0_playback_ops = {
+static const struct snd_pcm_ops snd_intel8x0_playback_ops = {
.open = snd_intel8x0_playback_open,
.close = snd_intel8x0_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1355,10 +1298,9 @@ static struct snd_pcm_ops snd_intel8x0_playback_ops = {
.pointer = snd_intel8x0_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0_capture_ops = {
+static const struct snd_pcm_ops snd_intel8x0_capture_ops = {
.open = snd_intel8x0_capture_open,
.close = snd_intel8x0_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1366,10 +1308,9 @@ static struct snd_pcm_ops snd_intel8x0_capture_ops = {
.pointer = snd_intel8x0_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0_capture_mic_ops = {
+static const struct snd_pcm_ops snd_intel8x0_capture_mic_ops = {
.open = snd_intel8x0_mic_open,
.close = snd_intel8x0_mic_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1377,10 +1318,9 @@ static struct snd_pcm_ops snd_intel8x0_capture_mic_ops = {
.pointer = snd_intel8x0_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0_capture_mic2_ops = {
+static const struct snd_pcm_ops snd_intel8x0_capture_mic2_ops = {
.open = snd_intel8x0_mic2_open,
.close = snd_intel8x0_mic2_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1388,10 +1328,9 @@ static struct snd_pcm_ops snd_intel8x0_capture_mic2_ops = {
.pointer = snd_intel8x0_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0_capture2_ops = {
+static const struct snd_pcm_ops snd_intel8x0_capture2_ops = {
.open = snd_intel8x0_capture2_open,
.close = snd_intel8x0_capture2_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1399,10 +1338,9 @@ static struct snd_pcm_ops snd_intel8x0_capture2_ops = {
.pointer = snd_intel8x0_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0_spdif_ops = {
+static const struct snd_pcm_ops snd_intel8x0_spdif_ops = {
.open = snd_intel8x0_spdif_open,
.close = snd_intel8x0_spdif_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1410,10 +1348,9 @@ static struct snd_pcm_ops snd_intel8x0_spdif_ops = {
.pointer = snd_intel8x0_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0_ali_playback_ops = {
+static const struct snd_pcm_ops snd_intel8x0_ali_playback_ops = {
.open = snd_intel8x0_playback_open,
.close = snd_intel8x0_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1421,10 +1358,9 @@ static struct snd_pcm_ops snd_intel8x0_ali_playback_ops = {
.pointer = snd_intel8x0_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0_ali_capture_ops = {
+static const struct snd_pcm_ops snd_intel8x0_ali_capture_ops = {
.open = snd_intel8x0_capture_open,
.close = snd_intel8x0_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1432,10 +1368,9 @@ static struct snd_pcm_ops snd_intel8x0_ali_capture_ops = {
.pointer = snd_intel8x0_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0_ali_capture_mic_ops = {
+static const struct snd_pcm_ops snd_intel8x0_ali_capture_mic_ops = {
.open = snd_intel8x0_mic_open,
.close = snd_intel8x0_mic_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1443,10 +1378,9 @@ static struct snd_pcm_ops snd_intel8x0_ali_capture_mic_ops = {
.pointer = snd_intel8x0_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0_ali_ac97spdifout_ops = {
+static const struct snd_pcm_ops snd_intel8x0_ali_ac97spdifout_ops = {
.open = snd_intel8x0_ali_ac97spdifout_open,
.close = snd_intel8x0_ali_ac97spdifout_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1458,7 +1392,6 @@ static struct snd_pcm_ops snd_intel8x0_ali_ac97spdifout_ops = {
static struct snd_pcm_ops snd_intel8x0_ali_spdifin_ops = {
.open = snd_intel8x0_ali_spdifin_open,
.close = snd_intel8x0_ali_spdifin_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1469,7 +1402,6 @@ static struct snd_pcm_ops snd_intel8x0_ali_spdifin_ops = {
static struct snd_pcm_ops snd_intel8x0_ali_spdifout_ops = {
.open = snd_intel8x0_ali_spdifout_open,
.close = snd_intel8x0_ali_spdifout_close,
- .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_intel8x0_hw_params,
.hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0_pcm_prepare,
@@ -1480,15 +1412,18 @@ static struct snd_pcm_ops snd_intel8x0_ali_spdifout_ops = {
struct ich_pcm_table {
char *suffix;
- struct snd_pcm_ops *playback_ops;
- struct snd_pcm_ops *capture_ops;
+ const struct snd_pcm_ops *playback_ops;
+ const struct snd_pcm_ops *capture_ops;
size_t prealloc_size;
size_t prealloc_max_size;
int ac97_idx;
};
-static int __devinit snd_intel8x0_pcm1(struct intel8x0 *chip, int device,
- struct ich_pcm_table *rec)
+#define intel8x0_dma_type(chip) \
+ ((chip)->fix_nocache ? SNDRV_DMA_TYPE_DEV_WC : SNDRV_DMA_TYPE_DEV)
+
+static int snd_intel8x0_pcm1(struct intel8x0 *chip, int device,
+ const struct ich_pcm_table *rec)
{
struct snd_pcm *pcm;
int err;
@@ -1497,7 +1432,7 @@ static int __devinit snd_intel8x0_pcm1(struct intel8x0 *chip, int device,
if (rec->suffix)
sprintf(name, "Intel ICH - %s", rec->suffix);
else
- strcpy(name, "Intel ICH");
+ strscpy(name, "Intel ICH");
err = snd_pcm_new(chip->card, name, device,
rec->playback_ops ? 1 : 0,
rec->capture_ops ? 1 : 0, &pcm);
@@ -1514,17 +1449,36 @@ static int __devinit snd_intel8x0_pcm1(struct intel8x0 *chip, int device,
if (rec->suffix)
sprintf(pcm->name, "%s - %s", chip->card->shortname, rec->suffix);
else
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
chip->pcm[device] = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- rec->prealloc_size, rec->prealloc_max_size);
+ snd_pcm_set_managed_buffer_all(pcm, intel8x0_dma_type(chip),
+ &chip->pci->dev,
+ rec->prealloc_size, rec->prealloc_max_size);
+
+ if (rec->playback_ops &&
+ rec->playback_ops->open == snd_intel8x0_playback_open) {
+ struct snd_pcm_chmap *chmap;
+ int chs = 2;
+ if (chip->multi8)
+ chs = 8;
+ else if (chip->multi6)
+ chs = 6;
+ else if (chip->multi4)
+ chs = 4;
+ err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ snd_pcm_alt_chmaps, chs, 0,
+ &chmap);
+ if (err < 0)
+ return err;
+ chmap->channel_mask = SND_PCM_CHMAP_MASK_2468;
+ chip->ac97[0]->chmaps[SNDRV_PCM_STREAM_PLAYBACK] = chmap;
+ }
return 0;
}
-static struct ich_pcm_table intel_pcms[] __devinitdata = {
+static const struct ich_pcm_table intel_pcms[] = {
{
.playback_ops = &snd_intel8x0_playback_ops,
.capture_ops = &snd_intel8x0_capture_ops,
@@ -1561,7 +1515,7 @@ static struct ich_pcm_table intel_pcms[] __devinitdata = {
},
};
-static struct ich_pcm_table nforce_pcms[] __devinitdata = {
+static const struct ich_pcm_table nforce_pcms[] = {
{
.playback_ops = &snd_intel8x0_playback_ops,
.capture_ops = &snd_intel8x0_capture_ops,
@@ -1584,7 +1538,7 @@ static struct ich_pcm_table nforce_pcms[] __devinitdata = {
},
};
-static struct ich_pcm_table ali_pcms[] __devinitdata = {
+static const struct ich_pcm_table ali_pcms[] = {
{
.playback_ops = &snd_intel8x0_ali_playback_ops,
.capture_ops = &snd_intel8x0_ali_capture_ops,
@@ -1616,10 +1570,10 @@ static struct ich_pcm_table ali_pcms[] __devinitdata = {
#endif
};
-static int __devinit snd_intel8x0_pcm(struct intel8x0 *chip)
+static int snd_intel8x0_pcm(struct intel8x0 *chip)
{
int i, tblsize, device, err;
- struct ich_pcm_table *tbl, *rec;
+ const struct ich_pcm_table *tbl, *rec;
switch (chip->device_type) {
case DEVICE_INTEL_ICH4:
@@ -1679,7 +1633,7 @@ static void snd_intel8x0_mixer_free_ac97(struct snd_ac97 *ac97)
chip->ac97[ac97->num] = NULL;
}
-static struct ac97_pcm ac97_pcm_defs[] __devinitdata = {
+static const struct ac97_pcm ac97_pcm_defs[] = {
/* front PCM */
{
.exclusive = 1,
@@ -1749,7 +1703,7 @@ static struct ac97_pcm ac97_pcm_defs[] __devinitdata = {
},
};
-static struct ac97_quirk ac97_quirks[] __devinitdata = {
+static const struct ac97_quirk ac97_quirks[] = {
{
.subvendor = 0x0e11,
.subdevice = 0x000e,
@@ -1866,6 +1820,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {
},
{
.subvendor = 0x1028,
+ .subdevice = 0x0182,
+ .name = "Dell Latitude D610", /* STAC9750/51 */
+ .type = AC97_TUNE_HP_ONLY
+ },
+ {
+ .subvendor = 0x1028,
.subdevice = 0x0186,
.name = "Dell Latitude D810", /* cf. Malone #41015 */
.type = AC97_TUNE_HP_MUTE_LED
@@ -1878,6 +1838,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {
},
{
.subvendor = 0x1028,
+ .subdevice = 0x0189,
+ .name = "Dell Inspiron 9300",
+ .type = AC97_TUNE_HP_MUTE_LED
+ },
+ {
+ .subvendor = 0x1028,
.subdevice = 0x0191,
.name = "Dell Inspiron 8600",
.type = AC97_TUNE_HP_ONLY
@@ -2070,6 +2036,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {
},
{
.subvendor = 0x161f,
+ .subdevice = 0x202f,
+ .name = "Gateway M520",
+ .type = AC97_TUNE_INV_EAPD
+ },
+ {
+ .subvendor = 0x161f,
.subdevice = 0x203a,
.name = "Gateway 4525GZ", /* AD1981B */
.type = AC97_TUNE_INV_EAPD
@@ -2138,20 +2110,20 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {
{ } /* terminator */
};
-static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
- const char *quirk_override)
+static int snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
+ const char *quirk_override)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
int err;
unsigned int i, codecs;
unsigned int glob_sta = 0;
- struct snd_ac97_bus_ops *ops;
- static struct snd_ac97_bus_ops standard_bus_ops = {
+ const struct snd_ac97_bus_ops *ops;
+ static const struct snd_ac97_bus_ops standard_bus_ops = {
.write = snd_intel8x0_codec_write,
.read = snd_intel8x0_codec_read,
};
- static struct snd_ac97_bus_ops ali_bus_ops = {
+ static const struct snd_ac97_bus_ops ali_bus_ops = {
.write = snd_intel8x0_ali_codec_write,
.read = snd_intel8x0_ali_codec_read,
};
@@ -2168,7 +2140,7 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
case DEVICE_INTEL_ICH4:
chip->spdif_idx = ICHD_SPBAR;
break;
- };
+ }
}
chip->in_ac97_init = 1;
@@ -2214,7 +2186,8 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
udelay(1);
}
}
- if ((err = snd_ac97_bus(chip->card, 0, ops, chip, &pbus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, ops, chip, &pbus);
+ if (err < 0)
goto __err;
pbus->private_free = snd_intel8x0_mixer_free_ac97_bus;
if (ac97_clock >= 8000 && ac97_clock <= 48000)
@@ -2230,9 +2203,11 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
ac97.pci = chip->pci;
for (i = 0; i < codecs; i++) {
ac97.num = i;
- if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) {
+ err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i]);
+ if (err < 0) {
if (err != -EACCES)
- snd_printk(KERN_ERR "Unable to initialize codec #%d\n", i);
+ dev_err(chip->card->dev,
+ "Unable to initialize codec #%d\n", i);
if (i == 0)
goto __err;
}
@@ -2270,7 +2245,7 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
tmp |= chip->ac97_sdin[0] << ICH_DI1L_SHIFT;
for (i = 1; i < 4; i++) {
if (pcm->r[0].codec[i]) {
- tmp |= chip->ac97_sdin[pcm->r[0].codec[1]->num] << ICH_DI2L_SHIFT;
+ tmp |= chip->ac97_sdin[pcm->r[0].codec[i]->num] << ICH_DI2L_SHIFT;
break;
}
}
@@ -2335,7 +2310,7 @@ static void do_ali_reset(struct intel8x0 *chip)
}
#ifdef CONFIG_SND_AC97_POWER_SAVE
-static struct snd_pci_quirk ich_chip_reset_mode[] = {
+static const struct snd_pci_quirk ich_chip_reset_mode[] = {
SND_PCI_QUIRK(0x1014, 0x051f, "Thinkpad R32", 1),
{ } /* end */
};
@@ -2384,7 +2359,7 @@ static int snd_intel8x0_ich_chip_reset(struct intel8x0 *chip)
return 0;
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_ERR "AC'97 warm reset still in progress? [0x%x]\n",
+ dev_err(chip->card->dev, "AC'97 warm reset still in progress? [0x%x]\n",
igetdword(chip, ICHREG(GLOB_CNT)));
return -EIO;
}
@@ -2426,7 +2401,8 @@ static int snd_intel8x0_ich_chip_init(struct intel8x0 *chip, int probing)
} while (time_after_eq(end_time, jiffies));
if (! status) {
/* no codec is found */
- snd_printk(KERN_ERR "codec_ready: codec is not ready [0x%x]\n",
+ dev_err(chip->card->dev,
+ "codec_ready: codec is not ready [0x%x]\n",
igetdword(chip, ICHREG(GLOB_STA)));
return -EIO;
}
@@ -2490,7 +2466,7 @@ static int snd_intel8x0_ali_chip_init(struct intel8x0 *chip, int probing)
goto __ok;
schedule_timeout_uninterruptible(1);
}
- snd_printk(KERN_ERR "AC'97 reset failed.\n");
+ dev_err(chip->card->dev, "AC'97 reset failed.\n");
if (probing)
return -EIO;
@@ -2513,11 +2489,13 @@ static int snd_intel8x0_chip_init(struct intel8x0 *chip, int probing)
int err;
if (chip->device_type != DEVICE_ALI) {
- if ((err = snd_intel8x0_ich_chip_init(chip, probing)) < 0)
+ err = snd_intel8x0_ich_chip_init(chip, probing);
+ if (err < 0)
return err;
iagetword(chip, 0); /* clear semaphore flag */
} else {
- if ((err = snd_intel8x0_ali_chip_init(chip, probing)) < 0)
+ err = snd_intel8x0_ali_chip_init(chip, probing);
+ if (err < 0)
return err;
}
@@ -2534,7 +2512,7 @@ static int snd_intel8x0_chip_init(struct intel8x0 *chip, int probing)
break;
}
if (timeout == 0)
- printk(KERN_ERR "intel8x0: reset of registers failed?\n");
+ dev_err(chip->card->dev, "reset of registers failed?\n");
}
/* initialize Buffer Descriptor Lists */
for (i = 0; i < chip->bdbars_count; i++)
@@ -2543,8 +2521,9 @@ static int snd_intel8x0_chip_init(struct intel8x0 *chip, int probing)
return 0;
}
-static int snd_intel8x0_free(struct intel8x0 *chip)
+static void snd_intel8x0_free(struct snd_card *card)
{
+ struct intel8x0 *chip = card->private_data;
unsigned int i;
if (chip->irq < 0)
@@ -2567,45 +2546,18 @@ static int snd_intel8x0_free(struct intel8x0 *chip)
__hw_end:
if (chip->irq >= 0)
free_irq(chip->irq, chip);
- if (chip->bdbars.area) {
- if (chip->fix_nocache)
- fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 0);
- snd_dma_free_pages(&chip->bdbars);
- }
- if (chip->addr)
- pci_iounmap(chip->pci, chip->addr);
- if (chip->bmaddr)
- pci_iounmap(chip->pci, chip->bmaddr);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
}
-#ifdef CONFIG_PM
/*
* power management
*/
-static int intel8x0_suspend(struct pci_dev *pci, pm_message_t state)
+static int intel8x0_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct intel8x0 *chip = card->private_data;
int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- for (i = 0; i < chip->pcm_devs; i++)
- snd_pcm_suspend_all(chip->pcm[i]);
- /* clear nocache */
- if (chip->fix_nocache) {
- for (i = 0; i < chip->bdbars_count; i++) {
- struct ichdev *ichdev = &chip->ichd[i];
- if (ichdev->substream && ichdev->page_attr_changed) {
- struct snd_pcm_runtime *runtime = ichdev->substream->runtime;
- if (runtime->dma_area)
- fill_nocache(runtime->dma_area, runtime->dma_bytes, 0);
- }
- }
- }
for (i = 0; i < chip->ncodecs; i++)
snd_ac97_suspend(chip->ac97[i]);
if (chip->device_type == DEVICE_INTEL_ICH4)
@@ -2614,41 +2566,28 @@ static int intel8x0_suspend(struct pci_dev *pci, pm_message_t state)
if (chip->irq >= 0) {
free_irq(chip->irq, chip);
chip->irq = -1;
+ card->sync_irq = -1;
}
- pci_disable_device(pci);
- pci_save_state(pci);
- /* The call below may disable built-in speaker on some laptops
- * after S2RAM. So, don't touch it.
- */
- /* pci_set_power_state(pci, pci_choose_state(pci, state)); */
return 0;
}
-static int intel8x0_resume(struct pci_dev *pci)
+static int intel8x0_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct snd_card *card = dev_get_drvdata(dev);
struct intel8x0 *chip = card->private_data;
int i;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "intel8x0: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
snd_intel8x0_chip_init(chip, 0);
if (request_irq(pci->irq, snd_intel8x0_interrupt,
- IRQF_SHARED, card->shortname, chip)) {
- printk(KERN_ERR "intel8x0: unable to grab IRQ %d, "
- "disabling device\n", pci->irq);
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(dev, "unable to grab IRQ %d, disabling device\n",
+ pci->irq);
snd_card_disconnect(card);
return -EIO;
}
chip->irq = pci->irq;
- synchronize_irq(chip->irq);
+ card->sync_irq = chip->irq;
/* re-initialize mixer stuff */
if (chip->device_type == DEVICE_INTEL_ICH4 && !spdif_aclink) {
@@ -2660,25 +2599,9 @@ static int intel8x0_resume(struct pci_dev *pci)
ICH_PCM_SPDIF_1011);
}
- /* refill nocache */
- if (chip->fix_nocache)
- fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 1);
-
for (i = 0; i < chip->ncodecs; i++)
snd_ac97_resume(chip->ac97[i]);
- /* refill nocache */
- if (chip->fix_nocache) {
- for (i = 0; i < chip->bdbars_count; i++) {
- struct ichdev *ichdev = &chip->ichd[i];
- if (ichdev->substream && ichdev->page_attr_changed) {
- struct snd_pcm_runtime *runtime = ichdev->substream->runtime;
- if (runtime->dma_area)
- fill_nocache(runtime->dma_area, runtime->dma_bytes, 1);
- }
- }
- }
-
/* resume status */
for (i = 0; i < chip->bdbars_count; i++) {
struct ichdev *ichdev = &chip->ichd[i];
@@ -2696,26 +2619,30 @@ static int intel8x0_resume(struct pci_dev *pci)
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
+
+static DEFINE_SIMPLE_DEV_PM_OPS(intel8x0_pm, intel8x0_suspend, intel8x0_resume);
#define INTEL8X0_TESTBUF_SIZE 32768 /* enough large for one shot */
-static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
+static void intel8x0_measure_ac97_clock(struct intel8x0 *chip)
{
struct snd_pcm_substream *subs;
struct ichdev *ichdev;
unsigned long port;
unsigned long pos, pos1, t;
int civ, timeout = 1000, attempt = 1;
- struct timespec start_time, stop_time;
+ ktime_t start_time, stop_time;
if (chip->ac97_bus->clock != 48000)
return; /* specified in module option */
+ if (chip->inside_vm && !ac97_clock)
+ return; /* no measurement on VM */
__again:
subs = chip->pcm[0]->streams[0].substream;
if (! subs || subs->dma_buffer.bytes < INTEL8X0_TESTBUF_SIZE) {
- snd_printk(KERN_WARNING "no playback buffer allocated - aborting measure ac97 clock\n");
+ dev_warn(chip->card->dev,
+ "no playback buffer allocated - aborting measure ac97 clock\n");
return;
}
ichdev = &chip->ichd[ICHD_PCMOUT];
@@ -2725,61 +2652,63 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
/* set rate */
if (snd_ac97_set_rate(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 48000) < 0) {
- snd_printk(KERN_ERR "cannot set ac97 rate: clock = %d\n", chip->ac97_bus->clock);
+ dev_err(chip->card->dev, "cannot set ac97 rate: clock = %d\n",
+ chip->ac97_bus->clock);
return;
}
snd_intel8x0_setup_periods(chip, ichdev);
port = ichdev->reg_offset;
- spin_lock_irq(&chip->reg_lock);
- chip->in_measurement = 1;
- /* trigger */
- if (chip->device_type != DEVICE_ALI)
- iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE | ICH_STARTBM);
- else {
- iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);
- iputdword(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ chip->in_measurement = 1;
+ /* trigger */
+ if (chip->device_type != DEVICE_ALI)
+ iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE | ICH_STARTBM);
+ else {
+ iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);
+ iputdword(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);
+ }
+ start_time = ktime_get();
}
- do_posix_clock_monotonic_gettime(&start_time);
- spin_unlock_irq(&chip->reg_lock);
msleep(50);
- spin_lock_irq(&chip->reg_lock);
- /* check the position */
- do {
- civ = igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV);
- pos1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
- if (pos1 == 0) {
- udelay(10);
- continue;
+ scoped_guard(spinlock_irq, &chip->reg_lock) {
+ /* check the position */
+ do {
+ civ = igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV);
+ pos1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
+ if (pos1 == 0) {
+ udelay(10);
+ continue;
+ }
+ if (civ == igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV) &&
+ pos1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))
+ break;
+ } while (timeout--);
+ if (pos1 == 0) { /* oops, this value is not reliable */
+ pos = 0;
+ } else {
+ pos = ichdev->fragsize1;
+ pos -= pos1 << ichdev->pos_shift;
+ pos += ichdev->position;
}
- if (civ == igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV) &&
- pos1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))
- break;
- } while (timeout--);
- if (pos1 == 0) { /* oops, this value is not reliable */
- pos = 0;
- } else {
- pos = ichdev->fragsize1;
- pos -= pos1 << ichdev->pos_shift;
- pos += ichdev->position;
- }
- chip->in_measurement = 0;
- do_posix_clock_monotonic_gettime(&stop_time);
- /* stop */
- if (chip->device_type == DEVICE_ALI) {
- iputdword(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 16));
- iputbyte(chip, port + ICH_REG_OFF_CR, 0);
- while (igetbyte(chip, port + ICH_REG_OFF_CR))
- ;
- } else {
- iputbyte(chip, port + ICH_REG_OFF_CR, 0);
- while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH))
- ;
+ chip->in_measurement = 0;
+ stop_time = ktime_get();
+ /* stop */
+ if (chip->device_type == DEVICE_ALI) {
+ iputdword(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 16));
+ iputbyte(chip, port + ICH_REG_OFF_CR, 0);
+ while (igetbyte(chip, port + ICH_REG_OFF_CR))
+ ;
+ } else {
+ iputbyte(chip, port + ICH_REG_OFF_CR, 0);
+ while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH))
+ ;
+ }
+ iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
}
- iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
- spin_unlock_irq(&chip->reg_lock);
if (pos == 0) {
- snd_printk(KERN_ERR "intel8x0: measure - unreliable DMA position..\n");
+ dev_err(chip->card->dev,
+ "measure - unreliable DMA position..\n");
__retry:
if (attempt < 3) {
msleep(300);
@@ -2790,19 +2719,18 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
}
pos /= 4;
- t = stop_time.tv_sec - start_time.tv_sec;
- t *= 1000000;
- t += (stop_time.tv_nsec - start_time.tv_nsec) / 1000;
- printk(KERN_INFO "%s: measured %lu usecs (%lu samples)\n", __func__, t, pos);
+ t = ktime_us_delta(stop_time, start_time);
+ dev_info(chip->card->dev,
+ "%s: measured %lu usecs (%lu samples)\n", __func__, t, pos);
if (t == 0) {
- snd_printk(KERN_ERR "intel8x0: ?? calculation error..\n");
+ dev_err(chip->card->dev, "?? calculation error..\n");
goto __retry;
}
pos *= 1000;
pos = (pos / t) * 1000 + ((pos % t) * 1000) / t;
if (pos < 40000 || pos >= 60000) {
/* abnormal value. hw problem? */
- printk(KERN_INFO "intel8x0: measured clock %ld rejected\n", pos);
+ dev_info(chip->card->dev, "measured clock %ld rejected\n", pos);
goto __retry;
} else if (pos > 40500 && pos < 41500)
/* first exception - 41000Hz reference clock */
@@ -2814,12 +2742,13 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)
/* not 48000Hz, tuning the clock.. */
chip->ac97_bus->clock = (chip->ac97_bus->clock * 48000) / pos;
__end:
- printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97_bus->clock);
+ dev_info(chip->card->dev, "clocking to %d\n", chip->ac97_bus->clock);
snd_ac97_update_power(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 0);
}
-static struct snd_pci_quirk intel8x0_clock_list[] __devinitdata = {
+static const struct snd_pci_quirk intel8x0_clock_list[] = {
SND_PCI_QUIRK(0x0e11, 0x008a, "AD1885", 41000),
+ SND_PCI_QUIRK(0x1014, 0x0581, "AD1981B", 48000),
SND_PCI_QUIRK(0x1028, 0x00be, "AD1885", 44100),
SND_PCI_QUIRK(0x1028, 0x0177, "AD1980", 48000),
SND_PCI_QUIRK(0x1028, 0x01ad, "AD1981B", 48000),
@@ -2827,7 +2756,7 @@ static struct snd_pci_quirk intel8x0_clock_list[] __devinitdata = {
{ } /* terminator */
};
-static int __devinit intel8x0_in_clock_list(struct intel8x0 *chip)
+static int intel8x0_in_clock_list(struct intel8x0 *chip)
{
struct pci_dev *pci = chip->pci;
const struct snd_pci_quirk *wl;
@@ -2835,13 +2764,12 @@ static int __devinit intel8x0_in_clock_list(struct intel8x0 *chip)
wl = snd_pci_quirk_lookup(pci, intel8x0_clock_list);
if (!wl)
return 0;
- printk(KERN_INFO "intel8x0: white list rate for %04x:%04x is %i\n",
+ dev_info(chip->card->dev, "allow list rate for %04x:%04x is %i\n",
pci->subsystem_vendor, pci->subsystem_device, wl->value);
chip->ac97_bus->clock = wl->value;
return 1;
}
-#ifdef CONFIG_PROC_FS
static void snd_intel8x0_proc_read(struct snd_info_entry * entry,
struct snd_info_buffer *buffer)
{
@@ -2876,21 +2804,10 @@ static void snd_intel8x0_proc_read(struct snd_info_entry * entry,
chip->ac97_sdin[2]);
}
-static void __devinit snd_intel8x0_proc_init(struct intel8x0 * chip)
+static void snd_intel8x0_proc_init(struct intel8x0 *chip)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(chip->card, "intel8x0", &entry))
- snd_info_set_text_ops(entry, chip, snd_intel8x0_proc_read);
-}
-#else
-#define snd_intel8x0_proc_init(x)
-#endif
-
-static int snd_intel8x0_dev_free(struct snd_device *device)
-{
- struct intel8x0 *chip = device->device_data;
- return snd_intel8x0_free(chip);
+ snd_card_ro_proc_new(chip->card, "intel8x0", chip,
+ snd_intel8x0_proc_read);
}
struct ich_reg_info {
@@ -2898,35 +2815,62 @@ struct ich_reg_info {
unsigned int offset;
};
-static unsigned int ich_codec_bits[3] = {
+static const unsigned int ich_codec_bits[3] = {
ICH_PCR, ICH_SCR, ICH_TCR
};
-static unsigned int sis_codec_bits[3] = {
+static const unsigned int sis_codec_bits[3] = {
ICH_PCR, ICH_SCR, ICH_SIS_TCR
};
-static int __devinit snd_intel8x0_create(struct snd_card *card,
- struct pci_dev *pci,
- unsigned long device_type,
- struct intel8x0 ** r_intel8x0)
+static int snd_intel8x0_inside_vm(struct pci_dev *pci)
{
- struct intel8x0 *chip;
+ int result = inside_vm;
+ char *msg = NULL;
+
+ /* check module parameter first (override detection) */
+ if (result >= 0) {
+ msg = result ? "enable (forced) VM" : "disable (forced) VM";
+ goto fini;
+ }
+
+ /* check for known (emulated) devices */
+ result = 0;
+ if (pci->subsystem_vendor == PCI_SUBVENDOR_ID_REDHAT_QUMRANET &&
+ pci->subsystem_device == PCI_SUBDEVICE_ID_QEMU) {
+ /* KVM emulated sound, PCI SSID: 1af4:1100 */
+ msg = "enable KVM";
+ result = 1;
+ } else if (pci->subsystem_vendor == 0x1ab8) {
+ /* Parallels VM emulated sound, PCI SSID: 1ab8:xxxx */
+ msg = "enable Parallels VM";
+ result = 1;
+ }
+
+fini:
+ if (msg != NULL)
+ dev_info(&pci->dev, "%s optimization\n", msg);
+
+ return result;
+}
+
+static int snd_intel8x0_init(struct snd_card *card,
+ struct pci_dev *pci,
+ unsigned long device_type)
+{
+ struct intel8x0 *chip = card->private_data;
int err;
unsigned int i;
unsigned int int_sta_masks;
struct ichdev *ichdev;
- static struct snd_device_ops ops = {
- .dev_free = snd_intel8x0_dev_free,
- };
- static unsigned int bdbars[] = {
+ static const unsigned int bdbars[] = {
3, /* DEVICE_INTEL */
6, /* DEVICE_INTEL_ICH4 */
3, /* DEVICE_SIS */
6, /* DEVICE_ALI */
4, /* DEVICE_NFORCE */
};
- static struct ich_reg_info intel_regs[6] = {
+ static const struct ich_reg_info intel_regs[6] = {
{ ICH_PIINT, 0 },
{ ICH_POINT, 0x10 },
{ ICH_MCINT, 0x20 },
@@ -2934,13 +2878,13 @@ static int __devinit snd_intel8x0_create(struct snd_card *card,
{ ICH_P2INT, 0x50 },
{ ICH_SPINT, 0x60 },
};
- static struct ich_reg_info nforce_regs[4] = {
+ static const struct ich_reg_info nforce_regs[4] = {
{ ICH_PIINT, 0 },
{ ICH_POINT, 0x10 },
{ ICH_MCINT, 0x20 },
{ ICH_NVSPINT, 0x70 },
};
- static struct ich_reg_info ali_regs[6] = {
+ static const struct ich_reg_info ali_regs[6] = {
{ ALI_INT_PCMIN, 0x40 },
{ ALI_INT_PCMOUT, 0x50 },
{ ALI_INT_MICIN, 0x60 },
@@ -2948,18 +2892,12 @@ static int __devinit snd_intel8x0_create(struct snd_card *card,
{ ALI_INT_SPDIFIN, 0xa0 },
{ ALI_INT_SPDIFOUT, 0xb0 },
};
- struct ich_reg_info *tbl;
-
- *r_intel8x0 = NULL;
+ const struct ich_reg_info *tbl;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
spin_lock_init(&chip->reg_lock);
chip->device_type = device_type;
chip->card = card;
@@ -2972,42 +2910,36 @@ static int __devinit snd_intel8x0_create(struct snd_card *card,
if (xbox)
chip->xbox = 1;
+ chip->inside_vm = snd_intel8x0_inside_vm(pci);
+
+ /*
+ * Intel 82443MX running a 100MHz processor system bus has a hardware
+ * bug, which aborts PCI busmaster for audio transfer. A workaround
+ * is to set the pages as non-cached. For details, see the errata in
+ * http://download.intel.com/design/chipsets/specupdt/24505108.pdf
+ */
if (pci->vendor == PCI_VENDOR_ID_INTEL &&
pci->device == PCI_DEVICE_ID_INTEL_440MX)
chip->fix_nocache = 1; /* enable workaround */
- if ((err = pci_request_regions(pci, card->shortname)) < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pcim_request_all_regions(pci, card->shortname);
+ if (err < 0)
return err;
- }
if (device_type == DEVICE_ALI) {
/* ALI5455 has no ac97 region */
- chip->bmaddr = pci_iomap(pci, 0, 0);
- goto port_inited;
- }
-
- if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) /* ICH4 and Nforce */
- chip->addr = pci_iomap(pci, 2, 0);
- else
- chip->addr = pci_iomap(pci, 0, 0);
- if (!chip->addr) {
- snd_printk(KERN_ERR "AC'97 space ioremap problem\n");
- snd_intel8x0_free(chip);
- return -EIO;
- }
- if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) /* ICH4 */
- chip->bmaddr = pci_iomap(pci, 3, 0);
- else
- chip->bmaddr = pci_iomap(pci, 1, 0);
- if (!chip->bmaddr) {
- snd_printk(KERN_ERR "Controller space ioremap problem\n");
- snd_intel8x0_free(chip);
- return -EIO;
+ chip->bmaddr = pcim_iomap(pci, 0, 0);
+ } else {
+ if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) /* ICH4 and Nforce */
+ chip->addr = pcim_iomap(pci, 2, 0);
+ else
+ chip->addr = pcim_iomap(pci, 0, 0);
+ if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) /* ICH4 */
+ chip->bmaddr = pcim_iomap(pci, 3, 0);
+ else
+ chip->bmaddr = pcim_iomap(pci, 1, 0);
}
- port_inited:
chip->bdbars_count = bdbars[device_type];
/* initialize offsets */
@@ -3043,24 +2975,20 @@ static int __devinit snd_intel8x0_create(struct snd_card *card,
/* allocate buffer descriptor lists */
/* the start of each lists must be aligned to 8 bytes */
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2,
- &chip->bdbars) < 0) {
- snd_intel8x0_free(chip);
- snd_printk(KERN_ERR "intel8x0: cannot allocate buffer descriptors\n");
+ chip->bdbars = snd_devm_alloc_pages(&pci->dev, intel8x0_dma_type(chip),
+ chip->bdbars_count * sizeof(u32) *
+ ICH_MAX_FRAGS * 2);
+ if (!chip->bdbars)
return -ENOMEM;
- }
+
/* tables must be aligned to 8 bytes here, but the kernel pages
are much bigger, so we don't care (on i386) */
- /* workaround for 440MX */
- if (chip->fix_nocache)
- fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 1);
int_sta_masks = 0;
for (i = 0; i < chip->bdbars_count; i++) {
ichdev = &chip->ichd[i];
- ichdev->bdbar = ((u32 *)chip->bdbars.area) +
+ ichdev->bdbar = ((__le32 *)chip->bdbars->area) +
(i * ICH_MAX_FRAGS * 2);
- ichdev->bdbar_addr = chip->bdbars.addr +
+ ichdev->bdbar_addr = chip->bdbars->addr +
(i * sizeof(u32) * ICH_MAX_FRAGS * 2);
int_sta_masks |= ichdev->int_sta_mask;
}
@@ -3093,35 +3021,32 @@ static int __devinit snd_intel8x0_create(struct snd_card *card,
for (i = 0; i < chip->max_codecs; i++)
chip->codec_isr_bits |= chip->codec_bit[i];
- if ((err = snd_intel8x0_chip_init(chip, 1)) < 0) {
- snd_intel8x0_free(chip);
+ err = snd_intel8x0_chip_init(chip, 1);
+ if (err < 0)
return err;
- }
/* request irq after initializaing int_sta_mask, etc */
+ /* NOTE: we don't use devm version here since it's released /
+ * re-acquired in PM callbacks.
+ * It's released explicitly in snd_intel8x0_free(), too.
+ */
if (request_irq(pci->irq, snd_intel8x0_interrupt,
- IRQF_SHARED, card->shortname, chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_intel8x0_free(chip);
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_intel8x0_free(chip);
- return err;
- }
-
- snd_card_set_dev(card, &pci->dev);
+ card->private_free = snd_intel8x0_free;
- *r_intel8x0 = chip;
return 0;
}
static struct shortname_table {
unsigned int id;
const char *s;
-} shortnames[] __devinitdata = {
+} shortnames[] = {
{ PCI_DEVICE_ID_INTEL_82801AA_5, "Intel 82801AA-ICH" },
{ PCI_DEVICE_ID_INTEL_82801AB_5, "Intel 82901AB-ICH0" },
{ PCI_DEVICE_ID_INTEL_82801BA_4, "Intel 82801BA-ICH2" },
@@ -3147,59 +3072,63 @@ static struct shortname_table {
{ 0, NULL },
};
-static struct snd_pci_quirk spdif_aclink_defaults[] __devinitdata = {
+static const struct snd_pci_quirk spdif_aclink_defaults[] = {
SND_PCI_QUIRK(0x147b, 0x1c1a, "ASUS KN8", 1),
{ } /* end */
};
-/* look up white/black list for SPDIF over ac-link */
-static int __devinit check_default_spdif_aclink(struct pci_dev *pci)
+/* look up allow/deny list for SPDIF over ac-link */
+static int check_default_spdif_aclink(struct pci_dev *pci)
{
const struct snd_pci_quirk *w;
w = snd_pci_quirk_lookup(pci, spdif_aclink_defaults);
if (w) {
if (w->value)
- snd_printdd(KERN_INFO "intel8x0: Using SPDIF over "
- "AC-Link for %s\n", w->name);
+ dev_dbg(&pci->dev,
+ "Using SPDIF over AC-Link for %s\n",
+ snd_pci_quirk_name(w));
else
- snd_printdd(KERN_INFO "intel8x0: Using integrated "
- "SPDIF DMA for %s\n", w->name);
+ dev_dbg(&pci->dev,
+ "Using integrated SPDIF DMA for %s\n",
+ snd_pci_quirk_name(w));
return w->value;
}
return 0;
}
-static int __devinit snd_intel8x0_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_intel8x0_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
struct snd_card *card;
struct intel8x0 *chip;
int err;
struct shortname_table *name;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index, id, THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
if (spdif_aclink < 0)
spdif_aclink = check_default_spdif_aclink(pci);
- strcpy(card->driver, "ICH");
+ strscpy(card->driver, "ICH");
if (!spdif_aclink) {
switch (pci_id->driver_data) {
case DEVICE_NFORCE:
- strcpy(card->driver, "NFORCE");
+ strscpy(card->driver, "NFORCE");
break;
case DEVICE_INTEL_ICH4:
- strcpy(card->driver, "ICH4");
+ strscpy(card->driver, "ICH4");
}
}
- strcpy(card->shortname, "Intel ICH");
+ strscpy(card->shortname, "Intel ICH");
for (name = shortnames; name->id; name++) {
if (pci->device == name->id) {
- strcpy(card->shortname, name->s);
+ strscpy(card->shortname, name->s);
break;
}
}
@@ -3214,21 +3143,16 @@ static int __devinit snd_intel8x0_probe(struct pci_dev *pci,
buggy_irq = 0;
}
- if ((err = snd_intel8x0_create(card, pci, pci_id->driver_data,
- &chip)) < 0) {
- snd_card_free(card);
+ err = snd_intel8x0_init(card, pci, pci_id->driver_data);
+ if (err < 0)
return err;
- }
- card->private_data = chip;
- if ((err = snd_intel8x0_mixer(chip, ac97_clock, ac97_quirk)) < 0) {
- snd_card_free(card);
+ err = snd_intel8x0_mixer(chip, ac97_clock, ac97_quirk);
+ if (err < 0)
return err;
- }
- if ((err = snd_intel8x0_pcm(chip)) < 0) {
- snd_card_free(card);
+ err = snd_intel8x0_pcm(chip);
+ if (err < 0)
return err;
- }
snd_intel8x0_proc_init(chip);
@@ -3245,41 +3169,27 @@ static int __devinit snd_intel8x0_probe(struct pci_dev *pci,
}
}
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
+
pci_set_drvdata(pci, card);
return 0;
}
-static void __devexit snd_intel8x0_remove(struct pci_dev *pci)
+static int snd_intel8x0_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_intel8x0_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "Intel ICH",
+static struct pci_driver intel8x0_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_intel8x0_ids,
.probe = snd_intel8x0_probe,
- .remove = __devexit_p(snd_intel8x0_remove),
-#ifdef CONFIG_PM
- .suspend = intel8x0_suspend,
- .resume = intel8x0_resume,
-#endif
+ .driver = {
+ .pm = &intel8x0_pm,
+ },
};
-
-static int __init alsa_card_intel8x0_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_intel8x0_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_intel8x0_init)
-module_exit(alsa_card_intel8x0_exit)
+module_pci_driver(intel8x0_driver);
diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c
index 13cec1e5ced9..84e1b7ea34e2 100644
--- a/sound/pci/intel8x0m.c
+++ b/sound/pci/intel8x0m.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA modem driver for Intel ICH (i8x0) chipsets
*
@@ -5,31 +6,15 @@
*
* This is modified (by Sasha Khapyorsky <sashak@alsa-project.org>) version
* of ALSA ICH sound driver intel8x0.c .
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
-#include <asm/io.h>
+#include <linux/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
@@ -40,21 +25,6 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; "
"SiS 7013; NVidia MCP/2/2S/3 modems");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Intel,82801AA-ICH},"
- "{Intel,82901AB-ICH0},"
- "{Intel,82801BA-ICH2},"
- "{Intel,82801CA-ICH3},"
- "{Intel,82801DB-ICH4},"
- "{Intel,ICH5},"
- "{Intel,ICH6},"
- "{Intel,ICH7},"
- "{Intel,MX440},"
- "{SiS,7013},"
- "{NVidia,NForce Modem},"
- "{NVidia,NForce2 Modem},"
- "{NVidia,NForce2s Modem},"
- "{NVidia,NForce3 Modem},"
- "{AMD,AMD768}}");
static int index = -2; /* Exclude the first card */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
@@ -68,7 +38,7 @@ module_param(ac97_clock, int, 0444);
MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = auto-detect).");
/* just for backward compatibility */
-static int enable;
+static bool enable;
module_param(enable, bool, 0444);
/*
@@ -87,7 +57,7 @@ enum { \
ICH_REG_##name##_PICB = base + 0x08, /* word - position in current buffer */ \
ICH_REG_##name##_PIV = base + 0x0a, /* byte - prefetched index value */ \
ICH_REG_##name##_CR = base + 0x0b, /* byte - control register */ \
-};
+}
/* busmaster blocks */
DEFINE_REGSET(OFF, 0); /* offset */
@@ -168,7 +138,7 @@ enum { ALID_MDMIN, ALID_MDMOUT, ALID_MDMLAST = ALID_MDMOUT };
struct ichdev {
unsigned int ichd; /* ich device number */
unsigned long reg_offset; /* offset to bmaddr */
- u32 *bdbar; /* CPU address (32bit) */
+ __le32 *bdbar; /* CPU address (32bit) */
unsigned int bdbar_addr; /* PCI bus address (32bit) */
struct snd_pcm_substream *substream;
unsigned int physbuf; /* physical address (32bit) */
@@ -212,14 +182,14 @@ struct intel8x0m {
spinlock_t reg_lock;
- struct snd_dma_buffer bdbars;
+ struct snd_dma_buffer *bdbars;
u32 bdbars_count;
u32 int_sta_reg; /* interrupt status register */
u32 int_sta_mask; /* interrupt status mask */
unsigned int pcm_pos_shift;
};
-static DEFINE_PCI_DEVICE_TABLE(snd_intel8x0m_ids) = {
+static const struct pci_device_id snd_intel8x0m_ids[] = {
{ PCI_VDEVICE(INTEL, 0x2416), DEVICE_INTEL }, /* 82801AA */
{ PCI_VDEVICE(INTEL, 0x2426), DEVICE_INTEL }, /* 82901AB */
{ PCI_VDEVICE(INTEL, 0x2446), DEVICE_INTEL }, /* 82801BA */
@@ -235,8 +205,8 @@ static DEFINE_PCI_DEVICE_TABLE(snd_intel8x0m_ids) = {
{ PCI_VDEVICE(NVIDIA, 0x0069), DEVICE_NFORCE }, /* NFORCE2 */
{ PCI_VDEVICE(NVIDIA, 0x0089), DEVICE_NFORCE }, /* NFORCE2s */
{ PCI_VDEVICE(NVIDIA, 0x00d9), DEVICE_NFORCE }, /* NFORCE3 */
+ { PCI_VDEVICE(AMD, 0x746e), DEVICE_INTEL }, /* AMD8111 */
#if 0
- { PCI_VDEVICE(AMD, 0x746d), DEVICE_INTEL }, /* AMD8111 */
{ PCI_VDEVICE(AL, 0x5455), DEVICE_ALI }, /* Ali5455 */
#endif
{ 0, }
@@ -303,7 +273,7 @@ static inline void iaputword(struct intel8x0m *chip, u32 offset, u16 val)
/* return the GLOB_STA bit for the corresponding codec */
static unsigned int get_ich_codec_bit(struct intel8x0m *chip, unsigned int codec)
{
- static unsigned int codec_bit[3] = {
+ static const unsigned int codec_bit[3] = {
ICH_PCR, ICH_SCR, ICH_TCR
};
if (snd_BUG_ON(codec >= 3))
@@ -331,31 +301,34 @@ static int snd_intel8x0m_codec_semaphore(struct intel8x0m *chip, unsigned int co
udelay(10);
} while (time--);
- /* access to some forbidden (non existant) ac97 registers will not
+ /* access to some forbidden (non existent) ac97 registers will not
* reset the semaphore. So even if you don't get the semaphore, still
* continue the access. We don't need the semaphore anyway. */
- snd_printk(KERN_ERR "codec_semaphore: semaphore is not ready [0x%x][0x%x]\n",
+ dev_err(chip->card->dev,
+ "codec_semaphore: semaphore is not ready [0x%x][0x%x]\n",
igetbyte(chip, ICHREG(ACC_SEMA)), igetdword(chip, ICHREG(GLOB_STA)));
iagetword(chip, 0); /* clear semaphore flag */
/* I don't care about the semaphore */
return -EBUSY;
}
-static void snd_intel8x0_codec_write(struct snd_ac97 *ac97,
- unsigned short reg,
- unsigned short val)
+static void snd_intel8x0m_codec_write(struct snd_ac97 *ac97,
+ unsigned short reg,
+ unsigned short val)
{
struct intel8x0m *chip = ac97->private_data;
if (snd_intel8x0m_codec_semaphore(chip, ac97->num) < 0) {
if (! chip->in_ac97_init)
- snd_printk(KERN_ERR "codec_write %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
+ dev_err(chip->card->dev,
+ "codec_write %d: semaphore is not ready for register 0x%x\n",
+ ac97->num, reg);
}
iaputword(chip, reg + ac97->num * 0x80, val);
}
-static unsigned short snd_intel8x0_codec_read(struct snd_ac97 *ac97,
- unsigned short reg)
+static unsigned short snd_intel8x0m_codec_read(struct snd_ac97 *ac97,
+ unsigned short reg)
{
struct intel8x0m *chip = ac97->private_data;
unsigned short res;
@@ -363,16 +336,21 @@ static unsigned short snd_intel8x0_codec_read(struct snd_ac97 *ac97,
if (snd_intel8x0m_codec_semaphore(chip, ac97->num) < 0) {
if (! chip->in_ac97_init)
- snd_printk(KERN_ERR "codec_read %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
+ dev_err(chip->card->dev,
+ "codec_read %d: semaphore is not ready for register 0x%x\n",
+ ac97->num, reg);
res = 0xffff;
} else {
res = iagetword(chip, reg + ac97->num * 0x80);
- if ((tmp = igetdword(chip, ICHREG(GLOB_STA))) & ICH_RCS) {
+ tmp = igetdword(chip, ICHREG(GLOB_STA));
+ if (tmp & ICH_RCS) {
/* reset RCS and preserve other R/WC bits */
iputdword(chip, ICHREG(GLOB_STA),
tmp & ~(ICH_SRI|ICH_PRI|ICH_TRI|ICH_GSCI));
if (! chip->in_ac97_init)
- snd_printk(KERN_ERR "codec_read %d: read timeout for register 0x%x\n", ac97->num, reg);
+ dev_err(chip->card->dev,
+ "codec_read %d: read timeout for register 0x%x\n",
+ ac97->num, reg);
res = 0xffff;
}
}
@@ -385,10 +363,10 @@ static unsigned short snd_intel8x0_codec_read(struct snd_ac97 *ac97,
/*
* DMA I/O
*/
-static void snd_intel8x0_setup_periods(struct intel8x0m *chip, struct ichdev *ichdev)
+static void snd_intel8x0m_setup_periods(struct intel8x0m *chip, struct ichdev *ichdev)
{
int idx;
- u32 *bdbar = ichdev->bdbar;
+ __le32 *bdbar = ichdev->bdbar;
unsigned long port = ichdev->reg_offset;
iputdword(chip, port + ICH_REG_OFF_BDBAR, ichdev->bdbar_addr);
@@ -412,7 +390,7 @@ static void snd_intel8x0_setup_periods(struct intel8x0m *chip, struct ichdev *ic
bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */
ichdev->fragsize >> chip->pcm_pos_shift);
/*
- printk(KERN_DEBUG "bdbar[%i] = 0x%x [0x%x]\n",
+ dev_dbg(chip->card->dev, "bdbar[%i] = 0x%x [0x%x]\n",
idx + 0, bdbar[idx + 0], bdbar[idx + 1]);
*/
}
@@ -424,8 +402,8 @@ static void snd_intel8x0_setup_periods(struct intel8x0m *chip, struct ichdev *ic
ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags;
ichdev->position = 0;
#if 0
- printk(KERN_DEBUG "lvi_frag = %i, frags = %i, period_size = 0x%x, "
- "period_size1 = 0x%x\n",
+ dev_dbg(chip->card->dev,
+ "lvi_frag = %i, frags = %i, period_size = 0x%x, period_size1 = 0x%x\n",
ichdev->lvi_frag, ichdev->frags, ichdev->fragsize,
ichdev->fragsize1);
#endif
@@ -437,7 +415,7 @@ static void snd_intel8x0_setup_periods(struct intel8x0m *chip, struct ichdev *ic
* Interrupt handler
*/
-static inline void snd_intel8x0_update(struct intel8x0m *chip, struct ichdev *ichdev)
+static inline void snd_intel8x0m_update(struct intel8x0m *chip, struct ichdev *ichdev)
{
unsigned long port = ichdev->reg_offset;
int civ, i, step;
@@ -445,7 +423,6 @@ static inline void snd_intel8x0_update(struct intel8x0m *chip, struct ichdev *ic
civ = igetbyte(chip, port + ICH_REG_OFF_CIV);
if (civ == ichdev->civ) {
- // snd_printd("civ same %d\n", civ);
step = 1;
ichdev->civ++;
ichdev->civ &= ICH_REG_LVI_MASK;
@@ -453,8 +430,6 @@ static inline void snd_intel8x0_update(struct intel8x0m *chip, struct ichdev *ic
step = civ - ichdev->civ;
if (step < 0)
step += ICH_REG_LVI_MASK + 1;
- // if (step != 1)
- // snd_printd("step = %d, %d -> %d\n", step, ichdev->civ, civ);
ichdev->civ = civ;
}
@@ -470,8 +445,8 @@ static inline void snd_intel8x0_update(struct intel8x0m *chip, struct ichdev *ic
ichdev->lvi_frag *
ichdev->fragsize1);
#if 0
- printk(KERN_DEBUG "new: bdbar[%i] = 0x%x [0x%x], "
- "prefetch = %i, all = 0x%x, 0x%x\n",
+ dev_dbg(chip->card->dev,
+ "new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n",
ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2],
ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_OFF_PIV + port),
inl(port + 4), inb(port + ICH_REG_OFF_CR));
@@ -489,35 +464,31 @@ static inline void snd_intel8x0_update(struct intel8x0m *chip, struct ichdev *ic
iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
}
-static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id)
+static irqreturn_t snd_intel8x0m_interrupt(int irq, void *dev_id)
{
struct intel8x0m *chip = dev_id;
struct ichdev *ichdev;
unsigned int status;
unsigned int i;
- spin_lock(&chip->reg_lock);
+ guard(spinlock)(&chip->reg_lock);
status = igetdword(chip, chip->int_sta_reg);
- if (status == 0xffffffff) { /* we are not yet resumed */
- spin_unlock(&chip->reg_lock);
+ if (status == 0xffffffff) /* we are not yet resumed */
return IRQ_NONE;
- }
if ((status & chip->int_sta_mask) == 0) {
if (status)
iputdword(chip, chip->int_sta_reg, status);
- spin_unlock(&chip->reg_lock);
return IRQ_NONE;
}
for (i = 0; i < chip->bdbars_count; i++) {
ichdev = &chip->ichd[i];
if (status & ichdev->int_sta_mask)
- snd_intel8x0_update(chip, ichdev);
+ snd_intel8x0m_update(chip, ichdev);
}
/* ack them */
iputdword(chip, chip->int_sta_reg, status & chip->int_sta_mask);
- spin_unlock(&chip->reg_lock);
return IRQ_HANDLED;
}
@@ -526,7 +497,7 @@ static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id)
* PCM part
*/
-static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int snd_intel8x0m_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct intel8x0m *chip = snd_pcm_substream_chip(substream);
struct ichdev *ichdev = get_ichdev(substream);
@@ -561,18 +532,7 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd
return 0;
}
-static int snd_intel8x0_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_intel8x0_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t snd_intel8x0m_pcm_pointer(struct snd_pcm_substream *substream)
{
struct intel8x0m *chip = snd_pcm_substream_chip(substream);
struct ichdev *ichdev = get_ichdev(substream);
@@ -600,11 +560,11 @@ static int snd_intel8x0m_pcm_prepare(struct snd_pcm_substream *substream)
ichdev->fragsize = snd_pcm_lib_period_bytes(substream);
snd_ac97_write(ichdev->ac97, AC97_LINE1_RATE, runtime->rate);
snd_ac97_write(ichdev->ac97, AC97_LINE1_LEVEL, 0);
- snd_intel8x0_setup_periods(chip, ichdev);
+ snd_intel8x0m_setup_periods(chip, ichdev);
return 0;
}
-static struct snd_pcm_hardware snd_intel8x0m_stream =
+static const struct snd_pcm_hardware snd_intel8x0m_stream =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -628,8 +588,8 @@ static struct snd_pcm_hardware snd_intel8x0m_stream =
static int snd_intel8x0m_pcm_open(struct snd_pcm_substream *substream, struct ichdev *ichdev)
{
- static unsigned int rates[] = { 8000, 9600, 12000, 16000 };
- static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+ static const unsigned int rates[] = { 8000, 9600, 12000, 16000 };
+ static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
@@ -678,40 +638,34 @@ static int snd_intel8x0m_capture_close(struct snd_pcm_substream *substream)
}
-static struct snd_pcm_ops snd_intel8x0m_playback_ops = {
+static const struct snd_pcm_ops snd_intel8x0m_playback_ops = {
.open = snd_intel8x0m_playback_open,
.close = snd_intel8x0m_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_intel8x0_hw_params,
- .hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0m_pcm_prepare,
- .trigger = snd_intel8x0_pcm_trigger,
- .pointer = snd_intel8x0_pcm_pointer,
+ .trigger = snd_intel8x0m_pcm_trigger,
+ .pointer = snd_intel8x0m_pcm_pointer,
};
-static struct snd_pcm_ops snd_intel8x0m_capture_ops = {
+static const struct snd_pcm_ops snd_intel8x0m_capture_ops = {
.open = snd_intel8x0m_capture_open,
.close = snd_intel8x0m_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_intel8x0_hw_params,
- .hw_free = snd_intel8x0_hw_free,
.prepare = snd_intel8x0m_pcm_prepare,
- .trigger = snd_intel8x0_pcm_trigger,
- .pointer = snd_intel8x0_pcm_pointer,
+ .trigger = snd_intel8x0m_pcm_trigger,
+ .pointer = snd_intel8x0m_pcm_pointer,
};
struct ich_pcm_table {
char *suffix;
- struct snd_pcm_ops *playback_ops;
- struct snd_pcm_ops *capture_ops;
+ const struct snd_pcm_ops *playback_ops;
+ const struct snd_pcm_ops *capture_ops;
size_t prealloc_size;
size_t prealloc_max_size;
int ac97_idx;
};
-static int __devinit snd_intel8x0_pcm1(struct intel8x0m *chip, int device,
- struct ich_pcm_table *rec)
+static int snd_intel8x0m_pcm1(struct intel8x0m *chip, int device,
+ const struct ich_pcm_table *rec)
{
struct snd_pcm *pcm;
int err;
@@ -720,7 +674,7 @@ static int __devinit snd_intel8x0_pcm1(struct intel8x0m *chip, int device,
if (rec->suffix)
sprintf(name, "Intel ICH - %s", rec->suffix);
else
- strcpy(name, "Intel ICH");
+ strscpy(name, "Intel ICH");
err = snd_pcm_new(chip->card, name, device,
rec->playback_ops ? 1 : 0,
rec->capture_ops ? 1 : 0, &pcm);
@@ -738,18 +692,18 @@ static int __devinit snd_intel8x0_pcm1(struct intel8x0m *chip, int device,
if (rec->suffix)
sprintf(pcm->name, "%s - %s", chip->card->shortname, rec->suffix);
else
- strcpy(pcm->name, chip->card->shortname);
+ strscpy(pcm->name, chip->card->shortname);
chip->pcm[device] = pcm;
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- rec->prealloc_size,
- rec->prealloc_max_size);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev,
+ rec->prealloc_size,
+ rec->prealloc_max_size);
return 0;
}
-static struct ich_pcm_table intel_pcms[] __devinitdata = {
+static const struct ich_pcm_table intel_pcms[] = {
{
.suffix = "Modem",
.playback_ops = &snd_intel8x0m_playback_ops,
@@ -759,10 +713,10 @@ static struct ich_pcm_table intel_pcms[] __devinitdata = {
},
};
-static int __devinit snd_intel8x0_pcm(struct intel8x0m *chip)
+static int snd_intel8x0m_pcm(struct intel8x0m *chip)
{
int i, tblsize, device, err;
- struct ich_pcm_table *tbl, *rec;
+ const struct ich_pcm_table *tbl, *rec;
#if 1
tbl = intel_pcms;
@@ -791,7 +745,7 @@ static int __devinit snd_intel8x0_pcm(struct intel8x0m *chip)
if (! chip->ichd[rec->ac97_idx].ac97)
continue;
}
- err = snd_intel8x0_pcm1(chip, device, rec);
+ err = snd_intel8x0m_pcm1(chip, device, rec);
if (err < 0)
return err;
device++;
@@ -806,51 +760,54 @@ static int __devinit snd_intel8x0_pcm(struct intel8x0m *chip)
* Mixer part
*/
-static void snd_intel8x0_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
+static void snd_intel8x0m_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
{
struct intel8x0m *chip = bus->private_data;
chip->ac97_bus = NULL;
}
-static void snd_intel8x0_mixer_free_ac97(struct snd_ac97 *ac97)
+static void snd_intel8x0m_mixer_free_ac97(struct snd_ac97 *ac97)
{
struct intel8x0m *chip = ac97->private_data;
chip->ac97 = NULL;
}
-static int __devinit snd_intel8x0_mixer(struct intel8x0m *chip, int ac97_clock)
+static int snd_intel8x0m_mixer(struct intel8x0m *chip, int ac97_clock)
{
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
struct snd_ac97 *x97;
int err;
unsigned int glob_sta = 0;
- static struct snd_ac97_bus_ops ops = {
- .write = snd_intel8x0_codec_write,
- .read = snd_intel8x0_codec_read,
+ static const struct snd_ac97_bus_ops ops = {
+ .write = snd_intel8x0m_codec_write,
+ .read = snd_intel8x0m_codec_read,
};
chip->in_ac97_init = 1;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
- ac97.private_free = snd_intel8x0_mixer_free_ac97;
+ ac97.private_free = snd_intel8x0m_mixer_free_ac97;
ac97.scaps = AC97_SCAP_SKIP_AUDIO | AC97_SCAP_POWER_SAVE;
glob_sta = igetdword(chip, ICHREG(GLOB_STA));
- if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0)
+ err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus);
+ if (err < 0)
goto __err;
- pbus->private_free = snd_intel8x0_mixer_free_ac97_bus;
+ pbus->private_free = snd_intel8x0m_mixer_free_ac97_bus;
if (ac97_clock >= 8000 && ac97_clock <= 48000)
pbus->clock = ac97_clock;
chip->ac97_bus = pbus;
ac97.pci = chip->pci;
ac97.num = glob_sta & ICH_SCR ? 1 : 0;
- if ((err = snd_ac97_mixer(pbus, &ac97, &x97)) < 0) {
- snd_printk(KERN_ERR "Unable to initialize codec #%d\n", ac97.num);
+ err = snd_ac97_mixer(pbus, &ac97, &x97);
+ if (err < 0) {
+ dev_err(chip->card->dev,
+ "Unable to initialize codec #%d\n", ac97.num);
if (ac97.num == 0)
goto __err;
return err;
@@ -894,13 +851,14 @@ static int snd_intel8x0m_ich_chip_init(struct intel8x0m *chip, int probing)
/* finish cold or do warm reset */
cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM;
iputdword(chip, ICHREG(GLOB_CNT), cnt);
- end_time = (jiffies + (HZ / 4)) + 1;
+ usleep_range(500, 1000); /* give warm reset some time */
+ end_time = jiffies + HZ / 4;
do {
if ((igetdword(chip, ICHREG(GLOB_CNT)) & ICH_AC97WARM) == 0)
goto __ok;
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
- snd_printk(KERN_ERR "AC'97 warm reset still in progress? [0x%x]\n",
+ dev_err(chip->card->dev, "AC'97 warm reset still in progress? [0x%x]\n",
igetdword(chip, ICHREG(GLOB_CNT)));
return -EIO;
@@ -920,7 +878,8 @@ static int snd_intel8x0m_ich_chip_init(struct intel8x0m *chip, int probing)
} while (time_after_eq(end_time, jiffies));
if (! status) {
/* no codec is found */
- snd_printk(KERN_ERR "codec_ready: codec is not ready [0x%x]\n",
+ dev_err(chip->card->dev,
+ "codec_ready: codec is not ready [0x%x]\n",
igetdword(chip, ICHREG(GLOB_STA)));
return -EIO;
}
@@ -952,19 +911,20 @@ static int snd_intel8x0m_ich_chip_init(struct intel8x0m *chip, int probing)
}
if (chip->device_type == DEVICE_SIS) {
- /* unmute the output on SIS7012 */
+ /* unmute the output on SIS7013 */
iputword(chip, 0x4c, igetword(chip, 0x4c) | 1);
}
return 0;
}
-static int snd_intel8x0_chip_init(struct intel8x0m *chip, int probing)
+static int snd_intel8x0m_chip_init(struct intel8x0m *chip, int probing)
{
unsigned int i;
int err;
- if ((err = snd_intel8x0m_ich_chip_init(chip, probing)) < 0)
+ err = snd_intel8x0m_ich_chip_init(chip, probing);
+ if (err < 0)
return err;
iagetword(chip, 0); /* clear semaphore flag */
@@ -980,8 +940,9 @@ static int snd_intel8x0_chip_init(struct intel8x0m *chip, int probing)
return 0;
}
-static int snd_intel8x0_free(struct intel8x0m *chip)
+static void snd_intel8x0m_free(struct snd_card *card)
{
+ struct intel8x0m *chip = card->private_data;
unsigned int i;
if (chip->irq < 0)
@@ -995,73 +956,50 @@ static int snd_intel8x0_free(struct intel8x0m *chip)
__hw_end:
if (chip->irq >= 0)
free_irq(chip->irq, chip);
- if (chip->bdbars.area)
- snd_dma_free_pages(&chip->bdbars);
- if (chip->addr)
- pci_iounmap(chip->pci, chip->addr);
- if (chip->bmaddr)
- pci_iounmap(chip->pci, chip->bmaddr);
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
- kfree(chip);
- return 0;
}
-#ifdef CONFIG_PM
/*
* power management
*/
-static int intel8x0m_suspend(struct pci_dev *pci, pm_message_t state)
+static int intel8x0m_suspend(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_card *card = dev_get_drvdata(dev);
struct intel8x0m *chip = card->private_data;
- int i;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- for (i = 0; i < chip->pcm_devs; i++)
- snd_pcm_suspend_all(chip->pcm[i]);
snd_ac97_suspend(chip->ac97);
if (chip->irq >= 0) {
free_irq(chip->irq, chip);
chip->irq = -1;
+ card->sync_irq = -1;
}
- pci_disable_device(pci);
- pci_save_state(pci);
- pci_set_power_state(pci, pci_choose_state(pci, state));
return 0;
}
-static int intel8x0m_resume(struct pci_dev *pci)
+static int intel8x0m_resume(struct device *dev)
{
- struct snd_card *card = pci_get_drvdata(pci);
+ struct pci_dev *pci = to_pci_dev(dev);
+ struct snd_card *card = dev_get_drvdata(dev);
struct intel8x0m *chip = card->private_data;
- pci_set_power_state(pci, PCI_D0);
- pci_restore_state(pci);
- if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "intel8x0m: pci_enable_device failed, "
- "disabling device\n");
- snd_card_disconnect(card);
- return -EIO;
- }
- pci_set_master(pci);
- if (request_irq(pci->irq, snd_intel8x0_interrupt,
- IRQF_SHARED, card->shortname, chip)) {
- printk(KERN_ERR "intel8x0m: unable to grab IRQ %d, "
- "disabling device\n", pci->irq);
+ if (request_irq(pci->irq, snd_intel8x0m_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, chip)) {
+ dev_err(dev, "unable to grab IRQ %d, disabling device\n",
+ pci->irq);
snd_card_disconnect(card);
return -EIO;
}
chip->irq = pci->irq;
- snd_intel8x0_chip_init(chip, 0);
+ card->sync_irq = chip->irq;
+ snd_intel8x0m_chip_init(chip, 0);
snd_ac97_resume(chip->ac97);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
-#endif /* CONFIG_PM */
-#ifdef CONFIG_PROC_FS
+static DEFINE_SIMPLE_DEV_PM_OPS(intel8x0m_pm, intel8x0m_suspend, intel8x0m_resume);
+
static void snd_intel8x0m_proc_read(struct snd_info_entry * entry,
struct snd_info_buffer *buffer)
{
@@ -1082,22 +1020,10 @@ static void snd_intel8x0m_proc_read(struct snd_info_entry * entry,
(tmp & (ICH_PCR | ICH_SCR | ICH_TCR)) == 0 ? " none" : "");
}
-static void __devinit snd_intel8x0m_proc_init(struct intel8x0m * chip)
-{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(chip->card, "intel8x0m", &entry))
- snd_info_set_text_ops(entry, chip, snd_intel8x0m_proc_read);
-}
-#else /* !CONFIG_PROC_FS */
-#define snd_intel8x0m_proc_init(chip)
-#endif /* CONFIG_PROC_FS */
-
-
-static int snd_intel8x0_dev_free(struct snd_device *device)
+static void snd_intel8x0m_proc_init(struct intel8x0m *chip)
{
- struct intel8x0m *chip = device->device_data;
- return snd_intel8x0_free(chip);
+ snd_card_ro_proc_new(chip->card, "intel8x0m", chip,
+ snd_intel8x0m_proc_read);
}
struct ich_reg_info {
@@ -1105,82 +1031,48 @@ struct ich_reg_info {
unsigned int offset;
};
-static int __devinit snd_intel8x0m_create(struct snd_card *card,
- struct pci_dev *pci,
- unsigned long device_type,
- struct intel8x0m ** r_intel8x0)
+static int snd_intel8x0m_init(struct snd_card *card,
+ struct pci_dev *pci,
+ unsigned long device_type)
{
- struct intel8x0m *chip;
+ struct intel8x0m *chip = card->private_data;
int err;
unsigned int i;
unsigned int int_sta_masks;
struct ichdev *ichdev;
- static struct snd_device_ops ops = {
- .dev_free = snd_intel8x0_dev_free,
- };
- static struct ich_reg_info intel_regs[2] = {
+ static const struct ich_reg_info intel_regs[2] = {
{ ICH_MIINT, 0 },
{ ICH_MOINT, 0x10 },
};
- struct ich_reg_info *tbl;
+ const struct ich_reg_info *tbl;
- *r_intel8x0 = NULL;
-
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
spin_lock_init(&chip->reg_lock);
chip->device_type = device_type;
chip->card = card;
chip->pci = pci;
chip->irq = -1;
- if ((err = pci_request_regions(pci, card->shortname)) < 0) {
- kfree(chip);
- pci_disable_device(pci);
+ err = pcim_request_all_regions(pci, card->shortname);
+ if (err < 0)
return err;
- }
if (device_type == DEVICE_ALI) {
/* ALI5455 has no ac97 region */
- chip->bmaddr = pci_iomap(pci, 0, 0);
- goto port_inited;
- }
-
- if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) /* ICH4 and Nforce */
- chip->addr = pci_iomap(pci, 2, 0);
- else
- chip->addr = pci_iomap(pci, 0, 0);
- if (!chip->addr) {
- snd_printk(KERN_ERR "AC'97 space ioremap problem\n");
- snd_intel8x0_free(chip);
- return -EIO;
- }
- if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) /* ICH4 */
- chip->bmaddr = pci_iomap(pci, 3, 0);
- else
- chip->bmaddr = pci_iomap(pci, 1, 0);
- if (!chip->bmaddr) {
- snd_printk(KERN_ERR "Controller space ioremap problem\n");
- snd_intel8x0_free(chip);
- return -EIO;
- }
-
- port_inited:
- if (request_irq(pci->irq, snd_intel8x0_interrupt, IRQF_SHARED,
- card->shortname, chip)) {
- snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
- snd_intel8x0_free(chip);
- return -EBUSY;
+ chip->bmaddr = pcim_iomap(pci, 0, 0);
+ } else {
+ if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) /* ICH4 and Nforce */
+ chip->addr = pcim_iomap(pci, 2, 0);
+ else
+ chip->addr = pcim_iomap(pci, 0, 0);
+ if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) /* ICH4 */
+ chip->bmaddr = pcim_iomap(pci, 3, 0);
+ else
+ chip->bmaddr = pcim_iomap(pci, 1, 0);
}
- chip->irq = pci->irq;
- pci_set_master(pci);
- synchronize_irq(chip->irq);
/* initialize offsets */
chip->bdbars_count = 2;
@@ -1207,44 +1099,51 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card,
/* allocate buffer descriptor lists */
/* the start of each lists must be aligned to 8 bytes */
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2,
- &chip->bdbars) < 0) {
- snd_intel8x0_free(chip);
+ chip->bdbars = snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV,
+ chip->bdbars_count * sizeof(u32) *
+ ICH_MAX_FRAGS * 2);
+ if (!chip->bdbars)
return -ENOMEM;
- }
+
/* tables must be aligned to 8 bytes here, but the kernel pages
are much bigger, so we don't care (on i386) */
int_sta_masks = 0;
for (i = 0; i < chip->bdbars_count; i++) {
ichdev = &chip->ichd[i];
- ichdev->bdbar = ((u32 *)chip->bdbars.area) + (i * ICH_MAX_FRAGS * 2);
- ichdev->bdbar_addr = chip->bdbars.addr + (i * sizeof(u32) * ICH_MAX_FRAGS * 2);
+ ichdev->bdbar = ((__le32 *)chip->bdbars->area) + (i * ICH_MAX_FRAGS * 2);
+ ichdev->bdbar_addr = chip->bdbars->addr + (i * sizeof(u32) * ICH_MAX_FRAGS * 2);
int_sta_masks |= ichdev->int_sta_mask;
}
chip->int_sta_reg = ICH_REG_GLOB_STA;
chip->int_sta_mask = int_sta_masks;
- if ((err = snd_intel8x0_chip_init(chip, 1)) < 0) {
- snd_intel8x0_free(chip);
- return err;
- }
+ pci_set_master(pci);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
- snd_intel8x0_free(chip);
+ err = snd_intel8x0m_chip_init(chip, 1);
+ if (err < 0)
return err;
+
+ /* NOTE: we don't use devm version here since it's released /
+ * re-acquired in PM callbacks.
+ * It's released explicitly in snd_intel8x0m_free(), too.
+ */
+ if (request_irq(pci->irq, snd_intel8x0m_interrupt, IRQF_SHARED,
+ KBUILD_MODNAME, chip)) {
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
+ return -EBUSY;
}
+ chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
- snd_card_set_dev(card, &pci->dev);
+ card->private_free = snd_intel8x0m_free;
- *r_intel8x0 = chip;
return 0;
}
static struct shortname_table {
unsigned int id;
const char *s;
-} shortnames[] __devinitdata = {
+} shortnames[] = {
{ PCI_DEVICE_ID_INTEL_82801AA_6, "Intel 82801AA-ICH" },
{ PCI_DEVICE_ID_INTEL_82801AB_6, "Intel 82901AB-ICH0" },
{ PCI_DEVICE_ID_INTEL_82801BA_6, "Intel 82801BA-ICH2" },
@@ -1260,90 +1159,73 @@ static struct shortname_table {
{ PCI_DEVICE_ID_NVIDIA_MCP2_MODEM, "NVidia nForce2" },
{ PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM, "NVidia nForce2s" },
{ PCI_DEVICE_ID_NVIDIA_MCP3_MODEM, "NVidia nForce3" },
+ { 0x746e, "AMD AMD8111" },
#if 0
{ 0x5455, "ALi M5455" },
- { 0x746d, "AMD AMD8111" },
#endif
{ 0 },
};
-static int __devinit snd_intel8x0m_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int __snd_intel8x0m_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
struct snd_card *card;
struct intel8x0m *chip;
int err;
struct shortname_table *name;
- err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index, id, THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- strcpy(card->driver, "ICH-MODEM");
- strcpy(card->shortname, "Intel ICH");
+ strscpy(card->driver, "ICH-MODEM");
+ strscpy(card->shortname, "Intel ICH");
for (name = shortnames; name->id; name++) {
if (pci->device == name->id) {
- strcpy(card->shortname, name->s);
+ strscpy(card->shortname, name->s);
break;
}
}
strcat(card->shortname," Modem");
- if ((err = snd_intel8x0m_create(card, pci, pci_id->driver_data, &chip)) < 0) {
- snd_card_free(card);
+ err = snd_intel8x0m_init(card, pci, pci_id->driver_data);
+ if (err < 0)
return err;
- }
- card->private_data = chip;
- if ((err = snd_intel8x0_mixer(chip, ac97_clock)) < 0) {
- snd_card_free(card);
+ err = snd_intel8x0m_mixer(chip, ac97_clock);
+ if (err < 0)
return err;
- }
- if ((err = snd_intel8x0_pcm(chip)) < 0) {
- snd_card_free(card);
+ err = snd_intel8x0m_pcm(chip);
+ if (err < 0)
return err;
- }
snd_intel8x0m_proc_init(chip);
sprintf(card->longname, "%s at irq %i",
card->shortname, chip->irq);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
+ err = snd_card_register(card);
+ if (err < 0)
return err;
- }
pci_set_drvdata(pci, card);
return 0;
}
-static void __devexit snd_intel8x0m_remove(struct pci_dev *pci)
+static int snd_intel8x0m_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ return snd_card_free_on_error(&pci->dev, __snd_intel8x0m_probe(pci, pci_id));
}
-static struct pci_driver driver = {
- .name = "Intel ICH Modem",
+static struct pci_driver intel8x0m_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_intel8x0m_ids,
.probe = snd_intel8x0m_probe,
- .remove = __devexit_p(snd_intel8x0m_remove),
-#ifdef CONFIG_PM
- .suspend = intel8x0m_suspend,
- .resume = intel8x0m_resume,
-#endif
+ .driver = {
+ .pm = &intel8x0m_pm,
+ },
};
-
-static int __init alsa_card_intel8x0m_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_intel8x0m_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_intel8x0m_init)
-module_exit(alsa_card_intel8x0m_exit)
+module_pci_driver(intel8x0m_driver);
diff --git a/sound/pci/korg1212/Makefile b/sound/pci/korg1212/Makefile
index f11ce1b1b3d4..ab0186ffbd58 100644
--- a/sound/pci/korg1212/Makefile
+++ b/sound/pci/korg1212/Makefile
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
-snd-korg1212-objs := korg1212.o
+snd-korg1212-y := korg1212.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_KORG1212) += snd-korg1212.o
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index 6d795700be79..d16acf83668a 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -1,22 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for the Korg 1212 IO PCI card
*
* Copyright (c) 2001 Haroldo Gamal <gamal@alternex.com.br>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
*/
#include <linux/delay.h>
@@ -25,9 +11,10 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/wait.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/firmware.h>
+#include <linux/io.h>
#include <sound/core.h>
#include <sound/info.h>
@@ -36,21 +23,19 @@
#include <sound/pcm_params.h>
#include <sound/initval.h>
-#include <asm/io.h>
-
// ----------------------------------------------------------------------------
// Debug Stuff
// ----------------------------------------------------------------------------
#define K1212_DEBUG_LEVEL 0
#if K1212_DEBUG_LEVEL > 0
-#define K1212_DEBUG_PRINTK(fmt,args...) printk(KERN_DEBUG fmt,##args)
+#define K1212_DEBUG_PRINTK(fmt, args...) pr_debug(fmt, ##args)
#else
-#define K1212_DEBUG_PRINTK(fmt,...)
+#define K1212_DEBUG_PRINTK(fmt, ...) do { } while (0)
#endif
#if K1212_DEBUG_LEVEL > 1
-#define K1212_DEBUG_PRINTK_VERBOSE(fmt,args...) printk(KERN_DEBUG fmt,##args)
+#define K1212_DEBUG_PRINTK_VERBOSE(fmt, args...) pr_debug(fmt, ##args)
#else
-#define K1212_DEBUG_PRINTK_VERBOSE(fmt,...)
+#define K1212_DEBUG_PRINTK_VERBOSE(fmt, ...)
#endif
// ----------------------------------------------------------------------------
@@ -196,8 +181,8 @@ enum MonitorModeSelector {
#define K1212_ADAT_BUF_SIZE (K1212_ADAT_CHANNELS * 2 * kPlayBufferFrames * kNumBuffers)
#define K1212_MAX_BUF_SIZE (K1212_ANALOG_BUF_SIZE + K1212_ADAT_BUF_SIZE)
-#define k1212MinADCSens 0x7f
-#define k1212MaxADCSens 0x00
+#define k1212MinADCSens 0x00
+#define k1212MaxADCSens 0x7f
#define k1212MaxVolume 0x7fff
#define k1212MaxWaveVolume 0xffff
#define k1212MinVolume 0x0000
@@ -323,9 +308,6 @@ struct snd_korg1212 {
spinlock_t lock;
struct mutex open_mutex;
- struct timer_list timer; /* timer callback for checking ack of stop request */
- int stop_pending_cnt; /* counter for stop pending check */
-
wait_queue_head_t wait;
unsigned long iomem;
@@ -335,10 +317,10 @@ struct snd_korg1212 {
unsigned long inIRQ;
void __iomem *iobase;
- struct snd_dma_buffer dma_dsp;
- struct snd_dma_buffer dma_play;
- struct snd_dma_buffer dma_rec;
- struct snd_dma_buffer dma_shared;
+ struct snd_dma_buffer *dma_dsp;
+ struct snd_dma_buffer *dma_play;
+ struct snd_dma_buffer *dma_rec;
+ struct snd_dma_buffer *dma_shared;
u32 DataBufsSize;
@@ -397,18 +379,17 @@ struct snd_korg1212 {
unsigned long totalerrorcnt; // Total Error Count
int dsp_is_loaded;
- int dsp_stop_is_processed;
+ int dsp_stop_processing;
};
MODULE_DESCRIPTION("korg1212");
MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{KORG,korg1212}}");
MODULE_FIRMWARE("korg/k1212.dsp");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Korg 1212 soundcard.");
@@ -418,7 +399,7 @@ module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Korg 1212 soundcard.");
MODULE_AUTHOR("Haroldo Gamal <gamal@alternex.com.br>");
-static DEFINE_PCI_DEVICE_TABLE(snd_korg1212_ids) = {
+static const struct pci_device_id snd_korg1212_ids[] = {
{
.vendor = 0x10b5,
.device = 0x906d,
@@ -430,7 +411,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_korg1212_ids) = {
MODULE_DEVICE_TABLE(pci, snd_korg1212_ids);
-static char *stateName[] = {
+static const char * const stateName[] = {
"Non-existent",
"Uninitialized",
"DSP download in process",
@@ -444,9 +425,9 @@ static char *stateName[] = {
"Invalid"
};
-static char *clockSourceTypeName[] = { "ADAT", "S/PDIF", "local" };
+static const char * const clockSourceTypeName[] = { "ADAT", "S/PDIF", "local" };
-static char *clockSourceName[] = {
+static const char * const clockSourceName[] = {
"ADAT at 44.1 kHz",
"ADAT at 48 kHz",
"S/PDIF at 44.1 kHz",
@@ -455,7 +436,7 @@ static char *clockSourceName[] = {
"local clock at 48 kHz"
};
-static char *channelName[] = {
+static const char * const channelName[] = {
"ADAT-1",
"ADAT-2",
"ADAT-3",
@@ -470,7 +451,7 @@ static char *channelName[] = {
"SPDIF-R",
};
-static u16 ClockSourceSelector[] = {
+static const u16 ClockSourceSelector[] = {
0x8000, // selects source as ADAT at 44.1 kHz
0x0000, // selects source as ADAT at 48 kHz
0x8001, // selects source as S/PDIF at 44.1 kHz
@@ -581,68 +562,25 @@ static int snd_korg1212_Send1212Command(struct snd_korg1212 *korg1212,
/* spinlock already held */
static void snd_korg1212_SendStop(struct snd_korg1212 *korg1212)
{
- if (! korg1212->stop_pending_cnt) {
- korg1212->sharedBufferPtr->cardCommand = 0xffffffff;
- /* program the timer */
- korg1212->stop_pending_cnt = HZ;
- korg1212->timer.expires = jiffies + 1;
- add_timer(&korg1212->timer);
- }
+ korg1212->dsp_stop_processing = 1;
+ korg1212->sharedBufferPtr->cardCommand = 0xffffffff;
}
static void snd_korg1212_SendStopAndWait(struct snd_korg1212 *korg1212)
{
- unsigned long flags;
- spin_lock_irqsave(&korg1212->lock, flags);
- korg1212->dsp_stop_is_processed = 0;
- snd_korg1212_SendStop(korg1212);
- spin_unlock_irqrestore(&korg1212->lock, flags);
- wait_event_timeout(korg1212->wait, korg1212->dsp_stop_is_processed, (HZ * 3) / 2);
-}
-
-/* timer callback for checking the ack of stop request */
-static void snd_korg1212_timer_func(unsigned long data)
-{
- struct snd_korg1212 *korg1212 = (struct snd_korg1212 *) data;
- unsigned long flags;
-
- spin_lock_irqsave(&korg1212->lock, flags);
- if (korg1212->sharedBufferPtr->cardCommand == 0) {
- /* ack'ed */
- korg1212->stop_pending_cnt = 0;
- korg1212->dsp_stop_is_processed = 1;
- wake_up(&korg1212->wait);
- K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: Stop ack'ed [%s]\n",
- stateName[korg1212->cardState]);
- } else {
- if (--korg1212->stop_pending_cnt > 0) {
- /* reprogram timer */
- korg1212->timer.expires = jiffies + 1;
- add_timer(&korg1212->timer);
- } else {
- snd_printd("korg1212_timer_func timeout\n");
- korg1212->sharedBufferPtr->cardCommand = 0;
- korg1212->dsp_stop_is_processed = 1;
- wake_up(&korg1212->wait);
- K1212_DEBUG_PRINTK("K1212_DEBUG: Stop timeout [%s]\n",
- stateName[korg1212->cardState]);
- }
+ scoped_guard(spinlock_irqsave, &korg1212->lock) {
+ snd_korg1212_SendStop(korg1212);
}
- spin_unlock_irqrestore(&korg1212->lock, flags);
+ wait_event_timeout(korg1212->wait, !korg1212->dsp_stop_processing, HZ);
}
static int snd_korg1212_TurnOnIdleMonitor(struct snd_korg1212 *korg1212)
{
- unsigned long flags;
- int rc;
-
udelay(INTERCOMMAND_DELAY);
- spin_lock_irqsave(&korg1212->lock, flags);
+ guard(spinlock_irqsave)(&korg1212->lock);
korg1212->idleMonitorOn = 1;
- rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
- K1212_MODE_MonitorOn, 0, 0, 0);
- spin_unlock_irqrestore(&korg1212->lock, flags);
- return rc;
+ return snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
+ K1212_MODE_MonitorOn, 0, 0, 0);
}
static void snd_korg1212_TurnOffIdleMonitor(struct snd_korg1212 *korg1212)
@@ -662,13 +600,12 @@ static int snd_korg1212_OpenCard(struct snd_korg1212 * korg1212)
{
K1212_DEBUG_PRINTK("K1212_DEBUG: OpenCard [%s] %d\n",
stateName[korg1212->cardState], korg1212->opencnt);
- mutex_lock(&korg1212->open_mutex);
+ guard(mutex)(&korg1212->open_mutex);
if (korg1212->opencnt++ == 0) {
snd_korg1212_TurnOffIdleMonitor(korg1212);
snd_korg1212_setCardState(korg1212, K1212_STATE_OPEN);
}
- mutex_unlock(&korg1212->open_mutex);
return 1;
}
@@ -677,11 +614,9 @@ static int snd_korg1212_CloseCard(struct snd_korg1212 * korg1212)
K1212_DEBUG_PRINTK("K1212_DEBUG: CloseCard [%s] %d\n",
stateName[korg1212->cardState], korg1212->opencnt);
- mutex_lock(&korg1212->open_mutex);
- if (--(korg1212->opencnt)) {
- mutex_unlock(&korg1212->open_mutex);
+ guard(mutex)(&korg1212->open_mutex);
+ if (--(korg1212->opencnt))
return 0;
- }
if (korg1212->cardState == K1212_STATE_SETUP) {
int rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_SelectPlayMode,
@@ -689,10 +624,8 @@ static int snd_korg1212_CloseCard(struct snd_korg1212 * korg1212)
if (rc)
K1212_DEBUG_PRINTK("K1212_DEBUG: CloseCard - RC = %d [%s]\n",
rc, stateName[korg1212->cardState]);
- if (rc != K1212_CMDRET_Success) {
- mutex_unlock(&korg1212->open_mutex);
+ if (rc != K1212_CMDRET_Success)
return 0;
- }
} else if (korg1212->cardState > K1212_STATE_SETUP) {
snd_korg1212_SendStopAndWait(korg1212);
}
@@ -702,7 +635,6 @@ static int snd_korg1212_CloseCard(struct snd_korg1212 * korg1212)
snd_korg1212_setCardState(korg1212, K1212_STATE_READY);
}
- mutex_unlock(&korg1212->open_mutex);
return 0;
}
@@ -830,12 +762,12 @@ static inline int snd_korg1212_use_is_exclusive(struct snd_korg1212 *korg1212)
static int snd_korg1212_SetRate(struct snd_korg1212 *korg1212, int rate)
{
- static enum ClockSourceIndex s44[] = {
+ static const enum ClockSourceIndex s44[] = {
K1212_CLKIDX_AdatAt44_1K,
K1212_CLKIDX_WordAt44_1K,
K1212_CLKIDX_LocalAt44_1K
};
- static enum ClockSourceIndex s48[] = {
+ static const enum ClockSourceIndex s48[] = {
K1212_CLKIDX_AdatAt48K,
K1212_CLKIDX_WordAt48K,
K1212_CLKIDX_LocalAt48K
@@ -900,7 +832,6 @@ static int snd_korg1212_WriteADCSensitivity(struct snd_korg1212 *korg1212)
u16 controlValue; // this keeps the current value to be written to
// the card's eeprom control register.
u16 count;
- unsigned long flags;
K1212_DEBUG_PRINTK("K1212_DEBUG: WriteADCSensivity [%s]\n",
stateName[korg1212->cardState]);
@@ -921,7 +852,7 @@ static int snd_korg1212_WriteADCSensitivity(struct snd_korg1212 *korg1212)
} else
monModeSet = 0;
- spin_lock_irqsave(&korg1212->lock, flags);
+ guard(spinlock_irqsave)(&korg1212->lock);
// ----------------------------------------------------------------------------
// we are about to send new values to the card, so clear the new values queued
@@ -1030,8 +961,6 @@ static int snd_korg1212_WriteADCSensitivity(struct snd_korg1212 *korg1212)
rc, stateName[korg1212->cardState]);
}
- spin_unlock_irqrestore(&korg1212->lock, flags);
-
return 1;
}
@@ -1123,7 +1052,7 @@ static irqreturn_t snd_korg1212_interrupt(int irq, void *dev_id)
if (!doorbellValue)
return IRQ_NONE;
- spin_lock(&korg1212->lock);
+ guard(spinlock)(&korg1212->lock);
writel(doorbellValue, korg1212->inDoorbellPtr);
@@ -1149,11 +1078,13 @@ static irqreturn_t snd_korg1212_interrupt(int irq, void *dev_id)
K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: IRQ DMAE count - %ld, %x, [%s].\n",
korg1212->irqcount, doorbellValue,
stateName[korg1212->cardState]);
- snd_printk(KERN_ERR "korg1212: DMA Error\n");
+ dev_err(korg1212->card->dev, "korg1212: DMA Error\n");
korg1212->errorcnt++;
korg1212->totalerrorcnt++;
korg1212->sharedBufferPtr->cardCommand = 0;
+ korg1212->dsp_stop_processing = 0;
snd_korg1212_setCardState(korg1212, K1212_STATE_ERRORSTOP);
+ wake_up(&korg1212->wait);
break;
// ------------------------------------------------------------------------
@@ -1165,6 +1096,8 @@ static irqreturn_t snd_korg1212_interrupt(int irq, void *dev_id)
korg1212->irqcount, doorbellValue,
stateName[korg1212->cardState]);
korg1212->sharedBufferPtr->cardCommand = 0;
+ korg1212->dsp_stop_processing = 0;
+ wake_up(&korg1212->wait);
break;
default:
@@ -1197,8 +1130,6 @@ static irqreturn_t snd_korg1212_interrupt(int irq, void *dev_id)
korg1212->inIRQ--;
- spin_unlock(&korg1212->lock);
-
return IRQ_HANDLED;
}
@@ -1218,8 +1149,8 @@ static int snd_korg1212_downloadDSPCode(struct snd_korg1212 *korg1212)
snd_korg1212_setCardState(korg1212, K1212_STATE_DSP_IN_PROCESS);
rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_StartDSPDownload,
- UpperWordSwap(korg1212->dma_dsp.addr),
- 0, 0, 0);
+ UpperWordSwap(korg1212->dma_dsp->addr),
+ 0, 0, 0);
if (rc)
K1212_DEBUG_PRINTK("K1212_DEBUG: Start DSP Download RC = %d [%s]\n",
rc, stateName[korg1212->cardState]);
@@ -1234,7 +1165,7 @@ static int snd_korg1212_downloadDSPCode(struct snd_korg1212 *korg1212)
return 0;
}
-static struct snd_pcm_hardware snd_korg1212_playback_info =
+static const struct snd_pcm_hardware snd_korg1212_playback_info =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -1255,7 +1186,7 @@ static struct snd_pcm_hardware snd_korg1212_playback_info =
.fifo_size = 0,
};
-static struct snd_pcm_hardware snd_korg1212_capture_info =
+static const struct snd_pcm_hardware snd_korg1212_capture_info =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -1290,8 +1221,8 @@ static int snd_korg1212_silence(struct snd_korg1212 *korg1212, int pos, int coun
#if K1212_DEBUG_LEVEL > 0
if ( (void *) dst < (void *) korg1212->playDataBufsPtr ||
(void *) dst > (void *) korg1212->playDataBufsPtr[8].bufferData ) {
- printk(KERN_DEBUG "K1212_DEBUG: snd_korg1212_silence KERNEL EFAULT dst=%p iter=%d\n",
- dst, i);
+ pr_debug("K1212_DEBUG: %s KERNEL EFAULT dst=%p iter=%d\n",
+ __func__, dst, i);
return -EFAULT;
}
#endif
@@ -1302,13 +1233,20 @@ static int snd_korg1212_silence(struct snd_korg1212 *korg1212, int pos, int coun
return 0;
}
-static int snd_korg1212_copy_to(struct snd_korg1212 *korg1212, void __user *dst, int pos, int count, int offset, int size)
+static int snd_korg1212_copy_to(struct snd_pcm_substream *substream,
+ struct iov_iter *dst, int pos, int count)
{
- struct KorgAudioFrame * src = korg1212->recordDataBufsPtr[0].bufferData + pos;
- int i, rc;
-
- K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_to pos=%d offset=%d size=%d\n",
- pos, offset, size);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
+ struct KorgAudioFrame *src;
+ int i, size;
+
+ pos = bytes_to_frames(runtime, pos);
+ count = bytes_to_frames(runtime, count);
+ size = korg1212->channels * 2;
+ src = korg1212->recordDataBufsPtr[0].bufferData + pos;
+ K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_to pos=%d size=%d count=%d\n",
+ pos, size, count);
if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES))
return -EINVAL;
@@ -1316,29 +1254,34 @@ static int snd_korg1212_copy_to(struct snd_korg1212 *korg1212, void __user *dst,
#if K1212_DEBUG_LEVEL > 0
if ( (void *) src < (void *) korg1212->recordDataBufsPtr ||
(void *) src > (void *) korg1212->recordDataBufsPtr[8].bufferData ) {
- printk(KERN_DEBUG "K1212_DEBUG: snd_korg1212_copy_to KERNEL EFAULT, src=%p dst=%p iter=%d\n", src, dst, i);
+ pr_debug("K1212_DEBUG: %s KERNEL EFAULT, src=%p dst=%p iter=%d\n",
+ __func__, src, dst->kvec.iov_base, i);
return -EFAULT;
}
#endif
- rc = copy_to_user(dst + offset, src, size);
- if (rc) {
- K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_to USER EFAULT src=%p dst=%p iter=%d\n", src, dst, i);
+ if (copy_to_iter(src, size, dst) != size)
return -EFAULT;
- }
src++;
- dst += size;
}
return 0;
}
-static int snd_korg1212_copy_from(struct snd_korg1212 *korg1212, void __user *src, int pos, int count, int offset, int size)
+static int snd_korg1212_copy_from(struct snd_pcm_substream *substream,
+ struct iov_iter *src, int pos, int count)
{
- struct KorgAudioFrame * dst = korg1212->playDataBufsPtr[0].bufferData + pos;
- int i, rc;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
+ struct KorgAudioFrame *dst;
+ int i, size;
- K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_from pos=%d offset=%d size=%d count=%d\n",
- pos, offset, size, count);
+ pos = bytes_to_frames(runtime, pos);
+ count = bytes_to_frames(runtime, count);
+ size = korg1212->channels * 2;
+ dst = korg1212->playDataBufsPtr[0].bufferData + pos;
+
+ K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: %s pos=%d size=%d count=%d\n",
+ __func__, pos, size, count);
if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES))
return -EINVAL;
@@ -1347,17 +1290,14 @@ static int snd_korg1212_copy_from(struct snd_korg1212 *korg1212, void __user *sr
#if K1212_DEBUG_LEVEL > 0
if ( (void *) dst < (void *) korg1212->playDataBufsPtr ||
(void *) dst > (void *) korg1212->playDataBufsPtr[8].bufferData ) {
- printk(KERN_DEBUG "K1212_DEBUG: snd_korg1212_copy_from KERNEL EFAULT, src=%p dst=%p iter=%d\n", src, dst, i);
+ pr_debug("K1212_DEBUG: %s KERNEL EFAULT, src=%p dst=%p iter=%d\n",
+ __func__, src->kvec.iov_base, dst, i);
return -EFAULT;
}
#endif
- rc = copy_from_user((void*) dst + offset, src, size);
- if (rc) {
- K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_from USER EFAULT src=%p dst=%p iter=%d\n", src, dst, i);
+ if (copy_from_iter(dst, size, src) != size)
return -EFAULT;
- }
dst++;
- src += size;
}
return 0;
@@ -1375,7 +1315,6 @@ static void snd_korg1212_free_pcm(struct snd_pcm *pcm)
static int snd_korg1212_playback_open(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -1385,26 +1324,25 @@ static int snd_korg1212_playback_open(struct snd_pcm_substream *substream)
snd_korg1212_OpenCard(korg1212);
runtime->hw = snd_korg1212_playback_info;
- snd_pcm_set_runtime_buffer(substream, &korg1212->dma_play);
-
- spin_lock_irqsave(&korg1212->lock, flags);
-
- korg1212->playback_substream = substream;
- korg1212->playback_pid = current->pid;
- korg1212->periodsize = K1212_PERIODS;
- korg1212->channels = K1212_CHANNELS;
- korg1212->errorcnt = 0;
+ snd_pcm_set_runtime_buffer(substream, korg1212->dma_play);
+
+ scoped_guard(spinlock_irqsave, &korg1212->lock) {
+ korg1212->playback_substream = substream;
+ korg1212->playback_pid = current->pid;
+ korg1212->periodsize = K1212_PERIODS;
+ korg1212->channels = K1212_CHANNELS;
+ korg1212->errorcnt = 0;
+ }
- spin_unlock_irqrestore(&korg1212->lock, flags);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ kPlayBufferFrames);
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, kPlayBufferFrames, kPlayBufferFrames);
return 0;
}
static int snd_korg1212_capture_open(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -1414,25 +1352,22 @@ static int snd_korg1212_capture_open(struct snd_pcm_substream *substream)
snd_korg1212_OpenCard(korg1212);
runtime->hw = snd_korg1212_capture_info;
- snd_pcm_set_runtime_buffer(substream, &korg1212->dma_rec);
+ snd_pcm_set_runtime_buffer(substream, korg1212->dma_rec);
- spin_lock_irqsave(&korg1212->lock, flags);
-
- korg1212->capture_substream = substream;
- korg1212->capture_pid = current->pid;
- korg1212->periodsize = K1212_PERIODS;
- korg1212->channels = K1212_CHANNELS;
-
- spin_unlock_irqrestore(&korg1212->lock, flags);
+ scoped_guard(spinlock_irqsave, &korg1212->lock) {
+ korg1212->capture_substream = substream;
+ korg1212->capture_pid = current->pid;
+ korg1212->periodsize = K1212_PERIODS;
+ korg1212->channels = K1212_CHANNELS;
+ }
- snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- kPlayBufferFrames, kPlayBufferFrames);
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ kPlayBufferFrames);
return 0;
}
static int snd_korg1212_playback_close(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_playback_close [%s]\n",
@@ -1440,13 +1375,11 @@ static int snd_korg1212_playback_close(struct snd_pcm_substream *substream)
snd_korg1212_silence(korg1212, 0, K1212_MAX_SAMPLES, 0, korg1212->channels * 2);
- spin_lock_irqsave(&korg1212->lock, flags);
-
- korg1212->playback_pid = -1;
- korg1212->playback_substream = NULL;
- korg1212->periodsize = 0;
-
- spin_unlock_irqrestore(&korg1212->lock, flags);
+ scoped_guard(spinlock_irqsave, &korg1212->lock) {
+ korg1212->playback_pid = -1;
+ korg1212->playback_substream = NULL;
+ korg1212->periodsize = 0;
+ }
snd_korg1212_CloseCard(korg1212);
return 0;
@@ -1454,19 +1387,16 @@ static int snd_korg1212_playback_close(struct snd_pcm_substream *substream)
static int snd_korg1212_capture_close(struct snd_pcm_substream *substream)
{
- unsigned long flags;
struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_capture_close [%s]\n",
stateName[korg1212->cardState]);
- spin_lock_irqsave(&korg1212->lock, flags);
-
- korg1212->capture_pid = -1;
- korg1212->capture_substream = NULL;
- korg1212->periodsize = 0;
-
- spin_unlock_irqrestore(&korg1212->lock, flags);
+ scoped_guard(spinlock_irqsave, &korg1212->lock) {
+ korg1212->capture_pid = -1;
+ korg1212->capture_substream = NULL;
+ korg1212->periodsize = 0;
+ }
snd_korg1212_CloseCard(korg1212);
return 0;
@@ -1492,7 +1422,6 @@ static int snd_korg1212_ioctl(struct snd_pcm_substream *substream,
static int snd_korg1212_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- unsigned long flags;
struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
int err;
pid_t this_pid;
@@ -1501,7 +1430,7 @@ static int snd_korg1212_hw_params(struct snd_pcm_substream *substream,
K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_hw_params [%s]\n",
stateName[korg1212->cardState]);
- spin_lock_irqsave(&korg1212->lock, flags);
+ guard(spinlock_irqsave)(&korg1212->lock);
if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
this_pid = korg1212->playback_pid;
@@ -1519,28 +1448,31 @@ static int snd_korg1212_hw_params(struct snd_pcm_substream *substream,
*/
if ((int)params_rate(params) != korg1212->clkRate) {
- spin_unlock_irqrestore(&korg1212->lock, flags);
_snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE);
return -EBUSY;
}
- spin_unlock_irqrestore(&korg1212->lock, flags);
return 0;
}
- if ((err = snd_korg1212_SetRate(korg1212, params_rate(params))) < 0) {
- spin_unlock_irqrestore(&korg1212->lock, flags);
+ err = snd_korg1212_SetRate(korg1212, params_rate(params));
+ if (err < 0)
return err;
- }
korg1212->channels = params_channels(params);
korg1212->periodsize = K1212_PERIOD_BYTES;
- spin_unlock_irqrestore(&korg1212->lock, flags);
-
return 0;
}
+static int snd_korg1212_sync_stop(struct snd_pcm_substream *substream)
+{
+ struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
+
+ wait_event_timeout(korg1212->wait, !korg1212->dsp_stop_processing, HZ);
+ return 0;
+}
+
static int snd_korg1212_prepare(struct snd_pcm_substream *substream)
{
struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
@@ -1549,27 +1481,13 @@ static int snd_korg1212_prepare(struct snd_pcm_substream *substream)
K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_prepare [%s]\n",
stateName[korg1212->cardState]);
- spin_lock_irq(&korg1212->lock);
-
- /* FIXME: we should wait for ack! */
- if (korg1212->stop_pending_cnt > 0) {
- K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_prepare - Stop is pending... [%s]\n",
- stateName[korg1212->cardState]);
- spin_unlock_irq(&korg1212->lock);
- return -EAGAIN;
- /*
- korg1212->sharedBufferPtr->cardCommand = 0;
- del_timer(&korg1212->timer);
- korg1212->stop_pending_cnt = 0;
- */
- }
+ guard(spinlock_irq)(&korg1212->lock);
+ korg1212->dsp_stop_processing = 0;
rc = snd_korg1212_SetupForPlay(korg1212);
korg1212->currentBuffer = 0;
- spin_unlock_irq(&korg1212->lock);
-
return rc ? -EINVAL : 0;
}
@@ -1582,7 +1500,7 @@ static int snd_korg1212_trigger(struct snd_pcm_substream *substream,
K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_trigger [%s] cmd=%d\n",
stateName[korg1212->cardState], cmd);
- spin_lock(&korg1212->lock);
+ guard(spinlock)(&korg1212->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
/*
@@ -1610,7 +1528,6 @@ static int snd_korg1212_trigger(struct snd_pcm_substream *substream,
rc = 1;
break;
}
- spin_unlock(&korg1212->lock);
return rc ? -EINVAL : 0;
}
@@ -1641,66 +1558,53 @@ static snd_pcm_uframes_t snd_korg1212_capture_pointer(struct snd_pcm_substream *
}
static int snd_korg1212_playback_copy(struct snd_pcm_substream *substream,
- int channel, /* not used (interleaved data) */
- snd_pcm_uframes_t pos,
- void __user *src,
- snd_pcm_uframes_t count)
+ int channel, unsigned long pos,
+ struct iov_iter *src, unsigned long count)
{
- struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
-
- K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_playback_copy [%s] %ld %ld\n",
- stateName[korg1212->cardState], pos, count);
-
- return snd_korg1212_copy_from(korg1212, src, pos, count, 0, korg1212->channels * 2);
-
+ return snd_korg1212_copy_from(substream, src, pos, count);
}
static int snd_korg1212_playback_silence(struct snd_pcm_substream *substream,
int channel, /* not used (interleaved data) */
- snd_pcm_uframes_t pos,
- snd_pcm_uframes_t count)
+ unsigned long pos,
+ unsigned long count)
{
+ struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
- K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_playback_silence [%s]\n",
- stateName[korg1212->cardState]);
-
- return snd_korg1212_silence(korg1212, pos, count, 0, korg1212->channels * 2);
+ return snd_korg1212_silence(korg1212, bytes_to_frames(runtime, pos),
+ bytes_to_frames(runtime, count),
+ 0, korg1212->channels * 2);
}
static int snd_korg1212_capture_copy(struct snd_pcm_substream *substream,
- int channel, /* not used (interleaved data) */
- snd_pcm_uframes_t pos,
- void __user *dst,
- snd_pcm_uframes_t count)
+ int channel, unsigned long pos,
+ struct iov_iter *dst, unsigned long count)
{
- struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
-
- K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_capture_copy [%s] %ld %ld\n",
- stateName[korg1212->cardState], pos, count);
-
- return snd_korg1212_copy_to(korg1212, dst, pos, count, 0, korg1212->channels * 2);
+ return snd_korg1212_copy_to(substream, dst, pos, count);
}
-static struct snd_pcm_ops snd_korg1212_playback_ops = {
+static const struct snd_pcm_ops snd_korg1212_playback_ops = {
.open = snd_korg1212_playback_open,
.close = snd_korg1212_playback_close,
.ioctl = snd_korg1212_ioctl,
.hw_params = snd_korg1212_hw_params,
.prepare = snd_korg1212_prepare,
.trigger = snd_korg1212_trigger,
+ .sync_stop = snd_korg1212_sync_stop,
.pointer = snd_korg1212_playback_pointer,
- .copy = snd_korg1212_playback_copy,
- .silence = snd_korg1212_playback_silence,
+ .copy = snd_korg1212_playback_copy,
+ .fill_silence = snd_korg1212_playback_silence,
};
-static struct snd_pcm_ops snd_korg1212_capture_ops = {
+static const struct snd_pcm_ops snd_korg1212_capture_ops = {
.open = snd_korg1212_capture_open,
.close = snd_korg1212_capture_close,
.ioctl = snd_korg1212_ioctl,
.hw_params = snd_korg1212_hw_params,
.prepare = snd_korg1212_prepare,
.trigger = snd_korg1212_trigger,
+ .sync_stop = snd_korg1212_sync_stop,
.pointer = snd_korg1212_capture_pointer,
.copy = snd_korg1212_capture_copy,
};
@@ -1723,15 +1627,13 @@ static int snd_korg1212_control_phase_get(struct snd_kcontrol *kcontrol,
struct snd_korg1212 *korg1212 = snd_kcontrol_chip(kcontrol);
int i = kcontrol->private_value;
- spin_lock_irq(&korg1212->lock);
+ guard(spinlock_irq)(&korg1212->lock);
u->value.integer.value[0] = korg1212->volumePhase[i];
if (i >= 8)
u->value.integer.value[1] = korg1212->volumePhase[i+1];
- spin_unlock_irq(&korg1212->lock);
-
return 0;
}
@@ -1742,7 +1644,7 @@ static int snd_korg1212_control_phase_put(struct snd_kcontrol *kcontrol,
int change = 0;
int i, val;
- spin_lock_irq(&korg1212->lock);
+ guard(spinlock_irq)(&korg1212->lock);
i = kcontrol->private_value;
@@ -1768,8 +1670,6 @@ static int snd_korg1212_control_phase_put(struct snd_kcontrol *kcontrol,
}
}
- spin_unlock_irq(&korg1212->lock);
-
return change;
}
@@ -1789,7 +1689,7 @@ static int snd_korg1212_control_volume_get(struct snd_kcontrol *kcontrol,
struct snd_korg1212 *korg1212 = snd_kcontrol_chip(kcontrol);
int i;
- spin_lock_irq(&korg1212->lock);
+ guard(spinlock_irq)(&korg1212->lock);
i = kcontrol->private_value;
u->value.integer.value[0] = abs(korg1212->sharedBufferPtr->volumeData[i]);
@@ -1797,8 +1697,6 @@ static int snd_korg1212_control_volume_get(struct snd_kcontrol *kcontrol,
if (i >= 8)
u->value.integer.value[1] = abs(korg1212->sharedBufferPtr->volumeData[i+1]);
- spin_unlock_irq(&korg1212->lock);
-
return 0;
}
@@ -1810,7 +1708,7 @@ static int snd_korg1212_control_volume_put(struct snd_kcontrol *kcontrol,
int i;
int val;
- spin_lock_irq(&korg1212->lock);
+ guard(spinlock_irq)(&korg1212->lock);
i = kcontrol->private_value;
@@ -1836,22 +1734,15 @@ static int snd_korg1212_control_volume_put(struct snd_kcontrol *kcontrol,
}
}
- spin_unlock_irq(&korg1212->lock);
-
return change;
}
static int snd_korg1212_control_route_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1;
- uinfo->value.enumerated.items = kAudioChannels;
- if (uinfo->value.enumerated.item > kAudioChannels-1) {
- uinfo->value.enumerated.item = kAudioChannels-1;
- }
- strcpy(uinfo->value.enumerated.name, channelName[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo,
+ (kcontrol->private_value >= 8) ? 2 : 1,
+ kAudioChannels, channelName);
}
static int snd_korg1212_control_route_get(struct snd_kcontrol *kcontrol,
@@ -1860,7 +1751,7 @@ static int snd_korg1212_control_route_get(struct snd_kcontrol *kcontrol,
struct snd_korg1212 *korg1212 = snd_kcontrol_chip(kcontrol);
int i;
- spin_lock_irq(&korg1212->lock);
+ guard(spinlock_irq)(&korg1212->lock);
i = kcontrol->private_value;
u->value.enumerated.item[0] = korg1212->sharedBufferPtr->routeData[i];
@@ -1868,8 +1759,6 @@ static int snd_korg1212_control_route_get(struct snd_kcontrol *kcontrol,
if (i >= 8)
u->value.enumerated.item[1] = korg1212->sharedBufferPtr->routeData[i+1];
- spin_unlock_irq(&korg1212->lock);
-
return 0;
}
@@ -1879,7 +1768,7 @@ static int snd_korg1212_control_route_put(struct snd_kcontrol *kcontrol,
struct snd_korg1212 *korg1212 = snd_kcontrol_chip(kcontrol);
int change = 0, i;
- spin_lock_irq(&korg1212->lock);
+ guard(spinlock_irq)(&korg1212->lock);
i = kcontrol->private_value;
@@ -1899,8 +1788,6 @@ static int snd_korg1212_control_route_put(struct snd_kcontrol *kcontrol,
}
}
- spin_unlock_irq(&korg1212->lock);
-
return change;
}
@@ -1919,13 +1806,11 @@ static int snd_korg1212_control_get(struct snd_kcontrol *kcontrol,
{
struct snd_korg1212 *korg1212 = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&korg1212->lock);
+ guard(spinlock_irq)(&korg1212->lock);
u->value.integer.value[0] = korg1212->leftADCInSens;
u->value.integer.value[1] = korg1212->rightADCInSens;
- spin_unlock_irq(&korg1212->lock);
-
return 0;
}
@@ -1935,22 +1820,20 @@ static int snd_korg1212_control_put(struct snd_kcontrol *kcontrol,
struct snd_korg1212 *korg1212 = snd_kcontrol_chip(kcontrol);
int change = 0;
- spin_lock_irq(&korg1212->lock);
-
- if (u->value.integer.value[0] >= k1212MinADCSens &&
- u->value.integer.value[0] <= k1212MaxADCSens &&
- u->value.integer.value[0] != korg1212->leftADCInSens) {
- korg1212->leftADCInSens = u->value.integer.value[0];
- change = 1;
- }
- if (u->value.integer.value[1] >= k1212MinADCSens &&
- u->value.integer.value[1] <= k1212MaxADCSens &&
- u->value.integer.value[1] != korg1212->rightADCInSens) {
- korg1212->rightADCInSens = u->value.integer.value[1];
- change = 1;
- }
-
- spin_unlock_irq(&korg1212->lock);
+ scoped_guard(spinlock_irq, &korg1212->lock) {
+ if (u->value.integer.value[0] >= k1212MinADCSens &&
+ u->value.integer.value[0] <= k1212MaxADCSens &&
+ u->value.integer.value[0] != korg1212->leftADCInSens) {
+ korg1212->leftADCInSens = u->value.integer.value[0];
+ change = 1;
+ }
+ if (u->value.integer.value[1] >= k1212MinADCSens &&
+ u->value.integer.value[1] <= k1212MaxADCSens &&
+ u->value.integer.value[1] != korg1212->rightADCInSens) {
+ korg1212->rightADCInSens = u->value.integer.value[1];
+ change = 1;
+ }
+ }
if (change)
snd_korg1212_WriteADCSensitivity(korg1212);
@@ -1961,14 +1844,7 @@ static int snd_korg1212_control_put(struct snd_kcontrol *kcontrol,
static int snd_korg1212_control_sync_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
- if (uinfo->value.enumerated.item > 2) {
- uinfo->value.enumerated.item = 2;
- }
- strcpy(uinfo->value.enumerated.name, clockSourceTypeName[uinfo->value.enumerated.item]);
- return 0;
+ return snd_ctl_enum_info(uinfo, 1, 3, clockSourceTypeName);
}
static int snd_korg1212_control_sync_get(struct snd_kcontrol *kcontrol,
@@ -1976,11 +1852,9 @@ static int snd_korg1212_control_sync_get(struct snd_kcontrol *kcontrol,
{
struct snd_korg1212 *korg1212 = snd_kcontrol_chip(kcontrol);
- spin_lock_irq(&korg1212->lock);
+ guard(spinlock_irq)(&korg1212->lock);
ucontrol->value.enumerated.item[0] = korg1212->clkSource;
-
- spin_unlock_irq(&korg1212->lock);
return 0;
}
@@ -1992,10 +1866,9 @@ static int snd_korg1212_control_sync_put(struct snd_kcontrol *kcontrol,
int change;
val = ucontrol->value.enumerated.item[0] % 3;
- spin_lock_irq(&korg1212->lock);
+ guard(spinlock_irq)(&korg1212->lock);
change = val != korg1212->clkSource;
snd_korg1212_SetClockSource(korg1212, val);
- spin_unlock_irq(&korg1212->lock);
return change;
}
@@ -2028,7 +1901,7 @@ static int snd_korg1212_control_sync_put(struct snd_kcontrol *kcontrol,
.private_value = ord, \
}
-static struct snd_kcontrol_new snd_korg1212_controls[] = {
+static const struct snd_kcontrol_new snd_korg1212_controls[] = {
MON_MIXER(8, "Analog"),
MON_MIXER(10, "SPDIF"),
MON_MIXER(0, "ADAT-1"), MON_MIXER(1, "ADAT-2"), MON_MIXER(2, "ADAT-3"), MON_MIXER(3, "ADAT-4"),
@@ -2064,7 +1937,7 @@ static void snd_korg1212_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, korg1212->card->longname);
snd_iprintf(buffer, " (index #%d)\n", korg1212->card->number + 1);
snd_iprintf(buffer, "\nGeneral settings\n");
- snd_iprintf(buffer, " period size: %Zd bytes\n", K1212_PERIOD_BYTES);
+ snd_iprintf(buffer, " period size: %zd bytes\n", K1212_PERIOD_BYTES);
snd_iprintf(buffer, " clock mode: %s\n", clockSourceName[korg1212->clkSrcRate] );
snd_iprintf(buffer, " left ADC Sens: %d\n", korg1212->leftADCInSens );
snd_iprintf(buffer, " right ADC Sens: %d\n", korg1212->rightADCInSens );
@@ -2083,110 +1956,42 @@ static void snd_korg1212_proc_read(struct snd_info_entry *entry,
snd_iprintf(buffer, " Error count: %ld\n", korg1212->totalerrorcnt);
}
-static void __devinit snd_korg1212_proc_init(struct snd_korg1212 *korg1212)
+static void snd_korg1212_proc_init(struct snd_korg1212 *korg1212)
{
- struct snd_info_entry *entry;
-
- if (! snd_card_proc_new(korg1212->card, "korg1212", &entry))
- snd_info_set_text_ops(entry, korg1212, snd_korg1212_proc_read);
+ snd_card_ro_proc_new(korg1212->card, "korg1212", korg1212,
+ snd_korg1212_proc_read);
}
-static int
-snd_korg1212_free(struct snd_korg1212 *korg1212)
+static void
+snd_korg1212_free(struct snd_card *card)
{
- snd_korg1212_TurnOffIdleMonitor(korg1212);
-
- if (korg1212->irq >= 0) {
- snd_korg1212_DisableCardInterrupts(korg1212);
- free_irq(korg1212->irq, korg1212);
- korg1212->irq = -1;
- }
-
- if (korg1212->iobase != NULL) {
- iounmap(korg1212->iobase);
- korg1212->iobase = NULL;
- }
-
- pci_release_regions(korg1212->pci);
-
- // ----------------------------------------------------
- // free up memory resources used for the DSP download.
- // ----------------------------------------------------
- if (korg1212->dma_dsp.area) {
- snd_dma_free_pages(&korg1212->dma_dsp);
- korg1212->dma_dsp.area = NULL;
- }
-
-#ifndef K1212_LARGEALLOC
-
- // ------------------------------------------------------
- // free up memory resources used for the Play/Rec Buffers
- // ------------------------------------------------------
- if (korg1212->dma_play.area) {
- snd_dma_free_pages(&korg1212->dma_play);
- korg1212->dma_play.area = NULL;
- }
-
- if (korg1212->dma_rec.area) {
- snd_dma_free_pages(&korg1212->dma_rec);
- korg1212->dma_rec.area = NULL;
- }
-
-#endif
-
- // ----------------------------------------------------
- // free up memory resources used for the Shared Buffers
- // ----------------------------------------------------
- if (korg1212->dma_shared.area) {
- snd_dma_free_pages(&korg1212->dma_shared);
- korg1212->dma_shared.area = NULL;
- }
-
- pci_disable_device(korg1212->pci);
- kfree(korg1212);
- return 0;
-}
+ struct snd_korg1212 *korg1212 = card->private_data;
-static int snd_korg1212_dev_free(struct snd_device *device)
-{
- struct snd_korg1212 *korg1212 = device->device_data;
- K1212_DEBUG_PRINTK("K1212_DEBUG: Freeing device\n");
- return snd_korg1212_free(korg1212);
+ snd_korg1212_TurnOffIdleMonitor(korg1212);
+ snd_korg1212_DisableCardInterrupts(korg1212);
}
-static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *pci,
- struct snd_korg1212 ** rchip)
+static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci)
{
int err, rc;
unsigned int i;
- unsigned ioport_size, iomem_size, iomem2_size;
- struct snd_korg1212 * korg1212;
+ __maybe_unused unsigned iomem_size;
+ __maybe_unused unsigned ioport_size;
+ __maybe_unused unsigned iomem2_size;
+ struct snd_korg1212 *korg1212 = card->private_data;
const struct firmware *dsp_code;
- static struct snd_device_ops ops = {
- .dev_free = snd_korg1212_dev_free,
- };
-
- * rchip = NULL;
- if ((err = pci_enable_device(pci)) < 0)
+ err = pcim_enable_device(pci);
+ if (err < 0)
return err;
- korg1212 = kzalloc(sizeof(*korg1212), GFP_KERNEL);
- if (korg1212 == NULL) {
- pci_disable_device(pci);
- return -ENOMEM;
- }
-
korg1212->card = card;
korg1212->pci = pci;
init_waitqueue_head(&korg1212->wait);
spin_lock_init(&korg1212->lock);
mutex_init(&korg1212->open_mutex);
- init_timer(&korg1212->timer);
- korg1212->timer.function = snd_korg1212_timer_func;
- korg1212->timer.data = (unsigned long)korg1212;
korg1212->irq = -1;
korg1212->clkSource = K1212_CLKIDX_Local;
@@ -2208,11 +2013,9 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
for (i=0; i<kAudioChannels; i++)
korg1212->volumePhase[i] = 0;
- if ((err = pci_request_regions(pci, "korg1212")) < 0) {
- kfree(korg1212);
- pci_disable_device(pci);
+ err = pcim_request_all_regions(pci, "korg1212");
+ if (err < 0)
return err;
- }
korg1212->iomem = pci_resource_start(korg1212->pci, 0);
korg1212->ioport = pci_resource_start(korg1212->pci, 1);
@@ -2232,24 +2035,22 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
korg1212->iomem2, iomem2_size,
stateName[korg1212->cardState]);
- if ((korg1212->iobase = ioremap(korg1212->iomem, iomem_size)) == NULL) {
- snd_printk(KERN_ERR "korg1212: unable to remap memory region 0x%lx-0x%lx\n", korg1212->iomem,
- korg1212->iomem + iomem_size - 1);
- snd_korg1212_free(korg1212);
- return -EBUSY;
- }
+ korg1212->iobase = pcim_iomap(pci, 0, 0);
+ if (!korg1212->iobase)
+ return -ENOMEM;
- err = request_irq(pci->irq, snd_korg1212_interrupt,
+ err = devm_request_irq(&pci->dev, pci->irq, snd_korg1212_interrupt,
IRQF_SHARED,
- "korg1212", korg1212);
+ KBUILD_MODNAME, korg1212);
if (err) {
- snd_printk(KERN_ERR "korg1212: unable to grab IRQ %d\n", pci->irq);
- snd_korg1212_free(korg1212);
+ dev_err(&pci->dev, "korg1212: unable to grab IRQ %d\n", pci->irq);
return -EBUSY;
}
korg1212->irq = pci->irq;
+ card->sync_irq = korg1212->irq;
+ card->private_free = snd_korg1212_free;
pci_set_master(korg1212->pci);
@@ -2288,41 +2089,36 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
korg1212->idRegPtr,
stateName[korg1212->cardState]);
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- sizeof(struct KorgSharedBuffer), &korg1212->dma_shared) < 0) {
- snd_printk(KERN_ERR "korg1212: can not allocate shared buffer memory (%Zd bytes)\n", sizeof(struct KorgSharedBuffer));
- snd_korg1212_free(korg1212);
- return -ENOMEM;
- }
- korg1212->sharedBufferPtr = (struct KorgSharedBuffer *)korg1212->dma_shared.area;
- korg1212->sharedBufferPhy = korg1212->dma_shared.addr;
+ korg1212->dma_shared = snd_devm_alloc_pages(&pci->dev,
+ SNDRV_DMA_TYPE_DEV,
+ sizeof(struct KorgSharedBuffer));
+ if (!korg1212->dma_shared)
+ return -ENOMEM;
+ korg1212->sharedBufferPtr = (struct KorgSharedBuffer *)korg1212->dma_shared->area;
+ korg1212->sharedBufferPhy = korg1212->dma_shared->addr;
K1212_DEBUG_PRINTK("K1212_DEBUG: Shared Buffer Area = 0x%p (0x%08lx), %d bytes\n", korg1212->sharedBufferPtr, korg1212->sharedBufferPhy, sizeof(struct KorgSharedBuffer));
#ifndef K1212_LARGEALLOC
-
korg1212->DataBufsSize = sizeof(struct KorgAudioBuffer) * kNumBuffers;
+ korg1212->dma_play = snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV,
+ korg1212->DataBufsSize);
+ if (!korg1212->dma_play)
+ return -ENOMEM;
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- korg1212->DataBufsSize, &korg1212->dma_play) < 0) {
- snd_printk(KERN_ERR "korg1212: can not allocate play data buffer memory (%d bytes)\n", korg1212->DataBufsSize);
- snd_korg1212_free(korg1212);
- return -ENOMEM;
- }
- korg1212->playDataBufsPtr = (struct KorgAudioBuffer *)korg1212->dma_play.area;
- korg1212->PlayDataPhy = korg1212->dma_play.addr;
+ korg1212->playDataBufsPtr = (struct KorgAudioBuffer *)korg1212->dma_play->area;
+ korg1212->PlayDataPhy = korg1212->dma_play->addr;
K1212_DEBUG_PRINTK("K1212_DEBUG: Play Data Area = 0x%p (0x%08x), %d bytes\n",
korg1212->playDataBufsPtr, korg1212->PlayDataPhy, korg1212->DataBufsSize);
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- korg1212->DataBufsSize, &korg1212->dma_rec) < 0) {
- snd_printk(KERN_ERR "korg1212: can not allocate record data buffer memory (%d bytes)\n", korg1212->DataBufsSize);
- snd_korg1212_free(korg1212);
- return -ENOMEM;
- }
- korg1212->recordDataBufsPtr = (struct KorgAudioBuffer *)korg1212->dma_rec.area;
- korg1212->RecDataPhy = korg1212->dma_rec.addr;
+ korg1212->dma_rec = snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV,
+ korg1212->DataBufsSize);
+ if (!korg1212->dma_rec)
+ return -ENOMEM;
+
+ korg1212->recordDataBufsPtr = (struct KorgAudioBuffer *)korg1212->dma_rec->area;
+ korg1212->RecDataPhy = korg1212->dma_rec->addr;
K1212_DEBUG_PRINTK("K1212_DEBUG: Record Data Area = 0x%p (0x%08x), %d bytes\n",
korg1212->recordDataBufsPtr, korg1212->RecDataPhy, korg1212->DataBufsSize);
@@ -2345,25 +2141,22 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
err = request_firmware(&dsp_code, "korg/k1212.dsp", &pci->dev);
if (err < 0) {
- release_firmware(dsp_code);
- snd_printk(KERN_ERR "firmware not available\n");
- snd_korg1212_free(korg1212);
+ dev_err(&pci->dev, "firmware not available\n");
return err;
}
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
- dsp_code->size, &korg1212->dma_dsp) < 0) {
- snd_printk(KERN_ERR "korg1212: cannot allocate dsp code memory (%zd bytes)\n", dsp_code->size);
- snd_korg1212_free(korg1212);
+ korg1212->dma_dsp = snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV,
+ dsp_code->size);
+ if (!korg1212->dma_dsp) {
release_firmware(dsp_code);
- return -ENOMEM;
- }
+ return -ENOMEM;
+ }
K1212_DEBUG_PRINTK("K1212_DEBUG: DSP Code area = 0x%p (0x%08x) %d bytes [%s]\n",
- korg1212->dma_dsp.area, korg1212->dma_dsp.addr, dsp_code->size,
+ korg1212->dma_dsp->area, korg1212->dma_dsp->addr, dsp_code->size,
stateName[korg1212->cardState]);
- memcpy(korg1212->dma_dsp.area, dsp_code->data, dsp_code->size);
+ memcpy(korg1212->dma_dsp->area, dsp_code->data, dsp_code->size);
release_firmware(dsp_code);
@@ -2372,11 +2165,6 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
if (rc)
K1212_DEBUG_PRINTK("K1212_DEBUG: Reboot Card - RC = %d [%s]\n", rc, stateName[korg1212->cardState]);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, korg1212, &ops)) < 0) {
- snd_korg1212_free(korg1212);
- return err;
- }
-
snd_korg1212_EnableCardInterrupts(korg1212);
mdelay(CARD_BOOT_DELAY_IN_MS);
@@ -2397,12 +2185,13 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
korg1212->RoutingTablePhy, LowerWordSwap(korg1212->RoutingTablePhy),
korg1212->AdatTimeCodePhy, LowerWordSwap(korg1212->AdatTimeCodePhy));
- if ((err = snd_pcm_new(korg1212->card, "korg1212", 0, 1, 1, &korg1212->pcm)) < 0)
+ err = snd_pcm_new(korg1212->card, "korg1212", 0, 1, 1, &korg1212->pcm);
+ if (err < 0)
return err;
korg1212->pcm->private_data = korg1212;
korg1212->pcm->private_free = snd_korg1212_free_pcm;
- strcpy(korg1212->pcm->name, "korg1212");
+ strscpy(korg1212->pcm->name, "korg1212");
snd_pcm_set_ops(korg1212->pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_korg1212_playback_ops);
@@ -2417,19 +2206,15 @@ static int __devinit snd_korg1212_create(struct snd_card *card, struct pci_dev *
}
snd_korg1212_proc_init(korg1212);
-
- snd_card_set_dev(card, &pci->dev);
- * rchip = korg1212;
return 0;
-
}
/*
* Card initialisation
*/
-static int __devinit
+static int
snd_korg1212_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
@@ -2445,53 +2230,39 @@ snd_korg1212_probe(struct pci_dev *pci,
dev++;
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*korg1212), &card);
if (err < 0)
return err;
+ korg1212 = card->private_data;
- if ((err = snd_korg1212_create(card, pci, &korg1212)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_korg1212_create(card, pci);
+ if (err < 0)
+ goto error;
- strcpy(card->driver, "korg1212");
- strcpy(card->shortname, "korg1212");
+ strscpy(card->driver, "korg1212");
+ strscpy(card->shortname, "korg1212");
sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname,
korg1212->iomem, korg1212->irq);
K1212_DEBUG_PRINTK("K1212_DEBUG: %s\n", card->longname);
- if ((err = snd_card_register(card)) < 0) {
- snd_card_free(card);
- return err;
- }
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
pci_set_drvdata(pci, card);
dev++;
return 0;
-}
-static void __devexit snd_korg1212_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
+ error:
+ snd_card_free(card);
+ return err;
}
-static struct pci_driver driver = {
- .name = "korg1212",
+static struct pci_driver korg1212_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_korg1212_ids,
.probe = snd_korg1212_probe,
- .remove = __devexit_p(snd_korg1212_remove),
};
-static int __init alsa_card_korg1212_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_korg1212_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_korg1212_init)
-module_exit(alsa_card_korg1212_exit)
+module_pci_driver(korg1212_driver);
diff --git a/sound/pci/lola/Makefile b/sound/pci/lola/Makefile
new file mode 100644
index 000000000000..8fdb5e5a776b
--- /dev/null
+++ b/sound/pci/lola/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+snd-lola-y := lola.o lola_pcm.o lola_clock.o lola_mixer.o
+snd-lola-$(CONFIG_SND_DEBUG) += lola_proc.o
+
+obj-$(CONFIG_SND_LOLA) += snd-lola.o
diff --git a/sound/pci/lola/lola.c b/sound/pci/lola/lola.c
new file mode 100644
index 000000000000..34a3ba17deb4
--- /dev/null
+++ b/sound/pci/lola/lola.c
@@ -0,0 +1,714 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include "lola.h"
+
+/* Standard options */
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Digigram Lola driver.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Digigram Lola driver.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Digigram Lola driver.");
+
+/* Lola-specific options */
+
+/* for instance use always max granularity which is compatible
+ * with all sample rates
+ */
+static int granularity[SNDRV_CARDS] = {
+ [0 ... (SNDRV_CARDS - 1)] = LOLA_GRANULARITY_MAX
+};
+
+/* below a sample_rate of 16kHz the analogue audio quality is NOT excellent */
+static int sample_rate_min[SNDRV_CARDS] = {
+ [0 ... (SNDRV_CARDS - 1) ] = 16000
+};
+
+module_param_array(granularity, int, NULL, 0444);
+MODULE_PARM_DESC(granularity, "Granularity value");
+module_param_array(sample_rate_min, int, NULL, 0444);
+MODULE_PARM_DESC(sample_rate_min, "Minimal sample rate");
+
+/*
+ */
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Digigram Lola driver");
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+static int debug;
+module_param(debug, int, 0644);
+#define verbose_debug(fmt, args...) \
+ do { if (debug > 1) pr_debug(SFX fmt, ##args); } while (0)
+#else
+#define verbose_debug(fmt, args...)
+#endif
+
+/*
+ * pseudo-codec read/write via CORB/RIRB
+ */
+
+static int corb_send_verb(struct lola *chip, unsigned int nid,
+ unsigned int verb, unsigned int data,
+ unsigned int extdata)
+{
+ int ret = -EIO;
+
+ chip->last_cmd_nid = nid;
+ chip->last_verb = verb;
+ chip->last_data = data;
+ chip->last_extdata = extdata;
+ data |= (nid << 20) | (verb << 8);
+
+ guard(spinlock_irqsave)(&chip->reg_lock);
+ if (chip->rirb.cmds < LOLA_CORB_ENTRIES - 1) {
+ unsigned int wp = chip->corb.wp + 1;
+ wp %= LOLA_CORB_ENTRIES;
+ chip->corb.wp = wp;
+ chip->corb.buf[wp * 2] = cpu_to_le32(data);
+ chip->corb.buf[wp * 2 + 1] = cpu_to_le32(extdata);
+ lola_writew(chip, BAR0, CORBWP, wp);
+ chip->rirb.cmds++;
+ smp_wmb();
+ ret = 0;
+ }
+ return ret;
+}
+
+static void lola_queue_unsol_event(struct lola *chip, unsigned int res,
+ unsigned int res_ex)
+{
+ lola_update_ext_clock_freq(chip, res);
+}
+
+/* retrieve RIRB entry - called from interrupt handler */
+static void lola_update_rirb(struct lola *chip)
+{
+ unsigned int rp, wp;
+ u32 res, res_ex;
+
+ wp = lola_readw(chip, BAR0, RIRBWP);
+ if (wp == chip->rirb.wp)
+ return;
+ chip->rirb.wp = wp;
+
+ while (chip->rirb.rp != wp) {
+ chip->rirb.rp++;
+ chip->rirb.rp %= LOLA_CORB_ENTRIES;
+
+ rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
+ res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
+ res = le32_to_cpu(chip->rirb.buf[rp]);
+ if (res_ex & LOLA_RIRB_EX_UNSOL_EV)
+ lola_queue_unsol_event(chip, res, res_ex);
+ else if (chip->rirb.cmds) {
+ chip->res = res;
+ chip->res_ex = res_ex;
+ smp_wmb();
+ chip->rirb.cmds--;
+ }
+ }
+}
+
+static int rirb_get_response(struct lola *chip, unsigned int *val,
+ unsigned int *extval)
+{
+ unsigned long timeout;
+
+ again:
+ timeout = jiffies + msecs_to_jiffies(1000);
+ for (;;) {
+ if (chip->polling_mode) {
+ spin_lock_irq(&chip->reg_lock);
+ lola_update_rirb(chip);
+ spin_unlock_irq(&chip->reg_lock);
+ }
+ if (!chip->rirb.cmds) {
+ *val = chip->res;
+ if (extval)
+ *extval = chip->res_ex;
+ verbose_debug("get_response: %x, %x\n",
+ chip->res, chip->res_ex);
+ if (chip->res_ex & LOLA_RIRB_EX_ERROR) {
+ dev_warn(chip->card->dev, "RIRB ERROR: "
+ "NID=%x, verb=%x, data=%x, ext=%x\n",
+ chip->last_cmd_nid,
+ chip->last_verb, chip->last_data,
+ chip->last_extdata);
+ return -EIO;
+ }
+ return 0;
+ }
+ if (time_after(jiffies, timeout))
+ break;
+ udelay(20);
+ cond_resched();
+ }
+ dev_warn(chip->card->dev, "RIRB response error\n");
+ if (!chip->polling_mode) {
+ dev_warn(chip->card->dev, "switching to polling mode\n");
+ chip->polling_mode = 1;
+ goto again;
+ }
+ return -EIO;
+}
+
+/* aynchronous write of a codec verb with data */
+int lola_codec_write(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata)
+{
+ verbose_debug("codec_write NID=%x, verb=%x, data=%x, ext=%x\n",
+ nid, verb, data, extdata);
+ return corb_send_verb(chip, nid, verb, data, extdata);
+}
+
+/* write a codec verb with data and read the returned status */
+int lola_codec_read(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata,
+ unsigned int *val, unsigned int *extval)
+{
+ int err;
+
+ verbose_debug("codec_read NID=%x, verb=%x, data=%x, ext=%x\n",
+ nid, verb, data, extdata);
+ err = corb_send_verb(chip, nid, verb, data, extdata);
+ if (err < 0)
+ return err;
+ err = rirb_get_response(chip, val, extval);
+ return err;
+}
+
+/* flush all pending codec writes */
+int lola_codec_flush(struct lola *chip)
+{
+ unsigned int tmp;
+ return rirb_get_response(chip, &tmp, NULL);
+}
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t lola_interrupt(int irq, void *dev_id)
+{
+ struct lola *chip = dev_id;
+ unsigned int notify_ins, notify_outs, error_ins, error_outs;
+ int handled = 0;
+ int i;
+
+ notify_ins = notify_outs = error_ins = error_outs = 0;
+ spin_lock(&chip->reg_lock);
+ for (;;) {
+ unsigned int status, in_sts, out_sts;
+ unsigned int reg;
+
+ status = lola_readl(chip, BAR1, DINTSTS);
+ if (!status || status == -1)
+ break;
+
+ in_sts = lola_readl(chip, BAR1, DIINTSTS);
+ out_sts = lola_readl(chip, BAR1, DOINTSTS);
+
+ /* clear Input Interrupts */
+ for (i = 0; in_sts && i < chip->pcm[CAPT].num_streams; i++) {
+ if (!(in_sts & (1 << i)))
+ continue;
+ in_sts &= ~(1 << i);
+ reg = lola_dsd_read(chip, i, STS);
+ if (reg & LOLA_DSD_STS_DESE) /* error */
+ error_ins |= (1 << i);
+ if (reg & LOLA_DSD_STS_BCIS) /* notify */
+ notify_ins |= (1 << i);
+ /* clear */
+ lola_dsd_write(chip, i, STS, reg);
+ }
+
+ /* clear Output Interrupts */
+ for (i = 0; out_sts && i < chip->pcm[PLAY].num_streams; i++) {
+ if (!(out_sts & (1 << i)))
+ continue;
+ out_sts &= ~(1 << i);
+ reg = lola_dsd_read(chip, i + MAX_STREAM_IN_COUNT, STS);
+ if (reg & LOLA_DSD_STS_DESE) /* error */
+ error_outs |= (1 << i);
+ if (reg & LOLA_DSD_STS_BCIS) /* notify */
+ notify_outs |= (1 << i);
+ lola_dsd_write(chip, i + MAX_STREAM_IN_COUNT, STS, reg);
+ }
+
+ if (status & LOLA_DINT_CTRL) {
+ unsigned char rbsts; /* ring status is byte access */
+ rbsts = lola_readb(chip, BAR0, RIRBSTS);
+ rbsts &= LOLA_RIRB_INT_MASK;
+ if (rbsts)
+ lola_writeb(chip, BAR0, RIRBSTS, rbsts);
+ rbsts = lola_readb(chip, BAR0, CORBSTS);
+ rbsts &= LOLA_CORB_INT_MASK;
+ if (rbsts)
+ lola_writeb(chip, BAR0, CORBSTS, rbsts);
+
+ lola_update_rirb(chip);
+ }
+
+ if (status & (LOLA_DINT_FIFOERR | LOLA_DINT_MUERR)) {
+ /* clear global fifo error interrupt */
+ lola_writel(chip, BAR1, DINTSTS,
+ (status & (LOLA_DINT_FIFOERR | LOLA_DINT_MUERR)));
+ }
+ handled = 1;
+ }
+ spin_unlock(&chip->reg_lock);
+
+ lola_pcm_update(chip, &chip->pcm[CAPT], notify_ins);
+ lola_pcm_update(chip, &chip->pcm[PLAY], notify_outs);
+
+ return IRQ_RETVAL(handled);
+}
+
+
+/*
+ * controller
+ */
+static int reset_controller(struct lola *chip)
+{
+ unsigned int gctl = lola_readl(chip, BAR0, GCTL);
+ unsigned long end_time;
+
+ if (gctl) {
+ /* to be sure */
+ lola_writel(chip, BAR1, BOARD_MODE, 0);
+ return 0;
+ }
+
+ chip->cold_reset = 1;
+ lola_writel(chip, BAR0, GCTL, LOLA_GCTL_RESET);
+ end_time = jiffies + msecs_to_jiffies(200);
+ do {
+ msleep(1);
+ gctl = lola_readl(chip, BAR0, GCTL);
+ if (gctl)
+ break;
+ } while (time_before(jiffies, end_time));
+ if (!gctl) {
+ dev_err(chip->card->dev, "cannot reset controller\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static void lola_irq_enable(struct lola *chip)
+{
+ unsigned int val;
+
+ /* enalbe all I/O streams */
+ val = (1 << chip->pcm[PLAY].num_streams) - 1;
+ lola_writel(chip, BAR1, DOINTCTL, val);
+ val = (1 << chip->pcm[CAPT].num_streams) - 1;
+ lola_writel(chip, BAR1, DIINTCTL, val);
+
+ /* enable global irqs */
+ val = LOLA_DINT_GLOBAL | LOLA_DINT_CTRL | LOLA_DINT_FIFOERR |
+ LOLA_DINT_MUERR;
+ lola_writel(chip, BAR1, DINTCTL, val);
+}
+
+static void lola_irq_disable(struct lola *chip)
+{
+ lola_writel(chip, BAR1, DINTCTL, 0);
+ lola_writel(chip, BAR1, DIINTCTL, 0);
+ lola_writel(chip, BAR1, DOINTCTL, 0);
+}
+
+static int setup_corb_rirb(struct lola *chip)
+{
+ unsigned char tmp;
+ unsigned long end_time;
+
+ chip->rb = snd_devm_alloc_pages(&chip->pci->dev, SNDRV_DMA_TYPE_DEV,
+ PAGE_SIZE);
+ if (!chip->rb)
+ return -ENOMEM;
+
+ chip->corb.addr = chip->rb->addr;
+ chip->corb.buf = (__le32 *)chip->rb->area;
+ chip->rirb.addr = chip->rb->addr + 2048;
+ chip->rirb.buf = (__le32 *)(chip->rb->area + 2048);
+
+ /* disable ringbuffer DMAs */
+ lola_writeb(chip, BAR0, RIRBCTL, 0);
+ lola_writeb(chip, BAR0, CORBCTL, 0);
+
+ end_time = jiffies + msecs_to_jiffies(200);
+ do {
+ if (!lola_readb(chip, BAR0, RIRBCTL) &&
+ !lola_readb(chip, BAR0, CORBCTL))
+ break;
+ msleep(1);
+ } while (time_before(jiffies, end_time));
+
+ /* CORB set up */
+ lola_writel(chip, BAR0, CORBLBASE, (u32)chip->corb.addr);
+ lola_writel(chip, BAR0, CORBUBASE, upper_32_bits(chip->corb.addr));
+ /* set the corb size to 256 entries */
+ lola_writeb(chip, BAR0, CORBSIZE, 0x02);
+ /* set the corb write pointer to 0 */
+ lola_writew(chip, BAR0, CORBWP, 0);
+ /* reset the corb hw read pointer */
+ lola_writew(chip, BAR0, CORBRP, LOLA_RBRWP_CLR);
+ /* enable corb dma */
+ lola_writeb(chip, BAR0, CORBCTL, LOLA_RBCTL_DMA_EN);
+ /* clear flags if set */
+ tmp = lola_readb(chip, BAR0, CORBSTS) & LOLA_CORB_INT_MASK;
+ if (tmp)
+ lola_writeb(chip, BAR0, CORBSTS, tmp);
+ chip->corb.wp = 0;
+
+ /* RIRB set up */
+ lola_writel(chip, BAR0, RIRBLBASE, (u32)chip->rirb.addr);
+ lola_writel(chip, BAR0, RIRBUBASE, upper_32_bits(chip->rirb.addr));
+ /* set the rirb size to 256 entries */
+ lola_writeb(chip, BAR0, RIRBSIZE, 0x02);
+ /* reset the rirb hw write pointer */
+ lola_writew(chip, BAR0, RIRBWP, LOLA_RBRWP_CLR);
+ /* set N=1, get RIRB response interrupt for new entry */
+ lola_writew(chip, BAR0, RINTCNT, 1);
+ /* enable rirb dma and response irq */
+ lola_writeb(chip, BAR0, RIRBCTL, LOLA_RBCTL_DMA_EN | LOLA_RBCTL_IRQ_EN);
+ /* clear flags if set */
+ tmp = lola_readb(chip, BAR0, RIRBSTS) & LOLA_RIRB_INT_MASK;
+ if (tmp)
+ lola_writeb(chip, BAR0, RIRBSTS, tmp);
+ chip->rirb.rp = chip->rirb.cmds = 0;
+
+ return 0;
+}
+
+static void stop_corb_rirb(struct lola *chip)
+{
+ /* disable ringbuffer DMAs */
+ lola_writeb(chip, BAR0, RIRBCTL, 0);
+ lola_writeb(chip, BAR0, CORBCTL, 0);
+}
+
+static void lola_reset_setups(struct lola *chip)
+{
+ /* update the granularity */
+ lola_set_granularity(chip, chip->granularity, true);
+ /* update the sample clock */
+ lola_set_clock_index(chip, chip->clock.cur_index);
+ /* enable unsolicited events of the clock widget */
+ lola_enable_clock_events(chip);
+ /* update the analog gains */
+ lola_setup_all_analog_gains(chip, CAPT, false); /* input, update */
+ /* update SRC configuration if applicable */
+ lola_set_src_config(chip, chip->input_src_mask, false);
+ /* update the analog outputs */
+ lola_setup_all_analog_gains(chip, PLAY, false); /* output, update */
+}
+
+static int lola_parse_tree(struct lola *chip)
+{
+ unsigned int val;
+ int nid, err;
+
+ err = lola_read_param(chip, 0, LOLA_PAR_VENDOR_ID, &val);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read VENDOR_ID\n");
+ return err;
+ }
+ val >>= 16;
+ if (val != 0x1369) {
+ dev_err(chip->card->dev, "Unknown codec vendor 0x%x\n", val);
+ return -EINVAL;
+ }
+
+ err = lola_read_param(chip, 1, LOLA_PAR_FUNCTION_TYPE, &val);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read FUNCTION_TYPE\n");
+ return err;
+ }
+ if (val != 1) {
+ dev_err(chip->card->dev, "Unknown function type %d\n", val);
+ return -EINVAL;
+ }
+
+ err = lola_read_param(chip, 1, LOLA_PAR_SPECIFIC_CAPS, &val);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read SPECCAPS\n");
+ return err;
+ }
+ chip->lola_caps = val;
+ chip->pin[CAPT].num_pins = LOLA_AFG_INPUT_PIN_COUNT(chip->lola_caps);
+ chip->pin[PLAY].num_pins = LOLA_AFG_OUTPUT_PIN_COUNT(chip->lola_caps);
+ dev_dbg(chip->card->dev, "speccaps=0x%x, pins in=%d, out=%d\n",
+ chip->lola_caps,
+ chip->pin[CAPT].num_pins, chip->pin[PLAY].num_pins);
+
+ if (chip->pin[CAPT].num_pins > MAX_AUDIO_INOUT_COUNT ||
+ chip->pin[PLAY].num_pins > MAX_AUDIO_INOUT_COUNT) {
+ dev_err(chip->card->dev, "Invalid Lola-spec caps 0x%x\n", val);
+ return -EINVAL;
+ }
+
+ nid = 0x02;
+ err = lola_init_pcm(chip, CAPT, &nid);
+ if (err < 0)
+ return err;
+ err = lola_init_pcm(chip, PLAY, &nid);
+ if (err < 0)
+ return err;
+
+ err = lola_init_pins(chip, CAPT, &nid);
+ if (err < 0)
+ return err;
+ err = lola_init_pins(chip, PLAY, &nid);
+ if (err < 0)
+ return err;
+
+ if (LOLA_AFG_CLOCK_WIDGET_PRESENT(chip->lola_caps)) {
+ err = lola_init_clock_widget(chip, nid);
+ if (err < 0)
+ return err;
+ nid++;
+ }
+ if (LOLA_AFG_MIXER_WIDGET_PRESENT(chip->lola_caps)) {
+ err = lola_init_mixer_widget(chip, nid);
+ if (err < 0)
+ return err;
+ nid++;
+ }
+
+ /* enable unsolicited events of the clock widget */
+ err = lola_enable_clock_events(chip);
+ if (err < 0)
+ return err;
+
+ /* if last ResetController was not a ColdReset, we don't know
+ * the state of the card; initialize here again
+ */
+ if (!chip->cold_reset) {
+ lola_reset_setups(chip);
+ chip->cold_reset = 1;
+ } else {
+ /* set the granularity if it is not the default */
+ if (chip->granularity != LOLA_GRANULARITY_MIN)
+ lola_set_granularity(chip, chip->granularity, true);
+ }
+
+ return 0;
+}
+
+static void lola_stop_hw(struct lola *chip)
+{
+ stop_corb_rirb(chip);
+ lola_irq_disable(chip);
+}
+
+static void lola_free(struct snd_card *card)
+{
+ struct lola *chip = card->private_data;
+
+ if (chip->initialized)
+ lola_stop_hw(chip);
+ lola_free_mixer(chip);
+}
+
+static int lola_create(struct snd_card *card, struct pci_dev *pci, int dev)
+{
+ struct lola *chip = card->private_data;
+ int err;
+ unsigned int dever;
+ void __iomem *iomem;
+
+ err = pcim_enable_device(pci);
+ if (err < 0)
+ return err;
+
+ spin_lock_init(&chip->reg_lock);
+ mutex_init(&chip->open_mutex);
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+ card->private_free = lola_free;
+
+ chip->granularity = granularity[dev];
+ switch (chip->granularity) {
+ case 8:
+ chip->sample_rate_max = 48000;
+ break;
+ case 16:
+ chip->sample_rate_max = 96000;
+ break;
+ case 32:
+ chip->sample_rate_max = 192000;
+ break;
+ default:
+ dev_warn(chip->card->dev,
+ "Invalid granularity %d, reset to %d\n",
+ chip->granularity, LOLA_GRANULARITY_MAX);
+ chip->granularity = LOLA_GRANULARITY_MAX;
+ chip->sample_rate_max = 192000;
+ break;
+ }
+ chip->sample_rate_min = sample_rate_min[dev];
+ if (chip->sample_rate_min > chip->sample_rate_max) {
+ dev_warn(chip->card->dev,
+ "Invalid sample_rate_min %d, reset to 16000\n",
+ chip->sample_rate_min);
+ chip->sample_rate_min = 16000;
+ }
+
+ iomem = pcim_iomap_region(pci, 0, DRVNAME);
+ if (IS_ERR(iomem))
+ return PTR_ERR(iomem);
+
+ chip->bar[0].remap_addr = iomem;
+ chip->bar[0].addr = pci_resource_start(pci, 0);
+
+ iomem = pcim_iomap_region(pci, 2, DRVNAME);
+ if (IS_ERR(iomem))
+ return PTR_ERR(iomem);
+
+ chip->bar[1].remap_addr = iomem;
+ chip->bar[1].addr = pci_resource_start(pci, 2);
+
+ pci_set_master(pci);
+
+ err = reset_controller(chip);
+ if (err < 0)
+ return err;
+
+ if (devm_request_irq(&pci->dev, pci->irq, lola_interrupt, IRQF_SHARED,
+ KBUILD_MODNAME, chip)) {
+ dev_err(chip->card->dev, "unable to grab IRQ %d\n", pci->irq);
+ return -EBUSY;
+ }
+ chip->irq = pci->irq;
+ card->sync_irq = chip->irq;
+
+ dever = lola_readl(chip, BAR1, DEVER);
+ chip->pcm[CAPT].num_streams = (dever >> 0) & 0x3ff;
+ chip->pcm[PLAY].num_streams = (dever >> 10) & 0x3ff;
+ chip->version = (dever >> 24) & 0xff;
+ dev_dbg(chip->card->dev, "streams in=%d, out=%d, version=0x%x\n",
+ chip->pcm[CAPT].num_streams, chip->pcm[PLAY].num_streams,
+ chip->version);
+
+ /* Test LOLA_BAR1_DEVER */
+ if (chip->pcm[CAPT].num_streams > MAX_STREAM_IN_COUNT ||
+ chip->pcm[PLAY].num_streams > MAX_STREAM_OUT_COUNT ||
+ (!chip->pcm[CAPT].num_streams &&
+ !chip->pcm[PLAY].num_streams)) {
+ dev_err(chip->card->dev, "invalid DEVER = %x\n", dever);
+ return -EINVAL;
+ }
+
+ err = setup_corb_rirb(chip);
+ if (err < 0)
+ return err;
+
+ strscpy(card->driver, "Lola");
+ strscpy(card->shortname, "Digigram Lola", sizeof(card->shortname));
+ snprintf(card->longname, sizeof(card->longname),
+ "%s at 0x%lx irq %i",
+ card->shortname, chip->bar[0].addr, chip->irq);
+ strscpy(card->mixername, card->shortname);
+
+ lola_irq_enable(chip);
+
+ chip->initialized = 1;
+ return 0;
+}
+
+static int __lola_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ static int dev;
+ struct snd_card *card;
+ struct lola *chip;
+ int err;
+
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
+ if (err < 0) {
+ dev_err(&pci->dev, "Error creating card!\n");
+ return err;
+ }
+ chip = card->private_data;
+
+ err = lola_create(card, pci, dev);
+ if (err < 0)
+ return err;
+
+ err = lola_parse_tree(chip);
+ if (err < 0)
+ return err;
+
+ err = lola_create_pcm(chip);
+ if (err < 0)
+ return err;
+
+ err = lola_create_mixer(chip);
+ if (err < 0)
+ return err;
+
+ lola_proc_debug_new(chip);
+
+ err = snd_card_register(card);
+ if (err < 0)
+ return err;
+
+ pci_set_drvdata(pci, card);
+ dev++;
+ return 0;
+}
+
+static int lola_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ return snd_card_free_on_error(&pci->dev, __lola_probe(pci, pci_id));
+}
+
+/* PCI IDs */
+static const struct pci_device_id lola_ids[] = {
+ { PCI_VDEVICE(DIGIGRAM, 0x0001) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, lola_ids);
+
+/* pci_driver definition */
+static struct pci_driver lola_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = lola_ids,
+ .probe = lola_probe,
+};
+
+module_pci_driver(lola_driver);
diff --git a/sound/pci/lola/lola.h b/sound/pci/lola/lola.h
new file mode 100644
index 000000000000..25f72f9e3f9b
--- /dev/null
+++ b/sound/pci/lola/lola.h
@@ -0,0 +1,511 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#ifndef _LOLA_H
+#define _LOLA_H
+
+#define DRVNAME "snd-lola"
+#define SFX DRVNAME ": "
+
+/*
+ * Lola HD Audio Registers BAR0
+ */
+#define LOLA_BAR0_GCAP 0x00
+#define LOLA_BAR0_VMIN 0x02
+#define LOLA_BAR0_VMAJ 0x03
+#define LOLA_BAR0_OUTPAY 0x04
+#define LOLA_BAR0_INPAY 0x06
+#define LOLA_BAR0_GCTL 0x08
+#define LOLA_BAR0_WAKEEN 0x0c
+#define LOLA_BAR0_STATESTS 0x0e
+#define LOLA_BAR0_GSTS 0x10
+#define LOLA_BAR0_OUTSTRMPAY 0x18
+#define LOLA_BAR0_INSTRMPAY 0x1a
+#define LOLA_BAR0_INTCTL 0x20
+#define LOLA_BAR0_INTSTS 0x24
+#define LOLA_BAR0_WALCLK 0x30
+#define LOLA_BAR0_SSYNC 0x38
+
+#define LOLA_BAR0_CORBLBASE 0x40
+#define LOLA_BAR0_CORBUBASE 0x44
+#define LOLA_BAR0_CORBWP 0x48 /* no ULONG access */
+#define LOLA_BAR0_CORBRP 0x4a /* no ULONG access */
+#define LOLA_BAR0_CORBCTL 0x4c /* no ULONG access */
+#define LOLA_BAR0_CORBSTS 0x4d /* UCHAR access only */
+#define LOLA_BAR0_CORBSIZE 0x4e /* no ULONG access */
+
+#define LOLA_BAR0_RIRBLBASE 0x50
+#define LOLA_BAR0_RIRBUBASE 0x54
+#define LOLA_BAR0_RIRBWP 0x58
+#define LOLA_BAR0_RINTCNT 0x5a /* no ULONG access */
+#define LOLA_BAR0_RIRBCTL 0x5c
+#define LOLA_BAR0_RIRBSTS 0x5d /* UCHAR access only */
+#define LOLA_BAR0_RIRBSIZE 0x5e /* no ULONG access */
+
+#define LOLA_BAR0_ICW 0x60
+#define LOLA_BAR0_IRR 0x64
+#define LOLA_BAR0_ICS 0x68
+#define LOLA_BAR0_DPLBASE 0x70
+#define LOLA_BAR0_DPUBASE 0x74
+
+/* stream register offsets from stream base 0x80 */
+#define LOLA_BAR0_SD0_OFFSET 0x80
+#define LOLA_REG0_SD_CTL 0x00
+#define LOLA_REG0_SD_STS 0x03
+#define LOLA_REG0_SD_LPIB 0x04
+#define LOLA_REG0_SD_CBL 0x08
+#define LOLA_REG0_SD_LVI 0x0c
+#define LOLA_REG0_SD_FIFOW 0x0e
+#define LOLA_REG0_SD_FIFOSIZE 0x10
+#define LOLA_REG0_SD_FORMAT 0x12
+#define LOLA_REG0_SD_BDLPL 0x18
+#define LOLA_REG0_SD_BDLPU 0x1c
+
+/*
+ * Lola Digigram Registers BAR1
+ */
+#define LOLA_BAR1_FPGAVER 0x00
+#define LOLA_BAR1_DEVER 0x04
+#define LOLA_BAR1_UCBMV 0x08
+#define LOLA_BAR1_JTAG 0x0c
+#define LOLA_BAR1_UARTRX 0x10
+#define LOLA_BAR1_UARTTX 0x14
+#define LOLA_BAR1_UARTCR 0x18
+#define LOLA_BAR1_NVRAMVER 0x1c
+#define LOLA_BAR1_CTRLSPI 0x20
+#define LOLA_BAR1_DSPI 0x24
+#define LOLA_BAR1_AISPI 0x28
+#define LOLA_BAR1_GRAN 0x2c
+
+#define LOLA_BAR1_DINTCTL 0x80
+#define LOLA_BAR1_DIINTCTL 0x84
+#define LOLA_BAR1_DOINTCTL 0x88
+#define LOLA_BAR1_LRC 0x90
+#define LOLA_BAR1_DINTSTS 0x94
+#define LOLA_BAR1_DIINTSTS 0x98
+#define LOLA_BAR1_DOINTSTS 0x9c
+
+#define LOLA_BAR1_DSD0_OFFSET 0xa0
+#define LOLA_BAR1_DSD_SIZE 0x18
+
+#define LOLA_BAR1_DSDnSTS 0x00
+#define LOLA_BAR1_DSDnLPIB 0x04
+#define LOLA_BAR1_DSDnCTL 0x08
+#define LOLA_BAR1_DSDnLVI 0x0c
+#define LOLA_BAR1_DSDnBDPL 0x10
+#define LOLA_BAR1_DSDnBDPU 0x14
+
+#define LOLA_BAR1_SSYNC 0x03e8
+
+#define LOLA_BAR1_BOARD_CTRL 0x0f00
+#define LOLA_BAR1_BOARD_MODE 0x0f02
+
+#define LOLA_BAR1_SOURCE_GAIN_ENABLE 0x1000
+#define LOLA_BAR1_DEST00_MIX_GAIN_ENABLE 0x1004
+#define LOLA_BAR1_DEST31_MIX_GAIN_ENABLE 0x1080
+#define LOLA_BAR1_SOURCE00_01_GAIN 0x1084
+#define LOLA_BAR1_SOURCE30_31_GAIN 0x10c0
+#define LOLA_BAR1_SOURCE_GAIN(src) \
+ (LOLA_BAR1_SOURCE00_01_GAIN + (src) * 2)
+#define LOLA_BAR1_DEST00_MIX00_01_GAIN 0x10c4
+#define LOLA_BAR1_DEST00_MIX30_31_GAIN 0x1100
+#define LOLA_BAR1_DEST01_MIX00_01_GAIN 0x1104
+#define LOLA_BAR1_DEST01_MIX30_31_GAIN 0x1140
+#define LOLA_BAR1_DEST31_MIX00_01_GAIN 0x1884
+#define LOLA_BAR1_DEST31_MIX30_31_GAIN 0x18c0
+#define LOLA_BAR1_MIX_GAIN(dest, mix) \
+ (LOLA_BAR1_DEST00_MIX00_01_GAIN + (dest) * 0x40 + (mix) * 2)
+#define LOLA_BAR1_ANALOG_CLIP_IN 0x18c4
+#define LOLA_BAR1_PEAKMETERS_SOURCE00_01 0x18c8
+#define LOLA_BAR1_PEAKMETERS_SOURCE30_31 0x1904
+#define LOLA_BAR1_PEAKMETERS_SOURCE(src) \
+ (LOLA_BAR1_PEAKMETERS_SOURCE00_01 + (src) * 2)
+#define LOLA_BAR1_PEAKMETERS_DEST00_01 0x1908
+#define LOLA_BAR1_PEAKMETERS_DEST30_31 0x1944
+#define LOLA_BAR1_PEAKMETERS_DEST(dest) \
+ (LOLA_BAR1_PEAKMETERS_DEST00_01 + (dest) * 2)
+#define LOLA_BAR1_PEAKMETERS_AGC00_01 0x1948
+#define LOLA_BAR1_PEAKMETERS_AGC14_15 0x1964
+#define LOLA_BAR1_PEAKMETERS_AGC(x) \
+ (LOLA_BAR1_PEAKMETERS_AGC00_01 + (x) * 2)
+
+/* GCTL reset bit */
+#define LOLA_GCTL_RESET (1 << 0)
+/* GCTL unsolicited response enable bit */
+#define LOLA_GCTL_UREN (1 << 8)
+
+/* CORB/RIRB control, read/write pointer */
+#define LOLA_RBCTL_DMA_EN 0x02 /* enable DMA */
+#define LOLA_RBCTL_IRQ_EN 0x01 /* enable IRQ */
+#define LOLA_RBRWP_CLR 0x8000 /* read/write pointer clear */
+
+#define LOLA_RIRB_EX_UNSOL_EV 0x40000000
+#define LOLA_RIRB_EX_ERROR 0x80000000
+
+/* CORB int mask: CMEI[0] */
+#define LOLA_CORB_INT_CMEI 0x01
+#define LOLA_CORB_INT_MASK LOLA_CORB_INT_CMEI
+
+/* RIRB int mask: overrun[2], response[0] */
+#define LOLA_RIRB_INT_RESPONSE 0x01
+#define LOLA_RIRB_INT_OVERRUN 0x04
+#define LOLA_RIRB_INT_MASK (LOLA_RIRB_INT_RESPONSE | LOLA_RIRB_INT_OVERRUN)
+
+/* DINTCTL and DINTSTS */
+#define LOLA_DINT_GLOBAL 0x80000000 /* global interrupt enable bit */
+#define LOLA_DINT_CTRL 0x40000000 /* controller interrupt enable bit */
+#define LOLA_DINT_FIFOERR 0x20000000 /* global fifo error enable bit */
+#define LOLA_DINT_MUERR 0x10000000 /* global microcontroller underrun error */
+
+/* DSDnCTL bits */
+#define LOLA_DSD_CTL_SRST 0x01 /* stream reset bit */
+#define LOLA_DSD_CTL_SRUN 0x02 /* stream DMA start bit */
+#define LOLA_DSD_CTL_IOCE 0x04 /* interrupt on completion enable */
+#define LOLA_DSD_CTL_DEIE 0x10 /* descriptor error interrupt enable */
+#define LOLA_DSD_CTL_VLRCV 0x20 /* valid LRCountValue information in bits 8..31 */
+#define LOLA_LRC_MASK 0xffffff00
+
+/* DSDnSTS */
+#define LOLA_DSD_STS_BCIS 0x04 /* buffer completion interrupt status */
+#define LOLA_DSD_STS_DESE 0x10 /* descriptor error interrupt */
+#define LOLA_DSD_STS_FIFORDY 0x20 /* fifo ready */
+
+#define LOLA_CORB_ENTRIES 256
+
+#define MAX_STREAM_IN_COUNT 16
+#define MAX_STREAM_OUT_COUNT 16
+#define MAX_STREAM_COUNT 16
+#define MAX_PINS MAX_STREAM_COUNT
+#define MAX_STREAM_BUFFER_COUNT 16
+#define MAX_AUDIO_INOUT_COUNT 16
+
+#define LOLA_CLOCK_TYPE_INTERNAL 0
+#define LOLA_CLOCK_TYPE_AES 1
+#define LOLA_CLOCK_TYPE_AES_SYNC 2
+#define LOLA_CLOCK_TYPE_WORDCLOCK 3
+#define LOLA_CLOCK_TYPE_ETHERSOUND 4
+#define LOLA_CLOCK_TYPE_VIDEO 5
+
+#define LOLA_CLOCK_FORMAT_NONE 0
+#define LOLA_CLOCK_FORMAT_NTSC 1
+#define LOLA_CLOCK_FORMAT_PAL 2
+
+#define MAX_SAMPLE_CLOCK_COUNT 48
+
+/* parameters used with mixer widget's mixer capabilities */
+#define LOLA_PEAK_METER_CAN_AGC_MASK 1
+#define LOLA_PEAK_METER_CAN_ANALOG_CLIP_MASK 2
+
+struct lola_bar {
+ unsigned long addr;
+ void __iomem *remap_addr;
+};
+
+/* CORB/RIRB */
+struct lola_rb {
+ __le32 *buf; /* CORB/RIRB buffer, 8 byte per each entry */
+ dma_addr_t addr; /* physical address of CORB/RIRB buffer */
+ unsigned short rp, wp; /* read/write pointers */
+ int cmds; /* number of pending requests */
+};
+
+/* Pin widget setup */
+struct lola_pin {
+ unsigned int nid;
+ bool is_analog;
+ unsigned int amp_mute;
+ unsigned int amp_step_size;
+ unsigned int amp_num_steps;
+ unsigned int amp_offset;
+ unsigned int max_level;
+ unsigned int config_default_reg;
+ unsigned int fixed_gain_list_len;
+ unsigned int cur_gain_step;
+};
+
+struct lola_pin_array {
+ unsigned int num_pins;
+ unsigned int num_analog_pins;
+ struct lola_pin pins[MAX_PINS];
+};
+
+/* Clock widget setup */
+struct lola_sample_clock {
+ unsigned int type;
+ unsigned int format;
+ unsigned int freq;
+};
+
+struct lola_clock_widget {
+ unsigned int nid;
+ unsigned int items;
+ unsigned int cur_index;
+ unsigned int cur_freq;
+ bool cur_valid;
+ struct lola_sample_clock sample_clock[MAX_SAMPLE_CLOCK_COUNT];
+ unsigned int idx_lookup[MAX_SAMPLE_CLOCK_COUNT];
+};
+
+#define LOLA_MIXER_DIM 32
+struct lola_mixer_array {
+ u32 src_gain_enable;
+ u32 dest_mix_gain_enable[LOLA_MIXER_DIM];
+ u16 src_gain[LOLA_MIXER_DIM];
+ u16 dest_mix_gain[LOLA_MIXER_DIM][LOLA_MIXER_DIM];
+};
+
+/* Mixer widget setup */
+struct lola_mixer_widget {
+ unsigned int nid;
+ unsigned int caps;
+ struct lola_mixer_array __iomem *array;
+ struct lola_mixer_array *array_saved;
+ unsigned int src_stream_outs;
+ unsigned int src_phys_ins;
+ unsigned int dest_stream_ins;
+ unsigned int dest_phys_outs;
+ unsigned int src_stream_out_ofs;
+ unsigned int dest_phys_out_ofs;
+ unsigned int src_mask;
+ unsigned int dest_mask;
+};
+
+/* Audio stream */
+struct lola_stream {
+ unsigned int nid; /* audio widget NID */
+ unsigned int index; /* array index */
+ unsigned int dsd; /* DSD index */
+ bool can_float;
+ struct snd_pcm_substream *substream; /* assigned PCM substream */
+ struct lola_stream *master; /* master stream (for multi-channel) */
+
+ /* buffer setup */
+ unsigned int bufsize;
+ unsigned int period_bytes;
+ unsigned int frags;
+
+ /* format + channel setup */
+ unsigned int format_verb;
+
+ /* flags */
+ unsigned int opened:1;
+ unsigned int prepared:1;
+ unsigned int paused:1;
+ unsigned int running:1;
+};
+
+#define PLAY SNDRV_PCM_STREAM_PLAYBACK
+#define CAPT SNDRV_PCM_STREAM_CAPTURE
+
+struct lola_pcm {
+ unsigned int num_streams;
+ struct snd_dma_buffer *bdl; /* BDL buffer */
+ struct lola_stream streams[MAX_STREAM_COUNT];
+};
+
+/* card instance */
+struct lola {
+ struct snd_card *card;
+ struct pci_dev *pci;
+
+ /* pci resources */
+ struct lola_bar bar[2];
+ int irq;
+
+ /* locks */
+ spinlock_t reg_lock;
+ struct mutex open_mutex;
+
+ /* CORB/RIRB */
+ struct lola_rb corb;
+ struct lola_rb rirb;
+ unsigned int res, res_ex; /* last read values */
+ /* last command (for debugging) */
+ unsigned int last_cmd_nid, last_verb, last_data, last_extdata;
+
+ /* CORB/RIRB buffers */
+ struct snd_dma_buffer *rb;
+
+ /* unsolicited events */
+ unsigned int last_unsol_res;
+
+ /* streams */
+ struct lola_pcm pcm[2];
+
+ /* input src */
+ unsigned int input_src_caps_mask;
+ unsigned int input_src_mask;
+
+ /* pins */
+ struct lola_pin_array pin[2];
+
+ /* clock */
+ struct lola_clock_widget clock;
+ int ref_count_rate;
+ unsigned int sample_rate;
+
+ /* mixer */
+ struct lola_mixer_widget mixer;
+
+ /* hw info */
+ unsigned int version;
+ unsigned int lola_caps;
+
+ /* parameters */
+ unsigned int granularity;
+ unsigned int sample_rate_min;
+ unsigned int sample_rate_max;
+
+ /* flags */
+ unsigned int initialized:1;
+ unsigned int cold_reset:1;
+ unsigned int polling_mode:1;
+
+ /* for debugging */
+ unsigned int debug_res;
+ unsigned int debug_res_ex;
+};
+
+#define BAR0 0
+#define BAR1 1
+
+/* Helper macros */
+#define lola_readl(chip, idx, name) \
+ readl((chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_readw(chip, idx, name) \
+ readw((chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_readb(chip, idx, name) \
+ readb((chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_writel(chip, idx, name, val) \
+ writel((val), (chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_writew(chip, idx, name, val) \
+ writew((val), (chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+#define lola_writeb(chip, idx, name, val) \
+ writeb((val), (chip)->bar[idx].remap_addr + LOLA_##idx##_##name)
+
+#define lola_dsd_read(chip, dsd, name) \
+ readl((chip)->bar[BAR1].remap_addr + LOLA_BAR1_DSD0_OFFSET + \
+ (LOLA_BAR1_DSD_SIZE * (dsd)) + LOLA_BAR1_DSDn##name)
+#define lola_dsd_write(chip, dsd, name, val) \
+ writel((val), (chip)->bar[BAR1].remap_addr + LOLA_BAR1_DSD0_OFFSET + \
+ (LOLA_BAR1_DSD_SIZE * (dsd)) + LOLA_BAR1_DSDn##name)
+
+/* GET verbs HDAudio */
+#define LOLA_VERB_GET_STREAM_FORMAT 0xa00
+#define LOLA_VERB_GET_AMP_GAIN_MUTE 0xb00
+#define LOLA_VERB_PARAMETERS 0xf00
+#define LOLA_VERB_GET_POWER_STATE 0xf05
+#define LOLA_VERB_GET_CONV 0xf06
+#define LOLA_VERB_GET_UNSOLICITED_RESPONSE 0xf08
+#define LOLA_VERB_GET_DIGI_CONVERT_1 0xf0d
+#define LOLA_VERB_GET_CONFIG_DEFAULT 0xf1c
+#define LOLA_VERB_GET_SUBSYSTEM_ID 0xf20
+/* GET verbs Digigram */
+#define LOLA_VERB_GET_FIXED_GAIN 0xfc0
+#define LOLA_VERB_GET_GAIN_SELECT 0xfc1
+#define LOLA_VERB_GET_MAX_LEVEL 0xfc2
+#define LOLA_VERB_GET_CLOCK_LIST 0xfc3
+#define LOLA_VERB_GET_CLOCK_SELECT 0xfc4
+#define LOLA_VERB_GET_CLOCK_STATUS 0xfc5
+
+/* SET verbs HDAudio */
+#define LOLA_VERB_SET_STREAM_FORMAT 0x200
+#define LOLA_VERB_SET_AMP_GAIN_MUTE 0x300
+#define LOLA_VERB_SET_POWER_STATE 0x705
+#define LOLA_VERB_SET_CHANNEL_STREAMID 0x706
+#define LOLA_VERB_SET_UNSOLICITED_ENABLE 0x708
+#define LOLA_VERB_SET_DIGI_CONVERT_1 0x70d
+/* SET verbs Digigram */
+#define LOLA_VERB_SET_GAIN_SELECT 0xf81
+#define LOLA_VERB_SET_CLOCK_SELECT 0xf84
+#define LOLA_VERB_SET_GRANULARITY_STEPS 0xf86
+#define LOLA_VERB_SET_SOURCE_GAIN 0xf87
+#define LOLA_VERB_SET_MIX_GAIN 0xf88
+#define LOLA_VERB_SET_DESTINATION_GAIN 0xf89
+#define LOLA_VERB_SET_SRC 0xf8a
+
+/* Parameter IDs used with LOLA_VERB_PARAMETERS */
+#define LOLA_PAR_VENDOR_ID 0x00
+#define LOLA_PAR_FUNCTION_TYPE 0x05
+#define LOLA_PAR_AUDIO_WIDGET_CAP 0x09
+#define LOLA_PAR_PCM 0x0a
+#define LOLA_PAR_STREAM_FORMATS 0x0b
+#define LOLA_PAR_PIN_CAP 0x0c
+#define LOLA_PAR_AMP_IN_CAP 0x0d
+#define LOLA_PAR_CONNLIST_LEN 0x0e
+#define LOLA_PAR_POWER_STATE 0x0f
+#define LOLA_PAR_GPIO_CAP 0x11
+#define LOLA_PAR_AMP_OUT_CAP 0x12
+#define LOLA_PAR_SPECIFIC_CAPS 0x80
+#define LOLA_PAR_FIXED_GAIN_LIST 0x81
+
+/* extract results of LOLA_PAR_SPECIFIC_CAPS */
+#define LOLA_AFG_MIXER_WIDGET_PRESENT(res) ((res & (1 << 21)) != 0)
+#define LOLA_AFG_CLOCK_WIDGET_PRESENT(res) ((res & (1 << 20)) != 0)
+#define LOLA_AFG_INPUT_PIN_COUNT(res) ((res >> 10) & 0x2ff)
+#define LOLA_AFG_OUTPUT_PIN_COUNT(res) ((res) & 0x2ff)
+
+/* extract results of LOLA_PAR_AMP_IN_CAP / LOLA_PAR_AMP_OUT_CAP */
+#define LOLA_AMP_MUTE_CAPABLE(res) ((res & (1 << 31)) != 0)
+#define LOLA_AMP_STEP_SIZE(res) ((res >> 24) & 0x7f)
+#define LOLA_AMP_NUM_STEPS(res) ((res >> 12) & 0x3ff)
+#define LOLA_AMP_OFFSET(res) ((res) & 0x3ff)
+
+#define LOLA_GRANULARITY_MIN 8
+#define LOLA_GRANULARITY_MAX 32
+#define LOLA_GRANULARITY_STEP 8
+
+/* parameters used with unsolicited command/response */
+#define LOLA_UNSOLICITED_TAG_MASK 0x3f
+#define LOLA_UNSOLICITED_TAG 0x1a
+#define LOLA_UNSOLICITED_ENABLE 0x80
+#define LOLA_UNSOL_RESP_TAG_OFFSET 26
+
+/* count values in the Vendor Specific Mixer Widget's Audio Widget Capabilities */
+#define LOLA_MIXER_SRC_INPUT_PLAY_SEPARATION(res) ((res >> 2) & 0x1f)
+#define LOLA_MIXER_DEST_REC_OUTPUT_SEPARATION(res) ((res >> 7) & 0x1f)
+
+int lola_codec_write(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata);
+int lola_codec_read(struct lola *chip, unsigned int nid, unsigned int verb,
+ unsigned int data, unsigned int extdata,
+ unsigned int *val, unsigned int *extval);
+int lola_codec_flush(struct lola *chip);
+#define lola_read_param(chip, nid, param, val) \
+ lola_codec_read(chip, nid, LOLA_VERB_PARAMETERS, param, 0, val, NULL)
+
+/* PCM */
+int lola_create_pcm(struct lola *chip);
+int lola_init_pcm(struct lola *chip, int dir, int *nidp);
+void lola_pcm_update(struct lola *chip, struct lola_pcm *pcm, unsigned int bits);
+
+/* clock */
+int lola_init_clock_widget(struct lola *chip, int nid);
+int lola_set_granularity(struct lola *chip, unsigned int val, bool force);
+int lola_enable_clock_events(struct lola *chip);
+int lola_set_clock_index(struct lola *chip, unsigned int idx);
+int lola_set_clock(struct lola *chip, int idx);
+int lola_set_sample_rate(struct lola *chip, int rate);
+bool lola_update_ext_clock_freq(struct lola *chip, unsigned int val);
+unsigned int lola_sample_rate_convert(unsigned int coded);
+
+/* mixer */
+int lola_init_pins(struct lola *chip, int dir, int *nidp);
+int lola_init_mixer_widget(struct lola *chip, int nid);
+void lola_free_mixer(struct lola *chip);
+int lola_create_mixer(struct lola *chip);
+int lola_setup_all_analog_gains(struct lola *chip, int dir, bool mute);
+int lola_set_src_config(struct lola *chip, unsigned int src_mask, bool update);
+
+/* proc */
+#ifdef CONFIG_SND_DEBUG
+void lola_proc_debug_new(struct lola *chip);
+#else
+#define lola_proc_debug_new(chip)
+#endif
+
+#endif /* _LOLA_H */
diff --git a/sound/pci/lola/lola_clock.c b/sound/pci/lola/lola_clock.c
new file mode 100644
index 000000000000..2e73fbf335ed
--- /dev/null
+++ b/sound/pci/lola/lola_clock.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "lola.h"
+
+unsigned int lola_sample_rate_convert(unsigned int coded)
+{
+ unsigned int freq;
+
+ /* base frequency */
+ switch (coded & 0x3) {
+ case 0: freq = 48000; break;
+ case 1: freq = 44100; break;
+ case 2: freq = 32000; break;
+ default: return 0; /* error */
+ }
+
+ /* multiplier / devisor */
+ switch (coded & 0x1c) {
+ case (0 << 2): break;
+ case (4 << 2): break;
+ case (1 << 2): freq *= 2; break;
+ case (2 << 2): freq *= 4; break;
+ case (5 << 2): freq /= 2; break;
+ case (6 << 2): freq /= 4; break;
+ default: return 0; /* error */
+ }
+
+ /* adjustement */
+ switch (coded & 0x60) {
+ case (0 << 5): break;
+ case (1 << 5): freq = (freq * 999) / 1000; break;
+ case (2 << 5): freq = (freq * 1001) / 1000; break;
+ default: return 0; /* error */
+ }
+ return freq;
+}
+
+/*
+ * Granualrity
+ */
+
+#define LOLA_MAXFREQ_AT_GRANULARITY_MIN 48000
+#define LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX 96000
+
+static bool check_gran_clock_compatibility(struct lola *chip,
+ unsigned int val,
+ unsigned int freq)
+{
+ if (!chip->granularity)
+ return true;
+
+ if (val < LOLA_GRANULARITY_MIN || val > LOLA_GRANULARITY_MAX ||
+ (val % LOLA_GRANULARITY_STEP) != 0)
+ return false;
+
+ if (val == LOLA_GRANULARITY_MIN) {
+ if (freq > LOLA_MAXFREQ_AT_GRANULARITY_MIN)
+ return false;
+ } else if (val < LOLA_GRANULARITY_MAX) {
+ if (freq > LOLA_MAXFREQ_AT_GRANULARITY_BELOW_MAX)
+ return false;
+ }
+ return true;
+}
+
+int lola_set_granularity(struct lola *chip, unsigned int val, bool force)
+{
+ int err;
+
+ if (!force) {
+ if (val == chip->granularity)
+ return 0;
+#if 0
+ /* change Gran only if there are no streams allocated ! */
+ if (chip->audio_in_alloc_mask || chip->audio_out_alloc_mask)
+ return -EBUSY;
+#endif
+ if (!check_gran_clock_compatibility(chip, val,
+ chip->clock.cur_freq))
+ return -EINVAL;
+ }
+
+ chip->granularity = val;
+ val /= LOLA_GRANULARITY_STEP;
+
+ /* audio function group */
+ err = lola_codec_write(chip, 1, LOLA_VERB_SET_GRANULARITY_STEPS,
+ val, 0);
+ if (err < 0)
+ return err;
+ /* this can be a very slow function !!! */
+ usleep_range(400 * val, 20000);
+ return lola_codec_flush(chip);
+}
+
+/*
+ * Clock widget handling
+ */
+
+int lola_init_clock_widget(struct lola *chip, int nid)
+{
+ unsigned int val;
+ int i, j, nitems, nb_verbs, idx, idx_list;
+ int err;
+
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+
+ if ((val & 0xfff00000) != 0x01f00000) { /* test SubType and Type */
+ dev_dbg(chip->card->dev, "No valid clock widget\n");
+ return 0;
+ }
+
+ chip->clock.nid = nid;
+ chip->clock.items = val & 0xff;
+ dev_dbg(chip->card->dev, "clock_list nid=%x, entries=%d\n", nid,
+ chip->clock.items);
+ if (chip->clock.items > MAX_SAMPLE_CLOCK_COUNT) {
+ dev_err(chip->card->dev, "CLOCK_LIST too big: %d\n",
+ chip->clock.items);
+ return -EINVAL;
+ }
+
+ nitems = chip->clock.items;
+ nb_verbs = DIV_ROUND_UP(nitems, 4);
+ idx = 0;
+ idx_list = 0;
+ for (i = 0; i < nb_verbs; i++) {
+ unsigned int res_ex;
+ unsigned short items[4];
+
+ err = lola_codec_read(chip, nid, LOLA_VERB_GET_CLOCK_LIST,
+ idx, 0, &val, &res_ex);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read CLOCK_LIST\n");
+ return -EINVAL;
+ }
+
+ items[0] = val & 0xfff;
+ items[1] = (val >> 16) & 0xfff;
+ items[2] = res_ex & 0xfff;
+ items[3] = (res_ex >> 16) & 0xfff;
+
+ for (j = 0; j < 4; j++) {
+ unsigned char type = items[j] >> 8;
+ unsigned int freq = items[j] & 0xff;
+ int format = LOLA_CLOCK_FORMAT_NONE;
+ bool add_clock = true;
+ if (type == LOLA_CLOCK_TYPE_INTERNAL) {
+ freq = lola_sample_rate_convert(freq);
+ if (freq < chip->sample_rate_min)
+ add_clock = false;
+ else if (freq == 48000) {
+ chip->clock.cur_index = idx_list;
+ chip->clock.cur_freq = 48000;
+ chip->clock.cur_valid = true;
+ }
+ } else if (type == LOLA_CLOCK_TYPE_VIDEO) {
+ freq = lola_sample_rate_convert(freq);
+ if (freq < chip->sample_rate_min)
+ add_clock = false;
+ /* video clock has a format (0:NTSC, 1:PAL)*/
+ if (items[j] & 0x80)
+ format = LOLA_CLOCK_FORMAT_NTSC;
+ else
+ format = LOLA_CLOCK_FORMAT_PAL;
+ }
+ if (add_clock) {
+ struct lola_sample_clock *sc;
+ sc = &chip->clock.sample_clock[idx_list];
+ sc->type = type;
+ sc->format = format;
+ sc->freq = freq;
+ /* keep the index used with the board */
+ chip->clock.idx_lookup[idx_list] = idx;
+ idx_list++;
+ } else {
+ chip->clock.items--;
+ }
+ if (++idx >= nitems)
+ break;
+ }
+ }
+ return 0;
+}
+
+/* enable unsolicited events of the clock widget */
+int lola_enable_clock_events(struct lola *chip)
+{
+ unsigned int res;
+ int err;
+
+ err = lola_codec_read(chip, chip->clock.nid,
+ LOLA_VERB_SET_UNSOLICITED_ENABLE,
+ LOLA_UNSOLICITED_ENABLE | LOLA_UNSOLICITED_TAG,
+ 0, &res, NULL);
+ if (err < 0)
+ return err;
+ if (res) {
+ dev_warn(chip->card->dev, "error in enable_clock_events %d\n",
+ res);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int lola_set_clock_index(struct lola *chip, unsigned int idx)
+{
+ unsigned int res;
+ int err;
+
+ err = lola_codec_read(chip, chip->clock.nid,
+ LOLA_VERB_SET_CLOCK_SELECT,
+ chip->clock.idx_lookup[idx],
+ 0, &res, NULL);
+ if (err < 0)
+ return err;
+ if (res) {
+ dev_warn(chip->card->dev, "error in set_clock %d\n", res);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+bool lola_update_ext_clock_freq(struct lola *chip, unsigned int val)
+{
+ unsigned int tag;
+
+ /* the current EXTERNAL clock information gets updated by interrupt
+ * with an unsolicited response
+ */
+ if (!val)
+ return false;
+ tag = (val >> LOLA_UNSOL_RESP_TAG_OFFSET) & LOLA_UNSOLICITED_TAG_MASK;
+ if (tag != LOLA_UNSOLICITED_TAG)
+ return false;
+
+ /* only for current = external clocks */
+ if (chip->clock.sample_clock[chip->clock.cur_index].type !=
+ LOLA_CLOCK_TYPE_INTERNAL) {
+ chip->clock.cur_freq = lola_sample_rate_convert(val & 0x7f);
+ chip->clock.cur_valid = (val & 0x100) != 0;
+ }
+ return true;
+}
+
+int lola_set_clock(struct lola *chip, int idx)
+{
+ int freq = 0;
+ bool valid = false;
+
+ if (idx == chip->clock.cur_index) {
+ /* current clock is allowed */
+ freq = chip->clock.cur_freq;
+ valid = chip->clock.cur_valid;
+ } else if (chip->clock.sample_clock[idx].type ==
+ LOLA_CLOCK_TYPE_INTERNAL) {
+ /* internal clocks allowed */
+ freq = chip->clock.sample_clock[idx].freq;
+ valid = true;
+ }
+
+ if (!freq || !valid)
+ return -EINVAL;
+
+ if (!check_gran_clock_compatibility(chip, chip->granularity, freq))
+ return -EINVAL;
+
+ if (idx != chip->clock.cur_index) {
+ int err = lola_set_clock_index(chip, idx);
+ if (err < 0)
+ return err;
+ /* update new settings */
+ chip->clock.cur_index = idx;
+ chip->clock.cur_freq = freq;
+ chip->clock.cur_valid = true;
+ }
+ return 0;
+}
+
+int lola_set_sample_rate(struct lola *chip, int rate)
+{
+ int i;
+
+ if (chip->clock.cur_freq == rate && chip->clock.cur_valid)
+ return 0;
+ /* search for new dwClockIndex */
+ for (i = 0; i < chip->clock.items; i++) {
+ if (chip->clock.sample_clock[i].type == LOLA_CLOCK_TYPE_INTERNAL &&
+ chip->clock.sample_clock[i].freq == rate)
+ break;
+ }
+ if (i >= chip->clock.items)
+ return -EINVAL;
+ return lola_set_clock(chip, i);
+}
+
diff --git a/sound/pci/lola/lola_mixer.c b/sound/pci/lola/lola_mixer.c
new file mode 100644
index 000000000000..9cb26a8a4e1a
--- /dev/null
+++ b/sound/pci/lola/lola_mixer.c
@@ -0,0 +1,842 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+#include "lola.h"
+
+static int lola_init_pin(struct lola *chip, struct lola_pin *pin,
+ int dir, int nid)
+{
+ unsigned int val;
+ int err;
+
+ pin->nid = nid;
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+ val &= 0x00f00fff; /* test TYPE and bits 0..11 */
+ if (val == 0x00400200) /* Type = 4, Digital = 1 */
+ pin->is_analog = false;
+ else if (val == 0x0040000a && dir == CAPT) /* Dig=0, InAmp/ovrd */
+ pin->is_analog = true;
+ else if (val == 0x0040000c && dir == PLAY) /* Dig=0, OutAmp/ovrd */
+ pin->is_analog = true;
+ else {
+ dev_err(chip->card->dev, "Invalid wcaps 0x%x for 0x%x\n", val, nid);
+ return -EINVAL;
+ }
+
+ /* analog parameters only following, so continue in case of Digital pin
+ */
+ if (!pin->is_analog)
+ return 0;
+
+ if (dir == PLAY)
+ err = lola_read_param(chip, nid, LOLA_PAR_AMP_OUT_CAP, &val);
+ else
+ err = lola_read_param(chip, nid, LOLA_PAR_AMP_IN_CAP, &val);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read AMP-caps for 0x%x\n", nid);
+ return err;
+ }
+
+ pin->amp_mute = LOLA_AMP_MUTE_CAPABLE(val);
+ pin->amp_step_size = LOLA_AMP_STEP_SIZE(val);
+ pin->amp_num_steps = LOLA_AMP_NUM_STEPS(val);
+ if (pin->amp_num_steps) {
+ /* zero as mute state */
+ pin->amp_num_steps++;
+ pin->amp_step_size++;
+ }
+ pin->amp_offset = LOLA_AMP_OFFSET(val);
+
+ err = lola_codec_read(chip, nid, LOLA_VERB_GET_MAX_LEVEL, 0, 0, &val,
+ NULL);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't get MAX_LEVEL 0x%x\n", nid);
+ return err;
+ }
+ pin->max_level = val & 0x3ff; /* 10 bits */
+
+ pin->config_default_reg = 0;
+ pin->fixed_gain_list_len = 0;
+ pin->cur_gain_step = 0;
+
+ return 0;
+}
+
+int lola_init_pins(struct lola *chip, int dir, int *nidp)
+{
+ int i, err, nid;
+ nid = *nidp;
+ for (i = 0; i < chip->pin[dir].num_pins; i++, nid++) {
+ err = lola_init_pin(chip, &chip->pin[dir].pins[i], dir, nid);
+ if (err < 0)
+ return err;
+ if (chip->pin[dir].pins[i].is_analog)
+ chip->pin[dir].num_analog_pins++;
+ }
+ *nidp = nid;
+ return 0;
+}
+
+void lola_free_mixer(struct lola *chip)
+{
+ vfree(chip->mixer.array_saved);
+}
+
+int lola_init_mixer_widget(struct lola *chip, int nid)
+{
+ unsigned int val;
+ int err;
+
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+
+ if ((val & 0xfff00000) != 0x02f00000) { /* test SubType and Type */
+ dev_dbg(chip->card->dev, "No valid mixer widget\n");
+ return 0;
+ }
+
+ chip->mixer.nid = nid;
+ chip->mixer.caps = val;
+ chip->mixer.array = (struct lola_mixer_array __iomem *)
+ (chip->bar[BAR1].remap_addr + LOLA_BAR1_SOURCE_GAIN_ENABLE);
+
+ /* reserve memory to copy mixer data for sleep mode transitions */
+ chip->mixer.array_saved = vmalloc(sizeof(struct lola_mixer_array));
+ if (!chip->mixer.array_saved)
+ return -ENOMEM;
+
+ /* mixer matrix sources are physical input data and play streams */
+ chip->mixer.src_stream_outs = chip->pcm[PLAY].num_streams;
+ chip->mixer.src_phys_ins = chip->pin[CAPT].num_pins;
+
+ /* mixer matrix destinations are record streams and physical output */
+ chip->mixer.dest_stream_ins = chip->pcm[CAPT].num_streams;
+ chip->mixer.dest_phys_outs = chip->pin[PLAY].num_pins;
+
+ /* mixer matrix may have unused areas between PhysIn and
+ * Play or Record and PhysOut zones
+ */
+ chip->mixer.src_stream_out_ofs = chip->mixer.src_phys_ins +
+ LOLA_MIXER_SRC_INPUT_PLAY_SEPARATION(val);
+ chip->mixer.dest_phys_out_ofs = chip->mixer.dest_stream_ins +
+ LOLA_MIXER_DEST_REC_OUTPUT_SEPARATION(val);
+
+ /* example : MixerMatrix of LoLa881 (LoLa16161 uses unused zones)
+ * +-+ 0-------8------16-------8------16
+ * | | | | | | |
+ * |s| | INPUT | | INPUT | |
+ * | |->| -> |unused | -> |unused |
+ * |r| |CAPTURE| | OUTPUT| |
+ * | | | MIX | | MIX | |
+ * |c| 8--------------------------------
+ * | | | | | | |
+ * | | | | | | |
+ * |g| |unused |unused |unused |unused |
+ * | | | | | | |
+ * |a| | | | | |
+ * | | 16-------------------------------
+ * |i| | | | | |
+ * | | | PLAYBK| | PLAYBK| |
+ * |n|->| -> |unused | -> |unused |
+ * | | |CAPTURE| | OUTPUT| |
+ * | | | MIX | | MIX | |
+ * |a| 8--------------------------------
+ * |r| | | | | |
+ * |r| | | | | |
+ * |a| |unused |unused |unused |unused |
+ * |y| | | | | |
+ * | | | | | | |
+ * +++ 16--|---------------|------------
+ * +---V---------------V-----------+
+ * | dest_mix_gain_enable array |
+ * +-------------------------------+
+ */
+ /* example : MixerMatrix of LoLa280
+ * +-+ 0-------8-2
+ * | | | | |
+ * |s| | INPUT | | INPUT
+ * |r|->| -> | | ->
+ * |c| |CAPTURE| | <- OUTPUT
+ * | | | MIX | | MIX
+ * |g| 8----------
+ * |a| | | |
+ * |i| | PLAYBK| | PLAYBACK
+ * |n|->| -> | | ->
+ * | | |CAPTURE| | <- OUTPUT
+ * |a| | MIX | | MIX
+ * |r| 8---|----|-
+ * |r| +---V----V-------------------+
+ * |a| | dest_mix_gain_enable array |
+ * |y| +----------------------------+
+ */
+ if (chip->mixer.src_stream_out_ofs > MAX_AUDIO_INOUT_COUNT ||
+ chip->mixer.dest_phys_out_ofs > MAX_STREAM_IN_COUNT) {
+ dev_err(chip->card->dev, "Invalid mixer widget size\n");
+ return -EINVAL;
+ }
+
+ chip->mixer.src_mask = ((1U << chip->mixer.src_phys_ins) - 1) |
+ (((1U << chip->mixer.src_stream_outs) - 1)
+ << chip->mixer.src_stream_out_ofs);
+ chip->mixer.dest_mask = ((1U << chip->mixer.dest_stream_ins) - 1) |
+ (((1U << chip->mixer.dest_phys_outs) - 1)
+ << chip->mixer.dest_phys_out_ofs);
+
+ dev_dbg(chip->card->dev, "Mixer src_mask=%x, dest_mask=%x\n",
+ chip->mixer.src_mask, chip->mixer.dest_mask);
+
+ return 0;
+}
+
+static int lola_mixer_set_src_gain(struct lola *chip, unsigned int id,
+ unsigned short gain, bool on)
+{
+ unsigned int oldval, val;
+
+ if (!(chip->mixer.src_mask & (1 << id)))
+ return -EINVAL;
+ oldval = val = readl(&chip->mixer.array->src_gain_enable);
+ if (on)
+ val |= (1 << id);
+ else
+ val &= ~(1 << id);
+ /* test if values unchanged */
+ if ((val == oldval) &&
+ (gain == readw(&chip->mixer.array->src_gain[id])))
+ return 0;
+
+ dev_dbg(chip->card->dev,
+ "lola_mixer_set_src_gain (id=%d, gain=%d) enable=%x\n",
+ id, gain, val);
+ writew(gain, &chip->mixer.array->src_gain[id]);
+ writel(val, &chip->mixer.array->src_gain_enable);
+ lola_codec_flush(chip);
+ /* inform micro-controller about the new source gain */
+ return lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN, id, 0);
+}
+
+#if 0 /* not used */
+static int lola_mixer_set_src_gains(struct lola *chip, unsigned int mask,
+ unsigned short *gains)
+{
+ int i;
+
+ if ((chip->mixer.src_mask & mask) != mask)
+ return -EINVAL;
+ for (i = 0; i < LOLA_MIXER_DIM; i++) {
+ if (mask & (1 << i)) {
+ writew(*gains, &chip->mixer.array->src_gain[i]);
+ gains++;
+ }
+ }
+ writel(mask, &chip->mixer.array->src_gain_enable);
+ lola_codec_flush(chip);
+ if (chip->mixer.caps & LOLA_PEAK_METER_CAN_AGC_MASK) {
+ /* update for all srcs at once */
+ return lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN, 0x80, 0);
+ }
+ /* update manually */
+ for (i = 0; i < LOLA_MIXER_DIM; i++) {
+ if (mask & (1 << i)) {
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_SOURCE_GAIN, i, 0);
+ }
+ }
+ return 0;
+}
+#endif /* not used */
+
+static int lola_mixer_set_mapping_gain(struct lola *chip,
+ unsigned int src, unsigned int dest,
+ unsigned short gain, bool on)
+{
+ unsigned int val;
+
+ if (!(chip->mixer.src_mask & (1 << src)) ||
+ !(chip->mixer.dest_mask & (1 << dest)))
+ return -EINVAL;
+ if (on)
+ writew(gain, &chip->mixer.array->dest_mix_gain[dest][src]);
+ val = readl(&chip->mixer.array->dest_mix_gain_enable[dest]);
+ if (on)
+ val |= (1 << src);
+ else
+ val &= ~(1 << src);
+ writel(val, &chip->mixer.array->dest_mix_gain_enable[dest]);
+ lola_codec_flush(chip);
+ return lola_codec_write(chip, chip->mixer.nid, LOLA_VERB_SET_MIX_GAIN,
+ src, dest);
+}
+
+#if 0 /* not used */
+static int lola_mixer_set_dest_gains(struct lola *chip, unsigned int id,
+ unsigned int mask, unsigned short *gains)
+{
+ int i;
+
+ if (!(chip->mixer.dest_mask & (1 << id)) ||
+ (chip->mixer.src_mask & mask) != mask)
+ return -EINVAL;
+ for (i = 0; i < LOLA_MIXER_DIM; i++) {
+ if (mask & (1 << i)) {
+ writew(*gains, &chip->mixer.array->dest_mix_gain[id][i]);
+ gains++;
+ }
+ }
+ writel(mask, &chip->mixer.array->dest_mix_gain_enable[id]);
+ lola_codec_flush(chip);
+ /* update for all dests at once */
+ return lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_DESTINATION_GAIN, id, 0);
+}
+#endif /* not used */
+
+/*
+ */
+
+static int set_analog_volume(struct lola *chip, int dir,
+ unsigned int idx, unsigned int val,
+ bool external_call);
+
+int lola_setup_all_analog_gains(struct lola *chip, int dir, bool mute)
+{
+ struct lola_pin *pin;
+ int idx, max_idx;
+
+ pin = chip->pin[dir].pins;
+ max_idx = chip->pin[dir].num_pins;
+ for (idx = 0; idx < max_idx; idx++) {
+ if (pin[idx].is_analog) {
+ unsigned int val = mute ? 0 : pin[idx].cur_gain_step;
+ /* set volume and do not save the value */
+ set_analog_volume(chip, dir, idx, val, false);
+ }
+ }
+ return lola_codec_flush(chip);
+}
+
+/*
+ */
+
+static int set_analog_volume(struct lola *chip, int dir,
+ unsigned int idx, unsigned int val,
+ bool external_call)
+{
+ struct lola_pin *pin;
+ int err;
+
+ if (idx >= chip->pin[dir].num_pins)
+ return -EINVAL;
+ pin = &chip->pin[dir].pins[idx];
+ if (!pin->is_analog || pin->amp_num_steps <= val)
+ return -EINVAL;
+ if (external_call && pin->cur_gain_step == val)
+ return 0;
+ if (external_call)
+ lola_codec_flush(chip);
+ dev_dbg(chip->card->dev,
+ "set_analog_volume (dir=%d idx=%d, volume=%d)\n",
+ dir, idx, val);
+ err = lola_codec_write(chip, pin->nid,
+ LOLA_VERB_SET_AMP_GAIN_MUTE, val, 0);
+ if (err < 0)
+ return err;
+ if (external_call)
+ pin->cur_gain_step = val;
+ return 0;
+}
+
+int lola_set_src_config(struct lola *chip, unsigned int src_mask, bool update)
+{
+ int ret = 0;
+ int success = 0;
+ int n, err;
+
+ /* SRC can be activated and the dwInputSRCMask is valid? */
+ if ((chip->input_src_caps_mask & src_mask) != src_mask)
+ return -EINVAL;
+ /* handle all even Inputs - SRC is a stereo setting !!! */
+ for (n = 0; n < chip->pin[CAPT].num_pins; n += 2) {
+ unsigned int mask = 3U << n; /* handle the stereo case */
+ unsigned int new_src, src_state;
+ if (!(chip->input_src_caps_mask & mask))
+ continue;
+ /* if one IO needs SRC, both stereo IO will get SRC */
+ new_src = (src_mask & mask) != 0;
+ if (update) {
+ src_state = (chip->input_src_mask & mask) != 0;
+ if (src_state == new_src)
+ continue; /* nothing to change for this IO */
+ }
+ err = lola_codec_write(chip, chip->pcm[CAPT].streams[n].nid,
+ LOLA_VERB_SET_SRC, new_src, 0);
+ if (!err)
+ success++;
+ else
+ ret = err;
+ }
+ if (success)
+ ret = lola_codec_flush(chip);
+ if (!ret)
+ chip->input_src_mask = src_mask;
+ return ret;
+}
+
+/*
+ */
+static int init_mixer_values(struct lola *chip)
+{
+ int i;
+
+ /* all sample rate converters on */
+ lola_set_src_config(chip, (1 << chip->pin[CAPT].num_pins) - 1, false);
+
+ /* clear all mixer matrix settings */
+ memset_io(chip->mixer.array, 0, sizeof(*chip->mixer.array));
+ /* inform firmware about all updated matrix columns - capture part */
+ for (i = 0; i < chip->mixer.dest_stream_ins; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_DESTINATION_GAIN,
+ i, 0);
+ /* inform firmware about all updated matrix columns - output part */
+ for (i = 0; i < chip->mixer.dest_phys_outs; i++)
+ lola_codec_write(chip, chip->mixer.nid,
+ LOLA_VERB_SET_DESTINATION_GAIN,
+ chip->mixer.dest_phys_out_ofs + i, 0);
+
+ /* set all digital input source (master) gains to 0dB */
+ for (i = 0; i < chip->mixer.src_phys_ins; i++)
+ lola_mixer_set_src_gain(chip, i, 336, true); /* 0dB */
+
+ /* set all digital playback source (master) gains to 0dB */
+ for (i = 0; i < chip->mixer.src_stream_outs; i++)
+ lola_mixer_set_src_gain(chip,
+ i + chip->mixer.src_stream_out_ofs,
+ 336, true); /* 0dB */
+ /* set gain value 0dB diagonally in matrix - part INPUT -> CAPTURE */
+ for (i = 0; i < chip->mixer.dest_stream_ins; i++) {
+ int src = i % chip->mixer.src_phys_ins;
+ lola_mixer_set_mapping_gain(chip, src, i, 336, true);
+ }
+ /* set gain value 0dB diagonally in matrix , part PLAYBACK -> OUTPUT
+ * (LoLa280 : playback channel 0,2,4,6 linked to output channel 0)
+ * (LoLa280 : playback channel 1,3,5,7 linked to output channel 1)
+ */
+ for (i = 0; i < chip->mixer.src_stream_outs; i++) {
+ int src = chip->mixer.src_stream_out_ofs + i;
+ int dst = chip->mixer.dest_phys_out_ofs +
+ i % chip->mixer.dest_phys_outs;
+ lola_mixer_set_mapping_gain(chip, src, dst, 336, true);
+ }
+ return 0;
+}
+
+/*
+ * analog mixer control element
+ */
+static int lola_analog_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chip->pin[dir].num_pins;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = chip->pin[dir].pins[0].amp_num_steps;
+ return 0;
+}
+
+static int lola_analog_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+ int i;
+
+ for (i = 0; i < chip->pin[dir].num_pins; i++)
+ ucontrol->value.integer.value[i] =
+ chip->pin[dir].pins[i].cur_gain_step;
+ return 0;
+}
+
+static int lola_analog_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+ int i, err;
+
+ for (i = 0; i < chip->pin[dir].num_pins; i++) {
+ err = set_analog_volume(chip, dir, i,
+ ucontrol->value.integer.value[i],
+ true);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int lola_analog_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int dir = kcontrol->private_value;
+ unsigned int val1, val2;
+ struct lola_pin *pin;
+
+ if (size < 4 * sizeof(unsigned int))
+ return -ENOMEM;
+ pin = &chip->pin[dir].pins[0];
+
+ val2 = pin->amp_step_size * 25;
+ val1 = -1 * (int)pin->amp_offset * (int)val2;
+#ifdef TLV_DB_SCALE_MUTE
+ val2 |= TLV_DB_SCALE_MUTE;
+#endif
+ if (put_user(SNDRV_CTL_TLVT_DB_SCALE, tlv))
+ return -EFAULT;
+ if (put_user(2 * sizeof(unsigned int), tlv + 1))
+ return -EFAULT;
+ if (put_user(val1, tlv + 2))
+ return -EFAULT;
+ if (put_user(val2, tlv + 3))
+ return -EFAULT;
+ return 0;
+}
+
+static struct snd_kcontrol_new lola_analog_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK),
+ .info = lola_analog_vol_info,
+ .get = lola_analog_vol_get,
+ .put = lola_analog_vol_put,
+ .tlv.c = lola_analog_vol_tlv,
+};
+
+static int create_analog_mixer(struct lola *chip, int dir, char *name)
+{
+ if (!chip->pin[dir].num_pins)
+ return 0;
+ /* no analog volumes on digital only adapters */
+ if (chip->pin[dir].num_pins != chip->pin[dir].num_analog_pins)
+ return 0;
+ lola_analog_mixer.name = name;
+ lola_analog_mixer.private_value = dir;
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_analog_mixer, chip));
+}
+
+/*
+ * Hardware sample rate converter on digital input
+ */
+static int lola_input_src_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = chip->pin[CAPT].num_pins;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int lola_input_src_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int i;
+
+ for (i = 0; i < chip->pin[CAPT].num_pins; i++)
+ ucontrol->value.integer.value[i] =
+ !!(chip->input_src_mask & (1 << i));
+ return 0;
+}
+
+static int lola_input_src_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ int i;
+ unsigned int mask;
+
+ mask = 0;
+ for (i = 0; i < chip->pin[CAPT].num_pins; i++)
+ if (ucontrol->value.integer.value[i])
+ mask |= 1 << i;
+ return lola_set_src_config(chip, mask, true);
+}
+
+static const struct snd_kcontrol_new lola_input_src_mixer = {
+ .name = "Digital SRC Capture Switch",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = lola_input_src_info,
+ .get = lola_input_src_get,
+ .put = lola_input_src_put,
+};
+
+/*
+ * Lola16161 or Lola881 can have Hardware sample rate converters
+ * on its digital input pins
+ */
+static int create_input_src_mixer(struct lola *chip)
+{
+ if (!chip->input_src_caps_mask)
+ return 0;
+
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_input_src_mixer, chip));
+}
+
+/*
+ * src gain mixer
+ */
+static int lola_src_gain_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int count = (kcontrol->private_value >> 8) & 0xff;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = count;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 409;
+ return 0;
+}
+
+static int lola_src_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int ofs = kcontrol->private_value & 0xff;
+ unsigned int count = (kcontrol->private_value >> 8) & 0xff;
+ unsigned int mask, i;
+
+ mask = readl(&chip->mixer.array->src_gain_enable);
+ for (i = 0; i < count; i++) {
+ unsigned int idx = ofs + i;
+ unsigned short val;
+ if (!(chip->mixer.src_mask & (1 << idx)))
+ return -EINVAL;
+ if (mask & (1 << idx))
+ val = readw(&chip->mixer.array->src_gain[idx]) + 1;
+ else
+ val = 0;
+ ucontrol->value.integer.value[i] = val;
+ }
+ return 0;
+}
+
+static int lola_src_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int ofs = kcontrol->private_value & 0xff;
+ unsigned int count = (kcontrol->private_value >> 8) & 0xff;
+ int i, err;
+
+ for (i = 0; i < count; i++) {
+ unsigned int idx = ofs + i;
+ unsigned short val = ucontrol->value.integer.value[i];
+ if (val)
+ val--;
+ err = lola_mixer_set_src_gain(chip, idx, val, !!val);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* raw value: 0 = -84dB, 336 = 0dB, 408=18dB, incremented 1 for mute */
+static const DECLARE_TLV_DB_SCALE(lola_src_gain_tlv, -8425, 25, 1);
+
+static struct snd_kcontrol_new lola_src_gain_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .info = lola_src_gain_info,
+ .get = lola_src_gain_get,
+ .put = lola_src_gain_put,
+ .tlv.p = lola_src_gain_tlv,
+};
+
+static int create_src_gain_mixer(struct lola *chip,
+ int num, int ofs, char *name)
+{
+ lola_src_gain_mixer.name = name;
+ lola_src_gain_mixer.private_value = ofs + (num << 8);
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_src_gain_mixer, chip));
+}
+
+#if 0 /* not used */
+/*
+ * destination gain (matrix-like) mixer
+ */
+static int lola_dest_gain_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = src_num;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 433;
+ return 0;
+}
+
+static int lola_dest_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int src_ofs = kcontrol->private_value & 0xff;
+ unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
+ unsigned int dst_ofs = (kcontrol->private_value >> 16) & 0xff;
+ unsigned int dst, mask, i;
+
+ dst = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + dst_ofs;
+ mask = readl(&chip->mixer.array->dest_mix_gain_enable[dst]);
+ for (i = 0; i < src_num; i++) {
+ unsigned int src = src_ofs + i;
+ unsigned short val;
+ if (!(chip->mixer.src_mask & (1 << src)))
+ return -EINVAL;
+ if (mask & (1 << dst))
+ val = readw(&chip->mixer.array->dest_mix_gain[dst][src]) + 1;
+ else
+ val = 0;
+ ucontrol->value.integer.value[i] = val;
+ }
+ return 0;
+}
+
+static int lola_dest_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct lola *chip = snd_kcontrol_chip(kcontrol);
+ unsigned int src_ofs = kcontrol->private_value & 0xff;
+ unsigned int src_num = (kcontrol->private_value >> 8) & 0xff;
+ unsigned int dst_ofs = (kcontrol->private_value >> 16) & 0xff;
+ unsigned int dst, mask;
+ unsigned short gains[MAX_STREAM_COUNT];
+ int i, num;
+
+ mask = 0;
+ num = 0;
+ for (i = 0; i < src_num; i++) {
+ unsigned short val = ucontrol->value.integer.value[i];
+ if (val) {
+ gains[num++] = val - 1;
+ mask |= 1 << i;
+ }
+ }
+ mask <<= src_ofs;
+ dst = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + dst_ofs;
+ return lola_mixer_set_dest_gains(chip, dst, mask, gains);
+}
+
+static const DECLARE_TLV_DB_SCALE(lola_dest_gain_tlv, -8425, 25, 1);
+
+static struct snd_kcontrol_new lola_dest_gain_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+ .info = lola_dest_gain_info,
+ .get = lola_dest_gain_get,
+ .put = lola_dest_gain_put,
+ .tlv.p = lola_dest_gain_tlv,
+};
+
+static int create_dest_gain_mixer(struct lola *chip,
+ int src_num, int src_ofs,
+ int num, int ofs, char *name)
+{
+ lola_dest_gain_mixer.count = num;
+ lola_dest_gain_mixer.name = name;
+ lola_dest_gain_mixer.private_value =
+ src_ofs + (src_num << 8) + (ofs << 16) + (num << 24);
+ return snd_ctl_add(chip->card,
+ snd_ctl_new1(&lola_dest_gain_mixer, chip));
+}
+#endif /* not used */
+
+/*
+ */
+int lola_create_mixer(struct lola *chip)
+{
+ int err;
+
+ err = create_analog_mixer(chip, PLAY, "Analog Playback Volume");
+ if (err < 0)
+ return err;
+ err = create_analog_mixer(chip, CAPT, "Analog Capture Volume");
+ if (err < 0)
+ return err;
+ err = create_input_src_mixer(chip);
+ if (err < 0)
+ return err;
+ err = create_src_gain_mixer(chip, chip->mixer.src_phys_ins, 0,
+ "Digital Capture Volume");
+ if (err < 0)
+ return err;
+ err = create_src_gain_mixer(chip, chip->mixer.src_stream_outs,
+ chip->mixer.src_stream_out_ofs,
+ "Digital Playback Volume");
+ if (err < 0)
+ return err;
+#if 0
+/* FIXME: buggy mixer matrix handling */
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_phys_ins, 0,
+ chip->mixer.dest_stream_ins, 0,
+ "Line Capture Volume");
+ if (err < 0)
+ return err;
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_stream_outs,
+ chip->mixer.src_stream_out_ofs,
+ chip->mixer.dest_stream_ins, 0,
+ "Stream-Loopback Capture Volume");
+ if (err < 0)
+ return err;
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_phys_ins, 0,
+ chip->mixer.dest_phys_outs,
+ chip->mixer.dest_phys_out_ofs,
+ "Line-Loopback Playback Volume");
+ if (err < 0)
+ return err;
+ err = create_dest_gain_mixer(chip,
+ chip->mixer.src_stream_outs,
+ chip->mixer.src_stream_out_ofs,
+ chip->mixer.dest_phys_outs,
+ chip->mixer.dest_phys_out_ofs,
+ "Stream Playback Volume");
+ if (err < 0)
+ return err;
+#endif /* FIXME */
+ return init_mixer_values(chip);
+}
diff --git a/sound/pci/lola/lola_pcm.c b/sound/pci/lola/lola_pcm.c
new file mode 100644
index 000000000000..6c046ecd6e08
--- /dev/null
+++ b/sound/pci/lola/lola_pcm.c
@@ -0,0 +1,681 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "lola.h"
+
+#define LOLA_MAX_BDL_ENTRIES 8
+#define LOLA_MAX_BUF_SIZE (1024*1024*1024)
+#define LOLA_BDL_ENTRY_SIZE (16 * 16)
+
+static struct lola_pcm *lola_get_pcm(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ return &chip->pcm[substream->stream];
+}
+
+static struct lola_stream *lola_get_stream(struct snd_pcm_substream *substream)
+{
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ unsigned int idx = substream->number;
+ return &pcm->streams[idx];
+}
+
+static unsigned int lola_get_lrc(struct lola *chip)
+{
+ return lola_readl(chip, BAR1, LRC);
+}
+
+static unsigned int lola_get_tstamp(struct lola *chip, bool quick_no_sync)
+{
+ unsigned int tstamp = lola_get_lrc(chip) >> 8;
+ if (chip->granularity) {
+ unsigned int wait_banks = quick_no_sync ? 0 : 8;
+ tstamp += (wait_banks + 1) * chip->granularity - 1;
+ tstamp -= tstamp % chip->granularity;
+ }
+ return tstamp << 8;
+}
+
+/* clear any pending interrupt status */
+static void lola_stream_clear_pending_irq(struct lola *chip,
+ struct lola_stream *str)
+{
+ unsigned int val = lola_dsd_read(chip, str->dsd, STS);
+ val &= LOLA_DSD_STS_DESE | LOLA_DSD_STS_BCIS;
+ if (val)
+ lola_dsd_write(chip, str->dsd, STS, val);
+}
+
+static void lola_stream_start(struct lola *chip, struct lola_stream *str,
+ unsigned int tstamp)
+{
+ lola_stream_clear_pending_irq(chip, str);
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_SRUN |
+ LOLA_DSD_CTL_IOCE |
+ LOLA_DSD_CTL_DEIE |
+ LOLA_DSD_CTL_VLRCV |
+ tstamp);
+}
+
+static void lola_stream_stop(struct lola *chip, struct lola_stream *str,
+ unsigned int tstamp)
+{
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_IOCE |
+ LOLA_DSD_CTL_DEIE |
+ LOLA_DSD_CTL_VLRCV |
+ tstamp);
+ lola_stream_clear_pending_irq(chip, str);
+}
+
+static void wait_for_srst_clear(struct lola *chip, struct lola_stream *str)
+{
+ unsigned long end_time = jiffies + msecs_to_jiffies(200);
+ while (time_before(jiffies, end_time)) {
+ unsigned int val;
+ val = lola_dsd_read(chip, str->dsd, CTL);
+ if (!(val & LOLA_DSD_CTL_SRST))
+ return;
+ msleep(1);
+ }
+ dev_warn(chip->card->dev, "SRST not clear (stream %d)\n", str->dsd);
+}
+
+static int lola_stream_wait_for_fifo(struct lola *chip,
+ struct lola_stream *str,
+ bool ready)
+{
+ unsigned int val = ready ? LOLA_DSD_STS_FIFORDY : 0;
+ unsigned long end_time = jiffies + msecs_to_jiffies(200);
+ while (time_before(jiffies, end_time)) {
+ unsigned int reg = lola_dsd_read(chip, str->dsd, STS);
+ if ((reg & LOLA_DSD_STS_FIFORDY) == val)
+ return 0;
+ msleep(1);
+ }
+ dev_warn(chip->card->dev, "FIFO not ready (stream %d)\n", str->dsd);
+ return -EIO;
+}
+
+/* sync for FIFO ready/empty for all linked streams;
+ * clear paused flag when FIFO gets ready again
+ */
+static int lola_sync_wait_for_fifo(struct lola *chip,
+ struct snd_pcm_substream *substream,
+ bool ready)
+{
+ unsigned int val = ready ? LOLA_DSD_STS_FIFORDY : 0;
+ unsigned long end_time = jiffies + msecs_to_jiffies(200);
+ struct snd_pcm_substream *s;
+ int pending = 0;
+
+ while (time_before(jiffies, end_time)) {
+ pending = 0;
+ snd_pcm_group_for_each_entry(s, substream) {
+ struct lola_stream *str;
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ str = lola_get_stream(s);
+ if (str->prepared && str->paused) {
+ unsigned int reg;
+ reg = lola_dsd_read(chip, str->dsd, STS);
+ if ((reg & LOLA_DSD_STS_FIFORDY) != val) {
+ pending = str->dsd + 1;
+ break;
+ }
+ if (ready)
+ str->paused = 0;
+ }
+ }
+ if (!pending)
+ return 0;
+ msleep(1);
+ }
+ dev_warn(chip->card->dev, "FIFO not ready (pending %d)\n", pending - 1);
+ return -EIO;
+}
+
+/* finish pause - prepare for a new resume */
+static void lola_sync_pause(struct lola *chip,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_substream *s;
+
+ lola_sync_wait_for_fifo(chip, substream, false);
+ snd_pcm_group_for_each_entry(s, substream) {
+ struct lola_stream *str;
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ str = lola_get_stream(s);
+ if (str->paused && str->prepared)
+ lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRUN |
+ LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE);
+ }
+ lola_sync_wait_for_fifo(chip, substream, true);
+}
+
+static void lola_stream_reset(struct lola *chip, struct lola_stream *str)
+{
+ if (str->prepared) {
+ if (str->paused)
+ lola_sync_pause(chip, str->substream);
+ str->prepared = 0;
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE);
+ lola_stream_wait_for_fifo(chip, str, false);
+ lola_stream_clear_pending_irq(chip, str);
+ lola_dsd_write(chip, str->dsd, CTL, LOLA_DSD_CTL_SRST);
+ lola_dsd_write(chip, str->dsd, LVI, 0);
+ lola_dsd_write(chip, str->dsd, BDPU, 0);
+ lola_dsd_write(chip, str->dsd, BDPL, 0);
+ wait_for_srst_clear(chip, str);
+ }
+}
+
+static const struct snd_pcm_hardware lola_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_FLOAT_LE),
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = LOLA_MAX_BUF_SIZE,
+ .period_bytes_min = 128,
+ .period_bytes_max = LOLA_MAX_BUF_SIZE / 2,
+ .periods_min = 2,
+ .periods_max = LOLA_MAX_BDL_ENTRIES,
+ .fifo_size = 0,
+};
+
+static int lola_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ guard(mutex)(&chip->open_mutex);
+ if (str->opened)
+ return -EBUSY;
+ str->substream = substream;
+ str->master = NULL;
+ str->opened = 1;
+ runtime->hw = lola_pcm_hw;
+ runtime->hw.channels_max = pcm->num_streams - str->index;
+ if (chip->sample_rate) {
+ /* sample rate is locked */
+ runtime->hw.rate_min = chip->sample_rate;
+ runtime->hw.rate_max = chip->sample_rate;
+ } else {
+ runtime->hw.rate_min = chip->sample_rate_min;
+ runtime->hw.rate_max = chip->sample_rate_max;
+ }
+ chip->ref_count_rate++;
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ /* period size = multiple of chip->granularity (8, 16 or 32 frames)*/
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ chip->granularity);
+ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ chip->granularity);
+ return 0;
+}
+
+static void lola_cleanup_slave_streams(struct lola_pcm *pcm,
+ struct lola_stream *str)
+{
+ int i;
+ for (i = str->index + 1; i < pcm->num_streams; i++) {
+ struct lola_stream *s = &pcm->streams[i];
+ if (s->master != str)
+ break;
+ s->master = NULL;
+ s->opened = 0;
+ }
+}
+
+static int lola_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+
+ guard(mutex)(&chip->open_mutex);
+ if (str->substream == substream) {
+ str->substream = NULL;
+ str->opened = 0;
+ }
+ if (--chip->ref_count_rate == 0) {
+ /* release sample rate */
+ chip->sample_rate = 0;
+ }
+ return 0;
+}
+
+static int lola_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct lola_stream *str = lola_get_stream(substream);
+
+ str->bufsize = 0;
+ str->period_bytes = 0;
+ str->format_verb = 0;
+ return 0;
+}
+
+static int lola_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+
+ guard(mutex)(&chip->open_mutex);
+ lola_stream_reset(chip, str);
+ lola_cleanup_slave_streams(pcm, str);
+ return 0;
+}
+
+/*
+ * set up a BDL entry
+ */
+static int setup_bdle(struct snd_pcm_substream *substream,
+ struct lola_stream *str, __le32 **bdlp,
+ int ofs, int size)
+{
+ __le32 *bdl = *bdlp;
+
+ while (size > 0) {
+ dma_addr_t addr;
+ int chunk;
+
+ if (str->frags >= LOLA_MAX_BDL_ENTRIES)
+ return -EINVAL;
+
+ addr = snd_pcm_sgbuf_get_addr(substream, ofs);
+ /* program the address field of the BDL entry */
+ bdl[0] = cpu_to_le32((u32)addr);
+ bdl[1] = cpu_to_le32(upper_32_bits(addr));
+ /* program the size field of the BDL entry */
+ chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size);
+ bdl[2] = cpu_to_le32(chunk);
+ /* program the IOC to enable interrupt
+ * only when the whole fragment is processed
+ */
+ size -= chunk;
+ bdl[3] = size ? 0 : cpu_to_le32(0x01);
+ bdl += 4;
+ str->frags++;
+ ofs += chunk;
+ }
+ *bdlp = bdl;
+ return ofs;
+}
+
+/*
+ * set up BDL entries
+ */
+static int lola_setup_periods(struct lola *chip, struct lola_pcm *pcm,
+ struct snd_pcm_substream *substream,
+ struct lola_stream *str)
+{
+ __le32 *bdl;
+ int i, ofs, periods, period_bytes;
+
+ period_bytes = str->period_bytes;
+ periods = str->bufsize / period_bytes;
+
+ /* program the initial BDL entries */
+ bdl = (__le32 *)(pcm->bdl->area + LOLA_BDL_ENTRY_SIZE * str->index);
+ ofs = 0;
+ str->frags = 0;
+ for (i = 0; i < periods; i++) {
+ ofs = setup_bdle(substream, str, &bdl, ofs, period_bytes);
+ if (ofs < 0)
+ goto error;
+ }
+ return 0;
+
+ error:
+ dev_err(chip->card->dev, "Too many BDL entries: buffer=%d, period=%d\n",
+ str->bufsize, period_bytes);
+ return -EINVAL;
+}
+
+static unsigned int lola_get_format_verb(struct snd_pcm_substream *substream)
+{
+ unsigned int verb;
+
+ switch (substream->runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ verb = 0x00000000;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ verb = 0x00000200;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ verb = 0x00000300;
+ break;
+ case SNDRV_PCM_FORMAT_FLOAT_LE:
+ verb = 0x00001300;
+ break;
+ default:
+ return 0;
+ }
+ verb |= substream->runtime->channels;
+ return verb;
+}
+
+static int lola_set_stream_config(struct lola *chip,
+ struct lola_stream *str,
+ int channels)
+{
+ int i, err;
+ unsigned int verb, val;
+
+ /* set format info for all channels
+ * (with only one command for the first channel)
+ */
+ err = lola_codec_read(chip, str->nid, LOLA_VERB_SET_STREAM_FORMAT,
+ str->format_verb, 0, &val, NULL);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Cannot set stream format 0x%x\n",
+ str->format_verb);
+ return err;
+ }
+
+ /* update stream - channel config */
+ for (i = 0; i < channels; i++) {
+ verb = (str->index << 6) | i;
+ err = lola_codec_read(chip, str[i].nid,
+ LOLA_VERB_SET_CHANNEL_STREAMID, 0, verb,
+ &val, NULL);
+ if (err < 0) {
+ dev_err(chip->card->dev,
+ "Cannot set stream channel %d\n", i);
+ return err;
+ }
+ }
+ return 0;
+}
+
+/*
+ * set up the SD for streaming
+ */
+static int lola_setup_controller(struct lola *chip, struct lola_pcm *pcm,
+ struct lola_stream *str)
+{
+ dma_addr_t bdl;
+
+ if (str->prepared)
+ return -EINVAL;
+
+ /* set up BDL */
+ bdl = pcm->bdl->addr + LOLA_BDL_ENTRY_SIZE * str->index;
+ lola_dsd_write(chip, str->dsd, BDPL, (u32)bdl);
+ lola_dsd_write(chip, str->dsd, BDPU, upper_32_bits(bdl));
+ /* program the stream LVI (last valid index) of the BDL */
+ lola_dsd_write(chip, str->dsd, LVI, str->frags - 1);
+ lola_stream_clear_pending_irq(chip, str);
+
+ lola_dsd_write(chip, str->dsd, CTL,
+ LOLA_DSD_CTL_IOCE | LOLA_DSD_CTL_DEIE | LOLA_DSD_CTL_SRUN);
+
+ str->prepared = 1;
+
+ return lola_stream_wait_for_fifo(chip, str, true);
+}
+
+static int lola_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_pcm *pcm = lola_get_pcm(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int bufsize, period_bytes, format_verb;
+ int i, err;
+
+ scoped_guard(mutex, &chip->open_mutex) {
+ lola_stream_reset(chip, str);
+ lola_cleanup_slave_streams(pcm, str);
+ if (str->index + runtime->channels > pcm->num_streams)
+ return -EINVAL;
+ for (i = 1; i < runtime->channels; i++) {
+ str[i].master = str;
+ str[i].opened = 1;
+ }
+ }
+
+ bufsize = snd_pcm_lib_buffer_bytes(substream);
+ period_bytes = snd_pcm_lib_period_bytes(substream);
+ format_verb = lola_get_format_verb(substream);
+
+ str->bufsize = bufsize;
+ str->period_bytes = period_bytes;
+ str->format_verb = format_verb;
+
+ err = lola_setup_periods(chip, pcm, substream, str);
+ if (err < 0)
+ return err;
+
+ err = lola_set_sample_rate(chip, runtime->rate);
+ if (err < 0)
+ return err;
+ chip->sample_rate = runtime->rate; /* sample rate gets locked */
+
+ err = lola_set_stream_config(chip, str, runtime->channels);
+ if (err < 0)
+ return err;
+
+ err = lola_setup_controller(chip, pcm, str);
+ if (err < 0) {
+ lola_stream_reset(chip, str);
+ return err;
+ }
+
+ return 0;
+}
+
+static int lola_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_stream *str;
+ struct snd_pcm_substream *s;
+ unsigned int start;
+ unsigned int tstamp;
+ bool sync_streams;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ start = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ start = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * sample correct synchronization is only needed starting several
+ * streams. On stop or if only one stream do as quick as possible
+ */
+ sync_streams = (start && snd_pcm_stream_linked(substream));
+ tstamp = lola_get_tstamp(chip, !sync_streams);
+ guard(spinlock)(&chip->reg_lock);
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ str = lola_get_stream(s);
+ if (start)
+ lola_stream_start(chip, str, tstamp);
+ else
+ lola_stream_stop(chip, str, tstamp);
+ str->running = start;
+ str->paused = !start;
+ snd_pcm_trigger_done(s, substream);
+ }
+ return 0;
+}
+
+static snd_pcm_uframes_t lola_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct lola *chip = snd_pcm_substream_chip(substream);
+ struct lola_stream *str = lola_get_stream(substream);
+ unsigned int pos = lola_dsd_read(chip, str->dsd, LPIB);
+
+ if (pos >= str->bufsize)
+ pos = 0;
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+void lola_pcm_update(struct lola *chip, struct lola_pcm *pcm, unsigned int bits)
+{
+ int i;
+ u8 num_streams = min_t(u8, pcm->num_streams, ARRAY_SIZE(pcm->streams));
+
+ for (i = 0; bits && i < num_streams; i++) {
+ if (bits & (1 << i)) {
+ struct lola_stream *str = &pcm->streams[i];
+ if (str->substream && str->running)
+ snd_pcm_period_elapsed(str->substream);
+ bits &= ~(1 << i);
+ }
+ }
+}
+
+static const struct snd_pcm_ops lola_pcm_ops = {
+ .open = lola_pcm_open,
+ .close = lola_pcm_close,
+ .hw_params = lola_pcm_hw_params,
+ .hw_free = lola_pcm_hw_free,
+ .prepare = lola_pcm_prepare,
+ .trigger = lola_pcm_trigger,
+ .pointer = lola_pcm_pointer,
+};
+
+int lola_create_pcm(struct lola *chip)
+{
+ struct snd_pcm *pcm;
+ int i, err;
+
+ for (i = 0; i < 2; i++) {
+ chip->pcm[i].bdl =
+ snd_devm_alloc_pages(&chip->pci->dev, SNDRV_DMA_TYPE_DEV,
+ PAGE_SIZE);
+ if (!chip->pcm[i].bdl)
+ return -ENOMEM;
+ }
+
+ err = snd_pcm_new(chip->card, "Digigram Lola", 0,
+ chip->pcm[SNDRV_PCM_STREAM_PLAYBACK].num_streams,
+ chip->pcm[SNDRV_PCM_STREAM_CAPTURE].num_streams,
+ &pcm);
+ if (err < 0)
+ return err;
+ strscpy(pcm->name, "Digigram Lola", sizeof(pcm->name));
+ pcm->private_data = chip;
+ for (i = 0; i < 2; i++) {
+ if (chip->pcm[i].num_streams)
+ snd_pcm_set_ops(pcm, i, &lola_pcm_ops);
+ }
+ /* buffer pre-allocation */
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ &chip->pci->dev,
+ 1024 * 64, 32 * 1024 * 1024);
+ return 0;
+}
+
+/*
+ */
+
+static int lola_init_stream(struct lola *chip, struct lola_stream *str,
+ int idx, int nid, int dir)
+{
+ unsigned int val;
+ int err;
+
+ str->nid = nid;
+ str->index = idx;
+ str->dsd = idx;
+ if (dir == PLAY)
+ str->dsd += MAX_STREAM_IN_COUNT;
+ err = lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read wcaps for 0x%x\n", nid);
+ return err;
+ }
+ if (dir == PLAY) {
+ /* test TYPE and bits 0..11 (no test bit9 : Digital = 0/1) */
+ if ((val & 0x00f00dff) != 0x00000010) {
+ dev_err(chip->card->dev,
+ "Invalid wcaps 0x%x for 0x%x\n",
+ val, nid);
+ return -EINVAL;
+ }
+ } else {
+ /* test TYPE and bits 0..11 (no test bit9 : Digital = 0/1)
+ * (bug : ignore bit8: Conn list = 0/1)
+ */
+ if ((val & 0x00f00cff) != 0x00100010) {
+ dev_err(chip->card->dev,
+ "Invalid wcaps 0x%x for 0x%x\n",
+ val, nid);
+ return -EINVAL;
+ }
+ /* test bit9:DIGITAL and bit12:SRC_PRESENT*/
+ if ((val & 0x00001200) == 0x00001200)
+ chip->input_src_caps_mask |= (1 << idx);
+ }
+
+ err = lola_read_param(chip, nid, LOLA_PAR_STREAM_FORMATS, &val);
+ if (err < 0) {
+ dev_err(chip->card->dev, "Can't read FORMATS 0x%x\n", nid);
+ return err;
+ }
+ val &= 3;
+ if (val == 3)
+ str->can_float = true;
+ if (!(val & 1)) {
+ dev_err(chip->card->dev,
+ "Invalid formats 0x%x for 0x%x", val, nid);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int lola_init_pcm(struct lola *chip, int dir, int *nidp)
+{
+ struct lola_pcm *pcm = &chip->pcm[dir];
+ int i, nid, err;
+
+ nid = *nidp;
+ for (i = 0; i < pcm->num_streams; i++, nid++) {
+ err = lola_init_stream(chip, &pcm->streams[i], i, nid, dir);
+ if (err < 0)
+ return err;
+ }
+ *nidp = nid;
+ return 0;
+}
diff --git a/sound/pci/lola/lola_proc.c b/sound/pci/lola/lola_proc.c
new file mode 100644
index 000000000000..a166672e22cb
--- /dev/null
+++ b/sound/pci/lola/lola_proc.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Support for Digigram Lola PCI-e boards
+ *
+ * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include "lola.h"
+
+static void print_audio_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid, const char *name)
+{
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x %s wcaps 0x%x\n", nid, name, val);
+ lola_read_param(chip, nid, LOLA_PAR_STREAM_FORMATS, &val);
+ snd_iprintf(buffer, " Formats: 0x%x\n", val);
+}
+
+static void print_pin_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid, unsigned int ampcap,
+ const char *name)
+{
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x %s wcaps 0x%x\n", nid, name, val);
+ if (val == 0x00400200)
+ return;
+ lola_read_param(chip, nid, ampcap, &val);
+ snd_iprintf(buffer, " Amp-Caps: 0x%x\n", val);
+ snd_iprintf(buffer, " mute=%d, step-size=%d, steps=%d, ofs=%d\n",
+ LOLA_AMP_MUTE_CAPABLE(val),
+ LOLA_AMP_STEP_SIZE(val),
+ LOLA_AMP_NUM_STEPS(val),
+ LOLA_AMP_OFFSET(val));
+ lola_codec_read(chip, nid, LOLA_VERB_GET_MAX_LEVEL, 0, 0, &val, NULL);
+ snd_iprintf(buffer, " Max-level: 0x%x\n", val);
+}
+
+static void print_clock_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid)
+{
+ int i, j, num_clocks;
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x [Clock] wcaps 0x%x\n", nid, val);
+ num_clocks = val & 0xff;
+ for (i = 0; i < num_clocks; i += 4) {
+ unsigned int res_ex;
+ unsigned short items[4];
+ const char *name;
+
+ lola_codec_read(chip, nid, LOLA_VERB_GET_CLOCK_LIST,
+ i, 0, &val, &res_ex);
+ items[0] = val & 0xfff;
+ items[1] = (val >> 16) & 0xfff;
+ items[2] = res_ex & 0xfff;
+ items[3] = (res_ex >> 16) & 0xfff;
+ for (j = 0; j < 4; j++) {
+ unsigned char type = items[j] >> 8;
+ unsigned int freq = items[j] & 0xff;
+ if (i + j >= num_clocks)
+ break;
+ if (type == LOLA_CLOCK_TYPE_INTERNAL) {
+ name = "Internal";
+ freq = lola_sample_rate_convert(freq);
+ } else if (type == LOLA_CLOCK_TYPE_VIDEO) {
+ name = "Video";
+ freq = lola_sample_rate_convert(freq);
+ } else {
+ name = "Other";
+ }
+ snd_iprintf(buffer, " Clock %d: Type %d:%s, freq=%d\n",
+ i + j, type, name, freq);
+ }
+ }
+}
+
+static void print_mixer_widget(struct snd_info_buffer *buffer,
+ struct lola *chip, int nid)
+{
+ unsigned int val;
+
+ lola_read_param(chip, nid, LOLA_PAR_AUDIO_WIDGET_CAP, &val);
+ snd_iprintf(buffer, "Node 0x%02x [Mixer] wcaps 0x%x\n", nid, val);
+}
+
+static void lola_proc_codec_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ unsigned int val;
+ int i, nid;
+
+ lola_read_param(chip, 0, LOLA_PAR_VENDOR_ID, &val);
+ snd_iprintf(buffer, "Vendor: 0x%08x\n", val);
+ lola_read_param(chip, 1, LOLA_PAR_FUNCTION_TYPE, &val);
+ snd_iprintf(buffer, "Function Type: %d\n", val);
+ lola_read_param(chip, 1, LOLA_PAR_SPECIFIC_CAPS, &val);
+ snd_iprintf(buffer, "Specific-Caps: 0x%08x\n", val);
+ snd_iprintf(buffer, " Pins-In %d, Pins-Out %d\n",
+ chip->pin[CAPT].num_pins, chip->pin[PLAY].num_pins);
+ nid = 2;
+ for (i = 0; i < chip->pcm[CAPT].num_streams; i++, nid++)
+ print_audio_widget(buffer, chip, nid, "[Audio-In]");
+ for (i = 0; i < chip->pcm[PLAY].num_streams; i++, nid++)
+ print_audio_widget(buffer, chip, nid, "[Audio-Out]");
+ for (i = 0; i < chip->pin[CAPT].num_pins; i++, nid++)
+ print_pin_widget(buffer, chip, nid, LOLA_PAR_AMP_IN_CAP,
+ "[Pin-In]");
+ for (i = 0; i < chip->pin[PLAY].num_pins; i++, nid++)
+ print_pin_widget(buffer, chip, nid, LOLA_PAR_AMP_OUT_CAP,
+ "[Pin-Out]");
+ if (LOLA_AFG_CLOCK_WIDGET_PRESENT(chip->lola_caps)) {
+ print_clock_widget(buffer, chip, nid);
+ nid++;
+ }
+ if (LOLA_AFG_MIXER_WIDGET_PRESENT(chip->lola_caps)) {
+ print_mixer_widget(buffer, chip, nid);
+ nid++;
+ }
+}
+
+/* direct codec access for debugging */
+static void lola_proc_codec_rw_write(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ char line[64];
+ unsigned int id, verb, data, extdata;
+ while (!snd_info_get_line(buffer, line, sizeof(line))) {
+ if (sscanf(line, "%u %u %u %u", &id, &verb, &data, &extdata) != 4)
+ continue;
+ lola_codec_read(chip, id, verb, data, extdata,
+ &chip->debug_res,
+ &chip->debug_res_ex);
+ }
+}
+
+static void lola_proc_codec_rw_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ snd_iprintf(buffer, "0x%x 0x%x\n", chip->debug_res, chip->debug_res_ex);
+}
+
+/*
+ * dump some registers
+ */
+static void lola_proc_regs_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct lola *chip = entry->private_data;
+ int i;
+
+ for (i = 0; i < 0x40; i += 4) {
+ snd_iprintf(buffer, "BAR0 %02x: %08x\n", i,
+ readl(chip->bar[BAR0].remap_addr + i));
+ }
+ snd_iprintf(buffer, "\n");
+ for (i = 0; i < 0x30; i += 4) {
+ snd_iprintf(buffer, "BAR1 %02x: %08x\n", i,
+ readl(chip->bar[BAR1].remap_addr + i));
+ }
+ snd_iprintf(buffer, "\n");
+ for (i = 0x80; i < 0xa0; i += 4) {
+ snd_iprintf(buffer, "BAR1 %02x: %08x\n", i,
+ readl(chip->bar[BAR1].remap_addr + i));
+ }
+ snd_iprintf(buffer, "\n");
+ for (i = 0; i < 32; i++) {
+ snd_iprintf(buffer, "DSD %02x STS %08x\n", i,
+ lola_dsd_read(chip, i, STS));
+ snd_iprintf(buffer, "DSD %02x LPIB %08x\n", i,
+ lola_dsd_read(chip, i, LPIB));
+ snd_iprintf(buffer, "DSD %02x CTL %08x\n", i,
+ lola_dsd_read(chip, i, CTL));
+ snd_iprintf(buffer, "DSD %02x LVIL %08x\n", i,
+ lola_dsd_read(chip, i, LVI));
+ snd_iprintf(buffer, "DSD %02x BDPL %08x\n", i,
+ lola_dsd_read(chip, i, BDPL));
+ snd_iprintf(buffer, "DSD %02x BDPU %08x\n", i,
+ lola_dsd_read(chip, i, BDPU));
+ }
+}
+
+void lola_proc_debug_new(struct lola *chip)
+{
+ snd_card_ro_proc_new(chip->card, "codec", chip, lola_proc_codec_read);
+ snd_card_rw_proc_new(chip->card, "codec_rw", chip,
+ lola_proc_codec_rw_read,
+ lola_proc_codec_rw_write);
+ snd_card_ro_proc_new(chip->card, "regs", chip, lola_proc_regs_read);
+}
diff --git a/sound/pci/lx6464es/Makefile b/sound/pci/lx6464es/Makefile
index eb04a6c73d8b..2b3047c7a388 100644
--- a/sound/pci/lx6464es/Makefile
+++ b/sound/pci/lx6464es/Makefile
@@ -1,2 +1,3 @@
-snd-lx6464es-objs := lx6464es.o lx_core.o
+# SPDX-License-Identifier: GPL-2.0-only
+snd-lx6464es-y := lx6464es.o lx_core.o
obj-$(CONFIG_SND_LX6464ES) += snd-lx6464es.o
diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c
index ef9af3f4ace2..96df00db51d5 100644
--- a/sound/pci/lx6464es/lx6464es.c
+++ b/sound/pci/lx6464es/lx6464es.c
@@ -1,25 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* -*- linux-c -*- *
*
* ALSA driver for the digigram lx6464es interface
*
* Copyright (c) 2008, 2009 Tim Blechmann <tim@klingt.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
*/
#include <linux/module.h>
@@ -37,12 +21,10 @@
MODULE_AUTHOR("Tim Blechmann");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("digigram lx6464es");
-MODULE_SUPPORTED_DEVICE("{digigram lx6464es{}}");
-
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Digigram LX6464ES interface.");
@@ -56,15 +38,23 @@ static const char card_name[] = "LX6464ES";
#define PCI_DEVICE_ID_PLX_LX6464ES PCI_DEVICE_ID_PLX_9056
-static DEFINE_PCI_DEVICE_TABLE(snd_lx6464es_ids) = {
- { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES),
- .subvendor = PCI_VENDOR_ID_DIGIGRAM,
- .subdevice = PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM
+static const struct pci_device_id snd_lx6464es_ids[] = {
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
+ PCI_VENDOR_ID_DIGIGRAM,
+ PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM),
}, /* LX6464ES */
- { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES),
- .subvendor = PCI_VENDOR_ID_DIGIGRAM,
- .subdevice = PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
+ PCI_VENDOR_ID_DIGIGRAM,
+ PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM),
}, /* LX6464ES-CAE */
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
+ PCI_VENDOR_ID_DIGIGRAM,
+ PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_SERIAL_SUBSYSTEM),
+ }, /* LX6464ESe */
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES,
+ PCI_VENDOR_ID_DIGIGRAM,
+ PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ESE_CAE_SERIAL_SUBSYSTEM),
+ }, /* LX6464ESe-CAE */
{ 0, },
};
@@ -77,7 +67,7 @@ MODULE_DEVICE_TABLE(pci, snd_lx6464es_ids);
/* alsa callbacks */
-static struct snd_pcm_hardware lx_caps = {
+static const struct snd_pcm_hardware lx_caps = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -112,16 +102,16 @@ static int lx_hardware_open(struct lx6464es *chip,
snd_pcm_uframes_t period_size = runtime->period_size;
- snd_printd(LXP "allocating pipe for %d channels\n", channels);
+ dev_dbg(chip->card->dev, "allocating pipe for %d channels\n", channels);
err = lx_pipe_allocate(chip, 0, is_capture, channels);
if (err < 0) {
- snd_printk(KERN_ERR LXP "allocating pipe failed\n");
+ dev_err(chip->card->dev, LXP "allocating pipe failed\n");
return err;
}
err = lx_set_granularity(chip, period_size);
if (err < 0) {
- snd_printk(KERN_ERR LXP "setting granularity to %ld failed\n",
+ dev_err(chip->card->dev, "setting granularity to %ld failed\n",
period_size);
return err;
}
@@ -136,24 +126,24 @@ static int lx_hardware_start(struct lx6464es *chip,
struct snd_pcm_runtime *runtime = substream->runtime;
int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
- snd_printd(LXP "setting stream format\n");
+ dev_dbg(chip->card->dev, "setting stream format\n");
err = lx_stream_set_format(chip, runtime, 0, is_capture);
if (err < 0) {
- snd_printk(KERN_ERR LXP "setting stream format failed\n");
+ dev_err(chip->card->dev, "setting stream format failed\n");
return err;
}
- snd_printd(LXP "starting pipe\n");
+ dev_dbg(chip->card->dev, "starting pipe\n");
err = lx_pipe_start(chip, 0, is_capture);
if (err < 0) {
- snd_printk(KERN_ERR LXP "starting pipe failed\n");
+ dev_err(chip->card->dev, "starting pipe failed\n");
return err;
}
- snd_printd(LXP "waiting for pipe to start\n");
+ dev_dbg(chip->card->dev, "waiting for pipe to start\n");
err = lx_pipe_wait_for_start(chip, 0, is_capture);
if (err < 0) {
- snd_printk(KERN_ERR LXP "waiting for pipe failed\n");
+ dev_err(chip->card->dev, "waiting for pipe failed\n");
return err;
}
@@ -167,24 +157,24 @@ static int lx_hardware_stop(struct lx6464es *chip,
int err = 0;
int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
- snd_printd(LXP "pausing pipe\n");
+ dev_dbg(chip->card->dev, "pausing pipe\n");
err = lx_pipe_pause(chip, 0, is_capture);
if (err < 0) {
- snd_printk(KERN_ERR LXP "pausing pipe failed\n");
+ dev_err(chip->card->dev, "pausing pipe failed\n");
return err;
}
- snd_printd(LXP "waiting for pipe to become idle\n");
+ dev_dbg(chip->card->dev, "waiting for pipe to become idle\n");
err = lx_pipe_wait_for_idle(chip, 0, is_capture);
if (err < 0) {
- snd_printk(KERN_ERR LXP "waiting for pipe failed\n");
+ dev_err(chip->card->dev, "waiting for pipe failed\n");
return err;
}
- snd_printd(LXP "stopping pipe\n");
+ dev_dbg(chip->card->dev, "stopping pipe\n");
err = lx_pipe_stop(chip, 0, is_capture);
if (err < 0) {
- snd_printk(LXP "stopping pipe failed\n");
+ dev_err(chip->card->dev, "stopping pipe failed\n");
return err;
}
@@ -198,10 +188,10 @@ static int lx_hardware_close(struct lx6464es *chip,
int err = 0;
int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
- snd_printd(LXP "releasing pipe\n");
+ dev_dbg(chip->card->dev, "releasing pipe\n");
err = lx_pipe_release(chip, 0, is_capture);
if (err < 0) {
- snd_printk(LXP "releasing pipe failed\n");
+ dev_err(chip->card->dev, "releasing pipe failed\n");
return err;
}
@@ -216,8 +206,8 @@ static int lx_pcm_open(struct snd_pcm_substream *substream)
int err = 0;
int board_rate;
- snd_printdd("->lx_pcm_open\n");
- mutex_lock(&chip->setup_mutex);
+ dev_dbg(chip->card->dev, "->lx_pcm_open\n");
+ guard(mutex)(&chip->setup_mutex);
/* copy the struct snd_pcm_hardware struct */
runtime->hw = lx_caps;
@@ -227,19 +217,19 @@ static int lx_pcm_open(struct snd_pcm_substream *substream)
err = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (err < 0) {
- snd_printk(KERN_WARNING LXP "could not constrain periods\n");
- goto exit;
+ dev_warn(chip->card->dev, "could not constrain periods\n");
+ return err;
}
#endif
/* the clock rate cannot be changed */
board_rate = chip->board_sample_rate;
- err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
- board_rate, board_rate);
+ err = snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_RATE,
+ board_rate);
if (err < 0) {
- snd_printk(KERN_WARNING LXP "could not constrain periods\n");
- goto exit;
+ dev_warn(chip->card->dev, "could not constrain periods\n");
+ return err;
}
/* constrain period size */
@@ -248,9 +238,9 @@ static int lx_pcm_open(struct snd_pcm_substream *substream)
MICROBLAZE_IBL_MIN,
MICROBLAZE_IBL_MAX);
if (err < 0) {
- snd_printk(KERN_WARNING LXP
+ dev_warn(chip->card->dev,
"could not constrain period size\n");
- goto exit;
+ return err;
}
snd_pcm_hw_constraint_step(runtime, 0,
@@ -259,19 +249,16 @@ static int lx_pcm_open(struct snd_pcm_substream *substream)
snd_pcm_set_sync(substream);
err = 0;
-exit:
runtime->private_data = chip;
- mutex_unlock(&chip->setup_mutex);
- snd_printdd("<-lx_pcm_open, %d\n", err);
+ dev_dbg(chip->card->dev, "<-lx_pcm_open, %d\n", err);
return err;
}
static int lx_pcm_close(struct snd_pcm_substream *substream)
{
- int err = 0;
- snd_printdd("->lx_pcm_close\n");
- return err;
+ dev_dbg(substream->pcm->card->dev, "->lx_pcm_close\n");
+ return 0;
}
static snd_pcm_uframes_t lx_pcm_stream_pointer(struct snd_pcm_substream
@@ -279,19 +266,17 @@ static snd_pcm_uframes_t lx_pcm_stream_pointer(struct snd_pcm_substream
{
struct lx6464es *chip = snd_pcm_substream_chip(substream);
snd_pcm_uframes_t pos;
- unsigned long flags;
int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
struct lx_stream *lx_stream = is_capture ? &chip->capture_stream :
&chip->playback_stream;
- snd_printdd("->lx_pcm_stream_pointer\n");
+ dev_dbg(chip->card->dev, "->lx_pcm_stream_pointer\n");
- spin_lock_irqsave(&chip->lock, flags);
+ guard(mutex)(&chip->lock);
pos = lx_stream->frame_pos * substream->runtime->period_size;
- spin_unlock_irqrestore(&chip->lock, flags);
- snd_printdd(LXP "stream_pointer at %ld\n", pos);
+ dev_dbg(chip->card->dev, "stream_pointer at %ld\n", pos);
return pos;
}
@@ -301,39 +286,39 @@ static int lx_pcm_prepare(struct snd_pcm_substream *substream)
int err = 0;
const int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
- snd_printdd("->lx_pcm_prepare\n");
+ dev_dbg(chip->card->dev, "->lx_pcm_prepare\n");
- mutex_lock(&chip->setup_mutex);
+ guard(mutex)(&chip->setup_mutex);
if (chip->hardware_running[is_capture]) {
err = lx_hardware_stop(chip, substream);
if (err < 0) {
- snd_printk(KERN_ERR LXP "failed to stop hardware. "
+ dev_err(chip->card->dev, "failed to stop hardware. "
"Error code %d\n", err);
- goto exit;
+ return err;
}
err = lx_hardware_close(chip, substream);
if (err < 0) {
- snd_printk(KERN_ERR LXP "failed to close hardware. "
+ dev_err(chip->card->dev, "failed to close hardware. "
"Error code %d\n", err);
- goto exit;
+ return err;
}
}
- snd_printd(LXP "opening hardware\n");
+ dev_dbg(chip->card->dev, "opening hardware\n");
err = lx_hardware_open(chip, substream);
if (err < 0) {
- snd_printk(KERN_ERR LXP "failed to open hardware. "
+ dev_err(chip->card->dev, "failed to open hardware. "
"Error code %d\n", err);
- goto exit;
+ return err;
}
err = lx_hardware_start(chip, substream);
if (err < 0) {
- snd_printk(KERN_ERR LXP "failed to start hardware. "
+ dev_err(chip->card->dev, "failed to start hardware. "
"Error code %d\n", err);
- goto exit;
+ return err;
}
chip->hardware_running[is_capture] = 1;
@@ -343,8 +328,6 @@ static int lx_pcm_prepare(struct snd_pcm_substream *substream)
chip->board_sample_rate = substream->runtime->rate;
}
-exit:
- mutex_unlock(&chip->setup_mutex);
return err;
}
@@ -352,23 +335,17 @@ static int lx_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params, int is_capture)
{
struct lx6464es *chip = snd_pcm_substream_chip(substream);
- int err = 0;
-
- snd_printdd("->lx_pcm_hw_params\n");
- mutex_lock(&chip->setup_mutex);
+ dev_dbg(chip->card->dev, "->lx_pcm_hw_params\n");
- /* set dma buffer */
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
+ guard(mutex)(&chip->setup_mutex);
if (is_capture)
chip->capture_stream.stream = substream;
else
chip->playback_stream.stream = substream;
- mutex_unlock(&chip->setup_mutex);
- return err;
+ return 0;
}
static int lx_pcm_hw_params_playback(struct snd_pcm_substream *substream,
@@ -389,43 +366,39 @@ static int lx_pcm_hw_free(struct snd_pcm_substream *substream)
int err = 0;
int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
- snd_printdd("->lx_pcm_hw_free\n");
- mutex_lock(&chip->setup_mutex);
+ dev_dbg(chip->card->dev, "->lx_pcm_hw_free\n");
+ guard(mutex)(&chip->setup_mutex);
if (chip->hardware_running[is_capture]) {
err = lx_hardware_stop(chip, substream);
if (err < 0) {
- snd_printk(KERN_ERR LXP "failed to stop hardware. "
+ dev_err(chip->card->dev, "failed to stop hardware. "
"Error code %d\n", err);
- goto exit;
+ return err;
}
err = lx_hardware_close(chip, substream);
if (err < 0) {
- snd_printk(KERN_ERR LXP "failed to close hardware. "
+ dev_err(chip->card->dev, "failed to close hardware. "
"Error code %d\n", err);
- goto exit;
+ return err;
}
chip->hardware_running[is_capture] = 0;
}
- err = snd_pcm_lib_free_pages(substream);
-
if (is_capture)
- chip->capture_stream.stream = 0;
+ chip->capture_stream.stream = NULL;
else
- chip->playback_stream.stream = 0;
+ chip->playback_stream.stream = NULL;
-exit:
- mutex_unlock(&chip->setup_mutex);
- return err;
+ return 0;
}
static void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream)
{
struct snd_pcm_substream *substream = lx_stream->stream;
- const int is_capture = lx_stream->is_capture;
+ const unsigned int is_capture = lx_stream->is_capture;
int err;
@@ -446,25 +419,25 @@ static void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream)
err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed,
size_array);
- snd_printdd(LXP "starting: needed %d, freed %d\n",
+ dev_dbg(chip->card->dev, "starting: needed %d, freed %d\n",
needed, freed);
err = lx_buffer_give(chip, 0, is_capture, period_bytes,
lower_32_bits(buf), upper_32_bits(buf),
&buffer_index);
- snd_printdd(LXP "starting: buffer index %x on %p (%d bytes)\n",
- buffer_index, (void *)buf, period_bytes);
+ dev_dbg(chip->card->dev, "starting: buffer index %x on 0x%lx (%d bytes)\n",
+ buffer_index, (unsigned long)buf, period_bytes);
buf += period_bytes;
}
err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, size_array);
- snd_printdd(LXP "starting: needed %d, freed %d\n", needed, freed);
+ dev_dbg(chip->card->dev, "starting: needed %d, freed %d\n", needed, freed);
- snd_printd(LXP "starting: starting stream\n");
+ dev_dbg(chip->card->dev, "starting: starting stream\n");
err = lx_stream_start(chip, 0, is_capture);
if (err < 0)
- snd_printk(KERN_ERR LXP "couldn't start stream\n");
+ dev_err(chip->card->dev, "couldn't start stream\n");
else
lx_stream->status = LX_STREAM_STATUS_RUNNING;
@@ -473,20 +446,20 @@ static void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream)
static void lx_trigger_stop(struct lx6464es *chip, struct lx_stream *lx_stream)
{
- const int is_capture = lx_stream->is_capture;
+ const unsigned int is_capture = lx_stream->is_capture;
int err;
- snd_printd(LXP "stopping: stopping stream\n");
+ dev_dbg(chip->card->dev, "stopping: stopping stream\n");
err = lx_stream_stop(chip, 0, is_capture);
if (err < 0)
- snd_printk(KERN_ERR LXP "couldn't stop stream\n");
+ dev_err(chip->card->dev, "couldn't stop stream\n");
else
lx_stream->status = LX_STREAM_STATUS_FREE;
}
-static void lx_trigger_tasklet_dispatch_stream(struct lx6464es *chip,
- struct lx_stream *lx_stream)
+static void lx_trigger_dispatch_stream(struct lx6464es *chip,
+ struct lx_stream *lx_stream)
{
switch (lx_stream->status) {
case LX_STREAM_STATUS_SCHEDULE_RUN:
@@ -502,24 +475,10 @@ static void lx_trigger_tasklet_dispatch_stream(struct lx6464es *chip,
}
}
-static void lx_trigger_tasklet(unsigned long data)
-{
- struct lx6464es *chip = (struct lx6464es *)data;
- unsigned long flags;
-
- snd_printdd("->lx_trigger_tasklet\n");
-
- spin_lock_irqsave(&chip->lock, flags);
- lx_trigger_tasklet_dispatch_stream(chip, &chip->capture_stream);
- lx_trigger_tasklet_dispatch_stream(chip, &chip->playback_stream);
- spin_unlock_irqrestore(&chip->lock, flags);
-}
-
static int lx_pcm_trigger_dispatch(struct lx6464es *chip,
struct lx_stream *lx_stream, int cmd)
{
- int err = 0;
-
+ guard(mutex)(&chip->lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
lx_stream->status = LX_STREAM_STATUS_SCHEDULE_RUN;
@@ -530,13 +489,13 @@ static int lx_pcm_trigger_dispatch(struct lx6464es *chip,
break;
default:
- err = -EINVAL;
- goto exit;
+ return -EINVAL;
}
- tasklet_schedule(&chip->trigger_tasklet);
-exit:
- return err;
+ lx_trigger_dispatch_stream(chip, &chip->capture_stream);
+ lx_trigger_dispatch_stream(chip, &chip->playback_stream);
+
+ return 0;
}
@@ -547,43 +506,25 @@ static int lx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
struct lx_stream *stream = is_capture ? &chip->capture_stream :
&chip->playback_stream;
- snd_printdd("->lx_pcm_trigger\n");
+ dev_dbg(chip->card->dev, "->lx_pcm_trigger\n");
return lx_pcm_trigger_dispatch(chip, stream, cmd);
}
-static int snd_lx6464es_free(struct lx6464es *chip)
+static void snd_lx6464es_free(struct snd_card *card)
{
- snd_printdd("->snd_lx6464es_free\n");
+ struct lx6464es *chip = card->private_data;
lx_irq_disable(chip);
-
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
-
- iounmap(chip->port_dsp_bar);
- ioport_unmap(chip->port_plx_remapped);
-
- pci_release_regions(chip->pci);
- pci_disable_device(chip->pci);
-
- kfree(chip);
-
- return 0;
-}
-
-static int snd_lx6464es_dev_free(struct snd_device *device)
-{
- return snd_lx6464es_free(device->device_data);
}
/* reset the dsp during initialization */
-static int __devinit lx_init_xilinx_reset(struct lx6464es *chip)
+static int lx_init_xilinx_reset(struct lx6464es *chip)
{
int i;
u32 plx_reg = lx_plx_reg_read(chip, ePLX_CHIPSC);
- snd_printdd("->lx_init_xilinx_reset\n");
+ dev_dbg(chip->card->dev, "->lx_init_xilinx_reset\n");
/* activate reset of xilinx */
plx_reg &= ~CHIPSC_RESET_XILINX;
@@ -603,8 +544,8 @@ static int __devinit lx_init_xilinx_reset(struct lx6464es *chip)
msleep(10);
reg_mbox3 = lx_plx_reg_read(chip, ePLX_MBOX3);
if (reg_mbox3) {
- snd_printd(LXP "xilinx reset done\n");
- snd_printdd(LXP "xilinx took %d loops\n", i);
+ dev_dbg(chip->card->dev, "xilinx reset done\n");
+ dev_dbg(chip->card->dev, "xilinx took %d loops\n", i);
break;
}
}
@@ -620,11 +561,11 @@ static int __devinit lx_init_xilinx_reset(struct lx6464es *chip)
return 0;
}
-static int __devinit lx_init_xilinx_test(struct lx6464es *chip)
+static int lx_init_xilinx_test(struct lx6464es *chip)
{
u32 reg;
- snd_printdd("->lx_init_xilinx_test\n");
+ dev_dbg(chip->card->dev, "->lx_init_xilinx_test\n");
/* TEST if we have access to Xilinx/MicroBlaze */
lx_dsp_reg_write(chip, eReg_CSM, 0);
@@ -632,25 +573,25 @@ static int __devinit lx_init_xilinx_test(struct lx6464es *chip)
reg = lx_dsp_reg_read(chip, eReg_CSM);
if (reg) {
- snd_printk(KERN_ERR LXP "Problem: Reg_CSM %x.\n", reg);
+ dev_err(chip->card->dev, "Problem: Reg_CSM %x.\n", reg);
/* PCI9056_SPACE0_REMAP */
lx_plx_reg_write(chip, ePLX_PCICR, 1);
reg = lx_dsp_reg_read(chip, eReg_CSM);
if (reg) {
- snd_printk(KERN_ERR LXP "Error: Reg_CSM %x.\n", reg);
+ dev_err(chip->card->dev, "Error: Reg_CSM %x.\n", reg);
return -EAGAIN; /* seems to be appropriate */
}
}
- snd_printd(LXP "Xilinx/MicroBlaze access test successful\n");
+ dev_dbg(chip->card->dev, "Xilinx/MicroBlaze access test successful\n");
return 0;
}
/* initialize ethersound */
-static int __devinit lx_init_ethersound_config(struct lx6464es *chip)
+static int lx_init_ethersound_config(struct lx6464es *chip)
{
int i;
u32 orig_conf_es = lx_dsp_reg_read(chip, eReg_CONFES);
@@ -661,7 +602,7 @@ static int __devinit lx_init_ethersound_config(struct lx6464es *chip)
(64 << IOCR_OUTPUTS_OFFSET) |
(FREQ_RATIO_SINGLE_MODE << FREQ_RATIO_OFFSET);
- snd_printdd("->lx_init_ethersound\n");
+ dev_dbg(chip->card->dev, "->lx_init_ethersound\n");
chip->freq_ratio = FREQ_RATIO_SINGLE_MODE;
@@ -675,35 +616,35 @@ static int __devinit lx_init_ethersound_config(struct lx6464es *chip)
for (i = 0; i != 1000; ++i) {
if (lx_dsp_reg_read(chip, eReg_CSES) & 4) {
- snd_printd(LXP "ethersound initialized after %dms\n",
+ dev_dbg(chip->card->dev, "ethersound initialized after %dms\n",
i);
goto ethersound_initialized;
}
msleep(1);
}
- snd_printk(KERN_WARNING LXP
+ dev_warn(chip->card->dev,
"ethersound could not be initialized after %dms\n", i);
return -ETIMEDOUT;
ethersound_initialized:
- snd_printd(LXP "ethersound initialized\n");
+ dev_dbg(chip->card->dev, "ethersound initialized\n");
return 0;
}
-static int __devinit lx_init_get_version_features(struct lx6464es *chip)
+static int lx_init_get_version_features(struct lx6464es *chip)
{
u32 dsp_version;
int err;
- snd_printdd("->lx_init_get_version_features\n");
+ dev_dbg(chip->card->dev, "->lx_init_get_version_features\n");
err = lx_dsp_get_version(chip, &dsp_version);
if (err == 0) {
u32 freq;
- snd_printk(LXP "DSP version: V%02d.%02d #%d\n",
+ dev_info(chip->card->dev, "DSP version: V%02d.%02d #%d\n",
(dsp_version>>16) & 0xff, (dsp_version>>8) & 0xff,
dsp_version & 0xff);
@@ -718,9 +659,9 @@ static int __devinit lx_init_get_version_features(struct lx6464es *chip)
err = lx_dsp_get_clock_frequency(chip, &freq);
if (err == 0)
chip->board_sample_rate = freq;
- snd_printd(LXP "actual clock frequency %d\n", freq);
+ dev_dbg(chip->card->dev, "actual clock frequency %d\n", freq);
} else {
- snd_printk(KERN_ERR LXP "DSP corrupted \n");
+ dev_err(chip->card->dev, "DSP corrupted \n");
err = -EAGAIN;
}
@@ -732,7 +673,7 @@ static int lx_set_granularity(struct lx6464es *chip, u32 gran)
int err = 0;
u32 snapped_gran = MICROBLAZE_IBL_MIN;
- snd_printdd("->lx_set_granularity\n");
+ dev_dbg(chip->card->dev, "->lx_set_granularity\n");
/* blocksize is a power of 2 */
while ((snapped_gran < gran) &&
@@ -745,39 +686,38 @@ static int lx_set_granularity(struct lx6464es *chip, u32 gran)
err = lx_dsp_set_granularity(chip, snapped_gran);
if (err < 0) {
- snd_printk(KERN_WARNING LXP "could not set granularity\n");
+ dev_warn(chip->card->dev, "could not set granularity\n");
err = -EAGAIN;
}
if (snapped_gran != gran)
- snd_printk(LXP "snapped blocksize to %d\n", snapped_gran);
+ dev_err(chip->card->dev, "snapped blocksize to %d\n", snapped_gran);
- snd_printd(LXP "set blocksize on board %d\n", snapped_gran);
+ dev_dbg(chip->card->dev, "set blocksize on board %d\n", snapped_gran);
chip->pcm_granularity = snapped_gran;
return err;
}
/* initialize and test the xilinx dsp chip */
-static int __devinit lx_init_dsp(struct lx6464es *chip)
+static int lx_init_dsp(struct lx6464es *chip)
{
int err;
- u8 mac_address[6];
int i;
- snd_printdd("->lx_init_dsp\n");
+ dev_dbg(chip->card->dev, "->lx_init_dsp\n");
- snd_printd(LXP "initialize board\n");
+ dev_dbg(chip->card->dev, "initialize board\n");
err = lx_init_xilinx_reset(chip);
if (err)
return err;
- snd_printd(LXP "testing board\n");
+ dev_dbg(chip->card->dev, "testing board\n");
err = lx_init_xilinx_test(chip);
if (err)
return err;
- snd_printd(LXP "initialize ethersound configuration\n");
+ dev_dbg(chip->card->dev, "initialize ethersound configuration\n");
err = lx_init_ethersound_config(chip);
if (err)
return err;
@@ -787,21 +727,22 @@ static int __devinit lx_init_dsp(struct lx6464es *chip)
/** \todo the mac address should be ready by not, but it isn't,
* so we wait for it */
for (i = 0; i != 1000; ++i) {
- err = lx_dsp_get_mac(chip, mac_address);
+ err = lx_dsp_get_mac(chip);
if (err)
return err;
- if (mac_address[0] || mac_address[1] || mac_address[2] ||
- mac_address[3] || mac_address[4] || mac_address[5])
+ if (chip->mac_address[0] || chip->mac_address[1] || chip->mac_address[2] ||
+ chip->mac_address[3] || chip->mac_address[4] || chip->mac_address[5])
goto mac_ready;
msleep(1);
}
return -ETIMEDOUT;
mac_ready:
- snd_printd(LXP "mac address ready read after: %dms\n", i);
- snd_printk(LXP "mac address: %02X.%02X.%02X.%02X.%02X.%02X\n",
- mac_address[0], mac_address[1], mac_address[2],
- mac_address[3], mac_address[4], mac_address[5]);
+ dev_dbg(chip->card->dev, "mac address ready read after: %dms\n", i);
+ dev_info(chip->card->dev,
+ "mac address: %02X.%02X.%02X.%02X.%02X.%02X\n",
+ chip->mac_address[0], chip->mac_address[1], chip->mac_address[2],
+ chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]);
err = lx_init_get_version_features(chip);
if (err)
@@ -814,10 +755,9 @@ mac_ready:
return err;
}
-static struct snd_pcm_ops lx_ops_playback = {
+static const struct snd_pcm_ops lx_ops_playback = {
.open = lx_pcm_open,
.close = lx_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.prepare = lx_pcm_prepare,
.hw_params = lx_pcm_hw_params_playback,
.hw_free = lx_pcm_hw_free,
@@ -825,10 +765,9 @@ static struct snd_pcm_ops lx_ops_playback = {
.pointer = lx_pcm_stream_pointer,
};
-static struct snd_pcm_ops lx_ops_capture = {
+static const struct snd_pcm_ops lx_ops_capture = {
.open = lx_pcm_open,
.close = lx_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
.prepare = lx_pcm_prepare,
.hw_params = lx_pcm_hw_params_capture,
.hw_free = lx_pcm_hw_free,
@@ -836,7 +775,7 @@ static struct snd_pcm_ops lx_ops_capture = {
.pointer = lx_pcm_stream_pointer,
};
-static int __devinit lx_pcm_create(struct lx6464es *chip)
+static int lx_pcm_create(struct lx6464es *chip)
{
int err;
struct snd_pcm *pcm;
@@ -852,6 +791,8 @@ static int __devinit lx_pcm_create(struct lx6464es *chip)
/* hardcoded device name & channel count */
err = snd_pcm_new(chip->card, (char *)card_name, 0,
1, 1, &pcm);
+ if (err < 0)
+ return err;
pcm->private_data = chip;
@@ -859,13 +800,11 @@ static int __devinit lx_pcm_create(struct lx6464es *chip)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &lx_ops_capture);
pcm->info_flags = 0;
- strcpy(pcm->name, card_name);
+ pcm->nonatomic = true;
+ strscpy(pcm->name, card_name);
- err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- snd_dma_pci_data(chip->pci),
- size, size);
- if (err < 0)
- return err;
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ &chip->pci->dev, size, size);
chip->pcm = pcm;
chip->capture_stream.is_capture = 1;
@@ -906,7 +845,7 @@ static int lx_control_playback_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new lx_control_playback_switch __devinitdata = {
+static const struct snd_kcontrol_new lx_control_playback_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
.index = 0,
@@ -953,98 +892,75 @@ static void lx_proc_levels_read(struct snd_info_entry *entry,
snd_iprintf(buffer, "\n");
}
-static int __devinit lx_proc_create(struct snd_card *card, struct lx6464es *chip)
+static int lx_proc_create(struct snd_card *card, struct lx6464es *chip)
{
- struct snd_info_entry *entry;
- int err = snd_card_proc_new(card, "levels", &entry);
- if (err < 0)
- return err;
-
- snd_info_set_text_ops(entry, chip, lx_proc_levels_read);
- return 0;
+ return snd_card_ro_proc_new(card, "levels", chip, lx_proc_levels_read);
}
-static int __devinit snd_lx6464es_create(struct snd_card *card,
- struct pci_dev *pci,
- struct lx6464es **rchip)
+static int snd_lx6464es_create(struct snd_card *card,
+ struct pci_dev *pci)
{
- struct lx6464es *chip;
+ struct lx6464es *chip = card->private_data;
int err;
- static struct snd_device_ops ops = {
- .dev_free = snd_lx6464es_dev_free,
- };
-
- snd_printdd("->snd_lx6464es_create\n");
-
- *rchip = NULL;
+ dev_dbg(card->dev, "->snd_lx6464es_create\n");
/* enable PCI device */
- err = pci_enable_device(pci);
+ err = pcim_enable_device(pci);
if (err < 0)
return err;
pci_set_master(pci);
/* check if we can restrict PCI DMA transfers to 32 bits */
- err = pci_set_dma_mask(pci, DMA_BIT_MASK(32));
+ err = dma_set_mask(&pci->dev, DMA_BIT_MASK(32));
if (err < 0) {
- snd_printk(KERN_ERR "architecture does not support "
- "32bit PCI busmaster DMA\n");
- pci_disable_device(pci);
+ dev_err(card->dev,
+ "architecture does not support 32bit PCI busmaster DMA\n");
return -ENXIO;
}
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- err = -ENOMEM;
- goto alloc_failed;
- }
-
chip->card = card;
chip->pci = pci;
chip->irq = -1;
/* initialize synchronization structs */
- spin_lock_init(&chip->lock);
- spin_lock_init(&chip->msg_lock);
+ mutex_init(&chip->lock);
+ mutex_init(&chip->msg_lock);
mutex_init(&chip->setup_mutex);
- tasklet_init(&chip->trigger_tasklet, lx_trigger_tasklet,
- (unsigned long)chip);
- tasklet_init(&chip->tasklet_capture, lx_tasklet_capture,
- (unsigned long)chip);
- tasklet_init(&chip->tasklet_playback, lx_tasklet_playback,
- (unsigned long)chip);
/* request resources */
- err = pci_request_regions(pci, card_name);
+ err = pcim_request_all_regions(pci, card_name);
if (err < 0)
- goto request_regions_failed;
+ return err;
/* plx port */
chip->port_plx = pci_resource_start(pci, 1);
- chip->port_plx_remapped = ioport_map(chip->port_plx,
- pci_resource_len(pci, 1));
+ chip->port_plx_remapped = devm_ioport_map(&pci->dev, chip->port_plx,
+ pci_resource_len(pci, 1));
+ if (!chip->port_plx_remapped)
+ return -ENOMEM;
/* dsp port */
- chip->port_dsp_bar = pci_ioremap_bar(pci, 2);
+ chip->port_dsp_bar = pcim_iomap(pci, 2, 0);
+ if (!chip->port_dsp_bar)
+ return -ENOMEM;
- err = request_irq(pci->irq, lx_interrupt, IRQF_SHARED,
- card_name, chip);
+ err = devm_request_threaded_irq(&pci->dev, pci->irq, lx_interrupt,
+ lx_threaded_irq, IRQF_SHARED,
+ KBUILD_MODNAME, chip);
if (err) {
- snd_printk(KERN_ERR LXP "unable to grab IRQ %d\n", pci->irq);
- goto request_irq_failed;
+ dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
+ return err;
}
chip->irq = pci->irq;
-
- err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
- if (err < 0)
- goto device_new_failed;
+ card->sync_irq = chip->irq;
+ card->private_free = snd_lx6464es_free;
err = lx_init_dsp(chip);
if (err < 0) {
- snd_printk(KERN_ERR LXP "error during DSP initialization\n");
+ dev_err(card->dev, "error during DSP initialization\n");
return err;
}
@@ -1061,35 +977,18 @@ static int __devinit snd_lx6464es_create(struct snd_card *card,
if (err < 0)
return err;
- snd_card_set_dev(card, &pci->dev);
-
- *rchip = chip;
return 0;
-
-device_new_failed:
- free_irq(pci->irq, chip);
-
-request_irq_failed:
- pci_release_regions(pci);
-
-request_regions_failed:
- kfree(chip);
-
-alloc_failed:
- pci_disable_device(pci);
-
- return err;
}
-static int __devinit snd_lx6464es_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
+static int snd_lx6464es_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
struct lx6464es *chip;
int err;
- snd_printdd("->snd_lx6464es_probe\n");
+ dev_dbg(&pci->dev, "->snd_lx6464es_probe\n");
if (dev >= SNDRV_CARDS)
return -ENODEV;
@@ -1098,62 +997,48 @@ static int __devinit snd_lx6464es_probe(struct pci_dev *pci,
return -ENOENT;
}
- err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+ err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+ sizeof(*chip), &card);
if (err < 0)
return err;
+ chip = card->private_data;
- err = snd_lx6464es_create(card, pci, &chip);
+ err = snd_lx6464es_create(card, pci);
if (err < 0) {
- snd_printk(KERN_ERR LXP "error during snd_lx6464es_create\n");
- goto out_free;
+ dev_err(card->dev, "error during snd_lx6464es_create\n");
+ goto error;
}
- strcpy(card->driver, "lx6464es");
- strcpy(card->shortname, "Digigram LX6464ES");
+ strscpy(card->driver, "LX6464ES");
+ sprintf(card->id, "LX6464ES_%02X%02X%02X",
+ chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]);
+
+ sprintf(card->shortname, "LX6464ES %02X.%02X.%02X.%02X.%02X.%02X",
+ chip->mac_address[0], chip->mac_address[1], chip->mac_address[2],
+ chip->mac_address[3], chip->mac_address[4], chip->mac_address[5]);
+
sprintf(card->longname, "%s at 0x%lx, 0x%p, irq %i",
card->shortname, chip->port_plx,
chip->port_dsp_bar, chip->irq);
err = snd_card_register(card);
if (err < 0)
- goto out_free;
+ goto error;
- snd_printdd(LXP "initialization successful\n");
+ dev_dbg(chip->card->dev, "initialization successful\n");
pci_set_drvdata(pci, card);
dev++;
return 0;
-out_free:
+ error:
snd_card_free(card);
return err;
-
-}
-
-static void __devexit snd_lx6464es_remove(struct pci_dev *pci)
-{
- snd_card_free(pci_get_drvdata(pci));
- pci_set_drvdata(pci, NULL);
}
-
-static struct pci_driver driver = {
- .name = "Digigram LX6464ES",
+static struct pci_driver lx6464es_driver = {
+ .name = KBUILD_MODNAME,
.id_table = snd_lx6464es_ids,
.probe = snd_lx6464es_probe,
- .remove = __devexit_p(snd_lx6464es_remove),
};
-
-/* module initialization */
-static int __init mod_init(void)
-{
- return pci_register_driver(&driver);
-}
-
-static void __exit mod_exit(void)
-{
- pci_unregister_driver(&driver);
-}
-
-module_init(mod_init);
-module_exit(mod_exit);
+module_pci_driver(lx6464es_driver);
diff --git a/sound/pci/lx6464es/lx6464es.h b/sound/pci/lx6464es/lx6464es.h
index 51afc048961d..1cfe10d23fa2 100644
--- a/sound/pci/lx6464es/lx6464es.h
+++ b/sound/pci/lx6464es/lx6464es.h
@@ -1,32 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* -*- linux-c -*- *
*
* ALSA driver for the digigram lx6464es interface
*
* Copyright (c) 2009 Tim Blechmann <tim@klingt.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
*/
#ifndef LX6464ES_H
#define LX6464ES_H
#include <linux/spinlock.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -60,7 +44,7 @@ struct lx_stream {
snd_pcm_uframes_t frame_pos;
enum lx_stream_status status; /* free, open, running, draining
* pause */
- int is_capture:1;
+ unsigned int is_capture:1;
};
@@ -69,14 +53,12 @@ struct lx6464es {
struct pci_dev *pci;
int irq;
- spinlock_t lock; /* interrupt spinlock */
+ u8 mac_address[6];
+
+ struct mutex lock; /* interrupt lock */
struct mutex setup_mutex; /* mutex used in hw_params, open
* and close */
- struct tasklet_struct trigger_tasklet; /* trigger tasklet */
- struct tasklet_struct tasklet_capture;
- struct tasklet_struct tasklet_playback;
-
/* ports */
unsigned long port_plx; /* io port (size=256) */
void __iomem *port_plx_remapped; /* remapped plx port */
@@ -85,8 +67,9 @@ struct lx6464es {
* size=8K) */
/* messaging */
- spinlock_t msg_lock; /* message spinlock */
+ struct mutex msg_lock; /* message lock */
struct lx_rmh rmh;
+ u32 irqsrc;
/* configuration */
uint freq_ratio : 2;
diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c
index 3086b751da4a..6f0843cfb3be 100644
--- a/sound/pci/lx6464es/lx_core.c
+++ b/sound/pci/lx6464es/lx_core.c
@@ -1,29 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* -*- linux-c -*- *
*
* ALSA driver for the digigram lx6464es interface
* low-level interface
*
* Copyright (c) 2009 Tim Blechmann <tim@klingt.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
*/
/* #define RMH_DEBUG 1 */
+#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
@@ -78,10 +64,15 @@ unsigned long lx_dsp_reg_read(struct lx6464es *chip, int port)